Import Android SDK Platform P [4697573]

/google/data/ro/projects/android/fetch_artifact \
    --bid 4697573 \
    --target sdk_phone_armv7-win_sdk \
    sdk-repo-linux-sources-4697573.zip

AndroidVersion.ApiLevel has been modified to appear as 28

Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
diff --git a/android/accessibilityservice/AccessibilityServiceInfo.java b/android/accessibilityservice/AccessibilityServiceInfo.java
index 06a9b06..452225c 100644
--- a/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,9 @@
 
 package android.accessibilityservice;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
+import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -43,12 +46,12 @@
 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;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -346,6 +349,19 @@
      */
     public String[] packageNames;
 
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FEEDBACK_" }, value = {
+            FEEDBACK_AUDIBLE,
+            FEEDBACK_GENERIC,
+            FEEDBACK_HAPTIC,
+            FEEDBACK_SPOKEN,
+            FEEDBACK_VISUAL,
+            FEEDBACK_BRAILLE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeedbackType {}
+
     /**
      * The feedback type an {@link AccessibilityService} provides.
      * <p>
@@ -358,6 +374,7 @@
      * @see #FEEDBACK_VISUAL
      * @see #FEEDBACK_BRAILLE
      */
+    @FeedbackType
     public int feedbackType;
 
     /**
@@ -393,6 +410,15 @@
     public int flags;
 
     /**
+     * Whether or not the service has crashed and is awaiting restart. Only valid from {@link
+     * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)},
+     * because that is populated from the internal list of running services.
+     *
+     * @hide
+     */
+    public boolean crashed;
+
+    /**
      * The component name the accessibility service.
      */
     private ComponentName mComponentName;
@@ -740,6 +766,7 @@
         parcel.writeInt(feedbackType);
         parcel.writeLong(notificationTimeout);
         parcel.writeInt(flags);
+        parcel.writeInt(crashed ? 1 : 0);
         parcel.writeParcelable(mComponentName, flagz);
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
@@ -756,6 +783,7 @@
         feedbackType = parcel.readInt();
         notificationTimeout = parcel.readLong();
         flags = parcel.readInt();
+        crashed = parcel.readInt() != 0;
         mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
@@ -818,7 +846,8 @@
         return stringBuilder.toString();
     }
 
-    private static void appendFeedbackTypes(StringBuilder stringBuilder, int feedbackTypes) {
+    private static void appendFeedbackTypes(StringBuilder stringBuilder,
+            @FeedbackType int feedbackTypes) {
         stringBuilder.append("feedbackTypes:");
         stringBuilder.append("[");
         while (feedbackTypes != 0) {
diff --git a/android/accounts/AccountManager.java b/android/accounts/AccountManager.java
index 782733f..5176d71 100644
--- a/android/accounts/AccountManager.java
+++ b/android/accounts/AccountManager.java
@@ -680,7 +680,7 @@
      */
     @NonNull
     public Account[] getAccountsByType(String type) {
-        return getAccountsByTypeAsUser(type, Process.myUserHandle());
+        return getAccountsByTypeAsUser(type, mContext.getUser());
     }
 
     /** @hide Same as {@link #getAccountsByType(String)} but for a specific user. */
@@ -2006,7 +2006,7 @@
             final AccountManagerCallback<Bundle> callback,
             final Handler handler) {
         return confirmCredentialsAsUser(account, options, activity, callback, handler,
-                Process.myUserHandle());
+                mContext.getUser());
     }
 
     /**
@@ -3208,7 +3208,7 @@
         return finishSessionAsUser(
                 sessionBundle,
                 activity,
-                Process.myUserHandle(),
+                mContext.getUser(),
                 callback,
                 handler);
     }
diff --git a/android/annotation/RequiresFeature.java b/android/annotation/RequiresFeature.java
new file mode 100644
index 0000000..fc93f03
--- /dev/null
+++ b/android/annotation/RequiresFeature.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.annotation;
+
+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.SOURCE;
+
+import android.content.pm.PackageManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element requires one or more device features. This
+ * is used to auto-generate documentation.
+ *
+ * @see PackageManager#hasSystemFeature(String)
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({TYPE,FIELD,METHOD,CONSTRUCTOR})
+public @interface RequiresFeature {
+    /**
+     * The name of the device feature that is required.
+     *
+     * @see PackageManager#hasSystemFeature(String)
+     */
+    String value();
+}
diff --git a/android/annotation/SystemService.java b/android/annotation/SystemService.java
index ba5002a..0c5d15e 100644
--- a/android/annotation/SystemService.java
+++ b/android/annotation/SystemService.java
@@ -26,12 +26,19 @@
 
 /**
  * Description of a system service available through
- * {@link Context#getSystemService(Class)}.
+ * {@link Context#getSystemService(Class)}. This is used to auto-generate
+ * documentation explaining how to obtain a reference to the service.
  *
  * @hide
  */
 @Retention(SOURCE)
 @Target(TYPE)
 public @interface SystemService {
+    /**
+     * The string name of the system service that can be passed to
+     * {@link Context#getSystemService(String)}.
+     *
+     * @see Context#getSystemServiceName(Class)
+     */
     String value();
 }
diff --git a/android/app/Activity.java b/android/app/Activity.java
index cd029c0..20149de 100644
--- a/android/app/Activity.java
+++ b/android/app/Activity.java
@@ -114,6 +114,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
@@ -123,17 +124,20 @@
 import android.widget.Toolbar;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneWindow;
 
+import dalvik.system.VMRuntime;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 
@@ -421,9 +425,15 @@
  * vs. those targeting prior platforms.  Starting with Honeycomb, an application
  * is not in the killable state until its {@link #onStop} has returned.  This
  * impacts when {@link #onSaveInstanceState(Bundle)} may be called (it may be
- * safely called after {@link #onPause()} and allows and application to safely
+ * safely called after {@link #onPause()}) and allows an application to safely
  * wait until {@link #onStop()} to save persistent state.</p>
  *
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
  * <p>For those methods that are not marked as being killable, the activity's
  * process will not be killed by the system starting from the time the method
  * is called and continuing after it returns.  Thus an activity is in the killable
@@ -647,13 +657,13 @@
  * <a name="ProcessLifecycle"></a>
  * <h3>Process Lifecycle</h3>
  *
- * <p>The Android system attempts to keep application process around for as
+ * <p>The Android system attempts to keep an application process around for as
  * long as possible, but eventually will need to remove old processes when
- * memory runs low.  As described in <a href="#ActivityLifecycle">Activity
+ * memory runs low. As described in <a href="#ActivityLifecycle">Activity
  * Lifecycle</a>, the decision about which process to remove is intimately
- * tied to the state of the user's interaction with it.  In general, there
+ * tied to the state of the user's interaction with it. In general, there
  * are four states a process can be in based on the activities running in it,
- * listed here in order of importance.  The system will kill less important
+ * listed here in order of importance. The system will kill less important
  * processes (the last ones) before it resorts to killing more important
  * processes (the first ones).
  *
@@ -742,6 +752,14 @@
 
     private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui";
 
+    private static final int LOG_AM_ON_CREATE_CALLED = 30057;
+    private static final int LOG_AM_ON_START_CALLED = 30059;
+    private static final int LOG_AM_ON_RESUME_CALLED = 30022;
+    private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
+    private static final int LOG_AM_ON_STOP_CALLED = 30049;
+    private static final int LOG_AM_ON_RESTART_CALLED = 30058;
+    private static final int LOG_AM_ON_DESTROY_CALLED = 30060;
+
     private static class ManagedDialog {
         Dialog mDialog;
         Bundle mArgs;
@@ -978,9 +996,9 @@
      * cursors for data being displayed, etc.
      *
      * <p>You can call {@link #finish} from within this function, in
-     * which case onDestroy() will be immediately called without any of the rest
-     * of the activity lifecycle ({@link #onStart}, {@link #onResume},
-     * {@link #onPause}, etc) executing.
+     * which case onDestroy() will be immediately called after {@link #onCreate} without any of the
+     * rest of the activity lifecycle ({@link #onStart}, {@link #onResume}, {@link #onPause}, etc)
+     * executing.
      *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
@@ -1383,6 +1401,7 @@
      *
      * {@hide}
      */
+    @Override
     public int getNextAutofillId() {
         if (mLastAutofillId == Integer.MAX_VALUE - 1) {
             mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
@@ -1394,6 +1413,14 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public AutofillId autofillClientGetNextAutofillId() {
+        return new AutofillId(getNextAutofillId());
+    }
+
+    /**
      * Check whether this activity is running as part of a voice interaction with the user.
      * If true, it should perform its interaction with the user through the
      * {@link VoiceInteractor} returned by {@link #getVoiceInteractor}.
@@ -1576,8 +1603,11 @@
      * call through to the default implementation, otherwise be prepared to save
      * all of the state of each view yourself.
      *
-     * <p>If called, this method will occur before {@link #onStop}.  There are
-     * no guarantees about whether it will occur before or after {@link #onPause}.
+     * <p>If called, this method will occur after {@link #onStop} for applications
+     * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+     * For applications targeting earlier platform versions this method will occur
+     * before {@link #onStop} and there are no guarantees about whether it will
+     * occur before or after {@link #onPause}.
      *
      * @param outState Bundle in which to place your saved state.
      *
@@ -1723,7 +1753,7 @@
      *
      * <p>This callback and {@link #onUserInteraction} are intended to help
      * activities manage status bar notifications intelligently; specifically,
-     * for helping activities determine the proper time to cancel a notfication.
+     * for helping activities determine the proper time to cancel a notification.
      *
      * @see #onUserInteraction()
      */
@@ -1731,32 +1761,16 @@
     }
 
     /**
-     * Generate a new thumbnail for this activity.  This method is called before
-     * pausing the activity, and should draw into <var>outBitmap</var> the
-     * imagery for the desired thumbnail in the dimensions of that bitmap.  It
-     * can use the given <var>canvas</var>, which is configured to draw into the
-     * bitmap, for rendering if desired.
-     *
-     * <p>The default implementation returns fails and does not draw a thumbnail;
-     * this will result in the platform creating its own thumbnail if needed.
-     *
-     * @param outBitmap The bitmap to contain the thumbnail.
-     * @param canvas Can be used to render into the bitmap.
-     *
-     * @return Return true if you have drawn into the bitmap; otherwise after
-     *         you return it will be filled with a default thumbnail.
-     *
-     * @see #onCreateDescription
-     * @see #onSaveInstanceState
-     * @see #onPause
+     * @deprecated Method doesn't do anything and will be removed in the future.
      */
+    @Deprecated
     public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
         return false;
     }
 
     /**
      * Generate a new description for this activity.  This method is called
-     * before pausing the activity and can, if desired, return some textual
+     * before stopping the activity and can, if desired, return some textual
      * description of its current state to be displayed to the user.
      *
      * <p>The default implementation returns null, which will cause you to
@@ -1767,9 +1781,8 @@
      * @return A description of what the user is doing.  It should be short and
      *         sweet (only a few words).
      *
-     * @see #onCreateThumbnail
      * @see #onSaveInstanceState
-     * @see #onPause
+     * @see #onStop
      */
     @Nullable
     public CharSequence onCreateDescription() {
@@ -1905,7 +1918,7 @@
 
         if (isFinishing()) {
             if (mAutoFillResetNeeded) {
-                getAutofillManager().onActivityFinished();
+                getAutofillManager().onActivityFinishing();
             } else if (mIntent != null
                     && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
                 // Activity was launched when user tapped a link in the Autofill Save UI - since
@@ -2136,11 +2149,15 @@
      * @param params non-null parameters to be combined with previously set parameters when entering
      * picture-in-picture.
      *
-     * @return true if the system puts this activity into picture-in-picture mode or was already
-     * in picture-in-picture mode (@see {@link #isInPictureInPictureMode())
+     * @return true if the system successfully put this activity into picture-in-picture mode or was
+     * already in picture-in-picture mode (@see {@link #isInPictureInPictureMode()). If the device
+     * does not support picture-in-picture, return false.
      */
     public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
         try {
+            if (!deviceSupportsPictureInPictureMode()) {
+                return false;
+            }
             if (params == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture params");
             }
@@ -2168,6 +2185,9 @@
      */
     public void setPictureInPictureParams(@NonNull PictureInPictureParams params) {
         try {
+            if (!deviceSupportsPictureInPictureMode()) {
+                return;
+            }
             if (params == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture params");
             }
@@ -2190,6 +2210,13 @@
         }
     }
 
+    /**
+     * @return Whether this device supports picture-in-picture.
+     */
+    private boolean deviceSupportsPictureInPictureMode() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
+    }
+
     void dispatchMovedToDisplay(int displayId, Configuration config) {
         updateDisplay(displayId);
         onMovedToDisplay(displayId, config);
@@ -4135,9 +4162,10 @@
      * <p>You can override this function to force global search, e.g. in response to a dedicated
      * search key, or to block search entirely (by simply returning false).
      *
-     * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_TELEVISION}, the default
-     * implementation changes to simply return false and you must supply your own custom
-     * implementation if you want to support search.</p>
+     * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_TELEVISION} or
+     * {@link Configuration#UI_MODE_TYPE_WATCH}, the default implementation changes to simply
+     * return false and you must supply your own custom implementation if you want to support
+     * search.
      *
      * @param searchEvent The {@link SearchEvent} that signaled this search.
      * @return Returns {@code true} if search launched, and {@code false} if the activity does
@@ -4157,8 +4185,10 @@
      * @see #onSearchRequested(SearchEvent)
      */
     public boolean onSearchRequested() {
-        if ((getResources().getConfiguration().uiMode&Configuration.UI_MODE_TYPE_MASK)
-                != Configuration.UI_MODE_TYPE_TELEVISION) {
+        final int uiMode = getResources().getConfiguration().uiMode
+            & Configuration.UI_MODE_TYPE_MASK;
+        if (uiMode != Configuration.UI_MODE_TYPE_TELEVISION
+                && uiMode != Configuration.UI_MODE_TYPE_WATCH) {
             startSearch(null, false, null, false);
             return true;
         } else {
@@ -4187,6 +4217,9 @@
      * is to inject specific data such as context data, it is preferred to <i>override</i>
      * onSearchRequested(), so that any callers to it will benefit from the override.
      *
+     * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_WATCH}, use of this API is
+     * not supported.
+     *
      * @param initialQuery Any non-null non-empty string will be inserted as
      * pre-entered text in the search query box.
      * @param selectInitialQuery If true, the initial query will be preselected, which means that
@@ -4697,7 +4730,6 @@
      * their launch had come from the original activity.
      * @param intent The Intent to start.
      * @param options ActivityOptions or null.
-     * @param permissionToken Token received from the system that permits this call to be made.
      * @param ignoreTargetSecurity If true, the activity manager will not check whether the
      * caller it is doing the start is, is actually allowed to start the target activity.
      * If you set this to true, you must set an explicit component in the Intent and do any
@@ -4706,7 +4738,7 @@
      * @hide
      */
     public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
-            IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
+            boolean ignoreTargetSecurity, int userId) {
         if (mParent != null) {
             throw new RuntimeException("Can't be called from a child");
         }
@@ -4714,7 +4746,7 @@
         Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivityAsCaller(
                         this, mMainThread.getApplicationThread(), mToken, this,
-                        intent, -1, options, permissionToken, ignoreTargetSecurity, userId);
+                        intent, -1, options, ignoreTargetSecurity, userId);
         if (ar != null) {
             mMainThread.sendActivityResult(
                 mToken, mEmbeddedID, -1, ar.getResultCode(),
@@ -5545,13 +5577,7 @@
         if (mParent != null) {
             throw new IllegalStateException("Can only be called on top-level activity");
         }
-        if (Looper.myLooper() != mMainThread.getLooper()) {
-            throw new IllegalStateException("Must be called from main thread");
-        }
-        try {
-            ActivityManager.getService().requestActivityRelaunch(mToken);
-        } catch (RemoteException e) {
-        }
+        mMainThread.handleRelaunchActivityLocally(mToken);
     }
 
     /**
@@ -5809,7 +5835,7 @@
                         ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
                         mParent == null ? mToken : mParent.mToken,
                         mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
-                        UserHandle.myUserId());
+                        getUserId());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             // Empty
@@ -5938,12 +5964,16 @@
      *
      * @return Returns the complete component name for this activity
      */
-    @Override
-    public ComponentName getComponentName()
-    {
+    public ComponentName getComponentName() {
         return mComponent;
     }
 
+    /** @hide */
+    @Override
+    public final ComponentName autofillClientGetComponentName() {
+        return getComponentName();
+    }
+
     /**
      * Retrieve a {@link SharedPreferences} object for accessing preferences
      * that are private to this activity.  This simply calls the underlying
@@ -6239,7 +6269,6 @@
      *
      * @param action the action to run on the UI thread
      */
-    @Override
     public final void runOnUiThread(Runnable action) {
         if (Thread.currentThread() != mUiThread) {
             mHandler.post(action);
@@ -6248,6 +6277,12 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public final void autofillClientRunOnUiThread(Runnable action) {
+        runOnUiThread(action);
+    }
+
     /**
      * Standard implementation of
      * {@link android.view.LayoutInflater.Factory#onCreateView} used when
@@ -6324,12 +6359,16 @@
 
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
 
-        final AutofillManager afm = mAutofillManager;
+        final AutofillManager afm = getAutofillManager();
         if (afm != null) {
+            writer.print(prefix); writer.print("Autofill Compat Mode: ");
+            writer.println(isAutofillCompatibilityEnabled());
             afm.dump(prefix, writer);
         } else {
             writer.print(prefix); writer.println("No AutofillManager");
         }
+
+        ResourcesManager.getInstance().dump(prefix, writer);
     }
 
     /**
@@ -7053,6 +7092,18 @@
         mCurrentConfig = config;
 
         mWindow.setColorMode(info.colorMode);
+
+        setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
+        enableAutofillCompatibilityIfNeeded();
+    }
+
+    private void enableAutofillCompatibilityIfNeeded() {
+        if (isAutofillCompatibilityEnabled()) {
+            final AutofillManager afm = getSystemService(AutofillManager.class);
+            if (afm != null) {
+                afm.enableCompatibilityMode();
+            }
+        }
     }
 
     /** @hide */
@@ -7060,6 +7111,12 @@
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public final ActivityThread getActivityThread() {
+        return mMainThread;
+    }
+
     final void performCreate(Bundle icicle) {
         performCreate(icicle, null);
     }
@@ -7072,6 +7129,7 @@
         } else {
             onCreate(icicle);
         }
+        writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
         mActivityTransitionState.readState(icicle);
 
         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
@@ -7085,12 +7143,14 @@
         onNewIntent(intent);
     }
 
-    final void performStart() {
+    final void performStart(String reason) {
         mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
         mFragments.noteStateNotSaved();
         mCalled = false;
         mFragments.execPendingActions();
         mInstrumentation.callActivityOnStart(this);
+        writeEventLog(LOG_AM_ON_START_CALLED, reason);
+
         if (!mCalled) {
             throw new SuperNotCalledException(
                 "Activity " + mComponent.toShortString() +
@@ -7099,11 +7159,12 @@
         mFragments.dispatchStart();
         mFragments.reportLoaderStart();
 
-        // This property is set for all builds except final release
-        boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
         boolean isAppDebuggable =
                 (mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
 
+        // This property is set for all non-user builds except final release
+        boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
         if (isAppDebuggable || isDlwarningEnabled) {
             String dlwarning = getDlWarning();
             if (dlwarning != null) {
@@ -7124,6 +7185,31 @@
             }
         }
 
+        // This property is set for all non-user builds except final release
+        boolean isApiWarningEnabled = SystemProperties.getInt("ro.art.hiddenapi.warning", 0) == 1;
+
+        if (isAppDebuggable || isApiWarningEnabled) {
+            if (!mMainThread.mHiddenApiWarningShown && VMRuntime.getRuntime().hasUsedHiddenApi()) {
+                // Only show the warning once per process.
+                mMainThread.mHiddenApiWarningShown = true;
+
+                String appName = getApplicationInfo().loadLabel(getPackageManager())
+                        .toString();
+                String warning = "Detected problems with API compatibility\n"
+                                 + "(visit g.co/dev/appcompat for more info)";
+                if (isAppDebuggable) {
+                    new AlertDialog.Builder(this)
+                        .setTitle(appName)
+                        .setMessage(warning)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .setCancelable(false)
+                        .show();
+                } else {
+                    Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+                }
+            }
+        }
+
         mActivityTransitionState.enterReady(this);
     }
 
@@ -7133,7 +7219,7 @@
      *              The option to not start immediately is needed in case a transaction with
      *              multiple lifecycle transitions is in progress.
      */
-    final void performRestart(boolean start) {
+    final void performRestart(boolean start, String reason) {
         mCanEnterPictureInPicture = true;
         mFragments.noteStateNotSaved();
 
@@ -7166,19 +7252,20 @@
 
             mCalled = false;
             mInstrumentation.callActivityOnRestart(this);
+            writeEventLog(LOG_AM_ON_RESTART_CALLED, reason);
             if (!mCalled) {
                 throw new SuperNotCalledException(
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onRestart()");
             }
             if (start) {
-                performStart();
+                performStart(reason);
             }
         }
     }
 
-    final void performResume(boolean followedByPause) {
-        performRestart(true /* start */);
+    final void performResume(boolean followedByPause, String reason) {
+        performRestart(true /* start */, reason);
 
         mFragments.execPendingActions();
 
@@ -7197,6 +7284,7 @@
         mCalled = false;
         // mResumed is set by the instrumentation
         mInstrumentation.callActivityOnResume(this);
+        writeEventLog(LOG_AM_ON_RESUME_CALLED, reason);
         if (!mCalled) {
             throw new SuperNotCalledException(
                 "Activity " + mComponent.toShortString() +
@@ -7233,6 +7321,7 @@
         mFragments.dispatchPause();
         mCalled = false;
         onPause();
+        writeEventLog(LOG_AM_ON_PAUSE_CALLED, "performPause");
         mResumed = false;
         if (!mCalled && getApplicationInfo().targetSdkVersion
                 >= android.os.Build.VERSION_CODES.GINGERBREAD) {
@@ -7240,7 +7329,6 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onPause()");
         }
-        mResumed = false;
     }
 
     final void performUserLeaving() {
@@ -7248,7 +7336,7 @@
         onUserLeaveHint();
     }
 
-    final void performStop(boolean preserveWindow) {
+    final void performStop(boolean preserveWindow, String reason) {
         mDoReportFullyDrawn = false;
         mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
 
@@ -7271,6 +7359,7 @@
 
             mCalled = false;
             mInstrumentation.callActivityOnStop(this);
+            writeEventLog(LOG_AM_ON_STOP_CALLED, reason);
             if (!mCalled) {
                 throw new SuperNotCalledException(
                     "Activity " + mComponent.toShortString() +
@@ -7298,6 +7387,7 @@
         mWindow.destroy();
         mFragments.dispatchDestroy();
         onDestroy();
+        writeEventLog(LOG_AM_ON_DESTROY_CALLED, "performDestroy");
         mFragments.doLoaderDestroy();
         if (mVoiceInteractor != null) {
             mVoiceInteractor.detachActivity();
@@ -7523,7 +7613,7 @@
 
     /** @hide */
     @Override
-    final public void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
+    public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
             Intent fillInIntent) {
         try {
             startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
@@ -7535,13 +7625,13 @@
 
     /** @hide */
     @Override
-    final public void autofillCallbackResetableStateAvailable() {
+    public final void autofillClientResetableStateAvailable() {
         mAutoFillResetNeeded = true;
     }
 
     /** @hide */
     @Override
-    final public boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width,
+    public final boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width,
             int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) {
         final boolean wasShowing;
 
@@ -7558,7 +7648,20 @@
 
     /** @hide */
     @Override
-    final public boolean autofillCallbackRequestHideFillUi() {
+    public final void autofillClientDispatchUnhandledKey(@NonNull View anchor,
+            @NonNull KeyEvent keyEvent) {
+        ViewRootImpl rootImpl = anchor.getViewRootImpl();
+        if (rootImpl != null) {
+            // dont care if anchorView is current focus, for example a custom view may only receive
+            // touchEvent, not focusable but can still trigger autofill window. The Key handling
+            // might be inside parent of the custom view.
+            rootImpl.dispatchKeyFromAutofill(keyEvent);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public final boolean autofillClientRequestHideFillUi() {
         if (mAutofillPopupWindow == null) {
             return false;
         }
@@ -7569,8 +7672,16 @@
 
     /** @hide */
     @Override
-    @NonNull public View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds) {
-        final View[] views = new View[viewIds.length];
+    public final boolean autofillClientIsFillUiShowing() {
+        return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing();
+    }
+
+    /** @hide */
+    @Override
+    @NonNull
+    public final View[] autofillClientFindViewsByAutofillIdTraversal(
+            @NonNull AutofillId[] autofillId) {
+        final View[] views = new View[autofillId.length];
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
 
@@ -7578,10 +7689,11 @@
             final View rootView = roots.get(rootNum).getView();
 
             if (rootView != null) {
-                for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
+                final int viewCount = autofillId.length;
+                for (int viewNum = 0; viewNum < viewCount; viewNum++) {
                     if (views[viewNum] == null) {
                         views[viewNum] = rootView.findViewByAutofillIdTraversal(
-                                viewIds[viewNum]);
+                                autofillId[viewNum].getViewId());
                     }
                 }
             }
@@ -7592,14 +7704,15 @@
 
     /** @hide */
     @Override
-    @Nullable public View findViewByAutofillIdTraversal(int viewId) {
+    @Nullable
+    public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
         for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
             final View rootView = roots.get(rootNum).getView();
 
             if (rootView != null) {
-                final View view = rootView.findViewByAutofillIdTraversal(viewId);
+                final View view = rootView.findViewByAutofillIdTraversal(autofillId.getViewId());
                 if (view != null) {
                     return view;
                 }
@@ -7611,50 +7724,65 @@
 
     /** @hide */
     @Override
-    @NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
-        final boolean[] isVisible = new boolean[viewIds.length];
-        final View views[] = findViewsByAutofillIdTraversal(viewIds);
-
-        for (int i = 0; i < viewIds.length; i++) {
-            View view = views[i];
-            if (view == null) {
-                isVisible[i] = false;
-                continue;
-            }
-
-            isVisible[i] = true;
-
-            // Check if the view is visible by checking all parents
-            while (true) {
-                if (view instanceof DecorView && view.getViewRootImpl() == view.getParent()) {
-                    break;
-                }
-
-                if (view.getVisibility() != View.VISIBLE) {
-                    isVisible[i] = false;
-                    break;
-                }
-
-                if (view.getParent() instanceof View) {
-                    view = (View) view.getParent();
+    public final @NonNull boolean[] autofillClientGetViewVisibility(
+            @NonNull AutofillId[] autofillIds) {
+        final int autofillIdCount = autofillIds.length;
+        final boolean[] visible = new boolean[autofillIdCount];
+        for (int i = 0; i < autofillIdCount; i++) {
+            final AutofillId autofillId = autofillIds[i];
+            final View view = autofillClientFindViewByAutofillIdTraversal(autofillId);
+            if (view != null) {
+                if (!autofillId.isVirtual()) {
+                    visible[i] = view.isVisibleToUser();
                 } else {
-                    break;
+                    visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildId());
                 }
             }
         }
+        if (android.view.autofill.Helper.sVerbose) {
+            Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible));
+        }
+        return visible;
+    }
 
-        return isVisible;
+    /** @hide */
+    public final @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId,
+            int windowId) {
+        final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance()
+                .getRootViews(getActivityToken());
+        for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+            final View rootView = roots.get(rootNum).getView();
+            if (rootView != null && rootView.getAccessibilityWindowId() == windowId) {
+                final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
+                if (view != null) {
+                    return view;
+                }
+            }
+        }
+        return null;
     }
 
     /** @hide */
     @Override
-    public boolean isVisibleForAutofill() {
+    public final @Nullable IBinder autofillClientGetActivityToken() {
+        return getActivityToken();
+    }
+
+    /** @hide */
+    @Override
+    public final boolean autofillClientIsVisibleForAutofill() {
         return !mStopped;
     }
 
     /** @hide */
     @Override
-    public boolean isDisablingEnterExitEventForAutofill() {
+    public final boolean autofillClientIsCompatibilityModeEnabled() {
+        return isAutofillCompatibilityEnabled();
+    }
+
+    /** @hide */
+    @Override
+    public final boolean isDisablingEnterExitEventForAutofill() {
         return mAutoFillIgnoreFirstResumePause || !mResumed;
     }
 
@@ -7676,7 +7804,6 @@
      * @param disable {@code true} to disable preview screenshots; {@code false} otherwise.
      * @hide
      */
-    @SystemApi
     public void setDisablePreviewScreenshots(boolean disable) {
         try {
             ActivityManager.getService().setDisablePreviewScreenshots(mToken, disable);
@@ -7748,6 +7875,12 @@
         }
     }
 
+    /** Log a lifecycle event for current user id and component class. */
+    private void writeEventLog(int event, String reason) {
+        EventLog.writeEvent(event, UserHandle.myUserId(), getComponentName().getClassName(),
+                reason);
+    }
+
     class HostCallbacks extends FragmentHostCallback<Activity> {
         public HostCallbacks() {
             super(Activity.this /*activity*/);
diff --git a/android/app/ActivityManager.java b/android/app/ActivityManager.java
index 8035058..289a4dd 100644
--- a/android/app/ActivityManager.java
+++ b/android/app/ActivityManager.java
@@ -28,7 +28,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.UriPermission;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
@@ -71,6 +70,7 @@
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.MemInfoReader;
 import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlSerializer;
@@ -450,31 +450,6 @@
      */
     public static final int INTENT_SENDER_FOREGROUND_SERVICE = 5;
 
-    /**
-     * Extra included on intents that are delegating the call to
-     * ActivityManager#startActivityAsCaller to another app.  This token is necessary for that call
-     * to succeed.  Type is IBinder.
-     * @hide
-     */
-    public static final String EXTRA_PERMISSION_TOKEN = "android.app.extra.PERMISSION_TOKEN";
-
-    /**
-     * Extra included on intents that contain an EXTRA_INTENT, with options that the contained
-     * intent may want to be started with.  Type is Bundle.
-     * TODO: remove once the ChooserActivity moves to systemui
-     * @hide
-     */
-    public static final String EXTRA_OPTIONS = "android.app.extra.OPTIONS";
-
-    /**
-     * Extra included on intents that contain an EXTRA_INTENT, use this boolean value for the
-     * parameter of the same name when starting the contained intent.
-     * TODO: remove once the ChooserActivity moves to systemui
-     * @hide
-     */
-    public static final String EXTRA_IGNORE_TARGET_SECURITY =
-            "android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
-
     /** @hide User operation call: success! */
     public static final int USER_OP_SUCCESS = 0;
 
@@ -576,18 +551,68 @@
     /** @hide Process does not exist. */
     public static final int PROCESS_STATE_NONEXISTENT = 19;
 
-    // NOTE: If PROCESS_STATEs are added or changed, then new fields must be added
-    // to frameworks/base/core/proto/android/app/activitymanager.proto and the following method must
+    // NOTE: If PROCESS_STATEs are added, then new fields must be added
+    // to frameworks/base/core/proto/android/app/enums.proto and the following method must
     // be updated to correctly map between them.
+    // However, if the current ActivityManager values are merely modified, no update should be made
+    // to enums.proto, to which values can only be added but never modified. Note that the proto
+    // versions do NOT have the ordering restrictions of the ActivityManager process state.
     /**
-     * Maps ActivityManager.PROCESS_STATE_ values to ProcessState enum.
+     * Maps ActivityManager.PROCESS_STATE_ values to enums.proto ProcessStateEnum value.
      *
      * @param amInt a process state of the form ActivityManager.PROCESS_STATE_
-     * @return the value of the corresponding ActivityManager's ProcessState enum.
+     * @return the value of the corresponding enums.proto ProcessStateEnum value.
      * @hide
      */
     public static final int processStateAmToProto(int amInt) {
-        return amInt * 100;
+        switch (amInt) {
+            case PROCESS_STATE_UNKNOWN:
+                return AppProtoEnums.PROCESS_STATE_UNKNOWN;
+            case PROCESS_STATE_PERSISTENT:
+                return AppProtoEnums.PROCESS_STATE_PERSISTENT;
+            case PROCESS_STATE_PERSISTENT_UI:
+                return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
+            case PROCESS_STATE_TOP:
+                return AppProtoEnums.PROCESS_STATE_TOP;
+            case PROCESS_STATE_FOREGROUND_SERVICE:
+                return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
+            case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+                return AppProtoEnums.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+            case PROCESS_STATE_IMPORTANT_FOREGROUND:
+                return AppProtoEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            case PROCESS_STATE_IMPORTANT_BACKGROUND:
+                return AppProtoEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
+            case PROCESS_STATE_TRANSIENT_BACKGROUND:
+                return AppProtoEnums.PROCESS_STATE_TRANSIENT_BACKGROUND;
+            case PROCESS_STATE_BACKUP:
+                return AppProtoEnums.PROCESS_STATE_BACKUP;
+            case PROCESS_STATE_SERVICE:
+                return AppProtoEnums.PROCESS_STATE_SERVICE;
+            case PROCESS_STATE_RECEIVER:
+                return AppProtoEnums.PROCESS_STATE_RECEIVER;
+            case PROCESS_STATE_TOP_SLEEPING:
+                return AppProtoEnums.PROCESS_STATE_TOP_SLEEPING;
+            case PROCESS_STATE_HEAVY_WEIGHT:
+                return AppProtoEnums.PROCESS_STATE_HEAVY_WEIGHT;
+            case PROCESS_STATE_HOME:
+                return AppProtoEnums.PROCESS_STATE_HOME;
+            case PROCESS_STATE_LAST_ACTIVITY:
+                return AppProtoEnums.PROCESS_STATE_LAST_ACTIVITY;
+            case PROCESS_STATE_CACHED_ACTIVITY:
+                return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY;
+            case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+            case PROCESS_STATE_CACHED_RECENT:
+                return AppProtoEnums.PROCESS_STATE_CACHED_RECENT;
+            case PROCESS_STATE_CACHED_EMPTY:
+                return AppProtoEnums.PROCESS_STATE_CACHED_EMPTY;
+            case PROCESS_STATE_NONEXISTENT:
+                return AppProtoEnums.PROCESS_STATE_NONEXISTENT;
+            default:
+                // ActivityManager process state (amInt)
+                // could not be mapped to an AppProtoEnums ProcessState state.
+                return AppProtoEnums.PROCESS_STATE_UNKNOWN_TO_PROTO;
+        }
     }
 
     /** @hide The lowest process state number */
@@ -934,8 +959,21 @@
      * @hide
      */
     static public boolean isHighEndGfx() {
-        return !isLowRamDeviceStatic() &&
-                !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel);
+        return !isLowRamDeviceStatic()
+                && !RoSystemProperties.CONFIG_AVOID_GFX_ACCEL
+                && !Resources.getSystem()
+                        .getBoolean(com.android.internal.R.bool.config_avoidGfxAccel);
+    }
+
+    /**
+     * Return the total number of bytes of RAM this device has.
+     * @hide
+     */
+    @TestApi
+    public long getTotalRam() {
+        MemInfoReader memreader = new MemInfoReader();
+        memreader.readMemInfo();
+        return memreader.getTotalSize();
     }
 
     /**
@@ -1667,7 +1705,7 @@
             if (maxNum < 0) {
                 throw new IllegalArgumentException("The requested number of tasks should be >= 0");
             }
-            return getService().getRecentTasks(maxNum, flags, UserHandle.myUserId()).getList();
+            return getService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2065,15 +2103,17 @@
         private final int mOrientation;
         private final Rect mContentInsets;
         private final boolean mReducedResolution;
+        private final boolean mIsRealSnapshot;
         private final float mScale;
 
         public TaskSnapshot(GraphicBuffer snapshot, int orientation, Rect contentInsets,
-                boolean reducedResolution, float scale) {
+                boolean reducedResolution, float scale, boolean isRealSnapshot) {
             mSnapshot = snapshot;
             mOrientation = orientation;
             mContentInsets = new Rect(contentInsets);
             mReducedResolution = reducedResolution;
             mScale = scale;
+            mIsRealSnapshot = isRealSnapshot;
         }
 
         private TaskSnapshot(Parcel source) {
@@ -2082,6 +2122,7 @@
             mContentInsets = source.readParcelable(null /* classLoader */);
             mReducedResolution = source.readBoolean();
             mScale = source.readFloat();
+            mIsRealSnapshot = source.readBoolean();
         }
 
         /**
@@ -2114,6 +2155,14 @@
         }
 
         /**
+         * @return Whether or not the snapshot is a real snapshot or an app-theme generated snapshot
+         * due to the task having a secure window or having previews disabled.
+         */
+        public boolean isRealSnapshot() {
+            return mIsRealSnapshot;
+        }
+
+        /**
          * @return The scale this snapshot was taken in.
          */
         public float getScale() {
@@ -2132,13 +2181,15 @@
             dest.writeParcelable(mContentInsets, 0);
             dest.writeBoolean(mReducedResolution);
             dest.writeFloat(mScale);
+            dest.writeBoolean(mIsRealSnapshot);
         }
 
         @Override
         public String toString() {
             return "TaskSnapshot{mSnapshot=" + mSnapshot + " mOrientation=" + mOrientation
                     + " mContentInsets=" + mContentInsets.toShortString()
-                    + " mReducedResolution=" + mReducedResolution + " mScale=" + mScale;
+                    + " mReducedResolution=" + mReducedResolution + " mScale=" + mScale
+                    + " mIsRealSnapshot=" + mIsRealSnapshot;
         }
 
         public static final Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
@@ -2661,7 +2712,7 @@
     public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
         try {
             return getService().clearApplicationUserData(packageName, false,
-                    observer, UserHandle.myUserId());
+                    observer, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2686,17 +2737,22 @@
     /**
      * Permits an application to get the persistent URI permissions granted to another.
      *
-     * <p>Typically called by Settings.
+     * <p>Typically called by Settings or DocumentsUI, requires
+     * {@code GET_APP_GRANTED_URI_PERMISSIONS}.
      *
-     * @param packageName application to look for the granted permissions
+     * @param packageName application to look for the granted permissions, or {@code null} to get
+     * granted permissions for all applications
      * @return list of granted URI permissions
      *
      * @hide
      */
-    public ParceledListSlice<UriPermission> getGrantedUriPermissions(String packageName) {
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName) {
         try {
-            return getService().getGrantedUriPermissions(packageName,
-                    UserHandle.myUserId());
+            @SuppressWarnings("unchecked")
+            final ParceledListSlice<GrantedUriPermission> castedList = getService()
+                    .getGrantedUriPermissions(packageName, mContext.getUserId());
+            return castedList;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2705,7 +2761,7 @@
     /**
      * Permits an application to clear the persistent URI permissions granted to another.
      *
-     * <p>Typically called by Settings.
+     * <p>Typically called by Settings, requires {@code CLEAR_APP_GRANTED_URI_PERMISSIONS}.
      *
      * @param packageName application to clear its granted permissions
      *
@@ -2714,7 +2770,7 @@
     public void clearGrantedUriPermissions(String packageName) {
         try {
             getService().clearGrantedUriPermissions(packageName,
-                    UserHandle.myUserId());
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3281,6 +3337,28 @@
     }
 
     /**
+     * Query whether the user has enabled background restrictions for this app.
+     *
+     * <p> The user may chose to do this, if they see that an app is consuming an unreasonable
+     * amount of battery while in the background. </p>
+     *
+     * <p> If true, any work that the app tries to do will be aggressively restricted while it is in
+     * the background. At a minimum, jobs and alarms will not execute and foreground services
+     * cannot be started unless an app activity is in the foreground. </p>
+     *
+     * <p><b> Note that these restrictions stay in effect even when the device is charging.</b></p>
+     *
+     * @return true if user has enforced background restrictions for this app, false otherwise.
+     */
+    public boolean isBackgroundRestricted() {
+        try {
+            return getService().isBackgroundRestricted(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Sets the memory trim mode for a process and schedules a memory trim operation.
      *
      * <p><b>Note: this method is only intended for testing framework.</b></p>
@@ -3499,7 +3577,7 @@
     public void killBackgroundProcesses(String packageName) {
         try {
             getService().killBackgroundProcesses(packageName,
-                    UserHandle.myUserId());
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3557,7 +3635,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.FORCE_STOP_PACKAGES)
     public void forceStopPackage(String packageName) {
-        forceStopPackageAsUser(packageName, UserHandle.myUserId());
+        forceStopPackageAsUser(packageName, mContext.getUserId());
     }
 
     /**
@@ -3669,6 +3747,24 @@
     }
 
     /**
+     * Unsupported compiled sdk warning should always be shown for the intput activity
+     * even in cases where the system would normally not show the warning. E.g. when running in a
+     * test harness.
+     *
+     * @param activity The component name of the activity to always show the warning for.
+     *
+     * @hide
+     */
+    @TestApi
+    public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
+        try {
+            getService().alwaysShowUnsupportedCompileSdkWarning(activity);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the launch count of each installed package.
      *
      * @hide
diff --git a/android/app/ActivityManagerInternal.java b/android/app/ActivityManagerInternal.java
index da9f728..db9d923 100644
--- a/android/app/ActivityManagerInternal.java
+++ b/android/app/ActivityManagerInternal.java
@@ -27,6 +27,7 @@
 import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.SparseIntArray;
+import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.app.IVoiceInteractor;
 
@@ -43,25 +44,36 @@
      * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
      * the splash screen.
      */
-    public static final int APP_TRANSITION_SPLASH_SCREEN = 1;
+    public static final int APP_TRANSITION_SPLASH_SCREEN =
+              AppProtoEnums.APP_TRANSITION_SPLASH_SCREEN; // 1
 
     /**
      * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
      * app windows were drawn
      */
-    public static final int APP_TRANSITION_WINDOWS_DRAWN = 2;
+    public static final int APP_TRANSITION_WINDOWS_DRAWN =
+              AppProtoEnums.APP_TRANSITION_WINDOWS_DRAWN; // 2
 
     /**
      * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
      * timeout.
      */
-    public static final int APP_TRANSITION_TIMEOUT = 3;
+    public static final int APP_TRANSITION_TIMEOUT =
+              AppProtoEnums.APP_TRANSITION_TIMEOUT; // 3
 
     /**
      * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
      * we drew a task snapshot.
      */
-    public static final int APP_TRANSITION_SNAPSHOT = 4;
+    public static final int APP_TRANSITION_SNAPSHOT =
+              AppProtoEnums.APP_TRANSITION_SNAPSHOT; // 4
+
+    /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because it was a
+     * recents animation and we only needed to wait on the wallpaper.
+     */
+    public static final int APP_TRANSITION_RECENTS_ANIM =
+            AppProtoEnums.APP_TRANSITION_RECENTS_ANIM; // 5
 
     /**
      * The bundle key to extract the assist data.
@@ -150,8 +162,8 @@
      * Callback for window manager to let activity manager know that we are finally starting the
      * app transition;
      *
-     * @param reasons A map from stack id to a reason integer why the transition was started,, which
-     *                must be one of the APP_TRANSITION_* values.
+     * @param reasons A map from windowing mode to a reason integer why the transition was started,
+     *                which must be one of the APP_TRANSITION_* values.
      * @param timestamp The time at which the app transition started in
      *                  {@link SystemClock#uptimeMillis()} timebase.
      */
@@ -215,6 +227,9 @@
     /**
      * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
      *
+     * - DO NOT call it with the calling UID cleared.
+     * - All the necessary caller permission checks must be done at callsites.
+     *
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
     public abstract int startActivitiesAsPackage(String packageName,
@@ -257,6 +272,17 @@
     public abstract void setHasOverlayUi(int pid, boolean hasOverlayUi);
 
     /**
+     * Sets if the given pid is currently running a remote animation, which is taken a signal for
+     * determining oom adjustment and scheduling behavior.
+     *
+     * @param pid The pid we are setting overlay UI for.
+     * @param runningRemoteAnimation True if the process is running a remote animation, false
+     *                               otherwise.
+     * @see RemoteAnimationAdapter
+     */
+    public abstract void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation);
+
+    /**
      * Called after the network policy rules are updated by
      * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} and
      * {@param procStateSeq}.
@@ -344,4 +370,25 @@
      * Returns is the caller has the same uid as the Recents component
      */
     public abstract boolean isCallerRecents(int callingUid);
+
+    /**
+     * Returns whether the recents component is the home activity for the given user.
+     */
+    public abstract boolean isRecentsComponentHomeActivity(int userId);
+
+    /**
+     * Whether an UID is active or idle.
+     */
+    public abstract boolean isUidActive(int uid);
+
+    /**
+     * Returns a list that contains the memory stats for currently running processes.
+     */
+    public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
+
+    /**
+     * This enforces {@code func} can only be called if either the caller is Recents activity or
+     * has {@code permission}.
+     */
+    public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
 }
diff --git a/android/app/ActivityOptions.java b/android/app/ActivityOptions.java
index fee5827..09dcbf2 100644
--- a/android/app/ActivityOptions.java
+++ b/android/app/ActivityOptions.java
@@ -164,7 +164,7 @@
 
     /**
      * Whether the activity should be launched into LockTask mode.
-     * @see #setLockTaskMode(boolean)
+     * @see #setLockTaskEnabled(boolean)
      */
     private static final String KEY_LOCK_TASK_MODE = "android:activity.lockTaskMode";
 
@@ -1106,6 +1106,11 @@
     }
 
     /** @hide */
+    public void setRemoteAnimationAdapter(RemoteAnimationAdapter remoteAnimationAdapter) {
+        mRemoteAnimationAdapter = remoteAnimationAdapter;
+    }
+
+    /** @hide */
     public static ActivityOptions fromBundle(Bundle bOptions) {
         return bOptions != null ? new ActivityOptions(bOptions) : null;
     }
@@ -1143,7 +1148,7 @@
      * @see Activity#startLockTask()
      * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[])
      */
-    public ActivityOptions setLockTaskMode(boolean lockTaskMode) {
+    public ActivityOptions setLockTaskEnabled(boolean lockTaskMode) {
         mLockTaskMode = lockTaskMode;
         return this;
     }
diff --git a/android/app/ActivityThread.java b/android/app/ActivityThread.java
index 934b0f3..50a4398 100644
--- a/android/app/ActivityThread.java
+++ b/android/app/ActivityThread.java
@@ -30,12 +30,15 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
+import android.app.servertransaction.TransactionExecutorHelper;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -64,6 +67,7 @@
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.ImageDecoder;
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
@@ -113,6 +117,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.util.MergedConfiguration;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -142,7 +147,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.server.am.proto.MemInfoProto;
+import com.android.server.am.MemInfoDumpProto;
 
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.CloseGuard;
@@ -166,9 +171,9 @@
 import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.text.DateFormat;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -204,12 +209,9 @@
     private static final boolean DEBUG_SERVICE = false;
     public static final boolean DEBUG_MEMORY_TRIM = false;
     private static final boolean DEBUG_PROVIDER = false;
-    private static final boolean DEBUG_ORDER = false;
+    public static final boolean DEBUG_ORDER = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
-    private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
-    private static final int LOG_AM_ON_RESUME_CALLED = 30022;
-    private static final int LOG_AM_ON_STOP_CALLED = 30049;
 
     /** Type for IActivityManager.serviceDoneExecuting: anonymous operation */
     public static final int SERVICE_DONE_EXECUTING_ANON = 0;
@@ -222,7 +224,7 @@
     private static final boolean REPORT_TO_ACTIVITY = true;
 
     // Maximum number of recent tokens to maintain for debugging purposes
-    private static final int MAX_RECENT_TOKENS = 10;
+    private static final int MAX_DESTROYED_ACTIVITIES = 10;
 
     /**
      * Denotes an invalid sequence number corresponding to a process state change.
@@ -256,7 +258,7 @@
     final H mH = new H();
     final Executor mExecutor = new HandlerExecutor(mH);
     final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
-    final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>();
+    final ArrayList<DestroyedActivityInfo> mRecentDestroyedActivities = new ArrayList<>();
 
     // List of new activities (via ActivityRecord.nextIdle) that should
     // be reported when next we idle.
@@ -291,6 +293,7 @@
     boolean mJitEnabled = false;
     boolean mSomeActivitiesChanged = false;
     boolean mUpdatingSystemConfig = false;
+    /* package */ boolean mHiddenApiWarningShown = false;
 
     // These can be accessed by multiple threads; mResourcesManager is the lock.
     // XXX For now we keep around information about all packages we have
@@ -338,6 +341,26 @@
         }
     }
 
+    /**
+     * TODO(b/71506345): Remove this once bug is resolved.
+     */
+    private static final class DestroyedActivityInfo {
+        private final Integer mToken;
+        private final String mReason;
+        private final long mTime;
+
+        DestroyedActivityInfo(Integer token, String reason) {
+            mToken = token;
+            mReason = reason;
+            mTime = System.currentTimeMillis();
+        }
+
+        void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + "[token:" + mToken + " | time:" + mTime + " | reason:" + mReason
+                    + "]");
+        }
+    }
+
     // The lock of mProviderMap protects the following variables.
     final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
         = new ArrayMap<ProviderKey, ProviderClientRecord>();
@@ -389,7 +412,7 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        public LoadedApk loadedApk;
+        public LoadedApk packageInfo;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
@@ -397,7 +420,6 @@
         boolean startsNotResumed;
         public final boolean isForward;
         int pendingConfigChanges;
-        boolean onlyLocalRequest;
 
         Window mPendingRemoveWindow;
         WindowManager mPendingRemoveWindowManager;
@@ -432,7 +454,7 @@
             this.isForward = isForward;
             this.profilerInfo = profilerInfo;
             this.overrideConfig = overrideConfig;
-            this.loadedApk = client.getLoadedApkNoCheck(activityInfo.applicationInfo,
+            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
                     compatInfo);
             init();
         }
@@ -487,18 +509,24 @@
             }
         }
 
-        public boolean isPreHoneycomb() {
-            if (activity != null) {
-                return activity.getApplicationInfo().targetSdkVersion
-                        < android.os.Build.VERSION_CODES.HONEYCOMB;
-            }
-            return false;
+        private boolean isPreHoneycomb() {
+            return activity != null && activity.getApplicationInfo().targetSdkVersion
+                    < android.os.Build.VERSION_CODES.HONEYCOMB;
+        }
+
+        private boolean isPreP() {
+            return activity != null && activity.getApplicationInfo().targetSdkVersion
+                    < android.os.Build.VERSION_CODES.P;
         }
 
         public boolean isPersistable() {
             return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
+        public boolean isVisibleFromServer() {
+            return activity != null && activity.mVisibleFromServer;
+        }
+
         public String toString() {
             ComponentName componentName = intent != null ? intent.getComponent() : null;
             return "ActivityRecord{"
@@ -517,7 +545,6 @@
             sb.append(", startsNotResumed=").append(startsNotResumed);
             sb.append(", isForward=").append(isForward);
             sb.append(", pendingConfigChanges=").append(pendingConfigChanges);
-            sb.append(", onlyLocalRequest=").append(onlyLocalRequest);
             sb.append(", preserveWindow=").append(mPreserveWindow);
             if (activity != null) {
                 sb.append(", Activity{");
@@ -614,7 +641,7 @@
     }
 
     static final class AppBindData {
-        LoadedApk loadedApk;
+        LoadedApk info;
         String processName;
         ApplicationInfo appInfo;
         List<ProviderInfo> providers;
@@ -634,6 +661,8 @@
         /** Initial values for {@link Profiler}. */
         ProfilerInfo initProfilerInfo;
 
+        boolean autofillCompatibilityEnabled;
+
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
@@ -683,7 +712,7 @@
                         streamingOutput);
                 profiling = true;
             } catch (RuntimeException e) {
-                Slog.w(TAG, "Profiling failed on path " + profileFile);
+                Slog.w(TAG, "Profiling failed on path " + profileFile, e);
                 try {
                     profileFd.close();
                     profileFd = null;
@@ -760,15 +789,6 @@
             sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
         }
 
-        @Override
-        public final void scheduleRelaunchActivity(IBinder token,
-                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-                int configChanges, boolean notResumed, Configuration config,
-                Configuration overrideConfig, boolean preserveWindow) {
-            requestRelaunchActivity(token, pendingResults, pendingNewIntents,
-                    configChanges, notResumed, config, overrideConfig, true, preserveWindow);
-        }
-
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                 boolean sync, int sendingUser, int processState) {
@@ -860,7 +880,7 @@
                 boolean enableBinderTracking, boolean trackAllocation,
                 boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
-                String buildSerial) {
+                String buildSerial, boolean autofillCompatibilityEnabled) {
 
             if (services != null) {
                 // Setup the service cache in the ServiceManager
@@ -886,6 +906,7 @@
             data.compatInfo = compatInfo;
             data.initProfilerInfo = profilerInfo;
             data.buildSerial = buildSerial;
+            data.autofillCompatibilityEnabled = autofillCompatibilityEnabled;
             sendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -1238,55 +1259,62 @@
             long parcelCount = Parcel.getGlobalAllocCount();
             SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
 
-            final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
-            proto.write(MemInfoProto.ProcessMemory.PID, Process.myPid());
-            proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME,
+            final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY);
+            proto.write(MemInfoDumpProto.ProcessMemory.PID, Process.myPid());
+            proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME,
                     (mBoundApplication != null) ? mBoundApplication.processName : "unknown");
             dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly,
                     nativeMax, nativeAllocated, nativeFree,
                     dalvikMax, dalvikAllocated, dalvikFree);
             proto.end(mToken);
 
-            final long oToken = proto.start(MemInfoProto.AppData.OBJECTS);
-            proto.write(MemInfoProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, viewInstanceCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
+            final long oToken = proto.start(MemInfoDumpProto.AppData.OBJECTS);
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT,
+                    viewInstanceCount);
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
                     viewRootInstanceCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
                     appContextInstanceCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
                     activityInstanceCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, globalAssetCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT,
+                    globalAssetCount);
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
                     globalAssetManagerCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
                     binderLocalObjectCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
                     binderProxyObjectCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_MEMORY_KB, parcelSize / 1024);
-            proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_MEMORY_KB,
+                    parcelSize / 1024);
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
                     binderDeathObjectCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT, openSslSocketCount);
-            proto.write(MemInfoProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT,
+                    openSslSocketCount);
+            proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
                     webviewInstanceCount);
             proto.end(oToken);
 
             // SQLite mem info
-            final long sToken = proto.start(MemInfoProto.AppData.SQL);
-            proto.write(MemInfoProto.AppData.SqlStats.MEMORY_USED_KB, stats.memoryUsed / 1024);
-            proto.write(MemInfoProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
+            final long sToken = proto.start(MemInfoDumpProto.AppData.SQL);
+            proto.write(MemInfoDumpProto.AppData.SqlStats.MEMORY_USED_KB,
+                    stats.memoryUsed / 1024);
+            proto.write(MemInfoDumpProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
                     stats.pageCacheOverflow / 1024);
-            proto.write(MemInfoProto.AppData.SqlStats.MALLOC_SIZE_KB, stats.largestMemAlloc / 1024);
+            proto.write(MemInfoDumpProto.AppData.SqlStats.MALLOC_SIZE_KB,
+                    stats.largestMemAlloc / 1024);
             int n = stats.dbStats.size();
             for (int i = 0; i < n; i++) {
                 DbStats dbStats = stats.dbStats.get(i);
 
-                final long dToken = proto.start(MemInfoProto.AppData.SqlStats.DATABASES);
-                proto.write(MemInfoProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
-                proto.write(MemInfoProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
-                proto.write(MemInfoProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
-                proto.write(MemInfoProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside);
-                proto.write(MemInfoProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
+                final long dToken = proto.start(MemInfoDumpProto.AppData.SqlStats.DATABASES);
+                proto.write(MemInfoDumpProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
+                proto.write(MemInfoDumpProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
+                proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
+                proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B,
+                        dbStats.lookaside);
+                proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
                 proto.end(dToken);
             }
             proto.end(sToken);
@@ -1294,7 +1322,7 @@
             // Asset details.
             String assetAlloc = AssetManager.getAssetAllocations();
             if (assetAlloc != null) {
-                proto.write(MemInfoProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
+                proto.write(MemInfoDumpProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
             }
 
             // Unreachable native memory
@@ -1302,7 +1330,7 @@
                 int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags;
                 boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
                         || android.os.Build.IS_DEBUGGABLE;
-                proto.write(MemInfoProto.AppData.UNREACHABLE_MEMORY,
+                proto.write(MemInfoDumpProto.AppData.UNREACHABLE_MEMORY,
                         Debug.getUnreachableMemory(100, showContents));
             }
         }
@@ -1525,7 +1553,6 @@
         public static final int UNBIND_SERVICE          = 122;
         public static final int DUMP_SERVICE            = 123;
         public static final int LOW_MEMORY              = 124;
-        public static final int RELAUNCH_ACTIVITY       = 126;
         public static final int PROFILER_CONTROL        = 127;
         public static final int CREATE_BACKUP_AGENT     = 128;
         public static final int DESTROY_BACKUP_AGENT    = 129;
@@ -1571,7 +1598,6 @@
                     case UNBIND_SERVICE: return "UNBIND_SERVICE";
                     case DUMP_SERVICE: return "DUMP_SERVICE";
                     case LOW_MEMORY: return "LOW_MEMORY";
-                    case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
                     case PROFILER_CONTROL: return "PROFILER_CONTROL";
                     case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
                     case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
@@ -1605,12 +1631,6 @@
         public void handleMessage(Message msg) {
             if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
             switch (msg.what) {
-                case RELAUNCH_ACTIVITY: {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
-                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;
-                    handleRelaunchActivity(r);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                } break;
                 case BIND_APPLICATION:
                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                     AppBindData data = (AppBindData)msg.obj;
@@ -1784,6 +1804,7 @@
                         // message is handled.
                         transaction.recycle();
                     }
+                    // TODO(lifecycler): Recycle locally scheduled transactions.
                     break;
             }
             Object obj = msg.obj;
@@ -1913,13 +1934,13 @@
         return mH;
     }
 
-    public final LoadedApk getLoadedApkForPackageName(String packageName,
-            CompatibilityInfo compatInfo, int flags) {
-        return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId());
+    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
+            int flags) {
+        return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
     }
 
-    public final LoadedApk getLoadedApkForPackageName(String packageName,
-            CompatibilityInfo compatInfo, int flags, int userId) {
+    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
+            int flags, int userId) {
         final boolean differentUser = (UserHandle.myUserId() != userId);
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
@@ -1932,13 +1953,13 @@
                 ref = mResourcePackages.get(packageName);
             }
 
-            LoadedApk loadedApk = ref != null ? ref.get() : null;
-            //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk);
-            //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir
-            //        + ": " + loadedApk.mResources.getAssets().isUpToDate());
-            if (loadedApk != null && (loadedApk.mResources == null
-                    || loadedApk.mResources.getAssets().isUpToDate())) {
-                if (loadedApk.isSecurityViolation()
+            LoadedApk packageInfo = ref != null ? ref.get() : null;
+            //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
+            //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
+            //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
+            if (packageInfo != null && (packageInfo.mResources == null
+                    || packageInfo.mResources.getAssets().isUpToDate())) {
+                if (packageInfo.isSecurityViolation()
                         && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                     throw new SecurityException(
                             "Requesting code from " + packageName
@@ -1946,7 +1967,7 @@
                             + mBoundApplication.processName
                             + "/" + mBoundApplication.appInfo.uid);
                 }
-                return loadedApk;
+                return packageInfo;
             }
         }
 
@@ -1961,13 +1982,13 @@
         }
 
         if (ai != null) {
-            return getLoadedApk(ai, compatInfo, flags);
+            return getPackageInfo(ai, compatInfo, flags);
         }
 
         return null;
     }
 
-    public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo,
+    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
             int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
@@ -1989,17 +2010,17 @@
                 throw new SecurityException(msg);
             }
         }
-        return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode,
+        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
                 registerPackage);
     }
 
     @Override
-    public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
+    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
-        return getLoadedApk(ai, compatInfo, null, false, true, false);
+        return getPackageInfo(ai, compatInfo, null, false, true, false);
     }
 
-    public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) {
+    public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
@@ -2011,7 +2032,7 @@
         }
     }
 
-    private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
             boolean registerPackage) {
         final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
@@ -2026,35 +2047,35 @@
                 ref = mResourcePackages.get(aInfo.packageName);
             }
 
-            LoadedApk loadedApk = ref != null ? ref.get() : null;
-            if (loadedApk == null || (loadedApk.mResources != null
-                    && !loadedApk.mResources.getAssets().isUpToDate())) {
+            LoadedApk packageInfo = ref != null ? ref.get() : null;
+            if (packageInfo == null || (packageInfo.mResources != null
+                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                 if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                         : "Loading resource-only package ") + aInfo.packageName
                         + " (in " + (mBoundApplication != null
                                 ? mBoundApplication.processName : null)
                         + ")");
-                loadedApk =
+                packageInfo =
                     new LoadedApk(this, aInfo, compatInfo, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
 
                 if (mSystemThread && "android".equals(aInfo.packageName)) {
-                    loadedApk.installSystemApplicationInfo(aInfo,
-                            getSystemContext().mLoadedApk.getClassLoader());
+                    packageInfo.installSystemApplicationInfo(aInfo,
+                            getSystemContext().mPackageInfo.getClassLoader());
                 }
 
                 if (differentUser) {
                     // Caching not supported across users
                 } else if (includeCode) {
                     mPackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(loadedApk));
+                            new WeakReference<LoadedApk>(packageInfo));
                 } else {
                     mResourcePackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(loadedApk));
+                            new WeakReference<LoadedApk>(packageInfo));
                 }
             }
-            return loadedApk;
+            return packageInfo;
         }
     }
 
@@ -2176,14 +2197,28 @@
 
     @Override
     public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "mActivities:");
+        pw.println(prefix + "Activities:");
 
-        for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
-            pw.println(prefix + "  [token:" + entry.getKey().hashCode() + " record:"
-                    + entry.getValue().toString() + "]");
+        if (!mActivities.isEmpty()) {
+            final Iterator<Map.Entry<IBinder, ActivityClientRecord>> activitiesIterator =
+                    mActivities.entrySet().iterator();
+
+            while (activitiesIterator.hasNext()) {
+                final ArrayMap.Entry<IBinder, ActivityClientRecord> entry =
+                        activitiesIterator.next();
+                pw.println(prefix + "  [token:" + entry.getKey().hashCode() + " record:"
+                        + entry.getValue().toString() + "]");
+            }
         }
 
-        pw.println(prefix + "mRecentTokens:" + mRecentTokens);
+        if (!mRecentDestroyedActivities.isEmpty()) {
+            pw.println(prefix + "Recent destroyed activities:");
+            for (int i = 0, size = mRecentDestroyedActivities.size(); i < size; i++) {
+                final DestroyedActivityInfo info = mRecentDestroyedActivities.get(i);
+                pw.print(prefix);
+                info.dump(pw, "  ");
+            }
+        }
     }
 
     public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
@@ -2486,17 +2521,17 @@
             boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss) {
         final long token = proto.start(fieldId);
 
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.NAME, name);
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
-        proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.NAME, name);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
+        proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
         if (hasSwappedOutPss) {
-            proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
+            proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
         } else {
-            proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
+            proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
         }
 
         proto.end(token);
@@ -2511,26 +2546,26 @@
             long dalvikMax, long dalvikAllocated, long dalvikFree) {
 
         if (!dumpSummaryOnly) {
-            final long nhToken = proto.start(MemInfoProto.ProcessMemory.NATIVE_HEAP);
-            dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
+            final long nhToken = proto.start(MemInfoDumpProto.ProcessMemory.NATIVE_HEAP);
+            dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
                     memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
                     memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
                     memInfo.nativePrivateClean, memInfo.hasSwappedOutPss,
                     memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
             proto.end(nhToken);
 
-            final long dvToken = proto.start(MemInfoProto.ProcessMemory.DALVIK_HEAP);
-            dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
+            final long dvToken = proto.start(MemInfoDumpProto.ProcessMemory.DALVIK_HEAP);
+            dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
                     memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
                     memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
                     memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss,
                     memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
             proto.end(dvToken);
 
             int otherPss = memInfo.otherPss;
@@ -2554,7 +2589,7 @@
                 if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
                         || mySharedClean != 0 || myPrivateClean != 0
                         || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
-                    dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.OTHER_HEAPS,
+                    dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.OTHER_HEAPS,
                             Debug.MemoryInfo.getOtherLabel(i),
                             myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
                             mySharedClean, myPrivateClean,
@@ -2571,21 +2606,23 @@
                 }
             }
 
-            dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
+            dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
                     otherPss, otherSwappablePss,
                     otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
                     memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss);
-            final long tToken = proto.start(MemInfoProto.ProcessMemory.TOTAL_HEAP);
-            dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
+            final long tToken = proto.start(MemInfoDumpProto.ProcessMemory.TOTAL_HEAP);
+            dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
                     memInfo.getTotalPss(), memInfo.getTotalSwappablePss(),
                     memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
                     memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
                     memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(),
                     memInfo.getTotalSwappedOutPss());
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB,
+                    nativeMax + dalvikMax);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
                     nativeAllocated + dalvikAllocated);
-            proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree);
+            proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB,
+                    nativeFree + dalvikFree);
             proto.end(tToken);
 
             if (dumpDalvik) {
@@ -2603,7 +2640,7 @@
                     if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
                             || mySharedClean != 0 || myPrivateClean != 0
                             || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
-                        dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.DALVIK_DETAILS,
+                        dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.DALVIK_DETAILS,
                                 Debug.MemoryInfo.getOtherLabel(i),
                                 myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
                                 mySharedClean, myPrivateClean,
@@ -2613,24 +2650,26 @@
             }
         }
 
-        final long asToken = proto.start(MemInfoProto.ProcessMemory.APP_SUMMARY);
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
+        final long asToken = proto.start(MemInfoDumpProto.ProcessMemory.APP_SUMMARY);
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
                 memInfo.getSummaryJavaHeap());
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
                 memInfo.getSummaryNativeHeap());
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode());
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack());
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.CODE_PSS_KB,
+                memInfo.getSummaryCode());
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.STACK_PSS_KB,
+                memInfo.getSummaryStack());
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
                 memInfo.getSummaryGraphics());
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
                 memInfo.getSummaryPrivateOther());
-        proto.write(MemInfoProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
+        proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
                 memInfo.getSummarySystem());
         if (memInfo.hasSwappedOutPss) {
-            proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
+            proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
                     memInfo.getSummaryTotalSwapPss());
         } else {
-            proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
+            proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
                     memInfo.getSummaryTotalSwap());
         }
         proto.end(asToken);
@@ -2724,6 +2763,11 @@
         }
     }
 
+    @Override
+    TransactionExecutor getTransactionExecutor() {
+        return mTransactionExecutor;
+    }
+
     void sendMessage(int what, Object obj) {
         sendMessage(what, obj, 0, 0, false);
     }
@@ -2778,8 +2822,8 @@
     /**  Core implementation of activity launch. */
     private Activity performLaunchActivity(ActivityClientRecord r) {
         ActivityInfo aInfo = r.activityInfo;
-        if (r.loadedApk == null) {
-            r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
+        if (r.packageInfo == null) {
+            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -2816,15 +2860,15 @@
         }
 
         try {
-            Application app = r.loadedApk.makeApplication(false, mInstrumentation);
+            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
 
             if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
             if (localLOGV) Slog.v(
                     TAG, r + ": app=" + app
                     + ", appName=" + app.getPackageName()
-                    + ", pkg=" + r.loadedApk.getPackageName()
+                    + ", pkg=" + r.packageInfo.getPackageName()
                     + ", comp=" + r.intent.getComponent().toShortString()
-                    + ", dir=" + r.loadedApk.getAppDir());
+                    + ", dir=" + r.packageInfo.getAppDir());
 
             if (activity != null) {
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2870,11 +2914,6 @@
             r.setState(ON_CREATE);
 
             mActivities.put(r.token, r);
-            mRecentTokens.push(r.token.hashCode());
-
-            if (mRecentTokens.size() > MAX_RECENT_TOKENS) {
-                mRecentTokens.removeLast();
-            }
 
         } catch (SuperNotCalledException e) {
             throw e;
@@ -2907,7 +2946,7 @@
         }
 
         // Start
-        activity.performStart();
+        activity.performStart("handleStartActivity");
         r.setState(ON_START);
 
         if (pendingActions == null) {
@@ -2969,7 +3008,7 @@
         }
 
         ContextImpl appContext = ContextImpl.createActivityContext(
-                this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
+                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
 
         final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
         // For debugging purposes, if the activity's package name contains the value of
@@ -2977,7 +3016,7 @@
         // its content on a secondary display if there is one.
         String pkgName = SystemProperties.get("debug.second-display.pkg");
         if (pkgName != null && !pkgName.isEmpty()
-                && r.loadedApk.mPackageName.contains(pkgName)) {
+                && r.packageInfo.mPackageName.contains(pkgName)) {
             for (int id : dm.getDisplayIds()) {
                 if (id != Display.DEFAULT_DISPLAY) {
                     Display display =
@@ -3096,7 +3135,7 @@
         checkAndBlockForNetworkAccess();
         deliverNewIntents(r, intents);
         if (resumed) {
-            r.activity.performResume(false);
+            r.activity.performResume(false, "performNewIntents");
             r.activity.mTemporaryPause = false;
         }
 
@@ -3309,7 +3348,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk loadedApk = getLoadedApkNoCheck(
+        LoadedApk packageInfo = getPackageInfoNoCheck(
                 data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManager.getService();
@@ -3318,7 +3357,7 @@
         BroadcastReceiver receiver;
         ContextImpl context;
         try {
-            app = loadedApk.makeApplication(false, mInstrumentation);
+            app = packageInfo.makeApplication(false, mInstrumentation);
             context = (ContextImpl) app.getBaseContext();
             if (data.info.splitName != null) {
                 context = (ContextImpl) context.createContextForSplit(data.info.splitName);
@@ -3327,7 +3366,7 @@
             data.intent.setExtrasClassLoader(cl);
             data.intent.prepareToEnterProcess();
             data.setExtrasClassLoader(cl);
-            receiver = loadedApk.getAppFactory()
+            receiver = packageInfo.getAppFactory()
                     .instantiateReceiver(cl, data.info.name, data.intent);
         } catch (Exception e) {
             if (DEBUG_BROADCAST) Slog.i(TAG,
@@ -3343,9 +3382,9 @@
                 TAG, "Performing receive of " + data.intent
                 + ": app=" + app
                 + ", appName=" + app.getPackageName()
-                + ", pkg=" + loadedApk.getPackageName()
+                + ", pkg=" + packageInfo.getPackageName()
                 + ", comp=" + data.intent.getComponent().toShortString()
-                + ", dir=" + loadedApk.getAppDir());
+                + ", dir=" + packageInfo.getAppDir());
 
             sCurrentBroadcastIntent.set(data.intent);
             receiver.setPendingResult(data);
@@ -3390,8 +3429,8 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
-        String packageName = loadedApk.mPackageName;
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        String packageName = packageInfo.mPackageName;
         if (packageName == null) {
             Slog.d(TAG, "Asked to create backup agent for nonexistent package");
             return;
@@ -3417,11 +3456,11 @@
                 try {
                     if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
 
-                    java.lang.ClassLoader cl = loadedApk.getClassLoader();
+                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
                     agent = (BackupAgent) cl.loadClass(classname).newInstance();
 
                     // set up the agent's context
-                    ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
+                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                     context.setOuterContext(agent);
                     agent.attach(context);
 
@@ -3457,8 +3496,8 @@
     private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
-        String packageName = loadedApk.mPackageName;
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        String packageName = packageInfo.mPackageName;
         BackupAgent agent = mBackupAgents.get(packageName);
         if (agent != null) {
             try {
@@ -3478,12 +3517,12 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
-        LoadedApk loadedApk = getLoadedApkNoCheck(
+        LoadedApk packageInfo = getPackageInfoNoCheck(
                 data.info.applicationInfo, data.compatInfo);
         Service service = null;
         try {
-            java.lang.ClassLoader cl = loadedApk.getClassLoader();
-            service = loadedApk.getAppFactory()
+            java.lang.ClassLoader cl = packageInfo.getClassLoader();
+            service = packageInfo.getAppFactory()
                     .instantiateService(cl, data.info.name, data.intent);
         } catch (Exception e) {
             if (!mInstrumentation.onException(service, e)) {
@@ -3496,10 +3535,10 @@
         try {
             if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
 
-            ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
+            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
             context.setOuterContext(service);
 
-            Application app = loadedApk.makeApplication(false, mInstrumentation);
+            Application app = packageInfo.makeApplication(false, mInstrumentation);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             service.onCreate();
@@ -3697,56 +3736,64 @@
         //Slog.i(TAG, "Running services: " + mServices);
     }
 
-    ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide, String reason) {
-        ActivityClientRecord r = mActivities.get(token);
-        if (localLOGV) Slog.v(TAG, "Performing resume of " + r
-                + " finished=" + r.activity.mFinished);
-        if (r != null && !r.activity.mFinished) {
-            if (clearHide) {
-                r.hideForNow = false;
-                r.activity.mStartedActivity = false;
+    /**
+     * Resume the activity.
+     * @param token Target activity token.
+     * @param finalStateRequest Flag indicating if this is part of final state resolution for a
+     *                          transaction.
+     * @param reason Reason for performing the action.
+     *
+     * @return The {@link ActivityClientRecord} that was resumed, {@code null} otherwise.
+     */
+    @VisibleForTesting
+    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
+            String reason) {
+        final ActivityClientRecord r = mActivities.get(token);
+        if (localLOGV) {
+            Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished);
+        }
+        if (r == null || r.activity.mFinished) {
+            return null;
+        }
+        if (r.getLifecycleState() == ON_RESUME) {
+            if (!finalStateRequest) {
+                final RuntimeException e = new IllegalStateException(
+                        "Trying to resume activity which is already resumed");
+                Slog.e(TAG, e.getMessage(), e);
+                Slog.e(TAG, r.getStateString());
+                // TODO(lifecycler): A double resume request is possible when an activity
+                // receives two consequent transactions with relaunch requests and "resumed"
+                // final state requests and the second relaunch is omitted. We still try to
+                // handle two resume requests for the final state. For cases other than this
+                // one, we don't expect it to happen.
             }
-            try {
-                r.activity.onStateNotSaved();
-                r.activity.mFragments.noteStateNotSaved();
-                checkAndBlockForNetworkAccess();
-                if (r.pendingIntents != null) {
-                    deliverNewIntents(r, r.pendingIntents);
-                    r.pendingIntents = null;
-                }
-                if (r.pendingResults != null) {
-                    deliverResults(r, r.pendingResults);
-                    r.pendingResults = null;
-                }
-                r.activity.performResume(r.startsNotResumed);
+            return null;
+        }
+        if (finalStateRequest) {
+            r.hideForNow = false;
+            r.activity.mStartedActivity = false;
+        }
+        try {
+            r.activity.onStateNotSaved();
+            r.activity.mFragments.noteStateNotSaved();
+            checkAndBlockForNetworkAccess();
+            if (r.pendingIntents != null) {
+                deliverNewIntents(r, r.pendingIntents);
+                r.pendingIntents = null;
+            }
+            if (r.pendingResults != null) {
+                deliverResults(r, r.pendingResults);
+                r.pendingResults = null;
+            }
+            r.activity.performResume(r.startsNotResumed, reason);
 
-                synchronized (mResourcesManager) {
-                    // If there is a pending local relaunch that was requested when the activity was
-                    // paused, it will put the activity into paused state when it finally happens.
-                    // Since the activity resumed before being relaunched, we don't want that to
-                    // happen, so we need to clear the request to relaunch paused.
-                    for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
-                        final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
-                        if (relaunching.token == r.token
-                                && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
-                            relaunching.startsNotResumed = false;
-                        }
-                    }
-                }
-
-                EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), reason);
-
-                r.state = null;
-                r.persistentState = null;
-                r.setState(ON_RESUME);
-            } catch (Exception e) {
-                if (!mInstrumentation.onException(r.activity, e)) {
-                    throw new RuntimeException(
-                        "Unable to resume activity "
-                        + r.intent.getComponent().toShortString()
-                        + ": " + e.toString(), e);
-                }
+            r.state = null;
+            r.persistentState = null;
+            r.setState(ON_RESUME);
+        } catch (Exception e) {
+            if (!mInstrumentation.onException(r.activity, e)) {
+                throw new RuntimeException("Unable to resume activity "
+                        + r.intent.getComponent().toShortString() + ": " + e.toString(), e);
             }
         }
         return r;
@@ -3770,7 +3817,7 @@
     }
 
     @Override
-    public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
+    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
             String reason) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -3778,190 +3825,121 @@
         mSomeActivitiesChanged = true;
 
         // TODO Push resumeArgs into the activity for consideration
-        final ActivityClientRecord r = performResumeActivity(token, clearHide, reason);
+        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
+        if (r == null) {
+            // We didn't actually resume the activity, so skipping any follow-up actions.
+            return;
+        }
 
-        if (r != null) {
-            final Activity a = r.activity;
+        final Activity a = r.activity;
 
-            if (localLOGV) Slog.v(
-                TAG, "Resume " + r + " started activity: " +
-                a.mStartedActivity + ", hideForNow: " + r.hideForNow
-                + ", finished: " + a.mFinished);
+        if (localLOGV) {
+            Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity
+                    + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
+        }
 
-            final int forwardBit = isForward ?
-                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
+        final int forwardBit = isForward
+                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
 
-            // If the window hasn't yet been added to the window manager,
-            // and this guy didn't finish itself or start another activity,
-            // then go ahead and add the window.
-            boolean willBeVisible = !a.mStartedActivity;
-            if (!willBeVisible) {
-                try {
-                    willBeVisible = ActivityManager.getService().willActivityBeVisible(
-                            a.getActivityToken());
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
+        // If the window hasn't yet been added to the window manager,
+        // and this guy didn't finish itself or start another activity,
+        // then go ahead and add the window.
+        boolean willBeVisible = !a.mStartedActivity;
+        if (!willBeVisible) {
+            try {
+                willBeVisible = ActivityManager.getService().willActivityBeVisible(
+                        a.getActivityToken());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        if (r.window == null && !a.mFinished && willBeVisible) {
+            r.window = r.activity.getWindow();
+            View decor = r.window.getDecorView();
+            decor.setVisibility(View.INVISIBLE);
+            ViewManager wm = a.getWindowManager();
+            WindowManager.LayoutParams l = r.window.getAttributes();
+            a.mDecor = decor;
+            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+            l.softInputMode |= forwardBit;
+            if (r.mPreserveWindow) {
+                a.mWindowAdded = true;
+                r.mPreserveWindow = false;
+                // Normally the ViewRoot sets up callbacks with the Activity
+                // in addView->ViewRootImpl#setView. If we are instead reusing
+                // the decor view we have to notify the view root that the
+                // callbacks may have changed.
+                ViewRootImpl impl = decor.getViewRootImpl();
+                if (impl != null) {
+                    impl.notifyChildRebuilt();
                 }
             }
-            if (r.window == null && !a.mFinished && willBeVisible) {
-                r.window = r.activity.getWindow();
-                View decor = r.window.getDecorView();
-                decor.setVisibility(View.INVISIBLE);
-                ViewManager wm = a.getWindowManager();
-                WindowManager.LayoutParams l = r.window.getAttributes();
-                a.mDecor = decor;
-                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-                l.softInputMode |= forwardBit;
-                if (r.mPreserveWindow) {
+            if (a.mVisibleFromClient) {
+                if (!a.mWindowAdded) {
                     a.mWindowAdded = true;
-                    r.mPreserveWindow = false;
-                    // Normally the ViewRoot sets up callbacks with the Activity
-                    // in addView->ViewRootImpl#setView. If we are instead reusing
-                    // the decor view we have to notify the view root that the
-                    // callbacks may have changed.
-                    ViewRootImpl impl = decor.getViewRootImpl();
-                    if (impl != null) {
-                        impl.notifyChildRebuilt();
-                    }
+                    wm.addView(decor, l);
+                } else {
+                    // The activity will get a callback for this {@link LayoutParams} change
+                    // earlier. However, at that time the decor will not be set (this is set
+                    // in this method), so no action will be taken. This call ensures the
+                    // callback occurs with the decor set.
+                    a.onWindowAttributesChanged(l);
                 }
-                if (a.mVisibleFromClient) {
-                    if (!a.mWindowAdded) {
-                        a.mWindowAdded = true;
-                        wm.addView(decor, l);
-                    } else {
-                        // The activity will get a callback for this {@link LayoutParams} change
-                        // earlier. However, at that time the decor will not be set (this is set
-                        // in this method), so no action will be taken. This call ensures the
-                        // callback occurs with the decor set.
-                        a.onWindowAttributesChanged(l);
-                    }
-                }
+            }
 
             // If the window has already been added, but during resume
             // we started another activity, then don't yet make the
             // window visible.
-            } else if (!willBeVisible) {
-                if (localLOGV) Slog.v(
-                    TAG, "Launch " + r + " mStartedActivity set");
-                r.hideForNow = true;
+        } else if (!willBeVisible) {
+            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
+            r.hideForNow = true;
+        }
+
+        // Get rid of anything left hanging around.
+        cleanUpPendingRemoveWindows(r, false /* force */);
+
+        // The window is now visible if it has been added, we are not
+        // simply finishing, and we are not starting another activity.
+        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
+            if (r.newConfig != null) {
+                performConfigurationChangedForActivity(r, r.newConfig);
+                if (DEBUG_CONFIGURATION) {
+                    Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig "
+                            + r.activity.mCurrentConfig);
+                }
+                r.newConfig = null;
             }
-
-            // Get rid of anything left hanging around.
-            cleanUpPendingRemoveWindows(r, false /* force */);
-
-            // The window is now visible if it has been added, we are not
-            // simply finishing, and we are not starting another activity.
-            if (!r.activity.mFinished && willBeVisible
-                    && r.activity.mDecor != null && !r.hideForNow) {
-                if (r.newConfig != null) {
-                    performConfigurationChangedForActivity(r, r.newConfig);
-                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
-                            + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
-                    r.newConfig = null;
-                }
-                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
-                        + isForward);
-                WindowManager.LayoutParams l = r.window.getAttributes();
-                if ((l.softInputMode
-                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
-                        != forwardBit) {
-                    l.softInputMode = (l.softInputMode
-                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
-                            | forwardBit;
-                    if (r.activity.mVisibleFromClient) {
-                        ViewManager wm = a.getWindowManager();
-                        View decor = r.window.getDecorView();
-                        wm.updateViewLayout(decor, l);
-                    }
-                }
-
-                r.activity.mVisibleFromServer = true;
-                mNumVisibleActivities++;
+            if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward);
+            WindowManager.LayoutParams l = r.window.getAttributes();
+            if ((l.softInputMode
+                    & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
+                    != forwardBit) {
+                l.softInputMode = (l.softInputMode
+                        & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
+                        | forwardBit;
                 if (r.activity.mVisibleFromClient) {
-                    r.activity.makeVisible();
+                    ViewManager wm = a.getWindowManager();
+                    View decor = r.window.getDecorView();
+                    wm.updateViewLayout(decor, l);
                 }
             }
 
-            if (!r.onlyLocalRequest) {
-                r.nextIdle = mNewActivities;
-                mNewActivities = r;
-                if (localLOGV) Slog.v(
-                    TAG, "Scheduling idle handler for " + r);
-                Looper.myQueue().addIdleHandler(new Idler());
-            }
-            r.onlyLocalRequest = false;
-        } else {
-            // If an exception was thrown when trying to resume, then
-            // just end this activity.
-            try {
-                ActivityManager.getService()
-                    .finishActivity(token, Activity.RESULT_CANCELED, null,
-                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
+            r.activity.mVisibleFromServer = true;
+            mNumVisibleActivities++;
+            if (r.activity.mVisibleFromClient) {
+                r.activity.makeVisible();
             }
         }
-    }
 
-    private int mThumbnailWidth = -1;
-    private int mThumbnailHeight = -1;
-    private Bitmap mAvailThumbnailBitmap = null;
-    private Canvas mThumbnailCanvas = null;
-
-    private Bitmap createThumbnailBitmap(ActivityClientRecord r) {
-        Bitmap thumbnail = mAvailThumbnailBitmap;
-        try {
-            if (thumbnail == null) {
-                int w = mThumbnailWidth;
-                int h;
-                if (w < 0) {
-                    Resources res = r.activity.getResources();
-                    int wId = com.android.internal.R.dimen.thumbnail_width;
-                    int hId = com.android.internal.R.dimen.thumbnail_height;
-                    mThumbnailWidth = w = res.getDimensionPixelSize(wId);
-                    mThumbnailHeight = h = res.getDimensionPixelSize(hId);
-                } else {
-                    h = mThumbnailHeight;
-                }
-
-                // On platforms where we don't want thumbnails, set dims to (0,0)
-                if ((w > 0) && (h > 0)) {
-                    thumbnail = Bitmap.createBitmap(r.activity.getResources().getDisplayMetrics(),
-                            w, h, THUMBNAIL_FORMAT);
-                    thumbnail.eraseColor(0);
-                }
-            }
-
-            if (thumbnail != null) {
-                Canvas cv = mThumbnailCanvas;
-                if (cv == null) {
-                    mThumbnailCanvas = cv = new Canvas();
-                }
-
-                cv.setBitmap(thumbnail);
-                if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
-                    mAvailThumbnailBitmap = thumbnail;
-                    thumbnail = null;
-                }
-                cv.setBitmap(null);
-            }
-
-        } catch (Exception e) {
-            if (!mInstrumentation.onException(r.activity, e)) {
-                throw new RuntimeException(
-                        "Unable to create thumbnail of "
-                        + r.intent.getComponent().toShortString()
-                        + ": " + e.toString(), e);
-            }
-            thumbnail = null;
-        }
-
-        return thumbnail;
+        r.nextIdle = mNewActivities;
+        mNewActivities = r;
+        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
+        Looper.myQueue().addIdleHandler(new Idler());
     }
 
     @Override
     public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport, PendingTransactionActions pendingActions) {
+            int configChanges, PendingTransactionActions pendingActions, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (r != null) {
             if (userLeaving) {
@@ -3969,22 +3947,12 @@
             }
 
             r.activity.mConfigChangeFlags |= configChanges;
-            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity",
-                    pendingActions);
+            performPauseActivity(r, finished, reason, pendingActions);
 
             // Make sure any pending writes are now committed.
             if (r.isPreHoneycomb()) {
                 QueuedWork.waitToFinish();
             }
-
-            // Tell the activity manager we have paused.
-            if (!dontReport) {
-                try {
-                    ActivityManager.getService().activityPaused(token);
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-            }
             mSomeActivitiesChanged = true;
         }
     }
@@ -3993,16 +3961,18 @@
         mInstrumentation.callActivityOnUserLeaving(r.activity);
     }
 
-    final Bundle performPauseActivity(IBinder token, boolean finished,
-            boolean saveState, String reason, PendingTransactionActions pendingActions) {
+    final Bundle performPauseActivity(IBinder token, boolean finished, String reason,
+            PendingTransactionActions pendingActions) {
         ActivityClientRecord r = mActivities.get(token);
-        return r != null
-                ? performPauseActivity(r, finished, saveState, reason, pendingActions)
-                : null;
+        return r != null ? performPauseActivity(r, finished, reason, pendingActions) : null;
     }
 
-    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, boolean saveState,
-            String reason, PendingTransactionActions pendingActions) {
+    /**
+     * Pause the activity.
+     * @return Saved instance state for pre-Honeycomb apps if it was saved, {@code null} otherwise.
+     */
+    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
+            PendingTransactionActions pendingActions) {
         if (r.paused) {
             if (r.activity.mFinished) {
                 // If we are finishing, we won't call onResume() in certain cases.
@@ -4019,9 +3989,10 @@
             r.activity.mFinished = true;
         }
 
-        // Next have the activity save its current state and managed dialogs...
-        if (!r.activity.mFinished && saveState) {
-            callCallActivityOnSaveInstanceState(r);
+        // Pre-Honeycomb apps always save their state before pausing
+        final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
+        if (shouldSaveState) {
+            callActivityOnSaveInstanceState(r);
         }
 
         performPauseActivityIfNeeded(r, reason);
@@ -4048,7 +4019,7 @@
             }
         }
 
-        return !r.activity.mFinished && saveState ? r.state : null;
+        return shouldSaveState ? r.state : null;
     }
 
     private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
@@ -4060,8 +4031,6 @@
         try {
             r.activity.mCalled = false;
             mInstrumentation.callActivityOnPause(r.activity);
-            EventLog.writeEvent(LOG_AM_ON_PAUSE_CALLED, UserHandle.myUserId(),
-                    r.activity.getComponentName().getClassName(), reason);
             if (!r.activity.mCalled) {
                 throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)
                         + " did not call through to super.onPause()");
@@ -4077,9 +4046,11 @@
         r.setState(ON_PAUSE);
     }
 
+    /** Called from {@link LocalActivityManager}. */
     final void performStopActivity(IBinder token, boolean saveState, String reason) {
         ActivityClientRecord r = mActivities.get(token);
-        performStopActivityInner(r, null, false, saveState, reason);
+        performStopActivityInner(r, null /* stopInfo */, false /* keepShown */, saveState,
+                false /* finalStateRequest */, reason);
     }
 
     private static final class ProviderRefCount {
@@ -4111,9 +4082,16 @@
      * it the result when it is done, but the window may still be visible.
      * For the client, we want to call onStop()/onStart() to indicate when
      * the activity's UI visibility changes.
+     * @param r Target activity client record.
+     * @param info Action that will report activity stop to server.
+     * @param keepShown Flag indicating whether the activity is still shown.
+     * @param saveState Flag indicating whether the activity state should be saved.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param reason Reason for performing this operation.
      */
     private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
-            boolean saveState, String reason) {
+            boolean saveState, boolean finalStateRequest, String reason) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         if (r != null) {
             if (!keepShown && r.stopped) {
@@ -4123,11 +4101,13 @@
                     // if the activity isn't resumed.
                     return;
                 }
-                RuntimeException e = new RuntimeException(
-                        "Performing stop of activity that is already stopped: "
-                        + r.intent.getComponent().toShortString());
-                Slog.e(TAG, e.getMessage(), e);
-                Slog.e(TAG, r.getStateString());
+                if (!finalStateRequest) {
+                    final RuntimeException e = new RuntimeException(
+                            "Performing stop of activity that is already stopped: "
+                                    + r.intent.getComponent().toShortString());
+                    Slog.e(TAG, e.getMessage(), e);
+                    Slog.e(TAG, r.getStateString());
+                }
             }
 
             // One must first be paused before stopped...
@@ -4149,30 +4129,45 @@
                 }
             }
 
-            // Next have the activity save its current state and managed dialogs...
-            if (!r.activity.mFinished && saveState) {
-                if (r.state == null) {
-                    callCallActivityOnSaveInstanceState(r);
-                }
-            }
-
             if (!keepShown) {
-                try {
-                    // Now we are idle.
-                    r.activity.performStop(false /*preserveWindow*/);
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to stop activity "
+                callActivityOnStop(r, saveState, reason);
+            }
+        }
+    }
+
+    /**
+     * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
+     * the client record's state.
+     * All calls to stop an activity must be done through this method to make sure that
+     * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
+     */
+    private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+        // Before P onSaveInstanceState was called before onStop, starting with P it's
+        // called after. Before Honeycomb state was always saved before onPause.
+        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
+                && !r.isPreHoneycomb();
+        final boolean isPreP = r.isPreP();
+        if (shouldSaveState && isPreP) {
+            callActivityOnSaveInstanceState(r);
+        }
+
+        try {
+            r.activity.performStop(false /*preserveWindow*/, reason);
+        } catch (SuperNotCalledException e) {
+            throw e;
+        } catch (Exception e) {
+            if (!mInstrumentation.onException(r.activity, e)) {
+                throw new RuntimeException(
+                        "Unable to stop activity "
                                 + r.intent.getComponent().toShortString()
                                 + ": " + e.toString(), e);
-                    }
-                }
-                r.setState(ON_STOP);
-                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), reason);
             }
         }
+        r.setState(ON_STOP);
+
+        if (shouldSaveState && !isPreP) {
+            callActivityOnSaveInstanceState(r);
+        }
     }
 
     private void updateVisibility(ActivityClientRecord r, boolean show) {
@@ -4205,12 +4200,13 @@
 
     @Override
     public void handleStopActivity(IBinder token, boolean show, int configChanges,
-            PendingTransactionActions pendingActions) {
+            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
         final ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
         final StopInfo stopInfo = new StopInfo();
-        performStopActivityInner(r, stopInfo, show, true, "handleStopActivity");
+        performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
+                reason);
 
         if (localLOGV) Slog.v(
             TAG, "Finishing stop of " + r + ": show=" + show
@@ -4245,7 +4241,7 @@
     public void performRestartActivity(IBinder token, boolean start) {
         ActivityClientRecord r = mActivities.get(token);
         if (r.stopped) {
-            r.activity.performRestart(start);
+            r.activity.performRestart(start, "performRestartActivity");
             if (start) {
                 r.setState(ON_START);
             }
@@ -4262,13 +4258,14 @@
         }
 
         if (!show && !r.stopped) {
-            performStopActivityInner(r, null, show, false, "handleWindowVisibility");
+            performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */,
+                    false /* finalStateRequest */, "handleWindowVisibility");
         } else if (show && r.stopped) {
             // If we are getting ready to gc after going to the background, well
             // we are back active so skip it.
             unscheduleGcIdler();
 
-            r.activity.performRestart(true /* start */);
+            r.activity.performRestart(true /* start */, "handleWindowVisibility");
             r.setState(ON_START);
         }
         if (r.activity.mDecor != null) {
@@ -4292,24 +4289,7 @@
 
         if (sleeping) {
             if (!r.stopped && !r.isPreHoneycomb()) {
-                if (!r.activity.mFinished && r.state == null) {
-                    callCallActivityOnSaveInstanceState(r);
-                }
-
-                try {
-                    // Now we are idle.
-                    r.activity.performStop(false /*preserveWindow*/);
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to stop activity "
-                                + r.intent.getComponent().toShortString()
-                                + ": " + e.toString(), e);
-                    }
-                }
-                r.setState(ON_STOP);
-                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), "sleeping");
+                callActivityOnStop(r, true /* saveState */, "sleeping");
             }
 
             // Make sure any pending writes are now committed.
@@ -4325,7 +4305,7 @@
             }
         } else {
             if (r.stopped && r.activity.mVisibleFromServer) {
-                r.activity.performRestart(true /* start */);
+                r.activity.performRestart(true /* start */, "handleSleeping");
                 r.setState(ON_START);
             }
         }
@@ -4345,29 +4325,25 @@
             View.mDebugViewAttributes = debugViewAttributes;
 
             // request all activities to relaunch for the changes to take place
-            requestRelaunchAllActivities();
+            relaunchAllActivities();
         }
     }
 
-    private void requestRelaunchAllActivities() {
+    private void relaunchAllActivities() {
         for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
             final Activity activity = entry.getValue().activity;
             if (!activity.mFinished) {
-                try {
-                    ActivityManager.getService().requestActivityRelaunch(entry.getKey());
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
+                handleRelaunchActivityLocally(entry.getKey());
             }
         }
     }
 
     private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
-        LoadedApk apk = peekLoadedApk(data.pkg, false);
+        LoadedApk apk = peekPackageInfo(data.pkg, false);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
-        apk = peekLoadedApk(data.pkg, true);
+        apk = peekPackageInfo(data.pkg, true);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
@@ -4437,7 +4413,7 @@
             checkAndBlockForNetworkAccess();
             deliverResults(r, results);
             if (resumed) {
-                r.activity.performResume(false);
+                r.activity.performResume(false, "handleSendResult");
                 r.activity.mTemporaryPause = false;
             }
         }
@@ -4445,7 +4421,7 @@
 
     /** Core implementation of activity destroy call. */
     ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
-            int configChanges, boolean getNonConfigInstance) {
+            int configChanges, boolean getNonConfigInstance, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         Class<? extends Activity> activityClass = null;
         if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
@@ -4459,21 +4435,7 @@
             performPauseActivityIfNeeded(r, "destroy");
 
             if (!r.stopped) {
-                try {
-                    r.activity.performStop(r.mPreserveWindow);
-                } catch (SuperNotCalledException e) {
-                    throw e;
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to stop activity "
-                                + safeToComponentShortString(r.intent)
-                                + ": " + e.toString(), e);
-                    }
-                }
-                r.setState(ON_STOP);
-                EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
-                        r.activity.getComponentName().getClassName(), "destroy");
+                callActivityOnStop(r, false /* saveState */, "destroy");
             }
             if (getNonConfigInstance) {
                 try {
@@ -4511,6 +4473,12 @@
             r.setState(ON_DESTROY);
         }
         mActivities.remove(token);
+        mRecentDestroyedActivities.add(0, new DestroyedActivityInfo(token.hashCode(), reason));
+
+        final int recentDestroyedActivitiesSize = mRecentDestroyedActivities.size();
+        if (recentDestroyedActivitiesSize > MAX_DESTROYED_ACTIVITIES) {
+            mRecentDestroyedActivities.remove(recentDestroyedActivitiesSize - 1);
+        }
         StrictMode.decrementExpectedActivityCount(activityClass);
         return r;
     }
@@ -4522,9 +4490,9 @@
 
     @Override
     public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
-            boolean getNonConfigInstance) {
+            boolean getNonConfigInstance, String reason) {
         ActivityClientRecord r = performDestroyActivity(token, finishing,
-                configChanges, getNonConfigInstance);
+                configChanges, getNonConfigInstance, reason);
         if (r != null) {
             cleanUpPendingRemoveWindows(r, finishing);
             WindowManager wm = r.activity.getWindowManager();
@@ -4592,15 +4560,12 @@
         mSomeActivitiesChanged = true;
     }
 
-    /**
-     * @param preserveWindow Whether the activity should try to reuse the window it created,
-     *                        including the decor view after the relaunch.
-     */
-    public final void requestRelaunchActivity(IBinder token,
+    @Override
+    public ActivityClientRecord prepareRelaunchActivity(IBinder token,
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-            int configChanges, boolean notResumed, Configuration config,
-            Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
+            int configChanges, MergedConfiguration config, boolean preserveWindow) {
         ActivityClientRecord target = null;
+        boolean scheduleRelaunch = false;
 
         synchronized (mResourcesManager) {
             for (int i=0; i<mRelaunchingActivities.size(); i++) {
@@ -4622,57 +4587,31 @@
                             r.pendingIntents = pendingNewIntents;
                         }
                     }
-
-                    // For each relaunch request, activity manager expects an answer
-                    if (!r.onlyLocalRequest && fromServer) {
-                        try {
-                            ActivityManager.getService().activityRelaunched(token);
-                        } catch (RemoteException e) {
-                            throw e.rethrowFromSystemServer();
-                        }
-                    }
                     break;
                 }
             }
 
             if (target == null) {
-                if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:"
-                        + fromServer);
+                if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null");
                 target = new ActivityClientRecord();
                 target.token = token;
                 target.pendingResults = pendingResults;
                 target.pendingIntents = pendingNewIntents;
                 target.mPreserveWindow = preserveWindow;
-                if (!fromServer) {
-                    final ActivityClientRecord existing = mActivities.get(token);
-                    if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing);
-                    if (existing != null) {
-                        if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= "
-                                + existing.paused);;
-                        target.startsNotResumed = existing.paused;
-                        target.overrideConfig = existing.overrideConfig;
-                    }
-                    target.onlyLocalRequest = true;
-                }
                 mRelaunchingActivities.add(target);
-                sendMessage(H.RELAUNCH_ACTIVITY, target);
+                scheduleRelaunch = true;
             }
-
-            if (fromServer) {
-                target.startsNotResumed = notResumed;
-                target.onlyLocalRequest = false;
-            }
-            if (config != null) {
-                target.createdConfig = config;
-            }
-            if (overrideConfig != null) {
-                target.overrideConfig = overrideConfig;
-            }
+            target.createdConfig = config.getGlobalConfiguration();
+            target.overrideConfig = config.getOverrideConfiguration();
             target.pendingConfigChanges |= configChanges;
         }
+
+        return scheduleRelaunch ? target : null;
     }
 
-    private void handleRelaunchActivity(ActivityClientRecord tmp) {
+    @Override
+    public void handleRelaunchActivity(ActivityClientRecord tmp,
+            PendingTransactionActions pendingActions) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -4741,18 +4680,10 @@
         ActivityClientRecord r = mActivities.get(tmp.token);
         if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
         if (r == null) {
-            if (!tmp.onlyLocalRequest) {
-                try {
-                    ActivityManager.getService().activityRelaunched(tmp.token);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
             return;
         }
 
         r.activity.mConfigChangeFlags |= configChanges;
-        r.onlyLocalRequest = tmp.onlyLocalRequest;
         r.mPreserveWindow = tmp.mPreserveWindow;
 
         r.activity.mChangingConfigurations = true;
@@ -4769,70 +4700,113 @@
         // preserved by the server, so we want to notify it that we are preparing to replace
         // everything
         try {
-            if (r.mPreserveWindow || r.onlyLocalRequest) {
+            if (r.mPreserveWindow) {
                 WindowManagerGlobal.getWindowSession().prepareToReplaceWindows(
-                        r.token, !r.onlyLocalRequest);
+                        r.token, true /* childrenOnly */);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
 
-        // Need to ensure state is saved.
-        if (!r.paused) {
-            performPauseActivity(r.token, false, r.isPreHoneycomb(), "handleRelaunchActivity",
-                    null /* pendingActions */);
+        handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
+                pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
+
+        if (pendingActions != null) {
+            // Only report a successful relaunch to WindowManager.
+            pendingActions.setReportRelaunchToWindowManager(true);
         }
-        if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
-            callCallActivityOnSaveInstanceState(r);
+    }
+
+    /** Performs the activity relaunch locally vs. requesting from system-server. */
+    void handleRelaunchActivityLocally(IBinder token) {
+        if (Looper.myLooper() != getLooper()) {
+            throw new IllegalStateException("Must be called from main thread");
         }
 
-        handleDestroyActivity(r.token, false, configChanges, true);
+        final ActivityClientRecord r = mActivities.get(token);
+        if (r == null) {
+            return;
+        }
+
+        final int prevState = r.getLifecycleState();
+
+        if (prevState < ON_RESUME) {
+            Log.w(TAG, "Activity needs to be already resumed in other to be relaunched.");
+            return;
+        }
+
+
+        // Initialize a relaunch request.
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+                r.createdConfig != null ? r.createdConfig : mConfiguration,
+                r.overrideConfig);
+        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
+                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
+                mergedConfiguration, r.mPreserveWindow);
+        // Make sure to match the existing lifecycle state in the end of the transaction.
+        final ActivityLifecycleItem lifecycleRequest =
+                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
+        // Schedule the transaction.
+        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        transaction.addCallback(activityRelaunchItem);
+        transaction.setLifecycleStateRequest(lifecycleRequest);
+        executeTransaction(transaction);
+    }
+
+    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
+            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
+            PendingTransactionActions pendingActions, boolean startsNotResumed,
+            Configuration overrideConfig, String reason) {
+        // Need to ensure state is saved.
+        if (!r.paused) {
+            performPauseActivity(r, false, reason, null /* pendingActions */);
+        }
+        if (!r.stopped) {
+            callActivityOnStop(r, true /* saveState */, reason);
+        }
+
+        handleDestroyActivity(r.token, false, configChanges, true, reason);
 
         r.activity = null;
         r.window = null;
         r.hideForNow = false;
         r.nextIdle = null;
         // Merge any pending results and pending intents; don't just replace them
-        if (tmp.pendingResults != null) {
+        if (pendingResults != null) {
             if (r.pendingResults == null) {
-                r.pendingResults = tmp.pendingResults;
+                r.pendingResults = pendingResults;
             } else {
-                r.pendingResults.addAll(tmp.pendingResults);
+                r.pendingResults.addAll(pendingResults);
             }
         }
-        if (tmp.pendingIntents != null) {
+        if (pendingIntents != null) {
             if (r.pendingIntents == null) {
-                r.pendingIntents = tmp.pendingIntents;
+                r.pendingIntents = pendingIntents;
             } else {
-                r.pendingIntents.addAll(tmp.pendingIntents);
+                r.pendingIntents.addAll(pendingIntents);
             }
         }
-        r.startsNotResumed = tmp.startsNotResumed;
-        r.overrideConfig = tmp.overrideConfig;
+        r.startsNotResumed = startsNotResumed;
+        r.overrideConfig = overrideConfig;
 
-        // TODO(lifecycler): Move relaunch to lifecycler.
-        PendingTransactionActions pendingActions = new PendingTransactionActions();
         handleLaunchActivity(r, pendingActions);
-        handleStartActivity(r, pendingActions);
-        handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
-        if (r.startsNotResumed) {
-            performPauseActivity(r, false /* finished */, r.isPreHoneycomb(), "relaunch",
-                    pendingActions);
-        }
+    }
 
-        if (!tmp.onlyLocalRequest) {
-            try {
-                ActivityManager.getService().activityRelaunched(r.token);
-                if (r.window != null) {
-                    r.window.reportActivityRelaunched();
-                }
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
+    @Override
+    public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
+        try {
+            ActivityManager.getService().activityRelaunched(token);
+            final ActivityClientRecord r = mActivities.get(token);
+            if (pendingActions.shouldReportRelaunchToWindowManager() && r != null
+                    && r.window != null) {
+                r.window.reportActivityRelaunched();
             }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
-    private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
+    private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
         r.state = new Bundle();
         r.state.setAllowFds(false);
         if (r.isPersistable()) {
@@ -4861,7 +4835,7 @@
                 if (a != null) {
                     Configuration thisConfig = applyConfigCompatMainThread(
                             mCurDefaultDisplayDpi, newConfig,
-                            ar.loadedApk.getCompatibilityInfo());
+                            ar.packageInfo.getCompatibilityInfo());
                     if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                         // If the activity is currently resumed, its configuration
                         // needs to change right now.
@@ -5114,6 +5088,8 @@
         // as that method uses the same check on the activity config override as well.
         final boolean equivalent = config != null && mConfiguration != null
                 && (0 == mConfiguration.diffPublicOnly(config));
+        final Theme systemTheme = getSystemContext().getTheme();
+        final Theme systemUiTheme = getSystemUiContext().getTheme();
 
         synchronized (mResourcesManager) {
             if (mPendingConfiguration != null) {
@@ -5146,12 +5122,10 @@
             configDiff = mConfiguration.updateFrom(config);
             config = applyCompatConfiguration(mCurDefaultDisplayDpi);
 
-            final Theme systemTheme = getSystemContext().getTheme();
             if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
                 systemTheme.rebase();
             }
 
-            final Theme systemUiTheme = getSystemUiContext().getTheme();
             if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
                 systemUiTheme.rebase();
             }
@@ -5218,7 +5192,7 @@
         newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1;
         handleConfigurationChanged(newConfig, null);
 
-        requestRelaunchAllActivities();
+        relaunchAllActivities();
     }
 
     static void freeTextLayoutCachesIfNeeded(int configDiff) {
@@ -5347,7 +5321,7 @@
     }
 
     final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
-        boolean hasLoadedApk = false;
+        boolean hasPkgInfo = false;
         switch (cmd) {
             case ApplicationThreadConstants.PACKAGE_REMOVED:
             case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
@@ -5358,14 +5332,14 @@
                 }
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
-                        if (!hasLoadedApk) {
+                        if (!hasPkgInfo) {
                             WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
                             if (ref != null && ref.get() != null) {
-                                hasLoadedApk = true;
+                                hasPkgInfo = true;
                             } else {
                                 ref = mResourcePackages.get(packages[i]);
                                 if (ref != null && ref.get() != null) {
-                                    hasLoadedApk = true;
+                                    hasPkgInfo = true;
                                 }
                             }
                         }
@@ -5385,21 +5359,21 @@
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
                         WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
-                        LoadedApk loadedApk = ref != null ? ref.get() : null;
-                        if (loadedApk != null) {
-                            hasLoadedApk = true;
+                        LoadedApk pkgInfo = ref != null ? ref.get() : null;
+                        if (pkgInfo != null) {
+                            hasPkgInfo = true;
                         } else {
                             ref = mResourcePackages.get(packages[i]);
-                            loadedApk = ref != null ? ref.get() : null;
-                            if (loadedApk != null) {
-                                hasLoadedApk = true;
+                            pkgInfo = ref != null ? ref.get() : null;
+                            if (pkgInfo != null) {
+                                hasPkgInfo = true;
                             }
                         }
                         // If the package is being replaced, yet it still has a valid
                         // LoadedApk object, the package was updated with _DONT_KILL.
                         // Adjust it's internal references to the application info and
                         // resources.
-                        if (loadedApk != null) {
+                        if (pkgInfo != null) {
                             try {
                                 final String packageName = packages[i];
                                 final ApplicationInfo aInfo =
@@ -5413,13 +5387,13 @@
                                         if (ar.activityInfo.applicationInfo.packageName
                                                 .equals(packageName)) {
                                             ar.activityInfo.applicationInfo = aInfo;
-                                            ar.loadedApk = loadedApk;
+                                            ar.packageInfo = pkgInfo;
                                         }
                                     }
                                 }
                                 final List<String> oldPaths =
                                         sPackageManager.getPreviousCodePaths(packageName);
-                                loadedApk.updateApplicationInfo(aInfo, oldPaths);
+                                pkgInfo.updateApplicationInfo(aInfo, oldPaths);
                             } catch (RemoteException e) {
                             }
                         }
@@ -5428,7 +5402,7 @@
                 break;
             }
         }
-        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
+        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
     }
 
     final void handleLowMemory() {
@@ -5610,6 +5584,13 @@
 
         Message.updateCheckRecycle(data.appInfo.targetSdkVersion);
 
+        // Prior to P, internal calls to decode Bitmaps used BitmapFactory,
+        // which may scale up to account for density. In P, we switched to
+        // ImageDecoder, which skips the upscale to save memory. ImageDecoder
+        // needs to still scale up in older apps, in case they rely on the
+        // size of the Bitmap without considering its density.
+        ImageDecoder.sApiLevel = data.appInfo.targetSdkVersion;
+
         /*
          * Before spawning a new process, reset the time zone to be the system time zone.
          * This needs to be done because the system time zone could have changed after the
@@ -5636,10 +5617,10 @@
             applyCompatConfiguration(mCurDefaultDisplayDpi);
         }
 
-        data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
         if (agent != null) {
-            handleAttachAgent(agent, data.loadedApk);
+            handleAttachAgent(agent, data.info);
         }
 
         /**
@@ -5684,7 +5665,7 @@
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
             if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
-                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+                Slog.w(TAG, "Application " + data.info.getPackageName()
                       + " is waiting for the debugger on port 8100...");
 
                 IActivityManager mgr = ActivityManager.getService();
@@ -5703,7 +5684,7 @@
                 }
 
             } else {
-                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+                Slog.w(TAG, "Application " + data.info.getPackageName()
                       + " can be debugged on port 8100...");
             }
         }
@@ -5711,6 +5692,7 @@
         // Allow application-generated systrace messages if we're debuggable.
         boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         Trace.setAppTracingAllowed(isAppDebuggable);
+        ThreadedRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
         if (isAppDebuggable && data.enableBinderTracking) {
             Binder.enableTracing();
         }
@@ -5751,14 +5733,14 @@
             mInstrumentationAppDir = ii.sourceDir;
             mInstrumentationSplitAppDirs = ii.splitSourceDirs;
             mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
-            mInstrumentedAppDir = data.loadedApk.getAppDir();
-            mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs();
-            mInstrumentedLibDir = data.loadedApk.getLibDir();
+            mInstrumentedAppDir = data.info.getAppDir();
+            mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+            mInstrumentedLibDir = data.info.getLibDir();
         } else {
             ii = null;
         }
 
-        final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk);
+        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
         updateLocaleListFromAppContext(appContext,
                 mResourcesManager.getConfiguration().getLocales());
 
@@ -5769,6 +5751,8 @@
             } finally {
                 StrictMode.setThreadPolicyMask(oldMask);
             }
+        } else {
+            ThreadedRenderer.setIsolatedProcess(true);
         }
 
         // If we use profiles, setup the dex reporter to notify package manager
@@ -5802,9 +5786,9 @@
             }
             ii.copyTo(instrApp);
             instrApp.initForUser(UserHandle.myUserId());
-            final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo,
+            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true, false);
-            final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk);
+            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
 
             try {
                 final ClassLoader cl = instrContext.getClassLoader();
@@ -5849,7 +5833,11 @@
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
-            app = data.loadedApk.makeApplication(data.restrictedBackupMode, null);
+            app = data.info.makeApplication(data.restrictedBackupMode, null);
+
+            // Propagate autofill compat state
+            app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
+
             mInitialApplication = app;
 
             // don't bring up providers in restricted mode; they may depend on the
@@ -5893,21 +5881,23 @@
 
         // Preload fonts resources
         FontsContract.setApplicationContextForResources(appContext);
-        try {
-            final ApplicationInfo info =
-                    getPackageManager().getApplicationInfo(
-                            data.appInfo.packageName,
-                            PackageManager.GET_META_DATA /*flags*/,
-                            UserHandle.myUserId());
-            if (info.metaData != null) {
-                final int preloadedFontsResource = info.metaData.getInt(
-                        ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
-                if (preloadedFontsResource != 0) {
-                    data.loadedApk.getResources().preloadFonts(preloadedFontsResource);
+        if (!Process.isIsolated()) {
+            try {
+                final ApplicationInfo info =
+                        getPackageManager().getApplicationInfo(
+                                data.appInfo.packageName,
+                                PackageManager.GET_META_DATA /*flags*/,
+                                UserHandle.myUserId());
+                if (info.metaData != null) {
+                    final int preloadedFontsResource = info.metaData.getInt(
+                            ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
+                    if (preloadedFontsResource != 0) {
+                        data.info.getResources().preloadFonts(preloadedFontsResource);
+                    }
                 }
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
             }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -6361,12 +6351,12 @@
 
             try {
                 final java.lang.ClassLoader cl = c.getClassLoader();
-                LoadedApk loadedApk = peekLoadedApk(ai.packageName, true);
-                if (loadedApk == null) {
+                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
+                if (packageInfo == null) {
                     // System startup case.
-                    loadedApk = getSystemContext().mLoadedApk;
+                    packageInfo = getSystemContext().mPackageInfo;
                 }
-                localProvider = loadedApk.getAppFactory()
+                localProvider = packageInfo.getAppFactory()
                         .instantiateProvider(cl, info.name);
                 provider = localProvider.getIContentProvider();
                 if (provider == null) {
@@ -6515,8 +6505,8 @@
                 mInstrumentation = new Instrumentation();
                 mInstrumentation.basicInit(this);
                 ContextImpl context = ContextImpl.createAppContext(
-                        this, getSystemContext().mLoadedApk);
-                mInitialApplication = context.mLoadedApk.makeApplication(true, null);
+                        this, getSystemContext().mPackageInfo);
+                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                 mInitialApplication.onCreate();
             } catch (Exception e) {
                 throw new RuntimeException(
diff --git a/android/app/ActivityView.java b/android/app/ActivityView.java
index 5d0143a..7032a2f 100644
--- a/android/app/ActivityView.java
+++ b/android/app/ActivityView.java
@@ -27,6 +27,7 @@
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.MotionEvent;
@@ -308,8 +309,14 @@
             return;
         }
 
-        mInputForwarder = InputManager.getInstance().createInputForwarder(
-                mVirtualDisplay.getDisplay().getDisplayId());
+        final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
+        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+        try {
+            wm.dontOverrideDisplayInfo(displayId);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
         mTaskStackListener = new TaskBackgroundChangeListener();
         try {
             mActivityManager.registerTaskStackListener(mTaskStackListener);
diff --git a/android/app/AlarmManager.java b/android/app/AlarmManager.java
index 382719b..124f933 100644
--- a/android/app/AlarmManager.java
+++ b/android/app/AlarmManager.java
@@ -178,6 +178,7 @@
     public static final int FLAG_IDLE_UNTIL = 1<<4;
 
     private final IAlarmManager mService;
+    private final Context mContext;
     private final String mPackageName;
     private final boolean mAlwaysExact;
     private final int mTargetSdkVersion;
@@ -265,6 +266,7 @@
     AlarmManager(IAlarmManager service, Context ctx) {
         mService = service;
 
+        mContext = ctx;
         mPackageName = ctx.getPackageName();
         mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
         mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT);
@@ -1028,7 +1030,7 @@
      * @see #ACTION_NEXT_ALARM_CLOCK_CHANGED
      */
     public AlarmClockInfo getNextAlarmClock() {
-        return getNextAlarmClock(UserHandle.myUserId());
+        return getNextAlarmClock(mContext.getUserId());
     }
 
     /**
diff --git a/android/app/AppComponentFactory.java b/android/app/AppComponentFactory.java
index 4df7379..cfaeec9 100644
--- a/android/app/AppComponentFactory.java
+++ b/android/app/AppComponentFactory.java
@@ -36,6 +36,10 @@
      * Allows application to override the creation of the application object. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the Application object. The returned object will not be initialized
+     * as a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -50,6 +54,10 @@
      * Allows application to override the creation of activities. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the Activity object. The returned object will not be initialized
+     * as a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -80,6 +88,10 @@
      * Allows application to override the creation of services. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the Service object. The returned object will not be initialized
+     * as a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -95,6 +107,10 @@
      * Allows application to override the creation of providers. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the ContentProvider object. The returned object will not be initialized
+     * with a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -108,5 +124,5 @@
     /**
      * @hide
      */
-    public static AppComponentFactory DEFAULT = new AppComponentFactory();
+    public static final AppComponentFactory DEFAULT = new AppComponentFactory();
 }
diff --git a/android/app/AppOpsManager.java b/android/app/AppOpsManager.java
index e923fb2..ab0001c 100644
--- a/android/app/AppOpsManager.java
+++ b/android/app/AppOpsManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -30,12 +31,13 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
 
+import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -74,8 +76,9 @@
 
     final Context mContext;
     final IAppOpsService mService;
-    final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers
-            = new ArrayMap<OnOpChangedListener, IAppOpsCallback>();
+    final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>();
+    final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+            new ArrayMap<>();
 
     static IBinder sToken;
 
@@ -164,12 +167,14 @@
     /** @hide */
     public static final int OP_WRITE_SETTINGS = 23;
     /** @hide Required to draw on top of other apps. */
+    @TestApi
     public static final int OP_SYSTEM_ALERT_WINDOW = 24;
     /** @hide */
     public static final int OP_ACCESS_NOTIFICATIONS = 25;
     /** @hide */
     public static final int OP_CAMERA = 26;
     /** @hide */
+    @TestApi
     public static final int OP_RECORD_AUDIO = 27;
     /** @hide */
     public static final int OP_PLAY_AUDIO = 28;
@@ -265,8 +270,12 @@
     public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
     /** @hide Continue handover of a call from another app */
     public static final int OP_ACCEPT_HANDOVER = 74;
+    /** @hide Create and Manage IPsec Tunnels */
+    public static final int OP_MANAGE_IPSEC_TUNNELS = 75;
+    /** @hide Any app start foreground service. */
+    public static final int OP_START_FOREGROUND = 76;
     /** @hide */
-    public static final int _NUM_OP = 75;
+    public static final int _NUM_OP = 77;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -494,13 +503,20 @@
     public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
     /** @hide */
     @SystemApi @TestApi
-    public static final String OPSTR_CHANGE_WIFI_STATE = "change_wifi_state";
+    public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
     /** @hide */
     @SystemApi @TestApi
-    public static final String OPSTR_REQUEST_DELETE_PACKAGES = "request_delete_packages";
+    public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     /** @hide */
     @SystemApi @TestApi
-    public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "bind_accessibility_service";
+    public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE =
+            "android:bind_accessibility_service";
+    /** @hide */
+    @SystemApi @TestApi
+    public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
+    /** @hide */
+    @SystemApi @TestApi
+    public static final String OPSTR_START_FOREGROUND = "android:start_foreground";
 
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
@@ -543,13 +559,13 @@
             OP_CAMERA,
             // Body sensors
             OP_BODY_SENSORS,
-            OP_REQUEST_DELETE_PACKAGES,
 
             // APPOP PERMISSIONS
             OP_ACCESS_NOTIFICATIONS,
             OP_SYSTEM_ALERT_WINDOW,
             OP_WRITE_SETTINGS,
             OP_REQUEST_INSTALL_PACKAGES,
+            OP_START_FOREGROUND,
     };
 
     /**
@@ -636,6 +652,8 @@
             OP_REQUEST_DELETE_PACKAGES,
             OP_BIND_ACCESSIBILITY_SERVICE,
             OP_ACCEPT_HANDOVER,
+            OP_MANAGE_IPSEC_TUNNELS,
+            OP_START_FOREGROUND,
     };
 
     /**
@@ -717,6 +735,8 @@
             OPSTR_REQUEST_DELETE_PACKAGES,
             OPSTR_BIND_ACCESSIBILITY_SERVICE,
             OPSTR_ACCEPT_HANDOVER,
+            OPSTR_MANAGE_IPSEC_TUNNELS,
+            OPSTR_START_FOREGROUND,
     };
 
     /**
@@ -799,6 +819,8 @@
             "REQUEST_DELETE_PACKAGES",
             "BIND_ACCESSIBILITY_SERVICE",
             "ACCEPT_HANDOVER",
+            "MANAGE_IPSEC_TUNNELS",
+            "START_FOREGROUND",
     };
 
     /**
@@ -881,6 +903,8 @@
             Manifest.permission.REQUEST_DELETE_PACKAGES,
             Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
             Manifest.permission.ACCEPT_HANDOVER,
+            null, // no permission for OP_MANAGE_IPSEC_TUNNELS
+            Manifest.permission.FOREGROUND_SERVICE,
     };
 
     /**
@@ -964,6 +988,8 @@
             null, // REQUEST_DELETE_PACKAGES
             null, // OP_BIND_ACCESSIBILITY_SERVICE
             null, // ACCEPT_HANDOVER
+            null, // MANAGE_IPSEC_TUNNELS
+            null, // START_FOREGROUND
     };
 
     /**
@@ -1046,6 +1072,8 @@
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
             false, // ACCEPT_HANDOVER
+            false, // MANAGE_IPSEC_HANDOVERS
+            false, // START_FOREGROUND
     };
 
     /**
@@ -1121,12 +1149,14 @@
             AppOpsManager.MODE_DEFAULT,  // OP_REQUEST_INSTALL_PACKAGES
             AppOpsManager.MODE_ALLOWED,  // OP_PICTURE_IN_PICTURE
             AppOpsManager.MODE_DEFAULT,  // OP_INSTANT_APP_START_FOREGROUND
-            AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
+            AppOpsManager.MODE_ALLOWED,  // ANSWER_PHONE_CALLS
             AppOpsManager.MODE_ALLOWED,  // OP_RUN_ANY_IN_BACKGROUND
             AppOpsManager.MODE_ALLOWED,  // OP_CHANGE_WIFI_STATE
             AppOpsManager.MODE_ALLOWED,  // REQUEST_DELETE_PACKAGES
             AppOpsManager.MODE_ALLOWED,  // OP_BIND_ACCESSIBILITY_SERVICE
             AppOpsManager.MODE_ALLOWED,  // ACCEPT_HANDOVER
+            AppOpsManager.MODE_ERRORED,  // MANAGE_IPSEC_TUNNELS
+            AppOpsManager.MODE_ALLOWED,  // OP_START_FOREGROUND
     };
 
     /**
@@ -1212,6 +1242,8 @@
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
             false, // ACCEPT_HANDOVER
+            false, // MANAGE_IPSEC_TUNNELS
+            false, // START_FOREGROUND
     };
 
     /**
@@ -1533,6 +1565,24 @@
     }
 
     /**
+     * Callback for notification of changes to operation active state.
+     *
+     * @hide
+     */
+    @TestApi
+    public interface OnOpActiveChangedListener {
+        /**
+         * Called when the active state of an app op changes.
+         *
+         * @param code The op code.
+         * @param uid The UID performing the operation.
+         * @param packageName The package performing the operation.
+         * @param active Whether the operation became active or inactive.
+         */
+        void onOpActiveChanged(int code, int uid, String packageName, boolean active);
+    }
+
+    /**
      * Callback for notification of changes to operation state.
      * This allows you to see the raw op codes instead of strings.
      * @hide
@@ -1587,6 +1637,7 @@
      * @param mode The app op mode to set.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(int code, int uid, int mode) {
         try {
             mService.setUidMode(code, uid, mode);
@@ -1606,7 +1657,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS)
+    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(String appOp, int uid, int mode) {
         try {
             mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
@@ -1638,6 +1689,7 @@
 
     /** @hide */
     @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setMode(int code, int uid, String packageName, int mode) {
         try {
             mService.setMode(code, uid, packageName, mode);
@@ -1647,6 +1699,27 @@
     }
 
     /**
+     * Change the operating mode for the given op in the given app package.  You must pass
+     * in both the uid and name of the application whose mode is being modified; if these
+     * do not match, the modification will not be applied.
+     *
+     * @param op The operation to modify.  One of the OPSTR_* constants.
+     * @param uid The user id of the application whose mode will be changed.
+     * @param packageName The name of the application package name whose mode will
+     * be changed.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
+    public void setMode(String op, int uid, String packageName, int mode) {
+        try {
+            mService.setMode(strOpToOp(op), uid, packageName, mode);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Set a non-persisted restriction on an audio operation at a stream-level.
      * Restrictions are temporary additional constraints imposed on top of the persisted rules
      * defined by {@link #setMode}.
@@ -1657,6 +1730,7 @@
      * @param exceptionPackages Optional list of packages to exclude from the restriction.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setRestriction(int code, @AttributeUsage int usage, int mode,
             String[] exceptionPackages) {
         try {
@@ -1668,9 +1742,10 @@
     }
 
     /** @hide */
+    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void resetAllModes() {
         try {
-            mService.resetAllModes(UserHandle.myUserId(), null);
+            mService.resetAllModes(mContext.getUserId(), null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1696,6 +1771,8 @@
 
     /**
      * Monitor for changes to the operating mode for the given op in the given app package.
+     * You can watch op changes only for your UID.
+     *
      * @param op The operation to monitor, one of OPSTR_*.
      * @param packageName The name of the application to monitor.
      * @param callback Where to report changes.
@@ -1707,11 +1784,16 @@
 
     /**
      * Monitor for changes to the operating mode for the given op in the given app package.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can watch changes only for your UID.
+     *
      * @param op The operation to monitor, one of OP_*.
      * @param packageName The name of the application to monitor.
      * @param callback Where to report changes.
      * @hide
      */
+    @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
     public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) {
         synchronized (mModeWatchers) {
             IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -1753,6 +1835,80 @@
         }
     }
 
+    /**
+     * Start watching for changes to the active state of app ops. An app op may be
+     * long running and it has a clear start and stop delimiters. If an op is being
+     * started or stopped by any package you will get a callback. To change the
+     * watched ops for a registered callback you need to unregister and register it
+     * again.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can watch changes only for your UID.
+     *
+     * @param ops The ops to watch.
+     * @param callback Where to report changes.
+     *
+     * @see #isOperationActive(int, int, String)
+     * @see #stopWatchingActive(OnOpActiveChangedListener)
+     * @see #startOp(int, int, String)
+     * @see #finishOp(int, int, String)
+     *
+     * @hide
+     */
+    @TestApi
+    // TODO: Uncomment below annotation once b/73559440 is fixed
+    // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
+    public void startWatchingActive(@NonNull int[] ops,
+            @NonNull OnOpActiveChangedListener callback) {
+        Preconditions.checkNotNull(ops, "ops cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        IAppOpsActiveCallback cb;
+        synchronized (mActiveWatchers) {
+            cb = mActiveWatchers.get(callback);
+            if (cb != null) {
+                return;
+            }
+            cb = new IAppOpsActiveCallback.Stub() {
+                @Override
+                public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+                    callback.onOpActiveChanged(op, uid, packageName, active);
+                }
+            };
+            mActiveWatchers.put(callback, cb);
+        }
+        try {
+            mService.startWatchingActive(ops, cb);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stop watching for changes to the active state of an app op. An app op may be
+     * long running and it has a clear start and stop delimiters. Unregistering a
+     * non-registered callback has no effect.
+     *
+     * @see #isOperationActive#(int, int, String)
+     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #startOp(int, int, String)
+     * @see #finishOp(int, int, String)
+     *
+     * @hide
+     */
+    @TestApi
+    public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
+        synchronized (mActiveWatchers) {
+            final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
+            if (cb != null) {
+                try {
+                    mService.stopWatchingActive(cb);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
     private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
         return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
     }
@@ -1991,15 +2147,11 @@
      * @hide
      */
     public int noteOp(int op, int uid, String packageName) {
-        try {
-            int mode = mService.noteOperation(op, uid, packageName);
-            if (mode == MODE_ERRORED) {
-                throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
-            }
-            return mode;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        final int mode = noteOpNoThrow(op, uid, packageName);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
         }
+        return mode;
     }
 
     /**
@@ -2078,6 +2230,11 @@
         }
     }
 
+    /** @hide */
+    public int startOp(int op) {
+        return startOp(op, Process.myUid(), mContext.getOpPackageName());
+    }
+
     /**
      * Report that an application has started executing a long-running operation.  Note that you
      * must pass in both the uid and name of the application to be checked; this function will
@@ -2086,6 +2243,7 @@
      * the current time and the operation will be marked as "running".  In this case you must
      * later call {@link #finishOp(int, int, String)} to report when the application is no
      * longer performing the operation.
+     *
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
@@ -2096,15 +2254,34 @@
      * @hide
      */
     public int startOp(int op, int uid, String packageName) {
-        try {
-            int mode = mService.startOperation(getToken(mService), op, uid, packageName);
-            if (mode == MODE_ERRORED) {
-                throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
-            }
-            return mode;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        return startOp(op, uid, packageName, false);
+    }
+
+    /**
+     * Report that an application has started executing a long-running operation. Similar
+     * to {@link #startOp(String, int, String) except that if the mode is {@link #MODE_DEFAULT}
+     * the operation should succeed since the caller has performed its standard permission
+     * checks which passed and would perform the protected operation for this mode.
+     *
+     * @param op The operation to start.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+     *
+     * @throws SecurityException If the app has been configured to crash on this op or
+     * the package is not in the passed in UID.
+     *
+     * @hide
+     */
+    public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
         }
+        return mode;
     }
 
     /**
@@ -2113,18 +2290,32 @@
      * @hide
      */
     public int startOpNoThrow(int op, int uid, String packageName) {
+        return startOpNoThrow(op, uid, packageName, false);
+    }
+
+    /**
+     * Like {@link #startOp(int, int, String, boolean)} but instead of throwing a
+     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @param op The operation to start.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+     *
+     * @hide
+     */
+    public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
         try {
-            return mService.startOperation(getToken(mService), op, uid, packageName);
+            return mService.startOperation(getToken(mService), op, uid, packageName,
+                    startIfModeDefault);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /** @hide */
-    public int startOp(int op) {
-        return startOp(op, Process.myUid(), mContext.getOpPackageName());
-    }
-
     /**
      * Report that an application is no longer performing an operation that had previously
      * been started with {@link #startOp(int, int, String)}.  There is no validation of input
@@ -2145,7 +2336,21 @@
         finishOp(op, Process.myUid(), mContext.getOpPackageName());
     }
 
-    /** @hide */
+    /**
+     * Checks whether the given op for a UID and package is active.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can query only for your UID.
+     *
+     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #stopWatchingMode(OnOpChangedListener)
+     * @see #finishOp(int)
+     * @see #startOp(int)
+     *
+     * @hide */
+    @TestApi
+    // TODO: Uncomment below annotation once b/73559440 is fixed
+    // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
     public boolean isOperationActive(int code, int uid, String packageName) {
         try {
             return mService.isOperationActive(code, uid, packageName);
diff --git a/android/app/Application.java b/android/app/Application.java
index 5822f5c..4531f53 100644
--- a/android/app/Application.java
+++ b/android/app/Application.java
@@ -16,8 +16,6 @@
 
 package android.app;
 
-import java.util.ArrayList;
-
 import android.annotation.CallSuper;
 import android.content.ComponentCallbacks;
 import android.content.ComponentCallbacks2;
@@ -26,6 +24,10 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
+
+import java.util.ArrayList;
 
 /**
  * Base class for maintaining global application state. You can provide your own
@@ -45,6 +47,7 @@
  * </p>
  */
 public class Application extends ContextWrapper implements ComponentCallbacks2 {
+    private static final String TAG = "Application";
     private ArrayList<ComponentCallbacks> mComponentCallbacks =
             new ArrayList<ComponentCallbacks>();
     private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
@@ -86,11 +89,21 @@
     /**
      * Called when the application is starting, before any activity, service,
      * or receiver objects (excluding content providers) have been created.
-     * Implementations should be as quick as possible (for example using
+     *
+     * <p>Implementations should be as quick as possible (for example using
      * lazy initialization of state) since the time spent in this function
      * directly impacts the performance of starting the first activity,
-     * service, or receiver in a process.
-     * If you override this method, be sure to call super.onCreate().
+     * service, or receiver in a process.</p>
+     *
+     * <p>If you override this method, be sure to call {@code super.onCreate()}.</p>
+     *
+     * <p class="note">Be aware that direct boot may also affect callback order on
+     * Android {@link android.os.Build.VERSION_CODES#N} and later devices.
+     * Until the user unlocks the device, only direct boot aware components are
+     * allowed to run. You should consider that all direct boot unaware
+     * components, including such {@link android.content.ContentProvider}, are
+     * disabled until user unlock happens, especially when component callback
+     * order matters.</p>
      */
     @CallSuper
     public void onCreate() {
@@ -180,6 +193,16 @@
         }
     }
 
+    /**
+     * Returns the name of the current process. A package's default process name
+     * is the same as its package name. Non-default processes will look like
+     * "$PACKAGE_NAME:$NAME", where $NAME corresponds to an android:process
+     * attribute within AndroidManifest.xml.
+     */
+    public static String getProcessName() {
+        return ActivityThread.currentProcessName();
+    }
+
     // ------------------ Internal API ------------------
 
     /**
@@ -187,7 +210,7 @@
      */
     /* package */ final void attach(Context context) {
         attachBaseContext(context);
-        mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
+        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
     }
 
     /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
@@ -289,4 +312,47 @@
             }
         }
     }
-}
\ No newline at end of file
+
+    /** @hide */
+    @Override
+    public AutofillManager.AutofillClient getAutofillClient() {
+        final AutofillManager.AutofillClient client = super.getAutofillClient();
+        if (client != null) {
+            return client;
+        }
+        if (android.view.autofill.Helper.sVerbose) {
+            Log.v(TAG, "getAutofillClient(): null on super, trying to find activity thread");
+        }
+        // Okay, ppl use the application context when they should not. This breaks
+        // autofill among other things. We pick the focused activity since autofill
+        // interacts only with the currently focused activity and we need the fill
+        // client only if a call comes from the focused activity. Sigh...
+        final ActivityThread activityThread = ActivityThread.currentActivityThread();
+        if (activityThread == null) {
+            return null;
+        }
+        final int activityCount = activityThread.mActivities.size();
+        for (int i = 0; i < activityCount; i++) {
+            final ActivityThread.ActivityClientRecord record =
+                    activityThread.mActivities.valueAt(i);
+            if (record == null) {
+                continue;
+            }
+            final Activity activity = record.activity;
+            if (activity == null) {
+                continue;
+            }
+            if (activity.getWindow().getDecorView().hasFocus()) {
+                if (android.view.autofill.Helper.sVerbose) {
+                    Log.v(TAG, "getAutofillClient(): found activity for " + this + ": " + activity);
+                }
+                return activity;
+            }
+        }
+        if (android.view.autofill.Helper.sVerbose) {
+            Log.v(TAG, "getAutofillClient(): none of the " + activityCount + " activities on "
+                    + this + " have focus");
+        }
+        return null;
+    }
+}
diff --git a/android/app/ApplicationPackageManager.java b/android/app/ApplicationPackageManager.java
index cc68c05..a68136b 100644
--- a/android/app/ApplicationPackageManager.java
+++ b/android/app/ApplicationPackageManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
+import android.annotation.UserIdInt;
 import android.annotation.XmlRes;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -69,6 +70,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -140,6 +142,11 @@
     }
 
     @Override
+    public int getUserId() {
+        return mContext.getUserId();
+    }
+
+    @Override
     public PackageInfo getPackageInfo(String packageName, int flags)
             throws NameNotFoundException {
         return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
@@ -1026,19 +1033,25 @@
     }
 
     @Override
-    public ResolveInfo resolveService(Intent intent, int flags) {
+    public ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId) {
         try {
             return mPM.resolveService(
                 intent,
                 intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                 flags,
-                mContext.getUserId());
+                userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    public ResolveInfo resolveService(Intent intent, int flags) {
+        return resolveServiceAsUser(intent, flags, mContext.getUserId());
+    }
+
+    @Override
     @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
         try {
@@ -1349,11 +1362,10 @@
         if (badgeColor == null) {
             return null;
         }
-        badgeColor.setTint(getUserBadgeColor(user));
         Drawable badgeForeground = getDrawableForDensity(
                 com.android.internal.R.drawable.ic_corp_badge_case, density);
-        Drawable badge = new LayerDrawable(
-                new Drawable[] {badgeColor, badgeForeground });
+        badgeForeground.setTint(getUserBadgeColor(user));
+        Drawable badge = new LayerDrawable(new Drawable[] {badgeColor, badgeForeground });
         return badge;
     }
 
@@ -1409,7 +1421,7 @@
                     sameUid ? app.sourceDir : app.publicSourceDir,
                     sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
                     app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
-                    mContext.mLoadedApk);
+                    mContext.mPackageInfo);
         if (r != null) {
             return r;
         }
@@ -2140,16 +2152,43 @@
     }
 
     @Override
-    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
-            int userId) {
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            PersistableBundle appExtras, PersistableBundle launcherExtras,
+            String dialogMessage) {
+        // TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
         try {
-            return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId);
+            return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
+                    launcherExtras, mContext.getOpPackageName(), mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+        try {
+            return mPM.getSuspendedPackageAppExtras(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public Bundle getSuspendedPackageAppExtras() {
+        final PersistableBundle extras = getSuspendedPackageAppExtras(mContext.getOpPackageName());
+        return extras != null ? new Bundle(extras.deepCopy()) : null;
+    }
+
+    @Override
+    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
+        try {
+            mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         try {
             return mPM.isPackageSuspendedForUser(packageName, userId);
@@ -2160,6 +2199,17 @@
 
     /** @hide */
     @Override
+    public boolean isPackageSuspended(String packageName) {
+        return isPackageSuspendedForUser(packageName, mContext.getUserId());
+    }
+
+    @Override
+    public boolean isPackageSuspended() {
+        return isPackageSuspendedForUser(mContext.getOpPackageName(), mContext.getUserId());
+    }
+
+    /** @hide */
+    @Override
     public void setApplicationCategoryHint(String packageName, int categoryHint) {
         try {
             mPM.setApplicationCategoryHint(packageName, categoryHint,
@@ -2797,4 +2847,22 @@
             return mArtManager;
         }
     }
+
+    @Override
+    public String getSystemTextClassifierPackageName() {
+        try {
+            return mPM.getSystemTextClassifierPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public boolean isPackageStateProtected(String packageName, int userId) {
+        try {
+            return mPM.isPackageStateProtected(packageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/android/app/ClientTransactionHandler.java b/android/app/ClientTransactionHandler.java
index 0f66652..0639b00 100644
--- a/android/app/ClientTransactionHandler.java
+++ b/android/app/ClientTransactionHandler.java
@@ -17,11 +17,14 @@
 
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
+import android.app.servertransaction.TransactionExecutor;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.IBinder;
+import android.util.MergedConfiguration;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
 
 import java.io.PrintWriter;
@@ -42,6 +45,23 @@
         sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
     }
 
+    /**
+     * Execute transaction immediately without scheduling it. This is used for local requests, so
+     * it will also recycle the transaction.
+     */
+    @VisibleForTesting
+    public void executeTransaction(ClientTransaction transaction) {
+        transaction.preExecute(this);
+        getTransactionExecutor().execute(transaction);
+        transaction.recycle();
+    }
+
+    /**
+     * Get the {@link TransactionExecutor} that will be performing lifecycle transitions and
+     * callbacks for activities.
+     */
+    abstract TransactionExecutor getTransactionExecutor();
+
     abstract void sendMessage(int what, Object obj);
 
 
@@ -60,19 +80,36 @@
 
     /** Destroy the activity. */
     public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
-            boolean getNonConfigInstance);
+            boolean getNonConfigInstance, String reason);
 
     /** Pause the activity. */
     public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport, PendingTransactionActions pendingActions);
+            int configChanges, PendingTransactionActions pendingActions, String reason);
 
-    /** Resume the activity. */
-    public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
-            String reason);
+    /**
+     * Resume the activity.
+     * @param token Target activity token.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param isForward Flag indicating if next transition is forward.
+     * @param reason Reason for performing this operation.
+     */
+    public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
+            boolean isForward, String reason);
 
-    /** Stop the activity. */
+    /**
+     * Stop the activity.
+     * @param token Target activity token.
+     * @param show Flag indicating whether activity is still shown.
+     * @param configChanges Activity configuration changes.
+     * @param pendingActions Pending actions to be used on this or later stages of activity
+     *                       transaction.
+     * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
+     *                          request for a transaction.
+     * @param reason Reason for performing this operation.
+     */
     public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
-            PendingTransactionActions pendingActions);
+            PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
 
     /** Report that activity was stopped to server. */
     public abstract void reportStop(PendingTransactionActions pendingActions);
@@ -111,7 +148,7 @@
             PendingTransactionActions pendingActions);
 
     /** Get package info. */
-    public abstract LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
+    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo);
 
     /** Deliver app configuration change notification. */
@@ -124,6 +161,39 @@
     public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
 
     /**
+     * Prepare activity relaunch to update internal bookkeeping. This is used to track multiple
+     * relaunch and config update requests.
+     * @param token Activity token.
+     * @param pendingResults Activity results to be delivered.
+     * @param pendingNewIntents New intent messages to be delivered.
+     * @param configChanges Mask of configuration changes that have occurred.
+     * @param config New configuration applied to the activity.
+     * @param preserveWindow Whether the activity should try to reuse the window it created,
+     *                        including the decor view after the relaunch.
+     * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
+     *         relaunch, or {@code null} if relaunch cancelled.
+     */
+    public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token,
+            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+            int configChanges, MergedConfiguration config, boolean preserveWindow);
+
+    /**
+     * Perform activity relaunch.
+     * @param r Activity client record prepared for relaunch.
+     * @param pendingActions Pending actions to be used on later stages of activity transaction.
+     * */
+    public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r,
+            PendingTransactionActions pendingActions);
+
+    /**
+     * Report that relaunch request was handled.
+     * @param token Target activity token.
+     * @param pendingActions Pending actions initialized on earlier stages of activity transaction.
+     *                       Used to check if we should report relaunch to WM.
+     * */
+    public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions);
+
+    /**
      * Debugging output.
      * @param pw {@link PrintWriter} to write logs to.
      * @param prefix Prefix to prepend to output.
diff --git a/android/app/ContextImpl.java b/android/app/ContextImpl.java
index 4914ffa..71b88fa 100644
--- a/android/app/ContextImpl.java
+++ b/android/app/ContextImpl.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -91,6 +92,7 @@
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
 
 class ReceiverRestrictedContext extends ContextWrapper {
     ReceiverRestrictedContext(Context base) {
@@ -159,12 +161,12 @@
     private ArrayMap<String, File> mSharedPrefsPaths;
 
     final @NonNull ActivityThread mMainThread;
-    final @NonNull LoadedApk mLoadedApk;
+    final @NonNull LoadedApk mPackageInfo;
     private @Nullable ClassLoader mClassLoader;
 
     private final @Nullable IBinder mActivityToken;
 
-    private final @Nullable UserHandle mUser;
+    private final @NonNull UserHandle mUser;
 
     private final ApplicationContentResolver mContentResolver;
 
@@ -187,6 +189,7 @@
     private @Nullable String mSplitName = null;
 
     private AutofillClient mAutofillClient = null;
+    private boolean mIsAutofillCompatEnabled;
 
     private final Object mSync = new Object();
 
@@ -206,6 +209,17 @@
     // The system service cache for the system services that are cached per-ContextImpl.
     final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
 
+    static final int STATE_UNINITIALIZED = 0;
+    static final int STATE_INITIALIZING = 1;
+    static final int STATE_READY = 2;
+
+    /**
+     * Initialization state for each service. Any of {@link #STATE_UNINITIALIZED},
+     * {@link #STATE_INITIALIZING} or {@link #STATE_READY},
+     */
+    final AtomicInteger[] mServiceInitializationStateArray =
+            SystemServiceRegistry.createServiceInitializationStateArray();
+
     static ContextImpl getImpl(Context context) {
         Context nextContext;
         while ((context instanceof ContextWrapper) &&
@@ -257,8 +271,8 @@
 
     @Override
     public Context getApplicationContext() {
-        return (mLoadedApk != null) ?
-                mLoadedApk.getApplication() : mMainThread.getApplication();
+        return (mPackageInfo != null) ?
+                mPackageInfo.getApplication() : mMainThread.getApplication();
     }
 
     @Override
@@ -302,15 +316,15 @@
 
     @Override
     public ClassLoader getClassLoader() {
-        return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader());
+        return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
     }
 
     @Override
     public String getPackageName() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getPackageName();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getPackageName();
         }
-        // No mLoadedApk means this is a Context for the system itself,
+        // No mPackageInfo means this is a Context for the system itself,
         // and this here is its name.
         return "android";
     }
@@ -329,24 +343,24 @@
 
     @Override
     public ApplicationInfo getApplicationInfo() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getApplicationInfo();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getApplicationInfo();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageResourcePath() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getResDir();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getResDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageCodePath() {
-        if (mLoadedApk != null) {
-            return mLoadedApk.getAppDir();
+        if (mPackageInfo != null) {
+            return mPackageInfo.getAppDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
@@ -356,7 +370,7 @@
         // At least one application in the world actually passes in a null
         // name.  This happened to work because when we generated the file name
         // we would stringify it to "null.xml".  Nice.
-        if (mLoadedApk.getApplicationInfo().targetSdkVersion <
+        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
                 Build.VERSION_CODES.KITKAT) {
             if (name == null) {
                 name = "null";
@@ -408,6 +422,7 @@
         return sp;
     }
 
+    @GuardedBy("ContextImpl.class")
     private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
         if (sSharedPrefsCache == null) {
             sSharedPrefsCache = new ArrayMap<>();
@@ -913,14 +928,14 @@
 
     /** @hide */
     @Override
-    public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+    public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
         if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
             throw new AndroidRuntimeException(
                     "Calling startActivities() from outside of an Activity "
                     + " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
                     + " Is this really what you want?");
         }
-        mMainThread.getInstrumentation().execStartActivitiesAsUser(
+        return mMainThread.getInstrumentation().execStartActivitiesAsUser(
                 getOuterContext(), mMainThread.getApplicationThread(), null,
                 (Activity) null, intents, options, userHandle.getIdentifier());
     }
@@ -1104,11 +1119,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1208,11 +1223,11 @@
             Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1262,11 +1277,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1344,11 +1359,11 @@
             Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mLoadedApk != null) {
+            if (mPackageInfo != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1425,11 +1440,11 @@
             Handler scheduler, Context context, int flags) {
         IIntentReceiver rd = null;
         if (receiver != null) {
-            if (mLoadedApk != null && context != null) {
+            if (mPackageInfo != null && context != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mLoadedApk.getReceiverDispatcher(
+                rd = mPackageInfo.getReceiverDispatcher(
                     receiver, context, scheduler,
                     mMainThread.getInstrumentation(), true);
             } else {
@@ -1456,8 +1471,8 @@
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
-        if (mLoadedApk != null) {
-            IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher(
+        if (mPackageInfo != null) {
+            IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
                     getOuterContext(), receiver);
             try {
                 ActivityManager.getService().unregisterReceiver(rd);
@@ -1565,8 +1580,7 @@
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
         warnIfCallingFromSystemProcess();
-        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
-                Process.myUserHandle());
+        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
     }
 
     /** @hide */
@@ -1590,7 +1604,7 @@
     @Override
     public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
             int flags) {
-        return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
     }
 
     /** @hide */
@@ -1612,16 +1626,16 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mLoadedApk != null) {
-            sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        if (mPackageInfo != null) {
+            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
         } else {
             throw new RuntimeException("Not supported in system context");
         }
         validateServiceIntent(service);
         try {
             IBinder token = getActivityToken();
-            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null
-                    && mLoadedApk.getApplicationInfo().targetSdkVersion
+            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
+                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                     < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 flags |= BIND_WAIVE_PRIORITY;
             }
@@ -1645,8 +1659,8 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mLoadedApk != null) {
-            IServiceConnection sd = mLoadedApk.forgetServiceDispatcher(
+        if (mPackageInfo != null) {
+            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
                     getOuterContext(), conn);
             try {
                 ActivityManager.getService().unbindService(sd);
@@ -1699,6 +1713,9 @@
                 Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                 return PackageManager.PERMISSION_GRANTED;
             }
+            Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+                    + permission);
+            return PackageManager.PERMISSION_DENIED;
         }
 
         try {
@@ -1991,20 +2008,40 @@
         }
     }
 
+    private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+        final String[] splitResDirs;
+        final ClassLoader classLoader;
+        try {
+            splitResDirs = pi.getSplitPaths(splitName);
+            classLoader = pi.getSplitClassLoader(splitName);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return ResourcesManager.getInstance().getResources(activityToken,
+                pi.getResDir(),
+                splitResDirs,
+                pi.getOverlayDirs(),
+                pi.getApplicationInfo().sharedLibraryFiles,
+                displayId,
+                overrideConfig,
+                compatInfo,
+                classLoader);
+    }
+
     @Override
     public Context createApplicationContext(ApplicationInfo application, int flags)
             throws NameNotFoundException {
-        LoadedApk loadedApk = mMainThread.getLoadedApk(application,
-                mResources.getCompatibilityInfo(),
+        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
-        if (loadedApk != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken,
+        if (pi != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
+            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2018,8 +2055,7 @@
     @Override
     public Context createPackageContext(String packageName, int flags)
             throws NameNotFoundException {
-        return createPackageContextAsUser(packageName, flags,
-                mUser != null ? mUser : Process.myUserHandle());
+        return createPackageContextAsUser(packageName, flags, mUser);
     }
 
     @Override
@@ -2028,21 +2064,20 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user,
+            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
                     flags, null);
         }
 
-        LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName,
-                mResources.getCompatibilityInfo(),
+        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
-        if (loadedApk != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user,
+        if (pi != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
                     flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
+            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2056,21 +2091,30 @@
 
     @Override
     public Context createContextForSplit(String splitName) throws NameNotFoundException {
-        if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
             // All Splits are always loaded.
             return this;
         }
 
-        final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName);
+        final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
+        final String[] paths = mPackageInfo.getSplitPaths(splitName);
 
-        final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName,
+        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
                 mActivityToken, mUser, mFlags, classLoader);
 
         final int displayId = mDisplay != null
                 ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-        context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName,
-                mActivityToken, displayId));
+        context.setResources(ResourcesManager.getInstance().getResources(
+                mActivityToken,
+                mPackageInfo.getResDir(),
+                paths,
+                mPackageInfo.getOverlayDirs(),
+                mPackageInfo.getApplicationInfo().sharedLibraryFiles,
+                displayId,
+                null,
+                mPackageInfo.getCompatibilityInfo(),
+                classLoader));
         return context;
     }
 
@@ -2080,11 +2124,11 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
-        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
+        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
     }
@@ -2095,11 +2139,11 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = display.getDisplayId();
-        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
+        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
                 null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         context.mDisplay = display;
         return context;
@@ -2109,7 +2153,7 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2117,7 +2161,7 @@
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2166,14 +2210,14 @@
 
     @Override
     public File getDataDir() {
-        if (mLoadedApk != null) {
+        if (mPackageInfo != null) {
             File res = null;
             if (isCredentialProtectedStorage()) {
-                res = mLoadedApk.getCredentialProtectedDataDirFile();
+                res = mPackageInfo.getCredentialProtectedDataDirFile();
             } else if (isDeviceProtectedStorage()) {
-                res = mLoadedApk.getDeviceProtectedDataDirFile();
+                res = mPackageInfo.getDeviceProtectedDataDirFile();
             } else {
-                res = mLoadedApk.getDataDirFile();
+                res = mPackageInfo.getDataDirFile();
             }
 
             if (res != null) {
@@ -2207,6 +2251,12 @@
 
     /** {@hide} */
     @Override
+    public UserHandle getUser() {
+        return mUser;
+    }
+
+    /** {@hide} */
+    @Override
     public int getUserId() {
         return mUser.getIdentifier();
     }
@@ -2223,11 +2273,24 @@
         mAutofillClient = client;
     }
 
+    /** @hide */
+    @Override
+    public boolean isAutofillCompatibilityEnabled() {
+        return mIsAutofillCompatEnabled;
+    }
+
+    /** @hide */
+    @TestApi
+    @Override
+    public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+        mIsAutofillCompatEnabled = autofillCompatEnabled;
+    }
+
     static ContextImpl createSystemContext(ActivityThread mainThread) {
-        LoadedApk loadedApk = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
+        LoadedApk packageInfo = new LoadedApk(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                 null);
-        context.setResources(loadedApk.getResources());
+        context.setResources(packageInfo.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
         return context;
@@ -2238,35 +2301,35 @@
      * Make sure that the created system UI context shares the same LoadedApk as the system context.
      */
     static ContextImpl createSystemUiContext(ContextImpl systemContext) {
-        final LoadedApk loadedApk = systemContext.mLoadedApk;
-        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null,
+        final LoadedApk packageInfo = systemContext.mPackageInfo;
+        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
                 null, null, 0, null);
-        context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null,
-                loadedApk.getCompatibilityInfo()));
+        context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
+                packageInfo.getCompatibilityInfo()));
         return context;
     }
 
-    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
-        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
-        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
+    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
+        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                 null);
-        context.setResources(loadedApk.getResources());
+        context.setResources(packageInfo.getResources());
         return context;
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
-            LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId,
+            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
             Configuration overrideConfiguration) {
-        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
+        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
 
-        String[] splitDirs = loadedApk.getSplitResDirs();
-        ClassLoader classLoader = loadedApk.getClassLoader();
+        String[] splitDirs = packageInfo.getSplitResDirs();
+        ClassLoader classLoader = packageInfo.getClassLoader();
 
-        if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
             try {
-                classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName);
-                splitDirs = loadedApk.getSplitPaths(activityInfo.splitName);
+                classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
+                splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
             } catch (NameNotFoundException e) {
                 // Nothing above us can handle a NameNotFoundException, better crash.
                 throw new RuntimeException(e);
@@ -2275,14 +2338,14 @@
             }
         }
 
-        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                 activityToken, null, 0, classLoader);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
 
         final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
-                ? loadedApk.getCompatibilityInfo()
+                ? packageInfo.getCompatibilityInfo()
                 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
 
         final ResourcesManager resourcesManager = ResourcesManager.getInstance();
@@ -2290,10 +2353,10 @@
         // Create the base resources for which all configuration contexts for this Activity
         // will be rebased upon.
         context.setResources(resourcesManager.createBaseActivityResources(activityToken,
-                loadedApk.getResDir(),
+                packageInfo.getResDir(),
                 splitDirs,
-                loadedApk.getOverlayDirs(),
-                loadedApk.getApplicationInfo().sharedLibraryFiles,
+                packageInfo.getOverlayDirs(),
+                packageInfo.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfiguration,
                 compatInfo,
@@ -2304,7 +2367,7 @@
     }
 
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
-            @NonNull LoadedApk loadedApk, @Nullable String splitName,
+            @NonNull LoadedApk packageInfo, @Nullable String splitName,
             @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
             @Nullable ClassLoader classLoader) {
         mOuterContext = this;
@@ -2313,10 +2376,10 @@
         // location for application.
         if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
-            final File dataDir = loadedApk.getDataDirFile();
-            if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) {
+            final File dataDir = packageInfo.getDataDirFile();
+            if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-            } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) {
+            } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
             }
         }
@@ -2330,7 +2393,7 @@
         }
         mUser = user;
 
-        mLoadedApk = loadedApk;
+        mPackageInfo = packageInfo;
         mSplitName = splitName;
         mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
@@ -2341,8 +2404,8 @@
             setResources(container.mResources);
             mDisplay = container.mDisplay;
         } else {
-            mBasePackageName = loadedApk.mPackageName;
-            ApplicationInfo ainfo = loadedApk.getApplicationInfo();
+            mBasePackageName = packageInfo.mPackageName;
+            ApplicationInfo ainfo = packageInfo.getApplicationInfo();
             if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
                 // Special case: system components allow themselves to be loaded in to other
                 // processes.  For purposes of app ops, we must then consider the context as
@@ -2354,7 +2417,7 @@
             }
         }
 
-        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
+        mContentResolver = new ApplicationContentResolver(this, mainThread);
     }
 
     void setResources(Resources r) {
@@ -2365,7 +2428,7 @@
     }
 
     void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
-        mLoadedApk.installSystemApplicationInfo(info, classLoader);
+        mPackageInfo.installSystemApplicationInfo(info, classLoader);
     }
 
     final void scheduleFinalCleanup(String who, String what) {
@@ -2374,7 +2437,7 @@
 
     final void performFinalCleanup(String who, String what) {
         //Log.i(TAG, "Cleanup up context: " + this);
-        mLoadedApk.removeContextRegistrations(getOuterContext(), who, what);
+        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
     }
 
     final Context getReceiverRestrictedContext() {
@@ -2470,13 +2533,10 @@
 
     private static final class ApplicationContentResolver extends ContentResolver {
         private final ActivityThread mMainThread;
-        private final UserHandle mUser;
 
-        public ApplicationContentResolver(
-                Context context, ActivityThread mainThread, UserHandle user) {
+        public ApplicationContentResolver(Context context, ActivityThread mainThread) {
             super(context);
             mMainThread = Preconditions.checkNotNull(mainThread);
-            mUser = Preconditions.checkNotNull(user);
         }
 
         @Override
@@ -2522,7 +2582,7 @@
 
         /** @hide */
         protected int resolveUserIdFromAuthority(String auth) {
-            return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
+            return ContentProvider.getUserIdFromAuthority(auth, getUserId());
         }
     }
 }
diff --git a/android/app/Dialog.java b/android/app/Dialog.java
index 2b648ea..e4a0583 100644
--- a/android/app/Dialog.java
+++ b/android/app/Dialog.java
@@ -291,7 +291,10 @@
                 if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                     mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                 }
-                mDecor.setVisibility(View.VISIBLE);
+                if (mDecor.getVisibility() != View.VISIBLE) {
+                    mDecor.setVisibility(View.VISIBLE);
+                    sendShowMessage();
+                }
             }
             return;
         }
@@ -318,16 +321,20 @@
         }
 
         WindowManager.LayoutParams l = mWindow.getAttributes();
+        boolean restoreSoftInputMode = false;
         if ((l.softInputMode
                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
-            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
-            nl.copyFrom(l);
-            nl.softInputMode |=
+            l.softInputMode |=
                     WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
-            l = nl;
+            restoreSoftInputMode = true;
         }
 
         mWindowManager.addView(mDecor, l);
+        if (restoreSoftInputMode) {
+            l.softInputMode &=
+                    ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+        }
+
         mShowing = true;
 
         sendShowMessage();
@@ -609,18 +616,19 @@
 
     /**
      * A key was pressed down.
+     * <p>
+     * If the focused view didn't want this event, this method is called.
+     * <p>
+     * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+     * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+     * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
      * 
-     * <p>If the focused view didn't want this event, this method is called.
-     *
-     * <p>The default implementation consumed the KEYCODE_BACK to later
-     * handle it in {@link #onKeyUp}.
-     *
      * @see #onKeyUp
      * @see android.view.KeyEvent
      */
     @Override
     public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
+        if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
             event.startTracking();
             return true;
         }
@@ -640,16 +648,18 @@
 
     /**
      * A key was released.
-     * 
-     * <p>The default implementation handles KEYCODE_BACK to close the
-     * dialog.
+     * <p>
+     * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+     * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+     * KEYCODE_ESCAPE} to close the dialog.
      *
      * @see #onKeyDown
-     * @see KeyEvent
+     * @see android.view.KeyEvent
      */
     @Override
     public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+        if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+                && event.isTracking()
                 && !event.isCanceled()) {
             onBackPressed();
             return true;
diff --git a/android/app/EphemeralResolverService.java b/android/app/EphemeralResolverService.java
deleted file mode 100644
index 427a038..0000000
--- a/android/app/EphemeralResolverService.java
+++ /dev/null
@@ -1,116 +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.app;
-
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.app.InstantAppResolverService.InstantAppResolutionCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.EphemeralResolveInfo;
-import android.content.pm.InstantAppResolveInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Base class for implementing the resolver service.
- * @hide
- * @removed
- * @deprecated use InstantAppResolverService instead
- */
-@Deprecated
-@SystemApi
-public abstract class EphemeralResolverService extends InstantAppResolverService {
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
-    private static final String TAG = "PackageManager";
-
-    /**
-     * Called to retrieve resolve info for ephemeral applications.
-     *
-     * @param digestPrefix The hash prefix of the ephemeral's domain.
-     * @param prefixMask A mask that was applied to each digest prefix. This should
-     *      be used when comparing against the digest prefixes as all bits might
-     *      not be set.
-     * @deprecated use {@link #onGetEphemeralResolveInfo(int[])} instead
-     */
-    @Deprecated
-    public abstract List<EphemeralResolveInfo> onEphemeralResolveInfoList(
-            int digestPrefix[], int prefix);
-
-    /**
-     * Called to retrieve resolve info for ephemeral applications.
-     *
-     * @param digestPrefix The hash prefix of the ephemeral's domain.
-     */
-    public List<EphemeralResolveInfo> onGetEphemeralResolveInfo(int digestPrefix[]) {
-        return onEphemeralResolveInfoList(digestPrefix, 0xFFFFF000);
-    }
-
-    /**
-     * Called to retrieve intent filters for ephemeral applications.
-     *
-     * @param hostName The name of the host to get intent filters for.
-     */
-    public EphemeralResolveInfo onGetEphemeralIntentFilter(String hostName) {
-        throw new IllegalStateException("Must define");
-    }
-
-    @Override
-    public Looper getLooper() {
-        return super.getLooper();
-    }
-
-    @Override
-    void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
-            InstantAppResolutionCallback callback) {
-        if (DEBUG_EPHEMERAL) {
-            Log.d(TAG, "Legacy resolver; getInstantAppResolveInfo;"
-                    + " prefix: " + Arrays.toString(digestPrefix));
-        }
-        final List<EphemeralResolveInfo> response = onGetEphemeralResolveInfo(digestPrefix);
-        final int responseSize = response == null ? 0 : response.size();
-        final List<InstantAppResolveInfo> resultList = new ArrayList<>(responseSize);
-        for (int i = 0; i < responseSize; i++) {
-            resultList.add(response.get(i).getInstantAppResolveInfo());
-        }
-        callback.onInstantAppResolveInfo(resultList);
-    }
-
-    @Override
-    void _onGetInstantAppIntentFilter(int[] digestPrefix, String token,
-            String hostName, InstantAppResolutionCallback callback) {
-        if (DEBUG_EPHEMERAL) {
-            Log.d(TAG, "Legacy resolver; getInstantAppIntentFilter;"
-                    + " prefix: " + Arrays.toString(digestPrefix));
-        }
-        final EphemeralResolveInfo response = onGetEphemeralIntentFilter(hostName);
-        final List<InstantAppResolveInfo> resultList = new ArrayList<>(1);
-        resultList.add(response.getInstantAppResolveInfo());
-        callback.onInstantAppResolveInfo(resultList);
-    }
-}
diff --git a/android/app/Fragment.java b/android/app/Fragment.java
index 4ff07f2..8e8270a 100644
--- a/android/app/Fragment.java
+++ b/android/app/Fragment.java
@@ -2311,7 +2311,7 @@
      * transaction have called {@link #startPostponedEnterTransition()}.
      * <p>
      * This method should be called before being added to the FragmentTransaction or
-     * in {@link #onCreate(Bundle), {@link #onAttach(Context)}, or
+     * in {@link #onCreate(Bundle)}, {@link #onAttach(Context)}, or
      * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}}.
      * {@link #startPostponedEnterTransition()} must be called to allow the Fragment to
      * start the transitions.
diff --git a/android/app/GrantedUriPermission.java b/android/app/GrantedUriPermission.java
new file mode 100644
index 0000000..9e84fe1
--- /dev/null
+++ b/android/app/GrantedUriPermission.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.UriPermission;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents an {@link UriPermission} granted to a package.
+ *
+ * {@hide}
+ */
+public class GrantedUriPermission implements Parcelable {
+
+    public final Uri uri;
+    public final String packageName;
+
+    public GrantedUriPermission(@NonNull Uri uri, @Nullable String packageName) {
+        this.uri = uri;
+        this.packageName = packageName;
+    }
+
+    @Override
+    public String toString() {
+        return packageName + ":" + uri;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(uri, flags);
+        out.writeString(packageName);
+    }
+
+    public static final Parcelable.Creator<GrantedUriPermission> CREATOR =
+            new Parcelable.Creator<GrantedUriPermission>() {
+                @Override
+                public GrantedUriPermission createFromParcel(Parcel in) {
+                    return new GrantedUriPermission(in);
+                }
+
+                @Override
+                public GrantedUriPermission[] newArray(int size) {
+                    return new GrantedUriPermission[size];
+                }
+            };
+
+    private GrantedUriPermission(Parcel in) {
+        uri = in.readParcelable(null);
+        packageName = in.readString();
+    }
+}
diff --git a/android/app/InstantAppResolverService.java b/android/app/InstantAppResolverService.java
index c5dc86c..58d0aaf 100644
--- a/android/app/InstantAppResolverService.java
+++ b/android/app/InstantAppResolverService.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.annotation.SystemApi;
-import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.InstantAppResolveInfo;
@@ -35,6 +34,7 @@
 import com.android.internal.os.SomeArgs;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -43,7 +43,7 @@
  */
 @SystemApi
 public abstract class InstantAppResolverService extends Service {
-    private static final boolean DEBUG_EPHEMERAL = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
     private static final String TAG = "PackageManager";
 
     /** @hide */
@@ -53,23 +53,89 @@
     Handler mHandler;
 
     /**
-     * Called to retrieve resolve info for instant applications.
+     * Called to retrieve resolve info for instant applications immediately.
      *
      * @param digestPrefix The hash prefix of the instant app's domain.
+     * @deprecated should implement {@link #onGetInstantAppResolveInfo(Intent, int[], String,
+     *             InstantAppResolutionCallback)}
      */
+    @Deprecated
     public void onGetInstantAppResolveInfo(
             int digestPrefix[], String token, InstantAppResolutionCallback callback) {
-        throw new IllegalStateException("Must define");
+        throw new IllegalStateException("Must define onGetInstantAppResolveInfo");
     }
 
     /**
-     * Called to retrieve intent filters for instant applications.
+     * Called to retrieve intent filters for instant applications from potentially expensive
+     * sources.
      *
      * @param digestPrefix The hash prefix of the instant app's domain.
+     * @deprecated should implement {@link #onGetInstantAppIntentFilter(Intent, int[], String,
+     *             InstantAppResolutionCallback)}
      */
+    @Deprecated
     public void onGetInstantAppIntentFilter(
             int digestPrefix[], String token, InstantAppResolutionCallback callback) {
-        throw new IllegalStateException("Must define");
+        throw new IllegalStateException("Must define onGetInstantAppIntentFilter");
+    }
+
+    /**
+     * Called to retrieve resolve info for instant applications immediately. The response will be
+     * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided
+     * in response to this method may be partial to request a second phase of resolution which will
+     * result in a subsequent call to
+     * {@link #onGetInstantAppIntentFilter(Intent, int[], String, InstantAppResolutionCallback)}
+     *
+     *
+     * @param sanitizedIntent The sanitized {@link Intent} used for resolution. A sanitized Intent
+     *                        is an intent with potential PII removed from the original intent.
+     *                        Fields removed include extras and the host + path of the data, if
+     *                        defined.
+     * @param hostDigestPrefix The hash prefix of the instant app's domain.
+     * @param token A unique identifier that will be provided in calls to
+     *              {@link #onGetInstantAppIntentFilter(Intent, int[], String,
+     *              InstantAppResolutionCallback)}
+     *              and provided to the installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN} to
+     *              tie a single launch together.
+     * @param callback The {@link InstantAppResolutionCallback} to provide results to.
+     *
+     * @see InstantAppResolveInfo
+     */
+    public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix,
+            String token, InstantAppResolutionCallback callback) {
+        // if not overridden, forward to old methods and filter out non-web intents
+        if (sanitizedIntent.isWebIntent()) {
+            onGetInstantAppResolveInfo(hostDigestPrefix, token, callback);
+        } else {
+            callback.onInstantAppResolveInfo(Collections.emptyList());
+        }
+    }
+
+    /**
+     * Called to retrieve intent filters for potentially matching instant applications. Unlike
+     * {@link #onGetInstantAppResolveInfo(Intent, int[], String, InstantAppResolutionCallback)},
+     * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s
+     * provided in response to this method must be completely populated.
+     *
+     * @param sanitizedIntent The sanitized {@link Intent} used for resolution.
+     * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is
+     *                         defined.
+     * @param token A unique identifier that was provided in
+     *              {@link #onGetInstantAppResolveInfo(Intent, int[], String,
+     *              InstantAppResolutionCallback)}
+     *              and provided to the currently visible installer via
+     *              {@link Intent#EXTRA_INSTANT_APP_TOKEN}.
+     * @param callback The {@link InstantAppResolutionCallback} to provide results to
+     */
+    public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix,
+            String token, InstantAppResolutionCallback callback) {
+        Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden");
+        // if not overridden, forward to old methods and filter out non-web intents
+        if (sanitizedIntent.isWebIntent()) {
+            onGetInstantAppIntentFilter(hostDigestPrefix, token, callback);
+        } else {
+            callback.onInstantAppResolveInfo(Collections.emptyList());
+        }
     }
 
     /**
@@ -89,33 +155,33 @@
     public final IBinder onBind(Intent intent) {
         return new IInstantAppResolver.Stub() {
             @Override
-            public void getInstantAppResolveInfoList(
-                    int digestPrefix[], String token, int sequence, IRemoteCallback callback) {
-                if (DEBUG_EPHEMERAL) {
+            public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix,
+                    String token, int sequence, IRemoteCallback callback) {
+                if (DEBUG_INSTANT) {
                     Slog.v(TAG, "[" + token + "] Phase1 called; posting");
                 }
                 final SomeArgs args = SomeArgs.obtain();
                 args.arg1 = callback;
                 args.arg2 = digestPrefix;
                 args.arg3 = token;
-                mHandler.obtainMessage(
-                                ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence, 0, args)
-                        .sendToTarget();
+                args.arg4 = sanitizedIntent;
+                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO,
+                        sequence, 0, args).sendToTarget();
             }
 
             @Override
-            public void getInstantAppIntentFilterList(
-                    int digestPrefix[], String token, String hostName, IRemoteCallback callback) {
-                if (DEBUG_EPHEMERAL) {
+            public void getInstantAppIntentFilterList(Intent sanitizedIntent,
+                    int[] digestPrefix, String token, IRemoteCallback callback) {
+                if (DEBUG_INSTANT) {
                     Slog.v(TAG, "[" + token + "] Phase2 called; posting");
                 }
                 final SomeArgs args = SomeArgs.obtain();
                 args.arg1 = callback;
                 args.arg2 = digestPrefix;
                 args.arg3 = token;
-                args.arg4 = hostName;
-                mHandler.obtainMessage(
-                        ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, callback).sendToTarget();
+                args.arg4 = sanitizedIntent;
+                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER,
+                        callback).sendToTarget();
             }
         };
     }
@@ -142,29 +208,9 @@
         }
     }
 
-    @Deprecated
-    void _onGetInstantAppResolveInfo(int[] digestPrefix, String token,
-            InstantAppResolutionCallback callback) {
-        if (DEBUG_EPHEMERAL) {
-            Slog.d(TAG, "[" + token + "] Phase1 request;"
-                    + " prefix: " + Arrays.toString(digestPrefix));
-        }
-        onGetInstantAppResolveInfo(digestPrefix, token, callback);
-    }
-    @Deprecated
-    void _onGetInstantAppIntentFilter(int digestPrefix[], String token, String hostName,
-            InstantAppResolutionCallback callback) {
-        if (DEBUG_EPHEMERAL) {
-            Slog.d(TAG, "[" + token + "] Phase2 request;"
-                    + " prefix: " + Arrays.toString(digestPrefix));
-        }
-        onGetInstantAppIntentFilter(digestPrefix, token, callback);
-    }
-
     private final class ServiceHandler extends Handler {
         public static final int MSG_GET_INSTANT_APP_RESOLVE_INFO = 1;
         public static final int MSG_GET_INSTANT_APP_INTENT_FILTER = 2;
-
         public ServiceHandler(Looper looper) {
             super(looper, null /*callback*/, true /*async*/);
         }
@@ -179,9 +225,13 @@
                     final IRemoteCallback callback = (IRemoteCallback) args.arg1;
                     final int[] digestPrefix = (int[]) args.arg2;
                     final String token = (String) args.arg3;
+                    final Intent intent = (Intent) args.arg4;
                     final int sequence = message.arg1;
-                    _onGetInstantAppResolveInfo(
-                            digestPrefix, token,
+                    if (DEBUG_INSTANT) {
+                        Slog.d(TAG, "[" + token + "] Phase1 request;"
+                                + " prefix: " + Arrays.toString(digestPrefix));
+                    }
+                    onGetInstantAppResolveInfo(intent, digestPrefix, token,
                             new InstantAppResolutionCallback(sequence, callback));
                 } break;
 
@@ -190,9 +240,12 @@
                     final IRemoteCallback callback = (IRemoteCallback) args.arg1;
                     final int[] digestPrefix = (int[]) args.arg2;
                     final String token = (String) args.arg3;
-                    final String hostName = (String) args.arg4;
-                    _onGetInstantAppIntentFilter(
-                            digestPrefix, token, hostName,
+                    final Intent intent = (Intent) args.arg4;
+                    if (DEBUG_INSTANT) {
+                        Slog.d(TAG, "[" + token + "] Phase2 request;"
+                                + " prefix: " + Arrays.toString(digestPrefix));
+                    }
+                    onGetInstantAppIntentFilter(intent, digestPrefix, token,
                             new InstantAppResolutionCallback(-1 /*sequence*/, callback));
                 } break;
 
diff --git a/android/app/Instrumentation.java b/android/app/Instrumentation.java
index 3c38a4e..7f87814 100644
--- a/android/app/Instrumentation.java
+++ b/android/app/Instrumentation.java
@@ -1216,10 +1216,10 @@
                     + " disabling AppComponentFactory", new Throwable());
             return AppComponentFactory.DEFAULT;
         }
-        LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true);
+        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
         // This is in the case of starting up "android".
-        if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk;
-        return loadedApk.getAppFactory();
+        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
+        return apk.getAppFactory();
     }
 
     private void prePerformCreate(Activity activity) {
@@ -1679,7 +1679,7 @@
     public void execStartActivities(Context who, IBinder contextThread,
             IBinder token, Activity target, Intent[] intents, Bundle options) {
         execStartActivitiesAsUser(who, contextThread, token, target, intents, options,
-                UserHandle.myUserId());
+                who.getUserId());
     }
 
     /**
@@ -1688,9 +1688,13 @@
      * {@link ActivityMonitor} objects only match against the first activity in
      * the array.
      *
+     * @return The corresponding flag {@link ActivityManager#START_CANCELED},
+     *         {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was
+     *         successful.
+     *
      * {@hide}
      */
-    public void execStartActivitiesAsUser(Context who, IBinder contextThread,
+    public int execStartActivitiesAsUser(Context who, IBinder contextThread,
             IBinder token, Activity target, Intent[] intents, Bundle options,
             int userId) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
@@ -1705,11 +1709,11 @@
                     }
                     if (result != null) {
                         am.mHits++;
-                        return;
+                        return ActivityManager.START_CANCELED;
                     } else if (am.match(who, null, intents[0])) {
                         am.mHits++;
                         if (am.isBlocking()) {
-                            return;
+                            return ActivityManager.START_CANCELED;
                         }
                         break;
                     }
@@ -1727,6 +1731,7 @@
                 .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
                         token, options, userId);
             checkStartActivityResult(result, intents[0]);
+            return result;
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -1874,8 +1879,8 @@
      */
     public ActivityResult execStartActivityAsCaller(
             Context who, IBinder contextThread, IBinder token, Activity target,
-            Intent intent, int requestCode, Bundle options, IBinder permissionToken,
-            boolean ignoreTargetSecurity, int userId) {
+            Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity,
+            int userId) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
         if (mActivityMonitors != null) {
             synchronized (mSync) {
@@ -1906,8 +1911,7 @@
                 .startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target != null ? target.mEmbeddedID : null,
-                        requestCode, 0, null, options, permissionToken,
-                        ignoreTargetSecurity, userId);
+                        requestCode, 0, null, options, ignoreTargetSecurity, userId);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
diff --git a/android/app/KeyguardManager.java b/android/app/KeyguardManager.java
index 553099f..4a3fcaf 100644
--- a/android/app/KeyguardManager.java
+++ b/android/app/KeyguardManager.java
@@ -407,7 +407,7 @@
      * password.
      */
     public boolean isDeviceLocked() {
-        return isDeviceLocked(UserHandle.myUserId());
+        return isDeviceLocked(mContext.getUserId());
     }
 
     /**
@@ -432,7 +432,7 @@
      * @return true if a PIN, pattern or password was set.
      */
     public boolean isDeviceSecure() {
-        return isDeviceSecure(UserHandle.myUserId());
+        return isDeviceSecure(mContext.getUserId());
     }
 
     /**
diff --git a/android/app/LoadedApk.java b/android/app/LoadedApk.java
index 26f4980..fc7d9a5 100644
--- a/android/app/LoadedApk.java
+++ b/android/app/LoadedApk.java
@@ -28,14 +28,13 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.dex.ArtManager;
 import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -49,13 +48,15 @@
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.LogPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
+
 import com.android.internal.util.ArrayUtils;
+
 import dalvik.system.VMRuntime;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -218,7 +219,7 @@
     }
 
     private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
-        if (appInfo.appComponentFactory != null) {
+        if (appInfo.appComponentFactory != null && cl != null) {
             try {
                 return (AppComponentFactory) cl.loadClass(appInfo.appComponentFactory)
                         .newInstance();
@@ -612,6 +613,7 @@
             } else {
                 mClassLoader = ClassLoader.getSystemClassLoader();
             }
+            mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
 
             return;
         }
@@ -686,6 +688,7 @@
                         librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                         null /* classLoaderName */);
                 StrictMode.setThreadPolicy(oldPolicy);
+                mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
             }
 
             return;
@@ -713,6 +716,7 @@
                     mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                     libraryPermittedPath, mBaseClassLoader,
                     mApplicationInfo.classLoaderName);
+            mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
 
             StrictMode.setThreadPolicy(oldPolicy);
             // Setup the class loader paths for profiling.
@@ -749,13 +753,6 @@
         }
     }
 
-    // Keep in sync with installd (frameworks/native/cmds/installd/commands.cpp).
-    private static File getPrimaryProfileFile(String packageName) {
-        File profileDir = Environment.getDataProfilesDePackageDirectory(
-                UserHandle.myUserId(), packageName);
-        return new File(profileDir, "primary.prof");
-    }
-
     private void setupJitProfileSupport() {
         if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
             return;
@@ -783,10 +780,12 @@
             return;
         }
 
-        final File profileFile = getPrimaryProfileFile(mPackageName);
-
-        VMRuntime.registerAppInfo(profileFile.getPath(),
-                codePaths.toArray(new String[codePaths.size()]));
+        for (int i = codePaths.size() - 1; i >= 0; i--) {
+            String splitName = i == 0 ? null : mApplicationInfo.splitNames[i - 1];
+            String profileFile = ArtManager.getCurrentProfilePath(
+                    mPackageName, UserHandle.myUserId(), splitName);
+            VMRuntime.registerAppInfo(profileFile, new String[] {codePaths.get(i)});
+        }
 
         // Register the app data directory with the reporter. It will
         // help deciding whether or not a dex file is the primary apk or a
@@ -967,78 +966,14 @@
                 throw new AssertionError("null split not found");
             }
 
-            mResources = ResourcesManager.getInstance().getResources(
-                    null,
-                    mResDir,
-                    splitPaths,
-                    mOverlayDirs,
-                    mApplicationInfo.sharedLibraryFiles,
-                    Display.DEFAULT_DISPLAY,
-                    null,
-                    getCompatibilityInfo(),
+            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
+                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
+                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                     getClassLoader());
         }
         return mResources;
     }
 
-    public Resources getOrCreateResourcesForSplit(@NonNull String splitName,
-            @Nullable IBinder activityToken, int displayId) throws NameNotFoundException {
-        return ResourcesManager.getInstance().getResources(
-                activityToken,
-                mResDir,
-                getSplitPaths(splitName),
-                mOverlayDirs,
-                mApplicationInfo.sharedLibraryFiles,
-                displayId,
-                null,
-                getCompatibilityInfo(),
-                getSplitClassLoader(splitName));
-    }
-
-    /**
-     * Creates the top level resources for the given package. Will return an existing
-     * Resources if one has already been created.
-     */
-    public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) {
-        // Request for this app, short circuit
-        if (appInfo.uid == Process.myUid()) {
-            return getResources();
-        }
-
-        // Get resources for a different package
-        return ResourcesManager.getInstance().getResources(
-                null,
-                appInfo.publicSourceDir,
-                appInfo.splitPublicSourceDirs,
-                appInfo.resourceDirs,
-                appInfo.sharedLibraryFiles,
-                Display.DEFAULT_DISPLAY,
-                null,
-                getCompatibilityInfo(),
-                getClassLoader());
-    }
-
-    public Resources createResources(IBinder activityToken, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
-        final String[] splitResDirs;
-        final ClassLoader classLoader;
-        try {
-            splitResDirs = getSplitPaths(splitName);
-            classLoader = getSplitClassLoader(splitName);
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-        return ResourcesManager.getInstance().getResources(activityToken,
-                mResDir,
-                splitResDirs,
-                mOverlayDirs,
-                mApplicationInfo.sharedLibraryFiles,
-                displayId,
-                overrideConfig,
-                compatInfo,
-                classLoader);
-    }
-
     public Application makeApplication(boolean forceDefaultAppClass,
             Instrumentation instrumentation) {
         if (mApplication != null) {
diff --git a/android/app/LocalActivityManager.java b/android/app/LocalActivityManager.java
index 998ac5f..e297719 100644
--- a/android/app/LocalActivityManager.java
+++ b/android/app/LocalActivityManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.servertransaction.PendingTransactionActions;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
@@ -141,6 +143,21 @@
             }
             r.window = r.activity.getWindow();
             r.instanceState = null;
+
+            final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
+            final PendingTransactionActions pendingActions;
+
+            if (!r.activity.mFinished) {
+                // This matches pending actions set in ActivityThread#handleLaunchActivity
+                pendingActions = new PendingTransactionActions();
+                pendingActions.setOldState(clientRecord.state);
+                pendingActions.setRestoreInstanceState(true);
+                pendingActions.setCallOnPostCreate(true);
+            } else {
+                pendingActions = null;
+            }
+
+            mActivityThread.handleStartActivity(clientRecord, pendingActions);
             r.curState = STARTED;
             
             if (desiredState == RESUMED) {
@@ -207,8 +224,8 @@
     
     private void performPause(LocalActivityRecord r, boolean finishing) {
         final boolean needState = r.instanceState == null;
-        final Bundle instanceState = mActivityThread.performPauseActivity(
-                r, finishing, needState, "performPause", null /* pendingActions */);
+        final Bundle instanceState = mActivityThread.performPauseActivity(r, finishing,
+                "performPause", null /* pendingActions */);
         if (needState) {
             r.instanceState = instanceState;
         }
@@ -363,7 +380,7 @@
         }
         if (localLOGV) Log.v(TAG, r.id + ": destroying");
         mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
-                false /* getNonConfigInstance */);
+                false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
         r.activity = null;
         r.window = null;
         if (finish) {
@@ -628,7 +645,7 @@
             LocalActivityRecord r = mActivityArray.get(i);
             if (localLOGV) Log.v(TAG, r.id + ": destroying");
             mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
-                    false /* getNonConfigInstance */);
+                    false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
         }
         mActivities.clear();
         mActivityArray.clear();
diff --git a/android/app/Notification.java b/android/app/Notification.java
index d6fddfc..4326ee3 100644
--- a/android/app/Notification.java
+++ b/android/app/Notification.java
@@ -89,6 +89,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -200,6 +201,16 @@
      */
     private static final int MAX_REPLY_HISTORY = 5;
 
+
+    /**
+     * If the notification contained an unsent draft for a RemoteInput when the user clicked on it,
+     * we're adding the draft as a String extra to the {@link #contentIntent} using this key.
+     *
+     * <p>Apps may use this extra to prepopulate text fields in the app, where the user usually
+     * sends messages.</p>
+     */
+    public static final String EXTRA_REMOTE_INPUT_DRAFT = "android.remoteInputDraft";
+
     /**
      * A timestamp related to this notification, in milliseconds since the epoch.
      *
@@ -350,6 +361,23 @@
     @Deprecated
     public RemoteViews headsUpContentView;
 
+    private boolean mUsesStandardHeader;
+
+    private static final ArraySet<Integer> STANDARD_LAYOUTS = new ArraySet<>();
+    static {
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_base);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_base);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_picture);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_text);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_inbox);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_messaging);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_media);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_big_media);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_ambient_header);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_header);
+        STANDARD_LAYOUTS.add(R.layout.notification_template_material_ambient);
+    }
+
     /**
      * A large bitmap to be shown in the notification content area.
      *
@@ -483,7 +511,7 @@
      * </ul>
      * <p>
      * Since hardware varies, you are not guaranteed that any of the values
-     * you pass are honored exactly.  Use the system defaults (TODO) if possible
+     * you pass are honored exactly.  Use the system defaults if possible
      * because they will be set to values that work on any given hardware.
      * <p>
      * The alpha channel must be set for forward compatibility.
@@ -736,6 +764,11 @@
     public static final String CATEGORY_CALL = "call";
 
     /**
+     * Notification category: map turn-by-turn navigation.
+     */
+    public static final String CATEGORY_NAVIGATION = "navigation";
+
+    /**
      * Notification category: incoming direct message (SMS, instant message, etc.).
      */
     public static final String CATEGORY_MESSAGE = "msg";
@@ -808,6 +841,27 @@
     public static final String CATEGORY_REMINDER = "reminder";
 
     /**
+     * Notification category: extreme car emergencies.
+     * @hide
+     */
+    @SystemApi
+    public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
+
+    /**
+     * Notification category: car warnings.
+     * @hide
+     */
+    @SystemApi
+    public static final String CATEGORY_CAR_WARNING = "car_warning";
+
+    /**
+     * Notification category: general car system information.
+     * @hide
+     */
+    @SystemApi
+    public static final String CATEGORY_CAR_INFORMATION = "car_information";
+
+    /**
      * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
      * that best describes this Notification.  May be used by the system for ranking and filtering.
      */
@@ -1045,11 +1099,10 @@
     /**
      * {@link #extras} key: A
      * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
-     * in the background when the notification is selected. The URI must point to an image stream
-     * suitable for passing into
+     * in the background when the notification is selected. Used on television platforms.
+     * The URI must point to an image stream suitable for passing into
      * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
-     * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
-     * URI used for this purpose must require no permissions to read the image data.
+     * BitmapFactory.decodeStream}; all other content types will be ignored.
      */
     public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
 
@@ -1166,11 +1219,11 @@
     public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
 
     /**
-     * This is set on the notification shown by the activity manager about all apps
-     * running in the background.  It indicates that the notification should be shown
-     * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
-     * notification currently visible to the user.  This is a string array of all
-     * package names of the apps.
+     * This is set on the notifications shown by system_server about apps running foreground
+     * services. It indicates that the notification should be shown
+     * only if any of the given apps do not already have a properly tagged
+     * {@link #FLAG_FOREGROUND_SERVICE} notification currently visible to the user.
+     * This is a string array of all package names of the apps.
      * @hide
      */
     public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
@@ -1323,6 +1376,10 @@
          */
         public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9;
 
+        /**
+         * {@code SemanticAction}: Call a contact, group, etc.
+         */
+        public static final int SEMANTIC_ACTION_CALL = 10;
 
         private final Bundle mExtras;
         private Icon mIcon;
@@ -1812,6 +1869,7 @@
              * @param label the label to display while the action is being prepared to execute
              * @return this object for method chaining
              */
+            @Deprecated
             public WearableExtender setInProgressLabel(CharSequence label) {
                 mInProgressLabel = label;
                 return this;
@@ -1823,6 +1881,7 @@
              *
              * @return the label to display while the action is being prepared to execute
              */
+            @Deprecated
             public CharSequence getInProgressLabel() {
                 return mInProgressLabel;
             }
@@ -1834,6 +1893,7 @@
              * @param label the label to confirm the action should be executed
              * @return this object for method chaining
              */
+            @Deprecated
             public WearableExtender setConfirmLabel(CharSequence label) {
                 mConfirmLabel = label;
                 return this;
@@ -1845,6 +1905,7 @@
              *
              * @return the label to confirm the action should be executed
              */
+            @Deprecated
             public CharSequence getConfirmLabel() {
                 return mConfirmLabel;
             }
@@ -1856,6 +1917,7 @@
              * @param label the label to display to cancel the action
              * @return this object for method chaining
              */
+            @Deprecated
             public WearableExtender setCancelLabel(CharSequence label) {
                 mCancelLabel = label;
                 return this;
@@ -1867,6 +1929,7 @@
              *
              * @return the label to display to cancel the action
              */
+            @Deprecated
             public CharSequence getCancelLabel() {
                 return mCancelLabel;
             }
@@ -1937,7 +2000,8 @@
                 SEMANTIC_ACTION_MUTE,
                 SEMANTIC_ACTION_UNMUTE,
                 SEMANTIC_ACTION_THUMBS_UP,
-                SEMANTIC_ACTION_THUMBS_DOWN
+                SEMANTIC_ACTION_THUMBS_DOWN,
+                SEMANTIC_ACTION_CALL
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SemanticAction {}
@@ -2514,6 +2578,8 @@
         }
 
         parcel.writeInt(mGroupAlertBehavior);
+
+        // mUsesStandardHeader is not written because it should be recomputed in listeners
     }
 
     /**
@@ -2534,6 +2600,80 @@
     };
 
     /**
+     * @hide
+     */
+    public static boolean areActionsVisiblyDifferent(Notification first, Notification second) {
+        Notification.Action[] firstAs = first.actions;
+        Notification.Action[] secondAs = second.actions;
+        if (firstAs == null && secondAs != null || firstAs != null && secondAs == null) {
+            return true;
+        }
+        if (firstAs != null && secondAs != null) {
+            if (firstAs.length != secondAs.length) {
+                return true;
+            }
+            for (int i = 0; i < firstAs.length; i++) {
+                if (!Objects.equals(firstAs[i].title, secondAs[i].title)) {
+                    return true;
+                }
+                RemoteInput[] firstRs = firstAs[i].getRemoteInputs();
+                RemoteInput[] secondRs = secondAs[i].getRemoteInputs();
+                if (firstRs == null) {
+                    firstRs = new RemoteInput[0];
+                }
+                if (secondRs == null) {
+                    secondRs = new RemoteInput[0];
+                }
+                if (firstRs.length != secondRs.length) {
+                    return true;
+                }
+                for (int j = 0; j < firstRs.length; j++) {
+                    if (!Objects.equals(firstRs[j].getLabel(), secondRs[j].getLabel())) {
+                        return true;
+                    }
+                    CharSequence[] firstCs = firstRs[j].getChoices();
+                    CharSequence[] secondCs = secondRs[j].getChoices();
+                    if (firstCs == null) {
+                        firstCs = new CharSequence[0];
+                    }
+                    if (secondCs == null) {
+                        secondCs = new CharSequence[0];
+                    }
+                    if (firstCs.length != secondCs.length) {
+                        return true;
+                    }
+                    for (int k = 0; k < firstCs.length; k++) {
+                        if (!Objects.equals(firstCs[k], secondCs[k])) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean areStyledNotificationsVisiblyDifferent(Builder first, Builder second) {
+        if (first.getStyle() == null) {
+            return second.getStyle() != null;
+        }
+        if (second.getStyle() == null) {
+            return true;
+        }
+        return first.getStyle().areNotificationsVisiblyDifferent(second.getStyle());
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean areRemoteViewsChanged(Builder first, Builder second) {
+        return !first.usesStandardHeader() || !second.usesStandardHeader();
+    }
+
+    /**
      * Parcelling creates multiple copies of objects in {@code extras}. Fix them.
      * <p>
      * For backwards compatibility {@code extras} holds some references to "real" member data such
@@ -2978,7 +3118,6 @@
         private int mActionBarColor = COLOR_INVALID;
         private int mBackgroundColor = COLOR_INVALID;
         private int mForegroundColor = COLOR_INVALID;
-        private int mBackgroundColorHint = COLOR_INVALID;
         /**
          * A temporary location where actions are stored. If != null the view originally has action
          * but doesn't have any for this inflation.
@@ -3974,6 +4113,13 @@
         }
 
         /**
+         * Returns the style set by {@link #setStyle(Style)}.
+         */
+        public Style getStyle() {
+            return mStyle;
+        }
+
+        /**
          * Specify the value of {@link #visibility}.
          *
          * @return The same Builder.
@@ -4072,6 +4218,25 @@
             }
         }
 
+        /**
+         * @hide
+         */
+        public boolean usesStandardHeader() {
+            if (mN.mUsesStandardHeader) {
+                return true;
+            }
+            if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.N) {
+                if (mN.contentView == null && mN.bigContentView == null) {
+                    return true;
+                }
+            }
+            boolean contentViewUsesHeader = mN.contentView == null
+                    || STANDARD_LAYOUTS.contains(mN.contentView.getLayoutId());
+            boolean bigContentViewUsesHeader = mN.bigContentView == null
+                    || STANDARD_LAYOUTS.contains(mN.bigContentView.getLayoutId());
+            return contentViewUsesHeader && bigContentViewUsesHeader;
+        }
+
         private void resetStandardTemplate(RemoteViews contentView) {
             resetNotificationHeader(contentView);
             resetContentMargins(contentView);
@@ -4103,6 +4268,7 @@
             contentView.setViewVisibility(R.id.time, View.GONE);
             contentView.setImageViewIcon(R.id.profile_badge, null);
             contentView.setViewVisibility(R.id.profile_badge, View.GONE);
+            mN.mUsesStandardHeader = false;
         }
 
         private void resetContentMargins(RemoteViews contentView) {
@@ -4220,8 +4386,7 @@
                             backgroundColor);
                     mSecondaryTextColor = NotificationColorUtil.resolveSecondaryColor(mContext,
                             backgroundColor);
-                    if (backgroundColor != COLOR_DEFAULT
-                            && (mBackgroundColorHint != COLOR_INVALID || isColorized())) {
+                    if (backgroundColor != COLOR_DEFAULT && isColorized()) {
                         mPrimaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
                                 mPrimaryTextColor, backgroundColor, 4.5);
                         mSecondaryTextColor = NotificationColorUtil.findAlphaToMeetContrast(
@@ -4424,24 +4589,17 @@
                 bindProfileBadge(contentView);
             }
             bindExpandButton(contentView);
+            mN.mUsesStandardHeader = true;
         }
 
         private void bindExpandButton(RemoteViews contentView) {
-            int color = getPrimaryHighlightColor();
+            int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
             contentView.setDrawableTint(R.id.expand_button, false, color,
                     PorterDuff.Mode.SRC_ATOP);
             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
                     color);
         }
 
-        /**
-         * @return the color that is used as the first primary highlight color. This is applied
-         * in several places like the action buttons or the app name in the header.
-         */
-        private int getPrimaryHighlightColor() {
-            return isColorized() ? getPrimaryTextColor() : resolveContrastColor();
-        }
-
         private void bindHeaderChronometerAndTime(RemoteViews contentView) {
             if (showsTimeOrChronometer()) {
                 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
@@ -4538,7 +4696,7 @@
                 setTextViewColorPrimary(contentView, R.id.app_name_text);
             } else {
                 contentView.setTextColor(R.id.app_name_text,
-                        ambient ? resolveAmbientColor() : resolveContrastColor());
+                        ambient ? resolveAmbientColor() : getSecondaryTextColor());
             }
         }
 
@@ -4573,7 +4731,8 @@
             big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
             big.setTextViewText(R.id.notification_material_reply_text_3, null);
 
-            big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
+            big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
+                    R.dimen.notification_content_margin);
         }
 
         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
@@ -4594,23 +4753,19 @@
             if (N > 0) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
-                if (p.ambient) {
-                    big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
-                } else if (isColorized()) {
-                    big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
-                } else {
-                    big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
-                            R.color.notification_action_list));
-                }
-                big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
-                        R.dimen.notification_action_list_height);
+                big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 for (int i=0; i<N; i++) {
                     Action action = mActions.get(i);
-                    validRemoteInput |= hasValidRemoteInput(action);
+                    boolean actionHasValidInput = hasValidRemoteInput(action);
+                    validRemoteInput |= actionHasValidInput;
 
                     final RemoteViews button = generateActionButton(action, emphazisedMode,
                             i % 2 != 0, p.ambient);
+                    if (actionHasValidInput) {
+                        // Clear the drawable
+                        button.setInt(R.id.action0, "setBackgroundResource", 0);
+                    }
                     big.addView(R.id.actions, button);
                 }
             } else {
@@ -5069,7 +5224,14 @@
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
                 boolean ambient) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
-            int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
+            int color;
+            if (ambient) {
+                color = resolveAmbientColor();
+            } else if (isColorized()) {
+                color = getPrimaryTextColor();
+            } else {
+                color = resolveContrastColor();
+            }
             if (colorable) {
                 contentView.setDrawableTint(R.id.icon, false, color,
                         PorterDuff.Mode.SRC_ATOP);
@@ -5105,14 +5267,11 @@
             }
 
             int color;
-            int background = mBackgroundColorHint;
-            if (mBackgroundColorHint == COLOR_INVALID) {
-                background = mContext.getColor(
-                        com.android.internal.R.color.notification_material_background_color);
-            }
+            int background = mContext.getColor(
+                    com.android.internal.R.color.notification_material_background_color);
             if (mN.color == COLOR_DEFAULT) {
                 ensureColors();
-                color = mSecondaryTextColor;
+                color = NotificationColorUtil.resolveDefaultColor(mContext, background);
             } else {
                 color = NotificationColorUtil.resolveContrastColor(mContext, mN.color,
                         background, mInNightMode);
@@ -5207,6 +5366,7 @@
             if (mStyle != null) {
                 mStyle.reduceImageSizes(mContext);
                 mStyle.purgeResources();
+                mStyle.validate(mContext);
                 mStyle.buildStyled(mN);
             }
             mN.reduceImageSizes(mContext);
@@ -5351,8 +5511,7 @@
             if (isColorized()) {
                 return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
             } else {
-                return mBackgroundColorHint != COLOR_INVALID ? mBackgroundColorHint
-                        : COLOR_DEFAULT;
+                return COLOR_DEFAULT;
             }
         }
 
@@ -5389,18 +5548,6 @@
         }
 
         /**
-         * Sets the background color for this notification to be a different one then the default.
-         * This is mainly used to calculate contrast and won't necessarily be applied to the
-         * background.
-         *
-         * @hide
-         */
-        public void setBackgroundColorHint(int backgroundColor) {
-            mBackgroundColorHint = backgroundColor;
-        }
-
-
-        /**
          * Forces all styled remoteViews to be built from scratch and not use any cached
          * RemoteViews.
          * This is needed for legacy apps that are baking in their remoteviews into the
@@ -5411,6 +5558,24 @@
         public void setRebuildStyledRemoteViews(boolean rebuild) {
             mRebuildStyledRemoteViews = rebuild;
         }
+
+        /**
+         * Get the text that should be displayed in the statusBar when heads upped. This is
+         * usually just the app name, but may be different depending on the style.
+         *
+         * @param publicMode If true, return a text that is safe to display in public.
+         *
+         * @hide
+         */
+        public CharSequence getHeadsUpStatusBarText(boolean publicMode) {
+            if (mStyle != null && !publicMode) {
+                CharSequence text = mStyle.getHeadsUpStatusBarText();
+                if (!TextUtils.isEmpty(text)) {
+                    return text;
+                }
+            }
+            return loadHeaderAppName();
+        }
     }
 
     /**
@@ -5776,6 +5941,28 @@
          */
         public void reduceImageSizes(Context context) {
         }
+
+        /**
+         * Validate that this style was properly composed. This is called at build time.
+         * @hide
+         */
+        public void validate(Context context) {
+        }
+
+        /**
+         * @hide
+         */
+        public abstract boolean areNotificationsVisiblyDifferent(Style other);
+
+        /**
+         * @return the the text that should be displayed in the statusBar when heads-upped.
+         * If {@code null} is returned, the default implementation will be used.
+         *
+         * @hide
+         */
+        public CharSequence getHeadsUpStatusBarText() {
+            return null;
+        }
     }
 
     /**
@@ -5829,6 +6016,13 @@
         }
 
         /**
+         * @hide
+         */
+        public Bitmap getBigPicture() {
+            return mPicture;
+        }
+
+        /**
          * Provide the bitmap to be used as the payload for the BigPicture notification.
          */
         public BigPictureStyle bigPicture(Bitmap b) {
@@ -5968,6 +6162,18 @@
         public boolean hasSummaryInHeader() {
             return false;
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            BigPictureStyle otherS = (BigPictureStyle) other;
+            return !Objects.equals(getBigPicture(), otherS.getBigPicture());
+        }
     }
 
     /**
@@ -6031,6 +6237,13 @@
         /**
          * @hide
          */
+        public CharSequence getBigText() {
+            return mBigText;
+        }
+
+        /**
+         * @hide
+         */
         public void addExtras(Bundle extras) {
             super.addExtras(extras);
 
@@ -6100,6 +6313,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            BigTextStyle newS = (BigTextStyle) other;
+            return !Objects.equals(getBigText(), newS.getBigText());
+        }
+
         static void applyBigTextContentView(Builder builder,
                 RemoteViews contentView, CharSequence bigTextText) {
             contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
@@ -6166,16 +6391,44 @@
          * @param user Required - The person displayed for any messages that are sent by the
          * user. Any messages added with {@link #addMessage(Notification.MessagingStyle.Message)}
          * who don't have a Person associated with it will be displayed as if they were sent
-         * by this user. The user also needs to have a valid name associated with it.
+         * by this user. The user also needs to have a valid name associated with it, which will
+         * be enforced starting in Android P.
          */
         public MessagingStyle(@NonNull Person user) {
             mUser = user;
-            if (user == null || user.getName() == null) {
-                throw new RuntimeException("user must be valid and have a name");
+        }
+
+        /**
+         * Validate that this style was properly composed. This is called at build time.
+         * @hide
+         */
+        @Override
+        public void validate(Context context) {
+            super.validate(context);
+            if (context.getApplicationInfo().targetSdkVersion
+                    >= Build.VERSION_CODES.P && (mUser == null || mUser.getName() == null)) {
+                throw new RuntimeException("User must be valid and have a name.");
             }
         }
 
         /**
+         * @return the the text that should be displayed in the statusBar when heads upped.
+         * If {@code null} is returned, the default implementation will be used.
+         *
+         * @hide
+         */
+        @Override
+        public CharSequence getHeadsUpStatusBarText() {
+            CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
+                    ? super.mBigContentTitle
+                    : mConversationTitle;
+            if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
+                return conversationTitle;
+            }
+            return null;
+        }
+
+        /**
          * @return the user to be displayed for any replies sent by the user
          */
         public Person getUser() {
@@ -6425,12 +6678,64 @@
         public RemoteViews makeContentView(boolean increasedHeight) {
             mBuilder.mOriginalActions = mBuilder.mActions;
             mBuilder.mActions = new ArrayList<>();
-            RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
+            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
             mBuilder.mActions = mBuilder.mOriginalActions;
             mBuilder.mOriginalActions = null;
             return remoteViews;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            MessagingStyle newS = (MessagingStyle) other;
+            List<MessagingStyle.Message> oldMs = getMessages();
+            List<MessagingStyle.Message> newMs = newS.getMessages();
+
+            if (oldMs == null) {
+                oldMs = new ArrayList<>();
+            }
+            if (newMs == null) {
+                newMs = new ArrayList<>();
+            }
+
+            int n = oldMs.size();
+            if (n != newMs.size()) {
+                return true;
+            }
+            for (int i = 0; i < n; i++) {
+                MessagingStyle.Message oldM = oldMs.get(i);
+                MessagingStyle.Message newM = newMs.get(i);
+                if (!Objects.equals(oldM.getText(), newM.getText())) {
+                    return true;
+                }
+                if (!Objects.equals(oldM.getDataUri(), newM.getDataUri())) {
+                    return true;
+                }
+                CharSequence oldSender = oldM.getSenderPerson() == null ? oldM.getSender()
+                        : oldM.getSenderPerson().getName();
+                CharSequence newSender = newM.getSenderPerson() == null ? newM.getSender()
+                        : newM.getSenderPerson().getName();
+                if (!Objects.equals(oldSender, newSender)) {
+                    return true;
+                }
+
+                String oldKey = oldM.getSenderPerson() == null
+                        ? null : oldM.getSenderPerson().getKey();
+                String newKey = newM.getSenderPerson() == null
+                        ? null : newM.getSenderPerson().getKey();
+                if (!Objects.equals(oldKey, newKey)) {
+                    return true;
+                }
+                // Other fields (like timestamp) intentionally excluded
+            }
+            return false;
+        }
+
         private Message findLatestIncomingMessage() {
             return findLatestIncomingMessage(mMessages);
         }
@@ -6460,11 +6765,11 @@
          */
         @Override
         public RemoteViews makeBigContentView() {
-            return makeBigContentView(false /* showRightIcon */);
+            return makeMessagingView(false /* isCollapsed */);
         }
 
         @NonNull
-        private RemoteViews makeBigContentView(boolean showRightIcon) {
+        private RemoteViews makeMessagingView(boolean isCollapsed) {
             CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
                     ? super.mBigContentTitle
                     : mConversationTitle;
@@ -6475,21 +6780,24 @@
                 nameReplacement = conversationTitle;
                 conversationTitle = null;
             }
+            boolean hideLargeIcon = !isCollapsed || isOneToOne;
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
                     mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
-                            .hideLargeIcon(!showRightIcon || isOneToOne)
+                            .hideLargeIcon(hideLargeIcon)
                             .headerTextSecondary(conversationTitle)
-                            .alwaysShowReply(showRightIcon));
+                            .alwaysShowReply(isCollapsed));
             addExtras(mBuilder.mN.extras);
             // also update the end margin if there is an image
             int endMargin = R.dimen.notification_content_margin_end;
-            if (mBuilder.mN.hasLargeIcon() && showRightIcon) {
+            if (isCollapsed) {
                 endMargin = R.dimen.notification_content_plus_picture_margin_end;
             }
             contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                     mBuilder.resolveContrastColor());
+            contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
+                    isCollapsed);
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     mBuilder.mN.mLargeIcon);
             contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
@@ -6556,7 +6864,7 @@
          */
         @Override
         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
-            RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
+            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
             remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
             return remoteViews;
         }
@@ -6859,6 +7167,13 @@
         /**
          * @hide
          */
+        public ArrayList<CharSequence> getLines() {
+            return mTexts;
+        }
+
+        /**
+         * @hide
+         */
         public void addExtras(Bundle extras) {
             super.addExtras(extras);
 
@@ -6937,6 +7252,18 @@
             return contentView;
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            InboxStyle newS = (InboxStyle) other;
+            return !Objects.equals(getLines(), newS.getLines());
+        }
+
         private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first) {
             int endMargin = 0;
             if (first) {
@@ -7100,6 +7427,18 @@
             }
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // All fields to compare are on the Notification object
+            return false;
+        }
+
         private RemoteViews generateMediaActionButton(Action action, int color) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mBuilder.mContext.getApplicationInfo(),
@@ -7140,8 +7479,7 @@
                     }
 
                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
-                    final RemoteViews button = generateMediaActionButton(action,
-                            getPrimaryHighlightColor());
+                    final RemoteViews button = generateMediaActionButton(action, getActionColor());
                     view.addView(com.android.internal.R.id.media_actions, button);
                 }
             }
@@ -7155,8 +7493,9 @@
             return view;
         }
 
-        private int getPrimaryHighlightColor() {
-            return mBuilder.getPrimaryHighlightColor();
+        private int getActionColor() {
+            return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
+                    : mBuilder.resolveContrastColor();
         }
 
         private RemoteViews makeMediaBigContentView() {
@@ -7176,7 +7515,7 @@
                 big.removeAllViews(com.android.internal.R.id.media_actions);
                 for (int i = 0; i < actionCount; i++) {
                     final RemoteViews button = generateMediaActionButton(mBuilder.mActions.get(i),
-                            getPrimaryHighlightColor());
+                            getActionColor());
                     big.addView(com.android.internal.R.id.media_actions, button);
                 }
             }
@@ -7309,6 +7648,18 @@
             }
             remoteViews.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // Comparison done for all custom RemoteViews, independent of style
+            return false;
+        }
     }
 
     /**
@@ -7399,6 +7750,18 @@
             return makeBigContentViewWithCustomContent(customRemoteView);
         }
 
+        /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+            // Comparison done for all custom RemoteViews, independent of style
+            return false;
+        }
+
         private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
                 RemoteViews customContent) {
             if (customContent != null) {
@@ -7422,6 +7785,8 @@
         @Nullable private Icon mIcon;
         @Nullable private String mUri;
         @Nullable private String mKey;
+        private boolean mBot;
+        private boolean mImportant;
 
         protected Person(Parcel in) {
             mName = in.readCharSequence();
@@ -7430,6 +7795,8 @@
             }
             mUri = in.readString();
             mKey = in.readString();
+            mImportant = in.readBoolean();
+            mBot = in.readBoolean();
         }
 
         /**
@@ -7506,6 +7873,27 @@
             return this;
         }
 
+        /**
+         * Sets whether this is an important person. Use this method to denote users who frequently
+         * interact with the user of this device, when it is not possible to refer to the user
+         * by {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+         *
+         * @param isImportant {@code true} if this is an important person, {@code false} otherwise.
+         */
+        public Person setImportant(boolean isImportant) {
+            mImportant = isImportant;
+            return this;
+        }
+
+        /**
+         * Sets whether this person is a machine rather than a human.
+         *
+         * @param isBot {@code true}  if this person is a machine, {@code false} otherwise.
+         */
+        public Person setBot(boolean isBot) {
+            mBot = isBot;
+            return this;
+        }
 
         /**
          * @return the uri provided for this person or {@code null} if no Uri was provided
@@ -7540,6 +7928,20 @@
         }
 
         /**
+         * @return whether this Person is a machine.
+         */
+        public boolean isBot() {
+            return mBot;
+        }
+
+        /**
+         * @return whether this Person is important.
+         */
+        public boolean isImportant() {
+            return mImportant;
+        }
+
+        /**
          * @return the URI associated with this person, or "name:mName" otherwise
          *  @hide
          */
@@ -7569,6 +7971,8 @@
             }
             dest.writeString(mUri);
             dest.writeString(mKey);
+            dest.writeBoolean(mImportant);
+            dest.writeBoolean(mBot);
         }
 
         public static final Creator<Person> CREATOR = new Creator<Person>() {
@@ -8049,6 +8453,7 @@
         /**
          * Set an icon that goes with the content of this notification.
          */
+        @Deprecated
         public WearableExtender setContentIcon(int icon) {
             mContentIcon = icon;
             return this;
@@ -8057,6 +8462,7 @@
         /**
          * Get an icon that goes with the content of this notification.
          */
+        @Deprecated
         public int getContentIcon() {
             return mContentIcon;
         }
@@ -8067,6 +8473,7 @@
          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
          * @see #setContentIcon
          */
+        @Deprecated
         public WearableExtender setContentIconGravity(int contentIconGravity) {
             mContentIconGravity = contentIconGravity;
             return this;
@@ -8078,6 +8485,7 @@
          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
          * @see #getContentIcon
          */
+        @Deprecated
         public int getContentIconGravity() {
             return mContentIconGravity;
         }
@@ -8125,6 +8533,7 @@
          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
          * The default value is {@link android.view.Gravity#BOTTOM}.
          */
+        @Deprecated
         public WearableExtender setGravity(int gravity) {
             mGravity = gravity;
             return this;
@@ -8136,6 +8545,7 @@
          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
          * The default value is {@link android.view.Gravity#BOTTOM}.
          */
+        @Deprecated
         public int getGravity() {
             return mGravity;
         }
@@ -8149,6 +8559,7 @@
          * documentation for the preset in question. See also
          * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
          */
+        @Deprecated
         public WearableExtender setCustomSizePreset(int sizePreset) {
             mCustomSizePreset = sizePreset;
             return this;
@@ -8162,6 +8573,7 @@
          * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
          * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
          */
+        @Deprecated
         public int getCustomSizePreset() {
             return mCustomSizePreset;
         }
@@ -8173,6 +8585,7 @@
          * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
          * {@link #getCustomContentHeight}.
          */
+        @Deprecated
         public WearableExtender setCustomContentHeight(int height) {
             mCustomContentHeight = height;
             return this;
@@ -8184,6 +8597,7 @@
          * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
          * {@link #setCustomContentHeight}.
          */
+        @Deprecated
         public int getCustomContentHeight() {
             return mCustomContentHeight;
         }
@@ -8234,6 +8648,7 @@
          * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
          * @return this object for method chaining
          */
+        @Deprecated
         public WearableExtender setHintHideIcon(boolean hintHideIcon) {
             setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
             return this;
@@ -8244,6 +8659,7 @@
          * @return {@code true} if this icon should not be displayed, false otherwise.
          * The default value is {@code false} if this was never set.
          */
+        @Deprecated
         public boolean getHintHideIcon() {
             return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
         }
@@ -8253,6 +8669,7 @@
          * displayed, and other semantic content should be hidden. This hint is only applicable
          * to sub-pages added using {@link #addPage}.
          */
+        @Deprecated
         public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
             setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
             return this;
@@ -8263,6 +8680,7 @@
          * displayed, and other semantic content should be hidden. This hint is only applicable
          * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
          */
+        @Deprecated
         public boolean getHintShowBackgroundOnly() {
             return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
         }
@@ -8274,6 +8692,7 @@
          * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
          * @return this object for method chaining
          */
+        @Deprecated
         public WearableExtender setHintAvoidBackgroundClipping(
                 boolean hintAvoidBackgroundClipping) {
             setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
@@ -8287,6 +8706,7 @@
          * @return {@code true} if it's ok if the background is clipped on the screen, false
          * otherwise. The default value is {@code false} if this was never set.
          */
+        @Deprecated
         public boolean getHintAvoidBackgroundClipping() {
             return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
         }
@@ -8298,6 +8718,7 @@
          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
          * @return this object for method chaining
          */
+        @Deprecated
         public WearableExtender setHintScreenTimeout(int timeout) {
             mHintScreenTimeout = timeout;
             return this;
@@ -8309,6 +8730,7 @@
          * @return the duration in milliseconds if > 0, or either one of the sentinel values
          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
          */
+        @Deprecated
         public int getHintScreenTimeout() {
             return mHintScreenTimeout;
         }
@@ -8845,6 +9267,7 @@
         private static final String EXTRA_CONTENT_INTENT = "content_intent";
         private static final String EXTRA_DELETE_INTENT = "delete_intent";
         private static final String EXTRA_CHANNEL_ID = "channel_id";
+        private static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps";
 
         // Flags bitwise-ored to mFlags
         private static final int FLAG_AVAILABLE_ON_TV = 0x1;
@@ -8853,6 +9276,7 @@
         private String mChannelId;
         private PendingIntent mContentIntent;
         private PendingIntent mDeleteIntent;
+        private boolean mSuppressShowOverApps;
 
         /**
          * Create a {@link TvExtender} with default options.
@@ -8872,6 +9296,7 @@
             if (bundle != null) {
                 mFlags = bundle.getInt(EXTRA_FLAGS);
                 mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
+                mSuppressShowOverApps = bundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS);
                 mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
                 mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
             }
@@ -8888,6 +9313,7 @@
 
             bundle.putInt(EXTRA_FLAGS, mFlags);
             bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
+            bundle.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps);
             if (mContentIntent != null) {
                 bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
             }
@@ -8980,6 +9406,23 @@
         public PendingIntent getDeleteIntent() {
             return mDeleteIntent;
         }
+
+        /**
+         * Specifies whether this notification should suppress showing a message over top of apps
+         * outside of the launcher.
+         */
+        public TvExtender setSuppressShowOverApps(boolean suppress) {
+            mSuppressShowOverApps = suppress;
+            return this;
+        }
+
+        /**
+         * Returns true if this notification should not show messages over top of apps
+         * outside of the launcher.
+         */
+        public boolean getSuppressShowOverApps() {
+            return mSuppressShowOverApps;
+        }
     }
 
     /**
diff --git a/android/app/NotificationChannel.java b/android/app/NotificationChannel.java
index 30f2697..4a7cf62 100644
--- a/android/app/NotificationChannel.java
+++ b/android/app/NotificationChannel.java
@@ -463,7 +463,11 @@
 
     /**
      * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
-     * notifications posted to this channel.
+     * notifications posted to this channel. Note: This value might be >
+     * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will
+     * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked.
+     * See {@link NotificationChannelGroup#isBlocked()} and
+     * {@link NotificationManager#areNotificationsEnabled()}.
      */
     public int getImportance() {
         return mImportance;
diff --git a/android/app/NotificationChannelGroup.java b/android/app/NotificationChannelGroup.java
index 16166f7..0fa3c7f 100644
--- a/android/app/NotificationChannelGroup.java
+++ b/android/app/NotificationChannelGroup.java
@@ -145,7 +145,9 @@
 
     /**
      * Returns whether or not notifications posted to {@link NotificationChannel channels} belonging
-     * to this group are blocked.
+     * to this group are blocked. This value is independent of
+     * {@link NotificationManager#areNotificationsEnabled()} and
+     * {@link NotificationChannel#getImportance()}.
      */
     public boolean isBlocked() {
         return mBlocked;
diff --git a/android/app/NotificationManager.java b/android/app/NotificationManager.java
index 49c03ab..46d1264 100644
--- a/android/app/NotificationManager.java
+++ b/android/app/NotificationManager.java
@@ -24,6 +24,7 @@
 import android.app.Notification.Builder;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -98,7 +99,7 @@
      * This broadcast is only sent to the app whose block state has changed.
      *
      * Input: nothing
-     * Output: nothing
+     * Output: {@link #EXTRA_BLOCKED_STATE}
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_APP_BLOCK_STATE_CHANGED =
@@ -113,24 +114,31 @@
      * This broadcast is only sent to the app that owns the channel that has changed.
      *
      * Input: nothing
-     * Output: {@link #EXTRA_BLOCK_STATE_CHANGED_ID}
+     * Output: {@link #EXTRA_NOTIFICATION_CHANNEL_ID}
+     * Output: {@link #EXTRA_BLOCKED_STATE}
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED =
             "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
 
     /**
-     * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} or
-     * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED} containing the id of the
-     * object which has a new blocked state.
+     * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} containing the id of the
+     * {@link NotificationChannel} which has a new blocked state.
      *
-     * The value will be the {@link NotificationChannel#getId()} of the channel for
-     * {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} and
-     * the {@link NotificationChannelGroup#getId()} of the group for
-     * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED}.
+     * The value will be the {@link NotificationChannel#getId()} of the channel.
      */
-    public static final String EXTRA_BLOCK_STATE_CHANGED_ID =
-            "android.app.extra.BLOCK_STATE_CHANGED_ID";
+    public static final String EXTRA_NOTIFICATION_CHANNEL_ID =
+            "android.app.extra.NOTIFICATION_CHANNEL_ID";
+
+    /**
+     * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED} containing the id
+     * of the {@link NotificationChannelGroup} which has a new blocked state.
+     *
+     * The value will be the {@link NotificationChannelGroup#getId()} of the group.
+     */
+    public static final String EXTRA_NOTIFICATION_CHANNEL_GROUP_ID =
+            "android.app.extra.NOTIFICATION_CHANNEL_GROUP_ID";
+
 
     /**
      * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} or
@@ -142,7 +150,6 @@
      */
     public static final String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
 
-
     /**
      * Intent that is broadcast when a {@link NotificationChannelGroup} is
      * {@link NotificationChannelGroup#isBlocked() blocked} or unblocked.
@@ -150,7 +157,8 @@
      * This broadcast is only sent to the app that owns the channel group that has changed.
      *
      * Input: nothing
-     * Output: {@link #EXTRA_BLOCK_STATE_CHANGED_ID}
+     * Output: {@link #EXTRA_NOTIFICATION_CHANNEL_GROUP_ID}
+     * Output: {@link #EXTRA_BLOCKED_STATE}
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED =
@@ -343,6 +351,14 @@
      * the same tag and id has already been posted by your application and has not yet been
      * canceled, it will be replaced by the updated information.
      *
+     * All {@link android.service.notification.NotificationListenerService listener services} will
+     * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris}
+     * provided on this notification or the
+     * {@link NotificationChannel} this notification is posted to using
+     * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the
+     * notification is canceled, or you can revoke permissions with
+     * {@link Context#revokeUriPermission(Uri, int)}.
+     *
      * @param tag A string identifier for this notification.  May be {@code null}.
      * @param id An identifier for this notification.  The pair (tag, id) must be unique
      *        within your application.
@@ -351,7 +367,7 @@
      */
     public void notify(String tag, int id, Notification notification)
     {
-        notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
+        notifyAsUser(tag, id, notification, mContext.getUser());
     }
 
     /**
@@ -363,11 +379,13 @@
         String pkg = mContext.getPackageName();
         // Fix the notification as best we can.
         Notification.addFieldsFromContext(mContext, notification);
+
         if (notification.sound != null) {
             notification.sound = notification.sound.getCanonicalUri();
             if (StrictMode.vmFileUriExposureEnabled()) {
                 notification.sound.checkFileUriExposed("Notification.sound");
             }
+
         }
         fixLegacySmallIcon(notification, pkg);
         if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
@@ -378,6 +396,7 @@
         }
         if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
         notification.reduceImageSizes(mContext);
+
         ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         boolean isLowRam = am.isLowRamDevice();
         final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
@@ -412,7 +431,7 @@
      */
     public void cancel(String tag, int id)
     {
-        cancelAsUser(tag, id, new UserHandle(UserHandle.myUserId()));
+        cancelAsUser(tag, id, mContext.getUser());
     }
 
     /**
@@ -440,7 +459,7 @@
         String pkg = mContext.getPackageName();
         if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
         try {
-            service.cancelAllNotifications(pkg, UserHandle.myUserId());
+            service.cancelAllNotifications(pkg, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1012,12 +1031,18 @@
         public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4;
         /** Alarms are prioritized */
         public static final int PRIORITY_CATEGORY_ALARMS = 1 << 5;
-        /** Media, system, game (catch-all for non-never suppressible sounds) are prioritized */
-        public static final int PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER = 1 << 6;
+        /** Media, game, voice navigation are prioritized */
+        public static final int PRIORITY_CATEGORY_MEDIA = 1 << 6;
+        /**System (catch-all for non-never suppressible sounds) are prioritized */
+        public static final int PRIORITY_CATEGORY_SYSTEM = 1 << 7;
 
-        private static final int[] ALL_PRIORITY_CATEGORIES = {
+        /**
+         * @hide
+         */
+        public static final int[] ALL_PRIORITY_CATEGORIES = {
             PRIORITY_CATEGORY_ALARMS,
-            PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
+            PRIORITY_CATEGORY_MEDIA,
+            PRIORITY_CATEGORY_SYSTEM,
             PRIORITY_CATEGORY_REMINDERS,
             PRIORITY_CATEGORY_EVENTS,
             PRIORITY_CATEGORY_MESSAGES,
@@ -1047,20 +1072,77 @@
          * @hide
          */
         public static final int SUPPRESSED_EFFECTS_UNSET = -1;
+
         /**
          * Whether notifications suppressed by DND should not interrupt visually (e.g. with
          * notification lights or by turning the screen on) when the screen is off.
+         *
+         * @deprecated use {@link #SUPPRESSED_EFFECT_FULL_SCREEN_INTENT} and
+         * {@link #SUPPRESSED_EFFECT_AMBIENT} and {@link #SUPPRESSED_EFFECT_LIGHTS} individually.
          */
+        @Deprecated
         public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1 << 0;
         /**
          * Whether notifications suppressed by DND should not interrupt visually when the screen
          * is on (e.g. by peeking onto the screen).
+         *
+         * @deprecated use {@link #SUPPRESSED_EFFECT_PEEK}.
          */
+        @Deprecated
         public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 1;
 
+        /**
+         * Whether {@link Notification#fullScreenIntent full screen intents} from
+         * notifications intercepted by DND are blocked.
+         */
+        public static final int SUPPRESSED_EFFECT_FULL_SCREEN_INTENT = 1 << 2;
+
+        /**
+         * Whether {@link NotificationChannel#shouldShowLights() notification lights} from
+         * notifications intercepted by DND are blocked.
+         */
+        public static final int SUPPRESSED_EFFECT_LIGHTS = 1 << 3;
+
+        /**
+         * Whether notifications intercepted by DND are prevented from peeking.
+         */
+        public static final int SUPPRESSED_EFFECT_PEEK = 1 << 4;
+
+        /**
+         * Whether notifications intercepted by DND are prevented from appearing in the status bar,
+         * on devices that support status bars.
+         */
+        public static final int SUPPRESSED_EFFECT_STATUS_BAR = 1 << 5;
+
+        /**
+         * Whether {@link NotificationChannel#canShowBadge() badges} from
+         * notifications intercepted by DND are blocked on devices that support badging.
+         */
+        public static final int SUPPRESSED_EFFECT_BADGE = 1 << 6;
+
+        /**
+         * Whether notification intercepted by DND are prevented from appearing on ambient displays
+         * on devices that support ambient display.
+         */
+        public static final int SUPPRESSED_EFFECT_AMBIENT = 1 << 7;
+
+        /**
+         * Whether notification intercepted by DND are prevented from appearing in notification
+         * list views like the notification shade or lockscreen on devices that support those
+         * views.
+         */
+        public static final int SUPPRESSED_EFFECT_NOTIFICATION_LIST = 1 << 8;
+
         private static final int[] ALL_SUPPRESSED_EFFECTS = {
                 SUPPRESSED_EFFECT_SCREEN_OFF,
                 SUPPRESSED_EFFECT_SCREEN_ON,
+                SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
+                SUPPRESSED_EFFECT_LIGHTS,
+                SUPPRESSED_EFFECT_PEEK,
+                SUPPRESSED_EFFECT_STATUS_BAR,
+                SUPPRESSED_EFFECT_BADGE,
+                SUPPRESSED_EFFECT_AMBIENT,
+                SUPPRESSED_EFFECT_NOTIFICATION_LIST
         };
 
         /**
@@ -1072,6 +1154,12 @@
         /**
          * Constructs a policy for Do Not Disturb priority mode behavior.
          *
+         * <p>
+         *     Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+         *     change user-designated values to allow or disallow
+         *     {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and
+         *     {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd.
+         *
          * @param priorityCategories bitmask of categories of notifications that can bypass DND.
          * @param priorityCallSenders which callers can bypass DND.
          * @param priorityMessageSenders which message senders can bypass DND.
@@ -1084,6 +1172,26 @@
         /**
          * Constructs a policy for Do Not Disturb priority mode behavior.
          *
+         * <p>
+         *     Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+         *     change user-designated values to allow or disallow
+         *     {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and
+         *     {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd.
+         * <p>
+         *     Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can
+         *     only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
+         *     {@link #SUPPRESSED_EFFECT_SCREEN_OFF} bits of the suppressed visual effects field.
+         *     All other suppressed effects will be ignored and reconstituted from the screen on
+         *     and screen off values.
+         * <p>
+         *     Apps that target {@link Build.VERSION_CODES#P} or above can set any
+         *     suppressed visual effects. However, if any suppressed effects >
+         *     {@link #SUPPRESSED_EFFECT_SCREEN_ON} are set, {@link #SUPPRESSED_EFFECT_SCREEN_ON}
+         *     and {@link #SUPPRESSED_EFFECT_SCREEN_OFF} will be ignored and reconstituted from
+         *     the more specific suppressed visual effect bits. Apps should migrate to targeting
+         *     specific effects instead of the deprecated {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
+         *     {@link #SUPPRESSED_EFFECT_SCREEN_OFF} effects.
+         *
          * @param priorityCategories bitmask of categories of notifications that can bypass DND.
          * @param priorityCallSenders which callers can bypass DND.
          * @param priorityMessageSenders which message senders can bypass DND.
@@ -1165,6 +1273,30 @@
             }
         }
 
+        /**
+         * @hide
+         */
+        public static int getAllSuppressedVisualEffects() {
+            int effects = 0;
+            for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) {
+                effects |= ALL_SUPPRESSED_EFFECTS[i];
+            }
+            return effects;
+        }
+
+        /**
+         * @hide
+         */
+        public static boolean areAllVisualEffectsSuppressed(int effects) {
+            for (int i = 0; i < ALL_SUPPRESSED_EFFECTS.length; i++) {
+                final int effect = ALL_SUPPRESSED_EFFECTS[i];
+                if ((effects & effect) == 0) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
         public static String suppressedEffectsToString(int effects) {
             if (effects <= 0) return "";
             final StringBuilder sb = new StringBuilder();
@@ -1203,9 +1335,26 @@
 
         private static String effectToString(int effect) {
             switch (effect) {
-                case SUPPRESSED_EFFECT_SCREEN_OFF: return "SUPPRESSED_EFFECT_SCREEN_OFF";
-                case SUPPRESSED_EFFECT_SCREEN_ON: return "SUPPRESSED_EFFECT_SCREEN_ON";
-                case SUPPRESSED_EFFECTS_UNSET: return "SUPPRESSED_EFFECTS_UNSET";
+                case SUPPRESSED_EFFECT_FULL_SCREEN_INTENT:
+                    return "SUPPRESSED_EFFECT_FULL_SCREEN_INTENT";
+                case SUPPRESSED_EFFECT_LIGHTS:
+                    return "SUPPRESSED_EFFECT_LIGHTS";
+                case SUPPRESSED_EFFECT_PEEK:
+                    return "SUPPRESSED_EFFECT_PEEK";
+                case SUPPRESSED_EFFECT_STATUS_BAR:
+                    return "SUPPRESSED_EFFECT_STATUS_BAR";
+                case SUPPRESSED_EFFECT_BADGE:
+                    return "SUPPRESSED_EFFECT_BADGE";
+                case SUPPRESSED_EFFECT_AMBIENT:
+                    return "SUPPRESSED_EFFECT_AMBIENT";
+                case SUPPRESSED_EFFECT_NOTIFICATION_LIST:
+                    return "SUPPRESSED_EFFECT_NOTIFICATION_LIST";
+                case SUPPRESSED_EFFECT_SCREEN_OFF:
+                    return "SUPPRESSED_EFFECT_SCREEN_OFF";
+                case SUPPRESSED_EFFECT_SCREEN_ON:
+                    return "SUPPRESSED_EFFECT_SCREEN_ON";
+                case SUPPRESSED_EFFECTS_UNSET:
+                    return "SUPPRESSED_EFFECTS_UNSET";
                 default: return "UNKNOWN_" + effect;
             }
         }
@@ -1218,8 +1367,8 @@
                 case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS";
                 case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS";
                 case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS";
-                case PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER:
-                    return "PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER";
+                case PRIORITY_CATEGORY_MEDIA: return "PRIORITY_CATEGORY_MEDIA";
+                case PRIORITY_CATEGORY_SYSTEM: return "PRIORITY_CATEGORY_SYSTEM";
                 default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
             }
         }
@@ -1264,7 +1413,7 @@
         final String pkg = mContext.getPackageName();
         try {
             final ParceledListSlice<StatusBarNotification> parceledList
-                    = service.getAppActiveNotifications(pkg, UserHandle.myUserId());
+                    = service.getAppActiveNotifications(pkg, mContext.getUserId());
             final List<StatusBarNotification> list = parceledList.getList();
             return list.toArray(new StatusBarNotification[list.size()]);
         } catch (RemoteException e) {
diff --git a/android/app/PendingIntent.java b/android/app/PendingIntent.java
index d6429ae..315259b 100644
--- a/android/app/PendingIntent.java
+++ b/android/app/PendingIntent.java
@@ -345,7 +345,7 @@
                     ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
-                    flags, options, UserHandle.myUserId());
+                    flags, options, context.getUserId());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -486,7 +486,7 @@
                 ActivityManager.getService().getIntentSender(
                     ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                     null, null, requestCode, intents, resolvedTypes, flags, options,
-                    UserHandle.myUserId());
+                    context.getUserId());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -544,8 +544,7 @@
      */
     public static PendingIntent getBroadcast(Context context, int requestCode,
             Intent intent, @Flags int flags) {
-        return getBroadcastAsUser(context, requestCode, intent, flags,
-                new UserHandle(UserHandle.myUserId()));
+        return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser());
     }
 
     /**
@@ -644,7 +643,7 @@
                     serviceKind, packageName,
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
-                    flags, null, UserHandle.myUserId());
+                    flags, null, context.getUserId());
             return target != null ? new PendingIntent(target) : null;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/android/app/PendingIntentPerfTest.java b/android/app/PendingIntentPerfTest.java
new file mode 100644
index 0000000..f8fd51d
--- /dev/null
+++ b/android/app/PendingIntentPerfTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.StubActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// Due to b/71353150, you might get "java.lang.AssertionError: Binder ProxyMap has too many
+// entries", but it's flaky. Adding "Runtime.getRuntime().gc()" between each iteration solves
+// the problem, but it doesn't seem like it's currently needed.
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PendingIntentPerfTest {
+
+    private Context mContext;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private Intent mIntent;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mIntent = StubActivity.createLaunchIntent(mContext);
+    }
+
+    /**
+     * Benchmark time to create a PendingIntent.
+     */
+    @Test
+    public void create() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            state.resumeTiming();
+
+            final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+                    0);
+
+            state.pauseTiming();
+            pendingIntent.cancel();
+            state.resumeTiming();
+        }
+    }
+
+    /**
+     * Benchmark time to create a PendingIntent with FLAG_CANCEL_CURRENT, already having an active
+     * PendingIntent.
+     */
+    @Test
+    public void createWithCancelFlag() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
+                    mIntent, 0);
+            state.resumeTiming();
+
+            final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+
+            state.pauseTiming();
+            pendingIntent.cancel();
+            state.resumeTiming();
+        }
+    }
+
+    /**
+     * Benchmark time to create a PendingIntent with FLAG_UPDATE_CURRENT, already having an active
+     * PendingIntent.
+     */
+    @Test
+    public void createWithUpdateFlag() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
+                    mIntent, 0);
+            state.resumeTiming();
+
+            final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
+                    PendingIntent.FLAG_UPDATE_CURRENT);
+
+            state.pauseTiming();
+            previousPendingIntent.cancel();
+            pendingIntent.cancel();
+            state.resumeTiming();
+        }
+    }
+
+    /**
+     * Benchmark time to cancel a PendingIntent.
+     */
+    @Test
+    public void cancel() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
+                    mIntent, 0);
+            state.resumeTiming();
+
+            pendingIntent.cancel();
+        }
+    }
+}
+
diff --git a/android/app/ProcessMemoryState.java b/android/app/ProcessMemoryState.java
new file mode 100644
index 0000000..39db16d
--- /dev/null
+++ b/android/app/ProcessMemoryState.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The memory stats for a process.
+ * {@hide}
+ */
+public class ProcessMemoryState implements Parcelable {
+    public int uid;
+    public String processName;
+    public int oomScore;
+    public long pgfault;
+    public long pgmajfault;
+    public long rssInBytes;
+    public long cacheInBytes;
+    public long swapInBytes;
+
+    public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
+                              long pgmajfault, long rssInBytes, long cacheInBytes,
+                              long swapInBytes) {
+        this.uid = uid;
+        this.processName = processName;
+        this.oomScore = oomScore;
+        this.pgfault = pgfault;
+        this.pgmajfault = pgmajfault;
+        this.rssInBytes = rssInBytes;
+        this.cacheInBytes = cacheInBytes;
+        this.swapInBytes = swapInBytes;
+    }
+
+    private ProcessMemoryState(Parcel in) {
+        uid = in.readInt();
+        processName = in.readString();
+        oomScore = in.readInt();
+        pgfault = in.readLong();
+        pgmajfault = in.readLong();
+        rssInBytes = in.readLong();
+        cacheInBytes = in.readLong();
+        swapInBytes = in.readLong();
+    }
+
+    public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
+        @Override
+        public ProcessMemoryState createFromParcel(Parcel in) {
+            return new ProcessMemoryState(in);
+        }
+
+        @Override
+        public ProcessMemoryState[] newArray(int size) {
+            return new ProcessMemoryState[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        parcel.writeInt(uid);
+        parcel.writeString(processName);
+        parcel.writeInt(oomScore);
+        parcel.writeLong(pgfault);
+        parcel.writeLong(pgmajfault);
+        parcel.writeLong(rssInBytes);
+        parcel.writeLong(cacheInBytes);
+        parcel.writeLong(swapInBytes);
+    }
+}
diff --git a/android/app/ProfilerInfo.java b/android/app/ProfilerInfo.java
index 0ed1b08..6fbe9c6 100644
--- a/android/app/ProfilerInfo.java
+++ b/android/app/ProfilerInfo.java
@@ -87,6 +87,15 @@
     }
 
     /**
+     * Return a new ProfilerInfo instance, with fields populated from this object,
+     * and {@link agent} and {@link attachAgentDuringBind} as given.
+     */
+    public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+        return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+                this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
+    }
+
+    /**
      * Close profileFd, if it is open. The field will be null after a call to this function.
      */
     public void closeFd() {
diff --git a/android/app/RemoteAction.java b/android/app/RemoteAction.java
index e7fe407..47741c0 100644
--- a/android/app/RemoteAction.java
+++ b/android/app/RemoteAction.java
@@ -18,14 +18,9 @@
 
 import android.annotation.NonNull;
 import android.graphics.drawable.Icon;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.text.TextUtils;
-import android.util.Log;
 
 import java.io.PrintWriter;
 
@@ -42,6 +37,7 @@
     private final CharSequence mContentDescription;
     private final PendingIntent mActionIntent;
     private boolean mEnabled;
+    private boolean mShouldShowIcon;
 
     RemoteAction(Parcel in) {
         mIcon = Icon.CREATOR.createFromParcel(in);
@@ -49,6 +45,7 @@
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mActionIntent = PendingIntent.CREATOR.createFromParcel(in);
         mEnabled = in.readBoolean();
+        mShouldShowIcon = in.readBoolean();
     }
 
     public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
@@ -62,6 +59,7 @@
         mContentDescription = contentDescription;
         mActionIntent = intent;
         mEnabled = true;
+        mShouldShowIcon = true;
     }
 
     /**
@@ -79,6 +77,20 @@
     }
 
     /**
+     * Sets whether the icon should be shown.
+     */
+    public void setShouldShowIcon(boolean shouldShowIcon) {
+        mShouldShowIcon = shouldShowIcon;
+    }
+
+    /**
+     * Return whether the icon should be shown.
+     */
+    public boolean shouldShowIcon() {
+        return mShouldShowIcon;
+    }
+
+    /**
      * Return an icon representing the action.
      */
     public @NonNull Icon getIcon() {
@@ -125,6 +137,7 @@
         TextUtils.writeToParcel(mContentDescription, out, flags);
         mActionIntent.writeToParcel(out, flags);
         out.writeBoolean(mEnabled);
+        out.writeBoolean(mShouldShowIcon);
     }
 
     public void dump(String prefix, PrintWriter pw) {
@@ -134,6 +147,7 @@
         pw.print(" contentDescription=" + mContentDescription);
         pw.print(" icon=" + mIcon);
         pw.print(" action=" + mActionIntent.getIntent());
+        pw.print(" shouldShowIcon=" + mShouldShowIcon);
         pw.println();
     }
 
diff --git a/android/app/RemoteInput.java b/android/app/RemoteInput.java
index b7100e6..6feb38e 100644
--- a/android/app/RemoteInput.java
+++ b/android/app/RemoteInput.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
@@ -25,6 +26,8 @@
 import android.os.Parcelable;
 import android.util.ArraySet;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -74,9 +77,14 @@
     private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
             "android.remoteinput.dataTypeResultsData";
 
-    /** Extra added to a clip data intent object identifying the source of the results. */
+    /** Extra added to a clip data intent object identifying the {@link Source} of the results. */
     private static final String EXTRA_RESULTS_SOURCE = "android.remoteinput.resultsSource";
 
+    /** @hide */
+    @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_FREE_FORM_INPUT, SOURCE_CHOICE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Source {}
+
     /** The user manually entered the data. */
     public static final int SOURCE_FREE_FORM_INPUT = 0;
 
@@ -437,10 +445,9 @@
      *
      * @param intent The intent to add remote input source to. The {@link ClipData}
      *               field of the intent will be modified to contain the source.
-     *               field of the intent will be modified to contain the source.
      * @param source The source of the results.
      */
-    public static void setResultsSource(Intent intent, int source) {
+    public static void setResultsSource(Intent intent, @Source int source) {
         Intent clipDataIntent = getClipDataIntentFromIntent(intent);
         if (clipDataIntent == null) {
             clipDataIntent = new Intent();  // First time we've added a result.
@@ -460,6 +467,7 @@
      * @return The source of the results. If no source was set, {@link #SOURCE_FREE_FORM_INPUT} will
      * be returned.
      */
+    @Source
     public static int getResultsSource(Intent intent) {
         Intent clipDataIntent = getClipDataIntentFromIntent(intent);
         if (clipDataIntent == null) {
diff --git a/android/app/ResourcesManager.java b/android/app/ResourcesManager.java
index fb11272..fc5ea66 100644
--- a/android/app/ResourcesManager.java
+++ b/android/app/ResourcesManager.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.ActivityInfo;
+import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.CompatResources;
 import android.content.res.CompatibilityInfo;
@@ -34,6 +35,7 @@
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.LruCache;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.Display;
@@ -41,9 +43,13 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
 
+import java.io.IOException;
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.function.Predicate;
@@ -59,12 +65,7 @@
      * Predicate that returns true if a WeakReference is gc'ed.
      */
     private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate =
-            new Predicate<WeakReference<Resources>>() {
-                @Override
-                public boolean test(WeakReference<Resources> weakRef) {
-                    return weakRef == null || weakRef.get() == null;
-                }
-            };
+            weakRef -> weakRef == null || weakRef.get() == null;
 
     /**
      * The global compatibility settings.
@@ -89,6 +90,48 @@
      */
     private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
 
+    private static class ApkKey {
+        public final String path;
+        public final boolean sharedLib;
+        public final boolean overlay;
+
+        ApkKey(String path, boolean sharedLib, boolean overlay) {
+            this.path = path;
+            this.sharedLib = sharedLib;
+            this.overlay = overlay;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 1;
+            result = 31 * result + this.path.hashCode();
+            result = 31 * result + Boolean.hashCode(this.sharedLib);
+            result = 31 * result + Boolean.hashCode(this.overlay);
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ApkKey)) {
+                return false;
+            }
+            ApkKey other = (ApkKey) obj;
+            return this.path.equals(other.path) && this.sharedLib == other.sharedLib
+                    && this.overlay == other.overlay;
+        }
+    }
+
+    /**
+     * The ApkAssets we are caching and intend to hold strong references to.
+     */
+    private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(15);
+
+    /**
+     * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't
+     * in our LRU cache. Bonus resources :)
+     */
+    private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
+
     /**
      * Resources and base configuration override associated with an Activity.
      */
@@ -260,6 +303,43 @@
         }
     }
 
+    private static String overlayPathToIdmapPath(String path) {
+        return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
+    }
+
+    private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
+            throws IOException {
+        final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
+        ApkAssets apkAssets = mLoadedApkAssets.get(newKey);
+        if (apkAssets != null) {
+            return apkAssets;
+        }
+
+        // Optimistically check if this ApkAssets exists somewhere else.
+        final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey);
+        if (apkAssetsRef != null) {
+            apkAssets = apkAssetsRef.get();
+            if (apkAssets != null) {
+                mLoadedApkAssets.put(newKey, apkAssets);
+                return apkAssets;
+            } else {
+                // Clean up the reference.
+                mCachedApkAssets.remove(newKey);
+            }
+        }
+
+        // We must load this from disk.
+        if (overlay) {
+            apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path),
+                    false /*system*/);
+        } else {
+            apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib);
+        }
+        mLoadedApkAssets.put(newKey, apkAssets);
+        mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
+        return apkAssets;
+    }
+
     /**
      * Creates an AssetManager from the paths within the ResourcesKey.
      *
@@ -270,13 +350,16 @@
     */
     @VisibleForTesting
     protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
-        AssetManager assets = new AssetManager();
+        final AssetManager.Builder builder = new AssetManager.Builder();
 
         // resDir can be null if the 'android' package is creating a new Resources object.
         // This is fine, since each AssetManager automatically loads the 'android' package
         // already.
         if (key.mResDir != null) {
-            if (assets.addAssetPath(key.mResDir) == 0) {
+            try {
+                builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
+                        false /*overlay*/));
+            } catch (IOException e) {
                 Log.e(TAG, "failed to add asset path " + key.mResDir);
                 return null;
             }
@@ -284,7 +367,10 @@
 
         if (key.mSplitResDirs != null) {
             for (final String splitResDir : key.mSplitResDirs) {
-                if (assets.addAssetPath(splitResDir) == 0) {
+                try {
+                    builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/,
+                            false /*overlay*/));
+                } catch (IOException e) {
                     Log.e(TAG, "failed to add split asset path " + splitResDir);
                     return null;
                 }
@@ -293,7 +379,14 @@
 
         if (key.mOverlayDirs != null) {
             for (final String idmapPath : key.mOverlayDirs) {
-                assets.addOverlayPath(idmapPath);
+                try {
+                    builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/,
+                            true /*overlay*/));
+                } catch (IOException e) {
+                    Log.w(TAG, "failed to add overlay path " + idmapPath);
+
+                    // continue.
+                }
             }
         }
 
@@ -302,14 +395,73 @@
                 if (libDir.endsWith(".apk")) {
                     // Avoid opening files we know do not have resources,
                     // like code-only .jar files.
-                    if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
+                    try {
+                        builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
+                                false /*overlay*/));
+                    } catch (IOException e) {
                         Log.w(TAG, "Asset path '" + libDir +
                                 "' does not exist or contains no resources.");
+
+                        // continue.
                     }
                 }
             }
         }
-        return assets;
+
+        return builder.build();
+    }
+
+    private static <T> int countLiveReferences(Collection<WeakReference<T>> collection) {
+        int count = 0;
+        for (WeakReference<T> ref : collection) {
+            final T value = ref != null ? ref.get() : null;
+            if (value != null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * @hide
+     */
+    public void dump(String prefix, PrintWriter printWriter) {
+        synchronized (this) {
+            IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+            for (int i = 0; i < prefix.length() / 2; i++) {
+                pw.increaseIndent();
+            }
+
+            pw.println("ResourcesManager:");
+            pw.increaseIndent();
+            pw.print("cached apks: total=");
+            pw.print(mLoadedApkAssets.size());
+            pw.print(" created=");
+            pw.print(mLoadedApkAssets.createCount());
+            pw.print(" evicted=");
+            pw.print(mLoadedApkAssets.evictionCount());
+            pw.print(" hit=");
+            pw.print(mLoadedApkAssets.hitCount());
+            pw.print(" miss=");
+            pw.print(mLoadedApkAssets.missCount());
+            pw.print(" max=");
+            pw.print(mLoadedApkAssets.maxSize());
+            pw.println();
+
+            pw.print("total apks: ");
+            pw.println(countLiveReferences(mCachedApkAssets.values()));
+
+            pw.print("resources: ");
+
+            int references = countLiveReferences(mResourceReferences);
+            for (ActivityResources activityResources : mActivityResourceReferences.values()) {
+                references += countLiveReferences(activityResources.activityResources);
+            }
+            pw.println(references);
+
+            pw.print("resource impls: ");
+            pw.println(countLiveReferences(mResourceImpls.values()));
+        }
     }
 
     private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
@@ -630,28 +782,16 @@
 
                 // We will create the ResourcesImpl object outside of holding this lock.
             }
-        }
 
-        // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
-        ResourcesImpl resourcesImpl = createResourcesImpl(key);
-        if (resourcesImpl == null) {
-            return null;
-        }
-
-        synchronized (this) {
-            ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
-            if (existingResourcesImpl != null) {
-                if (DEBUG) {
-                    Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
-                            + " new impl=" + resourcesImpl);
-                }
-                resourcesImpl.getAssets().close();
-                resourcesImpl = existingResourcesImpl;
-            } else {
-                // Add this ResourcesImpl to the cache.
-                mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
+            // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
+            ResourcesImpl resourcesImpl = createResourcesImpl(key);
+            if (resourcesImpl == null) {
+                return null;
             }
 
+            // Add this ResourcesImpl to the cache.
+            mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
+
             final Resources resources;
             if (activityToken != null) {
                 resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
diff --git a/android/app/SearchManager.java b/android/app/SearchManager.java
index ea990ad..49faf40 100644
--- a/android/app/SearchManager.java
+++ b/android/app/SearchManager.java
@@ -33,7 +33,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -48,6 +47,9 @@
  * and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
  * {@link android.content.Intent Intent}.
  *
+ * <p>
+ * {@link Configuration#UI_MODE_TYPE_WATCH} does not support this system service.
+ *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about using the search dialog and adding search
@@ -749,6 +751,8 @@
      *
      * <p>This function can be safely called at any time (even if no search is active.)
      *
+     * <p>{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method.
+     *
      * @see #startSearch
      */
     public void stopSearch() {
@@ -799,6 +803,8 @@
     /**
      * Set or clear the callback that will be invoked whenever the search UI is dismissed.
      *
+     * <p>{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method.
+     *
      * @param listener The {@link OnDismissListener} to use, or null.
      */
     public void setOnDismissListener(final OnDismissListener listener) {
@@ -808,6 +814,8 @@
     /**
      * Set or clear the callback that will be invoked whenever the search UI is canceled.
      *
+     * <p>{@link Configuration#UI_MODE_TYPE_TELEVISION} does not support this method.
+     *
      * @param listener The {@link OnCancelListener} to use, or null.
      */
     public void setOnCancelListener(OnCancelListener listener) {
diff --git a/android/app/Service.java b/android/app/Service.java
index 256c479..ea0fd75 100644
--- a/android/app/Service.java
+++ b/android/app/Service.java
@@ -471,14 +471,6 @@
      * {@link #onStart} and returns either {@link #START_STICKY}
      * or {@link #START_STICKY_COMPATIBILITY}.
      * 
-     * <p>If you need your application to run on platform versions prior to API
-     * level 5, you can use the following model to handle the older {@link #onStart}
-     * callback in that case.  The <code>handleCommand</code> method is implemented by
-     * you as appropriate:
-     * 
-     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
-     *   start_compatibility}
-     *
      * <p class="caution">Note that the system calls this on your
      * service's main thread.  A service's main thread is the same
      * thread where UI operations take place for Activities running in the
@@ -687,6 +679,10 @@
      * {@link #startService(Intent)} first to tell the system it should keep the service running,
      * and then use this method to tell it to keep it running harder.</p>
      *
+     * <p>Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request
+     * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
+     * this API.</p>
+     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/android/app/StatsManager.java b/android/app/StatsManager.java
index 963fc77..4a6fa8c 100644
--- a/android/app/StatsManager.java
+++ b/android/app/StatsManager.java
@@ -16,6 +16,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.IBinder;
@@ -30,23 +31,36 @@
  * @hide
  */
 @SystemApi
-public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends.
+public final class StatsManager {
     IStatsManager mService;
     private static final String TAG = "StatsManager";
+    private static final boolean DEBUG = false;
 
-    /** Long extra of uid that added the relevant stats config. */
-    public static final String EXTRA_STATS_CONFIG_UID =
-            "android.app.extra.STATS_CONFIG_UID";
-    /** Long extra of the relevant stats config's configKey. */
-    public static final String EXTRA_STATS_CONFIG_KEY =
-            "android.app.extra.STATS_CONFIG_KEY";
-    /** Long extra of the relevant statsd_config.proto's Subscription.id. */
+    /**
+     * Long extra of uid that added the relevant stats config.
+     */
+    public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
+    /**
+     * Long extra of the relevant stats config's configKey.
+     */
+    public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
+    /**
+     * Long extra of the relevant statsd_config.proto's Subscription.id.
+     */
     public static final String EXTRA_STATS_SUBSCRIPTION_ID =
             "android.app.extra.STATS_SUBSCRIPTION_ID";
-    /** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */
+    /**
+     * Long extra of the relevant statsd_config.proto's Subscription.rule_id.
+     */
     public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID =
             "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
     /**
+     *   List<String> of the relevant statsd_config.proto's BroadcastSubscriberDetails.cookie.
+     *   Obtain using {@link android.content.Intent#getStringArrayListExtra(String)}.
+     */
+    public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES =
+            "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
+    /**
      * Extra of a {@link android.os.StatsDimensionsValue} representing sliced dimension value
      * information.
      */
@@ -54,6 +68,12 @@
             "android.app.extra.STATS_DIMENSIONS_VALUE";
 
     /**
+     * Broadcast Action: Statsd has started.
+     * Configurations and PendingIntents can now be sent to it.
+     */
+    public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
+
+    /**
      * Constructor for StatsManagerClient.
      *
      * @hide
@@ -68,22 +88,20 @@
      * @param configKey An arbitrary integer that allows clients to track the configuration.
      * @param config    Wire-encoded StatsDConfig proto that specifies metrics (and all
      *                  dependencies eg, conditions and matchers).
-     * @param pkg       The package name to receive the broadcast.
-     * @param cls       The name of the class that receives the broadcast.
      * @return true if successful
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
+    public boolean addConfiguration(long configKey, byte[] config) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when adding configuration");
+                    Slog.e(TAG, "Failed to find statsd when adding configuration");
                     return false;
                 }
-                return service.addConfiguration(configKey, config, pkg, cls);
+                return service.addConfiguration(configKey, config);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+                Slog.e(TAG, "Failed to connect to statsd when adding configuration");
                 return false;
             }
         }
@@ -101,12 +119,12 @@
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when removing configuration");
+                    Slog.e(TAG, "Failed to find statsd when removing configuration");
                     return false;
                 }
                 return service.removeConfiguration(configKey);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+                Slog.e(TAG, "Failed to connect to statsd when removing configuration");
                 return false;
             }
         }
@@ -115,43 +133,40 @@
     /**
      * Set the PendingIntent to be used when broadcasting subscriber information to the given
      * subscriberId within the given config.
-     *
      * <p>
      * Suppose that the calling uid has added a config with key configKey, and that in this config
      * it is specified that when a particular anomaly is detected, a broadcast should be sent to
      * a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
      * that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
      * when the anomaly is detected.
-     *
      * <p>
      * When statsd sends the broadcast, the PendingIntent will used to send an intent with
      * information of
-     *   {@link #EXTRA_STATS_CONFIG_UID},
-     *   {@link #EXTRA_STATS_CONFIG_KEY},
-     *   {@link #EXTRA_STATS_SUBSCRIPTION_ID},
-     *   {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
-     *   {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
-     *
+     * {@link #EXTRA_STATS_CONFIG_UID},
+     * {@link #EXTRA_STATS_CONFIG_KEY},
+     * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
+     * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID},
+     * {@link #EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES}, and
+     * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
      * <p>
      * This function can only be called by the owner (uid) of the config. It must be called each
      * time statsd starts. The config must have been added first (via addConfiguration()).
      *
-     * @param configKey The integer naming the config to which this subscriber is attached.
-     * @param subscriberId ID of the subscriber, as used in the config.
+     * @param configKey     The integer naming the config to which this subscriber is attached.
+     * @param subscriberId  ID of the subscriber, as used in the config.
      * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
      *                      associated with the given subscriberId. May be null, in which case
      *                      it undoes any previous setting of this subscriberId.
      * @return true if successful
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean setBroadcastSubscriber(long configKey,
-                                          long subscriberId,
-                                          PendingIntent pendingIntent) {
+    public boolean setBroadcastSubscriber(
+            long configKey, long subscriberId, PendingIntent pendingIntent) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    Slog.w(TAG, "Failed to find statsd when adding broadcast subscriber");
+                    Slog.e(TAG, "Failed to find statsd when adding broadcast subscriber");
                     return false;
                 }
                 if (pendingIntent != null) {
@@ -162,7 +177,45 @@
                     return service.unsetBroadcastSubscriber(configKey, subscriberId);
                 }
             } catch (RemoteException e) {
-                Slog.w(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+                Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Registers the operation that is called to retrieve the metrics data. This must be called
+     * each time statsd starts. The config must have been added first (via addConfiguration(),
+     * although addConfiguration could have been called on a previous boot). This operation allows
+     * statsd to send metrics data whenever statsd determines that the metrics in memory are
+     * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
+     * data, which also deletes the retrieved metrics from statsd's memory.
+     *
+     * @param configKey     The integer naming the config to which this operation is attached.
+     * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
+     *                      associated with the given subscriberId. May be null, in which case
+     *                      it removes any associated pending intent with this configKey.
+     * @return true if successful
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (service == null) {
+                    Slog.e(TAG, "Failed to find statsd when registering data listener.");
+                    return false;
+                }
+                if (pendingIntent == null) {
+                    return service.removeDataFetchOperation(configKey);
+                } else {
+                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+                    IBinder intentSender = pendingIntent.getTarget().asBinder();
+                    return service.setDataFetchOperation(configKey, intentSender);
+                }
+
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
                 return false;
             }
         }
@@ -173,20 +226,21 @@
      * the retrieved metrics from statsd memory.
      *
      * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
+     * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
+     * crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getData(long configKey) {
+    public @Nullable byte[] getData(long configKey) {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when getting data");
+                    Slog.e(TAG, "Failed to find statsd when getting data");
                     return null;
                 }
                 return service.getData(configKey);
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connecto statsd when getting data");
+                Slog.e(TAG, "Failed to connect to statsd when getting data");
                 return null;
             }
         }
@@ -197,20 +251,20 @@
      * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
      * This getter is not destructive and will not reset any metrics/counters.
      *
-     * @return Serialized StatsdStatsReport proto. Returns null on failure.
+     * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getMetadata() {
+    public @Nullable byte[] getMetadata() {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when getting metadata");
+                    Slog.e(TAG, "Failed to find statsd when getting metadata");
                     return null;
                 }
                 return service.getMetadata();
             } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connecto statsd when getting metadata");
+                Slog.e(TAG, "Failed to connect to statsd when getting metadata");
                 return null;
             }
         }
diff --git a/android/app/StatusBarManager.java b/android/app/StatusBarManager.java
index 85a9be3..b83b44d 100644
--- a/android/app/StatusBarManager.java
+++ b/android/app/StatusBarManager.java
@@ -74,11 +74,12 @@
     public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
     public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
     public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3;
+    public static final int DISABLE2_ROTATE_SUGGESTIONS = 1 << 4;
 
     public static final int DISABLE2_NONE = 0x00000000;
 
     public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
-            | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS;
+            | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS | DISABLE2_ROTATE_SUGGESTIONS;
 
     @IntDef(flag = true, prefix = { "DISABLE2_" }, value = {
             DISABLE2_NONE,
@@ -86,7 +87,8 @@
             DISABLE2_QUICK_SETTINGS,
             DISABLE2_SYSTEM_ICONS,
             DISABLE2_NOTIFICATION_SHADE,
-            DISABLE2_GLOBAL_ACTIONS
+            DISABLE2_GLOBAL_ACTIONS,
+            DISABLE2_ROTATE_SUGGESTIONS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Disable2Flags {}
diff --git a/android/app/SystemServiceRegistry.java b/android/app/SystemServiceRegistry.java
index 4310434..1776eac 100644
--- a/android/app/SystemServiceRegistry.java
+++ b/android/app/SystemServiceRegistry.java
@@ -89,7 +89,6 @@
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
-import android.net.wifi.IRttManager;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.IWifiScanner;
 import android.net.wifi.RttManager;
@@ -116,7 +115,6 @@
 import android.os.IUserManager;
 import android.os.IncidentManager;
 import android.os.PowerManager;
-import android.os.Process;
 import android.os.RecoverySystem;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
@@ -162,6 +160,7 @@
 import com.android.internal.policy.PhoneLayoutInflater;
 
 import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Manages all of the system services that can be returned by {@link Context#getSystemService}.
@@ -638,13 +637,13 @@
 
         registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
                 new CachedServiceFetcher<RttManager>() {
-            @Override
-            public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE);
-                IRttManager service = IRttManager.Stub.asInterface(b);
-                return new RttManager(ctx.getOuterContext(), service,
-                        ConnectivityThread.getInstanceLooper());
-            }});
+                @Override
+                public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                    IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
+                    IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+                    return new RttManager(ctx.getOuterContext(),
+                            new WifiRttManager(ctx.getOuterContext(), service));
+                }});
 
         registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
                 new CachedServiceFetcher<WifiRttManager>() {
@@ -724,8 +723,9 @@
                     service = IPrintManager.Stub.asInterface(ServiceManager
                             .getServiceOrThrow(Context.PRINT_SERVICE));
                 }
-                return new PrintManager(ctx.getOuterContext(), service, UserHandle.myUserId(),
-                        UserHandle.getAppId(Process.myUid()));
+                final int userId = ctx.getUserId();
+                final int appId = UserHandle.getAppId(ctx.getApplicationInfo().uid);
+                return new PrintManager(ctx.getOuterContext(), service, userId, appId);
             }});
 
         registerService(Context.COMPANION_DEVICE_SERVICE, CompanionDeviceManager.class,
@@ -780,12 +780,12 @@
             }});
 
         registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
-                new StaticServiceFetcher<TvInputManager>() {
+                new CachedServiceFetcher<TvInputManager>() {
             @Override
-            public TvInputManager createService() throws ServiceNotFoundException {
+            public TvInputManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TV_INPUT_SERVICE);
                 ITvInputManager service = ITvInputManager.Stub.asInterface(iBinder);
-                return new TvInputManager(service, UserHandle.myUserId());
+                return new TvInputManager(service, ctx.getUserId());
             }});
 
         registerService(Context.NETWORK_SCORE_SERVICE, NetworkScoreManager.class,
@@ -993,6 +993,10 @@
         return new Object[sServiceCacheSize];
     }
 
+    public static AtomicInteger[] createServiceInitializationStateArray() {
+        return new AtomicInteger[sServiceCacheSize];
+    }
+
     /**
      * Gets a system service from a given context.
      */
@@ -1041,19 +1045,95 @@
         @SuppressWarnings("unchecked")
         public final T getService(ContextImpl ctx) {
             final Object[] cache = ctx.mServiceCache;
+
+            // Fast path. If it's already cached, just return it.
+            Object service = cache[mCacheIndex];
+            if (service != null) {
+                return (T) service;
+            }
+
+            // Slow path.
+            final AtomicInteger[] gates = ctx.mServiceInitializationStateArray;
+            final AtomicInteger gate;
+
             synchronized (cache) {
-                // Fetch or create the service.
-                Object service = cache[mCacheIndex];
-                if (service == null) {
+                // See if it's cached or not again, with the lock held this time.
+                service = cache[mCacheIndex];
+                if (service != null) {
+                    return (T) service;
+                }
+
+                // Not initialized yet. Create an atomic boolean to control which thread should
+                // instantiate the service.
+                if (gates[mCacheIndex] != null) {
+                    gate = gates[mCacheIndex];
+                } else {
+                    gate = new AtomicInteger(ContextImpl.STATE_UNINITIALIZED);
+                    gates[mCacheIndex] = gate;
+                }
+            }
+
+            // Not cached yet.
+            //
+            // Note multiple threads can reach here for the same service on the same context
+            // concurrently.
+            //
+            // Now we're going to instantiate the service, but do so without the cache held;
+            // otherwise it could deadlock. (b/71882178)
+            //
+            // However we still don't want to instantiate the same service multiple times, so
+            // use the atomic integer to ensure only one thread will call createService().
+
+            if (gate.compareAndSet(
+                    ContextImpl.STATE_UNINITIALIZED, ContextImpl.STATE_INITIALIZING)) {
+                try {
+                    // This thread is the first one to get here. Instantiate the service
+                    // *without* the cache lock held.
                     try {
                         service = createService(ctx);
-                        cache[mCacheIndex] = service;
+
+                        synchronized (cache) {
+                            cache[mCacheIndex] = service;
+                        }
                     } catch (ServiceNotFoundException e) {
                         onServiceNotFound(e);
                     }
+                } finally {
+                    // Tell the all other threads that the cache is ready now.
+                    // (But it's still be null in case of ServiceNotFoundException.)
+                    synchronized (gate) {
+                        gate.set(ContextImpl.STATE_READY);
+                        gate.notifyAll();
+                    }
                 }
-                return (T)service;
+                return (T) service;
             }
+            // Other threads will wait on the gate lock.
+            synchronized (gate) {
+                boolean interrupted = false;
+
+                // Note: We check whether "state == STATE_READY", not
+                // "cache[mCacheIndex] != null", because "cache[mCacheIndex] == null"
+                // is still a valid outcome in the ServiceNotFoundException case.
+                while (gate.get() != ContextImpl.STATE_READY) {
+                    try {
+                        gate.wait();
+                    } catch (InterruptedException e) {
+                        Log.w(TAG,  "getService() interrupted");
+                        interrupted = true;
+                    }
+                }
+                if (interrupted) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+            // Now the first thread has initialized it.
+            // It may still be null if ServiceNotFoundException was thrown, but that shouldn't
+            // happen, so we'll just return null here in that case.
+            synchronized (cache) {
+                service = cache[mCacheIndex];
+            }
+            return (T) service;
         }
 
         public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
diff --git a/android/app/TaskStackBuilder.java b/android/app/TaskStackBuilder.java
index bab993f..b99b327 100644
--- a/android/app/TaskStackBuilder.java
+++ b/android/app/TaskStackBuilder.java
@@ -213,13 +213,13 @@
      * Start the task stack constructed by this builder.
      * @hide
      */
-    public void startActivities(Bundle options, UserHandle userHandle) {
+    public int startActivities(Bundle options, UserHandle userHandle) {
         if (mIntents.isEmpty()) {
             throw new IllegalStateException(
                     "No intents added to TaskStackBuilder; cannot startActivities");
         }
 
-        mSourceContext.startActivitiesAsUser(getIntents(), options, userHandle);
+        return mSourceContext.startActivitiesAsUser(getIntents(), options, userHandle);
     }
 
     /**
@@ -230,7 +230,7 @@
      * Context.startActivity(Intent, Bundle)} for more details.
      */
     public void startActivities(Bundle options) {
-        startActivities(options, new UserHandle(UserHandle.myUserId()));
+        startActivities(options, mSourceContext.getUser());
     }
 
     /**
diff --git a/android/app/UiAutomation.java b/android/app/UiAutomation.java
index ba39740..bd4933a 100644
--- a/android/app/UiAutomation.java
+++ b/android/app/UiAutomation.java
@@ -47,13 +47,10 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 
-import com.android.internal.util.CollectionUtils;
-
 import libcore.io.IoUtils;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
@@ -583,8 +580,6 @@
         // Execute the command *without* the lock being held.
         command.run();
 
-        List<AccessibilityEvent> eventsReceived = Collections.emptyList();
-
         // Acquire the lock and wait for the event.
         try {
             // Wait for the event.
@@ -605,14 +600,14 @@
                     if (filter.accept(event)) {
                         return event;
                     }
-                    eventsReceived = CollectionUtils.add(eventsReceived, event);
+                    event.recycle();
                 }
                 // Check if timed out and if not wait.
                 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
                 final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
                 if (remainingTimeMillis <= 0) {
                     throw new TimeoutException("Expected event not received within: "
-                            + timeoutMillis + " ms, among " + eventsReceived);
+                            + timeoutMillis + " ms.");
                 }
                 synchronized (mLock) {
                     if (mEventQueue.isEmpty()) {
@@ -625,10 +620,6 @@
                 }
             }
         } finally {
-            for (int i = 0; i < CollectionUtils.size(eventsReceived); i++) {
-                AccessibilityEvent event = eventsReceived.get(i);
-                event.recycle();
-            }
             synchronized (mLock) {
                 mWaitingForEventDelivery = false;
                 mEventQueue.clear();
@@ -885,15 +876,35 @@
     }
 
     /**
+     * Grants a runtime permission to a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to grant the permission.
+     */
+    public void grantRuntimePermission(String packageName, String permission) {
+        grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    public boolean grantRuntimePermission(String packageName, String permission,
+            UserHandle userHandle) {
+        grantRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
      * Grants a runtime permission to a package for a user.
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
-     * @return Whether granting succeeded.
-     *
-     * @hide
+     * @throws SecurityException if unable to grant the permission.
      */
-    @TestApi
-    public boolean grantRuntimePermission(String packageName, String permission,
+    public void grantRuntimePermissionAsUser(String packageName, String permission,
             UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
@@ -905,25 +916,42 @@
             // Calling out without a lock held.
             mUiAutomationConnection.grantRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error granting runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
-     * Revokes a runtime permission from a package for a user.
-     * @param packageName The package from which to revoke.
-     * @param permission The permission to revoke.
-     * @return Whether revoking succeeded.
-     *
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermission(String packageName, String permission) {
+        revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
      * @hide
      */
+    @Deprecated
     @TestApi
     public boolean revokeRuntimePermission(String packageName, String permission,
             UserHandle userHandle) {
+        revokeRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermissionAsUser(String packageName, String permission,
+            UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
@@ -934,12 +962,9 @@
             // Calling out without a lock held.
             mUiAutomationConnection.revokeRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error revoking runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
@@ -958,6 +983,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source = null;
         ParcelFileDescriptor sink = null;
@@ -1000,6 +1026,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source_read = null;
         ParcelFileDescriptor sink_read = null;
@@ -1065,6 +1092,16 @@
         }
     }
 
+    private void warnIfBetterCommand(String cmd) {
+        if (cmd.startsWith("pm grant ")) {
+            Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm grant'");
+        } else if (cmd.startsWith("pm revoke ")) {
+            Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm revoke'");
+        }
+    }
+
     private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
 
         public IAccessibilityServiceClientImpl(Looper looper) {
diff --git a/android/app/WallpaperColors.java b/android/app/WallpaperColors.java
index a2864b9..2d007ad 100644
--- a/android/app/WallpaperColors.java
+++ b/android/app/WallpaperColors.java
@@ -21,6 +21,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -107,6 +108,11 @@
      * @param drawable Source where to extract from.
      */
     public static WallpaperColors fromDrawable(Drawable drawable) {
+        if (drawable == null) {
+            throw new IllegalArgumentException("Drawable cannot be null");
+        }
+
+        Rect initialBounds = drawable.copyBounds();
         int width = drawable.getIntrinsicWidth();
         int height = drawable.getIntrinsicHeight();
 
@@ -126,6 +132,7 @@
         final WallpaperColors colors = WallpaperColors.fromBitmap(bitmap);
         bitmap.recycle();
 
+        drawable.setBounds(initialBounds);
         return colors;
     }
 
@@ -137,6 +144,13 @@
      * @param bitmap Source where to extract from.
      */
     public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap) {
+        return fromBitmap(bitmap, false /* computeHints */);
+    }
+
+    /**
+     * @hide
+     */
+    public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap, boolean computeHints) {
         if (bitmap == null) {
             throw new IllegalArgumentException("Bitmap can't be null");
         }
@@ -186,7 +200,7 @@
             }
         }
 
-        int hints = calculateDarkHints(bitmap);
+        int hints = computeHints ? calculateDarkHints(bitmap) : 0;
 
         if (shouldRecycle) {
             bitmap.recycle();
diff --git a/android/app/WallpaperManager.java b/android/app/WallpaperManager.java
index f21746c..465340f 100644
--- a/android/app/WallpaperManager.java
+++ b/android/app/WallpaperManager.java
@@ -51,15 +51,13 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadSystemException;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.service.wallpaper.WallpaperService;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -443,6 +441,9 @@
             synchronized (this) {
                 mCachedWallpaper = null;
                 mCachedWallpaperUserId = 0;
+                if (mDefaultWallpaper != null) {
+                    mDefaultWallpaper.recycle();
+                }
                 mDefaultWallpaper = null;
             }
         }
@@ -914,9 +915,14 @@
     /**
      * Get the primary colors of a wallpaper.
      *
-     * <p>You can expect null if:
-     * • Colors are still being processed by the system.
-     * • A live wallpaper doesn't implement {@link WallpaperService.Engine#onComputeColors()}.
+     * <p>This method can return {@code null} when:
+     * <ul>
+     * <li>Colors are still being processed by the system.</li>
+     * <li>The user has chosen to use a live wallpaper:  live wallpapers might not
+     * implement
+     * {@link android.service.wallpaper.WallpaperService.Engine#onComputeColors()
+     *     WallpaperService.Engine#onComputeColors()}.</li>
+     * </ul>
      *
      * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}.
@@ -928,7 +934,7 @@
     }
 
     /**
-     * Get the primary colors of a wallpaper
+     * Get the primary colors of the wallpaper configured in the given user.
      * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}
      * @param userId Owner of the wallpaper.
@@ -1002,7 +1008,7 @@
                 Log.w(TAG, "WallpaperService not running");
                 throw new RuntimeException(new DeadSystemException());
             } else {
-                return sGlobals.mService.getWallpaperInfo(UserHandle.myUserId());
+                return sGlobals.mService.getWallpaperInfo(mContext.getUserId());
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1150,7 +1156,7 @@
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
                     "res:" + resources.getResourceName(resid),
                     mContext.getOpPackageName(), null, false, result, which, completion,
-                    UserHandle.myUserId());
+                    mContext.getUserId());
             if (fd != null) {
                 FileOutputStream fos = null;
                 boolean ok = false;
@@ -1255,7 +1261,7 @@
             boolean allowBackup, @SetWallpaperFlags int which)
             throws IOException {
         return setBitmap(fullImage, visibleCropHint, allowBackup, which,
-                UserHandle.myUserId());
+                mContext.getUserId());
     }
 
     /**
@@ -1329,11 +1335,7 @@
 
     private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)
             throws IOException {
-        byte[] buffer = new byte[32768];
-        int amt;
-        while ((amt=data.read(buffer)) > 0) {
-            fos.write(buffer, 0, amt);
-        }
+        FileUtils.copy(data, fos);
     }
 
     /**
@@ -1406,7 +1408,7 @@
         try {
             ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
                     mContext.getOpPackageName(), visibleCropHint, allowBackup,
-                    result, which, completion, UserHandle.myUserId());
+                    result, which, completion, mContext.getUserId());
             if (fd != null) {
                 FileOutputStream fos = null;
                 try {
@@ -1562,11 +1564,13 @@
      * Specify extra padding that the wallpaper should have outside of the display.
      * That is, the given padding supplies additional pixels the wallpaper should extend
      * outside of the display itself.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}.
+     *
      * @param padding The number of pixels the wallpaper should extend beyond the display,
      * on its left, top, right, and bottom sides.
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS)
     public void setDisplayPadding(Rect padding) {
         try {
@@ -1603,11 +1607,11 @@
     }
 
     /**
-     * Clear the wallpaper.
+     * Reset all wallpaper to the factory default.
      *
-     * @hide
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER}.
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
     public void clearWallpaper() {
         clearWallpaper(FLAG_LOCK, mContext.getUserId());
@@ -1643,7 +1647,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
     public boolean setWallpaperComponent(ComponentName name) {
-        return setWallpaperComponent(name, UserHandle.myUserId());
+        return setWallpaperComponent(name, mContext.getUserId());
     }
 
     /**
diff --git a/android/app/WindowConfiguration.java b/android/app/WindowConfiguration.java
index 085fc79..46566e7 100644
--- a/android/app/WindowConfiguration.java
+++ b/android/app/WindowConfiguration.java
@@ -146,6 +146,9 @@
     })
     public @interface WindowConfig {}
 
+    /** @hide */
+    public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5;
+
     public WindowConfiguration() {
         unset();
     }
diff --git a/android/app/admin/DeviceAdminInfo.java b/android/app/admin/DeviceAdminInfo.java
index 1de1d2f..5fbe5b3 100644
--- a/android/app/admin/DeviceAdminInfo.java
+++ b/android/app/admin/DeviceAdminInfo.java
@@ -16,31 +16,31 @@
 
 package android.app.admin;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.content.res.Resources.NotFoundException;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.Xml;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -75,6 +75,10 @@
      *
      * <p>To control this policy, the device admin must have a "limit-password"
      * tag in the "uses-policies" section of its meta-data.
+     *
+     * <p>This policy is deprecated for use by a device admin.  In future releases, it will
+     * only be possible for a device owner or profile owner to enforce constraints on user
+     * passwords.
      */
     public static final int USES_POLICY_LIMIT_PASSWORD = 0;
 
@@ -136,6 +140,9 @@
      *
      * <p>To control this policy, the device admin must have an "expire-password"
      * tag in the "uses-policies" section of its meta-data.
+     *
+     * <p>This policy is deprecated for use by a device admin.  In future releases, it will
+     * only be possible for a device owner or profile owner to enforce password expiry.
      */
     public static final int USES_POLICY_EXPIRE_PASSWORD = 6;
 
@@ -152,6 +159,9 @@
      *
      * <p>To control this policy, the device admin must have a "disable-camera"
      * tag in the "uses-policies" section of its meta-data.
+     *
+     * <p>This policy is deprecated for use by a device admin.  In future releases, it will
+     * only be possible for a device owner or profile owner to disable use of the camera.
      */
     public static final int USES_POLICY_DISABLE_CAMERA = 8;
 
@@ -160,6 +170,10 @@
      *
      * <p>To control this policy, the device admin must have a "disable-keyguard-features"
      * tag in the "uses-policies" section of its meta-data.
+     *
+     * <p>This policy is deprecated for use by a device admin.  In future releases, it will
+     * only be possible for a device owner or profile owner to disable use of keyguard
+     * features.
      */
     public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
 
@@ -252,6 +266,12 @@
      */
     int mUsesPolicies;
 
+    /**
+     * Whether this administrator can be a target in an ownership transfer.
+     *
+     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
+     */
+    boolean mSupportsTransferOwnership;
 
     /**
      * Constructor.
@@ -333,6 +353,12 @@
                                     + getComponent() + ": " + policyName);
                         }
                     }
+                } else if (tagName.equals("support-transfer-ownership")) {
+                    if (parser.next() != XmlPullParser.END_TAG) {
+                        throw new XmlPullParserException(
+                                "support-transfer-ownership tag must be empty.");
+                    }
+                    mSupportsTransferOwnership = true;
                 }
             }
         } catch (NameNotFoundException e) {
@@ -346,6 +372,7 @@
     DeviceAdminInfo(Parcel source) {
         mActivityInfo = ActivityInfo.CREATOR.createFromParcel(source);
         mUsesPolicies = source.readInt();
+        mSupportsTransferOwnership = source.readBoolean();
     }
 
     /**
@@ -444,6 +471,13 @@
         return sRevKnownPolicies.get(policyIdent).tag;
     }
 
+    /**
+     * Return true if this administrator can be a target in an ownership transfer.
+     */
+    public boolean supportsTransferOwnership() {
+        return mSupportsTransferOwnership;
+    }
+
     /** @hide */
     public ArrayList<PolicyInfo> getUsedPolicies() {
         ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>();
@@ -488,6 +522,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         mActivityInfo.writeToParcel(dest, flags);
         dest.writeInt(mUsesPolicies);
+        dest.writeBoolean(mSupportsTransferOwnership);
     }
 
     /**
diff --git a/android/app/admin/DeviceAdminReceiver.java b/android/app/admin/DeviceAdminReceiver.java
index 28e845a..1c9477d 100644
--- a/android/app/admin/DeviceAdminReceiver.java
+++ b/android/app/admin/DeviceAdminReceiver.java
@@ -19,6 +19,8 @@
 import android.accounts.AccountManager;
 import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -34,9 +36,6 @@
 import android.os.UserHandle;
 import android.security.KeyChain;
 
-import libcore.util.NonNull;
-import libcore.util.Nullable;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -506,31 +505,6 @@
     public static final String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE =
             "android.app.extra.TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE";
 
-    /**
-     * Name under which a device administration component indicates whether it supports transfer of
-     * ownership. This meta-data is of type <code>boolean</code>. A value of <code>true</code>
-     * allows this administrator to be used as a target administrator for a transfer. If the value
-     * is <code>false</code>, ownership cannot be transferred to this administrator. The default
-     * value is <code>false</code>.
-     * <p>This metadata is used to avoid ownership transfer migration to an administrator with a
-     * version which does not yet support it.
-     * <p>Usage:
-     * <pre>
-     * &lt;receiver name="..." android:permission="android.permission.BIND_DEVICE_ADMIN"&gt;
-     *     &lt;meta-data
-     *         android:name="android.app.device_admin"
-     *         android:resource="@xml/..." /&gt;
-     *     &lt;meta-data
-     *         android:name="android.app.support_transfer_ownership"
-     *         android:value="true" /&gt;
-     * &lt;/receiver&gt;
-     * </pre>
-     *
-     * @see DevicePolicyManager#transferOwnership(ComponentName, ComponentName, PersistableBundle)
-     */
-    public static final String SUPPORT_TRANSFER_OWNERSHIP_META_DATA =
-            "android.app.support_transfer_ownership";
-
     private DevicePolicyManager mManager;
     private ComponentName mWho;
 
@@ -928,29 +902,29 @@
             int networkLogsCount) {
     }
 
-     /**
-      * Called when a user or profile is created.
-      *
-      * <p>This callback is only applicable to device owners.
-      *
-      * @param context The running context as per {@link #onReceive}.
-      * @param intent The received intent as per {@link #onReceive}.
-      * @param newUser The {@link UserHandle} of the user that has just been added.
-      */
-     public void onUserAdded(Context context, Intent intent, UserHandle newUser) {
-     }
+    /**
+     * Called when a user or profile is created.
+     *
+     * <p>This callback is only applicable to device owners.
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param newUser The {@link UserHandle} of the user that has just been added.
+     */
+    public void onUserAdded(Context context, Intent intent, @NonNull UserHandle newUser) {
+    }
 
-     /**
-      * Called when a user or profile is removed.
-      *
-      * <p>This callback is only applicable to device owners.
-      *
-      * @param context The running context as per {@link #onReceive}.
-      * @param intent The received intent as per {@link #onReceive}.
-      * @param removedUser The {@link UserHandle} of the user that has just been removed.
-      */
-     public void onUserRemoved(Context context, Intent intent, UserHandle removedUser) {
-     }
+    /**
+     * Called when a user or profile is removed.
+     *
+     * <p>This callback is only applicable to device owners.
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param removedUser The {@link UserHandle} of the user that has just been removed.
+     */
+    public void onUserRemoved(Context context, Intent intent, @NonNull UserHandle removedUser) {
+    }
 
     /**
      * Called when a user or profile is started.
@@ -961,7 +935,7 @@
      * @param intent The received intent as per {@link #onReceive}.
      * @param startedUser The {@link UserHandle} of the user that has just been started.
      */
-    public void onUserStarted(Context context, Intent intent, UserHandle startedUser) {
+    public void onUserStarted(Context context, Intent intent, @NonNull UserHandle startedUser) {
     }
 
     /**
@@ -973,7 +947,7 @@
      * @param intent The received intent as per {@link #onReceive}.
      * @param stoppedUser The {@link UserHandle} of the user that has just been stopped.
      */
-    public void onUserStopped(Context context, Intent intent, UserHandle stoppedUser) {
+    public void onUserStopped(Context context, Intent intent, @NonNull UserHandle stoppedUser) {
     }
 
     /**
@@ -985,7 +959,7 @@
      * @param intent The received intent as per {@link #onReceive}.
      * @param switchedUser The {@link UserHandle} of the user that has just been switched to.
      */
-    public void onUserSwitched(Context context, Intent intent, UserHandle switchedUser) {
+    public void onUserSwitched(Context context, Intent intent, @NonNull UserHandle switchedUser) {
     }
 
     /**
diff --git a/android/app/admin/DevicePolicyCache.java b/android/app/admin/DevicePolicyCache.java
new file mode 100644
index 0000000..fbb8ddf
--- /dev/null
+++ b/android/app/admin/DevicePolicyCache.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import android.annotation.UserIdInt;
+
+import com.android.server.LocalServices;
+
+/**
+ * Stores a copy of the set of device policies maintained by {@link DevicePolicyManager} that
+ * can be accessed from any place without risking dead locks.
+ *
+ * @hide
+ */
+public abstract class DevicePolicyCache {
+    protected DevicePolicyCache() {
+    }
+
+    /**
+     * @return the instance.
+     */
+    public static DevicePolicyCache getInstance() {
+        final DevicePolicyManagerInternal dpmi =
+                LocalServices.getService(DevicePolicyManagerInternal.class);
+        return (dpmi != null) ? dpmi.getDevicePolicyCache() : EmptyDevicePolicyCache.INSTANCE;
+    }
+
+    /**
+     * See {@link DevicePolicyManager#getScreenCaptureDisabled}
+     */
+    public abstract boolean getScreenCaptureDisabled(@UserIdInt int userHandle);
+
+    /**
+     * Empty implementation.
+     */
+    private static class EmptyDevicePolicyCache extends DevicePolicyCache {
+        private static final EmptyDevicePolicyCache INSTANCE = new EmptyDevicePolicyCache();
+
+        @Override
+        public boolean getScreenCaptureDisabled(int userHandle) {
+            return false;
+        }
+    }
+}
diff --git a/android/app/admin/DevicePolicyManager.java b/android/app/admin/DevicePolicyManager.java
index 8f76032..b64aae5 100644
--- a/android/app/admin/DevicePolicyManager.java
+++ b/android/app/admin/DevicePolicyManager.java
@@ -21,9 +21,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -54,9 +56,13 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManager.UserOperationException;
+import android.os.UserManager.UserOperationResult;
 import android.provider.ContactsContract.Directory;
+import android.provider.Settings;
 import android.security.AttestedKeyPair;
 import android.security.Credentials;
 import android.security.KeyChain;
@@ -116,7 +122,9 @@
  * guide. </div>
  */
 @SystemService(Context.DEVICE_POLICY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
 public class DevicePolicyManager {
+
     private static String TAG = "DevicePolicyManager";
 
     private final Context mContext;
@@ -140,7 +148,7 @@
     /** @hide test will override it. */
     @VisibleForTesting
     protected int myUserId() {
-        return UserHandle.myUserId();
+        return mContext.getUserId();
     }
 
     /**
@@ -1605,8 +1613,6 @@
      *     <li>keyguard
      * </ul>
      *
-     * This is the default configuration for LockTask.
-     *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
     public static final int LOCK_TASK_FEATURE_NONE = 0;
@@ -1624,7 +1630,10 @@
     /**
      * Enable notifications during LockTask mode. This includes notification icons on the status
      * bar, heads-up notifications, and the expandable notification shade. Note that the Quick
-     * Settings panel will still be disabled.
+     * Settings panel remains disabled. This feature flag can only be used in combination with
+     * {@link #LOCK_TASK_FEATURE_HOME}. {@link #setLockTaskFeatures(ComponentName, int)}
+     * throws an {@link IllegalArgumentException} if this feature flag is defined without
+     * {@link #LOCK_TASK_FEATURE_HOME}.
      *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
@@ -1642,17 +1651,24 @@
     public static final int LOCK_TASK_FEATURE_HOME = 1 << 2;
 
     /**
-     * Enable the Recents button and the Recents screen during LockTask mode.
+     * Enable the Overview button and the Overview screen during LockTask mode. This feature flag
+     * can only be used in combination with {@link #LOCK_TASK_FEATURE_HOME}, and
+     * {@link #setLockTaskFeatures(ComponentName, int)} will throw an
+     * {@link IllegalArgumentException} if this feature flag is defined without
+     * {@link #LOCK_TASK_FEATURE_HOME}.
      *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
-    public static final int LOCK_TASK_FEATURE_RECENTS = 1 << 3;
+    public static final int LOCK_TASK_FEATURE_OVERVIEW = 1 << 3;
 
     /**
      * Enable the global actions dialog during LockTask mode. This is the dialog that shows up when
      * the user long-presses the power button, for example. Note that the user may not be able to
      * power off the device if this flag is not set.
      *
+     * <p>This flag is enabled by default until {@link #setLockTaskFeatures(ComponentName, int)} is
+     * called for the first time.
+     *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
     public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 1 << 4;
@@ -1678,7 +1694,7 @@
             LOCK_TASK_FEATURE_SYSTEM_INFO,
             LOCK_TASK_FEATURE_NOTIFICATIONS,
             LOCK_TASK_FEATURE_HOME,
-            LOCK_TASK_FEATURE_RECENTS,
+            LOCK_TASK_FEATURE_OVERVIEW,
             LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
             LOCK_TASK_FEATURE_KEYGUARD
     })
@@ -1736,6 +1752,25 @@
     public static final int ID_TYPE_MEID = 8;
 
     /**
+     * Specifies that the calling app should be granted access to the installed credentials
+     * immediately. Otherwise, access to the credentials will be gated by user approval.
+     * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+     *
+     * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+     */
+    public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1;
+
+    /**
+     * Specifies that a user can select the key via the Certificate Selection prompt.
+     * If this flag is not set when calling {@link #installKeyPair}, the key can only be granted
+     * access by implementing {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+     * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+     *
+     * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+     */
+    public static final int INSTALLKEY_SET_USER_SELECTABLE = 2;
+
+    /**
      * Broadcast action: sent when the profile owner is set, changed or cleared.
      *
      * This broadcast is sent only to the user managed by the new profile owner.
@@ -2712,110 +2747,6 @@
     }
 
     /**
-     * The maximum number of characters allowed in the password blacklist.
-     */
-    private static final int PASSWORD_BLACKLIST_CHARACTER_LIMIT = 128 * 1000;
-
-    /**
-     * Throws an exception if the password blacklist is too large.
-     *
-     * @hide
-     */
-    public static void enforcePasswordBlacklistSize(List<String> blacklist) {
-        if (blacklist == null) {
-            return;
-        }
-        long characterCount = 0;
-        for (final String item : blacklist) {
-            characterCount += item.length();
-        }
-        if (characterCount > PASSWORD_BLACKLIST_CHARACTER_LIMIT) {
-            throw new IllegalArgumentException("128 thousand blacklist character limit exceeded by "
-                      + (characterCount - PASSWORD_BLACKLIST_CHARACTER_LIMIT) + " characters");
-        }
-    }
-
-    /**
-     * Called by an application that is administering the device to blacklist passwords.
-     * <p>
-     * Any blacklisted password or PIN is prevented from being enrolled by the user or the admin.
-     * Note that the match against the blacklist is case insensitive. The blacklist applies for all
-     * password qualities requested by {@link #setPasswordQuality} however it is not taken into
-     * consideration by {@link #isActivePasswordSufficient}.
-     * <p>
-     * The blacklist can be cleared by passing {@code null} or an empty list. The blacklist is
-     * given a name that is used to track which blacklist is currently set by calling {@link
-     * #getPasswordBlacklistName}. If the blacklist is being cleared, the name is ignored and {@link
-     * #getPasswordBlacklistName} will return {@code null}. The name can only be {@code null} when
-     * the blacklist is being cleared.
-     * <p>
-     * The blacklist is limited to a total of 128 thousand characters rather than limiting to a
-     * number of entries.
-     * <p>
-     * This method can be called on the {@link DevicePolicyManager} instance returned by
-     * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
-     * profile.
-     *
-     * @param admin the {@link DeviceAdminReceiver} this request is associated with
-     * @param name name to associate with the blacklist
-     * @param blacklist list of passwords to blacklist or {@code null} to clear the blacklist
-     * @return whether the new blacklist was successfully installed
-     * @throws SecurityException if {@code admin} is not a device or profile owner
-     * @throws IllegalArgumentException if the blacklist surpasses the character limit
-     * @throws NullPointerException if {@code name} is {@code null} when setting a non-empty list
-     *
-     * @see #getPasswordBlacklistName
-     * @see #isActivePasswordSufficient
-     * @see #resetPasswordWithToken
-     */
-    public boolean setPasswordBlacklist(@NonNull ComponentName admin, @Nullable String name,
-            @Nullable List<String> blacklist) {
-        enforcePasswordBlacklistSize(blacklist);
-
-        try {
-            return mService.setPasswordBlacklist(admin, name, blacklist, mParentInstance);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the name of the password blacklist set by the given admin.
-     *
-     * @param admin the {@link DeviceAdminReceiver} this request is associated with
-     * @return the name of the blacklist or {@code null} if no blacklist is set
-     *
-     * @see #setPasswordBlacklist
-     */
-    public @Nullable String getPasswordBlacklistName(@NonNull ComponentName admin) {
-        try {
-            return mService.getPasswordBlacklistName(admin, myUserId(), mParentInstance);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Test if a given password is blacklisted.
-     *
-     * @param userId the user to valiate for
-     * @param password the password to check against the blacklist
-     * @return whether the password is blacklisted
-     *
-     * @see #setPasswordBlacklist
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.TEST_BLACKLISTED_PASSWORD)
-    public boolean isPasswordBlacklisted(@UserIdInt int userId, @NonNull String password) {
-        try {
-            return mService.isPasswordBlacklisted(userId, password);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Determine whether the current password the user has set is sufficient to meet the policy
      * requirements (e.g. quality, minimum length) that have been requested by the admins of this
      * user and its participating profiles. Restrictions on profiles that have a separate challenge
@@ -2850,8 +2781,8 @@
      * When called by a profile owner of a managed profile returns true if the profile uses unified
      * challenge with its parent user.
      *
-     * <strong>Note: This method is not concerned with password quality and will return false if
-     * the profile has empty password as a separate challenge.
+     * <strong>Note</strong>: This method is not concerned with password quality and will return
+     * false if the profile has empty password as a separate challenge.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @throws SecurityException if {@code admin} is not a profile owner of a managed profile.
@@ -3444,9 +3375,6 @@
 
     /**
      * Flag for {@link #wipeData(int)}: also erase the device's eUICC data.
-     *
-     * TODO(b/35851809): make this public.
-     * @hide
      */
     public static final int WIPE_EUICC = 0x0004;
 
@@ -3490,18 +3418,18 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
      * @throws IllegalArgumentException if the input reason string is null or empty.
      */
-    public void wipeDataWithReason(int flags, @NonNull CharSequence reason) {
-        throwIfParentInstance("wipeDataWithReason");
+    public void wipeData(int flags, @NonNull CharSequence reason) {
+        throwIfParentInstance("wipeData");
         Preconditions.checkNotNull(reason, "CharSequence is null");
         wipeDataInternal(flags, reason.toString());
     }
 
     /**
      * Internal function for both {@link #wipeData(int)} and
-     * {@link #wipeDataWithReason(int, CharSequence)} to call.
+     * {@link #wipeData(int, CharSequence)} to call.
      *
      * @see #wipeData(int)
-     * @see #wipeDataWithReason(int, CharSequence)
+     * @see #wipeData(int, CharSequence)
      * @hide
      */
     private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
@@ -3704,7 +3632,9 @@
     public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
 
     /**
-     * Disable all keyguard widgets. Has no effect.
+     * Disable all keyguard widgets. Has no effect starting from
+     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} since keyguard widget is only supported
+     * on Android versions lower than 5.0.
      */
     public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0;
 
@@ -3724,13 +3654,15 @@
     public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 1 << 3;
 
     /**
-     * Ignore trust agent state on secure keyguard screens
-     * (e.g. PIN/Pattern/Password).
+     * Disable trust agents on secure keyguard screens (e.g. PIN/Pattern/Password).
+     * By setting this flag alone, all trust agents are disabled. If the admin then wants to
+     * whitelist specific features of some trust agent, {@link #setTrustAgentConfiguration} can be
+     * used in conjuction to set trust-agent-specific configurations.
      */
     public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
 
     /**
-     * Disable fingerprint sensor on keyguard secure screens (e.g. PIN/Pattern/Password).
+     * Disable fingerprint authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
      */
     public static final int KEYGUARD_DISABLE_FINGERPRINT = 1 << 5;
 
@@ -3740,6 +3672,25 @@
     public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 1 << 6;
 
     /**
+     * Disable face authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_FACE = 1 << 7;
+
+    /**
+     * Disable iris authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_IRIS = 1 << 8;
+
+    /**
+     * Disable all biometric authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_BIOMETRICS =
+            DevicePolicyManager.KEYGUARD_DISABLE_FACE
+            | DevicePolicyManager.KEYGUARD_DISABLE_IRIS
+            | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+
+
+    /**
      * Disable all current and future keyguard customizations.
      */
     public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
@@ -3757,7 +3708,7 @@
 
     /**
      * Called by an application that is administering the device to request that the storage system
-     * be encrypted.
+     * be encrypted. Does nothing if the caller is on a secondary user or a managed profile.
      * <p>
      * When multiple device administrators attempt to control device encryption, the most secure,
      * supported setting will always be used. If any device administrator requests device
@@ -3779,10 +3730,13 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param encrypt true to request encryption, false to release any previous request
-     * @return the new request status (for all active admins) - will be one of
-     *         {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE}, or
-     *         {@link #ENCRYPTION_STATUS_ACTIVE}. This is the value of the requests; Use
-     *         {@link #getStorageEncryptionStatus()} to query the actual device state.
+     * @return the new total request status (for all active admins), or {@link
+     *         DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} if called for a non-system user.
+     *         Will be one of {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link
+     *         #ENCRYPTION_STATUS_INACTIVE}, or {@link #ENCRYPTION_STATUS_ACTIVE}. This is the value
+     *         of the requests; use {@link #getStorageEncryptionStatus()} to query the actual device
+     *         state.
+     *
      * @throws SecurityException if {@code admin} is not an active administrator or does not use
      *             {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE}
      */
@@ -4088,7 +4042,11 @@
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
-        return installKeyPair(admin, privKey, certs, alias, requestAccess, true);
+        int flags = INSTALLKEY_SET_USER_SELECTABLE;
+        if (requestAccess) {
+            flags |= INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+        }
+        return installKeyPair(admin, privKey, certs, alias, flags);
     }
 
     /**
@@ -4112,13 +4070,9 @@
      *        {@link android.security.KeyChain#getCertificateChain}.
      * @param alias The private key alias under which to install the certificate. If a certificate
      *        with that alias already exists, it will be overwritten.
-     * @param requestAccess {@code true} to request that the calling app be granted access to the
-     *        credentials immediately. Otherwise, access to the credentials will be gated by user
-     *        approval.
-     * @param isUserSelectable {@code true} to indicate that a user can select this key via the
-     *        Certificate Selection prompt, false to indicate that this key can only be granted
-     *        access by implementing
-     *        {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+     * @param flags Flags to request that the calling app be granted access to the credentials
+     *        and set the key to be user-selectable. See {@link #INSTALLKEY_SET_USER_SELECTABLE} and
+     *        {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
      * @return {@code true} if the keys were installed, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
@@ -4127,9 +4081,12 @@
      * @see #DELEGATION_CERT_INSTALL
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
-            @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess,
-            boolean isUserSelectable) {
+            @NonNull Certificate[] certs, @NonNull String alias, int flags) {
         throwIfParentInstance("installKeyPair");
+        boolean requestAccess = (flags & INSTALLKEY_REQUEST_CREDENTIALS_ACCESS)
+                == INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+        boolean isUserSelectable = (flags & INSTALLKEY_SET_USER_SELECTABLE)
+                == INSTALLKEY_SET_USER_SELECTABLE;
         try {
             final byte[] pemCert = Credentials.convertToPem(certs[0]);
             byte[] pemChain = null;
@@ -4208,6 +4165,8 @@
      *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
      *         or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
      *         {@code keySpec} does not contain an attestation challenge.
+     * @throws UnsupportedOperationException if Device ID attestation was requested but the
+     *         underlying hardware does not support it.
      * @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
      */
     public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
@@ -5511,7 +5470,7 @@
     @SystemApi
     public @Nullable ComponentName getProfileOwner() throws IllegalArgumentException {
         throwIfParentInstance("getProfileOwner");
-        return getProfileOwnerAsUser(Process.myUserHandle().getIdentifier());
+        return getProfileOwnerAsUser(mContext.getUserId());
     }
 
     /**
@@ -5539,7 +5498,7 @@
     public @Nullable String getProfileOwnerName() throws IllegalArgumentException {
         if (mService != null) {
             try {
-                return mService.getProfileOwnerName(Process.myUserHandle().getIdentifier());
+                return mService.getProfileOwnerName(mContext.getUserId());
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -5569,10 +5528,13 @@
     }
 
     /**
-     * Called by a profile owner or device owner to add a default intent handler activity for
-     * intents that match a certain intent filter. This activity will remain the default intent
-     * handler even if the set of potential event handlers for the intent filter changes and if the
-     * intent preferences are reset.
+     * Called by a profile owner or device owner to set a default activity that the system selects
+     * to handle intents that match the given {@link IntentFilter}. This activity will remain the
+     * default intent handler even if the set of potential event handlers for the intent filter
+     * changes and if the intent preferences are reset.
+     * <p>
+     * Note that the caller should still declare the activity in the manifest, the API just sets
+     * the activity to be the default one to handle the given intent filter.
      * <p>
      * The default disambiguation mechanism takes over if the activity is not installed (anymore).
      * When the activity is (re)installed, it is automatically reset as default intent handler for
@@ -5622,6 +5584,29 @@
     }
 
     /**
+     * Called by a device owner to set the default SMS application.
+     * <p>
+     * The calling device admin must be a device owner. If it is not, a security exception will be
+     * thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The name of the package to set as the default SMS application.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     *
+     * @hide
+     */
+    public void setDefaultSmsApplication(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("setDefaultSmsApplication");
+        if (mService != null) {
+            try {
+                mService.setDefaultSmsApplication(admin, packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
      * Called by a profile owner or device owner to grant permission to a package to manage
      * application restrictions for the calling user via {@link #setApplicationRestrictions} and
      * {@link #getApplicationRestrictions}.
@@ -5760,11 +5745,20 @@
     }
 
     /**
-     * Sets a list of configuration features to enable for a TrustAgent component. This is meant to
+     * Sets a list of configuration features to enable for a trust agent component. This is meant to
      * be used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which disables all trust
      * agents but those enabled by this function call. If flag
      * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is not set, then this call has no effect.
      * <p>
+     * For any specific trust agent, whether it is disabled or not depends on the aggregated state
+     * of each admin's {@link #KEYGUARD_DISABLE_TRUST_AGENTS} setting and its trust agent
+     * configuration as set by this function call. In particular: if any admin sets
+     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} and does not additionally set any
+     * trust agent configuration, the trust agent is disabled completely. Otherwise, the trust agent
+     * will receive the list of configurations from all admins who set
+     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} and aggregate the configurations to determine its
+     * behavior. The exact meaning of aggregation is trust-agent-specific.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call this method;
      * if not, a security exception will be thrown.
@@ -5774,17 +5768,10 @@
      * the parent profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param target Component name of the agent to be enabled.
-     * @param configuration TrustAgent-specific feature bundle. If null for any admin, agent will be
-     *            strictly disabled according to the state of the
-     *            {@link #KEYGUARD_DISABLE_TRUST_AGENTS} flag.
-     *            <p>
-     *            If {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is set and options is not null for all
-     *            admins, then it's up to the TrustAgent itself to aggregate the values from all
-     *            device admins.
-     *            <p>
-     *            Consult documentation for the specific TrustAgent to determine legal options
-     *            parameters.
+     * @param target Component name of the agent to be configured.
+     * @param configuration Trust-agent-specific feature configuration bundle. Please consult
+     *        documentation of the specific trust agent to determine the interpretation of this
+     *        bundle.
      * @throws SecurityException if {@code admin} is not an active administrator or does not use
      *             {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES}
      */
@@ -5845,7 +5832,7 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled If true caller-Id information in the managed profile is not displayed.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) {
         throwIfParentInstance("setCrossProfileCallerIdDisabled");
@@ -5866,7 +5853,7 @@
      * thrown.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) {
         throwIfParentInstance("getCrossProfileCallerIdDisabled");
@@ -5906,7 +5893,7 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled If true contacts search in the managed profile is not displayed.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
             boolean disabled) {
@@ -5928,7 +5915,7 @@
      * thrown.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
         throwIfParentInstance("getCrossProfileContactsSearchDisabled");
@@ -5999,7 +5986,7 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled If true, bluetooth devices cannot access enterprise contacts.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setBluetoothContactSharingDisabled(@NonNull ComponentName admin, boolean disabled) {
         throwIfParentInstance("setBluetoothContactSharingDisabled");
@@ -6022,7 +6009,7 @@
      * This API works on managed profile only.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public boolean getBluetoothContactSharingDisabled(@NonNull ComponentName admin) {
         throwIfParentInstance("getBluetoothContactSharingDisabled");
@@ -6092,7 +6079,7 @@
      * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void clearCrossProfileIntentFilters(@NonNull ComponentName admin) {
         throwIfParentInstance("clearCrossProfileIntentFilters");
@@ -6106,21 +6093,22 @@
     }
 
     /**
-     * Called by a profile or device owner to set the permitted accessibility services. When set by
+     * Called by a profile or device owner to set the permitted
+     * {@link android.accessibilityservice.AccessibilityService}. When set by
      * a device owner or profile owner the restriction applies to all profiles of the user the
-     * device owner or profile owner is an admin for. By default the user can use any accessiblity
-     * service. When zero or more packages have been added, accessiblity services that are not in
+     * device owner or profile owner is an admin for. By default, the user can use any accessibility
+     * service. When zero or more packages have been added, accessibility services that are not in
      * the list and not part of the system can not be enabled by the user.
      * <p>
      * Calling with a null value for the list disables the restriction so that all services can be
-     * used, calling with an empty list only allows the builtin system's services.
+     * used, calling with an empty list only allows the built-in system services. Any non-system
+     * accessibility service that's currently enabled must be included in the list.
      * <p>
      * System accessibility services are always available to the user the list can't modify this.
-     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageNames List of accessibility service package names.
-     * @return true if setting the restriction succeeded. It fail if there is one or more non-system
-     *         accessibility services enabled, that are not in the list.
+     * @return {@code true} if the operation succeeded, or {@code false} if the list didn't
+     *         contain every enabled non-system accessibility service.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin,
@@ -6209,10 +6197,11 @@
     /**
      * Called by a profile or device owner to set the permitted input methods services. When set by
      * a device owner or profile owner the restriction applies to all profiles of the user the
-     * device owner or profile owner is an admin for. By default the user can use any input method.
+     * device owner or profile owner is an admin for. By default, the user can use any input method.
      * When zero or more packages have been added, input method that are not in the list and not
      * part of the system can not be enabled by the user. This method will fail if it is called for
-     * a admin that is not for the foreground user or a profile of the foreground user.
+     * a admin that is not for the foreground user or a profile of the foreground user. Any
+     * non-system input method service that's currently enabled must be included in the list.
      * <p>
      * Calling with a null value for the list disables the restriction so that all input methods can
      * be used, calling with an empty list disables all but the system's own input methods.
@@ -6221,8 +6210,8 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageNames List of input method package names.
-     * @return true if setting the restriction succeeded. It will fail if there are one or more
-     *         non-system input methods currently enabled that are not in the packageNames list.
+     * @return {@code true} if the operation succeeded, or {@code false} if the list didn't
+     *        contain every enabled non-system input method service.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean setPermittedInputMethods(
@@ -6295,6 +6284,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @Nullable List<String> getPermittedInputMethodsForCurrentUser() {
         throwIfParentInstance("getPermittedInputMethodsForCurrentUser");
         if (mService != null) {
@@ -6544,6 +6534,9 @@
      * <p>
      * If the adminExtras are not null, they will be stored on the device until the user is started
      * for the first time. Then the extras will be passed to the admin when onEnable is called.
+     * <p>From {@link android.os.Build.VERSION_CODES#P} onwards, if targeting
+     * {@link android.os.Build.VERSION_CODES#P}, throws {@link UserOperationException} instead of
+     * returning {@code null} on failure.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param name The user's name.
@@ -6558,6 +6551,9 @@
      * @return the {@link android.os.UserHandle} object for the created user, or {@code null} if the
      *         user could not be created.
      * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws UserOperationException if the user could not be created and the calling app is
+     * targeting {@link android.os.Build.VERSION_CODES#P} and running on
+     * {@link android.os.Build.VERSION_CODES#P}.
      */
     public @Nullable UserHandle createAndManageUser(@NonNull ComponentName admin,
             @NonNull String name,
@@ -6566,6 +6562,8 @@
         throwIfParentInstance("createAndManageUser");
         try {
             return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
+        } catch (ServiceSpecificException e) {
+            throw new UserOperationException(e.getMessage(), e.errorCode);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -6612,12 +6610,16 @@
      * Called by a device owner to start the specified secondary user in background.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param userHandle the user to be stopped.
-     * @return {@code true} if the user can be started, {@code false} otherwise.
+     * @param userHandle the user to be started in background.
+     * @return one of the following result codes:
+     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+     * {@link UserManager#USER_OPERATION_SUCCESS},
+     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link UserManager#USER_OPERATION_ERROR_MAX_RUNNING_USERS},
      * @throws SecurityException if {@code admin} is not a device owner.
      * @see #getSecondaryUsers(ComponentName)
      */
-    public boolean startUserInBackground(
+    public @UserOperationResult int startUserInBackground(
             @NonNull ComponentName admin, @NonNull UserHandle userHandle) {
         throwIfParentInstance("startUserInBackground");
         try {
@@ -6632,11 +6634,16 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle the user to be stopped.
-     * @return {@code true} if the user can be stopped, {@code false} otherwise.
+     * @return one of the following result codes:
+     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+     * {@link UserManager#USER_OPERATION_SUCCESS},
+     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
      * @throws SecurityException if {@code admin} is not a device owner.
      * @see #getSecondaryUsers(ComponentName)
      */
-    public boolean stopUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
+    public @UserOperationResult int stopUser(
+            @NonNull ComponentName admin, @NonNull UserHandle userHandle) {
         throwIfParentInstance("stopUser");
         try {
             return mService.stopUser(admin, userHandle);
@@ -6650,11 +6657,15 @@
      * calling user and switch back to primary.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @return {@code true} if the exit was successful, {@code false} otherwise.
+     * @return one of the following result codes:
+     * {@link UserManager#USER_OPERATION_ERROR_UNKNOWN},
+     * {@link UserManager#USER_OPERATION_SUCCESS},
+     * {@link UserManager#USER_OPERATION_ERROR_MANAGED_PROFILE},
+     * {@link UserManager#USER_OPERATION_ERROR_CURRENT_USER}
      * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
      * @see #getSecondaryUsers(ComponentName)
      */
-    public boolean logoutUser(@NonNull ComponentName admin) {
+    public @UserOperationResult int logoutUser(@NonNull ComponentName admin) {
         throwIfParentInstance("logoutUser");
         try {
             return mService.logoutUser(admin);
@@ -7101,30 +7112,24 @@
     }
 
     /**
-     * Sets which system features to enable for LockTask mode.
-     * <p>
-     * Feature flags set through this method will only take effect for the duration when the device
-     * is in LockTask mode. If this method is not called, none of the features listed here will be
-     * enabled.
-     * <p>
-     * This function can only be called by the device owner, a profile owner of an affiliated user
-     * or profile, or the profile owner when no device owner is set. See {@link #isAffiliatedUser}.
-     * Any features set via this method will be cleared if the user becomes unaffiliated.
+     * Sets which system features are enabled when the device runs in lock task mode. This method
+     * doesn't affect the features when lock task mode is inactive. Any system features not included
+     * in {@code flags} are implicitly disabled when calling this method. By default, only
+     * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled—all the other features are disabled. To
+     * disable the global actions dialog, call this method omitting
+     * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}.
+     *
+     * <p>This method can only be called by the device owner, a profile owner of an affiliated
+     * user or profile, or the profile owner when no device owner is set. See
+     * {@link #isAffiliatedUser}.
+     * Any features set using this method are cleared if the user becomes unaffiliated.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param flags Bitfield of feature flags:
-     *              {@link #LOCK_TASK_FEATURE_NONE} (default),
-     *              {@link #LOCK_TASK_FEATURE_SYSTEM_INFO},
-     *              {@link #LOCK_TASK_FEATURE_NOTIFICATIONS},
-     *              {@link #LOCK_TASK_FEATURE_HOME},
-     *              {@link #LOCK_TASK_FEATURE_RECENTS},
-     *              {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS},
-     *              {@link #LOCK_TASK_FEATURE_KEYGUARD}
+     * @param flags The system features enabled during lock task mode.
      * @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
      * affiliated user or profile, or the profile owner when no device owner is set.
      * @see #isAffiliatedUser
-     * @throws SecurityException if {@code admin} is not the device owner or the profile owner.
-     */
+     **/
     public void setLockTaskFeatures(@NonNull ComponentName admin, @LockTaskFeature int flags) {
         throwIfParentInstance("setLockTaskFeatures");
         if (mService != null) {
@@ -7207,12 +7212,22 @@
         }
     }
 
+    /** @hide */
+    @StringDef({
+            Settings.System.SCREEN_BRIGHTNESS_MODE,
+            Settings.System.SCREEN_BRIGHTNESS,
+            Settings.System.SCREEN_OFF_TIMEOUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemSettingsWhitelist {}
+
     /**
-     * Called by device owner to update {@link android.provider.Settings.System} settings.
-     * Validation that the value of the setting is in the correct form for the setting type should
-     * be performed by the caller.
+     * Called by a device or profile owner to update {@link android.provider.Settings.System}
+     * settings. Validation that the value of the setting is in the correct form for the setting
+     * type should be performed by the caller.
      * <p>
-     * The settings that can be updated with this method are:
+     * The settings that can be updated by a device owner or profile owner of secondary user with
+     * this method are:
      * <ul>
      * <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS}</li>
      * <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS_MODE}</li>
@@ -7224,10 +7239,10 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param setting The name of the setting to update.
      * @param value The value to update the setting to.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public void setSystemSetting(@NonNull ComponentName admin, @NonNull String setting,
-            String value) {
+    public void setSystemSetting(@NonNull ComponentName admin,
+            @NonNull @SystemSettingsWhitelist String setting, String value) {
         throwIfParentInstance("setSystemSetting");
         if (mService != null) {
             try {
@@ -7540,13 +7555,28 @@
     /**
      * Called by device owners to set a local system update policy. When a new policy is set,
      * {@link #ACTION_SYSTEM_UPDATE_POLICY_CHANGED} is broadcasted.
+     * <p>
+     * If the supplied system update policy has freeze periods set but the freeze periods do not
+     * meet 90-day maximum length or 60-day minimum separation requirement set out in
+     * {@link SystemUpdatePolicy#setFreezePeriods},
+     * {@link SystemUpdatePolicy.ValidationFailedException} will the thrown. Note that the system
+     * keeps a record of freeze periods the device experienced previously, and combines them with
+     * the new freeze periods to be set when checking the maximum freeze length and minimum freeze
+     * separation constraints. As a result, freeze periods that passed validation during
+     * {@link SystemUpdatePolicy#setFreezePeriods} might fail the additional checks here due to
+     * the freeze period history. If this is causing issues during development,
+     * {@code adb shell dpm clear-freeze-period-record} can be used to clear the record.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. All
      *            components in the device owner package can set system update policies and the most
      *            recent policy takes effect.
      * @param policy the new policy, or {@code null} to clear the current policy.
      * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws IllegalArgumentException if the policy type or maintenance window is not valid.
+     * @throws SystemUpdatePolicy.ValidationFailedException if the policy's freeze period does not
+     *             meet the requirement.
      * @see SystemUpdatePolicy
+     * @see SystemUpdatePolicy#setFreezePeriods(List)
      */
     public void setSystemUpdatePolicy(@NonNull ComponentName admin, SystemUpdatePolicy policy) {
         throwIfParentInstance("setSystemUpdatePolicy");
@@ -7577,6 +7607,23 @@
     }
 
     /**
+     * Reset record of previous system update freeze period the device went through.
+     * Only callable by ADB.
+     * @hide
+     */
+    public void clearSystemUpdatePolicyFreezePeriodRecord() {
+        throwIfParentInstance("clearSystemUpdatePolicyFreezePeriodRecord");
+        if (mService == null) {
+            return;
+        }
+        try {
+            mService.clearSystemUpdatePolicyFreezePeriodRecord();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called by a device owner or profile owner of secondary users that is affiliated with the
      * device to disable the keyguard altogether.
      * <p>
@@ -7759,11 +7806,14 @@
      * {@link #PERMISSION_GRANT_STATE_DEFAULT default} in which a user can manage it through the UI,
      * {@link #PERMISSION_GRANT_STATE_DENIED denied}, in which the permission is denied and the user
      * cannot manage it through the UI, and {@link #PERMISSION_GRANT_STATE_GRANTED granted} in which
-     * the permission is granted and the user cannot manage it through the UI. This might affect all
-     * permissions in a group that the runtime permission belongs to. This method can only be called
-     * by a profile owner, device owner, or a delegate given the
+     * the permission is granted and the user cannot manage it through the UI. This method can only
+     * be called by a profile owner, device owner, or a delegate given the
      * {@link #DELEGATION_PERMISSION_GRANT} scope via {@link #setDelegatedScopes}.
      * <p/>
+     * Note that user cannot manage other permissions in the affected group through the UI
+     * either and their granted state will be kept as the current value. Thus, it's recommended that
+     * you set the grant state of all the permissions in the affected group.
+     * <p/>
      * Setting the grant state to {@link #PERMISSION_GRANT_STATE_DEFAULT default} does not revoke
      * the permission. It retains the previous grant, if any.
      * <p/>
@@ -8214,6 +8264,22 @@
     }
 
     /**
+     * Forces a batch of security logs to be fetched from logd and makes it available for DPC.
+     * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+     * @hide
+     */
+    public long forceSecurityLogs() {
+        if (mService == null) {
+            return 0;
+        }
+        try {
+            return mService.forceSecurityLogs();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called by the system to obtain a {@link DevicePolicyManager} whose calls act on the parent
      * profile.
      *
@@ -8230,19 +8296,19 @@
     }
 
     /**
-     * Called by a device or profile owner to restrict packages from accessing metered data.
+     * Called by a device or profile owner to restrict packages from using metered data.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageNames the list of package names to be restricted.
      * @return a list of package names which could not be restricted.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public @NonNull List<String> setMeteredDataDisabled(@NonNull ComponentName admin,
+    public @NonNull List<String> setMeteredDataDisabledPackages(@NonNull ComponentName admin,
             @NonNull List<String> packageNames) {
         throwIfParentInstance("setMeteredDataDisabled");
         if (mService != null) {
             try {
-                return mService.setMeteredDataDisabled(admin, packageNames);
+                return mService.setMeteredDataDisabledPackages(admin, packageNames);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -8252,17 +8318,17 @@
 
     /**
      * Called by a device or profile owner to retrieve the list of packages which are restricted
-     * by the admin from accessing metered data.
+     * by the admin from using metered data.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @return the list of restricted package names.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public @NonNull List<String> getMeteredDataDisabled(@NonNull ComponentName admin) {
+    public @NonNull List<String> getMeteredDataDisabledPackages(@NonNull ComponentName admin) {
         throwIfParentInstance("getMeteredDataDisabled");
         if (mService != null) {
             try {
-                return mService.getMeteredDataDisabled(admin);
+                return mService.getMeteredDataDisabledPackages(admin);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -8271,6 +8337,30 @@
     }
 
     /**
+     * Called by the system to check if a package is restricted from using metered data
+     * by {@param admin}.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName the package whose restricted status is needed.
+     * @param userId the user to which {@param packageName} belongs.
+     * @return {@code true} if the package is restricted by admin, otherwise {@code false}
+     * @throws SecurityException if the caller doesn't run with {@link Process#SYSTEM_UID}
+     * @hide
+     */
+    public boolean isMeteredDataDisabledPackageForUser(@NonNull ComponentName admin,
+            String packageName, @UserIdInt int userId) {
+        throwIfParentInstance("getMeteredDataDisabledForUser");
+        if (mService != null) {
+            try {
+                return mService.isMeteredDataDisabledPackageForUser(admin, packageName, userId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by device owners to retrieve device logs from before the device's last reboot.
      * <p>
      * <strong> This API is not supported on all devices. Calling this API on unsupported devices
@@ -8460,6 +8550,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @UserProvisioningState
     public int getUserProvisioningState() {
         throwIfParentInstance("getUserProvisioningState");
@@ -8604,6 +8695,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean isDeviceProvisioned() {
         try {
             return mService.isDeviceProvisioned();
@@ -8727,14 +8819,20 @@
      * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
      * specified, backups will be enabled.
      *
+     * <p>NOTE: The method shouldn't be called on the main thread.
+     *
      * @param admin admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
+     * @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public void setMandatoryBackupTransport(
-            @NonNull ComponentName admin, @Nullable ComponentName backupTransportComponent) {
+    @WorkerThread
+    public boolean setMandatoryBackupTransport(
+            @NonNull ComponentName admin,
+            @Nullable ComponentName backupTransportComponent) {
+        throwIfParentInstance("setMandatoryBackupTransport");
         try {
-            mService.setMandatoryBackupTransport(admin, backupTransportComponent);
+            return mService.setMandatoryBackupTransport(admin, backupTransportComponent);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -8748,6 +8846,7 @@
      *         mandatory or {@code null} if backups are not mandatory.
      */
     public ComponentName getMandatoryBackupTransport() {
+        throwIfParentInstance("getMandatoryBackupTransport");
         try {
             return mService.getMandatoryBackupTransport();
         } catch (RemoteException re) {
@@ -9045,15 +9144,15 @@
      * @param executor The executor through which the listener should be invoked.
      * @param listener A callback object that will inform the caller when the clearing is done.
      * @throws SecurityException if the caller is not the device owner/profile owner.
-     * @return whether the clearing succeeded.
      */
-    public boolean clearApplicationUserData(@NonNull ComponentName admin,
+    public void clearApplicationUserData(@NonNull ComponentName admin,
             @NonNull String packageName, @NonNull @CallbackExecutor Executor executor,
             @NonNull OnClearApplicationUserDataListener listener) {
         throwIfParentInstance("clearAppData");
         Preconditions.checkNotNull(executor);
+        Preconditions.checkNotNull(listener);
         try {
-            return mService.clearApplicationUserData(admin, packageName,
+            mService.clearApplicationUserData(admin, packageName,
                     new IPackageDataObserver.Stub() {
                         public void onRemoveCompleted(String pkg, boolean succeeded) {
                             executor.execute(() ->
@@ -9153,9 +9252,10 @@
      * after calling this method.
      *
      * <p>The incoming target administrator must have the
-     * {@link DeviceAdminReceiver#SUPPORT_TRANSFER_OWNERSHIP_META_DATA} <code>meta-data</code> tag
-     * included in its corresponding <code>receiver</code> component with a value of {@code true}.
-     * Otherwise an {@link IllegalArgumentException} will be thrown.
+     * <code>&lt;support-transfer-ownership /&gt;</code> tag inside the
+     * <code>&lt;device-admin&gt;&lt;/device-admin&gt;</code> tags in the xml file referenced by
+     * {@link DeviceAdminReceiver#DEVICE_ADMIN_META_DATA}. Otherwise an
+     * {@link IllegalArgumentException} will be thrown.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with
      * @param target which {@link DeviceAdminReceiver} we want the new administrator to be
@@ -9255,43 +9355,27 @@
     }
 
     /**
-     * Allows/disallows printing.
-     *
-     * Called by a device owner or a profile owner.
-     * Device owner changes policy for all users. Profile owner can override it if present.
-     * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored.
-     *
-     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
-     * @param enabled whether printing should be allowed or not.
-     * @throws SecurityException if {@code admin} is neither device, nor profile owner.
-     */
-    public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) {
-        try {
-            mService.setPrintingEnabled(admin, enabled);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Returns whether printing is enabled for this user.
-     *
-     * Always {@code false} if {@code FEATURE_PRINTING} is absent.
-     * Otherwise, {@code true} by default.
-     *
-     * @return {@code true} iff printing is enabled.
-     */
-    public boolean isPrintingEnabled() {
-        try {
-            return mService.isPrintingEnabled();
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Called by device owner to add an override APN.
      *
+     * <p>This method may returns {@code -1} if {@code apnSetting} conflicts with an existing
+     * override APN. Update the existing conflicted APN with
+     * {@link #updateOverrideApn(ComponentName, int, ApnSetting)} instead of adding a new entry.
+     * <p>Two override APNs are considered to conflict when all the following APIs return
+     * the same values on both override APNs:
+     * <ul>
+     *   <li>{@link ApnSetting#getOperatorNumeric()}</li>
+     *   <li>{@link ApnSetting#getApnName()}</li>
+     *   <li>{@link ApnSetting#getProxyAddress()}</li>
+     *   <li>{@link ApnSetting#getProxyPort()}</li>
+     *   <li>{@link ApnSetting#getMmsProxyAddress()}</li>
+     *   <li>{@link ApnSetting#getMmsProxyPort()}</li>
+     *   <li>{@link ApnSetting#getMmsc()}</li>
+     *   <li>{@link ApnSetting#isEnabled()}</li>
+     *   <li>{@link ApnSetting#getMvnoType()}</li>
+     *   <li>{@link ApnSetting#getProtocol()}</li>
+     *   <li>{@link ApnSetting#getRoamingProtocol()}</li>
+     * </ul>
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with
      * @param apnSetting the override APN to insert
      * @return The {@code id} of inserted override APN. Or {@code -1} when failed to insert into
@@ -9315,6 +9399,12 @@
     /**
      * Called by device owner to update an override APN.
      *
+     * <p>This method may returns {@code false} if there is no override APN with the given
+     * {@code apnId}.
+     * <p>This method may also returns {@code false} if {@code apnSetting} conflicts with an
+     * existing override APN. Update the existing conflicted APN instead.
+     * <p>See {@link #addOverrideApn} for the definition of conflict.
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with
      * @param apnId the {@code id} of the override APN to update
      * @param apnSetting the override APN to update
@@ -9340,6 +9430,9 @@
     /**
      * Called by device owner to remove an override APN.
      *
+     * <p>This method may returns {@code false} if there is no override APN with the given
+     * {@code apnId}.
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with
      * @param apnId the {@code id} of the override APN to remove
      * @return {@code true} if the required override APN is successfully removed, {@code false}
@@ -9426,12 +9519,19 @@
     /**
      * Returns the data passed from the current administrator to the new administrator during an
      * ownership transfer. This is the same {@code bundle} passed in
-     * {@link #transferOwnership(ComponentName, ComponentName, PersistableBundle)}.
+     * {@link #transferOwnership(ComponentName, ComponentName, PersistableBundle)}. The bundle is
+     * persisted until the profile owner or device owner is removed.
+     *
+     * <p>This is the same <code>bundle</code> received in the
+     * {@link DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)}.
+     * Use this method to retrieve it after the transfer as long as the new administrator is the
+     * active device or profile owner.
      *
      * <p>Returns <code>null</code> if no ownership transfer was started for the calling user.
      *
      * @see #transferOwnership
      * @see DeviceAdminReceiver#onTransferOwnershipComplete(Context, PersistableBundle)
+     * @throws SecurityException if the caller is not a device or profile owner.
      */
     @Nullable
     public PersistableBundle getTransferOwnershipBundle() {
diff --git a/android/app/admin/DevicePolicyManagerInternal.java b/android/app/admin/DevicePolicyManagerInternal.java
index ebaf464..de92978 100644
--- a/android/app/admin/DevicePolicyManagerInternal.java
+++ b/android/app/admin/DevicePolicyManagerInternal.java
@@ -141,4 +141,10 @@
      * @return localized error message
      */
     public abstract CharSequence getPrintingDisabledReasonForUser(@UserIdInt int userId);
+
+    /**
+     * @return cached version of DPM policies that can be accessed without risking deadlocks.
+     * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
+     */
+    protected abstract DevicePolicyCache getDevicePolicyCache();
 }
diff --git a/android/app/admin/FreezeInterval.java b/android/app/admin/FreezeInterval.java
new file mode 100644
index 0000000..de5e21a
--- /dev/null
+++ b/android/app/admin/FreezeInterval.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import android.app.admin.SystemUpdatePolicy.ValidationFailedException;
+import android.util.Log;
+import android.util.Pair;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An interval representing one freeze period which repeats annually. We use the number of days
+ * since the start of (non-leap) year to define the start and end dates of an interval, both
+ * inclusive. If the end date is smaller than the start date, the interval is considered wrapped
+ * around the year-end. As far as an interval is concerned, February 29th should be treated as
+ * if it were February 28th: so an interval starting or ending on February 28th are not
+ * distinguishable from an interval on February 29th. When calulating interval length or
+ * distance between two dates, February 29th is also disregarded.
+ *
+ * @see SystemUpdatePolicy#setFreezePeriods
+ * @hide
+ */
+public class FreezeInterval {
+    private static final String TAG = "FreezeInterval";
+
+    private static final int DUMMY_YEAR = 2001;
+    static final int DAYS_IN_YEAR = 365; // 365 since DUMMY_YEAR is not a leap year
+
+    final int mStartDay; // [1,365]
+    final int mEndDay; // [1,365]
+
+    FreezeInterval(int startDay, int endDay) {
+        if (startDay < 1 || startDay > 365 || endDay < 1 || endDay > 365) {
+            throw new RuntimeException("Bad dates for Interval: " + startDay + "," + endDay);
+        }
+        mStartDay = startDay;
+        mEndDay = endDay;
+    }
+
+    int getLength() {
+        return getEffectiveEndDay() - mStartDay + 1;
+    }
+
+    boolean isWrapped() {
+        return mEndDay < mStartDay;
+    }
+
+    /**
+     * Returns the effective end day, taking wrapping around year-end into consideration
+     */
+    int getEffectiveEndDay() {
+        if (!isWrapped()) {
+            return mEndDay;
+        } else {
+            return mEndDay + DAYS_IN_YEAR;
+        }
+    }
+
+    boolean contains(LocalDate localDate) {
+        final int daysOfYear = dayOfYearDisregardLeapYear(localDate);
+        if (!isWrapped()) {
+            // ---[start---now---end]---
+            return (mStartDay <= daysOfYear) && (daysOfYear <= mEndDay);
+        } else {
+            //    ---end]---[start---now---
+            // or ---now---end]---[start---
+            return (mStartDay <= daysOfYear) || (daysOfYear <= mEndDay);
+        }
+    }
+
+    boolean after(LocalDate localDate) {
+        return mStartDay > dayOfYearDisregardLeapYear(localDate);
+    }
+
+    /**
+     * Instantiate the current interval to real calendar dates, given a calendar date
+     * {@code now}. If the interval contains now, the returned calendar dates should be the
+     * current interval (in real calendar dates) that includes now. If the interval does not
+     * include now, the returned dates represents the next future interval.
+     * The result will always have the same month and dayOfMonth value as the non-instantiated
+     * interval itself.
+     */
+    Pair<LocalDate, LocalDate> toCurrentOrFutureRealDates(LocalDate now) {
+        final int nowDays = dayOfYearDisregardLeapYear(now);
+        final int startYearAdjustment, endYearAdjustment;
+        if (contains(now)) {
+            // current interval
+            if (mStartDay <= nowDays) {
+                //    ----------[start---now---end]---
+                // or ---end]---[start---now----------
+                startYearAdjustment = 0;
+                endYearAdjustment = isWrapped() ? 1 : 0;
+            } else /* nowDays <= mEndDay */ {
+                // or ---now---end]---[start----------
+                startYearAdjustment = -1;
+                endYearAdjustment = 0;
+            }
+        } else {
+            // next interval
+            if (mStartDay > nowDays) {
+                //    ----------now---[start---end]---
+                // or ---end]---now---[start----------
+                startYearAdjustment = 0;
+                endYearAdjustment = isWrapped() ? 1 : 0;
+            } else /* mStartDay <= nowDays */ {
+                // or ---[start---end]---now----------
+                startYearAdjustment = 1;
+                endYearAdjustment = 1;
+            }
+        }
+        final LocalDate startDate = LocalDate.ofYearDay(DUMMY_YEAR, mStartDay).withYear(
+                now.getYear() + startYearAdjustment);
+        final LocalDate endDate = LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).withYear(
+                now.getYear() + endYearAdjustment);
+        return new Pair<>(startDate, endDate);
+    }
+
+    @Override
+    public String toString() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd");
+        return LocalDate.ofYearDay(DUMMY_YEAR, mStartDay).format(formatter) + " - "
+                + LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).format(formatter);
+    }
+
+    // Treat the supplied date as in a non-leap year and return its day of year.
+    static int dayOfYearDisregardLeapYear(LocalDate date) {
+        return date.withYear(DUMMY_YEAR).getDayOfYear();
+    }
+
+    /**
+     * Compute the number of days between first (inclusive) and second (exclusive),
+     * treating all years in between as non-leap.
+     */
+    public static int distanceWithoutLeapYear(LocalDate first, LocalDate second) {
+        return dayOfYearDisregardLeapYear(first) - dayOfYearDisregardLeapYear(second)
+                + DAYS_IN_YEAR * (first.getYear() - second.getYear());
+    }
+
+    /**
+     * Sort, de-duplicate and merge an interval list
+     *
+     * Instead of using any fancy logic for merging intervals which has loads of corner cases,
+     * simply flatten the interval onto a list of 365 calendar days and recreate the interval list
+     * from that.
+     *
+     * This method should return a list of intervals with the following post-conditions:
+     *     1. Interval.startDay in strictly ascending order
+     *     2. No two intervals should overlap or touch
+     *     3. At most one wrapped Interval remains, and it will be at the end of the list
+     * @hide
+     */
+    protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
+        boolean[] taken = new boolean[DAYS_IN_YEAR];
+        // First convert the intervals into flat array
+        for (FreezeInterval interval : intervals) {
+            for (int i = interval.mStartDay; i <= interval.getEffectiveEndDay(); i++) {
+                taken[(i - 1) % DAYS_IN_YEAR] = true;
+            }
+        }
+        // Then reconstruct intervals from the array
+        List<FreezeInterval> result = new ArrayList<>();
+        int i = 0;
+        while (i < DAYS_IN_YEAR) {
+            if (!taken[i]) {
+                i++;
+                continue;
+            }
+            final int intervalStart = i + 1;
+            while (i < DAYS_IN_YEAR && taken[i]) i++;
+            result.add(new FreezeInterval(intervalStart, i));
+        }
+        // Check if the last entry can be merged to the first entry to become one single
+        // wrapped interval
+        final int lastIndex = result.size() - 1;
+        if (lastIndex > 0 && result.get(lastIndex).mEndDay == DAYS_IN_YEAR
+                && result.get(0).mStartDay == 1) {
+            FreezeInterval wrappedInterval = new FreezeInterval(result.get(lastIndex).mStartDay,
+                    result.get(0).mEndDay);
+            result.set(lastIndex, wrappedInterval);
+            result.remove(0);
+        }
+        return result;
+    }
+
+    /**
+     * Verifies if the supplied freeze periods satisfies the constraints set out in
+     * {@link SystemUpdatePolicy#setFreezePeriods(List)}, and in particular, any single freeze
+     * period cannot exceed {@link SystemUpdatePolicy#FREEZE_PERIOD_MAX_LENGTH} days, and two freeze
+     * periods need to be at least {@link SystemUpdatePolicy#FREEZE_PERIOD_MIN_SEPARATION} days
+     * apart.
+     *
+     * @hide
+     */
+    protected static void validatePeriods(List<FreezeInterval> periods) {
+        List<FreezeInterval> allPeriods = FreezeInterval.canonicalizeIntervals(periods);
+        if (allPeriods.size() != periods.size()) {
+            throw SystemUpdatePolicy.ValidationFailedException.duplicateOrOverlapPeriods();
+        }
+        for (int i = 0; i < allPeriods.size(); i++) {
+            FreezeInterval current = allPeriods.get(i);
+            if (current.getLength() > SystemUpdatePolicy.FREEZE_PERIOD_MAX_LENGTH) {
+                throw SystemUpdatePolicy.ValidationFailedException.freezePeriodTooLong("Freeze "
+                        + "period " + current + " is too long: " + current.getLength() + " days");
+            }
+            FreezeInterval previous = i > 0 ? allPeriods.get(i - 1)
+                    : allPeriods.get(allPeriods.size() - 1);
+            if (previous != current) {
+                final int separation;
+                if (i == 0 && !previous.isWrapped()) {
+                    // -->[current]---[-previous-]<---
+                    separation = current.mStartDay
+                            + (DAYS_IN_YEAR - previous.mEndDay) - 1;
+                } else {
+                    //    --[previous]<--->[current]---------
+                    // OR ----prev---]<--->[current]---[prev-
+                    separation = current.mStartDay - previous.mEndDay - 1;
+                }
+                if (separation < SystemUpdatePolicy.FREEZE_PERIOD_MIN_SEPARATION) {
+                    throw SystemUpdatePolicy.ValidationFailedException.freezePeriodTooClose("Freeze"
+                            + " periods " + previous + " and " + current + " are too close "
+                            + "together: " + separation + " days apart");
+                }
+            }
+        }
+    }
+
+    /**
+     * Verifies that the current freeze periods are still legal, considering the previous freeze
+     * periods the device went through. In particular, when combined with the previous freeze
+     * period, the maximum freeze length or the minimum freeze separation should not be violated.
+     *
+     * @hide
+     */
+    protected static void validateAgainstPreviousFreezePeriod(List<FreezeInterval> periods,
+            LocalDate prevPeriodStart, LocalDate prevPeriodEnd, LocalDate now) {
+        if (periods.size() == 0 || prevPeriodStart == null || prevPeriodEnd == null) {
+            return;
+        }
+        if (prevPeriodStart.isAfter(now) || prevPeriodEnd.isAfter(now)) {
+            Log.w(TAG, "Previous period (" + prevPeriodStart + "," + prevPeriodEnd + ") is after"
+                    + " current date " + now);
+            // Clock was adjusted backwards. We can continue execution though, the separation
+            // and length validation below still works under this condition.
+        }
+        List<FreezeInterval> allPeriods = FreezeInterval.canonicalizeIntervals(periods);
+        // Given current time now, find the freeze period that's either current, or the one
+        // that's immediately afterwards. For the later case, it might be after the year-end,
+        // but this can only happen if there is only one freeze period.
+        FreezeInterval curOrNextFreezePeriod = allPeriods.get(0);
+        for (FreezeInterval interval : allPeriods) {
+            if (interval.contains(now)
+                    || interval.mStartDay > FreezeInterval.dayOfYearDisregardLeapYear(now)) {
+                curOrNextFreezePeriod = interval;
+                break;
+            }
+        }
+        Pair<LocalDate, LocalDate> curOrNextFreezeDates = curOrNextFreezePeriod
+                .toCurrentOrFutureRealDates(now);
+        if (now.isAfter(curOrNextFreezeDates.first)) {
+            curOrNextFreezeDates = new Pair<>(now, curOrNextFreezeDates.second);
+        }
+        if (curOrNextFreezeDates.first.isAfter(curOrNextFreezeDates.second)) {
+            throw new IllegalStateException("Current freeze dates inverted: "
+                    + curOrNextFreezeDates.first + "-" + curOrNextFreezeDates.second);
+        }
+        // Now validate [prevPeriodStart, prevPeriodEnd] against curOrNextFreezeDates
+        final String periodsDescription = "Prev: " + prevPeriodStart + "," + prevPeriodEnd
+                + "; cur: " + curOrNextFreezeDates.first + "," + curOrNextFreezeDates.second;
+        long separation = FreezeInterval.distanceWithoutLeapYear(curOrNextFreezeDates.first,
+                prevPeriodEnd) - 1;
+        if (separation > 0) {
+            // Two intervals do not overlap, check separation
+            if (separation < SystemUpdatePolicy.FREEZE_PERIOD_MIN_SEPARATION) {
+                throw ValidationFailedException.combinedPeriodTooClose("Previous freeze period "
+                        + "too close to new period: " + separation + ", " + periodsDescription);
+            }
+        } else {
+            // Two intervals overlap, check combined length
+            long length = FreezeInterval.distanceWithoutLeapYear(curOrNextFreezeDates.second,
+                    prevPeriodStart) + 1;
+            if (length > SystemUpdatePolicy.FREEZE_PERIOD_MAX_LENGTH) {
+                throw ValidationFailedException.combinedPeriodTooLong("Combined freeze period "
+                        + "exceeds maximum days: " + length + ", " + periodsDescription);
+            }
+        }
+    }
+}
diff --git a/android/app/admin/SecurityLog.java b/android/app/admin/SecurityLog.java
index d3b66d0..38b4f8f 100644
--- a/android/app/admin/SecurityLog.java
+++ b/android/app/admin/SecurityLog.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.TestApi;
+import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemProperties;
@@ -53,64 +54,396 @@
             TAG_APP_PROCESS_START,
             TAG_KEYGUARD_DISMISSED,
             TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
-            TAG_KEYGUARD_SECURED
+            TAG_KEYGUARD_SECURED,
+            TAG_OS_STARTUP,
+            TAG_OS_SHUTDOWN,
+            TAG_LOGGING_STARTED,
+            TAG_LOGGING_STOPPED,
+            TAG_MEDIA_MOUNT,
+            TAG_MEDIA_UNMOUNT,
+            TAG_LOG_BUFFER_SIZE_CRITICAL,
+            TAG_PASSWORD_EXPIRATION_SET,
+            TAG_PASSWORD_COMPLEXITY_SET,
+            TAG_PASSWORD_HISTORY_LENGTH_SET,
+            TAG_MAX_SCREEN_LOCK_TIMEOUT_SET,
+            TAG_MAX_PASSWORD_ATTEMPTS_SET,
+            TAG_KEYGUARD_DISABLED_FEATURES_SET,
+            TAG_REMOTE_LOCK,
+            TAG_USER_RESTRICTION_ADDED,
+            TAG_USER_RESTRICTION_REMOVED,
+            TAG_WIPE_FAILURE,
+            TAG_KEY_GENERATED,
+            TAG_KEY_IMPORT,
+            TAG_KEY_DESTRUCTION,
+            TAG_CERT_AUTHORITY_INSTALLED,
+            TAG_CERT_AUTHORITY_REMOVED,
+            TAG_CRYPTO_SELF_TEST_COMPLETED,
+            TAG_KEY_INTEGRITY_VIOLATION,
+            TAG_CERT_VALIDATION_FAILURE,
     })
     public @interface SecurityLogTag {}
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "LEVEL_" }, value = {
+            LEVEL_INFO,
+            LEVEL_WARNING,
+            LEVEL_ERROR
+    })
+    public @interface SecurityLogLevel {}
+
     /**
-     * Indicate that an ADB interactive shell was opened via "adb shell".
+     * Indicates that an ADB interactive shell was opened via "adb shell".
      * There is no extra payload in the log event.
      */
     public static final int TAG_ADB_SHELL_INTERACTIVE =
             SecurityLogTags.SECURITY_ADB_SHELL_INTERACTIVE;
+
     /**
-     * Indicate that an shell command was issued over ADB via "adb shell command"
-     * The log entry contains a string data of the shell command, accessible via
-     * {@link SecurityEvent#getData()}
+     * Indicates that a shell command was issued over ADB via {@code adb shell <command>}
+     * The log entry contains a {@code String} payload containing the shell command, accessible
+     * via {@link SecurityEvent#getData()}.
      */
     public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND;
+
     /**
-     * Indicate that a file was pulled from the device via the adb daemon, for example via
-     * "adb pull". The log entry contains a string data of the path of the pulled file,
-     * accessible via {@link SecurityEvent#getData()}
+     * Indicates that a file was pulled from the device via the adb daemon, for example via
+     * {@code adb pull}. The log entry contains a {@code String} payload containing the path of the
+     * pulled file on the device, accessible via {@link SecurityEvent#getData()}.
      */
     public static final int TAG_SYNC_RECV_FILE = SecurityLogTags.SECURITY_ADB_SYNC_RECV;
+
     /**
-     * Indicate that a file was pushed to the device via the adb daemon, for example via
-     * "adb push". The log entry contains a string data of the destination path of the
-     * pushed file, accessible via {@link SecurityEvent#getData()}
+     * Indicates that a file was pushed to the device via the adb daemon, for example via
+     * {@code adb push}. The log entry contains a {@code String} payload containing the destination
+     * path of the pushed file, accessible via {@link SecurityEvent#getData()}.
      */
     public static final int TAG_SYNC_SEND_FILE = SecurityLogTags.SECURITY_ADB_SYNC_SEND;
+
     /**
-     * Indicate that an app process was started. The log entry contains the following
+     * Indicates that an app process was started. The log entry contains the following
      * information about the process encapsulated in an {@link Object} array, accessible via
      * {@link SecurityEvent#getData()}:
-     * process name (String), exact start time (long), app Uid (integer), app Pid (integer),
-     * seinfo tag (String), SHA-256 hash of the base APK in hexadecimal (String)
+     * <li> [0] process name ({@code String})
+     * <li> [1] exact start time in milliseconds according to {@code System.currentTimeMillis()}
+     *      ({@code Long})
+     * <li> [2] app uid ({@code Integer})
+     * <li> [3] app pid ({@code Integer})
+     * <li> [4] seinfo tag ({@code String})
+     * <li> [5] SHA-256 hash of the base APK in hexadecimal ({@code String})
      */
     public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START;
+
     /**
-     * Indicate that keyguard is being dismissed.
+     * Indicates that keyguard has been dismissed.
      * There is no extra payload in the log event.
      */
-    public static final int TAG_KEYGUARD_DISMISSED =
-            SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
+    public static final int TAG_KEYGUARD_DISMISSED = SecurityLogTags.SECURITY_KEYGUARD_DISMISSED;
+
     /**
-     * Indicate that there has been an authentication attempt to dismiss the keyguard. The log entry
-     * contains the following information about the attempt encapsulated in an {@link Object} array,
-     * accessible via {@link SecurityEvent#getData()}:
-     * attempt result (integer, 1 for successful, 0 for unsuccessful), strength of auth method
-     * (integer, 1 if strong auth method was used, 0 otherwise)
+     * Indicates that there has been an authentication attempt to dismiss the keyguard. The log
+     * entry contains the following information about the attempt encapsulated in an {@link Object}
+     * array, accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] attempt result ({@code Integer}, 1 for successful, 0 for unsuccessful)
+     * <li> [1] strength of authentication method ({@code Integer}, 1 if strong authentication
+     *      method was used, 0 otherwise)
      */
     public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT =
             SecurityLogTags.SECURITY_KEYGUARD_DISMISS_AUTH_ATTEMPT;
+
     /**
-     * Indicate that the device has been locked, either by user or by timeout.
-     * There is no extra payload in the log event.
+     * Indicates that the device has been locked, either by the user or by a timeout. There is no
+     * extra payload in the log event.
      */
     public static final int TAG_KEYGUARD_SECURED = SecurityLogTags.SECURITY_KEYGUARD_SECURED;
 
     /**
+     * Indicates that the Android OS has started. The log entry contains the following information
+     * about the startup time software integrity check encapsulated in an {@link Object} array,
+     * accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] Verified Boot state ({@code String})
+     * <li> [1] dm-verity mode ({@code String}).
+     * <p>Verified Boot state can be one of the following:
+     * <li> {@code green} indicates that there is a full chain of trust extending from the
+     * bootloader to verified partitions including the bootloader, boot partition, and all verified
+     * partitions.
+     * <li> {@code yellow} indicates that the boot partition has been verified using the embedded
+     * certificate and the signature is valid.
+     * <li> {@code orange} indicates that the device may be freely modified. Device integrity is
+     * left to the user to verify out-of-band.
+     * <p>dm-verity mode can be one of the following:
+     * <li> {@code enforcing} indicates that the device will be restarted when corruption is
+     * detected.
+     * <li> {@code eio} indicates that an I/O error will be returned for an attempt to read
+     * corrupted data blocks.
+     * For details see Verified Boot documentation.
+     */
+    public static final int TAG_OS_STARTUP = SecurityLogTags.SECURITY_OS_STARTUP;
+
+    /**
+     * Indicates that the Android OS has shutdown. There is no extra payload in the log event.
+     */
+    public static final int TAG_OS_SHUTDOWN = SecurityLogTags.SECURITY_OS_SHUTDOWN;
+
+    /**
+     * Indicates start-up of audit logging. There is no extra payload in the log event.
+     */
+    public static final int TAG_LOGGING_STARTED = SecurityLogTags.SECURITY_LOGGING_STARTED;
+
+    /**
+     * Indicates shutdown of audit logging. There is no extra payload in the log event.
+     */
+    public static final int TAG_LOGGING_STOPPED = SecurityLogTags.SECURITY_LOGGING_STOPPED;
+
+    /**
+     * Indicates that removable media has been mounted on the device. The log entry contains the
+     * following information about the event, encapsulated in an {@link Object} array and
+     * accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] mount point ({@code String})
+     * <li> [1] volume label ({@code String}).
+     */
+    public static final int TAG_MEDIA_MOUNT = SecurityLogTags.SECURITY_MEDIA_MOUNTED;
+
+    /**
+     * Indicates that removable media was unmounted from the device. The log entry contains the
+     * following information about the event, encapsulated in an {@link Object} array and
+     * accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] mount point ({@code String})
+     * <li> [1] volume label ({@code String}).
+     */
+    public static final int TAG_MEDIA_UNMOUNT = SecurityLogTags.SECURITY_MEDIA_UNMOUNTED;
+
+    /**
+     * Indicates that the audit log buffer has reached 90% of its capacity. There is no extra
+     * payload in the log event.
+     */
+    public static final int TAG_LOG_BUFFER_SIZE_CRITICAL =
+            SecurityLogTags.SECURITY_LOG_BUFFER_SIZE_CRITICAL;
+
+    /**
+     * Indicates that an admin has set a password expiration timeout. The log entry contains the
+     * following information about the event, encapsulated in an {@link Object} array and accessible
+     * via {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] new password expiration timeout in milliseconds ({@code Long}).
+     * @see DevicePolicyManager#setPasswordExpirationTimeout(ComponentName, long)
+     */
+    public static final int TAG_PASSWORD_EXPIRATION_SET =
+            SecurityLogTags.SECURITY_PASSWORD_EXPIRATION_SET;
+
+    /**
+     * Indicates that an admin has set a requirement for password complexity. The log entry contains
+     * the following information about the event, encapsulated in an {@link Object} array and
+     * accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] minimum password length ({@code Integer})
+     * <li> [4] password quality constraint ({@code Integer})
+     * <li> [5] minimum number of letters ({@code Integer})
+     * <li> [6] minimum number of non-letters ({@code Integer})
+     * <li> [7] minimum number of digits ({@code Integer})
+     * <li> [8] minimum number of uppercase letters ({@code Integer})
+     * <li> [9] minimum number of lowercase letters ({@code Integer})
+     * <li> [10] minimum number of symbols ({@code Integer})
+     *
+     * @see DevicePolicyManager#setPasswordMinimumLength(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordQuality(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordMinimumLetters(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordMinimumNonLetter(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordMinimumLowerCase(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordMinimumUpperCase(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordMinimumNumeric(ComponentName, int)
+     * @see DevicePolicyManager#setPasswordMinimumSymbols(ComponentName, int)
+     */
+    public static final int TAG_PASSWORD_COMPLEXITY_SET =
+            SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_SET;
+
+    /**
+     * Indicates that an admin has set a password history length. The log entry contains the
+     * following information about the event encapsulated in an {@link Object} array, accessible
+     * via {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] new password history length value ({@code Integer})
+     * @see DevicePolicyManager#setPasswordHistoryLength(ComponentName, int)
+     */
+    public static final int TAG_PASSWORD_HISTORY_LENGTH_SET =
+            SecurityLogTags.SECURITY_PASSWORD_HISTORY_LENGTH_SET;
+
+    /**
+     * Indicates that an admin has set a maximum screen lock timeout. The log entry contains the
+     * following information about the event encapsulated in an {@link Object} array, accessible
+     * via {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] new screen lock timeout in milliseconds ({@code Long})
+     * @see DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)
+     */
+    public static final int TAG_MAX_SCREEN_LOCK_TIMEOUT_SET =
+            SecurityLogTags.SECURITY_MAX_SCREEN_LOCK_TIMEOUT_SET;
+
+    /**
+     * Indicates that an admin has set a maximum number of failed password attempts before wiping
+     * data. The log entry contains the following information about the event encapsulated in an
+     * {@link Object} array, accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] new maximum number of failed password attempts ({@code Integer})
+     * @see DevicePolicyManager#setMaximumFailedPasswordsForWipe(ComponentName, int)
+     */
+    public static final int TAG_MAX_PASSWORD_ATTEMPTS_SET =
+            SecurityLogTags.SECURITY_MAX_PASSWORD_ATTEMPTS_SET;
+
+    /**
+     * Indicates that an admin has set disabled keyguard features. The log entry contains the
+     * following information about the event encapsulated in an {@link Object} array, accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] disabled keyguard feature mask ({@code Integer}).
+     * @see DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)
+     */
+    public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET =
+            SecurityLogTags.SECURITY_KEYGUARD_DISABLED_FEATURES_SET;
+
+    /**
+     * Indicates that an admin remotely locked the device or profile. The log entry contains the
+     * following information about the event encapsulated in an {@link Object} array, accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String}),
+     * <li> [1] admin user ID ({@code Integer}).
+     * <li> [2] target user ID ({@code Integer})
+     */
+    public static final int TAG_REMOTE_LOCK = SecurityLogTags.SECURITY_REMOTE_LOCK;
+
+    /**
+     * Indicates a failure to wipe device or user data. There is no extra payload in the log event.
+     */
+    public static final int TAG_WIPE_FAILURE = SecurityLogTags.SECURITY_WIPE_FAILED;
+
+    /**
+     * Indicates that an authentication key was generated. The log entry contains the following
+     * information about the event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
+     * <li> [1] alias of the key ({@code String})
+     * <li> [2] requesting process uid ({@code Integer}).
+     */
+    public static final int TAG_KEY_GENERATED =
+            SecurityLogTags.SECURITY_KEY_GENERATED;
+
+    /**
+     * Indicates that a cryptographic key was imported. The log entry contains the following
+     * information about the event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
+     * <li> [1] alias of the key ({@code String})
+     * <li> [2] requesting process uid ({@code Integer}).
+     */
+    public static final int TAG_KEY_IMPORT = SecurityLogTags.SECURITY_KEY_IMPORTED;
+
+    /**
+     * Indicates that a cryptographic key was destroyed. The log entry contains the following
+     * information about the event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
+     * <li> [1] alias of the key ({@code String})
+     * <li> [2] requesting process uid ({@code Integer}).
+     */
+    public static final int TAG_KEY_DESTRUCTION = SecurityLogTags.SECURITY_KEY_DESTROYED;
+
+    /**
+     * Indicates that a new root certificate has been installed into system's trusted credential
+     * storage. The log entry contains the following information about the event, encapsulated in an
+     * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
+     * <li> [1] subject of the certificate ({@code String}).
+     */
+    public static final int TAG_CERT_AUTHORITY_INSTALLED =
+            SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED;
+
+    /**
+     * Indicates that a new root certificate has been removed from system's trusted credential
+     * storage. The log entry contains the following information about the event, encapsulated in an
+     * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
+     * <li> [1] subject of the certificate ({@code String}).
+     */
+    public static final int TAG_CERT_AUTHORITY_REMOVED =
+            SecurityLogTags.SECURITY_CERT_AUTHORITY_REMOVED;
+
+    /**
+     * Indicates that an admin has set a user restriction. The log entry contains the following
+     * information about the event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] user restriction ({@code String})
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     */
+    public static final int TAG_USER_RESTRICTION_ADDED =
+            SecurityLogTags.SECURITY_USER_RESTRICTION_ADDED;
+
+    /**
+     * Indicates that an admin has removed a user restriction. The log entry contains the following
+     * information about the event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] user restriction ({@code String})
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     */
+    public static final int TAG_USER_RESTRICTION_REMOVED =
+            SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED;
+
+    /**
+     * Indicates that cryptographic functionality self test has completed. The log entry contains an
+     * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if
+     * succeeded) and accessible via {@link SecurityEvent#getData()}.
+     */
+    public static final int TAG_CRYPTO_SELF_TEST_COMPLETED =
+            SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED;
+
+    /**
+     * Indicates a failed cryptographic key integrity check. The log entry contains the following
+     * information about the event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] alias of the key ({@code String})
+     * <li> [1] owner application uid ({@code Integer}).
+     */
+    public static final int TAG_KEY_INTEGRITY_VIOLATION =
+            SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION;
+
+    /**
+     * Indicates a failure to validate X.509v3 certificate. The log entry contains a {@code String}
+     * payload indicating the failure reason, accessible via {@link SecurityEvent#getData()}.
+     */
+    public static final int TAG_CERT_VALIDATION_FAILURE =
+            SecurityLogTags.SECURITY_CERT_VALIDATION_FAILURE;
+
+    /**
+     * Event severity level indicating that the event corresponds to normal workflow.
+     */
+    public static final int LEVEL_INFO = 1;
+
+    /**
+     * Event severity level indicating that the event may require admin attention.
+     */
+    public static final int LEVEL_WARNING = 2;
+
+    /**
+     * Event severity level indicating that the event requires urgent admin action.
+     */
+    public static final int LEVEL_ERROR = 3;
+
+    /**
      * Returns if security logging is enabled. Log producers should only write new logs if this is
      * true. Under the hood this is the logical AND of whether device owner exists and whether
      * it enables logging by setting the system property {@link #PROPERTY_LOGGING_ENABLED}.
@@ -198,6 +531,64 @@
             return mId;
         }
 
+        /**
+         * Returns severity level for the event.
+         */
+        public @SecurityLogLevel int getLogLevel() {
+            switch (mEvent.getTag()) {
+                case TAG_ADB_SHELL_INTERACTIVE:
+                case TAG_ADB_SHELL_CMD:
+                case TAG_SYNC_RECV_FILE:
+                case TAG_SYNC_SEND_FILE:
+                case TAG_APP_PROCESS_START:
+                case TAG_KEYGUARD_DISMISSED:
+                case TAG_KEYGUARD_SECURED:
+                case TAG_OS_STARTUP:
+                case TAG_OS_SHUTDOWN:
+                case TAG_LOGGING_STARTED:
+                case TAG_LOGGING_STOPPED:
+                case TAG_MEDIA_MOUNT:
+                case TAG_MEDIA_UNMOUNT:
+                case TAG_PASSWORD_EXPIRATION_SET:
+                case TAG_PASSWORD_COMPLEXITY_SET:
+                case TAG_PASSWORD_HISTORY_LENGTH_SET:
+                case TAG_MAX_SCREEN_LOCK_TIMEOUT_SET:
+                case TAG_MAX_PASSWORD_ATTEMPTS_SET:
+                case TAG_USER_RESTRICTION_ADDED:
+                case TAG_USER_RESTRICTION_REMOVED:
+                    return LEVEL_INFO;
+                case TAG_CERT_AUTHORITY_REMOVED:
+                case TAG_CRYPTO_SELF_TEST_COMPLETED:
+                    return getSuccess() ? LEVEL_INFO : LEVEL_ERROR;
+                case TAG_CERT_AUTHORITY_INSTALLED:
+                case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT:
+                case TAG_KEY_IMPORT:
+                case TAG_KEY_DESTRUCTION:
+                case TAG_KEY_GENERATED:
+                    return getSuccess() ? LEVEL_INFO : LEVEL_WARNING;
+                case TAG_LOG_BUFFER_SIZE_CRITICAL:
+                case TAG_WIPE_FAILURE:
+                case TAG_KEY_INTEGRITY_VIOLATION:
+                    return LEVEL_ERROR;
+                case TAG_CERT_VALIDATION_FAILURE:
+                    return LEVEL_WARNING;
+                default:
+                    return LEVEL_INFO;
+            }
+        }
+
+        // Success/failure if present is encoded as an integer in the first (0th) element of data.
+        private boolean getSuccess() {
+            final Object data = getData();
+            if (data == null || !(data instanceof Object[])) {
+                return false;
+            }
+
+            final Object[] array = (Object[]) data;
+            return array.length >= 1 && array[0] instanceof Integer && (Integer) array[0] != 0;
+        }
+
+
         @Override
         public int describeContents() {
             return 0;
@@ -263,8 +654,8 @@
             throws IOException;
 
     /**
-     * Retrieve all security logs whose timestamp (in nanosceonds) is equal to or greater than the
-     * given timestamp. This method will block until either the last log earlier than the given
+     * Retrieve all security logs whose timestamp is equal to or greater than the given timestamp in
+     * nanoseconds. This method will block until either the last log earlier than the given
      * timestamp is about to be pruned, or after a 2-hour timeout has passed.
      * @hide
      */
diff --git a/android/app/admin/SystemUpdatePolicy.java b/android/app/admin/SystemUpdatePolicy.java
index 232a688..47b3a81 100644
--- a/android/app/admin/SystemUpdatePolicy.java
+++ b/android/app/admin/SystemUpdatePolicy.java
@@ -16,16 +16,34 @@
 
 package android.app.admin;
 
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.TEXT;
+
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
+import android.util.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * A class that represents a local system update policy set by the device owner.
@@ -34,6 +52,7 @@
  * @see DevicePolicyManager#getSystemUpdatePolicy
  */
 public class SystemUpdatePolicy implements Parcelable {
+    private static final String TAG = "SystemUpdatePolicy";
 
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
@@ -91,23 +110,173 @@
      */
     public static final int TYPE_POSTPONE = 3;
 
+    /**
+     * Incoming system updates (including security updates) should be blocked. This flag is not
+     * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used
+     * to represent the current installation option type to the privileged system update clients,
+     * for example to indicate OTA freeze is currently in place or when system is outside a daily
+     * maintenance window.
+     *
+     * @see InstallationOption
+     * @hide
+     */
+    @SystemApi
+    public static final int TYPE_PAUSE = 4;
+
     private static final String KEY_POLICY_TYPE = "policy_type";
     private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
     private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
+    private static final String KEY_FREEZE_TAG = "freeze";
+    private static final String KEY_FREEZE_START = "start";
+    private static final String KEY_FREEZE_END = "end";
+
     /**
      * The upper boundary of the daily maintenance window: 24 * 60 minutes.
      */
     private static final int WINDOW_BOUNDARY = 24 * 60;
 
+    /**
+     * The maximum length of a single freeze period: 90  days.
+     */
+    static final int FREEZE_PERIOD_MAX_LENGTH = 90;
+
+    /**
+     * The minimum allowed time between two adjacent freeze period (from the end of the first
+     * freeze period to the start of the second freeze period, both exclusive): 60 days.
+     */
+    static final int FREEZE_PERIOD_MIN_SEPARATION = 60;
+
+
+    /**
+     * An exception class that represents various validation errors thrown from
+     * {@link SystemUpdatePolicy#setFreezePeriods} and
+     * {@link DevicePolicyManager#setSystemUpdatePolicy}
+     */
+    public static final class ValidationFailedException extends IllegalArgumentException
+            implements Parcelable {
+
+        /** @hide */
+        @IntDef(prefix = { "ERROR_" }, value = {
+                ERROR_NONE,
+                ERROR_DUPLICATE_OR_OVERLAP,
+                ERROR_NEW_FREEZE_PERIOD_TOO_LONG,
+                ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE,
+                ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG,
+                ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface ValidationFailureType {}
+
+        /** @hide */
+        public static final int ERROR_NONE = 0;
+
+        /**
+         * The freeze periods contains duplicates, periods that overlap with each
+         * other or periods whose start and end joins.
+         */
+        public static final int ERROR_DUPLICATE_OR_OVERLAP = 1;
+
+        /**
+         * There exists at least one freeze period whose length exceeds 90 days.
+         */
+        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 2;
+
+        /**
+         * There exists some freeze period which starts within 60 days of the preceding period's
+         * end time.
+         */
+        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 3;
+
+        /**
+         * The device has been in a freeze period and when combining with the new freeze period
+         * to be set, it will result in the total freeze period being longer than 90 days.
+         */
+        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 4;
+
+        /**
+         * The device has been in a freeze period and some new freeze period to be set is less
+         * than 60 days from the end of the last freeze period the device went through.
+         */
+        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 5;
+
+        @ValidationFailureType
+        private final int mErrorCode;
+
+        private ValidationFailedException(int errorCode, String message) {
+            super(message);
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * Returns the type of validation error associated with this exception.
+         */
+        public @ValidationFailureType int getErrorCode() {
+            return mErrorCode;
+        }
+
+        /** @hide */
+        public static ValidationFailedException duplicateOrOverlapPeriods() {
+            return new ValidationFailedException(ERROR_DUPLICATE_OR_OVERLAP,
+                    "Found duplicate or overlapping periods");
+        }
+
+        /** @hide */
+        public static ValidationFailedException freezePeriodTooLong(String message) {
+            return new ValidationFailedException(ERROR_NEW_FREEZE_PERIOD_TOO_LONG, message);
+        }
+
+        /** @hide */
+        public static ValidationFailedException freezePeriodTooClose(String message) {
+            return new ValidationFailedException(ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE, message);
+        }
+
+        /** @hide */
+        public static ValidationFailedException combinedPeriodTooLong(String message) {
+            return new ValidationFailedException(ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG, message);
+        }
+
+        /** @hide */
+        public static ValidationFailedException combinedPeriodTooClose(String message) {
+            return new ValidationFailedException(ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE, message);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mErrorCode);
+            dest.writeString(getMessage());
+        }
+
+        public static final Parcelable.Creator<ValidationFailedException> CREATOR =
+                new Parcelable.Creator<ValidationFailedException>() {
+            @Override
+            public ValidationFailedException createFromParcel(Parcel source) {
+                return new ValidationFailedException(source.readInt(), source.readString());
+            }
+
+            @Override
+            public ValidationFailedException[] newArray(int size) {
+                return new ValidationFailedException[size];
+            }
+
+        };
+    }
+
     @SystemUpdatePolicyType
     private int mPolicyType;
 
     private int mMaintenanceWindowStart;
     private int mMaintenanceWindowEnd;
 
+    private final ArrayList<FreezeInterval> mFreezePeriods;
 
     private SystemUpdatePolicy() {
         mPolicyType = TYPE_UNKNOWN;
+        mFreezePeriods = new ArrayList<>();
     }
 
     /**
@@ -206,38 +375,305 @@
     }
 
     /**
-     * Return if this object represents a valid policy.
+     * Return if this object represents a valid policy with:
+     * 1. Correct type
+     * 2. Valid maintenance window if applicable
+     * 3. Valid freeze periods
      * @hide
      */
     public boolean isValid() {
-        if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+        try {
+            validateType();
+            validateFreezePeriods();
             return true;
-        } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
-            return mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY
-                    && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY;
-        } else {
+        } catch (IllegalArgumentException e) {
             return false;
         }
     }
 
-    @Override
-    public String toString() {
-        return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d)",
-                mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd);
+    /**
+     * Validate the type and maintenance window (if applicable) of this policy object,
+     * throws {@link IllegalArgumentException} if it's invalid.
+     * @hide
+     */
+    public void validateType() {
+        if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+            return;
+        } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            if (!(mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY
+                    && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY)) {
+                throw new IllegalArgumentException("Invalid maintenance window");
+            }
+        } else {
+            throw new IllegalArgumentException("Invalid system update policy type.");
+        }
     }
 
+    /**
+     * Configure a list of freeze periods on top of the current policy. When the device's clock is
+     * within any of the freeze periods, all incoming system updates including security patches will
+     * be blocked and cannot be installed. When the device is outside the freeze periods, the normal
+     * policy behavior will apply.
+     * <p>
+     * Each freeze period is defined by a starting and finishing date (both inclusive). Since the
+     * freeze period repeats annually, both of these dates are simply represented by integers
+     * counting the number of days since year start, similar to {@link LocalDate#getDayOfYear()}. We
+     * do not consider leap year when handling freeze period so the valid range of the integer is
+     * always [1,365] (see last section for more details on leap year). If the finishing date is
+     * smaller than the starting date, the freeze period is considered to be spanning across
+     * year-end.
+     * <p>
+     * Each individual freeze period is allowed to be at most 90 days long, and adjacent freeze
+     * periods need to be at least 60 days apart. Also, the list of freeze periods should not
+     * contain duplicates or overlap with each other. If any of these conditions is not met, a
+     * {@link ValidationFailedException} will be thrown.
+     * <p>
+     * Handling of leap year: we do not consider leap year when handling freeze period, in
+     * particular,
+     * <ul>
+     * <li>When a freeze period is defined by the day of year, February 29th does not count as one
+     * day, so day 59 is February 28th while day 60 is March 1st.</li>
+     * <li>When applying freeze period behavior to the device, a system clock of February 29th is
+     * treated as if it were February 28th</li>
+     * <li>When calculating the number of days of a freeze period or separation between two freeze
+     * periods, February 29th is also ignored and not counted as one day.</li>
+     * </ul>
+     *
+     * @param freezePeriods the list of freeze periods
+     * @throws ValidationFailedException if the supplied freeze periods do not meet the
+     *         requirement set above
+     * @return this instance
+     */
+    public SystemUpdatePolicy setFreezePeriods(List<Pair<Integer, Integer>> freezePeriods) {
+        List<FreezeInterval> newPeriods = freezePeriods.stream().map(
+                p -> new FreezeInterval(p.first, p.second)).collect(Collectors.toList());
+        FreezeInterval.validatePeriods(newPeriods);
+        mFreezePeriods.clear();
+        mFreezePeriods.addAll(newPeriods);
+        return this;
+    }
+
+    /**
+     * Returns the list of freeze periods previously set on this system update policy object.
+     *
+     * @return the list of freeze periods, or an empty list if none was set.
+     */
+    public List<Pair<Integer, Integer>> getFreezePeriods() {
+        List<Pair<Integer, Integer>> result = new ArrayList<>(mFreezePeriods.size());
+        for (FreezeInterval interval : mFreezePeriods) {
+            result.add(new Pair<>(interval.mStartDay, interval.mEndDay));
+        }
+        return result;
+    }
+
+    /**
+     * Returns the real calendar dates of the current freeze period, or null if the device
+     * is not in a freeze period at the moment.
+     * @hide
+     */
+    public Pair<LocalDate, LocalDate> getCurrentFreezePeriod(LocalDate now) {
+        for (FreezeInterval interval : mFreezePeriods) {
+            if (interval.contains(now)) {
+                return interval.toCurrentOrFutureRealDates(now);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns time (in milliseconds) until the start of the next freeze period, assuming now
+     * is not within a freeze period.
+     */
+    private long timeUntilNextFreezePeriod(long now) {
+        List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods);
+        LocalDate nowDate = millisToDate(now);
+        LocalDate nextFreezeStart = null;
+        for (FreezeInterval interval : sortedPeriods) {
+            if (interval.after(nowDate)) {
+                nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
+                break;
+            } else if (interval.contains(nowDate)) {
+                throw new IllegalArgumentException("Given date is inside a freeze period");
+            }
+        }
+        if (nextFreezeStart == null) {
+            // If no interval is after now, then it must be the one that starts at the beginning
+            // of next year
+            nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first;
+        }
+        return dateToMillis(nextFreezeStart) - now;
+    }
+
+    /** @hide */
+    public void validateFreezePeriods() {
+        FreezeInterval.validatePeriods(mFreezePeriods);
+    }
+
+    /** @hide */
+    public void validateAgainstPreviousFreezePeriod(LocalDate prevPeriodStart,
+            LocalDate prevPeriodEnd, LocalDate now) {
+        FreezeInterval.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
+                prevPeriodEnd, now);
+    }
+
+    /**
+     * An installation option represents how system update clients should act on incoming system
+     * updates and how long this action is valid for, given the current system update policy. Its
+     * action could be one of the following
+     * <ul>
+     * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without
+     * user intervention as soon as they become available.
+     * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
+     * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice
+     * </ul>
+     *
+     * The effective time measures how long this installation option is valid for from the queried
+     * time, in milliseconds.
+     *
+     * This is an internal API for system update clients.
+     * @hide
+     */
+    @SystemApi
+    public static class InstallationOption {
+        private final int mType;
+        private long mEffectiveTime;
+
+        InstallationOption(int type, long effectiveTime) {
+            this.mType = type;
+            this.mEffectiveTime = effectiveTime;
+        }
+
+        public int getType() {
+            return mType;
+        }
+
+        public long getEffectiveTime() {
+            return mEffectiveTime;
+        }
+
+        /** @hide */
+        protected void limitEffectiveTime(long otherTime) {
+            mEffectiveTime = Long.min(mEffectiveTime, otherTime);
+        }
+    }
+
+    /**
+     * Returns the installation option at the specified time, under the current
+     * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients
+     * so they can instantiate this policy at any given time and find out what to do with incoming
+     * system updates, without the need of examining the overall policy structure.
+     *
+     * Normally the system update clients will query the current installation option by calling this
+     * method with the current timestamp, and act on the returned option until its effective time
+     * lapses. It can then query the latest option using a new timestamp. It should also listen
+     * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the
+     * whole policy is updated.
+     *
+     * @param when At what time the intallation option is being queried, specified in number of
+           milliseonds since the epoch.
+     * @see InstallationOption
+     * @hide
+     */
+    @SystemApi
+    public InstallationOption getInstallationOptionAt(long when) {
+        LocalDate whenDate = millisToDate(when);
+        Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate);
+        if (current != null) {
+            return new InstallationOption(TYPE_PAUSE,
+                    dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when);
+        }
+        // We are not within a freeze period, query the underlying policy.
+        // But also consider the start of the next freeze period, which might
+        // reduce the effective time of the current installation option
+        InstallationOption option = getInstallationOptionRegardlessFreezeAt(when);
+        if (mFreezePeriods.size() > 0) {
+            option.limitEffectiveTime(timeUntilNextFreezePeriod(when));
+        }
+        return option;
+    }
+
+    private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) {
+        if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+            return new InstallationOption(mPolicyType, Long.MAX_VALUE);
+        } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            Calendar query = Calendar.getInstance();
+            query.setTimeInMillis(when);
+            // Calculate the number of milliseconds since midnight of the time specified by when
+            long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY))
+                    + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE))
+                    + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND))
+                    + query.get(Calendar.MILLISECOND);
+            long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart);
+            long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd);
+            final long dayInMillis = TimeUnit.DAYS.toMillis(1);
+
+            if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis)
+                    || ((windowStartMillis > windowEndMillis)
+                    && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) {
+                return new InstallationOption(TYPE_INSTALL_AUTOMATIC,
+                        (windowEndMillis - whenMillis + dayInMillis) % dayInMillis);
+            } else {
+                return new InstallationOption(TYPE_PAUSE,
+                        (windowStartMillis - whenMillis + dayInMillis) % dayInMillis);
+            }
+        } else {
+            throw new RuntimeException("Unknown policy type");
+        }
+    }
+
+    private static LocalDate roundUpLeapDay(LocalDate date) {
+        if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) {
+            return date.plusDays(1);
+        } else {
+            return date;
+        }
+    }
+
+    /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating
+     * the hour/min/seconds part.
+     */
+    private static LocalDate millisToDate(long when) {
+        return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+    /**
+     * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00.
+     */
+    private static long dateToMillis(LocalDate when) {
+        return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()
+                .toEpochMilli();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, "
+                + "freezes: [%s])",
+                mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd,
+                mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
+    }
+
+    @SystemApi
     @Override
     public int describeContents() {
         return 0;
     }
 
+    @SystemApi
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mPolicyType);
         dest.writeInt(mMaintenanceWindowStart);
         dest.writeInt(mMaintenanceWindowEnd);
+        int freezeCount = mFreezePeriods.size();
+        dest.writeInt(freezeCount);
+        for (int i = 0; i < freezeCount; i++) {
+            FreezeInterval interval = mFreezePeriods.get(i);
+            dest.writeInt(interval.mStartDay);
+            dest.writeInt(interval.mEndDay);
+        }
     }
 
+    @SystemApi
     public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
             new Parcelable.Creator<SystemUpdatePolicy>() {
 
@@ -247,6 +683,12 @@
                     policy.mPolicyType = source.readInt();
                     policy.mMaintenanceWindowStart = source.readInt();
                     policy.mMaintenanceWindowEnd = source.readInt();
+                    int freezeCount = source.readInt();
+                    policy.mFreezePeriods.ensureCapacity(freezeCount);
+                    for (int i = 0; i < freezeCount; i++) {
+                        policy.mFreezePeriods.add(
+                                new FreezeInterval(source.readInt(), source.readInt()));
+                    }
                     return policy;
                 }
 
@@ -256,8 +698,10 @@
                 }
     };
 
-
     /**
+     * Restore a previously saved SystemUpdatePolicy from XML. No need to validate
+     * the reconstructed policy since the XML is supposed to be created by the
+     * system server from a validated policy object previously.
      * @hide
      */
     public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) {
@@ -275,10 +719,26 @@
                 if (value != null) {
                     policy.mMaintenanceWindowEnd = Integer.parseInt(value);
                 }
+
+                int outerDepth = parser.getDepth();
+                int type;
+                while ((type = parser.next()) != END_DOCUMENT
+                        && (type != END_TAG || parser.getDepth() > outerDepth)) {
+                    if (type == END_TAG || type == TEXT) {
+                        continue;
+                    }
+                    if (!parser.getName().equals(KEY_FREEZE_TAG)) {
+                        continue;
+                    }
+                    policy.mFreezePeriods.add(new FreezeInterval(
+                            Integer.parseInt(parser.getAttributeValue(null, KEY_FREEZE_START)),
+                            Integer.parseInt(parser.getAttributeValue(null, KEY_FREEZE_END))));
+                }
                 return policy;
             }
-        } catch (NumberFormatException e) {
+        } catch (NumberFormatException | XmlPullParserException | IOException e) {
             // Fail through
+            Log.w(TAG, "Load xml failed", e);
         }
         return null;
     }
@@ -290,6 +750,13 @@
         out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType));
         out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
         out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
+        for (int i = 0; i < mFreezePeriods.size(); i++) {
+            FreezeInterval interval = mFreezePeriods.get(i);
+            out.startTag(null, KEY_FREEZE_TAG);
+            out.attribute(null, KEY_FREEZE_START, Integer.toString(interval.mStartDay));
+            out.attribute(null, KEY_FREEZE_END, Integer.toString(interval.mEndDay));
+            out.endTag(null, KEY_FREEZE_TAG);
+        }
     }
 }
 
diff --git a/android/app/assist/AssistStructure.java b/android/app/assist/AssistStructure.java
index 87f2271..d568662 100644
--- a/android/app/assist/AssistStructure.java
+++ b/android/app/assist/AssistStructure.java
@@ -4,6 +4,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.ComponentName;
+import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -23,6 +24,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.view.View;
+import android.view.View.AutofillImportance;
 import android.view.ViewRootImpl;
 import android.view.ViewStructure;
 import android.view.ViewStructure.HtmlInfo;
@@ -500,9 +502,8 @@
             ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
             if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                 if (forAutoFill) {
-                    final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
-                            ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
-                    view.onProvideAutofillStructure(builder, autofillFlags);
+                    final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
+                    view.onProvideAutofillStructure(builder, viewFlags);
                 } else {
                     // This is a secure window, so it doesn't want a screenshot, and that
                     // means we should also not copy out its view hierarchy for Assist
@@ -512,9 +513,8 @@
                 }
             }
             if (forAutoFill) {
-                final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
-                        ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
-                view.dispatchProvideAutofillStructure(builder, autofillFlags);
+                final int viewFlags = resolveViewAutofillFlags(view.getContext(), flags);
+                view.dispatchProvideAutofillStructure(builder, viewFlags);
             } else {
                 view.dispatchProvideStructure(builder);
             }
@@ -532,6 +532,12 @@
             mRoot = new ViewNode(reader, 0);
         }
 
+        int resolveViewAutofillFlags(Context context, int fillRequestFlags) {
+            return (fillRequestFlags & FillRequest.FLAG_MANUAL_REQUEST) != 0
+                        || context.isAutofillCompatibilityEnabled()
+                    ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+        }
+
         void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
             out.writeInt(mX);
             out.writeInt(mY);
@@ -627,6 +633,7 @@
         int mMaxEms = -1;
         int mMaxLength = -1;
         @Nullable String mTextIdEntry;
+        @AutofillImportance int mImportantForAutofill;
 
         // POJO used to override some autofill-related values when the node is parcelized.
         // Not written to parcel.
@@ -728,6 +735,7 @@
                 mMaxEms = in.readInt();
                 mMaxLength = in.readInt();
                 mTextIdEntry = preader.readString();
+                mImportantForAutofill = in.readInt();
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 mX = in.readInt();
@@ -895,6 +903,7 @@
                 out.writeInt(mMaxEms);
                 out.writeInt(mMaxLength);
                 pwriter.writeString(mTextIdEntry);
+                out.writeInt(mImportantForAutofill);
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 out.writeInt(mX);
@@ -1294,6 +1303,22 @@
         }
 
         /**
+         * @hide
+         */
+        public void setWebDomain(@Nullable String domain) {
+            if (domain == null) return;
+
+            final Uri uri = Uri.parse(domain);
+            if (uri == null) {
+                // Cannot log domain because it could contain PII;
+                Log.w(TAG, "Failed to parse web domain");
+                return;
+            }
+            mWebScheme = uri.getScheme();
+            mWebDomain = uri.getHost();
+        }
+
+        /**
          * Returns the scheme of the HTML document represented by this view.
          *
          * <p>Typically used when the view associated with the view is a container for an HTML
@@ -1507,6 +1532,16 @@
         public int getMaxTextLength() {
             return mMaxLength;
         }
+
+        /**
+         * Gets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of
+         * the view associated with this node.
+         *
+         * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
+         */
+        public @AutofillImportance int getImportantForAutofill() {
+            return mImportantForAutofill;
+        }
     }
 
     /**
@@ -1839,6 +1874,11 @@
         }
 
         @Override
+        public void setImportantForAutofill(@AutofillImportance int mode) {
+            mNode.mImportantForAutofill = mode;
+        }
+
+        @Override
         public void setInputType(int inputType) {
             mNode.mInputType = inputType;
         }
@@ -1865,14 +1905,7 @@
 
         @Override
         public void setWebDomain(@Nullable String domain) {
-            if (domain == null) {
-                mNode.mWebScheme = null;
-                mNode.mWebDomain = null;
-                return;
-            }
-            Uri uri = Uri.parse(domain);
-            mNode.mWebScheme = uri.getScheme();
-            mNode.mWebDomain = uri.getHost();
+            mNode.setWebDomain(domain);
         }
 
         @Override
@@ -2139,7 +2172,8 @@
                     + ", options=" + Arrays.toString(node.getAutofillOptions())
                     + ", hints=" + Arrays.toString(node.getAutofillHints())
                     + ", value=" + node.getAutofillValue()
-                    + ", sanitized=" + node.isSanitized());
+                    + ", sanitized=" + node.isSanitized()
+                    + ", importantFor=" + node.getImportantForAutofill());
         }
 
         final int NCHILDREN = node.getChildCount();
@@ -2203,6 +2237,22 @@
         return mWindowNodes.get(index);
     }
 
+    // TODO(b/35708678): temporary method that disable one-way warning flag on binder.
+    /** @hide */
+    public void ensureDataForAutofill() {
+        if (mHaveData) {
+            return;
+        }
+        mHaveData = true;
+        Binder.allowBlocking(mReceiveChannel);
+        try {
+            ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
+            reader.go();
+        } finally {
+            Binder.defaultBlocking(mReceiveChannel);
+        }
+    }
+
     /** @hide */
     public void ensureData() {
         if (mHaveData) {
diff --git a/android/app/backup/BackupAgent.java b/android/app/backup/BackupAgent.java
index 861cb9a..d1c957b 100644
--- a/android/app/backup/BackupAgent.java
+++ b/android/app/backup/BackupAgent.java
@@ -18,6 +18,7 @@
 
 import android.app.IBackupAgent;
 import android.app.QueuedWork;
+import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.ApplicationInfo;
@@ -143,6 +144,36 @@
     /** @hide */
     public static final int TYPE_SYMLINK = 3;
 
+    /**
+     * Flag for {@link BackupDataOutput#getTransportFlags()} and
+     * {@link FullBackupDataOutput#getTransportFlags()} only.
+     *
+     * <p>The transport has client-side encryption enabled. i.e., the user's backup has been
+     * encrypted with a key known only to the device, and not to the remote storage solution. Even
+     * if an attacker had root access to the remote storage provider they should not be able to
+     * decrypt the user's backup data.
+     */
+    public static final int FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED = 1;
+
+    /**
+     * Flag for {@link BackupDataOutput#getTransportFlags()} and
+     * {@link FullBackupDataOutput#getTransportFlags()} only.
+     *
+     * <p>The transport is for a device-to-device transfer. There is no third party or intermediate
+     * storage. The user's backup data is sent directly to another device over e.g., USB or WiFi.
+     */
+    public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
+
+    /**
+     * Flag for {@link BackupDataOutput#getTransportFlags()} and
+     * {@link FullBackupDataOutput#getTransportFlags()} only.
+     *
+     * <p>Used for internal testing only. Do not check this flag in production code.
+     *
+     * @hide
+     */
+    public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31;
+
     Handler mHandler = null;
 
     Handler getHandler() {
@@ -313,8 +344,8 @@
             return;
         }
 
-        Map<String, Set<String>> manifestIncludeMap;
-        ArraySet<String> manifestExcludeSet;
+        Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap;
+        ArraySet<PathWithRequiredFlags> manifestExcludeSet;
         try {
             manifestIncludeMap =
                     backupScheme.maybeParseAndGetCanonicalIncludePaths();
@@ -484,14 +515,13 @@
     /**
      * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
      * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
-     * is a directory.
+     * is a directory, but only if all the required flags of the include rule are satisfied by
+     * the transport.
      */
     private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
-                                                         Map<String, Set<String>> includeMap,
-                                                         ArraySet<String> filterSet,
-                                                         ArraySet<String> traversalExcludeSet,
-                                                         FullBackupDataOutput data)
-            throws IOException {
+            Map<String, Set<PathWithRequiredFlags>> includeMap,
+            ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet,
+            FullBackupDataOutput data) throws IOException {
         if (includeMap == null || includeMap.size() == 0) {
             // Do entire sub-tree for the provided token.
             fullBackupFileTree(packageName, domainToken,
@@ -500,13 +530,22 @@
         } else if (includeMap.get(domainToken) != null) {
             // This will be null if the xml parsing didn't yield any rules for
             // this domain (there may still be rules for other domains).
-            for (String includeFile : includeMap.get(domainToken)) {
-                fullBackupFileTree(packageName, domainToken, includeFile, filterSet,
-                        traversalExcludeSet, data);
+            for (PathWithRequiredFlags includeFile : includeMap.get(domainToken)) {
+                if (areIncludeRequiredTransportFlagsSatisfied(includeFile.getRequiredFlags(),
+                        data.getTransportFlags())) {
+                    fullBackupFileTree(packageName, domainToken, includeFile.getPath(), filterSet,
+                            traversalExcludeSet, data);
+                }
             }
         }
     }
 
+    private boolean areIncludeRequiredTransportFlagsSatisfied(int includeFlags,
+            int transportFlags) {
+        // all bits that are set in includeFlags must also be set in transportFlags
+        return (transportFlags & includeFlags) == includeFlags;
+    }
+
     /**
      * Write an entire file as part of a full-backup operation.  The file's contents
      * will be delivered to the backup destination along with the metadata necessary
@@ -653,7 +692,7 @@
      * @hide
      */
     protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
-                                            ArraySet<String> manifestExcludes,
+                                            ArraySet<PathWithRequiredFlags> manifestExcludes,
                                             ArraySet<String> systemExcludes,
             FullBackupDataOutput output) {
         // Pull out the domain and set it aside to use when making the tarball.
@@ -684,7 +723,8 @@
                     filePath = file.getCanonicalPath();
 
                     // prune this subtree?
-                    if (manifestExcludes != null && manifestExcludes.contains(filePath)) {
+                    if (manifestExcludes != null
+                            && manifestExcludesContainFilePath(manifestExcludes, filePath)) {
                         continue;
                     }
                     if (systemExcludes != null && systemExcludes.contains(filePath)) {
@@ -720,6 +760,17 @@
         }
     }
 
+    private boolean manifestExcludesContainFilePath(
+        ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) {
+        for (PathWithRequiredFlags exclude : manifestExcludes) {
+            String excludePath = exclude.getPath();
+            if (excludePath != null && excludePath.equals(filePath)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Handle the data delivered via the given file descriptor during a full restore
      * operation.  The agent is given the path to the file's original location as well
@@ -766,8 +817,8 @@
             return false;
         }
 
-        Map<String, Set<String>> includes = null;
-        ArraySet<String> excludes = null;
+        Map<String, Set<PathWithRequiredFlags>> includes = null;
+        ArraySet<PathWithRequiredFlags> excludes = null;
         final String destinationCanonicalPath = destination.getCanonicalPath();
         try {
             includes = bs.maybeParseAndGetCanonicalIncludePaths();
@@ -796,7 +847,7 @@
             // Rather than figure out the <include/> domain based on the path (a lot of code, and
             // it's a small list), we'll go through and look for it.
             boolean explicitlyIncluded = false;
-            for (Set<String> domainIncludes : includes.values()) {
+            for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) {
                 explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes);
                 if (explicitlyIncluded) {
                     break;
@@ -819,9 +870,10 @@
      * @return True if the provided file is either directly in the provided list, or the provided
      * file is within a directory in the list.
      */
-    private boolean isFileSpecifiedInPathList(File file, Collection<String> canonicalPathList)
-            throws IOException {
-        for (String canonicalPath : canonicalPathList) {
+    private boolean isFileSpecifiedInPathList(File file,
+            Collection<PathWithRequiredFlags> canonicalPathList) throws IOException {
+        for (PathWithRequiredFlags canonical : canonicalPathList) {
+            String canonicalPath = canonical.getPath();
             File fileFromList = new File(canonicalPath);
             if (fileFromList.isDirectory()) {
                 if (file.isDirectory()) {
@@ -920,12 +972,14 @@
         public void doBackup(ParcelFileDescriptor oldState,
                 ParcelFileDescriptor data,
                 ParcelFileDescriptor newState,
-                long quotaBytes, int token, IBackupManager callbackBinder) throws RemoteException {
+                long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags)
+                throws RemoteException {
             // Ensure that we're running with the app's normal permission level
             long ident = Binder.clearCallingIdentity();
 
             if (DEBUG) Log.v(TAG, "doBackup() invoked");
-            BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor(), quotaBytes);
+            BackupDataOutput output = new BackupDataOutput(
+                    data.getFileDescriptor(), quotaBytes, transportFlags);
 
             try {
                 BackupAgent.this.onBackup(oldState, output, newState);
@@ -999,7 +1053,7 @@
 
         @Override
         public void doFullBackup(ParcelFileDescriptor data,
-                long quotaBytes, int token, IBackupManager callbackBinder) {
+                long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) {
             // Ensure that we're running with the app's normal permission level
             long ident = Binder.clearCallingIdentity();
 
@@ -1010,7 +1064,8 @@
             waitForSharedPrefs();
 
             try {
-                BackupAgent.this.onFullBackup(new FullBackupDataOutput(data, quotaBytes));
+                BackupAgent.this.onFullBackup(new FullBackupDataOutput(
+                        data, quotaBytes, transportFlags));
             } catch (IOException ex) {
                 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw new RuntimeException(ex);
@@ -1044,10 +1099,12 @@
             }
         }
 
-        public void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder) {
+        public void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder,
+                int transportFlags) {
             // Ensure that we're running with the app's normal permission level
             final long ident = Binder.clearCallingIdentity();
-            FullBackupDataOutput measureOutput = new FullBackupDataOutput(quotaBytes);
+            FullBackupDataOutput measureOutput =
+                    new FullBackupDataOutput(quotaBytes, transportFlags);
 
             waitForSharedPrefs();
             try {
diff --git a/android/app/backup/BackupDataOutput.java b/android/app/backup/BackupDataOutput.java
index c7586a2..5a66f34 100644
--- a/android/app/backup/BackupDataOutput.java
+++ b/android/app/backup/BackupDataOutput.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.os.ParcelFileDescriptor;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 
@@ -62,7 +63,10 @@
  * @see BackupAgent
  */
 public class BackupDataOutput {
-    final long mQuota;
+
+    private final long mQuota;
+    private final int mTransportFlags;
+
     long mBackupWriter;
 
     /**
@@ -71,14 +75,20 @@
      * @hide */
     @SystemApi
     public BackupDataOutput(FileDescriptor fd) {
-        this(fd, -1);
+        this(fd, /*quota=*/ -1, /*transportFlags=*/ 0);
     }
 
     /** @hide */
     @SystemApi
     public BackupDataOutput(FileDescriptor fd, long quota) {
+        this(fd, quota, /*transportFlags=*/ 0);
+    }
+
+    /** @hide */
+    public BackupDataOutput(FileDescriptor fd, long quota, int transportFlags) {
         if (fd == null) throw new NullPointerException();
         mQuota = quota;
+        mTransportFlags = transportFlags;
         mBackupWriter = ctor(fd);
         if (mBackupWriter == 0) {
             throw new RuntimeException("Native initialization failed with fd=" + fd);
@@ -96,6 +106,16 @@
     }
 
     /**
+     * Returns flags with additional information about the backup transport. For supported flags see
+     * {@link android.app.backup.BackupAgent}
+     *
+     * @see FullBackupDataOutput#getTransportFlags()
+     */
+    public int getTransportFlags() {
+        return mTransportFlags;
+    }
+
+    /**
      * Mark the beginning of one record in the backup data stream. This must be called before
      * {@link #writeEntityData}.
      * @param key A string key that uniquely identifies the data record within the application.
diff --git a/android/app/backup/BackupManager.java b/android/app/backup/BackupManager.java
index 12f4483..debc32b 100644
--- a/android/app/backup/BackupManager.java
+++ b/android/app/backup/BackupManager.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -252,6 +253,8 @@
     }
 
     /**
+     * @deprecated Since Android P app can no longer request restoring of its backup.
+     *
      * Restore the calling application from backup.  The data will be restored from the
      * current backup dataset if the application has stored data there, or from
      * the dataset used during the last full device setup operation if the current
@@ -269,6 +272,7 @@
      *
      * @return Zero on success; nonzero on error.
      */
+    @Deprecated
     public int requestRestore(RestoreObserver observer) {
         return requestRestore(observer, null);
     }
@@ -276,6 +280,8 @@
     // system APIs start here
 
     /**
+     * @deprecated Since Android P app can no longer request restoring of its backup.
+     *
      * Restore the calling application from backup.  The data will be restored from the
      * current backup dataset if the application has stored data there, or from
      * the dataset used during the last full device setup operation if the current
@@ -298,28 +304,12 @@
      *
      * @hide
      */
+    @Deprecated
     @SystemApi
     public int requestRestore(RestoreObserver observer, BackupManagerMonitor monitor) {
-        int result = -1;
-        checkServiceBinder();
-        if (sService != null) {
-            RestoreSession session = null;
-            try {
-                IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(),
-                    null);
-                if (binder != null) {
-                    session = new RestoreSession(mContext, binder);
-                    result = session.restorePackage(mContext.getPackageName(), observer, monitor);
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "restoreSelf() unable to contact service");
-            } finally {
-                if (session != null) {
-                    session.endRestoreSession();
-                }
-            }
-        }
-        return result;
+        Log.w(TAG, "requestRestore(): Since Android P app can no longer request restoring"
+                + " of its backup.");
+        return -1;
     }
 
     /**
@@ -726,6 +716,92 @@
         }
     }
 
+    /**
+     * Returns an {@link Intent} for the specified transport's configuration UI.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public Intent getConfigurationIntent(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getConfigurationIntent(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getConfigurationIntent() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a {@link String} describing where the specified transport is sending data.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public String getDestinationString(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getDestinationString(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getDestinationString() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an {@link Intent} for the specified transport's data management UI.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public Intent getDataManagementIntent(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getDataManagementIntent(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getDataManagementIntent() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a {@link String} describing what the specified transport's data management intent is
+     * used for.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     *
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public String getDataManagementLabel(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getDataManagementLabel(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getDataManagementLabel() couldn't connect");
+            }
+        }
+        return null;
+    }
+
     /*
      * We wrap incoming binder calls with a private class implementation that
      * redirects them into main-thread actions.  This serializes the backup
diff --git a/android/app/backup/BackupManagerMonitor.java b/android/app/backup/BackupManagerMonitor.java
index a91aded..07e7688 100644
--- a/android/app/backup/BackupManagerMonitor.java
+++ b/android/app/backup/BackupManagerMonitor.java
@@ -174,7 +174,6 @@
 
     /**
      * The transport returned {@link BackupTransport#TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED}.
-     * @hide
      */
     public static final int LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED = 51;
 
diff --git a/android/app/backup/BackupTransport.java b/android/app/backup/BackupTransport.java
index 266f58d..0963594 100644
--- a/android/app/backup/BackupTransport.java
+++ b/android/app/backup/BackupTransport.java
@@ -60,8 +60,6 @@
      *
      * <p>This is only valid when backup manager called {@link
      * #performBackup(PackageInfo, ParcelFileDescriptor, int)} with {@link #FLAG_INCREMENTAL}.
-     *
-     * @hide
      */
     public static final int TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED = -1006;
 
@@ -73,7 +71,7 @@
      * For key value backup, indicates that the backup data is a diff from a previous backup. The
      * transport must apply this diff to an existing backup to build the new backup set.
      *
-     * @hide
+     * @see #performBackup(PackageInfo, ParcelFileDescriptor, int)
      */
     public static final int FLAG_INCREMENTAL = 1 << 1;
 
@@ -81,10 +79,17 @@
      * For key value backup, indicates that the backup data is a complete set, not a diff from a
      * previous backup. The transport should clear any previous backup when storing this backup.
      *
-     * @hide
+     * @see #performBackup(PackageInfo, ParcelFileDescriptor, int)
      */
     public static final int FLAG_NON_INCREMENTAL = 1 << 2;
 
+    /**
+     * Used as a boolean extra in the binding intent of transports. We pass {@code true} to
+     * notify transports that the current connection is used for registering the transport.
+     */
+    public static final String EXTRA_TRANSPORT_REGISTRATION =
+            "android.app.backup.extra.TRANSPORT_REGISTRATION";
+
     IBackupTransport mBinderImpl = new TransportImpl();
 
     public IBinder getBinder() {
@@ -609,6 +614,15 @@
     }
 
     /**
+     * Returns flags with additional information about the transport, which is accessible to the
+     * {@link android.app.backup.BackupAgent}. This allows the agent to decide what to do based on
+     * properties of the transport.
+     */
+    public int getTransportFlags() {
+        return 0;
+    }
+
+    /**
      * Bridge between the actual IBackupTransport implementation and the stable API.  If the
      * binder interface needs to change, we use this layer to translate so that we can
      * (if appropriate) decouple those framework-side changes from the BackupTransport
@@ -740,6 +754,11 @@
         }
 
         @Override
+        public int getTransportFlags() {
+            return BackupTransport.this.getTransportFlags();
+        }
+
+        @Override
         public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
             return BackupTransport.this.getNextFullRestoreDataChunk(socket);
         }
diff --git a/android/app/backup/FullBackup.java b/android/app/backup/FullBackup.java
index a5dd5bd..b7a8da5 100644
--- a/android/app/backup/FullBackup.java
+++ b/android/app/backup/FullBackup.java
@@ -82,6 +82,11 @@
     public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
     public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken";
 
+    public static final String FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION = "clientSideEncryption";
+    public static final String FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER = "deviceToDeviceTransfer";
+    public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION =
+            "fakeClientSideEncryption";
+
     /**
      * @hide
      */
@@ -224,6 +229,9 @@
 
         private final File EXTERNAL_DIR;
 
+        private final static String TAG_INCLUDE = "include";
+        private final static String TAG_EXCLUDE = "exclude";
+
         final int mFullBackupContent;
         final PackageManager mPackageManager;
         final StorageManager mStorageManager;
@@ -303,15 +311,45 @@
         }
 
         /**
-        * A map of domain -> list of canonical file names in that domain that are to be included.
-        * We keep track of the domain so that we can go through the file system in order later on.
-        */
-        Map<String, Set<String>> mIncludes;
-        /**e
-         * List that will be populated with the canonical names of each file or directory that is
-         * to be excluded.
+         * Represents a path attribute specified in an <include /> rule along with optional
+         * transport flags required from the transport to include file(s) under that path as
+         * specified by requiredFlags attribute. If optional requiredFlags attribute is not
+         * provided, default requiredFlags to 0.
+         * Note: since our parsing codepaths were the same for <include /> and <exclude /> tags,
+         * this structure is also used for <exclude /> tags to preserve that, however you can expect
+         * the getRequiredFlags() to always return 0 for exclude rules.
          */
-        ArraySet<String> mExcludes;
+        public static class PathWithRequiredFlags {
+            private final String mPath;
+            private final int mRequiredFlags;
+
+            public PathWithRequiredFlags(String path, int requiredFlags) {
+                mPath = path;
+                mRequiredFlags = requiredFlags;
+            }
+
+            public String getPath() {
+                return mPath;
+            }
+
+            public int getRequiredFlags() {
+                return mRequiredFlags;
+            }
+        }
+
+        /**
+         * A map of domain -> set of pairs (canonical file; required transport flags) in that
+         * domain that are to be included if the transport has decared the required flags.
+         * We keep track of the domain so that we can go through the file system in order later on.
+         */
+        Map<String, Set<PathWithRequiredFlags>> mIncludes;
+
+        /**
+         * Set that will be populated with pairs (canonical file; requiredFlags=0) for each file or
+         * directory that is to be excluded. Note that for excludes, the requiredFlags attribute is
+         * ignored and the value should be always set to 0.
+         */
+        ArraySet<PathWithRequiredFlags> mExcludes;
 
         BackupScheme(Context context) {
             mFullBackupContent = context.getApplicationInfo().fullBackupContent;
@@ -356,13 +394,14 @@
         }
 
         /**
-         * @return A mapping of domain -> canonical paths within that domain. Each of these paths
-         * specifies a file that the client has explicitly included in their backup set. If this
-         * map is empty we will back up the entire data directory (including managed external
-         * storage).
+         * @return A mapping of domain -> set of pairs (canonical file; required transport flags)
+         * in that domain that are to be included if the transport has decared the required flags.
+         * Each of these paths specifies a file that the client has explicitly included in their
+         * backup set. If this map is empty we will back up the entire data directory (including
+         * managed external storage).
          */
-        public synchronized Map<String, Set<String>> maybeParseAndGetCanonicalIncludePaths()
-                throws IOException, XmlPullParserException {
+        public synchronized Map<String, Set<PathWithRequiredFlags>>
+                maybeParseAndGetCanonicalIncludePaths() throws IOException, XmlPullParserException {
             if (mIncludes == null) {
                 maybeParseBackupSchemeLocked();
             }
@@ -370,9 +409,10 @@
         }
 
         /**
-         * @return A set of canonical paths that are to be excluded from the backup/restore set.
+         * @return A set of (canonical paths; requiredFlags=0) that are to be excluded from the
+         * backup/restore set.
          */
-        public synchronized ArraySet<String> maybeParseAndGetCanonicalExcludePaths()
+        public synchronized ArraySet<PathWithRequiredFlags> maybeParseAndGetCanonicalExcludePaths()
                 throws IOException, XmlPullParserException {
             if (mExcludes == null) {
                 maybeParseBackupSchemeLocked();
@@ -382,8 +422,8 @@
 
         private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException {
             // This not being null is how we know that we've tried to parse the xml already.
-            mIncludes = new ArrayMap<String, Set<String>>();
-            mExcludes = new ArraySet<String>();
+            mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>();
+            mExcludes = new ArraySet<PathWithRequiredFlags>();
 
             if (mFullBackupContent == 0) {
                 // android:fullBackupContent="true" which means that we'll do everything.
@@ -415,8 +455,8 @@
 
         @VisibleForTesting
         public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
-                                                   Set<String> excludes,
-                                                   Map<String, Set<String>> includes)
+                                                   Set<PathWithRequiredFlags> excludes,
+                                                   Map<String, Set<PathWithRequiredFlags>> includes)
                 throws IOException, XmlPullParserException {
             int event = parser.getEventType(); // START_DOCUMENT
             while (event != XmlPullParser.START_TAG) {
@@ -441,8 +481,7 @@
                     case XmlPullParser.START_TAG:
                         validateInnerTagContents(parser);
                         final String domainFromXml = parser.getAttributeValue(null, "domain");
-                        final File domainDirectory =
-                                getDirectoryForCriteriaDomain(domainFromXml);
+                        final File domainDirectory = getDirectoryForCriteriaDomain(domainFromXml);
                         if (domainDirectory == null) {
                             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                                 Log.v(TAG_XML_PARSER, "...parsing \"" + parser.getName() + "\": "
@@ -457,12 +496,23 @@
                             break;
                         }
 
-                        Set<String> activeSet = parseCurrentTagForDomain(
+                        int requiredFlags = 0; // no transport flags are required by default
+                        if (TAG_INCLUDE.equals(parser.getName())) {
+                            // requiredFlags are only supported for <include /> tag, for <exclude />
+                            // we should always leave them as the default = 0
+                            requiredFlags = getRequiredFlagsFromString(
+                                    parser.getAttributeValue(null, "requireFlags"));
+                        }
+
+                        // retrieve the include/exclude set we'll be adding this rule to
+                        Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
                                 parser, excludes, includes, domainFromXml);
-                        activeSet.add(canonicalFile.getCanonicalPath());
+                        activeSet.add(new PathWithRequiredFlags(canonicalFile.getCanonicalPath(),
+                                requiredFlags));
                         if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                             Log.v(TAG_XML_PARSER, "...parsed " + canonicalFile.getCanonicalPath()
-                                    + " for domain \"" + domainFromXml + "\"");
+                                    + " for domain \"" + domainFromXml + "\", requiredFlags + \""
+                                    + requiredFlags + "\"");
                         }
 
                         // Special case journal files (not dirs) for sqlite database. frowny-face.
@@ -472,14 +522,16 @@
                         if ("database".equals(domainFromXml) && !canonicalFile.isDirectory()) {
                             final String canonicalJournalPath =
                                     canonicalFile.getCanonicalPath() + "-journal";
-                            activeSet.add(canonicalJournalPath);
+                            activeSet.add(new PathWithRequiredFlags(canonicalJournalPath,
+                                    requiredFlags));
                             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                                 Log.v(TAG_XML_PARSER, "...automatically generated "
                                         + canonicalJournalPath + ". Ignore if nonexistent.");
                             }
                             final String canonicalWalPath =
                                     canonicalFile.getCanonicalPath() + "-wal";
-                            activeSet.add(canonicalWalPath);
+                            activeSet.add(new PathWithRequiredFlags(canonicalWalPath,
+                                    requiredFlags));
                             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                                 Log.v(TAG_XML_PARSER, "...automatically generated "
                                         + canonicalWalPath + ". Ignore if nonexistent.");
@@ -491,7 +543,8 @@
                             !canonicalFile.getCanonicalPath().endsWith(".xml")) {
                             final String canonicalXmlPath =
                                     canonicalFile.getCanonicalPath() + ".xml";
-                            activeSet.add(canonicalXmlPath);
+                            activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
+                                    requiredFlags));
                             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                                 Log.v(TAG_XML_PARSER, "...automatically generated "
                                         + canonicalXmlPath + ". Ignore if nonexistent.");
@@ -508,10 +561,12 @@
                     Log.v(TAG_XML_PARSER, "  ...nothing specified (This means the entirety of app"
                             + " data minus excludes)");
                 } else {
-                    for (Map.Entry<String, Set<String>> entry : includes.entrySet()) {
+                    for (Map.Entry<String, Set<PathWithRequiredFlags>> entry
+                            : includes.entrySet()) {
                         Log.v(TAG_XML_PARSER, "  domain=" + entry.getKey());
-                        for (String includeData : entry.getValue()) {
-                            Log.v(TAG_XML_PARSER, "  " + includeData);
+                        for (PathWithRequiredFlags includeData : entry.getValue()) {
+                            Log.v(TAG_XML_PARSER, " path: " + includeData.getPath()
+                                    + " requiredFlags: " + includeData.getRequiredFlags());
                         }
                     }
                 }
@@ -520,8 +575,9 @@
                 if (excludes.isEmpty()) {
                     Log.v(TAG_XML_PARSER, "  ...nothing to exclude.");
                 } else {
-                    for (String excludeData : excludes) {
-                        Log.v(TAG_XML_PARSER, "  " + excludeData);
+                    for (PathWithRequiredFlags excludeData : excludes) {
+                        Log.v(TAG_XML_PARSER, " path: " + excludeData.getPath()
+                                + " requiredFlags: " + excludeData.getRequiredFlags());
                     }
                 }
 
@@ -531,20 +587,43 @@
             }
         }
 
-        private Set<String> parseCurrentTagForDomain(XmlPullParser parser,
-                                                     Set<String> excludes,
-                                                     Map<String, Set<String>> includes,
-                                                     String domain)
+        private int getRequiredFlagsFromString(String requiredFlags) {
+            int flags = 0;
+            if (requiredFlags == null || requiredFlags.length() == 0) {
+                // requiredFlags attribute was missing or empty in <include /> tag
+                return flags;
+            }
+            String[] flagsStr = requiredFlags.split("\\|");
+            for (String f : flagsStr) {
+                switch (f) {
+                    case FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION:
+                        flags |= BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
+                        break;
+                    case FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER:
+                        flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER;
+                        break;
+                    case FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION:
+                        flags |= BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED;
+                    default:
+                        Log.w(TAG, "Unrecognized requiredFlag provided, value: \"" + f + "\"");
+                }
+            }
+            return flags;
+        }
+
+        private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
+                Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes, String domain)
                 throws XmlPullParserException {
-            if ("include".equals(parser.getName())) {
+            if (TAG_INCLUDE.equals(parser.getName())) {
                 final String domainToken = getTokenForXmlDomain(domain);
-                Set<String> includeSet = includes.get(domainToken);
+                Set<PathWithRequiredFlags> includeSet = includes.get(domainToken);
                 if (includeSet == null) {
-                    includeSet = new ArraySet<String>();
+                    includeSet = new ArraySet<PathWithRequiredFlags>();
                     includes.put(domainToken, includeSet);
                 }
                 return includeSet;
-            } else if ("exclude".equals(parser.getName())) {
+            } else if (TAG_EXCLUDE.equals(parser.getName())) {
                 return excludes;
             } else {
                 // Unrecognised tag => hard failure.
@@ -589,8 +668,8 @@
         /**
          *
          * @param domain Directory where the specified file should exist. Not null.
-         * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may be
-         *                        null.
+         * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may
+         *                        be null.
          * @return The canonical path of the file specified or null if no such file exists.
          */
         private File extractCanonicalFile(File domain, String filePathFromXml) {
@@ -650,15 +729,27 @@
          * Let's be strict about the type of xml the client can write. If we see anything untoward,
          * throw an XmlPullParserException.
          */
-        private void validateInnerTagContents(XmlPullParser parser)
-                throws XmlPullParserException {
-            if (parser.getAttributeCount() > 2) {
-                throw new XmlPullParserException("At most 2 tag attributes allowed for \""
-                        + parser.getName() + "\" tag (\"domain\" & \"path\".");
+        private void validateInnerTagContents(XmlPullParser parser) throws XmlPullParserException {
+            if (parser == null) {
+                return;
             }
-            if (!"include".equals(parser.getName()) && !"exclude".equals(parser.getName())) {
-                throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" +
-                        " \"<exclude/>. You provided \"" + parser.getName() + "\"");
+            switch (parser.getName()) {
+                case TAG_INCLUDE:
+                    if (parser.getAttributeCount() > 3) {
+                        throw new XmlPullParserException("At most 3 tag attributes allowed for "
+                                + "\"include\" tag (\"domain\" & \"path\""
+                                + " & optional \"requiredFlags\").");
+                    }
+                    break;
+                case TAG_EXCLUDE:
+                    if (parser.getAttributeCount() > 2) {
+                        throw new XmlPullParserException("At most 2 tag attributes allowed for "
+                                + "\"exclude\" tag (\"domain\" & \"path\".");
+                    }
+                    break;
+                default:
+                    throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" +
+                            " \"<exclude/>. You provided \"" + parser.getName() + "\"");
             }
         }
     }
diff --git a/android/app/backup/FullBackupDataOutput.java b/android/app/backup/FullBackupDataOutput.java
index 5deedd0..18f4283 100644
--- a/android/app/backup/FullBackupDataOutput.java
+++ b/android/app/backup/FullBackupDataOutput.java
@@ -11,6 +11,7 @@
     // Currently a name-scoping shim around BackupDataOutput
     private final BackupDataOutput mData;
     private final long mQuota;
+    private final int mTransportFlags;
     private long mSize;
 
     /**
@@ -23,22 +24,49 @@
         return mQuota;
     }
 
+    /**
+     * Returns flags with additional information about the backup transport. For supported flags see
+     * {@link android.app.backup.BackupAgent}
+     *
+     * @see BackupDataOutput#getTransportFlags()
+     */
+    public int getTransportFlags() {
+        return mTransportFlags;
+    }
+
     /** @hide - used only in measure operation */
     public FullBackupDataOutput(long quota) {
         mData = null;
         mQuota = quota;
         mSize = 0;
+        mTransportFlags = 0;
+    }
+
+    /** @hide - used only in measure operation */
+    public FullBackupDataOutput(long quota, int transportFlags) {
+        mData = null;
+        mQuota = quota;
+        mSize = 0;
+        mTransportFlags = transportFlags;
     }
 
     /** @hide */
     public FullBackupDataOutput(ParcelFileDescriptor fd, long quota) {
-        mData = new BackupDataOutput(fd.getFileDescriptor(), quota);
+        mData = new BackupDataOutput(fd.getFileDescriptor(), quota, 0);
         mQuota = quota;
+        mTransportFlags = 0;
+    }
+
+    /** @hide */
+    public FullBackupDataOutput(ParcelFileDescriptor fd, long quota, int transportFlags) {
+        mData = new BackupDataOutput(fd.getFileDescriptor(), quota, transportFlags);
+        mQuota = quota;
+        mTransportFlags = transportFlags;
     }
 
     /** @hide - used only internally to the backup manager service's stream construction */
     public FullBackupDataOutput(ParcelFileDescriptor fd) {
-        this(fd, -1);
+        this(fd, /*quota=*/ -1, /*transportFlags=*/ 0);
     }
 
     /** @hide */
diff --git a/android/app/job/JobInfo.java b/android/app/job/JobInfo.java
index cba9dcc..02afcc7 100644
--- a/android/app/job/JobInfo.java
+++ b/android/app/job/JobInfo.java
@@ -65,7 +65,6 @@
             NETWORK_TYPE_UNMETERED,
             NETWORK_TYPE_NOT_ROAMING,
             NETWORK_TYPE_CELLULAR,
-            NETWORK_TYPE_METERED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkType {}
@@ -253,7 +252,15 @@
     /**
      * @hide
      */
-    public static final int FLAG_IS_PREFETCH = 1 << 2;
+    public static final int FLAG_PREFETCH = 1 << 2;
+
+    /**
+     * This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
+     * can set it. Jobs with a time constrant must not have it.
+     *
+     * @hide
+     */
+    public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3;
 
     /**
      * @hide
@@ -288,7 +295,8 @@
     private final boolean hasEarlyConstraint;
     private final boolean hasLateConstraint;
     private final NetworkRequest networkRequest;
-    private final long networkBytes;
+    private final long networkDownloadBytes;
+    private final long networkUploadBytes;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
     private final boolean isPeriodic;
@@ -309,30 +317,28 @@
     }
 
     /**
-     * Bundle of extras which are returned to your application at execution time.
+     * @see JobInfo.Builder#setExtras(PersistableBundle)
      */
     public @NonNull PersistableBundle getExtras() {
         return extras;
     }
 
     /**
-     * Bundle of transient extras which are returned to your application at execution time,
-     * but not persisted by the system.
+     * @see JobInfo.Builder#setTransientExtras(Bundle)
      */
     public @NonNull Bundle getTransientExtras() {
         return transientExtras;
     }
 
     /**
-     * ClipData of information that is returned to your application at execution time,
-     * but not persisted by the system.
+     * @see JobInfo.Builder#setClipData(ClipData, int)
      */
     public @Nullable ClipData getClipData() {
         return clipData;
     }
 
     /**
-     * Permission grants that go along with {@link #getClipData}.
+     * @see JobInfo.Builder#setClipData(ClipData, int)
      */
     public int getClipGrantFlags() {
         return clipGrantFlags;
@@ -355,33 +361,34 @@
         return flags;
     }
 
+    /** @hide */
+    public boolean isExemptedFromAppStandby() {
+        return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic();
+    }
+
     /**
-     * Whether this job requires that the device be charging (or be a non-battery-powered
-     * device connected to permanent power, such as Android TV devices).
+     * @see JobInfo.Builder#setRequiresCharging(boolean)
      */
     public boolean isRequireCharging() {
         return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
     }
 
     /**
-     * Whether this job needs the device's battery level to not be at below the critical threshold.
+     * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
      */
     public boolean isRequireBatteryNotLow() {
         return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
     }
 
     /**
-     * Whether this job requires that the user <em>not</em> be interacting with the device.
-     *
-     * <p class="note">This is <em>not</em> the same as "doze" or "device idle";
-     * it is purely about the user's direct interactions.</p>
+     * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
      */
     public boolean isRequireDeviceIdle() {
         return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
     }
 
     /**
-     * Whether this job needs the device's storage to not be low.
+     * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
      */
     public boolean isRequireStorageNotLow() {
         return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
@@ -397,6 +404,7 @@
     /**
      * Which content: URIs must change for the job to be scheduled.  Returns null
      * if there are none required.
+     * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
      */
     public @Nullable TriggerContentUri[] getTriggerContentUris() {
         return triggerContentUris;
@@ -405,6 +413,7 @@
     /**
      * When triggering on content URI changes, this is the delay from when a change
      * is detected until the job is scheduled.
+     * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
      */
     public long getTriggerContentUpdateDelay() {
         return triggerContentUpdateDelay;
@@ -413,6 +422,7 @@
     /**
      * When triggering on content URI changes, this is the maximum delay we will
      * use before scheduling the job.
+     * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
      */
     public long getTriggerContentMaxDelay() {
         return triggerContentMaxDelay;
@@ -453,28 +463,59 @@
     }
 
     /**
-     * Return the estimated size of network traffic that will be performed by
+     * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+     *             {@link #getEstimatedNetworkUploadBytes()}.
+     * @removed
+     */
+    @Deprecated
+    public @BytesLong long getEstimatedNetworkBytes() {
+        if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN
+                && networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return NETWORK_BYTES_UNKNOWN;
+        } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+            return networkUploadBytes;
+        } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return networkDownloadBytes;
+        } else {
+            return networkDownloadBytes + networkUploadBytes;
+        }
+    }
+
+    /**
+     * Return the estimated size of download traffic that will be performed by
      * this job, in bytes.
      *
-     * @return Estimated size of network traffic, or
+     * @return Estimated size of download traffic, or
      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
-     * @see Builder#setEstimatedNetworkBytes(long)
+     * @see Builder#setEstimatedNetworkBytes(long, long)
      */
-    public @BytesLong long getEstimatedNetworkBytes() {
-        return networkBytes;
+    public @BytesLong long getEstimatedNetworkDownloadBytes() {
+        return networkDownloadBytes;
+    }
+
+    /**
+     * Return the estimated size of upload traffic that will be performed by
+     * this job, in bytes.
+     *
+     * @return Estimated size of upload traffic, or
+     *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+     * @see Builder#setEstimatedNetworkBytes(long, long)
+     */
+    public @BytesLong long getEstimatedNetworkUploadBytes() {
+        return networkUploadBytes;
     }
 
     /**
      * Set for a job that does not recur periodically, to specify a delay after which the job
      * will be eligible for execution. This value is not set if the job recurs periodically.
+     * @see JobInfo.Builder#setMinimumLatency(long)
      */
     public long getMinLatencyMillis() {
         return minLatencyMillis;
     }
 
     /**
-     * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
-     * periodically.
+     * @see JobInfo.Builder#setOverrideDeadline(long)
      */
     public long getMaxExecutionDelayMillis() {
         return maxExecutionDelayMillis;
@@ -482,13 +523,15 @@
 
     /**
      * Track whether this job will repeat with a given period.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public boolean isPeriodic() {
         return isPeriodic;
     }
 
     /**
-     * @return Whether or not this job should be persisted across device reboots.
+     * @see JobInfo.Builder#setPersisted(boolean)
      */
     public boolean isPersisted() {
         return isPersisted;
@@ -497,6 +540,8 @@
     /**
      * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
      * job does not recur periodically.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public long getIntervalMillis() {
         return intervalMillis;
@@ -505,6 +550,8 @@
     /**
      * Flex time for this job. Only valid if this is a periodic job.  The job can
      * execute at any time in a window of flex length at the end of the period.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public long getFlexMillis() {
         return flexMillis;
@@ -514,6 +561,7 @@
      * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
      * will be increased depending on the backoff policy specified at job creation time. Defaults
      * to 30 seconds, minimum is currently 10 seconds.
+     * @see JobInfo.Builder#setBackoffCriteria(long, int)
      */
     public long getInitialBackoffMillis() {
         return initialBackoffMillis;
@@ -521,12 +569,27 @@
 
     /**
      * Return the backoff policy of this job.
+     * @see JobInfo.Builder#setBackoffCriteria(long, int)
      */
     public @BackoffPolicy int getBackoffPolicy() {
         return backoffPolicy;
     }
 
     /**
+     * @see JobInfo.Builder#setImportantWhileForeground(boolean)
+     */
+    public boolean isImportantWhileForeground() {
+        return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
+    }
+
+    /**
+     * @see JobInfo.Builder#setPrefetch(boolean)
+     */
+    public boolean isPrefetch() {
+        return (flags & FLAG_PREFETCH) != 0;
+    }
+
+    /**
      * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
      * function was called at all.
      * @hide
@@ -597,7 +660,10 @@
         if (!Objects.equals(networkRequest, j.networkRequest)) {
             return false;
         }
-        if (networkBytes != j.networkBytes) {
+        if (networkDownloadBytes != j.networkDownloadBytes) {
+            return false;
+        }
+        if (networkUploadBytes != j.networkUploadBytes) {
             return false;
         }
         if (minLatencyMillis != j.minLatencyMillis) {
@@ -660,7 +726,8 @@
         if (networkRequest != null) {
             hashCode = 31 * hashCode + networkRequest.hashCode();
         }
-        hashCode = 31 * hashCode + Long.hashCode(networkBytes);
+        hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
+        hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -695,7 +762,8 @@
         } else {
             networkRequest = null;
         }
-        networkBytes = in.readLong();
+        networkDownloadBytes = in.readLong();
+        networkUploadBytes = in.readLong();
         minLatencyMillis = in.readLong();
         maxExecutionDelayMillis = in.readLong();
         isPeriodic = in.readInt() == 1;
@@ -724,7 +792,8 @@
         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
         networkRequest = b.mNetworkRequest;
-        networkBytes = b.mNetworkBytes;
+        networkDownloadBytes = b.mNetworkDownloadBytes;
+        networkUploadBytes = b.mNetworkUploadBytes;
         minLatencyMillis = b.mMinLatencyMillis;
         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
         isPeriodic = b.mIsPeriodic;
@@ -767,7 +836,8 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(networkBytes);
+        out.writeLong(networkDownloadBytes);
+        out.writeLong(networkUploadBytes);
         out.writeLong(minLatencyMillis);
         out.writeLong(maxExecutionDelayMillis);
         out.writeInt(isPeriodic ? 1 : 0);
@@ -901,7 +971,8 @@
         // Requirements.
         private int mConstraintFlags;
         private NetworkRequest mNetworkRequest;
-        private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
+        private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+        private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
         private ArrayList<TriggerContentUri> mTriggerContentUris;
         private long mTriggerContentUpdateDelay = -1;
         private long mTriggerContentMaxDelay = -1;
@@ -952,6 +1023,7 @@
         /**
          * Set optional extras. This is persisted, so we only allow primitive types.
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+         * @see JobInfo#getExtras()
          */
         public Builder setExtras(@NonNull PersistableBundle extras) {
             mExtras = extras;
@@ -966,6 +1038,7 @@
          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
          *
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+         * @see JobInfo#getTransientExtras()
          */
         public Builder setTransientExtras(@NonNull Bundle extras) {
             mTransientExtras = extras;
@@ -993,6 +1066,8 @@
          * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION},
          * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
          * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
+         * @see JobInfo#getClipData()
+         * @see JobInfo#getClipGrantFlags()
          */
         public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
             mClipData = clip;
@@ -1083,6 +1158,16 @@
         }
 
         /**
+         * @deprecated replaced by
+         *             {@link #setEstimatedNetworkBytes(long, long)}.
+         * @removed
+         */
+        @Deprecated
+        public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
+            return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN);
+        }
+
+        /**
          * Set the estimated size of network traffic that will be performed by
          * this job, in bytes.
          * <p>
@@ -1099,23 +1184,30 @@
          * <li>A job that synchronizes email could end up using an extreme range
          * of data, from under 1KB when nothing has changed, to dozens of MB
          * when there are new emails with attachments. Jobs that cannot provide
-         * reasonable estimates should leave this estimated value undefined.
+         * reasonable estimates should use the sentinel value
+         * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
          * </ul>
          * Note that the system may choose to delay jobs with large network
          * usage estimates when the device has a poor network connection, in
          * order to save battery.
+         * <p>
+         * The values provided here only reflect the traffic that will be
+         * performed by the base job; if you're using {@link JobWorkItem} then
+         * you also need to define the network traffic used by each work item
+         * when constructing them.
          *
-         * @param networkBytes The estimated size of network traffic that will
-         *            be performed by this job, in bytes. This value only
-         *            reflects the traffic that will be performed by the base
-         *            job; if you're using {@link JobWorkItem} then you also
-         *            need to define the network traffic used by each work item
-         *            when constructing them.
-         * @see JobInfo#getEstimatedNetworkBytes()
-         * @see JobWorkItem#JobWorkItem(android.content.Intent, long)
+         * @param downloadBytes The estimated size of network traffic that will
+         *            be downloaded by this job, in bytes.
+         * @param uploadBytes The estimated size of network traffic that will be
+         *            uploaded by this job, in bytes.
+         * @see JobInfo#getEstimatedNetworkDownloadBytes()
+         * @see JobInfo#getEstimatedNetworkUploadBytes()
+         * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
          */
-        public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
-            mNetworkBytes = networkBytes;
+        public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
+                @BytesLong long uploadBytes) {
+            mNetworkDownloadBytes = downloadBytes;
+            mNetworkUploadBytes = uploadBytes;
             return this;
         }
 
@@ -1133,6 +1225,7 @@
          *
          * @param requiresCharging Pass {@code true} to require that the device be
          *     charging in order to run the job.
+         * @see JobInfo#isRequireCharging()
          */
         public Builder setRequiresCharging(boolean requiresCharging) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
@@ -1146,6 +1239,7 @@
          * is not low, which is generally the point where the user is given a "low battery"
          * warning.
          * @param batteryNotLow Whether or not the device's battery level must not be low.
+         * @see JobInfo#isRequireBatteryNotLow()
          */
         public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
@@ -1170,6 +1264,7 @@
          *
          * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
          *     while the device is being used interactively.
+         * @see JobInfo#isRequireDeviceIdle()
          */
         public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
@@ -1183,6 +1278,7 @@
          * in a low storage state, which is generally the point where the user is given a
          * "low storage" warning.
          * @param storageNotLow Whether or not the device's available storage must not be low.
+         * @see JobInfo#isRequireStorageNotLow()
          */
         public Builder setRequiresStorageNotLow(boolean storageNotLow) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
@@ -1215,6 +1311,7 @@
          *      job}
          *
          * @param uri The content: URI to monitor.
+         * @see JobInfo#getTriggerContentUris()
          */
         public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
             if (mTriggerContentUris == null) {
@@ -1229,6 +1326,7 @@
          * the job is scheduled.  If there are more changes during that time, the delay
          * will be reset to start at the time of the most recent change.
          * @param durationMs Delay after most recent content change, in milliseconds.
+         * @see JobInfo#getTriggerContentUpdateDelay()
          */
         public Builder setTriggerContentUpdateDelay(long durationMs) {
             mTriggerContentUpdateDelay = durationMs;
@@ -1239,6 +1337,7 @@
          * Set the maximum total delay (in milliseconds) that is allowed from the first
          * time a content change is detected until the job is scheduled.
          * @param durationMs Delay after initial content change, in milliseconds.
+         * @see JobInfo#getTriggerContentMaxDelay()
          */
         public Builder setTriggerContentMaxDelay(long durationMs) {
             mTriggerContentMaxDelay = durationMs;
@@ -1252,6 +1351,8 @@
          * Setting this function on the builder with {@link #setMinimumLatency(long)} or
          * {@link #setOverrideDeadline(long)} will result in an error.
          * @param intervalMillis Millisecond interval for which this job will repeat.
+         * @see JobInfo#getIntervalMillis()
+         * @see JobInfo#getFlexMillis()
          */
         public Builder setPeriodic(long intervalMillis) {
             return setPeriodic(intervalMillis, intervalMillis);
@@ -1265,6 +1366,8 @@
          * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
          *                   {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
          *                   higher.
+         * @see JobInfo#getIntervalMillis()
+         * @see JobInfo#getFlexMillis()
          */
         public Builder setPeriodic(long intervalMillis, long flexMillis) {
             final long minPeriod = getMinPeriodMillis();
@@ -1296,6 +1399,7 @@
          * {@link android.app.job.JobInfo.Builder#build()} is called.
          * @param minLatencyMillis Milliseconds before which this job will not be considered for
          *                         execution.
+         * @see JobInfo#getMinLatencyMillis()
          */
         public Builder setMinimumLatency(long minLatencyMillis) {
             mMinLatencyMillis = minLatencyMillis;
@@ -1309,6 +1413,7 @@
          * this property on a periodic job, doing so will throw an
          * {@link java.lang.IllegalArgumentException} when
          * {@link android.app.job.JobInfo.Builder#build()} is called.
+         * @see JobInfo#getMaxExecutionDelayMillis()
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
@@ -1328,6 +1433,8 @@
          * mode.
          * @param initialBackoffMillis Millisecond time interval to wait initially when job has
          *                             failed.
+         * @see JobInfo#getInitialBackoffMillis()
+         * @see JobInfo#getBackoffPolicy()
          */
         public Builder setBackoffCriteria(long initialBackoffMillis,
                 @BackoffPolicy int backoffPolicy) {
@@ -1358,6 +1465,7 @@
          *
          * @param importantWhileForeground whether to relax doze restrictions for this job when the
          *                                 app is in the foreground. False by default.
+         * @see JobInfo#isImportantWhileForeground()
          */
         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
             if (importantWhileForeground) {
@@ -1369,6 +1477,15 @@
         }
 
         /**
+         * @removed
+         * @deprecated replaced with {@link #setPrefetch(boolean)}
+         */
+        @Deprecated
+        public Builder setIsPrefetch(boolean isPrefetch) {
+            return setPrefetch(isPrefetch);
+        }
+
+        /**
          * Setting this to true indicates that this job is designed to prefetch
          * content that will make a material improvement to the experience of
          * the specific user of this device. For example, fetching top headlines
@@ -1380,12 +1497,13 @@
          * network when there is a surplus of metered data available. The system
          * may also use this signal in combination with end user usage patterns
          * to ensure data is prefetched before the user launches your app.
+         * @see JobInfo#isPrefetch()
          */
-        public Builder setIsPrefetch(boolean isPrefetch) {
-            if (isPrefetch) {
-                mFlags |= FLAG_IS_PREFETCH;
+        public Builder setPrefetch(boolean prefetch) {
+            if (prefetch) {
+                mFlags |= FLAG_PREFETCH;
             } else {
-                mFlags &= (~FLAG_IS_PREFETCH);
+                mFlags &= (~FLAG_PREFETCH);
             }
             return this;
         }
@@ -1395,6 +1513,7 @@
          *
          * @param isPersisted True to indicate that the job will be written to
          *            disk and loaded at boot.
+         * @see JobInfo#isPersisted()
          */
         @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
         public Builder setPersisted(boolean isPersisted) {
@@ -1414,7 +1533,7 @@
                         "constraints, this is not allowed.");
             }
             // Check that network estimates require network type
-            if (mNetworkBytes > 0 && mNetworkRequest == null) {
+            if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
                 throw new IllegalArgumentException(
                         "Can't provide estimated network usage without requiring a network");
             }
diff --git a/android/app/job/JobParameters.java b/android/app/job/JobParameters.java
index c71bf2e..d67f11b 100644
--- a/android/app/job/JobParameters.java
+++ b/android/app/job/JobParameters.java
@@ -36,15 +36,16 @@
 public class JobParameters implements Parcelable {
 
     /** @hide */
-    public static final int REASON_CANCELED = 0;
+    public static final int REASON_CANCELED = JobProtoEnums.STOP_REASON_CANCELLED; // 0.
     /** @hide */
-    public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+    public static final int REASON_CONSTRAINTS_NOT_SATISFIED =
+            JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; //1.
     /** @hide */
-    public static final int REASON_PREEMPT = 2;
+    public static final int REASON_PREEMPT = JobProtoEnums.STOP_REASON_PREEMPT; // 2.
     /** @hide */
-    public static final int REASON_TIMEOUT = 3;
+    public static final int REASON_TIMEOUT = JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
     /** @hide */
-    public static final int REASON_DEVICE_IDLE = 4;
+    public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
 
     /** @hide */
     public static String getReasonName(int reason) {
diff --git a/android/app/job/JobWorkItem.java b/android/app/job/JobWorkItem.java
index 1c46e8e..995f522 100644
--- a/android/app/job/JobWorkItem.java
+++ b/android/app/job/JobWorkItem.java
@@ -16,6 +16,8 @@
 
 package android.app.job;
 
+import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
+
 import android.annotation.BytesLong;
 import android.content.Intent;
 import android.os.Parcel;
@@ -28,7 +30,8 @@
  */
 final public class JobWorkItem implements Parcelable {
     final Intent mIntent;
-    final long mNetworkBytes;
+    final long mNetworkDownloadBytes;
+    final long mNetworkUploadBytes;
     int mDeliveryCount;
     int mWorkId;
     Object mGrants;
@@ -41,22 +44,36 @@
      */
     public JobWorkItem(Intent intent) {
         mIntent = intent;
-        mNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+        mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+        mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+    }
+
+    /**
+     * @deprecated replaced by {@link #JobWorkItem(Intent, long, long)}
+     * @removed
+     */
+    @Deprecated
+    public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+        this(intent, networkBytes, NETWORK_BYTES_UNKNOWN);
     }
 
     /**
      * Create a new piece of work, which can be submitted to
      * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+     * <p>
+     * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+     * details about how to estimate network traffic.
      *
      * @param intent The general Intent describing this work.
-     * @param networkBytes The estimated size of network traffic that will be
-     *            performed by this job work item, in bytes. See
-     *            {@link JobInfo.Builder#setEstimatedNetworkBytes(long)} for
-     *            details about how to estimate.
+     * @param downloadBytes The estimated size of network traffic that will be
+     *            downloaded by this job work item, in bytes.
+     * @param uploadBytes The estimated size of network traffic that will be
+     *            uploaded by this job work item, in bytes.
      */
-    public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+    public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
         mIntent = intent;
-        mNetworkBytes = networkBytes;
+        mNetworkDownloadBytes = downloadBytes;
+        mNetworkUploadBytes = uploadBytes;
     }
 
     /**
@@ -67,14 +84,44 @@
     }
 
     /**
-     * Return the estimated size of network traffic that will be performed by
+     * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+     *             {@link #getEstimatedNetworkUploadBytes()}.
+     * @removed
+     */
+    @Deprecated
+    public @BytesLong long getEstimatedNetworkBytes() {
+        if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN
+                && mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return NETWORK_BYTES_UNKNOWN;
+        } else if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+            return mNetworkUploadBytes;
+        } else if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return mNetworkDownloadBytes;
+        } else {
+            return mNetworkDownloadBytes + mNetworkUploadBytes;
+        }
+    }
+
+    /**
+     * Return the estimated size of download traffic that will be performed by
+     * this job, in bytes.
+     *
+     * @return Estimated size of download traffic, or
+     *         {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+     */
+    public @BytesLong long getEstimatedNetworkDownloadBytes() {
+        return mNetworkDownloadBytes;
+    }
+
+    /**
+     * Return the estimated size of upload traffic that will be performed by
      * this job work item, in bytes.
      *
-     * @return estimated size, or {@link JobInfo#NETWORK_BYTES_UNKNOWN} when
-     *         unknown.
+     * @return Estimated size of upload traffic, or
+     *         {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
      */
-    public @BytesLong long getEstimatedNetworkBytes() {
-        return mNetworkBytes;
+    public @BytesLong long getEstimatedNetworkUploadBytes() {
+        return mNetworkUploadBytes;
     }
 
     /**
@@ -128,9 +175,13 @@
         sb.append(mWorkId);
         sb.append(" intent=");
         sb.append(mIntent);
-        if (mNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
-            sb.append(" networkBytes=");
-            sb.append(mNetworkBytes);
+        if (mNetworkDownloadBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" downloadBytes=");
+            sb.append(mNetworkDownloadBytes);
+        }
+        if (mNetworkUploadBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" uploadBytes=");
+            sb.append(mNetworkUploadBytes);
         }
         if (mDeliveryCount != 0) {
             sb.append(" dcount=");
@@ -151,7 +202,8 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(mNetworkBytes);
+        out.writeLong(mNetworkDownloadBytes);
+        out.writeLong(mNetworkUploadBytes);
         out.writeInt(mDeliveryCount);
         out.writeInt(mWorkId);
     }
@@ -173,7 +225,8 @@
         } else {
             mIntent = null;
         }
-        mNetworkBytes = in.readLong();
+        mNetworkDownloadBytes = in.readLong();
+        mNetworkUploadBytes = in.readLong();
         mDeliveryCount = in.readInt();
         mWorkId = in.readInt();
     }
diff --git a/android/app/servertransaction/ActivityLifecycleItem.java b/android/app/servertransaction/ActivityLifecycleItem.java
index 9a50a00..7f8c50c 100644
--- a/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/android/app/servertransaction/ActivityLifecycleItem.java
@@ -91,4 +91,9 @@
         pw.println(prefix + "target state:" + getTargetState());
         pw.println(prefix + "description: " + mDescription);
     }
+
+    @Override
+    public void recycle() {
+        setDescription(null);
+    }
 }
diff --git a/android/app/servertransaction/ActivityRelaunchItem.java b/android/app/servertransaction/ActivityRelaunchItem.java
new file mode 100644
index 0000000..d8a7463
--- /dev/null
+++ b/android/app/servertransaction/ActivityRelaunchItem.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.ActivityThread.DEBUG_ORDER;
+
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.app.ResultInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.MergedConfiguration;
+import android.util.Slog;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Activity relaunch callback.
+ * @hide
+ */
+public class ActivityRelaunchItem extends ClientTransactionItem {
+
+    private static final String TAG = "ActivityRelaunchItem";
+
+    private List<ResultInfo> mPendingResults;
+    private List<ReferrerIntent> mPendingNewIntents;
+    private int mConfigChanges;
+    private MergedConfiguration mConfig;
+    private boolean mPreserveWindow;
+
+    /**
+     * A record that was properly configured for relaunch. Execution will be cancelled if not
+     * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
+     */
+    private ActivityThread.ActivityClientRecord mActivityClientRecord;
+
+    @Override
+    public void preExecute(ClientTransactionHandler client, IBinder token) {
+        mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults,
+                mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
+    }
+
+    @Override
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        if (mActivityClientRecord == null) {
+            if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
+            return;
+        }
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
+        client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        client.reportRelaunch(token, pendingActions);
+    }
+
+    // ObjectPoolItem implementation
+
+    private ActivityRelaunchItem() {}
+
+    /** Obtain an instance initialized with provided params. */
+    public static ActivityRelaunchItem obtain(List<ResultInfo> pendingResults,
+            List<ReferrerIntent> pendingNewIntents, int configChanges, MergedConfiguration config,
+            boolean preserveWindow) {
+        ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class);
+        if (instance == null) {
+            instance = new ActivityRelaunchItem();
+        }
+        instance.mPendingResults = pendingResults;
+        instance.mPendingNewIntents = pendingNewIntents;
+        instance.mConfigChanges = configChanges;
+        instance.mConfig = config;
+        instance.mPreserveWindow = preserveWindow;
+
+        return instance;
+    }
+
+    @Override
+    public void recycle() {
+        mPendingResults = null;
+        mPendingNewIntents = null;
+        mConfigChanges = 0;
+        mConfig = null;
+        mPreserveWindow = false;
+        mActivityClientRecord = null;
+        ObjectPool.recycle(this);
+    }
+
+
+    // Parcelable implementation
+
+    /** Write to Parcel. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedList(mPendingResults, flags);
+        dest.writeTypedList(mPendingNewIntents, flags);
+        dest.writeInt(mConfigChanges);
+        dest.writeTypedObject(mConfig, flags);
+        dest.writeBoolean(mPreserveWindow);
+    }
+
+    /** Read from Parcel. */
+    private ActivityRelaunchItem(Parcel in) {
+        mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
+        mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+        mConfigChanges = in.readInt();
+        mConfig = in.readTypedObject(MergedConfiguration.CREATOR);
+        mPreserveWindow = in.readBoolean();
+    }
+
+    public static final Creator<ActivityRelaunchItem> CREATOR =
+            new Creator<ActivityRelaunchItem>() {
+                public ActivityRelaunchItem createFromParcel(Parcel in) {
+                    return new ActivityRelaunchItem(in);
+                }
+
+                public ActivityRelaunchItem[] newArray(int size) {
+                    return new ActivityRelaunchItem[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final ActivityRelaunchItem other = (ActivityRelaunchItem) o;
+        return Objects.equals(mPendingResults, other.mPendingResults)
+                && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
+                && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig)
+                && mPreserveWindow == other.mPreserveWindow;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mPendingResults);
+        result = 31 * result + Objects.hashCode(mPendingNewIntents);
+        result = 31 * result + mConfigChanges;
+        result = 31 * result + Objects.hashCode(mConfig);
+        result = 31 * result + (mPreserveWindow ? 1 : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ActivityRelaunchItem{pendingResults=" + mPendingResults
+                + ",pendingNewIntents=" + mPendingNewIntents + ",configChanges="  + mConfigChanges
+                + ",config=" + mConfig + ",preserveWindow" + mPreserveWindow + "}";
+    }
+}
diff --git a/android/app/servertransaction/ActivityResultItem.java b/android/app/servertransaction/ActivityResultItem.java
index 73b5ec4..545463c 100644
--- a/android/app/servertransaction/ActivityResultItem.java
+++ b/android/app/servertransaction/ActivityResultItem.java
@@ -16,7 +16,7 @@
 
 package android.app.servertransaction;
 
-import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import android.app.ClientTransactionHandler;
@@ -38,8 +38,8 @@
     private List<ResultInfo> mResultInfoList;
 
     @Override
-    public int getPreExecutionState() {
-        return ON_PAUSE;
+    public int getPostExecutionState() {
+        return ON_RESUME;
     }
 
     @Override
diff --git a/android/app/servertransaction/ClientTransactionItem.java b/android/app/servertransaction/ClientTransactionItem.java
index 6f2cc00..d94f08b 100644
--- a/android/app/servertransaction/ClientTransactionItem.java
+++ b/android/app/servertransaction/ClientTransactionItem.java
@@ -32,12 +32,6 @@
  */
 public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
 
-    /** Get the state in which this callback can be executed. */
-    @LifecycleState
-    public int getPreExecutionState() {
-        return UNDEFINED;
-    }
-
     /** Get the state that must follow this callback. */
     @LifecycleState
     public int getPostExecutionState() {
diff --git a/android/app/servertransaction/DestroyActivityItem.java b/android/app/servertransaction/DestroyActivityItem.java
index cbcf6c7..0edcf18 100644
--- a/android/app/servertransaction/DestroyActivityItem.java
+++ b/android/app/servertransaction/DestroyActivityItem.java
@@ -37,7 +37,7 @@
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
         client.handleDestroyActivity(token, mFinished, mConfigChanges,
-                false /* getNonConfigInstance */);
+                false /* getNonConfigInstance */, getDescription());
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -65,6 +65,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mFinished = false;
         mConfigChanges = 0;
         ObjectPool.recycle(this);
diff --git a/android/app/servertransaction/NewIntentItem.java b/android/app/servertransaction/NewIntentItem.java
index 7dfde73..e5ce3b0 100644
--- a/android/app/servertransaction/NewIntentItem.java
+++ b/android/app/servertransaction/NewIntentItem.java
@@ -38,11 +38,6 @@
 
     // TODO(lifecycler): Switch new intent handling to this scheme.
     /*@Override
-    public int getPreExecutionState() {
-        return ON_PAUSE;
-    }
-
-    @Override
     public int getPostExecutionState() {
         return ON_RESUME;
     }*/
diff --git a/android/app/servertransaction/PauseActivityItem.java b/android/app/servertransaction/PauseActivityItem.java
index 70a4755..65e4291 100644
--- a/android/app/servertransaction/PauseActivityItem.java
+++ b/android/app/servertransaction/PauseActivityItem.java
@@ -42,8 +42,8 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
-        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
-                pendingActions);
+        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
+                "PAUSE_ACTIVITY_ITEM");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -102,6 +102,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mFinished = false;
         mUserLeaving = false;
         mConfigChanges = 0;
diff --git a/android/app/servertransaction/PendingTransactionActions.java b/android/app/servertransaction/PendingTransactionActions.java
index 8304c1c..af7b7a2 100644
--- a/android/app/servertransaction/PendingTransactionActions.java
+++ b/android/app/servertransaction/PendingTransactionActions.java
@@ -44,6 +44,7 @@
     private boolean mCallOnPostCreate;
     private Bundle mOldState;
     private StopInfo mStopInfo;
+    private boolean mReportRelaunchToWM;
 
     public PendingTransactionActions() {
         clear();
@@ -91,6 +92,24 @@
         mStopInfo = stopInfo;
     }
 
+    /**
+     * Check if we should report an activity relaunch to WindowManager. We report back for every
+     * relaunch request to ActivityManager, but only for those that were actually finished to we
+     * report to WindowManager.
+     */
+    public boolean shouldReportRelaunchToWindowManager() {
+        return mReportRelaunchToWM;
+    }
+
+    /**
+     * Set if we should report an activity relaunch to WindowManager. We report back for every
+     * relaunch request to ActivityManager, but only for those that were actually finished we report
+     * to WindowManager.
+     */
+    public void setReportRelaunchToWindowManager(boolean reportToWm) {
+        mReportRelaunchToWM = reportToWm;
+    }
+
     /** Reports to server about activity stop. */
     public static class StopInfo implements Runnable {
         private static final String TAG = "ActivityStopInfo";
@@ -134,7 +153,7 @@
                 Bundle.dumpStats(pw, mPersistentState);
 
                 if (ex instanceof TransactionTooLargeException
-                        && mActivity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                     Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                     return;
                 }
diff --git a/android/app/servertransaction/ResumeActivityItem.java b/android/app/servertransaction/ResumeActivityItem.java
index ed90f2c..d16bc97 100644
--- a/android/app/servertransaction/ResumeActivityItem.java
+++ b/android/app/servertransaction/ResumeActivityItem.java
@@ -48,7 +48,8 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
-        client.handleResumeActivity(token, true /* clearHide */, mIsForward, "RESUME_ACTIVITY");
+        client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
+                "RESUME_ACTIVITY");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -101,6 +102,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
         mUpdateProcState = false;
         mIsForward = false;
diff --git a/android/app/servertransaction/StopActivityItem.java b/android/app/servertransaction/StopActivityItem.java
index b814d1a..8db38d3 100644
--- a/android/app/servertransaction/StopActivityItem.java
+++ b/android/app/servertransaction/StopActivityItem.java
@@ -38,7 +38,8 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
-        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions);
+        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
+                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -72,6 +73,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mShowWindow = false;
         mConfigChanges = 0;
         ObjectPool.recycle(this);
diff --git a/android/app/servertransaction/TransactionExecutor.java b/android/app/servertransaction/TransactionExecutor.java
index 78b393a..553c3ae 100644
--- a/android/app/servertransaction/TransactionExecutor.java
+++ b/android/app/servertransaction/TransactionExecutor.java
@@ -24,6 +24,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
 
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
@@ -48,11 +49,7 @@
 
     private ClientTransactionHandler mTransactionHandler;
     private PendingTransactionActions mPendingActions = new PendingTransactionActions();
-
-    // Temp holder for lifecycle path.
-    // No direct transition between two states should take more than one complete cycle of 6 states.
-    @ActivityLifecycleItem.LifecycleState
-    private IntArray mLifecycleSequence = new IntArray(6);
+    private TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
 
     /** Initialize an instance with transaction handler, that will execute all requested actions. */
     public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
@@ -89,13 +86,25 @@
 
         final IBinder token = transaction.getActivityToken();
         ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
+        // In case when post-execution state of the last callback matches the final state requested
+        // for the activity in this transaction, we won't do the last transition here and do it when
+        // moving to final state instead (because it may contain additional parameters from server).
+        final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
+        final int finalState = finalStateRequest != null ? finalStateRequest.getTargetState()
+                : UNDEFINED;
+        // Index of the last callback that requests some post-execution state.
+        final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);
+
         final int size = callbacks.size();
         for (int i = 0; i < size; ++i) {
             final ClientTransactionItem item = callbacks.get(i);
             log("Resolving callback: " + item);
-            final int preExecutionState = item.getPreExecutionState();
-            if (preExecutionState != UNDEFINED) {
-                cycleToPath(r, preExecutionState);
+            final int postExecutionState = item.getPostExecutionState();
+            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
+                    item.getPostExecutionState());
+            if (closestPreExecutionState != UNDEFINED) {
+                cycleToPath(r, closestPreExecutionState);
             }
 
             item.execute(mTransactionHandler, token, mPendingActions);
@@ -105,9 +114,11 @@
                 r = mTransactionHandler.getActivityClient(token);
             }
 
-            final int postExecutionState = item.getPostExecutionState();
-            if (postExecutionState != UNDEFINED) {
-                cycleToPath(r, postExecutionState);
+            if (postExecutionState != UNDEFINED && r != null) {
+                // Skip the very last transition and perform it by explicit state request instead.
+                final boolean shouldExcludeLastTransition =
+                        i == lastCallbackRequestingState && finalState == postExecutionState;
+                cycleToPath(r, postExecutionState, shouldExcludeLastTransition);
             }
         }
     }
@@ -136,7 +147,10 @@
             pw.println("Executor:");
             dump(pw, prefix);
 
-            Slog.wtf(TAG, stringWriter.toString());
+            Slog.w(TAG, stringWriter.toString());
+
+            // Ignore requests for non-existent client records for now.
+            return;
         }
 
         // Cycle to the state right before the final requested state.
@@ -162,15 +176,15 @@
             boolean excludeLastState) {
         final int start = r.getLifecycleState();
         log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
-        initLifecyclePath(start, finish, excludeLastState);
-        performLifecycleSequence(r);
+        final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
+        performLifecycleSequence(r, path);
     }
 
     /** Transition the client through previously initialized state sequence. */
-    private void performLifecycleSequence(ActivityClientRecord r) {
-        final int size = mLifecycleSequence.size();
+    private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
+        final int size = path.size();
         for (int i = 0, state; i < size; i++) {
-            state = mLifecycleSequence.get(i);
+            state = path.get(i);
             log("Transitioning to state: " + state);
             switch (state) {
                 case ON_CREATE:
@@ -180,21 +194,23 @@
                     mTransactionHandler.handleStartActivity(r, mPendingActions);
                     break;
                 case ON_RESUME:
-                    mTransactionHandler.handleResumeActivity(r.token, false /* clearHide */,
+                    mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
                             r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
                     break;
                 case ON_PAUSE:
                     mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
-                            false /* userLeaving */, 0 /* configChanges */,
-                            true /* dontReport */, mPendingActions);
+                            false /* userLeaving */, 0 /* configChanges */, mPendingActions,
+                            "LIFECYCLER_PAUSE_ACTIVITY");
                     break;
                 case ON_STOP:
                     mTransactionHandler.handleStopActivity(r.token, false /* show */,
-                            0 /* configChanges */, mPendingActions);
+                            0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
+                            "LIFECYCLER_STOP_ACTIVITY");
                     break;
                 case ON_DESTROY:
                     mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
-                            0 /* configChanges */, false /* getNonConfigInstance */);
+                            0 /* configChanges */, false /* getNonConfigInstance */,
+                            "performLifecycleSequence. cycling to:" + path.get(size - 1));
                     break;
                 case ON_RESTART:
                     mTransactionHandler.performRestartActivity(r.token, false /* start */);
@@ -205,60 +221,6 @@
         }
     }
 
-    /**
-     * Calculate the path through main lifecycle states for an activity and fill
-     * @link #mLifecycleSequence} with values starting with the state that follows the initial
-     * state.
-     */
-    public void initLifecyclePath(int start, int finish, boolean excludeLastState) {
-        mLifecycleSequence.clear();
-        if (finish >= start) {
-            // just go there
-            for (int i = start + 1; i <= finish; i++) {
-                mLifecycleSequence.add(i);
-            }
-        } else { // finish < start, can't just cycle down
-            if (start == ON_PAUSE && finish == ON_RESUME) {
-                // Special case when we can just directly go to resumed state.
-                mLifecycleSequence.add(ON_RESUME);
-            } else if (start <= ON_STOP && finish >= ON_START) {
-                // Restart and go to required state.
-
-                // Go to stopped state first.
-                for (int i = start + 1; i <= ON_STOP; i++) {
-                    mLifecycleSequence.add(i);
-                }
-                // Restart
-                mLifecycleSequence.add(ON_RESTART);
-                // Go to required state
-                for (int i = ON_START; i <= finish; i++) {
-                    mLifecycleSequence.add(i);
-                }
-            } else {
-                // Relaunch and go to required state
-
-                // Go to destroyed state first.
-                for (int i = start + 1; i <= ON_DESTROY; i++) {
-                    mLifecycleSequence.add(i);
-                }
-                // Go to required state
-                for (int i = ON_CREATE; i <= finish; i++) {
-                    mLifecycleSequence.add(i);
-                }
-            }
-        }
-
-        // Remove last transition in case we want to perform it with some specific params.
-        if (excludeLastState && mLifecycleSequence.size() != 0) {
-            mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
-        }
-    }
-
-    @VisibleForTesting
-    public int[] getLifecycleSequence() {
-        return mLifecycleSequence.toArray();
-    }
-
     private static void log(String message) {
         if (DEBUG_RESOLVER) Slog.d(TAG, message);
     }
diff --git a/android/app/servertransaction/TransactionExecutorHelper.java b/android/app/servertransaction/TransactionExecutorHelper.java
new file mode 100644
index 0000000..01b13a2
--- /dev/null
+++ b/android/app/servertransaction/TransactionExecutorHelper.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.util.IntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Helper class for {@link TransactionExecutor} that contains utils for lifecycle path resolution.
+ * @hide
+ */
+public class TransactionExecutorHelper {
+    // A penalty applied to path with destruction when looking for the shortest one.
+    private static final int DESTRUCTION_PENALTY = 10;
+
+    private static final int[] ON_RESUME_PRE_EXCUTION_STATES = new int[] { ON_START, ON_PAUSE };
+
+    // Temp holder for lifecycle path.
+    // No direct transition between two states should take more than one complete cycle of 6 states.
+    @ActivityLifecycleItem.LifecycleState
+    private IntArray mLifecycleSequence = new IntArray(6);
+
+    /**
+     * Calculate the path through main lifecycle states for an activity and fill
+     * @link #mLifecycleSequence} with values starting with the state that follows the initial
+     * state.
+     * <p>NOTE: The returned value is used internally in this class and is not a copy. It's contents
+     * may change after calling other methods of this class.</p>
+     */
+    @VisibleForTesting
+    public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
+        if (start == UNDEFINED || finish == UNDEFINED) {
+            throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state");
+        }
+        if (start == ON_RESTART || finish == ON_RESTART) {
+            throw new IllegalArgumentException(
+                    "Can't start or finish in intermittent RESTART state");
+        }
+        if (finish == PRE_ON_CREATE && start != finish) {
+            throw new IllegalArgumentException("Can only start in pre-onCreate state");
+        }
+
+        mLifecycleSequence.clear();
+        if (finish >= start) {
+            // just go there
+            for (int i = start + 1; i <= finish; i++) {
+                mLifecycleSequence.add(i);
+            }
+        } else { // finish < start, can't just cycle down
+            if (start == ON_PAUSE && finish == ON_RESUME) {
+                // Special case when we can just directly go to resumed state.
+                mLifecycleSequence.add(ON_RESUME);
+            } else if (start <= ON_STOP && finish >= ON_START) {
+                // Restart and go to required state.
+
+                // Go to stopped state first.
+                for (int i = start + 1; i <= ON_STOP; i++) {
+                    mLifecycleSequence.add(i);
+                }
+                // Restart
+                mLifecycleSequence.add(ON_RESTART);
+                // Go to required state
+                for (int i = ON_START; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
+            } else {
+                // Relaunch and go to required state
+
+                // Go to destroyed state first.
+                for (int i = start + 1; i <= ON_DESTROY; i++) {
+                    mLifecycleSequence.add(i);
+                }
+                // Go to required state
+                for (int i = ON_CREATE; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
+            }
+        }
+
+        // Remove last transition in case we want to perform it with some specific params.
+        if (excludeLastState && mLifecycleSequence.size() != 0) {
+            mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
+        }
+
+        return mLifecycleSequence;
+    }
+
+    /**
+     * Pick a state that goes before provided post-execution state and would require the least
+     * lifecycle transitions to get to.
+     * It will also make sure to try avoiding a path with activity destruction and relaunch if
+     * possible.
+     * @param r An activity that we're trying to resolve the transition for.
+     * @param postExecutionState Post execution state to compute for.
+     * @return One of states that precede the provided post-execution state, or
+     *         {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
+     */
+    @VisibleForTesting
+    public int getClosestPreExecutionState(ActivityClientRecord r,
+            int postExecutionState) {
+        switch (postExecutionState) {
+            case UNDEFINED:
+                return UNDEFINED;
+            case ON_RESUME:
+                return getClosestOfStates(r, ON_RESUME_PRE_EXCUTION_STATES);
+            default:
+                throw new UnsupportedOperationException("Pre-execution states for state: "
+                        + postExecutionState + " is not supported.");
+        }
+    }
+
+    /**
+     * Pick a state that would require the least lifecycle transitions to get to.
+     * It will also make sure to try avoiding a path with activity destruction and relaunch if
+     * possible.
+     * @param r An activity that we're trying to resolve the transition for.
+     * @param finalStates An array of valid final states.
+     * @return One of the provided final states, or {@link ActivityLifecycleItem#UNDEFINED} if none
+     *         were provided or there is not path.
+     */
+    @VisibleForTesting
+    public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {
+        if (finalStates == null || finalStates.length == 0) {
+            return UNDEFINED;
+        }
+
+        final int currentState = r.getLifecycleState();
+        int closestState = UNDEFINED;
+        for (int i = 0, shortestPath = Integer.MAX_VALUE, pathLength; i < finalStates.length; i++) {
+            getLifecyclePath(currentState, finalStates[i], false /* excludeLastState */);
+            pathLength = mLifecycleSequence.size();
+            if (pathInvolvesDestruction(mLifecycleSequence)) {
+                pathLength += DESTRUCTION_PENALTY;
+            }
+            if (shortestPath > pathLength) {
+                shortestPath = pathLength;
+                closestState = finalStates[i];
+            }
+        }
+        return closestState;
+    }
+
+    /** Get the lifecycle state request to match the current state in the end of a transaction. */
+    public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) {
+        final int prevState = r.getLifecycleState();
+        final ActivityLifecycleItem lifecycleItem;
+        switch (prevState) {
+            // TODO(lifecycler): Extend to support all possible states.
+            case ON_PAUSE:
+                lifecycleItem = PauseActivityItem.obtain();
+                break;
+            case ON_STOP:
+                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
+                        0 /* configChanges */);
+                break;
+            default:
+                lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
+                break;
+        }
+
+        return lifecycleItem;
+    }
+
+    /**
+     * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
+     * that involves destruction and recreation if there is another path.
+     */
+    private static boolean pathInvolvesDestruction(IntArray lifecycleSequence) {
+        final int size = lifecycleSequence.size();
+        for (int i = 0; i < size; i++) {
+            if (lifecycleSequence.get(i) == ON_DESTROY) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return the index of the last callback that requests the state in which activity will be after
+     * execution. If there is a group of callbacks in the end that requests the same specific state
+     * or doesn't request any - we will find the first one from such group.
+     *
+     * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
+     * specific state. If there is a sequence
+     *   Configuration - ActivityResult - Configuration - ActivityResult
+     * index 1 will be returned, because ActivityResult request on position 1 will be the last
+     * request that moves activity to the RESUMED state where it will eventually end.
+     */
+    static int lastCallbackRequestingState(ClientTransaction transaction) {
+        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
+        if (callbacks == null || callbacks.size() == 0) {
+            return -1;
+        }
+
+        // Go from the back of the list to front, look for the request closes to the beginning that
+        // requests the state in which activity will end after all callbacks are executed.
+        int lastRequestedState = UNDEFINED;
+        int lastRequestingCallback = -1;
+        for (int i = callbacks.size() - 1; i >= 0; i--) {
+            final ClientTransactionItem callback = callbacks.get(i);
+            final int postExecutionState = callback.getPostExecutionState();
+            if (postExecutionState != UNDEFINED) {
+                // Found a callback that requests some post-execution state.
+                if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) {
+                    // It's either a first-from-end callback that requests state or it requests
+                    // the same state as the last one. In both cases, we will use it as the new
+                    // candidate.
+                    lastRequestedState = postExecutionState;
+                    lastRequestingCallback = i;
+                } else {
+                    break;
+                }
+            }
+        }
+
+        return lastRequestingCallback;
+    }
+}
diff --git a/android/app/slice/Slice.java b/android/app/slice/Slice.java
index 5808f8b..bf3398a 100644
--- a/android/app/slice/Slice.java
+++ b/android/app/slice/Slice.java
@@ -65,20 +65,32 @@
             HINT_TOGGLE,
             HINT_HORIZONTAL,
             HINT_PARTIAL,
-            HINT_SEE_MORE
+            HINT_SEE_MORE,
+            HINT_KEYWORDS,
+            HINT_ERROR,
+            HINT_TTL,
+            HINT_LAST_UPDATED,
+            HINT_PERMISSION_REQUEST,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SliceHint {}
-
     /**
-     * The meta-data key that allows an activity to easily be linked directly to a slice.
-     * <p>
-     * An activity can be statically linked to a slice uri by including a meta-data item
-     * for this key that contains a valid slice uri for the same application declaring
-     * the activity.
      * @hide
      */
-    public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
+    @StringDef(prefix = { "SUBTYPE_" }, value = {
+            SUBTYPE_COLOR,
+            SUBTYPE_CONTENT_DESCRIPTION,
+            SUBTYPE_MAX,
+            SUBTYPE_MESSAGE,
+            SUBTYPE_PRIORITY,
+            SUBTYPE_RANGE,
+            SUBTYPE_SOURCE,
+            SUBTYPE_TOGGLE,
+            SUBTYPE_VALUE,
+            SUBTYPE_LAYOUT_DIRECTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceSubtype {}
 
     /**
      * Hint that this content is a title of other content in the slice. This can also indicate that
@@ -146,45 +158,98 @@
      */
     public static final String HINT_PARTIAL     = "partial";
     /**
-     * A hint representing that this item is the max value possible for the slice containing this.
-     * Used to indicate the maximum integer value for a {@link #SUBTYPE_SLIDER}.
-     */
-    public static final String HINT_MAX = "max";
-    /**
      * A hint representing that this item should be used to indicate that there's more
      * content associated with this slice.
      */
     public static final String HINT_SEE_MORE = "see_more";
     /**
-     * A hint used when implementing app-specific slice permissions.
-     * Tells the system that for this slice the return value of
-     * {@link SliceProvider#onBindSlice(Uri, List)} may be different depending on
-     * {@link SliceProvider#getBindingPackage} and should not be cached for multiple
-     * apps.
+     * @see Builder#setCallerNeeded
+     * @hide
      */
     public static final String HINT_CALLER_NEEDED = "caller_needed";
     /**
+     * A hint to indicate that the contents of this subslice represent a list of keywords
+     * related to the parent slice.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
+     */
+    public static final String HINT_KEYWORDS = "keywords";
+    /**
+     * A hint to indicate that this slice represents an error.
+     */
+    public static final String HINT_ERROR = "error";
+    /**
+     * Hint indicating an item representing a time-to-live for the content.
+     */
+    public static final String HINT_TTL = "ttl";
+    /**
+     * Hint indicating an item representing when the content was created or last updated.
+     */
+    public static final String HINT_LAST_UPDATED = "last_updated";
+    /**
+     * A hint to indicate that this slice represents a permission request for showing
+     * slices.
+     */
+    public static final String HINT_PERMISSION_REQUEST = "permission_request";
+    /**
+     * Subtype to indicate that this item indicates the layout direction for content
+     * in the slice.
+     * Expected to be an item of format {@link SliceItem#FORMAT_INT}.
+     */
+    public static final String SUBTYPE_LAYOUT_DIRECTION = "layout_direction";
+    /**
      * Key to retrieve an extra added to an intent when a control is changed.
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     /**
+     * Key to retrieve an extra added to an intent when the value of a slider is changed.
+     * @deprecated remove once support lib is update to use EXTRA_RANGE_VALUE instead
+     */
+    @Deprecated
+    public static final String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
+    /**
+     * Key to retrieve an extra added to an intent when the value of an input range is changed.
+     */
+    public static final String EXTRA_RANGE_VALUE = "android.app.slice.extra.RANGE_VALUE";
+    /**
      * Subtype to indicate that this is a message as part of a communication
      * sequence in this slice.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
      */
     public static final String SUBTYPE_MESSAGE = "message";
     /**
      * Subtype to tag the source (i.e. sender) of a {@link #SUBTYPE_MESSAGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT},
+     * {@link SliceItem#FORMAT_IMAGE} or an {@link SliceItem#FORMAT_SLICE} containing them.
      */
     public static final String SUBTYPE_SOURCE = "source";
     /**
      * Subtype to tag an item as representing a color.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_COLOR = "color";
     /**
-     * Subtype to tag an item represents a slider.
+     * Subtype to tag an item as representing a slider.
+     * @deprecated remove once support lib is update to use SUBTYPE_RANGE instead
      */
+    @Deprecated
     public static final String SUBTYPE_SLIDER = "slider";
     /**
+     * Subtype to tag an item as representing a range.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE} containing
+     * a {@link #SUBTYPE_VALUE} and possibly a {@link #SUBTYPE_MAX}.
+     */
+    public static final String SUBTYPE_RANGE = "range";
+    /**
+     * Subtype to tag an item as representing the max int value for a {@link #SUBTYPE_RANGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
+     */
+    public static final String SUBTYPE_MAX = "max";
+    /**
+     * Subtype to tag an item as representing the current int value for a {@link #SUBTYPE_RANGE}.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
+     */
+    public static final String SUBTYPE_VALUE = "value";
+    /**
      * Subtype to indicate that this content has a toggle action associated with it. To indicate
      * that the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the
      * intent associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE}
@@ -193,12 +258,19 @@
     public static final String SUBTYPE_TOGGLE = "toggle";
     /**
      * Subtype to tag an item representing priority.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_INT}.
      */
     public static final String SUBTYPE_PRIORITY = "priority";
     /**
      * Subtype to tag an item to use as a content description.
+     * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}.
      */
     public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
+    /**
+     * Subtype to tag an item as representing a time in milliseconds since midnight,
+     * January 1, 1970 UTC.
+     */
+    public static final String SUBTYPE_MILLIS = "millis";
 
     private final SliceItem[] mItems;
     private final @SliceHint String[] mHints;
@@ -275,6 +347,14 @@
     }
 
     /**
+     * Returns whether the caller for this slice matters.
+     * @see Builder#setCallerNeeded
+     */
+    public boolean isCallerNeeded() {
+        return hasHint(HINT_CALLER_NEEDED);
+    }
+
+    /**
      * A Builder used to construct {@link Slice}s
      */
     public static class Builder {
@@ -285,14 +365,24 @@
         private SliceSpec mSpec;
 
         /**
-         * Create a builder which will construct a {@link Slice} for the Given Uri.
-         * @param uri Uri to tag for this slice.
+         * @deprecated TO BE REMOVED
          */
+        @Deprecated
         public Builder(@NonNull Uri uri) {
             mUri = uri;
         }
 
         /**
+         * Create a builder which will construct a {@link Slice} for the given Uri.
+         * @param uri Uri to tag for this slice.
+         * @param spec the spec for this slice.
+         */
+        public Builder(@NonNull Uri uri, SliceSpec spec) {
+            mUri = uri;
+            mSpec = spec;
+        }
+
+        /**
          * Create a builder for a {@link Slice} that is a sub-slice of the slice
          * being constructed by the provided builder.
          * @param parent The builder constructing the parent slice
@@ -303,10 +393,17 @@
         }
 
         /**
-         * Add hints to the Slice being constructed
+         * Tells the system whether for this slice the return value of
+         * {@link SliceProvider#onBindSlice(Uri, List)} may be different depending on
+         * {@link SliceProvider#getCallingPackage()} and should not be cached for multiple
+         * apps.
          */
-        public Builder addHints(@SliceHint String... hints) {
-            mHints.addAll(Arrays.asList(hints));
+        public Builder setCallerNeeded(boolean callerNeeded) {
+            if (callerNeeded) {
+                mHints.add(HINT_CALLER_NEEDED);
+            } else {
+                mHints.remove(HINT_CALLER_NEEDED);
+            }
             return this;
         }
 
@@ -314,11 +411,12 @@
          * Add hints to the Slice being constructed
          */
         public Builder addHints(@SliceHint List<String> hints) {
-            return addHints(hints.toArray(new String[hints.size()]));
+            mHints.addAll(hints);
+            return this;
         }
 
         /**
-         * Add the spec for this slice.
+         * @deprecated TO BE REMOVED
          */
         public Builder setSpec(SliceSpec spec) {
             mSpec = spec;
@@ -327,17 +425,10 @@
 
         /**
          * Add a sub-slice to the slice being constructed
-         */
-        public Builder addSubSlice(@NonNull Slice slice) {
-            return addSubSlice(slice, null);
-        }
-
-        /**
-         * Add a sub-slice to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addSubSlice(@NonNull Slice slice, @Nullable String subType) {
+        public Builder addSubSlice(@NonNull Slice slice, @Nullable @SliceSubtype String subType) {
             mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType,
                     slice.getHints().toArray(new String[slice.getHints().size()])));
             return this;
@@ -345,18 +436,11 @@
 
         /**
          * Add an action to the slice being constructed
-         */
-        public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
-            return addAction(action, s, null);
-        }
-
-        /**
-         * Add an action to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
         public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s,
-                @Nullable String subType) {
+                @Nullable @SliceSubtype String subType) {
             List<String> hints = s.getHints();
             s.mSpec = null;
             mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray(
@@ -369,132 +453,83 @@
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addText(CharSequence text, @Nullable String subType,
-                @SliceHint String... hints) {
+        public Builder addText(CharSequence text, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(text, SliceItem.FORMAT_TEXT, subType, hints));
             return this;
         }
 
         /**
-         * Add text to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Builder addText(CharSequence text, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addText(text, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add an image to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint String... hints) {
+        public Builder addIcon(Icon icon, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints));
             return this;
         }
 
         /**
-         * Add an image to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Builder addIcon(Icon icon, @Nullable String subType, @SliceHint List<String> hints) {
-            return addIcon(icon, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add remote input to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+        public Slice.Builder addRemoteInput(RemoteInput remoteInput,
+                @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add remote input to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
-                @SliceHint String... hints) {
             mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT,
                     subType, hints));
             return this;
         }
 
         /**
-         * Add a color to the slice being constructed
+         * Add an integer to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Builder addInt(int value, @Nullable String subType, @SliceHint String... hints) {
+        public Builder addInt(int value, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(value, SliceItem.FORMAT_INT, subType, hints));
             return this;
         }
 
         /**
-         * Add a color to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
+         * @deprecated TO BE REMOVED.
          */
-        public Builder addInt(int value, @Nullable String subType,
+        @Deprecated
+        public Slice.Builder addTimestamp(long time, @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            return addInt(value, subType, hints.toArray(new String[hints.size()]));
+            return addLong(time, subType, hints);
         }
 
         /**
-         * Add a timestamp to the slice being constructed
+         * Add a long to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addTimestamp(long time, @Nullable String subType,
-                @SliceHint String... hints) {
-            mItems.add(new SliceItem(time, SliceItem.FORMAT_TIMESTAMP, subType,
-                    hints));
+        public Slice.Builder addLong(long value, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
+            mItems.add(new SliceItem(value, SliceItem.FORMAT_LONG, subType,
+                    hints.toArray(new String[hints.size()])));
             return this;
         }
 
         /**
-         * Add a timestamp to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addTimestamp(long time, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addTimestamp(time, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Add a bundle to the slice being constructed.
          * <p>Expected to be used for support library extension, should not be used for general
          * development
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
          */
-        public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
-                @SliceHint String... hints) {
+        public Slice.Builder addBundle(Bundle bundle, @Nullable @SliceSubtype String subType,
+                @SliceHint List<String> hints) {
             mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType,
                     hints));
             return this;
         }
 
         /**
-         * Add a bundle to the slice being constructed.
-         * <p>Expected to be used for support library extension, should not be used for general
-         * development
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         */
-        public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addBundle(bundle, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
          * Construct the slice.
          */
         public Slice build() {
diff --git a/android/app/slice/SliceItem.java b/android/app/slice/SliceItem.java
index 9eb2bb8..019ae49 100644
--- a/android/app/slice/SliceItem.java
+++ b/android/app/slice/SliceItem.java
@@ -67,7 +67,7 @@
             FORMAT_IMAGE,
             FORMAT_ACTION,
             FORMAT_INT,
-            FORMAT_TIMESTAMP,
+            FORMAT_LONG,
             FORMAT_REMOTE_INPUT,
             FORMAT_BUNDLE,
     })
@@ -98,9 +98,14 @@
      */
     public static final String FORMAT_INT = "int";
     /**
-     * A {@link SliceItem} that contains a timestamp.
+     * A {@link SliceItem} that contains a long.
      */
-    public static final String FORMAT_TIMESTAMP = "timestamp";
+    public static final String FORMAT_LONG = "long";
+    /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public static final String FORMAT_TIMESTAMP = FORMAT_LONG;
     /**
      * A {@link SliceItem} that contains a {@link RemoteInput}.
      */
@@ -123,6 +128,14 @@
      * @hide
      */
     public SliceItem(Object obj, @SliceType String format, String subType,
+            List<String> hints) {
+        this(obj, format, subType, hints.toArray(new String[hints.size()]));
+    }
+
+    /**
+     * @hide
+     */
+    public SliceItem(Object obj, @SliceType String format, String subType,
             @Slice.SliceHint String[] hints) {
         mHints = hints;
         mFormat = format;
diff --git a/android/app/slice/SliceManager.java b/android/app/slice/SliceManager.java
index 2fa9d8e..0285e9f 100644
--- a/android/app/slice/SliceManager.java
+++ b/android/app/slice/SliceManager.java
@@ -16,24 +16,29 @@
 
 package android.app.slice;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.IContentProvider;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.PermissionResult;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.ArrayMap;
+import android.os.UserHandle;
 import android.util.Log;
-import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
 
@@ -42,7 +47,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 /**
  * Class to handle interactions with {@link Slice}s.
@@ -60,10 +64,41 @@
     public static final String ACTION_REQUEST_SLICE_PERMISSION =
             "android.intent.action.REQUEST_SLICE_PERMISSION";
 
+    /**
+     * Category used to resolve intents that can be rendered as slices.
+     * <p>
+     * This category should be included on intent filters on providers that extend
+     * {@link SliceProvider}.
+     * @see SliceProvider
+     * @see SliceProvider#onMapIntentToUri(Intent)
+     * @see #mapIntentToUri(Intent)
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_SLICE = "android.app.slice.category.SLICE";
+
+    /**
+     * The meta-data key that allows an activity to easily be linked directly to a slice.
+     * <p>
+     * An activity can be statically linked to a slice uri by including a meta-data item
+     * for this key that contains a valid slice uri for the same application declaring
+     * the activity.
+     *
+     * <pre class="prettyprint">
+     * {@literal
+     * <activity android:name="com.example.mypkg.MyActivity">
+     *     <meta-data android:name="android.metadata.SLICE_URI"
+     *                android:value="content://com.example.mypkg/main_slice" />
+     *  </activity>}
+     * </pre>
+     *
+     * @see #mapIntentToUri(Intent)
+     * @see SliceProvider#onMapIntentToUri(Intent)
+     */
+    public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
+
     private final ISliceManager mService;
     private final Context mContext;
-    private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
-            new ArrayMap<>();
+    private final IBinder mToken = new Binder();
 
     /**
      * Permission denied.
@@ -91,117 +126,24 @@
     }
 
     /**
-     * @deprecated TO BE REMOVED.
-     */
-    @Deprecated
-    public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
-            @NonNull List<SliceSpec> specs) {
-        registerSliceCallback(uri, specs, mContext.getMainExecutor(), callback);
-    }
-
-    /**
-     * @deprecated TO BE REMOVED.
-     */
-    @Deprecated
-    public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
-            @NonNull List<SliceSpec> specs, Executor executor) {
-        registerSliceCallback(uri, specs, executor, callback);
-    }
-
-    /**
-     * Adds a callback to a specific slice uri.
-     * <p>
-     * This is a convenience that performs a few slice actions at once. It will put
-     * the slice in a pinned state since there is a callback attached. It will also
-     * listen for content changes, when a content change observes, the android system
-     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
-     *
-     * @param uri The uri of the slice being listened to.
-     * @param callback The listener that should receive the callbacks.
-     * @param specs The list of supported {@link SliceSpec}s of the callback.
-     * @see SliceProvider#onSlicePinned(Uri)
-     */
-    public void registerSliceCallback(@NonNull Uri uri, @NonNull List<SliceSpec> specs,
-            @NonNull SliceCallback callback) {
-        registerSliceCallback(uri, specs, mContext.getMainExecutor(), callback);
-    }
-
-    /**
-     * Adds a callback to a specific slice uri.
-     * <p>
-     * This is a convenience that performs a few slice actions at once. It will put
-     * the slice in a pinned state since there is a callback attached. It will also
-     * listen for content changes, when a content change observes, the android system
-     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
-     *
-     * @param uri The uri of the slice being listened to.
-     * @param callback The listener that should receive the callbacks.
-     * @param specs The list of supported {@link SliceSpec}s of the callback.
-     * @see SliceProvider#onSlicePinned(Uri)
-     */
-    public void registerSliceCallback(@NonNull Uri uri, @NonNull List<SliceSpec> specs,
-            @NonNull @CallbackExecutor Executor executor, @NonNull SliceCallback callback) {
-        try {
-            mService.addSliceListener(uri, mContext.getPackageName(),
-                    getListener(uri, callback, new ISliceListener.Stub() {
-                        @Override
-                        public void onSliceUpdated(Slice s) throws RemoteException {
-                            executor.execute(() -> callback.onSliceUpdated(s));
-                        }
-                    }), specs.toArray(new SliceSpec[specs.size()]));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    private ISliceListener getListener(Uri uri, SliceCallback callback,
-            ISliceListener listener) {
-        Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
-        if (mListenerLookup.containsKey(key)) {
-            try {
-                mService.removeSliceListener(uri, mContext.getPackageName(),
-                        mListenerLookup.get(key));
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-        mListenerLookup.put(key, listener);
-        return listener;
-    }
-
-    /**
-     * Removes a callback for a specific slice uri.
-     * <p>
-     * Removes the app from the pinned state (if there are no other apps/callbacks pinning it)
-     * in addition to removing the callback.
-     *
-     * @param uri The uri of the slice being listened to
-     * @param callback The listener that should no longer receive callbacks.
-     * @see #registerSliceCallback
-     */
-    public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) {
-        try {
-            mService.removeSliceListener(uri, mContext.getPackageName(),
-                    mListenerLookup.remove(new Pair<>(uri, callback)));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Ensures that a slice is in a pinned state.
      * <p>
      * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
      * they still care about after a reboot.
+     * <p>
+     * This may only be called by apps that are the default launcher for the device
+     * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
      *
      * @param uri The uri of the slice being pinned.
      * @param specs The list of supported {@link SliceSpec}s of the callback.
      * @see SliceProvider#onSlicePinned(Uri)
+     * @see Intent#ACTION_ASSIST
+     * @see Intent#CATEGORY_HOME
      */
     public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
         try {
             mService.pinSlice(mContext.getPackageName(), uri,
-                    specs.toArray(new SliceSpec[specs.size()]));
+                    specs.toArray(new SliceSpec[specs.size()]), mToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -211,14 +153,19 @@
      * Remove a pin for a slice.
      * <p>
      * If the slice has no other pins/callbacks then the slice will be unpinned.
+     * <p>
+     * This may only be called by apps that are the default launcher for the device
+     * or the default voice interaction service. Otherwise will throw {@link SecurityException}.
      *
      * @param uri The uri of the slice being unpinned.
      * @see #pinSlice
      * @see SliceProvider#onSliceUnpinned(Uri)
+     * @see Intent#ACTION_ASSIST
+     * @see Intent#CATEGORY_HOME
      */
     public void unpinSlice(@NonNull Uri uri) {
         try {
-            mService.unpinSlice(mContext.getPackageName(), uri);
+            mService.unpinSlice(mContext.getPackageName(), uri, mToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -251,6 +198,18 @@
     }
 
     /**
+     * Get the list of currently pinned slices for this app.
+     * @see SliceProvider#onSlicePinned
+     */
+    public @NonNull List<Uri> getPinnedSlices() {
+        try {
+            return Arrays.asList(mService.getPinnedSlices(mContext.getPackageName()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Obtains a list of slices that are descendants of the specified Uri.
      * <p>
      * Not all slice providers will implement this functionality, in which case,
@@ -262,17 +221,13 @@
      */
     public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
         ContentResolver resolver = mContext.getContentResolver();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
-            final Bundle res = provider.call(resolver.getPackageName(),
-                    SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
             return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to get slice descendants", e);
-        } finally {
-            resolver.releaseProvider(provider);
         }
         return Collections.emptyList();
     }
@@ -288,17 +243,15 @@
     public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(uri, "uri");
         ContentResolver resolver = mContext.getContentResolver();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            if (provider == null) {
+                throw new IllegalArgumentException("Unknown URI " + uri);
+            }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
-                    null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_SLICE, null, extras);
             Bundle.setDefusable(res, true);
             if (res == null) {
                 return null;
@@ -308,8 +261,80 @@
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
             return null;
-        } finally {
-            resolver.releaseProvider(provider);
+        }
+    }
+
+    /**
+     * Turns a slice intent into a slice uri. Expects an explicit intent.
+     * <p>
+     * This goes through a several stage resolution process to determine if any slice
+     * can represent this intent.
+     * <ol>
+     *  <li> If the intent contains data that {@link ContentResolver#getType} is
+     *  {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li>
+     *  <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
+     *  the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
+     *  will be returned.</li>
+     *  <li>Lastly, if the intent explicitly points at an activity, and that activity has
+     *  meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be
+     *  returned.</li>
+     *  <li>If no slice is found, then {@code null} is returned.</li>
+     * </ol>
+     * @param intent The intent associated with a slice.
+     * @return The Slice Uri provided by the app or null if none exists.
+     * @see Slice
+     * @see SliceProvider#onMapIntentToUri(Intent)
+     * @see Intent
+     */
+    public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
+        Preconditions.checkNotNull(intent, "intent");
+        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
+                || intent.getData() != null,
+                "Slice intent must be explicit %s", intent);
+        ContentResolver resolver = mContext.getContentResolver();
+
+        // Check if the intent has data for the slice uri on it and use that
+        final Uri intentData = intent.getData();
+        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+            return intentData;
+        }
+        // Otherwise ask the app
+        Intent queryIntent = new Intent(intent);
+        if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
+            queryIntent.addCategory(CATEGORY_SLICE);
+        }
+        List<ResolveInfo> providers =
+                mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
+        if (providers == null || providers.isEmpty()) {
+            // There are no providers, see if this activity has a direct link.
+            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+                    PackageManager.GET_META_DATA);
+            if (resolve != null && resolve.activityInfo != null
+                    && resolve.activityInfo.metaData != null
+                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+                return Uri.parse(
+                        resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
+            }
+            return null;
+        }
+        String authority = providers.get(0).providerInfo.authority;
+        Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(authority).build();
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            if (provider == null) {
+                throw new IllegalArgumentException("Unknown URI " + uri);
+            }
+            Bundle extras = new Bundle();
+            extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            final Bundle res = provider.call(SliceProvider.METHOD_MAP_ONLY_INTENT, null, extras);
+            if (res == null) {
+                return null;
+            }
+            return res.getParcelable(SliceProvider.EXTRA_SLICE);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
         }
     }
 
@@ -328,8 +353,9 @@
     public @Nullable Slice bindSlice(@NonNull Intent intent,
             @NonNull List<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(intent, "intent");
-        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
-                "Slice intent must be explicit " + intent);
+        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
+                || intent.getData() != null,
+                "Slice intent must be explicit %s", intent);
         ContentResolver resolver = mContext.getContentResolver();
 
         // Check if the intent has data for the slice uri on it and use that
@@ -340,23 +366,30 @@
         // Otherwise ask the app
         List<ResolveInfo> providers =
                 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
-        if (providers == null) {
-            throw new IllegalArgumentException("Unable to resolve intent " + intent);
+        if (providers == null || providers.isEmpty()) {
+            // There are no providers, see if this activity has a direct link.
+            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+                    PackageManager.GET_META_DATA);
+            if (resolve != null && resolve.activityInfo != null
+                    && resolve.activityInfo.metaData != null
+                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+                return bindSlice(Uri.parse(resolve.activityInfo.metaData
+                        .getString(SLICE_METADATA_KEY)), supportedSpecs);
+            }
+            return null;
         }
         String authority = providers.get(0).providerInfo.authority;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
+        try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+            if (provider == null) {
+                throw new IllegalArgumentException("Unknown URI " + uri);
+            }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(mContext.getPackageName(),
-                    SliceProvider.METHOD_MAP_INTENT, null, extras);
+            final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
             }
@@ -365,21 +398,82 @@
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
             return null;
-        } finally {
-            resolver.releaseProvider(provider);
         }
     }
 
     /**
+     * Determine whether a particular process and user ID has been granted
+     * permission to access a specific slice URI.
+     *
+     * @param uri The uri that is being checked.
+     * @param pid The process ID being checked against.  Must be &gt; 0.
+     * @param uid The user ID being checked against.  A uid of 0 is the root
+     * user, which will pass every permission check.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the given
+     * pid/uid is allowed to access that uri, or
+     * {@link PackageManager#PERMISSION_DENIED} if it is not.
+     *
+     * @see #grantSlicePermission(String, Uri)
+     */
+    public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
+        // TODO: Switch off Uri permissions.
+        return mContext.checkUriPermission(uri, pid, uid,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    /**
+     * Grant permission to access a specific slice Uri to another package.
+     *
+     * @param toPackage The package you would like to allow to access the Uri.
+     * @param uri The Uri you would like to grant access to.
+     *
+     * @see #revokeSlicePermission
+     */
+    public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
+        // TODO: Switch off Uri permissions.
+        mContext.grantUriPermission(toPackage, uri,
+                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+    }
+
+    /**
+     * Remove permissions to access a particular content provider Uri
+     * that were previously added with {@link #grantSlicePermission} for a specific target
+     * package.  The given Uri will match all previously granted Uris that are the same or a
+     * sub-path of the given Uri.  That is, revoking "content://foo/target" will
+     * revoke both "content://foo/target" and "content://foo/target/sub", but not
+     * "content://foo".  It will not remove any prefix grants that exist at a
+     * higher level.
+     *
+     * @param toPackage The package you would like to allow to access the Uri.
+     * @param uri The Uri you would like to revoke access to.
+     *
+     * @see #grantSlicePermission
+     */
+    public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
+        // TODO: Switch off Uri permissions.
+        mContext.revokeUriPermission(toPackage, uri,
+                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+    }
+
+    /**
      * Does the permission check to see if a caller has access to a specific slice.
      * @hide
      */
-    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) {
         try {
+            if (UserHandle.isSameApp(uid, Process.myUid())) {
+                return;
+            }
             if (pkg == null) {
                 throw new SecurityException("No pkg specified");
             }
-            int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+            int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
             if (result == PERMISSION_DENIED) {
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
@@ -391,6 +485,8 @@
                         Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                                 | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+                // Notify a change has happened because we just granted a permission.
+                mContext.getContentResolver().notifyChange(uri, null);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -408,18 +504,4 @@
             throw e.rethrowFromSystemServer();
         }
     }
-
-    /**
-     * Class that listens to changes in {@link Slice}s.
-     */
-    public interface SliceCallback {
-
-        /**
-         * Called when slice is updated.
-         *
-         * @param s The updated slice.
-         * @see #registerSliceCallback
-         */
-        void onSliceUpdated(Slice s);
-    }
 }
diff --git a/android/app/slice/SliceMetrics.java b/android/app/slice/SliceMetrics.java
new file mode 100644
index 0000000..20c1390
--- /dev/null
+++ b/android/app/slice/SliceMetrics.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.slice;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.Uri;
+
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * Metrics interface for slices.
+ *
+ * This is called by SliceView, so Slice developers should
+ * not need to reference this class.
+ *
+ * @see androidx.slice.widget.SliceView
+ */
+public class SliceMetrics {
+
+    private static final String TAG = "SliceMetrics";
+    private MetricsLogger mMetricsLogger;
+
+    /**
+     * An object to be used throughout the life of a slice to register events.
+     */
+    public SliceMetrics(@NonNull Context context, @NonNull Uri uri) {
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    /**
+     * To be called whenever the slice becomes visible to the user.
+     */
+    public void logVisible() {
+    }
+
+    /**
+     * To be called whenever the slice becomes invisible to the user.
+     */
+    public void logHidden() {
+    }
+
+    /**
+     * To be called whenever the user invokes a discrete action via a slice.
+     *
+     * <P>
+     *     Use this for discrete events like a tap or the end of a drag,
+     *     not for a continuous streams of events, such as the motion during a gesture.
+     * </P>
+     *
+     * @see androidx.slice.widget.EventInfo#actionType
+     *
+     * @param actionType The type of the event.
+     * @param subSlice The URI of the sub-slice that is the subject of the interaction.
+     */
+    public void logTouch(int actionType, @NonNull Uri subSlice) {
+    }
+}
diff --git a/android/app/slice/SliceProvider.java b/android/app/slice/SliceProvider.java
index 00e8cca..fe5742d 100644
--- a/android/app/slice/SliceProvider.java
+++ b/android/app/slice/SliceProvider.java
@@ -16,7 +16,6 @@
 package android.app.slice;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -35,18 +34,16 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.StrictMode.ThreadPolicy;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
 
 /**
  * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
@@ -65,8 +62,8 @@
  * <pre class="prettyprint">
  * {@literal
  * <provider
- *     android:name="com.android.mypkg.MySliceProvider"
- *     android:authorities="com.android.mypkg" />}
+ *     android:name="com.example.mypkg.MySliceProvider"
+ *     android:authorities="com.example.mypkg" />}
  * </pre>
  * <p>
  * Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider
@@ -77,10 +74,11 @@
  * <pre class="prettyprint">
  * {@literal
  * <provider
- *     android:name="com.android.mypkg.MySliceProvider"
- *     android:authorities="com.android.mypkg">
+ *     android:name="com.example.mypkg.MySliceProvider"
+ *     android:authorities="com.example.mypkg">
  *     <intent-filter>
- *         <action android:name="android.intent.action.MY_SLICE_INTENT" />
+ *         <action android:name="com.example.mypkg.intent.action.MY_SLICE_INTENT" />
+ *         <category android:name="android.app.slice.category.SLICE" />
  *     </intent-filter>
  * </provider>}
  * </pre>
@@ -114,6 +112,10 @@
     /**
      * @hide
      */
+    public static final String METHOD_MAP_ONLY_INTENT = "map_only";
+    /**
+     * @hide
+     */
     public static final String METHOD_PIN = "pin";
     /**
      * @hide
@@ -143,24 +145,33 @@
      * @hide
      */
     public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
-    /**
-     * @hide
-     */
-    public static final String EXTRA_OVERRIDE_PKG = "override_pkg";
 
     private static final boolean DEBUG = false;
 
-    private String mBindingPkg;
+    private static final long SLICE_BIND_ANR = 2000;
+    private final String[] mAutoGrantPermissions;
+
+    private String mCallback;
     private SliceManager mSliceManager;
 
     /**
-     * Return the package name of the caller that initiated the binding request
-     * currently happening. The returned package will have been
-     * verified to belong to the calling UID. Returns {@code null} if not
-     * currently performing an {@link #onBindSlice(Uri, List)}.
+     * A version of constructing a SliceProvider that allows autogranting slice permissions
+     * to apps that hold specific platform permissions.
+     * <p>
+     * When an app tries to bind a slice from this provider that it does not have access to,
+     * This provider will check if the caller holds permissions to any of the autoGrantPermissions
+     * specified, if they do they will be granted persisted uri access to all slices of this
+     * provider.
+     *
+     * @param autoGrantPermissions List of permissions that holders are auto-granted access
+     *                             to slices.
      */
-    public final @Nullable String getBindingPackage() {
-        return mBindingPkg;
+    public SliceProvider(@NonNull String... autoGrantPermissions) {
+        mAutoGrantPermissions = autoGrantPermissions;
+    }
+
+    public SliceProvider() {
+        mAutoGrantPermissions = new String[0];
     }
 
     @Override
@@ -170,12 +181,12 @@
     }
 
     /**
-     * Implemented to create a slice. Will be called on the main thread.
+     * Implemented to create a slice.
      * <p>
      * onBindSlice should return as quickly as possible so that the UI tied
      * to this slice can be responsive. No network or other IO will be allowed
      * during onBindSlice. Any loading that needs to be done should happen
-     * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
+     * in the background with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
      * when the app is ready to provide the complete data in onBindSlice.
      * <p>
      * The slice returned should have a spec that is compatible with one of
@@ -241,14 +252,36 @@
      * In that case, this method can be called and is expected to return a non-null Uri representing
      * a slice. Otherwise this will throw {@link UnsupportedOperationException}.
      *
+     * Any intent filter added to a slice provider should also contain
+     * {@link SliceManager#CATEGORY_SLICE}, because otherwise it will not be detected by
+     * {@link SliceManager#mapIntentToUri(Intent)}.
+     *
      * @return Uri representing the slice associated with the provided intent.
-     * @see {@link Slice}
+     * @see Slice
+     * @see SliceManager#mapIntentToUri(Intent)
      */
     public @NonNull Uri onMapIntentToUri(Intent intent) {
         throw new UnsupportedOperationException(
                 "This provider has not implemented intent to uri mapping");
     }
 
+    /**
+     * Called when an app requests a slice it does not have write permission
+     * to the uri for.
+     * <p>
+     * The return value will be the action on a slice that prompts the user that
+     * the calling app wants to show slices from this app. The default implementation
+     * launches a dialog that allows the user to grant access to this slice. Apps
+     * that do not want to allow this user grant, can override this and instead
+     * launch their own dialog with different behavior.
+     *
+     * @param sliceUri the Uri of the slice attempting to be bound.
+     * @see #getCallingPackage()
+     */
+    public @NonNull PendingIntent onCreatePermissionRequest(Uri sliceUri) {
+        return createPermissionIntent(getContext(), sliceUri, getCallingPackage());
+    }
+
     @Override
     public final int update(Uri uri, ContentValues values, String selection,
             String[] selectionArgs) {
@@ -302,13 +335,10 @@
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
 
             String callingPackage = getCallingPackage();
-            if (extras.containsKey(EXTRA_OVERRIDE_PKG)) {
-                if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                    throw new SecurityException("Only the system can override calling pkg");
-                }
-                callingPackage = extras.getString(EXTRA_OVERRIDE_PKG);
-            }
-            Slice s = handleBindSlice(uri, supportedSpecs, callingPackage);
+            int callingUid = Binder.getCallingUid();
+            int callingPid = Binder.getCallingPid();
+
+            Slice s = handleBindSlice(uri, supportedSpecs, callingPackage, callingUid, callingPid);
             Bundle b = new Bundle();
             b.putParcelable(EXTRA_SLICE, s);
             return b;
@@ -319,12 +349,20 @@
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
             Bundle b = new Bundle();
             if (uri != null) {
-                Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage());
+                Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage(),
+                        Binder.getCallingUid(), Binder.getCallingPid());
                 b.putParcelable(EXTRA_SLICE, s);
             } else {
                 b.putParcelable(EXTRA_SLICE, null);
             }
             return b;
+        } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
+            Intent intent = extras.getParcelable(EXTRA_INTENT);
+            if (intent == null) return null;
+            Uri uri = onMapIntentToUri(intent);
+            Bundle b = new Bundle();
+            b.putParcelable(EXTRA_SLICE, uri);
+            return b;
         } else if (method.equals(METHOD_PIN)) {
             Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -348,102 +386,80 @@
     }
 
     private Collection<Uri> handleGetDescendants(Uri uri) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
+        mCallback = "onGetSliceDescendants";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
             return onGetSliceDescendants(uri);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Collection<Uri>[] output = new Collection[1];
-            Handler.getMain().post(() -> {
-                output[0] = onGetSliceDescendants(uri);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-                return output[0];
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
     private void handlePinSlice(Uri sliceUri) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
+        mCallback = "onSlicePinned";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
             onSlicePinned(sliceUri);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Handler.getMain().post(() -> {
-                onSlicePinned(sliceUri);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
     private void handleUnpinSlice(Uri sliceUri) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
+        mCallback = "onSliceUnpinned";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
             onSliceUnpinned(sliceUri);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Handler.getMain().post(() -> {
-                onSliceUnpinned(sliceUri);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
     private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs,
-            String callingPkg) {
+            String callingPkg, int callingUid, int callingPid) {
         // This can be removed once Slice#bindSlice is removed and everyone is using
         // SliceManager#bindSlice.
         String pkg = callingPkg != null ? callingPkg
-                : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
-        if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
-            try {
-                mSliceManager.enforceSlicePermission(sliceUri, pkg,
-                        Binder.getCallingPid(), Binder.getCallingUid());
-            } catch (SecurityException e) {
-                return createPermissionSlice(getContext(), sliceUri, pkg);
-            }
+                : getContext().getPackageManager().getNameForUid(callingUid);
+        try {
+            mSliceManager.enforceSlicePermission(sliceUri, pkg,
+                    callingPid, callingUid, mAutoGrantPermissions);
+        } catch (SecurityException e) {
+            return createPermissionSlice(getContext(), sliceUri, pkg);
         }
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            return onBindSliceStrict(sliceUri, supportedSpecs, pkg);
-        } else {
-            CountDownLatch latch = new CountDownLatch(1);
-            Slice[] output = new Slice[1];
-            Handler.getMain().post(() -> {
-                output[0] = onBindSliceStrict(sliceUri, supportedSpecs, pkg);
-                latch.countDown();
-            });
-            try {
-                latch.await();
-                return output[0];
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
+        mCallback = "onBindSlice";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            return onBindSliceStrict(sliceUri, supportedSpecs);
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
         }
     }
 
     /**
      * @hide
      */
-    public static Slice createPermissionSlice(Context context, Uri sliceUri,
+    public Slice createPermissionSlice(Context context, Uri sliceUri,
             String callingPackage) {
-        return new Slice.Builder(sliceUri)
-                .addAction(createPermissionIntent(context, sliceUri, callingPackage),
-                        new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
-                                .addText(getPermissionString(context, callingPackage), null)
-                                .build())
-                .addHints(Slice.HINT_LIST_ITEM)
-                .build();
+        PendingIntent action;
+        mCallback = "onCreatePermissionRequest";
+        Handler.getMain().postDelayed(mAnr, SLICE_BIND_ANR);
+        try {
+            action = onCreatePermissionRequest(sliceUri);
+        } finally {
+            Handler.getMain().removeCallbacks(mAnr);
+        }
+        Slice.Builder parent = new Slice.Builder(sliceUri);
+        Slice.Builder childAction = new Slice.Builder(parent)
+                .addHints(Arrays.asList(Slice.HINT_TITLE, Slice.HINT_SHORTCUT))
+                .addAction(action, new Slice.Builder(parent).build(), null);
+
+        parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+                .addText(getPermissionString(context, callingPackage), null,
+                        Collections.emptyList())
+                .addSubSlice(childAction.build(), null)
+                .build(), null);
+        return parent.addHints(Arrays.asList(Slice.HINT_PERMISSION_REQUEST)).build();
     }
 
     /**
@@ -480,19 +496,21 @@
         }
     }
 
-    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs,
-            String callingPackage) {
+    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         try {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectAll()
                     .penaltyDeath()
                     .build());
-            mBindingPkg = callingPackage;
             return onBindSlice(sliceUri, supportedSpecs);
         } finally {
-            mBindingPkg = null;
             StrictMode.setThreadPolicy(oldPolicy);
         }
     }
+
+    private final Runnable mAnr = () -> {
+        Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
+        Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
+    };
 }
diff --git a/android/app/slice/SliceSpec.java b/android/app/slice/SliceSpec.java
index 8cc0384..03ffe6d 100644
--- a/android/app/slice/SliceSpec.java
+++ b/android/app/slice/SliceSpec.java
@@ -89,7 +89,7 @@
      *
      * @param candidate candidate format of data.
      * @return true if versions are compatible.
-     * @see androidx.app.slice.widget.SliceView
+     * @see androidx.slice.widget.SliceView
      */
     public boolean canRender(@NonNull SliceSpec candidate) {
         if (!mType.equals(candidate.mType)) return false;
diff --git a/android/app/timezone/RulesManager.java b/android/app/timezone/RulesManager.java
index 0a38eb9..fe83113 100644
--- a/android/app/timezone/RulesManager.java
+++ b/android/app/timezone/RulesManager.java
@@ -36,7 +36,7 @@
  * <p>This interface is intended for use with the default APK-based time zone rules update
  * application but it can also be used by OEMs if that mechanism is turned off using configuration.
  * All callers must possess the {@link android.Manifest.permission#UPDATE_TIME_ZONE_RULES} system
- * permission.
+ * permission unless otherwise stated.
  *
  * <p>When using the default mechanism, when properly configured the Android system will send a
  * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent with a
@@ -68,6 +68,23 @@
     private static final String TAG = "timezone.RulesManager";
     private static final boolean DEBUG = false;
 
+    /**
+     * The action of the intent that the Android system will broadcast when a time zone rules update
+     * operation has been successfully staged  (i.e. to be applied next reboot) or unstaged.
+     *
+     * <p>See {@link #EXTRA_OPERATION_STAGED}
+     *
+     * <p>This is a protected intent that can only be sent by the system.
+     */
+    public static final String ACTION_RULES_UPDATE_OPERATION =
+            "com.android.intent.action.timezone.RULES_UPDATE_OPERATION";
+
+    /**
+     * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to
+     * indicate whether the operation was a "stage" or an "unstage".
+     */
+    public static final String EXTRA_OPERATION_STAGED = "staged";
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
             SUCCESS,
@@ -103,9 +120,12 @@
 
     /**
      * Returns information about the current time zone rules state such as the IANA version of
-     * the system and any currently installed distro. This method is intended to allow clients to
-     * determine if the current state can be improved; for example by passing the information to a
-     * server that may provide a new distro for download.
+     * the system and any currently installed distro. This method allows clients to determine the
+     * current device state, perhaps to see if it can be improved; for example by passing the
+     * information to a server that may provide a new distro for download.
+     *
+     * <p>Callers must possess the {@link android.Manifest.permission#QUERY_TIME_ZONE_RULES} system
+     * permission.
      */
     public RulesState getRulesState() {
         try {
diff --git a/android/app/usage/AppStandbyInfo.java b/android/app/usage/AppStandbyInfo.java
new file mode 100644
index 0000000..51fe0e2
--- /dev/null
+++ b/android/app/usage/AppStandbyInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A pair of {package, bucket} to denote the app standby bucket for a given package.
+ * Used as a vehicle of data across the binder IPC.
+ * @hide
+ */
+public final class AppStandbyInfo implements Parcelable {
+
+    public String mPackageName;
+    public @UsageStatsManager.StandbyBuckets int mStandbyBucket;
+
+    private AppStandbyInfo(Parcel in) {
+        mPackageName = in.readString();
+        mStandbyBucket = in.readInt();
+    }
+
+    public AppStandbyInfo(String packageName, int bucket) {
+        mPackageName = packageName;
+        mStandbyBucket = bucket;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPackageName);
+        dest.writeInt(mStandbyBucket);
+    }
+
+    public static final Creator<AppStandbyInfo> CREATOR = new Creator<AppStandbyInfo>() {
+        @Override
+        public AppStandbyInfo createFromParcel(Parcel source) {
+            return new AppStandbyInfo(source);
+        }
+
+        @Override
+        public AppStandbyInfo[] newArray(int size) {
+            return new AppStandbyInfo[size];
+        }
+    };
+}
diff --git a/android/app/usage/EventStats.java b/android/app/usage/EventStats.java
new file mode 100644
index 0000000..ea95a05
--- /dev/null
+++ b/android/app/usage/EventStats.java
@@ -0,0 +1,181 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package android.app.usage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains usage statistics for an event type for a specific
+ * time range.
+ */
+public final class EventStats implements Parcelable {
+
+    /**
+     * {@hide}
+     */
+    public int mEventType;
+
+    /**
+     * {@hide}
+     */
+    public long mBeginTimeStamp;
+
+    /**
+     * {@hide}
+     */
+    public long mEndTimeStamp;
+
+    /**
+     * {@hide}
+     */
+    public long mLastEventTime;
+
+    /**
+     * {@hide}
+     */
+    public long mTotalTime;
+
+    /**
+     * {@hide}
+     */
+    public int mCount;
+
+    /**
+     * {@hide}
+     */
+    public EventStats() {
+    }
+
+    public EventStats(EventStats stats) {
+        mEventType = stats.mEventType;
+        mBeginTimeStamp = stats.mBeginTimeStamp;
+        mEndTimeStamp = stats.mEndTimeStamp;
+        mLastEventTime = stats.mLastEventTime;
+        mTotalTime = stats.mTotalTime;
+        mCount = stats.mCount;
+    }
+
+    /**
+     * Return the type of event this is usage for.  May be one of the event
+     * constants in {@link UsageEvents.Event}.
+     */
+    public int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * Get the beginning of the time range this {@link android.app.usage.EventStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getFirstTimeStamp() {
+        return mBeginTimeStamp;
+    }
+
+    /**
+     * Get the end of the time range this {@link android.app.usage.EventStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeStamp() {
+        return mEndTimeStamp;
+    }
+
+    /**
+     * Get the last time this event triggered, measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastEventTime() {
+        return mLastEventTime;
+    }
+
+    /**
+     * Return the number of times that this event occurred over the interval.
+     */
+    public int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Get the total time this event was active, measured in milliseconds.
+     */
+    public long getTotalTime() {
+        return mTotalTime;
+    }
+
+    /**
+     * Add the statistics from the right {@link EventStats} to the left. The event type for
+     * both {@link UsageStats} objects must be the same.
+     * @param right The {@link EventStats} object to merge into this one.
+     * @throws java.lang.IllegalArgumentException if the event types of the two
+     *         {@link UsageStats} objects are different.
+     */
+    public void add(EventStats right) {
+        if (mEventType != right.mEventType) {
+            throw new IllegalArgumentException("Can't merge EventStats for event #"
+                    + mEventType + " with EventStats for event #" + right.mEventType);
+        }
+
+        // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
+        // regards to their mEndTimeStamp.
+        if (right.mBeginTimeStamp > mBeginTimeStamp) {
+            mLastEventTime = Math.max(mLastEventTime, right.mLastEventTime);
+        }
+        mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
+        mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
+        mTotalTime += right.mTotalTime;
+        mCount += right.mCount;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mEventType);
+        dest.writeLong(mBeginTimeStamp);
+        dest.writeLong(mEndTimeStamp);
+        dest.writeLong(mLastEventTime);
+        dest.writeLong(mTotalTime);
+        dest.writeInt(mCount);
+    }
+
+    public static final Creator<EventStats> CREATOR = new Creator<EventStats>() {
+        @Override
+        public EventStats createFromParcel(Parcel in) {
+            EventStats stats = new EventStats();
+            stats.mEventType = in.readInt();
+            stats.mBeginTimeStamp = in.readLong();
+            stats.mEndTimeStamp = in.readLong();
+            stats.mLastEventTime = in.readLong();
+            stats.mTotalTime = in.readLong();
+            stats.mCount = in.readInt();
+            return stats;
+        }
+
+        @Override
+        public EventStats[] newArray(int size) {
+            return new EventStats[size];
+        }
+    };
+}
diff --git a/android/app/usage/NetworkStats.java b/android/app/usage/NetworkStats.java
index da36157..7252f02 100644
--- a/android/app/usage/NetworkStats.java
+++ b/android/app/usage/NetworkStats.java
@@ -24,7 +24,6 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.IntArray;
 import android.util.Log;
 
@@ -69,6 +68,11 @@
     private int mTag = android.net.NetworkStats.TAG_NONE;
 
     /**
+     * State in case it was not specified in the query.
+     */
+    private int mState = Bucket.STATE_ALL;
+
+    /**
      * The session while the query requires it, null if all the stats have been collected or close()
      * has been called.
      */
@@ -98,9 +102,8 @@
 
     /** @hide */
     NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
-            long endTimestamp) throws RemoteException, SecurityException {
-        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+            long endTimestamp, INetworkStatsService statsService)
+            throws RemoteException, SecurityException {
         // Open network stats session
         mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
         mCloseGuard.open("close");
@@ -269,6 +272,15 @@
         private long mTxBytes;
         private long mTxPackets;
 
+        private static int convertSet(@State int state) {
+            switch (state) {
+                case STATE_ALL: return android.net.NetworkStats.SET_ALL;
+                case STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
+                case STATE_FOREGROUND: return android.net.NetworkStats.SET_FOREGROUND;
+            }
+            return 0;
+        }
+
         private static @State int convertState(int networkStatsSet) {
             switch (networkStatsSet) {
                 case android.net.NetworkStats.SET_ALL : return STATE_ALL;
@@ -529,20 +541,13 @@
     /**
      * Collects history results for uid and resets history enumeration index.
      */
-    void startHistoryEnumeration(int uid) {
-        startHistoryEnumeration(uid, android.net.NetworkStats.TAG_NONE);
-    }
-
-    /**
-     * Collects history results for uid and resets history enumeration index.
-     */
-    void startHistoryEnumeration(int uid, int tag) {
+    void startHistoryEnumeration(int uid, int tag, int state) {
         mHistory = null;
         try {
             mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
-                    android.net.NetworkStats.SET_ALL, tag,
-                    NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
-            setSingleUidTag(uid, tag);
+                    Bucket.convertSet(state), tag, NetworkStatsHistory.FIELD_ALL,
+                    mStartTimeStamp, mEndTimeStamp);
+            setSingleUidTagState(uid, tag, state);
         } catch (RemoteException e) {
             Log.w(TAG, e);
             // Leaving mHistory null
@@ -638,6 +643,7 @@
         fillBucketFromSummaryEntry(bucket);
         return bucket;
     }
+
     /**
      * Getting the next item in a history enumeration.
      * @param bucketOut Next item will be set here.
@@ -650,7 +656,7 @@
                         mRecycledHistoryEntry);
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
-                bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mState = mState;
                 bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
                 bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
@@ -693,9 +699,10 @@
         return mUidOrUidIndex;
     }
 
-    private void setSingleUidTag(int uid, int tag) {
+    private void setSingleUidTagState(int uid, int tag, int state) {
         mUidOrUidIndex = uid;
         mTag = tag;
+        mState = state;
     }
 
     private void stepUid() {
diff --git a/android/app/usage/NetworkStatsManager.java b/android/app/usage/NetworkStatsManager.java
index 5576e86..b2fe958 100644
--- a/android/app/usage/NetworkStatsManager.java
+++ b/android/app/usage/NetworkStatsManager.java
@@ -37,6 +37,8 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Provides access to network usage history and statistics. Usage data is collected in
  * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
@@ -107,9 +109,15 @@
      * {@hide}
      */
     public NetworkStatsManager(Context context) throws ServiceNotFoundException {
+        this(context, INetworkStatsService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public NetworkStatsManager(Context context, INetworkStatsService service) {
         mContext = context;
-        mService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
+        mService = service;
         setPollOnOpen(true);
     }
 
@@ -135,7 +143,8 @@
     public Bucket querySummaryForDevice(NetworkTemplate template,
             long startTime, long endTime) throws SecurityException, RemoteException {
         Bucket bucket = null;
-        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime,
+                mService);
         bucket = stats.getDeviceSummaryForNetwork();
 
         stats.close();
@@ -208,7 +217,7 @@
         }
 
         NetworkStats stats;
-        stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
         stats.startSummaryEnumeration();
 
         stats.close();
@@ -245,7 +254,7 @@
         }
 
         NetworkStats result;
-        result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
         result.startSummaryEnumeration();
 
         return result;
@@ -254,20 +263,31 @@
     /**
      * Query network usage statistics details for a given uid.
      *
-     * #see queryDetailsForUidTag(int, String, long, long, int, int)
+     * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
      */
     public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
-            long startTime, long endTime, int uid) throws SecurityException, RemoteException {
-        return queryDetailsForUidTag(networkType, subscriberId, startTime, endTime, uid,
-            NetworkStats.Bucket.TAG_NONE);
+            long startTime, long endTime, int uid) throws SecurityException {
+        return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
+            NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
     }
 
     /**
-     * Query network usage statistics details for a given uid and tag. Only usable for uids
-     * belonging to calling user. Result is aggregated over state but not aggregated over time.
-     * This means buckets' start and end timestamps are going to be between 'startTime' and
-     * 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
-     * same as the 'uid' parameter and tag the same as 'tag' parameter.
+     * Query network usage statistics details for a given uid and tag.
+     *
+     * #see queryDetailsForUidTagState(int, String, long, long, int, int, int)
+     */
+    public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
+            long startTime, long endTime, int uid, int tag) throws SecurityException {
+        return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
+            tag, NetworkStats.Bucket.STATE_ALL);
+    }
+
+    /**
+     * Query network usage statistics details for a given uid, tag, and state. Only usable for uids
+     * belonging to calling user. Result is not aggregated over time. This means buckets' start and
+     * end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going
+     * to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state
+     * the same as the 'state' parameter.
      * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
      * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
      * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
@@ -288,17 +308,18 @@
      * @return Statistics object or null if an error happened during statistics collection.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
-    public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
-            long startTime, long endTime, int uid, int tag) throws SecurityException {
+    public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
+            long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
         NetworkTemplate template;
         template = createTemplate(networkType, subscriberId);
 
         NetworkStats result;
         try {
-            result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
-            result.startHistoryEnumeration(uid, tag);
+            result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startHistoryEnumeration(uid, tag, state);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e);
+            Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag
+                    + " state=" + state, e);
             return null;
         }
 
@@ -341,7 +362,7 @@
         }
 
         NetworkStats result;
-        result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
         result.startUserUidEnumeration();
         return result;
     }
@@ -451,19 +472,20 @@
     }
 
     private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
-        NetworkTemplate template = null;
+        final NetworkTemplate template;
         switch (networkType) {
-            case ConnectivityManager.TYPE_MOBILE: {
-                template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
-                } break;
-            case ConnectivityManager.TYPE_WIFI: {
+            case ConnectivityManager.TYPE_MOBILE:
+                template = subscriberId == null
+                        ? NetworkTemplate.buildTemplateMobileWildcard()
+                        : NetworkTemplate.buildTemplateMobileAll(subscriberId);
+                break;
+            case ConnectivityManager.TYPE_WIFI:
                 template = NetworkTemplate.buildTemplateWifiWildcard();
-                } break;
-            default: {
+                break;
+            default:
                 throw new IllegalArgumentException("Cannot create template for network type "
                         + networkType + ", subscriberId '"
                         + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
-            }
         }
         return template;
     }
diff --git a/android/app/usage/TimeSparseArray.java b/android/app/usage/TimeSparseArray.java
index 7974fa7..9ef88e4 100644
--- a/android/app/usage/TimeSparseArray.java
+++ b/android/app/usage/TimeSparseArray.java
@@ -17,6 +17,7 @@
 package android.app.usage;
 
 import android.util.LongSparseArray;
+import android.util.Slog;
 
 /**
  * An array that indexes by a long timestamp, representing milliseconds since the epoch.
@@ -24,6 +25,8 @@
  * {@hide}
  */
 public class TimeSparseArray<E> extends LongSparseArray<E> {
+    private static final String TAG = TimeSparseArray.class.getSimpleName();
+
     public TimeSparseArray() {
         super();
     }
@@ -70,6 +73,30 @@
     }
 
     /**
+     * {@inheritDoc}
+     *
+     * Overridden to ensure no collisions. The key (time in milliseconds) is incremented till an
+     * empty place is found.
+     */
+    @Override
+    public void put(long key, E value) {
+        final long origKey = key;
+        int keyIndex = indexOfKey(key);
+        if (keyIndex >= 0) {
+            final long sz = size();
+            while (keyIndex < sz && keyAt(keyIndex) == key) {
+                key++;
+                keyIndex++;
+            }
+            if (key >= origKey + 10) {
+                Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
+                        + " displaced to " + key);
+            }
+        }
+        super.put(key, value);
+    }
+
+    /**
      * Finds the index of the first element whose timestamp is less than or equal to
      * the given time.
      *
diff --git a/android/app/usage/UsageEvents.java b/android/app/usage/UsageEvents.java
index edb992b..84f57a3 100644
--- a/android/app/usage/UsageEvents.java
+++ b/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
 package android.app.usage;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.content.res.Configuration;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -80,6 +81,7 @@
          * An event type denoting that a package was interacted with in some way by the system.
          * @hide
          */
+        @SystemApi
         public static final int SYSTEM_INTERACTION = 6;
 
         /**
@@ -104,14 +106,64 @@
          * An event type denoting that a notification was viewed by the user.
          * @hide
          */
+        @SystemApi
         public static final int NOTIFICATION_SEEN = 10;
 
         /**
-         * An event type denoting a change in App Standby Bucket.
-         * @hide
+         * An event type denoting a change in App Standby Bucket. The new bucket can be
+         * retrieved by calling {@link #getStandbyBucket()}.
+         *
+         * @see UsageStatsManager#getAppStandbyBucket()
          */
         public static final int STANDBY_BUCKET_CHANGED = 11;
 
+        /**
+         * An event type denoting that an app posted an interruptive notification. Visual and
+         * audible interruptions are included.
+         * @hide
+         */
+        @SystemApi
+        public static final int NOTIFICATION_INTERRUPTION = 12;
+
+        /**
+         * A Slice was pinned by the default launcher or the default assistant.
+         * @hide
+         */
+        @SystemApi
+        public static final int SLICE_PINNED_PRIV = 13;
+
+        /**
+         * A Slice was pinned by an app.
+         * @hide
+         */
+        @SystemApi
+        public static final int SLICE_PINNED = 14;
+
+        /**
+         * An event type denoting that the screen has gone in to an interactive state (turned
+         * on for full user interaction, not ambient display or other non-interactive state).
+         */
+        public static final int SCREEN_INTERACTIVE = 15;
+
+        /**
+         * An event type denoting that the screen has gone in to a non-interactive state
+         * (completely turned off or turned on only in a non-interactive state like ambient
+         * display).
+         */
+        public static final int SCREEN_NON_INTERACTIVE = 16;
+
+        /**
+         * An event type denoting that the screen's keyguard has been shown, whether or not
+         * the screen is off.
+         */
+        public static final int KEYGUARD_SHOWN = 17;
+
+        /**
+         * An event type denoting that the screen's keyguard has been hidden.  This typically
+         * happens when the user unlocks their phone after turning it on.
+         */
+        public static final int KEYGUARD_HIDDEN = 18;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
@@ -177,11 +229,20 @@
         public String[] mContentAnnotations;
 
         /**
-         * The app standby bucket assigned.
+         * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
+         * is the low order 16 bits.
          * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
          * {@hide}
          */
-        public int mBucket;
+        public int mBucketAndReason;
+
+        /**
+         * The id of the {@link android.app.NotificationChannel} to which an interruptive
+         * notification was posted.
+         * Only present for {@link #NOTIFICATION_INTERRUPTION} event types.
+         * {@hide}
+         */
+        public String mNotificationChannelId;
 
         /** @hide */
         @EventFlags
@@ -202,7 +263,8 @@
             mContentType = orig.mContentType;
             mContentAnnotations = orig.mContentAnnotations;
             mFlags = orig.mFlags;
-            mBucket = orig.mBucket;
+            mBucketAndReason = orig.mBucketAndReason;
+            mNotificationChannelId = orig.mNotificationChannelId;
         }
 
         /**
@@ -232,8 +294,11 @@
         /**
          * The event type.
          *
-         * See {@link #MOVE_TO_BACKGROUND}
-         * See {@link #MOVE_TO_FOREGROUND}
+         * @see #MOVE_TO_BACKGROUND
+         * @see #MOVE_TO_FOREGROUND
+         * @see #CONFIGURATION_CHANGE
+         * @see #USER_INTERACTION
+         * @see #STANDBY_BUCKET_CHANGED
          */
         public int getEventType() {
             return mEventType;
@@ -257,6 +322,38 @@
             return mShortcutId;
         }
 
+        /**
+         * Returns the standby bucket of the app, if the event is of type
+         * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+         * @return the standby bucket associated with the event.
+         *
+         */
+        public int getStandbyBucket() {
+            return (mBucketAndReason & 0xFFFF0000) >>> 16;
+        }
+
+        /**
+         * Returns the reason for the bucketing, if the event is of type
+         * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
+         * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
+         * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
+         * is REASON_MAIN_USAGE.
+         * @hide
+         */
+        public int getStandbyReason() {
+            return mBucketAndReason & 0x0000FFFF;
+        }
+
+        /**
+         * Returns the ID of the {@link android.app.NotificationChannel} for this event if the
+         * event is of type {@link #NOTIFICATION_INTERRUPTION}, otherwise it returns null;
+         * @hide
+         */
+        @SystemApi
+        public String getNotificationChannelId() {
+            return mNotificationChannelId;
+        }
+
         /** @hide */
         public Event getObfuscatedIfInstantApp() {
             if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
@@ -414,7 +511,10 @@
                 p.writeStringArray(event.mContentAnnotations);
                 break;
             case Event.STANDBY_BUCKET_CHANGED:
-                p.writeInt(event.mBucket);
+                p.writeInt(event.mBucketAndReason);
+                break;
+            case Event.NOTIFICATION_INTERRUPTION:
+                p.writeString(event.mNotificationChannelId);
                 break;
         }
     }
@@ -445,6 +545,7 @@
         eventOut.mAction = null;
         eventOut.mContentType = null;
         eventOut.mContentAnnotations = null;
+        eventOut.mNotificationChannelId = null;
 
         switch (eventOut.mEventType) {
             case Event.CONFIGURATION_CHANGE:
@@ -460,7 +561,10 @@
                 eventOut.mContentAnnotations = p.createStringArray();
                 break;
             case Event.STANDBY_BUCKET_CHANGED:
-                eventOut.mBucket = p.readInt();
+                eventOut.mBucketAndReason = p.readInt();
+                break;
+            case Event.NOTIFICATION_INTERRUPTION:
+                eventOut.mNotificationChannelId = p.readString();
                 break;
         }
     }
diff --git a/android/app/usage/UsageStats.java b/android/app/usage/UsageStats.java
index 7eef85c..2b14841 100644
--- a/android/app/usage/UsageStats.java
+++ b/android/app/usage/UsageStats.java
@@ -16,6 +16,7 @@
 
 package android.app.usage;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -61,6 +62,11 @@
     /**
      * {@hide}
      */
+    public int mAppLaunchCount;
+
+    /**
+     * {@hide}
+     */
     public int mLastEvent;
 
     /**
@@ -81,6 +87,7 @@
         mLastTimeUsed = stats.mLastTimeUsed;
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
+        mAppLaunchCount = stats.mAppLaunchCount;
         mLastEvent = stats.mLastEvent;
         mChooserCounts = stats.mChooserCounts;
     }
@@ -137,6 +144,16 @@
     }
 
     /**
+     * Returns the number of times the app was launched as an activity from outside of the app.
+     * Excludes intra-app activity transitions.
+     * @hide
+     */
+    @SystemApi
+    public int getAppLaunchCount() {
+        return mAppLaunchCount;
+    }
+
+    /**
      * Add the statistics from the right {@link UsageStats} to the left. The package name for
      * both {@link UsageStats} objects must be the same.
      * @param right The {@link UsageStats} object to merge into this one.
@@ -161,6 +178,7 @@
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
         mLaunchCount += right.mLaunchCount;
+        mAppLaunchCount += right.mAppLaunchCount;
         if (mChooserCounts == null) {
             mChooserCounts = right.mChooserCounts;
         } else if (right.mChooserCounts != null) {
@@ -196,6 +214,7 @@
         dest.writeLong(mLastTimeUsed);
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
+        dest.writeInt(mAppLaunchCount);
         dest.writeInt(mLastEvent);
         Bundle allCounts = new Bundle();
         if (mChooserCounts != null) {
@@ -224,6 +243,7 @@
             stats.mLastTimeUsed = in.readLong();
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
+            stats.mAppLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
             Bundle allCounts = in.readBundle();
             if (allCounts != null) {
diff --git a/android/app/usage/UsageStatsManager.java b/android/app/usage/UsageStatsManager.java
index edb6a74..7fb97d3 100644
--- a/android/app/usage/UsageStatsManager.java
+++ b/android/app/usage/UsageStatsManager.java
@@ -17,9 +17,11 @@
 package android.app.usage;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
@@ -28,9 +30,11 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Provides access to device usage history and statistics. Usage data is aggregated into
@@ -51,10 +55,13 @@
  * </pre>
  * A request for data in the middle of a time interval will include that interval.
  * <p/>
- * <b>NOTE:</b> This API requires the permission android.permission.PACKAGE_USAGE_STATS.
- * However, declaring the permission implies intention to use the API and the user of the device
- * still needs to grant permission through the Settings application.
- * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS}
+ * <b>NOTE:</b> Most methods on this API require the permission
+ * android.permission.PACKAGE_USAGE_STATS. However, declaring the permission implies intention to
+ * use the API and the user of the device still needs to grant permission through the Settings
+ * application.
+ * See {@link android.provider.Settings#ACTION_USAGE_ACCESS_SETTINGS}.
+ * Methods which only return the information for the calling package do not require this permission.
+ * E.g. {@link #getAppStandbyBucket()} and {@link #queryEventsForSelf(long, long)}.
  */
 @SystemService(Context.USAGE_STATS_SERVICE)
 public final class UsageStatsManager {
@@ -101,25 +108,35 @@
     public static final int STANDBY_BUCKET_EXEMPTED = 5;
 
     /**
-     * The app was used very recently, currently in use or likely to be used very soon.
+     * The app was used very recently, currently in use or likely to be used very soon. Standby
+     * bucket values that are &le; {@link #STANDBY_BUCKET_ACTIVE} will not be throttled by the
+     * system while they are in this bucket. Buckets &gt; {@link #STANDBY_BUCKET_ACTIVE} will most
+     * likely be restricted in some way. For instance, jobs and alarms may be deferred.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_ACTIVE = 10;
 
     /**
-     * The app was used recently and/or likely to be used in the next few hours.
+     * The app was used recently and/or likely to be used in the next few hours. Restrictions will
+     * apply to these apps, such as deferral of jobs and alarms.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_WORKING_SET = 20;
 
     /**
      * The app was used in the last few days and/or likely to be used in the next few days.
+     * Restrictions will apply to these apps, such as deferral of jobs and alarms. The delays may be
+     * greater than for apps in higher buckets (lower bucket value). Bucket values &gt;
+     * {@link #STANDBY_BUCKET_FREQUENT} may additionally have network access limited.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_FREQUENT = 30;
 
     /**
      * The app has not be used for several days and/or is unlikely to be used for several days.
+     * Apps in this bucket will have the most restrictions, including network restrictions, except
+     * during certain short periods (at a minimum, once a day) when they are allowed to execute
+     * jobs, access the network, etc.
      * @see #getAppStandbyBucket()
      */
     public static final int STANDBY_BUCKET_RARE = 40;
@@ -131,24 +148,47 @@
     @SystemApi
     public static final int STANDBY_BUCKET_NEVER = 50;
 
-    /** {@hide} Reason for bucketing -- default initial state */
-    public static final String REASON_DEFAULT = "default";
+    /** @hide */
+    public static final int REASON_MAIN_MASK = 0xFF00;
+    /** @hide */
+    public static final int REASON_MAIN_DEFAULT =   0x0100;
+    /** @hide */
+    public static final int REASON_MAIN_TIMEOUT =   0x0200;
+    /** @hide */
+    public static final int REASON_MAIN_USAGE =     0x0300;
+    /** @hide */
+    public static final int REASON_MAIN_FORCED =    0x0400;
+    /** @hide */
+    public static final int REASON_MAIN_PREDICTED = 0x0500;
 
-    /** {@hide} Reason for bucketing -- timeout */
-    public static final String REASON_TIMEOUT = "timeout";
+    /** @hide */
+    public static final int REASON_SUB_MASK = 0x00FF;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN  = 0x0002;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_USER_INTERACTION   = 0x0003;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SYSTEM_UPDATE      = 0x0006;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT     = 0x0007;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SYNC_ADAPTER       = 0x0008;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SLICE_PINNED       = 0x0009;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV  = 0x000A;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000B;
 
-    /** {@hide} Reason for bucketing -- usage */
-    public static final String REASON_USAGE = "usage";
+    /** @hide */
+    public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
 
-    /** {@hide} Reason for bucketing -- forced by user / shell command */
-    public static final String REASON_FORCED = "forced";
-
-    /**
-     * {@hide}
-     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
-     * be appended.
-     */
-    public static final String REASON_PREDICTED = "predicted";
 
     /** @hide */
     @IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -162,6 +202,31 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StandbyBuckets {}
 
+    /**
+     * Observer id of the registered observer for the group of packages that reached the usage
+     * time limit. Included as an extra in the PendingIntent that was registered.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_OBSERVER_ID = "android.app.usage.extra.OBSERVER_ID";
+
+    /**
+     * Original time limit in milliseconds specified by the registered observer for the group of
+     * packages that reached the usage time limit. Included as an extra in the PendingIntent that
+     * was registered.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_TIME_LIMIT = "android.app.usage.extra.TIME_LIMIT";
+
+    /**
+     * Actual usage time in milliseconds for the group of packages that reached the specified time
+     * limit. Included as an extra in the PendingIntent that was registered.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
+
     private static final UsageEvents sEmptyResults = new UsageEvents();
 
     private final Context mContext;
@@ -192,6 +257,8 @@
      * 2014 - com.example.charlie
      * </pre>
      *
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
+     *
      * @param intervalType The time interval by which the stats are aggregated.
      * @param beginTime The inclusive beginning of the range of stats to include in the results.
      * @param endTime The exclusive end of the range of stats to include in the results.
@@ -221,6 +288,7 @@
      * Gets the hardware configurations the device was in for the given time range, aggregated by
      * the specified interval. The results are ordered as in
      * {@link #queryUsageStats(int, long, long)}.
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
      *
      * @param intervalType The time interval by which the stats are aggregated.
      * @param beginTime The inclusive beginning of the range of stats to include in the results.
@@ -243,8 +311,49 @@
     }
 
     /**
+     * Gets aggregated event stats for the given time range, aggregated by the specified interval.
+     * <p>The returned list will contain a {@link EventStats} object for each event type that
+     * is being aggregated and has data for an interval that is a subset of the time range given.
+     *
+     * <p>The current event types that will be aggregated here are:</p>
+     * <ul>
+     *     <li>{@link UsageEvents.Event#SCREEN_INTERACTIVE}</li>
+     *     <li>{@link UsageEvents.Event#SCREEN_NON_INTERACTIVE}</li>
+     *     <li>{@link UsageEvents.Event#KEYGUARD_SHOWN}</li>
+     *     <li>{@link UsageEvents.Event#KEYGUARD_HIDDEN}</li>
+     * </ul>
+     *
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
+     *
+     * @param intervalType The time interval by which the stats are aggregated.
+     * @param beginTime The inclusive beginning of the range of stats to include in the results.
+     * @param endTime The exclusive end of the range of stats to include in the results.
+     * @return A list of {@link EventStats}
+     *
+     * @see #INTERVAL_DAILY
+     * @see #INTERVAL_WEEKLY
+     * @see #INTERVAL_MONTHLY
+     * @see #INTERVAL_YEARLY
+     * @see #INTERVAL_BEST
+     */
+    public List<EventStats> queryEventStats(int intervalType, long beginTime, long endTime) {
+        try {
+            @SuppressWarnings("unchecked")
+            ParceledListSlice<EventStats> slice = mService.queryEventStats(intervalType, beginTime,
+                    endTime, mContext.getOpPackageName());
+            if (slice != null) {
+                return slice.getList();
+            }
+        } catch (RemoteException e) {
+            // fallthrough and return the empty list.
+        }
+        return Collections.emptyList();
+    }
+
+    /**
      * Query for events in the given time range. Events are only kept by the system for a few
      * days.
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
      *
      * @param beginTime The inclusive beginning of the range of events to include in the results.
      * @param endTime The exclusive end of the range of events to include in the results.
@@ -264,9 +373,32 @@
     }
 
     /**
+     * Like {@link #queryEvents(long, long)}, but only returns events for the calling package.
+     *
+     * @param beginTime The inclusive beginning of the range of events to include in the results.
+     * @param endTime The exclusive end of the range of events to include in the results.
+     * @return A {@link UsageEvents} object.
+     *
+     * @see #queryEvents(long, long)
+     */
+    public UsageEvents queryEventsForSelf(long beginTime, long endTime) {
+        try {
+            final UsageEvents events = mService.queryEventsForPackage(beginTime, endTime,
+                    mContext.getOpPackageName());
+            if (events != null) {
+                return events;
+            }
+        } catch (RemoteException e) {
+            // fallthrough
+        }
+        return sEmptyResults;
+    }
+
+    /**
      * A convenience method that queries for all stats in the given range (using the best interval
      * for that range), merges the resulting data, and keys it by package name.
      * See {@link #queryUsageStats(int, long, long)}.
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
      *
      * @param beginTime The inclusive beginning of the range of stats to include in the results.
      * @param endTime The exclusive end of the range of stats to include in the results.
@@ -301,7 +433,7 @@
      */
     public boolean isAppInactive(String packageName) {
         try {
-            return mService.isAppInactive(packageName, UserHandle.myUserId());
+            return mService.isAppInactive(packageName, mContext.getUserId());
         } catch (RemoteException e) {
             // fall through and return default
         }
@@ -313,7 +445,7 @@
      */
     public void setAppInactive(String packageName, boolean inactive) {
         try {
-            mService.setAppInactive(packageName, inactive, UserHandle.myUserId());
+            mService.setAppInactive(packageName, inactive, mContext.getUserId());
         } catch (RemoteException e) {
             // fall through
         }
@@ -322,11 +454,19 @@
     /**
      * Returns the current standby bucket of the calling app. The system determines the standby
      * state of the app based on app usage patterns. Standby buckets determine how much an app will
-     * be restricted from running background tasks such as jobs, alarms and certain PendingIntent
-     * callbacks.
+     * be restricted from running background tasks such as jobs and alarms.
      * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
      * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
      * restrictive. The battery level of the device might also affect the restrictions.
+     * <p>Apps in buckets &le; {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed.
+     * Apps in buckets &gt; {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when
+     * running in the background.
+     * <p>The standby state of an app can change at any time either due to a user interaction or a
+     * system interaction or some algorithm determining that the app can be restricted for a period
+     * of time before the user has a need for it.
+     * <p>You can also query the recent history of standby bucket changes by calling
+     * {@link #queryEventsForSelf(long, long)} and searching for
+     * {@link UsageEvents.Event#STANDBY_BUCKET_CHANGED}.
      *
      * @return the current standby bucket of the calling app. One of STANDBY_BUCKET_* constants.
      */
@@ -388,8 +528,16 @@
     @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     public Map<String, Integer> getAppStandbyBuckets() {
         try {
-            return (Map<String, Integer>) mService.getAppStandbyBuckets(
+            final ParceledListSlice<AppStandbyInfo> slice = mService.getAppStandbyBuckets(
                     mContext.getOpPackageName(), mContext.getUserId());
+            final List<AppStandbyInfo> bucketList = slice.getList();
+            final ArrayMap<String, Integer> bucketMap = new ArrayMap<>();
+            final int n = bucketList.size();
+            for (int i = 0; i < n; i++) {
+                final AppStandbyInfo bucketInfo = bucketList.get(i);
+                bucketMap.put(bucketInfo.mPackageName, bucketInfo.mStandbyBucket);
+            }
+            return bucketMap;
         } catch (RemoteException e) {
         }
         return Collections.EMPTY_MAP;
@@ -404,13 +552,134 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE)
     public void setAppStandbyBuckets(Map<String, Integer> appBuckets) {
+        if (appBuckets == null) {
+            return;
+        }
+        final List<AppStandbyInfo> bucketInfoList = new ArrayList<>(appBuckets.size());
+        for (Map.Entry<String, Integer> bucketEntry : appBuckets.entrySet()) {
+            bucketInfoList.add(new AppStandbyInfo(bucketEntry.getKey(), bucketEntry.getValue()));
+        }
+        final ParceledListSlice<AppStandbyInfo> slice = new ParceledListSlice<>(bucketInfoList);
         try {
-            mService.setAppStandbyBuckets(appBuckets, mContext.getUserId());
+            mService.setAppStandbyBuckets(slice, mContext.getUserId());
         } catch (RemoteException e) {
         }
     }
 
     /**
+     * @hide
+     * Register an app usage limit observer that receives a callback on the provided intent when
+     * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The
+     * observer will automatically be unregistered when the time limit is reached and the intent
+     * is delivered. Registering an {@code observerId} that was already registered will override
+     * the previous one.
+     * @param observerId A unique id associated with the group of apps to be monitored. There can
+     *                  be multiple groups with common packages and different time limits.
+     * @param packages The list of packages to observe for foreground activity time. Cannot be null
+     *                 and must include at least one package.
+     * @param timeLimit The total time the set of apps can be in the foreground before the
+     *                  callbackIntent is delivered. Must be greater than 0.
+     * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
+     * @param callbackIntent The PendingIntent that will be dispatched when the time limit is
+     *                       exceeded by the group of apps. The delivered Intent will also contain
+     *                       the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
+     *                       {@link #EXTRA_TIME_USED}. Cannot be null.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or
+     *                           is not the profile owner of this user.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,
+            @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+        try {
+            mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
+                    callbackIntent, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * @hide
+     * Unregister the app usage observer specified by the {@code observerId}. This will only apply
+     * to any observer registered by this application. Unregistering an observer that was already
+     * unregistered or never registered will have no effect.
+     * @param observerId The id of the observer that was previously registered.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or is
+     *                           not the profile owner of this user.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void unregisterAppUsageObserver(int observerId) {
+        try {
+            mService.unregisterAppUsageObserver(observerId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+        }
+    }
+
+    /** @hide */
+    public static String reasonToString(int standbyReason) {
+        StringBuilder sb = new StringBuilder();
+        switch (standbyReason & REASON_MAIN_MASK) {
+            case REASON_MAIN_DEFAULT:
+                sb.append("d");
+                break;
+            case REASON_MAIN_FORCED:
+                sb.append("f");
+                break;
+            case REASON_MAIN_PREDICTED:
+                sb.append("p");
+                switch (standbyReason & REASON_SUB_MASK) {
+                    case REASON_SUB_PREDICTED_RESTORED:
+                        sb.append("-r");
+                        break;
+                }
+                break;
+            case REASON_MAIN_TIMEOUT:
+                sb.append("t");
+                break;
+            case REASON_MAIN_USAGE:
+                sb.append("u");
+                switch (standbyReason & REASON_SUB_MASK) {
+                    case REASON_SUB_USAGE_SYSTEM_INTERACTION:
+                        sb.append("-si");
+                        break;
+                    case REASON_SUB_USAGE_NOTIFICATION_SEEN:
+                        sb.append("-ns");
+                        break;
+                    case REASON_SUB_USAGE_USER_INTERACTION:
+                        sb.append("-ui");
+                        break;
+                    case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
+                        sb.append("-mf");
+                        break;
+                    case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
+                        sb.append("-mb");
+                        break;
+                    case REASON_SUB_USAGE_SYSTEM_UPDATE:
+                        sb.append("-su");
+                        break;
+                    case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
+                        sb.append("-at");
+                        break;
+                    case REASON_SUB_USAGE_SYNC_ADAPTER:
+                        sb.append("-sa");
+                        break;
+                    case REASON_SUB_USAGE_SLICE_PINNED:
+                        sb.append("slp");
+                        break;
+                    case REASON_SUB_USAGE_SLICE_PINNED_PRIV:
+                        sb.append("slpp");
+                        break;
+                    case REASON_SUB_USAGE_EXEMPTED_SYNC_START:
+                        sb.append("es");
+                        break;
+                }
+                break;
+        }
+        return sb.toString();
+    }
+
+    /**
      * {@hide}
      * Temporarily whitelist the specified app for a short duration. This is to allow an app
      * receiving a high priority message to be able to access the network and acquire wakelocks
diff --git a/android/app/usage/UsageStatsManagerInternal.java b/android/app/usage/UsageStatsManagerInternal.java
index bd978e3..b8628a4 100644
--- a/android/app/usage/UsageStatsManagerInternal.java
+++ b/android/app/usage/UsageStatsManagerInternal.java
@@ -59,6 +59,16 @@
     public abstract void reportConfigurationChange(Configuration config, @UserIdInt int userId);
 
     /**
+     * Reports that an application has posted an interruptive notification.
+     *
+     * @param packageName The package name of the app that posted the notification
+     * @param channelId The ID of the NotificationChannel to which the notification was posted
+     * @param userId The user in which the notification was posted
+     */
+    public abstract void reportInterruptiveNotification(String packageName, String channelId,
+            @UserIdInt int userId);
+
+    /**
      * Reports that an action equivalent to a ShortcutInfo is taken by the user.
      *
      * @param packageName The package name of the shortcut publisher
@@ -139,13 +149,21 @@
 
         /** Callback to inform listeners that the idle state has changed to a new bucket. */
         public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
-                boolean idle, int bucket);
+                boolean idle, int bucket, int reason);
 
         /**
          * Callback to inform listeners that the parole state has changed. This means apps are
          * allowed to do work even if they're idle or in a low bucket.
          */
         public abstract void onParoleStateChanged(boolean isParoleOn);
+
+        /**
+         * Optional callback to inform the listener that the app has transitioned into
+         * an active state due to user interaction.
+         */
+        public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
+            // No-op by default
+        }
     }
 
     /**  Backup/Restore API */
@@ -212,4 +230,25 @@
      * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}.
      */
     public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId);
+
+    /**
+     * Report a few data points about an app's job state at the current time.
+     *
+     * @param packageName the app whose job state is being described
+     * @param userId which user the app is associated with
+     * @param numDeferredJobs the number of pending jobs that were deferred
+     *   due to bucketing policy
+     * @param timeSinceLastJobRun number of milliseconds since the last time one of
+     *   this app's jobs was executed
+     */
+    public abstract void reportAppJobState(String packageName, @UserIdInt int userId,
+            int numDeferredJobs, long timeSinceLastJobRun);
+
+    /**
+     * Report a sync that was scheduled by an active app is about to be executed.
+     *
+     * @param packageName name of the package that owns the sync adapter.
+     * @param userId which user the app is associated with
+     */
+    public abstract void reportExemptedSyncStart(String packageName, @UserIdInt int userId);
 }
diff --git a/android/appwidget/AppWidgetManager.java b/android/appwidget/AppWidgetManager.java
index a55bbda..20248b9 100644
--- a/android/appwidget/AppWidgetManager.java
+++ b/android/appwidget/AppWidgetManager.java
@@ -19,6 +19,7 @@
 import android.annotation.BroadcastBehavior;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
@@ -29,11 +30,11 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
@@ -55,6 +56,7 @@
  * </div>
  */
 @SystemService(Context.APPWIDGET_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_APP_WIDGETS)
 public class AppWidgetManager {
 
     /**
@@ -459,10 +461,9 @@
      */
     public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
 
+    private final Context mContext;
     private final String mPackageName;
-
     private final IAppWidgetService mService;
-
     private final DisplayMetrics mDisplayMetrics;
 
     /**
@@ -481,6 +482,7 @@
      * @hide
      */
     public AppWidgetManager(Context context, IAppWidgetService service) {
+        mContext = context;
         mPackageName = context.getOpPackageName();
         mService = service;
         mDisplayMetrics = context.getResources().getDisplayMetrics();
@@ -677,11 +679,13 @@
     }
 
     /**
-     * Updates the info for the supplied AppWidget provider.
+     * Updates the info for the supplied AppWidget provider. Apps can use this to change the default
+     * behavior of the widget based on the state of the app (for e.g., if the user is logged in
+     * or not). Calling this API completely replaces the previous definition.
      *
      * <p>
      * The manifest entry of the provider should contain an additional meta-data tag similar to
-     * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any additional definitions for
+     * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any alternative definitions for
      * the provider.
      *
      * <p>
@@ -845,7 +849,7 @@
         }
 
         if (profile == null) {
-            profile = Process.myUserHandle();
+            profile = mContext.getUser();
         }
 
         try {
@@ -924,7 +928,7 @@
         if (mService == null) {
             return;
         }
-        bindAppWidgetIdIfAllowed(appWidgetId, Process.myUserHandle(), provider, options);
+        bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUser(), provider, options);
     }
 
     /**
@@ -944,7 +948,7 @@
         if (mService == null) {
             return false;
         }
-        return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, null);
+        return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, null);
     }
 
     /**
@@ -968,7 +972,7 @@
         if (mService == null) {
             return false;
         }
-        return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, options);
+        return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, options);
     }
 
     /**
@@ -1030,7 +1034,7 @@
             return false;
         }
         try {
-            return mService.hasBindAppWidgetPermission(packageName, UserHandle.myUserId());
+            return mService.hasBindAppWidgetPermission(packageName, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1050,7 +1054,7 @@
         if (mService == null) {
             return;
         }
-        setBindAppWidgetPermission(packageName, UserHandle.myUserId(), permission);
+        setBindAppWidgetPermission(packageName, mContext.getUserId(), permission);
     }
 
     /**
@@ -1183,6 +1187,11 @@
      * calls this API multiple times in a row.  It may ignore the previous requests,
      * for example.
      *
+     * <p>Launcher will not show the configuration activity associated with the provider in this
+     * case. The app could either show the configuration activity as a response to the callback,
+     * or show if before calling the API (various configurations can be encapsulated in
+     * {@code successCallback} to avoid persisting them before the widgetId is known).
+     *
      * @param provider The {@link ComponentName} for the {@link
      *    android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget.
      * @param extras In not null, this is passed to the launcher app. For eg {@link
diff --git a/android/appwidget/AppWidgetProviderInfo.java b/android/appwidget/AppWidgetProviderInfo.java
index 75ce4fb..6dd85ca 100644
--- a/android/appwidget/AppWidgetProviderInfo.java
+++ b/android/appwidget/AppWidgetProviderInfo.java
@@ -16,6 +16,7 @@
 
 package android.appwidget;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -32,6 +33,9 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Describes the meta data for an installed AppWidget provider.  The fields in this class
  * correspond to the fields in the <code>&lt;appwidget-provider&gt;</code> xml tag.
@@ -55,6 +59,14 @@
      */
     public static final int RESIZE_BOTH = RESIZE_HORIZONTAL | RESIZE_VERTICAL;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            RESIZE_HORIZONTAL,
+            RESIZE_VERTICAL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ResizeModeFlags {}
+
     /**
      * Indicates that the widget can be displayed on the home screen. This is the default value.
      */
@@ -70,6 +82,15 @@
      */
     public static final int WIDGET_CATEGORY_SEARCHBOX = 4;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            WIDGET_CATEGORY_HOME_SCREEN,
+            WIDGET_CATEGORY_KEYGUARD,
+            WIDGET_CATEGORY_SEARCHBOX,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CategoryFlags {}
+
     /**
      * The widget can be reconfigured anytime after it is bound by starting the
      * {@link #configure} activity.
@@ -87,6 +108,14 @@
      */
     public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            WIDGET_FEATURE_RECONFIGURABLE,
+            WIDGET_FEATURE_HIDE_FROM_PICKER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureFlags {}
+
     /**
      * Identity of this AppWidget component.  This component should be a {@link
      * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
@@ -215,6 +244,7 @@
      * <p>This field corresponds to the <code>android:resizeMode</code> attribute in
      * the AppWidget meta-data file.
      */
+    @ResizeModeFlags
     public int resizeMode;
 
     /**
@@ -226,6 +256,7 @@
      * <p>This field corresponds to the <code>widgetCategory</code> attribute in
      * the AppWidget meta-data file.
      */
+    @CategoryFlags
     public int widgetCategory;
 
     /**
@@ -235,6 +266,7 @@
      * @see #WIDGET_FEATURE_RECONFIGURABLE
      * @see #WIDGET_FEATURE_HIDE_FROM_PICKER
      */
+    @FeatureFlags
     public int widgetFeatures;
 
     /** @hide */
diff --git a/android/arch/core/executor/ArchTaskExecutor.java b/android/arch/core/executor/ArchTaskExecutor.java
deleted file mode 100644
index 6276ee3..0000000
--- a/android/arch/core/executor/ArchTaskExecutor.java
+++ /dev/null
@@ -1,118 +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.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 ArchTaskExecutor extends TaskExecutor {
-    private static volatile ArchTaskExecutor 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 ArchTaskExecutor() {
-        mDefaultTaskExecutor = new DefaultTaskExecutor();
-        mDelegate = mDefaultTaskExecutor;
-    }
-
-    /**
-     * Returns an instance of the task executor.
-     *
-     * @return The singleton ArchTaskExecutor.
-     */
-    @NonNull
-    public static ArchTaskExecutor getInstance() {
-        if (sInstance != null) {
-            return sInstance;
-        }
-        synchronized (ArchTaskExecutor.class) {
-            if (sInstance == null) {
-                sInstance = new ArchTaskExecutor();
-            }
-        }
-        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/android/arch/core/executor/DefaultTaskExecutor.java b/android/arch/core/executor/DefaultTaskExecutor.java
deleted file mode 100644
index dbb1054..0000000
--- a/android/arch/core/executor/DefaultTaskExecutor.java
+++ /dev/null
@@ -1,60 +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.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/android/arch/core/executor/JunitTaskExecutorRule.java b/android/arch/core/executor/JunitTaskExecutorRule.java
deleted file mode 100644
index c3366f3..0000000
--- a/android/arch/core/executor/JunitTaskExecutorRule.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.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() {
-        ArchTaskExecutor.getInstance().setDelegate(mTaskExecutor);
-    }
-
-    private void afterFinished() {
-        ArchTaskExecutor.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/android/arch/core/executor/TaskExecutor.java b/android/arch/core/executor/TaskExecutor.java
deleted file mode 100644
index 7175801..0000000
--- a/android/arch/core/executor/TaskExecutor.java
+++ /dev/null
@@ -1,67 +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.arch.core.executor;
-
-import android.support.annotation.NonNull;
-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(@NonNull Runnable runnable);
-
-    /**
-     * Posts the given task to the main thread.
-     *
-     * @param runnable The runnable to run on the main thread.
-     */
-    public abstract void postToMainThread(@NonNull 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(@NonNull 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/android/arch/core/executor/TaskExecutorWithFakeMainThread.java b/android/arch/core/executor/TaskExecutorWithFakeMainThread.java
deleted file mode 100644
index af0aca4..0000000
--- a/android/arch/core/executor/TaskExecutorWithFakeMainThread.java
+++ /dev/null
@@ -1,154 +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.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/android/arch/core/executor/testing/CountingTaskExecutorRule.java b/android/arch/core/executor/testing/CountingTaskExecutorRule.java
deleted file mode 100644
index 77133d5..0000000
--- a/android/arch/core/executor/testing/CountingTaskExecutorRule.java
+++ /dev/null
@@ -1,141 +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.arch.core.executor.testing;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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);
-        ArchTaskExecutor.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);
-        ArchTaskExecutor.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/android/arch/core/executor/testing/CountingTaskExecutorRuleTest.java b/android/arch/core/executor/testing/CountingTaskExecutorRuleTest.java
deleted file mode 100644
index a6a5b2e..0000000
--- a/android/arch/core/executor/testing/CountingTaskExecutorRuleTest.java
+++ /dev/null
@@ -1,181 +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.arch.core.executor.testing;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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();
-        ArchTaskExecutor.getInstance().executeOnDiskIO(latchRunnable);
-        return latchRunnable;
-    }
-
-    private LatchRunnable runOnMain() {
-        LatchRunnable latchRunnable = new LatchRunnable();
-        ArchTaskExecutor.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/android/arch/core/executor/testing/InstantTaskExecutorRule.java b/android/arch/core/executor/testing/InstantTaskExecutorRule.java
deleted file mode 100644
index f88a3e3..0000000
--- a/android/arch/core/executor/testing/InstantTaskExecutorRule.java
+++ /dev/null
@@ -1,58 +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.arch.core.executor.testing;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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);
-        ArchTaskExecutor.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);
-        ArchTaskExecutor.getInstance().setDelegate(null);
-    }
-}
diff --git a/android/arch/core/executor/testing/InstantTaskExecutorRuleTest.java b/android/arch/core/executor/testing/InstantTaskExecutorRuleTest.java
deleted file mode 100644
index 0fdcbfb..0000000
--- a/android/arch/core/executor/testing/InstantTaskExecutorRuleTest.java
+++ /dev/null
@@ -1,66 +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.arch.core.executor.testing;
-
-import static org.junit.Assert.assertTrue;
-
-import android.arch.core.executor.ArchTaskExecutor;
-
-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;
-            }
-        });
-        ArchTaskExecutor.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;
-            }
-        });
-        ArchTaskExecutor.getInstance().executeOnDiskIO(check);
-        check.get(1, TimeUnit.SECONDS);
-    }
-}
diff --git a/android/arch/core/internal/FastSafeIterableMap.java b/android/arch/core/internal/FastSafeIterableMap.java
deleted file mode 100644
index dbd4d5f..0000000
--- a/android/arch/core/internal/FastSafeIterableMap.java
+++ /dev/null
@@ -1,80 +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.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/android/arch/core/internal/FastSafeIterableMapTest.java b/android/arch/core/internal/FastSafeIterableMapTest.java
deleted file mode 100644
index 41b1497..0000000
--- a/android/arch/core/internal/FastSafeIterableMapTest.java
+++ /dev/null
@@ -1,75 +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.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/android/arch/core/internal/SafeIterableMap.java b/android/arch/core/internal/SafeIterableMap.java
deleted file mode 100644
index 00e102f..0000000
--- a/android/arch/core/internal/SafeIterableMap.java
+++ /dev/null
@@ -1,384 +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.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 mBeforeStart = true;
-
-        @Override
-        public void supportRemove(@NonNull Entry<K, V> entry) {
-            if (entry == mCurrent) {
-                mCurrent = mCurrent.mPrevious;
-                mBeforeStart = mCurrent == null;
-            }
-        }
-
-        @Override
-        public boolean hasNext() {
-            if (mBeforeStart) {
-                return mStart != null;
-            }
-            return mCurrent != null && mCurrent.mNext != null;
-        }
-
-        @Override
-        public Map.Entry<K, V> next() {
-            if (mBeforeStart) {
-                mBeforeStart = 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/android/arch/core/internal/SafeIterableMapTest.java b/android/arch/core/internal/SafeIterableMapTest.java
deleted file mode 100644
index d879543..0000000
--- a/android/arch/core/internal/SafeIterableMapTest.java
+++ /dev/null
@@ -1,505 +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.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 testRemoveDuringIteration4() {
-        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2);
-        int[] expected = new int[]{1, 2};
-        int index = 0;
-        for (Entry<Integer, Boolean> entry : map) {
-            assertThat(entry.getKey(), is(expected[index++]));
-            if (index == 1) {
-                map.remove(1);
-            }
-        }
-        assertThat(index, is(2));
-    }
-
-    @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 testIteratorWithAddition5() {
-        SafeIterableMap<Integer, Boolean> map = mapOf(1, 2);
-        int[] expected = new int[]{1, 2};
-        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.remove(1);
-            }
-        }
-        assertThat(index, is(2));
-    }
-
-    @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/android/arch/core/util/Function.java b/android/arch/core/util/Function.java
deleted file mode 100644
index 25e7a6b..0000000
--- a/android/arch/core/util/Function.java
+++ /dev/null
@@ -1,33 +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.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/android/arch/lifecycle/ActivityFullLifecycleTest.java b/android/arch/lifecycle/ActivityFullLifecycleTest.java
deleted file mode 100644
index 78dd015..0000000
--- a/android/arch/lifecycle/ActivityFullLifecycleTest.java
+++ /dev/null
@@ -1,75 +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.arch.lifecycle;
-
-import static android.arch.lifecycle.TestUtils.OrderedTuples.CREATE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.DESTROY;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.PAUSE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.RESUME;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.START;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.STOP;
-import static android.arch.lifecycle.TestUtils.flatMap;
-
-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.CollectingLifecycleOwner;
-import android.arch.lifecycle.testapp.CollectingSupportActivity;
-import android.arch.lifecycle.testapp.FrameworkLifecycleRegistryActivity;
-import android.arch.lifecycle.testapp.TestEvent;
-import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v4.util.Pair;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(Parameterized.class)
-public class ActivityFullLifecycleTest {
-    @Rule
-    public final ActivityTestRule<? extends CollectingLifecycleOwner> activityTestRule;
-
-    @Parameterized.Parameters
-    public static Class[] params() {
-        return new Class[]{CollectingSupportActivity.class,
-                FrameworkLifecycleRegistryActivity.class};
-    }
-
-    public ActivityFullLifecycleTest(Class<? extends Activity> activityClass) {
-        //noinspection unchecked
-        activityTestRule = new ActivityTestRule(activityClass);
-    }
-
-
-    @Test
-    public void testFullLifecycle() throws Throwable {
-        CollectingLifecycleOwner owner = activityTestRule.getActivity();
-        TestUtils.waitTillResumed(owner, activityTestRule);
-        activityTestRule.finishActivity();
-
-        TestUtils.waitTillDestroyed(owner, activityTestRule);
-        List<Pair<TestEvent, Event>> results = owner.copyCollectedEvents();
-        assertThat(results, is(flatMap(CREATE, START, RESUME, PAUSE, STOP, DESTROY)));
-    }
-}
diff --git a/android/arch/lifecycle/AndroidViewModel.java b/android/arch/lifecycle/AndroidViewModel.java
deleted file mode 100644
index e8895bd..0000000
--- a/android/arch/lifecycle/AndroidViewModel.java
+++ /dev/null
@@ -1,46 +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.arch.lifecycle;
-
-import android.annotation.SuppressLint;
-import android.app.Application;
-import android.support.annotation.NonNull;
-
-/**
- * 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 {
-    @SuppressLint("StaticFieldLeak")
-    private Application mApplication;
-
-    public AndroidViewModel(@NonNull Application application) {
-        mApplication = application;
-    }
-
-    /**
-     * Return the application.
-     */
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    @NonNull
-    public <T extends Application> T getApplication() {
-        //noinspection unchecked
-        return (T) mApplication;
-    }
-}
diff --git a/android/arch/lifecycle/ClassesInfoCache.java b/android/arch/lifecycle/ClassesInfoCache.java
deleted file mode 100644
index d88e276..0000000
--- a/android/arch/lifecycle/ClassesInfoCache.java
+++ /dev/null
@@ -1,249 +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.arch.lifecycle;
-
-import android.support.annotation.Nullable;
-
-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;
-
-/**
- * Reflection is expensive, so we cache information about methods
- * for {@link ReflectiveGenericLifecycleObserver}, so it can call them,
- * and for {@link Lifecycling} to determine which observer adapter to use.
- */
-class ClassesInfoCache {
-
-    static ClassesInfoCache sInstance = new ClassesInfoCache();
-
-    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;
-
-    private final Map<Class, CallbackInfo> mCallbackMap = new HashMap<>();
-    private final Map<Class, Boolean> mHasLifecycleMethods = new HashMap<>();
-
-    boolean hasLifecycleMethods(Class klass) {
-        if (mHasLifecycleMethods.containsKey(klass)) {
-            return mHasLifecycleMethods.get(klass);
-        }
-
-        Method[] methods = getDeclaredMethods(klass);
-        for (Method method : methods) {
-            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
-            if (annotation != null) {
-                // Optimization for reflection, we know that this method is called
-                // when there is no generated adapter. But there are methods with @OnLifecycleEvent
-                // so we know that will use ReflectiveGenericLifecycleObserver,
-                // so we createInfo in advance.
-                // CreateInfo always initialize mHasLifecycleMethods for a class, so we don't do it
-                // here.
-                createInfo(klass, methods);
-                return true;
-            }
-        }
-        mHasLifecycleMethods.put(klass, false);
-        return false;
-    }
-
-    private Method[] getDeclaredMethods(Class klass) {
-        try {
-            return klass.getDeclaredMethods();
-        } catch (NoClassDefFoundError e) {
-            throw new IllegalArgumentException("The observer class has some methods that use "
-                    + "newer APIs which are not available in the current OS version. Lifecycles "
-                    + "cannot access even other methods so you should make sure that your "
-                    + "observer classes only access framework classes that are available "
-                    + "in your min API level OR use lifecycle:compiler annotation processor.", e);
-        }
-    }
-
-    CallbackInfo getInfo(Class klass) {
-        CallbackInfo existing = mCallbackMap.get(klass);
-        if (existing != null) {
-            return existing;
-        }
-        existing = createInfo(klass, null);
-        return existing;
-    }
-
-    private void verifyAndPutHandler(Map<MethodReference, Lifecycle.Event> handlers,
-            MethodReference newHandler, Lifecycle.Event newEvent, Class klass) {
-        Lifecycle.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 CallbackInfo createInfo(Class klass, @Nullable Method[] declaredMethods) {
-        Class superclass = klass.getSuperclass();
-        Map<MethodReference, Lifecycle.Event> handlerToEvent = new HashMap<>();
-        if (superclass != null) {
-            CallbackInfo superInfo = getInfo(superclass);
-            if (superInfo != null) {
-                handlerToEvent.putAll(superInfo.mHandlerToEvent);
-            }
-        }
-
-        Class[] interfaces = klass.getInterfaces();
-        for (Class intrfc : interfaces) {
-            for (Map.Entry<MethodReference, Lifecycle.Event> entry : getInfo(
-                    intrfc).mHandlerToEvent.entrySet()) {
-                verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass);
-            }
-        }
-
-        Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass);
-        boolean hasLifecycleMethods = false;
-        for (Method method : methods) {
-            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
-            if (annotation == null) {
-                continue;
-            }
-            hasLifecycleMethods = true;
-            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");
-                }
-            }
-            Lifecycle.Event event = annotation.value();
-
-            if (params.length > 1) {
-                callType = CALL_TYPE_PROVIDER_WITH_EVENT;
-                if (!params[1].isAssignableFrom(Lifecycle.Event.class)) {
-                    throw new IllegalArgumentException(
-                            "invalid parameter type. second arg must be an event");
-                }
-                if (event != Lifecycle.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);
-        mCallbackMap.put(klass, info);
-        mHasLifecycleMethods.put(klass, hasLifecycleMethods);
-        return info;
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static class CallbackInfo {
-        final Map<Lifecycle.Event, List<MethodReference>> mEventToHandlers;
-        final Map<MethodReference, Lifecycle.Event> mHandlerToEvent;
-
-        CallbackInfo(Map<MethodReference, Lifecycle.Event> handlerToEvent) {
-            mHandlerToEvent = handlerToEvent;
-            mEventToHandlers = new HashMap<>();
-            for (Map.Entry<MethodReference, Lifecycle.Event> entry : handlerToEvent.entrySet()) {
-                Lifecycle.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("ConstantConditions")
-        void invokeCallbacks(LifecycleOwner source, Lifecycle.Event event, Object target) {
-            invokeMethodsForEvent(mEventToHandlers.get(event), source, event, target);
-            invokeMethodsForEvent(mEventToHandlers.get(Lifecycle.Event.ON_ANY), source, event,
-                    target);
-        }
-
-        private static void invokeMethodsForEvent(List<MethodReference> handlers,
-                LifecycleOwner source, Lifecycle.Event event, Object mWrapped) {
-            if (handlers != null) {
-                for (int i = handlers.size() - 1; i >= 0; i--) {
-                    handlers.get(i).invokeCallback(source, event, mWrapped);
-                }
-            }
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static class MethodReference {
-        final int mCallType;
-        final Method mMethod;
-
-        MethodReference(int callType, Method method) {
-            mCallType = callType;
-            mMethod = method;
-            mMethod.setAccessible(true);
-        }
-
-        void invokeCallback(LifecycleOwner source, Lifecycle.Event event, Object target) {
-            //noinspection TryWithIdenticalCatches
-            try {
-                switch (mCallType) {
-                    case CALL_TYPE_NO_ARG:
-                        mMethod.invoke(target);
-                        break;
-                    case CALL_TYPE_PROVIDER:
-                        mMethod.invoke(target, source);
-                        break;
-                    case CALL_TYPE_PROVIDER_WITH_EVENT:
-                        mMethod.invoke(target, 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 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();
-        }
-    }
-}
diff --git a/android/arch/lifecycle/CompositeGeneratedAdaptersObserver.java b/android/arch/lifecycle/CompositeGeneratedAdaptersObserver.java
deleted file mode 100644
index e8cbe7c..0000000
--- a/android/arch/lifecycle/CompositeGeneratedAdaptersObserver.java
+++ /dev/null
@@ -1,44 +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.arch.lifecycle;
-
-
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class CompositeGeneratedAdaptersObserver implements GenericLifecycleObserver {
-
-    private final GeneratedAdapter[] mGeneratedAdapters;
-
-    CompositeGeneratedAdaptersObserver(GeneratedAdapter[] generatedAdapters) {
-        mGeneratedAdapters = generatedAdapters;
-    }
-
-    @Override
-    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
-        MethodCallsLogger logger = new MethodCallsLogger();
-        for (GeneratedAdapter mGenerated: mGeneratedAdapters) {
-            mGenerated.callMethods(source, event, false, logger);
-        }
-        for (GeneratedAdapter mGenerated: mGeneratedAdapters) {
-            mGenerated.callMethods(source, event, true, logger);
-        }
-    }
-}
diff --git a/android/arch/lifecycle/ComputableLiveData.java b/android/arch/lifecycle/ComputableLiveData.java
deleted file mode 100644
index f135244..0000000
--- a/android/arch/lifecycle/ComputableLiveData.java
+++ /dev/null
@@ -1,9 +0,0 @@
-//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/android/arch/lifecycle/ComputableLiveDataTest.java b/android/arch/lifecycle/ComputableLiveDataTest.java
deleted file mode 100644
index eb89d8d..0000000
--- a/android/arch/lifecycle/ComputableLiveDataTest.java
+++ /dev/null
@@ -1,215 +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.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.ArchTaskExecutor;
-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());
-        ArchTaskExecutor.getInstance().setDelegate(mTaskExecutor);
-    }
-
-    @After
-    public void removeExecutorDelegate() {
-        ArchTaskExecutor.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);
-        ArchTaskExecutor.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 {
-            ArchTaskExecutor.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/android/arch/lifecycle/DefaultLifecycleObserver.java b/android/arch/lifecycle/DefaultLifecycleObserver.java
deleted file mode 100644
index b6f468c..0000000
--- a/android/arch/lifecycle/DefaultLifecycleObserver.java
+++ /dev/null
@@ -1,100 +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.arch.lifecycle;
-
-import android.support.annotation.NonNull;
-
-/**
- * Callback interface for listening to {@link LifecycleOwner} state changes.
- * <p>
- * If you use Java 8 language, <b>always</b> prefer it over annotations.
- */
-@SuppressWarnings("unused")
-public interface DefaultLifecycleObserver extends FullLifecycleObserver {
-
-    /**
-     * Notifies that {@code ON_CREATE} event occurred.
-     * <p>
-     * This method will be called after the {@link LifecycleOwner}'s {@code onCreate}
-     * method returns.
-     *
-     * @param owner the component, whose state was changed
-     */
-    @Override
-    default void onCreate(@NonNull LifecycleOwner owner) {
-    }
-
-    /**
-     * Notifies that {@code ON_START} event occurred.
-     * <p>
-     * This method will be called after the {@link LifecycleOwner}'s {@code onStart} method returns.
-     *
-     * @param owner the component, whose state was changed
-     */
-    @Override
-    default void onStart(@NonNull LifecycleOwner owner) {
-    }
-
-    /**
-     * Notifies that {@code ON_RESUME} event occurred.
-     * <p>
-     * This method will be called after the {@link LifecycleOwner}'s {@code onResume}
-     * method returns.
-     *
-     * @param owner the component, whose state was changed
-     */
-    @Override
-    default void onResume(@NonNull LifecycleOwner owner) {
-    }
-
-    /**
-     * Notifies that {@code ON_PAUSE} event occurred.
-     * <p>
-     * This method will be called before the {@link LifecycleOwner}'s {@code onPause} method
-     * is called.
-     *
-     * @param owner the component, whose state was changed
-     */
-    @Override
-    default void onPause(@NonNull LifecycleOwner owner) {
-    }
-
-    /**
-     * Notifies that {@code ON_STOP} event occurred.
-     * <p>
-     * This method will be called before the {@link LifecycleOwner}'s {@code onStop} method
-     * is called.
-     *
-     * @param owner the component, whose state was changed
-     */
-    @Override
-    default void onStop(@NonNull LifecycleOwner owner) {
-    }
-
-    /**
-     * Notifies that {@code ON_DESTROY} event occurred.
-     * <p>
-     * This method will be called before the {@link LifecycleOwner}'s {@code onStop} method
-     * is called.
-     *
-     * @param owner the component, whose state was changed
-     */
-    @Override
-    default void onDestroy(@NonNull LifecycleOwner owner) {
-    }
-}
-
diff --git a/android/arch/lifecycle/DispatcherActivityCallbackTest.java b/android/arch/lifecycle/DispatcherActivityCallbackTest.java
deleted file mode 100644
index 86b25b6..0000000
--- a/android/arch/lifecycle/DispatcherActivityCallbackTest.java
+++ /dev/null
@@ -1,77 +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.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.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)
-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/android/arch/lifecycle/EmptyActivity.java b/android/arch/lifecycle/EmptyActivity.java
deleted file mode 100644
index 738bd66..0000000
--- a/android/arch/lifecycle/EmptyActivity.java
+++ /dev/null
@@ -1,22 +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.arch.lifecycle;
-
-import android.support.v4.app.FragmentActivity;
-
-public class EmptyActivity extends FragmentActivity {
-}
diff --git a/android/arch/lifecycle/EmptyActivityLifecycleCallbacks.java b/android/arch/lifecycle/EmptyActivityLifecycleCallbacks.java
deleted file mode 100644
index ed41f2d..0000000
--- a/android/arch/lifecycle/EmptyActivityLifecycleCallbacks.java
+++ /dev/null
@@ -1,51 +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.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/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java b/android/arch/lifecycle/FragmentInBackStackLifecycleTest.java
deleted file mode 100644
index f48f788..0000000
--- a/android/arch/lifecycle/FragmentInBackStackLifecycleTest.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.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.Fragment;
-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(@SuppressWarnings("unused") LifecycleOwner owner, Event event) {
-                collectedEvents.add(event);
-            }
-        };
-        final FragmentActivity activity = activityTestRule.getActivity();
-        activityTestRule.runOnUiThread(() -> {
-            FragmentManager fm = activity.getSupportFragmentManager();
-            Fragment fragment = new Fragment();
-            fm.beginTransaction().add(R.id.fragment_container, fragment, "tag").addToBackStack(null)
-                    .commit();
-            fm.executePendingTransactions();
-
-            fragment.getLifecycle().addObserver(collectingObserver);
-            Fragment fragment2 = new Fragment();
-            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);
-
-        //noinspection ArraysAsListWithZeroOrOneArgument
-        assertThat(collectedEvents, is(asList(ON_DESTROY)));
-        collectedEvents.clear();
-        EmptyActivity lastActivity = recreateActivity(newActivity, activityTestRule);
-        activityTestRule.runOnUiThread(() -> {
-            FragmentManager fm = lastActivity.getSupportFragmentManager();
-            Fragment fragment = 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/android/arch/lifecycle/FragmentLifecycleInActivityTest.java b/android/arch/lifecycle/FragmentLifecycleInActivityTest.java
deleted file mode 100644
index 70a4465..0000000
--- a/android/arch/lifecycle/FragmentLifecycleInActivityTest.java
+++ /dev/null
@@ -1,155 +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.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/android/arch/lifecycle/FragmentOperationsLifecycleTest.java b/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
deleted file mode 100644
index 3e61277..0000000
--- a/android/arch/lifecycle/FragmentOperationsLifecycleTest.java
+++ /dev/null
@@ -1,119 +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.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.Fragment;
-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();
-        Fragment fragment = new Fragment();
-        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();
-        Fragment fragment1 = new Fragment();
-        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)));
-
-        Fragment fragment2 = new Fragment();
-        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(Fragment 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/android/arch/lifecycle/FullLifecycleObserver.java b/android/arch/lifecycle/FullLifecycleObserver.java
deleted file mode 100644
index f179274..0000000
--- a/android/arch/lifecycle/FullLifecycleObserver.java
+++ /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.
- */
-
-package android.arch.lifecycle;
-
-interface FullLifecycleObserver extends LifecycleObserver {
-
-    void onCreate(LifecycleOwner owner);
-
-    void onStart(LifecycleOwner owner);
-
-    void onResume(LifecycleOwner owner);
-
-    void onPause(LifecycleOwner owner);
-
-    void onStop(LifecycleOwner owner);
-
-    void onDestroy(LifecycleOwner owner);
-}
diff --git a/android/arch/lifecycle/FullLifecycleObserverAdapter.java b/android/arch/lifecycle/FullLifecycleObserverAdapter.java
deleted file mode 100644
index 0a91a66..0000000
--- a/android/arch/lifecycle/FullLifecycleObserverAdapter.java
+++ /dev/null
@@ -1,52 +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.arch.lifecycle;
-
-class FullLifecycleObserverAdapter implements GenericLifecycleObserver {
-
-    private final FullLifecycleObserver mObserver;
-
-    FullLifecycleObserverAdapter(FullLifecycleObserver observer) {
-        mObserver = observer;
-    }
-
-    @Override
-    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
-        switch (event) {
-            case ON_CREATE:
-                mObserver.onCreate(source);
-                break;
-            case ON_START:
-                mObserver.onStart(source);
-                break;
-            case ON_RESUME:
-                mObserver.onResume(source);
-                break;
-            case ON_PAUSE:
-                mObserver.onPause(source);
-                break;
-            case ON_STOP:
-                mObserver.onStop(source);
-                break;
-            case ON_DESTROY:
-                mObserver.onDestroy(source);
-                break;
-            case ON_ANY:
-                throw new IllegalArgumentException("ON_ANY must not been send by anybody");
-        }
-    }
-}
diff --git a/android/arch/lifecycle/FullLifecycleObserverTest.java b/android/arch/lifecycle/FullLifecycleObserverTest.java
deleted file mode 100644
index def6755..0000000
--- a/android/arch/lifecycle/FullLifecycleObserverTest.java
+++ /dev/null
@@ -1,89 +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.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.INITIALIZED;
-import static android.arch.lifecycle.Lifecycle.State.RESUMED;
-import static android.arch.lifecycle.Lifecycle.State.STARTED;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-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.InOrder;
-import org.mockito.Mockito;
-
-@RunWith(JUnit4.class)
-public class FullLifecycleObserverTest {
-    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 eachEvent() {
-        FullLifecycleObserver obj = mock(FullLifecycleObserver.class);
-        FullLifecycleObserverAdapter observer = new FullLifecycleObserverAdapter(obj);
-        when(mLifecycle.getCurrentState()).thenReturn(CREATED);
-
-        observer.onStateChanged(mOwner, ON_CREATE);
-        InOrder inOrder = Mockito.inOrder(obj);
-        inOrder.verify(obj).onCreate(mOwner);
-        reset(obj);
-
-        when(mLifecycle.getCurrentState()).thenReturn(STARTED);
-        observer.onStateChanged(mOwner, ON_START);
-        inOrder.verify(obj).onStart(mOwner);
-        reset(obj);
-
-        when(mLifecycle.getCurrentState()).thenReturn(RESUMED);
-        observer.onStateChanged(mOwner, ON_RESUME);
-        inOrder.verify(obj).onResume(mOwner);
-        reset(obj);
-
-        when(mLifecycle.getCurrentState()).thenReturn(STARTED);
-        observer.onStateChanged(mOwner, ON_PAUSE);
-        inOrder.verify(obj).onPause(mOwner);
-        reset(obj);
-
-        when(mLifecycle.getCurrentState()).thenReturn(CREATED);
-        observer.onStateChanged(mOwner, ON_STOP);
-        inOrder.verify(obj).onStop(mOwner);
-        reset(obj);
-
-        when(mLifecycle.getCurrentState()).thenReturn(INITIALIZED);
-        observer.onStateChanged(mOwner, ON_DESTROY);
-        inOrder.verify(obj).onDestroy(mOwner);
-        reset(obj);
-    }
-}
diff --git a/android/arch/lifecycle/GeneratedAdapter.java b/android/arch/lifecycle/GeneratedAdapter.java
deleted file mode 100644
index a8862da..0000000
--- a/android/arch/lifecycle/GeneratedAdapter.java
+++ /dev/null
@@ -1,38 +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.arch.lifecycle;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public interface GeneratedAdapter {
-
-    /**
-     * Called when a state transition event happens.
-     *
-     * @param source The source of the event
-     * @param event The event
-     * @param onAny approveCall onAny handlers
-     * @param logger if passed, used to track called methods and prevent calling the same method
-     *              twice
-     */
-    void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
-            MethodCallsLogger logger);
-}
diff --git a/android/arch/lifecycle/GeneratedAdaptersTest.java b/android/arch/lifecycle/GeneratedAdaptersTest.java
deleted file mode 100644
index 2abb511..0000000
--- a/android/arch/lifecycle/GeneratedAdaptersTest.java
+++ /dev/null
@@ -1,212 +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.arch.lifecycle;
-
-import static android.arch.lifecycle.Lifecycle.Event.ON_ANY;
-import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-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 static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-
-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.List;
-
-@RunWith(JUnit4.class)
-public class GeneratedAdaptersTest {
-
-    private LifecycleOwner mOwner;
-    @SuppressWarnings("FieldCanBeLocal")
-    private Lifecycle mLifecycle;
-
-    @Before
-    public void initMocks() {
-        mOwner = mock(LifecycleOwner.class);
-        mLifecycle = mock(Lifecycle.class);
-        when(mOwner.getLifecycle()).thenReturn(mLifecycle);
-    }
-
-    static class SimpleObserver implements LifecycleObserver {
-        List<String> mLog;
-
-        SimpleObserver(List<String> log) {
-            mLog = log;
-        }
-
-        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-        void onCreate() {
-            mLog.add("onCreate");
-        }
-    }
-
-    @Test
-    public void testSimpleSingleGeneratedAdapter() {
-        List<String>  actual = new ArrayList<>();
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new SimpleObserver(actual));
-        callback.onStateChanged(mOwner, Lifecycle.Event.ON_CREATE);
-        assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
-        assertThat(actual, is(singletonList("onCreate")));
-    }
-
-    static class TestObserver implements LifecycleObserver {
-        List<String> mLog;
-
-        TestObserver(List<String> log) {
-            mLog = log;
-        }
-
-        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-        void onCreate() {
-            mLog.add("onCreate");
-        }
-
-        @OnLifecycleEvent(ON_ANY)
-        void onAny() {
-            mLog.add("onAny");
-        }
-    }
-
-    @Test
-    public void testOnAny() {
-        List<String>  actual = new ArrayList<>();
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new TestObserver(actual));
-        callback.onStateChanged(mOwner, Lifecycle.Event.ON_CREATE);
-        assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
-        assertThat(actual, is(asList("onCreate", "onAny")));
-    }
-
-    interface OnPauses extends LifecycleObserver {
-        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-        void onPause();
-
-        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-        void onPause(LifecycleOwner owner);
-    }
-
-    interface OnPauseResume extends LifecycleObserver {
-        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
-        void onPause();
-
-        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-        void onResume();
-    }
-
-    class Impl1 implements OnPauses, OnPauseResume {
-
-        List<String> mLog;
-
-        Impl1(List<String> log) {
-            mLog = log;
-        }
-
-        @Override
-        public void onPause() {
-            mLog.add("onPause_0");
-        }
-
-        @Override
-        public void onResume() {
-            mLog.add("onResume");
-        }
-
-        @Override
-        public void onPause(LifecycleOwner owner) {
-            mLog.add("onPause_1");
-        }
-    }
-
-    @Test
-    public void testClashingInterfaces() {
-        List<String>  actual = new ArrayList<>();
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new Impl1(actual));
-        callback.onStateChanged(mOwner, Lifecycle.Event.ON_PAUSE);
-        assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
-        assertThat(actual, is(asList("onPause_0", "onPause_1")));
-        actual.clear();
-        callback.onStateChanged(mOwner, Lifecycle.Event.ON_RESUME);
-        assertThat(actual, is(singletonList("onResume")));
-    }
-
-    class Base implements LifecycleObserver {
-
-        List<String> mLog;
-
-        Base(List<String> log) {
-            mLog = log;
-        }
-
-        @OnLifecycleEvent(ON_ANY)
-        void onAny() {
-            mLog.add("onAny_0");
-        }
-
-        @OnLifecycleEvent(ON_ANY)
-        void onAny(LifecycleOwner owner) {
-            mLog.add("onAny_1");
-        }
-
-        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-        void onResume() {
-            mLog.add("onResume");
-        }
-    }
-
-    interface OnAny extends LifecycleObserver {
-        @OnLifecycleEvent(ON_ANY)
-        void onAny();
-
-        @OnLifecycleEvent(ON_ANY)
-        void onAny(LifecycleOwner owner, Lifecycle.Event event);
-    }
-
-    class Derived extends Base implements OnAny {
-        Derived(List<String> log) {
-            super(log);
-        }
-
-        @Override
-        public void onAny() {
-            super.onAny();
-        }
-
-        @Override
-        public void onAny(LifecycleOwner owner, Lifecycle.Event event) {
-            mLog.add("onAny_2");
-            assertThat(event, is(ON_RESUME));
-        }
-    }
-
-    @Test
-    public void testClashingClassAndInterface() {
-        List<String>  actual = new ArrayList<>();
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new Derived(actual));
-        callback.onStateChanged(mOwner, Lifecycle.Event.ON_RESUME);
-        assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
-        assertThat(actual, is(asList("onResume", "onAny_0", "onAny_1", "onAny_2")));
-    }
-
-}
diff --git a/android/arch/lifecycle/GenericLifecycleObserver.java b/android/arch/lifecycle/GenericLifecycleObserver.java
deleted file mode 100644
index 4601478..0000000
--- a/android/arch/lifecycle/GenericLifecycleObserver.java
+++ /dev/null
@@ -1,35 +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.arch.lifecycle;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Internal class that can receive any lifecycle change and dispatch it to the receiver.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@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);
-}
diff --git a/android/arch/lifecycle/HolderFragment.java b/android/arch/lifecycle/HolderFragment.java
deleted file mode 100644
index ca5e181..0000000
--- a/android/arch/lifecycle/HolderFragment.java
+++ /dev/null
@@ -1,194 +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.arch.lifecycle;
-
-import android.app.Activity;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-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 implements ViewModelStoreOwner {
-    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();
-    }
-
-    @NonNull
-    @Override
-    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/android/arch/lifecycle/Lifecycle.java b/android/arch/lifecycle/Lifecycle.java
deleted file mode 100644
index e04cdb4..0000000
--- a/android/arch/lifecycle/Lifecycle.java
+++ /dev/null
@@ -1,202 +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.arch.lifecycle;
-
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-
-/**
- * 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>
- * If you use <b>Java 8 Language</b>, then observe events with {@link DefaultLifecycleObserver}.
- * To include it you should add {@code "android.arch.lifecycle:common-java8:<version>"} to your
- * build.gradle file.
- * <pre>
- * class TestObserver implements DefaultLifecycleObserver {
- *     {@literal @}Override
- *     public void onCreate(LifecycleOwner owner) {
- *         // your code
- *     }
- * }
- * </pre>
- * If you use <b>Java 7 Language</b>, Lifecycle events are observed using annotations.
- * Once Java 8 Language becomes mainstream on Android, annotations will be deprecated, so between
- * {@link DefaultLifecycleObserver} and annotations,
- * you must always prefer {@code DefaultLifecycleObserver}.
- * <pre>
- * class TestObserver implements LifecycleObserver {
- *   {@literal @}OnLifecycleEvent(ON_STOP)
- *   void onStopped() {}
- * }
- * </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(@NonNull 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(@NonNull LifecycleObserver observer);
-
-    /**
-     * Returns the current state of the Lifecycle.
-     *
-     * @return The current state of the Lifecycle.
-     */
-    @MainThread
-    @NonNull
-    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(@NonNull State state) {
-            return compareTo(state) >= 0;
-        }
-    }
-}
diff --git a/android/arch/lifecycle/LifecycleDispatcher.java b/android/arch/lifecycle/LifecycleDispatcher.java
deleted file mode 100644
index 9fdec95..0000000
--- a/android/arch/lifecycle/LifecycleDispatcher.java
+++ /dev/null
@@ -1,182 +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.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/android/arch/lifecycle/LifecycleObserver.java b/android/arch/lifecycle/LifecycleObserver.java
deleted file mode 100644
index 94670bf..0000000
--- a/android/arch/lifecycle/LifecycleObserver.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.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/android/arch/lifecycle/LifecycleOwner.java b/android/arch/lifecycle/LifecycleOwner.java
deleted file mode 100644
index 068bac1..0000000
--- a/android/arch/lifecycle/LifecycleOwner.java
+++ /dev/null
@@ -1,36 +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.arch.lifecycle;
-
-import android.support.annotation.NonNull;
-
-/**
- * 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.
-     */
-    @NonNull
-    Lifecycle getLifecycle();
-}
diff --git a/android/arch/lifecycle/LifecycleRegistry.java b/android/arch/lifecycle/LifecycleRegistry.java
deleted file mode 100644
index eff946b..0000000
--- a/android/arch/lifecycle/LifecycleRegistry.java
+++ /dev/null
@@ -1,358 +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.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.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-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 {
-
-    private static final String LOG_TAG = "LifecycleRegistry";
-
-    /**
-     * 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.
-     * Only WeakReference on LifecycleOwner is kept, so if somebody leaks Lifecycle, they won't leak
-     * the whole Fragment / Activity. However, to leak Lifecycle object isn't great idea neither,
-     * because it keeps strong references on all other listeners, so you'll leak all of them as
-     * well.
-     */
-    private final WeakReference<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 = new WeakReference<>(provider);
-        mState = INITIALIZED;
-    }
-
-    /**
-     * Moves the Lifecycle to the given state and dispatches necessary events to the observers.
-     *
-     * @param state new state
-     */
-    @SuppressWarnings("WeakerAccess")
-    @MainThread
-    public void markState(@NonNull State state) {
-        moveToState(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(@NonNull Lifecycle.Event event) {
-        State next = getStateAfter(event);
-        moveToState(next);
-    }
-
-    private void moveToState(State next) {
-        if (mState == next) {
-            return;
-        }
-        mState = next;
-        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(@NonNull LifecycleObserver observer) {
-        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
-        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
-        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
-
-        if (previous != null) {
-            return;
-        }
-        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
-        if (lifecycleOwner == null) {
-            // it is null we should be destroyed. Fallback quickly
-            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(lifecycleOwner, 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(@NonNull 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();
-    }
-
-    @NonNull
-    @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(LifecycleOwner lifecycleOwner) {
-        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(lifecycleOwner, upEvent(observer.mState));
-                popParentState();
-            }
-        }
-    }
-
-    private void backwardPass(LifecycleOwner lifecycleOwner) {
-        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(lifecycleOwner, 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() {
-        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
-        if (lifecycleOwner == null) {
-            Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "
-                    + "new events from it.");
-            return;
-        }
-        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(lifecycleOwner);
-            }
-            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
-            if (!mNewEventOccurred && newest != null
-                    && mState.compareTo(newest.getValue().mState) > 0) {
-                forwardPass(lifecycleOwner);
-            }
-        }
-        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/android/arch/lifecycle/LifecycleRegistryOwner.java b/android/arch/lifecycle/LifecycleRegistryOwner.java
deleted file mode 100644
index 0c67fef..0000000
--- a/android/arch/lifecycle/LifecycleRegistryOwner.java
+++ /dev/null
@@ -1,31 +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.arch.lifecycle;
-
-import android.support.annotation.NonNull;
-
-/**
- * @deprecated Use {@code android.support.v7.app.AppCompatActivity}
- * which extends {@link LifecycleOwner}, so there are no use cases for this class.
- */
-@SuppressWarnings({"WeakerAccess", "unused"})
-@Deprecated
-public interface LifecycleRegistryOwner extends LifecycleOwner {
-    @NonNull
-    @Override
-    LifecycleRegistry getLifecycle();
-}
diff --git a/android/arch/lifecycle/LifecycleRegistryTest.java b/android/arch/lifecycle/LifecycleRegistryTest.java
deleted file mode 100644
index 2a7bbad..0000000
--- a/android/arch/lifecycle/LifecycleRegistryTest.java
+++ /dev/null
@@ -1,624 +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.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 org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.InOrder;
-
-@RunWith(JUnit4.class)
-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 static void forceGc() {
-        Runtime.getRuntime().gc();
-        Runtime.getRuntime().runFinalization();
-        Runtime.getRuntime().gc();
-        Runtime.getRuntime().runFinalization();
-    }
-
-    @Test
-    public void goneLifecycleOwner() {
-        fullyInitializeRegistry();
-        mLifecycleOwner = null;
-        forceGc();
-        TestObserver observer = mock(TestObserver.class);
-        mRegistry.addObserver(observer);
-        verify(observer, never()).onCreate();
-        verify(observer, never()).onStart();
-        verify(observer, never()).onResume();
-    }
-
-    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/android/arch/lifecycle/LifecycleService.java b/android/arch/lifecycle/LifecycleService.java
deleted file mode 100644
index adc4ffc..0000000
--- a/android/arch/lifecycle/LifecycleService.java
+++ /dev/null
@@ -1,76 +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.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/android/arch/lifecycle/Lifecycling.java b/android/arch/lifecycle/Lifecycling.java
deleted file mode 100644
index 7d6b37f..0000000
--- a/android/arch/lifecycle/Lifecycling.java
+++ /dev/null
@@ -1,181 +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.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.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Internal class to handle lifecycle conversion etc.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class Lifecycling {
-
-    private static final int REFLECTIVE_CALLBACK = 1;
-    private static final int GENERATED_CALLBACK = 2;
-
-    private static Map<Class, Integer> sCallbackCache = new HashMap<>();
-    private static Map<Class, List<Constructor<? extends GeneratedAdapter>>> sClassToAdapters =
-            new HashMap<>();
-
-    @NonNull
-    static GenericLifecycleObserver getCallback(Object object) {
-        if (object instanceof FullLifecycleObserver) {
-            return new FullLifecycleObserverAdapter((FullLifecycleObserver) object);
-        }
-
-        if (object instanceof GenericLifecycleObserver) {
-            return (GenericLifecycleObserver) object;
-        }
-
-        final Class<?> klass = object.getClass();
-        int type = getObserverConstructorType(klass);
-        if (type == GENERATED_CALLBACK) {
-            List<Constructor<? extends GeneratedAdapter>> constructors =
-                    sClassToAdapters.get(klass);
-            if (constructors.size() == 1) {
-                GeneratedAdapter generatedAdapter = createGeneratedAdapter(
-                        constructors.get(0), object);
-                return new SingleGeneratedAdapterObserver(generatedAdapter);
-            }
-            GeneratedAdapter[] adapters = new GeneratedAdapter[constructors.size()];
-            for (int i = 0; i < constructors.size(); i++) {
-                adapters[i] = createGeneratedAdapter(constructors.get(i), object);
-            }
-            return new CompositeGeneratedAdaptersObserver(adapters);
-        }
-        return new ReflectiveGenericLifecycleObserver(object);
-    }
-
-    private static GeneratedAdapter createGeneratedAdapter(
-            Constructor<? extends GeneratedAdapter> constructor, Object object) {
-        //noinspection TryWithIdenticalCatches
-        try {
-            return constructor.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 GeneratedAdapter> generatedConstructor(Class<?> klass) {
-        try {
-            Package aPackage = klass.getPackage();
-            String name = klass.getCanonicalName();
-            final String fullPackage = aPackage != null ? aPackage.getName() : "";
-            final String adapterName = getAdapterName(fullPackage.isEmpty() ? name :
-                    name.substring(fullPackage.length() + 1));
-
-            @SuppressWarnings("unchecked") final Class<? extends GeneratedAdapter> aClass =
-                    (Class<? extends GeneratedAdapter>) Class.forName(
-                            fullPackage.isEmpty() ? adapterName : fullPackage + "." + adapterName);
-            Constructor<? extends GeneratedAdapter> constructor =
-                    aClass.getDeclaredConstructor(klass);
-            if (!constructor.isAccessible()) {
-                constructor.setAccessible(true);
-            }
-            return constructor;
-        } catch (ClassNotFoundException e) {
-            return null;
-        } catch (NoSuchMethodException e) {
-            // this should not happen
-            throw new RuntimeException(e);
-        }
-    }
-
-    private static int getObserverConstructorType(Class<?> klass) {
-        if (sCallbackCache.containsKey(klass)) {
-            return sCallbackCache.get(klass);
-        }
-        int type = resolveObserverCallbackType(klass);
-        sCallbackCache.put(klass, type);
-        return type;
-    }
-
-    private static int resolveObserverCallbackType(Class<?> klass) {
-        // anonymous class bug:35073837
-        if (klass.getCanonicalName() == null) {
-            return REFLECTIVE_CALLBACK;
-        }
-
-        Constructor<? extends GeneratedAdapter> constructor = generatedConstructor(klass);
-        if (constructor != null) {
-            sClassToAdapters.put(klass, Collections
-                    .<Constructor<? extends GeneratedAdapter>>singletonList(constructor));
-            return GENERATED_CALLBACK;
-        }
-
-        boolean hasLifecycleMethods = ClassesInfoCache.sInstance.hasLifecycleMethods(klass);
-        if (hasLifecycleMethods) {
-            return REFLECTIVE_CALLBACK;
-        }
-
-        Class<?> superclass = klass.getSuperclass();
-        List<Constructor<? extends GeneratedAdapter>> adapterConstructors = null;
-        if (isLifecycleParent(superclass)) {
-            if (getObserverConstructorType(superclass) == REFLECTIVE_CALLBACK) {
-                return REFLECTIVE_CALLBACK;
-            }
-            adapterConstructors = new ArrayList<>(sClassToAdapters.get(superclass));
-        }
-
-        for (Class<?> intrface : klass.getInterfaces()) {
-            if (!isLifecycleParent(intrface)) {
-                continue;
-            }
-            if (getObserverConstructorType(intrface) == REFLECTIVE_CALLBACK) {
-                return REFLECTIVE_CALLBACK;
-            }
-            if (adapterConstructors == null) {
-                adapterConstructors = new ArrayList<>();
-            }
-            adapterConstructors.addAll(sClassToAdapters.get(intrface));
-        }
-        if (adapterConstructors != null) {
-            sClassToAdapters.put(klass, adapterConstructors);
-            return GENERATED_CALLBACK;
-        }
-
-        return REFLECTIVE_CALLBACK;
-    }
-
-    private static boolean isLifecycleParent(Class<?> klass) {
-        return klass != null && LifecycleObserver.class.isAssignableFrom(klass);
-    }
-
-    /**
-     * Create a name for an adapter class.
-     */
-    public static String getAdapterName(String className) {
-        return className.replace(".", "_") + "_LifecycleAdapter";
-    }
-}
diff --git a/android/arch/lifecycle/LifecyclingTest.java b/android/arch/lifecycle/LifecyclingTest.java
deleted file mode 100644
index 70ce84c..0000000
--- a/android/arch/lifecycle/LifecyclingTest.java
+++ /dev/null
@@ -1,83 +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.arch.lifecycle;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.lifecycle.observers.DerivedSequence1;
-import android.arch.lifecycle.observers.DerivedSequence2;
-import android.arch.lifecycle.observers.DerivedWithNewMethods;
-import android.arch.lifecycle.observers.DerivedWithNoNewMethods;
-import android.arch.lifecycle.observers.DerivedWithOverridenMethodsWithLfAnnotation;
-import android.arch.lifecycle.observers.InterfaceImpl1;
-import android.arch.lifecycle.observers.InterfaceImpl2;
-import android.arch.lifecycle.observers.InterfaceImpl3;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class LifecyclingTest {
-
-    @Test
-    public void testDerivedWithNewLfMethodsNoGeneratedAdapter() {
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new DerivedWithNewMethods());
-        assertThat(callback, instanceOf(ReflectiveGenericLifecycleObserver.class));
-    }
-
-    @Test
-    public void testDerivedWithNoNewLfMethodsNoGeneratedAdapter() {
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new DerivedWithNoNewMethods());
-        assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
-    }
-
-    @Test
-    public void testDerivedWithOverridenMethodsNoGeneratedAdapter() {
-        GenericLifecycleObserver callback = Lifecycling.getCallback(
-                new DerivedWithOverridenMethodsWithLfAnnotation());
-        // that is not effective but...
-        assertThat(callback, instanceOf(ReflectiveGenericLifecycleObserver.class));
-    }
-
-    @Test
-    public void testInterfaceImpl1NoGeneratedAdapter() {
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new InterfaceImpl1());
-        assertThat(callback, instanceOf(SingleGeneratedAdapterObserver.class));
-    }
-
-    @Test
-    public void testInterfaceImpl2NoGeneratedAdapter() {
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new InterfaceImpl2());
-        assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
-    }
-
-    @Test
-    public void testInterfaceImpl3NoGeneratedAdapter() {
-        GenericLifecycleObserver callback = Lifecycling.getCallback(new InterfaceImpl3());
-        assertThat(callback, instanceOf(CompositeGeneratedAdaptersObserver.class));
-    }
-
-    @Test
-    public void testDerivedSequence() {
-        GenericLifecycleObserver callback2 = Lifecycling.getCallback(new DerivedSequence2());
-        assertThat(callback2, instanceOf(ReflectiveGenericLifecycleObserver.class));
-        GenericLifecycleObserver callback1 = Lifecycling.getCallback(new DerivedSequence1());
-        assertThat(callback1, instanceOf(SingleGeneratedAdapterObserver.class));
-    }
-}
diff --git a/android/arch/lifecycle/LiveData.java b/android/arch/lifecycle/LiveData.java
deleted file mode 100644
index 3aea6ac..0000000
--- a/android/arch/lifecycle/LiveData.java
+++ /dev/null
@@ -1,4 +0,0 @@
-//LiveData interface for tests
-package android.arch.lifecycle;
-public class LiveData<T> {
-}
diff --git a/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java b/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
deleted file mode 100644
index 836cfff..0000000
--- a/android/arch/lifecycle/LiveDataOnSaveInstanceStateTest.java
+++ /dev/null
@@ -1,177 +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.arch.lifecycle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.app.Instrumentation;
-import android.arch.lifecycle.testapp.CollectingSupportActivity;
-import android.arch.lifecycle.testapp.CollectingSupportFragment;
-import android.arch.lifecycle.testapp.NavigationDialogActivity;
-import android.content.Intent;
-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.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.app.FragmentActivity;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LiveDataOnSaveInstanceStateTest {
-    @Rule
-    public ActivityTestRule<CollectingSupportActivity> mActivityTestRule =
-            new ActivityTestRule<>(CollectingSupportActivity.class);
-
-    @Test
-    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
-    public void liveData_partiallyObscuredActivity_maxSdkM() throws Throwable {
-        CollectingSupportActivity activity = mActivityTestRule.getActivity();
-
-        liveData_partiallyObscuredLifecycleOwner_maxSdkM(activity);
-    }
-
-    @Test
-    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
-    public void liveData_partiallyObscuredActivityWithFragment_maxSdkM() throws Throwable {
-        CollectingSupportActivity activity = mActivityTestRule.getActivity();
-        CollectingSupportFragment fragment = new CollectingSupportFragment();
-        mActivityTestRule.runOnUiThread(() -> activity.replaceFragment(fragment));
-
-        liveData_partiallyObscuredLifecycleOwner_maxSdkM(fragment);
-    }
-
-    @Test
-    @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
-    public void liveData_partiallyObscuredActivityFragmentInFragment_maxSdkM() throws Throwable {
-        CollectingSupportActivity activity = mActivityTestRule.getActivity();
-        CollectingSupportFragment fragment = new CollectingSupportFragment();
-        CollectingSupportFragment fragment2 = new CollectingSupportFragment();
-        mActivityTestRule.runOnUiThread(() -> {
-            activity.replaceFragment(fragment);
-            fragment.replaceFragment(fragment2);
-        });
-
-        liveData_partiallyObscuredLifecycleOwner_maxSdkM(fragment2);
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-    public void liveData_partiallyObscuredActivity_minSdkN() throws Throwable {
-        CollectingSupportActivity activity = mActivityTestRule.getActivity();
-
-        liveData_partiallyObscuredLifecycleOwner_minSdkN(activity);
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-    public void liveData_partiallyObscuredActivityWithFragment_minSdkN() throws Throwable {
-        CollectingSupportActivity activity = mActivityTestRule.getActivity();
-        CollectingSupportFragment fragment = new CollectingSupportFragment();
-        mActivityTestRule.runOnUiThread(() -> activity.replaceFragment(fragment));
-
-        liveData_partiallyObscuredLifecycleOwner_minSdkN(fragment);
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-    public void liveData_partiallyObscuredActivityFragmentInFragment_minSdkN() throws Throwable {
-        CollectingSupportActivity activity = mActivityTestRule.getActivity();
-        CollectingSupportFragment fragment = new CollectingSupportFragment();
-        CollectingSupportFragment fragment2 = new CollectingSupportFragment();
-        mActivityTestRule.runOnUiThread(() -> {
-            activity.replaceFragment(fragment);
-            fragment.replaceFragment(fragment2);
-        });
-
-        liveData_partiallyObscuredLifecycleOwner_minSdkN(fragment2);
-    }
-
-    private void liveData_partiallyObscuredLifecycleOwner_maxSdkM(LifecycleOwner lifecycleOwner)
-            throws Throwable {
-        final AtomicInteger atomicInteger = new AtomicInteger(0);
-        MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
-        mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(0));
-
-        TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
-
-        mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
-
-        final FragmentActivity dialogActivity = launchDialog();
-
-        TestUtils.waitTillCreated(lifecycleOwner, mActivityTestRule);
-
-        // Change the LiveData value and assert that the observer is not called given that the
-        // lifecycle is in the CREATED state.
-        mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(1));
-        assertThat(atomicInteger.get(), is(0));
-
-        // Finish the dialog Activity, wait for the main activity to be resumed, and assert that
-        // the observer's onChanged method is called.
-        mActivityTestRule.runOnUiThread(dialogActivity::finish);
-        TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
-        assertThat(atomicInteger.get(), is(1));
-    }
-
-    private void liveData_partiallyObscuredLifecycleOwner_minSdkN(LifecycleOwner lifecycleOwner)
-            throws Throwable {
-        final AtomicInteger atomicInteger = new AtomicInteger(0);
-        MutableLiveData<Integer> mutableLiveData = new MutableLiveData<>();
-        mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(0));
-
-        TestUtils.waitTillResumed(lifecycleOwner, mActivityTestRule);
-
-        mutableLiveData.observe(lifecycleOwner, atomicInteger::set);
-
-        // Launch the NavigationDialogActivity, partially obscuring the activity, and wait for the
-        // lifecycleOwner to hit onPause (or enter the STARTED state).  On API 24 and above, this
-        // onPause should be the last lifecycle method called (and the STARTED state should be the
-        // final resting state).
-        launchDialog();
-        TestUtils.waitTillStarted(lifecycleOwner, mActivityTestRule);
-
-        // Change the LiveData's value and verify that the observer's onChanged method is called
-        // since we are in the STARTED state.
-        mActivityTestRule.runOnUiThread(() -> mutableLiveData.setValue(1));
-        assertThat(atomicInteger.get(), is(1));
-    }
-
-    private FragmentActivity launchDialog() throws Throwable {
-        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
-                NavigationDialogActivity.class.getCanonicalName(), null, false);
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.addMonitor(monitor);
-
-        FragmentActivity activity = mActivityTestRule.getActivity();
-        // helps with less flaky API 16 tests
-        Intent intent = new Intent(activity, NavigationDialogActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        activity.startActivity(intent);
-        FragmentActivity fragmentActivity = (FragmentActivity) monitor.waitForActivity();
-        TestUtils.waitTillResumed(fragmentActivity, mActivityTestRule);
-        return fragmentActivity;
-    }
-}
diff --git a/android/arch/lifecycle/LiveDataReactiveStreams.java b/android/arch/lifecycle/LiveDataReactiveStreams.java
deleted file mode 100644
index ed3c57c..0000000
--- a/android/arch/lifecycle/LiveDataReactiveStreams.java
+++ /dev/null
@@ -1,284 +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.arch.lifecycle;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import org.reactivestreams.Publisher;
-import org.reactivestreams.Subscriber;
-import org.reactivestreams.Subscription;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * 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.
-     */
-    @NonNull
-    public static <T> Publisher<T> toPublisher(
-            @NonNull LifecycleOwner lifecycle, @NonNull LiveData<T> liveData) {
-
-        return new LiveDataPublisher<>(lifecycle, liveData);
-    }
-
-    private static final class LiveDataPublisher<T> implements Publisher<T> {
-        final LifecycleOwner mLifecycle;
-        final LiveData<T> mLiveData;
-
-        LiveDataPublisher(LifecycleOwner lifecycle, LiveData<T> liveData) {
-            this.mLifecycle = lifecycle;
-            this.mLiveData = liveData;
-        }
-
-        @Override
-        public void subscribe(Subscriber<? super T> subscriber) {
-            subscriber.onSubscribe(new LiveDataSubscription<T>(subscriber, mLifecycle, mLiveData));
-        }
-
-        static final class LiveDataSubscription<T> implements Subscription, Observer<T> {
-            final Subscriber<? super T> mSubscriber;
-            final LifecycleOwner mLifecycle;
-            final LiveData<T> mLiveData;
-
-            volatile boolean mCanceled;
-            // used on main thread only
-            boolean mObserving;
-            long mRequested;
-            // used on main thread only
-            @Nullable
-            T mLatest;
-
-            LiveDataSubscription(final Subscriber<? super T> subscriber,
-                    final LifecycleOwner lifecycle, final LiveData<T> liveData) {
-                this.mSubscriber = subscriber;
-                this.mLifecycle = lifecycle;
-                this.mLiveData = liveData;
-            }
-
-            @Override
-            public void onChanged(@Nullable T t) {
-                if (mCanceled) {
-                    return;
-                }
-                if (mRequested > 0) {
-                    mLatest = null;
-                    mSubscriber.onNext(t);
-                    if (mRequested != Long.MAX_VALUE) {
-                        mRequested--;
-                    }
-                } else {
-                    mLatest = t;
-                }
-            }
-
-            @Override
-            public void request(final long n) {
-                if (mCanceled) {
-                    return;
-                }
-                ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mCanceled) {
-                            return;
-                        }
-                        if (n <= 0L) {
-                            mCanceled = true;
-                            if (mObserving) {
-                                mLiveData.removeObserver(LiveDataSubscription.this);
-                                mObserving = false;
-                            }
-                            mLatest = null;
-                            mSubscriber.onError(
-                                    new IllegalArgumentException("Non-positive request"));
-                            return;
-                        }
-
-                        // Prevent overflowage.
-                        mRequested = mRequested + n >= mRequested
-                                ? mRequested + n : Long.MAX_VALUE;
-                        if (!mObserving) {
-                            mObserving = true;
-                            mLiveData.observe(mLifecycle, LiveDataSubscription.this);
-                        } else if (mLatest != null) {
-                            onChanged(mLatest);
-                            mLatest = null;
-                        }
-                    }
-                });
-            }
-
-            @Override
-            public void cancel() {
-                if (mCanceled) {
-                    return;
-                }
-                mCanceled = true;
-                ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mObserving) {
-                            mLiveData.removeObserver(LiveDataSubscription.this);
-                            mObserving = false;
-                        }
-                        mLatest = null;
-                    }
-                });
-            }
-        }
-    }
-
-    /**
-     * Creates an Observable {@link LiveData} stream from a ReactiveStreams publisher.
-     *
-     * <p>
-     * When the LiveData becomes active, it subscribes to the emissions from the Publisher.
-     *
-     * <p>
-     * When the LiveData becomes inactive, the subscription is cleared.
-     * LiveData holds the last value emitted by the Publisher when the LiveData was active.
-     * <p>
-     * Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
-     * added, it will automatically notify with the last value held in LiveData,
-     * which might not be the last value emitted by the Publisher.
-     * <p>
-     * Note that LiveData does NOT handle errors and it expects that errors are treated as states
-     * in the data that's held. In case of an error being emitted by the publisher, an error will
-     * be propagated to the main thread and the app will crash.
-     *
-     * @param <T> The type of data hold by this instance.
-     */
-    @NonNull
-    public static <T> LiveData<T> fromPublisher(@NonNull Publisher<T> publisher) {
-        return new PublisherLiveData<>(publisher);
-    }
-
-    /**
-     * Defines a {@link LiveData} object that wraps a {@link Publisher}.
-     *
-     * <p>
-     * When the LiveData becomes active, it subscribes to the emissions from the Publisher.
-     *
-     * <p>
-     * When the LiveData becomes inactive, the subscription is cleared.
-     * LiveData holds the last value emitted by the Publisher when the LiveData was active.
-     * <p>
-     * Therefore, in the case of a hot RxJava Observable, when a new LiveData {@link Observer} is
-     * added, it will automatically notify with the last value held in LiveData,
-     * which might not be the last value emitted by the Publisher.
-     *
-     * <p>
-     * Note that LiveData does NOT handle errors and it expects that errors are treated as states
-     * in the data that's held. In case of an error being emitted by the publisher, an error will
-     * be propagated to the main thread and the app will crash.
-     *
-     * @param <T> The type of data hold by this instance.
-     */
-    private static class PublisherLiveData<T> extends LiveData<T> {
-        private final Publisher<T> mPublisher;
-        final AtomicReference<LiveDataSubscriber> mSubscriber;
-
-        PublisherLiveData(@NonNull Publisher<T> publisher) {
-            mPublisher = publisher;
-            mSubscriber = new AtomicReference<>();
-        }
-
-        @Override
-        protected void onActive() {
-            super.onActive();
-            LiveDataSubscriber s = new LiveDataSubscriber();
-            mSubscriber.set(s);
-            mPublisher.subscribe(s);
-        }
-
-        @Override
-        protected void onInactive() {
-            super.onInactive();
-            LiveDataSubscriber s = mSubscriber.getAndSet(null);
-            if (s != null) {
-                s.cancelSubscription();
-            }
-        }
-
-        final class LiveDataSubscriber extends AtomicReference<Subscription>
-                implements Subscriber<T> {
-
-            @Override
-            public void onSubscribe(Subscription s) {
-                if (compareAndSet(null, s)) {
-                    s.request(Long.MAX_VALUE);
-                } else {
-                    s.cancel();
-                }
-            }
-
-            @Override
-            public void onNext(T item) {
-                postValue(item);
-            }
-
-            @Override
-            public void onError(final Throwable ex) {
-                mSubscriber.compareAndSet(this, null);
-
-                ArchTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        // Errors should be handled upstream, so propagate as a crash.
-                        throw new RuntimeException("LiveData does not handle errors. Errors from "
-                                + "publishers should be handled upstream and propagated as "
-                                + "state", ex);
-                    }
-                });
-            }
-
-            @Override
-            public void onComplete() {
-                mSubscriber.compareAndSet(this, null);
-            }
-
-            public void cancelSubscription() {
-                Subscription s = get();
-                if (s != null) {
-                    s.cancel();
-                }
-            }
-        }
-    }
-}
diff --git a/android/arch/lifecycle/LiveDataReactiveStreamsTest.java b/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
deleted file mode 100644
index 163cff0..0000000
--- a/android/arch/lifecycle/LiveDataReactiveStreamsTest.java
+++ /dev/null
@@ -1,381 +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.arch.lifecycle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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 java.util.concurrent.TimeUnit;
-
-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 LifecycleOwner mLifecycleOwner;
-
-    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() {
-        mLifecycleOwner = new LifecycleOwner() {
-            LifecycleRegistry mRegistry = new LifecycleRegistry(this);
-            {
-                mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
-            }
-
-            @Override
-            public Lifecycle getLifecycle() {
-                return mRegistry;
-            }
-        };
-        mTestThread = Thread.currentThread();
-        ArchTaskExecutor.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() {
-        ArchTaskExecutor.getInstance().setDelegate(null);
-    }
-
-    @Test
-    public void convertsFromPublisher() {
-        PublishProcessor<String> processor = PublishProcessor.create();
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        processor.onNext("foo");
-        processor.onNext("bar");
-        processor.onNext("baz");
-
-        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "bar", "baz")));
-    }
-
-    @Test
-    public void convertsFromPublisherSubscribeWithDelay() {
-        PublishProcessor<String> processor = PublishProcessor.create();
-        processor.delaySubscription(100, TimeUnit.SECONDS, sBackgroundScheduler);
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        processor.onNext("foo");
-        liveData.removeObserver(mObserver);
-        sBackgroundScheduler.triggerActions();
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        processor.onNext("bar");
-        processor.onNext("baz");
-
-        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "foo", "bar", "baz")));
-    }
-
-    @Test
-    public void convertsFromPublisherThrowsException() {
-        PublishProcessor<String> processor = PublishProcessor.create();
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        IllegalStateException exception = new IllegalStateException("test exception");
-        try {
-            processor.onError(exception);
-            fail("Runtime Exception expected");
-        } catch (RuntimeException ex) {
-            assertEquals(ex.getCause(), exception);
-        }
-    }
-
-    @Test
-    public void convertsFromPublisherWithMultipleObservers() {
-        final List<String> output2 = new ArrayList<>();
-        PublishProcessor<String> processor = PublishProcessor.create();
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        processor.onNext("foo");
-        processor.onNext("bar");
-
-        // The second observer should only get the newest value and any later values.
-        liveData.observe(mLifecycleOwner, 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 convertsFromPublisherWithMultipleObserversAfterInactive() {
-        final List<String> output2 = new ArrayList<>();
-        PublishProcessor<String> processor = PublishProcessor.create();
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        processor.onNext("foo");
-        processor.onNext("bar");
-
-        // The second observer should only get the newest value and any later values.
-        liveData.observe(mLifecycleOwner, new Observer<String>() {
-            @Override
-            public void onChanged(@Nullable String s) {
-                output2.add(s);
-            }
-        });
-
-        liveData.removeObserver(mObserver);
-        processor.onNext("baz");
-
-        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "bar")));
-        assertThat(output2, is(Arrays.asList("bar", "baz")));
-    }
-
-    @Test
-    public void convertsFromPublisherAfterInactive() {
-        PublishProcessor<String> processor = PublishProcessor.create();
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        liveData.observe(mLifecycleOwner, mObserver);
-        processor.onNext("foo");
-        liveData.removeObserver(mObserver);
-        processor.onNext("bar");
-
-        liveData.observe(mLifecycleOwner, mObserver);
-        processor.onNext("baz");
-
-        assertThat(mLiveDataOutput, is(Arrays.asList("foo", "foo", "baz")));
-    }
-
-    @Test
-    public void convertsFromPublisherManagesSubscriptions() {
-        PublishProcessor<String> processor = PublishProcessor.create();
-        LiveData<String> liveData = LiveDataReactiveStreams.fromPublisher(processor);
-
-        assertThat(processor.hasSubscribers(), is(false));
-        liveData.observe(mLifecycleOwner, mObserver);
-
-        // once the live data is active, there's a subscriber
-        assertThat(processor.hasSubscribers(), is(true));
-
-        liveData.removeObserver(mObserver);
-        // once the live data is inactive, the subscriber is removed
-        assertThat(processor.hasSubscribers(), is(false));
-    }
-
-    @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(mLifecycleOwner, 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(mLifecycleOwner, 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(mLifecycleOwner, 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(mLifecycleOwner, 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(mLifecycleOwner, 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/android/arch/lifecycle/LiveDataTest.java b/android/arch/lifecycle/LiveDataTest.java
deleted file mode 100644
index 046059b..0000000
--- a/android/arch/lifecycle/LiveDataTest.java
+++ /dev/null
@@ -1,842 +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.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.only;
-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.ArchTaskExecutor;
-import android.arch.core.executor.testing.InstantTaskExecutorRule;
-import android.support.annotation.Nullable;
-
-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.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mockito;
-
-@SuppressWarnings({"unchecked"})
-@RunWith(JUnit4.class)
-public class LiveDataTest {
-
-    @Rule
-    public InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
-
-    private PublicLiveData<String> mLiveData;
-    private MethodExec mActiveObserversChanged;
-
-    private LifecycleOwner mOwner;
-    private LifecycleRegistry mRegistry;
-
-    private LifecycleOwner mOwner2;
-    private LifecycleRegistry mRegistry2;
-
-    private LifecycleOwner mOwner3;
-    private Lifecycle mLifecycle3;
-    private Observer<String> mObserver3;
-
-    private LifecycleOwner mOwner4;
-    private Lifecycle mLifecycle4;
-    private Observer<String> mObserver4;
-
-    private boolean mInObserver;
-
-    @Before
-    public void init() {
-        mLiveData = new PublicLiveData<>();
-
-        mActiveObserversChanged = mock(MethodExec.class);
-        mLiveData.activeObserversChanged = mActiveObserversChanged;
-
-        mOwner = mock(LifecycleOwner.class);
-        mRegistry = new LifecycleRegistry(mOwner);
-        when(mOwner.getLifecycle()).thenReturn(mRegistry);
-
-        mOwner2 = mock(LifecycleOwner.class);
-        mRegistry2 = new LifecycleRegistry(mOwner2);
-        when(mOwner2.getLifecycle()).thenReturn(mRegistry2);
-
-        mInObserver = false;
-    }
-
-    @Before
-    public void initNonLifecycleRegistry() {
-        mOwner3 = mock(LifecycleOwner.class);
-        mLifecycle3 = mock(Lifecycle.class);
-        mObserver3 = (Observer<String>) mock(Observer.class);
-        when(mOwner3.getLifecycle()).thenReturn(mLifecycle3);
-
-        mOwner4 = mock(LifecycleOwner.class);
-        mLifecycle4 = mock(Lifecycle.class);
-        mObserver4 = (Observer<String>) mock(Observer.class);
-        when(mOwner4.getLifecycle()).thenReturn(mLifecycle4);
-    }
-
-    @After
-    public void removeExecutorDelegate() {
-        ArchTaskExecutor.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);
-
-        mLiveData.observe(mOwner, observer);
-        Throwable throwable = null;
-        try {
-            mLiveData.observe(mOwner2, 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 testRemoveDuringSetValue() {
-        mRegistry.handleLifecycleEvent(ON_START);
-        final Observer observer1 = spy(new Observer<String>() {
-            @Override
-            public void onChanged(String o) {
-                mLiveData.removeObserver(this);
-            }
-        });
-        Observer<String> observer2 = (Observer<String>) mock(Observer.class);
-        mLiveData.observeForever(observer1);
-        mLiveData.observe(mOwner, observer2);
-        mLiveData.setValue("gt");
-        verify(observer2).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());
-    }
-
-    @Test
-    public void testRemoveDuringAddition() {
-        mRegistry.handleLifecycleEvent(ON_START);
-        mLiveData.setValue("bla");
-        mLiveData.observeForever(new Observer<String>() {
-            @Override
-            public void onChanged(@Nullable String s) {
-                mLiveData.removeObserver(this);
-            }
-        });
-        assertThat(mLiveData.hasActiveObservers(), is(false));
-        InOrder inOrder = Mockito.inOrder(mActiveObserversChanged);
-        inOrder.verify(mActiveObserversChanged).onCall(true);
-        inOrder.verify(mActiveObserversChanged).onCall(false);
-        inOrder.verifyNoMoreInteractions();
-    }
-
-    @Test
-    public void testRemoveDuringBringingUpToState() {
-        mLiveData.setValue("bla");
-        mLiveData.observeForever(new Observer<String>() {
-            @Override
-            public void onChanged(@Nullable String s) {
-                mLiveData.removeObserver(this);
-            }
-        });
-        mRegistry.handleLifecycleEvent(ON_RESUME);
-        assertThat(mLiveData.hasActiveObservers(), is(false));
-        InOrder inOrder = Mockito.inOrder(mActiveObserversChanged);
-        inOrder.verify(mActiveObserversChanged).onCall(true);
-        inOrder.verify(mActiveObserversChanged).onCall(false);
-        inOrder.verifyNoMoreInteractions();
-    }
-
-    @Test
-    public void setValue_neverActive_observerOnChangedNotCalled() {
-        Observer<String> observer = (Observer<String>) mock(Observer.class);
-        mLiveData.observe(mOwner, observer);
-
-        mLiveData.setValue("1");
-
-        verify(observer, never()).onChanged(anyString());
-    }
-
-    @Test
-    public void setValue_twoObserversTwoStartedOwners_onChangedCalledOnBoth() {
-        Observer<String> observer1 = mock(Observer.class);
-        Observer<String> observer2 = mock(Observer.class);
-
-        mLiveData.observe(mOwner, observer1);
-        mLiveData.observe(mOwner2, observer2);
-
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-        mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
-        mLiveData.setValue("1");
-
-        verify(observer1).onChanged("1");
-        verify(observer2).onChanged("1");
-    }
-
-    @Test
-    public void setValue_twoObserversOneStartedOwner_onChangedCalledOnOneCorrectObserver() {
-        Observer<String> observer1 = mock(Observer.class);
-        Observer<String> observer2 = mock(Observer.class);
-
-        mLiveData.observe(mOwner, observer1);
-        mLiveData.observe(mOwner2, observer2);
-
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
-        mLiveData.setValue("1");
-
-        verify(observer1).onChanged("1");
-        verify(observer2, never()).onChanged(anyString());
-    }
-
-    @Test
-    public void setValue_twoObserversBothStartedAfterSetValue_onChangedCalledOnBoth() {
-        Observer<String> observer1 = mock(Observer.class);
-        Observer<String> observer2 = mock(Observer.class);
-
-        mLiveData.observe(mOwner, observer1);
-        mLiveData.observe(mOwner2, observer2);
-
-        mLiveData.setValue("1");
-
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-        mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
-        verify(observer1).onChanged("1");
-        verify(observer1).onChanged("1");
-    }
-
-    @Test
-    public void setValue_twoObserversOneStartedAfterSetValue_onChangedCalledOnCorrectObserver() {
-        Observer<String> observer1 = mock(Observer.class);
-        Observer<String> observer2 = mock(Observer.class);
-
-        mLiveData.observe(mOwner, observer1);
-        mLiveData.observe(mOwner2, observer2);
-
-        mLiveData.setValue("1");
-
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
-        verify(observer1).onChanged("1");
-        verify(observer2, never()).onChanged(anyString());
-    }
-
-    @Test
-    public void setValue_twoObserversOneStarted_liveDataBecomesActive() {
-        Observer<String> observer1 = mock(Observer.class);
-        Observer<String> observer2 = mock(Observer.class);
-
-        mLiveData.observe(mOwner, observer1);
-        mLiveData.observe(mOwner2, observer2);
-
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
-        verify(mActiveObserversChanged).onCall(true);
-    }
-
-    @Test
-    public void setValue_twoObserversOneStopped_liveDataStaysActive() {
-        Observer<String> observer1 = mock(Observer.class);
-        Observer<String> observer2 = mock(Observer.class);
-
-        mLiveData.observe(mOwner, observer1);
-        mLiveData.observe(mOwner2, observer2);
-
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-        mRegistry2.handleLifecycleEvent(Lifecycle.Event.ON_START);
-
-        verify(mActiveObserversChanged).onCall(true);
-
-        reset(mActiveObserversChanged);
-        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
-
-        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
-    }
-
-    @Test
-    public void setValue_lifecycleIsCreatedNoEvent_liveDataBecomesInactiveAndObserverNotCalled() {
-
-        // Arrange.
-
-        mLiveData.observe(mOwner3, mObserver3);
-
-        GenericLifecycleObserver lifecycleObserver = getGenericLifecycleObserver(mLifecycle3);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-
-        reset(mActiveObserversChanged);
-        reset(mObserver3);
-
-        // Act.
-
-        mLiveData.setValue("1");
-
-        // Assert.
-
-        verify(mActiveObserversChanged).onCall(false);
-        verify(mObserver3, never()).onChanged(anyString());
-    }
-
-    /*
-     * Arrange: LiveData was made inactive via SetValue (because the Lifecycle it was
-     * observing was in the CREATED state and no event was dispatched).
-     * Act: Lifecycle enters Started state and dispatches event.
-     * Assert: LiveData becomes active and dispatches new value to observer.
-     */
-    @Test
-    public void test_liveDataInactiveViaSetValueThenLifecycleResumes() {
-
-        // Arrange.
-
-        mLiveData.observe(mOwner3, mObserver3);
-
-        GenericLifecycleObserver lifecycleObserver = getGenericLifecycleObserver(mLifecycle3);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-        mLiveData.setValue("1");
-
-        reset(mActiveObserversChanged);
-        reset(mObserver3);
-
-        // Act.
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-
-        // Assert.
-
-        verify(mActiveObserversChanged).onCall(true);
-        verify(mObserver3).onChanged("1");
-    }
-
-    /*
-     * Arrange: One of two Lifecycles enter the CREATED state without sending an event.
-     * Act: Lifecycle's setValue method is called with new value.
-     * Assert: LiveData stays active and new value is dispatched to Lifecycle that is still at least
-     * STARTED.
-     */
-    @Test
-    public void setValue_oneOfTwoLifecyclesAreCreatedNoEvent() {
-
-        // Arrange.
-
-        mLiveData.observe(mOwner3, mObserver3);
-        mLiveData.observe(mOwner4, mObserver4);
-
-        GenericLifecycleObserver lifecycleObserver3 = getGenericLifecycleObserver(mLifecycle3);
-        GenericLifecycleObserver lifecycleObserver4 = getGenericLifecycleObserver(mLifecycle4);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        when(mLifecycle4.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver3.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-        lifecycleObserver4.onStateChanged(mOwner4, Lifecycle.Event.ON_START);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-
-        reset(mActiveObserversChanged);
-        reset(mObserver3);
-        reset(mObserver4);
-
-        // Act.
-
-        mLiveData.setValue("1");
-
-        // Assert.
-
-        verify(mActiveObserversChanged, never()).onCall(anyBoolean());
-        verify(mObserver3, never()).onChanged(anyString());
-        verify(mObserver4).onChanged("1");
-    }
-
-    /*
-     * Arrange: Two observed Lifecycles enter the CREATED state without sending an event.
-     * Act: Lifecycle's setValue method is called with new value.
-     * Assert: LiveData becomes inactive and nothing is dispatched to either observer.
-     */
-    @Test
-    public void setValue_twoLifecyclesAreCreatedNoEvent() {
-
-        // Arrange.
-
-        mLiveData.observe(mOwner3, mObserver3);
-        mLiveData.observe(mOwner4, mObserver4);
-
-        GenericLifecycleObserver lifecycleObserver3 = getGenericLifecycleObserver(mLifecycle3);
-        GenericLifecycleObserver lifecycleObserver4 = getGenericLifecycleObserver(mLifecycle4);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        when(mLifecycle4.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver3.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-        lifecycleObserver4.onStateChanged(mOwner4, Lifecycle.Event.ON_START);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-        when(mLifecycle4.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-
-        reset(mActiveObserversChanged);
-        reset(mObserver3);
-        reset(mObserver4);
-
-        // Act.
-
-        mLiveData.setValue("1");
-
-        // Assert.
-
-        verify(mActiveObserversChanged).onCall(false);
-        verify(mObserver3, never()).onChanged(anyString());
-        verify(mObserver3, never()).onChanged(anyString());
-    }
-
-    /*
-     * Arrange: LiveData was made inactive via SetValue (because both Lifecycles it was
-     * observing were in the CREATED state and no event was dispatched).
-     * Act: One Lifecycle enters STARTED state and dispatches lifecycle event.
-     * Assert: LiveData becomes active and dispatches new value to observer associated with started
-     * Lifecycle.
-     */
-    @Test
-    public void test_liveDataInactiveViaSetValueThenOneLifecycleResumes() {
-
-        // Arrange.
-
-        mLiveData.observe(mOwner3, mObserver3);
-        mLiveData.observe(mOwner4, mObserver4);
-
-        GenericLifecycleObserver lifecycleObserver3 = getGenericLifecycleObserver(mLifecycle3);
-        GenericLifecycleObserver lifecycleObserver4 = getGenericLifecycleObserver(mLifecycle4);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        when(mLifecycle4.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver3.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-        lifecycleObserver4.onStateChanged(mOwner4, Lifecycle.Event.ON_START);
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-        when(mLifecycle4.getCurrentState()).thenReturn(Lifecycle.State.CREATED);
-
-        mLiveData.setValue("1");
-
-        reset(mActiveObserversChanged);
-        reset(mObserver3);
-        reset(mObserver4);
-
-        // Act.
-
-        when(mLifecycle3.getCurrentState()).thenReturn(Lifecycle.State.STARTED);
-        lifecycleObserver3.onStateChanged(mOwner3, Lifecycle.Event.ON_START);
-
-        // Assert.
-
-        verify(mActiveObserversChanged).onCall(true);
-        verify(mObserver3).onChanged("1");
-        verify(mObserver4, never()).onChanged(anyString());
-    }
-
-    @Test
-    public void nestedForeverObserver() {
-        mLiveData.setValue(".");
-        mLiveData.observeForever(new Observer<String>() {
-            @Override
-            public void onChanged(@Nullable String s) {
-                mLiveData.observeForever(mock(Observer.class));
-                mLiveData.removeObserver(this);
-            }
-        });
-        verify(mActiveObserversChanged, only()).onCall(true);
-    }
-
-    @Test
-    public void readdForeverObserver() {
-        Observer observer = mock(Observer.class);
-        mLiveData.observeForever(observer);
-        mLiveData.observeForever(observer);
-        mLiveData.removeObserver(observer);
-        assertThat(mLiveData.hasObservers(), is(false));
-    }
-
-    private GenericLifecycleObserver getGenericLifecycleObserver(Lifecycle lifecycle) {
-        ArgumentCaptor<GenericLifecycleObserver> captor =
-                ArgumentCaptor.forClass(GenericLifecycleObserver.class);
-        verify(lifecycle).addObserver(captor.capture());
-        return (captor.getValue());
-    }
-
-    @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/android/arch/lifecycle/MediatorLiveData.java b/android/arch/lifecycle/MediatorLiveData.java
deleted file mode 100644
index 5864739..0000000
--- a/android/arch/lifecycle/MediatorLiveData.java
+++ /dev/null
@@ -1,156 +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.arch.lifecycle;
-
-import android.arch.core.internal.SafeIterableMap;
-import android.support.annotation.CallSuper;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.Map;
-
-/**
- * {@link LiveData} subclass which may observe 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.
- * <p>
- * Consider the following scenario: we have 2 instances of {@code LiveData}, let's name them
- * {@code liveData1} and {@code liveData2}, and we want to merge their emissions in one object:
- * {@code liveDataMerger}. Then, {@code liveData1} and {@code liveData2} will become sources for
- * the {@code MediatorLiveData liveDataMerger} and every time {@code onChanged} callback
- * is called for either of them, we set a new value in {@code liveDataMerger}.
- *
- * <pre>
- * LiveData<Integer> liveData1 = ...;
- * LiveData<Integer> liveData2 = ...;
- *
- * MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
- * liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
- * liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
- * </pre>
- * <p>
- * Let's consider that we only want 10 values emitted by {@code liveData1}, to be
- * merged in the {@code liveDataMerger}. Then, after 10 values, we can stop listening to {@code
- * liveData1} and remove it as a source.
- * <pre>
- * liveDataMerger.addSource(liveData1, new Observer<Integer>() {
- *      private int count = 1;
- *
- *      {@literal @}Override public void onChanged(@Nullable Integer s) {
- *          count++;
- *          liveDataMerger.setValue(s);
- *          if (count > 10) {
- *              liveDataMerger.removeSource(liveData1);
- *          }
- *      }
- * });
- * </pre>
- *
- * @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(@NonNull LiveData<S> source, @NonNull 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(@NonNull 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> implements Observer<V> {
-        final LiveData<V> mLiveData;
-        final Observer<V> mObserver;
-        int mVersion = START_VERSION;
-
-        Source(LiveData<V> liveData, final Observer<V> observer) {
-            mLiveData = liveData;
-            mObserver = observer;
-        }
-
-        void plug() {
-            mLiveData.observeForever(this);
-        }
-
-        void unplug() {
-            mLiveData.removeObserver(this);
-        }
-
-        @Override
-        public void onChanged(@Nullable V v) {
-            if (mVersion != mLiveData.getVersion()) {
-                mVersion = mLiveData.getVersion();
-                mObserver.onChanged(v);
-            }
-        }
-    }
-}
diff --git a/android/arch/lifecycle/MediatorLiveDataTest.java b/android/arch/lifecycle/MediatorLiveDataTest.java
deleted file mode 100644
index e2eadbe..0000000
--- a/android/arch/lifecycle/MediatorLiveDataTest.java
+++ /dev/null
@@ -1,239 +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.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.ArchTaskExecutor;
-import android.arch.lifecycle.util.InstantTaskExecutor;
-import android.support.annotation.Nullable;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@SuppressWarnings("unchecked")
-@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() {
-        ArchTaskExecutor.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 addSameSourceWithSameObserver() {
-        Observer observer = mock(Observer.class);
-        mMediator.addSource(mSource, observer);
-        mMediator.addSource(mSource, observer);
-        // no exception was thrown
-    }
-
-    @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/android/arch/lifecycle/MethodCallsLogger.java b/android/arch/lifecycle/MethodCallsLogger.java
deleted file mode 100644
index 031e43e..0000000
--- a/android/arch/lifecycle/MethodCallsLogger.java
+++ /dev/null
@@ -1,42 +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.arch.lifecycle;
-
-import android.support.annotation.RestrictTo;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class MethodCallsLogger {
-    private Map<String, Integer> mCalledMethods = new HashMap<>();
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public boolean approveCall(String name, int type) {
-        Integer nullableMask = mCalledMethods.get(name);
-        int mask = nullableMask != null ? nullableMask : 0;
-        boolean wasCalled = (mask & type) != 0;
-        mCalledMethods.put(name, mask | type);
-        return !wasCalled;
-    }
-}
diff --git a/android/arch/lifecycle/MissingClassTest.java b/android/arch/lifecycle/MissingClassTest.java
deleted file mode 100644
index 81a0756..0000000
--- a/android/arch/lifecycle/MissingClassTest.java
+++ /dev/null
@@ -1,44 +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.arch.lifecycle;
-
-import android.app.PictureInPictureParams;
-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;
-
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.N_MR1)
-@SmallTest
-public class MissingClassTest {
-    public static class ObserverWithMissingClasses {
-        @SuppressWarnings("unused")
-        public void newApiMethod(PictureInPictureParams params) {}
-
-        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
-        public void onResume() {}
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testMissingApi() {
-        new ReflectiveGenericLifecycleObserver(new ObserverWithMissingClasses());
-    }
-}
diff --git a/android/arch/lifecycle/MutableLiveData.java b/android/arch/lifecycle/MutableLiveData.java
deleted file mode 100644
index ecd7752..0000000
--- a/android/arch/lifecycle/MutableLiveData.java
+++ /dev/null
@@ -1,35 +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.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/android/arch/lifecycle/Observer.java b/android/arch/lifecycle/Observer.java
deleted file mode 100644
index 0e36775..0000000
--- a/android/arch/lifecycle/Observer.java
+++ /dev/null
@@ -1,34 +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.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/android/arch/lifecycle/OnLifecycleEvent.java b/android/arch/lifecycle/OnLifecycleEvent.java
deleted file mode 100644
index 9a86b0c..0000000
--- a/android/arch/lifecycle/OnLifecycleEvent.java
+++ /dev/null
@@ -1,29 +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.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/android/arch/lifecycle/PartiallyCoveredActivityTest.java b/android/arch/lifecycle/PartiallyCoveredActivityTest.java
deleted file mode 100644
index 07a9dc5..0000000
--- a/android/arch/lifecycle/PartiallyCoveredActivityTest.java
+++ /dev/null
@@ -1,200 +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.arch.lifecycle;
-
-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.OrderedTuples.CREATE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.DESTROY;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.PAUSE;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.RESUME;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.START;
-import static android.arch.lifecycle.TestUtils.OrderedTuples.STOP;
-import static android.arch.lifecycle.TestUtils.flatMap;
-import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-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.app.Instrumentation;
-import android.arch.lifecycle.testapp.CollectingLifecycleOwner;
-import android.arch.lifecycle.testapp.CollectingSupportActivity;
-import android.arch.lifecycle.testapp.CollectingSupportFragment;
-import android.arch.lifecycle.testapp.NavigationDialogActivity;
-import android.arch.lifecycle.testapp.TestEvent;
-import android.content.Intent;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.util.Pair;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-
-/**
- * Runs tests about the state when an activity is partially covered by another activity. Pre
- * API 24, framework behavior changes so the test rely on whether state is saved or not and makes
- * assertions accordingly.
- */
-@SuppressWarnings("unchecked")
-@RunWith(Parameterized.class)
-@LargeTest
-public class PartiallyCoveredActivityTest {
-    private static final List[] IF_SAVED = new List[]{
-            // when overlaid
-            flatMap(CREATE, START, RESUME, PAUSE,
-                    singletonList(new Pair<>(LIFECYCLE_EVENT, ON_STOP))),
-            // post dialog dismiss
-            asList(new Pair<>(OWNER_CALLBACK, ON_RESUME),
-                    new Pair<>(LIFECYCLE_EVENT, ON_START),
-                    new Pair<>(LIFECYCLE_EVENT, ON_RESUME)),
-            // post finish
-            flatMap(PAUSE, STOP, DESTROY)};
-
-    private static final List[] IF_NOT_SAVED = new List[]{
-            // when overlaid
-            flatMap(CREATE, START, RESUME, PAUSE),
-            // post dialog dismiss
-            flatMap(RESUME),
-            // post finish
-            flatMap(PAUSE, STOP, DESTROY)};
-
-    private static final boolean sShouldSave = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;
-    private static final List<Pair<TestEvent, Lifecycle.Event>>[] EXPECTED =
-            sShouldSave ? IF_SAVED : IF_NOT_SAVED;
-
-    @Rule
-    public ActivityTestRule<CollectingSupportActivity> activityRule =
-            new ActivityTestRule<CollectingSupportActivity>(
-                    CollectingSupportActivity.class) {
-                @Override
-                protected Intent getActivityIntent() {
-                    // helps with less flaky API 16 tests
-                    Intent intent = new Intent(InstrumentationRegistry.getTargetContext(),
-                            CollectingSupportActivity.class);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-                    return intent;
-                }
-            };
-    private final boolean mDismissDialog;
-
-    @Parameterized.Parameters(name = "dismissDialog_{0}")
-    public static List<Boolean> dismissDialog() {
-        return asList(true, false);
-    }
-
-    public PartiallyCoveredActivityTest(boolean dismissDialog) {
-        mDismissDialog = dismissDialog;
-    }
-
-    @Test
-    public void coveredWithDialog_activity() throws Throwable {
-        final CollectingSupportActivity activity = activityRule.getActivity();
-        runTest(activity);
-    }
-
-    @Test
-    public void coveredWithDialog_fragment() throws Throwable {
-        CollectingSupportFragment fragment = new CollectingSupportFragment();
-        activityRule.runOnUiThread(() -> activityRule.getActivity().replaceFragment(fragment));
-        runTest(fragment);
-    }
-
-    @Test
-    public void coveredWithDialog_childFragment() throws Throwable {
-        CollectingSupportFragment parentFragment = new CollectingSupportFragment();
-        CollectingSupportFragment childFragment = new CollectingSupportFragment();
-        activityRule.runOnUiThread(() -> {
-            activityRule.getActivity().replaceFragment(parentFragment);
-            parentFragment.replaceFragment(childFragment);
-        });
-        runTest(childFragment);
-    }
-
-    private void runTest(CollectingLifecycleOwner owner) throws Throwable {
-        TestUtils.waitTillResumed(owner, activityRule);
-        FragmentActivity dialog = launchDialog();
-        assertStateSaving();
-        waitForIdle();
-        assertThat(owner.copyCollectedEvents(), is(EXPECTED[0]));
-        List<Pair<TestEvent, Lifecycle.Event>> expected;
-        if (mDismissDialog) {
-            dialog.finish();
-            TestUtils.waitTillResumed(activityRule.getActivity(), activityRule);
-            assertThat(owner.copyCollectedEvents(), is(flatMap(EXPECTED[0], EXPECTED[1])));
-            expected = flatMap(EXPECTED[0], EXPECTED[1], EXPECTED[2]);
-        } else {
-            expected = flatMap(CREATE, START, RESUME, PAUSE, STOP, DESTROY);
-        }
-        CollectingSupportActivity activity = activityRule.getActivity();
-        activityRule.finishActivity();
-        TestUtils.waitTillDestroyed(activity, activityRule);
-        assertThat(owner.copyCollectedEvents(), is(expected));
-    }
-
-    // test sanity
-    private void assertStateSaving() throws ExecutionException, InterruptedException {
-        final CollectingSupportActivity activity = activityRule.getActivity();
-        if (sShouldSave) {
-            // state should be saved. wait for it to be saved
-            assertThat("test sanity",
-                    activity.waitForStateSave(20), is(true));
-            assertThat("test sanity", activity.getSupportFragmentManager()
-                    .isStateSaved(), is(true));
-        } else {
-            // should should not be saved
-            assertThat("test sanity", activity.getSupportFragmentManager()
-                    .isStateSaved(), is(false));
-        }
-    }
-
-    private void waitForIdle() {
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    private FragmentActivity launchDialog() throws Throwable {
-        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
-                NavigationDialogActivity.class.getCanonicalName(), null, false);
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.addMonitor(monitor);
-
-        FragmentActivity activity = activityRule.getActivity();
-
-        Intent intent = new Intent(activity, NavigationDialogActivity.class);
-        // disabling animations helps with less flaky API 16 tests
-        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        activity.startActivity(intent);
-        FragmentActivity fragmentActivity = (FragmentActivity) monitor.waitForActivity();
-        TestUtils.waitTillResumed(fragmentActivity, activityRule);
-        return fragmentActivity;
-    }
-}
diff --git a/android/arch/lifecycle/ProcessLifecycleOwner.java b/android/arch/lifecycle/ProcessLifecycleOwner.java
deleted file mode 100644
index 74ea97f..0000000
--- a/android/arch/lifecycle/ProcessLifecycleOwner.java
+++ /dev/null
@@ -1,180 +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.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.NonNull;
-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();
-            }
-        });
-    }
-
-    @NonNull
-    @Override
-    public Lifecycle getLifecycle() {
-        return mRegistry;
-    }
-}
diff --git a/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java b/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java
deleted file mode 100644
index 6cf80d2..0000000
--- a/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java
+++ /dev/null
@@ -1,68 +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.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 ProcessLifecycleOwnerInitializer 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/android/arch/lifecycle/ProcessOwnerTest.java b/android/arch/lifecycle/ProcessOwnerTest.java
deleted file mode 100644
index 77baf94..0000000
--- a/android/arch/lifecycle/ProcessOwnerTest.java
+++ /dev/null
@@ -1,191 +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.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.arch.lifecycle.testapp.NonSupportActivity;
-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 android.support.v4.app.FragmentActivity;
-
-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 {
-        FragmentActivity 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);
-
-        FragmentActivity secondActivity = (FragmentActivity) monitor.waitForActivity();
-        assertThat("Failed to navigate", secondActivity, notNullValue());
-        checkProcessObserverSilent(secondActivity);
-    }
-
-    @Test
-    public void testNavigationToNonSupport() throws Throwable {
-        FragmentActivity firstActivity = setupObserverOnResume();
-        Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
-                NonSupportActivity.class.getCanonicalName(), null, false);
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.addMonitor(monitor);
-
-        Intent intent = new Intent(firstActivity, NonSupportActivity.class);
-        firstActivity.finish();
-        firstActivity.startActivity(intent);
-        NonSupportActivity secondActivity = (NonSupportActivity) monitor.waitForActivity();
-        assertThat("Failed to navigate", secondActivity, notNullValue());
-        checkProcessObserverSilent(secondActivity);
-    }
-
-    @Test
-    public void testRecreation() throws Throwable {
-        FragmentActivity activity = setupObserverOnResume();
-        FragmentActivity 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));
-        FragmentActivity dialogActivity = (FragmentActivity) monitor.waitForActivity();
-        checkProcessObserverSilent(dialogActivity);
-
-        List<Event> events = Collections.synchronizedList(new ArrayList<>());
-
-        LifecycleObserver collectingObserver = new LifecycleObserver() {
-            @OnLifecycleEvent(Event.ON_ANY)
-            public void onStateChanged(@SuppressWarnings("unused") 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 FragmentActivity setupObserverOnResume() throws Throwable {
-        FragmentActivity 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(FragmentActivity activity) throws Throwable {
-        waitTillResumed(activity, activityTestRule);
-        assertThat(mObserver.mChangedState, is(false));
-        activityTestRule.runOnUiThread(() ->
-                ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
-    }
-
-    private void checkProcessObserverSilent(NonSupportActivity activity) throws Throwable {
-        assertThat(activity.awaitResumedState(), is(true));
-        assertThat(mObserver.mChangedState, is(false));
-        activityTestRule.runOnUiThread(() ->
-                ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
-    }
-}
diff --git a/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java b/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
deleted file mode 100644
index f010ed8..0000000
--- a/android/arch/lifecycle/ReflectiveGenericLifecycleObserver.java
+++ /dev/null
@@ -1,38 +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.arch.lifecycle;
-
-import android.arch.lifecycle.ClassesInfoCache.CallbackInfo;
-import android.arch.lifecycle.Lifecycle.Event;
-
-/**
- * An internal implementation of {@link GenericLifecycleObserver} that relies on reflection.
- */
-class ReflectiveGenericLifecycleObserver implements GenericLifecycleObserver {
-    private final Object mWrapped;
-    private final CallbackInfo mInfo;
-
-    ReflectiveGenericLifecycleObserver(Object wrapped) {
-        mWrapped = wrapped;
-        mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());
-    }
-
-    @Override
-    public void onStateChanged(LifecycleOwner source, Event event) {
-        mInfo.invokeCallbacks(source, event, mWrapped);
-    }
-}
diff --git a/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java b/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
deleted file mode 100644
index 68f04e8..0000000
--- a/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
+++ /dev/null
@@ -1,365 +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.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/android/arch/lifecycle/ReportFragment.java b/android/arch/lifecycle/ReportFragment.java
deleted file mode 100644
index 16a89ce..0000000
--- a/android/arch/lifecycle/ReportFragment.java
+++ /dev/null
@@ -1,137 +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.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) {
-        Activity activity = getActivity();
-        if (activity instanceof LifecycleRegistryOwner) {
-            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
-            return;
-        }
-
-        if (activity instanceof LifecycleOwner) {
-            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
-            if (lifecycle instanceof LifecycleRegistry) {
-                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
-            }
-        }
-    }
-
-    void setProcessListener(ActivityInitializationListener processListener) {
-        mProcessListener = processListener;
-    }
-
-    interface ActivityInitializationListener {
-        void onCreate();
-
-        void onStart();
-
-        void onResume();
-    }
-}
diff --git a/android/arch/lifecycle/ServiceLifecycleDispatcher.java b/android/arch/lifecycle/ServiceLifecycleDispatcher.java
deleted file mode 100644
index 6067d7b..0000000
--- a/android/arch/lifecycle/ServiceLifecycleDispatcher.java
+++ /dev/null
@@ -1,108 +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.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/android/arch/lifecycle/ServiceLifecycleTest.java b/android/arch/lifecycle/ServiceLifecycleTest.java
deleted file mode 100644
index fe0a306..0000000
--- a/android/arch/lifecycle/ServiceLifecycleTest.java
+++ /dev/null
@@ -1,249 +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.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/android/arch/lifecycle/SimpleAppFullLifecycleTest.java b/android/arch/lifecycle/SimpleAppFullLifecycleTest.java
deleted file mode 100644
index 44d1e8a..0000000
--- a/android/arch/lifecycle/SimpleAppFullLifecycleTest.java
+++ /dev/null
@@ -1,105 +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.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/android/arch/lifecycle/SingleGeneratedAdapterObserver.java b/android/arch/lifecycle/SingleGeneratedAdapterObserver.java
deleted file mode 100644
index d176a3a..0000000
--- a/android/arch/lifecycle/SingleGeneratedAdapterObserver.java
+++ /dev/null
@@ -1,38 +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.arch.lifecycle;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class SingleGeneratedAdapterObserver implements GenericLifecycleObserver {
-
-    private final GeneratedAdapter mGeneratedAdapter;
-
-    SingleGeneratedAdapterObserver(GeneratedAdapter generatedAdapter) {
-        mGeneratedAdapter = generatedAdapter;
-    }
-
-    @Override
-    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
-        mGeneratedAdapter.callMethods(source, event, false, null);
-        mGeneratedAdapter.callMethods(source, event, true, null);
-    }
-}
diff --git a/android/arch/lifecycle/SynchronousActivityLifecycleTest.java b/android/arch/lifecycle/SynchronousActivityLifecycleTest.java
deleted file mode 100644
index 4b91363..0000000
--- a/android/arch/lifecycle/SynchronousActivityLifecycleTest.java
+++ /dev/null
@@ -1,192 +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.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;
-        }
-    }
-
-    private interface ActivityCall {
-        void call(Activity activity);
-    }
-}
diff --git a/android/arch/lifecycle/TestUtils.java b/android/arch/lifecycle/TestUtils.java
deleted file mode 100644
index f7f9bbe..0000000
--- a/android/arch/lifecycle/TestUtils.java
+++ /dev/null
@@ -1,167 +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.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.RESUMED;
-import static android.arch.lifecycle.Lifecycle.State.STARTED;
-import static android.arch.lifecycle.testapp.TestEvent.LIFECYCLE_EVENT;
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.app.Instrumentation.ActivityMonitor;
-import android.arch.lifecycle.testapp.TestEvent;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v4.util.Pair;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-class TestUtils {
-
-    private static final long TIMEOUT_MS = 2000;
-
-    @SuppressWarnings("unchecked")
-    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 waitTillCreated(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
-            throws Throwable {
-        waitTillState(owner, activityRule, CREATED);
-    }
-
-    static void waitTillStarted(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
-            throws Throwable {
-        waitTillState(owner, activityRule, STARTED);
-    }
-
-    static void waitTillResumed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
-            throws Throwable {
-        waitTillState(owner, activityRule, RESUMED);
-    }
-
-    static void waitTillDestroyed(final LifecycleOwner owner, ActivityTestRule<?> activityRule)
-            throws Throwable {
-        waitTillState(owner, activityRule, DESTROYED);
-    }
-
-    static void waitTillState(final LifecycleOwner owner, ActivityTestRule<?> activityRule,
-            Lifecycle.State state)
-            throws Throwable {
-        final CountDownLatch latch = new CountDownLatch(1);
-        activityRule.runOnUiThread(() -> {
-            Lifecycle.State currentState = owner.getLifecycle().getCurrentState();
-            if (currentState == state) {
-                latch.countDown();
-            } else {
-                owner.getLifecycle().addObserver(new LifecycleObserver() {
-                    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
-                    public void onStateChanged(LifecycleOwner provider) {
-                        if (provider.getLifecycle().getCurrentState() == state) {
-                            latch.countDown();
-                            provider.getLifecycle().removeObserver(this);
-                        }
-                    }
-                });
-            }
-        });
-        boolean latchResult = latch.await(1, TimeUnit.MINUTES);
-        assertThat("expected " + state + " never happened. Current state:"
-                        + owner.getLifecycle().getCurrentState(), latchResult, is(true));
-
-        // wait for another loop to ensure all observers are called
-        activityRule.runOnUiThread(() -> {
-            // do nothing
-        });
-    }
-
-    @SafeVarargs
-    static <T> List<T> flatMap(List<T>... items) {
-        ArrayList<T> result = new ArrayList<>();
-        for (List<T> item : items) {
-            result.addAll(item);
-        }
-        return result;
-    }
-
-    /**
-     * Event tuples of {@link TestEvent} and {@link Lifecycle.Event}
-     * in the order they should arrive.
-     */
-    @SuppressWarnings("unchecked")
-    static class OrderedTuples {
-        static final List<Pair<TestEvent, Lifecycle.Event>> CREATE =
-                Arrays.asList(new Pair(OWNER_CALLBACK, ON_CREATE),
-                        new Pair(LIFECYCLE_EVENT, ON_CREATE));
-        static final List<Pair<TestEvent, Lifecycle.Event>> START =
-                Arrays.asList(new Pair(OWNER_CALLBACK, ON_START),
-                        new Pair(LIFECYCLE_EVENT, ON_START));
-        static final List<Pair<TestEvent, Lifecycle.Event>> RESUME =
-                Arrays.asList(new Pair(OWNER_CALLBACK, ON_RESUME),
-                        new Pair(LIFECYCLE_EVENT, ON_RESUME));
-        static final List<Pair<TestEvent, Lifecycle.Event>> PAUSE =
-                Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_PAUSE),
-                        new Pair(OWNER_CALLBACK, ON_PAUSE));
-        static final List<Pair<TestEvent, Lifecycle.Event>> STOP =
-                Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_STOP),
-                        new Pair(OWNER_CALLBACK, ON_STOP));
-        static final List<Pair<TestEvent, Lifecycle.Event>> DESTROY =
-                Arrays.asList(new Pair(LIFECYCLE_EVENT, ON_DESTROY),
-                        new Pair(OWNER_CALLBACK, ON_DESTROY));
-    }
-}
diff --git a/android/arch/lifecycle/ThreadedLiveDataTest.java b/android/arch/lifecycle/ThreadedLiveDataTest.java
deleted file mode 100644
index 3366641..0000000
--- a/android/arch/lifecycle/ThreadedLiveDataTest.java
+++ /dev/null
@@ -1,97 +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.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 org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(JUnit4.class)
-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/android/arch/lifecycle/Transformations.java b/android/arch/lifecycle/Transformations.java
deleted file mode 100644
index c735f8b..0000000
--- a/android/arch/lifecycle/Transformations.java
+++ /dev/null
@@ -1,153 +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.arch.lifecycle;
-
-import android.arch.core.util.Function;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-/**
- * Transformations for a {@link LiveData} class.
- * <p>
- * You can use transformation methods to carry information across the observer's lifecycle. The
- * transformations aren't calculated unless an observer is observing the returned LiveData object.
- * <p>
- * Because the transformations are calculated lazily, lifecycle-related behavior is implicitly
- * passed down without requiring additional explicit calls or dependencies.
- */
-@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.
-     * <p>
-     * Suppose that you have a LiveData, named {@code userLiveData}, that contains user data and you
-     * need to display the user name, created by concatenating the first and the last
-     * name of the user. You can define a function that handles the name creation, that will be
-     * applied to every value emitted by {@code useLiveData}.
-     *
-     * <pre>
-     * LiveData<User> userLiveData = ...;
-     * LiveData<String> userName = Transformations.map(userLiveData, user -> {
-     *      return user.firstName + " " + user.lastName
-     * });
-     * </pre>
-     *
-     * @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(@NonNull LiveData<X> source,
-            @NonNull 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.
-     *
-     * <p>
-     * Consider the case where you have a LiveData containing a user id. Every time there's a new
-     * user id emitted, you want to trigger a request to get the user object corresponding to that
-     * id, from a repository that also returns a LiveData.
-     * <p>
-     * The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code
-     * repository.getUserById} is the "backing" LiveData.
-     * <p>
-     * In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the
-     * userIdLiveData value is set to "1", the {@code switchMap} will call {@code getUser(1)},
-     * that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData
-     * will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"),
-     * the {@code userLiveData} gets automatically notified and will emit User(1, "Sarah").
-     * <p>
-     * When the {@code setUserId} method is called with userId = "2", the value of the {@code
-     * userIdLiveData} changes and automatically triggers a request for getting the user with id
-     * "2" from the repository. So, the {@code userLiveData} emits User(2, "John"). The LiveData
-     * returned by {@code repository.getUserById(1)} is removed as a source.
-     *
-     * <pre>
-     * MutableLiveData<String> userIdLiveData = ...;
-     * LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, id ->
-     *     repository.getUserById(id));
-     *
-     * void setUserId(String userId) {
-     *      this.userIdLiveData.setValue(userId);
-     * }
-     * </pre>
-     *
-     * @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(@NonNull LiveData<X> trigger,
-            @NonNull 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/android/arch/lifecycle/TransformationsTest.java b/android/arch/lifecycle/TransformationsTest.java
deleted file mode 100644
index 02397da..0000000
--- a/android/arch/lifecycle/TransformationsTest.java
+++ /dev/null
@@ -1,216 +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.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.only;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.core.util.Function;
-import android.arch.lifecycle.util.InstantTaskExecutor;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@SuppressWarnings("unchecked")
-@RunWith(JUnit4.class)
-public class TransformationsTest {
-
-    private LifecycleOwner mOwner;
-
-    @Before
-    public void swapExecutorDelegate() {
-        ArchTaskExecutor.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));
-    }
-
-    @Test
-    public void noObsoleteValueTest() {
-        MutableLiveData<Integer> numbers = new MutableLiveData<>();
-        LiveData<Integer> squared = Transformations.map(numbers, new Function<Integer, Integer>() {
-            @Override
-            public Integer apply(Integer input) {
-                return input * input;
-            }
-        });
-
-        Observer observer = mock(Observer.class);
-        squared.setValue(1);
-        squared.observeForever(observer);
-        verify(observer).onChanged(1);
-        squared.removeObserver(observer);
-        reset(observer);
-        numbers.setValue(2);
-        squared.observeForever(observer);
-        verify(observer, only()).onChanged(4);
-    }
-}
diff --git a/android/arch/lifecycle/ViewModel.java b/android/arch/lifecycle/ViewModel.java
deleted file mode 100644
index 0310c46..0000000
--- a/android/arch/lifecycle/ViewModel.java
+++ /dev/null
@@ -1,106 +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.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/android/arch/lifecycle/ViewModelProvider.java b/android/arch/lifecycle/ViewModelProvider.java
deleted file mode 100644
index e01aa19..0000000
--- a/android/arch/lifecycle/ViewModelProvider.java
+++ /dev/null
@@ -1,211 +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.arch.lifecycle;
-
-import android.app.Application;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * 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
-         */
-        @NonNull
-        <T extends ViewModel> T create(@NonNull 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(@NonNull ViewModelStore store, @NonNull 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}.
-     */
-    @NonNull
-    public <T extends ViewModel> T get(@NonNull 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 {
-
-        @SuppressWarnings("ClassNewInstance")
-        @NonNull
-        @Override
-        public <T extends ViewModel> T create(@NonNull 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);
-            }
-        }
-    }
-
-    /**
-     * {@link Factory} which may create {@link AndroidViewModel} and
-     * {@link ViewModel}, which have an empty constructor.
-     */
-    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
-
-        private static AndroidViewModelFactory sInstance;
-
-        /**
-         * Retrieve a singleton instance of AndroidViewModelFactory.
-         *
-         * @param application an application to pass in {@link AndroidViewModel}
-         * @return A valid {@link AndroidViewModelFactory}
-         */
-        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
-            if (sInstance == null) {
-                sInstance = new AndroidViewModelFactory(application);
-            }
-            return sInstance;
-        }
-
-        private Application mApplication;
-
-        /**
-         * Creates a {@code AndroidViewModelFactory}
-         *
-         * @param application an application to pass in {@link AndroidViewModel}
-         */
-        public AndroidViewModelFactory(@NonNull Application application) {
-            mApplication = application;
-        }
-
-        @NonNull
-        @Override
-        public <T extends ViewModel> T create(@NonNull 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/android/arch/lifecycle/ViewModelProviderTest.java b/android/arch/lifecycle/ViewModelProviderTest.java
deleted file mode 100644
index 142f19a..0000000
--- a/android/arch/lifecycle/ViewModelProviderTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.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 android.support.annotation.NonNull;
-
-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() {
-            @NonNull
-            @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/android/arch/lifecycle/ViewModelProviders.java b/android/arch/lifecycle/ViewModelProviders.java
deleted file mode 100644
index d9894a8..0000000
--- a/android/arch/lifecycle/ViewModelProviders.java
+++ /dev/null
@@ -1,148 +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.arch.lifecycle;
-
-import android.app.Activity;
-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;
-
-/**
- * Utilities methods for {@link ViewModelStore} class.
- */
-public class ViewModelProviders {
-
-    /**
-     * @deprecated This class should not be directly instantiated
-     */
-    @Deprecated
-    public ViewModelProviders() {
-    }
-
-    private static Application checkApplication(Activity activity) {
-        Application application = activity.getApplication();
-        if (application == null) {
-            throw new IllegalStateException("Your activity/fragment is not yet attached to "
-                    + "Application. You can't request ViewModel before onCreate call.");
-        }
-        return application;
-    }
-
-    private static Activity checkActivity(Fragment fragment) {
-        Activity activity = fragment.getActivity();
-        if (activity == null) {
-            throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
-        }
-        return activity;
-    }
-
-    /**
-     * 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 ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
-     *
-     * @param fragment a fragment, in whose scope ViewModels should be retained
-     * @return a ViewModelProvider instance
-     */
-    @NonNull
-    @MainThread
-    public static ViewModelProvider of(@NonNull Fragment fragment) {
-        ViewModelProvider.AndroidViewModelFactory factory =
-                ViewModelProvider.AndroidViewModelFactory.getInstance(
-                        checkApplication(checkActivity(fragment)));
-        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 {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
-     *
-     * @param activity an activity, in whose scope ViewModels should be retained
-     * @return a ViewModelProvider instance
-     */
-    @NonNull
-    @MainThread
-    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
-        ViewModelProvider.AndroidViewModelFactory factory =
-                ViewModelProvider.AndroidViewModelFactory.getInstance(
-                        checkApplication(activity));
-        return new ViewModelProvider(ViewModelStores.of(activity), factory);
-    }
-
-    /**
-     * 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
-     */
-    @NonNull
-    @MainThread
-    public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
-        checkApplication(checkActivity(fragment));
-        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
-     */
-    @NonNull
-    @MainThread
-    public static ViewModelProvider of(@NonNull FragmentActivity activity,
-            @NonNull Factory factory) {
-        checkApplication(activity);
-        return new ViewModelProvider(ViewModelStores.of(activity), factory);
-    }
-
-    /**
-     * {@link Factory} which may create {@link AndroidViewModel} and
-     * {@link ViewModel}, which have an empty constructor.
-     *
-     * @deprecated Use {@link ViewModelProvider.AndroidViewModelFactory}
-     */
-    @SuppressWarnings("WeakerAccess")
-    @Deprecated
-    public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
-        /**
-         * Creates a {@code AndroidViewModelFactory}
-         *
-         * @param application an application to pass in {@link AndroidViewModel}
-         * @deprecated Use {@link ViewModelProvider.AndroidViewModelFactory} or
-         * {@link ViewModelProvider.AndroidViewModelFactory#getInstance(Application)}.
-         */
-        @Deprecated
-        public DefaultFactory(@NonNull Application application) {
-            super(application);
-        }
-    }
-}
diff --git a/android/arch/lifecycle/ViewModelProvidersTest.java b/android/arch/lifecycle/ViewModelProvidersTest.java
deleted file mode 100644
index f37c9a2..0000000
--- a/android/arch/lifecycle/ViewModelProvidersTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.lifecycle;
-
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ViewModelProvidersTest {
-
-    @Test(expected = IllegalStateException.class)
-    public void testNotAttachedActivity() {
-        // This is similar to call ViewModelProviders.of in Activity's constructor
-        ViewModelProviders.of(new FragmentActivity());
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testNotAttachedFragment() {
-        // This is similar to call ViewModelProviders.of in Activity's constructor
-        ViewModelProviders.of(new Fragment());
-    }
-}
diff --git a/android/arch/lifecycle/ViewModelStore.java b/android/arch/lifecycle/ViewModelStore.java
deleted file mode 100644
index a1864bb..0000000
--- a/android/arch/lifecycle/ViewModelStore.java
+++ /dev/null
@@ -1,61 +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.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/android/arch/lifecycle/ViewModelStoreOwner.java b/android/arch/lifecycle/ViewModelStoreOwner.java
deleted file mode 100644
index e26fa32..0000000
--- a/android/arch/lifecycle/ViewModelStoreOwner.java
+++ /dev/null
@@ -1,37 +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.arch.lifecycle;
-
-import android.support.annotation.NonNull;
-
-/**
- * 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}
-     */
-    @NonNull
-    ViewModelStore getViewModelStore();
-}
diff --git a/android/arch/lifecycle/ViewModelStoreTest.java b/android/arch/lifecycle/ViewModelStoreTest.java
deleted file mode 100644
index cfeefb7..0000000
--- a/android/arch/lifecycle/ViewModelStoreTest.java
+++ /dev/null
@@ -1,54 +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.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/android/arch/lifecycle/ViewModelStores.java b/android/arch/lifecycle/ViewModelStores.java
deleted file mode 100644
index 348a06e..0000000
--- a/android/arch/lifecycle/ViewModelStores.java
+++ /dev/null
@@ -1,64 +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.arch.lifecycle;
-
-import static android.arch.lifecycle.HolderFragment.holderFragmentFor;
-
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-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}
-     */
-    @NonNull
-    @MainThread
-    public static ViewModelStore of(@NonNull FragmentActivity activity) {
-        if (activity instanceof ViewModelStoreOwner) {
-            return ((ViewModelStoreOwner) activity).getViewModelStore();
-        }
-        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}
-     */
-    @NonNull
-    @MainThread
-    public static ViewModelStore of(@NonNull Fragment fragment) {
-        if (fragment instanceof ViewModelStoreOwner) {
-            return ((ViewModelStoreOwner) fragment).getViewModelStore();
-        }
-        return holderFragmentFor(fragment).getViewModelStore();
-    }
-}
diff --git a/android/arch/lifecycle/ViewModelTest.java b/android/arch/lifecycle/ViewModelTest.java
deleted file mode 100644
index 03ebdf3..0000000
--- a/android/arch/lifecycle/ViewModelTest.java
+++ /dev/null
@@ -1,192 +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.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.Fragment;
-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();
-                    Fragment fragment = new Fragment();
-                    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/android/arch/lifecycle/ViewModelTestInTransaction.java b/android/arch/lifecycle/ViewModelTestInTransaction.java
deleted file mode 100644
index 3832b3b..0000000
--- a/android/arch/lifecycle/ViewModelTestInTransaction.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.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/android/arch/lifecycle/activity/EmptyActivity.java b/android/arch/lifecycle/activity/EmptyActivity.java
deleted file mode 100644
index c32c898..0000000
--- a/android/arch/lifecycle/activity/EmptyActivity.java
+++ /dev/null
@@ -1,30 +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.arch.lifecycle.activity;
-
-import android.arch.lifecycle.extensions.test.R;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.FragmentActivity;
-
-public class EmptyActivity extends FragmentActivity {
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-    }
-}
diff --git a/android/arch/lifecycle/activity/FragmentLifecycleActivity.java b/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
deleted file mode 100644
index 2eb1cc2..0000000
--- a/android/arch/lifecycle/activity/FragmentLifecycleActivity.java
+++ /dev/null
@@ -1,120 +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.arch.lifecycle.activity;
-
-import android.arch.lifecycle.Lifecycle;
-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.v4.app.Fragment;
-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 Fragment {
-        @Nullable
-        Fragment 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 Fragment {
-    }
-
-    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(@SuppressWarnings("unused") 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/android/arch/lifecycle/observers/Base.java b/android/arch/lifecycle/observers/Base.java
deleted file mode 100644
index 08919d4..0000000
--- a/android/arch/lifecycle/observers/Base.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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleObserver;
-import android.arch.lifecycle.OnLifecycleEvent;
-
-public class Base implements LifecycleObserver {
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-    public void onCreate() {
-    }
-}
diff --git a/android/arch/lifecycle/observers/Base_LifecycleAdapter.java b/android/arch/lifecycle/observers/Base_LifecycleAdapter.java
deleted file mode 100644
index 4218b96..0000000
--- a/android/arch/lifecycle/observers/Base_LifecycleAdapter.java
+++ /dev/null
@@ -1,34 +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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.GeneratedAdapter;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.arch.lifecycle.MethodCallsLogger;
-
-public class Base_LifecycleAdapter implements GeneratedAdapter {
-
-    public Base_LifecycleAdapter(Base base) {
-    }
-
-    @Override
-    public void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
-            MethodCallsLogger logger) {
-
-    }
-}
diff --git a/android/arch/lifecycle/observers/DerivedSequence1.java b/android/arch/lifecycle/observers/DerivedSequence1.java
deleted file mode 100644
index 9db37f1..0000000
--- a/android/arch/lifecycle/observers/DerivedSequence1.java
+++ /dev/null
@@ -1,23 +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.arch.lifecycle.observers;
-
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
-}
diff --git a/android/arch/lifecycle/observers/DerivedSequence2.java b/android/arch/lifecycle/observers/DerivedSequence2.java
deleted file mode 100644
index f2ef943..0000000
--- a/android/arch/lifecycle/observers/DerivedSequence2.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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.OnLifecycleEvent;
-
-public class DerivedSequence2 extends DerivedSequence1 {
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
-    void onStop() {
-
-    }
-}
diff --git a/android/arch/lifecycle/observers/DerivedWithNewMethods.java b/android/arch/lifecycle/observers/DerivedWithNewMethods.java
deleted file mode 100644
index b1eaef0..0000000
--- a/android/arch/lifecycle/observers/DerivedWithNewMethods.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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.OnLifecycleEvent;
-
-public class DerivedWithNewMethods extends Base {
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
-    void onStop() {
-
-    }
-}
diff --git a/android/arch/lifecycle/observers/DerivedWithNoNewMethods.java b/android/arch/lifecycle/observers/DerivedWithNoNewMethods.java
deleted file mode 100644
index cb1afb8..0000000
--- a/android/arch/lifecycle/observers/DerivedWithNoNewMethods.java
+++ /dev/null
@@ -1,20 +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.arch.lifecycle.observers;
-
-public class DerivedWithNoNewMethods extends Base {
-}
diff --git a/android/arch/lifecycle/observers/DerivedWithOverridenMethodsWithLfAnnotation.java b/android/arch/lifecycle/observers/DerivedWithOverridenMethodsWithLfAnnotation.java
deleted file mode 100644
index 40c7c9a..0000000
--- a/android/arch/lifecycle/observers/DerivedWithOverridenMethodsWithLfAnnotation.java
+++ /dev/null
@@ -1,29 +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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.OnLifecycleEvent;
-
-public class DerivedWithOverridenMethodsWithLfAnnotation extends Base {
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-    @Override
-    public void onCreate() {
-        super.onCreate();
-    }
-}
diff --git a/android/arch/lifecycle/observers/Interface1.java b/android/arch/lifecycle/observers/Interface1.java
deleted file mode 100644
index e193de9..0000000
--- a/android/arch/lifecycle/observers/Interface1.java
+++ /dev/null
@@ -1,27 +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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleObserver;
-import android.arch.lifecycle.OnLifecycleEvent;
-
-public interface Interface1 extends LifecycleObserver {
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-    void onCreate();
-}
diff --git a/android/arch/lifecycle/observers/Interface1_LifecycleAdapter.java b/android/arch/lifecycle/observers/Interface1_LifecycleAdapter.java
deleted file mode 100644
index c597b1c..0000000
--- a/android/arch/lifecycle/observers/Interface1_LifecycleAdapter.java
+++ /dev/null
@@ -1,34 +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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.GeneratedAdapter;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.arch.lifecycle.MethodCallsLogger;
-
-public class Interface1_LifecycleAdapter implements GeneratedAdapter {
-
-    public Interface1_LifecycleAdapter(Interface1 base) {
-    }
-
-    @Override
-    public void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
-            MethodCallsLogger logger) {
-
-    }
-}
diff --git a/android/arch/lifecycle/observers/Interface2.java b/android/arch/lifecycle/observers/Interface2.java
deleted file mode 100644
index 1056fcb..0000000
--- a/android/arch/lifecycle/observers/Interface2.java
+++ /dev/null
@@ -1,30 +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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleObserver;
-import android.arch.lifecycle.OnLifecycleEvent;
-
-public interface Interface2 extends LifecycleObserver {
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-    void onCreate();
-
-    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
-    void onDestroy();
-}
diff --git a/android/arch/lifecycle/observers/Interface2_LifecycleAdapter.java b/android/arch/lifecycle/observers/Interface2_LifecycleAdapter.java
deleted file mode 100644
index b05b41a..0000000
--- a/android/arch/lifecycle/observers/Interface2_LifecycleAdapter.java
+++ /dev/null
@@ -1,34 +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.arch.lifecycle.observers;
-
-import android.arch.lifecycle.GeneratedAdapter;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.arch.lifecycle.MethodCallsLogger;
-
-public class Interface2_LifecycleAdapter implements GeneratedAdapter {
-
-    public Interface2_LifecycleAdapter(Interface2 base) {
-    }
-
-    @Override
-    public void callMethods(LifecycleOwner source, Lifecycle.Event event, boolean onAny,
-            MethodCallsLogger logger) {
-
-    }
-}
diff --git a/android/arch/lifecycle/observers/InterfaceImpl1.java b/android/arch/lifecycle/observers/InterfaceImpl1.java
deleted file mode 100644
index 2f03393..0000000
--- a/android/arch/lifecycle/observers/InterfaceImpl1.java
+++ /dev/null
@@ -1,23 +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.arch.lifecycle.observers;
-
-public class InterfaceImpl1 implements Interface1 {
-    @Override
-    public void onCreate() {
-    }
-}
diff --git a/android/arch/lifecycle/observers/InterfaceImpl2.java b/android/arch/lifecycle/observers/InterfaceImpl2.java
deleted file mode 100644
index eef8ce4..0000000
--- a/android/arch/lifecycle/observers/InterfaceImpl2.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.arch.lifecycle.observers;
-
-public class InterfaceImpl2 implements Interface1, Interface2 {
-    @Override
-    public void onCreate() {
-    }
-
-    @Override
-    public void onDestroy() {
-
-    }
-}
diff --git a/android/arch/lifecycle/observers/InterfaceImpl3.java b/android/arch/lifecycle/observers/InterfaceImpl3.java
deleted file mode 100644
index 8f31808..0000000
--- a/android/arch/lifecycle/observers/InterfaceImpl3.java
+++ /dev/null
@@ -1,23 +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.arch.lifecycle.observers;
-
-public class InterfaceImpl3 extends Base implements Interface1 {
-    @Override
-    public void onCreate() {
-    }
-}
diff --git a/android/arch/lifecycle/service/TestService.java b/android/arch/lifecycle/service/TestService.java
deleted file mode 100644
index fcf06cc..0000000
--- a/android/arch/lifecycle/service/TestService.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.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/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java b/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java
deleted file mode 100644
index 4213cab..0000000
--- a/android/arch/lifecycle/testapp/CollectingLifecycleOwner.java
+++ /dev/null
@@ -1,36 +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.arch.lifecycle.testapp;
-
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.support.v4.util.Pair;
-
-import java.util.List;
-
-/**
- * For activities that collect their events.
- */
-public interface CollectingLifecycleOwner extends LifecycleOwner {
-    /**
-     * Return a copy of currently collected events
-     *
-     * @return The list of collected events.
-     * @throws InterruptedException
-     */
-    List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents();
-}
diff --git a/android/arch/lifecycle/testapp/CollectingSupportActivity.java b/android/arch/lifecycle/testapp/CollectingSupportActivity.java
deleted file mode 100644
index f38d422..0000000
--- a/android/arch/lifecycle/testapp/CollectingSupportActivity.java
+++ /dev/null
@@ -1,113 +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.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import android.arch.lifecycle.Lifecycle.Event;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.util.Pair;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * LifecycleRegistryOwner that extends FragmentActivity.
- */
-public class CollectingSupportActivity extends FragmentActivity implements
-        CollectingLifecycleOwner {
-
-    private final List<Pair<TestEvent, Event>> mCollectedEvents = new ArrayList<>();
-    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
-    private CountDownLatch mSavedStateLatch = new CountDownLatch(1);
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        FrameLayout layout = new FrameLayout(this);
-        layout.setId(R.id.fragment_container);
-        setContentView(layout);
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_CREATE));
-        getLifecycle().addObserver(mTestObserver);
-    }
-
-    /**
-     * replaces the main content fragment w/ the given fragment.
-     */
-    public void replaceFragment(Fragment fragment) {
-        getSupportFragmentManager()
-                .beginTransaction()
-                .add(R.id.fragment_container, fragment)
-                .commitNow();
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_START));
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_RESUME));
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_DESTROY));
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_STOP));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Event.ON_PAUSE));
-        // helps with less flaky API 16 tests.
-        overridePendingTransition(0, 0);
-    }
-
-    @Override
-    public List<Pair<TestEvent, Event>> copyCollectedEvents() {
-        return new ArrayList<>(mCollectedEvents);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        mSavedStateLatch.countDown();
-    }
-
-    /**
-     * Waits for onSaveInstanceState to be called.
-     */
-    public boolean waitForStateSave(@SuppressWarnings("SameParameterValue") int seconds)
-            throws InterruptedException {
-        return mSavedStateLatch.await(seconds, TimeUnit.SECONDS);
-    }
-}
diff --git a/android/arch/lifecycle/testapp/CollectingSupportFragment.java b/android/arch/lifecycle/testapp/CollectingSupportFragment.java
deleted file mode 100644
index 9bbbe16..0000000
--- a/android/arch/lifecycle/testapp/CollectingSupportFragment.java
+++ /dev/null
@@ -1,104 +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.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_CALLBACK;
-
-import android.annotation.SuppressLint;
-import android.arch.lifecycle.Lifecycle;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.util.Pair;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A support fragment that collects all of its events.
- */
-@SuppressLint("ValidFragment")
-public class CollectingSupportFragment extends Fragment implements CollectingLifecycleOwner {
-    private final List<Pair<TestEvent, Lifecycle.Event>> mCollectedEvents =
-            new ArrayList<>();
-    private TestObserver mTestObserver = new TestObserver(mCollectedEvents);
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_CREATE));
-        getLifecycle().addObserver(mTestObserver);
-    }
-
-    @Nullable
-    @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
-        //noinspection ConstantConditions
-        FrameLayout layout = new FrameLayout(container.getContext());
-        layout.setId(R.id.child_fragment_container);
-        return layout;
-    }
-
-    /**
-     * Runs a replace fragment transaction with 'fragment' on this Fragment.
-     */
-    public void replaceFragment(Fragment fragment) {
-        getChildFragmentManager()
-                .beginTransaction()
-                .add(R.id.child_fragment_container, fragment)
-                .commitNow();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_START));
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_RESUME));
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_DESTROY));
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_STOP));
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_PAUSE));
-    }
-
-    @Override
-    public List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents() {
-        return new ArrayList<>(mCollectedEvents);
-    }
-}
diff --git a/android/arch/lifecycle/testapp/EmptyActivity.java b/android/arch/lifecycle/testapp/EmptyActivity.java
deleted file mode 100644
index 68a82e1..0000000
--- a/android/arch/lifecycle/testapp/EmptyActivity.java
+++ /dev/null
@@ -1,33 +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.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/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java b/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
deleted file mode 100644
index cdf577c..0000000
--- a/android/arch/lifecycle/testapp/FrameworkLifecycleRegistryActivity.java
+++ /dev/null
@@ -1,93 +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.arch.lifecycle.testapp;
-
-import static android.arch.lifecycle.testapp.TestEvent.OWNER_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.support.annotation.NonNull;
-import android.support.v4.util.Pair;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * LifecycleRegistryOwner that extends framework activity.
- */
-@SuppressWarnings("deprecation")
-public class FrameworkLifecycleRegistryActivity extends Activity implements
-        LifecycleRegistryOwner, CollectingLifecycleOwner {
-    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
-
-    @NonNull
-    @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<>(OWNER_CALLBACK, Lifecycle.Event.ON_CREATE));
-        getLifecycle().addObserver(mTestObserver);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_START));
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_RESUME));
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_DESTROY));
-        mLatch.countDown();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_STOP));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mCollectedEvents.add(new Pair<>(OWNER_CALLBACK, Lifecycle.Event.ON_PAUSE));
-    }
-
-    @Override
-    public List<Pair<TestEvent, Lifecycle.Event>> copyCollectedEvents() {
-        return new ArrayList<>(mCollectedEvents);
-    }
-}
diff --git a/android/arch/lifecycle/testapp/LifecycleTestActivity.java b/android/arch/lifecycle/testapp/LifecycleTestActivity.java
deleted file mode 100644
index cf07aee..0000000
--- a/android/arch/lifecycle/testapp/LifecycleTestActivity.java
+++ /dev/null
@@ -1,73 +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.arch.lifecycle.testapp;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-
-/**
- * Activity for testing events by themselves
- */
-public class LifecycleTestActivity extends FragmentActivity {
-
-    /**
-     * 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/android/arch/lifecycle/testapp/NavigationDialogActivity.java b/android/arch/lifecycle/testapp/NavigationDialogActivity.java
deleted file mode 100644
index 7d53528..0000000
--- a/android/arch/lifecycle/testapp/NavigationDialogActivity.java
+++ /dev/null
@@ -1,31 +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.arch.lifecycle.testapp;
-
-import android.support.v4.app.FragmentActivity;
-
-/**
- *  an activity with Dialog theme.
- */
-public class NavigationDialogActivity extends FragmentActivity {
-    @Override
-    protected void onPause() {
-        super.onPause();
-        // helps with less flaky API 16 tests
-        overridePendingTransition(0, 0);
-    }
-}
diff --git a/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java b/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
deleted file mode 100644
index 69fd478..0000000
--- a/android/arch/lifecycle/testapp/NavigationTestActivityFirst.java
+++ /dev/null
@@ -1,25 +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.arch.lifecycle.testapp;
-
-import android.support.v4.app.FragmentActivity;
-
-/**
- * Activity for ProcessOwnerTest
- */
-public class NavigationTestActivityFirst extends FragmentActivity {
-}
diff --git a/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java b/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
deleted file mode 100644
index 0f9a4c9..0000000
--- a/android/arch/lifecycle/testapp/NavigationTestActivitySecond.java
+++ /dev/null
@@ -1,25 +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.arch.lifecycle.testapp;
-
-import android.support.v4.app.FragmentActivity;
-
-/**
- * Activity for ProcessOwnerTest
- */
-public class NavigationTestActivitySecond extends FragmentActivity {
-}
diff --git a/android/arch/lifecycle/testapp/NonSupportActivity.java b/android/arch/lifecycle/testapp/NonSupportActivity.java
deleted file mode 100644
index 835d846..0000000
--- a/android/arch/lifecycle/testapp/NonSupportActivity.java
+++ /dev/null
@@ -1,85 +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.arch.lifecycle.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Activity which doesn't extend FragmentActivity, to test ProcessLifecycleOwner because it
- * should work anyway.
- */
-public class NonSupportActivity extends Activity {
-
-    private static final int TIMEOUT = 1; //secs
-    private final Lock mLock = new ReentrantLock();
-    private Condition mIsResumedCondition = mLock.newCondition();
-    private boolean mIsResumed = false;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mLock.lock();
-        try {
-            mIsResumed = true;
-            mIsResumedCondition.signalAll();
-        } finally {
-            mLock.unlock();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mLock.lock();
-        try {
-            mIsResumed = false;
-        } finally {
-            mLock.unlock();
-        }
-    }
-
-    /**
-     *  awaits resumed state
-     * @return
-     * @throws InterruptedException
-     */
-    public boolean awaitResumedState() throws InterruptedException {
-        mLock.lock();
-        try {
-            while (!mIsResumed) {
-                if (!mIsResumedCondition.await(TIMEOUT, TimeUnit.SECONDS)) {
-                    return false;
-                }
-            }
-            return true;
-        } finally {
-            mLock.unlock();
-        }
-    }
-}
diff --git a/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java b/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
deleted file mode 100644
index 77bd99f..0000000
--- a/android/arch/lifecycle/testapp/SimpleAppLifecycleTestActivity.java
+++ /dev/null
@@ -1,100 +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.arch.lifecycle.testapp;
-
-import android.arch.lifecycle.Lifecycle;
-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.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;
-
-/**
- * Activity for SimpleAppFullLifecycleTest
- */
-public class SimpleAppLifecycleTestActivity extends FragmentActivity {
-
-    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/android/arch/lifecycle/testapp/TestEvent.java b/android/arch/lifecycle/testapp/TestEvent.java
deleted file mode 100644
index 788045a..0000000
--- a/android/arch/lifecycle/testapp/TestEvent.java
+++ /dev/null
@@ -1,22 +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.arch.lifecycle.testapp;
-
-public enum TestEvent {
-    OWNER_CALLBACK,
-    LIFECYCLE_EVENT,
-}
diff --git a/android/arch/lifecycle/testapp/TestObserver.java b/android/arch/lifecycle/testapp/TestObserver.java
deleted file mode 100644
index 00b8e16..0000000
--- a/android/arch/lifecycle/testapp/TestObserver.java
+++ /dev/null
@@ -1,70 +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.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.support.v4.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/android/arch/lifecycle/testapp/UsualFragment.java b/android/arch/lifecycle/testapp/UsualFragment.java
deleted file mode 100644
index fb6cae0..0000000
--- a/android/arch/lifecycle/testapp/UsualFragment.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.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/android/arch/lifecycle/util/InstantTaskExecutor.java b/android/arch/lifecycle/util/InstantTaskExecutor.java
deleted file mode 100644
index 52318fd..0000000
--- a/android/arch/lifecycle/util/InstantTaskExecutor.java
+++ /dev/null
@@ -1,36 +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.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/android/arch/lifecycle/viewmodeltest/TestViewModel.java b/android/arch/lifecycle/viewmodeltest/TestViewModel.java
deleted file mode 100644
index ec3550c..0000000
--- a/android/arch/lifecycle/viewmodeltest/TestViewModel.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.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/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java b/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
deleted file mode 100644
index 1f9f100..0000000
--- a/android/arch/lifecycle/viewmodeltest/ViewModelActivity.java
+++ /dev/null
@@ -1,64 +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.arch.lifecycle.viewmodeltest;
-
-import android.arch.lifecycle.ViewModelProviders;
-import android.arch.lifecycle.extensions.test.R;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-
-public class ViewModelActivity extends FragmentActivity {
-    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 Fragment {
-        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/android/arch/paging/ContiguousDataSource.java b/android/arch/paging/ContiguousDataSource.java
deleted file mode 100644
index b2e389f..0000000
--- a/android/arch/paging/ContiguousDataSource.java
+++ /dev/null
@@ -1,59 +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.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.concurrent.Executor;
-
-abstract class ContiguousDataSource<Key, Value> extends DataSource<Key, Value> {
-    @Override
-    boolean isContiguous() {
-        return true;
-    }
-
-    abstract void dispatchLoadInitial(
-            @Nullable Key key,
-            int initialLoadSize,
-            int pageSize,
-            boolean enablePlaceholders,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver);
-
-    abstract void dispatchLoadAfter(
-            int currentEndIndex,
-            @NonNull Value currentEndItem,
-            int pageSize,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver);
-
-    abstract void dispatchLoadBefore(
-            int currentBeginIndex,
-            @NonNull Value currentBeginItem,
-            int pageSize,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver);
-
-    /**
-     * Get the key from either the position, or item, or null if position/item invalid.
-     * <p>
-     * Position may not match passed item's position - if trying to query the key from a position
-     * that isn't yet loaded, a fallback item (last loaded item accessed) will be passed.
-     */
-    abstract Key getKey(int position, Value item);
-}
diff --git a/android/arch/paging/ContiguousPagedList.java b/android/arch/paging/ContiguousPagedList.java
deleted file mode 100644
index c622f65..0000000
--- a/android/arch/paging/ContiguousPagedList.java
+++ /dev/null
@@ -1,289 +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.arch.paging;
-
-import android.support.annotation.AnyThread;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-class ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback {
-    private final ContiguousDataSource<K, V> mDataSource;
-    private boolean mPrependWorkerRunning = false;
-    private boolean mAppendWorkerRunning = false;
-
-    private int mPrependItemsRequested = 0;
-    private int mAppendItemsRequested = 0;
-
-    private PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() {
-        // Creation thread for initial synchronous load, otherwise main thread
-        // Safe to access main thread only state - no other thread has reference during construction
-        @AnyThread
-        @Override
-        public void onPageResult(@PageResult.ResultType int resultType,
-                @NonNull PageResult<V> pageResult) {
-            if (pageResult.isInvalid()) {
-                detach();
-                return;
-            }
-
-            if (isDetached()) {
-                // No op, have detached
-                return;
-            }
-
-            List<V> page = pageResult.page;
-            if (resultType == PageResult.INIT) {
-                mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls,
-                        pageResult.positionOffset, ContiguousPagedList.this);
-                mLastLoad = pageResult.leadingNulls + pageResult.positionOffset + page.size() / 2;
-            } else if (resultType == PageResult.APPEND) {
-                mStorage.appendPage(page, ContiguousPagedList.this);
-            } else if (resultType == PageResult.PREPEND) {
-                mStorage.prependPage(page, ContiguousPagedList.this);
-            } else {
-                throw new IllegalArgumentException("unexpected resultType " + resultType);
-            }
-
-
-            if (mBoundaryCallback != null) {
-                boolean deferEmpty = mStorage.size() == 0;
-                boolean deferBegin = !deferEmpty
-                        && resultType == PageResult.PREPEND
-                        && pageResult.page.size() == 0;
-                boolean deferEnd = !deferEmpty
-                        && resultType == PageResult.APPEND
-                        && pageResult.page.size() == 0;
-                deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd);
-            }
-        }
-    };
-
-    ContiguousPagedList(
-            @NonNull ContiguousDataSource<K, V> dataSource,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @Nullable BoundaryCallback<V> boundaryCallback,
-            @NonNull Config config,
-            final @Nullable K key) {
-        super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor,
-                boundaryCallback, config);
-        mDataSource = dataSource;
-
-        if (mDataSource.isInvalid()) {
-            detach();
-        } else {
-            mDataSource.dispatchLoadInitial(key,
-                    mConfig.initialLoadSizeHint,
-                    mConfig.pageSize,
-                    mConfig.enablePlaceholders,
-                    mMainThreadExecutor,
-                    mReceiver);
-        }
-    }
-
-    @MainThread
-    @Override
-    void dispatchUpdatesSinceSnapshot(
-            @NonNull PagedList<V> pagedListSnapshot, @NonNull Callback callback) {
-        final PagedStorage<V> snapshot = pagedListSnapshot.mStorage;
-
-        final int newlyAppended = mStorage.getNumberAppended() - snapshot.getNumberAppended();
-        final int newlyPrepended = mStorage.getNumberPrepended() - snapshot.getNumberPrepended();
-
-        final int previousTrailing = snapshot.getTrailingNullCount();
-        final int previousLeading = snapshot.getLeadingNullCount();
-
-        // Validate that the snapshot looks like a previous version of this list - if it's not,
-        // we can't be sure we'll dispatch callbacks safely
-        if (snapshot.isEmpty()
-                || newlyAppended < 0
-                || newlyPrepended < 0
-                || mStorage.getTrailingNullCount() != Math.max(previousTrailing - newlyAppended, 0)
-                || mStorage.getLeadingNullCount() != Math.max(previousLeading - newlyPrepended, 0)
-                || (mStorage.getStorageCount()
-                        != snapshot.getStorageCount() + newlyAppended + newlyPrepended)) {
-            throw new IllegalArgumentException("Invalid snapshot provided - doesn't appear"
-                    + " to be a snapshot of this PagedList");
-        }
-
-        if (newlyAppended != 0) {
-            final int changedCount = Math.min(previousTrailing, newlyAppended);
-            final int addedCount = newlyAppended - changedCount;
-
-            final int endPosition = snapshot.getLeadingNullCount() + snapshot.getStorageCount();
-            if (changedCount != 0) {
-                callback.onChanged(endPosition, changedCount);
-            }
-            if (addedCount != 0) {
-                callback.onInserted(endPosition + changedCount, addedCount);
-            }
-        }
-        if (newlyPrepended != 0) {
-            final int changedCount = Math.min(previousLeading, newlyPrepended);
-            final int addedCount = newlyPrepended - changedCount;
-
-            if (changedCount != 0) {
-                callback.onChanged(previousLeading, changedCount);
-            }
-            if (addedCount != 0) {
-                callback.onInserted(0, addedCount);
-            }
-        }
-    }
-
-    @MainThread
-    @Override
-    protected void loadAroundInternal(int index) {
-        int prependItems = mConfig.prefetchDistance - (index - mStorage.getLeadingNullCount());
-        int appendItems = index + mConfig.prefetchDistance
-                - (mStorage.getLeadingNullCount() + mStorage.getStorageCount());
-
-        mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested);
-        if (mPrependItemsRequested > 0) {
-            schedulePrepend();
-        }
-
-        mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested);
-        if (mAppendItemsRequested > 0) {
-            scheduleAppend();
-        }
-    }
-
-    @MainThread
-    private void schedulePrepend() {
-        if (mPrependWorkerRunning) {
-            return;
-        }
-        mPrependWorkerRunning = true;
-
-        final int position = mStorage.getLeadingNullCount() + mStorage.getPositionOffset();
-
-        // safe to access first item here - mStorage can't be empty if we're prepending
-        final V item = mStorage.getFirstLoadedItem();
-        mBackgroundThreadExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                if (isDetached()) {
-                    return;
-                }
-                if (mDataSource.isInvalid()) {
-                    detach();
-                } else {
-                    mDataSource.dispatchLoadBefore(position, item, mConfig.pageSize,
-                            mMainThreadExecutor, mReceiver);
-                }
-
-            }
-        });
-    }
-
-    @MainThread
-    private void scheduleAppend() {
-        if (mAppendWorkerRunning) {
-            return;
-        }
-        mAppendWorkerRunning = true;
-
-        final int position = mStorage.getLeadingNullCount()
-                + mStorage.getStorageCount() - 1 + mStorage.getPositionOffset();
-
-        // safe to access first item here - mStorage can't be empty if we're appending
-        final V item = mStorage.getLastLoadedItem();
-        mBackgroundThreadExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                if (isDetached()) {
-                    return;
-                }
-                if (mDataSource.isInvalid()) {
-                    detach();
-                } else {
-                    mDataSource.dispatchLoadAfter(position, item, mConfig.pageSize,
-                            mMainThreadExecutor, mReceiver);
-                }
-            }
-        });
-    }
-
-    @Override
-    boolean isContiguous() {
-        return true;
-    }
-
-    @Nullable
-    @Override
-    public Object getLastKey() {
-        return mDataSource.getKey(mLastLoad, mLastItem);
-    }
-
-    @MainThread
-    @Override
-    public void onInitialized(int count) {
-        notifyInserted(0, count);
-    }
-
-    @MainThread
-    @Override
-    public void onPagePrepended(int leadingNulls, int changedCount, int addedCount) {
-        // consider whether to post more work, now that a page is fully prepended
-        mPrependItemsRequested = mPrependItemsRequested - changedCount - addedCount;
-        mPrependWorkerRunning = false;
-        if (mPrependItemsRequested > 0) {
-            // not done prepending, keep going
-            schedulePrepend();
-        }
-
-        // finally dispatch callbacks, after prepend may have already been scheduled
-        notifyChanged(leadingNulls, changedCount);
-        notifyInserted(0, addedCount);
-
-        offsetBoundaryAccessIndices(addedCount);
-    }
-
-    @MainThread
-    @Override
-    public void onPageAppended(int endPosition, int changedCount, int addedCount) {
-        // consider whether to post more work, now that a page is fully appended
-
-        mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount;
-        mAppendWorkerRunning = false;
-        if (mAppendItemsRequested > 0) {
-            // not done appending, keep going
-            scheduleAppend();
-        }
-
-        // finally dispatch callbacks, after append may have already been scheduled
-        notifyChanged(endPosition, changedCount);
-        notifyInserted(endPosition + changedCount, addedCount);
-    }
-
-    @MainThread
-    @Override
-    public void onPagePlaceholderInserted(int pageIndex) {
-        throw new IllegalStateException("Tiled callback on ContiguousPagedList");
-    }
-
-    @MainThread
-    @Override
-    public void onPageInserted(int start, int count) {
-        throw new IllegalStateException("Tiled callback on ContiguousPagedList");
-    }
-}
diff --git a/android/arch/paging/DataSource.java b/android/arch/paging/DataSource.java
deleted file mode 100644
index 9f51539..0000000
--- a/android/arch/paging/DataSource.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-abstract public class DataSource<K, T> {
-    public interface Factory<Key, Value> {
-    }
-}
diff --git a/android/arch/paging/ItemKeyedDataSource.java b/android/arch/paging/ItemKeyedDataSource.java
deleted file mode 100644
index cb8247b..0000000
--- a/android/arch/paging/ItemKeyedDataSource.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Incremental data loader for paging keyed content, where loaded content uses previously loaded
- * items as input to future loads.
- * <p>
- * Implement a DataSource using ItemKeyedDataSource if you need to use data from item {@code N - 1}
- * to load item {@code N}. This is common, for example, in sorted database queries where
- * attributes of the item such just before the next query define how to execute it.
- * <p>
- * The {@code InMemoryByItemRepository} in the
- * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
- * shows how to implement a network ItemKeyedDataSource using
- * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
- * handling swipe-to-refresh, network errors, and retry.
- *
- * @param <Key> Type of data used to query Value types out of the DataSource.
- * @param <Value> Type of items being loaded by the DataSource.
- */
-public abstract class ItemKeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
-
-    /**
-     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-     *
-     * @param <Key> Type of data used to query Value types out of the DataSource.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadInitialParams<Key> {
-        /**
-         * Load items around this key, or at the beginning of the data set if {@code null} is
-         * passed.
-         * <p>
-         * Note that this key is generally a hint, and may be ignored if you want to always load
-         * from the beginning.
-         */
-        @Nullable
-        public final Key requestedInitialKey;
-
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Note that this may be larger than available data.
-         */
-        public final int requestedLoadSize;
-
-        /**
-         * Defines whether placeholders are enabled, and whether the total count passed to
-         * {@link LoadInitialCallback#onResult(List, int, int)} will be ignored.
-         */
-        public final boolean placeholdersEnabled;
-
-
-        LoadInitialParams(@Nullable Key requestedInitialKey, int requestedLoadSize,
-                boolean placeholdersEnabled) {
-            this.requestedInitialKey = requestedInitialKey;
-            this.requestedLoadSize = requestedLoadSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadBefore(LoadParams, LoadCallback)}
-     * and {@link #loadAfter(LoadParams, LoadCallback)}.
-     *
-     * @param <Key> Type of data used to query Value types out of the DataSource.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadParams<Key> {
-        /**
-         * Load items before/after this key.
-         * <p>
-         * Returned data must begin directly adjacent to this position.
-         */
-        public final Key key;
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Returned page can be of this size, but it may be altered if that is easier, e.g. a
-         * network data source where the backend defines page size.
-         */
-        public final int requestedLoadSize;
-
-        LoadParams(Key key, int requestedLoadSize) {
-            this.key = key;
-            this.requestedLoadSize = requestedLoadSize;
-        }
-    }
-
-    /**
-     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
-     * to return data and, optionally, position/count information.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * If you can compute the number of items in the data set before and after the loaded range,
-     * call the three parameter {@link #onResult(List, int, int)} to pass that information. You
-     * can skip passing this information by calling the single parameter {@link #onResult(List)},
-     * either if it's difficult to compute, or if {@link LoadInitialParams#placeholdersEnabled} is
-     * {@code false}, so the positioning information will be ignored.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Value> Type of items being loaded.
-     */
-    public static class LoadInitialCallback<Value> extends LoadCallback<Value> {
-        private final boolean mCountingEnabled;
-        LoadInitialCallback(@NonNull ItemKeyedDataSource dataSource, boolean countingEnabled,
-                @NonNull PageResult.Receiver<Value> receiver) {
-            super(dataSource, PageResult.INIT, null, receiver);
-            mCountingEnabled = countingEnabled;
-        }
-
-        /**
-         * Called to pass initial load state from a DataSource.
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * and inform how many placeholders should be shown before and after. If counting is cheap
-         * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass data back through this method.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be loaded from this DataSource,
-         *                 pass {@code N}.
-         * @param totalCount Total number of items that may be returned from this DataSource.
-         *                   Includes the number in the initial {@code data} parameter
-         *                   as well as any items that can be loaded in front or behind of
-         *                   {@code data}.
-         */
-        public void onResult(@NonNull List<Value> data, int position, int totalCount) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                validateInitialLoadParams(data, position, totalCount);
-
-                int trailingUnloadedCount = totalCount - position - data.size();
-                if (mCountingEnabled) {
-                    dispatchResultToReceiver(new PageResult<>(
-                            data, position, trailingUnloadedCount, 0));
-                } else {
-                    dispatchResultToReceiver(new PageResult<>(data, position));
-                }
-            }
-        }
-    }
-
-    /**
-     * Callback for ItemKeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)}
-     * and {@link #loadAfter(LoadParams, LoadCallback)} to return data.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Value> Type of items being loaded.
-     */
-    public static class LoadCallback<Value> extends BaseLoadCallback<Value> {
-        LoadCallback(@NonNull ItemKeyedDataSource dataSource, @PageResult.ResultType int type,
-                @Nullable Executor mainThreadExecutor,
-                @NonNull PageResult.Receiver<Value> receiver) {
-            super(dataSource, type, mainThreadExecutor, receiver);
-        }
-
-        /**
-         * Called to pass loaded data from a DataSource.
-         * <p>
-         * Call this method from your ItemKeyedDataSource's
-         * {@link #loadBefore(LoadParams, LoadCallback)} and
-         * {@link #loadAfter(LoadParams, LoadCallback)} methods to return data.
-         * <p>
-         * Call this from {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} to
-         * initialize without counting available data, or supporting placeholders.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the ItemKeyedDataSource.
-         */
-        public void onResult(@NonNull List<Value> data) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
-            }
-        }
-    }
-
-    @Nullable
-    @Override
-    final Key getKey(int position, Value item) {
-        if (item == null) {
-            return null;
-        }
-
-        return getKey(item);
-    }
-
-    @Override
-    final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
-            boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver) {
-        LoadInitialCallback<Value> callback =
-                new LoadInitialCallback<>(this, enablePlaceholders, receiver);
-        loadInitial(new LoadInitialParams<>(key, initialLoadSize, enablePlaceholders), callback);
-
-        // If initialLoad's callback is not called within the body, we force any following calls
-        // to post to the UI thread. This constructor may be run on a background thread, but
-        // after constructor, mutation must happen on UI thread.
-        callback.setPostExecutor(mainThreadExecutor);
-    }
-
-    @Override
-    final void dispatchLoadAfter(int currentEndIndex, @NonNull Value currentEndItem,
-            int pageSize, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver) {
-        loadAfter(new LoadParams<>(getKey(currentEndItem), pageSize),
-                new LoadCallback<>(this, PageResult.APPEND, mainThreadExecutor, receiver));
-    }
-
-    @Override
-    final void dispatchLoadBefore(int currentBeginIndex, @NonNull Value currentBeginItem,
-            int pageSize, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver) {
-        loadBefore(new LoadParams<>(getKey(currentBeginItem), pageSize),
-                new LoadCallback<>(this, PageResult.PREPEND, mainThreadExecutor, receiver));
-    }
-
-    /**
-     * Load initial data.
-     * <p>
-     * This method is called first to initialize a PagedList with data. If it's possible to count
-     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
-     * the callback via the three-parameter
-     * {@link LoadInitialCallback#onResult(List, int, int)}. This enables PagedLists
-     * presenting data from this source to display placeholders to represent unloaded items.
-     * <p>
-     * {@link LoadInitialParams#requestedInitialKey} and {@link LoadInitialParams#requestedLoadSize}
-     * are hints, not requirements, so they may be altered or ignored. Note that ignoring the
-     * {@code requestedInitialKey} can prevent subsequent PagedList/DataSource pairs from
-     * initializing at the same location. If your data source never invalidates (for example,
-     * loading from the network without the network ever signalling that old data must be reloaded),
-     * it's fine to ignore the {@code initialLoadKey} and always start from the beginning of the
-     * data set.
-     *
-     * @param params Parameters for initial load, including initial key and requested size.
-     * @param callback Callback that receives initial load data.
-     */
-    public abstract void loadInitial(@NonNull LoadInitialParams<Key> params,
-            @NonNull LoadInitialCallback<Value> callback);
-
-    /**
-     * Load list data after the key specified in {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally safer to increase the number loaded than reduce.
-     * <p>
-     * Data may be passed synchronously during the loadAfter method, or deferred and called at a
-     * later time. Further loads going down will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key to load after, and requested size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadAfter(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Value> callback);
-
-    /**
-     * Load list data before the key specified in {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally safer to increase the number loaded than reduce.
-     * <p>
-     * <p class="note"><strong>Note:</strong> Data returned will be prepended just before the key
-     * passed, so if you vary size, ensure that the last item is adjacent to the passed key.
-     * <p>
-     * Data may be passed synchronously during the loadBefore method, or deferred and called at a
-     * later time. Further loads going up will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key to load before, and requested size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadBefore(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Value> callback);
-
-    /**
-     * Return a key associated with the given item.
-     * <p>
-     * If your ItemKeyedDataSource is loading from a source that is sorted and loaded by a unique
-     * integer ID, you would return {@code item.getID()} here. This key can then be passed to
-     * {@link #loadBefore(LoadParams, LoadCallback)} or
-     * {@link #loadAfter(LoadParams, LoadCallback)} to load additional items adjacent to the item
-     * passed to this function.
-     * <p>
-     * If your key is more complex, such as when you're sorting by name, then resolving collisions
-     * with integer ID, you'll need to return both. In such a case you would use a wrapper class,
-     * such as {@code Pair<String, Integer>} or, in Kotlin,
-     * {@code data class Key(val name: String, val id: Int)}
-     *
-     * @param item Item to get the key from.
-     * @return Key associated with given item.
-     */
-    @NonNull
-    public abstract Key getKey(@NonNull Value item);
-}
diff --git a/android/arch/paging/ListDataSource.java b/android/arch/paging/ListDataSource.java
deleted file mode 100644
index 1482a91..0000000
--- a/android/arch/paging/ListDataSource.java
+++ /dev/null
@@ -1,51 +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.arch.paging;
-
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class ListDataSource<T> extends PositionalDataSource<T> {
-    private final List<T> mList;
-
-    public ListDataSource(List<T> list) {
-        mList = new ArrayList<>(list);
-    }
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback) {
-        final int totalCount = mList.size();
-
-        final int position = computeInitialLoadPosition(params, totalCount);
-        final int loadSize = computeInitialLoadSize(params, position, totalCount);
-
-        // for simplicity, we could return everything immediately,
-        // but we tile here since it's expected behavior
-        List<T> sublist = mList.subList(position, position + loadSize);
-        callback.onResult(sublist, position, totalCount);
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback) {
-        callback.onResult(mList.subList(params.startPosition,
-                params.startPosition + params.loadSize));
-    }
-}
diff --git a/android/arch/paging/LivePagedListBuilder.java b/android/arch/paging/LivePagedListBuilder.java
deleted file mode 100644
index f2d09cc..0000000
--- a/android/arch/paging/LivePagedListBuilder.java
+++ /dev/null
@@ -1,211 +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.arch.paging;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.lifecycle.ComputableLiveData;
-import android.arch.lifecycle.LiveData;
-import android.support.annotation.AnyThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.concurrent.Executor;
-
-/**
- * Builder for {@code LiveData<PagedList>}, given a {@link DataSource.Factory} and a
- * {@link PagedList.Config}.
- * <p>
- * The required parameters are in the constructor, so you can simply construct and build, or
- * optionally enable extra features (such as initial load key, or BoundaryCallback.
- *
- * @param <Key> Type of input valued used to load data from the DataSource. Must be integer if
- *             you're using PositionalDataSource.
- * @param <Value> Item type being presented.
- */
-public class LivePagedListBuilder<Key, Value> {
-    private Key mInitialLoadKey;
-    private PagedList.Config mConfig;
-    private DataSource.Factory<Key, Value> mDataSourceFactory;
-    private PagedList.BoundaryCallback mBoundaryCallback;
-    private Executor mBackgroundThreadExecutor;
-
-    /**
-     * Creates a LivePagedListBuilder with required parameters.
-     *
-     * @param dataSourceFactory DataSource factory providing DataSource generations.
-     * @param config Paging configuration.
-     */
-    public LivePagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
-            @NonNull PagedList.Config config) {
-        mDataSourceFactory = dataSourceFactory;
-        mConfig = config;
-    }
-
-    /**
-     * Creates a LivePagedListBuilder with required parameters.
-     * <p>
-     * This method is a convenience for:
-     * <pre>
-     * LivePagedListBuilder(dataSourceFactory,
-     *         new PagedList.Config.Builder().setPageSize(pageSize).build())
-     * </pre>
-     *
-     * @param dataSourceFactory DataSource.Factory providing DataSource generations.
-     * @param pageSize Size of pages to load.
-     */
-    public LivePagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
-            int pageSize) {
-        this(dataSourceFactory, new PagedList.Config.Builder().setPageSize(pageSize).build());
-    }
-
-    /**
-     * First loading key passed to the first PagedList/DataSource.
-     * <p>
-     * When a new PagedList/DataSource pair is created after the first, it acquires a load key from
-     * the previous generation so that data is loaded around the position already being observed.
-     *
-     * @param key Initial load key passed to the first PagedList/DataSource.
-     * @return this
-     */
-    @NonNull
-    public LivePagedListBuilder<Key, Value> setInitialLoadKey(@Nullable Key key) {
-        mInitialLoadKey = key;
-        return this;
-    }
-
-    /**
-     * Sets a {@link PagedList.BoundaryCallback} on each PagedList created, typically used to load
-     * additional data from network when paging from local storage.
-     * <p>
-     * Pass a BoundaryCallback to listen to when the PagedList runs out of data to load. If this
-     * method is not called, or {@code null} is passed, you will not be notified when each
-     * DataSource runs out of data to provide to its PagedList.
-     * <p>
-     * If you are paging from a DataSource.Factory backed by local storage, you can set a
-     * BoundaryCallback to know when there is no more information to page from local storage.
-     * This is useful to page from the network when local storage is a cache of network data.
-     * <p>
-     * Note that when using a BoundaryCallback with a {@code LiveData<PagedList>}, method calls
-     * on the callback may be dispatched multiple times - one for each PagedList/DataSource
-     * pair. If loading network data from a BoundaryCallback, you should prevent multiple
-     * dispatches of the same method from triggering multiple simultaneous network loads.
-     *
-     * @param boundaryCallback The boundary callback for listening to PagedList load state.
-     * @return this
-     */
-    @SuppressWarnings("unused")
-    @NonNull
-    public LivePagedListBuilder<Key, Value> setBoundaryCallback(
-            @Nullable PagedList.BoundaryCallback<Value> boundaryCallback) {
-        mBoundaryCallback = boundaryCallback;
-        return this;
-    }
-
-    /**
-     * Sets executor which will be used for background loading of pages.
-     * <p>
-     * If not set, defaults to the Arch components I/O thread.
-     * <p>
-     * Does not affect initial load, which will be always be done on done on the Arch components
-     * I/O thread.
-     *
-     * @param backgroundThreadExecutor Executor for background DataSource loading.
-     * @return this
-     */
-    @SuppressWarnings("unused")
-    @NonNull
-    public LivePagedListBuilder<Key, Value> setBackgroundThreadExecutor(
-            @NonNull Executor backgroundThreadExecutor) {
-        mBackgroundThreadExecutor = backgroundThreadExecutor;
-        return this;
-    }
-
-    /**
-     * Constructs the {@code LiveData<PagedList>}.
-     * <p>
-     * No work (such as loading) is done immediately, the creation of the first PagedList is is
-     * deferred until the LiveData is observed.
-     *
-     * @return The LiveData of PagedLists
-     */
-    @NonNull
-    public LiveData<PagedList<Value>> build() {
-        if (mConfig == null) {
-            throw new IllegalArgumentException("PagedList.Config must be provided");
-        }
-        if (mDataSourceFactory == null) {
-            throw new IllegalArgumentException("DataSource.Factory must be provided");
-        }
-        if (mBackgroundThreadExecutor == null) {
-            mBackgroundThreadExecutor = ArchTaskExecutor.getIOThreadExecutor();
-        }
-
-        return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
-                ArchTaskExecutor.getMainThreadExecutor(), mBackgroundThreadExecutor);
-    }
-
-    @AnyThread
-    @NonNull
-    private static <Key, Value> LiveData<PagedList<Value>> create(
-            @Nullable final Key initialLoadKey,
-            @NonNull final PagedList.Config config,
-            @Nullable final PagedList.BoundaryCallback boundaryCallback,
-            @NonNull final DataSource.Factory<Key, Value> dataSourceFactory,
-            @NonNull final Executor mainThreadExecutor,
-            @NonNull final Executor backgroundThreadExecutor) {
-        return new ComputableLiveData<PagedList<Value>>() {
-            @Nullable
-            private PagedList<Value> mList;
-            @Nullable
-            private DataSource<Key, Value> mDataSource;
-
-            private final DataSource.InvalidatedCallback mCallback =
-                    new DataSource.InvalidatedCallback() {
-                        @Override
-                        public void onInvalidated() {
-                            invalidate();
-                        }
-                    };
-
-            @Override
-            protected PagedList<Value> compute() {
-                @Nullable Key initializeKey = initialLoadKey;
-                if (mList != null) {
-                    //noinspection unchecked
-                    initializeKey = (Key) mList.getLastKey();
-                }
-
-                do {
-                    if (mDataSource != null) {
-                        mDataSource.removeInvalidatedCallback(mCallback);
-                    }
-
-                    mDataSource = dataSourceFactory.create();
-                    mDataSource.addInvalidatedCallback(mCallback);
-
-                    mList = new PagedList.Builder<>(mDataSource, config)
-                            .setMainThreadExecutor(mainThreadExecutor)
-                            .setBackgroundThreadExecutor(backgroundThreadExecutor)
-                            .setBoundaryCallback(boundaryCallback)
-                            .setInitialKey(initializeKey)
-                            .build();
-                } while (mList.isDetached());
-                return mList;
-            }
-        }.getLiveData();
-    }
-}
diff --git a/android/arch/paging/LivePagedListProvider.java b/android/arch/paging/LivePagedListProvider.java
deleted file mode 100644
index 74334ee..0000000
--- a/android/arch/paging/LivePagedListProvider.java
+++ /dev/null
@@ -1,106 +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.arch.paging;
-
-import android.arch.lifecycle.LiveData;
-import android.support.annotation.AnyThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-
-// NOTE: Room 1.0 depends on this class, so it should not be removed until
-// we can require a version of Room that uses DataSource.Factory directly
-/**
- * Provides a {@code LiveData<PagedList>}, given a means to construct a DataSource.
- * <p>
- * Return type for data-loading system of an application or library to produce a
- * {@code LiveData<PagedList>}, while leaving the details of the paging mechanism up to the
- * consumer.
- *
- * @param <Key> Type of input valued used to load data from the DataSource. Must be integer if
- *             you're using PositionalDataSource.
- * @param <Value> Data type produced by the DataSource, and held by the PagedLists.
- *
- * @see PagedListAdapter
- * @see DataSource
- * @see PagedList
- *
- * @deprecated use {@link LivePagedListBuilder} to construct a {@code LiveData<PagedList>}. It
- * provides the same construction capability with more customization, and simpler defaults. The role
- * of DataSource construction has been separated out to {@link DataSource.Factory} to access or
- * provide a self-invalidating sequence of DataSources. If you were acquiring this from Room, you
- * can switch to having your Dao return a {@link DataSource.Factory} instead, and create a
- * {@code LiveData<PagedList>} with a {@link LivePagedListBuilder}.
- */
-@Deprecated
-public abstract class LivePagedListProvider<Key, Value> implements DataSource.Factory<Key, Value> {
-
-    @Override
-    public DataSource<Key, Value> create() {
-        return createDataSource();
-    }
-
-    /**
-     * Construct a new data source to be wrapped in a new PagedList, which will be returned
-     * through the LiveData.
-     *
-     * @return The data source.
-     */
-    @WorkerThread
-    protected abstract DataSource<Key, Value> createDataSource();
-
-    /**
-     * Creates a LiveData of PagedLists, given the page size.
-     * <p>
-     * This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
-     * {@link android.support.v7.widget.RecyclerView}.
-     *
-     * @param initialLoadKey Initial key used to load initial data from the data source.
-     * @param pageSize Page size defining how many items are loaded from a data source at a time.
-     *                 Recommended to be multiple times the size of item displayed at once.
-     *
-     * @return The LiveData of PagedLists.
-     */
-    @AnyThread
-    @NonNull
-    public LiveData<PagedList<Value>> create(@Nullable Key initialLoadKey, int pageSize) {
-        return new LivePagedListBuilder<>(this, pageSize)
-                .setInitialLoadKey(initialLoadKey)
-                .build();
-    }
-
-    /**
-     * Creates a LiveData of PagedLists, given the PagedList.Config.
-     * <p>
-     * This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
-     * {@link android.support.v7.widget.RecyclerView}.
-     *
-     * @param initialLoadKey Initial key to pass to the data source to initialize data with.
-     * @param config PagedList.Config to use with created PagedLists. This specifies how the
-     *               lists will load data.
-     *
-     * @return The LiveData of PagedLists.
-     */
-    @AnyThread
-    @NonNull
-    public LiveData<PagedList<Value>> create(@Nullable Key initialLoadKey,
-            @NonNull PagedList.Config config) {
-        return new LivePagedListBuilder<>(this, config)
-                .setInitialLoadKey(initialLoadKey)
-                .build();
-    }
-}
diff --git a/android/arch/paging/PageKeyedDataSource.java b/android/arch/paging/PageKeyedDataSource.java
deleted file mode 100644
index a10ecee..0000000
--- a/android/arch/paging/PageKeyedDataSource.java
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.GuardedBy;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Incremental data loader for page-keyed content, where requests return keys for next/previous
- * pages.
- * <p>
- * Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N - 1}
- * to load page {@code N}. This is common, for example, in network APIs that include a next/previous
- * link or key with each page load.
- * <p>
- * The {@code InMemoryByPageRepository} in the
- * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
- * shows how to implement a network PageKeyedDataSource using
- * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
- * handling swipe-to-refresh, network errors, and retry.
- *
- * @param <Key> Type of data used to query Value types out of the DataSource.
- * @param <Value> Type of items being loaded by the DataSource.
- */
-public abstract class PageKeyedDataSource<Key, Value> extends ContiguousDataSource<Key, Value> {
-    private final Object mKeyLock = new Object();
-
-    @Nullable
-    @GuardedBy("mKeyLock")
-    private Key mNextKey = null;
-
-    @Nullable
-    @GuardedBy("mKeyLock")
-    private Key mPreviousKey = null;
-
-    private void initKeys(@Nullable Key previousKey, @Nullable Key nextKey) {
-        synchronized (mKeyLock) {
-            mPreviousKey = previousKey;
-            mNextKey = nextKey;
-        }
-    }
-
-    private void setPreviousKey(@Nullable Key previousKey) {
-        synchronized (mKeyLock) {
-            mPreviousKey = previousKey;
-        }
-    }
-
-    private void setNextKey(@Nullable Key nextKey) {
-        synchronized (mKeyLock) {
-            mNextKey = nextKey;
-        }
-    }
-
-    private @Nullable Key getPreviousKey() {
-        synchronized (mKeyLock) {
-            return mPreviousKey;
-        }
-    }
-
-    private @Nullable Key getNextKey() {
-        synchronized (mKeyLock) {
-            return mNextKey;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-     *
-     * @param <Key> Type of data used to query pages.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadInitialParams<Key> {
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Note that this may be larger than available data.
-         */
-        public final int requestedLoadSize;
-
-        /**
-         * Defines whether placeholders are enabled, and whether the total count passed to
-         * {@link LoadInitialCallback#onResult(List, int, int, Key, Key)} will be ignored.
-         */
-        public final boolean placeholdersEnabled;
-
-
-        LoadInitialParams(int requestedLoadSize,
-                boolean placeholdersEnabled) {
-            this.requestedLoadSize = requestedLoadSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadBefore(LoadParams, LoadCallback)} and
-     * {@link #loadAfter(LoadParams, LoadCallback)}.
-     *
-     * @param <Key> Type of data used to query pages.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadParams<Key> {
-        /**
-         * Load items before/after this key.
-         * <p>
-         * Returned data must begin directly adjacent to this position.
-         */
-        public final Key key;
-
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Returned page can be of this size, but it may be altered if that is easier, e.g. a
-         * network data source where the backend defines page size.
-         */
-        public final int requestedLoadSize;
-
-        LoadParams(Key key, int requestedLoadSize) {
-            this.key = key;
-            this.requestedLoadSize = requestedLoadSize;
-        }
-    }
-
-    /**
-     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
-     * to return data and, optionally, position/count information.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * If you can compute the number of items in the data set before and after the loaded range,
-     * call the five parameter {@link #onResult(List, int, int, Object, Object)} to pass that
-     * information. You can skip passing this information by calling the three parameter
-     * {@link #onResult(List, Object, Object)}, either if it's difficult to compute, or if
-     * {@link LoadInitialParams#placeholdersEnabled} is {@code false}, so the positioning
-     * information will be ignored.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Key> Type of data used to query pages.
-     * @param <Value> Type of items being loaded.
-     */
-    public static class LoadInitialCallback<Key, Value> extends BaseLoadCallback<Value> {
-        private final PageKeyedDataSource<Key, Value> mDataSource;
-        private final boolean mCountingEnabled;
-        LoadInitialCallback(@NonNull PageKeyedDataSource<Key, Value> dataSource,
-                boolean countingEnabled, @NonNull PageResult.Receiver<Value> receiver) {
-            super(dataSource, PageResult.INIT, null, receiver);
-            mDataSource = dataSource;
-            mCountingEnabled = countingEnabled;
-        }
-
-        /**
-         * Called to pass initial load state from a DataSource.
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * and inform how many placeholders should be shown before and after. If counting is cheap
-         * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass data back through this method.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be loaded from this DataSource,
-         *                 pass {@code N}.
-         * @param totalCount Total number of items that may be returned from this DataSource.
-         *                   Includes the number in the initial {@code data} parameter
-         *                   as well as any items that can be loaded in front or behind of
-         *                   {@code data}.
-         */
-        public void onResult(@NonNull List<Value> data, int position, int totalCount,
-                @Nullable Key previousPageKey, @Nullable Key nextPageKey) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                validateInitialLoadParams(data, position, totalCount);
-
-                // setup keys before dispatching data, so guaranteed to be ready
-                mDataSource.initKeys(previousPageKey, nextPageKey);
-
-                int trailingUnloadedCount = totalCount - position - data.size();
-                if (mCountingEnabled) {
-                    dispatchResultToReceiver(new PageResult<>(
-                            data, position, trailingUnloadedCount, 0));
-                } else {
-                    dispatchResultToReceiver(new PageResult<>(data, position));
-                }
-            }
-        }
-
-        /**
-         * Called to pass loaded data from a DataSource.
-         * <p>
-         * Call this from {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} to
-         * initialize without counting available data, or supporting placeholders.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         *
-         * @param data List of items loaded from the PageKeyedDataSource.
-         * @param previousPageKey Key for page before the initial load result, or {@code null} if no
-         *                        more data can be loaded before.
-         * @param nextPageKey Key for page after the initial load result, or {@code null} if no
-         *                        more data can be loaded after.
-         */
-        public void onResult(@NonNull List<Value> data, @Nullable Key previousPageKey,
-                @Nullable Key nextPageKey) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                mDataSource.initKeys(previousPageKey, nextPageKey);
-                dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
-            }
-        }
-    }
-
-    /**
-     * Callback for PageKeyedDataSource {@link #loadBefore(LoadParams, LoadCallback)} and
-     * {@link #loadAfter(LoadParams, LoadCallback)} to return data.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <Key> Type of data used to query pages.
-     * @param <Value> Type of items being loaded.
-     */
-    public static class LoadCallback<Key, Value> extends BaseLoadCallback<Value> {
-        private final PageKeyedDataSource<Key, Value> mDataSource;
-        LoadCallback(@NonNull PageKeyedDataSource<Key, Value> dataSource,
-                @PageResult.ResultType int type, @Nullable Executor mainThreadExecutor,
-                @NonNull PageResult.Receiver<Value> receiver) {
-            super(dataSource, type, mainThreadExecutor, receiver);
-            mDataSource = dataSource;
-        }
-
-        /**
-         * Called to pass loaded data from a DataSource.
-         * <p>
-         * Call this method from your PageKeyedDataSource's
-         * {@link #loadBefore(LoadParams, LoadCallback)} and
-         * {@link #loadAfter(LoadParams, LoadCallback)} methods to return data.
-         * <p>
-         * It is always valid to pass a different amount of data than what is requested. Pass an
-         * empty list if there is no more data to load.
-         * <p>
-         * Pass the key for the subsequent page to load to adjacentPageKey. For example, if you've
-         * loaded a page in {@link #loadBefore(LoadParams, LoadCallback)}, pass the key for the
-         * previous page, or {@code null} if the loaded page is the first. If in
-         * {@link #loadAfter(LoadParams, LoadCallback)}, pass the key for the next page, or
-         * {@code null} if the loaded page is the last.
-         *
-         * @param data List of items loaded from the PageKeyedDataSource.
-         * @param adjacentPageKey Key for subsequent page load (previous page in {@link #loadBefore}
-         *                        / next page in {@link #loadAfter}), or {@code null} if there are
-         *                        no more pages to load in the current load direction.
-         */
-        public void onResult(@NonNull List<Value> data, @Nullable Key adjacentPageKey) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                if (mResultType == PageResult.APPEND) {
-                    mDataSource.setNextKey(adjacentPageKey);
-                } else {
-                    mDataSource.setPreviousKey(adjacentPageKey);
-                }
-                dispatchResultToReceiver(new PageResult<>(data, 0, 0, 0));
-            }
-        }
-    }
-
-    @Nullable
-    @Override
-    final Key getKey(int position, Value item) {
-        // don't attempt to persist keys, since we currently don't pass them to initial load
-        return null;
-    }
-
-    @Override
-    final void dispatchLoadInitial(@Nullable Key key, int initialLoadSize, int pageSize,
-            boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver) {
-        LoadInitialCallback<Key, Value> callback =
-                new LoadInitialCallback<>(this, enablePlaceholders, receiver);
-        loadInitial(new LoadInitialParams<Key>(initialLoadSize, enablePlaceholders), callback);
-
-        // If initialLoad's callback is not called within the body, we force any following calls
-        // to post to the UI thread. This constructor may be run on a background thread, but
-        // after constructor, mutation must happen on UI thread.
-        callback.setPostExecutor(mainThreadExecutor);
-    }
-
-
-    @Override
-    final void dispatchLoadAfter(int currentEndIndex, @NonNull Value currentEndItem,
-            int pageSize, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver) {
-        @Nullable Key key = getNextKey();
-        if (key != null) {
-            loadAfter(new LoadParams<>(key, pageSize),
-                    new LoadCallback<>(this, PageResult.APPEND, mainThreadExecutor, receiver));
-        }
-    }
-
-    @Override
-    final void dispatchLoadBefore(int currentBeginIndex, @NonNull Value currentBeginItem,
-            int pageSize, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<Value> receiver) {
-        @Nullable Key key = getPreviousKey();
-        if (key != null) {
-            loadBefore(new LoadParams<>(key, pageSize),
-                    new LoadCallback<>(this, PageResult.PREPEND, mainThreadExecutor, receiver));
-        }
-    }
-
-    /**
-     * Load initial data.
-     * <p>
-     * This method is called first to initialize a PagedList with data. If it's possible to count
-     * the items that can be loaded by the DataSource, it's recommended to pass the loaded data to
-     * the callback via the three-parameter
-     * {@link LoadInitialCallback#onResult(List, int, int, Object, Object)}. This enables PagedLists
-     * presenting data from this source to display placeholders to represent unloaded items.
-     * <p>
-     * {@link LoadInitialParams#requestedLoadSize} is a hint, not a requirement, so it may be may be
-     * altered or ignored.
-     *
-     * @param params Parameters for initial load, including requested load size.
-     * @param callback Callback that receives initial load data.
-     */
-    public abstract void loadInitial(@NonNull LoadInitialParams<Key> params,
-            @NonNull LoadInitialCallback<Key, Value> callback);
-
-    /**
-     * Prepend page with the key specified by {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally safer to increase the number loaded than reduce.
-     * <p>
-     * Data may be passed synchronously during the load method, or deferred and called at a
-     * later time. Further loads going down will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key for the new page, and requested load
-     *               size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadBefore(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Key, Value> callback);
-
-    /**
-     * Append page with the key specified by {@link LoadParams#key LoadParams.key}.
-     * <p>
-     * It's valid to return a different list size than the page size if it's easier, e.g. if your
-     * backend defines page sizes. It is generally safer to increase the number loaded than reduce.
-     * <p>
-     * Data may be passed synchronously during the load method, or deferred and called at a
-     * later time. Further loads going down will be blocked until the callback is called.
-     * <p>
-     * If data cannot be loaded (for example, if the request is invalid, or the data would be stale
-     * and inconsistent, it is valid to call {@link #invalidate()} to invalidate the data source,
-     * and prevent further loading.
-     *
-     * @param params Parameters for the load, including the key for the new page, and requested load
-     *               size.
-     * @param callback Callback that receives loaded data.
-     */
-    public abstract void loadAfter(@NonNull LoadParams<Key> params,
-            @NonNull LoadCallback<Key, Value> callback);
-}
diff --git a/android/arch/paging/PageResult.java b/android/arch/paging/PageResult.java
deleted file mode 100644
index cf2216f..0000000
--- a/android/arch/paging/PageResult.java
+++ /dev/null
@@ -1,92 +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.arch.paging;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.support.annotation.IntDef;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-
-import java.lang.annotation.Retention;
-import java.util.Collections;
-import java.util.List;
-
-class PageResult<T> {
-    @SuppressWarnings("unchecked")
-    private static final PageResult INVALID_RESULT =
-            new PageResult(Collections.EMPTY_LIST, 0);
-
-    @SuppressWarnings("unchecked")
-    static <T> PageResult<T> getInvalidResult() {
-        return INVALID_RESULT;
-    }
-
-
-    @Retention(SOURCE)
-    @IntDef({INIT, APPEND, PREPEND, TILE})
-    @interface ResultType {}
-
-    static final int INIT = 0;
-
-    // contiguous results
-    static final int APPEND = 1;
-    static final int PREPEND = 2;
-
-    // non-contiguous, tile result
-    static final int TILE = 3;
-
-    @NonNull
-    public final List<T> page;
-    @SuppressWarnings("WeakerAccess")
-    public final int leadingNulls;
-    @SuppressWarnings("WeakerAccess")
-    public final int trailingNulls;
-    @SuppressWarnings("WeakerAccess")
-    public final int positionOffset;
-
-    PageResult(@NonNull List<T> list, int leadingNulls, int trailingNulls, int positionOffset) {
-        this.page = list;
-        this.leadingNulls = leadingNulls;
-        this.trailingNulls = trailingNulls;
-        this.positionOffset = positionOffset;
-    }
-
-    PageResult(@NonNull List<T> list, int positionOffset) {
-        this.page = list;
-        this.leadingNulls = 0;
-        this.trailingNulls = 0;
-        this.positionOffset = positionOffset;
-    }
-
-    @Override
-    public String toString() {
-        return "Result " + leadingNulls
-                + ", " + page
-                + ", " + trailingNulls
-                + ", offset " + positionOffset;
-    }
-
-    public boolean isInvalid() {
-        return this == INVALID_RESULT;
-    }
-
-    abstract static class Receiver<T> {
-        @MainThread
-        public abstract void onPageResult(@ResultType int type, @NonNull PageResult<T> pageResult);
-    }
-}
diff --git a/android/arch/paging/PagedList.java b/android/arch/paging/PagedList.java
deleted file mode 100644
index c6de5c5..0000000
--- a/android/arch/paging/PagedList.java
+++ /dev/null
@@ -1,967 +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.arch.paging;
-
-import android.support.annotation.AnyThread;
-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.lang.ref.WeakReference;
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Lazy loading list that pages in content from a {@link DataSource}.
- * <p>
- * A PagedList is a {@link List} which loads its data in chunks (pages) from a {@link DataSource}.
- * Items can be accessed with {@link #get(int)}, and further loading can be triggered with
- * {@link #loadAround(int)}. See {@link PagedListAdapter}, which enables the binding of a PagedList
- * to a {@link android.support.v7.widget.RecyclerView}.
- * <h4>Loading Data</h4>
- * <p>
- * All data in a PagedList is loaded from its {@link DataSource}. Creating a PagedList loads data
- * from the DataSource immediately, and should for this reason be done on a background thread. The
- * constructed PagedList may then be passed to and used on the UI thread. This is done to prevent
- * passing a list with no loaded content to the UI thread, which should generally not be presented
- * to the user.
- * <p>
- * When {@link #loadAround} is called, items will be loaded in near the passed list index. If
- * placeholder {@code null}s are present in the list, they will be replaced as content is
- * loaded. If not, newly loaded items will be inserted at the beginning or end of the list.
- * <p>
- * PagedList can present data for an unbounded, infinite scrolling list, or a very large but
- * countable list. Use {@link Config} to control how many items a PagedList loads, and when.
- * <p>
- * If you use {@link LivePagedListBuilder} to get a
- * {@link android.arch.lifecycle.LiveData}&lt;PagedList>, it will initialize PagedLists on a
- * background thread for you.
- * <h4>Placeholders</h4>
- * <p>
- * There are two ways that PagedList can represent its not-yet-loaded data - with or without
- * {@code null} placeholders.
- * <p>
- * With placeholders, the PagedList is always the full size of the data set. {@code get(N)} returns
- * the {@code N}th item in the data set, or {@code null} if its not yet loaded.
- * <p>
- * Without {@code null} placeholders, the PagedList is the sublist of data that has already been
- * loaded. The size of the PagedList is the number of currently loaded items, and {@code get(N)}
- * returns the {@code N}th <em>loaded</em> item. This is not necessarily the {@code N}th item in the
- * data set.
- * <p>
- * Placeholders have several benefits:
- * <ul>
- *     <li>They express the full sized list to the presentation layer (often a
- *     {@link PagedListAdapter}), and so can support scrollbars (without jumping as pages are
- *     loaded) and fast-scrolling to any position, whether loaded or not.
- *     <li>They avoid the need for a loading spinner at the end of the loaded list, since the list
- *     is always full sized.
- * </ul>
- * <p>
- * They also have drawbacks:
- * <ul>
- *     <li>Your Adapter (or other presentation mechanism) needs to account for {@code null} items.
- *     This often means providing default values in data you bind to a
- *     {@link android.support.v7.widget.RecyclerView.ViewHolder}.
- *     <li>They don't work well if your item views are of different sizes, as this will prevent
- *     loading items from cross-fading nicely.
- *     <li>They require you to count your data set, which can be expensive or impossible, depending
- *     on where your data comes from.
- * </ul>
- * <p>
- * Placeholders are enabled by default, but can be disabled in two ways. They are disabled if the
- * DataSource does not count its data set in its initial load, or if  {@code false} is passed to
- * {@link Config.Builder#setEnablePlaceholders(boolean)} when building a {@link Config}.
- *
- * @param <T> The type of the entries in the list.
- */
-public abstract class PagedList<T> extends AbstractList<T> {
-    @NonNull
-    final Executor mMainThreadExecutor;
-    @NonNull
-    final Executor mBackgroundThreadExecutor;
-    @Nullable
-    final BoundaryCallback<T> mBoundaryCallback;
-    @NonNull
-    final Config mConfig;
-    @NonNull
-    final PagedStorage<T> mStorage;
-
-    int mLastLoad = 0;
-    T mLastItem = null;
-
-    // if set to true, mBoundaryCallback is non-null, and should
-    // be dispatched when nearby load has occurred
-    private boolean mBoundaryCallbackBeginDeferred = false;
-    private boolean mBoundaryCallbackEndDeferred = false;
-
-    // lowest and highest index accessed by loadAround. Used to
-    // decide when mBoundaryCallback should be dispatched
-    private int mLowestIndexAccessed = Integer.MAX_VALUE;
-    private int mHighestIndexAccessed = Integer.MIN_VALUE;
-
-    private final AtomicBoolean mDetached = new AtomicBoolean(false);
-
-    protected final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
-
-    PagedList(@NonNull PagedStorage<T> storage,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @Nullable BoundaryCallback<T> boundaryCallback,
-            @NonNull Config config) {
-        mStorage = storage;
-        mMainThreadExecutor = mainThreadExecutor;
-        mBackgroundThreadExecutor = backgroundThreadExecutor;
-        mBoundaryCallback = boundaryCallback;
-        mConfig = config;
-    }
-
-    /**
-     * Create a PagedList which loads data from the provided data source on a background thread,
-     * posting updates to the main thread.
-     *
-     *
-     * @param dataSource DataSource providing data to the PagedList
-     * @param mainThreadExecutor Thread that will use and consume data from the PagedList.
-     *                           Generally, this is the UI/main thread.
-     * @param backgroundThreadExecutor Data loading will be done via this executor - should be a
-     *                                 background thread.
-     * @param boundaryCallback Optional boundary callback to attach to the list.
-     * @param config PagedList Config, which defines how the PagedList will load data.
-     * @param <K> Key type that indicates to the DataSource what data to load.
-     * @param <T> Type of items to be held and loaded by the PagedList.
-     *
-     * @return Newly created PagedList, which will page in data from the DataSource as needed.
-     */
-    @NonNull
-    private static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @Nullable BoundaryCallback<T> boundaryCallback,
-            @NonNull Config config,
-            @Nullable K key) {
-        if (dataSource.isContiguous() || !config.enablePlaceholders) {
-            if (!dataSource.isContiguous()) {
-                //noinspection unchecked
-                dataSource = (DataSource<K, T>) ((PositionalDataSource<T>) dataSource)
-                        .wrapAsContiguousWithoutPlaceholders();
-            }
-            ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
-            return new ContiguousPagedList<>(contigDataSource,
-                    mainThreadExecutor,
-                    backgroundThreadExecutor,
-                    boundaryCallback,
-                    config,
-                    key);
-        } else {
-            return new TiledPagedList<>((PositionalDataSource<T>) dataSource,
-                    mainThreadExecutor,
-                    backgroundThreadExecutor,
-                    boundaryCallback,
-                    config,
-                    (key != null) ? (Integer) key : 0);
-        }
-    }
-
-    /**
-     * Builder class for PagedList.
-     * <p>
-     * DataSource, Config, main thread and background executor must all be provided.
-     * <p>
-     * A PagedList queries initial data from its DataSource during construction, to avoid empty
-     * PagedLists being presented to the UI when possible. It's preferred to present initial data,
-     * so that the UI doesn't show an empty list, or placeholders for a few frames, just before
-     * showing initial content.
-     * <p>
-     * {@link LivePagedListBuilder} does this creation on a background thread automatically, if you
-     * want to receive a {@code LiveData<PagedList<...>>}.
-     *
-     * @param <Key> Type of key used to load data from the DataSource.
-     * @param <Value> Type of items held and loaded by the PagedList.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class Builder<Key, Value> {
-        private final DataSource<Key, Value> mDataSource;
-        private final Config mConfig;
-        private Executor mMainThreadExecutor;
-        private Executor mBackgroundThreadExecutor;
-        private BoundaryCallback mBoundaryCallback;
-        private Key mInitialKey;
-
-        /**
-         * Create a PagedList.Builder with the provided {@link DataSource} and {@link Config}.
-         *
-         * @param dataSource DataSource the PagedList will load from.
-         * @param config Config that defines how the PagedList loads data from its DataSource.
-         */
-        public Builder(@NonNull DataSource<Key, Value> dataSource, @NonNull Config config) {
-            //noinspection ConstantConditions
-            if (dataSource == null) {
-                throw new IllegalArgumentException("DataSource may not be null");
-            }
-            //noinspection ConstantConditions
-            if (config == null) {
-                throw new IllegalArgumentException("Config may not be null");
-            }
-            mDataSource = dataSource;
-            mConfig = config;
-        }
-
-        /**
-         * Create a PagedList.Builder with the provided {@link DataSource} and page size.
-         * <p>
-         * This method is a convenience for:
-         * <pre>
-         * PagedList.Builder(dataSource,
-         *         new PagedList.Config.Builder().setPageSize(pageSize).build());
-         * </pre>
-         *
-         * @param dataSource DataSource the PagedList will load from.
-         * @param pageSize Config that defines how the PagedList loads data from its DataSource.
-         */
-        public Builder(@NonNull DataSource<Key, Value> dataSource, int pageSize) {
-            this(dataSource, new PagedList.Config.Builder().setPageSize(pageSize).build());
-        }
-        /**
-         * The executor defining where main/UI thread for page loading updates.
-         *
-         * @param mainThreadExecutor Executor for main/UI thread to receive {@link Callback} calls.
-         * @return this
-         */
-        @NonNull
-        public Builder<Key, Value> setMainThreadExecutor(@NonNull Executor mainThreadExecutor) {
-            mMainThreadExecutor = mainThreadExecutor;
-            return this;
-        }
-
-        /**
-         * The executor on which background loading will be run.
-         * <p>
-         * Does not affect initial load, which will be done on whichever thread the PagedList is
-         * created on.
-         *
-         * @param backgroundThreadExecutor Executor for background DataSource loading.
-         * @return this
-         */
-        @NonNull
-        public Builder<Key, Value> setBackgroundThreadExecutor(
-                @NonNull Executor backgroundThreadExecutor) {
-            mBackgroundThreadExecutor = backgroundThreadExecutor;
-            return this;
-        }
-
-        /**
-         * The BoundaryCallback for out of data events.
-         * <p>
-         * Pass a BoundaryCallback to listen to when the PagedList runs out of data to load.
-         *
-         * @param boundaryCallback BoundaryCallback for listening to out-of-data events.
-         * @return this
-         */
-        @SuppressWarnings("unused")
-        @NonNull
-        public Builder<Key, Value> setBoundaryCallback(
-                @Nullable BoundaryCallback boundaryCallback) {
-            mBoundaryCallback = boundaryCallback;
-            return this;
-        }
-
-        /**
-         * Sets the initial key the DataSource should load around as part of initialization.
-         *
-         * @param initialKey Key the DataSource should load around as part of initialization.
-         * @return this
-         */
-        @NonNull
-        public Builder<Key, Value> setInitialKey(@Nullable Key initialKey) {
-            mInitialKey = initialKey;
-            return this;
-        }
-
-        /**
-         * Creates a {@link PagedList} with the given parameters.
-         * <p>
-         * This call will dispatch the {@link DataSource}'s loadInitial method immediately. If a
-         * DataSource posts all of its work (e.g. to a network thread), the PagedList will
-         * be immediately created as empty, and grow to its initial size when the initial load
-         * completes.
-         * <p>
-         * If the DataSource implements its load synchronously, doing the load work immediately in
-         * the loadInitial method, the PagedList will block on that load before completing
-         * construction. In this case, use a background thread to create a PagedList.
-         * <p>
-         * It's fine to create a PagedList with an async DataSource on the main thread, such as in
-         * the constructor of a ViewModel. An async network load won't block the initialLoad
-         * function. For a synchronous DataSource such as one created from a Room database, a
-         * {@code LiveData<PagedList>} can be safely constructed with {@link LivePagedListBuilder}
-         * on the main thread, since actual construction work is deferred, and done on a background
-         * thread.
-         * <p>
-         * While build() will always return a PagedList, it's important to note that the PagedList
-         * initial load may fail to acquire data from the DataSource. This can happen for example if
-         * the DataSource is invalidated during its initial load. If this happens, the PagedList
-         * will be immediately {@link PagedList#isDetached() detached}, and you can retry
-         * construction (including setting a new DataSource).
-         *
-         * @return The newly constructed PagedList
-         */
-        @WorkerThread
-        @NonNull
-        public PagedList<Value> build() {
-            // TODO: define defaults, once they can be used in module without android dependency
-            if (mMainThreadExecutor == null) {
-                throw new IllegalArgumentException("MainThreadExecutor required");
-            }
-            if (mBackgroundThreadExecutor == null) {
-                throw new IllegalArgumentException("BackgroundThreadExecutor required");
-            }
-
-            //noinspection unchecked
-            return PagedList.create(
-                    mDataSource,
-                    mMainThreadExecutor,
-                    mBackgroundThreadExecutor,
-                    mBoundaryCallback,
-                    mConfig,
-                    mInitialKey);
-        }
-    }
-
-    /**
-     * Get the item in the list of loaded items at the provided index.
-     *
-     * @param index Index in the loaded item list. Must be >= 0, and &lt; {@link #size()}
-     * @return The item at the passed index, or null if a null placeholder is at the specified
-     *         position.
-     *
-     * @see #size()
-     */
-    @Override
-    @Nullable
-    public T get(int index) {
-        T item = mStorage.get(index);
-        if (item != null) {
-            mLastItem = item;
-        }
-        return item;
-    }
-
-    /**
-     * Load adjacent items to passed index.
-     *
-     * @param index Index at which to load.
-     */
-    public void loadAround(int index) {
-        mLastLoad = index + getPositionOffset();
-        loadAroundInternal(index);
-
-        mLowestIndexAccessed = Math.min(mLowestIndexAccessed, index);
-        mHighestIndexAccessed = Math.max(mHighestIndexAccessed, index);
-
-        /*
-         * mLowestIndexAccessed / mHighestIndexAccessed have been updated, so check if we need to
-         * dispatch boundary callbacks. Boundary callbacks are deferred until last items are loaded,
-         * and accesses happen near the boundaries.
-         *
-         * Note: we post here, since RecyclerView may want to add items in response, and this
-         * call occurs in PagedListAdapter bind.
-         */
-        tryDispatchBoundaryCallbacks(true);
-    }
-
-    // Creation thread for initial synchronous load, otherwise main thread
-    // Safe to access main thread only state - no other thread has reference during construction
-    @AnyThread
-    void deferBoundaryCallbacks(final boolean deferEmpty,
-            final boolean deferBegin, final boolean deferEnd) {
-        if (mBoundaryCallback == null) {
-            throw new IllegalStateException("Computing boundary");
-        }
-
-        /*
-         * If lowest/highest haven't been initialized, set them to storage size,
-         * since placeholders must already be computed by this point.
-         *
-         * This is just a minor optimization so that BoundaryCallback callbacks are sent immediately
-         * if the initial load size is smaller than the prefetch window (see
-         * TiledPagedListTest#boundaryCallback_immediate())
-         */
-        if (mLowestIndexAccessed == Integer.MAX_VALUE) {
-            mLowestIndexAccessed = mStorage.size();
-        }
-        if (mHighestIndexAccessed == Integer.MIN_VALUE) {
-            mHighestIndexAccessed = 0;
-        }
-
-        if (deferEmpty || deferBegin || deferEnd) {
-            // Post to the main thread, since we may be on creation thread currently
-            mMainThreadExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    // on is dispatched immediately, since items won't be accessed
-                    //noinspection ConstantConditions
-                    if (deferEmpty) {
-                        mBoundaryCallback.onZeroItemsLoaded();
-                    }
-
-                    // for other callbacks, mark deferred, and only dispatch if loadAround
-                    // has been called near to the position
-                    if (deferBegin) {
-                        mBoundaryCallbackBeginDeferred = true;
-                    }
-                    if (deferEnd) {
-                        mBoundaryCallbackEndDeferred = true;
-                    }
-                    tryDispatchBoundaryCallbacks(false);
-                }
-            });
-        }
-    }
-
-    /**
-     * Call this when mLowest/HighestIndexAccessed are changed, or
-     * mBoundaryCallbackBegin/EndDeferred is set.
-     */
-    private void tryDispatchBoundaryCallbacks(boolean post) {
-        final boolean dispatchBegin = mBoundaryCallbackBeginDeferred
-                && mLowestIndexAccessed <= mConfig.prefetchDistance;
-        final boolean dispatchEnd = mBoundaryCallbackEndDeferred
-                && mHighestIndexAccessed >= size() - mConfig.prefetchDistance;
-
-        if (!dispatchBegin && !dispatchEnd) {
-            return;
-        }
-
-        if (dispatchBegin) {
-            mBoundaryCallbackBeginDeferred = false;
-        }
-        if (dispatchEnd) {
-            mBoundaryCallbackEndDeferred = false;
-        }
-        if (post) {
-            mMainThreadExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    dispatchBoundaryCallbacks(dispatchBegin, dispatchEnd);
-                }
-            });
-        } else {
-            dispatchBoundaryCallbacks(dispatchBegin, dispatchEnd);
-        }
-    }
-
-    private void dispatchBoundaryCallbacks(boolean begin, boolean end) {
-        // safe to deref mBoundaryCallback here, since we only defer if mBoundaryCallback present
-        if (begin) {
-            //noinspection ConstantConditions
-            mBoundaryCallback.onItemAtFrontLoaded(mStorage.getFirstLoadedItem());
-        }
-        if (end) {
-            //noinspection ConstantConditions
-            mBoundaryCallback.onItemAtEndLoaded(mStorage.getLastLoadedItem());
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    void offsetBoundaryAccessIndices(int offset) {
-        mLowestIndexAccessed += offset;
-        mHighestIndexAccessed += offset;
-    }
-
-    /**
-     * Returns size of the list, including any not-yet-loaded null padding.
-     *
-     * @return Current total size of the list.
-     */
-    @Override
-    public int size() {
-        return mStorage.size();
-    }
-
-    /**
-     * Returns whether the list is immutable. Immutable lists may not become mutable again, and may
-     * safely be accessed from any thread.
-     *
-     * @return True if the PagedList is immutable.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public boolean isImmutable() {
-        return isDetached();
-    }
-
-    /**
-     * Returns an immutable snapshot of the PagedList. If this PagedList is already
-     * immutable, it will be returned.
-     *
-     * @return Immutable snapshot of PagedList data.
-     */
-    @SuppressWarnings("WeakerAccess")
-    @NonNull
-    public List<T> snapshot() {
-        if (isImmutable()) {
-            return this;
-        }
-        return new SnapshotPagedList<>(this);
-    }
-
-    abstract boolean isContiguous();
-
-    /**
-     * Return the Config used to construct this PagedList.
-     *
-     * @return the Config of this PagedList
-     */
-    @NonNull
-    public Config getConfig() {
-        return mConfig;
-    }
-
-    /**
-     * Return the key for the position passed most recently to {@link #loadAround(int)}.
-     * <p>
-     * When a PagedList is invalidated, you can pass the key returned by this function to initialize
-     * the next PagedList. This ensures (depending on load times) that the next PagedList that
-     * arrives will have data that overlaps. If you use {@link LivePagedListBuilder}, it will do
-     * this for you.
-     *
-     * @return Key of position most recently passed to {@link #loadAround(int)}.
-     */
-    @Nullable
-    public abstract Object getLastKey();
-
-    /**
-     * True if the PagedList has detached the DataSource it was loading from, and will no longer
-     * load new data.
-     *
-     * @return True if the data source is detached.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public boolean isDetached() {
-        return mDetached.get();
-    }
-
-    /**
-     * Detach the PagedList from its DataSource, and attempt to load no more data.
-     * <p>
-     * This is called automatically when a DataSource load returns <code>null</code>, which is a
-     * signal to stop loading. The PagedList will continue to present existing data, but will not
-     * initiate new loads.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void detach() {
-        mDetached.set(true);
-    }
-
-    /**
-     * Position offset of the data in the list.
-     * <p>
-     * If data is supplied by a {@link PositionalDataSource}, the item returned from
-     * <code>get(i)</code> has a position of <code>i + getPositionOffset()</code>.
-     * <p>
-     * If the DataSource is a {@link ItemKeyedDataSource} or {@link PageKeyedDataSource}, it
-     * doesn't use positions, returns 0.
-     */
-    public int getPositionOffset() {
-        return mStorage.getPositionOffset();
-    }
-
-    /**
-     * Adds a callback, and issues updates since the previousSnapshot was created.
-     * <p>
-     * If previousSnapshot is passed, the callback will also immediately be dispatched any
-     * differences between the previous snapshot, and the current state. For example, if the
-     * previousSnapshot was of 5 nulls, 10 items, 5 nulls, and the current state was 5 nulls,
-     * 12 items, 3 nulls, the callback would immediately receive a call of
-     * <code>onChanged(14, 2)</code>.
-     * <p>
-     * This allows an observer that's currently presenting a snapshot to catch up to the most recent
-     * version, including any changes that may have been made.
-     * <p>
-     * The callback is internally held as weak reference, so PagedList doesn't hold a strong
-     * reference to its observer, such as a {@link PagedListAdapter}. If an adapter were held with a
-     * strong reference, it would be necessary to clear its PagedList observer before it could be
-     * GC'd.
-     *
-     * @param previousSnapshot Snapshot previously captured from this List, or null.
-     * @param callback Callback to dispatch to.
-     *
-     * @see #removeWeakCallback(Callback)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void addWeakCallback(@Nullable List<T> previousSnapshot, @NonNull Callback callback) {
-        if (previousSnapshot != null && previousSnapshot != this) {
-
-            if (previousSnapshot.isEmpty()) {
-                if (!mStorage.isEmpty()) {
-                    // If snapshot is empty, diff is trivial - just notify number new items.
-                    // Note: occurs in async init, when snapshot taken before init page arrives
-                    callback.onInserted(0, mStorage.size());
-                }
-            } else {
-                PagedList<T> storageSnapshot = (PagedList<T>) previousSnapshot;
-
-                //noinspection unchecked
-                dispatchUpdatesSinceSnapshot(storageSnapshot, callback);
-            }
-        }
-
-        // first, clean up any empty weak refs
-        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            Callback currentCallback = mCallbacks.get(i).get();
-            if (currentCallback == null) {
-                mCallbacks.remove(i);
-            }
-        }
-
-        // then add the new one
-        mCallbacks.add(new WeakReference<>(callback));
-    }
-    /**
-     * Removes a previously added callback.
-     *
-     * @param callback Callback, previously added.
-     * @see #addWeakCallback(List, Callback)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void removeWeakCallback(@NonNull Callback callback) {
-        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-            Callback currentCallback = mCallbacks.get(i).get();
-            if (currentCallback == null || currentCallback == callback) {
-                // found callback, or empty weak ref
-                mCallbacks.remove(i);
-            }
-        }
-    }
-
-    void notifyInserted(int position, int count) {
-        if (count != 0) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                Callback callback = mCallbacks.get(i).get();
-                if (callback != null) {
-                    callback.onInserted(position, count);
-                }
-            }
-        }
-    }
-
-    void notifyChanged(int position, int count) {
-        if (count != 0) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                Callback callback = mCallbacks.get(i).get();
-
-                if (callback != null) {
-                    callback.onChanged(position, count);
-                }
-            }
-        }
-    }
-
-
-
-    /**
-     * Dispatch updates since the non-empty snapshot was taken.
-     *
-     * @param snapshot Non-empty snapshot.
-     * @param callback Callback for updates that have occurred since snapshot.
-     */
-    abstract void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> snapshot,
-            @NonNull Callback callback);
-
-    abstract void loadAroundInternal(int index);
-
-    /**
-     * Callback signaling when content is loaded into the list.
-     * <p>
-     * Can be used to listen to items being paged in and out. These calls will be dispatched on
-     * the executor defined by {@link Builder#setMainThreadExecutor(Executor)}, which defaults to
-     * the main/UI thread.
-     */
-    public abstract static class Callback {
-        /**
-         * Called when null padding items have been loaded to signal newly available data, or when
-         * data that hasn't been used in a while has been dropped, and swapped back to null.
-         *
-         * @param position Position of first newly loaded items, out of total number of items
-         *                 (including padded nulls).
-         * @param count    Number of items loaded.
-         */
-        public abstract void onChanged(int position, int count);
-
-        /**
-         * Called when new items have been loaded at the end or beginning of the list.
-         *
-         * @param position Position of the first newly loaded item (in practice, either
-         *                 <code>0</code> or <code>size - 1</code>.
-         * @param count    Number of items loaded.
-         */
-        public abstract void onInserted(int position, int count);
-
-        /**
-         * Called when items have been removed at the end or beginning of the list, and have not
-         * been replaced by padded nulls.
-         *
-         * @param position Position of the first newly loaded item (in practice, either
-         *                 <code>0</code> or <code>size - 1</code>.
-         * @param count    Number of items loaded.
-         */
-        @SuppressWarnings("unused")
-        public abstract void onRemoved(int position, int count);
-    }
-
-    /**
-     * Configures how a PagedList loads content from its DataSource.
-     * <p>
-     * Use a Config {@link Builder} to construct and define custom loading behavior, such as
-     * {@link Builder#setPageSize(int)}, which defines number of items loaded at a time}.
-     */
-    public static class Config {
-        /**
-         * Size of each page loaded by the PagedList.
-         */
-        public final int pageSize;
-
-        /**
-         * Prefetch distance which defines how far ahead to load.
-         * <p>
-         * If this value is set to 50, the paged list will attempt to load 50 items in advance of
-         * data that's already been accessed.
-         *
-         * @see PagedList#loadAround(int)
-         */
-        @SuppressWarnings("WeakerAccess")
-        public final int prefetchDistance;
-
-        /**
-         * Defines whether the PagedList may display null placeholders, if the DataSource provides
-         * them.
-         */
-        @SuppressWarnings("WeakerAccess")
-        public final boolean enablePlaceholders;
-
-        /**
-         * Size hint for initial load of PagedList, often larger than a regular page.
-         */
-        @SuppressWarnings("WeakerAccess")
-        public final int initialLoadSizeHint;
-
-        private Config(int pageSize, int prefetchDistance,
-                boolean enablePlaceholders, int initialLoadSizeHint) {
-            this.pageSize = pageSize;
-            this.prefetchDistance = prefetchDistance;
-            this.enablePlaceholders = enablePlaceholders;
-            this.initialLoadSizeHint = initialLoadSizeHint;
-        }
-
-        /**
-         * Builder class for {@link Config}.
-         * <p>
-         * You must at minimum specify page size with {@link #setPageSize(int)}.
-         */
-        public static class Builder {
-            private int mPageSize = -1;
-            private int mPrefetchDistance = -1;
-            private int mInitialLoadSizeHint = -1;
-            private boolean mEnablePlaceholders = true;
-
-            /**
-             * Defines the number of items loaded at once from the DataSource.
-             * <p>
-             * Should be several times the number of visible items onscreen.
-             * <p>
-             * Configuring your page size depends on how your data is being loaded and used. Smaller
-             * page sizes improve memory usage, latency, and avoid GC churn. Larger pages generally
-             * improve loading throughput, to a point
-             * (avoid loading more than 2MB from SQLite at once, since it incurs extra cost).
-             * <p>
-             * If you're loading data for very large, social-media style cards that take up most of
-             * a screen, and your database isn't a bottleneck, 10-20 may make sense. If you're
-             * displaying dozens of items in a tiled grid, which can present items during a scroll
-             * much more quickly, consider closer to 100.
-             *
-             * @param pageSize Number of items loaded at once from the DataSource.
-             * @return this
-             */
-            public Builder setPageSize(int pageSize) {
-                this.mPageSize = pageSize;
-                return this;
-            }
-
-            /**
-             * Defines how far from the edge of loaded content an access must be to trigger further
-             * loading.
-             * <p>
-             * Should be several times the number of visible items onscreen.
-             * <p>
-             * If not set, defaults to page size.
-             * <p>
-             * A value of 0 indicates that no list items will be loaded until they are specifically
-             * requested. This is generally not recommended, so that users don't observe a
-             * placeholder item (with placeholders) or end of list (without) while scrolling.
-             *
-             * @param prefetchDistance Distance the PagedList should prefetch.
-             * @return this
-             */
-            public Builder setPrefetchDistance(int prefetchDistance) {
-                this.mPrefetchDistance = prefetchDistance;
-                return this;
-            }
-
-            /**
-             * Pass false to disable null placeholders in PagedLists using this Config.
-             * <p>
-             * If not set, defaults to true.
-             * <p>
-             * A PagedList will present null placeholders for not-yet-loaded content if two
-             * conditions are met:
-             * <p>
-             * 1) Its DataSource can count all unloaded items (so that the number of nulls to
-             * present is known).
-             * <p>
-             * 2) placeholders are not disabled on the Config.
-             * <p>
-             * Call {@code setEnablePlaceholders(false)} to ensure the receiver of the PagedList
-             * (often a {@link PagedListAdapter}) doesn't need to account for null items.
-             * <p>
-             * If placeholders are disabled, not-yet-loaded content will not be present in the list.
-             * Paging will still occur, but as items are loaded or removed, they will be signaled
-             * as inserts to the {@link PagedList.Callback}.
-             * {@link PagedList.Callback#onChanged(int, int)} will not be issued as part of loading,
-             * though a {@link PagedListAdapter} may still receive change events as a result of
-             * PagedList diffing.
-             *
-             * @param enablePlaceholders False if null placeholders should be disabled.
-             * @return this
-             */
-            @SuppressWarnings("SameParameterValue")
-            public Builder setEnablePlaceholders(boolean enablePlaceholders) {
-                this.mEnablePlaceholders = enablePlaceholders;
-                return this;
-            }
-
-            /**
-             * Defines how many items to load when first load occurs.
-             * <p>
-             * This value is typically larger than page size, so on first load data there's a large
-             * enough range of content loaded to cover small scrolls.
-             * <p>
-             * When using a {@link PositionalDataSource}, the initial load size will be coerced to
-             * an integer multiple of pageSize, to enable efficient tiling.
-             * <p>
-             * If not set, defaults to three times page size.
-             *
-             * @param initialLoadSizeHint Number of items to load while initializing the PagedList.
-             * @return this
-             */
-            @SuppressWarnings("WeakerAccess")
-            public Builder setInitialLoadSizeHint(int initialLoadSizeHint) {
-                this.mInitialLoadSizeHint = initialLoadSizeHint;
-                return this;
-            }
-
-            /**
-             * Creates a {@link Config} with the given parameters.
-             *
-             * @return A new Config.
-             */
-            public Config build() {
-                if (mPageSize < 1) {
-                    throw new IllegalArgumentException("Page size must be a positive number");
-                }
-                if (mPrefetchDistance < 0) {
-                    mPrefetchDistance = mPageSize;
-                }
-                if (mInitialLoadSizeHint < 0) {
-                    mInitialLoadSizeHint = mPageSize * 3;
-                }
-                if (!mEnablePlaceholders && mPrefetchDistance == 0) {
-                    throw new IllegalArgumentException("Placeholders and prefetch are the only ways"
-                            + " to trigger loading of more data in the PagedList, so either"
-                            + " placeholders must be enabled, or prefetch distance must be > 0.");
-                }
-
-                return new Config(mPageSize, mPrefetchDistance,
-                        mEnablePlaceholders, mInitialLoadSizeHint);
-            }
-        }
-    }
-
-    /**
-     * Signals when a PagedList has reached the end of available data.
-     * <p>
-     * When local storage is a cache of network data, it's common to set up a streaming pipeline:
-     * Network data is paged into the database, database is paged into UI. Paging from the database
-     * to UI can be done with a {@code LiveData<PagedList>}, but it's still necessary to know when
-     * to trigger network loads.
-     * <p>
-     * BoundaryCallback does this signaling - when a DataSource runs out of data at the end of
-     * the list, {@link #onItemAtEndLoaded(Object)} is called, and you can start an async network
-     * load that will write the result directly to the database. Because the database is being
-     * observed, the UI bound to the {@code LiveData<PagedList>} will update automatically to
-     * account for the new items.
-     * <p>
-     * Note that a BoundaryCallback instance shared across multiple PagedLists (e.g. when passed to
-     * {@link LivePagedListBuilder#setBoundaryCallback}), the callbacks may be issued multiple
-     * times. If for example {@link #onItemAtEndLoaded(Object)} triggers a network load, it should
-     * avoid triggering it again while the load is ongoing.
-     * <p>
-     * BoundaryCallback only passes the item at front or end of the list. Number of items is not
-     * passed, since it may not be fully computed by the DataSource if placeholders are not
-     * supplied. Keys are not known because the BoundaryCallback is independent of the
-     * DataSource-specific keys, which may be different for local vs remote storage.
-     * <p>
-     * The database + network Repository in the
-     * <a href="https://github.com/googlesamples/android-architecture-components/blob/master/PagingWithNetworkSample/README.md">PagingWithNetworkSample</a>
-     * shows how to implement a network BoundaryCallback using
-     * <a href="https://square.github.io/retrofit/">Retrofit</a>, while
-     * handling swipe-to-refresh, network errors, and retry.
-     *
-     * @param <T> Type loaded by the PagedList.
-     */
-    @MainThread
-    public abstract static class BoundaryCallback<T> {
-        /**
-         * Called when zero items are returned from an initial load of the PagedList's data source.
-         */
-        public void onZeroItemsLoaded() {}
-
-        /**
-         * Called when the item at the front of the PagedList has been loaded, and access has
-         * occurred within {@link Config#prefetchDistance} of it.
-         * <p>
-         * No more data will be prepended to the PagedList before this item.
-         *
-         * @param itemAtFront The first item of PagedList
-         */
-        public void onItemAtFrontLoaded(@NonNull T itemAtFront) {}
-
-        /**
-         * Called when the item at the end of the PagedList has been loaded, and access has
-         * occurred within {@link Config#prefetchDistance} of it.
-         * <p>
-         * No more data will be appended to the PagedList after this item.
-         *
-         * @param itemAtEnd The first item of PagedList
-         */
-        public void onItemAtEndLoaded(@NonNull T itemAtEnd) {}
-    }
-}
diff --git a/android/arch/paging/PagedListAdapter.java b/android/arch/paging/PagedListAdapter.java
deleted file mode 100644
index be23271..0000000
--- a/android/arch/paging/PagedListAdapter.java
+++ /dev/null
@@ -1,193 +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.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.recyclerview.extensions.DiffCallback;
-import android.support.v7.recyclerview.extensions.ListAdapterConfig;
-import android.support.v7.recyclerview.extensions.ListAdapterHelper;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting paged data from
- * {@link PagedList}s in a {@link RecyclerView}.
- * <p>
- * This class is a convenience wrapper around PagedListAdapterHelper that implements common default
- * behavior for item counting, and listening to PagedList update callbacks.
- * <p>
- * While using a LiveData&lt;PagedList> is an easy way to provide data to the adapter, it isn't
- * required - you can use {@link #setList(PagedList)} when new lists are available.
- * <p>
- * PagedListAdapter listens to PagedList loading callbacks as pages are loaded, and uses DiffUtil on
- * a background thread to compute fine grained updates as new PagedLists are received.
- * <p>
- * Handles both the internal paging of the list as more data is loaded, and updates in the form of
- * new PagedLists.
- * <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")
- *     public abstract DataSource.Factory&lt;Integer, User> usersByLastName();
- * }
- *
- * class MyViewModel extends ViewModel {
- *     public final LiveData&lt;PagedList&lt;User>> usersList;
- *     public MyViewModel(UserDao userDao) {
- *         usersList = new LivePagedListBuilder&lt;>(
- *                 userDao.usersByLastName(), /* page size {@literal *}/ 20).build();
- *     }
- * }
- *
- * class MyActivity extends AppCompatActivity {
- *     {@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();
- *         viewModel.usersList.observe(this, pagedList -> adapter.setList(pagedList));
- *         recyclerView.setAdapter(adapter);
- *     }
- * }
- *
- * class UserAdapter extends PagedListAdapter&lt;User, UserViewHolder> {
- *     public UserAdapter() {
- *         super(DIFF_CALLBACK);
- *     }
- *     {@literal @}Override
- *     public void onBindViewHolder(UserViewHolder holder, int position) {
- *         User user = getItem(position);
- *         if (user != null) {
- *             holder.bindTo(user);
- *         } else {
- *             // Null defines a placeholder item - PagedListAdapter will automatically invalidate
- *             // this row when the actual object is loaded from the database
- *             holder.clear();
- *         }
- *     }
- *     public static final DiffCallback&lt;User> DIFF_CALLBACK = new DiffCallback&lt;User>() {
- *         {@literal @}Override
- *         public boolean areItemsTheSame(
- *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *             // User properties may have changed if reloaded from the DB, but ID is fixed
- *             return oldUser.getId() == newUser.getId();
- *         }
- *         {@literal @}Override
- *         public boolean areContentsTheSame(
- *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *             // NOTE: if you use equals, your object must properly override Object#equals()
- *             // Incorrectly returning false here will result in too many animations.
- *             return oldUser.equals(newUser);
- *         }
- *     }
- * }</pre>
- *
- * Advanced users that wish for more control over adapter behavior, or to provide a specific base
- * class should refer to {@link PagedListAdapterHelper}, which provides the mapping from paging
- * events to adapter-friendly callbacks.
- *
- * @param <T> Type of the PagedLists this helper will receive.
- * @param <VH> A class that extends ViewHolder that will be used by the adapter.
- */
-public abstract class PagedListAdapter<T, VH extends RecyclerView.ViewHolder>
-        extends RecyclerView.Adapter<VH> {
-    private final PagedListAdapterHelper<T> mHelper;
-    private final PagedListAdapterHelper.PagedListListener<T> mListener =
-            new PagedListAdapterHelper.PagedListListener<T>() {
-        @Override
-        public void onCurrentListChanged(@Nullable PagedList<T> currentList) {
-            PagedListAdapter.this.onCurrentListChanged(currentList);
-        }
-    };
-
-    /**
-     * Creates a PagedListAdapter with default threading and
-     * {@link android.support.v7.util.ListUpdateCallback}.
-     *
-     * Convenience for {@link #PagedListAdapter(ListAdapterConfig)}, which uses default threading
-     * behavior.
-     *
-     * @param diffCallback The {@link DiffCallback} instance to compare items in the list.
-     */
-    protected PagedListAdapter(@NonNull DiffCallback<T> diffCallback) {
-        mHelper = new PagedListAdapterHelper<>(this, diffCallback);
-        mHelper.mListener = mListener;
-    }
-
-    @SuppressWarnings("unused, WeakerAccess")
-    protected PagedListAdapter(@NonNull ListAdapterConfig<T> config) {
-        mHelper = new PagedListAdapterHelper<>(new ListAdapterHelper.AdapterCallback(this), config);
-        mHelper.mListener = mListener;
-    }
-
-    /**
-     * Set the new list to be displayed.
-     * <p>
-     * If a list is already being displayed, a diff will be computed on a background thread, which
-     * will dispatch Adapter.notifyItem events on the main thread.
-     *
-     * @param pagedList The new list to be displayed.
-     */
-    public void setList(PagedList<T> pagedList) {
-        mHelper.setList(pagedList);
-    }
-
-    @Nullable
-    protected T getItem(int position) {
-        return mHelper.getItem(position);
-    }
-
-    @Override
-    public int getItemCount() {
-        return mHelper.getItemCount();
-    }
-
-    /**
-     * Returns the list currently being displayed by the Adapter.
-     * <p>
-     * This is not necessarily the most recent list passed to {@link #setList(PagedList)}, because a
-     * diff is computed asynchronously between the new list and the current list before updating the
-     * currentList value.
-     *
-     * @return The list currently being displayed.
-     */
-    @Nullable
-    public PagedList<T> getCurrentList() {
-        return mHelper.getCurrentList();
-    }
-
-    /**
-     * Called when the current PagedList is updated.
-     * <p>
-     * This may be dispatched as part of {@link #setList(PagedList)} if a background diff isn't
-     * needed (such as when the first list is passed, or the list is cleared). In either case,
-     * PagedListAdapter will simply call
-     * {@link #notifyItemRangeInserted(int, int) notifyItemRangeInserted/Removed(0, mPreviousSize)}.
-     * <p>
-     * This method will <em>not</em>be called when the Adapter switches from presenting a PagedList
-     * to a snapshot version of the PagedList during a diff. This means you cannot observe each
-     * PagedList via this method.
-     *
-     * @param currentList new PagedList being displayed, may be null.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void onCurrentListChanged(@Nullable PagedList<T> currentList) {
-    }
-}
diff --git a/android/arch/paging/PagedListAdapterHelper.java b/android/arch/paging/PagedListAdapterHelper.java
deleted file mode 100644
index ba8ffab..0000000
--- a/android/arch/paging/PagedListAdapterHelper.java
+++ /dev/null
@@ -1,345 +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.arch.paging;
-
-import android.arch.lifecycle.LiveData;
-import android.support.annotation.Nullable;
-import android.support.v7.recyclerview.extensions.DiffCallback;
-import android.support.v7.recyclerview.extensions.ListAdapterConfig;
-import android.support.v7.recyclerview.extensions.ListAdapterHelper;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.util.ListUpdateCallback;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * Helper object for mapping a {@link PagedList} into a
- * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}.
- * <p>
- * For simplicity, the {@link PagedListAdapter} wrapper class can often be used instead of the
- * helper directly. This helper class is exposed for complex cases, and where overriding an adapter
- * base class to support paging isn't convenient.
- * <p>
- * Both the internal paging of the list as more data is loaded, and updates in the form of new
- * PagedLists.
- * <p>
- * The PagedListAdapterHelper can be bound to a {@link LiveData} of PagedList and present the data
- * simply for an adapter. It listens to PagedList loading callbacks, and uses DiffUtil on a
- * background thread to compute updates as new PagedLists are received.
- * <p>
- * It provides a simple list-like API with {@link #getItem(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")
- *     public abstract DataSource.Factory&lt;Integer, User> usersByLastName();
- * }
- *
- * class MyViewModel extends ViewModel {
- *     public final LiveData&lt;PagedList&lt;User>> usersList;
- *     public MyViewModel(UserDao userDao) {
- *         usersList = new LivePagedListBuilder&lt;>(
- *                 userDao.usersByLastName(), /* page size {@literal *}/ 20).build();
- *     }
- * }
- *
- * class MyActivity extends AppCompatActivity {
- *     {@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);
- *         final UserAdapter&lt;User> adapter = new UserAdapter();
- *         viewModel.usersList.observe(this, pagedList -> adapter.setList(pagedList));
- *         recyclerView.setAdapter(adapter);
- *     }
- * }
- *
- * class UserAdapter extends RecyclerView.Adapter&lt;UserViewHolder> {
- *     private final PagedListAdapterHelper&lt;User> mHelper
- *             = new PagedListAdapterHelper(this, DIFF_CALLBACK);
- *     {@literal @}Override
- *     public int getItemCount() {
- *         return mHelper.getItemCount();
- *     }
- *     public void setList(PagedList&lt;User> pagedList) {
- *         mHelper.setList(pagedList);
- *     }
- *     {@literal @}Override
- *     public void onBindViewHolder(UserViewHolder holder, int position) {
- *         User user = mHelper.getItem(position);
- *         if (user != null) {
- *             holder.bindTo(user);
- *         } else {
- *             // Null defines a placeholder item - PagedListAdapterHelper will automatically
- *             // invalidate this row when the actual object is loaded from the database
- *             holder.clear();
- *         }
- *     }
- *     public static final DiffCallback&lt;User> DIFF_CALLBACK = new DiffCallback&lt;User>() {
- *          {@literal @}Override
- *          public boolean areItemsTheSame(
- *                  {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *              // User properties may have changed if reloaded from the DB, but ID is fixed
- *              return oldUser.getId() == newUser.getId();
- *          }
- *          {@literal @}Override
- *          public boolean areContentsTheSame(
- *                  {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *              // NOTE: if you use equals, your object must properly override Object#equals()
- *              // Incorrectly returning false here will result in too many animations.
- *              return oldUser.equals(newUser);
- *          }
- *      }
- * }</pre>
- *
- * @param <T> Type of the PagedLists this helper will receive.
- */
-public class PagedListAdapterHelper<T> {
-    // updateCallback notifications must only be notified *after* new data and item count are stored
-    // this ensures Adapter#notifyItemRangeInserted etc are accessing the new data
-    private final ListUpdateCallback mUpdateCallback;
-    private final ListAdapterConfig<T> mConfig;
-
-    // TODO: REAL API
-    interface PagedListListener<T> {
-        void onCurrentListChanged(@Nullable PagedList<T> currentList);
-    }
-
-    @Nullable
-    PagedListListener<T> mListener;
-
-    private boolean mIsContiguous;
-
-    private PagedList<T> mPagedList;
-    private PagedList<T> mSnapshot;
-
-    // Max generation of currently scheduled runnable
-    private int mMaxScheduledGeneration;
-
-    /**
-     * Convenience for {@code PagedListAdapterHelper(new ListAdapterHelper.AdapterCallback(adapter),
-     * new ListAdapterConfig.Builder<T>().setDiffCallback(diffCallback).build());
-     *
-     * @param adapter Adapter that will receive update signals.
-     * @param diffCallback The {@link DiffCallback } instance to compare items in the list.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public PagedListAdapterHelper(RecyclerView.Adapter adapter, DiffCallback<T> diffCallback) {
-        mUpdateCallback = new ListAdapterHelper.AdapterCallback(adapter);
-        mConfig = new ListAdapterConfig.Builder<T>().setDiffCallback(diffCallback).build();
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    public PagedListAdapterHelper(ListUpdateCallback listUpdateCallback,
-            ListAdapterConfig<T> config) {
-        mUpdateCallback = listUpdateCallback;
-        mConfig = config;
-    }
-
-    private PagedList.Callback mPagedListCallback = new PagedList.Callback() {
-        @Override
-        public void onInserted(int position, int count) {
-            mUpdateCallback.onInserted(position, count);
-        }
-
-        @Override
-        public void onRemoved(int position, int count) {
-            mUpdateCallback.onRemoved(position, count);
-        }
-
-        @Override
-        public void onChanged(int position, int count) {
-            // NOTE: pass a null payload to convey null -> item
-            mUpdateCallback.onChanged(position, count, null);
-        }
-    };
-
-    /**
-     * Get the item from the current PagedList at the specified index.
-     * <p>
-     * Note that this operates on both loaded items and null padding within the PagedList.
-     *
-     * @param index Index of item to get, must be >= 0, and &lt; {@link #getItemCount()}.
-     * @return The item, or null, if a null placeholder is at the specified position.
-     */
-    @SuppressWarnings("WeakerAccess")
-    @Nullable
-    public T getItem(int index) {
-        if (mPagedList == null) {
-            if (mSnapshot == null) {
-                throw new IndexOutOfBoundsException(
-                        "Item count is zero, getItem() call is invalid");
-            } else {
-                return mSnapshot.get(index);
-            }
-        }
-
-        mPagedList.loadAround(index);
-        return mPagedList.get(index);
-    }
-
-    /**
-     * Get the number of items currently presented by this AdapterHelper. This value can be directly
-     * returned to {@link RecyclerView.Adapter#getItemCount()}.
-     *
-     * @return Number of items being presented.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public int getItemCount() {
-        if (mPagedList != null) {
-            return mPagedList.size();
-        }
-
-        return mSnapshot == null ? 0 : mSnapshot.size();
-    }
-
-    /**
-     * Pass a new PagedList to the AdapterHelper.
-     * <p>
-     * If a PagedList is already present, a diff will be computed asynchronously on a background
-     * thread. When the diff is computed, it will be applied (dispatched to the
-     * {@link ListUpdateCallback}), and the new PagedList will be swapped in.
-     *
-     * @param pagedList The new PagedList.
-     */
-    public void setList(final PagedList<T> pagedList) {
-        if (pagedList != null) {
-            if (mPagedList == null && mSnapshot == null) {
-                mIsContiguous = pagedList.isContiguous();
-            } else {
-                if (pagedList.isContiguous() != mIsContiguous) {
-                    throw new IllegalArgumentException("AdapterHelper cannot handle both contiguous"
-                            + " and non-contiguous lists.");
-                }
-            }
-        }
-
-        if (pagedList == mPagedList) {
-            // nothing to do
-            return;
-        }
-
-        // incrementing generation means any currently-running diffs are discarded when they finish
-        final int runGeneration = ++mMaxScheduledGeneration;
-
-        if (pagedList == null) {
-            int removedCount = getItemCount();
-            if (mPagedList != null) {
-                mPagedList.removeWeakCallback(mPagedListCallback);
-                mPagedList = null;
-            } else if (mSnapshot != null) {
-                mSnapshot = null;
-            }
-            // dispatch update callback after updating mPagedList/mSnapshot
-            mUpdateCallback.onRemoved(0, removedCount);
-            if (mListener != null) {
-                mListener.onCurrentListChanged(null);
-            }
-            return;
-        }
-
-        if (mPagedList == null && mSnapshot == null) {
-            // fast simple first insert
-            mPagedList = pagedList;
-            pagedList.addWeakCallback(null, mPagedListCallback);
-
-            // dispatch update callback after updating mPagedList/mSnapshot
-            mUpdateCallback.onInserted(0, pagedList.size());
-
-            if (mListener != null) {
-                mListener.onCurrentListChanged(pagedList);
-            }
-            return;
-        }
-
-        if (mPagedList != null) {
-            // first update scheduled on this list, so capture mPages as a snapshot, removing
-            // callbacks so we don't have resolve updates against a moving target
-            mPagedList.removeWeakCallback(mPagedListCallback);
-            mSnapshot = (PagedList<T>) mPagedList.snapshot();
-            mPagedList = null;
-        }
-
-        if (mSnapshot == null || mPagedList != null) {
-            throw new IllegalStateException("must be in snapshot state to diff");
-        }
-
-        final PagedList<T> oldSnapshot = mSnapshot;
-        final PagedList<T> newSnapshot = (PagedList<T>) pagedList.snapshot();
-        mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                final DiffUtil.DiffResult result;
-                result = PagedStorageDiffHelper.computeDiff(
-                        oldSnapshot.mStorage,
-                        newSnapshot.mStorage,
-                        mConfig.getDiffCallback());
-
-                mConfig.getMainThreadExecutor().execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mMaxScheduledGeneration == runGeneration) {
-                            latchPagedList(pagedList, newSnapshot, result);
-                        }
-                    }
-                });
-            }
-        });
-    }
-
-    private void latchPagedList(
-            PagedList<T> newList, PagedList<T> diffSnapshot,
-            DiffUtil.DiffResult diffResult) {
-        if (mSnapshot == null || mPagedList != null) {
-            throw new IllegalStateException("must be in snapshot state to apply diff");
-        }
-
-        PagedList<T> previousSnapshot = mSnapshot;
-        mPagedList = newList;
-        mSnapshot = null;
-
-        // dispatch update callback after updating mPagedList/mSnapshot
-        PagedStorageDiffHelper.dispatchDiff(mUpdateCallback,
-                previousSnapshot.mStorage, newList.mStorage, diffResult);
-
-        newList.addWeakCallback(diffSnapshot, mPagedListCallback);
-        if (mListener != null) {
-            mListener.onCurrentListChanged(mPagedList);
-        }
-    }
-
-    /**
-     * Returns the list currently being displayed by the AdapterHelper.
-     * <p>
-     * This is not necessarily the most recent list passed to {@link #setList(PagedList)}, because a
-     * diff is computed asynchronously between the new list and the current list before updating the
-     * currentList value.
-     *
-     * @return The list currently being displayed.
-     */
-    @SuppressWarnings("WeakerAccess")
-    @Nullable
-    public PagedList<T> getCurrentList() {
-        if (mSnapshot != null) {
-            return mSnapshot;
-        }
-        return mPagedList;
-    }
-}
diff --git a/android/arch/paging/PagedStorage.java b/android/arch/paging/PagedStorage.java
deleted file mode 100644
index d4531d3..0000000
--- a/android/arch/paging/PagedStorage.java
+++ /dev/null
@@ -1,451 +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.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.AbstractList;
-import java.util.ArrayList;
-import java.util.List;
-
-final class PagedStorage<T> extends AbstractList<T> {
-    /**
-     * Lists instances are compared (with instance equality) to PLACEHOLDER_LIST to check if an item
-     * in that position is already loading. We use a singleton placeholder list that is distinct
-     * from Collections.EMPTY_LIST for safety.
-     */
-    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
-    private static final List PLACEHOLDER_LIST = new ArrayList();
-
-    // Always set
-    private int mLeadingNullCount;
-    /**
-     * List of pages in storage.
-     *
-     * Two storage modes:
-     *
-     * Contiguous - all content in mPages is valid and loaded, but may return false from isTiled().
-     *     Safe to access any item in any page.
-     *
-     * Non-contiguous - mPages may have nulls or a placeholder page, isTiled() always returns true.
-     *     mPages may have nulls, or placeholder (empty) pages while content is loading.
-     */
-    private final ArrayList<List<T>> mPages;
-    private int mTrailingNullCount;
-
-    private int mPositionOffset;
-    /**
-     * Number of items represented by {@link #mPages}. If tiling is enabled, unloaded items in
-     * {@link #mPages} may be null, but this value still counts them.
-     */
-    private int mStorageCount;
-
-    // If mPageSize > 0, tiling is enabled, 'mPages' may have gaps, and leadingPages is set
-    private int mPageSize;
-
-    private int mNumberPrepended;
-    private int mNumberAppended;
-
-    PagedStorage() {
-        mLeadingNullCount = 0;
-        mPages = new ArrayList<>();
-        mTrailingNullCount = 0;
-        mPositionOffset = 0;
-        mStorageCount = 0;
-        mPageSize = 1;
-        mNumberPrepended = 0;
-        mNumberAppended = 0;
-    }
-
-    PagedStorage(int leadingNulls, List<T> page, int trailingNulls) {
-        this();
-        init(leadingNulls, page, trailingNulls, 0);
-    }
-
-    private PagedStorage(PagedStorage<T> other) {
-        mLeadingNullCount = other.mLeadingNullCount;
-        mPages = new ArrayList<>(other.mPages);
-        mTrailingNullCount = other.mTrailingNullCount;
-        mPositionOffset = other.mPositionOffset;
-        mStorageCount = other.mStorageCount;
-        mPageSize = other.mPageSize;
-        mNumberPrepended = other.mNumberPrepended;
-        mNumberAppended = other.mNumberAppended;
-    }
-
-    PagedStorage<T> snapshot() {
-        return new PagedStorage<>(this);
-    }
-
-    private void init(int leadingNulls, List<T> page, int trailingNulls, int positionOffset) {
-        mLeadingNullCount = leadingNulls;
-        mPages.clear();
-        mPages.add(page);
-        mTrailingNullCount = trailingNulls;
-
-        mPositionOffset = positionOffset;
-        mStorageCount = page.size();
-
-        // initialized as tiled. There may be 3 nulls, 2 items, but we still call this tiled
-        // even if it will break if nulls convert.
-        mPageSize = page.size();
-
-        mNumberPrepended = 0;
-        mNumberAppended = 0;
-    }
-
-    void init(int leadingNulls, @NonNull List<T> page, int trailingNulls, int positionOffset,
-            @NonNull Callback callback) {
-        init(leadingNulls, page, trailingNulls, positionOffset);
-        callback.onInitialized(size());
-    }
-
-    @Override
-    public T get(int i) {
-        if (i < 0 || i >= size()) {
-            throw new IndexOutOfBoundsException("Index: " + i + ", Size: " + size());
-        }
-
-        // is it definitely outside 'mPages'?
-        int localIndex = i - mLeadingNullCount;
-        if (localIndex < 0 || localIndex >= mStorageCount) {
-            return null;
-        }
-
-        int localPageIndex;
-        int pageInternalIndex;
-
-        if (isTiled()) {
-            // it's inside mPages, and we're tiled. Jump to correct tile.
-            localPageIndex = localIndex / mPageSize;
-            pageInternalIndex = localIndex % mPageSize;
-        } else {
-            // it's inside mPages, but page sizes aren't regular. Walk to correct tile.
-            // Pages can only be null while tiled, so accessing page count is safe.
-            pageInternalIndex = localIndex;
-            final int localPageCount = mPages.size();
-            for (localPageIndex = 0; localPageIndex < localPageCount; localPageIndex++) {
-                int pageSize = mPages.get(localPageIndex).size();
-                if (pageSize > pageInternalIndex) {
-                    // stop, found the page
-                    break;
-                }
-                pageInternalIndex -= pageSize;
-            }
-        }
-
-        List<T> page = mPages.get(localPageIndex);
-        if (page == null || page.size() == 0) {
-            // can only occur in tiled case, with untouched inner/placeholder pages
-            return null;
-        }
-        return page.get(pageInternalIndex);
-    }
-
-    /**
-     * Returns true if all pages are the same size, except for the last, which may be smaller
-     */
-    boolean isTiled() {
-        return mPageSize > 0;
-    }
-
-    int getLeadingNullCount() {
-        return mLeadingNullCount;
-    }
-
-    int getTrailingNullCount() {
-        return mTrailingNullCount;
-    }
-
-    int getStorageCount() {
-        return mStorageCount;
-    }
-
-    int getNumberAppended() {
-        return mNumberAppended;
-    }
-
-    int getNumberPrepended() {
-        return mNumberPrepended;
-    }
-
-    int getPageCount() {
-        return mPages.size();
-    }
-
-    interface Callback {
-        void onInitialized(int count);
-        void onPagePrepended(int leadingNulls, int changed, int added);
-        void onPageAppended(int endPosition, int changed, int added);
-        void onPagePlaceholderInserted(int pageIndex);
-        void onPageInserted(int start, int count);
-    }
-
-    int getPositionOffset() {
-        return mPositionOffset;
-    }
-
-    @Override
-    public int size() {
-        return mLeadingNullCount + mStorageCount + mTrailingNullCount;
-    }
-
-    int computeLeadingNulls() {
-        int total = mLeadingNullCount;
-        final int pageCount = mPages.size();
-        for (int i = 0; i < pageCount; i++) {
-            List page = mPages.get(i);
-            if (page != null && page != PLACEHOLDER_LIST) {
-                break;
-            }
-            total += mPageSize;
-        }
-        return total;
-    }
-
-    int computeTrailingNulls() {
-        int total = mTrailingNullCount;
-        for (int i = mPages.size() - 1; i >= 0; i--) {
-            List page = mPages.get(i);
-            if (page != null && page != PLACEHOLDER_LIST) {
-                break;
-            }
-            total += mPageSize;
-        }
-        return total;
-    }
-
-    // ---------------- Contiguous API -------------------
-
-    T getFirstLoadedItem() {
-        // safe to access first page's first item here:
-        // If contiguous, mPages can't be empty, can't hold null Pages, and items can't be empty
-        return mPages.get(0).get(0);
-    }
-
-    T getLastLoadedItem() {
-        // safe to access last page's last item here:
-        // If contiguous, mPages can't be empty, can't hold null Pages, and items can't be empty
-        List<T> page = mPages.get(mPages.size() - 1);
-        return page.get(page.size() - 1);
-    }
-
-    void prependPage(@NonNull List<T> page, @NonNull Callback callback) {
-        final int count = page.size();
-        if (count == 0) {
-            // Nothing returned from source, stop loading in this direction
-            return;
-        }
-        if (mPageSize > 0 && count != mPageSize) {
-            if (mPages.size() == 1 && count > mPageSize) {
-                // prepending to a single item - update current page size to that of 'inner' page
-                mPageSize = count;
-            } else {
-                // no longer tiled
-                mPageSize = -1;
-            }
-        }
-
-        mPages.add(0, page);
-        mStorageCount += count;
-
-        final int changedCount = Math.min(mLeadingNullCount, count);
-        final int addedCount = count - changedCount;
-
-        if (changedCount != 0) {
-            mLeadingNullCount -= changedCount;
-        }
-        mPositionOffset -= addedCount;
-        mNumberPrepended += count;
-
-        callback.onPagePrepended(mLeadingNullCount, changedCount, addedCount);
-    }
-
-    void appendPage(@NonNull List<T> page, @NonNull Callback callback) {
-        final int count = page.size();
-        if (count == 0) {
-            // Nothing returned from source, stop loading in this direction
-            return;
-        }
-
-        if (mPageSize > 0) {
-            // if the previous page was smaller than mPageSize,
-            // or if this page is larger than the previous, disable tiling
-            if (mPages.get(mPages.size() - 1).size() != mPageSize
-                    || count > mPageSize) {
-                mPageSize = -1;
-            }
-        }
-
-        mPages.add(page);
-        mStorageCount += count;
-
-        final int changedCount = Math.min(mTrailingNullCount, count);
-        final int addedCount = count - changedCount;
-
-        if (changedCount != 0) {
-            mTrailingNullCount -= changedCount;
-        }
-        mNumberAppended += count;
-        callback.onPageAppended(mLeadingNullCount + mStorageCount - count,
-                changedCount, addedCount);
-    }
-
-    // ------------------ Non-Contiguous API (tiling required) ----------------------
-
-    void initAndSplit(int leadingNulls, @NonNull List<T> multiPageList,
-            int trailingNulls, int positionOffset, int pageSize, @NonNull Callback callback) {
-
-        int pageCount = (multiPageList.size() + (pageSize - 1)) / pageSize;
-        for (int i = 0; i < pageCount; i++) {
-            int beginInclusive = i * pageSize;
-            int endExclusive = Math.min(multiPageList.size(), (i + 1) * pageSize);
-
-            List<T> sublist = multiPageList.subList(beginInclusive, endExclusive);
-
-            if (i == 0) {
-                // Trailing nulls for first page includes other pages in multiPageList
-                int initialTrailingNulls = trailingNulls + multiPageList.size() - sublist.size();
-                init(leadingNulls, sublist, initialTrailingNulls, positionOffset);
-            } else {
-                int insertPosition = leadingNulls + beginInclusive;
-                insertPage(insertPosition, sublist, null);
-            }
-        }
-        callback.onInitialized(size());
-    }
-
-    public void insertPage(int position, @NonNull List<T> page, @Nullable Callback callback) {
-        final int newPageSize = page.size();
-        if (newPageSize != mPageSize) {
-            // differing page size is OK in 2 cases, when the page is being added:
-            // 1) to the end (in which case, ignore new smaller size)
-            // 2) only the last page has been added so far (in which case, adopt new bigger size)
-
-            int size = size();
-            boolean addingLastPage = position == (size - size % mPageSize)
-                    && newPageSize < mPageSize;
-            boolean onlyEndPagePresent = mTrailingNullCount == 0 && mPages.size() == 1
-                    && newPageSize > mPageSize;
-
-            // OK only if existing single page, and it's the last one
-            if (!onlyEndPagePresent && !addingLastPage) {
-                throw new IllegalArgumentException("page introduces incorrect tiling");
-            }
-            if (onlyEndPagePresent) {
-                mPageSize = newPageSize;
-            }
-        }
-
-        int pageIndex = position / mPageSize;
-
-        allocatePageRange(pageIndex, pageIndex);
-
-        int localPageIndex = pageIndex - mLeadingNullCount / mPageSize;
-
-        List<T> oldPage = mPages.get(localPageIndex);
-        if (oldPage != null && oldPage != PLACEHOLDER_LIST) {
-            throw new IllegalArgumentException(
-                    "Invalid position " + position + ": data already loaded");
-        }
-        mPages.set(localPageIndex, page);
-        if (callback != null) {
-            callback.onPageInserted(position, page.size());
-        }
-    }
-
-    private void allocatePageRange(final int minimumPage, final int maximumPage) {
-        int leadingNullPages = mLeadingNullCount / mPageSize;
-
-        if (minimumPage < leadingNullPages) {
-            for (int i = 0; i < leadingNullPages - minimumPage; i++) {
-                mPages.add(0, null);
-            }
-            int newStorageAllocated = (leadingNullPages - minimumPage) * mPageSize;
-            mStorageCount += newStorageAllocated;
-            mLeadingNullCount -= newStorageAllocated;
-
-            leadingNullPages = minimumPage;
-        }
-        if (maximumPage >= leadingNullPages + mPages.size()) {
-            int newStorageAllocated = Math.min(mTrailingNullCount,
-                    (maximumPage + 1 - (leadingNullPages + mPages.size())) * mPageSize);
-            for (int i = mPages.size(); i <= maximumPage - leadingNullPages; i++) {
-                mPages.add(mPages.size(), null);
-            }
-            mStorageCount += newStorageAllocated;
-            mTrailingNullCount -= newStorageAllocated;
-        }
-    }
-
-    public void allocatePlaceholders(int index, int prefetchDistance,
-            int pageSize, Callback callback) {
-        if (pageSize != mPageSize) {
-            if (pageSize < mPageSize) {
-                throw new IllegalArgumentException("Page size cannot be reduced");
-            }
-            if (mPages.size() != 1 || mTrailingNullCount != 0) {
-                // not in single, last page allocated case - can't change page size
-                throw new IllegalArgumentException(
-                        "Page size can change only if last page is only one present");
-            }
-            mPageSize = pageSize;
-        }
-
-        final int maxPageCount = (size() + mPageSize - 1) / mPageSize;
-        int minimumPage = Math.max((index - prefetchDistance) / mPageSize, 0);
-        int maximumPage = Math.min((index + prefetchDistance) / mPageSize, maxPageCount - 1);
-
-        allocatePageRange(minimumPage, maximumPage);
-        int leadingNullPages = mLeadingNullCount / mPageSize;
-        for (int pageIndex = minimumPage; pageIndex <= maximumPage; pageIndex++) {
-            int localPageIndex = pageIndex - leadingNullPages;
-            if (mPages.get(localPageIndex) == null) {
-                //noinspection unchecked
-                mPages.set(localPageIndex, PLACEHOLDER_LIST);
-                callback.onPagePlaceholderInserted(pageIndex);
-            }
-        }
-    }
-
-    public boolean hasPage(int pageSize, int index) {
-        // NOTE: we pass pageSize here to avoid in case mPageSize
-        // not fully initialized (when last page only one loaded)
-        int leadingNullPages = mLeadingNullCount / pageSize;
-
-        if (index < leadingNullPages || index >= leadingNullPages + mPages.size()) {
-            return false;
-        }
-
-        List<T> page = mPages.get(index - leadingNullPages);
-
-        return page != null && page != PLACEHOLDER_LIST;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder ret = new StringBuilder("leading " + mLeadingNullCount
-                + ", storage " + mStorageCount
-                + ", trailing " + getTrailingNullCount());
-
-        for (int i = 0; i < mPages.size(); i++) {
-            ret.append(" ").append(mPages.get(i));
-        }
-        return ret.toString();
-    }
-}
diff --git a/android/arch/paging/PagedStorageDiffHelper.java b/android/arch/paging/PagedStorageDiffHelper.java
deleted file mode 100644
index d991b72..0000000
--- a/android/arch/paging/PagedStorageDiffHelper.java
+++ /dev/null
@@ -1,174 +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.arch.paging;
-
-import android.support.annotation.Nullable;
-import android.support.v7.recyclerview.extensions.DiffCallback;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.util.ListUpdateCallback;
-
-class PagedStorageDiffHelper {
-    private PagedStorageDiffHelper() {
-    }
-
-    static <T> DiffUtil.DiffResult computeDiff(
-            final PagedStorage<T> oldList,
-            final PagedStorage<T> newList,
-            final DiffCallback<T> diffCallback) {
-        final int oldOffset = oldList.computeLeadingNulls();
-        final int newOffset = newList.computeLeadingNulls();
-
-        final int oldSize = oldList.size() - oldOffset - oldList.computeTrailingNulls();
-        final int newSize = newList.size() - newOffset - newList.computeTrailingNulls();
-
-        return DiffUtil.calculateDiff(new DiffUtil.Callback() {
-            @Nullable
-            @Override
-            public Object getChangePayload(int oldItemPosition, int newItemPosition) {
-                T oldItem = oldList.get(oldItemPosition + oldOffset);
-                T newItem = newList.get(newItemPosition + newList.getLeadingNullCount());
-                if (oldItem == null || newItem == null) {
-                    return null;
-                }
-                return diffCallback.getChangePayload(oldItem, newItem);
-            }
-
-            @Override
-            public int getOldListSize() {
-                return oldSize;
-            }
-
-            @Override
-            public int getNewListSize() {
-                return newSize;
-            }
-
-            @Override
-            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-                T oldItem = oldList.get(oldItemPosition + oldOffset);
-                T newItem = newList.get(newItemPosition + newList.getLeadingNullCount());
-                if (oldItem == newItem) {
-                    return true;
-                }
-                //noinspection SimplifiableIfStatement
-                if (oldItem == null || newItem == null) {
-                    return false;
-                }
-                return diffCallback.areItemsTheSame(oldItem, newItem);
-            }
-
-            @Override
-            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
-                T oldItem = oldList.get(oldItemPosition + oldOffset);
-                T newItem = newList.get(newItemPosition + newList.getLeadingNullCount());
-                if (oldItem == newItem) {
-                    return true;
-                }
-                //noinspection SimplifiableIfStatement
-                if (oldItem == null || newItem == null) {
-                    return false;
-                }
-
-                return diffCallback.areContentsTheSame(oldItem, newItem);
-            }
-        }, true);
-    }
-
-    private static class OffsettingListUpdateCallback implements ListUpdateCallback {
-        private final int mOffset;
-        private final ListUpdateCallback mCallback;
-
-        private OffsettingListUpdateCallback(int offset, ListUpdateCallback callback) {
-            mOffset = offset;
-            mCallback = callback;
-        }
-
-        @Override
-        public void onInserted(int position, int count) {
-            mCallback.onInserted(position + mOffset, count);
-        }
-
-        @Override
-        public void onRemoved(int position, int count) {
-            mCallback.onRemoved(position + mOffset, count);
-        }
-
-        @Override
-        public void onMoved(int fromPosition, int toPosition) {
-            mCallback.onRemoved(fromPosition + mOffset, toPosition + mOffset);
-        }
-
-        @Override
-        public void onChanged(int position, int count, Object payload) {
-            mCallback.onChanged(position + mOffset, count, payload);
-        }
-    }
-
-    /**
-     * TODO: improve diffing logic
-     *
-     * This function currently does a naive diff, assuming null does not become an item, and vice
-     * versa (so it won't dispatch onChange events for these). It's similar to passing a list with
-     * leading/trailing nulls in the beginning / end to DiffUtil, but dispatches the remove/insert
-     * for changed nulls at the beginning / end of the list.
-     *
-     * Note: if lists mutate between diffing the snapshot and dispatching the diff here, then we
-     * handle this by passing the snapshot to the callback, and dispatching those changes
-     * immediately after dispatching this diff.
-     */
-    static <T> void dispatchDiff(ListUpdateCallback callback,
-            final PagedStorage<T> oldList,
-            final PagedStorage<T> newList,
-            final DiffUtil.DiffResult diffResult) {
-
-        final int trailingOld = oldList.computeTrailingNulls();
-        final int trailingNew = newList.computeTrailingNulls();
-        final int leadingOld = oldList.computeLeadingNulls();
-        final int leadingNew = newList.computeLeadingNulls();
-
-        if (trailingOld == 0
-                && trailingNew == 0
-                && leadingOld == 0
-                && leadingNew == 0) {
-            // Simple case, dispatch & return
-            diffResult.dispatchUpdatesTo(callback);
-            return;
-        }
-
-        // First, remove or insert trailing nulls
-        if (trailingOld > trailingNew) {
-            int count = trailingOld - trailingNew;
-            callback.onRemoved(oldList.size() - count, count);
-        } else if (trailingOld < trailingNew) {
-            callback.onInserted(oldList.size(), trailingNew - trailingOld);
-        }
-
-        // Second, remove or insert leading nulls
-        if (leadingOld > leadingNew) {
-            callback.onRemoved(0, leadingOld - leadingNew);
-        } else if (leadingOld < leadingNew) {
-            callback.onInserted(0, leadingNew - leadingOld);
-        }
-
-        // apply the diff, with an offset if needed
-        if (leadingNew != 0) {
-            diffResult.dispatchUpdatesTo(new OffsettingListUpdateCallback(leadingNew, callback));
-        } else {
-            diffResult.dispatchUpdatesTo(callback);
-        }
-    }
-}
diff --git a/android/arch/paging/PositionalDataSource.java b/android/arch/paging/PositionalDataSource.java
deleted file mode 100644
index 7ffce00..0000000
--- a/android/arch/paging/PositionalDataSource.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Position-based data loader for a fixed-size, countable data set, supporting fixed-size loads at
- * arbitrary page positions.
- * <p>
- * Extend PositionalDataSource if you can load pages of a requested size at arbitrary
- * positions, and provide a fixed item count. If your data source can't support loading arbitrary
- * requested page sizes (e.g. when network page size constraints are only known at runtime), use
- * either {@link PageKeyedDataSource} or {@link ItemKeyedDataSource} instead.
- * <p>
- * Note that unless {@link PagedList.Config#enablePlaceholders placeholders are disabled}
- * PositionalDataSource requires counting the size of the data set. This allows pages to be tiled in
- * at arbitrary, non-contiguous locations based upon what the user observes in a {@link PagedList}.
- * If placeholders are disabled, initialize with the two parameter
- * {@link LoadInitialCallback#onResult(List, int)}.
- * <p>
- * Room can generate a Factory of PositionalDataSources for you:
- * <pre>
- * {@literal @}Dao
- * interface UserDao {
- *     {@literal @}Query("SELECT * FROM user ORDER BY mAge DESC")
- *     public abstract DataSource.Factory&lt;Integer, User> loadUsersByAgeDesc();
- * }</pre>
- *
- * @param <T> Type of items being loaded by the PositionalDataSource.
- */
-public abstract class PositionalDataSource<T> extends DataSource<Integer, T> {
-
-    /**
-     * Holder object for inputs to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadInitialParams {
-        /**
-         * Initial load position requested.
-         * <p>
-         * Note that this may not be within the bounds of your data set, it may need to be adjusted
-         * before you execute your load.
-         */
-        public final int requestedStartPosition;
-
-        /**
-         * Requested number of items to load.
-         * <p>
-         * Note that this may be larger than available data.
-         */
-        public final int requestedLoadSize;
-
-        /**
-         * Defines page size acceptable for return values.
-         * <p>
-         * List of items passed to the callback must be an integer multiple of page size.
-         */
-        public final int pageSize;
-
-        /**
-         * Defines whether placeholders are enabled, and whether the total count passed to
-         * {@link LoadInitialCallback#onResult(List, int, int)} will be ignored.
-         */
-        public final boolean placeholdersEnabled;
-
-        LoadInitialParams(
-                int requestedStartPosition,
-                int requestedLoadSize,
-                int pageSize,
-                boolean placeholdersEnabled) {
-            this.requestedStartPosition = requestedStartPosition;
-            this.requestedLoadSize = requestedLoadSize;
-            this.pageSize = pageSize;
-            this.placeholdersEnabled = placeholdersEnabled;
-        }
-    }
-
-    /**
-     * Holder object for inputs to {@link #loadRange(LoadRangeParams, LoadRangeCallback)}.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static class LoadRangeParams {
-        /**
-         * Start position of data to load.
-         * <p>
-         * Returned data must start at this position.
-         */
-        public final int startPosition;
-        /**
-         * Number of items to load.
-         * <p>
-         * Returned data must be of this size, unless at end of the list.
-         */
-        public final int loadSize;
-
-        LoadRangeParams(int startPosition, int loadSize) {
-            this.startPosition = startPosition;
-            this.loadSize = loadSize;
-        }
-    }
-
-    /**
-     * Callback for {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}
-     * to return data, position, and count.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <T> Type of items being loaded.
-     */
-    public static class LoadInitialCallback<T> extends BaseLoadCallback<T> {
-        private final boolean mCountingEnabled;
-        private final int mPageSize;
-
-        LoadInitialCallback(@NonNull PositionalDataSource dataSource, boolean countingEnabled,
-                int pageSize, PageResult.Receiver<T> receiver) {
-            super(dataSource, PageResult.INIT, null, receiver);
-            mCountingEnabled = countingEnabled;
-            mPageSize = pageSize;
-            if (mPageSize < 1) {
-                throw new IllegalArgumentException("Page size must be non-negative");
-            }
-        }
-
-        /**
-         * Called to pass initial load state from a DataSource.
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * and inform how many placeholders should be shown before and after. If counting is cheap
-         * to compute (for example, if a network load returns the information regardless), it's
-         * recommended to pass the total size to the totalCount parameter. If placeholders are not
-         * requested (when {@link LoadInitialParams#placeholdersEnabled} is false), you can instead
-         * call {@link #onResult(List, int)}.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be loaded from this DataSource,
-         *                 pass {@code N}.
-         * @param totalCount Total number of items that may be returned from this DataSource.
-         *                   Includes the number in the initial {@code data} parameter
-         *                   as well as any items that can be loaded in front or behind of
-         *                   {@code data}.
-         */
-        public void onResult(@NonNull List<T> data, int position, int totalCount) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                validateInitialLoadParams(data, position, totalCount);
-                if (position + data.size() != totalCount
-                        && data.size() % mPageSize != 0) {
-                    throw new IllegalArgumentException("PositionalDataSource requires initial load"
-                            + " size to be a multiple of page size to support internal tiling."
-                            + " loadSize " + data.size() + ", position " + position
-                            + ", totalCount " + totalCount + ", pageSize " + mPageSize);
-                }
-
-                if (mCountingEnabled) {
-                    int trailingUnloadedCount = totalCount - position - data.size();
-                    dispatchResultToReceiver(
-                            new PageResult<>(data, position, trailingUnloadedCount, 0));
-                } else {
-                    // Only occurs when wrapped as contiguous
-                    dispatchResultToReceiver(new PageResult<>(data, position));
-                }
-            }
-        }
-
-        /**
-         * Called to pass initial load state from a DataSource without total count,
-         * when placeholders aren't requested.
-         * <p class="note"><strong>Note:</strong> This method can only be called when placeholders
-         * are disabled ({@link LoadInitialParams#placeholdersEnabled} is false).
-         * <p>
-         * Call this method from your DataSource's {@code loadInitial} function to return data,
-         * if position is known but total size is not. If placeholders are requested, call the three
-         * parameter variant: {@link #onResult(List, int, int)}.
-         *
-         * @param data List of items loaded from the DataSource. If this is empty, the DataSource
-         *             is treated as empty, and no further loads will occur.
-         * @param position Position of the item at the front of the list. If there are {@code N}
-         *                 items before the items in data that can be provided by this DataSource,
-         *                 pass {@code N}.
-         */
-        @SuppressWarnings("WeakerAccess")
-        public void onResult(@NonNull List<T> data, int position) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                if (position < 0) {
-                    throw new IllegalArgumentException("Position must be non-negative");
-                }
-                if (data.isEmpty() && position != 0) {
-                    throw new IllegalArgumentException(
-                            "Initial result cannot be empty if items are present in data set.");
-                }
-                if (mCountingEnabled) {
-                    throw new IllegalStateException("Placeholders requested, but totalCount not"
-                            + " provided. Please call the three-parameter onResult method, or"
-                            + " disable placeholders in the PagedList.Config");
-                }
-                dispatchResultToReceiver(new PageResult<>(data, position));
-            }
-        }
-    }
-
-    /**
-     * Callback for PositionalDataSource {@link #loadRange(LoadRangeParams, LoadRangeCallback)}
-     * to return data.
-     * <p>
-     * A callback can be called only once, and will throw if called again.
-     * <p>
-     * It is always valid for a DataSource loading method that takes a callback to stash the
-     * callback and call it later. This enables DataSources to be fully asynchronous, and to handle
-     * temporary, recoverable error states (such as a network error that can be retried).
-     *
-     * @param <T> Type of items being loaded.
-     */
-    public static class LoadRangeCallback<T> extends BaseLoadCallback<T> {
-        private final int mPositionOffset;
-        LoadRangeCallback(@NonNull PositionalDataSource dataSource,
-                @PageResult.ResultType int resultType, int positionOffset,
-                Executor mainThreadExecutor, PageResult.Receiver<T> receiver) {
-            super(dataSource, resultType, mainThreadExecutor, receiver);
-            mPositionOffset = positionOffset;
-        }
-
-        /**
-         * Called to pass loaded data from {@link #loadRange(LoadRangeParams, LoadRangeCallback)}.
-         *
-         * @param data List of items loaded from the DataSource. Must be same size as requested,
-         *             unless at end of list.
-         */
-        public void onResult(@NonNull List<T> data) {
-            if (!dispatchInvalidResultIfInvalid()) {
-                dispatchResultToReceiver(new PageResult<>(
-                        data, 0, 0, mPositionOffset));
-            }
-        }
-    }
-
-    final void dispatchLoadInitial(boolean acceptCount,
-            int requestedStartPosition, int requestedLoadSize, int pageSize,
-            @NonNull Executor mainThreadExecutor, @NonNull PageResult.Receiver<T> receiver) {
-        LoadInitialCallback<T> callback =
-                new LoadInitialCallback<>(this, acceptCount, pageSize, receiver);
-
-        LoadInitialParams params = new LoadInitialParams(
-                requestedStartPosition, requestedLoadSize, pageSize, acceptCount);
-        loadInitial(params, callback);
-
-        // If initialLoad's callback is not called within the body, we force any following calls
-        // to post to the UI thread. This constructor may be run on a background thread, but
-        // after constructor, mutation must happen on UI thread.
-        callback.setPostExecutor(mainThreadExecutor);
-    }
-
-    final void dispatchLoadRange(@PageResult.ResultType int resultType, int startPosition,
-            int count, @NonNull Executor mainThreadExecutor,
-            @NonNull PageResult.Receiver<T> receiver) {
-        LoadRangeCallback<T> callback = new LoadRangeCallback<>(
-                this, resultType, startPosition, mainThreadExecutor, receiver);
-        if (count == 0) {
-            callback.onResult(Collections.<T>emptyList());
-        } else {
-            loadRange(new LoadRangeParams(startPosition, count), callback);
-        }
-    }
-
-    /**
-     * Load initial list data.
-     * <p>
-     * This method is called to load the initial page(s) from the DataSource.
-     * <p>
-     * Result list must be a multiple of pageSize to enable efficient tiling.
-     *
-     * @param params Parameters for initial load, including requested start position, load size, and
-     *               page size.
-     * @param callback Callback that receives initial load data, including
-     *                 position and total data set size.
-     */
-    @WorkerThread
-    public abstract void loadInitial(
-            @NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback);
-
-    /**
-     * Called to load a range of data from the DataSource.
-     * <p>
-     * This method is called to load additional pages from the DataSource after the
-     * LoadInitialCallback passed to dispatchLoadInitial has initialized a PagedList.
-     * <p>
-     * Unlike {@link #loadInitial(LoadInitialParams, LoadInitialCallback)}, this method must return
-     * the number of items requested, at the position requested.
-     *
-     * @param params Parameters for load, including start position and load size.
-     * @param callback Callback that receives loaded data.
-     */
-    @WorkerThread
-    public abstract void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback);
-
-    @Override
-    boolean isContiguous() {
-        return false;
-    }
-
-    @NonNull
-    ContiguousDataSource<Integer, T> wrapAsContiguousWithoutPlaceholders() {
-        return new ContiguousWithoutPlaceholdersWrapper<>(this);
-    }
-
-    /**
-     * Helper for computing an initial position in
-     * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} when total data set size can be
-     * computed ahead of loading.
-     * <p>
-     * The value computed by this function will do bounds checking, page alignment, and positioning
-     * based on initial load size requested.
-     * <p>
-     * Example usage in a PositionalDataSource subclass:
-     * <pre>
-     * class ItemDataSource extends PositionalDataSource&lt;Item> {
-     *     private int computeCount() {
-     *         // actual count code here
-     *     }
-     *
-     *     private List&lt;Item> loadRangeInternal(int startPosition, int loadCount) {
-     *         // actual load code here
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadInitial({@literal @}NonNull LoadInitialParams params,
-     *             {@literal @}NonNull LoadInitialCallback&lt;Item> callback) {
-     *         int totalCount = computeCount();
-     *         int position = computeInitialLoadPosition(params, totalCount);
-     *         int loadSize = computeInitialLoadSize(params, position, totalCount);
-     *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadRange({@literal @}NonNull LoadRangeParams params,
-     *             {@literal @}NonNull LoadRangeCallback&lt;Item> callback) {
-     *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
-     *     }
-     * }</pre>
-     *
-     * @param params Params passed to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)},
-     *               including page size, and requested start/loadSize.
-     * @param totalCount Total size of the data set.
-     * @return Position to start loading at.
-     *
-     * @see #computeInitialLoadSize(LoadInitialParams, int, int)
-     */
-    public static int computeInitialLoadPosition(@NonNull LoadInitialParams params,
-            int totalCount) {
-        int position = params.requestedStartPosition;
-        int initialLoadSize = params.requestedLoadSize;
-        int pageSize = params.pageSize;
-
-        int roundedPageStart = Math.round(position / pageSize) * pageSize;
-
-        // maximum start pos is that which will encompass end of list
-        int maximumLoadPage = ((totalCount - initialLoadSize + pageSize - 1) / pageSize) * pageSize;
-        roundedPageStart = Math.min(maximumLoadPage, roundedPageStart);
-
-        // minimum start position is 0
-        roundedPageStart = Math.max(0, roundedPageStart);
-
-        return roundedPageStart;
-    }
-
-    /**
-     * Helper for computing an initial load size in
-     * {@link #loadInitial(LoadInitialParams, LoadInitialCallback)} when total data set size can be
-     * computed ahead of loading.
-     * <p>
-     * This function takes the requested load size, and bounds checks it against the value returned
-     * by {@link #computeInitialLoadPosition(LoadInitialParams, int)}.
-     * <p>
-     * Example usage in a PositionalDataSource subclass:
-     * <pre>
-     * class ItemDataSource extends PositionalDataSource&lt;Item> {
-     *     private int computeCount() {
-     *         // actual count code here
-     *     }
-     *
-     *     private List&lt;Item> loadRangeInternal(int startPosition, int loadCount) {
-     *         // actual load code here
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadInitial({@literal @}NonNull LoadInitialParams params,
-     *             {@literal @}NonNull LoadInitialCallback&lt;Item> callback) {
-     *         int totalCount = computeCount();
-     *         int position = computeInitialLoadPosition(params, totalCount);
-     *         int loadSize = computeInitialLoadSize(params, position, totalCount);
-     *         callback.onResult(loadRangeInternal(position, loadSize), position, totalCount);
-     *     }
-     *
-     *     {@literal @}Override
-     *     public void loadRange({@literal @}NonNull LoadRangeParams params,
-     *             {@literal @}NonNull LoadRangeCallback&lt;Item> callback) {
-     *         callback.onResult(loadRangeInternal(params.startPosition, params.loadSize));
-     *     }
-     * }</pre>
-     *
-     * @param params Params passed to {@link #loadInitial(LoadInitialParams, LoadInitialCallback)},
-     *               including page size, and requested start/loadSize.
-     * @param initialLoadPosition Value returned by
-     *                          {@link #computeInitialLoadPosition(LoadInitialParams, int)}
-     * @param totalCount Total size of the data set.
-     * @return Number of items to load.
-     *
-     * @see #computeInitialLoadPosition(LoadInitialParams, int)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public static int computeInitialLoadSize(@NonNull LoadInitialParams params,
-            int initialLoadPosition, int totalCount) {
-        return Math.min(totalCount - initialLoadPosition, params.requestedLoadSize);
-    }
-
-    @SuppressWarnings("deprecation")
-    static class ContiguousWithoutPlaceholdersWrapper<Value>
-            extends ContiguousDataSource<Integer, Value> {
-
-        @NonNull
-        final PositionalDataSource<Value> mPositionalDataSource;
-
-        ContiguousWithoutPlaceholdersWrapper(
-                @NonNull PositionalDataSource<Value> positionalDataSource) {
-            mPositionalDataSource = positionalDataSource;
-        }
-
-        @Override
-        void dispatchLoadInitial(@Nullable Integer position, int initialLoadSize, int pageSize,
-                boolean enablePlaceholders, @NonNull Executor mainThreadExecutor,
-                @NonNull PageResult.Receiver<Value> receiver) {
-            final int convertPosition = position == null ? 0 : position;
-
-            // Note enablePlaceholders will be false here, but we don't have a way to communicate
-            // this to PositionalDataSource. This is fine, because only the list and its position
-            // offset will be consumed by the LoadInitialCallback.
-            mPositionalDataSource.dispatchLoadInitial(false, convertPosition, initialLoadSize,
-                    pageSize, mainThreadExecutor, receiver);
-        }
-
-        @Override
-        void dispatchLoadAfter(int currentEndIndex, @NonNull Value currentEndItem, int pageSize,
-                @NonNull Executor mainThreadExecutor,
-                @NonNull PageResult.Receiver<Value> receiver) {
-            int startIndex = currentEndIndex + 1;
-            mPositionalDataSource.dispatchLoadRange(
-                    PageResult.APPEND, startIndex, pageSize, mainThreadExecutor, receiver);
-        }
-
-        @Override
-        void dispatchLoadBefore(int currentBeginIndex, @NonNull Value currentBeginItem,
-                int pageSize, @NonNull Executor mainThreadExecutor,
-                @NonNull PageResult.Receiver<Value> receiver) {
-
-            int startIndex = currentBeginIndex - 1;
-            if (startIndex < 0) {
-                // trigger empty list load
-                mPositionalDataSource.dispatchLoadRange(
-                        PageResult.PREPEND, startIndex, 0, mainThreadExecutor, receiver);
-            } else {
-                int loadSize = Math.min(pageSize, startIndex + 1);
-                startIndex = startIndex - loadSize + 1;
-                mPositionalDataSource.dispatchLoadRange(
-                        PageResult.PREPEND, startIndex, loadSize, mainThreadExecutor, receiver);
-            }
-        }
-
-        @Override
-        Integer getKey(int position, Value item) {
-            return position;
-        }
-    }
-}
diff --git a/android/arch/paging/SnapshotPagedList.java b/android/arch/paging/SnapshotPagedList.java
deleted file mode 100644
index 6a8a748..0000000
--- a/android/arch/paging/SnapshotPagedList.java
+++ /dev/null
@@ -1,65 +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.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-class SnapshotPagedList<T> extends PagedList<T> {
-    private final boolean mContiguous;
-    private final Object mLastKey;
-
-    SnapshotPagedList(@NonNull PagedList<T> pagedList) {
-        super(pagedList.mStorage.snapshot(),
-                pagedList.mMainThreadExecutor,
-                pagedList.mBackgroundThreadExecutor,
-                null,
-                pagedList.mConfig);
-        mContiguous = pagedList.isContiguous();
-        mLastKey = pagedList.getLastKey();
-    }
-
-    @Override
-    public boolean isImmutable() {
-        return true;
-    }
-
-    @Override
-    public boolean isDetached() {
-        return true;
-    }
-
-    @Override
-    boolean isContiguous() {
-        return mContiguous;
-    }
-
-    @Nullable
-    @Override
-    public Object getLastKey() {
-        return mLastKey;
-    }
-
-    @Override
-    void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> storageSnapshot,
-            @NonNull Callback callback) {
-    }
-
-    @Override
-    void loadAroundInternal(int index) {
-    }
-}
diff --git a/android/arch/paging/TiledDataSource.java b/android/arch/paging/TiledDataSource.java
deleted file mode 100644
index 7285aa4..0000000
--- a/android/arch/paging/TiledDataSource.java
+++ /dev/null
@@ -1,84 +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.arch.paging;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.WorkerThread;
-
-import java.util.Collections;
-import java.util.List;
-
-// NOTE: Room 1.0 depends on this class, so it should not be removed until
-// we can require a version of Room that uses PositionalDataSource directly
-/**
- * @param <T> Type loaded by the TiledDataSource.
- *
- * @deprecated Use {@link PositionalDataSource}
- * @hide
- */
-@SuppressWarnings("DeprecatedIsStillUsed")
-@Deprecated
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class TiledDataSource<T> extends PositionalDataSource<T> {
-
-    @WorkerThread
-    public abstract int countItems();
-
-    @Override
-    boolean isContiguous() {
-        return false;
-    }
-
-    @WorkerThread
-    public abstract List<T> loadRange(int startPosition, int count);
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback) {
-        int totalCount = countItems();
-        if (totalCount == 0) {
-            callback.onResult(Collections.<T>emptyList(), 0, 0);
-            return;
-        }
-
-        // bound the size requested, based on known count
-        final int firstLoadPosition = computeInitialLoadPosition(params, totalCount);
-        final int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
-
-        // convert from legacy behavior
-        List<T> list = loadRange(firstLoadPosition, firstLoadSize);
-        if (list != null && list.size() == firstLoadSize) {
-            callback.onResult(list, firstLoadPosition, totalCount);
-        } else {
-            // null list, or size doesn't match request
-            // The size check is a WAR for Room 1.0, subsequent versions do the check in Room
-            invalidate();
-        }
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback) {
-        List<T> list = loadRange(params.startPosition, params.loadSize);
-        if (list != null) {
-            callback.onResult(list);
-        } else {
-            invalidate();
-        }
-    }
-}
diff --git a/android/arch/paging/TiledPagedList.java b/android/arch/paging/TiledPagedList.java
deleted file mode 100644
index 9958b8d..0000000
--- a/android/arch/paging/TiledPagedList.java
+++ /dev/null
@@ -1,196 +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.arch.paging;
-
-import android.support.annotation.AnyThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-
-import java.util.concurrent.Executor;
-
-class TiledPagedList<T> extends PagedList<T>
-        implements PagedStorage.Callback {
-    private final PositionalDataSource<T> mDataSource;
-
-    private PageResult.Receiver<T> mReceiver = new PageResult.Receiver<T>() {
-        // Creation thread for initial synchronous load, otherwise main thread
-        // Safe to access main thread only state - no other thread has reference during construction
-        @AnyThread
-        @Override
-        public void onPageResult(@PageResult.ResultType int type,
-                @NonNull PageResult<T> pageResult) {
-            if (pageResult.isInvalid()) {
-                detach();
-                return;
-            }
-
-            if (isDetached()) {
-                // No op, have detached
-                return;
-            }
-
-            if (type != PageResult.INIT && type != PageResult.TILE) {
-                throw new IllegalArgumentException("unexpected resultType" + type);
-            }
-
-            if (mStorage.getPageCount() == 0) {
-                mStorage.initAndSplit(
-                        pageResult.leadingNulls, pageResult.page, pageResult.trailingNulls,
-                        pageResult.positionOffset, mConfig.pageSize, TiledPagedList.this);
-            } else {
-                mStorage.insertPage(pageResult.positionOffset, pageResult.page,
-                        TiledPagedList.this);
-            }
-
-            if (mBoundaryCallback != null) {
-                boolean deferEmpty = mStorage.size() == 0;
-                boolean deferBegin = !deferEmpty
-                        && pageResult.leadingNulls == 0
-                        && pageResult.positionOffset == 0;
-                int size = size();
-                boolean deferEnd = !deferEmpty
-                        && ((type == PageResult.INIT && pageResult.trailingNulls == 0)
-                                || (type == PageResult.TILE
-                                        && pageResult.positionOffset
-                                                == (size - size % mConfig.pageSize)));
-                deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd);
-            }
-        }
-    };
-
-    @WorkerThread
-    TiledPagedList(@NonNull PositionalDataSource<T> dataSource,
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @Nullable BoundaryCallback<T> boundaryCallback,
-            @NonNull Config config,
-            int position) {
-        super(new PagedStorage<T>(), mainThreadExecutor, backgroundThreadExecutor,
-                boundaryCallback, config);
-        mDataSource = dataSource;
-
-        final int pageSize = mConfig.pageSize;
-        mLastLoad = position;
-
-        if (mDataSource.isInvalid()) {
-            detach();
-        } else {
-            final int firstLoadSize =
-                    (Math.max(Math.round(mConfig.initialLoadSizeHint / pageSize), 2)) * pageSize;
-
-            final int idealStart = position - firstLoadSize / 2;
-            final int roundedPageStart = Math.max(0, Math.round(idealStart / pageSize) * pageSize);
-
-            mDataSource.dispatchLoadInitial(true, roundedPageStart, firstLoadSize,
-                    pageSize, mMainThreadExecutor, mReceiver);
-        }
-    }
-
-    @Override
-    boolean isContiguous() {
-        return false;
-    }
-
-    @Nullable
-    @Override
-    public Object getLastKey() {
-        return mLastLoad;
-    }
-
-    @Override
-    protected void dispatchUpdatesSinceSnapshot(@NonNull PagedList<T> pagedListSnapshot,
-            @NonNull Callback callback) {
-        //noinspection UnnecessaryLocalVariable
-        final PagedStorage<T> snapshot = pagedListSnapshot.mStorage;
-
-        if (snapshot.isEmpty()
-                || mStorage.size() != snapshot.size()) {
-            throw new IllegalArgumentException("Invalid snapshot provided - doesn't appear"
-                    + " to be a snapshot of this PagedList");
-        }
-
-        // loop through each page and signal the callback for any pages that are present now,
-        // but not in the snapshot.
-        final int pageSize = mConfig.pageSize;
-        final int leadingNullPages = mStorage.getLeadingNullCount() / pageSize;
-        final int pageCount = mStorage.getPageCount();
-        for (int i = 0; i < pageCount; i++) {
-            int pageIndex = i + leadingNullPages;
-            int updatedPages = 0;
-            // count number of consecutive pages that were added since the snapshot...
-            while (updatedPages < mStorage.getPageCount()
-                    && mStorage.hasPage(pageSize, pageIndex + updatedPages)
-                    && !snapshot.hasPage(pageSize, pageIndex + updatedPages)) {
-                updatedPages++;
-            }
-            // and signal them all at once to the callback
-            if (updatedPages > 0) {
-                callback.onChanged(pageIndex * pageSize, pageSize * updatedPages);
-                i += updatedPages - 1;
-            }
-        }
-    }
-
-    @Override
-    protected void loadAroundInternal(int index) {
-        mStorage.allocatePlaceholders(index, mConfig.prefetchDistance, mConfig.pageSize, this);
-    }
-
-    @Override
-    public void onInitialized(int count) {
-        notifyInserted(0, count);
-    }
-
-    @Override
-    public void onPagePrepended(int leadingNulls, int changed, int added) {
-        throw new IllegalStateException("Contiguous callback on TiledPagedList");
-    }
-
-    @Override
-    public void onPageAppended(int endPosition, int changed, int added) {
-        throw new IllegalStateException("Contiguous callback on TiledPagedList");
-    }
-
-    @Override
-    public void onPagePlaceholderInserted(final int pageIndex) {
-        // placeholder means initialize a load
-        mBackgroundThreadExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                if (isDetached()) {
-                    return;
-                }
-                final int pageSize = mConfig.pageSize;
-
-                if (mDataSource.isInvalid()) {
-                    detach();
-                } else {
-                    int startPosition = pageIndex * pageSize;
-                    int count = Math.min(pageSize, mStorage.size() - startPosition);
-                    mDataSource.dispatchLoadRange(
-                            PageResult.TILE, startPosition, count, mMainThreadExecutor, mReceiver);
-                }
-            }
-        });
-    }
-
-    @Override
-    public void onPageInserted(int start, int count) {
-        notifyChanged(start, count);
-    }
-}
diff --git a/android/arch/paging/integration/testapp/Item.java b/android/arch/paging/integration/testapp/Item.java
deleted file mode 100644
index 70f2133..0000000
--- a/android/arch/paging/integration/testapp/Item.java
+++ /dev/null
@@ -1,59 +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.arch.paging.integration.testapp;
-
-import android.support.annotation.NonNull;
-import android.support.v7.recyclerview.extensions.DiffCallback;
-
-/**
- * Sample item.
- */
-class Item {
-    public final int id;
-    public final String text;
-    @SuppressWarnings("WeakerAccess")
-    public final int bgColor;
-
-    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);
-    }
-
-    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/android/arch/paging/integration/testapp/ItemDataSource.java b/android/arch/paging/integration/testapp/ItemDataSource.java
deleted file mode 100644
index c53d361..0000000
--- a/android/arch/paging/integration/testapp/ItemDataSource.java
+++ /dev/null
@@ -1,74 +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.arch.paging.integration.testapp;
-
-import android.arch.paging.PositionalDataSource;
-import android.graphics.Color;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Sample data source with artificial data.
- */
-class ItemDataSource extends PositionalDataSource<Item> {
-    private static final int COUNT = 500;
-
-    @ColorInt
-    private static final int[] COLORS = new int[] {
-            Color.RED,
-            Color.BLUE,
-            Color.BLACK,
-    };
-
-    private static int sGenerationId;
-    private final int mGenerationId = sGenerationId++;
-
-    private List<Item> loadRangeInternal(int startPosition, int loadCount) {
-        List<Item> items = new ArrayList<>();
-        int end = Math.min(COUNT, startPosition + loadCount);
-        int bgColor = COLORS[mGenerationId % COLORS.length];
-
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-        for (int i = startPosition; i != end; i++) {
-            items.add(new Item(i, "item " + i, bgColor));
-        }
-        return items;
-    }
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<Item> callback) {
-        int position = computeInitialLoadPosition(params, COUNT);
-        int loadSize = computeInitialLoadSize(params, position, COUNT);
-        List<Item> data = loadRangeInternal(position, loadSize);
-        callback.onResult(data, position, COUNT);
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<Item> callback) {
-        List<Item> data = loadRangeInternal(params.startPosition, params.loadSize);
-        callback.onResult(data);
-    }
-}
diff --git a/android/arch/paging/integration/testapp/PagedListItemAdapter.java b/android/arch/paging/integration/testapp/PagedListItemAdapter.java
deleted file mode 100644
index d1ae5ab..0000000
--- a/android/arch/paging/integration/testapp/PagedListItemAdapter.java
+++ /dev/null
@@ -1,53 +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.arch.paging.integration.testapp;
-
-import android.arch.paging.PagedListAdapter;
-import android.graphics.Color;
-import android.support.v7.widget.RecyclerView;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-/**
- * Sample PagedList item Adapter, which uses a PagedListAdapterHelper.
- */
-class PagedListItemAdapter extends PagedListAdapter<Item, RecyclerView.ViewHolder> {
-
-    PagedListItemAdapter() {
-        super(Item.DIFF_CALLBACK);
-    }
-
-    @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 = getItem(position);
-        if (item != null) {
-            ((TextView) (holder.itemView)).setText(item.text);
-            holder.itemView.setBackgroundColor(item.bgColor);
-        } else {
-            ((TextView) (holder.itemView)).setText(R.string.loading);
-            holder.itemView.setBackgroundColor(Color.TRANSPARENT);
-        }
-    }
-}
diff --git a/android/arch/paging/integration/testapp/PagedListItemViewModel.java b/android/arch/paging/integration/testapp/PagedListItemViewModel.java
deleted file mode 100644
index be14bc1..0000000
--- a/android/arch/paging/integration/testapp/PagedListItemViewModel.java
+++ /dev/null
@@ -1,59 +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.arch.paging.integration.testapp;
-
-import android.arch.lifecycle.LiveData;
-import android.arch.lifecycle.ViewModel;
-import android.arch.paging.DataSource;
-import android.arch.paging.LivePagedListBuilder;
-import android.arch.paging.PagedList;
-
-/**
- * Sample ViewModel backed by an artificial data source
- */
-@SuppressWarnings("WeakerAccess")
-public class PagedListItemViewModel extends ViewModel {
-    private ItemDataSource mDataSource;
-    private final Object mDataSourceLock = new Object();
-
-    private final DataSource.Factory<Integer, Item> mFactory =
-            new DataSource.Factory<Integer, Item>() {
-        @Override
-        public DataSource<Integer, Item> create() {
-            ItemDataSource newDataSource = new ItemDataSource();
-            synchronized (mDataSourceLock) {
-                mDataSource = newDataSource;
-                return mDataSource;
-            }
-        }
-    };
-
-    private LiveData<PagedList<Item>> mLivePagedList =
-            new LivePagedListBuilder<>(mFactory, 20).build();
-
-    void invalidateList() {
-        synchronized (mDataSourceLock) {
-            if (mDataSource != null) {
-                mDataSource.invalidate();
-            }
-        }
-    }
-
-    LiveData<PagedList<Item>> getLivePagedList() {
-        return mLivePagedList;
-    }
-}
diff --git a/android/arch/paging/integration/testapp/PagedListSampleActivity.java b/android/arch/paging/integration/testapp/PagedListSampleActivity.java
deleted file mode 100644
index f1f233f..0000000
--- a/android/arch/paging/integration/testapp/PagedListSampleActivity.java
+++ /dev/null
@@ -1,58 +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.arch.paging.integration.testapp;
-
-import android.arch.lifecycle.Observer;
-import android.arch.lifecycle.ViewModelProviders;
-import android.arch.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 with artificial data source.
- */
-public class PagedListSampleActivity extends AppCompatActivity {
-
-    @Override
-    protected void onCreate(final Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_recycler_view);
-        final PagedListItemViewModel viewModel = ViewModelProviders.of(this)
-                .get(PagedListItemViewModel.class);
-
-        final PagedListItemAdapter adapter = new PagedListItemAdapter();
-        final RecyclerView recyclerView = findViewById(R.id.recyclerview);
-        recyclerView.setAdapter(adapter);
-        viewModel.getLivePagedList().observe(this, new Observer<PagedList<Item>>() {
-            @Override
-            public void onChanged(@Nullable PagedList<Item> items) {
-                adapter.setList(items);
-            }
-        });
-        final Button button = findViewById(R.id.button);
-        button.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                viewModel.invalidateList();
-            }
-        });
-    }
-}
diff --git a/android/arch/persistence/db/SimpleSQLiteQuery.java b/android/arch/persistence/db/SimpleSQLiteQuery.java
deleted file mode 100644
index bcf4f49..0000000
--- a/android/arch/persistence/db/SimpleSQLiteQuery.java
+++ /dev/null
@@ -1,100 +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.arch.persistence.db;
-
-/**
- * A basic implementation of {@link SupportSQLiteQuery} which receives a query and its args and
- * binds args based on the passed in Object type.
- */
-public final 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/android/arch/persistence/db/SupportSQLiteDatabase.java b/android/arch/persistence/db/SupportSQLiteDatabase.java
deleted file mode 100644
index 5f71baf..0000000
--- a/android/arch/persistence/db/SupportSQLiteDatabase.java
+++ /dev/null
@@ -1,605 +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.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/android/arch/persistence/db/SupportSQLiteOpenHelper.java b/android/arch/persistence/db/SupportSQLiteOpenHelper.java
deleted file mode 100644
index 02e4e7d..0000000
--- a/android/arch/persistence/db/SupportSQLiteOpenHelper.java
+++ /dev/null
@@ -1,391 +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.arch.persistence.db;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * 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();
-
-    /**
-     * Handles various lifecycle events for the SQLite connection, similar to
-     * {@link android.database.sqlite.SQLiteOpenHelper}.
-     */
-    @SuppressWarnings({"unused", "WeakerAccess"})
-    abstract class Callback {
-        private static final String TAG = "SupportSQLite";
-        /**
-         * 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;
-
-        /**
-         * Creates a new Callback to get database lifecycle events.
-         * @param version The version for the database instance. See {@link #version}.
-         */
-        public Callback(int version) {
-            this.version = version;
-        }
-
-        /**
-         * 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 method invoked when database corruption is detected. Default implementation will
-         * delete the database file.
-         *
-         * @param db the {@link SupportSQLiteDatabase} object representing the database on which
-         *           corruption is detected.
-         */
-        public void onCorruption(SupportSQLiteDatabase db) {
-            // the following implementation is taken from {@link DefaultDatabaseErrorHandler}.
-
-            Log.e(TAG, "Corruption reported by sqlite on database: " + db.getPath());
-            // is the corruption detected even before database could be 'opened'?
-            if (!db.isOpen()) {
-                // database files are not even openable. delete this database file.
-                // NOTE if the database has attached databases, then any of them could be corrupt.
-                // and not deleting all of them could cause corrupted database file to remain and
-                // make the application crash on database open operation. To avoid this problem,
-                // the application should provide its own {@link DatabaseErrorHandler} impl class
-                // to delete ALL files of the database (including the attached databases).
-                deleteDatabaseFile(db.getPath());
-                return;
-            }
-
-            List<Pair<String, String>> attachedDbs = null;
-            try {
-                // Close the database, which will cause subsequent operations to fail.
-                // before that, get the attached database list first.
-                try {
-                    attachedDbs = db.getAttachedDbs();
-                } catch (SQLiteException e) {
-                /* ignore */
-                }
-                try {
-                    db.close();
-                } catch (IOException e) {
-                /* ignore */
-                }
-            } finally {
-                // Delete all files of this corrupt database and/or attached databases
-                if (attachedDbs != null) {
-                    for (Pair<String, String> p : attachedDbs) {
-                        deleteDatabaseFile(p.second);
-                    }
-                } else {
-                    // attachedDbs = null is possible when the database is so corrupt that even
-                    // "PRAGMA database_list;" also fails. delete the main database file
-                    deleteDatabaseFile(db.getPath());
-                }
-            }
-        }
-
-        private void deleteDatabaseFile(String fileName) {
-            if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
-                return;
-            }
-            Log.w(TAG, "deleting the database file: " + fileName);
-            try {
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-                    SQLiteDatabase.deleteDatabase(new File(fileName));
-                } else {
-                    try {
-                        final boolean deleted = new File(fileName).delete();
-                        if (!deleted) {
-                            Log.e(TAG, "Could not delete the database file " + fileName);
-                        }
-                    } catch (Exception error) {
-                        Log.e(TAG, "error while deleting corrupted database file", error);
-                    }
-                }
-            } catch (Exception e) {
-            /* print warning and ignore exception */
-                Log.w(TAG, "delete failed: ", e);
-            }
-        }
-    }
-
-    /**
-     * 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;
-        /**
-         * The callback class to handle creation, upgrade and downgrade.
-         */
-        @NonNull
-        public final SupportSQLiteOpenHelper.Callback callback;
-
-        Configuration(@NonNull Context context, @Nullable String name, @NonNull Callback callback) {
-            this.context = context;
-            this.name = name;
-            this.callback = callback;
-        }
-
-        /**
-         * 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;
-            SupportSQLiteOpenHelper.Callback mCallback;
-
-            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.");
-                }
-                return new Configuration(mContext, mName, mCallback);
-            }
-
-            Builder(@NonNull Context context) {
-                mContext = context;
-            }
-
-            /**
-             * @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;
-            }
-        }
-    }
-
-    /**
-     * 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/android/arch/persistence/db/SupportSQLiteProgram.java b/android/arch/persistence/db/SupportSQLiteProgram.java
deleted file mode 100644
index 38c1ac1..0000000
--- a/android/arch/persistence/db/SupportSQLiteProgram.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.arch.persistence.db;
-
-import java.io.Closeable;
-
-/**
- * An interface to map the behavior of {@link android.database.sqlite.SQLiteProgram}.
- */
-
-@SuppressWarnings("unused")
-public interface SupportSQLiteProgram extends Closeable {
-    /**
-     * 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/android/arch/persistence/db/SupportSQLiteQuery.java b/android/arch/persistence/db/SupportSQLiteQuery.java
deleted file mode 100644
index 2007634..0000000
--- a/android/arch/persistence/db/SupportSQLiteQuery.java
+++ /dev/null
@@ -1,38 +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.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/android/arch/persistence/db/SupportSQLiteQueryBuilder.java b/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
deleted file mode 100644
index 52957a3..0000000
--- a/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
+++ /dev/null
@@ -1,202 +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.arch.persistence.db;
-
-import java.util.regex.Pattern;
-
-/**
- * A simple query builder to create SQL SELECT queries.
- */
-@SuppressWarnings("unused")
-public final 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/android/arch/persistence/db/SupportSQLiteStatement.java b/android/arch/persistence/db/SupportSQLiteStatement.java
deleted file mode 100644
index 5a329d7..0000000
--- a/android/arch/persistence/db/SupportSQLiteStatement.java
+++ /dev/null
@@ -1,72 +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.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/android/arch/persistence/db/framework/FrameworkSQLiteDatabase.java b/android/arch/persistence/db/framework/FrameworkSQLiteDatabase.java
deleted file mode 100644
index d564a03..0000000
--- a/android/arch/persistence/db/framework/FrameworkSQLiteDatabase.java
+++ /dev/null
@@ -1,317 +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.arch.persistence.db.framework;
-
-import static android.text.TextUtils.isEmpty;
-
-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.
-     */
-    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();
-    }
-}
diff --git a/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper.java b/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper.java
deleted file mode 100644
index a1690f4..0000000
--- a/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelper.java
+++ /dev/null
@@ -1,142 +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.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,
-            Callback callback) {
-        mDelegate = createDelegate(context, name, callback);
-    }
-
-    private OpenHelper createDelegate(Context context, String name, Callback callback) {
-        final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];
-        return new OpenHelper(context, name, dbRef, callback);
-    }
-
-    @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();
-    }
-
-    static class OpenHelper extends SQLiteOpenHelper {
-        /**
-         * This is used as an Object reference so that we can access the wrapped database inside
-         * the constructor. SQLiteOpenHelper requires the error handler to be passed in the
-         * constructor.
-         */
-        final FrameworkSQLiteDatabase[] mDbRef;
-        final Callback mCallback;
-
-        OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,
-                final Callback callback) {
-            super(context, name, null, callback.version,
-                    new DatabaseErrorHandler() {
-                        @Override
-                        public void onCorruption(SQLiteDatabase dbObj) {
-                            FrameworkSQLiteDatabase db = dbRef[0];
-                            if (db != null) {
-                                callback.onCorruption(db);
-                            }
-                        }
-                    });
-            mCallback = callback;
-            mDbRef = dbRef;
-        }
-
-        SupportSQLiteDatabase getWritableSupportDatabase() {
-            SQLiteDatabase db = super.getWritableDatabase();
-            return getWrappedDb(db);
-        }
-
-        SupportSQLiteDatabase getReadableSupportDatabase() {
-            SQLiteDatabase db = super.getReadableDatabase();
-            return getWrappedDb(db);
-        }
-
-        FrameworkSQLiteDatabase getWrappedDb(SQLiteDatabase sqLiteDatabase) {
-            FrameworkSQLiteDatabase dbRef = mDbRef[0];
-            if (dbRef == null) {
-                dbRef = new FrameworkSQLiteDatabase(sqLiteDatabase);
-                mDbRef[0] = dbRef;
-            }
-            return mDbRef[0];
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase sqLiteDatabase) {
-            mCallback.onCreate(getWrappedDb(sqLiteDatabase));
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
-            mCallback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion);
-        }
-
-        @Override
-        public void onConfigure(SQLiteDatabase db) {
-            mCallback.onConfigure(getWrappedDb(db));
-        }
-
-        @Override
-        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            mCallback.onDowngrade(getWrappedDb(db), oldVersion, newVersion);
-        }
-
-        @Override
-        public void onOpen(SQLiteDatabase db) {
-            mCallback.onOpen(getWrappedDb(db));
-        }
-
-        @Override
-        public synchronized void close() {
-            super.close();
-            mDbRef[0] = null;
-        }
-    }
-}
diff --git a/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java b/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java
deleted file mode 100644
index ab11d49..0000000
--- a/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.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.arch.persistence.db.framework;
-
-import android.arch.persistence.db.SupportSQLiteOpenHelper;
-
-/**
- * Implements {@link SupportSQLiteOpenHelper.Factory} using the SQLite implementation in the
- * framework.
- */
-@SuppressWarnings("unused")
-public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
-    @Override
-    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
-        return new FrameworkSQLiteOpenHelper(
-                configuration.context, configuration.name, configuration.callback);
-    }
-}
diff --git a/android/arch/persistence/db/framework/FrameworkSQLiteProgram.java b/android/arch/persistence/db/framework/FrameworkSQLiteProgram.java
deleted file mode 100644
index 73c98c6..0000000
--- a/android/arch/persistence/db/framework/FrameworkSQLiteProgram.java
+++ /dev/null
@@ -1,66 +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.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() {
-        mDelegate.close();
-    }
-}
diff --git a/android/arch/persistence/db/framework/FrameworkSQLiteStatement.java b/android/arch/persistence/db/framework/FrameworkSQLiteStatement.java
deleted file mode 100644
index 7f07865..0000000
--- a/android/arch/persistence/db/framework/FrameworkSQLiteStatement.java
+++ /dev/null
@@ -1,62 +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.arch.persistence.db.framework;
-
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.database.sqlite.SQLiteStatement;
-
-/**
- * Delegates all calls to a {@link SQLiteStatement}.
- */
-class FrameworkSQLiteStatement extends FrameworkSQLiteProgram implements SupportSQLiteStatement {
-    private final SQLiteStatement mDelegate;
-
-    /**
-     * Creates a wrapper around a framework {@link SQLiteStatement}.
-     *
-     * @param delegate The SQLiteStatement to delegate calls to.
-     */
-    FrameworkSQLiteStatement(SQLiteStatement delegate) {
-        super(delegate);
-        mDelegate = delegate;
-    }
-
-    @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();
-    }
-}
diff --git a/android/arch/persistence/room/BuilderTest.java b/android/arch/persistence/room/BuilderTest.java
deleted file mode 100644
index 2c9b9e7..0000000
--- a/android/arch/persistence/room/BuilderTest.java
+++ /dev/null
@@ -1,275 +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.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 android.support.annotation.NonNull;
-
-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 migrationDowngrade() {
-        Migration m1_2 = new EmptyMigration(1, 2);
-        Migration m2_3 = new EmptyMigration(2, 3);
-        Migration m3_4 = new EmptyMigration(3, 4);
-        Migration m3_2 = new EmptyMigration(3, 2);
-        Migration m2_1 = new EmptyMigration(2, 1);
-        TestDatabase db = Room.databaseBuilder(mock(Context.class), TestDatabase.class, "foo")
-                .addMigrations(m1_2, m2_3, m3_4, m3_2, m2_1).build();
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-        RoomDatabase.MigrationContainer migrations = config.migrationContainer;
-        assertThat(migrations.findMigrationPath(3, 2), is(asList(m3_2)));
-        assertThat(migrations.findMigrationPath(3, 1), is(asList(m3_2, m2_1)));
-    }
-
-    @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 fallbackToDestructiveMigrationFrom_calledOnce_migrationsNotRequiredForValues() {
-        Context context = mock(Context.class);
-
-        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
-                .fallbackToDestructiveMigrationFrom(1, 2).build();
-
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-        assertThat(config.isMigrationRequiredFrom(1), is(false));
-        assertThat(config.isMigrationRequiredFrom(2), is(false));
-    }
-
-    @Test
-    public void fallbackToDestructiveMigrationFrom_calledTwice_migrationsNotRequiredForValues() {
-        Context context = mock(Context.class);
-        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
-                .fallbackToDestructiveMigrationFrom(1, 2)
-                .fallbackToDestructiveMigrationFrom(3, 4)
-                .build();
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-
-        assertThat(config.isMigrationRequiredFrom(1), is(false));
-        assertThat(config.isMigrationRequiredFrom(2), is(false));
-        assertThat(config.isMigrationRequiredFrom(3), is(false));
-        assertThat(config.isMigrationRequiredFrom(4), is(false));
-    }
-
-    @Test
-    public void isMigrationRequiredFrom_fallBackToDestructiveCalled_alwaysReturnsFalse() {
-        Context context = mock(Context.class);
-
-        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
-                .fallbackToDestructiveMigration()
-                .build();
-
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-        assertThat(config.isMigrationRequiredFrom(0), is(false));
-        assertThat(config.isMigrationRequiredFrom(1), is(false));
-        assertThat(config.isMigrationRequiredFrom(5), is(false));
-        assertThat(config.isMigrationRequiredFrom(12), is(false));
-        assertThat(config.isMigrationRequiredFrom(132), is(false));
-    }
-
-    @Test
-    public void isMigrationRequiredFrom_byDefault_alwaysReturnsTrue() {
-        Context context = mock(Context.class);
-
-        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
-                .build();
-
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-        assertThat(config.isMigrationRequiredFrom(0), is(true));
-        assertThat(config.isMigrationRequiredFrom(1), is(true));
-        assertThat(config.isMigrationRequiredFrom(5), is(true));
-        assertThat(config.isMigrationRequiredFrom(12), is(true));
-        assertThat(config.isMigrationRequiredFrom(132), is(true));
-    }
-
-    @Test
-    public void isMigrationRequiredFrom_fallBackToDestFromCalled_falseForProvidedValues() {
-        Context context = mock(Context.class);
-
-        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
-                .fallbackToDestructiveMigrationFrom(1, 4, 81)
-                .build();
-
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-        assertThat(config.isMigrationRequiredFrom(1), is(false));
-        assertThat(config.isMigrationRequiredFrom(4), is(false));
-        assertThat(config.isMigrationRequiredFrom(81), is(false));
-    }
-
-    @Test
-    public void isMigrationRequiredFrom_fallBackToDestFromCalled_trueForNonProvidedValues() {
-        Context context = mock(Context.class);
-
-        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
-                .fallbackToDestructiveMigrationFrom(1, 4, 81)
-                .build();
-
-        DatabaseConfiguration config = ((BuilderTest_TestDatabase_Impl) db).mConfig;
-        assertThat(config.isMigrationRequiredFrom(2), is(true));
-        assertThat(config.isMigrationRequiredFrom(3), is(true));
-        assertThat(config.isMigrationRequiredFrom(73), is(true));
-    }
-
-    @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(@NonNull SupportSQLiteDatabase database) {
-        }
-    }
-
-}
diff --git a/android/arch/persistence/room/BuilderTest_TestDatabase_Impl.java b/android/arch/persistence/room/BuilderTest_TestDatabase_Impl.java
deleted file mode 100644
index d261454..0000000
--- a/android/arch/persistence/room/BuilderTest_TestDatabase_Impl.java
+++ /dev/null
@@ -1,38 +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.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/android/arch/persistence/room/ColumnInfo.java b/android/arch/persistence/room/ColumnInfo.java
deleted file mode 100644
index 32b5818..0000000
--- a/android/arch/persistence/room/ColumnInfo.java
+++ /dev/null
@@ -1,160 +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.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, the value defaults to {@link #UNDEFINED} and 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. This is either {@link #UNDEFINED}, {@link #TEXT},
-     * {@link #INTEGER}, {@link #REAL}, or {@link #BLOB}.
-     */
-    @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;
-
-    /**
-     * The collation sequence for the column, which will be used when constructing the database.
-     * <p>
-     * The default value is {@link #UNSPECIFIED}. In that case, Room does not add any
-     * collation sequence to the column, and SQLite treats it like {@link #BINARY}.
-     *
-     * @return The collation sequence of the column. This is either {@link #UNSPECIFIED},
-     * {@link #BINARY}, {@link #NOCASE}, {@link #RTRIM}, {@link #LOCALIZED} or {@link #UNICODE}.
-     */
-    @Collate int collate() default UNSPECIFIED;
-
-    /**
-     * 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.
-     *
-     * @see #typeAffinity()
-     */
-    int UNDEFINED = 1;
-    /**
-     * Column affinity constant for strings.
-     *
-     * @see #typeAffinity()
-     */
-    int TEXT = 2;
-    /**
-     * Column affinity constant for integers or booleans.
-     *
-     * @see #typeAffinity()
-     */
-    int INTEGER = 3;
-    /**
-     * Column affinity constant for floats or doubles.
-     *
-     * @see #typeAffinity()
-     */
-    int REAL = 4;
-    /**
-     * Column affinity constant for binary data.
-     *
-     * @see #typeAffinity()
-     */
-    int BLOB = 5;
-
-    /**
-     * The SQLite column type constants that can be used in {@link #typeAffinity()}
-     */
-    @IntDef({UNDEFINED, TEXT, INTEGER, REAL, BLOB})
-    @interface SQLiteTypeAffinity {
-    }
-
-    /**
-     * Collation sequence is not specified. The match will behave like {@link #BINARY}.
-     *
-     * @see #collate()
-     */
-    int UNSPECIFIED = 1;
-    /**
-     * Collation sequence for case-sensitive match.
-     *
-     * @see #collate()
-     */
-    int BINARY = 2;
-    /**
-     * Collation sequence for case-insensitive match.
-     *
-     * @see #collate()
-     */
-    int NOCASE = 3;
-    /**
-     * Collation sequence for case-sensitive match except that trailing space characters are
-     * ignored.
-     *
-     * @see #collate()
-     */
-    int RTRIM = 4;
-    /**
-     * Collation sequence that uses system's current locale.
-     *
-     * @see #collate()
-     */
-    int LOCALIZED = 5;
-    /**
-     * Collation sequence that uses Unicode Collation Algorithm.
-     *
-     * @see #collate()
-     */
-    int UNICODE = 6;
-
-    @IntDef({UNSPECIFIED, BINARY, NOCASE, RTRIM, LOCALIZED, UNICODE})
-    @interface Collate {
-    }
-}
diff --git a/android/arch/persistence/room/Dao.java b/android/arch/persistence/room/Dao.java
deleted file mode 100644
index ea205e6..0000000
--- a/android/arch/persistence/room/Dao.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.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/android/arch/persistence/room/Database.java b/android/arch/persistence/room/Database.java
deleted file mode 100644
index 14e722f..0000000
--- a/android/arch/persistence/room/Database.java
+++ /dev/null
@@ -1,96 +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.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/android/arch/persistence/room/DatabaseConfiguration.java b/android/arch/persistence/room/DatabaseConfiguration.java
deleted file mode 100644
index 42acc1d..0000000
--- a/android/arch/persistence/room/DatabaseConfiguration.java
+++ /dev/null
@@ -1,121 +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.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;
-import java.util.Set;
-
-/**
- * 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;
-
-    /**
-     * The collection of schema versions from which migrations aren't required.
-     */
-    private final Set<Integer> mMigrationNotRequiredFrom;
-
-    /**
-     * 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.
-     * @param migrationNotRequiredFrom The collection of schema versions from which migrations
-     *                                 aren't required.
-     *
-     * @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,
-            @Nullable Set<Integer> migrationNotRequiredFrom) {
-        this.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
-        this.context = context;
-        this.name = name;
-        this.migrationContainer = migrationContainer;
-        this.callbacks = callbacks;
-        this.allowMainThreadQueries = allowMainThreadQueries;
-        this.requireMigration = requireMigration;
-        this.mMigrationNotRequiredFrom = migrationNotRequiredFrom;
-    }
-
-    /**
-     * Returns whether a migration is required from the specified version.
-     *
-     * @param version  The schema version.
-     * @return True if a valid migration is required, false otherwise.
-     */
-    public boolean isMigrationRequiredFrom(int version) {
-        // Migrations are required from this version if we generally require migrations AND EITHER
-        // there are no exceptions OR the supplied version is not one of the exceptions.
-        return requireMigration
-                && (mMigrationNotRequiredFrom == null
-                || !mMigrationNotRequiredFrom.contains(version));
-
-    }
-}
diff --git a/android/arch/persistence/room/Delete.java b/android/arch/persistence/room/Delete.java
deleted file mode 100644
index 678b743..0000000
--- a/android/arch/persistence/room/Delete.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.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/android/arch/persistence/room/Embedded.java b/android/arch/persistence/room/Embedded.java
deleted file mode 100644
index 781f026..0000000
--- a/android/arch/persistence/room/Embedded.java
+++ /dev/null
@@ -1,91 +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.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/android/arch/persistence/room/EmptyResultSetException.java b/android/arch/persistence/room/EmptyResultSetException.java
deleted file mode 100644
index 0f2d281..0000000
--- a/android/arch/persistence/room/EmptyResultSetException.java
+++ /dev/null
@@ -1,31 +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.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/android/arch/persistence/room/Entity.java b/android/arch/persistence/room/Entity.java
deleted file mode 100644
index 94ca3bf..0000000
--- a/android/arch/persistence/room/Entity.java
+++ /dev/null
@@ -1,123 +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.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>
- * If a field is {@code transient}, it is automatically ignored <b>unless</b> it is annotated with
- * {@link ColumnInfo}, {@link Embedded} or {@link Relation}.
- * <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/android/arch/persistence/room/EntityDeletionOrUpdateAdapter.java b/android/arch/persistence/room/EntityDeletionOrUpdateAdapter.java
deleted file mode 100644
index 373b122..0000000
--- a/android/arch/persistence/room/EntityDeletionOrUpdateAdapter.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.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.
-     */
-    @Override
-    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/android/arch/persistence/room/EntityInsertionAdapter.java b/android/arch/persistence/room/EntityInsertionAdapter.java
deleted file mode 100644
index 6cfa332..0000000
--- a/android/arch/persistence/room/EntityInsertionAdapter.java
+++ /dev/null
@@ -1,251 +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.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/android/arch/persistence/room/ForeignKey.java b/android/arch/persistence/room/ForeignKey.java
deleted file mode 100644
index 3ba632b..0000000
--- a/android/arch/persistence/room/ForeignKey.java
+++ /dev/null
@@ -1,166 +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.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/android/arch/persistence/room/Ignore.java b/android/arch/persistence/room/Ignore.java
deleted file mode 100644
index 6effc33..0000000
--- a/android/arch/persistence/room/Ignore.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.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/android/arch/persistence/room/Index.java b/android/arch/persistence/room/Index.java
deleted file mode 100644
index b814efd..0000000
--- a/android/arch/persistence/room/Index.java
+++ /dev/null
@@ -1,74 +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.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/android/arch/persistence/room/Insert.java b/android/arch/persistence/room/Insert.java
deleted file mode 100644
index 802dd96..0000000
--- a/android/arch/persistence/room/Insert.java
+++ /dev/null
@@ -1,59 +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.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/android/arch/persistence/room/InvalidationTracker.java b/android/arch/persistence/room/InvalidationTracker.java
deleted file mode 100644
index b31dc13..0000000
--- a/android/arch/persistence/room/InvalidationTracker.java
+++ /dev/null
@@ -1,668 +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.arch.persistence.room;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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 = 0;
-
-    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("`")
-                .append("room_table_modification_trigger_")
-                .append(tableName)
-                .append("_")
-                .append(triggerType)
-                .append("`");
-    }
-
-    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(@NonNull 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)) {
-            ArchTaskExecutor.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(@NonNull final Observer observer) {
-        ObserverWrapper wrapper;
-        synchronized (mObserverMap) {
-            wrapper = mObserverMap.remove(observer);
-        }
-        if (wrapper != null && mObservedTableTracker.onRemoved(wrapper.mTableIds)) {
-            ArchTaskExecutor.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 (!mPendingRefresh.compareAndSet(true, false)) {
-                    // no pending refresh
-                    return;
-                }
-
-                if (mDatabase.inTransaction()) {
-                    // current thread is in a transaction. when it ends, it will invoke
-                    // refreshRunnable again. mPendingRefresh is left as false on purpose
-                    // so that the last transaction can flip it on again.
-                    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)) {
-            ArchTaskExecutor.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/android/arch/persistence/room/InvalidationTrackerTest.java b/android/arch/persistence/room/InvalidationTrackerTest.java
deleted file mode 100644
index d7474fd..0000000
--- a/android/arch/persistence/room/InvalidationTrackerTest.java
+++ /dev/null
@@ -1,342 +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.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 - disabled due to flakiness b/65257997
-    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/android/arch/persistence/room/InvalidationTrackerTrojan.java b/android/arch/persistence/room/InvalidationTrackerTrojan.java
deleted file mode 100644
index 5fa15ac..0000000
--- a/android/arch/persistence/room/InvalidationTrackerTrojan.java
+++ /dev/null
@@ -1,26 +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.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/android/arch/persistence/room/ObservedTableTrackerTest.java b/android/arch/persistence/room/ObservedTableTrackerTest.java
deleted file mode 100644
index ffddee9..0000000
--- a/android/arch/persistence/room/ObservedTableTrackerTest.java
+++ /dev/null
@@ -1,108 +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.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/android/arch/persistence/room/OnConflictStrategy.java b/android/arch/persistence/room/OnConflictStrategy.java
deleted file mode 100644
index 5217b61..0000000
--- a/android/arch/persistence/room/OnConflictStrategy.java
+++ /dev/null
@@ -1,56 +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.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/android/arch/persistence/room/PrimaryKey.java b/android/arch/persistence/room/PrimaryKey.java
deleted file mode 100644
index 9a8062c..0000000
--- a/android/arch/persistence/room/PrimaryKey.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.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/android/arch/persistence/room/Query.java b/android/arch/persistence/room/Query.java
deleted file mode 100644
index bd838e8..0000000
--- a/android/arch/persistence/room/Query.java
+++ /dev/null
@@ -1,108 +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.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/android/arch/persistence/room/RawQuery.java b/android/arch/persistence/room/RawQuery.java
deleted file mode 100644
index b41feab..0000000
--- a/android/arch/persistence/room/RawQuery.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.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 raw query method where you can pass the
- * query as a {@link String} or a
- * {@link android.arch.persistence.db.SupportSQLiteQuery SupportSQLiteQuery}.
- * <pre>
- * {@literal @}Dao
- * interface RawDao {
- *     {@literal @}RawQuery
- *     User getUser(String query);
- *     {@literal @}RawQuery
- *     User getUserViaQuery(SupportSQLiteQuery query);
- * }
- * User user = rawDao.getUser("SELECT * FROM User WHERE id = 3 LIMIT 1");
- * SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM User WHERE id = ? LIMIT 1",
- *         new Object[]{3});
- * User user2 = rawDao.getUserViaQuery(query);
- * </pre>
- * <p>
- * Room will generate the code based on the return type of the function and failure to
- * pass a proper query will result in a runtime failure or an undefined result.
- * <p>
- * If you know the query at compile time, you should always prefer {@link Query} since it validates
- * the query at compile time and also generates more efficient code since Room can compute the
- * query result at compile time (e.g. it does not need to account for possibly missing columns in
- * the response).
- * <p>
- * On the other hand, {@code RawQuery} serves as an escape hatch where you can build your own
- * SQL query at runtime but still use Room to convert it into objects.
- * <p>
- * {@code RawQuery} methods must return a non-void type. If you want to execute a raw query that
- * does not return any value, use {@link android.arch.persistence.room.RoomDatabase#query
- * RoomDatabase#query} methods.
- * <p>
- * <b>Observable Queries:</b>
- * <p>
- * {@code RawQuery} methods can return observable types but you need to specify which tables are
- * accessed in the query using the {@link #observedEntities()} field in the annotation.
- * <pre>
- * {@literal @}Dao
- * interface RawDao {
- *     {@literal @}RawQuery(observedEntities = User.class)
- *     LiveData&lt;List&lt;User>> getUsers(String query);
- * }
- * LiveData&lt;List&lt;User>> liveUsers = rawDao.getUsers("SELECT * FROM User ORDER BY name DESC");
- * </pre>
- * <b>Returning Pojos:</b>
- * <p>
- * RawQueries can also return plain old java objects, similar to {@link Query} methods.
- * <pre>
- * public class NameAndLastName {
- *     public final String name;
- *     public final String lastName;
- *
- *     public NameAndLastName(String name, String lastName) {
- *         this.name = name;
- *         this.lastName = lastName;
- *     }
- * }
- *
- * {@literal @}Dao
- * interface RawDao {
- *     {@literal @}RawQuery
- *     NameAndLastName getNameAndLastName(String query);
- * }
- * NameAndLastName result = rawDao.getNameAndLastName("SELECT * FROM User WHERE id = 3")
- * // or
- * NameAndLastName result = rawDao.getNameAndLastName("SELECT name, lastName FROM User WHERE id =
- * 3")
- * </pre>
- * <p>
- * <b>Pojos with Embedded Fields:</b>
- * <p>
- * {@code RawQuery} methods can return pojos that include {@link Embedded} fields as well.
- * <pre>
- * public class UserAndPet {
- *     {@literal @}Embedded
- *     public User user;
- *     {@literal @}Embedded
- *     public Pet pet;
- * }
- *
- * {@literal @}Dao
- * interface RawDao {
- *     {@literal @}RawQuery
- *     UserAndPet getUserAndPet(String query);
- * }
- * UserAndPet received = rawDao.getUserAndPet(
- *         "SELECT * FROM User, Pet WHERE User.id = Pet.userId LIMIT 1")
- * </pre>
- *
- * <b>Relations:</b>
- * <p>
- * {@code RawQuery} return types can also be objects with {@link Relation Relations}.
- * <pre>
- * public class UserAndAllPets {
- *     {@literal @}Embedded
- *     public User user;
- *     {@literal @}Relation(parentColumn = "id", entityColumn = "userId")
- *     public List&lt;Pet> pets;
- * }
- *
- * {@literal @}Dao
- * interface RawDao {
- *     {@literal @}RawQuery
- *     List&lt;UserAndAllPets> getUsersAndAllPets(String query);
- * }
- * List&lt;UserAndAllPets> result = rawDao.getUsersAndAllPets("SELECT * FROM users");
- * </pre>
- */
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.CLASS)
-public @interface RawQuery {
-    /**
-     * Denotes the list of entities which are accessed in the provided query and should be observed
-     * for invalidation if the query is observable.
-     * <p>
-     * The listed classes should be {@link Entity Entities} that are linked from the containing
-     * {@link Database}.
-     * <p>
-     * Providing this field in a non-observable query has no impact.
-     * <pre>
-     * {@literal @}Dao
-     * interface RawDao {
-     *     {@literal @}RawQuery(observedEntities = User.class)
-     *     LiveData&lt;List&lt;User>> getUsers(String query);
-     * }
-     * LiveData&lt;List&lt;User>> liveUsers = rawDao.getUsers("select * from User ORDER BY name
-     * DESC");
-     * </pre>
-     *
-     * @return List of entities that should invalidate the query if changed.
-     */
-    Class[] observedEntities() default {};
-}
diff --git a/android/arch/persistence/room/Relation.java b/android/arch/persistence/room/Relation.java
deleted file mode 100644
index d55bbfe..0000000
--- a/android/arch/persistence/room/Relation.java
+++ /dev/null
@@ -1,142 +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.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 {
- *     {@literal @} PrimaryKey
- *     int id;
- *     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")
- *     public List&lt;UserNameAndAllPets&gt; loadUserAndPets();
- * }
- * </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&lt;PetNameAndId&gt; pets;
- * }
- * {@literal @}Dao
- * public interface UserPetDao {
- *     {@literal @}Query("SELECT * from User")
- *     public List&lt;UserAllPets&gt; loadUserAndPets();
- * }
- * </pre>
- * <p>
- * In the example above, {@code PetNameAndId} is a regular Pojo 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&lt;String&gt; 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}.
- * <p>
- * 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/android/arch/persistence/room/Room.java b/android/arch/persistence/room/Room.java
deleted file mode 100644
index 9b168fc..0000000
--- a/android/arch/persistence/room/Room.java
+++ /dev/null
@@ -1,102 +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.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")
-    @NonNull
-    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 build 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.
-     */
-    @NonNull
-    public static <T extends RoomDatabase> RoomDatabase.Builder<T> inMemoryDatabaseBuilder(
-            @NonNull Context context, @NonNull Class<T> klass) {
-        return new RoomDatabase.Builder<>(context, klass, null);
-    }
-
-    @SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
-    @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/android/arch/persistence/room/RoomDatabase.java b/android/arch/persistence/room/RoomDatabase.java
deleted file mode 100644
index db7af1d..0000000
--- a/android/arch/persistence/room/RoomDatabase.java
+++ /dev/null
@@ -1,655 +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.arch.persistence.room;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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.HashSet;
-import java.util.List;
-import java.util.Set;
-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";
-    /**
-     * Unfortunately, we cannot read this value so we are only setting it to the SQLite default.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static final int MAX_BIND_PARAMETER_CNT = 999;
-    // 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(@NonNull 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.
-     */
-    @NonNull
-    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.
-     */
-    @NonNull
-    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.
-     */
-    @NonNull
-    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
-     */
-    @SuppressWarnings("WeakerAccess")
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    // used in generated code
-    public void assertNotMainThread() {
-        if (mAllowMainThreadQueries) {
-            return;
-        }
-        if (ArchTaskExecutor.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(@NonNull 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();
-        if (!inTransaction()) {
-            // enqueue refresh only if we are NOT in a transaction. Otherwise, wait for the last
-            // endTransaction call to do it.
-            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(@NonNull 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(@NonNull 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(@NonNull 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.
-     */
-    @NonNull
-    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()
-     */
-    @SuppressWarnings("WeakerAccess")
-    public boolean inTransaction() {
-        return mOpenHelper.getWritableDatabase().inTransaction();
-    }
-
-    /**
-     * Builder for RoomDatabase.
-     *
-     * @param <T> The type of the abstract database class.
-     */
-    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 mAllowMainThreadQueries;
-        private boolean mRequireMigration;
-        /**
-         * Migrations, mapped by from-to pairs.
-         */
-        private final MigrationContainer mMigrationContainer;
-        private Set<Integer> mMigrationsNotRequiredFrom;
-        /**
-         * Keeps track of {@link Migration#startVersion}s and {@link Migration#endVersion}s added in
-         * {@link #addMigrations(Migration...)} for later validation that makes those versions don't
-         * match any versions passed to {@link #fallbackToDestructiveMigrationFrom(Integer...)}.
-         */
-        private Set<Integer> mMigrationStartAndEndVersions;
-
-        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
-         */
-        @NonNull
-        public Builder<T> openHelperFactory(@Nullable 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
-         */
-        @NonNull
-        public Builder<T> addMigrations(@NonNull  Migration... migrations) {
-            if (mMigrationStartAndEndVersions == null) {
-                mMigrationStartAndEndVersions = new HashSet<>();
-            }
-            for (Migration migration: migrations) {
-                mMigrationStartAndEndVersions.add(migration.startVersion);
-                mMigrationStartAndEndVersions.add(migration.endVersion);
-            }
-
-            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
-         */
-        @NonNull
-        public Builder<T> allowMainThreadQueries() {
-            mAllowMainThreadQueries = true;
-            return this;
-        }
-
-        /**
-         * Allows Room to destructively recreate database tables if {@link Migration}s that would
-         * migrate old database schemas to the latest schema version are not found.
-         * <p>
-         * 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
-         */
-        @NonNull
-        public Builder<T> fallbackToDestructiveMigration() {
-            mRequireMigration = false;
-            return this;
-        }
-
-        /**
-         * Informs Room that it is allowed to destructively recreate database tables from specific
-         * starting schema versions.
-         * <p>
-         * This functionality is the same as that provided by
-         * {@link #fallbackToDestructiveMigration()}, except that this method allows the
-         * specification of a set of schema versions for which destructive recreation is allowed.
-         * <p>
-         * Using this method is preferable to {@link #fallbackToDestructiveMigration()} if you want
-         * to allow destructive migrations from some schema versions while still taking advantage
-         * of exceptions being thrown due to unintentionally missing migrations.
-         * <p>
-         * Note: No versions passed to this method may also exist as either starting or ending
-         * versions in the {@link Migration}s provided to {@link #addMigrations(Migration...)}. If a
-         * version passed to this method is found as a starting or ending version in a Migration, an
-         * exception will be thrown.
-         *
-         * @param startVersions The set of schema versions from which Room should use a destructive
-         *                      migration.
-         * @return this
-         */
-        @NonNull
-        public Builder<T> fallbackToDestructiveMigrationFrom(Integer... startVersions) {
-            if (mMigrationsNotRequiredFrom == null) {
-                mMigrationsNotRequiredFrom = new HashSet<>();
-            }
-            Collections.addAll(mMigrationsNotRequiredFrom, startVersions);
-            return this;
-        }
-
-        /**
-         * Adds a {@link Callback} to this database.
-         *
-         * @param callback The callback.
-         * @return this
-         */
-        @NonNull
-        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.
-         */
-        @NonNull
-        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 (mMigrationStartAndEndVersions != null && mMigrationsNotRequiredFrom != null) {
-                for (Integer version : mMigrationStartAndEndVersions) {
-                    if (mMigrationsNotRequiredFrom.contains(version)) {
-                        throw new IllegalArgumentException(
-                                "Inconsistency detected. A Migration was supplied to "
-                                        + "addMigration(Migration... migrations) that has a start "
-                                        + "or end version equal to a start version supplied to "
-                                        + "fallbackToDestructiveMigrationFrom(Integer ... "
-                                        + "startVersions). Start version: "
-                                        + version);
-                    }
-                }
-            }
-
-            if (mFactory == null) {
-                mFactory = new FrameworkSQLiteOpenHelperFactory();
-            }
-            DatabaseConfiguration configuration =
-                    new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
-                            mCallbacks, mAllowMainThreadQueries, mRequireMigration,
-                            mMigrationsNotRequiredFrom);
-            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(@NonNull 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}.
-         */
-        @SuppressWarnings("WeakerAccess")
-        @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) {
-                    final int targetVersion = targetNodes.keyAt(i);
-                    final boolean shouldAddToPath;
-                    if (upgrade) {
-                        shouldAddToPath = targetVersion <= end && targetVersion > start;
-                    } else {
-                        shouldAddToPath = targetVersion >= end && targetVersion < start;
-                    }
-                    if (shouldAddToPath) {
-                        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/android/arch/persistence/room/RoomMasterTable.java b/android/arch/persistence/room/RoomMasterTable.java
deleted file mode 100644
index 621845d..0000000
--- a/android/arch/persistence/room/RoomMasterTable.java
+++ /dev/null
@@ -1,55 +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.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/android/arch/persistence/room/RoomOpenHelper.java b/android/arch/persistence/room/RoomOpenHelper.java
deleted file mode 100644
index aad6895..0000000
--- a/android/arch/persistence/room/RoomOpenHelper.java
+++ /dev/null
@@ -1,171 +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.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;
-    /**
-     * Room v1 had a bug where the hash was not consistent if fields are reordered.
-     * The new has fixes it but we still need to accept the legacy hash.
-     */
-    @NonNull // b/64290754
-    private final String mLegacyHash;
-
-    public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate,
-            @NonNull String identityHash, @NonNull String legacyHash) {
-        super(delegate.version);
-        mConfiguration = configuration;
-        mDelegate = delegate;
-        mIdentityHash = identityHash;
-        mLegacyHash = legacyHash;
-    }
-
-    @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.isMigrationRequiredFrom(oldVersion)) {
-                mDelegate.dropAllTables(db);
-                mDelegate.createAllTables(db);
-            } else {
-                throw new IllegalStateException("A migration from " + oldVersion + " to "
-                        + newVersion + " was required but not found. Please provide the "
-                        + "necessary Migration path via "
-                        + "RoomDatabase.Builder.addMigration(Migration ...) or allow for "
-                        + "destructive migrations via one of the "
-                        + "RoomDatabase.Builder.fallbackToDestructiveMigration* methods.");
-            }
-        }
-    }
-
-    @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) && !mLegacyHash.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 {
-        public final int version;
-
-        public Delegate(int version) {
-            this.version = version;
-        }
-
-        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/android/arch/persistence/room/RoomSQLiteQuery.java b/android/arch/persistence/room/RoomSQLiteQuery.java
deleted file mode 100644
index a10cc52..0000000
--- a/android/arch/persistence/room/RoomSQLiteQuery.java
+++ /dev/null
@@ -1,249 +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.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() {
-        // 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/android/arch/persistence/room/RoomSQLiteQueryTest.java b/android/arch/persistence/room/RoomSQLiteQueryTest.java
deleted file mode 100644
index e7a8644..0000000
--- a/android/arch/persistence/room/RoomSQLiteQueryTest.java
+++ /dev/null
@@ -1,149 +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.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/android/arch/persistence/room/RoomWarnings.java b/android/arch/persistence/room/RoomWarnings.java
deleted file mode 100644
index f05e6be..0000000
--- a/android/arch/persistence/room/RoomWarnings.java
+++ /dev/null
@@ -1,136 +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.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";
-
-    /**
-     * Reported when a Pojo has multiple constructors, one of which is a no-arg constructor. Room
-     * will pick that one by default but will print this warning in case the constructor choice is
-     * important. You can always guide Room to use the right constructor using the @Ignore
-     * annotation.
-     */
-    public static final String DEFAULT_CONSTRUCTOR = "ROOM_DEFAULT_CONSTRUCTOR";
-
-    /**
-     * Reported when a @Query method returns a Pojo that has relations but the method is not
-     * annotated with @Transaction. Relations are run as separate queries and if the query is not
-     * run inside a transaction, it might return inconsistent results from the database.
-     */
-    public static final String RELATION_QUERY_WITHOUT_TRANSACTION =
-            "ROOM_RELATION_QUERY_WITHOUT_TRANSACTION";
-}
diff --git a/android/arch/persistence/room/Rx2Room.java b/android/arch/persistence/room/Rx2Room.java
deleted file mode 100644
index da474fd..0000000
--- a/android/arch/persistence/room/Rx2Room.java
+++ /dev/null
@@ -1,5 +0,0 @@
-// mock rx2 helper
-package android.arch.persistence.room;
-
-class RxRoom {
-}
diff --git a/android/arch/persistence/room/RxRoom.java b/android/arch/persistence/room/RxRoom.java
deleted file mode 100644
index 285b3f8..0000000
--- a/android/arch/persistence/room/RxRoom.java
+++ /dev/null
@@ -1,189 +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.arch.persistence.room;
-
-import android.arch.core.executor.ArchTaskExecutor;
-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);
-                    ArchTaskExecutor.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/android/arch/persistence/room/RxRoomTest.java b/android/arch/persistence/room/RxRoomTest.java
deleted file mode 100644
index 502eaa1..0000000
--- a/android/arch/persistence/room/RxRoomTest.java
+++ /dev/null
@@ -1,179 +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.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/android/arch/persistence/room/SharedSQLiteStatement.java b/android/arch/persistence/room/SharedSQLiteStatement.java
deleted file mode 100644
index 6b1f8ea..0000000
--- a/android/arch/persistence/room/SharedSQLiteStatement.java
+++ /dev/null
@@ -1,100 +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.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/android/arch/persistence/room/SharedSQLiteStatementTest.java b/android/arch/persistence/room/SharedSQLiteStatementTest.java
deleted file mode 100644
index 4e715e9..0000000
--- a/android/arch/persistence/room/SharedSQLiteStatementTest.java
+++ /dev/null
@@ -1,126 +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.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/android/arch/persistence/room/SkipQueryVerification.java b/android/arch/persistence/room/SkipQueryVerification.java
deleted file mode 100644
index dc2ded3..0000000
--- a/android/arch/persistence/room/SkipQueryVerification.java
+++ /dev/null
@@ -1,42 +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.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/android/arch/persistence/room/Transaction.java b/android/arch/persistence/room/Transaction.java
deleted file mode 100644
index 3b6ede9..0000000
--- a/android/arch/persistence/room/Transaction.java
+++ /dev/null
@@ -1,84 +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.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} class as a transaction method.
- * <p>
- * When used on a non-abstract method of an abstract {@link Dao} class,
- * the derived implementation of the method will execute the super method in a database transaction.
- * All the parameters and return types are preserved. The transaction will be marked as successful
- * unless an exception is thrown in the method body.
- * <p>
- * Example:
- * <pre>
- * {@literal @}Dao
- * public abstract class ProductDao {
- *    {@literal @}Insert
- *     public abstract void insert(Product product);
- *    {@literal @}Delete
- *     public abstract void delete(Product product);
- *    {@literal @}Transaction
- *     public void insertAndDeleteInTransaction(Product newProduct, Product oldProduct) {
- *         // Anything inside this method runs in a single transaction.
- *         insert(newProduct);
- *         delete(oldProduct);
- *     }
- * }
- * </pre>
- * <p>
- * When used on a {@link Query} method that has a {@code Select} statement, the generated code for
- * the Query will be run in a transaction. There are 2 main cases where you may want to do that:
- * <ol>
- *     <li>If the result of the query is fairly big, it is better to run it inside a transaction
- *     to receive a consistent result. Otherwise, if the query result does not fit into a single
- *     {@link android.database.CursorWindow CursorWindow}, the query result may be corrupted due to
- *     changes in the database in between cursor window swaps.
- *     <li>If the result of the query is a Pojo with {@link Relation} fields, these fields are
- *     queried separately. To receive consistent results between these queries, you probably want
- *     to run them in a single transaction.
- * </ol>
- * Example:
- * <pre>
- * class ProductWithReviews extends Product {
- *     {@literal @}Relation(parentColumn = "id", entityColumn = "productId", entity = Review.class)
- *     public List&lt;Review> reviews;
- * }
- * {@literal @}Dao
- * public interface ProductDao {
- *     {@literal @}Transaction {@literal @}Query("SELECT * from products")
- *     public List&lt;ProductWithReviews> loadAll();
- * }
- * </pre>
- * If the query is an async query (e.g. returns a {@link android.arch.lifecycle.LiveData LiveData}
- * or RxJava Flowable, the transaction is properly handled when the query is run, not when the
- * method is called.
- * <p>
- * Putting this annotation on an {@link Insert}, {@link Update} or {@link Delete} method has no
- * impact because they are always run inside a transaction. Similarly, if it is annotated with
- * {@link Query} but runs an update or delete statement, it is automatically wrapped in a
- * transaction.
- */
-@Target({ElementType.METHOD})
-@Retention(RetentionPolicy.CLASS)
-public @interface Transaction {
-}
diff --git a/android/arch/persistence/room/TypeConverter.java b/android/arch/persistence/room/TypeConverter.java
deleted file mode 100644
index 850481c..0000000
--- a/android/arch/persistence/room/TypeConverter.java
+++ /dev/null
@@ -1,52 +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.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 @TypeConverter 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/android/arch/persistence/room/TypeConverters.java b/android/arch/persistence/room/TypeConverters.java
deleted file mode 100644
index 7f7e6ab..0000000
--- a/android/arch/persistence/room/TypeConverters.java
+++ /dev/null
@@ -1,50 +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.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/android/arch/persistence/room/Update.java b/android/arch/persistence/room/Update.java
deleted file mode 100644
index 670a085..0000000
--- a/android/arch/persistence/room/Update.java
+++ /dev/null
@@ -1,40 +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.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/android/arch/persistence/room/integration/testapp/CustomerViewModel.java b/android/arch/persistence/room/integration/testapp/CustomerViewModel.java
deleted file mode 100644
index d8cd8d4..0000000
--- a/android/arch/persistence/room/integration/testapp/CustomerViewModel.java
+++ /dev/null
@@ -1,110 +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.arch.persistence.room.integration.testapp;
-
-import android.app.Application;
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.lifecycle.AndroidViewModel;
-import android.arch.lifecycle.LiveData;
-import android.arch.paging.DataSource;
-import android.arch.paging.LivePagedListBuilder;
-import android.arch.paging.PagedList;
-import android.arch.persistence.room.Room;
-import android.arch.persistence.room.integration.testapp.database.Customer;
-import android.arch.persistence.room.integration.testapp.database.LastNameAscCustomerDataSource;
-import android.arch.persistence.room.integration.testapp.database.SampleDatabase;
-import android.support.annotation.WorkerThread;
-
-import java.util.UUID;
-
-/**
- * Sample database-backed view model of Customers
- */
-public class CustomerViewModel extends AndroidViewModel {
-    private SampleDatabase mDatabase;
-    private LiveData<PagedList<Customer>> mLiveCustomerList;
-
-    public CustomerViewModel(Application application) {
-        super(application);
-        createDb();
-    }
-
-    private void createDb() {
-        mDatabase = Room.databaseBuilder(this.getApplication(),
-                SampleDatabase.class, "customerDatabase").build();
-
-        ArchTaskExecutor.getInstance().executeOnDiskIO(new Runnable() {
-            @Override
-            public void run() {
-                // fill with some simple data
-                int customerCount = mDatabase.getCustomerDao().countCustomers();
-                if (customerCount == 0) {
-                    Customer[] initialCustomers = new Customer[10];
-                    for (int i = 0; i < 10; i++) {
-                        initialCustomers[i] = createCustomer();
-                    }
-                    mDatabase.getCustomerDao().insertAll(initialCustomers);
-                }
-
-            }
-        });
-    }
-
-    @WorkerThread
-    private Customer createCustomer() {
-        Customer customer = new Customer();
-        customer.setName(UUID.randomUUID().toString());
-        customer.setLastName(UUID.randomUUID().toString());
-        return customer;
-    }
-
-    void insertCustomer() {
-        ArchTaskExecutor.getInstance().executeOnDiskIO(new Runnable() {
-            @Override
-            public void run() {
-                mDatabase.getCustomerDao().insert(createCustomer());
-            }
-        });
-    }
-
-    private static <K> LiveData<PagedList<Customer>> getLivePagedList(
-            K initialLoadKey, DataSource.Factory<K, Customer> dataSourceFactory) {
-        PagedList.Config config = new PagedList.Config.Builder()
-                .setPageSize(10)
-                .setEnablePlaceholders(false)
-                .build();
-        return new LivePagedListBuilder<>(dataSourceFactory, config)
-                .setInitialLoadKey(initialLoadKey)
-                .build();
-    }
-
-    LiveData<PagedList<Customer>> getLivePagedList(int position) {
-        if (mLiveCustomerList == null) {
-            mLiveCustomerList =
-                    getLivePagedList(position, mDatabase.getCustomerDao().loadPagedAgeOrder());
-        }
-        return mLiveCustomerList;
-    }
-
-    LiveData<PagedList<Customer>> getLivePagedList(String key) {
-        if (mLiveCustomerList == null) {
-            mLiveCustomerList =
-                    getLivePagedList(key, LastNameAscCustomerDataSource.factory(mDatabase));
-        }
-        return mLiveCustomerList;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/PKeyTestDatabase.java b/android/arch/persistence/room/integration/testapp/PKeyTestDatabase.java
deleted file mode 100644
index 63b9507..0000000
--- a/android/arch/persistence/room/integration/testapp/PKeyTestDatabase.java
+++ /dev/null
@@ -1,89 +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.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 android.arch.persistence.room.integration.testapp.vo.IntegerPKeyEntity;
-import android.arch.persistence.room.integration.testapp.vo.ObjectPKeyEntity;
-
-import java.util.List;
-
-@Database(entities = {IntAutoIncPKeyEntity.class, IntegerAutoIncPKeyEntity.class,
-        ObjectPKeyEntity.class, IntegerPKeyEntity.class}, version = 1,
-        exportSchema = false)
-public abstract class PKeyTestDatabase extends RoomDatabase {
-    public abstract IntPKeyDao intPKeyDao();
-    public abstract IntegerAutoIncPKeyDao integerAutoIncPKeyDao();
-    public abstract ObjectPKeyDao objectPKeyDao();
-    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 IntegerAutoIncPKeyDao {
-        @Insert
-        void insertMe(IntegerAutoIncPKeyEntity item);
-
-        @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);
-    }
-
-    @Dao
-    public interface ObjectPKeyDao {
-        @Insert
-        void insertMe(ObjectPKeyEntity item);
-    }
-
-    @Dao
-    public interface IntegerPKeyDao {
-        @Insert
-        void insertMe(IntegerPKeyEntity item);
-
-        @Query("select * from IntegerPKeyEntity")
-        List<IntegerPKeyEntity> loadAll();
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/PagedListCustomerAdapter.java b/android/arch/persistence/room/integration/testapp/PagedListCustomerAdapter.java
deleted file mode 100644
index b5f635c..0000000
--- a/android/arch/persistence/room/integration/testapp/PagedListCustomerAdapter.java
+++ /dev/null
@@ -1,114 +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.arch.persistence.room.integration.testapp;
-
-import android.arch.paging.PagedList;
-import android.arch.paging.PagedListAdapter;
-import android.arch.persistence.room.integration.testapp.database.Customer;
-import android.arch.persistence.room.integration.testapp.database.LastNameAscCustomerDataSource;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-/**
- * Sample adapter which uses a PagedListAdapterHelper.
- */
-class PagedListCustomerAdapter extends PagedListAdapter<Customer, RecyclerView.ViewHolder> {
-    private RecyclerView mRecyclerView;
-    private boolean mSetObserved;
-    private int mScrollToPosition = -1;
-    private String mScrollToKey = null;
-
-    PagedListCustomerAdapter() {
-        super(Customer.DIFF_CALLBACK);
-    }
-
-    void setScrollToPosition(int position) {
-        mScrollToPosition = position;
-    }
-
-    void setScrollToKey(String key) {
-        mScrollToKey = key;
-    }
-
-    @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) {
-        Customer customer = getItem(position);
-
-        if (customer != null) {
-            ((TextView) (holder.itemView)).setText(customer.getId() + " " + customer.getLastName());
-        } else {
-            ((TextView) (holder.itemView)).setText(R.string.loading);
-        }
-    }
-
-    private static int findKeyInPagedList(@NonNull String key, @NonNull PagedList<Customer> list) {
-        for (int i = 0; i < list.size(); i++) {
-            @Nullable Customer customer = list.get(i);
-            if (customer != null
-                    && LastNameAscCustomerDataSource.getKeyStatic(customer).equals(key)) {
-                return i;
-            }
-        }
-        return 0; // couldn't find, fall back to 0 - could alternately search with comparator
-    }
-
-    @Override
-    public void setList(PagedList<Customer> pagedList) {
-        super.setList(pagedList);
-
-        if (pagedList != null) {
-            final boolean firstSet = !mSetObserved;
-            mSetObserved = true;
-
-            if (firstSet
-                    && mRecyclerView != null
-                    && (mScrollToPosition >= 0 || mScrollToKey != null)) {
-                int localScrollToPosition;
-                if (mScrollToKey != null) {
-                    localScrollToPosition = findKeyInPagedList(mScrollToKey, pagedList);
-                    mScrollToKey = null;
-                } else {
-                    // if there's 20 items unloaded items (without placeholders holding the spots)
-                    // at the beginning of list, we subtract 20 from saved position
-                    localScrollToPosition = mScrollToPosition - pagedList.getPositionOffset();
-                }
-                mRecyclerView.scrollToPosition(localScrollToPosition);
-            }
-        }
-    }
-
-    @Override
-    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
-        mRecyclerView = recyclerView;
-    }
-
-    @Override
-    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
-        mRecyclerView = null;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/RoomKeyedPagedListActivity.java b/android/arch/persistence/room/integration/testapp/RoomKeyedPagedListActivity.java
deleted file mode 100644
index 17ac4ce..0000000
--- a/android/arch/persistence/room/integration/testapp/RoomKeyedPagedListActivity.java
+++ /dev/null
@@ -1,27 +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.arch.persistence.room.integration.testapp;
-
-/**
- * Sample PagedList activity which uses Room.
- */
-public class RoomKeyedPagedListActivity extends RoomPagedListActivity {
-    @Override
-    protected boolean useKeyedQuery() {
-        return true;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java b/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
deleted file mode 100644
index 0c79aee..0000000
--- a/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
+++ /dev/null
@@ -1,116 +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.arch.persistence.room.integration.testapp;
-
-import android.arch.lifecycle.LiveData;
-import android.arch.lifecycle.Observer;
-import android.arch.lifecycle.ViewModelProviders;
-import android.arch.paging.PagedList;
-import android.arch.persistence.room.integration.testapp.database.Customer;
-import android.arch.persistence.room.integration.testapp.database.LastNameAscCustomerDataSource;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.Button;
-
-/**
- * Sample PagedList activity which uses Room.
- */
-public class RoomPagedListActivity extends AppCompatActivity {
-
-    private RecyclerView mRecyclerView;
-    private PagedListCustomerAdapter mAdapter;
-
-    private static final String STRING_KEY = "STRING_KEY";
-    private static final String INT_KEY = "INT_KEY";
-
-    @Override
-    protected void onCreate(final Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_recycler_view);
-        final CustomerViewModel viewModel = ViewModelProviders.of(this)
-                .get(CustomerViewModel.class);
-
-        mRecyclerView = findViewById(R.id.recyclerview);
-        mAdapter = new PagedListCustomerAdapter();
-        mRecyclerView.setAdapter(mAdapter);
-
-        LiveData<PagedList<Customer>> livePagedList;
-        if (useKeyedQuery()) {
-            String key = null;
-            if (savedInstanceState != null) {
-                key = savedInstanceState.getString(STRING_KEY);
-                mAdapter.setScrollToKey(key);
-            }
-            livePagedList = viewModel.getLivePagedList(key);
-        } else {
-            int position = 0;
-            if (savedInstanceState != null) {
-                position = savedInstanceState.getInt(INT_KEY);
-                mAdapter.setScrollToPosition(position);
-            }
-            livePagedList = viewModel.getLivePagedList(position);
-        }
-        livePagedList.observe(this, new Observer<PagedList<Customer>>() {
-            @Override
-            public void onChanged(@Nullable PagedList<Customer> items) {
-                mAdapter.setList(items);
-            }
-        });
-        final Button button = findViewById(R.id.addButton);
-        button.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                viewModel.insertCustomer();
-            }
-        });
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        PagedList<Customer> list = mAdapter.getCurrentList();
-        if (list == null) {
-            // Can't find anything to restore
-            return;
-        }
-
-        LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
-        final int targetPosition = layoutManager.findFirstVisibleItemPosition();
-
-        if (useKeyedQuery()) {
-            Customer customer = list.get(targetPosition);
-            if (customer != null) {
-                String key = LastNameAscCustomerDataSource.getKeyStatic(customer);
-                outState.putString(STRING_KEY, key);
-            }
-        } else {
-            // NOTE: in the general case, we can't just rely on RecyclerView/LinearLayoutManager to
-            // preserve position, because of position offset which is present when using an
-            // uncounted, non-keyed source).
-            int absolutePosition = targetPosition + list.getPositionOffset();
-            outState.putInt(INT_KEY, absolutePosition);
-        }
-    }
-
-    protected boolean useKeyedQuery() {
-        return false;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/TestDatabase.java b/android/arch/persistence/room/integration/testapp/TestDatabase.java
deleted file mode 100644
index 98282ab..0000000
--- a/android/arch/persistence/room/integration/testapp/TestDatabase.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.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.FunnyNamedDao;
-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.RawDao;
-import android.arch.persistence.room.integration.testapp.dao.SchoolDao;
-import android.arch.persistence.room.integration.testapp.dao.SpecificDogDao;
-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.dao.WithClauseDao;
-import android.arch.persistence.room.integration.testapp.vo.BlobEntity;
-import android.arch.persistence.room.integration.testapp.vo.Day;
-import android.arch.persistence.room.integration.testapp.vo.FunnyNamedEntity;
-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;
-import java.util.HashSet;
-import java.util.Set;
-
-@Database(entities = {User.class, Pet.class, School.class, PetCouple.class, Toy.class,
-        BlobEntity.class, Product.class, FunnyNamedEntity.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();
-    public abstract SpecificDogDao getSpecificDogDao();
-    public abstract WithClauseDao getWithClauseDao();
-    public abstract FunnyNamedDao getFunnyNamedDao();
-    public abstract RawDao getRawDao();
-
-    @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();
-            }
-        }
-
-        @TypeConverter
-        public Set<Day> decomposeDays(int flags) {
-            Set<Day> result = new HashSet<>();
-            for (Day day : Day.values()) {
-                if ((flags & (1 << day.ordinal())) != 0) {
-                    result.add(day);
-                }
-            }
-            return result;
-        }
-
-        @TypeConverter
-        public int composeDays(Set<Day> days) {
-            int result = 0;
-            for (Day day : days) {
-                result |= 1 << day.ordinal();
-            }
-            return result;
-        }
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/BlobEntityDao.java b/android/arch/persistence/room/integration/testapp/dao/BlobEntityDao.java
deleted file mode 100644
index 212eba7..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/BlobEntityDao.java
+++ /dev/null
@@ -1,40 +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.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/android/arch/persistence/room/integration/testapp/dao/FunnyNamedDao.java b/android/arch/persistence/room/integration/testapp/dao/FunnyNamedDao.java
deleted file mode 100644
index 93b5e72..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/FunnyNamedDao.java
+++ /dev/null
@@ -1,50 +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.arch.persistence.room.integration.testapp.dao;
-
-import static android.arch.persistence.room.integration.testapp.vo.FunnyNamedEntity.COLUMN_ID;
-import static android.arch.persistence.room.integration.testapp.vo.FunnyNamedEntity.TABLE_NAME;
-
-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.FunnyNamedEntity;
-
-import java.util.List;
-
-@Dao
-public interface FunnyNamedDao {
-    String SELECT_ONE = "select * from \"" +  TABLE_NAME + "\" WHERE \"" + COLUMN_ID + "\" = :id";
-    @Insert
-    void insert(FunnyNamedEntity... entities);
-    @Delete
-    void delete(FunnyNamedEntity... entities);
-    @Update
-    void update(FunnyNamedEntity... entities);
-
-    @Query("select * from \"" +  TABLE_NAME + "\" WHERE \"" + COLUMN_ID + "\" IN (:ids)")
-    List<FunnyNamedEntity> loadAll(int... ids);
-
-    @Query(SELECT_ONE)
-    LiveData<FunnyNamedEntity> observableOne(int id);
-
-    @Query(SELECT_ONE)
-    FunnyNamedEntity load(int id);
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/PetCoupleDao.java b/android/arch/persistence/room/integration/testapp/dao/PetCoupleDao.java
deleted file mode 100644
index 4f7c4e2..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/PetCoupleDao.java
+++ /dev/null
@@ -1,33 +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.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/android/arch/persistence/room/integration/testapp/dao/PetDao.java b/android/arch/persistence/room/integration/testapp/dao/PetDao.java
deleted file mode 100644
index e3a45a0..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/PetDao.java
+++ /dev/null
@@ -1,58 +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.arch.persistence.room.integration.testapp.dao;
-
-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.Transaction;
-import android.arch.persistence.room.integration.testapp.vo.Pet;
-import android.arch.persistence.room.integration.testapp.vo.PetWithToyIds;
-
-import java.util.List;
-
-@Dao
-public interface PetDao {
-    @Insert(onConflict = OnConflictStrategy.REPLACE)
-    void insertOrReplace(Pet... pets);
-
-    @Insert
-    void insertAll(Pet[] pets);
-
-    @Query("SELECT COUNT(*) FROM Pet")
-    int count();
-
-    @Query("SELECT * FROM Pet ORDER BY Pet.mPetId ASC")
-    List<PetWithToyIds> allPetsWithToyIds();
-
-    @Delete
-    void delete(Pet pet);
-
-    @Query("SELECT mPetId FROM Pet")
-    int[] allIds();
-
-    @Transaction
-    default void deleteAndInsert(Pet oldPet, Pet newPet, boolean shouldFail) {
-        delete(oldPet);
-        if (shouldFail) {
-            throw new RuntimeException();
-        }
-        insertOrReplace(newPet);
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/ProductDao.java b/android/arch/persistence/room/integration/testapp/dao/ProductDao.java
deleted file mode 100644
index 417fb72..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/ProductDao.java
+++ /dev/null
@@ -1,30 +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.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/android/arch/persistence/room/integration/testapp/dao/RawDao.java b/android/arch/persistence/room/integration/testapp/dao/RawDao.java
deleted file mode 100644
index b4469c0..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/RawDao.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.dao;
-
-import android.arch.lifecycle.LiveData;
-import android.arch.persistence.db.SupportSQLiteQuery;
-import android.arch.persistence.room.ColumnInfo;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.RawQuery;
-import android.arch.persistence.room.integration.testapp.vo.NameAndLastName;
-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 java.util.Date;
-import java.util.List;
-
-@Dao
-public interface RawDao {
-    @RawQuery
-    User getUser(String query);
-    @RawQuery
-    UserAndAllPets getUserAndAllPets(String query);
-    @RawQuery
-    User getUser(SupportSQLiteQuery query);
-    @RawQuery
-    UserAndPet getUserAndPet(String query);
-    @RawQuery
-    NameAndLastName getUserNameAndLastName(String query);
-    @RawQuery(observedEntities = User.class)
-    NameAndLastName getUserNameAndLastName(SupportSQLiteQuery query);
-    @RawQuery
-    int count(String query);
-    @RawQuery
-    List<User> getUserList(String query);
-    @RawQuery
-    List<UserAndPet> getUserAndPetList(String query);
-    @RawQuery(observedEntities = User.class)
-    LiveData<User> getUserLiveData(String query);
-    @RawQuery
-    UserNameAndBirthday getUserAndBirthday(String query);
-    class UserNameAndBirthday {
-        @ColumnInfo(name = "mName")
-        public final String name;
-        @ColumnInfo(name = "mBirthday")
-        public final Date birthday;
-
-        public UserNameAndBirthday(String name, Date birthday) {
-            this.name = name;
-            this.birthday = birthday;
-        }
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java b/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
deleted file mode 100644
index 18e8d93..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/SchoolDao.java
+++ /dev/null
@@ -1,50 +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.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 mId, mName, manager_mName FROM School")
-    public abstract List<School> schoolAndManagerNames();
-
-    @Query("SELECT mId, 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 mId, address_lat, address_lng FROM School WHERE mId = :schoolId")
-    public abstract School loadCoordinatesAsSchool(int schoolId);
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/SpecificDogDao.java b/android/arch/persistence/room/integration/testapp/dao/SpecificDogDao.java
deleted file mode 100644
index 971826d..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/SpecificDogDao.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.arch.persistence.room.integration.testapp.dao;
-
-import android.arch.lifecycle.LiveData;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.Query;
-import android.arch.persistence.room.integration.testapp.vo.PetsToys;
-
-@Dao
-public interface SpecificDogDao {
-    @Query("SELECT 123 as petId")
-    LiveData<PetsToys> getSpecificDogsToys();
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/ToyDao.java b/android/arch/persistence/room/integration/testapp/dao/ToyDao.java
deleted file mode 100644
index 5d50a62..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/ToyDao.java
+++ /dev/null
@@ -1,27 +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.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/android/arch/persistence/room/integration/testapp/dao/UserDao.java b/android/arch/persistence/room/integration/testapp/dao/UserDao.java
deleted file mode 100644
index 7cb8b60..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/UserDao.java
+++ /dev/null
@@ -1,223 +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.arch.persistence.room.integration.testapp.dao;
-
-import android.arch.lifecycle.LiveData;
-import android.arch.paging.DataSource;
-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.Transaction;
-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.Day;
-import android.arch.persistence.room.integration.testapp.vo.NameAndLastName;
-import android.arch.persistence.room.integration.testapp.vo.User;
-import android.database.Cursor;
-
-import org.reactivestreams.Publisher;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
-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);
-
-    @Query("select * from user where custommm = :customField")
-    public abstract List<User> findByCustomField(String customField);
-
-    @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();
-
-    @Query("SELECT mAdmin from User where mId = :uid")
-    public abstract boolean isAdmin(int uid);
-
-    @Query("SELECT mAdmin from User where mId = :uid")
-    public abstract LiveData<Boolean> isAdminLiveData(int uid);
-
-    public void insertBothByRunnable(final User a, final User b) {
-        mDatabase.runInTransaction(new Runnable() {
-            @Override
-            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 DataSource.Factory<Integer, User> loadPagedByAge(int age);
-
-    // TODO: switch to PositionalDataSource once Room supports it
-    @Query("SELECT * FROM user ORDER BY mAge DESC")
-    public abstract DataSource.Factory<Integer, 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);
-
-    @Query("SELECT * FROM user WHERE (mWorkDays & :days) != 0")
-    public abstract List<User> findUsersByWorkDays(Set<Day> days);
-
-    // QueryLoader
-
-    @Query("SELECT COUNT(*) from user")
-    public abstract Integer getUserCount();
-
-    @Query("SELECT u.mName, u.mLastName from user u where mId = :id")
-    public abstract NameAndLastName getNameAndLastName(int id);
-
-    @Transaction
-    public void insertBothByAnnotation(final User a, final User b) {
-        insert(a);
-        insert(b);
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/dao/UserPetDao.java b/android/arch/persistence/room/integration/testapp/dao/UserPetDao.java
deleted file mode 100644
index 263417e..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/UserPetDao.java
+++ /dev/null
@@ -1,85 +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.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.UserAndPetAdoptionDates;
-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;
-
-import io.reactivex.Flowable;
-
-@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")
-    List<UserAndPetAdoptionDates> loadUserWithPetAdoptionDates();
-
-    @Query("SELECT * FROM User u where u.mId = :userId")
-    LiveData<UserAndAllPets> liveUserWithPets(int userId);
-
-    @Query("SELECT * FROM User u where u.mId = :userId")
-    Flowable<UserAndAllPets> flowableUserWithPets(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/android/arch/persistence/room/integration/testapp/dao/WithClauseDao.java b/android/arch/persistence/room/integration/testapp/dao/WithClauseDao.java
deleted file mode 100644
index 40098ed..0000000
--- a/android/arch/persistence/room/integration/testapp/dao/WithClauseDao.java
+++ /dev/null
@@ -1,53 +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.arch.persistence.room.integration.testapp.dao;
-
-import android.annotation.TargetApi;
-import android.arch.lifecycle.LiveData;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.Query;
-import android.os.Build;
-
-import java.util.List;
-
-@Dao
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public interface WithClauseDao {
-    @Query("WITH RECURSIVE factorial(n, fact) AS \n"
-            + "(SELECT 0, 1 \n"
-            + "   UNION ALL \n"
-            + " SELECT n+1, (n+1)*fact FROM factorial \n"
-            + "   WHERE n < :maxIndexInclusive)\n"
-            + " SELECT fact FROM factorial;")
-    List<Integer> getFactorials(int maxIndexInclusive);
-
-    @Query("WITH RECURSIVE factorial(n, fact) AS \n"
-            + "(SELECT 0, 1 \n"
-            + "   UNION ALL \n"
-            + " SELECT n+1, (n+1)*fact FROM factorial \n"
-            + "   WHERE n < :maxIndexInclusive)\n"
-            + " SELECT mName FROM User WHERE User.mId IN (Select fact FROM factorial);")
-    List<String> getUsersWithFactorialIds(int maxIndexInclusive);
-
-    @Query("WITH RECURSIVE factorial(n, fact) AS \n"
-            + "(SELECT 0, 1 \n"
-            + "   UNION ALL \n"
-            + " SELECT n+1, (n+1)*fact FROM factorial \n"
-            + "   WHERE n < :maxIndexInclusive)\n"
-            + " SELECT mName FROM User WHERE User.mId IN (Select fact FROM factorial);")
-    LiveData<List<String>> getUsersWithFactorialIdsLiveData(int maxIndexInclusive);
-}
diff --git a/android/arch/persistence/room/integration/testapp/database/Customer.java b/android/arch/persistence/room/integration/testapp/database/Customer.java
deleted file mode 100644
index 65e9828..0000000
--- a/android/arch/persistence/room/integration/testapp/database/Customer.java
+++ /dev/null
@@ -1,110 +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.arch.persistence.room.integration.testapp.database;
-
-import android.arch.persistence.room.Entity;
-import android.arch.persistence.room.PrimaryKey;
-import android.support.annotation.NonNull;
-import android.support.v7.recyclerview.extensions.DiffCallback;
-
-/**
- * Sample entity
- */
-@Entity
-public class Customer {
-
-    @PrimaryKey(autoGenerate = true)
-    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/android/arch/persistence/room/integration/testapp/database/CustomerDao.java b/android/arch/persistence/room/integration/testapp/database/CustomerDao.java
deleted file mode 100644
index db45dc4..0000000
--- a/android/arch/persistence/room/integration/testapp/database/CustomerDao.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.arch.persistence.room.integration.testapp.database;
-
-import android.arch.paging.DataSource;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.Insert;
-import android.arch.persistence.room.Query;
-
-import java.util.List;
-
-/**
- * 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 DataSource.Factory of customers, ordered by last name. Use
-     * {@link android.arch.paging.LivePagedListBuilder} to get a LiveData of PagedLists.
-     */
-    @Query("SELECT * FROM customer ORDER BY mLastName ASC")
-    DataSource.Factory<Integer, Customer> loadPagedAgeOrder();
-
-    /**
-     * @return number of customers
-     */
-    @Query("SELECT COUNT(*) FROM customer")
-    int countCustomers();
-
-    // Keyed
-
-    @Query("SELECT * from customer ORDER BY mLastName DESC LIMIT :limit")
-    List<Customer> customerNameInitial(int limit);
-
-    @Query("SELECT * from customer WHERE mLastName < :key ORDER BY mLastName DESC LIMIT :limit")
-    List<Customer> customerNameLoadAfter(String key, int limit);
-
-    @Query("SELECT COUNT(*) from customer WHERE mLastName < :key ORDER BY mLastName DESC")
-    int customerNameCountAfter(String key);
-
-    @Query("SELECT * from customer WHERE mLastName > :key ORDER BY mLastName ASC LIMIT :limit")
-    List<Customer> customerNameLoadBefore(String key, int limit);
-
-    @Query("SELECT COUNT(*) from customer WHERE mLastName > :key ORDER BY mLastName ASC")
-    int customerNameCountBefore(String key);
-}
diff --git a/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java b/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java
deleted file mode 100644
index 6278bc2..0000000
--- a/android/arch/persistence/room/integration/testapp/database/LastNameAscCustomerDataSource.java
+++ /dev/null
@@ -1,125 +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.arch.persistence.room.integration.testapp.database;
-
-import android.arch.paging.DataSource;
-import android.arch.paging.ItemKeyedDataSource;
-import android.arch.persistence.room.InvalidationTracker;
-import android.support.annotation.NonNull;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Sample Room keyed data source.
- */
-public class LastNameAscCustomerDataSource extends ItemKeyedDataSource<String, Customer> {
-    private final CustomerDao mCustomerDao;
-    @SuppressWarnings("FieldCanBeLocal")
-    private final InvalidationTracker.Observer mObserver;
-    private SampleDatabase mDb;
-
-    public static Factory<String, Customer> factory(final SampleDatabase db) {
-        return new Factory<String, Customer>() {
-            @Override
-            public DataSource<String, Customer> create() {
-                return new LastNameAscCustomerDataSource(db);
-            }
-        };
-    }
-
-    /**
-     * Create a DataSource from the customer table of the given database
-     */
-    private LastNameAscCustomerDataSource(SampleDatabase db) {
-        mDb = db;
-        mCustomerDao = db.getCustomerDao();
-        mObserver = new InvalidationTracker.Observer("customer") {
-            @Override
-            public void onInvalidated(@NonNull Set<String> tables) {
-                invalidate();
-            }
-        };
-        db.getInvalidationTracker().addWeakObserver(mObserver);
-    }
-
-    @Override
-    public boolean isInvalid() {
-        mDb.getInvalidationTracker().refreshVersionsSync();
-        return super.isInvalid();
-    }
-
-    @NonNull
-    public static String getKeyStatic(@NonNull Customer customer) {
-        return customer.getLastName();
-    }
-
-    @NonNull
-    @Override
-    public String getKey(@NonNull Customer customer) {
-        return getKeyStatic(customer);
-    }
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams<String> params,
-            @NonNull LoadInitialCallback<Customer> callback) {
-        String customerName = params.requestedInitialKey;
-        List<Customer> list;
-        if (customerName != null) {
-            // initial keyed load - load before 'customerName',
-            // and load after last item in before list
-            int pageSize = params.requestedLoadSize / 2;
-            String key = customerName;
-            list = mCustomerDao.customerNameLoadBefore(key, pageSize);
-            Collections.reverse(list);
-            if (!list.isEmpty()) {
-                key = getKey(list.get(list.size() - 1));
-            }
-            list.addAll(mCustomerDao.customerNameLoadAfter(key, pageSize));
-        } else {
-            list = mCustomerDao.customerNameInitial(params.requestedLoadSize);
-        }
-
-        if (params.placeholdersEnabled && !list.isEmpty()) {
-            String firstKey = getKey(list.get(0));
-            String lastKey = getKey(list.get(list.size() - 1));
-
-            // only bother counting if placeholders are desired
-            final int position = mCustomerDao.customerNameCountBefore(firstKey);
-            final int count = position + list.size() + mCustomerDao.customerNameCountAfter(lastKey);
-            callback.onResult(list, position, count);
-        } else {
-            callback.onResult(list);
-        }
-    }
-
-    @Override
-    public void loadAfter(@NonNull LoadParams<String> params,
-            @NonNull LoadCallback<Customer> callback) {
-        callback.onResult(mCustomerDao.customerNameLoadAfter(params.key, params.requestedLoadSize));
-    }
-
-    @Override
-    public void loadBefore(@NonNull LoadParams<String> params,
-            @NonNull LoadCallback<Customer> callback) {
-        List<Customer> list = mCustomerDao.customerNameLoadBefore(
-                params.key, params.requestedLoadSize);
-        Collections.reverse(list);
-        callback.onResult(list);
-    }
-}
-
diff --git a/android/arch/persistence/room/integration/testapp/database/SampleDatabase.java b/android/arch/persistence/room/integration/testapp/database/SampleDatabase.java
deleted file mode 100644
index 9020eb1..0000000
--- a/android/arch/persistence/room/integration/testapp/database/SampleDatabase.java
+++ /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.
- */
-
-package android.arch.persistence.room.integration.testapp.database;
-
-import android.arch.persistence.room.Database;
-import android.arch.persistence.room.RoomDatabase;
-
-/**
- * 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/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java b/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
deleted file mode 100644
index ef207cf..0000000
--- a/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
+++ /dev/null
@@ -1,141 +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.arch.persistence.room.integration.testapp.migration;
-
-import android.arch.persistence.db.SupportSQLiteDatabase;
-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.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;
-        @ColumnInfo(collate = ColumnInfo.NOCASE)
-        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/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java b/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
deleted file mode 100644
index c850a4d..0000000
--- a/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
+++ /dev/null
@@ -1,433 +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.arch.persistence.room.integration.testapp.migration;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.endsWith;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.CoreMatchers.startsWith;
-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.hamcrest.MatcherAssert;
-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();
-    }
-
-    @Test
-    public void failWithIdentityCheck() throws IOException {
-        for (int i = 1; i < MigrationDb.LATEST_VERSION; i++) {
-            String name = "test_" + i;
-            helper.createDatabase(name, i).close();
-            IllegalStateException exception = null;
-            try {
-                MigrationDb db = Room.databaseBuilder(
-                        InstrumentationRegistry.getInstrumentation().getTargetContext(),
-                        MigrationDb.class, name).build();
-                db.runInTransaction(new Runnable() {
-                    @Override
-                    public void run() {
-                        // do nothing
-                    }
-                });
-            } catch (IllegalStateException ex) {
-                exception = ex;
-            }
-            MatcherAssert.assertThat("identity detection should've failed",
-                    exception, notNullValue());
-        }
-    }
-
-    @Test
-    public void fallbackToDestructiveMigrationFrom_destructiveMigrationOccursForSuppliedVersion()
-            throws IOException {
-        SupportSQLiteDatabase database = helper.createDatabase(TEST_DB, 6);
-        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)
-                .fallbackToDestructiveMigrationFrom(6)
-                .build();
-
-        assertThat(db.dao().loadAllEntity1s().size(), is(0));
-    }
-
-    @Test
-    public void fallbackToDestructiveMigrationFrom_suppliedValueIsMigrationStartVersion_exception()
-            throws IOException {
-        SupportSQLiteDatabase database = helper.createDatabase(TEST_DB, 6);
-        database.close();
-        Context targetContext = InstrumentationRegistry.getTargetContext();
-
-        Throwable throwable = null;
-        try {
-            Room.databaseBuilder(targetContext, MigrationDb.class, TEST_DB)
-                    .addMigrations(MIGRATION_6_7)
-                    .fallbackToDestructiveMigrationFrom(6)
-                    .build();
-        } catch (Throwable t) {
-            throwable = t;
-        }
-
-        assertThat(throwable, is(not(nullValue())));
-        //noinspection ConstantConditions
-        assertThat(throwable.getMessage(),
-                startsWith("Inconsistency detected. A Migration was supplied to"));
-        assertThat(throwable.getMessage(), endsWith("6"));
-    }
-
-    @Test
-    public void fallbackToDestructiveMigrationFrom_suppliedValueIsMigrationEndVersion_exception()
-            throws IOException {
-        SupportSQLiteDatabase database = helper.createDatabase(TEST_DB, 5);
-        database.close();
-        Context targetContext = InstrumentationRegistry.getTargetContext();
-
-        Throwable throwable = null;
-        try {
-            Room.databaseBuilder(targetContext, MigrationDb.class, TEST_DB)
-                    .addMigrations(MIGRATION_5_6)
-                    .fallbackToDestructiveMigrationFrom(6)
-                    .build();
-        } catch (Throwable t) {
-            throwable = t;
-        }
-
-        assertThat(throwable, is(not(nullValue())));
-        //noinspection ConstantConditions
-        assertThat(throwable.getMessage(),
-                startsWith("Inconsistency detected. A Migration was supplied to"));
-        assertThat(throwable.getMessage(), endsWith("6"));
-    }
-
-    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 COLLATE NOCASE, PRIMARY KEY(`id`),"
-                    + " FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`)"
-                    + " ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)");
-            database.execSQL("CREATE UNIQUE INDEX `index_entity1` ON "
-                    + MigrationDb.Entity1.TABLE_NAME + " (`name`)");
-        }
-    };
-
-    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/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java b/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java
deleted file mode 100644
index 0f68656..0000000
--- a/android/arch/persistence/room/integration/testapp/paging/DataSourceFactoryTest.java
+++ /dev/null
@@ -1,183 +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.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.ArchTaskExecutor;
-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.paging.LivePagedListBuilder;
-import android.arch.paging.PagedList;
-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.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;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class DataSourceFactoryTest extends TestDatabaseTest {
-    @Rule
-    public CountingTaskExecutorRule mExecutorRule = new CountingTaskExecutorRule();
-
-    private interface LivePagedListFactory {
-        LiveData<PagedList<User>> create();
-    }
-
-    @Test
-    public void getUsersAsPagedList()
-            throws InterruptedException, ExecutionException, TimeoutException {
-        validateUsersAsPagedList(() -> new LivePagedListBuilder<>(
-                mUserDao.loadPagedByAge(3),
-                new PagedList.Config.Builder()
-                        .setPageSize(10)
-                        .setPrefetchDistance(1)
-                        .setInitialLoadSizeHint(10).build())
-                .build());
-    }
-
-    private void validateUsersAsPagedList(LivePagedListFactory factory)
-            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<PagedList<User>> livePagedUsers = factory.create();
-
-        final TestLifecycleOwner testOwner = new TestLifecycleOwner();
-        testOwner.handleEvent(Lifecycle.Event.ON_CREATE);
-        drain();
-        PagedListObserver<User> observer = new PagedListObserver<>();
-
-        observe(livePagedUsers, testOwner, observer);
-        assertThat(observer.get(), nullValue());
-        observer.reset();
-
-        testOwner.handleEvent(Lifecycle.Event.ON_START);
-        drain();
-
-        PagedList<User> pagedList1 = observer.get();
-        assertThat(pagedList1, is(notNullValue()));
-
-        assertThat(pagedList1.size(), is(96));
-        assertThat(getAndLoad(pagedList1, 20), is(nullValue()));
-        drain();
-        assertThat(getAndLoad(pagedList1, 31), nullValue());
-        assertThat(getAndLoad(pagedList1, 20), notNullValue());
-        assertThat(getAndLoad(pagedList1, 16), notNullValue());
-
-        drain();
-        assertThat(getAndLoad(pagedList1, 31), notNullValue());
-        assertThat(getAndLoad(pagedList1, 50), nullValue());
-        drain();
-        assertThat(getAndLoad(pagedList1, 50), notNullValue());
-        observer.reset();
-        // now invalidate the database but don't get the new paged list
-        mUserDao.updateById(50, "foo");
-        assertThat(getAndLoad(pagedList1, 70), nullValue());
-        drain();
-        assertThat(getAndLoad(pagedList1, 70), nullValue());
-        PagedList<User> pagedList = observer.get();
-        assertThat(getAndLoad(pagedList, 70), notNullValue());
-    }
-
-    private <T> T getAndLoad(PagedList<T> list, int pos) {
-        T result = list.get(pos);
-        list.loadAround(pos);
-        return result;
-    }
-
-    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;
-            }
-        });
-        ArchTaskExecutor.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 PagedListObserver<T> implements Observer<PagedList<T>> {
-        private PagedList<T> mList;
-        void reset() {
-            mList = null;
-        }
-
-        public PagedList<T> get() {
-            return mList;
-        }
-
-        @Override
-        public void onChanged(@Nullable PagedList<T> pagedList) {
-            mList = pagedList;
-        }
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/paging/LimitOffsetDataSourceTest.java b/android/arch/persistence/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
deleted file mode 100644
index 89359be..0000000
--- a/android/arch/persistence/room/integration/testapp/paging/LimitOffsetDataSourceTest.java
+++ /dev/null
@@ -1,115 +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.arch.persistence.room.integration.testapp.paging;
-
-import static junit.framework.Assert.assertFalse;
-
-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.persistence.room.paging.LimitOffsetDataSource;
-import android.support.annotation.NonNull;
-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.util.ArrayList;
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LimitOffsetDataSourceTest extends TestDatabaseTest {
-
-    @After
-    public void teardown() {
-        mUserDao.deleteEverything();
-    }
-
-    private LimitOffsetDataSource<User> loadUsersByAgeDesc() {
-        return (LimitOffsetDataSource<User>) mUserDao.loadUsersByAgeDesc().create();
-    }
-
-    @Test
-    public void emptyPage() {
-        LimitOffsetDataSource<User> dataSource = loadUsersByAgeDesc();
-        assertThat(dataSource.countItems(), is(0));
-    }
-
-    @Test
-    public void loadCount() {
-        createUsers(6);
-        LimitOffsetDataSource<User> dataSource = loadUsersByAgeDesc();
-        assertThat(dataSource.countItems(), is(6));
-    }
-
-    @Test
-    public void singleItem() {
-        List<User> users = createUsers(1);
-        LimitOffsetDataSource<User> dataSource = loadUsersByAgeDesc();
-        assertThat(dataSource.countItems(), is(1));
-        List<User> initial = dataSource.loadRange(0, 10);
-
-        assertThat(initial.get(0), is(users.get(0)));
-        assertFalse(dataSource.loadRange(1, 10).iterator().hasNext());
-    }
-
-    @Test
-    public void initial() {
-        List<User> users = createUsers(10);
-        LimitOffsetDataSource<User> dataSource = loadUsersByAgeDesc();
-        assertThat(dataSource.countItems(), is(10));
-        List<User> initial = dataSource.loadRange(0, 1);
-        assertThat(initial.get(0), is(users.get(0)));
-        List<User> second = dataSource.loadRange(1, 1);
-        assertThat(second.get(0), is(users.get(1)));
-    }
-
-    @Test
-    public void loadAll() {
-        List<User> users = createUsers(10);
-
-        LimitOffsetDataSource<User> dataSource = loadUsersByAgeDesc();
-        List<User> all = dataSource.loadRange(0, 10);
-        assertThat(users, is(all));
-    }
-
-    @Test
-    public void loadAfter() {
-        List<User> users = createUsers(10);
-        LimitOffsetDataSource<User> dataSource = loadUsersByAgeDesc();
-        List<User> result = dataSource.loadRange(4, 2);
-        assertThat(result, is(users.subList(4, 6)));
-    }
-
-    @NonNull
-    private List<User> createUsers(int count) {
-        List<User> users = new ArrayList<>();
-        for (int i = 0; i < count; i++) {
-            User user = TestUtil.createUser(i);
-            user.setAge(1);
-            mUserDao.insert(user);
-            users.add(user);
-        }
-        return users;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/CollationTest.java b/android/arch/persistence/room/integration/testapp/test/CollationTest.java
deleted file mode 100644
index 7b0e933..0000000
--- a/android/arch/persistence/room/integration/testapp/test/CollationTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.persistence.room.ColumnInfo;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.Database;
-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.hamcrest.CoreMatchers;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Locale;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class CollationTest {
-    private CollateDb mDb;
-    private CollateDao mDao;
-    private Locale mDefaultLocale;
-    private final CollateEntity mItem1 = new CollateEntity(1, "abı");
-    private final CollateEntity mItem2 = new CollateEntity(2, "abi");
-    private final CollateEntity mItem3 = new CollateEntity(3, "abj");
-    private final CollateEntity mItem4 = new CollateEntity(4, "abç");
-
-    @Before
-    public void init() {
-        mDefaultLocale = Locale.getDefault();
-    }
-
-    private void initDao(Locale systemLocale) {
-        Locale.setDefault(systemLocale);
-        mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getTargetContext(),
-                CollateDb.class).build();
-        mDao = mDb.dao();
-        mDao.insert(mItem1);
-        mDao.insert(mItem2);
-        mDao.insert(mItem3);
-        mDao.insert(mItem4);
-    }
-
-    @After
-    public void closeDb() {
-        mDb.close();
-        Locale.setDefault(mDefaultLocale);
-    }
-
-    @Test
-    public void localized() {
-        initDao(new Locale("tr", "TR"));
-        List<CollateEntity> result = mDao.sortedByLocalized();
-        assertThat(result, CoreMatchers.is(Arrays.asList(
-                mItem4, mItem1, mItem2, mItem3
-        )));
-    }
-
-    @Test
-    public void localized_asUnicode() {
-        initDao(Locale.getDefault());
-        List<CollateEntity> result = mDao.sortedByLocalizedAsUnicode();
-        assertThat(result, CoreMatchers.is(Arrays.asList(
-                mItem4, mItem2, mItem1, mItem3
-        )));
-    }
-
-    @Test
-    public void unicode_asLocalized() {
-        initDao(new Locale("tr", "TR"));
-        List<CollateEntity> result = mDao.sortedByUnicodeAsLocalized();
-        assertThat(result, CoreMatchers.is(Arrays.asList(
-                mItem4, mItem1, mItem2, mItem3
-        )));
-    }
-
-    @Test
-    public void unicode() {
-        initDao(Locale.getDefault());
-        List<CollateEntity> result = mDao.sortedByUnicode();
-        assertThat(result, CoreMatchers.is(Arrays.asList(
-                mItem4, mItem2, mItem1, mItem3
-        )));
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    @android.arch.persistence.room.Entity
-    static class CollateEntity {
-        @PrimaryKey
-        public final int id;
-        @ColumnInfo(collate = ColumnInfo.LOCALIZED)
-        public final String localizedName;
-        @ColumnInfo(collate = ColumnInfo.UNICODE)
-        public final String unicodeName;
-
-        CollateEntity(int id, String name) {
-            this.id = id;
-            this.localizedName = name;
-            this.unicodeName = name;
-        }
-
-        CollateEntity(int id, String localizedName, String unicodeName) {
-            this.id = id;
-            this.localizedName = localizedName;
-            this.unicodeName = unicodeName;
-        }
-
-        @SuppressWarnings("SimplifiableIfStatement")
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            CollateEntity that = (CollateEntity) o;
-
-            if (id != that.id) return false;
-            if (!localizedName.equals(that.localizedName)) return false;
-            return unicodeName.equals(that.unicodeName);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = id;
-            result = 31 * result + localizedName.hashCode();
-            result = 31 * result + unicodeName.hashCode();
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return "CollateEntity{"
-                    + "id=" + id
-                    + ", localizedName='" + localizedName + '\''
-                    + ", unicodeName='" + unicodeName + '\''
-                    + '}';
-        }
-    }
-
-    @Dao
-    interface CollateDao {
-        @Query("SELECT * FROM CollateEntity ORDER BY localizedName ASC")
-        List<CollateEntity> sortedByLocalized();
-
-        @Query("SELECT * FROM CollateEntity ORDER BY localizedName COLLATE UNICODE ASC")
-        List<CollateEntity> sortedByLocalizedAsUnicode();
-
-        @Query("SELECT * FROM CollateEntity ORDER BY unicodeName ASC")
-        List<CollateEntity> sortedByUnicode();
-
-        @Query("SELECT * FROM CollateEntity ORDER BY unicodeName COLLATE LOCALIZED ASC")
-        List<CollateEntity> sortedByUnicodeAsLocalized();
-
-        @Insert
-        void insert(CollateEntity... entities);
-    }
-
-    @Database(entities = CollateEntity.class, version = 1, exportSchema = false)
-    abstract static class CollateDb extends RoomDatabase {
-        abstract CollateDao dao();
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/ConstructorTest.java b/android/arch/persistence/room/integration/testapp/test/ConstructorTest.java
deleted file mode 100644
index 46c875c..0000000
--- a/android/arch/persistence/room/integration/testapp/test/ConstructorTest.java
+++ /dev/null
@@ -1,256 +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.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.annotation.NonNull;
-import android.support.annotation.Nullable;
-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,
-            EntityWithAnnotations.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);
-
-        @Insert
-        void insertEntityWithAnnotations(EntityWithAnnotations... items);
-
-        @Query("SELECT * FROM EntityWithAnnotations")
-        EntityWithAnnotations getEntitiWithAnnotations();
-    }
-
-    @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;
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    @Entity
-    static class EntityWithAnnotations {
-        @PrimaryKey
-        @NonNull
-        public final String id;
-
-        @NonNull
-        public final String username;
-
-        @Nullable
-        public final String displayName;
-
-        EntityWithAnnotations(
-                @NonNull String id,
-                @NonNull String username,
-                @Nullable String displayName) {
-            this.id = id;
-            this.username = username;
-            this.displayName = displayName;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            EntityWithAnnotations that = (EntityWithAnnotations) o;
-
-            if (!id.equals(that.id)) return false;
-            if (!username.equals(that.username)) return false;
-            return displayName != null ? displayName.equals(that.displayName)
-                    : that.displayName == null;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = id.hashCode();
-            result = 31 * result + username.hashCode();
-            result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
-            return result;
-        }
-    }
-
-    private MyDb mDb;
-    private MyDao mDao;
-
-    @Before
-    public void init() {
-        mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getTargetContext(), 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));
-    }
-
-    @Test // for bug b/69562125
-    public void entityWithAnnotations() {
-        EntityWithAnnotations item = new EntityWithAnnotations("a", "b", null);
-        mDao.insertEntityWithAnnotations(item);
-        assertThat(mDao.getEntitiWithAnnotations(), is(item));
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/CustomDatabaseTest.java b/android/arch/persistence/room/integration/testapp/test/CustomDatabaseTest.java
deleted file mode 100644
index 6f44546..0000000
--- a/android/arch/persistence/room/integration/testapp/test/CustomDatabaseTest.java
+++ /dev/null
@@ -1,109 +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.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();
-            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/android/arch/persistence/room/integration/testapp/test/DatabaseCallbackTest.java b/android/arch/persistence/room/integration/testapp/test/DatabaseCallbackTest.java
deleted file mode 100644
index fafcc2c..0000000
--- a/android/arch/persistence/room/integration/testapp/test/DatabaseCallbackTest.java
+++ /dev/null
@@ -1,131 +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.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 = null;
-        TestDatabase db2 = null;
-        try {
-            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();
-            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);
-        } finally {
-            if (db1 != null) {
-                db1.close();
-            }
-            if (db2 != null) {
-                db2.close();
-            }
-            assertTrue(context.deleteDatabase("test"));
-        }
-    }
-
-    @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/android/arch/persistence/room/integration/testapp/test/EmbeddedTest.java b/android/arch/persistence/room/integration/testapp/test/EmbeddedTest.java
deleted file mode 100644
index d680f3d..0000000
--- a/android/arch/persistence/room/integration/testapp/test/EmbeddedTest.java
+++ /dev/null
@@ -1,246 +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.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/android/arch/persistence/room/integration/testapp/test/ForeignKeyTest.java b/android/arch/persistence/room/integration/testapp/test/ForeignKeyTest.java
deleted file mode 100644
index 1c94e18..0000000
--- a/android/arch/persistence/room/integration/testapp/test/ForeignKeyTest.java
+++ /dev/null
@@ -1,425 +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.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.getTargetContext(),
-                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/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java b/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java
deleted file mode 100644
index f4fca7f..0000000
--- a/android/arch/persistence/room/integration/testapp/test/FunnyNamedDaoTest.java
+++ /dev/null
@@ -1,96 +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.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.testing.CountingTaskExecutorRule;
-import android.arch.lifecycle.Observer;
-import android.arch.persistence.room.integration.testapp.vo.FunnyNamedEntity;
-import android.support.annotation.Nullable;
-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.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class FunnyNamedDaoTest extends TestDatabaseTest {
-    @Rule
-    public CountingTaskExecutorRule mExecutorRule = new CountingTaskExecutorRule();
-
-    @Test
-    public void readWrite() {
-        FunnyNamedEntity entity = new FunnyNamedEntity(1, "a");
-        mFunnyNamedDao.insert(entity);
-        FunnyNamedEntity loaded = mFunnyNamedDao.load(1);
-        assertThat(loaded, is(entity));
-    }
-
-    @Test
-    public void update() {
-        FunnyNamedEntity entity = new FunnyNamedEntity(1, "a");
-        mFunnyNamedDao.insert(entity);
-        entity.setValue("b");
-        mFunnyNamedDao.update(entity);
-        FunnyNamedEntity loaded = mFunnyNamedDao.load(1);
-        assertThat(loaded.getValue(), is("b"));
-    }
-
-    @Test
-    public void delete() {
-        FunnyNamedEntity entity = new FunnyNamedEntity(1, "a");
-        mFunnyNamedDao.insert(entity);
-        assertThat(mFunnyNamedDao.load(1), notNullValue());
-        mFunnyNamedDao.delete(entity);
-        assertThat(mFunnyNamedDao.load(1), nullValue());
-    }
-
-    @Test
-    public void observe() throws TimeoutException, InterruptedException {
-        final FunnyNamedEntity[] item = new FunnyNamedEntity[1];
-        mFunnyNamedDao.observableOne(2).observeForever(new Observer<FunnyNamedEntity>() {
-            @Override
-            public void onChanged(@Nullable FunnyNamedEntity funnyNamedEntity) {
-                item[0] = funnyNamedEntity;
-            }
-        });
-
-        FunnyNamedEntity entity = new FunnyNamedEntity(1, "a");
-        mFunnyNamedDao.insert(entity);
-        mExecutorRule.drainTasks(1, TimeUnit.MINUTES);
-        assertThat(item[0], nullValue());
-
-        final FunnyNamedEntity entity2 = new FunnyNamedEntity(2, "b");
-        mFunnyNamedDao.insert(entity2);
-        mExecutorRule.drainTasks(1, TimeUnit.MINUTES);
-        assertThat(item[0], is(entity2));
-
-        final FunnyNamedEntity entity3 = new FunnyNamedEntity(2, "c");
-        mFunnyNamedDao.update(entity3);
-        mExecutorRule.drainTasks(1, TimeUnit.MINUTES);
-        assertThat(item[0], is(entity3));
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/IdentityDetectionTest.java b/android/arch/persistence/room/integration/testapp/test/IdentityDetectionTest.java
deleted file mode 100644
index 1c1b503..0000000
--- a/android/arch/persistence/room/integration/testapp/test/IdentityDetectionTest.java
+++ /dev/null
@@ -1,106 +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.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/android/arch/persistence/room/integration/testapp/test/IndexingTest.java b/android/arch/persistence/room/integration/testapp/test/IndexingTest.java
deleted file mode 100644
index 16f916b..0000000
--- a/android/arch/persistence/room/integration/testapp/test/IndexingTest.java
+++ /dev/null
@@ -1,97 +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.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/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java b/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
deleted file mode 100644
index 33f4018..0000000
--- a/android/arch/persistence/room/integration/testapp/test/InvalidationTest.java
+++ /dev/null
@@ -1,149 +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.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.CoreMatchers.hasItem;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
-
-import android.arch.core.executor.testing.CountingTaskExecutorRule;
-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.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.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Tests invalidation tracking.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class InvalidationTest {
-    @Rule
-    public CountingTaskExecutorRule executorRule = new CountingTaskExecutorRule();
-    private UserDao mUserDao;
-    private TestDatabase mDb;
-
-    @Before
-    public void createDb() throws TimeoutException, InterruptedException {
-        Context context = InstrumentationRegistry.getTargetContext();
-        mDb = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
-        mUserDao = mDb.getUserDao();
-        drain();
-    }
-
-    @After
-    public void closeDb() throws TimeoutException, InterruptedException {
-        mDb.close();
-        drain();
-    }
-
-    private void drain() throws TimeoutException, InterruptedException {
-        executorRule.drainTasks(1, TimeUnit.MINUTES);
-    }
-
-    @Test
-    public void testInvalidationOnUpdate() throws InterruptedException, TimeoutException {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        LoggingObserver observer = new LoggingObserver("User");
-        mDb.getInvalidationTracker().addObserver(observer);
-        drain();
-        mUserDao.updateById(3, "foo2");
-        drain();
-        assertThat(observer.getInvalidatedTables(), hasSize(1));
-        assertThat(observer.getInvalidatedTables(), hasItem("User"));
-    }
-
-    @Test
-    public void testInvalidationOnDelete() throws InterruptedException, TimeoutException {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        LoggingObserver observer = new LoggingObserver("User");
-        mDb.getInvalidationTracker().addObserver(observer);
-        drain();
-        mUserDao.delete(user);
-        drain();
-        assertThat(observer.getInvalidatedTables(), hasSize(1));
-        assertThat(observer.getInvalidatedTables(), hasItem("User"));
-    }
-
-    @Test
-    public void testInvalidationOnInsert() throws InterruptedException, TimeoutException {
-        LoggingObserver observer = new LoggingObserver("User");
-        mDb.getInvalidationTracker().addObserver(observer);
-        drain();
-        mUserDao.insert(TestUtil.createUser(3));
-        drain();
-        assertThat(observer.getInvalidatedTables(), hasSize(1));
-        assertThat(observer.getInvalidatedTables(), hasItem("User"));
-    }
-
-    @Test
-    public void testDontInvalidateOnLateInsert() throws InterruptedException, TimeoutException {
-        LoggingObserver observer = new LoggingObserver("User");
-        mUserDao.insert(TestUtil.createUser(3));
-        drain();
-        mDb.getInvalidationTracker().addObserver(observer);
-        drain();
-        assertThat(observer.getInvalidatedTables(), nullValue());
-    }
-
-    @Test
-    public void testMultipleTables() throws InterruptedException, TimeoutException {
-        LoggingObserver observer = new LoggingObserver("User", "Pet");
-        mDb.getInvalidationTracker().addObserver(observer);
-        drain();
-        mUserDao.insert(TestUtil.createUser(3));
-        drain();
-        assertThat(observer.getInvalidatedTables(), hasSize(1));
-        assertThat(observer.getInvalidatedTables(), hasItem("User"));
-    }
-
-    private static class LoggingObserver extends InvalidationTracker.Observer {
-        private Set<String> mInvalidatedTables;
-
-        LoggingObserver(String... tables) {
-            super(tables);
-        }
-
-        @Override
-        public void onInvalidated(@NonNull Set<String> tables) {
-            mInvalidatedTables = tables;
-        }
-
-        Set<String> getInvalidatedTables() {
-            return mInvalidatedTables;
-        }
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java b/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java
deleted file mode 100644
index d073598..0000000
--- a/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java
+++ /dev/null
@@ -1,405 +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.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.ArchTaskExecutor;
-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.PetsToys;
-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.os.Build;
-import android.support.annotation.Nullable;
-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.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-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)));
-    }
-
-    @Test
-    public void withRelationOnly() throws ExecutionException, InterruptedException,
-            TimeoutException {
-        LiveData<PetsToys> liveData = mSpecificDogDao.getSpecificDogsToys();
-
-        PetsToys expected = new PetsToys();
-        expected.petId = 123;
-
-        Toy toy = new Toy();
-        toy.setId(1);
-        toy.setPetId(123);
-        toy.setName("ball");
-
-        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
-        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
-        final TestObserver<PetsToys> observer = new TestObserver<>();
-        observe(liveData, lifecycleOwner, observer);
-        assertThat(observer.get(), is(expected));
-
-        observer.reset();
-        expected.toys.add(toy);
-        mToyDao.insert(toy);
-        assertThat(observer.get(), is(expected));
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
-    public void withWithClause() throws ExecutionException, InterruptedException,
-            TimeoutException {
-        LiveData<List<String>> actual =
-                mWithClauseDao.getUsersWithFactorialIdsLiveData(0);
-        List<String> expected = new ArrayList<>();
-
-        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
-        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
-        final TestObserver<List<String>> observer = new TestObserver<>();
-        observe(actual, lifecycleOwner, observer);
-        assertThat(observer.get(), is(expected));
-
-        observer.reset();
-        User user = new User();
-        user.setId(0);
-        user.setName("Zero");
-        mUserDao.insert(user);
-        assertThat(observer.get(), is(expected));
-
-        observer.reset();
-        user = new User();
-        user.setId(1);
-        user.setName("One");
-        mUserDao.insert(user);
-        expected.add("One");
-        assertThat(observer.get(), is(expected));
-
-        observer.reset();
-        user = new User();
-        user.setId(6);
-        user.setName("Six");
-        mUserDao.insert(user);
-        assertThat(observer.get(), is(expected));
-
-        actual = mWithClauseDao.getUsersWithFactorialIdsLiveData(3);
-        observe(actual, lifecycleOwner, observer);
-        expected.add("Six");
-        assertThat(observer.get(), is(expected));
-    }
-
-    @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());
-    }
-
-    @Test
-    public void booleanLiveData() throws ExecutionException, InterruptedException,
-            TimeoutException {
-        User user = TestUtil.createUser(3);
-        user.setAdmin(false);
-        LiveData<Boolean> adminLiveData = mUserDao.isAdminLiveData(3);
-        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
-        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
-        final TestObserver<Boolean> observer = new TestObserver<>();
-        observe(adminLiveData, lifecycleOwner, observer);
-        assertThat(observer.get(), is(nullValue()));
-        mUserDao.insert(user);
-        assertThat(observer.get(), is(false));
-        user.setAdmin(true);
-        mUserDao.insertOrReplace(user);
-        assertThat(observer.get(), is(true));
-    }
-
-    private void observe(final LiveData liveData, final LifecycleOwner provider,
-            final Observer observer) throws ExecutionException, InterruptedException {
-        FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                //noinspection unchecked
-                liveData.observe(provider, observer);
-                return null;
-            }
-        });
-        ArchTaskExecutor.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/android/arch/persistence/room/integration/testapp/test/MainThreadCheckTest.java b/android/arch/persistence/room/integration/testapp/test/MainThreadCheckTest.java
deleted file mode 100644
index f4ed5dd..0000000
--- a/android/arch/persistence/room/integration/testapp/test/MainThreadCheckTest.java
+++ /dev/null
@@ -1,116 +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.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/android/arch/persistence/room/integration/testapp/test/PojoTest.java b/android/arch/persistence/room/integration/testapp/test/PojoTest.java
deleted file mode 100644
index b1579fc..0000000
--- a/android/arch/persistence/room/integration/testapp/test/PojoTest.java
+++ /dev/null
@@ -1,74 +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.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.CoreMatchers.is;
-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.UserDao;
-import android.arch.persistence.room.integration.testapp.vo.AvgWeightByAge;
-import android.arch.persistence.room.integration.testapp.vo.User;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-
-@LargeTest
-@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/android/arch/persistence/room/integration/testapp/test/PojoWithRelationTest.java b/android/arch/persistence/room/integration/testapp/test/PojoWithRelationTest.java
deleted file mode 100644
index c3ebfe9..0000000
--- a/android/arch/persistence/room/integration/testapp/test/PojoWithRelationTest.java
+++ /dev/null
@@ -1,234 +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.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.PetWithToyIds;
-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.UserAndPetAdoptionDates;
-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.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
-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));
-    }
-
-    @Test
-    public void boxedPrimitiveList() {
-        Pet pet1 = TestUtil.createPet(3);
-        Pet pet2 = TestUtil.createPet(5);
-
-        Toy pet1_toy1 = TestUtil.createToyForPet(pet1, 10);
-        Toy pet1_toy2 = TestUtil.createToyForPet(pet1, 20);
-        Toy pet2_toy1 = TestUtil.createToyForPet(pet2, 30);
-
-        mPetDao.insertOrReplace(pet1, pet2);
-        mToyDao.insert(pet1_toy1, pet1_toy2, pet2_toy1);
-
-        List<PetWithToyIds> petWithToyIds = mPetDao.allPetsWithToyIds();
-        //noinspection ArraysAsListWithZeroOrOneArgument
-        assertThat(petWithToyIds, is(
-                Arrays.asList(
-                        new PetWithToyIds(pet1, Arrays.asList(10, 20)),
-                        new PetWithToyIds(pet2, Arrays.asList(30)))
-        ));
-    }
-
-    @Test
-    public void viaTypeConverter() {
-        User user = TestUtil.createUser(3);
-        Pet pet1 = TestUtil.createPet(3);
-        Date date1 = new Date(300);
-        pet1.setAdoptionDate(date1);
-        Pet pet2 = TestUtil.createPet(5);
-        Date date2 = new Date(700);
-        pet2.setAdoptionDate(date2);
-
-        pet1.setUserId(3);
-        pet2.setUserId(3);
-        mUserDao.insert(user);
-        mPetDao.insertOrReplace(pet1, pet2);
-
-        List<UserAndPetAdoptionDates> adoptions =
-                mUserPetDao.loadUserWithPetAdoptionDates();
-
-        assertThat(adoptions, is(Arrays.asList(
-                new UserAndPetAdoptionDates(user, Arrays.asList(new Date(300), new Date(700)))
-        )));
-    }
-
-    @Test
-    public void largeRelation_child() {
-        User user = TestUtil.createUser(3);
-        List<Pet> pets = new ArrayList<>();
-        for (int i = 0; i < 2000; i++) {
-            Pet pet = TestUtil.createPet(i + 1);
-            pet.setUserId(3);
-        }
-        mUserDao.insert(user);
-        mPetDao.insertAll(pets.toArray(new Pet[pets.size()]));
-        List<UserAndAllPets> result = mUserPetDao.loadAllUsersWithTheirPets();
-        assertThat(result.size(), is(1));
-        assertThat(result.get(0).user, is(user));
-        assertThat(result.get(0).pets, is(pets));
-    }
-
-    @Test
-    public void largeRelation_parent() {
-        final List<User> users = new ArrayList<>();
-        final List<Pet> pets = new ArrayList<>();
-        for (int i = 0; i < 2000; i++) {
-            User user = TestUtil.createUser(i + 1);
-            users.add(user);
-            Pet pet = TestUtil.createPet(i + 1);
-            pet.setUserId(user.getId());
-            pets.add(pet);
-        }
-        mDatabase.runInTransaction(new Runnable() {
-            @Override
-            public void run() {
-                mUserDao.insertAll(users.toArray(new User[users.size()]));
-                mPetDao.insertAll(pets.toArray(new Pet[pets.size()]));
-            }
-        });
-        List<UserAndAllPets> result = mUserPetDao.loadAllUsersWithTheirPets();
-        assertThat(result.size(), is(2000));
-        for (int i = 0; i < 2000; i++) {
-            assertThat(result.get(i).user, is(users.get(i)));
-            assertThat(result.get(i).pets, is(Collections.singletonList(pets.get(i))));
-        }
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/PrimaryKeyTest.java b/android/arch/persistence/room/integration/testapp/test/PrimaryKeyTest.java
deleted file mode 100644
index fda4373..0000000
--- a/android/arch/persistence/room/integration/testapp/test/PrimaryKeyTest.java
+++ /dev/null
@@ -1,147 +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.arch.persistence.room.integration.testapp.test;
-
-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 static org.junit.Assert.assertNotNull;
-
-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 android.arch.persistence.room.integration.testapp.vo.IntegerPKeyEntity;
-import android.arch.persistence.room.integration.testapp.vo.ObjectPKeyEntity;
-import android.database.sqlite.SQLiteConstraintException;
-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.Arrays;
-import java.util.List;
-
-@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.integerAutoIncPKeyDao().insertMe(entity);
-        IntegerAutoIncPKeyEntity loaded = mDatabase.integerAutoIncPKeyDao().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.integerAutoIncPKeyDao().insertMe(entity);
-        IntegerAutoIncPKeyEntity loaded = mDatabase.integerAutoIncPKeyDao().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.integerAutoIncPKeyDao().insertAndGetId(entity);
-        assertThat(mDatabase.integerAutoIncPKeyDao().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.integerAutoIncPKeyDao().insertAndGetIds(entity, entity2);
-        assertThat(mDatabase.integerAutoIncPKeyDao().loadDataById(ids),
-                is(Arrays.asList("foo", "foo2")));
-    }
-
-    @Test
-    public void insertNullPrimaryKey() throws Exception {
-        ObjectPKeyEntity o1 = new ObjectPKeyEntity(null, "1");
-
-        Throwable throwable = null;
-        try {
-            mDatabase.objectPKeyDao().insertMe(o1);
-        } catch (Throwable t) {
-            throwable = t;
-        }
-        assertNotNull("Was expecting an exception", throwable);
-        assertThat(throwable, instanceOf(SQLiteConstraintException.class));
-    }
-
-    @Test
-    public void insertNullPrimaryKeyForInteger() throws Exception {
-        IntegerPKeyEntity entity = new IntegerPKeyEntity();
-        entity.data = "data";
-        mDatabase.integerPKeyDao().insertMe(entity);
-
-        List<IntegerPKeyEntity> list = mDatabase.integerPKeyDao().loadAll();
-        assertThat(list.size(), is(1));
-        assertThat(list.get(0).data, is("data"));
-        assertNotNull(list.get(0).pKey);
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java b/android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java
deleted file mode 100644
index 292e588..0000000
--- a/android/arch/persistence/room/integration/testapp/test/QueryTransactionTest.java
+++ /dev/null
@@ -1,476 +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.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.core.executor.ArchTaskExecutor;
-import android.arch.core.executor.testing.CountingTaskExecutorRule;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LiveData;
-import android.arch.lifecycle.Observer;
-import android.arch.paging.DataSource;
-import android.arch.paging.LivePagedListBuilder;
-import android.arch.paging.PagedList;
-import android.arch.paging.PositionalDataSource;
-import android.arch.persistence.room.Dao;
-import android.arch.persistence.room.Database;
-import android.arch.persistence.room.Entity;
-import android.arch.persistence.room.Ignore;
-import android.arch.persistence.room.Insert;
-import android.arch.persistence.room.PrimaryKey;
-import android.arch.persistence.room.Query;
-import android.arch.persistence.room.Relation;
-import android.arch.persistence.room.Room;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.RoomWarnings;
-import android.arch.persistence.room.Transaction;
-import android.arch.persistence.room.paging.LimitOffsetDataSource;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-
-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.Parameterized;
-
-import java.util.Collections;
-import java.util.List;
-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;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import io.reactivex.Flowable;
-import io.reactivex.Maybe;
-import io.reactivex.Single;
-import io.reactivex.observers.TestObserver;
-import io.reactivex.schedulers.Schedulers;
-import io.reactivex.subscribers.TestSubscriber;
-
-@SmallTest
-@RunWith(Parameterized.class)
-public class QueryTransactionTest {
-    @Rule
-    public CountingTaskExecutorRule countingTaskExecutorRule = new CountingTaskExecutorRule();
-    private static final AtomicInteger sStartedTransactionCount = new AtomicInteger(0);
-    private TransactionDb mDb;
-    private final boolean mUseTransactionDao;
-    private Entity1Dao mDao;
-    private final LiveDataQueryTest.TestLifecycleOwner mLifecycleOwner = new LiveDataQueryTest
-            .TestLifecycleOwner();
-
-    @NonNull
-    @Parameterized.Parameters(name = "useTransaction_{0}")
-    public static Boolean[] getParams() {
-        return new Boolean[]{false, true};
-    }
-
-    public QueryTransactionTest(boolean useTransactionDao) {
-        mUseTransactionDao = useTransactionDao;
-    }
-
-    @Before
-    public void initDb() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mLifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
-            }
-        });
-
-        resetTransactionCount();
-        mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getTargetContext(),
-                TransactionDb.class).build();
-        mDao = mUseTransactionDao ? mDb.transactionDao() : mDb.dao();
-        drain();
-    }
-
-    @After
-    public void closeDb() {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mLifecycleOwner.handleEvent(Lifecycle.Event.ON_DESTROY);
-            }
-        });
-        drain();
-        mDb.close();
-    }
-
-    @Test
-    public void readList() {
-        mDao.insert(new Entity1(1, "foo"));
-        resetTransactionCount();
-
-        int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
-        List<Entity1> allEntities = mDao.allEntities();
-        assertTransactionCount(allEntities, expectedTransactionCount);
-    }
-
-    @Test
-    public void liveData() {
-        LiveData<List<Entity1>> listLiveData = mDao.liveData();
-        observeForever(listLiveData);
-        drain();
-        assertThat(listLiveData.getValue(), is(Collections.<Entity1>emptyList()));
-
-        resetTransactionCount();
-        mDao.insert(new Entity1(1, "foo"));
-        drain();
-
-        //noinspection ConstantConditions
-        assertThat(listLiveData.getValue().size(), is(1));
-        int expectedTransactionCount = mUseTransactionDao ? 2 : 1;
-        assertTransactionCount(listLiveData.getValue(), expectedTransactionCount);
-    }
-
-    @Test
-    public void flowable() {
-        Flowable<List<Entity1>> flowable = mDao.flowable();
-        TestSubscriber<List<Entity1>> subscriber = observe(flowable);
-        drain();
-        assertThat(subscriber.values().size(), is(1));
-
-        resetTransactionCount();
-        mDao.insert(new Entity1(1, "foo"));
-        drain();
-
-        List<Entity1> allEntities = subscriber.values().get(1);
-        assertThat(allEntities.size(), is(1));
-        int expectedTransactionCount = mUseTransactionDao ? 2 : 1;
-        assertTransactionCount(allEntities, expectedTransactionCount);
-    }
-
-    @Test
-    public void maybe() {
-        mDao.insert(new Entity1(1, "foo"));
-        resetTransactionCount();
-
-        int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
-        Maybe<List<Entity1>> listMaybe = mDao.maybe();
-        TestObserver<List<Entity1>> observer = observe(listMaybe);
-        drain();
-        List<Entity1> allEntities = observer.values().get(0);
-        assertTransactionCount(allEntities, expectedTransactionCount);
-    }
-
-    @Test
-    public void single() {
-        mDao.insert(new Entity1(1, "foo"));
-        resetTransactionCount();
-
-        int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
-        Single<List<Entity1>> listMaybe = mDao.single();
-        TestObserver<List<Entity1>> observer = observe(listMaybe);
-        drain();
-        List<Entity1> allEntities = observer.values().get(0);
-        assertTransactionCount(allEntities, expectedTransactionCount);
-    }
-
-    @Test
-    public void relation() {
-        mDao.insert(new Entity1(1, "foo"));
-        mDao.insert(new Child(1, 1));
-        mDao.insert(new Child(2, 1));
-        resetTransactionCount();
-
-        List<Entity1WithChildren> result = mDao.withRelation();
-        int expectedTransactionCount = mUseTransactionDao ? 1 : 0;
-        assertTransactionCountWithChildren(result, expectedTransactionCount);
-    }
-
-    @Test
-    public void pagedList() {
-        LiveData<PagedList<Entity1>> pagedList =
-                new LivePagedListBuilder<>(mDao.pagedList(), 10).build();
-        observeForever(pagedList);
-        drain();
-        assertThat(sStartedTransactionCount.get(), is(mUseTransactionDao ? 0 : 0));
-
-        mDao.insert(new Entity1(1, "foo"));
-        drain();
-        //noinspection ConstantConditions
-        assertThat(pagedList.getValue().size(), is(1));
-        assertTransactionCount(pagedList.getValue(), mUseTransactionDao ? 2 : 1);
-
-        mDao.insert(new Entity1(2, "bar"));
-        drain();
-        assertThat(pagedList.getValue().size(), is(2));
-        assertTransactionCount(pagedList.getValue(), mUseTransactionDao ? 4 : 2);
-    }
-
-    @Test
-    public void dataSource() {
-        mDao.insert(new Entity1(2, "bar"));
-        drain();
-        resetTransactionCount();
-        @SuppressWarnings("deprecation")
-        LimitOffsetDataSource<Entity1> dataSource =
-                (LimitOffsetDataSource<Entity1>) mDao.dataSource();
-        dataSource.loadRange(0, 10);
-        assertThat(sStartedTransactionCount.get(), is(mUseTransactionDao ? 1 : 0));
-    }
-
-    private void assertTransactionCount(List<Entity1> allEntities, int expectedTransactionCount) {
-        assertThat(sStartedTransactionCount.get(), is(expectedTransactionCount));
-        assertThat(allEntities.isEmpty(), is(false));
-        for (Entity1 entity1 : allEntities) {
-            assertThat(entity1.transactionId, is(expectedTransactionCount));
-        }
-    }
-
-    private void assertTransactionCountWithChildren(List<Entity1WithChildren> allEntities,
-            int expectedTransactionCount) {
-        assertThat(sStartedTransactionCount.get(), is(expectedTransactionCount));
-        assertThat(allEntities.isEmpty(), is(false));
-        for (Entity1WithChildren entity1 : allEntities) {
-            assertThat(entity1.transactionId, is(expectedTransactionCount));
-            assertThat(entity1.children, notNullValue());
-            assertThat(entity1.children.isEmpty(), is(false));
-            for (Child child : entity1.children) {
-                assertThat(child.transactionId, is(expectedTransactionCount));
-            }
-        }
-    }
-
-    private void resetTransactionCount() {
-        sStartedTransactionCount.set(0);
-    }
-
-    private void drain() {
-        try {
-            countingTaskExecutorRule.drainTasks(30, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-            throw new AssertionError("interrupted", e);
-        } catch (TimeoutException e) {
-            throw new AssertionError("drain timed out", e);
-        }
-    }
-
-    private <T> TestSubscriber<T> observe(final Flowable<T> flowable) {
-        TestSubscriber<T> subscriber = new TestSubscriber<>();
-        flowable.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor()))
-                .subscribeWith(subscriber);
-        return subscriber;
-    }
-
-    private <T> TestObserver<T> observe(final Maybe<T> maybe) {
-        TestObserver<T> observer = new TestObserver<>();
-        maybe.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor()))
-                .subscribeWith(observer);
-        return observer;
-    }
-
-    private <T> TestObserver<T> observe(final Single<T> single) {
-        TestObserver<T> observer = new TestObserver<>();
-        single.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor()))
-                .subscribeWith(observer);
-        return observer;
-    }
-
-    private <T> void observeForever(final LiveData<T> liveData) {
-        FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
-            @Override
-            public Void call() throws Exception {
-                liveData.observe(mLifecycleOwner, new Observer<T>() {
-                    @Override
-                    public void onChanged(@Nullable T t) {
-
-                    }
-                });
-                return null;
-            }
-        });
-        ArchTaskExecutor.getMainThreadExecutor().execute(futureTask);
-        try {
-            futureTask.get();
-        } catch (InterruptedException e) {
-            throw new AssertionError("interrupted", e);
-        } catch (ExecutionException e) {
-            throw new AssertionError("execution error", e);
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    static class Entity1WithChildren extends Entity1 {
-        @Relation(entity = Child.class, parentColumn = "id",
-                entityColumn = "entity1Id")
-        public List<Child> children;
-
-        Entity1WithChildren(int id, String value) {
-            super(id, value);
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    @Entity
-    static class Child {
-        @PrimaryKey(autoGenerate = true)
-        public int id;
-        public int entity1Id;
-        @Ignore
-        public final int transactionId = sStartedTransactionCount.get();
-
-        Child(int id, int entity1Id) {
-            this.id = id;
-            this.entity1Id = entity1Id;
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    @Entity
-    static class Entity1 {
-        @PrimaryKey(autoGenerate = true)
-        public int id;
-        public String value;
-        @Ignore
-        public final int transactionId = sStartedTransactionCount.get();
-
-        Entity1(int id, String value) {
-            this.id = id;
-            this.value = value;
-        }
-    }
-
-    // we don't support dao inheritance for queries so for now, go with this
-    interface Entity1Dao {
-        String SELECT_ALL = "select * from Entity1";
-
-        List<Entity1> allEntities();
-
-        Flowable<List<Entity1>> flowable();
-
-        Maybe<List<Entity1>> maybe();
-
-        Single<List<Entity1>> single();
-
-        LiveData<List<Entity1>> liveData();
-
-        List<Entity1WithChildren> withRelation();
-
-        DataSource.Factory<Integer, Entity1> pagedList();
-
-        PositionalDataSource<Entity1> dataSource();
-
-        @Insert
-        void insert(Entity1 entity1);
-
-        @Insert
-        void insert(Child entity1);
-    }
-
-    @Dao
-    interface EntityDao extends Entity1Dao {
-        @Override
-        @Query(SELECT_ALL)
-        List<Entity1> allEntities();
-
-        @Override
-        @Query(SELECT_ALL)
-        Flowable<List<Entity1>> flowable();
-
-        @Override
-        @Query(SELECT_ALL)
-        LiveData<List<Entity1>> liveData();
-
-        @Override
-        @Query(SELECT_ALL)
-        Maybe<List<Entity1>> maybe();
-
-        @Override
-        @Query(SELECT_ALL)
-        Single<List<Entity1>> single();
-
-        @Override
-        @Query(SELECT_ALL)
-        @SuppressWarnings(RoomWarnings.RELATION_QUERY_WITHOUT_TRANSACTION)
-        List<Entity1WithChildren> withRelation();
-
-        @Override
-        @Query(SELECT_ALL)
-        DataSource.Factory<Integer, Entity1> pagedList();
-
-        @Override
-        @Query(SELECT_ALL)
-        PositionalDataSource<Entity1> dataSource();
-    }
-
-    @Dao
-    interface TransactionDao extends Entity1Dao {
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        List<Entity1> allEntities();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        Flowable<List<Entity1>> flowable();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        LiveData<List<Entity1>> liveData();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        Maybe<List<Entity1>> maybe();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        Single<List<Entity1>> single();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        List<Entity1WithChildren> withRelation();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        DataSource.Factory<Integer, Entity1> pagedList();
-
-        @Override
-        @Transaction
-        @Query(SELECT_ALL)
-        PositionalDataSource<Entity1> dataSource();
-    }
-
-    @Database(version = 1, entities = {Entity1.class, Child.class}, exportSchema = false)
-    abstract static class TransactionDb extends RoomDatabase {
-        abstract EntityDao dao();
-
-        abstract TransactionDao transactionDao();
-
-        @Override
-        public void beginTransaction() {
-            super.beginTransaction();
-            sStartedTransactionCount.incrementAndGet();
-        }
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java b/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java
deleted file mode 100644
index 4aae4ea..0000000
--- a/android/arch/persistence/room/integration/testapp/test/RawQueryTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.core.executor.testing.CountingTaskExecutorRule;
-import android.arch.lifecycle.LiveData;
-import android.arch.persistence.db.SimpleSQLiteQuery;
-import android.arch.persistence.room.integration.testapp.dao.RawDao;
-import android.arch.persistence.room.integration.testapp.vo.NameAndLastName;
-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.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.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RawQueryTest extends TestDatabaseTest {
-    @Rule
-    public CountingTaskExecutorRule mExecutorRule = new CountingTaskExecutorRule();
-
-    @Test
-    public void entity_null() {
-        User user = mRawDao.getUser("SELECT * FROM User WHERE mId = 0");
-        assertThat(user, is(nullValue()));
-    }
-
-    @Test
-    public void entity_one() {
-        User expected = TestUtil.createUser(3);
-        mUserDao.insert(expected);
-        User received = mRawDao.getUser("SELECT * FROM User WHERE mId = 3");
-        assertThat(received, is(expected));
-    }
-
-    @Test
-    public void entity_list() {
-        List<User> expected = TestUtil.createUsersList(1, 2, 3, 4);
-        mUserDao.insertAll(expected.toArray(new User[4]));
-        List<User> received = mRawDao.getUserList("SELECT * FROM User ORDER BY mId ASC");
-        assertThat(received, is(expected));
-    }
-
-    @Test
-    public void entity_liveData() throws TimeoutException, InterruptedException {
-        LiveData<User> liveData = mRawDao.getUserLiveData("SELECT * FROM User WHERE mId = 3");
-        liveData.observeForever(user -> {
-        });
-        drain();
-        assertThat(liveData.getValue(), is(nullValue()));
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        drain();
-        assertThat(liveData.getValue(), is(user));
-        user.setLastName("cxZ");
-        mUserDao.insertOrReplace(user);
-        drain();
-        assertThat(liveData.getValue(), is(user));
-    }
-
-    @Test
-    public void entity_supportSql() {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM User WHERE mId = ?",
-                new Object[]{3});
-        User received = mRawDao.getUser(query);
-        assertThat(received, is(user));
-    }
-
-    @Test
-    public void embedded() {
-        User user = TestUtil.createUser(3);
-        Pet[] pets = TestUtil.createPetsForUser(3, 1, 1);
-        mUserDao.insert(user);
-        mPetDao.insertAll(pets);
-        UserAndPet received = mRawDao.getUserAndPet(
-                "SELECT * FROM User, Pet WHERE User.mId = Pet.mUserId LIMIT 1");
-        assertThat(received.getUser(), is(user));
-        assertThat(received.getPet(), is(pets[0]));
-    }
-
-    @Test
-    public void relation() {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        Pet[] pets = TestUtil.createPetsForUser(3, 1, 10);
-        mPetDao.insertAll(pets);
-        UserAndAllPets result = mRawDao
-                .getUserAndAllPets("SELECT * FROM User WHERE mId = 3");
-        assertThat(result.user, is(user));
-        assertThat(result.pets, is(Arrays.asList(pets)));
-    }
-
-    @Test
-    public void pojo() {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        NameAndLastName result =
-                mRawDao.getUserNameAndLastName("SELECT * FROM User");
-        assertThat(result, is(new NameAndLastName(user.getName(), user.getLastName())));
-    }
-
-    @Test
-    public void pojo_supportSql() {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        NameAndLastName result =
-                mRawDao.getUserNameAndLastName(new SimpleSQLiteQuery(
-                        "SELECT * FROM User WHERE mId = ?",
-                        new Object[] {3}
-                ));
-        assertThat(result, is(new NameAndLastName(user.getName(), user.getLastName())));
-    }
-
-    @Test
-    public void pojo_typeConverter() {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        RawDao.UserNameAndBirthday result = mRawDao.getUserAndBirthday(
-                "SELECT mName, mBirthday FROM user LIMIT 1");
-        assertThat(result.name, is(user.getName()));
-        assertThat(result.birthday, is(user.getBirthday()));
-    }
-
-    @Test
-    public void embedded_nullField() {
-        User user = TestUtil.createUser(3);
-        Pet[] pets = TestUtil.createPetsForUser(3, 1, 1);
-        mUserDao.insert(user);
-        mPetDao.insertAll(pets);
-        UserAndPet received = mRawDao.getUserAndPet("SELECT * FROM User LIMIT 1");
-        assertThat(received.getUser(), is(user));
-        assertThat(received.getPet(), is(nullValue()));
-    }
-
-    @Test
-    public void embedded_list() {
-        User[] users = TestUtil.createUsersArray(3, 5);
-        Pet[] pets = TestUtil.createPetsForUser(3, 1, 2);
-        mUserDao.insertAll(users);
-        mPetDao.insertAll(pets);
-        List<UserAndPet> received = mRawDao.getUserAndPetList(
-                "SELECT * FROM User LEFT JOIN Pet ON (User.mId = Pet.mUserId)"
-                        + " ORDER BY mId ASC, mPetId ASC");
-        assertThat(received.size(), is(3));
-        // row 0
-        assertThat(received.get(0).getUser(), is(users[0]));
-        assertThat(received.get(0).getPet(), is(pets[0]));
-        // row 1
-        assertThat(received.get(1).getUser(), is(users[0]));
-        assertThat(received.get(1).getPet(), is(pets[1]));
-        // row 2
-        assertThat(received.get(2).getUser(), is(users[1]));
-        assertThat(received.get(2).getPet(), is(nullValue()));
-    }
-
-    @Test
-    public void count() {
-        mUserDao.insertAll(TestUtil.createUsersArray(3, 5, 7, 10));
-        int count = mRawDao.count("SELECT COUNT(*) FROM User");
-        assertThat(count, is(4));
-    }
-
-    private void drain() throws TimeoutException, InterruptedException {
-        mExecutorRule.drainTasks(1, TimeUnit.MINUTES);
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/RxJava2Test.java b/android/arch/persistence/room/integration/testapp/test/RxJava2Test.java
deleted file mode 100644
index 01d071e..0000000
--- a/android/arch/persistence/room/integration/testapp/test/RxJava2Test.java
+++ /dev/null
@@ -1,331 +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.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.core.executor.ArchTaskExecutor;
-import android.arch.core.executor.TaskExecutor;
-import android.arch.persistence.room.EmptyResultSetException;
-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.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.functions.Predicate;
-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();
-        ArchTaskExecutor.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();
-        ArchTaskExecutor.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();
-    }
-
-    @Test
-    public void flowableWithRelation() throws InterruptedException {
-        final TestSubscriber<UserAndAllPets> subscriber = new TestSubscriber<>();
-
-        mUserPetDao.flowableUserWithPets(3).subscribe(subscriber);
-        drain();
-        subscriber.assertSubscribed();
-
-        drain();
-        subscriber.assertNoValues();
-
-        final User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        drain();
-        subscriber.assertValue(new Predicate<UserAndAllPets>() {
-            @Override
-            public boolean test(UserAndAllPets userAndAllPets) throws Exception {
-                return userAndAllPets.user.equals(user);
-            }
-        });
-        subscriber.assertValueCount(1);
-        final Pet[] pets = TestUtil.createPetsForUser(3, 1, 2);
-        mPetDao.insertAll(pets);
-        drain();
-        subscriber.assertValueAt(1, new Predicate<UserAndAllPets>() {
-            @Override
-            public boolean test(UserAndAllPets userAndAllPets) throws Exception {
-                return userAndAllPets.user.equals(user)
-                        && userAndAllPets.pets.equals(Arrays.asList(pets));
-            }
-        });
-    }
-
-    @Test
-    public void flowable_updateInTransaction() throws InterruptedException {
-        // When subscribing to the emissions of the user
-        final TestSubscriber<User> userTestSubscriber = mUserDao
-                .flowableUserById(3)
-                .observeOn(mTestScheduler)
-                .test();
-        drain();
-        userTestSubscriber.assertValueCount(0);
-
-        // When inserting a new user in the data source
-        mDatabase.beginTransaction();
-        try {
-            mUserDao.insert(TestUtil.createUser(3));
-            mDatabase.setTransactionSuccessful();
-
-        } finally {
-            mDatabase.endTransaction();
-        }
-        drain();
-        userTestSubscriber.assertValueCount(1);
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/RxJava2WithInstantTaskExecutorTest.java b/android/arch/persistence/room/integration/testapp/test/RxJava2WithInstantTaskExecutorTest.java
deleted file mode 100644
index fcd0b00..0000000
--- a/android/arch/persistence/room/integration/testapp/test/RxJava2WithInstantTaskExecutorTest.java
+++ /dev/null
@@ -1,70 +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.arch.persistence.room.integration.testapp.test;
-
-import android.arch.core.executor.testing.InstantTaskExecutorRule;
-import android.arch.persistence.room.Room;
-import android.arch.persistence.room.integration.testapp.TestDatabase;
-import android.arch.persistence.room.integration.testapp.vo.User;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import io.reactivex.subscribers.TestSubscriber;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RxJava2WithInstantTaskExecutorTest {
-    @Rule
-    public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
-
-    private TestDatabase mDatabase;
-
-    @Before
-    public void initDb() throws Exception {
-        // using an in-memory database because the information stored here disappears when the
-        // process is killed
-        mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
-                TestDatabase.class)
-                // allowing main thread queries, just for testing
-                .allowMainThreadQueries()
-                .build();
-    }
-
-    @Test
-    public void testFlowableInTransaction() {
-        // When subscribing to the emissions of the user
-        TestSubscriber<User> subscriber = mDatabase.getUserDao().flowableUserById(3).test();
-        subscriber.assertValueCount(0);
-
-        // When inserting a new user in the data source
-        mDatabase.beginTransaction();
-        try {
-            mDatabase.getUserDao().insert(TestUtil.createUser(3));
-            mDatabase.setTransactionSuccessful();
-        } finally {
-            mDatabase.endTransaction();
-        }
-
-        subscriber.assertValueCount(1);
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
deleted file mode 100644
index 793523c..0000000
--- a/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ /dev/null
@@ -1,611 +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.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.Day;
-import android.arch.persistence.room.integration.testapp.vo.NameAndLastName;
-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 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.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@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 AssertionError("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 returnBoolean() {
-        User user1 = TestUtil.createUser(1);
-        User user2 = TestUtil.createUser(2);
-        user1.setAdmin(true);
-        user2.setAdmin(false);
-        mUserDao.insert(user1);
-        mUserDao.insert(user2);
-        assertThat(mUserDao.isAdmin(1), is(true));
-        assertThat(mUserDao.isAdmin(2), is(false));
-    }
-
-    @Test
-    public void findByCollateNoCase() {
-        User user = TestUtil.createUser(3);
-        user.setCustomField("abc");
-        mUserDao.insert(user);
-        List<User> users = mUserDao.findByCustomField("ABC");
-        assertThat(users, hasSize(1));
-        assertThat(users.get(0).getId(), is(3));
-    }
-
-    @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 transactionByDefaultImplementation() {
-        Pet pet1 = TestUtil.createPet(1);
-        mPetDao.insertOrReplace(pet1);
-        assertThat(mPetDao.count(), is(1));
-        assertThat(mPetDao.allIds()[0], is(1));
-        Pet pet2 = TestUtil.createPet(2);
-        mPetDao.deleteAndInsert(pet1, pet2, false);
-        assertThat(mPetDao.count(), is(1));
-        assertThat(mPetDao.allIds()[0], is(2));
-    }
-
-    @Test
-    public void transactionByDefaultImplementation_failure() {
-        Pet pet1 = TestUtil.createPet(1);
-        mPetDao.insertOrReplace(pet1);
-        assertThat(mPetDao.count(), is(1));
-        assertThat(mPetDao.allIds()[0], is(1));
-        Pet pet2 = TestUtil.createPet(2);
-        Throwable throwable = null;
-        try {
-            mPetDao.deleteAndInsert(pet1, pet2, true);
-        } catch (Throwable t) {
-            throwable = t;
-        }
-        assertNotNull("Was expecting an exception", throwable);
-        assertThat(mPetDao.count(), is(1));
-        assertThat(mPetDao.allIds()[0], is(1));
-    }
-
-    @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));
-    }
-
-    @Test
-    public void transactionByAnnotation() {
-        User a = TestUtil.createUser(3);
-        User b = TestUtil.createUser(5);
-        mUserDao.insertBothByAnnotation(a, b);
-        assertThat(mUserDao.count(), is(2));
-    }
-
-    @Test
-    public void transactionByAnnotation_failure() {
-        User a = TestUtil.createUser(3);
-        User b = TestUtil.createUser(3);
-        boolean caught = false;
-        try {
-            mUserDao.insertBothByAnnotation(a, b);
-        } catch (SQLiteConstraintException e) {
-            caught = true;
-        }
-        assertTrue("SQLiteConstraintException expected", caught);
-        assertThat(mUserDao.count(), is(0));
-    }
-
-    @Test
-    public void tablePrefix_simpleSelect() {
-        User user = TestUtil.createUser(3);
-        mUserDao.insert(user);
-        NameAndLastName result = mUserDao.getNameAndLastName(3);
-        assertThat(result.getName(), is(user.getName()));
-        assertThat(result.getLastName(), is(user.getLastName()));
-    }
-
-    @Test
-    public void enumSet_simpleLoad() {
-        User a = TestUtil.createUser(3);
-        Set<Day> expected = toSet(Day.MONDAY, Day.TUESDAY);
-        a.setWorkDays(expected);
-        mUserDao.insert(a);
-        User loaded = mUserDao.load(3);
-        assertThat(loaded.getWorkDays(), is(expected));
-    }
-
-    @Test
-    public void enumSet_query() {
-        User user1 = TestUtil.createUser(3);
-        user1.setWorkDays(toSet(Day.MONDAY, Day.FRIDAY));
-        User user2 = TestUtil.createUser(5);
-        user2.setWorkDays(toSet(Day.MONDAY, Day.THURSDAY));
-        mUserDao.insert(user1);
-        mUserDao.insert(user2);
-        List<User> empty = mUserDao.findUsersByWorkDays(toSet(Day.WEDNESDAY));
-        assertThat(empty.size(), is(0));
-        List<User> friday = mUserDao.findUsersByWorkDays(toSet(Day.FRIDAY));
-        assertThat(friday, is(Arrays.asList(user1)));
-        List<User> monday = mUserDao.findUsersByWorkDays(toSet(Day.MONDAY));
-        assertThat(monday, is(Arrays.asList(user1, user2)));
-
-    }
-
-    private Set<Day> toSet(Day... days) {
-        return new HashSet<>(Arrays.asList(days));
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/TestDatabaseTest.java b/android/arch/persistence/room/integration/testapp/test/TestDatabaseTest.java
deleted file mode 100644
index e2525c4..0000000
--- a/android/arch/persistence/room/integration/testapp/test/TestDatabaseTest.java
+++ /dev/null
@@ -1,65 +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.arch.persistence.room.integration.testapp.test;
-
-import android.arch.persistence.room.Room;
-import android.arch.persistence.room.integration.testapp.TestDatabase;
-import android.arch.persistence.room.integration.testapp.dao.FunnyNamedDao;
-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.RawDao;
-import android.arch.persistence.room.integration.testapp.dao.SchoolDao;
-import android.arch.persistence.room.integration.testapp.dao.SpecificDogDao;
-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.dao.WithClauseDao;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-
-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;
-    protected SpecificDogDao mSpecificDogDao;
-    protected WithClauseDao mWithClauseDao;
-    protected FunnyNamedDao mFunnyNamedDao;
-    protected RawDao mRawDao;
-
-    @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();
-        mSpecificDogDao = mDatabase.getSpecificDogDao();
-        mWithClauseDao = mDatabase.getWithClauseDao();
-        mFunnyNamedDao = mDatabase.getFunnyNamedDao();
-        mRawDao = mDatabase.getRawDao();
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/test/TestUtil.java b/android/arch/persistence/room/integration/testapp/test/TestUtil.java
deleted file mode 100644
index d5309b3..0000000
--- a/android/arch/persistence/room/integration/testapp/test/TestUtil.java
+++ /dev/null
@@ -1,109 +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.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.Toy;
-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());
-        pet.setAdoptionDate(new Date());
-        return pet;
-    }
-
-    public static Toy createToyForPet(Pet pet, int toyId) {
-        Toy toy = new Toy();
-        toy.setName("toy " + toyId);
-        toy.setId(toyId);
-        toy.setPetId(pet.getPetId());
-        return toy;
-    }
-
-    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/android/arch/persistence/room/integration/testapp/test/WithClauseTest.java b/android/arch/persistence/room/integration/testapp/test/WithClauseTest.java
deleted file mode 100644
index 9209638..0000000
--- a/android/arch/persistence/room/integration/testapp/test/WithClauseTest.java
+++ /dev/null
@@ -1,83 +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.arch.persistence.room.integration.testapp.test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.arch.persistence.room.integration.testapp.vo.User;
-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.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
-public class WithClauseTest extends TestDatabaseTest{
-    @Test
-    public void noSourceOfData() {
-        @SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
-        List<Integer> expected = Arrays.asList(1);
-        List<Integer> actual = mWithClauseDao.getFactorials(0);
-        assertThat(expected, is(actual));
-
-        expected = Arrays.asList(1, 1, 2, 6, 24);
-        actual = mWithClauseDao.getFactorials(4);
-        assertThat(expected, is(actual));
-    }
-
-    @Test
-    public void sourceOfData() {
-        List<String> expected = new ArrayList<>();
-        List<String> actual = mWithClauseDao.getUsersWithFactorialIds(0);
-        assertThat(expected, is(actual));
-
-        User user = new User();
-        user.setId(0);
-        user.setName("Zero");
-        mUserDao.insert(user);
-        actual = mWithClauseDao.getUsersWithFactorialIds(0);
-        assertThat(expected, is(actual));
-
-        user = new User();
-        user.setId(1);
-        user.setName("One");
-        mUserDao.insert(user);
-        expected.add("One");
-        actual = mWithClauseDao.getUsersWithFactorialIds(0);
-        assertThat(expected, is(actual));
-
-        user = new User();
-        user.setId(6);
-        user.setName("Six");
-        mUserDao.insert(user);
-        actual = mWithClauseDao.getUsersWithFactorialIds(0);
-        assertThat(expected, is(actual));
-
-        actual = mWithClauseDao.getUsersWithFactorialIds(3);
-        expected.add("Six");
-        assertThat(expected, is(actual));
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/Address.java b/android/arch/persistence/room/integration/testapp/vo/Address.java
deleted file mode 100644
index 46f3bb6..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/Address.java
+++ /dev/null
@@ -1,90 +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.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/android/arch/persistence/room/integration/testapp/vo/AvgWeightByAge.java b/android/arch/persistence/room/integration/testapp/vo/AvgWeightByAge.java
deleted file mode 100644
index 4d22f13..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/AvgWeightByAge.java
+++ /dev/null
@@ -1,87 +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.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/android/arch/persistence/room/integration/testapp/vo/BlobEntity.java b/android/arch/persistence/room/integration/testapp/vo/BlobEntity.java
deleted file mode 100644
index 134afc7..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/BlobEntity.java
+++ /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.
- */
-
-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/android/arch/persistence/room/integration/testapp/vo/Coordinates.java b/android/arch/persistence/room/integration/testapp/vo/Coordinates.java
deleted file mode 100644
index e8cfd06..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/Coordinates.java
+++ /dev/null
@@ -1,44 +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.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/android/arch/persistence/room/integration/testapp/vo/Day.java b/android/arch/persistence/room/integration/testapp/vo/Day.java
deleted file mode 100644
index e02b91c..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/Day.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.vo;
-
-public enum Day {
-    MONDAY,
-    TUESDAY,
-    WEDNESDAY,
-    THURSDAY,
-    FRIDAY,
-    SATURDAY,
-    SUNDAY
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/EmbeddedUserAndAllPets.java b/android/arch/persistence/room/integration/testapp/vo/EmbeddedUserAndAllPets.java
deleted file mode 100644
index ca82bfe..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/EmbeddedUserAndAllPets.java
+++ /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.
- */
-
-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/android/arch/persistence/room/integration/testapp/vo/FunnyNamedEntity.java b/android/arch/persistence/room/integration/testapp/vo/FunnyNamedEntity.java
deleted file mode 100644
index 20f3c21..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/FunnyNamedEntity.java
+++ /dev/null
@@ -1,75 +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.arch.persistence.room.integration.testapp.vo;
-
-import android.arch.persistence.room.ColumnInfo;
-import android.arch.persistence.room.Entity;
-import android.arch.persistence.room.PrimaryKey;
-
-/**
- * An entity that was weird names
- */
-@Entity(tableName = FunnyNamedEntity.TABLE_NAME)
-public class FunnyNamedEntity {
-    public static final String TABLE_NAME = "funny but not so funny";
-    public static final String COLUMN_ID = "_this $is id$";
-    public static final String COLUMN_VALUE = "unlikely-Ωşå¨ıünames";
-    @PrimaryKey(autoGenerate = true)
-    @ColumnInfo(name = COLUMN_ID)
-    private int mId;
-    @ColumnInfo(name = COLUMN_VALUE)
-    private String mValue;
-
-    public FunnyNamedEntity(int id, String value) {
-        mId = id;
-        mValue = value;
-    }
-
-    public int getId() {
-        return mId;
-    }
-
-    public void setId(int id) {
-        mId = id;
-    }
-
-    public String getValue() {
-        return mValue;
-    }
-
-    public void setValue(String value) {
-        mValue = value;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        FunnyNamedEntity entity = (FunnyNamedEntity) o;
-
-        if (mId != entity.mId) return false;
-        return mValue != null ? mValue.equals(entity.mValue) : entity.mValue == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mId;
-        result = 31 * result + (mValue != null ? mValue.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/IntAutoIncPKeyEntity.java b/android/arch/persistence/room/integration/testapp/vo/IntAutoIncPKeyEntity.java
deleted file mode 100644
index 3392649..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/IntAutoIncPKeyEntity.java
+++ /dev/null
@@ -1,27 +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.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/android/arch/persistence/room/integration/testapp/vo/IntegerAutoIncPKeyEntity.java b/android/arch/persistence/room/integration/testapp/vo/IntegerAutoIncPKeyEntity.java
deleted file mode 100644
index 636ffd9..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/IntegerAutoIncPKeyEntity.java
+++ /dev/null
@@ -1,27 +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.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/android/arch/persistence/room/integration/testapp/vo/IntegerPKeyEntity.java b/android/arch/persistence/room/integration/testapp/vo/IntegerPKeyEntity.java
deleted file mode 100644
index cae1843..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/IntegerPKeyEntity.java
+++ /dev/null
@@ -1,27 +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.arch.persistence.room.integration.testapp.vo;
-
-import android.arch.persistence.room.Entity;
-import android.arch.persistence.room.PrimaryKey;
-
-@Entity
-public class IntegerPKeyEntity {
-    @PrimaryKey
-    public Integer pKey;
-    public String data;
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/NameAndLastName.java b/android/arch/persistence/room/integration/testapp/vo/NameAndLastName.java
deleted file mode 100644
index a6e8223..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/NameAndLastName.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.vo;
-
-
-public class NameAndLastName {
-    private String mName;
-    private String mLastName;
-
-    public NameAndLastName(String name, String lastName) {
-        mName = name;
-        mLastName = lastName;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public String getLastName() {
-        return mLastName;
-    }
-
-    @SuppressWarnings("SimplifiableIfStatement")
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        NameAndLastName that = (NameAndLastName) o;
-
-        if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false;
-        return mLastName != null ? mLastName.equals(that.mLastName) : that.mLastName == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mName != null ? mName.hashCode() : 0;
-        result = 31 * result + (mLastName != null ? mLastName.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/ObjectPKeyEntity.java b/android/arch/persistence/room/integration/testapp/vo/ObjectPKeyEntity.java
deleted file mode 100644
index 895a35a..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/ObjectPKeyEntity.java
+++ /dev/null
@@ -1,34 +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.arch.persistence.room.integration.testapp.vo;
-
-import android.arch.persistence.room.Entity;
-import android.arch.persistence.room.PrimaryKey;
-import android.support.annotation.NonNull;
-
-@Entity
-public class ObjectPKeyEntity {
-    @PrimaryKey
-    @NonNull
-    public String pKey;
-    public String data;
-
-    public ObjectPKeyEntity(String pKey, String data) {
-        this.pKey = pKey;
-        this.data = data;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/Pet.java b/android/arch/persistence/room/integration/testapp/vo/Pet.java
deleted file mode 100644
index cc7549f..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/Pet.java
+++ /dev/null
@@ -1,99 +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.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 java.util.Date;
-
-@Entity
-public class Pet {
-    @PrimaryKey
-    private int mPetId;
-    private int mUserId;
-    @ColumnInfo(name = "mPetName")
-    private String mName;
-
-    private Date mAdoptionDate;
-
-    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;
-    }
-
-    public Date getAdoptionDate() {
-        return mAdoptionDate;
-    }
-
-    public void setAdoptionDate(Date adoptionDate) {
-        mAdoptionDate = adoptionDate;
-    }
-
-    @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;
-        if (mName != null ? !mName.equals(pet.mName) : pet.mName != null) return false;
-        return mAdoptionDate != null ? mAdoptionDate.equals(pet.mAdoptionDate)
-                : pet.mAdoptionDate == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mPetId;
-        result = 31 * result + mUserId;
-        result = 31 * result + (mName != null ? mName.hashCode() : 0);
-        result = 31 * result + (mAdoptionDate != null ? mAdoptionDate.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "Pet{"
-                + "mPetId=" + mPetId
-                + ", mUserId=" + mUserId
-                + ", mName='" + mName + '\''
-                + ", mAdoptionDate=" + mAdoptionDate
-                + '}';
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/PetAndToys.java b/android/arch/persistence/room/integration/testapp/vo/PetAndToys.java
deleted file mode 100644
index 69fb591..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/PetAndToys.java
+++ /dev/null
@@ -1,29 +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.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/android/arch/persistence/room/integration/testapp/vo/PetCouple.java b/android/arch/persistence/room/integration/testapp/vo/PetCouple.java
deleted file mode 100644
index e5208ed..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/PetCouple.java
+++ /dev/null
@@ -1,61 +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.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;
-import android.support.annotation.NonNull;
-
-@Entity
-@SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
-public class PetCouple {
-    @PrimaryKey
-    @NonNull
-    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/android/arch/persistence/room/integration/testapp/vo/PetWithToyIds.java b/android/arch/persistence/room/integration/testapp/vo/PetWithToyIds.java
deleted file mode 100644
index 005afb1..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/PetWithToyIds.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.vo;
-
-import android.arch.persistence.room.Embedded;
-import android.arch.persistence.room.Ignore;
-import android.arch.persistence.room.Relation;
-
-import java.util.List;
-
-public class PetWithToyIds {
-    @Embedded
-    public final Pet pet;
-    @Relation(parentColumn = "mPetId", entityColumn = "mPetId", projection = "mId",
-            entity = Toy.class)
-    public List<Integer> toyIds;
-
-    // for the relation
-    public PetWithToyIds(Pet pet) {
-        this.pet = pet;
-    }
-
-    @Ignore
-    public PetWithToyIds(Pet pet, List<Integer> toyIds) {
-        this.pet = pet;
-        this.toyIds = toyIds;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        PetWithToyIds that = (PetWithToyIds) o;
-
-        if (pet != null ? !pet.equals(that.pet) : that.pet != null) return false;
-        return toyIds != null ? toyIds.equals(that.toyIds) : that.toyIds == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = pet != null ? pet.hashCode() : 0;
-        result = 31 * result + (toyIds != null ? toyIds.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "PetWithToyIds{"
-                + "pet=" + pet
-                + ", toyIds=" + toyIds
-                + '}';
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/PetsToys.java b/android/arch/persistence/room/integration/testapp/vo/PetsToys.java
deleted file mode 100644
index 16bddd6..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/PetsToys.java
+++ /dev/null
@@ -1,56 +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.arch.persistence.room.integration.testapp.vo;
-
-import android.arch.persistence.room.Relation;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PetsToys {
-    public int petId;
-    @Relation(parentColumn = "petId", entityColumn = "mPetId")
-    public List<Toy> toys;
-
-    public PetsToys() {
-        toys = new ArrayList<>();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        PetsToys petsToys = (PetsToys) o;
-
-        if (petId != petsToys.petId) {
-            return false;
-        }
-        return toys != null ? toys.equals(petsToys.toys) : petsToys.toys == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = petId;
-        result = 31 * result + (toys != null ? toys.hashCode() : 0);
-        return result;
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/Product.java b/android/arch/persistence/room/integration/testapp/vo/Product.java
deleted file mode 100644
index a395aea..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/Product.java
+++ /dev/null
@@ -1,36 +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.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/android/arch/persistence/room/integration/testapp/vo/School.java b/android/arch/persistence/room/integration/testapp/vo/School.java
deleted file mode 100644
index f0426f1..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/School.java
+++ /dev/null
@@ -1,95 +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.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/android/arch/persistence/room/integration/testapp/vo/SchoolRef.java b/android/arch/persistence/room/integration/testapp/vo/SchoolRef.java
deleted file mode 100644
index bbf0cb4..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/SchoolRef.java
+++ /dev/null
@@ -1,20 +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.arch.persistence.room.integration.testapp.vo;
-
-public class SchoolRef extends School {
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/Toy.java b/android/arch/persistence/room/integration/testapp/vo/Toy.java
deleted file mode 100644
index 5198c2d..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/Toy.java
+++ /dev/null
@@ -1,75 +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.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/android/arch/persistence/room/integration/testapp/vo/User.java b/android/arch/persistence/room/integration/testapp/vo/User.java
deleted file mode 100644
index a615819..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/User.java
+++ /dev/null
@@ -1,180 +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.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;
-import java.util.HashSet;
-import java.util.Set;
-
-@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", collate = ColumnInfo.NOCASE)
-    private String mCustomField;
-
-    // bit flags
-    private Set<Day> mWorkDays = new HashSet<>();
-
-    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;
-    }
-
-    public Set<Day> getWorkDays() {
-        return mWorkDays;
-    }
-
-    public void setWorkDays(
-            Set<Day> workDays) {
-        mWorkDays = workDays;
-    }
-
-    @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;
-        }
-        if (mCustomField != null ? !mCustomField.equals(user.mCustomField)
-                : user.mCustomField != null) {
-            return false;
-        }
-        return mWorkDays != null ? mWorkDays.equals(user.mWorkDays) : user.mWorkDays == 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);
-        result = 31 * result + (mWorkDays != null ? mWorkDays.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "User{"
-                + "mId=" + mId
-                + ", mName='" + mName + '\''
-                + ", mLastName='" + mLastName + '\''
-                + ", mAge=" + mAge
-                + ", mAdmin=" + mAdmin
-                + ", mWeight=" + mWeight
-                + ", mBirthday=" + mBirthday
-                + ", mCustomField='" + mCustomField + '\''
-                + ", mWorkDays=" + mWorkDays
-                + '}';
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/UserAndAllPets.java b/android/arch/persistence/room/integration/testapp/vo/UserAndAllPets.java
deleted file mode 100644
index 24a0710..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/UserAndAllPets.java
+++ /dev/null
@@ -1,29 +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.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/android/arch/persistence/room/integration/testapp/vo/UserAndPet.java b/android/arch/persistence/room/integration/testapp/vo/UserAndPet.java
deleted file mode 100644
index 628e9bf..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/UserAndPet.java
+++ /dev/null
@@ -1,41 +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.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/android/arch/persistence/room/integration/testapp/vo/UserAndPetAdoptionDates.java b/android/arch/persistence/room/integration/testapp/vo/UserAndPetAdoptionDates.java
deleted file mode 100644
index 28d2495..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/UserAndPetAdoptionDates.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.integration.testapp.vo;
-
-import android.arch.persistence.room.Embedded;
-import android.arch.persistence.room.Ignore;
-import android.arch.persistence.room.Relation;
-
-import java.util.Date;
-import java.util.List;
-
-public class UserAndPetAdoptionDates {
-    @Embedded
-    public final User user;
-    @Relation(entity = Pet.class,
-            parentColumn = "mId",
-            entityColumn = "mUserId",
-            projection = "mAdoptionDate")
-    public List<Date> petAdoptionDates;
-
-    public UserAndPetAdoptionDates(User user) {
-        this.user = user;
-    }
-
-    @Ignore
-    public UserAndPetAdoptionDates(User user, List<Date> petAdoptionDates) {
-        this.user = user;
-        this.petAdoptionDates = petAdoptionDates;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        UserAndPetAdoptionDates that = (UserAndPetAdoptionDates) o;
-
-        if (user != null ? !user.equals(that.user) : that.user != null) return false;
-        return petAdoptionDates != null ? petAdoptionDates.equals(that.petAdoptionDates)
-                : that.petAdoptionDates == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = user != null ? user.hashCode() : 0;
-        result = 31 * result + (petAdoptionDates != null ? petAdoptionDates.hashCode() : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "UserAndPetAdoptionDates{"
-                + "user=" + user
-                + ", petAdoptionDates=" + petAdoptionDates
-                + '}';
-    }
-}
diff --git a/android/arch/persistence/room/integration/testapp/vo/UserAndPetNonNull.java b/android/arch/persistence/room/integration/testapp/vo/UserAndPetNonNull.java
deleted file mode 100644
index 8739bd0..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/UserAndPetNonNull.java
+++ /dev/null
@@ -1,44 +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.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/android/arch/persistence/room/integration/testapp/vo/UserIdAndPetNames.java b/android/arch/persistence/room/integration/testapp/vo/UserIdAndPetNames.java
deleted file mode 100644
index 444431e..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/UserIdAndPetNames.java
+++ /dev/null
@@ -1,33 +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.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/android/arch/persistence/room/integration/testapp/vo/UserWithPetsAndToys.java b/android/arch/persistence/room/integration/testapp/vo/UserWithPetsAndToys.java
deleted file mode 100644
index 01c9bed..0000000
--- a/android/arch/persistence/room/integration/testapp/vo/UserWithPetsAndToys.java
+++ /dev/null
@@ -1,29 +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.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/android/arch/persistence/room/migration/Migration.java b/android/arch/persistence/room/migration/Migration.java
deleted file mode 100644
index d69ea0d..0000000
--- a/android/arch/persistence/room/migration/Migration.java
+++ /dev/null
@@ -1,63 +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.arch.persistence.room.migration;
-
-import android.arch.persistence.db.SupportSQLiteDatabase;
-import android.support.annotation.NonNull;
-
-/**
- * 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(@NonNull SupportSQLiteDatabase database);
-}
diff --git a/android/arch/persistence/room/migration/TableInfoTest.java b/android/arch/persistence/room/migration/TableInfoTest.java
deleted file mode 100644
index 0eb35f6..0000000
--- a/android/arch/persistence/room/migration/TableInfoTest.java
+++ /dev/null
@@ -1,280 +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.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 android.util.Pair;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-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())));
-    }
-
-    @Test
-    public void readIndices() {
-        mDb = createDatabase(
-                "CREATE TABLE foo (n INTEGER, indexed TEXT, unique_indexed TEXT,"
-                        + "a INTEGER, b INTEGER);",
-                "CREATE INDEX foo_indexed ON foo(indexed);",
-                "CREATE UNIQUE INDEX foo_unique_indexed ON foo(unique_indexed COLLATE NOCASE"
-                        + " DESC);",
-                "CREATE INDEX " + TableInfo.Index.DEFAULT_PREFIX + "foo_composite_indexed"
-                        + " ON foo(a, b);"
-        );
-        TableInfo info = TableInfo.read(mDb, "foo");
-        assertThat(info, is(new TableInfo(
-                "foo",
-                toMap(new TableInfo.Column("n", "INTEGER", false, 0),
-                        new TableInfo.Column("indexed", "TEXT", false, 0),
-                        new TableInfo.Column("unique_indexed", "TEXT", false, 0),
-                        new TableInfo.Column("a", "INTEGER", false, 0),
-                        new TableInfo.Column("b", "INTEGER", false, 0)),
-                Collections.<TableInfo.ForeignKey>emptySet(),
-                toSet(new TableInfo.Index("index_foo_blahblah", false,
-                        Arrays.asList("a", "b")),
-                        new TableInfo.Index("foo_unique_indexed", true,
-                                Arrays.asList("unique_indexed")),
-                        new TableInfo.Index("foo_indexed", false,
-                                Arrays.asList("indexed"))))
-        ));
-    }
-
-    @Test
-    public void compatColumnTypes() {
-        // see:https://www.sqlite.org/datatype3.html 3.1
-        List<Pair<String, String>> testCases = Arrays.asList(
-                new Pair<>("TINYINT", "integer"),
-                new Pair<>("VARCHAR", "text"),
-                new Pair<>("DOUBLE", "real"),
-                new Pair<>("BOOLEAN", "numeric"),
-                new Pair<>("FLOATING POINT", "integer")
-        );
-        for (Pair<String, String> testCase : testCases) {
-            mDb = createDatabase(
-                    "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT,"
-                            + "name " + testCase.first + ")");
-            TableInfo info = TableInfo.read(mDb, "foo");
-            assertThat(info, is(new TableInfo("foo",
-                    toMap(new TableInfo.Column("id", "INTEGER", false, 1),
-                            new TableInfo.Column("name", testCase.second, false, 0)),
-                    Collections.<TableInfo.ForeignKey>emptySet())));
-        }
-    }
-
-    private static Map<String, TableInfo.Column> toMap(TableInfo.Column... columns) {
-        Map<String, TableInfo.Column> result = new HashMap<>();
-        for (TableInfo.Column column : columns) {
-            result.put(column.name, column);
-        }
-        return result;
-    }
-
-    private static <T> Set<T> toSet(T... ts) {
-        final HashSet<T> result = new HashSet<T>();
-        for (T t : ts) {
-            result.add(t);
-        }
-        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)
-                        .callback(new SupportSQLiteOpenHelper.Callback(1) {
-                            @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/android/arch/persistence/room/migration/bundle/BundleUtil.java b/android/arch/persistence/room/migration/bundle/BundleUtil.java
deleted file mode 100644
index 4356e69..0000000
--- a/android/arch/persistence/room/migration/bundle/BundleUtil.java
+++ /dev/null
@@ -1,36 +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.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/android/arch/persistence/room/migration/bundle/DatabaseBundle.java b/android/arch/persistence/room/migration/bundle/DatabaseBundle.java
deleted file mode 100644
index f131838..0000000
--- a/android/arch/persistence/room/migration/bundle/DatabaseBundle.java
+++ /dev/null
@@ -1,113 +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.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 implements SchemaEquality<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;
-    }
-
-    @Override
-    public boolean isSchemaEqual(DatabaseBundle other) {
-        return SchemaEqualityUtil.checkSchemaEquality(getEntitiesByTableName(),
-                other.getEntitiesByTableName());
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/EntityBundle.java b/android/arch/persistence/room/migration/bundle/EntityBundle.java
deleted file mode 100644
index d78ac35..0000000
--- a/android/arch/persistence/room/migration/bundle/EntityBundle.java
+++ /dev/null
@@ -1,192 +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.arch.persistence.room.migration.bundle;
-
-import static android.arch.persistence.room.migration.bundle.SchemaEqualityUtil.checkSchemaEquality;
-
-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 implements SchemaEquality<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;
-    }
-
-    @Override
-    public boolean isSchemaEqual(EntityBundle other) {
-        if (!mTableName.equals(other.mTableName)) {
-            return false;
-        }
-        return checkSchemaEquality(getFieldsByColumnName(), other.getFieldsByColumnName())
-                && checkSchemaEquality(mPrimaryKey, other.mPrimaryKey)
-                && checkSchemaEquality(mIndices, other.mIndices)
-                && checkSchemaEquality(mForeignKeys, other.mForeignKeys);
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/EntityBundleTest.java b/android/arch/persistence/room/migration/bundle/EntityBundleTest.java
deleted file mode 100644
index 4b4df8b..0000000
--- a/android/arch/persistence/room/migration/bundle/EntityBundleTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import static org.hamcrest.CoreMatchers.is;
-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 EntityBundleTest {
-    @Test
-    public void schemaEquality_same_equal() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                asList(createFieldBundle("foo"), createFieldBundle("bar")),
-                new PrimaryKeyBundle(false, asList("foo")),
-                asList(createIndexBundle("foo")),
-                asList(createForeignKeyBundle("bar", "foo")));
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                asList(createFieldBundle("foo"), createFieldBundle("bar")),
-                new PrimaryKeyBundle(false, asList("foo")),
-                asList(createIndexBundle("foo")),
-                asList(createForeignKeyBundle("bar", "foo")));
-
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_reorderedFields_equal() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                asList(createFieldBundle("foo"), createFieldBundle("bar")),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                asList(createFieldBundle("bar"), createFieldBundle("foo")),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffFields_notEqual() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                asList(createFieldBundle("foo"), createFieldBundle("bar")),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                asList(createFieldBundle("foo2"), createFieldBundle("bar")),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_reorderedForeignKeys_equal() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                asList(createForeignKeyBundle("x", "y"),
-                        createForeignKeyBundle("bar", "foo")));
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                asList(createForeignKeyBundle("bar", "foo"),
-                        createForeignKeyBundle("x", "y")));
-
-
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffForeignKeys_notEqual() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                asList(createForeignKeyBundle("bar", "foo")));
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                Collections.<IndexBundle>emptyList(),
-                asList(createForeignKeyBundle("bar2", "foo")));
-
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_reorderedIndices_equal() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                asList(createIndexBundle("foo"), createIndexBundle("baz")),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                asList(createIndexBundle("baz"), createIndexBundle("foo")),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffIndices_notEqual() {
-        EntityBundle bundle = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                asList(createIndexBundle("foo")),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        EntityBundle other = new EntityBundle("foo", "sq",
-                Collections.<FieldBundle>emptyList(),
-                new PrimaryKeyBundle(false, asList("foo")),
-                asList(createIndexBundle("foo2")),
-                Collections.<ForeignKeyBundle>emptyList());
-
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    private FieldBundle createFieldBundle(String name) {
-        return new FieldBundle("foo", name, "text", false);
-    }
-
-    private IndexBundle createIndexBundle(String colName) {
-        return new IndexBundle("ind_" + colName, false,
-                asList(colName), "create");
-    }
-
-    private ForeignKeyBundle createForeignKeyBundle(String targetTable, String column) {
-        return new ForeignKeyBundle(targetTable, "CASCADE", "CASCADE",
-                asList(column), asList(column));
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/FieldBundle.java b/android/arch/persistence/room/migration/bundle/FieldBundle.java
deleted file mode 100644
index 5f74087..0000000
--- a/android/arch/persistence/room/migration/bundle/FieldBundle.java
+++ /dev/null
@@ -1,72 +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.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 implements SchemaEquality<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;
-    }
-
-    @Override
-    public boolean isSchemaEqual(FieldBundle other) {
-        if (mNonNull != other.mNonNull) return false;
-        if (mColumnName != null ? !mColumnName.equals(other.mColumnName)
-                : other.mColumnName != null) {
-            return false;
-        }
-        return mAffinity != null ? mAffinity.equals(other.mAffinity) : other.mAffinity == null;
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/FieldBundleTest.java b/android/arch/persistence/room/migration/bundle/FieldBundleTest.java
deleted file mode 100644
index eac4477..0000000
--- a/android/arch/persistence/room/migration/bundle/FieldBundleTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import static org.hamcrest.CoreMatchers.is;
-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 FieldBundleTest {
-    @Test
-    public void schemaEquality_same_equal() {
-        FieldBundle bundle = new FieldBundle("foo", "foo", "text", false);
-        FieldBundle copy = new FieldBundle("foo", "foo", "text", false);
-        assertThat(bundle.isSchemaEqual(copy), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffNonNull_notEqual() {
-        FieldBundle bundle = new FieldBundle("foo", "foo", "text", false);
-        FieldBundle copy = new FieldBundle("foo", "foo", "text", true);
-        assertThat(bundle.isSchemaEqual(copy), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffColumnName_notEqual() {
-        FieldBundle bundle = new FieldBundle("foo", "foo", "text", false);
-        FieldBundle copy = new FieldBundle("foo", "foo2", "text", true);
-        assertThat(bundle.isSchemaEqual(copy), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffAffinity_notEqual() {
-        FieldBundle bundle = new FieldBundle("foo", "foo", "text", false);
-        FieldBundle copy = new FieldBundle("foo", "foo2", "int", false);
-        assertThat(bundle.isSchemaEqual(copy), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffPath_equal() {
-        FieldBundle bundle = new FieldBundle("foo", "foo", "text", false);
-        FieldBundle copy = new FieldBundle("foo>bar", "foo", "text", false);
-        assertThat(bundle.isSchemaEqual(copy), is(true));
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java b/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
deleted file mode 100644
index 367dd74..0000000
--- a/android/arch/persistence/room/migration/bundle/ForeignKeyBundle.java
+++ /dev/null
@@ -1,119 +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.arch.persistence.room.migration.bundle;
-
-import android.support.annotation.RestrictTo;
-
-import com.google.gson.annotations.SerializedName;
-
-import java.util.List;
-
-/**
- * Holds the information about a foreign key reference.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ForeignKeyBundle implements SchemaEquality<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;
-    }
-
-    @Override
-    public boolean isSchemaEqual(ForeignKeyBundle other) {
-        if (mTable != null ? !mTable.equals(other.mTable) : other.mTable != null) return false;
-        if (mOnDelete != null ? !mOnDelete.equals(other.mOnDelete) : other.mOnDelete != null) {
-            return false;
-        }
-        if (mOnUpdate != null ? !mOnUpdate.equals(other.mOnUpdate) : other.mOnUpdate != null) {
-            return false;
-        }
-        // order matters
-        return mColumns.equals(other.mColumns) && mReferencedColumns.equals(
-                other.mReferencedColumns);
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/ForeignKeyBundleTest.java b/android/arch/persistence/room/migration/bundle/ForeignKeyBundleTest.java
deleted file mode 100644
index be1b81e..0000000
--- a/android/arch/persistence/room/migration/bundle/ForeignKeyBundleTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.Arrays;
-
-@RunWith(JUnit4.class)
-public class ForeignKeyBundleTest {
-    @Test
-    public void schemaEquality_same_equal() {
-        ForeignKeyBundle bundle = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        ForeignKeyBundle other = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffTable_notEqual() {
-        ForeignKeyBundle bundle = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        ForeignKeyBundle other = new ForeignKeyBundle("table2", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffOnDelete_notEqual() {
-        ForeignKeyBundle bundle = new ForeignKeyBundle("table", "onDelete2",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        ForeignKeyBundle other = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffOnUpdate_notEqual() {
-        ForeignKeyBundle bundle = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        ForeignKeyBundle other = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate2", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffSrcOrder_notEqual() {
-        ForeignKeyBundle bundle = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col2", "col1"),
-                Arrays.asList("target1", "target2"));
-        ForeignKeyBundle other = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffTargetOrder_notEqual() {
-        ForeignKeyBundle bundle = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target1", "target2"));
-        ForeignKeyBundle other = new ForeignKeyBundle("table", "onDelete",
-                "onUpdate", Arrays.asList("col1", "col2"),
-                Arrays.asList("target2", "target1"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/IndexBundle.java b/android/arch/persistence/room/migration/bundle/IndexBundle.java
deleted file mode 100644
index e991316..0000000
--- a/android/arch/persistence/room/migration/bundle/IndexBundle.java
+++ /dev/null
@@ -1,91 +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.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 implements SchemaEquality<IndexBundle> {
-    // should match Index.kt
-    public static final String DEFAULT_PREFIX = "index_";
-    @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);
-    }
-
-    @Override
-    public boolean isSchemaEqual(IndexBundle other) {
-        if (mUnique != other.mUnique) return false;
-        if (mName.startsWith(DEFAULT_PREFIX)) {
-            if (!other.mName.startsWith(DEFAULT_PREFIX)) {
-                return false;
-            }
-        } else if (other.mName.startsWith(DEFAULT_PREFIX)) {
-            return false;
-        } else if (!mName.equals(other.mName)) {
-            return false;
-        }
-
-        // order matters
-        if (mColumnNames != null ? !mColumnNames.equals(other.mColumnNames)
-                : other.mColumnNames != null) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/IndexBundleTest.java b/android/arch/persistence/room/migration/bundle/IndexBundleTest.java
deleted file mode 100644
index aa7230f..0000000
--- a/android/arch/persistence/room/migration/bundle/IndexBundleTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.Arrays;
-
-@RunWith(JUnit4.class)
-public class IndexBundleTest {
-    @Test
-    public void schemaEquality_same_equal() {
-        IndexBundle bundle = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql");
-        IndexBundle other = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql");
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffName_notEqual() {
-        IndexBundle bundle = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql");
-        IndexBundle other = new IndexBundle("index3", false,
-                Arrays.asList("col1", "col2"), "sql");
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffGenericName_equal() {
-        IndexBundle bundle = new IndexBundle(IndexBundle.DEFAULT_PREFIX + "x", false,
-                Arrays.asList("col1", "col2"), "sql");
-        IndexBundle other = new IndexBundle(IndexBundle.DEFAULT_PREFIX + "y", false,
-                Arrays.asList("col1", "col2"), "sql");
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffUnique_notEqual() {
-        IndexBundle bundle = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql");
-        IndexBundle other = new IndexBundle("index1", true,
-                Arrays.asList("col1", "col2"), "sql");
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffColumns_notEqual() {
-        IndexBundle bundle = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql");
-        IndexBundle other = new IndexBundle("index1", false,
-                Arrays.asList("col2", "col1"), "sql");
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffSql_equal() {
-        IndexBundle bundle = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql");
-        IndexBundle other = new IndexBundle("index1", false,
-                Arrays.asList("col1", "col2"), "sql22");
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/PrimaryKeyBundle.java b/android/arch/persistence/room/migration/bundle/PrimaryKeyBundle.java
deleted file mode 100644
index 820aa7e..0000000
--- a/android/arch/persistence/room/migration/bundle/PrimaryKeyBundle.java
+++ /dev/null
@@ -1,54 +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.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 implements SchemaEquality<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;
-    }
-
-    @Override
-    public boolean isSchemaEqual(PrimaryKeyBundle other) {
-        return mColumnNames.equals(other.mColumnNames) && mAutoGenerate == other.mAutoGenerate;
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/PrimaryKeyBundleTest.java b/android/arch/persistence/room/migration/bundle/PrimaryKeyBundleTest.java
deleted file mode 100644
index 3b9e464..0000000
--- a/android/arch/persistence/room/migration/bundle/PrimaryKeyBundleTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.Arrays;
-
-@RunWith(JUnit4.class)
-public class PrimaryKeyBundleTest {
-    @Test
-    public void schemaEquality_same_equal() {
-        PrimaryKeyBundle bundle = new PrimaryKeyBundle(true,
-                Arrays.asList("foo", "bar"));
-        PrimaryKeyBundle other = new PrimaryKeyBundle(true,
-                Arrays.asList("foo", "bar"));
-        assertThat(bundle.isSchemaEqual(other), is(true));
-    }
-
-    @Test
-    public void schemaEquality_diffAutoGen_notEqual() {
-        PrimaryKeyBundle bundle = new PrimaryKeyBundle(true,
-                Arrays.asList("foo", "bar"));
-        PrimaryKeyBundle other = new PrimaryKeyBundle(false,
-                Arrays.asList("foo", "bar"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffColumns_notEqual() {
-        PrimaryKeyBundle bundle = new PrimaryKeyBundle(true,
-                Arrays.asList("foo", "baz"));
-        PrimaryKeyBundle other = new PrimaryKeyBundle(true,
-                Arrays.asList("foo", "bar"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-
-    @Test
-    public void schemaEquality_diffColumnOrder_notEqual() {
-        PrimaryKeyBundle bundle = new PrimaryKeyBundle(true,
-                Arrays.asList("foo", "bar"));
-        PrimaryKeyBundle other = new PrimaryKeyBundle(true,
-                Arrays.asList("bar", "foo"));
-        assertThat(bundle.isSchemaEqual(other), is(false));
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/SchemaBundle.java b/android/arch/persistence/room/migration/bundle/SchemaBundle.java
deleted file mode 100644
index af35e6f..0000000
--- a/android/arch/persistence/room/migration/bundle/SchemaBundle.java
+++ /dev/null
@@ -1,113 +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.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 implements SchemaEquality<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) {
-            }
-        }
-    }
-
-    @Override
-    public boolean isSchemaEqual(SchemaBundle other) {
-        return SchemaEqualityUtil.checkSchemaEquality(mDatabase, other.mDatabase)
-                && mFormatVersion == other.mFormatVersion;
-    }
-}
diff --git a/android/arch/persistence/room/migration/bundle/SchemaEquality.java b/android/arch/persistence/room/migration/bundle/SchemaEquality.java
deleted file mode 100644
index 59ea4b0..0000000
--- a/android/arch/persistence/room/migration/bundle/SchemaEquality.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * A loose equals check which checks schema equality instead of 100% equality (e.g. order of
- * columns in an entity does not have to match)
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface SchemaEquality<T> {
-    boolean isSchemaEqual(T other);
-}
diff --git a/android/arch/persistence/room/migration/bundle/SchemaEqualityUtil.java b/android/arch/persistence/room/migration/bundle/SchemaEqualityUtil.java
deleted file mode 100644
index 65a7572..0000000
--- a/android/arch/persistence/room/migration/bundle/SchemaEqualityUtil.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.arch.persistence.room.migration.bundle;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * utility class to run schema equality on collections.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class SchemaEqualityUtil {
-    static <T, K extends SchemaEquality<K>> boolean checkSchemaEquality(
-            @Nullable Map<T, K> map1, @Nullable Map<T, K> map2) {
-        if (map1 == null) {
-            return map2 == null;
-        }
-        if (map2 == null) {
-            return false;
-        }
-        if (map1.size() != map2.size()) {
-            return false;
-        }
-        for (Map.Entry<T, K> pair : map1.entrySet()) {
-            if (!checkSchemaEquality(pair.getValue(), map2.get(pair.getKey()))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    static <K extends SchemaEquality<K>> boolean checkSchemaEquality(
-            @Nullable List<K> list1, @Nullable List<K> list2) {
-        if (list1 == null) {
-            return list2 == null;
-        }
-        if (list2 == null) {
-            return false;
-        }
-        if (list1.size() != list2.size()) {
-            return false;
-        }
-        // we don't care this is n^2, small list + only used for testing.
-        for (K item1 : list1) {
-            // find matching item
-            boolean matched = false;
-            for (K item2 : list2) {
-                if (checkSchemaEquality(item1, item2)) {
-                    matched = true;
-                    break;
-                }
-            }
-            if (!matched) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @SuppressWarnings("SimplifiableIfStatement")
-    static <K extends SchemaEquality<K>> boolean checkSchemaEquality(
-            @Nullable K item1, @Nullable K item2) {
-        if (item1 == null) {
-            return item2 == null;
-        }
-        if (item2 == null) {
-            return false;
-        }
-        return item1.isSchemaEqual(item2);
-    }
-}
diff --git a/android/arch/persistence/room/package-info.java b/android/arch/persistence/room/package-info.java
deleted file mode 100644
index 1dafc1b..0000000
--- a/android/arch/persistence/room/package-info.java
+++ /dev/null
@@ -1,130 +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.
- */
-
-/**
- * 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 (and its super class) is persisted in the database unless it is denoted
- *     otherwise (see {@link android.arch.persistence.room.Entity Entity} docs for details).
- *     </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/android/arch/persistence/room/paging/LimitOffsetDataSource.java b/android/arch/persistence/room/paging/LimitOffsetDataSource.java
deleted file mode 100644
index baa5b43..0000000
--- a/android/arch/persistence/room/paging/LimitOffsetDataSource.java
+++ /dev/null
@@ -1,169 +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.arch.persistence.room.paging;
-
-import android.arch.paging.PositionalDataSource;
-import android.arch.persistence.room.InvalidationTracker;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.RoomSQLiteQuery;
-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
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class LimitOffsetDataSource<T> extends PositionalDataSource<T> {
-    private final RoomSQLiteQuery mSourceQuery;
-    private final String mCountQuery;
-    private final String mLimitOffsetQuery;
-    private final RoomDatabase mDb;
-    @SuppressWarnings("FieldCanBeLocal")
-    private final InvalidationTracker.Observer mObserver;
-    private final boolean mInTransaction;
-
-    protected LimitOffsetDataSource(RoomDatabase db, RoomSQLiteQuery query,
-            boolean inTransaction, String... tables) {
-        mDb = db;
-        mSourceQuery = query;
-        mInTransaction = inTransaction;
-        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);
-    }
-
-    /**
-     * Count number of rows query can return
-     */
-    @SuppressWarnings("WeakerAccess")
-    public int countItems() {
-        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();
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    protected abstract List<T> convertRows(Cursor cursor);
-
-    @Override
-    public void loadInitial(@NonNull LoadInitialParams params,
-            @NonNull LoadInitialCallback<T> callback) {
-        int totalCount = countItems();
-        if (totalCount == 0) {
-            callback.onResult(Collections.<T>emptyList(), 0, 0);
-            return;
-        }
-
-        // bound the size requested, based on known count
-        final int firstLoadPosition = computeInitialLoadPosition(params, totalCount);
-        final int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
-
-        List<T> list = loadRange(firstLoadPosition, firstLoadSize);
-        if (list != null && list.size() == firstLoadSize) {
-            callback.onResult(list, firstLoadPosition, totalCount);
-        } else {
-            // null list, or size doesn't match request - DB modified between count and load
-            invalidate();
-        }
-    }
-
-    @Override
-    public void loadRange(@NonNull LoadRangeParams params,
-            @NonNull LoadRangeCallback<T> callback) {
-        List<T> list = loadRange(params.startPosition, params.loadSize);
-        if (list != null) {
-            callback.onResult(list);
-        } else {
-            invalidate();
-        }
-    }
-
-    /**
-     * Return the rows from startPos to startPos + loadCount
-     */
-    @Nullable
-    public List<T> loadRange(int startPosition, int loadCount) {
-        final RoomSQLiteQuery sqLiteQuery = RoomSQLiteQuery.acquire(mLimitOffsetQuery,
-                mSourceQuery.getArgCount() + 2);
-        sqLiteQuery.copyArgumentsFrom(mSourceQuery);
-        sqLiteQuery.bindLong(sqLiteQuery.getArgCount() - 1, loadCount);
-        sqLiteQuery.bindLong(sqLiteQuery.getArgCount(), startPosition);
-        if (mInTransaction) {
-            mDb.beginTransaction();
-            Cursor cursor = null;
-            try {
-                cursor = mDb.query(sqLiteQuery);
-                List<T> rows = convertRows(cursor);
-                mDb.setTransactionSuccessful();
-                return rows;
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-                mDb.endTransaction();
-                sqLiteQuery.release();
-            }
-        } else {
-            Cursor cursor = mDb.query(sqLiteQuery);
-            //noinspection TryFinallyCanBeTryWithResources
-            try {
-                return convertRows(cursor);
-            } finally {
-                cursor.close();
-                sqLiteQuery.release();
-            }
-        }
-    }
-}
diff --git a/android/arch/persistence/room/testing/MigrationTestHelper.java b/android/arch/persistence/room/testing/MigrationTestHelper.java
deleted file mode 100644
index 013dd37..0000000
--- a/android/arch/persistence/room/testing/MigrationTestHelper.java
+++ /dev/null
@@ -1,438 +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.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.IndexBundle;
-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, Collections.<Integer>emptySet());
-        RoomOpenHelper roomOpenHelper = new RoomOpenHelper(configuration,
-                new CreatingDelegate(schemaBundle.getDatabase()),
-                schemaBundle.getDatabase().getIdentityHash(),
-                // we pass the same hash twice since an old schema does not necessarily have
-                // a legacy hash and we would not even persist it.
-                schemaBundle.getDatabase().getIdentityHash());
-        return openDatabase(name, 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, Collections.<Integer>emptySet());
-        RoomOpenHelper roomOpenHelper = new RoomOpenHelper(configuration,
-                new MigratingDelegate(schemaBundle.getDatabase(), validateDroppedTables),
-                // we pass the same hash twice since an old schema does not necessarily have
-                // a legacy hash and we would not even persist it.
-                schemaBundle.getDatabase().getIdentityHash(),
-                schemaBundle.getDatabase().getIdentityHash());
-        return openDatabase(name, roomOpenHelper);
-    }
-
-    private SupportSQLiteDatabase openDatabase(String name, RoomOpenHelper roomOpenHelper) {
-        SupportSQLiteOpenHelper.Configuration config =
-                SupportSQLiteOpenHelper.Configuration
-                        .builder(mInstrumentation.getTargetContext())
-                        .callback(roomOpenHelper)
-                        .name(name)
-                        .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()), toIndices(entityBundle.getIndices()));
-    }
-
-    private static Set<TableInfo.Index> toIndices(List<IndexBundle> indices) {
-        if (indices == null) {
-            return Collections.emptySet();
-        }
-        Set<TableInfo.Index> result = new HashSet<>();
-        for (IndexBundle bundle : indices) {
-            result.add(new TableInfo.Index(bundle.getName(), bundle.isUnique(),
-                    bundle.getColumnNames()));
-        }
-        return result;
-    }
-
-    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;
-    }
-
-    static 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) {
-            super(databaseBundle.getVersion());
-            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/android/arch/persistence/room/util/StringUtil.java b/android/arch/persistence/room/util/StringUtil.java
deleted file mode 100644
index d01e3c5..0000000
--- a/android/arch/persistence/room/util/StringUtil.java
+++ /dev/null
@@ -1,114 +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.arch.persistence.room.util;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-/**
- * @hide
- *
- * String utilities for Room
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class StringUtil {
-
-    @SuppressWarnings("unused")
-    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/android/arch/persistence/room/util/StringUtilTest.java b/android/arch/persistence/room/util/StringUtilTest.java
deleted file mode 100644
index c10fab6..0000000
--- a/android/arch/persistence/room/util/StringUtilTest.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.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/android/arch/persistence/room/util/TableInfo.java b/android/arch/persistence/room/util/TableInfo.java
deleted file mode 100644
index 19d9853..0000000
--- a/android/arch/persistence/room/util/TableInfo.java
+++ /dev/null
@@ -1,591 +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.arch.persistence.room.util;
-
-import android.arch.persistence.db.SupportSQLiteDatabase;
-import android.arch.persistence.room.ColumnInfo;
-import android.database.Cursor;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-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.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-/**
- * 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",
-        "SimplifiableIfStatement"})
-// 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;
-
-    /**
-     * Sometimes, Index information is not available (older versions). If so, we skip their
-     * verification.
-     */
-    @Nullable
-    public final Set<Index> indices;
-
-    @SuppressWarnings("unused")
-    public TableInfo(String name, Map<String, Column> columns, Set<ForeignKey> foreignKeys,
-            Set<Index> indices) {
-        this.name = name;
-        this.columns = Collections.unmodifiableMap(columns);
-        this.foreignKeys = Collections.unmodifiableSet(foreignKeys);
-        this.indices = indices == null ? null : Collections.unmodifiableSet(indices);
-    }
-
-    /**
-     * For backward compatibility with dbs created with older versions.
-     */
-    @SuppressWarnings("unused")
-    public TableInfo(String name, Map<String, Column> columns, Set<ForeignKey> foreignKeys) {
-        this(name, columns, foreignKeys, Collections.<Index>emptySet());
-    }
-
-    @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 != null ? !name.equals(tableInfo.name) : tableInfo.name != null) return false;
-        if (columns != null ? !columns.equals(tableInfo.columns) : tableInfo.columns != null) {
-            return false;
-        }
-        if (foreignKeys != null ? !foreignKeys.equals(tableInfo.foreignKeys)
-                : tableInfo.foreignKeys != null) {
-            return false;
-        }
-        if (indices == null || tableInfo.indices == null) {
-            // if one us is missing index information, seems like we couldn't acquire the
-            // information so we better skip.
-            return true;
-        }
-        return indices.equals(tableInfo.indices);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (columns != null ? columns.hashCode() : 0);
-        result = 31 * result + (foreignKeys != null ? foreignKeys.hashCode() : 0);
-        // skip index, it is not reliable for comparison.
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "TableInfo{"
-                + "name='" + name + '\''
-                + ", columns=" + columns
-                + ", foreignKeys=" + foreignKeys
-                + ", indices=" + indices
-                + '}';
-    }
-
-    /**
-     * 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);
-        Set<Index> indices = readIndices(database, tableName);
-        return new TableInfo(tableName, columns, foreignKeys, indices);
-    }
-
-    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;
-    }
-
-    /**
-     * @return null if we cannot read the indices due to older sqlite implementations.
-     */
-    @Nullable
-    private static Set<Index> readIndices(SupportSQLiteDatabase database, String tableName) {
-        Cursor cursor = database.query("PRAGMA index_list(`" + tableName + "`)");
-        try {
-            final int nameColumnIndex = cursor.getColumnIndex("name");
-            final int originColumnIndex = cursor.getColumnIndex("origin");
-            final int uniqueIndex = cursor.getColumnIndex("unique");
-            if (nameColumnIndex == -1 || originColumnIndex == -1 || uniqueIndex == -1) {
-                // we cannot read them so better not validate any index.
-                return null;
-            }
-            HashSet<Index> indices = new HashSet<>();
-            while (cursor.moveToNext()) {
-                String origin = cursor.getString(originColumnIndex);
-                if (!"c".equals(origin)) {
-                    // Ignore auto-created indices
-                    continue;
-                }
-                String name = cursor.getString(nameColumnIndex);
-                boolean unique = cursor.getInt(uniqueIndex) == 1;
-                Index index = readIndex(database, name, unique);
-                if (index == null) {
-                    // we cannot read it properly so better not read it
-                    return null;
-                }
-                indices.add(index);
-            }
-            return indices;
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * @return null if we cannot read the index due to older sqlite implementations.
-     */
-    @Nullable
-    private static Index readIndex(SupportSQLiteDatabase database, String name, boolean unique) {
-        Cursor cursor = database.query("PRAGMA index_xinfo(`" + name + "`)");
-        try {
-            final int seqnoColumnIndex = cursor.getColumnIndex("seqno");
-            final int cidColumnIndex = cursor.getColumnIndex("cid");
-            final int nameColumnIndex = cursor.getColumnIndex("name");
-            if (seqnoColumnIndex == -1 || cidColumnIndex == -1 || nameColumnIndex == -1) {
-                // we cannot read them so better not validate any index.
-                return null;
-            }
-            final TreeMap<Integer, String> results = new TreeMap<>();
-
-            while (cursor.moveToNext()) {
-                int cid = cursor.getInt(cidColumnIndex);
-                if (cid < 0) {
-                    // Ignore SQLite row ID
-                    continue;
-                }
-                int seq = cursor.getInt(seqnoColumnIndex);
-                String columnName = cursor.getString(nameColumnIndex);
-                results.put(seq, columnName);
-            }
-            final List<String> columns = new ArrayList<>(results.size());
-            columns.addAll(results.values());
-            return new Index(name, unique, columns);
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * 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;
-        /**
-         * The column type after it is normalized to one of the basic types according to
-         * https://www.sqlite.org/datatype3.html Section 3.1.
-         * <p>
-         * This is the value Room uses for equality check.
-         */
-        @ColumnInfo.SQLiteTypeAffinity
-        public final int affinity;
-        /**
-         * Whether or not the column can be NULL.
-         */
-        public final boolean notNull;
-        /**
-         * 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;
-            this.affinity = findAffinity(type);
-        }
-
-        /**
-         * Implements https://www.sqlite.org/datatype3.html section 3.1
-         *
-         * @param type The type that was given to the sqlite
-         * @return The normalized type which is one of the 5 known affinities
-         */
-        @ColumnInfo.SQLiteTypeAffinity
-        private static int findAffinity(@Nullable String type) {
-            if (type == null) {
-                return ColumnInfo.BLOB;
-            }
-            String uppercaseType = type.toUpperCase(Locale.US);
-            if (uppercaseType.contains("INT")) {
-                return ColumnInfo.INTEGER;
-            }
-            if (uppercaseType.contains("CHAR")
-                    || uppercaseType.contains("CLOB")
-                    || uppercaseType.contains("TEXT")) {
-                return ColumnInfo.TEXT;
-            }
-            if (uppercaseType.contains("BLOB")) {
-                return ColumnInfo.BLOB;
-            }
-            if (uppercaseType.contains("REAL")
-                    || uppercaseType.contains("FLOA")
-                    || uppercaseType.contains("DOUB")) {
-                return ColumnInfo.REAL;
-            }
-            // sqlite returns NUMERIC here but it is like a catch all. We already
-            // have UNDEFINED so it is better to use UNDEFINED for consistency.
-            return ColumnInfo.UNDEFINED;
-        }
-
-        @Override
-        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 affinity == column.affinity;
-        }
-
-        /**
-         * 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 + affinity;
-            result = 31 * result + (notNull ? 1231 : 1237);
-            result = 31 * result + primaryKeyPosition;
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return "Column{"
-                    + "name='" + name + '\''
-                    + ", type='" + type + '\''
-                    + ", affinity='" + affinity + '\''
-                    + ", 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(@NonNull ForeignKeyWithSequence o) {
-            final int idCmp = mId - o.mId;
-            if (idCmp == 0) {
-                return mSequence - o.mSequence;
-            } else {
-                return idCmp;
-            }
-        }
-    }
-
-    /**
-     * Holds the information about an SQLite index
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static class Index {
-        // should match the value in Index.kt
-        public static final String DEFAULT_PREFIX = "index_";
-        public final String name;
-        public final boolean unique;
-        public final List<String> columns;
-
-        public Index(String name, boolean unique, List<String> columns) {
-            this.name = name;
-            this.unique = unique;
-            this.columns = columns;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            Index index = (Index) o;
-            if (unique != index.unique) {
-                return false;
-            }
-            if (!columns.equals(index.columns)) {
-                return false;
-            }
-            if (name.startsWith(Index.DEFAULT_PREFIX)) {
-                return index.name.startsWith(Index.DEFAULT_PREFIX);
-            } else {
-                return name.equals(index.name);
-            }
-        }
-
-        @Override
-        public int hashCode() {
-            int result;
-            if (name.startsWith(DEFAULT_PREFIX)) {
-                result = DEFAULT_PREFIX.hashCode();
-            } else {
-                result = name.hashCode();
-            }
-            result = 31 * result + (unique ? 1 : 0);
-            result = 31 * result + columns.hashCode();
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return "Index{"
-                    + "name='" + name + '\''
-                    + ", unique=" + unique
-                    + ", columns=" + columns
-                    + '}';
-        }
-    }
-}
diff --git a/android/bluetooth/BluetoothA2dp.java b/android/bluetooth/BluetoothA2dp.java
index 35a21a4..94fd138 100644
--- a/android/bluetooth/BluetoothA2dp.java
+++ b/android/bluetooth/BluetoothA2dp.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.media.AudioManager;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.ParcelUuid;
@@ -262,7 +261,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
             return false;
         }
@@ -300,11 +299,7 @@
     }
 
     /**
-     * Initiate connection to a profile of the remote bluetooth device.
-     *
-     * <p> Currently, the system supports only 1 connection to the
-     * A2DP profile. The API will automatically disconnect connected
-     * devices before connecting.
+     * Initiate connection to a profile of the remote Bluetooth device.
      *
      * <p> This API returns false in scenarios like the profile on the
      * device is already connected or Bluetooth is not turned on.
@@ -603,34 +598,6 @@
     }
 
     /**
-     * Tells remote device to adjust volume. Only if absolute volume is
-     * supported. Uses the following values:
-     * <ul>
-     * <li>{@link AudioManager#ADJUST_LOWER}</li>
-     * <li>{@link AudioManager#ADJUST_RAISE}</li>
-     * <li>{@link AudioManager#ADJUST_MUTE}</li>
-     * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
-     * </ul>
-     *
-     * @param direction One of the supported adjust values.
-     * @hide
-     */
-    public void adjustAvrcpAbsoluteVolume(int direction) {
-        if (DBG) Log.d(TAG, "adjustAvrcpAbsoluteVolume");
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()) {
-                mService.adjustAvrcpAbsoluteVolume(direction);
-            }
-            if (mService == null) Log.w(TAG, "Proxy not attached to service");
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to BT service in adjustAvrcpAbsoluteVolume()", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-    }
-
-    /**
      * Tells remote device to set an absolute volume. Only if absolute volume is supported
      *
      * @param volume Absolute volume to be set on AVRCP side
@@ -699,15 +666,17 @@
     /**
      * Gets the current codec status (configuration and capability).
      *
+     * @param device the remote Bluetooth device. If null, use the current
+     * active A2DP Bluetooth device.
      * @return the current codec status
      * @hide
      */
-    public BluetoothCodecStatus getCodecStatus() {
-        if (DBG) Log.d(TAG, "getCodecStatus");
+    public BluetoothCodecStatus getCodecStatus(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
-                return mService.getCodecStatus();
+                return mService.getCodecStatus(device);
             }
             if (mService == null) {
                 Log.w(TAG, "Proxy not attached to service");
@@ -724,15 +693,18 @@
     /**
      * Sets the codec configuration preference.
      *
+     * @param device the remote Bluetooth device. If null, use the current
+     * active A2DP Bluetooth device.
      * @param codecConfig the codec configuration preference
      * @hide
      */
-    public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
-        if (DBG) Log.d(TAG, "setCodecConfigPreference");
+    public void setCodecConfigPreference(BluetoothDevice device,
+                                         BluetoothCodecConfig codecConfig) {
+        if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
-                mService.setCodecConfigPreference(codecConfig);
+                mService.setCodecConfigPreference(device, codecConfig);
             }
             if (mService == null) Log.w(TAG, "Proxy not attached to service");
             return;
@@ -747,36 +719,42 @@
     /**
      * Enables the optional codecs.
      *
+     * @param device the remote Bluetooth device. If null, use the currect
+     * active A2DP Bluetooth device.
      * @hide
      */
-    public void enableOptionalCodecs() {
-        if (DBG) Log.d(TAG, "enableOptionalCodecs");
-        enableDisableOptionalCodecs(true);
+    public void enableOptionalCodecs(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+        enableDisableOptionalCodecs(device, true);
     }
 
     /**
      * Disables the optional codecs.
      *
+     * @param device the remote Bluetooth device. If null, use the currect
+     * active A2DP Bluetooth device.
      * @hide
      */
-    public void disableOptionalCodecs() {
-        if (DBG) Log.d(TAG, "disableOptionalCodecs");
-        enableDisableOptionalCodecs(false);
+    public void disableOptionalCodecs(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+        enableDisableOptionalCodecs(device, false);
     }
 
     /**
      * Enables or disables the optional codecs.
      *
+     * @param device the remote Bluetooth device. If null, use the currect
+     * active A2DP Bluetooth device.
      * @param enable if true, enable the optional codecs, other disable them
      */
-    private void enableDisableOptionalCodecs(boolean enable) {
+    private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
                 if (enable) {
-                    mService.enableOptionalCodecs();
+                    mService.enableOptionalCodecs(device);
                 } else {
-                    mService.disableOptionalCodecs();
+                    mService.disableOptionalCodecs(device);
                 }
             }
             if (mService == null) Log.w(TAG, "Proxy not attached to service");
diff --git a/android/bluetooth/BluetoothA2dpSink.java b/android/bluetooth/BluetoothA2dpSink.java
index faab000..13f0aaf 100644
--- a/android/bluetooth/BluetoothA2dpSink.java
+++ b/android/bluetooth/BluetoothA2dpSink.java
@@ -182,7 +182,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothAdapter.java b/android/bluetooth/BluetoothAdapter.java
index 9f11d6e..ee667c2 100644
--- a/android/bluetooth/BluetoothAdapter.java
+++ b/android/bluetooth/BluetoothAdapter.java
@@ -438,7 +438,7 @@
      * Intent used to broadcast the change in connection state of the local
      * Bluetooth adapter to a profile of the remote device. When the adapter is
      * not connected to any profiles of any remote devices and it attempts a
-     * connection to a profile this intent will sent. Once connected, this intent
+     * connection to a profile this intent will be sent. Once connected, this intent
      * will not be sent for any more connection attempts to any profiles of any
      * remote device. When the adapter disconnects from the last profile its
      * connected to of any remote device, this intent will be sent.
@@ -541,13 +541,14 @@
             "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
 
     /** The profile is in disconnected state */
-    public static final int STATE_DISCONNECTED = 0;
+    public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
     /** The profile is in connecting state */
-    public static final int STATE_CONNECTING = 1;
+    public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
     /** The profile is in connected state */
-    public static final int STATE_CONNECTED = 2;
+    public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
     /** The profile is in disconnecting state */
-    public static final int STATE_DISCONNECTING = 3;
+    public static final int STATE_DISCONNECTING =
+            BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
 
     /** @hide */
     public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
@@ -2307,6 +2308,9 @@
         } else if (profile == BluetoothProfile.HID_DEVICE) {
             BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.HEARING_AID) {
+            BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -2389,6 +2393,9 @@
                 BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
                 hidDevice.close();
                 break;
+            case BluetoothProfile.HEARING_AID:
+                BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
+                hearingAid.close();
         }
     }
 
diff --git a/android/bluetooth/BluetoothAvrcpController.java b/android/bluetooth/BluetoothAvrcpController.java
index 5f0e5d9..e7c8944 100644
--- a/android/bluetooth/BluetoothAvrcpController.java
+++ b/android/bluetooth/BluetoothAvrcpController.java
@@ -138,7 +138,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothGatt.java b/android/bluetooth/BluetoothGatt.java
index a2af342..3df4336 100644
--- a/android/bluetooth/BluetoothGatt.java
+++ b/android/bluetooth/BluetoothGatt.java
@@ -1507,6 +1507,38 @@
     }
 
     /**
+     * Request an LE connection parameter update.
+     *
+     * <p>This function will send an LE connection parameters update request to the remote device.
+     *
+     * @return true, if the request is send to the Bluetooth stack.
+     * @hide
+     */
+    public boolean requestLeConnectionUpdate(int minConnectionInterval,
+                                                 int maxConnectionInterval,
+                                                 int slaveLatency, int supervisionTimeout) {
+        if (DBG) {
+            Log.d(TAG, "requestLeConnectionUpdate() - min=(" + minConnectionInterval
+                       + ")" + (1.25 * minConnectionInterval)
+                       + "msec, max=(" + maxConnectionInterval + ")"
+                        + (1.25 * maxConnectionInterval) + "msec, latency=" + slaveLatency
+                       + ", timeout=" + supervisionTimeout + "msec");
+        }
+        if (mService == null || mClientIf == 0) return false;
+
+        try {
+            mService.leConnectionUpdate(mClientIf, mDevice.getAddress(),
+                                               minConnectionInterval, maxConnectionInterval,
+                                               slaveLatency, supervisionTimeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
      * with {@link BluetoothProfile#GATT} as argument
      *
diff --git a/android/bluetooth/BluetoothHeadsetClient.java b/android/bluetooth/BluetoothHeadsetClient.java
index 031287f..397b906 100644
--- a/android/bluetooth/BluetoothHeadsetClient.java
+++ b/android/bluetooth/BluetoothHeadsetClient.java
@@ -427,7 +427,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothHealth.java b/android/bluetooth/BluetoothHealth.java
index 57a0197..b967fb2 100644
--- a/android/bluetooth/BluetoothHealth.java
+++ b/android/bluetooth/BluetoothHealth.java
@@ -491,7 +491,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothHearingAid.java b/android/bluetooth/BluetoothHearingAid.java
new file mode 100644
index 0000000..8f8083e
--- /dev/null
+++ b/android/bluetooth/BluetoothHearingAid.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Hearing Aid
+ * profile.
+ *
+ * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHearingAid proxy object.
+ *
+ * <p> Each method is protected with its appropriate permission.
+ * @hide
+ */
+public final class BluetoothHearingAid implements BluetoothProfile {
+    private static final String TAG = "BluetoothHearingAid";
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    /**
+     * Intent used to broadcast the change in connection state of the Hearing Aid
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+            "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in the Playing state of the Hearing Aid
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PLAYING_STATE_CHANGED =
+            "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the selection of a connected device as active.
+     *
+     * <p>This intent will have one extra:
+     * <ul>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+     * be null if no device is active. </li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+            "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
+
+    /**
+     * Hearing Aid device is streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_PLAYING = 10;
+
+    /**
+     * Hearing Aid device is NOT streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_NOT_PLAYING = 11;
+
+    /** This device represents Left Hearing Aid. */
+    public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
+
+    /** This device represents Right Hearing Aid. */
+    public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
+
+    /** This device is Monaural. */
+    public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
+
+    /** This device is Binaural (should receive only left or right audio). */
+    public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
+
+    /** Can't read ClientID for this device */
+    public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+    @GuardedBy("mServiceLock")
+    private IBluetoothHearingAid mService;
+    private BluetoothAdapter mAdapter;
+
+    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+                public void onBluetoothStateChange(boolean up) {
+                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+                    if (!up) {
+                        if (VDBG) Log.d(TAG, "Unbinding service...");
+                        try {
+                            mServiceLock.writeLock().lock();
+                            mService = null;
+                            mContext.unbindService(mConnection);
+                        } catch (Exception re) {
+                            Log.e(TAG, "", re);
+                        } finally {
+                            mServiceLock.writeLock().unlock();
+                        }
+                    } else {
+                        try {
+                            mServiceLock.readLock().lock();
+                            if (mService == null) {
+                                if (VDBG) Log.d(TAG, "Binding service...");
+                                doBind();
+                            }
+                        } catch (Exception re) {
+                            Log.e(TAG, "", re);
+                        } finally {
+                            mServiceLock.readLock().unlock();
+                        }
+                    }
+                }
+            };
+
+    /**
+     * Create a BluetoothHearingAid proxy object for interacting with the local
+     * Bluetooth Hearing Aid service.
+     */
+    /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
+        mContext = context;
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        doBind();
+    }
+
+    void doBind() {
+        Intent intent = new Intent(IBluetoothHearingAid.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                android.os.Process.myUserHandle())) {
+            Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
+            return;
+        }
+    }
+
+    /*package*/ void close() {
+        mServiceListener = null;
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (Exception e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        try {
+            mServiceLock.writeLock().lock();
+            if (mService != null) {
+                mService = null;
+                mContext.unbindService(mConnection);
+            }
+        } catch (Exception re) {
+            Log.e(TAG, "", re);
+        } finally {
+            mServiceLock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public void finalize() {
+        // The empty finalize needs to be kept or the
+        // cts signature tests would fail.
+    }
+
+    /**
+     * Initiate connection to a profile of the remote bluetooth device.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is already connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that
+     * connection state intent for the profile will be broadcasted with
+     * the state. Users can get the connection state of the profile
+     * from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled() && isValidDevice(device)) {
+                return mService.connect(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Initiate disconnection from a profile
+     *
+     * <p> This API will return false in scenarios like the profile on the
+     * Bluetooth device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that the connection state change
+     * intent will be broadcasted with the state. Users can get the
+     * disconnection state of the profile from this intent.
+     *
+     * <p> If the disconnection is initiated by a remote device, the state
+     * will transition from {@link #STATE_CONNECTED} to
+     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+     * host (local) device the state will transition from
+     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+     * state {@link #STATE_DISCONNECTED}. The transition to
+     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+     * two scenarios.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled() && isValidDevice(device)) {
+                return mService.disconnect(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (VDBG) log("getConnectedDevices()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getConnectedDevices();
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (VDBG) log("getDevicesMatchingStates()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getDevicesMatchingConnectionStates(states);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getConnectionState(BluetoothDevice device) {
+        if (VDBG) log("getState(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getConnectionState(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Select a connected device as active.
+     *
+     * The active device selection is per profile. An active device's
+     * purpose is profile-specific. For example, Hearing Aid audio
+     * streaming is to the active Hearing Aid device. If a remote device
+     * is not connected, it cannot be selected as active.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is not connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that the
+     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+     * with the active device.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device the remote Bluetooth device. Could be null to clear
+     * the active device and stop streaming audio to a Bluetooth device.
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+        if (DBG) log("setActiveDevice(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && ((device == null) || isValidDevice(device))) {
+                mService.setActiveDevice(device);
+                return true;
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Check whether the device is active.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * permission.
+     *
+     * @return the connected device that is active or null if no device
+     * is active
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public boolean isActiveDevice(@Nullable BluetoothDevice device) {
+        if (VDBG) log("isActiveDevice()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && ((device == null) || isValidDevice(device))) {
+                return mService.isActiveDevice(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+     * {@link #PRIORITY_OFF},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                if (priority != BluetoothProfile.PRIORITY_OFF
+                        && priority != BluetoothProfile.PRIORITY_ON) {
+                    return false;
+                }
+                return mService.setPriority(device, priority);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the priority of the profile.
+     *
+     * <p> The priority can be any of:
+     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) log("getPriority(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getPriority(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.PRIORITY_OFF;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.PRIORITY_OFF;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Helper for converting a state to a string.
+     *
+     * For debug use only - strings are not internationalized.
+     *
+     * @hide
+     */
+    public static String stateToString(int state) {
+        switch (state) {
+            case STATE_DISCONNECTED:
+                return "disconnected";
+            case STATE_CONNECTING:
+                return "connecting";
+            case STATE_CONNECTED:
+                return "connected";
+            case STATE_DISCONNECTING:
+                return "disconnecting";
+            case STATE_PLAYING:
+                return "playing";
+            case STATE_NOT_PLAYING:
+                return "not playing";
+            default:
+                return "<unknown state " + state + ">";
+        }
+    }
+
+    /**
+     * Get the volume of the device.
+     *
+     * <p> The volume is between -128 dB (mute) to 0 dB.
+     *
+     * @return volume of the hearing aid device.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getVolume() {
+        if (VDBG) {
+            log("getVolume()");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getVolume();
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return 0;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return 0;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Tells remote device to adjust volume. Uses the following values:
+     * <ul>
+     * <li>{@link AudioManager#ADJUST_LOWER}</li>
+     * <li>{@link AudioManager#ADJUST_RAISE}</li>
+     * <li>{@link AudioManager#ADJUST_MUTE}</li>
+     * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+     * </ul>
+     *
+     * @param direction One of the supported adjust values.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void adjustVolume(int direction) {
+        if (DBG) log("adjustVolume(" + direction + ")");
+
+        try {
+            mServiceLock.readLock().lock();
+
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return;
+            }
+
+            if (!isEnabled()) return;
+
+            mService.adjustVolume(direction);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Tells remote device to set an absolute volume.
+     *
+     * @param volume Absolute volume to be set on remote
+     * @hide
+     */
+    public void setVolume(int volume) {
+        if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
+
+        try {
+            mServiceLock.readLock().lock();
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return;
+            }
+
+            if (!isEnabled()) return;
+
+            mService.setVolume(volume);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the CustomerId of the device.
+     *
+     * @param device Bluetooth device
+     * @return the CustomerId of the device
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public long getHiSyncId(BluetoothDevice device) {
+        if (VDBG) {
+            log("getCustomerId(" + device + ")");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return HI_SYNC_ID_INVALID;
+            }
+
+            if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
+
+            return mService.getHiSyncId(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return HI_SYNC_ID_INVALID;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the side of the device.
+     *
+     * @param device Bluetooth device.
+     * @return SIDE_LEFT or SIDE_RIGHT
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getDeviceSide(BluetoothDevice device) {
+        if (VDBG) {
+            log("getDeviceSide(" + device + ")");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getDeviceSide(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return SIDE_LEFT;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return SIDE_LEFT;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the mode of the device.
+     *
+     * @param device Bluetooth device
+     * @return MODE_MONAURAL or MODE_BINAURAL
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getDeviceMode(BluetoothDevice device) {
+        if (VDBG) {
+            log("getDeviceMode(" + device + ")");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getDeviceMode(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return MODE_MONAURAL;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return MODE_MONAURAL;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) Log.d(TAG, "Proxy object connected");
+            try {
+                mServiceLock.writeLock().lock();
+                mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+            } finally {
+                mServiceLock.writeLock().unlock();
+            }
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
+                                                    BluetoothHearingAid.this);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) Log.d(TAG, "Proxy object disconnected");
+            try {
+                mServiceLock.writeLock().lock();
+                mService = null;
+            } finally {
+                mServiceLock.writeLock().unlock();
+            }
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
+            }
+        }
+    };
+
+    private boolean isEnabled() {
+        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+        if (device == null) return false;
+
+        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+        return false;
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+}
diff --git a/android/bluetooth/BluetoothHidDevice.java b/android/bluetooth/BluetoothHidDevice.java
index 2fab305..af99bf7 100644
--- a/android/bluetooth/BluetoothHidDevice.java
+++ b/android/bluetooth/BluetoothHidDevice.java
@@ -27,8 +27,8 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Provides the public APIs to control the Bluetooth HID Device profile.
@@ -37,7 +37,6 @@
  * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
  */
 public final class BluetoothHidDevice implements BluetoothProfile {
-
     private static final String TAG = BluetoothHidDevice.class.getSimpleName();
 
     /**
@@ -62,106 +61,327 @@
             "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
 
     /**
-     * Constants representing device subclass.
+     * Constant representing unspecified HID device subclass.
      *
      * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
      */
     public static final byte SUBCLASS1_NONE = (byte) 0x00;
+    /**
+     * Constant representing keyboard subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
+    /**
+     * Constant representing mouse subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS1_MOUSE = (byte) 0x80;
+    /**
+     * Constant representing combo keyboard and mouse subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS1_COMBO = (byte) 0xC0;
 
+    /**
+     * Constant representing uncategorized HID device subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00;
+    /**
+     * Constant representing joystick subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01;
+    /**
+     * Constant representing gamepad subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
+    /**
+     * Constant representing remote control subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
+    /**
+     * Constant representing sensing device subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
+    /**
+     * Constant representing digitizer tablet subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05;
+    /**
+     * Constant representing card reader subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
 
     /**
-     * Constants representing report types.
+     * Constant representing HID Input Report type.
      *
-     * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[])
+     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
      */
     public static final byte REPORT_TYPE_INPUT = (byte) 1;
+    /**
+     * Constant representing HID Output Report type.
+     *
+     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
+     */
     public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
+    /**
+     * Constant representing HID Feature Report type.
+     *
+     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
+     */
     public static final byte REPORT_TYPE_FEATURE = (byte) 3;
 
     /**
-     * Constants representing error response for Set Report.
+     * Constant representing success response for Set Report.
      *
-     * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
      */
     public static final byte ERROR_RSP_SUCCESS = (byte) 0;
+    /**
+     * Constant representing error response for Set Report due to "not ready".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_NOT_READY = (byte) 1;
+    /**
+     * Constant representing error response for Set Report due to "invalid report ID".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2;
+    /**
+     * Constant representing error response for Set Report due to "unsupported request".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3;
+    /**
+     * Constant representing error response for Set Report due to "invalid parameter".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4;
+    /**
+     * Constant representing error response for Set Report with unknown reason.
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
 
     /**
-     * Constants representing protocol mode used set by host. Default is always {@link
+     * Constant representing boot protocol mode used set by host. Default is always {@link
      * #PROTOCOL_REPORT_MODE} unless notified otherwise.
      *
-     * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte)
+     * @see Callback#onSetProtocol(BluetoothDevice, byte)
      */
     public static final byte PROTOCOL_BOOT_MODE = (byte) 0;
+    /**
+     * Constant representing report protocol mode used set by host. Default is always {@link
+     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
+     *
+     * @see Callback#onSetProtocol(BluetoothDevice, byte)
+     */
     public static final byte PROTOCOL_REPORT_MODE = (byte) 1;
 
+    /**
+     * The template class that applications use to call callback functions on events from the HID
+     * host. Callback functions are wrapped in this class and registered to the Android system
+     * during app registration.
+     */
+    public abstract static class Callback {
+
+        private static final String TAG = "BluetoothHidDevCallback";
+
+        /**
+         * Callback called when application registration state changes. Usually it's called due to
+         * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
+         * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
+         * unsolicited in case e.g. Bluetooth was turned off in which case application is
+         * unregistered automatically.
+         *
+         * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently
+         *     has Virtual Cable established with device. Only valid when application is registered,
+         *     can be <code>null</code>.
+         * @param registered <code>true</code> if application is registered, <code>false</code>
+         *     otherwise.
+         */
+        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
+            Log.d(
+                    TAG,
+                    "onAppStatusChanged: pluggedDevice="
+                            + pluggedDevice
+                            + " registered="
+                            + registered);
+        }
+
+        /**
+         * Callback called when connection state with remote host was changed. Application can
+         * assume than Virtual Cable is established when called with {@link
+         * BluetoothProfile#STATE_CONNECTED} <code>state</code>.
+         *
+         * @param device {@link BluetoothDevice} object representing host device which connection
+         *     state was changed.
+         * @param state Connection state as defined in {@link BluetoothProfile}.
+         */
+        public void onConnectionStateChanged(BluetoothDevice device, int state) {
+            Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
+        }
+
+        /**
+         * Callback called when GET_REPORT is received from remote host. Should be replied by
+         * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
+         * byte[])}.
+         *
+         * @param type Requested Report Type.
+         * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
+         * @param bufferSize Requested buffer size, application shall respond with at least given
+         *     number of bytes.
+         */
+        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+            Log.d(
+                    TAG,
+                    "onGetReport: device="
+                            + device
+                            + " type="
+                            + type
+                            + " id="
+                            + id
+                            + " bufferSize="
+                            + bufferSize);
+        }
+
+        /**
+         * Callback called when SET_REPORT is received from remote host. In case received data are
+         * invalid, application shall respond with {@link
+         * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
+         *
+         * @param type Report Type.
+         * @param id Report Id.
+         * @param data Report data.
+         */
+        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+            Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
+        }
+
+        /**
+         * Callback called when SET_PROTOCOL is received from remote host. Application shall use
+         * this information to send only reports valid for given protocol mode. By default, {@link
+         * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
+         *
+         * @param protocol Protocol Mode.
+         */
+        public void onSetProtocol(BluetoothDevice device, byte protocol) {
+            Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
+        }
+
+        /**
+         * Callback called when report data is received over interrupt channel. Report Type is
+         * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
+         *
+         * @param reportId Report Id.
+         * @param data Report data.
+         */
+        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+            Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
+        }
+
+        /**
+         * Callback called when Virtual Cable is removed. After this callback is received connection
+         * will be disconnected automatically.
+         */
+        public void onVirtualCableUnplug(BluetoothDevice device) {
+            Log.d(TAG, "onVirtualCableUnplug: device=" + device);
+        }
+    }
+
     private Context mContext;
-
     private ServiceListener mServiceListener;
-
     private volatile IBluetoothHidDevice mService;
-
     private BluetoothAdapter mAdapter;
 
-    private static class BluetoothHidDeviceCallbackWrapper
-            extends IBluetoothHidDeviceCallback.Stub {
+    private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
 
-        private BluetoothHidDeviceCallback mCallback;
+        private final Executor mExecutor;
+        private final Callback mCallback;
 
-        public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) {
+        CallbackWrapper(Executor executor, Callback callback) {
+            mExecutor = executor;
             mCallback = callback;
         }
 
         @Override
         public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            mCallback.onAppStatusChanged(pluggedDevice, registered);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
         }
 
         @Override
         public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            mCallback.onConnectionStateChanged(device, state);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
         }
 
         @Override
         public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            mCallback.onGetReport(device, type, id, bufferSize);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
         }
 
         @Override
         public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            mCallback.onSetReport(device, type, id, data);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
         }
 
         @Override
         public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            mCallback.onSetProtocol(device, protocol);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
         }
 
         @Override
         public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            mCallback.onInterruptData(device, reportId, data);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
         }
 
         @Override
         public void onVirtualCableUnplug(BluetoothDevice device) {
-            mCallback.onVirtualCableUnplug(device);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
         }
     }
 
@@ -213,8 +433,6 @@
             };
 
     BluetoothHidDevice(Context context, ServiceListener listener) {
-        Log.v(TAG, "BluetoothHidDevice");
-
         mContext = context;
         mServiceListener = listener;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -236,7 +454,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
             return false;
         }
@@ -245,7 +463,6 @@
     }
 
     void doUnbind() {
-        Log.d(TAG, "Unbinding HidDevService");
         if (mService != null) {
             mService = null;
             try {
@@ -257,8 +474,6 @@
     }
 
     void close() {
-        Log.v(TAG, "close()");
-
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
@@ -277,8 +492,6 @@
     /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
-        Log.v(TAG, "getConnectedDevices()");
-
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
@@ -290,14 +503,12 @@
             Log.w(TAG, "Proxy not attached to service");
         }
 
-        return new ArrayList<BluetoothDevice>();
+        return new ArrayList<>();
     }
 
     /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
-
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
@@ -309,14 +520,12 @@
             Log.w(TAG, "Proxy not attached to service");
         }
 
-        return new ArrayList<BluetoothDevice>();
+        return new ArrayList<>();
     }
 
     /** {@inheritDoc} */
     @Override
     public int getConnectionState(BluetoothDevice device) {
-        Log.v(TAG, "getConnectionState(): device=" + device);
-
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
@@ -336,9 +545,9 @@
      * when application is registered. Only one application can be registered at one time. When an
      * application is registered, the HID Host service will be disabled until it is unregistered.
      * When no longer used, application should be unregistered using {@link #unregisterApp()}. The
-     * registration status should be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to
-     * the return value of this method.
+     * app will be automatically unregistered if it is not foreground. The registration status
+     * should be tracked by the application by handling callback from Callback#onAppStatusChanged.
+     * The app registration status is not related to the return value of this method.
      *
      * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID
      *     Device SDP record is required.
@@ -348,27 +557,36 @@
      * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The
      *     Outgoing QoS Settings is not required. Use null or default
      *     BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be
-     *     sent. The BluetoothHidDeviceCallback object is required.
+     * @param executor {@link Executor} object on which callback will be executed. The Executor
+     *     object is required.
+     * @param callback {@link Callback} object to which callback messages will be sent. The Callback
+     *     object is required.
      * @return true if the command is successfully sent; otherwise false.
      */
-    public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
-            BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
-            BluetoothHidDeviceCallback callback) {
-        Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos
-                        + " callback=" + callback);
-
+    public boolean registerApp(
+            BluetoothHidDeviceAppSdpSettings sdp,
+            BluetoothHidDeviceAppQosSettings inQos,
+            BluetoothHidDeviceAppQosSettings outQos,
+            Executor executor,
+            Callback callback) {
         boolean result = false;
 
-        if (sdp == null || callback == null) {
-            return false;
+        if (sdp == null) {
+            throw new IllegalArgumentException("sdp parameter cannot be null");
+        }
+
+        if (executor == null) {
+            throw new IllegalArgumentException("executor parameter cannot be null");
+        }
+
+        if (callback == null) {
+            throw new IllegalArgumentException("callback parameter cannot be null");
         }
 
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
-                BluetoothHidDeviceCallbackWrapper cbw =
-                        new BluetoothHidDeviceCallbackWrapper(callback);
+                CallbackWrapper cbw = new CallbackWrapper(executor, callback);
                 result = service.registerApp(sdp, inQos, outQos, cbw);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
@@ -384,16 +602,13 @@
      * Unregisters application. Active connection will be disconnected and no new connections will
      * be allowed until registered again using {@link #registerApp
      * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should
-     * be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to
-     * the return value of this method.
+     * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be
+     * tracked by the application by handling callback from Callback#onAppStatusChanged. The app
+     * registration status is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean unregisterApp() {
-        Log.v(TAG, "unregisterApp()");
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -437,7 +652,7 @@
 
     /**
      * Sends report to remote host as reply for GET_REPORT request from {@link
-     * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
+     * Callback#onGetReport(BluetoothDevice, byte, byte, int)}.
      *
      * @param type Report Type, as in request.
      * @param id Report Id, as in request.
@@ -445,8 +660,6 @@
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-        Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -465,14 +678,12 @@
 
     /**
      * Sends error handshake message as reply for invalid SET_REPORT request from {@link
-     * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
+     * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
      *
      * @param error Error to be sent for SET_REPORT via HANDSHAKE.
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean reportError(BluetoothDevice device, byte error) {
-        Log.v(TAG, "reportError(): device=" + device + " error=" + error);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -490,42 +701,14 @@
     }
 
     /**
-     * Sends Virtual Cable Unplug to currently connected host.
-     *
-     * @return
-     * {@hide}
-     */
-    public boolean unplug(BluetoothDevice device) {
-        Log.v(TAG, "unplug(): device=" + device);
-
-        boolean result = false;
-
-        final IBluetoothHidDevice service = mService;
-        if (service != null) {
-            try {
-                result = service.unplug(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-
-        return result;
-    }
-
-    /**
      * Initiates connection to host which is currently paired with this device. If the application
      * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
-     * tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to
-     * the return value of this method.
+     * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
+     * connection state is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean connect(BluetoothDevice device) {
-        Log.v(TAG, "connect(): device=" + device);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -544,14 +727,12 @@
 
     /**
      * Disconnects from currently connected host. The connection state should be tracked by the
-     * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged.
-     * The connection state is not related to the return value of this method.
+     * application by handling callback from Callback#onConnectionStateChanged. The connection state
+     * is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean disconnect(BluetoothDevice device) {
-        Log.v(TAG, "disconnect(): device=" + device);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
diff --git a/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index c05df2d..a485b89 100644
--- a/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -29,12 +29,12 @@
  */
 public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
 
-    public final int serviceType;
-    public final int tokenRate;
-    public final int tokenBucketSize;
-    public final int peakBandwidth;
-    public final int latency;
-    public final int delayVariation;
+    private final int mServiceType;
+    private final int mTokenRate;
+    private final int mTokenBucketSize;
+    private final int mPeakBandwidth;
+    private final int mLatency;
+    private final int mDelayVariation;
 
     public static final int SERVICE_NO_TRAFFIC = 0x00;
     public static final int SERVICE_BEST_EFFORT = 0x01;
@@ -44,38 +44,53 @@
 
     /**
      * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
-     * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
-     * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters.
+     * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and
+     * Appendix D for parameters.
      *
-     * @param serviceType L2CAP service type
-     * @param tokenRate L2CAP token rate
-     * @param tokenBucketSize L2CAP token bucket size
-     * @param peakBandwidth L2CAP peak bandwidth
-     * @param latency L2CAP latency
-     * @param delayVariation L2CAP delay variation
+     * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT
+     * @param tokenRate L2CAP token rate, default = 0
+     * @param tokenBucketSize L2CAP token bucket size, default = 0
+     * @param peakBandwidth L2CAP peak bandwidth, default = 0
+     * @param latency L2CAP latency, default = MAX
+     * @param delayVariation L2CAP delay variation, default = MAX
      */
-    public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize,
-            int peakBandwidth, int latency, int delayVariation) {
-        this.serviceType = serviceType;
-        this.tokenRate = tokenRate;
-        this.tokenBucketSize = tokenBucketSize;
-        this.peakBandwidth = peakBandwidth;
-        this.latency = latency;
-        this.delayVariation = delayVariation;
+    public BluetoothHidDeviceAppQosSettings(
+            int serviceType,
+            int tokenRate,
+            int tokenBucketSize,
+            int peakBandwidth,
+            int latency,
+            int delayVariation) {
+        mServiceType = serviceType;
+        mTokenRate = tokenRate;
+        mTokenBucketSize = tokenBucketSize;
+        mPeakBandwidth = peakBandwidth;
+        mLatency = latency;
+        mDelayVariation = delayVariation;
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothHidDeviceAppQosSettings) {
-            BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o;
-            return this.serviceType == qos.serviceType
-                    && this.tokenRate == qos.tokenRate
-                    && this.tokenBucketSize == qos.tokenBucketSize
-                    && this.peakBandwidth == qos.peakBandwidth
-                    && this.latency == qos.latency
-                    && this.delayVariation == qos.delayVariation;
-        }
-        return false;
+    public int getServiceType() {
+        return mServiceType;
+    }
+
+    public int getTokenRate() {
+        return mTokenRate;
+    }
+
+    public int getTokenBucketSize() {
+        return mTokenBucketSize;
+    }
+
+    public int getPeakBandwidth() {
+        return mPeakBandwidth;
+    }
+
+    public int getLatency() {
+        return mLatency;
+    }
+
+    public int getDelayVariation() {
+        return mDelayVariation;
     }
 
     @Override
@@ -106,104 +121,11 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(serviceType);
-        out.writeInt(tokenRate);
-        out.writeInt(tokenBucketSize);
-        out.writeInt(peakBandwidth);
-        out.writeInt(latency);
-        out.writeInt(delayVariation);
-    }
-
-    /** @return an int array representation of this instance */
-    public int[] toArray() {
-        return new int[] {
-            serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
-        };
-    }
-
-    /** A helper to build the BluetoothHidDeviceAppQosSettings object. */
-    public static class Builder {
-        // Optional parameters - initialized to default values
-        private int mServiceType = SERVICE_BEST_EFFORT;
-        private int mTokenRate = 0;
-        private int mTokenBucketSize = 0;
-        private int mPeakBandwidth = 0;
-        private int mLatency = MAX;
-        private int mDelayVariation = MAX;
-
-        /**
-         * Set the service type.
-         *
-         * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT,
-         *     SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one.
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified service type.
-         */
-        public Builder serviceType(int val) {
-            mServiceType = val;
-            return this;
-        }
-        /**
-         * Set the token rate.
-         *
-         * @param val token rate
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate.
-         */
-        public Builder tokenRate(int val) {
-            mTokenRate = val;
-            return this;
-        }
-
-        /**
-         * Set the bucket size.
-         *
-         * @param val bucket size
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size.
-         */
-        public Builder tokenBucketSize(int val) {
-            mTokenBucketSize = val;
-            return this;
-        }
-
-        /**
-         * Set the peak bandwidth.
-         *
-         * @param val peak bandwidth
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth.
-         */
-        public Builder peakBandwidth(int val) {
-            mPeakBandwidth = val;
-            return this;
-        }
-        /**
-         * Set the latency.
-         *
-         * @param val latency
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified latency.
-         */
-        public Builder latency(int val) {
-            mLatency = val;
-            return this;
-        }
-
-        /**
-         * Set the delay variation.
-         *
-         * @param val delay variation
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation.
-         */
-        public Builder delayVariation(int val) {
-            mDelayVariation = val;
-            return this;
-        }
-
-        /**
-         * Build the BluetoothHidDeviceAppQosSettings object.
-         *
-         * @return BluetoothHidDeviceAppQosSettings object with current settings.
-         */
-        public BluetoothHidDeviceAppQosSettings build() {
-            return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize,
-                    mPeakBandwidth, mLatency, mDelayVariation);
-        }
+        out.writeInt(mServiceType);
+        out.writeInt(mTokenRate);
+        out.writeInt(mTokenBucketSize);
+        out.writeInt(mPeakBandwidth);
+        out.writeInt(mLatency);
+        out.writeInt(mDelayVariation);
     }
 }
diff --git a/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 562c559..237082e 100644
--- a/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -19,7 +19,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
 
 /**
  * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application.
@@ -31,11 +30,11 @@
  */
 public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
 
-    public final String name;
-    public final String description;
-    public final String provider;
-    public final byte subclass;
-    public final byte[] descriptors;
+    private final String mName;
+    private final String mDescription;
+    private final String mProvider;
+    private final byte mSubclass;
+    private final byte[] mDescriptors;
 
     /**
      * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
@@ -52,24 +51,31 @@
      */
     public BluetoothHidDeviceAppSdpSettings(
             String name, String description, String provider, byte subclass, byte[] descriptors) {
-        this.name = name;
-        this.description = description;
-        this.provider = provider;
-        this.subclass = subclass;
-        this.descriptors = descriptors.clone();
+        mName = name;
+        mDescription = description;
+        mProvider = provider;
+        mSubclass = subclass;
+        mDescriptors = descriptors.clone();
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothHidDeviceAppSdpSettings) {
-            BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o;
-            return this.name.equals(sdp.name)
-                    && this.description.equals(sdp.description)
-                    && this.provider.equals(sdp.provider)
-                    && this.subclass == sdp.subclass
-                    && Arrays.equals(this.descriptors, sdp.descriptors);
-        }
-        return false;
+    public String getName() {
+        return mName;
+    }
+
+    public String getDescription() {
+        return mDescription;
+    }
+
+    public String getProvider() {
+        return mProvider;
+    }
+
+    public byte getSubclass() {
+        return mSubclass;
+    }
+
+    public byte[] getDescriptors() {
+        return mDescriptors;
     }
 
     @Override
@@ -99,10 +105,10 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeString(name);
-        out.writeString(description);
-        out.writeString(provider);
-        out.writeByte(subclass);
-        out.writeByteArray(descriptors);
+        out.writeString(mName);
+        out.writeString(mDescription);
+        out.writeString(mProvider);
+        out.writeByte(mSubclass);
+        out.writeByteArray(mDescriptors);
     }
 }
diff --git a/android/bluetooth/BluetoothHidDeviceCallback.java b/android/bluetooth/BluetoothHidDeviceCallback.java
deleted file mode 100644
index e71b00f..0000000
--- a/android/bluetooth/BluetoothHidDeviceCallback.java
+++ /dev/null
@@ -1,120 +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.bluetooth;
-
-import android.util.Log;
-
-/**
- * The template class that applications use to call callback functions on events from the HID host.
- * Callback functions are wrapped in this class and registered to the Android system during app
- * registration.
- *
- * <p>{@see BluetoothHidDevice}
- */
-public abstract class BluetoothHidDeviceCallback {
-
-    private static final String TAG = "BluetoothHidDevCallback";
-
-    /**
-     * Callback called when application registration state changes. Usually it's called due to
-     * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
-     * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
-     * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered
-     * automatically.
-     *
-     * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has
-     *     Virtual Cable established with device. Only valid when application is registered, can be
-     *     <code>null</code>.
-     * @param registered <code>true</code> if application is registered, <code>false</code>
-     *     otherwise.
-     */
-    public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-        Log.d(TAG,
-                "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered);
-    }
-
-    /**
-     * Callback called when connection state with remote host was changed. Application can assume
-     * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED}
-     * <code>state</code>.
-     *
-     * @param device {@link BluetoothDevice} object representing host device which connection state
-     *     was changed.
-     * @param state Connection state as defined in {@link BluetoothProfile}.
-     */
-    public void onConnectionStateChanged(BluetoothDevice device, int state) {
-        Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
-    }
-
-    /**
-     * Callback called when GET_REPORT is received from remote host. Should be replied by
-     * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
-     * byte[])}.
-     *
-     * @param type Requested Report Type.
-     * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
-     * @param bufferSize Requested buffer size, application shall respond with at least given number
-     *     of bytes.
-     */
-    public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-        Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize="
-                + bufferSize);
-    }
-
-    /**
-     * Callback called when SET_REPORT is received from remote host. In case received data are
-     * invalid, application shall respond with {@link
-     * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
-     *
-     * @param type Report Type.
-     * @param id Report Id.
-     * @param data Report data.
-     */
-    public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-        Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
-    }
-
-    /**
-     * Callback called when SET_PROTOCOL is received from remote host. Application shall use this
-     * information to send only reports valid for given protocol mode. By default, {@link
-     * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
-     *
-     * @param protocol Protocol Mode.
-     */
-    public void onSetProtocol(BluetoothDevice device, byte protocol) {
-        Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
-    }
-
-    /**
-     * Callback called when report data is received over interrupt channel. Report Type is assumed
-     * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
-     *
-     * @param reportId Report Id.
-     * @param data Report data.
-     */
-    public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-        Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
-    }
-
-    /**
-     * Callback called when Virtual Cable is removed. After this callback is
-     * received connection will be disconnected automatically.
-     */
-    public void onVirtualCableUnplug(BluetoothDevice device) {
-        Log.d(TAG, "onVirtualCableUnplug: device=" + device);
-    }
-}
diff --git a/android/bluetooth/BluetoothHidHost.java b/android/bluetooth/BluetoothHidHost.java
index 8ad0f9d..0ca39f1 100644
--- a/android/bluetooth/BluetoothHidHost.java
+++ b/android/bluetooth/BluetoothHidHost.java
@@ -279,7 +279,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothManager.java b/android/bluetooth/BluetoothManager.java
index 7e3bb05..11f8ab7 100644
--- a/android/bluetooth/BluetoothManager.java
+++ b/android/bluetooth/BluetoothManager.java
@@ -17,9 +17,11 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -47,6 +49,7 @@
  * @see BluetoothAdapter#getDefaultAdapter()
  */
 @SystemService(Context.BLUETOOTH_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
 public final class BluetoothManager {
     private static final String TAG = "BluetoothManager";
     private static final boolean DBG = true;
diff --git a/android/bluetooth/BluetoothMap.java b/android/bluetooth/BluetoothMap.java
index 5b55b23..0fa1d5d 100644
--- a/android/bluetooth/BluetoothMap.java
+++ b/android/bluetooth/BluetoothMap.java
@@ -109,7 +109,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothMapClient.java b/android/bluetooth/BluetoothMapClient.java
index af3b662..4f21d93 100644
--- a/android/bluetooth/BluetoothMapClient.java
+++ b/android/bluetooth/BluetoothMapClient.java
@@ -125,7 +125,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothPan.java b/android/bluetooth/BluetoothPan.java
index 866b063..9f401eb 100644
--- a/android/bluetooth/BluetoothPan.java
+++ b/android/bluetooth/BluetoothPan.java
@@ -147,7 +147,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothPbap.java b/android/bluetooth/BluetoothPbap.java
index 7944354..c60e9e0 100644
--- a/android/bluetooth/BluetoothPbap.java
+++ b/android/bluetooth/BluetoothPbap.java
@@ -163,7 +163,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothPbapClient.java b/android/bluetooth/BluetoothPbapClient.java
index 01b3f6e..1446adc 100644
--- a/android/bluetooth/BluetoothPbapClient.java
+++ b/android/bluetooth/BluetoothPbapClient.java
@@ -116,7 +116,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothProfile.java b/android/bluetooth/BluetoothProfile.java
index 0e2263f..6aeb94d 100644
--- a/android/bluetooth/BluetoothProfile.java
+++ b/android/bluetooth/BluetoothProfile.java
@@ -38,7 +38,7 @@
      * This extra represents the current connection state of the profile of the
      * Bluetooth device.
      */
-    public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+    String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
 
     /**
      * Extra for the connection state intents of the individual profiles.
@@ -46,123 +46,137 @@
      * This extra represents the previous connection state of the profile of the
      * Bluetooth device.
      */
-    public static final String EXTRA_PREVIOUS_STATE =
+    String EXTRA_PREVIOUS_STATE =
             "android.bluetooth.profile.extra.PREVIOUS_STATE";
 
     /** The profile is in disconnected state */
-    public static final int STATE_DISCONNECTED = 0;
+    int STATE_DISCONNECTED = 0;
     /** The profile is in connecting state */
-    public static final int STATE_CONNECTING = 1;
+    int STATE_CONNECTING = 1;
     /** The profile is in connected state */
-    public static final int STATE_CONNECTED = 2;
+    int STATE_CONNECTED = 2;
     /** The profile is in disconnecting state */
-    public static final int STATE_DISCONNECTING = 3;
+    int STATE_DISCONNECTING = 3;
 
     /**
      * Headset and Handsfree profile
      */
-    public static final int HEADSET = 1;
+    int HEADSET = 1;
 
     /**
      * A2DP profile.
      */
-    public static final int A2DP = 2;
+    int A2DP = 2;
 
     /**
      * Health Profile
      */
-    public static final int HEALTH = 3;
+    int HEALTH = 3;
 
     /**
      * HID Host
      *
      * @hide
      */
-    public static final int HID_HOST = 4;
+    int HID_HOST = 4;
 
     /**
      * PAN Profile
      *
      * @hide
      */
-    public static final int PAN = 5;
+    int PAN = 5;
 
     /**
      * PBAP
      *
      * @hide
      */
-    public static final int PBAP = 6;
+    int PBAP = 6;
 
     /**
      * GATT
      */
-    public static final int GATT = 7;
+    int GATT = 7;
 
     /**
      * GATT_SERVER
      */
-    public static final int GATT_SERVER = 8;
+    int GATT_SERVER = 8;
 
     /**
      * MAP Profile
      *
      * @hide
      */
-    public static final int MAP = 9;
+    int MAP = 9;
 
     /*
      * SAP Profile
      * @hide
      */
-    public static final int SAP = 10;
+    int SAP = 10;
 
     /**
      * A2DP Sink Profile
      *
      * @hide
      */
-    public static final int A2DP_SINK = 11;
+    int A2DP_SINK = 11;
 
     /**
      * AVRCP Controller Profile
      *
      * @hide
      */
-    public static final int AVRCP_CONTROLLER = 12;
+    int AVRCP_CONTROLLER = 12;
+
+    /**
+     * AVRCP Target Profile
+     *
+     * @hide
+     */
+    int AVRCP = 13;
 
     /**
      * Headset Client - HFP HF Role
      *
      * @hide
      */
-    public static final int HEADSET_CLIENT = 16;
+    int HEADSET_CLIENT = 16;
 
     /**
      * PBAP Client
      *
      * @hide
      */
-    public static final int PBAP_CLIENT = 17;
+    int PBAP_CLIENT = 17;
 
     /**
      * MAP Messaging Client Equipment (MCE)
      *
      * @hide
      */
-    public static final int MAP_CLIENT = 18;
+    int MAP_CLIENT = 18;
 
     /**
      * HID Device
      */
-    public static final int HID_DEVICE = 19;
+    int HID_DEVICE = 19;
 
     /**
      * Object Push Profile (OPP)
      *
      * @hide
      */
-    public static final int OPP = 20;
+    int OPP = 20;
+
+    /**
+     * Hearing Aid Device
+     *
+     * @hide
+     */
+    int HEARING_AID = 21;
 
     /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
@@ -170,7 +184,7 @@
      *
      * @hide
      */
-    public static final int MAX_PROFILE_ID = 20;
+    int MAX_PROFILE_ID = 21;
 
     /**
      * Default priority for devices that we try to auto-connect to and
@@ -178,7 +192,7 @@
      *
      * @hide
      **/
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
+    int PRIORITY_AUTO_CONNECT = 1000;
 
     /**
      * Default priority for devices that allow incoming
@@ -187,7 +201,7 @@
      * @hide
      **/
     @SystemApi
-    public static final int PRIORITY_ON = 100;
+    int PRIORITY_ON = 100;
 
     /**
      * Default priority for devices that does not allow incoming
@@ -196,14 +210,14 @@
      * @hide
      **/
     @SystemApi
-    public static final int PRIORITY_OFF = 0;
+    int PRIORITY_OFF = 0;
 
     /**
      * Default priority when not set or when the device is unpaired
      *
      * @hide
      */
-    public static final int PRIORITY_UNDEFINED = -1;
+    int PRIORITY_UNDEFINED = -1;
 
     /**
      * Get connected devices for this specific profile.
diff --git a/android/bluetooth/BluetoothSap.java b/android/bluetooth/BluetoothSap.java
index 4848162..c51e39a 100644
--- a/android/bluetooth/BluetoothSap.java
+++ b/android/bluetooth/BluetoothSap.java
@@ -147,7 +147,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                mContext.getUser())) {
             Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
             return false;
         }
diff --git a/android/bluetooth/BluetoothSocket.java b/android/bluetooth/BluetoothSocket.java
index 09f9684..09a5b59 100644
--- a/android/bluetooth/BluetoothSocket.java
+++ b/android/bluetooth/BluetoothSocket.java
@@ -676,6 +676,35 @@
         mExcludeSdp = excludeSdp;
     }
 
+    /**
+     * Set the LE Transmit Data Length to be the maximum that the BT Controller is capable of. This
+     * parameter is used by the BT Controller to set the maximum transmission packet size on this
+     * connection. This function is currently used for testing only.
+     * @hide
+     */
+    public void requestMaximumTxDataLength() throws IOException {
+        if (mDevice == null) {
+            throw new IOException("requestMaximumTxDataLength is called on null device");
+        }
+
+        try {
+            if (mSocketState == SocketState.CLOSED) {
+                throw new IOException("socket closed");
+            }
+            IBluetooth bluetoothProxy =
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+            if (bluetoothProxy == null) {
+                throw new IOException("Bluetooth is off");
+            }
+
+            if (DBG) Log.d(TAG, "requestMaximumTxDataLength");
+            bluetoothProxy.getSocketManager().requestMaximumTxDataLength(mDevice);
+        } catch (RemoteException e) {
+            Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            throw new IOException("unable to send RPC: " + e.getMessage());
+        }
+    }
+
     private String convertAddr(final byte[] addr) {
         return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
                 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
diff --git a/android/bluetooth/BluetoothUuid.java b/android/bluetooth/BluetoothUuid.java
index 76cb3f5..605dbd2 100644
--- a/android/bluetooth/BluetoothUuid.java
+++ b/android/bluetooth/BluetoothUuid.java
@@ -79,6 +79,8 @@
             ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid SAP =
             ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+    public static final ParcelUuid HearingAid =
+            ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb");
 
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/android/bluetooth/MockBluetoothProxyHelper.java b/android/bluetooth/MockBluetoothProxyHelper.java
new file mode 100644
index 0000000..32896e1
--- /dev/null
+++ b/android/bluetooth/MockBluetoothProxyHelper.java
@@ -0,0 +1,66 @@
+package android.bluetooth;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.when;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Mock {@link BluetoothAdapter} in the same package to access package private methods */
+public class MockBluetoothProxyHelper {
+    @Mock IBluetooth mockBluetoothService;
+    @Mock IBluetoothSocketManager mockBluetoothSocketManager;
+    @Mock ParcelFileDescriptor mockParcelFileDescriptor;
+
+    private BluetoothAdapter mockBluetoothAdapter;
+
+    public MockBluetoothProxyHelper(BluetoothAdapter mockBluetoothAdapter) {
+        this.mockBluetoothAdapter = mockBluetoothAdapter;
+
+        MockitoAnnotations.initMocks(this);
+
+        /** Mocks out package protected method */
+        when(mockBluetoothAdapter.getBluetoothService(any())).thenReturn(mockBluetoothService);
+
+        /** IBluetooth package protected method */
+        try {
+            when(mockBluetoothService.getSocketManager()).thenReturn(mockBluetoothSocketManager);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+
+        /** IBluetooth package protected method */
+        try {
+            when(mockBluetoothSocketManager.connectSocket(anyObject(), anyInt(), anyObject(),
+                        anyInt(), anyInt())).thenReturn(mockParcelFileDescriptor);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    public void setBluetoothService(IBluetooth bluetoothProxyService) {
+        when(mockBluetoothAdapter.getBluetoothService(any())).thenReturn(bluetoothProxyService);
+    }
+
+    public void setBluetoothSocketManager(IBluetoothSocketManager bluetoothSocketManager) {
+        try {
+            when(mockBluetoothService.getSocketManager()).thenReturn(bluetoothSocketManager);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    public void setMockParcelFileDescriptor(ParcelFileDescriptor parcelFileDescriptor) {
+        try {
+            when(mockBluetoothSocketManager.connectSocket(anyObject(), anyInt(), anyObject(),
+                        anyInt(), anyInt())).thenReturn(parcelFileDescriptor);
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+    }
+}
diff --git a/android/companion/CompanionDeviceManager.java b/android/companion/CompanionDeviceManager.java
index 1a5de56..cb35357 100644
--- a/android/companion/CompanionDeviceManager.java
+++ b/android/companion/CompanionDeviceManager.java
@@ -84,7 +84,7 @@
         public abstract void onDeviceFound(IntentSender chooserLauncher);
 
         /**
-         * Called if there was an error looking for device(s), e.g. timeout
+         * Called if there was an error looking for device(s)
          *
          * @param error the cause of the error
          */
diff --git a/android/content/AbstractThreadedSyncAdapter.java b/android/content/AbstractThreadedSyncAdapter.java
index 2629929..b528e39 100644
--- a/android/content/AbstractThreadedSyncAdapter.java
+++ b/android/content/AbstractThreadedSyncAdapter.java
@@ -16,11 +16,17 @@
 
 package android.content;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.accounts.Account;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
 
@@ -166,6 +172,13 @@
 
     private class ISyncAdapterImpl extends ISyncAdapter.Stub {
         @Override
+        public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) {
+            Handler.getMain().sendMessage(obtainMessage(
+                    AbstractThreadedSyncAdapter::handleOnUnsyncableAccount,
+                    AbstractThreadedSyncAdapter.this, cb));
+        }
+
+        @Override
         public void startSync(ISyncContext syncContext, String authority, Account account,
                 Bundle extras) {
             if (ENABLE_LOG) {
@@ -374,6 +387,54 @@
     }
 
     /**
+     * Handle a call of onUnsyncableAccount.
+     *
+     * @param cb The callback to report the return value to
+     */
+    private void handleOnUnsyncableAccount(@NonNull ISyncAdapterUnsyncableAccountCallback cb) {
+        boolean doSync;
+        try {
+            doSync = onUnsyncableAccount();
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Exception while calling onUnsyncableAccount, assuming 'true'", e);
+            doSync = true;
+        }
+
+        try {
+            cb.onUnsyncableAccountDone(doSync);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not report result of onUnsyncableAccount", e);
+        }
+    }
+
+    /**
+     * Allows to defer syncing until all accounts are properly set up.
+     *
+     * <p>Called when a account / authority pair
+     * <ul>
+     * <li>that can be handled by this adapter</li>
+     * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li>
+     * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has
+     * unknown state (<0)}.</li>
+     * </ul>
+     *
+     * <p>This might be called on a different service connection as {@link #onPerformSync}.
+     *
+     * <p>The system expects this method to immediately return. If the call stalls the system
+     * behaves as if this method returned {@code true}. If it is required to perform a longer task
+     * (such as interacting with the user), return {@code false} and proceed in a difference
+     * context, such as an {@link android.app.Activity}, or foreground service. The sync can then be
+     * rescheduled once the account becomes syncable.
+     *
+     * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by
+     *         default syncing starts immediately.
+     */
+    @MainThread
+    public boolean onUnsyncableAccount() {
+        return true;
+    }
+
+    /**
      * Perform a sync for this account. SyncAdapter-specific parameters may
      * be specified in extras, which is guaranteed to not be null. Invocations
      * of this method are guaranteed to be serialized.
diff --git a/android/content/ClipboardManager.java b/android/content/ClipboardManager.java
index 718e465..73b6eb2 100644
--- a/android/content/ClipboardManager.java
+++ b/android/content/ClipboardManager.java
@@ -16,13 +16,16 @@
 
 package android.content;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.os.Handler;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 
 /**
@@ -45,6 +48,7 @@
 @SystemService(Context.CLIPBOARD_SERVICE)
 public class ClipboardManager extends android.text.ClipboardManager {
     private final Context mContext;
+    private final Handler mHandler;
     private final IClipboard mService;
 
     private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners
@@ -52,20 +56,11 @@
 
     private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener
             = new IOnPrimaryClipChangedListener.Stub() {
-        public void dispatchPrimaryClipChanged() {
-            mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED);
-        }
-    };
-
-    static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1;
-
-    private final Handler mHandler = new Handler() {
         @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_REPORT_PRIMARY_CLIP_CHANGED:
-                    reportPrimaryClipChanged();
-            }
+        public void dispatchPrimaryClipChanged() {
+            mHandler.post(() -> {
+                reportPrimaryClipChanged();
+            });
         }
     };
 
@@ -89,6 +84,7 @@
     /** {@hide} */
     public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
         mContext = context;
+        mHandler = handler;
         mService = IClipboard.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
     }
@@ -98,12 +94,13 @@
      * is involved in normal cut and paste operations.
      *
      * @param clip The clipped data item to set.
+     * @see #getPrimaryClip()
+     * @see #clearPrimaryClip()
      */
-    public void setPrimaryClip(ClipData clip) {
+    public void setPrimaryClip(@NonNull ClipData clip) {
         try {
-            if (clip != null) {
-                clip.prepareToLeaveProcess(true);
-            }
+            Preconditions.checkNotNull(clip);
+            clip.prepareToLeaveProcess(true);
             mService.setPrimaryClip(clip, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -111,9 +108,24 @@
     }
 
     /**
-     * Returns the current primary clip on the clipboard.
+     * Clears any current primary clip on the clipboard.
+     *
+     * @see #setPrimaryClip(ClipData)
      */
-    public ClipData getPrimaryClip() {
+    public void clearPrimaryClip() {
+        try {
+            mService.clearPrimaryClip(mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current primary clip on the clipboard.
+     *
+     * @see #setPrimaryClip(ClipData)
+     */
+    public @Nullable ClipData getPrimaryClip() {
         try {
             return mService.getPrimaryClip(mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -124,8 +136,10 @@
     /**
      * Returns a description of the current primary clip on the clipboard
      * but not a copy of its data.
+     *
+     * @see #setPrimaryClip(ClipData)
      */
-    public ClipDescription getPrimaryClipDescription() {
+    public @Nullable ClipDescription getPrimaryClipDescription() {
         try {
             return mService.getPrimaryClipDescription(mContext.getOpPackageName());
         } catch (RemoteException e) {
diff --git a/android/content/ContentResolver.java b/android/content/ContentResolver.java
index 8d2e141..9f3df37 100644
--- a/android/content/ContentResolver.java
+++ b/android/content/ContentResolver.java
@@ -166,6 +166,15 @@
     public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
 
     /**
+     * {@hide} Integer extra containing a SyncExemption flag.
+     *
+     * Only the system and the shell user can set it.
+     *
+     * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
+     */
+    public static final String SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG = "v_exemption";
+
+    /**
      * Set by the SyncManager to request that the SyncAdapter initialize itself for
      * the given account/authority pair. One required initialization step is to
      * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
@@ -505,6 +514,38 @@
      */
     public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
 
+    /**
+     * No exception, throttled by app standby normally.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_NONE = 0;
+
+    /**
+     * When executing a sync with this exemption, we'll put the target app in the ACTIVE bucket
+     * for 10 minutes. This will allow the sync adapter to schedule/run further syncs and jobs.
+     *
+     * Note this will still *not* let RARE apps to run syncs, because they still won't get network
+     * connection.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_ACTIVE = 1;
+
+    /**
+     * In addition to {@link #SYNC_EXEMPTION_ACTIVE}, we put the sync adapter app in the
+     * temp whitelist for 10 minutes, so that even RARE apps can run syncs right away.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_ACTIVE_WITH_TEMP = 2;
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "SYNC_EXEMPTION_" }, value = {
+            SYNC_EXEMPTION_NONE,
+            SYNC_EXEMPTION_ACTIVE,
+            SYNC_EXEMPTION_ACTIVE_WITH_TEMP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SyncExemption {}
+
     // Always log queries which take 500ms+; shorter queries are
     // sampled accordingly.
     private static final boolean ENABLE_CONTENT_SAMPLE = false;
@@ -2082,7 +2123,23 @@
         Preconditions.checkNotNull(uri, "uri");
         try {
             ActivityManager.getService().takePersistableUriPermission(
-                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+                    resolveUserId(uri));
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void takePersistableUriPermission(@NonNull String toPackage, @NonNull Uri uri,
+            @Intent.AccessUriMode int modeFlags) {
+        Preconditions.checkNotNull(toPackage, "toPackage");
+        Preconditions.checkNotNull(uri, "uri");
+        try {
+            ActivityManager.getService().takePersistableUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
+                    resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -2100,7 +2157,8 @@
         Preconditions.checkNotNull(uri, "uri");
         try {
             ActivityManager.getService().releasePersistableUriPermission(
-                    ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
+                    ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
+                    resolveUserId(uri));
         } catch (RemoteException e) {
         }
     }
@@ -2427,21 +2485,16 @@
      * @param account the account to specify in the sync
      * @param authority the provider to specify in the sync request
      * @param extras extra parameters to go along with the sync request
-     * @param pollFrequency how frequently the sync should be performed, in seconds. A minimum value
-     *                      of 1 hour is enforced.
+     * @param pollFrequency how frequently the sync should be performed, in seconds.
+     * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+     * On previous versions, the minimum interval is 1 hour.
      * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
      * are null.
      */
     public static void addPeriodicSync(Account account, String authority, Bundle extras,
             long pollFrequency) {
         validateSyncExtrasBundle(extras);
-        if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
-                || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
-                || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
-                || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
-                || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
-                || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
-                || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
+        if (invalidPeriodicExtras(extras)) {
             throw new IllegalArgumentException("illegal extras were set");
         }
         try {
@@ -2999,6 +3052,11 @@
     }
 
     /** @hide */
+    public int getUserId() {
+        return mContext.getUserId();
+    }
+
+    /** @hide */
     public Drawable getTypeDrawable(String mimeType) {
         return MimeIconUtils.loadMimeIcon(mContext, mimeType);
     }
diff --git a/android/content/Context.java b/android/content/Context.java
index 1b05033..920056a 100644
--- a/android/content/Context.java
+++ b/android/content/Context.java
@@ -32,6 +32,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.VrManager;
@@ -48,6 +49,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -558,6 +560,7 @@
      *
      * @param resId Resource id for the CharSequence text
      */
+    @NonNull
     public final CharSequence getText(@StringRes int resId) {
         return getResources().getText(resId);
     }
@@ -614,8 +617,7 @@
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
-     * @return An object that can be used to draw this resource, or
-     *         {@code null} if the resource could not be resolved.
+     * @return An object that can be used to draw this resource.
      * @throws android.content.res.Resources.NotFoundException if the given ID
      *         does not exist.
      */
@@ -631,12 +633,11 @@
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
-     * @return A color state list, or {@code null} if the resource could not be
-     *         resolved.
+     * @return A color state list.
      * @throws android.content.res.Resources.NotFoundException if the given ID
      *         does not exist.
      */
-    @Nullable
+    @NonNull
     public final ColorStateList getColorStateList(@ColorRes int id) {
         return getResources().getColorStateList(id, getTheme());
     }
@@ -1834,13 +1835,17 @@
      * See {@link android.content.Context#startActivity(Intent, Bundle)}
      * Context.startActivity(Intent, Bundle)} for more details.
      *
+     * @return The corresponding flag {@link ActivityManager#START_CANCELED},
+     *         {@link ActivityManager#START_SUCCESS} etc. indicating whether the launch was
+     *         successful.
+     *
      * @throws ActivityNotFoundException &nbsp;
      *
      * @see #startActivities(Intent[])
      * @see PackageManager#resolveActivity
      */
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-    public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+    public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
@@ -3365,7 +3370,11 @@
      * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.app.SearchManager} for handling searches.
      *
-     * @see #getSystemService(String)
+     * <p>
+     * {@link Configuration#UI_MODE_TYPE_WATCH} does not support
+     * {@link android.app.SearchManager}.
+     *
+     * @see #getSystemService
      * @see android.app.SearchManager
      */
     public static final String SEARCH_SERVICE = "search";
@@ -3533,6 +3542,7 @@
      * @hide
      */
     @SystemApi
+    @Deprecated
     public static final String WIFI_RTT_SERVICE = "rttmanager";
 
     /**
@@ -3665,10 +3675,8 @@
      *
      * @see #getSystemService(String)
      * @see android.telephony.euicc.EuiccManager
-     * TODO(b/35851809): Unhide this API.
-     * @hide
      */
-    public static final String EUICC_SERVICE = "euicc_service";
+    public static final String EUICC_SERVICE = "euicc";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
@@ -3676,10 +3684,10 @@
      *
      * @see #getSystemService(String)
      * @see android.telephony.euicc.EuiccCardManager
-     * TODO(b/35851809): Make this a SystemApi.
      * @hide
      */
-    public static final String EUICC_CARD_SERVICE = "euicc_card_service";
+    @SystemApi
+    public static final String EUICC_CARD_SERVICE = "euicc_card";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
@@ -4165,6 +4173,16 @@
     public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.se.omapi.ISecureElementService}
+     * for accessing the SecureElementService.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
@@ -4663,9 +4681,15 @@
      *
      * @hide
      */
-    public abstract Context createPackageContextAsUser(
+    @SystemApi
+    public Context createPackageContextAsUser(
             String packageName, @CreatePackageOptions int flags, UserHandle user)
-            throws PackageManager.NameNotFoundException;
+            throws PackageManager.NameNotFoundException {
+        if (Build.IS_ENG) {
+            throw new IllegalStateException("createPackageContextAsUser not overridden!");
+        }
+        return this;
+    }
 
     /**
      * Creates a context given an {@link android.content.pm.ApplicationInfo}.
@@ -4690,13 +4714,22 @@
             throws PackageManager.NameNotFoundException;
 
     /**
-     * Get the userId associated with this context
-     * @return user id
-     *
+     * Get the user associated with this context
      * @hide
      */
     @TestApi
-    public abstract @UserIdInt int getUserId();
+    public UserHandle getUser() {
+        return android.os.Process.myUserHandle();
+    }
+
+    /**
+     * Get the user associated with this context
+     * @hide
+     */
+    @TestApi
+    public @UserIdInt int getUserId() {
+        return android.os.UserHandle.myUserId();
+    }
 
     /**
      * Return a new Context object for the current Context but whose resources
@@ -4890,7 +4923,22 @@
     /**
      * @hide
      */
-    public void setAutofillClient(AutofillClient client) {
+    public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) {
+    }
+
+    /**
+     * @hide
+     */
+    public boolean isAutofillCompatibilityEnabled() {
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public void setAutofillCompatibilityEnabled(
+            @SuppressWarnings("unused") boolean autofillCompatEnabled) {
     }
 
     /**
diff --git a/android/content/ContextWrapper.java b/android/content/ContextWrapper.java
index 67de4fe..1867a6d 100644
--- a/android/content/ContextWrapper.java
+++ b/android/content/ContextWrapper.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.content.pm.ApplicationInfo;
@@ -418,8 +419,8 @@
 
     /** @hide */
     @Override
-    public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
-        mBase.startActivitiesAsUser(intents, options, userHandle);
+    public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+        return mBase.startActivitiesAsUser(intents, options, userHandle);
     }
 
     @Override
@@ -994,4 +995,23 @@
     public void setAutofillClient(AutofillClient client) {
         mBase.setAutofillClient(client);
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isAutofillCompatibilityEnabled() {
+        return mBase != null && mBase.isAutofillCompatibilityEnabled();
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    @Override
+    public void setAutofillCompatibilityEnabled(boolean  autofillCompatEnabled) {
+        if (mBase != null) {
+            mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+        }
+    }
 }
diff --git a/android/content/Intent.java b/android/content/Intent.java
index acbdf14..000912c 100644
--- a/android/content/Intent.java
+++ b/android/content/Intent.java
@@ -50,6 +50,7 @@
 import android.provider.DocumentsProvider;
 import android.provider.MediaStore;
 import android.provider.OpenableColumns;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -265,8 +266,8 @@
  * </ul>
  *
  * <p>For example, consider the Note Pad sample application that
- * allows user to browse through a list of notes data and view details about
- * individual items.  Text in italics indicate places were you would replace a
+ * allows a user to browse through a list of notes data and view details about
+ * individual items.  Text in italics indicates places where you would replace a
  * name with one specific to your own package.</p>
  *
  * <pre> &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1553,16 +1554,6 @@
     public static final String ACTION_INSTALL_FAILURE = "android.intent.action.INSTALL_FAILURE";
 
     /**
-     * @hide
-     * @removed
-     * @deprecated Do not use. This will go away.
-     *     Replace with {@link #ACTION_INSTALL_INSTANT_APP_PACKAGE}.
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_INSTALL_EPHEMERAL_PACKAGE
-            = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
-    /**
      * Activity Action: Launch instant application installer.
      * <p class="note">
      * This is a protected intent that can only be sent by the system.
@@ -1576,16 +1567,6 @@
             = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
 
     /**
-     * @hide
-     * @removed
-     * @deprecated Do not use. This will go away.
-     *     Replace with {@link #ACTION_RESOLVE_INSTANT_APP_PACKAGE}.
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String ACTION_RESOLVE_EPHEMERAL_PACKAGE
-            = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
-    /**
      * Service Action: Resolve instant application.
      * <p>
      * The system will have a persistent connection to this service.
@@ -1600,16 +1581,6 @@
             = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
 
     /**
-     * @hide
-     * @removed
-     * @deprecated Do not use. This will go away.
-     *     Replace with {@link #ACTION_INSTANT_APP_RESOLVER_SETTINGS}.
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_EPHEMERAL_RESOLVER_SETTINGS
-            = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
-    /**
      * Activity Action: Launch instant app settings.
      *
      * <p class="note">
@@ -1843,6 +1814,17 @@
     public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
 
     /**
+     * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent with
+     * {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+     *
+     * @see #ACTION_MY_PACKAGE_SUSPENDED
+     * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
+
+    /**
      * Intent extra: An app split name.
      * <p>
      * Type: String
@@ -1870,6 +1852,17 @@
     public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
 
     /**
+     * Intent extra: A {@link Bundle} of extras supplied for the launcher when any packages on
+     * device are suspended. Will be sent with {@link #ACTION_PACKAGES_SUSPENDED}.
+     *
+     * @see PackageManager#isPackageSuspended()
+     * @see #ACTION_PACKAGES_SUSPENDED
+     *
+     * @hide
+     */
+    public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS";
+
+    /**
      * Activity action: Launch UI to manage which apps have a given permission.
      * <p>
      * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
@@ -2266,6 +2259,43 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+
+    /**
+     * Broadcast Action: Sent to a package that has been suspended by the system. This is sent
+     * whenever a package is put into a suspended state or any of its app extras change while in the
+     * suspended state.
+     * <p> Optionally includes the following extras:
+     * <ul>
+     *     <li> {@link #EXTRA_SUSPENDED_PACKAGE_EXTRAS} which is a {@link Bundle} which will contain
+     *     useful information for the app being suspended.
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
+
+    /**
+     * Broadcast Action: Sent to a package that has been unsuspended.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see #ACTION_MY_PACKAGE_SUSPENDED
+     * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
+
     /**
      * Broadcast Action: A user ID has been removed from the system.  The user
      * ID number is stored in the extra data under {@link #EXTRA_UID}.
@@ -2428,6 +2458,26 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+
+    /**
+     * Broadcast Action: The current device {@link android.content.res.Configuration} has changed
+     * such that the device may be eligible for the installation of additional configuration splits.
+     * Configuration properties that can trigger this broadcast include locale and display density.
+     *
+     * <p class="note">
+     * Unlike {@link #ACTION_CONFIGURATION_CHANGED}, you <em>can</em> receive this through
+     * components declared in manifests. However, the receiver <em>must</em> hold the
+     * {@link android.Manifest.permission#INSTALL_PACKAGES} permission.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SPLIT_CONFIGURATION_CHANGED =
+            "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
     /**
      * Broadcast Action: The current device's locale has changed.
      *
@@ -2457,6 +2507,23 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
+
+
+    /**
+     * Broadcast Action: Sent when the current battery level changes.
+     *
+     * It has {@link android.os.BatteryManager#EXTRA_EVENTS} that carries a list of {@link Bundle}
+     * instances representing individual battery level changes with associated
+     * extras from {@link #ACTION_BATTERY_CHANGED}.
+     *
+     * <p class="note">
+     * This broadcast requires {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_BATTERY_LEVEL_CHANGED =
+            "android.intent.action.BATTERY_LEVEL_CHANGED";
     /**
      * Broadcast Action:  Indicates low battery condition on the device.
      * This broadcast corresponds to the "Low battery warning" system dialog.
@@ -2507,6 +2574,9 @@
      * off, not sleeping).  Once the broadcast is complete, the final shutdown
      * will proceed and all unsaved data lost.  Apps will not normally need
      * to handle this, since the foreground activity will be paused as well.
+     * <p>As of {@link Build.VERSION_CODES#P} this broadcast is only sent to receivers registered
+     * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+     * Context.registerReceiver}.
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
@@ -4443,45 +4513,109 @@
 
     /**
      * A {@link IntentSender} to start after ephemeral installation success.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_SUCCESS).
+     * @removed
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
 
     /**
-     * A {@link IntentSender} to start after ephemeral installation failure.
+     * A {@link IntentSender} to start after instant app installation success.
      * @hide
      */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_SUCCESS =
+            "android.intent.extra.INSTANT_APP_SUCCESS";
+
+    /**
+     * A {@link IntentSender} to start after ephemeral installation failure.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_FAILURE).
+     * @removed
+     * @hide
+     */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
 
     /**
-     * The host name that triggered an ephemeral resolution.
+     * A {@link IntentSender} to start after instant app installation failure.
      * @hide
      */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_FAILURE =
+            "android.intent.extra.INSTANT_APP_FAILURE";
+
+    /**
+     * The host name that triggered an ephemeral resolution.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_HOSTNAME).
+     * @removed
+     * @hide
+     */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_HOSTNAME = "android.intent.extra.EPHEMERAL_HOSTNAME";
 
     /**
-     * An opaque token to track ephemeral resolution.
+     * The host name that triggered an instant app resolution.
      * @hide
      */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_HOSTNAME =
+            "android.intent.extra.INSTANT_APP_HOSTNAME";
+
+    /**
+     * An opaque token to track ephemeral resolution.
+     * @deprecated Use {@link #EXTRA_INSTANT_APP_TOKEN).
+     * @removed
+     * @hide
+     */
+    @Deprecated
     public static final String EXTRA_EPHEMERAL_TOKEN = "android.intent.extra.EPHEMERAL_TOKEN";
 
     /**
+     * An opaque token to track instant app resolution.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_TOKEN =
+            "android.intent.extra.INSTANT_APP_TOKEN";
+
+    /**
      * The action that triggered an instant application resolution.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
 
     /**
-     * A {@link Bundle} of metadata that describes the instanta application that needs to be
+     * An array of {@link Bundle}s containing details about resolved instant apps..
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_INSTANT_APP_BUNDLES =
+            "android.intent.extra.INSTANT_APP_BUNDLES";
+
+    /**
+     * A {@link Bundle} of metadata that describes the instant application that needs to be
      * installed. This data is populated from the response to
      * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
      * instant application resolver.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_INSTANT_APP_EXTRAS =
             "android.intent.extra.INSTANT_APP_EXTRAS";
 
     /**
+     * A boolean value indicating that the instant app resolver was unable to state with certainty
+     * that it did or did not have an app for the sanitized {@link Intent} defined at
+     * {@link #EXTRA_INTENT}.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_UNKNOWN_INSTANT_APP =
+            "android.intent.extra.UNKNOWN_INSTANT_APP";
+
+    /**
      * The version code of the app to install components from.
      * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
      * @hide
@@ -4493,12 +4627,14 @@
      * The version code of the app to install components from.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
 
     /**
-     * The app that triggered the ephemeral installation.
+     * The app that triggered the instant app installation.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_CALLING_PACKAGE
             = "android.intent.extra.CALLING_PACKAGE";
 
@@ -4507,6 +4643,7 @@
      * installer may use.
      * @hide
      */
+    @SystemApi
     public static final String EXTRA_VERIFICATION_BUNDLE
             = "android.intent.extra.VERIFICATION_BUNDLE";
 
@@ -5029,6 +5166,7 @@
             FLAG_GRANT_PREFIX_URI_PERMISSION,
             FLAG_DEBUG_TRIAGED_MISSING,
             FLAG_IGNORE_EPHEMERAL,
+            FLAG_ACTIVITY_MATCH_EXTERNAL,
             FLAG_ACTIVITY_NO_HISTORY,
             FLAG_ACTIVITY_SINGLE_TOP,
             FLAG_ACTIVITY_NEW_TASK,
@@ -5072,6 +5210,7 @@
             FLAG_INCLUDE_STOPPED_PACKAGES,
             FLAG_DEBUG_TRIAGED_MISSING,
             FLAG_IGNORE_EPHEMERAL,
+            FLAG_ACTIVITY_MATCH_EXTERNAL,
             FLAG_ACTIVITY_NO_HISTORY,
             FLAG_ACTIVITY_SINGLE_TOP,
             FLAG_ACTIVITY_NEW_TASK,
@@ -5475,6 +5614,14 @@
      */
     public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000;
 
+
+    /**
+     * If set, resolution of this intent may take place via an instant app not
+     * yet on the device if there does not yet exist an app on device to
+     * resolve it.
+     */
+    public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800;
+
     /**
      * If set, when sending a broadcast only registered receivers will be
      * called -- no BroadcastReceiver components will be launched.
@@ -7028,7 +7175,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if none was found.
      *
      * @deprecated
@@ -7046,7 +7193,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, boolean)
@@ -7063,7 +7210,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, byte)
@@ -7080,7 +7227,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, short)
@@ -7097,7 +7244,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, char)
@@ -7114,7 +7261,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, int)
@@ -7131,7 +7278,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, long)
@@ -7148,7 +7295,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra(),
+     * @return the value of an item previously added with putExtra(),
      * or the default value if no such item is present
      *
      * @see #putExtra(String, float)
@@ -7165,7 +7312,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, double)
@@ -7180,7 +7327,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no String value was found.
      *
      * @see #putExtra(String, String)
@@ -7194,7 +7341,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no CharSequence value was found.
      *
      * @see #putExtra(String, CharSequence)
@@ -7208,7 +7355,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Parcelable value was found.
      *
      * @see #putExtra(String, Parcelable)
@@ -7222,7 +7369,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Parcelable[] value was found.
      *
      * @see #putExtra(String, Parcelable[])
@@ -7236,8 +7383,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<Parcelable> value was found.
+     * @return the value of an item previously added with
+     * putParcelableArrayListExtra(), or null if no
+     * ArrayList<Parcelable> value was found.
      *
      * @see #putParcelableArrayListExtra(String, ArrayList)
      */
@@ -7250,7 +7398,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Serializable value was found.
      *
      * @see #putExtra(String, Serializable)
@@ -7264,8 +7412,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<Integer> value was found.
+     * @return the value of an item previously added with
+     * putIntegerArrayListExtra(), or null if no
+     * ArrayList<Integer> value was found.
      *
      * @see #putIntegerArrayListExtra(String, ArrayList)
      */
@@ -7278,8 +7427,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<String> value was found.
+     * @return the value of an item previously added with
+     * putStringArrayListExtra(), or null if no
+     * ArrayList<String> value was found.
      *
      * @see #putStringArrayListExtra(String, ArrayList)
      */
@@ -7292,8 +7442,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<CharSequence> value was found.
+     * @return the value of an item previously added with
+     * putCharSequenceArrayListExtra, or null if no
+     * ArrayList<CharSequence> value was found.
      *
      * @see #putCharSequenceArrayListExtra(String, ArrayList)
      */
@@ -7306,7 +7457,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no boolean array value was found.
      *
      * @see #putExtra(String, boolean[])
@@ -7320,7 +7471,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no byte array value was found.
      *
      * @see #putExtra(String, byte[])
@@ -7334,7 +7485,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no short array value was found.
      *
      * @see #putExtra(String, short[])
@@ -7348,7 +7499,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no char array value was found.
      *
      * @see #putExtra(String, char[])
@@ -7362,7 +7513,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no int array value was found.
      *
      * @see #putExtra(String, int[])
@@ -7376,7 +7527,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no long array value was found.
      *
      * @see #putExtra(String, long[])
@@ -7390,7 +7541,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no float array value was found.
      *
      * @see #putExtra(String, float[])
@@ -7404,7 +7555,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no double array value was found.
      *
      * @see #putExtra(String, double[])
@@ -7418,7 +7569,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no String array value was found.
      *
      * @see #putExtra(String, String[])
@@ -7432,7 +7583,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no CharSequence array value was found.
      *
      * @see #putExtra(String, CharSequence[])
@@ -7446,7 +7597,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Bundle value was found.
      *
      * @see #putExtra(String, Bundle)
@@ -7460,7 +7611,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no IBinder value was found.
      *
      * @see #putExtra(String, IBinder)
@@ -7480,7 +7631,7 @@
      * @param defaultValue The default value to return in case no item is
      * associated with the key 'name'
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or defaultValue if none was found.
      *
      * @see #putExtra
@@ -9451,7 +9602,7 @@
             proto.write(IntentProto.PACKAGE, mPackage);
         }
         if (comp && mComponent != null) {
-            proto.write(IntentProto.COMPONENT, mComponent.flattenToShortString());
+            mComponent.writeToProto(proto, IntentProto.COMPONENT);
         }
         if (mSourceBounds != null) {
             proto.write(IntentProto.SOURCE_BOUNDS, mSourceBounds.toShortString());
@@ -10024,6 +10175,24 @@
         }
     }
 
+    /** @hide */
+    public boolean hasWebURI() {
+        if (getData() == null) {
+            return false;
+        }
+        final String scheme = getScheme();
+        if (TextUtils.isEmpty(scheme)) {
+            return false;
+        }
+        return scheme.equals(IntentFilter.SCHEME_HTTP) || scheme.equals(IntentFilter.SCHEME_HTTPS);
+    }
+
+    /** @hide */
+    public boolean isWebIntent() {
+        return ACTION_VIEW.equals(mAction)
+                && hasWebURI();
+    }
+
     /**
      * @hide
      */
diff --git a/android/content/IntentFilter.java b/android/content/IntentFilter.java
index a957aed..cec3bad 100644
--- a/android/content/IntentFilter.java
+++ b/android/content/IntentFilter.java
@@ -1872,9 +1872,10 @@
                 du.println(sb.toString());
             }
         }
-        if (mPriority != 0 || mHasPartialTypes) {
+        if (mPriority != 0 || mOrder != 0 || mHasPartialTypes) {
             sb.setLength(0);
             sb.append(prefix); sb.append("mPriority="); sb.append(mPriority);
+                    sb.append(", mOrder="); sb.append(mOrder);
                     sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
             du.println(sb.toString());
         }
@@ -1951,6 +1952,7 @@
         dest.writeInt(mHasPartialTypes ? 1 : 0);
         dest.writeInt(getAutoVerify() ? 1 : 0);
         dest.writeInt(mInstantAppVisibility);
+        dest.writeInt(mOrder);
     }
 
     /**
@@ -2020,6 +2022,7 @@
         mHasPartialTypes = source.readInt() > 0;
         setAutoVerify(source.readInt() > 0);
         setVisibilityToInstantApp(source.readInt());
+        mOrder = source.readInt();
     }
 
     private final boolean findMimeType(String type) {
diff --git a/android/content/PermissionChecker.java b/android/content/PermissionChecker.java
new file mode 100644
index 0000000..9f5c877
--- /dev/null
+++ b/android/content/PermissionChecker.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class provides permission check APIs that verify both the
+ * permission and the associated app op for this permission if
+ * such is defined.
+ * <p>
+ * In the new permission model permissions with protection level
+ * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
+ * and above the user may not grant such permissions or revoke
+ * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
+ * these permissions are always granted as such apps do not expect
+ * permission revocations and would crash. Therefore, when the
+ * user disables a permission for a legacy app in the UI the
+ * platform disables the APIs guarded by this permission making
+ * them a no-op which is doing nothing or returning an empty
+ * result or default error.
+ * </p>
+ * <p>
+ * It is important that when you perform an operation on behalf of
+ * another app you use these APIs to check for permissions as the
+ * app may be a legacy app that does not participate in the new
+ * permission model for which the user had disabled the "permission"
+ * which is achieved by disallowing the corresponding app op.
+ * </p>
+ *
+ * @hide
+ */
+public final class PermissionChecker {
+    /** Permission result: The permission is granted. */
+    public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;
+
+    /** Permission result: The permission is denied. */
+    public static final int PERMISSION_DENIED =  PackageManager.PERMISSION_DENIED;
+
+    /** 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 */
+    @IntDef({PERMISSION_GRANTED,
+            PERMISSION_DENIED,
+            PERMISSION_DENIED_APP_OP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionResult {}
+
+    private PermissionChecker() {
+        /* do nothing */
+    }
+
+    /**
+     * Checks whether a given package in a UID and PID has a given permission
+     * and whether the app op that corresponds to this permission is allowed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @param pid The process id for which to check.
+     * @param uid The uid for which to check.
+     * @param packageName The package name for which to check. If null the
+     *     the first package for the calling UID will be used.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+     */
+    @PermissionResult
+    public static int checkPermission(@NonNull Context context, @NonNull String permission,
+            int pid, int uid, @Nullable String packageName) {
+        if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+            return PERMISSION_DENIED;
+        }
+
+        AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+        String op = appOpsManager.permissionToOp(permission);
+        if (op == null) {
+            return PERMISSION_GRANTED;
+        }
+
+        if (packageName == null) {
+            String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+            if (packageNames == null || packageNames.length <= 0) {
+                return PERMISSION_DENIED;
+            }
+            packageName = packageNames[0];
+        }
+
+        if (appOpsManager.noteProxyOpNoThrow(op, packageName)
+                != AppOpsManager.MODE_ALLOWED) {
+            return PERMISSION_DENIED_APP_OP;
+        }
+
+        return PERMISSION_GRANTED;
+    }
+
+    /**
+     * Checks whether your app has a given permission and whether the app op
+     * that corresponds to this permission is allowed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+     */
+    @PermissionResult
+    public static int checkSelfPermission(@NonNull Context context,
+            @NonNull String permission) {
+        return checkPermission(context, permission, Process.myPid(),
+                Process.myUid(), context.getPackageName());
+    }
+
+    /**
+     * Checks whether the IPC you are handling has a given permission and whether
+     * the app op that corresponds to this permission is allowed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @param packageName The package name making the IPC. If null the
+     *     the first package for the calling UID will be used.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+     */
+    @PermissionResult
+    public static int checkCallingPermission(@NonNull Context context,
+            @NonNull String permission, @Nullable String packageName) {
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return PERMISSION_DENIED;
+        }
+        return checkPermission(context, permission, Binder.getCallingPid(),
+                Binder.getCallingUid(), packageName);
+    }
+
+    /**
+     * Checks whether the IPC you are handling or your app has a given permission
+     * and whether the app op that corresponds to this permission is allowed.
+     *
+     * @param context Context for accessing resources.
+     * @param permission The permission to check.
+     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+     */
+    @PermissionResult
+    public static int checkCallingOrSelfPermission(@NonNull Context context,
+            @NonNull String permission) {
+        String packageName = (Binder.getCallingPid() == Process.myPid())
+                ? context.getPackageName() : null;
+        return checkPermission(context, permission, Binder.getCallingPid(),
+                Binder.getCallingUid(), packageName);
+    }
+}
diff --git a/android/content/SyncResult.java b/android/content/SyncResult.java
index 4f86af9..f67d7f5 100644
--- a/android/content/SyncResult.java
+++ b/android/content/SyncResult.java
@@ -79,7 +79,17 @@
 
     /**
      * Used to indicate to the SyncManager that future sync requests that match the request's
-     * Account and authority should be delayed at least this many seconds.
+     * Account and authority should be delayed until a time in seconds since Java epoch.
+     *
+     * <p>For example, if you want to delay the next sync for at least 5 minutes, then:
+     * <pre>
+     * result.delayUntil = (System.currentTimeMillis() / 1000) + 5 * 60;
+     * </pre>
+     *
+     * <p>By default, when a sync fails, the system retries later with an exponential back-off
+     * with the system default initial delay time, which always wins over {@link #delayUntil} --
+     * i.e. if the system back-off time is larger than {@link #delayUntil}, {@link #delayUntil}
+     * will essentially be ignored.
      */
     public long delayUntil;
 
diff --git a/android/content/om/OverlayInfo.java b/android/content/om/OverlayInfo.java
index 1a207ba..6e63342 100644
--- a/android/content/om/OverlayInfo.java
+++ b/android/content/om/OverlayInfo.java
@@ -16,10 +16,15 @@
 
 package android.content.om;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Immutable overlay information about a package. All PackageInfos that
  * represent an overlay package will have a corresponding OverlayInfo.
@@ -27,6 +32,19 @@
  * @hide
  */
 public final class OverlayInfo implements Parcelable {
+
+    @IntDef(prefix = "STATE_", value = {
+            STATE_UNKNOWN,
+            STATE_MISSING_TARGET,
+            STATE_NO_IDMAP,
+            STATE_DISABLED,
+            STATE_ENABLED,
+            STATE_TARGET_UPGRADING,
+            STATE_OVERLAY_UPGRADING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
     /**
      * An internal state used as the initial state of an overlay. OverlayInfo
      * objects exposed outside the {@link
@@ -49,18 +67,35 @@
     /**
      * The overlay is currently disabled. It can be enabled.
      *
-     * @see IOverlayManager.setEnabled
+     * @see IOverlayManager#setEnabled
      */
     public static final int STATE_DISABLED = 2;
 
     /**
      * The overlay is currently enabled. It can be disabled.
      *
-     * @see IOverlayManager.setEnabled
+     * @see IOverlayManager#setEnabled
      */
     public static final int STATE_ENABLED = 3;
 
     /**
+     * The target package is currently being upgraded; the state will change
+     * once the package installation has finished.
+     */
+    public static final int STATE_TARGET_UPGRADING = 4;
+
+    /**
+     * The overlay package is currently being upgraded; the state will change
+     * once the package installation has finished.
+     */
+    public static final int STATE_OVERLAY_UPGRADING = 5;
+
+    /**
+     * Category for theme overlays.
+     */
+    public static final String CATEGORY_THEME = "android.theme";
+
+    /**
      * Package name of the overlay package
      */
     public final String packageName;
@@ -71,19 +106,19 @@
     public final String targetPackageName;
 
     /**
+     * Category of the overlay package
+     */
+    public final String category;
+
+    /**
      * Full path to the base APK for this overlay package
      */
     public final String baseCodePath;
 
     /**
      * The state of this OverlayInfo as defined by the STATE_* constants in this class.
-     *
-     * @see #STATE_MISSING_TARGET
-     * @see #STATE_NO_IDMAP
-     * @see #STATE_DISABLED
-     * @see #STATE_ENABLED
      */
-    public final int state;
+    public final @State int state;
 
     /**
      * User handle for which this overlay applies
@@ -96,15 +131,16 @@
      * @param source the source OverlayInfo to base the new instance on
      * @param state the new state for the source OverlayInfo
      */
-    public OverlayInfo(@NonNull OverlayInfo source, int state) {
-        this(source.packageName, source.targetPackageName, source.baseCodePath, state,
-                source.userId);
+    public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
+        this(source.packageName, source.targetPackageName, source.category, source.baseCodePath,
+                state, source.userId);
     }
 
     public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
-            @NonNull String baseCodePath, int state, int userId) {
+            @Nullable String category, @NonNull String baseCodePath, int state, int userId) {
         this.packageName = packageName;
         this.targetPackageName = targetPackageName;
+        this.category = category;
         this.baseCodePath = baseCodePath;
         this.state = state;
         this.userId = userId;
@@ -114,6 +150,7 @@
     public OverlayInfo(Parcel source) {
         packageName = source.readString();
         targetPackageName = source.readString();
+        category = source.readString();
         baseCodePath = source.readString();
         state = source.readInt();
         userId = source.readInt();
@@ -136,6 +173,8 @@
             case STATE_NO_IDMAP:
             case STATE_DISABLED:
             case STATE_ENABLED:
+            case STATE_TARGET_UPGRADING:
+            case STATE_OVERLAY_UPGRADING:
                 break;
             default:
                 throw new IllegalArgumentException("State " + state + " is not a valid state");
@@ -151,12 +190,14 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(packageName);
         dest.writeString(targetPackageName);
+        dest.writeString(category);
         dest.writeString(baseCodePath);
         dest.writeInt(state);
         dest.writeInt(userId);
     }
 
-    public static final Parcelable.Creator<OverlayInfo> CREATOR = new Parcelable.Creator<OverlayInfo>() {
+    public static final Parcelable.Creator<OverlayInfo> CREATOR =
+            new Parcelable.Creator<OverlayInfo>() {
         @Override
         public OverlayInfo createFromParcel(Parcel source) {
             return new OverlayInfo(source);
@@ -189,14 +230,9 @@
      * Translate a state to a human readable string. Only intended for
      * debugging purposes.
      *
-     * @see #STATE_MISSING_TARGET
-     * @see #STATE_NO_IDMAP
-     * @see #STATE_DISABLED
-     * @see #STATE_ENABLED
-     *
      * @return a human readable String representing the state.
      */
-    public static String stateToString(int state) {
+    public static String stateToString(@State int state) {
         switch (state) {
             case STATE_UNKNOWN:
                 return "STATE_UNKNOWN";
@@ -208,6 +244,10 @@
                 return "STATE_DISABLED";
             case STATE_ENABLED:
                 return "STATE_ENABLED";
+            case STATE_TARGET_UPGRADING:
+                return "STATE_TARGET_UPGRADING";
+            case STATE_OVERLAY_UPGRADING:
+                return "STATE_OVERLAY_UPGRADING";
             default:
                 return "<unknown state>";
         }
@@ -249,6 +289,9 @@
         if (!targetPackageName.equals(other.targetPackageName)) {
             return false;
         }
+        if (!category.equals(other.category)) {
+            return false;
+        }
         if (!baseCodePath.equals(other.baseCodePath)) {
             return false;
         }
diff --git a/android/content/pm/AndroidTestBaseUpdater.java b/android/content/pm/AndroidTestBaseUpdater.java
new file mode 100644
index 0000000..2aaac02
--- /dev/null
+++ b/android/content/pm/AndroidTestBaseUpdater.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets < P that the android.test.base library is
+ * included by default.
+ *
+ * <p>This is separated out so that it can be conditionally included at build time depending on
+ * whether android.test.base is on the bootclasspath or not. In order to include this at
+ * build time, and remove android.test.base from the bootclasspath pass
+ * REMOVE_ATB_FROM_BCP=true on the build command line, otherwise this class will not be included
+ * and the
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
+
+    @Override
+    public void updatePackage(Package pkg) {
+        // Packages targeted at <= O_MR1 expect the classes in the android.test.base library
+        // to be accessible so this maintains backward compatibility by adding the
+        // android.test.base library to those packages.
+        if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+            prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
+        } else {
+            // If a package already depends on android.test.runner then add a dependency on
+            // android.test.base because android.test.runner depends on classes from the
+            // android.test.base library.
+            prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
+        }
+    }
+}
diff --git a/android/content/pm/ApplicationInfo.java b/android/content/pm/ApplicationInfo.java
index 746a090..e85058d 100644
--- a/android/content/pm/ApplicationInfo.java
+++ b/android/content/pm/ApplicationInfo.java
@@ -37,6 +37,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -602,6 +603,13 @@
      */
     public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
 
+    /**
+     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * product partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -619,6 +627,7 @@
             PRIVATE_FLAG_OEM,
             PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
             PRIVATE_FLAG_PRIVILEGED,
+            PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
             PRIVATE_FLAG_VENDOR,
@@ -754,15 +763,13 @@
     public String[] resourceDirs;
 
     /**
-     * String retrieved from the seinfo tag found in selinux policy. This value
-     * can be overridden with a value set through the mac_permissions.xml policy
-     * construct. This value is useful in setting an SELinux security context on
-     * the process as well as its data directory. The String default is being used
-     * here to represent a catchall label when no policy matches.
+     * String retrieved from the seinfo tag found in selinux policy. This value can be set through
+     * the mac_permissions.xml policy construct. This value is used for setting an SELinux security
+     * context on the process as well as its data directory.
      *
      * {@hide}
      */
-    public String seInfo = "default";
+    public String seInfo;
 
     /**
      * The seinfo tag generated per-user. This value may change based upon the
@@ -950,6 +957,7 @@
      * Version of the sandbox the application wants to run in.
      * @hide
      */
+    @SystemApi
     public int targetSandboxVersion;
 
     /**
@@ -1093,6 +1101,58 @@
     /** @hide */
     public String[] splitClassLoaderNames;
 
+    /**
+     * Represents the default policy. The actual policy used will depend on other properties of
+     * the application, e.g. the target SDK version.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1;
+    /**
+     * No API enforcement; the app can access the entire internal private API. Only for use by
+     * system apps.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
+    /**
+     * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
+     * black lists.
+     * @hide
+     * */
+    public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+    /**
+     * Dark grey list enforcement. Enforces the dark grey and black lists
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK = 2;
+    /**
+     * Blacklist enforcement only.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_BLACK = 3;
+
+    private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_BLACK;
+
+    /**
+     * Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
+     * art/runtime/hidden_api.h
+     * @hide
+     */
+    @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
+            HIDDEN_API_ENFORCEMENT_DEFAULT,
+            HIDDEN_API_ENFORCEMENT_NONE,
+            HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+            HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
+            HIDDEN_API_ENFORCEMENT_BLACK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HiddenApiEnforcementPolicy {}
+
+    private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+        return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+    }
+
+    private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -1180,6 +1240,7 @@
             if (category != CATEGORY_UNDEFINED) {
                 pw.println(prefix + "category=" + category);
             }
+            pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
         }
         super.dumpBack(pw, prefix);
     }
@@ -1375,6 +1436,9 @@
         classLoaderName = orig.classLoaderName;
         splitClassLoaderNames = orig.splitClassLoaderNames;
         appComponentFactory = orig.appComponentFactory;
+        compileSdkVersion = orig.compileSdkVersion;
+        compileSdkVersionCodename = orig.compileSdkVersionCodename;
+        mHiddenApiPolicy = orig.mHiddenApiPolicy;
     }
 
     public String toString() {
@@ -1448,6 +1512,7 @@
         dest.writeInt(compileSdkVersion);
         dest.writeString(compileSdkVersionCodename);
         dest.writeString(appComponentFactory);
+        dest.writeInt(mHiddenApiPolicy);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1518,6 +1583,7 @@
         compileSdkVersion = source.readInt();
         compileSdkVersionCodename = source.readString();
         appComponentFactory = source.readString();
+        mHiddenApiPolicy = source.readInt();
     }
 
     /**
@@ -1588,11 +1654,31 @@
         }
     }
 
+    private boolean isPackageWhitelistedForHiddenApis() {
+        return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+    }
+
     /**
      * @hide
      */
-    public boolean isAllowedToUseHiddenApi() {
-        return isSystemApp();
+    public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+        if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+            return mHiddenApiPolicy;
+        }
+        if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
+            return HIDDEN_API_ENFORCEMENT_NONE;
+        }
+        return HIDDEN_API_ENFORCEMENT_BLACK;
+    }
+
+    /**
+     * @hide
+     */
+    public void setHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+        if (!isValidHiddenApiEnforcementPolicy(policy)) {
+            throw new IllegalArgumentException("Invalid API enforcement policy: " + policy);
+        }
+        mHiddenApiPolicy = policy;
     }
 
     /**
@@ -1647,7 +1733,11 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
 
-    /** @hide */
+    /**
+     * True if the application is installed as an instant app.
+     * @hide
+     */
+    @SystemApi
     public boolean isInstantApp() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
     }
@@ -1699,6 +1789,11 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
+    /** @hide */
+    public boolean isProduct() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    }
+
     /**
      * Returns whether or not this application was installed as a virtual preload.
      */
diff --git a/android/content/pm/AuxiliaryResolveInfo.java b/android/content/pm/AuxiliaryResolveInfo.java
index 6bdcefb..202df50 100644
--- a/android/content/pm/AuxiliaryResolveInfo.java
+++ b/android/content/pm/AuxiliaryResolveInfo.java
@@ -21,6 +21,10 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Auxiliary application resolution response.
@@ -31,56 +35,95 @@
  * hasn't been installed.
  * @hide
  */
-public final class AuxiliaryResolveInfo extends IntentFilter {
-    /** Resolved information returned from the external instant resolver */
-    public final InstantAppResolveInfo resolveInfo;
-    /** The resolved package. Copied from {@link #resolveInfo}. */
-    public final String packageName;
+public final class AuxiliaryResolveInfo {
     /** The activity to launch if there's an installation failure. */
     public final ComponentName installFailureActivity;
-    /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
-    public final String splitName;
     /** Whether or not instant resolution needs the second phase */
     public final boolean needsPhaseTwo;
     /** Opaque token to track the instant application resolution */
     public final String token;
-    /** The version code of the package */
-    public final long versionCode;
     /** An intent to start upon failure to install */
     public final Intent failureIntent;
+    /** The matching filters for this resolve info. */
+    public final List<AuxiliaryFilter> filters;
 
     /** Create a response for installing an instant application. */
-    public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo,
-            @NonNull IntentFilter orig,
-            @Nullable String splitName,
-            @NonNull String token,
+    public AuxiliaryResolveInfo(@NonNull String token,
             boolean needsPhase2,
-            @Nullable Intent failureIntent) {
-        super(orig);
-        this.resolveInfo = resolveInfo;
-        this.packageName = resolveInfo.getPackageName();
-        this.splitName = splitName;
+            @Nullable Intent failureIntent,
+            @Nullable List<AuxiliaryFilter> filters) {
         this.token = token;
         this.needsPhaseTwo = needsPhase2;
-        this.versionCode = resolveInfo.getVersionCode();
         this.failureIntent = failureIntent;
+        this.filters = filters;
         this.installFailureActivity = null;
     }
 
     /** Create a response for installing a split on demand. */
-    public AuxiliaryResolveInfo(@NonNull String packageName,
-            @Nullable String splitName,
-            @Nullable ComponentName failureActivity,
-            long versionCode,
-            @Nullable Intent failureIntent) {
+    public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+            @Nullable Intent failureIntent,
+            @Nullable List<AuxiliaryFilter> filters) {
         super();
-        this.packageName = packageName;
         this.installFailureActivity = failureActivity;
-        this.splitName = splitName;
-        this.versionCode = versionCode;
-        this.resolveInfo = null;
+        this.filters = filters;
         this.token = null;
         this.needsPhaseTwo = false;
         this.failureIntent = failureIntent;
     }
+
+    /** Create a response for installing a split on demand. */
+    public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+            String packageName, long versionCode, String splitName) {
+        this(failureActivity, null, Collections.singletonList(
+                new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName)));
+    }
+
+    /** @hide */
+    public static final class AuxiliaryFilter extends IntentFilter {
+        /** Resolved information returned from the external instant resolver */
+        public final InstantAppResolveInfo resolveInfo;
+        /** The resolved package. Copied from {@link #resolveInfo}. */
+        public final String packageName;
+        /** The version code of the package */
+        public final long versionCode;
+        /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+        public final String splitName;
+        /** The extras to pass on to the installer for this filter. */
+        public final Bundle extras;
+
+        public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo,
+                String splitName, Bundle extras) {
+            super(orig);
+            this.resolveInfo = resolveInfo;
+            this.packageName = resolveInfo.getPackageName();
+            this.versionCode = resolveInfo.getLongVersionCode();
+            this.splitName = splitName;
+            this.extras = extras;
+        }
+
+        public AuxiliaryFilter(InstantAppResolveInfo resolveInfo,
+                String splitName, Bundle extras) {
+            this.resolveInfo = resolveInfo;
+            this.packageName = resolveInfo.getPackageName();
+            this.versionCode = resolveInfo.getLongVersionCode();
+            this.splitName = splitName;
+            this.extras = extras;
+        }
+
+        public AuxiliaryFilter(String packageName, long versionCode, String splitName) {
+            this.resolveInfo = null;
+            this.packageName = packageName;
+            this.versionCode = versionCode;
+            this.splitName = splitName;
+            this.extras = null;
+        }
+
+        @Override
+        public String toString() {
+            return "AuxiliaryFilter{"
+                    + "packageName='" + packageName + '\''
+                    + ", versionCode=" + versionCode
+                    + ", splitName='" + splitName + '\'' + '}';
+        }
+    }
 }
\ No newline at end of file
diff --git a/android/content/pm/EphemeralIntentFilter.java b/android/content/pm/EphemeralIntentFilter.java
deleted file mode 100644
index 1dbbf81..0000000
--- a/android/content/pm/EphemeralIntentFilter.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.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information about an ephemeral application intent filter.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralIntentFilter implements Parcelable {
-    private final InstantAppIntentFilter mInstantAppIntentFilter;
-
-    public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
-        mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters);
-    }
-
-    EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) {
-        mInstantAppIntentFilter = intentFilter;
-    }
-
-    EphemeralIntentFilter(Parcel in) {
-        mInstantAppIntentFilter = in.readParcelable(null /*loader*/);
-    }
-
-    public String getSplitName() {
-        return mInstantAppIntentFilter.getSplitName();
-    }
-
-    public List<IntentFilter> getFilters() {
-        return mInstantAppIntentFilter.getFilters();
-    }
-
-    /** @hide */
-    InstantAppIntentFilter getInstantAppIntentFilter() {
-        return mInstantAppIntentFilter;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mInstantAppIntentFilter, flags);
-    }
-
-    public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
-            = new Parcelable.Creator<EphemeralIntentFilter>() {
-        @Override
-        public EphemeralIntentFilter createFromParcel(Parcel in) {
-            return new EphemeralIntentFilter(in);
-        }
-        @Override
-        public EphemeralIntentFilter[] newArray(int size) {
-            return new EphemeralIntentFilter[size];
-        }
-    };
-}
diff --git a/android/content/pm/EphemeralResolveInfo.java b/android/content/pm/EphemeralResolveInfo.java
deleted file mode 100644
index 12131a3..0000000
--- a/android/content/pm/EphemeralResolveInfo.java
+++ /dev/null
@@ -1,224 +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.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Information about an ephemeral application.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralResolveInfo implements Parcelable {
-    /** Algorithm that will be used to generate the domain digest */
-    public static final String SHA_ALGORITHM = "SHA-256";
-
-    private final InstantAppResolveInfo mInstantAppResolveInfo;
-    @Deprecated
-    private final List<IntentFilter> mLegacyFilters;
-
-    @Deprecated
-    public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
-            @NonNull List<IntentFilter> filters) {
-        if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
-            throw new IllegalArgumentException();
-        }
-        final List<EphemeralIntentFilter> ephemeralFilters = new ArrayList<>(1);
-        ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters));
-        mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName,
-                createInstantAppIntentFilterList(ephemeralFilters));
-        mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
-        mLegacyFilters.addAll(filters);
-    }
-
-    @Deprecated
-    public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters) {
-        this(digest, packageName, filters, -1 /*versionCode*/);
-    }
-
-    public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters, int versionCode) {
-        mInstantAppResolveInfo = new InstantAppResolveInfo(
-                digest.getInstantAppDigest(), packageName,
-                createInstantAppIntentFilterList(filters), versionCode);
-        mLegacyFilters = null;
-    }
-
-    public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
-            @Nullable List<EphemeralIntentFilter> filters) {
-        this(new EphemeralDigest(hostName), packageName, filters);
-    }
-
-    EphemeralResolveInfo(Parcel in) {
-        mInstantAppResolveInfo = in.readParcelable(null /*loader*/);
-        mLegacyFilters = new ArrayList<IntentFilter>();
-        in.readList(mLegacyFilters, null /*loader*/);
-    }
-
-    /** @hide */
-    public InstantAppResolveInfo getInstantAppResolveInfo() {
-        return mInstantAppResolveInfo;
-    }
-
-    private static List<InstantAppIntentFilter> createInstantAppIntentFilterList(
-            List<EphemeralIntentFilter> filters) {
-        if (filters == null) {
-            return null;
-        }
-        final int filterCount = filters.size();
-        final List<InstantAppIntentFilter> returnList = new ArrayList<>(filterCount);
-        for (int i = 0; i < filterCount; i++) {
-            returnList.add(filters.get(i).getInstantAppIntentFilter());
-        }
-        return returnList;
-    }
-
-    public byte[] getDigestBytes() {
-        return mInstantAppResolveInfo.getDigestBytes();
-    }
-
-    public int getDigestPrefix() {
-        return mInstantAppResolveInfo.getDigestPrefix();
-    }
-
-    public String getPackageName() {
-        return mInstantAppResolveInfo.getPackageName();
-    }
-
-    public List<EphemeralIntentFilter> getIntentFilters() {
-        final List<InstantAppIntentFilter> filters = mInstantAppResolveInfo.getIntentFilters();
-        final int filterCount = filters.size();
-        final List<EphemeralIntentFilter> returnList = new ArrayList<>(filterCount);
-        for (int i = 0; i < filterCount; i++) {
-            returnList.add(new EphemeralIntentFilter(filters.get(i)));
-        }
-        return returnList;
-    }
-
-    public int getVersionCode() {
-        return mInstantAppResolveInfo.getVersionCode();
-    }
-
-    @Deprecated
-    public List<IntentFilter> getFilters() {
-        return mLegacyFilters;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeParcelable(mInstantAppResolveInfo, flags);
-        out.writeList(mLegacyFilters);
-    }
-
-    public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
-            = new Parcelable.Creator<EphemeralResolveInfo>() {
-        @Override
-        public EphemeralResolveInfo createFromParcel(Parcel in) {
-            return new EphemeralResolveInfo(in);
-        }
-        @Override
-        public EphemeralResolveInfo[] newArray(int size) {
-            return new EphemeralResolveInfo[size];
-        }
-    };
-
-    /**
-     * Helper class to generate and store each of the digests and prefixes
-     * sent to the Ephemeral Resolver.
-     * <p>
-     * Since intent filters may want to handle multiple hosts within a
-     * domain [eg “*.google.com”], the resolver is presented with multiple
-     * hash prefixes. For example, "a.b.c.d.e" generates digests for
-     * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class EphemeralDigest implements Parcelable {
-        private final InstantAppDigest mInstantAppDigest;
-
-        public EphemeralDigest(@NonNull String hostName) {
-            this(hostName, -1 /*maxDigests*/);
-        }
-
-        /** @hide */
-        public EphemeralDigest(@NonNull String hostName, int maxDigests) {
-            mInstantAppDigest = new InstantAppDigest(hostName, maxDigests);
-        }
-
-        EphemeralDigest(Parcel in) {
-            mInstantAppDigest = in.readParcelable(null /*loader*/);
-        }
-
-        /** @hide */
-        InstantAppDigest getInstantAppDigest() {
-            return mInstantAppDigest;
-        }
-
-        public byte[][] getDigestBytes() {
-            return mInstantAppDigest.getDigestBytes();
-        }
-
-        public int[] getDigestPrefix() {
-            return mInstantAppDigest.getDigestPrefix();
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeParcelable(mInstantAppDigest, flags);
-        }
-
-        @SuppressWarnings("hiding")
-        public static final Parcelable.Creator<EphemeralDigest> CREATOR =
-                new Parcelable.Creator<EphemeralDigest>() {
-            @Override
-            public EphemeralDigest createFromParcel(Parcel in) {
-                return new EphemeralDigest(in);
-            }
-            @Override
-            public EphemeralDigest[] newArray(int size) {
-                return new EphemeralDigest[size];
-            }
-        };
-    }
-}
diff --git a/android/content/pm/InstantAppRequest.java b/android/content/pm/InstantAppRequest.java
index 38f0225..361d4e4 100644
--- a/android/content/pm/InstantAppRequest.java
+++ b/android/content/pm/InstantAppRequest.java
@@ -18,12 +18,14 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.text.TextUtils;
 
 /**
  * Information needed to make an instant application resolution request.
  * @hide
  */
 public final class InstantAppRequest {
+
     /** Response from the first phase of instant application resolution */
     public final AuxiliaryResolveInfo responseObj;
     /** The original intent that triggered instant application resolution */
@@ -40,6 +42,8 @@
     public final Bundle verificationBundle;
     /** Whether resolution occurs because an application is starting */
     public final boolean resolveForStart;
+    /** The instant app digest for this request */
+    public final InstantAppResolveInfo.InstantAppDigest digest;
 
     public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
             String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
@@ -51,5 +55,11 @@
         this.userId = userId;
         this.verificationBundle = verificationBundle;
         this.resolveForStart = resolveForStart;
+        if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
+            digest = new InstantAppResolveInfo.InstantAppDigest(
+                    origIntent.getData().getHost(), 5 /*maxDigests*/);
+        } else {
+            digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
+        }
     }
 }
diff --git a/android/content/pm/InstantAppResolveInfo.java b/android/content/pm/InstantAppResolveInfo.java
index 19cb932..8184361 100644
--- a/android/content/pm/InstantAppResolveInfo.java
+++ b/android/content/pm/InstantAppResolveInfo.java
@@ -19,18 +19,46 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Random;
 
 /**
- * Information about an instant application.
+ * Describes an externally resolvable instant application. There are three states that this class
+ * can represent: <p/>
+ * <ul>
+ *     <li>
+ *         The first, usable only for non http/s intents, implies that the resolver cannot
+ *         immediately resolve this intent and would prefer that resolution be deferred to the
+ *         instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}.
+ *         If the {@link android.content.Intent} has the scheme set to http/s and a set of digest
+ *         prefixes were passed into one of the resolve methods in
+ *         {@link android.app.InstantAppResolverService}, this state cannot be used.
+ *     </li>
+ *     <li>
+ *         The second represents a partial match and is constructed with any of the other
+ *         constructors. By setting one or more of the {@link Nullable}arguments to null, you
+ *         communicate to the resolver in response to
+ *         {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[],
+ *                String, InstantAppResolverService.InstantAppResolutionCallback)}
+ *         that you need a 2nd round of resolution to complete the request.
+ *     </li>
+ *     <li>
+ *         The third represents a complete match and is constructed with all @Nullable parameters
+ *         populated.
+ *     </li>
+ * </ul>
  * @hide
  */
 @SystemApi
@@ -38,6 +66,8 @@
     /** Algorithm that will be used to generate the domain digest */
     private static final String SHA_ALGORITHM = "SHA-256";
 
+    private static final byte[] EMPTY_DIGEST = new byte[0];
+
     private final InstantAppDigest mDigest;
     private final String mPackageName;
     /** The filters used to match domain */
@@ -46,15 +76,43 @@
     private final long mVersionCode;
     /** Data about the app that should be passed along to the Instant App installer on resolve */
     private final Bundle mExtras;
+    /**
+     * A flag that indicates that the resolver is aware that an app may match, but would prefer
+     * that the installer get the sanitized intent to decide.
+     */
+    private final boolean mShouldLetInstallerDecide;
 
+    /** Constructor for intent-based InstantApp resolution results. */
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
         this(digest, packageName, filters, (long) versionCode, null /* extras */);
     }
 
+    /** Constructor for intent-based InstantApp resolution results with extras. */
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, long versionCode,
             @Nullable Bundle extras) {
+        this(digest, packageName, filters, versionCode, extras, false);
+    }
+
+    /** Constructor for intent-based InstantApp resolution results by hostname. */
+    public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters) {
+        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+                null /* extras */);
+    }
+
+    /**
+     * Constructor that indicates that resolution could be delegated to the installer when the
+     * sanitized intent contains enough information to resolve completely.
+     */
+    public InstantAppResolveInfo(@Nullable Bundle extras) {
+        this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
+    }
+
+    private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+            @Nullable Bundle extras, boolean shouldLetInstallerDecide) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -62,7 +120,7 @@
         }
         mDigest = digest;
         if (filters != null) {
-            mFilters = new ArrayList<InstantAppIntentFilter>(filters.size());
+            mFilters = new ArrayList<>(filters.size());
             mFilters.addAll(filters);
         } else {
             mFilters = null;
@@ -70,25 +128,37 @@
         mPackageName = packageName;
         mVersionCode = versionCode;
         mExtras = extras;
-    }
-
-    public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
-            @Nullable List<InstantAppIntentFilter> filters) {
-        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
-                null /* extras */);
+        mShouldLetInstallerDecide = shouldLetInstallerDecide;
     }
 
     InstantAppResolveInfo(Parcel in) {
-        mDigest = in.readParcelable(null /*loader*/);
-        mPackageName = in.readString();
-        mFilters = new ArrayList<InstantAppIntentFilter>();
-        in.readList(mFilters, null /*loader*/);
-        mVersionCode = in.readLong();
+        mShouldLetInstallerDecide = in.readBoolean();
         mExtras = in.readBundle();
+        if (mShouldLetInstallerDecide) {
+            mDigest = InstantAppDigest.UNDEFINED;
+            mPackageName = null;
+            mFilters = Collections.emptyList();
+            mVersionCode = -1;
+        } else {
+            mDigest = in.readParcelable(null /*loader*/);
+            mPackageName = in.readString();
+            mFilters = new ArrayList<>();
+            in.readList(mFilters, null /*loader*/);
+            mVersionCode = in.readLong();
+        }
+    }
+
+    /**
+     * Returns true if the resolver is aware that an app may match, but would prefer
+     * that the installer get the sanitized intent to decide. This should not be true for
+     * resolutions that include a host and will be ignored in such cases.
+     */
+    public boolean shouldLetInstallerDecide() {
+        return mShouldLetInstallerDecide;
     }
 
     public byte[] getDigestBytes() {
-        return mDigest.getDigestBytes()[0];
+        return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST;
     }
 
     public int getDigestPrefix() {
@@ -127,11 +197,15 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
+        out.writeBoolean(mShouldLetInstallerDecide);
+        out.writeBundle(mExtras);
+        if (mShouldLetInstallerDecide) {
+            return;
+        }
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
         out.writeLong(mVersionCode);
-        out.writeBundle(mExtras);
     }
 
     public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
@@ -158,12 +232,30 @@
      */
     @SystemApi
     public static final class InstantAppDigest implements Parcelable {
-        private static final int DIGEST_MASK = 0xfffff000;
-        private static final int DIGEST_PREFIX_COUNT = 5;
+        static final int DIGEST_MASK = 0xfffff000;
+
+        /**
+         * A special instance that represents and undefined digest used for cases that a host was
+         * not provided or is irrelevant to the response.
+         */
+        public static final InstantAppDigest UNDEFINED =
+                new InstantAppDigest(new byte[][]{}, new int[]{});
+
+        private static Random sRandom = null;
+        static {
+            try {
+                sRandom = SecureRandom.getInstance("SHA1PRNG");
+            } catch (NoSuchAlgorithmException e) {
+                // oh well
+                sRandom = new Random();
+            }
+        }
         /** Full digest of the domain hashes */
         private final byte[][] mDigestBytes;
-        /** The first 4 bytes of the domain hashes */
+        /** The first 5 bytes of the domain hashes */
         private final int[] mDigestPrefix;
+        /** The first 5 bytes of the domain hashes interspersed with random data */
+        private int[] mDigestPrefixSecure;
 
         public InstantAppDigest(@NonNull String hostName) {
             this(hostName, -1 /*maxDigests*/);
@@ -186,6 +278,11 @@
             }
         }
 
+        private InstantAppDigest(byte[][] digestBytes, int[] prefix) {
+            this.mDigestPrefix = prefix;
+            this.mDigestBytes = digestBytes;
+        }
+
         private static byte[][] generateDigest(String hostName, int maxDigests) {
             ArrayList<byte[]> digests = new ArrayList<>();
             try {
@@ -230,6 +327,7 @@
                 }
             }
             mDigestPrefix = in.createIntArray();
+            mDigestPrefixSecure = in.createIntArray();
         }
 
         public byte[][] getDigestBytes() {
@@ -240,6 +338,26 @@
             return mDigestPrefix;
         }
 
+        /**
+         * Returns a digest prefix with additional random prefixes interspersed.
+         * @hide
+         */
+        public int[] getDigestPrefixSecure() {
+            if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
+                return getDigestPrefix();
+            } else if (mDigestPrefixSecure == null) {
+                // let's generate some random data to intersperse throughout the set of prefixes
+                final int realSize = getDigestPrefix().length;
+                final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
+                mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
+                for (int i = realSize; i < manufacturedSize; i++) {
+                    mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
+                }
+                Arrays.sort(mDigestPrefixSecure);
+            }
+            return mDigestPrefixSecure;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -247,6 +365,11 @@
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
+            final boolean isUndefined = this == UNDEFINED;
+            out.writeBoolean(isUndefined);
+            if (isUndefined) {
+                return;
+            }
             if (mDigestBytes == null) {
                 out.writeInt(-1);
             } else {
@@ -256,6 +379,7 @@
                 }
             }
             out.writeIntArray(mDigestPrefix);
+            out.writeIntArray(mDigestPrefixSecure);
         }
 
         @SuppressWarnings("hiding")
@@ -263,6 +387,9 @@
                 new Parcelable.Creator<InstantAppDigest>() {
             @Override
             public InstantAppDigest createFromParcel(Parcel in) {
+                if (in.readBoolean() /* is undefined */) {
+                    return UNDEFINED;
+                }
                 return new InstantAppDigest(in);
             }
             @Override
diff --git a/android/content/pm/LauncherApps.java b/android/content/pm/LauncherApps.java
index b4a7eec..9aace2e 100644
--- a/android/content/pm/LauncherApps.java
+++ b/android/content/pm/LauncherApps.java
@@ -211,6 +211,10 @@
          * example, this can happen when a Device Administrator suspends
          * an applicaton.
          *
+         * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
+         * any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will
+         * not receive this callback.
+         *
          * @param packageNames The names of the packages that have just been
          *            suspended.
          * @param user The UserHandle of the profile that generated the change.
@@ -219,6 +223,22 @@
         }
 
         /**
+         * Indicates that one or more packages have been suspended. A device administrator or an app
+         * with {@code android.permission.SUSPEND_APPS} can do this.
+         *
+         * @param packageNames The names of the packages that have just been suspended.
+         * @param launcherExtras A {@link Bundle} of extras for the launcher.
+         * @param user the user for which the given packages were suspended.
+         *
+         * @see PackageManager#isPackageSuspended()
+         * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
+         */
+        public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras,
+                UserHandle user) {
+            onPackagesSuspended(packageNames, user);
+        }
+
+        /**
          * Indicates that one or more packages have been unsuspended. For
          * example, this can happen when a Device Administrator unsuspends
          * an applicaton.
@@ -638,6 +658,31 @@
     }
 
     /**
+     * Gets the launcher extras supplied to the system when the given package was suspended via
+     * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)}.
+     *
+     * <p>Note: This just returns whatever extras were provided to the system, <em>which might
+     * even be {@code null}.</em>
+     *
+     * @param packageName The package for which to fetch the launcher extras.
+     * @param user The {@link UserHandle} of the profile.
+     * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
+     *         suspended.
+     *
+     * @see Callback#onPackagesSuspended(String[], Bundle, UserHandle)
+     * @see PackageManager#isPackageSuspended()
+     */
+    public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            return mService.getSuspendedPackageLauncherExtras(packageName, user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
      * @param packageName The package name of the application
@@ -652,7 +697,7 @@
             @ApplicationInfoFlags int flags, @NonNull UserHandle user)
             throws PackageManager.NameNotFoundException {
         Preconditions.checkNotNull(packageName, "packageName");
-        Preconditions.checkNotNull(packageName, "user");
+        Preconditions.checkNotNull(user, "user");
         logErrorForInvalidProfileAccess(user);
         try {
             final ApplicationInfo ai = mService
@@ -1163,14 +1208,15 @@
         }
 
         @Override
-        public void onPackagesSuspended(UserHandle user, String[] packageNames)
+        public void onPackagesSuspended(UserHandle user, String[] packageNames,
+                Bundle launcherExtras)
                 throws RemoteException {
             if (DEBUG) {
                 Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
             }
             synchronized (LauncherApps.this) {
                 for (CallbackMessageHandler callback : mCallbacks) {
-                    callback.postOnPackagesSuspended(packageNames, user);
+                    callback.postOnPackagesSuspended(packageNames, launcherExtras, user);
                 }
             }
         }
@@ -1218,6 +1264,7 @@
         private static class CallbackInfo {
             String[] packageNames;
             String packageName;
+            Bundle launcherExtras;
             boolean replacing;
             UserHandle user;
             List<ShortcutInfo> shortcuts;
@@ -1251,7 +1298,8 @@
                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
                     break;
                 case MSG_SUSPENDED:
-                    mCallback.onPackagesSuspended(info.packageNames, info.user);
+                    mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras,
+                            info.user);
                     break;
                 case MSG_UNSUSPENDED:
                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
@@ -1301,10 +1349,12 @@
             obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
         }
 
-        public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
+        public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras,
+                UserHandle user) {
             CallbackInfo info = new CallbackInfo();
             info.packageNames = packageNames;
             info.user = user;
+            info.launcherExtras = launcherExtras;
             obtainMessage(MSG_SUSPENDED, info).sendToTarget();
         }
 
@@ -1429,6 +1479,10 @@
          * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
          * different request type.
          *
+         * <p>Launcher should not show any configuration activity associated with the provider, and
+         * assume that the widget is already fully configured. Upon accepting the widget, it should
+         * pass the widgetId in {@link #accept(Bundle)}.
+         *
          * @return requested {@link AppWidgetProviderInfo} when a request is of the
          * {@link #REQUEST_TYPE_APPWIDGET} type.  Null otherwise.
          */
diff --git a/android/content/pm/OrgApacheHttpLegacyUpdater.java b/android/content/pm/OrgApacheHttpLegacyUpdater.java
index 81041e9..81e4105 100644
--- a/android/content/pm/OrgApacheHttpLegacyUpdater.java
+++ b/android/content/pm/OrgApacheHttpLegacyUpdater.java
@@ -15,13 +15,12 @@
  */
 package android.content.pm;
 
+import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
 import android.content.pm.PackageParser.Package;
-import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.ArrayList;
-
 /**
  * Updates a package to ensure that if it targets < P that the org.apache.http.legacy library is
  * included by default.
@@ -37,30 +36,13 @@
 @VisibleForTesting
 public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater {
 
-    private static final String APACHE_HTTP_LEGACY = "org.apache.http.legacy";
-
     @Override
     public void updatePackage(Package pkg) {
-        ArrayList<String> usesLibraries = pkg.usesLibraries;
-        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
         // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
         // to be accessible so this maintains backward compatibility by adding the
         // org.apache.http.legacy library to those packages.
         if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
-            boolean apacheHttpLegacyPresent = isLibraryPresent(
-                    usesLibraries, usesOptionalLibraries, APACHE_HTTP_LEGACY);
-            if (!apacheHttpLegacyPresent) {
-                usesLibraries = prefix(usesLibraries, APACHE_HTTP_LEGACY);
-            }
+            prefixRequiredLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
         }
-
-        pkg.usesLibraries = usesLibraries;
-        pkg.usesOptionalLibraries = usesOptionalLibraries;
-    }
-
-    private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) {
-        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
-        return targetSdkVersion <= Build.VERSION_CODES.O_MR1;
     }
 }
diff --git a/android/content/pm/PackageBackwardCompatibility.java b/android/content/pm/PackageBackwardCompatibility.java
index 9bdb78b..a16f81b 100644
--- a/android/content/pm/PackageBackwardCompatibility.java
+++ b/android/content/pm/PackageBackwardCompatibility.java
@@ -16,14 +16,19 @@
 
 package android.content.pm;
 
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
 import android.content.pm.PackageParser.Package;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Supplier;
 
 /**
  * Modifies {@link Package} in order to maintain backwards compatibility.
@@ -35,54 +40,90 @@
 
     private static final String TAG = PackageBackwardCompatibility.class.getSimpleName();
 
-    private static final String ANDROID_TEST_MOCK = "android.test.mock";
-
-    private static final String ANDROID_TEST_RUNNER = "android.test.runner";
-
     private static final PackageBackwardCompatibility INSTANCE;
 
     static {
-        String className = "android.content.pm.OrgApacheHttpLegacyUpdater";
+        final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
+
+        // Attempt to load and add the optional updater that will only be available when
+        // REMOVE_OAHL_FROM_BCP=true. If that could not be found then add the default updater that
+        // will remove any references to org.apache.http.library from the package so that it does
+        // not try and load the library when it is on the bootclasspath.
+        boolean bootClassPathContainsOAHL = !addOptionalUpdater(packageUpdaters,
+                "android.content.pm.OrgApacheHttpLegacyUpdater",
+                RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
+
+        // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
+        // android.test.mock.
+        packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
+
+        // Attempt to load and add the optional updater that will only be available when
+        // REMOVE_ATB_FROM_BCP=true. If that could not be found then add the default updater that
+        // will remove any references to org.apache.http.library from the package so that it does
+        // not try and load the library when it is on the bootclasspath.
+        boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters,
+                "android.content.pm.AndroidTestBaseUpdater",
+                RemoveUnnecessaryAndroidTestBaseLibrary::new);
+
+        PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
+                .toArray(new PackageSharedLibraryUpdater[0]);
+        INSTANCE = new PackageBackwardCompatibility(
+                bootClassPathContainsOAHL, bootClassPathContainsATB, updaterArray);
+    }
+
+    /**
+     * Add an optional {@link PackageSharedLibraryUpdater} instance to the list, if it could not be
+     * found then add a default instance instead.
+     *
+     * @param packageUpdaters the list to update.
+     * @param className the name of the optional class.
+     * @param defaultUpdater the supplier of the default instance.
+     * @return true if the optional updater was added false otherwise.
+     */
+    private static boolean addOptionalUpdater(List<PackageSharedLibraryUpdater> packageUpdaters,
+            String className, Supplier<PackageSharedLibraryUpdater> defaultUpdater) {
         Class<? extends PackageSharedLibraryUpdater> clazz;
         try {
             clazz = (PackageBackwardCompatibility.class.getClassLoader()
                     .loadClass(className)
                     .asSubclass(PackageSharedLibraryUpdater.class));
+            Log.i(TAG, "Loaded " + className);
         } catch (ClassNotFoundException e) {
             Log.i(TAG, "Could not find " + className + ", ignoring");
             clazz = null;
         }
 
-        boolean hasOrgApacheHttpLegacy = false;
-        final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
+        boolean usedOptional = false;
+        PackageSharedLibraryUpdater updater;
         if (clazz == null) {
-            // Add an updater that will remove any references to org.apache.http.library from the
-            // package so that it does not try and load the library when it is on the
-            // bootclasspath.
-            packageUpdaters.add(new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
+            updater = defaultUpdater.get();
         } else {
             try {
-                packageUpdaters.add(clazz.getConstructor().newInstance());
-                hasOrgApacheHttpLegacy = true;
+                updater = clazz.getConstructor().newInstance();
+                usedOptional = true;
             } catch (ReflectiveOperationException e) {
                 throw new IllegalStateException("Could not create instance of " + className, e);
             }
         }
-
-        packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
-
-        PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
-                .toArray(new PackageSharedLibraryUpdater[0]);
-        INSTANCE = new PackageBackwardCompatibility(hasOrgApacheHttpLegacy, updaterArray);
+        packageUpdaters.add(updater);
+        return usedOptional;
     }
 
-    private final boolean mRemovedOAHLFromBCP;
+    @VisibleForTesting
+    public static PackageSharedLibraryUpdater getInstance() {
+        return INSTANCE;
+    }
+
+    private final boolean mBootClassPathContainsOAHL;
+
+    private final boolean mBootClassPathContainsATB;
 
     private final PackageSharedLibraryUpdater[] mPackageUpdaters;
 
-    public PackageBackwardCompatibility(boolean removedOAHLFromBCP,
-            PackageSharedLibraryUpdater[] packageUpdaters) {
-        this.mRemovedOAHLFromBCP = removedOAHLFromBCP;
+    public PackageBackwardCompatibility(boolean bootClassPathContainsOAHL,
+            boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
+        this.mBootClassPathContainsOAHL = bootClassPathContainsOAHL;
+        this.mBootClassPathContainsATB = bootClassPathContainsATB;
         this.mPackageUpdaters = packageUpdaters;
     }
 
@@ -99,17 +140,25 @@
 
     @Override
     public void updatePackage(Package pkg) {
-
         for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
             packageUpdater.updatePackage(pkg);
         }
     }
 
     /**
-     * True if the org.apache.http.legacy has been removed the bootclasspath, false otherwise.
+     * True if the org.apache.http.legacy is on the bootclasspath, false otherwise.
      */
-    public static boolean removeOAHLFromBCP() {
-        return INSTANCE.mRemovedOAHLFromBCP;
+    @VisibleForTesting
+    public static boolean bootClassPathContainsOAHL() {
+        return INSTANCE.mBootClassPathContainsOAHL;
+    }
+
+    /**
+     * True if the android.test.base is on the bootclasspath, false otherwise.
+     */
+    @VisibleForTesting
+    public static boolean bootClassPathContainsATB() {
+        return INSTANCE.mBootClassPathContainsATB;
     }
 
     /**
@@ -126,24 +175,9 @@
 
         @Override
         public void updatePackage(Package pkg) {
-            ArrayList<String> usesLibraries = pkg.usesLibraries;
-            ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
             // android.test.runner has a dependency on android.test.mock so if android.test.runner
             // is present but android.test.mock is not then add android.test.mock.
-            boolean androidTestMockPresent = isLibraryPresent(
-                    usesLibraries, usesOptionalLibraries, ANDROID_TEST_MOCK);
-            if (ArrayUtils.contains(usesLibraries, ANDROID_TEST_RUNNER)
-                    && !androidTestMockPresent) {
-                usesLibraries.add(ANDROID_TEST_MOCK);
-            }
-            if (ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_RUNNER)
-                    && !androidTestMockPresent) {
-                usesOptionalLibraries.add(ANDROID_TEST_MOCK);
-            }
-
-            pkg.usesLibraries = usesLibraries;
-            pkg.usesOptionalLibraries = usesOptionalLibraries;
+            prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
         }
     }
 
@@ -155,13 +189,24 @@
     public static class RemoveUnnecessaryOrgApacheHttpLegacyLibrary
             extends PackageSharedLibraryUpdater {
 
-        private static final String APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+        @Override
+        public void updatePackage(Package pkg) {
+            removeLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
+        }
+
+    }
+
+    /**
+     * Remove any usages of android.test.base from the shared library as the library is on the
+     * bootclasspath.
+     */
+    @VisibleForTesting
+    public static class RemoveUnnecessaryAndroidTestBaseLibrary
+            extends PackageSharedLibraryUpdater {
 
         @Override
         public void updatePackage(Package pkg) {
-            pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, APACHE_HTTP_LEGACY);
-            pkg.usesOptionalLibraries =
-                    ArrayUtils.remove(pkg.usesOptionalLibraries, APACHE_HTTP_LEGACY);
+            removeLibrary(pkg, ANDROID_TEST_BASE);
         }
     }
 }
diff --git a/android/content/pm/PackageInfo.java b/android/content/pm/PackageInfo.java
index 09a46b8..627ceb7 100644
--- a/android/content/pm/PackageInfo.java
+++ b/android/content/pm/PackageInfo.java
@@ -362,6 +362,13 @@
      */
     public String overlayTarget;
 
+    /**
+     * The overlay category, if any, of this package
+     *
+     * @hide
+     */
+    public String overlayCategory;
+
     /** @hide */
     public int overlayPriority;
 
@@ -464,10 +471,23 @@
         dest.writeString(restrictedAccountType);
         dest.writeString(requiredAccountType);
         dest.writeString(overlayTarget);
+        dest.writeString(overlayCategory);
         dest.writeInt(overlayPriority);
         dest.writeBoolean(mOverlayIsStatic);
         dest.writeInt(compileSdkVersion);
         dest.writeString(compileSdkVersionCodename);
+        writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
+    }
+
+    private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
+        if (signingCertificateHistory != null) {
+            dest.writeInt(signingCertificateHistory.length);
+            for (int i = 0; i < signingCertificateHistory.length; i++) {
+                dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
+            }
+        } else {
+            dest.writeInt(-1);
+        }
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -519,10 +539,12 @@
         restrictedAccountType = source.readString();
         requiredAccountType = source.readString();
         overlayTarget = source.readString();
+        overlayCategory = source.readString();
         overlayPriority = source.readInt();
         mOverlayIsStatic = source.readBoolean();
         compileSdkVersion = source.readInt();
         compileSdkVersionCodename = source.readString();
+        readSigningCertificateHistoryFromParcel(source);
 
         // The component lists were flattened with the redundant ApplicationInfo
         // instances omitted.  Distribute the canonical one here as appropriate.
@@ -534,6 +556,16 @@
         }
     }
 
+    private void readSigningCertificateHistoryFromParcel(Parcel source) {
+        int len = source.readInt();
+        if (len != -1) {
+            signingCertificateHistory = new Signature[len][];
+            for (int i = 0; i < len; i++) {
+                signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
+            }
+        }
+    }
+
     private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
         if (components != null) {
             for (ComponentInfo ci : components) {
diff --git a/android/content/pm/PackageInstaller.java b/android/content/pm/PackageInstaller.java
index df677d2..25af1a7 100644
--- a/android/content/pm/PackageInstaller.java
+++ b/android/content/pm/PackageInstaller.java
@@ -448,11 +448,17 @@
 
     /**
      * Uninstall the given package, removing it completely from the device. This
-     * method is only available to the current "installer of record" for the
-     * package.
+     * method is available to:
+     * <ul>
+     * <li>the current "installer of record" for the package
+     * <li>the device owner
+     * <li>the affiliated profile owner
+     * </ul>
      *
      * @param packageName The package to uninstall.
      * @param statusReceiver Where to deliver the result.
+     *
+     * @see android.app.admin.DevicePolicyManager
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
@@ -480,14 +486,22 @@
 
     /**
      * Uninstall the given package with a specific version code, removing it
-     * completely from the device. This method is only available to the current
-     * "installer of record" for the package. If the version code of the package
+     * completely from the device. If the version code of the package
      * does not match the one passed in the versioned package argument this
      * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
      * uninstall the latest version of the package.
+     * <p>
+     * This method is available to:
+     * <ul>
+     * <li>the current "installer of record" for the package
+     * <li>the device owner
+     * <li>the affiliated profile owner
+     * </ul>
      *
      * @param versionedPackage The versioned package to uninstall.
      * @param statusReceiver Where to deliver the result.
+     *
+     * @see android.app.admin.DevicePolicyManager
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
@@ -829,7 +843,19 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
+        }
 
+        /** {@hide} */
+        public void write(@NonNull String name, long offsetBytes, long lengthBytes,
+                @NonNull ParcelFileDescriptor fd) throws IOException {
+            try {
+                mSession.write(name, offsetBytes, lengthBytes, fd);
+            } catch (RuntimeException e) {
+                ExceptionUtils.maybeUnwrapIOException(e);
+                throw e;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         /**
@@ -929,9 +955,14 @@
          * Once this method is called, the session is sealed and no additional
          * mutations may be performed on the session. If the device reboots
          * before the session has been finalized, you may commit the session again.
+         * <p>
+         * If the installer is the device owner or the affiliated profile owner, there will be no
+         * user intervention.
          *
          * @throws SecurityException if streams opened through
          *             {@link #openWrite(String, long, long)} are still open.
+         *
+         * @see android.app.admin.DevicePolicyManager
          */
         public void commit(@NonNull IntentSender statusReceiver) {
             try {
diff --git a/android/content/pm/PackageItemInfo.java b/android/content/pm/PackageItemInfo.java
index 2c0c6ad..53ffd55 100644
--- a/android/content/pm/PackageItemInfo.java
+++ b/android/content/pm/PackageItemInfo.java
@@ -43,6 +43,14 @@
  */
 public class PackageItemInfo {
     private static final float MAX_LABEL_SIZE_PX = 500f;
+
+    private static volatile boolean sForceSafeLabels = false;
+
+    /** {@hide} */
+    public static void setForceSafeLabels(boolean forceSafeLabels) {
+        sForceSafeLabels = forceSafeLabels;
+    }
+
     /**
      * Public name of this item. From the "android:name" attribute.
      */
@@ -128,7 +136,16 @@
      * @return Returns a CharSequence containing the item's label.  If the
      * item does not have a label, its name is returned.
      */
-    public CharSequence loadLabel(PackageManager pm) {
+    public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
+        if (sForceSafeLabels) {
+            return loadSafeLabel(pm);
+        } else {
+            return loadUnsafeLabel(pm);
+        }
+    }
+
+    /** {@hide} */
+    public CharSequence loadUnsafeLabel(PackageManager pm) {
         if (nonLocalizedLabel != null) {
             return nonLocalizedLabel;
         }
@@ -163,7 +180,7 @@
     @SystemApi
     public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
         // loadLabel() always returns non-null
-        String label = loadLabel(pm).toString();
+        String label = loadUnsafeLabel(pm).toString();
         // strip HTML tags to avoid <br> and other tags overwriting original message
         String labelStr = Html.fromHtml(label).toString();
 
diff --git a/android/content/pm/PackageManager.java b/android/content/pm/PackageManager.java
index df69d80..491f0af 100644
--- a/android/content/pm/PackageManager.java
+++ b/android/content/pm/PackageManager.java
@@ -51,6 +51,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -441,6 +442,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int MATCH_FACTORY_ONLY = 0x00200000;
 
     /**
@@ -1950,6 +1952,14 @@
      * <li>Minor version number in bits 21-12</li>
      * <li>Patch version number in bits 11-0</li>
      * </ul>
+     * A version of 1.1.0 or higher also indicates:
+     * <ul>
+     * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is
+     *     supported.</li>
+     * <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
+     * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
+     *     supported.</li>
+     * </ul>
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
@@ -2061,6 +2071,15 @@
             "android.hardware.sensor.hifi_sensors";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device supports a hardware mechanism for invoking an assist gesture.
+     * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a telephony radio with data
      * communication support.
@@ -2099,8 +2118,6 @@
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
      * supports embedded subscriptions on eUICCs.
-     * TODO(b/35851809): Make this public.
-     * @hide
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
@@ -2604,6 +2621,14 @@
     public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a StrongBox hardware-backed Keystore.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_STRONGBOX_KEYSTORE =
+            "android.hardware.strongbox_keystore";
+
+    /**
      * Action to external storage service to clean out removed apps.
      * @hide
      */
@@ -2949,6 +2974,11 @@
      */
     public static final int VERSION_CODE_HIGHEST = -1;
 
+    /** {@hide} */
+    public int getUserId() {
+        return UserHandle.myUserId();
+    }
+
     /**
      * Retrieve overall information about an application package that is
      * installed on the system.
@@ -3681,6 +3711,7 @@
      *
      * @hide
      */
+    @TestApi
     public abstract @Nullable String[] getNamesForUids(int[] uids);
 
     /**
@@ -4154,6 +4185,12 @@
     public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
 
     /**
+     * @hide
+     */
+    public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+            @UserIdInt int userId);
+
+    /**
      * Retrieve all services that can match the given intent.
      *
      * @param intent The desired intent as per resolveService().
@@ -4750,7 +4787,7 @@
 
             PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
             if ((flags & GET_SIGNATURES) != 0) {
-                PackageParser.collectCertificates(pkg, 0);
+                PackageParser.collectCertificates(pkg, false /* skipVerify */);
             }
             PackageUserState state = new PackageUserState();
             return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
@@ -5045,6 +5082,7 @@
      * which market the package came from.
      *
      * @param packageName The name of the package to query
+     * @throws IllegalArgumentException if the given package name is not installed
      */
     public abstract String getInstallerPackageName(String packageName);
 
@@ -5196,7 +5234,7 @@
      */
     @Deprecated
     public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
-        getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
+        getPackageSizeInfoAsUser(packageName, getUserId(), observer);
     }
 
     /**
@@ -5474,28 +5512,49 @@
     /**
      * Puts the package in a suspended state, where attempts at starting activities are denied.
      *
-     * <p>It doesn't remove the data or the actual package file. The application notifications
-     * will be hidden, the application will not show up in recents, will not be able to show
-     * toasts or dialogs or ring the device.
+     * <p>It doesn't remove the data or the actual package file. The application's notifications
+     * will be hidden, any of its started activities will be stopped and it will not be able to
+     * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
+     * system dialog with the given {@code dialogMessage} will be shown instead.</p>
      *
      * <p>The package must already be installed. If the package is uninstalled while suspended
-     * the package will no longer be suspended.
+     * the package will no longer be suspended. </p>
+     *
+     * <p>Optionally, the suspending app can provide extra information in the form of
+     * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+     * launcher to support customization that they might need to handle the suspended state. </p>
+     *
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
+     * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
      *
      * @param packageNames The names of the packages to set the suspended status.
      * @param suspended If set to {@code true} than the packages will be suspended, if set to
-     * {@code false} the packages will be unsuspended.
-     * @param userId The user id.
+     * {@code false}, the packages will be unsuspended.
+     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+     *                  which will be shared with the apps being suspended. Ignored if
+     *                  {@code suspended} is false.
+     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+     *                       provide which will be shared with the launcher. Ignored if
+     *                       {@code suspended} is false.
+     * @param dialogMessage The message to be displayed to the user, when they try to launch a
+     *                      suspended app.
      *
      * @return an array of package names for which the suspended status is not set as requested in
      * this method.
      *
      * @hide
      */
-    public abstract String[] setPackagesSuspendedAsUser(
-            String[] packageNames, boolean suspended, @UserIdInt int userId);
+    @SystemApi
+    @RequiresPermission(anyOf = {Manifest.permission.SUSPEND_APPS,
+            Manifest.permission.MANAGE_USERS})
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+            String dialogMessage) {
+        throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+    }
 
     /**
-     * @see #setPackageSuspendedAsUser(String, boolean, int)
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
      * @param packageName The name of the package to get the suspended status of.
      * @param userId The user id.
      * @return {@code true} if the package is suspended or {@code false} if the package is not
@@ -5505,6 +5564,103 @@
     public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
 
     /**
+     * Query if an app is currently suspended.
+     *
+     * @return {@code true} if the given package is suspended, {@code false} otherwise
+     *
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     * @hide
+     */
+    @SystemApi
+    public boolean isPackageSuspended(String packageName) {
+        throw new UnsupportedOperationException("isPackageSuspended not implemented");
+    }
+
+    /**
+     * Apps can query this to know if they have been suspended. A system app with the permission
+     * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state.
+     *
+     * <p>While in this state, the application's notifications will be hidden, any of its started
+     * activities will be stopped and it will not be able to show toasts or dialogs or ring the
+     * device. When the user tries to launch a suspended app, the system will, instead, show a
+     * dialog to the user informing them that they cannot use this app while it is suspended.
+     *
+     * <p>When an app is put into this state, the broadcast action
+     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast
+     * receivers that included this action in their intent-filters, <em>including manifest
+     * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED}
+     * is delivered when a previously suspended app is taken out of this state.
+     * </p>
+     *
+     * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
+     *
+     * @see #getSuspendedPackageAppExtras()
+     * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+     */
+    public boolean isPackageSuspended() {
+        throw new UnsupportedOperationException("isPackageSuspended not implemented");
+    }
+
+    /**
+     * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
+     * package was suspended.
+     *
+     * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
+     * api.</p>
+     *
+     * @param packageName The package to retrieve extras for.
+     * @return The {@code appExtras} for the suspended package.
+     *
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
+     * Set the app extras for a suspended package. This method can be used to update the appExtras
+     * for a package that was earlier suspended using
+     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * String)}
+     * Does nothing if the given package is not already in a suspended state.
+     *
+     * @param packageName The package for which the appExtras need to be updated
+     * @param appExtras The new appExtras for the given package
+     *
+     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    public void setSuspendedPackageAppExtras(String packageName,
+            @Nullable PersistableBundle appExtras) {
+        throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
+     * Returns any extra information supplied as {@code appExtras} to the system when the calling
+     * app was suspended.
+     *
+     * <p>Note: If no extras were supplied to the system, this method will return {@code null}, even
+     * when the calling app has been suspended.</p>
+     *
+     * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
+     * package is not currently suspended.
+     *
+     * @see #isPackageSuspended()
+     * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     */
+    public @Nullable Bundle getSuspendedPackageAppExtras() {
+        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+    }
+
+    /**
      * Provide a hint of what the {@link ApplicationInfo#category} value should
      * be for the given package.
      * <p>
@@ -5992,4 +6148,26 @@
         throw new UnsupportedOperationException(
                 "hasSigningCertificate not implemented in subclass");
     }
+
+    /**
+     * @return the system defined text classifier package name, or null if there's none.
+     *
+     * @hide
+     */
+    public String getSystemTextClassifierPackageName() {
+        throw new UnsupportedOperationException(
+                "getSystemTextClassifierPackageName not implemented in subclass");
+    }
+
+    /**
+     * @return whether a given package's state is protected, e.g. package cannot be disabled,
+     *         suspended, hidden or force stopped.
+     *
+     * @hide
+     */
+    public boolean isPackageStateProtected(String packageName, int userId) {
+        throw new UnsupportedOperationException(
+            "isPackageStateProtected not implemented in subclass");
+    }
+
 }
diff --git a/android/content/pm/PackageManagerInternal.java b/android/content/pm/PackageManagerInternal.java
index 6f093ba..c9b78c0 100644
--- a/android/content/pm/PackageManagerInternal.java
+++ b/android/content/pm/PackageManagerInternal.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.PackageManager.ResolveInfoFlags;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.util.SparseArray;
 
 import java.lang.annotation.Retention;
@@ -43,12 +44,14 @@
     public static final int PACKAGE_INSTALLER = 2;
     public static final int PACKAGE_VERIFIER = 3;
     public static final int PACKAGE_BROWSER = 4;
+    public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
         PACKAGE_INSTALLER,
         PACKAGE_VERIFIER,
         PACKAGE_BROWSER,
+        PACKAGE_SYSTEM_TEXT_CLASSIFIER,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
@@ -186,6 +189,22 @@
             @PackageInfoFlags int flags, int filterCallingUid, int userId);
 
     /**
+     * Retrieve launcher extras for a suspended package provided to the system in
+     * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)}
+     *
+     * @param packageName The package for which to return launcher extras.
+     * @param userId The user for which to check,
+     * @return The launcher extras.
+     *
+     * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)
+     * @see PackageManager#isPackageSuspended()
+     */
+    public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
+            int userId);
+
+    /**
      * Do a straight uid lookup for the given package/application in the given user.
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
@@ -234,6 +253,11 @@
             int userId);
 
     /**
+     * @return The default home activity component name.
+     */
+    public abstract ComponentName getDefaultHomeActivity(int userId);
+
+    /**
      * Called by DeviceOwnerManagerService to set the package names of device owner and profile
      * owners.
      */
@@ -444,6 +468,9 @@
     /** Whether the binder caller can access instant apps. */
     public abstract boolean canAccessInstantApps(int callingUid, int userId);
 
+    /** Whether the binder caller can access the given component. */
+    public abstract boolean canAccessComponent(int callingUid, ComponentName component, int userId);
+
     /**
      * Returns {@code true} if a given package has instant application meta-data.
      * Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
@@ -537,4 +564,18 @@
     /** Updates the flags for the given permission. */
     public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
             @NonNull String packageName, int flagMask, int flagValues, int userId);
+
+    /**
+     * Returns true if it's still safe to restore data backed up from this app's version
+     * that was signed with restoringFromSigHash.
+     */
+    public abstract boolean isDataRestoreSafe(@NonNull byte[] restoringFromSigHash,
+            @NonNull String packageName);
+
+    /**
+     * Returns true if it's still safe to restore data backed up from this app's version
+     * that was signed with restoringFromSig.
+     */
+    public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
+            @NonNull String packageName);
 }
diff --git a/android/content/pm/PackageParser.java b/android/content/pm/PackageParser.java
index 24e3dfa..2f0faf2 100644
--- a/android/content/pm/PackageParser.java
+++ b/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -78,8 +79,10 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Base64;
+import android.util.ByteStringUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.PackageUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -194,10 +197,6 @@
     private static final String TAG_RESTRICT_UPDATE = "restrict-update";
     private static final String TAG_USES_SPLIT = "uses-split";
 
-    // [b/36551762] STOPSHIP remove the ability to expose components via meta-data
-    // Temporary workaround; allow meta-data to expose components to instant apps
-    private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed";
-
     private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
 
     /**
@@ -678,6 +677,7 @@
         pi.restrictedAccountType = p.mRestrictedAccountType;
         pi.requiredAccountType = p.mRequiredAccountType;
         pi.overlayTarget = p.mOverlayTarget;
+        pi.overlayCategory = p.mOverlayCategory;
         pi.overlayPriority = p.mOverlayPriority;
         pi.mOverlayIsStatic = p.mOverlayIsStatic;
         pi.compileSdkVersion = p.mCompileSdkVersion;
@@ -1288,7 +1288,6 @@
      */
     @Deprecated
     public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
-        final AssetManager assets = newConfiguredAssetManager();
         final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
         if (mOnlyCoreApps) {
             if (!lite.coreApp) {
@@ -1297,8 +1296,9 @@
             }
         }
 
+        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
         try {
-            final Package pkg = parseBaseApk(apkFile, assets, flags);
+            final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
             pkg.setCodePath(apkFile.getCanonicalPath());
             pkg.setUse32bitAbi(lite.use32bitAbi);
             return pkg;
@@ -1306,28 +1306,10 @@
             throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                     "Failed to get path: " + apkFile, e);
         } finally {
-            IoUtils.closeQuietly(assets);
+            IoUtils.closeQuietly(assetLoader);
         }
     }
 
-    private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
-            throws PackageParserException {
-        if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                    "Invalid package file: " + apkPath);
-        }
-
-        // The AssetManager guarantees uniqueness for asset paths, so if this asset path
-        // already exists in the AssetManager, addAssetPath will only return the cookie
-        // assigned to it.
-        int cookie = assets.addAssetPath(apkPath);
-        if (cookie == 0) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                    "Failed adding asset path: " + apkPath);
-        }
-        return cookie;
-    }
-
     private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
             throws PackageParserException {
         final String apkPath = apkFile.getAbsolutePath();
@@ -1343,13 +1325,15 @@
 
         if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
 
-        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
-
-        Resources res = null;
         XmlResourceParser parser = null;
         try {
-            res = new Resources(assets, mMetrics, null);
+            final int cookie = assets.findCookieForPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
             parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+            final Resources res = new Resources(assets, mMetrics, null);
 
             final String[] outError = new String[1];
             final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
@@ -1384,15 +1368,18 @@
 
         if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
 
-        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
-
         final Resources res;
         XmlResourceParser parser = null;
         try {
-            res = new Resources(assets, mMetrics, null);
-            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                    Build.VERSION.RESOURCES_SDK_INT);
+            // This must always succeed, as the path has been added to the AssetManager before.
+            final int cookie = assets.findCookieForPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
+
             parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+            res = new Resources(assets, mMetrics, null);
 
             final String[] outError = new String[1];
             pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
@@ -1496,9 +1483,9 @@
      * populating {@link Package#mSigningDetails}. Also asserts that all APK
      * contents are signed correctly and consistently.
      */
-    public static void collectCertificates(Package pkg, @ParseFlags int parseFlags)
+    public static void collectCertificates(Package pkg, boolean skipVerify)
             throws PackageParserException {
-        collectCertificatesInternal(pkg, parseFlags);
+        collectCertificatesInternal(pkg, skipVerify);
         final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
         for (int i = 0; i < childCount; i++) {
             Package childPkg = pkg.childPackages.get(i);
@@ -1506,17 +1493,17 @@
         }
     }
 
-    private static void collectCertificatesInternal(Package pkg, @ParseFlags int parseFlags)
+    private static void collectCertificatesInternal(Package pkg, boolean skipVerify)
             throws PackageParserException {
         pkg.mSigningDetails = SigningDetails.UNKNOWN;
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
         try {
-            collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
+            collectCertificates(pkg, new File(pkg.baseCodePath), skipVerify);
 
             if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
                 for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                    collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
+                    collectCertificates(pkg, new File(pkg.splitCodePaths[i]), skipVerify);
                 }
             }
         } finally {
@@ -1524,7 +1511,7 @@
         }
     }
 
-    private static void collectCertificates(Package pkg, File apkFile, @ParseFlags int parseFlags)
+    private static void collectCertificates(Package pkg, File apkFile, boolean skipVerify)
             throws PackageParserException {
         final String apkPath = apkFile.getAbsolutePath();
 
@@ -1534,7 +1521,7 @@
             minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
         }
         SigningDetails verified;
-        if ((parseFlags & PARSE_IS_SYSTEM_DIR) != 0) {
+        if (skipVerify) {
             // systemDir APKs are already trusted, save time by not verifying
             verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
                         apkPath, minSignatureScheme);
@@ -1594,29 +1581,28 @@
             int flags) throws PackageParserException {
         final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
 
-        AssetManager assets = null;
         XmlResourceParser parser = null;
         try {
-            assets = newConfiguredAssetManager();
-            int cookie = fd != null
-                    ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath);
-            if (cookie == 0) {
+            final ApkAssets apkAssets;
+            try {
+                apkAssets = fd != null
+                        ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+                        : ApkAssets.loadFromPath(apkPath);
+            } catch (IOException e) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                         "Failed to parse " + apkPath);
             }
 
-            final DisplayMetrics metrics = new DisplayMetrics();
-            metrics.setToDefaults();
-
-            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+            parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
 
             final SigningDetails signingDetails;
             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                 // TODO: factor signature related items out of Package object
                 final Package tempPkg = new Package((String) null);
+                final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                 try {
-                    collectCertificates(tempPkg, apkFile, flags);
+                    collectCertificates(tempPkg, apkFile, skipVerify);
                 } finally {
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
@@ -1634,7 +1620,7 @@
                     "Failed to parse " + apkPath, e);
         } finally {
             IoUtils.closeQuietly(parser);
-            IoUtils.closeQuietly(assets);
+            // TODO(b/72056911): Implement and call close() on ApkAssets.
         }
     }
 
@@ -2074,6 +2060,8 @@
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                 pkg.mOverlayTarget = sa.getString(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+                pkg.mOverlayCategory = sa.getString(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
                 pkg.mOverlayPriority = sa.getInt(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
                         0);
@@ -3183,12 +3171,12 @@
 
         perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
 
-        if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
+        if (perm.info.getProtectionFlags() != 0) {
             if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
                     PermissionInfo.PROTECTION_SIGNATURE) {
-                outError[0] = "<permission>  protectionLevel specifies a non-instnat flag but is "
+                outError[0] = "<permission>  protectionLevel specifies a non-instant flag but is "
                         + "not based on signature type";
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                 return false;
@@ -3504,7 +3492,7 @@
 
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
-                true)) {
+                owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.P)) {
             ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
         }
 
@@ -3639,7 +3627,9 @@
         // getting added to the wrong package.
         final CachedComponentArgs cachedArgs = new CachedComponentArgs();
         int type;
-
+        boolean hasActivityOrder = false;
+        boolean hasReceiverOrder = false;
+        boolean hasServiceOrder = false;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3655,6 +3645,7 @@
                     return false;
                 }
 
+                hasActivityOrder |= (a.order != 0);
                 owner.activities.add(a);
 
             } else if (tagName.equals("receiver")) {
@@ -3665,6 +3656,7 @@
                     return false;
                 }
 
+                hasReceiverOrder |= (a.order != 0);
                 owner.receivers.add(a);
 
             } else if (tagName.equals("service")) {
@@ -3674,6 +3666,7 @@
                     return false;
                 }
 
+                hasServiceOrder |= (s.order != 0);
                 owner.services.add(s);
 
             } else if (tagName.equals("provider")) {
@@ -3692,6 +3685,7 @@
                     return false;
                 }
 
+                hasActivityOrder |= (a.order != 0);
                 owner.activities.add(a);
 
             } else if (parser.getName().equals("meta-data")) {
@@ -3825,6 +3819,15 @@
             }
         }
 
+        if (hasActivityOrder) {
+            Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+        }
+        if (hasReceiverOrder) {
+            Collections.sort(owner.receivers,  (r1, r2) -> Integer.compare(r2.order, r1.order));
+        }
+        if (hasServiceOrder) {
+            Collections.sort(owner.services,  (s1, s2) -> Integer.compare(s2.order, s1.order));
+        }
         // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
         // every activity info has had a chance to set it from its attributes.
         setMaxAspectRatio(owner);
@@ -4366,6 +4369,7 @@
                             + mArchiveSourcePath + " "
                             + parser.getPositionDescription());
                 } else {
+                    a.order = Math.max(intent.getOrder(), a.order);
                     a.intents.add(intent);
                 }
                 // adjust activity flags when we implicitly expose it via a browsable filter
@@ -4427,24 +4431,6 @@
                         outError)) == null) {
                     return null;
                 }
-                // we don't have an attribute [or it's false], but, we have meta-data
-                if (!visibleToEphemeral && a.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
-                    visibleToEphemeral = true; // set in case there are more intent filters
-                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
-                    a.info.flags &= ~ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
-                    owner.visibleToInstantApps = true;
-                    // cycle through any filters already seen
-                    for (int i = a.intents.size() - 1; i >= 0; --i) {
-                        a.intents.get(i)
-                                .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
-                    }
-                    if (owner.preferredActivityFilters != null) {
-                        for (int i = owner.preferredActivityFilters.size() - 1; i >= 0; --i) {
-                            owner.preferredActivityFilters.get(i)
-                                    .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
-                        }
-                    }
-                }
             } else if (!receiver && parser.getName().equals("layout")) {
                 parseLayout(res, parser, a);
             } else {
@@ -4694,6 +4680,7 @@
         info.windowLayout = target.info.windowLayout;
         info.resizeMode = target.info.resizeMode;
         info.maxAspectRatio = target.info.maxAspectRatio;
+        info.requestedVrComponent = target.info.requestedVrComponent;
 
         info.encryptionAware = info.directBootAware = target.info.directBootAware;
 
@@ -4761,6 +4748,7 @@
                             + mArchiveSourcePath + " "
                             + parser.getPositionDescription());
                 } else {
+                    a.order = Math.max(intent.getOrder(), a.order);
                     a.intents.add(intent);
                 }
                 // adjust activity flags when we implicitly expose it via a browsable filter
@@ -4939,7 +4927,7 @@
         p.info.authority = cpname.intern();
 
         if (!parseProviderTags(
-                res, parser, visibleToEphemeral, owner, p, outError)) {
+                res, parser, visibleToEphemeral, p, outError)) {
             return null;
         }
 
@@ -4947,7 +4935,7 @@
     }
 
     private boolean parseProviderTags(Resources res, XmlResourceParser parser,
-            boolean visibleToEphemeral, Package owner, Provider outInfo, String[] outError)
+            boolean visibleToEphemeral, Provider outInfo, String[] outError)
                     throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
@@ -4968,6 +4956,7 @@
                     intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
                     outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
                 }
+                outInfo.order = Math.max(intent.getOrder(), outInfo.order);
                 outInfo.intents.add(intent);
 
             } else if (parser.getName().equals("meta-data")) {
@@ -4975,17 +4964,6 @@
                         outInfo.metaData, outError)) == null) {
                     return false;
                 }
-                // we don't have an attribute [or it's false], but, we have meta-data
-                if (!visibleToEphemeral && outInfo.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
-                    visibleToEphemeral = true; // set in case there are more intent filters
-                    outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
-                    owner.visibleToInstantApps = true;
-                    // cycle through any filters already seen
-                    for (int i = outInfo.intents.size() - 1; i >= 0; --i) {
-                        outInfo.intents.get(i)
-                                .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
-                    }
-                }
 
             } else if (parser.getName().equals("grant-uri-permission")) {
                 TypedArray sa = res.obtainAttributes(parser,
@@ -5268,23 +5246,13 @@
                     intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
                     s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
                 }
+                s.order = Math.max(intent.getOrder(), s.order);
                 s.intents.add(intent);
             } else if (parser.getName().equals("meta-data")) {
                 if ((s.metaData=parseMetaData(res, parser, s.metaData,
                         outError)) == null) {
                     return null;
                 }
-                // we don't have an attribute [or it's false], but, we have meta-data
-                if (!visibleToEphemeral && s.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
-                    visibleToEphemeral = true; // set in case there are more intent filters
-                    s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
-                    owner.visibleToInstantApps = true;
-                    // cycle through any filters already seen
-                    for (int i = s.intents.size() - 1; i >= 0; --i) {
-                        s.intents.get(i)
-                                .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
-                    }
-                }
             } else {
                 if (!RIGID_PARSER) {
                     Slog.w(TAG, "Unknown element under <service>: "
@@ -5504,6 +5472,10 @@
                 com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
         outInfo.setPriority(priority);
 
+        int order = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0);
+        outInfo.setOrder(order);
+
         TypedValue v = sa.peekValue(
                 com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
         if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -5682,7 +5654,10 @@
         return true;
     }
 
-    /** A container for signing-related data of an application package. */
+    /**
+     *  A container for signing-related data of an application package.
+     * @hide
+     */
     public static final class SigningDetails implements Parcelable {
 
         @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
@@ -5704,15 +5679,58 @@
         public final ArraySet<PublicKey> publicKeys;
 
         /**
-         * Collection of {@code Signature} objects, each of which is formed from a former signing
-         * certificate of this APK before it was changed by signing certificate rotation.
+         * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+         * contains two pieces of information:
+         *   1) the past signing certificates
+         *   2) the flags that APK wants to assign to each of the past signing certificates.
+         *
+         * This collection of {@code Signature} objects, each of which is formed from a former
+         * signing certificate of this APK before it was changed by signing certificate rotation,
+         * represents the first piece of information.  It is the APK saying to the rest of the
+         * world: "hey if you trust the old cert, you can trust me!"  This is useful, if for
+         * instance, the platform would like to determine whether or not to allow this APK to do
+         * something it would've allowed it to do under the old cert (like upgrade).
          */
         @Nullable
         public final Signature[] pastSigningCertificates;
 
+        /** special value used to see if cert is in package - not exposed to callers */
+        private static final int PAST_CERT_EXISTS = 0;
+
+        @IntDef(
+                flag = true,
+                value = {CertCapabilities.INSTALLED_DATA,
+                        CertCapabilities.SHARED_USER_ID,
+                        CertCapabilities.PERMISSION,
+                        CertCapabilities.ROLLBACK})
+        public @interface CertCapabilities {
+
+            /** accept data from already installed pkg with this cert */
+            int INSTALLED_DATA = 1;
+
+            /** accept sharedUserId with pkg with this cert */
+            int SHARED_USER_ID = 2;
+
+            /** grant SIGNATURE permissions to pkgs with this cert */
+            int PERMISSION = 4;
+
+            /** allow pkg to update to one signed by this certificate */
+            int ROLLBACK = 8;
+        }
+
         /**
-         * Flags for the {@code pastSigningCertificates} collection, which indicate the capabilities
-         * the including APK wishes to grant to its past signing certificates.
+         * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+         * contains two pieces of information:
+         *   1) the past signing certificates
+         *   2) the flags that APK wants to assign to each of the past signing certificates.
+         *
+         * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
+         * collection, represent the second piece of information and are viewed as capabilities.
+         * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+         * please enforce that." This is useful for situation where this app itself is using its
+         * signing certificate as an authorization mechanism, like whether or not to allow another
+         * app to have its SIGNATURE permission.  An app could specify whether to allow other apps
+         * signed by its old cert 'X' to still get a signature permission it defines, for example.
          */
         @Nullable
         public final int[] pastSigningCertificatesFlags;
@@ -5783,6 +5801,244 @@
             return pastSigningCertificates != null && pastSigningCertificates.length > 0;
         }
 
+        /**
+         * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+         * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+         * then that means it has authorized a signing certificate rotation, which eventually leads
+         * to our certificate, and thus can be trusted. If this method evaluates to true, this
+         * SigningDetails object should be trusted if the previous one is.
+         */
+        public boolean hasAncestorOrSelf(SigningDetails oldDetails) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (oldDetails.signatures.length > 1) {
+
+                // multiple-signer packages cannot rotate signing certs, so we just compare current
+                // signers for an exact match
+                return signaturesMatchExactly(oldDetails);
+            } else {
+
+                // we may have signing certificate rotation history, check to see if the oldDetails
+                // was one of our old signing certificates
+                return hasCertificate(oldDetails.signatures[0]);
+            }
+        }
+
+        /**
+         * Similar to {@code hasAncestorOrSelf}.  Returns true only if this {@code SigningDetails}
+         * is a descendant of {@code oldDetails}, not if they're the same.  This is used to
+         * determine if this object is newer than the provided one.
+         */
+        public boolean hasAncestor(SigningDetails oldDetails) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (this.hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+                // the last entry in pastSigningCertificates is the current signer, ignore it
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (pastSigningCertificates[i].equals(oldDetails.signatures[i])) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+         * not this one grants it the provided capability, represented by the {@code flags}
+         * parameter.  In the event of signing certificate rotation, a package may still interact
+         * with entities signed by its old signing certificate and not want to break previously
+         * functioning behavior.  The {@code flags} value determines which capabilities the app
+         * signed by the newer signing certificate would like to continue to give to its previous
+         * signing certificate(s).
+         */
+        public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) {
+            if (this == UNKNOWN || oldDetails == UNKNOWN) {
+                return false;
+            }
+            if (oldDetails.signatures.length > 1) {
+
+                // multiple-signer packages cannot rotate signing certs, so we must have an exact
+                // match, which also means all capabilities are granted
+                return signaturesMatchExactly(oldDetails);
+            } else {
+
+                // we may have signing certificate rotation history, check to see if the oldDetails
+                // was one of our old signing certificates, and if we grant it the capability it's
+                // requesting
+                return hasCertificate(oldDetails.signatures[0], flags);
+            }
+        }
+
+        /**
+         * A special case of {@code checkCapability} which re-encodes both sets of signing
+         * certificates to counteract a previous re-encoding.
+         */
+        public boolean checkCapabilityRecover(SigningDetails oldDetails,
+                @CertCapabilities int flags) throws CertificateException {
+            if (oldDetails == UNKNOWN || this == UNKNOWN) {
+                return false;
+            }
+            if (hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+                // signing certificates may have rotated, check entire history for effective match
+                for (int i = 0; i < pastSigningCertificates.length; i++) {
+                    if (Signature.areEffectiveMatch(
+                            oldDetails.signatures[0],
+                            pastSigningCertificates[i])
+                            && pastSigningCertificatesFlags[i] == flags) {
+                        return true;
+                    }
+                }
+            } else {
+                return Signature.areEffectiveMatch(oldDetails.signatures, signatures);
+            }
+            return false;
+        }
+
+        /**
+         * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+         * including the current signer.  Automatically returns false if this object has multiple
+         * signing certificates, since rotation is only supported for single-signers; this is
+         * enforced by {@code hasCertificateInternal}.
+         */
+        public boolean hasCertificate(Signature signature) {
+            return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+        }
+
+        /**
+         * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+         * including the current signer, and whether or not it has the given permission.
+         * Certificates which match our current signer automatically get all capabilities.
+         * Automatically returns false if this object has multiple signing certificates, since
+         * rotation is only supported for single-signers.
+         */
+        public boolean hasCertificate(Signature signature, @CertCapabilities int flags) {
+            return hasCertificateInternal(signature, flags);
+        }
+
+        /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+        public boolean hasCertificate(byte[] certificate) {
+            Signature signature = new Signature(certificate);
+            return hasCertificate(signature);
+        }
+
+        private boolean hasCertificateInternal(Signature signature, int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+
+            // only single-signed apps can have pastSigningCertificates
+            if (hasPastSigningCertificates()) {
+
+                // check all past certs, except for the current one, which automatically gets all
+                // capabilities, since it is the same as the current signature
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (pastSigningCertificates[i].equals(signature)) {
+                        if (flags == PAST_CERT_EXISTS
+                                || (flags & pastSigningCertificatesFlags[i]) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            // not in previous certs signing history, just check the current signer and make sure
+            // we are singly-signed
+            return signatures.length == 1 && signatures[0].equals(signature);
+        }
+
+        /**
+         * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+         * or not this one grants it the provided capability, represented by the {@code flags}
+         * parameter.  In the event of signing certificate rotation, a package may still interact
+         * with entities signed by its old signing certificate and not want to break previously
+         * functioning behavior.  The {@code flags} value determines which capabilities the app
+         * signed by the newer signing certificate would like to continue to give to its previous
+         * signing certificate(s).
+         *
+         * @param sha256String A hex-encoded representation of a sha256 digest.  In the case of an
+         *                     app with multiple signers, this represents the hex-encoded sha256
+         *                     digest of the combined hex-encoded sha256 digests of each individual
+         *                     signing certificate according to {@link
+         *                     PackageUtils#computeSignaturesSha256Digest(Signature[])}
+         */
+        public boolean checkCapability(String sha256String, @CertCapabilities int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+
+            // first see if the hash represents a single-signer in our signing history
+            byte[] sha256Bytes = ByteStringUtils.fromHexToByteArray(sha256String);
+            if (hasSha256Certificate(sha256Bytes, flags)) {
+                return true;
+            }
+
+            // Not in signing history, either represents multiple signatures or not a match.
+            // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+            // We already check the single-signer case above as part of hasSha256Certificate, so no
+            // need to verify we have multiple signers, just run the old check
+            // just consider current signing certs
+            final String[] mSignaturesSha256Digests =
+                    PackageUtils.computeSignaturesSha256Digests(signatures);
+            final String mSignaturesSha256Digest =
+                    PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+            return mSignaturesSha256Digest.equals(sha256String);
+        }
+
+        /**
+         * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+         * history, including the current signer.  Automatically returns false if this object has
+         * multiple signing certificates, since rotation is only supported for single-signers.
+         */
+        public boolean hasSha256Certificate(byte[] sha256Certificate) {
+            return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+        }
+
+        /**
+         * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+         * certificate in this SigningDetails' signing certificate history, including the current
+         * signer, and whether or not it has the given permission.  Certificates which match our
+         * current signer automatically get all capabilities. Automatically returns false if this
+         * object has multiple signing certificates, since rotation is only supported for
+         * single-signers.
+         */
+        public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+            return hasSha256CertificateInternal(sha256Certificate, flags);
+        }
+
+        private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+            if (this == UNKNOWN) {
+                return false;
+            }
+            if (hasPastSigningCertificates()) {
+
+                // check all past certs, except for the last one, which automatically gets all
+                // capabilities, since it is the same as the current signature, and is checked below
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    byte[] digest = PackageUtils.computeSha256DigestBytes(
+                            pastSigningCertificates[i].toByteArray());
+                    if (Arrays.equals(sha256Certificate, digest)) {
+                        if (flags == PAST_CERT_EXISTS
+                                || (flags & pastSigningCertificatesFlags[i]) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            // not in previous certs signing history, just check the current signer
+            if (signatures.length == 1) {
+                byte[] digest =
+                        PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+                return Arrays.equals(sha256Certificate, digest);
+            }
+            return false;
+        }
+
         /** Returns true if the signatures in this and other match exactly. */
         public boolean signaturesMatchExactly(SigningDetails other) {
             return Signature.areExactMatch(this.signatures, other.signatures);
@@ -6085,6 +6341,7 @@
         public String mRequiredAccountType;
 
         public String mOverlayTarget;
+        public String mOverlayCategory;
         public int mOverlayPriority;
         public boolean mOverlayIsStatic;
 
@@ -6393,6 +6650,11 @@
         }
 
         /** @hide */
+        public boolean isProduct() {
+            return applicationInfo.isProduct();
+        }
+
+        /** @hide */
         public boolean isPrivileged() {
             return applicationInfo.isPrivilegedApp();
         }
@@ -6450,31 +6712,6 @@
                 + " " + packageName + "}";
         }
 
-        public String dumpState_temp() {
-            String flags = "";
-            flags += ((applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
-            flags += ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
-            if ("".equals(flags)) {
-                flags = "-";
-            }
-            String privFlags = "";
-            privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
-            privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
-            privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
-            if ("".equals(privFlags)) {
-                privFlags = "-";
-            }
-            return "Package{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " " + packageName
-            + ", ver:" + getLongVersionCode()
-            + ", path: " + codePath
-            + ", flags: " + flags
-            + ", privFlags: " + privFlags
-            + ", extra: " + (mExtras == null ? "<<NULL>>" : Integer.toHexString(System.identityHashCode(mExtras)) + "}")
-            + "}";
-        }
-
         @Override
         public int describeContents() {
             return 0;
@@ -6615,6 +6852,7 @@
             mRestrictedAccountType = dest.readString();
             mRequiredAccountType = dest.readString();
             mOverlayTarget = dest.readString();
+            mOverlayCategory = dest.readString();
             mOverlayPriority = dest.readInt();
             mOverlayIsStatic = (dest.readInt() == 1);
             mCompileSdkVersion = dest.readInt();
@@ -6738,6 +6976,7 @@
             dest.writeString(mRestrictedAccountType);
             dest.writeString(mRequiredAccountType);
             dest.writeString(mOverlayTarget);
+            dest.writeString(mOverlayCategory);
             dest.writeInt(mOverlayPriority);
             dest.writeInt(mOverlayIsStatic ? 1 : 0);
             dest.writeInt(mCompileSdkVersion);
@@ -6828,6 +7067,8 @@
 
         public Bundle metaData;
         public Package owner;
+        /** The order of this component in relation to its peers */
+        public int order;
 
         ComponentName componentName;
         String componentShortName;
@@ -7346,6 +7587,7 @@
 
             for (ActivityIntentInfo aii : intents) {
                 aii.activity = this;
+                order = Math.max(aii.getOrder(), order);
             }
 
             if (info.permission != null) {
@@ -7435,6 +7677,7 @@
 
             for (ServiceIntentInfo aii : intents) {
                 aii.service = this;
+                order = Math.max(aii.getOrder(), order);
             }
 
             if (info.permission != null) {
diff --git a/android/content/pm/PackageSharedLibraryUpdater.java b/android/content/pm/PackageSharedLibraryUpdater.java
index 49d884c..fa89432 100644
--- a/android/content/pm/PackageSharedLibraryUpdater.java
+++ b/android/content/pm/PackageSharedLibraryUpdater.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -38,6 +39,12 @@
      */
     public abstract void updatePackage(PackageParser.Package pkg);
 
+    static void removeLibrary(PackageParser.Package pkg, String libraryName) {
+        pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, libraryName);
+        pkg.usesOptionalLibraries =
+                ArrayUtils.remove(pkg.usesOptionalLibraries, libraryName);
+    }
+
     static @NonNull
             <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
         if (cur == null) {
@@ -47,9 +54,54 @@
         return cur;
     }
 
-    static boolean isLibraryPresent(ArrayList<String> usesLibraries,
+    private static boolean isLibraryPresent(ArrayList<String> usesLibraries,
             ArrayList<String> usesOptionalLibraries, String apacheHttpLegacy) {
         return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
                 || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
     }
+
+    static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) {
+        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+        return targetSdkVersion <= Build.VERSION_CODES.O_MR1;
+    }
+
+    /**
+     * Add an implicit dependency.
+     *
+     * <p>If the package has an existing dependency on {@code existingLibrary} then prefix it with
+     * the {@code implicitDependency} if it is not already in the list of libraries.
+     *
+     * @param pkg the {@link PackageParser.Package} to update.
+     * @param existingLibrary the existing library.
+     * @param implicitDependency the implicit dependency to add
+     */
+    void prefixImplicitDependency(PackageParser.Package pkg, String existingLibrary,
+            String implicitDependency) {
+        ArrayList<String> usesLibraries = pkg.usesLibraries;
+        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+        if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
+            if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
+                prefix(usesLibraries, implicitDependency);
+            } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
+                prefix(usesOptionalLibraries, implicitDependency);
+            }
+
+            pkg.usesLibraries = usesLibraries;
+            pkg.usesOptionalLibraries = usesOptionalLibraries;
+        }
+    }
+
+    void prefixRequiredLibrary(PackageParser.Package pkg, String libraryName) {
+        ArrayList<String> usesLibraries = pkg.usesLibraries;
+        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+        boolean alreadyPresent = isLibraryPresent(
+                usesLibraries, usesOptionalLibraries, libraryName);
+        if (!alreadyPresent) {
+            usesLibraries = prefix(usesLibraries, libraryName);
+
+            pkg.usesLibraries = usesLibraries;
+        }
+    }
 }
diff --git a/android/content/pm/PackageUserState.java b/android/content/pm/PackageUserState.java
index 293beb2..f7b6e09 100644
--- a/android/content/pm/PackageUserState.java
+++ b/android/content/pm/PackageUserState.java
@@ -27,6 +27,8 @@
 import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
 import android.util.ArraySet;
 
 import com.android.internal.util.ArrayUtils;
@@ -44,6 +46,9 @@
     public boolean notLaunched;
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
+    public String suspendingPackage;
+    public PersistableBundle suspendedAppExtras;
+    public PersistableBundle suspendedLauncherExtras;
     public boolean instantApp;
     public boolean virtualPreload;
     public int enabled;
@@ -76,6 +81,9 @@
         notLaunched = o.notLaunched;
         hidden = o.hidden;
         suspended = o.suspended;
+        suspendingPackage = o.suspendingPackage;
+        suspendedAppExtras = o.suspendedAppExtras;
+        suspendedLauncherExtras = o.suspendedLauncherExtras;
         instantApp = o.instantApp;
         virtualPreload = o.virtualPreload;
         enabled = o.enabled;
@@ -195,6 +203,20 @@
         if (suspended != oldState.suspended) {
             return false;
         }
+        if (suspended) {
+            if (suspendingPackage == null
+                    || !suspendingPackage.equals(oldState.suspendingPackage)) {
+                return false;
+            }
+            if (!BaseBundle.kindofEquals(suspendedAppExtras,
+                    oldState.suspendedAppExtras)) {
+                return false;
+            }
+            if (!BaseBundle.kindofEquals(suspendedLauncherExtras,
+                    oldState.suspendedLauncherExtras)) {
+                return false;
+            }
+        }
         if (instantApp != oldState.instantApp) {
             return false;
         }
diff --git a/android/content/pm/PermissionInfo.java b/android/content/pm/PermissionInfo.java
index 21bd7f0..938409a 100644
--- a/android/content/pm/PermissionInfo.java
+++ b/android/content/pm/PermissionInfo.java
@@ -16,12 +16,16 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Information you can retrieve about a particular security permission
  * known to the system.  This corresponds to information collected from the
@@ -56,6 +60,16 @@
     @Deprecated
     public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
 
+    /** @hide */
+    @IntDef(flag = false, prefix = { "PROTECTION_" }, value = {
+            PROTECTION_NORMAL,
+            PROTECTION_DANGEROUS,
+            PROTECTION_SIGNATURE,
+            PROTECTION_SIGNATURE_OR_SYSTEM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Protection {}
+
     /**
      * Additional flag for {@link #protectionLevel}, corresponding
      * to the <code>privileged</code> value of
@@ -155,30 +169,71 @@
     public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
 
     /**
-     * Mask for {@link #protectionLevel}: the basic protection type.
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>text_classifier</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
      */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
+            PROTECTION_FLAG_PRIVILEGED,
+            PROTECTION_FLAG_SYSTEM,
+            PROTECTION_FLAG_DEVELOPMENT,
+            PROTECTION_FLAG_APPOP,
+            PROTECTION_FLAG_PRE23,
+            PROTECTION_FLAG_INSTALLER,
+            PROTECTION_FLAG_VERIFIER,
+            PROTECTION_FLAG_PREINSTALLED,
+            PROTECTION_FLAG_SETUP,
+            PROTECTION_FLAG_INSTANT,
+            PROTECTION_FLAG_RUNTIME_ONLY,
+            PROTECTION_FLAG_OEM,
+            PROTECTION_FLAG_VENDOR_PRIVILEGED,
+            PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtectionFlags {}
+
+    /**
+     * Mask for {@link #protectionLevel}: the basic protection type.
+     *
+     * @deprecated Use #getProtection() instead.
+     */
+    @Deprecated
     public static final int PROTECTION_MASK_BASE = 0xf;
 
     /**
      * Mask for {@link #protectionLevel}: additional flag bits.
+     *
+     * @deprecated Use #getProtectionFlags() instead.
      */
+    @Deprecated
     public static final int PROTECTION_MASK_FLAGS = 0xfff0;
 
     /**
      * The level of access this permission is protecting, as per
      * {@link android.R.attr#protectionLevel}. Consists of
-     * a base permission type and zero or more flags:
+     * a base permission type and zero or more flags. Use the following functions
+     * to extract them.
      *
      * <pre>
-     * int basePermissionType = protectionLevel & {@link #PROTECTION_MASK_BASE};
-     * int permissionFlags = protectionLevel & {@link #PROTECTION_MASK_FLAGS};
+     * int basePermissionType = permissionInfo.getProtection();
+     * int permissionFlags = permissionInfo.getProtectionFlags();
      * </pre>
      *
      * <p></p>Base permission types are {@link #PROTECTION_NORMAL},
      * {@link #PROTECTION_DANGEROUS}, {@link #PROTECTION_SIGNATURE}
      * and the deprecated {@link #PROTECTION_SIGNATURE_OR_SYSTEM}.
      * Flags are listed under {@link android.R.attr#protectionLevel}.
+     *
+     * @deprecated Use #getProtection() and #getProtectionFlags() instead.
      */
+    @Deprecated
     public int protectionLevel;
 
     /**
@@ -304,6 +359,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
             protLevel += "|vendorPrivileged";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
+            protLevel += "|textClassifier";
+        }
         return protLevel;
     }
 
@@ -344,6 +402,22 @@
         return null;
     }
 
+    /**
+     * Return the base permission type.
+     */
+    @Protection
+    public int getProtection() {
+        return protectionLevel & PROTECTION_MASK_BASE;
+    }
+
+    /**
+     * Return the additional flags in {@link #protectionLevel}.
+     */
+    @ProtectionFlags
+    public int getProtectionFlags() {
+        return protectionLevel & ~PROTECTION_MASK_BASE;
+    }
+
     @Override
     public String toString() {
         return "PermissionInfo{"
diff --git a/android/content/pm/ResolveInfo.java b/android/content/pm/ResolveInfo.java
index 3f63d80..fc2eba2 100644
--- a/android/content/pm/ResolveInfo.java
+++ b/android/content/pm/ResolveInfo.java
@@ -277,7 +277,7 @@
             dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
         }
         if (dr != null) {
-            return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId()));
+            return pm.getUserBadgedIcon(dr, new UserHandle(pm.getUserId()));
         }
         return ci.loadIcon(pm);
     }
diff --git a/android/content/pm/SharedLibraryNames.java b/android/content/pm/SharedLibraryNames.java
new file mode 100644
index 0000000..83e8663
--- /dev/null
+++ b/android/content/pm/SharedLibraryNames.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+/**
+ * A set of shared library names
+ *
+ * @hide
+ */
+public class SharedLibraryNames {
+
+    static final String ANDROID_TEST_BASE = "android.test.base";
+
+    static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+    static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+    static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+}
diff --git a/android/content/pm/ShortcutManager.java b/android/content/pm/ShortcutManager.java
index 30222b7..25e0ccd 100644
--- a/android/content/pm/ShortcutManager.java
+++ b/android/content/pm/ShortcutManager.java
@@ -770,6 +770,6 @@
     /** @hide injection point */
     @VisibleForTesting
     protected int injectMyUserId() {
-        return UserHandle.myUserId();
+        return mContext.getUserId();
     }
 }
diff --git a/android/content/pm/Signature.java b/android/content/pm/Signature.java
index fdc54ae..a2a14ed 100644
--- a/android/content/pm/Signature.java
+++ b/android/content/pm/Signature.java
@@ -285,6 +285,29 @@
     }
 
     /**
+     * Test if given {@link Signature} objects are effectively equal. In rare
+     * cases, certificates can have slightly malformed encoding which causes
+     * exact-byte checks to fail.
+     * <p>
+     * To identify effective equality, we bounce the certificates through an
+     * decode/encode pass before doing the exact-byte check. To reduce attack
+     * surface area, we only allow a byte size delta of a few bytes.
+     *
+     * @throws CertificateException if the before/after length differs
+     *             substantially, usually a signal of something fishy going on.
+     * @hide
+     */
+    public static boolean areEffectiveMatch(Signature a, Signature b)
+            throws CertificateException {
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+        final Signature aPrime = bounce(cf, a);
+        final Signature bPrime = bounce(cf, b);
+
+        return aPrime.equals(bPrime);
+    }
+
+    /**
      * Bounce the given {@link Signature} through a decode/encode cycle.
      *
      * @throws CertificateException if the before/after length differs
diff --git a/android/content/pm/VerifierDeviceIdentity.java b/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6a..90be6f3 100644
--- a/android/content/pm/VerifierDeviceIdentity.java
+++ b/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.UnsupportedEncodingException;
 import java.security.SecureRandom;
 import java.util.Random;
@@ -86,6 +88,7 @@
      * @return verifier device identity based on the input from the provided
      *         random number generator
      */
+    @VisibleForTesting
     static VerifierDeviceIdentity generate(Random rng) {
         long identity = rng.nextLong();
         return new VerifierDeviceIdentity(identity);
diff --git a/android/content/pm/dex/ArtManager.java b/android/content/pm/dex/ArtManager.java
index aa9c46e..4129398 100644
--- a/android/content/pm/dex/ArtManager.java
+++ b/android/content/pm/dex/ArtManager.java
@@ -16,16 +16,22 @@
 
 package android.content.pm.dex;
 
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
 /**
  * Class for retrieving various kinds of information related to the runtime artifacts of
  * packages that are currently installed on the device.
@@ -43,6 +49,20 @@
     /** The snapshot failed because of an internal error (e.g. error during opening profiles). */
     public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
 
+    /** Constant used for applications profiles. */
+    public static final int PROFILE_APPS = 0;
+    /** Constant used for the boot image profile. */
+    public static final int PROFILE_BOOT_IMAGE = 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PROFILE_" }, value = {
+            PROFILE_APPS,
+            PROFILE_BOOT_IMAGE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProfileType {}
+
+
     private IArtManager mArtManager;
 
     /**
@@ -53,41 +73,59 @@
     }
 
     /**
-     * Snapshots the runtime profile for an apk belonging to the package {@code packageName}.
-     * The apk is identified by {@code codePath}. The calling process must have
-     * {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     * Snapshots a runtime profile according to the {@code profileType} parameter.
      *
-     * The result will be posted on {@code handler} using the given {@code callback}.
-     * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}.
+     * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot
+     * the profile for for an apk belonging to the package {@code packageName}.
+     * The apk is identified by {@code codePath}.
      *
-     * @param packageName the target package name
-     * @param codePath the code path for which the profile should be retrieved
+     * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot
+     * the profile for the boot image. In this case {@code codePath can be null}. The parameters
+     * {@code packageName} and {@code codePath} are ignored.
+     *u
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * The result will be posted on the {@code executor} using the given {@code callback}.
+     * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
+     *
+     * This method will throw {@link IllegalStateException} if
+     * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
+     * {@code profileType}.
+     *
+     * @param profileType the type of profile that should be snapshot (boot image or app)
+     * @param packageName the target package name or null if the target is the boot image
+     * @param codePath the code path for which the profile should be retrieved or null if
+     *                 the target is the boot image
      * @param callback the callback which should be used for the result
-     * @param handler the handler which should be used to post the result
+     * @param executor the executor which should be used to post the result
      */
     @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
-    public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath,
-            @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) {
+    public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
+            @Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
+            @NonNull SnapshotRuntimeProfileCallback callback) {
         Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
 
         SnapshotRuntimeProfileCallbackDelegate delegate =
-                new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper());
+                new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
         try {
-            mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate);
+            mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
     }
 
     /**
-     * Returns true if runtime profiles are enabled, false otherwise.
+     * Returns true if runtime profiles are enabled for the given type, false otherwise.
      *
      * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * @param profileType can be either {@link ArtManager#PROFILE_APPS}
+     *                    or {@link ArtManager#PROFILE_BOOT_IMAGE}
      */
     @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
-    public boolean isRuntimeProfilingEnabled() {
+    public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
         try {
-            return mArtManager.isRuntimeProfilingEnabled();
+            return mArtManager.isRuntimeProfilingEnabled(profileType);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
@@ -116,41 +154,24 @@
     }
 
     private static class SnapshotRuntimeProfileCallbackDelegate
-            extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub
-            implements Handler.Callback {
-        private static final int MSG_SNAPSHOT_OK = 1;
-        private static final int MSG_ERROR = 2;
+            extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub {
         private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
-        private final Handler mHandler;
+        private final Executor mExecutor;
 
         private SnapshotRuntimeProfileCallbackDelegate(
-                ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) {
+                ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) {
             mCallback = callback;
-            mHandler = new Handler(looper, this);
+            mExecutor = executor;
         }
 
         @Override
-        public void onSuccess(ParcelFileDescriptor profileReadFd) {
-            mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget();
+        public void onSuccess(final ParcelFileDescriptor profileReadFd) {
+            mExecutor.execute(() -> mCallback.onSuccess(profileReadFd));
         }
 
         @Override
         public void onError(int errCode) {
-            mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget();
-        }
-
-        @Override
-        public boolean handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SNAPSHOT_OK:
-                    mCallback.onSuccess((ParcelFileDescriptor) msg.obj);
-                    break;
-                case MSG_ERROR:
-                    mCallback.onError(msg.arg1);
-                    break;
-                default: return false;
-            }
-            return true;
+            mExecutor.execute(() -> mCallback.onError(errCode));
         }
     }
 
@@ -163,4 +184,27 @@
     public static String getProfileName(String splitName) {
         return splitName == null ? "primary.prof" : splitName + ".split.prof";
     }
+
+    /**
+     * Return the path to the current profile corresponding to given package and split.
+     *
+     * @hide
+     */
+    public static String getCurrentProfilePath(String packageName, int userId, String splitName) {
+        File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName);
+        return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
+    }
+
+    /**
+     * Return the snapshot profile file for the given package and profile name.
+     *
+     * KEEP in sync with installd dexopt.cpp.
+     * TODO(calin): inject the snapshot profile name from PM to avoid the dependency.
+     *
+     * @hide
+     */
+    public static File getProfileSnapshotFileForName(String packageName, String profileName) {
+        File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
+        return new File(profileDir, profileName  + ".snapshot");
+    }
 }
diff --git a/android/content/pm/dex/ArtManagerInternal.java b/android/content/pm/dex/ArtManagerInternal.java
new file mode 100644
index 0000000..62ab9e0
--- /dev/null
+++ b/android/content/pm/dex/ArtManagerInternal.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Art manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ArtManagerInternal {
+
+    /**
+     * Return optimization information about the application {@code info} when
+     * in executes using the specified {@code abi}.
+     */
+    public abstract PackageOptimizationInfo getPackageOptimizationInfo(
+            ApplicationInfo info, String abi);
+}
diff --git a/android/content/pm/dex/PackageOptimizationInfo.java b/android/content/pm/dex/PackageOptimizationInfo.java
new file mode 100644
index 0000000..7e7d29e
--- /dev/null
+++ b/android/content/pm/dex/PackageOptimizationInfo.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+/**
+ * Encapsulates information about the optimizations performed on a package.
+ *
+ * @hide
+ */
+public class PackageOptimizationInfo {
+    private final int mCompilationFilter;
+    private final int mCompilationReason;
+
+    public PackageOptimizationInfo(int compilerFilter, int compilationReason) {
+        this.mCompilationReason = compilationReason;
+        this.mCompilationFilter = compilerFilter;
+    }
+
+    public int getCompilationReason() {
+        return mCompilationReason;
+    }
+
+    public int getCompilationFilter() {
+        return mCompilationFilter;
+    }
+
+    /**
+     * Create a default optimization info object for the case when we have no information.
+     */
+    public static PackageOptimizationInfo createWithNoInfo() {
+        return new PackageOptimizationInfo(-1, -1);
+    }
+}
diff --git a/android/content/pm/permission/RuntimePermissionPresenter.java b/android/content/pm/permission/RuntimePermissionPresenter.java
index 02d0a6d..79bc9a3 100644
--- a/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -274,6 +274,7 @@
             }
         }
 
+        @GuardedBy("mLock")
         private void scheduleNextMessageIfNeededLocked() {
             if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
                 Message nextMessage = mPendingWork.remove(0);
diff --git a/android/content/pm/split/DefaultSplitAssetLoader.java b/android/content/pm/split/DefaultSplitAssetLoader.java
index 99eb470..9e3a8f4 100644
--- a/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,10 +15,13 @@
  */
 package android.content.pm.split;
 
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.os.Build;
 
@@ -26,6 +29,8 @@
 
 import libcore.io.IoUtils;
 
+import java.io.IOException;
+
 /**
  * Loads the base and split APKs into a single AssetManager.
  * @hide
@@ -33,68 +38,66 @@
 public class DefaultSplitAssetLoader implements SplitAssetLoader {
     private final String mBaseCodePath;
     private final String[] mSplitCodePaths;
-    private final int mFlags;
-
+    private final @ParseFlags int mFlags;
     private AssetManager mCachedAssetManager;
 
-    public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) {
+    public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
         mBaseCodePath = pkg.baseCodePath;
         mSplitCodePaths = pkg.splitCodePaths;
         mFlags = flags;
     }
 
-    private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
-            throws PackageParser.PackageParserException {
-        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) {
-            throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                    "Invalid package file: " + apkPath);
+    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+            throws PackageParserException {
+        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + path);
         }
 
-        if (assets.addAssetPath(apkPath) == 0) {
-            throw new PackageParser.PackageParserException(
-                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                    "Failed adding asset path: " + apkPath);
+        try {
+            return ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+                    "Failed to load APK at path " + path, e);
         }
     }
 
     @Override
-    public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+    public AssetManager getBaseAssetManager() throws PackageParserException {
         if (mCachedAssetManager != null) {
             return mCachedAssetManager;
         }
 
-        AssetManager assets = new AssetManager();
-        try {
-            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                    Build.VERSION.RESOURCES_SDK_INT);
-            loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
+        ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+                ? mSplitCodePaths.length : 0) + 1];
 
-            if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
-                for (String apkPath : mSplitCodePaths) {
-                    loadApkIntoAssetManager(assets, apkPath, mFlags);
-                }
-            }
+        // Load the base.
+        int splitIdx = 0;
+        apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
 
-            mCachedAssetManager = assets;
-            assets = null;
-            return mCachedAssetManager;
-        } finally {
-            if (assets != null) {
-                IoUtils.closeQuietly(assets);
+        // Load any splits.
+        if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+            for (String apkPath : mSplitCodePaths) {
+                apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
             }
         }
+
+        AssetManager assets = new AssetManager();
+        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+        mCachedAssetManager = assets;
+        return mCachedAssetManager;
     }
 
     @Override
-    public AssetManager getSplitAssetManager(int splitIdx)
-            throws PackageParser.PackageParserException {
+    public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
         return getBaseAssetManager();
     }
 
     @Override
     public void close() throws Exception {
-        if (mCachedAssetManager != null) {
-            IoUtils.closeQuietly(mCachedAssetManager);
-        }
+        IoUtils.closeQuietly(mCachedAssetManager);
     }
 }
diff --git a/android/content/pm/split/SplitAssetDependencyLoader.java b/android/content/pm/split/SplitAssetDependencyLoader.java
index 16023f0..58eaabf 100644
--- a/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,17 +15,21 @@
  */
 package android.content.pm.split;
 
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 
 import android.annotation.NonNull;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.os.Build;
 import android.util.SparseArray;
 
 import libcore.io.IoUtils;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 
@@ -34,17 +38,15 @@
  * is to be used when an application opts-in to isolated split loading.
  * @hide
  */
-public class SplitAssetDependencyLoader
-        extends SplitDependencyLoader<PackageParser.PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
         implements SplitAssetLoader {
     private final String[] mSplitPaths;
-    private final int mFlags;
-
-    private String[][] mCachedPaths;
-    private AssetManager[] mCachedAssetManagers;
+    private final @ParseFlags int mFlags;
+    private final ApkAssets[][] mCachedSplitApks;
+    private final AssetManager[] mCachedAssetManagers;
 
     public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
-            SparseArray<int[]> dependencies, int flags) {
+            SparseArray<int[]> dependencies, @ParseFlags int flags) {
         super(dependencies);
 
         // The base is inserted into index 0, so we need to shift all the splits by 1.
@@ -53,7 +55,7 @@
         System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
 
         mFlags = flags;
-        mCachedPaths = new String[mSplitPaths.length][];
+        mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
         mCachedAssetManagers = new AssetManager[mSplitPaths.length];
     }
 
@@ -62,58 +64,60 @@
         return mCachedAssetManagers[splitIdx] != null;
     }
 
-    private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
-            throws PackageParser.PackageParserException {
-        final AssetManager assets = new AssetManager();
-        try {
-            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                    Build.VERSION.RESOURCES_SDK_INT);
-
-            for (String assetPath : assetPaths) {
-                if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
-                        !PackageParser.isApkPath(assetPath)) {
-                    throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                            "Invalid package file: " + assetPath);
-                }
-
-                if (assets.addAssetPath(assetPath) == 0) {
-                    throw new PackageParser.PackageParserException(
-                            INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                            "Failed adding asset path: " + assetPath);
-                }
-            }
-            return assets;
-        } catch (Throwable e) {
-            IoUtils.closeQuietly(assets);
-            throw e;
+    private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+            throws PackageParserException {
+        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                    "Invalid package file: " + path);
         }
+
+        try {
+            return ApkAssets.loadFromPath(path);
+        } catch (IOException e) {
+            throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+                    "Failed to load APK at path " + path, e);
+        }
+    }
+
+    private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+        final AssetManager assets = new AssetManager();
+        assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                Build.VERSION.RESOURCES_SDK_INT);
+        assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+        return assets;
     }
 
     @Override
     protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
-            int parentSplitIdx) throws PackageParser.PackageParserException {
-        final ArrayList<String> assetPaths = new ArrayList<>();
+            int parentSplitIdx) throws PackageParserException {
+        final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+        // Include parent ApkAssets.
         if (parentSplitIdx >= 0) {
-            Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
+            Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
         }
 
-        assetPaths.add(mSplitPaths[splitIdx]);
+        // Include this ApkAssets.
+        assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+        // Load and include all config splits for this feature.
         for (int configSplitIdx : configSplitIndices) {
-            assetPaths.add(mSplitPaths[configSplitIdx]);
+            assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
         }
-        mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
-        mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
-                mFlags);
+
+        // Cache the results.
+        mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+        mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
     }
 
     @Override
-    public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+    public AssetManager getBaseAssetManager() throws PackageParserException {
         loadDependenciesForSplit(0);
         return mCachedAssetManagers[0];
     }
 
     @Override
-    public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
+    public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
         // Since we insert the base at position 0, and PackageParser keeps splits separate from
         // the base, we need to adjust the index.
         loadDependenciesForSplit(idx + 1);
diff --git a/android/content/res/ApkAssets.java b/android/content/res/ApkAssets.java
new file mode 100644
index 0000000..9de8be3
--- /dev/null
+++ b/android/content/res/ApkAssets.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.content.res;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * The loaded, immutable, in-memory representation of an APK.
+ *
+ * The main implementation is native C++ and there is very little API surface exposed here. The APK
+ * is mainly accessed via {@link AssetManager}.
+ *
+ * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
+ * making the creation of AssetManagers very cheap.
+ * @hide
+ */
+public final class ApkAssets {
+    @GuardedBy("this") private final long mNativePtr;
+    @GuardedBy("this") private StringBlock mStringBlock;
+
+    /**
+     * Creates a new ApkAssets instance from the given path on disk.
+     *
+     * @param path The path to an APK on disk.
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
+        return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given path on disk.
+     *
+     * @param path The path to an APK on disk.
+     * @param system When true, the APK is loaded as a system APK (framework).
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
+            throws IOException {
+        return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given path on disk.
+     *
+     * @param path The path to an APK on disk.
+     * @param system When true, the APK is loaded as a system APK (framework).
+     * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
+     *                           loaded as a shared library.
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
+            boolean forceSharedLibrary) throws IOException {
+        return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
+     *
+     * Performs a dup of the underlying fd, so you must take care of still closing
+     * the FileDescriptor yourself (and can do that whenever you want).
+     *
+     * @param fd The FileDescriptor of an open, readable APK.
+     * @param friendlyName The friendly name used to identify this ApkAssets when logging.
+     * @param system When true, the APK is loaded as a system APK (framework).
+     * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
+     *                           loaded as a shared library.
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
+            @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
+            throws IOException {
+        return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
+    }
+
+    /**
+     * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
+     * is encoded within the IDMAP.
+     *
+     * @param idmapPath Path to the IDMAP of an overlay APK.
+     * @param system When true, the APK is loaded as a system APK (framework).
+     * @return a new instance of ApkAssets.
+     * @throws IOException if a disk I/O error or parsing error occurred.
+     */
+    public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
+            throws IOException {
+        return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
+    }
+
+    private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+            throws IOException {
+        Preconditions.checkNotNull(path, "path");
+        mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
+        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+    }
+
+    private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
+            boolean forceSharedLib) throws IOException {
+        Preconditions.checkNotNull(fd, "fd");
+        Preconditions.checkNotNull(friendlyName, "friendlyName");
+        mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
+        mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
+    }
+
+    public @NonNull String getAssetPath() {
+        synchronized (this) {
+            return nativeGetAssetPath(mNativePtr);
+        }
+    }
+
+    CharSequence getStringFromPool(int idx) {
+        synchronized (this) {
+            return mStringBlock.get(idx);
+        }
+    }
+
+    /**
+     * Retrieve a parser for a compiled XML file. This is associated with a single APK and
+     * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
+     * dynamically assigned runtime package IDs.
+     *
+     * @param fileName The path to the file within the APK.
+     * @return An XmlResourceParser.
+     * @throws IOException if the file was not found or an error occurred retrieving it.
+     */
+    public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
+        Preconditions.checkNotNull(fileName, "fileName");
+        synchronized (this) {
+            long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
+            try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
+                XmlResourceParser parser = block.newParser();
+                // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
+                // which makes newParser always return non-null. But let's be paranoid.
+                if (parser == null) {
+                    throw new AssertionError("block.newParser() returned a null parser");
+                }
+                return parser;
+            }
+        }
+    }
+
+    /**
+     * Returns false if the underlying APK was changed since this ApkAssets was loaded.
+     */
+    public boolean isUpToDate() {
+        synchronized (this) {
+            return nativeIsUpToDate(mNativePtr);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ApkAssets{path=" + getAssetPath() + "}";
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        nativeDestroy(mNativePtr);
+    }
+
+    private static native long nativeLoad(
+            @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+            throws IOException;
+    private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
+            @NonNull String friendlyName, boolean system, boolean forceSharedLib)
+            throws IOException;
+    private static native void nativeDestroy(long ptr);
+    private static native @NonNull String nativeGetAssetPath(long ptr);
+    private static native long nativeGetStringBlock(long ptr);
+    private static native boolean nativeIsUpToDate(long ptr);
+    private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
+}
diff --git a/android/content/res/AssetManager.java b/android/content/res/AssetManager.java
index 7866560..2895342 100644
--- a/android/content/res/AssetManager.java
+++ b/android/content/res/AssetManager.java
@@ -18,20 +18,33 @@
 
 import android.annotation.AnyRes;
 import android.annotation.ArrayRes;
+import android.annotation.AttrRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
+import android.annotation.StyleRes;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration.NativeConfig;
 import android.os.ParcelFileDescriptor;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.TypedValue;
 
-import java.io.FileDescriptor;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import libcore.io.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.channels.FileLock;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 
 /**
@@ -42,7 +55,20 @@
  * bytes.
  */
 public final class AssetManager implements AutoCloseable {
-    /* modes used when opening an asset */
+    private static final String TAG = "AssetManager";
+    private static final boolean DEBUG_REFS = false;
+
+    private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
+
+    private static final Object sSync = new Object();
+
+    private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0];
+
+    // Not private for LayoutLib's BridgeAssetManager.
+    @GuardedBy("sSync") static AssetManager sSystem = null;
+
+    @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0];
+    @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet;
 
     /**
      * Mode for {@link #open(String, int)}: no specific information about how
@@ -65,146 +91,358 @@
      */
     public static final int ACCESS_BUFFER = 3;
 
-    private static final String TAG = "AssetManager";
-    private static final boolean localLOGV = false || false;
-    
-    private static final boolean DEBUG_REFS = false;
-    
-    private static final Object sSync = new Object();
-    /*package*/ static AssetManager sSystem = null;
+    @GuardedBy("this") private final TypedValue mValue = new TypedValue();
+    @GuardedBy("this") private final long[] mOffsets = new long[2];
 
-    private final TypedValue mValue = new TypedValue();
-    private final long[] mOffsets = new long[2];
-    
-    // For communication with native code.
-    private long mObject;
+    // Pointer to native implementation, stuffed inside a long.
+    @GuardedBy("this") private long mObject;
 
-    private StringBlock mStringBlocks[] = null;
-    
-    private int mNumRefs = 1;
-    private boolean mOpen = true;
-    private HashMap<Long, RuntimeException> mRefStacks;
- 
+    // The loaded asset paths.
+    @GuardedBy("this") private ApkAssets[] mApkAssets;
+
+    // Debug/reference counting implementation.
+    @GuardedBy("this") private boolean mOpen = true;
+    @GuardedBy("this") private int mNumRefs = 1;
+    @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+
+    /**
+     * A Builder class that helps create an AssetManager with only a single invocation of
+     * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
+     * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined
+     * with the user's call to add additional ApkAssets, results in multiple calls to
+     * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}.
+     * @hide
+     */
+    public static class Builder {
+        private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
+
+        public Builder addApkAssets(ApkAssets apkAssets) {
+            mUserApkAssets.add(apkAssets);
+            return this;
+        }
+
+        public AssetManager build() {
+            // Retrieving the system ApkAssets forces their creation as well.
+            final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
+
+            final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size();
+            final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
+
+            System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
+
+            final int userApkAssetCount = mUserApkAssets.size();
+            for (int i = 0; i < userApkAssetCount; i++) {
+                apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
+            }
+
+            // Calling this constructor prevents creation of system ApkAssets, which we took care
+            // of in this Builder.
+            final AssetManager assetManager = new AssetManager(false /*sentinel*/);
+            assetManager.mApkAssets = apkAssets;
+            AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
+                    false /*invalidateCaches*/);
+            return assetManager;
+        }
+    }
+
     /**
      * Create a new AssetManager containing only the basic system assets.
      * Applications will not generally use this method, instead retrieving the
      * appropriate asset manager with {@link Resources#getAssets}.    Not for
      * use by applications.
-     * {@hide}
+     * @hide
      */
     public AssetManager() {
-        synchronized (this) {
-            if (DEBUG_REFS) {
-                mNumRefs = 0;
-                incRefsLocked(this.hashCode());
-            }
-            init(false);
-            if (localLOGV) Log.v(TAG, "New asset manager: " + this);
-            ensureSystemAssets();
+        final ApkAssets[] assets;
+        synchronized (sSync) {
+            createSystemAssetsInZygoteLocked();
+            assets = sSystemApkAssets;
+        }
+
+        mObject = nativeCreate();
+        if (DEBUG_REFS) {
+            mNumRefs = 0;
+            incRefsLocked(hashCode());
+        }
+
+        // Always set the framework resources.
+        setApkAssets(assets, false /*invalidateCaches*/);
+    }
+
+    /**
+     * Private constructor that doesn't call ensureSystemAssets.
+     * Used for the creation of system assets.
+     */
+    @SuppressWarnings("unused")
+    private AssetManager(boolean sentinel) {
+        mObject = nativeCreate();
+        if (DEBUG_REFS) {
+            mNumRefs = 0;
+            incRefsLocked(hashCode());
         }
     }
 
-    private static void ensureSystemAssets() {
-        synchronized (sSync) {
-            if (sSystem == null) {
-                AssetManager system = new AssetManager(true);
-                system.makeStringBlocks(null);
-                sSystem = system;
-            }
+    /**
+     * This must be called from Zygote so that system assets are shared by all applications.
+     */
+    @GuardedBy("sSync")
+    private static void createSystemAssetsInZygoteLocked() {
+        if (sSystem != null) {
+            return;
+        }
+
+        // Make sure that all IDMAPs are up to date.
+        nativeVerifySystemIdmaps();
+
+        try {
+            final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+            apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
+            loadStaticRuntimeOverlays(apkAssets);
+
+            sSystemApkAssetsSet = new ArraySet<>(apkAssets);
+            sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
+            sSystem = new AssetManager(true /*sentinel*/);
+            sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
+        } catch (IOException e) {
+            throw new IllegalStateException("Failed to create system AssetManager", e);
         }
     }
-    
-    private AssetManager(boolean isSystem) {
-        if (DEBUG_REFS) {
-            synchronized (this) {
-                mNumRefs = 0;
-                incRefsLocked(this.hashCode());
-            }
+
+    /**
+     * Loads the static runtime overlays declared in /data/resource-cache/overlays.list.
+     * Throws an exception if the file is corrupt or if loading the APKs referenced by the file
+     * fails. Returns quietly if the overlays.list file doesn't exist.
+     * @param outApkAssets The list to fill with the loaded ApkAssets.
+     */
+    private static void loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets)
+            throws IOException {
+        final FileInputStream fis;
+        try {
+            fis = new FileInputStream("/data/resource-cache/overlays.list");
+        } catch (FileNotFoundException e) {
+            // We might not have any overlays, this is fine. We catch here since ApkAssets
+            // loading can also fail with the same exception, which we would want to propagate.
+            Log.i(TAG, "no overlays.list file found");
+            return;
         }
-        init(true);
-        if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+
+        try {
+            // Acquire a lock so that any idmap scanning doesn't impact the current set.
+            // The order of this try-with-resources block matters. We must release the lock, and
+            // then close the file streams when exiting the block.
+            try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+                 final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) {
+                for (String line; (line = br.readLine()) != null; ) {
+                    final String idmapPath = line.split(" ")[1];
+                    outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+                }
+            }
+        } finally {
+            // When BufferedReader is closed above, FileInputStream is closed as well. But let's be
+            // paranoid.
+            IoUtils.closeQuietly(fis);
+        }
     }
 
     /**
      * Return a global shared asset manager that provides access to only
      * system assets (no application assets).
-     * {@hide}
+     * @hide
      */
     public static AssetManager getSystem() {
-        ensureSystemAssets();
-        return sSystem;
+        synchronized (sSync) {
+            createSystemAssetsInZygoteLocked();
+            return sSystem;
+        }
     }
 
     /**
      * Close this asset manager.
      */
+    @Override
     public void close() {
-        synchronized(this) {
-            //System.out.println("Release: num=" + mNumRefs
-            //                   + ", released=" + mReleased);
+        synchronized (this) {
+            if (!mOpen) {
+                return;
+            }
+
+            mOpen = false;
+            decRefsLocked(hashCode());
+        }
+    }
+
+    /**
+     * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
+     * family of methods.
+     *
+     * @param apkAssets The new set of paths.
+     * @param invalidateCaches Whether to invalidate any caches. This should almost always be true.
+     *                         Set this to false if you are appending new resources
+     *                         (not new configurations).
+     * @hide
+     */
+    public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
+        Preconditions.checkNotNull(apkAssets, "apkAssets");
+
+        ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length];
+
+        // Copy the system assets first.
+        System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
+
+        // Copy the given ApkAssets if they are not already in the system list.
+        int newLength = sSystemApkAssets.length;
+        for (ApkAssets apkAsset : apkAssets) {
+            if (!sSystemApkAssetsSet.contains(apkAsset)) {
+                newApkAssets[newLength++] = apkAsset;
+            }
+        }
+
+        // Truncate if necessary.
+        if (newLength != newApkAssets.length) {
+            newApkAssets = Arrays.copyOf(newApkAssets, newLength);
+        }
+
+        synchronized (this) {
+            ensureOpenLocked();
+            mApkAssets = newApkAssets;
+            nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+            if (invalidateCaches) {
+                // Invalidate all caches.
+                invalidateCachesLocked(-1);
+            }
+        }
+    }
+
+    /**
+     * Invalidates the caches in this AssetManager according to the bitmask `diff`.
+     *
+     * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
+     * @see ActivityInfo.Config
+     */
+    private void invalidateCachesLocked(int diff) {
+        // TODO(adamlesinski): Currently there are no caches to invalidate in Java code.
+    }
+
+    /**
+     * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this
+     * returns a 0-length array.
+     * @hide
+     */
+    public @NonNull ApkAssets[] getApkAssets() {
+        synchronized (this) {
             if (mOpen) {
-                mOpen = false;
-                decRefsLocked(this.hashCode());
+                return mApkAssets;
             }
         }
+        return sEmptyApkAssets;
     }
 
     /**
-     * Retrieves the string value associated with a particular resource
-     * identifier for the current configuration.
-     *
-     * @param resId the resource identifier to load
-     * @return the string value, or {@code null}
+     * Returns a cookie for use with the other APIs of AssetManager.
+     * @return 0 if the path was not found, otherwise a positive integer cookie representing
+     * this path in the AssetManager.
+     * @hide
      */
-    @Nullable
-    final CharSequence getResourceText(@StringRes int resId) {
+    public int findCookieForPath(@NonNull String path) {
+        Preconditions.checkNotNull(path, "path");
         synchronized (this) {
-            final TypedValue outValue = mValue;
-            if (getResourceValue(resId, 0, outValue, true)) {
-                return outValue.coerceToString();
+            ensureValidLocked();
+            final int count = mApkAssets.length;
+            for (int i = 0; i < count; i++) {
+                if (path.equals(mApkAssets[i].getAssetPath())) {
+                    return i + 1;
+                }
             }
-            return null;
         }
+        return 0;
     }
 
     /**
-     * Retrieves the string value associated with a particular resource
-     * identifier for the current configuration.
-     *
-     * @param resId the resource identifier to load
-     * @param bagEntryId
-     * @return the string value, or {@code null}
+     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+     * @hide
      */
-    @Nullable
-    final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+    @Deprecated
+    public int addAssetPath(String path) {
+        return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
+    }
+
+    /**
+     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+     * @hide
+     */
+    @Deprecated
+    public int addAssetPathAsSharedLibrary(String path) {
+        return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
+    }
+
+    /**
+     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
+     * @hide
+     */
+    @Deprecated
+    public int addOverlayPath(String path) {
+        return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
+    }
+
+    private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
+        Preconditions.checkNotNull(path, "path");
         synchronized (this) {
-            final TypedValue outValue = mValue;
-            final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
-            if (block < 0) {
-                return null;
+            ensureOpenLocked();
+            final int count = mApkAssets.length;
+
+            // See if we already have it loaded.
+            for (int i = 0; i < count; i++) {
+                if (mApkAssets[i].getAssetPath().equals(path)) {
+                    return i + 1;
+                }
             }
 
-            // Convert the changing configurations flags populated by native code.
-            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
-                    outValue.changingConfigurations);
-
-            if (outValue.type == TypedValue.TYPE_STRING) {
-                return mStringBlocks[block].get(outValue.data);
+            final ApkAssets assets;
+            try {
+                if (overlay) {
+                    // TODO(b/70343104): This hardcoded path will be removed once
+                    // addAssetPathInternal is deleted.
+                    final String idmapPath = "/data/resource-cache/"
+                            + path.substring(1).replace('/', '@')
+                            + "@idmap";
+                    assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
+                } else {
+                    assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib);
+                }
+            } catch (IOException e) {
+                return 0;
             }
-            return outValue.coerceToString();
+
+            mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
+            mApkAssets[count] = assets;
+            nativeSetApkAssets(mObject, mApkAssets, true);
+            invalidateCachesLocked(-1);
+            return count + 1;
         }
     }
 
     /**
-     * Retrieves the string array associated with a particular resource
-     * identifier for the current configuration.
-     *
-     * @param resId the resource identifier of the string array
-     * @return the string array, or {@code null}
+     * Ensures that the native implementation has not been destroyed.
+     * The AssetManager may have been closed, but references to it still exist
+     * and therefore the native implementation is not destroyed.
      */
-    @Nullable
-    final String[] getResourceStringArray(@ArrayRes int resId) {
-        return getArrayStringResource(resId);
+    @GuardedBy("this")
+    private void ensureValidLocked() {
+        if (mObject == 0) {
+            throw new RuntimeException("AssetManager has been destroyed");
+        }
+    }
+
+    /**
+     * Ensures that the AssetManager has not been explicitly closed. If this method passes,
+     * then this implies that ensureValidLocked() also passes.
+     */
+    @GuardedBy("this")
+    private void ensureOpenLocked() {
+        // If mOpen is true, this implies that mObject != 0.
+        if (!mOpen) {
+            throw new RuntimeException("AssetManager has been closed");
+        }
     }
 
     /**
@@ -219,11 +457,14 @@
      * @return {@code true} if the data was loaded into {@code outValue},
      *         {@code false} otherwise
      */
-    final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+    boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
             boolean resolveRefs) {
+        Preconditions.checkNotNull(outValue, "outValue");
         synchronized (this) {
-            final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
-            if (block < 0) {
+            ensureValidLocked();
+            final int cookie = nativeGetResourceValue(
+                    mObject, resId, (short) densityDpi, outValue, resolveRefs);
+            if (cookie <= 0) {
                 return false;
             }
 
@@ -232,38 +473,156 @@
                     outValue.changingConfigurations);
 
             if (outValue.type == TypedValue.TYPE_STRING) {
-                outValue.string = mStringBlocks[block].get(outValue.data);
+                outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
             }
             return true;
         }
     }
 
     /**
+     * Retrieves the string value associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @return the string value, or {@code null}
+     */
+    @Nullable CharSequence getResourceText(@StringRes int resId) {
+        synchronized (this) {
+            final TypedValue outValue = mValue;
+            if (getResourceValue(resId, 0, outValue, true)) {
+                return outValue.coerceToString();
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Retrieves the string value associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier to load
+     * @param bagEntryId the index into the bag to load
+     * @return the string value, or {@code null}
+     */
+    @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+        synchronized (this) {
+            ensureValidLocked();
+            final TypedValue outValue = mValue;
+            final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
+            if (cookie <= 0) {
+                return null;
+            }
+
+            // Convert the changing configurations flags populated by native code.
+            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+                    outValue.changingConfigurations);
+
+            if (outValue.type == TypedValue.TYPE_STRING) {
+                return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+            }
+            return outValue.coerceToString();
+        }
+    }
+
+    int getResourceArraySize(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceArraySize(mObject, resId);
+        }
+    }
+
+    /**
+     * Populates `outData` with array elements of `resId`. `outData` is normally
+     * used with
+     * {@link TypedArray}.
+     *
+     * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
+     * long,
+     * with the indices of the data representing the type, value, asset cookie,
+     * resource ID,
+     * configuration change mask, and density of the element.
+     *
+     * @param resId The resource ID of an array resource.
+     * @param outData The array to populate with data.
+     * @return The length of the array.
+     *
+     * @see TypedArray#STYLE_TYPE
+     * @see TypedArray#STYLE_DATA
+     * @see TypedArray#STYLE_ASSET_COOKIE
+     * @see TypedArray#STYLE_RESOURCE_ID
+     * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
+     * @see TypedArray#STYLE_DENSITY
+     */
+    int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
+        Preconditions.checkNotNull(outData, "outData");
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceArray(mObject, resId, outData);
+        }
+    }
+
+    /**
+     * Retrieves the string array associated with a particular resource
+     * identifier for the current configuration.
+     *
+     * @param resId the resource identifier of the string array
+     * @return the string array, or {@code null}
+     */
+    @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceStringArray(mObject, resId);
+        }
+    }
+
+    /**
      * Retrieve the text array associated with a particular resource
      * identifier.
      *
      * @param resId the resource id of the string array
      */
-    final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+    @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
         synchronized (this) {
-            final int[] rawInfoArray = getArrayStringInfo(resId);
+            ensureValidLocked();
+            final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
             if (rawInfoArray == null) {
                 return null;
             }
+
             final int rawInfoArrayLen = rawInfoArray.length;
             final int infoArrayLen = rawInfoArrayLen / 2;
-            int block;
-            int index;
             final CharSequence[] retArray = new CharSequence[infoArrayLen];
             for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
-                block = rawInfoArray[i];
-                index = rawInfoArray[i + 1];
-                retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+                int cookie = rawInfoArray[i];
+                int index = rawInfoArray[i + 1];
+                retArray[j] = (index >= 0 && cookie > 0)
+                        ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
             }
             return retArray;
         }
     }
 
+    @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceIntArray(mObject, resId);
+        }
+    }
+
+    /**
+     * Get the attributes for a style resource. These are the &lt;item&gt;
+     * elements in
+     * a &lt;style&gt; resource.
+     * @param resId The resource ID of the style
+     * @return An array of attribute IDs.
+     */
+    @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetStyleAttributes(mObject, resId);
+        }
+    }
+
     /**
      * Populates {@code outValue} with the data associated with a particular
      * resource identifier for the current configuration. Resolves theme
@@ -277,73 +636,88 @@
      * @return {@code true} if the data was loaded into {@code outValue},
      *         {@code false} otherwise
      */
-    final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+    boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
             boolean resolveRefs) {
-        final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs);
-        if (block < 0) {
-            return false;
-        }
-
-        // Convert the changing configurations flags populated by native code.
-        outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
-                outValue.changingConfigurations);
-
-        if (outValue.type == TypedValue.TYPE_STRING) {
-            final StringBlock[] blocks = ensureStringBlocks();
-            outValue.string = blocks[block].get(outValue.data);
-        }
-        return true;
-    }
-
-    /**
-     * Ensures the string blocks are loaded.
-     *
-     * @return the string blocks
-     */
-    @NonNull
-    final StringBlock[] ensureStringBlocks() {
+        Preconditions.checkNotNull(outValue, "outValue");
         synchronized (this) {
-            if (mStringBlocks == null) {
-                makeStringBlocks(sSystem.mStringBlocks);
+            ensureValidLocked();
+            final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
+                    resolveRefs);
+            if (cookie <= 0) {
+                return false;
             }
-            return mStringBlocks;
+
+            // Convert the changing configurations flags populated by native code.
+            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+                    outValue.changingConfigurations);
+
+            if (outValue.type == TypedValue.TYPE_STRING) {
+                outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+            }
+            return true;
         }
     }
 
-    /*package*/ final void makeStringBlocks(StringBlock[] seed) {
-        final int seedNum = (seed != null) ? seed.length : 0;
-        final int num = getStringBlockCount();
-        mStringBlocks = new StringBlock[num];
-        if (localLOGV) Log.v(TAG, "Making string blocks for " + this
-                + ": " + num);
-        for (int i=0; i<num; i++) {
-            if (i < seedNum) {
-                mStringBlocks[i] = seed[i];
-            } else {
-                mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
-            }
-        }
-    }
-
-    /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
+    void dumpTheme(long theme, int priority, String tag, String prefix) {
         synchronized (this) {
-            // Cookies map to string blocks starting at 1.
-            return mStringBlocks[cookie - 1].get(id);
+            ensureValidLocked();
+            nativeThemeDump(mObject, theme, priority, tag, prefix);
         }
     }
 
+    @Nullable String getResourceName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceName(mObject, resId);
+        }
+    }
+
+    @Nullable String getResourcePackageName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourcePackageName(mObject, resId);
+        }
+    }
+
+    @Nullable String getResourceTypeName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceTypeName(mObject, resId);
+        }
+    }
+
+    @Nullable String getResourceEntryName(@AnyRes int resId) {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetResourceEntryName(mObject, resId);
+        }
+    }
+
+    @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
+            @Nullable String defPackage) {
+        synchronized (this) {
+            ensureValidLocked();
+            // name is checked in JNI.
+            return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
+        }
+    }
+
+    CharSequence getPooledStringForCookie(int cookie, int id) {
+        // Cookies map to ApkAssets starting at 1.
+        return getApkAssets()[cookie - 1].getStringFromPool(id);
+    }
+
     /**
      * Open an asset using ACCESS_STREAMING mode.  This provides access to
      * files that have been bundled with an application as assets -- that is,
      * files placed in to the "assets" directory.
      * 
-     * @param fileName The name of the asset to open.  This name can be
-     *                 hierarchical.
+     * @param fileName The name of the asset to open.  This name can be hierarchical.
      * 
      * @see #open(String, int)
      * @see #list
      */
-    public final InputStream open(String fileName) throws IOException {
+    public @NonNull InputStream open(@NonNull String fileName) throws IOException {
         return open(fileName, ACCESS_STREAMING);
     }
 
@@ -353,8 +727,7 @@
      * with an application as assets -- that is, files placed in to the
      * "assets" directory.
      * 
-     * @param fileName The name of the asset to open.  This name can be
-     *                 hierarchical.
+     * @param fileName The name of the asset to open.  This name can be hierarchical.
      * @param accessMode Desired access mode for retrieving the data.
      * 
      * @see #ACCESS_UNKNOWN
@@ -364,34 +737,40 @@
      * @see #open(String)
      * @see #list
      */
-    public final InputStream open(String fileName, int accessMode)
-        throws IOException {
+    public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
+        Preconditions.checkNotNull(fileName, "fileName");
         synchronized (this) {
-            if (!mOpen) {
-                throw new RuntimeException("Assetmanager has been closed");
+            ensureOpenLocked();
+            final long asset = nativeOpenAsset(mObject, fileName, accessMode);
+            if (asset == 0) {
+                throw new FileNotFoundException("Asset file: " + fileName);
             }
-            long asset = openAsset(fileName, accessMode);
-            if (asset != 0) {
-                AssetInputStream res = new AssetInputStream(asset);
-                incRefsLocked(res.hashCode());
-                return res;
-            }
+            final AssetInputStream assetInputStream = new AssetInputStream(asset);
+            incRefsLocked(assetInputStream.hashCode());
+            return assetInputStream;
         }
-        throw new FileNotFoundException("Asset file: " + fileName);
     }
 
-    public final AssetFileDescriptor openFd(String fileName)
-            throws IOException {
+    /**
+     * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
+     * This provides access to files that have been bundled with an application as assets -- that
+     * is, files placed in to the "assets" directory.
+     *
+     * The asset must be uncompressed, or an exception will be thrown.
+     *
+     * @param fileName The name of the asset to open.  This name can be hierarchical.
+     * @return An open AssetFileDescriptor.
+     */
+    public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
+        Preconditions.checkNotNull(fileName, "fileName");
         synchronized (this) {
-            if (!mOpen) {
-                throw new RuntimeException("Assetmanager has been closed");
+            ensureOpenLocked();
+            final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
+            if (pfd == null) {
+                throw new FileNotFoundException("Asset file: " + fileName);
             }
-            ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
-            if (pfd != null) {
-                return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
-            }
+            return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
         }
-        throw new FileNotFoundException("Asset file: " + fileName);
     }
 
     /**
@@ -406,90 +785,121 @@
      * 
      * @see #open
      */
-    public native final String[] list(String path)
-        throws IOException;
+    public @Nullable String[] list(@NonNull String path) throws IOException {
+        Preconditions.checkNotNull(path, "path");
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeList(mObject, path);
+        }
+    }
 
     /**
-     * {@hide}
      * Open a non-asset file as an asset using ACCESS_STREAMING mode.  This
      * provides direct access to all of the files included in an application
      * package (not only its assets).  Applications should not normally use
      * this.
-     * 
+     *
+     * @param fileName Name of the asset to retrieve.
+     *
      * @see #open(String)
+     * @hide
      */
-    public final InputStream openNonAsset(String fileName) throws IOException {
+    public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
         return openNonAsset(0, fileName, ACCESS_STREAMING);
     }
 
     /**
-     * {@hide}
      * Open a non-asset file as an asset using a specific access mode.  This
      * provides direct access to all of the files included in an application
      * package (not only its assets).  Applications should not normally use
      * this.
-     * 
+     *
+     * @param fileName Name of the asset to retrieve.
+     * @param accessMode Desired access mode for retrieving the data.
+     *
+     * @see #ACCESS_UNKNOWN
+     * @see #ACCESS_STREAMING
+     * @see #ACCESS_RANDOM
+     * @see #ACCESS_BUFFER
      * @see #open(String, int)
+     * @hide
      */
-    public final InputStream openNonAsset(String fileName, int accessMode)
-        throws IOException {
+    public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
+            throws IOException {
         return openNonAsset(0, fileName, accessMode);
     }
 
     /**
-     * {@hide}
      * Open a non-asset in a specified package.  Not for use by applications.
-     * 
+     *
      * @param cookie Identifier of the package to be opened.
      * @param fileName Name of the asset to retrieve.
+     * @hide
      */
-    public final InputStream openNonAsset(int cookie, String fileName)
-        throws IOException {
+    public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
+            throws IOException {
         return openNonAsset(cookie, fileName, ACCESS_STREAMING);
     }
 
     /**
-     * {@hide}
      * Open a non-asset in a specified package.  Not for use by applications.
-     * 
+     *
      * @param cookie Identifier of the package to be opened.
      * @param fileName Name of the asset to retrieve.
      * @param accessMode Desired access mode for retrieving the data.
+     * @hide
      */
-    public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
-        throws IOException {
+    public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
+            throws IOException {
+        Preconditions.checkNotNull(fileName, "fileName");
         synchronized (this) {
-            if (!mOpen) {
-                throw new RuntimeException("Assetmanager has been closed");
+            ensureOpenLocked();
+            final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
+            if (asset == 0) {
+                throw new FileNotFoundException("Asset absolute file: " + fileName);
             }
-            long asset = openNonAssetNative(cookie, fileName, accessMode);
-            if (asset != 0) {
-                AssetInputStream res = new AssetInputStream(asset);
-                incRefsLocked(res.hashCode());
-                return res;
-            }
+            final AssetInputStream assetInputStream = new AssetInputStream(asset);
+            incRefsLocked(assetInputStream.hashCode());
+            return assetInputStream;
         }
-        throw new FileNotFoundException("Asset absolute file: " + fileName);
     }
 
-    public final AssetFileDescriptor openNonAssetFd(String fileName)
+    /**
+     * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+     * This provides direct access to all of the files included in an application
+     * package (not only its assets).  Applications should not normally use this.
+     *
+     * The asset must not be compressed, or an exception will be thrown.
+     *
+     * @param fileName Name of the asset to retrieve.
+     */
+    public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
             throws IOException {
         return openNonAssetFd(0, fileName);
     }
-    
-    public final AssetFileDescriptor openNonAssetFd(int cookie,
-            String fileName) throws IOException {
+
+    /**
+     * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
+     * This provides direct access to all of the files included in an application
+     * package (not only its assets).  Applications should not normally use this.
+     *
+     * The asset must not be compressed, or an exception will be thrown.
+     *
+     * @param cookie Identifier of the package to be opened.
+     * @param fileName Name of the asset to retrieve.
+     */
+    public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
+            throws IOException {
+        Preconditions.checkNotNull(fileName, "fileName");
         synchronized (this) {
-            if (!mOpen) {
-                throw new RuntimeException("Assetmanager has been closed");
+            ensureOpenLocked();
+            final ParcelFileDescriptor pfd =
+                    nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
+            if (pfd == null) {
+                throw new FileNotFoundException("Asset absolute file: " + fileName);
             }
-            ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
-                    fileName, mOffsets);
-            if (pfd != null) {
-                return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
-            }
+            return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
         }
-        throw new FileNotFoundException("Asset absolute file: " + fileName);
     }
     
     /**
@@ -497,7 +907,7 @@
      * 
      * @param fileName The name of the file to retrieve.
      */
-    public final XmlResourceParser openXmlResourceParser(String fileName)
+    public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
             throws IOException {
         return openXmlResourceParser(0, fileName);
     }
@@ -508,270 +918,265 @@
      * @param cookie Identifier of the package to be opened.
      * @param fileName The name of the file to retrieve.
      */
-    public final XmlResourceParser openXmlResourceParser(int cookie,
-            String fileName) throws IOException {
-        XmlBlock block = openXmlBlockAsset(cookie, fileName);
-        XmlResourceParser rp = block.newParser();
-        block.close();
-        return rp;
+    public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
+            throws IOException {
+        try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
+            XmlResourceParser parser = block.newParser();
+            // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
+            // a valid native pointer, which makes newParser always return non-null. But let's
+            // be paranoid.
+            if (parser == null) {
+                throw new AssertionError("block.newParser() returned a null parser");
+            }
+            return parser;
+        }
     }
 
     /**
-     * {@hide}
-     * Retrieve a non-asset as a compiled XML file.  Not for use by
-     * applications.
+     * Retrieve a non-asset as a compiled XML file.  Not for use by applications.
      * 
      * @param fileName The name of the file to retrieve.
+     * @hide
      */
-    /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
-            throws IOException {
+    @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
         return openXmlBlockAsset(0, fileName);
     }
 
     /**
-     * {@hide}
      * Retrieve a non-asset as a compiled XML file.  Not for use by
      * applications.
      * 
      * @param cookie Identifier of the package to be opened.
      * @param fileName Name of the asset to retrieve.
+     * @hide
      */
-    /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
-        throws IOException {
+    @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
+        Preconditions.checkNotNull(fileName, "fileName");
         synchronized (this) {
-            if (!mOpen) {
-                throw new RuntimeException("Assetmanager has been closed");
+            ensureOpenLocked();
+            final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+            if (xmlBlock == 0) {
+                throw new FileNotFoundException("Asset XML file: " + fileName);
             }
-            long xmlBlock = openXmlAssetNative(cookie, fileName);
-            if (xmlBlock != 0) {
-                XmlBlock res = new XmlBlock(this, xmlBlock);
-                incRefsLocked(res.hashCode());
-                return res;
-            }
+            final XmlBlock block = new XmlBlock(this, xmlBlock);
+            incRefsLocked(block.hashCode());
+            return block;
         }
-        throw new FileNotFoundException("Asset XML file: " + fileName);
     }
 
-    /*package*/ void xmlBlockGone(int id) {
+    void xmlBlockGone(int id) {
         synchronized (this) {
             decRefsLocked(id);
         }
     }
 
-    /*package*/ final long createTheme() {
+    void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+            @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+            long outIndicesAddress) {
+        Preconditions.checkNotNull(inAttrs, "inAttrs");
         synchronized (this) {
-            if (!mOpen) {
-                throw new RuntimeException("Assetmanager has been closed");
-            }
-            long res = newTheme();
-            incRefsLocked(res);
-            return res;
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
+                    parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
+                    outIndicesAddress);
         }
     }
 
-    /*package*/ final void releaseTheme(long theme) {
+    boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+            @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
+            @NonNull int[] outIndices) {
+        Preconditions.checkNotNull(inAttrs, "inAttrs");
+        Preconditions.checkNotNull(outValues, "outValues");
+        Preconditions.checkNotNull(outIndices, "outIndices");
         synchronized (this) {
-            deleteTheme(theme);
-            decRefsLocked(theme);
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            return nativeResolveAttrs(mObject,
+                    themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
         }
     }
 
+    boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
+            @NonNull int[] outValues, @NonNull int[] outIndices) {
+        Preconditions.checkNotNull(parser, "parser");
+        Preconditions.checkNotNull(inAttrs, "inAttrs");
+        Preconditions.checkNotNull(outValues, "outValues");
+        Preconditions.checkNotNull(outIndices, "outIndices");
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            return nativeRetrieveAttributes(
+                    mObject, parser.mParseState, inAttrs, outValues, outIndices);
+        }
+    }
+
+    long createTheme() {
+        synchronized (this) {
+            ensureValidLocked();
+            long themePtr = nativeThemeCreate(mObject);
+            incRefsLocked(themePtr);
+            return themePtr;
+        }
+    }
+
+    void releaseTheme(long themePtr) {
+        synchronized (this) {
+            nativeThemeDestroy(themePtr);
+            decRefsLocked(themePtr);
+        }
+    }
+
+    void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            nativeThemeApplyStyle(mObject, themePtr, resId, force);
+        }
+    }
+
+    @Override
     protected void finalize() throws Throwable {
-        try {
-            if (DEBUG_REFS && mNumRefs != 0) {
-                Log.w(TAG, "AssetManager " + this
-                        + " finalized with non-zero refs: " + mNumRefs);
-                if (mRefStacks != null) {
-                    for (RuntimeException e : mRefStacks.values()) {
-                        Log.w(TAG, "Reference from here", e);
-                    }
+        if (DEBUG_REFS && mNumRefs != 0) {
+            Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
+            if (mRefStacks != null) {
+                for (RuntimeException e : mRefStacks.values()) {
+                    Log.w(TAG, "Reference from here", e);
                 }
             }
-            destroy();
-        } finally {
-            super.finalize();
+        }
+
+        if (mObject != 0) {
+            nativeDestroy(mObject);
         }
     }
-    
+
+    /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
+    safe and it does not rely on AssetManager once it has been created. It completely owns the
+    underlying Asset. */
     public final class AssetInputStream extends InputStream {
+        private long mAssetNativePtr;
+        private long mLength;
+        private long mMarkPos;
+
         /**
          * @hide
          */
         public final int getAssetInt() {
             throw new UnsupportedOperationException();
         }
+
         /**
          * @hide
          */
         public final long getNativeAsset() {
-            return mAsset;
+            return mAssetNativePtr;
         }
-        private AssetInputStream(long asset)
-        {
-            mAsset = asset;
-            mLength = getAssetLength(asset);
+
+        private AssetInputStream(long assetNativePtr) {
+            mAssetNativePtr = assetNativePtr;
+            mLength = nativeAssetGetLength(assetNativePtr);
         }
+
+        @Override
         public final int read() throws IOException {
-            return readAssetChar(mAsset);
+            ensureOpen();
+            return nativeAssetReadChar(mAssetNativePtr);
         }
-        public final boolean markSupported() {
-            return true;
+
+        @Override
+        public final int read(@NonNull byte[] b) throws IOException {
+            ensureOpen();
+            Preconditions.checkNotNull(b, "b");
+            return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
         }
-        public final int available() throws IOException {
-            long len = getAssetRemainingLength(mAsset);
-            return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
+
+        @Override
+        public final int read(@NonNull byte[] b, int off, int len) throws IOException {
+            ensureOpen();
+            Preconditions.checkNotNull(b, "b");
+            return nativeAssetRead(mAssetNativePtr, b, off, len);
         }
-        public final void close() throws IOException {
-            synchronized (AssetManager.this) {
-                if (mAsset != 0) {
-                    destroyAsset(mAsset);
-                    mAsset = 0;
-                    decRefsLocked(hashCode());
-                }
-            }
-        }
-        public final void mark(int readlimit) {
-            mMarkPos = seekAsset(mAsset, 0, 0);
-        }
-        public final void reset() throws IOException {
-            seekAsset(mAsset, mMarkPos, -1);
-        }
-        public final int read(byte[] b) throws IOException {
-            return readAsset(mAsset, b, 0, b.length);
-        }
-        public final int read(byte[] b, int off, int len) throws IOException {
-            return readAsset(mAsset, b, off, len);
-        }
+
+        @Override
         public final long skip(long n) throws IOException {
-            long pos = seekAsset(mAsset, 0, 0);
-            if ((pos+n) > mLength) {
-                n = mLength-pos;
+            ensureOpen();
+            long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+            if ((pos + n) > mLength) {
+                n = mLength - pos;
             }
             if (n > 0) {
-                seekAsset(mAsset, n, 0);
+                nativeAssetSeek(mAssetNativePtr, n, 0);
             }
             return n;
         }
 
-        protected void finalize() throws Throwable
-        {
+        @Override
+        public final int available() throws IOException {
+            ensureOpen();
+            final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
+            return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
+        }
+
+        @Override
+        public final boolean markSupported() {
+            return true;
+        }
+
+        @Override
+        public final void mark(int readlimit) {
+            ensureOpen();
+            mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+        }
+
+        @Override
+        public final void reset() throws IOException {
+            ensureOpen();
+            nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
+        }
+
+        @Override
+        public final void close() throws IOException {
+            if (mAssetNativePtr != 0) {
+                nativeAssetDestroy(mAssetNativePtr);
+                mAssetNativePtr = 0;
+
+                synchronized (AssetManager.this) {
+                    decRefsLocked(hashCode());
+                }
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
             close();
         }
 
-        private long mAsset;
-        private long mLength;
-        private long mMarkPos;
-    }
-
-    /**
-     * Add an additional set of assets to the asset manager.  This can be
-     * either a directory or ZIP file.  Not for use by applications.  Returns
-     * the cookie of the added asset, or 0 on failure.
-     * {@hide}
-     */
-    public final int addAssetPath(String path) {
-        return  addAssetPathInternal(path, false);
-    }
-
-    /**
-     * Add an application assets to the asset manager and loading it as shared library.
-     * This can be either a directory or ZIP file.  Not for use by applications.  Returns
-     * the cookie of the added asset, or 0 on failure.
-     * {@hide}
-     */
-    public final int addAssetPathAsSharedLibrary(String path) {
-        return addAssetPathInternal(path, true);
-    }
-
-    private final int addAssetPathInternal(String path, boolean appAsLib) {
-        synchronized (this) {
-            int res = addAssetPathNative(path, appAsLib);
-            makeStringBlocks(mStringBlocks);
-            return res;
+        private void ensureOpen() {
+            if (mAssetNativePtr == 0) {
+                throw new IllegalStateException("AssetInputStream is closed");
+            }
         }
     }
 
-    private native final int addAssetPathNative(String path, boolean appAsLib);
-
-    /**
-     * Add an additional set of assets to the asset manager from an already open
-     * FileDescriptor.  Not for use by applications.
-     * This does not give full AssetManager functionality for these assets,
-     * since the origin of the file is not known for purposes of sharing,
-     * overlay resolution, and other features.  However it does allow you
-     * to do simple access to the contents of the given fd as an apk file.
-     * Performs a dup of the underlying fd, so you must take care of still closing
-     * the FileDescriptor yourself (and can do that whenever you want).
-     * Returns the cookie of the added asset, or 0 on failure.
-     * {@hide}
-     */
-    public int addAssetFd(FileDescriptor fd, String debugPathName) {
-        return addAssetFdInternal(fd, debugPathName, false);
-    }
-
-    private int addAssetFdInternal(FileDescriptor fd, String debugPathName,
-            boolean appAsLib) {
-        synchronized (this) {
-            int res = addAssetFdNative(fd, debugPathName, appAsLib);
-            makeStringBlocks(mStringBlocks);
-            return res;
-        }
-    }
-
-    private native int addAssetFdNative(FileDescriptor fd, String debugPathName,
-            boolean appAsLib);
-
-    /**
-     * Add a set of assets to overlay an already added set of assets.
-     *
-     * This is only intended for application resources. System wide resources
-     * are handled before any Java code is executed.
-     *
-     * {@hide}
-     */
-
-    public final int addOverlayPath(String idmapPath) {
-        synchronized (this) {
-            int res = addOverlayPathNative(idmapPath);
-            makeStringBlocks(mStringBlocks);
-            return res;
-        }
-    }
-
-    /**
-     * See addOverlayPath.
-     *
-     * {@hide}
-     */
-    public native final int addOverlayPathNative(String idmapPath);
-
-    /**
-     * Add multiple sets of assets to the asset manager at once.  See
-     * {@link #addAssetPath(String)} for more information.  Returns array of
-     * cookies for each added asset with 0 indicating failure, or null if
-     * the input array of paths is null.
-     * {@hide}
-     */
-    public final int[] addAssetPaths(String[] paths) {
-        if (paths == null) {
-            return null;
-        }
-
-        int[] cookies = new int[paths.length];
-        for (int i = 0; i < paths.length; i++) {
-            cookies[i] = addAssetPath(paths[i]);
-        }
-
-        return cookies;
-    }
-
     /**
      * Determine whether the state in this asset manager is up-to-date with
      * the files on the filesystem.  If false is returned, you need to
      * instantiate a new AssetManager class to see the new data.
-     * {@hide}
+     * @hide
      */
-    public native final boolean isUpToDate();
+    public boolean isUpToDate() {
+        for (ApkAssets apkAssets : getApkAssets()) {
+            if (!apkAssets.isUpToDate()) {
+                return false;
+            }
+        }
+        return true;
+    }
 
     /**
      * Get the locales that this asset manager contains data for.
@@ -784,7 +1189,12 @@
      * are of the form {@code ll_CC} where {@code ll} is a two letter language code,
      * and {@code CC} is a two letter country code.
      */
-    public native final String[] getLocales();
+    public String[] getLocales() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLocales(mObject, false /*excludeSystem*/);
+        }
+    }
 
     /**
      * Same as getLocales(), except that locales that are only provided by the system (i.e. those
@@ -794,131 +1204,58 @@
      * assets support Cherokee and French, getLocales() would return
      * [Cherokee, English, French, German], while getNonSystemLocales() would return
      * [Cherokee, French].
-     * {@hide}
+     * @hide
      */
-    public native final String[] getNonSystemLocales();
-
-    /** {@hide} */
-    public native final Configuration[] getSizeConfigurations();
+    public String[] getNonSystemLocales() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetLocales(mObject, true /*excludeSystem*/);
+        }
+    }
 
     /**
-     * Change the configuation used when retrieving resources.  Not for use by
+     * @hide
+     */
+    Configuration[] getSizeConfigurations() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetSizeConfigurations(mObject);
+        }
+    }
+
+    /**
+     * Change the configuration used when retrieving resources.  Not for use by
      * applications.
-     * {@hide}
+     * @hide
      */
-    public native final void setConfiguration(int mcc, int mnc, String locale,
-            int orientation, int touchscreen, int density, int keyboard,
-            int keyboardHidden, int navigation, int screenWidth, int screenHeight,
-            int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
-            int screenLayout, int uiMode, int colorMode, int majorVersion);
+    public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
+            int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+            int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+            int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
+        synchronized (this) {
+            ensureValidLocked();
+            nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+                    keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+                    smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+                    colorMode, majorVersion);
+        }
+    }
 
     /**
-     * Retrieve the resource identifier for the given resource name.
+     * @hide
      */
-    /*package*/ native final int getResourceIdentifier(String name,
-                                                       String defType,
-                                                       String defPackage);
+    public SparseArray<String> getAssignedPackageIdentifiers() {
+        synchronized (this) {
+            ensureValidLocked();
+            return nativeGetAssignedPackageIdentifiers(mObject);
+        }
+    }
 
-    /*package*/ native final String getResourceName(int resid);
-    /*package*/ native final String getResourcePackageName(int resid);
-    /*package*/ native final String getResourceTypeName(int resid);
-    /*package*/ native final String getResourceEntryName(int resid);
-    
-    private native final long openAsset(String fileName, int accessMode);
-    private final native ParcelFileDescriptor openAssetFd(String fileName,
-            long[] outOffsets) throws IOException;
-    private native final long openNonAssetNative(int cookie, String fileName,
-            int accessMode);
-    private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
-            String fileName, long[] outOffsets) throws IOException;
-    private native final void destroyAsset(long asset);
-    private native final int readAssetChar(long asset);
-    private native final int readAsset(long asset, byte[] b, int off, int len);
-    private native final long seekAsset(long asset, long offset, int whence);
-    private native final long getAssetLength(long asset);
-    private native final long getAssetRemainingLength(long asset);
-
-    /** Returns true if the resource was found, filling in mRetStringBlock and
-     *  mRetData. */
-    private native final int loadResourceValue(int ident, short density, TypedValue outValue,
-            boolean resolve);
-    /** Returns true if the resource was found, filling in mRetStringBlock and
-     *  mRetData. */
-    private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
-                                               boolean resolve);
-    /*package*/ static final int STYLE_NUM_ENTRIES = 6;
-    /*package*/ static final int STYLE_TYPE = 0;
-    /*package*/ static final int STYLE_DATA = 1;
-    /*package*/ static final int STYLE_ASSET_COOKIE = 2;
-    /*package*/ static final int STYLE_RESOURCE_ID = 3;
-
-    /* Offset within typed data array for native changingConfigurations. */
-    static final int STYLE_CHANGING_CONFIGURATIONS = 4;
-
-    /*package*/ static final int STYLE_DENSITY = 5;
-    /*package*/ native static final void applyStyle(long theme,
-            int defStyleAttr, int defStyleRes, long xmlParser,
-            int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress);
-    /*package*/ native static final boolean resolveAttrs(long theme,
-            int defStyleAttr, int defStyleRes, int[] inValues,
-            int[] inAttrs, int[] outValues, int[] outIndices);
-    /*package*/ native final boolean retrieveAttributes(
-            long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
-    /*package*/ native final int getArraySize(int resource);
-    /*package*/ native final int retrieveArray(int resource, int[] outValues);
-    private native final int getStringBlockCount();
-    private native final long getNativeStringBlock(int block);
-
-    /**
-     * {@hide}
-     */
-    public native final String getCookieName(int cookie);
-
-    /**
-     * {@hide}
-     */
-    public native final SparseArray<String> getAssignedPackageIdentifiers();
-
-    /**
-     * {@hide}
-     */
-    public native static final int getGlobalAssetCount();
-    
-    /**
-     * {@hide}
-     */
-    public native static final String getAssetAllocations();
-    
-    /**
-     * {@hide}
-     */
-    public native static final int getGlobalAssetManagerCount();
-    
-    private native final long newTheme();
-    private native final void deleteTheme(long theme);
-    /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
-    /*package*/ native static final void copyTheme(long dest, long source);
-    /*package*/ native static final void clearTheme(long theme);
-    /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
-                                                                TypedValue outValue,
-                                                                boolean resolve);
-    /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
-    /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme);
-
-    private native final long openXmlAssetNative(int cookie, String fileName);
-
-    private native final String[] getArrayStringResource(int arrayRes);
-    private native final int[] getArrayStringInfo(int arrayRes);
-    /*package*/ native final int[] getArrayIntResource(int arrayRes);
-    /*package*/ native final int[] getStyleAttributes(int themeRes);
-
-    private native final void init(boolean isSystem);
-    private native final void destroy();
-
-    private final void incRefsLocked(long id) {
+    @GuardedBy("this")
+    private void incRefsLocked(long id) {
         if (DEBUG_REFS) {
             if (mRefStacks == null) {
-                mRefStacks = new HashMap<Long, RuntimeException>();
+                mRefStacks = new HashMap<>();
             }
             RuntimeException ex = new RuntimeException();
             ex.fillInStackTrace();
@@ -926,16 +1263,119 @@
         }
         mNumRefs++;
     }
-    
-    private final void decRefsLocked(long id) {
+
+    @GuardedBy("this")
+    private void decRefsLocked(long id) {
         if (DEBUG_REFS && mRefStacks != null) {
             mRefStacks.remove(id);
         }
         mNumRefs--;
-        //System.out.println("Dec streams: mNumRefs=" + mNumRefs
-        //                   + " mReleased=" + mReleased);
-        if (mNumRefs == 0) {
-            destroy();
+        if (mNumRefs == 0 && mObject != 0) {
+            nativeDestroy(mObject);
+            mObject = 0;
+            mApkAssets = sEmptyApkAssets;
         }
     }
+
+    // AssetManager setup native methods.
+    private static native long nativeCreate();
+    private static native void nativeDestroy(long ptr);
+    private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
+            boolean invalidateCaches);
+    private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
+            @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
+            int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+            int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
+            int uiMode, int colorMode, int majorVersion);
+    private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
+            long ptr);
+
+    // File native methods.
+    private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
+            throws IOException;
+    private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
+    private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
+            @NonNull String fileName, long[] outOffsets) throws IOException;
+    private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
+            int accessMode);
+    private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
+            @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
+    private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+
+    // Primitive resource native methods.
+    private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
+            @NonNull TypedValue outValue, boolean resolveReferences);
+    private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
+            @NonNull TypedValue outValue);
+
+    private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
+            @StyleRes int resId);
+    private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
+            @ArrayRes int resId);
+    private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
+            @ArrayRes int resId);
+    private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
+    private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
+    private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
+            @NonNull int[] outValues);
+
+    // Resource name/ID native methods.
+    private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
+            @Nullable String defType, @Nullable String defPackage);
+    private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
+    private static native @Nullable String nativeGetResourcePackageName(long ptr,
+            @AnyRes int resid);
+    private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
+    private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
+    private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
+    private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+
+    // Style attribute retrieval native methods.
+    private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
+            @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
+            long outValuesAddress, long outIndicesAddress);
+    private static native boolean nativeResolveAttrs(long ptr, long themePtr,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
+            @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+    private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
+            @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
+
+    // Theme related native methods
+    private static native long nativeThemeCreate(long ptr);
+    private static native void nativeThemeDestroy(long themePtr);
+    private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
+            boolean force);
+    static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
+    static native void nativeThemeClear(long themePtr);
+    private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
+            @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
+    private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
+            String prefix);
+    static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+
+    // AssetInputStream related native methods.
+    private static native void nativeAssetDestroy(long assetPtr);
+    private static native int nativeAssetReadChar(long assetPtr);
+    private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
+    private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
+    private static native long nativeAssetGetLength(long assetPtr);
+    private static native long nativeAssetGetRemainingLength(long assetPtr);
+
+    private static native void nativeVerifySystemIdmaps();
+
+    // Global debug native methods.
+    /**
+     * @hide
+     */
+    public static native int getGlobalAssetCount();
+
+    /**
+     * @hide
+     */
+    public static native String getAssetAllocations();
+
+    /**
+     * @hide
+     */
+    public static native int getGlobalAssetManagerCount();
 }
diff --git a/android/content/res/Configuration.java b/android/content/res/Configuration.java
index eb30979..19b5c45 100644
--- a/android/content/res/Configuration.java
+++ b/android/content/res/Configuration.java
@@ -16,15 +16,29 @@
 
 package android.content.res;
 
+import static android.content.ConfigurationProto.COLOR_MODE;
 import static android.content.ConfigurationProto.DENSITY_DPI;
 import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.KEYBOARD;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
 import static android.content.ConfigurationProto.ORIENTATION;
 import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
 import static android.content.ConfigurationProto.SCREEN_LAYOUT;
 import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
 import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
 import static android.content.ConfigurationProto.UI_MODE;
 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -38,6 +52,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
 import android.view.View;
 
@@ -1076,7 +1091,17 @@
     public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
         final long token = protoOutputStream.start(fieldId);
         protoOutputStream.write(FONT_SCALE, fontScale);
+        protoOutputStream.write(MCC, mcc);
+        protoOutputStream.write(MNC, mnc);
+        mLocaleList.writeToProto(protoOutputStream, LOCALES);
         protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+        protoOutputStream.write(COLOR_MODE, colorMode);
+        protoOutputStream.write(TOUCHSCREEN, touchscreen);
+        protoOutputStream.write(KEYBOARD, keyboard);
+        protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+        protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+        protoOutputStream.write(NAVIGATION, navigation);
+        protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
         protoOutputStream.write(ORIENTATION, orientation);
         protoOutputStream.write(UI_MODE, uiMode);
         protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
@@ -1088,6 +1113,36 @@
     }
 
     /**
+     * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+     * stream.
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param metrics           Current display information
+     * @hide
+     */
+    public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+            DisplayMetrics metrics) {
+        final int width, height;
+        if (metrics.widthPixels >= metrics.heightPixels) {
+            width = metrics.widthPixels;
+            height = metrics.heightPixels;
+        } else {
+            //noinspection SuspiciousNameCombination
+            width = metrics.heightPixels;
+            //noinspection SuspiciousNameCombination
+            height = metrics.widthPixels;
+        }
+
+        final long token = protoOutputStream.start(fieldId);
+        writeToProto(protoOutputStream, CONFIGURATION);
+        protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+        protoOutputStream.write(SCREEN_WIDTH_PX, width);
+        protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+        protoOutputStream.end(token);
+    }
+
+    /**
      * Convert the UI mode to a human readable format.
      * @hide
      */
@@ -1558,12 +1613,7 @@
         dest.writeInt(mnc);
 
         fixUpLocaleList();
-        final int localeListSize = mLocaleList.size();
-        dest.writeInt(localeListSize);
-        for (int i = 0; i < localeListSize; ++i) {
-            final Locale l = mLocaleList.get(i);
-            dest.writeString(l.toLanguageTag());
-        }
+        dest.writeParcelable(mLocaleList, flags);
 
         if(userSetLocale) {
             dest.writeInt(1);
@@ -1597,12 +1647,7 @@
         mcc = source.readInt();
         mnc = source.readInt();
 
-        final int localeListSize = source.readInt();
-        final Locale[] localeArray = new Locale[localeListSize];
-        for (int i = 0; i < localeListSize; ++i) {
-            localeArray[i] = Locale.forLanguageTag(source.readString());
-        }
-        mLocaleList = new LocaleList(localeArray);
+        mLocaleList = source.readParcelable(LocaleList.class.getClassLoader());
         locale = mLocaleList.get(0);
 
         userSetLocale = (source.readInt()==1);
@@ -1925,11 +1970,21 @@
 
     /**
      * Returns a string representation of the configuration that can be parsed
-     * by build tools (like AAPT).
+     * by build tools (like AAPT), without display metrics included
      *
      * @hide
      */
     public static String resourceQualifierString(Configuration config) {
+        return resourceQualifierString(config, null);
+    }
+
+    /**
+     * Returns a string representation of the configuration that can be parsed
+     * by build tools (like AAPT).
+     *
+     * @hide
+     */
+    public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
         ArrayList<String> parts = new ArrayList<String>();
 
         if (config.mcc != 0) {
@@ -2177,6 +2232,20 @@
                 break;
         }
 
+        if (metrics != null) {
+            final int width, height;
+            if (metrics.widthPixels >= metrics.heightPixels) {
+                width = metrics.widthPixels;
+                height = metrics.heightPixels;
+            } else {
+                //noinspection SuspiciousNameCombination
+                width = metrics.heightPixels;
+                //noinspection SuspiciousNameCombination
+                height = metrics.widthPixels;
+            }
+            parts.add(width + "x" + height);
+        }
+
         parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
         return TextUtils.join("-", parts);
     }
diff --git a/android/content/res/Resources.java b/android/content/res/Resources.java
index e173653..c58cde0 100644
--- a/android/content/res/Resources.java
+++ b/android/content/res/Resources.java
@@ -590,7 +590,7 @@
      */
     @NonNull
     public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
-        int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
+        int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
         if (res != null) {
             return res;
         }
@@ -613,13 +613,13 @@
     @NonNull
     public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
         final ResourcesImpl impl = mResourcesImpl;
-        int len = impl.getAssets().getArraySize(id);
+        int len = impl.getAssets().getResourceArraySize(id);
         if (len < 0) {
             throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
         }
         
         TypedArray array = TypedArray.obtain(this, len);
-        array.mLength = impl.getAssets().retrieveArray(id, array.mData);
+        array.mLength = impl.getAssets().getResourceArray(id, array.mData);
         array.mIndices[0] = 0;
         
         return array;
@@ -847,6 +847,7 @@
      * @see #getDrawableForDensity(int, int, Theme)
      * @deprecated Use {@link #getDrawableForDensity(int, int, Theme)} instead.
      */
+    @Nullable
     @Deprecated
     public Drawable getDrawableForDensity(@DrawableRes int id, int density)
             throws NotFoundException {
@@ -864,11 +865,13 @@
      *            found in {@link DisplayMetrics}. A value of 0 means to use the
      *            density returned from {@link #getConfiguration()}.
      *            This is equivalent to calling {@link #getDrawable(int, Theme)}.
-     * @param theme The theme used to style the drawable attributes, may be {@code null}.
+     * @param theme The theme used to style the drawable attributes, may be {@code null} if the
+     *              drawable cannot be decoded.
      * @return Drawable An object that can be used to draw this resource.
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *             not exist.
      */
+    @Nullable
     public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
         final TypedValue value = obtainTempTypedValue();
         try {
@@ -980,7 +983,7 @@
      *         or multiple colors that can be selected based on a state.
      * @deprecated Use {@link #getColorStateList(int, Theme)} instead.
      */
-    @Nullable
+    @NonNull
     @Deprecated
     public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
         final ColorStateList csl = getColorStateList(id, null);
@@ -1011,7 +1014,7 @@
      * @return A themed ColorStateList object containing either a single solid
      *         color or multiple colors that can be selected based on a state.
      */
-    @Nullable
+    @NonNull
     public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
             throws NotFoundException {
         final TypedValue value = obtainTempTypedValue();
@@ -1024,7 +1027,7 @@
         }
     }
 
-    @Nullable
+    @NonNull
     ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
             throws NotFoundException {
         return mResourcesImpl.loadColorStateList(this, value, id, theme);
@@ -1033,7 +1036,7 @@
     /**
      * @hide
      */
-    @Nullable
+    @NonNull
     public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
         return mResourcesImpl.loadComplexColor(this, value, id, theme);
     }
@@ -1139,6 +1142,7 @@
      *         
      * @see #getXml
      */
+    @NonNull
     public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
         return loadXmlResourceParser(id, "layout");
     }
@@ -1163,6 +1167,7 @@
      *         
      * @see #getXml
      */
+    @NonNull
     public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
         return loadXmlResourceParser(id, "anim");
     }
@@ -1188,6 +1193,7 @@
      *         
      * @see android.util.AttributeSet
      */
+    @NonNull
     public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
         return loadXmlResourceParser(id, "xml");
     }
@@ -1203,8 +1209,8 @@
      * @return InputStream Access to the resource data.
      *
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
-     * 
      */
+    @NonNull
     public InputStream openRawResource(@RawRes int id) throws NotFoundException {
         final TypedValue value = obtainTempTypedValue();
         try {
@@ -1261,6 +1267,7 @@
      *
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
      */
+    @NonNull
     public InputStream openRawResource(@RawRes int id, TypedValue value)
             throws NotFoundException {
         return mResourcesImpl.openRawResource(id, value);
@@ -1789,8 +1796,7 @@
         // out the attributes from the XML file (applying type information
         // contained in the resources and such).
         XmlBlock.Parser parser = (XmlBlock.Parser)set;
-        mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
-                array.mData, array.mIndices);
+        mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
 
         array.mXml = parser;
 
diff --git a/android/content/res/ResourcesImpl.java b/android/content/res/ResourcesImpl.java
index 97cb78b..8c98067 100644
--- a/android/content/res/ResourcesImpl.java
+++ b/android/content/res/ResourcesImpl.java
@@ -27,9 +27,11 @@
 import android.annotation.StyleableRes;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
+import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Configuration.NativeConfig;
 import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -168,7 +170,6 @@
         mDisplayAdjustments = displayAdjustments;
         mConfiguration.setToDefaults();
         updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
-        mAssets.ensureStringBlocks();
     }
 
     public DisplayAdjustments getDisplayAdjustments() {
@@ -627,7 +628,7 @@
             } else if (isColorDrawable) {
                 dr = new ColorDrawable(value.data);
             } else {
-                dr = loadDrawableForCookie(wrapper, value, id, density, null);
+                dr = loadDrawableForCookie(wrapper, value, id, density);
             }
             // DrawableContainer' constant state has drawables instances. In order to leave the
             // constant state intact in the cache, we need to create a new DrawableContainer after
@@ -752,10 +753,34 @@
     }
 
     /**
-     * Loads a drawable from XML or resources stream.
+     * Loads a Drawable from an encoded image stream, or null.
+     *
+     * This call will handle closing ais.
      */
+    @Nullable
+    private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
+            @NonNull Resources wrapper, @NonNull TypedValue value) {
+        ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
+                            wrapper, value);
+        try {
+            return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (IOException ioe) {
+            // This is okay. This may be something that ImageDecoder does not
+            // support, like SVG.
+            return null;
+        }
+    }
+
+    /**
+     * Loads a drawable from XML or resources stream.
+     *
+     * @return Drawable, or null if Drawable cannot be decoded.
+     */
+    @Nullable
     private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
-            int id, int density, @Nullable Resources.Theme theme) {
+            int id, int density) {
         if (value.string == null) {
             throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
                     + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -774,22 +799,23 @@
             }
         }
 
-        // For prelaod tracing.
+        // For preload tracing.
         long startTime = 0;
         int startBitmapCount = 0;
         long startBitmapSize = 0;
-        int startDrwableCount = 0;
+        int startDrawableCount = 0;
         if (TRACE_FOR_DETAILED_PRELOAD) {
             startTime = System.nanoTime();
             startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
             startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
-            startDrwableCount = sPreloadTracingNumLoadedDrawables;
+            startDrawableCount = sPreloadTracingNumLoadedDrawables;
         }
 
         if (DEBUG_LOAD) {
             Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
         }
 
+
         final Drawable dr;
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
@@ -804,13 +830,13 @@
                 if (file.endsWith(".xml")) {
                     final XmlResourceParser rp = loadXmlResourceParser(
                             file, id, value.assetCookie, "drawable");
-                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
+                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
                     rp.close();
                 } else {
                     final InputStream is = mAssets.openNonAsset(
                             value.assetCookie, file, AssetManager.ACCESS_STREAMING);
-                    dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
-                    is.close();
+                    AssetInputStream ais = (AssetInputStream) is;
+                    dr = decodeImageDrawable(ais, wrapper, value);
                 }
             } finally {
                 stack.pop();
@@ -834,7 +860,7 @@
                     final long loadedBitmapSize =
                             Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
                     final int loadedDrawables =
-                            sPreloadTracingNumLoadedDrawables - startDrwableCount;
+                            sPreloadTracingNumLoadedDrawables - startDrawableCount;
 
                     sPreloadTracingNumLoadedDrawables++;
 
@@ -910,6 +936,7 @@
      * first try to load CSL from the cache. If not found, try to get from the constant state.
      * Last, parse the XML and generate the CSL.
      */
+    @Nullable
     private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
             TypedValue value, int id) {
         final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -985,7 +1012,7 @@
         return complexColor;
     }
 
-    @Nullable
+    @NonNull
     ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
             Resources.Theme theme)
             throws NotFoundException {
@@ -1043,9 +1070,10 @@
      * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
      * and selector tag.
      *
-     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content, or
+     *     {@code null} if the XML file is neither.
      */
-    @Nullable
+    @NonNull
     private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
             Resources.Theme theme) {
         if (value.string == null) {
@@ -1274,8 +1302,7 @@
 
         void applyStyle(int resId, boolean force) {
             synchronized (mKey) {
-                AssetManager.applyThemeStyle(mTheme, resId, force);
-
+                mAssets.applyStyleToTheme(mTheme, resId, force);
                 mThemeResId = resId;
                 mKey.append(resId, force);
             }
@@ -1284,7 +1311,7 @@
         void setTo(ThemeImpl other) {
             synchronized (mKey) {
                 synchronized (other.mKey) {
-                    AssetManager.copyTheme(mTheme, other.mTheme);
+                    AssetManager.nativeThemeCopy(mTheme, other.mTheme);
 
                     mThemeResId = other.mThemeResId;
                     mKey.setTo(other.getKey());
@@ -1307,12 +1334,10 @@
                 // out the attributes from the XML file (applying type information
                 // contained in the resources and such).
                 final XmlBlock.Parser parser = (XmlBlock.Parser) set;
-                AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
-                        parser != null ? parser.mParseState : 0,
-                        attrs, attrs.length, array.mDataAddress, array.mIndicesAddress);
+                mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
+                        array.mDataAddress, array.mIndicesAddress);
                 array.mTheme = wrapper;
                 array.mXml = parser;
-
                 return array;
             }
         }
@@ -1329,7 +1354,7 @@
                 }
 
                 final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
-                AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+                mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
                 array.mTheme = wrapper;
                 array.mXml = null;
                 return array;
@@ -1349,14 +1374,14 @@
         @Config int getChangingConfigurations() {
             synchronized (mKey) {
                 final @NativeConfig int nativeChangingConfig =
-                        AssetManager.getThemeChangingConfigurations(mTheme);
+                        AssetManager.nativeThemeGetChangingConfigurations(mTheme);
                 return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
             }
         }
 
         public void dump(int priority, String tag, String prefix) {
             synchronized (mKey) {
-                AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+                mAssets.dumpTheme(mTheme, priority, tag, prefix);
             }
         }
 
@@ -1385,13 +1410,13 @@
          */
         void rebase() {
             synchronized (mKey) {
-                AssetManager.clearTheme(mTheme);
+                AssetManager.nativeThemeClear(mTheme);
 
                 // Reapply the same styles in the same order.
                 for (int i = 0; i < mKey.mCount; i++) {
                     final int resId = mKey.mResId[i];
                     final boolean force = mKey.mForce[i];
-                    AssetManager.applyThemeStyle(mTheme, resId, force);
+                    mAssets.applyStyleToTheme(mTheme, resId, force);
                 }
             }
         }
diff --git a/android/content/res/TypedArray.java b/android/content/res/TypedArray.java
index f33c751..cbb3c6d 100644
--- a/android/content/res/TypedArray.java
+++ b/android/content/res/TypedArray.java
@@ -61,6 +61,15 @@
         return attrs;
     }
 
+    // STYLE_ prefixed constants are offsets within the typed data array.
+    static final int STYLE_NUM_ENTRIES = 6;
+    static final int STYLE_TYPE = 0;
+    static final int STYLE_DATA = 1;
+    static final int STYLE_ASSET_COOKIE = 2;
+    static final int STYLE_RESOURCE_ID = 3;
+    static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+    static final int STYLE_DENSITY = 5;
+
     private final Resources mResources;
     private DisplayMetrics mMetrics;
     private AssetManager mAssets;
@@ -78,7 +87,7 @@
 
     private void resize(int len) {
         mLength = len;
-        final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES;
+        final int dataLen = len * STYLE_NUM_ENTRIES;
         final int indicesLen = len + 1;
         final VMRuntime runtime = VMRuntime.getRuntime();
         if (mDataAddress == 0 || mData.length < dataLen) {
@@ -166,9 +175,9 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return null;
         } else if (type == TypedValue.TYPE_STRING) {
@@ -203,9 +212,9 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return null;
         } else if (type == TypedValue.TYPE_STRING) {
@@ -242,14 +251,13 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_STRING) {
-            final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+            final int cookie = data[index + STYLE_ASSET_COOKIE];
             if (cookie < 0) {
-                return mXml.getPooledString(
-                    data[index+AssetManager.STYLE_DATA]).toString();
+                return mXml.getPooledString(data[index + STYLE_DATA]).toString();
             }
         }
         return null;
@@ -274,11 +282,11 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
-                data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+                data[index + STYLE_CHANGING_CONFIGURATIONS]);
         if ((changingConfigs & ~allowedChangingConfigs) != 0) {
             return null;
         }
@@ -320,14 +328,14 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA] != 0;
+            return data[index + STYLE_DATA] != 0;
         }
 
         final TypedValue v = mValue;
@@ -359,14 +367,14 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA];
+            return data[index + STYLE_DATA];
         }
 
         final TypedValue v = mValue;
@@ -396,16 +404,16 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type == TypedValue.TYPE_FLOAT) {
-            return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
+            return Float.intBitsToFloat(data[index + STYLE_DATA]);
         } else if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA];
+            return data[index + STYLE_DATA];
         }
 
         final TypedValue v = mValue;
@@ -446,15 +454,15 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA];
+            return data[index + STYLE_DATA];
         } else if (type == TypedValue.TYPE_STRING) {
             final TypedValue value = mValue;
             if (getValueAt(index, value)) {
@@ -498,7 +506,7 @@
         }
 
         final TypedValue value = mValue;
-        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
                         "Failed to resolve attribute at index " + index + ": " + value);
@@ -533,7 +541,7 @@
         }
 
         final TypedValue value = mValue;
-        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
                         "Failed to resolve attribute at index " + index + ": " + value);
@@ -564,15 +572,15 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA];
+            return data[index + STYLE_DATA];
         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
             final TypedValue value = mValue;
             getValueAt(index, value);
@@ -612,15 +620,14 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type == TypedValue.TYPE_DIMENSION) {
-            return TypedValue.complexToDimension(
-                    data[index + AssetManager.STYLE_DATA], mMetrics);
+            return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
             final TypedValue value = mValue;
             getValueAt(index, value);
@@ -661,15 +668,14 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type == TypedValue.TYPE_DIMENSION) {
-            return TypedValue.complexToDimensionPixelOffset(
-                    data[index + AssetManager.STYLE_DATA], mMetrics);
+            return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
             final TypedValue value = mValue;
             getValueAt(index, value);
@@ -711,15 +717,14 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type == TypedValue.TYPE_DIMENSION) {
-            return TypedValue.complexToDimensionPixelSize(
-                data[index+AssetManager.STYLE_DATA], mMetrics);
+            return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
             final TypedValue value = mValue;
             getValueAt(index, value);
@@ -755,16 +760,15 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA];
+            return data[index + STYLE_DATA];
         } else if (type == TypedValue.TYPE_DIMENSION) {
-            return TypedValue.complexToDimensionPixelSize(
-                data[index+AssetManager.STYLE_DATA], mMetrics);
+            return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
             final TypedValue value = mValue;
             getValueAt(index, value);
@@ -795,15 +799,14 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type >= TypedValue.TYPE_FIRST_INT
                 && type <= TypedValue.TYPE_LAST_INT) {
-            return data[index+AssetManager.STYLE_DATA];
+            return data[index + STYLE_DATA];
         } else if (type == TypedValue.TYPE_DIMENSION) {
-            return TypedValue.complexToDimensionPixelSize(
-                    data[index + AssetManager.STYLE_DATA], mMetrics);
+            return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
         }
 
         return defValue;
@@ -833,15 +836,14 @@
         }
 
         final int attrIndex = index;
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
 
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return defValue;
         } else if (type == TypedValue.TYPE_FRACTION) {
-            return TypedValue.complexToFraction(
-                data[index+AssetManager.STYLE_DATA], base, pbase);
+            return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
         } else if (type == TypedValue.TYPE_ATTRIBUTE) {
             final TypedValue value = mValue;
             getValueAt(index, value);
@@ -874,10 +876,10 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
-            final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
+        if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
+            final int resid = data[index + STYLE_RESOURCE_ID];
             if (resid != 0) {
                 return resid;
             }
@@ -902,10 +904,10 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
-            return data[index + AssetManager.STYLE_DATA];
+        if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+            return data[index + STYLE_DATA];
         }
         return defValue;
     }
@@ -939,7 +941,7 @@
         }
 
         final TypedValue value = mValue;
-        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
                         "Failed to resolve attribute at index " + index + ": " + value);
@@ -975,7 +977,7 @@
         }
 
         final TypedValue value = mValue;
-        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                 throw new UnsupportedOperationException(
                         "Failed to resolve attribute at index " + index + ": " + value);
@@ -1006,7 +1008,7 @@
         }
 
         final TypedValue value = mValue;
-        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             return mResources.getTextArray(value.resourceId);
         }
         return null;
@@ -1027,7 +1029,7 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
+        return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
     }
 
     /**
@@ -1043,8 +1045,8 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
-        return mData[index + AssetManager.STYLE_TYPE];
+        index *= STYLE_NUM_ENTRIES;
+        return mData[index + STYLE_TYPE];
     }
 
     /**
@@ -1063,9 +1065,9 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         return type != TypedValue.TYPE_NULL;
     }
 
@@ -1084,11 +1086,11 @@
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
 
-        index *= AssetManager.STYLE_NUM_ENTRIES;
+        index *= STYLE_NUM_ENTRIES;
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         return type != TypedValue.TYPE_NULL
-                || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
+                || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
     }
 
     /**
@@ -1109,7 +1111,7 @@
         }
 
         final TypedValue value = mValue;
-        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+        if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
             return value;
         }
         return null;
@@ -1181,16 +1183,16 @@
         final int[] data = mData;
         final int N = length();
         for (int i = 0; i < N; i++) {
-            final int index = i * AssetManager.STYLE_NUM_ENTRIES;
-            if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
+            final int index = i * STYLE_NUM_ENTRIES;
+            if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
                 // Not an attribute, ignore.
                 continue;
             }
 
             // Null the entry so that we can safely call getZzz().
-            data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
+            data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
 
-            final int attr = data[index + AssetManager.STYLE_DATA];
+            final int attr = data[index + STYLE_DATA];
             if (attr == 0) {
                 // Useless data, ignore.
                 continue;
@@ -1231,45 +1233,44 @@
         final int[] data = mData;
         final int N = length();
         for (int i = 0; i < N; i++) {
-            final int index = i * AssetManager.STYLE_NUM_ENTRIES;
-            final int type = data[index + AssetManager.STYLE_TYPE];
+            final int index = i * STYLE_NUM_ENTRIES;
+            final int type = data[index + STYLE_TYPE];
             if (type == TypedValue.TYPE_NULL) {
                 continue;
             }
             changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
-                    data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+                    data[index + STYLE_CHANGING_CONFIGURATIONS]);
         }
         return changingConfig;
     }
 
     private boolean getValueAt(int index, TypedValue outValue) {
         final int[] data = mData;
-        final int type = data[index+AssetManager.STYLE_TYPE];
+        final int type = data[index + STYLE_TYPE];
         if (type == TypedValue.TYPE_NULL) {
             return false;
         }
         outValue.type = type;
-        outValue.data = data[index+AssetManager.STYLE_DATA];
-        outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
-        outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
+        outValue.data = data[index + STYLE_DATA];
+        outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
+        outValue.resourceId = data[index + STYLE_RESOURCE_ID];
         outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
-                data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
-        outValue.density = data[index+AssetManager.STYLE_DENSITY];
+                data[index + STYLE_CHANGING_CONFIGURATIONS]);
+        outValue.density = data[index + STYLE_DENSITY];
         outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
         return true;
     }
 
     private CharSequence loadStringValueAt(int index) {
         final int[] data = mData;
-        final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+        final int cookie = data[index + STYLE_ASSET_COOKIE];
         if (cookie < 0) {
             if (mXml != null) {
-                return mXml.getPooledString(
-                    data[index+AssetManager.STYLE_DATA]);
+                return mXml.getPooledString(data[index + STYLE_DATA]);
             }
             return null;
         }
-        return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
+        return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
     }
 
     /** @hide */
diff --git a/android/content/res/XmlBlock.java b/android/content/res/XmlBlock.java
index e6b9574..d4ccffb 100644
--- a/android/content/res/XmlBlock.java
+++ b/android/content/res/XmlBlock.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.annotation.Nullable;
 import android.util.TypedValue;
 
 import com.android.internal.util.XmlUtils;
@@ -33,7 +34,7 @@
  * 
  * {@hide}
  */
-final class XmlBlock {
+final class XmlBlock implements AutoCloseable {
     private static final boolean DEBUG=false;
 
     public XmlBlock(byte[] data) {
@@ -48,6 +49,7 @@
         mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
     }
 
+    @Override
     public void close() {
         synchronized (this) {
             if (mOpen) {
@@ -478,13 +480,13 @@
      *  are doing!  The given native object must exist for the entire lifetime
      *  of this newly creating XmlBlock.
      */
-    XmlBlock(AssetManager assets, long xmlBlock) {
+    XmlBlock(@Nullable AssetManager assets, long xmlBlock) {
         mAssets = assets;
         mNative = xmlBlock;
         mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
     }
 
-    private final AssetManager mAssets;
+    private @Nullable final AssetManager mAssets;
     private final long mNative;
     /*package*/ final StringBlock mStrings;
     private boolean mOpen = true;
diff --git a/android/content/res/XmlResourceParser.java b/android/content/res/XmlResourceParser.java
index 6be9b9e..86f4ba6 100644
--- a/android/content/res/XmlResourceParser.java
+++ b/android/content/res/XmlResourceParser.java
@@ -27,6 +27,8 @@
  * it is done reading the resource.
  */
 public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
+    String getAttributeNamespace (int index);
+
     /**
      * Close this parser. Calls on the interface are no longer valid after this call.
      */
diff --git a/android/database/AbstractCursor.java b/android/database/AbstractCursor.java
index fdb702f..76fa008 100644
--- a/android/database/AbstractCursor.java
+++ b/android/database/AbstractCursor.java
@@ -395,7 +395,7 @@
      */
     @Override
     public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
-        setNotificationUri(cr, notifyUri, UserHandle.myUserId());
+        setNotificationUri(cr, notifyUri, cr.getUserId());
     }
 
     /** @hide - set the notification uri but with an observer for a particular user's view */
diff --git a/android/database/CursorWindow.java b/android/database/CursorWindow.java
index f84ec65..a748f4d 100644
--- a/android/database/CursorWindow.java
+++ b/android/database/CursorWindow.java
@@ -28,6 +28,7 @@
 import android.util.LongSparseArray;
 import android.util.SparseIntArray;
 
+import dalvik.annotation.optimization.FastNative;
 import dalvik.system.CloseGuard;
 
 /**
@@ -62,28 +63,43 @@
     private static native void nativeDispose(long windowPtr);
     private static native void nativeWriteToParcel(long windowPtr, Parcel parcel);
 
-    private static native void nativeClear(long windowPtr);
-
-    private static native int nativeGetNumRows(long windowPtr);
-    private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
-    private static native boolean nativeAllocRow(long windowPtr);
-    private static native void nativeFreeLastRow(long windowPtr);
-
-    private static native int nativeGetType(long windowPtr, int row, int column);
+    private static native String nativeGetName(long windowPtr);
     private static native byte[] nativeGetBlob(long windowPtr, int row, int column);
     private static native String nativeGetString(long windowPtr, int row, int column);
-    private static native long nativeGetLong(long windowPtr, int row, int column);
-    private static native double nativeGetDouble(long windowPtr, int row, int column);
     private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column,
             CharArrayBuffer buffer);
-
     private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column);
-    private static native boolean nativePutString(long windowPtr, String value, int row, int column);
+    private static native boolean nativePutString(long windowPtr, String value,
+            int row, int column);
+
+    // Below native methods don't do unconstrained work, so are FastNative for performance
+
+    @FastNative
+    private static native void nativeClear(long windowPtr);
+
+    @FastNative
+    private static native int nativeGetNumRows(long windowPtr);
+    @FastNative
+    private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
+    @FastNative
+    private static native boolean nativeAllocRow(long windowPtr);
+    @FastNative
+    private static native void nativeFreeLastRow(long windowPtr);
+
+    @FastNative
+    private static native int nativeGetType(long windowPtr, int row, int column);
+    @FastNative
+    private static native long nativeGetLong(long windowPtr, int row, int column);
+    @FastNative
+    private static native double nativeGetDouble(long windowPtr, int row, int column);
+
+    @FastNative
     private static native boolean nativePutLong(long windowPtr, long value, int row, int column);
+    @FastNative
     private static native boolean nativePutDouble(long windowPtr, double value, int row, int column);
+    @FastNative
     private static native boolean nativePutNull(long windowPtr, int row, int column);
 
-    private static native String nativeGetName(long windowPtr);
 
     /**
      * Creates a new empty cursor window and gives it a name.
diff --git a/android/database/SQLiteDatabasePerfTest.java b/android/database/SQLiteDatabasePerfTest.java
index 7a32c0c..e2b75c3 100644
--- a/android/database/SQLiteDatabasePerfTest.java
+++ b/android/database/SQLiteDatabasePerfTest.java
@@ -118,6 +118,52 @@
     }
 
     @Test
+    public void testCursorIterateForward() {
+        // A larger dataset is needed to exceed default CursorWindow size
+        int datasetSize = DEFAULT_DATASET_SIZE * 50;
+        insertT1TestDataSet(datasetSize);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            try (Cursor cursor = mDatabase
+                    .rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 ORDER BY _ID", null)) {
+                int i = 0;
+                while(cursor.moveToNext()) {
+                    assertEquals(i, cursor.getInt(0));
+                    assertEquals(i, cursor.getInt(1));
+                    assertEquals("T1Value" + i, cursor.getString(2));
+                    assertEquals(1.1 * i, cursor.getDouble(3), 0.0000001d);
+                    i++;
+                }
+                assertEquals(datasetSize, i);
+            }
+        }
+    }
+
+    @Test
+    public void testCursorIterateBackwards() {
+        // A larger dataset is needed to exceed default CursorWindow size
+        int datasetSize = DEFAULT_DATASET_SIZE * 50;
+        insertT1TestDataSet(datasetSize);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            try (Cursor cursor = mDatabase
+                    .rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 ORDER BY _ID", null)) {
+                int i = datasetSize - 1;
+                while(cursor.moveToPosition(i)) {
+                    assertEquals(i, cursor.getInt(0));
+                    assertEquals(i, cursor.getInt(1));
+                    assertEquals("T1Value" + i, cursor.getString(2));
+                    assertEquals(1.1 * i, cursor.getDouble(3), 0.0000001d);
+                    i--;
+                }
+                assertEquals(-1, i);
+            }
+        }
+    }
+
+    @Test
     public void testInnerJoin() {
         mDatabase.setForeignKeyConstraintsEnabled(true);
         mDatabase.beginTransaction();
@@ -201,8 +247,12 @@
     }
 
     private void insertT1TestDataSet() {
+        insertT1TestDataSet(DEFAULT_DATASET_SIZE);
+    }
+
+    private void insertT1TestDataSet(int size) {
         mDatabase.beginTransaction();
-        for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
+        for (int i = 0; i < size; i++) {
             mDatabase.execSQL("INSERT INTO T1 VALUES (?, ?, ?, ?)",
                     new Object[]{i, i, "T1Value" + i, i * 1.1});
         }
diff --git a/android/database/sqlite/SQLiteConnection.java b/android/database/sqlite/SQLiteConnection.java
index 7717b8d..316c796 100644
--- a/android/database/sqlite/SQLiteConnection.java
+++ b/android/database/sqlite/SQLiteConnection.java
@@ -292,8 +292,8 @@
             final boolean walEnabled =
                     (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
             // Use compatibility WAL unless an app explicitly set journal/synchronous mode
-            final boolean useCompatibilityWal = mConfiguration.journalMode == null
-                    && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal;
+            // or DISABLE_COMPATIBILITY_WAL flag is set
+            final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal();
             if (walEnabled || useCompatibilityWal) {
                 setJournalMode("WAL");
                 if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) {
@@ -423,8 +423,8 @@
         boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
                 != mConfiguration.foreignKeyConstraintsEnabled;
         boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
-                & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0
-                || configuration.useCompatibilityWal != mConfiguration.useCompatibilityWal;
+                & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING
+                | SQLiteDatabase.DISABLE_COMPATIBILITY_WAL)) != 0;
         boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
 
         // Update configuration parameters.
diff --git a/android/database/sqlite/SQLiteConnectionPool.java b/android/database/sqlite/SQLiteConnectionPool.java
index b211700..e519302 100644
--- a/android/database/sqlite/SQLiteConnectionPool.java
+++ b/android/database/sqlite/SQLiteConnectionPool.java
@@ -23,6 +23,7 @@
 import android.os.Message;
 import android.os.OperationCanceledException;
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrefixPrinter;
 import android.util.Printer;
@@ -315,7 +316,12 @@
                 }
             }
 
-            if (mConfiguration.openFlags != configuration.openFlags) {
+            // We should do in-place switching when transitioning from compatibility WAL
+            // to rollback journal. Otherwise transient connection state will be lost
+            boolean onlyCompatWalChanged = (mConfiguration.openFlags ^ configuration.openFlags)
+                    == SQLiteDatabase.DISABLE_COMPATIBILITY_WAL;
+
+            if (!onlyCompatWalChanged && mConfiguration.openFlags != configuration.openFlags) {
                 // If we are changing open flags and WAL mode at the same time, then
                 // we have no choice but to close the primary connection beforehand
                 // because there can only be one connection open when we change WAL mode.
@@ -422,6 +428,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private boolean recycleConnectionLocked(SQLiteConnection connection,
             AcquiredConnectionStatus status) {
         if (status == AcquiredConnectionStatus.RECONFIGURE) {
@@ -531,6 +538,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void closeAvailableConnectionsAndLogExceptionsLocked() {
         closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
 
@@ -541,6 +549,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private boolean closeAvailableConnectionLocked(int connectionId) {
         final int count = mAvailableNonPrimaryConnections.size();
         for (int i = count - 1; i >= 0; i--) {
@@ -562,6 +571,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
         final int count = mAvailableNonPrimaryConnections.size();
         for (int i = 0; i < count; i++) {
@@ -581,6 +591,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void closeExcessConnectionsAndLogExceptionsLocked() {
         int availableCount = mAvailableNonPrimaryConnections.size();
         while (availableCount-- > mMaxConnectionPoolSize - 1) {
@@ -591,6 +602,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
         try {
             connection.close(); // might throw
@@ -609,6 +621,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void reconfigureAllConnectionsLocked() {
         if (mAvailablePrimaryConnection != null) {
             try {
@@ -776,6 +789,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
         if (waiter.mAssignedConnection != null || waiter.mException != null) {
             // Waiter is done waiting but has not woken up yet.
@@ -848,6 +862,7 @@
     }
 
     // Can't throw.
+    @GuardedBy("mLock")
     private void wakeConnectionWaitersLocked() {
         // Unpark all waiters that have requests that we can fulfill.
         // This method is designed to not throw runtime exceptions, although we might send
@@ -910,6 +925,7 @@
     }
 
     // Might throw.
+    @GuardedBy("mLock")
     private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
         // If the primary connection is available, acquire it now.
         SQLiteConnection connection = mAvailablePrimaryConnection;
@@ -935,6 +951,7 @@
     }
 
     // Might throw.
+    @GuardedBy("mLock")
     private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
             String sql, int connectionFlags) {
         // Try to acquire the next connection in the queue.
@@ -974,6 +991,7 @@
     }
 
     // Might throw.
+    @GuardedBy("mLock")
     private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
         try {
             final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
@@ -1094,6 +1112,11 @@
             printer.println("  Open: " + mIsOpen);
             printer.println("  Max connections: " + mMaxConnectionPoolSize);
             printer.println("  Total execution time: " + mTotalExecutionTimeCounter);
+            printer.println("  Configuration: openFlags=" + mConfiguration.openFlags
+                    + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal()
+                    + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode)
+                    + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode));
+
             if (SQLiteCompatibilityWalFlags.areFlagsSet()) {
                 printer.println("  Compatibility WAL settings: compatibility_wal_supported="
                         + SQLiteCompatibilityWalFlags
diff --git a/android/database/sqlite/SQLiteDatabase.java b/android/database/sqlite/SQLiteDatabase.java
index c1c0812..b463d8d 100644
--- a/android/database/sqlite/SQLiteDatabase.java
+++ b/android/database/sqlite/SQLiteDatabase.java
@@ -253,6 +253,13 @@
     public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
 
     /**
+     * Open flag: Flag for {@link #openDatabase} to disable Compatibility WAL when opening database.
+     *
+     * @hide
+     */
+    public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000;
+
+    /**
      * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
      *
      * Each prepared-statement is between 1K - 6K, depending on the complexity of the
@@ -288,10 +295,10 @@
         mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs;
         mConfigurationLocked.journalMode = journalMode;
         mConfigurationLocked.syncMode = syncMode;
-        mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
-        if (!mConfigurationLocked.isInMemoryDb() && SQLiteCompatibilityWalFlags.areFlagsSet()) {
-            mConfigurationLocked.useCompatibilityWal = SQLiteCompatibilityWalFlags
-                    .isCompatibilityWalSupported();
+        if (!SQLiteGlobal.isCompatibilityWalSupported() || (
+                SQLiteCompatibilityWalFlags.areFlagsSet() && !SQLiteCompatibilityWalFlags
+                        .isCompatibilityWalSupported())) {
+            mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL;
         }
     }
 
@@ -2006,7 +2013,6 @@
      *     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 {@link #enableWriteAheadLogging}
@@ -2083,21 +2089,21 @@
         synchronized (mLock) {
             throwIfNotOpenLocked();
 
-            final boolean oldUseCompatibilityWal = mConfigurationLocked.useCompatibilityWal;
             final int oldFlags = mConfigurationLocked.openFlags;
-            if (!oldUseCompatibilityWal && (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
+            final boolean walDisabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0;
+            final boolean compatibilityWalDisabled = (oldFlags & DISABLE_COMPATIBILITY_WAL) != 0;
+            if (walDisabled && compatibilityWalDisabled) {
                 return;
             }
 
             mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
-            // If an app explicitly disables WAL, do not even use compatibility mode
-            mConfigurationLocked.useCompatibilityWal = false;
+            // If an app explicitly disables WAL, compatibility mode should be disabled too
+            mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL;
 
             try {
                 mConnectionPoolLocked.reconfigure(mConfigurationLocked);
             } catch (RuntimeException ex) {
                 mConfigurationLocked.openFlags = oldFlags;
-                mConfigurationLocked.useCompatibilityWal = oldUseCompatibilityWal;
                 throw ex;
             }
         }
diff --git a/android/database/sqlite/SQLiteDatabaseConfiguration.java b/android/database/sqlite/SQLiteDatabaseConfiguration.java
index a14df1e..8b9dfcf 100644
--- a/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -111,15 +111,6 @@
     public long idleConnectionTimeoutMs = Long.MAX_VALUE;
 
     /**
-     * Enables compatibility WAL mode. Applications cannot explicitly choose compatibility WAL mode,
-     * therefore it is not exposed as a flag.
-     *
-     * <p>In this mode, only database journal mode will be changed, connection pool
-     * size will still be limited to a single connection.
-     */
-    public boolean useCompatibilityWal;
-
-    /**
      * Journal mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set.
      * <p>Default is returned by {@link SQLiteGlobal#getDefaultJournalMode()}
      */
@@ -191,7 +182,6 @@
         lookasideSlotSize = other.lookasideSlotSize;
         lookasideSlotCount = other.lookasideSlotCount;
         idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;
-        useCompatibilityWal = other.useCompatibilityWal;
         journalMode = other.journalMode;
         syncMode = other.syncMode;
     }
@@ -204,6 +194,11 @@
         return path.equalsIgnoreCase(MEMORY_DB_PATH);
     }
 
+    boolean useCompatibilityWal() {
+        return journalMode == null && syncMode == null
+                && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0;
+    }
+
     private static String stripPathForLogs(String path) {
         if (path.indexOf('@') == -1) {
             return path;
diff --git a/android/database/sqlite/SQLiteOpenHelper.java b/android/database/sqlite/SQLiteOpenHelper.java
index a2991e6..7ff6635 100644
--- a/android/database/sqlite/SQLiteOpenHelper.java
+++ b/android/database/sqlite/SQLiteOpenHelper.java
@@ -58,7 +58,7 @@
 
     private SQLiteDatabase mDatabase;
     private boolean mIsInitializing;
-    private final SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
+    private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
 
     /**
      * Create a helper object to create, open, and/or manage a database.
@@ -163,8 +163,7 @@
         mName = name;
         mNewVersion = version;
         mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
-        mOpenParamsBuilder = openParamsBuilder;
-        mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+        setOpenParamsBuilder(openParamsBuilder);
     }
 
     /**
@@ -198,6 +197,8 @@
                 }
                 mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled);
             }
+            // Compatibility WAL is disabled if an app disables or enables WAL
+            mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL);
         }
     }
 
@@ -230,6 +231,30 @@
     }
 
     /**
+     * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
+     * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
+     * opening the database
+     *
+     * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
+     * @throws IllegalStateException if the database is already open
+     */
+    public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
+        Preconditions.checkNotNull(openParams);
+        synchronized (this) {
+            if (mDatabase != null && mDatabase.isOpen()) {
+                throw new IllegalStateException(
+                        "OpenParams cannot be set after opening the database");
+            }
+            setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
+        }
+    }
+
+    private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
+        mOpenParamsBuilder = openParamsBuilder;
+        mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+    }
+
+    /**
      * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
      * before it is closed and removed from the pool.
      *
diff --git a/android/database/sqlite/SQLiteQueryBuilder.java b/android/database/sqlite/SQLiteQueryBuilder.java
index 56cba79..c6c676f 100644
--- a/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/android/database/sqlite/SQLiteQueryBuilder.java
@@ -31,7 +31,7 @@
 import java.util.regex.Pattern;
 
 /**
- * This is a convience class that helps build SQL queries to be sent to
+ * This is a convenience class that helps build SQL queries to be sent to
  * {@link SQLiteDatabase} objects.
  */
 public class SQLiteQueryBuilder
diff --git a/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
index 4709d35..9ba7e09 100644
--- a/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ b/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
@@ -15,6 +15,8 @@
  */
 package android.ext.services.autofill;
 
+import static android.ext.services.autofill.EditDistanceScorer.DEFAULT_ALGORITHM;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
@@ -29,8 +31,6 @@
 public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
 
     private static final String TAG = "AutofillFieldClassificationServiceImpl";
-    // TODO(b/70291841): set to false before launching
-    private static final boolean DEBUG = true;
 
     @Nullable
     @Override
@@ -40,30 +40,13 @@
         if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
             Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues ("
                     + userDataValues + ")");
-            // TODO(b/70939974): add unit test
             return null;
         }
-        if (algorithmName != null && !algorithmName.equals(EditDistanceScorer.NAME)) {
+        if (algorithmName != null && !algorithmName.equals(DEFAULT_ALGORITHM)) {
             Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using "
-                    + EditDistanceScorer.NAME + " instead");
+                    + DEFAULT_ALGORITHM + " instead");
         }
 
-        final String actualAlgorithmName = EditDistanceScorer.NAME;
-        final int actualValuesSize = actualValues.size();
-        final int userDataValuesSize = userDataValues.size();
-        if (DEBUG) {
-            Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
-                    + userDataValuesSize + " matrix for " + actualAlgorithmName);
-        }
-        final float[][] scores = new float[actualValuesSize][userDataValuesSize];
-
-        final EditDistanceScorer algorithm = EditDistanceScorer.getInstance();
-        for (int i = 0; i < actualValuesSize; i++) {
-            for (int j = 0; j < userDataValuesSize; j++) {
-                final float score = algorithm.getScore(actualValues.get(i), userDataValues.get(j));
-                scores[i][j] = score;
-            }
-        }
-        return scores;
+        return EditDistanceScorer.getScores(actualValues, userDataValues);
     }
 }
diff --git a/android/ext/services/autofill/EditDistanceScorer.java b/android/ext/services/autofill/EditDistanceScorer.java
index d2e804a..302b160 100644
--- a/android/ext/services/autofill/EditDistanceScorer.java
+++ b/android/ext/services/autofill/EditDistanceScorer.java
@@ -16,53 +16,133 @@
 package android.ext.services.autofill;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
 import android.view.autofill.AutofillValue;
 
-/**
- * Helper used to calculate the classification score between an actual {@link AutofillValue} filled
- * by the user and the expected value predicted by an autofill service.
- */
-// TODO(b/70291841): explain algorithm once it's fully implemented
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
 final class EditDistanceScorer {
 
-    private static final EditDistanceScorer sInstance = new EditDistanceScorer();
+    private static final String TAG = "EditDistanceScorer";
 
-    public static final String NAME = "EDIT_DISTANCE";
+    // TODO(b/70291841): STOPSHIP - set to false before launching
+    private static final boolean DEBUG = true;
+
+    static final String DEFAULT_ALGORITHM = "EDIT_DISTANCE";
 
     /**
-     * Gets the singleton instance.
-     */
-    public static EditDistanceScorer getInstance() {
-        return sInstance;
-    }
-
-    private EditDistanceScorer() {
-    }
-
-    /**
-     * Returns the classification score between an actual {@link AutofillValue} filled
-     * by the user and the expected value predicted by an autofill service.
+     * Gets the field classification score of 2 values based on the edit distance between them.
      *
-     * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
-     * partial mathces are something in between, typically using edit-distance algorithms.
-     *
+     * <p>The score is defined as: @(max_length - edit_distance) / max_length
      */
-    public float getScore(@NonNull AutofillValue actualValue, @NonNull String userDataValue) {
+    @VisibleForTesting
+    static float getScore(@Nullable AutofillValue actualValue, @Nullable String userDataValue) {
         if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
-        // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
-        // partial match when number of chars match
-        final String textValue = actualValue.getTextValue().toString();
-        final int total = textValue.length();
-        if (total != userDataValue.length()) return 0F;
 
-        int matches = 0;
-        for (int i = 0; i < total; i++) {
-            if (Character.toLowerCase(textValue.charAt(i)) == Character
-                    .toLowerCase(userDataValue.charAt(i))) {
-                matches++;
+        final String actualValueText = actualValue.getTextValue().toString();
+        final int actualValueLength = actualValueText.length();
+        final int userDatalength = userDataValue.length();
+        if (userDatalength == 0) {
+            return (actualValueLength == 0) ? 1 : 0;
+        }
+
+        final int distance = editDistance(actualValueText.toLowerCase(),
+                userDataValue.toLowerCase());
+        final int maxLength = Math.max(actualValueLength, userDatalength);
+        return ((float) maxLength - distance) / maxLength;
+    }
+
+    /**
+     * Computes the edit distance (number of insertions, deletions or substitutions to edit one
+     * string into the other) between two strings. In particular, this will compute the Levenshtein
+     * distance.
+     *
+     * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
+     *
+     * @param s the first string to compare
+     * @param t the second string to compare
+     * @return the edit distance between the two strings
+     */
+    // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java
+    public static int editDistance(@NonNull String s, @NonNull String t) {
+        return editDistance(s, t, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Computes the edit distance (number of insertions, deletions or substitutions to edit one
+     * string into the other) between two strings. In particular, this will compute the Levenshtein
+     * distance.
+     *
+     * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
+     *
+     * @param s the first string to compare
+     * @param t the second string to compare
+     * @param max the maximum edit distance that we care about; if for example the string length
+     *     delta is greater than this we don't bother computing the exact edit distance since the
+     *     caller has indicated they're not interested in the result
+     * @return the edit distance between the two strings, or some other value greater than that if
+     *     the edit distance is at least as big as the {@code max} parameter
+     */
+    // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java
+    private static int editDistance(@NonNull String s, @NonNull String t, int max) {
+        if (s.equals(t)) {
+            return 0;
+        }
+
+        if (Math.abs(s.length() - t.length()) > max) {
+            // The string lengths differ more than the allowed edit distance;
+            // no point in even attempting to compute the edit distance (requires
+            // O(n*m) storage and O(n*m) speed, where n and m are the string lengths)
+            return Integer.MAX_VALUE;
+        }
+
+        int m = s.length();
+        int n = t.length();
+        int[][] d = new int[m + 1][n + 1];
+        for (int i = 0; i <= m; i++) {
+            d[i][0] = i;
+        }
+        for (int j = 0; j <= n; j++) {
+            d[0][j] = j;
+        }
+        for (int j = 1; j <= n; j++) {
+            for (int i = 1; i <= m; i++) {
+                if (s.charAt(i - 1) == t.charAt(j - 1)) {
+                    d[i][j] = d[i - 1][j - 1];
+                } else {
+                    int deletion = d[i - 1][j] + 1;
+                    int insertion = d[i][j - 1] + 1;
+                    int substitution = d[i - 1][j - 1] + 1;
+                    d[i][j] = Math.min(deletion, Math.min(insertion, substitution));
+                }
             }
         }
 
-        return ((float) matches) / total;
+        return d[m][n];
     }
+    /**
+     * Gets the scores in a batch.
+     */
+    static float[][] getScores(@NonNull List<AutofillValue> actualValues,
+            @NonNull List<String> userDataValues) {
+        final int actualValuesSize = actualValues.size();
+        final int userDataValuesSize = userDataValues.size();
+        if (DEBUG) {
+            Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
+                    + userDataValuesSize + " matrix for " + DEFAULT_ALGORITHM);
+        }
+        final float[][] scores = new float[actualValuesSize][userDataValuesSize];
+
+        for (int i = 0; i < actualValuesSize; i++) {
+            for (int j = 0; j < userDataValuesSize; j++) {
+                final float score = getScore(actualValues.get(i), userDataValues.get(j));
+                scores[i][j] = score;
+            }
+        }
+        return scores;
+    }
+
 }
diff --git a/android/ext/services/notification/Assistant.java b/android/ext/services/notification/Assistant.java
index 6fe8975..9a66b07 100644
--- a/android/ext/services/notification/Assistant.java
+++ b/android/ext/services/notification/Assistant.java
@@ -94,7 +94,7 @@
                 infile = mFile.openRead();
                 readXml(infile);
             } catch (FileNotFoundException e) {
-                // No data yet
+                Log.d(TAG, "File doesn't exist or isn't readable yet");
             } catch (IOException e) {
                 Log.e(TAG, "Unable to read channel impressions", e);
             } catch (NumberFormatException | XmlPullParserException e) {
diff --git a/android/ext/services/notification/ChannelImpressions.java b/android/ext/services/notification/ChannelImpressions.java
index 4ad4b24..de2659f 100644
--- a/android/ext/services/notification/ChannelImpressions.java
+++ b/android/ext/services/notification/ChannelImpressions.java
@@ -30,7 +30,7 @@
     private static final String TAG = "ExtAssistant.CI";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    static final double DISMISS_TO_VIEW_RATIO_LIMIT = .8;
+    static final double DISMISS_TO_VIEW_RATIO_LIMIT = .4;
     static final int STREAK_LIMIT = 2;
     static final String ATT_DISMISSALS = "dismisses";
     static final String ATT_VIEWS = "views";
diff --git a/android/graphics/BaseCanvas.java b/android/graphics/BaseCanvas.java
index 627d551..71ee6c2 100644
--- a/android/graphics/BaseCanvas.java
+++ b/android/graphics/BaseCanvas.java
@@ -22,8 +22,7 @@
 import android.annotation.Size;
 import android.graphics.Canvas.VertexMode;
 import android.text.GraphicsOperations;
-import android.text.MeasuredParagraph;
-import android.text.MeasuredText;
+import android.text.PrecomputedText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -42,13 +41,19 @@
     /**
      * Should only be assigned in constructors (or setBitmap if software canvas),
      * freed by NativeAllocation.
+     * @hide
      */
     protected long mNativeCanvasWrapper;
 
     /**
      * Used to determine when compatibility scaling is in effect.
+     * @hide
      */
     protected int mScreenDensity = Bitmap.DENSITY_NONE;
+
+    /**
+     * @hide
+     */
     protected int mDensity = Bitmap.DENSITY_NONE;
     private boolean mAllowHwBitmapsInSwMode = false;
 
@@ -455,8 +460,7 @@
 
         throwIfHasHwBitmapInSwMode(paint);
         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
-                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */,
-                0 /* measured text offset */);
+                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
     }
 
     public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
@@ -487,19 +491,16 @@
             char[] buf = TemporaryBuffer.obtain(contextLen);
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
-            int measuredTextOffset = 0;
-            if (text instanceof MeasuredText) {
-                MeasuredText mt = (MeasuredText) text;
+            if (text instanceof PrecomputedText) {
+                PrecomputedText mt = (PrecomputedText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
-                    // Only suppor the same paragraph.
+                    // Only suppor the text in the same paragraph.
                     measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
-                    measuredTextOffset = start - mt.getParagraphStart(paraIndex);
                 }
             }
             nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
-                    0, contextLen, x, y, isRtl, paint.getNativeInstance(),
-                    measuredTextPtr, measuredTextOffset);
+                    0, contextLen, x, y, isRtl, paint.getNativeInstance(), measuredTextPtr);
             TemporaryBuffer.recycle(buf);
         }
     }
@@ -541,10 +542,19 @@
         return mAllowHwBitmapsInSwMode;
     }
 
+    /**
+     * @hide
+     */
+    protected void onHwBitmapInSwMode() {
+        if (!mAllowHwBitmapsInSwMode) {
+            throw new IllegalArgumentException(
+                    "Software rendering doesn't support hardware bitmaps");
+        }
+    }
+
     private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
-        if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
-                && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
-            throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+            onHwBitmapInSwMode();
         }
     }
 
@@ -639,7 +649,7 @@
 
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativeMeasuredText, int measuredTextOffset);
+            long nativePrecomputedText);
 
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
             long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
diff --git a/android/graphics/BaseCanvas_Delegate.java b/android/graphics/BaseCanvas_Delegate.java
index 89fccc7..9260099 100644
--- a/android/graphics/BaseCanvas_Delegate.java
+++ b/android/graphics/BaseCanvas_Delegate.java
@@ -507,7 +507,8 @@
     @LayoutlibDelegate
     /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
             int start, int count, int contextStart, int contextCount,
-            float x, float y, boolean isRtl, long paint) {
+            float x, float y, boolean isRtl, long paint,
+            long nativeMeasuredText, int measuredTextOffset) {
         drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint);
     }
 
diff --git a/android/graphics/BidiRenderer.java b/android/graphics/BidiRenderer.java
index 3dc1d41..9a102c1 100644
--- a/android/graphics/BidiRenderer.java
+++ b/android/graphics/BidiRenderer.java
@@ -43,7 +43,10 @@
  */
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
-    private static String JAVA_VENDOR = System.getProperty("java.vendor");
+    private static final String JETBRAINS_VENDOR_ID = "JetBrains s.r.o";
+    private static final String JAVA_VENDOR = System.getProperty("java.vendor");
+    /** When scaleX is bigger than this, we need to apply the workaround for http://b.android.com/211659 */
+    private static final double SCALEX_WORKAROUND_LIMIT = 9;
 
     private static class ScriptRun {
         private final int start;
@@ -209,23 +212,42 @@
      */
     private void render(int start, int limit, Font font, int flag, float[] advances,
             int advancesIndex, boolean draw) {
+        FontRenderContext frc = mGraphics != null ? mGraphics.getFontRenderContext() :
+                    Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
 
-        FontRenderContext frc;
-        if (mGraphics != null) {
-            frc = mGraphics.getFontRenderContext();
-        } else {
-            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+        boolean frcIsAntialiased = frc.isAntiAliased();
+        boolean useAntialiasing = mPaint.isAntiAliased();
 
-            // Metrics obtained this way don't have anti-aliasing set. So,
-            // we create a new FontRenderContext with anti-aliasing set.
+        if (frcIsAntialiased) {
+            if (!useAntialiasing) {
+                // The context has antialiasing enabled but the paint does not. We need to
+                // disable it
+                frc = new FontRenderContext(font.getTransform(), false,
+                        frc.usesFractionalMetrics());
+            } else {
+                // In this case both the paint and the context antialising match but we need
+                // to check for a bug in the JDK
+                // Workaround for http://b.android.com/211659 (disable antialiasing)
+                if (font.isTransformed()) {
+                    AffineTransform transform = font.getTransform();
+                    if (transform.getScaleX() >= SCALEX_WORKAROUND_LIMIT &&
+                            JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
+                        frc = new FontRenderContext(transform, false, frc.usesFractionalMetrics());
+                    }
+                }
+            }
+        } else if (useAntialiasing) {
+            // The context does not have antialiasing enabled but the paint does. We need to
+            // enable it unless we need to avoid the JDK bug
+
             AffineTransform transform = font.getTransform();
-            if (mPaint.isAntiAliased() &&
-                    // Workaround for http://b.android.com/211659
-                    (transform.getScaleX() <= 9.9 ||
-                    !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
-                frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
+            // Workaround for http://b.android.com/211659 (disable antialiasing)
+            if (transform.getScaleX() < SCALEX_WORKAROUND_LIMIT ||
+                    !JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
+                frc = new FontRenderContext(font.getTransform(), true, frc.usesFractionalMetrics());
             }
         }
+
         GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
         int[] ci = gv.getGlyphCharIndices(0, ng, null);
diff --git a/android/graphics/Bitmap.java b/android/graphics/Bitmap.java
index 0072012..e8ede94 100644
--- a/android/graphics/Bitmap.java
+++ b/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@
 import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.io.OutputStream;
@@ -457,6 +461,11 @@
          *
          * This configuration may be useful when using opaque bitmaps
          * that do not require high color fidelity.
+         *
+         * <p>Use this formula to pack into 16 bits:</p>
+         * <pre class="prettyprint">
+         * short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f);
+         * </pre>
          */
         RGB_565     (3),
 
@@ -489,6 +498,11 @@
          *
          * This configuration is very flexible and offers the best
          * quality. It should be used whenever possible.
+         *
+         * <p>Use this formula to pack into 32 bits:</p>
+         * <pre class="prettyprint">
+         * int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);
+         * </pre>
          */
         ARGB_8888   (5),
 
@@ -499,6 +513,11 @@
          *
          * This configuration is particularly suited for wide-gamut and
          * HDR content.
+         *
+         * <p>Use this formula to pack into 64 bits:</p>
+         * <pre class="prettyprint">
+         * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
+         * </pre>
          */
         RGBA_F16    (6),
 
@@ -1171,6 +1190,82 @@
     }
 
     /**
+     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+     *
+     * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+     * width and height the same as the Picture's width and height and a Config.HARDWARE
+     * config.
+     *
+     * @param source The recorded {@link Picture} of drawing commands that will be
+     *               drawn into the returned Bitmap.
+     * @return An immutable bitmap with a HARDWARE config whose contents are created
+     * from the recorded drawing commands in the Picture source.
+     */
+    public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+        return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+    }
+
+    /**
+     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+     *
+     * The bitmap will be immutable with the given width and height. If the width and height
+     * are not the same as the Picture's width & height, the Picture will be scaled to
+     * fit the given width and height.
+     *
+     * @param source The recorded {@link Picture} of drawing commands that will be
+     *               drawn into the returned Bitmap.
+     * @param width The width of the bitmap to create. The picture's width will be
+     *              scaled to match if necessary.
+     * @param height The height of the bitmap to create. The picture's height will be
+     *              scaled to match if necessary.
+     * @param config The {@link Config} of the created bitmap. If this is null then
+     *               the bitmap will be {@link Config#HARDWARE}.
+     *
+     * @return An immutable bitmap with a HARDWARE config whose contents are created
+     * from the recorded drawing commands in the Picture source.
+     */
+    public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+            @NonNull Config config) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width & height must be > 0");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("Config must not be null");
+        }
+        if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+            StrictMode.noteSlowCall("GPU readback");
+        }
+        if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+            final RenderNode node = RenderNode.create("BitmapTemporary", null);
+            node.setLeftTopRightBottom(0, 0, width, height);
+            node.setClipToBounds(false);
+            final DisplayListCanvas canvas = node.start(width, height);
+            if (source.getWidth() != width || source.getHeight() != height) {
+                canvas.scale(width / (float) source.getWidth(),
+                        height / (float) source.getHeight());
+            }
+            canvas.drawPicture(source);
+            node.end(canvas);
+            Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+            if (config != Config.HARDWARE) {
+                bitmap = bitmap.copy(config, false);
+            }
+            return bitmap;
+        } else {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+            Canvas canvas = new Canvas(bitmap);
+            if (source.getWidth() != width || source.getHeight() != height) {
+                canvas.scale(width / (float) source.getWidth(),
+                        height / (float) source.getHeight());
+            }
+            canvas.drawPicture(source);
+            canvas.setBitmap(null);
+            bitmap.makeImmutable();
+            return bitmap;
+        }
+    }
+
+    /**
      * Returns an optional array of private data, used by the UI system for
      * some bitmaps. Not intended to be called by applications.
      */
@@ -1259,6 +1354,12 @@
         return mIsMutable;
     }
 
+    /** @hide */
+    public final void makeImmutable() {
+        // todo mIsMutable = false;
+        // todo nMakeImmutable();
+    }
+
     /**
      * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
      * When a pixel is pre-multiplied, the RGB components have been multiplied by
@@ -1568,6 +1669,8 @@
         if (mColorSpace == null) {
             if (nativeIsSRGB(mNativePtr)) {
                 mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+            } else if (getConfig() == Config.HARDWARE && nativeIsSRGBLinear(mNativePtr)) {
+                mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
             } else {
                 float[] xyz = new float[9];
                 float[] params = new float[7];
@@ -1991,5 +2094,6 @@
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
     private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
     private static native boolean nativeIsSRGB(long nativePtr);
+    private static native boolean nativeIsSRGBLinear(long nativePtr);
     private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap);
 }
diff --git a/android/graphics/BitmapFactory.java b/android/graphics/BitmapFactory.java
index f5bf754..7ea35e7 100644
--- a/android/graphics/BitmapFactory.java
+++ b/android/graphics/BitmapFactory.java
@@ -18,6 +18,8 @@
 
 import static android.graphics.BitmapFactory.Options.validate;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -518,8 +520,9 @@
      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
      */
-    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
-            InputStream is, Rect pad, Options opts) {
+    @Nullable
+    public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
+            @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
         validate(opts);
         if (opts == null) {
             opts = new Options();
@@ -707,7 +710,9 @@
      * <code>is.mark(1024)</code> would be called. As of
      * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
      */
-    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+    @Nullable
+    public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
+            @Nullable Options opts) {
         // we don't throw in this case, thus allowing the caller to only check
         // the cache, and not force the image to be decoded.
         if (is == null) {
@@ -742,7 +747,8 @@
      * Private helper function for decoding an InputStream natively. Buffers the input enough to
      * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
      */
-    private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
+    private static Bitmap decodeStreamInternal(@NonNull InputStream is,
+            @Nullable Rect outPadding, @Nullable Options opts) {
         // ASSERT(is != null);
         byte [] tempStorage = null;
         if (opts != null) tempStorage = opts.inTempStorage;
diff --git a/android/graphics/Canvas.java b/android/graphics/Canvas.java
index f5e8633..3cc92bc 100644
--- a/android/graphics/Canvas.java
+++ b/android/graphics/Canvas.java
@@ -47,6 +47,7 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas extends BaseCanvas {
+    private static int sCompatiblityVersion = 0;
     /** @hide */
     public static boolean sCompatibilityRestore = false;
     /** @hide */
@@ -306,7 +307,7 @@
 
     /**
      * Restore the current matrix when restore() is called.
-     *
+     * @removed
      * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
      *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the matrix
      *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
@@ -318,6 +319,7 @@
     /**
      * Restore the current clip when restore() is called.
      *
+     * @removed
      * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
      *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the clip
      *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
@@ -329,6 +331,7 @@
     /**
      * The layer requires a per-pixel alpha channel.
      *
+     * @removed
      * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
      *             {@link #saveLayerAlpha(RectF, int)}.
      */
@@ -337,6 +340,7 @@
     /**
      * The layer requires full 8-bit precision for each color channel.
      *
+     * @removed
      * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
      *             {@link #saveLayerAlpha(RectF, int)}.
      */
@@ -349,6 +353,7 @@
      * <code>saveLayerAlpha()</code> variants. Not passing this flag generally
      * triggers extremely poor performance with hardware accelerated rendering.
      *
+     * @removed
      * @deprecated This flag results in poor performance and the same effect can be achieved with
      *             a single layer or multiple draw commands with different clips.
      *
@@ -367,6 +372,14 @@
      */
     public static final int ALL_SAVE_FLAG = 0x1F;
 
+    private static void checkValidSaveFlags(int saveFlags) {
+        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+                && saveFlags != ALL_SAVE_FLAG) {
+            throw new IllegalArgumentException(
+                    "Invalid Layer Save Flag - only ALL_SAVE_FLAGS is allowed");
+        }
+    }
+
     /**
      * Saves the current matrix and clip onto a private stack.
      * <p>
@@ -393,6 +406,7 @@
      * restore() is made, those calls will be forgotten, and the settings that
      * existed before the save() will be reinstated.
      *
+     * @removed
      * @deprecated Use {@link #save()} instead.
      * @param saveFlags flag bits that specify which parts of the Canvas state
      *                  to save/restore
@@ -407,10 +421,8 @@
      * redirects drawing to an offscreen bitmap.
      * <p class="note"><strong>Note:</strong> this method is very expensive,
      * incurring more than double rendering cost for contained content. Avoid
-     * using this method, especially if the bounds provided are large, or if
-     * the {@link #CLIP_TO_LAYER_SAVE_FLAG} is omitted from the
-     * {@code saveFlags} parameter. It is recommended to use a
-     * {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
+     * using this method, especially if the bounds provided are large. It is
+     * recommended to use a {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
      * to apply an xfermode, color filter, or alpha, as it will perform much
      * better than this method.
      * <p>
@@ -424,6 +436,9 @@
      * {@link Paint#getColorFilter() ColorFilter} are applied when the
      * offscreen bitmap is drawn back when restore() is called.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayer(RectF, Paint)} instead.
      * @param bounds May be null. The maximum size the offscreen bitmap
      *               needs to be (in local coordinates)
@@ -437,7 +452,9 @@
         if (bounds == null) {
             bounds = new RectF(getClipBounds());
         }
-        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
+        checkValidSaveFlags(saveFlags);
+        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint,
+                ALL_SAVE_FLAG);
     }
 
     /**
@@ -471,15 +488,26 @@
     }
 
     /**
+     * @hide
+     */
+    public int saveUnclippedLayer(int left, int top, int right, int bottom) {
+        return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+    }
+
+    /**
      * Helper version of saveLayer() that takes 4 values rather than a RectF.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayer(float, float, float, float, Paint)} instead.
      */
     public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
             @Saveflags int saveFlags) {
+        checkValidSaveFlags(saveFlags);
         return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom,
                 paint != null ? paint.getNativeInstance() : 0,
-                saveFlags);
+                ALL_SAVE_FLAG);
     }
 
     /**
@@ -495,10 +523,8 @@
      * redirects drawing to an offscreen bitmap.
      * <p class="note"><strong>Note:</strong> this method is very expensive,
      * incurring more than double rendering cost for contained content. Avoid
-     * using this method, especially if the bounds provided are large, or if
-     * the {@link #CLIP_TO_LAYER_SAVE_FLAG} is omitted from the
-     * {@code saveFlags} parameter. It is recommended to use a
-     * {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
+     * using this method, especially if the bounds provided are large. It is
+     * recommended to use a {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
      * to apply an xfermode, color filter, or alpha, as it will perform much
      * better than this method.
      * <p>
@@ -510,6 +536,9 @@
      * The {@code alpha} parameter is applied when the offscreen bitmap is
      * drawn back when restore() is called.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayerAlpha(RectF, int)} instead.
      * @param bounds    The maximum size the offscreen bitmap needs to be
      *                  (in local coordinates)
@@ -523,7 +552,9 @@
         if (bounds == null) {
             bounds = new RectF(getClipBounds());
         }
-        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, alpha, saveFlags);
+        checkValidSaveFlags(saveFlags);
+        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, alpha,
+                ALL_SAVE_FLAG);
     }
 
     /**
@@ -542,13 +573,17 @@
     /**
      * Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayerAlpha(float, float, float, float, int)} instead.
      */
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             @Saveflags int saveFlags) {
+        checkValidSaveFlags(saveFlags);
         alpha = Math.min(255, Math.max(0, alpha));
         return nSaveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
-                                     alpha, saveFlags);
+                                     alpha, ALL_SAVE_FLAG);
     }
 
     /**
@@ -738,6 +773,14 @@
         return m;
     }
 
+    private static void checkValidClipOp(@NonNull Region.Op op) {
+        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+                && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
+            throw new IllegalArgumentException(
+                    "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
+        }
+    }
+
     /**
      * Modify the current clip with the specified rectangle.
      *
@@ -750,9 +793,13 @@
      * are intended to only expand the clip as a result of a restore operation. This enables a view
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(RectF)} and {@link #clipOutRect(RectF)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
@@ -770,9 +817,13 @@
      * are intended to only expand the clip as a result of a restore operation. This enables a view
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(Rect)} and {@link #clipOutRect(Rect)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(@NonNull Rect rect, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
@@ -846,10 +897,14 @@
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(float,float,float,float)} and
      * {@link #clipOutRect(float,float,float,float)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(float left, float top, float right, float bottom,
             @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
     }
 
@@ -932,9 +987,13 @@
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipPath(Path)} and
      * {@link #clipOutPath(Path)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
     }
 
@@ -1219,10 +1278,17 @@
         nFreeTextLayoutCaches();
     }
 
+    /** @hide */
+    public static void setCompatibilityVersion(int apiLevel) {
+        sCompatiblityVersion = apiLevel;
+        nSetCompatibilityVersion(apiLevel);
+    }
+
     private static native void nFreeCaches();
     private static native void nFreeTextLayoutCaches();
     private static native long nInitRaster(Bitmap bitmap);
     private static native long nGetNativeFinalizer();
+    private static native void nSetCompatibilityVersion(int apiLevel);
 
     // ---------------- @FastNative -------------------
 
@@ -1486,6 +1552,10 @@
      * across the top of the bitmap from left to right. A more general version of this method is
      * drawVertices().
      *
+     * Prior to API level {@value Build.VERSION_CODES#P} vertOffset and colorOffset were ignored,
+     * effectively treating them as zeros. In API level {@value Build.VERSION_CODES#P} and above
+     * these parameters will be respected.
+     *
      * @param bitmap The bitmap to draw using the mesh
      * @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
      * @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
diff --git a/android/graphics/Canvas_Delegate.java b/android/graphics/Canvas_Delegate.java
index 9d8af38..a23244b 100644
--- a/android/graphics/Canvas_Delegate.java
+++ b/android/graphics/Canvas_Delegate.java
@@ -470,6 +470,11 @@
         return sFinalizer;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
+        // Unsupported by layoutlib, do nothing
+    }
+
     private Canvas_Delegate(Bitmap_Delegate bitmap) {
         super(bitmap);
     }
diff --git a/android/graphics/EmbossMaskFilter.java b/android/graphics/EmbossMaskFilter.java
index a9e180f..003678a 100644
--- a/android/graphics/EmbossMaskFilter.java
+++ b/android/graphics/EmbossMaskFilter.java
@@ -20,12 +20,15 @@
     /**
      * Create an emboss maskfilter
      *
+     * @deprecated This subclass is not supported and should not be instantiated.
+     *
      * @param direction  array of 3 scalars [x, y, z] specifying the direction of the light source
      * @param ambient    0...1 amount of ambient light
      * @param specular   coefficient for specular highlights (e.g. 8)
      * @param blurRadius amount to blur before applying lighting (e.g. 3)
      * @return           the emboss maskfilter
      */
+    @Deprecated
     public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) {
         if (direction.length < 3) {
             throw new ArrayIndexOutOfBoundsException();
diff --git a/android/graphics/FontFamily.java b/android/graphics/FontFamily.java
index d77e601..c69eb32 100644
--- a/android/graphics/FontFamily.java
+++ b/android/graphics/FontFamily.java
@@ -19,11 +19,13 @@
 import android.annotation.Nullable;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
 import android.text.TextUtils;
 import android.util.Log;
+
 import dalvik.annotation.optimization.CriticalNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -38,6 +40,14 @@
 
     private static String TAG = "FontFamily";
 
+    private static final NativeAllocationRegistry sBuilderRegistry = new NativeAllocationRegistry(
+            FontFamily.class.getClassLoader(), nGetBuilderReleaseFunc(), 64);
+
+    private @Nullable Runnable mNativeBuilderCleaner;
+
+    private static final NativeAllocationRegistry sFamilyRegistry = new NativeAllocationRegistry(
+            FontFamily.class.getClassLoader(), nGetFamilyReleaseFunc(), 64);
+
     /**
      * @hide
      */
@@ -48,6 +58,7 @@
 
     public FontFamily() {
         mBuilderPtr = nInitBuilder(null, 0);
+        mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
     }
 
     public FontFamily(@Nullable String[] langs, int variant) {
@@ -60,6 +71,7 @@
             langsString = TextUtils.join(",", langs);
         }
         mBuilderPtr = nInitBuilder(langsString, variant);
+        mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
     }
 
     /**
@@ -73,7 +85,11 @@
             throw new IllegalStateException("This FontFamily is already frozen");
         }
         mNativePtr = nCreateFamily(mBuilderPtr);
+        mNativeBuilderCleaner.run();
         mBuilderPtr = 0;
+        if (mNativePtr != 0) {
+            sFamilyRegistry.registerNativeAllocation(this, mNativePtr);
+        }
         return mNativePtr != 0;
     }
 
@@ -81,24 +97,10 @@
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("This FontFamily is already frozen or abandoned");
         }
-        nAbort(mBuilderPtr);
+        mNativeBuilderCleaner.run();
         mBuilderPtr = 0;
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mNativePtr != 0) {
-                nUnrefFamily(mNativePtr);
-            }
-            if (mBuilderPtr != 0) {
-                nAbort(mBuilderPtr);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
     public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
             int italic) {
         if (mBuilderPtr == 0) {
@@ -160,25 +162,6 @@
                 isItalic);
     }
 
-    /**
-     * Allow creating unsupported FontFamily.
-     *
-     * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
-     * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
-     * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
-     * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
-     * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
-     * the top of the fallback chain but is never used. if we don't create this empty FontFamily
-     * and put it at top, bad things (performance regressions, unexpected glyph selection) will
-     * happen.
-     */
-    public void allowUnsupportedFont() {
-        if (mBuilderPtr == 0) {
-            throw new IllegalStateException("Unable to allow unsupported font.");
-        }
-        nAllowUnsupportedFont(mBuilderPtr);
-    }
-
     // TODO: Remove once internal user stop using private API.
     private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
         return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,13 +173,10 @@
     private static native long nCreateFamily(long mBuilderPtr);
 
     @CriticalNative
-    private static native void nAllowUnsupportedFont(long builderPtr);
+    private static native long nGetBuilderReleaseFunc();
 
     @CriticalNative
-    private static native void nAbort(long mBuilderPtr);
-
-    @CriticalNative
-    private static native void nUnrefFamily(long nativePtr);
+    private static native long nGetFamilyReleaseFunc();
     // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
     // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
     private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
diff --git a/android/graphics/FontFamily_Delegate.java b/android/graphics/FontFamily_Delegate.java
index 1e3ebd7..1ad1f8f 100644
--- a/android/graphics/FontFamily_Delegate.java
+++ b/android/graphics/FontFamily_Delegate.java
@@ -27,7 +27,6 @@
 import android.content.res.AssetManager;
 import android.content.res.BridgeAssetManager;
 import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
 
 import java.awt.Font;
 import java.awt.FontFormatException;
@@ -38,7 +37,6 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -453,14 +451,6 @@
         sManager.removeJavaReferenceFor(builderPtr);
     }
 
-    /**
-     * @see FontFamily#allowUnsupportedFont
-     */
-    @LayoutlibDelegate
-    /*package*/ static void nAllowUnsupportedFont(long builderPtr) {
-        // Do nothing here as this is used for Minikin fonts
-    }
-
     // ---- private helper methods ----
 
     private void init() {
diff --git a/android/graphics/FontListParser.java b/android/graphics/FontListParser.java
index 9f672e3..431d0e0 100644
--- a/android/graphics/FontListParser.java
+++ b/android/graphics/FontListParser.java
@@ -16,16 +16,13 @@
 
 package android.graphics;
 
-import android.text.FontConfig;
 import android.graphics.fonts.FontVariationAxis;
+import android.text.FontConfig;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
diff --git a/android/graphics/ImageDecoder.java b/android/graphics/ImageDecoder.java
index 3de050b..506eab5 100644
--- a/android/graphics/ImageDecoder.java
+++ b/android/graphics/ImageDecoder.java
@@ -16,50 +16,34 @@
 
 package android.graphics;
 
-import static android.system.OsConstants.SEEK_CUR;
-import static android.system.OsConstants.SEEK_SET;
-
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RawRes;
 import android.content.ContentResolver;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
+import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.NinePatchDrawable;
 import android.net.Uri;
-import android.util.Size;
-import android.system.ErrnoException;
-import android.system.Os;
 import android.util.DisplayMetrics;
+import android.util.Size;
 import android.util.TypedValue;
 
-import libcore.io.IoUtils;
-import dalvik.system.CloseGuard;
-
 import java.nio.ByteBuffer;
 import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ArrayIndexOutOfBoundsException;
 import java.lang.AutoCloseable;
 import java.lang.NullPointerException;
-import java.lang.RuntimeException;
 import java.lang.annotation.Retention;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
  */
 public final class ImageDecoder implements AutoCloseable {
+
     /**
      *  Source of the encoded image data.
      */
@@ -100,7 +84,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return nCreate(mData, mOffset, mLength);
+            return new ImageDecoder();
         }
     }
 
@@ -112,12 +96,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            if (!mBuffer.isDirect() && mBuffer.hasArray()) {
-                int offset = mBuffer.arrayOffset() + mBuffer.position();
-                int length = mBuffer.limit() - mBuffer.position();
-                return nCreate(mBuffer.array(), offset, length);
-            }
-            return nCreate(mBuffer, mBuffer.position(), mBuffer.limit());
+            return new ImageDecoder();
         }
     }
 
@@ -132,89 +111,13 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            AssetFileDescriptor assetFd = null;
-            try {
-                if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
-                    assetFd = mResolver.openTypedAssetFileDescriptor(mUri,
-                            "image/*", null);
-                } else {
-                    assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
-                }
-            } catch (FileNotFoundException e) {
-                // Some images cannot be opened as AssetFileDescriptors (e.g.
-                // bmp, ico). Open them as InputStreams.
-                InputStream is = mResolver.openInputStream(mUri);
-                if (is == null) {
-                    throw new FileNotFoundException(mUri.toString());
-                }
-
-                return createFromStream(is);
-            }
-
-            final FileDescriptor fd = assetFd.getFileDescriptor();
-            final long offset = assetFd.getStartOffset();
-
-            ImageDecoder decoder = null;
-            try {
-                try {
-                    Os.lseek(fd, offset, SEEK_SET);
-                    decoder = nCreate(fd);
-                } catch (ErrnoException e) {
-                    decoder = createFromStream(new FileInputStream(fd));
-                }
-            } finally {
-                if (decoder == null) {
-                    IoUtils.closeQuietly(assetFd);
-                } else {
-                    decoder.mAssetFd = assetFd;
-                }
-            }
-            return decoder;
+            return new ImageDecoder();
         }
     }
 
-    @NonNull
-    private static ImageDecoder createFromFile(@NonNull File file) throws IOException {
-        FileInputStream stream = new FileInputStream(file);
-        FileDescriptor fd = stream.getFD();
-        try {
-            Os.lseek(fd, 0, SEEK_CUR);
-        } catch (ErrnoException e) {
-            return createFromStream(stream);
-        }
-
-        ImageDecoder decoder = null;
-        try {
-            decoder = nCreate(fd);
-        } finally {
-            if (decoder == null) {
-                IoUtils.closeQuietly(stream);
-            } else {
-                decoder.mInputStream = stream;
-            }
-        }
-        return decoder;
-    }
-
-    @NonNull
-    private static ImageDecoder createFromStream(@NonNull InputStream is) throws IOException {
-        // Arbitrary size matches BitmapFactory.
-        byte[] storage = new byte[16 * 1024];
-        ImageDecoder decoder = null;
-        try {
-            decoder = nCreate(is, storage);
-        } finally {
-            if (decoder == null) {
-                IoUtils.closeQuietly(is);
-            } else {
-                decoder.mInputStream = is;
-                decoder.mTempStorage = storage;
-            }
-        }
-
-        return decoder;
-    }
-
+    /**
+     * For backwards compatibility, this does *not* close the InputStream.
+     */
     private static class InputStreamSource extends Source {
         InputStreamSource(Resources res, InputStream is, int inputDensity) {
             if (is == null) {
@@ -237,16 +140,46 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
 
-            synchronized (this) {
-                if (mInputStream == null) {
-                    throw new IOException("Cannot reuse InputStreamSource");
-                }
-                InputStream is = mInputStream;
-                mInputStream = null;
-                return createFromStream(is);
+    /**
+     * Takes ownership of the AssetInputStream.
+     *
+     * @hide
+     */
+    public static class AssetInputStreamSource extends Source {
+        public AssetInputStreamSource(@NonNull AssetInputStream ais,
+                @NonNull Resources res, @NonNull TypedValue value) {
+            mAssetInputStream = ais;
+            mResources = res;
+
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                mDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                mDensity = value.density;
+            } else {
+                mDensity = Bitmap.DENSITY_NONE;
             }
         }
+
+        private AssetInputStream mAssetInputStream;
+        private final Resources  mResources;
+        private final int        mDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() {
+            return mDensity;
+        }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
     }
 
     private static class ResourceSource extends Source {
@@ -268,34 +201,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            // This is just used in order to access the underlying Asset and
-            // keep it alive. FIXME: Can we skip creating this object?
-            InputStream is = null;
-            ImageDecoder decoder = null;
-            TypedValue value = new TypedValue();
-            try {
-                is = mResources.openRawResource(mResId, value);
-
-                if (value.density == TypedValue.DENSITY_DEFAULT) {
-                    mResDensity = DisplayMetrics.DENSITY_DEFAULT;
-                } else if (value.density != TypedValue.DENSITY_NONE) {
-                    mResDensity = value.density;
-                }
-
-                if (!(is instanceof AssetManager.AssetInputStream)) {
-                    // This should never happen.
-                    throw new RuntimeException("Resource is not an asset?");
-                }
-                long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
-                decoder = nCreate(asset);
-            } finally {
-                if (decoder == null) {
-                    IoUtils.closeQuietly(is);
-                } else {
-                    decoder.mInputStream = is;
-                }
-            }
-            return decoder;
+            return new ImageDecoder();
         }
     }
 
@@ -308,7 +214,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return createFromFile(mFile);
+            return new ImageDecoder();
         }
     }
 
@@ -316,11 +222,9 @@
      *  Contains information about the encoded image.
      */
     public static class ImageInfo {
-        private final Size mSize;
         private ImageDecoder mDecoder;
 
         private ImageInfo(@NonNull ImageDecoder decoder) {
-            mSize = new Size(decoder.mWidth, decoder.mHeight);
             mDecoder = decoder;
         }
 
@@ -329,7 +233,7 @@
          */
         @NonNull
         public Size getSize() {
-            return mSize;
+            return new Size(0, 0);
         }
 
         /**
@@ -337,7 +241,17 @@
          */
         @NonNull
         public String getMimeType() {
-            return mDecoder.getMimeType();
+            return "";
+        }
+
+        /**
+         * Whether the image is animated.
+         *
+         * <p>Calling {@link #decodeDrawable} will return an
+         * {@link AnimatedImageDrawable}.</p>
+         */
+        public boolean isAnimated() {
+            return mDecoder.mAnimated;
         }
     };
 
@@ -350,7 +264,7 @@
      *  Optional listener supplied to {@link #decodeDrawable} or
      *  {@link #decodeBitmap}.
      */
-    public static interface OnHeaderDecodedListener {
+    public interface OnHeaderDecodedListener {
         /**
          *  Called when the header is decoded and the size is known.
          *
@@ -358,7 +272,7 @@
          *  @param info Information about the encoded image.
          *  @param source that created the decoder.
          */
-        public void onHeaderDecoded(@NonNull ImageDecoder decoder,
+        void onHeaderDecoded(@NonNull ImageDecoder decoder,
                 @NonNull ImageInfo info, @NonNull Source source);
 
     };
@@ -379,15 +293,14 @@
     public static final int ERROR_SOURCE_ERROR      = 3;
 
     @Retention(SOURCE)
-    @IntDef({ ERROR_SOURCE_EXCEPTION, ERROR_SOURCE_INCOMPLETE, ERROR_SOURCE_ERROR })
-    public @interface Error {};
+    public @interface Error {}
 
     /**
      *  Optional listener supplied to the ImageDecoder.
      *
      *  Without this listener, errors will throw {@link java.io.IOException}.
      */
-    public static interface OnPartialImageListener {
+    public interface OnPartialImageListener {
         /**
          *  Called when there is only a partial image to display.
          *
@@ -402,62 +315,14 @@
          *      with partial data. False (which is the default) to abort the
          *      decode and throw {@link java.io.IOException}.
          */
-        public boolean onPartialImage(@Error int error, @NonNull Source source);
-    };
-
-    // Fields
-    private long          mNativePtr;
-    private final int     mWidth;
-    private final int     mHeight;
-    private final boolean mAnimated;
-
-    private int     mDesiredWidth;
-    private int     mDesiredHeight;
-    private int     mAllocator = ALLOCATOR_DEFAULT;
-    private boolean mRequireUnpremultiplied = false;
-    private boolean mMutable = false;
-    private boolean mPreferRamOverQuality = false;
-    private boolean mAsAlphaMask = false;
-    private Rect    mCropRect;
-    private Source  mSource;
-
-    private PostProcessor          mPostProcessor;
-    private OnPartialImageListener mOnPartialImageListener;
-
-    // Objects for interacting with the input.
-    private InputStream         mInputStream;
-    private byte[]              mTempStorage;
-    private AssetFileDescriptor mAssetFd;
-    private final AtomicBoolean mClosed = new AtomicBoolean();
-    private final CloseGuard    mCloseGuard = CloseGuard.get();
-
-    /**
-     * Private constructor called by JNI. {@link #close} must be
-     * called after decoding to delete native resources.
-     */
-    @SuppressWarnings("unused")
-    private ImageDecoder(long nativePtr, int width, int height,
-            boolean animated) {
-        mNativePtr = nativePtr;
-        mWidth = width;
-        mHeight = height;
-        mDesiredWidth = width;
-        mDesiredHeight = height;
-        mAnimated = animated;
-        mCloseGuard.open("close");
+        boolean onPartialImage(@Error int error, @NonNull Source source);
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mCloseGuard != null) {
-                mCloseGuard.warnIfOpen();
-            }
+    private boolean mAnimated;
+    private Rect mOutPaddingRect;
 
-            close();
-        } finally {
-            super.finalize();
-        }
+    public ImageDecoder() {
+        mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable
     }
 
     /**
@@ -471,7 +336,7 @@
      *      {@link #decodeDrawable} or {@link #decodeBitmap}.
      */
     @NonNull
-    public static Source createSource(@NonNull Resources res, @RawRes int resId)
+    public static Source createSource(@NonNull Resources res, int resId)
     {
         return new ResourceSource(res, resId);
     }
@@ -505,9 +370,6 @@
     @NonNull
     public static Source createSource(@NonNull byte[] data, int offset,
             int length) throws ArrayIndexOutOfBoundsException {
-        if (data == null) {
-            throw new NullPointerException("null byte[] in createSource!");
-        }
         if (offset < 0 || length < 0 || offset >= data.length ||
                 offset + length > data.length) {
             throw new ArrayIndexOutOfBoundsException(
@@ -580,15 +442,7 @@
      */
     @NonNull
     public Size getSampledSize(int sampleSize) {
-        if (sampleSize <= 0) {
-            throw new IllegalArgumentException("sampleSize must be positive! "
-                    + "provided " + sampleSize);
-        }
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("ImageDecoder is closed!");
-        }
-
-        return nGetSampledSize(mNativePtr, sampleSize);
+        return new Size(0, 0);
     }
 
     // Modifiers
@@ -599,13 +453,6 @@
      *  @param height must be greater than 0.
      */
     public void setResize(int width, int height) {
-        if (width <= 0 || height <= 0) {
-            throw new IllegalArgumentException("Dimensions must be positive! "
-                    + "provided (" + width + ", " + height + ")");
-        }
-
-        mDesiredWidth = width;
-        mDesiredHeight = height;
     }
 
     /**
@@ -617,12 +464,6 @@
      *  @param sampleSize Sampling rate of the encoded image.
      */
     public void setResize(int sampleSize) {
-        Size size = this.getSampledSize(sampleSize);
-        this.setResize(size.getWidth(), size.getHeight());
-    }
-
-    private boolean requestedResize() {
-        return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
     }
 
     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
@@ -663,8 +504,6 @@
 
     /** @hide **/
     @Retention(SOURCE)
-    @IntDef({ ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, ALLOCATOR_SHARED_MEMORY,
-              ALLOCATOR_HARDWARE })
     public @interface Allocator {};
 
     /**
@@ -674,11 +513,8 @@
      *
      *  @param allocator Type of allocator to use.
      */
-    public void setAllocator(@Allocator int allocator) {
-        if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
-            throw new IllegalArgumentException("invalid allocator " + allocator);
-        }
-        mAllocator = allocator;
+    public ImageDecoder setAllocator(@Allocator int allocator) {
+        return this;
     }
 
     /**
@@ -693,8 +529,8 @@
      *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
      *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
      */
-    public void setRequireUnpremultiplied(boolean requireUnpremultiplied) {
-        mRequireUnpremultiplied = requireUnpremultiplied;
+    public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+        return this;
     }
 
     /**
@@ -710,8 +546,8 @@
      *  {@link Canvas} will be recorded immediately and then applied to each
      *  frame.</p>
      */
-    public void setPostProcessor(@Nullable PostProcessor p) {
-        mPostProcessor = p;
+    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
+        return this;
     }
 
     /**
@@ -720,8 +556,8 @@
      *  Will be called if there is an error in the input. Without one, a
      *  partial {@link Bitmap} will be created.
      */
-    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
-        mOnPartialImageListener = l;
+    public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
+        return this;
     }
 
     /**
@@ -736,8 +572,21 @@
      *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
      *  but merely crops the output.</p>
      */
-    public void setCrop(@Nullable Rect subset) {
-        mCropRect = subset;
+    public ImageDecoder setCrop(@Nullable Rect subset) {
+        return this;
+    }
+
+    /**
+     *  Set a Rect for retrieving nine patch padding.
+     *
+     *  If the image is a nine patch, this Rect will be set to the padding
+     *  rectangle during decode. Otherwise it will not be modified.
+     *
+     *  @hide
+     */
+    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+        mOutPaddingRect = outPadding;
+        return this;
     }
 
     /**
@@ -756,8 +605,8 @@
      *  order to modify. Attempting to decode a mutable {@link Drawable} will
      *  throw an {@link java.lang.IllegalStateException}.</p>
      */
-    public void setMutable(boolean mutable) {
-        mMutable = mutable;
+    public ImageDecoder setMutable(boolean mutable) {
+        return this;
     }
 
     /**
@@ -768,8 +617,8 @@
      *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
      *  with no alpha information.
      */
-    public void setPreferRamOverQuality(boolean preferRamOverQuality) {
-        mPreferRamOverQuality = preferRamOverQuality;
+    public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) {
+        return this;
     }
 
     /**
@@ -784,84 +633,12 @@
      *  {@link #decodeBitmap} throwing an
      *  {@link java.lang.IllegalStateException}.</p>
      */
-    public void setAsAlphaMask(boolean asAlphaMask) {
-        mAsAlphaMask = asAlphaMask;
+    public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
+        return this;
     }
 
     @Override
     public void close() {
-        mCloseGuard.close();
-        if (!mClosed.compareAndSet(false, true)) {
-            return;
-        }
-        nClose(mNativePtr);
-        mNativePtr = 0;
-
-        IoUtils.closeQuietly(mInputStream);
-        IoUtils.closeQuietly(mAssetFd);
-
-        mInputStream = null;
-        mAssetFd = null;
-        mTempStorage = null;
-    }
-
-    private void checkState() {
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("Cannot use closed ImageDecoder!");
-        }
-
-        checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
-
-        if (mAllocator == ALLOCATOR_HARDWARE) {
-            if (mMutable) {
-                throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
-            }
-            if (mAsAlphaMask) {
-                throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
-            }
-        }
-
-        if (mPostProcessor != null && mRequireUnpremultiplied) {
-            throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
-        }
-    }
-
-    private static void checkSubset(int width, int height, Rect r) {
-        if (r == null) {
-            return;
-        }
-        if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
-            throw new IllegalStateException("Subset " + r + " not contained by "
-                    + "scaled image bounds: (" + width + " x " + height + ")");
-        }
-    }
-
-    @NonNull
-    private Bitmap decodeBitmap() throws IOException {
-        checkState();
-        // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener
-        // exists
-        ImageDecoder partialImagePtr = mOnPartialImageListener == null ? null : this;
-        // nDecodeBitmap calls postProcessAndRelease only if mPostProcessor
-        // exists.
-        ImageDecoder postProcessPtr = mPostProcessor == null ? null : this;
-        return nDecodeBitmap(mNativePtr, partialImagePtr,
-                postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
-                mMutable, mAllocator, mRequireUnpremultiplied,
-                mPreferRamOverQuality, mAsAlphaMask);
-
-    }
-
-    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
-            @NonNull Source src) {
-        if (listener != null) {
-            ImageInfo info = new ImageInfo(this);
-            try {
-                listener.onHeaderDecoded(this, info, src);
-            } finally {
-                info.mDecoder = null;
-            }
-        }
     }
 
     /**
@@ -879,57 +656,8 @@
     @NonNull
     public static Drawable decodeDrawable(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
-            decoder.mSource = src;
-            decoder.callHeaderDecoded(listener, src);
-
-            if (decoder.mRequireUnpremultiplied) {
-                // Though this could be supported (ignored) for opaque images,
-                // it seems better to always report this error.
-                throw new IllegalStateException("Cannot decode a Drawable " +
-                                                "with unpremultiplied pixels!");
-            }
-
-            if (decoder.mMutable) {
-                throw new IllegalStateException("Cannot decode a mutable " +
-                                                "Drawable!");
-            }
-
-            // this call potentially manipulates the decoder so it must be performed prior to
-            // decoding the bitmap and after decode set the density on the resulting bitmap
-            final int srcDensity = computeDensity(src, decoder);
-            if (decoder.mAnimated) {
-                // AnimatedImageDrawable calls postProcessAndRelease only if
-                // mPostProcessor exists.
-                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
-                        null : decoder;
-                Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
-                        postProcessPtr, decoder.mDesiredWidth,
-                        decoder.mDesiredHeight, srcDensity,
-                        src.computeDstDensity(), decoder.mCropRect,
-                        decoder.mInputStream, decoder.mAssetFd);
-                // d has taken ownership of these objects.
-                decoder.mInputStream = null;
-                decoder.mAssetFd = null;
-                return d;
-            }
-
-            Bitmap bm = decoder.decodeBitmap();
-            bm.setDensity(srcDensity);
-
-            Resources res = src.getResources();
-            byte[] np = bm.getNinePatchChunk();
-            if (np != null && NinePatch.isNinePatchChunk(np)) {
-                Rect opticalInsets = new Rect();
-                bm.getOpticalInsets(opticalInsets);
-                Rect padding = new Rect();
-                nGetPadding(decoder.mNativePtr, padding);
-                return new NinePatchDrawable(res, bm, np, padding,
-                        opticalInsets, null);
-            }
-
-            return new BitmapDrawable(res, bm);
-        }
+        Bitmap bitmap = decodeBitmap(src, listener);
+        return new BitmapDrawable(src.getResources(), bitmap);
     }
 
     /**
@@ -956,55 +684,14 @@
     @NonNull
     public static Bitmap decodeBitmap(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
-            decoder.mSource = src;
-            decoder.callHeaderDecoded(listener, src);
-
-            // this call potentially manipulates the decoder so it must be performed prior to
-            // decoding the bitmap
-            final int srcDensity = computeDensity(src, decoder);
-            Bitmap bm = decoder.decodeBitmap();
-            bm.setDensity(srcDensity);
-            return bm;
+        TypedValue value = new TypedValue();
+        value.density = src.getDensity();
+        ImageDecoder decoder = src.createImageDecoder();
+        if (listener != null) {
+            listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src);
         }
-    }
-
-    // This method may modify the decoder so it must be called prior to performing the decode
-    private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
-        // if the caller changed the size then we treat the density as unknown
-        if (decoder.requestedResize()) {
-            return Bitmap.DENSITY_NONE;
-        }
-
-        // Special stuff for compatibility mode: if the target density is not
-        // the same as the display density, but the resource -is- the same as
-        // the display density, then don't scale it down to the target density.
-        // This allows us to load the system's density-correct resources into
-        // an application in compatibility mode, without scaling those down
-        // to the compatibility density only to have them scaled back up when
-        // drawn to the screen.
-        Resources res = src.getResources();
-        final int srcDensity = src.getDensity();
-        if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
-            return srcDensity;
-        }
-
-        // downscale the bitmap if the asset has a higher density than the default
-        final int dstDensity = src.computeDstDensity();
-        if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) {
-            float scale = (float) dstDensity / srcDensity;
-            int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
-            int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-            decoder.setResize(scaledWidth, scaledHeight);
-            return dstDensity;
-        }
-
-        return srcDensity;
-    }
-
-    @NonNull
-    private String getMimeType() {
-        return nGetMimeType(mNativePtr);
+        return BitmapFactory.decodeResourceStream(src.getResources(), value,
+                ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null);
     }
 
     /**
@@ -1014,48 +701,4 @@
     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
         return decodeBitmap(src, null);
     }
-
-    /**
-     * Private method called by JNI.
-     */
-    @SuppressWarnings("unused")
-    private int postProcessAndRelease(@NonNull Canvas canvas) {
-        try {
-            return mPostProcessor.onPostProcess(canvas);
-        } finally {
-            canvas.release();
-        }
-    }
-
-    /**
-     * Private method called by JNI.
-     */
-    @SuppressWarnings("unused")
-    private boolean onPartialImage(@Error int error) {
-        return mOnPartialImageListener.onPartialImage(error, mSource);
-    }
-
-    private static native ImageDecoder nCreate(long asset) throws IOException;
-    private static native ImageDecoder nCreate(ByteBuffer buffer,
-                                               int position,
-                                               int limit) throws IOException;
-    private static native ImageDecoder nCreate(byte[] data, int offset,
-                                               int length) throws IOException;
-    private static native ImageDecoder nCreate(InputStream is, byte[] storage);
-    // The fd must be seekable.
-    private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
-    @NonNull
-    private static native Bitmap nDecodeBitmap(long nativePtr,
-            @Nullable ImageDecoder partialImageListener,
-            @Nullable ImageDecoder postProcessor,
-            int width, int height,
-            @Nullable Rect cropRect, boolean mutable,
-            int allocator, boolean requireUnpremul,
-            boolean preferRamOverQuality, boolean asAlphaMask)
-        throws IOException;
-    private static native Size nGetSampledSize(long nativePtr,
-                                               int sampleSize);
-    private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
-    private static native void nClose(long nativePtr);
-    private static native String nGetMimeType(long nativePtr);
 }
diff --git a/android/graphics/ImageDecoder_Delegate.java b/android/graphics/ImageDecoder_Delegate.java
deleted file mode 100644
index d9fe9bf..0000000
--- a/android/graphics/ImageDecoder_Delegate.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.ImageDecoder.InputStreamSource;
-import android.graphics.ImageDecoder.OnHeaderDecodedListener;
-import android.graphics.ImageDecoder.Source;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
-
-import java.io.IOException;
-
-public class ImageDecoder_Delegate {
-    @LayoutlibDelegate
-    static Bitmap decodeBitmap(@NonNull Source src, @Nullable OnHeaderDecodedListener listener)
-            throws IOException {
-        TypedValue value = new TypedValue();
-        value.density = src.getDensity();
-        return BitmapFactory.decodeResourceStream(src.getResources(), value,
-                ((InputStreamSource) src).mInputStream, null, null);
-    }
-
-    @LayoutlibDelegate
-    static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
-        return decodeBitmap(src, null);
-    }
-
-    @LayoutlibDelegate
-    static Bitmap decodeBitmap(ImageDecoder thisDecoder) {
-        return null;
-    }
-
-    @LayoutlibDelegate
-    static Drawable decodeDrawable(@NonNull Source src, @Nullable OnHeaderDecodedListener listener)
-            throws IOException {
-        Bitmap bitmap = decodeBitmap(src, listener);
-        return new BitmapDrawable(src.getResources(), bitmap);
-    }
-
-    @LayoutlibDelegate
-    static Drawable decodeDrawable(@NonNull Source src) throws IOException {
-        return decodeDrawable(src, null);
-    }
-}
diff --git a/android/graphics/Matrix_Delegate.java b/android/graphics/Matrix_Delegate.java
index 354f919..5ae181d 100644
--- a/android/graphics/Matrix_Delegate.java
+++ b/android/graphics/Matrix_Delegate.java
@@ -118,8 +118,7 @@
         return true;
     }
 
-    public static float[] makeValues(AffineTransform matrix) {
-        float[] values = new float[MATRIX_SIZE];
+    private static float[] setValues(AffineTransform matrix, float[] values) {
         values[0] = (float) matrix.getScaleX();
         values[1] = (float) matrix.getShearX();
         values[2] = (float) matrix.getTranslateX();
@@ -133,6 +132,10 @@
         return values;
     }
 
+    public static float[] makeValues(AffineTransform matrix) {
+        return setValues(matrix, new float[MATRIX_SIZE]);
+    }
+
     public static Matrix_Delegate make(AffineTransform matrix) {
         return new Matrix_Delegate(makeValues(matrix));
     }
@@ -616,12 +619,7 @@
         try {
             AffineTransform affineTransform = d.getAffineTransform();
             AffineTransform inverseTransform = affineTransform.createInverse();
-            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
-            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
-            inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
-            inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
-            inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
-            inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+            setValues(inverseTransform, inv_mtx.mValues);
 
             return true;
         } catch (NoninvertibleTransformException e) {
diff --git a/android/graphics/Movie.java b/android/graphics/Movie.java
index c8f86c6..83857be 100644
--- a/android/graphics/Movie.java
+++ b/android/graphics/Movie.java
@@ -17,9 +17,13 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
-import java.io.InputStream;
-import java.io.FileInputStream;
 
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+/**
+ * @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
+ */
 public class Movie {
     private long mNativeMovie;
 
diff --git a/android/graphics/Paint.java b/android/graphics/Paint.java
index 5a80ee2..42dac38 100644
--- a/android/graphics/Paint.java
+++ b/android/graphics/Paint.java
@@ -19,10 +19,8 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Size;
-import android.graphics.FontListParser;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.LocaleList;
-import android.text.FontConfig;
 import android.text.GraphicsOperations;
 import android.text.SpannableString;
 import android.text.SpannedString;
@@ -33,14 +31,13 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
 
-import libcore.util.NativeAllocationRegistry;
-
 /**
  * The Paint class holds the style and color information about how to draw
  * geometries, text and bitmaps.
@@ -2838,6 +2835,16 @@
         return result;
     }
 
+    /**
+     * Returns true of the passed {@link Paint} will have the same effect on text measurement
+     *
+     * @param other A {@link Paint} object.
+     * @return true if the other {@link Paint} has the same effect on text measurement.
+     */
+    public boolean equalsForTextMeasurement(@NonNull Paint other) {
+        return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
+    }
+
     // regular JNI
     private static native long nGetNativeFinalizer();
     private static native long nInit();
@@ -3005,4 +3012,6 @@
     private static native float nGetStrikeThruThickness(long paintPtr);
     @CriticalNative
     private static native void nSetTextSize(long paintPtr, float textSize);
+    @CriticalNative
+    private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
 }
diff --git a/android/graphics/Paint_Delegate.java b/android/graphics/Paint_Delegate.java
index 4f05176..dae966d 100644
--- a/android/graphics/Paint_Delegate.java
+++ b/android/graphics/Paint_Delegate.java
@@ -1197,6 +1197,11 @@
         return (1.0f / 18.0f) * nGetTextSize(paintPtr);
     }
 
+    @LayoutlibDelegate
+    /*package*/ static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
+        return leftPaintPtr == rightPaintPtr;
+    }
+
     // ---- Private delegate/helper methods ----
 
     /*package*/ Paint_Delegate() {
diff --git a/android/graphics/Path.java b/android/graphics/Path.java
index 098cdc6..cd0862c 100644
--- a/android/graphics/Path.java
+++ b/android/graphics/Path.java
@@ -24,6 +24,8 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * The Path class encapsulates compound (multiple contour) geometric paths
  * consisting of straight line segments, quadratic curves, and cubic curves.
@@ -32,10 +34,14 @@
  * text on a path.
  */
 public class Path {
+
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                Path.class.getClassLoader(), nGetFinalizer(), 48 /* dummy size */);
+
     /**
      * @hide
      */
-    public long mNativePath;
+    public final long mNativePath;
 
     /**
      * @hide
@@ -52,6 +58,7 @@
      */
     public Path() {
         mNativePath = nInit();
+        sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -69,6 +76,7 @@
             }
         }
         mNativePath = nInit(valNative);
+        sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -297,7 +305,7 @@
      *             a rectangle
      * @return     true if the path specifies a rectangle
      */
-    public boolean isRect(RectF rect) {
+    public boolean isRect(@Nullable  RectF rect) {
         return nIsRect(mNativePath, rect);
     }
 
@@ -771,15 +779,6 @@
         nTransform(mNativePath, matrix.native_instance);
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            nFinalize(mNativePath);
-            mNativePath = 0;  //  Other finalizers can still call us.
-        } finally {
-            super.finalize();
-        }
-    }
-
     /** @hide */
     public final long readOnlyNI() {
         return mNativePath;
@@ -820,7 +819,7 @@
 
     private static native long nInit();
     private static native long nInit(long nPath);
-    private static native void nFinalize(long nPath);
+    private static native long nGetFinalizer();
     private static native void nSet(long native_dst, long nSrc);
     private static native void nComputeBounds(long nPath, RectF bounds);
     private static native void nIncReserve(long nPath, int extraPtCount);
diff --git a/android/graphics/Picture.java b/android/graphics/Picture.java
index 08eeaff..d01ff6f 100644
--- a/android/graphics/Picture.java
+++ b/android/graphics/Picture.java
@@ -31,8 +31,9 @@
  * be replayed on a hardware accelerated canvas.</p>
  */
 public class Picture {
-    private Canvas mRecordingCanvas;
+    private PictureCanvas mRecordingCanvas;
     private long mNativePicture;
+    private boolean mRequiresHwAcceleration;
 
     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
 
@@ -78,8 +79,12 @@
      * into it.
      */
     public Canvas beginRecording(int width, int height) {
+        if (mRecordingCanvas != null) {
+            throw new IllegalStateException("Picture already recording, must call #endRecording()");
+        }
         long ni = nativeBeginRecording(mNativePicture, width, height);
-        mRecordingCanvas = new RecordingCanvas(this, ni);
+        mRecordingCanvas = new PictureCanvas(this, ni);
+        mRequiresHwAcceleration = false;
         return mRecordingCanvas;
     }
 
@@ -91,6 +96,7 @@
      */
     public void endRecording() {
         if (mRecordingCanvas != null) {
+            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
             mRecordingCanvas = null;
             nativeEndRecording(mNativePicture);
         }
@@ -113,6 +119,18 @@
     }
 
     /**
+     * Indicates whether or not this Picture contains recorded commands that only work when
+     * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+     * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+     *
+     * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+     *         false otherwise.
+     */
+    public boolean requiresHardwareAcceleration() {
+        return mRequiresHwAcceleration;
+    }
+
+    /**
      * Draw this picture on the canvas.
      * <p>
      * Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
+        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+            canvas.onHwBitmapInSwMode();
+        }
         nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
     }
 
@@ -164,8 +185,7 @@
         if (stream == null) {
             throw new NullPointerException();
         }
-        if (!nativeWriteToStream(mNativePicture, stream,
-                             new byte[WORKING_STREAM_STORAGE])) {
+        if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
             throw new RuntimeException();
         }
     }
@@ -182,12 +202,15 @@
                                            OutputStream stream, byte[] storage);
     private static native void nativeDestructor(long nativePicture);
 
-    private static class RecordingCanvas extends Canvas {
+    private static class PictureCanvas extends Canvas {
         private final Picture mPicture;
+        boolean mHoldsHwBitmap;
 
-        public RecordingCanvas(Picture pict, long nativeCanvas) {
+        public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
             mPicture = pict;
+            // Disable bitmap density scaling. This matches DisplayListCanvas.
+            mDensity = 0;
         }
 
         @Override
@@ -202,5 +225,10 @@
             }
             super.drawPicture(picture);
         }
+
+        @Override
+        protected void onHwBitmapInSwMode() {
+            mHoldsHwBitmap = true;
+        }
     }
 }
diff --git a/android/graphics/SurfaceTexture.java b/android/graphics/SurfaceTexture.java
index 97edf22..1eebd26 100644
--- a/android/graphics/SurfaceTexture.java
+++ b/android/graphics/SurfaceTexture.java
@@ -318,13 +318,17 @@
      * Retrieve the timestamp associated with the texture image set by the most recent call to
      * updateTexImage.
      *
-     * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
-     * should be unaffected by time-of-day adjustments, and for a camera should be strictly
-     * monotonic but for a MediaPlayer may be reset when the position is set.  The
-     * specific meaning and zero point of the timestamp depends on the source providing images to
-     * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot
-     * generally be compared across SurfaceTexture instances, or across multiple program
-     * invocations. It is mostly useful for determining time offsets between subsequent frames.
+     * <p>This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
+     * should be unaffected by time-of-day adjustments. The specific meaning and zero point of the
+     * timestamp depends on the source providing images to the SurfaceTexture. Unless otherwise
+     * specified by the image source, timestamps cannot generally be compared across SurfaceTexture
+     * instances, or across multiple program invocations. It is mostly useful for determining time
+     * offsets between subsequent frames.</p>
+     *
+     * <p>For camera sources, timestamps should be strictly monotonic. Timestamps from MediaPlayer
+     * sources may be reset when the playback position is set. For EGL and Vulkan producers, the
+     * timestamp is the desired present time set with the EGL_ANDROID_presentation_time or
+     * VK_GOOGLE_display_timing extensions.</p>
      */
 
     public long getTimestamp() {
diff --git a/android/graphics/Typeface.java b/android/graphics/Typeface.java
index ef41507..20c22b7 100644
--- a/android/graphics/Typeface.java
+++ b/android/graphics/Typeface.java
@@ -16,25 +16,18 @@
 
 package android.graphics;
 
-import static android.content.res.FontResourcesParser.ProviderResourceEntry;
-import static android.content.res.FontResourcesParser.FontFileResourceEntry;
-import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
 import static android.content.res.FontResourcesParser.FamilyResourceEntry;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
+import static android.content.res.FontResourcesParser.FontFileResourceEntry;
+import static android.content.res.FontResourcesParser.ProviderResourceEntry;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetManager;
-import android.graphics.FontListParser;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ParcelFileDescriptor;
-import android.os.ResultReceiver;
 import android.provider.FontRequest;
 import android.provider.FontsContract;
 import android.text.FontConfig;
@@ -49,7 +42,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
-import libcore.io.IoUtils;
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -58,18 +53,17 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
-import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * The Typeface class specifies the typeface and intrinsic style of a font.
@@ -81,6 +75,9 @@
 
     private static String TAG = "Typeface";
 
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            Typeface.class.getClassLoader(), nativeGetReleaseFunc(), 64);
+
     /** The default NORMAL typeface object */
     public static final Typeface DEFAULT;
     /**
@@ -130,6 +127,11 @@
      */
     public long native_instance;
 
+    /** @hide */
+    @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Style {}
+
     // Style
     public static final int NORMAL = 0;
     public static final int BOLD = 1;
@@ -137,8 +139,15 @@
     public static final int BOLD_ITALIC = 3;
     /** @hide */ public static final int STYLE_MASK = 0x03;
 
-    private int mStyle = 0;
-    private int mWeight = 0;
+    private @Style int mStyle = 0;
+
+    /**
+     * A maximum value for the weight value.
+     * @hide
+     */
+    public static final int MAX_WEIGHT = 1000;
+
+    private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0;
 
     // Value for weight and italic. Indicates the value is resolved by font metadata.
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -158,15 +167,13 @@
         nativeSetDefault(t.native_instance);
     }
 
-    // TODO: Make this public API. (b/64852739)
-    /** @hide */
-    @VisibleForTesting
-    public int getWeight() {
+    /** Returns the typeface's weight value */
+    public @IntRange(from = 0, to = 1000) int getWeight() {
         return mWeight;
     }
 
     /** Returns the typeface's intrinsic style attributes */
-    public int getStyle() {
+    public @Style int getStyle() {
         return mStyle;
     }
 
@@ -394,7 +401,7 @@
          * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
          * for style matching during font selection.
          *
-         * @param results The array of {@link FontsContract.FontInfo}
+         * @param fonts The array of {@link FontsContract.FontInfo}
          * @param buffers The mapping from URI to buffers to be used during building.
          * @hide
          */
@@ -672,7 +679,7 @@
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
      */
-    public static Typeface create(String familyName, int style) {
+    public static Typeface create(String familyName, @Style int style) {
         return create(sSystemFontMap.get(familyName), style);
     }
 
@@ -693,7 +700,7 @@
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
      */
-    public static Typeface create(Typeface family, int style) {
+    public static Typeface create(Typeface family, @Style int style) {
         if ((style & ~STYLE_MASK) != 0) {
             style = NORMAL;
         }
@@ -789,7 +796,7 @@
      *
      * @return the default typeface that corresponds to the style
      */
-    public static Typeface defaultFromStyle(int style) {
+    public static Typeface defaultFromStyle(@Style int style) {
         return sDefaults[style];
     }
 
@@ -801,39 +808,18 @@
      * @return The new typeface.
      */
     public static Typeface createFromAsset(AssetManager mgr, String path) {
-        if (path == null) {
-            throw new NullPointerException();  // for backward compatibility
-        }
-        synchronized (sDynamicCacheLock) {
-            Typeface typeface = new Builder(mgr, path).build();
-            if (typeface != null) return typeface;
+        Preconditions.checkNotNull(path); // for backward compatibility
+        Preconditions.checkNotNull(mgr);
 
-            final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
-                    null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
-                    DEFAULT_FAMILY);
-            typeface = sDynamicTypefaceCache.get(key);
-            if (typeface != null) return typeface;
-
-            final FontFamily fontFamily = new FontFamily();
-            if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
-                    0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
-                    null /* axes */)) {
-                // Due to backward compatibility, even if the font is not supported by our font
-                // stack, we need to place the empty font at the first place. The typeface with
-                // empty font behaves different from default typeface especially in fallback
-                // font selection.
-                fontFamily.allowUnsupportedFont();
-                fontFamily.freeze();
-                final FontFamily[] families = { fontFamily };
-                typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
-                        RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                sDynamicTypefaceCache.put(key, typeface);
-                return typeface;
-            } else {
-                fontFamily.abortCreation();
-            }
+        Typeface typeface = new Builder(mgr, path).build();
+        if (typeface != null) return typeface;
+        // check if the file exists, and throw an exception for backward compatibility
+        try (InputStream inputStream = mgr.open(path)) {
+        } catch (IOException e) {
+            throw new RuntimeException("Font asset not found " + path);
         }
-        throw new RuntimeException("Font asset not found " + path);
+
+        return Typeface.DEFAULT;
     }
 
     /**
@@ -851,13 +837,22 @@
     /**
      * Create a new typeface from the specified font file.
      *
-     * @param path The path to the font data.
+     * @param file The path to the font data.
      * @return The new typeface.
      */
-    public static Typeface createFromFile(@Nullable File path) {
+    public static Typeface createFromFile(@Nullable File file) {
         // For the compatibility reasons, leaving possible NPE here.
         // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
-        return createFromFile(path.getAbsolutePath());
+
+        Typeface typeface = new Builder(file).build();
+        if (typeface != null) return typeface;
+
+        // check if the file exists, and throw an exception for backward compatibility
+        if (!file.exists()) {
+            throw new RuntimeException("Font asset not found " + file.getAbsolutePath());
+        }
+
+        return Typeface.DEFAULT;
     }
 
     /**
@@ -867,22 +862,8 @@
      * @return The new typeface.
      */
     public static Typeface createFromFile(@Nullable String path) {
-        final FontFamily fontFamily = new FontFamily();
-        if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
-                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
-            // Due to backward compatibility, even if the font is not supported by our font
-            // stack, we need to place the empty font at the first place. The typeface with
-            // empty font behaves different from default typeface especially in fallback font
-            // selection.
-            fontFamily.allowUnsupportedFont();
-            fontFamily.freeze();
-            FontFamily[] families = { fontFamily };
-            return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
-                    RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-        } else {
-            fontFamily.abortCreation();
-        }
-        throw new RuntimeException("Font not found " + path);
+        Preconditions.checkNotNull(path); // for backward compatibility
+        return createFromFile(new File(path));
     }
 
     /**
@@ -900,18 +881,25 @@
     }
 
     /**
+     * This method is used by supportlib-v27.
+     * TODO: Remove private API use in supportlib: http://b/72665240
+     */
+    private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
+                int italic) {
+        return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
+    }
+
+    /**
      * Create a new typeface from an array of font families, including
      * also the font families in the fallback list.
      * @param fallbackName the family name. If given families don't support characters, the
      *               characters will be rendered with this family.
-     * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
-     *               case, the table information in the first family's font is used. If the first
-     *               family has multiple fonts, the closest to the regular weight and upright font
-     *               is used.
-     * @param italic the italic information for this family. {@link RESOLVE_BY_FONT_TABLE} can be
-     *               used. In that case, the table information in the first family's font is used.
-     *               If the first family has multiple fonts, the closest to the regular weight and
-     *               upright font is used.
+     * @param weight the weight for this family. In that case, the table information in the first
+     *               family's font is used. If the first family has multiple fonts, the closest to
+     *               the regular weight and upright font is used.
+     * @param italic the italic information for this family. In that case, the table information in
+     *               the first family's font is used. If the first family has multiple fonts, the
+     *               closest to the regular weight and upright font is used.
      * @param families array of font families
      */
     private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
@@ -937,6 +925,7 @@
         }
 
         native_instance = ni;
+        sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
     }
@@ -1146,16 +1135,6 @@
     }
 
     @Override
-    protected void finalize() throws Throwable {
-        try {
-            nativeUnref(native_instance);
-            native_instance = 0;  // Other finalizers can still call us.
-        } finally {
-            super.finalize();
-        }
-    }
-
-    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
@@ -1199,10 +1178,18 @@
     private static native long nativeCreateFromTypefaceWithVariation(
             long native_instance, List<FontVariationAxis> axes);
     private static native long nativeCreateWeightAlias(long native_instance, int weight);
-    private static native void nativeUnref(long native_instance);
-    private static native int  nativeGetStyle(long native_instance);
-    private static native int  nativeGetWeight(long native_instance);
     private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
-    private static native void nativeSetDefault(long native_instance);
     private static native int[] nativeGetSupportedAxes(long native_instance);
+
+    @CriticalNative
+    private static native void nativeSetDefault(long nativePtr);
+
+    @CriticalNative
+    private static native int  nativeGetStyle(long nativePtr);
+
+    @CriticalNative
+    private static native int  nativeGetWeight(long nativePtr);
+
+    @CriticalNative
+    private static native long nativeGetReleaseFunc();
 }
diff --git a/android/graphics/drawable/AdaptiveIconDrawable.java b/android/graphics/drawable/AdaptiveIconDrawable.java
index 1d0cfa5..fdd638a 100644
--- a/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -44,6 +44,7 @@
 import android.util.PathParser;
 
 import com.android.internal.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -668,13 +669,7 @@
 
     @Override
     public void setAlpha(int alpha) {
-        final ChildDrawable[] array = mLayerState.mChildren;
-        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
-            final Drawable dr = array[i].mDrawable;
-            if (dr != null) {
-                dr.setAlpha(alpha);
-            }
-        }
+        mPaint.setAlpha(alpha);
     }
 
     @Override
diff --git a/android/graphics/drawable/AnimatedImageDrawable.java b/android/graphics/drawable/AnimatedImageDrawable.java
index 3034a10..898939e 100644
--- a/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/android/graphics/drawable/AnimatedImageDrawable.java
@@ -20,39 +20,266 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.ImageDecoder;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.SystemClock;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
 
-import libcore.io.IoUtils;
+import com.android.internal.R;
+
+import dalvik.annotation.optimization.FastNative;
+
 import libcore.util.NativeAllocationRegistry;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.Runnable;
+import java.util.ArrayList;
 
 /**
- * @hide
+ * {@link Drawable} for drawing animated images (like GIF).
+ *
+ * <p>The framework handles decoding subsequent frames in another thread and
+ * updating when necessary. The drawable will only animate while it is being
+ * displayed.</p>
+ *
+ * <p>Created by {@link ImageDecoder#decodeDrawable}. A user needs to call
+ * {@link #start} to start the animation.</p>
+ *
+ * <p>It can also be defined in XML using the <code>&lt;animated-image></code>
+ * element.</p>
+ *
+ * @attr ref android.R.styleable#AnimatedImageDrawable_src
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoStart
+ * @attr ref android.R.styleable#AnimatedImageDrawable_repeatCount
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoMirrored
  */
-public class AnimatedImageDrawable extends Drawable implements Animatable {
-    private final long                mNativePtr;
-    private final InputStream         mInputStream;
-    private final AssetFileDescriptor mAssetFd;
+public class AnimatedImageDrawable extends Drawable implements Animatable2 {
+    private int mIntrinsicWidth;
+    private int mIntrinsicHeight;
 
-    private final int                 mIntrinsicWidth;
-    private final int                 mIntrinsicHeight;
+    private boolean mStarting;
 
-    private Runnable mRunnable = new Runnable() {
-        @Override
-        public void run() {
-            invalidateSelf();
+    private Handler mHandler;
+
+    private class State {
+        State(long nativePtr, InputStream is, AssetFileDescriptor afd) {
+            mNativePtr = nativePtr;
+            mInputStream = is;
+            mAssetFd = afd;
         }
-    };
+
+        final long mNativePtr;
+
+        // These just keep references so the native code can continue using them.
+        private final InputStream mInputStream;
+        private final AssetFileDescriptor mAssetFd;
+
+        int[] mThemeAttrs = null;
+        boolean mAutoMirrored = false;
+        int mRepeatCount = REPEAT_UNDEFINED;
+    }
+
+    private State mState;
+
+    private Runnable mRunnable;
+
+    private ColorFilter mColorFilter;
+
+    /**
+     *  Pass this to {@link #setRepeatCount} to repeat infinitely.
+     *
+     *  <p>{@link Animatable2.AnimationCallback#onAnimationEnd} will never be
+     *  called unless there is an error.</p>
+     */
+    public static final int REPEAT_INFINITE = -1;
+
+    /** @removed
+     * @deprecated Replaced with REPEAT_INFINITE to match other APIs.
+     */
+    @java.lang.Deprecated
+    public static final int LOOP_INFINITE = REPEAT_INFINITE;
+
+    private static final int REPEAT_UNDEFINED = -2;
+
+    /**
+     *  Specify the number of times to repeat the animation.
+     *
+     *  <p>By default, the repeat count in the encoded data is respected. If set
+     *  to {@link #REPEAT_INFINITE}, the animation will repeat as long as it is
+     *  displayed. If the value is {@code 0}, the animation will play once.</p>
+     *
+     *  <p>This call replaces the current repeat count. If the encoded data
+     *  specified a repeat count of {@code 2} (meaning that
+     *  {@link #getRepeatCount()} returns {@code 2}, the animation will play
+     *  three times. Calling {@code setRepeatCount(1)} will result in playing only
+     *  twice and {@link #getRepeatCount()} returning {@code 1}.</p>
+     *
+     *  <p>If the animation is already playing, the iterations that have already
+     *  occurred count towards the new count. If the animation has already
+     *  repeated the appropriate number of times (or more), it will finish its
+     *  current iteration and then stop.</p>
+     */
+    public void setRepeatCount(@IntRange(from = REPEAT_INFINITE) int repeatCount) {
+        if (repeatCount < REPEAT_INFINITE) {
+            throw new IllegalArgumentException("invalid value passed to setRepeatCount"
+                    + repeatCount);
+        }
+        if (mState.mRepeatCount != repeatCount) {
+            mState.mRepeatCount = repeatCount;
+            if (mState.mNativePtr != 0) {
+                nSetRepeatCount(mState.mNativePtr, repeatCount);
+            }
+        }
+    }
+
+    /** @removed
+     * @deprecated Replaced with setRepeatCount to match other APIs.
+     */
+    @java.lang.Deprecated
+    public void setLoopCount(int loopCount) {
+        setRepeatCount(loopCount);
+    }
+
+    /**
+     *  Retrieve the number of times the animation will repeat.
+     *
+     *  <p>By default, the repeat count in the encoded data is respected. If the
+     *  value is {@link #REPEAT_INFINITE}, the animation will repeat as long as
+     *  it is displayed. If the value is {@code 0}, it will play once.</p>
+     *
+     *  <p>Calling {@link #setRepeatCount} will make future calls to this method
+     *  return the value passed to {@link #setRepeatCount}.</p>
+     */
+    public int getRepeatCount() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called getRepeatCount on empty AnimatedImageDrawable");
+        }
+        if (mState.mRepeatCount == REPEAT_UNDEFINED) {
+            mState.mRepeatCount = nGetRepeatCount(mState.mNativePtr);
+
+        }
+        return mState.mRepeatCount;
+    }
+
+    /** @removed
+     * @deprecated Replaced with getRepeatCount to match other APIs.
+     */
+    @java.lang.Deprecated
+    public int getLoopCount(int loopCount) {
+        return getRepeatCount();
+    }
+
+    /**
+     * Create an empty AnimatedImageDrawable.
+     */
+    public AnimatedImageDrawable() {
+        mState = new State(0, null, null);
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+
+        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedImageDrawable);
+        updateStateFromTypedArray(a, mSrcDensityOverride);
+    }
+
+    private void updateStateFromTypedArray(TypedArray a, int srcDensityOverride)
+            throws XmlPullParserException {
+        State oldState = mState;
+        final Resources r = a.getResources();
+        final int srcResId = a.getResourceId(R.styleable.AnimatedImageDrawable_src, 0);
+        if (srcResId != 0) {
+            // Follow the density handling in BitmapDrawable.
+            final TypedValue value = new TypedValue();
+            r.getValueForDensity(srcResId, srcDensityOverride, value, true);
+            if (srcDensityOverride > 0 && value.density > 0
+                    && value.density != TypedValue.DENSITY_NONE) {
+                if (value.density == srcDensityOverride) {
+                    value.density = r.getDisplayMetrics().densityDpi;
+                } else {
+                    value.density =
+                            (value.density * r.getDisplayMetrics().densityDpi) / srcDensityOverride;
+                }
+            }
+
+            int density = Bitmap.DENSITY_NONE;
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                density = value.density;
+            }
+
+            Drawable drawable = null;
+            try {
+                InputStream is = r.openRawResource(srcResId, value);
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                    if (!info.isAnimated()) {
+                        throw new IllegalArgumentException("image is not animated");
+                    }
+                });
+            } catch (IOException e) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        ": <animated-image> requires a valid 'src' attribute", null, e);
+            }
+
+            if (!(drawable instanceof AnimatedImageDrawable)) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        ": <animated-image> did not decode animated");
+            }
+
+            // This may have previously been set without a src if we were waiting for a
+            // theme.
+            final int repeatCount = mState.mRepeatCount;
+            // Transfer the state of other to this one. other will be discarded.
+            AnimatedImageDrawable other = (AnimatedImageDrawable) drawable;
+            mState = other.mState;
+            other.mState = null;
+            mIntrinsicWidth =  other.mIntrinsicWidth;
+            mIntrinsicHeight = other.mIntrinsicHeight;
+            if (repeatCount != REPEAT_UNDEFINED) {
+                this.setRepeatCount(repeatCount);
+            }
+        }
+
+        mState.mThemeAttrs = a.extractThemeAttrs();
+        if (mState.mNativePtr == 0 && (mState.mThemeAttrs == null
+                || mState.mThemeAttrs[R.styleable.AnimatedImageDrawable_src] == 0)) {
+            throw new XmlPullParserException(a.getPositionDescription() +
+                    ": <animated-image> requires a valid 'src' attribute");
+        }
+
+        mState.mAutoMirrored = a.getBoolean(
+                R.styleable.AnimatedImageDrawable_autoMirrored, oldState.mAutoMirrored);
+
+        int repeatCount = a.getInt(
+                R.styleable.AnimatedImageDrawable_repeatCount, REPEAT_UNDEFINED);
+        if (repeatCount != REPEAT_UNDEFINED) {
+            this.setRepeatCount(repeatCount);
+        }
+
+        boolean autoStart = a.getBoolean(
+                R.styleable.AnimatedImageDrawable_autoStart, false);
+        if (autoStart && mState.mNativePtr != 0) {
+            this.start();
+        }
+    }
 
     /**
      * @hide
@@ -80,30 +307,13 @@
             mIntrinsicHeight = cropRect.height();
         }
 
-        mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
-        mInputStream = inputStream;
-        mAssetFd = afd;
+        mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect),
+                inputStream, afd);
 
-        // FIXME: Use the right size for the native allocation.
-        long nativeSize = 200;
+        final long nativeSize = nNativeByteSize(mState.mNativePtr);
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
                 AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
-        registry.registerNativeAllocation(this, mNativePtr);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        // FIXME: It's a shame that we have *both* a native finalizer and a Java
-        // one. The native one is necessary to report how much memory is being
-        // used natively, and this one is necessary to close the input. An
-        // alternative might be to read the entire stream ahead of time, so we
-        // can eliminate the Java finalizer.
-        try {
-            IoUtils.closeQuietly(mInputStream);
-            IoUtils.closeQuietly(mAssetFd);
-        } finally {
-            super.finalize();
-        }
+        registry.registerNativeAllocation(mState, mState.mNativePtr);
     }
 
     @Override
@@ -116,36 +326,76 @@
         return mIntrinsicHeight;
     }
 
+    // nDraw returns -1 if the animation has finished.
+    private static final int FINISHED = -1;
+
     @Override
     public void draw(@NonNull Canvas canvas) {
-        long nextUpdate = nDraw(mNativePtr, canvas.getNativeCanvasWrapper());
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called draw on empty AnimatedImageDrawable");
+        }
+
+        if (mStarting) {
+            mStarting = false;
+
+            postOnAnimationStart();
+        }
+
+        long nextUpdate = nDraw(mState.mNativePtr, canvas.getNativeCanvasWrapper());
         // a value <= 0 indicates that the drawable is stopped or that renderThread
         // will manage the animation
         if (nextUpdate > 0) {
+            if (mRunnable == null) {
+                mRunnable = this::invalidateSelf;
+            }
             scheduleSelf(mRunnable, nextUpdate);
+        } else if (nextUpdate == FINISHED) {
+            // This means the animation was drawn in software mode and ended.
+            postOnAnimationEnd();
         }
     }
 
     @Override
-    public void setAlpha(@IntRange(from=0,to=255) int alpha) {
+    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
         if (alpha < 0 || alpha > 255) {
             throw new IllegalArgumentException("Alpha must be between 0 and"
                    + " 255! provided " + alpha);
         }
-        nSetAlpha(mNativePtr, alpha);
+
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called setAlpha on empty AnimatedImageDrawable");
+        }
+
+        nSetAlpha(mState.mNativePtr, alpha);
         invalidateSelf();
     }
 
     @Override
     public int getAlpha() {
-        return nGetAlpha(mNativePtr);
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called getAlpha on empty AnimatedImageDrawable");
+        }
+        return nGetAlpha(mState.mNativePtr);
     }
 
     @Override
     public void setColorFilter(@Nullable ColorFilter colorFilter) {
-        long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance();
-        nSetColorFilter(mNativePtr, nativeFilter);
-        invalidateSelf();
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called setColorFilter on empty AnimatedImageDrawable");
+        }
+
+        if (colorFilter != mColorFilter) {
+            mColorFilter = colorFilter;
+            long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance();
+            nSetColorFilter(mState.mNativePtr, nativeFilter);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    @Nullable
+    public ColorFilter getColorFilter() {
+        return mColorFilter;
     }
 
     @Override
@@ -153,39 +403,220 @@
         return PixelFormat.TRANSLUCENT;
     }
 
-    // TODO: Add a Constant State?
-    // @Override
-    // public @Nullable ConstantState getConstantState() {}
-
-
-    // Animatable overrides
     @Override
-    public boolean isRunning() {
-        return nIsRunning(mNativePtr);
-    }
-
-    @Override
-    public void start() {
-        if (nStart(mNativePtr)) {
-            invalidateSelf();
+    public void setAutoMirrored(boolean mirrored) {
+        if (mState.mAutoMirrored != mirrored) {
+            mState.mAutoMirrored = mirrored;
+            if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL && mState.mNativePtr != 0) {
+                nSetMirrored(mState.mNativePtr, mirrored);
+                invalidateSelf();
+            }
         }
     }
 
     @Override
-    public void stop() {
-        nStop(mNativePtr);
+    public boolean onLayoutDirectionChanged(int layoutDirection) {
+        if (!mState.mAutoMirrored || mState.mNativePtr == 0) {
+            return false;
+        }
+
+        final boolean mirror = layoutDirection == View.LAYOUT_DIRECTION_RTL;
+        nSetMirrored(mState.mNativePtr, mirror);
+        return true;
     }
 
+    @Override
+    public final boolean isAutoMirrored() {
+        return mState.mAutoMirrored;
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        if (!super.setVisible(visible, restart)) {
+            return false;
+        }
+
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called setVisible on empty AnimatedImageDrawable");
+        }
+
+        if (!visible) {
+            nMarkInvisible(mState.mNativePtr);
+        }
+
+        return true;
+    }
+
+    // Animatable overrides
+    /**
+     *  Return whether the animation is currently running.
+     *
+     *  <p>When this drawable is created, this will return {@code false}. A client
+     *  needs to call {@link #start} to start the animation.</p>
+     */
+    @Override
+    public boolean isRunning() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called isRunning on empty AnimatedImageDrawable");
+        }
+        return nIsRunning(mState.mNativePtr);
+    }
+
+    /**
+     *  Start the animation.
+     *
+     *  <p>Does nothing if the animation is already running. If the animation is stopped,
+     *  this will reset it.</p>
+     *
+     *  <p>When the drawable is drawn, starting the animation,
+     *  {@link Animatable2.AnimationCallback#onAnimationStart} will be called.</p>
+     */
+    @Override
+    public void start() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called start on empty AnimatedImageDrawable");
+        }
+
+        if (nStart(mState.mNativePtr)) {
+            mStarting = true;
+            invalidateSelf();
+        }
+    }
+
+    /**
+     *  Stop the animation.
+     *
+     *  <p>If the animation is stopped, it will continue to display the frame
+     *  it was displaying when stopped.</p>
+     */
+    @Override
+    public void stop() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
+        }
+        if (nStop(mState.mNativePtr)) {
+            postOnAnimationEnd();
+        }
+    }
+
+    // Animatable2 overrides
+    private ArrayList<Animatable2.AnimationCallback> mAnimationCallbacks = null;
+
+    @Override
+    public void registerAnimationCallback(@NonNull AnimationCallback callback) {
+        if (callback == null) {
+            return;
+        }
+
+        if (mAnimationCallbacks == null) {
+            mAnimationCallbacks = new ArrayList<Animatable2.AnimationCallback>();
+            nSetOnAnimationEndListener(mState.mNativePtr, this);
+        }
+
+        if (!mAnimationCallbacks.contains(callback)) {
+            mAnimationCallbacks.add(callback);
+        }
+    }
+
+    @Override
+    public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
+        if (callback == null || mAnimationCallbacks == null
+                || !mAnimationCallbacks.remove(callback)) {
+            return false;
+        }
+
+        if (mAnimationCallbacks.isEmpty()) {
+            clearAnimationCallbacks();
+        }
+
+        return true;
+    }
+
+    @Override
+    public void clearAnimationCallbacks() {
+        if (mAnimationCallbacks != null) {
+            mAnimationCallbacks = null;
+            nSetOnAnimationEndListener(mState.mNativePtr, null);
+        }
+    }
+
+    private void postOnAnimationStart() {
+        if (mAnimationCallbacks == null) {
+            return;
+        }
+
+        getHandler().post(() -> {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationStart(this);
+            }
+        });
+    }
+
+    private void postOnAnimationEnd() {
+        if (mAnimationCallbacks == null) {
+            return;
+        }
+
+        getHandler().post(() -> {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationEnd(this);
+            }
+        });
+    }
+
+    private Handler getHandler() {
+        if (mHandler == null) {
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+        return mHandler;
+    }
+
+    /**
+     *  Called by JNI.
+     *
+     *  The JNI code has already posted this to the thread that created the
+     *  callback, so no need to post.
+     */
+    @SuppressWarnings("unused")
+    private void onAnimationEnd() {
+        if (mAnimationCallbacks != null) {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationEnd(this);
+            }
+        }
+    }
+
+
     private static native long nCreate(long nativeImageDecoder,
             @Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
         throws IOException;
+    @FastNative
     private static native long nGetNativeFinalizer();
     private static native long nDraw(long nativePtr, long canvasNativePtr);
+    @FastNative
     private static native void nSetAlpha(long nativePtr, int alpha);
+    @FastNative
     private static native int nGetAlpha(long nativePtr);
+    @FastNative
     private static native void nSetColorFilter(long nativePtr, long nativeFilter);
+    @FastNative
     private static native boolean nIsRunning(long nativePtr);
+    // Return whether the animation started.
+    @FastNative
     private static native boolean nStart(long nativePtr);
-    private static native void nStop(long nativePtr);
+    @FastNative
+    private static native boolean nStop(long nativePtr);
+    @FastNative
+    private static native int nGetRepeatCount(long nativePtr);
+    @FastNative
+    private static native void nSetRepeatCount(long nativePtr, int repeatCount);
+    // Pass the drawable down to native so it can call onAnimationEnd.
+    private static native void nSetOnAnimationEndListener(long nativePtr,
+            @Nullable AnimatedImageDrawable drawable);
+    @FastNative
     private static native long nNativeByteSize(long nativePtr);
+    @FastNative
+    private static native void nMarkInvisible(long nativePtr);
+    @FastNative
+    private static native void nSetMirrored(long nativePtr, boolean mirror);
 }
diff --git a/android/graphics/drawable/AnimatedVectorDrawable.java b/android/graphics/drawable/AnimatedVectorDrawable.java
index e74dc6d..54358e3 100644
--- a/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -201,10 +201,10 @@
  *     android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
  *     &lt;target
  *         android:name=&quot;rotationGroup&quot;
- *         android:animation=&quot;@anim/rotation&quot; /&gt;
+ *         android:animation=&quot;@animator/rotation&quot; /&gt;
  *     &lt;target
  *         android:name=&quot;v&quot;
- *         android:animation=&quot;@anim/path_morph&quot; /&gt;
+ *         android:animation=&quot;@animator/path_morph&quot; /&gt;
  * &lt;/animated-vector&gt;
  * </pre>
  * </li>
diff --git a/android/graphics/drawable/BitmapDrawable.java b/android/graphics/drawable/BitmapDrawable.java
index 7ad062a..44b783b 100644
--- a/android/graphics/drawable/BitmapDrawable.java
+++ b/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Outline;
@@ -49,6 +50,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -111,7 +113,7 @@
      */
     @Deprecated
     public BitmapDrawable() {
-        mBitmapState = new BitmapState((Bitmap) null);
+        init(new BitmapState((Bitmap) null), null);
     }
 
     /**
@@ -124,8 +126,7 @@
     @SuppressWarnings("unused")
     @Deprecated
     public BitmapDrawable(Resources res) {
-        mBitmapState = new BitmapState((Bitmap) null);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState((Bitmap) null), res);
     }
 
     /**
@@ -135,7 +136,7 @@
      */
     @Deprecated
     public BitmapDrawable(Bitmap bitmap) {
-        this(new BitmapState(bitmap), null);
+        init(new BitmapState(bitmap), null);
     }
 
     /**
@@ -143,8 +144,7 @@
      * the display metrics of the resources.
      */
     public BitmapDrawable(Resources res, Bitmap bitmap) {
-        this(new BitmapState(bitmap), res);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState(bitmap), res);
     }
 
     /**
@@ -154,10 +154,7 @@
      */
     @Deprecated
     public BitmapDrawable(String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
-        }
+        this(null, filepath);
     }
 
     /**
@@ -165,10 +162,21 @@
      */
     @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+        Bitmap bitmap = null;
+        try (FileInputStream stream = new FileInputStream(filepath)) {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+            }
         }
     }
 
@@ -179,10 +187,7 @@
      */
     @Deprecated
     public BitmapDrawable(java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
-        }
+        this(null, is);
     }
 
     /**
@@ -190,10 +195,21 @@
      */
     @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+        Bitmap bitmap = null;
+        try {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+            }
         }
     }
 
@@ -812,9 +828,19 @@
                 }
             }
 
+            int density = Bitmap.DENSITY_NONE;
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                density = value.density;
+            }
+
             Bitmap bitmap = null;
             try (InputStream is = r.openRawResource(srcResId, value)) {
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
             } catch (Exception e) {
                 // Do nothing and pick up the error below.
             }
@@ -1013,14 +1039,21 @@
         }
     }
 
+    private BitmapDrawable(BitmapState state, Resources res) {
+        init(state, res);
+    }
+
     /**
-     * The one constructor to rule them all. This is called by all public
+     * The one helper to rule them all. This is called by all public & private
      * constructors to set the state and initialize local properties.
      */
-    private BitmapDrawable(BitmapState state, Resources res) {
+    private void init(BitmapState state, Resources res) {
         mBitmapState = state;
-
         updateLocalState(res);
+
+        if (mBitmapState != null && res != null) {
+            mBitmapState.mTargetDensity = mTargetDensity;
+        }
     }
 
     /**
diff --git a/android/graphics/drawable/Drawable.java b/android/graphics/drawable/Drawable.java
index f17cd76..8af2fd8 100644
--- a/android/graphics/drawable/Drawable.java
+++ b/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -50,11 +51,13 @@
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.StateSet;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.View;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -1168,13 +1171,21 @@
     /**
      * Create a drawable from an inputstream, using the given resources and
      * value to determine density information.
+     *
+     * @deprecated Prefer the version without an Options object.
      */
-    public static Drawable createFromResourceStream(Resources res, TypedValue value,
-            InputStream is, String srcName, BitmapFactory.Options opts) {
+    @Nullable
+    public static Drawable createFromResourceStream(@Nullable Resources res,
+            @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName,
+            @Nullable BitmapFactory.Options opts) {
         if (is == null) {
             return null;
         }
 
+        if (opts == null) {
+            return getBitmapDrawable(res, value, is);
+        }
+
         /*  ugh. The decodeStream contract is that we have already allocated
             the pad rect, but if the bitmap does not had a ninepatch chunk,
             then the pad will be ignored. If we could change this to lazily
@@ -1190,7 +1201,6 @@
         // an application in compatibility mode, without scaling those down
         // to the compatibility density only to have them scaled back up when
         // drawn to the screen.
-        if (opts == null) opts = new BitmapFactory.Options();
         opts.inScreenDensity = Drawable.resolveDensity(res, 0);
         Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
         if (bm != null) {
@@ -1207,6 +1217,33 @@
         return null;
     }
 
+    private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+        try {
+            ImageDecoder.Source source = null;
+            if (value != null) {
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                source = ImageDecoder.createSource(res, is, density);
+            } else {
+                source = ImageDecoder.createSource(res, is);
+            }
+
+            return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (IOException e) {
+            /*  do nothing.
+                If the exception happened on decode, the drawable will be null.
+            */
+            Log.e("Drawable", "Unable to decode stream: " + e);
+        }
+        return null;
+    }
+
     /**
      * Create a drawable from an XML document. For more information on how to
      * create resources in XML, see
@@ -1306,11 +1343,10 @@
         }
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
-        try {
-            Bitmap bm = BitmapFactory.decodeFile(pathName);
-            if (bm != null) {
-                return drawableFromBitmap(null, bm, null, null, null, pathName);
-            }
+        try (FileInputStream stream = new FileInputStream(pathName)) {
+            return getBitmapDrawable(null, null, stream);
+        } catch(IOException e) {
+            // Do nothing; we will just return null if the FileInputStream had an error
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
diff --git a/android/graphics/drawable/DrawableInflater.java b/android/graphics/drawable/DrawableInflater.java
index eea7048..0ee9071 100644
--- a/android/graphics/drawable/DrawableInflater.java
+++ b/android/graphics/drawable/DrawableInflater.java
@@ -185,6 +185,8 @@
                 return new BitmapDrawable();
             case "nine-patch":
                 return new NinePatchDrawable();
+            case "animated-image":
+                return new AnimatedImageDrawable();
             default:
                 return null;
         }
diff --git a/android/graphics/drawable/GradientDrawable.java b/android/graphics/drawable/GradientDrawable.java
index 6c3aea2..dfdddb2 100644
--- a/android/graphics/drawable/GradientDrawable.java
+++ b/android/graphics/drawable/GradientDrawable.java
@@ -42,6 +42,7 @@
 import android.graphics.RectF;
 import android.graphics.Shader;
 import android.graphics.SweepGradient;
+import android.graphics.Xfermode;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -814,6 +815,24 @@
         }
     }
 
+    /**
+     * @param mode to draw this drawable with
+     * @hide
+     */
+    @Override
+    public void setXfermode(@Nullable Xfermode mode) {
+        super.setXfermode(mode);
+        mFillPaint.setXfermode(mode);
+    }
+
+    /**
+     * @param aa to draw this drawable with
+     * @hide
+     */
+    public void setAntiAlias(boolean aa) {
+        mFillPaint.setAntiAlias(aa);
+    }
+
     private void buildPathIfDirty() {
         final GradientState st = mGradientState;
         if (mPathIsDirty) {
@@ -2074,6 +2093,7 @@
             }
             mRadius = radius;
             mRadiusArray = null;
+            computeOpacity();
         }
 
         public void setCornerRadii(float[] radii) {
@@ -2081,6 +2101,7 @@
             if (radii == null) {
                 mRadius = 0;
             }
+            computeOpacity();
         }
 
         public void setSize(int width, int height) {
diff --git a/android/graphics/drawable/Icon.java b/android/graphics/drawable/Icon.java
index 749b759..361fe0b 100644
--- a/android/graphics/drawable/Icon.java
+++ b/android/graphics/drawable/Icon.java
@@ -18,11 +18,14 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
-import android.content.res.ColorStateList;
+import android.annotation.IdRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -60,17 +63,40 @@
 public final class Icon implements Parcelable {
     private static final String TAG = "Icon";
 
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}.
+     * @see #getType
+     */
     public static final int TYPE_BITMAP   = 1;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithResource}.
+     * @see #getType
+     */
     public static final int TYPE_RESOURCE = 2;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithData(byte[], int, int)}.
+     * @see #getType
+     */
     public static final int TYPE_DATA     = 3;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithContentUri}
+     * or {@link Icon#createWithFilePath(String)}.
+     * @see #getType
+     */
     public static final int TYPE_URI      = 4;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithAdaptiveBitmap}.
+     * @see #getType
+     */
     public static final int TYPE_ADAPTIVE_BITMAP = 5;
 
+    /**
+     * @hide
+     */
+    @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+    public @interface IconType {
+    }
+
     private static final int VERSION_STREAM_SERIALIZER = 1;
 
     private final int mType;
@@ -99,14 +125,12 @@
     private int             mInt2;
 
     /**
-     * @return The type of image data held in this Icon. One of
-     * {@link #TYPE_BITMAP},
-     * {@link #TYPE_RESOURCE},
-     * {@link #TYPE_DATA}, or
-     * {@link #TYPE_URI}.
-     * {@link #TYPE_ADAPTIVE_BITMAP}
-     * @hide
+     * Gets the type of the icon provided.
+     * <p>
+     * Note that new types may be added later, so callers should guard against other
+     * types being returned.
      */
+    @IconType
     public int getType() {
         return mType;
     }
@@ -179,9 +203,13 @@
     }
 
     /**
-     * @return The package containing resources for this {@link #TYPE_RESOURCE} Icon.
-     * @hide
+     * Gets the package used to create this icon.
+     * <p>
+     * Only valid for icons of type {@link #TYPE_RESOURCE}.
+     * Note: This package may not be available if referenced in the future, and it is
+     * up to the caller to ensure safety if this package is re-used and/or persisted.
      */
+    @NonNull
     public String getResPackage() {
         if (mType != TYPE_RESOURCE) {
             throw new IllegalStateException("called getResPackage() on " + this);
@@ -190,9 +218,13 @@
     }
 
     /**
-     * @return The resource ID for this {@link #TYPE_RESOURCE} Icon.
-     * @hide
+     * Gets the resource used to create this icon.
+     * <p>
+     * Only valid for icons of type {@link #TYPE_RESOURCE}.
+     * Note: This resource may not be available if the application changes at all, and it is
+     * up to the caller to ensure safety if this resource is re-used and/or persisted.
      */
+    @IdRes
     public int getResId() {
         if (mType != TYPE_RESOURCE) {
             throw new IllegalStateException("called getResId() on " + this);
@@ -212,9 +244,13 @@
     }
 
     /**
-     * @return The {@link android.net.Uri} for this {@link #TYPE_URI} Icon.
-     * @hide
+     * Gets the uri used to create this icon.
+     * <p>
+     * Only valid for icons of type {@link #TYPE_URI}.
+     * Note: This uri may not be available in the future, and it is
+     * up to the caller to ensure safety if this uri is re-used and/or persisted.
      */
+    @NonNull
     public Uri getUri() {
         return Uri.parse(getUriString());
     }
diff --git a/android/graphics/drawable/NinePatchDrawable.java b/android/graphics/drawable/NinePatchDrawable.java
index 1790020..66f2a31 100644
--- a/android/graphics/drawable/NinePatchDrawable.java
+++ b/android/graphics/drawable/NinePatchDrawable.java
@@ -24,9 +24,9 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -211,7 +211,8 @@
             restoreAlpha = -1;
         }
 
-        final boolean needsDensityScaling = canvas.getDensity() == 0;
+        final boolean needsDensityScaling = canvas.getDensity() == 0
+                && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
         if (needsDensityScaling) {
             restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
 
@@ -421,10 +422,6 @@
 
         final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
         if (srcResId != 0) {
-            final BitmapFactory.Options options = new BitmapFactory.Options();
-            options.inDither = !state.mDither;
-            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
             final Rect padding = new Rect();
             final Rect opticalInsets = new Rect();
             Bitmap bitmap = null;
@@ -433,7 +430,17 @@
                 final TypedValue value = new TypedValue();
                 final InputStream is = r.openRawResource(srcResId, value);
 
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setOutPaddingRect(padding);
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
 
                 is.close();
             } catch (IOException e) {
@@ -660,8 +667,9 @@
             return;
         }
 
-        final int sourceDensity = ninePatch.getDensity();
         final int targetDensity = mTargetDensity;
+        final int sourceDensity = ninePatch.getDensity() == Bitmap.DENSITY_NONE ?
+            targetDensity : ninePatch.getDensity();
 
         final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets;
         if (sourceOpticalInsets != Insets.NONE) {
diff --git a/android/graphics/drawable/RippleBackground.java b/android/graphics/drawable/RippleBackground.java
index 41d3698..2812abe 100644
--- a/android/graphics/drawable/RippleBackground.java
+++ b/android/graphics/drawable/RippleBackground.java
@@ -78,9 +78,10 @@
     }
 
     private void onStateChanged() {
-        float newOpacity = 0.0f;
-        if (mHovered) newOpacity += .25f;
-        if (mFocused) newOpacity += .75f;
+        // Hover             = .2 * alpha
+        // Focus             = .6 * alpha
+        // Focused + Hovered = .6 * alpha
+        float newOpacity = mFocused ? .6f : mHovered ? .2f : 0f;
         if (mAnimator != null) {
             mAnimator.cancel();
             mAnimator = null;
diff --git a/android/graphics/drawable/RippleForeground.java b/android/graphics/drawable/RippleForeground.java
index 4129868..a8dc34a 100644
--- a/android/graphics/drawable/RippleForeground.java
+++ b/android/graphics/drawable/RippleForeground.java
@@ -110,6 +110,7 @@
 
         // Take 60% of the maximum of the width and height, then divided half to get the radius.
         mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
+        clampStartingPosition();
     }
 
     @Override
@@ -350,7 +351,7 @@
         final float cY = mBounds.exactCenterY();
         final float dX = mStartingX - cX;
         final float dY = mStartingY - cY;
-        final float r = mTargetRadius;
+        final float r = mTargetRadius - mStartRadius;
         if (dX * dX + dY * dY > r * r) {
             // Point is outside the circle, clamp to the perimeter.
             final double angle = Math.atan2(dY, dX);
diff --git a/android/hardware/Camera.java b/android/hardware/Camera.java
index 931b5c9..9a276fb 100644
--- a/android/hardware/Camera.java
+++ b/android/hardware/Camera.java
@@ -242,6 +242,9 @@
 
     /**
      * Returns the number of physical cameras available on this device.
+     * The return value of this method might change dynamically if the device
+     * supports external cameras and an external camera is connected or
+     * disconnected.
      *
      * @return total number of accessible camera devices, or 0 if there are no
      *   cameras or an error was encountered enumerating them.
@@ -3532,8 +3535,8 @@
         /**
          * Gets the focal length (in millimeter) of the camera.
          *
-         * @return the focal length. This method will always return a valid
-         *         value.
+         * @return the focal length. Returns -1.0 when the device
+         *         doesn't report focal length information.
          */
         public float getFocalLength() {
             return Float.parseFloat(get(KEY_FOCAL_LENGTH));
@@ -3542,8 +3545,8 @@
         /**
          * Gets the horizontal angle of view in degrees.
          *
-         * @return horizontal angle of view. This method will always return a
-         *         valid value.
+         * @return horizontal angle of view. Returns -1.0 when the device
+         *         doesn't report view angle information.
          */
         public float getHorizontalViewAngle() {
             return Float.parseFloat(get(KEY_HORIZONTAL_VIEW_ANGLE));
@@ -3552,8 +3555,8 @@
         /**
          * Gets the vertical angle of view in degrees.
          *
-         * @return vertical angle of view. This method will always return a
-         *         valid value.
+         * @return vertical angle of view. Returns -1.0 when the device
+         *         doesn't report view angle information.
          */
         public float getVerticalViewAngle() {
             return Float.parseFloat(get(KEY_VERTICAL_VIEW_ANGLE));
diff --git a/android/hardware/ConsumerIrManager.java b/android/hardware/ConsumerIrManager.java
index c7a33ff..6f589cd 100644
--- a/android/hardware/ConsumerIrManager.java
+++ b/android/hardware/ConsumerIrManager.java
@@ -16,8 +16,10 @@
 
 package android.hardware;
 
+import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
@@ -27,6 +29,7 @@
  * Class that operates consumer infrared on the device.
  */
 @SystemService(Context.CONSUMER_IR_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_CONSUMER_IR)
 public final class ConsumerIrManager {
     private static final String TAG = "ConsumerIr";
 
diff --git a/android/hardware/Sensor.java b/android/hardware/Sensor.java
index 7fb0c89..7297426 100644
--- a/android/hardware/Sensor.java
+++ b/android/hardware/Sensor.java
@@ -22,7 +22,9 @@
 
 /**
  * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
- * the list of available Sensors.
+ * the list of available sensors. For more information about Android sensors,
+ * read the
+ * <a href="/guide/topics/sensors/sensors_motion.html">Motion Sensors guide</a>.</p>
  *
  * @see SensorManager
  * @see SensorEventListener
diff --git a/android/hardware/biometrics/BiometricAuthenticator.java b/android/hardware/biometrics/BiometricAuthenticator.java
new file mode 100644
index 0000000..c811999
--- /dev/null
+++ b/android/hardware/biometrics/BiometricAuthenticator.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.os.CancellationSignal;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * This is the common interface that all biometric authentication classes should implement.
+ * @hide
+ */
+public interface BiometricAuthenticator {
+
+    /**
+     * Container for biometric data
+     * @hide
+     */
+    abstract class BiometricIdentifier implements Parcelable {}
+
+    /**
+     * Container for callback data from {@link BiometricAuthenticator#authenticate(
+     * CancellationSignal, Executor, AuthenticationCallback)} and
+     * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+     * AuthenticationCallback)}
+     */
+    class AuthenticationResult {
+        private BiometricIdentifier mIdentifier;
+        private CryptoObject mCryptoObject;
+        private int mUserId;
+
+        /**
+         * @hide
+         */
+        public AuthenticationResult() { }
+
+        /**
+         * Authentication result
+         * @param crypto
+         * @param identifier
+         * @param userId
+         * @hide
+         */
+        public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+                int userId) {
+            mCryptoObject = crypto;
+            mIdentifier = identifier;
+            mUserId = userId;
+        }
+
+        /**
+         * Obtain the crypto object associated with this transaction
+         * @return crypto object provided to {@link BiometricAuthenticator#authenticate(
+         * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+         */
+        public CryptoObject getCryptoObject() {
+            return mCryptoObject;
+        }
+
+        /**
+         * Obtain the biometric identifier associated with this operation. Applications are strongly
+         * discouraged from associating specific identifiers with specific applications or
+         * operations.
+         * @hide
+         */
+        public BiometricIdentifier getId() {
+            return mIdentifier;
+        }
+
+        /**
+         * Obtain the userId for which this biometric was authenticated.
+         * @hide
+         */
+        public int getUserId() {
+            return mUserId;
+        }
+    };
+
+    /**
+     * Callback structure provided to {@link BiometricAuthenticator#authenticate(CancellationSignal,
+     * Executor, AuthenticationCallback)} or {@link BiometricAuthenticator#authenticate(
+     * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}. Users must provide
+     * an implementation of this for listening to biometric events.
+     */
+    abstract class AuthenticationCallback {
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further actions will be made on this object.
+         * @param errorCode An integer identifying the error message
+         * @param errString A human-readable error string that can be shown on an UI
+         */
+        public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+        /**
+         * Called when a recoverable error has been encountered during authentication. The help
+         * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+         * please clean it."
+         * @param helpCode An integer identifying the error message
+         * @param helpString A human-readable string that can be shown on an UI
+         */
+        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+        /**
+         * Called when a biometric is recognized.
+         * @param result An object containing authentication-related data
+         */
+        public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+        /**
+         * Called when a biometric is valid but not recognized.
+         */
+        public void onAuthenticationFailed() {}
+
+        /**
+         * Called when a biometric has been acquired, but hasn't been processed yet.
+         * @hide
+         */
+        public void onAuthenticationAcquired(int acquireInfo) {}
+    };
+
+    /**
+     * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+     * when {@link AuthenticationCallback#onAuthenticationError(int,
+     * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+     * AuthenticationResult)} is called, at which point the crypto object becomes invalid. This
+     * operation can be canceled by using the provided cancel object. The application wil receive
+     * authentication errors through {@link AuthenticationCallback}. Calling
+     * {@link BiometricAuthenticator#authenticate(CryptoObject, CancellationSignal, Executor,
+     * AuthenticationCallback)} while an existing authentication attempt is occurring will stop
+     * the previous client and start a new authentication. The interrupted client will receive a
+     * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+     * CharSequence)}.
+     *
+     * @throws IllegalArgumentException If any of the arguments are null
+     *
+     * @param crypto Object associated with the call
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     */
+    void authenticate(@NonNull CryptoObject crypto,
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback);
+
+    /**
+     * This call warms up the hardware and starts scanning for valid biometrics. It terminates
+     * when {@link AuthenticationCallback#onAuthenticationError(int,
+     * CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
+     * AuthenticationResult)} is called. This operation can be canceled by using the provided cancel
+     * object. The application wil receive authentication errors through
+     * {@link AuthenticationCallback}. Calling {@link BiometricAuthenticator#authenticate(
+     * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)} while an existing
+     * authentication attempt is occurring will stop the previous client and start a new
+     * authentication. The interrupted client will receive a cancelled notification through
+     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+     *
+     * @throws IllegalArgumentException If any of the arguments are null
+     *
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     */
+    void authenticate(@NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback);
+}
diff --git a/android/hardware/biometrics/BiometricConstants.java b/android/hardware/biometrics/BiometricConstants.java
new file mode 100644
index 0000000..a037289
--- /dev/null
+++ b/android/hardware/biometrics/BiometricConstants.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+
+/**
+ * Interface containing all of the biometric modality agnostic constants.
+ * @hide
+ */
+public interface BiometricConstants {
+    //
+    // Error messages from biometric hardware during initilization, enrollment, authentication or
+    // removal.
+    //
+
+    /**
+     * The hardware is unavailable. Try again later.
+     */
+    int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
+
+    /**
+     * Error state returned when the sensor was unable to process the current image.
+     */
+    int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2;
+
+    /**
+     * Error state returned when the current request has been running too long. This is intended to
+     * prevent programs from waiting for the biometric sensor indefinitely. The timeout is platform
+     * and sensor-specific, but is generally on the order of 30 seconds.
+     */
+    int BIOMETRIC_ERROR_TIMEOUT = 3;
+
+    /**
+     * Error state returned for operations like enrollment; the operation cannot be completed
+     * because there's not enough storage remaining to complete the operation.
+     */
+    int BIOMETRIC_ERROR_NO_SPACE = 4;
+
+    /**
+     * The operation was canceled because the biometric sensor is unavailable. For example, this may
+     * happen when the user is switched, the device is locked or another pending operation prevents
+     * or disables it.
+     */
+    int BIOMETRIC_ERROR_CANCELED = 5;
+
+    /**
+     * The {@link BiometricManager#remove} call failed. Typically this will happen when the provided
+     * biometric id was incorrect.
+     *
+     * @hide
+     */
+    int BIOMETRIC_ERROR_UNABLE_TO_REMOVE = 6;
+
+    /**
+     * The operation was canceled because the API is locked out due to too many attempts.
+     * This occurs after 5 failed attempts, and lasts for 30 seconds.
+     */
+    int BIOMETRIC_ERROR_LOCKOUT = 7;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * These messages are typically reserved for internal operations such as enrollment, but may be
+     * used to express vendor errors not otherwise covered. Applications are expected to show the
+     * error message string if they happen, but are advised not to rely on the message id since they
+     * will be device and vendor-specific
+     */
+    int BIOMETRIC_ERROR_VENDOR = 8;
+
+    /**
+     * The operation was canceled because BIOMETRIC_ERROR_LOCKOUT occurred too many times.
+     * Biometric authentication is disabled until the user unlocks with strong authentication
+     * (PIN/Pattern/Password)
+     */
+    int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9;
+
+    /**
+     * The user canceled the operation. Upon receiving this, applications should use alternate
+     * authentication (e.g. a password). The application should also provide the means to return to
+     * biometric authentication, such as a "use <biometric>" button.
+     */
+    int BIOMETRIC_ERROR_USER_CANCELED = 10;
+
+    /**
+     * The user does not have any biometrics enrolled.
+     */
+    int BIOMETRIC_ERROR_NO_BIOMETRICS = 11;
+
+    /**
+     * The device does not have a biometric sensor.
+     */
+    int BIOMETRIC_ERROR_HW_NOT_PRESENT = 12;
+
+    /**
+     * @hide
+     */
+    int BIOMETRIC_ERROR_VENDOR_BASE = 1000;
+
+    //
+    // Image acquisition messages.
+    //
+
+    /**
+     * The image acquired was good.
+     */
+    int BIOMETRIC_ACQUIRED_GOOD = 0;
+
+    /**
+     * Only a partial biometric image was detected. During enrollment, the user should be informed
+     * on what needs to happen to resolve this problem, e.g. "press firmly on sensor." (for
+     * fingerprint)
+     */
+    int BIOMETRIC_ACQUIRED_PARTIAL = 1;
+
+    /**
+     * The biometric image was too noisy to process due to a detected condition or a possibly dirty
+     * sensor (See {@link #BIOMETRIC_ACQUIRED_IMAGER_DIRTY}).
+     */
+    int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2;
+
+    /**
+     * The biometric image was too noisy due to suspected or detected dirt on the sensor.  For
+     * example, it's reasonable return this after multiple {@link #BIOMETRIC_ACQUIRED_INSUFFICIENT}
+     * or actual detection of dirt on the sensor (stuck pixels, swaths, etc.). The user is expected
+     * to take action to clean the sensor when this is returned.
+     */
+    int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3;
+
+    /**
+     * The biometric image was unreadable due to lack of motion.
+     */
+    int BIOMETRIC_ACQUIRED_TOO_SLOW = 4;
+
+    /**
+     * The biometric image was incomplete due to quick motion. For example, this could also happen
+     * if the user moved during acquisition. The user should be asked to repeat the operation more
+     * slowly.
+     */
+    int BIOMETRIC_ACQUIRED_TOO_FAST = 5;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * @hide
+     */
+    int BIOMETRIC_ACQUIRED_VENDOR = 6;
+    /**
+     * @hide
+     */
+    int BIOMETRICT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/android/hardware/biometrics/BiometricDialog.java b/android/hardware/biometrics/BiometricDialog.java
new file mode 100644
index 0000000..dd848a3
--- /dev/null
+++ b/android/hardware/biometrics/BiometricDialog.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import static android.Manifest.permission.USE_BIOMETRIC;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.text.TextUtils;
+
+import java.security.Signature;
+import java.util.concurrent.Executor;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A class that manages a system-provided biometric dialog.
+ */
+public class BiometricDialog implements BiometricAuthenticator, BiometricConstants {
+
+    /**
+     * @hide
+     */
+    public static final String KEY_TITLE = "title";
+    /**
+     * @hide
+     */
+    public static final String KEY_SUBTITLE = "subtitle";
+    /**
+     * @hide
+     */
+    public static final String KEY_DESCRIPTION = "description";
+    /**
+     * @hide
+     */
+    public static final String KEY_POSITIVE_TEXT = "positive_text";
+    /**
+     * @hide
+     */
+    public static final String KEY_NEGATIVE_TEXT = "negative_text";
+
+    /**
+     * Error/help message will show for this amount of time.
+     * For error messages, the dialog will also be dismissed after this amount of time.
+     * Error messages will be propagated back to the application via AuthenticationCallback
+     * after this amount of time.
+     * @hide
+     */
+    public static final int HIDE_DIALOG_DELAY = 2000; // ms
+    /**
+     * @hide
+     */
+    public static final int DISMISSED_REASON_POSITIVE = 1;
+
+    /**
+     * @hide
+     */
+    public static final int DISMISSED_REASON_NEGATIVE = 2;
+
+    /**
+     * @hide
+     */
+    public static final int DISMISSED_REASON_USER_CANCEL = 3;
+
+    private static class ButtonInfo {
+        Executor executor;
+        DialogInterface.OnClickListener listener;
+        ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
+            executor = ex;
+            listener = l;
+        }
+    }
+
+    /**
+     * A builder that collects arguments to be shown on the system-provided biometric dialog.
+     **/
+    public static class Builder {
+        private final Bundle mBundle;
+        private ButtonInfo mPositiveButtonInfo;
+        private ButtonInfo mNegativeButtonInfo;
+        private Context mContext;
+
+        /**
+         * Creates a builder for a biometric dialog.
+         * @param context
+         */
+        public Builder(Context context) {
+            mBundle = new Bundle();
+            mContext = context;
+        }
+
+        /**
+         * Required: Set the title to display.
+         * @param title
+         * @return
+         */
+        public Builder setTitle(@NonNull CharSequence title) {
+            mBundle.putCharSequence(KEY_TITLE, title);
+            return this;
+        }
+
+        /**
+         * Optional: Set the subtitle to display.
+         * @param subtitle
+         * @return
+         */
+        public Builder setSubtitle(@NonNull CharSequence subtitle) {
+            mBundle.putCharSequence(KEY_SUBTITLE, subtitle);
+            return this;
+        }
+
+        /**
+         * Optional: Set the description to display.
+         * @param description
+         * @return
+         */
+        public Builder setDescription(@NonNull CharSequence description) {
+            mBundle.putCharSequence(KEY_DESCRIPTION, description);
+            return this;
+        }
+
+        /**
+         * Optional: Set the text for the positive button. If not set, the positive button
+         * will not show.
+         * @param text
+         * @return
+         * @hide
+         */
+        public Builder setPositiveButton(@NonNull CharSequence text,
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull DialogInterface.OnClickListener listener) {
+            if (TextUtils.isEmpty(text)) {
+                throw new IllegalArgumentException("Text must be set and non-empty");
+            }
+            if (executor == null) {
+                throw new IllegalArgumentException("Executor must not be null");
+            }
+            if (listener == null) {
+                throw new IllegalArgumentException("Listener must not be null");
+            }
+            mBundle.putCharSequence(KEY_POSITIVE_TEXT, text);
+            mPositiveButtonInfo = new ButtonInfo(executor, listener);
+            return this;
+        }
+
+        /**
+         * Required: Set the text for the negative button. This would typically be used as a
+         * "Cancel" button, but may be also used to show an alternative method for authentication,
+         * such as screen that asks for a backup password.
+         * @param text
+         * @return
+         */
+        public Builder setNegativeButton(@NonNull CharSequence text,
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull DialogInterface.OnClickListener listener) {
+            if (TextUtils.isEmpty(text)) {
+                throw new IllegalArgumentException("Text must be set and non-empty");
+            }
+            if (executor == null) {
+                throw new IllegalArgumentException("Executor must not be null");
+            }
+            if (listener == null) {
+                throw new IllegalArgumentException("Listener must not be null");
+            }
+            mBundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
+            mNegativeButtonInfo = new ButtonInfo(executor, listener);
+            return this;
+        }
+
+        /**
+         * Creates a {@link BiometricDialog}.
+         * @return a {@link BiometricDialog}
+         * @throws IllegalArgumentException if any of the required fields are not set.
+         */
+        public BiometricDialog build() {
+            final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
+            final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
+
+            if (TextUtils.isEmpty(title)) {
+                throw new IllegalArgumentException("Title must be set and non-empty");
+            } else if (TextUtils.isEmpty(negative)) {
+                throw new IllegalArgumentException("Negative text must be set and non-empty");
+            }
+            return new BiometricDialog(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
+        }
+    }
+
+    private PackageManager mPackageManager;
+    private FingerprintManager mFingerprintManager;
+    private Bundle mBundle;
+    private ButtonInfo mPositiveButtonInfo;
+    private ButtonInfo mNegativeButtonInfo;
+
+    IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
+        @Override
+        public void onDialogDismissed(int reason) {
+            // Check the reason and invoke OnClickListener(s) if necessary
+            if (reason == DISMISSED_REASON_POSITIVE) {
+                mPositiveButtonInfo.executor.execute(() -> {
+                    mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+                });
+            } else if (reason == DISMISSED_REASON_NEGATIVE) {
+                mNegativeButtonInfo.executor.execute(() -> {
+                    mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+                });
+            }
+        }
+    };
+
+    private BiometricDialog(Context context, Bundle bundle,
+            ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
+        mBundle = bundle;
+        mPositiveButtonInfo = positiveButtonInfo;
+        mNegativeButtonInfo = negativeButtonInfo;
+        mFingerprintManager = context.getSystemService(FingerprintManager.class);
+        mPackageManager = context.getPackageManager();
+    }
+
+    /**
+     * A wrapper class for the crypto objects supported by BiometricDialog. Currently the framework
+     * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+     */
+    public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+        public CryptoObject(@NonNull Signature signature) {
+            super(signature);
+        }
+
+        public CryptoObject(@NonNull Cipher cipher) {
+            super(cipher);
+        }
+
+        public CryptoObject(@NonNull Mac mac) {
+            super(mac);
+        }
+
+        /**
+         * Get {@link Signature} object.
+         * @return {@link Signature} object or null if this doesn't contain one.
+         */
+        public Signature getSignature() {
+            return super.getSignature();
+        }
+
+        /**
+         * Get {@link Cipher} object.
+         * @return {@link Cipher} object or null if this doesn't contain one.
+         */
+        public Cipher getCipher() {
+            return super.getCipher();
+        }
+
+        /**
+         * Get {@link Mac} object.
+         * @return {@link Mac} object or null if this doesn't contain one.
+         */
+        public Mac getMac() {
+            return super.getMac();
+        }
+    }
+
+    /**
+     * Container for callback data from {@link #authenticate( CancellationSignal, Executor,
+     * AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor,
+     * AuthenticationCallback)}
+     */
+    public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
+        /**
+         * Authentication result
+         * @param crypto
+         * @param identifier
+         * @param userId
+         * @hide
+         */
+        public AuthenticationResult(CryptoObject crypto, BiometricIdentifier identifier,
+                int userId) {
+            super(crypto, identifier, userId);
+        }
+        /**
+         * Obtain the crypto object associated with this transaction
+         * @return crypto object provided to {@link #authenticate( CryptoObject, CancellationSignal,
+         * Executor, AuthenticationCallback)}
+         */
+        public CryptoObject getCryptoObject() {
+            return (CryptoObject) super.getCryptoObject();
+        }
+    }
+
+    /**
+     * Callback structure provided to {@link BiometricDialog#authenticate(CancellationSignal,
+     * Executor, AuthenticationCallback)} or {@link BiometricDialog#authenticate(CryptoObject,
+     * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
+     * of this for listening to authentication events.
+     */
+    public abstract static class AuthenticationCallback extends
+            BiometricAuthenticator.AuthenticationCallback {
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further actions will be made on this object.
+         * @param errorCode An integer identifying the error message
+         * @param errString A human-readable error string that can be shown on an UI
+         */
+        @Override
+        public void onAuthenticationError(int errorCode, CharSequence errString) {}
+
+        /**
+         * Called when a recoverable error has been encountered during authentication. The help
+         * string is provided to give the user guidance for what went wrong, such as "Sensor dirty,
+         * please clean it."
+         * @param helpCode An integer identifying the error message
+         * @param helpString A human-readable string that can be shown on an UI
+         */
+        @Override
+        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
+
+        /**
+         * Called when a biometric is recognized.
+         * @param result An object containing authentication-related data
+         */
+        public void onAuthenticationSucceeded(AuthenticationResult result) {}
+
+        /**
+         * Called when a biometric is valid but not recognized.
+         */
+        @Override
+        public void onAuthenticationFailed() {}
+
+        /**
+         * Called when a biometric has been acquired, but hasn't been processed yet.
+         * @hide
+         */
+        @Override
+        public void onAuthenticationAcquired(int acquireInfo) {}
+
+        /**
+         * @param result An object containing authentication-related data
+         * @hide
+         */
+        @Override
+        public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+            onAuthenticationSucceeded(new AuthenticationResult(
+                    (CryptoObject) result.getCryptoObject(),
+                    result.getId(),
+                    result.getUserId()));
+        }
+    }
+
+    /**
+     * @param crypto Object associated with the call
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     * @hide
+     */
+    @Override
+    public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+            throw new IllegalArgumentException("Callback cannot be casted");
+        }
+        authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
+    }
+
+    /**
+     *
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     * @hide
+     */
+    @Override
+    public void authenticate(@NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
+        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+            throw new IllegalArgumentException("Callback cannot be casted");
+        }
+        authenticate(cancel, executor, (AuthenticationCallback) callback);
+    }
+
+    /**
+     * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
+     * scanning for a fingerprint. It terminates when {@link
+     * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
+     * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user
+     * dismisses the system-provided dialog, at which point the crypto object becomes invalid. This
+     * operation can be canceled by using the provided cancel object. The application will receive
+     * authentication errors through {@link AuthenticationCallback}, and button events through the
+     * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor,
+     * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricDialog} object,
+     * and calling {@link BiometricDialog#authenticate( CancellationSignal, Executor,
+     * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the
+     * previous client and start a new authentication. The interrupted client will receive a
+     * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
+     * CharSequence)}.
+     *
+     * @throws IllegalArgumentException If any of the arguments are null
+     *
+     * @param crypto Object associated with the call
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public void authenticate(@NonNull CryptoObject crypto,
+            @NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback) {
+        if (handlePreAuthenticationErrors(callback, executor)) {
+            return;
+        }
+        mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
+                callback);
+    }
+
+    /**
+     * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
+     * scanning for a fingerprint. It terminates when {@link
+     * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
+     * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when
+     * the user dismisses the system-provided dialog.  This operation can be canceled by using the
+     * provided cancel object. The application will receive authentication errors through {@link
+     * AuthenticationCallback}, and button events through the corresponding callback set in {@link
+     * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.  It is
+     * safe to reuse the {@link BiometricDialog} object, and calling {@link
+     * BiometricDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
+     * an existing authentication attempt is occurring will stop the previous client and start a new
+     * authentication. The interrupted client will receive a cancelled notification through {@link
+     * AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
+     *
+     * @throws IllegalArgumentException If any of the arguments are null
+     *
+     * @param cancel An object that can be used to cancel authentication
+     * @param executor An executor to handle callback events
+     * @param callback An object to receive authentication events
+     */
+    @RequiresPermission(USE_BIOMETRIC)
+    public void authenticate(@NonNull CancellationSignal cancel,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull AuthenticationCallback callback) {
+        if (handlePreAuthenticationErrors(callback, executor)) {
+            return;
+        }
+        mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
+    }
+
+    private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
+            Executor executor) {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
+                      executor);
+            return true;
+        } else if (!mFingerprintManager.isHardwareDetected()) {
+            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
+                      executor);
+            return true;
+        } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+            sendError(BiometricDialog.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
+                      executor);
+            return true;
+        }
+        return false;
+    }
+
+    private void sendError(int error, AuthenticationCallback callback, Executor executor) {
+        executor.execute(() -> {
+            callback.onAuthenticationError(error, mFingerprintManager.getErrorString(
+                    error, 0 /* vendorCode */));
+        });
+    }
+}
diff --git a/android/hardware/biometrics/BiometricFingerprintConstants.java b/android/hardware/biometrics/BiometricFingerprintConstants.java
new file mode 100644
index 0000000..638f525
--- /dev/null
+++ b/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.hardware.fingerprint.FingerprintManager;
+
+/**
+ * Interface containing all of the fingerprint-specific constants.
+ * @hide
+ */
+public interface BiometricFingerprintConstants {
+    //
+    // Error messages from fingerprint hardware during initilization, enrollment, authentication or
+    // removal. Must agree with the list in fingerprint.h
+    //
+
+    /**
+     * The hardware is unavailable. Try again later.
+     */
+    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+
+    /**
+     * Error state returned when the sensor was unable to process the current image.
+     */
+    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+
+    /**
+     * Error state returned when the current request has been running too long. This is intended to
+     * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
+     * platform and sensor-specific, but is generally on the order of 30 seconds.
+     */
+    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+
+    /**
+     * Error state returned for operations like enrollment; the operation cannot be completed
+     * because there's not enough storage remaining to complete the operation.
+     */
+    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+    /**
+     * The operation was canceled because the fingerprint sensor is unavailable. For example,
+     * this may happen when the user is switched, the device is locked or another pending operation
+     * prevents or disables it.
+     */
+    public static final int FINGERPRINT_ERROR_CANCELED = 5;
+
+    /**
+     * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
+     * provided fingerprint id was incorrect.
+     *
+     * @hide
+     */
+    public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+
+    /**
+     * The operation was canceled because the API is locked out due to too many attempts.
+     * This occurs after 5 failed attempts, and lasts for 30 seconds.
+     */
+    public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * These messages are typically reserved for internal operations such as enrollment, but may be
+     * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
+     * expected to show the error message string if they happen, but are advised not to rely on the
+     * message id since they will be device and vendor-specific
+     */
+    public static final int FINGERPRINT_ERROR_VENDOR = 8;
+
+    /**
+     * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
+     * Fingerprint authentication is disabled until the user unlocks with strong authentication
+     * (PIN/Pattern/Password)
+     */
+    public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+
+    /**
+     * The user canceled the operation. Upon receiving this, applications should use alternate
+     * authentication (e.g. a password). The application should also provide the means to return
+     * to fingerprint authentication, such as a "use fingerprint" button.
+     */
+    public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+
+    /**
+     * The user does not have any fingerprints enrolled.
+     */
+    public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+
+    /**
+     * The device does not have a fingerprint sensor.
+     */
+    public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+
+    /**
+     * @hide
+     */
+    public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+
+    //
+    // Image acquisition messages. Must agree with those in fingerprint.h
+    //
+
+    /**
+     * The image acquired was good.
+     */
+    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+
+    /**
+     * Only a partial fingerprint image was detected. During enrollment, the user should be
+     * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
+     */
+    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+
+    /**
+     * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
+     * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
+     */
+    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+
+    /**
+     * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
+     * For example, it's reasonable return this after multiple
+     * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
+     * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
+     * when this is returned.
+     */
+    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+
+    /**
+     * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
+     * linear array sensors that require a swipe motion.
+     */
+    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+
+    /**
+     * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
+     * linear array sensors,  this could also happen if the finger was moved during acquisition.
+     * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
+     * longer.
+     */
+    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * @hide
+     */
+    public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+    /**
+     * @hide
+     */
+    public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/android/hardware/biometrics/CryptoObject.java b/android/hardware/biometrics/CryptoObject.java
new file mode 100644
index 0000000..496d9c5
--- /dev/null
+++ b/android/hardware/biometrics/CryptoObject.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.security.keystore.AndroidKeyStoreProvider;
+
+import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * @hide
+ */
+public class CryptoObject {
+    private final Object mCrypto;
+
+    public CryptoObject(@NonNull Signature signature) {
+        mCrypto = signature;
+    }
+
+    public CryptoObject(@NonNull Cipher cipher) {
+        mCrypto = cipher;
+    }
+
+    public CryptoObject(@NonNull Mac mac) {
+        mCrypto = mac;
+    }
+
+    /**
+     * Get {@link Signature} object.
+     * @return {@link Signature} object or null if this doesn't contain one.
+     */
+    public Signature getSignature() {
+        return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+    }
+
+    /**
+     * Get {@link Cipher} object.
+     * @return {@link Cipher} object or null if this doesn't contain one.
+     */
+    public Cipher getCipher() {
+        return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+    }
+
+    /**
+     * Get {@link Mac} object.
+     * @return {@link Mac} object or null if this doesn't contain one.
+     */
+    public Mac getMac() {
+        return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+    }
+
+    /**
+     * @hide
+     * @return the opId associated with this object or 0 if none
+     */
+    public final long getOpId() {
+        return mCrypto != null
+                ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+    }
+};
diff --git a/android/hardware/camera2/CameraCaptureSession.java b/android/hardware/camera2/CameraCaptureSession.java
index ff69bd8..eafe593 100644
--- a/android/hardware/camera2/CameraCaptureSession.java
+++ b/android/hardware/camera2/CameraCaptureSession.java
@@ -16,15 +16,16 @@
 
 package android.hardware.camera2;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.os.Handler;
 import android.view.Surface;
 
+import java.util.concurrent.Executor;
 import java.util.List;
 
-
 /**
  * A configured capture session for a {@link CameraDevice}, used for capturing images from the
  * camera or reprocessing images captured from the camera in the same session previously.
@@ -354,6 +355,50 @@
             throws CameraAccessException;
 
     /**
+     * <p>Submit a request for an image to be captured by the camera device.</p>
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #capture(CaptureRequest, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param request the settings for this capture
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify once this request has been
+     * processed.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
+     *                                  configured as outputs for this session; or the request
+     *                                  targets a set of Surfaces that cannot be submitted
+     *                                  simultaneously in a reprocessable capture session; or a
+     *                                  reprocess capture request is submitted in a
+     *                                  non-reprocessable capture session; or the reprocess capture
+     *                                  request was created with a {@link TotalCaptureResult} from
+     *                                  a different session; or the capture targets a Surface in
+     *                                  the middle of being {@link #prepare prepared}; or the
+     *                                  executor is null, or the listener is not null.
+     *
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see #abortCaptures
+     * @see CameraDevice#createReprocessableCaptureSession
+     */
+    public int captureSingleRequest(@NonNull CaptureRequest request,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * Submit a list of requests to be captured in sequence as a burst. The
      * burst will be captured in the minimum amount of time possible, and will
      * not be interleaved with requests submitted by other capture or repeat
@@ -416,6 +461,53 @@
             throws CameraAccessException;
 
     /**
+     * Submit a list of requests to be captured in sequence as a burst. The
+     * burst will be captured in the minimum amount of time possible, and will
+     * not be interleaved with requests submitted by other capture or repeat
+     * calls.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #captureBurst(List, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param requests the list of settings for this burst capture
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify each time one of the
+     * requests in the burst has been processed.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target
+     *                                  Surfaces not currently configured as outputs; or one of the
+     *                                  requests targets a set of Surfaces that cannot be submitted
+     *                                  simultaneously in a reprocessable capture session; or a
+     *                                  reprocess capture request is submitted in a
+     *                                  non-reprocessable capture session; or one of the reprocess
+     *                                  capture requests was created with a
+     *                                  {@link TotalCaptureResult} from a different session; or one
+     *                                  of the captures targets a Surface in the middle of being
+     *                                  {@link #prepare prepared}; or if the executor is null; or if
+     *                                  the listener is null.
+     *
+     * @see #capture
+     * @see #setRepeatingRequest
+     * @see #setRepeatingBurst
+     * @see #abortCaptures
+     */
+    public int captureBurstRequests(@NonNull List<CaptureRequest> requests,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * Request endlessly repeating capture of images by this capture session.
      *
      * <p>With this method, the camera device will continually capture images
@@ -483,6 +575,45 @@
             throws CameraAccessException;
 
     /**
+     * Request endlessly repeating capture of images by this capture session.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #setRepeatingRequest(CaptureRequest, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param request the request to repeat indefinitely
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify every time the
+     * request finishes processing.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces
+     *                                  that are not currently configured as outputs; or the request
+     *                                  is a reprocess capture request; or the capture targets a
+     *                                  Surface in the middle of being {@link #prepare prepared}; or
+     *                                  the executor is null; or the listener is null.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingBurst
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public int setSingleRepeatingRequest(@NonNull CaptureRequest request,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * <p>Request endlessly repeating capture of a sequence of images by this
      * capture session.</p>
      *
@@ -555,6 +686,47 @@
             throws CameraAccessException;
 
     /**
+     * <p>Request endlessly repeating capture of a sequence of images by this
+     * capture session.</p>
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #setRepeatingBurst(List, CaptureCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param requests the list of requests to cycle through indefinitely
+     * @param executor the executor which will be used for invoking the listener.
+     * @param listener The callback object to notify each time one of the
+     * requests in the repeating bursts has finished processing.
+     *
+     * @return int A unique capture sequence ID used by
+     *             {@link CaptureCallback#onCaptureSequenceCompleted}.
+     *
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if this session is no longer active, either because the session
+     *                               was explicitly closed, a new session has been created
+     *                               or the camera device has been closed.
+     * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces
+     *                                  not currently configured as outputs; or one of the requests
+     *                                  is a reprocess capture request; or one of the captures
+     *                                  targets a Surface in the middle of being
+     *                                  {@link #prepare prepared}; or the executor is null; or the
+     *                                  listener is null.
+     *
+     * @see #capture
+     * @see #captureBurst
+     * @see #setRepeatingRequest
+     * @see #stopRepeating
+     * @see #abortCaptures
+     */
+    public int setRepeatingBurstRequests(@NonNull List<CaptureRequest> requests,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener)
+            throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * <p>Cancel any ongoing repeating capture set by either
      * {@link #setRepeatingRequest setRepeatingRequest} or
      * {@link #setRepeatingBurst}. Has no effect on requests submitted through
diff --git a/android/hardware/camera2/CameraCharacteristics.java b/android/hardware/camera2/CameraCharacteristics.java
index 96d043c..4279b19 100644
--- a/android/hardware/camera2/CameraCharacteristics.java
+++ b/android/hardware/camera2/CameraCharacteristics.java
@@ -28,7 +28,9 @@
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * <p>The properties describing a
@@ -341,7 +343,7 @@
      */
     @SuppressWarnings({"unchecked"})
     public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
-        if (mAvailableSessionKeys == null) {
+        if (mAvailablePhysicalRequestKeys == null) {
             Object crKey = CaptureRequest.Key.class;
             Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
 
@@ -450,23 +452,20 @@
     }
 
     /**
-     * Returns the list of physical camera ids that this logical {@link CameraDevice} is
+     * Returns the set of physical camera ids that this logical {@link CameraDevice} is
      * made up of.
      *
      * <p>A camera device is a logical camera if it has
      * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device
-     * doesn't have the capability, the return value will be an empty list. </p>
+     * doesn't have the capability, the return value will be an empty set. </p>
      *
-     * <p>The list returned is not modifiable, so any attempts to modify it will throw
+     * <p>The set returned is not modifiable, so any attempts to modify it will throw
      * a {@code UnsupportedOperationException}.</p>
      *
-     * <p>Each physical camera id is only listed once in the list. The order of the keys
-     * is undefined.</p>
-     *
-     * @return List of physical camera ids for this logical camera device.
+     * @return Set of physical camera ids for this logical camera device.
      */
     @NonNull
-    public List<String> getPhysicalCameraIds() {
+    public Set<String> getPhysicalCameraIds() {
         int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
         if (availableCapabilities == null) {
             throw new AssertionError("android.request.availableCapabilities must be non-null "
@@ -475,7 +474,7 @@
 
         if (!ArrayUtils.contains(availableCapabilities,
                 REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
-            return Collections.emptyList();
+            return Collections.emptySet();
         }
         byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
 
@@ -485,9 +484,9 @@
         } catch (java.io.UnsupportedEncodingException e) {
             throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
         }
-        String[] physicalCameraIdList = physicalCamIdString.split("\0");
+        String[] physicalCameraIdArray = physicalCamIdString.split("\0");
 
-        return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList));
+        return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(physicalCameraIdArray)));
     }
 
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
@@ -1243,7 +1242,7 @@
      * from the main sensor along the +X axis (to the right from the user's perspective) will
      * report <code>(0.03, 0, 0)</code>.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
-     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
      * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
      * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -1257,10 +1256,10 @@
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
     @PublicKey
     public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -1306,7 +1305,7 @@
      * where <code>(0,0)</code> is the top-left of the
      * preCorrectionActiveArraySize rectangle. Once the pose and
      * intrinsic calibration transforms have been applied to a
-     * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+     * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
      * transform needs to be applied, and the result adjusted to
      * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system (where <code>(0, 0)</code> is the top-left of the
@@ -1319,9 +1318,9 @@
      * coordinate system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
@@ -1363,7 +1362,14 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @deprecated
+     * <p>This field was inconsistently defined in terms of its
+     * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+
      */
+    @Deprecated
     @PublicKey
     public static final Key<float[]> LENS_RADIAL_DISTORTION =
             new Key<float[]>("android.lens.radialDistortion", float[].class);
@@ -1372,9 +1378,6 @@
      * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
      * <p>Different calibration methods and use cases can produce better or worse results
      * depending on the selected coordinate origin.</p>
-     * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin
-     * makes device calibration and later usage by applications combining camera and gyroscope
-     * information together simpler.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
@@ -1391,6 +1394,46 @@
             new Key<Integer>("android.lens.poseReference", int.class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion.</p>
+     * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+     * inconsistently defined.</p>
+     * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+     * kappa_3]</code> and two tangential distortion coefficients
+     * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+     * lens's geometric distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+     *  y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+     * </code></pre>
+     * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+     * input image that correspond to the pixel values in the
+     * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+     * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+     * </code></pre>
+     * <p>The pixel coordinates are defined in a coordinate system
+     * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+     * calibration fields; see that entry for details of the mapping stages.
+     * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+     * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+     * the range of the coordinates depends on the focal length
+     * terms of the intrinsic calibration.</p>
+     * <p>Finally, <code>r</code> represents the radial distance from the
+     * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p>The distortion model used is the Brown-Conrady model.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_DISTORTION =
+            new Key<float[]>("android.lens.distortion", float[].class);
+
+    /**
      * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
      * by this camera device.</p>
      * <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -1422,6 +1465,8 @@
      * consideration of future support.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer; replaced by better partials mechanism</p>
+
      * @hide
      */
     @Deprecated
@@ -1663,6 +1708,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1679,6 +1725,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1793,11 +1840,7 @@
      * The respective value of such request key can be obtained by calling
      * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
      * individual physical device requests must be built via
-     * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
-     * Such extended capture requests can be passed only to
-     * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
-     * not to {@link CameraCaptureSession#setRepeatingRequest } or
-     * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+     * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Limited capability</b> -
      * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
@@ -1816,6 +1859,8 @@
      * <p>When set to YUV_420_888, application can access the YUV420 data directly.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1836,6 +1881,8 @@
      * TODO: Remove property.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1852,6 +1899,8 @@
      *
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1891,6 +1940,8 @@
      * <p><b>Units</b>: Nanoseconds</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -1913,6 +1964,8 @@
      * check if it limits the maximum size for image data.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -2552,7 +2605,7 @@
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</p>
      * <p>The currently supported fields that correct for geometric distortion are:</p>
      * <ol>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}.</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}.</li>
      * </ol>
      * <p>If all of the geometric distortion fields are no-ops, this rectangle will be the same
      * as the post-distortion-corrected rectangle given in
@@ -2565,7 +2618,7 @@
      * <p><b>Units</b>: Pixel coordinates on the image sensor</p>
      * <p>This key is available on all devices.</p>
      *
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
@@ -3341,6 +3394,21 @@
     public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
             new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
 
+    /**
+     * <p>List of distortion correction modes for {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} that are
+     * supported by this camera device.</p>
+     * <p>No device is required to support this API; such devices will always list only 'OFF'.
+     * All devices that support this API will list both FAST and HIGH_QUALITY.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES =
+            new Key<int[]>("android.distortionCorrection.availableModes", int[].class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/android/hardware/camera2/CameraDevice.java b/android/hardware/camera2/CameraDevice.java
index 40ee834..f47d464 100644
--- a/android/hardware/camera2/CameraDevice.java
+++ b/android/hardware/camera2/CameraDevice.java
@@ -145,37 +145,6 @@
      */
     public static final int TEMPLATE_MANUAL = 6;
 
-    /**
-     * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
-     * possible while improving the camera output for motion tracking use cases.
-     *
-     * <p>This template is best used by applications that are frequently switching between motion
-     * tracking use cases and regular still capture use cases, to minimize the IQ changes
-     * when swapping use cases.</p>
-     *
-     * <p>This template is guaranteed to be supported on camera devices that support the
-     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
-     * capability.</p>
-     *
-     * @see #createCaptureRequest
-     */
-    public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7;
-
-    /**
-     * A template for selecting camera parameters that maximize the quality of camera output for
-     * motion tracking use cases.
-     *
-     * <p>This template is best used by applications dedicated to motion tracking applications,
-     * which aren't concerned about fast switches between motion tracking and other use cases.</p>
-     *
-     * <p>This template is guaranteed to be supported on camera devices that support the
-     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
-     * capability.</p>
-     *
-     * @see #createCaptureRequest
-     */
-    public static final int TEMPLATE_MOTION_TRACKING_BEST = 8;
-
      /** @hide */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(prefix = {"TEMPLATE_"}, value =
@@ -184,9 +153,7 @@
           TEMPLATE_RECORD,
           TEMPLATE_VIDEO_SNAPSHOT,
           TEMPLATE_ZERO_SHUTTER_LAG,
-          TEMPLATE_MANUAL,
-          TEMPLATE_MOTION_TRACKING_PREVIEW,
-          TEMPLATE_MOTION_TRACKING_BEST})
+          TEMPLATE_MANUAL})
      public @interface RequestTemplate {};
 
     /**
@@ -420,27 +387,6 @@
      * </table><br>
      * </p>
      *
-     * <p>MOTION_TRACKING-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
-     * includes
-     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING})
-     * devices support at least the below stream combinations in addition to those for
-     * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. The
-     * {@code FULL FOV 640} entry means that the device will support a resolution that's 640 pixels
-     * wide, with the height set so that the resolution aspect ratio matches the MAXIMUM output
-     * aspect ratio, rounded down.  So for a device with a 4:3 image sensor, this will be 640x480,
-     * and for a device with a 16:9 sensor, this will be 640x360, and so on. And the
-     * {@code MAX 30FPS} entry means the largest JPEG resolution on the device for which
-     * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration}
-     * returns a value less than or equal to 1/30s.
-     *
-     * <table>
-     * <tr><th colspan="7">MOTION_TRACKING-capability additional guaranteed configurations</th></tr>
-     * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th rowspan="2">Sample use case(s)</th> </tr>
-     * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
-     * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code JPEG}</td><td id="rb">{@code MAX 30FPS}</td> <td>Preview with a tracking YUV output and a as-large-as-possible JPEG for still captures.</td> </tr>
-     * </table><br>
-     * </p>
-     *
      * <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
      * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices
      * support at least the below stream combinations in addition to those for
@@ -873,7 +819,8 @@
      * @param config A session configuration (see {@link SessionConfiguration}).
      *
      * @throws IllegalArgumentException In case the session configuration is invalid; or the output
-     *                                  configurations are empty.
+     *                                  configurations are empty; or the session configuration
+     *                                  executor is invalid.
      * @throws CameraAccessException In case the camera device is no longer connected or has
      *                               encountered a fatal error.
      * @see #createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)
@@ -917,11 +864,16 @@
      * request for a specific physical camera. The settings are chosen
      * to be the best options for the specific logical camera device. If
      * additional physical camera ids are passed, then they will also use the
-     * same settings template. Requests containing individual physical camera
-     * settings can be passed only to {@link CameraCaptureSession#capture} or
-     * {@link CameraCaptureSession#captureBurst} and not to
-     * {@link CameraCaptureSession#setRepeatingRequest} or
-     * {@link CameraCaptureSession#setRepeatingBurst}</p>
+     * same settings template. Clients can further modify individual camera
+     * settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p>
+     *
+     * <p>Individual physical camera settings will only be honored for camera session
+     * that was initialiazed with corresponding physical camera id output configuration
+     * {@link OutputConfiguration#setPhysicalCameraId} and the same output targets are
+     * also attached in the request by {@link CaptureRequest.Builder#addTarget}.</p>
+     *
+     * <p>The output is undefined for any logical camera streams in case valid physical camera
+     * settings are attached.</p>
      *
      * @param templateType An enumeration selecting the use case for this request. Not all template
      * types are supported on every device. See the documentation for each template type for
@@ -942,8 +894,8 @@
      * @see #TEMPLATE_STILL_CAPTURE
      * @see #TEMPLATE_VIDEO_SNAPSHOT
      * @see #TEMPLATE_MANUAL
-     * @see CaptureRequest.Builder#setKey
-     * @see CaptureRequest.Builder#getKey
+     * @see CaptureRequest.Builder#setPhysicalCameraKey
+     * @see CaptureRequest.Builder#getPhysicalCameraKey
      */
     @NonNull
     public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
diff --git a/android/hardware/camera2/CameraManager.java b/android/hardware/camera2/CameraManager.java
index a2bc91e..4124536 100644
--- a/android/hardware/camera2/CameraManager.java
+++ b/android/hardware/camera2/CameraManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware.camera2;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -25,6 +26,7 @@
 import android.hardware.CameraStatus;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
+import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -41,6 +43,11 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 
 /**
  * <p>A system service manager for detecting, characterizing, and connecting to
@@ -123,16 +130,29 @@
      */
     public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
             @Nullable Handler handler) {
-        if (handler == null) {
-            Looper looper = Looper.myLooper();
-            if (looper == null) {
-                throw new IllegalArgumentException(
-                        "No handler given, and current thread has no looper!");
-            }
-            handler = new Handler(looper);
-        }
+        CameraManagerGlobal.get().registerAvailabilityCallback(callback,
+                CameraDeviceImpl.checkAndWrapHandler(handler));
+    }
 
-        CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
+    /**
+     * Register a callback to be notified about camera device availability.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param executor The executor which will be used to invoke the callback.
+     * @param callback the new callback to send camera availability notices to
+     *
+     * @throws IllegalArgumentException if the executor is {@code null}.
+     */
+    public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull AvailabilityCallback callback) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor was null");
+        }
+        CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor);
     }
 
     /**
@@ -170,15 +190,29 @@
      *             no looper.
      */
     public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) {
-        if (handler == null) {
-            Looper looper = Looper.myLooper();
-            if (looper == null) {
-                throw new IllegalArgumentException(
-                        "No handler given, and current thread has no looper!");
-            }
-            handler = new Handler(looper);
+        CameraManagerGlobal.get().registerTorchCallback(callback,
+                CameraDeviceImpl.checkAndWrapHandler(handler));
+    }
+
+    /**
+     * Register a callback to be notified about torch mode status.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #registerTorchCallback(TorchCallback, Handler)},
+     * except that it uses {@link java.util.concurrent.Executor} as an argument
+     * instead of {@link android.os.Handler}.</p>
+     *
+     * @param executor The executor which will be used to invoke the callback
+     * @param callback The new callback to send torch mode status to
+     *
+     * @throws IllegalArgumentException if the executor is {@code null}.
+     */
+    public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull TorchCallback callback) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor was null");
         }
-        CameraManagerGlobal.get().registerTorchCallback(callback, handler);
+        CameraManagerGlobal.get().registerTorchCallback(callback, executor);
     }
 
     /**
@@ -257,7 +291,7 @@
      *
      * @param cameraId The unique identifier of the camera device to open
      * @param callback The callback for the camera. Must not be null.
-     * @param handler  The handler to invoke the callback on. Must not be null.
+     * @param executor The executor to invoke the callback with. Must not be null.
      * @param uid      The UID of the application actually opening the camera.
      *                 Must be USE_CALLING_UID unless the caller is a service
      *                 that is trusted to open the device on behalf of an
@@ -276,7 +310,7 @@
      * @see android.app.admin.DevicePolicyManager#setCameraDisabled
      */
     private CameraDevice openCameraDeviceUserAsync(String cameraId,
-            CameraDevice.StateCallback callback, Handler handler, final int uid)
+            CameraDevice.StateCallback callback, Executor executor, final int uid)
             throws CameraAccessException {
         CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
         CameraDevice device = null;
@@ -289,7 +323,7 @@
                     new android.hardware.camera2.impl.CameraDeviceImpl(
                         cameraId,
                         callback,
-                        handler,
+                        executor,
                         characteristics,
                         mContext.getApplicationInfo().targetSdkVersion);
 
@@ -430,7 +464,47 @@
             @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
             throws CameraAccessException {
 
-        openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
+        openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
+                USE_CALLING_UID);
+    }
+
+    /**
+     * Open a connection to a camera with the given ID.
+     *
+     * <p>The behavior of this method matches that of
+     * {@link #openCamera(String, StateCallback, Handler)}, except that it uses
+     * {@link java.util.concurrent.Executor} as an argument instead of
+     * {@link android.os.Handler}.</p>
+     *
+     * @param cameraId
+     *             The unique identifier of the camera device to open
+     * @param executor
+     *             The executor which will be used when invoking the callback.
+     * @param callback
+     *             The callback which is invoked once the camera is opened
+     *
+     * @throws CameraAccessException if the camera is disabled by device policy,
+     * has been disconnected, or is being used by a higher-priority camera API client.
+     *
+     * @throws IllegalArgumentException if cameraId, the callback or the executor was null,
+     * or the cameraId does not match any currently or previously available
+     * camera device.
+     *
+     * @throws SecurityException if the application does not have permission to
+     * access the camera
+     *
+     * @see #getCameraIdList
+     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+     */
+    @RequiresPermission(android.Manifest.permission.CAMERA)
+    public void openCamera(@NonNull String cameraId,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull final CameraDevice.StateCallback callback)
+            throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor was null");
+        }
+        openCameraForUid(cameraId, callback, executor, USE_CALLING_UID);
     }
 
     /**
@@ -449,7 +523,7 @@
      * @hide
      */
     public void openCameraForUid(@NonNull String cameraId,
-            @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler,
+            @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
             int clientUid)
             throws CameraAccessException {
 
@@ -457,19 +531,12 @@
             throw new IllegalArgumentException("cameraId was null");
         } else if (callback == null) {
             throw new IllegalArgumentException("callback was null");
-        } else if (handler == null) {
-            if (Looper.myLooper() != null) {
-                handler = new Handler();
-            } else {
-                throw new IllegalArgumentException(
-                        "Handler argument is null, but no looper exists in the calling thread");
-            }
         }
         if (CameraManagerGlobal.sCameraServiceDisabled) {
             throw new IllegalArgumentException("No cameras available on device");
         }
 
-        openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
+        openCameraDeviceUserAsync(cameraId, callback, executor, clientUid);
     }
 
     /**
@@ -728,12 +795,13 @@
          */
         private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
 
+        private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
         // Camera ID -> Status map
         private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
 
-        // Registered availablility callbacks and their handlers
-        private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
-            new ArrayMap<AvailabilityCallback, Handler>();
+        // Registered availablility callbacks and their executors
+        private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
+            new ArrayMap<AvailabilityCallback, Executor>();
 
         // torch client binder to set the torch mode with.
         private Binder mTorchClientBinder = new Binder();
@@ -741,9 +809,9 @@
         // Camera ID -> Torch status map
         private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
 
-        // Registered torch callbacks and their handlers
-        private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
-                new ArrayMap<TorchCallback, Handler>();
+        // Registered torch callbacks and their executors
+        private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap =
+                new ArrayMap<TorchCallback, Executor>();
 
         private final Object mLock = new Object();
 
@@ -925,49 +993,63 @@
             }
         }
 
-        private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
+        private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
                 final String id, final int status) {
             if (isAvailable(status)) {
-                handler.post(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            callback.onCameraAvailable(id);
-                        }
-                    });
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                callback.onCameraAvailable(id);
+                            }
+                        });
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             } else {
-                handler.post(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            callback.onCameraUnavailable(id);
-                        }
-                    });
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                callback.onCameraUnavailable(id);
+                            }
+                        });
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
 
-        private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
+        private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor,
                 final String id, final int status) {
             switch(status) {
                 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON:
-                case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF:
-                    handler.post(
-                            new Runnable() {
-                                @Override
-                                public void run() {
-                                    callback.onTorchModeChanged(id, status ==
-                                            ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
-                                }
+                case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: {
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            executor.execute(() -> {
+                                callback.onTorchModeChanged(id, status ==
+                                        ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON);
                             });
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
+                    }
                     break;
-                default:
-                    handler.post(
-                            new Runnable() {
-                                @Override
-                                public void run() {
-                                    callback.onTorchModeUnavailable(id);
-                                }
+                default: {
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            executor.execute(() -> {
+                                callback.onTorchModeUnavailable(id);
                             });
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
+                    }
                     break;
             }
         }
@@ -976,11 +1058,11 @@
          * Send the state of all known cameras to the provided listener, to initialize
          * the listener's knowledge of camera state.
          */
-        private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) {
+        private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) {
             for (int i = 0; i < mDeviceStatus.size(); i++) {
                 String id = mDeviceStatus.keyAt(i);
                 Integer status = mDeviceStatus.valueAt(i);
-                postSingleUpdate(callback, handler, id, status);
+                postSingleUpdate(callback, executor, id, status);
             }
         }
 
@@ -1039,18 +1121,18 @@
 
             final int callbackCount = mCallbackMap.size();
             for (int i = 0; i < callbackCount; i++) {
-                Handler handler = mCallbackMap.valueAt(i);
+                Executor executor = mCallbackMap.valueAt(i);
                 final AvailabilityCallback callback = mCallbackMap.keyAt(i);
 
-                postSingleUpdate(callback, handler, id, status);
+                postSingleUpdate(callback, executor, id, status);
             }
         } // onStatusChangedLocked
 
-        private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
+        private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
             for (int i = 0; i < mTorchStatus.size(); i++) {
                 String id = mTorchStatus.keyAt(i);
                 Integer status = mTorchStatus.valueAt(i);
-                postSingleTorchUpdate(callback, handler, id, status);
+                postSingleTorchUpdate(callback, executor, id, status);
             }
         }
 
@@ -1078,9 +1160,9 @@
 
             final int callbackCount = mTorchCallbackMap.size();
             for (int i = 0; i < callbackCount; i++) {
-                final Handler handler = mTorchCallbackMap.valueAt(i);
+                final Executor executor = mTorchCallbackMap.valueAt(i);
                 final TorchCallback callback = mTorchCallbackMap.keyAt(i);
-                postSingleTorchUpdate(callback, handler, id, status);
+                postSingleTorchUpdate(callback, executor, id, status);
             }
         } // onTorchStatusChangedLocked
 
@@ -1089,16 +1171,16 @@
          * global listener singleton.
          *
          * @param callback the new callback to send camera availability notices to
-         * @param handler The handler on which the callback should be invoked. May not be null.
+         * @param executor The executor which should invoke the callback. May not be null.
          */
-        public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
+        public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) {
             synchronized (mLock) {
                 connectCameraServiceLocked();
 
-                Handler oldHandler = mCallbackMap.put(callback, handler);
+                Executor oldExecutor = mCallbackMap.put(callback, executor);
                 // For new callbacks, provide initial availability information
-                if (oldHandler == null) {
-                    updateCallbackLocked(callback, handler);
+                if (oldExecutor == null) {
+                    updateCallbackLocked(callback, executor);
                 }
 
                 // If not connected to camera service, schedule a reconnect to camera service.
@@ -1120,14 +1202,14 @@
             }
         }
 
-        public void registerTorchCallback(TorchCallback callback, Handler handler) {
+        public void registerTorchCallback(TorchCallback callback, Executor executor) {
             synchronized(mLock) {
                 connectCameraServiceLocked();
 
-                Handler oldHandler = mTorchCallbackMap.put(callback, handler);
+                Executor oldExecutor = mTorchCallbackMap.put(callback, executor);
                 // For new callbacks, provide initial torch information
-                if (oldHandler == null) {
-                    updateTorchCallbackLocked(callback, handler);
+                if (oldExecutor == null) {
+                    updateTorchCallbackLocked(callback, executor);
                 }
 
                 // If not connected to camera service, schedule a reconnect to camera service.
@@ -1165,13 +1247,7 @@
          * availability callback or torch status callback.
          */
         private void scheduleCameraServiceReconnectionLocked() {
-            final Handler handler;
-
-            if (mCallbackMap.size() > 0) {
-                handler = mCallbackMap.valueAt(0);
-            } else if (mTorchCallbackMap.size() > 0) {
-                handler = mTorchCallbackMap.valueAt(0);
-            } else {
+            if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) {
                 // Not necessary to reconnect camera service if no client registers a callback.
                 return;
             }
@@ -1181,22 +1257,21 @@
                         " ms");
             }
 
-            handler.postDelayed(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            ICameraService cameraService = getCameraService();
-                            if (cameraService == null) {
-                                synchronized(mLock) {
-                                    if (DEBUG) {
-                                        Log.v(TAG, "Reconnecting Camera Service failed.");
-                                    }
-                                    scheduleCameraServiceReconnectionLocked();
-                                }
+            try {
+                mScheduler.schedule(() -> {
+                    ICameraService cameraService = getCameraService();
+                    if (cameraService == null) {
+                        synchronized(mLock) {
+                            if (DEBUG) {
+                                Log.v(TAG, "Reconnecting Camera Service failed.");
                             }
+                            scheduleCameraServiceReconnectionLocked();
                         }
-                    },
-                    CAMERA_SERVICE_RECONNECT_DELAY_MS);
+                    }
+                }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
+            } catch (RejectedExecutionException e) {
+                Log.e(TAG, "Failed to schedule camera service re-connect: " + e);
+            }
         }
 
         /**
diff --git a/android/hardware/camera2/CameraMetadata.java b/android/hardware/camera2/CameraMetadata.java
index e7c8961..1a5d3ac 100644
--- a/android/hardware/camera2/CameraMetadata.java
+++ b/android/hardware/camera2/CameraMetadata.java
@@ -342,7 +342,7 @@
     /**
      * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the optical center of
      * the largest camera device facing the same direction as this camera.</p>
-     * <p>This default value for API levels before Android P.</p>
+     * <p>This is the default value for API levels before Android P.</p>
      *
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
@@ -352,7 +352,6 @@
     /**
      * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the position of the
      * primary gyroscope of this Android device.</p>
-     * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p>
      *
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
@@ -685,7 +684,7 @@
      * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
      * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
      * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
      * </ul>
      * </li>
      * <li>The {@link CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE android.depth.depthIsExclusive} entry is listed by this device.</li>
@@ -703,12 +702,12 @@
      * rate, including depth stall time.</p>
      *
      * @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_FACING
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
@@ -801,46 +800,12 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
 
     /**
-     * <p>The device supports controls and metadata required for accurate motion tracking for
-     * use cases such as augmented reality, electronic image stabilization, and so on.</p>
-     * <p>This means this camera device has accurate optical calibration and timestamps relative
-     * to the inertial sensors.</p>
-     * <p>This capability requires the camera device to support the following:</p>
-     * <ul>
-     * <li>Capture request templates {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW } and {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_BEST } are defined.</li>
-     * <li>The stream configurations listed in {@link android.hardware.camera2.CameraDevice#createCaptureSession } for MOTION_TRACKING are
-     *   supported, either at 30 or 60fps maximum frame rate.</li>
-     * <li>The following camera characteristics and capture result metadata are provided:<ul>
-     * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
-     * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
-     * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
-     * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} with value GYROSCOPE</li>
-     * </ul>
-     * </li>
-     * <li>The {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} field has value <code>REALTIME</code>. When compared to
-     *   timestamps from the device's gyroscopes, the clock difference for events occuring at
-     *   the same actual time instant will be less than 1 ms.</li>
-     * <li>The value of the {@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew} field is accurate to within 1 ms.</li>
-     * <li>The value of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} is guaranteed to be available in the
-     *   capture result.</li>
-     * <li>The {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} control supports MOTION_TRACKING to limit maximum
-     *   exposure to 20 milliseconds.</li>
-     * <li>The stream configurations required for MOTION_TRACKING (listed at {@link android.hardware.camera2.CameraDevice#createCaptureSession }) can operate at least at
-     *   30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in
-     *   {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}.</li>
-     * </ul>
+     * <p>The camera device supports the MOTION_TRACKING value for
+     * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent}, which limits maximum exposure time to 20 ms.</p>
+     * <p>This limits the motion blur of capture images, resulting in better image tracking
+     * results for use cases such as image stabilization or augmented reality.</p>
      *
-     * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
      * @see CaptureRequest#CONTROL_CAPTURE_INTENT
-     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
-     * @see CameraCharacteristics#LENS_POSE_REFERENCE
-     * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
-     * @see CaptureRequest#SENSOR_EXPOSURE_TIME
-     * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
-     * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
@@ -861,37 +826,49 @@
      * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
      * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
      * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
-     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * <li>{@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}</li>
      * </ul>
      * </li>
+     * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
+     *   the same.</li>
      * <li>The logical camera device must be LIMITED or higher device.</li>
      * </ul>
      * <p>Both the logical camera device and its underlying physical devices support the
      * mandatory stream combinations required for their device levels.</p>
      * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
      * <ul>
-     * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+     * <li>For each guaranteed stream combination, the logical camera supports replacing one
+     *   logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
      *   or raw stream with two physical streams of the same size and format, each from a
      *   separate physical camera, given that the size and format are supported by both
      *   physical cameras.</li>
-     * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
-     *   advertise RAW capability, but the underlying physical cameras do. This is usually
-     *   the case when the physical cameras have different sensor sizes.</li>
+     * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
+     *   cameras do, the logical camera will support guaranteed stream combinations for RAW
+     *   capability, except that the RAW streams will be physical streams, each from a separate
+     *   physical camera. This is usually the case when the physical cameras have different
+     *   sensor sizes.</li>
      * </ul>
      * <p>Using physical streams in place of a logical stream of the same size and format will
      * not slow down the frame rate of the capture, as long as the minimum frame duration
      * of the physical and logical streams are the same.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
 
+    /**
+     * <p>The camera device is a monochrome camera that doesn't contain a color filter array,
+     * and the pixel values on U and Y planes are all 128.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -1465,9 +1442,11 @@
      * for the external flash. Otherwise, this mode acts like ON.</p>
      * <p>When the external flash is turned off, AE mode should be changed to one of the
      * other available AE modes.</p>
-     * <p>If the camera device supports AE external flash mode, aeState must be
-     * FLASH_REQUIRED after the camera device finishes AE scan and it's too dark without
+     * <p>If the camera device supports AE external flash mode, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} must
+     * be FLASH_REQUIRED after the camera device finishes AE scan and it's too dark without
      * flash.</p>
+     *
+     * @see CaptureResult#CONTROL_AE_STATE
      * @see CaptureRequest#CONTROL_AE_MODE
      */
     public static final int CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5;
@@ -2676,6 +2655,10 @@
 
     /**
      * <p>Include OIS data in the capture result.</p>
+     * <p>{@link CaptureResult#STATISTICS_OIS_SAMPLES android.statistics.oisSamples} provides OIS sample data in the
+     * output result metadata.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_SAMPLES
      * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
      */
     public static final int STATISTICS_OIS_DATA_MODE_ON = 1;
@@ -2754,6 +2737,31 @@
     public static final int TONEMAP_PRESET_CURVE_REC709 = 1;
 
     //
+    // Enumeration values for CaptureRequest#DISTORTION_CORRECTION_MODE
+    //
+
+    /**
+     * <p>No distortion correction is applied.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_OFF = 0;
+
+    /**
+     * <p>Lens distortion correction is applied without reducing frame rate
+     * relative to sensor output. It may be the same as OFF if distortion correction would
+     * reduce frame rate relative to sensor.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_FAST = 1;
+
+    /**
+     * <p>High-quality distortion correction is applied, at the cost of
+     * possibly reduced frame rate relative to sensor output.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2;
+
+    //
     // Enumeration values for CaptureResult#CONTROL_AE_STATE
     //
 
diff --git a/android/hardware/camera2/CaptureRequest.java b/android/hardware/camera2/CaptureRequest.java
index 481b764..2252571 100644
--- a/android/hardware/camera2/CaptureRequest.java
+++ b/android/hardware/camera2/CaptureRequest.java
@@ -24,6 +24,7 @@
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.TypeReference;
+import android.hardware.camera2.utils.SurfaceUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
@@ -643,6 +644,30 @@
                         break;
                     }
                 }
+
+                if (!streamFound) {
+                    // Check if we can match s by native object ID
+                    long reqSurfaceId = SurfaceUtils.getSurfaceId(s);
+                    for (int j = 0; j < configuredOutputs.size(); ++j) {
+                        int streamId = configuredOutputs.keyAt(j);
+                        OutputConfiguration outConfig = configuredOutputs.valueAt(j);
+                        int surfaceId = 0;
+                        for (Surface outSurface : outConfig.getSurfaces()) {
+                            if (reqSurfaceId == SurfaceUtils.getSurfaceId(outSurface)) {
+                                streamFound = true;
+                                mStreamIdxArray[i] = streamId;
+                                mSurfaceIdxArray[i] = surfaceId;
+                                i++;
+                                break;
+                            }
+                            surfaceId++;
+                        }
+                        if (streamFound) {
+                            break;
+                        }
+                    }
+                }
+
                 if (!streamFound) {
                     mStreamIdxArray = null;
                     mSurfaceIdxArray = null;
@@ -783,7 +808,7 @@
          *
          *<p>This method can be called for logical camera devices, which are devices that have
          * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
-         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty set of
          * physical devices that are backing the logical camera. The camera Id included in the
          * 'physicalCameraId' argument selects an individual physical device that will receive
          * the customized capture request field.</p>
@@ -1432,7 +1457,8 @@
      * is used, all non-zero weights will have the same effect. A region with 0 weight is
      * ignored.</p>
      * <p>If all regions have 0 weight, then no specific metering area needs to be used by the
-     * camera device.</p>
+     * camera device. The capture result will either be a zero weight region as well, or
+     * the region selected by the camera device as the focus area of interest.</p>
      * <p>If the metering region is outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in
      * capture result metadata, the camera device will ignore the sections outside the crop
      * region and output only the intersection rectangle as the metering region in the result
@@ -2757,21 +2783,18 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
-     * <p>Whether the camera device outputs the OIS data in output
+     * <p>A control for selecting whether OIS position information is included in output
      * result metadata.</p>
-     * <p>When set to ON,
-     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
-     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
-     * android.Statistics.info.availableOisDataModes</p>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
-     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
      * @see #STATISTICS_OIS_DATA_MODE_OFF
      * @see #STATISTICS_OIS_DATA_MODE_ON
      */
@@ -2830,6 +2853,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -2892,6 +2917,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -3144,6 +3171,49 @@
     public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
             new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
 
+    /**
+     * <p>Mode of operation for the lens distortion correction block.</p>
+     * <p>The lens distortion correction block attempts to improve image quality by fixing
+     * radial, tangential, or other geometric aberrations in the camera device's optics.  If
+     * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+     * <p>OFF means no distortion correction is done.</p>
+     * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+     * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+     * correction algorithms, even if it slows down capture rate. FAST means the camera device
+     * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+     * any correction at all would slow down capture rate.  Every output stream will have a
+     * similar amount of enhancement applied.</p>
+     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
+     * regions are also not affected by correction.</p>
+     * <p>Applications enabling distortion correction need to pay extra attention when converting
+     * image coordinates between corrected output buffers and the sensor array. For example, if
+     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+     * calculate the tap position on the sensor active array to be used with
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+     * they need to be drawn on top of the corrected output buffers.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see #DISTORTION_CORRECTION_MODE_OFF
+     * @see #DISTORTION_CORRECTION_MODE_FAST
+     * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+     */
+    @PublicKey
+    public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+            new Key<Integer>("android.distortionCorrection.mode", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/android/hardware/camera2/CaptureResult.java b/android/hardware/camera2/CaptureResult.java
index d730fa8..8df5447 100644
--- a/android/hardware/camera2/CaptureResult.java
+++ b/android/hardware/camera2/CaptureResult.java
@@ -1001,8 +1001,8 @@
      * </tbody>
      * </table>
      * <p>If the camera device supports AE external flash mode (ON_EXTERNAL_FLASH is included in
-     * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}), aeState must be FLASH_REQUIRED after the camera device
-     * finishes AE scan and it's too dark without flash.</p>
+     * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES android.control.aeAvailableModes}), {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} must be FLASH_REQUIRED after
+     * the camera device finishes AE scan and it's too dark without flash.</p>
      * <p>For the above table, the camera device may skip reporting any state changes that happen
      * without application intervention (i.e. mode switch, trigger, locking). Any state that
      * can be skipped in that manner is called a transient state.</p>
@@ -1081,6 +1081,7 @@
      * @see CaptureRequest#CONTROL_AE_LOCK
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+     * @see CaptureResult#CONTROL_AE_STATE
      * @see CaptureRequest#CONTROL_MODE
      * @see CaptureRequest#CONTROL_SCENE_MODE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
@@ -1156,7 +1157,8 @@
      * is used, all non-zero weights will have the same effect. A region with 0 weight is
      * ignored.</p>
      * <p>If all regions have 0 weight, then no specific metering area needs to be used by the
-     * camera device.</p>
+     * camera device. The capture result will either be a zero weight region as well, or
+     * the region selected by the camera device as the focus area of interest.</p>
      * <p>If the metering region is outside the used {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} returned in
      * capture result metadata, the camera device will ignore the sections outside the crop
      * region and output only the intersection rectangle as the metering region in the result
@@ -2781,7 +2783,7 @@
      * from the main sensor along the +X axis (to the right from the user's perspective) will
      * report <code>(0.03, 0, 0)</code>.</p>
      * <p>To transform a pixel coordinates between two cameras facing the same direction, first
-     * the source camera {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} must be corrected for.  Then the source
+     * the source camera {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} must be corrected for.  Then the source
      * camera {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration} needs to be applied, followed by the
      * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the source camera, the translation of the source camera
      * relative to the destination camera, the {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} of the destination
@@ -2795,10 +2797,10 @@
      * <p><b>Units</b>: Meters</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
      * @see CameraCharacteristics#LENS_POSE_REFERENCE
      * @see CameraCharacteristics#LENS_POSE_ROTATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      */
     @PublicKey
     public static final Key<float[]> LENS_POSE_TRANSLATION =
@@ -2844,7 +2846,7 @@
      * where <code>(0,0)</code> is the top-left of the
      * preCorrectionActiveArraySize rectangle. Once the pose and
      * intrinsic calibration transforms have been applied to a
-     * world point, then the {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}
+     * world point, then the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion}
      * transform needs to be applied, and the result adjusted to
      * be in the {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} coordinate
      * system (where <code>(0, 0)</code> is the top-left of the
@@ -2857,9 +2859,9 @@
      * coordinate system.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
+     * @see CameraCharacteristics#LENS_DISTORTION
      * @see CameraCharacteristics#LENS_POSE_ROTATION
      * @see CameraCharacteristics#LENS_POSE_TRANSLATION
-     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
@@ -2901,12 +2903,59 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @deprecated
+     * <p>This field was inconsistently defined in terms of its
+     * normalization. Use {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} instead.</p>
+     *
+     * @see CameraCharacteristics#LENS_DISTORTION
+
      */
+    @Deprecated
     @PublicKey
     public static final Key<float[]> LENS_RADIAL_DISTORTION =
             new Key<float[]>("android.lens.radialDistortion", float[].class);
 
     /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial and tangential lens distortion.</p>
+     * <p>Replaces the deprecated {@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion} field, which was
+     * inconsistently defined.</p>
+     * <p>Three radial distortion coefficients <code>[kappa_1, kappa_2,
+     * kappa_3]</code> and two tangential distortion coefficients
+     * <code>[kappa_4, kappa_5]</code> that can be used to correct the
+     * lens's geometric distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_4 * (2 * x_i * y_i) + kappa_5 * ( r^2 + 2 * x_i^2 )
+     *  y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 ) +
+     *        kappa_5 * (2 * x_i * y_i) + kappa_4 * ( r^2 + 2 * y_i^2 )
+     * </code></pre>
+     * <p>Here, <code>[x_c, y_c]</code> are the coordinates to sample in the
+     * input image that correspond to the pixel values in the
+     * corrected image at the coordinate <code>[x_i, y_i]</code>:</p>
+     * <pre><code> correctedImage(x_i, y_i) = sample_at(x_c, y_c, inputImage)
+     * </code></pre>
+     * <p>The pixel coordinates are defined in a coordinate system
+     * related to the {@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}
+     * calibration fields; see that entry for details of the mapping stages.
+     * Both <code>[x_i, y_i]</code> and <code>[x_c, y_c]</code>
+     * have <code>(0,0)</code> at the lens optical center <code>[c_x, c_y]</code>, and
+     * the range of the coordinates depends on the focal length
+     * terms of the intrinsic calibration.</p>
+     * <p>Finally, <code>r</code> represents the radial distance from the
+     * optical center, <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p>The distortion model used is the Brown-Conrady model.</p>
+     * <p><b>Units</b>:
+     * Unitless coefficients.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_DISTORTION =
+            new Key<float[]>("android.lens.distortion", float[].class);
+
+    /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
      * excessive noise added by the capture process, especially in dark conditions.</p>
@@ -2979,6 +3028,8 @@
      * Optional. Default value is FINAL.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -2995,6 +3046,8 @@
      * &gt; 0</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Not used in HALv3 or newer</p>
+
      * @hide
      */
     @Deprecated
@@ -3775,6 +3828,8 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @deprecated
+     * <p>Never fully implemented or specified; do not use</p>
+
      * @hide
      */
     @Deprecated
@@ -3799,6 +3854,8 @@
      * regardless of the android.control.* current values.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * @deprecated
+     * <p>Never fully implemented or specified; do not use</p>
+
      * @hide
      */
     @Deprecated
@@ -3909,21 +3966,18 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
-     * <p>Whether the camera device outputs the OIS data in output
+     * <p>A control for selecting whether OIS position information is included in output
      * result metadata.</p>
-     * <p>When set to ON,
-     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
-     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
      *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
-     * android.Statistics.info.availableOisDataModes</p>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES android.statistics.info.availableOisDataModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
-     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES
      * @see #STATISTICS_OIS_DATA_MODE_OFF
      * @see #STATISTICS_OIS_DATA_MODE_ON
      */
@@ -3939,8 +3993,8 @@
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      *
      * @see CaptureResult#SENSOR_TIMESTAMP
+     * @hide
      */
-    @PublicKey
     public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
             new Key<long[]>("android.statistics.oisTimestamps", long[].class);
 
@@ -3948,16 +4002,14 @@
      * <p>An array of shifts of OIS samples, in x direction.</p>
      * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
      * A positive value is a shift from left to right in active array coordinate system. For
-     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * example, if the optical center is (1000, 500) in active array coordinates, a shift of
      * (3, 0) puts the new optical center at (1003, 500).</p>
      * <p>The number of shifts must match the number of timestamps in
-     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+     * android.statistics.oisTimestamps.</p>
      * <p><b>Units</b>: Pixels in active array.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     *
-     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @hide
      */
-    @PublicKey
     public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
             new Key<float[]>("android.statistics.oisXShifts", float[].class);
 
@@ -3965,20 +4017,35 @@
      * <p>An array of shifts of OIS samples, in y direction.</p>
      * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
      * A positive value is a shift from top to bottom in active array coordinate system. For
-     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * example, if the optical center is (1000, 500) in active array coordinates, a shift of
      * (0, 5) puts the new optical center at (1000, 505).</p>
      * <p>The number of shifts must match the number of timestamps in
-     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+     * android.statistics.oisTimestamps.</p>
      * <p><b>Units</b>: Pixels in active array.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     *
-     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @hide
      */
-    @PublicKey
     public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
             new Key<float[]>("android.statistics.oisYShifts", float[].class);
 
     /**
+     * <p>An array of OIS samples.</p>
+     * <p>Each OIS sample contains the timestamp and the amount of shifts in x and y direction,
+     * in pixels, of the OIS sample.</p>
+     * <p>A positive value for a shift in x direction is a shift from left to right in active array
+     * coordinate system. For example, if the optical center is (1000, 500) in active array
+     * coordinates, a shift of (3, 0) puts the new optical center at (1003, 500).</p>
+     * <p>A positive value for a shift in y direction is a shift from top to bottom in active array
+     * coordinate system. For example, if the optical center is (1000, 500) in active array
+     * coordinates, a shift of (0, 5) puts the new optical center at (1000, 505).</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.OisSample[]> STATISTICS_OIS_SAMPLES =
+            new Key<android.hardware.camera2.params.OisSample[]>("android.statistics.oisSamples", android.hardware.camera2.params.OisSample[].class);
+
+    /**
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
@@ -4029,6 +4096,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -4091,6 +4160,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -4383,6 +4454,49 @@
     public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
             new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
 
+    /**
+     * <p>Mode of operation for the lens distortion correction block.</p>
+     * <p>The lens distortion correction block attempts to improve image quality by fixing
+     * radial, tangential, or other geometric aberrations in the camera device's optics.  If
+     * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+     * <p>OFF means no distortion correction is done.</p>
+     * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+     * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+     * correction algorithms, even if it slows down capture rate. FAST means the camera device
+     * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+     * any correction at all would slow down capture rate.  Every output stream will have a
+     * similar amount of enhancement applied.</p>
+     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
+     * regions are also not affected by correction.</p>
+     * <p>Applications enabling distortion correction need to pay extra attention when converting
+     * image coordinates between corrected output buffers and the sensor array. For example, if
+     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+     * calculate the tap position on the sensor active array to be used with
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+     * they need to be drawn on top of the corrected output buffers.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see #DISTORTION_CORRECTION_MODE_OFF
+     * @see #DISTORTION_CORRECTION_MODE_FAST
+     * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+     */
+    @PublicKey
+    public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+            new Key<Integer>("android.distortionCorrection.mode", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/android/hardware/camera2/TotalCaptureResult.java b/android/hardware/camera2/TotalCaptureResult.java
index 657745c..4e20cb8 100644
--- a/android/hardware/camera2/TotalCaptureResult.java
+++ b/android/hardware/camera2/TotalCaptureResult.java
@@ -19,10 +19,13 @@
 import android.annotation.NonNull;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * <p>The total assembled results of a single image capture from the image sensor.</p>
@@ -44,6 +47,12 @@
  * as {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE}). Refer to each key documentation on
  * a case-by-case basis.</p>
  *
+ * <p>For a logical multi-camera device, if the CaptureRequest contains a surface for an underlying
+ * physical camera, the corresponding {@link TotalCaptureResult} object will include the metadata
+ * for that physical camera. And the mapping between the physical camera id and result metadata
+ * can be accessed via {@link #getPhysicalCameraResults}. If all requested surfaces are for the
+ * logical camera, no metadata for physical camera will be included.</p>
+ *
  * <p>{@link TotalCaptureResult} objects are immutable.</p>
  *
  * @see CameraDevice.CaptureCallback#onCaptureCompleted
@@ -52,6 +61,8 @@
 
     private final List<CaptureResult> mPartialResults;
     private final int mSessionId;
+    // The map between physical camera id and capture result
+    private final HashMap<String, CaptureResult> mPhysicalCaptureResults;
 
     /**
      * Takes ownership of the passed-in camera metadata and the partial results
@@ -60,7 +71,8 @@
      * @hide
      */
     public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
-            CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
+            CaptureResultExtras extras, List<CaptureResult> partials, int sessionId,
+            PhysicalCaptureResultInfo physicalResults[]) {
         super(results, parent, extras);
 
         if (partials == null) {
@@ -70,6 +82,14 @@
         }
 
         mSessionId = sessionId;
+
+        mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
+        for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
+            CaptureResult physicalResult = new CaptureResult(
+                    onePhysicalResult.getCameraMetadata(), parent, extras);
+            mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
+                    physicalResult);
+        }
     }
 
     /**
@@ -83,6 +103,7 @@
 
         mPartialResults = new ArrayList<>();
         mSessionId = CameraCaptureSession.SESSION_ID_NONE;
+        mPhysicalCaptureResults = new HashMap<String, CaptureResult>();
     }
 
     /**
@@ -111,4 +132,22 @@
     public int getSessionId() {
         return mSessionId;
     }
+
+    /**
+     * Get the map between physical camera ids and their capture result metadata
+     *
+     * <p>This function can be called for logical multi-camera devices, which are devices that have
+     * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to {@link
+     * CameraCharacteristics#getPhysicalCameraIds} return a non-empty set of physical devices that
+     * are backing the logical camera.</p>
+     *
+     * <p>If one or more streams from the underlying physical cameras were requested by the
+     * corresponding capture request, this function returns the result metadata for those physical
+     * cameras. Otherwise, an empty map is returned.</p>
+
+     * @return unmodifiable map between physical camera ids and their capture result metadata
+     */
+    public Map<String, CaptureResult> getPhysicalCameraResults() {
+        return Collections.unmodifiableMap(mPhysicalCaptureResults);
+    }
 }
diff --git a/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
deleted file mode 100644
index 866f370..0000000
--- a/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
+++ /dev/null
@@ -1,85 +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.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * A dispatcher that replaces one argument with another; replaces any argument at an index
- * with another argument.
- *
- * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
- * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
- * be something
- * that's not an {@code int}.</p>
- *
- * @param <T>
- *          source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <TArg>
- *          argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
- *          will be overriden to objects of this type
- */
-public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
-
-    private final Dispatchable<T> mTarget;
-    private final int mArgumentIndex;
-    private final TArg mReplaceWith;
-
-    /**
-     * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
-     * after the argument is replaced.
-     *
-     * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
-     * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
-     * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
-     *
-     * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
-     * passed through with the arguments unchanged.</p>
-     *
-     * @param target destination dispatch type, methods will be redirected to this dispatcher
-     * @param argumentIndex the numeric index of the argument {@code >= 0}
-     * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
-     */
-    public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
-            TArg replaceWith) {
-        mTarget = checkNotNull(target, "target must not be null");
-        mArgumentIndex = checkArgumentNonnegative(argumentIndex,
-                "argumentIndex must not be negative");
-        mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) throws Throwable {
-
-        if (args.length > mArgumentIndex) {
-            args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
-            args[mArgumentIndex] = mReplaceWith;
-        }
-
-        return mTarget.dispatch(method, args);
-    }
-
-    private static Object[] arrayCopy(Object[] array) {
-        int length = array.length;
-        Object[] newArray = new Object[length];
-        for (int i = 0; i < length; ++i) {
-            newArray[i] = array[i];
-        }
-        return newArray;
-    }
-}
diff --git a/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/android/hardware/camera2/dispatch/BroadcastDispatcher.java
deleted file mode 100644
index fe575b2..0000000
--- a/android/hardware/camera2/dispatch/BroadcastDispatcher.java
+++ /dev/null
@@ -1,64 +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.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Broadcast a single dispatch into multiple other dispatchables.
- *
- * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
- * see the same dispatch as well. The first target's return value is returned.</p>
- *
- * <p>This enables a single listener to be converted into a multi-listener.</p>
- */
-public class BroadcastDispatcher<T> implements Dispatchable<T> {
-
-    private final List<Dispatchable<T>> mDispatchTargets;
-
-    /**
-     * Create a broadcast dispatcher from the supplied dispatch targets.
-     *
-     * @param dispatchTargets one or more targets to dispatch to
-     */
-    @SafeVarargs
-    public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
-        mDispatchTargets = Arrays.asList(
-                checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) throws Throwable {
-        Object result = null;
-        boolean gotResult = false;
-
-        for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
-            Object localResult = dispatchTarget.dispatch(method, args);
-
-            if (!gotResult) {
-                gotResult = true;
-                result = localResult;
-            }
-        }
-
-        return result;
-    }
-}
diff --git a/android/hardware/camera2/dispatch/Dispatchable.java b/android/hardware/camera2/dispatch/Dispatchable.java
deleted file mode 100644
index 753103f..0000000
--- a/android/hardware/camera2/dispatch/Dispatchable.java
+++ /dev/null
@@ -1,35 +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.hardware.camera2.dispatch;
-
-import java.lang.reflect.Method;
-
-/**
- * Dynamically dispatch a method and its argument to some object.
- *
- * <p>This can be used to intercept method calls and do work around them, redirect work,
- * or block calls entirely.</p>
- */
-public interface Dispatchable<T> {
-    /**
-     * Dispatch the method and arguments to this object.
-     * @param method a method defined in class {@code T}
-     * @param args arguments corresponding to said {@code method}
-     * @return the object returned when invoking {@code method}
-     * @throws Throwable any exception that might have been raised while invoking the method
-     */
-    public Object dispatch(Method method, Object[] args) throws Throwable;
-}
diff --git a/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
deleted file mode 100644
index 75f97e4..0000000
--- a/android/hardware/camera2/dispatch/DuckTypingDispatcher.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.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Duck typing dispatcher; converts dispatch methods calls from one class to another by
- * looking up equivalently methods at runtime by name.
- *
- * <p>For example, if two types have identical method names and arguments, but
- * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
- * made from one type to the other.</p>
- *
- * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
- * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
- */
-public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
-
-    private final MethodNameInvoker<T> mDuck;
-
-    /**
-     * Create a new duck typing dispatcher.
-     *
-     * @param target destination dispatch type, methods will be redirected to this dispatcher
-     * @param targetClass destination dispatch class, methods will be converted to this class's
-     */
-    public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
-        checkNotNull(targetClass, "targetClass must not be null");
-        checkNotNull(target, "target must not be null");
-
-        mDuck = new MethodNameInvoker<T>(target, targetClass);
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) {
-        return mDuck.invoke(method.getName(), args);
-    }
-}
diff --git a/android/hardware/camera2/dispatch/HandlerDispatcher.java b/android/hardware/camera2/dispatch/HandlerDispatcher.java
deleted file mode 100644
index f8e9d49..0000000
--- a/android/hardware/camera2/dispatch/HandlerDispatcher.java
+++ /dev/null
@@ -1,85 +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.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.os.Handler;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-/**
- * Forward all interface calls into a handler by posting it as a {@code Runnable}.
- *
- * <p>All calls will return immediately; functions with return values will return a default
- * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
- *
- * <p>Any exceptions thrown on the handler while trying to invoke a method
- * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
- * checked exceptions to be thrown will result in "undefined" behavior
- * (although in practice it is usually thrown as normal).</p>
- */
-public class HandlerDispatcher<T> implements Dispatchable<T> {
-
-    private static final String TAG = "HandlerDispatcher";
-
-    private final Dispatchable<T> mDispatchTarget;
-    private final Handler mHandler;
-
-    /**
-     * Create a dispatcher that forwards it's dispatch calls by posting
-     * them onto the {@code handler} as a {@code Runnable}.
-     *
-     * @param dispatchTarget the destination whose method calls will be redirected into the handler
-     * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
-     * @param <T> the type of the element you want to wrap.
-     * @return a dispatcher that will forward it's dispatch calls to a handler
-     */
-    public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
-        mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-        mHandler = checkNotNull(handler, "handler must not be null");
-    }
-
-    @Override
-    public Object dispatch(final Method method, final Object[] args) throws Throwable {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mDispatchTarget.dispatch(method, args);
-                } catch (InvocationTargetException e) {
-                    Throwable t = e.getTargetException();
-                    // Potential UB. Hopefully 't' is a runtime exception.
-                    UncheckedThrow.throwAnyException(t);
-                } catch (IllegalAccessException e) {
-                    // Impossible
-                    Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
-                } catch (IllegalArgumentException e) {
-                    // Impossible
-                    Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
-                } catch (Throwable e) {
-                    UncheckedThrow.throwAnyException(e);
-                }
-            }
-        });
-
-        // TODO handle primitive return values that would avoid NPE if unboxed
-        return null;
-    }
-}
diff --git a/android/hardware/camera2/dispatch/InvokeDispatcher.java b/android/hardware/camera2/dispatch/InvokeDispatcher.java
deleted file mode 100644
index ac5f526..0000000
--- a/android/hardware/camera2/dispatch/InvokeDispatcher.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.hardware.camera2.dispatch;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import static com.android.internal.util.Preconditions.*;
-
-
-public class InvokeDispatcher<T> implements Dispatchable<T> {
-
-    private static final String TAG = "InvocationSink";
-    private final T mTarget;
-
-    public InvokeDispatcher(T target) {
-        mTarget = checkNotNull(target, "target must not be null");
-    }
-
-    @Override
-    public Object dispatch(Method method, Object[] args) {
-        try {
-            return method.invoke(mTarget, args);
-        } catch (InvocationTargetException e) {
-            Throwable t = e.getTargetException();
-            // Potential UB. Hopefully 't' is a runtime exception.
-            UncheckedThrow.throwAnyException(t);
-        } catch (IllegalAccessException e) {
-            // Impossible
-            Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
-        } catch (IllegalArgumentException e) {
-            // Impossible
-            Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
-        }
-
-        // unreachable
-        return null;
-    }
-}
diff --git a/android/hardware/camera2/dispatch/MethodNameInvoker.java b/android/hardware/camera2/dispatch/MethodNameInvoker.java
deleted file mode 100644
index 8296b7a..0000000
--- a/android/hardware/camera2/dispatch/MethodNameInvoker.java
+++ /dev/null
@@ -1,97 +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.hardware.camera2.dispatch;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.hardware.camera2.utils.UncheckedThrow;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
- *
- * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
- */
-public class MethodNameInvoker<T> {
-
-    private final Dispatchable<T> mTarget;
-    private final Class<T> mTargetClass;
-    private final Method[] mTargetClassMethods;
-    private final ConcurrentHashMap<String, Method> mMethods =
-            new ConcurrentHashMap<>();
-
-    /**
-     * Create a new method name invoker.
-     *
-     * @param target destination dispatch type, invokes will be redirected to this dispatcher
-     * @param targetClass destination dispatch class, the invoked methods will be from this class
-     */
-    public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
-        mTargetClass = targetClass;
-        mTargetClassMethods = targetClass.getMethods();
-        mTarget = target;
-    }
-
-    /**
-     * Invoke a method by its name.
-     *
-     * <p>If more than one method exists in {@code targetClass}, the first method with the right
-     * number of arguments will be used, and later calls will all use that method.</p>
-     *
-     * @param methodName
-     *          The name of the method, which will be matched 1:1 to the destination method
-     * @param params
-     *          Variadic parameter list.
-     * @return
-     *          The same kind of value that would normally be returned by calling {@code methodName}
-     *          statically.
-     *
-     * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
-     * @throws Throwable will rethrow anything that the target method would normally throw
-     */
-    @SuppressWarnings("unchecked")
-    public <K> K invoke(String methodName, Object... params) {
-        checkNotNull(methodName, "methodName must not be null");
-
-        Method targetMethod = mMethods.get(methodName);
-        if (targetMethod == null) {
-            for (Method method : mTargetClassMethods) {
-                // TODO future: match types of params if possible
-                if (method.getName().equals(methodName) &&
-                        (params.length == method.getParameterTypes().length) ) {
-                    targetMethod = method;
-                    mMethods.put(methodName, targetMethod);
-                    break;
-                }
-            }
-
-            if (targetMethod == null) {
-                throw new IllegalArgumentException(
-                        "Method " + methodName + " does not exist on class " + mTargetClass);
-            }
-        }
-
-        try {
-            return (K) mTarget.dispatch(targetMethod, params);
-        } catch (Throwable e) {
-            UncheckedThrow.throwAnyException(e);
-            // unreachable
-            return null;
-        }
-    }
-}
diff --git a/android/hardware/camera2/dispatch/NullDispatcher.java b/android/hardware/camera2/dispatch/NullDispatcher.java
deleted file mode 100644
index fada075..0000000
--- a/android/hardware/camera2/dispatch/NullDispatcher.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.hardware.camera2.dispatch;
-
-
-import java.lang.reflect.Method;
-
-/**
- * Do nothing when dispatching; follows the null object pattern.
- */
-public class NullDispatcher<T> implements Dispatchable<T> {
-    /**
-     * Create a dispatcher that does nothing when dispatched to.
-     */
-    public NullDispatcher() {
-    }
-
-    /**
-     * Do nothing; all parameters are ignored.
-     */
-    @Override
-    public Object dispatch(Method method, Object[] args) {
-        return null;
-    }
-}
diff --git a/android/hardware/camera2/impl/CallbackProxies.java b/android/hardware/camera2/impl/CallbackProxies.java
index c9eecf1..9e4cb80 100644
--- a/android/hardware/camera2/impl/CallbackProxies.java
+++ b/android/hardware/camera2/impl/CallbackProxies.java
@@ -15,16 +15,17 @@
  */
 package android.hardware.camera2.impl;
 
+import android.os.Binder;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.dispatch.Dispatchable;
-import android.hardware.camera2.dispatch.MethodNameInvoker;
 import android.view.Surface;
 
+import java.util.concurrent.Executor;
+
 import static com.android.internal.util.Preconditions.*;
 
 /**
@@ -34,164 +35,86 @@
  * to use our own proxy mechanism.</p>
  */
 public class CallbackProxies {
-
-    // TODO: replace with codegen
-
-    public static class DeviceStateCallbackProxy extends CameraDeviceImpl.StateCallbackKK {
-        private final MethodNameInvoker<CameraDeviceImpl.StateCallbackKK> mProxy;
-
-        public DeviceStateCallbackProxy(
-                Dispatchable<CameraDeviceImpl.StateCallbackKK> dispatchTarget) {
-            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-            mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateCallbackKK.class);
-        }
-
-        @Override
-        public void onOpened(CameraDevice camera) {
-            mProxy.invoke("onOpened", camera);
-        }
-
-        @Override
-        public void onDisconnected(CameraDevice camera) {
-            mProxy.invoke("onDisconnected", camera);
-        }
-
-        @Override
-        public void onError(CameraDevice camera, int error) {
-            mProxy.invoke("onError", camera, error);
-        }
-
-        @Override
-        public void onUnconfigured(CameraDevice camera) {
-            mProxy.invoke("onUnconfigured", camera);
-        }
-
-        @Override
-        public void onActive(CameraDevice camera) {
-            mProxy.invoke("onActive", camera);
-        }
-
-        @Override
-        public void onBusy(CameraDevice camera) {
-            mProxy.invoke("onBusy", camera);
-        }
-
-        @Override
-        public void onClosed(CameraDevice camera) {
-            mProxy.invoke("onClosed", camera);
-        }
-
-        @Override
-        public void onIdle(CameraDevice camera) {
-            mProxy.invoke("onIdle", camera);
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
-        private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
-
-        public DeviceCaptureCallbackProxy(
-                Dispatchable<CameraDeviceImpl.CaptureCallback> dispatchTarget) {
-            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-            mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureCallback.class);
-        }
-
-        @Override
-        public void onCaptureStarted(CameraDevice camera,
-                CaptureRequest request, long timestamp, long frameNumber) {
-            mProxy.invoke("onCaptureStarted", camera, request, timestamp, frameNumber);
-        }
-
-        @Override
-        public void onCapturePartial(CameraDevice camera,
-                CaptureRequest request, CaptureResult result) {
-            mProxy.invoke("onCapturePartial", camera, request, result);
-        }
-
-        @Override
-        public void onCaptureProgressed(CameraDevice camera,
-                CaptureRequest request, CaptureResult partialResult) {
-            mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
-        }
-
-        @Override
-        public void onCaptureCompleted(CameraDevice camera,
-                CaptureRequest request, TotalCaptureResult result) {
-            mProxy.invoke("onCaptureCompleted", camera, request, result);
-        }
-
-        @Override
-        public void onCaptureFailed(CameraDevice camera,
-                CaptureRequest request, CaptureFailure failure) {
-            mProxy.invoke("onCaptureFailed", camera, request, failure);
-        }
-
-        @Override
-        public void onCaptureSequenceCompleted(CameraDevice camera,
-                int sequenceId, long frameNumber) {
-            mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
-        }
-
-        @Override
-        public void onCaptureSequenceAborted(CameraDevice camera,
-                int sequenceId) {
-            mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
-        }
-
-        @Override
-        public void onCaptureBufferLost(CameraDevice camera,
-                CaptureRequest request, Surface target, long frameNumber) {
-            mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
-        }
-
-    }
-
     public static class SessionStateCallbackProxy
             extends CameraCaptureSession.StateCallback {
-        private final MethodNameInvoker<CameraCaptureSession.StateCallback> mProxy;
+        private final Executor mExecutor;
+        private final CameraCaptureSession.StateCallback mCallback;
 
-        public SessionStateCallbackProxy(
-                Dispatchable<CameraCaptureSession.StateCallback> dispatchTarget) {
-            dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
-            mProxy = new MethodNameInvoker<>(dispatchTarget,
-                    CameraCaptureSession.StateCallback.class);
+        public SessionStateCallbackProxy(Executor executor,
+                CameraCaptureSession.StateCallback callback) {
+            mExecutor = checkNotNull(executor, "executor must not be null");
+            mCallback = checkNotNull(callback, "callback must not be null");
         }
 
         @Override
         public void onConfigured(CameraCaptureSession session) {
-            mProxy.invoke("onConfigured", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onConfigured(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
 
         @Override
         public void onConfigureFailed(CameraCaptureSession session) {
-            mProxy.invoke("onConfigureFailed", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onConfigureFailed(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onReady(CameraCaptureSession session) {
-            mProxy.invoke("onReady", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onReady(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onActive(CameraCaptureSession session) {
-            mProxy.invoke("onActive", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onActive(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onCaptureQueueEmpty(CameraCaptureSession session) {
-            mProxy.invoke("onCaptureQueueEmpty", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onCaptureQueueEmpty(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onClosed(CameraCaptureSession session) {
-            mProxy.invoke("onClosed", session);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onClosed(session));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         @Override
         public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
-            mProxy.invoke("onSurfacePrepared", session, surface);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onSurfacePrepared(session, surface));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
     }
diff --git a/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 8b8bbc3..a4640c1 100644
--- a/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -20,20 +20,17 @@
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
-import android.hardware.camera2.dispatch.BroadcastDispatcher;
-import android.hardware.camera2.dispatch.DuckTypingDispatcher;
-import android.hardware.camera2.dispatch.HandlerDispatcher;
-import android.hardware.camera2.dispatch.InvokeDispatcher;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.TaskDrainer;
 import android.hardware.camera2.utils.TaskSingleDrainer;
+import android.os.Binder;
 import android.os.Handler;
 import android.util.Log;
 import android.view.Surface;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
 import static com.android.internal.util.Preconditions.*;
@@ -51,16 +48,16 @@
     private final Surface mInput;
     /**
      * User-specified state callback, used for outgoing events; calls to this object will be
-     * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
+     * automatically invoked via {@code mStateExecutor}.
      */
     private final CameraCaptureSession.StateCallback mStateCallback;
-    /** User-specified state handler used for outgoing state callback events */
-    private final Handler mStateHandler;
+    /** User-specified state executor used for outgoing state callback events */
+    private final Executor mStateExecutor;
 
     /** Internal camera device; used to translate calls into existing deprecated API */
     private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
-    /** Internal handler; used for all incoming events to preserve total order */
-    private final Handler mDeviceHandler;
+    /** Internal executor; used for all incoming events to preserve total order */
+    private final Executor mDeviceExecutor;
 
     /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
     private final TaskDrainer<Integer> mSequenceDrainer;
@@ -87,9 +84,9 @@
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
     CameraCaptureSessionImpl(int id, Surface input,
-            CameraCaptureSession.StateCallback callback, Handler stateHandler,
+            CameraCaptureSession.StateCallback callback, Executor stateExecutor,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
-            Handler deviceStateHandler, boolean configureSuccess) {
+            Executor deviceStateExecutor, boolean configureSuccess) {
         if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
@@ -98,10 +95,11 @@
         mIdString = String.format("Session %d: ", mId);
 
         mInput = input;
-        mStateHandler = checkHandler(stateHandler);
-        mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
+        mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");
+        mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);
 
-        mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
+        mDeviceExecutor = checkNotNull(deviceStateExecutor,
+                "deviceStateExecutor must not be null");
         mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
 
         /*
@@ -110,11 +108,11 @@
          * This ensures total ordering between CameraDevice.StateCallback and
          * CameraDeviceImpl.CaptureCallback events.
          */
-        mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+        mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),
                 /*name*/"seq");
-        mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+        mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),
                 /*name*/"idle");
-        mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+        mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),
                 /*name*/"abort");
 
         // CameraDevice should call configureOutputs and have it finish before constructing us
@@ -160,14 +158,7 @@
     @Override
     public int capture(CaptureRequest request, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
-        if (request == null) {
-            throw new IllegalArgumentException("request must not be null");
-        } else if (request.isReprocess() && !isReprocessable()) {
-            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
-                    "requests");
-        } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
-            throw new IllegalArgumentException("capture request was created for another session");
-        }
+        checkCaptureRequest(request);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -180,13 +171,94 @@
             }
 
             return addPendingSequence(mDeviceImpl.capture(request,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
+        }
+    }
+
+    @Override
+    public int captureSingleRequest(CaptureRequest request, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkCaptureRequest(request);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback +
+                        " executor " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.capture(request,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkCaptureRequest(CaptureRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("request must not be null");
+        } else if (request.isReprocess() && !isReprocessable()) {
+            throw new IllegalArgumentException("this capture session cannot handle reprocess " +
+                    "requests");
+        } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
+            throw new IllegalArgumentException("capture request was created for another session");
         }
     }
 
     @Override
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
+        checkCaptureRequests(requests);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            handler = checkHandler(handler, callback);
+
+            if (DEBUG) {
+                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+                Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
+                        ", callback " + callback + " handler " + handler);
+            }
+
+            return addPendingSequence(mDeviceImpl.captureBurst(requests,
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
+        }
+    }
+
+    @Override
+    public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkCaptureRequests(requests);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+                Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
+                        ", callback " + callback + " executor " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.captureBurst(requests,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkCaptureRequests(List<CaptureRequest> requests) {
         if (requests == null) {
             throw new IllegalArgumentException("Requests must not be null");
         } else if (requests.isEmpty()) {
@@ -205,30 +277,12 @@
             }
         }
 
-        synchronized (mDeviceImpl.mInterfaceLock) {
-            checkNotClosed();
-
-            handler = checkHandler(handler, callback);
-
-            if (DEBUG) {
-                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
-                Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) +
-                        ", callback " + callback + " handler " + handler);
-            }
-
-            return addPendingSequence(mDeviceImpl.captureBurst(requests,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
-        }
     }
 
     @Override
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
             Handler handler) throws CameraAccessException {
-        if (request == null) {
-            throw new IllegalArgumentException("request must not be null");
-        } else if (request.isReprocess()) {
-            throw new IllegalArgumentException("repeating reprocess requests are not supported");
-        }
+        checkRepeatingRequest(request);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -241,25 +295,47 @@
             }
 
             return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
+        }
+    }
+
+    @Override
+    public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkRepeatingRequest(request);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " +
+                        callback + " executor" + " " + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkRepeatingRequest(CaptureRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("request must not be null");
+        } else if (request.isReprocess()) {
+            throw new IllegalArgumentException("repeating reprocess requests are not supported");
         }
     }
 
     @Override
     public int setRepeatingBurst(List<CaptureRequest> requests,
             CaptureCallback callback, Handler handler) throws CameraAccessException {
-        if (requests == null) {
-            throw new IllegalArgumentException("requests must not be null");
-        } else if (requests.isEmpty()) {
-            throw new IllegalArgumentException("requests must have at least one element");
-        }
-
-        for (CaptureRequest r : requests) {
-            if (r.isReprocess()) {
-                throw new IllegalArgumentException("repeating reprocess burst requests are not " +
-                        "supported");
-            }
-        }
+        checkRepeatingRequests(requests);
 
         synchronized (mDeviceImpl.mInterfaceLock) {
             checkNotClosed();
@@ -274,7 +350,49 @@
             }
 
             return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
-                    createCaptureCallbackProxy(handler, callback), mDeviceHandler));
+                    createCaptureCallbackProxy(handler, callback), mDeviceExecutor));
+        }
+    }
+
+    @Override
+    public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback callback) throws CameraAccessException {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        } else if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+        checkRepeatingRequests(requests);
+
+        synchronized (mDeviceImpl.mInterfaceLock) {
+            checkNotClosed();
+
+            executor = CameraDeviceImpl.checkExecutor(executor, callback);
+
+            if (DEBUG) {
+                CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+                Log.v(TAG, mIdString + "setRepeatingBurst - requests " +
+                        Arrays.toString(requestArray) + ", callback " + callback +
+                        " executor" + "" + executor);
+            }
+
+            return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
+                    createCaptureCallbackProxyWithExecutor(executor, callback), mDeviceExecutor));
+        }
+    }
+
+    private void checkRepeatingRequests(List<CaptureRequest> requests) {
+        if (requests == null) {
+            throw new IllegalArgumentException("requests must not be null");
+        } else if (requests.isEmpty()) {
+            throw new IllegalArgumentException("requests must have at least one element");
+        }
+
+        for (CaptureRequest r : requests) {
+            if (r.isReprocess()) {
+                throw new IllegalArgumentException("repeating reprocess burst requests are not " +
+                        "supported");
+            }
         }
     }
 
@@ -446,119 +564,150 @@
     }
 
     /**
-     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}.
+     * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code executor}.
      */
-    private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) {
-        InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback);
-        HandlerDispatcher<StateCallback> handlerPassthrough =
-                new HandlerDispatcher<>(userCallbackSink, handler);
-
-        return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough);
+    private StateCallback createUserStateCallbackProxy(Executor executor, StateCallback callback) {
+        return new CallbackProxies.SessionStateCallbackProxy(executor, callback);
     }
 
     /**
      * Forward callbacks from
      * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback.
      *
-     * <p>In particular, all calls are automatically split to go both to our own
-     * internal callback, and to the user-specified callback (by transparently posting
-     * to the user-specified handler).</p>
-     *
      * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
      */
     @SuppressWarnings("deprecation")
     private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
             Handler handler, CaptureCallback callback) {
-        CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+        final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler(
+                handler) : null;
 
+        return createCaptureCallbackProxyWithExecutor(executor, callback);
+    }
+
+    private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxyWithExecutor(
+            Executor executor, CaptureCallback callback) {
+        return new CameraDeviceImpl.CaptureCallback() {
             @Override
             public void onCaptureStarted(CameraDevice camera,
                     CaptureRequest request, long timestamp, long frameNumber) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureStarted(
+                                    CameraCaptureSessionImpl.this, request, timestamp,
+                                    frameNumber));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCapturePartial(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureResult result) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCapturePartial(
+                                    CameraCaptureSessionImpl.this, request, result));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureProgressed(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureProgressed(
+                                    CameraCaptureSessionImpl.this, request, partialResult));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureCompleted(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureCompleted(
+                                    CameraCaptureSessionImpl.this, request, result));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureFailed(CameraDevice camera,
                     CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureFailed(
+                                    CameraCaptureSessionImpl.this, request, failure));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
 
             @Override
             public void onCaptureSequenceCompleted(CameraDevice camera,
                     int sequenceId, long frameNumber) {
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureSequenceCompleted(
+                                    CameraCaptureSessionImpl.this, sequenceId, frameNumber));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
                 finishPendingSequence(sequenceId);
             }
 
             @Override
             public void onCaptureSequenceAborted(CameraDevice camera,
                     int sequenceId) {
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureSequenceAborted(
+                                    CameraCaptureSessionImpl.this, sequenceId));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
                 finishPendingSequence(sequenceId);
             }
 
             @Override
             public void onCaptureBufferLost(CameraDevice camera,
                     CaptureRequest request, Surface target, long frameNumber) {
-                // Do nothing
+                if ((callback != null) && (executor != null)) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        executor.execute(() -> callback.onCaptureBufferLost(
+                                    CameraCaptureSessionImpl.this, request, target, frameNumber));
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
             }
-
         };
-
-        /*
-         * Split the calls from the device callback into local callback and the following chain:
-         * - replace the first CameraDevice arg with a CameraCaptureSession
-         * - duck type from device callback to session callback
-         * - then forward the call to a handler
-         * - then finally invoke the destination method on the session callback object
-         */
-        if (callback == null) {
-            // OK: API allows the user to not specify a callback, and the handler may
-            // also be null in that case. Collapse whole dispatch chain to only call the local
-            // callback
-            return localCallback;
-        }
-
-        InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink =
-                new InvokeDispatcher<>(localCallback);
-
-        InvokeDispatcher<CaptureCallback> userCallbackSink =
-                new InvokeDispatcher<>(callback);
-        HandlerDispatcher<CaptureCallback> handlerPassthrough =
-                new HandlerDispatcher<>(userCallbackSink, handler);
-        DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession
-                = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class);
-        ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl>
-                replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
-                        /*argumentIndex*/0, this);
-
-        BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster =
-                new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>(
-                    replaceDeviceWithSession,
-                    localSink);
-
-        return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster);
     }
 
     /**
      *
-     * Create an internal state callback, to be invoked on the mDeviceHandler
+     * Create an internal state callback, to be invoked on the mDeviceExecutor
      *
      * <p>It has a few behaviors:
      * <ul>
diff --git a/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 06c2c25..3494a7f 100644
--- a/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -33,6 +33,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -59,14 +60,14 @@
      * (e.g. no pending captures, no repeating requests, no flush).</p>
      */
     CameraConstrainedHighSpeedCaptureSessionImpl(int id,
-            CameraCaptureSession.StateCallback callback, Handler stateHandler,
+            CameraCaptureSession.StateCallback callback, Executor stateExecutor,
             android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
-            Handler deviceStateHandler, boolean configureSuccess,
+            Executor deviceStateExecutor, boolean configureSuccess,
             CameraCharacteristics characteristics) {
         mCharacteristics = characteristics;
         CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
         mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback,
-                stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
+                stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess);
     }
 
     @Override
@@ -192,6 +193,13 @@
     }
 
     @Override
+    public int captureSingleRequest(CaptureRequest request, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+                + " this method");
+    }
+
+    @Override
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
         if (!isConstrainedHighSpeedRequestList(requests)) {
@@ -203,6 +211,17 @@
     }
 
     @Override
+    public int captureBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        if (!isConstrainedHighSpeedRequestList(requests)) {
+            throw new IllegalArgumentException(
+                "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+                "a constrained high speed capture session");
+        }
+        return mSessionImpl.captureBurstRequests(requests, executor, listener);
+    }
+
+    @Override
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
         throw new UnsupportedOperationException("Constrained high speed session doesn't support"
@@ -210,6 +229,13 @@
     }
 
     @Override
+    public int setSingleRepeatingRequest(CaptureRequest request, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+                + " this method");
+    }
+
+    @Override
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
             Handler handler) throws CameraAccessException {
         if (!isConstrainedHighSpeedRequestList(requests)) {
@@ -221,6 +247,17 @@
     }
 
     @Override
+    public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor,
+            CaptureCallback listener) throws CameraAccessException {
+        if (!isConstrainedHighSpeedRequestList(requests)) {
+            throw new IllegalArgumentException(
+                "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+                "a constrained high speed capture session");
+        }
+        return mSessionImpl.setRepeatingBurstRequests(requests, executor, listener);
+    }
+
+    @Override
     public void stopRepeating() throws CameraAccessException {
         mSessionImpl.stopRepeating();
     }
diff --git a/android/hardware/camera2/impl/CameraDeviceImpl.java b/android/hardware/camera2/impl/CameraDeviceImpl.java
index cab9d70..d967fba 100644
--- a/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
+import android.annotation.NonNull;
 import android.hardware.ICameraService;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
@@ -35,6 +36,7 @@
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.hardware.camera2.utils.SurfaceUtils;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -47,6 +49,8 @@
 import android.util.SparseArray;
 import android.view.Surface;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -58,6 +62,8 @@
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executor;
+
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,7 +84,7 @@
 
     private final StateCallback mDeviceCallback;
     private volatile StateCallbackKK mSessionStateCallback;
-    private final Handler mDeviceHandler;
+    private final Executor mDeviceExecutor;
 
     private final AtomicBoolean mClosing = new AtomicBoolean();
     private boolean mInError = false;
@@ -234,14 +240,14 @@
         }
     };
 
-    public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
+    public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor,
                         CameraCharacteristics characteristics, int appTargetSdkVersion) {
-        if (cameraId == null || callback == null || handler == null || characteristics == null) {
+        if (cameraId == null || callback == null || executor == null || characteristics == null) {
             throw new IllegalArgumentException("Null argument given");
         }
         mCameraId = cameraId;
         mDeviceCallback = callback;
-        mDeviceHandler = handler;
+        mDeviceExecutor = executor;
         mCharacteristics = characteristics;
         mAppTargetSdkVersion = appTargetSdkVersion;
 
@@ -288,15 +294,15 @@
                 try {
                     remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
                 } catch (RemoteException e) {
-                    CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+                    CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
 
                     throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                             "The camera device has encountered a serious error");
                 }
             }
 
-            mDeviceHandler.post(mCallOnOpened);
-            mDeviceHandler.post(mCallOnUnconfigured);
+            mDeviceExecutor.execute(mCallOnOpened);
+            mDeviceExecutor.execute(mCallOnUnconfigured);
         }
     }
 
@@ -335,7 +341,7 @@
         final boolean isError = failureIsError;
         synchronized(mInterfaceLock) {
             mInError = true;
-            mDeviceHandler.post(new Runnable() {
+            mDeviceExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     if (isError) {
@@ -423,7 +429,7 @@
                 }
             }
 
-            mDeviceHandler.post(mCallOnBusy);
+            mDeviceExecutor.execute(mCallOnBusy);
             stopRepeating();
 
             try {
@@ -482,10 +488,10 @@
                 throw e;
             } finally {
                 if (success && outputs.size() > 0) {
-                    mDeviceHandler.post(mCallOnIdle);
+                    mDeviceExecutor.execute(mCallOnIdle);
                 } else {
                     // Always return to the 'unconfigured' state if we didn't hit a fatal error
-                    mDeviceHandler.post(mCallOnUnconfigured);
+                    mDeviceExecutor.execute(mCallOnUnconfigured);
                 }
             }
         }
@@ -501,8 +507,9 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(null, outConfigurations, callback, handler,
-                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+        createCaptureSessionInternal(null, outConfigurations, callback,
+                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+                /*sessionParams*/ null);
     }
 
     @Override
@@ -517,7 +524,7 @@
         // OutputConfiguration objects are immutable, but need to have our own array
         List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
 
-        createCaptureSessionInternal(null, currentOutputs, callback, handler,
+        createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler),
                 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null);
     }
 
@@ -537,8 +544,9 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
-                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
+        createCaptureSessionInternal(inputConfig, outConfigurations, callback,
+                checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
+                /*sessionParams*/ null);
     }
 
     @Override
@@ -566,8 +574,8 @@
             currentOutputs.add(new OutputConfiguration(output));
         }
         createCaptureSessionInternal(inputConfig, currentOutputs,
-                callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,
-                /*sessionParams*/ null);
+                callback, checkAndWrapHandler(handler),
+                /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null);
     }
 
     @Override
@@ -582,7 +590,8 @@
         for (Surface surface : outputs) {
             outConfigurations.add(new OutputConfiguration(surface));
         }
-        createCaptureSessionInternal(null, outConfigurations, callback, handler,
+        createCaptureSessionInternal(null, outConfigurations, callback,
+                checkAndWrapHandler(handler),
                 /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE,
                 /*sessionParams*/ null);
     }
@@ -597,8 +606,8 @@
         for (OutputConfiguration output : outputs) {
             currentOutputs.add(new OutputConfiguration(output));
         }
-        createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode,
-                /*sessionParams*/ null);
+        createCaptureSessionInternal(inputConfig, currentOutputs, callback,
+                checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null);
     }
 
     @Override
@@ -612,14 +621,17 @@
         if (outputConfigs == null) {
             throw new IllegalArgumentException("Invalid output configurations");
         }
+        if (config.getExecutor() == null) {
+            throw new IllegalArgumentException("Invalid executor");
+        }
         createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs,
-                config.getStateCallback(), config.getHandler(), config.getSessionType(),
+                config.getStateCallback(), config.getExecutor(), config.getSessionType(),
                 config.getSessionParameters());
     }
 
     private void createCaptureSessionInternal(InputConfiguration inputConfig,
             List<OutputConfiguration> outputConfigurations,
-            CameraCaptureSession.StateCallback callback, Handler handler,
+            CameraCaptureSession.StateCallback callback, Executor executor,
             int operatingMode, CaptureRequest sessionParams) throws CameraAccessException {
         synchronized(mInterfaceLock) {
             if (DEBUG) {
@@ -673,12 +685,11 @@
                 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);
 
                 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
-                        callback, handler, this, mDeviceHandler, configureSuccess,
+                        callback, executor, this, mDeviceExecutor, configureSuccess,
                         mCharacteristics);
             } else {
                 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
-                        callback, handler, this, mDeviceHandler,
-                        configureSuccess);
+                        callback, executor, this, mDeviceExecutor, configureSuccess);
             }
 
             // TODO: wait until current session closes, then create the new session
@@ -893,22 +904,22 @@
         }
     }
 
-    public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
+    public int capture(CaptureRequest request, CaptureCallback callback, Executor executor)
             throws CameraAccessException {
         if (DEBUG) {
             Log.d(TAG, "calling capture");
         }
         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
         requestList.add(request);
-        return submitCaptureRequest(requestList, callback, handler, /*streaming*/false);
+        return submitCaptureRequest(requestList, callback, executor, /*streaming*/false);
     }
 
     public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
-            Handler handler) throws CameraAccessException {
+            Executor executor) throws CameraAccessException {
         if (requests == null || requests.isEmpty()) {
             throw new IllegalArgumentException("At least one request must be given");
         }
-        return submitCaptureRequest(requests, callback, handler, /*streaming*/false);
+        return submitCaptureRequest(requests, callback, executor, /*streaming*/false);
     }
 
     /**
@@ -963,7 +974,12 @@
                         }
                     }
                 };
-                holder.getHandler().post(resultDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(resultDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             } else {
                 Log.w(TAG, String.format(
                         "did not register callback to request %d",
@@ -982,11 +998,11 @@
     }
 
     private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
-            Handler handler, boolean repeating) throws CameraAccessException {
+            Executor executor, boolean repeating) throws CameraAccessException {
 
-        // Need a valid handler, or current thread needs to have a looper, if
+        // Need a valid executor, or current thread needs to have a looper, if
         // callback is valid
-        handler = checkHandler(handler, callback);
+        executor = checkExecutor(executor, callback);
 
         // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
         // the surface isn't a physical stream surface for reprocessing request
@@ -1001,19 +1017,17 @@
                     throw new IllegalArgumentException("Null Surface targets are not allowed");
                 }
 
-                if (!request.isReprocess()) {
-                    continue;
-                }
                 for (int i = 0; i < mConfiguredOutputs.size(); i++) {
                     OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
                     if (configuration.isForPhysicalCamera()
                             && configuration.getSurfaces().contains(surface)) {
-                        throw new IllegalArgumentException(
-                                "Reprocess request on physical stream is not allowed");
+                        if (request.isReprocess()) {
+                            throw new IllegalArgumentException(
+                                    "Reprocess request on physical stream is not allowed");
+                        }
                     }
                 }
             }
-
         }
 
         synchronized(mInterfaceLock) {
@@ -1042,7 +1056,7 @@
             if (callback != null) {
                 mCaptureCallbackMap.put(requestInfo.getRequestId(),
                         new CaptureCallbackHolder(
-                            callback, requestList, handler, repeating, mNextSessionId - 1));
+                            callback, requestList, executor, repeating, mNextSessionId - 1));
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null");
@@ -1061,7 +1075,7 @@
             }
 
             if (mIdle) {
-                mDeviceHandler.post(mCallOnActive);
+                mDeviceExecutor.execute(mCallOnActive);
             }
             mIdle = false;
 
@@ -1070,18 +1084,18 @@
     }
 
     public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback,
-            Handler handler) throws CameraAccessException {
+            Executor executor) throws CameraAccessException {
         List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
         requestList.add(request);
-        return submitCaptureRequest(requestList, callback, handler, /*streaming*/true);
+        return submitCaptureRequest(requestList, callback, executor, /*streaming*/true);
     }
 
     public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback,
-            Handler handler) throws CameraAccessException {
+            Executor executor) throws CameraAccessException {
         if (requests == null || requests.isEmpty()) {
             throw new IllegalArgumentException("At least one request must be given");
         }
-        return submitCaptureRequest(requests, callback, handler, /*streaming*/true);
+        return submitCaptureRequest(requests, callback, executor, /*streaming*/true);
     }
 
     public void stopRepeating() throws CameraAccessException {
@@ -1126,12 +1140,12 @@
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
 
-            mDeviceHandler.post(mCallOnBusy);
+            mDeviceExecutor.execute(mCallOnBusy);
 
             // If already idle, just do a busy->idle transition immediately, don't actually
             // flush.
             if (mIdle) {
-                mDeviceHandler.post(mCallOnIdle);
+                mDeviceExecutor.execute(mCallOnIdle);
                 return;
             }
 
@@ -1159,7 +1173,7 @@
             // either a normal close where the remote device is valid
             // or a close after a startup error (no remote device but in error state)
             if (mRemoteDevice != null || mInError) {
-                mDeviceHandler.post(mCallOnClosed);
+                mDeviceExecutor.execute(mCallOnClosed);
             }
 
             mRemoteDevice = null;
@@ -1356,7 +1370,7 @@
         private final boolean mRepeating;
         private final CaptureCallback mCallback;
         private final List<CaptureRequest> mRequestList;
-        private final Handler mHandler;
+        private final Executor mExecutor;
         private final int mSessionId;
         /**
          * <p>Determine if the callback holder is for a constrained high speed request list that
@@ -1368,13 +1382,13 @@
         private final boolean mHasBatchedOutputs;
 
         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
-                Handler handler, boolean repeating, int sessionId) {
-            if (callback == null || handler == null) {
+                Executor executor, boolean repeating, int sessionId) {
+            if (callback == null || executor == null) {
                 throw new UnsupportedOperationException(
                     "Must have a valid handler and a valid callback");
             }
             mRepeating = repeating;
-            mHandler = handler;
+            mExecutor = executor;
             mRequestList = new ArrayList<CaptureRequest>(requestList);
             mCallback = callback;
             mSessionId = sessionId;
@@ -1427,8 +1441,8 @@
             return getRequest(0);
         }
 
-        public Handler getHandler() {
-            return mHandler;
+        public Executor getExecutor() {
+            return mExecutor;
         }
 
         public int getSessionId() {
@@ -1812,7 +1826,12 @@
                         }
                     }
                 };
-                holder.getHandler().post(resultDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(resultDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
     }
@@ -1840,7 +1859,12 @@
 
                 switch (errorCode) {
                     case ERROR_CAMERA_DISCONNECTED:
-                        CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected);
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
                         break;
                     case ERROR_CAMERA_REQUEST:
                     case ERROR_CAMERA_RESULT:
@@ -1862,8 +1886,13 @@
 
         private void scheduleNotifyError(int code) {
             mInError = true;
-            CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
-                    CameraDeviceCallbacks::notifyError, this, code));
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable(
+                            CameraDeviceCallbacks::notifyError, this, code));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         }
 
         private void notifyError(int code) {
@@ -1902,7 +1931,12 @@
                 if (mRemoteDevice == null) return; // Camera already closed
 
                 if (!CameraDeviceImpl.this.mIdle) {
-                    CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
                 }
                 CameraDeviceImpl.this.mIdle = true;
             }
@@ -1931,42 +1965,48 @@
                 if (isClosed()) return;
 
                 // Dispatch capture start notice
-                holder.getHandler().post(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            if (!CameraDeviceImpl.this.isClosed()) {
-                                final int subsequenceId = resultExtras.getSubsequenceId();
-                                final CaptureRequest request = holder.getRequest(subsequenceId);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                if (!CameraDeviceImpl.this.isClosed()) {
+                                    final int subsequenceId = resultExtras.getSubsequenceId();
+                                    final CaptureRequest request = holder.getRequest(subsequenceId);
 
-                                if (holder.hasBatchedOutputs()) {
-                                    // Send derived onCaptureStarted for requests within the batch
-                                    final Range<Integer> fpsRange =
-                                        request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
-                                    for (int i = 0; i < holder.getRequestCount(); i++) {
+                                    if (holder.hasBatchedOutputs()) {
+                                        // Send derived onCaptureStarted for requests within the
+                                        // batch
+                                        final Range<Integer> fpsRange =
+                                            request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+                                        for (int i = 0; i < holder.getRequestCount(); i++) {
+                                            holder.getCallback().onCaptureStarted(
+                                                CameraDeviceImpl.this,
+                                                holder.getRequest(i),
+                                                timestamp - (subsequenceId - i) *
+                                                NANO_PER_SECOND/fpsRange.getUpper(),
+                                                frameNumber - (subsequenceId - i));
+                                        }
+                                    } else {
                                         holder.getCallback().onCaptureStarted(
                                             CameraDeviceImpl.this,
-                                            holder.getRequest(i),
-                                            timestamp - (subsequenceId - i) *
-                                            NANO_PER_SECOND/fpsRange.getUpper(),
-                                            frameNumber - (subsequenceId - i));
+                                            holder.getRequest(resultExtras.getSubsequenceId()),
+                                            timestamp, frameNumber);
                                     }
-                                } else {
-                                    holder.getCallback().onCaptureStarted(
-                                        CameraDeviceImpl.this,
-                                        holder.getRequest(resultExtras.getSubsequenceId()),
-                                        timestamp, frameNumber);
                                 }
                             }
-                        }
-                    });
-
+                        });
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
         }
 
         @Override
         public void onResultReceived(CameraMetadataNative result,
-                CaptureResultExtras resultExtras) throws RemoteException {
+                CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
+                throws RemoteException {
 
             int requestId = resultExtras.getRequestId();
             long frameNumber = resultExtras.getFrameNumber();
@@ -2073,7 +2113,8 @@
                             request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
                     final int subsequenceId = resultExtras.getSubsequenceId();
                     final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
-                            request, resultExtras, partialResults, holder.getSessionId());
+                            request, resultExtras, partialResults, holder.getSessionId(),
+                            physicalResults);
                     // Final capture result
                     resultDispatch = new Runnable() {
                         @Override
@@ -2088,9 +2129,11 @@
                                                 NANO_PER_SECOND/fpsRange.getUpper());
                                         CameraMetadataNative resultLocal =
                                                 new CameraMetadataNative(resultCopy);
+                                        // No logical multi-camera support for batched output mode.
                                         TotalCaptureResult resultInBatch = new TotalCaptureResult(
                                             resultLocal, holder.getRequest(i), resultExtras,
-                                            partialResults, holder.getSessionId());
+                                            partialResults, holder.getSessionId(),
+                                            new PhysicalCaptureResultInfo[0]);
 
                                         holder.getCallback().onCaptureCompleted(
                                             CameraDeviceImpl.this,
@@ -2109,7 +2152,12 @@
                     finalResult = resultAsCapture;
                 }
 
-                holder.getHandler().post(resultDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(resultDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
 
                 // Collect the partials for a total result; or mark the frame as totally completed
                 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
@@ -2205,7 +2253,12 @@
                         }
                     };
                     // Dispatch the failure callback
-                    holder.getHandler().post(failureDispatch);
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        holder.getExecutor().execute(failureDispatch);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
                 }
             } else {
                 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
@@ -2245,7 +2298,12 @@
                 checkAndFireSequenceComplete();
 
                 // Dispatch the failure callback
-                holder.getHandler().post(failureDispatch);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    holder.getExecutor().execute(failureDispatch);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
 
         }
@@ -2253,6 +2311,62 @@
     } // public class CameraDeviceCallbacks
 
     /**
+     * A camera specific adapter {@link Executor} that posts all executed tasks onto the given
+     * {@link Handler}.
+     *
+     * @hide
+     */
+    private static class CameraHandlerExecutor implements Executor {
+        private final Handler mHandler;
+
+        public CameraHandlerExecutor(@NonNull Handler handler) {
+            mHandler = Preconditions.checkNotNull(handler);
+        }
+
+        @Override
+        public void execute(Runnable command) {
+            // Return value of 'post()' will be ignored in order to keep the
+            // same camera behavior. For further details see b/74605221 .
+            mHandler.post(command);
+        }
+    }
+
+    /**
+     * Default executor management.
+     *
+     * <p>
+     * If executor is null, get the current thread's
+     * Looper to create a Executor with. If no looper exists, throw
+     * {@code IllegalArgumentException}.
+     * </p>
+     */
+    static Executor checkExecutor(Executor executor) {
+        return (executor == null) ? checkAndWrapHandler(null) : executor;
+    }
+
+    /**
+     * Default executor management.
+     *
+     * <p>If the callback isn't null, check the executor, otherwise pass it through.</p>
+     */
+    public static <T> Executor checkExecutor(Executor executor, T callback) {
+        return (callback != null) ? checkExecutor(executor) : executor;
+    }
+
+    /**
+     * Wrap Handler in Executor.
+     *
+     * <p>
+     * If handler is null, get the current thread's
+     * Looper to create a Executor with. If no looper exists, throw
+     * {@code IllegalArgumentException}.
+     * </p>
+     */
+    public static Executor checkAndWrapHandler(Handler handler) {
+        return new CameraHandlerExecutor(checkHandler(handler));
+    }
+
+    /**
      * Default handler management.
      *
      * <p>
@@ -2326,6 +2440,11 @@
                 }
             }
         };
-        CameraDeviceImpl.this.mDeviceHandler.post(r);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            CameraDeviceImpl.this.mDeviceExecutor.execute(r);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 }
diff --git a/android/hardware/camera2/impl/CameraMetadataNative.java b/android/hardware/camera2/impl/CameraMetadataNative.java
index ebe2fa1..e4b1339 100644
--- a/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -22,12 +22,12 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.marshal.Marshaler;
 import android.hardware.camera2.marshal.MarshalQueryable;
 import android.hardware.camera2.marshal.MarshalRegistry;
+import android.hardware.camera2.marshal.Marshaler;
 import android.hardware.camera2.marshal.impl.MarshalQueryableArray;
-import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
 import android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern;
+import android.hardware.camera2.marshal.impl.MarshalQueryableBoolean;
 import android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform;
 import android.hardware.camera2.marshal.impl.MarshalQueryableEnum;
 import android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration;
@@ -48,6 +48,7 @@
 import android.hardware.camera2.params.Face;
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.OisSample;
 import android.hardware.camera2.params.ReprocessFormatsMap;
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
@@ -56,8 +57,8 @@
 import android.hardware.camera2.utils.TypeReference;
 import android.location.Location;
 import android.location.LocationManager;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.ServiceSpecificException;
 import android.util.Log;
 import android.util.Size;
@@ -614,6 +615,15 @@
                         return (T) metadata.getLensShadingMap();
                     }
                 });
+        sGetCommandMap.put(
+                CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getOisSamples();
+                    }
+                });
     }
 
     private int[] getAvailableFormats() {
@@ -962,6 +972,50 @@
         return tc;
     }
 
+    private OisSample[] getOisSamples() {
+        long[] timestamps = getBase(CaptureResult.STATISTICS_OIS_TIMESTAMPS);
+        float[] xShifts = getBase(CaptureResult.STATISTICS_OIS_X_SHIFTS);
+        float[] yShifts = getBase(CaptureResult.STATISTICS_OIS_Y_SHIFTS);
+
+        if (timestamps == null) {
+            if (xShifts != null) {
+                throw new AssertionError("timestamps is null but xShifts is not");
+            }
+
+            if (yShifts != null) {
+                throw new AssertionError("timestamps is null but yShifts is not");
+            }
+
+            return null;
+        }
+
+        if (xShifts == null) {
+            throw new AssertionError("timestamps is not null but xShifts is");
+        }
+
+        if (yShifts == null) {
+            throw new AssertionError("timestamps is not null but yShifts is");
+        }
+
+        if (xShifts.length != timestamps.length) {
+            throw new AssertionError(String.format(
+                    "timestamps has %d entries but xShifts has %d", timestamps.length,
+                    xShifts.length));
+        }
+
+        if (yShifts.length != timestamps.length) {
+            throw new AssertionError(String.format(
+                    "timestamps has %d entries but yShifts has %d", timestamps.length,
+                    yShifts.length));
+        }
+
+        OisSample[] samples = new OisSample[timestamps.length];
+        for (int i = 0; i < timestamps.length; i++) {
+            samples[i] = new OisSample(timestamps[i], xShifts[i], yShifts[i]);
+        }
+        return samples;
+    }
+
     private <T> void setBase(CameraCharacteristics.Key<T> key, T value) {
         setBase(key.getNativeKey(), value);
     }
diff --git a/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java b/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
new file mode 100644
index 0000000..30eaf13
--- /dev/null
+++ b/android/hardware/camera2/impl/PhysicalCaptureResultInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class PhysicalCaptureResultInfo implements Parcelable {
+    private String cameraId;
+    private CameraMetadataNative cameraMetadata;
+
+    public static final Parcelable.Creator<PhysicalCaptureResultInfo> CREATOR =
+            new Parcelable.Creator<PhysicalCaptureResultInfo>() {
+        @Override
+        public PhysicalCaptureResultInfo createFromParcel(Parcel in) {
+            return new PhysicalCaptureResultInfo(in);
+        }
+
+        @Override
+        public PhysicalCaptureResultInfo[] newArray(int size) {
+            return new PhysicalCaptureResultInfo[size];
+        }
+    };
+
+    private PhysicalCaptureResultInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public PhysicalCaptureResultInfo(String cameraId, CameraMetadataNative cameraMetadata) {
+        this.cameraId = cameraId;
+        this.cameraMetadata = cameraMetadata;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(cameraId);
+        cameraMetadata.writeToParcel(dest, flags);
+    }
+
+    public void readFromParcel(Parcel in) {
+        cameraId = in.readString();
+        cameraMetadata = new CameraMetadataNative();
+        cameraMetadata.readFromParcel(in);
+    }
+
+    public String getCameraId() {
+        return cameraId;
+    }
+
+    public CameraMetadataNative getCameraMetadata() {
+        return cameraMetadata;
+    }
+}
diff --git a/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index eccab75..bc7b126 100644
--- a/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.os.ConditionVariable;
@@ -249,7 +250,8 @@
 
         @Override
         public void onResultReceived(final CameraMetadataNative result,
-                final CaptureResultExtras resultExtras) {
+                final CaptureResultExtras resultExtras,
+                PhysicalCaptureResultInfo physicalResults[]) {
             Object[] resultArray = new Object[] { result, resultExtras };
             Message msg = getHandler().obtainMessage(RESULT_RECEIVED,
                     /*obj*/ resultArray);
@@ -320,7 +322,8 @@
                             Object[] resultArray = (Object[]) msg.obj;
                             CameraMetadataNative result = (CameraMetadataNative) resultArray[0];
                             CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1];
-                            mCallbacks.onResultReceived(result, resultExtras);
+                            mCallbacks.onResultReceived(result, resultExtras,
+                                    new PhysicalCaptureResultInfo[0]);
                             break;
                         }
                         case PREPARED: {
diff --git a/android/hardware/camera2/legacy/LegacyCameraDevice.java b/android/hardware/camera2/legacy/LegacyCameraDevice.java
index cb59fd1..71a361b 100644
--- a/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -23,6 +23,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.ArrayUtils;
@@ -253,7 +254,8 @@
                                 holder.getRequestId());
                     }
                     try {
-                        mDeviceCallbacks.onResultReceived(result, extras);
+                        mDeviceCallbacks.onResultReceived(result, extras,
+                                new PhysicalCaptureResultInfo[0]);
                     } catch (RemoteException e) {
                         throw new IllegalStateException(
                                 "Received remote exception during onCameraError callback: ", e);
@@ -728,7 +730,7 @@
         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
     }
 
-    static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
+    public static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
         checkNotNull(surface);
         try {
             return nativeGetSurfaceId(surface);
diff --git a/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/android/hardware/camera2/legacy/LegacyExceptionUtils.java
index 93d6001..55130c8 100644
--- a/android/hardware/camera2/legacy/LegacyExceptionUtils.java
+++ b/android/hardware/camera2/legacy/LegacyExceptionUtils.java
@@ -62,14 +62,14 @@
      * exceptions.</p>
      *
      * @param errorFlag error to throw as an exception.
-     * @throws {@link BufferQueueAbandonedException} for -ENODEV.
+     * @throws {@link BufferQueueAbandonedException} for BAD_VALUE.
      * @throws {@link UnsupportedOperationException} for an unknown negative error code.
      * @return {@code errorFlag} if the value was non-negative, throws otherwise.
      */
     public static int throwOnError(int errorFlag) throws BufferQueueAbandonedException {
         if (errorFlag == NO_ERROR) {
             return NO_ERROR;
-        } else if (errorFlag == -ENODEV) {
+        } else if (errorFlag == BAD_VALUE) {
             throw new BufferQueueAbandonedException();
         }
 
diff --git a/android/hardware/camera2/params/OisSample.java b/android/hardware/camera2/params/OisSample.java
new file mode 100644
index 0000000..7ebaae3
--- /dev/null
+++ b/android/hardware/camera2/params/OisSample.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Immutable class to store an
+ * {@link CaptureResult#STATISTICS_OIS_SAMPLES optical image stabilization sample}.
+ */
+public final class OisSample {
+    /**
+     * Create a new {@link OisSample}.
+     *
+     * <p>{@link OisSample} contains the timestamp and the amount of shifts in x and y direction,
+     * in pixels, of the OIS sample.
+     *
+     * <p>A positive value for a shift in x direction is a shift from left to right in active array
+     * coordinate system. For example, if the optical center is {@code (1000, 500)} in active array
+     * coordinates, a shift of {@code (3, 0)} puts the new optical center at {@code (1003, 500)}.
+     * </p>
+     *
+     * <p>A positive value for a shift in y direction is a shift from top to bottom in active array
+     * coordinate system. For example, if the optical center is {@code (1000, 500)} in active array
+     * coordinates, a shift of {@code (0, 5)} puts the new optical center at {@code (1000, 505)}.
+     * </p>
+     *
+     * <p>xShift and yShift must be finite; NaN and infinity is not allowed.</p>
+     *
+     * @param timestamp timestamp of the OIS sample.
+     * @param xShift shift of the OIS sample in x direction.
+     * @param yShift shift of the OIS sample in y direction.
+     *
+     * @throws IllegalArgumentException if xShift or yShift is not finite
+     */
+    public OisSample(final long timestamp, final float xShift, final float yShift) {
+        mTimestampNs = timestamp;
+        mXShift = Preconditions.checkArgumentFinite(xShift, "xShift must be finite");
+        mYShift = Preconditions.checkArgumentFinite(yShift, "yShift must be finite");
+    }
+
+    /**
+     * Get the timestamp in nanoseconds.
+     *
+     *<p>The timestamps are in the same timebase as and comparable to
+     *{@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
+     *
+     * @return a long value (guaranteed to be finite)
+     */
+    public long getTimestamp() {
+        return mTimestampNs;
+    }
+
+    /**
+     * Get the shift in x direction.
+     *
+     * @return a floating point value (guaranteed to be finite)
+     */
+    public float getXshift() {
+        return mXShift;
+    }
+
+    /**
+     * Get the shift in y direction.
+     *
+     * @return a floating point value (guaranteed to be finite)
+     */
+    public float getYshift() {
+        return mYShift;
+    }
+
+    /**
+     * Check if this {@link OisSample} is equal to another {@link OisSample}.
+     *
+     * <p>Two samples are only equal if and only if each of the OIS information is equal.</p>
+     *
+     * @return {@code true} if the objects were equal, {@code false} otherwise
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == null) {
+            return false;
+        } else if (this == obj) {
+            return true;
+        } else if (obj instanceof OisSample) {
+            final OisSample other = (OisSample) obj;
+            return mTimestampNs == other.mTimestampNs
+                    && mXShift == other.mXShift
+                    && mYShift == other.mYShift;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        int timestampHash = HashCodeHelpers.hashCode(mTimestampNs);
+        return HashCodeHelpers.hashCode(mXShift, mYShift, timestampHash);
+    }
+
+    /**
+     * Return the OisSample as a string representation.
+     *
+     * <p> {@code "OisSample{timestamp:%l, shift_x:%f, shift_y:%f}"} represents the OIS sample's
+     * timestamp, shift in x direction, and shift in y direction.</p>
+     *
+     * @return string representation of {@link OisSample}
+     */
+    @Override
+    public String toString() {
+        return String.format("OisSample{timestamp:%d, shift_x:%f, shift_y:%f}", mTimestampNs,
+                mXShift, mYShift);
+    }
+
+    private final long mTimestampNs;
+    private final float mXShift;
+    private final float mYShift;
+}
diff --git a/android/hardware/camera2/params/OutputConfiguration.java b/android/hardware/camera2/params/OutputConfiguration.java
index f47cd66..a040a09 100644
--- a/android/hardware/camera2/params/OutputConfiguration.java
+++ b/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,11 +82,9 @@
  *
  * </ul>
  *
- * <p>Please note that surface sharing is currently only enabled for outputs that use the
- * {@link ImageFormat#PRIVATE} format. This includes surface sources like
- * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
- * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
- * the aforementioned format.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
+ * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
+ * format may be used.</p>
  *
  * @see CameraDevice#createCaptureSessionByOutputConfigurations
  *
@@ -368,7 +366,7 @@
      * desirable for the camera application to request streams from individual physical cameras.
      * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
      *
-     * <p>The valid physical camera id can be queried by {@link
+     * <p>The valid physical camera ids can be queried by {@link
      * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
      * </p>
      *
@@ -576,7 +574,7 @@
      *
      * @see #enableSurfaceSharing
      */
-    public static int getMaxSharedSurfaceCount() {
+    public int getMaxSharedSurfaceCount() {
         return MAX_SURFACES_COUNT;
     }
 
diff --git a/android/hardware/camera2/params/SessionConfiguration.java b/android/hardware/camera2/params/SessionConfiguration.java
index a79a6c1..7bdb4a2 100644
--- a/android/hardware/camera2/params/SessionConfiguration.java
+++ b/android/hardware/camera2/params/SessionConfiguration.java
@@ -17,10 +17,10 @@
 
 package android.hardware.camera2.params;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.IntDef;
-import android.os.Handler;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -31,6 +31,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -78,7 +79,7 @@
     private List<OutputConfiguration> mOutputConfigurations;
     private CameraCaptureSession.StateCallback mStateCallback;
     private int mSessionType;
-    private Handler mHandler = null;
+    private Executor mExecutor = null;
     private InputConfiguration mInputConfig = null;
     private CaptureRequest mSessionParameters = null;
 
@@ -87,10 +88,9 @@
      *
      * @param sessionType The session type.
      * @param outputs A list of output configurations for the capture session.
+     * @param executor The executor which should be used to invoke the callback. In general it is
+     *                 recommended that camera operations are not done on the main (UI) thread.
      * @param cb A state callback interface implementation.
-     * @param handler The handler on which the callback will be invoked. If it is
-     *                set to null, the callback will be invoked on the current thread's
-     *                {@link android.os.Looper looper}.
      *
      * @see #SESSION_REGULAR
      * @see #SESSION_HIGH_SPEED
@@ -101,11 +101,12 @@
      */
     public SessionConfiguration(@SessionMode int sessionType,
             @NonNull List<OutputConfiguration> outputs,
-            @NonNull CameraCaptureSession.StateCallback cb, @Nullable Handler handler) {
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull CameraCaptureSession.StateCallback cb) {
         mSessionType = sessionType;
         mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
         mStateCallback = cb;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     /**
@@ -136,14 +137,12 @@
     }
 
     /**
-     * Retrieve the {@link CameraCaptureSession.StateCallback} for the capture session.
+     * Retrieve the {@link java.util.concurrent.Executor} for the capture session.
      *
-     * @return The handler on which the callback will be invoked. If it is
-     *         set to null, the callback will be invoked on the current thread's
-     *         {@link android.os.Looper looper}.
+     * @return The Executor on which the callback will be invoked.
      */
-    public Handler getHandler() {
-        return mHandler;
+    public Executor getExecutor() {
+        return mExecutor;
     }
 
     /**
diff --git a/android/hardware/camera2/utils/SurfaceUtils.java b/android/hardware/camera2/utils/SurfaceUtils.java
index e1e1c4f..9247844 100644
--- a/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/android/hardware/camera2/utils/SurfaceUtils.java
@@ -56,6 +56,20 @@
     }
 
     /**
+     * Get the native object id of a surface.
+     *
+     * @param surface The surface to be checked.
+     * @return the native object id of the surface, 0 if surface is not backed by a native object.
+     */
+    public static long getSurfaceId(Surface surface) {
+        try {
+            return LegacyCameraDevice.getSurfaceId(surface);
+        } catch (BufferQueueAbandonedException e) {
+            return 0;
+        }
+    }
+
+    /**
      * Get the Surface size.
      *
      * @param surface The surface to be queried for size.
diff --git a/android/hardware/camera2/utils/TaskDrainer.java b/android/hardware/camera2/utils/TaskDrainer.java
index ed30ff3..e71f26a 100644
--- a/android/hardware/camera2/utils/TaskDrainer.java
+++ b/android/hardware/camera2/utils/TaskDrainer.java
@@ -15,11 +15,11 @@
  */
 package android.hardware.camera2.utils;
 
-import android.os.Handler;
 import android.util.Log;
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 import static com.android.internal.util.Preconditions.*;
 
@@ -55,7 +55,7 @@
     private static final String TAG = "TaskDrainer";
     private final boolean DEBUG = false;
 
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final DrainListener mListener;
     private final String mName;
 
@@ -73,28 +73,27 @@
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      */
-    public TaskDrainer(Handler handler, DrainListener listener) {
-        mHandler = checkNotNull(handler, "handler must not be null");
+    public TaskDrainer(Executor executor, DrainListener listener) {
+        mExecutor = checkNotNull(executor, "executor must not be null");
         mListener = checkNotNull(listener, "listener must not be null");
         mName = null;
     }
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      * @param name an optional name used for debug logging
      */
-    public TaskDrainer(Handler handler, DrainListener listener, String name) {
-        // XX: Probably don't need a handler at all here
-        mHandler = checkNotNull(handler, "handler must not be null");
+    public TaskDrainer(Executor executor, DrainListener listener, String name) {
+        mExecutor = checkNotNull(executor, "executor must not be null");
         mListener = checkNotNull(listener, "listener must not be null");
         mName = name;
     }
@@ -200,15 +199,12 @@
     }
 
     private void postDrained() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
+        mExecutor.execute(() -> {
                 if (DEBUG) {
                     Log.v(TAG + "[" + mName + "]", "onDrained");
                 }
 
                 mListener.onDrained();
-            }
         });
     }
 }
diff --git a/android/hardware/camera2/utils/TaskSingleDrainer.java b/android/hardware/camera2/utils/TaskSingleDrainer.java
index f6272c9..9615450 100644
--- a/android/hardware/camera2/utils/TaskSingleDrainer.java
+++ b/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -16,7 +16,8 @@
 package android.hardware.camera2.utils;
 
 import android.hardware.camera2.utils.TaskDrainer.DrainListener;
-import android.os.Handler;
+
+import java.util.concurrent.Executor;
 
 /**
  * Keep track of a single concurrent task starting and finishing;
@@ -38,25 +39,25 @@
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      */
-    public TaskSingleDrainer(Handler handler, DrainListener listener) {
-        mTaskDrainer = new TaskDrainer<>(handler, listener);
+    public TaskSingleDrainer(Executor executor, DrainListener listener) {
+        mTaskDrainer = new TaskDrainer<>(executor, listener);
     }
 
     /**
      * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
-     * via the {@code handler}.
+     * via the {@code executor}.
      *
-     * @param handler a non-{@code null} handler to use to post runnables to
+     * @param executor a non-{@code null} executor to use for listener execution
      * @param listener a non-{@code null} listener where {@code onDrained} will be called
      * @param name an optional name used for debug logging
      */
-    public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
-        mTaskDrainer = new TaskDrainer<>(handler, listener, name);
+    public TaskSingleDrainer(Executor executor, DrainListener listener, String name) {
+        mTaskDrainer = new TaskDrainer<>(executor, listener, name);
     }
 
     /**
diff --git a/android/hardware/display/AmbientBrightnessDayStats.java b/android/hardware/display/AmbientBrightnessDayStats.java
new file mode 100644
index 0000000..1aa2557
--- /dev/null
+++ b/android/hardware/display/AmbientBrightnessDayStats.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+/**
+ * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
+ * {@see DisplayManager.getAmbientBrightnessStats()}
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class AmbientBrightnessDayStats implements Parcelable {
+
+    /** The localdate for which brightness stats are being tracked */
+    private final LocalDate mLocalDate;
+
+    /** Ambient brightness values for creating bucket boundaries from */
+    private final float[] mBucketBoundaries;
+
+    /** Stats of how much time (in seconds) was spent in each of the buckets */
+    private final float[] mStats;
+
+    /**
+     * Initialize day stats from the given state. The time spent in each of the bucket is
+     * initialized to 0.
+     *
+     * @param localDate        The date for which stats are being tracked
+     * @param bucketBoundaries Bucket boundaries used from creating the buckets from
+     * @hide
+     */
+    public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+            @NonNull float[] bucketBoundaries) {
+        this(localDate, bucketBoundaries, null);
+    }
+
+    /**
+     * Initialize day stats from the given state
+     *
+     * @param localDate        The date for which stats are being tracked
+     * @param bucketBoundaries Bucket boundaries used from creating the buckets from
+     * @param stats            Time spent in each of the buckets (in seconds)
+     * @hide
+     */
+    public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
+            @NonNull float[] bucketBoundaries, float[] stats) {
+        Preconditions.checkNotNull(localDate);
+        Preconditions.checkNotNull(bucketBoundaries);
+        Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
+                "bucketBoundaries");
+        if (bucketBoundaries.length < 1) {
+            throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
+        }
+        checkSorted(bucketBoundaries);
+        if (stats == null) {
+            stats = new float[bucketBoundaries.length];
+        } else {
+            Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
+            if (bucketBoundaries.length != stats.length) {
+                throw new IllegalArgumentException(
+                        "Bucket boundaries and stats must be of same size.");
+            }
+        }
+        mLocalDate = localDate;
+        mBucketBoundaries = bucketBoundaries;
+        mStats = stats;
+    }
+
+    /**
+     * @return The {@link LocalDate} for which brightness stats are being tracked.
+     */
+    public LocalDate getLocalDate() {
+        return mLocalDate;
+    }
+
+    /**
+     * @return Aggregated stats of time spent (in seconds) in various buckets.
+     */
+    public float[] getStats() {
+        return mStats;
+    }
+
+    /**
+     * Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket
+     * boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf).
+     *
+     * @return The list of bucket boundaries.
+     */
+    public float[] getBucketBoundaries() {
+        return mBucketBoundaries;
+    }
+
+    private AmbientBrightnessDayStats(Parcel source) {
+        mLocalDate = LocalDate.parse(source.readString());
+        mBucketBoundaries = source.createFloatArray();
+        mStats = source.createFloatArray();
+    }
+
+    public static final Creator<AmbientBrightnessDayStats> CREATOR =
+            new Creator<AmbientBrightnessDayStats>() {
+
+                @Override
+                public AmbientBrightnessDayStats createFromParcel(Parcel source) {
+                    return new AmbientBrightnessDayStats(source);
+                }
+
+                @Override
+                public AmbientBrightnessDayStats[] newArray(int size) {
+                    return new AmbientBrightnessDayStats[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
+        return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
+                other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = result * prime + mLocalDate.hashCode();
+        result = result * prime + Arrays.hashCode(mBucketBoundaries);
+        result = result * prime + Arrays.hashCode(mStats);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder bucketBoundariesString = new StringBuilder();
+        StringBuilder statsString = new StringBuilder();
+        for (int i = 0; i < mBucketBoundaries.length; i++) {
+            if (i != 0) {
+                bucketBoundariesString.append(", ");
+                statsString.append(", ");
+            }
+            bucketBoundariesString.append(mBucketBoundaries[i]);
+            statsString.append(mStats[i]);
+        }
+        return new StringBuilder()
+                .append(mLocalDate).append(" ")
+                .append("{").append(bucketBoundariesString).append("} ")
+                .append("{").append(statsString).append("}").toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mLocalDate.toString());
+        dest.writeFloatArray(mBucketBoundaries);
+        dest.writeFloatArray(mStats);
+    }
+
+    /**
+     * Updates the stats by incrementing the time spent for the appropriate bucket based on ambient
+     * brightness reading.
+     *
+     * @param ambientBrightness Ambient brightness reading (in lux)
+     * @param durationSec       Time spent with the given reading (in seconds)
+     * @hide
+     */
+    public void log(float ambientBrightness, float durationSec) {
+        int bucketIndex = getBucketIndex(ambientBrightness);
+        if (bucketIndex >= 0) {
+            mStats[bucketIndex] += durationSec;
+        }
+    }
+
+    private int getBucketIndex(float ambientBrightness) {
+        if (ambientBrightness < mBucketBoundaries[0]) {
+            return -1;
+        }
+        int low = 0;
+        int high = mBucketBoundaries.length - 1;
+        while (low < high) {
+            int mid = (low + high) / 2;
+            if (mBucketBoundaries[mid] <= ambientBrightness
+                    && ambientBrightness < mBucketBoundaries[mid + 1]) {
+                return mid;
+            } else if (mBucketBoundaries[mid] < ambientBrightness) {
+                low = mid + 1;
+            } else if (mBucketBoundaries[mid] > ambientBrightness) {
+                high = mid - 1;
+            }
+        }
+        return low;
+    }
+
+    private static void checkSorted(float[] values) {
+        if (values.length <= 1) {
+            return;
+        }
+        float prevValue = values[0];
+        for (int i = 1; i < values.length; i++) {
+            Preconditions.checkState(prevValue < values[i]);
+            prevValue = values[i];
+        }
+        return;
+    }
+}
diff --git a/android/hardware/display/BrightnessChangeEvent.java b/android/hardware/display/BrightnessChangeEvent.java
index 2301824..02eb28c 100644
--- a/android/hardware/display/BrightnessChangeEvent.java
+++ b/android/hardware/display/BrightnessChangeEvent.java
@@ -54,19 +54,30 @@
     /** Most recent battery level when brightness was changed or Float.NaN */
     public final float batteryLevel;
 
+    /** Factor applied to brightness due to battery level, 0.0-1.0 */
+    public final float powerBrightnessFactor;
+
     /** Color filter active to provide night mode */
     public final boolean nightMode;
 
     /** If night mode color filter is active this will be the temperature in kelvin */
     public final int colorTemperature;
 
-    /** Brightness le vel before slider adjustment */
+    /** Brightness level before slider adjustment */
     public final float lastBrightness;
 
+    /** Whether brightness configuration is default version */
+    public final boolean isDefaultBrightnessConfig;
+
+    /** Whether brightness curve includes a user brightness point */
+    public final boolean isUserSetBrightness;
+
+
     /** @hide */
     private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
             int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
-            boolean nightMode, int colorTemperature, float lastBrightness) {
+            float powerBrightnessFactor, boolean nightMode, int colorTemperature,
+            float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
         this.brightness = brightness;
         this.timeStamp = timeStamp;
         this.packageName = packageName;
@@ -74,9 +85,12 @@
         this.luxValues = luxValues;
         this.luxTimestamps = luxTimestamps;
         this.batteryLevel = batteryLevel;
+        this.powerBrightnessFactor = powerBrightnessFactor;
         this.nightMode = nightMode;
         this.colorTemperature = colorTemperature;
         this.lastBrightness = lastBrightness;
+        this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
+        this.isUserSetBrightness = isUserSetBrightness;
     }
 
     /** @hide */
@@ -88,9 +102,12 @@
         this.luxValues = other.luxValues;
         this.luxTimestamps = other.luxTimestamps;
         this.batteryLevel = other.batteryLevel;
+        this.powerBrightnessFactor = other.powerBrightnessFactor;
         this.nightMode = other.nightMode;
         this.colorTemperature = other.colorTemperature;
         this.lastBrightness = other.lastBrightness;
+        this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
+        this.isUserSetBrightness = other.isUserSetBrightness;
     }
 
     private BrightnessChangeEvent(Parcel source) {
@@ -101,9 +118,12 @@
         luxValues = source.createFloatArray();
         luxTimestamps = source.createLongArray();
         batteryLevel = source.readFloat();
+        powerBrightnessFactor = source.readFloat();
         nightMode = source.readBoolean();
         colorTemperature = source.readInt();
         lastBrightness = source.readFloat();
+        isDefaultBrightnessConfig = source.readBoolean();
+        isUserSetBrightness = source.readBoolean();
     }
 
     public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -130,9 +150,12 @@
         dest.writeFloatArray(luxValues);
         dest.writeLongArray(luxTimestamps);
         dest.writeFloat(batteryLevel);
+        dest.writeFloat(powerBrightnessFactor);
         dest.writeBoolean(nightMode);
         dest.writeInt(colorTemperature);
         dest.writeFloat(lastBrightness);
+        dest.writeBoolean(isDefaultBrightnessConfig);
+        dest.writeBoolean(isUserSetBrightness);
     }
 
     /** @hide */
@@ -144,9 +167,12 @@
         private float[] mLuxValues;
         private long[] mLuxTimestamps;
         private float mBatteryLevel;
+        private float mPowerBrightnessFactor;
         private boolean mNightMode;
         private int mColorTemperature;
         private float mLastBrightness;
+        private boolean mIsDefaultBrightnessConfig;
+        private boolean mIsUserSetBrightness;
 
         /** {@see BrightnessChangeEvent#brightness} */
         public Builder setBrightness(float brightness) {
@@ -190,6 +216,12 @@
             return this;
         }
 
+        /** {@see BrightnessChangeEvent#powerSaveBrightness} */
+        public Builder setPowerBrightnessFactor(float powerBrightnessFactor) {
+            mPowerBrightnessFactor = powerBrightnessFactor;
+            return this;
+        }
+
         /** {@see BrightnessChangeEvent#nightMode} */
         public Builder setNightMode(boolean nightMode) {
             mNightMode = nightMode;
@@ -208,11 +240,24 @@
             return this;
         }
 
+        /** {@see BrightnessChangeEvent#isDefaultBrightnessConfig} */
+        public Builder setIsDefaultBrightnessConfig(boolean isDefaultBrightnessConfig) {
+            mIsDefaultBrightnessConfig = isDefaultBrightnessConfig;
+            return this;
+        }
+
+        /** {@see BrightnessChangeEvent#userBrightnessPoint} */
+        public Builder setUserBrightnessPoint(boolean isUserSetBrightness) {
+            mIsUserSetBrightness = isUserSetBrightness;
+            return this;
+        }
+
         /** Builds a BrightnessChangeEvent */
         public BrightnessChangeEvent build() {
             return new BrightnessChangeEvent(mBrightness, mTimeStamp,
                     mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
-                    mNightMode, mColorTemperature, mLastBrightness);
+                    mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
+                    mIsDefaultBrightnessConfig, mIsUserSetBrightness);
         }
     }
 }
diff --git a/android/hardware/display/DisplayManager.java b/android/hardware/display/DisplayManager.java
index 4de4880..efb9517 100644
--- a/android/hardware/display/DisplayManager.java
+++ b/android/hardware/display/DisplayManager.java
@@ -28,7 +28,6 @@
 import android.graphics.Point;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
-import android.os.UserHandle;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.Surface;
@@ -536,6 +535,19 @@
     }
 
     /**
+     * Set the level of color saturation to apply to the display.
+     * @param level The amount of saturation to apply, between 0 and 1 inclusive.
+     * 0 produces a grayscale image, 1 is normal.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
+    public void setSaturationLevel(float level) {
+        mGlobal.setSaturationLevel(level);
+    }
+
+    /**
      * Creates a virtual display.
      *
      * @see #createVirtualDisplay(String, int, int, int, Surface, int,
@@ -615,6 +627,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public Point getStableDisplaySize() {
         return mGlobal.getStableDisplaySize();
     }
@@ -631,6 +644,18 @@
     }
 
     /**
+     * Fetch {@link AmbientBrightnessDayStats}s.
+     *
+     * @hide until we make it a system api
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
+    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+        return mGlobal.getAmbientBrightnessStats();
+    }
+
+    /**
      * Sets the global display brightness configuration.
      *
      * @hide
@@ -639,7 +664,7 @@
     @TestApi
     @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
     public void setBrightnessConfiguration(BrightnessConfiguration c) {
-        setBrightnessConfigurationForUser(c, UserHandle.myUserId(), mContext.getPackageName());
+        setBrightnessConfigurationForUser(c, mContext.getUserId(), mContext.getPackageName());
     }
 
     /**
@@ -656,6 +681,45 @@
     }
 
     /**
+     * Gets the global display brightness configuration or the default curve if one hasn't been set.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    public BrightnessConfiguration getBrightnessConfiguration() {
+        return getBrightnessConfigurationForUser(mContext.getUserId());
+    }
+
+    /**
+     * Gets the global display brightness configuration or the default curve if one hasn't been set
+     * for a specific user.
+     *
+     * Note this requires the INTERACT_ACROSS_USERS permission if getting the configuration for a
+     * user other than the one you're currently running as.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        return mGlobal.getBrightnessConfigurationForUser(userId);
+    }
+
+    /**
+     * Gets the default global display brightness configuration or null one hasn't
+     * been configured.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    @Nullable
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        return mGlobal.getDefaultBrightnessConfiguration();
+    }
+
+    /**
      * Temporarily sets the brightness of the display.
      * <p>
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
diff --git a/android/hardware/display/DisplayManagerGlobal.java b/android/hardware/display/DisplayManagerGlobal.java
index 2d5f5e0..2d0ef2f 100644
--- a/android/hardware/display/DisplayManagerGlobal.java
+++ b/android/hardware/display/DisplayManagerGlobal.java
@@ -384,6 +384,17 @@
         }
     }
 
+    /**
+     * Set the level of color saturation to apply to the display.
+     */
+    public void setSaturationLevel(float level) {
+        try {
+            mDm.setSaturationLevel(level);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
             String name, int width, int height, int densityDpi, Surface surface, int flags,
             VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
@@ -490,6 +501,32 @@
     }
 
     /**
+     * Gets the global brightness configuration for a given user or null if one hasn't been set.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        try {
+            return mDm.getBrightnessConfigurationForUser(userId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the default brightness configuration or null if one hasn't been configured.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+        try {
+            return mDm.getDefaultBrightnessConfiguration();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Temporarily sets the brightness of the display.
      * <p>
      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
@@ -525,6 +562,21 @@
         }
     }
 
+    /**
+     * Retrieves ambient brightness stats.
+     */
+    public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+        try {
+            ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
+            if (stats == null) {
+                return Collections.emptyList();
+            }
+            return stats.getList();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
         @Override
         public void onDisplayEvent(int displayId, int event) {
diff --git a/android/hardware/display/DisplayManagerInternal.java b/android/hardware/display/DisplayManagerInternal.java
index 1cfad4f..504f840 100644
--- a/android/hardware/display/DisplayManagerInternal.java
+++ b/android/hardware/display/DisplayManagerInternal.java
@@ -23,6 +23,7 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.SurfaceControl;
 
 /**
  * Display manager local system service interface.
@@ -115,7 +116,7 @@
      * Called by the window manager to perform traversals while holding a
      * surface flinger transaction.
      */
-    public abstract void performTraversalInTransactionFromWindowManager();
+    public abstract void performTraversal(SurfaceControl.Transaction t);
 
     /**
      * Tells the display manager about properties of the display that depend on the windows on it.
@@ -174,9 +175,9 @@
     public abstract boolean isUidPresentOnDisplay(int uid, int displayId);
 
     /**
-     * Persist brightness slider events.
+     * Persist brightness slider events and ambient brightness stats.
      */
-    public abstract void persistBrightnessSliderEvents();
+    public abstract void persistBrightnessTrackerState();
 
     /**
      * Notifies the display manager that resource overlays have changed.
diff --git a/android/hardware/display/WifiDisplay.java b/android/hardware/display/WifiDisplay.java
index af5a84e..bb32c19 100644
--- a/android/hardware/display/WifiDisplay.java
+++ b/android/hardware/display/WifiDisplay.java
@@ -19,7 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import libcore.util.Objects;
+import java.util.Objects;
 
 /**
  * Describes the properties of a Wifi display.
@@ -140,7 +140,7 @@
         return other != null
                 && mDeviceAddress.equals(other.mDeviceAddress)
                 && mDeviceName.equals(other.mDeviceName)
-                && Objects.equal(mDeviceAlias, other.mDeviceAlias);
+                && Objects.equals(mDeviceAlias, other.mDeviceAlias);
     }
 
     /**
diff --git a/android/hardware/fingerprint/Fingerprint.java b/android/hardware/fingerprint/Fingerprint.java
index c307634..c7ce8fa 100644
--- a/android/hardware/fingerprint/Fingerprint.java
+++ b/android/hardware/fingerprint/Fingerprint.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.fingerprint;
 
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -22,7 +23,7 @@
  * Container for fingerprint metadata.
  * @hide
  */
-public final class Fingerprint implements Parcelable {
+public final class Fingerprint extends BiometricAuthenticator.BiometricIdentifier {
     private CharSequence mName;
     private int mGroupId;
     private int mFingerId;
diff --git a/android/hardware/fingerprint/FingerprintDialog.java b/android/hardware/fingerprint/FingerprintDialog.java
deleted file mode 100644
index 6b7fab7..0000000
--- a/android/hardware/fingerprint/FingerprintDialog.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.fingerprint;
-
-import static android.Manifest.permission.USE_FINGERPRINT;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.text.TextUtils;
-
-import java.util.concurrent.Executor;
-
-/**
- * A class that manages a system-provided fingerprint dialog.
- */
-public class FingerprintDialog {
-
-    /**
-     * @hide
-     */
-    public static final String KEY_TITLE = "title";
-    /**
-     * @hide
-     */
-    public static final String KEY_SUBTITLE = "subtitle";
-    /**
-     * @hide
-     */
-    public static final String KEY_DESCRIPTION = "description";
-    /**
-     * @hide
-     */
-    public static final String KEY_POSITIVE_TEXT = "positive_text";
-    /**
-     * @hide
-     */
-    public static final String KEY_NEGATIVE_TEXT = "negative_text";
-
-    /**
-     * Error/help message will show for this amount of time.
-     * For error messages, the dialog will also be dismissed after this amount of time.
-     * Error messages will be propagated back to the application via AuthenticationCallback
-     * after this amount of time.
-     * @hide
-     */
-    public static final int HIDE_DIALOG_DELAY = 3000; // ms
-    /**
-     * @hide
-     */
-    public static final int DISMISSED_REASON_POSITIVE = 1;
-
-    /**
-     * @hide
-     */
-    public static final int DISMISSED_REASON_NEGATIVE = 2;
-
-    /**
-     * @hide
-     */
-    public static final int DISMISSED_REASON_USER_CANCEL = 3;
-
-    private static class ButtonInfo {
-        Executor executor;
-        DialogInterface.OnClickListener listener;
-        ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
-            executor = ex;
-            listener = l;
-        }
-    }
-
-    /**
-     * A builder that collects arguments, to be shown on the system-provided fingerprint dialog.
-     **/
-    public static class Builder {
-        private final Bundle bundle;
-        private ButtonInfo positiveButtonInfo;
-        private ButtonInfo negativeButtonInfo;
-
-        /**
-         * Creates a builder for a fingerprint dialog.
-         */
-        public Builder() {
-            bundle = new Bundle();
-        }
-
-        /**
-         * Required: Set the title to display.
-         * @param title
-         * @return
-         */
-        public Builder setTitle(@NonNull CharSequence title) {
-            bundle.putCharSequence(KEY_TITLE, title);
-            return this;
-        }
-
-        /**
-         * Optional: Set the subtitle to display.
-         * @param subtitle
-         * @return
-         */
-        public Builder setSubtitle(@NonNull CharSequence subtitle) {
-            bundle.putCharSequence(KEY_SUBTITLE, subtitle);
-            return this;
-        }
-
-        /**
-         * Optional: Set the description to display.
-         * @param description
-         * @return
-         */
-        public Builder setDescription(@NonNull CharSequence description) {
-            bundle.putCharSequence(KEY_DESCRIPTION, description);
-            return this;
-        }
-
-        /**
-         * Optional: Set the text for the positive button. If not set, the positive button
-         * will not show.
-         * @param text
-         * @return
-         * @hide
-         */
-        public Builder setPositiveButton(@NonNull CharSequence text,
-                @NonNull @CallbackExecutor Executor executor,
-                @NonNull DialogInterface.OnClickListener listener) {
-            if (TextUtils.isEmpty(text)) {
-                throw new IllegalArgumentException("Text must be set and non-empty");
-            }
-            if (executor == null) {
-                throw new IllegalArgumentException("Executor must not be null");
-            }
-            if (listener == null) {
-                throw new IllegalArgumentException("Listener must not be null");
-            }
-            bundle.putCharSequence(KEY_POSITIVE_TEXT, text);
-            positiveButtonInfo = new ButtonInfo(executor, listener);
-            return this;
-        }
-
-        /**
-         * Required: Set the text for the negative button.
-         * @param text
-         * @return
-         */
-        public Builder setNegativeButton(@NonNull CharSequence text,
-                @NonNull @CallbackExecutor Executor executor,
-                @NonNull DialogInterface.OnClickListener listener) {
-            if (TextUtils.isEmpty(text)) {
-                throw new IllegalArgumentException("Text must be set and non-empty");
-            }
-            if (executor == null) {
-                throw new IllegalArgumentException("Executor must not be null");
-            }
-            if (listener == null) {
-                throw new IllegalArgumentException("Listener must not be null");
-            }
-            bundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
-            negativeButtonInfo = new ButtonInfo(executor, listener);
-            return this;
-        }
-
-        /**
-         * Creates a {@link FingerprintDialog} with the arguments supplied to this builder.
-         * @param context
-         * @return a {@link FingerprintDialog}
-         * @throws IllegalArgumentException if any of the required fields are not set.
-         */
-        public FingerprintDialog build(Context context) {
-            final CharSequence title = bundle.getCharSequence(KEY_TITLE);
-            final CharSequence negative = bundle.getCharSequence(KEY_NEGATIVE_TEXT);
-
-            if (TextUtils.isEmpty(title)) {
-                throw new IllegalArgumentException("Title must be set and non-empty");
-            } else if (TextUtils.isEmpty(negative)) {
-                throw new IllegalArgumentException("Negative text must be set and non-empty");
-            }
-            return new FingerprintDialog(context, bundle, positiveButtonInfo, negativeButtonInfo);
-        }
-    }
-
-    private FingerprintManager mFingerprintManager;
-    private Bundle mBundle;
-    private ButtonInfo mPositiveButtonInfo;
-    private ButtonInfo mNegativeButtonInfo;
-
-    IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
-        @Override
-        public void onDialogDismissed(int reason) {
-            // Check the reason and invoke OnClickListener(s) if necessary
-            if (reason == DISMISSED_REASON_POSITIVE) {
-                mPositiveButtonInfo.executor.execute(() -> {
-                    mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
-                });
-            } else if (reason == DISMISSED_REASON_NEGATIVE) {
-                mNegativeButtonInfo.executor.execute(() -> {
-                    mNegativeButtonInfo.listener.onClick(null, DialogInterface.BUTTON_NEGATIVE);
-                });
-            }
-        }
-    };
-
-    private FingerprintDialog(Context context, Bundle bundle,
-            ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
-        mBundle = bundle;
-        mPositiveButtonInfo = positiveButtonInfo;
-        mNegativeButtonInfo = negativeButtonInfo;
-        mFingerprintManager = context.getSystemService(FingerprintManager.class);
-    }
-
-    /**
-     * This call warms up the fingerprint hardware, displays a system-provided dialog,
-     * and starts scanning for a fingerprint. It terminates when
-     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
-     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
-     * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
-     * dismisses the system-provided dialog, at which point the crypto object becomes invalid.
-     * This operation can be canceled by using the provided cancel object. The application will
-     * receive authentication errors through {@link AuthenticationCallback}, and button events
-     * through the corresponding callback set in
-     * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
-     * It is safe to reuse the {@link FingerprintDialog} object, and calling
-     * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
-     * while an existing authentication attempt is occurring will stop the previous client and
-     * start a new authentication. The interrupted client will receive a cancelled notification
-     * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
-     *
-     * @throws IllegalArgumentException if any of the arguments are null
-     *
-     * @param crypto object associated with the call
-     * @param cancel an object that can be used to cancel authentication
-     * @param executor an executor to handle callback events
-     * @param callback an object to receive authentication events
-     */
-    @RequiresPermission(USE_FINGERPRINT)
-    public void authenticate(@NonNull FingerprintManager.CryptoObject crypto,
-            @NonNull CancellationSignal cancel,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull FingerprintManager.AuthenticationCallback callback) {
-        mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
-                callback);
-    }
-
-    /**
-     * This call warms up the fingerprint hardware, displays a system-provided dialog,
-     * and starts scanning for a fingerprint. It terminates when
-     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when
-     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called,
-     * when {@link AuthenticationCallback#onAuthenticationFailed()} is called or when the user
-     * dismisses the system-provided dialog. This operation can be canceled by using the provided
-     * cancel object. The application will receive authentication errors through
-     * {@link AuthenticationCallback}, and button events through the corresponding callback set in
-     * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
-     * It is safe to reuse the {@link FingerprintDialog} object, and calling
-     * {@link FingerprintDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)}
-     * while an existing authentication attempt is occurring will stop the previous client and
-     * start a new authentication. The interrupted client will receive a cancelled notification
-     * through {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
-     *
-     * @throws IllegalArgumentException if any of the arguments are null
-     *
-     * @param cancel an object that can be used to cancel authentication
-     * @param executor an executor to handle callback events
-     * @param callback an object to receive authentication events
-     */
-    @RequiresPermission(USE_FINGERPRINT)
-    public void authenticate(@NonNull CancellationSignal cancel,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull FingerprintManager.AuthenticationCallback callback) {
-        mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
-    }
-}
diff --git a/android/hardware/fingerprint/FingerprintManager.java b/android/hardware/fingerprint/FingerprintManager.java
index 62d92c4..a6c8c67 100644
--- a/android/hardware/fingerprint/FingerprintManager.java
+++ b/android/hardware/fingerprint/FingerprintManager.java
@@ -18,15 +18,22 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricDialog;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -38,7 +45,6 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.security.keystore.AndroidKeyStoreProvider;
 import android.util.Log;
 import android.util.Slog;
 
@@ -51,9 +57,15 @@
 
 /**
  * A class that coordinates access to the fingerprint hardware.
+ * @deprecated See {@link BiometricDialog} which shows a system-provided dialog upon starting
+ * authentication. In a world where devices may have different types of biometric authentication,
+ * it's much more realistic to have a system-provided authentication dialog since the method may
+ * vary by vendor/device.
  */
+@Deprecated
 @SystemService(Context.FINGERPRINT_SERVICE)
-public class FingerprintManager {
+@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
+public class FingerprintManager implements BiometricFingerprintConstants {
     private static final String TAG = "FingerprintManager";
     private static final boolean DEBUG = true;
     private static final int MSG_ENROLL_RESULT = 100;
@@ -64,147 +76,14 @@
     private static final int MSG_REMOVED = 105;
     private static final int MSG_ENUMERATED = 106;
 
-    //
-    // Error messages from fingerprint hardware during initilization, enrollment, authentication or
-    // removal. Must agree with the list in fingerprint.h
-    //
-
-    /**
-     * The hardware is unavailable. Try again later.
-     */
-    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-
-    /**
-     * Error state returned when the sensor was unable to process the current image.
-     */
-    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
-
-    /**
-     * Error state returned when the current request has been running too long. This is intended to
-     * prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
-     * platform and sensor-specific, but is generally on the order of 30 seconds.
-     */
-    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
-
-    /**
-     * Error state returned for operations like enrollment; the operation cannot be completed
-     * because there's not enough storage remaining to complete the operation.
-     */
-    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
-
-    /**
-     * The operation was canceled because the fingerprint sensor is unavailable. For example,
-     * this may happen when the user is switched, the device is locked or another pending operation
-     * prevents or disables it.
-     */
-    public static final int FINGERPRINT_ERROR_CANCELED = 5;
-
-    /**
-     * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
-     * provided fingerprint id was incorrect.
-     *
-     * @hide
-     */
-    public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
-
-   /**
-     * The operation was canceled because the API is locked out due to too many attempts.
-     * This occurs after 5 failed attempts, and lasts for 30 seconds.
-     */
-    public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
-
-    /**
-     * Hardware vendors may extend this list if there are conditions that do not fall under one of
-     * the above categories. Vendors are responsible for providing error strings for these errors.
-     * These messages are typically reserved for internal operations such as enrollment, but may be
-     * used to express vendor errors not covered by the ones in fingerprint.h. Applications are
-     * expected to show the error message string if they happen, but are advised not to rely on the
-     * message id since they will be device and vendor-specific
-     */
-    public static final int FINGERPRINT_ERROR_VENDOR = 8;
-
-    /**
-     * The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
-     * Fingerprint authentication is disabled until the user unlocks with strong authentication
-     * (PIN/Pattern/Password)
-     */
-    public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
-
-    /**
-     * The user canceled the operation. Upon receiving this, applications should use alternate
-     * authentication (e.g. a password). The application should also provide the means to return
-     * to fingerprint authentication, such as a "use fingerprint" button.
-     */
-    public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
-
-    /**
-     * @hide
-     */
-    public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
-
-    //
-    // Image acquisition messages. Must agree with those in fingerprint.h
-    //
-
-    /**
-     * The image acquired was good.
-     */
-    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
-
-    /**
-     * Only a partial fingerprint image was detected. During enrollment, the user should be
-     * informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
-     */
-    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
-
-    /**
-     * The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
-     * a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
-     */
-    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
-
-    /**
-     * The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
-     * For example, it's reasonable return this after multiple
-     * {@link #FINGERPRINT_ACQUIRED_INSUFFICIENT} or actual detection of dirt on the sensor
-     * (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
-     * when this is returned.
-     */
-    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
-
-    /**
-     * The fingerprint image was unreadable due to lack of motion. This is most appropriate for
-     * linear array sensors that require a swipe motion.
-     */
-    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
-
-    /**
-     * The fingerprint image was incomplete due to quick motion. While mostly appropriate for
-     * linear array sensors,  this could also happen if the finger was moved during acquisition.
-     * The user should be asked to move the finger slower (linear) or leave the finger on the sensor
-     * longer.
-     */
-    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
-
-    /**
-     * Hardware vendors may extend this list if there are conditions that do not fall under one of
-     * the above categories. Vendors are responsible for providing error strings for these errors.
-     * @hide
-     */
-    public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
-    /**
-     * @hide
-     */
-    public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
-
     private IFingerprintService mService;
     private Context mContext;
     private IBinder mToken = new Binder();
-    private AuthenticationCallback mAuthenticationCallback;
+    private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
     private EnrollmentCallback mEnrollmentCallback;
     private RemovalCallback mRemovalCallback;
     private EnumerateCallback mEnumerateCallback;
-    private CryptoObject mCryptoObject;
+    private android.hardware.biometrics.CryptoObject mCryptoObject;
     private Fingerprint mRemovalFingerprint;
     private Handler mHandler;
     private Executor mExecutor;
@@ -217,9 +96,9 @@
     }
 
     private class OnAuthenticationCancelListener implements OnCancelListener {
-        private CryptoObject mCrypto;
+        private android.hardware.biometrics.CryptoObject mCrypto;
 
-        public OnAuthenticationCancelListener(CryptoObject crypto) {
+        public OnAuthenticationCancelListener(android.hardware.biometrics.CryptoObject crypto) {
             mCrypto = crypto;
         }
 
@@ -232,19 +111,20 @@
     /**
      * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
      * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.CryptoObject}
      */
-    public static final class CryptoObject {
-
+    @Deprecated
+    public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
         public CryptoObject(@NonNull Signature signature) {
-            mCrypto = signature;
+            super(signature);
         }
 
         public CryptoObject(@NonNull Cipher cipher) {
-            mCrypto = cipher;
+            super(cipher);
         }
 
         public CryptoObject(@NonNull Mac mac) {
-            mCrypto = mac;
+            super(mac);
         }
 
         /**
@@ -252,7 +132,7 @@
          * @return {@link Signature} object or null if this doesn't contain one.
          */
         public Signature getSignature() {
-            return mCrypto instanceof Signature ? (Signature) mCrypto : null;
+            return super.getSignature();
         }
 
         /**
@@ -260,7 +140,7 @@
          * @return {@link Cipher} object or null if this doesn't contain one.
          */
         public Cipher getCipher() {
-            return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
+            return super.getCipher();
         }
 
         /**
@@ -268,25 +148,16 @@
          * @return {@link Mac} object or null if this doesn't contain one.
          */
         public Mac getMac() {
-            return mCrypto instanceof Mac ? (Mac) mCrypto : null;
+            return super.getMac();
         }
-
-        /**
-         * @hide
-         * @return the opId associated with this object or 0 if none
-         */
-        public long getOpId() {
-            return mCrypto != null ?
-                    AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
-        }
-
-        private final Object mCrypto;
-    };
+    }
 
     /**
      * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
      *     CancellationSignal, int, AuthenticationCallback, Handler)}.
+     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationResult}
      */
+    @Deprecated
     public static class AuthenticationResult {
         private Fingerprint mFingerprint;
         private CryptoObject mCryptoObject;
@@ -333,14 +204,18 @@
      * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
      * fingerprint events.
+     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationCallback}
      */
-    public static abstract class AuthenticationCallback {
+    @Deprecated
+    public static abstract class AuthenticationCallback
+            extends BiometricAuthenticator.AuthenticationCallback {
         /**
          * Called when an unrecoverable error has been encountered and the operation is complete.
          * No further callbacks will be made on this object.
          * @param errorCode An integer identifying the error message
          * @param errString A human-readable error string that can be shown in UI
          */
+        @Override
         public void onAuthenticationError(int errorCode, CharSequence errString) { }
 
         /**
@@ -350,6 +225,7 @@
          * @param helpCode An integer identifying the error message
          * @param helpString A human-readable string that can be shown in UI
          */
+        @Override
         public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
 
         /**
@@ -361,6 +237,7 @@
         /**
          * Called when a fingerprint is valid but not recognized.
          */
+        @Override
         public void onAuthenticationFailed() { }
 
         /**
@@ -369,7 +246,19 @@
          * @param acquireInfo one of FINGERPRINT_ACQUIRED_* constants
          * @hide
          */
+        @Override
         public void onAuthenticationAcquired(int acquireInfo) {}
+
+        /**
+         * @hide
+         * @param result
+         */
+        @Override
+        public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
+            onAuthenticationSucceeded(new AuthenticationResult(
+                    (CryptoObject) result.getCryptoObject(),
+                    (Fingerprint) result.getId(), result.getUserId()));
+        }
     };
 
     /**
@@ -489,11 +378,16 @@
      *         by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
      *         facility</a>.
      * @throws IllegalStateException if the crypto primitive is not initialized.
+     * @deprecated See {@link BiometricDialog#authenticate(CancellationSignal, Executor,
+     * BiometricDialog.AuthenticationCallback)} and {@link BiometricDialog#authenticate(
+     * BiometricDialog.CryptoObject, CancellationSignal, Executor,
+     * BiometricDialog.AuthenticationCallback)}
      */
-    @RequiresPermission(USE_FINGERPRINT)
+    @Deprecated
+    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
-        authenticate(crypto, cancel, flags, callback, handler, UserHandle.myUserId());
+        authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
     }
 
     /**
@@ -514,7 +408,7 @@
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      * @hide
      */
-    @RequiresPermission(USE_FINGERPRINT)
+    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
         if (callback == null) {
@@ -550,16 +444,16 @@
 
     /**
      * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, Bundle, Executor, IFingerprintDialogReceiver, AuthenticationCallback)}
+     * CancellationSignal, Bundle, Executor, IBiometricDialogReceiver, AuthenticationCallback)}
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      */
     private void authenticate(int userId,
-            @Nullable CryptoObject crypto,
+            @Nullable android.hardware.biometrics.CryptoObject crypto,
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IFingerprintDialogReceiver receiver,
-            @NonNull AuthenticationCallback callback) {
+            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         mCryptoObject = crypto;
         if (cancel.isCanceled()) {
             Log.w(TAG, "authentication already canceled");
@@ -586,8 +480,8 @@
     }
 
     /**
-     * Private method, see {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
-     * AuthenticationCallback)}
+     * Private method, see {@link BiometricDialog#authenticate(CancellationSignal, Executor,
+     * BiometricDialog.AuthenticationCallback)}
      * @param cancel
      * @param executor
      * @param callback
@@ -597,8 +491,8 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IFingerprintDialogReceiver receiver,
-            @NonNull AuthenticationCallback callback) {
+            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (cancel == null) {
             throw new IllegalArgumentException("Must supply a cancellation signal");
         }
@@ -614,24 +508,24 @@
         if (callback == null) {
             throw new IllegalArgumentException("Must supply a calback");
         }
-        authenticate(UserHandle.myUserId(), null, cancel, bundle, executor, receiver, callback);
+        authenticate(mContext.getUserId(), null, cancel, bundle, executor, receiver, callback);
     }
 
     /**
-     * Private method, see {@link FingerprintDialog#authenticate(CryptoObject, CancellationSignal,
-     * Executor, AuthenticationCallback)}
+     * Private method, see {@link BiometricDialog#authenticate(BiometricDialog.CryptoObject,
+     * CancellationSignal, Executor, BiometricDialog.AuthenticationCallback)}
      * @param crypto
      * @param cancel
      * @param executor
      * @param callback
      * @hide
      */
-    public void authenticate(@NonNull CryptoObject crypto,
+    public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IFingerprintDialogReceiver receiver,
-            @NonNull AuthenticationCallback callback) {
+            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (crypto == null) {
             throw new IllegalArgumentException("Must supply a crypto object");
         }
@@ -648,9 +542,10 @@
             throw new IllegalArgumentException("Must supply a receiver");
         }
         if (callback == null) {
-            throw new IllegalArgumentException("Must supply a calback");
+            throw new IllegalArgumentException("Must supply a callback");
         }
-        authenticate(UserHandle.myUserId(), crypto, cancel, bundle, executor, receiver, callback);
+        authenticate(mContext.getUserId(), crypto, cancel,
+                bundle, executor, receiver, callback);
     }
 
     /**
@@ -841,19 +736,22 @@
      */
     @RequiresPermission(USE_FINGERPRINT)
     public List<Fingerprint> getEnrolledFingerprints() {
-        return getEnrolledFingerprints(UserHandle.myUserId());
+        return getEnrolledFingerprints(mContext.getUserId());
     }
 
     /**
      * Determine if there is at least one fingerprint enrolled.
      *
      * @return true if at least one fingerprint is enrolled, false otherwise
+     * @deprecated See {@link BiometricDialog} and
+     * {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
      */
+    @Deprecated
     @RequiresPermission(USE_FINGERPRINT)
     public boolean hasEnrolledFingerprints() {
         if (mService != null) try {
             return mService.hasEnrolledFingerprints(
-                    UserHandle.myUserId(), mContext.getOpPackageName());
+                    mContext.getUserId(), mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -879,7 +777,10 @@
      * Determine if fingerprint hardware is present and functional.
      *
      * @return true if hardware is present and functional, false otherwise.
+     * @deprecated See {@link BiometricDialog} and
+     * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
      */
+    @Deprecated
     @RequiresPermission(USE_FINGERPRINT)
     public boolean isHardwareDetected() {
         if (mService != null) {
@@ -1049,8 +950,8 @@
 
     private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
         if (mAuthenticationCallback != null) {
-            final AuthenticationResult result =
-                    new AuthenticationResult(mCryptoObject, fp, userId);
+            final BiometricAuthenticator.AuthenticationResult result =
+                    new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
             mAuthenticationCallback.onAuthenticationSucceeded(result);
         }
     }
@@ -1126,7 +1027,7 @@
         }
     }
 
-    private void cancelAuthentication(CryptoObject cryptoObject) {
+    private void cancelAuthentication(android.hardware.biometrics.CryptoObject cryptoObject) {
         if (mService != null) try {
             mService.cancelAuthentication(mToken, mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -1160,6 +1061,12 @@
             case FINGERPRINT_ERROR_USER_CANCELED:
                 return mContext.getString(
                         com.android.internal.R.string.fingerprint_error_user_canceled);
+            case FINGERPRINT_ERROR_NO_FINGERPRINTS:
+                return mContext.getString(
+                        com.android.internal.R.string.fingerprint_error_no_fingerprints);
+            case FINGERPRINT_ERROR_HW_NOT_PRESENT:
+                return mContext.getString(
+                        com.android.internal.R.string.fingerprint_error_hw_not_present);
             case FINGERPRINT_ERROR_VENDOR: {
                     String[] msgArray = mContext.getResources().getStringArray(
                             com.android.internal.R.array.fingerprint_error_vendor);
@@ -1251,9 +1158,22 @@
         @Override // binder call
         public void onError(long deviceId, int error, int vendorCode) {
             if (mExecutor != null) {
-                mExecutor.execute(() -> {
-                    sendErrorResult(deviceId, error, vendorCode);
-                });
+                // BiometricDialog case
+                if (error == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) {
+                    // User tapped somewhere to cancel, the biometric dialog is already dismissed.
+                    mExecutor.execute(() -> {
+                        sendErrorResult(deviceId, error, vendorCode);
+                    });
+                } else {
+                    // User got an error that needs to be displayed on the dialog, post a delayed
+                    // runnable on the FingerprintManager handler that sends the error message after
+                    // FingerprintDialog.HIDE_DIALOG_DELAY to send the error to the application.
+                    mHandler.postDelayed(() -> {
+                        mExecutor.execute(() -> {
+                            sendErrorResult(deviceId, error, vendorCode);
+                        });
+                    }, BiometricDialog.HIDE_DIALOG_DELAY);
+                }
             } else {
                 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
             }
diff --git a/android/hardware/hdmi/HdmiControlManager.java b/android/hardware/hdmi/HdmiControlManager.java
index a772cbe..e34423c 100644
--- a/android/hardware/hdmi/HdmiControlManager.java
+++ b/android/hardware/hdmi/HdmiControlManager.java
@@ -17,11 +17,13 @@
 package android.hardware.hdmi;
 
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.os.RemoteException;
@@ -42,6 +44,7 @@
  */
 @SystemApi
 @SystemService(Context.HDMI_CONTROL_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_HDMI_CEC)
 public final class HdmiControlManager {
     private static final String TAG = "HdmiControlManager";
 
diff --git a/android/hardware/input/InputManager.java b/android/hardware/input/InputManager.java
index 1de8882..6ae7a14 100644
--- a/android/hardware/input/InputManager.java
+++ b/android/hardware/input/InputManager.java
@@ -17,7 +17,6 @@
 package android.hardware.input;
 
 import android.annotation.IntDef;
-import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
@@ -43,8 +42,6 @@
 import android.view.InputEvent;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.os.SomeArgs;
 
@@ -703,52 +700,6 @@
         }
     }
 
-
-    /**
-     * Gets the keyboard layout for the specified input device and IME subtype.
-     *
-     * @param identifier The identifier for the input device.
-     * @param inputMethodInfo The input method.
-     * @param inputMethodSubtype The input method subtype. {@code null} if this input method does
-     * not support any subtype.
-     *
-     * @return The associated {@link KeyboardLayout}, or null if one has not been set.
-     *
-     * @hide
-     */
-    @Nullable
-    public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
-            InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype) {
-        try {
-            return mIm.getKeyboardLayoutForInputDevice(
-                    identifier, inputMethodInfo, inputMethodSubtype);
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Sets the keyboard layout for the specified input device and IME subtype pair.
-     *
-     * @param identifier The identifier for the input device.
-     * @param inputMethodInfo The input method with which to associate the keyboard layout.
-     * @param inputMethodSubtype The input method subtype which which to associate the keyboard
-     * layout. {@code null} if this input method does not support any subtype.
-     * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to set
-     *
-     * @hide
-     */
-    public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
-            InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype inputMethodSubtype,
-            String keyboardLayoutDescriptor) {
-        try {
-            mIm.setKeyboardLayoutForInputDevice(identifier, inputMethodInfo,
-                    inputMethodSubtype, keyboardLayoutDescriptor);
-        } catch (RemoteException ex) {
-            throw ex.rethrowFromSystemServer();
-        }
-    }
-
     /**
      * Gets the TouchCalibration applied to the specified input device's coordinates.
      *
@@ -1246,7 +1197,7 @@
             int repeat;
             if (effect instanceof VibrationEffect.OneShot) {
                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
-                pattern = new long[] { 0, oneShot.getTiming() };
+                pattern = new long[] { 0, oneShot.getDuration() };
                 repeat = -1;
             } else if (effect instanceof VibrationEffect.Waveform) {
                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
diff --git a/android/hardware/input/InputManagerInternal.java b/android/hardware/input/InputManagerInternal.java
index 4ea0f55..eb7ea67 100644
--- a/android/hardware/input/InputManagerInternal.java
+++ b/android/hardware/input/InputManagerInternal.java
@@ -16,11 +16,8 @@
 
 package android.hardware.input;
 
-import android.annotation.Nullable;
 import android.hardware.display.DisplayViewport;
 import android.view.InputEvent;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
 
 import java.util.List;
 
@@ -46,16 +43,6 @@
     public abstract void setInteractive(boolean interactive);
 
     /**
-     * Notifies that InputMethodManagerService switched the current input method subtype.
-     *
-     * @param userId user id that indicates who is using the specified input method and subtype.
-     * @param inputMethodInfo {@code null} when no input method is selected.
-     * @param subtype {@code null} when {@code inputMethodInfo} does has no subtype.
-     */
-    public abstract void onInputMethodSubtypeChanged(int userId,
-            @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype);
-
-    /**
      * Toggles Caps Lock state for input device with specific id.
      *
      * @param deviceId The id of input device.
diff --git a/android/hardware/input/TouchCalibration.java b/android/hardware/input/TouchCalibration.java
index 15503ed..025fad0 100644
--- a/android/hardware/input/TouchCalibration.java
+++ b/android/hardware/input/TouchCalibration.java
@@ -123,10 +123,4 @@
                Float.floatToIntBits(mYScale)  ^
                Float.floatToIntBits(mYOffset);
     }
-
-    @Override
-    public String toString() {
-        return String.format("[%f, %f, %f, %f, %f, %f]",
-                mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset);
-    }
 }
diff --git a/android/hardware/location/ContextHubMessage.java b/android/hardware/location/ContextHubMessage.java
index f85ce3e..e1c69d7 100644
--- a/android/hardware/location/ContextHubMessage.java
+++ b/android/hardware/location/ContextHubMessage.java
@@ -33,7 +33,7 @@
  */
 @SystemApi
 @Deprecated
-public class ContextHubMessage {
+public class ContextHubMessage implements Parcelable {
     private static final int DEBUG_LOG_NUM_BYTES = 16;
     private int mType;
     private int mVersion;
diff --git a/android/hardware/location/NanoApp.java b/android/hardware/location/NanoApp.java
index b5c01ec..ded1bb8 100644
--- a/android/hardware/location/NanoApp.java
+++ b/android/hardware/location/NanoApp.java
@@ -36,7 +36,7 @@
  */
 @SystemApi
 @Deprecated
-public class NanoApp {
+public class NanoApp implements Parcelable {
     private final String TAG = "NanoApp";
 
     private final String UNKNOWN = "Unknown";
diff --git a/android/hardware/location/NanoAppFilter.java b/android/hardware/location/NanoAppFilter.java
index 75a96ee..4d8e734 100644
--- a/android/hardware/location/NanoAppFilter.java
+++ b/android/hardware/location/NanoAppFilter.java
@@ -28,7 +28,7 @@
  */
 @SystemApi
 @Deprecated
-public class NanoAppFilter {
+public class NanoAppFilter implements Parcelable {
 
     private static final String TAG = "NanoAppFilter";
 
diff --git a/android/hardware/location/NanoAppInstanceInfo.java b/android/hardware/location/NanoAppInstanceInfo.java
index f1926ea..75fb915 100644
--- a/android/hardware/location/NanoAppInstanceInfo.java
+++ b/android/hardware/location/NanoAppInstanceInfo.java
@@ -34,7 +34,7 @@
  */
 @SystemApi
 @Deprecated
-public class NanoAppInstanceInfo {
+public class NanoAppInstanceInfo implements Parcelable {
     private String mPublisher = "Unknown";
     private String mName = "Unknown";
 
diff --git a/android/hardware/radio/ProgramList.java b/android/hardware/radio/ProgramList.java
index b2aa9ba..e6f523c 100644
--- a/android/hardware/radio/ProgramList.java
+++ b/android/hardware/radio/ProgramList.java
@@ -263,6 +263,17 @@
         /**
          * @hide for framework use only
          */
+        public Filter() {
+            mIdentifierTypes = Collections.emptySet();
+            mIdentifiers = Collections.emptySet();
+            mIncludeCategories = false;
+            mExcludeModifications = false;
+            mVendorFilter = null;
+        }
+
+        /**
+         * @hide for framework use only
+         */
         public Filter(@Nullable Map<String, String> vendorFilter) {
             mIdentifierTypes = Collections.emptySet();
             mIdentifiers = Collections.emptySet();
diff --git a/android/hardware/radio/ProgramSelector.java b/android/hardware/radio/ProgramSelector.java
index 0294a29..2a878eb 100644
--- a/android/hardware/radio/ProgramSelector.java
+++ b/android/hardware/radio/ProgramSelector.java
@@ -441,6 +441,15 @@
      */
     public static @NonNull ProgramSelector createAmFmSelector(
             @RadioManager.Band int band, int frequencyKhz, int subChannel) {
+        if (band == RadioManager.BAND_INVALID) {
+            // 50MHz is a rough boundary between AM (<30MHz) and FM (>60MHz).
+            if (frequencyKhz < 50000) {
+                band = (subChannel <= 0) ? RadioManager.BAND_AM : RadioManager.BAND_AM_HD;
+            } else {
+                band = (subChannel <= 0) ? RadioManager.BAND_FM : RadioManager.BAND_FM_HD;
+            }
+        }
+
         boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD);
         boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD);
         if (!isAm && !isDigital && band != RadioManager.BAND_FM) {
diff --git a/android/hardware/radio/RadioManager.java b/android/hardware/radio/RadioManager.java
index b00f603..8263bb8 100644
--- a/android/hardware/radio/RadioManager.java
+++ b/android/hardware/radio/RadioManager.java
@@ -21,10 +21,12 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -58,6 +60,7 @@
  */
 @SystemApi
 @SystemService(Context.RADIO_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_BROADCAST_RADIO)
 public class RadioManager {
     private static final String TAG = "BroadcastRadio.manager";
 
@@ -208,19 +211,23 @@
         private final String mSerial;
         private final int mNumTuners;
         private final int mNumAudioSources;
+        private final boolean mIsInitializationRequired;
         private final boolean mIsCaptureSupported;
         private final BandDescriptor[] mBands;
         private final boolean mIsBgScanSupported;
         private final Set<Integer> mSupportedProgramTypes;
         private final Set<Integer> mSupportedIdentifierTypes;
+        @Nullable private final Map<String, Integer> mDabFrequencyTable;
         @NonNull private final Map<String, String> mVendorInfo;
 
         /** @hide */
         public ModuleProperties(int id, String serviceName, int classId, String implementor,
                 String product, String version, String serial, int numTuners, int numAudioSources,
-                boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+                boolean isInitializationRequired, boolean isCaptureSupported,
+                BandDescriptor[] bands, boolean isBgScanSupported,
                 @ProgramSelector.ProgramType int[] supportedProgramTypes,
                 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
+                @Nullable Map<String, Integer> dabFrequencyTable,
                 Map<String, String> vendorInfo) {
             mId = id;
             mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
@@ -231,11 +238,19 @@
             mSerial = serial;
             mNumTuners = numTuners;
             mNumAudioSources = numAudioSources;
+            mIsInitializationRequired = isInitializationRequired;
             mIsCaptureSupported = isCaptureSupported;
             mBands = bands;
             mIsBgScanSupported = isBgScanSupported;
             mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
             mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
+            if (dabFrequencyTable != null) {
+                for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) {
+                    Objects.requireNonNull(entry.getKey());
+                    Objects.requireNonNull(entry.getValue());
+                }
+            }
+            mDabFrequencyTable = dabFrequencyTable;
             mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
         }
 
@@ -317,6 +332,18 @@
             return mNumAudioSources;
         }
 
+        /**
+         * Checks, if BandConfig initialization (after {@link RadioManager#openTuner})
+         * is required to be done before other operations or not.
+         *
+         * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged}
+         * callback before executing any other operations. Otherwise, such operation will fail
+         * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code.
+         */
+        public boolean isInitializationRequired() {
+            return mIsInitializationRequired;
+        }
+
         /** {@code true} if audio capture is possible from radio tuner output.
          * This indicates if routing to audio devices not connected to the same HAL as the FM radio
          * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
@@ -363,6 +390,19 @@
         }
 
         /**
+         * A frequency table for Digital Audio Broadcasting (DAB).
+         *
+         * The key is a channel name, i.e. 5A, 7B.
+         *
+         * The value is a frequency, in kHz.
+         *
+         * @return a frequency table, or {@code null} if the module doesn't support DAB
+         */
+        public @Nullable Map<String, Integer> getDabFrequencyTable() {
+            return mDabFrequencyTable;
+        }
+
+        /**
          * A map of vendor-specific opaque strings, passed from HAL without changes.
          * Format of these strings can vary across vendors.
          *
@@ -394,6 +434,7 @@
             mSerial = in.readString();
             mNumTuners = in.readInt();
             mNumAudioSources = in.readInt();
+            mIsInitializationRequired = in.readInt() == 1;
             mIsCaptureSupported = in.readInt() == 1;
             Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
             mBands = new BandDescriptor[tmp.length];
@@ -403,6 +444,7 @@
             mIsBgScanSupported = in.readInt() == 1;
             mSupportedProgramTypes = arrayToSet(in.createIntArray());
             mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
+            mDabFrequencyTable = Utils.readStringIntMap(in);
             mVendorInfo = Utils.readStringMap(in);
         }
 
@@ -428,11 +470,13 @@
             dest.writeString(mSerial);
             dest.writeInt(mNumTuners);
             dest.writeInt(mNumAudioSources);
+            dest.writeInt(mIsInitializationRequired ? 1 : 0);
             dest.writeInt(mIsCaptureSupported ? 1 : 0);
             dest.writeParcelableArray(mBands, flags);
             dest.writeInt(mIsBgScanSupported ? 1 : 0);
             dest.writeIntArray(setToArray(mSupportedProgramTypes));
             dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
+            Utils.writeStringIntMap(dest, mDabFrequencyTable);
             Utils.writeStringMap(dest, mVendorInfo);
         }
 
@@ -449,6 +493,7 @@
                     + ", mVersion=" + mVersion + ", mSerial=" + mSerial
                     + ", mNumTuners=" + mNumTuners
                     + ", mNumAudioSources=" + mNumAudioSources
+                    + ", mIsInitializationRequired=" + mIsInitializationRequired
                     + ", mIsCaptureSupported=" + mIsCaptureSupported
                     + ", mIsBgScanSupported=" + mIsBgScanSupported
                     + ", mBands=" + Arrays.toString(mBands) + "]";
@@ -456,67 +501,32 @@
 
         @Override
         public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + mId;
-            result = prime * result + mServiceName.hashCode();
-            result = prime * result + mClassId;
-            result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode());
-            result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode());
-            result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());
-            result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode());
-            result = prime * result + mNumTuners;
-            result = prime * result + mNumAudioSources;
-            result = prime * result + (mIsCaptureSupported ? 1 : 0);
-            result = prime * result + Arrays.hashCode(mBands);
-            result = prime * result + (mIsBgScanSupported ? 1 : 0);
-            result = prime * result + mVendorInfo.hashCode();
-            return result;
+            return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
+                mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
+                mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
         }
 
         @Override
         public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (!(obj instanceof ModuleProperties))
-                return false;
+            if (this == obj) return true;
+            if (!(obj instanceof ModuleProperties)) return false;
             ModuleProperties other = (ModuleProperties) obj;
-            if (mId != other.getId())
-                return false;
+
+            if (mId != other.getId()) return false;
             if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
-            if (mClassId != other.getClassId())
-                return false;
-            if (mImplementor == null) {
-                if (other.getImplementor() != null)
-                    return false;
-            } else if (!mImplementor.equals(other.getImplementor()))
-                return false;
-            if (mProduct == null) {
-                if (other.getProduct() != null)
-                    return false;
-            } else if (!mProduct.equals(other.getProduct()))
-                return false;
-            if (mVersion == null) {
-                if (other.getVersion() != null)
-                    return false;
-            } else if (!mVersion.equals(other.getVersion()))
-                return false;
-            if (mSerial == null) {
-                if (other.getSerial() != null)
-                    return false;
-            } else if (!mSerial.equals(other.getSerial()))
-                return false;
-            if (mNumTuners != other.getNumTuners())
-                return false;
-            if (mNumAudioSources != other.getNumAudioSources())
-                return false;
-            if (mIsCaptureSupported != other.isCaptureSupported())
-                return false;
-            if (!Arrays.equals(mBands, other.getBands()))
-                return false;
-            if (mIsBgScanSupported != other.isBackgroundScanningSupported())
-                return false;
-            if (!mVendorInfo.equals(other.mVendorInfo)) return false;
+            if (mClassId != other.mClassId) return false;
+            if (!Objects.equals(mImplementor, other.mImplementor)) return false;
+            if (!Objects.equals(mProduct, other.mProduct)) return false;
+            if (!Objects.equals(mVersion, other.mVersion)) return false;
+            if (!Objects.equals(mSerial, other.mSerial)) return false;
+            if (mNumTuners != other.mNumTuners) return false;
+            if (mNumAudioSources != other.mNumAudioSources) return false;
+            if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
+            if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
+            if (!Objects.equals(mBands, other.mBands)) return false;
+            if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
+            if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
+            if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
             return true;
         }
     }
diff --git a/android/hardware/radio/RadioMetadata.java b/android/hardware/radio/RadioMetadata.java
index 3cc4b56..6e51060 100644
--- a/android/hardware/radio/RadioMetadata.java
+++ b/android/hardware/radio/RadioMetadata.java
@@ -96,6 +96,48 @@
      */
     public static final String METADATA_KEY_CLOCK = "android.hardware.radio.metadata.CLOCK";
 
+    /**
+     * Technology-independent program name (station name).
+     */
+    public static final String METADATA_KEY_PROGRAM_NAME =
+            "android.hardware.radio.metadata.PROGRAM_NAME";
+
+    /**
+     * DAB ensemble name.
+     */
+    public static final String METADATA_KEY_DAB_ENSEMBLE_NAME =
+            "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME";
+
+    /**
+     * DAB ensemble name - short version (up to 8 characters).
+     */
+    public static final String METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT =
+            "android.hardware.radio.metadata.DAB_ENSEMBLE_NAME_SHORT";
+
+    /**
+     * DAB service name.
+     */
+    public static final String METADATA_KEY_DAB_SERVICE_NAME =
+            "android.hardware.radio.metadata.DAB_SERVICE_NAME";
+
+    /**
+     * DAB service name - short version (up to 8 characters).
+     */
+    public static final String METADATA_KEY_DAB_SERVICE_NAME_SHORT =
+            "android.hardware.radio.metadata.DAB_SERVICE_NAME_SHORT";
+
+    /**
+     * DAB component name.
+     */
+    public static final String METADATA_KEY_DAB_COMPONENT_NAME =
+            "android.hardware.radio.metadata.DAB_COMPONENT_NAME";
+
+    /**
+     * DAB component name.
+     */
+    public static final String METADATA_KEY_DAB_COMPONENT_NAME_SHORT =
+            "android.hardware.radio.metadata.DAB_COMPONENT_NAME_SHORT";
+
 
     private static final int METADATA_TYPE_INVALID = -1;
     private static final int METADATA_TYPE_INT = 0;
@@ -119,6 +161,13 @@
         METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP);
         METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
         METADATA_KEYS_TYPE.put(METADATA_KEY_CLOCK, METADATA_TYPE_CLOCK);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_PROGRAM_NAME, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_SERVICE_NAME_SHORT, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DAB_COMPONENT_NAME_SHORT, METADATA_TYPE_TEXT);
     }
 
     // keep in sync with: system/media/radio/include/system/radio_metadata.h
diff --git a/android/hardware/radio/RadioTuner.java b/android/hardware/radio/RadioTuner.java
index ed20c4a..0edd055 100644
--- a/android/hardware/radio/RadioTuner.java
+++ b/android/hardware/radio/RadioTuner.java
@@ -64,7 +64,9 @@
      *  <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
      *  service fails, </li>
      * </ul>
+     * @deprecated Only applicable for HAL 1.x.
      */
+    @Deprecated
     public abstract int setConfiguration(RadioManager.BandConfig config);
 
     /**
@@ -80,7 +82,10 @@
      *  <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
      *  service fails, </li>
      * </ul>
+     *
+     * @deprecated Only applicable for HAL 1.x.
      */
+    @Deprecated
     public abstract int getConfiguration(RadioManager.BandConfig[] config);
 
 
@@ -228,7 +233,9 @@
      *  <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
      *  service fails, </li>
      * </ul>
+     * @deprecated Use {@link onProgramInfoChanged} callback instead.
      */
+    @Deprecated
     public abstract int getProgramInformation(RadioManager.ProgramInfo[] info);
 
     /**
@@ -427,7 +434,10 @@
      * Get current antenna connection state for current configuration.
      * Only valid if a configuration has been applied.
      * @return {@code true} if the antenna is connected, {@code false} otherwise.
+     *
+     * @deprecated Use {@link onAntennaState} callback instead
      */
+    @Deprecated
     public abstract boolean isAntennaConnected();
 
     /**
@@ -446,20 +456,41 @@
     public abstract boolean hasControl();
 
     /** Indicates a failure of radio IC or driver.
-     * The application must close and re open the tuner */
+     * The application must close and re open the tuner
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final int ERROR_HARDWARE_FAILURE = 0;
     /** Indicates a failure of the radio service.
-     * The application must close and re open the tuner */
+     * The application must close and re open the tuner
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final  int ERROR_SERVER_DIED = 1;
-    /** A pending seek or tune operation was cancelled */
+    /** A pending seek or tune operation was cancelled
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final  int ERROR_CANCELLED = 2;
-    /** A pending seek or tune operation timed out */
+    /** A pending seek or tune operation timed out
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final  int ERROR_SCAN_TIMEOUT = 3;
-    /** The requested configuration could not be applied */
+    /** The requested configuration could not be applied
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final  int ERROR_CONFIG = 4;
-    /** Background scan was interrupted due to hardware becoming temporarily unavailable. */
+    /** Background scan was interrupted due to hardware becoming temporarily unavailable.
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5;
-    /** Background scan failed due to other error, ie. HW failure. */
+    /** Background scan failed due to other error, ie. HW failure.
+     * @deprecated See {@link onError} callback.
+     */
+    @Deprecated
     public static final int ERROR_BACKGROUND_SCAN_FAILED = 6;
 
     /**
@@ -473,13 +504,29 @@
          * status is one of {@link #ERROR_HARDWARE_FAILURE}, {@link #ERROR_SERVER_DIED},
          * {@link #ERROR_CANCELLED}, {@link #ERROR_SCAN_TIMEOUT},
          * {@link #ERROR_CONFIG}
+         *
+         * @deprecated Use {@link onTuneFailed} for tune, scan and step;
+         *             other use cases (configuration, background scan) are already deprecated.
          */
         public void onError(int status) {}
+
+        /**
+         * Called when tune, scan or step operation fails.
+         *
+         * @param result cause of the failure
+         * @param selector ProgramSelector argument of tune that failed;
+         *                 null for scan and step.
+         */
+        public void onTuneFailed(int result, @Nullable ProgramSelector selector) {}
+
         /**
          * onConfigurationChanged() is called upon successful completion of
          * {@link RadioManager#openTuner(int, RadioManager.BandConfig, boolean, Callback, Handler)}
          * or {@link RadioTuner#setConfiguration(RadioManager.BandConfig)}
+         *
+         * @deprecated Only applicable for HAL 1.x.
          */
+        @Deprecated
         public void onConfigurationChanged(RadioManager.BandConfig config) {}
 
         /**
diff --git a/android/hardware/radio/TunerAdapter.java b/android/hardware/radio/TunerAdapter.java
index 91944bf..be2846f 100644
--- a/android/hardware/radio/TunerAdapter.java
+++ b/android/hardware/radio/TunerAdapter.java
@@ -60,6 +60,7 @@
                 mLegacyListProxy.close();
                 mLegacyListProxy = null;
             }
+            mCallback.close();
         }
         try {
             mTuner.close();
@@ -202,15 +203,17 @@
     @Override
     public int getProgramInformation(RadioManager.ProgramInfo[] info) {
         if (info == null || info.length != 1) {
-            throw new IllegalArgumentException("The argument must be an array of length 1");
+            Log.e(TAG, "The argument must be an array of length 1");
+            return RadioManager.STATUS_BAD_VALUE;
         }
-        try {
-            info[0] = mTuner.getProgramInformation();
-            return RadioManager.STATUS_OK;
-        } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
-            return RadioManager.STATUS_DEAD_OBJECT;
+
+        RadioManager.ProgramInfo current = mCallback.getCurrentProgramInformation();
+        if (current == null) {
+            Log.w(TAG, "Didn't get program info yet");
+            return RadioManager.STATUS_INVALID_OPERATION;
         }
+        info[0] = current;
+        return RadioManager.STATUS_OK;
     }
 
     @Override
@@ -276,6 +279,7 @@
             try {
                 mTuner.startProgramListUpdates(filter);
             } catch (UnsupportedOperationException ex) {
+                Log.i(TAG, "Program list is not supported with this hardware");
                 return null;
             } catch (RemoteException ex) {
                 mCallback.setProgramListObserver(null, () -> { });
@@ -288,12 +292,20 @@
 
     @Override
     public boolean isAnalogForced() {
-        return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG);
+        try {
+            return isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG);
+        } catch (UnsupportedOperationException ex) {
+            throw new IllegalStateException(ex);
+        }
     }
 
     @Override
     public void setAnalogForced(boolean isForced) {
-        setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced);
+        try {
+            setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, isForced);
+        } catch (UnsupportedOperationException ex) {
+            throw new IllegalStateException(ex);
+        }
     }
 
     @Override
@@ -343,11 +355,7 @@
 
     @Override
     public boolean isAntennaConnected() {
-        try {
-            return mTuner.isAntennaConnected();
-        } catch (RemoteException e) {
-            throw new RuntimeException("service died", e);
-        }
+        return mCallback.isAntennaConnected();
     }
 
     @Override
diff --git a/android/hardware/radio/TunerCallbackAdapter.java b/android/hardware/radio/TunerCallbackAdapter.java
index b299ffe..0fb93e5 100644
--- a/android/hardware/radio/TunerCallbackAdapter.java
+++ b/android/hardware/radio/TunerCallbackAdapter.java
@@ -37,8 +37,12 @@
     @NonNull private final Handler mHandler;
 
     @Nullable ProgramList mProgramList;
-    @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;  // for legacy getProgramList call
+
+    // cache for deprecated methods
+    boolean mIsAntennaConnected = true;
+    @Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
     private boolean mDelayedCompleteCallback = false;
+    @Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
 
     TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
         mCallback = callback;
@@ -49,6 +53,12 @@
         }
     }
 
+    void close() {
+        synchronized (mLock) {
+            if (mProgramList != null) mProgramList.close();
+        }
+    }
+
     void setProgramListObserver(@Nullable ProgramList programList,
             @NonNull ProgramList.OnCloseListener closeListener) {
         Objects.requireNonNull(closeListener);
@@ -92,12 +102,46 @@
         }
     }
 
+    @Nullable RadioManager.ProgramInfo getCurrentProgramInformation() {
+        synchronized (mLock) {
+            return mCurrentProgramInfo;
+        }
+    }
+
+    boolean isAntennaConnected() {
+        return mIsAntennaConnected;
+    }
+
     @Override
     public void onError(int status) {
         mHandler.post(() -> mCallback.onError(status));
     }
 
     @Override
+    public void onTuneFailed(int status, @Nullable ProgramSelector selector) {
+        mHandler.post(() -> mCallback.onTuneFailed(status, selector));
+
+        int errorCode;
+        switch (status) {
+            case RadioManager.STATUS_PERMISSION_DENIED:
+            case RadioManager.STATUS_DEAD_OBJECT:
+                errorCode = RadioTuner.ERROR_SERVER_DIED;
+                break;
+            case RadioManager.STATUS_ERROR:
+            case RadioManager.STATUS_NO_INIT:
+            case RadioManager.STATUS_BAD_VALUE:
+            case RadioManager.STATUS_INVALID_OPERATION:
+                Log.i(TAG, "Got an error with no mapping to the legacy API (" + status
+                        + "), doing a best-effort conversion to ERROR_SCAN_TIMEOUT");
+            // fall through
+            case RadioManager.STATUS_TIMED_OUT:
+            default:
+                errorCode = RadioTuner.ERROR_SCAN_TIMEOUT;
+        }
+        mHandler.post(() -> mCallback.onError(errorCode));
+    }
+
+    @Override
     public void onConfigurationChanged(RadioManager.BandConfig config) {
         mHandler.post(() -> mCallback.onConfigurationChanged(config));
     }
@@ -109,6 +153,10 @@
             return;
         }
 
+        synchronized (mLock) {
+            mCurrentProgramInfo = info;
+        }
+
         mHandler.post(() -> {
             mCallback.onProgramInfoChanged(info);
 
@@ -129,6 +177,7 @@
 
     @Override
     public void onAntennaState(boolean connected) {
+        mIsAntennaConnected = connected;
         mHandler.post(() -> mCallback.onAntennaState(connected));
     }
 
diff --git a/android/hardware/radio/Utils.java b/android/hardware/radio/Utils.java
index f1b5897..9887f78 100644
--- a/android/hardware/radio/Utils.java
+++ b/android/hardware/radio/Utils.java
@@ -56,6 +56,29 @@
         return map;
     }
 
+    static void writeStringIntMap(@NonNull Parcel dest, @Nullable Map<String, Integer> map) {
+        if (map == null) {
+            dest.writeInt(0);
+            return;
+        }
+        dest.writeInt(map.size());
+        for (Map.Entry<String, Integer> entry : map.entrySet()) {
+            dest.writeString(entry.getKey());
+            dest.writeInt(entry.getValue());
+        }
+    }
+
+    static @NonNull Map<String, Integer> readStringIntMap(@NonNull Parcel in) {
+        int size = in.readInt();
+        Map<String, Integer> map = new HashMap<>();
+        while (size-- > 0) {
+            String key = in.readString();
+            int value = in.readInt();
+            map.put(key, value);
+        }
+        return map;
+    }
+
     static <T extends Parcelable> void writeSet(@NonNull Parcel dest, @Nullable Set<T> set) {
         if (set == null) {
             dest.writeInt(0);
diff --git a/android/hardware/soundtrigger/SoundTrigger.java b/android/hardware/soundtrigger/SoundTrigger.java
index b635088..dde8a33 100644
--- a/android/hardware/soundtrigger/SoundTrigger.java
+++ b/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,14 @@
 
 package android.hardware.soundtrigger;
 
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENODEV;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.EPERM;
+import static android.system.OsConstants.EPIPE;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.media.AudioFormat;
 import android.os.Handler;
 import android.os.Parcel;
@@ -25,22 +33,33 @@
 import java.util.Arrays;
 import java.util.UUID;
 
-import static android.system.OsConstants.*;
-
 /**
  * The SoundTrigger class provides access via JNI to the native service managing
  * the sound trigger HAL.
  *
  * @hide
  */
+@SystemApi
 public class SoundTrigger {
 
+    private SoundTrigger() {
+    }
+
+    /**
+     * Status code used when the operation succeeded
+     */
     public static final int STATUS_OK = 0;
+    /** @hide */
     public static final int STATUS_ERROR = Integer.MIN_VALUE;
+    /** @hide */
     public static final int STATUS_PERMISSION_DENIED = -EPERM;
+    /** @hide */
     public static final int STATUS_NO_INIT = -ENODEV;
+    /** @hide */
     public static final int STATUS_BAD_VALUE = -EINVAL;
+    /** @hide */
     public static final int STATUS_DEAD_OBJECT = -EPIPE;
+    /** @hide */
     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
 
     /*****************************************************************************
@@ -48,6 +67,8 @@
      * managed by the native sound trigger service. Each module has a unique
      * ID used to target any API call to this paricular module. Module
      * properties are returned by listModules() method.
+     *
+     * @hide
      ****************************************************************************/
     public static class ModuleProperties implements Parcelable {
         /** Unique module ID provided by the native service */
@@ -187,6 +208,8 @@
      * implementation to detect a particular sound pattern.
      * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
      * sound models.
+     *
+     * @hide
      ****************************************************************************/
     public static class SoundModel {
         /** Undefined sound model type */
@@ -261,6 +284,8 @@
     /*****************************************************************************
      * A Keyphrase describes a key phrase that can be detected by a
      * {@link KeyphraseSoundModel}
+     *
+     * @hide
      ****************************************************************************/
     public static class Keyphrase implements Parcelable {
         /** Unique identifier for this keyphrase */
@@ -382,6 +407,8 @@
      * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
      * It contains data needed by the hardware to detect a certain number of key phrases
      * and the list of corresponding {@link Keyphrase} descriptors.
+     *
+     * @hide
      ****************************************************************************/
     public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
         /** Key phrases in this sound model */
@@ -468,6 +495,8 @@
     /*****************************************************************************
      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
      * patterns.
+     *
+     * @hide
      ****************************************************************************/
     public static class GenericSoundModel extends SoundModel implements Parcelable {
 
@@ -524,52 +553,115 @@
     /**
      *  Modes for key phrase recognition
      */
-    /** Simple recognition of the key phrase */
+
+    /**
+     * Simple recognition of the key phrase
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
-    /** Trigger only if one user is identified */
+    /**
+     * Trigger only if one user is identified
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
-    /** Trigger only if one user is authenticated */
+    /**
+     * Trigger only if one user is authenticated
+     *
+     * @hide
+     */
     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
 
     /**
      *  Status codes for {@link RecognitionEvent}
      */
-    /** Recognition success */
+    /**
+     * Recognition success
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_SUCCESS = 0;
-    /** Recognition aborted (e.g. capture preempted by anotehr use case */
+    /**
+     * Recognition aborted (e.g. capture preempted by anotehr use case
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_ABORT = 1;
-    /** Recognition failure */
+    /**
+     * Recognition failure
+     *
+     * @hide
+     */
     public static final int RECOGNITION_STATUS_FAILURE = 2;
 
     /**
      *  A RecognitionEvent is provided by the
-     *  {@link StatusListener#onRecognition(RecognitionEvent)}
+     *  {@code StatusListener#onRecognition(RecognitionEvent)}
      *  callback upon recognition success or failure.
      */
-    public static class RecognitionEvent implements Parcelable {
-        /** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */
+    public static class RecognitionEvent {
+        /**
+         * Recognition status e.g RECOGNITION_STATUS_SUCCESS
+         *
+         * @hide
+         */
         public final int status;
-        /** Sound Model corresponding to this event callback */
+        /**
+         *
+         * Sound Model corresponding to this event callback
+         *
+         * @hide
+         */
         public final int soundModelHandle;
-        /** True if it is possible to capture audio from this utterance buffered by the hardware */
+        /**
+         * True if it is possible to capture audio from this utterance buffered by the hardware
+         *
+         * @hide
+         */
         public final boolean captureAvailable;
-        /** Audio session ID to be used when capturing the utterance with an AudioRecord
-         * if captureAvailable() is true. */
+        /**
+         * Audio session ID to be used when capturing the utterance with an AudioRecord
+         * if captureAvailable() is true.
+         *
+         * @hide
+         */
         public final int captureSession;
-        /** Delay in ms between end of model detection and start of audio available for capture.
-         * A negative value is possible (e.g. if keyphrase is also available for capture) */
+        /**
+         * Delay in ms between end of model detection and start of audio available for capture.
+         * A negative value is possible (e.g. if keyphrase is also available for capture)
+         *
+         * @hide
+         */
         public final int captureDelayMs;
-        /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+        /**
+         * Duration in ms of audio captured before the start of the trigger. 0 if none.
+         *
+         * @hide
+         */
         public final int capturePreambleMs;
-        /** True if  the trigger (key phrase capture is present in binary data */
+        /**
+         * True if  the trigger (key phrase capture is present in binary data
+         *
+         * @hide
+         */
         public final boolean triggerInData;
-        /** Audio format of either the trigger in event data or to use for capture of the
-          * rest of the utterance */
-        public AudioFormat captureFormat;
-        /** Opaque data for use by system applications who know about voice engine internals,
-         * typically during enrollment. */
+        /**
+         * Audio format of either the trigger in event data or to use for capture of the
+         * rest of the utterance
+         *
+         * @hide
+         */
+        public final AudioFormat captureFormat;
+        /**
+         * Opaque data for use by system applications who know about voice engine internals,
+         * typically during enrollment.
+         *
+         * @hide
+         */
         public final byte[] data;
 
+        /** @hide */
         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                 int captureSession, int captureDelayMs, int capturePreambleMs,
                 boolean triggerInData, AudioFormat captureFormat, byte[] data) {
@@ -584,6 +676,46 @@
             this.data = data;
         }
 
+        /**
+         * Check if is possible to capture audio from this utterance buffered by the hardware.
+         *
+         * @return {@code true} iff a capturing is possible
+         */
+        public boolean isCaptureAvailable() {
+            return captureAvailable;
+        }
+
+        /**
+         * Get the audio format of either the trigger in event data or to use for capture of the
+         * rest of the utterance
+         *
+         * @return the audio format
+         */
+        @Nullable public AudioFormat getCaptureFormat() {
+            return captureFormat;
+        }
+
+        /**
+         * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
+         * if {@link #isCaptureAvailable()} is true.
+         *
+         * @return The id of the capture session
+         */
+        public int getCaptureSession() {
+            return captureSession;
+        }
+
+        /**
+         * Get the opaque data for use by system applications who know about voice engine
+         * internals, typically during enrollment.
+         *
+         * @return The data of the event
+         */
+        public byte[] getData() {
+            return data;
+        }
+
+        /** @hide */
         public static final Parcelable.Creator<RecognitionEvent> CREATOR
                 = new Parcelable.Creator<RecognitionEvent>() {
             public RecognitionEvent createFromParcel(Parcel in) {
@@ -595,6 +727,7 @@
             }
         };
 
+        /** @hide */
         protected static RecognitionEvent fromParcel(Parcel in) {
             int status = in.readInt();
             int soundModelHandle = in.readInt();
@@ -619,12 +752,12 @@
                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
         }
 
-        @Override
+        /** @hide */
         public int describeContents() {
             return 0;
         }
 
-        @Override
+        /** @hide */
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(status);
             dest.writeInt(soundModelHandle);
@@ -726,6 +859,8 @@
      *  A RecognitionConfig is provided to
      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
      *  recognition request.
+     *
+     *  @hide
      */
     public static class RecognitionConfig implements Parcelable {
         /** True if the DSP should capture the trigger sound and make it available for further
@@ -744,7 +879,7 @@
         public final byte[] data;
 
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                KeyphraseRecognitionExtra keyphrases[], byte[] data) {
+                KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
             this.captureRequested = captureRequested;
             this.allowMultipleTriggers = allowMultipleTriggers;
             this.keyphrases = keyphrases;
@@ -799,6 +934,8 @@
      * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
      * should trigger a recognition.
      * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
+     *
+     * @hide
      */
     public static class ConfidenceLevel implements Parcelable {
         public final int userId;
@@ -872,6 +1009,8 @@
     /**
      *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
      *  for a key phrase detection.
+     *
+     * @hide
      */
     public static class KeyphraseRecognitionExtra implements Parcelable {
         /** The keyphrase ID */
@@ -970,8 +1109,10 @@
 
     /**
      *  Specialized {@link RecognitionEvent} for a key phrase detection.
+     *
+     *  @hide
      */
-    public static class KeyphraseRecognitionEvent extends RecognitionEvent {
+    public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
         public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
@@ -1091,8 +1232,10 @@
     /**
      * Sub-class of RecognitionEvent specifically for sound-trigger based sound
      * models(non-keyphrase). Currently does not contain any additional fields.
+     *
+     * @hide
      */
-    public static class GenericRecognitionEvent extends RecognitionEvent {
+    public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
         public GenericRecognitionEvent(int status, int soundModelHandle,
                 boolean captureAvailable, int captureSession, int captureDelayMs,
                 int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
@@ -1140,13 +1283,19 @@
     /**
      *  Status codes for {@link SoundModelEvent}
      */
-    /** Sound Model was updated */
+    /**
+     * Sound Model was updated
+     *
+     * @hide
+     */
     public static final int SOUNDMODEL_STATUS_UPDATED = 0;
 
     /**
      *  A SoundModelEvent is provided by the
      *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
      *  callback when a sound model has been updated by the implementation
+     *
+     *  @hide
      */
     public static class SoundModelEvent implements Parcelable {
         /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
@@ -1231,9 +1380,17 @@
      *  Native service state. {@link StatusListener#onServiceStateChange(int)}
      */
     // Keep in sync with system/core/include/system/sound_trigger.h
-    /** Sound trigger service is enabled */
+    /**
+     * Sound trigger service is enabled
+     *
+     * @hide
+     */
     public static final int SERVICE_STATE_ENABLED = 0;
-    /** Sound trigger service is disabled */
+    /**
+     * Sound trigger service is disabled
+     *
+     * @hide
+     */
     public static final int SERVICE_STATE_DISABLED = 1;
 
     /**
@@ -1245,6 +1402,8 @@
      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
      *         - {@link #STATUS_BAD_VALUE} if modules is null
      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+     *
+     * @hide
      */
     public static native int listModules(ArrayList <ModuleProperties> modules);
 
@@ -1256,6 +1415,8 @@
      * @param handler the Handler that will receive the callabcks. Can be null if default handler
      *                is OK.
      * @return a valid sound module in case of success or null in case of error.
+     *
+     * @hide
      */
     public static SoundTriggerModule attachModule(int moduleId,
                                                   StatusListener listener,
@@ -1270,6 +1431,8 @@
     /**
      * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
      * to received recognition and error notifications.
+     *
+     * @hide
      */
     public static interface StatusListener {
         /**
diff --git a/android/hardware/usb/AccessoryFilter.java b/android/hardware/usb/AccessoryFilter.java
index d9b7c5b..00070fe 100644
--- a/android/hardware/usb/AccessoryFilter.java
+++ b/android/hardware/usb/AccessoryFilter.java
@@ -16,6 +16,11 @@
 
 package android.hardware.usb;
 
+import android.annotation.NonNull;
+import android.service.usb.UsbAccessoryFilterProto;
+
+import com.android.internal.util.dump.DualDumpOutputStream;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -142,4 +147,17 @@
                 "\", mModel=\"" + mModel +
                 "\", mVersion=\"" + mVersion + "\"]";
     }
+
+    /**
+     * Write a description of the filter to a dump stream.
+     */
+    public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+        long token = dump.start(idName, id);
+
+        dump.write("manufacturer", UsbAccessoryFilterProto.MANUFACTURER, mManufacturer);
+        dump.write("model", UsbAccessoryFilterProto.MODEL, mModel);
+        dump.write("version", UsbAccessoryFilterProto.VERSION, mVersion);
+
+        dump.end(token);
+    }
 }
diff --git a/android/hardware/usb/DeviceFilter.java b/android/hardware/usb/DeviceFilter.java
index 439c629..6f1aff7 100644
--- a/android/hardware/usb/DeviceFilter.java
+++ b/android/hardware/usb/DeviceFilter.java
@@ -16,8 +16,12 @@
 
 package android.hardware.usb;
 
+import android.annotation.NonNull;
+import android.service.usb.UsbDeviceFilterProto;
 import android.util.Slog;
 
+import com.android.internal.util.dump.DualDumpOutputStream;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -310,4 +314,22 @@
                 ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
                 "]";
     }
+
+    /**
+     * Write a description of the filter to a dump stream.
+     */
+    public void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+        long token = dump.start(idName, id);
+
+        dump.write("vendor_id", UsbDeviceFilterProto.VENDOR_ID, mVendorId);
+        dump.write("product_id", UsbDeviceFilterProto.PRODUCT_ID, mProductId);
+        dump.write("class", UsbDeviceFilterProto.CLASS, mClass);
+        dump.write("subclass", UsbDeviceFilterProto.SUBCLASS, mSubclass);
+        dump.write("protocol", UsbDeviceFilterProto.PROTOCOL, mProtocol);
+        dump.write("manufacturer_name", UsbDeviceFilterProto.MANUFACTURER_NAME, mManufacturerName);
+        dump.write("product_name", UsbDeviceFilterProto.PRODUCT_NAME, mProductName);
+        dump.write("serial_number", UsbDeviceFilterProto.SERIAL_NUMBER, mSerialNumber);
+
+        dump.end(token);
+    }
 }
diff --git a/android/hardware/usb/UsbConfiguration.java b/android/hardware/usb/UsbConfiguration.java
index a171570..6ce4201 100644
--- a/android/hardware/usb/UsbConfiguration.java
+++ b/android/hardware/usb/UsbConfiguration.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+
 import com.android.internal.util.Preconditions;
 
 /**
@@ -106,6 +107,17 @@
     }
 
     /**
+     * Returns the attributes of this configuration
+     *
+     * @return the configuration's attributes
+     *
+     * @hide
+     */
+    public int getAttributes() {
+        return mAttributes;
+    }
+
+    /**
      * Returns the configuration's max power consumption, in milliamps.
      *
      * @return the configuration's max power
diff --git a/android/hardware/usb/UsbConstants.java b/android/hardware/usb/UsbConstants.java
index 0e8d47c..215e9d5 100644
--- a/android/hardware/usb/UsbConstants.java
+++ b/android/hardware/usb/UsbConstants.java
@@ -16,6 +16,8 @@
 
 package android.hardware.usb;
 
+import android.service.ServiceProtoEnums;
+
 /**
  * Contains constants for the USB protocol.
  * These constants correspond to definitions in linux/usb/ch9.h in the linux kernel.
@@ -35,12 +37,12 @@
      * Used to signify direction of data for a {@link UsbEndpoint} is OUT (host to device)
      * @see UsbEndpoint#getDirection
      */
-    public static final int USB_DIR_OUT = 0;
+    public static final int USB_DIR_OUT = ServiceProtoEnums.USB_ENDPOINT_DIR_OUT; // 0
     /**
      * Used to signify direction of data for a {@link UsbEndpoint} is IN (device to host)
      * @see UsbEndpoint#getDirection
      */
-    public static final int USB_DIR_IN = 0x80;
+    public static final int USB_DIR_IN = ServiceProtoEnums.USB_ENDPOINT_DIR_IN; // 0x80
 
     /**
      * Bitmask used for extracting the {@link UsbEndpoint} number its address field.
@@ -63,22 +65,26 @@
      * Control endpoint type (endpoint zero)
      * @see UsbEndpoint#getType
      */
-    public static final int USB_ENDPOINT_XFER_CONTROL = 0;
+    public static final int USB_ENDPOINT_XFER_CONTROL =
+            ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_CONTROL; // 0
     /**
      * Isochronous endpoint type (currently not supported)
      * @see UsbEndpoint#getType
      */
-    public static final int USB_ENDPOINT_XFER_ISOC = 1;
+    public static final int USB_ENDPOINT_XFER_ISOC =
+            ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_ISOC; // 1
     /**
      * Bulk endpoint type
      * @see UsbEndpoint#getType
      */
-    public static final int USB_ENDPOINT_XFER_BULK = 2;
+    public static final int USB_ENDPOINT_XFER_BULK =
+            ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_BULK; // 2
     /**
      * Interrupt endpoint type
      * @see UsbEndpoint#getType
      */
-    public static final int USB_ENDPOINT_XFER_INT = 3;
+    public static final int USB_ENDPOINT_XFER_INT =
+            ServiceProtoEnums.USB_ENDPOINT_TYPE_XFER_INT; // 3
 
 
     /**
diff --git a/android/hardware/usb/UsbDeviceConnection.java b/android/hardware/usb/UsbDeviceConnection.java
index 5b15c0d..9e5174a 100644
--- a/android/hardware/usb/UsbDeviceConnection.java
+++ b/android/hardware/usb/UsbDeviceConnection.java
@@ -222,7 +222,10 @@
      * @param endpoint the endpoint for this transaction
      * @param buffer buffer for data to send or receive; can be {@code null} to wait for next
      *               transaction without reading data
-     * @param length the length of the data to send or receive
+     * @param length the length of the data to send or receive. Before
+     *               {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes
+     *               would be truncated down to 16384. In API {@value Build.VERSION_CODES#P}
+     *               and after, any value of length is valid.
      * @param timeout in milliseconds, 0 is infinite
      * @return length of data transferred (or zero) for success,
      * or negative value for failure
@@ -239,7 +242,10 @@
      * @param endpoint the endpoint for this transaction
      * @param buffer buffer for data to send or receive
      * @param offset the index of the first byte in the buffer to send or receive
-     * @param length the length of the data to send or receive
+     * @param length the length of the data to send or receive. Before
+     *               {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes
+     *               would be truncated down to 16384. In API {@value Build.VERSION_CODES#P}
+     *               and after, any value of length is valid.
      * @param timeout in milliseconds, 0 is infinite
      * @return length of data transferred (or zero) for success,
      * or negative value for failure
@@ -247,6 +253,10 @@
     public int bulkTransfer(UsbEndpoint endpoint,
             byte[] buffer, int offset, int length, int timeout) {
         checkBounds(buffer, offset, length);
+        if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+                && length > UsbRequest.MAX_USBFS_BUFFER_SIZE) {
+            length = UsbRequest.MAX_USBFS_BUFFER_SIZE;
+        }
         return native_bulk_request(endpoint.getAddress(), buffer, offset, length, timeout);
     }
 
diff --git a/android/hardware/usb/UsbManager.java b/android/hardware/usb/UsbManager.java
index 7617c2b..46142e3 100644
--- a/android/hardware/usb/UsbManager.java
+++ b/android/hardware/usb/UsbManager.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -27,7 +28,9 @@
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.gadget.V1_0.GadgetFunction;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -37,6 +40,8 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
+import java.util.Map;
+import java.util.StringJoiner;
 
 /**
  * This class allows you to access the state of USB and communicate with USB devices.
@@ -70,7 +75,7 @@
      * MTP function is enabled
      * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
      * PTP function is enabled
-     * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
+     * <li> {@link #USB_FUNCTION_ACCESSORY} boolean extra indicating whether the
      * accessory function is enabled
      * <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
      * audio source function is enabled
@@ -187,17 +192,8 @@
     public static final String USB_DATA_UNLOCKED = "unlocked";
 
     /**
-     * Boolean extra indicating whether the intent represents a change in the usb
-     * configuration (as opposed to a state update).
-     *
-     * {@hide}
-     */
-    public static final String USB_CONFIG_CHANGED = "config_changed";
-
-    /**
      * A placeholder indicating that no USB function is being specified.
-     * Used to distinguish between selecting no function vs. the default function in
-     * {@link #setCurrentFunction(String)}.
+     * Used for compatibility with old init scripts to indicate no functions vs. charging function.
      *
      * {@hide}
      */
@@ -298,6 +294,69 @@
      */
     public static final String EXTRA_PERMISSION_GRANTED = "permission";
 
+    /**
+     * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_NONE = 0;
+
+    /**
+     * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_MTP = GadgetFunction.MTP;
+
+    /**
+     * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_PTP = GadgetFunction.PTP;
+
+    /**
+     * Code for the rndis usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS;
+
+    /**
+     * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * {@hide}
+     */
+    public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
+
+    /**
+     * Code for the accessory usb function.
+     * {@hide}
+     */
+    public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
+
+    /**
+     * Code for the audio source usb function.
+     * {@hide}
+     */
+    public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
+
+    /**
+     * Code for the adb usb function.
+     * {@hide}
+     */
+    public static final long FUNCTION_ADB = GadgetFunction.ADB;
+
+    private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
+            | FUNCTION_MIDI;
+
+    private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
+
+    static {
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MTP, FUNCTION_MTP);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_PTP, FUNCTION_PTP);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_RNDIS, FUNCTION_RNDIS);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_MIDI, FUNCTION_MIDI);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
+    }
+
     private final Context mContext;
     private final IUsbManager mService;
 
@@ -317,6 +376,7 @@
      *
      * @return HashMap containing all connected USB devices.
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_HOST)
     public HashMap<String,UsbDevice> getDeviceList() {
         HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
         if (mService == null) {
@@ -341,6 +401,7 @@
      * @param device the device to open
      * @return a {@link UsbDeviceConnection}, or {@code null} if open failed
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_HOST)
     public UsbDeviceConnection openDevice(UsbDevice device) {
         try {
             String deviceName = device.getDeviceName();
@@ -365,6 +426,7 @@
      *
      * @return list of USB accessories, or null if none are attached.
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
     public UsbAccessory[] getAccessoryList() {
         if (mService == null) {
             return null;
@@ -384,9 +446,14 @@
     /**
      * Opens a file descriptor for reading and writing data to the USB accessory.
      *
+     * <p>If data is read from the {@link java.io.InputStream} created from this file descriptor all
+     * data of a USB transfer should be read at once. If only a partial request is read the rest of
+     * the transfer is dropped.
+     *
      * @param accessory the USB accessory to open
-     * @return file descriptor, or null if the accessor could not be opened.
+     * @return file descriptor, or null if the accessory could not be opened.
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
         try {
             return mService.openAccessory(accessory);
@@ -396,6 +463,25 @@
     }
 
     /**
+     * Gets the functionfs control file descriptor for the given function, with
+     * the usb descriptors and strings already written. The file descriptor is used
+     * by the function implementation to handle events and control requests.
+     *
+     * @param function to get control fd for. Currently {@link #FUNCTION_MTP} and
+     * {@link #FUNCTION_PTP} are supported.
+     * @return A ParcelFileDescriptor holding the valid fd, or null if the fd was not found.
+     *
+     * {@hide}
+     */
+    public ParcelFileDescriptor getControlFd(long function) {
+        try {
+            return mService.getControlFd(function);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns true if the caller has permission to access the device.
      * Permission might have been granted temporarily via
      * {@link #requestPermission(UsbDevice, PendingIntent)} or
@@ -407,6 +493,7 @@
      * @param device to check permissions for
      * @return true if caller has permission
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_HOST)
     public boolean hasPermission(UsbDevice device) {
         if (mService == null) {
             return false;
@@ -427,6 +514,7 @@
      * @param accessory to check permissions for
      * @return true if caller has permission
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
     public boolean hasPermission(UsbAccessory accessory) {
         if (mService == null) {
             return false;
@@ -460,6 +548,7 @@
      * @param device to request permissions for
      * @param pi PendingIntent for returning result
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_HOST)
     public void requestPermission(UsbDevice device, PendingIntent pi) {
         try {
             mService.requestDevicePermission(device, mContext.getPackageName(), pi);
@@ -486,6 +575,7 @@
      * @param accessory to request permissions for
      * @param pi PendingIntent for returning result
      */
+    @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
     public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
         try {
             mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
@@ -548,15 +638,14 @@
      * services offered by the device.
      * </p>
      *
+     * @deprecated use getCurrentFunctions() instead.
      * @param function name of the USB function
      * @return true if the USB function is enabled
      *
      * {@hide}
      */
+    @Deprecated
     public boolean isFunctionEnabled(String function) {
-        if (mService == null) {
-            return false;
-        }
         try {
             return mService.isFunctionEnabled(function);
         } catch (RemoteException e) {
@@ -565,7 +654,7 @@
     }
 
     /**
-     * Sets the current USB function when in device mode.
+     * Sets the current USB functions when in device mode.
      * <p>
      * USB functions represent interfaces which are published to the host to access
      * services offered by the device.
@@ -574,27 +663,59 @@
      * automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
      * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
      * </p><p>
-     * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE},
-     * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
-     * or {@link #USB_FUNCTION_RNDIS}.
-     * </p><p>
-     * Also sets whether USB data (for example, MTP exposed pictures) should be made available
-     * on the USB connection when in device mode. Unlocking usb data should only be done with
-     * user involvement, since exposing pictures or other data could leak sensitive
-     * user information.
+     * An argument of 0 indicates that the device is charging, and can pick any
+     * appropriate function for that purpose.
      * </p><p>
      * Note: This function is asynchronous and may fail silently without applying
      * the requested changes.
      * </p>
      *
-     * @param function name of the USB function, or null to restore the default function
-     * @param usbDataUnlocked whether user data is accessible
+     * @param functions the USB function(s) to set, as a bitwise mask.
+     *                  Must satisfy {@link UsbManager#areSettableFunctions}
      *
      * {@hide}
      */
-    public void setCurrentFunction(String function, boolean usbDataUnlocked) {
+    public void setCurrentFunctions(long functions) {
         try {
-            mService.setCurrentFunction(function, usbDataUnlocked);
+            mService.setCurrentFunctions(functions);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the current USB functions when in device mode.
+     *
+     * @deprecated use setCurrentFunctions(long) instead.
+     * @param functions the USB function(s) to set.
+     * @param usbDataUnlocked unused
+
+     * {@hide}
+     */
+    @Deprecated
+    public void setCurrentFunction(String functions, boolean usbDataUnlocked) {
+        try {
+            mService.setCurrentFunction(functions, usbDataUnlocked);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current USB functions in device mode.
+     * <p>
+     * This function returns the state of primary USB functions and can return a
+     * mask containing any usb function(s) except for ADB.
+     * </p>
+     *
+     * @return The currently enabled functions, in a bitwise mask.
+     * A zero mask indicates that the current function is the charging function.
+     *
+     * {@hide}
+     */
+    public long getCurrentFunctions() {
+        try {
+            return mService.getCurrentFunctions();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -604,23 +725,37 @@
      * Sets the screen unlocked functions, which are persisted and set as the current functions
      * whenever the screen is unlocked.
      * <p>
-     * The allowed values are: {@link #USB_FUNCTION_NONE},
-     * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
-     * or {@link #USB_FUNCTION_RNDIS}.
-     * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions
+     * A zero mask has the effect of switching off this feature, so functions
      * no longer change on screen unlock.
      * </p><p>
      * Note: When the screen is on, this method will apply given functions as current functions,
      * which is asynchronous and may fail silently without applying the requested changes.
      * </p>
      *
-     * @param function function to set as default
+     * @param functions functions to set, in a bitwise mask.
+     *                  Must satisfy {@link UsbManager#areSettableFunctions}
      *
      * {@hide}
      */
-    public void setScreenUnlockedFunctions(String function) {
+    public void setScreenUnlockedFunctions(long functions) {
         try {
-            mService.setScreenUnlockedFunctions(function);
+            mService.setScreenUnlockedFunctions(functions);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the current screen unlocked functions.
+     *
+     * @return The currently set screen enabled functions.
+     * A zero mask indicates that the screen unlocked functions feature is not enabled.
+     *
+     * {@hide}
+     */
+    public long getScreenUnlockedFunctions() {
+        try {
+            return mService.getScreenUnlockedFunctions();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -719,51 +854,71 @@
         }
     }
 
-    /** @hide */
-    public static String addFunction(String functions, String function) {
-        if (USB_FUNCTION_NONE.equals(functions)) {
-            return function;
-        }
-        if (!containsFunction(functions, function)) {
-            if (functions.length() > 0) {
-                functions += ",";
-            }
-            functions += function;
-        }
-        return functions;
+    /**
+     * Returns whether the given functions are valid inputs to UsbManager.
+     * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+     *
+     * @return Whether the mask is settable.
+     *
+     * {@hide}
+     */
+    public static boolean areSettableFunctions(long functions) {
+        return functions == FUNCTION_NONE
+                || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
     }
 
-    /** @hide */
-    public static String removeFunction(String functions, String function) {
-        String[] split = functions.split(",");
-        for (int i = 0; i < split.length; i++) {
-            if (function.equals(split[i])) {
-                split[i] = null;
-            }
+    /**
+     * Converts the given function mask to string. Maintains ordering with respect to init scripts.
+     *
+     * @return String representation of given mask
+     *
+     * {@hide}
+     */
+    public static String usbFunctionsToString(long functions) {
+        StringJoiner joiner = new StringJoiner(",");
+        if ((functions & FUNCTION_MTP) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_MTP);
         }
-        if (split.length == 1 && split[0] == null) {
-            return USB_FUNCTION_NONE;
+        if ((functions & FUNCTION_PTP) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_PTP);
         }
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < split.length; i++) {
-            String s = split[i];
-            if (s != null) {
-                if (builder.length() > 0) {
-                    builder.append(",");
-                }
-                builder.append(s);
-            }
+        if ((functions & FUNCTION_RNDIS) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_RNDIS);
         }
-        return builder.toString();
+        if ((functions & FUNCTION_MIDI) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_MIDI);
+        }
+        if ((functions & FUNCTION_ACCESSORY) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_ACCESSORY);
+        }
+        if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+        }
+        if ((functions & FUNCTION_ADB) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_ADB);
+        }
+        return joiner.toString();
     }
 
-    /** @hide */
-    public static boolean containsFunction(String functions, String function) {
-        int index = functions.indexOf(function);
-        if (index < 0) return false;
-        if (index > 0 && functions.charAt(index - 1) != ',') return false;
-        int charAfter = index + function.length();
-        if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
-        return true;
+    /**
+     * Parses a string of usb functions that are comma separated.
+     *
+     * @return A mask of all valid functions in the string
+     *
+     * {@hide}
+     */
+    public static long usbFunctionsFromString(String functions) {
+        if (functions == null || functions.equals(USB_FUNCTION_NONE)) {
+            return FUNCTION_NONE;
+        }
+        long ret = 0;
+        for (String function : functions.split(",")) {
+            if (FUNCTION_NAME_TO_CODE.containsKey(function)) {
+                ret |= FUNCTION_NAME_TO_CODE.get(function);
+            } else if (function.length() > 0) {
+                throw new IllegalArgumentException("Invalid usb function " + functions);
+            }
+        }
+        return ret;
     }
 }
diff --git a/android/hardware/usb/UsbRequest.java b/android/hardware/usb/UsbRequest.java
index 2e8f8e1..f59c87e 100644
--- a/android/hardware/usb/UsbRequest.java
+++ b/android/hardware/usb/UsbRequest.java
@@ -17,6 +17,7 @@
 package android.hardware.usb;
 
 import android.annotation.Nullable;
+import android.os.Build;
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
@@ -43,7 +44,7 @@
     private static final String TAG = "UsbRequest";
 
     // From drivers/usb/core/devio.c
-    private static final int MAX_USBFS_BUFFER_SIZE = 16384;
+    static final int MAX_USBFS_BUFFER_SIZE = 16384;
 
     // used by the JNI code
     private long mNativeContext;
@@ -175,7 +176,9 @@
      *               capacity will be ignored. Once the request
      *               {@link UsbDeviceConnection#requestWait() is processed} the position will be set
      *               to the number of bytes read/written.
-     * @param length number of bytes to read or write.
+     * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a
+     *               value larger than 16384 bytes would be truncated down to 16384. In API
+     *               {@value Build.VERSION_CODES#P} and after, any value of length is valid.
      *
      * @return true if the queueing operation succeeded
      *
@@ -186,6 +189,11 @@
         boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
         boolean result;
 
+        if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+                && length > MAX_USBFS_BUFFER_SIZE) {
+            length = MAX_USBFS_BUFFER_SIZE;
+        }
+
         synchronized (mLock) {
             // save our buffer for when the request has completed
             mBuffer = buffer;
@@ -222,7 +230,10 @@
      *               of the buffer is undefined until the request is returned by
      *               {@link UsbDeviceConnection#requestWait}. If the request failed the buffer
      *               will be unchanged; if the request succeeded the position of the buffer is
-     *               incremented by the number of bytes sent/received.
+     *               incremented by the number of bytes sent/received. Before
+     *               {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes
+     *               would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P}
+     *               and after, any size buffer is valid.
      *
      * @return true if the queueing operation succeeded
      */
@@ -244,9 +255,12 @@
                 mIsUsingNewQueue = true;
                 wasQueued = native_queue(null, 0, 0);
             } else {
-                // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
-                Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
-                        "number of remaining bytes");
+                if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+                        < Build.VERSION_CODES.P) {
+                    // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
+                    Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
+                            "number of remaining bytes");
+                }
 
                 // Can not receive into read-only buffers.
                 Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be "
diff --git a/android/inputmethodservice/InputMethodService.java b/android/inputmethodservice/InputMethodService.java
index 7528bc3..b4c8a5e 100644
--- a/android/inputmethodservice/InputMethodService.java
+++ b/android/inputmethodservice/InputMethodService.java
@@ -20,6 +20,8 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
@@ -239,19 +241,89 @@
     static final boolean DEBUG = false;
 
     /**
-     * The back button will close the input window.
+     * Allows the system to optimize the back button affordance based on the presence of software
+     * keyboard.
+     *
+     * <p>For instance, on devices that have navigation bar and software-rendered back button, the
+     * system may use a different icon while {@link #isInputViewShown()} returns {@code true}, to
+     * indicate that the back button has "dismiss" affordance.</p>
+     *
+     * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
+     * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
+     * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
+     * not take this mode into account.</p>
+     *
+     * <p>For API level {@link android.os.Build.VERSION_CODES#O_MR1} and lower devices, this is the
+     * only mode you can safely specify without worrying about the compatibility.</p>
+     *
+     * @see #setBackDisposition(int)
      */
-    public static final int BACK_DISPOSITION_DEFAULT = 0;  // based on window
+    public static final int BACK_DISPOSITION_DEFAULT = 0;
 
     /**
-     * This input method will not consume the back key.
+     * Deprecated flag.
+     *
+     * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
+     *
+     * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
+     *             handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
+     *             {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
+     *             of this mode had not been well defined. Most likely the end result would be the
+     *             same as {@link #BACK_DISPOSITION_DEFAULT}. Either way it is not recommended to
+     *             use this mode
+     * @see #setBackDisposition(int)
      */
-    public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // back
+    @Deprecated
+    public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1;
 
     /**
-     * This input method will consume the back key.
+     * Deprecated flag.
+     *
+     * <p>To avoid compatibility issues, IME developers should not use this flag.</p>
+     *
+     * @deprecated on {@link android.os.Build.VERSION_CODES#P} and later devices, this flag is
+     *             handled as a synonym of {@link #BACK_DISPOSITION_DEFAULT}. On
+     *             {@link android.os.Build.VERSION_CODES#O_MR1} and prior devices, expected behavior
+     *             of this mode had not been well defined. In AOSP implementation running on devices
+     *             that have navigation bar, specifying this flag could change the software back
+     *             button to "Dismiss" icon no matter whether the software keyboard is shown or not,
+     *             but there would be no easy way to restore the icon state even after IME lost the
+     *             connection to the application. To avoid user confusions, do not specify this mode
+     *             anyway
+     * @see #setBackDisposition(int)
      */
-    public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // down
+    @Deprecated
+    public static final int BACK_DISPOSITION_WILL_DISMISS = 2;
+
+    /**
+     * Asks the system to not adjust the back button affordance even when the software keyboard is
+     * shown.
+     *
+     * <p>This mode is useful for UI modes where IME's main soft input window is used for some
+     * supplemental UI, such as floating candidate window for languages such as Chinese and
+     * Japanese, where users expect the back button is, or at least looks to be, handled by the
+     * target application rather than the UI shown by the IME even while {@link #isInputViewShown()}
+     * returns {@code true}.</p>
+     *
+     * <p>Note that {@link KeyEvent#KEYCODE_BACK} events continue to be sent to
+     * {@link #onKeyDown(int, KeyEvent)} even when this mode is specified. The default
+     * implementation of {@link #onKeyDown(int, KeyEvent)} for {@link KeyEvent#KEYCODE_BACK} does
+     * not take this mode into account.</p>
+     *
+     * @see #setBackDisposition(int)
+     */
+    public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
+
+    /**
+     * Enum flag to be used for {@link #setBackDisposition(int)}.
+     *
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
+            BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
+            prefix = "BACK_DISPOSITION_")
+    public @interface BackDispositionMode {}
 
     /**
      * @hide
@@ -265,6 +337,10 @@
      */
     public static final int IME_VISIBLE = 0x2;
 
+    // Min and max values for back disposition.
+    private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
+    private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
+
     InputMethodManager mImm;
     
     int mTheme = 0;
@@ -327,6 +403,8 @@
     boolean mIsInputViewShown;
     
     int mStatusIcon;
+
+    @BackDispositionMode
     int mBackDisposition;
 
     /**
@@ -501,9 +579,8 @@
             }
             clearInsetOfPreviousIme();
             // If user uses hard keyboard, IME button should always be shown.
-            boolean showing = isInputViewShown();
             mImm.setImeWindowStatus(mToken, mStartInputToken,
-                    IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+                    mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
             if (resultReceiver != null) {
                 resultReceiver.send(wasVis != isInputViewShown()
                         ? InputMethodManager.RESULT_SHOWN
@@ -1012,11 +1089,38 @@
     public Dialog getWindow() {
         return mWindow;
     }
-    
-    public void setBackDisposition(int disposition) {
+
+    /**
+     * Sets the disposition mode that indicates the expected affordance for the back button.
+     *
+     * <p>Keep in mind that specifying this flag does not change the the default behavior of
+     * {@link #onKeyDown(int, KeyEvent)}.  It is IME developers' responsibility for making sure that
+     * their custom implementation of {@link #onKeyDown(int, KeyEvent)} is consistent with the mode
+     * specified to this API.</p>
+     *
+     * @see #getBackDisposition()
+     * @param disposition disposition mode to be set
+     */
+    public void setBackDisposition(@BackDispositionMode int disposition) {
+        if (disposition == mBackDisposition) {
+            return;
+        }
+        if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
+            Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
+            return;
+        }
         mBackDisposition = disposition;
+        mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
+                mBackDisposition);
     }
 
+    /**
+     * Retrieves the current disposition mode that indicates the expected back button affordance.
+     *
+     * @see #setBackDisposition(int)
+     * @return currently selected disposition mode
+     */
+    @BackDispositionMode
     public int getBackDisposition() {
         return mBackDisposition;
     }
@@ -1063,33 +1167,14 @@
     }
 
     /**
-     * Force switch to a new input method component. This can only be called
-     * from an application or a service which has a token of the currently active input method.
-     * @param id The unique identifier for the new input method to be switched to.
-     */
-    public void setInputMethod(String id) {
-        mImm.setInputMethodInternal(mToken, id);
-    }
-
-    /**
-     * Force switch to a new input method and subtype. This can only be called
-     * from an application or a service which has a token of the currently active input method.
-     * @param id The unique identifier for the new input method to be switched to.
-     * @param subtype The new subtype of the new input method to be switched to.
-     */
-    public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
-        mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype);
-    }
-
-    /**
      * Force switch to the last used input method and subtype. If the last input method didn't have
      * any subtypes, the framework will simply switch to the last input method with no subtype
      * specified.
      * @return true if the current input method and subtype was successfully switched to the last
      * used input method and subtype.
      */
-    public boolean switchToLastInputMethod() {
-        return mImm.switchToLastInputMethodInternal(mToken);
+    public final boolean switchToPreviousInputMethod() {
+        return mImm.switchToPreviousInputMethodInternal(mToken);
     }
 
     /**
@@ -1100,7 +1185,7 @@
      * @return true if the current input method and subtype was successfully switched to the next
      * input method and subtype.
      */
-    public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
+    public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
         return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme);
     }
 
@@ -1113,7 +1198,7 @@
      * and subtype in order to provide the consistent user experience in switching
      * between IMEs and subtypes.
      */
-    public boolean shouldOfferSwitchingToNextInputMethod() {
+    public final boolean shouldOfferSwitchingToNextInputMethod() {
         return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken);
     }
 
@@ -1443,12 +1528,24 @@
      * input method will be destroyed, and the requested one started on the
      * current input field.
      * 
-     * @param id Unique identifier of the new input method ot start.
+     * @param id Unique identifier of the new input method to start.
      */
     public void switchInputMethod(String id) {
         mImm.setInputMethodInternal(mToken, id);
     }
 
+    /**
+     * Force switch to a new input method, as identified by {@code id}.  This
+     * input method will be destroyed, and the requested one started on the
+     * current input field.
+     *
+     * @param id Unique identifier of the new input method to start.
+     * @param subtype The new subtype of the new input method to be switched to.
+     */
+    public final void switchInputMethod(String id, InputMethodSubtype subtype) {
+        mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype);
+    }
+
     public void setExtractView(View view) {
         mExtractFrame.removeAllViews();
         mExtractFrame.addView(view, new FrameLayout.LayoutParams(
@@ -1762,7 +1859,7 @@
             startExtractingText(false);
         }
 
-        final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
+        final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
         if (previousImeWindowStatus != nextImeWindowStatus) {
             mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
                     mBackDisposition);
@@ -1806,9 +1903,9 @@
     }
 
     /**
-     * Called when the input method window has been shown to the user, after
-     * previously not being visible.  This is done after all of the UI setup
-     * for the window has occurred (creating its views etc).
+     * Called immediately before the input method window is shown to the user.
+     * You could override this to prepare for the window to be shown
+     * (update view structure etc).
      */
     public void onWindowShown() {
         // Intentionally empty
@@ -2053,7 +2150,7 @@
      * @see InputMethodManager#SHOW_FORCED
      * @param flags Provides additional operating flags.
      */
-    public void requestShowSelf(int flags) {
+    public final void requestShowSelf(int flags) {
         mImm.showSoftInputFromInputMethodInternal(mToken, flags);
     }
 
@@ -2090,18 +2187,28 @@
         return mExtractEditText;
     }
 
+
     /**
-     * Override this to intercept key down events before they are processed by the
-     * application.  If you return true, the application will not 
-     * process the event itself.  If you return false, the normal application processing
-     * will occur as if the IME had not seen the event at all.
-     * 
-     * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
-     * KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
-     * possibly hide it when the key goes up (if not canceled or long pressed).  In
-     * addition, in fullscreen mode only, it will consume DPAD movement
-     * events to move the cursor in the extracted text view, not allowing
-     * them to perform navigation in the underlying application.
+     * Called back when a {@link KeyEvent} is forwarded from the target application.
+     *
+     * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK} if the IME is
+     * currently shown , to possibly hide it when the key goes up (if not canceled or long pressed).
+     * In addition, in fullscreen mode only, it will consume DPAD movement events to move the cursor
+     * in the extracted text view, not allowing them to perform navigation in the underlying
+     * application.</p>
+     *
+     * <p>The default implementation does not take flags specified to
+     * {@link #setBackDisposition(int)} into account, even on API version
+     * {@link android.os.Build.VERSION_CODES#P} and later devices.  IME developers are responsible
+     * for making sure that their special handling for {@link KeyEvent#KEYCODE_BACK} are consistent
+     * with the flag they specified to {@link #setBackDisposition(int)}.</p>
+     *
+     * @param keyCode The value in {@code event.getKeyCode()}
+     * @param event Description of the key event
+     *
+     * @return {@code true} if the event is consumed by the IME and the application no longer needs
+     *         to consume it.  Return {@code false} when the event should be handled as if the IME
+     *         had not seen the event at all.
      */
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
@@ -2738,6 +2845,10 @@
         mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
     }
 
+    private static int mapToImeWindowStatus(boolean isInputViewShown) {
+        return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0);
+    }
+
     /**
      * Performs a dump of the InputMethodService's internal state.  Override
      * to add your own information to the dump.
diff --git a/android/inputmethodservice/KeyboardView.java b/android/inputmethodservice/KeyboardView.java
index 13b9206..2306e5f 100644
--- a/android/inputmethodservice/KeyboardView.java
+++ b/android/inputmethodservice/KeyboardView.java
@@ -61,6 +61,7 @@
  * @attr ref android.R.styleable#KeyboardView_keyBackground
  * @attr ref android.R.styleable#KeyboardView_keyPreviewLayout
  * @attr ref android.R.styleable#KeyboardView_keyPreviewOffset
+ * @attr ref android.R.styleable#KeyboardView_keyPreviewHeight
  * @attr ref android.R.styleable#KeyboardView_labelTextSize
  * @attr ref android.R.styleable#KeyboardView_keyTextSize
  * @attr ref android.R.styleable#KeyboardView_keyTextColor
diff --git a/android/location/GnssMeasurement.java b/android/location/GnssMeasurement.java
index 412cc29..2152e1e 100644
--- a/android/location/GnssMeasurement.java
+++ b/android/location/GnssMeasurement.java
@@ -83,6 +83,20 @@
      */
     public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
 
+    /**
+     * GNSS measurement tracking loop state
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "STATE_" }, value = {
+            STATE_CODE_LOCK, STATE_BIT_SYNC, STATE_SUBFRAME_SYNC,
+            STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC,
+            STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC,
+            STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC,
+            STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
     /** This GNSS measurement's tracking state is invalid or unknown. */
     public static final int STATE_UNKNOWN = 0;
     /** This GNSS measurement's tracking state has code lock. */
@@ -133,29 +147,68 @@
     private static final int STATE_ALL = 0x3fff;  // 2 bits + 4 bits + 4 bits + 4 bits = 14 bits
 
     /**
-     * The state of the 'Accumulated Delta Range' is invalid or unknown.
+     * GNSS measurement accumulated delta range state
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "ADR_STATE_" }, value = {
+            ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP, ADR_STATE_HALF_CYCLE_RESOLVED,
+            ADR_STATE_HALF_CYCLE_REPORTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AdrState {}
+
+    /**
+     * The state of the value {@link #getAccumulatedDeltaRangeMeters()} is invalid or unknown.
      */
     public static final int ADR_STATE_UNKNOWN = 0;
 
     /**
-     * The state of the 'Accumulated Delta Range' is valid.
+     * The state of the {@link #getAccumulatedDeltaRangeMeters()} is valid.
      */
     public static final int ADR_STATE_VALID = (1<<0);
 
     /**
-     * The state of the 'Accumulated Delta Range' has detected a reset.
+     * The state of the {@link #getAccumulatedDeltaRangeMeters()} has detected a reset.
      */
     public static final int ADR_STATE_RESET = (1<<1);
 
     /**
-     * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+     * The state of the {@link #getAccumulatedDeltaRangeMeters()} has a cycle slip detected.
      */
     public static final int ADR_STATE_CYCLE_SLIP = (1<<2);
 
     /**
-     * All the 'Accumulated Delta Range' flags.
+     * Reports whether the value {@link #getAccumulatedDeltaRangeMeters()} has resolved the half
+     * cycle ambiguity.
+     *
+     * <p> When this bit is set, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+     * carrier phase measurement plus an accumulated integer number of carrier full cycles.
+     *
+     * <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+     * carrier phase measurement plus an accumulated integer number of carrier half cycles.
      */
-    private static final int ADR_ALL = ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP;
+    public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
+
+    /**
+     * Reports whether the flag {@link #ADR_STATE_HALF_CYCLE_RESOLVED} has been reported by the
+     * GNSS hardware.
+     *
+     * <p> When this bit is set, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+     * can be low (centimeter level) whether or not the half cycle ambiguity is resolved.
+     *
+     * <p> When this bit is unset, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+     * is larger, to cover the potential error due to half cycle ambiguity being unresolved.
+     */
+    public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
+    /**
+     * All the 'Accumulated Delta Range' flags.
+     * @hide
+     */
+    @TestApi
+    public static final int ADR_STATE_ALL =
+            ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP |
+            ADR_STATE_HALF_CYCLE_RESOLVED | ADR_STATE_HALF_CYCLE_REPORTED;
 
     // End enumerations in sync with gps.h
 
@@ -278,6 +331,7 @@
      *
      * <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
      */
+    @State
     public int getState() {
         return mState;
     }
@@ -287,7 +341,7 @@
      * @hide
      */
     @TestApi
-    public void setState(int value) {
+    public void setState(@State int value) {
         mState = value;
     }
 
@@ -557,6 +611,7 @@
      * <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
      * cycle slip (indicating 'loss of lock').
      */
+    @AdrState
     public int getAccumulatedDeltaRangeState() {
         return mAccumulatedDeltaRangeState;
     }
@@ -566,7 +621,7 @@
      * @hide
      */
     @TestApi
-    public void setAccumulatedDeltaRangeState(int value) {
+    public void setAccumulatedDeltaRangeState(@AdrState int value) {
         mAccumulatedDeltaRangeState = value;
     }
 
@@ -589,7 +644,15 @@
         if ((mAccumulatedDeltaRangeState & ADR_STATE_CYCLE_SLIP) == ADR_STATE_CYCLE_SLIP) {
             builder.append("CycleSlip|");
         }
-        int remainingStates = mAccumulatedDeltaRangeState & ~ADR_ALL;
+        if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_RESOLVED) ==
+                ADR_STATE_HALF_CYCLE_RESOLVED) {
+            builder.append("HalfCycleResolved|");
+        }
+        if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_REPORTED)
+                == ADR_STATE_HALF_CYCLE_REPORTED) {
+            builder.append("HalfCycleReported|");
+        }
+        int remainingStates = mAccumulatedDeltaRangeState & ~ADR_STATE_ALL;
         if (remainingStates > 0) {
             builder.append("Other(");
             builder.append(Integer.toBinaryString(remainingStates));
@@ -674,7 +737,7 @@
      * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
      * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
      *
-     * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
+     * <p> For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
      * measurement objects will be reported for this same satellite, in one of the measurement
      * objects, all the values related to L1 will be filled, and in the other all of the values
      * related to L5 will be filled.
@@ -709,7 +772,10 @@
 
     /**
      * Returns {@code true} if {@link #getCarrierCycles()} is available, {@code false} otherwise.
+     * 
+     * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
      */
+    @Deprecated
     public boolean hasCarrierCycles() {
         return isFlagSet(HAS_CARRIER_CYCLES);
     }
@@ -720,16 +786,24 @@
      * <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
      *
      * <p>The value is only available if {@link #hasCarrierCycles()} is {@code true}.
+     *
+     * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
      */
+    @Deprecated
     public long getCarrierCycles() {
         return mCarrierCycles;
     }
 
     /**
      * Sets the number of full carrier cycles between the satellite and the receiver.
+     *
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void setCarrierCycles(long value) {
         setFlag(HAS_CARRIER_CYCLES);
         mCarrierCycles = value;
@@ -737,9 +811,13 @@
 
     /**
      * Resets the number of full carrier cycles between the satellite and the receiver.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
      * @hide
      */
     @TestApi
+    @Deprecated
     public void resetCarrierCycles() {
         resetFlag(HAS_CARRIER_CYCLES);
         mCarrierCycles = Long.MIN_VALUE;
@@ -747,7 +825,10 @@
 
     /**
      * Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
+     * 
+     * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
      */
+    @Deprecated
     public boolean hasCarrierPhase() {
         return isFlagSet(HAS_CARRIER_PHASE);
     }
@@ -764,16 +845,24 @@
      * <p>The error estimate for this value is {@link #getCarrierPhaseUncertainty()}.
      *
      * <p>The value is only available if {@link #hasCarrierPhase()} is {@code true}.
+     *
+     * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
      */
+    @Deprecated
     public double getCarrierPhase() {
         return mCarrierPhase;
     }
 
     /**
      * Sets the RF phase detected by the receiver.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void setCarrierPhase(double value) {
         setFlag(HAS_CARRIER_PHASE);
         mCarrierPhase = value;
@@ -781,9 +870,14 @@
 
     /**
      * Resets the RF phase detected by the receiver.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void resetCarrierPhase() {
         resetFlag(HAS_CARRIER_PHASE);
         mCarrierPhase = Double.NaN;
@@ -792,7 +886,10 @@
     /**
      * Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
      * otherwise.
+     * 
+     * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
      */
+    @Deprecated
     public boolean hasCarrierPhaseUncertainty() {
         return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
     }
@@ -803,16 +900,24 @@
      * <p>The uncertainty is represented as an absolute (single sided) value.
      *
      * <p>The value is only available if {@link #hasCarrierPhaseUncertainty()} is {@code true}.
+     *
+     * @deprecated use {@link #getAccumulatedDeltaRangeUncertaintyMeters()} instead.
      */
+    @Deprecated
     public double getCarrierPhaseUncertainty() {
         return mCarrierPhaseUncertainty;
     }
 
     /**
      * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void setCarrierPhaseUncertainty(double value) {
         setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
         mCarrierPhaseUncertainty = value;
@@ -820,9 +925,14 @@
 
     /**
      * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+     * 
+     * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+     * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+     * 
      * @hide
      */
     @TestApi
+    @Deprecated
     public void resetCarrierPhaseUncertainty() {
         resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
         mCarrierPhaseUncertainty = Double.NaN;
@@ -859,7 +969,7 @@
             case MULTIPATH_INDICATOR_NOT_DETECTED:
                 return "NotDetected";
             default:
-                return "<Invalid:" + mMultipathIndicator + ">";
+                return "<Invalid: " + mMultipathIndicator + ">";
         }
     }
 
@@ -916,11 +1026,12 @@
      * number. Hence in cases of strong jamming, in the band of this signal, this value will go more
      * negative.
      *
-     * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+     * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
      * components) may also affect the typical output of of this value on any given hardware design
      * in an open sky test - the important aspect of this output is that changes in this value are
      * indicative of changes on input signal power in the frequency band for this measurement.
-     * <p>The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+     *
+     * <p> The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
      */
     public double getAutomaticGainControlLevelDb() {
         return mAutomaticGainControlLevelInDb;
diff --git a/android/location/LocationManager.java b/android/location/LocationManager.java
index 9db9d33..a523958 100644
--- a/android/location/LocationManager.java
+++ b/android/location/LocationManager.java
@@ -23,6 +23,8 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -30,6 +32,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,11 +41,16 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import com.android.internal.location.ProviderProperties;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This class provides access to the system location services.  These
@@ -60,6 +68,7 @@
  * location will be obfuscated to a coarse level of accuracy.
  */
 @SystemService(Context.LOCATION_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_LOCATION)
 public class LocationManager {
     private static final String TAG = "LocationManager";
 
@@ -228,10 +237,60 @@
         "android.location.HIGH_POWER_REQUEST_CHANGE";
 
     /**
-     * The value returned by {@link LocationManager#getGnssHardwareModelName()} when the hardware
-     * does not support providing the actual value.
+     * Broadcast intent action for Settings app to inject a footer at the bottom of location
+     * settings.
+     *
+     * <p>This broadcast is used for two things:
+     * <ol>
+     *     <li>For receivers to inject a footer with provided text. This is for use only by apps
+     *         that are included in the system image. </li>
+     *     <li>For receivers to know their footer is injected under location settings.</li>
+     * </ol>
+     *
+     * <p>To inject a footer to location settings, you must declare a broadcast receiver of
+     * {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} in the manifest as so:
+     * <pre>
+     *     &lt;receiver android:name="com.example.android.footer.MyFooterInjector"&gt;
+     *         &lt;intent-filter&gt;
+     *             &lt;action android:name="com.android.settings.location.INJECT_FOOTER" /&gt;
+     *         &lt;/intent-filter&gt;
+     *         &lt;meta-data
+     *             android:name="com.android.settings.location.FOOTER_STRING"
+     *             android:resource="@string/my_injected_footer_string" /&gt;
+     *     &lt;/receiver&gt;
+     * </pre>
+     *
+     * <p>On entering location settings, Settings app will send a
+     * {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast to receivers whose footer is successfully
+     * injected. On leaving location settings, the footer becomes not visible to users. Settings app
+     * will send a {@link #SETTINGS_FOOTER_REMOVED_ACTION} broadcast to those receivers.
+     *
+     * @hide
      */
-    public static final String GNSS_HARDWARE_MODEL_NAME_UNKNOWN = "Model Name Unknown";
+    public static final String SETTINGS_FOOTER_DISPLAYED_ACTION =
+            "com.android.settings.location.DISPLAYED_FOOTER";
+
+    /**
+     * Broadcast intent action when location settings footer is not visible to users.
+     *
+     * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use.
+     *
+     * @hide
+     */
+    public static final String SETTINGS_FOOTER_REMOVED_ACTION =
+            "com.android.settings.location.REMOVED_FOOTER";
+
+    /**
+     * Metadata name for {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast
+     * receivers to specify a string resource id as location settings footer text. This is for use
+     * only by apps that are included in the system image.
+     *
+     * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use.
+     *
+     * @hide
+     */
+    public static final String METADATA_SETTINGS_FOOTER_STRING =
+            "com.android.settings.location.FOOTER_STRING";
 
     // Map from LocationListeners to their associated ListenerTransport objects
     private HashMap<LocationListener,ListenerTransport> mListeners =
@@ -1184,18 +1243,46 @@
      *
      * @param enabled true to enable location. false to disable location
      * @param userHandle the user to set
-     * @return true if the value was set, false on database errors
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        try {
-            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        final List<String> allProvidersList = getAllProviders();
+        // Update all providers on device plus gps and network provider when disabling location.
+        Set<String> allProvidersSet = new ArraySet<>(allProvidersList.size() + 2);
+        allProvidersSet.addAll(allProvidersList);
+        // When disabling location, disable gps and network provider that could have been enabled by
+        // location mode api.
+        if (enabled == false) {
+            allProvidersSet.add(GPS_PROVIDER);
+            allProvidersSet.add(NETWORK_PROVIDER);
         }
+        if (allProvidersSet.isEmpty()) {
+            return;
+        }
+        // to ensure thread safety, we write the provider name with a '+' or '-'
+        // and let the SettingsProvider handle it rather than reading and modifying
+        // the list of enabled providers.
+        final String prefix = enabled ? "+" : "-";
+        StringBuilder locationProvidersAllowed = new StringBuilder();
+        for (String provider : allProvidersSet) {
+            checkProvider(provider);
+            if (provider.equals(PASSIVE_PROVIDER)) {
+                continue;
+            }
+            locationProvidersAllowed.append(prefix);
+            locationProvidersAllowed.append(provider);
+            locationProvidersAllowed.append(",");
+        }
+        // Remove the trailing comma
+        locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+        Settings.Secure.putStringForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                locationProvidersAllowed.toString(),
+                userHandle.getIdentifier());
     }
 
     /**
@@ -1208,11 +1295,22 @@
      */
     @SystemApi
     public boolean isLocationEnabledForUser(UserHandle userHandle) {
-        try {
-            return mService.isLocationEnabledForUser(userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        final String allowedProviders = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                userHandle.getIdentifier());
+        if (allowedProviders == null) {
+            return false;
         }
+        final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+        for(String provider : getAllProviders()) {
+            if (provider.equals(PASSIVE_PROVIDER)) {
+                continue;
+            }
+            if (providerList.contains(provider)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -1236,13 +1334,7 @@
      * @throws IllegalArgumentException if provider is null
      */
     public boolean isProviderEnabled(String provider) {
-        checkProvider(provider);
-
-        try {
-            return mService.isProviderEnabled(provider);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return isProviderEnabledForUser(provider, Process.myUserHandle());
     }
 
     /**
@@ -1270,12 +1362,9 @@
     @SystemApi
     public boolean isProviderEnabledForUser(String provider, UserHandle userHandle) {
         checkProvider(provider);
-
-        try {
-            return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        String allowedProviders = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userHandle.getIdentifier());
+        return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
     }
 
     /**
@@ -1294,13 +1383,16 @@
     public boolean setProviderEnabledForUser(
             String provider, boolean enabled, UserHandle userHandle) {
         checkProvider(provider);
-
-        try {
-            return mService.setProviderEnabledForUser(
-                    provider, enabled, userHandle.getIdentifier());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        // to ensure thread safety, we write the provider name with a '+' or '-'
+        // and let the SettingsProvider handle it rather than reading and modifying
+        // the list of enabled providers.
+        if (enabled) {
+            provider = "+" + provider;
+        } else {
+            provider = "-" + provider;
         }
+        return Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, userHandle.getIdentifier());
     }
 
     /**
@@ -2109,7 +2201,9 @@
     /**
      * Returns the model year of the GNSS hardware and software build.
      *
-     * May return 0 if the model year is less than 2016.
+     * <p> More details, such as build date, may be available in {@link #getGnssHardwareModelName()}.
+     *
+     * <p> May return 0 if the model year is less than 2016.
      */
     public int getGnssYearOfHardware() {
         try {
@@ -2123,10 +2217,12 @@
      * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
      * driver.
      *
-     * Will return {@link LocationManager#GNSS_HARDWARE_MODEL_NAME_UNKNOWN} when the GNSS hardware
-     * abstraction layer does not support providing this value.
+     * <p> No device-specific serial number or ID is returned from this API.
+     *
+     * <p> Will return null when the GNSS hardware abstraction layer does not support providing
+     * this value.
      */
-    @NonNull
+    @Nullable
     public String getGnssHardwareModelName() {
         try {
             return mService.getGnssHardwareModelName();
diff --git a/android/location/LocationRequest.java b/android/location/LocationRequest.java
index 6abba95..96a0817 100644
--- a/android/location/LocationRequest.java
+++ b/android/location/LocationRequest.java
@@ -339,7 +339,8 @@
      * substantially restrict power.
      *
      * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF &
-     * signal searches for more than one second per interval {@link #mInterval}
+     * signal searches for more than one second per interval (specified by
+     * {@link #setInterval(long)}).
      *
      * @param enabled Enable or disable low power mode
      * @return the same object, so that setters can be chained
diff --git a/android/media/AudioAttributes.java b/android/media/AudioAttributes.java
index 44a2ff9..d432658 100644
--- a/android/media/AudioAttributes.java
+++ b/android/media/AudioAttributes.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.media.AudioAttributesProto;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -206,19 +205,26 @@
     /**
      * @hide
      * Denotes a usage for alarms,
-     * will be muted when the Zen mode doesn't allow alarms
+     * will be muted when the Zen mode priority doesn't allow alarms or in Alarms Only Mode
      * @see #SUPPRESSIBLE_USAGES
      */
     public final static int SUPPRESSIBLE_ALARM = 4;
     /**
      * @hide
-     * Denotes a usage for all other sounds not caught in SUPPRESSIBLE_NOTIFICATION,
-     * SUPPRESSIBLE_CALL,SUPPRESSIBLE_NEVER or SUPPRESSIBLE_ALARM.
-     * This includes media, system, game, navigation, the assistant, and more.
-     * These will be muted when the Zen mode doesn't allow media/system/other.
+     * Denotes a usage for media, game, assistant, and navigation
+     * will be muted when the Zen priority mode doesn't allow media
      * @see #SUPPRESSIBLE_USAGES
      */
-    public final static int SUPPRESSIBLE_MEDIA_SYSTEM_OTHER = 5;
+    public final static int SUPPRESSIBLE_MEDIA = 5;
+    /**
+     * @hide
+     * Denotes a usage for all other sounds not caught in SUPPRESSIBLE_NOTIFICATION,
+     * SUPPRESSIBLE_CALL,SUPPRESSIBLE_NEVER, SUPPRESSIBLE_ALARM or SUPPRESSIBLE_MEDIA.
+     * This includes system, sonification and unknown sounds.
+     * These will be muted when the Zen priority mode doesn't allow sytem sounds
+     * @see #SUPPRESSIBLE_USAGES
+     */
+    public final static int SUPPRESSIBLE_SYSTEM = 6;
 
     /**
      * @hide
@@ -239,13 +245,13 @@
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_ACCESSIBILITY,          SUPPRESSIBLE_NEVER);
         SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION,               SUPPRESSIBLE_NEVER);
         SUPPRESSIBLE_USAGES.put(USAGE_ALARM,                             SUPPRESSIBLE_ALARM);
-        SUPPRESSIBLE_USAGES.put(USAGE_MEDIA,                             SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
-        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION,           SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
-        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,    SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
-        SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
-        SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING,    SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
-        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
-        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+        SUPPRESSIBLE_USAGES.put(USAGE_MEDIA,                             SUPPRESSIBLE_MEDIA);
+        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,    SUPPRESSIBLE_MEDIA);
+        SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA);
+        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA);
+        SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING,    SUPPRESSIBLE_SYSTEM);
+        SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION,           SUPPRESSIBLE_SYSTEM);
+        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_SYSTEM);
     }
 
     /**
diff --git a/android/media/AudioDeviceInfo.java b/android/media/AudioDeviceInfo.java
index 1a97b6b..ca895fc 100644
--- a/android/media/AudioDeviceInfo.java
+++ b/android/media/AudioDeviceInfo.java
@@ -22,6 +22,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.TreeSet;
 
 /**
@@ -122,6 +123,10 @@
      * A device type describing a USB audio headset.
      */
     public static final int TYPE_USB_HEADSET       = 22;
+    /**
+     * A device type describing a Hearing Aid.
+     */
+    public static final int TYPE_HEARING_AID   = 23;
 
     /** @hide */
     @IntDef(flag = false, prefix = "TYPE", value = {
@@ -142,7 +147,9 @@
             TYPE_LINE_DIGITAL,
             TYPE_FM,
             TYPE_AUX_LINE,
-            TYPE_IP }
+            TYPE_IP,
+            TYPE_BUS,
+            TYPE_HEARING_AID }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceTypeOut {}
@@ -168,12 +175,27 @@
             case TYPE_FM:
             case TYPE_AUX_LINE:
             case TYPE_IP:
+            case TYPE_BUS:
+            case TYPE_HEARING_AID:
                 return true;
             default:
                 return false;
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AudioDeviceInfo that = (AudioDeviceInfo) o;
+        return Objects.equals(getPort(), that.getPort());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getPort());
+    }
+
     private final AudioDevicePort mPort;
 
     AudioDeviceInfo(AudioDevicePort port) {
@@ -204,11 +226,10 @@
     }
 
     /**
-     * @hide
      * @return The "address" string of the device. This generally contains device-specific
      * parameters.
      */
-    public String getAddress() {
+    public @NonNull String getAddress() {
         return mPort.address();
     }
 
@@ -351,6 +372,7 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_AUX_LINE, TYPE_AUX_LINE);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_IP, TYPE_IP);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BUS, TYPE_BUS);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_HEARING_AID, TYPE_HEARING_AID);
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -399,6 +421,7 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_AUX_LINE, AudioSystem.DEVICE_OUT_AUX_LINE);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_OUT_IP);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_OUT_BUS);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_HEARING_AID, AudioSystem.DEVICE_OUT_HEARING_AID);
     }
 }
 
diff --git a/android/media/AudioFocusInfo.java b/android/media/AudioFocusInfo.java
index 5d0c8e2..5467a69 100644
--- a/android/media/AudioFocusInfo.java
+++ b/android/media/AudioFocusInfo.java
@@ -38,6 +38,10 @@
     private int mLossReceived;
     private int mFlags;
 
+    // generation count for the validity of a request/response async exchange between
+    // external focus policy and MediaFocusControl
+    private long mGenCount = -1;
+
 
     /**
      * Class constructor
@@ -61,6 +65,16 @@
         mSdkTarget = sdk;
     }
 
+    /** @hide */
+    public void setGen(long g) {
+        mGenCount = g;
+    }
+
+    /** @hide */
+    public long getGen() {
+        return mGenCount;
+    }
+
 
     /**
      * The audio attributes for the audio focus request.
@@ -128,6 +142,7 @@
         dest.writeInt(mLossReceived);
         dest.writeInt(mFlags);
         dest.writeInt(mSdkTarget);
+        dest.writeLong(mGenCount);
     }
 
     @Override
@@ -168,6 +183,8 @@
         if (mSdkTarget != other.mSdkTarget) {
             return false;
         }
+        // mGenCount is not used to verify equality between two focus holds as multiple requests
+        // (hence of different generations) could correspond to the same hold
         return true;
     }
 
@@ -175,7 +192,7 @@
             = new Parcelable.Creator<AudioFocusInfo>() {
 
         public AudioFocusInfo createFromParcel(Parcel in) {
-            return new AudioFocusInfo(
+            final AudioFocusInfo afi = new AudioFocusInfo(
                     AudioAttributes.CREATOR.createFromParcel(in), //AudioAttributes aa
                     in.readInt(), // int clientUid
                     in.readString(), //String clientId
@@ -185,6 +202,8 @@
                     in.readInt(), //int flags
                     in.readInt()  //int sdkTarget
                     );
+            afi.setGen(in.readLong());
+            return afi;
         }
 
         public AudioFocusInfo[] newArray(int size) {
diff --git a/android/media/AudioFormat.java b/android/media/AudioFormat.java
index b07d042..f98480b 100644
--- a/android/media/AudioFormat.java
+++ b/android/media/AudioFormat.java
@@ -265,6 +265,12 @@
     public static final int ENCODING_AAC_XHE = 16;
     /** Audio data format: AC-4 sync frame transport format */
     public static final int ENCODING_AC4 = 17;
+    /** Audio data format: E-AC-3-JOC compressed
+     * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}.
+     * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device
+     * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
+     **/
+    public static final int ENCODING_E_AC3_JOC = 18;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -512,6 +518,7 @@
         case ENCODING_PCM_FLOAT:
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_MP3:
@@ -537,6 +544,7 @@
         case ENCODING_PCM_FLOAT:
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_IEC61937:
@@ -564,6 +572,7 @@
             return true;
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_MP3:
@@ -593,6 +602,7 @@
             return true;
         case ENCODING_AC3:
         case ENCODING_E_AC3:
+        case ENCODING_E_AC3_JOC:
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_MP3:
@@ -829,6 +839,7 @@
                 case ENCODING_PCM_FLOAT:
                 case ENCODING_AC3:
                 case ENCODING_E_AC3:
+                case ENCODING_E_AC3_JOC:
                 case ENCODING_DTS:
                 case ENCODING_DTS_HD:
                 case ENCODING_IEC61937:
@@ -1044,6 +1055,7 @@
         ENCODING_PCM_FLOAT,
         ENCODING_AC3,
         ENCODING_E_AC3,
+        ENCODING_E_AC3_JOC,
         ENCODING_DTS,
         ENCODING_DTS_HD,
         ENCODING_IEC61937,
diff --git a/android/media/AudioManager.java b/android/media/AudioManager.java
index 2ac4063..aeef215 100644
--- a/android/media/AudioManager.java
+++ b/android/media/AudioManager.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.audiopolicy.AudioPolicy;
+import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionLegacyHelper;
@@ -48,17 +49,25 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
 
 /**
  * AudioManager provides access to volume and ringer mode control.
@@ -400,6 +409,18 @@
     public static final int ADJUST_TOGGLE_MUTE = 101;
 
     /** @hide */
+    @IntDef(flag = false, prefix = "ADJUST", value = {
+            ADJUST_RAISE,
+            ADJUST_LOWER,
+            ADJUST_SAME,
+            ADJUST_MUTE,
+            ADJUST_UNMUTE,
+            ADJUST_TOGGLE_MUTE }
+            )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VolumeAdjustment {}
+
+    /** @hide */
     public static final String adjustToString(int adj) {
         switch (adj) {
             case ADJUST_RAISE: return "ADJUST_RAISE";
@@ -1331,6 +1352,7 @@
     //====================================================================
     // Offload query
     /**
+     * @hide
      * Returns whether offloaded playback of an audio format is supported on the device.
      * Offloaded playback is where the decoding of an audio stream is not competing with other
      * software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
@@ -2077,27 +2099,7 @@
      */
     private boolean querySoundEffectsEnabled(int user) {
         return Settings.System.getIntForUser(getContext().getContentResolver(),
-                Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0
-                && !areSystemSoundsZenModeBlocked(getContext());
-    }
-
-    private boolean areSystemSoundsZenModeBlocked(Context context) {
-        int zenMode = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.ZEN_MODE, 0);
-
-        switch (zenMode) {
-            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
-            case Settings.Global.ZEN_MODE_ALARMS:
-                return true;
-            case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                final NotificationManager noMan = (NotificationManager) context
-                        .getSystemService(Context.NOTIFICATION_SERVICE);
-                return (noMan.getNotificationPolicy().priorityCategories
-                        & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
-            case Settings.Global.ZEN_MODE_OFF:
-            default:
-                return false;
-        }
+                Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
     }
 
     /**
@@ -2324,6 +2326,20 @@
                 }
             }
         }
+
+        @Override
+        public void dispatchFocusResultFromExtPolicy(int requestResult, String clientId) {
+            synchronized (mFocusRequestsLock) {
+                // TODO use generation counter as the key instead
+                final BlockingFocusResultReceiver focusReceiver =
+                        mFocusRequestsAwaitingResult.remove(clientId);
+                if (focusReceiver != null) {
+                    focusReceiver.notifyResult(requestResult);
+                } else {
+                    Log.e(TAG, "dispatchFocusResultFromExtPolicy found no result receiver");
+                }
+            }
+        }
     };
 
     private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
@@ -2376,6 +2392,40 @@
       */
     public static final int AUDIOFOCUS_REQUEST_DELAYED = 2;
 
+    /** @hide */
+    @IntDef(flag = false, prefix = "AUDIOFOCUS_REQUEST", value = {
+            AUDIOFOCUS_REQUEST_FAILED,
+            AUDIOFOCUS_REQUEST_GRANTED,
+            AUDIOFOCUS_REQUEST_DELAYED }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FocusRequestResult {}
+
+    /**
+     * @hide
+     * code returned when a synchronous focus request on the client-side is to be blocked
+     * until the external audio focus policy decides on the response for the client
+     */
+    public static final int AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY = 100;
+
+    /**
+     * Timeout duration in ms when waiting on an external focus policy for the result for a
+     * focus request
+     */
+    private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 200;
+
+    private static final String FOCUS_CLIENT_ID_STRING = "android_audio_focus_client_id";
+
+    private final Object mFocusRequestsLock = new Object();
+    /**
+     * Map of all receivers of focus request results, one per unresolved focus request.
+     * Receivers are added before sending the request to the external focus policy,
+     * and are removed either after receiving the result, or after the timeout.
+     * This variable is lazily initialized.
+     */
+    @GuardedBy("mFocusRequestsLock")
+    private HashMap<String, BlockingFocusResultReceiver> mFocusRequestsAwaitingResult;
+
 
     /**
      *  Request audio focus.
@@ -2642,18 +2692,100 @@
             // some tests don't have a Context
             sdk = Build.VERSION.SDK_INT;
         }
-        try {
-            status = service.requestAudioFocus(afr.getAudioAttributes(),
-                    afr.getFocusGain(), mICallBack,
-                    mAudioFocusDispatcher,
-                    getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener()),
-                    getContext().getOpPackageName() /* package name */, afr.getFlags(),
-                    ap != null ? ap.cb() : null,
-                    sdk);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+
+        final String clientId = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
+        final BlockingFocusResultReceiver focusReceiver;
+        synchronized (mFocusRequestsLock) {
+            try {
+                // TODO status contains result and generation counter for ext policy
+                status = service.requestAudioFocus(afr.getAudioAttributes(),
+                        afr.getFocusGain(), mICallBack,
+                        mAudioFocusDispatcher,
+                        clientId,
+                        getContext().getOpPackageName() /* package name */, afr.getFlags(),
+                        ap != null ? ap.cb() : null,
+                        sdk);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
+                // default path with no external focus policy
+                return status;
+            }
+            if (mFocusRequestsAwaitingResult == null) {
+                mFocusRequestsAwaitingResult =
+                        new HashMap<String, BlockingFocusResultReceiver>(1);
+            }
+            focusReceiver = new BlockingFocusResultReceiver(clientId);
+            mFocusRequestsAwaitingResult.put(clientId, focusReceiver);
         }
-        return status;
+        focusReceiver.waitForResult(EXT_FOCUS_POLICY_TIMEOUT_MS);
+        if (DEBUG && !focusReceiver.receivedResult()) {
+            Log.e(TAG, "requestAudio response from ext policy timed out, denying request");
+        }
+        synchronized (mFocusRequestsLock) {
+            mFocusRequestsAwaitingResult.remove(clientId);
+        }
+        return focusReceiver.requestResult();
+    }
+
+    // helper class that abstracts out the handling of spurious wakeups in Object.wait()
+    private static final class SafeWaitObject {
+        private boolean mQuit = false;
+
+        public void safeNotify() {
+            synchronized (this) {
+                mQuit = true;
+                this.notify();
+            }
+        }
+
+        public void safeWait(long millis) throws InterruptedException {
+            final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
+            synchronized (this) {
+                while (!mQuit) {
+                    final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
+                    if (timeToWait < 0) { break; }
+                    this.wait(timeToWait);
+                }
+            }
+        }
+    }
+
+    private static final class BlockingFocusResultReceiver {
+        private final SafeWaitObject mLock = new SafeWaitObject();
+        @GuardedBy("mLock")
+        private boolean mResultReceived = false;
+        // request denied by default (e.g. timeout)
+        private int mFocusRequestResult = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+        private final String mFocusClientId;
+
+        BlockingFocusResultReceiver(String clientId) {
+            mFocusClientId = clientId;
+        }
+
+        boolean receivedResult() { return mResultReceived; }
+        int requestResult() { return mFocusRequestResult; }
+
+        void notifyResult(int requestResult) {
+            synchronized (mLock) {
+                mResultReceived = true;
+                mFocusRequestResult = requestResult;
+                mLock.safeNotify();
+            }
+        }
+
+        public void waitForResult(long timeOutMs) {
+            synchronized (mLock) {
+                if (mResultReceived) {
+                    // the result was received before waiting
+                    return;
+                }
+                try {
+                    mLock.safeWait(timeOutMs);
+                } catch (InterruptedException e) { }
+            }
+        }
     }
 
     /**
@@ -2700,6 +2832,32 @@
 
     /**
      * @hide
+     * Set the result to the audio focus request received through
+     * {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
+     * @param afi the information about the focus requester
+     * @param requestResult the result to the focus request to be passed to the requester
+     * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
+            @FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
+        if (afi == null) {
+            throw new IllegalArgumentException("Illegal null AudioFocusInfo");
+        }
+        if (ap == null) {
+            throw new IllegalArgumentException("Illegal null AudioPolicy");
+        }
+        final IAudioService service = getService();
+        try {
+            service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
      * Notifies an application with a focus listener of gain or loss of audio focus.
      * This method can only be used by owners of an {@link AudioPolicy} configured with
      * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
@@ -2989,7 +3147,7 @@
         final IAudioService service = getService();
         try {
             String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
-                    policy.hasFocusListener(), policy.isFocusPolicy());
+                    policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController());
             if (regId == null) {
                 return ERROR;
             } else {
@@ -3736,6 +3894,21 @@
     }
 
      /**
+     * Indicate Hearing Aid connection state change.
+     * @param device Bluetooth device connected/disconnected
+     * @param state new connection state (BluetoothProfile.STATE_xxx)
+     * {@hide}
+     */
+    public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) {
+        final IAudioService service = getService();
+        try {
+            service.setHearingAidDeviceConnectionState(device, state);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+     /**
      * Indicate A2DP source or sink connection state change.
      * @param device Bluetooth device connected/disconnected
      * @param state  new connection state (BluetoothProfile.STATE_xxx)
@@ -4423,8 +4596,7 @@
 
     private static boolean checkTypes(AudioDevicePort port) {
         return AudioDeviceInfo.convertInternalDeviceToDeviceType(port.type()) !=
-                    AudioDeviceInfo.TYPE_UNKNOWN &&
-                port.type() != AudioSystem.DEVICE_IN_BACK_MIC;
+                    AudioDeviceInfo.TYPE_UNKNOWN;
     }
 
     /**
@@ -4566,6 +4738,82 @@
         }
     }
 
+    /**
+     * Set port id for microphones by matching device type and address.
+     * @hide
+     */
+    public static void setPortIdForMicrophones(ArrayList<MicrophoneInfo> microphones) {
+        AudioDeviceInfo[] devices = getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
+        for (int i = microphones.size() - 1; i >= 0; i--) {
+            boolean foundPortId = false;
+            for (AudioDeviceInfo device : devices) {
+                if (device.getPort().type() == microphones.get(i).getInternalDeviceType()
+                        && TextUtils.equals(device.getAddress(), microphones.get(i).getAddress())) {
+                    microphones.get(i).setId(device.getId());
+                    foundPortId = true;
+                    break;
+                }
+            }
+            if (!foundPortId) {
+                Log.i(TAG, "Failed to find port id for device with type:"
+                        + microphones.get(i).getType() + " address:"
+                        + microphones.get(i).getAddress());
+                microphones.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Convert {@link AudioDeviceInfo} to {@link MicrophoneInfo}.
+     * @hide
+     */
+    public static MicrophoneInfo microphoneInfoFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
+        int deviceType = deviceInfo.getType();
+        int micLocation = (deviceType == AudioDeviceInfo.TYPE_BUILTIN_MIC
+                || deviceType == AudioDeviceInfo.TYPE_TELEPHONY) ? MicrophoneInfo.LOCATION_MAINBODY
+                : deviceType == AudioDeviceInfo.TYPE_UNKNOWN ? MicrophoneInfo.LOCATION_UNKNOWN
+                        : MicrophoneInfo.LOCATION_PERIPHERAL;
+        MicrophoneInfo microphone = new MicrophoneInfo(
+                deviceInfo.getPort().name() + deviceInfo.getId(),
+                deviceInfo.getPort().type(), deviceInfo.getAddress(), micLocation,
+                MicrophoneInfo.GROUP_UNKNOWN, MicrophoneInfo.INDEX_IN_THE_GROUP_UNKNOWN,
+                MicrophoneInfo.POSITION_UNKNOWN, MicrophoneInfo.ORIENTATION_UNKNOWN,
+                new ArrayList<Pair<Float, Float>>(), new ArrayList<Pair<Integer, Integer>>(),
+                MicrophoneInfo.SENSITIVITY_UNKNOWN, MicrophoneInfo.SPL_UNKNOWN,
+                MicrophoneInfo.SPL_UNKNOWN, MicrophoneInfo.DIRECTIONALITY_UNKNOWN);
+        microphone.setId(deviceInfo.getId());
+        return microphone;
+    }
+
+    /**
+     * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
+     * of all available microphones. The list is empty when no microphones are available
+     * on the device. An error during the query will result in an IOException being thrown.
+     *
+     * @return a list that contains all microphones' characteristics
+     * @throws IOException if an error occurs.
+     */
+    public List<MicrophoneInfo> getMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
+        int status = AudioSystem.getMicrophones(microphones);
+        if (status != AudioManager.SUCCESS) {
+            // fail and bail!
+            Log.e(TAG, "getMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>(); // Always return a list.
+        }
+        setPortIdForMicrophones(microphones);
+        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
+        for (AudioDeviceInfo device : devices) {
+            if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC ||
+                    device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
+                continue;
+            }
+            MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
+            microphones.add(microphone);
+        }
+        return microphones;
+    }
+
     // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
     // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
     // of the ports that exist at the time of the last notification.
@@ -4645,6 +4893,114 @@
         }
     }
 
+
+    /**
+     * @hide
+     * Abstract class to receive event notification about audioserver process state.
+     */
+    @SystemApi
+    public abstract static class AudioServerStateCallback {
+        public void onAudioServerDown() { }
+        public void onAudioServerUp() { }
+    }
+
+    private Executor mAudioServerStateExec;
+    private AudioServerStateCallback mAudioServerStateCb;
+    private final Object mAudioServerStateCbLock = new Object();
+
+    private final IAudioServerStateDispatcher mAudioServerStateDispatcher =
+            new IAudioServerStateDispatcher.Stub() {
+        @Override
+        public void dispatchAudioServerStateChange(boolean state) {
+            Executor exec;
+            AudioServerStateCallback cb;
+
+            synchronized (mAudioServerStateCbLock) {
+                exec = mAudioServerStateExec;
+                cb = mAudioServerStateCb;
+            }
+
+            if ((exec == null) || (cb == null)) {
+                return;
+            }
+            if (state) {
+                exec.execute(() -> cb.onAudioServerUp());
+            } else {
+                exec.execute(() -> cb.onAudioServerDown());
+            }
+        }
+    };
+
+    /**
+     * @hide
+     * Registers a callback for notification of audio server state changes.
+     * @param executor {@link Executor} to handle the callbacks
+     * @param stateCallback the callback to receive the audio server state changes
+     *        To remove the callabck, pass a null reference for both executor and stateCallback.
+     */
+    @SystemApi
+    public void setAudioServerStateCallback(@NonNull Executor executor,
+            @NonNull AudioServerStateCallback stateCallback) {
+        if (stateCallback == null) {
+            throw new IllegalArgumentException("Illegal null AudioServerStateCallback");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException(
+                    "Illegal null Executor for the AudioServerStateCallback");
+        }
+
+        synchronized (mAudioServerStateCbLock) {
+            if (mAudioServerStateCb != null) {
+                throw new IllegalStateException(
+                    "setAudioServerStateCallback called with already registered callabck");
+            }
+            final IAudioService service = getService();
+            try {
+                service.registerAudioServerStateDispatcher(mAudioServerStateDispatcher);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            mAudioServerStateExec = executor;
+            mAudioServerStateCb = stateCallback;
+        }
+    }
+
+    /**
+     * @hide
+     * Unregisters the callback for notification of audio server state changes.
+     */
+    @SystemApi
+    public void clearAudioServerStateCallback() {
+        synchronized (mAudioServerStateCbLock) {
+            if (mAudioServerStateCb != null) {
+                final IAudioService service = getService();
+                try {
+                    service.unregisterAudioServerStateDispatcher(
+                            mAudioServerStateDispatcher);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            mAudioServerStateExec = null;
+            mAudioServerStateCb = null;
+        }
+    }
+
+    /**
+     * @hide
+     * Checks if native audioservice is running or not.
+     * @return true if native audioservice runs, false otherwise.
+     */
+    @SystemApi
+    public boolean isAudioServerRunning() {
+        final IAudioService service = getService();
+        try {
+            return service.isAudioServerRunning();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     //---------------------------------------------------------
     // Inner classes
     //--------------------
diff --git a/android/media/AudioManagerInternal.java b/android/media/AudioManagerInternal.java
index 0a1de33..98c2d7f 100644
--- a/android/media/AudioManagerInternal.java
+++ b/android/media/AudioManagerInternal.java
@@ -42,6 +42,8 @@
 
     public abstract void setRingerModeInternal(int ringerMode, String caller);
 
+    public abstract void silenceRingerModeInternal(String caller);
+
     public abstract void updateRingerModeAffectedStreamsInternal();
 
     public abstract void setAccessibilityServiceUids(IntArray uids);
diff --git a/android/media/AudioPresentation.java b/android/media/AudioPresentation.java
new file mode 100644
index 0000000..e39cb7d
--- /dev/null
+++ b/android/media/AudioPresentation.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * The AudioPresentation class encapsulates the information that describes an audio presentation
+ * which is available in next generation audio content.
+ *
+ * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
+ * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
+ * presentations and to select one.
+ *
+ * A list of available audio presentations in a media source can be queried using
+ * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
+ * selection.
+ * An AudioPresentation can be passed to an offloaded audio decoder via
+ * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected
+ * presentation. An audio stream may contain multiple presentations that differ by language,
+ * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have
+ * a set of description labels in different languages to help the user to make an informed
+ * selection.
+ */
+public final class AudioPresentation {
+    private final int mPresentationId;
+    private final int mProgramId;
+    private final Map<String, String> mLabels;
+    private final String mLanguage;
+
+    /** @hide */
+    @IntDef(
+        value = {
+            MASTERING_NOT_INDICATED,
+            MASTERED_FOR_STEREO,
+            MASTERED_FOR_SURROUND,
+            MASTERED_FOR_3D,
+            MASTERED_FOR_HEADPHONE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MasteringIndicationType {}
+
+    private final @MasteringIndicationType int mMasteringIndication;
+    private final boolean mAudioDescriptionAvailable;
+    private final boolean mSpokenSubtitlesAvailable;
+    private final boolean mDialogueEnhancementAvailable;
+
+    /**
+     * No preferred reproduction channel layout.
+     */
+    public static final int MASTERING_NOT_INDICATED         = 0;
+    /**
+     * Stereo speaker layout.
+     */
+    public static final int MASTERED_FOR_STEREO             = 1;
+    /**
+     * Two-dimensional (e.g. 5.1) speaker layout.
+     */
+    public static final int MASTERED_FOR_SURROUND           = 2;
+    /**
+     * Three-dimensional (e.g. 5.1.2) speaker layout.
+     */
+    public static final int MASTERED_FOR_3D                 = 3;
+    /**
+     * Prerendered for headphone playback.
+     */
+    public static final int MASTERED_FOR_HEADPHONE          = 4;
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public AudioPresentation(int presentationId,
+                        int programId,
+                        @NonNull Map<String, String> labels,
+                        @NonNull String language,
+                        @MasteringIndicationType int masteringIndication,
+                        boolean audioDescriptionAvailable,
+                        boolean spokenSubtitlesAvailable,
+                        boolean dialogueEnhancementAvailable) {
+        this.mPresentationId = presentationId;
+        this.mProgramId = programId;
+        this.mLanguage = language;
+        this.mMasteringIndication = masteringIndication;
+        this.mAudioDescriptionAvailable = audioDescriptionAvailable;
+        this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+        this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+
+        this.mLabels = new HashMap<String, String>(labels);
+    }
+
+    /**
+     * The framework uses this presentation id to select an audio presentation rendered by a
+     * decoder. Presentation id is typically sequential, but does not have to be.
+     * @hide
+     */
+    @VisibleForTesting
+    public int getPresentationId() {
+        return mPresentationId;
+    }
+
+    /**
+     * The framework uses this program id to select an audio presentation rendered by a decoder.
+     * Program id can be used to further uniquely identify the presentation to a decoder.
+     * @hide
+     */
+    @VisibleForTesting
+    public int getProgramId() {
+        return mProgramId;
+    }
+
+    /**
+     * @return a map of available text labels for this presentation. Each label is indexed by its
+     * locale corresponding to the language code as specified by ISO 639-2. Either ISO 639-2/B
+     * or ISO 639-2/T could be used.
+     */
+    public Map<Locale, String> getLabels() {
+        Map<Locale, String> localeLabels = new HashMap<>();
+        for (Map.Entry<String, String> entry : mLabels.entrySet()) {
+            localeLabels.put(new Locale(entry.getKey()), entry.getValue());
+        }
+        return localeLabels;
+    }
+
+    /**
+     * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
+     */
+    public Locale getLocale() {
+        return new Locale(mLanguage);
+    }
+
+    /**
+     * @return the mastering indication of the audio presentation.
+     * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO},
+     * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE}
+     */
+    @MasteringIndicationType
+    public int getMasteringIndication() {
+        return mMasteringIndication;
+    }
+
+    /**
+     * Indicates whether an audio description for the visually impaired is available.
+     * @return {@code true} if audio description is available.
+     */
+    public boolean hasAudioDescription() {
+        return mAudioDescriptionAvailable;
+    }
+
+    /**
+     * Indicates whether spoken subtitles for the visually impaired are available.
+     * @return {@code true} if spoken subtitles are available.
+     */
+    public boolean hasSpokenSubtitles() {
+        return mSpokenSubtitlesAvailable;
+    }
+
+    /**
+     * Indicates whether dialogue enhancement is available.
+     * @return {@code true} if dialogue enhancement is available.
+     */
+    public boolean hasDialogueEnhancement() {
+        return mDialogueEnhancementAvailable;
+    }
+}
diff --git a/android/media/AudioRecord.java b/android/media/AudioRecord.java
index 27784e9..4f0dccb 100644
--- a/android/media/AudioRecord.java
+++ b/android/media/AudioRecord.java
@@ -16,12 +16,15 @@
 
 package android.media;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.nio.ByteBuffer;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -32,10 +35,13 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -1314,6 +1320,23 @@
         return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING);
     }
 
+    /**
+     *  Return Metrics data about the current AudioTrack instance.
+     *
+     * @return a {@link PersistableBundle} containing the set of attributes and values
+     * available for the media being handled by this instance of AudioRecord
+     * The attributes are descibed in {@link MetricsConstants}.
+     *
+     * Additional vendor-specific fields may also be present in
+     * the return value.
+     */
+    public PersistableBundle getMetrics() {
+        PersistableBundle bundle = native_getMetrics();
+        return bundle;
+    }
+
+    private native PersistableBundle native_getMetrics();
+
     //--------------------------------------------------------------------------
     // Initialization / configuration
     //--------------------
@@ -1394,6 +1417,7 @@
     /*
      * Call BEFORE adding a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void testEnableNativeRoutingCallbacksLocked() {
         if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback();
@@ -1403,6 +1427,7 @@
     /*
      * Call AFTER removing a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void testDisableNativeRoutingCallbacksLocked() {
         if (mRoutingChangeListeners.size() == 0) {
             native_disableDeviceCallback();
@@ -1583,6 +1608,46 @@
         }
     }
 
+    //--------------------------------------------------------------------------
+    // Microphone information
+    //--------------------
+    /**
+     * Returns a lists of {@link MicrophoneInfo} representing the active microphones.
+     * By querying channel mapping for each active microphone, developer can know how
+     * the microphone is used by each channels or a capture stream.
+     * Note that the information about the active microphones may change during a recording.
+     * See {@link AudioManager#registerAudioDeviceCallback} to be notified of changes
+     * in the audio devices, querying the active microphones then will return the latest
+     * information.
+     *
+     * @return a lists of {@link MicrophoneInfo} representing the active microphones.
+     * @throws IOException if an error occurs
+     */
+    public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+        int status = native_get_active_microphones(activeMicrophones);
+        if (status != AudioManager.SUCCESS) {
+            Log.e(TAG, "getActiveMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>();
+        }
+        AudioManager.setPortIdForMicrophones(activeMicrophones);
+
+        // Use routed device when there is not information returned by hal.
+        if (activeMicrophones.size() == 0) {
+            AudioDeviceInfo device = getRoutedDevice();
+            if (device != null) {
+                MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
+                ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
+                for (int i = 0; i < mChannelCount; i++) {
+                    channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
+                }
+                microphone.setChannelMapping(channelMapping);
+                activeMicrophones.add(microphone);
+            }
+        }
+        return activeMicrophones;
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -1728,6 +1793,9 @@
     private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
             @AudioTimestamp.Timebase int timebase);
 
+    private native final int native_get_active_microphones(
+            ArrayList<MicrophoneInfo> activeMicrophones);
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
@@ -1739,4 +1807,46 @@
     private static void loge(String msg) {
         Log.e(TAG, msg);
     }
+
+    public static final class MetricsConstants
+    {
+        private MetricsConstants() {}
+
+        /**
+         * Key to extract the output format being recorded
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String ENCODING = "android.media.audiorecord.encoding";
+
+        /**
+         * Key to extract the Source Type for this track
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String SOURCE = "android.media.audiorecord.source";
+
+        /**
+         * Key to extract the estimated latency through the recording pipeline
+         * from the {@link AudioRecord#getMetrics} return value.
+         * This is in units of milliseconds.
+         * The value is an integer.
+         */
+        public static final String LATENCY = "android.media.audiorecord.latency";
+
+        /**
+         * Key to extract the sink sample rate for this record track in Hz
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String SAMPLERATE = "android.media.audiorecord.samplerate";
+
+        /**
+         * Key to extract the number of channels being recorded in this record track
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String CHANNELS = "android.media.audiorecord.channels";
+
+    }
 }
diff --git a/android/media/AudioSystem.java b/android/media/AudioSystem.java
index dcd37da..aaba1e3 100644
--- a/android/media/AudioSystem.java
+++ b/android/media/AudioSystem.java
@@ -401,6 +401,7 @@
     public static final int DEVICE_OUT_BUS = 0x1000000;
     public static final int DEVICE_OUT_PROXY = 0x2000000;
     public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;
+    public static final int DEVICE_OUT_HEARING_AID = 0x8000000;
 
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
 
@@ -431,6 +432,7 @@
                                               DEVICE_OUT_BUS |
                                               DEVICE_OUT_PROXY |
                                               DEVICE_OUT_USB_HEADSET |
+                                              DEVICE_OUT_HEARING_AID |
                                               DEVICE_OUT_DEFAULT);
     public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
                                                    DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
@@ -546,6 +548,7 @@
     public static final String DEVICE_OUT_BUS_NAME = "bus";
     public static final String DEVICE_OUT_PROXY_NAME = "proxy";
     public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
+    public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
 
     public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
     public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
@@ -628,6 +631,8 @@
             return DEVICE_OUT_PROXY_NAME;
         case DEVICE_OUT_USB_HEADSET:
             return DEVICE_OUT_USB_HEADSET_NAME;
+        case DEVICE_OUT_HEARING_AID:
+            return DEVICE_OUT_HEARING_AID_NAME;
         case DEVICE_OUT_DEFAULT:
         default:
             return Integer.toString(device);
@@ -742,7 +747,8 @@
     public static final int FOR_SYSTEM = 4;
     public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
     public static final int FOR_ENCODED_SURROUND = 6;
-    private static final int NUM_FORCE_USE = 7;
+    public static final int FOR_VIBRATE_RINGING = 7;
+    private static final int NUM_FORCE_USE = 8;
 
     public static String forceUseUsageToString(int usage) {
         switch (usage) {
@@ -753,6 +759,7 @@
             case FOR_SYSTEM: return "FOR_SYSTEM";
             case FOR_HDMI_SYSTEM_AUDIO: return "FOR_HDMI_SYSTEM_AUDIO";
             case FOR_ENCODED_SURROUND: return "FOR_ENCODED_SURROUND";
+            case FOR_VIBRATE_RINGING: return "FOR_VIBRATE_RINGING";
             default: return "unknown usage (" + usage + ")" ;
         }
     }
@@ -827,6 +834,8 @@
     private static native boolean native_is_offload_supported(int encoding, int sampleRate,
             int channelMask, int channelIndexMask);
 
+    public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
+
     // Items shared with audio service
 
     /**
diff --git a/android/media/AudioTrack.java b/android/media/AudioTrack.java
index 5928d03..87b5d43 100644
--- a/android/media/AudioTrack.java
+++ b/android/media/AudioTrack.java
@@ -36,6 +36,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -895,6 +896,7 @@
         }
 
         /**
+         * @hide
          * Sets whether this track will play through the offloaded audio path.
          * When set to true, at build time, the audio format will be checked against
          * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
@@ -1718,6 +1720,23 @@
          return ret;
      }
 
+    /**
+     *  Return Metrics data about the current AudioTrack instance.
+     *
+     * @return a {@link PersistableBundle} containing the set of attributes and values
+     * available for the media being handled by this instance of AudioTrack
+     * The attributes are descibed in {@link MetricsConstants}.
+     *
+     * Additional vendor-specific fields may also be present in
+     * the return value.
+     */
+    public PersistableBundle getMetrics() {
+        PersistableBundle bundle = native_getMetrics();
+        return bundle;
+    }
+
+    private native PersistableBundle native_getMetrics();
+
     //--------------------------------------------------------------------------
     // Initialization / configuration
     //--------------------
@@ -1990,6 +2009,26 @@
     }
 
     /**
+     * Sets the audio presentation.
+     * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned.
+     * If a multi-stream decoder (MSD) is not present, or the format does not support
+     * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned.
+     * {@link #ERROR} is returned in case of any other error.
+     * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+     * @return error code or success, see {@link #SUCCESS}, {@link #ERROR},
+     *    {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+     * @throws IllegalArgumentException if the audio presentation is null.
+     * @throws IllegalStateException if track is not initialized.
+     */
+    public int setPresentation(@NonNull AudioPresentation presentation) {
+        if (presentation == null) {
+            throw new IllegalArgumentException("audio presentation is null");
+        }
+        return native_setPresentation(presentation.getPresentationId(),
+                presentation.getProgramId());
+    }
+
+    /**
      * Sets the initialization state of the instance. This method was originally intended to be used
      * in an AudioTrack subclass constructor to set a subclass-specific post-initialization state.
      * However, subclasses of AudioTrack are no longer recommended, so this method is obsolete.
@@ -2784,6 +2823,7 @@
     /*
      * Call BEFORE adding a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void testEnableNativeRoutingCallbacksLocked() {
         if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback();
@@ -2793,6 +2833,7 @@
     /*
      * Call AFTER removing a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void testDisableNativeRoutingCallbacksLocked() {
         if (mRoutingChangeListeners.size() == 0) {
             native_disableDeviceCallback();
@@ -2939,6 +2980,7 @@
     }
 
     /**
+     * @hide
      * Abstract class to receive event notification about the stream playback.
      * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
      * the callback on the given {@link AudioTrack} instance.
@@ -2972,6 +3014,7 @@
     private final Object mStreamEventCbLock = new Object();
 
     /**
+     * @hide
      * Sets the callback for the notification of stream events.
      * @param executor {@link Executor} to handle the callbacks
      * @param eventCallback the callback to receive the stream event notifications
@@ -2991,6 +3034,7 @@
     }
 
     /**
+     * @hide
      * Unregisters the callback for notification of stream events, previously set
      * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
      */
@@ -3227,6 +3271,7 @@
             @NonNull VolumeShaper.Operation operation);
 
     private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+    private native final int native_setPresentation(int presentationId, int programId);
 
     //---------------------------------------------------------
     // Utility methods
@@ -3239,4 +3284,46 @@
     private static void loge(String msg) {
         Log.e(TAG, msg);
     }
+
+    public final static class MetricsConstants
+    {
+        private MetricsConstants() {}
+
+        /**
+         * Key to extract the Stream Type for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String STREAMTYPE = "android.media.audiotrack.streamtype";
+
+        /**
+         * Key to extract the Content Type for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String CONTENTTYPE = "android.media.audiotrack.type";
+
+        /**
+         * Key to extract the Content Type for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String USAGE = "android.media.audiotrack.usage";
+
+        /**
+         * Key to extract the sample rate for this track in Hz
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String SAMPLERATE = "android.media.audiorecord.samplerate";
+
+        /**
+         * Key to extract the channel mask information for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         *
+         * The value is a Long integer.
+         */
+        public static final String CHANNELMASK = "android.media.audiorecord.channelmask";
+
+    }
 }
diff --git a/android/media/ClosedCaptionRenderer.java b/android/media/ClosedCaptionRenderer.java
index cc7722a..66759e5 100644
--- a/android/media/ClosedCaptionRenderer.java
+++ b/android/media/ClosedCaptionRenderer.java
@@ -59,7 +59,7 @@
     public boolean supports(MediaFormat format) {
         if (format.containsKey(MediaFormat.KEY_MIME)) {
             String mimeType = format.getString(MediaFormat.KEY_MIME);
-            return MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType);
+            return MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType);
         }
         return false;
     }
@@ -67,7 +67,7 @@
     @Override
     public SubtitleTrack createTrack(MediaFormat format) {
         String mimeType = format.getString(MediaFormat.KEY_MIME);
-        if (MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType)) {
+        if (MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType)) {
             if (mCCWidget == null) {
                 mCCWidget = new Cea608CCWidget(mContext);
             }
diff --git a/android/media/DataSourceDesc.java b/android/media/DataSourceDesc.java
index 73fad7a..a53fa11 100644
--- a/android/media/DataSourceDesc.java
+++ b/android/media/DataSourceDesc.java
@@ -30,6 +30,8 @@
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.CookieHandler;
+import java.net.CookieManager;
 import java.net.HttpCookie;
 
 import java.util.ArrayList;
@@ -38,6 +40,7 @@
 import java.util.Map;
 
 /**
+ * @hide
  * Structure for data source descriptor.
  *
  * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
@@ -72,7 +75,7 @@
     private List<HttpCookie> mUriCookies;
     private Context mUriContext;
 
-    private long mId = 0;
+    private String mMediaId;
     private long mStartPositionMs = 0;
     private long mEndPositionMs = LONG_MAX;
 
@@ -80,11 +83,11 @@
     }
 
     /**
-     * Return the Id of data source.
-     * @return the Id of data source
+     * Return the media Id of data source.
+     * @return the media Id of data source
      */
-    public long getId() {
-        return mId;
+    public String getMediaId() {
+        return mMediaId;
     }
 
     /**
@@ -220,7 +223,7 @@
         private List<HttpCookie> mUriCookies;
         private Context mUriContext;
 
-        private long mId = 0;
+        private String mMediaId;
         private long mStartPositionMs = 0;
         private long mEndPositionMs = LONG_MAX;
 
@@ -246,7 +249,7 @@
             mUriCookies = dsd.mUriCookies;
             mUriContext = dsd.mUriContext;
 
-            mId = dsd.mId;
+            mMediaId = dsd.mMediaId;
             mStartPositionMs = dsd.mStartPositionMs;
             mEndPositionMs = dsd.mEndPositionMs;
         }
@@ -280,7 +283,7 @@
             dsd.mUriCookies = mUriCookies;
             dsd.mUriContext = mUriContext;
 
-            dsd.mId = mId;
+            dsd.mMediaId = mMediaId;
             dsd.mStartPositionMs = mStartPositionMs;
             dsd.mEndPositionMs = mEndPositionMs;
 
@@ -288,13 +291,13 @@
         }
 
         /**
-         * Sets the Id of this data source.
+         * Sets the media Id of this data source.
          *
-         * @param id the Id of this data source
+         * @param mediaId the media Id of this data source
          * @return the same Builder instance.
          */
-        public Builder setId(long id) {
-            mId = id;
+        public Builder setMediaId(String mediaId) {
+            mMediaId = mediaId;
             return this;
         }
 
@@ -433,10 +436,22 @@
          * @param cookies the cookies to be sent together with the request
          * @return the same Builder instance.
          * @throws NullPointerException if context or uri is null.
+         * @throws IllegalArgumentException if the cookie handler is not of CookieManager type
+         *                                  when cookies are provided.
          */
         public Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
                 @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
+            Preconditions.checkNotNull(context, "context cannot be null");
             Preconditions.checkNotNull(uri);
+            if (cookies != null) {
+                CookieHandler cookieHandler = CookieHandler.getDefault();
+                if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
+                    throw new IllegalArgumentException(
+                            "The cookie handler has to be of CookieManager type "
+                            + "when cookies are provided.");
+                }
+            }
+
             resetDataSource();
             mType = TYPE_URI;
             mUri = uri;
diff --git a/android/media/ExifInterface.java b/android/media/ExifInterface.java
index 9175416..bc0e43b 100644
--- a/android/media/ExifInterface.java
+++ b/android/media/ExifInterface.java
@@ -3226,9 +3226,18 @@
 
         if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
             long[] stripOffsets =
-                    (long[]) stripOffsetsAttribute.getValue(mExifByteOrder);
+                    convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
             long[] stripByteCounts =
-                    (long[]) stripByteCountsAttribute.getValue(mExifByteOrder);
+                    convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
+
+            if (stripOffsets == null) {
+                Log.w(TAG, "stripOffsets should not be null.");
+                return;
+            }
+            if (stripByteCounts == null) {
+                Log.w(TAG, "stripByteCounts should not be null.");
+                return;
+            }
 
             // Set thumbnail byte array data for non-consecutive strip bytes
             byte[] totalStripBytes =
@@ -4025,4 +4034,22 @@
         }
         return false;
     }
+
+    /**
+     * Convert given int[] to long[]. If long[] is given, just return it.
+     * Return null for other types of input.
+     */
+    private static long[] convertToLongArray(Object inputObj) {
+        if (inputObj instanceof int[]) {
+            int[] input = (int[]) inputObj;
+            long[] result = new long[input.length];
+            for (int i = 0; i < input.length; i++) {
+                result[i] = input[i];
+            }
+            return result;
+        } else if (inputObj instanceof long[]) {
+            return (long[]) inputObj;
+        }
+        return null;
+    }
 }
diff --git a/android/media/Image.java b/android/media/Image.java
index fbe5561..37c5785 100644
--- a/android/media/Image.java
+++ b/android/media/Image.java
@@ -19,7 +19,9 @@
 import java.nio.ByteBuffer;
 import java.lang.AutoCloseable;
 
+import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
 
 /**
  * <p>A single complete image buffer to use with a media source such as a
@@ -184,6 +186,30 @@
     public abstract long getTimestamp();
 
     /**
+     * Get the transformation associated with this frame.
+     * @return The window transformation that needs to be applied for this frame.
+     * @hide
+     */
+    public abstract int getTransform();
+
+    /**
+     * Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image
+     * intended for GPU and/or hardware access.
+     * <p>
+     * The returned {@link android.hardware.HardwareBuffer HardwareBuffer} shall not be used
+     * after  {@link Image#close Image.close()} has been called.
+     * </p>
+     * @return the HardwareBuffer associated with this Image or null if this Image doesn't support
+     * this feature (e.g. {@link android.media.ImageWriter ImageWriter} or
+     * {@link android.media.MediaCodec MediaCodec} don't).
+     */
+    @Nullable
+    public HardwareBuffer getHardwareBuffer() {
+        throwISEIfImageIsInvalid();
+        return null;
+    }
+
+    /**
      * Set the timestamp associated with this frame.
      * <p>
      * The timestamp is measured in nanoseconds, and is normally monotonically
diff --git a/android/media/ImageReader.java b/android/media/ImageReader.java
index 1019580..72d52d3 100644
--- a/android/media/ImageReader.java
+++ b/android/media/ImageReader.java
@@ -727,18 +727,7 @@
             return false;
         }
 
-        if (format == ImageFormat.PRIVATE) {
-            // Usage need to be either USAGE0_GPU_SAMPLED_IMAGE or USAGE0_VIDEO_ENCODE or combined.
-            boolean isAllowed = (usage == HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
-            isAllowed = isAllowed || (usage == HardwareBuffer.USAGE_VIDEO_ENCODE);
-            isAllowed = isAllowed || (usage ==
-                    (HardwareBuffer.USAGE_VIDEO_ENCODE | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE));
-            return isAllowed;
-        } else {
-            // Usage need to make the buffer CPU readable for explicit format.
-            return ((usage == HardwareBuffer.USAGE_CPU_READ_RARELY) ||
-                    (usage == HardwareBuffer.USAGE_CPU_READ_OFTEN));
-        }
+        return true;
     }
 
     /**
@@ -876,6 +865,18 @@
         }
 
         @Override
+        public int getTransform() {
+            throwISEIfImageIsInvalid();
+            return mTransform;
+        }
+
+        @Override
+        public HardwareBuffer getHardwareBuffer() {
+            throwISEIfImageIsInvalid();
+            return nativeGetHardwareBuffer();
+        }
+
+        @Override
         public void setTimestamp(long timestampNs) {
             throwISEIfImageIsInvalid();
             mTimestamp = timestampNs;
@@ -1007,6 +1008,11 @@
          */
         private long mTimestamp;
 
+        /**
+         * This field is set by native code during nativeImageSetup().
+         */
+        private int mTransform;
+
         private SurfacePlane[] mPlanes;
         private int mFormat = ImageFormat.UNKNOWN;
         // If this image is detached from the ImageReader.
@@ -1017,6 +1023,7 @@
         private synchronized native int nativeGetWidth();
         private synchronized native int nativeGetHeight();
         private synchronized native int nativeGetFormat(int readerFormat);
+        private synchronized native HardwareBuffer nativeGetHardwareBuffer();
     }
 
     private synchronized native void nativeInit(Object weakSelf, int w, int h,
diff --git a/android/media/ImageWriter.java b/android/media/ImageWriter.java
index 2b7309f..8ee27ae 100644
--- a/android/media/ImageWriter.java
+++ b/android/media/ImageWriter.java
@@ -371,7 +371,7 @@
 
         Rect crop = image.getCropRect();
         nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top,
-                crop.right, crop.bottom);
+                crop.right, crop.bottom, image.getTransform());
 
         /**
          * Only remove and cleanup the Images that are owned by this
@@ -557,7 +557,8 @@
         // buffer caused leak.
         Rect crop = image.getCropRect();
         nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
-                image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom);
+                image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
+                image.getTransform());
     }
 
     /**
@@ -674,6 +675,8 @@
         private final long DEFAULT_TIMESTAMP = Long.MIN_VALUE;
         private long mTimestamp = DEFAULT_TIMESTAMP;
 
+        private int mTransform = 0; //Default no transform
+
         public WriterSurfaceImage(ImageWriter writer) {
             mOwner = writer;
         }
@@ -711,6 +714,13 @@
         }
 
         @Override
+        public int getTransform() {
+            throwISEIfImageIsInvalid();
+
+            return mTransform;
+        }
+
+        @Override
         public long getTimestamp() {
             throwISEIfImageIsInvalid();
 
@@ -856,11 +866,11 @@
     private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi);
 
     private synchronized native void nativeQueueInputImage(long nativeCtx, Image image,
-            long timestampNs, int left, int top, int right, int bottom);
+            long timestampNs, int left, int top, int right, int bottom, int transform);
 
     private synchronized native int nativeAttachAndQueueImage(long nativeCtx,
             long imageNativeBuffer, int imageFormat, long timestampNs, int left,
-            int top, int right, int bottom);
+            int top, int right, int bottom, int transform);
 
     private synchronized native void cancelImage(long nativeCtx, Image image);
 
diff --git a/android/media/Media2DataSource.java b/android/media/Media2DataSource.java
index 8ee4a70..08df632 100644
--- a/android/media/Media2DataSource.java
+++ b/android/media/Media2DataSource.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 
 /**
+ * @hide
  * For supplying media data to the framework. Implement this if your app has
  * special requirements for the way media data is obtained.
  *
diff --git a/android/media/MediaActionSound.java b/android/media/MediaActionSound.java
index 983ca75..dcd4dce 100644
--- a/android/media/MediaActionSound.java
+++ b/android/media/MediaActionSound.java
@@ -47,11 +47,16 @@
     private SoundPool mSoundPool;
     private SoundState[] mSounds;
 
+    private static final String[] SOUND_DIRS = {
+        "/product/media/audio/ui/",
+        "/system/media/audio/ui/",
+    };
+
     private static final String[] SOUND_FILES = {
-        "/system/media/audio/ui/camera_click.ogg",
-        "/system/media/audio/ui/camera_focus.ogg",
-        "/system/media/audio/ui/VideoRecord.ogg",
-        "/system/media/audio/ui/VideoStop.ogg"
+        "camera_click.ogg",
+        "camera_focus.ogg",
+        "VideoRecord.ogg",
+        "VideoStop.ogg"
     };
 
     private static final String TAG = "MediaActionSound";
@@ -132,12 +137,16 @@
     }
 
     private int loadSound(SoundState sound) {
-        int id = mSoundPool.load(SOUND_FILES[sound.name], 1);
-        if (id > 0) {
-            sound.state = STATE_LOADING;
-            sound.id = id;
+        final String soundFileName = SOUND_FILES[sound.name];
+        for (String soundDir : SOUND_DIRS) {
+            int id = mSoundPool.load(soundDir + soundFileName, 1);
+            if (id > 0) {
+                sound.state = STATE_LOADING;
+                sound.id = id;
+                return id;
+            }
         }
-        return id;
+        return 0;
     }
 
     /**
diff --git a/android/media/MediaBrowser2.java b/android/media/MediaBrowser2.java
index be4be3f..452371a 100644
--- a/android/media/MediaBrowser2.java
+++ b/android/media/MediaBrowser2.java
@@ -16,9 +16,12 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
 import android.media.update.MediaBrowser2Provider;
 import android.os.Bundle;
@@ -27,8 +30,8 @@
 import java.util.concurrent.Executor;
 
 /**
- * Browses media content offered by a {@link MediaLibraryService2}.
  * @hide
+ * Browses media content offered by a {@link MediaLibraryService2}.
  */
 public class MediaBrowser2 extends MediaController2 {
     // Equals to the ((MediaBrowser2Provider) getProvider())
@@ -39,138 +42,197 @@
      */
     public static class BrowserCallback extends MediaController2.ControllerCallback {
         /**
-         * Called with the result of {@link #getBrowserRoot(Bundle)}.
+         * Called with the result of {@link #getLibraryRoot(Bundle)}.
          * <p>
-         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the library root isn't
          * available.
          *
+         * @param browser the browser for this event
          * @param rootHints rootHints that you previously requested.
-         * @param rootMediaId media id of the browser root. Can be {@code null}
-         * @param rootExtra extra of the browser root. Can be {@code null}
+         * @param rootMediaId media id of the library root. Can be {@code null}
+         * @param rootExtra extra of the library root. Can be {@code null}
          */
-        public void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
-                @Nullable Bundle rootExtra) { }
+        public void onGetLibraryRootDone(@NonNull MediaBrowser2 browser, @Nullable Bundle rootHints,
+                @Nullable String rootMediaId, @Nullable Bundle rootExtra) { }
 
         /**
-         * Called when the item has been returned by the library service for the previous
-         * {@link MediaBrowser2#getItem} call.
+         * Called when there's change in the parent's children.
          * <p>
-         * Result can be null if there had been error.
+         * This API is called when the library service called
+         * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)} or
+         * {@link MediaLibrarySession#notifyChildrenChanged(String, int, Bundle)} for the parent.
          *
-         * @param mediaId media id
-         * @param result result. Can be {@code null}
+         * @param browser the browser for this event
+         * @param parentId parent id that you've specified with {@link #subscribe(String, Bundle)}
+         * @param itemCount number of children
+         * @param extras extra bundle from the library service. Can be differ from extras that
+         *               you've specified with {@link #subscribe(String, Bundle)}.
          */
-        public void onItemLoaded(@NonNull String mediaId, @Nullable MediaItem2 result) { }
+        public void onChildrenChanged(@NonNull MediaBrowser2 browser, @NonNull String parentId,
+                int itemCount, @Nullable Bundle extras) { }
 
         /**
          * Called when the list of items has been returned by the library service for the previous
          * {@link MediaBrowser2#getChildren(String, int, int, Bundle)}.
          *
+         * @param browser the browser for this event
          * @param parentId parent id
-         * @param page page number that you've specified
-         * @param pageSize page size that you've specified
-         * @param options optional bundle that you've specified
+         * @param page page number that you've specified with
+         *             {@link #getChildren(String, int, int, Bundle)}
+         * @param pageSize page size that you've specified with
+         *                 {@link #getChildren(String, int, int, Bundle)}
          * @param result result. Can be {@code null}
+         * @param extras extra bundle from the library service
          */
-        public void onChildrenLoaded(@NonNull String parentId, int page, int pageSize,
-                @Nullable Bundle options, @Nullable List<MediaItem2> result) { }
+        public void onGetChildrenDone(@NonNull MediaBrowser2 browser, @NonNull String parentId,
+                int page, int pageSize, @Nullable List<MediaItem2> result,
+                @Nullable Bundle extras) { }
 
         /**
-         * Called when there's change in the parent's children.
-         *
-         * @param parentId parent id that you've specified with subscribe
-         * @param options optional bundle that you've specified with subscribe
-         */
-        public void onChildrenChanged(@NonNull String parentId, @Nullable Bundle options) { }
-
-        /**
-         * Called when the search result has been returned by the library service for the previous
-         * {@link MediaBrowser2#search(String, int, int, Bundle)}.
+         * Called when the item has been returned by the library service for the previous
+         * {@link MediaBrowser2#getItem(String)} call.
          * <p>
          * Result can be null if there had been error.
          *
-         * @param query query string that you've specified
-         * @param page page number that you've specified
-         * @param pageSize page size that you've specified
-         * @param options optional bundle that you've specified
+         * @param browser the browser for this event
+         * @param mediaId media id
          * @param result result. Can be {@code null}
          */
-        public void onSearchResult(@NonNull String query, int page, int pageSize,
-                @Nullable Bundle options, @Nullable List<MediaItem2> result) { }
+        public void onGetItemDone(@NonNull MediaBrowser2 browser, @NonNull String mediaId,
+                @Nullable MediaItem2 result) { }
+
+        /**
+         * Called when there's change in the search result requested by the previous
+         * {@link MediaBrowser2#search(String, Bundle)}.
+         *
+         * @param browser the browser for this event
+         * @param query search query that you've specified with {@link #search(String, Bundle)}
+         * @param itemCount The item count for the search result
+         * @param extras extra bundle from the library service
+         */
+        public void onSearchResultChanged(@NonNull MediaBrowser2 browser, @NonNull String query,
+                int itemCount, @Nullable Bundle extras) { }
+
+        /**
+         * Called when the search result has been returned by the library service for the previous
+         * {@link MediaBrowser2#getSearchResult(String, int, int, Bundle)}.
+         * <p>
+         * Result can be null if there had been error.
+         *
+         * @param browser the browser for this event
+         * @param query search query that you've specified with
+         *              {@link #getSearchResult(String, int, int, Bundle)}
+         * @param page page number that you've specified with
+         *             {@link #getSearchResult(String, int, int, Bundle)}
+         * @param pageSize page size that you've specified with
+         *                 {@link #getSearchResult(String, int, int, Bundle)}
+         * @param result result. Can be {@code null}.
+         * @param extras extra bundle from the library service
+         */
+        public void onGetSearchResultDone(@NonNull MediaBrowser2 browser, @NonNull String query,
+                int page, int pageSize, @Nullable List<MediaItem2> result,
+                @Nullable Bundle extras) { }
     }
 
-    public MediaBrowser2(Context context, SessionToken2 token, BrowserCallback callback,
-            Executor executor) {
-        super(context, token, callback, executor);
+    public MediaBrowser2(@NonNull Context context, @NonNull SessionToken2 token,
+            @NonNull @CallbackExecutor Executor executor, @NonNull BrowserCallback callback) {
+        super(context, token, executor, callback);
         mProvider = (MediaBrowser2Provider) getProvider();
     }
 
     @Override
     MediaBrowser2Provider createProvider(Context context, SessionToken2 token,
-            ControllerCallback callback, Executor executor) {
-        return ApiLoader.getProvider(context)
-                .createMediaBrowser2(this, context, token, (BrowserCallback) callback, executor);
+            Executor executor, ControllerCallback callback) {
+        return ApiLoader.getProvider().createMediaBrowser2(
+                context, this, token, executor, (BrowserCallback) callback);
     }
 
-    public void getBrowserRoot(Bundle rootHints) {
-        mProvider.getBrowserRoot_impl(rootHints);
+    /**
+     * Get the library root. Result would be sent back asynchronously with the
+     * {@link BrowserCallback#onGetLibraryRootDone(MediaBrowser2, Bundle, String, Bundle)}.
+     *
+     * @param rootHints hint for the root
+     * @see BrowserCallback#onGetLibraryRootDone(MediaBrowser2, Bundle, String, Bundle)
+     */
+    public void getLibraryRoot(@Nullable Bundle rootHints) {
+        mProvider.getLibraryRoot_impl(rootHints);
     }
 
     /**
      * Subscribe to a parent id for the change in its children. When there's a change,
-     * {@link BrowserCallback#onChildrenChanged(String, Bundle)} will be called with the bundle
-     * that you've specified. You should call {@link #getChildren(String, int, int, Bundle)} to get
-     * the actual contents for the parent.
+     * {@link BrowserCallback#onChildrenChanged(MediaBrowser2, String, int, Bundle)} will be called
+     * with the bundle that you've specified. You should call
+     * {@link #getChildren(String, int, int, Bundle)} to get the actual contents for the parent.
      *
      * @param parentId parent id
-     * @param options optional bundle
+     * @param extras extra bundle
      */
-    public void subscribe(String parentId, @Nullable Bundle options) {
-        mProvider.subscribe_impl(parentId, options);
+    public void subscribe(@NonNull String parentId, @Nullable Bundle extras) {
+        mProvider.subscribe_impl(parentId, extras);
     }
 
     /**
      * Unsubscribe for changes to the children of the parent, which was previously subscribed with
      * {@link #subscribe(String, Bundle)}.
+     * <p>
+     * This unsubscribes all previous subscription with the parent id, regardless of the extra
+     * that was previously sent to the library service.
      *
      * @param parentId parent id
-     * @param options optional bundle
      */
-    public void unsubscribe(String parentId, @Nullable Bundle options) {
-        mProvider.unsubscribe_impl(parentId, options);
-    }
-
-    /**
-     * Get the media item with the given media id. Result would be sent back asynchronously with the
-     * {@link BrowserCallback#onItemLoaded(String, MediaItem2)}.
-     *
-     * @param mediaId media id
-     */
-    public void getItem(String mediaId) {
-        mProvider.getItem_impl(mediaId);
+    public void unsubscribe(@NonNull String parentId) {
+        mProvider.unsubscribe_impl(parentId);
     }
 
     /**
      * Get list of children under the parent. Result would be sent back asynchronously with the
-     * {@link BrowserCallback#onChildrenLoaded(String, int, int, Bundle, List)}.
+     * {@link BrowserCallback#onGetChildrenDone(MediaBrowser2, String, int, int, List, Bundle)}.
      *
-     * @param parentId
-     * @param page
-     * @param pageSize
-     * @param options
+     * @param parentId parent id for getting the children.
+     * @param page page number to get the result. Starts from {@code 1}
+     * @param pageSize page size. Should be greater or equal to {@code 1}
+     * @param extras extra bundle
      */
-    public void getChildren(String parentId, int page, int pageSize, @Nullable Bundle options) {
-        mProvider.getChildren_impl(parentId, page, pageSize, options);
+    public void getChildren(@NonNull String parentId, int page, int pageSize,
+            @Nullable Bundle extras) {
+        mProvider.getChildren_impl(parentId, page, pageSize, extras);
     }
 
     /**
+     * Get the media item with the given media id. Result would be sent back asynchronously with the
+     * {@link BrowserCallback#onGetItemDone(MediaBrowser2, String, MediaItem2)}.
      *
-     * @param query search query deliminated by string
+     * @param mediaId media id for specifying the item
+     */
+    public void getItem(@NonNull String mediaId) {
+        mProvider.getItem_impl(mediaId);
+    }
+
+    /**
+     * Send a search request to the library service. When the search result is changed,
+     * {@link BrowserCallback#onSearchResultChanged(MediaBrowser2, String, int, Bundle)} will be
+     * called. You should call {@link #getSearchResult(String, int, int, Bundle)} to get the actual
+     * search result.
+     *
+     * @param query search query. Should not be an empty string.
+     * @param extras extra bundle
+     */
+    public void search(@NonNull String query, @Nullable Bundle extras) {
+        mProvider.search_impl(query, extras);
+    }
+
+    /**
+     * Get the search result from lhe library service. Result would be sent back asynchronously with
+     * the
+     * {@link BrowserCallback#onGetSearchResultDone(MediaBrowser2, String, int, int, List, Bundle)}.
+     *
+     * @param query search query that you've specified with {@link #search(String, Bundle)}
      * @param page page number to get search result. Starts from {@code 1}
      * @param pageSize page size. Should be greater or equal to {@code 1}
      * @param extras extra bundle
      */
-    public void search(String query, int page, int pageSize, Bundle extras) {
-        mProvider.search_impl(query, page, pageSize, extras);
+    public void getSearchResult(@NonNull String query, int page, int pageSize,
+            @Nullable Bundle extras) {
+        mProvider.getSearchResult_impl(query, page, pageSize, extras);
     }
 }
diff --git a/android/media/MediaBrowser2Test.java b/android/media/MediaBrowser2Test.java
deleted file mode 100644
index 5c960c8..0000000
--- a/android/media/MediaBrowser2Test.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.content.Context;
-import android.media.MediaBrowser2.BrowserCallback;
-import android.media.MediaSession2.CommandGroup;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-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.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests {@link MediaBrowser2}.
- * <p>
- * This test inherits {@link MediaController2Test} to ensure that inherited APIs from
- * {@link MediaController2} works cleanly.
- */
-// TODO(jaewan): Implement host-side test so browser and service can run in different processes.
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class MediaBrowser2Test extends MediaController2Test {
-    private static final String TAG = "MediaBrowser2Test";
-
-    @Override
-    TestControllerInterface onCreateController(@NonNull SessionToken2 token,
-            @NonNull TestControllerCallbackInterface callback) {
-        return new TestMediaBrowser(mContext, token, new TestBrowserCallback(callback));
-    }
-
-    @Test
-    public void testGetBrowserRoot() throws InterruptedException {
-        final Bundle param = new Bundle();
-        param.putString(TAG, TAG);
-
-        final CountDownLatch latch = new CountDownLatch(1);
-        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
-            @Override
-            public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
-                assertTrue(TestUtils.equals(param, rootHints));
-                assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
-                assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRA, rootExtra));
-                latch.countDown();
-            }
-        };
-
-        final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
-        MediaBrowser2 browser =
-                (MediaBrowser2) createController(token,true, callback);
-        browser.getBrowserRoot(param);
-        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-    }
-
-    public static class TestBrowserCallback extends BrowserCallback
-            implements WaitForConnectionInterface {
-        private final TestControllerCallbackInterface mCallbackProxy;
-        public final CountDownLatch connectLatch = new CountDownLatch(1);
-        public final CountDownLatch disconnectLatch = new CountDownLatch(1);
-
-        TestBrowserCallback(TestControllerCallbackInterface callbackProxy) {
-            mCallbackProxy = callbackProxy;
-        }
-
-        @CallSuper
-        @Override
-        public void onConnected(CommandGroup commands) {
-            super.onConnected(commands);
-            connectLatch.countDown();
-        }
-
-        @CallSuper
-        @Override
-        public void onDisconnected() {
-            super.onDisconnected();
-            disconnectLatch.countDown();
-        }
-
-        @Override
-        public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
-            mCallbackProxy.onGetRootResult(rootHints, rootMediaId, rootExtra);
-        }
-
-        @Override
-        public void waitForConnect(boolean expect) throws InterruptedException {
-            if (expect) {
-                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-            } else {
-                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            }
-        }
-
-        @Override
-        public void waitForDisconnect(boolean expect) throws InterruptedException {
-            if (expect) {
-                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-            } else {
-                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            }
-        }
-    }
-
-    public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface {
-        private final BrowserCallback mCallback;
-
-        public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken2 token,
-                @NonNull ControllerCallback callback) {
-            super(context, token, (BrowserCallback) callback, sHandlerExecutor);
-            mCallback = (BrowserCallback) callback;
-        }
-
-        @Override
-        public BrowserCallback getCallback() {
-            return mCallback;
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/media/MediaCodec.java b/android/media/MediaCodec.java
index 3d5f6bc..e3fba0c 100644
--- a/android/media/MediaCodec.java
+++ b/android/media/MediaCodec.java
@@ -1602,7 +1602,9 @@
     private EventHandler mCallbackHandler;
     private Callback mCallback;
     private OnFrameRenderedListener mOnFrameRenderedListener;
-    private Object mListenerLock = new Object();
+    private final Object mListenerLock = new Object();
+    private MediaCodecInfo mCodecInfo;
+    private final Object mCodecInfoLock = new Object();
 
     private static final int EVENT_CALLBACK = 1;
     private static final int EVENT_SET_CALLBACK = 2;
@@ -2357,17 +2359,61 @@
     public static final int CRYPTO_MODE_AES_CBC     = 2;
 
     /**
-     * Metadata describing the structure of a (at least partially) encrypted
-     * input sample.
-     * A buffer's data is considered to be partitioned into "subSamples",
-     * each subSample starts with a (potentially empty) run of plain,
-     * unencrypted bytes followed by a (also potentially empty) run of
-     * encrypted bytes. If pattern encryption applies, each of the latter runs
-     * is encrypted only partly, according to a repeating pattern of "encrypt"
-     * and "skip" blocks. numBytesOfClearData can be null to indicate that all
-     * data is encrypted. This information encapsulates per-sample metadata as
-     * outlined in ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base
-     * media file format files".
+     * Metadata describing the structure of an encrypted input sample.
+     * <p>
+     * A buffer's data is considered to be partitioned into "subSamples". Each subSample starts with
+     * a run of plain, unencrypted bytes followed by a run of encrypted bytes. Either of these runs
+     * may be empty. If pattern encryption applies, each of the encrypted runs is encrypted only
+     * partly, according to a repeating pattern of "encrypt" and "skip" blocks.
+     * {@link #numBytesOfClearData} can be null to indicate that all data is encrypted, and
+     * {@link #numBytesOfEncryptedData} can be null to indicate that all data is clear. At least one
+     * of {@link #numBytesOfClearData} and {@link #numBytesOfEncryptedData} must be non-null.
+     * <p>
+     * This information encapsulates per-sample metadata as outlined in ISO/IEC FDIS 23001-7:2016
+     * "Common encryption in ISO base media file format files".
+     * <p>
+     * <h3>ISO-CENC Schemes</h3>
+     * ISO/IEC FDIS 23001-7:2016 defines four possible schemes by which media may be encrypted,
+     * corresponding to each possible combination of an AES mode with the presence or absence of
+     * patterned encryption.
+     *
+     * <table style="width: 0%">
+     *   <thead>
+     *     <tr>
+     *       <th>&nbsp;</th>
+     *       <th>AES-CTR</th>
+     *       <th>AES-CBC</th>
+     *     </tr>
+     *   </thead>
+     *   <tbody>
+     *     <tr>
+     *       <th>Without Patterns</th>
+     *       <td>cenc</td>
+     *       <td>cbc1</td>
+     *     </tr><tr>
+     *       <th>With Patterns</th>
+     *       <td>cens</td>
+     *       <td>cbcs</td>
+     *     </tr>
+     *   </tbody>
+     * </table>
+     *
+     * For {@code CryptoInfo}, the scheme is selected implicitly by the combination of the
+     * {@link #mode} field and the value set with {@link #setPattern}. For the pattern, setting the
+     * pattern to all zeroes (that is, both {@code blocksToEncrypt} and {@code blocksToSkip} are
+     * zero) is interpreted as turning patterns off completely. A scheme that does not use patterns
+     * will be selected, either cenc or cbc1. Setting the pattern to any nonzero value will choose
+     * one of the pattern-supporting schemes, cens or cbcs. The default pattern if
+     * {@link #setPattern} is never called is all zeroes.
+     * <p>
+     * <h4>HLS SAMPLE-AES Audio</h4>
+     * HLS SAMPLE-AES audio is encrypted in a manner compatible with the cbcs scheme, except that it
+     * does not use patterned encryption. However, if {@link #setPattern} is used to set the pattern
+     * to all zeroes, this will be interpreted as selecting the cbc1 scheme. The cbc1 scheme cannot
+     * successfully decrypt HLS SAMPLE-AES audio because of differences in how the IVs are handled.
+     * For this reason, it is recommended that a pattern of {@code 1} encrypted block and {@code 0}
+     * skip blocks be used with HLS SAMPLE-AES audio. This will trigger decryption to use cbcs mode
+     * while still decrypting every block.
      */
     public final static class CryptoInfo {
         /**
@@ -2375,11 +2421,13 @@
          */
         public int numSubSamples;
         /**
-         * The number of leading unencrypted bytes in each subSample.
+         * The number of leading unencrypted bytes in each subSample. If null, all bytes are treated
+         * as encrypted and {@link #numBytesOfEncryptedData} must be specified.
          */
         public int[] numBytesOfClearData;
         /**
-         * The number of trailing encrypted bytes in each subSample.
+         * The number of trailing encrypted bytes in each subSample. If null, all bytes are treated
+         * as clear and {@link #numBytesOfClearData} must be specified.
          */
         public int[] numBytesOfEncryptedData;
         /**
@@ -2398,35 +2446,34 @@
         public int mode;
 
         /**
-         * Metadata describing an encryption pattern for the protected bytes in
-         * a subsample.  An encryption pattern consists of a repeating sequence
-         * of crypto blocks comprised of a number of encrypted blocks followed
-         * by a number of unencrypted, or skipped, blocks.
+         * Metadata describing an encryption pattern for the protected bytes in a subsample.  An
+         * encryption pattern consists of a repeating sequence of crypto blocks comprised of a
+         * number of encrypted blocks followed by a number of unencrypted, or skipped, blocks.
          */
         public final static class Pattern {
             /**
-             * Number of blocks to be encrypted in the pattern. If zero, pattern
-             * encryption is inoperative.
+             * Number of blocks to be encrypted in the pattern. If both this and
+             * {@link #mSkipBlocks} are zero, pattern encryption is inoperative.
              */
             private int mEncryptBlocks;
 
             /**
-             * Number of blocks to be skipped (left clear) in the pattern. If zero,
-             * pattern encryption is inoperative.
+             * Number of blocks to be skipped (left clear) in the pattern. If both this and
+             * {@link #mEncryptBlocks} are zero, pattern encryption is inoperative.
              */
             private int mSkipBlocks;
 
             /**
-             * Construct a sample encryption pattern given the number of blocks to
-             * encrypt and skip in the pattern.
+             * Construct a sample encryption pattern given the number of blocks to encrypt and skip
+             * in the pattern. If both parameters are zero, pattern encryption is inoperative.
              */
             public Pattern(int blocksToEncrypt, int blocksToSkip) {
                 set(blocksToEncrypt, blocksToSkip);
             }
 
             /**
-             * Set the number of blocks to encrypt and skip in a sample encryption
-             * pattern.
+             * Set the number of blocks to encrypt and skip in a sample encryption pattern. If both
+             * parameters are zero, pattern encryption is inoperative.
              */
             public void set(int blocksToEncrypt, int blocksToSkip) {
                 mEncryptBlocks = blocksToEncrypt;
@@ -3469,10 +3516,26 @@
      */
     @NonNull
     public MediaCodecInfo getCodecInfo() {
-        return MediaCodecList.getInfoFor(getName());
+        // Get the codec name first. If the codec is already released,
+        // IllegalStateException will be thrown here.
+        String name = getName();
+        synchronized (mCodecInfoLock) {
+            if (mCodecInfo == null) {
+                // Get the codec info for this codec itself first. Only initialize
+                // the full codec list if this somehow fails because it can be slow.
+                mCodecInfo = getOwnCodecInfo();
+                if (mCodecInfo == null) {
+                    mCodecInfo = MediaCodecList.getInfoFor(name);
+                }
+            }
+            return mCodecInfo;
+        }
     }
 
     @NonNull
+    private native final MediaCodecInfo getOwnCodecInfo();
+
+    @NonNull
     private native final ByteBuffer[] getBuffers(boolean input);
 
     @Nullable
@@ -3510,6 +3573,8 @@
 
         private final static int TYPE_YUV = 1;
 
+        private final int mTransform = 0; //Default no transform
+
         @Override
         public int getFormat() {
             throwISEIfImageIsInvalid();
@@ -3529,6 +3594,12 @@
         }
 
         @Override
+        public int getTransform() {
+            throwISEIfImageIsInvalid();
+            return mTransform;
+        }
+
+        @Override
         public long getTimestamp() {
             throwISEIfImageIsInvalid();
             return mTimestamp;
diff --git a/android/media/MediaCodecInfo.java b/android/media/MediaCodecInfo.java
index 44d9099..2a601f9 100644
--- a/android/media/MediaCodecInfo.java
+++ b/android/media/MediaCodecInfo.java
@@ -829,14 +829,24 @@
 
         /** @hide */
         public CodecCapabilities dup() {
-            return new CodecCapabilities(
-                // clone writable arrays
-                Arrays.copyOf(profileLevels, profileLevels.length),
-                Arrays.copyOf(colorFormats, colorFormats.length),
-                isEncoder(),
-                mFlagsVerified,
-                mDefaultFormat,
-                mCapabilitiesInfo);
+            CodecCapabilities caps = new CodecCapabilities();
+
+            // profileLevels and colorFormats may be modified by client.
+            caps.profileLevels = Arrays.copyOf(profileLevels, profileLevels.length);
+            caps.colorFormats = Arrays.copyOf(colorFormats, colorFormats.length);
+
+            caps.mMime = mMime;
+            caps.mMaxSupportedInstances = mMaxSupportedInstances;
+            caps.mFlagsRequired = mFlagsRequired;
+            caps.mFlagsSupported = mFlagsSupported;
+            caps.mFlagsVerified = mFlagsVerified;
+            caps.mAudioCaps = mAudioCaps;
+            caps.mVideoCaps = mVideoCaps;
+            caps.mEncoderCaps = mEncoderCaps;
+            caps.mDefaultFormat = mDefaultFormat;
+            caps.mCapabilitiesInfo = mCapabilitiesInfo;
+
+            return caps;
         }
 
         /**
@@ -898,13 +908,13 @@
 
             if (mMime.toLowerCase().startsWith("audio/")) {
                 mAudioCaps = AudioCapabilities.create(info, this);
-                mAudioCaps.setDefaultFormat(mDefaultFormat);
+                mAudioCaps.getDefaultFormat(mDefaultFormat);
             } else if (mMime.toLowerCase().startsWith("video/")) {
                 mVideoCaps = VideoCapabilities.create(info, this);
             }
             if (encoder) {
                 mEncoderCaps = EncoderCapabilities.create(info, this);
-                mEncoderCaps.setDefaultFormat(mDefaultFormat);
+                mEncoderCaps.getDefaultFormat(mDefaultFormat);
             }
 
             final Map<String, Object> global = MediaCodecList.getGlobalSettings();
@@ -990,8 +1000,7 @@
             return caps;
         }
 
-        /** @hide */
-        public void init(MediaFormat info, CodecCapabilities parent) {
+        private void init(MediaFormat info, CodecCapabilities parent) {
             mParent = parent;
             initWithPlatformLimits();
             applyLevelLimits();
@@ -1171,7 +1180,7 @@
         }
 
         /** @hide */
-        public void setDefaultFormat(MediaFormat format) {
+        public void getDefaultFormat(MediaFormat format) {
             // report settings that have only a single choice
             if (mBitrateRange.getLower().equals(mBitrateRange.getUpper())) {
                 format.setInteger(MediaFormat.KEY_BIT_RATE, mBitrateRange.getLower());
@@ -1585,8 +1594,7 @@
             return caps;
         }
 
-        /** @hide */
-        public void init(MediaFormat info, CodecCapabilities parent) {
+        private void init(MediaFormat info, CodecCapabilities parent) {
             mParent = parent;
             initWithPlatformLimits();
             applyLevelLimits();
@@ -2707,8 +2715,7 @@
             return caps;
         }
 
-        /** @hide */
-        public void init(MediaFormat info, CodecCapabilities parent) {
+        private void init(MediaFormat info, CodecCapabilities parent) {
             // no support for complexity or quality yet
             mParent = parent;
             mComplexityRange = Range.create(0, 0);
@@ -2789,7 +2796,7 @@
         }
 
         /** @hide */
-        public void setDefaultFormat(MediaFormat format) {
+        public void getDefaultFormat(MediaFormat format) {
             // don't list trivial quality/complexity as default for now
             if (!mQualityRange.getUpper().equals(mQualityRange.getLower())
                     && mDefaultQuality != null) {
@@ -3002,6 +3009,7 @@
         // from OMX_VIDEO_HEVCPROFILETYPE
         public static final int HEVCProfileMain        = 0x01;
         public static final int HEVCProfileMain10      = 0x02;
+        public static final int HEVCProfileMainStill   = 0x04;
         public static final int HEVCProfileMain10HDR10 = 0x1000;
 
         // from OMX_VIDEO_HEVCLEVELTYPE
@@ -3078,6 +3086,23 @@
          * {@link VideoCapabilities} to determine the codec capabilities.
          */
         public int level;
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (obj instanceof CodecProfileLevel) {
+                CodecProfileLevel other = (CodecProfileLevel)obj;
+                return other.profile == profile && other.level == level;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Long.hashCode(((long)profile << Integer.SIZE) | level);
+        }
     };
 
     /**
diff --git a/android/media/MediaController2.java b/android/media/MediaController2.java
index d669bc1..591f33f 100644
--- a/android/media/MediaController2.java
+++ b/android/media/MediaController2.java
@@ -16,18 +16,22 @@
 
 package android.media;
 
+import static android.media.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
+
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.media.MediaSession2.Command;
+import android.media.MediaPlaylistAgent.RepeatMode;
+import android.media.MediaPlaylistAgent.ShuffleMode;
 import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.PlaylistParam;
+import android.media.MediaSession2.ErrorCode;
 import android.media.session.MediaSessionManager;
 import android.media.update.ApiLoader;
 import android.media.update.MediaController2Provider;
+import android.media.update.MediaController2Provider.PlaybackInfoProvider;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -36,6 +40,7 @@
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * Allows an app to interact with an active {@link MediaSession2} or a
  * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
  * the session.
@@ -48,9 +53,9 @@
  * <p>
  * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
  * available only if the session service allows this controller by
- * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait
- * {@link ControllerCallback#onConnected(CommandGroup)} or
- * {@link ControllerCallback#onDisconnected()} for the result.
+ * {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)} for the service.
+ * Wait {@link ControllerCallback#onConnected(MediaController2, SessionCommandGroup2)} or
+ * {@link ControllerCallback#onDisconnected(MediaController2)} for the result.
  * <p>
  * A controller can be created through token from {@link MediaSessionManager} if you hold the
  * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
@@ -61,10 +66,7 @@
  * <p>
  * @see MediaSession2
  * @see MediaSessionService2
- * @hide
  */
-// TODO(jaewan): Unhide
-// TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing.
 public class MediaController2 implements AutoCloseable {
     /**
      * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
@@ -75,9 +77,11 @@
          * Called when the controller is successfully connected to the session. The controller
          * becomes available afterwards.
          *
+         * @param controller the controller for this event
          * @param allowedCommands commands that's allowed by the session.
          */
-        public void onConnected(CommandGroup allowedCommands) { }
+        public void onConnected(@NonNull MediaController2 controller,
+                @NonNull SessionCommandGroup2 allowedCommands) { }
 
         /**
          * Called when the session refuses the controller or the controller is disconnected from
@@ -86,58 +90,159 @@
          * <p>
          * It will be also called after the {@link #close()}, so you can put clean up code here.
          * You don't need to call {@link #close()} after this.
+         *
+         * @param controller the controller for this event
+         * @param controller controller for this event
          */
-        public void onDisconnected() { }
+        public void onDisconnected(@NonNull MediaController2 controller) { }
 
         /**
          * Called when the session set the custom layout through the
          * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
          * <p>
-         * Can be called before {@link #onConnected(CommandGroup)} is called.
+         * Can be called before {@link #onConnected(MediaController2, SessionCommandGroup2)} is
+         * called.
          *
+         * @param controller the controller for this event
          * @param layout
          */
-        public void onCustomLayoutChanged(List<CommandButton> layout) { }
+        public void onCustomLayoutChanged(@NonNull MediaController2 controller,
+                @NonNull List<CommandButton> layout) { }
 
         /**
          * Called when the session has changed anything related with the {@link PlaybackInfo}.
          *
+         * @param controller the controller for this event
          * @param info new playback info
          */
-        public void onAudioInfoChanged(PlaybackInfo info) { }
+        public void onPlaybackInfoChanged(@NonNull MediaController2 controller,
+                @NonNull PlaybackInfo info) { }
 
         /**
          * Called when the allowed commands are changed by session.
          *
+         * @param controller the controller for this event
          * @param commands newly allowed commands
          */
-        public void onAllowedCommandsChanged(CommandGroup commands) { }
+        public void onAllowedCommandsChanged(@NonNull MediaController2 controller,
+                @NonNull SessionCommandGroup2 commands) { }
 
         /**
          * Called when the session sent a custom command.
          *
+         * @param controller the controller for this event
          * @param command
          * @param args
          * @param receiver
          */
-        public void onCustomCommand(Command command, @Nullable Bundle args,
+        public void onCustomCommand(@NonNull MediaController2 controller,
+                @NonNull SessionCommand2 command, @Nullable Bundle args,
                 @Nullable ResultReceiver receiver) { }
 
         /**
-         * Called when the playlist is changed.
+         * Called when the player state is changed.
          *
-         * @param list
-         * @param param
-         */
-        public void onPlaylistChanged(
-                @NonNull List<MediaItem2> list, @NonNull PlaylistParam param) { }
-
-        /**
-         * Called when the playback state is changed.
-         *
+         * @param controller the controller for this event
          * @param state
          */
-        public void onPlaybackStateChanged(@NonNull PlaybackState2 state) { }
+        public void onPlayerStateChanged(@NonNull MediaController2 controller, int state) { }
+
+        /**
+         * Called when playback speed is changed.
+         *
+         * @param controller the controller for this event
+         * @param speed speed
+         */
+        public void onPlaybackSpeedChanged(@NonNull MediaController2 controller,
+                float speed) { }
+
+        /**
+         * Called to report buffering events for a data source.
+         * <p>
+         * Use {@link #getBufferedPosition()} for current buffering position.
+         *
+         * @param controller the controller for this event
+         * @param item the media item for which buffering is happening.
+         * @param state the new buffering state.
+         */
+        public void onBufferingStateChanged(@NonNull MediaController2 controller,
+                @NonNull MediaItem2 item, @MediaPlayerBase.BuffState int state) { }
+
+        /**
+         * Called to indicate that seeking is completed.
+         *
+         * @param controller the controller for this event.
+         * @param position the previous seeking request.
+         */
+        public void onSeekCompleted(@NonNull MediaController2 controller, long position) { }
+
+        /**
+         * Called when a error from
+         *
+         * @param controller the controller for this event
+         * @param errorCode error code
+         * @param extras extra information
+         */
+        public void onError(@NonNull MediaController2 controller, @ErrorCode int errorCode,
+                @Nullable Bundle extras) { }
+
+        /**
+         * Called when the player's currently playing item is changed
+         * <p>
+         * When it's called, you should invalidate previous playback information and wait for later
+         * callbacks.
+         *
+         * @param controller the controller for this event
+         * @param item new item
+         * @see #onBufferingStateChanged(MediaController2, MediaItem2, int)
+         */
+        // TODO(jaewan): Use this (b/74316764)
+        public void onCurrentMediaItemChanged(@NonNull MediaController2 controller,
+                @NonNull MediaItem2 item) { }
+
+        /**
+         * Called when a playlist is changed.
+         *
+         * @param controller the controller for this event
+         * @param list new playlist
+         * @param metadata new metadata
+         */
+        public void onPlaylistChanged(@NonNull MediaController2 controller,
+                @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when a playlist metadata is changed.
+         *
+         * @param controller the controller for this event
+         * @param metadata new metadata
+         */
+        public void onPlaylistMetadataChanged(@NonNull MediaController2 controller,
+                @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when the shuffle mode is changed.
+         *
+         * @param controller the controller for this event
+         * @param shuffleMode repeat mode
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+         */
+        public void onShuffleModeChanged(@NonNull MediaController2 controller,
+                @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
+
+        /**
+         * Called when the repeat mode is changed.
+         *
+         * @param controller the controller for this event
+         * @param repeatMode repeat mode
+         * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+         * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+         */
+        public void onRepeatModeChanged(@NonNull MediaController2 controller,
+                @MediaPlaylistAgent.RepeatMode int repeatMode) { }
     }
 
     /**
@@ -155,21 +260,20 @@
          */
         public static final int PLAYBACK_TYPE_LOCAL = 1;
 
-        private final int mVolumeType;
-        private final int mVolumeControl;
-        private final int mMaxVolume;
-        private final int mCurrentVolume;
-        private final AudioAttributes mAudioAttrs;
+        private final PlaybackInfoProvider mProvider;
 
         /**
          * @hide
          */
-        public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
-            mVolumeType = type;
-            mAudioAttrs = attrs;
-            mVolumeControl = control;
-            mMaxVolume = max;
-            mCurrentVolume = current;
+        public PlaybackInfo(PlaybackInfoProvider provider) {
+            mProvider = provider;
+        }
+
+        /**
+         * @hide
+         */
+        public PlaybackInfoProvider getProvider() {
+            return mProvider;
         }
 
         /**
@@ -182,7 +286,7 @@
          * @return The type of playback this session is using.
          */
         public int getPlaybackType() {
-            return mVolumeType;
+            return mProvider.getPlaybackType_impl();
         }
 
         /**
@@ -194,22 +298,21 @@
          * @return The attributes for this session.
          */
         public AudioAttributes getAudioAttributes() {
-            return mAudioAttrs;
+            return mProvider.getAudioAttributes_impl();
         }
 
         /**
          * Get the type of volume control that can be used. One of:
          * <ul>
-         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
-         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
-         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+         * <li>{@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}</li>
+         * <li>{@link VolumeProvider2#VOLUME_CONTROL_RELATIVE}</li>
+         * <li>{@link VolumeProvider2#VOLUME_CONTROL_FIXED}</li>
          * </ul>
          *
-         * @return The type of volume control that may be used with this
-         *         session.
+         * @return The type of volume control that may be used with this session.
          */
-        public int getVolumeControl() {
-            return mVolumeControl;
+        public int getControlType() {
+            return mProvider.getControlType_impl();
         }
 
         /**
@@ -218,7 +321,7 @@
          * @return The maximum allowed volume where this session is playing.
          */
         public int getMaxVolume() {
-            return mMaxVolume;
+            return mProvider.getMaxVolume_impl();
         }
 
         /**
@@ -227,39 +330,39 @@
          * @return The current volume where this session is playing.
          */
         public int getCurrentVolume() {
-            return mCurrentVolume;
+            return mProvider.getCurrentVolume_impl();
         }
     }
 
     private final MediaController2Provider mProvider;
 
     /**
-     * Create a {@link MediaController2} from the {@link SessionToken2}. This connects to the session
-     * and may wake up the service if it's not available.
+     * Create a {@link MediaController2} from the {@link SessionToken2}.
+     * This connects to the session and may wake up the service if it's not available.
      *
      * @param context Context
      * @param token token to connect to
-     * @param callback controller callback to receive changes in
      * @param executor executor to run callbacks on.
+     * @param callback controller callback to receive changes in
      */
-    // TODO(jaewan): Put @CallbackExecutor to the constructor.
     public MediaController2(@NonNull Context context, @NonNull SessionToken2 token,
-            @NonNull ControllerCallback callback, @NonNull Executor executor) {
+            @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) {
         super();
 
+        mProvider = createProvider(context, token, executor, callback);
         // This also connects to the token.
         // Explicit connect() isn't added on purpose because retrying connect() is impossible with
         // session whose session binder is only valid while it's active.
         // prevent a controller from reusable after the
         // session is released and recreated.
-        mProvider = createProvider(context, token, callback, executor);
+        mProvider.initialize();
     }
 
     MediaController2Provider createProvider(@NonNull Context context,
-            @NonNull SessionToken2 token, @NonNull ControllerCallback callback,
-            @NonNull Executor executor) {
-        return ApiLoader.getProvider(context)
-                .createMediaController2(this, context, token, callback, executor);
+            @NonNull SessionToken2 token, @NonNull Executor executor,
+            @NonNull ControllerCallback callback) {
+        return ApiLoader.getProvider().createMediaController2(
+                context, this, token, executor, callback);
     }
 
     /**
@@ -281,8 +384,7 @@
     /**
      * @return token
      */
-    public @NonNull
-    SessionToken2 getSessionToken() {
+    public @NonNull SessionToken2 getSessionToken() {
         return mProvider.getSessionToken_impl();
     }
 
@@ -305,36 +407,26 @@
         mProvider.stop_impl();
     }
 
-    public void skipToPrevious() {
-        mProvider.skipToPrevious_impl();
-    }
-
-    public void skipToNext() {
-        mProvider.skipToNext_impl();
-    }
-
     /**
      * Request that the player prepare its playback. In other words, other sessions 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 PlaybackState2#STATE_PAUSED}. Afterwards, {@link #play} can be called to
-     * start playback.
+     * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
+     * to start playback.
      */
     public void prepare() {
         mProvider.prepare_impl();
     }
 
     /**
-     * Start fast forwarding. If playback is already fast forwarding this
-     * may increase the rate.
+     * Fast forwards playback. If playback is already fast forwarding this may increase the rate.
      */
     public void fastForward() {
         mProvider.fastForward_impl();
     }
 
     /**
-     * Start rewinding. If playback is already rewinding this may increase
-     * the rate.
+     * Rewinds playback. If playback is already rewinding this may increase the rate.
      */
     public void rewind() {
         mProvider.rewind_impl();
@@ -350,20 +442,11 @@
     }
 
     /**
-     * Sets the index of current DataSourceDesc in the play list to be played.
-     *
-     * @param index the index of DataSourceDesc in the play list you want to play
-     * @throws IllegalArgumentException if the play list is null
-     * @throws NullPointerException if index is outside play list range
-     */
-    public void setCurrentPlaylistItem(int index) {
-        mProvider.setCurrentPlaylistItem_impl(index);
-    }
-
-    /**
+     * Revisit this API later.
      * @hide
      */
     public void skipForward() {
+        // TODO(jaewan): (Post-P) Discuss this API later.
         // To match with KEYCODE_MEDIA_SKIP_FORWARD
     }
 
@@ -371,6 +454,7 @@
      * @hide
      */
     public void skipBackward() {
+        // TODO(jaewan): (Post-P) Discuss this API later.
         // To match with KEYCODE_MEDIA_SKIP_BACKWARD
     }
 
@@ -387,12 +471,9 @@
 
     /**
      * Request that the player start playback for a specific search query.
-     * An empty or null query should be treated as a request to play any
-     * music.
      *
-     * @param query The search query.
-     * @param extras Optional extras that can include extra information
-     *               about the query.
+     * @param query The search query. Should not be an empty string.
+     * @param extras Optional extras that can include extra information about the query.
      */
     public void playFromSearch(@NonNull String query, @Nullable Bundle extras) {
         mProvider.playFromSearch_impl(query, extras);
@@ -405,16 +486,15 @@
      * @param extras Optional extras that can include extra information about the media item
      *               to be played.
      */
-    public void playFromUri(@NonNull String uri, @Nullable Bundle extras) {
+    public void playFromUri(@NonNull Uri uri, @Nullable Bundle extras) {
         mProvider.playFromUri_impl(uri, extras);
     }
 
-
     /**
      * Request that the player prepare playback for a specific media id. In other words, other
      * sessions 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 PlaybackState2#STATE_PAUSED}. Afterwards,
+     * will change its playback state to {@link MediaPlayerBase#PLAYER_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.
      *
@@ -423,21 +503,20 @@
      *               to be prepared.
      */
     public void prepareFromMediaId(@NonNull String mediaId, @Nullable Bundle extras) {
-        mProvider.prepareMediaId_impl(mediaId, extras);
+        mProvider.prepareFromMediaId_impl(mediaId, 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 sessions
-     * 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 PlaybackState2#STATE_PAUSED}. Afterwards,
+     * Request that the player prepare playback for a specific search query.
+     * In other words, other sessions 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 MediaPlayerBase#PLAYER_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.
      *
-     * @param query The search query.
-     * @param extras Optional extras that can include extra information
-     *               about the query.
+     * @param query The search query. Should not be an empty string.
+     * @param extras Optional extras that can include extra information about the query.
      */
     public void prepareFromSearch(@NonNull String query, @Nullable Bundle extras) {
         mProvider.prepareFromSearch_impl(query, extras);
@@ -447,8 +526,8 @@
      * Request that the player prepare playback for a specific {@link Uri}. In other words,
      * other sessions 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 PlaybackState2#STATE_PAUSED}. Afterwards,
-     * {@link #play} can be called to start playback. If the preparation is not needed,
+     * session will change its playback state to {@link MediaPlayerBase#PLAYER_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.
      *
      * @param uri The URI of the requested media.
@@ -461,7 +540,7 @@
 
     /**
      * Set the volume of the output this session is playing on. The command will be ignored if it
-     * does not support {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+     * does not support {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
      * <p>
      * If the session is local playback, this changes the device's volume with the stream that
      * session's player is using. Flags will be specified for the {@link AudioManager}.
@@ -483,8 +562,8 @@
      * 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
-     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
-     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}.
+     * {@link VolumeProvider2#VOLUME_CONTROL_RELATIVE} or
+     * {@link VolumeProvider2#VOLUME_CONTROL_ABSOLUTE}.
      * <p>
      * If the session is local playback, this changes the device's volume with the stream that
      * session's player is using. Flags will be specified for the {@link AudioManager}.
@@ -502,24 +581,6 @@
     }
 
     /**
-     * Get the rating type supported by the session. One of:
-     * <ul>
-     * <li>{@link Rating2#RATING_NONE}</li>
-     * <li>{@link Rating2#RATING_HEART}</li>
-     * <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
-     * <li>{@link Rating2#RATING_3_STARS}</li>
-     * <li>{@link Rating2#RATING_4_STARS}</li>
-     * <li>{@link Rating2#RATING_5_STARS}</li>
-     * <li>{@link Rating2#RATING_PERCENTAGE}</li>
-     * </ul>
-     *
-     * @return The supported rating type
-     */
-    public int getRatingType() {
-        return mProvider.getRatingType_impl();
-    }
-
-    /**
      * Get an intent for launching UI associated with this session if one exists.
      *
      * @return A {@link PendingIntent} to launch UI or null.
@@ -529,12 +590,65 @@
     }
 
     /**
-     * Get the latest {@link PlaybackState2} from the session.
+     * Get the lastly cached player state from
+     * {@link ControllerCallback#onPlayerStateChanged(MediaController2, int)}.
      *
-     * @return a playback state
+     * @return player state
      */
-    public PlaybackState2 getPlaybackState() {
-        return mProvider.getPlaybackState_impl();
+    public int getPlayerState() {
+        return mProvider.getPlayerState_impl();
+    }
+
+    /**
+     * Gets the current playback position.
+     * <p>
+     * This returns the calculated value of the position, based on the difference between the
+     * update time and current time.
+     *
+     * @return position
+     */
+    public long getCurrentPosition() {
+        return mProvider.getCurrentPosition_impl();
+    }
+
+    /**
+     * Get the lastly cached playback speed from
+     * {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}.
+     *
+     * @return speed
+     */
+    public float getPlaybackSpeed() {
+        return mProvider.getPlaybackSpeed_impl();
+    }
+
+    /**
+     * Set the playback speed.
+     */
+    public void setPlaybackSpeed(float speed) {
+        // TODO(jaewan): implement this (b/74093080)
+    }
+
+
+    /**
+     * Gets the current buffering state of the player.
+     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+     * buffered.
+     * @return the buffering state.
+     */
+    public @MediaPlayerBase.BuffState int getBufferingState() {
+        // TODO(jaewan): Implement.
+        return BUFFERING_STATE_UNKNOWN;
+    }
+
+    /**
+     * Gets the lastly cached buffered position from the session when
+     * {@link ControllerCallback#onBufferingStateChanged(MediaController2, MediaItem2, int)} is
+     * called.
+     *
+     * @return buffering position in millis
+     */
+    public long getBufferedPosition() {
+        return mProvider.getBufferedPosition_impl();
     }
 
     /**
@@ -547,14 +661,19 @@
     }
 
     /**
-     * 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()}.
+     * Rate the media. This will cause the rating to be set for the current user.
+     * The rating style must follow the user rating style from the session.
+     * You can get the rating style from the session through the
+     * {@link MediaMetadata#getRating(String)} with the key
+     * {@link MediaMetadata#METADATA_KEY_USER_RATING}.
+     * <p>
+     * If the user rating was {@code null}, the media item does not accept setting user rating.
      *
-     * @param rating The rating to set for the current content
+     * @param mediaId The id of the media
+     * @param rating The rating to set
      */
-    public void setRating(Rating2 rating) {
-        mProvider.setRating_impl(rating);
+    public void setRating(@NonNull String mediaId, @NonNull Rating2 rating) {
+        mProvider.setRating_impl(mediaId, rating);
     }
 
     /**
@@ -564,53 +683,189 @@
      * @param args optional argument
      * @param cb optional result receiver
      */
-    public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args,
+    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args,
             @Nullable ResultReceiver cb) {
         mProvider.sendCustomCommand_impl(command, args, cb);
     }
 
     /**
-     * Return playlist from the session.
+     * Returns the cached playlist from
+     * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
+     * <p>
+     * This list may differ with the list that was specified with
+     * {@link #setPlaylist(List, MediaMetadata2)} depending on the session implementation. Use media
+     * items returned here for other playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
      *
-     * @return playlist. Can be {@code null} if the controller doesn't have enough permission.
+     * @return The playlist. Can be {@code null} if the controller doesn't have enough permission or
+     *         the session hasn't set any playlist.
      */
     public @Nullable List<MediaItem2> getPlaylist() {
         return mProvider.getPlaylist_impl();
     }
 
-    public @Nullable PlaylistParam getPlaylistParam() {
-        return mProvider.getPlaylistParam_impl();
+    /**
+     * Sets the playlist.
+     * <p>
+     * Even when the playlist is successfully set, use the playlist returned from
+     * {@link #getPlaylist()} for playlist APIs such as {@link #skipToPlaylistItem(MediaItem2)}.
+     * Otherwise the session in the remote process can't distinguish between media items.
+     *
+     * @param list playlist
+     * @param metadata metadata of the playlist
+     * @see #getPlaylist()
+     * @see ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)
+     */
+    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
+        mProvider.setPlaylist_impl(list, metadata);
     }
 
     /**
-     * Removes the media item at index in the play list.
+     * Updates the playlist metadata
+     *
+     * @param metadata metadata of the playlist
+     */
+    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
+        mProvider.updatePlaylistMetadata_impl(metadata);
+    }
+
+    /**
+     * Gets the lastly cached playlist playlist metadata either from
+     * {@link ControllerCallback#onPlaylistMetadataChanged(MediaController2,  MediaMetadata2)} or
+     * {@link ControllerCallback#onPlaylistChanged(MediaController2, List, MediaMetadata2)}.
+     *
+     * @return metadata metadata of the playlist, or null if none is set
+     */
+    public @Nullable MediaMetadata2 getPlaylistMetadata() {
+        return mProvider.getPlaylistMetadata_impl();
+    }
+
+
+    /**
+     * Adds the media item to the playlist at position index. Index equals or greater than
+     * the current playlist size will add the item at the end of the playlist.
+     * <p>
+     * This will not change the currently playing media item.
+     * If index is less than or equal to the current index of the playlist,
+     * the current index of the playlist will be incremented correspondingly.
+     *
+     * @param index the index you want to add
+     * @param item the media item you want to add
+     */
+    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
+        mProvider.addPlaylistItem_impl(index, item);
+    }
+
+    /**
+     * Removes the media item at index in the playlist.
      *<p>
-     * If index is same as the current index of the playlist, current playback
+     * If the item is the currently playing item of the playlist, current playback
      * will be stopped and playback moves to next source in the list.
      *
-     * @return the removed DataSourceDesc at index in the play list
-     * @throws IllegalArgumentException if the play list is null
-     * @throws IndexOutOfBoundsException if index is outside play list range
+     * @param item the media item you want to add
      */
-    // TODO(jaewan): Remove with index was previously rejected by council (b/36524925)
-    // TODO(jaewan): Should we also add movePlaylistItem from index to index?
-    public void removePlaylistItem(MediaItem2 item) {
+    public void removePlaylistItem(@NonNull MediaItem2 item) {
         mProvider.removePlaylistItem_impl(item);
     }
 
     /**
-     * Inserts the media item to the play list at position index.
-     * <p>
-     * This will not change the currently playing media item.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
+     * Replace the media item at index in the playlist. This can be also used to update metadata of
+     * an item.
      *
-     * @param index the index you want to add dsd to the play list
-     * @param item the media item you want to add to the play list
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     * @throws NullPointerException if dsd is null
+     * @param index the index of the item to replace
+     * @param item the new item
      */
-    public void addPlaylistItem(int index, MediaItem2 item) {
-        mProvider.addPlaylistItem_impl(index, item);
+    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+        mProvider.replacePlaylistItem_impl(index, item);
+    }
+
+    /**
+     * Get the lastly cached current item from
+     * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController2, MediaItem2)}.
+     *
+     * @return index of the current item
+     */
+    public MediaItem2 getCurrentMediaItem() {
+        return mProvider.getCurrentMediaItem_impl();
+    }
+
+    /**
+     * Skips to the previous item in the playlist.
+     * <p>
+     * This calls {@link MediaSession2#skipToPreviousItem()} if the session allows.
+     */
+     public void skipToPreviousItem() {
+         mProvider.skipToPreviousItem_impl();
+     }
+
+    /**
+     * Skips to the next item in the playlist.
+     * <p>
+     * This calls {@link MediaSession2#skipToNextItem()} if the session allows.
+     */
+    public void skipToNextItem() {
+        mProvider.skipToNextItem_impl();
+    }
+
+    /**
+     * Skips to the item in the playlist.
+     * <p>
+     * This calls {@link MediaSession2#skipToPlaylistItem(MediaItem2)} if the session allows.
+     *
+     * @param item The item in the playlist you want to play
+     */
+    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.skipToPlaylistItem_impl(item);
+    }
+
+    /**
+     * Gets the cached repeat mode from the {@link ControllerCallback#onRepeatModeChanged(
+     * MediaController2, int)}.
+     *
+     * @return repeat mode
+     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+     */
+    public @RepeatMode int getRepeatMode() {
+        return mProvider.getRepeatMode_impl();
+    }
+
+    /**
+     * Sets the repeat mode.
+     *
+     * @param repeatMode repeat mode
+     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+     */
+    public void setRepeatMode(@RepeatMode int repeatMode) {
+        mProvider.setRepeatMode_impl(repeatMode);
+    }
+
+    /**
+     * Gets the cached shuffle mode from the {@link ControllerCallback#onShuffleModeChanged(
+     * MediaController2, int)}.
+     *
+     * @return The shuffle mode
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+     */
+    public @ShuffleMode int getShuffleMode() {
+        return mProvider.getShuffleMode_impl();
+    }
+
+    /**
+     * Sets the shuffle mode.
+     *
+     * @param shuffleMode The shuffle mode
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+     */
+    public void setShuffleMode(@ShuffleMode int shuffleMode) {
+        mProvider.setShuffleMode_impl(shuffleMode);
     }
 }
diff --git a/android/media/MediaController2Test.java b/android/media/MediaController2Test.java
deleted file mode 100644
index ae67a95..0000000
--- a/android/media/MediaController2Test.java
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.media.TestUtils.SyncHandler;
-import android.media.session.PlaybackState;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static android.media.TestUtils.createPlaybackState;
-import static org.junit.Assert.*;
-
-/**
- * Tests {@link MediaController2}.
- */
-// TODO(jaewan): Implement host-side test so controller and session can run in different processes.
-// TODO(jaewan): Fix flaky failure -- see MediaController2Impl.getController()
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@FlakyTest
-public class MediaController2Test extends MediaSession2TestBase {
-    private static final String TAG = "MediaController2Test";
-
-    MediaSession2 mSession;
-    MediaController2 mController;
-    MockPlayer mPlayer;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        // Create this test specific MediaSession2 to use our own Handler.
-        sHandler.postAndSync(()->{
-            mPlayer = new MockPlayer(1);
-            mSession = new MediaSession2.Builder(mContext, mPlayer).setId(TAG).build();
-        });
-
-        mController = createController(mSession.getToken());
-        TestServiceRegistry.getInstance().setHandler(sHandler);
-    }
-
-    @After
-    @Override
-    public void cleanUp() throws Exception {
-        super.cleanUp();
-        sHandler.postAndSync(() -> {
-            if (mSession != null) {
-                mSession.close();
-            }
-        });
-        TestServiceRegistry.getInstance().cleanUp();
-    }
-
-    @Test
-    public void testPlay() throws InterruptedException {
-        mController.play();
-        try {
-            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        } catch (InterruptedException e) {
-            fail(e.getMessage());
-        }
-        assertTrue(mPlayer.mPlayCalled);
-    }
-
-    @Test
-    public void testPause() throws InterruptedException {
-        mController.pause();
-        try {
-            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        } catch (InterruptedException e) {
-            fail(e.getMessage());
-        }
-        assertTrue(mPlayer.mPauseCalled);
-    }
-
-
-    @Test
-    public void testSkipToPrevious() throws InterruptedException {
-        mController.skipToPrevious();
-        try {
-            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        } catch (InterruptedException e) {
-            fail(e.getMessage());
-        }
-        assertTrue(mPlayer.mSkipToPreviousCalled);
-    }
-
-    @Test
-    public void testSkipToNext() throws InterruptedException {
-        mController.skipToNext();
-        try {
-            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        } catch (InterruptedException e) {
-            fail(e.getMessage());
-        }
-        assertTrue(mPlayer.mSkipToNextCalled);
-    }
-
-    @Test
-    public void testStop() throws InterruptedException {
-        mController.stop();
-        try {
-            assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        } catch (InterruptedException e) {
-            fail(e.getMessage());
-        }
-        assertTrue(mPlayer.mStopCalled);
-    }
-
-    @Test
-    public void testGetPackageName() {
-        assertEquals(mContext.getPackageName(), mController.getSessionToken().getPackageName());
-    }
-
-    @Test
-    public void testGetPlaybackState() throws InterruptedException {
-        // TODO(jaewan): add equivalent test later
-        /*
-        final CountDownLatch latch = new CountDownLatch(1);
-        final MediaPlayerBase.PlaybackListener listener = (state) -> {
-            assertEquals(PlaybackState.STATE_BUFFERING, state.getState());
-            latch.countDown();
-        };
-        assertNull(mController.getPlaybackState());
-        mController.addPlaybackListener(listener, sHandler);
-
-        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_BUFFERING));
-        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertEquals(PlaybackState.STATE_BUFFERING, mController.getPlaybackState().getState());
-        */
-    }
-
-    // TODO(jaewan): add equivalent test later
-    /*
-    @Test
-    public void testAddPlaybackListener() throws InterruptedException {
-        final CountDownLatch latch = new CountDownLatch(2);
-        final MediaPlayerBase.PlaybackListener listener = (state) -> {
-            switch ((int) latch.getCount()) {
-                case 2:
-                    assertEquals(PlaybackState.STATE_PLAYING, state.getState());
-                    break;
-                case 1:
-                    assertEquals(PlaybackState.STATE_PAUSED, state.getState());
-                    break;
-            }
-            latch.countDown();
-        };
-
-        mController.addPlaybackListener(listener, sHandler);
-        sHandler.postAndSync(()->{
-            mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
-            mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
-        });
-        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testRemovePlaybackListener() throws InterruptedException {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final MediaPlayerBase.PlaybackListener listener = (state) -> {
-            fail();
-            latch.countDown();
-        };
-        mController.addPlaybackListener(listener, sHandler);
-        mController.removePlaybackListener(listener);
-        mPlayer.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
-        assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-    }
-    */
-
-    @Test
-    public void testControllerCallback_onConnected() throws InterruptedException {
-        // createController() uses controller callback to wait until the controller becomes
-        // available.
-        MediaController2 controller = createController(mSession.getToken());
-        assertNotNull(controller);
-    }
-
-    @Test
-    public void testControllerCallback_sessionRejects() throws InterruptedException {
-        final MediaSession2.SessionCallback sessionCallback = new SessionCallback() {
-            @Override
-            public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
-                return null;
-            }
-        };
-        sHandler.postAndSync(() -> {
-            mSession.close();
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
-                    .setSessionCallback(sHandlerExecutor, sessionCallback).build();
-        });
-        MediaController2 controller =
-                createController(mSession.getToken(), false, null);
-        assertNotNull(controller);
-        waitForConnect(controller, false);
-        waitForDisconnect(controller, true);
-    }
-
-    @Test
-    public void testControllerCallback_releaseSession() throws InterruptedException {
-        sHandler.postAndSync(() -> {
-            mSession.close();
-        });
-        waitForDisconnect(mController, true);
-    }
-
-    @Test
-    public void testControllerCallback_release() throws InterruptedException {
-        mController.close();
-        waitForDisconnect(mController, true);
-    }
-
-    @Test
-    public void testIsConnected() throws InterruptedException {
-        assertTrue(mController.isConnected());
-        sHandler.postAndSync(()->{
-            mSession.close();
-        });
-        // postAndSync() to wait until the disconnection is propagated.
-        sHandler.postAndSync(()->{
-            assertFalse(mController.isConnected());
-        });
-    }
-
-    /**
-     * Test potential deadlock for calls between controller and session.
-     */
-    @Test
-    public void testDeadlock() throws InterruptedException {
-        sHandler.postAndSync(() -> {
-            mSession.close();
-            mSession = null;
-        });
-
-        // Two more threads are needed not to block test thread nor test wide thread (sHandler).
-        final HandlerThread sessionThread = new HandlerThread("testDeadlock_session");
-        final HandlerThread testThread = new HandlerThread("testDeadlock_test");
-        sessionThread.start();
-        testThread.start();
-        final SyncHandler sessionHandler = new SyncHandler(sessionThread.getLooper());
-        final Handler testHandler = new Handler(testThread.getLooper());
-        final CountDownLatch latch = new CountDownLatch(1);
-        try {
-            final MockPlayer player = new MockPlayer(0);
-            sessionHandler.postAndSync(() -> {
-                mSession = new MediaSession2.Builder(mContext, mPlayer)
-                        .setId("testDeadlock").build();
-            });
-            final MediaController2 controller = createController(mSession.getToken());
-            testHandler.post(() -> {
-                final PlaybackState2 state = createPlaybackState(PlaybackState.STATE_ERROR);
-                for (int i = 0; i < 100; i++) {
-                    // triggers call from session to controller.
-                    player.notifyPlaybackState(state);
-                    // triggers call from controller to session.
-                    controller.play();
-
-                    // Repeat above
-                    player.notifyPlaybackState(state);
-                    controller.pause();
-                    player.notifyPlaybackState(state);
-                    controller.stop();
-                    player.notifyPlaybackState(state);
-                    controller.skipToNext();
-                    player.notifyPlaybackState(state);
-                    controller.skipToPrevious();
-                }
-                // This may hang if deadlock happens.
-                latch.countDown();
-            });
-            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        } finally {
-            if (mSession != null) {
-                sessionHandler.postAndSync(() -> {
-                    // Clean up here because sessionHandler will be removed afterwards.
-                    mSession.close();
-                    mSession = null;
-                });
-            }
-            if (sessionThread != null) {
-                sessionThread.quitSafely();
-            }
-            if (testThread != null) {
-                testThread.quitSafely();
-            }
-        }
-    }
-
-    @Ignore
-    @Test
-    public void testGetServiceToken() {
-        SessionToken2 token = TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID);
-        assertNotNull(token);
-        assertEquals(mContext.getPackageName(), token.getPackageName());
-        assertEquals(MockMediaSessionService2.ID, token.getId());
-        assertNull(token.getSessionBinder());
-        assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
-    }
-
-    private void connectToService(SessionToken2 token) throws InterruptedException {
-        mController = createController(token);
-        mSession = TestServiceRegistry.getInstance().getServiceInstance().getSession();
-        mPlayer = (MockPlayer) mSession.getPlayer();
-    }
-
-    // TODO(jaewan): Reenable when session manager detects app installs
-    @Ignore
-    @Test
-    public void testConnectToService_sessionService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testConnectToService();
-    }
-
-    // TODO(jaewan): Reenable when session manager detects app installs
-    @Ignore
-    @Test
-    public void testConnectToService_libraryService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaLibraryService2.ID));
-        testConnectToService();
-    }
-
-    public void testConnectToService() throws InterruptedException {
-        TestServiceRegistry serviceInfo = TestServiceRegistry.getInstance();
-        ControllerInfo info = serviceInfo.getOnConnectControllerInfo();
-        assertEquals(mContext.getPackageName(), info.getPackageName());
-        assertEquals(Process.myUid(), info.getUid());
-        assertFalse(info.isTrusted());
-
-        // Test command from controller to session service
-        mController.play();
-        assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        assertTrue(mPlayer.mPlayCalled);
-
-        // Test command from session service to controller
-        // TODO(jaewan): Add equivalent tests again
-        /*
-        final CountDownLatch latch = new CountDownLatch(1);
-        mController.addPlaybackListener((state) -> {
-            assertNotNull(state);
-            assertEquals(PlaybackState.STATE_REWINDING, state.getState());
-            latch.countDown();
-        }, sHandler);
-        mPlayer.notifyPlaybackState(
-                TestUtils.createPlaybackState(PlaybackState.STATE_REWINDING));
-        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        */
-    }
-
-    @Test
-    public void testControllerAfterSessionIsGone_session() throws InterruptedException {
-        testControllerAfterSessionIsGone(mSession.getToken().getId());
-    }
-
-    @Ignore
-    @Test
-    public void testControllerAfterSessionIsGone_sessionService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testControllerAfterSessionIsGone(MockMediaSessionService2.ID);
-    }
-
-    @Test
-    public void testClose_beforeConnected() throws InterruptedException {
-        MediaController2 controller =
-                createController(mSession.getToken(), false, null);
-        controller.close();
-    }
-
-    @Test
-    public void testClose_twice() throws InterruptedException {
-        mController.close();
-        mController.close();
-    }
-
-    @Test
-    public void testClose_session() throws InterruptedException {
-        final String id = mSession.getToken().getId();
-        mController.close();
-        // close is done immediately for session.
-        testNoInteraction();
-
-        // Test whether the controller is notified about later close of the session or
-        // re-creation.
-        testControllerAfterSessionIsGone(id);
-    }
-
-    // TODO(jaewan): Reenable when session manager detects app installs
-    @Ignore
-    @Test
-    public void testClose_sessionService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testCloseFromService();
-    }
-
-    // TODO(jaewan): Reenable when session manager detects app installs
-    @Ignore
-    @Test
-    public void testClose_libraryService() throws InterruptedException {
-        connectToService(TestUtils.getServiceToken(mContext, MockMediaSessionService2.ID));
-        testCloseFromService();
-    }
-
-    private void testCloseFromService() throws InterruptedException {
-        final String id = mController.getSessionToken().getId();
-        final CountDownLatch latch = new CountDownLatch(1);
-        TestServiceRegistry.getInstance().setServiceInstanceChangedCallback((service) -> {
-            if (service == null) {
-                // Destroying..
-                latch.countDown();
-            }
-        });
-        mController.close();
-        // Wait until close triggers onDestroy() of the session service.
-        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertNull(TestServiceRegistry.getInstance().getServiceInstance());
-        testNoInteraction();
-
-        // Test whether the controller is notified about later close of the session or
-        // re-creation.
-        testControllerAfterSessionIsGone(id);
-    }
-
-    private void testControllerAfterSessionIsGone(final String id) throws InterruptedException {
-        sHandler.postAndSync(() -> {
-            // TODO(jaewan): Use Session.close later when we add the API.
-            mSession.close();
-        });
-        waitForDisconnect(mController, true);
-        testNoInteraction();
-
-        // Test with the newly created session.
-        sHandler.postAndSync(() -> {
-            // Recreated session has different session stub, so previously created controller
-            // shouldn't be available.
-            mSession = new MediaSession2.Builder(mContext, mPlayer).setId(id).build();
-        });
-        testNoInteraction();
-    }
-
-    private void testNoInteraction() throws InterruptedException {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final PlaybackListener playbackListener = (state) -> {
-            fail("Controller shouldn't be notified about change in session after the close.");
-            latch.countDown();
-        };
-        // TODO(jaewan): Add equivalent tests again
-        /*
-        mController.addPlaybackListener(playbackListener, sHandler);
-        mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
-        assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        mController.removePlaybackListener(playbackListener);
-        */
-    }
-
-    // TODO(jaewan): Add  test for service connect rejection, when we differentiate session
-    //               active/inactive and connection accept/refuse
-}
diff --git a/android/media/MediaDataSource.java b/android/media/MediaDataSource.java
index 948da0b..4ba2120 100644
--- a/android/media/MediaDataSource.java
+++ b/android/media/MediaDataSource.java
@@ -34,8 +34,8 @@
     /**
      * Called to request data from the given position.
      *
-     * Implementations should should write up to {@code size} bytes into
-     * {@code buffer}, and return the number of bytes written.
+     * Implementations should fill {@code buffer} with up to {@code size}
+     * bytes of data, and return the number of valid bytes in the buffer.
      *
      * Return {@code 0} if size is zero (thus no bytes are read).
      *
diff --git a/android/media/MediaDescrambler.java b/android/media/MediaDescrambler.java
index 40c837b..99bd254 100644
--- a/android/media/MediaDescrambler.java
+++ b/android/media/MediaDescrambler.java
@@ -125,6 +125,38 @@
     }
 
     /**
+     * Scramble control value indicating that the samples are not scrambled.
+     * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+     */
+    public static final byte SCRAMBLE_CONTROL_UNSCRAMBLED = 0;
+
+    /**
+     * Scramble control value reserved and shouldn't be used currently.
+     * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+     */
+    public static final byte SCRAMBLE_CONTROL_RESERVED    = 1;
+
+    /**
+     * Scramble control value indicating that the even key is used.
+     * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+     */
+    public static final byte SCRAMBLE_CONTROL_EVEN_KEY     = 2;
+
+    /**
+     * Scramble control value indicating that the odd key is used.
+     * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+     */
+    public static final byte SCRAMBLE_CONTROL_ODD_KEY      = 3;
+
+    /**
+     * Scramble flag for a hint indicating that the descrambling request is for
+     * retrieving the PES header info only.
+     *
+     * @see #descramble(ByteBuffer, ByteBuffer, android.media.MediaCodec.CryptoInfo)
+     */
+    public static final byte SCRAMBLE_FLAG_PES_HEADER = (1 << 0);
+
+    /**
      * Descramble a ByteBuffer of data described by a
      * {@link android.media.MediaCodec.CryptoInfo} structure.
      *
@@ -133,7 +165,15 @@
      * @param dstBuf ByteBuffer to hold the descrambled data, which starts at
      * dstBuf.position().
      * @param cryptoInfo a {@link android.media.MediaCodec.CryptoInfo} structure
-     * describing the subsamples contained in src.
+     * describing the subsamples contained in srcBuf. The iv and mode fields in
+     * CryptoInfo are not used. key[0] contains the MPEG2TS scrambling control bits
+     * (as defined in ETSI TS 100 289 (2011): "Digital Video Broadcasting (DVB);
+     * Support for use of the DVB Scrambling Algorithm version 3 within digital
+     * broadcasting systems"), and the value must be one of {@link #SCRAMBLE_CONTROL_UNSCRAMBLED},
+     * {@link #SCRAMBLE_CONTROL_RESERVED}, {@link #SCRAMBLE_CONTROL_EVEN_KEY} or
+     * {@link #SCRAMBLE_CONTROL_ODD_KEY}. key[1] is a set of bit flags, with the
+     * only possible bit being {@link #SCRAMBLE_FLAG_PES_HEADER} currently.
+     * key[2~15] are not used.
      *
      * @return number of bytes that have been successfully descrambled, with negative
      * values indicating errors.
@@ -169,6 +209,7 @@
         try {
             return native_descramble(
                     cryptoInfo.key[0],
+                    cryptoInfo.key[1],
                     cryptoInfo.numSubSamples,
                     cryptoInfo.numBytesOfClearData,
                     cryptoInfo.numBytesOfEncryptedData,
@@ -204,7 +245,8 @@
     private native final void native_setup(@NonNull IHwBinder decramblerBinder);
     private native final void native_release();
     private native final int native_descramble(
-            byte key, int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
+            byte key, byte flags, int numSubSamples,
+            int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
             @NonNull ByteBuffer srcBuf, int srcOffset, int srcLimit,
             ByteBuffer dstBuf, int dstOffset, int dstLimit) throws RemoteException;
 
diff --git a/android/media/MediaDrm.java b/android/media/MediaDrm.java
index 063186d..7ac1529 100644
--- a/android/media/MediaDrm.java
+++ b/android/media/MediaDrm.java
@@ -628,14 +628,48 @@
     }
 
     /**
-     * Open a new session with the MediaDrm object.  A session ID is returned.
+     * Open a new session with the MediaDrm object. A session ID is returned.
+     * By default, sessions are opened at the native security level of the device.
      *
      * @throws NotProvisionedException if provisioning is needed
      * @throws ResourceBusyException if required resources are in use
      */
     @NonNull
-    public native byte[] openSession() throws NotProvisionedException,
-            ResourceBusyException;
+    public byte[] openSession() throws NotProvisionedException,
+            ResourceBusyException {
+        return openSession(getMaxSecurityLevel());
+    }
+
+    /**
+     * Open a new session at a requested security level. The security level
+     * represents the robustness of the device's DRM implementation. By default,
+     * sessions are opened at the native security level of the device.
+     * Overriding the security level is necessary when the decrypted frames need
+     * to be manipulated, such as for image compositing. The security level
+     * parameter must be lower than the native level. Reducing the security
+     * level will typically limit the content to lower resolutions, as
+     * determined by the license policy. If the requested level is not
+     * supported, the next lower supported security level will be set. The level
+     * can be queried using {@link #getSecurityLevel}. A session
+     * ID is returned.
+     *
+     * @param level the new security level, one of
+     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO},
+     * {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
+     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
+     * {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
+     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
+     *
+     * @throws NotProvisionedException if provisioning is needed
+     * @throws ResourceBusyException if required resources are in use
+     * @throws IllegalArgumentException if the requested security level is
+     * higher than the native level or lower than the lowest supported level or
+     * if the device does not support specifying the security level when opening
+     * a session
+     */
+    @NonNull
+    public native byte[] openSession(@SecurityLevel int level) throws
+            NotProvisionedException, ResourceBusyException;
 
     /**
      * Close a session on the MediaDrm object that was previously opened
@@ -671,7 +705,9 @@
     public @interface KeyType {}
 
     /**
-     * Contains the opaque data an app uses to request keys from a license server
+     * Contains the opaque data an app uses to request keys from a license server.
+     * These request types may or may not be generated by a given plugin. Refer
+     * to plugin vendor documentation for more information.
      */
     public static final class KeyRequest {
         private byte[] mData;
@@ -696,8 +732,8 @@
         public static final int REQUEST_TYPE_RELEASE = 2;
 
         /**
-         * Keys are already loaded. No license request is necessary, and no
-         * key request data is returned.
+         * Keys are already loaded and are available for use. No license request is necessary, and
+         * no key request data is returned.
          */
         public static final int REQUEST_TYPE_NONE = 3;
 
@@ -942,43 +978,84 @@
             throws DeniedByServerException;
 
     /**
-     * A means of enforcing limits on the number of concurrent streams per subscriber
-     * across devices is provided via SecureStop. This is achieved by securely
-     * monitoring the lifetime of sessions.
+     * Secure stops are a way to enforce limits on the number of concurrent
+     * streams per subscriber across devices. They provide secure monitoring of
+     * the lifetime of content decryption keys in MediaDrm sessions.
      * <p>
-     * Information from the server related to the current playback session is written
-     * to persistent storage on the device when each MediaCrypto object is created.
+     * A secure stop is written to secure persistent memory when keys are loaded
+     * into a MediaDrm session. The secure stop state indicates that the keys
+     * are available for use. When playback completes and the keys are removed
+     * or the session is destroyed, the secure stop state is updated to indicate
+     * that keys are no longer usable.
      * <p>
-     * In the normal case, playback will be completed, the session destroyed and the
-     * Secure Stops will be queried. The app queries secure stops and forwards the
-     * secure stop message to the server which verifies the signature and notifies the
-     * server side database that the session destruction has been confirmed. The persisted
-     * record on the client is only removed after positive confirmation that the server
-     * received the message using releaseSecureStops().
+     * After playback, the app can query the secure stop and send it in a
+     * message to the license server confirming that the keys are no longer
+     * active. The license server returns a secure stop release response
+     * message to the app which then deletes the secure stop from persistent
+     * memory using {@link #releaseSecureStops}.
+     * <p>
+     * Each secure stop has a unique ID that can be used to identify it during
+     * enumeration, access and removal.
+     * @return a list of all secure stops from secure persistent memory
      */
     @NonNull
     public native List<byte[]> getSecureStops();
 
     /**
-     * Access secure stop by secure stop ID.
+     * Return a list of all secure stop IDs currently in persistent memory.
+     * The secure stop ID can be used to access or remove the corresponding
+     * secure stop.
      *
-     * @param ssid - The secure stop ID provided by the license server.
+     * @return a list of secure stop IDs
+     */
+    @NonNull
+    public native List<byte[]> getSecureStopIds();
+
+    /**
+     * Access a specific secure stop given its secure stop ID.
+     * Each secure stop has a unique ID.
+     *
+     * @param ssid the ID of the secure stop to return
+     * @return the secure stop identified by ssid
      */
     @NonNull
     public native byte[] getSecureStop(@NonNull byte[] ssid);
 
     /**
-     * Process the SecureStop server response message ssRelease.  After authenticating
-     * the message, remove the SecureStops identified in the response.
+     * Process the secure stop server response message ssRelease.  After
+     * authenticating the message, remove the secure stops identified in the
+     * response.
      *
      * @param ssRelease the server response indicating which secure stops to release
      */
     public native void releaseSecureStops(@NonNull byte[] ssRelease);
 
     /**
-     * Remove all secure stops without requiring interaction with the server.
+     * Remove a specific secure stop without requiring a secure stop release message
+     * from the license server.
+     * @param ssid the ID of the secure stop to remove
      */
-    public native void releaseAllSecureStops();
+    public native void removeSecureStop(@NonNull byte[] ssid);
+
+    /**
+     * Remove all secure stops without requiring a secure stop release message from
+     * the license server.
+     *
+     * This method was added in API 28. In API versions 18 through 27,
+     * {@link #releaseAllSecureStops} should be called instead. There is no need to
+     * do anything for API versions prior to 18.
+     */
+    public native void removeAllSecureStops();
+
+    /**
+     * Remove all secure stops without requiring a secure stop release message from
+     * the license server.
+     *
+     * @deprecated Remove all secure stops using {@link #removeAllSecureStops} instead.
+     */
+    public void releaseAllSecureStops() {
+        removeAllSecureStops();;
+    }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HDCP_LEVEL_UNKNOWN, HDCP_NONE, HDCP_V1, HDCP_V2,
@@ -1073,8 +1150,9 @@
      * implementation.
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE,
-                        HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
+    @IntDef({SECURITY_LEVEL_UNKNOWN, SECURITY_LEVEL_SW_SECURE_CRYPTO,
+            SECURITY_LEVEL_SW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_CRYPTO,
+            SECURITY_LEVEL_HW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_ALL})
     public @interface SecurityLevel {}
 
     /**
@@ -1084,65 +1162,66 @@
     public static final int SECURITY_LEVEL_UNKNOWN = 0;
 
     /**
-     *  Software-based whitebox crypto
+     * DRM key management uses software-based whitebox crypto.
      */
-    public static final int SW_SECURE_CRYPTO = 1;
+    public static final int SECURITY_LEVEL_SW_SECURE_CRYPTO = 1;
 
     /**
-     * Software-based whitebox crypto and an obfuscated decoder
+     * DRM key management and decoding use software-based whitebox crypto.
      */
-     public static final int SW_SECURE_DECODE = 2;
+    public static final int SECURITY_LEVEL_SW_SECURE_DECODE = 2;
 
     /**
-     * DRM key management and crypto operations are performed within a
-     * hardware backed trusted execution environment
+     * DRM key management and crypto operations are performed within a hardware
+     * backed trusted execution environment.
      */
-    public static final int HW_SECURE_CRYPTO = 3;
+    public static final int SECURITY_LEVEL_HW_SECURE_CRYPTO = 3;
 
     /**
-     * DRM key management, crypto operations and decoding of content
-     * are performed within a hardware backed trusted execution environment
+     * DRM key management, crypto operations and decoding of content are
+     * performed within a hardware backed trusted execution environment.
      */
-     public static final int HW_SECURE_DECODE = 4;
+    public static final int SECURITY_LEVEL_HW_SECURE_DECODE = 4;
 
     /**
      * DRM key management, crypto operations, decoding of content and all
-     * handling of the media (compressed and uncompressed) is handled within
-     * a hardware backed trusted execution environment.
+     * handling of the media (compressed and uncompressed) is handled within a
+     * hardware backed trusted execution environment.
      */
-    public static final int HW_SECURE_ALL = 5;
+    public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
 
     /**
-     * Return the current security level of a session. A session
-     * has an initial security level determined by the robustness of
-     * the DRM system's implementation on the device. The security
-     * level may be adjusted using {@link #setSecurityLevel}.
+     * The maximum security level supported by the device. This is the default
+     * security level when a session is opened.
+     * @hide
+     */
+    public static final int SECURITY_LEVEL_MAX = 6;
+
+    /**
+     * The maximum security level supported by the device. This is the default
+     * security level when a session is opened.
+     */
+    @SecurityLevel
+    public static final int getMaxSecurityLevel() {
+        return SECURITY_LEVEL_MAX;
+    }
+
+    /**
+     * Return the current security level of a session. A session has an initial
+     * security level determined by the robustness of the DRM system's
+     * implementation on the device. The security level may be changed at the
+     * time a session is opened using {@link #openSession}.
      * @param sessionId the session to query.
      * <p>
      * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
-     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
-     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
-     * {@link #HW_SECURE_ALL}.
+     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
+     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
+     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
      */
     @SecurityLevel
     public native int getSecurityLevel(@NonNull byte[] sessionId);
 
     /**
-     * Set the security level of a session. This can be useful if specific
-     * attributes of a lower security level are needed by an application,
-     * such as image manipulation or compositing. Reducing the security
-     * level will typically limit decryption to lower content resolutions,
-     * depending on the license policy.
-     * @param sessionId the session to set the security level on.
-     * @param level the new security level, one of
-     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
-     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
-     * {@link #HW_SECURE_ALL}.
-     */
-    public native void setSecurityLevel(@NonNull byte[] sessionId,
-            @SecurityLevel int level);
-
-    /**
      * String property name: identifies the maker of the DRM plugin
      */
     public static final String PROPERTY_VENDOR = "vendor";
@@ -1253,8 +1332,6 @@
      *
      * Additional vendor-specific fields may also be present in
      * the return value.
-     *
-     * @hide - not part of the public API at this time
      */
     public PersistableBundle getMetrics() {
         PersistableBundle bundle = getMetricsNative();
@@ -1571,8 +1648,6 @@
     /**
      * Definitions for the metrics that are reported via the
      * {@link #getMetrics} call.
-     *
-     * @hide - not part of the public API at this time
      */
     public final static class MetricsConstants
     {
@@ -1582,16 +1657,350 @@
          * Key to extract the number of successful {@link #openSession} calls
          * from the {@link PersistableBundle} returned by a
          * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
          */
         public static final String OPEN_SESSION_OK_COUNT
-            = "/drm/mediadrm/open_session/ok/count";
+            = "drm.mediadrm.open_session.ok.count";
 
         /**
          * Key to extract the number of failed {@link #openSession} calls
          * from the {@link PersistableBundle} returned by a
          * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
          */
         public static final String OPEN_SESSION_ERROR_COUNT
-            = "/drm/mediadrm/open_session/error/count";
+            = "drm.mediadrm.open_session.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #openSession} calls. The key is used to lookup the list
+         * in the {@link PersistableBundle} returned by a {@link #getMetrics}
+         * call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String OPEN_SESSION_ERROR_LIST
+            = "drm.mediadrm.open_session.error.list";
+
+        /**
+         * Key to extract the number of successful {@link #closeSession} calls
+         * from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String CLOSE_SESSION_OK_COUNT
+            = "drm.mediadrm.close_session.ok.count";
+
+        /**
+         * Key to extract the number of failed {@link #closeSession} calls
+         * from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String CLOSE_SESSION_ERROR_COUNT
+            = "drm.mediadrm.close_session.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #closeSession} calls. The key is used to lookup the list
+         * in the {@link PersistableBundle} returned by a {@link #getMetrics}
+         * call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String CLOSE_SESSION_ERROR_LIST
+            = "drm.mediadrm.close_session.error.list";
+
+        /**
+         * Key to extract the start times of sessions. Times are
+         * represented as milliseconds since epoch (1970-01-01T00:00:00Z).
+         * The start times are returned from the {@link PersistableBundle}
+         * from a {@link #getMetrics} call.
+         * The start times are returned as another {@link PersistableBundle}
+         * containing the session ids as keys and the start times as long
+         * values. Use {@link android.os.BaseBundle#keySet} to get the list of
+         * session ids, and then {@link android.os.BaseBundle#getLong} to get
+         * the start time for each session.
+         */
+        public static final String SESSION_START_TIMES_MS
+            = "drm.mediadrm.session_start_times_ms";
+
+        /**
+         * Key to extract the end times of sessions. Times are
+         * represented as milliseconds since epoch (1970-01-01T00:00:00Z).
+         * The end times are returned from the {@link PersistableBundle}
+         * from a {@link #getMetrics} call.
+         * The end times are returned as another {@link PersistableBundle}
+         * containing the session ids as keys and the end times as long
+         * values. Use {@link android.os.BaseBundle#keySet} to get the list of
+         * session ids, and then {@link android.os.BaseBundle#getLong} to get
+         * the end time for each session.
+         */
+        public static final String SESSION_END_TIMES_MS
+            = "drm.mediadrm.session_end_times_ms";
+
+        /**
+         * Key to extract the number of successful {@link #getKeyRequest} calls
+         * from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_KEY_REQUEST_OK_COUNT
+            = "drm.mediadrm.get_key_request.ok.count";
+
+        /**
+         * Key to extract the number of failed {@link #getKeyRequest}
+         * calls from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_KEY_REQUEST_ERROR_COUNT
+            = "drm.mediadrm.get_key_request.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #getKeyRequest} calls. The key is used to lookup the list
+         * in the {@link PersistableBundle} returned by a {@link #getMetrics}
+         * call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String GET_KEY_REQUEST_ERROR_LIST
+            = "drm.mediadrm.get_key_request.error.list";
+
+        /**
+         * Key to extract the average time in microseconds of calls to
+         * {@link #getKeyRequest}. The value is retrieved from the
+         * {@link PersistableBundle} returned from {@link #getMetrics}.
+         * The time is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_KEY_REQUEST_OK_TIME_MICROS
+            = "drm.mediadrm.get_key_request.ok.average_time_micros";
+
+        /**
+         * Key to extract the number of successful {@link #provideKeyResponse}
+         * calls from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String PROVIDE_KEY_RESPONSE_OK_COUNT
+            = "drm.mediadrm.provide_key_response.ok.count";
+
+        /**
+         * Key to extract the number of failed {@link #provideKeyResponse}
+         * calls from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String PROVIDE_KEY_RESPONSE_ERROR_COUNT
+            = "drm.mediadrm.provide_key_response.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #provideKeyResponse} calls. The key is used to lookup the
+         * list in the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String PROVIDE_KEY_RESPONSE_ERROR_LIST
+            = "drm.mediadrm.provide_key_response.error.list";
+
+        /**
+         * Key to extract the average time in microseconds of calls to
+         * {@link #provideKeyResponse}. The valus is retrieved from the
+         * {@link PersistableBundle} returned from {@link #getMetrics}.
+         * The time is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String PROVIDE_KEY_RESPONSE_OK_TIME_MICROS
+            = "drm.mediadrm.provide_key_response.ok.average_time_micros";
+
+        /**
+         * Key to extract the number of successful {@link #getProvisionRequest}
+         * calls from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_PROVISION_REQUEST_OK_COUNT
+            = "drm.mediadrm.get_provision_request.ok.count";
+
+        /**
+         * Key to extract the number of failed {@link #getProvisionRequest}
+         * calls from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_PROVISION_REQUEST_ERROR_COUNT
+            = "drm.mediadrm.get_provision_request.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #getProvisionRequest} calls. The key is used to lookup the
+         * list in the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String GET_PROVISION_REQUEST_ERROR_LIST
+            = "drm.mediadrm.get_provision_request.error.list";
+
+        /**
+         * Key to extract the number of successful
+         * {@link #provideProvisionResponse} calls from the
+         * {@link PersistableBundle} returned by a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String PROVIDE_PROVISION_RESPONSE_OK_COUNT
+            = "drm.mediadrm.provide_provision_response.ok.count";
+
+        /**
+         * Key to extract the number of failed
+         * {@link #provideProvisionResponse} calls from the
+         * {@link PersistableBundle} returned by a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String PROVIDE_PROVISION_RESPONSE_ERROR_COUNT
+            = "drm.mediadrm.provide_provision_response.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #provideProvisionResponse} calls. The key is used to lookup
+         * the list in the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String PROVIDE_PROVISION_RESPONSE_ERROR_LIST
+            = "drm.mediadrm.provide_provision_response.error.list";
+
+        /**
+         * Key to extract the number of successful
+         * {@link #getPropertyByteArray} calls were made with the
+         * {@link #PROPERTY_DEVICE_UNIQUE_ID} value. The key is used to lookup
+         * the value in the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_DEVICE_UNIQUE_ID_OK_COUNT
+            = "drm.mediadrm.get_device_unique_id.ok.count";
+
+        /**
+         * Key to extract the number of failed
+         * {@link #getPropertyByteArray} calls were made with the
+         * {@link #PROPERTY_DEVICE_UNIQUE_ID} value. The key is used to lookup
+         * the value in the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String GET_DEVICE_UNIQUE_ID_ERROR_COUNT
+            = "drm.mediadrm.get_device_unique_id.error.count";
+
+        /**
+         * Key to extract the list of error codes that were returned from
+         * {@link #getPropertyByteArray} calls with the
+         * {@link #PROPERTY_DEVICE_UNIQUE_ID} value. The key is used to lookup
+         * the list in the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         * The list is an array of Long values
+         * ({@link android.os.BaseBundle#getLongArray}).
+         */
+        public static final String GET_DEVICE_UNIQUE_ID_ERROR_LIST
+            = "drm.mediadrm.get_device_unique_id.error.list";
+
+        /**
+         * Key to extraact the count of {@link KeyStatus#STATUS_EXPIRED} events
+         * that occured. The count is extracted from the
+         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String KEY_STATUS_EXPIRED_COUNT
+            = "drm.mediadrm.key_status.EXPIRED.count";
+
+        /**
+         * Key to extract the count of {@link KeyStatus#STATUS_INTERNAL_ERROR}
+         * events that occured. The count is extracted from the
+         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String KEY_STATUS_INTERNAL_ERROR_COUNT
+            = "drm.mediadrm.key_status.INTERNAL_ERROR.count";
+
+        /**
+         * Key to extract the count of
+         * {@link KeyStatus#STATUS_OUTPUT_NOT_ALLOWED} events that occured.
+         * The count is extracted from the
+         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String KEY_STATUS_OUTPUT_NOT_ALLOWED_COUNT
+            = "drm.mediadrm.key_status_change.OUTPUT_NOT_ALLOWED.count";
+
+        /**
+         * Key to extract the count of {@link KeyStatus#STATUS_PENDING}
+         * events that occured. The count is extracted from the
+         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String KEY_STATUS_PENDING_COUNT
+            = "drm.mediadrm.key_status_change.PENDING.count";
+
+        /**
+         * Key to extract the count of {@link KeyStatus#STATUS_USABLE}
+         * events that occured. The count is extracted from the
+         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String KEY_STATUS_USABLE_COUNT
+            = "drm.mediadrm.key_status_change.USABLE.count";
+
+        /**
+         * Key to extract the count of {@link OnEventListener#onEvent}
+         * calls of type PROVISION_REQUIRED occured. The count is
+         * extracted from the {@link PersistableBundle} returned from a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String EVENT_PROVISION_REQUIRED_COUNT
+            = "drm.mediadrm.event.PROVISION_REQUIRED.count";
+
+        /**
+         * Key to extract the count of {@link OnEventListener#onEvent}
+         * calls of type KEY_NEEDED occured. The count is
+         * extracted from the {@link PersistableBundle} returned from a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String EVENT_KEY_NEEDED_COUNT
+            = "drm.mediadrm.event.KEY_NEEDED.count";
+
+        /**
+         * Key to extract the count of {@link OnEventListener#onEvent}
+         * calls of type KEY_EXPIRED occured. The count is
+         * extracted from the {@link PersistableBundle} returned from a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String EVENT_KEY_EXPIRED_COUNT
+            = "drm.mediadrm.event.KEY_EXPIRED.count";
+
+        /**
+         * Key to extract the count of {@link OnEventListener#onEvent}
+         * calls of type VENDOR_DEFINED. The count is
+         * extracted from the {@link PersistableBundle} returned from a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String EVENT_VENDOR_DEFINED_COUNT
+            = "drm.mediadrm.event.VENDOR_DEFINED.count";
+
+        /**
+         * Key to extract the count of {@link OnEventListener#onEvent}
+         * calls of type SESSION_RECLAIMED. The count is
+         * extracted from the {@link PersistableBundle} returned from a
+         * {@link #getMetrics} call.
+         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
+         */
+        public static final String EVENT_SESSION_RECLAIMED_COUNT
+            = "drm.mediadrm.event.SESSION_RECLAIMED.count";
     }
 }
diff --git a/android/media/MediaExtractor.java b/android/media/MediaExtractor.java
index 2c1b4b3..4919eeb 100644
--- a/android/media/MediaExtractor.java
+++ b/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.media.AudioPresentation;
 import android.media.MediaCodec;
 import android.media.MediaFormat;
 import android.media.MediaHTTPService;
@@ -40,6 +41,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -396,6 +398,17 @@
     }
 
     /**
+     * Get the list of available audio presentations for the track.
+     * @param trackIndex index of the track.
+     * @return a list of available audio presentations for a given valid audio track index.
+     * The list will be empty if the source does not contain any audio presentations.
+     */
+    @NonNull
+    public List<AudioPresentation> getAudioPresentations(int trackIndex) {
+        return new ArrayList<AudioPresentation>();
+    }
+
+    /**
      * Get the PSSH info if present.
      * @return a map of uuid-to-bytes, with the uuid specifying
      * the crypto scheme, and the bytes being the data specific to that scheme.
@@ -626,6 +639,12 @@
      */
     public native long getSampleTime();
 
+    /**
+     * @return size of the current sample in bytes or -1 if no more
+     * samples are available.
+     */
+    public native long getSampleSize();
+
     // Keep these in sync with their equivalents in NuMediaExtractor.h
     /**
      * The sample is a sync sample (or in {@link MediaCodec}'s terminology
diff --git a/android/media/MediaFormat.java b/android/media/MediaFormat.java
index e9128e4..384326f 100644
--- a/android/media/MediaFormat.java
+++ b/android/media/MediaFormat.java
@@ -87,6 +87,7 @@
  * <tr><td>{@link #KEY_AAC_DRC_ATTENUATION_FACTOR}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the DRC attenuation factor.</td></tr>
  * <tr><td>{@link #KEY_AAC_DRC_HEAVY_COMPRESSION}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies whether to use heavy compression.</td></tr>
  * <tr><td>{@link #KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the maximum number of channels the decoder outputs.</td></tr>
+ * <tr><td>{@link #KEY_AAC_DRC_EFFECT_TYPE}</td><td>Integer</td><td><b>decoder-only</b>, optional, if content is AAC audio, specifies the DRC effect type to use.</td></tr>
  * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>optional, a mask of audio channel assignments</td></tr>
  * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>
  * </table>
@@ -104,10 +105,10 @@
  * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr>
  * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user
  *         for encoders, readable in the output format of decoders</b></td></tr>
- * <tr><td>{@link #KEY_GRID_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
- * <tr><td>{@link #KEY_GRID_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_TILE_WIDTH}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_TILE_HEIGHT}</td><td>Integer</td><td>required if the image has grid</td></tr>
  * <tr><td>{@link #KEY_GRID_ROWS}</td><td>Integer</td><td>required if the image has grid</td></tr>
- * <tr><td>{@link #KEY_GRID_COLS}</td><td>Integer</td><td>required if the image has grid</td></tr>
+ * <tr><td>{@link #KEY_GRID_COLUMNS}</td><td>Integer</td><td>required if the image has grid</td></tr>
  * </table>
  */
 public final class MediaFormat {
@@ -149,17 +150,17 @@
      * The track's MediaFormat will come with {@link #KEY_WIDTH} and
      * {@link #KEY_HEIGHT} keys, which describes the width and height
      * of the image. If the image doesn't contain grid (i.e. none of
-     * {@link #KEY_GRID_WIDTH}, {@link #KEY_GRID_HEIGHT},
-     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLS} are present}), the
+     * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
+     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the
      * track will contain a single sample of coded data for the entire image,
      * and the image width and height should be used to set up the decoder.
      *
      * If the image does come with grid, each sample from the track will
      * contain one tile in the grid, of which the size is described by
-     * {@link #KEY_GRID_WIDTH} and {@link #KEY_GRID_HEIGHT}. This size
+     * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size
      * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
      * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
-     * by {@link #KEY_GRID_COLS} samples in row-major, top-row first,
+     * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first,
      * left-to-right order. The output image should be reconstructed by
      * first tiling the decoding results of the tiles in the correct order,
      * then trimming (before rotation is applied) on the bottom and right
@@ -173,10 +174,20 @@
     public static final String MIMETYPE_TEXT_VTT = "text/vtt";
 
     /**
+     * MIME type for SubRip (SRT) container.
+     */
+    public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+    /**
      * MIME type for CEA-608 closed caption data.
      */
     public static final String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
 
+    /**
+     * MIME type for CEA-708 closed caption data.
+     */
+    public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
+
     private Map<String, Object> mMap;
 
     /**
@@ -274,28 +285,28 @@
     public static final String KEY_FRAME_RATE = "frame-rate";
 
     /**
-     * A key describing the grid width of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
-     * track. The associated value is an integer.
+     * A key describing the width (in pixels) of each tile of the content in a
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_HEIGHT
+     * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
-     * @see #KEY_GRID_COLS
+     * @see #KEY_GRID_COLUMNS
      */
-    public static final String KEY_GRID_WIDTH = "grid-width";
+    public static final String KEY_TILE_WIDTH = "tile-width";
 
     /**
-     * A key describing the grid height of the content in a {@link #MIMETYPE_IMAGE_ANDROID_HEIC}
-     * track. The associated value is an integer.
+     * A key describing the height (in pixels) of each tile of the content in a
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
+     * @see #KEY_TILE_WIDTH
      * @see #KEY_GRID_ROWS
-     * @see #KEY_GRID_COLS
+     * @see #KEY_GRID_COLUMNS
      */
-    public static final String KEY_GRID_HEIGHT = "grid-height";
+    public static final String KEY_TILE_HEIGHT = "tile-height";
 
     /**
      * A key describing the number of grid rows in the content in a
@@ -303,9 +314,9 @@
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
-     * @see #KEY_GRID_HEIGHT
-     * @see #KEY_GRID_COLS
+     * @see #KEY_TILE_WIDTH
+     * @see #KEY_TILE_HEIGHT
+     * @see #KEY_GRID_COLUMNS
      */
     public static final String KEY_GRID_ROWS = "grid-rows";
 
@@ -315,11 +326,11 @@
      *
      * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
      *
-     * @see #KEY_GRID_WIDTH
-     * @see #KEY_GRID_HEIGHT
+     * @see #KEY_TILE_WIDTH
+     * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
      */
-    public static final String KEY_GRID_COLS = "grid-cols";
+    public static final String KEY_GRID_COLUMNS = "grid-cols";
 
     /**
      * A key describing the raw audio sample encoding/format.
@@ -512,6 +523,31 @@
     public static final String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level";
 
     /**
+     * A key describing for selecting the DRC effect type for MPEG-D DRC.
+     * The supported values are defined in ISO/IEC 23003-4:2015 and are described as follows:
+     * <table>
+     * <tr><th>Value</th><th>Effect</th></tr>
+     * <tr><th>-1</th><th>Off</th></tr>
+     * <tr><th>0</th><th>None</th></tr>
+     * <tr><th>1</th><th>Late night</th></tr>
+     * <tr><th>2</th><th>Noisy environment</th></tr>
+     * <tr><th>3</th><th>Limited playback range</th></tr>
+     * <tr><th>4</th><th>Low playback level</th></tr>
+     * <tr><th>5</th><th>Dialog enhancement</th></tr>
+     * <tr><th>6</th><th>General compression</th></tr>
+     * </table>
+     * <p>The value -1 (Off) disables DRC processing, while loudness normalization may still be
+     * active and dependent on KEY_AAC_DRC_TARGET_REFERENCE_LEVEL.<br>
+     * The value 0 (None) automatically enables DRC processing if necessary to prevent signal
+     * clipping<br>
+     * The value 6 (General compression) can be used for enabling MPEG-D DRC without particular
+     * DRC effect type request.<br>
+     * The default is DRC effect type "None".
+     * <p>This key is only used during decoding.
+     */
+    public static final String KEY_AAC_DRC_EFFECT_TYPE = "aac-drc-effect-type";
+
+    /**
      * A key describing the target reference level that was assumed at the encoder for
      * calculation of attenuation gains for clipping prevention. This information can be provided
      * if it is known, otherwise a worst-case assumption is used.
diff --git a/android/media/MediaItem2.java b/android/media/MediaItem2.java
index 96a87d5..423a1fd 100644
--- a/android/media/MediaItem2.java
+++ b/android/media/MediaItem2.java
@@ -19,31 +19,23 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.media.update.ApiLoader;
+import android.media.update.MediaItem2Provider;
 import android.os.Bundle;
-import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * A class with information on a single media item with the metadata information.
  * Media item are application dependent so we cannot guarantee that they contain the right values.
  * <p>
  * When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent.
  * <p>
  * This object isn't a thread safe.
- *
- * @hide
  */
-// TODO(jaewan): Unhide and extends from DataSourceDesc.
-//               Note) Feels like an anti-pattern. We should anonymize MediaItem2 to remove *all*
-//                     information in the DataSourceDesc. Why it should extends from this?
-// TODO(jaewan): Move this to updatable
-// Previously MediaBrowser.MediaItem
 public class MediaItem2 {
-    private final int mFlags;
-    private MediaMetadata2 mMetadata;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag=true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
@@ -62,15 +54,21 @@
      */
     public static final int FLAG_PLAYABLE = 1 << 1;
 
+    private final MediaItem2Provider mProvider;
+
     /**
-     * Create a new media item.
-     *
-     * @param metadata metadata with the media id.
-     * @param flags The flags for this item.
+     * Create a new media item
+     * @hide
      */
-    public MediaItem2(@NonNull MediaMetadata2 metadata, @Flags int flags) {
-        mFlags = flags;
-        setMetadata(metadata);
+    public MediaItem2(MediaItem2Provider provider) {
+        mProvider = provider;
+    }
+
+    /**
+     * @hide
+     */
+    public MediaItem2Provider getProvider() {
+        return mProvider;
     }
 
     /**
@@ -79,23 +77,22 @@
      * @return a new bundle instance
      */
     public Bundle toBundle() {
-        // TODO(jaewan): Fill here when we rebase.
-        return new Bundle();
+        return mProvider.toBundle_impl();
+    }
+
+    public static MediaItem2 fromBundle(Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_MediaItem2(bundle);
     }
 
     public String toString() {
-        final StringBuilder sb = new StringBuilder("MediaItem2{");
-        sb.append("mFlags=").append(mFlags);
-        sb.append(", mMetadata=").append(mMetadata);
-        sb.append('}');
-        return sb.toString();
+        return mProvider.toString_impl();
     }
 
     /**
      * Gets the flags of the item.
      */
     public @Flags int getFlags() {
-        return mFlags;
+        return mProvider.getFlags_impl();
     }
 
     /**
@@ -103,7 +100,7 @@
      * @see #FLAG_BROWSABLE
      */
     public boolean isBrowsable() {
-        return (mFlags & FLAG_BROWSABLE) != 0;
+        return mProvider.isBrowsable_impl();
     }
 
     /**
@@ -111,36 +108,113 @@
      * @see #FLAG_PLAYABLE
      */
     public boolean isPlayable() {
-        return (mFlags & FLAG_PLAYABLE) != 0;
+        return mProvider.isPlayable_impl();
     }
 
     /**
-     * Set a metadata. Metadata shouldn't be null and should have non-empty media id.
+     * Set a metadata. If the metadata is not null, its id should be matched with this instance's
+     * media id.
      *
-     * @param metadata
+     * @param metadata metadata to update
      */
-    public void setMetadata(@NonNull MediaMetadata2 metadata) {
-        if (metadata == null) {
-            throw new IllegalArgumentException("metadata cannot be null");
-        }
-        if (TextUtils.isEmpty(metadata.getMediaId())) {
-            throw new IllegalArgumentException("metadata must have a non-empty media id");
-        }
-        mMetadata = metadata;
+    public void setMetadata(@Nullable MediaMetadata2 metadata) {
+        mProvider.setMetadata_impl(metadata);
     }
 
     /**
      * Returns the metadata of the media.
      */
-    public @NonNull MediaMetadata2 getMetadata() {
-        return mMetadata;
+    public @Nullable MediaMetadata2 getMetadata() {
+        return mProvider.getMetadata_impl();
     }
 
     /**
-     * Returns the media id in the {@link MediaMetadata2} for this item.
-     * @see MediaMetadata2#METADATA_KEY_MEDIA_ID
+     * Returns the media id for this item.
      */
-    public @Nullable String getMediaId() {
-        return mMetadata.getMediaId();
+    public @NonNull String getMediaId() {
+        return mProvider.getMediaId_impl();
+    }
+
+    /**
+     * Return the {@link DataSourceDesc}
+     * <p>
+     * Can be {@code null} if the MediaItem2 came from another process and anonymized
+     *
+     * @return data source descriptor
+     */
+    public @Nullable DataSourceDesc getDataSourceDesc() {
+        return mProvider.getDataSourceDesc_impl();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return mProvider.equals_impl(obj);
+    }
+
+    /**
+     * Build {@link MediaItem2}
+     */
+    public static final class Builder {
+        private final MediaItem2Provider.BuilderProvider mProvider;
+
+        /**
+         * Constructor for {@link Builder}
+         *
+         * @param flags
+         */
+        public Builder(@Flags int flags) {
+            mProvider = ApiLoader.getProvider().createMediaItem2Builder(this, flags);
+        }
+
+        /**
+         * Set the media id of this instance. {@code null} for unset.
+         * <p>
+         * Media id is used to identify a media contents between session and controller.
+         * <p>
+         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
+         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
+         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
+         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
+         *
+         * @param mediaId media id
+         * @return this instance for chaining
+         */
+        public Builder setMediaId(@Nullable String mediaId) {
+            return mProvider.setMediaId_impl(mediaId);
+        }
+
+        /**
+         * Set the metadata of this instance. {@code null} for unset.
+         * <p>
+         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
+         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
+         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
+         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
+         *
+         * @param metadata metadata
+         * @return this instance for chaining
+         */
+        public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
+            return mProvider.setMetadata_impl(metadata);
+        }
+
+        /**
+         * Set the data source descriptor for this instance. {@code null} for unset.
+         *
+         * @param dataSourceDesc data source descriptor
+         * @return this instance for chaining
+         */
+        public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
+            return mProvider.setDataSourceDesc_impl(dataSourceDesc);
+        }
+
+        /**
+         * Build {@link MediaItem2}.
+         *
+         * @return a new {@link MediaItem2}.
+         */
+        public MediaItem2 build() {
+            return mProvider.build_impl();
+        }
     }
 }
diff --git a/android/media/MediaLibraryService2.java b/android/media/MediaLibraryService2.java
index d7e43ec..f29d386 100644
--- a/android/media/MediaLibraryService2.java
+++ b/android/media/MediaLibraryService2.java
@@ -20,27 +20,27 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
-import android.content.Context;
-import android.media.MediaSession2.BuilderBase;
+import android.media.MediaLibraryService2.MediaLibrarySession.Builder;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
-import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSessionService2Provider;
 import android.os.Bundle;
-import android.service.media.MediaBrowserService.BrowserRoot;
 
 import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * Base class for media library services.
  * <p>
  * Media library services enable applications to browse media content provided by an application
  * and ask the application to start playing it. They may also be used to control content that
  * is already playing by way of a {@link MediaSession2}.
  * <p>
- * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * When extending this class, also add the following to your {@code AndroidManifest.xml}.
  * <pre>
  * &lt;service android:name="component_name_of_your_implementation" &gt;
  *   &lt;intent-filter&gt;
@@ -48,13 +48,13 @@
  *   &lt;/intent-filter&gt;
  * &lt;/service&gt;</pre>
  * <p>
- * A {@link MediaLibraryService2} is extension of {@link MediaSessionService2}. IDs shouldn't
+ * The {@link MediaLibraryService2} class derives from {@link MediaSessionService2}. IDs shouldn't
  * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
  * default, an empty string will be used for ID of the service. If you want to specify an ID,
  * declare metadata in the manifest as follows.
- * @hide
+ *
+ * @see MediaSessionService2
  */
-// TODO(jaewan): Unhide
 public abstract class MediaLibraryService2 extends MediaSessionService2 {
     /**
      * This is the interface name that a service implementing a session service should say that it
@@ -63,178 +63,252 @@
     public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
 
     /**
-     * Session for the media library service.
+     * Session for the {@link MediaLibraryService2}. Build this object with
+     * {@link Builder} and return in {@link #onCreateSession(String)}.
      */
-    public class MediaLibrarySession extends MediaSession2 {
+    public static final class MediaLibrarySession extends MediaSession2 {
         private final MediaLibrarySessionProvider mProvider;
 
-        MediaLibrarySession(Context context, MediaPlayerBase player, String id,
-                Executor callbackExecutor, SessionCallback callback, VolumeProvider volumeProvider,
-                int ratingType, PendingIntent sessionActivity) {
-            super(context, player, id, callbackExecutor, callback, volumeProvider, ratingType,
-                    sessionActivity);
-            mProvider = (MediaLibrarySessionProvider) getProvider();
-        }
+        /**
+         * Callback for the {@link MediaLibrarySession}.
+         */
+        public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
+            public MediaLibrarySessionCallback() {
+                super();
+            }
 
-        @Override
-        MediaSession2Provider createProvider(Context context, MediaPlayerBase player, String id,
-                Executor callbackExecutor, SessionCallback callback, VolumeProvider volumeProvider,
-                int ratingType, PendingIntent sessionActivity) {
-            return ApiLoader.getProvider(context)
-                    .createMediaLibraryService2MediaLibrarySession(this, context, player, id,
-                            callbackExecutor, (MediaLibrarySessionCallback) callback,
-                            volumeProvider, ratingType, sessionActivity);
+            /**
+             * Called to get the root information for browsing by a particular client.
+             * <p>
+             * The implementation should verify that the client package has permission
+             * to access browse media information before returning the root id; it
+             * should return null if the client is not allowed to access this
+             * information.
+             *
+             * @param session the session for this event
+             * @param controllerInfo information of the controller requesting access to browse media.
+             * @param rootHints An optional bundle of service-specific arguments to send
+             * to the media library service when connecting and retrieving the
+             * root id for browsing, or null if none. The contents of this
+             * bundle may affect the information returned when browsing.
+             * @return The {@link LibraryRoot} for accessing this app's content or null.
+             * @see LibraryRoot#EXTRA_RECENT
+             * @see LibraryRoot#EXTRA_OFFLINE
+             * @see LibraryRoot#EXTRA_SUGGESTED
+             */
+            public @Nullable LibraryRoot onGetLibraryRoot(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo controllerInfo, @Nullable Bundle rootHints) {
+                return null;
+            }
+
+            /**
+             * Called to get an item. Return result here for the browser.
+             * <p>
+             * Return {@code null} for no result or error.
+             *
+             * @param session the session for this event
+             * @param mediaId item id to get media item.
+             * @return a media item. {@code null} for no result or error.
+             */
+            public @Nullable MediaItem2 onGetItem(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo controllerInfo, @NonNull String mediaId) {
+                return null;
+            }
+
+            /**
+             * Called to get children of given parent id. Return the children here for the browser.
+             * <p>
+             * Return an empty list for no children, and return {@code null} for the error.
+             *
+             * @param session the session for this event
+             * @param parentId parent id to get children
+             * @param page number of page
+             * @param pageSize size of the page
+             * @param extras extra bundle
+             * @return list of children. Can be {@code null}.
+             */
+            public @Nullable List<MediaItem2> onGetChildren(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo controller, @NonNull String parentId, int page,
+                    int pageSize, @Nullable Bundle extras) {
+                return null;
+            }
+
+            /**
+             * Called when a controller subscribes to the parent.
+             * <p>
+             * It's your responsibility to keep subscriptions by your own and call
+             * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
+             * when the parent is changed.
+             *
+             * @param session the session for this event
+             * @param controller controller
+             * @param parentId parent id
+             * @param extras extra bundle
+             */
+            public void onSubscribe(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo controller, @NonNull String parentId,
+                    @Nullable Bundle extras) {
+            }
+
+            /**
+             * Called when a controller unsubscribes to the parent.
+             *
+             * @param session the session for this event
+             * @param controller controller
+             * @param parentId parent id
+             */
+            public void onUnsubscribe(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo controller, @NonNull String parentId) {
+            }
+
+            /**
+             * Called when a controller requests search.
+             *
+             * @param session the session for this event
+             * @param query The search query sent from the media browser. It contains keywords
+             *              separated by space.
+             * @param extras The bundle of service-specific arguments sent from the media browser.
+             */
+            public void onSearch(@NonNull MediaLibrarySession session,
+                    @NonNull ControllerInfo controllerInfo, @NonNull String query,
+                    @Nullable Bundle extras) {
+            }
+
+            /**
+             * Called to get the search result. Return search result here for the browser which has
+             * requested search previously.
+             * <p>
+             * Return an empty list for no search result, and return {@code null} for the error.
+             *
+             * @param session the session for this event
+             * @param controllerInfo Information of the controller requesting the search result.
+             * @param query The search query which was previously sent through
+             *              {@link #onSearch(MediaLibrarySession, ControllerInfo, String, Bundle)}.
+             * @param page page number. Starts from {@code 1}.
+             * @param pageSize page size. Should be greater or equal to {@code 1}.
+             * @param extras The bundle of service-specific arguments sent from the media browser.
+             * @return search result. {@code null} for error.
+             */
+            public @Nullable List<MediaItem2> onGetSearchResult(
+                    @NonNull MediaLibrarySession session, @NonNull ControllerInfo controllerInfo,
+                    @NonNull String query, int page, int pageSize, @Nullable Bundle extras) {
+                return null;
+            }
         }
 
         /**
-         * Notify subscribed controller about change in a parent's children.
+         * Builder for {@link MediaLibrarySession}.
+         */
+        // Override all methods just to show them with the type instead of generics in Javadoc.
+        // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
+        public static final class Builder extends BuilderBase<MediaLibrarySession, Builder,
+                MediaLibrarySessionCallback> {
+            // Builder requires MediaLibraryService2 instead of Context just to ensure that the
+            // builder can be only instantiated within the MediaLibraryService2.
+            // Ideally it's better to make it inner class of service to enforce, it violates API
+            // guideline that Builders should be the inner class of the building target.
+            public Builder(@NonNull MediaLibraryService2 service,
+                    @NonNull @CallbackExecutor Executor callbackExecutor,
+                    @NonNull MediaLibrarySessionCallback callback) {
+                super((instance) -> ApiLoader.getProvider().createMediaLibraryService2Builder(
+                        service, (Builder) instance, callbackExecutor, callback));
+            }
+
+            @Override
+            public Builder setPlayer(@NonNull MediaPlayerBase player) {
+                return super.setPlayer(player);
+            }
+
+            @Override
+            public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
+                return super.setPlaylistAgent(playlistAgent);
+            }
+
+            @Override
+            public Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+                return super.setVolumeProvider(volumeProvider);
+            }
+
+            @Override
+            public Builder setSessionActivity(@Nullable PendingIntent pi) {
+                return super.setSessionActivity(pi);
+            }
+
+            @Override
+            public Builder setId(@NonNull String id) {
+                return super.setId(id);
+            }
+
+            @Override
+            public Builder setSessionCallback(@NonNull @CallbackExecutor Executor executor,
+                    @NonNull MediaLibrarySessionCallback callback) {
+                return super.setSessionCallback(executor, callback);
+            }
+
+            @Override
+            public MediaLibrarySession build() {
+                return super.build();
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public MediaLibrarySession(MediaLibrarySessionProvider provider) {
+            super(provider);
+            mProvider = provider;
+        }
+
+        /**
+         * Notify the controller of the change in a parent's children.
+         * <p>
+         * If the controller hasn't subscribed to the parent, the API will do nothing.
+         * <p>
+         * Controllers will use {@link MediaBrowser2#getChildren(String, int, int, Bundle)} to get
+         * the list of children.
          *
          * @param controller controller to notify
-         * @param parentId
-         * @param options
+         * @param parentId parent id with changes in its children
+         * @param itemCount number of children.
+         * @param extras extra information from session to controller
          */
         public void notifyChildrenChanged(@NonNull ControllerInfo controller,
-                @NonNull String parentId, @NonNull Bundle options) {
-            mProvider.notifyChildrenChanged_impl(controller, parentId, options);
+                @NonNull String parentId, int itemCount, @Nullable Bundle extras) {
+            mProvider.notifyChildrenChanged_impl(controller, parentId, itemCount, extras);
         }
 
         /**
-         * Notify subscribed controller about change in a parent's children.
+         * Notify all controllers that subscribed to the parent about change in the parent's
+         * children, regardless of the extra bundle supplied by
+         * {@link MediaBrowser2#subscribe(String, Bundle)}.
          *
          * @param parentId parent id
-         * @param options optional bundle
+         * @param itemCount number of children
+         * @param extras extra information from session to controller
          */
         // This is for the backward compatibility.
-        public void notifyChildrenChanged(@NonNull String parentId, @Nullable Bundle options) {
-            mProvider.notifyChildrenChanged_impl(parentId, options);
-        }
-    }
-
-    public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
-        /**
-         * Called to get the root information for browsing by a particular client.
-         * <p>
-         * The implementation should verify that the client package has permission
-         * to access browse media information before returning the root id; it
-         * should return null if the client is not allowed to access this
-         * information.
-         *
-         * @param controllerInfo information of the controller requesting access to browse media.
-         * @param rootHints An optional bundle of service-specific arguments to send
-         * to the media browser service when connecting and retrieving the
-         * root id for browsing, or null if none. The contents of this
-         * bundle may affect the information returned when browsing.
-         * @return The {@link BrowserRoot} for accessing this app's content or null.
-         * @see BrowserRoot#EXTRA_RECENT
-         * @see BrowserRoot#EXTRA_OFFLINE
-         * @see BrowserRoot#EXTRA_SUGGESTED
-         */
-        public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
-                @Nullable Bundle rootHints) {
-            return null;
+        public void notifyChildrenChanged(@NonNull String parentId, int itemCount,
+                @Nullable Bundle extras) {
+            mProvider.notifyChildrenChanged_impl(parentId, itemCount, extras);
         }
 
         /**
-         * Called to get the search result. Return search result here for the browser.
-         * <p>
-         * Return an empty list for no search result, and return {@code null} for the error.
+         * Notify controller about change in the search result.
          *
-         * @param query The search query sent from the media browser. It contains keywords separated
-         *            by space.
-         * @param extras The bundle of service-specific arguments sent from the media browser.
-         * @return search result. {@code null} for error.
+         * @param controller controller to notify
+         * @param query previously sent search query from the controller.
+         * @param itemCount the number of items that have been found in the search.
+         * @param extras extra bundle
          */
-        public @Nullable List<MediaItem2> onSearch(@NonNull ControllerInfo controllerInfo,
-                @NonNull String query, @Nullable Bundle extras) {
-            return null;
-        }
-
-        /**
-         * Called to get the search result . Return result here for the browser.
-         * <p>
-         * Return an empty list for no search result, and return {@code null} for the error.
-         *
-         * @param itemId item id to get media item.
-         * @return media item2. {@code null} for error.
-         */
-        public @Nullable MediaItem2 onLoadItem(@NonNull ControllerInfo controllerInfo,
-                @NonNull String itemId) {
-            return null;
-        }
-
-        /**
-         * Called to get the search result. Return search result here for the browser.
-         * <p>
-         * Return an empty list for no search result, and return {@code null} for the error.
-         *
-         * @param parentId parent id to get children
-         * @param page number of page
-         * @param pageSize size of the page
-         * @param options
-         * @return list of children. Can be {@code null}.
-         */
-        public @Nullable List<MediaItem2> onLoadChildren(@NonNull ControllerInfo controller,
-                @NonNull String parentId, int page, int pageSize, @Nullable Bundle options) {
-            return null;
-        }
-
-        /**
-         * Called when a controller subscribes to the parent.
-         *
-         * @param controller controller
-         * @param parentId parent id
-         * @param options optional bundle
-         */
-        public void onSubscribed(@NonNull ControllerInfo controller,
-                String parentId, @Nullable Bundle options) {
-        }
-
-        /**
-         * Called when a controller unsubscribes to the parent.
-         *
-         * @param controller controller
-         * @param parentId parent id
-         * @param options optional bundle
-         */
-        public void onUnsubscribed(@NonNull ControllerInfo controller,
-                String parentId, @Nullable Bundle options) {
-        }
-    }
-
-    /**
-     * Builder for {@link MediaLibrarySession}.
-     */
-    // TODO(jaewan): Move this to updatable.
-    public class MediaLibrarySessionBuilder
-            extends BuilderBase<MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
-        public MediaLibrarySessionBuilder(
-                @NonNull Context context, @NonNull MediaPlayerBase player,
-                @NonNull @CallbackExecutor Executor callbackExecutor,
-                @NonNull MediaLibrarySessionCallback callback) {
-            super(context, player);
-            setSessionCallback(callbackExecutor, callback);
-        }
-
-        @Override
-        public MediaLibrarySessionBuilder setSessionCallback(
-                @NonNull @CallbackExecutor Executor callbackExecutor,
-                @NonNull MediaLibrarySessionCallback callback) {
-            if (callback == null) {
-                throw new IllegalArgumentException("MediaLibrarySessionCallback cannot be null");
-            }
-            return super.setSessionCallback(callbackExecutor, callback);
-        }
-
-        @Override
-        public MediaLibrarySession build() throws IllegalStateException {
-            return new MediaLibrarySession(mContext, mPlayer, mId, mCallbackExecutor, mCallback,
-                    mVolumeProvider, mRatingType, mSessionActivity);
+        public void notifySearchResultChanged(@NonNull ControllerInfo controller,
+                @NonNull String query, int itemCount, @NonNull Bundle extras) {
+            mProvider.notifySearchResultChanged_impl(controller, query, itemCount, extras);
         }
     }
 
     @Override
     MediaSessionService2Provider createProvider() {
-        return ApiLoader.getProvider(this).createMediaLibraryService2(this);
+        return ApiLoader.getProvider().createMediaLibraryService2(this);
     }
 
     /**
@@ -249,8 +323,8 @@
      * This method will be called on the main thread.
      *
      * @param sessionId session id written in the AndroidManifest.xml.
-     * @return a new browser session
-     * @see MediaLibrarySessionBuilder
+     * @return a new library session
+     * @see Builder
      * @see #getSession()
      * @throws RuntimeException if returned session is invalid
      */
@@ -258,93 +332,91 @@
     public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
 
     /**
-     * Contains information that the browser service needs to send to the client
-     * when first connected.
+     * Contains information that the library service needs to send to the client when
+     * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
      */
-    public static final class BrowserRoot {
+    public static final class LibraryRoot {
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for recently played media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * librar root for recently played media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
+         * <p>When creating a media browser for a given media library service, this key can be
          * supplied as a root hint for retrieving media items that are recently played.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
-         * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
+         * {@link MediaLibrarySessionCallback#onGetLibraryRoot(MediaLibrarySession, ControllerInfo, Bundle)}
+         * is called back.
          *
          * <p>The root hint may contain multiple keys.
          *
          * @see #EXTRA_OFFLINE
          * @see #EXTRA_SUGGESTED
          */
-        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
+        public static final String EXTRA_RECENT = "android.media.extra.RECENT";
 
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for offline media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * library root for offline media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
+         * <p>When creating a media browser for a given media library service, this key can be
          * supplied as a root hint for retrieving media items that are can be played without an
          * internet connection.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
-         * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
+         * {@link MediaLibrarySessionCallback#onGetLibraryRoot(MediaLibrarySession, ControllerInfo, Bundle)}
+         * is called back.
          *
          * <p>The root hint may contain multiple keys.
          *
          * @see #EXTRA_RECENT
          * @see #EXTRA_SUGGESTED
          */
-        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
+        public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
 
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for suggested media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * library root for suggested media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint for retrieving the media items suggested by the media browser
-         * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
-         * is considered ordered by relevance, first being the top suggestion.
-         * If the media browser service can provide such media items, the implementation must return
+         * <p>When creating a media browser for a given media library service, this key can be
+         * supplied as a root hint for retrieving the media items suggested by the media library
+         * service. The list of media items is considered ordered by relevance, first being the top
+         * suggestion.
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
-         * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
+         * {@link MediaLibrarySessionCallback#onGetLibraryRoot(MediaLibrarySession, ControllerInfo, Bundle)}
+         * is called back.
          *
          * <p>The root hint may contain multiple keys.
          *
          * @see #EXTRA_RECENT
          * @see #EXTRA_OFFLINE
          */
-        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
+        public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
 
-        final private String mRootId;
-        final private Bundle mExtras;
+        private final LibraryRootProvider mProvider;
 
         /**
-         * Constructs a browser root.
+         * Constructs a library root.
          * @param rootId The root id for browsing.
-         * @param extras Any extras about the browser service.
+         * @param extras Any extras about the library service.
          */
-        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
-            if (rootId == null) {
-                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
-                        "Use null for BrowserRoot instead.");
-            }
-            mRootId = rootId;
-            mExtras = extras;
+        public LibraryRoot(@NonNull String rootId, @Nullable Bundle extras) {
+            mProvider = ApiLoader.getProvider().createMediaLibraryService2LibraryRoot(
+                    this, rootId, extras);
         }
 
         /**
          * Gets the root id for browsing.
          */
         public String getRootId() {
-            return mRootId;
+            return mProvider.getRootId_impl();
         }
 
         /**
-         * Gets any extras about the browser service.
+         * Gets any extras about the library service.
          */
         public Bundle getExtras() {
-            return mExtras;
+            return mProvider.getExtras_impl();
         }
     }
 }
diff --git a/android/media/MediaMetadata2.java b/android/media/MediaMetadata2.java
index 0e24db6..7b03ae0 100644
--- a/android/media/MediaMetadata2.java
+++ b/android/media/MediaMetadata2.java
@@ -16,216 +16,348 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.graphics.Bitmap;
+import android.media.update.ApiLoader;
+import android.media.update.MediaMetadata2Provider;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.ArrayMap;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Set;
 
 /**
- * Contains metadata about an item, such as the title, artist, etc.
  * @hide
+ * Contains metadata about an item, such as the title, artist, etc.
  */
-// TODO(jaewan): Move this to updatable
+// New version of MediaMetadata with following changes
+//   - Don't implement Parcelable for updatable support.
+//   - Also support MediaDescription features. MediaDescription is deprecated instead because
+//     it was insufficient for controller to display media contents.
 public final class MediaMetadata2 {
-    private static final String TAG = "MediaMetadata2";
-
     /**
-     * The title of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the title of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
 
     /**
-     * The artist of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the artist of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
 
     /**
-     * The duration of the media in ms. A negative duration indicates that the
-     * duration is unknown (or infinite).
+     * The metadata key for a {@link Long} typed value to retrieve the information about the
+     * duration of the media in ms. A negative duration indicates that the duration is unknown
+     * (or infinite).
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
 
     /**
-     * The album title for the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the album title for the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
 
     /**
-     * The author of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the author of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
 
     /**
-     * The writer of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the writer of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
 
     /**
-     * The composer of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the composer of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
 
     /**
-     * The compilation status of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the compilation status of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
 
     /**
-     * The date the media was created or published. The format is unspecified
-     * but RFC 3339 is recommended.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the date the media was created or published.
+     * The format is unspecified but RFC 3339 is recommended.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
 
     /**
-     * The year the media was created or published as a long.
+     * The metadata key for a {@link Long} typed value to retrieve the information about the year
+     * the media was created or published.
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
 
     /**
-     * The genre of the media.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the genre of the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
 
     /**
-     * The track number for the media.
+     * The metadata key for a {@link Long} typed value to retrieve the information about the
+     * track number for the media.
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
 
     /**
-     * The number of tracks in the media's original source.
+     * The metadata key for a {@link Long} typed value to retrieve the information about the
+     * number of tracks in the media's original source.
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
 
     /**
-     * The disc number for the media's original source.
+     * The metadata key for a {@link Long} typed value to retrieve the information about the
+     * disc number for the media's original source.
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
 
     /**
-     * The artist for the album of the media's original source.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the artist for the album of the media's original source.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
 
     /**
-     * The artwork for the media as a {@link Bitmap}.
+     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
+     * artwork for the media.
+     * The artwork should be relatively small and may be scaled down if it is too large.
+     * For higher resolution artwork, {@link #METADATA_KEY_ART_URI} should be used instead.
      *
-     * The artwork should be relatively small and may be scaled down
-     * if it is too large. For higher resolution artwork
-     * {@link #METADATA_KEY_ART_URI} should be used instead.
+     * @see Builder#putBitmap(String, Bitmap)
+     * @see #getBitmap(String)
      */
     public static final String METADATA_KEY_ART = "android.media.metadata.ART";
 
     /**
-     * The artwork for the media as a Uri style String.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about Uri of the artwork for the media.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
 
     /**
-     * The artwork for the album of the media's original source as a
-     * {@link Bitmap}.
-     * The artwork should be relatively small and may be scaled down
-     * if it is too large. For higher resolution artwork
-     * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
+     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
+     * artwork for the album of the media's original source.
+     * The artwork should be relatively small and may be scaled down if it is too large.
+     * For higher resolution artwork, {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
+     *
+     * @see Builder#putBitmap(String, Bitmap)
+     * @see #getBitmap(String)
      */
     public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
 
     /**
-     * The artwork for the album of the media's original source as a Uri style
-     * String.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the Uri of the artwork for the album of the media's original source.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
 
     /**
-     * The user's rating for the media.
+     * The metadata key for a {@link Rating2} typed value to retrieve the information about the
+     * user's rating for the media.
      *
-     * @see Rating
+     * @see Builder#putRating(String, Rating2)
+     * @see #getRating(String)
      */
     public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
 
     /**
-     * The overall rating for the media.
+     * The metadata key for a {@link Rating2} typed value to retrieve the information about the
+     * overall rating for the media.
      *
-     * @see Rating
+     * @see Builder#putRating(String, Rating2)
+     * @see #getRating(String)
      */
     public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
 
     /**
-     * A title that is suitable for display to the user. This will generally be
-     * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
-     * When displaying media described by this metadata this should be preferred
-     * if present.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the title that is suitable for display to the user.
+     * It will generally be the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
+     * When displaying media described by this metadata, this should be preferred if present.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
 
     /**
-     * A subtitle that is suitable for display to the user. When displaying a
-     * second line for media described by this metadata this should be preferred
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the subtitle that is suitable for display to the user.
+     * When displaying a second line for media described by this metadata, this should be preferred
      * to other fields if present.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_DISPLAY_SUBTITLE
             = "android.media.metadata.DISPLAY_SUBTITLE";
 
     /**
-     * A description that is suitable for display to the user. When displaying
-     * more information for media described by this metadata this should be
-     * preferred to other fields if present.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the description that is suitable for display to the user.
+     * When displaying more information for media described by this metadata,
+     * this should be preferred to other fields if present.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_DISPLAY_DESCRIPTION
             = "android.media.metadata.DISPLAY_DESCRIPTION";
 
     /**
-     * An icon or thumbnail that is suitable for display to the user. When
-     * displaying an icon for media described by this metadata this should be
-     * preferred to other fields if present. This must be a {@link Bitmap}.
+     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the icon
+     * or thumbnail that is suitable for display to the user.
+     * When displaying an icon for media described by this metadata, this should be preferred to
+     * other fields if present.
+     * <p>
+     * The icon should be relatively small and may be scaled down if it is too large.
+     * For higher resolution artwork, {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
      *
-     * The icon should be relatively small and may be scaled down
-     * if it is too large. For higher resolution artwork
-     * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
+     * @see Builder#putBitmap(String, Bitmap)
+     * @see #getBitmap(String)
      */
-    public static final String METADATA_KEY_DISPLAY_ICON
-            = "android.media.metadata.DISPLAY_ICON";
+    public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
 
     /**
-     * An icon or thumbnail that is suitable for display to the user. When
-     * displaying more information for media described by this metadata the
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the Uri of icon or thumbnail that is suitable for display to the user.
+     * When displaying more information for media described by this metadata, the
      * display description should be preferred to other fields when present.
-     * This must be a Uri style String.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_DISPLAY_ICON_URI
             = "android.media.metadata.DISPLAY_ICON_URI";
 
     /**
-     * A String key for identifying the content. This value is specific to the
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the media ID of the content. This value is specific to the
      * service providing the content. If used, this should be a persistent
      * unique key for the underlying content.  It may be used with
      * {@link MediaController2#playFromMediaId(String, Bundle)}
      * to initiate playback when provided by a {@link MediaBrowser2} connected to
      * the same app.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
 
     /**
-     * A Uri formatted String representing the content. This value is specific to the
-     * service providing the content. It may be used with
-     * {@link MediaController2#playFromUri(Uri, Bundle)}
-     * to initiate playback when provided by a {@link MediaBrowser2} connected to
-     * the same app.
+     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
+     * information about the Uri of the content. This value is specific to the service providing the
+     * content. It may be used with {@link MediaController2#playFromUri(Uri, Bundle)}
+     * to initiate playback when provided by a {@link MediaBrowser2} connected to the same app.
+     *
+     * @see Builder#putText(String, CharSequence)
+     * @see Builder#putString(String, String)
+     * @see #getText(String)
+     * @see #getString(String)
      */
     public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
 
     /**
-     * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
+     * The metadata key for a {@link Long} typed value to retrieve the information about the
+     * bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
      * AVRCP 1.5. It should be one of the following:
      * <ul>
      * <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
@@ -236,6 +368,9 @@
      * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
      * <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
      * </ul>
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_BT_FOLDER_TYPE
             = "android.media.metadata.BT_FOLDER_TYPE";
@@ -283,14 +418,19 @@
     public static final long BT_FOLDER_TYPE_YEARS = 6;
 
     /**
-     * Whether the media is an advertisement. A value of 0 indicates it is not an advertisement. A
-     * value of 1 or non-zero indicates it is an advertisement. If not specified, this value is set
-     * to 0 by default.
+     * The metadata key for a {@link Long} typed value to retrieve the information about whether
+     * the media is an advertisement. A value of 0 indicates it is not an advertisement.
+     * A value of 1 or non-zero indicates it is an advertisement.
+     * If not specified, this value is set to 0 by default.
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     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
+     * The metadata key for a {@link Long} typed value to retrieve the information about the
+     * download status of the media which will be used for later offline playback. It should be
      * one of the following:
      *
      * <ul>
@@ -298,6 +438,9 @@
      * <li>{@link #STATUS_DOWNLOADING}</li>
      * <li>{@link #STATUS_DOWNLOADED}</li>
      * </ul>
+     *
+     * @see Builder#putLong(String, long)
+     * @see #getLong(String)
      */
     public static final String METADATA_KEY_DOWNLOAD_STATUS =
             "android.media.metadata.DOWNLOAD_STATUS";
@@ -325,9 +468,8 @@
 
     /**
      * A {@link Bundle} extra.
-     * @hide
      */
-    public static final String METADATA_KEY_EXTRA = "android.media.metadata.EXTRA";
+    public static final String METADATA_KEY_EXTRAS = "android.media.metadata.EXTRAS";
 
     /**
      * @hide
@@ -364,76 +506,20 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface RatingKey {}
 
-    static final int METADATA_TYPE_LONG = 0;
-    static final int METADATA_TYPE_TEXT = 1;
-    static final int METADATA_TYPE_BITMAP = 2;
-    static final int METADATA_TYPE_RATING = 3;
-    static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
+    /**
+     * @hide
+     */
+    // TODO(jaewan): Add predefined float key.
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FloatKey {}
 
-    static {
-        METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
-        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
-        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 = {
-            METADATA_KEY_TITLE,
-            METADATA_KEY_ARTIST,
-            METADATA_KEY_ALBUM,
-            METADATA_KEY_ALBUM_ARTIST,
-            METADATA_KEY_WRITER,
-            METADATA_KEY_AUTHOR,
-            METADATA_KEY_COMPOSER
-    };
-
-    private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
-            METADATA_KEY_DISPLAY_ICON,
-            METADATA_KEY_ART,
-            METADATA_KEY_ALBUM_ART
-    };
-
-    private static final @TextKey String[] PREFERRED_URI_ORDER = {
-            METADATA_KEY_DISPLAY_ICON_URI,
-            METADATA_KEY_ART_URI,
-            METADATA_KEY_ALBUM_ART_URI
-    };
-
-    final Bundle mBundle;
+    private final MediaMetadata2Provider mProvider;
 
     /**
      * @hide
      */
-    public MediaMetadata2(Bundle bundle) {
-        mBundle = new Bundle(bundle);
+    public MediaMetadata2(MediaMetadata2Provider provider) {
+        mProvider = provider;
     }
 
     /**
@@ -442,8 +528,8 @@
      * @param key a String key
      * @return true if the key exists in this metadata, false otherwise
      */
-    public boolean containsKey(String key) {
-        return mBundle.containsKey(key);
+    public boolean containsKey(@NonNull String key) {
+        return mProvider.containsKey_impl(key);
     }
 
     /**
@@ -454,20 +540,20 @@
      * @param key The key the value is stored under
      * @return a CharSequence value, or null
      */
-    public CharSequence getText(@TextKey String key) {
-        return mBundle.getCharSequence(key);
+    public @Nullable CharSequence getText(@NonNull @TextKey String key) {
+        return mProvider.getText_impl(key);
     }
 
     /**
-     * Returns the value associated with the given key, or null if no mapping of
-     * the desired type exists for the given key or a null value is explicitly
-     * associated with the key.
+     * Returns the media id, or {@code null} if the id doesn't exist.
+     *<p>
+     * This is equivalent to the {@link #getString(String)} with the {@link #METADATA_KEY_MEDIA_ID}.
      *
-     * @
      * @return media id. Can be {@code null}
+     * @see #METADATA_KEY_MEDIA_ID
      */
     public @Nullable String getMediaId() {
-        return getString(METADATA_KEY_MEDIA_ID);
+        return mProvider.getMediaId_impl();
     }
 
     /**
@@ -478,12 +564,8 @@
      * @param key The key the value is stored under
      * @return a String value, or null
      */
-    public String getString(@TextKey String key) {
-        CharSequence text = mBundle.getCharSequence(key);
-        if (text != null) {
-            return text.toString();
-        }
-        return null;
+    public @Nullable String getString(@NonNull @TextKey String key) {
+        return mProvider.getString_impl(key);
     }
 
     /**
@@ -493,27 +575,22 @@
      * @param key The key the value is stored under
      * @return a long value
      */
-    public long getLong(@LongKey String key) {
-        return mBundle.getLong(key, 0);
+    public long getLong(@NonNull @LongKey String key) {
+        return mProvider.getLong_impl(key);
     }
 
     /**
      * Return a {@link Rating2} for the given key or null if no rating exists for
      * the given key.
+     * <p>
+     * For the {@link #METADATA_KEY_USER_RATING}, A {@code null} return value means that user rating
+     * cannot be set by {@link MediaController2}.
      *
      * @param key The key the value is stored under
-     * @return A {@link Rating2} or null
+     * @return A {@link Rating2} or {@code null}
      */
-    public Rating2 getRating(@RatingKey String key) {
-        // TODO(jaewan): Add backward compatibility
-        Rating2 rating = null;
-        try {
-            rating = Rating2.fromBundle(mBundle.getBundle(key));
-        } catch (Exception e) {
-            // ignore, value was not a rating
-            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
-        }
-        return rating;
+    public @Nullable Rating2 getRating(@NonNull @RatingKey String key) {
+        return mProvider.getRating_impl(key);
     }
 
     /**
@@ -523,15 +600,19 @@
      * @param key The key the value is stored under
      * @return A {@link Bitmap} or null
      */
-    public Bitmap getBitmap(@BitmapKey String key) {
-        Bitmap bmp = null;
-        try {
-            bmp = mBundle.getParcelable(key);
-        } catch (Exception e) {
-            // ignore, value was not a bitmap
-            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
-        }
-        return bmp;
+    public @Nullable Bitmap getBitmap(@NonNull @BitmapKey String key) {
+        return mProvider.getBitmap_impl(key);
+    }
+
+    /**
+     * Return the value associated with the given key, or 0.0f if no long exists
+     * for the given key.
+     *
+     * @param key The key the value is stored under
+     * @return a float value
+     */
+    public float getFloat(@NonNull @FloatKey String key) {
+        return mProvider.getFloat_impl(key);
     }
 
     /**
@@ -539,14 +620,8 @@
      *
      * @return A {@link Bundle} or {@code null}
      */
-    public Bundle getExtra() {
-        try {
-            return mBundle.getBundle(METADATA_KEY_EXTRA);
-        } catch (Exception e) {
-            // ignore, value was not an bundle
-            Log.w(TAG, "Failed to retrieve an extra");
-        }
-        return null;
+    public @Nullable Bundle getExtras() {
+        return mProvider.getExtras_impl();
     }
 
     /**
@@ -555,7 +630,7 @@
      * @return The number of fields in the metadata.
      */
     public int size() {
-        return mBundle.size();
+        return mProvider.size_impl();
     }
 
     /**
@@ -563,8 +638,8 @@
      *
      * @return a Set of String keys
      */
-    public Set<String> keySet() {
-        return mBundle.keySet();
+    public @NonNull Set<String> keySet() {
+        return mProvider.keySet_impl();
     }
 
     /**
@@ -573,8 +648,19 @@
      *
      * @return The Bundle backing this metadata.
      */
-    public Bundle getBundle() {
-        return mBundle;
+    public @NonNull Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    /**
+     * Creates the {@link MediaMetadata2} from the bundle that previously returned by
+     * {@link #toBundle()}.
+     *
+     * @param bundle bundle for the metadata
+     * @return a new MediaMetadata2
+     */
+    public static @NonNull MediaMetadata2 fromBundle(@Nullable Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_MediaMetadata2(bundle);
     }
 
     /**
@@ -582,14 +668,14 @@
      * use the appropriate data type.
      */
     public static final class Builder {
-        private final Bundle mBundle;
+        private final MediaMetadata2Provider.BuilderProvider mProvider;
 
         /**
          * Create an empty Builder. Any field that should be included in the
          * {@link MediaMetadata2} must be added.
          */
         public Builder() {
-            mBundle = new Bundle();
+            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this);
         }
 
         /**
@@ -599,31 +685,15 @@
          *
          * @param source
          */
-        public Builder(MediaMetadata2 source) {
-            mBundle = new Bundle(source.mBundle);
+        public Builder(@NonNull MediaMetadata2 source) {
+            mProvider = ApiLoader.getProvider().createMediaMetadata2Builder(this, source);
         }
 
         /**
-         * Create a Builder using a {@link MediaMetadata2} instance to set
-         * initial values, but replace bitmaps with a scaled down copy if they
-         * are larger than maxBitmapSize.
-         *
-         * @param source The original metadata to copy.
-         * @param maxBitmapSize The maximum height/width for bitmaps contained
-         *            in the metadata.
          * @hide
          */
-        public Builder(MediaMetadata2 source, int maxBitmapSize) {
-            this(source);
-            for (String key : mBundle.keySet()) {
-                Object value = mBundle.get(key);
-                if (value instanceof Bitmap) {
-                    Bitmap bmp = (Bitmap) value;
-                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
-                        putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
-                    }
-                }
-            }
+        public Builder(@NonNull MediaMetadata2Provider.BuilderProvider provider) {
+            mProvider = provider;
         }
 
         /**
@@ -652,15 +722,9 @@
          * @param value The CharSequence value to store
          * @return The Builder to allow chaining
          */
-        public Builder putText(@TextKey String key, CharSequence value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a CharSequence");
-                }
-            }
-            mBundle.putCharSequence(key, value);
-            return this;
+        public @NonNull Builder putText(@NonNull @TextKey String key,
+                @Nullable CharSequence value) {
+            return mProvider.putText_impl(key, value);
         }
 
         /**
@@ -689,15 +753,9 @@
          * @param value The String value to store
          * @return The Builder to allow chaining
          */
-        public Builder putString(@TextKey String key, String value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a String");
-                }
-            }
-            mBundle.putCharSequence(key, value);
-            return this;
+        public @NonNull Builder putString(@NonNull @TextKey String key,
+                @Nullable String value) {
+            return mProvider.putString_impl(key, value);
         }
 
         /**
@@ -719,15 +777,8 @@
          * @param value The String value to store
          * @return The Builder to allow chaining
          */
-        public Builder putLong(@LongKey String key, long value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a long");
-                }
-            }
-            mBundle.putLong(key, value);
-            return this;
+        public @NonNull Builder putLong(@NonNull @LongKey String key, long value) {
+            return mProvider.putLong_impl(key, value);
         }
 
         /**
@@ -743,16 +794,8 @@
          * @param value The String value to store
          * @return The Builder to allow chaining
          */
-        public Builder putRating(@RatingKey String key, Rating2 value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a Rating");
-                }
-            }
-            mBundle.putBundle(key, value.toBundle());
-
-            return this;
+        public @NonNull Builder putRating(@NonNull @RatingKey String key, @Nullable Rating2 value) {
+            return mProvider.putRating_impl(key, value);
         }
 
         /**
@@ -773,23 +816,29 @@
          * @param value The Bitmap to store
          * @return The Builder to allow chaining
          */
-        public Builder putBitmap(@BitmapKey String key, Bitmap value) {
-            if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
-                    throw new IllegalArgumentException("The " + key
-                            + " key cannot be used to put a Bitmap");
-                }
-            }
-            mBundle.putParcelable(key, value);
-            return this;
+        public @NonNull Builder putBitmap(@NonNull @BitmapKey String key, @Nullable Bitmap value) {
+            return mProvider.putBitmap_impl(key, value);
         }
 
         /**
-         * Set an extra {@link Bundle} into the metadata.
+         * Put a float value into the metadata. Custom keys may be used.
+         *
+         * @param key The key for referencing this value
+         * @param value The float value to store
+         * @return The Builder to allow chaining
          */
-        public Builder setExtra(Bundle bundle) {
-            mBundle.putBundle(METADATA_KEY_EXTRA, bundle);
-            return this;
+        public @NonNull Builder putFloat(@NonNull @LongKey String key, float value) {
+            return mProvider.putFloat_impl(key, value);
+        }
+
+        /**
+         * Set a bundle of extras.
+         *
+         * @param extras The extras to include with this description or null.
+         * @return The Builder to allow chaining
+         */
+        public Builder setExtras(@Nullable Bundle extras) {
+            return mProvider.setExtras_impl(extras);
         }
 
         /**
@@ -797,18 +846,8 @@
          *
          * @return The new MediaMetadata2 instance
          */
-        public MediaMetadata2 build() {
-            return new MediaMetadata2(mBundle);
-        }
-
-        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
-            float maxSizeF = maxSize;
-            float widthScale = maxSizeF / bmp.getWidth();
-            float heightScale = maxSizeF / bmp.getHeight();
-            float scale = Math.min(widthScale, heightScale);
-            int height = (int) (bmp.getHeight() * scale);
-            int width = (int) (bmp.getWidth() * scale);
-            return Bitmap.createScaledBitmap(bmp, width, height, true);
+        public @NonNull MediaMetadata2 build() {
+            return mProvider.build_impl();
         }
     }
 }
diff --git a/android/media/MediaMetadataRetriever.java b/android/media/MediaMetadataRetriever.java
index 745eb74..0955dd6 100644
--- a/android/media/MediaMetadataRetriever.java
+++ b/android/media/MediaMetadataRetriever.java
@@ -17,6 +17,8 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -30,7 +32,7 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -367,10 +369,83 @@
 
     private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
 
+    public static final class BitmapParams {
+        private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
+        private Bitmap.Config outActualConfig = Bitmap.Config.ARGB_8888;
+
+        /**
+         * Create a default BitmapParams object. By default, it uses {@link Bitmap.Config#ARGB_8888}
+         * as the preferred bitmap config.
+         */
+        public BitmapParams() {}
+
+        /**
+         * Set the preferred bitmap config for the decoder to decode into.
+         *
+         * If not set, or the request cannot be met, the decoder will output
+         * in {@link Bitmap.Config#ARGB_8888} config by default.
+         *
+         * After decode, the actual config used can be retrieved by {@link #getActualConfig()}.
+         *
+         * @param config the preferred bitmap config to use.
+         */
+        public void setPreferredConfig(@NonNull Bitmap.Config config) {
+            if (config == null) {
+                throw new IllegalArgumentException("preferred config can't be null");
+            }
+            inPreferredConfig = config;
+        }
+
+        /**
+         * Retrieve the preferred bitmap config in the params.
+         *
+         * @return the preferred bitmap config.
+         */
+        public @NonNull Bitmap.Config getPreferredConfig() {
+            return inPreferredConfig;
+        }
+
+        /**
+         * Get the actual bitmap config used to decode the bitmap after the decoding.
+         *
+         * @return the actual bitmap config used.
+         */
+        public @NonNull Bitmap.Config getActualConfig() {
+            return outActualConfig;
+        }
+    }
+
     /**
      * This method retrieves a video frame by its index. It should only be called
      * after {@link #setDataSource}.
      *
+     * After the bitmap is returned, you can query the actual parameters that were
+     * used to create the bitmap from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
+     *
+     * @param frameIndex 0-based index of the video frame. The frame index must be that of
+     *        a valid frame. The total number of frames available for retrieval can be queried
+     *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *
+     * @throws IllegalStateException if the container doesn't contain video or image sequences.
+     * @throws IllegalArgumentException if the requested frame index does not exist.
+     *
+     * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
+     *
+     * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
+     * @see #getFramesAtIndex(int, int)
+     */
+    public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) {
+        List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params);
+        return bitmaps.get(0);
+    }
+
+    /**
+     * This method is similar to {@link #getFrameAtIndex(int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
      * @param frameIndex 0-based index of the video frame. The frame index must be that of
      *        a valid frame. The total number of frames available for retrieval can be queried
      *        via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
@@ -380,14 +455,13 @@
      *
      * @return A Bitmap containing the requested video frame, or null if the retrieval fails.
      *
+     * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
      * @see #getFramesAtIndex(int, int)
      */
     public Bitmap getFrameAtIndex(int frameIndex) {
-        Bitmap[] bitmaps = getFramesAtIndex(frameIndex, 1);
-        if (bitmaps == null || bitmaps.length < 1) {
-            return null;
-        }
-        return bitmaps[0];
+        List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1);
+        return bitmaps.get(0);
     }
 
     /**
@@ -395,7 +469,38 @@
      * specified index. It should only be called after {@link #setDataSource}.
      *
      * If the caller intends to retrieve more than one consecutive video frames,
-     * this method is preferred over {@link #getFrameAtIndex(int)} for efficiency.
+     * this method is preferred over {@link #getFrameAtIndex(int, BitmapParams)} for efficiency.
+     *
+     * After the bitmaps are returned, you can query the actual parameters that were
+     * used to create the bitmaps from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmaps with {@link BitmapParams#getActualConfig}.
+     *
+     * @param frameIndex 0-based index of the first video frame to retrieve. The frame index
+     *        must be that of a valid frame. The total number of frames available for retrieval
+     *        can be queried via the {@link #METADATA_KEY_VIDEO_FRAME_COUNT} key.
+     * @param numFrames number of consecutive video frames to retrieve. Must be a positive
+     *        value. The stream must contain at least numFrames frames starting at frameIndex.
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *
+     * @throws IllegalStateException if the container doesn't contain video or image sequences.
+     * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
+     *         stream doesn't contain at least numFrames starting at frameIndex.
+
+     * @return An list of Bitmaps containing the requested video frames. The returned
+     *         array could contain less frames than requested if the retrieval fails.
+     *
+     * @see #getFrameAtIndex(int, BitmapParams)
+     * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int)
+     */
+    public @NonNull List<Bitmap> getFramesAtIndex(
+            int frameIndex, int numFrames, @NonNull BitmapParams params) {
+        return getFramesAtIndexInternal(frameIndex, numFrames, params);
+    }
+
+    /**
+     * This method is similar to {@link #getFramesAtIndex(int, int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
      *
      * @param frameIndex 0-based index of the first video frame to retrieve. The frame index
      *        must be that of a valid frame. The total number of frames available for retrieval
@@ -407,12 +512,19 @@
      * @throws IllegalArgumentException if the frameIndex or numFrames is invalid, or the
      *         stream doesn't contain at least numFrames starting at frameIndex.
 
-     * @return An array of Bitmaps containing the requested video frames. The returned
+     * @return An list of Bitmaps containing the requested video frames. The returned
      *         array could contain less frames than requested if the retrieval fails.
      *
+     * @see #getFrameAtIndex(int, BitmapParams)
      * @see #getFrameAtIndex(int)
+     * @see #getFramesAtIndex(int, int, BitmapParams)
      */
-    public Bitmap[] getFramesAtIndex(int frameIndex, int numFrames) {
+    public @NonNull List<Bitmap> getFramesAtIndex(int frameIndex, int numFrames) {
+        return getFramesAtIndexInternal(frameIndex, numFrames, null);
+    }
+
+    private @NonNull List<Bitmap> getFramesAtIndexInternal(
+            int frameIndex, int numFrames, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
             throw new IllegalStateException("Does not contail video or image sequences");
         }
@@ -424,24 +536,94 @@
             throw new IllegalArgumentException("Invalid frameIndex or numFrames: "
                 + frameIndex + ", " + numFrames);
         }
-        return _getFrameAtIndex(frameIndex, numFrames);
+        return _getFrameAtIndex(frameIndex, numFrames, params);
     }
-    private native Bitmap[] _getFrameAtIndex(int frameIndex, int numFrames);
+
+    private native @NonNull List<Bitmap> _getFrameAtIndex(
+            int frameIndex, int numFrames, @Nullable BitmapParams params);
 
     /**
      * This method retrieves a still image by its index. It should only be called
      * after {@link #setDataSource}.
      *
-     * @param imageIndex 0-based index of the image, with negative value indicating
-     *        the primary image.
+     * After the bitmap is returned, you can query the actual parameters that were
+     * used to create the bitmap from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
+     *
+     * @param imageIndex 0-based index of the image.
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *
      * @throws IllegalStateException if the container doesn't contain still images.
      * @throws IllegalArgumentException if the requested image does not exist.
      *
      * @return the requested still image, or null if the image cannot be retrieved.
      *
-     * @see #getPrimaryImage
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage(BitmapParams)
+     * @see #getPrimaryImage()
+     */
+    public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) {
+        return getImageAtIndexInternal(imageIndex, params);
+    }
+
+    /**
+     * This method is similar to {@link #getImageAtIndex(int, BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @param imageIndex 0-based index of the image.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     * @throws IllegalArgumentException if the requested image does not exist.
+     *
+     * @return the requested still image, or null if the image cannot be retrieved.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getPrimaryImage(BitmapParams)
+     * @see #getPrimaryImage()
      */
     public Bitmap getImageAtIndex(int imageIndex) {
+        return getImageAtIndexInternal(imageIndex, null);
+    }
+
+    /**
+     * This method retrieves the primary image of the media content. It should only
+     * be called after {@link #setDataSource}.
+     *
+     * After the bitmap is returned, you can query the actual parameters that were
+     * used to create the bitmap from the {@code BitmapParams} argument, for instance
+     * to query the bitmap config used for the bitmap with {@link BitmapParams#getActualConfig}.
+     *
+     * @param params BitmapParams that controls the returned bitmap config (such as pixel formats).
+     *
+     * @return the primary image, or null if it cannot be retrieved.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage()
+     */
+    public Bitmap getPrimaryImage(@NonNull BitmapParams params) {
+        return getImageAtIndexInternal(-1, params);
+    }
+
+    /**
+     * This method is similar to {@link #getPrimaryImage(BitmapParams)} except that
+     * the default for {@link BitmapParams} will be used.
+     *
+     * @return the primary image, or null if it cannot be retrieved.
+     *
+     * @throws IllegalStateException if the container doesn't contain still images.
+     *
+     * @see #getImageAtIndex(int, BitmapParams)
+     * @see #getImageAtIndex(int)
+     * @see #getPrimaryImage(BitmapParams)
+     */
+    public Bitmap getPrimaryImage() {
+        return getImageAtIndexInternal(-1, null);
+    }
+
+    private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
             throw new IllegalStateException("Does not contail still images");
         }
@@ -451,24 +633,10 @@
             throw new IllegalArgumentException("Invalid image index: " + imageCount);
         }
 
-        return _getImageAtIndex(imageIndex);
+        return _getImageAtIndex(imageIndex, params);
     }
 
-    /**
-     * This method retrieves the primary image of the media content. It should only
-     * be called after {@link #setDataSource}.
-     *
-     * @return the primary image, or null if it cannot be retrieved.
-     *
-     * @throws IllegalStateException if the container doesn't contain still images.
-     *
-     * @see #getImageAtIndex(int)
-     */
-    public Bitmap getPrimaryImage() {
-        return getImageAtIndex(-1);
-    }
-
-    private native Bitmap _getImageAtIndex(int imageIndex);
+    private native Bitmap _getImageAtIndex(int imageIndex, @Nullable BitmapParams params);
 
     /**
      * Call this method after setDataSource(). This method finds the optional
@@ -712,7 +880,8 @@
     public static final int METADATA_KEY_IMAGE_HEIGHT    = 30;
     /**
      * If the media contains still images, this key retrieves the rotation
-     * of the primary image.
+     * angle (in degrees clockwise) of the primary image. The image rotation
+     * angle must be one of 0, 90, 180, or 270 degrees.
      */
     public static final int METADATA_KEY_IMAGE_ROTATION  = 31;
     /**
diff --git a/android/media/MediaMuxer.java b/android/media/MediaMuxer.java
index 02c71b2..205ce8d 100644
--- a/android/media/MediaMuxer.java
+++ b/android/media/MediaMuxer.java
@@ -328,6 +328,7 @@
         RandomAccessFile file = null;
         try {
             file = new RandomAccessFile(path, "rws");
+            file.setLength(0);
             FileDescriptor fd = file.getFD();
             setUpMediaMuxer(fd, format);
         } finally {
diff --git a/android/media/MediaPlayer.java b/android/media/MediaPlayer.java
index 1bc3dfa..aef31b1 100644
--- a/android/media/MediaPlayer.java
+++ b/android/media/MediaPlayer.java
@@ -1484,6 +1484,7 @@
     /*
      * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void enableNativeRoutingCallbacksLocked(boolean enabled) {
         if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback(enabled);
@@ -2415,7 +2416,7 @@
          * Gets the track type.
          * @return TrackType which indicates if the track is video, audio, timed text.
          */
-        public int getTrackType() {
+        public @TrackType int getTrackType() {
             return mTrackType;
         }
 
@@ -2449,6 +2450,19 @@
         public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
         public static final int MEDIA_TRACK_TYPE_METADATA = 5;
 
+        /** @hide */
+        @IntDef(flag = false, prefix = "MEDIA_TRACK_TYPE", value = {
+                MEDIA_TRACK_TYPE_UNKNOWN,
+                MEDIA_TRACK_TYPE_VIDEO,
+                MEDIA_TRACK_TYPE_AUDIO,
+                MEDIA_TRACK_TYPE_TIMEDTEXT,
+                MEDIA_TRACK_TYPE_SUBTITLE,
+                MEDIA_TRACK_TYPE_METADATA }
+        )
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface TrackType {}
+
+
         final int mTrackType;
         final MediaFormat mFormat;
 
@@ -2599,26 +2613,30 @@
      */
     /**
      * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
+     * @deprecated use {@link MediaFormat#MIMETYPE_TEXT_SUBRIP}
      */
-    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = MediaFormat.MIMETYPE_TEXT_SUBRIP;
 
     /**
      * MIME type for WebVTT subtitle data.
      * @hide
+     * @deprecated
      */
-    public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
+    public static final String MEDIA_MIMETYPE_TEXT_VTT = MediaFormat.MIMETYPE_TEXT_VTT;
 
     /**
      * MIME type for CEA-608 closed caption data.
      * @hide
+     * @deprecated
      */
-    public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+    public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = MediaFormat.MIMETYPE_TEXT_CEA_608;
 
     /**
      * MIME type for CEA-708 closed caption data.
      * @hide
+     * @deprecated
      */
-    public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708";
+    public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = MediaFormat.MIMETYPE_TEXT_CEA_708;
 
     /*
      * A helper function to check if the mime type is supported by media framework.
@@ -3107,7 +3125,7 @@
      * this function is called.
      * </p>
      * <p>
-     * Currently, only timed text tracks or audio tracks can be selected via this method.
+     * Currently, only timed text, subtitle or audio tracks can be selected via this method.
      * In addition, the support for selecting an audio track at runtime is pretty limited
      * in that an audio track can only be selected in the <em>Prepared</em> state.
      * </p>
@@ -3794,29 +3812,158 @@
     private OnTimedTextListener mOnTimedTextListener;
 
     /**
-     * Interface definition of a callback to be invoked when a
-     * track has data available.
-     *
-     * @hide
+     * Interface definition of a callback to be invoked when a player subtitle track has new
+     * subtitle data available.
+     * See the {@link MediaPlayer#setOnSubtitleDataListener(OnSubtitleDataListener, Handler)}
+     * method for the description of which track will report data through this listener.
      */
-    public interface OnSubtitleDataListener
-    {
-        public void onSubtitleData(MediaPlayer mp, SubtitleData data);
+    public interface OnSubtitleDataListener {
+        /**
+         * Method called when new subtitle data is available
+         * @param mp the player that reports the new subtitle data
+         * @param data the subtitle data
+         */
+        public void onSubtitleData(@NonNull MediaPlayer mp, @NonNull SubtitleData data);
     }
 
     /**
-     * Register a callback to be invoked when a track has data available.
-     *
-     * @param listener the callback that will be run
-     *
-     * @hide
+     * Sets the listener to be invoked when a subtitle track has new data available.
+     * The subtitle data comes from a subtitle track previously selected with
+     * {@link #selectTrack(int)}. Use {@link #getTrackInfo()} to determine which tracks are
+     * subtitles (of type {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}), Subtitle track encodings
+     * can be determined by {@link TrackInfo#getFormat()}).<br>
+     * See {@link SubtitleData} for an example of querying subtitle encoding.
+     * @param listener the listener called when new data is available
+     * @param handler the {@link Handler} that receives the listener events
      */
-    public void setOnSubtitleDataListener(OnSubtitleDataListener listener)
+    public void setOnSubtitleDataListener(@NonNull OnSubtitleDataListener listener,
+            @NonNull Handler handler) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("Illegal null handler");
+        }
+        setOnSubtitleDataListenerInt(listener, handler);
+    }
+    /**
+     * Sets the listener to be invoked when a subtitle track has new data available.
+     * The subtitle data comes from a subtitle track previously selected with
+     * {@link #selectTrack(int)}. Use {@link #getTrackInfo()} to determine which tracks are
+     * subtitles (of type {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}), Subtitle track encodings
+     * can be determined by {@link TrackInfo#getFormat()}).<br>
+     * See {@link SubtitleData} for an example of querying subtitle encoding.<br>
+     * The listener will be called on the same thread as the one in which the MediaPlayer was
+     * created.
+     * @param listener the listener called when new data is available
+     */
+    public void setOnSubtitleDataListener(@NonNull OnSubtitleDataListener listener)
     {
-        mOnSubtitleDataListener = listener;
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        setOnSubtitleDataListenerInt(listener, null);
+    }
+
+    /**
+     * Clears the listener previously set with
+     * {@link #setOnSubtitleDataListener(OnSubtitleDataListener)} or
+     * {@link #setOnSubtitleDataListener(OnSubtitleDataListener, Handler)}.
+     */
+    public void clearOnSubtitleDataListener() {
+        setOnSubtitleDataListenerInt(null, null);
+    }
+
+    private void setOnSubtitleDataListenerInt(
+            @Nullable OnSubtitleDataListener listener, @Nullable Handler handler) {
+        synchronized (this) {
+            mOnSubtitleDataListener = listener;
+            mOnSubtitleDataHandler = handler;
+        }
     }
 
     private OnSubtitleDataListener mOnSubtitleDataListener;
+    private Handler mOnSubtitleDataHandler;
+
+    /**
+     * Interface definition of a callback to be invoked when discontinuity in the normal progression
+     * of the media time is detected.
+     * The "normal progression" of media time is defined as the expected increase of the playback
+     * position when playing media, relative to the playback speed (for instance every second, media
+     * time increases by two seconds when playing at 2x).<br>
+     * Discontinuities are encountered in the following cases:
+     * <ul>
+     * <li>when the player is starved for data and cannot play anymore</li>
+     * <li>when the player encounters a playback error</li>
+     * <li>when the a seek operation starts, and when it's completed</li>
+     * <li>when the playback speed changes</li>
+     * <li>when the playback state changes</li>
+     * <li>when the player is reset</li>
+     * </ul>
+     * See the
+     * {@link MediaPlayer#setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener, Handler)}
+     * method to set a listener for these events.
+     */
+    public interface OnMediaTimeDiscontinuityListener {
+        /**
+         * Called to indicate a time discontinuity has occured.
+         * @param mp the MediaPlayer for which the discontinuity has occured.
+         * @param mts the timestamp that correlates media time, system time and clock rate,
+         *     or {@link MediaTimestamp#TIMESTAMP_UNKNOWN} in an error case.
+         */
+        public void onMediaTimeDiscontinuity(@NonNull MediaPlayer mp, @NonNull MediaTimestamp mts);
+    }
+
+    /**
+     * Sets the listener to be invoked when a media time discontinuity is encountered.
+     * @param listener the listener called after a discontinuity
+     * @param handler the {@link Handler} that receives the listener events
+     */
+    public void setOnMediaTimeDiscontinuityListener(
+            @NonNull OnMediaTimeDiscontinuityListener listener, @NonNull Handler handler) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("Illegal null handler");
+        }
+        setOnMediaTimeDiscontinuityListenerInt(listener, handler);
+    }
+
+    /**
+     * Sets the listener to be invoked when a media time discontinuity is encountered.
+     * The listener will be called on the same thread as the one in which the MediaPlayer was
+     * created.
+     * @param listener the listener called after a discontinuity
+     */
+    public void setOnMediaTimeDiscontinuityListener(
+            @NonNull OnMediaTimeDiscontinuityListener listener)
+    {
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        setOnMediaTimeDiscontinuityListenerInt(listener, null);
+    }
+
+    /**
+     * Clears the listener previously set with
+     * {@link #setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener)}
+     * or {@link #setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener, Handler)}
+     */
+    public void clearOnMediaTimeDiscontinuityListener() {
+        setOnMediaTimeDiscontinuityListenerInt(null, null);
+    }
+
+    private void setOnMediaTimeDiscontinuityListenerInt(
+            @Nullable OnMediaTimeDiscontinuityListener listener, @Nullable Handler handler) {
+        synchronized (this) {
+            mOnMediaTimeDiscontinuityListener = listener;
+            mOnMediaTimeDiscontinuityHandler = handler;
+        }
+    }
+
+    private OnMediaTimeDiscontinuityListener mOnMediaTimeDiscontinuityListener;
+    private Handler mOnMediaTimeDiscontinuityHandler;
 
     /**
      * Interface definition of a callback to be invoked when a
@@ -3952,8 +4099,8 @@
 
     /** The player was started because it was used as the next player for another
      * player, which just completed playback.
+     * @see android.media.MediaPlayer#setNextMediaPlayer(MediaPlayer)
      * @see android.media.MediaPlayer.OnInfoListener
-     * @hide
      */
     public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
 
diff --git a/android/media/MediaPlayer2.java b/android/media/MediaPlayer2.java
index d36df84..dcc872c 100644
--- a/android/media/MediaPlayer2.java
+++ b/android/media/MediaPlayer2.java
@@ -28,32 +28,22 @@
 import android.os.PersistableBundle;
 import android.view.Surface;
 import android.view.SurfaceHolder;
-import android.media.MediaDrm;
-import android.media.MediaFormat;
-import android.media.MediaPlayer2Impl;
-import android.media.MediaTimeProvider;
-import android.media.PlaybackParams;
-import android.media.SubtitleController;
-import android.media.SubtitleController.Anchor;
-import android.media.SubtitleData;
-import android.media.SubtitleTrack.RenderingWidget;
-import android.media.SyncParams;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.AutoCloseable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.net.InetSocketAddress;
-import java.util.concurrent.Executor;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.Executor;
 
 
 /**
+ * @hide
  * MediaPlayer2 class can be used to control playback
  * of audio/video files and streams. An example on how to use the methods in
  * this class can be found in {@link android.widget.VideoView}.
@@ -91,27 +81,18 @@
  * <p>From this state diagram, one can see that a MediaPlayer2 object has the
  *    following states:</p>
  * <ul>
- *     <li>When a MediaPlayer2 object is just created using <code>new</code> or
+ *     <li>When a MediaPlayer2 object is just created using <code>create</code> or
  *         after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
  *         {@link #close()} is called, it is in the <em>End</em> state. Between these
  *         two states is the life cycle of the MediaPlayer2 object.
  *         <ul>
- *         <li>There is a subtle but important difference between a newly constructed
- *         MediaPlayer2 object and the MediaPlayer2 object after {@link #reset()}
- *         is called. It is a programming error to invoke methods such
+ *         <li> It is a programming error to invoke methods such
  *         as {@link #getCurrentPosition()},
  *         {@link #getDuration()}, {@link #getVideoHeight()},
  *         {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()},
+ *         {@link #setPlayerVolume(float)}, {@link #pause()}, {@link #play()},
  *         {@link #seekTo(long, int)} or
- *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
- *         methods is called right after a MediaPlayer2 object is constructed,
- *         the user supplied callback method OnErrorListener.onError() won't be
- *         called by the internal player engine and the object state remains
- *         unchanged; but if these methods are called right after {@link #reset()},
- *         the user supplied callback method OnErrorListener.onError() will be
- *         invoked by the internal player engine and the object will be
- *         transfered to the <em>Error</em> state. </li>
+ *         {@link #prepare()} in the <em>Idle</em> state.
  *         <li>It is also recommended that once
  *         a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
  *         so that resources used by the internal player engine associated with the
@@ -135,9 +116,9 @@
  *         these circumstances. Sometimes, due to programming errors, invoking a playback
  *         control operation in an invalid state may also occur. Under all these
  *         error conditions, the internal player engine invokes a user supplied
- *         EventCallback.onError() method if an EventCallback has been
+ *         MediaPlayer2EventCallback.onError() method if an MediaPlayer2EventCallback has been
  *         registered beforehand via
- *         {@link #registerEventCallback(Executor, EventCallback)}.
+ *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
  *         <ul>
  *         <li>It is important to note that once an error occurs, the
  *         MediaPlayer2 object enters the <em>Error</em> state (except as noted
@@ -151,43 +132,42 @@
  *         the internal player engine.</li>
  *         <li>IllegalStateException is
  *         thrown to prevent programming errors such as calling
- *         {@link #prepareAsync()}, {@link #setDataSource(DataSourceDesc)}, or
- *         {@code setPlaylist} methods in an invalid state. </li>
+ *         {@link #prepare()}, {@link #setDataSource(DataSourceDesc)}
+ *         methods in an invalid state. </li>
  *         </ul>
  *         </li>
  *     <li>Calling
- *         {@link #setDataSource(DataSourceDesc)}, or
- *         {@code setPlaylist} transfers a
+ *         {@link #setDataSource(DataSourceDesc)} transfers a
  *         MediaPlayer2 object in the <em>Idle</em> state to the
  *         <em>Initialized</em> state.
  *         <ul>
  *         <li>An IllegalStateException is thrown if
- *         setDataSource() or setPlaylist() is called in any other state.</li>
+ *         setDataSource() is called in any other state.</li>
  *         <li>It is good programming
  *         practice to always look out for <code>IllegalArgumentException</code>
  *         and <code>IOException</code> that may be thrown from
- *         <code>setDataSource</code> and <code>setPlaylist</code> methods.</li>
+ *         <code>setDataSource</code>.</li>
  *         </ul>
  *         </li>
  *     <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
  *         before playback can be started.
  *         <ul>
  *         <li>There are an asynchronous way that the <em>Prepared</em> state can be reached:
- *         a call to {@link #prepareAsync()} (asynchronous) which
+ *         a call to {@link #prepare()} (asynchronous) which
  *         first transfers the object to the <em>Preparing</em> state after the
  *         call returns (which occurs almost right way) while the internal
  *         player engine continues working on the rest of preparation work
  *         until the preparation work completes. When the preparation completes,
  *         the internal player engine then calls a user supplied callback method,
- *         onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED}, if an
- *         EventCallback is registered beforehand via
- *         {@link #registerEventCallback(Executor, EventCallback)}.</li>
+ *         onInfo() of the MediaPlayer2EventCallback interface with {@link #MEDIA_INFO_PREPARED},
+ *         if an MediaPlayer2EventCallback is registered beforehand via
+ *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
  *         <li>It is important to note that
  *         the <em>Preparing</em> state is a transient state, and the behavior
  *         of calling any method with side effect while a MediaPlayer2 object is
  *         in the <em>Preparing</em> state is undefined.</li>
  *         <li>An IllegalStateException is
- *         thrown if {@link #prepareAsync()} is called in
+ *         thrown if {@link #prepare()} is called in
  *         any other state.</li>
  *         <li>While in the <em>Prepared</em> state, properties
  *         such as audio/sound volume, screenOnWhilePlaying, looping can be
@@ -196,13 +176,14 @@
  *         </li>
  *     <li>To start the playback, {@link #play()} must be called. After
  *         {@link #play()} returns successfully, the MediaPlayer2 object is in the
- *         <em>Started</em> state. {@link #isPlaying()} can be called to test
+ *         <em>Started</em> state. {@link #getPlayerState()} can be called to test
  *         whether the MediaPlayer2 object is in the <em>Started</em> state.
  *         <ul>
  *         <li>While in the <em>Started</em> state, the internal player engine calls
- *         a user supplied EventCallback.onBufferingUpdate() callback
- *         method if an EventCallback has been registered beforehand
- *         via {@link #registerEventCallback(Executor, EventCallback)}.
+ *         a user supplied callback method MediaPlayer2EventCallback.onInfo() with
+ *         {@link #MEDIA_INFO_BUFFERING_UPDATE} if an MediaPlayer2EventCallback has been
+ *         registered beforehand via
+ *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
  *         This callback allows applications to keep track of the buffering status
  *         while streaming audio/video.</li>
  *         <li>Calling {@link #play()} has not effect
@@ -215,7 +196,7 @@
  *         <em>Paused</em> state. Note that the transition from the <em>Started</em>
  *         state to the <em>Paused</em> state and vice versa happens
  *         asynchronously in the player engine. It may take some time before
- *         the state is updated in calls to {@link #isPlaying()}, and it can be
+ *         the state is updated in calls to {@link #getPlayerState()}, and it can be
  *         a number of seconds in the case of streamed content.
  *         <ul>
  *         <li>Calling {@link #play()} to resume playback for a paused
@@ -234,9 +215,10 @@
  *         call returns right away, the actual seek operation may take a while to
  *         finish, especially for audio/video being streamed. When the actual
  *         seek operation completes, the internal player engine calls a user
- *         supplied EventCallback.onInfo() with {@link #MEDIA_INFO_COMPLETE_CALL_SEEK}
- *         if an EventCallback has been registered beforehand via
- *         {@link #registerEventCallback(Executor, EventCallback)}.</li>
+ *         supplied MediaPlayer2EventCallback.onCallCompleted() with
+ *         {@link #CALL_COMPLETED_SEEK_TO}
+ *         if an MediaPlayer2EventCallback has been registered beforehand via
+ *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.</li>
  *         <li>Please
  *         note that {@link #seekTo(long, int)} can also be called in the other states,
  *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
@@ -252,15 +234,13 @@
  *         </li>
  *     <li>When the playback reaches the end of stream, the playback completes.
  *         <ul>
- *         <li>If the looping mode was being set to one of the values of
- *         {@link #LOOPING_MODE_FULL}, {@link #LOOPING_MODE_SINGLE} or
- *         {@link #LOOPING_MODE_SHUFFLE} with
- *         {@link #setLoopingMode(int)}, the MediaPlayer2 object shall remain in
- *         the <em>Started</em> state.</li>
+ *         <li>If current source is set to loop by {@link #loopCurrent(boolean)},
+ *         the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
  *         <li>If the looping mode was set to <var>false
  *         </var>, the player engine calls a user supplied callback method,
- *         EventCallback.onCompletion(), if an EventCallback is registered
- *         beforehand via {@link #registerEventCallback(Executor, EventCallback)}.
+ *         MediaPlayer2EventCallback.onCompletion(), if an MediaPlayer2EventCallback is
+ *         registered beforehand via
+ *         {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)}.
  *         The invoke of the callback signals that the object is now in the <em>
  *         PlaybackCompleted</em> state.</li>
  *         <li>While in the <em>PlaybackCompleted</em>
@@ -280,7 +260,7 @@
  * <tr><td>attachAuxEffect </p></td>
  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
  *     <td>{Idle, Error} </p></td>
- *     <td>This method must be called after setDataSource or setPlaylist.
+ *     <td>This method must be called after setDataSource.
  *     Calling it does not change the object state. </p></td></tr>
  * <tr><td>getAudioSessionId </p></td>
  *     <td>any </p></td>
@@ -314,7 +294,7 @@
  *     <td>Successful invoke of this method in a valid state does not change
  *         the state. Calling this method in an invalid state transfers the
  *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>isPlaying </p></td>
+ * <tr><td>getPlayerState </p></td>
  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
  *          PlaybackCompleted}</p></td>
  *     <td>{Error}</p></td>
@@ -327,7 +307,7 @@
  *     <td>Successful invoke of this method in a valid state transfers the
  *         object to the <em>Paused</em> state. Calling this method in an
  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepareAsync </p></td>
+ * <tr><td>prepare </p></td>
  *     <td>{Initialized, Stopped} </p></td>
  *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
  *     <td>Successful invoke of this method in a valid state transfers the
@@ -354,13 +334,13 @@
  *     <td>{Error}</p></td>
  *     <td>Successful invoke of this method does not change the state. In order for the
  *         target audio attributes type to become effective, this method must be called before
- *         prepareAsync().</p></td></tr>
+ *         prepare().</p></td></tr>
  * <tr><td>setAudioSessionId </p></td>
  *     <td>{Idle} </p></td>
  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
  *          Error} </p></td>
  *     <td>This method must be called in idle state as the audio session ID must be known before
- *         calling setDataSource or setPlaylist. Calling it does not change the object
+ *         calling setDataSource. Calling it does not change the object
  *         state. </p></td></tr>
  * <tr><td>setAudioStreamType (deprecated)</p></td>
  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
@@ -368,7 +348,7 @@
  *     <td>{Error}</p></td>
  *     <td>Successful invoke of this method does not change the state. In order for the
  *         target audio stream type to become effective, this method must be called before
- *         prepareAsync().</p></td></tr>
+ *         prepare().</p></td></tr>
  * <tr><td>setAuxEffectSendLevel </p></td>
  *     <td>any</p></td>
  *     <td>{} </p></td>
@@ -380,13 +360,6 @@
  *     <td>Successful invoke of this method in a valid state transfers the
  *         object to the <em>Initialized</em> state. Calling this method in an
  *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setPlaylist </p></td>
- *     <td>{Idle} </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- *          Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Initialized</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
  * <tr><td>setDisplay </p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
@@ -397,7 +370,7 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>setLoopingMode </p></td>
+ * <tr><td>loopCurrent </p></td>
  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
  *         PlaybackCompleted}</p></td>
  *     <td>{Error}</p></td>
@@ -409,12 +382,12 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>registerDrmEventCallback </p></td>
+ * <tr><td>setDrmEventCallback </p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>registerEventCallback </p></td>
+ * <tr><td>setMediaPlayer2EventCallback </p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
@@ -424,7 +397,7 @@
  *     <td>{Idle, Stopped} </p></td>
  *     <td>This method will change state in some cases, depending on when it's called.
  *         </p></td></tr>
- * <tr><td>setVolume </p></td>
+ * <tr><td>setPlayerVolume </p></td>
  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
  *          PlaybackCompleted}</p></td>
  *     <td>{Error}</p></td>
@@ -472,51 +445,17 @@
  * possible runtime errors during playback or streaming. Registration for
  * these events is done by properly setting the appropriate listeners (via calls
  * to
- * {@link #registerEventCallback(Executor, EventCallback)},
- * {@link #registerDrmEventCallback(Executor, DrmEventCallback)}).
+ * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)},
+ * {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
  * In order to receive the respective callback
  * associated with these listeners, applications are required to create
  * MediaPlayer2 objects on a thread with its own Looper running (main UI
  * thread by default has a Looper running).
  *
  */
-public abstract class MediaPlayer2 implements SubtitleController.Listener
-                                            , AudioRouting
-                                            , AutoCloseable
-{
-    /**
-       Constant to retrieve only the new metadata since the last
-       call.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean METADATA_UPDATE_ONLY = true;
-
-    /**
-       Constant to retrieve all the metadata.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean METADATA_ALL = false;
-
-    /**
-       Constant to enable the metadata filter during retrieval.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean APPLY_METADATA_FILTER = true;
-
-    /**
-       Constant to disable the metadata filter during retrieval.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean BYPASS_METADATA_FILTER = false;
-
+public abstract class MediaPlayer2 extends MediaPlayerBase
+                                   implements SubtitleController.Listener
+                                            , AudioRouting {
     /**
      * Create a MediaPlayer2 object.
      *
@@ -527,6 +466,47 @@
         return new MediaPlayer2Impl();
     }
 
+    private static final String[] decodeMediaPlayer2Uri(String location) {
+        Uri uri = Uri.parse(location);
+        if (!"mediaplayer2".equals(uri.getScheme())) {
+            return new String[] {location};
+        }
+
+        List<String> uris = uri.getQueryParameters("uri");
+        if (uris.isEmpty()) {
+            return new String[] {location};
+        }
+
+        List<String> keys = uri.getQueryParameters("key");
+        List<String> values = uri.getQueryParameters("value");
+        if (keys.size() != values.size()) {
+            return new String[] {uris.get(0)};
+        }
+
+        List<String> ls = new ArrayList();
+        ls.add(uris.get(0));
+        for (int i = 0; i < keys.size() ; i++) {
+            ls.add(keys.get(i));
+            ls.add(values.get(i));
+        }
+
+        return ls.toArray(new String[ls.size()]);
+    }
+
+    private static final String encodeMediaPlayer2Uri(String uri, String[] keys, String[] values) {
+        Uri.Builder builder = new Uri.Builder();
+        builder.scheme("mediaplayer2").path("/").appendQueryParameter("uri", uri);
+        if (keys == null || values == null || keys.length != values.length) {
+            return builder.build().toString();
+        }
+        for (int i = 0; i < keys.length ; i++) {
+            builder
+                .appendQueryParameter("key", keys[i])
+                .appendQueryParameter("value", values[i]);
+        }
+        return builder.build().toString();
+    }
+
     /**
      * @hide
      */
@@ -534,6 +514,271 @@
     public MediaPlayer2() { }
 
     /**
+     * Releases the resources held by this {@code MediaPlayer2} object.
+     *
+     * It is considered good practice to call this method when you're
+     * done using the MediaPlayer2. In particular, whenever an Activity
+     * of an application is paused (its onPause() method is called),
+     * or stopped (its onStop() method is called), this method should be
+     * invoked to release the MediaPlayer2 object, unless the application
+     * has a special need to keep the object around. In addition to
+     * unnecessary resources (such as memory and instances of codecs)
+     * being held, failure to call this method immediately if a
+     * MediaPlayer2 object is no longer needed may also lead to
+     * continuous battery consumption for mobile devices, and playback
+     * failure for other applications if no multiple instances of the
+     * same codec are supported on a device. Even if multiple instances
+     * of the same codec are supported, some performance degradation
+     * may be expected when unnecessary multiple instances are used
+     * at the same time.
+     *
+     * {@code close()} may be safely called after a prior {@code close()}.
+     * This class implements the Java {@code AutoCloseable} interface and
+     * may be used with try-with-resources.
+     */
+    // This is a synchronous call.
+    @Override
+    public abstract void close();
+
+    /**
+     * Starts or resumes playback. If playback had previously been paused,
+     * playback will continue from where it was paused. If playback had
+     * reached end of stream and been paused, or never started before,
+     * playback will start at the beginning. If the source had not been
+     * prepared, the player will prepare the source and play.
+     *
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void play();
+
+    /**
+     * Prepares the player for playback, asynchronously.
+     *
+     * After setting the datasource and the display surface, you need to
+     * call prepare().
+     *
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void prepare();
+
+    /**
+     * Pauses playback. Call play() to resume.
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void pause();
+
+    /**
+     * Tries to play next data source if applicable.
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void skipToNext();
+
+    /**
+     * Moves the media to specified time position.
+     * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
+     *
+     * @param msec the offset in milliseconds from the start to seek to
+     */
+    // This is an asynchronous call.
+    @Override
+    public void seekTo(long msec) {
+        seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
+    }
+
+    /**
+     * Gets the current playback position.
+     *
+     * @return the current position in milliseconds
+     */
+    @Override
+    public abstract long getCurrentPosition();
+
+    /**
+     * Gets the duration of the file.
+     *
+     * @return the duration in milliseconds, if no duration is available
+     *         (for example, if streaming live content), -1 is returned.
+     */
+    @Override
+    public abstract long getDuration();
+
+    /**
+     * Gets the current buffered media source position received through progressive downloading.
+     * The received buffering percentage indicates how much of the content has been buffered
+     * or played. For example a buffering update of 80 percent when half the content
+     * has already been played indicates that the next 30 percent of the
+     * content to play has been buffered.
+     *
+     * @return the current buffered media source position in milliseconds
+     */
+    @Override
+    public abstract long getBufferedPosition();
+
+    /**
+     * Gets the current player state.
+     *
+     * @return the current player state.
+     */
+    @Override
+    public abstract @PlayerState int getPlayerState();
+
+    /**
+     * Gets the current buffering state of the player.
+     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+     * buffered.
+     * @return the buffering state, one of the following:
+     */
+    @Override
+    public abstract @BuffState int getBufferingState();
+
+    /**
+     * Sets the audio attributes for this MediaPlayer2.
+     * See {@link AudioAttributes} for how to build and configure an instance of this class.
+     * You must call this method before {@link #prepare()} in order
+     * for the audio attributes to become effective thereafter.
+     * @param attributes a non-null set of audio attributes
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
+
+    /**
+     * Gets the audio attributes for this MediaPlayer2.
+     * @return attributes a set of audio attributes
+     */
+    @Override
+    public abstract @Nullable AudioAttributes getAudioAttributes();
+
+    /**
+     * Sets the data source as described by a DataSourceDesc.
+     *
+     * @param dsd the descriptor of data source you want to play
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void setDataSource(@NonNull DataSourceDesc dsd);
+
+    /**
+     * Sets a single data source as described by a DataSourceDesc which will be played
+     * after current data source is finished.
+     *
+     * @param dsd the descriptor of data source you want to play after current one
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
+
+    /**
+     * Sets a list of data sources to be played sequentially after current data source is done.
+     *
+     * @param dsds the list of data sources you want to play after current one
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
+
+    /**
+     * Gets the current data source as described by a DataSourceDesc.
+     *
+     * @return the current DataSourceDesc
+     */
+    @Override
+    public abstract @NonNull DataSourceDesc getCurrentDataSource();
+
+    /**
+     * Configures the player to loop on the current data source.
+     * @param loop true if the current data source is meant to loop.
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void loopCurrent(boolean loop);
+
+    /**
+     * Sets the playback speed.
+     * A value of 1.0f is the default playback value.
+     * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
+     * before using negative values.<br>
+     * After changing the playback speed, it is recommended to query the actual speed supported
+     * by the player, see {@link #getPlaybackSpeed()}.
+     * @param speed the desired playback speed
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void setPlaybackSpeed(float speed);
+
+    /**
+     * Returns the actual playback speed to be used by the player when playing.
+     * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
+     * @return the actual playback speed
+     */
+    @Override
+    public float getPlaybackSpeed() {
+        return 1.0f;
+    }
+
+    /**
+     * Indicates whether reverse playback is supported.
+     * Reverse playback is indicated by negative playback speeds, see
+     * {@link #setPlaybackSpeed(float)}.
+     * @return true if reverse playback is supported.
+     */
+    @Override
+    public boolean isReversePlaybackSupported() {
+        return false;
+    }
+
+    /**
+     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
+     * on the audio samples.
+     * Note that this volume is specific to the player, and is separate from stream volume
+     * used across the platform.<br>
+     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
+     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
+     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
+     */
+    // This is an asynchronous call.
+    @Override
+    public abstract void setPlayerVolume(float volume);
+
+    /**
+     * Returns the current volume of this player to this player.
+     * Note that it does not take into account the associated stream volume.
+     * @return the player volume.
+     */
+    @Override
+    public abstract float getPlayerVolume();
+
+    /**
+     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
+     */
+    @Override
+    public float getMaxPlayerVolume() {
+        return 1.0f;
+    }
+
+    /**
+     * Adds a callback to be notified of events for this player.
+     * @param e the {@link Executor} to be used for the events.
+     * @param cb the callback to receive the events.
+     */
+    // This is a synchronous call.
+    @Override
+    public abstract void registerPlayerEventCallback(@NonNull Executor e,
+            @NonNull PlayerEventCallback cb);
+
+    /**
+     * Removes a previously registered callback for player events
+     * @param cb the callback to remove
+     */
+    // This is a synchronous call.
+    @Override
+    public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
+
+    /**
      * Create a request parcel which can be routed to the native media
      * player using {@link #invoke(Parcel, Parcel)}. The Parcel
      * returned has the proper InterfaceToken set. The caller should
@@ -552,7 +797,7 @@
      * Invoke a generic method on the native player using opaque
      * parcels for the request and reply. Both payloads' format is a
      * convention between the java caller and the native player.
-     * Must be called after setDataSource or setPlaylist to make sure a native player
+     * Must be called after setDataSource to make sure a native player
      * exists. On failure, a RuntimeException is thrown.
      *
      * @param request Parcel with the data for the extension. The
@@ -565,6 +810,20 @@
     public void invoke(Parcel request, Parcel reply) { }
 
     /**
+     * Insert a task in the command queue to help the client to identify whether a batch
+     * of commands has been finished. When this command is processed, a notification
+     * {@code MediaPlayer2EventCallback.onCommandLabelReached} will be fired with the
+     * given {@code label}.
+     *
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+     *
+     * @param label An application specific Object used to help to identify the completeness
+     * of a batch of commands.
+     */
+    // This is an asynchronous call.
+    public void notifyWhenCommandLabelReached(@NonNull Object label) { }
+
+    /**
      * Sets the {@link SurfaceHolder} to use for displaying the video
      * portion of the media.
      *
@@ -600,6 +859,7 @@
      * @throws IllegalStateException if the internal player engine has not been
      * initialized or has been released.
      */
+    // This is an asynchronous call.
     public abstract void setSurface(Surface surface);
 
     /* Do not change these video scaling mode values below without updating
@@ -648,204 +908,10 @@
     /**
      * Discards all pending commands.
      */
+    // This is a synchronous call.
     public abstract void clearPendingCommands();
 
     /**
-     * Sets the data source as described by a DataSourceDesc.
-     *
-     * @param dsd the descriptor of data source you want to play
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws NullPointerException if dsd is null
-     */
-    public abstract void setDataSource(@NonNull DataSourceDesc dsd) throws IOException;
-
-    /**
-     * Gets the current data source as described by a DataSourceDesc.
-     *
-     * @return the current DataSourceDesc
-     */
-    public abstract DataSourceDesc getCurrentDataSource();
-
-    /**
-     * Sets the play list.
-     *
-     * If startIndex falls outside play list range, it will be clamped to the nearest index
-     * in the play list.
-     *
-     * @param pl the play list of data source you want to play
-     * @param startIndex the index of the DataSourceDesc in the play list you want to play first
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if pl is null or empty, or pl contains null DataSourceDesc
-     */
-    public abstract void setPlaylist(@NonNull List<DataSourceDesc> pl, int startIndex)
-            throws IOException;
-
-    /**
-     * Gets a copy of the play list.
-     *
-     * @return a copy of the play list used by {@link MediaPlayer2}
-     */
-    public abstract List<DataSourceDesc> getPlaylist();
-
-    /**
-     * Sets the index of current DataSourceDesc in the play list to be played.
-     *
-     * @param index the index of DataSourceDesc in the play list you want to play
-     * @throws IllegalArgumentException if the play list is null
-     * @throws NullPointerException if index is outside play list range
-     */
-    public abstract void setCurrentPlaylistItem(int index);
-
-    /**
-     * Sets the index of next-to-be-played DataSourceDesc in the play list.
-     *
-     * @param index the index of next-to-be-played DataSourceDesc in the play list
-     * @throws IllegalArgumentException if the play list is null
-     * @throws NullPointerException if index is outside play list range
-     */
-    public abstract void setNextPlaylistItem(int index);
-
-    /**
-     * Gets the current index of play list.
-     *
-     * @return the index of the current DataSourceDesc in the play list
-     */
-    public abstract int getCurrentPlaylistItemIndex();
-
-    /**
-     * Specifies a playback looping mode. The source will not be played in looping mode.
-     */
-    public static final int LOOPING_MODE_NONE = 0;
-    /**
-     * Specifies a playback looping mode. The full list of source will be played in looping mode,
-     * and in the order specified in the play list.
-     */
-    public static final int LOOPING_MODE_FULL = 1;
-    /**
-     * Specifies a playback looping mode. The current DataSourceDesc will be played in looping mode.
-     */
-    public static final int LOOPING_MODE_SINGLE = 2;
-    /**
-     * Specifies a playback looping mode. The full list of source will be played in looping mode,
-     * and in a random order.
-     */
-    public static final int LOOPING_MODE_SHUFFLE = 3;
-
-    /** @hide */
-    @IntDef(
-        value = {
-            LOOPING_MODE_NONE,
-            LOOPING_MODE_FULL,
-            LOOPING_MODE_SINGLE,
-            LOOPING_MODE_SHUFFLE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface LoopingMode {}
-
-    /**
-     * Sets the looping mode of the play list.
-     * The mode shall be one of {@link #LOOPING_MODE_NONE}, {@link #LOOPING_MODE_FULL},
-     * {@link #LOOPING_MODE_SINGLE}, {@link #LOOPING_MODE_SHUFFLE}.
-     *
-     * @param mode the mode in which the play list will be played
-     * @throws IllegalArgumentException if mode is not supported
-     */
-    public abstract void setLoopingMode(@LoopingMode int mode);
-
-    /**
-     * Gets the looping mode of play list.
-     *
-     * @return the looping mode of the play list
-     */
-    public abstract int getLoopingMode();
-
-    /**
-     * Moves the DataSourceDesc at indexFrom in the play list to indexTo.
-     *
-     * @throws IllegalArgumentException if the play list is null
-     * @throws IndexOutOfBoundsException if indexFrom or indexTo is outside play list range
-     */
-    public abstract void movePlaylistItem(int indexFrom, int indexTo);
-
-    /**
-     * Removes the DataSourceDesc at index in the play list.
-     *
-     * If index is same as the current index of the play list, current DataSourceDesc
-     * will be stopped and playback moves to next source in the list.
-     *
-     * @return the removed DataSourceDesc at index in the play list
-     * @throws IllegalArgumentException if the play list is null
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     */
-    public abstract DataSourceDesc removePlaylistItem(int index);
-
-    /**
-     * Inserts the DataSourceDesc to the play list at position index.
-     *
-     * This will not change the DataSourceDesc currently being played.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
-     *
-     * @param index the index you want to add dsd to the play list
-     * @param dsd the descriptor of data source you want to add to the play list
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     * @throws NullPointerException if dsd is null
-     */
-    public abstract void addPlaylistItem(int index, DataSourceDesc dsd);
-
-    /**
-     * replaces the DataSourceDesc at index in the play list with given dsd.
-     *
-     * When index is same as the current index of the play list, the current source
-     * will be stopped and the new source will be played, except that if new
-     * and old source only differ on end position and current media position is
-     * smaller then the new end position.
-     *
-     * This will not change the DataSourceDesc currently being played.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
-     *
-     * @param index the index you want to add dsd to the play list
-     * @param dsd the descriptor of data source you want to add to the play list
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     * @throws NullPointerException if dsd is null
-     */
-    public abstract DataSourceDesc editPlaylistItem(int index, DataSourceDesc dsd);
-
-    /**
-     * Prepares the player for playback, synchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
-     * which blocks until MediaPlayer2 is ready for playback.
-     *
-     * @throws IOException if source can not be accessed
-     * @throws IllegalStateException if it is called in an invalid state
-     * @hide
-     */
-    public void prepare() throws IOException { }
-
-    /**
-     * Prepares the player for playback, asynchronously.
-     *
-     * After setting the datasource and the display surface, you need to
-     * call prepareAsync().
-     *
-     * @throws IllegalStateException if it is called in an invalid state
-     */
-    public abstract void prepareAsync();
-
-    /**
-     * Starts or resumes playback. If playback had previously been paused,
-     * playback will continue from where it was paused. If playback had
-     * been stopped, or never started before, playback will start at the
-     * beginning.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
-     */
-    public abstract void play();
-
-    /**
      * Stops playback after playback has been started or paused.
      *
      * @throws IllegalStateException if the internal player engine has not been
@@ -854,14 +920,6 @@
      */
     public void stop() { }
 
-    /**
-     * Pauses playback. Call play() to resume.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     */
-    public abstract void pause();
-
     //--------------------------------------------------------------------------
     // Explicit Routing
     //--------------------
@@ -874,6 +932,7 @@
      * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
      * does not correspond to a valid audio device.
      */
+    // This is an asynchronous call.
     @Override
     public abstract boolean setPreferredDevice(AudioDeviceInfo deviceInfo);
 
@@ -901,6 +960,7 @@
      * @param handler  Specifies the {@link Handler} object for the thread on which to execute
      * the callback. If <code>null</code>, the handler on the main looper will be used.
      */
+    // This is a synchronous call.
     @Override
     public abstract void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
             Handler handler);
@@ -911,6 +971,7 @@
      * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
      * to remove.
      */
+    // This is a synchronous call.
     @Override
     public abstract void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener);
 
@@ -949,9 +1010,10 @@
      *
      * @return the width of the video, or 0 if there is no video,
      * no display surface was set, or the width has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #registerEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the width is available.
+     * yet. The {@code MediaPlayer2EventCallback} can be registered via
+     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+     * is available.
      */
     public abstract int getVideoWidth();
 
@@ -960,9 +1022,9 @@
      *
      * @return the height of the video, or 0 if there is no video,
      * no display surface was set, or the height has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #registerEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the height is available.
+     * yet. The {@code MediaPlayer2EventCallback} can be registered via
+     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height is available.
      */
     public abstract int getVideoHeight();
 
@@ -984,10 +1046,65 @@
      * @return true if currently playing, false otherwise
      * @throws IllegalStateException if the internal player engine has not been
      * initialized or has been released.
+     * @hide
      */
     public abstract boolean isPlaying();
 
     /**
+     * MediaPlayer2 has not been prepared or just has been reset.
+     * In this state, MediaPlayer2 doesn't fetch data.
+     * @hide
+     */
+    public static final int MEDIAPLAYER2_STATE_IDLE = 1;
+
+    /**
+     * MediaPlayer2 has been just prepared.
+     * In this state, MediaPlayer2 just fetches data from media source,
+     * but doesn't actively render data.
+     * @hide
+     */
+    public static final int MEDIAPLAYER2_STATE_PREPARED = 2;
+
+    /**
+     * MediaPlayer2 is paused.
+     * In this state, MediaPlayer2 doesn't actively render data.
+     * @hide
+     */
+    public static final int MEDIAPLAYER2_STATE_PAUSED = 3;
+
+    /**
+     * MediaPlayer2 is actively playing back data.
+     * @hide
+     */
+    public static final int MEDIAPLAYER2_STATE_PLAYING = 4;
+
+    /**
+     * MediaPlayer2 has hit some fatal error and cannot continue playback.
+     * @hide
+     */
+    public static final int MEDIAPLAYER2_STATE_ERROR = 5;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
+        MEDIAPLAYER2_STATE_IDLE,
+        MEDIAPLAYER2_STATE_PREPARED,
+        MEDIAPLAYER2_STATE_PAUSED,
+        MEDIAPLAYER2_STATE_PLAYING,
+        MEDIAPLAYER2_STATE_ERROR })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaPlayer2State {}
+
+    /**
+     * Gets the current MediaPlayer2 state.
+     *
+     * @return the current MediaPlayer2 state.
+     * @hide
+     */
+    public abstract @MediaPlayer2State int getMediaPlayer2State();
+
+    /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
      * Each type of data source might have different set of default params.
@@ -1016,6 +1133,7 @@
      * @throws IllegalArgumentException if params is invalid or not supported.
      * @hide
      */
+    // This is an asynchronous call.
     public void setBufferingParams(@NonNull BufferingParams params) { }
 
     /**
@@ -1060,8 +1178,7 @@
     public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
 
     /** @hide */
-    @IntDef(
-        value = {
+    @IntDef(flag = false, prefix = "PLAYBACK_RATE_AUDIO_MODE", value = {
             PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
             PLAYBACK_RATE_AUDIO_MODE_STRETCH,
             PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
@@ -1097,19 +1214,14 @@
      * non-zero speed is equivalent to calling play().
      *
      * @param params the playback params.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @throws IllegalArgumentException if params is not supported.
      */
+    // This is an asynchronous call.
     public abstract void setPlaybackParams(@NonNull PlaybackParams params);
 
     /**
      * Gets the playback params, containing the current playback rate.
      *
      * @return the playback params.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
      */
     @NonNull
     public abstract PlaybackParams getPlaybackParams();
@@ -1118,20 +1230,14 @@
      * Sets A/V sync mode.
      *
      * @param params the A/V sync params to apply
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     * @throws IllegalArgumentException if params are not supported.
      */
+    // This is an asynchronous call.
     public abstract void setSyncParams(@NonNull SyncParams params);
 
     /**
      * Gets the A/V sync mode.
      *
      * @return the A/V sync params
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
      */
     @NonNull
     public abstract SyncParams getSyncParams();
@@ -1177,8 +1283,7 @@
     public static final int SEEK_CLOSEST          = 0x03;
 
     /** @hide */
-    @IntDef(
-        value = {
+    @IntDef(flag = false, prefix = "SEEK", value = {
             SEEK_PREVIOUS_SYNC,
             SEEK_NEXT_SYNC,
             SEEK_CLOSEST_SYNC,
@@ -1203,20 +1308,8 @@
      * If msec is negative, time position zero will be used.
      * If msec is larger than duration, duration will be used.
      * @param mode the mode indicating where exactly to seek to.
-     * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp earlier than or the same as msec. Use
-     * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp later than or the same as msec. Use
-     * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp closest to or the same as msec. Use
-     * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may
-     * or may not be a sync frame but is closest to or the same as msec.
-     * {@link #SEEK_CLOSEST} often has larger performance overhead compared
-     * to the other options if there is no sync frame located at msec.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized
-     * @throws IllegalArgumentException if the mode is invalid.
      */
+    // This is an asynchronous call.
     public abstract void seekTo(long msec, @SeekMode int mode);
 
     /**
@@ -1241,21 +1334,6 @@
     public abstract MediaTimestamp getTimestamp();
 
     /**
-     * Gets the current playback position.
-     *
-     * @return the current position in milliseconds
-     */
-    public abstract int getCurrentPosition();
-
-    /**
-     * Gets the duration of the file.
-     *
-     * @return the duration in milliseconds, if no duration is available
-     *         (for example, if streaming live content), -1 is returned.
-     */
-    public abstract int getDuration();
-
-    /**
      * Gets the media metadata.
      *
      * @param update_only controls whether the full set of available
@@ -1300,31 +1378,12 @@
     }
 
     /**
-     * Set the MediaPlayer2 to start when this MediaPlayer2 finishes playback
-     * (i.e. reaches the end of the stream).
-     * The media framework will attempt to transition from this player to
-     * the next as seamlessly as possible. The next player can be set at
-     * any time before completion, but shall be after setDataSource has been
-     * called successfully. The next player must be prepared by the
-     * app, and the application should not call play() on it.
-     * The next MediaPlayer2 must be different from 'this'. An exception
-     * will be thrown if next == this.
-     * The application may call setNextMediaPlayer(null) to indicate no
-     * next player should be started at the end of playback.
-     * If the current player is looping, it will keep looping and the next
-     * player will not be started.
-     *
-     * @param next the player to start after this one completes playback.
-     *
-     * @hide
-     */
-    public void setNextMediaPlayer(MediaPlayer2 next) { }
-
-    /**
      * Resets the MediaPlayer2 to its uninitialized state. After calling
      * this method, you will have to initialize it again by setting the
-     * data source and calling prepareAsync().
+     * data source and calling prepare().
      */
+    // This is a synchronous call.
+    @Override
     public abstract void reset();
 
     /**
@@ -1338,24 +1397,6 @@
     public void notifyAt(long mediaTimeUs) { }
 
     /**
-     * Sets the audio attributes for this MediaPlayer2.
-     * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepareAsync()} in order
-     * for the audio attributes to become effective thereafter.
-     * @param attributes a non-null set of audio attributes
-     * @throws IllegalArgumentException if the attributes are null or invalid.
-     */
-    public abstract void setAudioAttributes(AudioAttributes attributes);
-
-    /**
-     * Sets the player to be looping or non-looping.
-     *
-     * @param looping whether to loop or not
-     * @hide
-     */
-    public void setLooping(boolean looping) { }
-
-    /**
      * Checks whether the MediaPlayer2 is looping or non-looping.
      *
      * @return true if the MediaPlayer2 is currently looping, false otherwise
@@ -1366,31 +1407,6 @@
     }
 
     /**
-     * Sets the volume on this player.
-     * This API is recommended for balancing the output of audio streams
-     * within an application. Unless you are writing an application to
-     * control user settings, this API should be used in preference to
-     * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
-     * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
-     * UI controls should be scaled logarithmically.
-     *
-     * @param leftVolume left volume scalar
-     * @param rightVolume right volume scalar
-     */
-    /*
-     * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
-     * The single parameter form below is preferred if the channel volumes don't need
-     * to be set independently.
-     */
-    public abstract void setVolume(float leftVolume, float rightVolume);
-
-    /**
-     * Similar, excepts sets volume of all channels to same value.
-     * @hide
-     */
-    public void setVolume(float volume) { }
-
-    /**
      * Sets the audio session ID.
      *
      * @param sessionId the audio session ID.
@@ -1404,9 +1420,8 @@
      * However, it is possible to force this player to be part of an already existing audio session
      * by calling this method.
      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if the sessionId is invalid.
      */
+    // This is an asynchronous call.
     public abstract void setAudioSessionId(int sessionId);
 
     /**
@@ -1431,6 +1446,7 @@
      * methods.
      * @param effectId system wide unique id of the effect to attach
      */
+    // This is an asynchronous call.
     public abstract void attachAuxEffect(int effectId);
 
 
@@ -1446,6 +1462,7 @@
      * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
      * @param level send level scalar
      */
+    // This is an asynchronous call.
     public abstract void setAuxEffectSendLevel(float level);
 
     /**
@@ -1494,7 +1511,6 @@
      * @return List of track info. The total number of tracks is the array length.
      * Must be called again if an external timed text source has been added after
      * addTimedTextSource method is called.
-     * @throws IllegalStateException if it is called in an invalid state.
      */
     public abstract List<TrackInfo> getTrackInfo();
 
@@ -1661,6 +1677,7 @@
      *
      * @see android.media.MediaPlayer2#getTrackInfo
      */
+    // This is an asynchronous call.
     public abstract void selectTrack(int index);
 
     /**
@@ -1677,63 +1694,9 @@
      *
      * @see android.media.MediaPlayer2#getTrackInfo
      */
+    // This is an asynchronous call.
     public abstract void deselectTrack(int index);
 
-    /**
-     * Sets the target UDP re-transmit endpoint for the low level player.
-     * Generally, the address portion of the endpoint is an IP multicast
-     * address, although a unicast address would be equally valid.  When a valid
-     * retransmit endpoint has been set, the media player will not decode and
-     * render the media presentation locally.  Instead, the player will attempt
-     * to re-multiplex its media data using the Android@Home RTP profile and
-     * re-transmit to the target endpoint.  Receiver devices (which may be
-     * either the same as the transmitting device or different devices) may
-     * instantiate, prepare, and start a receiver player using a setDataSource
-     * URL of the form...
-     *
-     * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
-     *
-     * to receive, decode and render the re-transmitted content.
-     *
-     * setRetransmitEndpoint may only be called before setDataSource has been
-     * called; while the player is in the Idle state.
-     *
-     * @param endpoint the address and UDP port of the re-transmission target or
-     * null if no re-transmission is to be performed.
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if the retransmit endpoint is supplied,
-     * but invalid.
-     *
-     * {@hide} pending API council
-     */
-    public void setRetransmitEndpoint(InetSocketAddress endpoint) { }
-
-    /**
-     * Releases the resources held by this {@code MediaPlayer2} object.
-     *
-     * It is considered good practice to call this method when you're
-     * done using the MediaPlayer2. In particular, whenever an Activity
-     * of an application is paused (its onPause() method is called),
-     * or stopped (its onStop() method is called), this method should be
-     * invoked to release the MediaPlayer2 object, unless the application
-     * has a special need to keep the object around. In addition to
-     * unnecessary resources (such as memory and instances of codecs)
-     * being held, failure to call this method immediately if a
-     * MediaPlayer2 object is no longer needed may also lead to
-     * continuous battery consumption for mobile devices, and playback
-     * failure for other applications if no multiple instances of the
-     * same codec are supported on a device. Even if multiple instances
-     * of the same codec are supported, some performance degradation
-     * may be expected when unnecessary multiple instances are used
-     * at the same time.
-     *
-     * {@code close()} may be safely called after a prior {@code close()}.
-     * This class implements the Java {@code AutoCloseable} interface and
-     * may be used with try-with-resources.
-     */
-    @Override
-    public abstract void close();
-
     /** @hide */
     public MediaTimeProvider getMediaTimeProvider() {
         return null;
@@ -1743,22 +1706,7 @@
      * Interface definition for callbacks to be invoked when the player has the corresponding
      * events.
      */
-    public abstract static class EventCallback {
-        /**
-         * Called to update status in buffering a media source received through
-         * progressive downloading. The received buffering percentage
-         * indicates how much of the content has been buffered or played.
-         * For example a buffering update of 80 percent when half the content
-         * has already been played indicates that the next 30 percent of the
-         * content to play has been buffered.
-         *
-         * @param mp the MediaPlayer2 the update pertains to
-         * @param srcId the Id of this data source
-         * @param percent the percentage (0-100) of the content
-         *                that has been buffered or played thus far
-         */
-        public void onBufferingUpdate(MediaPlayer2 mp, long srcId, int percent) { }
-
+    public abstract static class MediaPlayer2EventCallback {
         /**
          * Called to indicate the video size
          *
@@ -1766,22 +1714,22 @@
          * no display surface was set, or the value was not determined yet.
          *
          * @param mp the MediaPlayer2 associated with this callback
-         * @param srcId the Id of this data source
+         * @param dsd the DataSourceDesc of this data source
          * @param width the width of the video
          * @param height the height of the video
          */
-        public void onVideoSizeChanged(MediaPlayer2 mp, long srcId, int width, int height) { }
+        public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd, int width, int height) { }
 
         /**
          * Called to indicate an avaliable timed text
          *
          * @param mp the MediaPlayer2 associated with this callback
-         * @param srcId the Id of this data source
+         * @param dsd the DataSourceDesc of this data source
          * @param text the timed text sample which contains the text
          *             needed to be displayed and the display format.
          * @hide
          */
-        public void onTimedText(MediaPlayer2 mp, long srcId, TimedText text) { }
+        public void onTimedText(MediaPlayer2 mp, DataSourceDesc dsd, TimedText text) { }
 
         /**
          * Called to indicate avaliable timed metadata
@@ -1798,82 +1746,82 @@
          * @see TimedMetaData
          *
          * @param mp the MediaPlayer2 associated with this callback
-         * @param srcId the Id of this data source
+         * @param dsd the DataSourceDesc of this data source
          * @param data the timed metadata sample associated with this event
          */
-        public void onTimedMetaDataAvailable(MediaPlayer2 mp, long srcId, TimedMetaData data) { }
+        public void onTimedMetaDataAvailable(
+                MediaPlayer2 mp, DataSourceDesc dsd, TimedMetaData data) { }
 
         /**
          * Called to indicate an error.
          *
          * @param mp the MediaPlayer2 the error pertains to
-         * @param srcId the Id of this data source
-         * @param what the type of error that has occurred:
-         * <ul>
-         * <li>{@link #MEDIA_ERROR_UNKNOWN}
-         * </ul>
+         * @param dsd the DataSourceDesc of this data source
+         * @param what the type of error that has occurred.
          * @param extra an extra code, specific to the error. Typically
          * implementation dependent.
-         * <ul>
-         * <li>{@link #MEDIA_ERROR_IO}
-         * <li>{@link #MEDIA_ERROR_MALFORMED}
-         * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
-         * <li>{@link #MEDIA_ERROR_TIMED_OUT}
-         * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error.
-         * </ul>
          */
-        public void onError(MediaPlayer2 mp, long srcId, int what, int extra) { }
+        public void onError(
+                MediaPlayer2 mp, DataSourceDesc dsd, @MediaError int what, int extra) { }
 
         /**
          * Called to indicate an info or a warning.
          *
          * @param mp the MediaPlayer2 the info pertains to.
-         * @param srcId the Id of this data source
+         * @param dsd the DataSourceDesc of this data source
          * @param what the type of info or warning.
-         * <ul>
-         * <li>{@link #MEDIA_INFO_UNKNOWN}
-         * <li>{@link #MEDIA_INFO_STARTED_AS_NEXT}
-         * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
-         * <li>{@link #MEDIA_INFO_AUDIO_RENDERING_START}
-         * <li>{@link #MEDIA_INFO_PLAYBACK_COMPLETE}
-         * <li>{@link #MEDIA_INFO_PLAYLIST_END}
-         * <li>{@link #MEDIA_INFO_PREPARED}
-         * <li>{@link #MEDIA_INFO_COMPLETE_CALL_PLAY}
-         * <li>{@link #MEDIA_INFO_COMPLETE_CALL_PAUSE}
-         * <li>{@link #MEDIA_INFO_COMPLETE_CALL_SEEK}
-         * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
-         * <li>{@link #MEDIA_INFO_BUFFERING_START}
-         * <li>{@link #MEDIA_INFO_BUFFERING_END}
-         * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> -
-         *     bandwidth information is available (as <code>extra</code> kbps)
-         * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
-         * <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
-         * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
-         * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE}
-         * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT}
-         * </ul>
          * @param extra an extra code, specific to the info. Typically
          * implementation dependent.
          */
-        public void onInfo(MediaPlayer2 mp, long srcId, int what, int extra) { }
+        public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, @MediaInfo int what, int extra) { }
+
+        /**
+         * Called to acknowledge an API call.
+         *
+         * @param mp the MediaPlayer2 the call was made on.
+         * @param dsd the DataSourceDesc of this data source
+         * @param what the enum for the API call.
+         * @param status the returned status code for the call.
+         */
+        public void onCallCompleted(
+                MediaPlayer2 mp, DataSourceDesc dsd, @CallCompleted int what,
+                @CallStatus int status) { }
+
+        /**
+         * Called to indicate media clock has changed.
+         *
+         * @param mp the MediaPlayer2 the media time pertains to.
+         * @param dsd the DataSourceDesc of this data source
+         * @param timestamp the new media clock.
+         */
+        public void onMediaTimeChanged(
+                MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) { }
+
+        /**
+         * Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed.
+         *
+         * @param mp the MediaPlayer2 {@link #notifyWhenCommandLabelReached(Object)} was called on.
+         * @param label the application specific Object given by
+         *        {@link #notifyWhenCommandLabelReached(Object)}.
+         */
+        public void onCommandLabelReached(MediaPlayer2 mp, @NonNull Object label) { }
     }
 
     /**
-     * Register a callback to be invoked when the media source is ready
-     * for playback.
+     * Sets the callback to be invoked when the media source is ready for playback.
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
      */
-    public abstract void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull EventCallback eventCallback);
+    // This is a synchronous call.
+    public abstract void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull MediaPlayer2EventCallback eventCallback);
 
     /**
-     * Unregisters an {@link EventCallback}.
-     *
-     * @param callback an {@link EventCallback} to unregister
+     * Clears the {@link MediaPlayer2EventCallback}.
      */
-    public abstract void unregisterEventCallback(EventCallback callback);
+    // This is a synchronous call.
+    public abstract void clearMediaPlayer2EventCallback();
 
     /**
      * Interface definition of a callback to be invoked when a
@@ -1893,6 +1841,7 @@
      *
      * @hide
      */
+    // This is a synchronous call.
     public void setOnSubtitleDataListener(OnSubtitleDataListener listener) { }
 
 
@@ -1900,14 +1849,14 @@
      * in include/media/mediaplayer2.h!
      */
     /** Unspecified media player error.
-     * @see android.media.MediaPlayer2.EventCallback.onError
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
      */
     public static final int MEDIA_ERROR_UNKNOWN = 1;
 
     /** The video is streamed and its container is not valid for progressive
      * playback i.e the video's index (e.g moov atom) is not at the start of the
      * file.
-     * @see android.media.MediaPlayer2.EventCallback.onError
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
      */
     public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
 
@@ -1923,106 +1872,117 @@
 
     /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
      * system/core/include/utils/Errors.h
-     * @see android.media.MediaPlayer2.EventCallback.onError
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onError
      * @hide
      */
     public static final int MEDIA_ERROR_SYSTEM = -2147483648;
 
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "MEDIA_ERROR", value = {
+            MEDIA_ERROR_UNKNOWN,
+            MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,
+            MEDIA_ERROR_IO,
+            MEDIA_ERROR_MALFORMED,
+            MEDIA_ERROR_UNSUPPORTED,
+            MEDIA_ERROR_TIMED_OUT,
+            MEDIA_ERROR_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaError {}
 
     /* Do not change these values without updating their counterparts
      * in include/media/mediaplayer2.h!
      */
     /** Unspecified media player info.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_UNKNOWN = 1;
 
     /** The player switched to this datas source because it is the
-     * next-to-be-played in the play list.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * next-to-be-played in the playlist.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
 
     /** The player just pushed the very first video frame for rendering.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
 
     /** The player just rendered the very first audio sample.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4;
 
     /** The player just completed the playback of this data source.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_PLAYBACK_COMPLETE = 5;
 
-    /** The player just completed the playback of the full play list.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+    /** The player just completed the playback of the full playlist.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_PLAYLIST_END = 6;
 
     /** The player just prepared a data source.
-     * This also serves as call completion notification for {@link #prepareAsync()}.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_PREPARED = 100;
 
-    /** The player just completed a call {@link #play()}.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
-     */
-    public static final int MEDIA_INFO_COMPLETE_CALL_PLAY = 101;
-
-    /** The player just completed a call {@link #pause()}.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
-     */
-    public static final int MEDIA_INFO_COMPLETE_CALL_PAUSE = 102;
-
-    /** The player just completed a call {@link #seekTo(long, int)}.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
-     */
-    public static final int MEDIA_INFO_COMPLETE_CALL_SEEK = 103;
-
     /** The video is too complex for the decoder: it can't decode frames fast
      *  enough. Possibly only the audio plays fine at this stage.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
 
     /** MediaPlayer2 is temporarily pausing playback internally in order to
      * buffer more data.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BUFFERING_START = 701;
 
     /** MediaPlayer2 is resuming playback after filling buffers.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BUFFERING_END = 702;
 
     /** Estimated network bandwidth information (kbps) is available; currently this event fires
      * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
      * when playing network files.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      * @hide
      */
     public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
 
+    /**
+     * Update status in buffering a media source received through progressive downloading.
+     * The received buffering percentage indicates how much of the content has been buffered
+     * or played. For example a buffering update of 80 percent when half the content
+     * has already been played indicates that the next 30 percent of the
+     * content to play has been buffered.
+     *
+     * The {@code extra} parameter in {@code MediaPlayer2EventCallback.onInfo} is the
+     * percentage (0-100) of the content that has been buffered or played thus far.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
+     */
+    public static final int MEDIA_INFO_BUFFERING_UPDATE = 704;
+
     /** Bad interleaving means that a media has been improperly interleaved or
      * not interleaved at all, e.g has all the video samples first then all the
      * audio ones. Video is playing but a lot of disk seeks may be happening.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
 
     /** The media cannot be seeked (e.g live stream)
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
 
     /** A new set of metadata is available.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_METADATA_UPDATE = 802;
 
@@ -2034,33 +1994,273 @@
 
     /** Informs that audio is not playing. Note that playback of the video
      * is not interrupted.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
 
     /** Informs that video is not playing. Note that playback of the audio
      * is not interrupted.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
 
     /** Failed to handle timed text track properly.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      *
      * {@hide}
      */
     public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
 
     /** Subtitle track was not supported by the media framework.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
 
     /** Reading the subtitle track takes too long.
-     * @see android.media.MediaPlayer2.EventCallback.onInfo
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onInfo
      */
     public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
 
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "MEDIA_INFO", value = {
+            MEDIA_INFO_UNKNOWN,
+            MEDIA_INFO_STARTED_AS_NEXT,
+            MEDIA_INFO_VIDEO_RENDERING_START,
+            MEDIA_INFO_AUDIO_RENDERING_START,
+            MEDIA_INFO_PLAYBACK_COMPLETE,
+            MEDIA_INFO_PLAYLIST_END,
+            MEDIA_INFO_PREPARED,
+            MEDIA_INFO_VIDEO_TRACK_LAGGING,
+            MEDIA_INFO_BUFFERING_START,
+            MEDIA_INFO_BUFFERING_END,
+            MEDIA_INFO_NETWORK_BANDWIDTH,
+            MEDIA_INFO_BUFFERING_UPDATE,
+            MEDIA_INFO_BAD_INTERLEAVING,
+            MEDIA_INFO_NOT_SEEKABLE,
+            MEDIA_INFO_METADATA_UPDATE,
+            MEDIA_INFO_EXTERNAL_METADATA_UPDATE,
+            MEDIA_INFO_AUDIO_NOT_PLAYING,
+            MEDIA_INFO_VIDEO_NOT_PLAYING,
+            MEDIA_INFO_TIMED_TEXT_ERROR,
+            MEDIA_INFO_UNSUPPORTED_SUBTITLE,
+            MEDIA_INFO_SUBTITLE_TIMED_OUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaInfo {}
+
+    //--------------------------------------------------------------------------
+    /** The player just completed a call {@link #attachAuxEffect}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1;
+
+    /** The player just completed a call {@link #deselectTrack}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_DESELECT_TRACK = 2;
+
+    /** The player just completed a call {@link #loopCurrent}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_LOOP_CURRENT = 3;
+
+    /** The player just completed a call {@link #pause}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_PAUSE = 4;
+
+    /** The player just completed a call {@link #play}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_PLAY = 5;
+
+    /** The player just completed a call {@link #prepare}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_PREPARE = 6;
+
+    /** The player just completed a call {@link #releaseDrm}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_RELEASE_DRM = 12;
+
+    /** The player just completed a call {@link #restoreDrmKeys}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_RESTORE_DRM_KEYS = 13;
+
+    /** The player just completed a call {@link #seekTo}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SEEK_TO = 14;
+
+    /** The player just completed a call {@link #selectTrack}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SELECT_TRACK = 15;
+
+    /** The player just completed a call {@link #setAudioAttributes}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16;
+
+    /** The player just completed a call {@link #setAudioSessionId}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17;
+
+    /** The player just completed a call {@link #setAuxEffectSendLevel}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18;
+
+    /** The player just completed a call {@link #setDataSource}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19;
+
+    /** The player just completed a call {@link #setNextDataSource}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22;
+
+    /** The player just completed a call {@link #setNextDataSources}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23;
+
+    /** The player just completed a call {@link #setPlaybackParams}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24;
+
+    /** The player just completed a call {@link #setPlaybackSpeed}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_PLAYBACK_SPEED = 25;
+
+    /** The player just completed a call {@link #setPlayerVolume}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26;
+
+    /** The player just completed a call {@link #setSurface}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_SURFACE = 27;
+
+    /** The player just completed a call {@link #setSyncParams}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28;
+
+    /** The player just completed a call {@link #skipToNext}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29;
+
+    /** The player just completed a call {@link #setBufferingParams}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @hide
+     */
+    public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 1001;
+
+    /** The player just completed a call {@code setVideoScalingMode}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     * @hide
+     */
+    public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 1002;
+
+    /** The player just completed a call {@code notifyWhenCommandLabelReached}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCommandLabelReached
+     * @hide
+     */
+    public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED = 1003;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "CALL_COMPLETED", value = {
+            CALL_COMPLETED_ATTACH_AUX_EFFECT,
+            CALL_COMPLETED_DESELECT_TRACK,
+            CALL_COMPLETED_LOOP_CURRENT,
+            CALL_COMPLETED_PAUSE,
+            CALL_COMPLETED_PLAY,
+            CALL_COMPLETED_PREPARE,
+            CALL_COMPLETED_RELEASE_DRM,
+            CALL_COMPLETED_RESTORE_DRM_KEYS,
+            CALL_COMPLETED_SEEK_TO,
+            CALL_COMPLETED_SELECT_TRACK,
+            CALL_COMPLETED_SET_AUDIO_ATTRIBUTES,
+            CALL_COMPLETED_SET_AUDIO_SESSION_ID,
+            CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL,
+            CALL_COMPLETED_SET_DATA_SOURCE,
+            CALL_COMPLETED_SET_NEXT_DATA_SOURCE,
+            CALL_COMPLETED_SET_NEXT_DATA_SOURCES,
+            CALL_COMPLETED_SET_PLAYBACK_PARAMS,
+            CALL_COMPLETED_SET_PLAYBACK_SPEED,
+            CALL_COMPLETED_SET_PLAYER_VOLUME,
+            CALL_COMPLETED_SET_SURFACE,
+            CALL_COMPLETED_SET_SYNC_PARAMS,
+            CALL_COMPLETED_SKIP_TO_NEXT,
+            CALL_COMPLETED_SET_BUFFERING_PARAMS,
+            CALL_COMPLETED_SET_VIDEO_SCALING_MODE,
+            CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallCompleted {}
+
+    /** Status code represents that call is completed without an error.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_NO_ERROR = 0;
+
+    /** Status code represents that call is ended with an unknown error.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_ERROR_UNKNOWN = Integer.MIN_VALUE;
+
+    /** Status code represents that the player is not in valid state for the operation.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_INVALID_OPERATION = 1;
+
+    /** Status code represents that the argument is illegal.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_BAD_VALUE = 2;
+
+    /** Status code represents that the operation is not allowed.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_PERMISSION_DENIED = 3;
+
+    /** Status code represents a file or network related operation error.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_ERROR_IO = 4;
+
+    /** Status code represents that DRM operation is called before preparing a DRM scheme through
+     *  {@link #prepareDrm}.
+     * @see android.media.MediaPlayer2.MediaPlayer2EventCallback#onCallCompleted
+     */
+    public static final int CALL_STATUS_NO_DRM_SCHEME = 5;
+
+    /**
+     * @hide
+     */
+    @IntDef(flag = false, prefix = "CALL_STATUS", value = {
+            CALL_STATUS_NO_ERROR,
+            CALL_STATUS_ERROR_UNKNOWN,
+            CALL_STATUS_INVALID_OPERATION,
+            CALL_STATUS_BAD_VALUE,
+            CALL_STATUS_PERMISSION_DENIED,
+            CALL_STATUS_ERROR_IO,
+            CALL_STATUS_NO_DRM_SCHEME})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallStatus {}
 
     // Modular DRM begin
 
@@ -2071,8 +2271,8 @@
      * 'securityLevel', which has to be set after DRM scheme creation but
      * before the DRM session is opened.
      *
-     * The only allowed DRM calls in this listener are {@code getDrmPropertyString}
-     * and {@code setDrmPropertyString}.
+     * The only allowed DRM calls in this listener are {@link #getDrmPropertyString}
+     * and {@link #setDrmPropertyString}.
      */
     public interface OnDrmConfigHelper
     {
@@ -2080,8 +2280,9 @@
          * Called to give the app the opportunity to configure DRM before the session is created
          *
          * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the DataSourceDesc of this data source
          */
-        public void onDrmConfig(MediaPlayer2 mp);
+        public void onDrmConfig(MediaPlayer2 mp, DataSourceDesc dsd);
     }
 
     /**
@@ -2092,6 +2293,7 @@
      *
      * @param listener the callback that will be run
      */
+    // This is a synchronous call.
     public abstract void setOnDrmConfigHelper(OnDrmConfigHelper listener);
 
     /**
@@ -2102,42 +2304,40 @@
         /**
          * Called to indicate DRM info is available
          *
-         * @param mp       the {@code MediaPlayer2} associated with this callback
-         * @param drmInfo  DRM info of the source including PSSH, and subset
-         *                 of crypto schemes supported by this device
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the DataSourceDesc of this data source
+         * @param drmInfo DRM info of the source including PSSH, and subset
+         *                of crypto schemes supported by this device
          */
-        public void onDrmInfo(MediaPlayer2 mp, DrmInfo drmInfo) { }
+        public void onDrmInfo(MediaPlayer2 mp, DataSourceDesc dsd, DrmInfo drmInfo) { }
 
         /**
-         * Called to notify the client that {@code prepareDrm} is finished and ready for key request/response.
+         * Called to notify the client that {@link #prepareDrm} is finished and ready for
+         * key request/response.
          *
-         * @param mp      the {@code MediaPlayer2} associated with this callback
-         * @param status  the result of DRM preparation which can be
-         * {@link #PREPARE_DRM_STATUS_SUCCESS},
-         * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR},
-         * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or
-         * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}.
+         * @param mp the {@code MediaPlayer2} associated with this callback
+         * @param dsd the DataSourceDesc of this data source
+         * @param status the result of DRM preparation.
          */
-        public void onDrmPrepared(MediaPlayer2 mp, @PrepareDrmStatusCode int status) { }
-
+        public void onDrmPrepared(
+                MediaPlayer2 mp, DataSourceDesc dsd, @PrepareDrmStatusCode int status) { }
     }
 
     /**
-     * Register a callback to be invoked when the media source is ready
-     * for playback.
+     * Sets the callback to be invoked when the media source is ready for playback.
      *
      * @param eventCallback the callback that will be run
      * @param executor the executor through which the callback should be invoked
      */
-    public abstract void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    // This is a synchronous call.
+    public abstract void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback);
 
     /**
-     * Unregisters a {@link DrmEventCallback}.
-     *
-     * @param callback a {@link DrmEventCallback} to unregister
+     * Clears the {@link DrmEventCallback}.
      */
-    public abstract void unregisterDrmEventCallback(DrmEventCallback callback);
+    // This is a synchronous call.
+    public abstract void clearDrmEventCallback();
 
     /**
      * The status codes for {@link DrmEventCallback#onDrmPrepared} listener.
@@ -2164,7 +2364,7 @@
 
 
     /** @hide */
-    @IntDef({
+    @IntDef(flag = false, prefix = "PREPARE_DRM_STATUS", value = {
         PREPARE_DRM_STATUS_SUCCESS,
         PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
         PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
@@ -2183,10 +2383,10 @@
     /**
      * Prepares the DRM for the current source
      * <p>
-     * If {@code OnDrmConfigHelper} is registered, it will be called during
+     * If {@link OnDrmConfigHelper} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
      * DRM session. Note that the callback is called synchronously in the thread that called
-     * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
+     * {@link #prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
      * and {@code setDrmPropertyString} calls and refrain from any lengthy operation.
      * <p>
      * If the device has not been provisioned before, this call also provisions the device
@@ -2216,6 +2416,7 @@
      * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
      *                                            the request denied by the provisioning server
      */
+    // This is a synchronous call.
     public abstract void prepareDrm(@NonNull UUID uuid)
             throws UnsupportedSchemeException, ResourceBusyException,
                    ProvisioningNetworkErrorException, ProvisioningServerErrorException;
@@ -2229,20 +2430,21 @@
      *
      * @throws NoDrmSchemeException if there is no active DRM session to release
      */
+    // This is an asynchronous call.
     public abstract void releaseDrm() throws NoDrmSchemeException;
 
     /**
      * A key request/response exchange occurs between the app and a license server
      * to obtain or release keys used to decrypt encrypted content.
      * <p>
-     * getKeyRequest() is used to obtain an opaque key request byte array that is
+     * getDrmKeyRequest() is used to obtain an opaque key request byte array that is
      * delivered to the license server.  The opaque key request byte array is returned
      * in KeyRequest.data.  The recommended URL to deliver the key request to is
      * returned in KeyRequest.defaultUrl.
      * <p>
      * After the app has received the key request response from the server,
      * it should deliver to the response to the DRM engine plugin using the method
-     * {@link #provideKeyResponse}.
+     * {@link #provideDrmKeyResponse}.
      *
      * @param keySetId is the key-set identifier of the offline keys being released when keyType is
      * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when
@@ -2269,22 +2471,23 @@
      * @throws NoDrmSchemeException if there is no active DRM session
      */
     @NonNull
-    public abstract MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
+    public abstract MediaDrm.KeyRequest getDrmKeyRequest(
+            @Nullable byte[] keySetId, @Nullable byte[] initData,
             @Nullable String mimeType, @MediaDrm.KeyType int keyType,
             @Nullable Map<String, String> optionalParameters)
             throws NoDrmSchemeException;
 
     /**
      * A key response is received from the license server by the app, then it is
-     * provided to the DRM engine plugin using provideKeyResponse. When the
+     * provided to the DRM engine plugin using provideDrmKeyResponse. When the
      * response is for an offline key request, a key-set identifier is returned that
      * can be used to later restore the keys to a new session with the method
-     * {@ link # restoreKeys}.
+     * {@ link # restoreDrmKeys}.
      * When the response is for a streaming or release request, null is returned.
      *
      * @param keySetId When the response is for a release request, keySetId identifies
      * the saved key associated with the release request (i.e., the same keySetId
-     * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the
+     * passed to the earlier {@ link # getDrmKeyRequest} call. It MUST be null when the
      * response is for either streaming or offline key requests.
      *
      * @param response the byte array response from the server
@@ -2293,16 +2496,19 @@
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
      */
-    public abstract byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
+    // This is a synchronous call.
+    public abstract byte[] provideDrmKeyResponse(
+            @Nullable byte[] keySetId, @NonNull byte[] response)
             throws NoDrmSchemeException, DeniedByServerException;
 
     /**
      * Restore persisted offline keys into a new session.  keySetId identifies the
-     * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
+     * keys to load, obtained from a prior call to {@link #provideDrmKeyResponse}.
      *
      * @param keySetId identifies the saved key set to restore
      */
-    public abstract void restoreKeys(@NonNull byte[] keySetId)
+    // This is an asynchronous call.
+    public abstract void restoreDrmKeys(@NonNull byte[] keySetId)
             throws NoDrmSchemeException;
 
     /**
@@ -2315,7 +2521,8 @@
      * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
      */
     @NonNull
-    public abstract String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName)
+    public abstract String getDrmPropertyString(
+            @NonNull @MediaDrm.StringProperty String propertyName)
             throws NoDrmSchemeException;
 
     /**
@@ -2328,8 +2535,9 @@
      * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
      * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
      */
-    public abstract void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName,
-                                     @NonNull String value)
+    // This is a synchronous call.
+    public abstract void setDrmPropertyString(
+            @NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value)
             throws NoDrmSchemeException;
 
     /**
@@ -2473,4 +2681,38 @@
         public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
 
     }
+
+    /**
+       Constant to retrieve only the new metadata since the last
+       call.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean METADATA_UPDATE_ONLY = true;
+
+    /**
+       Constant to retrieve all the metadata.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean METADATA_ALL = false;
+
+    /**
+       Constant to enable the metadata filter during retrieval.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean APPLY_METADATA_FILTER = true;
+
+    /**
+       Constant to disable the metadata filter during retrieval.
+       // FIXME: unhide.
+       // FIXME: add link to getMetadata(boolean, boolean)
+       {@hide}
+     */
+    public static final boolean BYPASS_METADATA_FILTER = false;
+
 }
diff --git a/android/media/MediaPlayer2Impl.java b/android/media/MediaPlayer2Impl.java
index 86a285c..56423fd 100644
--- a/android/media/MediaPlayer2Impl.java
+++ b/android/media/MediaPlayer2Impl.java
@@ -17,7 +17,6 @@
 package android.media;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
@@ -25,8 +24,10 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.graphics.SurfaceTexture;
+import android.media.SubtitleController.Anchor;
+import android.media.SubtitleTrack.RenderingWidget;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -34,31 +35,19 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.Process;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
-import android.util.ArrayMap;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.widget.VideoView;
-import android.graphics.SurfaceTexture;
-import android.media.AudioManager;
-import android.media.MediaDrm;
-import android.media.MediaFormat;
-import android.media.MediaPlayer2;
-import android.media.MediaTimeProvider;
-import android.media.PlaybackParams;
-import android.media.SubtitleController;
-import android.media.SubtitleController.Anchor;
-import android.media.SubtitleData;
-import android.media.SubtitleTrack.RenderingWidget;
-import android.media.SyncParams;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -74,485 +63,26 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.AutoCloseable;
-import java.lang.Runnable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
-import java.net.CookieHandler;
-import java.net.CookieManager;
 import java.net.HttpCookie;
 import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
 import java.net.URL;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
-import java.util.Collections;
-import java.util.concurrent.Executor;
 import java.util.HashMap;
-import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Scanner;
 import java.util.Set;
 import java.util.UUID;
 import java.util.Vector;
-
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * MediaPlayer2 class can be used to control playback
- * of audio/video files and streams. An example on how to use the methods in
- * this class can be found in {@link android.widget.VideoView}.
- *
- * <p>Topics covered here are:
- * <ol>
- * <li><a href="#StateDiagram">State Diagram</a>
- * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
- * <li><a href="#Permissions">Permissions</a>
- * <li><a href="#Callbacks">Register informational and error callbacks</a>
- * </ol>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about how to use MediaPlayer2, read the
- * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
- * </div>
- *
- * <a name="StateDiagram"></a>
- * <h3>State Diagram</h3>
- *
- * <p>Playback control of audio/video files and streams is managed as a state
- * machine. The following diagram shows the life cycle and the states of a
- * MediaPlayer2 object driven by the supported playback control operations.
- * The ovals represent the states a MediaPlayer2 object may reside
- * in. The arcs represent the playback control operations that drive the object
- * state transition. There are two types of arcs. The arcs with a single arrow
- * head represent synchronous method calls, while those with
- * a double arrow head represent asynchronous method calls.</p>
- *
- * <p><img src="../../../images/mediaplayer_state_diagram.gif"
- *         alt="MediaPlayer State diagram"
- *         border="0" /></p>
- *
- * <p>From this state diagram, one can see that a MediaPlayer2 object has the
- *    following states:</p>
- * <ul>
- *     <li>When a MediaPlayer2 object is just created using <code>new</code> or
- *         after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
- *         {@link #close()} is called, it is in the <em>End</em> state. Between these
- *         two states is the life cycle of the MediaPlayer2 object.
- *         <ul>
- *         <li>There is a subtle but important difference between a newly constructed
- *         MediaPlayer2 object and the MediaPlayer2 object after {@link #reset()}
- *         is called. It is a programming error to invoke methods such
- *         as {@link #getCurrentPosition()},
- *         {@link #getDuration()}, {@link #getVideoHeight()},
- *         {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- *         {@link #setLooping(boolean)},
- *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()},
- *         {@link #seekTo(long, int)}, {@link #prepare()} or
- *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
- *         methods is called right after a MediaPlayer2 object is constructed,
- *         the user supplied callback method OnErrorListener.onError() won't be
- *         called by the internal player engine and the object state remains
- *         unchanged; but if these methods are called right after {@link #reset()},
- *         the user supplied callback method OnErrorListener.onError() will be
- *         invoked by the internal player engine and the object will be
- *         transfered to the <em>Error</em> state. </li>
- *         <li>It is also recommended that once
- *         a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
- *         so that resources used by the internal player engine associated with the
- *         MediaPlayer2 object can be released immediately. Resource may include
- *         singleton resources such as hardware acceleration components and
- *         failure to call {@link #close()} may cause subsequent instances of
- *         MediaPlayer2 objects to fallback to software implementations or fail
- *         altogether. Once the MediaPlayer2
- *         object is in the <em>End</em> state, it can no longer be used and
- *         there is no way to bring it back to any other state. </li>
- *         <li>Furthermore,
- *         the MediaPlayer2 objects created using <code>new</code> is in the
- *         <em>Idle</em> state.
- *         </li>
- *         </ul>
- *         </li>
- *     <li>In general, some playback control operation may fail due to various
- *         reasons, such as unsupported audio/video format, poorly interleaved
- *         audio/video, resolution too high, streaming timeout, and the like.
- *         Thus, error reporting and recovery is an important concern under
- *         these circumstances. Sometimes, due to programming errors, invoking a playback
- *         control operation in an invalid state may also occur. Under all these
- *         error conditions, the internal player engine invokes a user supplied
- *         EventCallback.onError() method if an EventCallback has been
- *         registered beforehand via
- *         {@link #registerEventCallback(Executor, EventCallback)}.
- *         <ul>
- *         <li>It is important to note that once an error occurs, the
- *         MediaPlayer2 object enters the <em>Error</em> state (except as noted
- *         above), even if an error listener has not been registered by the application.</li>
- *         <li>In order to reuse a MediaPlayer2 object that is in the <em>
- *         Error</em> state and recover from the error,
- *         {@link #reset()} can be called to restore the object to its <em>Idle</em>
- *         state.</li>
- *         <li>It is good programming practice to have your application
- *         register a OnErrorListener to look out for error notifications from
- *         the internal player engine.</li>
- *         <li>IllegalStateException is
- *         thrown to prevent programming errors such as calling {@link #prepare()},
- *         {@link #prepareAsync()}, {@link #setDataSource(DataSourceDesc)}, or
- *         {@code setPlaylist} methods in an invalid state. </li>
- *         </ul>
- *         </li>
- *     <li>Calling
- *         {@link #setDataSource(DataSourceDesc)}, or
- *         {@code setPlaylist} transfers a
- *         MediaPlayer2 object in the <em>Idle</em> state to the
- *         <em>Initialized</em> state.
- *         <ul>
- *         <li>An IllegalStateException is thrown if
- *         setDataSource() or setPlaylist() is called in any other state.</li>
- *         <li>It is good programming
- *         practice to always look out for <code>IllegalArgumentException</code>
- *         and <code>IOException</code> that may be thrown from
- *         <code>setDataSource</code> and <code>setPlaylist</code> methods.</li>
- *         </ul>
- *         </li>
- *     <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
- *         before playback can be started.
- *         <ul>
- *         <li>There are two ways (synchronous vs.
- *         asynchronous) that the <em>Prepared</em> state can be reached:
- *         either a call to {@link #prepare()} (synchronous) which
- *         transfers the object to the <em>Prepared</em> state once the method call
- *         returns, or a call to {@link #prepareAsync()} (asynchronous) which
- *         first transfers the object to the <em>Preparing</em> state after the
- *         call returns (which occurs almost right way) while the internal
- *         player engine continues working on the rest of preparation work
- *         until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
- *         the internal player engine then calls a user supplied callback method,
- *         onPrepared() of the EventCallback interface, if an
- *         EventCallback is registered beforehand via {@link
- *         #registerEventCallback(Executor, EventCallback)}.</li>
- *         <li>It is important to note that
- *         the <em>Preparing</em> state is a transient state, and the behavior
- *         of calling any method with side effect while a MediaPlayer2 object is
- *         in the <em>Preparing</em> state is undefined.</li>
- *         <li>An IllegalStateException is
- *         thrown if {@link #prepare()} or {@link #prepareAsync()} is called in
- *         any other state.</li>
- *         <li>While in the <em>Prepared</em> state, properties
- *         such as audio/sound volume, screenOnWhilePlaying, looping can be
- *         adjusted by invoking the corresponding set methods.</li>
- *         </ul>
- *         </li>
- *     <li>To start the playback, {@link #play()} must be called. After
- *         {@link #play()} returns successfully, the MediaPlayer2 object is in the
- *         <em>Started</em> state. {@link #isPlaying()} can be called to test
- *         whether the MediaPlayer2 object is in the <em>Started</em> state.
- *         <ul>
- *         <li>While in the <em>Started</em> state, the internal player engine calls
- *         a user supplied EventCallback.onBufferingUpdate() callback
- *         method if an EventCallback has been registered beforehand
- *         via {@link #registerEventCallback(Executor, EventCallback)}.
- *         This callback allows applications to keep track of the buffering status
- *         while streaming audio/video.</li>
- *         <li>Calling {@link #play()} has not effect
- *         on a MediaPlayer2 object that is already in the <em>Started</em> state.</li>
- *         </ul>
- *         </li>
- *     <li>Playback can be paused and stopped, and the current playback position
- *         can be adjusted. Playback can be paused via {@link #pause()}. When the call to
- *         {@link #pause()} returns, the MediaPlayer2 object enters the
- *         <em>Paused</em> state. Note that the transition from the <em>Started</em>
- *         state to the <em>Paused</em> state and vice versa happens
- *         asynchronously in the player engine. It may take some time before
- *         the state is updated in calls to {@link #isPlaying()}, and it can be
- *         a number of seconds in the case of streamed content.
- *         <ul>
- *         <li>Calling {@link #play()} to resume playback for a paused
- *         MediaPlayer2 object, and the resumed playback
- *         position is the same as where it was paused. When the call to
- *         {@link #play()} returns, the paused MediaPlayer2 object goes back to
- *         the <em>Started</em> state.</li>
- *         <li>Calling {@link #pause()} has no effect on
- *         a MediaPlayer2 object that is already in the <em>Paused</em> state.</li>
- *         </ul>
- *         </li>
- *     <li>The playback position can be adjusted with a call to
- *         {@link #seekTo(long, int)}.
- *         <ul>
- *         <li>Although the asynchronuous {@link #seekTo(long, int)}
- *         call returns right away, the actual seek operation may take a while to
- *         finish, especially for audio/video being streamed. When the actual
- *         seek operation completes, the internal player engine calls a user
- *         supplied EventCallback.onSeekComplete() if an EventCallback
- *         has been registered beforehand via
- *         {@link #registerEventCallback(Executor, EventCallback)}.</li>
- *         <li>Please
- *         note that {@link #seekTo(long, int)} can also be called in the other states,
- *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
- *         </em> state. When {@link #seekTo(long, int)} is called in those states,
- *         one video frame will be displayed if the stream has video and the requested
- *         position is valid.
- *         </li>
- *         <li>Furthermore, the actual current playback position
- *         can be retrieved with a call to {@link #getCurrentPosition()}, which
- *         is helpful for applications such as a Music player that need to keep
- *         track of the playback progress.</li>
- *         </ul>
- *         </li>
- *     <li>When the playback reaches the end of stream, the playback completes.
- *         <ul>
- *         <li>If the looping mode was being set to <var>true</var>with
- *         {@link #setLooping(boolean)}, the MediaPlayer2 object shall remain in
- *         the <em>Started</em> state.</li>
- *         <li>If the looping mode was set to <var>false
- *         </var>, the player engine calls a user supplied callback method,
- *         EventCallback.onCompletion(), if an EventCallback is registered
- *         beforehand via {@link #registerEventCallback(Executor, EventCallback)}.
- *         The invoke of the callback signals that the object is now in the <em>
- *         PlaybackCompleted</em> state.</li>
- *         <li>While in the <em>PlaybackCompleted</em>
- *         state, calling {@link #play()} can restart the playback from the
- *         beginning of the audio/video source.</li>
- * </ul>
- *
- *
- * <a name="Valid_and_Invalid_States"></a>
- * <h3>Valid and invalid states</h3>
- *
- * <table border="0" cellspacing="0" cellpadding="0">
- * <tr><td>Method Name </p></td>
- *     <td>Valid Sates </p></td>
- *     <td>Invalid States </p></td>
- *     <td>Comments </p></td></tr>
- * <tr><td>attachAuxEffect </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- *     <td>{Idle, Error} </p></td>
- *     <td>This method must be called after setDataSource or setPlaylist.
- *     Calling it does not change the object state. </p></td></tr>
- * <tr><td>getAudioSessionId </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>getCurrentPosition </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted} </p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change the
- *         state. Calling this method in an invalid state transfers the object
- *         to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getDuration </p></td>
- *     <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- *     <td>{Idle, Initialized, Error} </p></td>
- *     <td>Successful invoke of this method in a valid state does not change the
- *         state. Calling this method in an invalid state transfers the object
- *         to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoHeight </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change the
- *         state. Calling this method in an invalid state transfers the object
- *         to the <em>Error</em> state.  </p></td></tr>
- * <tr><td>getVideoWidth </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an invalid state transfers the
- *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>isPlaying </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an invalid state transfers the
- *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>pause </p></td>
- *     <td>{Started, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Paused</em> state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepare </p></td>
- *     <td>{Initialized, Stopped} </p></td>
- *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Prepared</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>prepareAsync </p></td>
- *     <td>{Initialized, Stopped} </p></td>
- *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Preparing</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>release </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>After {@link #close()}, the object is no longer available. </p></td></tr>
- * <tr><td>reset </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted, Error}</p></td>
- *     <td>{}</p></td>
- *     <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
- * <tr><td>seekTo </p></td>
- *     <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
- *     <td>{Idle, Initialized, Stopped, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an invalid state transfers the
- *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>setAudioAttributes </p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method does not change the state. In order for the
- *         target audio attributes type to become effective, this method must be called before
- *         prepare() or prepareAsync().</p></td></tr>
- * <tr><td>setAudioSessionId </p></td>
- *     <td>{Idle} </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- *          Error} </p></td>
- *     <td>This method must be called in idle state as the audio session ID must be known before
- *         calling setDataSource or setPlaylist. Calling it does not change the object
- *         state. </p></td></tr>
- * <tr><td>setAudioStreamType (deprecated)</p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method does not change the state. In order for the
- *         target audio stream type to become effective, this method must be called before
- *         prepare() or prepareAsync().</p></td></tr>
- * <tr><td>setAuxEffectSendLevel </p></td>
- *     <td>any</p></td>
- *     <td>{} </p></td>
- *     <td>Calling this method does not change the object state. </p></td></tr>
- * <tr><td>setDataSource </p></td>
- *     <td>{Idle} </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- *          Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Initialized</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setPlaylist </p></td>
- *     <td>{Idle} </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- *          Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Initialized</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setDisplay </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setSurface </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setVideoScalingMode </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- *     <td>{Idle, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>setLooping </p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *         PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>isLooping </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>registerDrmEventCallback </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>registerEventCallback </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setPlaybackParams</p></td>
- *     <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
- *     <td>{Idle, Stopped} </p></td>
- *     <td>This method will change state in some cases, depending on when it's called.
- *         </p></td></tr>
- * <tr><td>setScreenOnWhilePlaying</></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state.  </p></td></tr>
- * <tr><td>setVolume </p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.
- * <tr><td>setWakeMode </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state.</p></td></tr>
- * <tr><td>start </p></td>
- *     <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Stopped, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Started</em> state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>stop </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Stopped</em> state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>getTrackInfo </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>addTimedTextSource </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>selectTrack </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>deselectTrack </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- *
- * </table>
- *
- * <a name="Permissions"></a>
- * <h3>Permissions</h3>
- * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
- * android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * element.
- *
- * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
- * when used with network-based content.
- *
- * <a name="Callbacks"></a>
- * <h3>Callbacks</h3>
- * <p>Applications may want to register for informational and error
- * events in order to be informed of some internal state update and
- * possible runtime errors during playback or streaming. Registration for
- * these events is done by properly setting the appropriate listeners (via calls
- * to
- * {@link #registerEventCallback(Executor, EventCallback)},
- * {@link #registerDrmEventCallback(Executor, DrmEventCallback)}).
- * In order to receive the respective callback
- * associated with these listeners, applications are required to create
- * MediaPlayer2 objects on a thread with its own Looper running (main UI
- * thread by default has a Looper running).
- *
  * @hide
  */
 public final class MediaPlayer2Impl extends MediaPlayer2 {
@@ -572,18 +102,27 @@
     private boolean mScreenOnWhilePlaying;
     private boolean mStayAwake;
     private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
-    private int mUsage = -1;
-    private boolean mBypassInterruptionPolicy;
     private final CloseGuard mGuard = CloseGuard.get();
 
-    private List<DataSourceDesc> mPlaylist;
-    private int mPLCurrentIndex = 0;
-    private int mPLNextIndex = -1;
-    private int mLoopingMode = LOOPING_MODE_NONE;
+    private final Object mSrcLock = new Object();
+    //--- guarded by |mSrcLock| start
+    private long mSrcIdGenerator = 0;
+    private DataSourceDesc mCurrentDSD;
+    private long mCurrentSrcId = mSrcIdGenerator++;
+    private List<DataSourceDesc> mNextDSDs;
+    private long mNextSrcId = mSrcIdGenerator++;
+    private int mNextSourceState = NEXT_SOURCE_STATE_INIT;
+    private boolean mNextSourcePlayPending = false;
+    //--- guarded by |mSrcLock| end
+
+    private AtomicInteger mBufferedPercentageCurrent = new AtomicInteger(0);
+    private AtomicInteger mBufferedPercentageNext = new AtomicInteger(0);
+    private volatile float mVolume = 1.0f;
 
     // Modular DRM
-    private UUID mDrmUUID;
     private final Object mDrmLock = new Object();
+    //--- guarded by |mDrmLock| start
+    private UUID mDrmUUID;
     private DrmInfoImpl mDrmInfoImpl;
     private MediaDrm mDrmObj;
     private byte[] mDrmSessionId;
@@ -593,6 +132,15 @@
     private boolean mDrmProvisioningInProgress;
     private boolean mPrepareDrmInProgress;
     private ProvisioningThread mDrmProvisioningThread;
+    //--- guarded by |mDrmLock| end
+
+    private HandlerThread mHandlerThread;
+    private final Handler mTaskHandler;
+    private final Object mTaskLock = new Object();
+    @GuardedBy("mTaskLock")
+    private final List<Task> mPendingTasks = new LinkedList<>();
+    @GuardedBy("mTaskLock")
+    private Task mCurrentTask;
 
     /**
      * Default constructor.
@@ -610,6 +158,11 @@
             mEventHandler = null;
         }
 
+        mHandlerThread = new HandlerThread("MediaPlayer2TaskThread");
+        mHandlerThread.start();
+        looper = mHandlerThread.getLooper();
+        mTaskHandler = new Handler(looper);
+
         mTimeProvider = new TimeProvider(this);
         mOpenSubtitleSources = new Vector<InputStream>();
         mGuard.open("close");
@@ -620,6 +173,436 @@
         native_setup(new WeakReference<MediaPlayer2Impl>(this));
     }
 
+    /**
+     * Releases the resources held by this {@code MediaPlayer2} object.
+     *
+     * It is considered good practice to call this method when you're
+     * done using the MediaPlayer2. In particular, whenever an Activity
+     * of an application is paused (its onPause() method is called),
+     * or stopped (its onStop() method is called), this method should be
+     * invoked to release the MediaPlayer2 object, unless the application
+     * has a special need to keep the object around. In addition to
+     * unnecessary resources (such as memory and instances of codecs)
+     * being held, failure to call this method immediately if a
+     * MediaPlayer2 object is no longer needed may also lead to
+     * continuous battery consumption for mobile devices, and playback
+     * failure for other applications if no multiple instances of the
+     * same codec are supported on a device. Even if multiple instances
+     * of the same codec are supported, some performance degradation
+     * may be expected when unnecessary multiple instances are used
+     * at the same time.
+     *
+     * {@code close()} may be safely called after a prior {@code close()}.
+     * This class implements the Java {@code AutoCloseable} interface and
+     * may be used with try-with-resources.
+     */
+    @Override
+    public void close() {
+        synchronized (mGuard) {
+            release();
+        }
+    }
+
+    /**
+     * Starts or resumes playback. If playback had previously been paused,
+     * playback will continue from where it was paused. If playback had
+     * been stopped, or never started before, playback will start at the
+     * beginning.
+     *
+     * @throws IllegalStateException if it is called in an invalid state
+     */
+    @Override
+    public void play() {
+        addTask(new Task(CALL_COMPLETED_PLAY, false) {
+            @Override
+            void process() {
+                stayAwake(true);
+                _start();
+            }
+        });
+    }
+
+    private native void _start() throws IllegalStateException;
+
+    /**
+     * Prepares the player for playback, asynchronously.
+     *
+     * After setting the datasource and the display surface, you need to either
+     * call prepare(). For streams, you should call prepare(),
+     * which returns immediately, rather than blocking until enough data has been
+     * buffered.
+     *
+     * @throws IllegalStateException if it is called in an invalid state
+     */
+    @Override
+    public void prepare() {
+        addTask(new Task(CALL_COMPLETED_PREPARE, true) {
+            @Override
+            void process() {
+                _prepare();
+            }
+        });
+    }
+
+    public native void _prepare();
+
+    /**
+     * Pauses playback. Call play() to resume.
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     */
+    @Override
+    public void pause() {
+        addTask(new Task(CALL_COMPLETED_PAUSE, false) {
+            @Override
+            void process() {
+                stayAwake(false);
+                _pause();
+            }
+        });
+    }
+
+    private native void _pause() throws IllegalStateException;
+
+    /**
+     * Tries to play next data source if applicable.
+     *
+     * @throws IllegalStateException if it is called in an invalid state
+     */
+    @Override
+    public void skipToNext() {
+        addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
+            @Override
+            void process() {
+                // TODO: switch to next data source and play
+            }
+        });
+    }
+
+    /**
+     * Gets the current playback position.
+     *
+     * @return the current position in milliseconds
+     */
+    @Override
+    public native long getCurrentPosition();
+
+    /**
+     * Gets the duration of the file.
+     *
+     * @return the duration in milliseconds, if no duration is available
+     *         (for example, if streaming live content), -1 is returned.
+     */
+    @Override
+    public native long getDuration();
+
+    /**
+     * Gets the current buffered media source position received through progressive downloading.
+     * The received buffering percentage indicates how much of the content has been buffered
+     * or played. For example a buffering update of 80 percent when half the content
+     * has already been played indicates that the next 30 percent of the
+     * content to play has been buffered.
+     *
+     * @return the current buffered media source position in milliseconds
+     */
+    @Override
+    public long getBufferedPosition() {
+        // Use cached buffered percent for now.
+        return getDuration() * mBufferedPercentageCurrent.get() / 100;
+    }
+
+    @Override
+    public @PlayerState int getPlayerState() {
+        int mediaplayer2State = getMediaPlayer2State();
+        int playerState;
+        switch (mediaplayer2State) {
+            case MEDIAPLAYER2_STATE_IDLE:
+                playerState = PLAYER_STATE_IDLE;
+                break;
+            case MEDIAPLAYER2_STATE_PREPARED:
+            case MEDIAPLAYER2_STATE_PAUSED:
+                playerState = PLAYER_STATE_PAUSED;
+                break;
+            case MEDIAPLAYER2_STATE_PLAYING:
+                playerState = PLAYER_STATE_PLAYING;
+                break;
+            case MEDIAPLAYER2_STATE_ERROR:
+            default:
+                playerState = PLAYER_STATE_ERROR;
+                break;
+        }
+
+        return playerState;
+    }
+
+    /**
+     * Gets the current buffering state of the player.
+     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+     * buffered.
+     */
+    @Override
+    public @BuffState int getBufferingState() {
+        // TODO: use cached state or call native function.
+        return BUFFERING_STATE_UNKNOWN;
+    }
+
+    /**
+     * Sets the audio attributes for this MediaPlayer2.
+     * See {@link AudioAttributes} for how to build and configure an instance of this class.
+     * You must call this method before {@link #prepare()} in order
+     * for the audio attributes to become effective thereafter.
+     * @param attributes a non-null set of audio attributes
+     * @throws IllegalArgumentException if the attributes are null or invalid.
+     */
+    @Override
+    public void setAudioAttributes(@NonNull AudioAttributes attributes) {
+        addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
+            @Override
+            void process() {
+                if (attributes == null) {
+                    final String msg = "Cannot set AudioAttributes to null";
+                    throw new IllegalArgumentException(msg);
+                }
+                Parcel pattributes = Parcel.obtain();
+                attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
+                setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
+                pattributes.recycle();
+            }
+        });
+    }
+
+    @Override
+    public @NonNull AudioAttributes getAudioAttributes() {
+        Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
+        AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes);
+        pattributes.recycle();
+        return attributes;
+    }
+
+    /**
+     * Sets the data source as described by a DataSourceDesc.
+     *
+     * @param dsd the descriptor of data source you want to play
+     * @throws IllegalStateException if it is called in an invalid state
+     * @throws NullPointerException if dsd is null
+     */
+    @Override
+    public void setDataSource(@NonNull DataSourceDesc dsd) {
+        addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+                // TODO: setDataSource could update exist data source
+                synchronized (mSrcLock) {
+                    mCurrentDSD = dsd;
+                    mCurrentSrcId = mSrcIdGenerator++;
+                    try {
+                        handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Sets a single data source as described by a DataSourceDesc which will be played
+     * after current data source is finished.
+     *
+     * @param dsd the descriptor of data source you want to play after current one
+     * @throws IllegalStateException if it is called in an invalid state
+     * @throws NullPointerException if dsd is null
+     */
+    @Override
+    public void setNextDataSource(@NonNull DataSourceDesc dsd) {
+        addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+                synchronized (mSrcLock) {
+                    mNextDSDs = new ArrayList<DataSourceDesc>(1);
+                    mNextDSDs.add(dsd);
+                    mNextSrcId = mSrcIdGenerator++;
+                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mNextSourcePlayPending = false;
+                }
+                int state = getMediaPlayer2State();
+                if (state != MEDIAPLAYER2_STATE_IDLE) {
+                    synchronized (mSrcLock) {
+                        prepareNextDataSource_l();
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Sets a list of data sources to be played sequentially after current data source is done.
+     *
+     * @param dsds the list of data sources you want to play after current one
+     * @throws IllegalStateException if it is called in an invalid state
+     * @throws IllegalArgumentException if dsds is null or empty, or contains null DataSourceDesc
+     */
+    @Override
+    public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
+        addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
+            @Override
+            void process() {
+                if (dsds == null || dsds.size() == 0) {
+                    throw new IllegalArgumentException("data source list cannot be null or empty.");
+                }
+                for (DataSourceDesc dsd : dsds) {
+                    if (dsd == null) {
+                        throw new IllegalArgumentException(
+                                "DataSourceDesc in the source list cannot be null.");
+                    }
+                }
+
+                synchronized (mSrcLock) {
+                    mNextDSDs = new ArrayList(dsds);
+                    mNextSrcId = mSrcIdGenerator++;
+                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mNextSourcePlayPending = false;
+                }
+                int state = getMediaPlayer2State();
+                if (state != MEDIAPLAYER2_STATE_IDLE) {
+                    synchronized (mSrcLock) {
+                        prepareNextDataSource_l();
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public @NonNull DataSourceDesc getCurrentDataSource() {
+        synchronized (mSrcLock) {
+            return mCurrentDSD;
+        }
+    }
+
+    /**
+     * Configures the player to loop on the current data source.
+     * @param loop true if the current data source is meant to loop.
+     */
+    @Override
+    public void loopCurrent(boolean loop) {
+        addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
+            @Override
+            void process() {
+                // TODO: set the looping mode, send notification
+                setLooping(loop);
+            }
+        });
+    }
+
+    private native void setLooping(boolean looping);
+
+    /**
+     * Sets the playback speed.
+     * A value of 1.0f is the default playback value.
+     * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
+     * before using negative values.<br>
+     * After changing the playback speed, it is recommended to query the actual speed supported
+     * by the player, see {@link #getPlaybackSpeed()}.
+     * @param speed the desired playback speed
+     */
+    @Override
+    public void setPlaybackSpeed(float speed) {
+        addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_SPEED, false) {
+            @Override
+            void process() {
+                _setPlaybackParams(getPlaybackParams().setSpeed(speed));
+            }
+        });
+    }
+
+    /**
+     * Returns the actual playback speed to be used by the player when playing.
+     * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
+     * @return the actual playback speed
+     */
+    @Override
+    public float getPlaybackSpeed() {
+        return getPlaybackParams().getSpeed();
+    }
+
+    /**
+     * Indicates whether reverse playback is supported.
+     * Reverse playback is indicated by negative playback speeds, see
+     * {@link #setPlaybackSpeed(float)}.
+     * @return true if reverse playback is supported.
+     */
+    @Override
+    public boolean isReversePlaybackSupported() {
+        return false;
+    }
+
+    /**
+     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
+     * on the audio samples.
+     * Note that this volume is specific to the player, and is separate from stream volume
+     * used across the platform.<br>
+     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
+     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
+     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
+     */
+    @Override
+    public void setPlayerVolume(float volume) {
+        addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
+            @Override
+            void process() {
+                mVolume = volume;
+                _setVolume(volume, volume);
+            }
+        });
+    }
+
+    private native void _setVolume(float leftVolume, float rightVolume);
+
+    /**
+     * Returns the current volume of this player to this player.
+     * Note that it does not take into account the associated stream volume.
+     * @return the player volume.
+     */
+    @Override
+    public float getPlayerVolume() {
+        return mVolume;
+    }
+
+    /**
+     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
+     */
+    @Override
+    public float getMaxPlayerVolume() {
+        return 1.0f;
+    }
+
+    /**
+     * Adds a callback to be notified of events for this player.
+     * @param e the {@link Executor} to be used for the events.
+     * @param cb the callback to receive the events.
+     */
+    @Override
+    public void registerPlayerEventCallback(@NonNull Executor e,
+            @NonNull PlayerEventCallback cb) {
+    }
+
+    /**
+     * Removes a previously registered callback for player events
+     * @param cb the callback to remove
+     */
+    @Override
+    public void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb) {
+    }
+
+
+    private static final int NEXT_SOURCE_STATE_ERROR = -1;
+    private static final int NEXT_SOURCE_STATE_INIT = 0;
+    private static final int NEXT_SOURCE_STATE_PREPARING = 1;
+    private static final int NEXT_SOURCE_STATE_PREPARED = 2;
+
     /*
      * Update the MediaPlayer2Impl SurfaceTexture.
      * Call after setting a new display surface.
@@ -677,6 +660,21 @@
         }
     }
 
+    @Override
+    public void notifyWhenCommandLabelReached(Object label) {
+        addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
+            @Override
+            void process() {
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onCommandLabelReached(
+                                MediaPlayer2Impl.this, label));
+                    }
+                }
+            }
+        });
+    }
+
     /**
      * Sets the {@link SurfaceHolder} to use for displaying the video
      * portion of the media.
@@ -727,12 +725,17 @@
      */
     @Override
     public void setSurface(Surface surface) {
-        if (mScreenOnWhilePlaying && surface != null) {
-            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
-        }
-        mSurfaceHolder = null;
-        _setVideoSurface(surface);
-        updateSurfaceScreenOn();
+        addTask(new Task(CALL_COMPLETED_SET_SURFACE, false) {
+            @Override
+            void process() {
+                if (mScreenOnWhilePlaying && surface != null) {
+                    Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
+                }
+                mSurfaceHolder = null;
+                _setVideoSurface(surface);
+                updateSurfaceScreenOn();
+            }
+        });
     }
 
     /**
@@ -756,20 +759,25 @@
      */
     @Override
     public void setVideoScalingMode(int mode) {
-        if (!isVideoScalingModeSupported(mode)) {
-            final String msg = "Scaling mode " + mode + " is not supported";
-            throw new IllegalArgumentException(msg);
-        }
-        Parcel request = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        try {
-            request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
-            request.writeInt(mode);
-            invoke(request, reply);
-        } finally {
-            request.recycle();
-            reply.recycle();
-        }
+        addTask(new Task(CALL_COMPLETED_SET_VIDEO_SCALING_MODE, false) {
+            @Override
+            void process() {
+                if (!isVideoScalingModeSupported(mode)) {
+                    final String msg = "Scaling mode " + mode + " is not supported";
+                    throw new IllegalArgumentException(msg);
+                }
+                Parcel request = Parcel.obtain();
+                Parcel reply = Parcel.obtain();
+                try {
+                    request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
+                    request.writeInt(mode);
+                    invoke(request, reply);
+                } finally {
+                    request.recycle();
+                    reply.recycle();
+                }
+            }
+        });
     }
 
     /**
@@ -779,314 +787,51 @@
     public void clearPendingCommands() {
     }
 
-    /**
-     * Sets the data source as described by a DataSourceDesc.
-     *
-     * @param dsd the descriptor of data source you want to play
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws NullPointerException if dsd is null
-     */
-    @Override
-    public void setDataSource(@NonNull DataSourceDesc dsd) throws IOException {
-        Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-        mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>(1));
-        mPlaylist.add(dsd);
-        mPLCurrentIndex = 0;
-        setDataSourcePriv(dsd);
-    }
-
-    /**
-     * Gets the current data source as described by a DataSourceDesc.
-     *
-     * @return the current DataSourceDesc
-     */
-    @Override
-    public DataSourceDesc getCurrentDataSource() {
-        if (mPlaylist == null) {
-            return null;
+    private void addTask(Task task) {
+        synchronized (mTaskLock) {
+            mPendingTasks.add(task);
+            processPendingTask_l();
         }
-        return mPlaylist.get(mPLCurrentIndex);
     }
 
-    /**
-     * Sets the play list.
-     *
-     * If startIndex falls outside play list range, it will be clamped to the nearest index
-     * in the play list.
-     *
-     * @param pl the play list of data source you want to play
-     * @param startIndex the index of the DataSourceDesc in the play list you want to play first
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if pl is null or empty, or pl contains null DataSourceDesc
-     */
-    @Override
-    public void setPlaylist(@NonNull List<DataSourceDesc> pl, int startIndex)
+    @GuardedBy("mTaskLock")
+    private void processPendingTask_l() {
+        if (mCurrentTask != null) {
+            return;
+        }
+        if (!mPendingTasks.isEmpty()) {
+            Task task = mPendingTasks.remove(0);
+            mCurrentTask = task;
+            mTaskHandler.post(task);
+        }
+    }
+
+    private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
             throws IOException {
-        if (pl == null || pl.size() == 0) {
-            throw new IllegalArgumentException("play list cannot be null or empty.");
-        }
-        HashSet ids = new HashSet(pl.size());
-        for (DataSourceDesc dsd : pl) {
-            if (dsd == null) {
-                throw new IllegalArgumentException("DataSourceDesc in play list cannot be null.");
-            }
-            if (ids.add(dsd.getId()) == false) {
-                throw new IllegalArgumentException("DataSourceDesc Id in play list should be unique.");
-            }
-        }
-
-        if (startIndex < 0) {
-            startIndex = 0;
-        } else if (startIndex >= pl.size()) {
-            startIndex = pl.size() - 1;
-        }
-
-        mPlaylist = Collections.synchronizedList(new ArrayList(pl));
-        mPLCurrentIndex = startIndex;
-        setDataSourcePriv(mPlaylist.get(startIndex));
-        // TODO: handle the preparation of next source in the play list.
-        // It should be processed after current source is prepared.
-    }
-
-    /**
-     * Gets a copy of the play list.
-     *
-     * @return a copy of the play list used by {@link MediaPlayer2}
-     */
-    @Override
-    public List<DataSourceDesc> getPlaylist() {
-        if (mPlaylist == null) {
-            return null;
-        }
-        return new ArrayList(mPlaylist);
-    }
-
-    /**
-     * Sets the index of current DataSourceDesc in the play list to be played.
-     *
-     * @param index the index of DataSourceDesc in the play list you want to play
-     * @throws IllegalArgumentException if the play list is null
-     * @throws NullPointerException if index is outside play list range
-     */
-    @Override
-    public void setCurrentPlaylistItem(int index) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
-        if (index < 0 || index >= mPlaylist.size()) {
-            throw new IndexOutOfBoundsException("index is out of play list range.");
-        }
-
-        if (index == mPLCurrentIndex) {
-            return;
-        }
-
-        // TODO: in playing state, stop current source and start to play source of index.
-        mPLCurrentIndex = index;
-    }
-
-    /**
-     * Sets the index of next-to-be-played DataSourceDesc in the play list.
-     *
-     * @param index the index of next-to-be-played DataSourceDesc in the play list
-     * @throws IllegalArgumentException if the play list is null
-     * @throws NullPointerException if index is outside play list range
-     */
-    @Override
-    public void setNextPlaylistItem(int index) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
-        if (index < 0 || index >= mPlaylist.size()) {
-            throw new IndexOutOfBoundsException("index is out of play list range.");
-        }
-
-        if (index == mPLNextIndex) {
-            return;
-        }
-
-        // TODO: prepare the new next-to-be-played DataSourceDesc
-        mPLNextIndex = index;
-    }
-
-    /**
-     * Gets the current index of play list.
-     *
-     * @return the index of the current DataSourceDesc in the play list
-     */
-    @Override
-    public int getCurrentPlaylistItemIndex() {
-        return mPLCurrentIndex;
-    }
-
-    /**
-     * Sets the looping mode of the play list.
-     * The mode shall be one of {@link #LOOPING_MODE_NONE}, {@link #LOOPING_MODE_FULL},
-     * {@link #LOOPING_MODE_SINGLE}, {@link #LOOPING_MODE_SHUFFLE}.
-     *
-     * @param mode the mode in which the play list will be played
-     * @throws IllegalArgumentException if mode is not supported
-     */
-    @Override
-    public void setLoopingMode(@LoopingMode int mode) {
-        if (mode != LOOPING_MODE_NONE
-            && mode != LOOPING_MODE_FULL
-            && mode != LOOPING_MODE_SINGLE
-            && mode != LOOPING_MODE_SHUFFLE) {
-            throw new IllegalArgumentException("mode is not supported.");
-        }
-        mLoopingMode = mode;
-        if (mPlaylist == null) {
-            return;
-        }
-
-        // TODO: handle the new mode if necessary.
-    }
-
-    /**
-     * Gets the looping mode of play list.
-     *
-     * @return the looping mode of the play list
-     */
-    @Override
-    public int getLoopingMode() {
-        return mPLCurrentIndex;
-    }
-
-    /**
-     * Moves the DataSourceDesc at indexFrom in the play list to indexTo.
-     *
-     * @throws IllegalArgumentException if the play list is null
-     * @throws IndexOutOfBoundsException if indexFrom or indexTo is outside play list range
-     */
-    @Override
-    public void movePlaylistItem(int indexFrom, int indexTo) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
-        // TODO: move the DataSourceDesc from indexFrom to indexTo.
-    }
-
-    /**
-     * Removes the DataSourceDesc at index in the play list.
-     *
-     * If index is same as the current index of the play list, current DataSourceDesc
-     * will be stopped and playback moves to next source in the list.
-     *
-     * @return the removed DataSourceDesc at index in the play list
-     * @throws IllegalArgumentException if the play list is null
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     */
-    @Override
-    public DataSourceDesc removePlaylistItem(int index) {
-        if (mPlaylist == null) {
-            throw new IllegalArgumentException("play list has not been set yet.");
-        }
-
-        DataSourceDesc oldDsd = mPlaylist.remove(index);
-        // TODO: if index == mPLCurrentIndex, stop current source and move to next one.
-        // if index == mPLNextIndex, prepare the new next-to-be-played source.
-        return oldDsd;
-    }
-
-    /**
-     * Inserts the DataSourceDesc to the play list at position index.
-     *
-     * This will not change the DataSourceDesc currently being played.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
-     *
-     * @param index the index you want to add dsd to the play list
-     * @param dsd the descriptor of data source you want to add to the play list
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     * @throws NullPointerException if dsd is null
-     */
-    @Override
-    public void addPlaylistItem(int index, DataSourceDesc dsd) {
-        Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-
-        if (mPlaylist == null) {
-            if (index == 0) {
-                mPlaylist = Collections.synchronizedList(new ArrayList<DataSourceDesc>());
-                mPlaylist.add(dsd);
-                mPLCurrentIndex = 0;
-                return;
-            }
-            throw new IllegalArgumentException("index should be 0 for first DataSourceDesc.");
-        }
-
-        long id = dsd.getId();
-        for (DataSourceDesc pldsd : mPlaylist) {
-            if (id == pldsd.getId()) {
-                throw new IllegalArgumentException("Id of dsd already exists in the play list.");
-            }
-        }
-
-        mPlaylist.add(index, dsd);
-        if (index <= mPLCurrentIndex) {
-            ++mPLCurrentIndex;
-        }
-    }
-
-    /**
-     * replaces the DataSourceDesc at index in the play list with given dsd.
-     *
-     * When index is same as the current index of the play list, the current source
-     * will be stopped and the new source will be played, except that if new
-     * and old source only differ on end position and current media position is
-     * smaller then the new end position.
-     *
-     * This will not change the DataSourceDesc currently being played.
-     * If index is less than or equal to the current index of the play list,
-     * the current index of the play list will be incremented correspondingly.
-     *
-     * @param index the index you want to add dsd to the play list
-     * @param dsd the descriptor of data source you want to add to the play list
-     * @throws IndexOutOfBoundsException if index is outside play list range
-     * @throws NullPointerException if dsd is null
-     */
-    @Override
-    public DataSourceDesc editPlaylistItem(int index, DataSourceDesc dsd) {
-        Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-        Preconditions.checkNotNull(mPlaylist, "the play list cannot be null");
-
-        long id = dsd.getId();
-        for (int i = 0; i < mPlaylist.size(); ++i) {
-            if (i == index) {
-                continue;
-            }
-            if (id == mPlaylist.get(i).getId()) {
-                throw new IllegalArgumentException("Id of dsd already exists in the play list.");
-            }
-        }
-
-        // TODO: if needed, stop playback of current source, and start new dsd.
-        DataSourceDesc oldDsd = mPlaylist.set(index, dsd);
-        return mPlaylist.set(index, dsd);
-    }
-
-    private void setDataSourcePriv(@NonNull DataSourceDesc dsd) throws IOException {
         Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
 
         switch (dsd.getType()) {
             case DataSourceDesc.TYPE_CALLBACK:
-                setDataSourcePriv(dsd.getId(),
-                                  dsd.getMedia2DataSource());
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 dsd.getMedia2DataSource());
                 break;
 
             case DataSourceDesc.TYPE_FD:
-                setDataSourcePriv(dsd.getId(),
-                                  dsd.getFileDescriptor(),
-                                  dsd.getFileDescriptorOffset(),
-                                  dsd.getFileDescriptorLength());
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 dsd.getFileDescriptor(),
+                                 dsd.getFileDescriptorOffset(),
+                                 dsd.getFileDescriptorLength());
                 break;
 
             case DataSourceDesc.TYPE_URI:
-                setDataSourcePriv(dsd.getId(),
-                                  dsd.getUriContext(),
-                                  dsd.getUri(),
-                                  dsd.getUriHeaders(),
-                                  dsd.getUriCookies());
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 dsd.getUriContext(),
+                                 dsd.getUri(),
+                                 dsd.getUriHeaders(),
+                                 dsd.getUriCookies());
                 break;
 
             default:
@@ -1113,66 +858,59 @@
      * @throws NullPointerException     if context or uri is null
      * @throws IOException              if uri has a file scheme and an I/O error occurs
      */
-    private void setDataSourcePriv(long srcId, @NonNull Context context, @NonNull Uri uri,
+    private void handleDataSource(
+            boolean isCurrent, long srcId,
+            @NonNull Context context, @NonNull Uri uri,
             @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
             throws IOException {
-        if (context == null) {
-            throw new NullPointerException("context param can not be null.");
-        }
-
-        if (uri == null) {
-            throw new NullPointerException("uri param can not be null.");
-        }
-
-        if (cookies != null) {
-            CookieHandler cookieHandler = CookieHandler.getDefault();
-            if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
-                throw new IllegalArgumentException("The cookie handler has to be of CookieManager "
-                        + "type when cookies are provided.");
-            }
-        }
-
         // The context and URI usually belong to the calling user. Get a resolver for that user
         // and strip out the userId from the URI if present.
         final ContentResolver resolver = context.getContentResolver();
         final String scheme = uri.getScheme();
         final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
         if (ContentResolver.SCHEME_FILE.equals(scheme)) {
-            setDataSourcePriv(srcId, uri.getPath(), null, null);
+            handleDataSource(isCurrent, srcId, uri.getPath(), null, null);
             return;
-        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
+        }
+
+        if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                 && Settings.AUTHORITY.equals(authority)) {
             // Try cached ringtone first since the actual provider may not be
             // encryption aware, or it may be stored on CE media storage
             final int type = RingtoneManager.getDefaultType(uri);
             final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
             final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
-            if (attemptDataSource(srcId, resolver, cacheUri)) {
+            if (attemptDataSource(isCurrent, srcId, resolver, cacheUri)) {
                 return;
-            } else if (attemptDataSource(srcId, resolver, actualUri)) {
-                return;
-            } else {
-                setDataSourcePriv(srcId, uri.toString(), headers, cookies);
             }
+            if (attemptDataSource(isCurrent, srcId, resolver, actualUri)) {
+                return;
+            }
+            handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
         } else {
             // Try requested Uri locally first, or fallback to media server
-            if (attemptDataSource(srcId, resolver, uri)) {
+            if (attemptDataSource(isCurrent, srcId, resolver, uri)) {
                 return;
-            } else {
-                setDataSourcePriv(srcId, uri.toString(), headers, cookies);
             }
+            handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
         }
     }
 
-    private boolean attemptDataSource(long srcId, ContentResolver resolver, Uri uri) {
+    private boolean attemptDataSource(
+            boolean isCurrent, long srcId, ContentResolver resolver, Uri uri) {
         try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
             if (afd.getDeclaredLength() < 0) {
-                setDataSourcePriv(srcId, afd.getFileDescriptor(), 0, DataSourceDesc.LONG_MAX);
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 afd.getFileDescriptor(),
+                                 0,
+                                 DataSourceDesc.LONG_MAX);
             } else {
-                setDataSourcePriv(srcId,
-                                  afd.getFileDescriptor(),
-                                  afd.getStartOffset(),
-                                  afd.getDeclaredLength());
+                handleDataSource(isCurrent,
+                                 srcId,
+                                 afd.getFileDescriptor(),
+                                 afd.getStartOffset(),
+                                 afd.getDeclaredLength());
             }
             return true;
         } catch (NullPointerException | SecurityException | IOException ex) {
@@ -1181,10 +919,10 @@
         }
     }
 
-    private void setDataSourcePriv(
-            long srcId, String path, Map<String, String> headers, List<HttpCookie> cookies)
-            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
-    {
+    private void handleDataSource(
+            boolean isCurrent, long srcId,
+            String path, Map<String, String> headers, List<HttpCookie> cookies)
+            throws IOException {
         String[] keys = null;
         String[] values = null;
 
@@ -1199,19 +937,21 @@
                 ++i;
             }
         }
-        setDataSourcePriv(srcId, path, keys, values, cookies);
+        handleDataSource(isCurrent, srcId, path, keys, values, cookies);
     }
 
-    private void setDataSourcePriv(long srcId, String path, String[] keys, String[] values,
-            List<HttpCookie> cookies)
-            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
+    private void handleDataSource(boolean isCurrent, long srcId,
+            String path, String[] keys, String[] values, List<HttpCookie> cookies)
+            throws IOException {
         final Uri uri = Uri.parse(path);
         final String scheme = uri.getScheme();
         if ("file".equals(scheme)) {
             path = uri.getPath();
         } else if (scheme != null) {
             // handle non-file sources
-            nativeSetDataSource(
+            nativeHandleDataSourceUrl(
+                isCurrent,
+                srcId,
                 Media2HTTPService.createHTTPService(path, cookies),
                 path,
                 keys,
@@ -1223,16 +963,17 @@
         if (file.exists()) {
             FileInputStream is = new FileInputStream(file);
             FileDescriptor fd = is.getFD();
-            setDataSourcePriv(srcId, fd, 0, DataSourceDesc.LONG_MAX);
+            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX);
             is.close();
         } else {
-            throw new IOException("setDataSourcePriv failed.");
+            throw new IOException("handleDataSource failed.");
         }
     }
 
-    private native void nativeSetDataSource(
-        Media2HTTPService httpService, String path, String[] keys, String[] values)
-        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
+    private native void nativeHandleDataSourceUrl(
+            boolean isCurrent, long srcId,
+            Media2HTTPService httpService, String path, String[] keys, String[] values)
+            throws IOException;
 
     /**
      * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
@@ -1243,76 +984,91 @@
      * @throws IllegalArgumentException if fd is not a valid FileDescriptor
      * @throws IOException if fd can not be read
      */
-    private void setDataSourcePriv(long srcId, FileDescriptor fd, long offset, long length)
-            throws IOException {
-        _setDataSource(fd, offset, length);
+    private void handleDataSource(
+            boolean isCurrent, long srcId,
+            FileDescriptor fd, long offset, long length) throws IOException {
+        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length);
     }
 
-    private native void _setDataSource(FileDescriptor fd, long offset, long length)
-            throws IOException;
+    private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
+            FileDescriptor fd, long offset, long length) throws IOException;
 
     /**
      * @throws IllegalStateException if it is called in an invalid state
      * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
      */
-    private void setDataSourcePriv(long srcId, Media2DataSource dataSource) {
-        _setDataSource(dataSource);
+    private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource) {
+        nativeHandleDataSourceCallback(isCurrent, srcId, dataSource);
     }
 
-    private native void _setDataSource(Media2DataSource dataSource);
+    private native void nativeHandleDataSourceCallback(
+            boolean isCurrent, long srcId, Media2DataSource dataSource);
 
-    /**
-     * Prepares the player for playback, synchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
-     * which blocks until MediaPlayer2 is ready for playback.
-     *
-     * @throws IOException if source can not be accessed
-     * @throws IllegalStateException if it is called in an invalid state
-     * @hide
-     */
-    @Override
-    public void prepare() throws IOException {
-        _prepare();
-        scanInternalSubtitleTracks();
+    // This function shall be called with |mSrcLock| acquired.
+    private void prepareNextDataSource_l() {
+        if (mNextDSDs == null || mNextDSDs.isEmpty()
+                || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
+            // There is no next source or it's in preparing or prepared state.
+            return;
+        }
 
-        // DrmInfo, if any, has been resolved by now.
-        synchronized (mDrmLock) {
-            mDrmInfoResolved = true;
+        try {
+            mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
+            handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
+        } catch (Exception e) {
+            Message msg2 = mEventHandler.obtainMessage(
+                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+            final long nextSrcId = mNextSrcId;
+            mEventHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mEventHandler.handleMessage(msg2, nextSrcId);
+                }
+            });
         }
     }
 
-    private native void _prepare() throws IOException, IllegalStateException;
+    // This function shall be called with |mSrcLock| acquired.
+    private void playNextDataSource_l() {
+        if (mNextDSDs == null || mNextDSDs.isEmpty()) {
+            return;
+        }
 
-    /**
-     * Prepares the player for playback, asynchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
-     * which returns immediately, rather than blocking until enough data has been
-     * buffered.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
-     */
-    @Override
-    public native void prepareAsync();
+        if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
+            // Switch to next source only when it's in prepared state.
+            mCurrentDSD = mNextDSDs.get(0);
+            mCurrentSrcId = mNextSrcId;
+            mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
+            mNextDSDs.remove(0);
+            mNextSrcId = mSrcIdGenerator++;  // make it different from mCurrentSrcId
+            mBufferedPercentageNext.set(0);
+            mNextSourceState = NEXT_SOURCE_STATE_INIT;
+            mNextSourcePlayPending = false;
 
-    /**
-     * Starts or resumes playback. If playback had previously been paused,
-     * playback will continue from where it was paused. If playback had
-     * been stopped, or never started before, playback will start at the
-     * beginning.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
-     */
-    @Override
-    public void play() {
-        stayAwake(true);
-        _start();
+            long srcId = mCurrentSrcId;
+            try {
+                nativePlayNextDataSource(srcId);
+            } catch (Exception e) {
+                Message msg2 = mEventHandler.obtainMessage(
+                        MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+                mEventHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mEventHandler.handleMessage(msg2, srcId);
+                    }
+                });
+            }
+
+            // Wait for MEDIA2_INFO_STARTED_AS_NEXT to prepare next source.
+        } else {
+            if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
+                prepareNextDataSource_l();
+            }
+            mNextSourcePlayPending = true;
+        }
     }
 
-    private native void _start() throws IllegalStateException;
+    private native void nativePlayNextDataSource(long srcId);
 
 
     private int getAudioStreamType() {
@@ -1339,20 +1095,6 @@
 
     private native void _stop() throws IllegalStateException;
 
-    /**
-     * Pauses playback. Call play() to resume.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     */
-    @Override
-    public void pause() {
-        stayAwake(false);
-        _pause();
-    }
-
-    private native void _pause() throws IllegalStateException;
-
     //--------------------------------------------------------------------------
     // Explicit Routing
     //--------------------
@@ -1417,6 +1159,7 @@
     /*
      * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void enableNativeRoutingCallbacksLocked(boolean enabled) {
         if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback(enabled);
@@ -1562,9 +1305,10 @@
      *
      * @return the width of the video, or 0 if there is no video,
      * no display surface was set, or the width has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #registerEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the width is available.
+     * yet. The {@code MediaPlayer2EventCallback} can be registered via
+     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the width
+     * is available.
      */
     @Override
     public native int getVideoWidth();
@@ -1574,9 +1318,10 @@
      *
      * @return the height of the video, or 0 if there is no video,
      * no display surface was set, or the height has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #registerEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the height is available.
+     * yet. The {@code MediaPlayer2EventCallback} can be registered via
+     * {@link #setMediaPlayer2EventCallback(Executor, MediaPlayer2EventCallback)} to provide a
+     * notification {@code MediaPlayer2EventCallback.onVideoSizeChanged} when the height
+     * is available.
      */
     @Override
     public native int getVideoHeight();
@@ -1605,10 +1350,18 @@
      * @return true if currently playing, false otherwise
      * @throws IllegalStateException if the internal player engine has not been
      * initialized or has been released.
+     * @hide
      */
     @Override
     public native boolean isPlaying();
 
+    @Override
+    public @MediaPlayer2State int getMediaPlayer2State() {
+        return native_getMediaPlayer2State();
+    }
+
+    private native int native_getMediaPlayer2State();
+
     /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
@@ -1638,7 +1391,17 @@
      * @hide
      */
     @Override
-    public native void setBufferingParams(@NonNull BufferingParams params);
+    public void setBufferingParams(@NonNull BufferingParams params) {
+        addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(params, "the BufferingParams cannot be null");
+                _setBufferingParams(params);
+            }
+        });
+    }
+
+    private native void _setBufferingParams(@NonNull BufferingParams params);
 
     /**
      * Sets playback rate and audio mode.
@@ -1692,7 +1455,17 @@
      * @throws IllegalArgumentException if params is not supported.
      */
     @Override
-    public native void setPlaybackParams(@NonNull PlaybackParams params);
+    public void setPlaybackParams(@NonNull PlaybackParams params) {
+        addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(params, "the PlaybackParams cannot be null");
+                _setPlaybackParams(params);
+            }
+        });
+    }
+
+    private native void _setPlaybackParams(@NonNull PlaybackParams params);
 
     /**
      * Gets the playback params, containing the current playback rate.
@@ -1715,7 +1488,17 @@
      * @throws IllegalArgumentException if params are not supported.
      */
     @Override
-    public native void setSyncParams(@NonNull SyncParams params);
+    public void setSyncParams(@NonNull SyncParams params) {
+        addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
+            @Override
+            void process() {
+                Preconditions.checkNotNull(params, "the SyncParams cannot be null");
+                _setSyncParams(params);
+            }
+        });
+    }
+
+    private native void _setSyncParams(@NonNull SyncParams params);
 
     /**
      * Gets the A/V sync mode.
@@ -1729,8 +1512,6 @@
     @NonNull
     public native SyncParams getSyncParams();
 
-    private native final void _seekTo(long msec, int mode);
-
     /**
      * Moves the media to specified time position by considering the given mode.
      * <p>
@@ -1762,22 +1543,32 @@
      * @throws IllegalArgumentException if the mode is invalid.
      */
     @Override
-    public void seekTo(long msec, @SeekMode int mode) {
-        if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
-            final String msg = "Illegal seek mode: " + mode;
-            throw new IllegalArgumentException(msg);
-        }
-        // TODO: pass long to native, instead of truncating here.
-        if (msec > Integer.MAX_VALUE) {
-            Log.w(TAG, "seekTo offset " + msec + " is too large, cap to " + Integer.MAX_VALUE);
-            msec = Integer.MAX_VALUE;
-        } else if (msec < Integer.MIN_VALUE) {
-            Log.w(TAG, "seekTo offset " + msec + " is too small, cap to " + Integer.MIN_VALUE);
-            msec = Integer.MIN_VALUE;
-        }
-        _seekTo(msec, mode);
+    public void seekTo(final long msec, @SeekMode int mode) {
+        addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
+            @Override
+            void process() {
+                if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
+                    final String msg = "Illegal seek mode: " + mode;
+                    throw new IllegalArgumentException(msg);
+                }
+                // TODO: pass long to native, instead of truncating here.
+                long posMs = msec;
+                if (posMs > Integer.MAX_VALUE) {
+                    Log.w(TAG, "seekTo offset " + posMs + " is too large, cap to "
+                            + Integer.MAX_VALUE);
+                    posMs = Integer.MAX_VALUE;
+                } else if (posMs < Integer.MIN_VALUE) {
+                    Log.w(TAG, "seekTo offset " + posMs + " is too small, cap to "
+                            + Integer.MIN_VALUE);
+                    posMs = Integer.MIN_VALUE;
+                }
+                _seekTo(posMs, mode);
+            }
+        });
     }
 
+    private native final void _seekTo(long msec, int mode);
+
     /**
      * Get current playback position as a {@link MediaTimestamp}.
      * <p>
@@ -1812,23 +1603,6 @@
     }
 
     /**
-     * Gets the current playback position.
-     *
-     * @return the current position in milliseconds
-     */
-    @Override
-    public native int getCurrentPosition();
-
-    /**
-     * Gets the duration of the file.
-     *
-     * @return the duration in milliseconds, if no duration is available
-     *         (for example, if streaming live content), -1 is returned.
-     */
-    @Override
-    public native int getDuration();
-
-    /**
      * Gets the media metadata.
      *
      * @param update_only controls whether the full set of available
@@ -1914,28 +1688,6 @@
     }
 
     /**
-     * Set the MediaPlayer2 to start when this MediaPlayer2 finishes playback
-     * (i.e. reaches the end of the stream).
-     * The media framework will attempt to transition from this player to
-     * the next as seamlessly as possible. The next player can be set at
-     * any time before completion, but shall be after setDataSource has been
-     * called successfully. The next player must be prepared by the
-     * app, and the application should not call play() on it.
-     * The next MediaPlayer2 must be different from 'this'. An exception
-     * will be thrown if next == this.
-     * The application may call setNextMediaPlayer(null) to indicate no
-     * next player should be started at the end of playback.
-     * If the current player is looping, it will keep looping and the next
-     * player will not be started.
-     *
-     * @param next the player to start after this one completes playback.
-     *
-     * @hide
-     */
-    @Override
-    public native void setNextMediaPlayer(MediaPlayer2 next);
-
-    /**
      * Resets the MediaPlayer2 to its uninitialized state. After calling
      * this method, you will have to initialize it again by setting the
      * data source and calling prepare().
@@ -1960,6 +1712,13 @@
             mTimeProvider = null;
         }
 
+        synchronized (mEventCbLock) {
+            mEventCallbackRecords.clear();
+        }
+        synchronized (mDrmEventCbLock) {
+            mDrmEventCallbackRecords.clear();
+        }
+
         stayAwake(false);
         _reset();
         // make sure none of the listeners get called anymore
@@ -1999,41 +1758,11 @@
      * @param key key indicates the parameter to be set.
      * @param value value of the parameter to be set.
      * @return true if the parameter is set successfully, false otherwise
-     * {@hide}
      */
     private native boolean setParameter(int key, Parcel value);
 
-    /**
-     * Sets the audio attributes for this MediaPlayer2.
-     * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order
-     * for the audio attributes to become effective thereafter.
-     * @param attributes a non-null set of audio attributes
-     * @throws IllegalArgumentException if the attributes are null or invalid.
-     */
-    @Override
-    public void setAudioAttributes(AudioAttributes attributes) {
-        if (attributes == null) {
-            final String msg = "Cannot set AudioAttributes to null";
-            throw new IllegalArgumentException(msg);
-        }
-        mUsage = attributes.getUsage();
-        mBypassInterruptionPolicy = (attributes.getAllFlags()
-                & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
-        Parcel pattributes = Parcel.obtain();
-        attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
-        setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
-        pattributes.recycle();
-    }
+    private native Parcel getParameter(int key);
 
-    /**
-     * Sets the player to be looping or non-looping.
-     *
-     * @param looping whether to loop or not
-     * @hide
-     */
-    @Override
-    public native void setLooping(boolean looping);
 
     /**
      * Checks whether the MediaPlayer2 is looping or non-looping.
@@ -2045,39 +1774,6 @@
     public native boolean isLooping();
 
     /**
-     * Sets the volume on this player.
-     * This API is recommended for balancing the output of audio streams
-     * within an application. Unless you are writing an application to
-     * control user settings, this API should be used in preference to
-     * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
-     * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
-     * UI controls should be scaled logarithmically.
-     *
-     * @param leftVolume left volume scalar
-     * @param rightVolume right volume scalar
-     */
-    /*
-     * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
-     * The single parameter form below is preferred if the channel volumes don't need
-     * to be set independently.
-     */
-    @Override
-    public void setVolume(float leftVolume, float rightVolume) {
-        _setVolume(leftVolume, rightVolume);
-    }
-
-    private native void _setVolume(float leftVolume, float rightVolume);
-
-    /**
-     * Similar, excepts sets volume of all channels to same value.
-     * @hide
-     */
-    @Override
-    public void setVolume(float volume) {
-        setVolume(volume, volume);
-    }
-
-    /**
      * Sets the audio session ID.
      *
      * @param sessionId the audio session ID.
@@ -2095,7 +1791,16 @@
      * @throws IllegalArgumentException if the sessionId is invalid.
      */
     @Override
-    public native void setAudioSessionId(int sessionId);
+    public void setAudioSessionId(int sessionId) {
+        addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
+            @Override
+            void process() {
+                _setAudioSessionId(sessionId);
+            }
+        });
+    }
+
+    private native void _setAudioSessionId(int sessionId);
 
     /**
      * Returns the audio session ID.
@@ -2121,8 +1826,16 @@
      * @param effectId system wide unique id of the effect to attach
      */
     @Override
-    public native void attachAuxEffect(int effectId);
+    public void attachAuxEffect(int effectId) {
+        addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
+            @Override
+            void process() {
+                _attachAuxEffect(effectId);
+            }
+        });
+    }
 
+    private native void _attachAuxEffect(int effectId);
 
     /**
      * Sets the send level of the player to the attached auxiliary effect.
@@ -2138,7 +1851,12 @@
      */
     @Override
     public void setAuxEffectSendLevel(float level) {
-        _setAuxEffectSendLevel(level);
+        addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
+            @Override
+            void process() {
+                _setAuxEffectSendLevel(level);
+            }
+        });
     }
 
     private native void _setAuxEffectSendLevel(float level);
@@ -2181,6 +1899,13 @@
     private native final void native_setup(Object mediaplayer2_this);
     private native final void native_finalize();
 
+    private static native final void native_stream_event_onTearDown(
+            long nativeCallbackPtr, long userDataPtr);
+    private static native final void native_stream_event_onStreamPresentationEnd(
+            long nativeCallbackPtr, long userDataPtr);
+    private static native final void native_stream_event_onStreamDataRequest(
+            long jAudioTrackPtr, long nativeCallbackPtr, long userDataPtr);
+
     /**
      * Class for MediaPlayer2 to return each audio/video/subtitle track's metadata.
      *
@@ -2870,7 +2595,12 @@
      */
     @Override
     public void selectTrack(int index) {
-        selectOrDeselectTrack(index, true /* select */);
+        addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
+            @Override
+            void process() {
+                selectOrDeselectTrack(index, true /* select */);
+            }
+        });
     }
 
     /**
@@ -2889,7 +2619,12 @@
      */
     @Override
     public void deselectTrack(int index) {
-        selectOrDeselectTrack(index, false /* select */);
+        addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
+            @Override
+            void process() {
+                selectOrDeselectTrack(index, false /* select */);
+            }
+        });
     }
 
     private void selectOrDeselectTrack(int index, boolean select)
@@ -2956,83 +2691,6 @@
         }
     }
 
-    /**
-     * Sets the target UDP re-transmit endpoint for the low level player.
-     * Generally, the address portion of the endpoint is an IP multicast
-     * address, although a unicast address would be equally valid.  When a valid
-     * retransmit endpoint has been set, the media player will not decode and
-     * render the media presentation locally.  Instead, the player will attempt
-     * to re-multiplex its media data using the Android@Home RTP profile and
-     * re-transmit to the target endpoint.  Receiver devices (which may be
-     * either the same as the transmitting device or different devices) may
-     * instantiate, prepare, and start a receiver player using a setDataSource
-     * URL of the form...
-     *
-     * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
-     *
-     * to receive, decode and render the re-transmitted content.
-     *
-     * setRetransmitEndpoint may only be called before setDataSource has been
-     * called; while the player is in the Idle state.
-     *
-     * @param endpoint the address and UDP port of the re-transmission target or
-     * null if no re-transmission is to be performed.
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if the retransmit endpoint is supplied,
-     * but invalid.
-     *
-     * {@hide} pending API council
-     */
-    @Override
-    public void setRetransmitEndpoint(InetSocketAddress endpoint)
-            throws IllegalStateException, IllegalArgumentException
-    {
-        String addrString = null;
-        int port = 0;
-
-        if (null != endpoint) {
-            addrString = endpoint.getAddress().getHostAddress();
-            port = endpoint.getPort();
-        }
-
-        int ret = native_setRetransmitEndpoint(addrString, port);
-        if (ret != 0) {
-            throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
-        }
-    }
-
-    private native final int native_setRetransmitEndpoint(String addrString, int port);
-
-    /**
-     * Releases the resources held by this {@code MediaPlayer2} object.
-     *
-     * It is considered good practice to call this method when you're
-     * done using the MediaPlayer2. In particular, whenever an Activity
-     * of an application is paused (its onPause() method is called),
-     * or stopped (its onStop() method is called), this method should be
-     * invoked to release the MediaPlayer2 object, unless the application
-     * has a special need to keep the object around. In addition to
-     * unnecessary resources (such as memory and instances of codecs)
-     * being held, failure to call this method immediately if a
-     * MediaPlayer2 object is no longer needed may also lead to
-     * continuous battery consumption for mobile devices, and playback
-     * failure for other applications if no multiple instances of the
-     * same codec are supported on a device. Even if multiple instances
-     * of the same codec are supported, some performance degradation
-     * may be expected when unnecessary multiple instances are used
-     * at the same time.
-     *
-     * {@code close()} may be safely called after a prior {@code close()}.
-     * This class implements the Java {@code AutoCloseable} interface and
-     * may be used with try-with-resources.
-     */
-    @Override
-    public void close() {
-        synchronized (mGuard) {
-            release();
-        }
-    }
-
     // Have to declare protected for finalize() since it is protected
     // in the base class Object.
     @Override
@@ -3049,8 +2707,11 @@
         stayAwake(false);
         updateSurfaceScreenOn();
         synchronized (mEventCbLock) {
-            mEventCb = null;
-            mEventExec = null;
+            mEventCallbackRecords.clear();
+        }
+        if (mHandlerThread != null) {
+            mHandlerThread.quitSafely();
+            mHandlerThread = null;
         }
         if (mTimeProvider != null) {
             mTimeProvider.close();
@@ -3061,8 +2722,7 @@
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
         synchronized (mDrmEventCbLock) {
-            mDrmEventCb = null;
-            mDrmEventExec = null;
+            mDrmEventCallbackRecords.clear();
         }
         resetDrmState();
 
@@ -3114,24 +2774,20 @@
 
         @Override
         public void handleMessage(Message msg) {
+            handleMessage(msg, 0);
+        }
+
+        public void handleMessage(Message msg, long srcId) {
             if (mMediaPlayer.mNativeContext == 0) {
                 Log.w(TAG, "mediaplayer2 went away with unhandled events");
                 return;
             }
-            final Executor eventExec;
-            final EventCallback eventCb;
-            synchronized (mEventCbLock) {
-                eventExec = mEventExec;
-                eventCb = mEventCb;
-            }
-            final Executor drmEventExec;
-            final DrmEventCallback drmEventCb;
-            synchronized (mDrmEventCbLock) {
-                drmEventExec = mDrmEventExec;
-                drmEventCb = mDrmEventCb;
-            }
+            final int what = msg.arg1;
+            final int extra = msg.arg2;
+
             switch(msg.what) {
             case MEDIA_PREPARED:
+            {
                 try {
                     scanInternalSubtitleTracks();
                 } catch (RuntimeException e) {
@@ -3143,174 +2799,273 @@
                     sendMessage(msg2);
                 }
 
-                if (eventCb != null && eventExec != null) {
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_PREPARED, 0));
+                final DataSourceDesc dsd;
+                synchronized (mSrcLock) {
+                    Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
+                            + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
+                    if (srcId == mCurrentSrcId) {
+                        dsd = mCurrentDSD;
+                        prepareNextDataSource_l();
+                    } else if (mNextDSDs != null && !mNextDSDs.isEmpty()
+                            && srcId == mNextSrcId) {
+                        dsd = mNextDSDs.get(0);
+                        mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
+                        if (mNextSourcePlayPending) {
+                            playNextDataSource_l();
+                        }
+                    } else {
+                        dsd = null;
+                    }
+                }
+
+                if (dsd != null) {
+                    synchronized (mEventCbLock) {
+                        for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onInfo(
+                                    mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
+                        }
+                    }
+                }
+                synchronized (mTaskLock) {
+                    if (mCurrentTask != null
+                            && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
+                            && mCurrentTask.mDSD == dsd
+                            && mCurrentTask.mNeedToWaitForEventToComplete) {
+                        mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
+                        mCurrentTask = null;
+                        processPendingTask_l();
+                    }
                 }
                 return;
+            }
 
             case MEDIA_DRM_INFO:
-                Log.v(TAG, "MEDIA_DRM_INFO " + mDrmEventCb);
-
+            {
                 if (msg.obj == null) {
                     Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
                 } else if (msg.obj instanceof Parcel) {
-                    if (drmEventExec != null && drmEventCb != null) {
-                        // The parcel was parsed already in postEventFromNative
-                        final DrmInfoImpl drmInfo;
+                    // The parcel was parsed already in postEventFromNative
+                    final DrmInfoImpl drmInfo;
 
-                        synchronized (mDrmLock) {
-                            if (mDrmInfoImpl != null) {
-                                drmInfo = mDrmInfoImpl.makeCopy();
-                            } else {
-                                drmInfo = null;
-                            }
+                    synchronized (mDrmLock) {
+                        if (mDrmInfoImpl != null) {
+                            drmInfo = mDrmInfoImpl.makeCopy();
+                        } else {
+                            drmInfo = null;
                         }
+                    }
 
-                        // notifying the client outside the lock
-                        if (drmInfo != null) {
-                            drmEventExec.execute(() -> drmEventCb.onDrmInfo(mMediaPlayer, drmInfo));
+                    // notifying the client outside the lock
+                    if (drmInfo != null) {
+                        synchronized (mEventCbLock) {
+                            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                                cb.first.execute(() -> cb.second.onDrmInfo(
+                                        mMediaPlayer, mCurrentDSD, drmInfo));
+                            }
                         }
                     }
                 } else {
                     Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
                 }
                 return;
+            }
 
             case MEDIA_PLAYBACK_COMPLETE:
-                if (eventCb != null && eventExec != null) {
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+            {
+                final DataSourceDesc dsd = mCurrentDSD;
+                synchronized (mSrcLock) {
+                    if (srcId == mCurrentSrcId) {
+                        Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
+                                + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
+                        playNextDataSource_l();
+                    }
+                }
+
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                    }
                 }
                 stayAwake(false);
                 return;
+            }
 
             case MEDIA_STOPPED:
-                {
-                    TimeProvider timeProvider = mTimeProvider;
-                    if (timeProvider != null) {
-                        timeProvider.onStopped();
-                    }
+            {
+                TimeProvider timeProvider = mTimeProvider;
+                if (timeProvider != null) {
+                    timeProvider.onStopped();
                 }
                 break;
+            }
 
             case MEDIA_STARTED:
             case MEDIA_PAUSED:
-                {
-                    TimeProvider timeProvider = mTimeProvider;
-                    if (timeProvider != null) {
-                        timeProvider.onPaused(msg.what == MEDIA_PAUSED);
-                    }
+            {
+                TimeProvider timeProvider = mTimeProvider;
+                if (timeProvider != null) {
+                    timeProvider.onPaused(msg.what == MEDIA_PAUSED);
                 }
                 break;
+            }
 
             case MEDIA_BUFFERING_UPDATE:
-                if (eventCb != null && eventExec != null) {
-                    final int percent = msg.arg1;
-                    eventExec.execute(() -> eventCb.onBufferingUpdate(mMediaPlayer, 0, percent));
+            {
+                final int percent = msg.arg1;
+                synchronized (mEventCbLock) {
+                    if (srcId == mCurrentSrcId) {
+                        mBufferedPercentageCurrent.set(percent);
+                        for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onInfo(
+                                    mMediaPlayer, mCurrentDSD, MEDIA_INFO_BUFFERING_UPDATE,
+                                    percent));
+                        }
+                    } else if (srcId == mNextSrcId && !mNextDSDs.isEmpty()) {
+                        mBufferedPercentageNext.set(percent);
+                        DataSourceDesc nextDSD = mNextDSDs.get(0);
+                        for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                            cb.first.execute(() -> cb.second.onInfo(
+                                    mMediaPlayer, nextDSD, MEDIA_INFO_BUFFERING_UPDATE,
+                                    percent));
+                        }
+                    }
                 }
                 return;
+            }
 
             case MEDIA_SEEK_COMPLETE:
-                if (eventCb != null && eventExec != null) {
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_COMPLETE_CALL_SEEK, 0));
+            {
+                synchronized (mTaskLock) {
+                    if (mCurrentTask != null
+                            && mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
+                            && mCurrentTask.mNeedToWaitForEventToComplete) {
+                        mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
+                        mCurrentTask = null;
+                        processPendingTask_l();
+                    }
                 }
+            }
                 // fall through
 
             case MEDIA_SKIPPED:
-                {
-                    TimeProvider timeProvider = mTimeProvider;
-                    if (timeProvider != null) {
-                        timeProvider.onSeekComplete(mMediaPlayer);
+            {
+                TimeProvider timeProvider = mTimeProvider;
+                if (timeProvider != null) {
+                    timeProvider.onSeekComplete(mMediaPlayer);
+                }
+                return;
+            }
+
+            case MEDIA_SET_VIDEO_SIZE:
+            {
+                final int width = msg.arg1;
+                final int height = msg.arg2;
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onVideoSizeChanged(
+                                mMediaPlayer, mCurrentDSD, width, height));
                     }
                 }
                 return;
-
-            case MEDIA_SET_VIDEO_SIZE:
-                if (eventCb != null && eventExec != null) {
-                    final int width = msg.arg1;
-                    final int height = msg.arg2;
-                    eventExec.execute(() -> eventCb.onVideoSizeChanged(
-                            mMediaPlayer, 0, width, height));
-                }
-                return;
+            }
 
             case MEDIA_ERROR:
+            {
                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
-                if (eventCb != null && eventExec != null) {
-                    final int what = msg.arg1;
-                    final int extra = msg.arg2;
-                    eventExec.execute(() -> eventCb.onError(mMediaPlayer, 0, what, extra));
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onError(
+                                mMediaPlayer, mCurrentDSD, what, extra));
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, mCurrentDSD, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                    }
                 }
                 stayAwake(false);
                 return;
+            }
 
             case MEDIA_INFO:
+            {
                 switch (msg.arg1) {
-                case MEDIA_INFO_VIDEO_TRACK_LAGGING:
-                    Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
-                    break;
-                case MEDIA_INFO_METADATA_UPDATE:
-                    try {
-                        scanInternalSubtitleTracks();
-                    } catch (RuntimeException e) {
-                        Message msg2 = obtainMessage(
-                                MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
-                        sendMessage(msg2);
-                    }
-                    // fall through
+                    case MEDIA_INFO_STARTED_AS_NEXT:
+                        if (srcId == mCurrentSrcId) {
+                            prepareNextDataSource_l();
+                        }
+                        break;
 
-                case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
-                    msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
-                    // update default track selection
-                    if (mSubtitleController != null) {
-                        mSubtitleController.selectDefaultTrack();
-                    }
-                    break;
-                case MEDIA_INFO_BUFFERING_START:
-                case MEDIA_INFO_BUFFERING_END:
-                    TimeProvider timeProvider = mTimeProvider;
-                    if (timeProvider != null) {
-                        timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
-                    }
-                    break;
+                    case MEDIA_INFO_VIDEO_TRACK_LAGGING:
+                        Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
+                        break;
+
+                    case MEDIA_INFO_METADATA_UPDATE:
+                        try {
+                            scanInternalSubtitleTracks();
+                        } catch (RuntimeException e) {
+                            Message msg2 = obtainMessage(
+                                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED,
+                                    null);
+                            sendMessage(msg2);
+                        }
+                        // fall through
+
+                    case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
+                        msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
+                        // update default track selection
+                        if (mSubtitleController != null) {
+                            mSubtitleController.selectDefaultTrack();
+                        }
+                        break;
+
+                    case MEDIA_INFO_BUFFERING_START:
+                    case MEDIA_INFO_BUFFERING_END:
+                        TimeProvider timeProvider = mTimeProvider;
+                        if (timeProvider != null) {
+                            timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
+                        }
+                        break;
                 }
 
-                if (eventCb != null && eventExec != null) {
-                    final int what = msg.arg1;
-                    final int extra = msg.arg2;
-                    eventExec.execute(() -> eventCb.onInfo(mMediaPlayer, 0, what, extra));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, mCurrentDSD, what, extra));
+                    }
                 }
                 // No real default action so far.
                 return;
+            }
 
             case MEDIA_NOTIFY_TIME:
-                    TimeProvider timeProvider = mTimeProvider;
-                    if (timeProvider != null) {
-                        timeProvider.onNotifyTime();
-                    }
+            {
+                TimeProvider timeProvider = mTimeProvider;
+                if (timeProvider != null) {
+                    timeProvider.onNotifyTime();
+                }
                 return;
+            }
 
             case MEDIA_TIMED_TEXT:
-                if (eventCb == null || eventExec == null) {
-                    return;
-                }
-                if (msg.obj == null) {
-                    eventExec.execute(() -> eventCb.onTimedText(mMediaPlayer, 0, null));
+            {
+                final TimedText text;
+                if (msg.obj instanceof Parcel) {
+                    Parcel parcel = (Parcel)msg.obj;
+                    text = new TimedText(parcel);
+                    parcel.recycle();
                 } else {
-                    if (msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        TimedText text = new TimedText(parcel);
-                        parcel.recycle();
-                        eventExec.execute(() -> eventCb.onTimedText(mMediaPlayer, 0, text));
+                    text = null;
+                }
+
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, mCurrentDSD, text));
                     }
                 }
                 return;
+            }
 
             case MEDIA_SUBTITLE_DATA:
+            {
                 OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
                 if (onSubtitleDataListener == null) {
                     return;
@@ -3322,24 +3077,35 @@
                     onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
                 }
                 return;
+            }
 
             case MEDIA_META_DATA:
-                if (eventCb == null || eventExec == null) {
-                    return;
-                }
+            {
+                final TimedMetaData data;
                 if (msg.obj instanceof Parcel) {
                     Parcel parcel = (Parcel) msg.obj;
-                    TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
+                    data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
                     parcel.recycle();
-                    eventExec.execute(() -> eventCb.onTimedMetaDataAvailable(
-                            mMediaPlayer, 0, data));
+                } else {
+                    data = null;
+                }
+
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
+                                mMediaPlayer, mCurrentDSD, data));
+                    }
                 }
                 return;
+            }
 
             case MEDIA_NOP: // interface test message - ignore
+            {
                 break;
+            }
 
             case MEDIA_AUDIO_ROUTING_CHANGED:
+            {
                 AudioManager.resetAudioPortGeneration();
                 synchronized (mRoutingChangeListeners) {
                     for (NativeRoutingEventHandlerDelegate delegate
@@ -3348,11 +3114,14 @@
                     }
                 }
                 return;
+            }
 
             default:
+            {
                 Log.e(TAG, "Unknown message type " + msg.what);
                 return;
             }
+            }
         }
     }
 
@@ -3363,7 +3132,7 @@
      * code is safe from the object disappearing from underneath it.  (This is
      * the cookie passed to native_setup().)
      */
-    private static void postEventFromNative(Object mediaplayer2_ref,
+    private static void postEventFromNative(Object mediaplayer2_ref, long srcId,
                                             int what, int arg1, int arg2, Object obj)
     {
         final MediaPlayer2Impl mp = (MediaPlayer2Impl)((WeakReference)mediaplayer2_ref).get();
@@ -3404,7 +3173,7 @@
 
         case MEDIA_PREPARED:
             // By this time, we've learned about DrmInfo's presence or absence. This is meant
-            // mainly for prepareAsync() use case. For prepare(), this still can run to a race
+            // mainly for prepare() use case. For prepare(), this still can run to a race
             // condition b/c MediaPlayerNative releases the prepare() lock before calling notify
             // so we also set mDrmInfoResolved in prepare().
             synchronized (mp.mDrmLock) {
@@ -3416,13 +3185,19 @@
 
         if (mp.mEventHandler != null) {
             Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
-            mp.mEventHandler.sendMessage(m);
+
+            mp.mEventHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mp.mEventHandler.handleMessage(m, srcId);
+                }
+            });
         }
     }
 
-    private Executor mEventExec;
-    private EventCallback mEventCb;
     private final Object mEventCbLock = new Object();
+    private ArrayList<Pair<Executor, MediaPlayer2EventCallback> > mEventCallbackRecords
+        = new ArrayList<Pair<Executor, MediaPlayer2EventCallback> >();
 
     /**
      * Register a callback to be invoked when the media source is ready
@@ -3432,33 +3207,27 @@
      * @param executor the executor through which the callback should be invoked
      */
     @Override
-    public void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull EventCallback eventCallback) {
+    public void setMediaPlayer2EventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull MediaPlayer2EventCallback eventCallback) {
         if (eventCallback == null) {
-            throw new IllegalArgumentException("Illegal null EventCallback");
+            throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
         }
         if (executor == null) {
-            throw new IllegalArgumentException("Illegal null Executor for the EventCallback");
+            throw new IllegalArgumentException(
+                    "Illegal null Executor for the MediaPlayer2EventCallback");
         }
         synchronized (mEventCbLock) {
-            // TODO: support multiple callbacks.
-            mEventExec = executor;
-            mEventCb = eventCallback;
+            mEventCallbackRecords.add(new Pair(executor, eventCallback));
         }
     }
 
     /**
-     * Unregisters an {@link EventCallback}.
-     *
-     * @param callback an {@link EventCallback} to unregister
+     * Clears the {@link MediaPlayer2EventCallback}.
      */
     @Override
-    public void unregisterEventCallback(EventCallback callback) {
+    public void clearMediaPlayer2EventCallback() {
         synchronized (mEventCbLock) {
-            if (callback == mEventCb) {
-                mEventExec = null;
-                mEventCb = null;
-            }
+            mEventCallbackRecords.clear();
         }
     }
 
@@ -3497,9 +3266,9 @@
 
     private OnDrmConfigHelper mOnDrmConfigHelper;
 
-    private Executor mDrmEventExec;
-    private DrmEventCallback mDrmEventCb;
     private final Object mDrmEventCbLock = new Object();
+    private ArrayList<Pair<Executor, DrmEventCallback> > mDrmEventCallbackRecords
+        = new ArrayList<Pair<Executor, DrmEventCallback> >();
 
     /**
      * Register a callback to be invoked when the media source is ready
@@ -3509,33 +3278,27 @@
      * @param executor the executor through which the callback should be invoked
      */
     @Override
-    public void registerDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull DrmEventCallback eventCallback) {
         if (eventCallback == null) {
-            throw new IllegalArgumentException("Illegal null EventCallback");
+            throw new IllegalArgumentException("Illegal null MediaPlayer2EventCallback");
         }
         if (executor == null) {
-            throw new IllegalArgumentException("Illegal null Executor for the EventCallback");
+            throw new IllegalArgumentException(
+                    "Illegal null Executor for the MediaPlayer2EventCallback");
         }
         synchronized (mDrmEventCbLock) {
-            // TODO: support multiple callbacks.
-            mDrmEventExec = executor;
-            mDrmEventCb = eventCallback;
+            mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
         }
     }
 
     /**
-     * Unregisters a {@link DrmEventCallback}.
-     *
-     * @param callback a {@link DrmEventCallback} to unregister
+     * Clears the {@link DrmEventCallback}.
      */
     @Override
-    public void unregisterDrmEventCallback(DrmEventCallback callback) {
+    public void clearDrmEventCallback() {
         synchronized (mDrmEventCbLock) {
-            if (callback == mDrmEventCb) {
-                mDrmEventExec = null;
-                mDrmEventCb = null;
-            }
+            mDrmEventCallbackRecords.clear();
         }
     }
 
@@ -3662,7 +3425,7 @@
 
         // call the callback outside the lock
         if (mOnDrmConfigHelper != null)  {
-            mOnDrmConfigHelper.onDrmConfig(this);
+            mOnDrmConfigHelper.onDrmConfig(this, mCurrentDSD);
         }
 
         synchronized (mDrmLock) {
@@ -3733,15 +3496,11 @@
 
         // if finished successfully without provisioning, call the callback outside the lock
         if (allDoneWithoutProvisioning) {
-            final Executor drmEventExec;
-            final DrmEventCallback drmEventCb;
             synchronized (mDrmEventCbLock) {
-                drmEventExec = mDrmEventExec;
-                drmEventCb = mDrmEventCb;
-            }
-            if (drmEventExec != null && drmEventCb != null) {
-                drmEventExec.execute(() -> drmEventCb.onDrmPrepared(
-                    this, PREPARE_DRM_STATUS_SUCCESS));
+                for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                    cb.first.execute(() -> cb.second.onDrmPrepared(
+                            this, mCurrentDSD, PREPARE_DRM_STATUS_SUCCESS));
+                }
             }
         }
 
@@ -3763,32 +3522,39 @@
     public void releaseDrm()
             throws NoDrmSchemeException
     {
-        Log.v(TAG, "releaseDrm:");
+        addTask(new Task(CALL_COMPLETED_RELEASE_DRM, false) {
+            @Override
+            void process() throws NoDrmSchemeException {
+                synchronized (mDrmLock) {
+                    Log.v(TAG, "releaseDrm:");
 
-        synchronized (mDrmLock) {
-            if (!mActiveDrmScheme) {
-                Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
-                throw new NoDrmSchemeExceptionImpl("releaseDrm: No active DRM scheme to release.");
+                    if (!mActiveDrmScheme) {
+                        Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+                        throw new NoDrmSchemeExceptionImpl(
+                                "releaseDrm: No active DRM scheme to release.");
+                    }
+
+                    try {
+                        // we don't have the player's state in this layer. The below call raises
+                        // exception if we're in a non-stopped/prepared state.
+
+                        // for cleaning native/mediaserver crypto object
+                        _releaseDrm();
+
+                        // for cleaning client-side MediaDrm object; only called if above has succeeded
+                        cleanDrmObj();
+
+                        mActiveDrmScheme = false;
+                    } catch (IllegalStateException e) {
+                        Log.w(TAG, "releaseDrm: Exception ", e);
+                        throw new IllegalStateException(
+                                "releaseDrm: The player is not in a valid state.");
+                    } catch (Exception e) {
+                        Log.e(TAG, "releaseDrm: Exception ", e);
+                    }
+                }   // synchronized
             }
-
-            try {
-                // we don't have the player's state in this layer. The below call raises
-                // exception if we're in a non-stopped/prepared state.
-
-                // for cleaning native/mediaserver crypto object
-                _releaseDrm();
-
-                // for cleaning client-side MediaDrm object; only called if above has succeeded
-                cleanDrmObj();
-
-                mActiveDrmScheme = false;
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "releaseDrm: Exception ", e);
-                throw new IllegalStateException("releaseDrm: The player is not in a valid state.");
-            } catch (Exception e) {
-                Log.e(TAG, "releaseDrm: Exception ", e);
-            }
-        }   // synchronized
+        });
     }
 
 
@@ -3796,14 +3562,14 @@
      * A key request/response exchange occurs between the app and a license server
      * to obtain or release keys used to decrypt encrypted content.
      * <p>
-     * getKeyRequest() is used to obtain an opaque key request byte array that is
+     * getDrmKeyRequest() is used to obtain an opaque key request byte array that is
      * delivered to the license server.  The opaque key request byte array is returned
      * in KeyRequest.data.  The recommended URL to deliver the key request to is
      * returned in KeyRequest.defaultUrl.
      * <p>
      * After the app has received the key request response from the server,
      * it should deliver to the response to the DRM engine plugin using the method
-     * {@link #provideKeyResponse}.
+     * {@link #provideDrmKeyResponse}.
      *
      * @param keySetId is the key-set identifier of the offline keys being released when keyType is
      * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when
@@ -3831,19 +3597,20 @@
      */
     @Override
     @NonNull
-    public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
+    public MediaDrm.KeyRequest getDrmKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
             @Nullable String mimeType, @MediaDrm.KeyType int keyType,
             @Nullable Map<String, String> optionalParameters)
             throws NoDrmSchemeException
     {
-        Log.v(TAG, "getKeyRequest: " +
+        Log.v(TAG, "getDrmKeyRequest: " +
                 " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
                 " keyType: " + keyType + " optionalParameters: " + optionalParameters);
 
         synchronized (mDrmLock) {
             if (!mActiveDrmScheme) {
-                Log.e(TAG, "getKeyRequest NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("getKeyRequest: Has to set a DRM scheme first.");
+                Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+                throw new NoDrmSchemeExceptionImpl(
+                        "getDrmKeyRequest: Has to set a DRM scheme first.");
             }
 
             try {
@@ -3858,16 +3625,16 @@
 
                 MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
                                                               keyType, hmapOptionalParameters);
-                Log.v(TAG, "getKeyRequest:   --> request: " + request);
+                Log.v(TAG, "getDrmKeyRequest:   --> request: " + request);
 
                 return request;
 
             } catch (NotProvisionedException e) {
-                Log.w(TAG, "getKeyRequest NotProvisionedException: " +
+                Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
                         "Unexpected. Shouldn't have reached here.");
-                throw new IllegalStateException("getKeyRequest: Unexpected provisioning error.");
+                throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error.");
             } catch (Exception e) {
-                Log.w(TAG, "getKeyRequest Exception " + e);
+                Log.w(TAG, "getDrmKeyRequest Exception " + e);
                 throw e;
             }
 
@@ -3877,15 +3644,15 @@
 
     /**
      * A key response is received from the license server by the app, then it is
-     * provided to the DRM engine plugin using provideKeyResponse. When the
+     * provided to the DRM engine plugin using provideDrmKeyResponse. When the
      * response is for an offline key request, a key-set identifier is returned that
      * can be used to later restore the keys to a new session with the method
-     * {@ link # restoreKeys}.
+     * {@ link # restoreDrmKeys}.
      * When the response is for a streaming or release request, null is returned.
      *
      * @param keySetId When the response is for a release request, keySetId identifies
      * the saved key associated with the release request (i.e., the same keySetId
-     * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the
+     * passed to the earlier {@ link #getDrmKeyRequest} call. It MUST be null when the
      * response is for either streaming or offline key requests.
      *
      * @param response the byte array response from the server
@@ -3895,16 +3662,17 @@
      * server rejected the request
      */
     @Override
-    public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
+    public byte[] provideDrmKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
             throws NoDrmSchemeException, DeniedByServerException
     {
-        Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response);
+        Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
 
         synchronized (mDrmLock) {
 
             if (!mActiveDrmScheme) {
-                Log.e(TAG, "getKeyRequest NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("getKeyRequest: Has to set a DRM scheme first.");
+                Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+                throw new NoDrmSchemeExceptionImpl(
+                        "getDrmKeyRequest: Has to set a DRM scheme first.");
             }
 
             try {
@@ -3914,19 +3682,19 @@
 
                 byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
 
-                Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response +
-                        " --> " + keySetResult);
+                Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response
+                        + " --> " + keySetResult);
 
 
                 return keySetResult;
 
             } catch (NotProvisionedException e) {
-                Log.w(TAG, "provideKeyResponse NotProvisionedException: " +
+                Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
                         "Unexpected. Shouldn't have reached here.");
-                throw new IllegalStateException("provideKeyResponse: " +
+                throw new IllegalStateException("provideDrmKeyResponse: " +
                         "Unexpected provisioning error.");
             } catch (Exception e) {
-                Log.w(TAG, "provideKeyResponse Exception " + e);
+                Log.w(TAG, "provideDrmKeyResponse Exception " + e);
                 throw e;
             }
         }   // synchronized
@@ -3935,31 +3703,37 @@
 
     /**
      * Restore persisted offline keys into a new session.  keySetId identifies the
-     * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
+     * keys to load, obtained from a prior call to {@link #provideDrmKeyResponse}.
      *
      * @param keySetId identifies the saved key set to restore
      */
     @Override
-    public void restoreKeys(@NonNull byte[] keySetId)
+    public void restoreDrmKeys(@NonNull byte[] keySetId)
             throws NoDrmSchemeException
     {
-        Log.v(TAG, "restoreKeys: keySetId: " + keySetId);
+        addTask(new Task(CALL_COMPLETED_RESTORE_DRM_KEYS, false) {
+            @Override
+            void process() throws NoDrmSchemeException {
+                Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
 
-        synchronized (mDrmLock) {
+                synchronized (mDrmLock) {
 
-            if (!mActiveDrmScheme) {
-                Log.w(TAG, "restoreKeys NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("restoreKeys: Has to set a DRM scheme first.");
+                    if (!mActiveDrmScheme) {
+                        Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+                        throw new NoDrmSchemeExceptionImpl(
+                                "restoreDrmKeys: Has to set a DRM scheme first.");
+                    }
+
+                    try {
+                        mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+                    } catch (Exception e) {
+                        Log.w(TAG, "restoreKeys Exception " + e);
+                        throw e;
+                    }
+
+                }   // synchronized
             }
-
-            try {
-                mDrmObj.restoreKeys(mDrmSessionId, keySetId);
-            } catch (Exception e) {
-                Log.w(TAG, "restoreKeys Exception " + e);
-                throw e;
-            }
-
-        }   // synchronized
+        });
     }
 
 
@@ -3984,7 +3758,8 @@
 
             if (!mActiveDrmScheme && !mDrmConfigAllowed) {
                 Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("getDrmPropertyString: Has to prepareDrm() first.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "getDrmPropertyString: Has to prepareDrm() first.");
             }
 
             try {
@@ -4022,7 +3797,8 @@
 
             if ( !mActiveDrmScheme && !mDrmConfigAllowed ) {
                 Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
-                throw new NoDrmSchemeExceptionImpl("setDrmPropertyString: Has to prepareDrm() first.");
+                throw new NoDrmSchemeExceptionImpl(
+                        "setDrmPropertyString: Has to prepareDrm() first.");
             }
 
             try {
@@ -4233,7 +4009,7 @@
 
         // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
         // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
-        // at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse
+        // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
         try {
             mDrmSessionId = mDrmObj.openSession();
             Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
@@ -4250,6 +4026,65 @@
 
     }
 
+    // Called from the native side
+    @SuppressWarnings("unused")
+    private static boolean setAudioOutputDeviceById(AudioTrack track, int deviceId) {
+        if (track == null) {
+            return false;
+        }
+
+        if (deviceId == 0) {
+            // Use default routing.
+            track.setPreferredDevice(null);
+            return true;
+        }
+
+        // TODO: Unhide AudioManager.getDevicesStatic.
+        AudioDeviceInfo[] outputDevices =
+                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
+
+        boolean success = false;
+        for (AudioDeviceInfo device : outputDevices) {
+            if (device.getId() == deviceId) {
+                track.setPreferredDevice(device);
+                success = true;
+                break;
+            }
+        }
+        return success;
+    }
+
+    // Instantiated from the native side
+    @SuppressWarnings("unused")
+    private static class StreamEventCallback extends AudioTrack.StreamEventCallback {
+        public long mJAudioTrackPtr;
+        public long mNativeCallbackPtr;
+        public long mUserDataPtr;
+
+        public StreamEventCallback(long jAudioTrackPtr, long nativeCallbackPtr, long userDataPtr) {
+            super();
+            mJAudioTrackPtr = jAudioTrackPtr;
+            mNativeCallbackPtr = nativeCallbackPtr;
+            mUserDataPtr = userDataPtr;
+        }
+
+        @Override
+        public void onTearDown(AudioTrack track) {
+            native_stream_event_onTearDown(mNativeCallbackPtr, mUserDataPtr);
+        }
+
+        @Override
+        public void onStreamPresentationEnd(AudioTrack track) {
+            native_stream_event_onStreamPresentationEnd(mNativeCallbackPtr, mUserDataPtr);
+        }
+
+        @Override
+        public void onStreamDataRequest(AudioTrack track) {
+            native_stream_event_onStreamDataRequest(
+                    mJAudioTrackPtr, mNativeCallbackPtr, mUserDataPtr);
+        }
+    }
+
     private class ProvisioningThread extends Thread {
         public static final int TIMEOUT_MS = 60000;
 
@@ -4324,14 +4159,12 @@
 
             boolean succeeded = false;
 
-            final Executor drmEventExec;
-            final DrmEventCallback drmEventCb;
+            boolean hasCallback = false;
             synchronized (mDrmEventCbLock) {
-                drmEventExec = mDrmEventExec;
-                drmEventCb = mDrmEventCb;
+                hasCallback = !mDrmEventCallbackRecords.isEmpty();
             }
             // non-blocking mode needs the lock
-            if (drmEventExec != null && drmEventCb != null) {
+            if (hasCallback) {
 
                 synchronized (drmLock) {
                     // continuing with prepareDrm
@@ -4349,7 +4182,12 @@
                 } // synchronized
 
                 // calling the callback outside the lock
-                drmEventExec.execute(() -> drmEventCb.onDrmPrepared(mediaPlayer, status));
+                synchronized (mDrmEventCbLock) {
+                    for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onDrmPrepared(
+                                mediaPlayer, mCurrentDSD, status));
+                    }
+                }
             } else {   // blocking mode already has the lock
 
                 // continuing with prepareDrm
@@ -4397,13 +4235,11 @@
         int result;
 
         // non-blocking: this is not the final result
-        final Executor drmEventExec;
-        final DrmEventCallback drmEventCb;
+        boolean hasCallback = false;
         synchronized (mDrmEventCbLock) {
-            drmEventExec = mDrmEventExec;
-            drmEventCb = mDrmEventCb;
+            hasCallback = !mDrmEventCallbackRecords.isEmpty();
         }
-        if (drmEventCb != null && drmEventExec != null) {
+        if (hasCallback) {
             result = PREPARE_DRM_STATUS_SUCCESS;
         } else {
             // if blocking mode, wait till provisioning is done
@@ -4523,7 +4359,7 @@
         // no need for log(N) search performance
         private MediaTimeProvider.OnMediaTimeListener mListeners[];
         private long mTimes[];
-        private Handler mEventHandler;
+        private EventHandler mEventHandler;
         private boolean mRefresh = false;
         private boolean mPausing = false;
         private boolean mSeeking = false;
@@ -4896,4 +4732,65 @@
             }
         }
     }
+
+    private abstract class Task implements Runnable {
+        private final int mMediaCallType;
+        private final boolean mNeedToWaitForEventToComplete;
+        private DataSourceDesc mDSD;
+
+        public Task (int mediaCallType, boolean needToWaitForEventToComplete) {
+            mMediaCallType = mediaCallType;
+            mNeedToWaitForEventToComplete = needToWaitForEventToComplete;
+        }
+
+        abstract void process() throws IOException, NoDrmSchemeException;
+
+        @Override
+        public void run() {
+            int status = CALL_STATUS_NO_ERROR;
+            try {
+                process();
+            } catch (IllegalStateException e) {
+                status = CALL_STATUS_INVALID_OPERATION;
+            } catch (IllegalArgumentException e) {
+                status = CALL_STATUS_BAD_VALUE;
+            } catch (SecurityException e) {
+                status = CALL_STATUS_PERMISSION_DENIED;
+            } catch (IOException e) {
+                status = CALL_STATUS_ERROR_IO;
+            } catch (NoDrmSchemeException e) {
+                status = CALL_STATUS_NO_DRM_SCHEME;
+            } catch (Exception e) {
+                status = CALL_STATUS_ERROR_UNKNOWN;
+            }
+            synchronized (mSrcLock) {
+                mDSD = mCurrentDSD;
+            }
+
+            // TODO: Make native implementations asynchronous and let them send notifications.
+            if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
+
+                sendCompleteNotification(status);
+
+                synchronized (mTaskLock) {
+                    mCurrentTask = null;
+                    processPendingTask_l();
+                }
+            }
+        }
+
+        private void sendCompleteNotification(int status) {
+            // In {@link #notifyWhenCommandLabelReached} case, a separate callback
+            // {#link #onCommandLabelReached} is already called in {@code process()}.
+            if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
+                return;
+            }
+            synchronized (mEventCbLock) {
+                for (Pair<Executor, MediaPlayer2EventCallback> cb : mEventCallbackRecords) {
+                    cb.first.execute(() -> cb.second.onCallCompleted(
+                            MediaPlayer2Impl.this, mDSD, mMediaCallType, status));
+                }
+            }
+        }
+    };
 }
diff --git a/android/media/MediaPlayerBase.java b/android/media/MediaPlayerBase.java
index d638a9f..a426552 100644
--- a/android/media/MediaPlayerBase.java
+++ b/android/media/MediaPlayerBase.java
@@ -16,57 +16,316 @@
 
 package android.media;
 
-import android.media.MediaSession2.PlaylistParam;
-import android.media.session.PlaybackState;
-import android.os.Handler;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
- * Base interfaces for all media players that want media session.
- *
  * @hide
+ * Base class for all media players that want media session.
  */
-public abstract class MediaPlayerBase {
+public abstract class MediaPlayerBase implements AutoCloseable {
     /**
-     * Listens change in {@link PlaybackState2}.
+     * @hide
      */
-    public interface PlaybackListener {
+    @IntDef({
+        PLAYER_STATE_IDLE,
+        PLAYER_STATE_PAUSED,
+        PLAYER_STATE_PLAYING,
+        PLAYER_STATE_ERROR })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PlayerState {}
+
+    /**
+     * @hide
+     */
+    @IntDef({
+        BUFFERING_STATE_UNKNOWN,
+        BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
+        BUFFERING_STATE_BUFFERING_AND_STARVED,
+        BUFFERING_STATE_BUFFERING_COMPLETE })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BuffState {}
+
+    /**
+     * State when the player is idle, and needs configuration to start playback.
+     */
+    public static final int PLAYER_STATE_IDLE = 0;
+
+    /**
+     * State when the player's playback is paused
+     */
+    public static final int PLAYER_STATE_PAUSED = 1;
+
+    /**
+     * State when the player's playback is ongoing
+     */
+    public static final int PLAYER_STATE_PLAYING = 2;
+
+    /**
+     * State when the player is in error state and cannot be recovered self.
+     */
+    public static final int PLAYER_STATE_ERROR = 3;
+
+    /**
+     * Buffering state is unknown.
+     */
+    public static final int BUFFERING_STATE_UNKNOWN = 0;
+
+    /**
+     * Buffering state indicating the player is buffering but enough has been buffered
+     * for this player to be able to play the content.
+     * See {@link #getBufferedPosition()} for how far is buffered already.
+     */
+    public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
+
+    /**
+     * Buffering state indicating the player is buffering, but the player is currently starved
+     * for data, and cannot play.
+     */
+    public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
+
+    /**
+     * Buffering state indicating the player is done buffering, and the remainder of the content is
+     * available for playback.
+     */
+    public static final int BUFFERING_STATE_BUFFERING_COMPLETE = 3;
+
+    /**
+     * Starts or resumes playback.
+     */
+    public abstract void play();
+
+    /**
+     * Prepares the player for playback.
+     * See {@link PlayerEventCallback#onMediaPrepared(MediaPlayerBase, DataSourceDesc)} for being
+     * notified when the preparation phase completed. During this time, the player may allocate
+     * resources required to play, such as audio and video decoders.
+     */
+    public abstract void prepare();
+
+    /**
+     * Pauses playback.
+     */
+    public abstract void pause();
+
+    /**
+     * Resets the MediaPlayerBase to its uninitialized state.
+     */
+    public abstract void reset();
+
+    /**
+     *
+     */
+    public abstract void skipToNext();
+
+    /**
+     * Moves the playback head to the specified position
+     * @param pos the new playback position expressed in ms.
+     */
+    public abstract void seekTo(long pos);
+
+    public static final long UNKNOWN_TIME = -1;
+
+    /**
+     * Gets the current playback head position.
+     * @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown.
+     */
+    public long getCurrentPosition() { return UNKNOWN_TIME; }
+
+    /**
+     * Returns the duration of the current data source, or {@link #UNKNOWN_TIME} if unknown.
+     * @return the duration in ms, or {@link #UNKNOWN_TIME}.
+     */
+    public long getDuration() { return UNKNOWN_TIME; }
+
+    /**
+     * Gets the buffered position of current playback, or {@link #UNKNOWN_TIME} if unknown.
+     * @return the buffered position in ms, or {@link #UNKNOWN_TIME}.
+     */
+    public long getBufferedPosition() { return UNKNOWN_TIME; }
+
+    /**
+     * Returns the current player state.
+     * See also {@link PlayerEventCallback#onPlayerStateChanged(MediaPlayerBase, int)} for
+     * notification of changes.
+     * @return the current player state
+     */
+    public abstract @PlayerState int getPlayerState();
+
+    /**
+     * Returns the current buffering state of the player.
+     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+     * buffered.
+     * @return the buffering state.
+     */
+    public abstract @BuffState int getBufferingState();
+
+    /**
+     * Sets the {@link AudioAttributes} to be used during the playback of the media.
+     *
+     * @param attributes non-null <code>AudioAttributes</code>.
+     */
+    public abstract void setAudioAttributes(@NonNull AudioAttributes attributes);
+
+    /**
+     * Returns AudioAttributes that media player has.
+     */
+    public abstract @Nullable AudioAttributes getAudioAttributes();
+
+    /**
+     * Sets the data source to be played.
+     * @param dsd
+     */
+    public abstract void setDataSource(@NonNull DataSourceDesc dsd);
+
+    /**
+     * Sets the data source that will be played immediately after the current one is done playing.
+     * @param dsd
+     */
+    public abstract void setNextDataSource(@NonNull DataSourceDesc dsd);
+
+    /**
+     * Sets the list of data sources that will be sequentially played after the current one. Each
+     * data source is played immediately after the previous one is done playing.
+     * @param dsds
+     */
+    public abstract void setNextDataSources(@NonNull List<DataSourceDesc> dsds);
+
+    /**
+     * Returns the current data source.
+     * @return the current data source, or null if none is set, or none available to play.
+     */
+    public abstract @Nullable DataSourceDesc getCurrentDataSource();
+
+    /**
+     * Configures the player to loop on the current data source.
+     * @param loop true if the current data source is meant to loop.
+     */
+    public abstract void loopCurrent(boolean loop);
+
+    /**
+     * Sets the playback speed.
+     * A value of 1.0f is the default playback value.
+     * A negative value indicates reverse playback, check {@link #isReversePlaybackSupported()}
+     * before using negative values.<br>
+     * After changing the playback speed, it is recommended to query the actual speed supported
+     * by the player, see {@link #getPlaybackSpeed()}.
+     * @param speed
+     */
+    public abstract void setPlaybackSpeed(float speed);
+
+    /**
+     * Returns the actual playback speed to be used by the player when playing.
+     * Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
+     * @return the actual playback speed
+     */
+    public float getPlaybackSpeed() { return 1.0f; }
+
+    /**
+     * Indicates whether reverse playback is supported.
+     * Reverse playback is indicated by negative playback speeds, see
+     * {@link #setPlaybackSpeed(float)}.
+     * @return true if reverse playback is supported.
+     */
+    public boolean isReversePlaybackSupported() { return false; }
+
+    /**
+     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
+     * on the audio samples.
+     * Note that this volume is specific to the player, and is separate from stream volume
+     * used across the platform.<br>
+     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
+     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
+     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
+     */
+    public abstract void setPlayerVolume(float volume);
+
+    /**
+     * Returns the current volume of this player to this player.
+     * Note that it does not take into account the associated stream volume.
+     * @return the player volume.
+     */
+    public abstract float getPlayerVolume();
+
+    /**
+     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
+     */
+    public float getMaxPlayerVolume() { return 1.0f; }
+
+    /**
+     * Adds a callback to be notified of events for this player.
+     * @param e the {@link Executor} to be used for the events.
+     * @param cb the callback to receive the events.
+     */
+    public abstract void registerPlayerEventCallback(@NonNull Executor e,
+            @NonNull PlayerEventCallback cb);
+
+    /**
+     * Removes a previously registered callback for player events
+     * @param cb the callback to remove
+     */
+    public abstract void unregisterPlayerEventCallback(@NonNull PlayerEventCallback cb);
+
+    /**
+     * A callback class to receive notifications for events on the media player.
+     * See {@link MediaPlayerBase#registerPlayerEventCallback(Executor, PlayerEventCallback)} to
+     * register this callback.
+     */
+    public static abstract class PlayerEventCallback {
         /**
-         * Called when {@link PlaybackState2} for this player is changed.
+         * Called when the player's current data source has changed.
+         *
+         * @param mpb the player whose data source changed.
+         * @param dsd the new current data source. null, if no more data sources available.
          */
-        void onPlaybackChanged(PlaybackState2 state);
+        public void onCurrentDataSourceChanged(@NonNull MediaPlayerBase mpb,
+                @Nullable DataSourceDesc dsd) { }
+        /**
+         * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
+         * referenced by the given data source.
+         * @param mpb the player that is prepared.
+         * @param dsd the data source that the player is prepared to play.
+         */
+        public void onMediaPrepared(@NonNull MediaPlayerBase mpb, @NonNull DataSourceDesc dsd) { }
+
+        /**
+         * Called to indicate that the state of the player has changed.
+         * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
+         * @param mpb the player whose state has changed.
+         * @param state the new state of the player.
+         */
+        public void onPlayerStateChanged(@NonNull MediaPlayerBase mpb, @PlayerState int state) { }
+
+        /**
+         * Called to report buffering events for a data source.
+         * @param mpb the player that is buffering
+         * @param dsd the data source for which buffering is happening.
+         * @param state the new buffering state.
+         */
+        public void onBufferingStateChanged(@NonNull MediaPlayerBase mpb,
+                @NonNull DataSourceDesc dsd, @BuffState int state) { }
+
+        /**
+         * Called to indicate that the playback speed has changed.
+         * @param mpb the player that has changed the playback speed.
+         * @param speed the new playback speed.
+         */
+        public void onPlaybackSpeedChanged(@NonNull MediaPlayerBase mpb, float speed) { }
+
+        /**
+         * Called to indicate that {@link #seekTo(long)} is completed.
+         *
+         * @param mpb the player that has completed seeking.
+         * @param position the previous seeking request.
+         * @see #seekTo(long)
+         */
+        public void onSeekCompleted(@NonNull MediaPlayerBase mpb, long position) { }
     }
 
-    public abstract void play();
-    public abstract void prepare();
-    public abstract void pause();
-    public abstract void stop();
-    public abstract void skipToPrevious();
-    public abstract void skipToNext();
-    public abstract void seekTo(long pos);
-    public abstract void fastFoward();
-    public abstract void rewind();
-
-    public abstract PlaybackState2 getPlaybackState();
-    public abstract AudioAttributes getAudioAttributes();
-
-    public abstract void setPlaylist(List<MediaItem2> item, PlaylistParam param);
-    public abstract void setCurrentPlaylistItem(int index);
-
-    /**
-     * Add a {@link PlaybackListener} to be invoked when the playback state is changed.
-     *
-     * @param executor the Handler that will receive the listener
-     * @param listener the listener that will be run
-     */
-    public abstract void addPlaybackListener(Executor executor, PlaybackListener listener);
-
-    /**
-     * Remove previously added {@link PlaybackListener}.
-     *
-     * @param listener the listener to be removed
-     */
-    public abstract void removePlaybackListener(PlaybackListener listener);
 }
diff --git a/android/media/MediaPlaylistAgent.java b/android/media/MediaPlaylistAgent.java
new file mode 100644
index 0000000..88f37e7
--- /dev/null
+++ b/android/media/MediaPlaylistAgent.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.update.ApiLoader;
+import android.media.update.MediaPlaylistAgentProvider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ * MediaPlaylistAgent is the abstract class an application needs to derive from to pass an object
+ * to a MediaSession2 that will override default playlist handling behaviors. It contains a set of
+ * notify methods to signal MediaSession2 that playlist-related state has changed.
+ * <p>
+ * Playlists are composed of one or multiple {@link MediaItem2} instances, which combine metadata
+ * and data sources (as {@link DataSourceDesc})
+ * Used by {@link MediaSession2} and {@link MediaController2}.
+ */
+// This class only includes methods that contain {@link MediaItem2}.
+public abstract class MediaPlaylistAgent {
+    /**
+     * @hide
+     */
+    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
+            REPEAT_MODE_GROUP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RepeatMode {}
+
+    /**
+     * Playback will be stopped at the end of the playing media list.
+     */
+    public static final int REPEAT_MODE_NONE = 0;
+
+    /**
+     * Playback of the current playing media item will be repeated.
+     */
+    public static final int REPEAT_MODE_ONE = 1;
+
+    /**
+     * Playing media list will be repeated.
+     */
+    public static final int REPEAT_MODE_ALL = 2;
+
+    /**
+     * 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. An example of a group is the playlist.
+     */
+    public static final int REPEAT_MODE_GROUP = 3;
+
+    /**
+     * @hide
+     */
+    @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ShuffleMode {}
+
+    /**
+     * Media list will be played in order.
+     */
+    public static final int SHUFFLE_MODE_NONE = 0;
+
+    /**
+     * Media list will be played in shuffled order.
+     */
+    public static final int SHUFFLE_MODE_ALL = 1;
+
+    /**
+     * 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. An example of a group is the playlist.
+     */
+    public static final int SHUFFLE_MODE_GROUP = 2;
+
+    private final MediaPlaylistAgentProvider mProvider;
+
+    /**
+     * A callback class to receive notifications for events on the media player. See
+     * {@link MediaPlaylistAgent#registerPlaylistEventCallback(Executor, PlaylistEventCallback)}
+     * to register this callback.
+     */
+    public static abstract class PlaylistEventCallback {
+        /**
+         * Called when a playlist is changed.
+         *
+         * @param playlistAgent playlist agent for this event
+         * @param list new playlist
+         * @param metadata new metadata
+         */
+        public void onPlaylistChanged(@NonNull MediaPlaylistAgent playlistAgent,
+                @NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when a playlist metadata is changed.
+         *
+         * @param playlistAgent playlist agent for this event
+         * @param metadata new metadata
+         */
+        public void onPlaylistMetadataChanged(@NonNull MediaPlaylistAgent playlistAgent,
+                @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when the shuffle mode is changed.
+         *
+         * @param playlistAgent playlist agent for this event
+         * @param shuffleMode repeat mode
+         * @see #SHUFFLE_MODE_NONE
+         * @see #SHUFFLE_MODE_ALL
+         * @see #SHUFFLE_MODE_GROUP
+         */
+        public void onShuffleModeChanged(@NonNull MediaPlaylistAgent playlistAgent,
+                @ShuffleMode int shuffleMode) { }
+
+        /**
+         * Called when the repeat mode is changed.
+         *
+         * @param playlistAgent playlist agent for this event
+         * @param repeatMode repeat mode
+         * @see #REPEAT_MODE_NONE
+         * @see #REPEAT_MODE_ONE
+         * @see #REPEAT_MODE_ALL
+         * @see #REPEAT_MODE_GROUP
+         */
+        public void onRepeatModeChanged(@NonNull MediaPlaylistAgent playlistAgent,
+                @RepeatMode int repeatMode) { }
+    }
+
+    public MediaPlaylistAgent() {
+        mProvider = ApiLoader.getProvider().createMediaPlaylistAgent(this);
+    }
+
+    /**
+     * Register {@link PlaylistEventCallback} to listen changes in the underlying
+     * {@link MediaPlaylistAgent}.
+     *
+     * @param executor a callback Executor
+     * @param callback a PlaylistEventCallback
+     * @throws IllegalArgumentException if executor or callback is {@code null}.
+     */
+    public final void registerPlaylistEventCallback(
+            @NonNull @CallbackExecutor Executor executor, @NonNull PlaylistEventCallback callback) {
+        mProvider.registerPlaylistEventCallback_impl(executor, callback);
+    }
+
+    /**
+     * Unregister the previously registered {@link PlaylistEventCallback}.
+     *
+     * @param callback the callback to be removed
+     * @throws IllegalArgumentException if the callback is {@code null}.
+     */
+    public final void unregisterPlaylistEventCallback(@NonNull PlaylistEventCallback callback) {
+        mProvider.unregisterPlaylistEventCallback_impl(callback);
+    }
+
+    public final void notifyPlaylistChanged() {
+        mProvider.notifyPlaylistChanged_impl();
+    }
+
+    public final void notifyPlaylistMetadataChanged() {
+        mProvider.notifyPlaylistMetadataChanged_impl();
+    }
+
+    public final void notifyShuffleModeChanged() {
+        mProvider.notifyShuffleModeChanged_impl();
+    }
+
+    public final void notifyRepeatModeChanged() {
+        mProvider.notifyRepeatModeChanged_impl();
+    }
+
+    /**
+     * Returns the playlist
+     *
+     * @return playlist, or null if none is set.
+     */
+    public @Nullable List<MediaItem2> getPlaylist() {
+        return mProvider.getPlaylist_impl();
+    }
+
+    /**
+     * Sets the playlist.
+     *
+     * @param list playlist
+     * @param metadata metadata of the playlist
+     */
+    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
+        mProvider.setPlaylist_impl(list, metadata);
+    }
+
+    /**
+     * Returns the playlist metadata
+     *
+     * @return metadata metadata of the playlist, or null if none is set
+     */
+    public @Nullable MediaMetadata2 getPlaylistMetadata() {
+        return mProvider.getPlaylistMetadata_impl();
+    }
+
+    /**
+     * Updates the playlist metadata
+     *
+     * @param metadata metadata of the playlist
+     */
+    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
+        mProvider.updatePlaylistMetadata_impl(metadata);
+    }
+
+    /**
+     * Adds the media item to the playlist at position index. Index equals or greater than
+     * the current playlist size will add the item at the end of the playlist.
+     * <p>
+     * This will not change the currently playing media item.
+     * If index is less than or equal to the current index of the playlist,
+     * the current index of the playlist will be incremented correspondingly.
+     *
+     * @param index the index you want to add
+     * @param item the media item you want to add
+     */
+    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
+        mProvider.addPlaylistItem_impl(index, item);
+    }
+
+    /**
+     * Removes the media item from the playlist
+     *
+     * @param item media item to remove
+     */
+    public void removePlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.removePlaylistItem_impl(item);
+    }
+
+    /**
+     * Replace the media item at index in the playlist. This can be also used to update metadata of
+     * an item.
+     *
+     * @param index the index of the item to replace
+     * @param item the new item
+     */
+    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+        mProvider.replacePlaylistItem_impl(index, item);
+    }
+
+    /**
+     * Skips to the the media item, and plays from it.
+     *
+     * @param item media item to start playing from
+     */
+    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.skipToPlaylistItem_impl(item);
+    }
+
+    /**
+     * Skips to the previous item in the playlist.
+     */
+    public void skipToPreviousItem() {
+        mProvider.skipToPreviousItem_impl();
+    }
+
+    /**
+     * Skips to the next item in the playlist.
+     */
+    public void skipToNextItem() {
+        mProvider.skipToNextItem_impl();
+    }
+
+    /**
+     * Gets the repeat mode
+     *
+     * @return repeat mode
+     * @see #REPEAT_MODE_NONE
+     * @see #REPEAT_MODE_ONE
+     * @see #REPEAT_MODE_ALL
+     * @see #REPEAT_MODE_GROUP
+     */
+    public @RepeatMode int getRepeatMode() {
+        return mProvider.getRepeatMode_impl();
+    }
+
+    /**
+     * Sets the repeat mode
+     *
+     * @param repeatMode repeat mode
+     * @see #REPEAT_MODE_NONE
+     * @see #REPEAT_MODE_ONE
+     * @see #REPEAT_MODE_ALL
+     * @see #REPEAT_MODE_GROUP
+     */
+    public void setRepeatMode(@RepeatMode int repeatMode) {
+        mProvider.setRepeatMode_impl(repeatMode);
+    }
+
+    /**
+     * Gets the shuffle mode
+     *
+     * @return The shuffle mode
+     * @see #SHUFFLE_MODE_NONE
+     * @see #SHUFFLE_MODE_ALL
+     * @see #SHUFFLE_MODE_GROUP
+     */
+    public @ShuffleMode int getShuffleMode() {
+        return mProvider.getShuffleMode_impl();
+    }
+
+    /**
+     * Sets the shuffle mode
+     *
+     * @param shuffleMode The shuffle mode
+     * @see #SHUFFLE_MODE_NONE
+     * @see #SHUFFLE_MODE_ALL
+     * @see #SHUFFLE_MODE_GROUP
+     */
+    public void setShuffleMode(@ShuffleMode int shuffleMode) {
+        mProvider.setShuffleMode_impl(shuffleMode);
+    }
+
+    /**
+     * Called by {@link MediaSession2} when it wants to translate {@link DataSourceDesc} from the
+     * {@link MediaPlayerBase.PlayerEventCallback} to the {@link MediaItem2}. Override this method
+     * if you want to create {@link DataSourceDesc}s dynamically, instead of specifying them with
+     * {@link #setPlaylist(List, MediaMetadata2)}.
+     * <p>
+     * Session would throw an exception if this returns {@code null} for {@param dsd} from the
+     * {@link MediaPlayerBase.PlayerEventCallback}.
+     * <p>
+     * Default implementation calls the {@link #getPlaylist()} and searches the {@link MediaItem2}
+     * with the {@param dsd}.
+     *
+     * @param dsd The dsd to query.
+     * @return A {@link MediaItem2} object in the playlist that matches given {@code dsd}.
+     * @throws IllegalArgumentException if {@code dsd} is null
+     */
+    public @Nullable MediaItem2 getMediaItem(@NonNull DataSourceDesc dsd) {
+        return mProvider.getMediaItem_impl(dsd);
+    }
+}
diff --git a/android/media/MediaRecorder.java b/android/media/MediaRecorder.java
index 78477f7..90b6bff 100644
--- a/android/media/MediaRecorder.java
+++ b/android/media/MediaRecorder.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.hardware.Camera;
@@ -25,8 +26,10 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Surface;
 
 import java.io.File;
@@ -34,6 +37,8 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -101,6 +106,8 @@
     private OnErrorListener mOnErrorListener;
     private OnInfoListener mOnInfoListener;
 
+    private int mChannelCount;
+
     /**
      * Default constructor.
      */
@@ -115,6 +122,7 @@
             mEventHandler = null;
         }
 
+        mChannelCount = 1;
         String packageName = ActivityThread.currentPackageName();
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
@@ -275,6 +283,7 @@
          * third-party applications.
          * </p>
          */
+        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
         public static final int REMOTE_SUBMIX = 8;
 
         /** Microphone audio source tuned for unprocessed (raw) sound if available, behaves like
@@ -300,6 +309,7 @@
          * @hide
          */
         @SystemApi
+        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
         public static final int HOTWORD = 1999;
     }
 
@@ -749,6 +759,7 @@
         if (numChannels <= 0) {
             throw new IllegalArgumentException("Number of channels is not positive");
         }
+        mChannelCount = numChannels;
         setParameter("audio-param-number-of-channels=" + numChannels);
     }
 
@@ -1350,6 +1361,7 @@
     /*
      * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
      */
+    @GuardedBy("mRoutingChangeListeners")
     private void enableNativeRoutingCallbacksLocked(boolean enabled) {
         if (mRoutingChangeListeners.size() == 0) {
             native_enableDeviceCallback(enabled);
@@ -1406,6 +1418,45 @@
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback(boolean enabled);
 
+    //--------------------------------------------------------------------------
+    // Microphone information
+    //--------------------
+    /**
+     * Return A lists of {@link MicrophoneInfo} representing the active microphones.
+     * By querying channel mapping for each active microphone, developer can know how
+     * the microphone is used by each channels or a capture stream.
+     *
+     * @return a lists of {@link MicrophoneInfo} representing the active microphones
+     * @throws IOException if an error occurs
+     */
+    public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
+        ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
+        int status = native_getActiveMicrophones(activeMicrophones);
+        if (status != AudioManager.SUCCESS) {
+            Log.e(TAG, "getActiveMicrophones failed:" + status);
+            return new ArrayList<MicrophoneInfo>();
+        }
+        AudioManager.setPortIdForMicrophones(activeMicrophones);
+
+        // Use routed device when there is not information returned by hal.
+        if (activeMicrophones.size() == 0) {
+            AudioDeviceInfo device = getRoutedDevice();
+            if (device != null) {
+                MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
+                ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
+                for (int i = 0; i < mChannelCount; i++) {
+                    channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
+                }
+                microphone.setChannelMapping(channelMapping);
+                activeMicrophones.add(microphone);
+            }
+        }
+        return activeMicrophones;
+    }
+
+    private native final int native_getActiveMicrophones(
+            ArrayList<MicrophoneInfo> activeMicrophones);
+
     /**
      * Called from native code when an interesting event happens.  This method
      * just uses the EventHandler system to post the event back to the main app thread.
diff --git a/android/media/MediaScanner.java b/android/media/MediaScanner.java
index cb4e46f..f476a6c 100644
--- a/android/media/MediaScanner.java
+++ b/android/media/MediaScanner.java
@@ -158,6 +158,7 @@
     public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
     public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
     private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
+    private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
     private static String sLastInternalScanFingerprint;
 
     private static final String[] ID3_GENRES = {
@@ -323,7 +324,6 @@
     private final Uri mAudioUri;
     private final Uri mVideoUri;
     private final Uri mImagesUri;
-    private final Uri mThumbsUri;
     private final Uri mPlaylistsUri;
     private final Uri mFilesUri;
     private final Uri mFilesUriNoNotify;
@@ -419,7 +419,6 @@
         mAudioUri = Audio.Media.getContentUri(volumeName);
         mVideoUri = Video.Media.getContentUri(volumeName);
         mImagesUri = Images.Media.getContentUri(volumeName);
-        mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
         mFilesUri = Files.getContentUri(volumeName);
         mFilesUriNoNotify = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
 
@@ -947,6 +946,7 @@
                 values.put(Audio.Media.IS_MUSIC, music);
                 values.put(Audio.Media.IS_PODCAST, podcasts);
             } else if ((mFileType == MediaFile.FILE_TYPE_JPEG
+                    || mFileType == MediaFile.FILE_TYPE_HEIF
                     || MediaFile.isRawImageFileType(mFileType)) && !mNoMedia) {
                 ExifInterface exif = null;
                 try {
@@ -1153,7 +1153,10 @@
     private static boolean isSystemSoundWithMetadata(String path) {
         if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
                 || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
-                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
+                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
+                || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
+                || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
+                || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
             return true;
         }
         return false;
@@ -1283,53 +1286,6 @@
         }
     }
 
-    private void pruneDeadThumbnailFiles() {
-        HashSet<String> existingFiles = new HashSet<String>();
-        String directory = "/sdcard/DCIM/.thumbnails";
-        String [] files = (new File(directory)).list();
-        Cursor c = null;
-        if (files == null)
-            files = new String[0];
-
-        for (int i = 0; i < files.length; i++) {
-            String fullPathString = directory + "/" + files[i];
-            existingFiles.add(fullPathString);
-        }
-
-        try {
-            c = mMediaProvider.query(
-                    mThumbsUri,
-                    new String [] { "_data" },
-                    null,
-                    null,
-                    null, null);
-            Log.v(TAG, "pruneDeadThumbnailFiles... " + c);
-            if (c != null && c.moveToFirst()) {
-                do {
-                    String fullPathString = c.getString(0);
-                    existingFiles.remove(fullPathString);
-                } while (c.moveToNext());
-            }
-
-            for (String fileToDelete : existingFiles) {
-                if (false)
-                    Log.v(TAG, "fileToDelete is " + fileToDelete);
-                try {
-                    (new File(fileToDelete)).delete();
-                } catch (SecurityException ex) {
-                }
-            }
-
-            Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
-        } catch (RemoteException e) {
-            // We will soon be killed...
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-    }
-
     static class MediaBulkDeleter {
         StringBuilder whereClause = new StringBuilder();
         ArrayList<String> whereArgs = new ArrayList<String>(100);
@@ -1373,9 +1329,6 @@
             processPlayLists();
         }
 
-        if (mOriginalCount == 0 && mImagesUri.equals(Images.Media.getContentUri("external")))
-            pruneDeadThumbnailFiles();
-
         // allow GC to clean up
         mPlayLists.clear();
     }
diff --git a/android/media/MediaSession2.java b/android/media/MediaSession2.java
index 0e90040..2b3c2b4 100644
--- a/android/media/MediaSession2.java
+++ b/android/media/MediaSession2.java
@@ -16,36 +16,37 @@
 
 package android.media;
 
+import static android.media.MediaPlayerBase.BUFFERING_STATE_UNKNOWN;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.session.MediaSession;
-import android.media.session.MediaSession.Callback;
-import android.media.session.PlaybackState;
+import android.media.MediaPlayerBase.BuffState;
+import android.media.MediaPlayerBase.PlayerState;
+import android.media.MediaPlaylistAgent.RepeatMode;
+import android.media.MediaPlaylistAgent.ShuffleMode;
 import android.media.update.ApiLoader;
 import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.media.update.ProviderCreator;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcelable;
+import android.os.IInterface;
 import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.ArraySet;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
+ * @hide
  * Allows a media app to expose its transport controls and playback information in a process to
  * other processes including the Android framework and other apps. Common use cases are as follows.
  * <ul>
@@ -59,7 +60,7 @@
  * sessions can be created to provide finer grain controls of media.
  * <p>
  * If you want to support background playback, {@link MediaSessionService2} is preferred
- * instead. With it, your playback can be revived even after you've finished playback. See
+ * instead. With it, your playback can be revived even after playback is finished. See
  * {@link MediaSessionService2} for details.
  * <p>
  * A session can be obtained by {@link Builder}. The owner of the session may pass its session token
@@ -67,7 +68,8 @@
  * session.
  * <p>
  * When a session receive transport control commands, the session sends the commands directly to
- * the the underlying media player set by {@link Builder} or {@link #setPlayer(MediaPlayerBase)}.
+ * the the underlying media player set by {@link Builder} or
+ * {@link #updatePlayer}.
  * <p>
  * When an app is finished performing playback it must call {@link #close()} to clean up the session
  * and notify any controllers.
@@ -75,228 +77,110 @@
  * {@link MediaSession2} objects should be used on the thread on the looper.
  *
  * @see MediaSessionService2
- * @hide
  */
-// TODO(jaewan): Unhide
-// TODO(jaewan): Revisit comments. Currently it's borrowed from the MediaSession.
-// TODO(jaewan): Should we support thread safe? It may cause tricky issue such as b/63797089
-// TODO(jaewan): Should we make APIs for MediaSessionService2 public? It's helpful for
-//               developers that doesn't want to override from Browser, but user may not use this
-//               correctly.
 public class MediaSession2 implements AutoCloseable {
     private final MediaSession2Provider mProvider;
 
-    // Note: Do not define IntDef because subclass can add more command code on top of these.
-    // TODO(jaewan): Shouldn't we pull out?
-    public static final int COMMAND_CODE_CUSTOM = 0;
-    public static final int COMMAND_CODE_PLAYBACK_START = 1;
-    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
-    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
-    public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
-    public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5;
-    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
-    public static final int COMMAND_CODE_PLAYBACK_FAST_FORWARD = 7;
-    public static final int COMMAND_CODE_PLAYBACK_REWIND = 8;
-    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
-    public static final int COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM = 10;
-
-    public static final int COMMAND_CODE_PLAYLIST_GET = 11;
-    public static final int COMMAND_CODE_PLAYLIST_ADD = 12;
-    public static final int COMMAND_CODE_PLAYLIST_REMOVE = 13;
-
-    public static final int COMMAND_CODE_PLAY_FROM_MEDIA_ID = 14;
-    public static final int COMMAND_CODE_PLAY_FROM_URI = 15;
-    public static final int COMMAND_CODE_PLAY_FROM_SEARCH = 16;
-
-    public static final int COMMAND_CODE_PREPARE_FROM_MEDIA_ID = 17;
-    public static final int COMMAND_CODE_PREPARE_FROM_URI = 18;
-    public static final int COMMAND_CODE_PREPARE_FROM_SEARCH = 19;
+    /**
+     * @hide
+     */
+    @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,
+            ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING,
+            ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE,
+            ERROR_CODE_SETUP_REQUIRED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCode {}
 
     /**
-     * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
-     * <p>
-     * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
-     * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
-     * {@link #getCustomCommand()} shouldn't be {@code null}.
+     * This is the default error code and indicates that none of the other error codes applies.
      */
-    // TODO(jaewan): Move this into the updatable.
-    public static final class Command {
-        private static final String KEY_COMMAND_CODE
-                = "android.media.media_session2.command.command_code";
-        private static final String KEY_COMMAND_CUSTOM_COMMAND
-                = "android.media.media_session2.command.custom_command";
-        private static final String KEY_COMMAND_EXTRA
-                = "android.media.media_session2.command.extra";
-
-        private final int mCommandCode;
-        // Nonnull if it's custom command
-        private final String mCustomCommand;
-        private final Bundle mExtra;
-
-        public Command(int commandCode) {
-            mCommandCode = commandCode;
-            mCustomCommand = null;
-            mExtra = null;
-        }
-
-        public Command(@NonNull String action, @Nullable Bundle extra) {
-            if (action == null) {
-                throw new IllegalArgumentException("action shouldn't be null");
-            }
-            mCommandCode = COMMAND_CODE_CUSTOM;
-            mCustomCommand = action;
-            mExtra = extra;
-        }
-
-        public int getCommandCode() {
-            return mCommandCode;
-        }
-
-        public @Nullable String getCustomCommand() {
-            return mCustomCommand;
-        }
-
-        public @Nullable Bundle getExtra() {
-            return mExtra;
-        }
-
-        /**
-         * @return a new Bundle instance from the Command
-         * @hide
-         */
-        public Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
-            bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
-            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
-            return bundle;
-        }
-
-        /**
-         * @return a new Command instance from the Bundle
-         * @hide
-         */
-        public static Command fromBundle(Bundle command) {
-            int code = command.getInt(KEY_COMMAND_CODE);
-            if (code != COMMAND_CODE_CUSTOM) {
-                return new Command(code);
-            } else {
-                String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
-                if (customCommand == null) {
-                    return null;
-                }
-                return new Command(customCommand, command.getBundle(KEY_COMMAND_EXTRA));
-            }
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof Command)) {
-                return false;
-            }
-            Command other = (Command) obj;
-            // TODO(jaewan): Should we also compare contents in bundle?
-            //               It may not be possible if the bundle contains private class.
-            return mCommandCode == other.mCommandCode
-                    && TextUtils.equals(mCustomCommand, other.mCustomCommand);
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            return ((mCustomCommand != null) ? mCustomCommand.hashCode() : 0) * prime + mCommandCode;
-        }
-    }
+    public static final int ERROR_CODE_UNKNOWN_ERROR = 0;
 
     /**
-     * Represent set of {@link Command}.
+     * Error code when the application state is invalid to fulfill the request.
      */
-    // TODO(jaewan): Move this to updatable
-    public static class CommandGroup {
-        private static final String KEY_COMMANDS =
-                "android.media.mediasession2.commandgroup.commands";
-        private ArraySet<Command> mCommands = new ArraySet<>();
+    public static final int ERROR_CODE_APP_ERROR = 1;
 
-        public CommandGroup() {
-        }
+    /**
+     * Error code when the request is not supported by the application.
+     */
+    public static final int ERROR_CODE_NOT_SUPPORTED = 2;
 
-        public CommandGroup(CommandGroup others) {
-            mCommands.addAll(others.mCommands);
-        }
+    /**
+     * Error code when the request cannot be performed because authentication has expired.
+     */
+    public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3;
 
-        public void addCommand(Command command) {
-            mCommands.add(command);
-        }
+    /**
+     * Error code when a premium account is required for the request to succeed.
+     */
+    public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4;
 
-        public void addAllPredefinedCommands() {
-            // TODO(jaewan): Is there any better way than this?
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_START));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PAUSE));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_STOP));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
-            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
-        }
+    /**
+     * Error code when too many concurrent streams are detected.
+     */
+    public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5;
 
-        public void removeCommand(Command command) {
-            mCommands.remove(command);
-        }
+    /**
+     * Error code when the content is blocked due to parental controls.
+     */
+    public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6;
 
-        public boolean hasCommand(Command command) {
-            return mCommands.contains(command);
-        }
+    /**
+     * Error code when the content is blocked due to being regionally unavailable.
+     */
+    public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7;
 
-        public boolean hasCommand(int code) {
-            if (code == COMMAND_CODE_CUSTOM) {
-                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
-            }
-            for (int i = 0; i < mCommands.size(); i++) {
-                if (mCommands.valueAt(i).getCommandCode() == code) {
-                    return true;
-                }
-            }
-            return false;
-        }
+    /**
+     * Error code when the requested content is already playing.
+     */
+    public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8;
 
+    /**
+     * Error code when the application cannot skip any more songs because skip limit is reached.
+     */
+    public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9;
+
+    /**
+     * Error code when the action is interrupted due to some external event.
+     */
+    public static final int ERROR_CODE_ACTION_ABORTED = 10;
+
+    /**
+     * Error code when the playback navigation (previous, next) is not possible because the queue
+     * was exhausted.
+     */
+    public static final int ERROR_CODE_END_OF_QUEUE = 11;
+
+    /**
+     * Error code when the session needs user's manual intervention.
+     */
+    public static final int ERROR_CODE_SETUP_REQUIRED = 12;
+
+    /**
+     * Interface definition of a callback to be invoked when a {@link MediaItem2} in the playlist
+     * didn't have a {@link DataSourceDesc} but it's needed now for preparing or playing it.
+     *
+     * #see #setOnDataSourceMissingHelper
+     */
+    public interface OnDataSourceMissingHelper {
         /**
-         * @return new bundle from the CommandGroup
-         * @hide
+         * Called when a {@link MediaItem2} in the playlist didn't have a {@link DataSourceDesc}
+         * but it's needed now for preparing or playing it. Returned data source descriptor will be
+         * sent to the player directly to prepare or play the contents.
+         * <p>
+         * An exception may be thrown if the returned {@link DataSourceDesc} is duplicated in the
+         * playlist, so items cannot be differentiated.
+         *
+         * @param session the session for this event
+         * @param item media item from the controller
+         * @return a data source descriptor if the media item. Can be {@code null} if the content
+         *        isn't available.
          */
-        public Bundle toBundle() {
-            ArrayList<Bundle> list = new ArrayList<>();
-            for (int i = 0; i < mCommands.size(); i++) {
-                list.add(mCommands.valueAt(i).toBundle());
-            }
-            Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(KEY_COMMANDS, list);
-            return bundle;
-        }
-
-        /**
-         * @return new instance of CommandGroup from the bundle
-         * @hide
-         */
-        public static @Nullable CommandGroup fromBundle(Bundle commands) {
-            if (commands == null) {
-                return null;
-            }
-            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
-            if (list == null) {
-                return null;
-            }
-            CommandGroup commandGroup = new CommandGroup();
-            for (int i = 0; i < list.size(); i++) {
-                Parcelable parcelable = list.get(i);
-                if (!(parcelable instanceof Bundle)) {
-                    continue;
-                }
-                Bundle commandBundle = (Bundle) parcelable;
-                Command command = Command.fromBundle(commandBundle);
-                if (command != null) {
-                    commandGroup.addCommand(command);
-                }
-            }
-            return commandGroup;
-        }
+        @Nullable DataSourceDesc onDataSourceMissing(@NonNull MediaSession2 session,
+                @NonNull MediaItem2 item);
     }
 
     /**
@@ -305,21 +189,23 @@
      * If it's not set, the session will accept all controllers and all incoming commands by
      * default.
      */
-    // TODO(jaewan): Can we move this inside of the updatable for default implementation.
-    public static class SessionCallback {
+    // TODO(jaewan): Move this to updatable for default implementation (b/74091963)
+    public static abstract class SessionCallback {
         /**
          * Called when a controller is created for this session. Return allowed commands for
          * controller. By default it allows all connection requests and commands.
          * <p>
          * You can reject the connection by return {@code null}. In that case, controller receives
-         * {@link MediaController2.ControllerCallback#onDisconnected()} and cannot be usable.
+         * {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)} and cannot
+         * be usable.
          *
+         * @param session the session for this event
          * @param controller controller information.
-         * @return allowed commands. Can be {@code null} to reject coonnection.
+         * @return allowed commands. Can be {@code null} to reject connection.
          */
-        // TODO(jaewan): Change return type. Once we do, null is for reject.
-        public @Nullable CommandGroup onConnect(@NonNull ControllerInfo controller) {
-            CommandGroup commands = new CommandGroup();
+        public @Nullable SessionCommandGroup2 onConnect(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) {
+            SessionCommandGroup2 commands = new SessionCommandGroup2();
             commands.addAllPredefinedCommands();
             return commands;
         }
@@ -327,213 +213,395 @@
         /**
          * Called when a controller is disconnected
          *
+         * @param session the session for this event
          * @param controller controller information
          */
-        public void onDisconnected(@NonNull ControllerInfo controller) { }
+        public void onDisconnected(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller) { }
 
         /**
-         * Called when a controller sent a command to the session, and the command will be sent to
-         * the player directly unless you reject the request by {@code false}.
+         * Called when a controller sent a command that will be sent directly to the player. Return
+         * {@code false} here to reject the request and stop sending command to the player.
          *
+         * @param session the session for this event
          * @param controller controller information.
          * @param command a command. This method will be called for every single command.
          * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PLAY
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PAUSE
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_STOP
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_PREPARE
+         * @see SessionCommand2#COMMAND_CODE_SESSION_FAST_FORWARD
+         * @see SessionCommand2#COMMAND_CODE_SESSION_REWIND
+         * @see SessionCommand2#COMMAND_CODE_PLAYBACK_SEEK_TO
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REMOVE_ITEM
+         * @see SessionCommand2#COMMAND_CODE_PLAYLIST_GET_LIST
+         * @see SessionCommand2#COMMAND_CODE_SET_VOLUME
          */
-        // TODO(jaewan): Add more documentations (or make it clear) which commands can be filtered
-        //               with this.
-        public boolean onCommandRequest(@NonNull ControllerInfo controller,
-                @NonNull Command command) {
+        public boolean onCommandRequest(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull SessionCommand2 command) {
             return true;
         }
 
         /**
-         * Called when a controller set rating on the currently playing contents.
+         * Called when a controller set rating of a media item through
+         * {@link MediaController2#setRating(String, Rating2)}.
+         * <p>
+         * To allow setting user rating for a {@link MediaItem2}, the media item's metadata
+         * should have {@link Rating2} with the key {@link MediaMetadata#METADATA_KEY_USER_RATING},
+         * in order to provide possible rating style for controller. Controller will follow the
+         * rating style.
          *
-         * @param
+         * @param session the session for this event
+         * @param controller controller information
+         * @param mediaId media id from the controller
+         * @param rating new rating from the controller
          */
-        public void onSetRating(@NonNull ControllerInfo controller, @NonNull Rating2 rating) { }
+        public void onSetRating(@NonNull MediaSession2 session, @NonNull ControllerInfo controller,
+                @NonNull String mediaId, @NonNull Rating2 rating) { }
 
         /**
-         * Called when a controller sent a custom command.
+         * Called when a controller sent a custom command through
+         * {@link MediaController2#sendCustomCommand(SessionCommand2, Bundle, ResultReceiver)}.
          *
+         * @param session the session for this event
          * @param controller controller information
          * @param customCommand custom command.
          * @param args optional arguments
          * @param cb optional result receiver
          */
-        public void onCustomCommand(@NonNull ControllerInfo controller,
-                @NonNull Command customCommand, @Nullable Bundle args,
-                @Nullable ResultReceiver cb) { }
+        public void onCustomCommand(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull SessionCommand2 customCommand,
+                @Nullable Bundle args, @Nullable ResultReceiver cb) { }
 
         /**
-         * Override to handle requests to prepare for playing a specific mediaId.
+         * Called when a controller requested to play a specific mediaId through
+         * {@link MediaController2#playFromMediaId(String, Bundle)}.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param mediaId media id
+         * @param extras optional extra bundle
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID
+         */
+        public void onPlayFromMediaId(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull String mediaId,
+                @Nullable Bundle extras) { }
+
+        /**
+         * Called when a controller requested to begin playback from a search query through
+         * {@link MediaController2#playFromSearch(String, Bundle)}
+         * <p>
+         * An empty query indicates that the app may play any music. The implementation should
+         * attempt to make a smart choice about what to play.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param query query string. Can be empty to indicate any suggested media
+         * @param extras optional extra bundle
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_SEARCH
+         */
+        public void onPlayFromSearch(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull String query,
+                @Nullable Bundle extras) { }
+
+        /**
+         * Called when a controller requested to play a specific media item represented by a URI
+         * through {@link MediaController2#playFromUri(Uri, Bundle)}
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param uri uri
+         * @param extras optional extra bundle
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PLAY_FROM_URI
+         */
+        public void onPlayFromUri(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull Uri uri,
+                @Nullable Bundle extras) { }
+
+        /**
+         * Called when a controller requested to prepare for playing a specific mediaId through
+         * {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+         * <p>
          * During the preparation, a session should not hold audio focus in order to allow other
          * sessions play seamlessly. The state of playback should be updated to
-         * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * {@link MediaPlayerBase#PLAYER_STATE_PAUSED} after the preparation is done.
          * <p>
          * The playback of the prepared content should start in the later calls of
          * {@link MediaSession2#play()}.
          * <p>
          * Override {@link #onPlayFromMediaId} to handle requests for starting
          * playback without preparation.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param mediaId media id to prepare
+         * @param extras optional extra bundle
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID
          */
-        public void onPlayFromMediaId(@NonNull ControllerInfo controller,
-                @NonNull String mediaId, @Nullable Bundle extras) { }
+        public void onPrepareFromMediaId(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull String mediaId,
+                @Nullable Bundle extras) { }
 
         /**
-         * 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 sessions play seamlessly. The state of playback should be
-         * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * Called when a controller requested to prepare playback from a search query through
+         * {@link MediaController2#prepareFromSearch(String, Bundle)}.
          * <p>
-         * The playback of the prepared content should start in the later calls of
-         * {@link MediaSession2#play()}.
+         * An empty query indicates that the app may prepare any music. The implementation should
+         * attempt to make a smart choice about what to play.
+         * <p>
+         * The state of playback should be updated to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}
+         * after the preparation is done. The playback of the prepared content should start in the
+         * later calls of {@link MediaSession2#play()}.
          * <p>
          * Override {@link #onPlayFromSearch} to handle requests for starting playback without
          * preparation.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param query query string. Can be empty to indicate any suggested media
+         * @param extras optional extra bundle
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH
          */
-        public void onPlayFromSearch(@NonNull ControllerInfo controller,
-                @NonNull String query, @Nullable Bundle extras) { }
+        public void onPrepareFromSearch(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull String query,
+                @Nullable Bundle extras) { }
 
         /**
-         * Override to handle requests to prepare a specific media item represented by a URI.
+         * Called when a controller requested to prepare a specific media item represented by a URI
+         * through {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+         * <p>
          * During the preparation, a session should not hold audio focus in order to allow
          * other sessions play seamlessly. The state of playback should be updated to
-         * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+         * {@link MediaPlayerBase#PLAYER_STATE_PAUSED} after the preparation is done.
          * <p>
          * The playback of the prepared content should start in the later calls of
          * {@link MediaSession2#play()}.
          * <p>
          * Override {@link #onPlayFromUri} to handle requests for starting playback without
          * preparation.
+         *
+         * @param session the session for this event
+         * @param controller controller information
+         * @param uri uri
+         * @param extras optional extra bundle
+         * @see SessionCommand2#COMMAND_CODE_SESSION_PREPARE_FROM_URI
          */
-        public void onPlayFromUri(@NonNull ControllerInfo controller,
-                @NonNull String uri, @Nullable Bundle extras) { }
+        public void onPrepareFromUri(@NonNull MediaSession2 session,
+                @NonNull ControllerInfo controller, @NonNull Uri uri, @Nullable Bundle extras) { }
 
         /**
-         * Override to handle requests to play a specific mediaId.
+         * Called when a controller called {@link MediaController2#fastForward()}
+         *
+         * @param session the session for this event
          */
-        public void onPrepareFromMediaId(@NonNull ControllerInfo controller,
-                @NonNull String mediaId, @Nullable Bundle extras) { }
+        public void onFastForward(@NonNull MediaSession2 session) { }
 
         /**
-         * Override to handle requests to begin playback from a search query. An
-         * empty query indicates that the app may play any music. The
-         * implementation should attempt to make a smart choice about what to
-         * play.
+         * Called when a controller called {@link MediaController2#rewind()}
+         *
+         * @param session the session for this event
          */
-        public void onPrepareFromSearch(@NonNull ControllerInfo controller,
-                @NonNull String query, @Nullable Bundle extras) { }
+        public void onRewind(@NonNull MediaSession2 session) { }
 
         /**
-         * Override to handle requests to play a specific media item represented by a URI.
-         */
-        public void prepareFromUri(@NonNull ControllerInfo controller,
-                @NonNull Uri uri, @Nullable Bundle extras) { }
-
-        /**
-         * Called when a controller wants to add a {@link MediaItem2} at the specified position
-         * in the play queue.
+         * Called when the player's current playing item is changed
          * <p>
-         * The item from the media controller wouldn't have valid data source descriptor because
-         * it would have been anonymized when it's sent to the remote process.
+         * When it's called, you should invalidate previous playback information and wait for later
+         * callbacks.
          *
-         * @param item The media item to be inserted.
-         * @param index The index at which the item is to be inserted.
+         * @param session the controller for this event
+         * @param player the player for this event
+         * @param item new item
          */
-        public void onAddPlaylistItem(@NonNull ControllerInfo controller,
-                @NonNull MediaItem2 item, int index) { }
+        // TODO(jaewan): Use this (b/74316764)
+        public void onCurrentMediaItemChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlayerBase player, @NonNull MediaItem2 item) { }
 
         /**
-         * Called when a controller wants to remove the {@link MediaItem2}
-         *
-         * @param item
+         * Called when the player is <i>prepared</i>, i.e. it is ready to play the content
+         * referenced by the given data source.
+         * @param session the session for this event
+         * @param player the player for this event
+         * @param item the media item for which buffering is happening
          */
-        // Can we do this automatically?
-        public void onRemovePlaylistItem(@NonNull MediaItem2 item) { }
-    };
+        public void onMediaPrepared(@NonNull MediaSession2 session, @NonNull MediaPlayerBase player,
+                @NonNull MediaItem2 item) { }
+
+        /**
+         * Called to indicate that the state of the player has changed.
+         * See {@link MediaPlayerBase#getPlayerState()} for polling the player state.
+         * @param session the session for this event
+         * @param player the player for this event
+         * @param state the new state of the player.
+         */
+        public void onPlayerStateChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlayerBase player, @PlayerState int state) { }
+
+        /**
+         * Called to report buffering events for a data source.
+         *
+         * @param session the session for this event
+         * @param player the player for this event
+         * @param item the media item for which buffering is happening.
+         * @param state the new buffering state.
+         */
+        public void onBufferingStateChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlayerBase player, @NonNull MediaItem2 item, @BuffState int state) { }
+
+        /**
+         * Called to indicate that the playback speed has changed.
+         * @param session the session for this event
+         * @param player the player for this event
+         * @param speed the new playback speed.
+         */
+        public void onPlaybackSpeedChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlayerBase player, float speed) { }
+
+        /**
+         * Called to indicate that {@link #seekTo(long)} is completed.
+         *
+         * @param session the session for this event.
+         * @param mpb the player that has completed seeking.
+         * @param position the previous seeking request.
+         * @see #seekTo(long)
+         */
+        public void onSeekCompleted(@NonNull MediaSession2 session, @NonNull MediaPlayerBase mpb,
+                long position) { }
+
+        /**
+         * Called when a playlist is changed from the {@link MediaPlaylistAgent}.
+         * <p>
+         * This is called when the underlying agent has called
+         * {@link MediaPlaylistAgent.PlaylistEventCallback#onPlaylistChanged(MediaPlaylistAgent,
+         * List, MediaMetadata2)}.
+         *
+         * @param session the session for this event
+         * @param playlistAgent playlist agent for this event
+         * @param list new playlist
+         * @param metadata new metadata
+         */
+        public void onPlaylistChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent playlistAgent, @NonNull List<MediaItem2> list,
+                @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when a playlist metadata is changed.
+         *
+         * @param session the session for this event
+         * @param playlistAgent playlist agent for this event
+         * @param metadata new metadata
+         */
+        public void onPlaylistMetadataChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent playlistAgent, @Nullable MediaMetadata2 metadata) { }
+
+        /**
+         * Called when the shuffle mode is changed.
+         *
+         * @param session the session for this event
+         * @param playlistAgent playlist agent for this event
+         * @param shuffleMode repeat mode
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+         * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+         */
+        public void onShuffleModeChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent playlistAgent,
+                @MediaPlaylistAgent.ShuffleMode int shuffleMode) { }
+
+        /**
+         * Called when the repeat mode is changed.
+         *
+         * @param session the session for this event
+         * @param playlistAgent playlist agent for this event
+         * @param repeatMode repeat mode
+         * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+         * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+         * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+         */
+        public void onRepeatModeChanged(@NonNull MediaSession2 session,
+                @NonNull MediaPlaylistAgent playlistAgent,
+                @MediaPlaylistAgent.RepeatMode int repeatMode) { }
+    }
 
     /**
-     * Base builder class for MediaSession2 and its subclass.
-     *
+     * Base builder class for MediaSession2 and its subclass. Any change in this class should be
+     * also applied to the subclasses {@link MediaSession2.Builder} and
+     * {@link MediaLibraryService2.MediaLibrarySession.Builder}.
+     * <p>
+     * APIs here should be package private, but should have documentations for developers.
+     * Otherwise, javadoc will generate documentation with the generic types such as follows.
+     * <pre>U extends BuilderBase<T, U, C> setSessionCallback(Executor executor, C callback)</pre>
+     * <p>
+     * This class is hidden to prevent from generating test stub, which fails with
+     * 'unexpected bound' because it tries to auto generate stub class as follows.
+     * <pre>abstract static class BuilderBase<
+     *      T extends android.media.MediaSession2,
+     *      U extends android.media.MediaSession2.BuilderBase<
+     *              T, U, C extends android.media.MediaSession2.SessionCallback>, C></pre>
      * @hide
      */
     static abstract class BuilderBase
-            <T extends MediaSession2.BuilderBase<T, C>, C extends SessionCallback> {
-        final Context mContext;
-        final MediaPlayerBase mPlayer;
-        String mId;
-        Executor mCallbackExecutor;
-        C mCallback;
-        VolumeProvider mVolumeProvider;
-        int mRatingType;
-        PendingIntent mSessionActivity;
+            <T extends MediaSession2, U extends BuilderBase<T, U, C>, C extends SessionCallback> {
+        private final BuilderBaseProvider<T, C> mProvider;
 
-        /**
-         * Constructor.
-         *
-         * @param context a context
-         * @param player a player to handle incoming command from any controller.
-         * @throws IllegalArgumentException if any parameter is null, or the player is a
-         *      {@link MediaSession2} or {@link MediaController2}.
-         */
-        // TODO(jaewan): Also need executor
-        public BuilderBase(@NonNull Context context, @NonNull MediaPlayerBase player) {
-            if (context == null) {
-                throw new IllegalArgumentException("context shouldn't be null");
-            }
-            if (player == null) {
-                throw new IllegalArgumentException("player shouldn't be null");
-            }
-            mContext = context;
-            mPlayer = player;
-            // Ensure non-null
-            mId = "";
+        BuilderBase(ProviderCreator<BuilderBase<T, U, C>, BuilderBaseProvider<T, C>> creator) {
+            mProvider = creator.createProvider(this);
         }
 
         /**
-         * Set volume provider to configure this session to use remote volume handling.
-         * This must be called to receive volume button events, otherwise the system
-         * will adjust the appropriate stream volume for this session's player.
+         * Sets the underlying {@link MediaPlayerBase} for this session to dispatch incoming event
+         * to.
+         *
+         * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
+         */
+        @NonNull U setPlayer(@NonNull MediaPlayerBase player) {
+            mProvider.setPlayer_impl(player);
+            return (U) this;
+        }
+
+        /**
+         * Sets the {@link MediaPlaylistAgent} for this session to manages playlist of the
+         * underlying {@link MediaPlayerBase}. The playlist agent should manage
+         * {@link MediaPlayerBase} for calling {@link MediaPlayerBase#setNextDataSources(List)}.
          * <p>
-         * Set {@code null} to reset.
+         * If the {@link MediaPlaylistAgent} isn't set, session will create the default playlist
+         * agent.
          *
-         * @param volumeProvider The provider that will handle volume changes. Can be {@code null}
+         * @param playlistAgent a {@link MediaPlaylistAgent} that manages playlist of the
+         *                      {@code player}
          */
-        public T setVolumeProvider(@Nullable VolumeProvider volumeProvider) {
-            mVolumeProvider = volumeProvider;
-            return (T) this;
+        U setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
+            mProvider.setPlaylistAgent_impl(playlistAgent);
+            return (U) this;
         }
 
         /**
-         * Set 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 Rating2#RATING_NONE}</li>
-         * <li>{@link Rating2#RATING_3_STARS}</li>
-         * <li>{@link Rating2#RATING_4_STARS}</li>
-         * <li>{@link Rating2#RATING_5_STARS}</li>
-         * <li>{@link Rating2#RATING_HEART}</li>
-         * <li>{@link Rating2#RATING_PERCENTAGE}</li>
-         * <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
-         * </ul>
+         * Sets the {@link VolumeProvider2} for this session to handle volume events. If not set,
+         * system will adjust the appropriate stream volume for this session's player.
+         *
+         * @param volumeProvider The provider that will receive volume button events.
          */
-        public T setRatingType(@Rating2.Style int type) {
-            mRatingType = type;
-            return (T) this;
+        @NonNull U setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+            mProvider.setVolumeProvider_impl(volumeProvider);
+            return (U) this;
         }
 
         /**
          * Set 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)}.
+         * activity that may be started using {@link Context#startActivity(Intent)}.
          *
          * @param pi The intent to launch to show UI for this session.
          */
-        public T setSessionActivity(@Nullable PendingIntent pi) {
-            mSessionActivity = pi;
-            return (T) this;
+        @NonNull U setSessionActivity(@Nullable PendingIntent pi) {
+            mProvider.setSessionActivity_impl(pi);
+            return (U) this;
         }
 
         /**
@@ -546,32 +614,22 @@
          * @throws IllegalArgumentException if id is {@code null}
          * @return
          */
-        public T setId(@NonNull String id) {
-            if (id == null) {
-                throw new IllegalArgumentException("id shouldn't be null");
-            }
-            mId = id;
-            return (T) this;
+        @NonNull U setId(@NonNull String id) {
+            mProvider.setId_impl(id);
+            return (U) this;
         }
 
         /**
-         * Set {@link SessionCallback}.
+         * Set callback for the session.
          *
          * @param executor callback executor
          * @param callback session callback.
          * @return
          */
-        public T setSessionCallback(@NonNull @CallbackExecutor Executor executor,
+        @NonNull U setSessionCallback(@NonNull @CallbackExecutor Executor executor,
                 @NonNull C callback) {
-            if (executor == null) {
-                throw new IllegalArgumentException("executor shouldn't be null");
-            }
-            if (callback == null) {
-                throw new IllegalArgumentException("callback shouldn't be null");
-            }
-            mCallbackExecutor = executor;
-            mCallback = callback;
-            return (T) this;
+            mProvider.setSessionCallback_impl(executor, callback);
+            return (U) this;
         }
 
         /**
@@ -581,7 +639,9 @@
          * @throws IllegalStateException if the session with the same id is already exists for the
          *      package.
          */
-        public abstract MediaSession2 build() throws IllegalStateException;
+        @NonNull T build() {
+            return mProvider.build_impl();
+        }
     }
 
     /**
@@ -590,47 +650,70 @@
      * Any incoming event from the {@link MediaController2} will be handled on the thread
      * that created session with the {@link Builder#build()}.
      */
-    // TODO(jaewan): Move this to updatable
-    // TODO(jaewan): Add setRatingType()
-    // TODO(jaewan): Add setSessionActivity()
-    public static final class Builder extends BuilderBase<Builder, SessionCallback> {
-        public Builder(Context context, @NonNull MediaPlayerBase player) {
-            super(context, player);
+    // Override all methods just to show them with the type instead of generics in Javadoc.
+    // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
+    public static final class Builder extends BuilderBase<MediaSession2, Builder, SessionCallback> {
+        public Builder(Context context) {
+            super((instance) -> ApiLoader.getProvider().createMediaSession2Builder(
+                    context, (Builder) instance));
         }
 
         @Override
-        public MediaSession2 build() throws IllegalStateException {
-            if (mCallback == null) {
-                mCallback = new SessionCallback();
-            }
-            return new MediaSession2(mContext, mPlayer, mId, mCallbackExecutor, mCallback,
-                    mVolumeProvider, mRatingType, mSessionActivity);
+        public @NonNull Builder setPlayer(@NonNull MediaPlayerBase player) {
+            return super.setPlayer(player);
+        }
+
+        @Override
+        public Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
+            return super.setPlaylistAgent(playlistAgent);
+        }
+
+        @Override
+        public @NonNull Builder setVolumeProvider(@Nullable VolumeProvider2 volumeProvider) {
+            return super.setVolumeProvider(volumeProvider);
+        }
+
+        @Override
+        public @NonNull Builder setSessionActivity(@Nullable PendingIntent pi) {
+            return super.setSessionActivity(pi);
+        }
+
+        @Override
+        public @NonNull Builder setId(@NonNull String id) {
+            return super.setId(id);
+        }
+
+        @Override
+        public @NonNull Builder setSessionCallback(@NonNull Executor executor,
+                @Nullable SessionCallback callback) {
+            return super.setSessionCallback(executor, callback);
+        }
+
+        @Override
+        public @NonNull MediaSession2 build() {
+            return super.build();
         }
     }
 
     /**
      * Information of a controller.
      */
-    // TODO(jaewan): Move implementation to the updatable.
     public static final class ControllerInfo {
         private final ControllerInfoProvider mProvider;
 
         /**
          * @hide
          */
-        // TODO(jaewan): SystemApi
-        // TODO(jaewan): Also accept componentName to check notificaiton listener.
-        public ControllerInfo(Context context, int uid, int pid, String packageName,
-                IMediaSession2Callback callback) {
-            mProvider = ApiLoader.getProvider(context)
-                    .createMediaSession2ControllerInfoProvider(
-                            this, context, uid, pid, packageName, callback);
+        public ControllerInfo(@NonNull Context context, int uid, int pid,
+                @NonNull String packageName, @NonNull IInterface callback) {
+            mProvider = ApiLoader.getProvider().createMediaSession2ControllerInfo(
+                    context, this, uid, pid, packageName, callback);
         }
 
         /**
          * @return package name of the controller
          */
-        public String getPackageName() {
+        public @NonNull String getPackageName() {
             return mProvider.getPackageName_impl();
         }
 
@@ -654,10 +737,8 @@
 
         /**
          * @hide
-         * @return
          */
-        // TODO(jaewan): SystemApi
-        public ControllerInfoProvider getProvider() {
+        public @NonNull ControllerInfoProvider getProvider() {
             return mProvider;
         }
 
@@ -668,52 +749,28 @@
 
         @Override
         public boolean equals(Object obj) {
-            if (!(obj instanceof ControllerInfo)) {
-                return false;
-            }
-            ControllerInfo other = (ControllerInfo) obj;
-            return mProvider.equals_impl(other.mProvider);
+            return mProvider.equals_impl(obj);
         }
 
         @Override
         public String toString() {
-            // TODO(jaewan): Move this to updatable.
-            return "ControllerInfo {pkg=" + getPackageName() + ", uid=" + getUid() + ", trusted="
-                    + isTrusted() + "}";
+            return mProvider.toString_impl();
         }
     }
 
     /**
-     * Button for a {@link Command} that will be shown by the controller.
+     * Button for a {@link SessionCommand2} that will be shown by the controller.
      * <p>
      * It's up to the controller's decision to respect or ignore this customization request.
      */
-    // TODO(jaewan): Move this to updatable.
-    public static class CommandButton {
-        private static final String KEY_COMMAND
-                = "android.media.media_session2.command_button.command";
-        private static final String KEY_ICON_RES_ID
-                = "android.media.media_session2.command_button.icon_res_id";
-        private static final String KEY_DISPLAY_NAME
-                = "android.media.media_session2.command_button.display_name";
-        private static final String KEY_EXTRA
-                = "android.media.media_session2.command_button.extra";
-        private static final String KEY_ENABLED
-                = "android.media.media_session2.command_button.enabled";
+    public static final class CommandButton {
+        private final CommandButtonProvider mProvider;
 
-        private Command mCommand;
-        private int mIconResId;
-        private String mDisplayName;
-        private Bundle mExtra;
-        private boolean mEnabled;
-
-        private CommandButton(@Nullable Command command, int iconResId,
-                @Nullable String displayName, Bundle extra, boolean enabled) {
-            mCommand = command;
-            mIconResId = iconResId;
-            mDisplayName = displayName;
-            mExtra = extra;
-            mEnabled = enabled;
+        /**
+         * @hide
+         */
+        public CommandButton(CommandButtonProvider provider) {
+            mProvider = provider;
         }
 
         /**
@@ -722,8 +779,9 @@
          *
          * @return command or {@code null}
          */
-        public @Nullable Command getCommand() {
-            return mCommand;
+        public @Nullable
+        SessionCommand2 getCommand() {
+            return mProvider.getCommand_impl();
         }
 
         /**
@@ -733,7 +791,7 @@
          * @return resource id of the icon. Can be {@code 0}.
          */
         public int getIconResId() {
-            return mIconResId;
+            return mProvider.getIconResId_impl();
         }
 
         /**
@@ -743,7 +801,7 @@
          * @return custom display name. Can be {@code null} or empty.
          */
         public @Nullable String getDisplayName() {
-            return mDisplayName;
+            return mProvider.getDisplayName_impl();
         }
 
         /**
@@ -751,8 +809,8 @@
          *
          * @return
          */
-        public @Nullable Bundle getExtra() {
-            return mExtra;
+        public @Nullable Bundle getExtras() {
+            return mProvider.getExtras_impl();
         }
 
         /**
@@ -761,181 +819,53 @@
          * @return {@code true} if enabled. {@code false} otherwise.
          */
         public boolean isEnabled() {
-            return mEnabled;
+            return mProvider.isEnabled_impl();
         }
 
         /**
          * @hide
          */
-        // TODO(jaewan): @SystemApi
-        public @NonNull Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
-            bundle.putInt(KEY_ICON_RES_ID, mIconResId);
-            bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
-            bundle.putBundle(KEY_EXTRA, mExtra);
-            bundle.putBoolean(KEY_ENABLED, mEnabled);
-            return bundle;
-        }
-
-        /**
-         * @hide
-         */
-        // TODO(jaewan): @SystemApi
-        public static @Nullable CommandButton fromBundle(Bundle bundle) {
-            Builder builder = new Builder();
-            builder.setCommand(Command.fromBundle(bundle.getBundle(KEY_COMMAND)));
-            builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
-            builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
-            builder.setExtra(bundle.getBundle(KEY_EXTRA));
-            builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
-            try {
-                return builder.build();
-            } catch (IllegalStateException e) {
-                // Malformed or version mismatch. Return null for now.
-                return null;
-            }
+        public @NonNull CommandButtonProvider getProvider() {
+            return mProvider;
         }
 
         /**
          * Builder for {@link CommandButton}.
          */
-        public static class Builder {
-            private Command mCommand;
-            private int mIconResId;
-            private String mDisplayName;
-            private Bundle mExtra;
-            private boolean mEnabled;
+        public static final class Builder {
+            private final CommandButtonProvider.BuilderProvider mProvider;
 
             public Builder() {
-                mEnabled = true;
+                mProvider = ApiLoader.getProvider().createMediaSession2CommandButtonBuilder(this);
             }
 
-            public Builder setCommand(Command command) {
-                mCommand = command;
-                return this;
+            public @NonNull Builder setCommand(@Nullable SessionCommand2 command) {
+                return mProvider.setCommand_impl(command);
             }
 
-            public Builder setIconResId(int resId) {
-                mIconResId = resId;
-                return this;
+            public @NonNull Builder setIconResId(int resId) {
+                return mProvider.setIconResId_impl(resId);
             }
 
-            public Builder setDisplayName(String displayName) {
-                mDisplayName = displayName;
-                return this;
+            public @NonNull Builder setDisplayName(@Nullable String displayName) {
+                return mProvider.setDisplayName_impl(displayName);
             }
 
-            public Builder setEnabled(boolean enabled) {
-                mEnabled = enabled;
-                return this;
+            public @NonNull Builder setEnabled(boolean enabled) {
+                return mProvider.setEnabled_impl(enabled);
             }
 
-            public Builder setExtra(Bundle extra) {
-                mExtra = extra;
-                return this;
+            public @NonNull Builder setExtras(@Nullable Bundle extras) {
+                return mProvider.setExtras_impl(extras);
             }
 
-            public CommandButton build() {
-                if (mEnabled && mCommand == null) {
-                    throw new IllegalStateException("Enabled button needs Command"
-                            + " for controller to invoke the command");
-                }
-                if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
-                        && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
-                    throw new IllegalStateException("Custom commands needs icon and"
-                            + " and name to display");
-                }
-                return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled);
+            public @NonNull CommandButton build() {
+                return mProvider.build_impl();
             }
         }
     }
 
     /**
-     * Parameter for the playlist.
-     */
-    // TODO(jaewan): add fromBundle()/toBundle()
-    public static class PlaylistParam {
-        /**
-         * @hide
-         */
-        @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
-                REPEAT_MODE_GROUP})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface RepeatMode {}
-
-        /**
-         * Playback will be stopped at the end of the playing media list.
-         */
-        public static final int REPEAT_MODE_NONE = 0;
-
-        /**
-         * Playback of the current playing media item will be repeated.
-         */
-        public static final int REPEAT_MODE_ONE = 1;
-
-        /**
-         * Playing media list will be repeated.
-         */
-        public static final int REPEAT_MODE_ALL = 2;
-
-        /**
-         * 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
-         */
-        @IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface ShuffleMode {}
-
-        /**
-         * Media list will be played in order.
-         */
-        public static final int SHUFFLE_MODE_NONE = 0;
-
-        /**
-         * Media list will be played in shuffled order.
-         */
-        public static final int SHUFFLE_MODE_ALL = 1;
-
-        /**
-         * 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;
-
-        private @RepeatMode int mRepeatMode;
-        private @ShuffleMode int mShuffleMode;
-
-        private MediaMetadata2 mPlaylistMetadata;
-
-        public PlaylistParam(@RepeatMode int repeatMode, @ShuffleMode int shuffleMode,
-                @Nullable MediaMetadata2 playlistMetadata) {
-            mRepeatMode = repeatMode;
-            mShuffleMode = shuffleMode;
-            mPlaylistMetadata = playlistMetadata;
-        }
-
-        public @RepeatMode int getRepeatMode() {
-            return mRepeatMode;
-        }
-
-        public @ShuffleMode int getShuffleMode() {
-            return mShuffleMode;
-        }
-
-        public MediaMetadata2 getPlaylistMetadata() {
-            return mPlaylistMetadata;
-        }
-    }
-
-    /**
      * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
      * <p>
      * This intended behavior and here's the reasons.
@@ -943,66 +873,45 @@
      *       Whenever it happens only one session was properly setup and others were all dummies.
      *       Android framework couldn't find the right session to dispatch media key event.
      *    2. Simplify session's lifecycle.
-     *       {@link MediaSession} can be available after all of {@link MediaSession#setFlags(int)},
-     *       {@link MediaSession#setCallback(Callback)}, and
-     *       {@link MediaSession#setActive(boolean)}. It was common for an app to omit one, so
-     *       framework had to add heuristics to figure out if an app is
+     *       {@link android.media.session.MediaSession} is available after all of
+     *       {@link android.media.session.MediaSession#setFlags(int)},
+     *       {@link android.media.session.MediaSession#setCallback(
+     *              android.media.session.MediaSession.Callback)},
+     *       and {@link android.media.session.MediaSession#setActive(boolean)}.
+     *       It was common for an app to omit one, so framework had to add heuristics to figure out
+     *       which should be the highest priority for handling media key event.
      * @hide
      */
-    MediaSession2(Context context, MediaPlayerBase player, String id, Executor callbackExecutor,
-            SessionCallback callback, VolumeProvider volumeProvider, int ratingType,
-            PendingIntent sessionActivity) {
+    public MediaSession2(MediaSession2Provider provider) {
         super();
-        mProvider = createProvider(context, player, id, callbackExecutor, callback,
-                volumeProvider, ratingType, sessionActivity);
-    }
-
-    MediaSession2Provider createProvider(Context context, MediaPlayerBase player, String id,
-            Executor callbackExecutor, SessionCallback callback, VolumeProvider volumeProvider,
-            int ratingType, PendingIntent sessionActivity) {
-        return ApiLoader.getProvider(context)
-                .createMediaSession2(this, context, player, id, callbackExecutor,
-                        callback, volumeProvider, ratingType, sessionActivity);
+        mProvider = provider;
     }
 
     /**
      * @hide
      */
-    // TODO(jaewan): SystemApi
-    public MediaSession2Provider getProvider() {
+    public @NonNull MediaSession2Provider getProvider() {
         return mProvider;
     }
 
     /**
-     * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event to.
-     * Events from the {@link MediaController2} will be sent directly to the underlying
-     * player on the {@link Handler} where the session is created on.
+     * Sets the underlying {@link MediaPlayerBase} and {@link MediaPlaylistAgent} for this session
+     * to dispatch incoming event to.
      * <p>
-     * If the new player is successfully set, {@link PlaybackListener}
-     * will be called to tell the current playback state of the new player.
+     * When a {@link MediaPlaylistAgent} is specified here, the playlist agent should manage
+     * {@link MediaPlayerBase} for calling {@link MediaPlayerBase#setNextDataSources(List)}.
      * <p>
-     * You can also specify a volume provider. If so, playback in the player is considered as
-     * remote playback.
+     * If the {@link MediaPlaylistAgent} isn't set, session will recreate the default playlist
+     * agent.
      *
-     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
-     * @throws IllegalArgumentException if the player is {@code null}.
+     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app
+     * @param playlistAgent a {@link MediaPlaylistAgent} that manages playlist of the {@code player}
+     * @param volumeProvider a {@link VolumeProvider2}. If {@code null}, system will adjust the
+     *                       appropriate stream volume for this session's player.
      */
-    public void setPlayer(@NonNull MediaPlayerBase player) {
-        mProvider.setPlayer_impl(player);
-    }
-
-    /**
-     * Set the underlying {@link MediaPlayerBase} with the volume provider for remote playback.
-     *
-     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
-     * @param volumeProvider a volume provider
-     * @see #setPlayer(MediaPlayerBase)
-     * @see Builder#setVolumeProvider(VolumeProvider)
-     * @throws IllegalArgumentException if a parameter is {@code null}.
-     */
-    public void setPlayer(@NonNull MediaPlayerBase player, @NonNull VolumeProvider volumeProvider)
-            throws IllegalArgumentException {
-        mProvider.setPlayer_impl(player, volumeProvider);
+    public void updatePlayer(@NonNull MediaPlayerBase player,
+            @Nullable MediaPlaylistAgent playlistAgent, @Nullable VolumeProvider2 volumeProvider) {
+        mProvider.updatePlayer_impl(player, playlistAgent, volumeProvider);
     }
 
     @Override
@@ -1013,11 +922,25 @@
     /**
      * @return player
      */
-    public @Nullable MediaPlayerBase getPlayer() {
+    public @NonNull MediaPlayerBase getPlayer() {
         return mProvider.getPlayer_impl();
     }
 
     /**
+     * @return playlist agent
+     */
+    public @NonNull MediaPlaylistAgent getPlaylistAgent() {
+        return mProvider.getPlaylistAgent_impl();
+    }
+
+    /**
+     * @return volume provider
+     */
+    public @Nullable VolumeProvider2 getVolumeProvider() {
+        return mProvider.getVolumeProvider_impl();
+    }
+
+    /**
      * Returns the {@link SessionToken2} for creating {@link MediaController2}.
      */
     public @NonNull
@@ -1030,31 +953,13 @@
     }
 
     /**
-     * Sets the {@link AudioAttributes} to be used during the playback of the video.
+     * Set the {@link AudioFocusRequest} to obtain the audio focus
      *
-     * @param attributes non-null <code>AudioAttributes</code>.
+     * @param afr the full request parameters
      */
-    public void setAudioAttributes(@NonNull AudioAttributes attributes) {
-        mProvider.setAudioAttributes_impl(attributes);
-    }
-
-    /**
-     * Sets which type of audio focus will be requested during the playback, or configures playback
-     * to not request audio focus. Valid values for focus requests are
-     * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
-     * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
-     * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. Or use
-     * {@link AudioManager#AUDIOFOCUS_NONE} to express that audio focus should not be
-     * requested when playback starts. You can for instance use this when playing a silent animation
-     * through this class, and you don't want to affect other audio applications playing in the
-     * background.
-     *
-     * @param focusGain the type of audio focus gain that will be requested, or
-     *                  {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during
-     *                  playback.
-     */
-    public void setAudioFocusRequest(int focusGain) {
-        mProvider.setAudioFocusRequest_impl(focusGain);
+    public void setAudioFocusRequest(@Nullable AudioFocusRequest afr) {
+        // TODO(jaewan): implement this (b/72529899)
+        // mProvider.setAudioFocusRequest_impl(focusGain);
     }
 
     /**
@@ -1071,10 +976,11 @@
      *      expanded row:   layout[5] layout[6] layout[7] layout[8] layout[9]
      *      main row:       layout[3] layout[1] layout[0] layout[2] layout[4]
      * <p>
-     * This API can be called in the {@link SessionCallback#onConnect(ControllerInfo)}.
+     * This API can be called in the {@link SessionCallback#onConnect(
+     * MediaSession2, ControllerInfo)}.
      *
      * @param controller controller to specify layout.
-     * @param layout oredered list of layout.
+     * @param layout ordered list of layout.
      */
     public void setCustomLayout(@NonNull ControllerInfo controller,
             @NonNull List<CommandButton> layout) {
@@ -1088,25 +994,17 @@
      * @param commands new allowed commands
      */
     public void setAllowedCommands(@NonNull ControllerInfo controller,
-            @NonNull CommandGroup commands) {
+            @NonNull SessionCommandGroup2 commands) {
         mProvider.setAllowedCommands_impl(controller, commands);
     }
 
     /**
-     * Notify changes in metadata of previously set playlist. Controller will get the whole set of
-     * playlist again.
-     */
-    public void notifyMetadataChanged() {
-        mProvider.notifyMetadataChanged_impl();
-    }
-
-    /**
      * Send custom command to all connected controllers.
      *
      * @param command a command
      * @param args optional argument
      */
-    public void sendCustomCommand(@NonNull Command command, @Nullable Bundle args) {
+    public void sendCustomCommand(@NonNull SessionCommand2 command, @Nullable Bundle args) {
         mProvider.sendCustomCommand_impl(command, args);
     }
 
@@ -1117,73 +1015,54 @@
      * @param args optional argument
      * @param receiver result receiver for the session
      */
-    public void sendCustomCommand(@NonNull ControllerInfo controller, @NonNull Command command,
-            @Nullable Bundle args, @Nullable ResultReceiver receiver) {
+    public void sendCustomCommand(@NonNull ControllerInfo controller,
+            @NonNull SessionCommand2 command, @Nullable Bundle args,
+            @Nullable ResultReceiver receiver) {
         // Equivalent to the MediaController.sendCustomCommand(Action action, ResultReceiver r);
         mProvider.sendCustomCommand_impl(controller, command, args, receiver);
     }
 
     /**
      * Play playback
+     * <p>
+     * This calls {@link MediaPlayerBase#play()}.
      */
     public void play() {
         mProvider.play_impl();
     }
 
     /**
-     * Pause playback
+     * Pause playback.
+     * <p>
+     * This calls {@link MediaPlayerBase#pause()}.
      */
     public void pause() {
         mProvider.pause_impl();
     }
 
     /**
-     * Stop playback
+     * Stop playback, and reset the player to the initial state.
+     * <p>
+     * This calls {@link MediaPlayerBase#reset()}.
      */
     public void stop() {
         mProvider.stop_impl();
     }
 
     /**
-     * Rewind playback
-     */
-    public void skipToPrevious() {
-        mProvider.skipToPrevious_impl();
-    }
-
-    /**
-     * Rewind playback
-     */
-    public void skipToNext() {
-        mProvider.skipToNext_impl();
-    }
-
-    /**
      * Request that the player prepare its playback. In other words, other sessions 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 PlaybackState#STATE_PAUSED}. Afterwards, {@link #play} can be called to
-     * start playback.
+     * state to {@link MediaPlayerBase#PLAYER_STATE_PAUSED}. Afterwards, {@link #play} can be called
+     * to start playback.
+     * <p>
+     * This calls {@link MediaPlayerBase#reset()}.
      */
     public void prepare() {
         mProvider.prepare_impl();
     }
 
     /**
-     * Start fast forwarding. If playback is already fast forwarding this may increase the rate.
-     */
-    public void fastForward() {
-        mProvider.fastForward_impl();
-    }
-
-    /**
-     * Start rewinding. If playback is already rewinding this may increase the rate.
-     */
-    public void rewind() {
-        mProvider.rewind_impl();
-    }
-
-    /**
      * Move to a new location in the media stream.
      *
      * @param pos Position to move to, in milliseconds.
@@ -1193,17 +1072,6 @@
     }
 
     /**
-     * Sets the index of current DataSourceDesc in the play list to be played.
-     *
-     * @param index the index of DataSourceDesc in the play list you want to play
-     * @throws IllegalArgumentException if the play list is null
-     * @throws NullPointerException if index is outside play list range
-     */
-    public void setCurrentPlaylistItem(int index) {
-        mProvider.setCurrentPlaylistItem_impl(index);
-    }
-
-    /**
      * @hide
      */
     public void skipForward() {
@@ -1217,7 +1085,310 @@
         // To match with KEYCODE_MEDIA_SKIP_BACKWARD
     }
 
-    public void setPlaylist(@NonNull List<MediaItem2> playlist, @NonNull PlaylistParam param) {
-        mProvider.setPlaylist_impl(playlist, param);
+    /**
+     * Notify errors to the connected controllers
+     *
+     * @param errorCode error code
+     * @param extras extras
+     */
+    public void notifyError(@ErrorCode int errorCode, @Nullable Bundle extras) {
+        mProvider.notifyError_impl(errorCode, extras);
+    }
+
+    /**
+     * Gets the current player state.
+     *
+     * @return the current player state
+     */
+    public @PlayerState int getPlayerState() {
+        return mProvider.getPlayerState_impl();
+    }
+
+    /**
+     * Gets the current position.
+     *
+     * @return the current playback position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME} if
+     *         unknown.
+     */
+    public long getCurrentPosition() {
+        return mProvider.getCurrentPosition_impl();
+    }
+
+    /**
+     * Gets the buffered position, or {@link MediaPlayerBase#UNKNOWN_TIME} if unknown.
+     *
+     * @return the buffered position in ms, or {@link MediaPlayerBase#UNKNOWN_TIME}.
+     */
+    public long getBufferedPosition() {
+        return mProvider.getBufferedPosition_impl();
+    }
+
+    /**
+     * Gets the current buffering state of the player.
+     * During buffering, see {@link #getBufferedPosition()} for the quantifying the amount already
+     * buffered.
+     *
+     * @return the buffering state.
+     */
+    public @BuffState int getBufferingState() {
+        // TODO(jaewan): Implement this
+        return BUFFERING_STATE_UNKNOWN;
+    }
+
+    /**
+     * Get the playback speed.
+     *
+     * @return speed
+     */
+    public float getPlaybackSpeed() {
+        // TODO(jaewan): implement this (b/74093080)
+        return -1;
+    }
+
+    /**
+     * Set the playback speed.
+     */
+    public void setPlaybackSpeed(float speed) {
+        // TODO(jaewan): implement this (b/74093080)
+    }
+
+    /**
+     * Sets the data source missing helper. Helper will be used to provide default implementation of
+     * {@link MediaPlaylistAgent} when it isn't set by developer.
+     * <p>
+     * Default implementation of the {@link MediaPlaylistAgent} will call helper when a
+     * {@link MediaItem2} in the playlist doesn't have a {@link DataSourceDesc}. This may happen
+     * when
+     * <ul>
+     *      <li>{@link MediaItem2} specified by {@link #setPlaylist(List, MediaMetadata2)} doesn't
+     *          have {@link DataSourceDesc}</li>
+     *      <li>{@link MediaController2#addPlaylistItem(int, MediaItem2)} is called and accepted
+     *          by {@link SessionCallback#onCommandRequest(
+     *          MediaSession2, ControllerInfo, SessionCommand2)}.
+     *          In that case, an item would be added automatically without the data source.</li>
+     * </ul>
+     * <p>
+     * If it's not set, playback wouldn't happen for the item without data source descriptor.
+     * <p>
+     * The helper will be run on the executor that was specified by
+     * {@link Builder#setSessionCallback(Executor, SessionCallback)}.
+     *
+     * @param helper a data source missing helper.
+     * @throws IllegalStateException when the helper is set when the playlist agent is set
+     * @see #setPlaylist(List, MediaMetadata2)
+     * @see SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)
+     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_ADD_ITEM
+     * @see SessionCommand2#COMMAND_CODE_PLAYLIST_REPLACE_ITEM
+     */
+    public void setOnDataSourceMissingHelper(@NonNull OnDataSourceMissingHelper helper) {
+        mProvider.setOnDataSourceMissingHelper_impl(helper);
+    }
+
+    /**
+     * Clears the data source missing helper.
+     *
+     * @see #setOnDataSourceMissingHelper(OnDataSourceMissingHelper)
+     */
+    public void clearOnDataSourceMissingHelper() {
+        mProvider.clearOnDataSourceMissingHelper_impl();
+    }
+
+    /**
+     * Returns the playlist from the {@link MediaPlaylistAgent}.
+     * <p>
+     * This list may differ with the list that was specified with
+     * {@link #setPlaylist(List, MediaMetadata2)} depending on the {@link MediaPlaylistAgent}
+     * implementation. Use media items returned here for other playlist agent APIs such as
+     * {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)}.
+     *
+     * @return playlist
+     * @see MediaPlaylistAgent#getPlaylist()
+     * @see SessionCallback#onPlaylistChanged(
+     *          MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
+     */
+    public List<MediaItem2> getPlaylist() {
+        return mProvider.getPlaylist_impl();
+    }
+
+    /**
+     * Sets a list of {@link MediaItem2} to the {@link MediaPlaylistAgent}. Ensure uniqueness of
+     * each {@link MediaItem2} in the playlist so the session can uniquely identity individual
+     * items.
+     * <p>
+     * This may be an asynchronous call, and {@link MediaPlaylistAgent} may keep the copy of the
+     * list. Wait for {@link SessionCallback#onPlaylistChanged(MediaSession2, MediaPlaylistAgent,
+     * List, MediaMetadata2)} to know the operation finishes.
+     * <p>
+     * You may specify a {@link MediaItem2} without {@link DataSourceDesc}. In that case,
+     * {@link MediaPlaylistAgent} has responsibility to dynamically query {@link DataSourceDesc}
+     * when such media item is ready for preparation or play. Default implementation needs
+     * {@link OnDataSourceMissingHelper} for such case.
+     *
+     * @param list A list of {@link MediaItem2} objects to set as a play list.
+     * @throws IllegalArgumentException if given list is {@code null}, or has duplicated media
+     * items.
+     * @see MediaPlaylistAgent#setPlaylist(List, MediaMetadata2)
+     * @see SessionCallback#onPlaylistChanged(
+     *          MediaSession2, MediaPlaylistAgent, List, MediaMetadata2)
+     * @see #setOnDataSourceMissingHelper
+     */
+    public void setPlaylist(@NonNull List<MediaItem2> list, @Nullable MediaMetadata2 metadata) {
+        mProvider.setPlaylist_impl(list, metadata);
+    }
+
+    /**
+     * Skips to the item in the playlist.
+     * <p>
+     * This calls {@link MediaPlaylistAgent#skipToPlaylistItem(MediaItem2)} and the behavior depends
+     * on the playlist agent implementation, especially with the shuffle/repeat mode.
+     *
+     * @param item The item in the playlist you want to play
+     * @see #getShuffleMode()
+     * @see #getRepeatMode()
+     */
+    public void skipToPlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.skipToPlaylistItem_impl(item);
+    }
+
+    /**
+     * Skips to the previous item.
+     * <p>
+     * This calls {@link MediaPlaylistAgent#skipToPreviousItem()} and the behavior depends on the
+     * playlist agent implementation, especially with the shuffle/repeat mode.
+     *
+     * @see #getShuffleMode()
+     * @see #getRepeatMode()
+     **/
+    public void skipToPreviousItem() {
+        mProvider.skipToPreviousItem_impl();
+    }
+
+    /**
+     * Skips to the next item.
+     * <p>
+     * This calls {@link MediaPlaylistAgent#skipToNextItem()} and the behavior depends on the
+     * playlist agent implementation, especially with the shuffle/repeat mode.
+     *
+     * @see #getShuffleMode()
+     * @see #getRepeatMode()
+     */
+    public void skipToNextItem() {
+        mProvider.skipToNextItem_impl();
+    }
+
+    /**
+     * Gets the playlist metadata from the {@link MediaPlaylistAgent}.
+     *
+     * @return the playlist metadata
+     */
+    public MediaMetadata2 getPlaylistMetadata() {
+        return mProvider.getPlaylistMetadata_impl();
+    }
+
+    /**
+     * Adds the media item to the playlist at position index. Index equals or greater than
+     * the current playlist size will add the item at the end of the playlist.
+     * <p>
+     * This will not change the currently playing media item.
+     * If index is less than or equal to the current index of the play list,
+     * the current index of the play list will be incremented correspondingly.
+     *
+     * @param index the index you want to add
+     * @param item the media item you want to add
+     */
+    public void addPlaylistItem(int index, @NonNull MediaItem2 item) {
+        mProvider.addPlaylistItem_impl(index, item);
+    }
+
+    /**
+     * Removes the media item in the playlist.
+     * <p>
+     * If the item is the currently playing item of the playlist, current playback
+     * will be stopped and playback moves to next source in the list.
+     *
+     * @param item the media item you want to add
+     */
+    public void removePlaylistItem(@NonNull MediaItem2 item) {
+        mProvider.removePlaylistItem_impl(item);
+    }
+
+    /**
+     * Replaces the media item at index in the playlist. This can be also used to update metadata of
+     * an item.
+     *
+     * @param index the index of the item to replace
+     * @param item the new item
+     */
+    public void replacePlaylistItem(int index, @NonNull MediaItem2 item) {
+        mProvider.replacePlaylistItem_impl(index, item);
+    }
+
+    /**
+     * Return currently playing media item.
+     *
+     * @return currently playing media item
+     */
+    public MediaItem2 getCurrentMediaItem() {
+        // TODO(jaewan): Rename provider, and implement (b/74316764)
+        return mProvider.getCurrentPlaylistItem_impl();
+    }
+
+    /**
+     * Updates the playlist metadata to the {@link MediaPlaylistAgent}.
+     *
+     * @param metadata metadata of the playlist
+     */
+    public void updatePlaylistMetadata(@Nullable MediaMetadata2 metadata) {
+        mProvider.updatePlaylistMetadata_impl(metadata);
+    }
+
+    /**
+     * Gets the repeat mode from the {@link MediaPlaylistAgent}.
+     *
+     * @return repeat mode
+     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+     */
+    public @RepeatMode int getRepeatMode() {
+        return mProvider.getRepeatMode_impl();
+    }
+
+    /**
+     * Sets the repeat mode to the {@link MediaPlaylistAgent}.
+     *
+     * @param repeatMode repeat mode
+     * @see MediaPlaylistAgent#REPEAT_MODE_NONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ONE
+     * @see MediaPlaylistAgent#REPEAT_MODE_ALL
+     * @see MediaPlaylistAgent#REPEAT_MODE_GROUP
+     */
+    public void setRepeatMode(@RepeatMode int repeatMode) {
+        mProvider.setRepeatMode_impl(repeatMode);
+    }
+
+    /**
+     * Gets the shuffle mode from the {@link MediaPlaylistAgent}.
+     *
+     * @return The shuffle mode
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+     */
+    public @ShuffleMode int getShuffleMode() {
+        return mProvider.getShuffleMode_impl();
+    }
+
+    /**
+     * Sets the shuffle mode to the {@link MediaPlaylistAgent}.
+     *
+     * @param shuffleMode The shuffle mode
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_NONE
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_ALL
+     * @see MediaPlaylistAgent#SHUFFLE_MODE_GROUP
+     */
+    public void setShuffleMode(@ShuffleMode int shuffleMode) {
+        mProvider.setShuffleMode_impl(shuffleMode);
     }
 }
diff --git a/android/media/MediaSession2Test.java b/android/media/MediaSession2Test.java
deleted file mode 100644
index 045dcd5..0000000
--- a/android/media/MediaSession2Test.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.MediaSession2.Builder;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.media.session.PlaybackState;
-import android.os.Process;
-import android.os.Looper;
-import android.support.annotation.NonNull;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import java.util.ArrayList;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static android.media.TestUtils.createPlaybackState;
-import static org.junit.Assert.*;
-
-/**
- * Tests {@link MediaSession2}.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class MediaSession2Test extends MediaSession2TestBase {
-    private static final String TAG = "MediaSession2Test";
-
-    private MediaSession2 mSession;
-    private MockPlayer mPlayer;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        sHandler.postAndSync(() -> {
-            mPlayer = new MockPlayer(0);
-            mSession = new MediaSession2.Builder(mContext, mPlayer).build();
-        });
-    }
-
-    @After
-    @Override
-    public void cleanUp() throws Exception {
-        super.cleanUp();
-        sHandler.postAndSync(() -> {
-            mSession.close();
-        });
-    }
-
-    @Test
-    public void testBuilder() throws Exception {
-        try {
-            MediaSession2.Builder builder = new Builder(mContext, null);
-            fail("null player shouldn't be allowed");
-        } catch (IllegalArgumentException e) {
-            // expected. pass-through
-        }
-        MediaSession2.Builder builder = new Builder(mContext, mPlayer);
-        try {
-            builder.setId(null);
-            fail("null id shouldn't be allowed");
-        } catch (IllegalArgumentException e) {
-            // expected. pass-through
-        }
-    }
-
-    @Test
-    public void testSetPlayer() throws Exception {
-        sHandler.postAndSync(() -> {
-            MockPlayer player = new MockPlayer(0);
-            // Test if setPlayer doesn't crash with various situations.
-            mSession.setPlayer(mPlayer);
-            mSession.setPlayer(player);
-            mSession.close();
-        });
-    }
-
-    @Test
-    public void testPlay() throws Exception {
-        sHandler.postAndSync(() -> {
-            mSession.play();
-            assertTrue(mPlayer.mPlayCalled);
-        });
-    }
-
-    @Test
-    public void testPause() throws Exception {
-        sHandler.postAndSync(() -> {
-            mSession.pause();
-            assertTrue(mPlayer.mPauseCalled);
-        });
-    }
-
-    @Test
-    public void testStop() throws Exception {
-        sHandler.postAndSync(() -> {
-            mSession.stop();
-            assertTrue(mPlayer.mStopCalled);
-        });
-    }
-
-    @Test
-    public void testSkipToNext() throws Exception {
-        sHandler.postAndSync(() -> {
-            mSession.skipToNext();
-            assertTrue(mPlayer.mSkipToNextCalled);
-        });
-    }
-
-    @Test
-    public void testSkipToPrevious() throws Exception {
-        sHandler.postAndSync(() -> {
-            mSession.skipToPrevious();
-            assertTrue(mPlayer.mSkipToPreviousCalled);
-        });
-    }
-
-    @Test
-    public void testPlaybackStateChangedListener() throws InterruptedException {
-        // TODO(jaewan): Add equivalent tests again
-        /*
-        final CountDownLatch latch = new CountDownLatch(2);
-        final MockPlayer player = new MockPlayer(0);
-        final PlaybackListener listener = (state) -> {
-            assertEquals(sHandler.getLooper(), Looper.myLooper());
-            assertNotNull(state);
-            switch ((int) latch.getCount()) {
-                case 2:
-                    assertEquals(PlaybackState.STATE_PLAYING, state.getState());
-                    break;
-                case 1:
-                    assertEquals(PlaybackState.STATE_PAUSED, state.getState());
-                    break;
-                case 0:
-                    fail();
-            }
-            latch.countDown();
-        };
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PLAYING));
-        sHandler.postAndSync(() -> {
-            mSession.addPlaybackListener(listener, sHandler);
-            // When the player is set, listeners will be notified about the player's current state.
-            mSession.setPlayer(player);
-        });
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
-        assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        */
-    }
-
-    @Test
-    public void testBadPlayer() throws InterruptedException {
-        // TODO(jaewan): Add equivalent tests again
-        /*
-        final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
-        final BadPlayer player = new BadPlayer(0);
-        sHandler.postAndSync(() -> {
-            mSession.addPlaybackListener((state) -> {
-                // This will be called for every setPlayer() calls, but no more.
-                assertNull(state);
-                latch.countDown();
-            }, sHandler);
-            mSession.setPlayer(player);
-            mSession.setPlayer(mPlayer);
-        });
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_PAUSED));
-        assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        */
-    }
-
-    private static class BadPlayer extends MockPlayer {
-        public BadPlayer(int count) {
-            super(count);
-        }
-
-        @Override
-        public void removePlaybackListener(@NonNull PlaybackListener listener) {
-            // No-op. This bad player will keep push notification to the listener that is previously
-            // registered by session.setPlayer().
-        }
-    }
-
-    @Test
-    public void testOnCommandCallback() throws InterruptedException {
-        final MockOnCommandCallback callback = new MockOnCommandCallback();
-        sHandler.postAndSync(() -> {
-            mSession.close();
-            mPlayer = new MockPlayer(1);
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
-                    .setSessionCallback(sHandlerExecutor, callback).build();
-        });
-        MediaController2 controller = createController(mSession.getToken());
-        controller.pause();
-        assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertFalse(mPlayer.mPauseCalled);
-        assertEquals(1, callback.commands.size());
-        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE,
-                (long) callback.commands.get(0).getCommandCode());
-        controller.skipToNext();
-        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertTrue(mPlayer.mSkipToNextCalled);
-        assertFalse(mPlayer.mPauseCalled);
-        assertEquals(2, callback.commands.size());
-        assertEquals(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
-                (long) callback.commands.get(1).getCommandCode());
-    }
-
-    @Test
-    public void testOnConnectCallback() throws InterruptedException {
-        final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
-        sHandler.postAndSync(() -> {
-            mSession.close();
-            mSession = new MediaSession2.Builder(mContext, mPlayer)
-                    .setSessionCallback(sHandlerExecutor, sessionCallback).build();
-        });
-        MediaController2 controller =
-                createController(mSession.getToken(), false, null);
-        assertNotNull(controller);
-        waitForConnect(controller, false);
-        waitForDisconnect(controller, true);
-    }
-
-    public class MockOnConnectCallback extends SessionCallback {
-        @Override
-        public MediaSession2.CommandGroup onConnect(ControllerInfo controllerInfo) {
-            if (Process.myUid() != controllerInfo.getUid()) {
-                return null;
-            }
-            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
-            assertEquals(Process.myUid(), controllerInfo.getUid());
-            assertFalse(controllerInfo.isTrusted());
-            // Reject all
-            return null;
-        }
-    }
-
-    public class MockOnCommandCallback extends SessionCallback {
-        public final ArrayList<MediaSession2.Command> commands = new ArrayList<>();
-
-        @Override
-        public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
-            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
-            assertEquals(Process.myUid(), controllerInfo.getUid());
-            assertFalse(controllerInfo.isTrusted());
-            commands.add(command);
-            if (command.getCommandCode() == MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE) {
-                return false;
-            }
-            return true;
-        }
-    }
-}
diff --git a/android/media/MediaSession2TestBase.java b/android/media/MediaSession2TestBase.java
deleted file mode 100644
index 96afcb9..0000000
--- a/android/media/MediaSession2TestBase.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.content.Context;
-import android.media.MediaController2.ControllerCallback;
-import android.media.MediaSession2.CommandGroup;
-import android.os.Bundle;
-import android.os.HandlerThread;
-import android.support.annotation.CallSuper;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.test.InstrumentationRegistry;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-
-/**
- * Base class for session test.
- */
-abstract class MediaSession2TestBase {
-    // Expected success
-    static final int WAIT_TIME_MS = 1000;
-
-    // Expected timeout
-    static final int TIMEOUT_MS = 500;
-
-    static TestUtils.SyncHandler sHandler;
-    static Executor sHandlerExecutor;
-
-    Context mContext;
-    private List<MediaController2> mControllers = new ArrayList<>();
-
-    interface TestControllerInterface {
-        ControllerCallback getCallback();
-    }
-
-    interface TestControllerCallbackInterface {
-        // Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
-
-        // Browser specific callbacks
-        default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
-    }
-
-    interface WaitForConnectionInterface {
-        void waitForConnect(boolean expect) throws InterruptedException;
-        void waitForDisconnect(boolean expect) throws InterruptedException;
-    }
-
-    @BeforeClass
-    public static void setUpThread() {
-        if (sHandler == null) {
-            HandlerThread handlerThread = new HandlerThread("MediaSession2TestBase");
-            handlerThread.start();
-            sHandler = new TestUtils.SyncHandler(handlerThread.getLooper());
-            sHandlerExecutor = (runnable) -> {
-                sHandler.post(runnable);
-            };
-        }
-    }
-
-    @AfterClass
-    public static void cleanUpThread() {
-        if (sHandler != null) {
-            sHandler.getLooper().quitSafely();
-            sHandler = null;
-            sHandlerExecutor = null;
-        }
-    }
-
-    @CallSuper
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-    }
-
-    @CallSuper
-    public void cleanUp() throws Exception {
-        for (int i = 0; i < mControllers.size(); i++) {
-            mControllers.get(i).close();
-        }
-    }
-
-    final MediaController2 createController(SessionToken2 token) throws InterruptedException {
-        return createController(token, true, null);
-    }
-
-    final MediaController2 createController(@NonNull SessionToken2 token,
-            boolean waitForConnect, @Nullable TestControllerCallbackInterface callback)
-            throws InterruptedException {
-        TestControllerInterface instance = onCreateController(token, callback);
-        if (!(instance instanceof MediaController2)) {
-            throw new RuntimeException("Test has a bug. Expected MediaController2 but returned "
-                    + instance);
-        }
-        MediaController2 controller = (MediaController2) instance;
-        mControllers.add(controller);
-        if (waitForConnect) {
-            waitForConnect(controller, true);
-        }
-        return controller;
-    }
-
-    private static WaitForConnectionInterface getWaitForConnectionInterface(
-            MediaController2 controller) {
-        if (!(controller instanceof TestControllerInterface)) {
-            throw new RuntimeException("Test has a bug. Expected controller implemented"
-                    + " TestControllerInterface but got " + controller);
-        }
-        ControllerCallback callback = ((TestControllerInterface) controller).getCallback();
-        if (!(callback instanceof WaitForConnectionInterface)) {
-            throw new RuntimeException("Test has a bug. Expected controller with callback "
-                    + " implemented WaitForConnectionInterface but got " + controller);
-        }
-        return (WaitForConnectionInterface) callback;
-    }
-
-    public static void waitForConnect(MediaController2 controller, boolean expected)
-            throws InterruptedException {
-        getWaitForConnectionInterface(controller).waitForConnect(expected);
-    }
-
-    public static void waitForDisconnect(MediaController2 controller, boolean expected)
-            throws InterruptedException {
-        getWaitForConnectionInterface(controller).waitForDisconnect(expected);
-    }
-
-    TestControllerInterface onCreateController(@NonNull SessionToken2 token,
-            @NonNull TestControllerCallbackInterface callback) {
-        return new TestMediaController(mContext, token, new TestControllerCallback(callback));
-    }
-
-    public static class TestControllerCallback extends MediaController2.ControllerCallback
-            implements WaitForConnectionInterface {
-        public final TestControllerCallbackInterface mCallbackProxy;
-        public final CountDownLatch connectLatch = new CountDownLatch(1);
-        public final CountDownLatch disconnectLatch = new CountDownLatch(1);
-
-        TestControllerCallback(TestControllerCallbackInterface callbackProxy) {
-            mCallbackProxy = callbackProxy;
-        }
-
-        @CallSuper
-        @Override
-        public void onConnected(CommandGroup commands) {
-            super.onConnected(commands);
-            connectLatch.countDown();
-        }
-
-        @CallSuper
-        @Override
-        public void onDisconnected() {
-            super.onDisconnected();
-            disconnectLatch.countDown();
-        }
-
-        @Override
-        public void waitForConnect(boolean expect) throws InterruptedException {
-            if (expect) {
-                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-            } else {
-                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            }
-        }
-
-        @Override
-        public void waitForDisconnect(boolean expect) throws InterruptedException {
-            if (expect) {
-                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-            } else {
-                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            }
-        }
-    }
-
-    public class TestMediaController extends MediaController2 implements TestControllerInterface {
-        private final ControllerCallback mCallback;
-
-        public TestMediaController(@NonNull Context context, @NonNull SessionToken2 token,
-                @NonNull ControllerCallback callback) {
-            super(context, token, callback, sHandlerExecutor);
-            mCallback = callback;
-        }
-
-        @Override
-        public ControllerCallback getCallback() {
-            return mCallback;
-        }
-    }
-}
diff --git a/android/media/MediaSessionManager_MediaSession2.java b/android/media/MediaSessionManager_MediaSession2.java
deleted file mode 100644
index 192cbc2..0000000
--- a/android/media/MediaSessionManager_MediaSession2.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.content.Context;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import static android.media.TestUtils.createPlaybackState;
-import static org.junit.Assert.*;
-
-/**
- * Tests {@link MediaSessionManager} with {@link MediaSession2} specific APIs.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Ignore
-// TODO(jaewan): Reenable test when the media session service detects newly installed sesison
-//               service app.
-public class MediaSessionManager_MediaSession2 extends MediaSession2TestBase {
-    private static final String TAG = "MediaSessionManager_MediaSession2";
-
-    private MediaSessionManager mManager;
-    private MediaSession2 mSession;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
-        // Specify TAG here so {@link MediaSession2.getInstance()} doesn't complaint about
-        // per test thread differs across the {@link MediaSession2} with the same TAG.
-        final MockPlayer player = new MockPlayer(1);
-        sHandler.postAndSync(() -> {
-            mSession = new MediaSession2.Builder(mContext, player).setId(TAG).build();
-        });
-        ensureChangeInSession();
-    }
-
-    @After
-    @Override
-    public void cleanUp() throws Exception {
-        super.cleanUp();
-        sHandler.removeCallbacksAndMessages(null);
-        sHandler.postAndSync(() -> {
-            mSession.close();
-        });
-    }
-
-    // TODO(jaewan): Make this host-side test to see per-user behavior.
-    @Test
-    public void testGetMediaSession2Tokens_hasMediaController() throws InterruptedException {
-        final MockPlayer player = (MockPlayer) mSession.getPlayer();
-        player.notifyPlaybackState(createPlaybackState(PlaybackState.STATE_STOPPED));
-
-        MediaController2 controller = null;
-        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
-        assertNotNull(tokens);
-        for (int i = 0; i < tokens.size(); i++) {
-            SessionToken2 token = tokens.get(i);
-            if (mContext.getPackageName().equals(token.getPackageName())
-                    && TAG.equals(token.getId())) {
-                assertNotNull(token.getSessionBinder());
-                assertNull(controller);
-                controller = createController(token);
-            }
-        }
-        assertNotNull(controller);
-
-        // Test if the found controller is correct one.
-        assertEquals(PlaybackState.STATE_STOPPED, controller.getPlaybackState().getState());
-        controller.play();
-
-        assertTrue(player.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        assertTrue(player.mPlayCalled);
-    }
-
-    /**
-     * Test if server recognizes session even if session refuses the connection from server.
-     *
-     * @throws InterruptedException
-     */
-    @Test
-    public void testGetSessionTokens_sessionRejected() throws InterruptedException {
-        sHandler.postAndSync(() -> {
-            mSession.close();
-            mSession = new MediaSession2.Builder(mContext, new MockPlayer(0)).setId(TAG)
-                    .setSessionCallback(sHandlerExecutor, new SessionCallback() {
-                        @Override
-                        public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
-                            // Reject all connection request.
-                            return null;
-                        }
-                    }).build();
-        });
-        ensureChangeInSession();
-
-        boolean foundSession = false;
-        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
-        assertNotNull(tokens);
-        for (int i = 0; i < tokens.size(); i++) {
-            SessionToken2 token = tokens.get(i);
-            if (mContext.getPackageName().equals(token.getPackageName())
-                    && TAG.equals(token.getId())) {
-                assertFalse(foundSession);
-                foundSession = true;
-            }
-        }
-        assertTrue(foundSession);
-    }
-
-    @Test
-    public void testGetMediaSession2Tokens_playerRemoved() throws InterruptedException {
-        // Release
-        sHandler.postAndSync(() -> {
-            mSession.close();
-        });
-        ensureChangeInSession();
-
-        // When the mSession's player becomes null, it should lose binder connection between server.
-        // So server will forget the session.
-        List<SessionToken2> tokens = mManager.getActiveSessionTokens();
-        for (int i = 0; i < tokens.size(); i++) {
-            SessionToken2 token = tokens.get(i);
-            assertFalse(mContext.getPackageName().equals(token.getPackageName())
-                    && TAG.equals(token.getId()));
-        }
-    }
-
-    @Test
-    public void testGetMediaSessionService2Token() throws InterruptedException {
-        boolean foundTestSessionService = false;
-        boolean foundTestLibraryService = false;
-        List<SessionToken2> tokens = mManager.getSessionServiceTokens();
-        for (int i = 0; i < tokens.size(); i++) {
-            SessionToken2 token = tokens.get(i);
-            if (mContext.getPackageName().equals(token.getPackageName())
-                    && MockMediaSessionService2.ID.equals(token.getId())) {
-                assertFalse(foundTestSessionService);
-                assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
-                assertNull(token.getSessionBinder());
-                foundTestSessionService = true;
-            } else if (mContext.getPackageName().equals(token.getPackageName())
-                    && MockMediaLibraryService2.ID.equals(token.getId())) {
-                assertFalse(foundTestLibraryService);
-                assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
-                assertNull(token.getSessionBinder());
-                foundTestLibraryService = true;
-            }
-        }
-        assertTrue(foundTestSessionService);
-        assertTrue(foundTestLibraryService);
-    }
-
-    @Test
-    public void testGetAllSessionTokens() throws InterruptedException {
-        boolean foundTestSession = false;
-        boolean foundTestSessionService = false;
-        boolean foundTestLibraryService = false;
-        List<SessionToken2> tokens = mManager.getAllSessionTokens();
-        for (int i = 0; i < tokens.size(); i++) {
-            SessionToken2 token = tokens.get(i);
-            if (!mContext.getPackageName().equals(token.getPackageName())) {
-                continue;
-            }
-            switch (token.getId()) {
-                case TAG:
-                    assertFalse(foundTestSession);
-                    foundTestSession = true;
-                    break;
-                case MockMediaSessionService2.ID:
-                    assertFalse(foundTestSessionService);
-                    foundTestSessionService = true;
-                    assertEquals(SessionToken2.TYPE_SESSION_SERVICE, token.getType());
-                    break;
-                case MockMediaLibraryService2.ID:
-                    assertFalse(foundTestLibraryService);
-                    assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, token.getType());
-                    foundTestLibraryService = true;
-                    break;
-                default:
-                    fail("Unexpected session " + token + " exists in the package");
-            }
-        }
-        assertTrue(foundTestSession);
-        assertTrue(foundTestSessionService);
-        assertTrue(foundTestLibraryService);
-    }
-
-    // Ensures if the session creation/release is notified to the server.
-    private void ensureChangeInSession() throws InterruptedException {
-        // TODO(jaewan): Wait by listener.
-        Thread.sleep(WAIT_TIME_MS);
-    }
-}
diff --git a/android/media/MediaSessionService2.java b/android/media/MediaSessionService2.java
index 19814f0..6c3a4bf 100644
--- a/android/media/MediaSessionService2.java
+++ b/android/media/MediaSessionService2.java
@@ -23,12 +23,13 @@
 import android.app.Service;
 import android.content.Intent;
 import android.media.MediaSession2.ControllerInfo;
-import android.media.session.PlaybackState;
 import android.media.update.ApiLoader;
 import android.media.update.MediaSessionService2Provider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.os.IBinder;
 
 /**
+ * @hide
  * Base class for media session services, which is the service version of the {@link MediaSession2}.
  * <p>
  * It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
@@ -84,12 +85,13 @@
  * session service, the controller binds to the session service. {@link #onCreateSession(String)}
  * may be called after the {@link #onCreate} if the service hasn't created yet.
  * <p>
- * After the binding, session's {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}
+ * After the binding, session's {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)}
+ *
  * will be called to accept or reject connection request from a controller. If the connection is
  * rejected, the controller will unbind. If it's accepted, the controller will be available to use
  * and keep binding.
  * <p>
- * When playback is started for this session service, {@link #onUpdateNotification(PlaybackState)}
+ * When playback is started for this session service, {@link #onUpdateNotification()}
  * is called and service would become a foreground service. It's needed to keep playback after the
  * controller is destroyed. The session service becomes background service when the playback is
  * stopped.
@@ -98,21 +100,8 @@
  * <p>
  * Any app can bind to the session service with controller, but the controller can be used only if
  * the session service accepted the connection request through
- * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}.
- *
- * @hide
+ * {@link MediaSession2.SessionCallback#onConnect(MediaSession2, ControllerInfo)}.
  */
-// TODO(jaewan): Unhide
-// TODO(jaewan): Can we clean up sessions in onDestroy() automatically instead?
-//               What about currently running SessionCallback when the onDestroy() is called?
-// TODO(jaewan): Protect this with system|privilleged permission - Q.
-// TODO(jaewan): Add permission check for the service to know incoming connection request.
-//               Follow-up questions: What about asking a XML for list of white/black packages for
-//                                    allowing enumeration?
-//                                    We can read the information even when the service is started,
-//                                    so SessionManager.getXXXXService() can only return apps
-//                                    TODO(jaewan): Will be the black/white listing persistent?
-//                                                  In other words, can we cache the rejection?
 public abstract class MediaSessionService2 extends Service {
     private final MediaSessionService2Provider mProvider;
 
@@ -134,7 +123,7 @@
     }
 
     MediaSessionService2Provider createProvider() {
-        return ApiLoader.getProvider(this).createMediaSessionService2(this);
+        return ApiLoader.getProvider().createMediaSessionService2(this);
     }
 
     /**
@@ -169,27 +158,27 @@
     public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
 
     /**
-     * Called when the playback state of this session is changed, and notification needs update.
-     * Override this method to show your own notification UI.
+     * Called when the playback state of this session is changed so notification needs update.
+     * Override this method to show or cancel your own notification UI.
      * <p>
      * With the notification returned here, the service become foreground service when the playback
      * is started. It becomes background service after the playback is stopped.
      *
-     * @param state playback state
      * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
      */
-    // TODO(jaewan): Also add metadata
-    public MediaNotification onUpdateNotification(PlaybackState2 state) {
-        return mProvider.onUpdateNotification_impl(state);
+    public @Nullable MediaNotification onUpdateNotification() {
+        return mProvider.onUpdateNotification_impl();
     }
 
     /**
      * Get instance of the {@link MediaSession2} that you've previously created with the
      * {@link #onCreateSession} for this service.
+     * <p>
+     * This may be {@code null} before the {@link #onCreate()} is finished.
      *
      * @return created session
      */
-    public final MediaSession2 getSession() {
+    public final @Nullable MediaSession2 getSession() {
         return mProvider.getSession_impl();
     }
 
@@ -213,35 +202,32 @@
     }
 
     /**
-     * Returned by {@link #onUpdateNotification(PlaybackState)} for making session service
-     * foreground service to keep playback running in the background. It's highly recommended to
-     * show media style notification here.
+     * Returned by {@link #onUpdateNotification()} for making session service foreground service
+     * to keep playback running in the background. It's highly recommended to show media style
+     * notification here.
      */
-    // TODO(jaewan): Should we also move this to updatable?
     public static class MediaNotification {
-        public final int id;
-        public final Notification notification;
-
-        private MediaNotification(int id, @NonNull Notification notification) {
-            this.id = id;
-            this.notification = notification;
-        }
+        private final MediaNotificationProvider mProvider;
 
         /**
-         * Create a {@link MediaNotification}.
+         * Default constructor
          *
          * @param notificationId notification id to be used for
          *      {@link android.app.NotificationManager#notify(int, Notification)}.
          * @param notification a notification to make session service foreground service. Media
          *      style notification is recommended here.
-         * @return
          */
-        public static MediaNotification create(int notificationId,
-                @NonNull Notification notification) {
-            if (notification == null) {
-                throw new IllegalArgumentException("Notification cannot be null");
-            }
-            return new MediaNotification(notificationId, notification);
+        public MediaNotification(int notificationId, @NonNull Notification notification) {
+            mProvider = ApiLoader.getProvider().createMediaSessionService2MediaNotification(
+                    this, notificationId, notification);
+        }
+
+        public int getNotificationId() {
+            return mProvider.getNotificationId_impl();
+        }
+
+        public @NonNull Notification getNotification() {
+            return mProvider.getNotification_impl();
         }
     }
 }
diff --git a/android/media/MediaTimestamp.java b/android/media/MediaTimestamp.java
index 5ea6bbe..938dd14 100644
--- a/android/media/MediaTimestamp.java
+++ b/android/media/MediaTimestamp.java
@@ -37,6 +37,11 @@
 public final class MediaTimestamp
 {
     /**
+     * An unknown media timestamp value
+     */
+    public static final MediaTimestamp TIMESTAMP_UNKNOWN = new MediaTimestamp(-1, -1, 0.0f);
+
+    /**
      * Get the media time of the anchor in microseconds.
      */
     public long getAnchorMediaTimeUs() {
@@ -82,4 +87,15 @@
         nanoTime = 0;
         clockRate = 1.0f;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+
+        final MediaTimestamp that = (MediaTimestamp) obj;
+        return (this.mediaTimeUs == that.mediaTimeUs)
+                && (this.nanoTime == that.nanoTime)
+                && (this.clockRate == that.clockRate);
+    }
 }
diff --git a/android/media/MicrophoneInfo.java b/android/media/MicrophoneInfo.java
new file mode 100644
index 0000000..d6399a4
--- /dev/null
+++ b/android/media/MicrophoneInfo.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.util.Pair;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Class providing information on a microphone. It indicates the location and orientation of the
+ * microphone on the device as well as useful information like frequency response and sensitivity.
+ * It can be used by applications implementing special pre processing effects like noise suppression
+ * of beam forming that need to know about precise microphone characteristics in order to adapt
+ * their algorithms.
+ */
+public final class MicrophoneInfo {
+
+    /**
+     * A microphone that the location is unknown.
+     */
+    public static final int LOCATION_UNKNOWN = 0;
+
+    /**
+     * A microphone that locate on main body of the device.
+     */
+    public static final int LOCATION_MAINBODY = 1;
+
+    /**
+     * A microphone that locate on a movable main body of the device.
+     */
+    public static final int LOCATION_MAINBODY_MOVABLE = 2;
+
+    /**
+     * A microphone that locate on a peripheral.
+     */
+    public static final int LOCATION_PERIPHERAL = 3;
+
+    /**
+     * Unknown microphone directionality.
+     */
+    public static final int DIRECTIONALITY_UNKNOWN = 0;
+
+    /**
+     * Microphone directionality type: omni.
+     */
+    public static final int DIRECTIONALITY_OMNI = 1;
+
+    /**
+     * Microphone directionality type: bi-directional.
+     */
+    public static final int DIRECTIONALITY_BI_DIRECTIONAL = 2;
+
+    /**
+     * Microphone directionality type: cardioid.
+     */
+    public static final int DIRECTIONALITY_CARDIOID = 3;
+
+    /**
+     * Microphone directionality type: hyper cardioid.
+     */
+    public static final int DIRECTIONALITY_HYPER_CARDIOID = 4;
+
+    /**
+     * Microphone directionality type: super cardioid.
+     */
+    public static final int DIRECTIONALITY_SUPER_CARDIOID = 5;
+
+    /**
+     * The channel contains raw audio from this microphone.
+     */
+    public static final int CHANNEL_MAPPING_DIRECT = 1;
+
+    /**
+     * The channel contains processed audio from this microphone and possibly another microphone.
+     */
+    public static final int CHANNEL_MAPPING_PROCESSED = 2;
+
+    /**
+     * Value used for when the group of the microphone is unknown.
+     */
+    public static final int GROUP_UNKNOWN = -1;
+
+    /**
+     * Value used for when the index in the group of the microphone is unknown.
+     */
+    public static final int INDEX_IN_THE_GROUP_UNKNOWN = -1;
+
+    /**
+     * Value used for when the position of the microphone is unknown.
+     */
+    public static final Coordinate3F POSITION_UNKNOWN = new Coordinate3F(
+            -Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
+
+    /**
+     * Value used for when the orientation of the microphone is unknown.
+     */
+    public static final Coordinate3F ORIENTATION_UNKNOWN = new Coordinate3F(0.0f, 0.0f, 0.0f);
+
+    /**
+     * Value used for when the sensitivity of the microphone is unknown.
+     */
+    public static final float SENSITIVITY_UNKNOWN = -Float.MAX_VALUE;
+
+    /**
+     * Value used for when the SPL of the microphone is unknown. This value could be used when
+     * maximum SPL or minimum SPL is unknown.
+     */
+    public static final float SPL_UNKNOWN = -Float.MAX_VALUE;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "LOCATION_" }, value = {
+            LOCATION_UNKNOWN,
+            LOCATION_MAINBODY,
+            LOCATION_MAINBODY_MOVABLE,
+            LOCATION_PERIPHERAL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MicrophoneLocation {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "DIRECTIONALITY_" }, value = {
+            DIRECTIONALITY_UNKNOWN,
+            DIRECTIONALITY_OMNI,
+            DIRECTIONALITY_BI_DIRECTIONAL,
+            DIRECTIONALITY_CARDIOID,
+            DIRECTIONALITY_HYPER_CARDIOID,
+            DIRECTIONALITY_SUPER_CARDIOID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MicrophoneDirectionality {}
+
+    private Coordinate3F mPosition;
+    private Coordinate3F mOrientation;
+    private String mDeviceId;
+    private String mAddress;
+    private List<Pair<Float, Float>> mFrequencyResponse;
+    private List<Pair<Integer, Integer>> mChannelMapping;
+    private float mMaxSpl;
+    private float mMinSpl;
+    private float mSensitivity;
+    private int mLocation;
+    private int mGroup; /* Usually 0 will be used for main body. */
+    private int mIndexInTheGroup;
+    private int mPortId; /* mPortId will correspond to the id in AudioPort */
+    private int mType;
+    private int mDirectionality;
+
+    MicrophoneInfo(String deviceId, int type, String address, int location,
+            int group, int indexInTheGroup, Coordinate3F position,
+            Coordinate3F orientation, List<Pair<Float, Float>> frequencyResponse,
+            List<Pair<Integer, Integer>> channelMapping, float sensitivity, float maxSpl,
+            float minSpl, int directionality) {
+        mDeviceId = deviceId;
+        mType = type;
+        mAddress = address;
+        mLocation = location;
+        mGroup = group;
+        mIndexInTheGroup = indexInTheGroup;
+        mPosition = position;
+        mOrientation = orientation;
+        mFrequencyResponse = frequencyResponse;
+        mChannelMapping = channelMapping;
+        mSensitivity = sensitivity;
+        mMaxSpl = maxSpl;
+        mMinSpl = minSpl;
+        mDirectionality = directionality;
+    }
+
+    /**
+     * Returns alphanumeric code that uniquely identifies the device.
+     *
+     * @return the description of the microphone
+     */
+    public String getDescription() {
+        return mDeviceId;
+    }
+
+    /**
+     * Returns The system unique device ID that corresponds to the id
+     * returned by {@link AudioDeviceInfo#getId()}.
+     *
+     * @return the microphone's id
+     */
+    public int getId() {
+        return mPortId;
+    }
+
+    /**
+     * @hide
+     * Returns the internal device type (e.g AudioSystem.DEVICE_IN_BUILTIN_MIC).
+     * The internal device type could be used when getting microphone's port id
+     * by matching type and address.
+     *
+     * @return the internal device type
+     */
+    public int getInternalDeviceType() {
+        return mType;
+    }
+
+    /**
+     * Returns the device type identifier of the microphone (e.g AudioDeviceInfo.TYPE_BUILTIN_MIC).
+     *
+     * @return the device type of the microphone
+     */
+    public int getType() {
+        return AudioDeviceInfo.convertInternalDeviceToDeviceType(mType);
+    }
+
+    /**
+     * Returns The "address" string of the microphone that corresponds to the
+     * address returned by {@link AudioDeviceInfo#getAddress()}
+     * @return the address of the microphone
+     */
+    public @NonNull String getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * Returns the location of the microphone. The return value is
+     * one of {@link #LOCATION_UNKNOWN}, {@link #LOCATION_MAINBODY},
+     * {@link #LOCATION_MAINBODY_MOVABLE}, or {@link #LOCATION_PERIPHERAL}.
+     *
+     * @return the location of the microphone
+     */
+    public @MicrophoneLocation int getLocation() {
+        return mLocation;
+    }
+
+    /**
+     * Returns A device group id that can be used to group together microphones on the same
+     * peripheral, attachments or logical groups. Main body is usually group 0.
+     *
+     * @return the group of the microphone or {@link #GROUP_UNKNOWN} if the group is unknown
+     */
+    public int getGroup() {
+        return mGroup;
+    }
+
+    /**
+     * Returns unique index for device within its group.
+     *
+     * @return the microphone's index in its group or {@link #INDEX_IN_THE_GROUP_UNKNOWN} if the
+     * index in the group is unknown
+     */
+    public int getIndexInTheGroup() {
+        return mIndexInTheGroup;
+    }
+
+    /**
+     * Returns A {@link Coordinate3F} object that represents the geometric location of microphone
+     * in meters, from bottom-left-back corner of appliance. X-axis, Y-axis and Z-axis show
+     * as the x, y, z values.
+     *
+     * @return the geometric location of the microphone or {@link #POSITION_UNKNOWN} if the
+     * geometric location is unknown
+     */
+    public Coordinate3F getPosition() {
+        return mPosition;
+    }
+
+    /**
+     * Returns A {@link Coordinate3F} object that represents the orientation of microphone.
+     * X-axis, Y-axis and Z-axis show as the x, y, z value. The orientation will be normalized
+     * such as sqrt(x^2 + y^2 + z^2) equals 1.
+     *
+     * @return the orientation of the microphone or {@link #ORIENTATION_UNKNOWN} if orientation
+     * is unknown
+     */
+    public Coordinate3F getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * Returns a {@link android.util.Pair} list of frequency responses.
+     * For every {@link android.util.Pair} in the list, the first value represents frequency in Hz,
+     * and the second value represents response in dB.
+     *
+     * @return the frequency response of the microphone
+     */
+    public List<Pair<Float, Float>> getFrequencyResponse() {
+        return mFrequencyResponse;
+    }
+
+    /**
+     * Returns a {@link android.util.Pair} list for channel mapping, which indicating how this
+     * microphone is used by each channels or a capture stream. For each {@link android.util.Pair},
+     * the first value is channel index, the second value is channel mapping type, which could be
+     * either {@link #CHANNEL_MAPPING_DIRECT} or {@link #CHANNEL_MAPPING_PROCESSED}.
+     * If a channel has contributions from more than one microphone, it is likely the HAL
+     * did some extra processing to combine the sources, but this is to be inferred by the user.
+     * Empty list when the MicrophoneInfo is returned by AudioManager.getMicrophones().
+     * At least one entry when the MicrophoneInfo is returned by AudioRecord.getActiveMicrophones().
+     *
+     * @return a {@link android.util.Pair} list for channel mapping
+     */
+    public List<Pair<Integer, Integer>> getChannelMapping() {
+        return mChannelMapping;
+    }
+
+    /**
+     * Returns the level in dBFS produced by a 1000Hz tone at 94 dB SPL.
+     *
+     * @return the sensitivity of the microphone or {@link #SENSITIVITY_UNKNOWN} if the sensitivity
+     * is unknown
+     */
+    public float getSensitivity() {
+        return mSensitivity;
+    }
+
+    /**
+     * Returns the level in dB of the maximum SPL supported by the device at 1000Hz.
+     *
+     * @return the maximum level in dB or {@link #SPL_UNKNOWN} if maximum SPL is unknown
+     */
+    public float getMaxSpl() {
+        return mMaxSpl;
+    }
+
+    /**
+     * Returns the level in dB of the minimum SPL that can be registered by the device at 1000Hz.
+     *
+     * @return the minimum level in dB or {@link #SPL_UNKNOWN} if minimum SPL is unknown
+     */
+    public float getMinSpl() {
+        return mMinSpl;
+    }
+
+    /**
+     * Returns the directionality of microphone. The return value is one of
+     * {@link #DIRECTIONALITY_UNKNOWN}, {@link #DIRECTIONALITY_OMNI},
+     * {@link #DIRECTIONALITY_BI_DIRECTIONAL}, {@link #DIRECTIONALITY_CARDIOID},
+     * {@link #DIRECTIONALITY_HYPER_CARDIOID}, or {@link #DIRECTIONALITY_SUPER_CARDIOID}.
+     *
+     * @return the directionality of microphone
+     */
+    public @MicrophoneDirectionality int getDirectionality() {
+        return mDirectionality;
+    }
+
+    /**
+     * Set the port id for the device.
+     * @hide
+     */
+    public void setId(int portId) {
+        mPortId = portId;
+    }
+
+    /**
+     * Set the channel mapping for the device.
+     * @hide
+     */
+    public void setChannelMapping(List<Pair<Integer, Integer>> channelMapping) {
+        mChannelMapping = channelMapping;
+    }
+
+    /* A class containing three float value to represent a 3D coordinate */
+    public static final class Coordinate3F {
+        public final float x;
+        public final float y;
+        public final float z;
+
+        Coordinate3F(float x, float y, float z) {
+            this.x = x;
+            this.y = y;
+            this.z = z;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Coordinate3F)) {
+                return false;
+            }
+            Coordinate3F other = (Coordinate3F) obj;
+            return this.x == other.x && this.y == other.y && this.z == other.z;
+        }
+    }
+}
diff --git a/android/media/MiniThumbFile.java b/android/media/MiniThumbFile.java
index 664308c..9899367 100644
--- a/android/media/MiniThumbFile.java
+++ b/android/media/MiniThumbFile.java
@@ -44,13 +44,14 @@
  */
 public class MiniThumbFile {
     private static final String TAG = "MiniThumbFile";
-    private static final int MINI_THUMB_DATA_FILE_VERSION = 3;
+    private static final int MINI_THUMB_DATA_FILE_VERSION = 4;
     public static final int BYTES_PER_MINTHUMB = 10000;
     private static final int HEADER_SIZE = 1 + 8 + 4;
     private Uri mUri;
     private RandomAccessFile mMiniThumbFile;
     private FileChannel mChannel;
     private ByteBuffer mBuffer;
+    private ByteBuffer mEmptyBuffer;
     private static final Hashtable<String, MiniThumbFile> sThumbFiles =
         new Hashtable<String, MiniThumbFile>();
 
@@ -127,9 +128,10 @@
         return mMiniThumbFile;
     }
 
-    public MiniThumbFile(Uri uri) {
+    private MiniThumbFile(Uri uri) {
         mUri = uri;
         mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
+        mEmptyBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);
     }
 
     public synchronized void deactivate() {
@@ -184,6 +186,54 @@
         return 0;
     }
 
+    public synchronized void eraseMiniThumb(long id) {
+        RandomAccessFile r = miniThumbDataFile();
+        if (r != null) {
+            long pos = id * BYTES_PER_MINTHUMB;
+            FileLock lock = null;
+            try {
+                mBuffer.clear();
+                mBuffer.limit(1 + 8);
+
+                lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);
+                // check that we can read the following 9 bytes
+                // (1 for the "status" and 8 for the long)
+                if (mChannel.read(mBuffer, pos) == 9) {
+                    mBuffer.position(0);
+                    if (mBuffer.get() == 1) {
+                        long currentMagic = mBuffer.getLong();
+                        if (currentMagic == 0) {
+                            // there is no thumbnail stored here
+                            Log.i(TAG, "no thumbnail for id " + id);
+                            return;
+                        }
+                        // zero out the thumbnail slot
+                        // Log.v(TAG, "clearing slot " + id + ", magic " + currentMagic
+                        //         + " at offset " + pos);
+                        mChannel.write(mEmptyBuffer, pos);
+                    }
+                } else {
+                    // Log.v(TAG, "No slot");
+                }
+            } catch (IOException ex) {
+                Log.v(TAG, "Got exception checking file magic: ", ex);
+            } catch (RuntimeException ex) {
+                // Other NIO related exception like disk full, read only channel..etc
+                Log.e(TAG, "Got exception when reading magic, id = " + id +
+                        ", disk full or mount read-only? " + ex.getClass());
+            } finally {
+                try {
+                    if (lock != null) lock.release();
+                }
+                catch (IOException ex) {
+                    // ignore it.
+                }
+            }
+        } else {
+            // Log.v(TAG, "No data file");
+        }
+    }
+
     public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic)
             throws IOException {
         RandomAccessFile r = miniThumbDataFile();
diff --git a/android/media/MockMediaLibraryService2.java b/android/media/MockMediaLibraryService2.java
deleted file mode 100644
index 14cf257..0000000
--- a/android/media/MockMediaLibraryService2.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
-* Copyright 2018 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package android.media;
-
-import static junit.framework.Assert.fail;
-
-import android.content.Context;
-import android.media.MediaSession2.CommandGroup;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.TestUtils.SyncHandler;
-import android.os.Bundle;
-import android.os.Process;
-
-import javax.annotation.concurrent.GuardedBy;
-
-/**
- * Mock implementation of {@link MediaLibraryService2} for testing.
- */
-public class MockMediaLibraryService2 extends MediaLibraryService2 {
-    // Keep in sync with the AndroidManifest.xml
-    public static final String ID = "TestLibrary";
-
-    public static final String ROOT_ID = "rootId";
-    public static final Bundle EXTRA = new Bundle();
-    static {
-        EXTRA.putString(ROOT_ID, ROOT_ID);
-    }
-    @GuardedBy("MockMediaLibraryService2.class")
-    private static SessionToken2 sToken;
-
-    private MediaLibrarySession mSession;
-
-    @Override
-    public MediaLibrarySession onCreateSession(String sessionId) {
-        final MockPlayer player = new MockPlayer(1);
-        final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
-        try {
-            handler.postAndSync(() -> {
-                TestLibrarySessionCallback callback = new TestLibrarySessionCallback();
-                mSession = new MediaLibrarySessionBuilder(MockMediaLibraryService2.this,
-                        player, (runnable) -> handler.post(runnable), callback)
-                        .setId(sessionId).build();
-            });
-        } catch (InterruptedException e) {
-            fail(e.toString());
-        }
-        return mSession;
-    }
-
-    @Override
-    public void onDestroy() {
-        TestServiceRegistry.getInstance().cleanUp();
-        super.onDestroy();
-    }
-
-    public static SessionToken2 getToken(Context context) {
-        synchronized (MockMediaLibraryService2.class) {
-            if (sToken == null) {
-                sToken = new SessionToken2(SessionToken2.TYPE_LIBRARY_SERVICE,
-                        context.getPackageName(), ID,
-                        MockMediaLibraryService2.class.getName(), null);
-            }
-            return sToken;
-        }
-    }
-
-    private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
-        @Override
-        public CommandGroup onConnect(ControllerInfo controller) {
-            if (Process.myUid() != controller.getUid()) {
-                // It's system app wants to listen changes. Ignore.
-                return super.onConnect(controller);
-            }
-            TestServiceRegistry.getInstance().setServiceInstance(
-                    MockMediaLibraryService2.this, controller);
-            return super.onConnect(controller);
-        }
-
-        @Override
-        public BrowserRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
-            return new BrowserRoot(ROOT_ID, EXTRA);
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/media/MockMediaSessionService2.java b/android/media/MockMediaSessionService2.java
deleted file mode 100644
index b058117..0000000
--- a/android/media/MockMediaSessionService2.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static junit.framework.Assert.fail;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
-import android.media.TestUtils.SyncHandler;
-import android.media.session.PlaybackState;
-import android.os.Process;
-
-/**
- * Mock implementation of {@link android.media.MediaSessionService2} for testing.
- */
-public class MockMediaSessionService2 extends MediaSessionService2 {
-    // Keep in sync with the AndroidManifest.xml
-    public static final String ID = "TestSession";
-
-    private static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
-    private static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
-
-    private NotificationChannel mDefaultNotificationChannel;
-    private MediaSession2 mSession;
-    private NotificationManager mNotificationManager;
-
-    @Override
-    public MediaSession2 onCreateSession(String sessionId) {
-        final MockPlayer player = new MockPlayer(1);
-        final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
-        try {
-            handler.postAndSync(() -> {
-                mSession = new MediaSession2.Builder(MockMediaSessionService2.this, player)
-                        .setId(sessionId).setSessionCallback((runnable)->handler.post(runnable),
-                                new MySessionCallback()).build();
-            });
-        } catch (InterruptedException e) {
-            fail(e.toString());
-        }
-        return mSession;
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-    }
-
-    @Override
-    public void onDestroy() {
-        TestServiceRegistry.getInstance().cleanUp();
-        super.onDestroy();
-    }
-
-    @Override
-    public MediaNotification onUpdateNotification(PlaybackState2 state) {
-        if (mDefaultNotificationChannel == null) {
-            mDefaultNotificationChannel = new NotificationChannel(
-                    DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
-                    DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID,
-                    NotificationManager.IMPORTANCE_DEFAULT);
-            mNotificationManager.createNotificationChannel(mDefaultNotificationChannel);
-        }
-        Notification notification = new Notification.Builder(
-                this, DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID)
-                .setContentTitle(getPackageName())
-                .setContentText("Playback state: " + state.getState())
-                .setSmallIcon(android.R.drawable.sym_def_app_icon).build();
-        return MediaNotification.create(DEFAULT_MEDIA_NOTIFICATION_ID, notification);
-    }
-
-    private class MySessionCallback extends SessionCallback {
-        @Override
-        public MediaSession2.CommandGroup onConnect(ControllerInfo controller) {
-            if (Process.myUid() != controller.getUid()) {
-                // It's system app wants to listen changes. Ignore.
-                return super.onConnect(controller);
-            }
-            TestServiceRegistry.getInstance().setServiceInstance(
-                    MockMediaSessionService2.this, controller);
-            return super.onConnect(controller);
-        }
-    }
-}
diff --git a/android/media/MockPlayer.java b/android/media/MockPlayer.java
deleted file mode 100644
index fd69309..0000000
--- a/android/media/MockPlayer.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.media.MediaSession2.PlaylistParam;
-import android.media.session.PlaybackState;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-
-/**
- * A mock implementation of {@link MediaPlayerBase} for testing.
- */
-public class MockPlayer extends MediaPlayerBase {
-    public final CountDownLatch mCountDownLatch;
-
-    public boolean mPlayCalled;
-    public boolean mPauseCalled;
-    public boolean mStopCalled;
-    public boolean mSkipToPreviousCalled;
-    public boolean mSkipToNextCalled;
-    public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
-    private PlaybackState2 mLastPlaybackState;
-
-    public MockPlayer(int count) {
-        mCountDownLatch = (count > 0) ? new CountDownLatch(count) : null;
-    }
-
-    @Override
-    public void play() {
-        mPlayCalled = true;
-        if (mCountDownLatch != null) {
-            mCountDownLatch.countDown();
-        }
-    }
-
-    @Override
-    public void pause() {
-        mPauseCalled = true;
-        if (mCountDownLatch != null) {
-            mCountDownLatch.countDown();
-        }
-    }
-
-    @Override
-    public void stop() {
-        mStopCalled = true;
-        if (mCountDownLatch != null) {
-            mCountDownLatch.countDown();
-        }
-    }
-
-    @Override
-    public void skipToPrevious() {
-        mSkipToPreviousCalled = true;
-        if (mCountDownLatch != null) {
-            mCountDownLatch.countDown();
-        }
-    }
-
-    @Override
-    public void skipToNext() {
-        mSkipToNextCalled = true;
-        if (mCountDownLatch != null) {
-            mCountDownLatch.countDown();
-        }
-    }
-
-
-
-    @Nullable
-    @Override
-    public PlaybackState2 getPlaybackState() {
-        return mLastPlaybackState;
-    }
-
-    @Override
-    public void addPlaybackListener(@NonNull Executor executor,
-            @NonNull PlaybackListener listener) {
-        mListeners.add(new PlaybackListenerHolder(executor, listener));
-    }
-
-    @Override
-    public void removePlaybackListener(@NonNull PlaybackListener listener) {
-        int index = PlaybackListenerHolder.indexOf(mListeners, listener);
-        if (index >= 0) {
-            mListeners.remove(index);
-        }
-    }
-
-    public void notifyPlaybackState(final PlaybackState2 state) {
-        mLastPlaybackState = state;
-        for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).postPlaybackChange(state);
-        }
-    }
-
-    // No-op. Should be added for test later.
-    @Override
-    public void prepare() {
-    }
-
-    @Override
-    public void seekTo(long pos) {
-    }
-
-    @Override
-    public void fastFoward() {
-    }
-
-    @Override
-    public void rewind() {
-    }
-
-    @Override
-    public AudioAttributes getAudioAttributes() {
-        return null;
-    }
-
-    @Override
-    public void setPlaylist(List<MediaItem2> item, PlaylistParam param) {
-    }
-
-    @Override
-    public void setCurrentPlaylistItem(int index) {
-    }
-}
diff --git a/android/media/PlaybackListenerHolder.java b/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 4e19d4d..0000000
--- a/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.session.PlaybackState;
-import android.os.Handler;
-import android.os.Message;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
-    public final Executor executor;
-    public final PlaybackListener listener;
-
-    public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
-        this.executor = executor;
-        this.listener = listener;
-    }
-
-    public void postPlaybackChange(final PlaybackState2 state) {
-        executor.execute(() -> listener.onPlaybackChanged(state));
-    }
-
-    /**
-     * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
-     * the given listener.
-     *
-     * @param list list to check
-     * @param listener listener to check
-     * @return {@code true} if the given list contains listener. {@code false} otherwise.
-     */
-    public static <Holder extends PlaybackListenerHolder> boolean contains(
-            @NonNull List<Holder> list, PlaybackListener listener) {
-        return indexOf(list, listener) >= 0;
-    }
-
-    /**
-     * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
-     *
-     * @param list list to check
-     * @param listener listener to check
-     * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
-     */
-    public static <Holder extends PlaybackListenerHolder> int indexOf(
-            @NonNull List<Holder> list, PlaybackListener listener) {
-        for (int i = 0; i < list.size(); i++) {
-            if (list.get(i).listener == listener) {
-                return i;
-            }
-        }
-        return -1;
-    }
-}
diff --git a/android/media/PlaybackState2.java b/android/media/PlaybackState2.java
deleted file mode 100644
index 46d6f45..0000000
--- a/android/media/PlaybackState2.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.IntDef;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Playback state for a {@link MediaPlayerBase}, to be shared between {@link MediaSession2} and
- * {@link MediaController2}. This includes a playback state {@link #STATE_PLAYING},
- * the current playback position and extra.
- * @hide
- */
-// TODO(jaewan): Move to updatable
-public final class PlaybackState2 {
-    private static final String TAG = "PlaybackState2";
-
-    private static final String KEY_STATE = "android.media.playbackstate2.state";
-
-    // TODO(jaewan): Replace states from MediaPlayer2
-    /**
-     * @hide
-     */
-    @IntDef({STATE_NONE, STATE_STOPPED, STATE_PREPARED, STATE_PAUSED, STATE_PLAYING,
-            STATE_FINISH, STATE_BUFFERING, STATE_ERROR})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface State {}
-
-    /**
-     * This is the default playback state and indicates that no media has been
-     * added yet, or the performer has been reset and has no content to play.
-     */
-    public final static int STATE_NONE = 0;
-
-    /**
-     * State indicating this item is currently stopped.
-     */
-    public final static int STATE_STOPPED = 1;
-
-    /**
-     * State indicating this item is currently prepared
-     */
-    public final static int STATE_PREPARED = 2;
-
-    /**
-     * State indicating this item is currently paused.
-     */
-    public final static int STATE_PAUSED = 3;
-
-    /**
-     * State indicating this item is currently playing.
-     */
-    public final static int STATE_PLAYING = 4;
-
-    /**
-     * State indicating the playback reaches the end of the item.
-     */
-    public final static int STATE_FINISH = 5;
-
-    /**
-     * State indicating this item is currently buffering and will begin playing
-     * when enough data has buffered.
-     */
-    public final static int STATE_BUFFERING = 6;
-
-    /**
-     * State indicating this item is currently in an error state. The error
-     * message should also be set when entering this state.
-     */
-    public final static int STATE_ERROR = 7;
-
-    /**
-     * Use this value for the position to indicate the position is not known.
-     */
-    public final static long PLAYBACK_POSITION_UNKNOWN = -1;
-
-    private final int mState;
-    private final long mPosition;
-    private final long mBufferedPosition;
-    private final float mSpeed;
-    private final CharSequence mErrorMessage;
-    private final long mUpdateTime;
-    private final long mActiveItemId;
-
-    public PlaybackState2(int state, long position, long updateTime, float speed,
-            long bufferedPosition, long activeItemId, CharSequence error) {
-        mState = state;
-        mPosition = position;
-        mSpeed = speed;
-        mUpdateTime = updateTime;
-        mBufferedPosition = bufferedPosition;
-        mActiveItemId = activeItemId;
-        mErrorMessage = error;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder bob = new StringBuilder("PlaybackState {");
-        bob.append("state=").append(mState);
-        bob.append(", position=").append(mPosition);
-        bob.append(", buffered position=").append(mBufferedPosition);
-        bob.append(", speed=").append(mSpeed);
-        bob.append(", updated=").append(mUpdateTime);
-        bob.append(", active item id=").append(mActiveItemId);
-        bob.append(", error=").append(mErrorMessage);
-        bob.append("}");
-        return bob.toString();
-    }
-
-    /**
-     * Get the current state of playback. One of the following:
-     * <ul>
-     * <li> {@link PlaybackState2#STATE_NONE}</li>
-     * <li> {@link PlaybackState2#STATE_STOPPED}</li>
-     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
-     * <li> {@link PlaybackState2#STATE_PAUSED}</li>
-     * <li> {@link PlaybackState2#STATE_BUFFERING}</li>
-     * <li> {@link PlaybackState2#STATE_ERROR}</li>
-     * </ul>
-     */
-    @State
-    public int getState() {
-        return mState;
-    }
-
-    /**
-     * Get the current playback position in ms.
-     */
-    public long getPosition() {
-        return mPosition;
-    }
-
-    /**
-     * Get the current buffered position in ms. This is the farthest playback
-     * point that can be reached from the current position using only buffered
-     * content.
-     */
-    public long getBufferedPosition() {
-        return mBufferedPosition;
-    }
-
-    /**
-     * Get the current playback speed as a multiple of normal playback. This
-     * should be negative when rewinding. A value of 1 means normal playback and
-     * 0 means paused.
-     *
-     * @return The current speed of playback.
-     */
-    public float getPlaybackSpeed() {
-        return mSpeed;
-    }
-
-    /**
-     * Get a user readable error message. This should be set when the state is
-     * {@link PlaybackState2#STATE_ERROR}.
-     */
-    public CharSequence getErrorMessage() {
-        return mErrorMessage;
-    }
-
-    /**
-     * Get the elapsed real time at which position was last updated. If the
-     * position has never been set this will return 0;
-     *
-     * @return The last time the position was updated.
-     */
-    public long getLastPositionUpdateTime() {
-        return mUpdateTime;
-    }
-
-    /**
-     * Get the id of the currently active item in the playlist.
-     *
-     * @return The id of the currently active item in the queue
-     */
-    public long getCurrentPlaylistItemIndex() {
-        return mActiveItemId;
-    }
-
-    /**
-     * @return Bundle object for this to share between processes.
-     */
-    public Bundle toBundle() {
-        // TODO(jaewan): Include other variables.
-        Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STATE, mState);
-        return bundle;
-    }
-
-    /**
-     * @param bundle input
-     * @return
-     */
-    public static PlaybackState2 fromBundle(Bundle bundle) {
-        // TODO(jaewan): Include other variables.
-        final int state = bundle.getInt(KEY_STATE);
-        return new PlaybackState2(state, 0, 0, 0, 0, 0, null);
-    }
-}
\ No newline at end of file
diff --git a/android/media/PlayerBase.java b/android/media/PlayerBase.java
index 09449a1..80049ba 100644
--- a/android/media/PlayerBase.java
+++ b/android/media/PlayerBase.java
@@ -47,10 +47,10 @@
 public abstract class PlayerBase {
 
     private static final String TAG = "PlayerBase";
-    private static final boolean DEBUG = false;
-    private static IAudioService sService; //lazy initialization, use getService()
     /** Debug app ops */
     private static final boolean DEBUG_APP_OPS = false;
+    private static final boolean DEBUG = DEBUG_APP_OPS || false;
+    private static IAudioService sService; //lazy initialization, use getService()
 
     // parameters of the player that affect AppOps
     protected AudioAttributes mAttributes;
@@ -102,6 +102,7 @@
             mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
                     ActivityThread.currentPackageName(), mAppOpsCallback);
         } catch (RemoteException e) {
+            Log.e(TAG, "Error registering appOps callback", e);
             mHasAppOpsPlayAudio = false;
         }
         try {
diff --git a/android/media/Rating2.java b/android/media/Rating2.java
index 67e5e72..9213190 100644
--- a/android/media/Rating2.java
+++ b/android/media/Rating2.java
@@ -16,7 +16,11 @@
 
 package android.media;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.IntDef;
+import android.media.update.ApiLoader;
+import android.media.update.Rating2Provider;
 import android.os.Bundle;
 import android.util.Log;
 
@@ -24,21 +28,17 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * A class to encapsulate rating information used as content metadata.
  * A rating is defined by its rating style (see {@link #RATING_HEART},
  * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS},
  * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may
  * be defined as "unrated"), both of which are defined when the rating instance is constructed
  * through one of the factory methods.
- * @hide
  */
-// TODO(jaewan): Move this to updatable
+// New version of Rating with following change
+//   - Don't implement Parcelable for updatable support.
 public final class Rating2 {
-    private static final String TAG = "Rating2";
-
-    private static final String KEY_STYLE = "android.media.rating2.style";
-    private static final String KEY_VALUE = "android.media.rating2.value";
-
     /**
      * @hide
      */
@@ -92,31 +92,45 @@
      */
     public final static int RATING_PERCENTAGE = 6;
 
-    private final static float RATING_NOT_RATED = -1.0f;
+    private final Rating2Provider mProvider;
 
-    private final int mRatingStyle;
-
-    private final float mRatingValue;
-
-    private Rating2(@Style int ratingStyle, float rating) {
-        mRatingStyle = ratingStyle;
-        mRatingValue = rating;
+    /**
+     * @hide
+     */
+    public Rating2(@NonNull Rating2Provider provider) {
+        mProvider = provider;
     }
 
     @Override
     public String toString() {
-        return "Rating2:style=" + mRatingStyle + " rating="
-                + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
+        return mProvider.toString_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public Rating2Provider getProvider() {
+        return mProvider;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return mProvider.equals_impl(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return mProvider.hashCode_impl();
     }
 
     /**
      * Create an instance from bundle object, previoulsy created by {@link #toBundle()}
      *
      * @param bundle bundle
-     * @return new Rating2 instance
+     * @return new Rating2 instance or {@code null} for error
      */
-    public static Rating2 fromBundle(Bundle bundle) {
-        return new Rating2(bundle.getInt(KEY_STYLE), bundle.getFloat(KEY_VALUE));
+    public static Rating2 fromBundle(@Nullable Bundle bundle) {
+        return ApiLoader.getProvider().fromBundle_Rating2(bundle);
     }
 
     /**
@@ -124,10 +138,7 @@
      * @return bundle of this object
      */
     public Bundle toBundle() {
-        Bundle bundle = new Bundle();
-        bundle.putInt(KEY_STYLE, mRatingStyle);
-        bundle.putFloat(KEY_VALUE, mRatingValue);
-        return bundle;
+        return mProvider.toBundle_impl();
     }
 
     /**
@@ -139,18 +150,8 @@
      *    or {@link #RATING_PERCENTAGE}.
      * @return null if an invalid rating style is passed, a new Rating2 instance otherwise.
      */
-    public static Rating2 newUnratedRating(@Style int ratingStyle) {
-        switch(ratingStyle) {
-            case RATING_HEART:
-            case RATING_THUMB_UP_DOWN:
-            case RATING_3_STARS:
-            case RATING_4_STARS:
-            case RATING_5_STARS:
-            case RATING_PERCENTAGE:
-                return new Rating2(ratingStyle, RATING_NOT_RATED);
-            default:
-                return null;
-        }
+    public static @Nullable Rating2 newUnratedRating(@Style int ratingStyle) {
+        return ApiLoader.getProvider().newUnratedRating_Rating2(ratingStyle);
     }
 
     /**
@@ -160,8 +161,8 @@
      * @param hasHeart true for a "heart selected" rating, false for "heart unselected".
      * @return a new Rating2 instance.
      */
-    public static Rating2 newHeartRating(boolean hasHeart) {
-        return new Rating2(RATING_HEART, hasHeart ? 1.0f : 0.0f);
+    public static @Nullable Rating2 newHeartRating(boolean hasHeart) {
+        return ApiLoader.getProvider().newHeartRating_Rating2(hasHeart);
     }
 
     /**
@@ -171,8 +172,8 @@
      * @param thumbIsUp true for a "thumb up" rating, false for "thumb down".
      * @return a new Rating2 instance.
      */
-    public static Rating2 newThumbRating(boolean thumbIsUp) {
-        return new Rating2(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f);
+    public static @Nullable Rating2 newThumbRating(boolean thumbIsUp) {
+        return ApiLoader.getProvider().newThumbRating_Rating2(thumbIsUp);
     }
 
     /**
@@ -187,27 +188,9 @@
      * @return null if the rating style is invalid, or the rating is out of range,
      *     a new Rating2 instance otherwise.
      */
-    public static Rating2 newStarRating(@StarStyle int starRatingStyle, float starRating) {
-        float maxRating = -1.0f;
-        switch(starRatingStyle) {
-            case RATING_3_STARS:
-                maxRating = 3.0f;
-                break;
-            case RATING_4_STARS:
-                maxRating = 4.0f;
-                break;
-            case RATING_5_STARS:
-                maxRating = 5.0f;
-                break;
-            default:
-                Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating");
-                        return null;
-        }
-        if ((starRating < 0.0f) || (starRating > maxRating)) {
-            Log.e(TAG, "Trying to set out of range star-based rating");
-            return null;
-        }
-        return new Rating2(starRatingStyle, starRating);
+    public static @Nullable Rating2 newStarRating(
+            @StarStyle int starRatingStyle, float starRating) {
+        return ApiLoader.getProvider().newStarRating_Rating2(starRatingStyle, starRating);
     }
 
     /**
@@ -217,13 +200,8 @@
      * @param percent the value of the rating
      * @return null if the rating is out of range, a new Rating2 instance otherwise.
      */
-    public static Rating2 newPercentageRating(float percent) {
-        if ((percent < 0.0f) || (percent > 100.0f)) {
-            Log.e(TAG, "Invalid percentage-based rating value");
-            return null;
-        } else {
-            return new Rating2(RATING_PERCENTAGE, percent);
-        }
+    public static @Nullable Rating2 newPercentageRating(float percent) {
+        return ApiLoader.getProvider().newPercentageRating_Rating2(percent);
     }
 
     /**
@@ -231,7 +209,7 @@
      * @return true if the instance was not created with {@link #newUnratedRating(int)}.
      */
     public boolean isRated() {
-        return mRatingValue >= 0.0f;
+        return mProvider.isRated_impl();
     }
 
     /**
@@ -240,9 +218,8 @@
      *    {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS},
      *    or {@link #RATING_PERCENTAGE}.
      */
-    @Style
-    public int getRatingStyle() {
-        return mRatingStyle;
+    public @Style int getRatingStyle() {
+        return mProvider.getRatingStyle_impl();
     }
 
     /**
@@ -251,11 +228,7 @@
      *    if the rating style is not {@link #RATING_HEART} or if it is unrated.
      */
     public boolean hasHeart() {
-        if (mRatingStyle != RATING_HEART) {
-            return false;
-        } else {
-            return (mRatingValue == 1.0f);
-        }
+        return mProvider.hasHeart_impl();
     }
 
     /**
@@ -264,11 +237,7 @@
      *    if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated.
      */
     public boolean isThumbUp() {
-        if (mRatingStyle != RATING_THUMB_UP_DOWN) {
-            return false;
-        } else {
-            return (mRatingValue == 1.0f);
-        }
+        return mProvider.isThumbUp_impl();
     }
 
     /**
@@ -277,16 +246,7 @@
      *    not star-based, or if it is unrated.
      */
     public float getStarRating() {
-        switch (mRatingStyle) {
-            case RATING_3_STARS:
-            case RATING_4_STARS:
-            case RATING_5_STARS:
-                if (isRated()) {
-                    return mRatingValue;
-                }
-            default:
-                return -1.0f;
-        }
+        return mProvider.getStarRating_impl();
     }
 
     /**
@@ -295,10 +255,6 @@
      *    not percentage-based, or if it is unrated.
      */
     public float getPercentRating() {
-        if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) {
-            return -1.0f;
-        } else {
-            return mRatingValue;
-        }
+        return mProvider.getPercentRating_impl();
     }
 }
diff --git a/android/media/Ringtone.java b/android/media/Ringtone.java
index 209ec42..c0468dc 100644
--- a/android/media/Ringtone.java
+++ b/android/media/Ringtone.java
@@ -40,7 +40,7 @@
  * <p>
  * For ways of retrieving {@link Ringtone} objects or to show a ringtone
  * picker, see {@link RingtoneManager}.
- * 
+ *
  * @see RingtoneManager
  */
 public class Ringtone {
@@ -97,7 +97,7 @@
 
     /**
      * Sets the stream type where this ringtone will be played.
-     * 
+     *
      * @param streamType The stream, see {@link AudioManager}.
      * @deprecated use {@link #setAudioAttributes(AudioAttributes)}
      */
@@ -111,7 +111,7 @@
 
     /**
      * Gets the stream type where this ringtone will be played.
-     * 
+     *
      * @return The stream type, see {@link AudioManager}.
      * @deprecated use of stream types is deprecated, see
      *     {@link #setAudioAttributes(AudioAttributes)}
@@ -146,9 +146,8 @@
     }
 
     /**
-     * @hide
      * Sets the player to be looping or non-looping.
-     * @param looping whether to loop or not
+     * @param looping whether to loop or not.
      */
     public void setLooping(boolean looping) {
         synchronized (mPlaybackSettingsLock) {
@@ -158,7 +157,16 @@
     }
 
     /**
-     * @hide
+     * Returns whether the looping mode was enabled on this player.
+     * @return true if this player loops when playing.
+     */
+    public boolean isLooping() {
+        synchronized (mPlaybackSettingsLock) {
+            return mIsLooping;
+        }
+    }
+
+    /**
      * Sets the volume on this player.
      * @param volume a raw scalar in range 0.0 to 1.0, where 0.0 mutes this player, and 1.0
      *   corresponds to no attenuation being applied.
@@ -173,6 +181,16 @@
     }
 
     /**
+     * Returns the volume scalar set on this player.
+     * @return a value between 0.0f and 1.0f.
+     */
+    public float getVolume() {
+        synchronized (mPlaybackSettingsLock) {
+            return mVolume;
+        }
+    }
+
+    /**
      * Must be called synchronized on mPlaybackSettingsLock
      */
     private void applyPlaybackProperties_sync() {
@@ -194,8 +212,8 @@
     /**
      * Returns a human-presentable title for ringtone. Looks in media
      * content provider. If not in either, uses the filename
-     * 
-     * @param context A context used for querying. 
+     *
+     * @param context A context used for querying.
      */
     public String getTitle(Context context) {
         if (mTitle != null) return mTitle;
@@ -265,12 +283,11 @@
 
         if (title == null) {
             title = context.getString(com.android.internal.R.string.ringtone_unknown);
-            
             if (title == null) {
                 title = "";
             }
         }
-        
+
         return title;
     }
 
@@ -395,7 +412,7 @@
 
     /**
      * Whether this ringtone is currently playing.
-     * 
+     *
      * @return True if playing, false otherwise.
      */
     public boolean isPlaying() {
diff --git a/android/media/RingtoneManager.java b/android/media/RingtoneManager.java
index 3eb9d52..fefa1ed 100644
--- a/android/media/RingtoneManager.java
+++ b/android/media/RingtoneManager.java
@@ -28,11 +28,13 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -47,22 +49,17 @@
 
 import com.android.internal.database.SortCursor;
 
-import libcore.io.Streams;
-
 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.io.OutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 
-import static android.content.ContentProvider.maybeAddUserId;
-import static android.content.pm.PackageManager.NameNotFoundException;
-
 /**
  * RingtoneManager provides access to ringtones, notification, and other types
  * of sounds. It manages querying the different media providers and combines the
@@ -855,7 +852,7 @@
             final Uri cacheUri = getCacheForType(type, context.getUserId());
             try (InputStream in = openRingtone(context, ringtoneUri);
                     OutputStream out = resolver.openOutputStream(cacheUri)) {
-                Streams.copy(in, out);
+                FileUtils.copy(in, out);
             } catch (IOException e) {
                 Log.w(TAG, "Failed to cache ringtone: " + e);
             }
@@ -960,7 +957,7 @@
         // Copy contents to external ringtone storage. Throws IOException if the copy fails.
         try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri);
                 final OutputStream output = new FileOutputStream(outFile)) {
-            Streams.copy(input, output);
+            FileUtils.copy(input, output);
         }
 
         // Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
diff --git a/android/media/SessionCommand2.java b/android/media/SessionCommand2.java
new file mode 100644
index 0000000..fe86a3a
--- /dev/null
+++ b/android/media/SessionCommand2.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.SessionCallback;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * @hide
+ * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+ * <p>
+ * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+ * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+ * {@link #getCustomCommand()} shouldn't be {@code null}.
+ */
+public final class SessionCommand2 {
+    /**
+     * Command code for the custom command which can be defined by string action in the
+     * {@link SessionCommand2}.
+     */
+    public static final int COMMAND_CODE_CUSTOM = 0;
+
+    /**
+     * Command code for {@link MediaController2#play()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PLAY = 1;
+
+    /**
+     * Command code for {@link MediaController2#pause()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+
+    /**
+     * Command code for {@link MediaController2#stop()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+
+    /**
+     * Command code for {@link MediaController2#skipToNextItem()}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_NEXT_ITEM = 4;
+
+    /**
+     * Command code for {@link MediaController2#skipToPreviousItem()}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the {@link SessionCallback#onCommandRequest(
+     * MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Command code for {@link MediaController2#prepare()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_PREPARE = 6;
+
+    /**
+     * Command code for {@link MediaController2#fastForward()}.
+     */
+    public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 7;
+
+    /**
+     * Command code for {@link MediaController2#rewind()}.
+     */
+    public static final int COMMAND_CODE_SESSION_REWIND = 8;
+
+    /**
+     * Command code for {@link MediaController2#seekTo(long)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo,
+     * SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYBACK_SEEK_TO = 9;
+
+    /**
+     * Command code for both {@link MediaController2#setVolumeTo(int, int)}.
+     * <p>
+     * Command would set the device volume or send to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_SET_VOLUME = 10;
+
+    /**
+     * Command code for both {@link MediaController2#adjustVolume(int, int)}.
+     * <p>
+     * Command would adjust the device volume or send to the volume provider directly if the session
+     * doesn't reject the request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_ADJUST_VOLUME = 11;
+
+    /**
+     * Command code for {@link MediaController2#skipToPlaylistItem(MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM = 12;
+
+    /**
+     * Command code for {@link MediaController2#setShuffleMode(int)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_SHUFFLE_MODE = 13;
+
+    /**
+     * Command code for {@link MediaController2#setRepeatMode(int)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_REPEAT_MODE = 14;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_ADD_ITEM = 15;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_REMOVE_ITEM = 16;
+
+    /**
+     * Command code for {@link MediaController2#replacePlaylistItem(int, MediaItem2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_REPLACE_ITEM = 17;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
+     * information to the controller.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET_LIST = 18;
+
+    /**
+     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_LIST = 19;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose
+     * metadata information to the controller.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_GET_LIST_METADATA = 20;
+
+    /**
+     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata2)}.
+     * <p>
+     * Command would be sent directly to the playlist agent if the session doesn't reject the
+     * request through the
+     * {@link SessionCallback#onCommandRequest(MediaSession2, ControllerInfo, SessionCommand2)}.
+     */
+    public static final int COMMAND_CODE_PLAYLIST_SET_LIST_METADATA = 21;
+
+    /**
+     * Command code for {@link MediaController2#playFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_MEDIA_ID = 22;
+
+    /**
+     * Command code for {@link MediaController2#playFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_URI = 23;
+
+    /**
+     * Command code for {@link MediaController2#playFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PLAY_FROM_SEARCH = 24;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromMediaId(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_MEDIA_ID = 25;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromUri(Uri, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_URI = 26;
+
+    /**
+     * Command code for {@link MediaController2#prepareFromSearch(String, Bundle)}.
+     */
+    public static final int COMMAND_CODE_SESSION_PREPARE_FROM_SEARCH = 27;
+
+    /**
+     * Command code for {@link MediaController2#setRating(String, Rating2)}.
+     */
+    public static final int COMMAND_CODE_SESSION_SET_RATING = 28;
+
+    // TODO(jaewan): Add javadoc
+    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 29;
+    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 30;
+    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 31;
+    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 32;
+    public static final int COMMAND_CODE_LIBRARY_SEARCH = 33;
+    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 34;
+    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 35;
+
+    // TODO(jaewan): Rename and move provider
+    private final MediaSession2Provider.CommandProvider mProvider;
+
+    public SessionCommand2(int commandCode) {
+        mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                this, commandCode, null, null);
+    }
+
+    public SessionCommand2(@NonNull String action, @Nullable Bundle extras) {
+        if (action == null) {
+            throw new IllegalArgumentException("action shouldn't be null");
+        }
+        mProvider = ApiLoader.getProvider().createMediaSession2Command(
+                this, COMMAND_CODE_CUSTOM, action, extras);
+    }
+
+    /**
+     * @hide
+     */
+    public MediaSession2Provider.CommandProvider getProvider() {
+        return mProvider;
+    }
+
+    public int getCommandCode() {
+        return mProvider.getCommandCode_impl();
+    }
+
+    public @Nullable String getCustomCommand() {
+        return mProvider.getCustomCommand_impl();
+    }
+
+    public @Nullable Bundle getExtras() {
+        return mProvider.getExtras_impl();
+    }
+
+    /**
+     * @return a new Bundle instance from the Command
+     * @hide
+     */
+    public Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SessionCommand2)) {
+            return false;
+        }
+        return mProvider.equals_impl(((SessionCommand2) obj).mProvider);
+    }
+
+    @Override
+    public int hashCode() {
+        return mProvider.hashCode_impl();
+    }
+
+    /**
+     * @return a new Command instance from the Bundle
+     * @hide
+     */
+    public static SessionCommand2 fromBundle(@NonNull Bundle command) {
+        return ApiLoader.getProvider().fromBundle_MediaSession2Command(command);
+    }
+}
diff --git a/android/media/SessionCommandGroup2.java b/android/media/SessionCommandGroup2.java
new file mode 100644
index 0000000..399765e
--- /dev/null
+++ b/android/media/SessionCommandGroup2.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @hide
+ * Represent set of {@link SessionCommand2}.
+ */
+public final class SessionCommandGroup2 {
+    // TODO(jaewan): Rename and move provider
+    private final MediaSession2Provider.CommandGroupProvider mProvider;
+
+    public SessionCommandGroup2() {
+        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, null);
+    }
+
+    public SessionCommandGroup2(@Nullable SessionCommandGroup2 others) {
+        mProvider = ApiLoader.getProvider().createMediaSession2CommandGroup(this, others);
+    }
+
+    /**
+     * @hide
+     */
+    public SessionCommandGroup2(@NonNull MediaSession2Provider.CommandGroupProvider provider) {
+        mProvider = provider;
+    }
+
+    public void addCommand(@NonNull SessionCommand2 command) {
+        mProvider.addCommand_impl(command);
+    }
+
+    public void addCommand(int commandCode) {
+        // TODO(jaewna): Implement
+    }
+
+    public void addAllPredefinedCommands() {
+        mProvider.addAllPredefinedCommands_impl();
+    }
+
+    public void removeCommand(@NonNull SessionCommand2 command) {
+        mProvider.removeCommand_impl(command);
+    }
+
+    public void removeCommand(int commandCode) {
+        // TODO(jaewan): Implement.
+    }
+
+    public boolean hasCommand(@NonNull SessionCommand2 command) {
+        return mProvider.hasCommand_impl(command);
+    }
+
+    public boolean hasCommand(int code) {
+        return mProvider.hasCommand_impl(code);
+    }
+
+    public @NonNull
+    Set<SessionCommand2> getCommands() {
+        return mProvider.getCommands_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public @NonNull MediaSession2Provider.CommandGroupProvider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return new bundle from the CommandGroup
+     * @hide
+     */
+    public @NonNull Bundle toBundle() {
+        return mProvider.toBundle_impl();
+    }
+
+    /**
+     * @return new instance of CommandGroup from the bundle
+     * @hide
+     */
+    public static @Nullable SessionCommandGroup2 fromBundle(Bundle commands) {
+        return ApiLoader.getProvider().fromBundle_MediaSession2CommandGroup(commands);
+    }
+}
diff --git a/android/media/SessionToken2.java b/android/media/SessionToken2.java
index 697a5a8..bf2d445 100644
--- a/android/media/SessionToken2.java
+++ b/android/media/SessionToken2.java
@@ -18,16 +18,17 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
+import android.content.Context;
 import android.media.session.MediaSessionManager;
+import android.media.update.ApiLoader;
+import android.media.update.SessionToken2Provider;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * @hide
  * Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
  * If it's representing a session service, it may not be ongoing.
  * <p>
@@ -35,11 +36,11 @@
  * {@link MediaController2} to communicate with the session.
  * <p>
  * It can be also obtained by {@link MediaSessionManager}.
- * @hide
  */
-// TODO(jaewan): Unhide. SessionToken2?
-// TODO(jaewan): Move Token to updatable!
-// TODO(jaewan): Find better name for this (SessionToken or Session2Token)
+// New version of MediaSession.Token for following reasons
+//   - Stop implementing Parcelable for updatable support
+//   - Represent session and library service (formerly browser service) in one class.
+//     Previously MediaSession.Token was for session and ComponentName was for service.
 public final class SessionToken2 {
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
@@ -50,91 +51,89 @@
     public static final int TYPE_SESSION_SERVICE = 1;
     public static final int TYPE_LIBRARY_SERVICE = 2;
 
-    private static final String KEY_TYPE = "android.media.token.type";
-    private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
-    private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
-    private static final String KEY_ID = "android.media.token.id";
-    private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
+    private final SessionToken2Provider mProvider;
 
-    private final @TokenType int mType;
-    private final String mPackageName;
-    private final String mServiceName;
-    private final String mId;
-    private final IMediaSession2 mSessionBinder;
+    // From the return value of android.os.Process.getUidForName(String) when error
+    private static final int UID_UNKNOWN = -1;
+
+    /**
+     * Constructor for the token. You can only create token for session service or library service
+     * to use by {@link MediaController2} or {@link MediaBrowser2}.
+     *
+     * @param context context
+     * @param packageName package name
+     * @param serviceName name of service. Can be {@code null} if it's not an service.
+     */
+    public SessionToken2(@NonNull Context context, @NonNull String packageName,
+            @NonNull String serviceName) {
+        this(context, packageName, serviceName, UID_UNKNOWN);
+    }
+
+    /**
+     * Constructor for the token. You can only create token for session service or library service
+     * to use by {@link MediaController2} or {@link MediaBrowser2}.
+     *
+     * @param context context
+     * @param packageName package name
+     * @param serviceName name of service. Can be {@code null} if it's not an service.
+     * @param uid uid of the app.
+     * @hide
+     */
+    public SessionToken2(@NonNull Context context, @NonNull String packageName,
+            @NonNull String serviceName, int uid) {
+        mProvider = ApiLoader.getProvider().createSessionToken2(
+                context, this, packageName, serviceName, uid);
+    }
 
     /**
      * Constructor for the token.
-     *
-     * @hide
-     * @param type type
-     * @param packageName package name
-     * @param id id
-     * @param serviceName name of service. Can be {@code null} if it's not an service.
-     * @param sessionBinder binder for this session. Can be {@code null} if it's service.
      * @hide
      */
-    // TODO(jaewan): UID is also needed.
-    // TODO(jaewan): Unhide
-    public SessionToken2(@TokenType int type, @NonNull String packageName, @NonNull String id,
-            @Nullable String serviceName, @Nullable IMediaSession2 sessionBinder) {
-        // TODO(jaewan): Add sanity check.
-        mType = type;
-        mPackageName = packageName;
-        mId = id;
-        mServiceName = serviceName;
-        mSessionBinder = sessionBinder;
+    public SessionToken2(@NonNull SessionToken2Provider provider) {
+        mProvider = provider;
     }
 
+    @Override
     public int hashCode() {
-        final int prime = 31;
-        return mType
-                + prime * (mPackageName.hashCode()
-                + prime * (mId.hashCode()
-                + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
-                + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0))));
+        return mProvider.hashCode_impl();
     }
 
     @Override
     public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        SessionToken2 other = (SessionToken2) obj;
-        if (!mPackageName.equals(other.getPackageName())
-                || !mServiceName.equals(other.getServiceName())
-                || !mId.equals(other.getId())
-                || mType != other.getType()) {
-            return false;
-        }
-        if (mSessionBinder == other.getSessionBinder()) {
-            return true;
-        } else if (mSessionBinder == null || other.getSessionBinder() == null) {
-            return false;
-        }
-        return mSessionBinder.asBinder().equals(other.getSessionBinder().asBinder());
+        return mProvider.equals_impl(obj);
     }
 
     @Override
     public String toString() {
-        return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
-                + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
+        return mProvider.toString_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public SessionToken2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return uid of the session
+     */
+    public int getUid() {
+        return mProvider.getUid_impl();
     }
 
     /**
      * @return package name
      */
     public String getPackageName() {
-        return mPackageName;
+        return mProvider.getPackageName_impl();
     }
 
     /**
      * @return id
      */
     public String getId() {
-        return mId;
+        return mProvider.getId_imp();
     }
 
     /**
@@ -143,83 +142,23 @@
      * @see #TYPE_SESSION_SERVICE
      */
     public @TokenType int getType() {
-        return mType;
-    }
-
-    /**
-     * @return session binder.
-     * @hide
-     */
-    public @Nullable IMediaSession2 getSessionBinder() {
-        return mSessionBinder;
-    }
-
-    /**
-     * @return service name if it's session service.
-     * @hide
-     */
-    public @Nullable String getServiceName() {
-        return mServiceName;
+        return mProvider.getType_impl();
     }
 
     /**
      * Create a token from the bundle, exported by {@link #toBundle()}.
-     *
      * @param bundle
      * @return
      */
     public static SessionToken2 fromBundle(@NonNull Bundle bundle) {
-        if (bundle == null) {
-            return null;
-        }
-        final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
-        final String packageName = bundle.getString(KEY_PACKAGE_NAME);
-        final String serviceName = bundle.getString(KEY_SERVICE_NAME);
-        final String id = bundle.getString(KEY_ID);
-        final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
-
-        // Sanity check.
-        switch (type) {
-            case TYPE_SESSION:
-                if (!(sessionBinder instanceof IMediaSession2)) {
-                    throw new IllegalArgumentException("Session needs sessionBinder");
-                }
-                break;
-            case TYPE_SESSION_SERVICE:
-                if (TextUtils.isEmpty(serviceName)) {
-                    throw new IllegalArgumentException("Session service needs service name");
-                }
-                if (sessionBinder != null && !(sessionBinder instanceof IMediaSession2)) {
-                    throw new IllegalArgumentException("Invalid session binder");
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid type");
-        }
-        if (TextUtils.isEmpty(packageName) || id == null) {
-            throw new IllegalArgumentException("Package name nor ID cannot be null.");
-        }
-        // TODO(jaewan): Revisit here when we add connection callback to the session for individual
-        //               controller's permission check. With it, sessionBinder should be available
-        //               if and only if for session, not session service.
-        return new SessionToken2(type, packageName, id, serviceName,
-                sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null);
+        return ApiLoader.getProvider().fromBundle_SessionToken2(bundle);
     }
 
     /**
      * Create a {@link Bundle} from this token to share it across processes.
-     *
      * @return Bundle
-     * @hide
      */
     public Bundle toBundle() {
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_PACKAGE_NAME, mPackageName);
-        bundle.putString(KEY_SERVICE_NAME, mServiceName);
-        bundle.putString(KEY_ID, mId);
-        bundle.putInt(KEY_TYPE, mType);
-        bundle.putBinder(KEY_SESSION_BINDER,
-                mSessionBinder != null ? mSessionBinder.asBinder() : null);
-        return bundle;
+        return mProvider.toBundle_impl();
     }
 }
diff --git a/android/media/SubtitleData.java b/android/media/SubtitleData.java
index 3e6f6f9..9797828 100644
--- a/android/media/SubtitleData.java
+++ b/android/media/SubtitleData.java
@@ -16,26 +16,50 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 
 /**
- * @hide
- *
- * Class to hold the subtitle track's data, including:
+ * Class encapsulating subtitle data, as received through the
+ * {@link MediaPlayer.OnSubtitleDataListener} interface.
+ * The subtitle data includes:
  * <ul>
- * <li> Track index</li>
- * <li> Start time (in microseconds) of the data</li>
- * <li> Duration (in microseconds) of the data</li>
- * <li> A byte-array of the data</li>
+ * <li> the track index</li>
+ * <li> the start time (in microseconds) of the data</li>
+ * <li> the duration (in microseconds) of the data</li>
+ * <li> the actual data.</li>
  * </ul>
- *
- * <p> To receive the subtitle data, applications need to do the following:
- *
- * <ul>
- * <li> Select a track of type MEDIA_TRACK_TYPE_SUBTITLE with {@link MediaPlayer.selectTrack(int)</li>
- * <li> Implement the {@link MediaPlayer.OnSubtitleDataListener} interface</li>
- * <li> Register the {@link MediaPlayer.OnSubtitleDataListener} callback on a MediaPlayer object</li>
- * </ul>
+ * The data is stored in a byte-array, and is encoded in one of the supported in-band
+ * subtitle formats. The subtitle encoding is determined by the MIME type of the
+ * {@link MediaPlayer.TrackInfo} of the subtitle track, one of
+ * {@link MediaFormat#MIMETYPE_TEXT_CEA_608}, {@link MediaFormat#MIMETYPE_TEXT_CEA_708},
+ * {@link MediaFormat#MIMETYPE_TEXT_VTT}.
+ * <p>
+ * Here is an example of iterating over the tracks of a {@link MediaPlayer}, and checking which
+ * encoding is used for the subtitle tracks:
+ * <p>
+ * <pre class="prettyprint">
+ * MediaPlayer mp = new MediaPlayer();
+ * mp.setDataSource(myContentLocation);
+ * mp.prepare(); // synchronous prepare, ready to use when method returns
+ * final TrackInfo[] trackInfos = mp.getTrackInfo();
+ * for (TrackInfo info : trackInfo) {
+ *     if (info.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+ *         final String mime = info.getFormat().getString(MediaFormat.KEY_MIME);
+ *         if (MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mime) {
+ *             // subtitle encoding is CEA 608
+ *         } else if (MediaFormat.MIMETYPE_TEXT_CEA_708.equals(mime) {
+ *             // subtitle encoding is CEA 708
+ *         } else if (MediaFormat.MIMETYPE_TEXT_VTT.equals(mime) {
+ *             // subtitle encoding is WebVTT
+ *         }
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * See
+ * {@link MediaPlayer#setOnSubtitleDataListener(android.media.MediaPlayer.OnSubtitleDataListener, android.os.Handler)}
+ * to receive subtitle data from a MediaPlayer object.
  *
  * @see android.media.MediaPlayer
  */
@@ -48,25 +72,47 @@
     private long mDurationUs;
     private byte[] mData;
 
+    /** @hide */
     public SubtitleData(Parcel parcel) {
         if (!parseParcel(parcel)) {
             throw new IllegalArgumentException("parseParcel() fails");
         }
     }
 
+    /**
+     * Returns the index of the MediaPlayer track which contains this subtitle data.
+     * @return an index in the array returned by {@link MediaPlayer#getTrackInfo()}.
+     */
     public int getTrackIndex() {
         return mTrackIndex;
     }
 
+    /**
+     * Returns the media time at which the subtitle should be displayed, expressed in microseconds.
+     * @return the display start time for the subtitle
+     */
     public long getStartTimeUs() {
         return mStartTimeUs;
     }
 
+    /**
+     * Returns the duration in microsecond during which the subtitle should be displayed.
+     * @return the display duration for the subtitle
+     */
     public long getDurationUs() {
         return mDurationUs;
     }
 
-    public byte[] getData() {
+    /**
+     * Returns the encoded data for the subtitle content.
+     * Encoding format depends on the subtitle type, refer to
+     * <a href="https://en.wikipedia.org/wiki/CEA-708">CEA 708</a>,
+     * <a href="https://en.wikipedia.org/wiki/EIA-608">CEA/EIA 608</a> and
+     * <a href="https://www.w3.org/TR/webvtt1/">WebVTT</a>, defined by the MIME type
+     * of the subtitle track.
+     * @return the encoded subtitle data
+     */
+    public @NonNull byte[] getData() {
         return mData;
     }
 
diff --git a/android/media/TestServiceRegistry.java b/android/media/TestServiceRegistry.java
deleted file mode 100644
index 6f5512e..0000000
--- a/android/media/TestServiceRegistry.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static org.junit.Assert.fail;
-
-import android.media.MediaSession2.ControllerInfo;
-import android.media.TestUtils.SyncHandler;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.GuardedBy;
-
-/**
- * Keeps the instance of currently running {@link MockMediaSessionService2}. And also provides
- * a way to control them in one place.
- * <p>
- * It only support only one service at a time.
- */
-public class TestServiceRegistry {
-    public interface ServiceInstanceChangedCallback {
-        void OnServiceInstanceChanged(MediaSessionService2 service);
-    }
-
-    @GuardedBy("TestServiceRegistry.class")
-    private static TestServiceRegistry sInstance;
-    @GuardedBy("TestServiceRegistry.class")
-    private MediaSessionService2 mService;
-    @GuardedBy("TestServiceRegistry.class")
-    private SyncHandler mHandler;
-    @GuardedBy("TestServiceRegistry.class")
-    private ControllerInfo mOnConnectControllerInfo;
-    @GuardedBy("TestServiceRegistry.class")
-    private ServiceInstanceChangedCallback mCallback;
-
-    public static TestServiceRegistry getInstance() {
-        synchronized (TestServiceRegistry.class) {
-            if (sInstance == null) {
-                sInstance = new TestServiceRegistry();
-            }
-            return sInstance;
-        }
-    }
-
-    public void setHandler(Handler handler) {
-        synchronized (TestServiceRegistry.class) {
-            mHandler = new SyncHandler(handler.getLooper());
-        }
-    }
-
-    public void setServiceInstanceChangedCallback(ServiceInstanceChangedCallback callback) {
-        synchronized (TestServiceRegistry.class) {
-            mCallback = callback;
-        }
-    }
-
-    public Handler getHandler() {
-        synchronized (TestServiceRegistry.class) {
-            return mHandler;
-        }
-    }
-
-    public void setServiceInstance(MediaSessionService2 service, ControllerInfo controller) {
-        synchronized (TestServiceRegistry.class) {
-            if (mService != null) {
-                fail("Previous service instance is still running. Clean up manually to ensure"
-                        + " previoulsy running service doesn't break current test");
-            }
-            mService = service;
-            mOnConnectControllerInfo = controller;
-            if (mCallback != null) {
-                mCallback.OnServiceInstanceChanged(service);
-            }
-        }
-    }
-
-    public MediaSessionService2 getServiceInstance() {
-        synchronized (TestServiceRegistry.class) {
-            return mService;
-        }
-    }
-
-    public ControllerInfo getOnConnectControllerInfo() {
-        synchronized (TestServiceRegistry.class) {
-            return mOnConnectControllerInfo;
-        }
-    }
-
-
-    public void cleanUp() {
-        synchronized (TestServiceRegistry.class) {
-            final ServiceInstanceChangedCallback callback = mCallback;
-            if (mService != null) {
-                try {
-                    if (mHandler.getLooper() == Looper.myLooper()) {
-                        mService.getSession().close();
-                    } else {
-                        mHandler.postAndSync(() -> {
-                            mService.getSession().close();
-                        });
-                    }
-                } catch (InterruptedException e) {
-                    // No-op. Service containing session will die, but shouldn't be a huge issue.
-                }
-                // stopSelf() would not kill service while the binder connection established by
-                // bindService() exists, and close() above will do the job instead.
-                // So stopSelf() isn't really needed, but just for sure.
-                mService.stopSelf();
-                mService = null;
-            }
-            if (mHandler != null) {
-                mHandler.removeCallbacksAndMessages(null);
-            }
-            mCallback = null;
-            mOnConnectControllerInfo = null;
-
-            if (callback != null) {
-                callback.OnServiceInstanceChanged(null);
-            }
-        }
-    }
-}
diff --git a/android/media/TestUtils.java b/android/media/TestUtils.java
deleted file mode 100644
index 9a1fa10..0000000
--- a/android/media/TestUtils.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.content.Context;
-import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
-import android.os.Bundle;
-import android.os.Handler;
-
-import android.os.Looper;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * Utilities for tests.
- */
-public final class TestUtils {
-    private static final int WAIT_TIME_MS = 1000;
-    private static final int WAIT_SERVICE_TIME_MS = 5000;
-
-    /**
-     * Creates a {@link android.media.session.PlaybackState} with the given state.
-     *
-     * @param state one of the PlaybackState.STATE_xxx.
-     * @return a PlaybackState
-     */
-    public static PlaybackState2 createPlaybackState(int state) {
-        return new PlaybackState2(state, 0, 0, 1.0f,
-                0, 0, null);
-    }
-
-    /**
-     * Finds the session with id in this test package.
-     *
-     * @param context
-     * @param id
-     * @return
-     */
-    // TODO(jaewan): Currently not working.
-    public static SessionToken2 getServiceToken(Context context, String id) {
-        MediaSessionManager manager =
-                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
-        List<SessionToken2> tokens = manager.getSessionServiceTokens();
-        for (int i = 0; i < tokens.size(); i++) {
-            SessionToken2 token = tokens.get(i);
-            if (context.getPackageName().equals(token.getPackageName())
-                    && id.equals(token.getId())) {
-                return token;
-            }
-        }
-        fail("Failed to find service");
-        return null;
-    }
-
-    /**
-     * Compares contents of two bundles.
-     *
-     * @param a a bundle
-     * @param b another bundle
-     * @return {@code true} if two bundles are the same. {@code false} otherwise. This may be
-     *     incorrect if any bundle contains a bundle.
-     */
-    public static boolean equals(Bundle a, Bundle b) {
-        if (a == b) {
-            return true;
-        }
-        if (a == null || b == null) {
-            return false;
-        }
-        if (!a.keySet().containsAll(b.keySet())
-                || !b.keySet().containsAll(a.keySet())) {
-            return false;
-        }
-        for (String key : a.keySet()) {
-            if (!Objects.equals(a.get(key), b.get(key))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Handler that always waits until the Runnable finishes.
-     */
-    public static class SyncHandler extends Handler {
-        public SyncHandler(Looper looper) {
-            super(looper);
-        }
-
-        public void postAndSync(Runnable runnable) throws InterruptedException {
-            final CountDownLatch latch = new CountDownLatch(1);
-            if (getLooper() == Looper.myLooper()) {
-                runnable.run();
-            } else {
-                post(()->{
-                    runnable.run();
-                    latch.countDown();
-                });
-                assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-            }
-        }
-    }
-}
diff --git a/android/media/VolumePolicy.java b/android/media/VolumePolicy.java
index bbcce82..bd6667f 100644
--- a/android/media/VolumePolicy.java
+++ b/android/media/VolumePolicy.java
@@ -23,7 +23,7 @@
 
 /** @hide */
 public final class VolumePolicy implements Parcelable {
-    public static final VolumePolicy DEFAULT = new VolumePolicy(false, false, true, 400);
+    public static final VolumePolicy DEFAULT = new VolumePolicy(false, false, false, 400);
 
     /**
      * Accessibility volume policy where the STREAM_MUSIC volume (i.e. media volume) affects
diff --git a/android/media/VolumeProvider2.java b/android/media/VolumeProvider2.java
new file mode 100644
index 0000000..1a4608f
--- /dev/null
+++ b/android/media/VolumeProvider2.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.media.update.ApiLoader;
+import android.media.update.VolumeProvider2Provider;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ * Handles requests to adjust or set the volume on a session. This is also used
+ * to push volume updates back to the session. The provider must call
+ * {@link #setCurrentVolume(int)} each time the volume being provided changes.
+ * <p>
+ * You can set a volume provider on a session by calling
+ * {@link MediaSession2#updatePlayer}.
+ */
+// New version of VolumeProvider with following changes
+//   - Don't implement Parcelable for updatable support.
+public abstract class VolumeProvider2 {
+    /**
+     * @hide
+     */
+    @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ControlType {}
+
+    /**
+     * The volume is fixed and can not be modified. Requests to change volume
+     * should be ignored.
+     */
+    public static final int VOLUME_CONTROL_FIXED = 0;
+
+    /**
+     * The volume control uses relative adjustment via
+     * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
+     * value should be ignored.
+     */
+    public static final int VOLUME_CONTROL_RELATIVE = 1;
+
+    /**
+     * The volume control uses an absolute value. It may be adjusted using
+     * {@link #onAdjustVolume(int)} or set directly using
+     * {@link #onSetVolumeTo(int)}.
+     */
+    public static final int VOLUME_CONTROL_ABSOLUTE = 2;
+
+    private final VolumeProvider2Provider mProvider;
+
+    /**
+     * Create a new volume provider for handling volume events. You must specify
+     * the type of volume control, the maximum volume that can be used, and the
+     * current volume on the output.
+     *
+     * @param controlType The method for controlling volume that is used by this provider.
+     * @param maxVolume The maximum allowed volume.
+     * @param currentVolume The current volume on the output.
+     */
+    public VolumeProvider2(@ControlType int controlType, int maxVolume, int currentVolume) {
+        mProvider = ApiLoader.getProvider().createVolumeProvider2(
+                this, controlType, maxVolume, currentVolume);
+    }
+
+    /**
+     * @hide
+     */
+    public VolumeProvider2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * Get the volume control type that this volume provider uses.
+     *
+     * @return The volume control type for this volume provider
+     */
+    @ControlType
+    public final int getControlType() {
+        return mProvider.getControlType_impl();
+    }
+
+    /**
+     * Get the maximum volume this provider allows.
+     *
+     * @return The max allowed volume.
+     */
+    public final int getMaxVolume() {
+        return mProvider.getMaxVolume_impl();
+    }
+
+    /**
+     * Gets the current volume. This will be the last value set by
+     * {@link #setCurrentVolume(int)}.
+     *
+     * @return The current volume.
+     */
+    public final int getCurrentVolume() {
+        return mProvider.getCurrentVolume_impl();
+    }
+
+    /**
+     * Notify the system that the current volume has been changed. This must be
+     * called every time the volume changes to ensure it is displayed properly.
+     *
+     * @param currentVolume The current volume on the output.
+     */
+    public final void setCurrentVolume(int currentVolume) {
+        mProvider.setCurrentVolume_impl(currentVolume);
+    }
+
+    /**
+     * Override to handle requests to set the volume of the current output.
+     * After the volume has been modified {@link #setCurrentVolume} must be
+     * called to notify the system.
+     *
+     * @param volume The volume to set the output to.
+     */
+    public void onSetVolumeTo(int volume) { }
+
+    /**
+     * Override to handle requests to adjust the volume of the current output.
+     * Direction will be one of {@link AudioManager#ADJUST_LOWER},
+     * {@link AudioManager#ADJUST_RAISE}, {@link AudioManager#ADJUST_SAME}.
+     * After the volume has been modified {@link #setCurrentVolume} must be
+     * called to notify the system.
+     *
+     * @param direction The direction to change the volume in.
+     */
+    public void onAdjustVolume(int direction) { }
+}
diff --git a/android/media/audiofx/AudioEffect.java b/android/media/audiofx/AudioEffect.java
index 7dbca3b..21d6873 100644
--- a/android/media/audiofx/AudioEffect.java
+++ b/android/media/audiofx/AudioEffect.java
@@ -39,6 +39,7 @@
  *   <li> {@link android.media.audiofx.BassBoost}</li>
  *   <li> {@link android.media.audiofx.PresetReverb}</li>
  *   <li> {@link android.media.audiofx.EnvironmentalReverb}</li>
+ *   <li> {@link android.media.audiofx.DynamicsProcessing}</li>
  * </ul>
  * <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance,
  * the application must specify the audio session ID of that instance when creating the AudioEffect.
@@ -126,6 +127,12 @@
               .fromString("fe3199be-aed0-413f-87bb-11260eb63cf1");
 
     /**
+     * UUID for Dynamics Processing
+     */
+    public static final UUID EFFECT_TYPE_DYNAMICS_PROCESSING = UUID
+              .fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
+
+    /**
      * Null effect UUID. Used when the UUID for effect type of
      * @hide
      */
@@ -203,7 +210,8 @@
      * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
      * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
      * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
-     * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.
+     * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+     * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
      *  </li>
      *  <li>uuid: UUID for this particular implementation</li>
      *  <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
@@ -224,7 +232,8 @@
          * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
          * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
          * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
-         * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.
+         * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+         * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
          * @param uuid         UUID for this particular implementation
          * @param connectMode  {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
          * @param name         human readable effect name
@@ -246,7 +255,8 @@
          *  {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
          *  {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER},
          *  {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}
-         *   or {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.<br>
+         *  {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}
+         *   or {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.<br>
          *  For reverberation, bass boost, EQ and virtualizer, the UUID
          *  corresponds to the OpenSL ES Interface ID.
          */
@@ -1344,6 +1354,34 @@
     /**
      * @hide
      */
+    public static float byteArrayToFloat(byte[] valueBuf) {
+        return byteArrayToFloat(valueBuf, 0);
+
+    }
+
+    /**
+     * @hide
+     */
+    public static float byteArrayToFloat(byte[] valueBuf, int offset) {
+        ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+        converter.order(ByteOrder.nativeOrder());
+        return converter.getFloat(offset);
+
+    }
+
+    /**
+     * @hide
+     */
+    public static byte[] floatToByteArray(float value) {
+        ByteBuffer converter = ByteBuffer.allocate(4);
+        converter.order(ByteOrder.nativeOrder());
+        converter.putFloat(value);
+        return converter.array();
+    }
+
+    /**
+     * @hide
+     */
     public static byte[] concatArrays(byte[]... arrays) {
         int len = 0;
         for (byte[] a : arrays) {
diff --git a/android/media/audiofx/DynamicsProcessing.java b/android/media/audiofx/DynamicsProcessing.java
new file mode 100644
index 0000000..4c17ae1
--- /dev/null
+++ b/android/media/audiofx/DynamicsProcessing.java
@@ -0,0 +1,2402 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audiofx;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioTrack;
+import android.media.MediaPlayer;
+import android.media.audiofx.AudioEffect;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.StringTokenizer;
+
+/**
+ * DynamicsProcessing is an audio effect for equalizing and changing dynamic range properties of the
+ * sound. It is composed of multiple stages including equalization, multi-band compression and
+ * limiter.
+ * <p>The number of bands and active stages is configurable, and most parameters can be controlled
+ * in realtime, such as gains, attack/release times, thresholds, etc.
+ * <p>The effect is instantiated and controlled by channels. Each channel has the same basic
+ * architecture, but all of their parameters are independent from other channels.
+ * <p>The basic channel configuration is:
+ * <pre>
+ *
+ *    Channel 0          Channel 1       ....       Channel N-1
+ *      Input              Input                       Input
+ *        |                  |                           |
+ *   +----v----+        +----v----+                 +----v----+
+ *   |inputGain|        |inputGain|                 |inputGain|
+ *   +---------+        +---------+                 +---------+
+ *        |                  |                           |
+ *  +-----v-----+      +-----v-----+               +-----v-----+
+ *  |   PreEQ   |      |   PreEQ   |               |   PreEQ   |
+ *  +-----------+      +-----------+               +-----------+
+ *        |                  |                           |
+ *  +-----v-----+      +-----v-----+               +-----v-----+
+ *  |    MBC    |      |    MBC    |               |    MBC    |
+ *  +-----------+      +-----------+               +-----------+
+ *        |                  |                           |
+ *  +-----v-----+      +-----v-----+               +-----v-----+
+ *  |  PostEQ   |      |  PostEQ   |               |  PostEQ   |
+ *  +-----------+      +-----------+               +-----------+
+ *        |                  |                           |
+ *  +-----v-----+      +-----v-----+               +-----v-----+
+ *  |  Limiter  |      |  Limiter  |               |  Limiter  |
+ *  +-----------+      +-----------+               +-----------+
+ *        |                  |                           |
+ *     Output             Output                      Output
+ * </pre>
+ *
+ * <p>Where the stages are:
+ * inputGain: input gain factor in decibels (dB). 0 dB means no change in level.
+ * PreEQ:  Multi-band Equalizer.
+ * MBC:    Multi-band Compressor
+ * PostEQ: Multi-band Equalizer
+ * Limiter: Single band compressor/limiter.
+ *
+ * <p>An application creates a DynamicsProcessing object to instantiate and control this audio
+ * effect in the audio framework. A DynamicsProcessor.Config and DynamicsProcessor.Config.Builder
+ * are available to help configure the multiple stages and each band parameters if desired.
+ * <p>See each stage documentation for further details.
+ * <p>If no Config is specified during creation, a default configuration is chosen.
+ * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer,
+ * specify the audio session ID of this AudioTrack or MediaPlayer when constructing the effect
+ * (see {@link AudioTrack#getAudioSessionId()} and {@link MediaPlayer#getAudioSessionId()}).
+ *
+ * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer, specify the audio
+ * session ID of this AudioTrack or MediaPlayer when constructing the DynamicsProcessing.
+ * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
+ * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
+ * effects.
+ */
+
+public final class DynamicsProcessing extends AudioEffect {
+
+    private final static String TAG = "DynamicsProcessing";
+
+    // These parameter constants must be synchronized with those in
+    // /system/media/audio_effects/include/audio_effects/effect_dynamicsprocessing.h
+    private static final int PARAM_GET_CHANNEL_COUNT = 0x10;
+    private static final int PARAM_INPUT_GAIN = 0x20;
+    private static final int PARAM_ENGINE_ARCHITECTURE = 0x30;
+    private static final int PARAM_PRE_EQ = 0x40;
+    private static final int PARAM_PRE_EQ_BAND = 0x45;
+    private static final int PARAM_MBC = 0x50;
+    private static final int PARAM_MBC_BAND = 0x55;
+    private static final int PARAM_POST_EQ = 0x60;
+    private static final int PARAM_POST_EQ_BAND = 0x65;
+    private static final int PARAM_LIMITER = 0x70;
+
+    /**
+     * Index of variant that favors frequency resolution. Frequency domain based implementation.
+     */
+    public static final int VARIANT_FAVOR_FREQUENCY_RESOLUTION  = 0;
+
+    /**
+     * Index of variant that favors time resolution resolution. Time domain based implementation.
+     */
+    public static final int VARIANT_FAVOR_TIME_RESOLUTION       = 1;
+
+    /**
+     * Maximum expected channels to be reported by effect
+     */
+    private static final int CHANNEL_COUNT_MAX = 32;
+
+    /**
+     * Number of channels in effect architecture
+     */
+    private int mChannelCount = 0;
+
+    /**
+     * Registered listener for parameter changes.
+     */
+    private OnParameterChangeListener mParamListener = null;
+
+    /**
+     * Listener used internally to to receive raw parameter change events
+     * from AudioEffect super class
+     */
+    private BaseParameterListener mBaseParamListener = null;
+
+    /**
+     * Lock for access to mParamListener
+     */
+    private final Object mParamListenerLock = new Object();
+
+    /**
+     * Class constructor.
+     * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
+     * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+     */
+    public DynamicsProcessing(int audioSession) {
+        this(0 /*priority*/, audioSession);
+    }
+
+    /**
+     * @hide
+     * Class constructor for the DynamicsProcessing audio effect.
+     * @param priority the priority level requested by the application for controlling the
+     * DynamicsProcessing engine. As the same engine can be shared by several applications,
+     * this parameter indicates how much the requesting application needs control of effect
+     * parameters. The normal priority is 0, above normal is a positive number, below normal a
+     * negative number.
+     * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
+     * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+     */
+    public DynamicsProcessing(int priority, int audioSession) {
+        this(priority, audioSession, null);
+    }
+
+    /**
+     * Class constructor for the DynamicsProcessing audio effect
+     * @param priority the priority level requested by the application for controlling the
+     * DynamicsProcessing engine. As the same engine can be shared by several applications,
+     * this parameter indicates how much the requesting application needs control of effect
+     * parameters. The normal priority is 0, above normal is a positive number, below normal a
+     * negative number.
+     * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
+     * will be attached to the MediaPlayer or AudioTrack in the same audio session.
+     * @param cfg Config object used to setup the audio effect, including bands per stage, and
+     * specific parameters for each stage/band. Use
+     * {@link android.media.audiofx.DynamicsProcessing.Config.Builder} to create a
+     * Config object that suits your needs. A null cfg parameter will create and use a default
+     * configuration for the effect
+     */
+    public DynamicsProcessing(int priority, int audioSession, @Nullable Config cfg) {
+        super(EFFECT_TYPE_DYNAMICS_PROCESSING, EFFECT_TYPE_NULL, priority, audioSession);
+        if (audioSession == 0) {
+            Log.w(TAG, "WARNING: attaching a DynamicsProcessing to global output mix is"
+                    + "deprecated!");
+        }
+        final Config config;
+        mChannelCount = getChannelCount();
+        if (cfg == null) {
+            //create a default configuration and effect, with the number of channels this effect has
+            DynamicsProcessing.Config.Builder builder =
+                    new DynamicsProcessing.Config.Builder(
+                            CONFIG_DEFAULT_VARIANT,
+                            mChannelCount,
+                            CONFIG_DEFAULT_USE_PREEQ,
+                            CONFIG_DEFAULT_PREEQ_BANDS,
+                            CONFIG_DEFAULT_USE_MBC,
+                            CONFIG_DEFAULT_MBC_BANDS,
+                            CONFIG_DEFAULT_USE_POSTEQ,
+                            CONFIG_DEFAULT_POSTEQ_BANDS,
+                            CONFIG_DEFAULT_USE_LIMITER);
+            config = builder.build();
+        } else {
+            //validate channels are ok. decide what to do: replicate channels if more
+            config = new DynamicsProcessing.Config(mChannelCount, cfg);
+        }
+
+        //configure engine
+        setEngineArchitecture(config.getVariant(),
+                config.getPreferredFrameDuration(),
+                config.isPreEqInUse(),
+                config.getPreEqBandCount(),
+                config.isMbcInUse(),
+                config.getMbcBandCount(),
+                config.isPostEqInUse(),
+                config.getPostEqBandCount(),
+                config.isLimiterInUse());
+        //update all the parameters
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            updateEngineChannelByChannelIndex(ch, config.getChannelByChannelIndex(ch));
+        }
+    }
+
+    /**
+     * Returns the Config object used to setup this effect.
+     * @return Config Current Config object used to setup this DynamicsProcessing effect.
+     */
+    public Config getConfig() {
+        //Query engine architecture to create config object
+        Number[] params = { PARAM_ENGINE_ARCHITECTURE };
+        Number[] values = { 0 /*0 variant */,
+                0.0f /* 1 preferredFrameDuration */,
+                0 /*2 preEqInUse */,
+                0 /*3 preEqBandCount */,
+                0 /*4 mbcInUse */,
+                0 /*5 mbcBandCount*/,
+                0 /*6 postEqInUse */,
+                0 /*7 postEqBandCount */,
+                0 /*8 limiterInUse */};
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
+        getParameter(paramBytes, valueBytes);
+        byteArrayToNumberArray(valueBytes, values);
+        DynamicsProcessing.Config.Builder builder =
+                new DynamicsProcessing.Config.Builder(
+                        values[0].intValue(),
+                        mChannelCount,
+                        values[2].intValue() > 0 /*use preEQ*/,
+                        values[3].intValue() /*pre eq bands*/,
+                        values[4].intValue() > 0 /*use mbc*/,
+                        values[5].intValue() /*mbc bands*/,
+                        values[6].intValue() > 0 /*use postEQ*/,
+                        values[7].intValue()/*postEq bands*/,
+                        values[8].intValue() > 0 /*use Limiter*/).
+                setPreferredFrameDuration(values[1].floatValue());
+        Config config = builder.build();
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            Channel channel = queryEngineByChannelIndex(ch);
+            config.setChannelTo(ch, channel);
+        }
+        return config;
+    }
+
+
+    private static final int CONFIG_DEFAULT_VARIANT = VARIANT_FAVOR_FREQUENCY_RESOLUTION;
+    private static final boolean CONFIG_DEFAULT_USE_PREEQ = true;
+    private static final int CONFIG_DEFAULT_PREEQ_BANDS = 6;
+    private static final boolean CONFIG_DEFAULT_USE_MBC = true;
+    private static final int CONFIG_DEFAULT_MBC_BANDS = 6;
+    private static final boolean CONFIG_DEFAULT_USE_POSTEQ = true;
+    private static final int CONFIG_DEFAULT_POSTEQ_BANDS = 6;
+    private static final boolean CONFIG_DEFAULT_USE_LIMITER = true;
+
+    private static final float CHANNEL_DEFAULT_INPUT_GAIN = 0; // dB
+    private static final float CONFIG_PREFERRED_FRAME_DURATION_MS = 10.0f; //milliseconds
+
+    private static final float EQ_DEFAULT_GAIN = 0; // dB
+    private static final boolean PREEQ_DEFAULT_ENABLED = true;
+    private static final boolean POSTEQ_DEFAULT_ENABLED = true;
+
+
+    private static final boolean MBC_DEFAULT_ENABLED = true;
+    private static final float MBC_DEFAULT_ATTACK_TIME = 50; // ms
+    private static final float MBC_DEFAULT_RELEASE_TIME = 120; // ms
+    private static final float MBC_DEFAULT_RATIO = 2; // 1:N
+    private static final float MBC_DEFAULT_THRESHOLD = -30; // dB
+    private static final float MBC_DEFAULT_KNEE_WIDTH = 0; // dB
+    private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -80; // dB
+    private static final float MBC_DEFAULT_EXPANDER_RATIO = 1.5f; // N:1
+    private static final float MBC_DEFAULT_PRE_GAIN = 0; // dB
+    private static final float MBC_DEFAULT_POST_GAIN = 10; // dB
+
+    private static final boolean LIMITER_DEFAULT_ENABLED = true;
+    private static final int LIMITER_DEFAULT_LINK_GROUP = 0;//;
+    private static final float LIMITER_DEFAULT_ATTACK_TIME = 50; // ms
+    private static final float LIMITER_DEFAULT_RELEASE_TIME = 120; // ms
+    private static final float LIMITER_DEFAULT_RATIO = 2; // 1:N
+    private static final float LIMITER_DEFAULT_THRESHOLD = -30; // dB
+    private static final float LIMITER_DEFAULT_POST_GAIN = 10; // dB
+
+    private static final float DEFAULT_MIN_FREQUENCY = 220; // Hz
+    private static final float DEFAULT_MAX_FREQUENCY = 20000; // Hz
+    private static final float mMinFreqLog = (float)Math.log10(DEFAULT_MIN_FREQUENCY);
+    private static final float mMaxFreqLog = (float)Math.log10(DEFAULT_MAX_FREQUENCY);
+
+    /**
+     * base class for the different stages.
+     */
+    public static class Stage {
+        private boolean mInUse;
+        private boolean mEnabled;
+        /**
+         * Class constructor for stage
+         * @param inUse true if this stage is set to be used. False otherwise. Stages that are not
+         * set "inUse" at initialization time are not available to be used at any time.
+         * @param enabled true if this stage is currently used to process sound. When disabled,
+         * the stage is bypassed and the sound is copied unaltered from input to output.
+         */
+        public Stage(boolean inUse, boolean enabled) {
+            mInUse = inUse;
+            mEnabled = enabled;
+        }
+
+        /**
+         * returns enabled state of the stage
+         * @return true if stage is enabled for processing, false otherwise
+         */
+        public boolean isEnabled() {
+            return mEnabled;
+        }
+        /**
+         * sets enabled state of the stage
+         * @param enabled true for enabled, false otherwise
+         */
+        public void setEnabled(boolean enabled) {
+            mEnabled = enabled;
+        }
+
+        /**
+         * returns inUse state of the stage.
+         * @return inUse state of the stage. True if this stage is currently used to process sound.
+         * When false, the stage is bypassed and the sound is copied unaltered from input to output.
+         */
+        public boolean isInUse() {
+            return mInUse;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(String.format(" Stage InUse: %b\n", isInUse()));
+            if (isInUse()) {
+                sb.append(String.format(" Stage Enabled: %b\n", mEnabled));
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Base class for stages that hold bands
+     */
+    public static class BandStage extends Stage{
+        private int mBandCount;
+        /**
+         * Class constructor for BandStage
+         * @param inUse true if this stage is set to be used. False otherwise. Stages that are not
+         * set "inUse" at initialization time are not available to be used at any time.
+         * @param enabled true if this stage is currently used to process sound. When disabled,
+         * the stage is bypassed and the sound is copied unaltered from input to output.
+         * @param bandCount number of bands this stage will handle. If stage is not inUse, bandcount
+         * is set to 0
+         */
+        public BandStage(boolean inUse, boolean enabled, int bandCount) {
+            super(inUse, enabled);
+            mBandCount = isInUse() ? bandCount : 0;
+        }
+
+        /**
+         * gets number of bands held in this stage
+         * @return number of bands held in this stage
+         */
+        public int getBandCount() {
+            return mBandCount;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            if (isInUse()) {
+                sb.append(String.format(" Band Count: %d\n", mBandCount));
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Base class for bands
+     */
+    public static class BandBase {
+        private boolean mEnabled;
+        private float mCutoffFrequency;
+        /**
+         * Class constructor for BandBase
+         * @param enabled true if this band is currently used to process sound. When false,
+         * the band is effectively muted and sound set to zero.
+         * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
+         * effective bandwidth for the band is then computed using this and the previous band
+         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+         */
+        public BandBase(boolean enabled, float cutoffFrequency) {
+            mEnabled = enabled;
+            mCutoffFrequency = cutoffFrequency;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(String.format(" Enabled: %b\n", mEnabled));
+            sb.append(String.format(" CutoffFrequency: %f\n", mCutoffFrequency));
+            return sb.toString();
+        }
+
+        /**
+         * returns enabled state of the band
+         * @return true if bands is enabled for processing, false otherwise
+         */
+        public boolean isEnabled() {
+            return mEnabled;
+        }
+        /**
+         * sets enabled state of the band
+         * @param enabled true for enabled, false otherwise
+         */
+        public void setEnabled(boolean enabled) {
+            mEnabled = enabled;
+        }
+
+        /**
+         * gets cutoffFrequency for this band in Hertz (Hz)
+         * @return cutoffFrequency for this band in Hertz (Hz)
+         */
+        public float getCutoffFrequency() {
+            return mCutoffFrequency;
+        }
+
+        /**
+         * sets topmost frequency number (in Hz) this band will process. The
+         * effective bandwidth for the band is then computed using this and the previous band
+         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+         * @param frequency
+         */
+        public void setCutoffFrequency(float frequency) {
+            mCutoffFrequency = frequency;
+        }
+    }
+
+    /**
+     * Class for Equalizer Bands
+     * Equalizer bands have three controllable parameters: enabled/disabled, cutoffFrequency and
+     * gain
+     */
+    public final static class EqBand extends BandBase {
+        private float mGain;
+        /**
+         * Class constructor for EqBand
+         * @param enabled true if this band is currently used to process sound. When false,
+         * the band is effectively muted and sound set to zero.
+         * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
+         * effective bandwidth for the band is then computed using this and the previous band
+         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+         * @param gain of equalizer band in decibels (dB). A gain of 0 dB means no change in level.
+         */
+        public EqBand(boolean enabled, float cutoffFrequency, float gain) {
+            super(enabled, cutoffFrequency);
+            mGain = gain;
+        }
+
+        /**
+         * Class constructor for EqBand
+         * @param cfg copy constructor
+         */
+        public EqBand(EqBand cfg) {
+            super(cfg.isEnabled(), cfg.getCutoffFrequency());
+            mGain = cfg.mGain;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            sb.append(String.format(" Gain: %f\n", mGain));
+            return sb.toString();
+        }
+
+        /**
+         * gets current gain of band in decibels (dB)
+         * @return current gain of band in decibels (dB)
+         */
+        public float getGain() {
+            return mGain;
+        }
+
+        /**
+         * sets current gain of band in decibels (dB)
+         * @param gain desired in decibels (db)
+         */
+        public void setGain(float gain) {
+            mGain = gain;
+        }
+    }
+
+    /**
+     * Class for Multi-Band compressor bands
+     * MBC bands have multiple controllable parameters: enabled/disabled, cutoffFrequency,
+     * attackTime, releaseTime, ratio, threshold, kneeWidth, noiseGateThreshold, expanderRatio,
+     * preGain and postGain.
+     */
+    public final static class MbcBand extends BandBase{
+        private float mAttackTime;
+        private float mReleaseTime;
+        private float mRatio;
+        private float mThreshold;
+        private float mKneeWidth;
+        private float mNoiseGateThreshold;
+        private float mExpanderRatio;
+        private float mPreGain;
+        private float mPostGain;
+        /**
+         * Class constructor for MbcBand
+         * @param enabled true if this band is currently used to process sound. When false,
+         * the band is effectively muted and sound set to zero.
+         * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
+         * effective bandwidth for the band is then computed using this and the previous band
+         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
+         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
+         * @param attackTime Attack Time for compressor in milliseconds (ms)
+         * @param releaseTime Release Time for compressor in milliseconds (ms)
+         * @param ratio Compressor ratio (1:N)
+         * @param threshold Compressor threshold measured in decibels (dB) from 0 dB Full Scale
+         * (dBFS).
+         * @param kneeWidth Width in decibels (dB) around compressor threshold point.
+         * @param noiseGateThreshold Noise gate threshold in decibels (dB) from 0 dB Full Scale
+         * (dBFS).
+         * @param expanderRatio Expander ratio (N:1) for signals below the Noise Gate Threshold.
+         * @param preGain Gain applied to the signal BEFORE the compression.
+         * @param postGain Gain applied to the signal AFTER compression.
+         */
+        public MbcBand(boolean enabled, float cutoffFrequency, float attackTime, float releaseTime,
+                float ratio, float threshold, float kneeWidth, float noiseGateThreshold,
+                float expanderRatio, float preGain, float postGain) {
+            super(enabled, cutoffFrequency);
+            mAttackTime = attackTime;
+            mReleaseTime = releaseTime;
+            mRatio = ratio;
+            mThreshold = threshold;
+            mKneeWidth = kneeWidth;
+            mNoiseGateThreshold = noiseGateThreshold;
+            mExpanderRatio = expanderRatio;
+            mPreGain = preGain;
+            mPostGain = postGain;
+        }
+
+        /**
+         * Class constructor for MbcBand
+         * @param cfg copy constructor
+         */
+        public MbcBand(MbcBand cfg) {
+            super(cfg.isEnabled(), cfg.getCutoffFrequency());
+            mAttackTime = cfg.mAttackTime;
+            mReleaseTime = cfg.mReleaseTime;
+            mRatio = cfg.mRatio;
+            mThreshold = cfg.mThreshold;
+            mKneeWidth = cfg.mKneeWidth;
+            mNoiseGateThreshold = cfg.mNoiseGateThreshold;
+            mExpanderRatio = cfg.mExpanderRatio;
+            mPreGain = cfg.mPreGain;
+            mPostGain = cfg.mPostGain;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime));
+            sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime));
+            sb.append(String.format(" Ratio: 1:%f\n", mRatio));
+            sb.append(String.format(" Threshold: %f (dB)\n", mThreshold));
+            sb.append(String.format(" NoiseGateThreshold: %f(dB)\n", mNoiseGateThreshold));
+            sb.append(String.format(" ExpanderRatio: %f:1\n", mExpanderRatio));
+            sb.append(String.format(" PreGain: %f (dB)\n", mPreGain));
+            sb.append(String.format(" PostGain: %f (dB)\n", mPostGain));
+            return sb.toString();
+        }
+
+        /**
+         * gets attack time for compressor in milliseconds (ms)
+         * @return attack time for compressor in milliseconds (ms)
+         */
+        public float getAttackTime() { return mAttackTime; }
+        /**
+         * sets attack time for compressor in milliseconds (ms)
+         * @param attackTime desired for compressor in milliseconds (ms)
+         */
+        public void setAttackTime(float attackTime) { mAttackTime = attackTime; }
+        /**
+         * gets release time for compressor in milliseconds (ms)
+         * @return release time for compressor in milliseconds (ms)
+         */
+        public float getReleaseTime() { return mReleaseTime; }
+        /**
+         * sets release time for compressor in milliseconds (ms)
+         * @param releaseTime desired for compressor in milliseconds (ms)
+         */
+        public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
+        /**
+         * gets the compressor ratio (1:N)
+         * @return compressor ratio (1:N)
+         */
+        public float getRatio() { return mRatio; }
+        /**
+         * sets compressor ratio (1:N)
+         * @param ratio desired for the compressor (1:N)
+         */
+        public void setRatio(float ratio) { mRatio = ratio; }
+        /**
+         * gets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+         * Thresholds are negative. A threshold of 0 dB means no compression will take place.
+         * @return compressor threshold in decibels (dB)
+         */
+        public float getThreshold() { return mThreshold; }
+        /**
+         * sets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
+         * Thresholds are negative. A threshold of 0 dB means no compression will take place.
+         * @param threshold desired for compressor in decibels(dB)
+         */
+        public void setThreshold(float threshold) { mThreshold = threshold; }
+        /**
+         * get Knee Width in decibels (dB) around compressor threshold point. Widths are always
+         * positive, with higher values representing a wider area of transition from the linear zone
+         * to the compression zone. A knee of 0 dB means a more abrupt transition.
+         * @return Knee Width in decibels (dB)
+         */
+        public float getKneeWidth() { return mKneeWidth; }
+        /**
+         * sets knee width in decibels (dB). See
+         * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getKneeWidth} for more
+         * information.
+         * @param kneeWidth desired in decibels (dB)
+         */
+        public void setKneeWidth(float kneeWidth) { mKneeWidth = kneeWidth; }
+        /**
+         * gets the noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). Noise gate
+         * thresholds are negative. Signals below this level will be expanded according the
+         * expanderRatio parameter. A Noise Gate Threshold of -75 dB means very quiet signals might
+         * be effectively removed from the signal.
+         * @return Noise Gate Threshold in decibels (dB)
+         */
+        public float getNoiseGateThreshold() { return mNoiseGateThreshold; }
+        /**
+         * sets noise gate threshod in decibels (dB). See
+         * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getNoiseGateThreshold} for more
+         * information.
+         * @param noiseGateThreshold desired in decibels (dB)
+         */
+        public void setNoiseGateThreshold(float noiseGateThreshold) {
+            mNoiseGateThreshold = noiseGateThreshold; }
+        /**
+         * gets Expander ratio (N:1) for signals below the Noise Gate Threshold.
+         * @return Expander ratio (N:1)
+         */
+        public float getExpanderRatio() { return mExpanderRatio; }
+        /**
+         * sets Expander ratio (N:1) for signals below the Noise Gate Threshold.
+         * @param expanderRatio desired expander ratio (N:1)
+         */
+        public void setExpanderRatio(float expanderRatio) { mExpanderRatio = expanderRatio; }
+        /**
+         * gets the gain applied to the signal BEFORE the compression. Measured in decibels (dB)
+         * where 0 dB means no level change.
+         * @return preGain value in decibels (dB)
+         */
+        public float getPreGain() { return mPreGain; }
+        /**
+         * sets the gain to be applied to the signal BEFORE the compression, measured in decibels
+         * (dB), where 0 dB means no level change.
+         * @param preGain desired in decibels (dB)
+         */
+        public void setPreGain(float preGain) { mPreGain = preGain; }
+        /**
+         * gets the gain applied to the signal AFTER compression. Measured in decibels (dB) where 0
+         * dB means no level change
+         * @return postGain value in decibels (dB)
+         */
+        public float getPostGain() { return mPostGain; }
+        /**
+         * sets the gain to be applied to the siganl AFTER the compression. Measured in decibels
+         * (dB), where 0 dB means no level change.
+         * @param postGain desired value in decibels (dB)
+         */
+        public void setPostGain(float postGain) { mPostGain = postGain; }
+    }
+
+    /**
+     * Class for Equalizer stage
+     */
+    public final static class Eq extends BandStage {
+        private final EqBand[] mBands;
+        /**
+         * Class constructor for Equalizer (Eq) stage
+         * @param inUse true if Eq stage will be used, false otherwise.
+         * @param enabled true if Eq stage is enabled/disabled. This can be changed while effect is
+         * running
+         * @param bandCount number of bands for this Equalizer stage. Can't be changed while effect
+         * is running
+         */
+        public Eq(boolean inUse, boolean enabled, int bandCount) {
+            super(inUse, enabled, bandCount);
+            if (isInUse()) {
+                mBands = new EqBand[bandCount];
+                for (int b = 0; b < bandCount; b++) {
+                    float freq = DEFAULT_MAX_FREQUENCY;
+                    if (bandCount > 1) {
+                        freq = (float)Math.pow(10, mMinFreqLog +
+                                b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1));
+                    }
+                    mBands[b] = new EqBand(true, freq, EQ_DEFAULT_GAIN);
+                }
+            } else {
+                mBands = null;
+            }
+        }
+        /**
+         * Class constructor for Eq stage
+         * @param cfg copy constructor
+         */
+        public Eq(Eq cfg) {
+            super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount());
+            if (isInUse()) {
+                mBands = new EqBand[cfg.mBands.length];
+                for (int b = 0; b < mBands.length; b++) {
+                    mBands[b] = new EqBand(cfg.mBands[b]);
+                }
+            } else {
+                mBands = null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            if (isInUse()) {
+                sb.append("--->EqBands: " + mBands.length + "\n");
+                for (int b = 0; b < mBands.length; b++) {
+                    sb.append(String.format("  Band %d\n", b));
+                    sb.append(mBands[b].toString());
+                }
+            }
+            return sb.toString();
+        }
+        /**
+         * Helper function to check if band index is within range
+         * @param band index to check
+         */
+        private void checkBand(int band) {
+            if (mBands == null || band < 0 || band >= mBands.length) {
+                throw new IllegalArgumentException("band index " + band +" out of bounds");
+            }
+        }
+        /**
+         * Sets EqBand object for given band index
+         * @param band index of band to be modified
+         * @param bandCfg EqBand object.
+         */
+        public void setBand(int band, EqBand bandCfg) {
+            checkBand(band);
+            mBands[band] = new EqBand(bandCfg);
+        }
+        /**
+         * Gets EqBand object for band of interest.
+         * @param band index of band of interest
+         * @return EqBand Object
+         */
+        public EqBand getBand(int band) {
+            checkBand(band);
+            return mBands[band];
+        }
+    }
+
+    /**
+     * Class for Multi-Band Compressor (MBC) stage
+     */
+    public final static class Mbc extends BandStage {
+        private final MbcBand[] mBands;
+        /**
+         * Constructor for Multi-Band Compressor (MBC) stage
+         * @param inUse true if MBC stage will be used, false otherwise.
+         * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect
+         * is running
+         * @param bandCount number of bands for this MBC stage. Can't be changed while effect is
+         * running
+         */
+        public Mbc(boolean inUse, boolean enabled, int bandCount) {
+            super(inUse, enabled, bandCount);
+            if (isInUse()) {
+                mBands = new MbcBand[bandCount];
+                for (int b = 0; b < bandCount; b++) {
+                    float freq = DEFAULT_MAX_FREQUENCY;
+                    if (bandCount > 1) {
+                        freq = (float)Math.pow(10, mMinFreqLog +
+                                b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1));
+                    }
+                    mBands[b] = new MbcBand(true, freq, MBC_DEFAULT_ATTACK_TIME,
+                            MBC_DEFAULT_RELEASE_TIME, MBC_DEFAULT_RATIO,
+                            MBC_DEFAULT_THRESHOLD, MBC_DEFAULT_KNEE_WIDTH,
+                            MBC_DEFAULT_NOISE_GATE_THRESHOLD, MBC_DEFAULT_EXPANDER_RATIO,
+                            MBC_DEFAULT_PRE_GAIN, MBC_DEFAULT_POST_GAIN);
+                }
+            } else {
+                mBands = null;
+            }
+        }
+        /**
+         * Class constructor for MBC stage
+         * @param cfg copy constructor
+         */
+        public Mbc(Mbc cfg) {
+            super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount());
+            if (isInUse()) {
+                mBands = new MbcBand[cfg.mBands.length];
+                for (int b = 0; b < mBands.length; b++) {
+                    mBands[b] = new MbcBand(cfg.mBands[b]);
+                }
+            } else {
+                mBands = null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            if (isInUse()) {
+                sb.append("--->MbcBands: " + mBands.length + "\n");
+                for (int b = 0; b < mBands.length; b++) {
+                    sb.append(String.format("  Band %d\n", b));
+                    sb.append(mBands[b].toString());
+                }
+            }
+            return sb.toString();
+        }
+        /**
+         * Helper function to check if band index is within range
+         * @param band index to check
+         */
+        private void checkBand(int band) {
+            if (mBands == null || band < 0 || band >= mBands.length) {
+                throw new IllegalArgumentException("band index " + band +" out of bounds");
+            }
+        }
+        /**
+         * Sets MbcBand object for given band index
+         * @param band index of band to be modified
+         * @param bandCfg MbcBand object.
+         */
+        public void setBand(int band, MbcBand bandCfg) {
+            checkBand(band);
+            mBands[band] = new MbcBand(bandCfg);
+        }
+        /**
+         * Gets MbcBand object for band of interest.
+         * @param band index of band of interest
+         * @return MbcBand Object
+         */
+        public MbcBand getBand(int band) {
+            checkBand(band);
+            return mBands[band];
+        }
+    }
+
+    /**
+     * Class for Limiter Stage
+     * Limiter is a single band compressor at the end of the processing chain, commonly used to
+     * protect the signal from overloading and distortion. Limiters have multiple controllable
+     * parameters: enabled/disabled, linkGroup, attackTime, releaseTime, ratio, threshold, and
+     * postGain.
+     * <p>Limiters can be linked in groups across multiple channels. Linked limiters will trigger
+     * the same limiting if any of the linked limiters starts compressing.
+     */
+    public final static class Limiter extends Stage {
+        private int mLinkGroup;
+        private float mAttackTime;
+        private float mReleaseTime;
+        private float mRatio;
+        private float mThreshold;
+        private float mPostGain;
+
+        /**
+         * Class constructor for Limiter Stage
+         * @param inUse true if MBC stage will be used, false otherwise.
+         * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect
+         * is running
+         * @param linkGroup index of group assigned to this Limiter. Only limiters that share the
+         * same linkGroup index will react together.
+         * @param attackTime Attack Time for limiter compressor in milliseconds (ms)
+         * @param releaseTime Release Time for limiter compressor in milliseconds (ms)
+         * @param ratio Limiter Compressor ratio (1:N)
+         * @param threshold Limiter Compressor threshold measured in decibels (dB) from 0 dB Full
+         * Scale (dBFS).
+         * @param postGain Gain applied to the signal AFTER compression.
+         */
+        public Limiter(boolean inUse, boolean enabled, int linkGroup, float attackTime,
+                float releaseTime, float ratio, float threshold, float postGain) {
+            super(inUse, enabled);
+            mLinkGroup = linkGroup;
+            mAttackTime = attackTime;
+            mReleaseTime = releaseTime;
+            mRatio = ratio;
+            mThreshold = threshold;
+            mPostGain = postGain;
+        }
+
+        /**
+         * Class Constructor for Limiter
+         * @param cfg copy constructor
+         */
+        public Limiter(Limiter cfg) {
+            super(cfg.isInUse(), cfg.isEnabled());
+            mLinkGroup = cfg.mLinkGroup;
+            mAttackTime = cfg.mAttackTime;
+            mReleaseTime = cfg.mReleaseTime;
+            mRatio = cfg.mRatio;
+            mThreshold = cfg.mThreshold;
+            mPostGain = cfg.mPostGain;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            if (isInUse()) {
+                sb.append(String.format(" LinkGroup: %d (group)\n", mLinkGroup));
+                sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime));
+                sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime));
+                sb.append(String.format(" Ratio: 1:%f\n", mRatio));
+                sb.append(String.format(" Threshold: %f (dB)\n", mThreshold));
+                sb.append(String.format(" PostGain: %f (dB)\n", mPostGain));
+            }
+            return sb.toString();
+        }
+        /**
+         * Gets the linkGroup index for this Limiter Stage. Only limiters that share the same
+         * linkGroup index will react together.
+         * @return linkGroup index.
+         */
+        public int getLinkGroup() { return mLinkGroup; }
+        /**
+         * Sets the linkGroup index for this limiter Stage.
+         * @param linkGroup desired linkGroup index
+         */
+        public void setLinkGroup(int linkGroup) { mLinkGroup = linkGroup; }
+        /**
+         * gets attack time for limiter compressor in milliseconds (ms)
+         * @return attack time for limiter compressor in milliseconds (ms)
+         */
+        public float getAttackTime() { return mAttackTime; }
+        /**
+         * sets attack time for limiter compressor in milliseconds (ms)
+         * @param attackTime desired for limiter compressor in milliseconds (ms)
+         */
+        public void setAttackTime(float attackTime) { mAttackTime = attackTime; }
+        /**
+         * gets release time for limiter compressor in milliseconds (ms)
+         * @return release time for limiter compressor in milliseconds (ms)
+         */
+        public float getReleaseTime() { return mReleaseTime; }
+        /**
+         * sets release time for limiter compressor in milliseconds (ms)
+         * @param releaseTime desired for limiter compressor in milliseconds (ms)
+         */
+        public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
+        /**
+         * gets the limiter compressor ratio (1:N)
+         * @return limiter compressor ratio (1:N)
+         */
+        public float getRatio() { return mRatio; }
+        /**
+         * sets limiter compressor ratio (1:N)
+         * @param ratio desired for the limiter compressor (1:N)
+         */
+        public void setRatio(float ratio) { mRatio = ratio; }
+        /**
+         * gets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale
+         * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place.
+         * @return limiter compressor threshold in decibels (dB)
+         */
+        public float getThreshold() { return mThreshold; }
+        /**
+         * sets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale
+         * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place.
+         * @param threshold desired for limiter compressor in decibels(dB)
+         */
+        public void setThreshold(float threshold) { mThreshold = threshold; }
+        /**
+         * gets the gain applied to the signal AFTER limiting. Measured in decibels (dB) where 0
+         * dB means no level change
+         * @return postGain value in decibels (dB)
+         */
+        public float getPostGain() { return mPostGain; }
+        /**
+         * sets the gain to be applied to the siganl AFTER the limiter. Measured in decibels
+         * (dB), where 0 dB means no level change.
+         * @param postGain desired value in decibels (dB)
+         */
+        public void setPostGain(float postGain) { mPostGain = postGain; }
+    }
+
+    /**
+     * Class for Channel configuration parameters. It is composed of multiple stages, which can be
+     * used/enabled independently. Stages not used or disabled will be bypassed and the sound would
+     * be unaffected by them.
+     */
+    public final static class Channel {
+        private float   mInputGain;
+        private Eq      mPreEq;
+        private Mbc     mMbc;
+        private Eq      mPostEq;
+        private Limiter mLimiter;
+
+        /**
+         * Class constructor for Channel configuration.
+         * @param inputGain value in decibels (dB) of level change applied to the audio before
+         * processing. A value of 0 dB means no change.
+         * @param preEqInUse true if PreEq stage will be used, false otherwise. This can't be
+         * changed later.
+         * @param preEqBandCount number of bands for PreEq stage. This can't be changed later.
+         * @param mbcInUse true if Mbc stage will be used, false otherwise. This can't be changed
+         * later.
+         * @param mbcBandCount number of bands for Mbc stage. This can't be changed later.
+         * @param postEqInUse true if PostEq stage will be used, false otherwise. This can't be
+         * changed later.
+         * @param postEqBandCount number of bands for PostEq stage. This can't be changed later.
+         * @param limiterInUse true if Limiter stage will be used, false otherwise. This can't be
+         * changed later.
+         */
+        public Channel (float inputGain,
+                boolean preEqInUse, int preEqBandCount,
+                boolean mbcInUse, int mbcBandCount,
+                boolean postEqInUse, int postEqBandCount,
+                boolean limiterInUse) {
+            mInputGain = inputGain;
+            mPreEq = new Eq(preEqInUse, PREEQ_DEFAULT_ENABLED, preEqBandCount);
+            mMbc = new Mbc(mbcInUse, MBC_DEFAULT_ENABLED, mbcBandCount);
+            mPostEq = new Eq(postEqInUse, POSTEQ_DEFAULT_ENABLED,
+                    postEqBandCount);
+            mLimiter = new Limiter(limiterInUse,
+                    LIMITER_DEFAULT_ENABLED, LIMITER_DEFAULT_LINK_GROUP,
+                    LIMITER_DEFAULT_ATTACK_TIME, LIMITER_DEFAULT_RELEASE_TIME,
+                    LIMITER_DEFAULT_RATIO, LIMITER_DEFAULT_THRESHOLD, LIMITER_DEFAULT_POST_GAIN);
+        }
+
+        /**
+         * Class constructor for Channel configuration
+         * @param cfg copy constructor
+         */
+        public Channel(Channel cfg) {
+            mInputGain = cfg.mInputGain;
+            mPreEq = new Eq(cfg.mPreEq);
+            mMbc = new Mbc(cfg.mMbc);
+            mPostEq = new Eq(cfg.mPostEq);
+            mLimiter = new Limiter(cfg.mLimiter);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(String.format(" InputGain: %f\n", mInputGain));
+            sb.append("-->PreEq\n");
+            sb.append(mPreEq.toString());
+            sb.append("-->MBC\n");
+            sb.append(mMbc.toString());
+            sb.append("-->PostEq\n");
+            sb.append(mPostEq.toString());
+            sb.append("-->Limiter\n");
+            sb.append(mLimiter.toString());
+            return sb.toString();
+        }
+        /**
+         * Gets inputGain value in decibels (dB). 0 dB means no change;
+         * @return gain value in decibels (dB)
+         */
+        public float getInputGain() {
+            return mInputGain;
+        }
+        /**
+         * Sets inputGain value in decibels (dB). 0 dB means no change;
+         * @param inputGain desired gain value in decibels (dB)
+         */
+        public void setInputGain(float inputGain) {
+            mInputGain = inputGain;
+        }
+
+        /**
+         * Gets PreEq configuration stage
+         * @return PreEq configuration stage
+         */
+        public Eq getPreEq() {
+            return mPreEq;
+        }
+        /**
+         * Sets PreEq configuration stage. New PreEq stage must have the same number of bands than
+         * original PreEq stage.
+         * @param preEq configuration
+         */
+        public void setPreEq(Eq preEq) {
+            if (preEq.getBandCount() != mPreEq.getBandCount()) {
+                throw new IllegalArgumentException("PreEqBandCount changed from " +
+                        mPreEq.getBandCount() + " to " + preEq.getBandCount());
+            }
+            mPreEq = new Eq(preEq);
+        }
+        /**
+         * Gets EqBand for PreEq stage for given band index.
+         * @param band index of band of interest from PreEq stage
+         * @return EqBand configuration
+         */
+        public EqBand getPreEqBand(int band) {
+            return mPreEq.getBand(band);
+        }
+        /**
+         * Sets EqBand for PreEq stage for given band index
+         * @param band index of band of interest from PreEq stage
+         * @param preEqBand configuration to be set.
+         */
+        public void setPreEqBand(int band, EqBand preEqBand) {
+            mPreEq.setBand(band, preEqBand);
+        }
+
+        /**
+         * Gets Mbc configuration stage
+         * @return Mbc configuration stage
+         */
+        public Mbc getMbc() {
+            return mMbc;
+        }
+        /**
+         * Sets Mbc configuration stage. New Mbc stage must have the same number of bands than
+         * original Mbc stage.
+         * @param mbc
+         */
+        public void setMbc(Mbc mbc) {
+            if (mbc.getBandCount() != mMbc.getBandCount()) {
+                throw new IllegalArgumentException("MbcBandCount changed from " +
+                        mMbc.getBandCount() + " to " + mbc.getBandCount());
+            }
+            mMbc = new Mbc(mbc);
+        }
+        /**
+         * Gets MbcBand configuration for Mbc stage, for given band index.
+         * @param band index of band of interest from Mbc stage
+         * @return MbcBand configuration
+         */
+        public MbcBand getMbcBand(int band) {
+            return mMbc.getBand(band);
+        }
+        /**
+         * Sets MbcBand for Mbc stage for given band index
+         * @param band index of band of interest from Mbc Stage
+         * @param mbcBand configuration to be set
+         */
+        public void setMbcBand(int band, MbcBand mbcBand) {
+            mMbc.setBand(band, mbcBand);
+        }
+
+        /**
+         * Gets PostEq configuration stage
+         * @return PostEq configuration stage
+         */
+        public Eq getPostEq() {
+            return mPostEq;
+        }
+        /**
+         * Sets PostEq configuration stage. New PostEq stage must have the same number of bands than
+         * original PostEq stage.
+         * @param postEq configuration
+         */
+        public void setPostEq(Eq postEq) {
+            if (postEq.getBandCount() != mPostEq.getBandCount()) {
+                throw new IllegalArgumentException("PostEqBandCount changed from " +
+                        mPostEq.getBandCount() + " to " + postEq.getBandCount());
+            }
+            mPostEq = new Eq(postEq);
+        }
+        /**
+         * Gets EqBand for PostEq stage for given band index.
+         * @param band index of band of interest from PostEq stage
+         * @return EqBand configuration
+         */
+        public EqBand getPostEqBand(int band) {
+            return mPostEq.getBand(band);
+        }
+        /**
+         * Sets EqBand for PostEq stage for given band index
+         * @param band index of band of interest from PostEq stage
+         * @param postEqBand configuration to be set.
+         */
+        public void setPostEqBand(int band, EqBand postEqBand) {
+            mPostEq.setBand(band, postEqBand);
+        }
+
+        /**
+         * Gets Limiter configuration stage
+         * @return Limiter configuration stage
+         */
+        public Limiter getLimiter() {
+            return mLimiter;
+        }
+        /**
+         * Sets Limiter configuration stage.
+         * @param limiter configuration stage.
+         */
+        public void setLimiter(Limiter limiter) {
+            mLimiter = new Limiter(limiter);
+        }
+    }
+
+    /**
+     * Class for Config object, used by DynamicsProcessing to configure and update the audio effect.
+     * use Builder to instantiate objects of this type.
+     */
+    public final static class Config {
+        private final int mVariant;
+        private final int mChannelCount;
+        private final boolean mPreEqInUse;
+        private final int mPreEqBandCount;
+        private final boolean mMbcInUse;
+        private final int mMbcBandCount;
+        private final boolean mPostEqInUse;
+        private final int mPostEqBandCount;
+        private final boolean mLimiterInUse;
+        private final float mPreferredFrameDuration;
+        private final Channel[] mChannel;
+
+        /**
+         * @hide
+         * Class constructor for config. None of these parameters can be changed later.
+         * @param variant index of variant used for effect engine. See
+         * {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and {@link #VARIANT_FAVOR_TIME_RESOLUTION}.
+         * @param frameDurationMs preferred frame duration in milliseconds (ms).
+         * @param channelCount Number of channels to be configured.
+         * @param preEqInUse true if PreEq stage will be used, false otherwise.
+         * @param preEqBandCount number of bands for PreEq stage.
+         * @param mbcInUse true if Mbc stage will be used, false otherwise.
+         * @param mbcBandCount number of bands for Mbc stage.
+         * @param postEqInUse true if PostEq stage will be used, false otherwise.
+         * @param postEqBandCount number of bands for PostEq stage.
+         * @param limiterInUse true if Limiter stage will be used, false otherwise.
+         * @param channel array of Channel objects to be used for this configuration.
+         */
+        public Config(int variant, float frameDurationMs, int channelCount,
+                boolean preEqInUse, int preEqBandCount,
+                boolean mbcInUse, int mbcBandCount,
+                boolean postEqInUse, int postEqBandCount,
+                boolean limiterInUse,
+                Channel[] channel) {
+            mVariant = variant;
+            mPreferredFrameDuration = frameDurationMs;
+            mChannelCount = channelCount;
+            mPreEqInUse = preEqInUse;
+            mPreEqBandCount = preEqBandCount;
+            mMbcInUse = mbcInUse;
+            mMbcBandCount = mbcBandCount;
+            mPostEqInUse = postEqInUse;
+            mPostEqBandCount = postEqBandCount;
+            mLimiterInUse = limiterInUse;
+
+            mChannel = new Channel[mChannelCount];
+            //check if channelconfig is null or has less channels than channel count.
+            //options: fill the missing with default options.
+            // or fail?
+            for (int ch = 0; ch < mChannelCount; ch++) {
+                if (ch < channel.length) {
+                    mChannel[ch] = new Channel(channel[ch]); //copy create
+                } else {
+                    //create a new one from scratch? //fail?
+                }
+            }
+        }
+        //a version that will scale to necessary number of channels
+        /**
+         * @hide
+         * Class constructor for Configuration.
+         * @param channelCount limit configuration to this number of channels. if channelCount is
+         * greater than number of channels in cfg, the constructor will duplicate the last channel
+         * found as many times as necessary to create a Config with channelCount number of channels.
+         * If channelCount is less than channels in cfg, the extra channels in cfg will be ignored.
+         * @param cfg copy constructor paremter.
+         */
+        public Config(int channelCount, Config cfg) {
+            mVariant = cfg.mVariant;
+            mPreferredFrameDuration = cfg.mPreferredFrameDuration;
+            mChannelCount = cfg.mChannelCount;
+            mPreEqInUse = cfg.mPreEqInUse;
+            mPreEqBandCount = cfg.mPreEqBandCount;
+            mMbcInUse = cfg.mMbcInUse;
+            mMbcBandCount = cfg.mMbcBandCount;
+            mPostEqInUse = cfg.mPostEqInUse;
+            mPostEqBandCount = cfg.mPostEqBandCount;
+            mLimiterInUse = cfg.mLimiterInUse;
+
+            if (mChannelCount != cfg.mChannel.length) {
+                throw new IllegalArgumentException("configuration channel counts differ " +
+                        mChannelCount + " !=" + cfg.mChannel.length);
+            }
+            if (channelCount < 1) {
+                throw new IllegalArgumentException("channel resizing less than 1 not allowed");
+            }
+
+            mChannel = new Channel[channelCount];
+            for (int ch = 0; ch < channelCount; ch++) {
+                if (ch < mChannelCount) {
+                    mChannel[ch] = new Channel(cfg.mChannel[ch]);
+                } else {
+                    //duplicate last
+                    mChannel[ch] = new Channel(cfg.mChannel[mChannelCount-1]);
+                }
+            }
+        }
+
+        /**
+         * @hide
+         * Class constructor for Config
+         * @param cfg Configuration object copy constructor
+         */
+        public Config(@NonNull Config cfg) {
+            this(cfg.mChannelCount, cfg);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(String.format("Variant: %d\n", mVariant));
+            sb.append(String.format("PreferredFrameDuration: %f\n", mPreferredFrameDuration));
+            sb.append(String.format("ChannelCount: %d\n", mChannelCount));
+            sb.append(String.format("PreEq inUse: %b, bandCount:%d\n",mPreEqInUse,
+                    mPreEqBandCount));
+            sb.append(String.format("Mbc inUse: %b, bandCount: %d\n",mMbcInUse, mMbcBandCount));
+            sb.append(String.format("PostEq inUse: %b, bandCount: %d\n", mPostEqInUse,
+                    mPostEqBandCount));
+            sb.append(String.format("Limiter inUse: %b\n", mLimiterInUse));
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                sb.append(String.format("==Channel %d\n", ch));
+                sb.append(mChannel[ch].toString());
+            }
+            return sb.toString();
+        }
+        private void checkChannel(int channelIndex) {
+            if (channelIndex < 0 || channelIndex >= mChannel.length) {
+                throw new IllegalArgumentException("ChannelIndex out of bounds");
+            }
+        }
+
+        //getters and setters
+        /**
+         * Gets variant for effect engine See {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and
+         * {@link #VARIANT_FAVOR_TIME_RESOLUTION}.
+         * @return variant of effect engine
+         */
+        public int getVariant() {
+            return mVariant;
+        }
+        /**
+         * Gets preferred frame duration in milliseconds (ms).
+         * @return preferred frame duration in milliseconds (ms)
+         */
+        public float getPreferredFrameDuration() {
+            return mPreferredFrameDuration;
+        }
+        /**
+         * Gets if preEq stage is in use
+         * @return true if preEq stage is in use;
+         */
+        public boolean isPreEqInUse() {
+            return mPreEqInUse;
+        }
+        /**
+         * Gets number of bands configured for the PreEq stage.
+         * @return number of bands configured for the PreEq stage.
+         */
+        public int getPreEqBandCount() {
+            return mPreEqBandCount;
+        }
+        /**
+         * Gets if Mbc stage is in use
+         * @return true if Mbc stage is in use;
+         */
+        public boolean isMbcInUse() {
+            return mMbcInUse;
+        }
+        /**
+         * Gets number of bands configured for the Mbc stage.
+         * @return number of bands configured for the Mbc stage.
+         */
+        public int getMbcBandCount() {
+            return mMbcBandCount;
+        }
+        /**
+         * Gets if PostEq stage is in use
+         * @return true if PostEq stage is in use;
+         */
+        public boolean isPostEqInUse() {
+            return mPostEqInUse;
+        }
+        /**
+         * Gets number of bands configured for the PostEq stage.
+         * @return number of bands configured for the PostEq stage.
+         */
+        public int getPostEqBandCount() {
+            return mPostEqBandCount;
+        }
+        /**
+         * Gets if Limiter stage is in use
+         * @return true if Limiter stage is in use;
+         */
+        public boolean isLimiterInUse() {
+            return mLimiterInUse;
+        }
+
+        //channel
+        /**
+         * Gets the Channel configuration object by using the channel index
+         * @param channelIndex of desired Channel object
+         * @return Channel configuration object
+         */
+        public Channel getChannelByChannelIndex(int channelIndex) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex];
+        }
+
+        /**
+         * Sets the chosen Channel object in the selected channelIndex
+         * Note that all the stages should have the same number of bands than the existing Channel
+         * object.
+         * @param channelIndex index of channel to be replaced
+         * @param channel Channel configuration object to be set
+         */
+        public void setChannelTo(int channelIndex, Channel channel) {
+            checkChannel(channelIndex);
+            //check all things are compatible
+            if (mMbcBandCount != channel.getMbc().getBandCount()) {
+                throw new IllegalArgumentException("MbcBandCount changed from " +
+                        mMbcBandCount + " to " + channel.getPreEq().getBandCount());
+            }
+            if (mPreEqBandCount != channel.getPreEq().getBandCount()) {
+                throw new IllegalArgumentException("PreEqBandCount changed from " +
+                        mPreEqBandCount + " to " + channel.getPreEq().getBandCount());
+            }
+            if (mPostEqBandCount != channel.getPostEq().getBandCount()) {
+                throw new IllegalArgumentException("PostEqBandCount changed from " +
+                        mPostEqBandCount + " to " + channel.getPostEq().getBandCount());
+            }
+            mChannel[channelIndex] = new Channel(channel);
+        }
+
+        /**
+         * Sets ALL channels to the chosen Channel object. Note that all the stages should have the
+         * same number of bands than the existing ones.
+         * @param channel Channel configuration object to be set.
+         */
+        public void setAllChannelsTo(Channel channel) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                setChannelTo(ch, channel);
+            }
+        }
+
+        //===channel params
+        /**
+         * Gets inputGain value in decibels (dB) for channel indicated by channelIndex
+         * @param channelIndex index of channel of interest
+         * @return inputGain value in decibels (dB). 0 dB means no change.
+         */
+        public float getInputGainByChannelIndex(int channelIndex) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getInputGain();
+        }
+        /**
+         * Sets the inputGain value in decibels (dB) for the channel indicated by channelIndex.
+         * @param channelIndex index of channel of interest
+         * @param inputGain desired value in decibels (dB).
+         */
+        public void setInputGainByChannelIndex(int channelIndex, float inputGain) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setInputGain(inputGain);
+        }
+        /**
+         * Sets the inputGain value in decibels (dB) for ALL channels
+         * @param inputGain desired value in decibels (dB)
+         */
+        public void setInputGainAllChannelsTo(float inputGain) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setInputGain(inputGain);
+            }
+        }
+
+        //=== PreEQ
+        /**
+         * Gets PreEq stage from channel indicated by channelIndex
+         * @param channelIndex index of channel of interest
+         * @return PreEq stage configuration object
+         */
+        public Eq getPreEqByChannelIndex(int channelIndex) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getPreEq();
+        }
+        /**
+         * Sets the PreEq stage configuration for the channel indicated by channelIndex. Note that
+         * new preEq stage must have the same number of bands than original preEq stage
+         * @param channelIndex index of channel to be set
+         * @param preEq desired PreEq configuration to be set
+         */
+        public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setPreEq(preEq);
+        }
+        /**
+         * Sets the PreEq stage configuration for ALL channels. Note that new preEq stage must have
+         * the same number of bands than original preEq stages.
+         * @param preEq desired PreEq configuration to be set
+         */
+        public void setPreEqAllChannelsTo(Eq preEq) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setPreEq(preEq);
+            }
+        }
+        public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getPreEqBand(band);
+        }
+        public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setPreEqBand(band, preEqBand);
+        }
+        public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setPreEqBand(band, preEqBand);
+            }
+        }
+
+        //=== MBC
+        public Mbc getMbcByChannelIndex(int channelIndex) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getMbc();
+        }
+        public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setMbc(mbc);
+        }
+        public void setMbcAllChannelsTo(Mbc mbc) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setMbc(mbc);
+            }
+        }
+        public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getMbcBand(band);
+        }
+        public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setMbcBand(band, mbcBand);
+        }
+        public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setMbcBand(band, mbcBand);
+            }
+        }
+
+        //=== PostEQ
+        public Eq getPostEqByChannelIndex(int channelIndex) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getPostEq();
+        }
+        public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setPostEq(postEq);
+        }
+        public void setPostEqAllChannelsTo(Eq postEq) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setPostEq(postEq);
+            }
+        }
+        public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getPostEqBand(band);
+        }
+        public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setPostEqBand(band, postEqBand);
+        }
+        public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setPostEqBand(band, postEqBand);
+            }
+        }
+
+        //Limiter
+        public Limiter getLimiterByChannelIndex(int channelIndex) {
+            checkChannel(channelIndex);
+            return mChannel[channelIndex].getLimiter();
+        }
+        public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
+            checkChannel(channelIndex);
+            mChannel[channelIndex].setLimiter(limiter);
+        }
+        public void setLimiterAllChannelsTo(Limiter limiter) {
+            for (int ch = 0; ch < mChannel.length; ch++) {
+                mChannel[ch].setLimiter(limiter);
+            }
+        }
+
+        public final static class Builder {
+            private int mVariant;
+            private int mChannelCount;
+            private boolean mPreEqInUse;
+            private int mPreEqBandCount;
+            private boolean mMbcInUse;
+            private int mMbcBandCount;
+            private boolean mPostEqInUse;
+            private int mPostEqBandCount;
+            private boolean mLimiterInUse;
+            private float mPreferredFrameDuration = CONFIG_PREFERRED_FRAME_DURATION_MS;
+            private Channel[] mChannel;
+
+            public Builder(int variant, int channelCount,
+                    boolean preEqInUse, int preEqBandCount,
+                    boolean mbcInUse, int mbcBandCount,
+                    boolean postEqInUse, int postEqBandCount,
+                    boolean limiterInUse) {
+                mVariant = variant;
+                mChannelCount = channelCount;
+                mPreEqInUse = preEqInUse;
+                mPreEqBandCount = preEqBandCount;
+                mMbcInUse = mbcInUse;
+                mMbcBandCount = mbcBandCount;
+                mPostEqInUse = postEqInUse;
+                mPostEqBandCount = postEqBandCount;
+                mLimiterInUse = limiterInUse;
+                mChannel = new Channel[mChannelCount];
+                for (int ch = 0; ch < mChannelCount; ch++) {
+                    this.mChannel[ch] = new Channel(CHANNEL_DEFAULT_INPUT_GAIN,
+                            this.mPreEqInUse, this.mPreEqBandCount,
+                            this.mMbcInUse, this.mMbcBandCount,
+                            this.mPostEqInUse, this.mPostEqBandCount,
+                            this.mLimiterInUse);
+                }
+            }
+
+            private void checkChannel(int channelIndex) {
+                if (channelIndex < 0 || channelIndex >= mChannel.length) {
+                    throw new IllegalArgumentException("ChannelIndex out of bounds");
+                }
+            }
+
+            public Builder setPreferredFrameDuration(float frameDuration) {
+                if (frameDuration < 0) {
+                    throw new IllegalArgumentException("Expected positive frameDuration");
+                }
+                mPreferredFrameDuration = frameDuration;
+                return this;
+            }
+
+            public Builder setInputGainByChannelIndex(int channelIndex, float inputGain) {
+                checkChannel(channelIndex);
+                mChannel[channelIndex].setInputGain(inputGain);
+                return this;
+            }
+            public Builder setInputGainAllChannelsTo(float inputGain) {
+                for (int ch = 0; ch < mChannel.length; ch++) {
+                    mChannel[ch].setInputGain(inputGain);
+                }
+                return this;
+            }
+
+            public Builder setChannelTo(int channelIndex, Channel channel) {
+                checkChannel(channelIndex);
+                //check all things are compatible
+                if (mMbcBandCount != channel.getMbc().getBandCount()) {
+                    throw new IllegalArgumentException("MbcBandCount changed from " +
+                            mMbcBandCount + " to " + channel.getPreEq().getBandCount());
+                }
+                if (mPreEqBandCount != channel.getPreEq().getBandCount()) {
+                    throw new IllegalArgumentException("PreEqBandCount changed from " +
+                            mPreEqBandCount + " to " + channel.getPreEq().getBandCount());
+                }
+                if (mPostEqBandCount != channel.getPostEq().getBandCount()) {
+                    throw new IllegalArgumentException("PostEqBandCount changed from " +
+                            mPostEqBandCount + " to " + channel.getPostEq().getBandCount());
+                }
+                mChannel[channelIndex] = new Channel(channel);
+                return this;
+            }
+            public Builder setAllChannelsTo(Channel channel) {
+                for (int ch = 0; ch < mChannel.length; ch++) {
+                    setChannelTo(ch, channel);
+                }
+                return this;
+            }
+
+            public Builder setPreEqByChannelIndex(int channelIndex, Eq preEq) {
+                checkChannel(channelIndex);
+                mChannel[channelIndex].setPreEq(preEq);
+                return this;
+            }
+            public Builder setPreEqAllChannelsTo(Eq preEq) {
+                for (int ch = 0; ch < mChannel.length; ch++) {
+                    setPreEqByChannelIndex(ch, preEq);
+                }
+                return this;
+            }
+
+            public Builder setMbcByChannelIndex(int channelIndex, Mbc mbc) {
+                checkChannel(channelIndex);
+                mChannel[channelIndex].setMbc(mbc);
+                return this;
+            }
+            public Builder setMbcAllChannelsTo(Mbc mbc) {
+                for (int ch = 0; ch < mChannel.length; ch++) {
+                    setMbcByChannelIndex(ch, mbc);
+                }
+                return this;
+            }
+
+            public Builder setPostEqByChannelIndex(int channelIndex, Eq postEq) {
+                checkChannel(channelIndex);
+                mChannel[channelIndex].setPostEq(postEq);
+                return this;
+            }
+            public Builder setPostEqAllChannelsTo(Eq postEq) {
+                for (int ch = 0; ch < mChannel.length; ch++) {
+                    setPostEqByChannelIndex(ch, postEq);
+                }
+                return this;
+            }
+
+            public Builder setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
+                checkChannel(channelIndex);
+                mChannel[channelIndex].setLimiter(limiter);
+                return this;
+            }
+            public Builder setLimiterAllChannelsTo(Limiter limiter) {
+                for (int ch = 0; ch < mChannel.length; ch++) {
+                    setLimiterByChannelIndex(ch, limiter);
+                }
+                return this;
+            }
+
+            public Config build() {
+                return new Config(mVariant, mPreferredFrameDuration, mChannelCount,
+                        mPreEqInUse, mPreEqBandCount,
+                        mMbcInUse, mMbcBandCount,
+                        mPostEqInUse, mPostEqBandCount,
+                        mLimiterInUse, mChannel);
+            }
+        }
+    }
+    //=== CHANNEL
+    public Channel getChannelByChannelIndex(int channelIndex) {
+        return queryEngineByChannelIndex(channelIndex);
+    }
+
+    public void setChannelTo(int channelIndex, Channel channel) {
+        updateEngineChannelByChannelIndex(channelIndex, channel);
+    }
+
+    public void setAllChannelsTo(Channel channel) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setChannelTo(ch, channel);
+        }
+    }
+
+    //=== channel params
+    public float getInputGainByChannelIndex(int channelIndex) {
+        return getTwoFloat(PARAM_INPUT_GAIN, channelIndex);
+    }
+    public void setInputGainbyChannel(int channelIndex, float inputGain) {
+        setTwoFloat(PARAM_INPUT_GAIN, channelIndex, inputGain);
+    }
+    public void setInputGainAllChannelsTo(float inputGain) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setInputGainbyChannel(ch, inputGain);
+        }
+    }
+
+    //=== PreEQ
+    public Eq getPreEqByChannelIndex(int channelIndex) {
+        return queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex);
+    }
+    public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
+        updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq);
+    }
+    public void setPreEqAllChannelsTo(Eq preEq) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setPreEqByChannelIndex(ch, preEq);
+        }
+    }
+    public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
+        return queryEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band);
+    }
+    public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
+        updateEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band, preEqBand);
+    }
+    public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setPreEqBandByChannelIndex(ch, band, preEqBand);
+        }
+    }
+
+    //=== MBC
+    public Mbc getMbcByChannelIndex(int channelIndex) {
+        return queryEngineMbcByChannelIndex(channelIndex);
+    }
+    public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
+        updateEngineMbcByChannelIndex(channelIndex, mbc);
+    }
+    public void setMbcAllChannelsTo(Mbc mbc) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setMbcByChannelIndex(ch, mbc);
+        }
+    }
+    public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
+        return queryEngineMbcBandByChannelIndex(channelIndex, band);
+    }
+    public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
+        updateEngineMbcBandByChannelIndex(channelIndex, band, mbcBand);
+    }
+    public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setMbcBandByChannelIndex(ch, band, mbcBand);
+        }
+    }
+
+    //== PostEq
+    public Eq getPostEqByChannelIndex(int channelIndex) {
+        return queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex);
+    }
+    public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
+        updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq);
+    }
+    public void setPostEqAllChannelsTo(Eq postEq) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setPostEqByChannelIndex(ch, postEq);
+        }
+    }
+    public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
+        return queryEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band);
+    }
+    public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
+        updateEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band, postEqBand);
+    }
+    public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setPostEqBandByChannelIndex(ch, band, postEqBand);
+        }
+    }
+
+    //==== Limiter
+    public Limiter getLimiterByChannelIndex(int channelIndex) {
+        return queryEngineLimiterByChannelIndex(channelIndex);
+    }
+    public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
+        updateEngineLimiterByChannelIndex(channelIndex, limiter);
+    }
+    public void setLimiterAllChannelsTo(Limiter limiter) {
+        for (int ch = 0; ch < mChannelCount; ch++) {
+            setLimiterByChannelIndex(ch, limiter);
+        }
+    }
+
+    /**
+     * Gets the number of channels in the effect engine
+     * @return number of channels currently in use by the effect engine
+     */
+    public int getChannelCount() {
+        return getOneInt(PARAM_GET_CHANNEL_COUNT);
+    }
+
+    //=== Engine calls
+    private void setEngineArchitecture(int variant, float preferredFrameDuration,
+            boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount,
+            boolean postEqInUse, int postEqBandCount, boolean limiterInUse) {
+
+        Number[] params = { PARAM_ENGINE_ARCHITECTURE };
+        Number[] values = { variant /* variant */,
+                preferredFrameDuration,
+                (preEqInUse ? 1 : 0),
+                preEqBandCount,
+                (mbcInUse ? 1 : 0),
+                mbcBandCount,
+                (postEqInUse ? 1 : 0),
+                postEqBandCount,
+                (limiterInUse ? 1 : 0)};
+        setNumberArray(params, values);
+    }
+
+    private void updateEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex,
+            @NonNull EqBand eqBand) {
+        Number[] params = {param,
+                channelIndex,
+                bandIndex};
+        Number[] values = {(eqBand.isEnabled() ? 1 : 0),
+                eqBand.getCutoffFrequency(),
+                eqBand.getGain()};
+        setNumberArray(params, values);
+    }
+    private Eq queryEngineEqByChannelIndex(int param, int channelIndex) {
+
+        Number[] params = {param == PARAM_PRE_EQ ? PARAM_PRE_EQ : PARAM_POST_EQ,
+                channelIndex};
+        Number[] values = {0 /*0 in use */,
+                            0 /*1 enabled*/,
+                            0 /*2 band count */};
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
+        getParameter(paramBytes, valueBytes);
+        byteArrayToNumberArray(valueBytes, values);
+        int bandCount = values[2].intValue();
+        Eq eq = new Eq(values[0].intValue() > 0 /* in use */,
+                values[1].intValue() > 0 /* enabled */,
+                bandCount /*band count*/);
+        for (int b = 0; b < bandCount; b++) {
+            EqBand eqBand = queryEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ?
+                    PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b);
+            eq.setBand(b, eqBand);
+        }
+        return eq;
+    }
+    private EqBand queryEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex) {
+        Number[] params = {param,
+                channelIndex,
+                bandIndex};
+        Number[] values = {0 /*0 enabled*/,
+                            0.0f /*1 cutoffFrequency */,
+                            0.0f /*2 gain */};
+
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
+        getParameter(paramBytes, valueBytes);
+
+        byteArrayToNumberArray(valueBytes, values);
+
+        return new EqBand(values[0].intValue() > 0 /* enabled */,
+                values[1].floatValue() /* cutoffFrequency */,
+                values[2].floatValue() /* gain*/);
+    }
+    private void updateEngineEqByChannelIndex(int param, int channelIndex, @NonNull Eq eq) {
+        int bandCount = eq.getBandCount();
+        Number[] params = {param,
+                channelIndex};
+        Number[] values = { (eq.isInUse() ? 1 : 0),
+                (eq.isEnabled() ? 1 : 0),
+                bandCount};
+        setNumberArray(params, values);
+        for (int b = 0; b < bandCount; b++) {
+            EqBand eqBand = eq.getBand(b);
+            updateEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ?
+                    PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b, eqBand);
+        }
+    }
+
+    private Mbc queryEngineMbcByChannelIndex(int channelIndex) {
+        Number[] params = {PARAM_MBC,
+                channelIndex};
+        Number[] values = {0 /*0 in use */,
+                            0 /*1 enabled*/,
+                            0 /*2 band count */};
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
+        getParameter(paramBytes, valueBytes);
+        byteArrayToNumberArray(valueBytes, values);
+        int bandCount = values[2].intValue();
+        Mbc mbc = new Mbc(values[0].intValue() > 0 /* in use */,
+                values[1].intValue() > 0 /* enabled */,
+                bandCount /*band count*/);
+        for (int b = 0; b < bandCount; b++) {
+            MbcBand mbcBand = queryEngineMbcBandByChannelIndex(channelIndex, b);
+            mbc.setBand(b, mbcBand);
+        }
+        return mbc;
+    }
+    private MbcBand queryEngineMbcBandByChannelIndex(int channelIndex, int bandIndex) {
+        Number[] params = {PARAM_MBC_BAND,
+                channelIndex,
+                bandIndex};
+        Number[] values = {0 /*0 enabled */,
+                0.0f /*1 cutoffFrequency */,
+                0.0f /*2 AttackTime */,
+                0.0f /*3 ReleaseTime */,
+                0.0f /*4 Ratio */,
+                0.0f /*5 Threshold */,
+                0.0f /*6 KneeWidth */,
+                0.0f /*7 NoiseGateThreshold */,
+                0.0f /*8 ExpanderRatio */,
+                0.0f /*9 PreGain */,
+                0.0f /*10 PostGain*/};
+
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
+        getParameter(paramBytes, valueBytes);
+
+        byteArrayToNumberArray(valueBytes, values);
+
+        return new MbcBand(values[0].intValue() > 0 /* enabled */,
+                values[1].floatValue() /* cutoffFrequency */,
+                values[2].floatValue()/*2 AttackTime */,
+                values[3].floatValue()/*3 ReleaseTime */,
+                values[4].floatValue()/*4 Ratio */,
+                values[5].floatValue()/*5 Threshold */,
+                values[6].floatValue()/*6 KneeWidth */,
+                values[7].floatValue()/*7 NoiseGateThreshold */,
+                values[8].floatValue()/*8 ExpanderRatio */,
+                values[9].floatValue()/*9 PreGain */,
+                values[10].floatValue()/*10 PostGain*/);
+    }
+    private void updateEngineMbcBandByChannelIndex(int channelIndex, int bandIndex,
+            @NonNull MbcBand mbcBand) {
+        Number[] params = { PARAM_MBC_BAND,
+                channelIndex,
+                bandIndex};
+        Number[] values = {(mbcBand.isEnabled() ? 1 : 0),
+                mbcBand.getCutoffFrequency(),
+                mbcBand.getAttackTime(),
+                mbcBand.getReleaseTime(),
+                mbcBand.getRatio(),
+                mbcBand.getThreshold(),
+                mbcBand.getKneeWidth(),
+                mbcBand.getNoiseGateThreshold(),
+                mbcBand.getExpanderRatio(),
+                mbcBand.getPreGain(),
+                mbcBand.getPostGain()};
+        setNumberArray(params, values);
+    }
+
+    private void updateEngineMbcByChannelIndex(int channelIndex, @NonNull Mbc mbc) {
+        int bandCount = mbc.getBandCount();
+        Number[] params = { PARAM_MBC,
+                channelIndex};
+        Number[] values = {(mbc.isInUse() ? 1 : 0),
+                (mbc.isEnabled() ? 1 : 0),
+                bandCount};
+        setNumberArray(params, values);
+        for (int b = 0; b < bandCount; b++) {
+            MbcBand mbcBand = mbc.getBand(b);
+            updateEngineMbcBandByChannelIndex(channelIndex, b, mbcBand);
+        }
+    }
+
+    private void updateEngineLimiterByChannelIndex(int channelIndex, @NonNull Limiter limiter) {
+        Number[] params = { PARAM_LIMITER,
+                channelIndex};
+        Number[] values = {(limiter.isInUse() ? 1 : 0),
+                (limiter.isEnabled() ? 1 : 0),
+                limiter.getLinkGroup(),
+                limiter.getAttackTime(),
+                limiter.getReleaseTime(),
+                limiter.getRatio(),
+                limiter.getThreshold(),
+                limiter.getPostGain()};
+        setNumberArray(params, values);
+    }
+
+    private Limiter queryEngineLimiterByChannelIndex(int channelIndex) {
+        Number[] params = {PARAM_LIMITER,
+                channelIndex};
+        Number[] values = {0 /*0 in use (int)*/,
+                0 /*1 enabled (int)*/,
+                0 /*2 link group (int)*/,
+                0.0f /*3 attack time (float)*/,
+                0.0f /*4 release time (float)*/,
+                0.0f /*5 ratio (float)*/,
+                0.0f /*6 threshold (float)*/,
+                0.0f /*7 post gain(float)*/};
+
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
+        getParameter(paramBytes, valueBytes);
+        byteArrayToNumberArray(valueBytes, values);
+
+        return new Limiter(values[0].intValue() > 0 /*in use*/,
+                values[1].intValue() > 0 /*enabled*/,
+                values[2].intValue() /*linkGroup*/,
+                values[3].floatValue() /*attackTime*/,
+                values[4].floatValue() /*releaseTime*/,
+                values[5].floatValue() /*ratio*/,
+                values[6].floatValue() /*threshold*/,
+                values[7].floatValue() /*postGain*/);
+    }
+
+    private Channel queryEngineByChannelIndex(int channelIndex) {
+        float inputGain = getTwoFloat(PARAM_INPUT_GAIN, channelIndex);
+        Eq preEq = queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex);
+        Mbc mbc = queryEngineMbcByChannelIndex(channelIndex);
+        Eq postEq = queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex);
+        Limiter limiter = queryEngineLimiterByChannelIndex(channelIndex);
+
+        Channel channel = new Channel(inputGain,
+                preEq.isInUse(), preEq.getBandCount(),
+                mbc.isInUse(), mbc.getBandCount(),
+                postEq.isInUse(), postEq.getBandCount(),
+                limiter.isInUse());
+        channel.setInputGain(inputGain);
+        channel.setPreEq(preEq);
+        channel.setMbc(mbc);
+        channel.setPostEq(postEq);
+        channel.setLimiter(limiter);
+        return channel;
+    }
+
+    private void updateEngineChannelByChannelIndex(int channelIndex, @NonNull Channel channel) {
+        //send things with as few calls as possible
+        setTwoFloat(PARAM_INPUT_GAIN, channelIndex, channel.getInputGain());
+        Eq preEq = channel.getPreEq();
+        updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq);
+        Mbc mbc = channel.getMbc();
+        updateEngineMbcByChannelIndex(channelIndex, mbc);
+        Eq postEq = channel.getPostEq();
+        updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq);
+        Limiter limiter = channel.getLimiter();
+        updateEngineLimiterByChannelIndex(channelIndex, limiter);
+    }
+
+    //****** convenience methods:
+    //
+    private int getOneInt(int param) {
+        final int[] params = { param };
+        final int[] result = new int[1];
+
+        checkStatus(getParameter(params, result));
+        return result[0];
+    }
+
+    private void setTwoFloat(int param, int paramA, float valueSet) {
+        final int[] params = { param, paramA };
+        final byte[] value;
+
+        value = floatToByteArray(valueSet);
+        checkStatus(setParameter(params, value));
+    }
+
+    private byte[] numberArrayToByteArray(Number[] values) {
+        int expectedBytes = 0;
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] instanceof Integer) {
+                expectedBytes += Integer.BYTES;
+            } else if (values[i] instanceof Float) {
+                expectedBytes += Float.BYTES;
+            } else {
+                throw new IllegalArgumentException("unknown value type " +
+                        values[i].getClass());
+            }
+        }
+        ByteBuffer converter = ByteBuffer.allocate(expectedBytes);
+        converter.order(ByteOrder.nativeOrder());
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] instanceof Integer) {
+                converter.putInt(values[i].intValue());
+            } else if (values[i] instanceof Float) {
+                converter.putFloat(values[i].floatValue());
+            }
+        }
+        return converter.array();
+    }
+
+    private void byteArrayToNumberArray(byte[] valuesIn, Number[] valuesOut) {
+        int inIndex = 0;
+        int outIndex = 0;
+        while (inIndex < valuesIn.length && outIndex < valuesOut.length) {
+            if (valuesOut[outIndex] instanceof Integer) {
+                valuesOut[outIndex++] = byteArrayToInt(valuesIn, inIndex);
+                inIndex += Integer.BYTES;
+            } else if (valuesOut[outIndex] instanceof Float) {
+                valuesOut[outIndex++] = byteArrayToFloat(valuesIn, inIndex);
+                inIndex += Float.BYTES;
+            } else {
+                throw new IllegalArgumentException("can't convert " +
+                        valuesOut[outIndex].getClass());
+            }
+        }
+        if (outIndex != valuesOut.length) {
+            throw new IllegalArgumentException("only converted " + outIndex +
+                    " values out of "+ valuesOut.length + " expected");
+        }
+    }
+
+    private void setNumberArray(Number[] params, Number[] values) {
+        byte[] paramBytes = numberArrayToByteArray(params);
+        byte[] valueBytes = numberArrayToByteArray(values);
+        checkStatus(setParameter(paramBytes, valueBytes));
+    }
+
+    private float getTwoFloat(int param, int paramA) {
+        final int[] params = { param, paramA };
+        final byte[] result = new byte[4];
+
+        checkStatus(getParameter(params, result));
+        return byteArrayToFloat(result);
+    }
+
+    /**
+     * @hide
+     * The OnParameterChangeListener interface defines a method called by the DynamicsProcessing
+     * when a parameter value has changed.
+     */
+    public interface OnParameterChangeListener {
+        /**
+         * Method called when a parameter value has changed. The method is called only if the
+         * parameter was changed by another application having the control of the same
+         * DynamicsProcessing engine.
+         * @param effect the DynamicsProcessing on which the interface is registered.
+         * @param param ID of the modified parameter. See {@link #PARAM_GENERIC_PARAM1} ...
+         * @param value the new parameter value.
+         */
+        void onParameterChange(DynamicsProcessing effect, int param, int value);
+    }
+
+    /**
+     * helper method to update effect architecture parameters
+     */
+    private void updateEffectArchitecture() {
+        mChannelCount = getChannelCount();
+    }
+
+    /**
+     * Listener used internally to receive unformatted parameter change events from AudioEffect
+     * super class.
+     */
+    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
+        private BaseParameterListener() {
+
+        }
+        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+            // only notify when the parameter was successfully change
+            if (status != AudioEffect.SUCCESS) {
+                return;
+            }
+            OnParameterChangeListener l = null;
+            synchronized (mParamListenerLock) {
+                if (mParamListener != null) {
+                    l = mParamListener;
+                }
+            }
+            if (l != null) {
+                int p = -1;
+                int v = Integer.MIN_VALUE;
+
+                if (param.length == 4) {
+                    p = byteArrayToInt(param, 0);
+                }
+                if (value.length == 4) {
+                    v = byteArrayToInt(value, 0);
+                }
+                if (p != -1 && v != Integer.MIN_VALUE) {
+                    l.onParameterChange(DynamicsProcessing.this, p, v);
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Registers an OnParameterChangeListener interface.
+     * @param listener OnParameterChangeListener interface registered
+     */
+    public void setParameterListener(OnParameterChangeListener listener) {
+        synchronized (mParamListenerLock) {
+            if (mParamListener == null) {
+                mBaseParamListener = new BaseParameterListener();
+                super.setParameterListener(mBaseParamListener);
+            }
+            mParamListener = listener;
+        }
+    }
+
+    /**
+     * @hide
+     * The Settings class regroups the DynamicsProcessing parameters. It is used in
+     * conjunction with the getProperties() and setProperties() methods to backup and restore
+     * all parameters in a single call.
+     */
+
+    public static class Settings {
+        public int channelCount;
+        public float[] inputGain;
+
+        public Settings() {
+        }
+
+        /**
+         * Settings class constructor from a key=value; pairs formatted string. The string is
+         * typically returned by Settings.toString() method.
+         * @throws IllegalArgumentException if the string is not correctly formatted.
+         */
+        public Settings(String settings) {
+            StringTokenizer st = new StringTokenizer(settings, "=;");
+            //int tokens = st.countTokens();
+            if (st.countTokens() != 3) {
+                throw new IllegalArgumentException("settings: " + settings);
+            }
+            String key = st.nextToken();
+            if (!key.equals("DynamicsProcessing")) {
+                throw new IllegalArgumentException(
+                        "invalid settings for DynamicsProcessing: " + key);
+            }
+            try {
+                key = st.nextToken();
+                if (!key.equals("channelCount")) {
+                    throw new IllegalArgumentException("invalid key name: " + key);
+                }
+                channelCount = Short.parseShort(st.nextToken());
+                if (channelCount > CHANNEL_COUNT_MAX) {
+                    throw new IllegalArgumentException("too many channels Settings:" + settings);
+                }
+                if (st.countTokens() != channelCount*1) { //check expected parameters.
+                    throw new IllegalArgumentException("settings: " + settings);
+                }
+                //check to see it is ok the size
+                inputGain = new float[channelCount];
+                for (int ch = 0; ch < channelCount; ch++) {
+                    key = st.nextToken();
+                    if (!key.equals(ch +"_inputGain")) {
+                        throw new IllegalArgumentException("invalid key name: " + key);
+                    }
+                    inputGain[ch] = Float.parseFloat(st.nextToken());
+                }
+             } catch (NumberFormatException nfe) {
+                throw new IllegalArgumentException("invalid value for key: " + key);
+            }
+        }
+
+        @Override
+        public String toString() {
+            String str = new String (
+                    "DynamicsProcessing"+
+                    ";channelCount="+Integer.toString(channelCount));
+                    for (int ch = 0; ch < channelCount; ch++) {
+                        str = str.concat(";"+ch+"_inputGain="+Float.toString(inputGain[ch]));
+                    }
+            return str;
+        }
+    };
+
+
+    /**
+     * @hide
+     * Gets the DynamicsProcessing properties. This method is useful when a snapshot of current
+     * effect settings must be saved by the application.
+     * @return a DynamicsProcessing.Settings object containing all current parameters values
+     */
+    public DynamicsProcessing.Settings getProperties() {
+        Settings settings = new Settings();
+
+        //TODO: just for testing, we are calling the getters one by one, this is
+        // supposed to be done in a single (or few calls) and get all the parameters at once.
+
+        settings.channelCount = getChannelCount();
+
+        if (settings.channelCount > CHANNEL_COUNT_MAX) {
+            throw new IllegalArgumentException("too many channels Settings:" + settings);
+        }
+
+        { // get inputGainmB per channel
+            settings.inputGain = new float [settings.channelCount];
+            for (int ch = 0; ch < settings.channelCount; ch++) {
+//TODO:with config                settings.inputGain[ch] = getInputGain(ch);
+            }
+        }
+        return settings;
+    }
+
+    /**
+     * @hide
+     * Sets the DynamicsProcessing properties. This method is useful when bass boost settings
+     * have to be applied from a previous backup.
+     * @param settings a DynamicsProcessing.Settings object containing the properties to apply
+     */
+    public void setProperties(DynamicsProcessing.Settings settings) {
+
+        if (settings.channelCount != settings.inputGain.length ||
+                settings.channelCount != mChannelCount) {
+                throw new IllegalArgumentException("settings invalid channel count: "
+                + settings.channelCount);
+            }
+
+        //TODO: for now calling multiple times.
+        for (int ch = 0; ch < mChannelCount; ch++) {
+//TODO: use config            setInputGain(ch, settings.inputGain[ch]);
+        }
+    }
+}
diff --git a/android/media/audiofx/Visualizer.java b/android/media/audiofx/Visualizer.java
index 0fe7246..f2b4fe0 100644
--- a/android/media/audiofx/Visualizer.java
+++ b/android/media/audiofx/Visualizer.java
@@ -546,22 +546,39 @@
         /**
          * Method called when a new waveform capture is available.
          * <p>Data in the waveform buffer is valid only within the scope of the callback.
-         * Applications which needs access to the waveform data after returning from the callback
+         * Applications which need access to the waveform data after returning from the callback
          * should make a copy of the data instead of holding a reference.
          * @param visualizer Visualizer object on which the listener is registered.
          * @param waveform array of bytes containing the waveform representation.
-         * @param samplingRate sampling rate of the audio visualized.
+         * @param samplingRate sampling rate of the visualized audio.
          */
         void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
 
         /**
          * Method called when a new frequency capture is available.
          * <p>Data in the fft buffer is valid only within the scope of the callback.
-         * Applications which needs access to the fft data after returning from the callback
+         * Applications which need access to the fft data after returning from the callback
          * should make a copy of the data instead of holding a reference.
+         *
+         * <p>In order to obtain magnitude and phase values the following formulas can
+         * be used:
+         *    <pre class="prettyprint">
+         *       for (int i = 0; i &lt; fft.size(); i += 2) {
+         *           float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
+         *           float phase = (float)Math.atan2(fft[i + 1], fft[i]);
+         *       }</pre>
          * @param visualizer Visualizer object on which the listener is registered.
          * @param fft array of bytes containing the frequency representation.
-         * @param samplingRate sampling rate of the audio visualized.
+         *    The fft array only contains the first half of the actual
+         *    FFT spectrum (frequencies up to Nyquist frequency), exploiting
+         *    the symmetry of the spectrum. For each frequencies bin <code>i</code>:
+         *    <ul>
+         *      <li>the element at index <code>2*i</code> in the array contains
+         *          the real part of a complex number,</li>
+         *      <li>the element at index <code>2*i+1</code> contains the imaginary
+         *          part of the complex number.</li>
+         *    </ul>
+         * @param samplingRate sampling rate of the visualized audio.
          */
         void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
     }
diff --git a/android/media/audiopolicy/AudioMix.java b/android/media/audiopolicy/AudioMix.java
index adeb834..fca0cc7 100644
--- a/android/media/audiopolicy/AudioMix.java
+++ b/android/media/audiopolicy/AudioMix.java
@@ -162,6 +162,24 @@
     }
 
     /** @hide */
+    public boolean isAffectingUsage(int usage) {
+        return mRule.isAffectingUsage(usage);
+    }
+
+    /** @hide */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final AudioMix that = (AudioMix) o;
+        return (this.mRouteFlags == that.mRouteFlags)
+                && (this.mRule == that.mRule)
+                && (this.mMixType == that.mMixType)
+                && (this.mFormat == that.mFormat);
+    }
+
+    /** @hide */
     @Override
     public int hashCode() {
         return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
diff --git a/android/media/audiopolicy/AudioMixingRule.java b/android/media/audiopolicy/AudioMixingRule.java
index 5f12742..749a45e 100644
--- a/android/media/audiopolicy/AudioMixingRule.java
+++ b/android/media/audiopolicy/AudioMixingRule.java
@@ -135,11 +135,42 @@
         }
     }
 
+    boolean isAffectingUsage(int usage) {
+        for (AudioMixMatchCriterion criterion : mCriteria) {
+            if ((criterion.mRule & RULE_MATCH_ATTRIBUTE_USAGE) != 0
+                    && criterion.mAttr != null
+                    && criterion.mAttr.getUsage() == usage) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1,
+            ArrayList<AudioMixMatchCriterion> cr2) {
+        if (cr1 == null || cr2 == null) return false;
+        if (cr1 == cr2) return true;
+        if (cr1.size() != cr2.size()) return false;
+        //TODO iterate over rules to check they contain the same criterion
+        return (cr1.hashCode() == cr2.hashCode());
+    }
+
     private final int mTargetMixType;
     int getTargetMixType() { return mTargetMixType; }
     private final ArrayList<AudioMixMatchCriterion> mCriteria;
     ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
 
+    /** @hide */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        final AudioMixingRule that = (AudioMixingRule) o;
+        return (this.mTargetMixType == that.mTargetMixType)
+                && (areCriteriaEquivalent(this.mCriteria, that.mCriteria));
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mTargetMixType, mCriteria);
diff --git a/android/media/audiopolicy/AudioPolicy.java b/android/media/audiopolicy/AudioPolicy.java
index 7e88c27..11107e2 100644
--- a/android/media/audiopolicy/AudioPolicy.java
+++ b/android/media/audiopolicy/AudioPolicy.java
@@ -42,6 +42,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @hide
@@ -89,6 +90,8 @@
 
     private AudioPolicyFocusListener mFocusListener;
 
+    private final AudioPolicyVolumeCallback mVolCb;
+
     private Context mContext;
 
     private AudioPolicyConfig mConfig;
@@ -99,12 +102,15 @@
     public boolean hasFocusListener() { return mFocusListener != null; }
     /** @hide */
     public boolean isFocusPolicy() { return mIsFocusPolicy; }
+    /** @hide */
+    public boolean isVolumeController() { return mVolCb != null; }
 
     /**
      * The parameter is guaranteed non-null through the Builder
      */
     private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
-            AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) {
+            AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy,
+            AudioPolicyVolumeCallback vc) {
         mConfig = config;
         mStatus = POLICY_STATUS_UNREGISTERED;
         mContext = context;
@@ -120,6 +126,7 @@
         mFocusListener = fl;
         mStatusListener = sl;
         mIsFocusPolicy = isFocusPolicy;
+        mVolCb = vc;
     }
 
     /**
@@ -134,6 +141,7 @@
         private AudioPolicyFocusListener mFocusListener;
         private AudioPolicyStatusListener mStatusListener;
         private boolean mIsFocusPolicy = false;
+        private AudioPolicyVolumeCallback mVolCb;
 
         /**
          * Constructs a new Builder with no audio mixes.
@@ -208,6 +216,22 @@
             mStatusListener = l;
         }
 
+        @SystemApi
+        /**
+         * Sets the callback to receive all volume key-related events.
+         * The callback will only be called if the device is configured to handle volume events
+         * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager)
+         * @param vc
+         * @return the same Builder instance.
+         */
+        public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) {
+            if (vc == null) {
+                throw new IllegalArgumentException("Invalid null volume callback");
+            }
+            mVolCb = vc;
+            return this;
+        }
+
         /**
          * Combines all of the attributes that have been set on this {@code Builder} and returns a
          * new {@link AudioPolicy} object.
@@ -229,7 +253,90 @@
                         + "an AudioPolicyFocusListener");
             }
             return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
-                    mFocusListener, mStatusListener, mIsFocusPolicy);
+                    mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb);
+        }
+    }
+
+    /**
+     * @hide
+     * Update the current configuration of the set of audio mixes by adding new ones, while
+     * keeping the policy registered.
+     * This method can only be called on a registered policy.
+     * @param mixes the list of {@link AudioMix} to add
+     * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+     *    otherwise.
+     */
+    @SystemApi
+    public int attachMixes(@NonNull List<AudioMix> mixes) {
+        if (mixes == null) {
+            throw new IllegalArgumentException("Illegal null list of AudioMix");
+        }
+        synchronized (mLock) {
+            if (mStatus != POLICY_STATUS_REGISTERED) {
+                throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
+            }
+            final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
+            for (AudioMix mix : mixes) {
+                if (mix == null) {
+                    throw new IllegalArgumentException("Illegal null AudioMix in attachMixes");
+                } else {
+                    zeMixes.add(mix);
+                }
+            }
+            final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
+            IAudioService service = getService();
+            try {
+                final int status = service.addMixForPolicy(cfg, this.cb());
+                if (status == AudioManager.SUCCESS) {
+                    mConfig.add(zeMixes);
+                }
+                return status;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in attachMixes", e);
+                return AudioManager.ERROR;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Update the current configuration of the set of audio mixes by removing some, while
+     * keeping the policy registered.
+     * This method can only be called on a registered policy.
+     * @param mixes the list of {@link AudioMix} to remove
+     * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
+     *    otherwise.
+     */
+    @SystemApi
+    public int detachMixes(@NonNull List<AudioMix> mixes) {
+        if (mixes == null) {
+            throw new IllegalArgumentException("Illegal null list of AudioMix");
+        }
+        synchronized (mLock) {
+            if (mStatus != POLICY_STATUS_REGISTERED) {
+                throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
+            }
+            final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
+            for (AudioMix mix : mixes) {
+                if (mix == null) {
+                    throw new IllegalArgumentException("Illegal null AudioMix in detachMixes");
+                    // TODO also check mix is currently contained in list of mixes
+                } else {
+                    zeMixes.add(mix);
+                }
+            }
+            final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
+            IAudioService service = getService();
+            try {
+                final int status = service.removeMixForPolicy(cfg, this.cb());
+                if (status == AudioManager.SUCCESS) {
+                    mConfig.remove(zeMixes);
+                }
+                return status;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in detachMixes", e);
+                return AudioManager.ERROR;
+            }
         }
     }
 
@@ -377,6 +484,7 @@
                 new AudioAttributes.Builder()
                         .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
                         .addTag(addressForTag(mix))
+                        .addTag(AudioRecord.SUBMIX_FIXED_VOLUME)
                         .build(),
                 mixFormat,
                 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
@@ -440,9 +548,9 @@
          * Only ever called if the {@link AudioPolicy} was built with
          * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
          * @param afi information about the focus request and the requester
-         * @param requestResult the result that was returned synchronously by the framework to the
-         *     application, {@link #AUDIOFOCUS_REQUEST_FAILED},or
-         *     {@link #AUDIOFOCUS_REQUEST_DELAYED}.
+         * @param requestResult deprecated after the addition of
+         *     {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)}
+         *     in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}.
          */
         public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
         /**
@@ -455,6 +563,23 @@
         public void onAudioFocusAbandon(AudioFocusInfo afi) {}
     }
 
+    @SystemApi
+    /**
+     * Callback class to receive volume change-related events.
+     * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
+     * {@link AudioPolicy} to receive those events.
+     *
+     */
+    public static abstract class AudioPolicyVolumeCallback {
+        /** @hide */
+        public AudioPolicyVolumeCallback() {}
+        /**
+         * Called when volume key-related changes are triggered, on the key down event.
+         * @param adjustment the type of volume adjustment for the key.
+         */
+        public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
+    }
+
     private void onPolicyStatusChange() {
         AudioPolicyStatusListener l;
         synchronized (mLock) {
@@ -494,7 +619,7 @@
             sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
             if (DEBUG) {
                 Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
-                        + afi.getClientId() + "reqRes=" + requestResult);
+                        + afi.getClientId() + " gen=" + afi.getGen());
             }
         }
 
@@ -517,6 +642,13 @@
                 }
             }
         }
+
+        public void notifyVolumeAdjust(int adjustment) {
+            sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
+            if (DEBUG) {
+                Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
+            }
+        }
     };
 
     //==================================================
@@ -528,6 +660,7 @@
     private final static int MSG_MIX_STATE_UPDATE = 3;
     private final static int MSG_FOCUS_REQUEST = 4;
     private final static int MSG_FOCUS_ABANDON = 5;
+    private final static int MSG_VOL_ADJUST = 6;
 
     private class EventHandler extends Handler {
         public EventHandler(AudioPolicy ap, Looper looper) {
@@ -571,6 +704,13 @@
                         Log.e(TAG, "Invalid null focus listener for focus abandon event");
                     }
                     break;
+                case MSG_VOL_ADJUST:
+                    if (mVolCb != null) {
+                        mVolCb.onVolumeAdjustment(msg.arg1);
+                    } else { // should never be null, but don't crash
+                        Log.e(TAG, "Invalid null volume event");
+                    }
+                    break;
                 default:
                     Log.e(TAG, "Unknown event " + msg.what);
             }
diff --git a/android/media/audiopolicy/AudioPolicyConfig.java b/android/media/audiopolicy/AudioPolicyConfig.java
index cafa5a8..f725cac 100644
--- a/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/android/media/audiopolicy/AudioPolicyConfig.java
@@ -16,6 +16,7 @@
 
 package android.media.audiopolicy;
 
+import android.annotation.NonNull;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioPatch;
@@ -24,6 +25,8 @@
 import android.os.Parcelable;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -35,11 +38,16 @@
 
     private static final String TAG = "AudioPolicyConfig";
 
-    protected ArrayList<AudioMix> mMixes;
+    protected final ArrayList<AudioMix> mMixes;
     protected int mDuckingPolicy = AudioPolicy.FOCUS_POLICY_DUCKING_IN_APP;
 
     private String mRegistrationId = null;
 
+    /** counter for the mixes that are / have been in the list of AudioMix
+     *  e.g. register 4 mixes (counter is 3), remove 1 (counter is 3), add 1 (counter is 4)
+     */
+    private int mMixCounter = 0;
+
     protected AudioPolicyConfig(AudioPolicyConfig conf) {
         mMixes = conf.mMixes;
     }
@@ -201,20 +209,39 @@
             return;
         }
         mRegistrationId = regId == null ? "" : regId;
-        int mixIndex = 0;
         for (AudioMix mix : mMixes) {
-            if (!mRegistrationId.isEmpty()) {
-                if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
-                        AudioMix.ROUTE_FLAG_LOOP_BACK) {
-                    mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
-                            + mixIndex++);
-                } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
-                        AudioMix.ROUTE_FLAG_RENDER) {
-                    mix.setRegistration(mix.mDeviceAddress);
-                }
-            } else {
-                mix.setRegistration("");
+            setMixRegistration(mix);
+        }
+    }
+
+    private void setMixRegistration(@NonNull final AudioMix mix) {
+        if (!mRegistrationId.isEmpty()) {
+            if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
+                    AudioMix.ROUTE_FLAG_LOOP_BACK) {
+                mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
+                        + mMixCounter);
+            } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
+                    AudioMix.ROUTE_FLAG_RENDER) {
+                mix.setRegistration(mix.mDeviceAddress);
             }
+        } else {
+            mix.setRegistration("");
+        }
+        mMixCounter++;
+    }
+
+    @GuardedBy("mMixes")
+    protected void add(@NonNull ArrayList<AudioMix> mixes) {
+        for (AudioMix mix : mixes) {
+            setMixRegistration(mix);
+            mMixes.add(mix);
+        }
+    }
+
+    @GuardedBy("mMixes")
+    protected void remove(@NonNull ArrayList<AudioMix> mixes) {
+        for (AudioMix mix : mixes) {
+            mMixes.remove(mix);
         }
     }
 
diff --git a/android/media/midi/MidiManager.java b/android/media/midi/MidiManager.java
index a015732..dee94c6 100644
--- a/android/media/midi/MidiManager.java
+++ b/android/media/midi/MidiManager.java
@@ -16,9 +16,11 @@
 
 package android.media.midi;
 
+import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Bundle;
@@ -32,6 +34,7 @@
  * This class is the public application interface to the MIDI service.
  */
 @SystemService(Context.MIDI_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_MIDI)
 public final class MidiManager {
     private static final String TAG = "MidiManager";
 
diff --git a/android/media/session/MediaController.java b/android/media/session/MediaController.java
index 622900f..f16804c 100644
--- a/android/media/session/MediaController.java
+++ b/android/media/session/MediaController.java
@@ -133,7 +133,7 @@
             return false;
         }
         try {
-            return mSessionBinder.sendMediaButton(keyEvent);
+            return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent);
         } catch (RemoteException e) {
             // System is dead. =(
         }
@@ -301,7 +301,7 @@
      */
     public void setVolumeTo(int value, int flags) {
         try {
-            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
+            mSessionBinder.setVolumeTo(mContext.getPackageName(), value, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling setVolumeTo.", e);
         }
@@ -322,7 +322,7 @@
      */
     public void adjustVolume(int direction, int flags) {
         try {
-            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
+            mSessionBinder.adjustVolume(mContext.getPackageName(), direction, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
         }
@@ -388,7 +388,7 @@
             throw new IllegalArgumentException("command cannot be null or empty");
         }
         try {
-            mSessionBinder.sendCommand(command, args, cb);
+            mSessionBinder.sendCommand(mContext.getPackageName(), command, args, cb);
         } catch (RemoteException e) {
             Log.d(TAG, "Dead object in sendCommand.", e);
         }
@@ -600,7 +600,7 @@
          */
         public void prepare() {
             try {
-                mSessionBinder.prepare();
+                mSessionBinder.prepare(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare.", e);
             }
@@ -624,7 +624,7 @@
                         "You must specify a non-empty String for prepareFromMediaId.");
             }
             try {
-                mSessionBinder.prepareFromMediaId(mediaId, extras);
+                mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mediaId, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
             }
@@ -650,7 +650,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.prepareFromSearch(query, extras);
+                mSessionBinder.prepareFromSearch(mContext.getPackageName(), query, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
             }
@@ -674,7 +674,7 @@
                         "You must specify a non-empty Uri for prepareFromUri.");
             }
             try {
-                mSessionBinder.prepareFromUri(uri, extras);
+                mSessionBinder.prepareFromUri(mContext.getPackageName(), uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
             }
@@ -685,7 +685,7 @@
          */
         public void play() {
             try {
-                mSessionBinder.play();
+                mSessionBinder.play(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play.", e);
             }
@@ -704,7 +704,7 @@
                         "You must specify a non-empty String for playFromMediaId.");
             }
             try {
-                mSessionBinder.playFromMediaId(mediaId, extras);
+                mSessionBinder.playFromMediaId(mContext.getPackageName(), mediaId, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
             }
@@ -726,7 +726,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.playFromSearch(query, extras);
+                mSessionBinder.playFromSearch(mContext.getPackageName(), query, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + query + ").", e);
             }
@@ -745,7 +745,7 @@
                         "You must specify a non-empty Uri for playFromUri.");
             }
             try {
-                mSessionBinder.playFromUri(uri, extras);
+                mSessionBinder.playFromUri(mContext.getPackageName(), uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + uri + ").", e);
             }
@@ -757,7 +757,7 @@
          */
         public void skipToQueueItem(long id) {
             try {
-                mSessionBinder.skipToQueueItem(id);
+                mSessionBinder.skipToQueueItem(mContext.getPackageName(), id);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
             }
@@ -769,7 +769,7 @@
          */
         public void pause() {
             try {
-                mSessionBinder.pause();
+                mSessionBinder.pause(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling pause.", e);
             }
@@ -781,7 +781,7 @@
          */
         public void stop() {
             try {
-                mSessionBinder.stop();
+                mSessionBinder.stop(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling stop.", e);
             }
@@ -794,7 +794,7 @@
          */
         public void seekTo(long pos) {
             try {
-                mSessionBinder.seekTo(pos);
+                mSessionBinder.seekTo(mContext.getPackageName(), pos);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling seekTo.", e);
             }
@@ -806,7 +806,7 @@
          */
         public void fastForward() {
             try {
-                mSessionBinder.fastForward();
+                mSessionBinder.fastForward(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling fastForward.", e);
             }
@@ -817,7 +817,7 @@
          */
         public void skipToNext() {
             try {
-                mSessionBinder.next();
+                mSessionBinder.next(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling next.", e);
             }
@@ -829,7 +829,7 @@
          */
         public void rewind() {
             try {
-                mSessionBinder.rewind();
+                mSessionBinder.rewind(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rewind.", e);
             }
@@ -840,7 +840,7 @@
          */
         public void skipToPrevious() {
             try {
-                mSessionBinder.previous();
+                mSessionBinder.previous(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling previous.", e);
             }
@@ -855,7 +855,7 @@
          */
         public void setRating(Rating rating) {
             try {
-                mSessionBinder.rate(rating);
+                mSessionBinder.rate(mContext.getPackageName(), rating);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rate.", e);
             }
@@ -890,7 +890,7 @@
                 throw new IllegalArgumentException("CustomAction cannot be null.");
             }
             try {
-                mSessionBinder.sendCustomAction(action, args);
+                mSessionBinder.sendCustomAction(mContext.getPackageName(), action, args);
             } catch (RemoteException e) {
                 Log.d(TAG, "Dead object in sendCustomAction.", e);
             }
diff --git a/android/media/session/MediaSession.java b/android/media/session/MediaSession.java
index b8184a0..5e8b8ca 100644
--- a/android/media/session/MediaSession.java
+++ b/android/media/session/MediaSession.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.service.media.MediaBrowserService;
 import android.text.TextUtils;
 import android.util.Log;
@@ -103,6 +104,16 @@
      */
     public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
 
+    /**
+     * @hide
+     */
+    public static final int INVALID_UID = -1;
+
+    /**
+     * @hide
+     */
+    public static final int INVALID_PID = -1;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {
@@ -119,7 +130,8 @@
     private final ISession mBinder;
     private final CallbackStub mCbStub;
 
-    private CallbackMessageHandler mCallbackHandler;
+    // Do not change the name of mCallback. Support lib accesses this by using reflection.
+    private CallbackMessageHandler mCallback;
     private VolumeProvider mVolumeProvider;
     private PlaybackState mPlaybackState;
 
@@ -194,13 +206,13 @@
      */
     public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
         synchronized (mLock) {
-            if (mCallbackHandler != null) {
+            if (mCallback != null) {
                 // We're updating the callback, clear the session from the old one.
-                mCallbackHandler.mCallback.mSession = null;
-                mCallbackHandler.removeCallbacksAndMessages(null);
+                mCallback.mCallback.mSession = null;
+                mCallback.removeCallbacksAndMessages(null);
             }
             if (callback == null) {
-                mCallbackHandler = null;
+                mCallback = null;
                 return;
             }
             if (handler == null) {
@@ -209,7 +221,7 @@
             callback.mSession = this;
             CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
                     callback);
-            mCallbackHandler = msgHandler;
+            mCallback = msgHandler;
         }
     }
 
@@ -500,6 +512,22 @@
     }
 
     /**
+     * Gets the controller information who sent the current request.
+     * <p>
+     * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link Callback} methods.
+     * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
+        if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
+            throw new IllegalStateException(
+                    "This should be called inside of MediaSession.Callback methods");
+        }
+        return mCallback.mCurrentControllerInfo;
+    }
+
+    /**
      * Notify the system that the remote volume changed.
      *
      * @param provider The provider that is handling volume changes.
@@ -527,16 +555,14 @@
      * @hide
      */
     public String getCallingPackage() {
-        try {
-            return mBinder.getCallingPackage();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Dead object in getCallingPackage.", e);
+        if (mCallback != null) {
+            return mCallback.mCurrentControllerInfo.getPackageName();
         }
         return null;
     }
 
-    private void dispatchPrepare() {
-        postToCallback(CallbackMessageHandler.MSG_PREPARE);
+    private void dispatchPrepare(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PREPARE, null, extras);
     }
 
     private void dispatchPrepareFromMediaId(String mediaId, Bundle extras) {
@@ -551,8 +577,8 @@
         postToCallback(CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
     }
 
-    private void dispatchPlay() {
-        postToCallback(CallbackMessageHandler.MSG_PLAY);
+    private void dispatchPlay(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PLAY, null, extras);
     }
 
     private void dispatchPlayFromMediaId(String mediaId, Bundle extras) {
@@ -567,75 +593,67 @@
         postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
     }
 
-    private void dispatchSkipToItem(long id) {
-        postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id);
+    private void dispatchSkipToItem(long id, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, extras);
     }
 
-    private void dispatchPause() {
-        postToCallback(CallbackMessageHandler.MSG_PAUSE);
+    private void dispatchPause(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PAUSE, null, extras);
     }
 
-    private void dispatchStop() {
-        postToCallback(CallbackMessageHandler.MSG_STOP);
+    private void dispatchStop(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_STOP, null, extras);
     }
 
-    private void dispatchNext() {
-        postToCallback(CallbackMessageHandler.MSG_NEXT);
+    private void dispatchNext(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_NEXT, null, extras);
     }
 
-    private void dispatchPrevious() {
-        postToCallback(CallbackMessageHandler.MSG_PREVIOUS);
+    private void dispatchPrevious(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PREVIOUS, null, extras);
     }
 
-    private void dispatchFastForward() {
-        postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD);
+    private void dispatchFastForward(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD, null, extras);
     }
 
-    private void dispatchRewind() {
-        postToCallback(CallbackMessageHandler.MSG_REWIND);
+    private void dispatchRewind(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_REWIND, null, extras);
     }
 
-    private void dispatchSeekTo(long pos) {
-        postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos);
+    private void dispatchSeekTo(long pos, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos, extras);
     }
 
-    private void dispatchRate(Rating rating) {
-        postToCallback(CallbackMessageHandler.MSG_RATE, rating);
+    private void dispatchRate(Rating rating, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_RATE, rating, extras);
     }
 
-    private void dispatchCustomAction(String action, Bundle args) {
-        postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
+    private void dispatchCustomAction(String action, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, extras);
     }
 
-    private void dispatchMediaButton(Intent mediaButtonIntent) {
-        postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
+    private void dispatchMediaButton(Intent mediaButtonIntent, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, extras);
     }
 
-    private void dispatchAdjustVolume(int direction) {
-        postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction);
+    private void dispatchAdjustVolume(int direction, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, extras);
     }
 
-    private void dispatchSetVolumeTo(int volume) {
-        postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume);
+    private void dispatchSetVolumeTo(int volume, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume, extras);
     }
 
-    private void postToCallback(int what) {
-        postToCallback(what, null);
-    }
-
-    private void postCommand(String command, Bundle args, ResultReceiver resultCb) {
+    private void postCommand(String command, Bundle args, ResultReceiver resultCb, Bundle extras) {
         Command cmd = new Command(command, args, resultCb);
-        postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd);
-    }
-
-    private void postToCallback(int what, Object obj) {
-        postToCallback(what, obj, null);
+        postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd, extras);
     }
 
     private void postToCallback(int what, Object obj, Bundle extras) {
         synchronized (mLock) {
-            if (mCallbackHandler != null) {
-                mCallbackHandler.post(what, obj, extras);
+            if (mCallback != null) {
+                mCallback.post(what, obj, extras);
             }
         }
     }
@@ -733,9 +751,13 @@
      * and the system. A callback may be set using {@link #setCallback}.
      */
     public abstract static class Callback {
+
         private MediaSession mSession;
         private CallbackMessageHandler mHandler;
         private boolean mMediaPlayPauseKeyPending;
+        private String mCallingPackage;
+        private int mCallingPid;
+        private int mCallingUid;
 
         public Callback() {
         }
@@ -1022,24 +1044,26 @@
         private WeakReference<MediaSession> mMediaSession;
 
         public CallbackStub(MediaSession session) {
-            mMediaSession = new WeakReference<MediaSession>(session);
+            mMediaSession = new WeakReference<>(session);
         }
 
         @Override
-        public void onCommand(String command, Bundle args, ResultReceiver cb) {
+        public void onCommand(String packageName, int pid, int uid, String command, Bundle args,
+                ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.postCommand(command, args, cb);
+                session.postCommand(command, args, cb, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber,
-                ResultReceiver cb) {
+        public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
+                int sequenceNumber, ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             try {
                 if (session != null) {
-                    session.dispatchMediaButton(mediaButtonIntent);
+                    session.dispatchMediaButton(
+                            mediaButtonIntent, createExtraBundle(packageName, pid, uid));
                 }
             } finally {
                 if (cb != null) {
@@ -1049,165 +1073,191 @@
         }
 
         @Override
-        public void onPrepare() {
+        public void onPrepare(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepare();
+                session.dispatchPrepare(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+        public void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepareFromMediaId(mediaId, extras);
+                session.dispatchPrepareFromMediaId(
+                        mediaId, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPrepareFromSearch(String query, Bundle extras) {
+        public void onPrepareFromSearch(String packageName, int pid, int uid, String query,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepareFromSearch(query, extras);
+                session.dispatchPrepareFromSearch(
+                        query, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPrepareFromUri(Uri uri, Bundle extras) {
+        public void onPrepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepareFromUri(uri, extras);
+                session.dispatchPrepareFromUri(uri,
+                        createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPlay() {
+        public void onPlay(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlay();
+                session.dispatchPlay(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+        public void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayFromMediaId(mediaId, extras);
+                session.dispatchPlayFromMediaId(
+                        mediaId, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPlayFromSearch(String query, Bundle extras) {
+        public void onPlayFromSearch(String packageName, int pid, int uid, String query,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayFromSearch(query, extras);
+                session.dispatchPlayFromSearch(query, createExtraBundle(packageName, pid, uid,
+                        extras));
             }
         }
 
         @Override
-        public void onPlayFromUri(Uri uri, Bundle extras) {
+        public void onPlayFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayFromUri(uri, extras);
+                session.dispatchPlayFromUri(uri, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onSkipToTrack(long id) {
+        public void onSkipToTrack(String packageName, int pid, int uid, long id) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchSkipToItem(id);
+                session.dispatchSkipToItem(id, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPause() {
+        public void onPause(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPause();
+                session.dispatchPause(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onStop() {
+        public void onStop(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchStop();
+                session.dispatchStop(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onNext() {
+        public void onNext(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchNext();
+                session.dispatchNext(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPrevious() {
+        public void onPrevious(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrevious();
+                session.dispatchPrevious(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onFastForward() {
+        public void onFastForward(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchFastForward();
+                session.dispatchFastForward(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onRewind() {
+        public void onRewind(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchRewind();
+                session.dispatchRewind(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onSeekTo(long pos) {
+        public void onSeekTo(String packageName, int pid, int uid, long pos) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchSeekTo(pos);
+                session.dispatchSeekTo(pos, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onRate(Rating rating) {
+        public void onRate(String packageName, int pid, int uid, Rating rating) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchRate(rating);
+                session.dispatchRate(rating, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onCustomAction(String action, Bundle args) {
+        public void onCustomAction(String packageName, int pid, int uid, String action,
+                Bundle args) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchCustomAction(action, args);
+                session.dispatchCustomAction(
+                        action, createExtraBundle(packageName, pid, uid, args));
             }
         }
 
         @Override
-        public void onAdjustVolume(int direction) {
+        public void onAdjustVolume(String packageName, int pid, int uid, int direction) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchAdjustVolume(direction);
+                session.dispatchAdjustVolume(direction, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onSetVolumeTo(int value) {
+        public void onSetVolumeTo(String packageName, int pid, int uid, int value) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchSetVolumeTo(value);
+                session.dispatchSetVolumeTo(value, createExtraBundle(packageName, pid, uid));
             }
         }
 
+        private Bundle createExtraBundle(String packageName, int pid, int uid) {
+            return createExtraBundle(packageName, pid, uid, null);
+        }
+
+        private Bundle createExtraBundle(String packageName, int pid, int uid,
+                Bundle originalBundle) {
+            Bundle bundle = new Bundle();
+            bundle.putString(CallbackMessageHandler.EXTRA_KEY_CALLING_PACKAGE, packageName);
+            bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_PID, pid);
+            bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_UID, uid);
+            if (originalBundle != null) {
+                bundle.putBundle(CallbackMessageHandler.EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle);
+            }
+            return bundle;
+        }
     }
 
     /**
@@ -1271,7 +1321,8 @@
             return 0;
         }
 
-        public static final Creator<MediaSession.QueueItem> CREATOR = new Creator<MediaSession.QueueItem>() {
+        public static final Creator<MediaSession.QueueItem> CREATOR =
+                new Creator<MediaSession.QueueItem>() {
 
             @Override
             public MediaSession.QueueItem createFromParcel(Parcel p) {
@@ -1328,6 +1379,15 @@
 
     private class CallbackMessageHandler extends Handler {
 
+        private static final String EXTRA_KEY_CALLING_PACKAGE =
+                "android.media.session.extra.CALLING_PACKAGE";
+        private static final String EXTRA_KEY_CALLING_PID =
+                "android.media.session.extra.CALLING_PID";
+        private static final String EXTRA_KEY_CALLING_UID =
+                "android.media.session.extra.CALLING_UID";
+        private static final String EXTRA_KEY_ORIGINAL_BUNDLE =
+                "android.media.session.extra.ORIGINAL_BUNDLE";
+
         private static final int MSG_COMMAND = 1;
         private static final int MSG_MEDIA_BUTTON = 2;
         private static final int MSG_PREPARE = 3;
@@ -1354,6 +1414,8 @@
 
         private MediaSession.Callback mCallback;
 
+        private RemoteUserInfo mCurrentControllerInfo;
+
         public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
             super(looper, null, true);
             mCallback = callback;
@@ -1366,21 +1428,17 @@
             msg.sendToTarget();
         }
 
-        public void post(int what, Object obj) {
-            obtainMessage(what, obj).sendToTarget();
-        }
-
-        public void post(int what) {
-            post(what, null);
-        }
-
-        public void post(int what, Object obj, int arg1) {
-            obtainMessage(what, arg1, 0, obj).sendToTarget();
-        }
-
         @Override
         public void handleMessage(Message msg) {
             VolumeProvider vp;
+            Bundle bundle = msg.getData();
+            Bundle originalBundle = bundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE);
+
+            mCurrentControllerInfo = new RemoteUserInfo(
+                    bundle.getString(EXTRA_KEY_CALLING_PACKAGE),
+                    bundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID),
+                    bundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID));
+
             switch (msg.what) {
                 case MSG_COMMAND:
                     Command cmd = (Command) msg.obj;
@@ -1393,25 +1451,25 @@
                     mCallback.onPrepare();
                     break;
                 case MSG_PREPARE_MEDIA_ID:
-                    mCallback.onPrepareFromMediaId((String) msg.obj, msg.getData());
+                    mCallback.onPrepareFromMediaId((String) msg.obj, originalBundle);
                     break;
                 case MSG_PREPARE_SEARCH:
-                    mCallback.onPrepareFromSearch((String) msg.obj, msg.getData());
+                    mCallback.onPrepareFromSearch((String) msg.obj, originalBundle);
                     break;
                 case MSG_PREPARE_URI:
-                    mCallback.onPrepareFromUri((Uri) msg.obj, msg.getData());
+                    mCallback.onPrepareFromUri((Uri) msg.obj, originalBundle);
                     break;
                 case MSG_PLAY:
                     mCallback.onPlay();
                     break;
                 case MSG_PLAY_MEDIA_ID:
-                    mCallback.onPlayFromMediaId((String) msg.obj, msg.getData());
+                    mCallback.onPlayFromMediaId((String) msg.obj, originalBundle);
                     break;
                 case MSG_PLAY_SEARCH:
-                    mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
+                    mCallback.onPlayFromSearch((String) msg.obj, originalBundle);
                     break;
                 case MSG_PLAY_URI:
-                    mCallback.onPlayFromUri((Uri) msg.obj, msg.getData());
+                    mCallback.onPlayFromUri((Uri) msg.obj, originalBundle);
                     break;
                 case MSG_SKIP_TO_ITEM:
                     mCallback.onSkipToQueueItem((Long) msg.obj);
@@ -1441,7 +1499,7 @@
                     mCallback.onSetRating((Rating) msg.obj);
                     break;
                 case MSG_CUSTOM_ACTION:
-                    mCallback.onCustomAction((String) msg.obj, msg.getData());
+                    mCallback.onCustomAction((String) msg.obj, originalBundle);
                     break;
                 case MSG_ADJUST_VOLUME:
                     synchronized (mLock) {
@@ -1463,6 +1521,7 @@
                     mCallback.handleMediaPlayPauseKeySingleTapIfPending();
                     break;
             }
+            mCurrentControllerInfo = null;
         }
     }
 }
diff --git a/android/media/session/MediaSessionManager.java b/android/media/session/MediaSessionManager.java
index 81b4603..519af1b 100644
--- a/android/media/session/MediaSessionManager.java
+++ b/android/media/session/MediaSessionManager.java
@@ -16,6 +16,7 @@
 
 package android.media.session;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -24,8 +25,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
-import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.ISessionTokensListener;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2;
 import android.media.SessionToken2;
@@ -36,7 +37,9 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.service.media.MediaBrowserService;
 import android.service.notification.NotificationListenerService;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -44,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Provides support for interacting with {@link MediaSession media sessions}
@@ -71,6 +75,8 @@
 
     private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
             = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
+    private final ArrayMap<OnSessionTokensChangedListener, SessionTokensChangedWrapper>
+            mSessionTokensListener = new ArrayMap<>();
     private final Object mLock = new Object();
     private final ISessionManager mService;
 
@@ -336,37 +342,74 @@
     }
 
     /**
-     * Called when a {@link MediaSession2} is created.
+     * Returns whether the app is trusted.
+     * <p>
+     * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL
+     * permission or has an enabled notification listener.
      *
-     * @hide
+     * @param userInfo The remote user info
      */
-    // TODO(jaewan): System API
-    public SessionToken2 createSessionToken(@NonNull String callingPackage, @NonNull String id,
-            @NonNull IMediaSession2 binder) {
+    public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) {
+        if (userInfo.getPackageName() == null) {
+            return false;
+        }
         try {
-            Bundle bundle = mService.createSessionToken(callingPackage, id, binder);
-            return SessionToken2.fromBundle(bundle);
+            return mService.isTrusted(
+                    userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
         }
-        return null;
+        return false;
     }
 
     /**
+     * Called when a {@link MediaSession2} is created.
+     * @hide
+     */
+    public boolean createSession2(@NonNull SessionToken2 token) {
+        if (token == null) {
+            return false;
+        }
+        try {
+            return mService.createSession2(token.toBundle());
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+        }
+        return false;
+    }
+
+    /**
+     * Called when a {@link MediaSession2} is destroyed.
+     * @hide
+     */
+    public void destroySession2(@NonNull SessionToken2 token) {
+        if (token == null) {
+            return;
+        }
+        try {
+            mService.destroySession2(token.toBundle());
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+        }
+    }
+
+    /**
+     * @hide
      * Get {@link List} of {@link SessionToken2} whose sessions are active now. This list represents
      * active sessions regardless of whether they're {@link MediaSession2} or
      * {@link MediaSessionService2}.
+     * <p>
+     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
+     * calling app. You may also retrieve this list if your app is an enabled notification listener
+     * using the {@link NotificationListenerService} APIs.
      *
-     * @return list of Tokens
-     * @hide
+     * @return list of tokens
      */
-    // TODO(jaewan): Unhide
-    // TODO(jaewan): Protect this with permission.
-    // TODO(jaewna): Add listener for change in lists.
     public List<SessionToken2> getActiveSessionTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false,
+                    mContext.getPackageName());
             return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -375,18 +418,21 @@
     }
 
     /**
+     * @hide
      * Get {@link List} of {@link SessionToken2} for {@link MediaSessionService2} regardless of their
      * activeness. This list represents media apps that support background playback.
+     * <p>
+     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
+     * calling app. You may also retrieve this list if your app is an enabled notification listener
+     * using the {@link NotificationListenerService} APIs.
      *
-     * @return list of Tokens
-     * @hide
+     * @return list of tokens
      */
-    // TODO(jaewan): Unhide
-    // TODO(jaewna): Add listener for change in lists.
     public List<SessionToken2> getSessionServiceTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true,
+                    mContext.getPackageName());
             return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -395,21 +441,23 @@
     }
 
     /**
+     * @hide
      * Get all {@link SessionToken2}s. This is the combined list of {@link #getActiveSessionTokens()}
      * and {@link #getSessionServiceTokens}.
+     * <p>
+     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
+     * calling app. You may also retrieve this list if your app is an enabled notification listener
+     * using the {@link NotificationListenerService} APIs.
      *
-     * @return list of Tokens
+     * @return list of tokens
      * @see #getActiveSessionTokens
      * @see #getSessionServiceTokens
-     * @hide
      */
-    // TODO(jaewan): Unhide
-    // TODO(jaewan): Protect this with permission.
-    // TODO(jaewna): Add listener for change in lists.
     public List<SessionToken2> getAllSessionTokens() {
         try {
             List<Bundle> bundles = mService.getSessionTokens(
-                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false,
+                    mContext.getPackageName());
             return toTokenList(bundles);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
@@ -417,6 +465,84 @@
         }
     }
 
+    /**
+     * @hide
+     * Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
+     * <p>
+     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
+     * calling app. You may also retrieve this list if your app is an enabled notification listener
+     * using the {@link NotificationListenerService} APIs.
+     *
+     * @param executor executor to run this command
+     * @param listener The listener to add.
+     */
+    public void addOnSessionTokensChangedListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OnSessionTokensChangedListener listener) {
+        addOnSessionTokensChangedListener(UserHandle.myUserId(), executor, listener);
+    }
+
+    /**
+     * Add a listener to be notified when the {@link #getAllSessionTokens()} changes.
+     * <p>
+     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by the
+     * calling app. You may also retrieve this list if your app is an enabled notification listener
+     * using the {@link NotificationListenerService} APIs.
+     *
+     * @param userId The userId to listen for changes on.
+     * @param executor executor to run this command
+     * @param listener The listener to add.
+     * @hide
+     */
+    public void addOnSessionTokensChangedListener(int userId,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnSessionTokensChangedListener listener) {
+        if (executor == null) {
+            throw new IllegalArgumentException("executor may not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener may not be null");
+        }
+        synchronized (mLock) {
+            if (mSessionTokensListener.get(listener) != null) {
+                Log.w(TAG, "Attempted to add session listener twice, ignoring.");
+                return;
+            }
+            SessionTokensChangedWrapper wrapper = new SessionTokensChangedWrapper(
+                    mContext, executor, listener);
+            try {
+                mService.addSessionTokensListener(wrapper.mStub, userId, mContext.getPackageName());
+                mSessionTokensListener.put(listener, wrapper);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in addSessionTokensListener.", e);
+            }
+        }
+    }
+
+    /**
+     * @hide
+     * Stop receiving session token updates on the specified listener.
+     *
+     * @param listener The listener to remove.
+     */
+    public void removeOnSessionTokensChangedListener(
+            @NonNull OnSessionTokensChangedListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener may not be null");
+        }
+        synchronized (mLock) {
+            SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener);
+            if (wrapper != null) {
+                try {
+                    mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in removeSessionTokensListener.", e);
+                } finally {
+                    wrapper.release();
+                }
+            }
+        }
+    }
+
     private static List<SessionToken2> toTokenList(List<Bundle> bundles) {
         List<SessionToken2> tokens = new ArrayList<>();
         if (bundles != null) {
@@ -552,6 +678,15 @@
     }
 
     /**
+     * @hide
+     * Listens for changes to the {@link #getAllSessionTokens()}. This can be added
+     * using {@link #addOnActiveSessionsChangedListener}.
+     */
+    public interface OnSessionTokensChangedListener {
+        void onSessionTokensChanged(@NonNull List<SessionToken2> tokens);
+    }
+
+    /**
      * Listens the volume key long-presses.
      * @hide
      */
@@ -631,6 +766,56 @@
         public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
     }
 
+    /**
+     * Information of a remote user of {@link MediaSession} or {@link MediaBrowserService}.
+     * This can be used to decide whether the remote user is trusted app.
+     *
+     * @see #isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public static final class RemoteUserInfo {
+        private String mPackageName;
+        private int mPid;
+        private int mUid;
+
+        public RemoteUserInfo(String packageName, int pid, int uid) {
+            mPackageName = packageName;
+            mPid = pid;
+            mUid = uid;
+        }
+
+        /**
+         * @return package name of the controller
+         */
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * @return pid of the controller
+         */
+        public int getPid() {
+            return mPid;
+        }
+
+        /**
+         * @return uid of the controller
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof RemoteUserInfo)) {
+                return false;
+            }
+            RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj;
+            return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
+                    && mPid == otherUserInfo.mPid
+                    && mUid == otherUserInfo.mUid;
+        }
+    }
+
     private static final class SessionsChangedWrapper {
         private Context mContext;
         private OnActiveSessionsChangedListener mListener;
@@ -653,8 +838,7 @@
                         public void run() {
                             final Context context = mContext;
                             if (context != null) {
-                                ArrayList<MediaController> controllers
-                                        = new ArrayList<MediaController>();
+                                ArrayList<MediaController> controllers = new ArrayList<>();
                                 int size = tokens.size();
                                 for (int i = 0; i < size; i++) {
                                     controllers.add(new MediaController(context, tokens.get(i)));
@@ -677,6 +861,41 @@
         }
     }
 
+    private static final class SessionTokensChangedWrapper {
+        private Context mContext;
+        private Executor mExecutor;
+        private OnSessionTokensChangedListener mListener;
+
+        public SessionTokensChangedWrapper(Context context, Executor executor,
+                OnSessionTokensChangedListener listener) {
+            mContext = context;
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        private final ISessionTokensListener.Stub mStub = new ISessionTokensListener.Stub() {
+            @Override
+            public void onSessionTokensChanged(final List<Bundle> bundles) {
+                final Executor executor = mExecutor;
+                if (executor != null) {
+                    executor.execute(() -> {
+                        final Context context = mContext;
+                        final OnSessionTokensChangedListener listener = mListener;
+                        if (context != null && listener != null) {
+                            listener.onSessionTokensChanged(toTokenList(bundles));
+                        }
+                    });
+                }
+            }
+        };
+
+        private void release() {
+            mListener = null;
+            mContext = null;
+            mExecutor = null;
+        }
+    }
+
     private static final class OnVolumeKeyLongPressListenerImpl
             extends IOnVolumeKeyLongPressListener.Stub {
         private OnVolumeKeyLongPressListener mListener;
diff --git a/android/media/soundtrigger/SoundTriggerDetectionService.java b/android/media/soundtrigger/SoundTriggerDetectionService.java
new file mode 100644
index 0000000..7381d97
--- /dev/null
+++ b/android/media/soundtrigger/SoundTriggerDetectionService.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.soundtrigger;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.UUID;
+
+/**
+ * A service that allows interaction with the actual sound trigger detection on the system.
+ *
+ * <p> Sound trigger detection refers to detectors that match generic sound patterns that are
+ * not voice-based. The voice-based recognition models should utilize the {@link
+ * android.service.voice.VoiceInteractionService} instead. Access to this class needs to be
+ * protected by the {@value android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE}
+ * permission granted only to the system.
+ *
+ * <p>This service has to be explicitly started by an app, the system does not scan for and start
+ * these services.
+ *
+ * <p>If an operation ({@link #onGenericRecognitionEvent}, {@link #onError},
+ * {@link #onRecognitionPaused}, {@link #onRecognitionResumed}) is triggered the service is
+ * considered as running in the foreground. Once the operation is processed the service should call
+ * {@link #operationFinished(UUID, int)}. If this does not happen in
+ * {@link SoundTriggerManager#getDetectionServiceOperationsTimeout()} milliseconds
+ * {@link #onStopOperation(UUID, Bundle, int)} is called and the service is unbound.
+ *
+ * <p>The total amount of operations per day might be limited.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SoundTriggerDetectionService extends Service {
+    private static final String LOG_TAG = SoundTriggerDetectionService.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Client indexed by model uuid. This is needed for the {@link #operationFinished(UUID, int)}
+     * callbacks.
+     */
+    @GuardedBy("mLock")
+    private final ArrayMap<UUID, ISoundTriggerDetectionServiceClient> mClients =
+            new ArrayMap<>();
+
+    private Handler mHandler;
+
+    /**
+     * @hide
+     */
+    @Override
+    protected final void attachBaseContext(Context base) {
+        super.attachBaseContext(base);
+        mHandler = new Handler(base.getMainLooper());
+    }
+
+    private void setClient(@NonNull UUID uuid, @Nullable Bundle params,
+            @NonNull ISoundTriggerDetectionServiceClient client) {
+        if (DEBUG) Log.i(LOG_TAG, uuid + ": handle setClient");
+
+        synchronized (mLock) {
+            mClients.put(uuid, client);
+        }
+        onConnected(uuid, params);
+    }
+
+    private void removeClient(@NonNull UUID uuid, @Nullable Bundle params) {
+        if (DEBUG) Log.i(LOG_TAG, uuid + ": handle removeClient");
+
+        synchronized (mLock) {
+            mClients.remove(uuid);
+        }
+        onDisconnected(uuid, params);
+    }
+
+    /**
+     * The system has connected to this service for the recognition registered for the model
+     * {@code uuid}.
+     *
+     * <p> This is called before any operations are delivered.
+     *
+     * @param uuid   The {@code uuid} of the model the recognitions is registered for
+     * @param params The {@code params} passed when the recognition was started
+     */
+    @MainThread
+    public void onConnected(@NonNull UUID uuid, @Nullable Bundle params) {
+        /* do nothing */
+    }
+
+    /**
+     * The system has disconnected from this service for the recognition registered for the model
+     * {@code uuid}.
+     *
+     * <p>Once this is called {@link #operationFinished} cannot be called anymore for
+     * {@code uuid}.
+     *
+     * <p> {@link #onConnected(UUID, Bundle)} is called before any further operations are delivered.
+     *
+     * @param uuid   The {@code uuid} of the model the recognitions is registered for
+     * @param params The {@code params} passed when the recognition was started
+     */
+    @MainThread
+    public void onDisconnected(@NonNull UUID uuid, @Nullable Bundle params) {
+        /* do nothing */
+    }
+
+    /**
+     * A new generic sound trigger event has been detected.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of this operation. Once the operation is done, this service needs to call
+     *             {@link #operationFinished(UUID, int)}
+     * @param event The event that has been detected
+     */
+    @MainThread
+    public void onGenericRecognitionEvent(@NonNull UUID uuid, @Nullable Bundle params, int opId,
+            @NonNull SoundTrigger.RecognitionEvent event) {
+        operationFinished(uuid, opId);
+    }
+
+    /**
+     * A error has been detected.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of this operation. Once the operation is done, this service needs to call
+     *             {@link #operationFinished(UUID, int)}
+     * @param status The error code detected
+     */
+    @MainThread
+    public void onError(@NonNull UUID uuid, @Nullable Bundle params, int opId, int status) {
+        operationFinished(uuid, opId);
+    }
+
+    /**
+     * An operation took too long and should be stopped.
+     *
+     * @param uuid   The {@code uuid} of the model the recognition is registered for
+     * @param params The {@code params} passed when the recognition was started
+     * @param opId The id of the operation that took too long
+     */
+    @MainThread
+    public abstract void onStopOperation(@NonNull UUID uuid, @Nullable Bundle params, int opId);
+
+    /**
+     * Tell that the system that an operation has been fully processed.
+     *
+     * @param uuid The {@code uuid} of the model the recognition is registered for
+     * @param opId The id of the operation that is processed
+     */
+    public final void operationFinished(@Nullable UUID uuid, int opId) {
+        try {
+            ISoundTriggerDetectionServiceClient client;
+            synchronized (mLock) {
+                client = mClients.get(uuid);
+
+                if (client == null) {
+                    throw new IllegalStateException("operationFinished called, but no client for "
+                            + uuid + ". Was this called after onDisconnected?");
+                }
+            }
+            client.onOpFinished(opId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        return new ISoundTriggerDetectionService.Stub() {
+            private final Object mBinderLock = new Object();
+
+            /** Cached params bundles indexed by the model uuid */
+            @GuardedBy("mBinderLock")
+            public final ArrayMap<UUID, Bundle> mParams = new ArrayMap<>();
+
+            @Override
+            public void setClient(ParcelUuid puuid, Bundle params,
+                    ISoundTriggerDetectionServiceClient client) {
+                UUID uuid = puuid.getUuid();
+                synchronized (mBinderLock) {
+                    mParams.put(uuid, params);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + ": setClient(" + params + ")");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::setClient,
+                        SoundTriggerDetectionService.this, uuid, params, client));
+            }
+
+            @Override
+            public void removeClient(ParcelUuid puuid) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.remove(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + ": removeClient");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::removeClient,
+                        SoundTriggerDetectionService.this, uuid, params));
+            }
+
+            @Override
+            public void onGenericRecognitionEvent(ParcelUuid puuid, int opId,
+                    SoundTrigger.GenericRecognitionEvent event) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onGenericRecognitionEvent");
+                mHandler.sendMessage(
+                        obtainMessage(SoundTriggerDetectionService::onGenericRecognitionEvent,
+                                SoundTriggerDetectionService.this, uuid, params, opId, event));
+            }
+
+            @Override
+            public void onError(ParcelUuid puuid, int opId, int status) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onError(" + status + ")");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onError,
+                        SoundTriggerDetectionService.this, uuid, params, opId, status));
+            }
+
+            @Override
+            public void onStopOperation(ParcelUuid puuid, int opId) {
+                UUID uuid = puuid.getUuid();
+                Bundle params;
+                synchronized (mBinderLock) {
+                    params = mParams.get(uuid);
+                }
+
+                if (DEBUG) Log.i(LOG_TAG, uuid + "(" + opId + "): onStopOperation");
+                mHandler.sendMessage(obtainMessage(SoundTriggerDetectionService::onStopOperation,
+                        SoundTriggerDetectionService.this, uuid, params, opId));
+            }
+        };
+    }
+
+    @CallSuper
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mClients.clear();
+
+        return false;
+    }
+}
diff --git a/android/media/soundtrigger/SoundTriggerManager.java b/android/media/soundtrigger/SoundTriggerManager.java
index 92ffae0..c9ec752 100644
--- a/android/media/soundtrigger/SoundTriggerManager.java
+++ b/android/media/soundtrigger/SoundTriggerManager.java
@@ -15,26 +15,31 @@
  */
 
 package android.media.soundtrigger;
+
 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
 
-import android.app.PendingIntent;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.util.Slog;
 
 import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.util.Preconditions;
 
 import java.util.HashMap;
 import java.util.UUID;
@@ -276,6 +281,40 @@
     }
 
     /**
+     * Starts recognition for the given model id. All events from the model will be sent to the
+     * service.
+     *
+     * <p>This only supports generic sound trigger events. For keyphrase events, please use
+     * {@link android.service.voice.VoiceInteractionService}.
+     *
+     * @param soundModelId Id of the sound model
+     * @param params Opaque data sent to each service call of the service as the {@code params}
+     *               argument
+     * @param detectionService The component name of the service that should receive the events.
+     *                         Needs to subclass {@link SoundTriggerDetectionService}
+     * @param config Configures the recognition
+     *
+     * @return {@link SoundTrigger#STATUS_OK} if the recognition could be started, error code
+     *         otherwise
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
+        @NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
+        Preconditions.checkNotNull(soundModelId);
+        Preconditions.checkNotNull(detectionService);
+        Preconditions.checkNotNull(config);
+
+        try {
+            return mSoundTriggerService.startRecognitionForService(new ParcelUuid(soundModelId),
+                params, detectionService, config);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Stops the given model's recognition.
      * @hide
      */
@@ -324,4 +363,19 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get the amount of time (in milliseconds) an operation of the
+     * {@link ISoundTriggerDetectionService} is allowed to ask.
+     *
+     * @return The amount of time an sound trigger detection service operation is allowed to last
+     */
+    public int getDetectionServiceOperationsTimeout() {
+        try {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT);
+        } catch (Settings.SettingNotFoundException e) {
+            return Integer.MAX_VALUE;
+        }
+    }
 }
diff --git a/android/media/update/ApiLoader.java b/android/media/update/ApiLoader.java
index b928e93..6f82f68 100644
--- a/android/media/update/ApiLoader.java
+++ b/android/media/update/ApiLoader.java
@@ -16,45 +16,68 @@
 
 package android.media.update;
 
-import android.content.res.Resources;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.GuardedBy;
+
+import dalvik.system.PathClassLoader;
+
+import java.io.File;
 
 /**
  * @hide
  */
 public final class ApiLoader {
-    private static Object sMediaLibrary;
+    @GuardedBy("this")
+    private static StaticProvider sMediaUpdatable;
 
     private static final String UPDATE_PACKAGE = "com.android.media.update";
     private static final String UPDATE_CLASS = "com.android.media.update.ApiFactory";
     private static final String UPDATE_METHOD = "initialize";
+    private static final boolean REGISTER_UPDATE_DEPENDENCY = true;
 
     private ApiLoader() { }
 
-    public static StaticProvider getProvider(Context context) {
+    public static StaticProvider getProvider() {
+        if (sMediaUpdatable != null) return sMediaUpdatable;
+
         try {
-            return (StaticProvider) getMediaLibraryImpl(context);
-        } catch (PackageManager.NameNotFoundException | ReflectiveOperationException e) {
+            return getMediaUpdatable();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (NameNotFoundException | ReflectiveOperationException e) {
             throw new RuntimeException(e);
         }
     }
 
     // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode.
-    private static synchronized Object getMediaLibraryImpl(Context context)
-            throws PackageManager.NameNotFoundException, ReflectiveOperationException {
-        if (sMediaLibrary != null) return sMediaLibrary;
+    private static synchronized StaticProvider getMediaUpdatable()
+            throws NameNotFoundException, ReflectiveOperationException, RemoteException {
+        if (sMediaUpdatable != null) return sMediaUpdatable;
 
         // TODO Figure out when to use which package (query media update service)
         int flags = Build.IS_DEBUGGABLE ? 0 : PackageManager.MATCH_FACTORY_ONLY;
-        Context libContext = context.createApplicationContext(
-                context.getPackageManager().getPackageInfo(UPDATE_PACKAGE, flags).applicationInfo,
-                Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
-        sMediaLibrary = libContext.getClassLoader()
-                .loadClass(UPDATE_CLASS)
-                .getMethod(UPDATE_METHOD, Resources.class, Resources.Theme.class)
-                .invoke(null, libContext.getResources(), libContext.getTheme());
-        return sMediaLibrary;
+        flags |= PackageManager.GET_SHARED_LIBRARY_FILES;
+        ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+                UPDATE_PACKAGE, flags, UserHandle.myUserId());
+
+        if (REGISTER_UPDATE_DEPENDENCY) {
+            // Register a dependency to the updatable in order to be killed during updates
+            ActivityManager.getService().addPackageDependency(ai.packageName);
+        }
+
+        ClassLoader classLoader = new PathClassLoader(ai.sourceDir,
+                ai.nativeLibraryDir + File.pathSeparator + System.getProperty("java.library.path"),
+                ClassLoader.getSystemClassLoader().getParent());
+        return sMediaUpdatable = (StaticProvider) classLoader.loadClass(UPDATE_CLASS)
+                .getMethod(UPDATE_METHOD, ApplicationInfo.class).invoke(null, ai);
     }
 }
diff --git a/android/media/update/MediaBrowser2Provider.java b/android/media/update/MediaBrowser2Provider.java
index e48711d..a18701e 100644
--- a/android/media/update/MediaBrowser2Provider.java
+++ b/android/media/update/MediaBrowser2Provider.java
@@ -22,12 +22,13 @@
  * @hide
  */
 public interface MediaBrowser2Provider extends MediaController2Provider {
-    void getBrowserRoot_impl(Bundle rootHints);
+    void getLibraryRoot_impl(Bundle rootHints);
 
-    void subscribe_impl(String parentId, Bundle options);
-    void unsubscribe_impl(String parentId, Bundle options);
+    void subscribe_impl(String parentId, Bundle extras);
+    void unsubscribe_impl(String parentId);
 
     void getItem_impl(String mediaId);
-    void getChildren_impl(String parentId, int page, int pageSize, Bundle options);
-    void search_impl(String query, int page, int pageSize, Bundle extras);
+    void getChildren_impl(String parentId, int page, int pageSize, Bundle extras);
+    void search_impl(String query, Bundle extras);
+    void getSearchResult_impl(String query, int page, int pageSize, Bundle extras);
 }
diff --git a/android/media/update/MediaControlView2Provider.java b/android/media/update/MediaControlView2Provider.java
index 6b38c92..8e69653 100644
--- a/android/media/update/MediaControlView2Provider.java
+++ b/android/media/update/MediaControlView2Provider.java
@@ -16,9 +16,10 @@
 
 package android.media.update;
 
-import android.annotation.SystemApi;
+import android.media.SessionToken2;
 import android.media.session.MediaController;
-import android.view.View;
+import android.util.AttributeSet;
+import android.widget.MediaControlView2;
 
 /**
  * Interface for connecting the public API to an updatable implementation.
@@ -33,15 +34,19 @@
  *
  * @hide
  */
-// TODO @SystemApi
-public interface MediaControlView2Provider extends ViewProvider {
+// TODO: @SystemApi
+public interface MediaControlView2Provider extends ViewGroupProvider {
+    void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
+    void setMediaSessionToken_impl(SessionToken2 token);
+    void setOnFullScreenListener_impl(MediaControlView2.OnFullScreenListener l);
+    /**
+     * @hide TODO: remove
+     */
     void setController_impl(MediaController controller);
-    void show_impl();
-    void show_impl(int timeout);
-    boolean isShowing_impl();
-    void hide_impl();
-    void showSubtitle_impl();
-    void hideSubtitle_impl();
-    void setPrevNextListeners_impl(View.OnClickListener next, View.OnClickListener prev);
-    void setButtonVisibility_impl(int button, boolean visible);
+    /**
+     * @hide
+     */
+    void setButtonVisibility_impl(int button, int visibility);
+    void requestPlayButtonFocus_impl();
 }
diff --git a/android/media/update/MediaController2Provider.java b/android/media/update/MediaController2Provider.java
index c5f6b96..7234f7b 100644
--- a/android/media/update/MediaController2Provider.java
+++ b/android/media/update/MediaController2Provider.java
@@ -17,11 +17,11 @@
 package android.media.update;
 
 import android.app.PendingIntent;
+import android.media.AudioAttributes;
 import android.media.MediaController2.PlaybackInfo;
 import android.media.MediaItem2;
-import android.media.MediaSession2.Command;
-import android.media.MediaSession2.PlaylistParam;
-import android.media.PlaybackState2;
+import android.media.MediaMetadata2;
+import android.media.SessionCommand2;
 import android.media.Rating2;
 import android.media.SessionToken2;
 import android.net.Uri;
@@ -34,12 +34,13 @@
  * @hide
  */
 public interface MediaController2Provider extends TransportControlProvider {
+    void initialize();
+
     void close_impl();
     SessionToken2 getSessionToken_impl();
     boolean isConnected_impl();
 
     PendingIntent getSessionActivity_impl();
-    int getRatingType_impl();
 
     void setVolumeTo_impl(int value, int flags);
     void adjustVolume_impl(int direction, int flags);
@@ -47,18 +48,35 @@
 
     void prepareFromUri_impl(Uri uri, Bundle extras);
     void prepareFromSearch_impl(String query, Bundle extras);
-    void prepareMediaId_impl(String mediaId, Bundle extras);
+    void prepareFromMediaId_impl(String mediaId, Bundle extras);
     void playFromSearch_impl(String query, Bundle extras);
-    void playFromUri_impl(String uri, Bundle extras);
+    void playFromUri_impl(Uri uri, Bundle extras);
     void playFromMediaId_impl(String mediaId, Bundle extras);
+    void fastForward_impl();
+    void rewind_impl();
 
-    void setRating_impl(Rating2 rating);
-    void sendCustomCommand_impl(Command command, Bundle args, ResultReceiver cb);
+    void setRating_impl(String mediaId, Rating2 rating);
+    void sendCustomCommand_impl(SessionCommand2 command, Bundle args, ResultReceiver cb);
     List<MediaItem2> getPlaylist_impl();
+    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
+    MediaMetadata2 getPlaylistMetadata_impl();
+    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
 
-    void removePlaylistItem_impl(MediaItem2 index);
     void addPlaylistItem_impl(int index, MediaItem2 item);
+    void replacePlaylistItem_impl(int index, MediaItem2 item);
+    void removePlaylistItem_impl(MediaItem2 item);
 
-    PlaylistParam getPlaylistParam_impl();
-    PlaybackState2 getPlaybackState_impl();
+    int getPlayerState_impl();
+    long getCurrentPosition_impl();
+    float getPlaybackSpeed_impl();
+    long getBufferedPosition_impl();
+    MediaItem2 getCurrentMediaItem_impl();
+
+    interface PlaybackInfoProvider {
+        int getPlaybackType_impl();
+        AudioAttributes getAudioAttributes_impl();
+        int getControlType_impl();
+        int getMaxVolume_impl();
+        int getCurrentVolume_impl();
+    }
 }
diff --git a/android/media/update/MediaItem2Provider.java b/android/media/update/MediaItem2Provider.java
new file mode 100644
index 0000000..47db22f
--- /dev/null
+++ b/android/media/update/MediaItem2Provider.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaItem2.Builder;
+import android.media.MediaMetadata2;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public interface MediaItem2Provider {
+    Bundle toBundle_impl();
+    String toString_impl();
+    int getFlags_impl();
+    boolean isBrowsable_impl();
+    boolean isPlayable_impl();
+    void setMetadata_impl(MediaMetadata2 metadata);
+    MediaMetadata2 getMetadata_impl();
+    String getMediaId_impl();
+    DataSourceDesc getDataSourceDesc_impl();
+    boolean equals_impl(Object obj);
+
+    interface BuilderProvider {
+        Builder setMediaId_impl(String mediaId);
+        Builder setMetadata_impl(MediaMetadata2 metadata);
+        Builder setDataSourceDesc_impl(DataSourceDesc dataSourceDesc);
+        MediaItem2 build_impl();
+    }
+}
diff --git a/android/media/update/MediaLibraryService2Provider.java b/android/media/update/MediaLibraryService2Provider.java
index dac5784..9a0d693 100644
--- a/android/media/update/MediaLibraryService2Provider.java
+++ b/android/media/update/MediaLibraryService2Provider.java
@@ -17,14 +17,24 @@
 package android.media.update;
 
 import android.media.MediaSession2.ControllerInfo;
-import android.os.Bundle; /**
+import android.os.Bundle;
+
+/**
  * @hide
  */
 public interface MediaLibraryService2Provider extends MediaSessionService2Provider {
     // Nothing new for now
 
     interface MediaLibrarySessionProvider extends MediaSession2Provider {
-        void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle options);
-        void notifyChildrenChanged_impl(String parentId, Bundle options);
+        void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
+                int itemCount, Bundle extras);
+        void notifyChildrenChanged_impl(String parentId, int itemCount, Bundle extras);
+        void notifySearchResultChanged_impl(ControllerInfo controller, String query, int itemCount,
+                Bundle extras);
+    }
+
+    interface LibraryRootProvider {
+        String getRootId_impl();
+        Bundle getExtras_impl();
     }
 }
diff --git a/android/media/update/MediaMetadata2Provider.java b/android/media/update/MediaMetadata2Provider.java
new file mode 100644
index 0000000..22463e9
--- /dev/null
+++ b/android/media/update/MediaMetadata2Provider.java
@@ -0,0 +1,38 @@
+package android.media.update;
+
+import android.graphics.Bitmap;
+import android.media.MediaMetadata2;
+import android.media.MediaMetadata2.Builder;
+import android.media.Rating2;
+import android.os.Bundle;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public interface MediaMetadata2Provider {
+    boolean containsKey_impl(String key);
+    CharSequence getText_impl(String key);
+    String getMediaId_impl();
+    String getString_impl(String key);
+    long getLong_impl(String key);
+    Rating2 getRating_impl(String key);
+    Bundle toBundle_impl();
+    Set<String> keySet_impl();
+    int size_impl();
+    Bitmap getBitmap_impl(String key);
+    float getFloat_impl(String key);
+    Bundle getExtras_impl();
+
+    interface BuilderProvider {
+        Builder putText_impl(String key, CharSequence value);
+        Builder putString_impl(String key, String value);
+        Builder putLong_impl(String key, long value);
+        Builder putRating_impl(String key, Rating2 value);
+        Builder putBitmap_impl(String key, Bitmap value);
+        Builder putFloat_impl(String key, float value);
+        Builder setExtras_impl(Bundle bundle);
+        MediaMetadata2 build_impl();
+    }
+}
diff --git a/android/media/update/MediaPlaylistAgentProvider.java b/android/media/update/MediaPlaylistAgentProvider.java
new file mode 100644
index 0000000..e1522cf
--- /dev/null
+++ b/android/media/update/MediaPlaylistAgentProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent.PlaylistEventCallback;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public interface MediaPlaylistAgentProvider {
+    // final methods of MediaPlaylistAgent
+    void registerPlaylistEventCallback_impl(Executor executor, PlaylistEventCallback callback);
+    void unregisterPlaylistEventCallback_impl(PlaylistEventCallback callback);
+    void notifyPlaylistChanged_impl();
+    void notifyPlaylistMetadataChanged_impl();
+    void notifyShuffleModeChanged_impl();
+    void notifyRepeatModeChanged_impl();
+
+    // public methods of MediaPlaylistAgent
+    List<MediaItem2> getPlaylist_impl();
+    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
+    MediaMetadata2 getPlaylistMetadata_impl();
+    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
+    void addPlaylistItem_impl(int index, MediaItem2 item);
+    void removePlaylistItem_impl(MediaItem2 item);
+    void replacePlaylistItem_impl(int index, MediaItem2 item);
+    void skipToPlaylistItem_impl(MediaItem2 item);
+    void skipToPreviousItem_impl();
+    void skipToNextItem_impl();
+    int getRepeatMode_impl();
+    void setRepeatMode_impl(int repeatMode);
+    int getShuffleMode_impl();
+    void setShuffleMode_impl(int shuffleMode);
+    MediaItem2 getMediaItem_impl(DataSourceDesc dsd);
+}
diff --git a/android/media/update/MediaSession2Provider.java b/android/media/update/MediaSession2Provider.java
index 2a68ad1..4751348 100644
--- a/android/media/update/MediaSession2Provider.java
+++ b/android/media/update/MediaSession2Provider.java
@@ -16,50 +16,117 @@
 
 package android.media.update;
 
-import android.media.AudioAttributes;
+import android.app.PendingIntent;
+import android.media.AudioFocusRequest;
 import android.media.MediaItem2;
+import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
+import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
-import android.media.MediaSession2.Command;
+import android.media.SessionCommand2;
 import android.media.MediaSession2.CommandButton;
-import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.CommandButton.Builder;
+import android.media.SessionCommandGroup2;
 import android.media.MediaSession2.ControllerInfo;
+import android.media.MediaSession2.OnDataSourceMissingHelper;
+import android.media.MediaSession2.SessionCallback;
 import android.media.SessionToken2;
-import android.media.VolumeProvider;
+import android.media.VolumeProvider2;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * @hide
  */
 public interface MediaSession2Provider extends TransportControlProvider {
     void close_impl();
-    void setPlayer_impl(MediaPlayerBase player);
-    void setPlayer_impl(MediaPlayerBase player, VolumeProvider volumeProvider);
+    void updatePlayer_impl(MediaPlayerBase player, MediaPlaylistAgent playlistAgent,
+            VolumeProvider2 volumeProvider);
     MediaPlayerBase getPlayer_impl();
+    MediaMetadata2 getPlaylistMetadata_impl();
+    void updatePlaylistMetadata_impl(MediaMetadata2 metadata);
+    MediaPlaylistAgent getPlaylistAgent_impl();
+    VolumeProvider2 getVolumeProvider_impl();
     SessionToken2 getToken_impl();
     List<ControllerInfo> getConnectedControllers_impl();
     void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);
-    void setAudioAttributes_impl(AudioAttributes attributes);
-    void setAudioFocusRequest_impl(int focusGain);
-
-    void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands);
-    void notifyMetadataChanged_impl();
-    void sendCustomCommand_impl(ControllerInfo controller, Command command, Bundle args,
+    void setAudioFocusRequest_impl(AudioFocusRequest afr);
+    void setAllowedCommands_impl(ControllerInfo controller, SessionCommandGroup2 commands);
+    void sendCustomCommand_impl(ControllerInfo controller, SessionCommand2 command, Bundle args,
             ResultReceiver receiver);
-    void sendCustomCommand_impl(Command command, Bundle args);
-    void setPlaylist_impl(List<MediaItem2> playlist, MediaSession2.PlaylistParam param);
+    void sendCustomCommand_impl(SessionCommand2 command, Bundle args);
+    void addPlaylistItem_impl(int index, MediaItem2 item);
+    void removePlaylistItem_impl(MediaItem2 item);
+    void replacePlaylistItem_impl(int index, MediaItem2 item);
+    List<MediaItem2> getPlaylist_impl();
+    void setPlaylist_impl(List<MediaItem2> list, MediaMetadata2 metadata);
+    MediaItem2 getCurrentPlaylistItem_impl();
+    void notifyError_impl(int errorCode, Bundle extras);
+    int getPlayerState_impl();
+    long getCurrentPosition_impl();
+    long getBufferedPosition_impl();
+    void setOnDataSourceMissingHelper_impl(OnDataSourceMissingHelper helper);
+    void clearOnDataSourceMissingHelper_impl();
 
-    /**
-     * @hide
-     */
+    // TODO(jaewan): Rename and move provider
+    interface CommandProvider {
+        int getCommandCode_impl();
+        String getCustomCommand_impl();
+        Bundle getExtras_impl();
+        Bundle toBundle_impl();
+
+        boolean equals_impl(Object ob);
+        int hashCode_impl();
+    }
+
+    // TODO(jaewan): Rename and move provider
+    interface CommandGroupProvider {
+        void addCommand_impl(SessionCommand2 command);
+        void addAllPredefinedCommands_impl();
+        void removeCommand_impl(SessionCommand2 command);
+        boolean hasCommand_impl(SessionCommand2 command);
+        boolean hasCommand_impl(int code);
+        Set<SessionCommand2> getCommands_impl();
+        Bundle toBundle_impl();
+    }
+
+    interface CommandButtonProvider {
+        SessionCommand2 getCommand_impl();
+        int getIconResId_impl();
+        String getDisplayName_impl();
+        Bundle getExtras_impl();
+        boolean isEnabled_impl();
+
+        interface BuilderProvider {
+            Builder setCommand_impl(SessionCommand2 command);
+            Builder setIconResId_impl(int resId);
+            Builder setDisplayName_impl(String displayName);
+            Builder setEnabled_impl(boolean enabled);
+            Builder setExtras_impl(Bundle extras);
+            CommandButton build_impl();
+        }
+    }
+
     interface ControllerInfoProvider {
         String getPackageName_impl();
         int getUid_impl();
         boolean isTrusted_impl();
         int hashCode_impl();
-        boolean equals_impl(ControllerInfoProvider obj);
+        boolean equals_impl(Object obj);
+        String toString_impl();
+    }
+
+    interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
+        void setPlayer_impl(MediaPlayerBase player);
+        void setPlaylistAgent_impl(MediaPlaylistAgent playlistAgent);
+        void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
+        void setSessionActivity_impl(PendingIntent pi);
+        void setId_impl(String id);
+        void setSessionCallback_impl(Executor executor, C callback);
+        T build_impl();
     }
 }
diff --git a/android/media/update/MediaSessionService2Provider.java b/android/media/update/MediaSessionService2Provider.java
index a6b462b..5eb6254 100644
--- a/android/media/update/MediaSessionService2Provider.java
+++ b/android/media/update/MediaSessionService2Provider.java
@@ -16,10 +16,10 @@
 
 package android.media.update;
 
+import android.app.Notification;
 import android.content.Intent;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2.MediaNotification;
-import android.media.PlaybackState2;
 import android.os.IBinder;
 
 /**
@@ -27,9 +27,14 @@
  */
 public interface MediaSessionService2Provider {
     MediaSession2 getSession_impl();
-    MediaNotification onUpdateNotification_impl(PlaybackState2 state);
+    MediaNotification onUpdateNotification_impl();
 
     // Service
     void onCreate_impl();
     IBinder onBind_impl(Intent intent);
+
+    interface MediaNotificationProvider {
+        int getNotificationId_impl();
+        Notification getNotification_impl();
+    }
 }
diff --git a/android/media/update/ProviderCreator.java b/android/media/update/ProviderCreator.java
new file mode 100644
index 0000000..f5f3e47
--- /dev/null
+++ b/android/media/update/ProviderCreator.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+/** @hide */
+@FunctionalInterface
+public interface ProviderCreator<T, U> {
+    U createProvider(T instance);
+}
diff --git a/android/media/update/Rating2Provider.java b/android/media/update/Rating2Provider.java
new file mode 100644
index 0000000..28ad273
--- /dev/null
+++ b/android/media/update/Rating2Provider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public interface Rating2Provider {
+    String toString_impl();
+    boolean equals_impl(Object obj);
+    int hashCode_impl();
+    Bundle toBundle_impl();
+    boolean isRated_impl();
+    int getRatingStyle_impl();
+    boolean hasHeart_impl();
+    boolean isThumbUp_impl();
+    float getStarRating_impl();
+    float getPercentRating_impl();
+}
diff --git a/android/media/update/SessionToken2Provider.java b/android/media/update/SessionToken2Provider.java
new file mode 100644
index 0000000..95d6ce0
--- /dev/null
+++ b/android/media/update/SessionToken2Provider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public interface SessionToken2Provider {
+    String getPackageName_impl();
+    String getId_imp();
+    int getType_impl();
+    int getUid_impl();
+    Bundle toBundle_impl();
+
+    int hashCode_impl();
+    boolean equals_impl(Object obj);
+    String toString_impl();
+}
diff --git a/android/media/update/StaticProvider.java b/android/media/update/StaticProvider.java
index 7c222c3..8687b80 100644
--- a/android/media/update/StaticProvider.java
+++ b/android/media/update/StaticProvider.java
@@ -17,24 +17,37 @@
 package android.media.update;
 
 import android.annotation.Nullable;
-import android.app.PendingIntent;
+import android.app.Notification;
 import android.content.Context;
-import android.media.IMediaSession2Callback;
 import android.media.MediaBrowser2;
 import android.media.MediaBrowser2.BrowserCallback;
 import android.media.MediaController2;
 import android.media.MediaController2.ControllerCallback;
+import android.media.MediaItem2;
 import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaLibraryService2.MediaLibrarySession;
-import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
-import android.media.MediaPlayerBase;
+import android.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
+import android.media.MediaMetadata2;
+import android.media.MediaPlaylistAgent;
 import android.media.MediaSession2;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.Rating2;
+import android.media.SessionCommand2;
+import android.media.SessionCommandGroup2;
 import android.media.SessionToken2;
-import android.media.VolumeProvider;
-import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
+import android.media.VolumeProvider2;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
+import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
+import android.media.update.MediaSession2Provider.CommandGroupProvider;
+import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
+import android.os.Bundle;
+import android.os.IInterface;
 import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
@@ -46,36 +59,71 @@
  *
  * This interface provides access to constructors and static methods that are otherwise not directly
  * accessible via an implementation object.
- *
  * @hide
  */
-// TODO @SystemApi
 public interface StaticProvider {
-    MediaControlView2Provider createMediaControlView2(
-            MediaControlView2 instance, ViewProvider superProvider);
-    VideoView2Provider createVideoView2(
-            VideoView2 instance, ViewProvider superProvider,
+    MediaControlView2Provider createMediaControlView2(MediaControlView2 instance,
+            ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
+            @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+    VideoView2Provider createVideoView2(VideoView2 instance,
+            ViewGroupProvider superProvider, ViewGroupProvider privateProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
 
-    MediaSession2Provider createMediaSession2(MediaSession2 mediaSession2, Context context,
-            MediaPlayerBase player, String id, Executor callbackExecutor, SessionCallback callback,
-            VolumeProvider volumeProvider, int ratingType,
-            PendingIntent sessionActivity);
-    ControllerInfoProvider createMediaSession2ControllerInfoProvider(
-            MediaSession2.ControllerInfo instance, Context context, int uid, int pid,
-            String packageName, IMediaSession2Callback callback);
-    MediaController2Provider createMediaController2(
-            MediaController2 instance, Context context, SessionToken2 token,
-            ControllerCallback callback, Executor executor);
-    MediaBrowser2Provider createMediaBrowser2(
-            MediaBrowser2 instance, Context context, SessionToken2 token,
-            BrowserCallback callback, Executor executor);
-    MediaSessionService2Provider createMediaSessionService2(
-            MediaSessionService2 instance);
-    MediaSessionService2Provider createMediaLibraryService2(
-            MediaLibraryService2 instance);
-    MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(
-            MediaLibrarySession instance, Context context, MediaPlayerBase player, String id,
-            Executor callbackExecutor, MediaLibrarySessionCallback callback,
-            VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity);
+    CommandProvider createMediaSession2Command(SessionCommand2 instance,
+            int commandCode, String action, Bundle extra);
+    SessionCommand2 fromBundle_MediaSession2Command(Bundle bundle);
+    CommandGroupProvider createMediaSession2CommandGroup(SessionCommandGroup2 instance,
+            SessionCommandGroup2 others);
+    SessionCommandGroup2 fromBundle_MediaSession2CommandGroup(Bundle bundle);
+    ControllerInfoProvider createMediaSession2ControllerInfo(Context context,
+            MediaSession2.ControllerInfo instance, int uid, int pid,
+            String packageName, IInterface callback);
+    CommandButtonProvider.BuilderProvider createMediaSession2CommandButtonBuilder(
+            MediaSession2.CommandButton.Builder instance);
+    BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
+            Context context, MediaSession2.Builder instance);
+
+    MediaController2Provider createMediaController2(Context context, MediaController2 instance,
+            SessionToken2 token, Executor executor, ControllerCallback callback);
+
+    MediaBrowser2Provider createMediaBrowser2(Context context, MediaBrowser2 instance,
+            SessionToken2 token, Executor executor, BrowserCallback callback);
+
+    MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
+    MediaNotificationProvider createMediaSessionService2MediaNotification(
+            MediaNotification mediaNotification, int notificationId, Notification notification);
+
+    MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
+    BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
+        createMediaLibraryService2Builder(
+            MediaLibraryService2 service, MediaLibrarySession.Builder instance,
+            Executor callbackExecutor, MediaLibrarySessionCallback callback);
+    LibraryRootProvider createMediaLibraryService2LibraryRoot(LibraryRoot instance, String rootId,
+            Bundle extras);
+
+    SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
+            String packageName, String serviceName, int uid);
+    SessionToken2 fromBundle_SessionToken2(Bundle bundle);
+
+    MediaItem2Provider.BuilderProvider createMediaItem2Builder(MediaItem2.Builder instance,
+            int flags);
+    MediaItem2 fromBundle_MediaItem2(Bundle bundle);
+
+    VolumeProvider2Provider createVolumeProvider2(VolumeProvider2 instance, int controlType,
+            int maxVolume, int currentVolume);
+
+    MediaMetadata2 fromBundle_MediaMetadata2(Bundle bundle);
+    MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+            MediaMetadata2.Builder instance);
+    MediaMetadata2Provider.BuilderProvider createMediaMetadata2Builder(
+            MediaMetadata2.Builder instance, MediaMetadata2 source);
+
+    Rating2 newUnratedRating_Rating2(int ratingStyle);
+    Rating2 fromBundle_Rating2(Bundle bundle);
+    Rating2 newHeartRating_Rating2(boolean hasHeart);
+    Rating2 newThumbRating_Rating2(boolean thumbIsUp);
+    Rating2 newStarRating_Rating2(int starRatingStyle, float starRating);
+    Rating2 newPercentageRating_Rating2(float percent);
+
+    MediaPlaylistAgentProvider createMediaPlaylistAgent(MediaPlaylistAgent instance);
 }
diff --git a/android/media/update/TransportControlProvider.java b/android/media/update/TransportControlProvider.java
index 5217a9d..d89a88a 100644
--- a/android/media/update/TransportControlProvider.java
+++ b/android/media/update/TransportControlProvider.java
@@ -16,24 +16,24 @@
 
 package android.media.update;
 
-import android.media.MediaPlayerBase;
-import android.media.session.PlaybackState;
-import android.os.Handler;
+import android.media.MediaItem2;
 
 /**
  * @hide
  */
-// TODO(jaewan): SystemApi
 public interface TransportControlProvider {
     void play_impl();
     void pause_impl();
     void stop_impl();
-    void skipToPrevious_impl();
-    void skipToNext_impl();
+    void skipToPreviousItem_impl();
+    void skipToNextItem_impl();
 
     void prepare_impl();
-    void fastForward_impl();
-    void rewind_impl();
     void seekTo_impl(long pos);
-    void setCurrentPlaylistItem_impl(int index);
+    void skipToPlaylistItem_impl(MediaItem2 item);
+
+    int getRepeatMode_impl();
+    void setRepeatMode_impl(int repeatMode);
+    int getShuffleMode_impl();
+    void setShuffleMode_impl(int shuffleMode);
 }
diff --git a/android/media/update/VideoView2Provider.java b/android/media/update/VideoView2Provider.java
index 416ea98..27b436f 100644
--- a/android/media/update/VideoView2Provider.java
+++ b/android/media/update/VideoView2Provider.java
@@ -16,14 +16,26 @@
 
 package android.media.update;
 
+import android.annotation.SystemApi;
 import android.media.AudioAttributes;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
 import android.media.MediaPlayerBase;
+import android.media.SessionToken2;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.media.session.MediaSession;
 import android.net.Uri;
+import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 /**
  * Interface for connecting the public API to an updatable implementation.
@@ -39,35 +51,49 @@
  * @hide
  */
 // TODO @SystemApi
-public interface VideoView2Provider extends ViewProvider {
-    void setMediaControlView2_impl(MediaControlView2 mediaControlView);
+public interface VideoView2Provider extends ViewGroupProvider {
+    void initialize(AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
+    void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs);
+    void setMediaMetadata_impl(MediaMetadata2 metadata);
+    /**
+     * @hide TODO: remove
+     */
+    MediaController getMediaController_impl();
+    SessionToken2 getMediaSessionToken_impl();
     MediaControlView2 getMediaControlView2_impl();
-    void start_impl();
-    void pause_impl();
-    int getDuration_impl();
-    int getCurrentPosition_impl();
-    void seekTo_impl(int msec);
-    boolean isPlaying_impl();
-    int getBufferPercentage_impl();
-    int getAudioSessionId_impl();
-    void showSubtitle_impl();
-    void hideSubtitle_impl();
-    void setFullScreen_impl(boolean fullScreen);
+    MediaMetadata2 getMediaMetadata_impl();
+    void setSubtitleEnabled_impl(boolean enable);
+    boolean isSubtitleEnabled_impl();
+    // TODO: remove setSpeed_impl once MediaController2 is ready.
     void setSpeed_impl(float speed);
-    float getSpeed_impl();
     void setAudioFocusRequest_impl(int focusGain);
     void setAudioAttributes_impl(AudioAttributes attributes);
-    void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player);
     void setVideoPath_impl(String path);
-    void setVideoURI_impl(Uri uri);
-    void setVideoURI_impl(Uri uri, Map<String, String> headers);
+    /**
+     * @hide TODO: remove
+     */
+    void setVideoUri_impl(Uri uri);
+    /**
+     * @hide TODO: remove
+     */
+    void setVideoUri_impl(Uri uri, Map<String, String> headers);
+    void setMediaItem_impl(MediaItem2 mediaItem);
+    void setDataSource_impl(DataSourceDesc dsd);
     void setViewType_impl(int viewType);
     int getViewType_impl();
-    void stopPlayback_impl();
-    void setOnPreparedListener_impl(VideoView2.OnPreparedListener l);
-    void setOnCompletionListener_impl(VideoView2.OnCompletionListener l);
-    void setOnErrorListener_impl(VideoView2.OnErrorListener l);
-    void setOnInfoListener_impl(VideoView2.OnInfoListener l);
+    /**
+     * @hide TODO: remove
+     */
+    void setCustomActions_impl(List<PlaybackState.CustomAction> actionList,
+            Executor executor, VideoView2.OnCustomActionListener listener);
+    /**
+     * @hide
+     */
+    @VisibleForTesting
     void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l);
-    void setFullScreenChangedListener_impl(VideoView2.OnFullScreenChangedListener l);
+    /**
+     * @hide TODO: remove
+     */
+    void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l);
 }
diff --git a/android/media/update/ViewGroupHelper.java b/android/media/update/ViewGroupHelper.java
new file mode 100644
index 0000000..6b4f15d
--- /dev/null
+++ b/android/media/update/ViewGroupHelper.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Helper class for connecting the public API to an updatable implementation.
+ *
+ * @see ViewGroupProvider
+ *
+ * @hide
+ */
+public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup {
+    /** @hide */
+    final public T mProvider;
+
+    /** @hide */
+    public ViewGroupHelper(ProviderCreator<T> creator,
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        mProvider = creator.createProvider(this, new SuperProvider(),
+                new PrivateProvider());
+    }
+
+    /** @hide */
+    // TODO @SystemApi
+    public T getProvider() {
+        return mProvider;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return mProvider.getAccessibilityClassName_impl();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mProvider.onTouchEvent_impl(ev);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent ev) {
+        return mProvider.onTrackballEvent_impl(ev);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        mProvider.onFinishInflate_impl();
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mProvider.setEnabled_impl(enabled);
+    }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        mProvider.onVisibilityAggregated_impl(isVisible);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mProvider.onLayout_impl(changed, left, top, right, bottom);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mProvider.onMeasure_impl(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    protected int getSuggestedMinimumWidth() {
+        return mProvider.getSuggestedMinimumWidth_impl();
+    }
+
+    @Override
+    protected int getSuggestedMinimumHeight() {
+        return mProvider.getSuggestedMinimumHeight_impl();
+    }
+
+    // setMeasuredDimension is final
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        return mProvider.dispatchTouchEvent_impl(ev);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(LayoutParams p) {
+        return mProvider.checkLayoutParams_impl(p);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return mProvider.generateDefaultLayoutParams_impl();
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return mProvider.generateLayoutParams_impl(attrs);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(LayoutParams lp) {
+        return mProvider.generateLayoutParams_impl(lp);
+    }
+
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return mProvider.shouldDelayChildPressedState_impl();
+    }
+
+    @Override
+    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+            int parentHeightMeasureSpec, int heightUsed) {
+        mProvider.measureChildWithMargins_impl(child,
+                parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+    }
+
+    /** @hide */
+    public class SuperProvider implements ViewGroupProvider {
+        @Override
+        public CharSequence getAccessibilityClassName_impl() {
+            return ViewGroupHelper.super.getAccessibilityClassName();
+        }
+
+        @Override
+        public boolean onTouchEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.super.onTouchEvent(ev);
+        }
+
+        @Override
+        public boolean onTrackballEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.super.onTrackballEvent(ev);
+        }
+
+        @Override
+        public void onFinishInflate_impl() {
+            ViewGroupHelper.super.onFinishInflate();
+        }
+
+        @Override
+        public void setEnabled_impl(boolean enabled) {
+            ViewGroupHelper.super.setEnabled(enabled);
+        }
+
+        @Override
+        public void onAttachedToWindow_impl() {
+            ViewGroupHelper.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            ViewGroupHelper.super.onDetachedFromWindow();
+        }
+
+        @Override
+        public void onVisibilityAggregated_impl(boolean isVisible) {
+            ViewGroupHelper.super.onVisibilityAggregated(isVisible);
+        }
+
+        @Override
+        public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+            // abstract method; no super
+        }
+
+        @Override
+        public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+            ViewGroupHelper.super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public int getSuggestedMinimumWidth_impl() {
+            return ViewGroupHelper.super.getSuggestedMinimumWidth();
+        }
+
+        @Override
+        public int getSuggestedMinimumHeight_impl() {
+            return ViewGroupHelper.super.getSuggestedMinimumHeight();
+        }
+
+        @Override
+        public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+            ViewGroupHelper.super.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        public boolean checkLayoutParams_impl(LayoutParams p) {
+            return ViewGroupHelper.super.checkLayoutParams(p);
+        }
+
+        @Override
+        public LayoutParams generateDefaultLayoutParams_impl() {
+            return ViewGroupHelper.super.generateDefaultLayoutParams();
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+            return ViewGroupHelper.super.generateLayoutParams(attrs);
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+            return ViewGroupHelper.super.generateLayoutParams(lp);
+        }
+
+        @Override
+        public boolean shouldDelayChildPressedState_impl() {
+            return ViewGroupHelper.super.shouldDelayChildPressedState();
+        }
+
+        @Override
+        public void measureChildWithMargins_impl(View child,
+                int parentWidthMeasureSpec, int widthUsed,
+                int parentHeightMeasureSpec, int heightUsed) {
+            ViewGroupHelper.super.measureChildWithMargins(child,
+                    parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+        }
+    }
+
+    /** @hide */
+    public class PrivateProvider implements ViewGroupProvider {
+        @Override
+        public CharSequence getAccessibilityClassName_impl() {
+            return ViewGroupHelper.this.getAccessibilityClassName();
+        }
+
+        @Override
+        public boolean onTouchEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.this.onTouchEvent(ev);
+        }
+
+        @Override
+        public boolean onTrackballEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.this.onTrackballEvent(ev);
+        }
+
+        @Override
+        public void onFinishInflate_impl() {
+            ViewGroupHelper.this.onFinishInflate();
+        }
+
+        @Override
+        public void setEnabled_impl(boolean enabled) {
+            ViewGroupHelper.this.setEnabled(enabled);
+        }
+
+        @Override
+        public void onAttachedToWindow_impl() {
+            ViewGroupHelper.this.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            ViewGroupHelper.this.onDetachedFromWindow();
+        }
+
+        @Override
+        public void onVisibilityAggregated_impl(boolean isVisible) {
+            ViewGroupHelper.this.onVisibilityAggregated(isVisible);
+        }
+
+        @Override
+        public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) {
+            ViewGroupHelper.this.onLayout(changed, left, top, right, bottom);
+        }
+
+        @Override
+        public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) {
+            ViewGroupHelper.this.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        @Override
+        public int getSuggestedMinimumWidth_impl() {
+            return ViewGroupHelper.this.getSuggestedMinimumWidth();
+        }
+
+        @Override
+        public int getSuggestedMinimumHeight_impl() {
+            return ViewGroupHelper.this.getSuggestedMinimumHeight();
+        }
+
+        @Override
+        public void setMeasuredDimension_impl(int measuredWidth, int measuredHeight) {
+            ViewGroupHelper.this.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent_impl(MotionEvent ev) {
+            return ViewGroupHelper.this.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        public boolean checkLayoutParams_impl(LayoutParams p) {
+            return ViewGroupHelper.this.checkLayoutParams(p);
+        }
+
+        @Override
+        public LayoutParams generateDefaultLayoutParams_impl() {
+            return ViewGroupHelper.this.generateDefaultLayoutParams();
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
+            return ViewGroupHelper.this.generateLayoutParams(attrs);
+        }
+
+        @Override
+        public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
+            return ViewGroupHelper.this.generateLayoutParams(lp);
+        }
+
+        @Override
+        public boolean shouldDelayChildPressedState_impl() {
+            return ViewGroupHelper.this.shouldDelayChildPressedState();
+        }
+
+        @Override
+        public void measureChildWithMargins_impl(View child,
+                int parentWidthMeasureSpec, int widthUsed,
+                int parentHeightMeasureSpec, int heightUsed) {
+            ViewGroupHelper.this.measureChildWithMargins(child,
+                    parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
+        }
+    }
+
+        /** @hide */
+    @FunctionalInterface
+    public interface ProviderCreator<T extends ViewGroupProvider> {
+        T createProvider(ViewGroupHelper<T> instance, ViewGroupProvider superProvider,
+                ViewGroupProvider privateProvider);
+    }
+}
diff --git a/android/media/update/ViewGroupProvider.java b/android/media/update/ViewGroupProvider.java
new file mode 100644
index 0000000..67e8cea
--- /dev/null
+++ b/android/media/update/ViewGroupProvider.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.annotation.SystemApi;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+/**
+ * Interface for connecting the public API to an updatable implementation.
+ *
+ * Each instance object is connected to one corresponding updatable object which implements the
+ * runtime behavior of that class. There should a corresponding provider method for all public
+ * methods.
+ *
+ * All methods behave as per their namesake in the public API.
+ *
+ * @see android.view.View
+ *
+ * @hide
+ */
+// TODO @SystemApi
+public interface ViewGroupProvider {
+    // View methods
+    void onAttachedToWindow_impl();
+    void onDetachedFromWindow_impl();
+    CharSequence getAccessibilityClassName_impl();
+    boolean onTouchEvent_impl(MotionEvent ev);
+    boolean onTrackballEvent_impl(MotionEvent ev);
+    void onFinishInflate_impl();
+    void setEnabled_impl(boolean enabled);
+    void onVisibilityAggregated_impl(boolean isVisible);
+    void onLayout_impl(boolean changed, int left, int top, int right, int bottom);
+    void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec);
+    int getSuggestedMinimumWidth_impl();
+    int getSuggestedMinimumHeight_impl();
+    void setMeasuredDimension_impl(int measuredWidth, int measuredHeight);
+    boolean dispatchTouchEvent_impl(MotionEvent ev);
+
+    // ViewGroup methods
+    boolean checkLayoutParams_impl(LayoutParams p);
+    LayoutParams generateDefaultLayoutParams_impl();
+    LayoutParams generateLayoutParams_impl(AttributeSet attrs);
+    LayoutParams generateLayoutParams_impl(LayoutParams lp);
+    boolean shouldDelayChildPressedState_impl();
+    void measureChildWithMargins_impl(View child, int parentWidthMeasureSpec, int widthUsed,
+        int parentHeightMeasureSpec, int heightUsed);
+
+    // ViewManager methods
+    // ViewParent methods
+}
diff --git a/android/media/update/ViewProvider.java b/android/media/update/ViewProvider.java
deleted file mode 100644
index 78c5b36..0000000
--- a/android/media/update/ViewProvider.java
+++ /dev/null
@@ -1,49 +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.media.update;
-
-import android.annotation.SystemApi;
-import android.graphics.Canvas;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-/**
- * Interface for connecting the public API to an updatable implementation.
- *
- * Each instance object is connected to one corresponding updatable object which implements the
- * runtime behavior of that class. There should a corresponding provider method for all public
- * methods.
- *
- * All methods behave as per their namesake in the public API.
- *
- * @see android.view.View
- *
- * @hide
- */
-// TODO @SystemApi
-public interface ViewProvider {
-    // TODO Add more (all?) methods from View
-    void onAttachedToWindow_impl();
-    void onDetachedFromWindow_impl();
-    CharSequence getAccessibilityClassName_impl();
-    boolean onTouchEvent_impl(MotionEvent ev);
-    boolean onTrackballEvent_impl(MotionEvent ev);
-    boolean onKeyDown_impl(int keyCode, KeyEvent event);
-    void onFinishInflate_impl();
-    boolean dispatchKeyEvent_impl(KeyEvent event);
-    void setEnabled_impl(boolean enabled);
-}
diff --git a/android/media/update/VolumeProvider2Provider.java b/android/media/update/VolumeProvider2Provider.java
new file mode 100644
index 0000000..5b5cfd3
--- /dev/null
+++ b/android/media/update/VolumeProvider2Provider.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.update;
+
+/**
+ * @hide
+ */
+public interface VolumeProvider2Provider {
+    int getControlType_impl();
+    int getMaxVolume_impl();
+    int getCurrentVolume_impl();
+    void setCurrentVolume_impl(int currentVolume);
+}
diff --git a/android/metrics/LogMaker.java b/android/metrics/LogMaker.java
index 2bb43bd..e84f913 100644
--- a/android/metrics/LogMaker.java
+++ b/android/metrics/LogMaker.java
@@ -103,7 +103,7 @@
      * @hide // TODO Expose in the future?  Too late for O.
      */
     public LogMaker setLatency(long milliseconds) {
-        entries.put(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, milliseconds);
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_LATENCY_MILLIS, milliseconds);
         return this;
     }
 
diff --git a/android/mtp/MtpDatabase.java b/android/mtp/MtpDatabase.java
index a647dcc..7976f67 100644
--- a/android/mtp/MtpDatabase.java
+++ b/android/mtp/MtpDatabase.java
@@ -243,11 +243,11 @@
         }
     };
 
-    public MtpDatabase(Context context, Context userContext, String volumeName,
+    public MtpDatabase(Context context, String volumeName,
             String[] subDirectories) {
         native_setup();
         mContext = context;
-        mMediaProvider = userContext.getContentResolver()
+        mMediaProvider = context.getContentResolver()
                 .acquireContentProviderClient(MediaStore.AUTHORITY);
         mVolumeName = volumeName;
         mObjectsUri = Files.getMtpObjectsUri(volumeName);
@@ -292,7 +292,9 @@
         mCloseGuard.close();
         if (mClosed.compareAndSet(false, true)) {
             mMediaScanner.close();
-            mMediaProvider.close();
+            if (mMediaProvider != null) {
+                mMediaProvider.close();
+            }
             native_finalize();
         }
     }
@@ -312,7 +314,9 @@
     public void addStorage(StorageVolume storage) {
         MtpStorage mtpStorage = mManager.addMtpStorage(storage);
         mStorageMap.put(storage.getPath(), mtpStorage);
-        mServer.addStorage(mtpStorage);
+        if (mServer != null) {
+            mServer.addStorage(mtpStorage);
+        }
     }
 
     public void removeStorage(StorageVolume storage) {
@@ -320,7 +324,9 @@
         if (mtpStorage == null) {
             return;
         }
-        mServer.removeStorage(mtpStorage);
+        if (mServer != null) {
+            mServer.removeStorage(mtpStorage);
+        }
         mManager.removeMtpStorage(mtpStorage);
         mStorageMap.remove(storage.getPath());
     }
diff --git a/android/mtp/MtpServer.java b/android/mtp/MtpServer.java
index f2b1106..8af5ff7 100644
--- a/android/mtp/MtpServer.java
+++ b/android/mtp/MtpServer.java
@@ -18,6 +18,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.io.FileDescriptor;
+
 /**
  * Java wrapper for MTP/PTP support as USB responder.
  * {@hide}
@@ -34,6 +36,7 @@
 
     public MtpServer(
             MtpDatabase database,
+            FileDescriptor controlFd,
             boolean usePtp,
             Runnable onTerminate,
             String deviceInfoManufacturer,
@@ -44,6 +47,7 @@
         mOnTerminate = Preconditions.checkNotNull(onTerminate);
         native_setup(
                 database,
+                controlFd,
                 usePtp,
                 deviceInfoManufacturer,
                 deviceInfoModel,
@@ -92,6 +96,7 @@
     public static native final void native_configure(boolean usePtp);
     private native final void native_setup(
             MtpDatabase database,
+            FileDescriptor controlFd,
             boolean usePtp,
             String deviceInfoManufacturer,
             String deviceInfoModel,
diff --git a/android/net/ConnectivityManager.java b/android/net/ConnectivityManager.java
index 166342d..80b1c3d 100644
--- a/android/net/ConnectivityManager.java
+++ b/android/net/ConnectivityManager.java
@@ -112,8 +112,14 @@
      * <p/>
      * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
      * is set to {@code true} if there are no connected networks at all.
+     *
+     * @deprecated apps should use the more versatile {@link #requestNetwork},
+     *             {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
+     *             functions instead for faster and more detailed updates about the network
+     *             changes they care about.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
@@ -447,133 +453,177 @@
     public static final int TYPE_NONE        = -1;
 
     /**
-     * The Mobile data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route)
+     * A Mobile data connection. Devices may support more than one.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_MOBILE      = 0;
+
     /**
-     * The WIFI data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * A WIFI data connection. Devices may support more than one.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_WIFI        = 1;
+
     /**
      * An MMS-specific Mobile data connection.  This network type may use the
      * same network interface as {@link #TYPE_MOBILE} or it may use a different
      * one.  This is used by applications needing to talk to the carrier's
      * Multimedia Messaging Service servers.
      *
-     * @deprecated Applications should instead use
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
      */
     @Deprecated
     public static final int TYPE_MOBILE_MMS  = 2;
+
     /**
      * A SUPL-specific Mobile data connection.  This network type may use the
      * same network interface as {@link #TYPE_MOBILE} or it may use a different
      * one.  This is used by applications needing to talk to the carrier's
      * Secure User Plane Location servers for help locating the device.
      *
-     * @deprecated Applications should instead use
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
      */
     @Deprecated
     public static final int TYPE_MOBILE_SUPL = 3;
+
     /**
      * A DUN-specific Mobile data connection.  This network type may use the
      * same network interface as {@link #TYPE_MOBILE} or it may use a different
      * one.  This is sometimes by the system when setting up an upstream connection
      * for tethering so that the carrier is aware of DUN traffic.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
+     *         provides the {@link NetworkCapabilities#NET_CAPABILITY_DUN} capability.
      */
+    @Deprecated
     public static final int TYPE_MOBILE_DUN  = 4;
+
     /**
      * A High Priority Mobile data connection.  This network type uses the
      * same network interface as {@link #TYPE_MOBILE} but the routing setup
      * is different.
      *
-     * @deprecated Applications should instead use
-     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
-     *         uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
     @Deprecated
     public static final int TYPE_MOBILE_HIPRI = 5;
+
     /**
-     * The WiMAX data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * A WiMAX data connection.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_WIMAX       = 6;
 
     /**
-     * The Bluetooth data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * A Bluetooth data connection.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_BLUETOOTH   = 7;
 
     /**
      * Dummy data connection.  This should not be used on shipping devices.
+     * @deprecated This is not used any more.
      */
+    @Deprecated
     public static final int TYPE_DUMMY       = 8;
 
     /**
-     * The Ethernet data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * An Ethernet data connection.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_ETHERNET    = 9;
 
     /**
      * Over the air Administration.
+     * @deprecated Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_FOTA = 10;
 
     /**
      * IP Multimedia Subsystem.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IMS} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_IMS  = 11;
 
     /**
      * Carrier Branded Services.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_CBS} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_CBS  = 12;
 
     /**
      * A Wi-Fi p2p connection. Only requesting processes will have access to
      * the peers connected.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_WIFI_P2P} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_WIFI_P2P    = 13;
 
     /**
      * The network to use for initially attaching to the network
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IA} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_IA = 14;
 
     /**
      * Emergency PDN connection for emergency services.  This
      * may include IMS and MMS in emergency situations.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_EIMS} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_EMERGENCY = 15;
 
     /**
      * The network that uses proxy to achieve connectivity.
+     * @deprecated Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_PROXY = 16;
 
     /**
      * A virtual network using one or more native bearers.
      * It may or may not be providing security services.
+     * @deprecated Applications should use {@link NetworkCapabilities#TRANSPORT_VPN} instead.
      */
+    @Deprecated
     public static final int TYPE_VPN = 17;
 
     /** {@hide} */
@@ -680,8 +730,10 @@
      * @param type the type needing naming
      * @return a String for the given type, or a string version of the type ("87")
      * if no name is known.
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static String getNetworkTypeName(int type) {
         switch (type) {
           case TYPE_NONE:
@@ -732,8 +784,10 @@
      * This should be replaced in the future by a network property.
      * @param networkType the type to check
      * @return a boolean - {@code true} if uses cellular network, else {@code false}
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static boolean isNetworkTypeMobile(int networkType) {
         switch (networkType) {
             case TYPE_MOBILE:
@@ -755,8 +809,10 @@
     /**
      * Checks if the given network type is backed by a Wi-Fi radio.
      *
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * @hide
      */
+    @Deprecated
     public static boolean isNetworkTypeWifi(int networkType) {
         switch (networkType) {
             case TYPE_WIFI:
@@ -805,6 +861,10 @@
      * You should always check {@link NetworkInfo#isConnected()} before initiating
      * network traffic. This may return {@code null} when there is no default
      * network.
+     * Note that if the default network is a VPN, this method will return the
+     * NetworkInfo for one of its underlying networks instead, or null if the
+     * VPN agent did not specify any. Apps interested in learning about VPNs
+     * should use {@link #getNetworkInfo(android.net.Network)} instead.
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
@@ -962,7 +1022,11 @@
      *        which you're interested.
      * @return a {@link NetworkInfo} object for the requested
      *        network type or {@code null} if the type is not
-     *        supported by the device.
+     *        supported by the device. If {@code networkType} is
+     *        TYPE_VPN and a VPN is active for the calling app,
+     *        then this method will try to return one of the
+     *        underlying networks for the VPN or null if the
+     *        VPN agent didn't specify any.
      *
      * @deprecated This method does not support multiple connected networks
      *             of the same type. Use {@link #getAllNetworks} and
@@ -1523,6 +1587,8 @@
      * IllegalArgumentException if no mapping from the legacy type to
      * NetworkCapabilities is known.
      *
+     * @deprecated Types are deprecated. Use {@link NetworkCallback} or {@link NetworkRequest}
+     *     to find the network instead.
      * @hide
      */
     public static NetworkCapabilities networkCapabilitiesForType(int type) {
@@ -1596,8 +1662,12 @@
         /** The hardware returned an error. */
         public static final int ERROR_HARDWARE_ERROR = -31;
 
+        /** The NAT-T destination port for IPsec */
         public static final int NATT_PORT = 4500;
 
+        /** The minimum interval in seconds between keepalive packet transmissions */
+        public static final int MIN_INTERVAL = 10;
+
         private final Network mNetwork;
         private final PacketKeepaliveCallback mCallback;
         private final Looper mLooper;
@@ -1915,13 +1985,6 @@
      * services.jar, possibly in com.android.server.net. */
 
     /** {@hide} */
-    public static final boolean checkChangePermission(Context context) {
-        int uid = Binder.getCallingUid();
-        return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
-                .getPackageNameForUid(context, uid), false /* throwException */);
-    }
-
-    /** {@hide} */
     public static final void enforceChangePermission(Context context) {
         int uid = Binder.getCallingUid();
         Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
@@ -2370,6 +2433,7 @@
      *
      * @param networkType The type of network you want to report on
      * @param percentage The quality of the connection 0 is bad, 100 is good
+     * @deprecated Types are deprecated. Use {@link #reportNetworkConnectivity} instead.
      * {@hide}
      */
     public void reportInetCondition(int networkType, int percentage) {
@@ -2501,9 +2565,10 @@
      *
      * @param networkType The network type we'd like to check
      * @return {@code true} if supported, else {@code false}
-     *
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * @hide
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     public boolean isNetworkSupported(int networkType) {
         try {
@@ -2656,7 +2721,7 @@
      * A {@code NetworkCallback} is registered by calling
      * {@link #requestNetwork(NetworkRequest, NetworkCallback)},
      * {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)},
-     * or {@link #registerDefaultNetworkCallback(NetworkCallback). A {@code NetworkCallback} is
+     * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is
      * unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}.
      * A {@code NetworkCallback} should be registered at most once at any time.
      * A {@code NetworkCallback} that has been unregistered can be registered again.
@@ -2685,6 +2750,32 @@
          * satisfying the request changes.
          *
          * @param network The {@link Network} of the satisfying network.
+         * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
+         * @param linkProperties The {@link LinkProperties} of the satisfying network.
+         * @hide
+         */
+        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+                LinkProperties linkProperties) {
+            // Internally only this method is called when a new network is available, and
+            // it calls the callback in the same way and order that older versions used
+            // to call so as not to change the behavior.
+            onAvailable(network);
+            if (!networkCapabilities.hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
+                onNetworkSuspended(network);
+            }
+            onCapabilitiesChanged(network, networkCapabilities);
+            onLinkPropertiesChanged(network, linkProperties);
+        }
+
+        /**
+         * Called when the framework connects and has declared a new network ready for use.
+         * This callback may be called more than once if the {@link Network} that is
+         * satisfying the request changes. This will always immediately be followed by a
+         * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
+         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+         *
+         * @param network The {@link Network} of the satisfying network.
          */
         public void onAvailable(Network network) {}
 
@@ -2727,7 +2818,8 @@
          * changes capabilities but still satisfies the stated need.
          *
          * @param network The {@link Network} whose capabilities have changed.
-         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
+         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+         *                            network.
          */
         public void onCapabilitiesChanged(Network network,
                 NetworkCapabilities networkCapabilities) {}
@@ -2743,7 +2835,7 @@
 
         /**
          * Called when the network the framework connected to for this request
-         * goes into {@link NetworkInfo.DetailedState.SUSPENDED}.
+         * goes into {@link NetworkInfo.State#SUSPENDED}.
          * This generally means that while the TCP connections are still live,
          * temporarily network data fails to transfer.  Specifically this is used
          * on cellular networks to mask temporary outages when driving through
@@ -2754,9 +2846,8 @@
 
         /**
          * Called when the network the framework connected to for this request
-         * returns from a {@link NetworkInfo.DetailedState.SUSPENDED} state.
-         * This should always be preceeded by a matching {@code onNetworkSuspended}
-         * call.
+         * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
+         * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
          * @hide
          */
         public void onNetworkResumed(Network network) {}
@@ -2865,7 +2956,9 @@
                     break;
                 }
                 case CALLBACK_AVAILABLE: {
-                    callback.onAvailable(network);
+                    NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+                    LinkProperties lp = getObject(message, LinkProperties.class);
+                    callback.onAvailable(network, cap, lp);
                     break;
                 }
                 case CALLBACK_LOSING: {
@@ -3766,8 +3859,8 @@
 
     /**
      * The network watchlist is a list of domains and IP addresses that are associated with
-     * potentially harmful apps. This method returns the hash of the watchlist currently
-     * used by the system.
+     * potentially harmful apps. This method returns the SHA-256 of the watchlist config file
+     * currently used by the system for validation purposes.
      *
      * @return Hash of network watchlist config file. Null if config does not exist.
      */
diff --git a/android/net/EthernetManager.java b/android/net/EthernetManager.java
index 31a3096..ecccda5 100644
--- a/android/net/EthernetManager.java
+++ b/android/net/EthernetManager.java
@@ -18,9 +18,6 @@
 
 import android.annotation.SystemService;
 import android.content.Context;
-import android.net.IEthernetManager;
-import android.net.IEthernetServiceListener;
-import android.net.IpConfiguration;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
@@ -45,18 +42,18 @@
             if (msg.what == MSG_AVAILABILITY_CHANGED) {
                 boolean isAvailable = (msg.arg1 == 1);
                 for (Listener listener : mListeners) {
-                    listener.onAvailabilityChanged(isAvailable);
+                    listener.onAvailabilityChanged((String) msg.obj, isAvailable);
                 }
             }
         }
     };
-    private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+    private final ArrayList<Listener> mListeners = new ArrayList<>();
     private final IEthernetServiceListener.Stub mServiceListener =
             new IEthernetServiceListener.Stub() {
                 @Override
-                public void onAvailabilityChanged(boolean isAvailable) {
+                public void onAvailabilityChanged(String iface, boolean isAvailable) {
                     mHandler.obtainMessage(
-                            MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
+                            MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, iface).sendToTarget();
                 }
             };
 
@@ -66,9 +63,10 @@
     public interface Listener {
         /**
          * Called when Ethernet port's availability is changed.
-         * @param isAvailable {@code true} if one or more Ethernet port exists.
+         * @param iface Ethernet interface name
+         * @param isAvailable {@code true} if Ethernet port exists.
          */
-        public void onAvailabilityChanged(boolean isAvailable);
+        void onAvailabilityChanged(String iface, boolean isAvailable);
     }
 
     /**
@@ -86,9 +84,9 @@
      * Get Ethernet configuration.
      * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
      */
-    public IpConfiguration getConfiguration() {
+    public IpConfiguration getConfiguration(String iface) {
         try {
-            return mService.getConfiguration();
+            return mService.getConfiguration(iface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -97,21 +95,29 @@
     /**
      * Set Ethernet configuration.
      */
-    public void setConfiguration(IpConfiguration config) {
+    public void setConfiguration(String iface, IpConfiguration config) {
         try {
-            mService.setConfiguration(config);
+            mService.setConfiguration(iface, config);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Indicates whether the system currently has one or more
-     * Ethernet interfaces.
+     * Indicates whether the system currently has one or more Ethernet interfaces.
      */
     public boolean isAvailable() {
+        return getAvailableInterfaces().length > 0;
+    }
+
+    /**
+     * Indicates whether the system has given interface.
+     *
+     * @param iface Ethernet interface name
+     */
+    public boolean isAvailable(String iface) {
         try {
-            return mService.isAvailable();
+            return mService.isAvailable(iface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -137,6 +143,17 @@
     }
 
     /**
+     * Returns an array of available Ethernet interface names.
+     */
+    public String[] getAvailableInterfaces() {
+        try {
+            return mService.getAvailableInterfaces();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Removes a listener.
      * @param listener A {@link Listener} to remove.
      * @throws IllegalArgumentException If the listener is null.
diff --git a/android/net/IpPrefix.java b/android/net/IpPrefix.java
index 6e2654e..4631c56 100644
--- a/android/net/IpPrefix.java
+++ b/android/net/IpPrefix.java
@@ -25,6 +25,7 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.Comparator;
 
 /**
  * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
@@ -187,6 +188,20 @@
     }
 
     /**
+     * Returns whether the specified prefix is entirely contained in this prefix.
+     *
+     * Note this is mathematical inclusion, so a prefix is always contained within itself.
+     * @param otherPrefix the prefix to test
+     * @hide
+     */
+    public boolean containsPrefix(IpPrefix otherPrefix) {
+        if (otherPrefix.getPrefixLength() < prefixLength) return false;
+        final byte[] otherAddress = otherPrefix.getRawAddress();
+        NetworkUtils.maskRawAddress(otherAddress, prefixLength);
+        return Arrays.equals(otherAddress, address);
+    }
+
+    /**
      * @hide
      */
     public boolean isIPv6() {
@@ -230,6 +245,38 @@
     }
 
     /**
+     * Returns a comparator ordering IpPrefixes by length, shorter to longer.
+     * Contents of the address will break ties.
+     * @hide
+     */
+    public static Comparator<IpPrefix> lengthComparator() {
+        return new Comparator<IpPrefix>() {
+            @Override
+            public int compare(IpPrefix prefix1, IpPrefix prefix2) {
+                if (prefix1.isIPv4()) {
+                    if (prefix2.isIPv6()) return -1;
+                } else {
+                    if (prefix2.isIPv4()) return 1;
+                }
+                final int p1len = prefix1.getPrefixLength();
+                final int p2len = prefix2.getPrefixLength();
+                if (p1len < p2len) return -1;
+                if (p2len < p1len) return 1;
+                final byte[] a1 = prefix1.address;
+                final byte[] a2 = prefix2.address;
+                final int len = a1.length < a2.length ? a1.length : a2.length;
+                for (int i = 0; i < len; ++i) {
+                    if (a1[i] < a2[i]) return -1;
+                    if (a1[i] > a2[i]) return 1;
+                }
+                if (a2.length < len) return 1;
+                if (a1.length < len) return -1;
+                return 0;
+            }
+        };
+    }
+
+    /**
      * Implement the Parcelable interface.
      */
     public static final Creator<IpPrefix> CREATOR =
diff --git a/android/net/IpSecAlgorithm.java b/android/net/IpSecAlgorithm.java
index c69a4d4..8034bb6 100644
--- a/android/net/IpSecAlgorithm.java
+++ b/android/net/IpSecAlgorithm.java
@@ -38,6 +38,13 @@
     private static final String TAG = "IpSecAlgorithm";
 
     /**
+     * Null cipher.
+     *
+     * @hide
+     */
+    public static final String CRYPT_NULL = "ecb(cipher_null)";
+
+    /**
      * AES-CBC Encryption/Ciphering Algorithm.
      *
      * <p>Valid lengths for this key are {128, 192, 256}.
@@ -49,7 +56,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 128 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
      */
     public static final String AUTH_HMAC_MD5 = "hmac(md5)";
 
@@ -58,7 +66,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 160 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
      */
     public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
 
@@ -66,7 +75,8 @@
      * SHA256 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 256 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
      */
     public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
 
@@ -74,7 +84,8 @@
      * SHA384 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 384 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
      */
     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
 
@@ -82,7 +93,8 @@
      * SHA512 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 512 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
      */
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
@@ -105,6 +117,7 @@
         AUTH_HMAC_MD5,
         AUTH_HMAC_SHA1,
         AUTH_HMAC_SHA256,
+        AUTH_HMAC_SHA384,
         AUTH_HMAC_SHA512,
         AUTH_CRYPT_AES_GCM
     })
@@ -119,11 +132,14 @@
      * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
      * defined as constants in this class.
      *
+     * <p>For algorithms that produce an integrity check value, the truncation length is a required
+     * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
+     *
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      */
-    public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key) {
-        this(algorithm, key, key.length * 8);
+    public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
+        this(algorithm, key, 0);
     }
 
     /**
@@ -137,7 +153,8 @@
      * @param key key padded to a multiple of 8 bits.
      * @param truncLenBits number of bits of output hash to use.
      */
-    public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
+    public IpSecAlgorithm(
+            @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
         mName = algorithm;
         mKey = key.clone();
         mTruncLenBits = truncLenBits;
@@ -145,11 +162,13 @@
     }
 
     /** Get the algorithm name */
+    @NonNull
     public String getName() {
         return mName;
     }
 
     /** Get the key for this algorithm */
+    @NonNull
     public byte[] getKey() {
         return mKey.clone();
     }
@@ -218,6 +237,7 @@
             case AUTH_CRYPT_AES_GCM:
                 // The keying material for GCM is a key plus a 32-bit salt
                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
                 break;
             default:
                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
@@ -263,6 +283,7 @@
     }
 
     @Override
+    @NonNull
     public String toString() {
         return new StringBuilder()
                 .append("{mName=")
diff --git a/android/net/IpSecConfig.java b/android/net/IpSecConfig.java
index 6a262e2..8599f47 100644
--- a/android/net/IpSecConfig.java
+++ b/android/net/IpSecConfig.java
@@ -218,6 +218,25 @@
     @VisibleForTesting
     public IpSecConfig() {}
 
+    /** Copy constructor */
+    @VisibleForTesting
+    public IpSecConfig(IpSecConfig c) {
+        mMode = c.mMode;
+        mSourceAddress = c.mSourceAddress;
+        mDestinationAddress = c.mDestinationAddress;
+        mNetwork = c.mNetwork;
+        mSpiResourceId = c.mSpiResourceId;
+        mEncryption = c.mEncryption;
+        mAuthentication = c.mAuthentication;
+        mAuthenticatedEncryption = c.mAuthenticatedEncryption;
+        mEncapType = c.mEncapType;
+        mEncapSocketResourceId = c.mEncapSocketResourceId;
+        mEncapRemotePort = c.mEncapRemotePort;
+        mNattKeepaliveInterval = c.mNattKeepaliveInterval;
+        mMarkValue = c.mMarkValue;
+        mMarkMask = c.mMarkMask;
+    }
+
     private IpSecConfig(Parcel in) {
         mMode = in.readInt();
         mSourceAddress = in.readString();
diff --git a/android/net/IpSecManager.java b/android/net/IpSecManager.java
index 24a078f..1525508 100644
--- a/android/net/IpSecManager.java
+++ b/android/net/IpSecManager.java
@@ -19,6 +19,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -57,14 +58,18 @@
     private static final String TAG = "IpSecManager";
 
     /**
-     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
-     * applies to traffic towards the host.
+     * Used when applying a transform to direct traffic through an {@link IpSecTransform}
+     * towards the host.
+     *
+     * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
      */
     public static final int DIRECTION_IN = 0;
 
     /**
-     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
-     * applies to traffic from the host.
+     * Used when applying a transform to direct traffic through an {@link IpSecTransform}
+     * away from the host.
+     *
+     * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
      */
     public static final int DIRECTION_OUT = 1;
 
@@ -248,8 +253,9 @@
      * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
      *     currently allocated for this user
      */
-    public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
-            throws ResourceUnavailableException {
+    @NonNull
+    public SecurityParameterIndex allocateSecurityParameterIndex(
+                @NonNull InetAddress destinationAddress) throws ResourceUnavailableException {
         try {
             return new SecurityParameterIndex(
                     mService,
@@ -268,15 +274,17 @@
      *
      * @param destinationAddress the destination address for traffic bearing the requested SPI.
      *     For inbound traffic, the destination should be an address currently assigned on-device.
-     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI
+     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is
+     *     reserved and may not be used. See RFC 4303 Section 2.1.
      * @return the reserved SecurityParameterIndex
      * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
      *     currently allocated for this user
      * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
      *     reserved
      */
+    @NonNull
     public SecurityParameterIndex allocateSecurityParameterIndex(
-            InetAddress destinationAddress, int requestedSpi)
+            @NonNull InetAddress destinationAddress, int requestedSpi)
             throws SpiUnavailableException, ResourceUnavailableException {
         if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
             throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
@@ -298,22 +306,36 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
+     * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+     * applied transform before completion of graceful shutdown may result in the shutdown sequence
+     * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+     * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+     * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+     * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+     * sufficient to ensure shutdown.
+     *
+     * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+     * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+     * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+     * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+     *
      * <h4>Rekey Procedure</h4>
      *
-     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
-     * inbound traffic on the old transform will continue to be decrypted until that transform is
-     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
-     * where both transforms are valid until both endpoints are using the new transform and all
-     * in-flight packets have been received.
+     * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
+     * will be removed and the new transform will take effect immediately, sending all traffic on
+     * the new transform; however, when applying a transform in the inbound direction, traffic
+     * on the old transform will continue to be decrypted and delivered until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
+     * procedures where both transforms are valid until both endpoints are using the new transform
+     * and all in-flight packets have been received.
      *
      * @param socket a stream socket
-     * @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
+     * @param direction the direction in which the transform should be applied
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            Socket socket, int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull Socket socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -333,19 +355,21 @@
      *
      * <h4>Rekey Procedure</h4>
      *
-     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
-     * inbound traffic on the old transform will continue to be decrypted until that transform is
-     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
-     * where both transforms are valid until both endpoints are using the new transform and all
-     * in-flight packets have been received.
+     * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
+     * will be removed and the new transform will take effect immediately, sending all traffic on
+     * the new transform; however, when applying a transform in the inbound direction, traffic
+     * on the old transform will continue to be decrypted and delivered until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
+     * procedures where both transforms are valid until both endpoints are using the new transform
+     * and all in-flight packets have been received.
      *
      * @param socket a datagram socket
-     * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
+     * @param direction the direction in which the transform should be applied
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
+    public void applyTransportModeTransform(@NonNull DatagramSocket socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -363,22 +387,36 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
+     * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+     * applied transform before completion of graceful shutdown may result in the shutdown sequence
+     * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+     * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+     * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+     * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+     * sufficient to ensure shutdown.
+     *
+     * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+     * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+     * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+     * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+     *
      * <h4>Rekey Procedure</h4>
      *
-     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
-     * inbound traffic on the old transform will continue to be decrypted until that transform is
-     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
-     * where both transforms are valid until both endpoints are using the new transform and all
-     * in-flight packets have been received.
+     * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
+     * will be removed and the new transform will take effect immediately, sending all traffic on
+     * the new transform; however, when applying a transform in the inbound direction, traffic
+     * on the old transform will continue to be decrypted and delivered until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
+     * procedures where both transforms are valid until both endpoints are using the new transform
+     * and all in-flight packets have been received.
      *
      * @param socket a socket file descriptor
-     * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
+     * @param direction the direction in which the transform should be applied
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            FileDescriptor socket, int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull FileDescriptor socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
         // constructor takes control and closes the user's FD when we exit the method.
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
@@ -389,21 +427,6 @@
     }
 
     /**
-     * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
-     * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to
-     * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic).
-     * Applications should probably not use this API directly. Instead, they should use {@link
-     * VpnService} to provide VPN capability in a more generic fashion.
-     *
-     * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
-     *
-     * @param net a {@link Network} that will be tunneled via IP Sec.
-     * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
-     * @hide
-     */
-    public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
-
-    /**
      * Remove an IPsec transform from a stream socket.
      *
      * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
@@ -416,8 +439,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(Socket socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -434,8 +456,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(DatagramSocket socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull DatagramSocket socket) throws IOException {
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -452,8 +473,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(FileDescriptor socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
             mService.removeTransportModeTransforms(pfd);
         } catch (RemoteException e) {
@@ -483,7 +503,7 @@
      * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link
      * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the
      * caller. The caller should not close the {@code FileDescriptor} returned by {@link
-     * #getSocket}, but should use {@link #close} instead.
+     * #getFileDescriptor}, but should use {@link #close} instead.
      *
      * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic
      * of the next user who binds to that port. To prevent this scenario, these sockets are held
@@ -522,8 +542,8 @@
             mCloseGuard.open("constructor");
         }
 
-        /** Get the wrapped socket. */
-        public FileDescriptor getSocket() {
+        /** Get the encapsulation socket's file descriptor. */
+        public FileDescriptor getFileDescriptor() {
             if (mPfd == null) {
                 return null;
             }
@@ -591,6 +611,7 @@
     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
     // the port, which could potentially impact the traffic of the next user who binds to that
     // socket.
+    @NonNull
     public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
             throws IOException, ResourceUnavailableException {
         /*
@@ -620,6 +641,7 @@
     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
     // the port, which could potentially impact the traffic of the next user who binds to that
     // socket.
+    @NonNull
     public UdpEncapsulationSocket openUdpEncapsulationSocket()
             throws IOException, ResourceUnavailableException {
         return new UdpEncapsulationSocket(mService, 0);
@@ -648,6 +670,7 @@
         private int mResourceId = INVALID_RESOURCE_ID;
 
         /** Get the underlying SPI held by this object. */
+        @NonNull
         public String getInterfaceName() {
             return mInterfaceName;
         }
@@ -659,10 +682,16 @@
          * tunneled traffic.
          *
          * @param address the local address for traffic inside the tunnel
-         * @throws IOException if the address could not be added
          * @hide
          */
-        public void addAddress(LinkAddress address) throws IOException {
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+        public void addAddress(@NonNull LinkAddress address) throws IOException {
+            try {
+                mService.addAddressToTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         /**
@@ -671,10 +700,16 @@
          * <p>Remove an address which was previously added to the IpSecTunnelInterface
          *
          * @param address to be removed
-         * @throws IOException if the address could not be removed
          * @hide
          */
-        public void removeAddress(LinkAddress address) throws IOException {
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+        public void removeAddress(@NonNull LinkAddress address) throws IOException {
+            try {
+                mService.removeAddressFromTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         private IpSecTunnelInterface(@NonNull IIpSecService service,
@@ -761,6 +796,8 @@
      * @hide
      */
     @SystemApi
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
             throws ResourceUnavailableException, IOException {
@@ -768,7 +805,12 @@
     }
 
     /**
-     * Apply a transform to the IpSecTunnelInterface
+     * Apply an active Tunnel Mode IPsec Transform to a {@link IpSecTunnelInterface}, which will
+     * tunnel all traffic for the given direction through the underlying network's interface with
+     * IPsec (applies an outer IP header and IPsec Header to all traffic, and expects an additional
+     * IP header and IPsec Header on all inbound traffic).
+     * <p>Applications should probably not use this API directly.
+     *
      *
      * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
      *        transform.
@@ -780,8 +822,9 @@
      * @hide
      */
     @SystemApi
-    public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
-            IpSecTransform transform) throws IOException {
+    @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+    public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         try {
             mService.applyTunnelModeTransform(
                     tunnel.getResourceId(), direction, transform.getResourceId());
@@ -789,6 +832,7 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
     /**
      * Construct an instance of IpSecManager within an application context.
      *
diff --git a/android/net/IpSecTransform.java b/android/net/IpSecTransform.java
index 37e2c4f..099fe02 100644
--- a/android/net/IpSecTransform.java
+++ b/android/net/IpSecTransform.java
@@ -17,11 +17,15 @@
 
 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -80,9 +84,11 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EncapType {}
 
-    private IpSecTransform(Context context, IpSecConfig config) {
+    /** @hide */
+    @VisibleForTesting
+    public IpSecTransform(Context context, IpSecConfig config) {
         mContext = context;
-        mConfig = config;
+        mConfig = new IpSecConfig(config);
         mResourceId = INVALID_RESOURCE_ID;
     }
 
@@ -128,13 +134,6 @@
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
-
-                /* Keepalive will silently fail if not needed by the config; but, if needed and
-                 * it fails to start, we need to bail because a transform will not be reliable
-                 * to use if keepalive is expected to offload and fails.
-                 */
-                // FIXME: if keepalive fails, we need to fail spectacularly
-                startKeepalive(mContext);
                 Log.d(TAG, "Added Transform with Id " + mResourceId);
                 mCloseGuard.open("build");
             } catch (RemoteException e) {
@@ -146,6 +145,18 @@
     }
 
     /**
+     * Equals method used for testing
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) {
+        if (lhs == null || rhs == null) return (lhs == rhs);
+        return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig())
+                && lhs.mResourceId == rhs.mResourceId;
+    }
+
+    /**
      * Deactivate this {@code IpSecTransform} and free allocated resources.
      *
      * <p>Deactivating a transform while it is still applied to a socket will result in errors on
@@ -164,13 +175,9 @@
             return;
         }
         try {
-            /* Order matters here because the keepalive is best-effort but could fail in some
-             * horrible way to be removed if the wifi (or cell) subsystem has crashed, and we
-             * still want to clear out the transform.
-             */
             IIpSecService svc = getIpSecService();
             svc.deleteTransform(mResourceId);
-            stopKeepalive();
+            stopNattKeepalive();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         } finally {
@@ -198,42 +205,35 @@
     private final Context mContext;
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private ConnectivityManager.PacketKeepalive mKeepalive;
-    private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
-    private Object mKeepaliveSyncLock = new Object();
-    private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
+    private Handler mCallbackHandler;
+    private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
             new ConnectivityManager.PacketKeepaliveCallback() {
 
                 @Override
                 public void onStarted() {
-                    synchronized (mKeepaliveSyncLock) {
-                        mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
-                        mKeepaliveSyncLock.notifyAll();
+                    synchronized (this) {
+                        mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted());
                     }
                 }
 
                 @Override
                 public void onStopped() {
-                    synchronized (mKeepaliveSyncLock) {
-                        mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
-                        mKeepaliveSyncLock.notifyAll();
+                    synchronized (this) {
+                        mKeepalive = null;
+                        mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped());
                     }
                 }
 
                 @Override
                 public void onError(int error) {
-                    synchronized (mKeepaliveSyncLock) {
-                        mKeepaliveStatus = error;
-                        mKeepaliveSyncLock.notifyAll();
+                    synchronized (this) {
+                        mKeepalive = null;
+                        mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error));
                     }
                 }
             };
 
-    /* Package */
-    void startKeepalive(Context c) {
-        if (mConfig.getNattKeepaliveInterval() != 0) {
-            Log.wtf(TAG, "Keepalive not yet supported.");
-        }
-    }
+    private NattKeepaliveCallback mUserKeepaliveCallback;
 
     /** @hide */
     @VisibleForTesting
@@ -241,9 +241,101 @@
         return mResourceId;
     }
 
-    /* Package */
-    void stopKeepalive() {
-        return;
+    /**
+     * A callback class to provide status information regarding a NAT-T keepalive session
+     *
+     * <p>Use this callback to receive status information regarding a NAT-T keepalive session
+     * by registering it when calling {@link #startNattKeepalive}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static class NattKeepaliveCallback {
+        /** The specified {@code Network} is not connected. */
+        public static final int ERROR_INVALID_NETWORK = 1;
+        /** The hardware does not support this request. */
+        public static final int ERROR_HARDWARE_UNSUPPORTED = 2;
+        /** The hardware returned an error. */
+        public static final int ERROR_HARDWARE_ERROR = 3;
+
+        /** The requested keepalive was successfully started. */
+        public void onStarted() {}
+        /** The keepalive was successfully stopped. */
+        public void onStopped() {}
+        /** An error occurred. */
+        public void onError(int error) {}
+    }
+
+    /**
+     * Start a NAT-T keepalive session for the current transform.
+     *
+     * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides
+     * a power efficient mechanism of sending NAT-T packets at a specified interval.
+     *
+     * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status
+     *      information about the requested NAT-T keepalive session.
+     * @param intervalSeconds the interval between NAT-T keepalives being sent. The
+     *      the allowed range is between 20 and 3600 seconds.
+     * @param handler a handler on which to post callbacks when received.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
+            android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+    })
+    public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
+            int intervalSeconds, @NonNull Handler handler) throws IOException {
+        checkNotNull(userCallback);
+        if (intervalSeconds < 20 || intervalSeconds > 3600) {
+            throw new IllegalArgumentException("Invalid NAT-T keepalive interval");
+        }
+        checkNotNull(handler);
+        if (mResourceId == INVALID_RESOURCE_ID) {
+            throw new IllegalStateException(
+                    "Packet keepalive cannot be started for an inactive transform");
+        }
+
+        synchronized (mKeepaliveCallback) {
+            if (mKeepaliveCallback != null) {
+                throw new IllegalStateException("Keepalive already active");
+            }
+
+            mUserKeepaliveCallback = userCallback;
+            ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                    Context.CONNECTIVITY_SERVICE);
+            mKeepalive = cm.startNattKeepalive(
+                    mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback,
+                    NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()),
+                    4500, // FIXME urgently, we need to get the port number from the Encap socket
+                    NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress()));
+            mCallbackHandler = handler;
+        }
+    }
+
+    /**
+     * Stop an ongoing NAT-T keepalive session.
+     *
+     * Calling this API will request that an ongoing NAT-T keepalive session be terminated.
+     * If this API is not called when a Transform is closed, the underlying NAT-T session will
+     * be terminated automatically.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
+            android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+    })
+    public void stopNattKeepalive() {
+        synchronized (mKeepaliveCallback) {
+            if (mKeepalive == null) {
+                Log.e(TAG, "No active keepalive to stop");
+                return;
+            }
+            mKeepalive.stop();
+        }
     }
 
     /** This class is used to build {@link IpSecTransform} objects. */
@@ -258,6 +350,7 @@
          *
          * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
             Preconditions.checkNotNull(algo);
@@ -272,6 +365,7 @@
          *
          * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
             Preconditions.checkNotNull(algo);
@@ -292,6 +386,7 @@
          * @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
          *     be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
             Preconditions.checkNotNull(algo);
             mConfig.setAuthenticatedEncryption(algo);
@@ -311,6 +406,7 @@
          * @param remotePort the UDP port number of the remote host that will send and receive
          *     encapsulated traffic. In the case of IKEv2, this should be port 4500.
          */
+        @NonNull
         public IpSecTransform.Builder setIpv4Encapsulation(
                 @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
             Preconditions.checkNotNull(localSocket);
@@ -323,26 +419,6 @@
             return this;
         }
 
-        // TODO: Decrease the minimum keepalive to maybe 10?
-        // TODO: Probably a better exception to throw for NATTKeepalive failure
-        // TODO: Specify the needed NATT keepalive permission.
-        /**
-         * Set NAT-T keepalives to be sent with a given interval.
-         *
-         * <p>This will set power-efficient keepalive packets to be sent by the system. If NAT-T
-         * keepalive is requested but cannot be activated, then creation of an {@link
-         * IpSecTransform} will fail when calling the build method.
-         *
-         * @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
-         *     between 20s and 3600s.
-         * @hide
-         */
-        @SystemApi
-        public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
-            mConfig.setNattKeepaliveInterval(intervalSeconds);
-            return this;
-        }
-
         /**
          * Build a transport mode {@link IpSecTransform}.
          *
@@ -364,6 +440,7 @@
          *     collides with an existing transform
          * @throws IOException indicating other errors
          */
+        @NonNull
         public IpSecTransform buildTransportModeTransform(
                 @NonNull InetAddress sourceAddress,
                 @NonNull IpSecManager.SecurityParameterIndex spi)
@@ -400,6 +477,8 @@
          * @hide
          */
         @SystemApi
+        @NonNull
+        @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public IpSecTransform buildTunnelModeTransform(
                 @NonNull InetAddress sourceAddress,
                 @NonNull IpSecManager.SecurityParameterIndex spi)
@@ -413,7 +492,7 @@
             mConfig.setMode(MODE_TUNNEL);
             mConfig.setSourceAddress(sourceAddress.getHostAddress());
             mConfig.setSpiResourceId(spi.getResourceId());
-            return new IpSecTransform(mContext, mConfig);
+            return new IpSecTransform(mContext, mConfig).activate();
         }
 
         /**
diff --git a/android/net/KeepalivePacketData.java b/android/net/KeepalivePacketData.java
index 08d4ff5..7436ad0 100644
--- a/android/net/KeepalivePacketData.java
+++ b/android/net/KeepalivePacketData.java
@@ -16,8 +16,8 @@
 
 package android.net;
 
-import android.system.OsConstants;
-import android.net.ConnectivityManager;
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+
 import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -25,13 +25,10 @@
 import android.util.Log;
 
 import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
-import static android.net.ConnectivityManager.PacketKeepalive.*;
-
 /**
  * Represents the actual packets that are sent by the
  * {@link android.net.ConnectivityManager.PacketKeepalive} API.
@@ -98,13 +95,6 @@
             InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
             throws InvalidPacketException {
 
-        // FIXME: remove this and actually support IPv6 keepalives
-        if (srcAddress instanceof Inet6Address && dstAddress instanceof Inet6Address) {
-            // Optimistically returning an IPv6 Keepalive Packet with no data,
-            // which currently only works on cellular
-            return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, new byte[0]);
-        }
-
         if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
             throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
         }
diff --git a/android/net/MacAddress.java b/android/net/MacAddress.java
index 287bdc8..74d6470 100644
--- a/android/net/MacAddress.java
+++ b/android/net/MacAddress.java
@@ -26,6 +26,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.Random;
 
@@ -329,16 +330,34 @@
 
     /**
      * Returns a generated MAC address whose 24 least significant bits constituting the
-     * NIC part of the address are randomly selected.
+     * NIC part of the address are randomly selected and has Google OUI base.
      *
      * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
      *
-     * @return a random locally assigned MacAddress.
+     * @return a random locally assigned, unicast MacAddress with Google OUI.
+     *
+     * @hide
+     */
+    public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
+        return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
+    }
+
+    /**
+     * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
+     * unicast bit, are randomly selected.
+     *
+     * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+     *
+     * @return a random locally assigned, unicast MacAddress.
      *
      * @hide
      */
     public static @NonNull MacAddress createRandomUnicastAddress() {
-        return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
+        SecureRandom r = new SecureRandom();
+        long addr = r.nextLong() & VALID_LONG_MASK;
+        addr |= LOCALLY_ASSIGNED_MASK;
+        addr &= ~MULTICAST_MASK;
+        return new MacAddress(addr);
     }
 
     /**
@@ -355,8 +374,8 @@
      */
     public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
         long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
-        addr = addr | LOCALLY_ASSIGNED_MASK;
-        addr = addr & ~MULTICAST_MASK;
+        addr |= LOCALLY_ASSIGNED_MASK;
+        addr &= ~MULTICAST_MASK;
         return new MacAddress(addr);
     }
 
diff --git a/android/net/Network.java b/android/net/Network.java
index 5df168d..15a0ee5 100644
--- a/android/net/Network.java
+++ b/android/net/Network.java
@@ -26,6 +26,8 @@
 import com.android.okhttp.internalandroidapi.Dns;
 import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.DatagramSocket;
@@ -77,6 +79,11 @@
             httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0;
     private static final long httpKeepAliveDurationMs =
             Long.parseLong(System.getProperty("http.keepAliveDuration", "300000"));  // 5 minutes.
+    // Value used to obfuscate network handle longs.
+    // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+    // value in the native/android/net.c NDK implementation.
+    private static final long HANDLE_MAGIC = 0xcafed00dL;
+    private static final int HANDLE_MAGIC_SIZE = 32;
 
     /**
      * @hide
@@ -137,9 +144,15 @@
             for (int i = 0; i < hostAddresses.length; i++) {
                 try {
                     Socket socket = createSocket();
-                    if (localAddress != null) socket.bind(localAddress);
-                    socket.connect(new InetSocketAddress(hostAddresses[i], port));
-                    return socket;
+                    boolean failed = true;
+                    try {
+                        if (localAddress != null) socket.bind(localAddress);
+                        socket.connect(new InetSocketAddress(hostAddresses[i], port));
+                        failed = false;
+                        return socket;
+                    } finally {
+                        if (failed) IoUtils.closeQuietly(socket);
+                    }
                 } catch (IOException e) {
                     if (i == (hostAddresses.length - 1)) throw e;
                 }
@@ -156,15 +169,27 @@
         public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
                 int localPort) throws IOException {
             Socket socket = createSocket();
-            socket.bind(new InetSocketAddress(localAddress, localPort));
-            socket.connect(new InetSocketAddress(address, port));
+            boolean failed = true;
+            try {
+                socket.bind(new InetSocketAddress(localAddress, localPort));
+                socket.connect(new InetSocketAddress(address, port));
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
 
         @Override
         public Socket createSocket(InetAddress host, int port) throws IOException {
             Socket socket = createSocket();
-            socket.connect(new InetSocketAddress(host, port));
+            boolean failed = true;
+            try {
+                socket.connect(new InetSocketAddress(host, port));
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
 
@@ -176,7 +201,13 @@
         @Override
         public Socket createSocket() throws IOException {
             Socket socket = new Socket();
-            bindSocket(socket);
+            boolean failed = true;
+            try {
+                bindSocket(socket);
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
     }
@@ -335,6 +366,25 @@
     }
 
     /**
+     * Returns a {@link Network} object given a handle returned from {@link #getNetworkHandle}.
+     *
+     * @param networkHandle a handle returned from {@link #getNetworkHandle}.
+     * @return A {@link Network} object derived from {@code networkHandle}.
+     */
+    public static Network fromNetworkHandle(long networkHandle) {
+        if (networkHandle == 0) {
+            throw new IllegalArgumentException(
+                    "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
+        }
+        if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
+                || networkHandle < 0) {
+            throw new IllegalArgumentException(
+                    "Value passed to fromNetworkHandle() is not a network handle.");
+        }
+        return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+    }
+
+    /**
      * Returns a handle representing this {@code Network}, for use with the NDK API.
      */
     public long getNetworkHandle() {
@@ -356,14 +406,10 @@
         // At some future date it may be desirable to realign the handle with
         // Multiple Provisioning Domains API recommendations, as made by the
         // IETF mif working group.
-        //
-        // The handleMagic value MUST be kept in sync with the corresponding
-        // value in the native/android/net.c NDK implementation.
         if (netId == 0) {
             return 0L;  // make this zero condition obvious for debugging
         }
-        final long handleMagic = 0xcafed00dL;
-        return (((long) netId) << 32) | handleMagic;
+        return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
     }
 
     // implement the Parcelable interface
diff --git a/android/net/NetworkAgentHelper.java b/android/net/NetworkAgentHelper.java
new file mode 100644
index 0000000..7ea7665
--- /dev/null
+++ b/android/net/NetworkAgentHelper.java
@@ -0,0 +1,16 @@
+package android.net;
+
+import android.annotation.NonNull;
+
+/**
+ * Wrapper around {@link android.net.NetworkAgent} to help test coverage
+ *
+ * {@link NetworkAgent} will call non-public method unwanted() when the
+ * agent should be disabled.
+ */
+public class NetworkAgentHelper {
+    public static void callUnwanted(@NonNull NetworkAgent networkAgent) {
+        System.out.println("NetworkAgentHelper Faking unwanted() call from connectivity manager");
+        networkAgent.unwanted();
+    }
+}
diff --git a/android/net/NetworkCapabilities.java b/android/net/NetworkCapabilities.java
index 8e05cfa..374b3ab 100644
--- a/android/net/NetworkCapabilities.java
+++ b/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -69,6 +70,7 @@
             mSignalStrength = nc.mSignalStrength;
             mUids = nc.mUids;
             mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
+            mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
         }
     }
 
@@ -78,7 +80,7 @@
      * @hide
      */
     public void clearAll() {
-        mNetworkCapabilities = mTransportTypes = 0;
+        mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
         mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
         mNetworkSpecifier = null;
         mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
@@ -92,6 +94,11 @@
      */
     private long mNetworkCapabilities;
 
+    /**
+     * If any capabilities specified here they must not exist in the matching Network.
+     */
+    private long mUnwantedNetworkCapabilities;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "NET_CAPABILITY_" }, value = {
@@ -116,6 +123,8 @@
             NET_CAPABILITY_NOT_ROAMING,
             NET_CAPABILITY_FOREGROUND,
             NET_CAPABILITY_NOT_CONGESTED,
+            NET_CAPABILITY_NOT_SUSPENDED,
+            NET_CAPABILITY_OEM_PAID,
     })
     public @interface NetCapability { }
 
@@ -239,7 +248,6 @@
     /**
      * Indicates that this network is available for use by apps, and not a network that is being
      * kept up in the background to facilitate fast network switching.
-     * @hide
      */
     public static final int NET_CAPABILITY_FOREGROUND = 19;
 
@@ -252,8 +260,28 @@
      */
     public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
 
+    /**
+     * Indicates that this network is not currently suspended.
+     * <p>
+     * When a network is suspended, the network's IP addresses and any connections
+     * established on the network remain valid, but the network is temporarily unable
+     * to transfer data. This can happen, for example, if a cellular network experiences
+     * a temporary loss of signal, such as when driving through a tunnel, etc.
+     * A network with this capability is not suspended, so is expected to be able to
+     * transfer data.
+     */
+    public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+
+    /**
+     * Indicates that traffic that goes through this network is paid by oem. For example,
+     * this network can be used by system apps to upload telemetry data.
+     * @hide
+     */
+    @SystemApi
+    public static final int NET_CAPABILITY_OEM_PAID = 22;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PAID;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -262,12 +290,13 @@
     private static final long MUTABLE_CAPABILITIES =
             // TRUSTED can change when user explicitly connects to an untrusted network in Settings.
             // http://b/18206275
-            (1 << NET_CAPABILITY_TRUSTED) |
-            (1 << NET_CAPABILITY_VALIDATED) |
-            (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
-            (1 << NET_CAPABILITY_NOT_ROAMING) |
-            (1 << NET_CAPABILITY_FOREGROUND) |
-            (1 << NET_CAPABILITY_NOT_CONGESTED);
+            (1 << NET_CAPABILITY_TRUSTED)
+            | (1 << NET_CAPABILITY_VALIDATED)
+            | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+            | (1 << NET_CAPABILITY_NOT_ROAMING)
+            | (1 << NET_CAPABILITY_FOREGROUND)
+            | (1 << NET_CAPABILITY_NOT_CONGESTED)
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -300,7 +329,8 @@
             (1 << NET_CAPABILITY_IA) |
             (1 << NET_CAPABILITY_IMS) |
             (1 << NET_CAPABILITY_RCS) |
-            (1 << NET_CAPABILITY_XCAP);
+            (1 << NET_CAPABILITY_XCAP) |
+            (1 << NET_CAPABILITY_OEM_PAID);
 
     /**
      * Capabilities that suggest that a network is unrestricted.
@@ -317,31 +347,55 @@
      * Adds the given capability to this {@code NetworkCapability} instance.
      * Multiple capabilities may be applied sequentially.  Note that when searching
      * for a network to satisfy a request, all capabilities requested must be satisfied.
+     * <p>
+     * If the given capability was previously added to the list of unwanted capabilities
+     * then the capability will also be removed from the list of unwanted capabilities.
      *
      * @param capability the capability to be added.
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
     public NetworkCapabilities addCapability(@NetCapability int capability) {
-        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
-            throw new IllegalArgumentException("NetworkCapability out of range");
-        }
+        checkValidCapability(capability);
         mNetworkCapabilities |= 1 << capability;
+        mUnwantedNetworkCapabilities &= ~(1 << capability);  // remove from unwanted capability list
         return this;
     }
 
     /**
+     * Adds the given capability to the list of unwanted capabilities of this
+     * {@code NetworkCapability} instance.  Multiple unwanted capabilities may be applied
+     * sequentially.  Note that when searching for a network to satisfy a request, the network
+     * must not contain any capability from unwanted capability list.
+     * <p>
+     * If the capability was previously added to the list of required capabilities (for
+     * example, it was there by default or added using {@link #addCapability(int)} method), then
+     * it will be removed from the list of required capabilities as well.
+     *
+     * @see #addCapability(int)
+     * @hide
+     */
+    public void addUnwantedCapability(@NetCapability int capability) {
+        checkValidCapability(capability);
+        mUnwantedNetworkCapabilities |= 1 << capability;
+        mNetworkCapabilities &= ~(1 << capability);  // remove from requested capabilities
+    }
+
+    /**
      * Removes (if found) the given capability from this {@code NetworkCapability} instance.
+     * <p>
+     * Note that this method removes capabilities that was added via {@link #addCapability(int)},
+     * {@link #addUnwantedCapability(int)} or {@link #setCapabilities(int[], int[])} .
      *
      * @param capability the capability to be removed.
      * @return This NetworkCapabilities instance, to facilitate chaining.
      * @hide
      */
     public NetworkCapabilities removeCapability(@NetCapability int capability) {
-        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
-            throw new IllegalArgumentException("NetworkCapability out of range");
-        }
-        mNetworkCapabilities &= ~(1 << capability);
+        checkValidCapability(capability);
+        final long mask = ~(1 << capability);
+        mNetworkCapabilities &= mask;
+        mUnwantedNetworkCapabilities &= mask;
         return this;
     }
 
@@ -371,30 +425,57 @@
     }
 
     /**
+     * Gets all the unwanted capabilities set on this {@code NetworkCapability} instance.
+     *
+     * @return an array of unwanted capability values for this instance.
+     * @hide
+     */
+    public @NetCapability int[] getUnwantedCapabilities() {
+        return BitUtils.unpackBits(mUnwantedNetworkCapabilities);
+    }
+
+
+    /**
      * Sets all the capabilities set on this {@code NetworkCapability} instance.
      * This overwrites any existing capabilities.
      *
      * @hide
      */
-    public void setCapabilities(@NetCapability int[] capabilities) {
+    public void setCapabilities(@NetCapability int[] capabilities,
+            @NetCapability int[] unwantedCapabilities) {
         mNetworkCapabilities = BitUtils.packBits(capabilities);
+        mUnwantedNetworkCapabilities = BitUtils.packBits(unwantedCapabilities);
     }
 
     /**
-     * Tests for the presence of a capabilitity on this instance.
+     * @deprecated use {@link #setCapabilities(int[], int[])}
+     * @hide
+     */
+    @Deprecated
+    public void setCapabilities(@NetCapability int[] capabilities) {
+        setCapabilities(capabilities, new int[] {});
+    }
+
+    /**
+     * Tests for the presence of a capability on this instance.
      *
      * @param capability the capabilities to be tested for.
      * @return {@code true} if set on this instance.
      */
     public boolean hasCapability(@NetCapability int capability) {
-        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
-            return false;
-        }
-        return ((mNetworkCapabilities & (1 << capability)) != 0);
+        return isValidCapability(capability)
+                && ((mNetworkCapabilities & (1 << capability)) != 0);
+    }
+
+    /** @hide */
+    public boolean hasUnwantedCapability(@NetCapability int capability) {
+        return isValidCapability(capability)
+                && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
     }
 
     private void combineNetCapabilities(NetworkCapabilities nc) {
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
+        this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities;
     }
 
     /**
@@ -405,7 +486,9 @@
      * @hide
      */
     public String describeFirstNonRequestableCapability() {
-        final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES);
+        final long nonRequestable = (mNetworkCapabilities | mUnwantedNetworkCapabilities)
+                & NON_REQUESTABLE_CAPABILITIES;
+
         if (nonRequestable != 0) {
             return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]);
         }
@@ -415,21 +498,29 @@
     }
 
     private boolean satisfiedByNetCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
-        long networkCapabilities = this.mNetworkCapabilities;
+        long requestedCapabilities = mNetworkCapabilities;
+        long requestedUnwantedCapabilities = mUnwantedNetworkCapabilities;
+        long providedCapabilities = nc.mNetworkCapabilities;
+
         if (onlyImmutable) {
-            networkCapabilities = networkCapabilities & ~MUTABLE_CAPABILITIES;
+            requestedCapabilities &= ~MUTABLE_CAPABILITIES;
+            requestedUnwantedCapabilities &= ~MUTABLE_CAPABILITIES;
         }
-        return ((nc.mNetworkCapabilities & networkCapabilities) == networkCapabilities);
+        return ((providedCapabilities & requestedCapabilities) == requestedCapabilities)
+                && ((requestedUnwantedCapabilities & providedCapabilities) == 0);
     }
 
     /** @hide */
     public boolean equalsNetCapabilities(NetworkCapabilities nc) {
-        return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
+        return (nc.mNetworkCapabilities == this.mNetworkCapabilities)
+                && (nc.mUnwantedNetworkCapabilities == this.mUnwantedNetworkCapabilities);
     }
 
     private boolean equalsNetCapabilitiesRequestable(NetworkCapabilities that) {
         return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
-                (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES));
+                (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES))
+                && ((this.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) ==
+                (that.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES));
     }
 
     /**
@@ -869,7 +960,16 @@
     /**
      * List of UIDs this network applies to. No restriction if null.
      * <p>
-     * This is typically (and at this time, only) used by VPN. This network is only available to
+     * For networks, mUids represent the list of network this applies to, and null means this
+     * network applies to all UIDs.
+     * For requests, mUids is the list of UIDs this network MUST apply to to match ; ALL UIDs
+     * must be included in a network so that they match. As an exception to the general rule,
+     * a null mUids field for requests mean "no requirements" rather than what the general rule
+     * would suggest ("must apply to all UIDs") : this is because this has shown to be what users
+     * of this API expect in practice. A network that must match all UIDs can still be
+     * expressed with a set ranging the entire set of possible UIDs.
+     * <p>
+     * mUids is typically (and at this time, only) used by VPN. This network is only available to
      * the UIDs in this list, and it is their default network. Apps in this list that wish to
      * bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this
      * member is null, then the network is not restricted by app UID. If it's an empty list, then
@@ -991,8 +1091,7 @@
      * @hide
      */
     public boolean satisfiedByUids(NetworkCapabilities nc) {
-        if (null == nc.mUids) return true; // The network satisfies everything.
-        if (null == mUids) return false; // Not everything allowed but requires everything
+        if (null == nc.mUids || null == mUids) return true; // The network satisfies everything.
         for (UidRange requiredRange : mUids) {
             if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true;
             if (!nc.appliesToUidRange(requiredRange)) {
@@ -1155,15 +1254,17 @@
 
     @Override
     public int hashCode() {
-        return ((int) (mNetworkCapabilities & 0xFFFFFFFF)
+        return (int) (mNetworkCapabilities & 0xFFFFFFFF)
                 + ((int) (mNetworkCapabilities >> 32) * 3)
-                + ((int) (mTransportTypes & 0xFFFFFFFF) * 5)
-                + ((int) (mTransportTypes >> 32) * 7)
-                + (mLinkUpBandwidthKbps * 11)
-                + (mLinkDownBandwidthKbps * 13)
-                + Objects.hashCode(mNetworkSpecifier) * 17
-                + (mSignalStrength * 19)
-                + Objects.hashCode(mUids) * 23);
+                + ((int) (mUnwantedNetworkCapabilities & 0xFFFFFFFF) * 5)
+                + ((int) (mUnwantedNetworkCapabilities >> 32) * 7)
+                + ((int) (mTransportTypes & 0xFFFFFFFF) * 11)
+                + ((int) (mTransportTypes >> 32) * 13)
+                + (mLinkUpBandwidthKbps * 17)
+                + (mLinkDownBandwidthKbps * 19)
+                + Objects.hashCode(mNetworkSpecifier) * 23
+                + (mSignalStrength * 29)
+                + Objects.hashCode(mUids) * 31;
     }
 
     @Override
@@ -1173,6 +1274,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(mNetworkCapabilities);
+        dest.writeLong(mUnwantedNetworkCapabilities);
         dest.writeLong(mTransportTypes);
         dest.writeInt(mLinkUpBandwidthKbps);
         dest.writeInt(mLinkDownBandwidthKbps);
@@ -1188,6 +1290,7 @@
                 NetworkCapabilities netCap = new NetworkCapabilities();
 
                 netCap.mNetworkCapabilities = in.readLong();
+                netCap.mUnwantedNetworkCapabilities = in.readLong();
                 netCap.mTransportTypes = in.readLong();
                 netCap.mLinkUpBandwidthKbps = in.readInt();
                 netCap.mLinkDownBandwidthKbps = in.readInt();
@@ -1205,34 +1308,73 @@
 
     @Override
     public String toString() {
-        // TODO: enumerate bits for transports and capabilities instead of creating arrays.
-        // TODO: use a StringBuilder instead of string concatenation.
-        int[] types = getTransportTypes();
-        String transports = (types.length > 0) ? " Transports: " + transportNamesOf(types) : "";
-
-        types = getCapabilities();
-        String capabilities = (types.length > 0 ? " Capabilities: " : "");
-        for (int i = 0; i < types.length; ) {
-            capabilities += capabilityNameOf(types[i]);
-            if (++i < types.length) capabilities += "&";
+        final StringBuilder sb = new StringBuilder("[");
+        if (0 != mTransportTypes) {
+            sb.append(" Transports: ");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, mTransportTypes,
+                    NetworkCapabilities::transportNameOf, "|");
+        }
+        if (0 != mNetworkCapabilities) {
+            sb.append(" Capabilities: ");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities,
+                    NetworkCapabilities::capabilityNameOf, "&");
+        }
+        if (0 != mNetworkCapabilities) {
+            sb.append(" Unwanted: ");
+            appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities,
+                    NetworkCapabilities::capabilityNameOf, "&");
+        }
+        if (mLinkUpBandwidthKbps > 0) {
+            sb.append(" LinkUpBandwidth>=").append(mLinkUpBandwidthKbps).append("Kbps");
+        }
+        if (mLinkDownBandwidthKbps > 0) {
+            sb.append(" LinkDnBandwidth>=").append(mLinkDownBandwidthKbps).append("Kbps");
+        }
+        if (mNetworkSpecifier != null) {
+            sb.append(" Specifier: <").append(mNetworkSpecifier).append(">");
+        }
+        if (hasSignalStrength()) {
+            sb.append(" SignalStrength: ").append(mSignalStrength);
         }
 
-        String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
-                mLinkUpBandwidthKbps + "Kbps" : "");
-        String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
-                mLinkDownBandwidthKbps + "Kbps" : "");
+        if (null != mUids) {
+            if ((1 == mUids.size()) && (mUids.valueAt(0).count() == 1)) {
+                sb.append(" Uid: ").append(mUids.valueAt(0).start);
+            } else {
+                sb.append(" Uids: <").append(mUids).append(">");
+            }
+        }
+        if (mEstablishingVpnAppUid != INVALID_UID) {
+            sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
+        }
 
-        String specifier = (mNetworkSpecifier == null ?
-                "" : " Specifier: <" + mNetworkSpecifier + ">");
+        sb.append("]");
+        return sb.toString();
+    }
 
-        String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : "");
 
-        String uids = (null != mUids ? " Uids: <" + mUids + ">" : "");
-
-        String establishingAppUid = " EstablishingAppUid: " + mEstablishingVpnAppUid;
-
-        return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength
-            + uids + establishingAppUid + "]";
+    private interface NameOf {
+        String nameOf(int value);
+    }
+    /**
+     * @hide
+     */
+    public static void appendStringRepresentationOfBitMaskToStringBuilder(StringBuilder sb,
+            long bitMask, NameOf nameFetcher, String separator) {
+        int bitPos = 0;
+        boolean firstElementAdded = false;
+        while (bitMask != 0) {
+            if ((bitMask & 1) != 0) {
+                if (firstElementAdded) {
+                    sb.append(separator);
+                } else {
+                    firstElementAdded = true;
+                }
+                sb.append(nameFetcher.nameOf(bitPos));
+            }
+            bitMask >>= 1;
+            ++bitPos;
+        }
     }
 
     /** @hide */
@@ -1299,6 +1441,8 @@
             case NET_CAPABILITY_NOT_ROAMING:    return "NOT_ROAMING";
             case NET_CAPABILITY_FOREGROUND:     return "FOREGROUND";
             case NET_CAPABILITY_NOT_CONGESTED:  return "NOT_CONGESTED";
+            case NET_CAPABILITY_NOT_SUSPENDED:  return "NOT_SUSPENDED";
+            case NET_CAPABILITY_OEM_PAID:       return "OEM_PAID";
             default:                            return Integer.toString(capability);
         }
     }
@@ -1330,4 +1474,13 @@
         Preconditions.checkArgument(
                 isValidTransport(transport), "Invalid TransportType " + transport);
     }
+
+    private static boolean isValidCapability(@NetworkCapabilities.NetCapability int capability) {
+        return capability >= MIN_NET_CAPABILITY && capability <= MAX_NET_CAPABILITY;
+    }
+
+    private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) {
+        Preconditions.checkArgument(isValidCapability(capability),
+                "NetworkCapability " + capability + "out of range");
+    }
 }
diff --git a/android/net/NetworkInfo.java b/android/net/NetworkInfo.java
index e6ad89a..999771a 100644
--- a/android/net/NetworkInfo.java
+++ b/android/net/NetworkInfo.java
@@ -38,14 +38,18 @@
      * <table>
      * <tr><td><b>Detailed state</b></td><td><b>Coarse-grained state</b></td></tr>
      * <tr><td><code>IDLE</code></td><td><code>DISCONNECTED</code></td></tr>
-     * <tr><td><code>SCANNING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>SCANNING</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr>
      * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>OBTAINING_IPADDR</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>VERIFYING_POOR_LINK</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>CAPTIVE_PORTAL_CHECK</code></td><td><code>CONNECTING</code></td></tr>
      * <tr><td><code>CONNECTED</code></td><td><code>CONNECTED</code></td></tr>
+     * <tr><td><code>SUSPENDED</code></td><td><code>SUSPENDED</code></td></tr>
      * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr>
      * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr>
-     * <tr><td><code>UNAVAILABLE</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
+     * <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr>
      * </table>
      */
     public enum State {
@@ -163,8 +167,17 @@
      * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
      * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
      * ConnectivityManager#TYPE_ETHERNET},  {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
-     * types defined by {@link ConnectivityManager}
+     * types defined by {@link ConnectivityManager}.
+     * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
+     *             instead with one of the NetworkCapabilities#TRANSPORT_* constants :
+     *             {@link #getType} and {@link #getTypeName} cannot account for networks using
+     *             multiple transports. Note that generally apps should not care about transport;
+     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
+     *             {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
+     *             apps concerned with meteredness or bandwidth should be looking at, as they
+     *             offer this information with much better accuracy.
      */
+    @Deprecated
     public int getType() {
         synchronized (this) {
             return mNetworkType;
@@ -172,8 +185,10 @@
     }
 
     /**
+     * @deprecated Use {@link NetworkCapabilities} instead
      * @hide
      */
+    @Deprecated
     public void setType(int type) {
         synchronized (this) {
             mNetworkType = type;
@@ -205,7 +220,16 @@
      * Return a human-readable name describe the type of the network,
      * for example "WIFI" or "MOBILE".
      * @return the name of the network type
+     * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
+     *             instead with one of the NetworkCapabilities#TRANSPORT_* constants :
+     *             {@link #getType} and {@link #getTypeName} cannot account for networks using
+     *             multiple transports. Note that generally apps should not care about transport;
+     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
+     *             {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
+     *             apps concerned with meteredness or bandwidth should be looking at, as they
+     *             offer this information with much better accuracy.
      */
+    @Deprecated
     public String getTypeName() {
         synchronized (this) {
             return mTypeName;
@@ -230,7 +254,15 @@
      * that the network is fully usable.
      * @return {@code true} if network connectivity exists or is in the process
      * of being established, {@code false} otherwise.
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes.
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isConnectedOrConnecting() {
         synchronized (this) {
             return mState == State.CONNECTED || mState == State.CONNECTING;
@@ -259,8 +291,18 @@
      * data roaming has been disabled.</li>
      * <li>The device's radio is turned off, e.g., because airplane mode is enabled.</li>
      * </ul>
+     * Since Android L, this always returns {@code true}, because the system only
+     * returns info for available networks.
      * @return {@code true} if the network is available, {@code false} otherwise
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes.
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isAvailable() {
         synchronized (this) {
             return mIsAvailable;
@@ -270,9 +312,11 @@
     /**
      * Sets if the network is available, ie, if the connectivity is possible.
      * @param isAvailable the new availability value.
+     * @deprecated Use {@link NetworkCapabilities} instead
      *
      * @hide
      */
+    @Deprecated
     public void setIsAvailable(boolean isAvailable) {
         synchronized (this) {
             mIsAvailable = isAvailable;
@@ -285,7 +329,10 @@
      * network following a disconnect from another network.
      * @return {@code true} if this is a failover attempt, {@code false}
      * otherwise.
+     * @deprecated This field is not populated in recent Android releases,
+     *             and does not make a lot of sense in a multi-network world.
      */
+    @Deprecated
     public boolean isFailover() {
         synchronized (this) {
             return mIsFailover;
@@ -296,8 +343,10 @@
      * Set the failover boolean.
      * @param isFailover {@code true} to mark the current connection attempt
      * as a failover.
+     * @deprecated This hasn't been set in any recent Android release.
      * @hide
      */
+    @Deprecated
     public void setFailover(boolean isFailover) {
         synchronized (this) {
             mIsFailover = isFailover;
@@ -322,7 +371,10 @@
         }
     }
 
-    /** {@hide} */
+    /**
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING} instead.
+     * {@hide}
+     */
     @VisibleForTesting
     @Deprecated
     public void setRoaming(boolean isRoaming) {
@@ -334,7 +386,15 @@
     /**
      * Reports the current coarse-grained state of the network.
      * @return the coarse-grained state
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes.
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public State getState() {
         synchronized (this) {
             return mState;
@@ -358,8 +418,10 @@
      * if one was supplied. May be {@code null}.
      * @param extraInfo an optional {@code String} providing addditional network state
      * information passed up from the lower networking layers.
+     * @deprecated Use {@link NetworkCapabilities} instead.
      * @hide
      */
+    @Deprecated
     public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
         synchronized (this) {
             this.mDetailedState = detailedState;
@@ -385,6 +447,8 @@
      * Report the reason an attempt to establish connectivity failed,
      * if one is available.
      * @return the reason for failure, or null if not available
+     * @deprecated This method does not have a consistent contract that could make it useful
+     *             to callers.
      */
     public String getReason() {
         synchronized (this) {
diff --git a/android/net/NetworkPolicy.java b/android/net/NetworkPolicy.java
index 5df742c..1a28732 100644
--- a/android/net/NetworkPolicy.java
+++ b/android/net/NetworkPolicy.java
@@ -42,6 +42,7 @@
 public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
     private static final int VERSION_INIT = 1;
     private static final int VERSION_RULE = 2;
+    private static final int VERSION_RAPID = 3;
 
     public static final int CYCLE_NONE = -1;
     public static final long WARNING_DISABLED = -1;
@@ -54,6 +55,7 @@
     public long limitBytes = LIMIT_DISABLED;
     public long lastWarningSnooze = SNOOZE_NEVER;
     public long lastLimitSnooze = SNOOZE_NEVER;
+    public long lastRapidSnooze = SNOOZE_NEVER;
     @Deprecated public boolean metered = true;
     public boolean inferred = false;
 
@@ -82,15 +84,24 @@
                 limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
     }
 
+    @Deprecated
     public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
             long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered,
             boolean inferred) {
+        this(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze,
+                SNOOZE_NEVER, metered, inferred);
+    }
+
+    public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
+            long limitBytes, long lastWarningSnooze, long lastLimitSnooze, long lastRapidSnooze,
+            boolean metered, boolean inferred) {
         this.template = Preconditions.checkNotNull(template, "missing NetworkTemplate");
         this.cycleRule = Preconditions.checkNotNull(cycleRule, "missing RecurrenceRule");
         this.warningBytes = warningBytes;
         this.limitBytes = limitBytes;
         this.lastWarningSnooze = lastWarningSnooze;
         this.lastLimitSnooze = lastLimitSnooze;
+        this.lastRapidSnooze = lastRapidSnooze;
         this.metered = metered;
         this.inferred = inferred;
     }
@@ -102,6 +113,7 @@
         limitBytes = source.readLong();
         lastWarningSnooze = source.readLong();
         lastLimitSnooze = source.readLong();
+        lastRapidSnooze = source.readLong();
         metered = source.readInt() != 0;
         inferred = source.readInt() != 0;
     }
@@ -114,6 +126,7 @@
         dest.writeLong(limitBytes);
         dest.writeLong(lastWarningSnooze);
         dest.writeLong(lastLimitSnooze);
+        dest.writeLong(lastRapidSnooze);
         dest.writeInt(metered ? 1 : 0);
         dest.writeInt(inferred ? 1 : 0);
     }
@@ -151,6 +164,7 @@
     public void clearSnooze() {
         lastWarningSnooze = SNOOZE_NEVER;
         lastLimitSnooze = SNOOZE_NEVER;
+        lastRapidSnooze = SNOOZE_NEVER;
     }
 
     /**
@@ -176,7 +190,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(template, cycleRule, warningBytes, limitBytes,
-                lastWarningSnooze, lastLimitSnooze, metered, inferred);
+                lastWarningSnooze, lastLimitSnooze, lastRapidSnooze, metered, inferred);
     }
 
     @Override
@@ -186,7 +200,9 @@
             return warningBytes == other.warningBytes
                     && limitBytes == other.limitBytes
                     && lastWarningSnooze == other.lastWarningSnooze
-                    && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
+                    && lastLimitSnooze == other.lastLimitSnooze
+                    && lastRapidSnooze == other.lastRapidSnooze
+                    && metered == other.metered
                     && inferred == other.inferred
                     && Objects.equals(template, other.template)
                     && Objects.equals(cycleRule, other.cycleRule);
@@ -203,6 +219,7 @@
                 .append(" limitBytes=").append(limitBytes)
                 .append(" lastWarningSnooze=").append(lastWarningSnooze)
                 .append(" lastLimitSnooze=").append(lastLimitSnooze)
+                .append(" lastRapidSnooze=").append(lastRapidSnooze)
                 .append(" metered=").append(metered)
                 .append(" inferred=").append(inferred)
                 .append("}").toString();
@@ -224,13 +241,14 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream out = new DataOutputStream(baos);
 
-        out.writeInt(VERSION_RULE);
+        out.writeInt(VERSION_RAPID);
         out.write(template.getBytesForBackup());
         cycleRule.writeToStream(out);
         out.writeLong(warningBytes);
         out.writeLong(limitBytes);
         out.writeLong(lastWarningSnooze);
         out.writeLong(lastLimitSnooze);
+        out.writeLong(lastRapidSnooze);
         out.writeInt(metered ? 1 : 0);
         out.writeInt(inferred ? 1 : 0);
         return baos.toByteArray();
@@ -239,35 +257,32 @@
     public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException,
             BackupUtils.BadVersionException {
         final int version = in.readInt();
-        switch (version) {
-            case VERSION_INIT: {
-                NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
-                int cycleDay = in.readInt();
-                String cycleTimeZone = BackupUtils.readString(in);
-                long warningBytes = in.readLong();
-                long limitBytes = in.readLong();
-                long lastWarningSnooze = in.readLong();
-                long lastLimitSnooze = in.readLong();
-                boolean metered = in.readInt() == 1;
-                boolean inferred = in.readInt() == 1;
-                return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes,
-                        limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
-            }
-            case VERSION_RULE: {
-                NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
-                RecurrenceRule cycleRule = new RecurrenceRule(in);
-                long warningBytes = in.readLong();
-                long limitBytes = in.readLong();
-                long lastWarningSnooze = in.readLong();
-                long lastLimitSnooze = in.readLong();
-                boolean metered = in.readInt() == 1;
-                boolean inferred = in.readInt() == 1;
-                return new NetworkPolicy(template, cycleRule, warningBytes,
-                        limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
-            }
-            default: {
-                throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
-            }
+        if (version < VERSION_INIT || version > VERSION_RAPID) {
+            throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
         }
+
+        final NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
+        final RecurrenceRule cycleRule;
+        if (version >= VERSION_RULE) {
+            cycleRule = new RecurrenceRule(in);
+        } else {
+            final int cycleDay = in.readInt();
+            final String cycleTimezone = BackupUtils.readString(in);
+            cycleRule = buildRule(cycleDay, ZoneId.of(cycleTimezone));
+        }
+        final long warningBytes = in.readLong();
+        final long limitBytes = in.readLong();
+        final long lastWarningSnooze = in.readLong();
+        final long lastLimitSnooze = in.readLong();
+        final long lastRapidSnooze;
+        if (version >= VERSION_RAPID) {
+            lastRapidSnooze = in.readLong();
+        } else {
+            lastRapidSnooze = SNOOZE_NEVER;
+        }
+        final boolean metered = in.readInt() == 1;
+        final boolean inferred = in.readInt() == 1;
+        return new NetworkPolicy(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze,
+                lastLimitSnooze, lastRapidSnooze, metered, inferred);
     }
 }
diff --git a/android/net/NetworkPolicyManager.java b/android/net/NetworkPolicyManager.java
index 2c5a021..bf6b7e0 100644
--- a/android/net/NetworkPolicyManager.java
+++ b/android/net/NetworkPolicyManager.java
@@ -107,6 +107,9 @@
 
     private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
 
+    public static final int FOREGROUND_THRESHOLD_STATE =
+            ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+
     /**
      * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it
      * applies to.
@@ -331,7 +334,7 @@
      * to access network when the device is idle or in battery saver mode. Otherwise, false.
      */
     public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
-        return procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+        return procState <= FOREGROUND_THRESHOLD_STATE;
     }
 
     /**
@@ -339,7 +342,7 @@
      * to access network when the device is in data saver mode. Otherwise, false.
      */
     public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
-        return procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+        return procState <= FOREGROUND_THRESHOLD_STATE;
     }
 
     public static String resolveNetworkId(WifiConfiguration config) {
diff --git a/android/net/NetworkRequest.java b/android/net/NetworkRequest.java
index a072409..3d9d6e2 100644
--- a/android/net/NetworkRequest.java
+++ b/android/net/NetworkRequest.java
@@ -17,12 +17,16 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.net.NetworkCapabilities.NetCapability;
+import android.net.NetworkCapabilities.Transport;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.text.TextUtils;
 import android.util.proto.ProtoOutputStream;
 
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
@@ -132,12 +136,18 @@
      * needed in terms of {@link NetworkCapabilities} features
      */
     public static class Builder {
-        private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities();
+        private final NetworkCapabilities mNetworkCapabilities;
 
         /**
          * Default constructor for Builder.
          */
-        public Builder() {}
+        public Builder() {
+            // By default, restrict this request to networks available to this app.
+            // Apps can rescind this restriction, but ConnectivityService will enforce
+            // it for apps that do not have the NETWORK_SETTINGS permission.
+            mNetworkCapabilities = new NetworkCapabilities();
+            mNetworkCapabilities.setSingleUid(Process.myUid());
+        }
 
         /**
          * Build {@link NetworkRequest} give the current set of capabilities.
@@ -158,6 +168,9 @@
          * the requested network's required capabilities.  Note that when searching
          * for a network to satisfy a request, all capabilities requested must be
          * satisfied.
+         * <p>
+         * If the given capability was previously added to the list of unwanted capabilities
+         * then the capability will also be removed from the list of unwanted capabilities.
          *
          * @param capability The capability to add.
          * @return The builder to facilitate chaining
@@ -169,7 +182,8 @@
         }
 
         /**
-         * Removes (if found) the given capability from this builder instance.
+         * Removes (if found) the given capability from this builder instance from both required
+         * and unwanted capabilities lists.
          *
          * @param capability The capability to remove.
          * @return The builder to facilitate chaining.
@@ -194,6 +208,36 @@
         }
 
         /**
+         * Set the watched UIDs for this request. This will be reset and wiped out unless
+         * the calling app holds the CHANGE_NETWORK_STATE permission.
+         *
+         * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+         * @return The builder to facilitate chaining.
+         * @hide
+         */
+        public Builder setUids(Set<UidRange> uids) {
+            mNetworkCapabilities.setUids(uids);
+            return this;
+        }
+
+        /**
+         * Add a capability that must not exist in the requested network.
+         * <p>
+         * If the capability was previously added to the list of required capabilities (for
+         * example, it was there by default or added using {@link #addCapability(int)} method), then
+         * it will be removed from the list of required capabilities as well.
+         *
+         * @see #addCapability(int)
+         *
+         * @param capability The capability to add to unwanted capability list.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
+            mNetworkCapabilities.addUnwantedCapability(capability);
+            return this;
+        }
+
+        /**
          * Completely clears all the {@code NetworkCapabilities} from this builder instance,
          * removing even the capabilities that are set by default when the object is constructed.
          *
@@ -384,6 +428,20 @@
         return type == Type.BACKGROUND_REQUEST;
     }
 
+    /**
+     * @see Builder#addCapability(int)
+     */
+    public boolean hasCapability(@NetCapability int capability) {
+        return networkCapabilities.hasCapability(capability);
+    }
+
+    /**
+     * @see Builder#addTransportType(int)
+     */
+    public boolean hasTransport(@Transport int transportType) {
+        return networkCapabilities.hasTransport(transportType);
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/android/net/NetworkStats.java b/android/net/NetworkStats.java
index 01b2b39..f0dd262 100644
--- a/android/net/NetworkStats.java
+++ b/android/net/NetworkStats.java
@@ -31,6 +31,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -64,6 +65,9 @@
     /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
     public static final int SET_DBG_VPN_OUT = 1002;
 
+    /** Include all interfaces when filtering */
+    public static final String[] INTERFACES_ALL = null;
+
     /** {@link #tag} value for total data across all tags. */
     // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
@@ -94,6 +98,11 @@
     /** Denotes a request for stats at the interface and UID level. */
     public static final int STATS_PER_UID = 1;
 
+    private static final String CLATD_INTERFACE_PREFIX = "v4-";
+    // Delta between IPv4 header (20b) and IPv6 header (40b).
+    // Used for correct stats accounting on clatd interfaces.
+    private static final int IPV4V6_HEADER_DELTA = 20;
+
     // TODO: move fields to "mVariable" notation
 
     /**
@@ -234,7 +243,7 @@
     public NetworkStats(long elapsedRealtime, int initialSize) {
         this.elapsedRealtime = elapsedRealtime;
         this.size = 0;
-        if (initialSize >= 0) {
+        if (initialSize > 0) {
             this.capacity = initialSize;
             this.iface = new String[initialSize];
             this.uid = new int[initialSize];
@@ -250,19 +259,7 @@
             this.operations = new long[initialSize];
         } else {
             // Special case for use by NetworkStatsFactory to start out *really* empty.
-            this.capacity = 0;
-            this.iface = EmptyArray.STRING;
-            this.uid = EmptyArray.INT;
-            this.set = EmptyArray.INT;
-            this.tag = EmptyArray.INT;
-            this.metered = EmptyArray.INT;
-            this.roaming = EmptyArray.INT;
-            this.defaultNetwork = EmptyArray.INT;
-            this.rxBytes = EmptyArray.LONG;
-            this.rxPackets = EmptyArray.LONG;
-            this.txBytes = EmptyArray.LONG;
-            this.txPackets = EmptyArray.LONG;
-            this.operations = EmptyArray.LONG;
+            clear();
         }
     }
 
@@ -314,6 +311,25 @@
         return clone;
     }
 
+    /**
+     * Clear all data stored in this object.
+     */
+    public void clear() {
+        this.capacity = 0;
+        this.iface = EmptyArray.STRING;
+        this.uid = EmptyArray.INT;
+        this.set = EmptyArray.INT;
+        this.tag = EmptyArray.INT;
+        this.metered = EmptyArray.INT;
+        this.roaming = EmptyArray.INT;
+        this.defaultNetwork = EmptyArray.INT;
+        this.rxBytes = EmptyArray.LONG;
+        this.rxPackets = EmptyArray.LONG;
+        this.txBytes = EmptyArray.LONG;
+        this.txPackets = EmptyArray.LONG;
+        this.operations = EmptyArray.LONG;
+    }
+
     @VisibleForTesting
     public NetworkStats addIfaceValues(
             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
@@ -359,23 +375,27 @@
             capacity = newLength;
         }
 
-        iface[size] = entry.iface;
-        uid[size] = entry.uid;
-        set[size] = entry.set;
-        tag[size] = entry.tag;
-        metered[size] = entry.metered;
-        roaming[size] = entry.roaming;
-        defaultNetwork[size] = entry.defaultNetwork;
-        rxBytes[size] = entry.rxBytes;
-        rxPackets[size] = entry.rxPackets;
-        txBytes[size] = entry.txBytes;
-        txPackets[size] = entry.txPackets;
-        operations[size] = entry.operations;
+        setValues(size, entry);
         size++;
 
         return this;
     }
 
+    private void setValues(int i, Entry entry) {
+        iface[i] = entry.iface;
+        uid[i] = entry.uid;
+        set[i] = entry.set;
+        tag[i] = entry.tag;
+        metered[i] = entry.metered;
+        roaming[i] = entry.roaming;
+        defaultNetwork[i] = entry.defaultNetwork;
+        rxBytes[i] = entry.rxBytes;
+        rxPackets[i] = entry.rxPackets;
+        txBytes[i] = entry.txBytes;
+        txPackets[i] = entry.txPackets;
+        operations[i] = entry.operations;
+    }
+
     /**
      * Return specific stats entry.
      */
@@ -745,6 +765,75 @@
     }
 
     /**
+     * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+     *
+     * <p>This mutates both base and stacked traffic stats, to account respectively for
+     * double-counted traffic and IPv4/IPv6 header size difference.
+     *
+     * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
+     * packet on the stacked interface, and once as translated to an IPv6 packet on the
+     * base interface. For correct stats accounting on the base interface, every 464xlat
+     * packet needs to be subtracted from the root UID on the base interface both for tx
+     * and rx traffic (http://b/12249687, http:/b/33681750).
+     *
+     * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
+     * {@code ConcurrentHashMap}
+     * @param baseTraffic Traffic on the base interfaces. Will be mutated.
+     * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
+     * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     */
+    public static void apply464xlatAdjustments(NetworkStats baseTraffic,
+            NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
+        // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
+        // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
+        final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
+
+        // For recycling
+        Entry entry = null;
+        Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
+
+        for (int i = 0; i < stackedTraffic.size; i++) {
+            entry = stackedTraffic.getValues(i, entry);
+            if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
+                continue;
+            }
+            final String baseIface = stackedIfaces.get(entry.iface);
+            if (baseIface == null) {
+                continue;
+            }
+            // Subtract any 464lat traffic seen for the root UID on the current base interface.
+            adjust.iface = baseIface;
+            adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+            adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
+            adjust.rxPackets = -entry.rxPackets;
+            adjust.txPackets = -entry.txPackets;
+            adjustments.combineValues(adjust);
+
+            // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
+            // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
+            // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
+            // difference for all packets (http://b/12249687, http:/b/33681750).
+            entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
+            entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
+            stackedTraffic.setValues(i, entry);
+        }
+
+        baseTraffic.combineAllValues(adjustments);
+    }
+
+    /**
+     * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+     *
+     * <p>This mutates the object this method is called on. Equivalent to calling
+     * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
+     * base and stacked traffic.
+     * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     */
+    public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
+        apply464xlatAdjustments(this, this, stackedIfaces);
+    }
+
+    /**
      * Return total statistics grouped by {@link #iface}; doesn't mutate the
      * original structure.
      */
@@ -824,6 +913,39 @@
         return stats;
     }
 
+    /**
+     * Only keep entries that match all specified filters.
+     *
+     * <p>This mutates the original structure in place. After this method is called,
+     * size is the number of matching entries, and capacity is the previous capacity.
+     * @param limitUid UID to filter for, or {@link #UID_ALL}.
+     * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
+     * @param limitTag Tag to filter for, or {@link #TAG_ALL}.
+     */
+    public void filter(int limitUid, String[] limitIfaces, int limitTag) {
+        if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
+            return;
+        }
+
+        Entry entry = new Entry();
+        int nextOutputEntry = 0;
+        for (int i = 0; i < size; i++) {
+            entry = getValues(i, entry);
+            final boolean matches =
+                    (limitUid == UID_ALL || limitUid == entry.uid)
+                    && (limitTag == TAG_ALL || limitTag == entry.tag)
+                    && (limitIfaces == INTERFACES_ALL
+                            || ArrayUtils.contains(limitIfaces, entry.iface));
+
+            if (matches) {
+                setValues(nextOutputEntry, entry);
+                nextOutputEntry++;
+            }
+        }
+
+        size = nextOutputEntry;
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
diff --git a/android/net/NetworkStatsHistory.java b/android/net/NetworkStatsHistory.java
index 433f941..a13ad65 100644
--- a/android/net/NetworkStatsHistory.java
+++ b/android/net/NetworkStatsHistory.java
@@ -39,6 +39,8 @@
 
 import com.android.internal.util.IndentingPrintWriter;
 
+import libcore.util.EmptyArray;
+
 import java.io.CharArrayWriter;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -459,6 +461,21 @@
     }
 
     /**
+     * Clear all data stored in this object.
+     */
+    public void clear() {
+        bucketStart = EmptyArray.LONG;
+        if (activeTime != null) activeTime = EmptyArray.LONG;
+        if (rxBytes != null) rxBytes = EmptyArray.LONG;
+        if (rxPackets != null) rxPackets = EmptyArray.LONG;
+        if (txBytes != null) txBytes = EmptyArray.LONG;
+        if (txPackets != null) txPackets = EmptyArray.LONG;
+        if (operations != null) operations = EmptyArray.LONG;
+        bucketCount = 0;
+        totalBytes = 0;
+    }
+
+    /**
      * Remove buckets older than requested cutoff.
      */
     @Deprecated
diff --git a/android/net/NetworkTemplate.java b/android/net/NetworkTemplate.java
index 8efd39a..74233fd 100644
--- a/android/net/NetworkTemplate.java
+++ b/android/net/NetworkTemplate.java
@@ -23,7 +23,6 @@
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
-import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -34,11 +33,6 @@
 import static android.net.NetworkStats.ROAMING_NO;
 import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
-import static android.telephony.TelephonyManager.getNetworkClass;
 
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -55,8 +49,8 @@
 import java.util.Objects;
 
 /**
- * Template definition used to generically match {@link NetworkIdentity},
- * usually when collecting statistics.
+ * Predicate used to match {@link NetworkIdentity}, usually when collecting
+ * statistics. (It should probably have been named {@code NetworkPredicate}.)
  *
  * @hide
  */
@@ -68,13 +62,7 @@
      */
     private static final int BACKUP_VERSION = 1;
 
-    public static final int MATCH_MOBILE_ALL = 1;
-    /** @deprecated don't use this any more */
-    @Deprecated
-    public static final int MATCH_MOBILE_3G_LOWER = 2;
-    /** @deprecated don't use this any more */
-    @Deprecated
-    public static final int MATCH_MOBILE_4G = 3;
+    public static final int MATCH_MOBILE = 1;
     public static final int MATCH_WIFI = 4;
     public static final int MATCH_ETHERNET = 5;
     public static final int MATCH_MOBILE_WILDCARD = 6;
@@ -84,9 +72,7 @@
 
     private static boolean isKnownMatchRule(final int rule) {
         switch (rule) {
-            case MATCH_MOBILE_ALL:
-            case MATCH_MOBILE_3G_LOWER:
-            case MATCH_MOBILE_4G:
+            case MATCH_MOBILE:
             case MATCH_WIFI:
             case MATCH_ETHERNET:
             case MATCH_MOBILE_WILDCARD:
@@ -111,25 +97,7 @@
      * the given IMSI.
      */
     public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
-        return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId, null);
-    }
-
-    /**
-     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
-     * the given IMSI that roughly meet a "3G" definition, or lower.
-     */
-    @Deprecated
-    public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) {
-        return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId, null);
-    }
-
-    /**
-     * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
-     * the given IMSI that roughly meet a "4G" definition.
-     */
-    @Deprecated
-    public static NetworkTemplate buildTemplateMobile4g(String subscriberId) {
-        return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId, null);
+        return new NetworkTemplate(MATCH_MOBILE, subscriberId, null);
     }
 
     /**
@@ -307,9 +275,7 @@
 
     public boolean isMatchRuleMobile() {
         switch (mMatchRule) {
-            case MATCH_MOBILE_3G_LOWER:
-            case MATCH_MOBILE_4G:
-            case MATCH_MOBILE_ALL:
+            case MATCH_MOBILE:
             case MATCH_MOBILE_WILDCARD:
                 return true;
             default:
@@ -348,12 +314,8 @@
         if (!matchesDefaultNetwork(ident)) return false;
 
         switch (mMatchRule) {
-            case MATCH_MOBILE_ALL:
+            case MATCH_MOBILE:
                 return matchesMobile(ident);
-            case MATCH_MOBILE_3G_LOWER:
-                return matchesMobile3gLower(ident);
-            case MATCH_MOBILE_4G:
-                return matchesMobile4g(ident);
             case MATCH_WIFI:
                 return matchesWifi(ident);
             case MATCH_ETHERNET:
@@ -410,43 +372,6 @@
     }
 
     /**
-     * Check if mobile network classified 3G or lower with matching IMSI.
-     */
-    @Deprecated
-    private boolean matchesMobile3gLower(NetworkIdentity ident) {
-        ensureSubtypeAvailable();
-        if (ident.mType == TYPE_WIMAX) {
-            return false;
-        } else if (matchesMobile(ident)) {
-            switch (getNetworkClass(ident.mSubType)) {
-                case NETWORK_CLASS_UNKNOWN:
-                case NETWORK_CLASS_2_G:
-                case NETWORK_CLASS_3_G:
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Check if mobile network classified 4G with matching IMSI.
-     */
-    @Deprecated
-    private boolean matchesMobile4g(NetworkIdentity ident) {
-        ensureSubtypeAvailable();
-        if (ident.mType == TYPE_WIMAX) {
-            // TODO: consider matching against WiMAX subscriber identity
-            return true;
-        } else if (matchesMobile(ident)) {
-            switch (getNetworkClass(ident.mSubType)) {
-                case NETWORK_CLASS_4_G:
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Check if matches Wi-Fi network template.
      */
     private boolean matchesWifi(NetworkIdentity ident) {
@@ -506,12 +431,8 @@
 
     private static String getMatchRuleName(int matchRule) {
         switch (matchRule) {
-            case MATCH_MOBILE_3G_LOWER:
-                return "MOBILE_3G_LOWER";
-            case MATCH_MOBILE_4G:
-                return "MOBILE_4G";
-            case MATCH_MOBILE_ALL:
-                return "MOBILE_ALL";
+            case MATCH_MOBILE:
+                return "MOBILE";
             case MATCH_WIFI:
                 return "WIFI";
             case MATCH_ETHERNET:
@@ -529,13 +450,6 @@
         }
     }
 
-    private static void ensureSubtypeAvailable() {
-        if (COMBINE_SUBTYPE_ENABLED) {
-            throw new IllegalArgumentException(
-                    "Unable to enforce 3G_LOWER template on combined data.");
-        }
-    }
-
     /**
      * Examine the given template and normalize if it refers to a "merged"
      * mobile subscriber. We pick the "lowest" merged subscriber as the primary
diff --git a/android/net/NetworkUtils.java b/android/net/NetworkUtils.java
index fe9563d..9a5d502 100644
--- a/android/net/NetworkUtils.java
+++ b/android/net/NetworkUtils.java
@@ -16,19 +16,20 @@
 
 package android.net;
 
-import java.io.FileDescriptor;
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.Collection;
-import java.util.Locale;
-
 import android.os.Parcel;
 import android.util.Log;
 import android.util.Pair;
 
+import java.io.FileDescriptor;
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.TreeSet;
 
 /**
  * Native methods for managing network interfaces.
@@ -385,4 +386,72 @@
         result = builder.toString();
         return result;
     }
+
+    /**
+     * Returns a prefix set without overlaps.
+     *
+     * This expects the src set to be sorted from shorter to longer. Results are undefined
+     * failing this condition. The returned prefix set is sorted in the same order as the
+     * passed set, with the same comparator.
+     */
+    private static TreeSet<IpPrefix> deduplicatePrefixSet(final TreeSet<IpPrefix> src) {
+        final TreeSet<IpPrefix> dst = new TreeSet<>(src.comparator());
+        // Prefixes match addresses that share their upper part up to their length, therefore
+        // the only kind of possible overlap in two prefixes is strict inclusion of the longer
+        // (more restrictive) in the shorter (including equivalence if they have the same
+        // length).
+        // Because prefixes in the src set are sorted from shorter to longer, deduplicating
+        // is done by simply iterating in order, and not adding any longer prefix that is
+        // already covered by a shorter one.
+        newPrefixes:
+        for (IpPrefix newPrefix : src) {
+            for (IpPrefix existingPrefix : dst) {
+                if (existingPrefix.containsPrefix(newPrefix)) {
+                    continue newPrefixes;
+                }
+            }
+            dst.add(newPrefix);
+        }
+        return dst;
+    }
+
+    /**
+     * Returns how many IPv4 addresses match any of the prefixes in the passed ordered set.
+     *
+     * Obviously this returns an integral value between 0 and 2**32.
+     * The behavior is undefined if any of the prefixes is not an IPv4 prefix or if the
+     * set is not ordered smallest prefix to longer prefix.
+     *
+     * @param prefixes the set of prefixes, ordered by length
+     */
+    public static long routedIPv4AddressCount(final TreeSet<IpPrefix> prefixes) {
+        long routedIPCount = 0;
+        for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
+            if (!prefix.isIPv4()) {
+                Log.wtf(TAG, "Non-IPv4 prefix in routedIPv4AddressCount");
+            }
+            int rank = 32 - prefix.getPrefixLength();
+            routedIPCount += 1L << rank;
+        }
+        return routedIPCount;
+    }
+
+    /**
+     * Returns how many IPv6 addresses match any of the prefixes in the passed ordered set.
+     *
+     * This returns a BigInteger between 0 and 2**128.
+     * The behavior is undefined if any of the prefixes is not an IPv6 prefix or if the
+     * set is not ordered smallest prefix to longer prefix.
+     */
+    public static BigInteger routedIPv6AddressCount(final TreeSet<IpPrefix> prefixes) {
+        BigInteger routedIPCount = BigInteger.ZERO;
+        for (final IpPrefix prefix : deduplicatePrefixSet(prefixes)) {
+            if (!prefix.isIPv6()) {
+                Log.wtf(TAG, "Non-IPv6 prefix in routedIPv6AddressCount");
+            }
+            int rank = 128 - prefix.getPrefixLength();
+            routedIPCount = routedIPCount.add(BigInteger.ONE.shiftLeft(rank));
+        }
+        return routedIPCount;
+    }
 }
diff --git a/android/net/SntpClient.java b/android/net/SntpClient.java
index ffc735c..66cdc99 100644
--- a/android/net/SntpClient.java
+++ b/android/net/SntpClient.java
@@ -80,25 +80,27 @@
      *
      * @param host host name of the server.
      * @param timeout network timeout in milliseconds.
+     * @param network network over which to send the request.
      * @return true if the transaction was successful.
      */
-    public boolean requestTime(String host, int timeout) {
+    public boolean requestTime(String host, int timeout, Network network) {
         InetAddress address = null;
         try {
-            address = InetAddress.getByName(host);
+            address = network.getByName(host);
         } catch (Exception e) {
             EventLogTags.writeNtpFailure(host, e.toString());
             if (DBG) Log.d(TAG, "request time failed: " + e);
             return false;
         }
-        return requestTime(address, NTP_PORT, timeout);
+        return requestTime(address, NTP_PORT, timeout, network);
     }
 
-    public boolean requestTime(InetAddress address, int port, int timeout) {
+    public boolean requestTime(InetAddress address, int port, int timeout, Network network) {
         DatagramSocket socket = null;
         final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
         try {
             socket = new DatagramSocket();
+            network.bindSocket(socket);
             socket.setSoTimeout(timeout);
             byte[] buffer = new byte[NTP_PACKET_SIZE];
             DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, port);
@@ -168,6 +170,12 @@
         return true;
     }
 
+    @Deprecated
+    public boolean requestTime(String host, int timeout) {
+        Log.w(TAG, "Shame on you for calling the hidden API requestTime()!");
+        return false;
+    }
+
     /**
      * Returns the time computed from the NTP transaction.
      *
diff --git a/android/net/TrafficStats.java b/android/net/TrafficStats.java
index bda720b..40d53b7 100644
--- a/android/net/TrafficStats.java
+++ b/android/net/TrafficStats.java
@@ -16,7 +16,6 @@
 
 package android.net;
 
-import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -259,30 +258,47 @@
     /**
      * Set specific UID to use when accounting {@link Socket} traffic
      * originating from the current thread. Designed for use when performing an
-     * operation on behalf of another application.
+     * operation on behalf of another application, or when another application
+     * is performing operations on your behalf.
+     * <p>
+     * Any app can <em>accept</em> blame for traffic performed on a socket
+     * originally created by another app by calling this method with the
+     * {@link android.system.Os#getuid()} value. However, only apps holding the
+     * {@code android.Manifest.permission#UPDATE_DEVICE_STATS} permission may
+     * <em>assign</em> blame to another UIDs.
      * <p>
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
-     * <p>
-     * To take effect, caller must hold
-     * {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
-     *
-     * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @SuppressLint("Doclava125")
     public static void setThreadStatsUid(int uid) {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
     }
 
     /**
+     * Get the active UID used when accounting {@link Socket} traffic originating
+     * from the current thread. Only one active tag per thread is supported.
+     * {@link #tagSocket(Socket)}.
+     *
+     * @see #setThreadStatsUid(int)
+     */
+    public static int getThreadStatsUid() {
+        return NetworkManagementSocketTagger.getThreadSocketStatsUid();
+    }
+
+    /**
      * Set specific UID to use when accounting {@link Socket} traffic
      * originating from the current thread as the calling UID. Designed for use
      * when another application is performing operations on your behalf.
      * <p>
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
+     *
+     * @removed
+     * @deprecated use {@link #setThreadStatsUid(int)} instead.
      */
+    @Deprecated
     public static void setThreadStatsUidSelf() {
         setThreadStatsUid(android.os.Process.myUid());
     }
@@ -439,6 +455,10 @@
         }
     }
 
+    private static long addIfSupported(long stat) {
+        return (stat == UNSUPPORTED) ? 0 : stat;
+    }
+
     /**
      * Return number of packets transmitted across mobile networks since device
      * boot. Counts packets across all mobile network interfaces, and always
@@ -451,7 +471,7 @@
     public static long getMobileTxPackets() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getTxPackets(iface);
+            total += addIfSupported(getTxPackets(iface));
         }
         return total;
     }
@@ -468,7 +488,7 @@
     public static long getMobileRxPackets() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getRxPackets(iface);
+            total += addIfSupported(getRxPackets(iface));
         }
         return total;
     }
@@ -485,7 +505,7 @@
     public static long getMobileTxBytes() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getTxBytes(iface);
+            total += addIfSupported(getTxBytes(iface));
         }
         return total;
     }
@@ -502,7 +522,7 @@
     public static long getMobileRxBytes() {
         long total = 0;
         for (String iface : getMobileIfaces()) {
-            total += getRxBytes(iface);
+            total += addIfSupported(getRxBytes(iface));
         }
         return total;
     }
@@ -517,9 +537,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
-            if (stat != UNSUPPORTED) {
-                total += stat;
-            }
+            total += addIfSupported(stat);
         }
         return total;
     }
@@ -534,9 +552,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
-            if (stat != UNSUPPORTED) {
-                total += stat;
-            }
+            total += addIfSupported(stat);
         }
         return total;
     }
diff --git a/android/net/UidRange.java b/android/net/UidRange.java
index fd465d9..3164929 100644
--- a/android/net/UidRange.java
+++ b/android/net/UidRange.java
@@ -21,8 +21,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.IllegalArgumentException;
-
 /**
  * An inclusive range of UIDs.
  *
@@ -53,6 +51,13 @@
     }
 
     /**
+     * Returns the count of UIDs in this range.
+     */
+    public int count() {
+        return 1 + stop - start;
+    }
+
+    /**
      * @return {@code true} if this range contains every UID contained by the {@param other} range.
      */
     public boolean containsRange(UidRange other) {
diff --git a/android/net/Uri.java b/android/net/Uri.java
index 9edcc0e..437153b 100644
--- a/android/net/Uri.java
+++ b/android/net/Uri.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.content.Intent;
 import android.os.Environment;
 import android.os.Parcel;
@@ -23,6 +24,8 @@
 import android.os.StrictMode;
 import android.util.Log;
 
+import libcore.net.UriCodec;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -38,8 +41,6 @@
 import java.util.RandomAccess;
 import java.util.Set;
 
-import libcore.net.UriCodec;
-
 /**
  * Immutable URI reference. A URI reference includes a URI and a fragment, the
  * component of the URI following a '#'. Builds and parses URI references
@@ -174,6 +175,7 @@
      *
      * @return the scheme or null if this is a relative URI
      */
+    @Nullable
     public abstract String getScheme();
 
     /**
@@ -208,6 +210,7 @@
      *
      * @return the authority for this URI or null if not present
      */
+    @Nullable
     public abstract String getAuthority();
 
     /**
@@ -219,6 +222,7 @@
      *
      * @return the authority for this URI or null if not present
      */
+    @Nullable
     public abstract String getEncodedAuthority();
 
     /**
@@ -228,6 +232,7 @@
      *
      * @return the user info for this URI or null if not present
      */
+    @Nullable
     public abstract String getUserInfo();
 
     /**
@@ -237,6 +242,7 @@
      *
      * @return the user info for this URI or null if not present
      */
+    @Nullable
     public abstract String getEncodedUserInfo();
 
     /**
@@ -246,6 +252,7 @@
      *
      * @return the host for this URI or null if not present
      */
+    @Nullable
     public abstract String getHost();
 
     /**
@@ -262,6 +269,7 @@
      * @return the decoded path, or null if this is not a hierarchical URI
      * (like "mailto:[email protected]") or the URI is invalid
      */
+    @Nullable
     public abstract String getPath();
 
     /**
@@ -270,6 +278,7 @@
      * @return the encoded path, or null if this is not a hierarchical URI
      * (like "mailto:[email protected]") or the URI is invalid
      */
+    @Nullable
     public abstract String getEncodedPath();
 
     /**
@@ -280,6 +289,7 @@
      *
      * @return the decoded query or null if there isn't one
      */
+    @Nullable
     public abstract String getQuery();
 
     /**
@@ -290,6 +300,7 @@
      *
      * @return the encoded query or null if there isn't one
      */
+    @Nullable
     public abstract String getEncodedQuery();
 
     /**
@@ -297,6 +308,7 @@
      *
      * @return the decoded fragment or null if there isn't one
      */
+    @Nullable
     public abstract String getFragment();
 
     /**
@@ -304,6 +316,7 @@
      *
      * @return the encoded fragment or null if there isn't one
      */
+    @Nullable
     public abstract String getEncodedFragment();
 
     /**
@@ -318,6 +331,7 @@
      *
      * @return the decoded last segment or null if the path is empty
      */
+    @Nullable
     public abstract String getLastPathSegment();
 
     /**
@@ -720,6 +734,10 @@
                 LOOP: while (end < length) {
                     switch (uriString.charAt(end)) {
                         case '/': // Start of path
+                        case '\\':// Start of path
+                          // Per http://url.spec.whatwg.org/#host-state, the \ character
+                          // is treated as if it were a / character when encountered in a
+                          // host
                         case '?': // Start of query
                         case '#': // Start of fragment
                             break LOOP;
@@ -758,6 +776,10 @@
                         case '#': // Start of fragment
                             return ""; // Empty path.
                         case '/': // Start of path!
+                        case '\\':// Start of path!
+                          // Per http://url.spec.whatwg.org/#host-state, the \ character
+                          // is treated as if it were a / character when encountered in a
+                          // host
                             break LOOP;
                     }
                     pathStart++;
@@ -1666,6 +1688,7 @@
      * @throws NullPointerException if key is null
      * @return the decoded value or null if no parameter is found
      */
+    @Nullable
     public String getQueryParameter(String key) {
         if (isOpaque()) {
             throw new UnsupportedOperationException(NOT_HIERARCHICAL);
diff --git a/android/net/VpnService.java b/android/net/VpnService.java
index 185b181..712576f 100644
--- a/android/net/VpnService.java
+++ b/android/net/VpnService.java
@@ -192,7 +192,7 @@
      */
     public static Intent prepare(Context context) {
         try {
-            if (getService().prepareVpn(context.getPackageName(), null, UserHandle.myUserId())) {
+            if (getService().prepareVpn(context.getPackageName(), null, context.getUserId())) {
                 return null;
             }
         } catch (RemoteException e) {
@@ -219,7 +219,7 @@
         String packageName = context.getPackageName();
         try {
             // Only prepare if we're not already prepared.
-            int userId = UserHandle.myUserId();
+            int userId = context.getUserId();
             if (!cm.prepareVpn(packageName, null, userId)) {
                 cm.prepareVpn(null, packageName, userId);
             }
diff --git a/android/net/apf/ApfFilter.java b/android/net/apf/ApfFilter.java
index 7d9736e..d190432 100644
--- a/android/net/apf/ApfFilter.java
+++ b/android/net/apf/ApfFilter.java
@@ -1068,6 +1068,7 @@
         mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
     }
 
+    @GuardedBy("this")
     private void logApfProgramEventLocked(long now) {
         if (mLastInstallEvent == null) {
             return;
diff --git a/android/net/apf/ApfGenerator.java b/android/net/apf/ApfGenerator.java
index d41fbce..ca8f727 100644
--- a/android/net/apf/ApfGenerator.java
+++ b/android/net/apf/ApfGenerator.java
@@ -367,7 +367,7 @@
      */
     public boolean setApfVersion(int version) {
         // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
-        return version == 2;
+        return version >= 2;
     }
 
     private void addInstruction(Instruction instruction) {
diff --git a/android/net/ip/IpClient.java b/android/net/ip/IpClient.java
index d3a97b3..9863370 100644
--- a/android/net/ip/IpClient.java
+++ b/android/net/ip/IpClient.java
@@ -72,6 +72,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.StringJoiner;
+import java.util.concurrent.CountDownLatch;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -100,6 +101,11 @@
 
     /**
      * Callbacks for handling IpClient events.
+     *
+     * These methods are called by IpClient on its own thread. Implementations
+     * of this class MUST NOT carry out long-running computations or hold locks
+     * for which there might be contention with other code calling public
+     * methods of the same IpClient instance.
      */
     public static class Callback {
         // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
@@ -534,6 +540,8 @@
     // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
     private static final String CLAT_PREFIX = "v4-";
 
+    private static final int IMMEDIATE_FAILURE_DURATION = 0;
+
     private final State mStoppedState = new StoppedState();
     private final State mStoppingState = new StoppingState();
     private final State mStartedState = new StartedState();
@@ -545,6 +553,8 @@
     private final String mClatInterfaceName;
     @VisibleForTesting
     protected final Callback mCallback;
+    private final Dependencies mDependencies;
+    private final CountDownLatch mShutdownLatch;
     private final INetworkManagementService mNwService;
     private final NetlinkTracker mNetlinkTracker;
     private final WakeupMessage mProvisioningTimeoutAlarm;
@@ -572,10 +582,23 @@
     private boolean mMulticastFiltering;
     private long mStartTimeMillis;
 
+    public static class Dependencies {
+        public INetworkManagementService getNMS() {
+            return INetworkManagementService.Stub.asInterface(
+                    ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+        }
+
+        public INetd getNetd() {
+            return NetdService.getInstance();
+        }
+
+        public InterfaceParams getInterfaceParams(String ifname) {
+            return InterfaceParams.getByName(ifname);
+        }
+    }
+
     public IpClient(Context context, String ifName, Callback callback) {
-        this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)),
-                NetdService.getInstance());
+        this(context, ifName, callback, new Dependencies());
     }
 
     /**
@@ -584,26 +607,35 @@
      */
     public IpClient(Context context, String ifName, Callback callback,
             INetworkManagementService nwService) {
-        this(context, ifName, callback, nwService, NetdService.getInstance());
+        this(context, ifName, callback, new Dependencies() {
+            @Override
+            public INetworkManagementService getNMS() { return nwService; }
+        });
     }
 
     @VisibleForTesting
-    IpClient(Context context, String ifName, Callback callback,
-            INetworkManagementService nwService, INetd netd) {
+    IpClient(Context context, String ifName, Callback callback, Dependencies deps) {
         super(IpClient.class.getSimpleName() + "." + ifName);
+        Preconditions.checkNotNull(ifName);
+        Preconditions.checkNotNull(callback);
+
         mTag = getName();
 
         mContext = context;
         mInterfaceName = ifName;
         mClatInterfaceName = CLAT_PREFIX + ifName;
         mCallback = new LoggingCallbackWrapper(callback);
-        mNwService = nwService;
+        mDependencies = deps;
+        mShutdownLatch = new CountDownLatch(1);
+        mNwService = deps.getNMS();
 
         mLog = new SharedLog(MAX_LOG_RECORDS, mTag);
         mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
         mMsgStateLogger = new MessageHandlingLogger();
 
-        mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, netd, mLog);
+        // TODO: Consider creating, constructing, and passing in some kind of
+        // InterfaceController.Dependencies class.
+        mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, deps.getNetd(), mLog);
 
         mNetlinkTracker = new NetlinkTracker(
                 mInterfaceName,
@@ -704,6 +736,7 @@
     @Override
     protected void onQuitting() {
         mCallback.onQuit();
+        mShutdownLatch.countDown();
     }
 
     // Shut down this IpClient instance altogether.
@@ -712,6 +745,17 @@
         sendMessage(CMD_TERMINATE_AFTER_STOP);
     }
 
+    // In order to avoid deadlock, this method MUST NOT be called on the
+    // IpClient instance's thread. This prohibition includes code executed by
+    // when methods on the passed-in IpClient.Callback instance are called.
+    public void awaitShutdown() {
+        try {
+            mShutdownLatch.await();
+        } catch (InterruptedException e) {
+            mLog.e("Interrupted while awaiting shutdown: " + e);
+        }
+    }
+
     public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
         return new ProvisioningConfiguration.Builder();
     }
@@ -722,11 +766,11 @@
             return;
         }
 
-        mInterfaceParams = InterfaceParams.getByName(mInterfaceName);
+        mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
         if (mInterfaceParams == null) {
             logError("Failed to find InterfaceParams for " + mInterfaceName);
-            // TODO: call doImmediateProvisioningFailure() with an error code
-            // indicating something like "interface not ready".
+            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
+            return;
         }
 
         mCallback.setNeighborDiscoveryOffload(true);
@@ -910,8 +954,11 @@
     }
 
     private void recordMetric(final int type) {
-        if (mStartTimeMillis <= 0) { Log.wtf(mTag, "Start time undefined!"); }
-        final long duration = SystemClock.elapsedRealtime() - mStartTimeMillis;
+        // We may record error metrics prior to starting.
+        // Map this to IMMEDIATE_FAILURE_DURATION.
+        final long duration = (mStartTimeMillis > 0)
+                ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
+                : IMMEDIATE_FAILURE_DURATION;
         mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
     }
 
diff --git a/android/net/ip/IpManager.java b/android/net/ip/IpManager.java
index 3898145..508a43d 100644
--- a/android/net/ip/IpManager.java
+++ b/android/net/ip/IpManager.java
@@ -144,20 +144,7 @@
     }
 
     public IpManager(Context context, String ifName, Callback callback) {
-        this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)),
-                NetdService.getInstance());
-    }
-
-    public IpManager(Context context, String ifName, Callback callback,
-            INetworkManagementService nwService) {
-        this(context, ifName, callback, nwService, NetdService.getInstance());
-    }
-
-    @VisibleForTesting
-    public IpManager(Context context, String ifName, Callback callback,
-            INetworkManagementService nwService, INetd netd) {
-        super(context, ifName, callback, nwService, netd);
+        super(context, ifName, callback);
     }
 
     public void startProvisioning(ProvisioningConfiguration req) {
diff --git a/android/net/ip/RouterAdvertisementDaemon.java b/android/net/ip/RouterAdvertisementDaemon.java
index 49a1e79..8fbc01e 100644
--- a/android/net/ip/RouterAdvertisementDaemon.java
+++ b/android/net/ip/RouterAdvertisementDaemon.java
@@ -268,6 +268,7 @@
         mUnicastResponder = null;
     }
 
+    @GuardedBy("mLock")
     private void assembleRaLocked() {
         final ByteBuffer ra = ByteBuffer.wrap(mRA);
         ra.order(ByteOrder.BIG_ENDIAN);
diff --git a/android/net/lowpan/LowpanManager.java b/android/net/lowpan/LowpanManager.java
index 2d974ee..76876ce 100644
--- a/android/net/lowpan/LowpanManager.java
+++ b/android/net/lowpan/LowpanManager.java
@@ -57,7 +57,7 @@
      * This design pattern allows us to skip removal of items
      * from this Map without leaking memory.
      */
-    private final Map<ILowpanInterface, WeakReference<LowpanInterface>> mBinderCache =
+    private final Map<IBinder, WeakReference<LowpanInterface>> mBinderCache =
             new WeakHashMap<>();
 
     private final ILowpanManager mService;
@@ -109,13 +109,27 @@
 
     /** @hide */
     @Nullable
+    public LowpanInterface getInterfaceNoCreate(@NonNull ILowpanInterface ifaceService) {
+        LowpanInterface iface = null;
+
+        synchronized (mBinderCache) {
+            if (mBinderCache.containsKey(ifaceService.asBinder())) {
+                iface = mBinderCache.get(ifaceService.asBinder()).get();
+            }
+        }
+
+        return iface;
+    }
+
+    /** @hide */
+    @Nullable
     public LowpanInterface getInterface(@NonNull ILowpanInterface ifaceService) {
         LowpanInterface iface = null;
 
         try {
             synchronized (mBinderCache) {
-                if (mBinderCache.containsKey(ifaceService)) {
-                    iface = mBinderCache.get(ifaceService).get();
+                if (mBinderCache.containsKey(ifaceService.asBinder())) {
+                    iface = mBinderCache.get(ifaceService.asBinder()).get();
                 }
 
                 if (iface == null) {
@@ -127,7 +141,7 @@
                         mInterfaceCache.put(iface.getName(), iface);
                     }
 
-                    mBinderCache.put(ifaceService, new WeakReference(iface));
+                    mBinderCache.put(ifaceService.asBinder(), new WeakReference(iface));
 
                     /* Make sure we remove the object from the
                      * interface cache if the associated service
@@ -260,7 +274,7 @@
                     public void onInterfaceRemoved(ILowpanInterface ifaceService) {
                         Runnable runnable =
                                 () -> {
-                                    LowpanInterface iface = getInterface(ifaceService);
+                                    LowpanInterface iface = getInterfaceNoCreate(ifaceService);
 
                                     if (iface != null) {
                                         cb.onInterfaceRemoved(iface);
diff --git a/android/net/metrics/IpManagerEvent.java b/android/net/metrics/IpManagerEvent.java
index a94b928..f8a63ce 100644
--- a/android/net/metrics/IpManagerEvent.java
+++ b/android/net/metrics/IpManagerEvent.java
@@ -40,11 +40,12 @@
     public static final int ERROR_STARTING_IPV6                   = 5;
     public static final int ERROR_STARTING_IPREACHABILITYMONITOR  = 6;
     public static final int ERROR_INVALID_PROVISIONING            = 7;
+    public static final int ERROR_INTERFACE_NOT_FOUND             = 8;
 
     @IntDef(value = {
             PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE,
             ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR,
-            ERROR_INVALID_PROVISIONING,
+            ERROR_INVALID_PROVISIONING, ERROR_INTERFACE_NOT_FOUND,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
diff --git a/android/net/metrics/NetworkMetrics.java b/android/net/metrics/NetworkMetrics.java
index 2b662a0..66d92c4 100644
--- a/android/net/metrics/NetworkMetrics.java
+++ b/android/net/metrics/NetworkMetrics.java
@@ -96,6 +96,16 @@
         }
     }
 
+    /** Accumulate a single netd sock_diag poll result reported by netd. */
+    public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+        if (pendingSummary == null) {
+            pendingSummary = new Summary(netId, transports);
+        }
+        pendingSummary.tcpLossRate.count(lost, sent);
+        pendingSummary.roundTripTimeUs.count(rttUs);
+        pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
+    }
+
     /** Represents running sums for dns and connect average error counts and average latencies. */
     public static class Summary {
 
@@ -109,6 +119,13 @@
         public final Metrics connectLatencies = new Metrics();
         // Blocking and non blocking connect error rate measured in percentage points.
         public final Metrics connectErrorRate = new Metrics();
+        // TCP socket packet loss stats collected from Netlink sock_diag.
+        public final Metrics tcpLossRate = new Metrics();
+        // TCP averaged microsecond round-trip-time stats collected from Netlink sock_diag.
+        public final Metrics roundTripTimeUs = new Metrics();
+        // TCP stats collected from Netlink sock_diag that averages millisecond per-socket
+        // differences between last packet sent timestamp and last ack received timestamp.
+        public final Metrics sentAckTimeDiffenceMs = new Metrics();
 
         public Summary(int netId, long transports) {
             this.netId = netId;
@@ -120,6 +137,7 @@
             dnsErrorRate.merge(that.dnsErrorRate);
             connectLatencies.merge(that.connectLatencies);
             connectErrorRate.merge(that.connectErrorRate);
+            tcpLossRate.merge(that.tcpLossRate);
         }
 
         @Override
@@ -135,6 +153,10 @@
             j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
                     (int) connectLatencies.average(), (int) connectLatencies.max,
                     100 * connectErrorRate.average(), connectErrorRate.count));
+            j.add(String.format("tcp avg_loss=%.1f%% total_sent=%d total_lost=%d",
+                    100 * tcpLossRate.average(), tcpLossRate.count, (int) tcpLossRate.sum));
+            j.add(String.format("tcp rtt=%dms", (int) (roundTripTimeUs.average() / 1000)));
+            j.add(String.format("tcp sent-ack_diff=%dms", (int) sentAckTimeDiffenceMs.average()));
             return j.toString();
         }
     }
@@ -152,7 +174,11 @@
         }
 
         void count(double value) {
-            count++;
+            count(value, 1);
+        }
+
+        void count(double value, int subcount) {
+            count += subcount;
             sum += value;
             max = Math.max(max, value);
         }
diff --git a/android/net/util/InterfaceSet.java b/android/net/util/InterfaceSet.java
new file mode 100644
index 0000000..9f26fa1
--- /dev/null
+++ b/android/net/util/InterfaceSet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+/**
+ * @hide
+ */
+public class InterfaceSet {
+    public final Set<String> ifnames;
+
+    public InterfaceSet(String... names) {
+        final Set<String> nameSet = new HashSet<>();
+        for (String name : names) {
+            if (name != null) nameSet.add(name);
+        }
+        ifnames = Collections.unmodifiableSet(nameSet);
+    }
+
+    @Override
+    public String toString() {
+        final StringJoiner sj = new StringJoiner(",", "[", "]");
+        for (String ifname : ifnames) sj.add(ifname);
+        return sj.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj != null
+                && obj instanceof InterfaceSet
+                && ifnames.equals(((InterfaceSet)obj).ifnames);
+    }
+}
diff --git a/android/net/wifi/RttManager.java b/android/net/wifi/RttManager.java
index dc5ba0c..50c4b5e 100644
--- a/android/net/wifi/RttManager.java
+++ b/android/net/wifi/RttManager.java
@@ -7,24 +7,24 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
+import java.util.List;
+
 /** @hide */
 @SystemApi
+@Deprecated
 @SystemService(Context.WIFI_RTT_SERVICE)
 public class RttManager {
 
@@ -175,12 +175,14 @@
     @Deprecated
     @SuppressLint("Doclava125")
     public Capabilities getCapabilities() {
-        return new Capabilities();
+        throw new UnsupportedOperationException(
+                "getCapabilities is not supported in the adaptation layer");
     }
 
     /**
      * This class describe the RTT capability of the Hardware
      */
+    @Deprecated
     public static class RttCapabilities implements Parcelable {
         /** @deprecated It is not supported*/
         @Deprecated
@@ -314,21 +316,16 @@
              };
     }
 
+    /**
+     * This method is deprecated. Please use the {@link WifiRttManager} API.
+     */
     @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
     public RttCapabilities getRttCapabilities() {
-        synchronized (mCapabilitiesLock) {
-            if (mRttCapabilities == null) {
-                try {
-                    mRttCapabilities = mService.getRttCapabilities();
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
-            return mRttCapabilities;
-        }
+        return mRttCapabilities;
     }
 
     /** specifies parameters for RTT request */
+    @Deprecated
     public static class RttParams {
         /**
          * type of destination device being ranged
@@ -511,6 +508,7 @@
     }
 
     /** pseudo-private class used to parcel arguments */
+    @Deprecated
     public static class ParcelableRttParams implements Parcelable {
 
         @NonNull
@@ -598,12 +596,14 @@
                 };
     }
 
+    @Deprecated
     public static class WifiInformationElement {
         /** Information Element ID 0xFF means element is invalid. */
         public byte id;
         public byte[] data;
     }
     /** specifies RTT results */
+    @Deprecated
     public static class RttResult {
         /** mac address of the device being ranged. */
         public String bssid;
@@ -755,6 +755,7 @@
 
 
     /** pseudo-private class used to parcel results. */
+    @Deprecated
     public static class ParcelableRttResults implements Parcelable {
 
         public RttResult mResults[];
@@ -847,8 +848,8 @@
                     }
                     dest.writeByte(result.LCR.id);
                     if (result.LCR.id != (byte) 0xFF) {
-                        dest.writeInt((byte) result.LCR.data.length);
-                        dest.writeByte(result.LCR.id);
+                        dest.writeByte((byte) result.LCR.data.length);
+                        dest.writeByteArray(result.LCR.data);
                     }
                     dest.writeByte(result.secure ? (byte) 1 : 0);
                 }
@@ -920,7 +921,7 @@
                 };
     }
 
-
+    @Deprecated
     public static interface RttListener {
         public void onSuccess(RttResult[] results);
         public void onFailure(int reason, String description);
@@ -928,115 +929,11 @@
     }
 
     /**
-     * A parcelable that contains rtt client information.
-     *
-     * @hide
-     */
-    public static class RttClient implements Parcelable {
-        // Package name of RttClient.
-        private final String mPackageName;
-
-        public RttClient(String packageName) {
-            mPackageName = packageName;
-        }
-
-        protected RttClient(Parcel in) {
-            mPackageName = in.readString();
-        }
-
-        public static final Creator<RttManager.RttClient> CREATOR =
-                new Creator<RttManager.RttClient>() {
-            @Override
-            public RttManager.RttClient createFromParcel(Parcel in) {
-                return new RttManager.RttClient(in);
-            }
-
-            @Override
-            public RttManager.RttClient[] newArray(int size) {
-                return new RttManager.RttClient[size];
-            }
-        };
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int i) {
-            parcel.writeString(mPackageName);
-        }
-
-        public String getPackageName() {
-            return mPackageName;
-        }
-    }
-
-    private boolean rttParamSanity(RttParams params, int index) {
-        if (mRttCapabilities == null) {
-            if(getRttCapabilities() == null) {
-                Log.e(TAG, "Can not get RTT capabilities");
-                throw new IllegalStateException("RTT chip is not working");
-            }
-        }
-
-        if (params.deviceType != RTT_PEER_TYPE_AP) {
-            return false;
-        } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
-                RTT_TYPE_TWO_SIDED) {
-            Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
-            return false;
-        } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
-                !mRttCapabilities.oneSidedRttSupported) {
-            Log.e(TAG, "Request " + index + ": One side RTT is not supported");
-            return false;
-        } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
-                !mRttCapabilities.twoSided11McRttSupported) {
-            Log.e(TAG, "Request " + index + ": two side RTT is not supported");
-            return false;
-        }  else if(params.bssid == null || params.bssid.isEmpty()) {
-            Log.e(TAG,"No BSSID in params");
-            return false;
-        } else if ( params.numberBurst != 0 ) {
-            Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
-            return false;
-        } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
-            Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
-                    params.numSamplesPerBurst);
-            return false;
-        } else if (params.numRetriesPerMeasurementFrame < 0 ||
-                params.numRetriesPerMeasurementFrame > 3) {
-            Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
-                    params.numRetriesPerMeasurementFrame);
-            return false;
-        } else if(params.numRetriesPerFTMR < 0 ||
-                params.numRetriesPerFTMR > 3) {
-            Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
-                    params.numRetriesPerFTMR);
-            return false;
-        } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
-            Log.e(TAG, "Request " + index + ": LCI is not supported");
-            return false;
-        } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
-            Log.e(TAG, "Request " + index + ": LCR is not supported");
-            return false;
-        } else if (params.burstTimeout < 1 ||
-                (params.burstTimeout > 11 && params.burstTimeout != 15)){
-            Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
-            return false;
-        } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
-            Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
-            return false;
-        } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
-            Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
      * Request to start an RTT ranging
+     * <p>
+     * This method is deprecated. Please use the
+     * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}
+     * API.
      *
      * @param params  -- RTT request Parameters
      * @param listener -- Call back to inform RTT result
@@ -1045,24 +942,84 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void startRanging(RttParams[] params, RttListener listener) {
-        int index  = 0;
-        for(RttParams rttParam : params) {
-            if (!rttParamSanity(rttParam, index)) {
-                throw new IllegalArgumentException("RTT Request Parameter Illegal");
-            }
-            index++;
-        }
-        validateChannel();
-        ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
         Log.i(TAG, "Send RTT request to RTT Service");
-        mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
-                0, putListener(listener), parcelableParams);
+
+        if (!mNewService.isAvailable()) {
+            listener.onFailure(REASON_NOT_AVAILABLE, "");
+            return;
+        }
+
+        RangingRequest.Builder builder = new RangingRequest.Builder();
+        for (RttParams rttParams : params) {
+            if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+                listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+                return;
+            }
+
+            ScanResult reconstructed = new ScanResult();
+            reconstructed.BSSID = rttParams.bssid;
+            if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+                reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+            }
+            reconstructed.channelWidth = rttParams.channelWidth;
+            reconstructed.frequency = rttParams.frequency;
+            reconstructed.centerFreq0 = rttParams.centerFreq0;
+            reconstructed.centerFreq1 = rttParams.centerFreq1;
+            builder.addResponder(
+                    android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+        }
+        try {
+            mNewService.startRanging(builder.build(),
+                    mContext.getMainExecutor(),
+                    new RangingResultCallback() {
+                @Override
+                public void onRangingFailure(int code) {
+                    int localCode = REASON_UNSPECIFIED;
+                    if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+                        localCode = REASON_NOT_AVAILABLE;
+                    }
+                    listener.onFailure(localCode, "");
+                }
+
+                @Override
+                public void onRangingResults(List<RangingResult> results) {
+                    RttResult[] legacyResults = new RttResult[results.size()];
+                    int i = 0;
+                    for (RangingResult result : results) {
+                        legacyResults[i] = new RttResult();
+                        legacyResults[i].status = result.getStatus();
+                        legacyResults[i].bssid = result.getMacAddress().toString();
+                        if (result.getStatus() == RangingResult.STATUS_SUCCESS) {
+                            legacyResults[i].distance = result.getDistanceMm() / 10;
+                            legacyResults[i].distanceStandardDeviation =
+                                    result.getDistanceStdDevMm() / 10;
+                            legacyResults[i].rssi = result.getRssi() * -2;
+                            legacyResults[i].ts = result.getRangingTimestampMillis() * 1000;
+                        } else {
+                            // just in case legacy API needed some relatively real timestamp
+                            legacyResults[i].ts = SystemClock.elapsedRealtime() * 1000;
+                        }
+                        i++;
+                    }
+                    listener.onSuccess(legacyResults);
+                }
+            });
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "startRanging: invalid arguments - " + e);
+            listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+        } catch (SecurityException e) {
+            Log.e(TAG, "startRanging: security exception - " + e);
+            listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
+        }
     }
 
+    /**
+     * This method is deprecated and performs no function. Please use the {@link WifiRttManager}
+     * API.
+     */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void stopRanging(RttListener listener) {
-        validateChannel();
-        mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+        Log.e(TAG, "stopRanging: unsupported operation - nop");
     }
 
     /**
@@ -1074,6 +1031,7 @@
      * The client can freely destroy or reuse the callback after {@link RttManager#disableResponder}
      * is called.
      */
+    @Deprecated
     public abstract static class ResponderCallback {
         /** Callback when responder is enabled. */
         public abstract void onResponderEnabled(ResponderConfig config);
@@ -1089,18 +1047,18 @@
      * Note calling this method with the same callback when the responder is already enabled won't
      * change the responder state, a cached {@link ResponderConfig} from the last enabling will be
      * returned through the callback.
+     * <p>
+     * This method is deprecated and will throw an {@link UnsupportedOperationException}
+     * exception. Please use the {@link WifiRttManager} API to perform a Wi-Fi Aware peer-to-peer
+     * ranging.
      *
      * @param callback Callback for responder enabling/disabling result.
      * @throws IllegalArgumentException If {@code callback} is null.
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void enableResponder(ResponderCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        validateChannel();
-        int key = putListenerIfAbsent(callback);
-        mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+        throw new UnsupportedOperationException(
+                "enableResponder is not supported in the adaptation layer");
     }
 
     /**
@@ -1109,22 +1067,18 @@
      * <p>
      * Calling this method when responder isn't enabled won't have any effect. The callback can be
      * reused for enabling responder after this method is called.
+     * <p>
+     * This method is deprecated and will throw an {@link UnsupportedOperationException}
+     * exception. Please use the {@link WifiRttManager} API to perform a Wi-Fi Aware peer-to-peer
+     * ranging.
      *
      * @param callback The same callback used for enabling responder.
      * @throws IllegalArgumentException If {@code callback} is null.
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void disableResponder(ResponderCallback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null");
-        }
-        validateChannel();
-        int key = removeListener(callback);
-        if (key == INVALID_KEY) {
-            Log.e(TAG, "responder not enabled yet");
-            return;
-        }
-        mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+        throw new UnsupportedOperationException(
+                "disableResponder is not supported in the adaptation layer");
     }
 
     /**
@@ -1133,6 +1087,7 @@
      *
      * @see ScanResult
      */
+    @Deprecated
     public static class ResponderConfig implements Parcelable {
 
         // TODO: make all fields final once we can get mac address from responder HAL APIs.
@@ -1238,195 +1193,35 @@
     /** @hide */
     public static final int CMD_OP_REG_BINDER           = BASE + 9;
 
-    private static final int INVALID_KEY = 0;
-
+    private final WifiRttManager mNewService;
     private final Context mContext;
-    private final IRttManager mService;
-    private final SparseArray mListenerMap = new SparseArray();
-    private final Object mListenerMapLock = new Object();
-    private final Object mCapabilitiesLock = new Object();
-
     private RttCapabilities mRttCapabilities;
-    private int mListenerKey = 1;
-    private AsyncChannel mAsyncChannel;
 
     /**
      * Create a new WifiScanner instance.
      * Applications will almost always want to use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
      * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
-     * @param context the application context
-     * @param service the Binder interface
-     * @param looper Looper for running the callbacks.
+     * @param service the new WifiRttManager service
      *
      * @hide
      */
-    public RttManager(Context context, IRttManager service, Looper looper) {
+    public RttManager(Context context, WifiRttManager service) {
+        mNewService = service;
         mContext = context;
-        mService = service;
-        Messenger messenger = null;
-        int[] key = new int[1];
-        try {
-            Log.d(TAG, "Get the messenger from " + mService);
-            messenger = mService.getMessenger(new Binder(), key);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
 
-        if (messenger == null) {
-            throw new IllegalStateException("getMessenger() returned null!  This is invalid.");
-        }
+        boolean rttSupported = context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_RTT);
 
-        mAsyncChannel = new AsyncChannel();
-
-        Handler handler = new ServiceHandler(looper);
-        mAsyncChannel.connectSync(mContext, handler, messenger);
-        // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
-        // synchronously, which causes RttService to receive the wrong replyTo value.
-        mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
-                new RttClient(context.getPackageName()));
-        mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+        mRttCapabilities = new RttCapabilities();
+        mRttCapabilities.oneSidedRttSupported = rttSupported;
+        mRttCapabilities.twoSided11McRttSupported = rttSupported;
+        mRttCapabilities.lciSupported = false;
+        mRttCapabilities.lcrSupported = false;
+        mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+        mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+        mRttCapabilities.responderSupported = false;
+        mRttCapabilities.secureRttSupported = false;
     }
-
-    private void validateChannel() {
-        if (mAsyncChannel == null) throw new IllegalStateException(
-                "No permission to access and change wifi or a bad initialization");
-    }
-
-    private int putListener(Object listener) {
-        if (listener == null) return INVALID_KEY;
-        int key;
-        synchronized (mListenerMapLock) {
-            do {
-                key = mListenerKey++;
-            } while (key == INVALID_KEY);
-            mListenerMap.put(key, listener);
-        }
-        return key;
-    }
-
-    // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
-    private int putListenerIfAbsent(Object listener) {
-        if (listener == null) return INVALID_KEY;
-        synchronized (mListenerMapLock) {
-            int key = getListenerKey(listener);
-            if (key != INVALID_KEY) {
-                return key;
-            }
-            do {
-                key = mListenerKey++;
-            } while (key == INVALID_KEY);
-            mListenerMap.put(key, listener);
-            return key;
-        }
-
-    }
-
-    private Object getListener(int key) {
-        if (key == INVALID_KEY) return null;
-        synchronized (mListenerMapLock) {
-            Object listener = mListenerMap.get(key);
-            return listener;
-        }
-    }
-
-    private int getListenerKey(Object listener) {
-        if (listener == null) return INVALID_KEY;
-        synchronized (mListenerMapLock) {
-            int index = mListenerMap.indexOfValue(listener);
-            if (index == -1) {
-                return INVALID_KEY;
-            } else {
-                return mListenerMap.keyAt(index);
-            }
-        }
-    }
-
-    private Object removeListener(int key) {
-        if (key == INVALID_KEY) return null;
-        synchronized (mListenerMapLock) {
-            Object listener = mListenerMap.get(key);
-            mListenerMap.remove(key);
-            return listener;
-        }
-    }
-
-    private int removeListener(Object listener) {
-        int key = getListenerKey(listener);
-        if (key == INVALID_KEY) return key;
-        synchronized (mListenerMapLock) {
-            mListenerMap.remove(key);
-            return key;
-        }
-    }
-
-    private class ServiceHandler extends Handler {
-        ServiceHandler(Looper looper) {
-            super(looper);
-        }
-        @Override
-        public void handleMessage(Message msg) {
-            Log.i(TAG, "RTT manager get message: " + msg.what);
-            switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
-                    return;
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
-                    Log.e(TAG, "Channel connection lost");
-                    // This will cause all further async API calls on the WifiManager
-                    // to fail and throw an exception
-                    mAsyncChannel = null;
-                    getLooper().quit();
-                    return;
-            }
-
-            Object listener = getListener(msg.arg2);
-            if (listener == null) {
-                Log.e(TAG, "invalid listener key = " + msg.arg2 );
-                return;
-            } else {
-                Log.i(TAG, "listener key = " + msg.arg2);
-            }
-
-            switch (msg.what) {
-                /* ActionListeners grouped together */
-                case CMD_OP_SUCCEEDED :
-                    reportSuccess(listener, msg);
-                    removeListener(msg.arg2);
-                    break;
-                case CMD_OP_FAILED :
-                    reportFailure(listener, msg);
-                    removeListener(msg.arg2);
-                    break;
-                case CMD_OP_ABORTED :
-                    ((RttListener) listener).onAborted();
-                    removeListener(msg.arg2);
-                    break;
-                case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
-                    ResponderConfig config = (ResponderConfig) msg.obj;
-                    ((ResponderCallback) (listener)).onResponderEnabled(config);
-                    break;
-                case CMD_OP_ENALBE_RESPONDER_FAILED:
-                    ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
-                    removeListener(msg.arg2);
-                    break;
-                default:
-                    if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
-                    return;
-            }
-        }
-
-        void reportSuccess(Object listener, Message msg) {
-            RttListener rttListener = (RttListener) listener;
-            ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
-            ((RttListener) listener).onSuccess(parcelableResults.mResults);
-        }
-
-        void reportFailure(Object listener, Message msg) {
-            RttListener rttListener = (RttListener) listener;
-            Bundle bundle = (Bundle) msg.obj;
-            ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
-        }
-    }
-
 }
 
diff --git a/android/net/wifi/ScanResult.java b/android/net/wifi/ScanResult.java
index c46789c..8024bf0 100644
--- a/android/net/wifi/ScanResult.java
+++ b/android/net/wifi/ScanResult.java
@@ -273,27 +273,6 @@
     public RadioChainInfo[] radioChainInfos;
 
     /**
-     * @hide
-     * Update RSSI of the scan result
-     * @param previousRssi
-     * @param previousSeen
-     * @param maxAge
-     */
-    public void averageRssi(int previousRssi, long previousSeen, int maxAge) {
-
-        if (seen == 0) {
-            seen = System.currentTimeMillis();
-        }
-        long age = seen - previousSeen;
-
-        if (previousSeen > 0 && age > 0 && age < maxAge/2) {
-            // Average the RSSI with previously seen instances of this scan result
-            double alpha = 0.5 - (double) age / (double) maxAge;
-            level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha);
-        }
-    }
-
-    /**
      * Status indicating the scan result does not correspond to a user's saved configuration
      * @hide
      * @removed
diff --git a/android/net/wifi/ScanSettings.java b/android/net/wifi/ScanSettings.java
deleted file mode 100644
index 094ce34..0000000
--- a/android/net/wifi/ScanSettings.java
+++ /dev/null
@@ -1,87 +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.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Bundle of customized scan settings
- *
- * @see WifiManager#startCustomizedScan
- *
- * @hide
- */
-public class ScanSettings implements Parcelable {
-
-    /** channel set to scan. this can be null or empty, indicating a full scan */
-    public Collection<WifiChannel> channelSet;
-
-    /** public constructor */
-    public ScanSettings() { }
-
-    /** copy constructor */
-    public ScanSettings(ScanSettings source) {
-        if (source.channelSet != null)
-            channelSet = new ArrayList<WifiChannel>(source.channelSet);
-    }
-
-    /** check for validity */
-    public boolean isValid() {
-        for (WifiChannel channel : channelSet)
-            if (!channel.isValid()) return false;
-        return true;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(channelSet == null ? 0 : channelSet.size());
-        if (channelSet != null)
-            for (WifiChannel channel : channelSet) channel.writeToParcel(out, flags);
-    }
-
-    /** implement Parcelable interface */
-    public static final Parcelable.Creator<ScanSettings> CREATOR =
-            new Parcelable.Creator<ScanSettings>() {
-        @Override
-        public ScanSettings createFromParcel(Parcel in) {
-            ScanSettings settings = new ScanSettings();
-            int size = in.readInt();
-            if (size > 0) {
-                settings.channelSet = new ArrayList<WifiChannel>(size);
-                while (size-- > 0)
-                    settings.channelSet.add(WifiChannel.CREATOR.createFromParcel(in));
-            }
-            return settings;
-        }
-
-        @Override
-        public ScanSettings[] newArray(int size) {
-            return new ScanSettings[size];
-        }
-    };
-}
diff --git a/android/net/wifi/WifiChannel.java b/android/net/wifi/WifiChannel.java
deleted file mode 100644
index 640481e..0000000
--- a/android/net/wifi/WifiChannel.java
+++ /dev/null
@@ -1,87 +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.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Wifi Channel
- *
- * @see ScanSettings
- *
- * @hide
- */
-public class WifiChannel implements Parcelable {
-
-    private static final int MIN_FREQ_MHZ = 2412;
-    private static final int MAX_FREQ_MHZ = 5825;
-
-    private static final int MIN_CHANNEL_NUM = 1;
-    private static final int MAX_CHANNEL_NUM = 196;
-
-    /** frequency */
-    public int freqMHz;
-
-    /** channel number */
-    public int channelNum;
-
-    /** is it a DFS channel? */
-    public boolean isDFS;
-
-    /** public constructor */
-    public WifiChannel() { }
-
-    /** check for validity */
-    public boolean isValid() {
-        if (freqMHz < MIN_FREQ_MHZ || freqMHz > MAX_FREQ_MHZ) return false;
-        if (channelNum < MIN_CHANNEL_NUM || channelNum > MAX_CHANNEL_NUM) return false;
-        return true;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** implement Parcelable interface */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(freqMHz);
-        out.writeInt(channelNum);
-        out.writeInt(isDFS ? 1 : 0);
-    }
-
-    /** implement Parcelable interface */
-    public static final Parcelable.Creator<WifiChannel> CREATOR =
-            new Parcelable.Creator<WifiChannel>() {
-        @Override
-        public WifiChannel createFromParcel(Parcel in) {
-            WifiChannel channel = new WifiChannel();
-            channel.freqMHz = in.readInt();
-            channel.channelNum = in.readInt();
-            channel.isDFS = in.readInt() != 0;
-            return channel;
-        }
-
-        @Override
-        public WifiChannel[] newArray(int size) {
-            return new WifiChannel[size];
-        }
-    };
-}
diff --git a/android/net/wifi/WifiConfiguration.java b/android/net/wifi/WifiConfiguration.java
index 8d1a00b..b77b1ad 100644
--- a/android/net/wifi/WifiConfiguration.java
+++ b/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.pm.PackageManager;
 import android.net.IpConfiguration;
@@ -24,11 +25,13 @@
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
+import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.BackupUtils;
+import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
@@ -75,6 +78,8 @@
 
     /** {@hide} */
     private String mPasspointManagementObjectTree;
+    /** {@hide} */
+    private static final int MAXIMUM_RANDOM_MAC_GENERATION_RETRY = 3;
 
     /**
      * Recognized key management schemes.
@@ -85,9 +90,6 @@
         /** WPA is not used; plaintext or static WEP could be used. */
         public static final int NONE = 0;
         /** WPA pre-shared key (requires {@code preSharedKey} to be specified). */
-        /** @deprecated Due to security and performance limitations, use of WPA-1 networks
-         * is discouraged. WPA-2 (RSN) should be used instead. */
-        @Deprecated
         public static final int WPA_PSK = 1;
         /** WPA using EAP authentication. Generally used with an external authentication server. */
         public static final int WPA_EAP = 2;
@@ -121,7 +123,7 @@
 
         public static final String varName = "key_mgmt";
 
-        public static final String[] strings = { "NONE", /* deprecated */ "WPA_PSK", "WPA_EAP",
+        public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP",
                 "IEEE8021X", "WPA2_PSK", "OSEN", "FT_PSK", "FT_EAP" };
     }
 
@@ -439,6 +441,7 @@
     /**
      * @hide
      */
+    @NonNull
     private IpConfiguration mIpConfiguration;
 
     /**
@@ -530,91 +533,6 @@
     /** @hide **/
     public static int INVALID_RSSI = -127;
 
-    /**
-     * @hide
-     * A summary of the RSSI and Band status for that configuration
-     * This is used as a temporary value by the auto-join controller
-     */
-    public static final class Visibility {
-        public int rssi5;   // strongest 5GHz RSSI
-        public int rssi24;  // strongest 2.4GHz RSSI
-        public int num5;    // number of BSSIDs on 5GHz
-        public int num24;   // number of BSSIDs on 2.4GHz
-        public long age5;   // timestamp of the strongest 5GHz BSSID (last time it was seen)
-        public long age24;  // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
-        public String BSSID24;
-        public String BSSID5;
-        public int score; // Debug only, indicate last score used for autojoin/cell-handover
-        public int currentNetworkBoost; // Debug only, indicate boost applied to RSSI if current
-        public int bandPreferenceBoost; // Debug only, indicate boost applied to RSSI if current
-        public int lastChoiceBoost; // Debug only, indicate last choice applied to this configuration
-        public String lastChoiceConfig; // Debug only, indicate last choice applied to this configuration
-
-        public Visibility() {
-            rssi5 = INVALID_RSSI;
-            rssi24 = INVALID_RSSI;
-        }
-
-        public Visibility(Visibility source) {
-            rssi5 = source.rssi5;
-            rssi24 = source.rssi24;
-            age24 = source.age24;
-            age5 = source.age5;
-            num24 = source.num24;
-            num5 = source.num5;
-            BSSID5 = source.BSSID5;
-            BSSID24 = source.BSSID24;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sbuf = new StringBuilder();
-            sbuf.append("[");
-            if (rssi24 > INVALID_RSSI) {
-                sbuf.append(Integer.toString(rssi24));
-                sbuf.append(",");
-                sbuf.append(Integer.toString(num24));
-                if (BSSID24 != null) sbuf.append(",").append(BSSID24);
-            }
-            sbuf.append("; ");
-            if (rssi5 > INVALID_RSSI) {
-                sbuf.append(Integer.toString(rssi5));
-                sbuf.append(",");
-                sbuf.append(Integer.toString(num5));
-                if (BSSID5 != null) sbuf.append(",").append(BSSID5);
-            }
-            if (score != 0) {
-                sbuf.append("; ").append(score);
-                sbuf.append(", ").append(currentNetworkBoost);
-                sbuf.append(", ").append(bandPreferenceBoost);
-                if (lastChoiceConfig != null) {
-                    sbuf.append(", ").append(lastChoiceBoost);
-                    sbuf.append(", ").append(lastChoiceConfig);
-                }
-            }
-            sbuf.append("]");
-            return sbuf.toString();
-        }
-    }
-
-    /** @hide
-     * Cache the visibility status of this configuration.
-     * Visibility can change at any time depending on scan results availability.
-     * Owner of the WifiConfiguration is responsible to set this field based on
-     * recent scan results.
-     ***/
-    public Visibility visibility;
-
-    /** @hide
-     * calculate and set Visibility for that configuration.
-     *
-     * age in milliseconds: we will consider only ScanResults that are more recent,
-     * i.e. younger.
-     ***/
-    public void setVisibility(Visibility status) {
-        visibility = status;
-    }
-
     // States for the userApproved field
     /**
      * @hide
@@ -884,27 +802,37 @@
      * @hide
      * Randomized MAC address to use with this particular network
      */
+    @NonNull
     private MacAddress mRandomizedMacAddress;
 
     /**
      * @hide
      * Checks if the given MAC address can be used for Connected Mac Randomization
-     * by verifying that it is non-null, unicast, and locally assigned.
+     * by verifying that it is non-null, unicast, locally assigned, and not default mac.
      * @param mac MacAddress to check
      * @return true if mac is good to use
      */
-    private boolean isValidMacAddressForRandomization(MacAddress mac) {
-        return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned();
+    public static boolean isValidMacAddressForRandomization(MacAddress mac) {
+        return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned()
+                && !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac);
     }
 
     /**
      * @hide
      * Returns Randomized MAC address to use with the network.
-     * If it is not set/valid, create a new randomized address.
+     * If it is not set/valid, creates a new randomized address.
+     * If it can't generate a valid mac, returns the default MAC.
      */
-    public MacAddress getOrCreateRandomizedMacAddress() {
-        if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
+    public @NonNull MacAddress getOrCreateRandomizedMacAddress() {
+        int randomMacGenerationCount = 0;
+        while (!isValidMacAddressForRandomization(mRandomizedMacAddress)
+                && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) {
             mRandomizedMacAddress = MacAddress.createRandomUnicastAddress();
+            randomMacGenerationCount++;
+        }
+
+        if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
+            mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
         }
         return mRandomizedMacAddress;
     }
@@ -914,7 +842,7 @@
      * Returns MAC address set to be the local randomized MAC address.
      * Does not guarantee that the returned address is valid for use.
      */
-    public MacAddress getRandomizedMacAddress() {
+    public @NonNull MacAddress getRandomizedMacAddress() {
         return mRandomizedMacAddress;
     }
 
@@ -922,7 +850,11 @@
      * @hide
      * @param mac MacAddress to change into
      */
-    public void setRandomizedMacAddress(MacAddress mac) {
+    public void setRandomizedMacAddress(@NonNull MacAddress mac) {
+        if (mac == null) {
+            Log.e(TAG, "setRandomizedMacAddress received null MacAddress.");
+            return;
+        }
         mRandomizedMacAddress = mac;
     }
 
@@ -1615,6 +1547,7 @@
         creatorUid = -1;
         shared = true;
         dtimInterval = 0;
+        mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
     }
 
     /**
@@ -2023,6 +1956,7 @@
 
     /** @hide */
     public void setIpConfiguration(IpConfiguration ipConfiguration) {
+        if (ipConfiguration == null) ipConfiguration = new IpConfiguration();
         mIpConfiguration = ipConfiguration;
     }
 
@@ -2069,11 +2003,15 @@
     }
 
     /**
-     * Set the {@link ProxyInfo} for this WifiConfiguration.
+     * Set the {@link ProxyInfo} for this WifiConfiguration. This method should only be used by a
+     * device owner or profile owner. When other apps attempt to save a {@link WifiConfiguration}
+     * with modified proxy settings, the methods {@link WifiManager#addNetwork} and
+     * {@link WifiManager#updateNetwork} fail and return {@code -1}.
+     *
      * @param httpProxy {@link ProxyInfo} representing the httpProxy to be used by this
-     *                  WifiConfiguration. Setting this {@code null} will explicitly set no proxy,
-     *                  removing any proxy that was previously set.
-     * @exception throw IllegalArgumentException for invalid httpProxy
+     *                  WifiConfiguration. Setting this to {@code null} will explicitly set no
+     *                  proxy, removing any proxy that was previously set.
+     * @exception IllegalArgumentException for invalid httpProxy
      */
     public void setHttpProxy(ProxyInfo httpProxy) {
         if (httpProxy == null) {
@@ -2170,9 +2108,6 @@
             meteredHint = source.meteredHint;
             meteredOverride = source.meteredOverride;
             useExternalScores = source.useExternalScores;
-            if (source.visibility != null) {
-                visibility = new Visibility(source.visibility);
-            }
 
             didSelfAdd = source.didSelfAdd;
             lastConnectUid = source.lastConnectUid;
@@ -2306,7 +2241,7 @@
                 config.allowedGroupCiphers    = readBitSet(in);
 
                 config.enterpriseConfig = in.readParcelable(null);
-                config.mIpConfiguration = in.readParcelable(null);
+                config.setIpConfiguration(in.readParcelable(null));
                 config.dhcpServer = in.readString();
                 config.defaultGwMacAddress = in.readString();
                 config.selfAdded = in.readInt() != 0;
diff --git a/android/net/wifi/WifiManager.java b/android/net/wifi/WifiManager.java
index 05dcb33..433285b 100644
--- a/android/net/wifi/WifiManager.java
+++ b/android/net/wifi/WifiManager.java
@@ -99,35 +99,45 @@
     // Supplicant error codes:
     /**
      * The error code if there was a problem authenticating.
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
     public static final int ERROR_AUTHENTICATING = 1;
 
     /**
      * The reason code if there is no error during authentication.
      * It could also imply that there no authentication in progress,
      * this reason code also serves as a reset value.
+     * @deprecated This is no longer supported.
      * @hide
      */
+    @Deprecated
     public static final int ERROR_AUTH_FAILURE_NONE = 0;
 
     /**
      * The reason code if there was a timeout authenticating.
+     * @deprecated This is no longer supported.
      * @hide
      */
+    @Deprecated
     public static final int ERROR_AUTH_FAILURE_TIMEOUT = 1;
 
     /**
      * The reason code if there was a wrong password while
      * authenticating.
+     * @deprecated This is no longer supported.
      * @hide
      */
+    @Deprecated
     public static final int ERROR_AUTH_FAILURE_WRONG_PSWD = 2;
 
     /**
      * The reason code if there was EAP failure while
      * authenticating.
+     * @deprecated This is no longer supported.
      * @hide
      */
+    @Deprecated
     public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3;
 
     /**
@@ -565,8 +575,10 @@
      * to perform Wi-Fi operations) or the connection to the supplicant has been
      * lost. One extra provides the connection state as a boolean, where {@code true}
      * means CONNECTED.
+     * @deprecated This is no longer supported.
      * @see #EXTRA_SUPPLICANT_CONNECTED
      */
+    @Deprecated
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String SUPPLICANT_CONNECTION_CHANGE_ACTION =
         "android.net.wifi.supplicant.CONNECTION_CHANGE";
@@ -575,7 +587,9 @@
      * the supplicant daemon has been gained or lost. {@code true} means
      * a connection now exists.
      * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
     public static final String EXTRA_SUPPLICANT_CONNECTED = "connected";
     /**
      * Broadcast intent action indicating that the state of Wi-Fi connectivity
@@ -612,7 +626,9 @@
      * the overall state of connectivity.
      * @see #EXTRA_NEW_STATE
      * @see #EXTRA_SUPPLICANT_ERROR
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String SUPPLICANT_STATE_CHANGED_ACTION =
         "android.net.wifi.supplicant.STATE_CHANGE";
@@ -620,7 +636,9 @@
      * The lookup key for a {@link SupplicantState} describing the new state
      * Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
     public static final String EXTRA_NEW_STATE = "newState";
 
     /**
@@ -629,7 +647,9 @@
      * Retrieve with
      * {@link android.content.Intent#getIntExtra(String, int)}.
      * @see #ERROR_AUTHENTICATING
+     * @deprecated This is no longer supported.
      */
+    @Deprecated
     public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
 
     /**
@@ -638,8 +658,10 @@
      * Retrieve with
      * {@link android.content.Intent#getIntExtra(String, int)}.
      * @see #ERROR_AUTH_FAILURE_#REASON_CODE
+     * @deprecated This is no longer supported.
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_SUPPLICANT_ERROR_REASON = "supplicantErrorReason";
 
     /**
@@ -1295,6 +1317,7 @@
         if (pin) {
             NetworkRequest request = new NetworkRequest.Builder()
                     .clearCapabilities()
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                     .build();
             NetworkPinner.pin(mContext, request);
@@ -1612,7 +1635,7 @@
     public boolean startScan(WorkSource workSource) {
         try {
             String packageName = mContext.getOpPackageName();
-            mService.startScan(null, workSource, packageName);
+            mService.startScan(packageName);
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1620,19 +1643,14 @@
     }
 
     /**
-     * Creates a configuration token describing the current network of MIME type
-     * application/vnd.wfa.wsc. Can be used to configure WiFi networks via NFC.
+     * WPS has been deprecated from Client mode operation.
      *
-     * @return hex-string encoded configuration token or null if there is no current network
+     * @return null
      * @hide
      * @deprecated This API is deprecated
      */
     public String getCurrentNetworkWpsNfcConfigurationToken() {
-        try {
-            return mService.getCurrentNetworkWpsNfcConfigurationToken();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return null;
     }
 
     /**
@@ -1693,18 +1711,14 @@
      * existing networks. You should assume the network IDs can be different
      * after calling this method.
      *
-     * @return {@code true} if the operation succeeded
+     * @return {@code false} Will always return true.
      * @deprecated There is no need to call this method -
      * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)}
      * and {@link #removeNetwork(int)} already persist the configurations automatically.
      */
     @Deprecated
     public boolean saveConfiguration() {
-        try {
-            return mService.saveConfiguration(mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return true;
     }
 
     /**
@@ -2851,36 +2865,6 @@
                         ((ActionListener) listener).onSuccess();
                     }
                     break;
-                case WifiManager.START_WPS_SUCCEEDED:
-                    if (listener != null) {
-                        WpsResult result = (WpsResult) message.obj;
-                        ((WpsCallback) listener).onStarted(result.pin);
-                        //Listener needs to stay until completion or failure
-                        synchronized (mListenerMapLock) {
-                            mListenerMap.put(message.arg2, listener);
-                        }
-                    }
-                    break;
-                case WifiManager.WPS_COMPLETED:
-                    if (listener != null) {
-                        ((WpsCallback) listener).onSucceeded();
-                    }
-                    break;
-                case WifiManager.WPS_FAILED:
-                    if (listener != null) {
-                        ((WpsCallback) listener).onFailed(message.arg1);
-                    }
-                    break;
-                case WifiManager.CANCEL_WPS_SUCCEDED:
-                    if (listener != null) {
-                        ((WpsCallback) listener).onSucceeded();
-                    }
-                    break;
-                case WifiManager.CANCEL_WPS_FAILED:
-                    if (listener != null) {
-                        ((WpsCallback) listener).onFailed(message.arg1);
-                    }
-                    break;
                 case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
                     if (listener != null) {
                         RssiPacketCountInfo info = (RssiPacketCountInfo) message.obj;
@@ -3059,29 +3043,32 @@
     }
 
     /**
-     * Start Wi-fi Protected Setup
+     * WPS suport has been deprecated from Client mode and this method will immediately trigger
+     * {@link WpsCallback#onFailed(int)} with a generic error.
      *
      * @param config WPS configuration (does not support {@link WpsInfo#LABEL})
      * @param listener for callbacks on success or failure. Can be null.
-     * @throws IllegalStateException if the WifiManager instance needs to be
-     * initialized again
+     * @throws IllegalStateException if the WifiManager instance needs to be initialized again
      * @deprecated This API is deprecated
      */
     public void startWps(WpsInfo config, WpsCallback listener) {
-        if (config == null) throw new IllegalArgumentException("config cannot be null");
-        getChannel().sendMessage(START_WPS, 0, putListener(listener), config);
+        if (listener != null ) {
+            listener.onFailed(ERROR);
+        }
     }
 
     /**
-     * Cancel any ongoing Wi-fi Protected Setup
+     * WPS support has been deprecated from Client mode and this method will immediately trigger
+     * {@link WpsCallback#onFailed(int)} with a generic error.
      *
      * @param listener for callbacks on success or failure. Can be null.
-     * @throws IllegalStateException if the WifiManager instance needs to be
-     * initialized again
+     * @throws IllegalStateException if the WifiManager instance needs to be initialized again
      * @deprecated This API is deprecated
      */
     public void cancelWps(WpsCallback listener) {
-        getChannel().sendMessage(CANCEL_WPS, 0, putListener(listener));
+        if (listener != null) {
+            listener.onFailed(ERROR);
+        }
     }
 
     /**
@@ -3568,32 +3555,7 @@
     }
 
     /**
-     * Set wifi Aggressive Handover. Called from developer settings.
-     * @hide
-     */
-    public void enableAggressiveHandover(int enabled) {
-        try {
-            mService.enableAggressiveHandover(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the WiFi Handover aggressiveness.This is used by settings
-     * to decide what to show within the picker.
-     * @hide
-     */
-    public int getAggressiveHandover() {
-        try {
-            return mService.getAggressiveHandover();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Resets all wifi manager settings back to factory defaults.
+     * Removes all saved wifi networks.
      *
      * @hide
      */
@@ -3678,8 +3640,10 @@
      * Restore state from the older version of back up data.
      * The old backup data was essentially a backup of wpa_supplicant.conf
      * and ipconfig.txt file.
+     * @deprecated this is no longer supported.
      * @hide
      */
+    @Deprecated
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         try {
             mService.restoreSupplicantBackupData(supplicantData, ipConfigData);
diff --git a/android/net/wifi/WpsInfo.java b/android/net/wifi/WpsInfo.java
index ae2e771..d12cce1 100644
--- a/android/net/wifi/WpsInfo.java
+++ b/android/net/wifi/WpsInfo.java
@@ -21,37 +21,58 @@
 
 /**
  * A class representing Wi-Fi Protected Setup
- *
+ * @deprecated This class is no longer supported.
  * {@see WifiP2pConfig}
  */
+@Deprecated
 public class WpsInfo implements Parcelable {
 
-    /** Push button configuration */
+    /** Push button configuration
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public static final int PBC     = 0;
-    /** Display pin method configuration - pin is generated and displayed on device */
+    /** Display pin method configuration - pin is generated and displayed on device
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public static final int DISPLAY = 1;
-    /** Keypad pin method configuration - pin is entered on device */
+    /** Keypad pin method configuration - pin is entered on device
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public static final int KEYPAD  = 2;
-    /** Label pin method configuration - pin is labelled on device */
+    /** Label pin method configuration - pin is labelled on device
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public static final int LABEL   = 3;
-    /** Invalid configuration */
+    /** Invalid configuration
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public static final int INVALID = 4;
 
-    /** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */
+    /** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public int setup;
 
-    /** Passed with pin method KEYPAD */
+    /** Passed with pin method KEYPAD
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public String BSSID;
 
-    /** Passed with pin method configuration */
+    /** Passed with pin method configuration
+     * @deprecated This is no longer supported.*/
+    @Deprecated
     public String pin;
 
+    /** @deprecated This API is no longer supported.*/
+    @Deprecated
     public WpsInfo() {
         setup = INVALID;
         BSSID = null;
         pin = null;
     }
 
+    /** @deprecated This API is no longer supported.*/
+    @Deprecated
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
         sbuf.append(" setup: ").append(setup);
@@ -63,12 +84,16 @@
         return sbuf.toString();
     }
 
-    /** Implement the Parcelable interface */
+    /** Implement the Parcelable interface
+     * @deprecated This API is no longer supported.*/
+    @Deprecated
     public int describeContents() {
         return 0;
     }
 
-    /* Copy constructor */
+    /* Copy constructor
+    * @deprecated This API is no longer supported.*/
+    @Deprecated
     public WpsInfo(WpsInfo source) {
         if (source != null) {
             setup = source.setup;
@@ -77,16 +102,22 @@
         }
     }
 
-    /** Implement the Parcelable interface */
+    /** Implement the Parcelable interface
+     * @deprecated This API is no longer supported. */
+    @Deprecated
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(setup);
         dest.writeString(BSSID);
         dest.writeString(pin);
     }
 
-    /** Implement the Parcelable interface */
+    /** Implement the Parcelable interface
+     * @deprecated This API is no longer supported.*/
+    @Deprecated
     public static final Creator<WpsInfo> CREATOR =
         new Creator<WpsInfo>() {
+            /** @deprecated This API is nolonger supported.*/
+            @Deprecated
             public WpsInfo createFromParcel(Parcel in) {
                 WpsInfo config = new WpsInfo();
                 config.setup = in.readInt();
@@ -95,6 +126,8 @@
                 return config;
             }
 
+            /** @deprecated This API is nolonger supported.*/
+            @Deprecated
             public WpsInfo[] newArray(int size) {
                 return new WpsInfo[size];
             }
diff --git a/android/net/wifi/aware/DiscoverySession.java b/android/net/wifi/aware/DiscoverySession.java
index 9f73622..699f54c 100644
--- a/android/net/wifi/aware/DiscoverySession.java
+++ b/android/net/wifi/aware/DiscoverySession.java
@@ -22,6 +22,8 @@
 import android.net.NetworkSpecifier;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import dalvik.system.CloseGuard;
 
 import java.lang.ref.WeakReference;
@@ -142,6 +144,34 @@
     }
 
     /**
+     * Access the client ID of the Aware session.
+     *
+     * Note: internal visibility for testing.
+     *
+     * @return The internal client ID.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public int getClientId() {
+        return mClientId;
+    }
+
+    /**
+     * Access the discovery session ID of the Aware session.
+     *
+     * Note: internal visibility for testing.
+     *
+     * @return The internal discovery session ID.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public int getSessionId() {
+        return mSessionId;
+    }
+
+    /**
      * Sends a message to the specified destination. Aware messages are transmitted in the context
      * of a discovery session - executed subsequent to a publish/subscribe
      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
@@ -246,8 +276,7 @@
      *                   or
      *                   {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
      *                   On a RESPONDER this value is used to gate the acceptance of a connection
-     *                   request from only that peer. A RESPONDER may specify a {@code null} -
-     *                   indicating that it will accept connection requests from any device.
+     *                   request from only that peer.
      *
      * @return A {@link NetworkSpecifier} to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
@@ -255,7 +284,7 @@
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
+    public NetworkSpecifier createNetworkSpecifierOpen(@NonNull PeerHandle peerHandle) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
             return null;
@@ -295,8 +324,7 @@
      * byte[], java.util.List)} or
      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
-     *                   from only that peer. A RESPONDER may specify a {@code null} - indicating
-     *                   that it will accept connection requests from any device.
+     *                   from only that peer.
      * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
      *                   the passphrase. Use the
      *                   {@link #createNetworkSpecifierOpen(PeerHandle)} API to
@@ -309,7 +337,7 @@
      * [or other varieties of that API].
      */
     public NetworkSpecifier createNetworkSpecifierPassphrase(
-            @Nullable PeerHandle peerHandle, @NonNull String passphrase) {
+            @NonNull PeerHandle peerHandle, @NonNull String passphrase) {
         if (!WifiAwareUtils.validatePassphrase(passphrase)) {
             throw new IllegalArgumentException("Passphrase must meet length requirements");
         }
@@ -354,8 +382,7 @@
      * byte[], java.util.List)} or
      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
-     *                   from only that peer. A RESPONDER may specify a null - indicating that
-     *                   it will accept connection requests from any device.
+     *                   from only that peer.
      * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
      *            encrypting the data-path. Use the
      *            {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a
@@ -371,7 +398,7 @@
      * @hide
      */
     @SystemApi
-    public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
+    public NetworkSpecifier createNetworkSpecifierPmk(@NonNull PeerHandle peerHandle,
             @NonNull byte[] pmk) {
         if (!WifiAwareUtils.validatePmk(pmk)) {
             throw new IllegalArgumentException("PMK must 32 bytes");
diff --git a/android/net/wifi/aware/DiscoverySessionCallback.java b/android/net/wifi/aware/DiscoverySessionCallback.java
index 2052f15..ebf6007 100644
--- a/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -116,9 +116,12 @@
      * Called when a discovery (publish or subscribe) operation results in a
      * service discovery. Called when a Subscribe service was configured with a range requirement
      * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or
-     * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared
-     * (i.e. this callback called) if the range of the publisher is within the specified distance
-     * constraints.
+     * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} and the Publish service was configured
+     * with {@link PublishConfig.Builder#setRangingEnabled(boolean)}.
+     * <p>
+     * If either Publisher or Subscriber does not enable Ranging, or if Ranging is temporarily
+     * disabled by the underlying device, service discovery proceeds without ranging and the
+     * {@link #onServiceDiscovered(PeerHandle, byte[], List)} is called.
      *
      * @param peerHandle An opaque handle to the peer matching our discovery operation.
      * @param serviceSpecificInfo The service specific information (arbitrary
@@ -130,7 +133,8 @@
      *                    match filter. For {@link PublishConfig#PUBLISH_TYPE_SOLICITED},
      *                    {@link SubscribeConfig#SUBSCRIBE_TYPE_ACTIVE} discovery sessions this
      *                    is the subscriber's match filter.
-     * @param distanceMm The measured distance to the Publisher in mm.
+     * @param distanceMm The measured distance to the Publisher in mm. Note: the measured distance
+     *                   may be negative for very close devices.
      */
     public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
         byte[] serviceSpecificInfo, List<byte[]> matchFilter, int distanceMm) {
diff --git a/android/net/wifi/aware/PeerHandle.java b/android/net/wifi/aware/PeerHandle.java
index b525212..8ae4b5a 100644
--- a/android/net/wifi/aware/PeerHandle.java
+++ b/android/net/wifi/aware/PeerHandle.java
@@ -18,11 +18,20 @@
 
 /**
  * Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in
- * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}, used
- * when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])},
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} or
+ * received messages in {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}, and
+ * used when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])},
  * or when configuring a network link to a peer, e.g.
  * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or
  * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ * <p>
+ * Note that while a {@code PeerHandle} can be used to track a particular peer (i.e. you can compare
+ * the values received from subsequent messages) - it is good practice not to rely on it. Instead
+ * use an application level peer identifier encoded in the message,
+ * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}, and/or in the Publish
+ * configuration's service-specific information field,
+ * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, or match filter,
+ * {@link PublishConfig.Builder#setMatchFilter(java.util.List)}.
  */
 public class PeerHandle {
     /** @hide */
diff --git a/android/net/wifi/aware/PublishConfig.java b/android/net/wifi/aware/PublishConfig.java
index 7a0250b..d43f727 100644
--- a/android/net/wifi/aware/PublishConfig.java
+++ b/android/net/wifi/aware/PublishConfig.java
@@ -365,9 +365,8 @@
          * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or
          * maximum distance at which discovery will be triggered.
          * <p>
-         * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
-         * device will be refused. If the peer has ranging enabled (using the
-         * {@link SubscribeConfig} APIs listed above, it will never discover this device.
+         * Optional. Disabled by default - i.e. any peer attempt to measure distance to this device
+         * will be refused and discovery will proceed without ranging constraints.
          * <p>
          * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
          * as described in {@link android.net.wifi.rtt}.
diff --git a/android/net/wifi/aware/SubscribeConfig.java b/android/net/wifi/aware/SubscribeConfig.java
index 2eab76a..51353c6 100644
--- a/android/net/wifi/aware/SubscribeConfig.java
+++ b/android/net/wifi/aware/SubscribeConfig.java
@@ -415,17 +415,23 @@
 
         /**
          * Configure the minimum distance to a discovered publisher at which to trigger a discovery
-         * notification. I.e. discovery will only be triggered if we've found a matching publisher
+         * notification. I.e. discovery will be triggered if we've found a matching publisher
          * (based on the other criteria in this configuration) <b>and</b> the distance to the
-         * publisher is > the value specified in this API.
+         * publisher is larger than the value specified in this API. Can be used in conjunction with
+         * {@link #setMaxDistanceMm(int)} to specify a geofence, i.e. discovery with min <=
+         * distance <= max.
          * <p>
-         * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence,
-         * i.e. discovery with min < distance < max.
+         * For ranging to be used in discovery it must also be enabled on the publisher using
+         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. However, ranging may
+         * not be available or enabled on the publisher or may be temporarily disabled on either
+         * subscriber or publisher - in such cases discovery will proceed without ranging.
          * <p>
-         * If this API is called, the subscriber requires ranging. In such a case, the publisher
-         * peer must enable ranging using
-         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
-         * never be triggered.
+         * When ranging is enabled and available on both publisher and subscriber and a service
+         * is discovered based on geofence constraints the
+         * {@link DiscoverySessionCallback#onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)}
+         * is called, otherwise the
+         * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}
+         * is called.
          * <p>
          * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
          * as described in {@link android.net.wifi.rtt}.
@@ -444,17 +450,23 @@
 
         /**
          * Configure the maximum distance to a discovered publisher at which to trigger a discovery
-         * notification. I.e. discovery will only be triggered if we've found a matching publisher
+         * notification. I.e. discovery will be triggered if we've found a matching publisher
          * (based on the other criteria in this configuration) <b>and</b> the distance to the
-         * publisher is < the value specified in this API.
+         * publisher is smaller than the value specified in this API. Can be used in conjunction
+         * with {@link #setMinDistanceMm(int)} to specify a geofence, i.e. discovery with min <=
+         * distance <= max.
          * <p>
-         * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence,
-         * i.e. discovery with min < distance < max.
+         * For ranging to be used in discovery it must also be enabled on the publisher using
+         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. However, ranging may
+         * not be available or enabled on the publisher or may be temporarily disabled on either
+         * subscriber or publisher - in such cases discovery will proceed without ranging.
          * <p>
-         * If this API is called, the subscriber requires ranging. In such a case, the publisher
-         * peer must enable ranging using
-         * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
-         * never be triggered.
+         * When ranging is enabled and available on both publisher and subscriber and a service
+         * is discovered based on geofence constraints the
+         * {@link DiscoverySessionCallback#onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)}
+         * is called, otherwise the
+         * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}
+         * is called.
          * <p>
          * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
          * as described in {@link android.net.wifi.rtt}.
diff --git a/android/net/wifi/aware/WifiAwareManager.java b/android/net/wifi/aware/WifiAwareManager.java
index 2f0c316..8529a89 100644
--- a/android/net/wifi/aware/WifiAwareManager.java
+++ b/android/net/wifi/aware/WifiAwareManager.java
@@ -27,6 +27,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -406,7 +407,7 @@
 
     /** @hide */
     public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
-            PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
+            @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
                     + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
@@ -420,11 +421,11 @@
                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
                             + "specifier");
         }
-        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
+        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
+                Build.VERSION_CODES.P)) {
             if (peerHandle == null) {
                 throw new IllegalArgumentException(
-                        "createNetworkSpecifier: Invalid peer handle (value of null) - not "
-                                + "permitted on INITIATOR");
+                        "createNetworkSpecifier: Invalid peer handle - cannot be null");
             }
         }
 
@@ -443,7 +444,7 @@
 
     /** @hide */
     public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role,
-            @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
+            @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role
                     + ", pmk=" + ((pmk == null) ? "null" : "non-null")
@@ -456,10 +457,11 @@
                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
                             + "specifier");
         }
-        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
+        if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
+                Build.VERSION_CODES.P)) {
             if (peer == null) {
-                throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC "
-                        + "address - null not permitted on INITIATOR");
+                throw new IllegalArgumentException(
+                        "createNetworkSpecifier: Invalid peer MAC - cannot be null");
             }
         }
         if (peer != null && peer.length != 6) {
diff --git a/android/net/wifi/aware/WifiAwareSession.java b/android/net/wifi/aware/WifiAwareSession.java
index f26b9f5..3219653 100644
--- a/android/net/wifi/aware/WifiAwareSession.java
+++ b/android/net/wifi/aware/WifiAwareSession.java
@@ -25,6 +25,8 @@
 import android.os.Looper;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import dalvik.system.CloseGuard;
 
 import java.lang.ref.WeakReference;
@@ -97,6 +99,20 @@
     }
 
     /**
+     * Access the client ID of the Aware session.
+     *
+     * Note: internal visibility for testing.
+     *
+     * @return The internal client ID.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public int getClientId() {
+        return mClientId;
+    }
+
+    /**
      * Issue a request to the Aware service to create a new Aware publish discovery session, using
      * the specified {@code publishConfig} configuration. The results of the publish operation
      * are routed to the callbacks of {@link DiscoverySessionCallback}:
@@ -207,8 +223,7 @@
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specify a {@code null} - indicating that it will accept
-     *              connection requests from any device.
+     *              peer.
      *
      * @return A {@link NetworkSpecifier} to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
@@ -217,7 +232,7 @@
      * [or other varieties of that API].
      */
     public NetworkSpecifier createNetworkSpecifierOpen(
-            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer) {
+            @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
@@ -246,8 +261,7 @@
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specify a {@code null} - indicating that it will accept
-     *              connection requests from any device.
+     *              peer.
      * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
      *                   the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to
      *                   specify an open (unencrypted) link.
@@ -259,7 +273,7 @@
      * [or other varieties of that API].
      */
     public NetworkSpecifier createNetworkSpecifierPassphrase(
-            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer,
+            @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer,
             @NonNull String passphrase) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
@@ -293,8 +307,7 @@
      *              {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
      * @param peer  The MAC address of the peer's Aware discovery interface. On a RESPONDER this
      *              value is used to gate the acceptance of a connection request from only that
-     *              peer. A RESPONDER may specify a null - indicating that it will accept
-     *              connection requests from any device.
+     *              peer.
      * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
      *            encrypting the data-path. Use the
      *            {@link #createNetworkSpecifierPassphrase(int, byte[], String)} to specify a
@@ -311,7 +324,7 @@
      */
     @SystemApi
     public NetworkSpecifier createNetworkSpecifierPmk(
-            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, @NonNull byte[] pmk) {
+            @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer, @NonNull byte[] pmk) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
diff --git a/android/net/wifi/aware/WifiAwareUtils.java b/android/net/wifi/aware/WifiAwareUtils.java
index fda7a9a..3ece93d 100644
--- a/android/net/wifi/aware/WifiAwareUtils.java
+++ b/android/net/wifi/aware/WifiAwareUtils.java
@@ -16,6 +16,8 @@
 
 package android.net.wifi.aware;
 
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.wifi.V1_0.Constants;
 
 /**
@@ -84,4 +86,21 @@
 
         return true;
     }
+
+    /**
+     * Returns true if the App version is older than minVersion.
+     */
+    public static boolean isLegacyVersion(Context context, int minVersion) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(context.getOpPackageName(), 0)
+                    .targetSdkVersion < minVersion) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify valididity before checking App's version.
+        }
+        return false;
+    }
 }
diff --git a/android/net/wifi/rtt/LocationCivic.java b/android/net/wifi/rtt/LocationCivic.java
deleted file mode 100644
index 610edb6..0000000
--- a/android/net/wifi/rtt/LocationCivic.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.rtt;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * Location Civic Report (LCR).
- * <p>
- * The information matches the IEEE 802.11-2016 LCR report.
- * <p>
- * Note: depending on the mechanism by which this information is returned (i.e. the API which
- * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
- * the information is NOT validated - use with caution. Consider validating it with other sources
- * of information before using it.
- */
-public final class LocationCivic implements Parcelable {
-    private final byte[] mData;
-
-    /**
-     * Parse the raw LCR information element (byte array) and extract the LocationCivic structure.
-     *
-     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
-     *
-     * @hide
-     */
-    @Nullable
-    public static LocationCivic parseInformationElement(byte id, byte[] data) {
-        // TODO
-        return null;
-    }
-
-    /** @hide */
-    public LocationCivic(byte[] data) {
-        mData = data;
-    }
-
-    /**
-     * Return the Location Civic data reported by the peer.
-     *
-     * @return An arbitrary location information.
-     */
-    public byte[] getData() {
-        return mData;
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByteArray(mData);
-    }
-
-    public static final Parcelable.Creator<LocationCivic> CREATOR =
-            new Parcelable.Creator<LocationCivic>() {
-                @Override
-                public LocationCivic[] newArray(int size) {
-                    return new LocationCivic[size];
-                }
-
-                @Override
-                public LocationCivic createFromParcel(Parcel in) {
-                    byte[] data = in.createByteArray();
-
-                    return new LocationCivic(data);
-                }
-            };
-
-    /** @hide */
-    @Override
-    public String toString() {
-        return new StringBuilder("LCR: data=").append(Arrays.toString(mData)).toString();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof LocationCivic)) {
-            return false;
-        }
-
-        LocationCivic lhs = (LocationCivic) o;
-
-        return Arrays.equals(mData, lhs.mData);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mData);
-    }
-}
diff --git a/android/net/wifi/rtt/LocationConfigurationInformation.java b/android/net/wifi/rtt/LocationConfigurationInformation.java
deleted file mode 100644
index 8aba56a..0000000
--- a/android/net/wifi/rtt/LocationConfigurationInformation.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.rtt;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * The Device Location Configuration Information (LCI) specifies the location information of a peer
- * device (e.g. an Access Point).
- * <p>
- * The information matches the IEEE 802.11-2016 LCI report (Location configuration information
- * report).
- * <p>
- * Note: depending on the mechanism by which this information is returned (i.e. the API which
- * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
- * the information is NOT validated - use with caution. Consider validating it with other sources
- * of information before using it.
- */
-public final class LocationConfigurationInformation implements Parcelable {
-    /** @hide */
-    @IntDef({
-            ALTITUDE_UNKNOWN, ALTITUDE_IN_METERS, ALTITUDE_IN_FLOORS })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AltitudeTypes {
-    }
-
-    /**
-     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
-     * does not specify an altitude or altitude uncertainty. The corresponding methods,
-     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} are not valid and will throw
-     * an exception.
-     */
-    public static final int ALTITUDE_UNKNOWN = 0;
-
-    /**
-     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
-     * specifies the altitude and altitude uncertainty in meters. The corresponding methods,
-     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} return a valid value in meters.
-     */
-    public static final int ALTITUDE_IN_METERS = 1;
-
-    /**
-     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the
-     * location specifies the altitude in floors, and does not specify an altitude uncertainty.
-     * The {@link #getAltitude()} method returns valid value in floors, and the
-     * {@link #getAltitudeUncertainty()} method is not valid and will throw an exception.
-     */
-    public static final int ALTITUDE_IN_FLOORS = 2;
-
-    private final double mLatitude;
-    private final double mLatitudeUncertainty;
-    private final double mLongitude;
-    private final double mLongitudeUncertainty;
-    private final int mAltitudeType;
-    private final double mAltitude;
-    private final double mAltitudeUncertainty;
-
-    /**
-     * Parse the raw LCI information element (byte array) and extract the
-     * LocationConfigurationInformation structure.
-     *
-     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
-     *
-     * @hide
-     */
-    @Nullable
-    public static LocationConfigurationInformation parseInformationElement(byte id, byte[] data) {
-        // TODO
-        return null;
-    }
-
-    /** @hide */
-    public LocationConfigurationInformation(double latitude, double latitudeUncertainty,
-            double longitude, double longitudeUncertainty, @AltitudeTypes int altitudeType,
-            double altitude, double altitudeUncertainty) {
-        mLatitude = latitude;
-        mLatitudeUncertainty = latitudeUncertainty;
-        mLongitude = longitude;
-        mLongitudeUncertainty = longitudeUncertainty;
-        mAltitudeType = altitudeType;
-        mAltitude = altitude;
-        mAltitudeUncertainty = altitudeUncertainty;
-    }
-
-    /**
-     * Get latitude in degrees. Values are per WGS 84 reference system. Valid values are between
-     * -90 and 90.
-     *
-     * @return Latitude in degrees.
-     */
-    public double getLatitude() {
-        return mLatitude;
-    }
-
-    /**
-     * Get the uncertainty of the latitude {@link #getLatitude()} in degrees. A value of 0 indicates
-     * an unknown uncertainty.
-     *
-     * @return Uncertainty of the latitude in degrees.
-     */
-    public double getLatitudeUncertainty() {
-        return mLatitudeUncertainty;
-    }
-
-    /**
-     * Get longitude in degrees. Values are per WGS 84 reference system. Valid values are between
-     * -180 and 180.
-     *
-     * @return Longitude in degrees.
-     */
-    public double getLongitude() {
-        return mLongitude;
-    }
-
-    /**
-     * Get the uncertainty of the longitude {@link #getLongitude()} ()} in degrees.  A value of 0
-     * indicates an unknown uncertainty.
-     *
-     * @return Uncertainty of the longitude in degrees.
-     */
-    public double getLongitudeUncertainty() {
-        return mLongitudeUncertainty;
-    }
-
-    /**
-     * Specifies the type of the altitude measurement returned by {@link #getAltitude()} and
-     * {@link #getAltitudeUncertainty()}. The possible values are:
-     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude and altitude uncertainty are not provided.
-     * <li>{@link #ALTITUDE_IN_METERS}: The altitude and altitude uncertainty are provided in
-     * meters. Values are per WGS 84 reference system.
-     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors, the altitude uncertainty
-     * is not provided.
-     *
-     * @return The type of the altitude and altitude uncertainty.
-     */
-    public @AltitudeTypes int getAltitudeType() {
-        return mAltitudeType;
-    }
-
-    /**
-     * The altitude is interpreted according to the {@link #getAltitudeType()}. The possible values
-     * are:
-     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude is not provided - this method will throw an
-     * exception.
-     * <li>{@link #ALTITUDE_IN_METERS}: The altitude is provided in meters. Values are per WGS 84
-     * reference system.
-     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors.
-     *
-     * @return Altitude value whose meaning is specified by {@link #getAltitudeType()}.
-     */
-    public double getAltitude() {
-        if (mAltitudeType == ALTITUDE_UNKNOWN) {
-            throw new IllegalStateException(
-                    "getAltitude(): invoked on an invalid type: getAltitudeType()==UNKNOWN");
-        }
-        return mAltitude;
-    }
-
-    /**
-     * Only valid if the the {@link #getAltitudeType()} is equal to {@link #ALTITUDE_IN_METERS} -
-     * otherwise this method will throw an exception.
-     * <p>
-     * Get the uncertainty of the altitude {@link #getAltitude()} in meters.  A value of 0
-     * indicates an unknown uncertainty.
-     *
-     * @return Uncertainty of the altitude in meters.
-     */
-    public double getAltitudeUncertainty() {
-        if (mAltitudeType != ALTITUDE_IN_METERS) {
-            throw new IllegalStateException(
-                    "getAltitude(): invoked on an invalid type: getAltitudeType()!=IN_METERS");
-        }
-        return mAltitudeUncertainty;
-    }
-
-    /** @hide */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** @hide */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeDouble(mLatitude);
-        dest.writeDouble(mLatitudeUncertainty);
-        dest.writeDouble(mLongitude);
-        dest.writeDouble(mLongitudeUncertainty);
-        dest.writeInt(mAltitudeType);
-        dest.writeDouble(mAltitude);
-        dest.writeDouble(mAltitudeUncertainty);
-    }
-
-    public static final Creator<LocationConfigurationInformation> CREATOR =
-            new Creator<LocationConfigurationInformation>() {
-        @Override
-        public LocationConfigurationInformation[] newArray(int size) {
-            return new LocationConfigurationInformation[size];
-        }
-
-        @Override
-        public LocationConfigurationInformation createFromParcel(Parcel in) {
-            double latitude = in.readDouble();
-            double latitudeUnc = in.readDouble();
-            double longitude = in.readDouble();
-            double longitudeUnc = in.readDouble();
-            int altitudeType = in.readInt();
-            double altitude = in.readDouble();
-            double altitudeUnc = in.readDouble();
-
-            return new LocationConfigurationInformation(latitude, latitudeUnc, longitude,
-                    longitudeUnc, altitudeType, altitude, altitudeUnc);
-        }
-    };
-
-    /** @hide */
-    @Override
-    public String toString() {
-        return new StringBuilder("LCI: latitude=").append(mLatitude).append(
-                ", latitudeUncertainty=").append(mLatitudeUncertainty).append(
-                ", longitude=").append(mLongitude).append(", longitudeUncertainty=").append(
-                mLongitudeUncertainty).append(", altitudeType=").append(mAltitudeType).append(
-                ", altitude=").append(mAltitude).append(", altitudeUncertainty=").append(
-                mAltitudeUncertainty).toString();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (!(o instanceof LocationConfigurationInformation)) {
-            return false;
-        }
-
-        LocationConfigurationInformation lhs = (LocationConfigurationInformation) o;
-
-        return mLatitude == lhs.mLatitude && mLatitudeUncertainty == lhs.mLatitudeUncertainty
-                && mLongitude == lhs.mLongitude
-                && mLongitudeUncertainty == lhs.mLongitudeUncertainty
-                && mAltitudeType == lhs.mAltitudeType && mAltitude == lhs.mAltitude
-                && mAltitudeUncertainty == lhs.mAltitudeUncertainty;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mLatitude, mLatitudeUncertainty, mLongitude, mLongitudeUncertainty,
-                mAltitudeType, mAltitude, mAltitudeUncertainty);
-    }
-}
diff --git a/android/net/wifi/rtt/RangingRequest.java b/android/net/wifi/rtt/RangingRequest.java
index 32f21b9..339233b 100644
--- a/android/net/wifi/rtt/RangingRequest.java
+++ b/android/net/wifi/rtt/RangingRequest.java
@@ -37,7 +37,7 @@
  * Defines the ranging request to other devices. The ranging request is built using
  * {@link RangingRequest.Builder}.
  * A ranging request is executed using
- * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}.
+ * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}.
  * <p>
  * The ranging request is a batch request - specifying a set of devices (specified using
  * {@link RangingRequest.Builder#addAccessPoint(ScanResult)} and
@@ -122,6 +122,11 @@
          * Add the device specified by the {@link ScanResult} to the list of devices with
          * which to measure range. The total number of peers added to a request cannot exceed the
          * limit specified by {@link #getMaxPeers()}.
+         * <p>
+         * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
+         * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
+         * not supported the result status will be
+         * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
          *
          * @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
          * @return The builder to facilitate chaining
@@ -138,6 +143,11 @@
          * Add the devices specified by the {@link ScanResult}s to the list of devices with
          * which to measure range. The total number of peers added to a request cannot exceed the
          * limit specified by {@link #getMaxPeers()}.
+         * <p>
+         * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
+         * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
+         * not supported the result status will be
+         * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
          *
          * @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
          * @return The builder to facilitate chaining
@@ -156,14 +166,18 @@
         /**
          * Add the device specified by the {@code peerMacAddress} to the list of devices with
          * which to measure range.
-         *
+         * <p>
          * The MAC address may be obtained out-of-band from a peer Wi-Fi Aware device. A Wi-Fi
          * Aware device may obtain its MAC address using the {@link IdentityChangedListener}
          * provided to
          * {@link WifiAwareManager#attach(AttachCallback, IdentityChangedListener, Handler)}.
-         *
-         * * Note: in order to use this API the device must support Wi-Fi Aware
-         * {@link android.net.wifi.aware}.
+         * <p>
+         * Note: in order to use this API the device must support Wi-Fi Aware
+         * {@link android.net.wifi.aware}. The peer device which is being ranged to must be
+         * configured to publish a service (with any name) with:
+         * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}.
+         * <li>Ranging enabled
+         * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}.
          *
          * @param peerMacAddress The MAC address of the Wi-Fi Aware peer.
          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
@@ -179,12 +193,16 @@
         /**
          * Add a device specified by a {@link PeerHandle} to the list of devices with which to
          * measure range.
-         *
+         * <p>
          * The {@link PeerHandle} may be obtained as part of the Wi-Fi Aware discovery process. E.g.
          * using {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}.
-         *
+         * <p>
          * Note: in order to use this API the device must support Wi-Fi Aware
-         * {@link android.net.wifi.aware}.
+         * {@link android.net.wifi.aware}. The peer device which is being ranged to must be
+         * configured to publish a service (with any name) with:
+         * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}.
+         * <li>Ranging enabled
+         * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}.
          *
          * @param peerHandle The peer handler of the peer Wi-Fi Aware device.
          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
diff --git a/android/net/wifi/rtt/RangingResult.java b/android/net/wifi/rtt/RangingResult.java
index 201833b..758a8d5 100644
--- a/android/net/wifi/rtt/RangingResult.java
+++ b/android/net/wifi/rtt/RangingResult.java
@@ -19,30 +19,32 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.net.MacAddress;
 import android.net.wifi.aware.PeerHandle;
-import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
 /**
  * Ranging result for a request started by
- * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. Results are
- * returned in {@link RangingResultCallback#onRangingResults(List)}.
+ * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}.
+ * Results are returned in {@link RangingResultCallback#onRangingResults(List)}.
  * <p>
  * A ranging result is the distance measurement result for a single device specified in the
  * {@link RangingRequest}.
  */
 public final class RangingResult implements Parcelable {
     private static final String TAG = "RangingResult";
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
     /** @hide */
-    @IntDef({STATUS_SUCCESS, STATUS_FAIL})
+    @IntDef({STATUS_SUCCESS, STATUS_FAIL, STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC})
     @Retention(RetentionPolicy.SOURCE)
     public @interface RangeResultStatus {
     }
@@ -59,43 +61,60 @@
      */
     public static final int STATUS_FAIL = 1;
 
+    /**
+     * Individual range request status, {@link #getStatus()}. Indicates that the ranging operation
+     * failed because the specified peer does not support IEEE 802.11mc RTT operations. Support by
+     * an Access Point can be confirmed using
+     * {@link android.net.wifi.ScanResult#is80211mcResponder()}.
+     * <p>
+     * On such a failure, the individual result fields of {@link RangingResult} such as
+     * {@link RangingResult#getDistanceMm()} are invalid.
+     */
+    public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2;
+
     private final int mStatus;
     private final MacAddress mMac;
     private final PeerHandle mPeerHandle;
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
     private final int mRssi;
-    private final LocationConfigurationInformation mLci;
-    private final LocationCivic mLcr;
+    private final int mNumAttemptedMeasurements;
+    private final int mNumSuccessfulMeasurements;
+    private final byte[] mLci;
+    private final byte[] mLcr;
     private final long mTimestamp;
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
-            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
-            long timestamp) {
+            int distanceStdDevMm, int rssi, int numAttemptedMeasurements,
+            int numSuccessfulMeasurements, byte[] lci, byte[] lcr, long timestamp) {
         mStatus = status;
         mMac = mac;
         mPeerHandle = null;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
-        mLci = lci;
-        mLcr = lcr;
+        mNumAttemptedMeasurements = numAttemptedMeasurements;
+        mNumSuccessfulMeasurements = numSuccessfulMeasurements;
+        mLci = lci == null ? EMPTY_BYTE_ARRAY : lci;
+        mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr;
         mTimestamp = timestamp;
     }
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
-            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
-            long timestamp) {
+            int distanceStdDevMm, int rssi, int numAttemptedMeasurements,
+            int numSuccessfulMeasurements, byte[] lci, byte[] lcr, long timestamp) {
         mStatus = status;
         mMac = null;
         mPeerHandle = peerHandle;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
-        mLci = lci;
-        mLcr = lcr;
+        mNumAttemptedMeasurements = numAttemptedMeasurements;
+        mNumSuccessfulMeasurements = numSuccessfulMeasurements;
+        mLci = lci == null ? EMPTY_BYTE_ARRAY : lci;
+        mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr;
         mTimestamp = timestamp;
     }
 
@@ -135,6 +154,8 @@
      * @return The distance (in mm) to the device specified by {@link #getMacAddress()} or
      * {@link #getPeerHandle()}.
      * <p>
+     * Note: the measured distance may be negative for very close devices.
+     * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
      * exception.
      */
@@ -149,7 +170,9 @@
     /**
      * @return The standard deviation of the measured distance (in mm) to the device specified by
      * {@link #getMacAddress()} or {@link #getPeerHandle()}. The standard deviation is calculated
-     * over the measurements executed in a single RTT burst.
+     * over the measurements executed in a single RTT burst. The number of measurements is returned
+     * by {@link #getNumSuccessfulMeasurements()} - 0 successful measurements indicate that the
+     * standard deviation is not valid (a valid standard deviation requires at least 2 data points).
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
      * exception.
@@ -163,7 +186,7 @@
     }
 
     /**
-     * @return The average RSSI (in units of -0.5dB) observed during the RTT measurement.
+     * @return The average RSSI, in units of dBm, observed during the RTT measurement.
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
      * exception.
@@ -177,29 +200,76 @@
     }
 
     /**
-     * @return The Location Configuration Information (LCI) as self-reported by the peer.
+     * @return The number of attempted measurements used in the RTT exchange resulting in this set
+     * of results. The number of successful measurements is returned by
+     * {@link #getNumSuccessfulMeasurements()} which at most, if there are no errors, will be 1 less
+     * that the number of attempted measurements.
+     * <p>
+     * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
+     * exception.
+     */
+    public int getNumAttemptedMeasurements() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getNumAttemptedMeasurements(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mNumAttemptedMeasurements;
+    }
+
+    /**
+     * @return The number of successful measurements used to calculate the distance and standard
+     * deviation. If the number of successful measurements if 1 then then standard deviation,
+     * returned by {@link #getDistanceStdDevMm()}, is not valid (a 0 is returned for the standard
+     * deviation).
+     * <p>
+     * The total number of measurement attempts is returned by
+     * {@link #getNumAttemptedMeasurements()}. The number of successful measurements will be at
+     * most 1 less then the number of attempted measurements.
+     * <p>
+     * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
+     * exception.
+     */
+    public int getNumSuccessfulMeasurements() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getNumSuccessfulMeasurements(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mNumSuccessfulMeasurements;
+    }
+
+    /**
+     * @return The Location Configuration Information (LCI) as self-reported by the peer. The format
+     * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.10.
      * <p>
      * Note: the information is NOT validated - use with caution. Consider validating it with
      * other sources of information before using it.
+     *
+     * @hide
      */
-    @Nullable
-    public LocationConfigurationInformation getReportedLocationConfigurationInformation() {
+    @SystemApi
+    @NonNull
+    public byte[] getLci() {
         if (mStatus != STATUS_SUCCESS) {
             throw new IllegalStateException(
-                    "getReportedLocationConfigurationInformation(): invoked on an invalid result: "
-                            + "getStatus()=" + mStatus);
+                    "getLci(): invoked on an invalid result: getStatus()=" + mStatus);
         }
         return mLci;
     }
 
     /**
-     * @return The Location Civic report (LCR) as self-reported by the peer.
+     * @return The Location Civic report (LCR) as self-reported by the peer. The format
+     * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.13.
      * <p>
      * Note: the information is NOT validated - use with caution. Consider validating it with
      * other sources of information before using it.
+     *
+     * @hide
      */
-    @Nullable
-    public LocationCivic getReportedLocationCivic() {
+    @SystemApi
+    @NonNull
+    public byte[] getLcr() {
         if (mStatus != STATUS_SUCCESS) {
             throw new IllegalStateException(
                     "getReportedLocationCivic(): invoked on an invalid result: getStatus()="
@@ -209,15 +279,18 @@
     }
 
     /**
-     * @return The timestamp, in us since boot, at which the ranging operation was performed.
+     * @return The timestamp at which the ranging operation was performed. The timestamp is in
+     * milliseconds since boot, including time spent in sleep, corresponding to values provided by
+     * {@link android.os.SystemClock#elapsedRealtime()}.
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
      * exception.
      */
-    public long getRangingTimestampUs() {
+    public long getRangingTimestampMillis() {
         if (mStatus != STATUS_SUCCESS) {
             throw new IllegalStateException(
-                    "getRangingTimestamp(): invoked on an invalid result: getStatus()=" + mStatus);
+                    "getRangingTimestampMillis(): invoked on an invalid result: getStatus()="
+                            + mStatus);
         }
         return mTimestamp;
     }
@@ -245,18 +318,10 @@
         dest.writeInt(mDistanceMm);
         dest.writeInt(mDistanceStdDevMm);
         dest.writeInt(mRssi);
-        if (mLci == null) {
-            dest.writeBoolean(false);
-        } else {
-            dest.writeBoolean(true);
-            mLci.writeToParcel(dest, flags);
-        }
-        if (mLcr == null) {
-            dest.writeBoolean(false);
-        } else {
-            dest.writeBoolean(true);
-            mLcr.writeToParcel(dest, flags);
-        }
+        dest.writeInt(mNumAttemptedMeasurements);
+        dest.writeInt(mNumSuccessfulMeasurements);
+        dest.writeByteArray(mLci);
+        dest.writeByteArray(mLcr);
         dest.writeLong(mTimestamp);
     }
 
@@ -282,23 +347,17 @@
             int distanceMm = in.readInt();
             int distanceStdDevMm = in.readInt();
             int rssi = in.readInt();
-            boolean lciPresent = in.readBoolean();
-            LocationConfigurationInformation lci = null;
-            if (lciPresent) {
-                lci = LocationConfigurationInformation.CREATOR.createFromParcel(in);
-            }
-            boolean lcrPresent = in.readBoolean();
-            LocationCivic lcr = null;
-            if (lcrPresent) {
-                lcr = LocationCivic.CREATOR.createFromParcel(in);
-            }
+            int numAttemptedMeasurements = in.readInt();
+            int numSuccessfulMeasurements = in.readInt();
+            byte[] lci = in.createByteArray();
+            byte[] lcr = in.createByteArray();
             long timestamp = in.readLong();
             if (peerHandlePresent) {
                 return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
-                        lci, lcr, timestamp);
+                        numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, timestamp);
             } else {
                 return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
-                        lci, lcr, timestamp);
+                        numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, timestamp);
             }
         }
     };
@@ -310,7 +369,9 @@
                 mMac).append(", peerHandle=").append(
                 mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
                 mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
-                ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append(
+                ", rssi=").append(mRssi).append(", numAttemptedMeasurements=").append(
+                mNumAttemptedMeasurements).append(", numSuccessfulMeasurements=").append(
+                mNumSuccessfulMeasurements).append(", lci=").append(mLci).append(", lcr=").append(
                 mLcr).append(", timestamp=").append(mTimestamp).append("]").toString();
     }
 
@@ -329,13 +390,15 @@
         return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
-                && Objects.equals(mLci, lhs.mLci) && Objects.equals(mLcr, lhs.mLcr)
+                && mNumAttemptedMeasurements == lhs.mNumAttemptedMeasurements
+                && mNumSuccessfulMeasurements == lhs.mNumSuccessfulMeasurements
+                && Arrays.equals(mLci, lhs.mLci) && Arrays.equals(mLcr, lhs.mLcr)
                 && mTimestamp == lhs.mTimestamp;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
-                mLci, mLcr, mTimestamp);
+                mNumAttemptedMeasurements, mNumSuccessfulMeasurements, mLci, mLcr, mTimestamp);
     }
 }
diff --git a/android/net/wifi/rtt/RangingResultCallback.java b/android/net/wifi/rtt/RangingResultCallback.java
index 9639dc8..fa7d79e 100644
--- a/android/net/wifi/rtt/RangingResultCallback.java
+++ b/android/net/wifi/rtt/RangingResultCallback.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.os.Handler;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -26,11 +25,11 @@
 
 /**
  * Base class for ranging result callbacks. Should be extended by applications and set when calling
- * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. If the
- * ranging operation fails in whole (not attempted) then {@link #onRangingFailure(int)} will be
- * called with a failure code. If the ranging operation is performed for each of the requested
- * peers then the {@link #onRangingResults(List)} will be called with the set of results (@link
- * {@link RangingResult}, each of which has its own success/failure code
+ * {@link WifiRttManager#startRanging(RangingRequest, java.util.concurrent.Executor, RangingResultCallback)}.
+ * If the ranging operation fails in whole (not attempted) then {@link #onRangingFailure(int)}
+ * will be called with a failure code. If the ranging operation is performed for each of the
+ * requested peers then the {@link #onRangingResults(List)} will be called with the set of
+ * results (@link {@link RangingResult}, each of which has its own success/failure code
  * {@link RangingResult#getStatus()}.
  */
 public abstract class RangingResultCallback {
diff --git a/android/net/wifi/rtt/WifiRttManager.java b/android/net/wifi/rtt/WifiRttManager.java
index ec6c46e..457e904 100644
--- a/android/net/wifi/rtt/WifiRttManager.java
+++ b/android/net/wifi/rtt/WifiRttManager.java
@@ -16,11 +16,12 @@
 
 package android.net.wifi.rtt;
 
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.Manifest.permission.ACCESS_WIFI_STATE;
 import static android.Manifest.permission.CHANGE_WIFI_STATE;
 import static android.Manifest.permission.LOCATION_HARDWARE;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -29,13 +30,12 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.Binder;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.util.Log;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * This class provides the primary API for measuring distance (range) to other devices using the
@@ -46,7 +46,7 @@
  * <li>Wi-Fi Aware peers
  * <p>
  * Ranging requests are triggered using
- * {@link #startRanging(RangingRequest, RangingResultCallback, Handler)}. Results (in case of
+ * {@link #startRanging(RangingRequest, Executor, RangingResultCallback)}. Results (in case of
  * successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)}
  * callback.
  * <p>
@@ -106,15 +106,13 @@
      *
      * @param request  A request specifying a set of devices whose distance measurements are
      *                 requested.
+     * @param executor The Executor on which to run the callback.
      * @param callback A callback for the result of the ranging request.
-     * @param handler  The Handler on whose thread to execute the callbacks of the {@code
-     *                 callback} object. If a null is provided then the application's main thread
-     *                 will be used.
      */
-    @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
+    @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
     public void startRanging(@NonNull RangingRequest request,
-            @NonNull RangingResultCallback callback, @Nullable Handler handler) {
-        startRanging(null, request, callback, handler);
+            @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback) {
+        startRanging(null, request, executor, callback);
     }
 
     /**
@@ -124,32 +122,45 @@
      * @param workSource A mechanism to specify an alternative work-source for the request.
      * @param request  A request specifying a set of devices whose distance measurements are
      *                 requested.
+     * @param executor The Executor on which to run the callback.
      * @param callback A callback for the result of the ranging request.
-     * @param handler  The Handler on whose thread to execute the callbacks of the {@code
-     *                 callback} object. If a null is provided then the application's main thread
-     *                 will be used.
      *
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE,
+    @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION, CHANGE_WIFI_STATE,
             ACCESS_WIFI_STATE})
     public void startRanging(@Nullable WorkSource workSource, @NonNull RangingRequest request,
-            @NonNull RangingResultCallback callback, @Nullable Handler handler) {
+            @NonNull @CallbackExecutor Executor executor, @NonNull RangingResultCallback callback) {
         if (VDBG) {
             Log.v(TAG, "startRanging: workSource=" + workSource + ", request=" + request
-                    + ", callback=" + callback + ", handler=" + handler);
+                    + ", callback=" + callback + ", executor=" + executor);
         }
 
+        if (executor == null) {
+            throw new IllegalArgumentException("Null executor provided");
+        }
         if (callback == null) {
             throw new IllegalArgumentException("Null callback provided");
         }
 
-        Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
             mService.startRanging(binder, mContext.getOpPackageName(), workSource, request,
-                    new RttCallbackProxy(looper, callback));
+                    new IRttCallback.Stub() {
+                        @Override
+                        public void onRangingFailure(int status) throws RemoteException {
+                            clearCallingIdentity();
+                            executor.execute(() -> callback.onRangingFailure(status));
+                        }
+
+                        @Override
+                        public void onRangingResults(List<RangingResult> results)
+                                throws RemoteException {
+                            clearCallingIdentity();
+                            executor.execute(() -> callback.onRangingResults(results));
+                        }
+                    });
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -157,7 +168,7 @@
 
     /**
      * Cancel all ranging requests for the specified work sources. The requests have been requested
-     * using {@link #startRanging(WorkSource, RangingRequest, RangingResultCallback, Handler)}.
+     * using {@link #startRanging(WorkSource, RangingRequest, Executor, RangingResultCallback)}.
      *
      * @param workSource The work-sources of the requesters.
      *
@@ -176,30 +187,4 @@
             throw e.rethrowFromSystemServer();
         }
     }
-
-    private static class RttCallbackProxy extends IRttCallback.Stub {
-        private final Handler mHandler;
-        private final RangingResultCallback mCallback;
-
-        RttCallbackProxy(Looper looper, RangingResultCallback callback) {
-            mHandler = new Handler(looper);
-            mCallback = callback;
-        }
-
-        @Override
-        public void onRangingFailure(int status) throws RemoteException {
-            if (VDBG) Log.v(TAG, "RttCallbackProxy: onRangingFailure: status=" + status);
-            mHandler.post(() -> {
-               mCallback.onRangingFailure(status);
-            });
-        }
-
-        @Override
-        public void onRangingResults(List<RangingResult> results) throws RemoteException {
-            if (VDBG) Log.v(TAG, "RttCallbackProxy: onRanginResults: results=" + results);
-            mHandler.post(() -> {
-               mCallback.onRangingResults(results);
-            });
-        }
-    }
 }
diff --git a/android/nfc/NfcActivityManager.java b/android/nfc/NfcActivityManager.java
index c7d4c65..958063a 100644
--- a/android/nfc/NfcActivityManager.java
+++ b/android/nfc/NfcActivityManager.java
@@ -393,7 +393,7 @@
                                     "either scheme file or scheme content");
                             continue;
                         }
-                        uri = ContentProvider.maybeAddUserId(uri, UserHandle.myUserId());
+                        uri = ContentProvider.maybeAddUserId(uri, activity.getUserId());
                         validUris.add(uri);
                     }
 
@@ -410,7 +410,7 @@
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        return new BeamShareData(message, uris, new UserHandle(UserHandle.myUserId()), flags);
+        return new BeamShareData(message, uris, activity.getUser(), flags);
     }
 
     /** Callback from NFC service, usually on binder thread */
diff --git a/android/nfc/NfcAdapter.java b/android/nfc/NfcAdapter.java
index debef63..c3f23a1 100644
--- a/android/nfc/NfcAdapter.java
+++ b/android/nfc/NfcAdapter.java
@@ -147,6 +147,19 @@
     public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
 
     /**
+     * Broadcast Action: Intent to notify an application that an transaction event has occurred
+     * on the Secure Element.
+     *
+     * <p>This intent will only be sent if the application has requested permission for
+     * {@link android.Manifest.permission#NFC_TRANSACTION_EVENT} and if the application has the
+     * necessary access to Secure Element which witnessed the particular event.
+     */
+    @RequiresPermission(android.Manifest.permission.NFC_TRANSACTION_EVENT)
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_TRANSACTION_DETECTED =
+            "android.nfc.action.TRANSACTION_DETECTED";
+
+    /**
      * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
      * @hide
      */
@@ -197,6 +210,23 @@
      */
     public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
 
+    /**
+     * Mandatory byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+     */
+    public static final String EXTRA_AID = "android.nfc.extra.AID";
+
+    /**
+     * Optional byte[] extra field in {@link #ACTION_TRANSACTION_DETECTED}
+     */
+    public static final String EXTRA_DATA = "android.nfc.extra.DATA";
+
+    /**
+     * Mandatory String extra field in {@link #ACTION_TRANSACTION_DETECTED}
+     * Indicates the Secure Element on which the transaction occurred.
+     * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
+     */
+    public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
+
     public static final int STATE_OFF = 1;
     public static final int STATE_TURNING_ON = 2;
     public static final int STATE_ON = 3;
diff --git a/android/nfc/cardemulation/CardEmulation.java b/android/nfc/cardemulation/CardEmulation.java
index 6dd7993..15d02f2 100644
--- a/android/nfc/cardemulation/CardEmulation.java
+++ b/android/nfc/cardemulation/CardEmulation.java
@@ -201,7 +201,7 @@
      */
     public boolean isDefaultServiceForCategory(ComponentName service, String category) {
         try {
-            return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+            return sService.isDefaultServiceForCategory(mContext.getUserId(), service, category);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -210,7 +210,7 @@
                 return false;
             }
             try {
-                return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
+                return sService.isDefaultServiceForCategory(mContext.getUserId(), service,
                         category);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to recover CardEmulationService.");
@@ -232,7 +232,7 @@
      */
     public boolean isDefaultServiceForAid(ComponentName service, String aid) {
         try {
-            return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+            return sService.isDefaultServiceForAid(mContext.getUserId(), service, aid);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -241,7 +241,7 @@
                 return false;
             }
             try {
-                return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
+                return sService.isDefaultServiceForAid(mContext.getUserId(), service, aid);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 return false;
@@ -326,7 +326,7 @@
             List<String> aids) {
         AidGroup aidGroup = new AidGroup(aids, category);
         try {
-            return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
+            return sService.registerAidGroupForService(mContext.getUserId(), service, aidGroup);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -335,7 +335,7 @@
                 return false;
             }
             try {
-                return sService.registerAidGroupForService(UserHandle.myUserId(), service,
+                return sService.registerAidGroupForService(mContext.getUserId(), service,
                         aidGroup);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -360,7 +360,7 @@
      */
     public List<String> getAidsForService(ComponentName service, String category) {
         try {
-            AidGroup group =  sService.getAidGroupForService(UserHandle.myUserId(), service,
+            AidGroup group =  sService.getAidGroupForService(mContext.getUserId(), service,
                     category);
             return (group != null ? group.getAids() : null);
         } catch (RemoteException e) {
@@ -370,7 +370,7 @@
                 return null;
             }
             try {
-                AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
+                AidGroup group = sService.getAidGroupForService(mContext.getUserId(), service,
                         category);
                 return (group != null ? group.getAids() : null);
             } catch (RemoteException ee) {
@@ -397,7 +397,7 @@
      */
     public boolean removeAidsForService(ComponentName service, String category) {
         try {
-            return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
+            return sService.removeAidGroupForService(mContext.getUserId(), service, category);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -406,7 +406,7 @@
                 return false;
             }
             try {
-                return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
+                return sService.removeAidGroupForService(mContext.getUserId(), service, category);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 return false;
@@ -537,7 +537,7 @@
      */
     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
         try {
-            return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
+            return sService.setDefaultServiceForCategory(mContext.getUserId(), service, category);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -546,7 +546,7 @@
                 return false;
             }
             try {
-                return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
+                return sService.setDefaultServiceForCategory(mContext.getUserId(), service,
                         category);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -560,7 +560,7 @@
      */
     public boolean setDefaultForNextTap(ComponentName service) {
         try {
-            return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+            return sService.setDefaultForNextTap(mContext.getUserId(), service);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -569,7 +569,7 @@
                 return false;
             }
             try {
-                return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
+                return sService.setDefaultForNextTap(mContext.getUserId(), service);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 return false;
@@ -582,7 +582,7 @@
      */
     public List<ApduServiceInfo> getServices(String category) {
         try {
-            return sService.getServices(UserHandle.myUserId(), category);
+            return sService.getServices(mContext.getUserId(), category);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -591,7 +591,7 @@
                 return null;
             }
             try {
-                return sService.getServices(UserHandle.myUserId(), category);
+                return sService.getServices(mContext.getUserId(), category);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 return null;
diff --git a/android/nfc/cardemulation/NfcFCardEmulation.java b/android/nfc/cardemulation/NfcFCardEmulation.java
index f2b5ebc..80e8579 100644
--- a/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -118,7 +118,7 @@
             throw new NullPointerException("service is null");
         }
         try {
-            return sService.getSystemCodeForService(UserHandle.myUserId(), service);
+            return sService.getSystemCodeForService(mContext.getUserId(), service);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -127,7 +127,7 @@
                 return null;
             }
             try {
-                return sService.getSystemCodeForService(UserHandle.myUserId(), service);
+                return sService.getSystemCodeForService(mContext.getUserId(), service);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 ee.rethrowAsRuntimeException();
@@ -164,7 +164,7 @@
             throw new NullPointerException("service or systemCode is null");
         }
         try {
-            return sService.registerSystemCodeForService(UserHandle.myUserId(),
+            return sService.registerSystemCodeForService(mContext.getUserId(),
                     service, systemCode);
         } catch (RemoteException e) {
             // Try one more time
@@ -174,7 +174,7 @@
                 return false;
             }
             try {
-                return sService.registerSystemCodeForService(UserHandle.myUserId(),
+                return sService.registerSystemCodeForService(mContext.getUserId(),
                         service, systemCode);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -195,7 +195,7 @@
             throw new NullPointerException("service is null");
         }
         try {
-            return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
+            return sService.removeSystemCodeForService(mContext.getUserId(), service);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -204,7 +204,7 @@
                 return false;
             }
             try {
-                return sService.removeSystemCodeForService(UserHandle.myUserId(), service);
+                return sService.removeSystemCodeForService(mContext.getUserId(), service);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 ee.rethrowAsRuntimeException();
@@ -230,7 +230,7 @@
             throw new NullPointerException("service is null");
         }
         try {
-            return sService.getNfcid2ForService(UserHandle.myUserId(), service);
+            return sService.getNfcid2ForService(mContext.getUserId(), service);
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -239,7 +239,7 @@
                 return null;
             }
             try {
-                return sService.getNfcid2ForService(UserHandle.myUserId(), service);
+                return sService.getNfcid2ForService(mContext.getUserId(), service);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 ee.rethrowAsRuntimeException();
@@ -273,7 +273,7 @@
             throw new NullPointerException("service or nfcid2 is null");
         }
         try {
-            return sService.setNfcid2ForService(UserHandle.myUserId(),
+            return sService.setNfcid2ForService(mContext.getUserId(),
                     service, nfcid2);
         } catch (RemoteException e) {
             // Try one more time
@@ -283,7 +283,7 @@
                 return false;
             }
             try {
-                return sService.setNfcid2ForService(UserHandle.myUserId(),
+                return sService.setNfcid2ForService(mContext.getUserId(),
                         service, nfcid2);
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -381,7 +381,7 @@
      */
     public List<NfcFServiceInfo> getNfcFServices() {
         try {
-            return sService.getNfcFServices(UserHandle.myUserId());
+            return sService.getNfcFServices(mContext.getUserId());
         } catch (RemoteException e) {
             // Try one more time
             recoverService();
@@ -390,7 +390,7 @@
                 return null;
             }
             try {
-                return sService.getNfcFServices(UserHandle.myUserId());
+                return sService.getNfcFServices(mContext.getUserId());
             } catch (RemoteException ee) {
                 Log.e(TAG, "Failed to reach CardEmulationService.");
                 return null;
diff --git a/android/os/BaseBundle.java b/android/os/BaseBundle.java
index 5312dca..f5a7433 100644
--- a/android/os/BaseBundle.java
+++ b/android/os/BaseBundle.java
@@ -357,6 +357,23 @@
     }
 
     /**
+     * Does a loose equality check between two given {@link BaseBundle} objects.
+     * Returns {@code true} if both are {@code null}, or if both are equal as per
+     * {@link #kindofEquals(BaseBundle)}
+     *
+     * @param a A {@link BaseBundle} object
+     * @param b Another {@link BaseBundle} to compare with a
+     * @return {@code true} if both are the same, {@code false} otherwise
+     *
+     * @see #kindofEquals(BaseBundle)
+     *
+     * @hide
+     */
+    public static boolean kindofEquals(BaseBundle a, BaseBundle b) {
+        return (a == b) || (a != null && a.kindofEquals(b));
+    }
+
+    /**
      * @hide This kind-of does an equality comparison.  Kind-of.
      */
     public boolean kindofEquals(BaseBundle other) {
diff --git a/android/os/BatteryManager.java b/android/os/BatteryManager.java
index 843bdb5..6363161 100644
--- a/android/os/BatteryManager.java
+++ b/android/os/BatteryManager.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.Intent;
@@ -138,6 +139,23 @@
      */
     public static final String EXTRA_SEQUENCE = "seq";
 
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
+     * Contains list of Bundles representing battery events
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EVENTS = "android.os.extra.EVENTS";
+
+    /**
+     * Extra for event in {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
+     * Long value representing time when event occurred as returned by
+     * {@link android.os.SystemClock#elapsedRealtime()}
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP";
+
     // values for "status" field in the ACTION_BATTERY_CHANGED Intent
     public static final int BATTERY_STATUS_UNKNOWN = Constants.BATTERY_STATUS_UNKNOWN;
     public static final int BATTERY_STATUS_CHARGING = Constants.BATTERY_STATUS_CHARGING;
@@ -157,11 +175,11 @@
     // values of the "plugged" field in the ACTION_BATTERY_CHANGED intent.
     // These must be powers of 2.
     /** Power source is an AC charger. */
-    public static final int BATTERY_PLUGGED_AC = 1;
+    public static final int BATTERY_PLUGGED_AC = OsProtoEnums.BATTERY_PLUGGED_AC; // = 1
     /** Power source is a USB port. */
-    public static final int BATTERY_PLUGGED_USB = 2;
+    public static final int BATTERY_PLUGGED_USB = OsProtoEnums.BATTERY_PLUGGED_USB; // = 2
     /** Power source is wireless. */
-    public static final int BATTERY_PLUGGED_WIRELESS = 4;
+    public static final int BATTERY_PLUGGED_WIRELESS = OsProtoEnums.BATTERY_PLUGGED_WIRELESS; // = 4
 
     /** @hide */
     public static final int BATTERY_PLUGGED_ANY =
@@ -323,4 +341,16 @@
     public long getLongProperty(int id) {
         return queryProperty(id);
     }
+
+    /**
+     * Return true if the plugType given is wired
+     * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB},
+     *        or {@link #BATTERY_PLUGGED_WIRELESS}
+     *
+     * @return true if plugType is wired
+     * @hide
+     */
+    public static boolean isPlugWired(int plugType) {
+        return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
+    }
 }
diff --git a/android/os/BatteryManagerInternal.java b/android/os/BatteryManagerInternal.java
index f3a95b9..a86237d 100644
--- a/android/os/BatteryManagerInternal.java
+++ b/android/os/BatteryManagerInternal.java
@@ -24,26 +24,63 @@
 public abstract class BatteryManagerInternal {
     /**
      * Returns true if the device is plugged into any of the specified plug types.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract boolean isPowered(int plugTypeSet);
 
     /**
      * Returns the current plug type.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract int getPlugType();
 
     /**
      * Returns battery level as a percentage.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract int getBatteryLevel();
 
     /**
+     * Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct.
+     * Please note apparently it could be bigger than {@link #getBatteryFullCharge}.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     *
+     * @see android.hardware.health.V1_0.HealthInfo#batteryChargeCounter
+     */
+    public abstract int getBatteryChargeCounter();
+
+    /**
+     * Battery charge value when it is considered to be "full" in uA-h , as defined in the
+     * HealthInfo HAL struct.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     *
+     * @see android.hardware.health.V1_0.HealthInfo#batteryFullCharge
+     */
+    public abstract int getBatteryFullCharge();
+
+    /**
      * Returns whether we currently consider the battery level to be low.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract boolean getBatteryLevelLow();
 
     /**
      * Returns a non-zero value if an unsupported charger is attached.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract int getInvalidCharger();
 }
diff --git a/android/os/BatteryStats.java b/android/os/BatteryStats.java
index 03a8dba..6ebb102 100644
--- a/android/os/BatteryStats.java
+++ b/android/os/BatteryStats.java
@@ -20,8 +20,10 @@
 import android.app.job.JobParameters;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.server.ServerProtoEnums;
 import android.service.batterystats.BatteryStatsServiceDumpProto;
 import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.LongSparseArray;
@@ -239,8 +241,11 @@
      * New in version 30:
      *   - Uid.PROCESS_STATE_FOREGROUND_SERVICE only tracks
      *   ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE.
+     * New in version 31:
+     *   - New cellular network types.
+     *   - Deferred job metrics.
      */
-    static final int CHECKIN_VERSION = 30;
+    static final int CHECKIN_VERSION = 31;
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -286,6 +291,16 @@
     private static final String SYNC_DATA = "sy";
     private static final String JOB_DATA = "jb";
     private static final String JOB_COMPLETION_DATA = "jbc";
+
+    /**
+     * jbd line is:
+     * BATTERY_STATS_CHECKIN_VERSION, uid, which, "jbd",
+     * jobsDeferredEventCount, jobsDeferredCount, totalLatencyMillis,
+     * count at latency < 1 hr, count at latency 1 to 2 hrs, 2 to 4 hrs, 4 to 8 hrs, and past 8 hrs
+     * <p>
+     * @see #JOB_FRESHNESS_BUCKETS
+     */
+    private static final String JOBS_DEFERRED_DATA = "jbd";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
     private static final String WAKEUP_REASON_DATA = "wr";
     private static final String NETWORK_DATA = "nt";
@@ -348,6 +363,20 @@
     public static final String UID_TIMES_TYPE_ALL = "A";
 
     /**
+     * These are the thresholds for bucketing last time since a job was run for an app
+     * that just moved to ACTIVE due to a launch. So if the last time a job ran was less
+     * than 1 hour ago, then it's reasonably fresh, 2 hours ago, not so fresh and so
+     * on.
+     */
+    public static final long[] JOB_FRESHNESS_BUCKETS = {
+            1 * 60 * 60 * 1000L,
+            2 * 60 * 60 * 1000L,
+            4 * 60 * 60 * 1000L,
+            8 * 60 * 60 * 1000L,
+            Long.MAX_VALUE
+    };
+
+    /**
      * State for keeping track of counting information.
      */
     public static abstract class Counter {
@@ -420,6 +449,11 @@
          */
         public abstract LongCounter getScanTimeCounter();
 
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * sleep state.
+         */
+        public abstract LongCounter getSleepTimeCounter();
 
         /**
          * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
@@ -845,6 +879,20 @@
          */
         public abstract long getWifiRadioApWakeupCount(int which);
 
+        /**
+         * Appends the deferred jobs data to the StringBuilder passed in, in checkin format
+         * @param sb StringBuilder that can be overwritten with the deferred jobs data
+         * @param which one of STATS_*
+         */
+        public abstract void getDeferredJobsCheckinLineLocked(StringBuilder sb, int which);
+
+        /**
+         * Appends the deferred jobs data to the StringBuilder passed in
+         * @param sb StringBuilder that can be overwritten with the deferred jobs data
+         * @param which one of STATS_*
+         */
+        public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which);
+
         public static abstract class Sensor {
             /*
              * FIXME: it's not correct to use this magic value because it
@@ -1536,6 +1584,8 @@
         public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
         public static final int STATE2_CAMERA_FLAG = 1<<21;
         public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
+        public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
+        public static final int STATE2_USB_DATA_LINK_FLAG = 1 << 18;
 
         public static final int MOST_INTERESTING_STATES2 =
                 STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2054,17 +2104,17 @@
     /**
      * Constant for device idle mode: not active.
      */
-    public static final int DEVICE_IDLE_MODE_OFF = 0;
+    public static final int DEVICE_IDLE_MODE_OFF = ServerProtoEnums.DEVICE_IDLE_MODE_OFF; // 0
 
     /**
      * Constant for device idle mode: active in lightweight mode.
      */
-    public static final int DEVICE_IDLE_MODE_LIGHT = 1;
+    public static final int DEVICE_IDLE_MODE_LIGHT = ServerProtoEnums.DEVICE_IDLE_MODE_LIGHT; // 1
 
     /**
      * Constant for device idle mode: active in full mode.
      */
-    public static final int DEVICE_IDLE_MODE_DEEP = 2;
+    public static final int DEVICE_IDLE_MODE_DEEP = ServerProtoEnums.DEVICE_IDLE_MODE_DEEP; // 2
 
     /**
      * Returns the time in microseconds that device has been in idle mode while
@@ -2223,27 +2273,12 @@
     public abstract int getMobileRadioActiveUnknownCount(int which);
 
     public static final int DATA_CONNECTION_NONE = 0;
-    public static final int DATA_CONNECTION_GPRS = 1;
-    public static final int DATA_CONNECTION_EDGE = 2;
-    public static final int DATA_CONNECTION_UMTS = 3;
-    public static final int DATA_CONNECTION_CDMA = 4;
-    public static final int DATA_CONNECTION_EVDO_0 = 5;
-    public static final int DATA_CONNECTION_EVDO_A = 6;
-    public static final int DATA_CONNECTION_1xRTT = 7;
-    public static final int DATA_CONNECTION_HSDPA = 8;
-    public static final int DATA_CONNECTION_HSUPA = 9;
-    public static final int DATA_CONNECTION_HSPA = 10;
-    public static final int DATA_CONNECTION_IDEN = 11;
-    public static final int DATA_CONNECTION_EVDO_B = 12;
-    public static final int DATA_CONNECTION_LTE = 13;
-    public static final int DATA_CONNECTION_EHRPD = 14;
-    public static final int DATA_CONNECTION_HSPAP = 15;
-    public static final int DATA_CONNECTION_OTHER = 16;
+    public static final int DATA_CONNECTION_OTHER = TelephonyManager.MAX_NETWORK_TYPE + 1;
 
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
-        "ehrpd", "hspap", "other"
+        "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
     };
 
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
@@ -2329,8 +2364,7 @@
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
     };
 
-    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
-            = new BitDescription[] {
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS = new BitDescription[] {
         new BitDescription(HistoryItem.STATE2_POWER_SAVE_FLAG, "power_save", "ps"),
         new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"),
@@ -2341,6 +2375,7 @@
                 new String[] { "off", "light", "full", "???" },
                 new String[] { "off", "light", "full", "???" }),
         new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"),
+        new BitDescription(HistoryItem.STATE2_USB_DATA_LINK_FLAG, "usb_data", "Ud"),
         new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
         new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
@@ -2352,9 +2387,11 @@
                 WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
         new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
         new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+        new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG,
+                "cellular_high_tx_power", "Chtp"),
         new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
             HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
-            new String[] { "poor", "good"}, new String[] { "poor", "good"}),
+            new String[] { "poor", "good"}, new String[] { "poor", "good"})
     };
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
@@ -3360,8 +3397,6 @@
         for (LongCounter txState : counter.getTxTimeCounters()) {
             totalTxTimeMs += txState.getCountLocked(which);
         }
-        final long sleepTimeMs
-            = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
 
         if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
             final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
@@ -3375,18 +3410,34 @@
             sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
             sb.append(")");
             pw.println(sb.toString());
+
+            final long sleepTimeMs
+                = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Sleep time:  ");
+            formatTimeMs(sb, sleepTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
         }
 
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("     ");
-        sb.append(controllerName);
-        sb.append(" Sleep time:  ");
-        formatTimeMs(sb, sleepTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
+        if (controllerName.equals(CELLULAR_CONTROLLER_NAME)) {
+            final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Sleep time:  ");
+            formatTimeMs(sb, sleepTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
 
         sb.setLength(0);
         sb.append(prefix);
@@ -4062,6 +4113,12 @@
                 }
             }
 
+            // Dump deferred jobs stats
+            u.getDeferredJobsCheckinLineLocked(sb, which);
+            if (sb.length() > 0) {
+                dumpLine(pw, uid, category, JOBS_DEFERRED_DATA, sb.toString());
+            }
+
             dumpTimer(pw, uid, category, FLASHLIGHT_DATA, u.getFlashlightTurnedOnTimer(),
                     rawRealtime, which);
             dumpTimer(pw, uid, category, CAMERA_DATA, u.getCameraTurnedOnTimer(),
@@ -5690,6 +5747,11 @@
                 }
             }
 
+            u.getDeferredJobsLineLocked(sb, which);
+            if (sb.length() > 0) {
+                pw.print("    Jobs deferred on launch "); pw.println(sb.toString());
+            }
+
             uidActivity |= printTimer(pw, sb, u.getFlashlightTurnedOnTimer(), rawRealtime, which,
                     prefix, "Flashlight");
             uidActivity |= printTimer(pw, sb, u.getCameraTurnedOnTimer(), rawRealtime, which,
@@ -7077,13 +7139,20 @@
 
                 for (int isvc = serviceStats.size() - 1; isvc >= 0; --isvc) {
                     final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+
+                    final long startTimeMs = roundUsToMs(ss.getStartTime(batteryUptimeUs, which));
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTimeMs == 0 && starts == 0 && launches == 0) {
+                        continue;
+                    }
+
                     long sToken = proto.start(UidProto.Package.SERVICES);
 
                     proto.write(UidProto.Package.Service.NAME, serviceStats.keyAt(isvc));
-                    proto.write(UidProto.Package.Service.START_DURATION_MS,
-                            roundUsToMs(ss.getStartTime(batteryUptimeUs, which)));
-                    proto.write(UidProto.Package.Service.START_COUNT, ss.getStarts(which));
-                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, ss.getLaunches(which));
+                    proto.write(UidProto.Package.Service.START_DURATION_MS, startTimeMs);
+                    proto.write(UidProto.Package.Service.START_COUNT, starts);
+                    proto.write(UidProto.Package.Service.LAUNCH_COUNT, launches);
 
                     proto.end(sToken);
                 }
@@ -7534,8 +7603,18 @@
 
         // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
         for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
+            // Map OTHER to TelephonyManager.NETWORK_TYPE_UNKNOWN and mark NONE as a boolean.
+            boolean isNone = (i == DATA_CONNECTION_NONE);
+            int telephonyNetworkType = i;
+            if (i == DATA_CONNECTION_OTHER) {
+                telephonyNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+            }
             final long pdcToken = proto.start(SystemProto.DATA_CONNECTION);
-            proto.write(SystemProto.DataConnection.NAME, i);
+            if (isNone) {
+                proto.write(SystemProto.DataConnection.IS_NONE, isNone);
+            } else {
+                proto.write(SystemProto.DataConnection.NAME, telephonyNetworkType);
+            }
             dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
                     rawRealtimeUs, which);
             proto.end(pdcToken);
diff --git a/android/os/BatteryStatsInternal.java b/android/os/BatteryStatsInternal.java
index b0436eb..679f18e 100644
--- a/android/os/BatteryStatsInternal.java
+++ b/android/os/BatteryStatsInternal.java
@@ -18,7 +18,7 @@
 
 /**
  * Battery stats local system service interface. This is used to pass internal data out of
- * BatteryStatsImpl.
+ * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
  *
  * @hide Only for use within Android OS.
  */
@@ -32,4 +32,13 @@
      * Returns the mobile data interfaces.
      */
     public abstract String[] getMobileIfaces();
+
+    /**
+     * Inform battery stats how many deferred jobs existed when the app got launched and how
+     * long ago was the last job execution for the app.
+     * @param uid the uid of the app.
+     * @param numDeferred number of deferred jobs.
+     * @param sinceLast how long in millis has it been since a job was run
+     */
+    public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
 }
diff --git a/android/os/BestClock.java b/android/os/BestClock.java
new file mode 100644
index 0000000..aa066b6
--- /dev/null
+++ b/android/os/BestClock.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.ZoneId;
+import java.util.Arrays;
+
+/**
+ * Single {@link Clock} that will return the best available time from a set of
+ * prioritized {@link Clock} instances.
+ * <p>
+ * For example, when {@link SystemClock#currentNetworkTimeClock()} isn't able to
+ * provide the time, this class could use {@link Clock#systemUTC()} instead.
+ *
+ * @hide
+ */
+public class BestClock extends SimpleClock {
+    private static final String TAG = "BestClock";
+
+    private final Clock[] clocks;
+
+    public BestClock(ZoneId zone, Clock... clocks) {
+        super(zone);
+        this.clocks = clocks;
+    }
+
+    @Override
+    public long millis() {
+        for (Clock clock : clocks) {
+            try {
+                return clock.millis();
+            } catch (DateTimeException e) {
+                // Ignore and attempt the next clock
+                Log.w(TAG, e.toString());
+            }
+        }
+        throw new DateTimeException(
+                "No clocks in " + Arrays.toString(clocks) + " were able to provide time");
+    }
+}
diff --git a/android/os/Binder.java b/android/os/Binder.java
index eb264d6..0ae5394 100644
--- a/android/os/Binder.java
+++ b/android/os/Binder.java
@@ -21,7 +21,10 @@
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
+import com.android.internal.os.BinderCallsStats;
+import com.android.internal.os.BinderInternal;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
@@ -374,7 +377,9 @@
      * Add the calling thread to the IPC thread pool.  This function does
      * not return until the current process is exiting.
      */
-    public static final native void joinThreadPool();
+    public static final void joinThreadPool() {
+        BinderInternal.joinThreadPool();
+    }
 
     /**
      * Returns true if the specified interface is a proxy.
@@ -708,6 +713,8 @@
     // Entry point from android_util_Binder.cpp's onTransact
     private boolean execTransact(int code, long dataObj, long replyObj,
             int flags) {
+        BinderCallsStats binderCallsStats = BinderCallsStats.getInstance();
+        BinderCallsStats.CallSession callSession = binderCallsStats.callStarted(this, code);
         Parcel data = Parcel.obtain(dataObj);
         Parcel reply = Parcel.obtain(replyObj);
         // theoretically, we should call transact, which will call onTransact,
@@ -752,6 +759,7 @@
         // to the main transaction loop to wait for another incoming transaction.  Either
         // way, strict mode begone!
         StrictMode.clearGatheredViolations();
+        binderCallsStats.callEnded(callSession);
 
         return res;
     }
@@ -782,7 +790,7 @@
         private static final int MAIN_INDEX_SIZE = 1 <<  LOG_MAIN_INDEX_SIZE;
         private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
         // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
-        private static final int CRASH_AT_SIZE = 5_000;
+        private static final int CRASH_AT_SIZE = 20_000;
 
         /**
          * We next warn when we exceed this bucket size.
@@ -934,6 +942,7 @@
                     final int totalUnclearedSize = unclearedSize();
                     if (totalUnclearedSize >= CRASH_AT_SIZE) {
                         dumpProxyInterfaceCounts();
+                        dumpPerUidProxyCounts();
                         Runtime.getRuntime().gc();
                         throw new AssertionError("Binder ProxyMap has too many entries: "
                                 + totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
@@ -987,6 +996,20 @@
             }
         }
 
+        /**
+         * Dump per uid binder proxy counts to the logcat.
+         */
+        private void dumpPerUidProxyCounts() {
+            SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+            if (counts.size() == 0) return;
+            Log.d(Binder.TAG, "Per Uid Binder Proxy Counts:");
+            for (int i = 0; i < counts.size(); i++) {
+                final int uid = counts.keyAt(i);
+                final int binderCount = counts.valueAt(i);
+                Log.d(Binder.TAG, "UID : " + uid + "  count = " + binderCount);
+            }
+        }
+
         // Corresponding ArrayLists in the following two arrays always have the same size.
         // They contain no empty entries. However WeakReferences in the values ArrayLists
         // may have been cleared.
@@ -1011,22 +1034,33 @@
      * in use, then we return the same bp.
      *
      * @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
-     * Takes ownership of nativeData iff <result>.mNativeData == nativeData.  Caller will usually
-     * delete nativeData if that's not the case.
+     * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
+     * we exit via an exception.  If neither applies, it's the callers responsibility to
+     * recycle nativeData.
      * @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
      */
     private static BinderProxy getInstance(long nativeData, long iBinder) {
-        BinderProxy result = sProxyMap.get(iBinder);
-        if (result == null) {
+        BinderProxy result;
+        try {
+            result = sProxyMap.get(iBinder);
+            if (result != null) {
+                return result;
+            }
             result = new BinderProxy(nativeData);
-            sProxyMap.set(iBinder, result);
+        } catch (Throwable e) {
+            // We're throwing an exception (probably OOME); don't drop nativeData.
+            NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+                    nativeData);
+            throw e;
         }
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+        // The registry now owns nativeData, even if registration threw an exception.
+        sProxyMap.set(iBinder, result);
         return result;
     }
 
     private BinderProxy(long nativeData) {
         mNativeData = nativeData;
-        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeData);
     }
 
     /**
@@ -1040,8 +1074,9 @@
     // Use a Holder to allow static initialization of BinderProxy in the boot image, and
     // to avoid some initialization ordering issues.
     private static class NoImagePreloadHolder {
+        public static final long sNativeFinalizer = getNativeFinalizer();
         public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-                BinderProxy.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+                BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
     }
 
     public native boolean pingBinder();
diff --git a/android/os/BinderCallsStatsPerfTest.java b/android/os/BinderCallsStatsPerfTest.java
new file mode 100644
index 0000000..28d4096
--- /dev/null
+++ b/android/os/BinderCallsStatsPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BinderCallsStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertNull;
+
+
+/**
+ * Performance tests for {@link BinderCallsStats}
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BinderCallsStatsPerfTest {
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    private BinderCallsStats mBinderCallsStats;
+
+    @Before
+    public void setUp() {
+        mBinderCallsStats = new BinderCallsStats(true);
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void timeCallSession() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        int i = 0;
+        while (state.keepRunning()) {
+            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, i % 100);
+            mBinderCallsStats.callEnded(s);
+            i++;
+        }
+    }
+
+    @Test
+    public void timeCallSessionTrackingDisabled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        Binder b = new Binder();
+        mBinderCallsStats = new BinderCallsStats(false);
+        assertNull(mBinderCallsStats.callStarted(b, 0));
+        while (state.keepRunning()) {
+            BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
+            mBinderCallsStats.callEnded(s);
+        }
+    }
+
+}
diff --git a/android/os/Build.java b/android/os/Build.java
index 48f5684..8378a82 100644
--- a/android/os/Build.java
+++ b/android/os/Build.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Application;
 import android.content.Context;
 import android.text.TextUtils;
@@ -241,7 +242,9 @@
          * Possible values are defined in {@link Build.VERSION_CODES}.
          *
          * @see #SDK_INT
+         * @hide
          */
+        @TestApi
         public static final int FIRST_SDK_INT = SystemProperties
                 .getInt("ro.product.first_api_level", 0);
 
@@ -287,12 +290,15 @@
          * we are operating under, we bump the assumed resource platform version by 1.
          * @hide
          */
+        @TestApi
         public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
 
         /**
          * The current lowest supported value of app target SDK. Applications targeting
-         * lower values will fail to install and run. Its possible values are defined
-         * in {@link Build.VERSION_CODES}.
+         * lower values may not function on devices running this SDK version. Its possible
+         * values are defined in {@link Build.VERSION_CODES}.
+         *
+         * @hide
          */
         public static final int MIN_SUPPORTED_TARGET_SDK_INT = SystemProperties.getInt(
                 "ro.build.version.min_supported_target_sdk", 0);
@@ -894,6 +900,14 @@
 
         /**
          * P.
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li>{@link android.app.Service#startForeground Service.startForeground} requires
+         * that apps hold the permission
+         * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
+         * </ul>
          */
         public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
     }
diff --git a/android/os/ChildZygoteProcess.java b/android/os/ChildZygoteProcess.java
new file mode 100644
index 0000000..337a3e2
--- /dev/null
+++ b/android/os/ChildZygoteProcess.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.net.LocalSocketAddress;
+
+/**
+ * Represents a connection to a child-zygote process. A child-zygote is spawend from another
+ * zygote process using {@link startChildZygote()}.
+ *
+ * {@hide}
+ */
+public class ChildZygoteProcess extends ZygoteProcess {
+    /**
+     * The PID of the child zygote process.
+     */
+    private final int mPid;
+
+    ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
+        super(socketAddress, null);
+        mPid = pid;
+    }
+
+    /**
+     * Returns the PID of the child-zygote process.
+     */
+    public int getPid() {
+        return mPid;
+    }
+}
diff --git a/android/os/Debug.java b/android/os/Debug.java
index 33e8c3e..e606964 100644
--- a/android/os/Debug.java
+++ b/android/os/Debug.java
@@ -56,10 +56,10 @@
  * <p><strong>Logging Trace Files</strong></p>
  * <p>Debug can create log files that give details about an application, such as
  * a call stack and start/stop times for any running methods. See <a
-href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer</a> for
- * information about reading trace files. To start logging trace files, call one
- * of the startMethodTracing() methods. To stop tracing, call
- * {@link #stopMethodTracing()}.
+ * href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs with
+ * Traceview</a> for information about reading trace files. To start logging
+ * trace files, call one of the startMethodTracing() methods. To stop tracing,
+ * call {@link #stopMethodTracing()}.
  */
 public final class Debug
 {
@@ -116,6 +116,8 @@
         /** The proportional set size that is swappable for dalvik heap. */
         /** @hide We may want to expose this, eventually. */
         public int dalvikSwappablePss;
+        /** @hide The resident set size for dalvik heap.  (Without other Dalvik overhead.) */
+        public int dalvikRss;
         /** The private dirty pages used by dalvik heap. */
         public int dalvikPrivateDirty;
         /** The shared dirty pages used by dalvik heap. */
@@ -138,6 +140,8 @@
         /** The proportional set size that is swappable for the native heap. */
         /** @hide We may want to expose this, eventually. */
         public int nativeSwappablePss;
+        /** @hide The resident set size for the native heap. */
+        public int nativeRss;
         /** The private dirty pages used by the native heap. */
         public int nativePrivateDirty;
         /** The shared dirty pages used by the native heap. */
@@ -160,6 +164,8 @@
         /** The proportional set size that is swappable for everything else. */
         /** @hide We may want to expose this, eventually. */
         public int otherSwappablePss;
+        /** @hide The resident set size for everything else. */
+        public int otherRss;
         /** The private dirty pages used by everything else. */
         public int otherPrivateDirty;
         /** The shared dirty pages used by everything else. */
@@ -288,24 +294,26 @@
         public static final int NUM_DVK_STATS = 14;
 
         /** @hide */
-        public static final int NUM_CATEGORIES = 8;
+        public static final int NUM_CATEGORIES = 9;
 
         /** @hide */
-        public static final int offsetPss = 0;
+        public static final int OFFSET_PSS = 0;
         /** @hide */
-        public static final int offsetSwappablePss = 1;
+        public static final int OFFSET_SWAPPABLE_PSS = 1;
         /** @hide */
-        public static final int offsetPrivateDirty = 2;
+        public static final int OFFSET_RSS = 2;
         /** @hide */
-        public static final int offsetSharedDirty = 3;
+        public static final int OFFSET_PRIVATE_DIRTY = 3;
         /** @hide */
-        public static final int offsetPrivateClean = 4;
+        public static final int OFFSET_SHARED_DIRTY = 4;
         /** @hide */
-        public static final int offsetSharedClean = 5;
+        public static final int OFFSET_PRIVATE_CLEAN = 5;
         /** @hide */
-        public static final int offsetSwappedOut = 6;
+        public static final int OFFSET_SHARED_CLEAN = 6;
         /** @hide */
-        public static final int offsetSwappedOutPss = 7;
+        public static final int OFFSET_SWAPPED_OUT = 7;
+        /** @hide */
+        public static final int OFFSET_SWAPPED_OUT_PSS = 8;
 
         private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
 
@@ -337,6 +345,13 @@
         }
 
         /**
+         * @hide Return total RSS memory usage in kB.
+         */
+        public int getTotalRss() {
+            return dalvikRss + nativeRss + otherRss;
+        }
+
+        /**
          * Return total private dirty memory usage in kB.
          */
         public int getTotalPrivateDirty() {
@@ -382,29 +397,32 @@
 
         /** @hide */
         public int getOtherPss(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetPss];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_PSS];
         }
 
-
         /** @hide */
         public int getOtherSwappablePss(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetSwappablePss];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPABLE_PSS];
         }
 
+        /** @hide */
+        public int getOtherRss(int which) {
+            return otherStats[which * NUM_CATEGORIES + OFFSET_RSS];
+        }
 
         /** @hide */
         public int getOtherPrivateDirty(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetPrivateDirty];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_PRIVATE_DIRTY];
         }
 
         /** @hide */
         public int getOtherSharedDirty(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetSharedDirty];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SHARED_DIRTY];
         }
 
         /** @hide */
         public int getOtherPrivateClean(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetPrivateClean];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_PRIVATE_CLEAN];
         }
 
         /** @hide */
@@ -414,17 +432,17 @@
 
         /** @hide */
         public int getOtherSharedClean(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetSharedClean];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SHARED_CLEAN];
         }
 
         /** @hide */
         public int getOtherSwappedOut(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetSwappedOut];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPED_OUT];
         }
 
         /** @hide */
         public int getOtherSwappedOutPss(int which) {
-            return otherStats[which*NUM_CATEGORIES + offsetSwappedOutPss];
+            return otherStats[which * NUM_CATEGORIES + OFFSET_SWAPPED_OUT_PSS];
         }
 
         /** @hide */
@@ -741,6 +759,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(dalvikPss);
             dest.writeInt(dalvikSwappablePss);
+            dest.writeInt(dalvikRss);
             dest.writeInt(dalvikPrivateDirty);
             dest.writeInt(dalvikSharedDirty);
             dest.writeInt(dalvikPrivateClean);
@@ -749,6 +768,7 @@
             dest.writeInt(dalvikSwappedOutPss);
             dest.writeInt(nativePss);
             dest.writeInt(nativeSwappablePss);
+            dest.writeInt(nativeRss);
             dest.writeInt(nativePrivateDirty);
             dest.writeInt(nativeSharedDirty);
             dest.writeInt(nativePrivateClean);
@@ -757,6 +777,7 @@
             dest.writeInt(nativeSwappedOutPss);
             dest.writeInt(otherPss);
             dest.writeInt(otherSwappablePss);
+            dest.writeInt(otherRss);
             dest.writeInt(otherPrivateDirty);
             dest.writeInt(otherSharedDirty);
             dest.writeInt(otherPrivateClean);
@@ -770,6 +791,7 @@
         public void readFromParcel(Parcel source) {
             dalvikPss = source.readInt();
             dalvikSwappablePss = source.readInt();
+            dalvikRss = source.readInt();
             dalvikPrivateDirty = source.readInt();
             dalvikSharedDirty = source.readInt();
             dalvikPrivateClean = source.readInt();
@@ -778,6 +800,7 @@
             dalvikSwappedOutPss = source.readInt();
             nativePss = source.readInt();
             nativeSwappablePss = source.readInt();
+            nativeRss = source.readInt();
             nativePrivateDirty = source.readInt();
             nativeSharedDirty = source.readInt();
             nativePrivateClean = source.readInt();
@@ -786,6 +809,7 @@
             nativeSwappedOutPss = source.readInt();
             otherPss = source.readInt();
             otherSwappablePss = source.readInt();
+            otherRss = source.readInt();
             otherPrivateDirty = source.readInt();
             otherSharedDirty = source.readInt();
             otherPrivateClean = source.readInt();
@@ -1001,8 +1025,8 @@
      * under your package-specific directory on primary shared/external storage,
      * as returned by {@link Context#getExternalFilesDir(String)}.
      * <p>
-     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
-     * A Graphical Log Viewer</a> for information about reading trace files.
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
      * <p class="note">
      * When method tracing is enabled, the VM will run more slowly than usual,
      * so the timings from the trace files should only be considered in relative
@@ -1025,8 +1049,8 @@
      * your package-specific directory on primary shared/external storage, as
      * returned by {@link Context#getExternalFilesDir(String)}.
      * <p>
-     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
-     * A Graphical Log Viewer</a> for information about reading trace files.
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
      * <p class="note">
      * When method tracing is enabled, the VM will run more slowly than usual,
      * so the timings from the trace files should only be considered in relative
@@ -1055,8 +1079,8 @@
      * your package-specific directory on primary shared/external storage, as
      * returned by {@link Context#getExternalFilesDir(String)}.
      * <p>
-     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
-     * A Graphical Log Viewer</a> for information about reading trace files.
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
      * <p class="note">
      * When method tracing is enabled, the VM will run more slowly than usual,
      * so the timings from the trace files should only be considered in relative
@@ -1087,8 +1111,8 @@
      * your package-specific directory on primary shared/external storage, as
      * returned by {@link Context#getExternalFilesDir(String)}.
      * <p>
-     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
-     * A Graphical Log Viewer</a> for information about reading trace files.
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
      * <p class="note">
      * When method tracing is enabled, the VM will run more slowly than usual,
      * so the timings from the trace files should only be considered in relative
@@ -1121,8 +1145,8 @@
      * your package-specific directory on primary shared/external storage, as
      * returned by {@link Context#getExternalFilesDir(String)}.
      * <p>
-     * See <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview:
-     * A Graphical Log Viewer</a> for information about reading trace files.
+     * See <a href="{@docRoot}studio/profile/traceview.html">Inspect Trace Logs
+     * with Traceview</a> for information about reading trace files.
      *
      * @param tracePath Path to the trace log file to create. If {@code null},
      *            this will default to "dmtrace.trace". If the file already
diff --git a/android/os/Environment.java b/android/os/Environment.java
index 62731e8..03203d0 100644
--- a/android/os/Environment.java
+++ b/android/os/Environment.java
@@ -41,6 +41,7 @@
     private static final String ENV_OEM_ROOT = "OEM_ROOT";
     private static final String ENV_ODM_ROOT = "ODM_ROOT";
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
+    private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
 
     /** {@hide} */
     public static final String DIR_ANDROID = "Android";
@@ -62,6 +63,7 @@
     private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
     private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
+    private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
 
     private static UserEnvironment sCurrentUser;
     private static boolean sUserRequired;
@@ -180,6 +182,16 @@
     }
 
     /**
+     * Return root directory of the "product" partition holding product-specific
+     * customizations if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    public static File getProductDirectory() {
+        return DIR_PRODUCT_ROOT;
+    }
+
+    /**
      * Return the system directory for a user. This is for use by system
      * services to store files relating to the user. This directory will be
      * automatically deleted when the user is removed.
@@ -302,9 +314,8 @@
     }
 
     /** {@hide} */
-    public static File getProfileSnapshotPath(String packageName, String codePath) {
-        return buildPath(buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName,
-                "primary.prof.snapshot"));
+    public static File getDataRefProfilesDePackageDirectory(String packageName) {
+        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
     }
 
     /** {@hide} */
diff --git a/android/os/FileUtils.java b/android/os/FileUtils.java
index 7c53ec1..88d6e84 100644
--- a/android/os/FileUtils.java
+++ b/android/os/FileUtils.java
@@ -16,6 +16,11 @@
 
 package android.os;
 
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.provider.DocumentsContract.Document;
@@ -28,7 +33,9 @@
 import android.webkit.MimeTypeMap;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.SizedInputStream;
 
+import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
 import java.io.BufferedInputStream;
@@ -41,10 +48,12 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
@@ -81,6 +90,14 @@
 
     private static final File[] EMPTY = new File[0];
 
+    private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+
+    private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+    public interface ProgressListener {
+        public void onProgress(long progress);
+    }
+
     /**
      * Set owner and mode of of given {@link File}.
      *
@@ -185,6 +202,9 @@
         return false;
     }
 
+    /**
+     * @deprecated use {@link #copy(File, File)} instead.
+     */
     @Deprecated
     public static boolean copyFile(File srcFile, File destFile) {
         try {
@@ -195,14 +215,19 @@
         }
     }
 
-    // copy a file from srcFile to destFile, return true if succeed, return
-    // false if fail
+    /**
+     * @deprecated use {@link #copy(File, File)} instead.
+     */
+    @Deprecated
     public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
         try (InputStream in = new FileInputStream(srcFile)) {
             copyToFileOrThrow(in, destFile);
         }
     }
 
+    /**
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+     */
     @Deprecated
     public static boolean copyToFile(InputStream inputStream, File destFile) {
         try {
@@ -214,32 +239,259 @@
     }
 
     /**
-     * Copy data from a source stream to destFile.
-     * Return true if succeed, return false if failed.
+     * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
      */
-    public static void copyToFileOrThrow(InputStream inputStream, File destFile)
-            throws IOException {
+    @Deprecated
+    public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
         if (destFile.exists()) {
             destFile.delete();
         }
-        FileOutputStream out = new FileOutputStream(destFile);
-        try {
-            byte[] buffer = new byte[4096];
-            int bytesRead;
-            while ((bytesRead = inputStream.read(buffer)) >= 0) {
-                out.write(buffer, 0, bytesRead);
-            }
-        } finally {
-            out.flush();
+        try (FileOutputStream out = new FileOutputStream(destFile)) {
+            copy(in, out);
             try {
-                out.getFD().sync();
-            } catch (IOException e) {
+                Os.fsync(out.getFD());
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
             }
-            out.close();
         }
     }
 
     /**
+     * Copy the contents of one file to another, replacing any existing content.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull File from, @NonNull File to) throws IOException {
+        return copy(from, to, null, null);
+    }
+
+    /**
+     * Copy the contents of one file to another, replacing any existing content.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param listener to be periodically notified as the copy progresses.
+     * @param signal to signal if the copy should be cancelled early.
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull File from, @NonNull File to,
+            @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+            throws IOException {
+        try (FileInputStream in = new FileInputStream(from);
+                FileOutputStream out = new FileOutputStream(to)) {
+            return copy(in, out, listener, signal);
+        }
+    }
+
+    /**
+     * Copy the contents of one stream to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
+        return copy(in, out, null, null);
+    }
+
+    /**
+     * Copy the contents of one stream to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param listener to be periodically notified as the copy progresses.
+     * @param signal to signal if the copy should be cancelled early.
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
+            @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+            throws IOException {
+        if (ENABLE_COPY_OPTIMIZATIONS) {
+            if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+                return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+                        listener, signal);
+            }
+        }
+
+        // Worse case fallback to userspace
+        return copyInternalUserspace(in, out, listener, signal);
+    }
+
+    /**
+     * Copy the contents of one FD to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
+            throws IOException {
+        return copy(in, out, null, null);
+    }
+
+    /**
+     * Copy the contents of one FD to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param listener to be periodically notified as the copy progresses.
+     * @param signal to signal if the copy should be cancelled early.
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+            @Nullable ProgressListener listener, @Nullable CancellationSignal signal)
+            throws IOException {
+        return copy(in, out, listener, signal, Long.MAX_VALUE);
+    }
+
+    /**
+     * Copy the contents of one FD to another.
+     * <p>
+     * Attempts to use several optimization strategies to copy the data in the
+     * kernel before falling back to a userspace copy as a last resort.
+     *
+     * @param listener to be periodically notified as the copy progresses.
+     * @param signal to signal if the copy should be cancelled early.
+     * @param count the number of bytes to copy.
+     * @return number of bytes copied.
+     */
+    public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+            @Nullable ProgressListener listener, @Nullable CancellationSignal signal, long count)
+            throws IOException {
+        if (ENABLE_COPY_OPTIMIZATIONS) {
+            try {
+                final StructStat st_in = Os.fstat(in);
+                final StructStat st_out = Os.fstat(out);
+                if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+                    return copyInternalSendfile(in, out, listener, signal, count);
+                } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+                    return copyInternalSplice(in, out, listener, signal, count);
+                }
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+        }
+
+        // Worse case fallback to userspace
+        return copyInternalUserspace(in, out, listener, signal, count);
+    }
+
+    /**
+     * Requires one of input or output to be a pipe.
+     */
+    @VisibleForTesting
+    public static long copyInternalSplice(FileDescriptor in, FileDescriptor out,
+            ProgressListener listener, CancellationSignal signal, long count)
+            throws ErrnoException {
+        long progress = 0;
+        long checkpoint = 0;
+
+        long t;
+        while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES),
+                SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+            progress += t;
+            checkpoint += t;
+            count -= t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (listener != null) {
+                    listener.onProgress(progress);
+                }
+                checkpoint = 0;
+            }
+        }
+        if (listener != null) {
+            listener.onProgress(progress);
+        }
+        return progress;
+    }
+
+    /**
+     * Requires both input and output to be a regular file.
+     */
+    @VisibleForTesting
+    public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out,
+            ProgressListener listener, CancellationSignal signal, long count)
+            throws ErrnoException {
+        long progress = 0;
+        long checkpoint = 0;
+
+        long t;
+        while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) {
+            progress += t;
+            checkpoint += t;
+            count -= t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (listener != null) {
+                    listener.onProgress(progress);
+                }
+                checkpoint = 0;
+            }
+        }
+        if (listener != null) {
+            listener.onProgress(progress);
+        }
+        return progress;
+    }
+
+    @VisibleForTesting
+    public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+            ProgressListener listener, CancellationSignal signal, long count) throws IOException {
+        if (count != Long.MAX_VALUE) {
+            return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
+                    new FileOutputStream(out), listener, signal);
+        } else {
+            return copyInternalUserspace(new FileInputStream(in),
+                    new FileOutputStream(out), listener, signal);
+        }
+    }
+
+    @VisibleForTesting
+    public static long copyInternalUserspace(InputStream in, OutputStream out,
+            ProgressListener listener, CancellationSignal signal) throws IOException {
+        long progress = 0;
+        long checkpoint = 0;
+        byte[] buffer = new byte[8192];
+
+        int t;
+        while ((t = in.read(buffer)) != -1) {
+            out.write(buffer, 0, t);
+
+            progress += t;
+            checkpoint += t;
+
+            if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+                if (signal != null) {
+                    signal.throwIfCanceled();
+                }
+                if (listener != null) {
+                    listener.onProgress(progress);
+                }
+                checkpoint = 0;
+            }
+        }
+        if (listener != null) {
+            listener.onProgress(progress);
+        }
+        return progress;
+    }
+
+    /**
      * Check if a filename is "safe" (no metacharacters or spaces).
      * @param file  The file to check
      */
@@ -797,4 +1049,69 @@
         }
         return val * pow;
     }
+
+    @VisibleForTesting
+    public static class MemoryPipe extends Thread implements AutoCloseable {
+        private final FileDescriptor[] pipe;
+        private final byte[] data;
+        private final boolean sink;
+
+        private MemoryPipe(byte[] data, boolean sink) throws IOException {
+            try {
+                this.pipe = Os.pipe();
+            } catch (ErrnoException e) {
+                throw e.rethrowAsIOException();
+            }
+            this.data = data;
+            this.sink = sink;
+        }
+
+        private MemoryPipe startInternal() {
+            super.start();
+            return this;
+        }
+
+        public static MemoryPipe createSource(byte[] data) throws IOException {
+            return new MemoryPipe(data, false).startInternal();
+        }
+
+        public static MemoryPipe createSink(byte[] data) throws IOException {
+            return new MemoryPipe(data, true).startInternal();
+        }
+
+        public FileDescriptor getFD() {
+            return sink ? pipe[1] : pipe[0];
+        }
+
+        public FileDescriptor getInternalFD() {
+            return sink ? pipe[0] : pipe[1];
+        }
+
+        @Override
+        public void run() {
+            final FileDescriptor fd = getInternalFD();
+            try {
+                int i = 0;
+                while (i < data.length) {
+                    if (sink) {
+                        i += Os.read(fd, data, i, data.length - i);
+                    } else {
+                        i += Os.write(fd, data, i, data.length - i);
+                    }
+                }
+            } catch (IOException | ErrnoException e) {
+                // Ignored
+            } finally {
+                if (sink) {
+                    SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+                }
+                IoUtils.closeQuietly(fd);
+            }
+        }
+
+        @Override
+        public void close() throws Exception {
+            IoUtils.closeQuietly(getFD());
+        }
+    }
 }
diff --git a/android/os/Handler.java b/android/os/Handler.java
index fc88e90..f5bca04 100644
--- a/android/os/Handler.java
+++ b/android/os/Handler.java
@@ -220,7 +220,7 @@
      *
      * Asynchronous messages represent interrupts or events that do not require global ordering
      * with respect to synchronous messages.  Asynchronous messages are not subject to
-     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
+     * the synchronization barriers introduced by conditions such as display vsync.
      *
      * @param looper The looper, must not be null.
      * @param callback The callback interface in which to handle messages, or null.
@@ -236,6 +236,43 @@
         mAsynchronous = async;
     }
 
+    /**
+     * Create a new Handler whose posted messages and runnables are not subject to
+     * synchronization barriers such as display vsync.
+     *
+     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
+     * but not necessarily with respect to messages from other Handlers.</p>
+     *
+     * @see #createAsync(Looper, Callback) to create an async Handler with custom message handling.
+     *
+     * @param looper the Looper that the new Handler should be bound to
+     * @return a new async Handler instance
+     */
+    @NonNull
+    public static Handler createAsync(@NonNull Looper looper) {
+        if (looper == null) throw new NullPointerException("looper must not be null");
+        return new Handler(looper, null, true);
+    }
+
+    /**
+     * Create a new Handler whose posted messages and runnables are not subject to
+     * synchronization barriers such as display vsync.
+     *
+     * <p>Messages sent to an async handler are guaranteed to be ordered with respect to one another,
+     * but not necessarily with respect to messages from other Handlers.</p>
+     *
+     * @see #createAsync(Looper) to create an async Handler without custom message handling.
+     *
+     * @param looper the Looper that the new Handler should be bound to
+     * @return a new async Handler instance
+     */
+    @NonNull
+    public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
+        if (looper == null) throw new NullPointerException("looper must not be null");
+        if (callback == null) throw new NullPointerException("callback must not be null");
+        return new Handler(looper, callback, true);
+    }
+
     /** @hide */
     @NonNull
     public static Handler getMain() {
@@ -683,6 +720,23 @@
         return enqueueMessage(queue, msg, 0);
     }
 
+    /**
+     * Executes the message synchronously if called on the same thread this handler corresponds to,
+     * or {@link #sendMessage pushes it to the queue} otherwise
+     *
+     * @return Returns true if the message was successfully ran or placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     * @hide
+     */
+    public final boolean executeOrSendMessage(Message msg) {
+        if (mLooper == Looper.myLooper()) {
+            dispatchMessage(msg);
+            return true;
+        }
+        return sendMessage(msg);
+    }
+
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
         msg.target = this;
         if (mAsynchronous) {
diff --git a/android/os/HardwarePropertiesManager.java b/android/os/HardwarePropertiesManager.java
index eae7d70..3d072c5 100644
--- a/android/os/HardwarePropertiesManager.java
+++ b/android/os/HardwarePropertiesManager.java
@@ -63,6 +63,8 @@
     /**
      * Device temperature types.
      */
+    // These constants are also defined in android/os/enums.proto.
+    // Any change to the types here or in the thermal hal should be made in the proto as well.
     /** Temperature of CPUs in Celsius. */
     public static final int DEVICE_TEMPERATURE_CPU = Constants.TemperatureType.CPU;
 
diff --git a/android/os/HidlSupport.java b/android/os/HidlSupport.java
index 335bf9d..91b796a 100644
--- a/android/os/HidlSupport.java
+++ b/android/os/HidlSupport.java
@@ -212,9 +212,10 @@
     }
 
     /**
-     * Return PID of process if sharable to clients.
+     * Return PID of process only if on a non-user build. For debugging purposes.
      * @hide
      */
+    @SystemApi
     public static native int getPidIfSharable();
 
     /** @hide */
diff --git a/android/os/HwBinder.java b/android/os/HwBinder.java
index ecac002..228fe7a 100644
--- a/android/os/HwBinder.java
+++ b/android/os/HwBinder.java
@@ -29,7 +29,13 @@
 
     private static final NativeAllocationRegistry sNativeRegistry;
 
-    /** @hide */
+    /**
+     * Create and initialize a HwBinder object and the native objects
+     * used to allow this to participate in hwbinder transactions.
+     *
+     * @hide
+     */
+    @SystemApi
     public HwBinder() {
         native_setup();
 
@@ -44,23 +50,55 @@
             int code, HwParcel request, HwParcel reply, int flags)
         throws RemoteException;
 
-    /** @hide */
+    /**
+     * Process a hwbinder transaction.
+     *
+     * @param code interface specific code for interface.
+     * @param request parceled transaction
+     * @param reply object to parcel reply into
+     * @param flags transaction flags to be chosen by wire protocol
+     *
+     * @hide
+     */
+    @SystemApi
     public abstract void onTransact(
             int code, HwParcel request, HwParcel reply, int flags)
         throws RemoteException;
 
-    /** @hide */
+    /**
+     * Registers this service with the hwservicemanager.
+     *
+     * @param serviceName instance name of the service
+     * @hide
+     */
+    @SystemApi
     public native final void registerService(String serviceName)
         throws RemoteException;
 
-    /** @hide */
+    /**
+     * Returns the specified service from the hwservicemanager. Does not retry.
+     *
+     * @param iface fully-qualified interface name for example [email protected]::IBaz
+     * @param serviceName the instance name of the service for example default.
+     * @throws NoSuchElementException when the service is unavailable
+     * @hide
+     */
+    @SystemApi
     public static final IHwBinder getService(
             String iface,
             String serviceName)
         throws RemoteException, NoSuchElementException {
         return getService(iface, serviceName, false /* retry */);
     }
-    /** @hide */
+    /**
+     * Returns the specified service from the hwservicemanager.
+     * @param iface fully-qualified interface name for example [email protected]::IBaz
+     * @param serviceName the instance name of the service for example default.
+     * @param retry whether to wait for the service to start if it's not already started
+     * @throws NoSuchElementException when the service is unavailable
+     * @hide
+     */
+    @SystemApi
     public static native final IHwBinder getService(
             String iface,
             String serviceName,
@@ -71,6 +109,9 @@
      * Configures how many threads the process-wide hwbinder threadpool
      * has to process incoming requests.
      *
+     * @param maxThreads total number of threads to create (includes this thread if
+     *     callerWillJoin is true)
+     * @param callerWillJoin whether joinRpcThreadpool will be called in advance
      * @hide
      */
     @SystemApi
@@ -108,7 +149,25 @@
     private static native void native_report_sysprop_change();
 
     /**
+     * Enable instrumentation if available.
+     *
+     * On a non-user build, this method:
+     * - tries to enable atracing (if enabled)
+     * - tries to enable coverage dumps (if running in VTS)
+     * - tries to enable record and replay (if running in VTS)
+     *
+     * @hide
+     */
+    @SystemApi
+    public static void enableInstrumentation() {
+        native_report_sysprop_change();
+    }
+
+    /**
      * Notifies listeners that a system property has changed
+     *
+     * TODO(b/72480743): remove this method
+     *
      * @hide
      */
     public static void reportSyspropChanged() {
diff --git a/android/os/IHwBinder.java b/android/os/IHwBinder.java
index ce9f6c1..fbdf27e 100644
--- a/android/os/IHwBinder.java
+++ b/android/os/IHwBinder.java
@@ -21,18 +21,28 @@
 /** @hide */
 @SystemApi
 public interface IHwBinder {
-    // These MUST match their corresponding libhwbinder/IBinder.h definition !!!
-    /** @hide */
-    public static final int FIRST_CALL_TRANSACTION = 1;
-    /** @hide */
-    public static final int FLAG_ONEWAY = 1;
-
-    /** @hide */
+    /**
+     * Process a hwbinder transaction.
+     *
+     * @param code interface specific code for interface.
+     * @param request parceled transaction
+     * @param reply object to parcel reply into
+     * @param flags transaction flags to be chosen by wire protocol
+     *
+     * @hide
+     */
+    @SystemApi
     public void transact(
             int code, HwParcel request, HwParcel reply, int flags)
         throws RemoteException;
 
-    /** @hide */
+    /**
+     * Return as IHwInterface instance only if this implements descriptor.
+     *
+     * @param descriptor for example [email protected]::IBaz
+     * @hide
+     */
+    @SystemApi
     public IHwInterface queryLocalInterface(String descriptor);
 
     /**
@@ -43,6 +53,8 @@
     public interface DeathRecipient {
         /**
          * Callback for a registered process dying.
+         *
+         * @param cookie cookie this death recipient was registered with.
          */
         @SystemApi
         public void serviceDied(long cookie);
@@ -51,11 +63,16 @@
     /**
      * Notifies the death recipient with the cookie when the process containing
      * this binder dies.
+     *
+     * @param recipient callback object to be called on object death.
+     * @param cookie value to be given to callback on object death.
      */
     @SystemApi
     public boolean linkToDeath(DeathRecipient recipient, long cookie);
     /**
      * Unregisters the death recipient from this binder.
+     *
+     * @param recipient callback to no longer recieve death notifications on this binder.
      */
     @SystemApi
     public boolean unlinkToDeath(DeathRecipient recipient);
diff --git a/android/os/IHwInterface.java b/android/os/IHwInterface.java
index a2f59a9..1d9e2b0 100644
--- a/android/os/IHwInterface.java
+++ b/android/os/IHwInterface.java
@@ -21,7 +21,7 @@
 @SystemApi
 public interface IHwInterface {
     /**
-     * Returns the binder object that corresponds to an interface.
+     * @return the binder object that corresponds to this interface.
      */
     @SystemApi
     public IHwBinder asBinder();
diff --git a/android/os/IServiceManager.java b/android/os/IServiceManager.java
index 2176a78..89bf7b9 100644
--- a/android/os/IServiceManager.java
+++ b/android/os/IServiceManager.java
@@ -76,9 +76,15 @@
     int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0;
     int DUMP_FLAG_PRIORITY_HIGH = 1 << 1;
     int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2;
+    /**
+     * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the
+     * same priority as NORMAL priority but the services are not called with dump priority
+     * arguments.
+     */
+    int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3;
     int DUMP_FLAG_PRIORITY_ALL = DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
-            | DUMP_FLAG_PRIORITY_NORMAL;
+            | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT;
     /* Allows services to dump sections in protobuf format. */
-    int DUMP_FLAG_PROTO = 1 << 3;
+    int DUMP_FLAG_PROTO = 1 << 4;
 
 }
diff --git a/android/os/IncidentManager.java b/android/os/IncidentManager.java
index 1336c66..0e6652d 100644
--- a/android/os/IncidentManager.java
+++ b/android/os/IncidentManager.java
@@ -21,7 +21,6 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
-import android.provider.Settings;
 import android.util.Slog;
 
 /**
@@ -33,10 +32,12 @@
 @TestApi
 @SystemService(Context.INCIDENT_SERVICE)
 public class IncidentManager {
-    private static final String TAG = "incident";
+    private static final String TAG = "IncidentManager";
 
     private final Context mContext;
 
+    private IIncidentManager mService;
+
     /**
      * @hide
      */
@@ -55,60 +56,45 @@
         reportIncidentInternal(args);
     }
 
-    /**
-     * Convenience method to trigger an incident report and put it in dropbox.
-     * <p>
-     * The fields that are reported will be looked up in the system setting named by
-     * the settingName parameter.  The setting must match one of these patterns:
-     *      The string "disabled": The report will not be taken.
-     *      The string "all": The report will taken with all sections.
-     *      The string "none": The report will taken with no sections, but with the header.
-     *      A comma separated list of field numbers: The report will have these fields.
-     * <p>
-     * The header parameter will be added as a header for the incident report.  Fill in a
-     * {@link android.util.proto.ProtoOutputStream ProtoOutputStream}, and then call the
-     * {@link android.util.proto.ProtoOutputStream#bytes bytes()} method to retrieve
-     * the encoded data for the header.
-     */
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.DUMP,
-            android.Manifest.permission.PACKAGE_USAGE_STATS
-    })
-    public void reportIncident(String settingName, byte[] headerProto) {
-        // Sections
-        String setting = Settings.Global.getString(mContext.getContentResolver(), settingName);
-        IncidentReportArgs args;
-        try {
-            args = IncidentReportArgs.parseSetting(setting);
-        } catch (IllegalArgumentException ex) {
-            Slog.w(TAG, "Bad value for incident report setting '" + settingName + "'", ex);
-            return;
+    private class IncidentdDeathRecipient implements IBinder.DeathRecipient {
+        @Override
+        public void binderDied() {
+            synchronized (this) {
+                mService = null;
+            }
         }
-        if (args == null) {
-            Slog.i(TAG, String.format("Incident report requested but disabled with "
-                    + "settings [name: %s, value: %s]", settingName, setting));
-            return;
-        }
-
-        args.addHeader(headerProto);
-
-        Slog.i(TAG, "Taking incident report: " + settingName);
-        reportIncidentInternal(args);
     }
 
     private void reportIncidentInternal(IncidentReportArgs args) {
-        final IIncidentManager service = IIncidentManager.Stub.asInterface(
-                ServiceManager.getService(Context.INCIDENT_SERVICE));
-        if (service == null) {
-            Slog.e(TAG, "reportIncident can't find incident binder service");
-            return;
-        }
-
         try {
+            final IIncidentManager service = getIIncidentManagerLocked();
+            if (service == null) {
+                Slog.e(TAG, "reportIncident can't find incident binder service");
+                return;
+            }
             service.reportIncident(args);
         } catch (RemoteException ex) {
             Slog.e(TAG, "reportIncident failed", ex);
         }
     }
+
+    private IIncidentManager getIIncidentManagerLocked() throws RemoteException {
+        if (mService != null) {
+            return mService;
+        }
+
+        synchronized (this) {
+            if (mService != null) {
+                return mService;
+            }
+            mService = IIncidentManager.Stub.asInterface(
+                ServiceManager.getService(Context.INCIDENT_SERVICE));
+            if (mService != null) {
+                mService.asBinder().linkToDeath(new IncidentdDeathRecipient(), 0);
+            }
+            return mService;
+        }
+    }
+
 }
 
diff --git a/android/os/IncidentReportArgs.java b/android/os/IncidentReportArgs.java
index fd0ebcf..1aeac5f 100644
--- a/android/os/IncidentReportArgs.java
+++ b/android/os/IncidentReportArgs.java
@@ -32,6 +32,9 @@
 @TestApi
 public final class IncidentReportArgs implements Parcelable {
 
+    private static final int DEST_EXPLICIT = 100;
+    private static final int DEST_AUTO = 200;
+
     private final IntArray mSections = new IntArray();
     private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
     private boolean mAll;
@@ -41,6 +44,7 @@
      * Construct an incident report args with no fields.
      */
     public IncidentReportArgs() {
+        mDest = DEST_AUTO;
     }
 
     /**
@@ -143,7 +147,14 @@
      * @hide
      */
     public void setPrivacyPolicy(int dest) {
-        mDest = dest;
+        switch (dest) {
+            case DEST_EXPLICIT:
+            case DEST_AUTO:
+                mDest = dest;
+                break;
+            default:
+                mDest = DEST_AUTO;
+        }
     }
 
     /**
@@ -177,53 +188,5 @@
     public void addHeader(byte[] header) {
         mHeaders.add(header);
     }
-
-    /**
-     * Parses an incident report config as described in the system setting.
-     *
-     * @see IncidentManager#reportIncident
-     */
-    public static IncidentReportArgs parseSetting(String setting)
-            throws IllegalArgumentException {
-        if (setting == null || setting.length() == 0) {
-            return null;
-        }
-        setting = setting.trim();
-        if (setting.length() == 0 || "disabled".equals(setting)) {
-            return null;
-        }
-
-        final IncidentReportArgs args = new IncidentReportArgs();
-
-        if ("all".equals(setting)) {
-            args.setAll(true);
-            return args;
-        } else if ("none".equals(setting)) {
-            return args;
-        }
-
-        final String[] splits = setting.split(",");
-        final int N = splits.length;
-        for (int i=0; i<N; i++) {
-            final String str = splits[i].trim();
-            if (str.length() == 0) {
-                continue;
-            }
-            int section;
-            try {
-                section = Integer.parseInt(str);
-            } catch (NumberFormatException ex) {
-                throw new IllegalArgumentException("Malformed setting. Bad integer at section"
-                        + " index " + i + ": section='" + str + "' setting='" + setting + "'");
-            }
-            if (section < 1) {
-                throw new IllegalArgumentException("Malformed setting. Illegal section at"
-                        + " index " + i + ": section='" + str + "' setting='" + setting + "'");
-            }
-            args.addSection(section);
-        }
-
-        return args;
-    }
 }
 
diff --git a/android/os/LocaleList.java b/android/os/LocaleList.java
index ca9cbec..87e1b7d 100644
--- a/android/os/LocaleList.java
+++ b/android/os/LocaleList.java
@@ -20,7 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.content.LocaleProto;
 import android.icu.util.ULocale;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -140,6 +142,25 @@
     }
 
     /**
+     * Helper to write LocaleList to a protocol buffer output stream.  Assumes the parent
+     * protobuf has declared the locale as repeated.
+     *
+     * @param protoOutputStream Stream to write the locale to.
+     * @param fieldId Field Id of the Locale as defined in the parent message.
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        for (int i = 0; i < mList.length; i++) {
+            final Locale locale = mList[i];
+            final long token = protoOutputStream.start(fieldId);
+            protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
+            protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
+            protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
+            protoOutputStream.end(token);
+        }
+    }
+
+    /**
      * Retrieves a String representation of the language tags in this list.
      */
     @NonNull
diff --git a/android/os/Looper.java b/android/os/Looper.java
index 04cceb8..848c596 100644
--- a/android/os/Looper.java
+++ b/android/os/Looper.java
@@ -331,7 +331,6 @@
         final long looperToken = proto.start(fieldId);
         proto.write(LooperProto.THREAD_NAME, mThread.getName());
         proto.write(LooperProto.THREAD_ID, mThread.getId());
-        proto.write(LooperProto.IDENTITY_HASH_CODE, System.identityHashCode(this));
         mQueue.writeToProto(proto, LooperProto.QUEUE);
         proto.end(looperToken);
     }
diff --git a/android/os/Parcel.java b/android/os/Parcel.java
index 62bb385..e3c4870 100644
--- a/android/os/Parcel.java
+++ b/android/os/Parcel.java
@@ -2803,8 +2803,8 @@
                     Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                             parcelableClassLoader);
                     if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
-                        throw new BadParcelableException("Parcelable protocol requires that the "
-                                + "class implements Parcelable");
+                        throw new BadParcelableException("Parcelable protocol requires subclassing "
+                                + "from Parcelable on class " + name);
                     }
                     Field f = parcelableClass.getField("CREATOR");
                     if ((f.getModifiers() & Modifier.STATIC) == 0) {
diff --git a/android/os/PowerManager.java b/android/os/PowerManager.java
index 3d17ffb..c00100b 100644
--- a/android/os/PowerManager.java
+++ b/android/os/PowerManager.java
@@ -110,7 +110,7 @@
     /* NOTE: Wake lock levels were previously defined as a bit field, except that only a few
      * combinations were actually supported so the bit field was removed.  This explains
      * why the numbering scheme is so odd.  If adding a new wake lock level, any unused
-     * value can be used.
+     * value (in frameworks/base/core/proto/android/os/enums.proto) can be used.
      */
 
     /**
@@ -121,7 +121,7 @@
      * but the CPU will be kept on until all partial wake locks have been released.
      * </p>
      */
-    public static final int PARTIAL_WAKE_LOCK = 0x00000001;
+    public static final int PARTIAL_WAKE_LOCK = OsProtoEnums.PARTIAL_WAKE_LOCK; // 0x00000001
 
     /**
      * Wake lock level: Ensures that the screen is on (but may be dimmed);
@@ -138,7 +138,7 @@
      * as the user moves between applications and doesn't require a special permission.
      */
     @Deprecated
-    public static final int SCREEN_DIM_WAKE_LOCK = 0x00000006;
+    public static final int SCREEN_DIM_WAKE_LOCK = OsProtoEnums.SCREEN_DIM_WAKE_LOCK; // 0x00000006
 
     /**
      * Wake lock level: Ensures that the screen is on at full brightness;
@@ -155,7 +155,8 @@
      * as the user moves between applications and doesn't require a special permission.
      */
     @Deprecated
-    public static final int SCREEN_BRIGHT_WAKE_LOCK = 0x0000000a;
+    public static final int SCREEN_BRIGHT_WAKE_LOCK =
+            OsProtoEnums.SCREEN_BRIGHT_WAKE_LOCK; // 0x0000000a
 
     /**
      * Wake lock level: Ensures that the screen and keyboard backlight are on at
@@ -172,7 +173,7 @@
      * as the user moves between applications and doesn't require a special permission.
      */
     @Deprecated
-    public static final int FULL_WAKE_LOCK = 0x0000001a;
+    public static final int FULL_WAKE_LOCK = OsProtoEnums.FULL_WAKE_LOCK; // 0x0000001a
 
     /**
      * Wake lock level: Turns the screen off when the proximity sensor activates.
@@ -193,7 +194,8 @@
      * Cannot be used with {@link #ACQUIRE_CAUSES_WAKEUP}.
      * </p>
      */
-    public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020;
+    public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK =
+            OsProtoEnums.PROXIMITY_SCREEN_OFF_WAKE_LOCK; // 0x00000020
 
     /**
      * Wake lock level: Put the screen in a low power state and allow the CPU to suspend
@@ -207,7 +209,7 @@
      *
      * {@hide}
      */
-    public static final int DOZE_WAKE_LOCK = 0x00000040;
+    public static final int DOZE_WAKE_LOCK = OsProtoEnums.DOZE_WAKE_LOCK; // 0x00000040
 
     /**
      * Wake lock level: Keep the device awake enough to allow drawing to occur.
@@ -221,7 +223,7 @@
      *
      * {@hide}
      */
-    public static final int DRAW_WAKE_LOCK = 0x00000080;
+    public static final int DRAW_WAKE_LOCK = OsProtoEnums.DRAW_WAKE_LOCK; // 0x00000080
 
     /**
      * Mask for the wake lock level component of a combined wake lock level and flags integer.
@@ -536,6 +538,7 @@
             ServiceType.DATA_SAVER,
             ServiceType.FORCE_ALL_APPS_STANDBY,
             ServiceType.OPTIONAL_SENSORS,
+            ServiceType.AOD,
     })
     public @interface ServiceType {
         int NULL = 0;
@@ -549,6 +552,7 @@
         int SOUND = 8;
         int BATTERY_STATS = 9;
         int DATA_SAVER = 10;
+        int AOD = 14;
 
         /**
          * Whether to enable force-app-standby on all apps or not.
@@ -1276,6 +1280,19 @@
     }
 
     /**
+     * If true, the doze component is not started until after the screen has been
+     * turned off and the screen off animation has been performed.
+     * @hide
+     */
+    public void setDozeAfterScreenOff(boolean dozeAfterScreenOf) {
+        try {
+            mService.setDozeAfterScreenOff(dozeAfterScreenOf);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the reason the phone was last shutdown. Calling app must have the
      * {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
      * @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
@@ -1639,12 +1656,12 @@
         public void writeToProto(ProtoOutputStream proto, long fieldId) {
             synchronized (mToken) {
                 final long token = proto.start(fieldId);
-                proto.write(PowerManagerProto.WakeLockProto.HEX_STRING,
-                        Integer.toHexString(System.identityHashCode(this)));
-                proto.write(PowerManagerProto.WakeLockProto.HELD, mHeld);
-                proto.write(PowerManagerProto.WakeLockProto.INTERNAL_COUNT, mInternalCount);
+                proto.write(PowerManagerProto.WakeLock.TAG, mTag);
+                proto.write(PowerManagerProto.WakeLock.PACKAGE_NAME, mPackageName);
+                proto.write(PowerManagerProto.WakeLock.HELD, mHeld);
+                proto.write(PowerManagerProto.WakeLock.INTERNAL_COUNT, mInternalCount);
                 if (mWorkSource != null) {
-                    mWorkSource.writeToProto(proto, PowerManagerProto.WakeLockProto.WORK_SOURCE);
+                    mWorkSource.writeToProto(proto, PowerManagerProto.WakeLock.WORK_SOURCE);
                 }
                 proto.end(token);
             }
diff --git a/android/os/PowerManagerInternal.java b/android/os/PowerManagerInternal.java
index c7d89b0..2cb5aee 100644
--- a/android/os/PowerManagerInternal.java
+++ b/android/os/PowerManagerInternal.java
@@ -141,6 +141,14 @@
     public abstract void setDozeOverrideFromDreamManager(
             int screenState, int screenBrightness);
 
+    /**
+     * Used by sidekick manager to tell the power manager if it shouldn't change the display state
+     * when a draw wake lock is acquired. Some processes may grab such a wake lock to do some work
+     * in a powered-up state, but we shouldn't give up sidekick control over the display until this
+     * override is lifted.
+     */
+    public abstract void setDrawWakeLockOverrideFromSidekick(boolean keepState);
+
     public abstract PowerSaveState getLowPowerState(int serviceType);
 
     public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
diff --git a/android/os/Process.java b/android/os/Process.java
index 6833908..21c1263 100644
--- a/android/os/Process.java
+++ b/android/os/Process.java
@@ -157,6 +157,12 @@
      */
     public static final int INCIDENTD_UID = 1067;
 
+    /**
+     * Defines the UID/GID for the Secure Element service process.
+     * @hide
+     */
+    public static final int SE_UID = 1068;
+
     /** {@hide} */
     public static final int NOBODY_UID = 9999;
 
@@ -593,7 +599,6 @@
 
     /**
      * Returns whether the current process is in an isolated sandbox.
-     * @hide
      */
     public static final boolean isIsolated() {
         return isIsolated(myUid());
diff --git a/android/os/SimpleClock.java b/android/os/SimpleClock.java
new file mode 100644
index 0000000..efc271f
--- /dev/null
+++ b/android/os/SimpleClock.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+
+/** {@hide} */
+public abstract class SimpleClock extends Clock {
+    private final ZoneId zone;
+
+    public SimpleClock(ZoneId zone) {
+        this.zone = zone;
+    }
+
+    @Override
+    public ZoneId getZone() {
+        return zone;
+    }
+
+    @Override
+    public Clock withZone(ZoneId zone) {
+        return new SimpleClock(zone) {
+            @Override
+            public long millis() {
+                return SimpleClock.this.millis();
+            }
+        };
+    }
+
+    @Override
+    public abstract long millis();
+
+    @Override
+    public Instant instant() {
+        return Instant.ofEpochMilli(millis());
+    }
+}
diff --git a/android/os/StatsLogEventWrapper.java b/android/os/StatsLogEventWrapper.java
index 3e8161f..d4d3dc8 100644
--- a/android/os/StatsLogEventWrapper.java
+++ b/android/os/StatsLogEventWrapper.java
@@ -46,16 +46,17 @@
      * @param tag    The integer representing the tag for this event.
      * @param fields The number of fields specified in this event.
      */
-    public StatsLogEventWrapper(int tag, int fields) {
+    public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
         // Write four bytes from tag, starting with least-significant bit.
         // For pulled data, this tag number is not really used. We use the same tag number as
         // pushed ones to be consistent.
         write4Bytes(STATS_BUFFER_TAG_ID);
         mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
-        mStorage.write(fields + 1); // Indicate number of elements in this list. +1 for the tag
-        mStorage.write(EVENT_TYPE_INT);
-        // The first element is the real atom tag number
-        write4Bytes(tag);
+        mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
+        // The first element is the elapsed realtime.
+        writeLong(elapsedNanos);
+        // The second element is the real atom tag number
+        writeInt(tag);
     }
 
     /**
diff --git a/android/os/StrictMode.java b/android/os/StrictMode.java
index f90604a..a93e25a 100644
--- a/android/os/StrictMode.java
+++ b/android/os/StrictMode.java
@@ -479,6 +479,8 @@
             /** Initialize a Builder from an existing ThreadPolicy. */
             public Builder(ThreadPolicy policy) {
                 mMask = policy.mask;
+                mListener = policy.mListener;
+                mExecutor = policy.mCallbackExecutor;
             }
 
             /**
@@ -636,7 +638,7 @@
              * executor every violation.
              */
             public Builder penaltyListener(
-                    @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+                    @NonNull Executor executor, @NonNull OnThreadViolationListener listener) {
                 if (executor == null) {
                     throw new NullPointerException("executor must not be null");
                 }
@@ -645,6 +647,12 @@
                 return this;
             }
 
+            /** @removed */
+            public Builder penaltyListener(
+                    @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+                return penaltyListener(executor, listener);
+            }
+
             private Builder enable(int bit) {
                 mMask |= bit;
                 return this;
@@ -971,7 +979,7 @@
              * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
              */
             public Builder penaltyListener(
-                    @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+                    @NonNull Executor executor, @NonNull OnVmViolationListener listener) {
                 if (executor == null) {
                     throw new NullPointerException("executor must not be null");
                 }
@@ -980,6 +988,12 @@
                 return this;
             }
 
+            /** @removed */
+            public Builder penaltyListener(
+                    @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+                return penaltyListener(executor, listener);
+            }
+
             private Builder enable(int bit) {
                 mMask |= bit;
                 return this;
diff --git a/android/os/SystemClock.java b/android/os/SystemClock.java
index c52c22d..b254166 100644
--- a/android/os/SystemClock.java
+++ b/android/os/SystemClock.java
@@ -24,8 +24,7 @@
 import dalvik.annotation.optimization.CriticalNative;
 
 import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
+import java.time.DateTimeException;
 import java.time.ZoneOffset;
 
 /**
@@ -148,8 +147,8 @@
      * @return if the clock was successfully set to the specified time.
      */
     public static boolean setCurrentTimeMillis(long millis) {
-        IBinder b = ServiceManager.getService(Context.ALARM_SERVICE);
-        IAlarmManager mgr = IAlarmManager.Stub.asInterface(b);
+        final IAlarmManager mgr = IAlarmManager.Stub
+                .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
         if (mgr == null) {
             return false;
         }
@@ -174,27 +173,25 @@
     native public static long uptimeMillis();
 
     /**
+     * @removed
+     */
+    @Deprecated
+    public static @NonNull Clock uptimeMillisClock() {
+        return uptimeClock();
+    }
+
+    /**
      * Return {@link Clock} that starts at system boot, not counting time spent
      * in deep sleep.
+     *
+     * @removed
      */
-    public static @NonNull Clock uptimeMillisClock() {
-        return new Clock() {
-            @Override
-            public ZoneId getZone() {
-                return ZoneOffset.UTC;
-            }
-            @Override
-            public Clock withZone(ZoneId zone) {
-                throw new UnsupportedOperationException();
-            }
+    public static @NonNull Clock uptimeClock() {
+        return new SimpleClock(ZoneOffset.UTC) {
             @Override
             public long millis() {
                 return SystemClock.uptimeMillis();
             }
-            @Override
-            public Instant instant() {
-                return Instant.ofEpochMilli(millis());
-            }
         };
     }
 
@@ -209,25 +206,15 @@
     /**
      * Return {@link Clock} that starts at system boot, including time spent in
      * sleep.
+     *
+     * @removed
      */
     public static @NonNull Clock elapsedRealtimeClock() {
-        return new Clock() {
-            @Override
-            public ZoneId getZone() {
-                return ZoneOffset.UTC;
-            }
-            @Override
-            public Clock withZone(ZoneId zone) {
-                throw new UnsupportedOperationException();
-            }
+        return new SimpleClock(ZoneOffset.UTC) {
             @Override
             public long millis() {
                 return SystemClock.elapsedRealtime();
             }
-            @Override
-            public Instant instant() {
-                return Instant.ofEpochMilli(millis());
-            }
         };
     }
 
@@ -266,4 +253,64 @@
      */
     @CriticalNative
     public static native long currentTimeMicro();
+
+    /**
+     * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
+     * using a remote network source outside the device.
+     * <p>
+     * While the time returned by {@link System#currentTimeMillis()} can be
+     * adjusted by the user, the time returned by this method cannot be adjusted
+     * by the user. Note that synchronization may occur using an insecure
+     * network protocol, so the returned time should not be used for security
+     * purposes.
+     * <p>
+     * This performs no blocking network operations and returns values based on
+     * a recent successful synchronization event; it will either return a valid
+     * time or throw.
+     *
+     * @throws DateTimeException when no accurate network time can be provided.
+     * @hide
+     */
+    public static long currentNetworkTimeMillis() {
+        final IAlarmManager mgr = IAlarmManager.Stub
+                .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+        if (mgr != null) {
+            try {
+                return mgr.currentNetworkTimeMillis();
+            } catch (ParcelableException e) {
+                e.maybeRethrow(DateTimeException.class);
+                throw new RuntimeException(e);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            throw new RuntimeException(new DeadSystemException());
+        }
+    }
+
+    /**
+     * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
+     * synchronized using a remote network source outside the device.
+     * <p>
+     * While the time returned by {@link System#currentTimeMillis()} can be
+     * adjusted by the user, the time returned by this method cannot be adjusted
+     * by the user. Note that synchronization may occur using an insecure
+     * network protocol, so the returned time should not be used for security
+     * purposes.
+     * <p>
+     * This performs no blocking network operations and returns values based on
+     * a recent successful synchronization event; it will either return a valid
+     * time or throw.
+     *
+     * @throws DateTimeException when no accurate network time can be provided.
+     * @hide
+     */
+    public static @NonNull Clock currentNetworkTimeClock() {
+        return new SimpleClock(ZoneOffset.UTC) {
+            @Override
+            public long millis() {
+                return SystemClock.currentNetworkTimeMillis();
+            }
+        };
+    }
 }
diff --git a/android/os/SystemProperties.java b/android/os/SystemProperties.java
index a9b8675..8eb39c0 100644
--- a/android/os/SystemProperties.java
+++ b/android/os/SystemProperties.java
@@ -205,7 +205,12 @@
             }
             ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
             for (int i=0; i<callbacks.size(); i++) {
-                callbacks.get(i).run();
+                try {
+                    callbacks.get(i).run();
+                } catch (Throwable t) {
+                    Log.wtf(TAG, "Exception in SystemProperties change callback", t);
+                    // Ignore and try to go on.
+                }
             }
         }
     }
diff --git a/android/os/Trace.java b/android/os/Trace.java
index fa96dd3..30451c2 100644
--- a/android/os/Trace.java
+++ b/android/os/Trace.java
@@ -89,6 +89,10 @@
     public static final long TRACE_TAG_NETWORK = 1L << 21;
     /** @hide */
     public static final long TRACE_TAG_ADB = 1L << 22;
+    /** @hide */
+    public static final long TRACE_TAG_VIBRATOR = 1L << 23;
+    /** @hide */
+    public static final long TRACE_TAG_AIDL = 1L << 24;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/android/os/UpdateEngine.java b/android/os/UpdateEngine.java
index c6149be..24c9c91 100644
--- a/android/os/UpdateEngine.java
+++ b/android/os/UpdateEngine.java
@@ -271,4 +271,22 @@
             }
         }
     }
+
+    /**
+     * Verifies that a payload associated with the given payload metadata
+     * {@code payloadMetadataFilename} can be safely applied to ths device.
+     * Returns {@code true} if the update can successfully be applied and
+     * returns {@code false} otherwise.
+     *
+     * @param payloadMetadataFilename the location of the metadata without the
+     * {@code file://} prefix.
+     */
+    @SystemApi
+    public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
+        try {
+            return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/android/os/UserHandle.java b/android/os/UserHandle.java
index 5be72bc..094f004 100644
--- a/android/os/UserHandle.java
+++ b/android/os/UserHandle.java
@@ -158,7 +158,7 @@
      * @hide
      */
     public static boolean isCore(int uid) {
-        if (uid > 0) {
+        if (uid >= 0) {
             final int appId = getAppId(uid);
             return appId < Process.FIRST_APPLICATION_UID;
         } else {
diff --git a/android/os/UserManager.java b/android/os/UserManager.java
index 13b5b5c..a9eb360 100644
--- a/android/os/UserManager.java
+++ b/android/os/UserManager.java
@@ -36,6 +36,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -393,8 +394,9 @@
     public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
 
     /**
-     * Specifies if a user is disallowed from configuring location mode. Device owner and profile
-     * owners can set this restriction and it only applies on the managed user.
+     * Specifies if a user is disallowed from enabling or disabling location providers. As a
+     * result, user is disallowed from turning on or off location. Device owner and profile owners
+     * can set this restriction and it only applies on the managed user.
      *
      * <p>In a managed profile, location sharing is forced off when it's off on primary user, so
      * user can still turn off location sharing on managed profile when the restriction is set by
@@ -408,11 +410,12 @@
      *
      * <p>Key for user restrictions.
      * <p>Type: Boolean
+     * @see android.location.LocationManager#isProviderEnabled(String)
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
      * @see #getUserRestrictions()
      */
-    public static final String DISALLOW_CONFIG_LOCATION_MODE = "no_config_location_mode";
+    public static final String DISALLOW_CONFIG_LOCATION = "no_config_location";
 
     /**
      * Specifies if date, time and timezone configuring is disallowed.
@@ -689,14 +692,13 @@
     /**
      * Specifies that system error dialogs for crashed or unresponsive apps should not be shown.
      * In this case, the system will force-stop the app as if the user chooses the "close app"
-     * option on the UI. No feedback report will be collected as there is no way for the user to
-     * provide explicit consent.
+     * option on the UI. A feedback report isn't collected as there is no way for the user to
+     * provide explicit consent. The default value is <code>false</code>.
      *
-     * When this user restriction is set by device owners, it's applied to all users; when it's set
-     * by profile owners, it's only applied to the relevant profiles.
-     * The default value is <code>false</code>.
+     * <p>When this user restriction is set by device owners, it's applied to all users. When set by
+     * the profile owner of the primary user or a secondary user, the restriction affects only the
+     * calling user. This user restriction has no effect on managed profiles.
      *
-     * <p>This user restriction has no effect on managed profiles.
      * <p>Key for user restrictions.
      * <p>Type: Boolean
      * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
@@ -911,6 +913,9 @@
      * Specifies if user switching is blocked on the current user.
      *
      * <p> This restriction can only be set by the device owner, it will be applied to all users.
+     * Device owner can still switch user via
+     * {@link DevicePolicyManager#switchUser(ComponentName, UserHandle)} when this restriction is
+     * set.
      *
      * <p>The default value is <code>false</code>.
      *
@@ -941,6 +946,20 @@
      * @see #getUserRestrictions()
      */
     public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
+
+    /**
+     * Specifies whether the user is allowed to print.
+     *
+     * This restriction can be set by device or profile owner.
+     *
+     * The default value is {@code false}.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_PRINTING = "no_printing";
+
     /**
      * Application restriction key that is used to indicate the pending arrival
      * of real restrictions for the app.
@@ -1023,6 +1042,85 @@
      */
     public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1;
 
+    /**
+     * Indicates user operation is successful.
+     */
+    public static final int USER_OPERATION_SUCCESS = 0;
+
+    /**
+     * Indicates user operation failed for unknown reason.
+     */
+    public static final int USER_OPERATION_ERROR_UNKNOWN = 1;
+
+    /**
+     * Indicates user operation failed because target user is a managed profile.
+     */
+    public static final int USER_OPERATION_ERROR_MANAGED_PROFILE = 2;
+
+    /**
+     * Indicates user operation failed because maximum running user limit has been reached.
+     */
+    public static final int USER_OPERATION_ERROR_MAX_RUNNING_USERS = 3;
+
+    /**
+     * Indicates user operation failed because the target user is in the foreground.
+     */
+    public static final int USER_OPERATION_ERROR_CURRENT_USER = 4;
+
+    /**
+     * Indicates user operation failed because device has low data storage.
+     */
+    public static final int USER_OPERATION_ERROR_LOW_STORAGE = 5;
+
+    /**
+     * Indicates user operation failed because maximum user limit has been reached.
+     */
+    public static final int USER_OPERATION_ERROR_MAX_USERS = 6;
+
+    /**
+     * Result returned from various user operations.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "USER_OPERATION_" }, value = {
+            USER_OPERATION_SUCCESS,
+            USER_OPERATION_ERROR_UNKNOWN,
+            USER_OPERATION_ERROR_MANAGED_PROFILE,
+            USER_OPERATION_ERROR_MAX_RUNNING_USERS,
+            USER_OPERATION_ERROR_CURRENT_USER,
+            USER_OPERATION_ERROR_LOW_STORAGE,
+            USER_OPERATION_ERROR_MAX_USERS
+    })
+    public @interface UserOperationResult {}
+
+    /**
+     * Thrown to indicate user operation failed.
+     */
+    public static class UserOperationException extends RuntimeException {
+        private final @UserOperationResult int mUserOperationResult;
+
+        /**
+         * Constructs a UserOperationException with specific result code.
+         *
+         * @param message the detail message
+         * @param userOperationResult the result code
+         * @hide
+         */
+        public UserOperationException(String message,
+                @UserOperationResult int userOperationResult) {
+            super(message);
+            mUserOperationResult = userOperationResult;
+        }
+
+        /**
+         * Returns the operation result code.
+         */
+        public @UserOperationResult int getUserOperationResult() {
+            return mUserOperationResult;
+        }
+    }
+
     /** @hide */
     public static UserManager get(Context context) {
         return (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -1662,6 +1760,18 @@
     }
 
     /**
+     * @hide
+     * Returns whether any user on the device has the given user restriction set.
+     */
+    public boolean hasUserRestrictionOnAnyUser(String restrictionKey) {
+        try {
+            return mService.hasUserRestrictionOnAnyUser(restrictionKey);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return the serial number for a user.  This is a device-unique
      * number assigned to that user; if the user is deleted and then a new
      * user created, the new users will not be given the same serial number.
@@ -2259,12 +2369,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    public boolean trySetQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle) {
-        return requestQuietModeEnabled(enableQuietMode, userHandle);
-    }
-
     /**
      * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
      * managed profile don't run, generate notifications, or consume data or battery.
@@ -2508,8 +2612,13 @@
     public static int getMaxSupportedUsers() {
         // Don't allow multiple users on certain builds
         if (android.os.Build.ID.startsWith("JVP")) return 1;
-        // Svelte devices don't get multi-user.
-        if (ActivityManager.isLowRamDeviceStatic()) return 1;
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // Low-ram devices are Svelte. Most of the time they don't get multi-user.
+            if ((Resources.getSystem().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+                    != Configuration.UI_MODE_TYPE_TELEVISION) {
+                return 1;
+            }
+        }
         return SystemProperties.getInt("fw.max_users",
                 Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));
     }
diff --git a/android/os/UserManagerInternal.java b/android/os/UserManagerInternal.java
index 6c9f1b2..1f6c3cc 100644
--- a/android/os/UserManagerInternal.java
+++ b/android/os/UserManagerInternal.java
@@ -16,6 +16,7 @@
 package android.os;
 
 import android.annotation.Nullable;
+import android.content.Context;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
 
@@ -186,4 +187,38 @@
      * @return the array of user ids.
      */
     public abstract int[] getUserIds();
+
+    /**
+     * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
+     * and that the {@code callingUserId} is not a managed profile and
+     * {@code targetUserId} is enabled.
+     *
+     * @return TRUE if the {@code callingUserId} can access {@code targetUserId}. FALSE
+     * otherwise
+     *
+     * @throws SecurityException if the calling user and {@code targetUser} are not in the same
+     * group and {@code throwSecurityException} is true, otherwise if will simply return false.
+     */
+    public abstract boolean isProfileAccessible(int callingUserId, int targetUserId,
+            String debugMsg, boolean throwSecurityException);
+
+    /**
+     * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return
+     * itself.
+     */
+    public abstract int getProfileParentId(int userId);
+
+    /**
+     * Checks whether changing a setting to a value is prohibited by the corresponding user
+     * restriction.
+     *
+     * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction(
+     * Context, int, String, boolean)}, which should be in sync with this method.
+     *
+     * @return {@code true} if the change is prohibited, {@code false} if the change is allowed.
+     *
+     * @hide
+     */
+    public abstract boolean isSettingRestrictedForUser(String setting, int userId, String value,
+            int callingUid);
 }
diff --git a/android/os/VibrationEffect.java b/android/os/VibrationEffect.java
index da0ed54..e9b4853 100644
--- a/android/os/VibrationEffect.java
+++ b/android/os/VibrationEffect.java
@@ -16,7 +16,14 @@
 
 package android.os;
 
-import android.hardware.vibrator.V1_1.Constants.Effect_1_1;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.vibrator.V1_0.EffectStrength;
+import android.hardware.vibrator.V1_2.Effect;
+import android.net.Uri;
+import android.util.MathUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Arrays;
 
@@ -36,12 +43,18 @@
     public static final int DEFAULT_AMPLITUDE = -1;
 
     /**
+     * The maximum amplitude value
+     * @hide
+     */
+    public static final int MAX_AMPLITUDE = 255;
+
+    /**
      * A click effect.
      *
      * @see #get(int)
      * @hide
      */
-    public static final int EFFECT_CLICK = Effect_1_1.CLICK;
+    public static final int EFFECT_CLICK = Effect.CLICK;
 
     /**
      * A double click effect.
@@ -49,14 +62,62 @@
      * @see #get(int)
      * @hide
      */
-    public static final int EFFECT_DOUBLE_CLICK = Effect_1_1.DOUBLE_CLICK;
+    public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
 
     /**
      * A tick effect.
      * @see #get(int)
      * @hide
      */
-    public static final int EFFECT_TICK = Effect_1_1.TICK;
+    public static final int EFFECT_TICK = Effect.TICK;
+
+    /**
+     * A thud effect.
+     * @see #get(int)
+     * @hide
+     */
+    public static final int EFFECT_THUD = Effect.THUD;
+
+    /**
+     * A pop effect.
+     * @see #get(int)
+     * @hide
+     */
+    public static final int EFFECT_POP = Effect.POP;
+
+    /**
+     * A heavy click effect.
+     * @see #get(int)
+     * @hide
+     */
+    public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
+
+
+    /**
+     * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
+     * pattern that can be played as a ringtone with any audio, depending on the device.
+     *
+     * @see #get(Uri, Context)
+     * @hide
+     */
+    @VisibleForTesting
+    public static final int[] RINGTONES = {
+        Effect.RINGTONE_1,
+        Effect.RINGTONE_2,
+        Effect.RINGTONE_3,
+        Effect.RINGTONE_4,
+        Effect.RINGTONE_5,
+        Effect.RINGTONE_6,
+        Effect.RINGTONE_7,
+        Effect.RINGTONE_8,
+        Effect.RINGTONE_9,
+        Effect.RINGTONE_10,
+        Effect.RINGTONE_11,
+        Effect.RINGTONE_12,
+        Effect.RINGTONE_13,
+        Effect.RINGTONE_14,
+        Effect.RINGTONE_15
+    };
 
     /** @hide to prevent subclassing from outside of the framework */
     public VibrationEffect() { }
@@ -190,6 +251,37 @@
         return effect;
     }
 
+    /**
+     * Get a predefined vibration effect associated with a given URI.
+     *
+     * Predefined effects are a set of common vibration effects that should be identical, regardless
+     * of the app they come from, in order to provide a cohesive experience for users across
+     * the entire device. They also may be custom tailored to the device hardware in order to
+     * provide a better experience than you could otherwise build using the generic building
+     * blocks.
+     *
+     * @param uri The URI associated with the haptic effect.
+     * @param context The context used to get the URI to haptic effect association.
+     *
+     * @return The desired effect, or {@code null} if there's no associated effect.
+     *
+     * @hide
+     */
+    @Nullable
+    public static VibrationEffect get(Uri uri, Context context) {
+        String[] uris = context.getResources().getStringArray(
+                com.android.internal.R.array.config_ringtoneEffectUris);
+        for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
+            if (uris[i] == null) {
+                continue;
+            }
+            if (Uri.parse(uris[i]).equals(uri)) {
+                return get(RINGTONES[i]);
+            }
+        }
+        return null;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -198,38 +290,75 @@
     /** @hide */
     public abstract void validate();
 
+    /**
+     * Gets the estimated duration of the vibration in milliseconds.
+     *
+     * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
+     * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
+     * the length is device and potentially run-time dependent), this returns -1.
+     *
+     * @hide
+     */
+    public abstract long getDuration();
+
+    /**
+     * Scale the amplitude with the given constraints.
+     *
+     * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
+     * @hide
+     */
+    protected static int scale(int amplitude, float gamma, int maxAmplitude) {
+        float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
+        return (int) (val * maxAmplitude);
+    }
+
     /** @hide */
     public static class OneShot extends VibrationEffect implements Parcelable {
-        private long mTiming;
-        private int mAmplitude;
+        private final long mDuration;
+        private final int mAmplitude;
 
         public OneShot(Parcel in) {
-            this(in.readLong(), in.readInt());
+            mDuration = in.readLong();
+            mAmplitude = in.readInt();
         }
 
         public OneShot(long milliseconds, int amplitude) {
-            mTiming = milliseconds;
+            mDuration = milliseconds;
             mAmplitude = amplitude;
         }
 
-        public long getTiming() {
-            return mTiming;
+        @Override
+        public long getDuration() {
+            return mDuration;
         }
 
         public int getAmplitude() {
             return mAmplitude;
         }
 
+        /**
+         * Scale the amplitude of this effect.
+         *
+         * @param gamma the gamma adjustment to apply
+         * @param maxAmplitude the new maximum amplitude of the effect
+         *
+         * @return A {@link OneShot} effect with the same timing but scaled amplitude.
+         */
+        public VibrationEffect scale(float gamma, int maxAmplitude) {
+            int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
+            return new OneShot(mDuration, newAmplitude);
+        }
+
         @Override
         public void validate() {
             if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
                 throw new IllegalArgumentException(
-                        "amplitude must either be DEFAULT_AMPLITUDE, " +
-                        "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
+                        "amplitude must either be DEFAULT_AMPLITUDE, "
+                        + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
             }
-            if (mTiming <= 0) {
+            if (mDuration <= 0) {
                 throw new IllegalArgumentException(
-                        "timing must be positive (timing=" + mTiming + ")");
+                        "duration must be positive (duration=" + mDuration + ")");
             }
         }
 
@@ -239,26 +368,26 @@
                 return false;
             }
             VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
-            return other.mTiming == mTiming && other.mAmplitude == mAmplitude;
+            return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
         }
 
         @Override
         public int hashCode() {
             int result = 17;
-            result = 37 * (int) mTiming;
-            result = 37 * mAmplitude;
+            result += 37 * (int) mDuration;
+            result += 37 * mAmplitude;
             return result;
         }
 
         @Override
         public String toString() {
-            return "OneShot{mTiming=" + mTiming +", mAmplitude=" + mAmplitude + "}";
+            return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             out.writeInt(PARCEL_TOKEN_ONE_SHOT);
-            out.writeLong(mTiming);
+            out.writeLong(mDuration);
             out.writeInt(mAmplitude);
         }
 
@@ -279,9 +408,9 @@
 
     /** @hide */
     public static class Waveform extends VibrationEffect implements Parcelable {
-        private long[] mTimings;
-        private int[] mAmplitudes;
-        private int mRepeat;
+        private final long[] mTimings;
+        private final int[] mAmplitudes;
+        private final int mRepeat;
 
         public Waveform(Parcel in) {
             this(in.createLongArray(), in.createIntArray(), in.readInt());
@@ -308,34 +437,68 @@
         }
 
         @Override
+        public long getDuration() {
+            if (mRepeat >= 0) {
+                return Long.MAX_VALUE;
+            }
+            long duration = 0;
+            for (long d : mTimings) {
+                duration += d;
+            }
+            return duration;
+        }
+
+        /**
+         * Scale the Waveform with the given gamma and new max amplitude.
+         *
+         * @param gamma the gamma adjustment to apply
+         * @param maxAmplitude the new maximum amplitude of the effect
+         *
+         * @return A {@link Waveform} effect with the same timings and repeat index
+         *         but scaled amplitude.
+         */
+        public VibrationEffect scale(float gamma, int maxAmplitude) {
+            if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
+                // Just return a copy of the original if there's no scaling to be done.
+                return new Waveform(mTimings, mAmplitudes, mRepeat);
+            }
+
+            int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
+            for (int i = 0; i < scaledAmplitudes.length; i++) {
+                scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
+            }
+            return new Waveform(mTimings, scaledAmplitudes, mRepeat);
+        }
+
+        @Override
         public void validate() {
             if (mTimings.length != mAmplitudes.length) {
                 throw new IllegalArgumentException(
-                        "timing and amplitude arrays must be of equal length" +
-                        " (timings.length=" + mTimings.length +
-                        ", amplitudes.length=" + mAmplitudes.length + ")");
+                        "timing and amplitude arrays must be of equal length"
+                        + " (timings.length=" + mTimings.length
+                        + ", amplitudes.length=" + mAmplitudes.length + ")");
             }
             if (!hasNonZeroEntry(mTimings)) {
-                throw new IllegalArgumentException("at least one timing must be non-zero" +
-                        " (timings=" + Arrays.toString(mTimings) + ")");
+                throw new IllegalArgumentException("at least one timing must be non-zero"
+                        + " (timings=" + Arrays.toString(mTimings) + ")");
             }
             for (long timing : mTimings) {
                 if (timing < 0) {
-                    throw new IllegalArgumentException("timings must all be >= 0" +
-                            " (timings=" + Arrays.toString(mTimings) + ")");
+                    throw new IllegalArgumentException("timings must all be >= 0"
+                            + " (timings=" + Arrays.toString(mTimings) + ")");
                 }
             }
             for (int amplitude : mAmplitudes) {
                 if (amplitude < -1 || amplitude > 255) {
                     throw new IllegalArgumentException(
-                            "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" +
-                            " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
+                            "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
+                            + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
                 }
             }
             if (mRepeat < -1 || mRepeat >= mTimings.length) {
                 throw new IllegalArgumentException(
-                        "repeat index must be within the bounds of the timings array" +
-                        " (timings.length=" + mTimings.length + ", index=" + mRepeat +")");
+                        "repeat index must be within the bounds of the timings array"
+                        + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
             }
         }
 
@@ -345,26 +508,26 @@
                 return false;
             }
             VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
-            return Arrays.equals(mTimings, other.mTimings) &&
-                Arrays.equals(mAmplitudes, other.mAmplitudes) &&
-                mRepeat == other.mRepeat;
+            return Arrays.equals(mTimings, other.mTimings)
+                && Arrays.equals(mAmplitudes, other.mAmplitudes)
+                && mRepeat == other.mRepeat;
         }
 
         @Override
         public int hashCode() {
             int result = 17;
-            result = 37 * Arrays.hashCode(mTimings);
-            result = 37 * Arrays.hashCode(mAmplitudes);
-            result = 37 * mRepeat;
+            result += 37 * Arrays.hashCode(mTimings);
+            result += 37 * Arrays.hashCode(mAmplitudes);
+            result += 37 * mRepeat;
             return result;
         }
 
         @Override
         public String toString() {
-            return "Waveform{mTimings=" + Arrays.toString(mTimings) +
-                ", mAmplitudes=" + Arrays.toString(mAmplitudes) +
-                ", mRepeat=" + mRepeat +
-                "}";
+            return "Waveform{mTimings=" + Arrays.toString(mTimings)
+                + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
+                + ", mRepeat=" + mRepeat
+                + "}";
         }
 
         @Override
@@ -402,16 +565,20 @@
 
     /** @hide */
     public static class Prebaked extends VibrationEffect implements Parcelable {
-        private int mEffectId;
-        private boolean mFallback;
+        private final int mEffectId;
+        private final boolean mFallback;
+
+        private int mEffectStrength;
 
         public Prebaked(Parcel in) {
             this(in.readInt(), in.readByte() != 0);
+            mEffectStrength = in.readInt();
         }
 
         public Prebaked(int effectId, boolean fallback) {
             mEffectId = effectId;
             mFallback = fallback;
+            mEffectStrength = EffectStrength.MEDIUM;
         }
 
         public int getId() {
@@ -427,15 +594,57 @@
         }
 
         @Override
+        public long getDuration() {
+            return -1;
+        }
+
+        /**
+         * Set the effect strength of the prebaked effect.
+         */
+        public void setEffectStrength(int strength) {
+            if (!isValidEffectStrength(strength)) {
+                throw new IllegalArgumentException("Invalid effect strength: " + strength);
+            }
+            mEffectStrength = strength;
+        }
+
+        /**
+         * Set the effect strength.
+         */
+        public int getEffectStrength() {
+            return mEffectStrength;
+        }
+
+        private static boolean isValidEffectStrength(int strength) {
+            switch (strength) {
+                case EffectStrength.LIGHT:
+                case EffectStrength.MEDIUM:
+                case EffectStrength.STRONG:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        @Override
         public void validate() {
             switch (mEffectId) {
                 case EFFECT_CLICK:
                 case EFFECT_DOUBLE_CLICK:
                 case EFFECT_TICK:
+                case EFFECT_THUD:
+                case EFFECT_POP:
+                case EFFECT_HEAVY_CLICK:
                     break;
                 default:
-                    throw new IllegalArgumentException(
-                            "Unknown prebaked effect type (value=" + mEffectId + ")");
+                    if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
+                        throw new IllegalArgumentException(
+                                "Unknown prebaked effect type (value=" + mEffectId + ")");
+                    }
+            }
+            if (!isValidEffectStrength(mEffectStrength)) {
+                throw new IllegalArgumentException(
+                        "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
             }
         }
 
@@ -445,17 +654,25 @@
                 return false;
             }
             VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
-            return mEffectId == other.mEffectId && mFallback == other.mFallback;
+            return mEffectId == other.mEffectId
+                && mFallback == other.mFallback
+                && mEffectStrength == other.mEffectStrength;
         }
 
         @Override
         public int hashCode() {
-            return mEffectId;
+            int result = 17;
+            result += 37 * mEffectId;
+            result += 37 * mEffectStrength;
+            return result;
         }
 
         @Override
         public String toString() {
-            return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
+            return "Prebaked{mEffectId=" + mEffectId
+                + ", mEffectStrength=" + mEffectStrength
+                + ", mFallback=" + mFallback
+                + "}";
         }
 
 
@@ -464,6 +681,7 @@
             out.writeInt(PARCEL_TOKEN_EFFECT);
             out.writeInt(mEffectId);
             out.writeByte((byte) (mFallback ? 1 : 0));
+            out.writeInt(mEffectStrength);
         }
 
         public static final Parcelable.Creator<Prebaked> CREATOR =
diff --git a/android/os/Vibrator.java b/android/os/Vibrator.java
index 8078fb8..f1f6f41 100644
--- a/android/os/Vibrator.java
+++ b/android/os/Vibrator.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.app.ActivityThread;
@@ -23,6 +24,9 @@
 import android.media.AudioAttributes;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Class that operates the vibrator on the device.
  * <p>
@@ -33,6 +37,40 @@
 public abstract class Vibrator {
     private static final String TAG = "Vibrator";
 
+    /**
+     * Vibration intensity: no vibrations.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_OFF = 0;
+
+    /**
+     * Vibration intensity: low.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_LOW = 1;
+
+    /**
+     * Vibration intensity: medium.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_MEDIUM = 2;
+
+    /**
+     * Vibration intensity: high.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_HIGH = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "VIBRATION_INTENSITY_" }, value = {
+        VIBRATION_INTENSITY_OFF,
+        VIBRATION_INTENSITY_LOW,
+        VIBRATION_INTENSITY_MEDIUM,
+        VIBRATION_INTENSITY_HIGH
+    })
+    public @interface VibrationIntensity{}
+
     private final String mPackageName;
 
     /**
@@ -50,6 +88,22 @@
     }
 
     /**
+     * Get the default vibration intensity for haptic feedback.
+     * @hide
+     */
+    public int getDefaultHapticFeedbackIntensity() {
+        return VIBRATION_INTENSITY_MEDIUM;
+    }
+
+    /**
+     * Get the default vibration intensity for notifications and ringtones.
+     * @hide
+     */
+    public int getDefaultNotificationVibrationIntensity() {
+        return VIBRATION_INTENSITY_HIGH;
+    }
+
+    /**
      * Check whether the hardware has a vibrator.
      *
      * @return True if the hardware has a vibrator, else false.
diff --git a/android/os/VintfObject.java b/android/os/VintfObject.java
index 12a495b..fb22194 100644
--- a/android/os/VintfObject.java
+++ b/android/os/VintfObject.java
@@ -80,4 +80,11 @@
      *  ("28", ["libjpeg.so", "libbase.so"])]
      */
     public static native Map<String, String[]> getVndkSnapshots();
+
+    /**
+     * @return target FCM version, a number specified in the device manifest
+     * indicating the FCM version that the device manifest implements. Null if
+     * device manifest doesn't specify this number (for legacy devices).
+     */
+    public static native Long getTargetFrameworkCompatibilityMatrixVersion();
 }
diff --git a/android/os/WorkSource.java b/android/os/WorkSource.java
index d0c2870..17d83db 100644
--- a/android/os/WorkSource.java
+++ b/android/os/WorkSource.java
@@ -1,10 +1,16 @@
 package android.os;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
 import android.os.WorkSourceProto;
+import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -109,6 +115,17 @@
         }
     }
 
+    /**
+     * Whether system services should create {@code WorkChains} (wherever possible) in the place
+     * of flat UID lists.
+     *
+     * @hide
+     */
+    public static boolean isChainedBatteryAttributionEnabled(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, 0) == 1;
+    }
+
     /** @hide */
     public int size() {
         return mNum;
@@ -450,6 +467,7 @@
      *
      * @hide
      */
+    @SystemApi
     public WorkChain createWorkChain() {
         if (mChains == null) {
             mChains = new ArrayList<>(4);
@@ -479,6 +497,29 @@
         return mChains;
     }
 
+    /**
+     * DO NOT USE: Hacky API provided solely for {@code GnssLocationProvider}. See
+     * {@code setReturningDiffs} as well.
+     *
+     * @hide
+     */
+    public void transferWorkChains(WorkSource other) {
+        if (mChains != null) {
+            mChains.clear();
+        }
+
+        if (other.mChains == null || other.mChains.isEmpty()) {
+            return;
+        }
+
+        if (mChains == null) {
+            mChains = new ArrayList<>(4);
+        }
+
+        mChains.addAll(other.mChains);
+        other.mChains.clear();
+    }
+
     private boolean removeUids(WorkSource other) {
         int N1 = mNum;
         final int[] uids1 = mUids;
@@ -817,7 +858,8 @@
      *
      * @hide
      */
-    public static class WorkChain implements Parcelable {
+    @SystemApi
+    public static final class WorkChain implements Parcelable {
         private int mSize;
         private int[] mUids;
         private String[] mTags;
@@ -829,7 +871,8 @@
             mTags = new String[4];
         }
 
-        // @VisibleForTesting
+        /** @hide */
+        @VisibleForTesting
         public WorkChain(WorkChain other) {
             mSize = other.mSize;
             mUids = other.mUids.clone();
@@ -866,19 +909,32 @@
             return mUids[0];
         }
 
+        /**
+         * Return the tag associated with the attribution UID. See (@link #getAttributionUid}.
+         */
+        public String getAttributionTag() {
+            return mTags[0];
+        }
+
         // TODO: The following three trivial getters are purely for testing and will be removed
         // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
         // diffing it etc.
-        //
-        // @VisibleForTesting
+
+
+        /** @hide */
+        @VisibleForTesting
         public int[] getUids() {
             return mUids;
         }
-        // @VisibleForTesting
+
+        /** @hide */
+        @VisibleForTesting
         public String[] getTags() {
             return mTags;
         }
-        // @VisibleForTesting
+
+        /** @hide */
+        @VisibleForTesting
         public int getSize() {
             return mSize;
         }
diff --git a/android/os/ZygoteProcess.java b/android/os/ZygoteProcess.java
index 670f794..b9dd376 100644
--- a/android/os/ZygoteProcess.java
+++ b/android/os/ZygoteProcess.java
@@ -32,7 +32,9 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
+import java.util.UUID;
 
 /*package*/ class ZygoteStartFailedEx extends Exception {
     ZygoteStartFailedEx(String s) {
@@ -61,18 +63,27 @@
     /**
      * The name of the socket used to communicate with the primary zygote.
      */
-    private final String mSocket;
+    private final LocalSocketAddress mSocket;
 
     /**
      * The name of the secondary (alternate ABI) zygote socket.
      */
-    private final String mSecondarySocket;
+    private final LocalSocketAddress mSecondarySocket;
 
     public ZygoteProcess(String primarySocket, String secondarySocket) {
+        this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
+                new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
+    }
+
+    public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
         mSocket = primarySocket;
         mSecondarySocket = secondarySocket;
     }
 
+    public LocalSocketAddress getPrimarySocketAddress() {
+        return mSocket;
+    }
+
     /**
      * State for communicating with the zygote process.
      */
@@ -92,14 +103,13 @@
             this.abiList = abiList;
         }
 
-        public static ZygoteState connect(String socketAddress) throws IOException {
+        public static ZygoteState connect(LocalSocketAddress address) throws IOException {
             DataInputStream zygoteInputStream = null;
             BufferedWriter zygoteWriter = null;
             final LocalSocket zygoteSocket = new LocalSocket();
 
             try {
-                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
-                        LocalSocketAddress.Namespace.RESERVED));
+                zygoteSocket.connect(address);
 
                 zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
 
@@ -115,8 +125,8 @@
             }
 
             String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
-            Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
-                    + abiListString);
+            Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
+                    + address.getName() + " opened, supported ABIS: " + abiListString);
 
             return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                     Arrays.asList(abiListString.split(",")));
@@ -149,6 +159,13 @@
     private final Object mLock = new Object();
 
     /**
+     * List of exemptions to the API blacklist. These are prefix matches on the runtime format
+     * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
+     * list.
+     */
+    private List<String> mApiBlacklistExemptions = Collections.emptyList();
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -166,7 +183,7 @@
      * The process will continue running after this function returns.
      *
      * <p>If processes are not enabled, a new thread in the caller's
-     * process is created and main() of <var>processClass</var> called there.
+     * process is created and main() of <var>processclass</var> called there.
      *
      * <p>The niceName parameter, if not an empty string, is a custom name to
      * give to the process instead of using processClass.  This allows you to
@@ -209,7 +226,8 @@
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
+                    zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -325,6 +343,8 @@
      * @param abi the ABI the process should use.
      * @param instructionSet null-ok the instruction set to use.
      * @param appDataDir null-ok the data directory of the app.
+     * @param startChildZygote Start a sub-zygote. This creates a new zygote process
+     * that has its state cloned from this zygote process.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws ZygoteStartFailedEx if process start failed for any reason
@@ -340,6 +360,7 @@
                                                       String instructionSet,
                                                       String appDataDir,
                                                       String invokeWith,
+                                                      boolean startChildZygote,
                                                       String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -396,6 +417,10 @@
             argsForZygote.add(invokeWith);
         }
 
+        if (startChildZygote) {
+            argsForZygote.add("--start-child-zygote");
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
@@ -410,6 +435,18 @@
     }
 
     /**
+     * Closes the connections to the zygote, if they exist.
+     */
+    public void close() {
+        if (primaryZygoteState != null) {
+            primaryZygoteState.close();
+        }
+        if (secondaryZygoteState != null) {
+            secondaryZygoteState.close();
+        }
+    }
+
+    /**
      * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
      * and retry if the zygote is unresponsive. This method is a no-op if a connection is
      * already open.
@@ -425,6 +462,49 @@
     }
 
     /**
+     * Push hidden API blacklisting exemptions into the zygote process(es).
+     *
+     * <p>The list of exemptions will take affect for all new processes forked from the zygote after
+     * this call.
+     *
+     * @param exemptions List of hidden API exemption prefixes.
+     */
+    public void setApiBlacklistExemptions(List<String> exemptions) {
+        synchronized (mLock) {
+            mApiBlacklistExemptions = exemptions;
+            maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+        if (state == null || state.isClosed()) {
+            return;
+        }
+        if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
+            return;
+        }
+        try {
+            state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+            state.writer.newLine();
+            state.writer.write("--set-api-blacklist-exemptions");
+            state.writer.newLine();
+            for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
+                state.writer.write(mApiBlacklistExemptions.get(i));
+                state.writer.newLine();
+            }
+            state.writer.flush();
+            int status = state.inputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
+        }
+    }
+
+    /**
      * Tries to open socket to Zygote process if not already open. If
      * already open, does nothing.  May block and retry.  Requires that mLock be held.
      */
@@ -438,8 +518,8 @@
             } catch (IOException ioe) {
                 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
             }
+            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
         }
-
         if (primaryZygoteState.matches(abi)) {
             return primaryZygoteState;
         }
@@ -451,6 +531,7 @@
             } catch (IOException ioe) {
                 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
             }
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
         }
 
         if (secondaryZygoteState.matches(abi)) {
@@ -464,11 +545,12 @@
      * Instructs the zygote to pre-load the classes and native libraries at the given paths
      * for the specified abi. Not all zygotes support this function.
      */
-    public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
-                                        String abi) throws ZygoteStartFailedEx, IOException {
+    public boolean preloadPackageForAbi(String packagePath, String libsPath, String libFileName,
+                                        String cacheKey, String abi) throws ZygoteStartFailedEx,
+                                                                            IOException {
         synchronized(mLock) {
             ZygoteState state = openZygoteSocketIfNeeded(abi);
-            state.writer.write("4");
+            state.writer.write("5");
             state.writer.newLine();
 
             state.writer.write("--preload-package");
@@ -480,6 +562,9 @@
             state.writer.write(libsPath);
             state.writer.newLine();
 
+            state.writer.write(libFileName);
+            state.writer.newLine();
+
             state.writer.write(cacheKey);
             state.writer.newLine();
 
@@ -514,9 +599,19 @@
      * @param socketName The name of the socket to connect to.
      */
     public static void waitForConnectionToZygote(String socketName) {
+        final LocalSocketAddress address =
+                new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
+        waitForConnectionToZygote(address);
+    }
+
+    /**
+     * Try connecting to the Zygote over and over again until we hit a time-out.
+     * @param address The name of the socket to connect to.
+     */
+    public static void waitForConnectionToZygote(LocalSocketAddress address) {
         for (int n = 20; n >= 0; n--) {
             try {
-                final ZygoteState zs = ZygoteState.connect(socketName);
+                final ZygoteState zs = ZygoteState.connect(address);
                 zs.close();
                 return;
             } catch (IOException ioe) {
@@ -529,6 +624,38 @@
             } catch (InterruptedException ie) {
             }
         }
-        Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + socketName);
+        Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
+    }
+
+    /**
+     * Starts a new zygote process as a child of this zygote. This is used to create
+     * secondary zygotes that inherit data from the zygote that this object
+     * communicates with. This returns a new ZygoteProcess representing a connection
+     * to the newly created zygote. Throws an exception if the zygote cannot be started.
+     */
+    public ChildZygoteProcess startChildZygote(final String processClass,
+                                               final String niceName,
+                                               int uid, int gid, int[] gids,
+                                               int runtimeFlags,
+                                               String seInfo,
+                                               String abi,
+                                               String instructionSet) {
+        // Create an unguessable address in the global abstract namespace.
+        final LocalSocketAddress serverAddress = new LocalSocketAddress(
+                processClass + "/" + UUID.randomUUID().toString());
+
+        final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+
+        Process.ProcessStartResult result;
+        try {
+            result = startViaZygote(processClass, niceName, uid, gid,
+                    gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
+                    abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
+                    true /* startChildZygote */, extraArgs);
+        } catch (ZygoteStartFailedEx ex) {
+            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
+        }
+
+        return new ChildZygoteProcess(serverAddress, result.pid);
     }
 }
diff --git a/android/os/storage/StorageManager.java b/android/os/storage/StorageManager.java
index f4deeed..8905ad1 100644
--- a/android/os/storage/StorageManager.java
+++ b/android/os/storage/StorageManager.java
@@ -50,7 +50,6 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -757,10 +756,15 @@
         }
         try {
             for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
-                if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
+                if (vol.path != null && FileUtils.contains(vol.path, pathString)
+                        && vol.type != VolumeInfo.TYPE_PUBLIC) {
                     // TODO: verify that emulated adopted devices have UUID of
                     // underlying volume
-                    return convert(vol.fsUuid);
+                    try {
+                        return convert(vol.fsUuid);
+                    } catch (IllegalArgumentException e) {
+                        continue;
+                    }
                 }
             }
         } catch (RemoteException e) {
@@ -1096,7 +1100,7 @@
     public @NonNull List<StorageVolume> getStorageVolumes() {
         final ArrayList<StorageVolume> res = new ArrayList<>();
         Collections.addAll(res,
-                getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
+                getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
         return res;
     }
 
@@ -1107,7 +1111,7 @@
      * {@link Context#getExternalFilesDir(String)}.
      */
     public @NonNull StorageVolume getPrimaryStorageVolume() {
-        return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
+        return getVolumeList(mContext.getUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
     }
 
     /** {@hide} */
@@ -1323,15 +1327,6 @@
     }
 
     /** {@hide} */
-    public void secdiscard(String path) {
-        try {
-            mStorageManager.secdiscard(path);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** {@hide} */
     public static boolean isUserKeyUnlocked(int userId) {
         if (sStorageManager == null) {
             sStorageManager = IStorageManager.Stub
diff --git a/android/os/storage/StorageResultCode.java b/android/os/storage/StorageResultCode.java
deleted file mode 100644
index c843887..0000000
--- a/android/os/storage/StorageResultCode.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-package android.os.storage;
-
-/**
- * Class that provides access to constants returned from StorageManager
- * and lower level StorageManagerService APIs.
- * 
- * @hide
- */
-public class StorageResultCode
-{
-    /**
-     * Operation succeeded.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationSucceeded               =  0;
-
-    /**
-     * Operation failed: Internal error.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedInternalError     = -1;
-
-    /**
-     * Operation failed: Missing media.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedNoMedia           = -2;
-
-    /**
-     * Operation failed: Media is blank.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedMediaBlank        = -3;
-
-    /**
-     * Operation failed: Media is corrupt.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedMediaCorrupt      = -4;
-
-    /**
-     * Operation failed: Storage not mounted.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedStorageNotMounted  = -5;
-
-    /**
-     * Operation failed: Storage is mounted.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedStorageMounted     = -6;
-
-    /**
-     * Operation failed: Storage is busy.
-     * @see android.os.storage.StorageManager
-     */
-    public static final int OperationFailedStorageBusy        = -7;
-
-}
diff --git a/android/os/storage/VolumeInfo.java b/android/os/storage/VolumeInfo.java
index d3877ca..9e3e386 100644
--- a/android/os/storage/VolumeInfo.java
+++ b/android/os/storage/VolumeInfo.java
@@ -269,22 +269,7 @@
         return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
     }
 
-    public boolean isVisibleForRead(int userId) {
-        if (type == TYPE_PUBLIC) {
-            if (isPrimary() && mountUserId != userId) {
-                // Primary physical is only visible to single user
-                return false;
-            } else {
-                return isVisible();
-            }
-        } else if (type == TYPE_EMULATED) {
-            return isVisible();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean isVisibleForWrite(int userId) {
+    public boolean isVisibleForUser(int userId) {
         if (type == TYPE_PUBLIC && mountUserId == userId) {
             return isVisible();
         } else if (type == TYPE_EMULATED) {
@@ -294,6 +279,14 @@
         }
     }
 
+    public boolean isVisibleForRead(int userId) {
+        return isVisibleForUser(userId);
+    }
+
+    public boolean isVisibleForWrite(int userId) {
+        return isVisibleForUser(userId);
+    }
+
     public File getPath() {
         return (path != null) ? new File(path) : null;
     }
@@ -319,7 +312,7 @@
      * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
      */
     public File getInternalPathForUser(int userId) {
-        if (type == TYPE_PUBLIC) {
+        if (type == TYPE_PUBLIC && !isVisible()) {
             // TODO: plumb through cleaner path from vold
             return new File(path.replace("/storage/", "/mnt/media_rw/"));
         } else {
@@ -409,9 +402,9 @@
      * Build an intent to browse the contents of this volume. Only valid for
      * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
      */
-    public Intent buildBrowseIntent() {
+    public @Nullable Intent buildBrowseIntent() {
         final Uri uri;
-        if (type == VolumeInfo.TYPE_PUBLIC) {
+        if (type == VolumeInfo.TYPE_PUBLIC && mountUserId == UserHandle.myUserId()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
         } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
diff --git a/android/perftests/utils/ManualBenchmarkState.java b/android/perftests/utils/ManualBenchmarkState.java
index 2c84db1..40778de 100644
--- a/android/perftests/utils/ManualBenchmarkState.java
+++ b/android/perftests/utils/ManualBenchmarkState.java
@@ -150,6 +150,8 @@
         final Bundle status = new Bundle();
         status.putLong(key + "_median", mStats.getMedian());
         status.putLong(key + "_mean", (long) mStats.getMean());
+        status.putLong(key + "_percentile90", mStats.getPercentile90());
+        status.putLong(key + "_percentile95", mStats.getPercentile95());
         status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
         instrumentation.sendStatus(Activity.RESULT_OK, status);
     }
diff --git a/android/perftests/utils/Stats.java b/android/perftests/utils/Stats.java
index acc44a8..5e50073 100644
--- a/android/perftests/utils/Stats.java
+++ b/android/perftests/utils/Stats.java
@@ -21,7 +21,7 @@
 import java.util.List;
 
 public class Stats {
-    private long mMedian, mMin, mMax;
+    private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
     private double mMean, mStandardDeviation;
 
     /* Calculate stats in constructor. */
@@ -35,12 +35,14 @@
 
         Collections.sort(values);
 
-        mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
-                values.get(size / 2);
-
         mMin = values.get(0);
         mMax = values.get(values.size() - 1);
 
+        mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
+                values.get(size / 2);
+        mPercentile90 = getPercentile(values, 90);
+        mPercentile95 = getPercentile(values, 95);
+
         for (int i = 0; i < size; ++i) {
             long result = values.get(i);
             mMean += result;
@@ -73,4 +75,21 @@
     public double getStandardDeviation() {
         return mStandardDeviation;
     }
+
+    public long getPercentile90() {
+        return mPercentile90;
+    }
+
+    public long getPercentile95() {
+        return mPercentile95;
+    }
+
+    private static long getPercentile(List<Long> values, int percentile) {
+        if (percentile < 0 || percentile > 100) {
+            throw new IllegalArgumentException(
+                    "invalid percentile " + percentile + ", should be 0-100");
+        }
+        int idx = (values.size() - 1) * percentile / 100;
+        return values.get(idx);
+    }
 }
diff --git a/android/perftests/utils/StubActivity.java b/android/perftests/utils/StubActivity.java
index 6012f4b..8f03f7e 100644
--- a/android/perftests/utils/StubActivity.java
+++ b/android/perftests/utils/StubActivity.java
@@ -17,6 +17,14 @@
 package android.perftests.utils;
 
 import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
 
 public class StubActivity extends Activity {
-}
\ No newline at end of file
+    public static Intent createLaunchIntent(Context context) {
+        final Intent intent = new Intent();
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setClass(context, StubActivity.class);
+        return intent;
+    }
+}
diff --git a/android/preference/BridgePreferenceInflater.java b/android/preference/BridgePreferenceInflater.java
index cb56116..3665d86 100644
--- a/android/preference/BridgePreferenceInflater.java
+++ b/android/preference/BridgePreferenceInflater.java
@@ -49,7 +49,8 @@
             preference = super.createItem(name, prefix, attrs);
         } catch (ClassNotFoundException | InflateException exception) {
             // name is probably not a valid preference type
-            if ("android.support.v7.preference".equals(prefix) &&
+            if (("android.support.v7.preference".equals(prefix) ||
+                    "androidx.preference".equals(prefix)) &&
                     "SwitchPreferenceCompat".equals(name)) {
                 preference = super.createItem("SwitchPreference", prefix, attrs);
             }
diff --git a/android/preference/SeekBarVolumizer.java b/android/preference/SeekBarVolumizer.java
index 3d2e1d1..0ed2526 100644
--- a/android/preference/SeekBarVolumizer.java
+++ b/android/preference/SeekBarVolumizer.java
@@ -34,6 +34,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.System;
+import android.service.notification.ZenModeConfig;
 import android.util.Log;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -87,10 +88,22 @@
     private static final int MSG_INIT_SAMPLE = 3;
     private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
 
+    private NotificationManager.Policy mNotificationPolicy;
+    private boolean mAllowAlarms;
+    private boolean mAllowMedia;
+    private boolean mAllowRinger;
+
     public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
         mContext = context;
         mAudioManager = context.getSystemService(AudioManager.class);
         mNotificationManager = context.getSystemService(NotificationManager.class);
+        mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+        mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
+                .PRIORITY_CATEGORY_ALARMS) != 0;
+        mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
+                .PRIORITY_CATEGORY_MEDIA) != 0;
+        mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+                mNotificationPolicy);
         mStreamType = streamType;
         mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
         mNotificationOrRing = isNotificationOrRing(mStreamType);
@@ -122,6 +135,14 @@
         return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
     }
 
+    private static boolean isAlarmsStream(int stream) {
+        return stream == AudioManager.STREAM_ALARM;
+    }
+
+    private static boolean isMediaStream(int stream) {
+        return stream == AudioManager.STREAM_MUSIC;
+    }
+
     public void setSeekBar(SeekBar seekBar) {
         if (mSeekBar != null) {
             mSeekBar.setOnSeekBarChangeListener(null);
@@ -135,7 +156,11 @@
 
     private boolean isZenMuted() {
         return mNotificationOrRing && mZenMode == Global.ZEN_MODE_ALARMS
-                || mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+                || mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+                || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                    && ((!mAllowAlarms && isAlarmsStream(mStreamType))
+                        || (!mAllowMedia && isMediaStream(mStreamType))
+                        || (!mAllowRinger && isNotificationOrRing(mStreamType))));
     }
 
     protected void updateSeekBar() {
@@ -396,6 +421,7 @@
                 final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
                 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
                 filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
+                filter.addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
                 filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
                 mContext.registerReceiver(this, filter);
             } else {
@@ -424,6 +450,15 @@
             } else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
                 mZenMode = mNotificationManager.getZenMode();
                 updateSlider();
+            } else if (NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED.equals(action)) {
+                mNotificationPolicy = mNotificationManager.getNotificationPolicy();
+                mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
+                        .PRIORITY_CATEGORY_ALARMS) != 0;
+                mAllowMedia = (mNotificationPolicy.priorityCategories
+                        & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0;
+                mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+                        mNotificationPolicy);
+                updateSlider();
             }
         }
 
diff --git a/android/print/PrintFileDocumentAdapter.java b/android/print/PrintFileDocumentAdapter.java
index 747400d..a5f9305 100644
--- a/android/print/PrintFileDocumentAdapter.java
+++ b/android/print/PrintFileDocumentAdapter.java
@@ -21,6 +21,8 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.CancellationSignal.OnCancelListener;
+import android.os.FileUtils;
+import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
@@ -114,28 +116,15 @@
 
         @Override
         protected Void doInBackground(Void... params) {
-            InputStream in = null;
-            OutputStream out = new FileOutputStream(mDestination.getFileDescriptor());
-            final byte[] buffer = new byte[8192];
-            try {
-                in = new FileInputStream(mFile);
-                while (true) {
-                    if (isCancelled()) {
-                        break;
-                    }
-                    final int readByteCount = in.read(buffer);
-                    if (readByteCount < 0) {
-                        break;
-                    }
-                    out.write(buffer, 0, readByteCount);
-                }
-             } catch (IOException ioe) {
-                 Log.e(LOG_TAG, "Error writing data!", ioe);
-                 mResultCallback.onWriteFailed(mContext.getString(
-                         R.string.write_fail_reason_cannot_write));
-             } finally {
-                IoUtils.closeQuietly(in);
-                IoUtils.closeQuietly(out);
+            try (InputStream in = new FileInputStream(mFile);
+                    OutputStream out = new FileOutputStream(mDestination.getFileDescriptor())) {
+                FileUtils.copy(in, out, null, mCancellationSignal);
+            } catch (OperationCanceledException e) {
+                // Ignored; already handled below
+            } catch (IOException e) {
+                Log.e(LOG_TAG, "Error writing data!", e);
+                mResultCallback.onWriteFailed(mContext.getString(
+                        R.string.write_fail_reason_cannot_write));
             }
             return null;
         }
diff --git a/android/print/PrintManager.java b/android/print/PrintManager.java
index 51b7798..e436bc6 100644
--- a/android/print/PrintManager.java
+++ b/android/print/PrintManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -27,6 +28,7 @@
 import android.content.Context;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -104,6 +106,7 @@
  * @see PrintJobInfo
  */
 @SystemService(Context.PRINT_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_PRINTING)
 public final class PrintManager {
 
     private static final String LOG_TAG = "PrintManager";
diff --git a/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java b/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
index 219868d..dd97f1e 100644
--- a/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
+++ b/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
@@ -19,6 +19,7 @@
 import android.privacy.DifferentialPrivacyEncoder;
 import android.privacy.internal.rappor.RapporConfig;
 import android.privacy.internal.rappor.RapporEncoder;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -48,6 +49,9 @@
  */
 public class LongitudinalReportingEncoder implements DifferentialPrivacyEncoder {
 
+    private static final String TAG = "LongitudinalEncoder";
+    private static final boolean DEBUG = false;
+
     // Suffix that will be added to Rappor's encoder id. There's a (relatively) small risk some
     // other Rappor encoder may re-use the same encoder id.
     private static final String PRR1_ENCODER_ID = "prr1_encoder_id";
@@ -121,11 +125,18 @@
 
     @Override
     public byte[] encodeBoolean(boolean original) {
+        if (DEBUG) {
+            Log.d(TAG, "encodeBoolean, encoderId:" + mConfig.getEncoderId() + ", original: "
+                    + original);
+        }
         if (mFakeValue != null) {
             // Use the fake value generated in PRR.
             original = mFakeValue.booleanValue();
+            if (DEBUG) Log.d(TAG, "Use fake value: " + original);
         }
-        return mIRREncoder.encodeBoolean(original);
+        byte[] result = mIRREncoder.encodeBoolean(original);
+        if (DEBUG) Log.d(TAG, "result: " + ((result[0] & 0x1) != 0));
+        return result;
     }
 
     @Override
diff --git a/android/privacy/internal/rappor/RapporEncoder.java b/android/privacy/internal/rappor/RapporEncoder.java
index 9ac2b3e..3bf09ec 100644
--- a/android/privacy/internal/rappor/RapporEncoder.java
+++ b/android/privacy/internal/rappor/RapporEncoder.java
@@ -20,6 +20,10 @@
 
 import com.google.android.rappor.Encoder;
 
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Random;
 
@@ -66,7 +70,7 @@
             random = sSecureRandom;
         } else {
             // To have deterministic result by hard coding encoder id as seed.
-            random = new Random((long) config.mEncoderId.hashCode());
+            random = new Random(getInsecureSeed(config.mEncoderId));
             userSecret = INSECURE_SECRET;
         }
         mEncoder = new Encoder(random, null, null,
@@ -75,6 +79,17 @@
                 config.mNumCohorts, config.mNumBloomHashes);
     }
 
+    private long getInsecureSeed(String input) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            byte[] bytes = digest.digest(input.getBytes(StandardCharsets.UTF_8));
+            return ByteBuffer.wrap(bytes).getLong();
+        } catch (NoSuchAlgorithmException e) {
+            // Should not happen
+            throw new AssertionError("Unable generate insecure seed");
+        }
+    }
+
     /**
      * Create {@link RapporEncoder} with {@link RapporConfig} and user secret provided.
      *
diff --git a/android/provider/BlockedNumberContract.java b/android/provider/BlockedNumberContract.java
index fb11d00..67c6fb9 100644
--- a/android/provider/BlockedNumberContract.java
+++ b/android/provider/BlockedNumberContract.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
+import android.telecom.Log;
 
 /**
  * <p>
@@ -228,6 +229,25 @@
     /** @hide */
     public static final String RES_CAN_BLOCK_NUMBERS = "can_block";
 
+    /** @hide */
+    public static final String RES_ENHANCED_SETTING_IS_ENABLED = "enhanced_setting_enabled";
+
+    /** @hide */
+    public static final String RES_SHOW_EMERGENCY_CALL_NOTIFICATION =
+            "show_emergency_call_notification";
+
+    /** @hide */
+    public static final String EXTRA_ENHANCED_SETTING_KEY = "extra_enhanced_setting_key";
+
+    /** @hide */
+    public static final String EXTRA_ENHANCED_SETTING_VALUE = "extra_enhanced_setting_value";
+
+    /** @hide */
+    public static final String EXTRA_CONTACT_EXIST = "extra_contact_exist";
+
+    /** @hide */
+    public static final String EXTRA_CALL_PRESENTATION = "extra_call_presentation";
+
     /**
      * Returns whether a given number is in the blocked list.
      *
@@ -242,9 +262,16 @@
      */
     @WorkerThread
     public static boolean isBlocked(Context context, String phoneNumber) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
-        return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
+            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "isBlocked: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -278,9 +305,16 @@
      * @return {@code true} if the current user can block numbers.
      */
     public static boolean canCurrentUserBlockNumbers(Context context) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
-        return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
+            return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -314,11 +348,33 @@
         public static final String METHOD_GET_BLOCK_SUPPRESSION_STATUS =
                 "get_block_suppression_status";
 
+        public static final String METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION =
+                "should_show_emergency_call_notification";
+
         public static final String RES_IS_BLOCKING_SUPPRESSED = "blocking_suppressed";
 
         public static final String RES_BLOCKING_SUPPRESSED_UNTIL_TIMESTAMP =
                 "blocking_suppressed_until_timestamp";
 
+        public static final String METHOD_GET_ENHANCED_BLOCK_SETTING = "get_enhanced_block_setting";
+        public static final String METHOD_SET_ENHANCED_BLOCK_SETTING = "set_enhanced_block_setting";
+
+        /* Preference key of block numbers not in contacts setting. */
+        public static final String ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED =
+                "block_numbers_not_in_contacts_setting";
+        /* Preference key of block private number calls setting. */
+        public static final String ENHANCED_SETTING_KEY_BLOCK_PRIVATE =
+                "block_private_number_calls_setting";
+        /* Preference key of block payphone calls setting. */
+        public static final String ENHANCED_SETTING_KEY_BLOCK_PAYPHONE =
+                "block_payphone_calls_setting";
+        /* Preference key of block unknown calls setting. */
+        public static final String ENHANCED_SETTING_KEY_BLOCK_UNKNOWN =
+                "block_unknown_calls_setting";
+        /* Preference key for whether should show an emergency call notification. */
+        public static final String ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION =
+                "show_emergency_call_notification";
+
         /**
          * Notifies the provider that emergency services were contacted by the user.
          * <p> This results in {@link #shouldSystemBlockNumber} returning {@code false} independent
@@ -327,8 +383,14 @@
          * the provider unless {@link #endBlockSuppression(Context)} is called.
          */
         public static void notifyEmergencyContact(Context context) {
-            context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            try {
+                context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "notifyEmergencyContact: provider not ready.");
+            }
         }
 
         /**
@@ -342,14 +404,27 @@
 
         /**
          * Returns {@code true} if {@code phoneNumber} is blocked taking
-         * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services have
-         * not been contacted recently, this method is equivalent to
-         * {@link #isBlocked(Context, String)}.
+         * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services
+         * have not been contacted recently and enhanced call blocking not been enabled, this
+         * method is equivalent to {@link #isBlocked(Context, String)}.
+         *
+         * @param context the context of the caller.
+         * @param phoneNumber the number to check.
+         * @param extras the extra attribute of the number.
+         * @return {@code true} if should block the number. {@code false} otherwise.
          */
-        public static boolean shouldSystemBlockNumber(Context context, String phoneNumber) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, null);
-            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        public static boolean shouldSystemBlockNumber(Context context, String phoneNumber,
+                Bundle extras) {
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
+                return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldSystemBlockNumber: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -363,9 +438,76 @@
         }
 
         /**
-         * Represents the current status of {@link #shouldSystemBlockNumber(Context, String)}. If
-         * emergency services have been contacted recently, {@link #isSuppressed} is {@code true},
-         * and blocking is disabled until the timestamp {@link #untilTimestampMillis}.
+         * Check whether should show the emergency call notification.
+         *
+         * @param context the context of the caller.
+         * @return {@code true} if should show emergency call notification. {@code false} otherwise.
+         */
+        public static boolean shouldShowEmergencyCallNotification(Context context) {
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
+                return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
+                return false;
+            }
+        }
+
+        /**
+         * Check whether the enhanced block setting is enabled.
+         *
+         * @param context the context of the caller.
+         * @param key the key of the setting to check, can be
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+         *        {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
+         * @return {@code true} if the setting is enabled. {@code false} otherwise.
+         */
+        public static boolean getEnhancedBlockSetting(Context context, String key) {
+            Bundle extras = new Bundle();
+            extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
+                return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "getEnhancedBlockSetting: provider not ready.");
+                return false;
+            }
+        }
+
+        /**
+         * Set the enhanced block setting enabled status.
+         *
+         * @param context the context of the caller.
+         * @param key the key of the setting to set, can be
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_PRIVATE}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_PAYPHONE}
+         *        {@link #ENHANCED_SETTING_KEY_BLOCK_UNKNOWN}
+         *        {@link #ENHANCED_SETTING_KEY_EMERGENCY_CALL_NOTIFICATION_SHOWING}
+         * @param value the enabled statue of the setting to set.
+         */
+        public static void setEnhancedBlockSetting(Context context, String key, boolean value) {
+            Bundle extras = new Bundle();
+            extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
+            extras.putBoolean(EXTRA_ENHANCED_SETTING_VALUE, value);
+            context.getContentResolver().call(AUTHORITY_URI, METHOD_SET_ENHANCED_BLOCK_SETTING,
+                    null, extras);
+        }
+
+        /**
+         * Represents the current status of
+         * {@link #shouldSystemBlockNumber(Context, String, Bundle)}. If emergency services
+         * have been contacted recently, {@link #isSuppressed} is {@code true}, and blocking
+         * is disabled until the timestamp {@link #untilTimestampMillis}.
          */
         public static class BlockSuppressionStatus {
             public final boolean isSuppressed;
diff --git a/android/provider/CallLog.java b/android/provider/CallLog.java
index c6c8d9d..70de09e 100644
--- a/android/provider/CallLog.java
+++ b/android/provider/CallLog.java
@@ -225,6 +225,7 @@
 
         /**
          * Indicates the call underwent Assisted Dialing.
+         * @hide
          */
         public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4;
 
diff --git a/android/provider/ContactsInternal.java b/android/provider/ContactsInternal.java
index 2cd1d48..362eba9 100644
--- a/android/provider/ContactsInternal.java
+++ b/android/provider/ContactsInternal.java
@@ -68,7 +68,7 @@
             }
         }
         // Launch on the current profile.
-        startQuickContactWithErrorToastForUser(context, intent, Process.myUserHandle());
+        startQuickContactWithErrorToastForUser(context, intent, context.getUser());
     }
 
     public static void startQuickContactWithErrorToastForUser(Context context, Intent intent,
diff --git a/android/provider/DocumentsContract.java b/android/provider/DocumentsContract.java
index e7fd59e..d96316a 100644
--- a/android/provider/DocumentsContract.java
+++ b/android/provider/DocumentsContract.java
@@ -738,7 +738,9 @@
     private static final String PATH_DOCUMENT = "document";
     private static final String PATH_CHILDREN = "children";
     private static final String PATH_SEARCH = "search";
-    private static final String PATH_TREE = "tree";
+    // TODO(b/72055774): make private again once ScopedAccessProvider is refactored
+    /** {@hide} */
+    public static final String PATH_TREE = "tree";
 
     private static final String PARAM_QUERY = "query";
     private static final String PARAM_MANAGE = "manage";
diff --git a/android/provider/MediaStore.java b/android/provider/MediaStore.java
index d9808a3..1da6602 100644
--- a/android/provider/MediaStore.java
+++ b/android/provider/MediaStore.java
@@ -692,8 +692,8 @@
             // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
             // If the magic is non-zero, we simply return thumbnail if it does exist.
             // querying MediaProvider and simply return thumbnail.
-            MiniThumbFile thumbFile = new MiniThumbFile(isVideo ? Video.Media.EXTERNAL_CONTENT_URI
-                    : Images.Media.EXTERNAL_CONTENT_URI);
+            MiniThumbFile thumbFile = MiniThumbFile.instance(
+                    isVideo ? Video.Media.EXTERNAL_CONTENT_URI : Images.Media.EXTERNAL_CONTENT_URI);
             Cursor c = null;
             try {
                 long magic = thumbFile.getMagic(origId);
diff --git a/android/provider/Settings.java b/android/provider/Settings.java
index 1ea4861..68fc6c1 100644
--- a/android/provider/Settings.java
+++ b/android/provider/Settings.java
@@ -83,7 +83,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MemoryIntArray;
-import android.util.StatsLog;
+import android.view.textservice.TextServicesManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.widget.ILockSettings;
@@ -429,6 +429,20 @@
             "android.settings.WIFI_IP_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of data and view data usage.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DATA_USAGE_SETTINGS =
+            "android.settings.DATA_USAGE_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of Bluetooth.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -769,6 +783,21 @@
             "android.settings.APPLICATION_DETAILS_SETTINGS";
 
     /**
+     * Activity Action: Show the "Open by Default" page in a particular application's details page.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+     * <p>
+     * Input: The Intent's data URI specifies the application package name
+     * to be shown, with the "package" scheme. That is "package:com.my.app".
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE =
+            "android.settings.APPLICATION_DETAILS_SETTINGS_OPEN_BY_DEFAULT_PAGE";
+
+    /**
      * Activity Action: Show list of applications that have been running
      * foreground services (to the user "running in the background").
      * <p>
@@ -1319,10 +1348,18 @@
             = "android.settings.NOTIFICATION_SETTINGS";
 
     /**
+     * Activity Action: Show app listing settings, filtered by those that send notifications.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ALL_APPS_NOTIFICATION_SETTINGS =
+            "android.settings.ALL_APPS_NOTIFICATION_SETTINGS";
+
+    /**
      * Activity Action: Show notification settings for a single app.
      * <p>
-     *     Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel to display.
-     *     Input: Optionally, {@link #EXTRA_CHANNEL_ID}, to highlight that channel.
+     *     Input: {@link #EXTRA_APP_PACKAGE}, the package to display.
      * <p>
      * Output: Nothing.
      */
@@ -1700,6 +1737,34 @@
     })
     public @interface ResetMode{}
 
+
+    /**
+     * User has not started setup personalization.
+     * @hide
+     */
+    public static final int USER_SETUP_PERSONALIZATION_NOT_STARTED = 0;
+
+    /**
+     * User has not yet completed setup personalization.
+     * @hide
+     */
+    public static final int USER_SETUP_PERSONALIZATION_STARTED = 1;
+
+    /**
+     * User has completed setup personalization.
+     * @hide
+     */
+    public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            USER_SETUP_PERSONALIZATION_NOT_STARTED,
+            USER_SETUP_PERSONALIZATION_STARTED,
+            USER_SETUP_PERSONALIZATION_COMPLETE
+    })
+    public @interface UserSetupPersonalization {}
+
     /**
      * Activity Extra: Number of certificates
      * <p>
@@ -1899,11 +1964,7 @@
                     arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
                 }
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                String prevValue = getStringForUser(cr, name, userHandle);
                 cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
-                String newValue = getStringForUser(cr, name, userHandle);
-                StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
-                        makeDefault ? 1 : 0, userHandle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
                 return false;
@@ -2255,7 +2316,7 @@
          * @return the corresponding value, or null if not present
          */
         public static String getString(ContentResolver resolver, String name) {
-            return getStringForUser(resolver, name, UserHandle.myUserId());
+            return getStringForUser(resolver, name, resolver.getUserId());
         }
 
         /** @hide */
@@ -2282,7 +2343,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putString(ContentResolver resolver, String name, String value) {
-            return putStringForUser(resolver, name, value, UserHandle.myUserId());
+            return putStringForUser(resolver, name, value, resolver.getUserId());
         }
 
         /** @hide */
@@ -2336,7 +2397,7 @@
          * or not a valid integer.
          */
         public static int getInt(ContentResolver cr, String name, int def) {
-            return getIntForUser(cr, name, def, UserHandle.myUserId());
+            return getIntForUser(cr, name, def, cr.getUserId());
         }
 
         /** @hide */
@@ -2369,7 +2430,7 @@
          */
         public static int getInt(ContentResolver cr, String name)
                 throws SettingNotFoundException {
-            return getIntForUser(cr, name, UserHandle.myUserId());
+            return getIntForUser(cr, name, cr.getUserId());
         }
 
         /** @hide */
@@ -2397,7 +2458,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putInt(ContentResolver cr, String name, int value) {
-            return putIntForUser(cr, name, value, UserHandle.myUserId());
+            return putIntForUser(cr, name, value, cr.getUserId());
         }
 
         /** @hide */
@@ -2421,7 +2482,7 @@
          * or not a valid {@code long}.
          */
         public static long getLong(ContentResolver cr, String name, long def) {
-            return getLongForUser(cr, name, def, UserHandle.myUserId());
+            return getLongForUser(cr, name, def, cr.getUserId());
         }
 
         /** @hide */
@@ -2456,7 +2517,7 @@
          */
         public static long getLong(ContentResolver cr, String name)
                 throws SettingNotFoundException {
-            return getLongForUser(cr, name, UserHandle.myUserId());
+            return getLongForUser(cr, name, cr.getUserId());
         }
 
         /** @hide */
@@ -2484,7 +2545,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putLong(ContentResolver cr, String name, long value) {
-            return putLongForUser(cr, name, value, UserHandle.myUserId());
+            return putLongForUser(cr, name, value, cr.getUserId());
         }
 
         /** @hide */
@@ -2508,7 +2569,7 @@
          * or not a valid float.
          */
         public static float getFloat(ContentResolver cr, String name, float def) {
-            return getFloatForUser(cr, name, def, UserHandle.myUserId());
+            return getFloatForUser(cr, name, def, cr.getUserId());
         }
 
         /** @hide */
@@ -2542,7 +2603,7 @@
          */
         public static float getFloat(ContentResolver cr, String name)
                 throws SettingNotFoundException {
-            return getFloatForUser(cr, name, UserHandle.myUserId());
+            return getFloatForUser(cr, name, cr.getUserId());
         }
 
         /** @hide */
@@ -2573,7 +2634,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putFloat(ContentResolver cr, String name, float value) {
-            return putFloatForUser(cr, name, value, UserHandle.myUserId());
+            return putFloatForUser(cr, name, value, cr.getUserId());
         }
 
         /** @hide */
@@ -2591,7 +2652,7 @@
          * @param outConfig Where to place the configuration settings.
          */
         public static void getConfiguration(ContentResolver cr, Configuration outConfig) {
-            adjustConfigurationForUser(cr, outConfig, UserHandle.myUserId(),
+            adjustConfigurationForUser(cr, outConfig, cr.getUserId(),
                     false /* updateSettingsIfEmpty */);
         }
 
@@ -2644,7 +2705,7 @@
          * @return true if the values were set, false on database errors
          */
         public static boolean putConfiguration(ContentResolver cr, Configuration config) {
-            return putConfigurationForUser(cr, config, UserHandle.myUserId());
+            return putConfigurationForUser(cr, config, cr.getUserId());
         }
 
         /** @hide */
@@ -2664,7 +2725,7 @@
         /** @deprecated - Do not use */
         @Deprecated
         public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
-            return getShowGTalkServiceStatusForUser(cr, UserHandle.myUserId());
+            return getShowGTalkServiceStatusForUser(cr, cr.getUserId());
         }
 
         /**
@@ -2680,7 +2741,7 @@
         /** @deprecated - Do not use */
         @Deprecated
         public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) {
-            setShowGTalkServiceStatusForUser(cr, flag, UserHandle.myUserId());
+            setShowGTalkServiceStatusForUser(cr, flag, cr.getUserId());
         }
 
         /**
@@ -3131,7 +3192,7 @@
         public static final String ALWAYS_FINISH_ACTIVITIES = Global.ALWAYS_FINISH_ACTIVITIES;
 
         /**
-         * Determines which streams are affected by ringer mode changes. The
+         * Determines which streams are affected by ringer and zen mode changes. The
          * stream type's bit should be set to 1 if it should be muted when going
          * into an inaudible ringer mode.
          */
@@ -3175,6 +3236,43 @@
         private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * The intensity of notification vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String NOTIFICATION_VIBRATION_INTENSITY =
+                "notification_vibration_intensity";
+
+        /**
+         * The intensity of haptic feedback vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their feedback intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String HAPTIC_FEEDBACK_INTENSITY =
+                "haptic_feedback_intensity";
+
+        private static final Validator VIBRATION_INTENSITY_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+
+        /**
          * Ringer volume. This is used internally, changing this value will not
          * change the volume. See AudioManager.
          *
@@ -3655,6 +3753,17 @@
                 new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
 
         /**
+         * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
+         * calls when supported by the device and carrier. Boolean value.
+         * 0 = OFF
+         * 1 = ON
+         */
+        public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+
+        /** @hide */
+        public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
          * boolean (1 or 0).
          */
@@ -3664,7 +3773,7 @@
         public static final Validator SOUND_EFFECTS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Whether the haptic feedback (long presses, ...) are enabled. The value is
+         * Whether haptic feedback (Vibrate on tap) is enabled. The value is
          * boolean (1 or 0).
          */
         public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
@@ -3979,6 +4088,7 @@
             DTMF_TONE_WHEN_DIALING,
             DTMF_TONE_TYPE_WHEN_DIALING,
             HEARING_AID,
+            RTT_CALLING_MODE,
             TTY_MODE,
             MASTER_MONO,
             SOUND_EFFECTS_ENABLED,
@@ -3995,7 +4105,9 @@
             LOCK_TO_APP_ENABLED,
             NOTIFICATION_SOUND,
             ACCELEROMETER_ROTATION,
-            SHOW_BATTERY_PERCENT
+            SHOW_BATTERY_PERCENT,
+            NOTIFICATION_VIBRATION_INTENSITY,
+            HAPTIC_FEEDBACK_INTENSITY,
         };
 
         /**
@@ -4136,6 +4248,8 @@
             VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
             VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
             VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
+            VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+            VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
             VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
             VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR);
@@ -4173,6 +4287,7 @@
             VALIDATORS.put(DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR);
             VALIDATORS.put(HEARING_AID, HEARING_AID_VALIDATOR);
             VALIDATORS.put(TTY_MODE, TTY_MODE_VALIDATOR);
+            VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, NOTIFICATION_LIGHT_PULSE_VALIDATOR);
             VALIDATORS.put(POINTER_LOCATION, POINTER_LOCATION_VALIDATOR);
             VALIDATORS.put(SHOW_TOUCHES, SHOW_TOUCHES_VALIDATOR);
@@ -4691,7 +4806,7 @@
          * @return the corresponding value, or null if not present
          */
         public static String getString(ContentResolver resolver, String name) {
-            return getStringForUser(resolver, name, UserHandle.myUserId());
+            return getStringForUser(resolver, name, resolver.getUserId());
         }
 
         /** @hide */
@@ -4745,7 +4860,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putString(ContentResolver resolver, String name, String value) {
-            return putStringForUser(resolver, name, value, UserHandle.myUserId());
+            return putStringForUser(resolver, name, value, resolver.getUserId());
         }
 
         /** @hide */
@@ -4818,7 +4933,7 @@
                 @NonNull String name, @Nullable String value, @Nullable String tag,
                 boolean makeDefault) {
             return putStringForUser(resolver, name, value, tag, makeDefault,
-                    UserHandle.myUserId());
+                    resolver.getUserId());
         }
 
         /**
@@ -4839,7 +4954,7 @@
         public static void resetToDefaults(@NonNull ContentResolver resolver,
                 @Nullable String tag) {
             resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS,
-                    UserHandle.myUserId());
+                    resolver.getUserId());
         }
 
         /**
@@ -4906,7 +5021,7 @@
          * or not a valid integer.
          */
         public static int getInt(ContentResolver cr, String name, int def) {
-            return getIntForUser(cr, name, def, UserHandle.myUserId());
+            return getIntForUser(cr, name, def, cr.getUserId());
         }
 
         /** @hide */
@@ -4943,7 +5058,7 @@
          */
         public static int getInt(ContentResolver cr, String name)
                 throws SettingNotFoundException {
-            return getIntForUser(cr, name, UserHandle.myUserId());
+            return getIntForUser(cr, name, cr.getUserId());
         }
 
         /** @hide */
@@ -4975,7 +5090,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putInt(ContentResolver cr, String name, int value) {
-            return putIntForUser(cr, name, value, UserHandle.myUserId());
+            return putIntForUser(cr, name, value, cr.getUserId());
         }
 
         /** @hide */
@@ -4999,7 +5114,7 @@
          * or not a valid {@code long}.
          */
         public static long getLong(ContentResolver cr, String name, long def) {
-            return getLongForUser(cr, name, def, UserHandle.myUserId());
+            return getLongForUser(cr, name, def, cr.getUserId());
         }
 
         /** @hide */
@@ -5034,7 +5149,7 @@
          */
         public static long getLong(ContentResolver cr, String name)
                 throws SettingNotFoundException {
-            return getLongForUser(cr, name, UserHandle.myUserId());
+            return getLongForUser(cr, name, cr.getUserId());
         }
 
         /** @hide */
@@ -5062,7 +5177,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putLong(ContentResolver cr, String name, long value) {
-            return putLongForUser(cr, name, value, UserHandle.myUserId());
+            return putLongForUser(cr, name, value, cr.getUserId());
         }
 
         /** @hide */
@@ -5086,7 +5201,7 @@
          * or not a valid float.
          */
         public static float getFloat(ContentResolver cr, String name, float def) {
-            return getFloatForUser(cr, name, def, UserHandle.myUserId());
+            return getFloatForUser(cr, name, def, cr.getUserId());
         }
 
         /** @hide */
@@ -5120,7 +5235,7 @@
          */
         public static float getFloat(ContentResolver cr, String name)
                 throws SettingNotFoundException {
-            return getFloatForUser(cr, name, UserHandle.myUserId());
+            return getFloatForUser(cr, name, cr.getUserId());
         }
 
         /** @hide */
@@ -5151,7 +5266,7 @@
          * @return true if the value was set, false on database errors
          */
         public static boolean putFloat(ContentResolver cr, String name, float value) {
-            return putFloatForUser(cr, name, value, UserHandle.myUserId());
+            return putFloatForUser(cr, name, value, cr.getUserId());
         }
 
         /** @hide */
@@ -5328,6 +5443,17 @@
                 "autofill_user_data_max_field_classification_size";
 
         /**
+         * Defines value returned by
+         * {@link android.service.autofill.UserData#getMaxCategoryCount()}.
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
+                "autofill_user_data_max_category_count";
+
+        /**
          * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
          *
          * @hide
@@ -5361,6 +5487,15 @@
         public static final String USER_SETUP_COMPLETE = "user_setup_complete";
 
         /**
+         * The current state of device personalization.
+         *
+         * @hide
+         * @see UserSetupPersonalization
+         */
+        public static final String USER_SETUP_PERSONALIZATION_STATE =
+                "user_setup_personalization_state";
+
+        /**
          * Whether the current user has been set up via setup wizard (0 = false, 1 = true)
          * This value differs from USER_SETUP_COMPLETE in that it can be reset back to 0
          * in case SetupWizard has been re-enabled on TV devices.
@@ -5386,32 +5521,6 @@
          */
         public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
 
-        private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
-            @Override
-            public boolean validate(String value) {
-                if (value == null) {
-                    return false;
-                }
-                String[] inputMethods = value.split(":");
-                boolean valid = true;
-                for (String inputMethod : inputMethods) {
-                    if (inputMethod.length() == 0) {
-                        return false;
-                    }
-                    String[] subparts = inputMethod.split(";");
-                    for (String subpart : subparts) {
-                        // allow either a non negative integer or a ComponentName
-                        valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
-                                || COMPONENT_NAME_VALIDATOR.validate(subpart));
-                    }
-                    if (!valid) {
-                        return false;
-                    }
-                }
-                return valid;
-            }
-        };
-
         /**
          * List of system input methods that are currently disabled.  This is a string
          * containing the IDs of all disabled input methods, each ID separated
@@ -5510,6 +5619,27 @@
         public static final String LOCATION_MODE = "location_mode";
 
         /**
+         * The App or module that changes the location mode.
+         * @hide
+         */
+        public static final String LOCATION_CHANGER = "location_changer";
+        /**
+         * The location changer is unknown or unable to detect.
+         * @hide
+         */
+        public static final int LOCATION_CHANGER_UNKNOWN = 0;
+        /**
+         * Location settings in system settings.
+         * @hide
+         */
+        public static final int LOCATION_CHANGER_SYSTEM_SETTINGS = 1;
+        /**
+         * The location icon in drop down notification drawer.
+         * @hide
+         */
+        public static final int LOCATION_CHANGER_QUICK_SETTINGS = 2;
+
+        /**
          * Location access disabled.
          *
          * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
@@ -6109,10 +6239,13 @@
 
         /**
          * Integer property that specifies the type of color space adjustment to
-         * perform. Valid values are defined in AccessibilityManager:
+         * perform. Valid values are defined in AccessibilityManager and Settings arrays.xml:
          * - AccessibilityManager.DALTONIZER_DISABLED = -1
          * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0
-         * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12
+         * - <item>@string/daltonizer_mode_protanomaly</item> = 11
+         * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY and
+         *       <item>@string/daltonizer_mode_deuteranomaly</item> = 12
+         * - <item>@string/daltonizer_mode_tritanomaly</item> = 13
          *
          * @hide
          */
@@ -6120,7 +6253,8 @@
                 "accessibility_display_daltonizer";
 
         private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"});
+                new SettingsValidators.DiscreteValueValidator(
+                        new String[] {"-1", "0", "11", "12", "13"});
 
         /**
          * Setting that specifies whether automatic click when the mouse pointer stops moving is
@@ -7043,6 +7177,14 @@
                 SHOW_ROTATION_SUGGESTIONS_ENABLED;
 
         /**
+         * The number of accepted rotation suggestions. Used to determine if the user has been
+         * introduced to rotation suggestions.
+         * @hide
+         */
+        public static final String NUM_ROTATION_SUGGESTIONS_ACCEPTED =
+                "num_rotation_suggestions_accepted";
+
+        /**
          * Read only list of the service components that the current user has explicitly allowed to
          * see and assist with all of the user's notifications.
          *
@@ -7318,7 +7460,8 @@
          */
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
-        private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+        private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Control the color temperature of Night Display, represented in Kelvin.
@@ -7601,6 +7744,24 @@
          */
         public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants";
 
+
+        /**
+         * Local transport parameters so we can configure it for tests.
+         * This is encoded as a key=value list, separated by commas.
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * fake_encryption_flag  (boolean)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         */
+        public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS =
+                "backup_local_transport_parameters";
+
         /**
          * Flag to set if the system should predictively attempt to re-enable Bluetooth while
          * the user is driving.
@@ -7609,6 +7770,53 @@
         public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
 
         /**
+         * What behavior should be invoked when the volume hush gesture is triggered
+         * One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
+         *
+         * @hide
+         */
+        public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
+
+        /** @hide */ public static final int VOLUME_HUSH_OFF = 0;
+        /** @hide */ public static final int VOLUME_HUSH_VIBRATE = 1;
+        /** @hide */ public static final int VOLUME_HUSH_MUTE = 2;
+
+        private static final Validator VOLUME_HUSH_GESTURE_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
+        /**
+         * The number of times (integer) the user has manually enabled battery saver.
+         * @hide
+         */
+        public static final String LOW_POWER_MANUAL_ACTIVATION_COUNT =
+                "low_power_manual_activation_count";
+
+        /**
+         * Whether the "first time battery saver warning" dialog needs to be shown (0: default)
+         * or not (1).
+         *
+         * @hide
+         */
+        public static final String LOW_POWER_WARNING_ACKNOWLEDGED =
+                "low_power_warning_acknowledged";
+
+        /**
+         * 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been
+         * suppressed.
+         * @hide
+         */
+        public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION =
+                "suppress_auto_battery_saver_suggestion";
+
+        /**
+         * List of packages, which data need to be unconditionally cleared before full restore.
+         * Type: string
+         * @hide
+         */
+        public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
+                "packages_to_clear_data_before_full_restore";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -7630,7 +7838,6 @@
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
             ENABLED_ACCESSIBILITY_SERVICES,
             ENABLED_VR_LISTENERS,
-            ENABLED_INPUT_METHODS,
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
             ACCESSIBILITY_ENABLED,
@@ -7707,6 +7914,7 @@
             SCREENSAVER_ACTIVATE_ON_SLEEP,
             LOCKDOWN_IN_POWER_MENU,
             SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+            VOLUME_HUSH_GESTURE
         };
 
         /**
@@ -7736,7 +7944,6 @@
             VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
                     ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
             VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
-            VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
             VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
                     TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
             VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
@@ -7844,6 +8051,7 @@
             VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR);
             VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                     SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR);
+            VALIDATORS.put(VOLUME_HUSH_GESTURE, VOLUME_HUSH_GESTURE_VALIDATOR);
             VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS,
                     ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting
             VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT,
@@ -7881,9 +8089,14 @@
             CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
+            CLONE_TO_MANAGED_PROFILE.add(LOCATION_CHANGER);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
             CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
+            if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) {
+                CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER);
+                CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE);
+            }
         }
 
         /** @hide */
@@ -7936,7 +8149,7 @@
          */
         @Deprecated
         public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
-            return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId());
+            return isLocationProviderEnabledForUser(cr, provider, cr.getUserId());
         }
 
         /**
@@ -7968,7 +8181,7 @@
         @Deprecated
         public static final void setLocationProviderEnabled(ContentResolver cr,
                 String provider, boolean enabled) {
-            setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId());
+            setLocationProviderEnabledForUser(cr, provider, enabled, cr.getUserId());
         }
 
         /**
@@ -8310,10 +8523,10 @@
         private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * URI for the "wireless charging started" sound.
+         * URI for the "wireless charging started" and "wired charging started" sound.
          * @hide
          */
-        public static final String WIRELESS_CHARGING_STARTED_SOUND =
+        public static final String CHARGING_STARTED_SOUND =
                 "wireless_charging_started_sound";
 
         /**
@@ -8483,9 +8696,8 @@
          *
          * @see android.service.euicc.EuiccService
          * @hide
-         *
-         * TODO(b/35851809): Make this a SystemApi.
          */
+        @SystemApi
         public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
 
         /**
@@ -8496,6 +8708,7 @@
          * (0 = false, 1 = true)
          * @hide
          */
+        @SystemApi
         public static final String EUICC_PROVISIONED = "euicc_provisioned";
 
         /**
@@ -8644,21 +8857,6 @@
             "location_background_throttle_package_whitelist";
 
         /**
-         * The interval in milliseconds at which wifi scan requests will be throttled when they are
-         * coming from the background.
-         * @hide
-         */
-        public static final String WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS =
-                "wifi_scan_background_throttle_interval_ms";
-
-        /**
-         * Packages that are whitelisted to be exempt for wifi background throttling.
-         * @hide
-         */
-        public static final String WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST =
-                "wifi_scan_background_throttle_package_whitelist";
-
-        /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
@@ -8701,6 +8899,7 @@
        /** {@hide} */
        public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
        /** {@hide} */
+       @Deprecated
        public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
        /** {@hide} */
        public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
@@ -8749,6 +8948,14 @@
         */
        public static final String NETWORK_SCORER_APP = "network_scorer_app";
 
+        /**
+         * Whether night display forced auto mode is available.
+         * 0 = unavailable, 1 = available.
+         * @hide
+         */
+        public static final String NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE =
+                "night_display_forced_auto_mode_available";
+
        /**
         * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment
         * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
@@ -8999,14 +9206,28 @@
          */
         public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
 
-       /**
-        * List of carrier apps which are whitelisted to prompt the user for install when
-        * a sim card with matching uicc carrier privilege rules is inserted.
-        *
-        * The value is "package1;package2;..."
-        * @hide
-        */
-       public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+        /**
+         * List of certificate (hex string representation of the application's certificate - SHA-1
+         * or SHA-256) and carrier app package pairs which are whitelisted to prompt the user for
+         * install when a sim card with matching UICC carrier privilege rules is inserted.  The
+         * certificate is used as a key, so the certificate encoding here must be the same as the
+         * certificate encoding used on the SIM.
+         *
+         * The value is "cert1:package1;cert2:package2;..."
+         * @hide
+         */
+        @SystemApi
+        public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+
+        /**
+         * Map of package name to application names. The application names cannot and will not be
+         * localized. App names may not contain colons or semicolons.
+         *
+         * The value is "packageName1:appName1;packageName2:appName2;..."
+         * @hide
+         */
+        @SystemApi
+        public static final String CARRIER_APP_NAMES = "carrier_app_names";
 
        /**
         * USB Mass Storage Enabled
@@ -9252,17 +9473,6 @@
         private static final Validator WIFI_WAKEUP_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Value to specify if Wi-Fi Wakeup is available.
-         *
-         * Wi-Fi Wakeup will only operate if it's available
-         * and {@link #WIFI_WAKEUP_ENABLED} is true.
-         *
-         * Type: int (0 for false, 1 for true)
-         * @hide
-         */
-        public static final String WIFI_WAKEUP_AVAILABLE = "wifi_wakeup_available";
-
-        /**
          * Value to specify whether network quality scores and badging should be shown in the UI.
          *
          * Type: int (0 for false, 1 for true)
@@ -9398,6 +9608,12 @@
         public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
                 "ble_scan_low_latency_interval_ms";
 
+        /**
+         * The mode that BLE scanning clients will be moved to when in the background.
+         * @hide
+         */
+        public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+
        /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
@@ -9475,6 +9691,21 @@
         public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
                 "wifi_connected_mac_randomization_enabled";
 
+        /**
+         * Parameters to adjust the performance of framework wifi scoring methods.
+         * <p>
+         * Encoded as a comma-separated key=value list, for example:
+         *   "rssi5=-80:-77:-70:-57,rssi2=-83:-80:-73:-60,horizon=15"
+         * This is intended for experimenting with new parameter values,
+         * and is normally unset or empty. The example does not include all
+         * parameters that may be honored.
+         * Default values are provided by code or device configurations.
+         * Errors in the parameters will cause the entire setting to be ignored.
+         * @hide
+         */
+        public static final String WIFI_SCORE_PARAMS =
+                "wifi_score_params";
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -10150,31 +10381,6 @@
                 "battery_saver_device_specific_constants";
 
         /**
-         * Battery anomaly detection specific settings
-         * This is encoded as a key=value list, separated by commas.
-         * wakeup_blacklisted_tags is a string, encoded as a set of tags, encoded via
-         * {@link Uri#encode(String)}, separated by colons. Ex:
-         *
-         * "anomaly_detection_enabled=true,wakelock_threshold=2000,wakeup_alarm_enabled=true,"
-         * "wakeup_alarm_threshold=10,wakeup_blacklisted_tags=tag1:tag2:with%2Ccomma:with%3Acolon"
-         *
-         * The following keys are supported:
-         *
-         * <pre>
-         * anomaly_detection_enabled       (boolean)
-         * wakelock_enabled                (boolean)
-         * wakelock_threshold              (long)
-         * wakeup_alarm_enabled            (boolean)
-         * wakeup_alarm_threshold          (long)
-         * wakeup_blacklisted_tags         (string)
-         * bluetooth_scan_enabled          (boolean)
-         * bluetooth_scan_threshold        (long)
-         * </pre>
-         * @hide
-         */
-        public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
-
-        /**
          * Battery tip specific settings
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -10203,6 +10409,45 @@
         public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants";
 
         /**
+         * Battery anomaly detection specific settings
+         * This is encoded as a key=value list, separated by commas.
+         * wakeup_blacklisted_tags is a string, encoded as a set of tags, encoded via
+         * {@link Uri#encode(String)}, separated by colons. Ex:
+         *
+         * "anomaly_detection_enabled=true,wakelock_threshold=2000,wakeup_alarm_enabled=true,"
+         * "wakeup_alarm_threshold=10,wakeup_blacklisted_tags=tag1:tag2:with%2Ccomma:with%3Acolon"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * anomaly_detection_enabled       (boolean)
+         * wakelock_enabled                (boolean)
+         * wakelock_threshold              (long)
+         * wakeup_alarm_enabled            (boolean)
+         * wakeup_alarm_threshold          (long)
+         * wakeup_blacklisted_tags         (string)
+         * bluetooth_scan_enabled          (boolean)
+         * bluetooth_scan_threshold        (long)
+         * </pre>
+         * @hide
+         */
+        public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
+
+        /**
+         * An integer to show the version of the anomaly config. Ex: 1, which means
+         * current version is 1.
+         * @hide
+         */
+        public static final String ANOMALY_CONFIG_VERSION = "anomaly_config_version";
+
+        /**
+         * A base64-encoded string represents anomaly stats config, used for
+         * {@link android.app.StatsManager}.
+         * @hide
+         */
+        public static final String ANOMALY_CONFIG = "anomaly_config";
+
+        /**
          * Always on display(AOD) specific settings
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -10233,6 +10478,27 @@
         public static final String SYS_VDSO = "sys_vdso";
 
         /**
+        * UidCpuPower global setting. This links the sys.uidcpupower system property.
+        * The following values are supported:
+        * 0 -> /proc/uid_cpupower/* are disabled
+        * 1 -> /proc/uid_cpupower/* are enabled
+        * Any other value defaults to enabled.
+        * @hide
+        */
+        public static final String SYS_UIDCPUPOWER = "sys_uidcpupower";
+
+        /**
+        * traced global setting. This controls weather the deamons: traced and
+        * traced_probes run. This links the sys.traced system property.
+        * The following values are supported:
+        * 0 -> traced and traced_probes are disabled
+        * 1 -> traced and traced_probes are enabled
+        * Any other value defaults to disabled.
+        * @hide
+        */
+        public static final String SYS_TRACED = "sys_traced";
+
+        /**
          * An integer to reduce the FPS by this factor. Only for experiments. Need to reboot the
          * device for this setting to take full effect.
          *
@@ -10241,6 +10507,15 @@
         public static final String FPS_DEVISOR = "fps_divisor";
 
         /**
+         * Flag to enable or disable display panel low power mode (lpm)
+         * false -> Display panel power saving mode is disabled.
+         * true  -> Display panel power saving mode is enabled.
+         *
+         * @hide
+         */
+        public static final String DISPLAY_PANEL_LPM = "display_panel_lpm";
+
+        /**
          * App standby (app idle) specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          * <p>
@@ -10381,17 +10656,30 @@
 
         /**
          * TextClassifier specific settings.
-         * This is encoded as a key=value list, separated by commas. Ex:
+         * This is encoded as a key=value list, separated by commas. String[] types like
+         * entity_list_default use ":" as delimiter for values. Ex:
          *
          * <pre>
-         * smart_selection_dark_launch              (boolean)
-         * smart_selection_enabled_for_edit_text    (boolean)
+         * smart_linkify_enabled                    (boolean)
+         * system_textclassifier_enabled            (boolean)
+         * model_dark_launch_enabled                (boolean)
+         * smart_selection_enabled                  (boolean)
+         * smart_text_share_enabled                 (boolean)
+         * smart_linkify_enabled                    (boolean)
+         * smart_select_animation_enabled           (boolean)
+         * suggest_selection_max_range_length       (int)
+         * classify_text_max_range_length           (int)
+         * generate_links_max_text_length           (int)
+         * generate_links_log_sample_rate           (int)
+         * entity_list_default                      (String[])
+         * entity_list_not_editable                 (String[])
+         * entity_list_editable                     (String[])
          * </pre>
          *
          * <p>
          * Type: string
          * @hide
-         * see also android.view.textclassifier.TextClassifierConstants
+         * see also android.view.textclassifier.TextClassificationConstants
          */
         public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
 
@@ -10404,6 +10692,7 @@
          * track_cpu_times_by_proc_state (boolean)
          * track_cpu_active_cluster_time (boolean)
          * read_binary_cpu_time          (boolean)
+         * proc_state_cpu_times_read_delay_ms (long)
          * </pre>
          *
          * <p>
@@ -10414,13 +10703,38 @@
         public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants";
 
         /**
+         * SyncManager specific settings.
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * @see com.android.server.content.SyncManagerConstants
+         */
+        public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
+
+        /**
          * Whether or not App Standby feature is enabled. This controls throttling of apps
          * based on usage patterns and predictions.
          * Type: int (0 for false, 1 for true)
          * Default: 1
          * @hide
          */
-        public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled";
+        public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
+
+        /**
+         * Whether or not app auto restriction is enabled. When it is enabled, settings app will
+         * auto restrict the app if it has bad behavior(e.g. hold wakelock for long time).
+         *
+         * Type: boolean (0 for false, 1 for true)
+         * Default: 1
+         *
+         * @hide
+         */
+        public static final String APP_AUTO_RESTRICTION_ENABLED =
+                "app_auto_restriction_enabled";
+
+        private static final Validator APP_AUTO_RESTRICTION_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
 
         /**
          * Feature flag to enable or disable the Forced App Standby feature.
@@ -10440,6 +10754,49 @@
                 = "forced_app_standby_for_small_battery_enabled";
 
         /**
+         * Whether or not to enable the Off Body, Radios Off feature on small battery devices.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
+                = "off_body_radios_off_for_small_battery_enabled";
+
+        /**
+         * How long after the device goes off body to disable radios, in milliseconds.
+         * Type: long
+         * Default: 10 minutes
+         * @hide
+         */
+        public static final String OFF_BODY_RADIOS_OFF_DELAY_MS = "off_body_radios_off_delay_ms";
+
+        /**
+         * Whether or not to turn on Wifi when proxy is disconnected.
+         * Type: int (0 for false, 1 for true)
+         * Default: 1
+         * @hide
+         */
+        public static final String WIFI_ON_WHEN_PROXY_DISCONNECTED
+                = "wifi_on_when_proxy_disconnected";
+
+        /**
+         * Time Only Mode specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * enabled                  (boolean)
+         * disable_tilt_to_wake     (boolean)
+         * disable_touch_to_wake    (boolean)
+         * </pre>
+         * Type: string
+         * @hide
+         */
+        public static final String TIME_ONLY_MODE_CONSTANTS
+                = "time_only_mode_constants";
+
+        /**
          * Whether or not Network Watchlist feature is enabled.
          * Type: int (0 for false, 1 for true)
          * Default: 0
@@ -10448,6 +10805,18 @@
         public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
 
         /**
+         * Flag to keep background restricted profiles running after exiting. If disabled,
+         * the restricted profile can be put into stopped state as soon as the user leaves it.
+         * Type: int (0 for false, 1 for true)
+         *
+         * Overridden by the system based on device information. If null, the value specified
+         * by {@code config_keepRestrictedProfilesInBackground} is used.
+         *
+         * @hide
+         */
+        public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background";
+
+        /**
          * Get the key that retrieves a bluetooth headset's priority.
          * @hide
          */
@@ -10659,23 +11028,47 @@
         public static final String SHOW_PROCESSES = "show_processes";
 
         /**
-         * If 1 low power mode is enabled.
+         * If 1 low power mode (aka battery saver) is enabled.
          * @hide
          */
         @TestApi
         public static final String LOW_POWER_MODE = "low_power";
 
         /**
-         * Battery level [1-99] at which low power mode automatically turns on.
+         * If 1, battery saver ({@link #LOW_POWER_MODE}) will be re-activated after the device
+         * is unplugged from a charger or rebooted.
+         * @hide
+         */
+        @TestApi
+        public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
+
+        /**
+         * Battery level [1-100] at which low power mode automatically turns on.
          * If 0, it will not automatically turn on.
          * @hide
          */
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
 
         private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 99);
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
 
-         /**
+
+        /**
+         * The max value for {@link #LOW_POWER_MODE_TRIGGER_LEVEL}. If this setting is not set
+         * or the value is 0, the default max will be used.
+         *
+         * @hide
+         */
+        public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max";
+
+        /**
+         * See com.android.settingslib.fuelgauge.BatterySaverUtils.
+         * @hide
+         */
+        public static final String LOW_POWER_MODE_SUGGESTION_PARAMS =
+                "low_power_mode_suggestion_params";
+
+        /**
          * If not 0, the activity manager will aggressively finish activities and
          * processes as soon as they are no longer needed.  If 0, the normal
          * extended lifetime is used.
@@ -10946,6 +11339,20 @@
         public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
 
         /**
+         * If 0, turning on dnd manually will last indefinitely.
+         * Else if non-negative, turning on dnd manually will last for this many minutes.
+         * Else (if negative), turning on dnd manually will surface a dialog that prompts
+         * user to specify a duration.
+         * @hide
+         */
+        public static final String ZEN_DURATION = "zen_duration";
+
+        private static final Validator ZEN_DURATION_VALIDATOR = ANY_INTEGER_VALIDATOR;
+
+        /** @hide */ public static final int ZEN_DURATION_PROMPT = -1;
+        /** @hide */ public static final int ZEN_DURATION_FOREVER = 0;
+
+        /**
          * Defines global heads up toggle.  One of HEADS_UP_OFF, HEADS_UP_ON.
          *
          * @hide
@@ -11248,6 +11655,61 @@
          */
         public static final String OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION =
                 "override_settings_provider_restore_any_version";
+        /**
+         * Flag to toggle whether system services report attribution chains when they attribute
+         * battery use via a {@code WorkSource}.
+         *
+         * Type: int (0 to disable, 1 to enable)
+         *
+         * @hide
+         */
+        public static final String CHAINED_BATTERY_ATTRIBUTION_ENABLED =
+                "chained_battery_attribution_enabled";
+
+        /**
+         * The packages whitelisted to be run in autofill compatibility mode. The list
+         * of packages is {@code ":"} colon delimited, and each entry has the name of the
+         * package and an optional list of url bar resource ids (the list is delimited by
+         * brackets&mdash{@code [} and {@code ]}&mdash and is also comma delimited).
+         *
+         * <p>For example, a list with 3 packages {@code p1}, {@code p2}, and {@code p3}, where
+         * package {@code p1} have one id ({@code url_bar}, {@code p2} has none, and {@code p3 }
+         * have 2 ids {@code url_foo} and {@code url_bas}) would be
+         * {@code p1[url_bar]:p2:p3[url_foo,url_bas]}
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
+                "autofill_compat_mode_allowed_packages";
+
+        /**
+         * Exemptions to the hidden API blacklist.
+         *
+         * @hide
+         */
+        @TestApi
+        public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
+                "hidden_api_blacklist_exemptions";
+
+        /**
+         * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
+         * operation (in ms).
+         *
+         * @hide
+         */
+        public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT =
+                "sound_trigger_detection_service_op_timeout";
+
+        /**
+         * Maximum number of {@link android.media.soundtrigger.SoundTriggerDetectionService}
+         * operations per day.
+         *
+         * @hide
+         */
+        public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY =
+                "max_sound_trigger_detection_service_ops_per_day";
 
         /**
          * Settings to backup. This is here so that it's in the same place as the settings
@@ -11268,6 +11730,7 @@
         public static final String[] SETTINGS_TO_BACKUP = {
             BUGREPORT_IN_POWER_MENU,
             STAY_ON_WHILE_PLUGGED_IN,
+            APP_AUTO_RESTRICTION_ENABLED,
             AUTO_TIME,
             AUTO_TIME_ZONE,
             POWER_SOUNDS_ENABLED,
@@ -11288,7 +11751,8 @@
             BLUETOOTH_ON,
             PRIVATE_DNS_MODE,
             PRIVATE_DNS_SPECIFIER,
-            SOFT_AP_TIMEOUT_ENABLED
+            SOFT_AP_TIMEOUT_ENABLED,
+            ZEN_DURATION,
         };
 
         /**
@@ -11320,12 +11784,16 @@
             VALIDATORS.put(DOCK_AUDIO_MEDIA_ENABLED, DOCK_AUDIO_MEDIA_ENABLED_VALIDATOR);
             VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR);
             VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR);
+            VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
+                    LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR);
             VALIDATORS.put(BLUETOOTH_ON, BLUETOOTH_ON_VALIDATOR);
             VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR);
             VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR);
             VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
             VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
                     WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
+            VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR);
+            VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
         }
 
         /**
@@ -11384,7 +11852,7 @@
          * @return the corresponding value, or null if not present
          */
         public static String getString(ContentResolver resolver, String name) {
-            return getStringForUser(resolver, name, UserHandle.myUserId());
+            return getStringForUser(resolver, name, resolver.getUserId());
         }
 
         /** @hide */
@@ -11407,7 +11875,7 @@
          */
         public static boolean putString(ContentResolver resolver,
                 String name, String value) {
-            return putStringForUser(resolver, name, value, null, false, UserHandle.myUserId());
+            return putStringForUser(resolver, name, value, null, false, resolver.getUserId());
         }
 
         /**
@@ -11456,7 +11924,7 @@
                 @NonNull String name, @Nullable String value, @Nullable String tag,
                 boolean makeDefault) {
             return putStringForUser(resolver, name, value, tag, makeDefault,
-                    UserHandle.myUserId());
+                    resolver.getUserId());
         }
 
         /**
@@ -11477,7 +11945,7 @@
         public static void resetToDefaults(@NonNull ContentResolver resolver,
                 @Nullable String tag) {
             resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS,
-                    UserHandle.myUserId());
+                    resolver.getUserId());
         }
 
         /**
@@ -11791,8 +12259,6 @@
           */
         public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt";
 
-
-
         /** User preferred subscriptions setting.
           * This holds the details of the user selected subscription from the card and
           * the activation status. Each settings string have the comma separated values
@@ -11950,6 +12416,28 @@
                 "enable_gnss_raw_meas_full_tracking";
 
         /**
+         * Whether the notification should be ongoing (persistent) when a carrier app install is
+         * required.
+         *
+         * The value is a boolean (1 or 0).
+         * @hide
+         */
+        @SystemApi
+        public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
+                "install_carrier_app_notification_persistent";
+
+        /**
+         * The amount of time (ms) to hide the install carrier app notification after the user has
+         * ignored it. After this time passes, the notification will be shown again
+         *
+         * The value is a long
+         * @hide
+         */
+        @SystemApi
+        public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
+                "install_carrier_app_notification_sleep_millis";
+
+        /**
          * Whether we've enabled zram on this device. Takes effect on
          * reboot. The value "1" enables zram; "0" disables it, and
          * everything else is unspecified.
@@ -11959,11 +12447,33 @@
                 "zram_enabled";
 
         /**
-         * Whether smart replies in notifications are enabled.
+         * Whether we have enable CPU frequency scaling for this device.
+         * For Wear, default is disable.
+         *
+         * The value is "1" for enable, "0" for disable.
          * @hide
          */
-        public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS =
-                "enable_smart_replies_in_notifications";
+        public static final String CPU_SCALING_ENABLED =
+                "cpu_frequency_scaling_enabled";
+
+        /**
+         * Configuration flags for smart replies in notifications.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "enabled=1,max_squeeze_remeasure_count=3"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * enabled                         (boolean)
+         * requires_targeting_p            (boolean)
+         * max_squeeze_remeasure_attempts  (int)
+         * </pre>
+         * @see com.android.systemui.statusbar.policy.SmartReplyConstants
+         * @hide
+         */
+        public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS =
+                "smart_replies_in_notifications_flags";
 
         /**
          * If nonzero, crashes in foreground processes will bring up a dialog.
@@ -11984,6 +12494,44 @@
          * @hide
          */
         public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
+
+        /**
+         * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
+         * @hide
+         */
+        public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
+
+        /**
+         * Backup and restore agent timeout parameters.
+         * These parameters are represented by a comma-delimited key-value list.
+         *
+         * The following strings are supported as keys:
+         * <pre>
+         *     kv_backup_agent_timeout_millis         (long)
+         *     full_backup_agent_timeout_millis       (long)
+         *     shared_backup_agent_timeout_millis     (long)
+         *     restore_agent_timeout_millis           (long)
+         *     restore_agent_finished_timeout_millis  (long)
+         * </pre>
+         *
+         * They map to milliseconds represented as longs.
+         *
+         * Ex: "kv_backup_agent_timeout_millis=30000,full_backup_agent_timeout_millis=300000"
+         *
+         * @hide
+         */
+        public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS =
+                "backup_agent_timeout_parameters";
+
+        /**
+         * Whether we have enabled swapping on this device. For Wear, default is
+         * enabled.
+         *
+         * The value is "1" for enable, "0" for disable.
+         * @hide
+         */
+         public static final String SWAP_ENABLED = "swap_enabled";
+
     }
 
     /**
diff --git a/android/provider/SettingsSlicesContract.java b/android/provider/SettingsSlicesContract.java
new file mode 100644
index 0000000..7dc9488
--- /dev/null
+++ b/android/provider/SettingsSlicesContract.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+
+/**
+ * Provides a contract for platform-supported Settings {@link android.app.slice.Slice Slices}.
+ * <p>
+ * Contains definitions for the supported {@link android.app.slice.SliceProvider SliceProvider}
+ * authority, authority {@link Uri}, and key constants.
+ * <p>
+ * {@link android.app.slice.Slice Slice} presenters interested in learning meta-data about the
+ * {@link android.app.slice.Slice Slice} should read the {@link android.app.slice.Slice Slice}
+ * object at runtime.
+ * <p>
+ * {@link Uri} builder example:
+ * <pre>
+ * Uri wifiActionUri = BASE_URI
+ *         .buildUpon()
+ *         .appendPath(PATH_SETTING_ACTION)
+ *         .appendPath(KEY_WIFI)
+ *         .build();
+ * Uri bluetoothIntentUri = BASE_URI
+ *         .buildUpon()
+ *         .appendPath(PATH_SETTING_INTENT)
+ *         .appendPath(KEY_BLUETOOTH)
+ *         .build();
+ * </pre>
+ */
+public class SettingsSlicesContract {
+    private SettingsSlicesContract() {
+    }
+
+    /**
+     * Authority for platform Settings Slices.
+     */
+    public static final String AUTHORITY = "android.settings.slices";
+
+    /**
+     * A content:// style uri to the Settings Slices authority, {@link #AUTHORITY}.
+     */
+    public static final Uri BASE_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(AUTHORITY)
+            .build();
+
+    /**
+     * {@link Uri} path indicating that the requested {@link android.app.slice.Slice Slice} should
+     * have inline controls for the corresponding setting.
+     * <p>
+     * This path will only contain Slices defined by keys in this class.
+     */
+    public static final String PATH_SETTING_ACTION = "action";
+
+    /**
+     * {@link Uri} path indicating that the requested {@link android.app.slice.Slice Slice} should
+     * be {@link android.content.Intent Intent}-only.
+     * <p>
+     * {@link android.app.slice.Slice Slices} with actions should use the {@link
+     * #PATH_SETTING_ACTION} path.
+     * <p>
+     * This path will only contain Slices defined by keys in this class
+     */
+    public static final String PATH_SETTING_INTENT = "intent";
+
+    /**
+     * {@link Uri} key for the Airplane Mode setting.
+     */
+    public static final String KEY_AIRPLANE_MODE = "airplane_mode";
+
+    /**
+     * {@link Uri} key for the Battery Saver setting.
+     */
+    public static final String KEY_BATTERY_SAVER = "battery_saver";
+
+    /**
+     * {@link Uri} key for the Bluetooth setting.
+     */
+    public static final String KEY_BLUETOOTH = "bluetooth";
+
+    /**
+     * {@link Uri} key for the Location setting.
+     */
+    public static final String KEY_LOCATION = "location";
+
+    /**
+     * {@link Uri} key for the Wi-fi setting.
+     */
+    public static final String KEY_WIFI = "wifi";
+}
diff --git a/android/provider/Telephony.java b/android/provider/Telephony.java
index 8c45724..f1653ce 100644
--- a/android/provider/Telephony.java
+++ b/android/provider/Telephony.java
@@ -33,6 +33,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Patterns;
 
@@ -1102,10 +1103,15 @@
                 "android.provider.Telephony.MMS_DOWNLOADED";
 
             /**
-             * Broadcast Action: A debug code has been entered in the dialer. These "secret codes"
-             * are used to activate developer menus by dialing certain codes. And they are of the
-             * form {@code *#*#&lt;code&gt;#*#*}. The intent will have the data URI:
-             * {@code android_secret_code://&lt;code&gt;}.
+             * Broadcast Action: A debug code has been entered in the dialer. This intent is
+             * broadcast by the system and OEM telephony apps may need to receive these broadcasts.
+             * These "secret codes" are used to activate developer menus by dialing certain codes.
+             * And they are of the form {@code *#*#&lt;code&gt;#*#*}. The intent will have the data
+             * URI: {@code android_secret_code://&lt;code&gt;}. It is possible that a manifest
+             * receiver would be woken up even if it is not currently running.
+             *
+             * <p>Requires {@code android.Manifest.permission#CONTROL_INCALL_EXPERIENCE} to
+             * send and receive.</p>
              */
             @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
             public static final String SECRET_CODE_ACTION =
@@ -2731,6 +2737,7 @@
          * This should be spread to other technologies,
          * but is currently only used for LTE (14) and eHRPD (13).
          * <P>Type: INTEGER</P>
+         * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead
          */
         @Deprecated
         public static final String BEARER = "bearer";
@@ -2743,13 +2750,14 @@
          * Bitmask for a radio tech R is (1 << (R - 1))
          * <P>Type: INTEGER</P>
          * @hide
+         * @deprecated this column is no longer supported, use {@link #NETWORK_TYPE_BITMASK} instead
          */
         @Deprecated
         public static final String BEARER_BITMASK = "bearer_bitmask";
 
         /**
          * Radio technology (network type) bitmask.
-         * To check what values can be contained, refer to
+         * To check what values can be contained, refer to the NETWORK_TYPE_ constants in
          * {@link android.telephony.TelephonyManager}.
          * Bitmask for a radio tech R is (1 << (R - 1))
          * <P>Type: INTEGER</P>
@@ -3157,8 +3165,8 @@
             values.put(RIL_VOICE_RADIO_TECHNOLOGY, state.getRilVoiceRadioTechnology());
             values.put(RIL_DATA_RADIO_TECHNOLOGY, state.getRilDataRadioTechnology());
             values.put(CSS_INDICATOR, state.getCssIndicator());
-            values.put(NETWORK_ID, state.getNetworkId());
-            values.put(SYSTEM_ID, state.getSystemId());
+            values.put(NETWORK_ID, state.getCdmaNetworkId());
+            values.put(SYSTEM_ID, state.getCdmaSystemId());
             values.put(CDMA_ROAMING_INDICATOR, state.getCdmaRoamingIndicator());
             values.put(CDMA_DEFAULT_ROAMING_INDICATOR, state.getCdmaDefaultRoamingIndicator());
             values.put(CDMA_ERI_ICON_INDEX, state.getCdmaEriIconIndex());
@@ -3288,13 +3296,13 @@
         public static final String CSS_INDICATOR = "css_indicator";
 
         /**
-         * This is the same as {@link ServiceState#getNetworkId()}.
+         * This is the same as {@link ServiceState#getCdmaNetworkId()}.
          * @hide
          */
         public static final String NETWORK_ID = "network_id";
 
         /**
-         * This is the same as {@link ServiceState#getSystemId()}.
+         * This is the same as {@link ServiceState#getCdmaSystemId()}.
          * @hide
          */
         public static final String SYSTEM_ID = "system_id";
@@ -3345,73 +3353,118 @@
     }
 
     /**
-     * Contains carrier identification information.
-     * @hide
+     * Contains carrier identification information for the current subscriptions.
+     * @see SubscriptionManager#getActiveSubscriptionIdList()
      */
-    public static final class CarrierIdentification implements BaseColumns {
+    public static final class CarrierId implements BaseColumns {
         /**
-         * Numeric operator ID (as String). {@code MCC + MNC}
-         * <P>Type: TEXT </P>
+         * Not instantiable.
+         * @hide
          */
-        public static final String MCCMNC = "mccmnc";
+        private CarrierId() {}
 
         /**
-         * Group id level 1 (as String).
-         * <P>Type: TEXT </P>
+         * The {@code content://} style URI for this provider.
          */
-        public static final String GID1 = "gid1";
+        public static final Uri CONTENT_URI = Uri.parse("content://carrier_id");
 
         /**
-         * Group id level 2 (as String).
-         * <P>Type: TEXT </P>
+         * The authority string for the CarrierId Provider
+         * @hide
          */
-        public static final String GID2 = "gid2";
+        public static final String AUTHORITY = "carrier_id";
+
 
         /**
-         * Public Land Mobile Network name.
-         * <P>Type: TEXT </P>
+         * Generates a content {@link Uri} used to receive updates on carrier identity change
+         * on the given subscriptionId
+         * <p>
+         * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+         * carrier identity {@link TelephonyManager#getSimCarrierId()}
+         * while your app is running. You can also use a {@link JobService} to ensure your app
+         * is notified of changes to the {@link Uri} even when it is not running.
+         * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+         * updates to the {@link Uri}.
+         *
+         * @param subscriptionId the subscriptionId to receive updates on
+         * @return the Uri used to observe carrier identity changes
          */
-        public static final String PLMN = "plmn";
+        public static Uri getUriForSubscriptionId(int subscriptionId) {
+            return CONTENT_URI.buildUpon().appendEncodedPath(
+                    String.valueOf(subscriptionId)).build();
+        }
 
         /**
-         * Prefix xpattern of IMSI (International Mobile Subscriber Identity).
+         * A user facing carrier name.
+         * @see TelephonyManager#getSimCarrierIdName()
          * <P>Type: TEXT </P>
          */
-        public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
-
-        /**
-         * Service Provider Name.
-         * <P>Type: TEXT </P>
-         */
-        public static final String SPN = "spn";
-
-        /**
-         * Prefer APN name.
-         * <P>Type: TEXT </P>
-         */
-        public static final String APN = "apn";
-
-        /**
-         * Prefix of Integrated Circuit Card Identifier.
-         * <P>Type: TEXT </P>
-         */
-        public static final String ICCID_PREFIX = "iccid_prefix";
-
-        /**
-         * User facing carrier name.
-         * <P>Type: TEXT </P>
-         */
-        public static final String NAME = "carrier_name";
+        public static final String CARRIER_NAME = "carrier_name";
 
         /**
          * A unique carrier id
+         * @see TelephonyManager#getSimCarrierId()
          * <P>Type: INTEGER </P>
          */
-        public static final String CID = "carrier_id";
+        public static final String CARRIER_ID = "carrier_id";
 
         /**
-         * The {@code content://} URI for this table.
+         * Contains mappings between matching rules with carrier id for all carriers.
+         * @hide
          */
-        public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification");
+        public static final class All implements BaseColumns {
+            /**
+             * Numeric operator ID (as String). {@code MCC + MNC}
+             * <P>Type: TEXT </P>
+             */
+            public static final String MCCMNC = "mccmnc";
+
+            /**
+             * Group id level 1 (as String).
+             * <P>Type: TEXT </P>
+             */
+            public static final String GID1 = "gid1";
+
+            /**
+             * Group id level 2 (as String).
+             * <P>Type: TEXT </P>
+             */
+            public static final String GID2 = "gid2";
+
+            /**
+             * Public Land Mobile Network name.
+             * <P>Type: TEXT </P>
+             */
+            public static final String PLMN = "plmn";
+
+            /**
+             * Prefix xpattern of IMSI (International Mobile Subscriber Identity).
+             * <P>Type: TEXT </P>
+             */
+            public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
+
+            /**
+             * Service Provider Name.
+             * <P>Type: TEXT </P>
+             */
+            public static final String SPN = "spn";
+
+            /**
+             * Prefer APN name.
+             * <P>Type: TEXT </P>
+             */
+            public static final String APN = "apn";
+
+            /**
+             * Prefix of Integrated Circuit Card Identifier.
+             * <P>Type: TEXT </P>
+             */
+            public static final String ICCID_PREFIX = "iccid_prefix";
+
+            /**
+             * The {@code content://} URI for this table.
+             */
+            public static final Uri CONTENT_URI = Uri.parse("content://carrier_id/all");
+        }
     }
 }
diff --git a/android/provider/VoicemailContract.java b/android/provider/VoicemailContract.java
index c568b6f..140336e 100644
--- a/android/provider/VoicemailContract.java
+++ b/android/provider/VoicemailContract.java
@@ -49,7 +49,8 @@
  * </ul>
  *
  * <P> The minimum permission needed to access this content provider is
- * {@link android.Manifest.permission#ADD_VOICEMAIL}
+ * {@link android.Manifest.permission#ADD_VOICEMAIL} or carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
  *
  * <P>Voicemails are inserted by what is called as a "voicemail source"
  * application, which is responsible for syncing voicemail data between a remote
diff --git a/android/renderscript/ScriptIntrinsic.java b/android/renderscript/ScriptIntrinsic.java
index 4edce84..61211a2 100644
--- a/android/renderscript/ScriptIntrinsic.java
+++ b/android/renderscript/ScriptIntrinsic.java
@@ -17,9 +17,9 @@
 package android.renderscript;
 
 /**
- * Base class for all Intrinsic scripts. An intrinsic a script
- * which implements a pre-defined function. Intrinsics are
- * provided to provide effecient implemtations of common
+ * Base class for all Intrinsic scripts. An intrinsic is a script
+ * that implements a pre-defined function. Intrinsics are
+ * provided to provide efficient implementations of common
  * operations.
  *
  * Not intended for direct use.
diff --git a/android/se/omapi/Channel.java b/android/se/omapi/Channel.java
new file mode 100644
index 0000000..c8efede
--- /dev/null
+++ b/android/se/omapi/Channel.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent an ISO/IEC 7816-4 channel opened to a
+ * Secure Element. It can be either a logical channel or the basic channel. They
+ * can be used to send APDUs to the secure element. Channels are opened by
+ * calling the Session.openBasicChannel(byte[]) or
+ * Session.openLogicalChannel(byte[]) methods.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Channel {
+
+    private static final String TAG = "OMAPI.Channel";
+    private Session mSession;
+    private final ISecureElementChannel mChannel;
+    private final SEService mService;
+    private final Object mLock = new Object();
+
+    Channel(@NonNull SEService service, @NonNull Session session,
+            @NonNull ISecureElementChannel channel) {
+        if (service == null || session == null || channel == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
+        }
+        mService = service;
+        mSession = session;
+        mChannel = channel;
+    }
+
+    /**
+     * Closes this channel to the Secure Element. If the method is called when
+     * the channel is already closed, this method will be ignored. The close()
+     * method shall wait for completion of any pending transmit(byte[] command)
+     * before closing the channel.
+     */
+    public void close() {
+        if (!isClosed()) {
+            synchronized (mLock) {
+                try {
+                    mChannel.close();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error closing channel", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Tells if this channel is closed.
+     *
+     * @return <code>true</code> if the channel is closed or in case of an error.
+     *         <code>false</code> otherwise.
+     */
+    public boolean isClosed() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service not connected to system");
+            return true;
+        }
+        try {
+            return mChannel.isClosed();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in isClosed()");
+            return true;
+        }
+    }
+
+    /**
+     * Returns a boolean telling if this channel is the basic channel.
+     *
+     * @return <code>true</code> if this channel is a basic channel. <code>false</code> if
+     *         this channel is a logical channel.
+     */
+    public boolean isBasicChannel() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        try {
+            return mChannel.isBasicChannel();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+
+    /**
+     * Transmit an APDU command (as per ISO/IEC 7816-4) to the Secure Element. The
+     * underlying layers generate as many TPDUs as necessary to transport this APDU. The
+     * API shall ensure that all available data returned from Secure Element, including
+     * concatenated responses, are retrieved and made available to the calling application. If a
+     * warning status code is received the API wont check for further response data but will
+     * return all data received so far and the warning status code.<br>
+     * The transport part is invisible from the application. The generated response is the
+     * response of the APDU which means that all protocols related responses are handled
+     * inside the API or the underlying implementation.<br>
+     * The transmit method shall support extended length APDU commands independently of
+     * the coding within the ATR.<br>
+     * For status word '61 XX' the API or underlying implementation shall issue a GET
+     * RESPONSE command as specified by ISO 7816-4 standard with LE=XX; for the status
+     * word '6C XX', the API or underlying implementation shall reissue the input command
+     * with LE=XX. For other status words, the API (or underlying implementation) shall return
+     * the complete response including data and status word to the device application. The API
+     * (or underlying implementation) shall not handle internally the received status words. The
+     * channel shall not be closed even if the Secure Element answered with an error code.
+     * The system ensures the synchronization between all the concurrent calls to this method,
+     * and that only one APDU will be sent at a time, irrespective of the number of TPDUs that
+     * might be required to transport it to the SE. The entire APDU communication to this SE is
+     * locked to the APDU.<br>
+     * The channel information in the class byte in the APDU will be ignored. The system will
+     * add any required information to ensure the APDU is transported on this channel.
+     * The only restrictions on the set of commands that can be sent is defined below, the API
+     * implementation shall be able to send all other commands: <br>
+     * <ul>
+     * <li>MANAGE_CHANNEL commands are not allowed.</li>
+     * <li>SELECT by DF Name (p1=04) are not allowed.</li>
+     * <li>CLA bytes with channel numbers are de-masked.</li>
+     * </ul>
+     *
+     * @param command the APDU command to be transmitted, as a byte array.
+     *
+     * @return the response received, as a byte array. The returned byte array contains the data
+     * bytes in the following order:
+     * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
+     *
+     * @throws IOException if there is a communication problem to the reader or the Secure Element.
+     * @throws IllegalStateException if the channel is used after being closed.
+     * @throws IllegalArgumentException if the command byte array is less than 4 bytes long.
+     * @throws IllegalArgumentException if Lc byte is inconsistent with length of the byte array.
+     * @throws IllegalArgumentException if CLA byte is invalid according to [2] (0xff).
+     * @throws IllegalArgumentException if INS byte is invalid according to [2] (0x6x or 0x9x).
+     * @throws SecurityException if the command is filtered by the security policy.
+     * @throws NullPointerException if command is NULL.
+     */
+    public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        synchronized (mLock) {
+            try {
+                byte[] response = mChannel.transmit(command);
+                if (response == null) {
+                    throw new IOException("Error in communicating with Secure Element");
+                }
+                return response;
+            } catch (ServiceSpecificException e) {
+                throw new IOException(e.getMessage());
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Get the session that has opened this channel.
+     *
+     * @return the session object this channel is bound to.
+     */
+    public @NonNull Session getSession() {
+        return mSession;
+    }
+
+    /**
+     * Returns the data as received from the application select command inclusively the status word
+     * received at applet selection.
+     * The returned byte array contains the data bytes in the following order:
+     * [&lt;first data byte&gt;, ..., &lt;last data byte&gt;, &lt;sw1&gt;, &lt;sw2&gt;]
+     * @return The data as returned by the application select command inclusively the status word.
+     * Only the status word if the application select command has no returned data.
+     * Returns null if an application select command has not been performed or the selection
+     * response can not be retrieved by the reader implementation.
+     */
+    public @Nullable byte[] getSelectResponse() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+
+        byte[] response;
+        try {
+            response = mChannel.getSelectResponse();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+
+        if (response != null && response.length == 0) {
+            response = null;
+        }
+        return response;
+    }
+
+    /**
+     * Performs a selection of the next Applet on this channel that matches to the partial AID
+     * specified in the openBasicChannel(byte[] aid) or openLogicalChannel(byte[] aid) method.
+     * This mechanism can be used by a device application to iterate through all Applets
+     * matching to the same partial AID.
+     * If selectNext() returns true a new Applet was successfully selected on this channel.
+     * If no further Applet exists with matches to the partial AID this method returns false
+     * and the already selected Applet stays selected. <br>
+     *
+     * Since the API cannot distinguish between a partial and full AID the API shall rely on the
+     * response of the Secure Element for the return value of this method. <br>
+     * The implementation of the underlying SELECT command within this method shall use
+     * the same values as the corresponding openBasicChannel(byte[] aid) or
+     * openLogicalChannel(byte[] aid) command with the option: <br>
+     * P2='02' (Next occurrence) <br>
+     * The select response stored in the Channel object shall be updated with the APDU
+     * response of the SELECT command.
+
+     * @return <code>true</code> if new Applet was selected on this channel.
+               <code>false</code> he already selected Applet stays selected on this channel.
+     *
+     * @throws IOException if there is a communication problem to the reader or the Secure Element.
+     * @throws IllegalStateException if the channel is used after being closed.
+     * @throws UnsupportedOperationException if this operation is not supported by the card.
+     */
+    public boolean selectNext() throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        try {
+            synchronized (mLock) {
+                return mChannel.selectNext();
+            }
+        } catch (ServiceSpecificException e) {
+            throw new IOException(e.getMessage());
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/android/se/omapi/Reader.java b/android/se/omapi/Reader.java
new file mode 100644
index 0000000..9be3da6
--- /dev/null
+++ b/android/se/omapi/Reader.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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) 2015-2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Instances of this class represent Secure Element Readers supported to this
+ * device. These Readers can be physical devices or virtual devices. They can be
+ * removable or not. They can contain Secure Element that can or cannot be
+ * removed.
+ *
+ * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
+ */
+public class Reader {
+
+    private static final String TAG = "OMAPI.Reader";
+    private final String mName;
+    private final SEService mService;
+    private ISecureElementReader mReader;
+    private final Object mLock = new Object();
+
+
+    Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
+        if (reader == null || service == null || name == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
+        }
+        mName = name;
+        mService = service;
+        mReader = reader;
+    }
+
+    /**
+     * Return the name of this reader.
+     * <ul>
+     * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
+     * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
+     * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
+     * </ul>
+     * Slot is a decimal number without leading zeros. The Numbering must start with 1
+     * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
+     * The slot number “1” for a reader is optional
+     * (SIM and SIM1 are both valid for the first SIM-reader,
+     * but if there are two readers then the second reader must be named SIM2).
+     * This applies also for other SD or SE readers.
+     *
+     * @return the reader name, as a String.
+     */
+    public @NonNull String getName() {
+        return mName;
+    }
+
+    /**
+     * Connects to a Secure Element in this reader. <br>
+     * This method prepares (initialises) the Secure Element for communication
+     * before the Session object is returned (e.g. powers the Secure Element by
+     * ICC ON if its not already on). There might be multiple sessions opened at
+     * the same time on the same reader. The system ensures the interleaving of
+     * APDUs between the respective sessions.
+     *
+     * @throws IOException if something went wrong with the communicating to the
+     *             Secure Element or the reader.
+     * @return a Session object to be used to create Channels.
+     */
+    public @NonNull Session openSession() throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service is not connected");
+        }
+
+        synchronized (mLock) {
+            ISecureElementSession session;
+            try {
+                session = mReader.openSession();
+            } catch (ServiceSpecificException e) {
+                throw new IOException(e.getMessage());
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e.getMessage());
+            }
+            if (session == null) {
+                throw new IOException("service session is null.");
+            }
+            return new Session(mService, session, this);
+        }
+    }
+
+    /**
+     * Check if a Secure Element is present in this reader.
+     *
+     * @throws IllegalStateException if the service is not connected
+     * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
+     */
+    public boolean isSecureElementPresent() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service is not connected");
+        }
+
+        try {
+            return mReader.isSecureElementPresent();
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Error in isSecureElementPresent()");
+        }
+    }
+
+    /**
+     * Return the Secure Element service this reader is bound to.
+     *
+     * @return the SEService object.
+     */
+    public @NonNull SEService getSEService() {
+        return mService;
+    }
+
+    /**
+     * Close all the sessions opened on this reader.
+     * All the channels opened by all these sessions will be closed.
+     */
+    public void closeSessions() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service is not connected");
+            return;
+        }
+        synchronized (mLock) {
+            try {
+                mReader.closeSessions();
+            } catch (RemoteException ignore) { }
+        }
+    }
+}
diff --git a/android/se/omapi/SEService.java b/android/se/omapi/SEService.java
new file mode 100644
index 0000000..311dc4c
--- /dev/null
+++ b/android/se/omapi/SEService.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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) 2015-2017, The Linux Foundation. All rights reserved.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * The SEService realises the communication to available Secure Elements on the
+ * device. This is the entry point of this API. It is used to connect to the
+ * infrastructure and get access to a list of Secure Element Readers.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
+ */
+public class SEService {
+
+    /**
+     * Error code used with ServiceSpecificException.
+     * Thrown if there was an error communicating with the Secure Element.
+     *
+     * @hide
+     */
+    public static final int IO_ERROR = 1;
+
+    /**
+     * Error code used with ServiceSpecificException.
+     * Thrown if AID cannot be selected or is not available when opening
+     * a logical channel.
+     *
+     * @hide
+     */
+    public static final int NO_SUCH_ELEMENT_ERROR = 2;
+
+    /**
+     * Interface to send call-backs to the application when the service is connected.
+     */
+    public interface SecureElementListener {
+        /**
+         * Called by the framework when the service is connected.
+         */
+        void onServiceConnected();
+    }
+
+    /**
+     * Listener object that allows the notification of the caller if this
+     * SEService could be bound to the backend.
+     */
+    private class SEListener extends ISecureElementListener.Stub {
+        public SecureElementListener mListener = null;
+
+        @Override
+        public IBinder asBinder() {
+            return this;
+        }
+
+        public void onServiceConnected() {
+            if (mListener != null) {
+                mListener.onServiceConnected();
+            }
+        }
+    }
+    private SEListener mSEListener = new SEListener();
+
+    private static final String TAG = "OMAPI.SEService";
+
+    private final Object mLock = new Object();
+
+    /** The client context (e.g. activity). */
+    private final Context mContext;
+
+    /** The backend system. */
+    private volatile ISecureElementService mSecureElementService;
+
+    /**
+     * Class for interacting with the main interface of the backend.
+     */
+    private ServiceConnection mConnection;
+
+    /**
+     * Collection of available readers
+     */
+    private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
+
+    /**
+     * Establishes a new connection that can be used to connect to all the
+     * Secure Elements available in the system. The connection process can be
+     * quite long, so it happens in an asynchronous way. It is usable only if
+     * the specified listener is called or if isConnected() returns
+     * <code>true</code>. <br>
+     * The call-back object passed as a parameter will have its
+     * onServiceConnected() method called when the connection actually happen.
+     *
+     * @param context
+     *            the context of the calling application. Cannot be
+     *            <code>null</code>.
+     * @param listener
+     *            a SecureElementListener object.
+     */
+    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
+
+        if (context == null) {
+            throw new NullPointerException("context must not be null");
+        }
+
+        mContext = context;
+        mSEListener.mListener = listener;
+
+        mConnection = new ServiceConnection() {
+
+            public synchronized void onServiceConnected(
+                    ComponentName className, IBinder service) {
+
+                mSecureElementService = ISecureElementService.Stub.asInterface(service);
+                if (mSEListener != null) {
+                    mSEListener.onServiceConnected();
+                }
+                Log.i(TAG, "Service onServiceConnected");
+            }
+
+            public void onServiceDisconnected(ComponentName className) {
+                mSecureElementService = null;
+                Log.i(TAG, "Service onServiceDisconnected");
+            }
+        };
+
+        Intent intent = new Intent(ISecureElementService.class.getName());
+        intent.setClassName("com.android.se",
+                            "com.android.se.SecureElementService");
+        boolean bindingSuccessful =
+                mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+        if (bindingSuccessful) {
+            Log.i(TAG, "bindService successful");
+        }
+    }
+
+    /**
+     * Tells whether or not the service is connected.
+     *
+     * @return <code>true</code> if the service is connected.
+     */
+    public boolean isConnected() {
+        return mSecureElementService != null;
+    }
+
+    /**
+     * Returns the list of available Secure Element readers.
+     * There must be no duplicated objects in the returned list.
+     * All available readers shall be listed even if no card is inserted.
+     *
+     * @return The readers list, as an array of Readers. If there are no
+     * readers the returned array is of length 0.
+     */
+    public @NonNull Reader[] getReaders() {
+        if (mSecureElementService == null) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        String[] readerNames;
+        try {
+            readerNames = mSecureElementService.getReaders();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        Reader[] readers = new Reader[readerNames.length];
+        int i = 0;
+        for (String readerName : readerNames) {
+            if (mReaders.get(readerName) == null) {
+                try {
+                    mReaders.put(readerName, new Reader(this, readerName,
+                            getReader(readerName)));
+                    readers[i++] = mReaders.get(readerName);
+                } catch (Exception e) {
+                    Log.e(TAG, "Error adding Reader: " + readerName, e);
+                }
+            } else {
+                readers[i++] = mReaders.get(readerName);
+            }
+        }
+        return readers;
+    }
+
+    /**
+     * Releases all Secure Elements resources allocated by this SEService
+     * (including any binding to an underlying service).
+     * As a result isConnected() will return false after shutdown() was called.
+     * After this method call, the SEService object is not connected.
+     * It is recommended to call this method in the termination method of the calling application
+     * (or part of this application) which is bound to this SEService.
+     */
+    public void shutdown() {
+        synchronized (mLock) {
+            if (mSecureElementService != null) {
+                for (Reader reader : mReaders.values()) {
+                    try {
+                        reader.closeSessions();
+                    } catch (Exception ignore) { }
+                }
+            }
+            try {
+                mContext.unbindService(mConnection);
+            } catch (IllegalArgumentException e) {
+                // Do nothing and fail silently since an error here indicates
+                // that binding never succeeded in the first place.
+            }
+            mSecureElementService = null;
+        }
+    }
+
+    /**
+     * Returns the version of the OpenMobile API specification this
+     * implementation is based on.
+     *
+     * @return String containing the OpenMobile API version (e.g. "3.0").
+     */
+    public @NonNull String getVersion() {
+        return "3.2";
+    }
+
+    @NonNull ISecureElementListener getListener() {
+        return mSEListener;
+    }
+
+    /**
+     * Obtain a Reader instance from the SecureElementService
+     */
+    private @NonNull ISecureElementReader getReader(String name) {
+        try {
+            return mSecureElementService.getReader(name);
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+}
diff --git a/android/se/omapi/Session.java b/android/se/omapi/Session.java
new file mode 100644
index 0000000..adfeddd
--- /dev/null
+++ b/android/se/omapi/Session.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.
+ */
+/*
+ * Copyright (c) 2017, The Linux Foundation.
+ */
+/*
+ * Contributed by: Giesecke & Devrient GmbH.
+ */
+
+package android.se.omapi;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.NoSuchElementException;
+
+/**
+ * Instances of this class represent a connection session to one of the Secure
+ * Elements available on the device. These objects can be used to get a
+ * communication channel with an Applet in the Secure Element.
+ * This channel can be the basic channel or a logical channel.
+ *
+ * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
+ */
+public class Session {
+
+    private final Object mLock = new Object();
+    private final SEService mService;
+    private final Reader mReader;
+    private final ISecureElementSession mSession;
+    private static final String TAG = "OMAPI.Session";
+
+    Session(@NonNull SEService service, @NonNull ISecureElementSession session,
+            @NonNull Reader reader) {
+        if (service == null || reader == null || session == null) {
+            throw new IllegalArgumentException("Parameters cannot be null");
+        }
+        mService = service;
+        mReader = reader;
+        mSession = session;
+    }
+
+    /**
+     * Get the reader that provides this session.
+     *
+     * @return The Reader object.
+     */
+    public @NonNull Reader getReader() {
+        return mReader;
+    }
+
+    /**
+     * Get the Answer to Reset of this Secure Element. <br>
+     * The returned byte array can be null if the ATR for this Secure Element is
+     * not available.
+     *
+     * @throws IllegalStateException if there was an error connecting to SE or
+     *                               if the service was not connected.
+     * @return the ATR as a byte array or null.
+     */
+    public @Nullable byte[] getATR() {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        try {
+            return mSession.getAtr();
+        } catch (RemoteException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+
+    /**
+     * Close the connection with the Secure Element. This will close any
+     * channels opened by this application with this Secure Element.
+     */
+    public void close() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service not connected to system");
+            return;
+        }
+        synchronized (mLock) {
+            try {
+                mSession.close();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error closing session", e);
+            }
+        }
+    }
+
+    /**
+     * Tells if this session is closed.
+     *
+     * @return <code>true</code> if the session is closed, false otherwise.
+     */
+    public boolean isClosed() {
+        try {
+            return mSession.isClosed();
+        } catch (RemoteException e) {
+            // If there was an error here, then the session is considered close
+            return true;
+        }
+    }
+
+    /**
+     * Close any channel opened on this session.
+     */
+    public void closeChannels() {
+        if (!mService.isConnected()) {
+            Log.e(TAG, "service not connected to system");
+            return;
+        }
+
+        synchronized (mLock) {
+            try {
+                mSession.closeChannels();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error closing channels", e);
+            }
+        }
+    }
+
+    /**
+     * Get an access to the basic channel, as defined in the ISO/IEC 7816-4 specification (the
+     * one that has number 0). The obtained object is an instance of the Channel class.
+     * If the AID is null, it means no Applet is to be selected on this channel and the default
+     * Applet is used. If the AID is defined then the corresponding Applet is selected.
+     * Once this channel has been opened by a device application, it is considered as "locked"
+     * by this device application, and other calls to this method will return null, until the
+     * channel is closed. Some Secure Elements (like the UICC) might always keep the basic channel
+     * locked (i.e. return null to applications), to prevent access to the basic channel, while
+     * some other might return a channel object implementing some kind of filtering on the
+     * commands, restricting the set of accepted command to a smaller set.
+     * It is recommended for the UICC to reject the opening of the basic channel to a specific
+     * applet, by always answering null to such a request.
+     * For other Secure Elements, the recommendation is to accept opening the basic channel
+     * on the default applet until another applet is selected on the basic channel. As there is no
+     * other way than a reset to select again the default applet, the implementation of the
+     * transport API should guarantee that the openBasicChannel(null) command will return
+     * null until a reset occurs.
+     * With previous release (V2.05) it was not possible to set P2 value, this value was always
+     * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+     * recommended that the device allows all values for P2, however only the following values
+     * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+     * The implementation of the underlying SELECT command within this method shall be
+     * based on ISO 7816-4 with following options:
+     * <ul>
+     * <li>CLA = '00'</li>
+     * <li>INS = 'A4'</li>
+     * <li>P1 = '04' (Select by DF name/application identifier)</li>
+     * </ul>
+     *
+     * The select response data can be retrieved with byte[] getSelectResponse().
+     * The API shall handle received status word as follow. If the status word indicates that the
+     * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+     * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+     * channel opened and the next getSelectResponse() shall return the received status
+     * word.
+     * Other received status codes indicating that the Secure Element was able not to open a
+     * channel shall be considered as an error and the corresponding channel shall not be
+     * opened.
+     * The function without P2 as parameter is provided for backwards compatibility and will
+     * fall back to a select command with P2='00'.
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array, or null if no Applet is to be selected.
+     * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element session is used after
+     *             being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+     *             selected.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device
+     * @return an instance of Channel if available or null.
+     */
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+
+        synchronized (mLock) {
+            try {
+                ISecureElementChannel channel = mSession.openBasicChannel(aid, p2,
+                        mReader.getSEService().getListener());
+                if (channel == null) {
+                    return null;
+                }
+                return new Channel(mService, this, channel);
+            } catch (ServiceSpecificException e) {
+                if (e.errorCode == SEService.IO_ERROR) {
+                    throw new IOException(e.getMessage());
+                } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+                    throw new NoSuchElementException(e.getMessage());
+                } else {
+                    throw new IllegalStateException(e.getMessage());
+                }
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Open a logical channel with the Secure Element, selecting the Applet represented by
+     * the given AID. If the AID is null, which means no Applet is to be selected on this
+     * channel, the default Applet is used. It's up to the Secure Element to choose which
+     * logical channel will be used.
+     * With previous release (V2.05) it was not possible to set P2 value, this value was always
+     * set to '00'.Except for specific needs it is recommended to keep P2 to '00'. It is
+     * recommended that the device allows all values for P2, however only the following values
+     * are mandatory: '00', '04', '08', '0C'(as defined in [2])
+     * The implementation of the underlying SELECT command within this method shall be
+     * based on ISO 7816-4 with following options:
+     *
+     * <ul>
+     * <li>CLA = '01' to '03', '40 to 4F'</li>
+     * <li>INS = 'A4'</li>
+     * <li>P1 = '04' (Select by DF name/application identifier)</li>
+     * </ul>
+     *
+     * The select response data can be retrieved with byte[] getSelectResponse().
+     * The API shall handle received status word as follow. If the status word indicates that the
+     * Secure Element was able to open a channel (e.g. status word '90 00' or status words
+     * referencing a warning in ISO-7816-4: '62 XX' or '63 XX') the API shall keep the
+     * channel opened and the next getSelectResponse() shall return the received status
+     * word.
+     * Other received status codes indicating that the Secure Element was able not to open a
+     * channel shall be considered as an error and the corresponding channel shall not be
+     * opened.
+     * In case of UICC it is recommended for the API to reject the opening of the logical
+     * channel without a specific AID, by always answering null to such a request.
+     * The function without P2 as parameter is provided for backwards compatibility and will
+     * fall back to a select command with P2=00.
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array.
+     * @param p2 the P2 parameter of the SELECT APDU executed on this channel.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not
+     *             available or cannot be selected or a logical channel is already
+     *             open to a non-multiselectable Applet.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device.
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
+        if (!mService.isConnected()) {
+            throw new IllegalStateException("service not connected to system");
+        }
+        synchronized (mLock) {
+            try {
+                ISecureElementChannel channel = mSession.openLogicalChannel(
+                        aid,
+                        p2,
+                        mReader.getSEService().getListener());
+                if (channel == null) {
+                    return null;
+                }
+                return new Channel(mService, this, channel);
+            } catch (ServiceSpecificException e) {
+                if (e.errorCode == SEService.IO_ERROR) {
+                    throw new IOException(e.getMessage());
+                } else if (e.errorCode == SEService.NO_SUCH_ELEMENT_ERROR) {
+                    throw new NoSuchElementException(e.getMessage());
+                } else {
+                    throw new IllegalStateException(e.getMessage());
+                }
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e.getMessage());
+            }
+        }
+    }
+}
diff --git a/android/security/ConfirmationAlreadyPresentingException.java b/android/security/ConfirmationAlreadyPresentingException.java
new file mode 100644
index 0000000..ae4ec5a
--- /dev/null
+++ b/android/security/ConfirmationAlreadyPresentingException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * This exception is thrown when presenting a prompt fails because another prompt is already
+ * being presented.
+ */
+public class ConfirmationAlreadyPresentingException extends Exception {
+    public ConfirmationAlreadyPresentingException() {}
+
+    public ConfirmationAlreadyPresentingException(String message) {
+        super(message);
+    }
+}
diff --git a/android/security/ConfirmationCallback.java b/android/security/ConfirmationCallback.java
new file mode 100644
index 0000000..4670bce
--- /dev/null
+++ b/android/security/ConfirmationCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback class used when signaling that a prompt is no longer being presented.
+ */
+public abstract class ConfirmationCallback {
+    /**
+     * Called when the requested prompt was accepted by the user.
+     *
+     * The format of 'dataThatWasConfirmed' parameter is a <a href="http://cbor.io/">CBOR</a>
+     * encoded map (type 5) with (at least) the keys <strong>prompt</strong> and
+     * <strong>extra</strong>. The keys are encoded as CBOR text string (type 3). The value of
+     * promptText is encoded as CBOR text string (type 3), and the value of extraData is encoded as
+     * CBOR byte string (type 2). Other keys may be added in the future.
+     *
+     * @param dataThatWasConfirmed the data that was confirmed, see above for the format.
+     */
+    public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {}
+
+    /**
+     * Called when the requested prompt was dismissed (not accepted) by the user.
+     */
+    public void onDismissedByUser() {}
+
+    /**
+     * Called when the requested prompt was dismissed by the application.
+     */
+    public void onDismissedByApplication() {}
+
+    /**
+     * Called when the requested prompt was dismissed because of a low-level error.
+     *
+     * @param e an exception representing the error.
+     */
+    public void onError(Exception e) {}
+}
diff --git a/android/security/ConfirmationDialog.java b/android/security/ConfirmationDialog.java
new file mode 100644
index 0000000..1697106
--- /dev/null
+++ b/android/security/ConfirmationDialog.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+/**
+ * Class used for displaying confirmation prompts.
+ *
+ * <p>Confirmation prompts are prompts shown to the user to confirm a given text and are
+ * implemented in a way that a positive response indicates with high confidence that the user has
+ * seen the given text, even if the Android framework (including the kernel) was
+ * compromised. Implementing confirmation prompts with these guarantees requires dedicated
+ * hardware-support and may not always be available.
+ *
+ * <p>Confirmation prompts are typically used with an external entitity - the <i>Relying Party</i> -
+ * in the following way. The setup steps are as follows:
+ * <ul>
+ * <li> Before first use, the application generates a key-pair with the
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
+ * CONFIRMATION tag} set. Device attestation,
+ * e.g. {@link java.security.KeyStore#getCertificateChain getCertificateChain()}, is used to
+ * generate a certificate chain that includes the public key (<code>Kpub</code> in the following)
+ * of the newly generated key.
+ * <li> The application sends <code>Kpub</code> and the certificate chain resulting from device
+ * attestation to the <i>Relying Party</i>.
+ * <li> The <i>Relying Party</i> validates the certificate chain which involves checking the root
+ * certificate is what is expected (e.g. a certificate from Google), each certificate signs the
+ * next one in the chain, ending with <code>Kpub</code>, and that the attestation certificate
+ * asserts that <code>Kpub</code> has the
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
+ * CONFIRMATION tag} set.
+ * Additionally the relying party stores <code>Kpub</code> and associates it with the device
+ * it was received from.
+ * </ul>
+ *
+ * <p>The <i>Relying Party</i> is typically an external device (for example connected via
+ * Bluetooth) or application server.
+ *
+ * <p>Before executing a transaction which requires a high assurance of user content, the
+ * application does the following:
+ * <ul>
+ * <li> The application gets a cryptographic nonce from the <i>Relying Party</i> and passes this as
+ * the <code>extraData</code> (via the Builder helper class) to the
+ * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally
+ * since it'll use it in a later step.
+ * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the
+ * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the
+ * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the
+ * user, the <code>extraData</code> parameter, and possibly other data.
+ * <li> The application signs the <i>Confirmation Response</i> with the previously created key and
+ * sends the blob and the signature to the <i>Relying Party</i>.
+ * <li> The <i>Relying Party</i> checks that the signature was made with <code>Kpub</code> and then
+ * extracts <code>promptText</code> matches what is expected and <code>extraData</code> matches the
+ * previously created nonce. If all checks passes, the transaction is executed.
+ * </ul>
+ *
+ * <p>A common way of implementing the "<code>promptText</code> is what is expected" check in the
+ * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
+ * along the nonce in the <code>extraData</code> blob.
+ */
+public class ConfirmationDialog {
+    private static final String TAG = "ConfirmationDialog";
+
+    private CharSequence mPromptText;
+    private byte[] mExtraData;
+    private ConfirmationCallback mCallback;
+    private Executor mExecutor;
+    private Context mContext;
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    private void doCallback(int responseCode, byte[] dataThatWasConfirmed,
+            ConfirmationCallback callback) {
+        switch (responseCode) {
+            case KeyStore.CONFIRMATIONUI_OK:
+                callback.onConfirmedByUser(dataThatWasConfirmed);
+                break;
+
+            case KeyStore.CONFIRMATIONUI_CANCELED:
+                callback.onDismissedByUser();
+                break;
+
+            case KeyStore.CONFIRMATIONUI_ABORTED:
+                callback.onDismissedByApplication();
+                break;
+
+            case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR:
+                callback.onError(new Exception("System error returned by ConfirmationUI."));
+                break;
+
+            default:
+                callback.onError(new Exception("Unexpected responseCode=" + responseCode
+                                + " from onConfirmtionPromptCompleted() callback."));
+                break;
+        }
+    }
+
+    private final android.os.IBinder mCallbackBinder =
+            new android.security.IConfirmationPromptCallback.Stub() {
+                @Override
+                public void onConfirmationPromptCompleted(
+                        int responseCode, final byte[] dataThatWasConfirmed)
+                        throws android.os.RemoteException {
+                    if (mCallback != null) {
+                        ConfirmationCallback callback = mCallback;
+                        Executor executor = mExecutor;
+                        mCallback = null;
+                        mExecutor = null;
+                        if (executor == null) {
+                            doCallback(responseCode, dataThatWasConfirmed, callback);
+                        } else {
+                            executor.execute(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        doCallback(responseCode, dataThatWasConfirmed, callback);
+                                    }
+                                });
+                        }
+                    }
+                }
+            };
+
+    /**
+     * A builder that collects arguments, to be shown on the system-provided confirmation dialog.
+     */
+    public static class Builder {
+
+        private CharSequence mPromptText;
+        private byte[] mExtraData;
+
+        /**
+         * Creates a builder for the confirmation dialog.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the prompt text for the dialog.
+         *
+         * @param promptText the text to present in the prompt.
+         * @return the builder.
+         */
+        public Builder setPromptText(CharSequence promptText) {
+            mPromptText = promptText;
+            return this;
+        }
+
+        /**
+         * Sets the extra data for the dialog.
+         *
+         * @param extraData data to include in the response data.
+         * @return the builder.
+         */
+        public Builder setExtraData(byte[] extraData) {
+            mExtraData = extraData;
+            return this;
+        }
+
+        /**
+         * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder.
+         *
+         * @param context the application context
+         * @return a {@link ConfirmationDialog}
+         * @throws IllegalArgumentException if any of the required fields are not set.
+         */
+        public ConfirmationDialog build(Context context) {
+            if (TextUtils.isEmpty(mPromptText)) {
+                throw new IllegalArgumentException("prompt text must be set and non-empty");
+            }
+            if (mExtraData == null) {
+                throw new IllegalArgumentException("extraData must be set");
+            }
+            return new ConfirmationDialog(context, mPromptText, mExtraData);
+        }
+    }
+
+    private ConfirmationDialog(Context context, CharSequence promptText, byte[] extraData) {
+        mContext = context;
+        mPromptText = promptText;
+        mExtraData = extraData;
+    }
+
+    private static final int UI_OPTION_ACCESSIBILITY_INVERTED_FLAG = 1 << 0;
+    private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1;
+
+    private int getUiOptionsAsFlags() {
+        int uiOptionsAsFlags = 0;
+        try {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            int inversionEnabled = Settings.Secure.getInt(contentResolver,
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+            if (inversionEnabled == 1) {
+                uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
+            }
+            float fontScale = Settings.System.getFloat(contentResolver,
+                    Settings.System.FONT_SCALE);
+            if (fontScale > 1.0) {
+                uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
+            }
+        } catch (SettingNotFoundException e) {
+            Log.w(TAG, "Unexpected SettingNotFoundException");
+        }
+        return uiOptionsAsFlags;
+    }
+
+    private boolean isAccessibilityServiceRunning() {
+        boolean serviceRunning = false;
+        try {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            int a11yEnabled = Settings.Secure.getInt(contentResolver,
+                    Settings.Secure.ACCESSIBILITY_ENABLED);
+            if (a11yEnabled == 1) {
+                serviceRunning = true;
+            }
+        } catch (SettingNotFoundException e) {
+            Log.w(TAG, "Unexpected SettingNotFoundException");
+            e.printStackTrace();
+        }
+        return serviceRunning;
+    }
+
+    /**
+     * Requests a confirmation prompt to be presented to the user.
+     *
+     * When the prompt is no longer being presented, one of the methods in
+     * {@link ConfirmationCallback} is called on the supplied callback object.
+     *
+     * Confirmation dialogs may not be available when accessibility services are running so this
+     * may fail with a {@link ConfirmationNotAvailableException} exception even if
+     * {@link #isSupported} returns {@code true}.
+     *
+     * @param executor the executor identifying the thread that will receive the callback.
+     * @param callback the callback to use when the dialog is done showing.
+     * @throws IllegalArgumentException if the prompt text is too long or malfomed.
+     * @throws ConfirmationAlreadyPresentingException if another prompt is being presented.
+     * @throws ConfirmationNotAvailableException if confirmation prompts are not supported.
+     */
+    public void presentPrompt(@NonNull Executor executor, @NonNull ConfirmationCallback callback)
+            throws ConfirmationAlreadyPresentingException,
+            ConfirmationNotAvailableException {
+        if (mCallback != null) {
+            throw new ConfirmationAlreadyPresentingException();
+        }
+        if (isAccessibilityServiceRunning()) {
+            throw new ConfirmationNotAvailableException();
+        }
+        mCallback = callback;
+        mExecutor = executor;
+
+        int uiOptionsAsFlags = getUiOptionsAsFlags();
+        String locale = Locale.getDefault().toLanguageTag();
+        int responseCode = mKeyStore.presentConfirmationPrompt(
+                mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
+        switch (responseCode) {
+            case KeyStore.CONFIRMATIONUI_OK:
+                return;
+
+            case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
+                throw new ConfirmationAlreadyPresentingException();
+
+            case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
+                throw new ConfirmationNotAvailableException();
+
+            case KeyStore.CONFIRMATIONUI_UIERROR:
+                throw new IllegalArgumentException();
+
+            default:
+                // Unexpected error code.
+                Log.w(TAG,
+                        "Unexpected responseCode=" + responseCode
+                        + " from presentConfirmationPrompt() call.");
+                throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Cancels a prompt currently being displayed.
+     *
+     * On success, the
+     * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on
+     * the supplied callback object will be called asynchronously.
+     *
+     * @throws IllegalStateException if no prompt is currently being presented.
+     */
+    public void cancelPrompt() {
+        int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
+        if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
+            return;
+        } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
+            throw new IllegalStateException();
+        } else {
+            // Unexpected error code.
+            Log.w(TAG,
+                    "Unexpected responseCode=" + responseCode
+                    + " from cancelConfirmationPrompt() call.");
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * Checks if the device supports confirmation prompts.
+     *
+     * @return true if confirmation prompts are supported by the device.
+     */
+    public static boolean isSupported() {
+        return KeyStore.getInstance().isConfirmationPromptSupported();
+    }
+}
diff --git a/android/security/ConfirmationNotAvailableException.java b/android/security/ConfirmationNotAvailableException.java
new file mode 100644
index 0000000..8d0e672
--- /dev/null
+++ b/android/security/ConfirmationNotAvailableException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * This exception is thrown when presenting a prompt fails because the the environment lacks
+ * facilities for showing confirmations.
+ */
+public class ConfirmationNotAvailableException extends Exception {
+    public ConfirmationNotAvailableException() {
+    }
+
+    public ConfirmationNotAvailableException(String message) {
+        super(message);
+    }
+}
diff --git a/android/security/KeyChain.java b/android/security/KeyChain.java
index 2daf733..46a7fa8 100644
--- a/android/security/KeyChain.java
+++ b/android/security/KeyChain.java
@@ -246,6 +246,82 @@
     public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
 
     /**
+     * Indicates that a call to {@link #generateKeyPair} was successful.
+     * @hide
+     */
+    public static final int KEY_GEN_SUCCESS = 0;
+
+    /**
+     * An alias was missing from the key specifications when calling {@link #generateKeyPair}.
+     * @hide
+     */
+    public static final int KEY_GEN_MISSING_ALIAS = 1;
+
+    /**
+     * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't
+     * have been provided.
+     * @hide
+     */
+    public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2;
+
+    /**
+     * Algorithm not supported by {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3;
+
+    /**
+     * Invalid algorithm parameters when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4;
+
+    /**
+     * Keystore is not available when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
+
+    /**
+     * General failure while calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_FAILURE = 6;
+
+    /**
+     * Successful call to {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_SUCCESS = 0;
+
+    /**
+     * Attestation challenge missing when calling {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1;
+
+    /**
+     * The caller requested Device ID attestation when calling {@link #attestKey}, but has no
+     * permissions to get device identifiers.
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2;
+
+    /**
+     * The underlying hardware does not support Device ID attestation or cannot attest to the
+     * identifiers that are stored on the device. This indicates permanent inability
+     * to get attestation records on the device.
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3;
+
+    /**
+     * General failure when calling {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_FAILURE = 4;
+
+    /**
      * Returns an {@code Intent} that can be used for credential
      * installation. The intent may be used without any extras, in
      * which case the user will be able to install credentials from
diff --git a/android/security/KeyStore.java b/android/security/KeyStore.java
index e25386b..fe05c13 100644
--- a/android/security/KeyStore.java
+++ b/android/security/KeyStore.java
@@ -16,6 +16,7 @@
 
 package android.security;
 
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.KeyguardManager;
@@ -38,6 +39,7 @@
 import android.security.keystore.KeyExpiredException;
 import android.security.keystore.KeyNotYetValidException;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.StrongBoxUnavailableException;
 import android.security.keystore.UserNotAuthenticatedException;
 import android.util.Log;
 
@@ -65,6 +67,8 @@
     public static final int VALUE_CORRUPTED = 8;
     public static final int UNDEFINED_ACTION = 9;
     public static final int WRONG_PASSWORD = 10;
+    public static final int CANNOT_ATTEST_IDS = -66;
+    public static final int HARDWARE_TYPE_UNAVAILABLE = -68;
 
     /**
      * Per operation authentication is needed before this operation is valid.
@@ -123,7 +127,6 @@
      */
     public static final int FLAG_STRONGBOX = 1 << 4;
 
-
     // States
     public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
 
@@ -277,7 +280,7 @@
     /**
      * Attempt to lock the keystore for {@code user}.
      *
-     * @param user Android user to lock.
+     * @param userId Android user to lock.
      * @return whether {@code user}'s keystore was locked.
      */
     public boolean lock(int userId) {
@@ -298,7 +301,7 @@
      * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or
      * created.
      *
-     * @param user Android user ID to operate on
+     * @param userId Android user ID to operate on
      * @param password user's keystore password. Should be the most recent value passed to
      * {@link #onUserPasswordChanged} for the user.
      *
@@ -730,6 +733,72 @@
         }
     }
 
+    // Keep in sync with confirmationui/1.0/types.hal.
+    public static final int CONFIRMATIONUI_OK = 0;
+    public static final int CONFIRMATIONUI_CANCELED = 1;
+    public static final int CONFIRMATIONUI_ABORTED = 2;
+    public static final int CONFIRMATIONUI_OPERATION_PENDING = 3;
+    public static final int CONFIRMATIONUI_IGNORED = 4;
+    public static final int CONFIRMATIONUI_SYSTEM_ERROR = 5;
+    public static final int CONFIRMATIONUI_UNIMPLEMENTED = 6;
+    public static final int CONFIRMATIONUI_UNEXPECTED = 7;
+    public static final int CONFIRMATIONUI_UIERROR = 0x10000;
+    public static final int CONFIRMATIONUI_UIERROR_MISSING_GLYPH = 0x10001;
+    public static final int CONFIRMATIONUI_UIERROR_MESSAGE_TOO_LONG = 0x10002;
+    public static final int CONFIRMATIONUI_UIERROR_MALFORMED_UTF8_ENCODING = 0x10003;
+
+    /**
+     * Requests keystore call into the confirmationui HAL to display a prompt.
+     *
+     * @param listener the binder to use for callbacks.
+     * @param promptText the prompt to display.
+     * @param extraData extra data / nonce from application.
+     * @param locale the locale as a BCP 47 langauge tag.
+     * @param uiOptionsAsFlags the UI options to use, as flags.
+     * @return one of the {@code CONFIRMATIONUI_*} constants, for
+     * example {@code KeyStore.CONFIRMATIONUI_OK}.
+     */
+    public int presentConfirmationPrompt(IBinder listener, String promptText, byte[] extraData,
+                                         String locale, int uiOptionsAsFlags) {
+        try {
+            return mBinder.presentConfirmationPrompt(listener, promptText, extraData, locale,
+                                                     uiOptionsAsFlags);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return CONFIRMATIONUI_SYSTEM_ERROR;
+        }
+    }
+
+    /**
+     * Requests keystore call into the confirmationui HAL to cancel displaying a prompt.
+     *
+     * @param listener the binder passed to the {@link #presentConfirmationPrompt} method.
+     * @return one of the {@code CONFIRMATIONUI_*} constants, for
+     * example {@code KeyStore.CONFIRMATIONUI_OK}.
+     */
+    public int cancelConfirmationPrompt(IBinder listener) {
+        try {
+            return mBinder.cancelConfirmationPrompt(listener);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return CONFIRMATIONUI_SYSTEM_ERROR;
+        }
+    }
+
+    /**
+     * Requests keystore to check if the confirmationui HAL is available.
+     *
+     * @return whether the confirmationUI HAL is available.
+     */
+    public boolean isConfirmationPromptSupported() {
+        try {
+            return mBinder.isConfirmationPromptSupported();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
     /**
      * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
      * code.
diff --git a/android/security/Scrypt.java b/android/security/Scrypt.java
new file mode 100644
index 0000000..edf8d31
--- /dev/null
+++ b/android/security/Scrypt.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+/**
+ * A Java wrapper for the JNI function to perform the password hashing algorithm SCRYPT.
+ *
+ * @hide
+ */
+public class Scrypt {
+
+    native byte[] nativeScrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen);
+
+    /** Computes the password hashing algorithm SCRYPT. */
+    public byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
+        return nativeScrypt(password, salt, n, r, p, outLen);
+    }
+}
diff --git a/android/security/keymaster/KeymasterDefs.java b/android/security/keymaster/KeymasterDefs.java
index 3464370..f4dcce1 100644
--- a/android/security/keymaster/KeymasterDefs.java
+++ b/android/security/keymaster/KeymasterDefs.java
@@ -74,6 +74,8 @@
     public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
     public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
+    public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
+    public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -215,6 +217,7 @@
     public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
     public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
     public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
+    public static final int KM_ERROR_DEVICE_LOCKED = -72;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -261,6 +264,7 @@
         sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
                 "Invalid MAC or authentication tag length");
         sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
+        sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
diff --git a/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index e4cf84a..cc80560 100644
--- a/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -93,15 +93,17 @@
         putSymmetricCipherImpl("AES/CTR/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
 
-        putSymmetricCipherImpl("DESede/CBC/NoPadding",
+        if ("true".equals(System.getProperty("supports3DES"))) {
+            putSymmetricCipherImpl("DESede/CBC/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding");
-        putSymmetricCipherImpl("DESede/CBC/PKCS7Padding",
+            putSymmetricCipherImpl("DESede/CBC/PKCS7Padding",
                 PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding");
 
-        putSymmetricCipherImpl("DESede/ECB/NoPadding",
+            putSymmetricCipherImpl("DESede/ECB/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding");
-        putSymmetricCipherImpl("DESede/ECB/PKCS7Padding",
+            putSymmetricCipherImpl("DESede/ECB/PKCS7Padding",
                 PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding");
+        }
 
         putSymmetricCipherImpl("AES/GCM/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding");
diff --git a/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 379e177..419eb24 100644
--- a/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,12 +243,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -284,12 +279,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-        KeymasterUtils.addUserAuthArgs(args,
-                spec.isUserAuthenticationRequired(),
-                spec.getUserAuthenticationValidityDurationSeconds(),
-                spec.isUserAuthenticationValidWhileOnBody(),
-                spec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+        KeymasterUtils.addUserAuthArgs(args, spec);
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index dba3949..5fc742a 100644
--- a/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -21,6 +21,7 @@
 import android.security.GateKeeper;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore;
+import android.security.KeyStoreException;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
 import android.security.keymaster.KeymasterCertificateChain;
@@ -343,12 +344,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        mSpec.isUserAuthenticationRequired(),
-                        mSpec.getUserAuthenticationValidityDurationSeconds(),
-                        mSpec.isUserAuthenticationValidWhileOnBody(),
-                        mSpec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -451,7 +447,7 @@
             throw new IllegalStateException("Not initialized");
         }
 
-        final int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0;
+        int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0;
         if (((flags & KeyStore.FLAG_ENCRYPTED) != 0)
                 && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
             throw new IllegalStateException(
@@ -459,6 +455,10 @@
                     + ", but the user has not yet entered the credential");
         }
 
+        if (mSpec.isStrongBoxBacked()) {
+            flags |= KeyStore.FLAG_STRONGBOX;
+        }
+
         byte[] additionalEntropy =
                 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
                         mRng, (mKeySizeBits + 7) / 8);
@@ -475,6 +475,12 @@
 
             success = true;
             return keyPair;
+        } catch (ProviderException e) {
+          if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
+              throw new SecureKeyImportUnavailableException(e);
+          } else {
+              throw e;
+          }
         } finally {
             if (!success) {
                 Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
@@ -501,8 +507,12 @@
         int errorCode = mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy,
                 mEntryUid, flags, resultingKeyCharacteristics);
         if (errorCode != KeyStore.NO_ERROR) {
-            throw new ProviderException(
-                    "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
+            if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) {
+                throw new StrongBoxUnavailableException("Failed to generate key pair");
+            } else {
+                throw new ProviderException(
+                        "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
+            }
         }
     }
 
@@ -531,12 +541,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        KeymasterUtils.addUserAuthArgs(args,
-                mSpec.isUserAuthenticationRequired(),
-                mSpec.getUserAuthenticationValidityDurationSeconds(),
-                mSpec.isUserAuthenticationValidWhileOnBody(),
-                mSpec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+        KeymasterUtils.addUserAuthArgs(args, mSpec);
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/android/security/keystore/AndroidKeyStoreProvider.java b/android/security/keystore/AndroidKeyStoreProvider.java
index 1018926..9b7695d 100644
--- a/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/android/security/keystore/AndroidKeyStoreProvider.java
@@ -67,6 +67,8 @@
     public AndroidKeyStoreProvider() {
         super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
 
+        boolean supports3DES = "true".equals(System.getProperty("supports3DES"));
+
         // java.security.KeyStore
         put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi");
 
@@ -80,16 +82,21 @@
 
         // javax.crypto.KeyGenerator
         put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
-        put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
         put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
         put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
         put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
         put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384");
         put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512");
 
+        if (supports3DES) {
+            put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
+        }
+
         // java.security.SecretKeyFactory
         putSecretKeyFactoryImpl("AES");
-        putSecretKeyFactoryImpl("DESede");
+        if (supports3DES) {
+            putSecretKeyFactoryImpl("DESede");
+        }
         putSecretKeyFactoryImpl("HmacSHA1");
         putSecretKeyFactoryImpl("HmacSHA224");
         putSecretKeyFactoryImpl("HmacSHA256");
@@ -339,6 +346,7 @@
         return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
     }
 
+    @NonNull
     public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
             @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)
             throws UnrecoverableKeyException  {
diff --git a/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index fdb885d..7bbc099 100644
--- a/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -177,6 +177,9 @@
                 && (keymasterSwEnforcedUserAuthenticators == 0);
         boolean userAuthenticationValidWhileOnBody =
                 keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
+        boolean trustedUserPresenceRequred =
+                keyCharacteristics.hwEnforced.getBoolean(
+                    KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
 
         boolean invalidatedByBiometricEnrollment = false;
         if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT
@@ -187,6 +190,8 @@
                     && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
         }
 
+        boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+
         return new KeyInfo(entryAlias,
                 insideSecureHardware,
                 origin,
@@ -203,7 +208,9 @@
                 (int) userAuthenticationValidityDurationSeconds,
                 userAuthenticationRequirementEnforcedBySecureHardware,
                 userAuthenticationValidWhileOnBody,
-                invalidatedByBiometricEnrollment);
+                trustedUserPresenceRequred,
+                invalidatedByBiometricEnrollment,
+                userConfirmationRequired);
     }
 
     private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/android/security/keystore/AndroidKeyStoreSpi.java b/android/security/keystore/AndroidKeyStoreSpi.java
index 440e086..3e9853c 100644
--- a/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/android/security/keystore/AndroidKeyStoreSpi.java
@@ -26,6 +26,7 @@
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.security.keystore.SecureKeyImportUnavailableException;
 import android.security.keystore.WrappedKeyEntry;
 import android.util.Log;
 
@@ -497,12 +498,7 @@
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
-                KeymasterUtils.addUserAuthArgs(importArgs,
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        spec.getBoundToSpecificSecureUserId());
+                KeymasterUtils.addUserAuthArgs(importArgs, spec);
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -699,12 +695,7 @@
             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                     params.getEncryptionPaddings());
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
-            KeymasterUtils.addUserAuthArgs(args,
-                    params.isUserAuthenticationRequired(),
-                    params.getUserAuthenticationValidityDurationSeconds(),
-                    params.isUserAuthenticationValidWhileOnBody(),
-                    params.isInvalidatedByBiometricEnrollment(),
-                    params.getBoundToSpecificSecureUserId());
+            KeymasterUtils.addUserAuthArgs(args, params);
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
@@ -765,7 +756,9 @@
             0, // FIXME fingerprint id?
             mUid,
             new KeyCharacteristics());
-        if (errorCode != KeyStore.NO_ERROR) {
+        if (errorCode == KeymasterDefs.KM_ERROR_UNIMPLEMENTED) {
+          throw new SecureKeyImportUnavailableException("Could not import wrapped key");
+        } else if (errorCode != KeyStore.NO_ERROR) {
             throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
                 + errorCode);
         }
diff --git a/android/security/keystore/BackwardsCompat.java b/android/security/keystore/BackwardsCompat.java
index 69558c4..cf5fe1f 100644
--- a/android/security/keystore/BackwardsCompat.java
+++ b/android/security/keystore/BackwardsCompat.java
@@ -61,8 +61,8 @@
     static android.security.keystore.recovery.KeyDerivationParams fromLegacyKeyDerivationParams(
             KeyDerivationParams keyDerivationParams
     ) {
-        return new android.security.keystore.recovery.KeyDerivationParams(
-                keyDerivationParams.getAlgorithm(), keyDerivationParams.getSalt());
+        return android.security.keystore.recovery.KeyDerivationParams.createSha256Params(
+                keyDerivationParams.getSalt());
     }
 
     static android.security.keystore.recovery.WrappedApplicationKey fromLegacyWrappedApplicationKey(
diff --git a/android/security/keystore/BadCertificateFormatException.java b/android/security/keystore/BadCertificateFormatException.java
index ddc7bd2..c51b773 100644
--- a/android/security/keystore/BadCertificateFormatException.java
+++ b/android/security/keystore/BadCertificateFormatException.java
@@ -17,8 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when the recovery agent supplies an invalid X509 certificate.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.BadCertificateFormatException}.
  * @hide
  */
 public class BadCertificateFormatException extends RecoveryControllerException {
diff --git a/android/security/keystore/DecryptionFailedException.java b/android/security/keystore/DecryptionFailedException.java
index 945fcf6..c0b52f7 100644
--- a/android/security/keystore/DecryptionFailedException.java
+++ b/android/security/keystore/DecryptionFailedException.java
@@ -17,9 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key,
- * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.DecryptionFailedException}.
  * @hide
  */
 public class DecryptionFailedException extends RecoveryControllerException {
diff --git a/android/security/keystore/InternalRecoveryServiceException.java b/android/security/keystore/InternalRecoveryServiceException.java
index 85829be..40076f7 100644
--- a/android/security/keystore/InternalRecoveryServiceException.java
+++ b/android/security/keystore/InternalRecoveryServiceException.java
@@ -17,11 +17,7 @@
 package android.security.keystore;
 
 /**
- * An error thrown when something went wrong internally in the recovery service.
- *
- * <p>This is an unexpected error, and indicates a problem with the service itself, rather than the
- * caller having performed some kind of illegal action.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.InternalRecoveryServiceException}.
  * @hide
  */
 public class InternalRecoveryServiceException extends RecoveryControllerException {
diff --git a/android/security/keystore/KeyDerivationParams.java b/android/security/keystore/KeyDerivationParams.java
index b19cee2..e475dc3 100644
--- a/android/security/keystore/KeyDerivationParams.java
+++ b/android/security/keystore/KeyDerivationParams.java
@@ -27,9 +27,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Collection of parameters which define a key derivation function.
- * Currently only supports salted SHA-256
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyDerivationParams}.
  * @hide
  */
 public final class KeyDerivationParams implements Parcelable {
diff --git a/android/security/keystore/KeyGenParameterSpec.java b/android/security/keystore/KeyGenParameterSpec.java
index 1e2b873..c0d0fb0 100644
--- a/android/security/keystore/KeyGenParameterSpec.java
+++ b/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
 
@@ -232,7 +233,7 @@
  * key = (SecretKey) keyStore.getKey("key2", null);
  * }</pre>
  */
-public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
 
     private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
@@ -258,11 +259,14 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final boolean mUserPresenceRequired;
     private final byte[] mAttestationChallenge;
     private final boolean mUniqueIdIncluded;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mIsStrongBoxBacked;
+    private final boolean mUserConfirmationRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     /**
      * @hide should be built with Builder
@@ -287,11 +291,14 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            boolean userPresenceRequired,
             byte[] attestationChallenge,
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
-            boolean isStrongBoxBacked) {
+            boolean isStrongBoxBacked,
+            boolean userConfirmationRequired,
+            boolean unlockedDeviceRequired) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -332,12 +339,15 @@
         mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
+        mUserPresenceRequired = userPresenceRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
         mUniqueIdIncluded = uniqueIdIncluded;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mIsStrongBoxBacked = isStrongBoxBacked;
+        mUserConfirmationRequired = userConfirmationRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -544,6 +554,26 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+     * user.
+     *
+     * Confirmation is separate from user authentication (see
+     * {@link Builder#setUserAuthenticationRequired(boolean)}). Keys can be created that require
+     * confirmation but not user authentication, or user authentication but not confirmation, or
+     * both. Confirmation verifies that some user with physical possession of the device has
+     * approved a displayed message. User authentication verifies that the correct user is present
+     * and has authenticated.
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @see Builder#setUserConfirmationRequired(boolean)
+     */
+    public boolean isUserConfirmationRequired() {
+        return mUserConfirmationRequired;
+    }
+
+    /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
      * (see {@link #isUserAuthenticationRequired()}).
@@ -562,6 +592,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     */
+    public boolean isUserPresenceRequired() {
+        return mUserPresenceRequired;
+    }
+
+    /**
      * Returns the attestation challenge value that will be placed in attestation certificate for
      * this key pair.
      *
@@ -635,6 +673,24 @@
     }
 
     /**
+     * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
+     * signing. Decryption and signature verification will still be available when the screen is
+     * locked.
+     *
+     * @see Builder#setUnlockedDeviceRequired(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
+     * @hide
+     */
+    public long getBoundToSpecificSecureUserId() {
+        return GateKeeper.INVALID_SECURE_USER_ID;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -658,11 +714,14 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mIsStrongBoxBacked = false;
+        private boolean mUserConfirmationRequired;
+        private boolean mUnlockedDeviceRequired = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -718,6 +777,7 @@
             mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
             mUserAuthenticationValidityDurationSeconds =
                 sourceSpec.getUserAuthenticationValidityDurationSeconds();
+            mUserPresenceRequired = sourceSpec.isUserPresenceRequired();
             mAttestationChallenge = sourceSpec.getAttestationChallenge();
             mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
             mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
@@ -1050,6 +1110,29 @@
         }
 
         /**
+         * Sets whether this key is authorized to be used only for messages confirmed by the
+         * user.
+         *
+         * Confirmation is separate from user authentication (see
+         * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
+         * confirmation but not user authentication, or user authentication but not confirmation,
+         * or both. Confirmation verifies that some user with physical possession of the device has
+         * approved a displayed message. User authentication verifies that the correct user is
+         * present and has authenticated.
+         *
+         * <p>This authorization applies only to secret key and private key operations. Public key
+         * operations are not restricted.
+         *
+         * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
+         * more details about user confirmations.
+         */
+        @NonNull
+        public Builder setUserConfirmationRequired(boolean required) {
+            mUserConfirmationRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the duration of time (seconds) for which this key is authorized to be used after the
          * user is successfully authenticated. This has effect if the key requires user
          * authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}).
@@ -1095,6 +1178,16 @@
         }
 
         /**
+         * Sets whether a test of user presence is required to be performed between the
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         */
+        @NonNull
+        public Builder setUserPresenceRequired(boolean required) {
+            mUserPresenceRequired = required;
+            return this;
+        }
+
+        /**
          * Sets whether an attestation certificate will be generated for this key pair, and what
          * challenge value will be placed in the certificate.  The attestation certificate chain
          * can be retrieved with with {@link java.security.KeyStore#getCertificateChain(String)}.
@@ -1197,6 +1290,19 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this
+         * key while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access. While the screen is locked, the key can
+         * still be used for encryption or signature verification.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1221,11 +1327,14 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mUserPresenceRequired,
                     mAttestationChallenge,
                     mUniqueIdIncluded,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
-                    mIsStrongBoxBacked);
+                    mIsStrongBoxBacked,
+                    mUserConfirmationRequired,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/android/security/keystore/KeyInfo.java b/android/security/keystore/KeyInfo.java
index f553319..0a75cd5 100644
--- a/android/security/keystore/KeyInfo.java
+++ b/android/security/keystore/KeyInfo.java
@@ -80,7 +80,9 @@
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
     private final boolean mUserAuthenticationValidWhileOnBody;
+    private final boolean mTrustedUserPresenceRequired;
     private final boolean mInvalidatedByBiometricEnrollment;
+    private final boolean mUserConfirmationRequired;
 
     /**
      * @hide
@@ -101,7 +103,9 @@
             int userAuthenticationValidityDurationSeconds,
             boolean userAuthenticationRequirementEnforcedBySecureHardware,
             boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment) {
+            boolean trustedUserPresenceRequired,
+            boolean invalidatedByBiometricEnrollment,
+            boolean userConfirmationRequired) {
         mKeystoreAlias = keystoreKeyAlias;
         mInsideSecureHardware = insideSecureHardware;
         mOrigin = origin;
@@ -121,7 +125,9 @@
         mUserAuthenticationRequirementEnforcedBySecureHardware =
                 userAuthenticationRequirementEnforcedBySecureHardware;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
+        mTrustedUserPresenceRequired = trustedUserPresenceRequired;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
+        mUserConfirmationRequired = userConfirmationRequired;
     }
 
     /**
@@ -257,6 +263,27 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+     * user.
+     *
+     * Confirmation is separate from user authentication (see
+     * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
+     * not user authentication, or user authentication but not confirmation, or both. Confirmation
+     * verifies that some user with physical possession of the device has approved a displayed
+     * message. User authentication verifies that the correct user is present and has
+     * authenticated.
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @see KeyGenParameterSpec.Builder#setUserConfirmationRequired(boolean)
+     * @see KeyProtection.Builder#setUserConfirmationRequired(boolean)
+     */
+    public boolean isUserConfirmationRequired() {
+        return mUserConfirmationRequired;
+    }
+
+    /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
      * (see {@link #isUserAuthenticationRequired()}).
@@ -301,4 +328,12 @@
     public boolean isInvalidatedByBiometricEnrollment() {
         return mInvalidatedByBiometricEnrollment;
     }
+
+    /**
+     * Returns {@code true} if the key can only be only be used if a test for user presence has
+     * succeeded since Signature.initSign() has been called.
+     */
+    public boolean isTrustedUserPresenceRequired() {
+        return mTrustedUserPresenceRequired;
+    }
 }
diff --git a/android/security/keystore/KeyProperties.java b/android/security/keystore/KeyProperties.java
index f54b6de..f12a659 100644
--- a/android/security/keystore/KeyProperties.java
+++ b/android/security/keystore/KeyProperties.java
@@ -689,7 +689,11 @@
      */
     public static final int ORIGIN_UNKNOWN = 1 << 2;
 
-    /** Key was imported into the AndroidKeyStore in an encrypted wrapper */
+    /**
+     * Key was imported into the AndroidKeyStore in an encrypted wrapper. Unlike imported keys,
+     * securely imported keys can be imported without appearing as plaintext in the device's host
+     * memory.
+     */
     public static final int ORIGIN_SECURELY_IMPORTED = 1 << 3;
 
 
diff --git a/android/security/keystore/KeyProtection.java b/android/security/keystore/KeyProtection.java
index dbacb9c..4daf30c 100644
--- a/android/security/keystore/KeyProtection.java
+++ b/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
  * ...
  * }</pre>
  */
-public final class KeyProtection implements ProtectionParameter {
+public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
@@ -224,10 +224,13 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final boolean mUserPresenceRequred;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final long mBoundToSecureUserId;
     private final boolean mCriticalToDeviceEncryption;
+    private final boolean mUserConfirmationRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -241,10 +244,13 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            boolean userPresenceRequred,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
-            boolean criticalToDeviceEncryption) {
+            boolean criticalToDeviceEncryption,
+            boolean userConfirmationRequired,
+            boolean unlockedDeviceRequired) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -258,10 +264,13 @@
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mUserPresenceRequred = userPresenceRequred;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mBoundToSecureUserId = boundToSecureUserId;
         mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+        mUserConfirmationRequired = userConfirmationRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -396,6 +405,26 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only for messages confirmed by the
+     * user.
+     *
+     * Confirmation is separate from user authentication (see
+     * {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
+     * not user authentication, or user authentication but not confirmation, or both. Confirmation
+     * verifies that some user with physical possession of the device has approved a displayed
+     * message. User authentication verifies that the correct user is present and has
+     * authenticated.
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @see Builder#setUserConfirmationRequired(boolean)
+     */
+    public boolean isUserConfirmationRequired() {
+        return mUserConfirmationRequired;
+    }
+
+    /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
      * (see {@link #isUserAuthenticationRequired()}).
@@ -414,6 +443,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     */
+    public boolean isUserPresenceRequired() {
+        return mUserPresenceRequred;
+    }
+
+    /**
      * Returns {@code true} if the key will be de-authorized when the device is removed from the
      * user's body.  This option has no effect on keys that don't have an authentication validity
      * duration, and has no effect if the device lacks an on-body sensor.
@@ -471,6 +508,17 @@
     }
 
     /**
+     * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
+     * signing. Decryption and signature verification will still be available when the screen is
+     * locked.
+     *
+     * @see Builder#setUnlockedDeviceRequired(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -486,8 +534,12 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private boolean mUserPresenceRequired = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
+        private boolean mUserConfirmationRequired;
+        private boolean mUnlockedDeviceRequired = false;
+
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
 
@@ -719,6 +771,29 @@
         }
 
         /**
+         * Sets whether this key is authorized to be used only for messages confirmed by the
+         * user.
+         *
+         * Confirmation is separate from user authentication (see
+         * {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
+         * confirmation but not user authentication, or user authentication but not confirmation,
+         * or both. Confirmation verifies that some user with physical possession of the device has
+         * approved a displayed message. User authentication verifies that the correct user is
+         * present and has authenticated.
+         *
+         * <p>This authorization applies only to secret key and private key operations. Public key
+         * operations are not restricted.
+         *
+         * @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
+         * more details about user confirmations.
+         */
+        @NonNull
+        public Builder setUserConfirmationRequired(boolean required) {
+            mUserConfirmationRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the duration of time (seconds) for which this key is authorized to be used after the
          * user is successfully authenticated. This has effect if the key requires user
          * authentication for its use (see {@link #setUserAuthenticationRequired(boolean)}).
@@ -764,6 +839,16 @@
         }
 
         /**
+         * Sets whether a test of user presence is required to be performed between the
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         */
+        @NonNull
+        public Builder setUserPresenceRequired(boolean required) {
+            mUserPresenceRequired = required;
+            return this;
+        }
+
+        /**
          * Sets whether the key will remain authorized only until the device is removed from the
          * user's body up to the limit of the authentication validity period (see
          * {@link #setUserAuthenticationValidityDurationSeconds} and
@@ -845,6 +930,19 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this
+         * key while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access. While the screen is locked, the key can
+         * still be used for encryption or signature verification.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -863,10 +961,13 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
-                    mCriticalToDeviceEncryption);
+                    mCriticalToDeviceEncryption,
+                    mUserConfirmationRequired,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/android/security/keystore/KeychainProtectionParams.java b/android/security/keystore/KeychainProtectionParams.java
index a940fdc..19a087d 100644
--- a/android/security/keystore/KeychainProtectionParams.java
+++ b/android/security/keystore/KeychainProtectionParams.java
@@ -28,23 +28,7 @@
 import java.util.Arrays;
 
 /**
- * A {@link KeychainSnapshot} is protected with a key derived from the user's lock screen. This
- * class wraps all the data necessary to derive the same key on a recovering device:
- *
- * <ul>
- *     <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
- *         the recovering device can display the pattern UI to the user when asking them to enter
- *         the lock screen from their previous device.
- *     <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
- * </ul>
- *
- * <p>As such, this data is sent along with the {@link KeychainSnapshot} when syncing the current
- * version of the keychain.
- *
- * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
- * user's lock screen. In the future, the keychain will support multiple layers of protection
- * (e.g. an additional keychain password, along with the lock screen).
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyChainProtectionParams}.
  * @hide
  */
 public final class KeychainProtectionParams implements Parcelable {
diff --git a/android/security/keystore/KeychainSnapshot.java b/android/security/keystore/KeychainSnapshot.java
index 23aec25..cf18fd1 100644
--- a/android/security/keystore/KeychainSnapshot.java
+++ b/android/security/keystore/KeychainSnapshot.java
@@ -25,21 +25,7 @@
 import java.util.List;
 
 /**
- * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
- *
- * <ul>
- *     <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
- *         protected the keychain, which is why this forces a new snapshot.)
- *     <li>A key is added to or removed from the recoverable keychain.
- * </ul>
- *
- * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
- * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
- * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
- * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
- * sends it to the framework, where it is decrypted using the user's lock screen from their previous
- * device.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.KeyChainSnapshot}.
  * @hide
  */
 public final class KeychainSnapshot implements Parcelable {
diff --git a/android/security/keystore/KeymasterUtils.java b/android/security/keystore/KeymasterUtils.java
index 34c8d1f..f829bb7 100644
--- a/android/security/keystore/KeymasterUtils.java
+++ b/android/security/keystore/KeymasterUtils.java
@@ -16,6 +16,7 @@
 
 package android.security.keystore;
 
+import android.app.ActivityManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
 import android.security.KeyStore;
@@ -93,22 +94,32 @@
      *        overriding the default logic in this method where the key is bound to either the root
      *        SID of the current user, or the fingerprint SID if explicit fingerprint authorization
      *        is requested.
+     * @param userConfirmationRequired whether user confirmation is required to authorize the use
+     *        of the key.
      * @throws IllegalStateException if user authentication is required but the system is in a wrong
      *         state (e.g., secure lock screen not set up) for generating or importing keys that
      *         require user authentication.
      */
-    public static void addUserAuthArgs(KeymasterArguments args,
-            boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment,
-            long boundToSpecificSecureUserId) {
-        if (!userAuthenticationRequired) {
+    public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) {
+
+        if (spec.isUserConfirmationRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
+        }
+
+        if (spec.isUserPresenceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+        }
+
+        if (spec.isUnlockedDeviceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
+        }
+
+        if (!spec.isUserAuthenticationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
         }
 
-        if (userAuthenticationValidityDurationSeconds == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint-only auth.
             FingerprintManager fingerprintManager =
@@ -124,9 +135,9 @@
             }
 
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
-            } else if (invalidatedByBiometricEnrollment) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
+            } else if (spec.isInvalidatedByBiometricEnrollment()) {
                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
                 // enrolled fingerprints, invalidating the key.
                 sid = fingerprintOnlySid;
@@ -139,14 +150,14 @@
             args.addUnsignedLong(
                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
-            if (userAuthenticationValidWhileOnBody) {
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
             } else {
                 // The key is authorized for use for the specified amount of time after the user has
                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -157,8 +168,8 @@
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
-                    userAuthenticationValidityDurationSeconds);
-            if (userAuthenticationValidWhileOnBody) {
+                    spec.getUserAuthenticationValidityDurationSeconds());
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
             }
         }
diff --git a/android/security/keystore/LockScreenRequiredException.java b/android/security/keystore/LockScreenRequiredException.java
index b07fb9c..0970284 100644
--- a/android/security/keystore/LockScreenRequiredException.java
+++ b/android/security/keystore/LockScreenRequiredException.java
@@ -17,10 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when trying to generate keys for a profile that has no lock screen set.
- *
- * <p>A lock screen must be set, as the lock screen is used to encrypt the snapshot.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.LockScreenRequiredException}.
  * @hide
  */
 public class LockScreenRequiredException extends RecoveryControllerException {
diff --git a/android/security/keystore/ParcelableKeyGenParameterSpec.java b/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 7cb8e37..911bbf8 100644
--- a/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -101,6 +101,7 @@
         out.writeBoolean(mSpec.isUniqueIdIncluded());
         out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
         out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
+        out.writeBoolean(mSpec.isUserPresenceRequired());
     }
 
     private static Date readDateOrNull(Parcel in) {
@@ -164,6 +165,7 @@
         builder.setUniqueIdIncluded(in.readBoolean());
         builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
         builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
+        builder.setUserPresenceRequired(in.readBoolean());
         mSpec = builder.build();
     }
 
diff --git a/android/security/keystore/RecoveryClaim.java b/android/security/keystore/RecoveryClaim.java
index 6f566af..12be607 100644
--- a/android/security/keystore/RecoveryClaim.java
+++ b/android/security/keystore/RecoveryClaim.java
@@ -17,8 +17,7 @@
 package android.security.keystore;
 
 /**
- * An attempt to recover a keychain protected by remote secure hardware.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
  * @hide
  */
 public class RecoveryClaim {
diff --git a/android/security/keystore/RecoveryController.java b/android/security/keystore/RecoveryController.java
index 8be6d52..ca67e35 100644
--- a/android/security/keystore/RecoveryController.java
+++ b/android/security/keystore/RecoveryController.java
@@ -21,7 +21,6 @@
 import android.app.PendingIntent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.util.Log;
 
@@ -31,22 +30,7 @@
 import java.util.Map;
 
 /**
- * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
- * other Android devices belonging to the user. The exported keychain is protected by the user's
- * lock screen.
- *
- * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
- * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
- * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
- * After  that number of incorrect guesses, the trusted hardware no longer allows access to the
- * key chain.
- *
- * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
- * recovery agent is itself the system app.
- *
- * <p>A recovery agent requires the privileged permission
- * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
  * @hide
  */
 public class RecoveryController {
@@ -114,12 +98,11 @@
     }
 
     /**
+     * Deprecated.
      * Gets a new instance of the class.
      */
     public static RecoveryController getInstance() {
-        ILockSettings lockSettings =
-                ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
-        return new RecoveryController(lockSettings);
+        throw new UnsupportedOperationException("using Deprecated RecoveryController version");
     }
 
     /**
@@ -143,16 +126,8 @@
     public void initRecoveryService(
             @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
             throws BadCertificateFormatException, InternalRecoveryServiceException {
-        try {
-            mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
-                throw new BadCertificateFormatException(e.getMessage());
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+        throw new UnsupportedOperationException("Deprecated initRecoveryService method called");
+
     }
 
     /**
@@ -167,7 +142,7 @@
     public @NonNull KeychainSnapshot getRecoveryData(@NonNull byte[] account)
             throws InternalRecoveryServiceException {
         try {
-            return BackwardsCompat.toLegacyKeychainSnapshot(mBinder.getRecoveryData(account));
+            return BackwardsCompat.toLegacyKeychainSnapshot(mBinder.getKeyChainSnapshot());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -210,17 +185,7 @@
      */
     public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
             throws InternalRecoveryServiceException {
-        try {
-            // IPC doesn't support generic Maps.
-            @SuppressWarnings("unchecked")
-            Map<byte[], Integer> result =
-                    (Map<byte[], Integer>) mBinder.getRecoverySnapshotVersions();
-            return result;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -259,7 +224,9 @@
             @NonNull String packageName, @Nullable String[] aliases, int status)
             throws NameNotFoundException, InternalRecoveryServiceException {
         try {
-            mBinder.setRecoveryStatus(packageName, aliases, status);
+            for (String alias : aliases) {
+                mBinder.setRecoveryStatus(alias, status);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -288,7 +255,7 @@
             // IPC doesn't support generic Maps.
             @SuppressWarnings("unchecked")
             Map<String, Integer> result =
-                    (Map<String, Integer>) mBinder.getRecoveryStatus(/*packageName=*/ null);
+                    (Map<String, Integer>) mBinder.getRecoveryStatus();
             return result;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -350,13 +317,7 @@
     @NonNull
     public @KeychainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
             throws InternalRecoveryServiceException {
-        try {
-            return mBinder.getPendingRecoverySecretTypes();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -472,16 +433,7 @@
      */
     public byte[] generateAndStoreKey(@NonNull String alias)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
-        try {
-            return mBinder.generateAndStoreKey(alias);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_INSECURE_USER) {
-                throw new LockScreenRequiredException(e.getMessage());
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+        throw new UnsupportedOperationException();
     }
 
     /**
diff --git a/android/security/keystore/RecoveryControllerException.java b/android/security/keystore/RecoveryControllerException.java
index 5b806b7..f990c23 100644
--- a/android/security/keystore/RecoveryControllerException.java
+++ b/android/security/keystore/RecoveryControllerException.java
@@ -19,8 +19,7 @@
 import java.security.GeneralSecurityException;
 
 /**
- * Base exception for errors thrown by {@link RecoveryController}.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
  * @hide
  */
 public abstract class RecoveryControllerException extends GeneralSecurityException {
diff --git a/android/security/keystore/RecoverySession.java b/android/security/keystore/RecoverySession.java
index ae8d91a..8a3e06b 100644
--- a/android/security/keystore/RecoverySession.java
+++ b/android/security/keystore/RecoverySession.java
@@ -19,9 +19,7 @@
 import java.security.SecureRandom;
 
 /**
- * Session to recover a {@link KeychainSnapshot} from the remote trusted hardware, initiated by a
- * recovery agent.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
  * @hide
  */
 public class RecoverySession implements AutoCloseable {
diff --git a/android/security/keystore/SecureKeyImportUnavailableException.java b/android/security/keystore/SecureKeyImportUnavailableException.java
new file mode 100644
index 0000000..d1cc572
--- /dev/null
+++ b/android/security/keystore/SecureKeyImportUnavailableException.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.security.keystore;
+
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+
+import java.security.ProviderException;
+
+/**
+ * Indicates that the Keystore does not support securely importing wrapped keys.
+ */
+public class SecureKeyImportUnavailableException extends ProviderException {
+
+    public SecureKeyImportUnavailableException() {
+        super();
+    }
+
+    public SecureKeyImportUnavailableException(String message) {
+        super(message, new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE,
+                "Secure Key Import not available"));
+    }
+
+    public SecureKeyImportUnavailableException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public SecureKeyImportUnavailableException(Throwable cause) {
+        super(cause);
+    }
+}
+
diff --git a/android/security/keystore/SessionExpiredException.java b/android/security/keystore/SessionExpiredException.java
index f13e206..7c8d5e4 100644
--- a/android/security/keystore/SessionExpiredException.java
+++ b/android/security/keystore/SessionExpiredException.java
@@ -17,8 +17,7 @@
 package android.security.keystore;
 
 /**
- * Error thrown when attempting to use a {@link RecoverySession} that has since expired.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.SessionExpiredException}.
  * @hide
  */
 public class SessionExpiredException extends RecoveryControllerException {
diff --git a/android/security/keystore/StrongBoxUnavailableException.java b/android/security/keystore/StrongBoxUnavailableException.java
index ad41a58..6c7e9a9 100644
--- a/android/security/keystore/StrongBoxUnavailableException.java
+++ b/android/security/keystore/StrongBoxUnavailableException.java
@@ -16,6 +16,9 @@
 
 package android.security.keystore;
 
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+
 import java.security.ProviderException;
 
 /**
@@ -24,5 +27,22 @@
  */
 public class StrongBoxUnavailableException extends ProviderException {
 
+    public StrongBoxUnavailableException() {
+        super();
+    }
+
+    public StrongBoxUnavailableException(String message) {
+        super(message,
+                new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, "No StrongBox available")
+        );
+    }
+
+    public StrongBoxUnavailableException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public StrongBoxUnavailableException(Throwable cause) {
+        super(cause);
+    }
 }
 
diff --git a/android/security/keystore/UserAuthArgs.java b/android/security/keystore/UserAuthArgs.java
new file mode 100644
index 0000000..6952060
--- /dev/null
+++ b/android/security/keystore/UserAuthArgs.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.security.keystore;
+
+/**
+ * @hide
+ *
+ * This is an interface to encapsulate the user authentication arguments that
+ * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent
+ * authorization characteristics for new or imported keys can implement this
+ * interface to be passed to that method.
+ */
+public interface UserAuthArgs {
+
+    boolean isUserAuthenticationRequired();
+    int getUserAuthenticationValidityDurationSeconds();
+    boolean isUserAuthenticationValidWhileOnBody();
+    boolean isInvalidatedByBiometricEnrollment();
+    boolean isUserConfirmationRequired();
+    long getBoundToSpecificSecureUserId();
+    boolean isUserPresenceRequired();
+    boolean isUnlockedDeviceRequired();
+}
diff --git a/android/security/keystore/UserPresenceUnavailableException.java b/android/security/keystore/UserPresenceUnavailableException.java
new file mode 100644
index 0000000..cf4099e
--- /dev/null
+++ b/android/security/keystore/UserPresenceUnavailableException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.security.keystore;
+
+import java.security.InvalidAlgorithmParameterException;
+
+/**
+ * Indicates the condition that a proof of user-presence was
+ * requested but this proof was not presented.
+ */
+public class UserPresenceUnavailableException extends InvalidAlgorithmParameterException {
+    /**
+     * Constructs a {@code UserPresenceUnavailableException} without a detail message or cause.
+     */
+    public UserPresenceUnavailableException() {
+        super("No Strong Box available.");
+    }
+
+    /**
+     * Constructs a {@code UserPresenceUnavailableException} using the provided detail message
+     * but no cause.
+     */
+    public UserPresenceUnavailableException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a {@code UserPresenceUnavailableException} using the provided detail message
+     * and cause.
+     */
+    public UserPresenceUnavailableException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/android/security/keystore/WrappedApplicationKey.java b/android/security/keystore/WrappedApplicationKey.java
index 522bb95..2ce8c7d 100644
--- a/android/security/keystore/WrappedApplicationKey.java
+++ b/android/security/keystore/WrappedApplicationKey.java
@@ -23,16 +23,7 @@
 import com.android.internal.util.Preconditions;
 
 /**
- * Helper class with data necessary recover a single application key, given a recovery key.
- *
- * <ul>
- *   <li>Alias - Keystore alias of the key.
- *   <li>Encrypted key material.
- * </ul>
- *
- * Note that Application info is not included. Recovery Agent can only make its own keys
- * recoverable.
- *
+ * @deprecated Use {@link android.security.keystore.recovery.WrappedApplicationKey}.
  * @hide
  */
 public final class WrappedApplicationKey implements Parcelable {
diff --git a/android/security/keystore/WrappedKeyEntry.java b/android/security/keystore/WrappedKeyEntry.java
index a8f4afe..7155686 100644
--- a/android/security/keystore/WrappedKeyEntry.java
+++ b/android/security/keystore/WrappedKeyEntry.java
@@ -21,7 +21,55 @@
 import java.security.spec.AlgorithmParameterSpec;
 
 /**
- * An {@link Entry} that holds a wrapped key.
+ * An {@link Entry} that holds a wrapped key. Wrapped keys contain encrypted key data and
+ * description information that can be used to securely import key material into a hardware-backed
+ * Keystore.
+ *
+ * <p>
+ *   The wrapped key is in DER-encoded ASN.1 format, specified by the following schema:
+ * </p>
+ *
+ * <pre>
+ *     KeyDescription ::= SEQUENCE(
+ *         keyFormat INTEGER,                   # Values from KeyFormat enum.
+ *         keyParams AuthorizationList,
+ *     )
+ *
+ *     SecureKeyWrapper ::= SEQUENCE(
+ *         version INTEGER,                     # Contains value 0
+ *         encryptedTransportKey OCTET_STRING,
+ *         initializationVector OCTET_STRING,
+ *         keyDescription KeyDescription,
+ *         encryptedKey OCTET_STRING,
+ *         tag OCTET_STRING
+ *     )
+ * </pre>
+ * <ul>
+ *     <li>keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
+ *       key material.
+ *     </li>
+ *     <li>keyParams is the characteristics of the key to be imported (as with generateKey or
+ *       importKey).  If the secure import is successful, these characteristics must be
+ *       associated with the key exactly as if the key material had been insecurely imported
+ *       with importKey. See <a href="https://developer.android.com/training/articles/security-key-attestation.html#certificate_schema">Key Attestation</a> for the AuthorizationList format.
+ *     </li>
+ *     <li>encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
+ *       in RSA-OAEP mode (SHA-256 digest, SHA-1 MGF1 digest) with the wrapping key specified by
+ *       wrappingKeyBlob.
+ *     </li>
+ *     <li>keyDescription is a KeyDescription, above.
+ *     </li>
+ *     <li>encryptedKey is the key material of the key to be imported, in format keyFormat, and
+ *       encrypted with encryptedEphemeralKey in AES-GCM mode, with the DER-encoded
+ *       representation of keyDescription provided as additional authenticated data.
+ *     </li>
+ *     <li>tag is the tag produced by the AES-GCM encryption of encryptedKey.
+ *     </li>
+ *</ul>
+ *
+ * <p>
+ *     Imported wrapped keys will have KeymasterDefs.KM_ORIGIN_SECURELY_IMPORTED
+ * </p>
  */
 public class WrappedKeyEntry implements Entry {
 
@@ -30,6 +78,14 @@
     private final String mTransformation;
     private final AlgorithmParameterSpec mAlgorithmParameterSpec;
 
+    /**
+     * Constructs a {@link WrappedKeyEntry} with a binary wrapped key.
+     *
+     * @param wrappedKeyBytes ASN.1 DER encoded wrapped key
+     * @param wrappingKeyAlias identifies the private key that can unwrap the wrapped key
+     * @param transformation used to unwrap the key. ex: "RSA/ECB/OAEPPadding"
+     * @param algorithmParameterSpec spec for the private key used to unwrap the wrapped key
+     */
     public WrappedKeyEntry(byte[] wrappedKeyBytes, String wrappingKeyAlias, String transformation,
             AlgorithmParameterSpec algorithmParameterSpec) {
         mWrappedKeyBytes = wrappedKeyBytes;
@@ -54,3 +110,4 @@
         return mAlgorithmParameterSpec;
     }
 }
+
diff --git a/android/security/keystore/recovery/BadCertificateFormatException.java b/android/security/keystore/recovery/BadCertificateFormatException.java
deleted file mode 100644
index e0781a5..0000000
--- a/android/security/keystore/recovery/BadCertificateFormatException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore.recovery;
-
-/**
- * Error thrown when the recovery agent supplies an invalid X509 certificate.
- *
- * @hide
- * Deprecated
- */
-public class BadCertificateFormatException extends RecoveryControllerException {
-    public BadCertificateFormatException(String msg) {
-        super(msg);
-    }
-}
diff --git a/android/security/keystore/recovery/InternalRecoveryServiceException.java b/android/security/keystore/recovery/InternalRecoveryServiceException.java
index 218d26e..e62bfb1 100644
--- a/android/security/keystore/recovery/InternalRecoveryServiceException.java
+++ b/android/security/keystore/recovery/InternalRecoveryServiceException.java
@@ -19,6 +19,7 @@
 import android.annotation.SystemApi;
 
 import java.security.GeneralSecurityException;
+
 /**
  * An error thrown when something went wrong internally in the recovery service.
  *
diff --git a/android/security/keystore/recovery/KeyChainProtectionParams.java b/android/security/keystore/recovery/KeyChainProtectionParams.java
index a43952a..4af1af5 100644
--- a/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -50,9 +50,25 @@
  */
 @SystemApi
 public final class KeyChainProtectionParams implements Parcelable {
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+    @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN})
     public @interface UserSecretType {
     }
 
@@ -61,11 +77,6 @@
      */
     public static final int TYPE_LOCKSCREEN = 100;
 
-    /**
-     * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
-     */
-    public static final int TYPE_CUSTOM_PASSWORD = 101;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"UI_FORMAT_"}, value = {UI_FORMAT_PIN, UI_FORMAT_PASSWORD, UI_FORMAT_PATTERN})
@@ -99,28 +110,12 @@
     private KeyDerivationParams mKeyDerivationParams;
     private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
 
-    /**
-     * @param secret Constructor creates a reference to the secret. Caller must use
-     * @link {#clearSecret} to overwrite its value in memory.
-     * @hide
-     */
-    public KeyChainProtectionParams(@UserSecretType int userSecretType,
-            @LockScreenUiFormat int lockScreenUiFormat,
-            @NonNull KeyDerivationParams keyDerivationParams,
-            @NonNull byte[] secret) {
-        mUserSecretType = userSecretType;
-        mLockScreenUiFormat = lockScreenUiFormat;
-        mKeyDerivationParams = Preconditions.checkNotNull(keyDerivationParams);
-        mSecret = Preconditions.checkNotNull(secret);
-    }
-
     private KeyChainProtectionParams() {
 
     }
 
     /**
      * @see TYPE_LOCKSCREEN
-     * @see TYPE_CUSTOM_PASSWORD
      */
     public @UserSecretType int getUserSecretType() {
         return mUserSecretType;
@@ -164,9 +159,9 @@
 
         /**
          * Sets user secret type.
+         * Default value is {@link TYPE_LOCKSCREEN}.
          *
          * @see TYPE_LOCKSCREEN
-         * @see TYPE_CUSTOM_PASSWORD
          * @param userSecretType The secret type
          * @return This builder.
          */
@@ -192,7 +187,7 @@
         /**
          * Sets parameters of the key derivation function.
          *
-         * @param keyDerivationParams Key derivation Params
+         * @param keyDerivationParams Key derivation parameters
          * @return This builder.
          */
         public Builder setKeyDerivationParams(@NonNull KeyDerivationParams
@@ -215,8 +210,8 @@
 
         /**
          * Creates a new {@link KeyChainProtectionParams} instance.
-         * The instance will include default values, if {@link setSecret}
-         * or {@link setUserSecretType} were not called.
+         * The instance will include default values, if {@link #setSecret}
+         * or {@link #setUserSecretType} were not called.
          *
          * @return new instance
          * @throws NullPointerException if some required fields were not set.
@@ -235,17 +230,7 @@
     }
 
     /**
-     * Removes secret from memory than object is no longer used.
-     * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        clearSecret();
-        super.finalize();
-    }
-
-    /**
-     * Fills mSecret with zeroes.
+     * Fills secret with zeroes.
      */
     public void clearSecret() {
         Arrays.fill(mSecret, (byte) 0);
diff --git a/android/security/keystore/recovery/KeyChainSnapshot.java b/android/security/keystore/recovery/KeyChainSnapshot.java
index df535ed..9334aa9 100644
--- a/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -18,11 +18,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.BadParcelableException;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.Preconditions;
 
+import java.security.cert.CertPath;
+import java.security.cert.CertificateException;
 import java.util.List;
 
 /**
@@ -45,6 +48,22 @@
  */
 @SystemApi
 public final class KeyChainSnapshot implements Parcelable {
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     private static final int DEFAULT_MAX_ATTEMPTS = 10;
     private static final long DEFAULT_COUNTER_ID = 1L;
 
@@ -52,43 +71,29 @@
     private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS;
     private long mCounterId = DEFAULT_COUNTER_ID;
     private byte[] mServerParams;
-    private byte[] mPublicKey;
+    private byte[] mPublicKey;  // The raw public key bytes used
+    private RecoveryCertPath mCertPath;  // The cert path including necessary intermediate certs
     private List<KeyChainProtectionParams> mKeyChainProtectionParams;
     private List<WrappedApplicationKey> mEntryRecoveryData;
     private byte[] mEncryptedRecoveryKeyBlob;
 
     /**
-     * @hide
-     * Deprecated, consider using builder.
+     * Use builder to create an instance of the class.
      */
-    public KeyChainSnapshot(
-            int snapshotVersion,
-            @NonNull List<KeyChainProtectionParams> keyChainProtectionParams,
-            @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
-            @NonNull byte[] encryptedRecoveryKeyBlob) {
-        mSnapshotVersion = snapshotVersion;
-        mKeyChainProtectionParams =
-                Preconditions.checkCollectionElementsNotNull(keyChainProtectionParams,
-                        "KeyChainProtectionParams");
-        mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
-                "wrappedApplicationKeys");
-        mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
-    }
-
     private KeyChainSnapshot() {
 
     }
 
     /**
-     * Snapshot version for given account. It is incremented when user secret or list of application
-     * keys changes.
+     * Snapshot version for given recovery agent. It is incremented when user secret or list of
+     * application keys changes.
      */
     public int getSnapshotVersion() {
         return mSnapshotVersion;
     }
 
     /**
-     * Number of user secret guesses allowed during Keychain recovery.
+     * Number of user secret guesses allowed during KeyChain recovery.
      */
     public int getMaxAttempts() {
         return mMaxAttempts;
@@ -111,14 +116,29 @@
     /**
      * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
      *
-     * See implementation for binary key format
+     * See implementation for binary key format.
+     *
+     * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
      */
-    // TODO: document key format.
+    @Deprecated
     public @NonNull byte[] getTrustedHardwarePublicKey() {
         return mPublicKey;
     }
 
     /**
+     * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
+     */
+    public @NonNull CertPath getTrustedHardwareCertPath() {
+        try {
+            return mCertPath.getCertPath();
+        } catch (CertificateException e) {
+            // Rethrow an unchecked exception as it should not happen. If such an issue exists,
+            // an exception should have been thrown during service initialization.
+            throw new BadParcelableException(e);
+        }
+    }
+
+    /**
      * UI and key derivation parameters. Note that combination of secrets may be used.
      */
     public @NonNull List<KeyChainProtectionParams> getKeyChainProtectionParams() {
@@ -159,7 +179,7 @@
         private KeyChainSnapshot mInstance = new KeyChainSnapshot();
 
         /**
-         * Snapshot version for given account.
+         * Snapshot version for the recovery agent.
          *
          * @param snapshotVersion The snapshot version
          * @return This builder.
@@ -207,21 +227,38 @@
          *
          * @param publicKey The public key
          * @return This builder.
+         * @deprecated Use {@link #setTrustedHardwareCertPath} instead.
          */
+        @Deprecated
         public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
             mInstance.mPublicKey = publicKey;
             return this;
         }
 
         /**
+         * Sets CertPath used to validate the trusted hardware public key. The CertPath should
+         * contain a certificate of the trusted hardware public key and any necessary intermediate
+         * certificates.
+         *
+         * @param certPath The certificate path
+         * @throws CertificateException if the given certificate path cannot be encoded properly
+         * @return This builder.
+         */
+        public Builder setTrustedHardwareCertPath(@NonNull CertPath certPath)
+                throws CertificateException {
+            mInstance.mCertPath = RecoveryCertPath.createRecoveryCertPath(certPath);
+            return this;
+        }
+
+        /**
          * Sets UI and key derivation parameters
          *
-         * @param recoveryMetadata The UI and key derivation parameters
+         * @param keyChainProtectionParams The UI and key derivation parameters
          * @return This builder.
          */
         public Builder setKeyChainProtectionParams(
-                @NonNull List<KeyChainProtectionParams> recoveryMetadata) {
-            mInstance.mKeyChainProtectionParams = recoveryMetadata;
+                @NonNull List<KeyChainProtectionParams> keyChainProtectionParams) {
+            mInstance.mKeyChainProtectionParams = keyChainProtectionParams;
             return this;
         }
 
@@ -237,7 +274,7 @@
         }
 
         /**
-         * Sets recovery key blob
+         * Sets recovery key blob.
          *
          * @param encryptedRecoveryKeyBlob The recovery key blob.
          * @return This builder.
@@ -252,16 +289,16 @@
          * Creates a new {@link KeyChainSnapshot} instance.
          *
          * @return new instance
-         * @throws NullPointerException if some required fields were not set.
+         * @throws NullPointerException if some of the required fields were not set.
          */
         @NonNull public KeyChainSnapshot build() {
             Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
-                    "recoveryMetadata");
+                    "keyChainProtectionParams");
             Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
                     "entryRecoveryData");
             Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
             Preconditions.checkNotNull(mInstance.mServerParams);
-            Preconditions.checkNotNull(mInstance.mPublicKey);
+            Preconditions.checkNotNull(mInstance.mCertPath);
             return mInstance;
         }
     }
@@ -276,6 +313,7 @@
         out.writeLong(mCounterId);
         out.writeByteArray(mServerParams);
         out.writeByteArray(mPublicKey);
+        out.writeTypedObject(mCertPath, /* no flags */ 0);
     }
 
     /**
@@ -290,6 +328,7 @@
         mCounterId = in.readLong();
         mServerParams = in.createByteArray();
         mPublicKey = in.createByteArray();
+        mCertPath = in.readTypedObject(RecoveryCertPath.CREATOR);
     }
 
     @Override
diff --git a/android/security/keystore/recovery/KeyDerivationParams.java b/android/security/keystore/recovery/KeyDerivationParams.java
index fc909a0..5165f0c 100644
--- a/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/android/security/keystore/recovery/KeyDerivationParams.java
@@ -22,7 +22,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -30,47 +29,88 @@
 
 /**
  * Collection of parameters which define a key derivation function.
- * Currently only supports salted SHA-256
+ * Currently only supports salted SHA-256.
  *
  * @hide
  */
 @SystemApi
 public final class KeyDerivationParams implements Parcelable {
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     private final int mAlgorithm;
-    private byte[] mSalt;
+    private final byte[] mSalt;
+    private final int mMemoryDifficulty;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_SCRYPT})
     public @interface KeyDerivationAlgorithm {
     }
 
     /**
-     * Salted SHA256
+     * Salted SHA256.
      */
     public static final int ALGORITHM_SHA256 = 1;
 
     /**
-     * Argon2ID
-     * @hide
+     * SCRYPT.
      */
-    // TODO: add Argon2ID support.
-    public static final int ALGORITHM_ARGON2ID = 2;
+    public static final int ALGORITHM_SCRYPT = 2;
 
     /**
-     * Creates instance of the class to to derive key using salted SHA256 hash.
+     * Creates instance of the class to to derive keys using salted SHA256 hash.
+     *
+     * <p>The salted SHA256 hash is computed over the concatenation of four byte strings, salt_len +
+     * salt + key_material_len + key_material, where salt_len and key_material_len are 4-byte, and
+     * denote the number of bytes for salt and key_material, respectively.
      */
-    public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
+    public static @NonNull KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
         return new KeyDerivationParams(ALGORITHM_SHA256, salt);
     }
 
     /**
+     * Creates instance of the class to to derive keys using the password hashing algorithm SCRYPT.
+     *
+     * <p>We expose only one tuning parameter of SCRYPT, which is the memory cost parameter (i.e. N
+     * in <a href="https://www.tarsnap.com/scrypt/scrypt.pdf">the SCRYPT paper</a>). Regular/default
+     * values are used for the other parameters, to keep the overall running time low. Specifically,
+     * the parallelization parameter p is 1, the block size parameter r is 8, and the hashing output
+     * length is 32-byte.
+     */
+    public static @NonNull KeyDerivationParams createScryptParams(
+            @NonNull byte[] salt, int memoryDifficulty) {
+        return new KeyDerivationParams(ALGORITHM_SCRYPT, salt, memoryDifficulty);
+    }
+
+    /**
      * @hide
      */
-    // TODO: Make private once legacy API is removed
-    public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+    private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+        this(algorithm, salt, /*memoryDifficulty=*/ -1);
+    }
+
+    /**
+     * @hide
+     */
+    private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
+            int memoryDifficulty) {
         mAlgorithm = algorithm;
         mSalt = Preconditions.checkNotNull(salt);
+        mMemoryDifficulty = memoryDifficulty;
     }
 
     /**
@@ -87,6 +127,19 @@
         return mSalt;
     }
 
+    /**
+     * Gets the memory difficulty parameter for the hashing algorithm.
+     *
+     * <p>The effect of this parameter depends on the algorithm in use. For example, please see
+     * {@link #createScryptParams(byte[], int)} for choosing the parameter for SCRYPT.
+     *
+     * <p>If the specific algorithm does not support such a memory difficulty parameter, its value
+     * should be -1.
+     */
+    public int getMemoryDifficulty() {
+        return mMemoryDifficulty;
+    }
+
     public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
             new Parcelable.Creator<KeyDerivationParams>() {
         public KeyDerivationParams createFromParcel(Parcel in) {
@@ -102,6 +155,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mAlgorithm);
         out.writeByteArray(mSalt);
+        out.writeInt(mMemoryDifficulty);
     }
 
     /**
@@ -110,6 +164,7 @@
     protected KeyDerivationParams(Parcel in) {
         mAlgorithm = in.readInt();
         mSalt = in.createByteArray();
+        mMemoryDifficulty = in.readInt();
     }
 
     @Override
diff --git a/android/security/keystore/recovery/RecoveryCertPath.java b/android/security/keystore/recovery/RecoveryCertPath.java
new file mode 100644
index 0000000..f3604fe
--- /dev/null
+++ b/android/security/keystore/recovery/RecoveryCertPath.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.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+/**
+ * The certificate path of the recovery service.
+ *
+ * @hide
+ */
+public final class RecoveryCertPath implements Parcelable {
+
+    private static final String CERT_PATH_ENCODING = "PkiPath";
+
+    private final byte[] mEncodedCertPath;
+
+    /**
+     * Wraps a {@code CertPath} to create a {@code Parcelable} for Binder calls.
+     *
+     * @param certPath The certificate path to be wrapped.
+     * @throws CertificateException if the given certificate path cannot be encoded properly.
+     */
+    public static @NonNull RecoveryCertPath createRecoveryCertPath(@NonNull CertPath certPath)
+            throws CertificateException {
+        // Perform the encoding here to avoid throwing exceptions in writeToParcel
+        try {
+            return new RecoveryCertPath(encodeCertPath(certPath));
+        } catch (CertificateEncodingException e) {
+            throw new CertificateException("Failed to encode the given CertPath", e);
+        }
+    }
+
+    /**
+     * Obtains the {@code CertPath} wrapped in the Parcelable.
+     *
+     * @return the wrapped certificate path.
+     * @throws CertificateException if the wrapped certificate path cannot be decoded properly.
+     */
+    public @NonNull CertPath getCertPath() throws CertificateException {
+        // Perform the decoding here to avoid throwing exceptions in createFromParcel
+        return decodeCertPath(mEncodedCertPath);
+    }
+
+    private RecoveryCertPath(@NonNull byte[] encodedCertPath) {
+        mEncodedCertPath = Preconditions.checkNotNull(encodedCertPath);
+    }
+
+    private RecoveryCertPath(Parcel in) {
+        mEncodedCertPath = in.createByteArray();
+    }
+
+    public static final Parcelable.Creator<RecoveryCertPath> CREATOR =
+            new Parcelable.Creator<RecoveryCertPath>() {
+        public RecoveryCertPath createFromParcel(Parcel in) {
+            return new RecoveryCertPath(in);
+        }
+
+        public RecoveryCertPath[] newArray(int length) {
+            return new RecoveryCertPath[length];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(mEncodedCertPath);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    private static byte[] encodeCertPath(@NonNull CertPath certPath)
+            throws CertificateEncodingException {
+        Preconditions.checkNotNull(certPath);
+        return certPath.getEncoded(CERT_PATH_ENCODING);
+    }
+
+    @NonNull
+    private static CertPath decodeCertPath(@NonNull byte[] bytes) throws CertificateException {
+        Preconditions.checkNotNull(bytes);
+        CertificateFactory certFactory;
+        try {
+            certFactory = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            // Should not happen, as X.509 is mandatory for all providers.
+            throw new RuntimeException(e);
+        }
+        return certFactory.generateCertPath(new ByteArrayInputStream(bytes), CERT_PATH_ENCODING);
+    }
+}
diff --git a/android/security/keystore/recovery/RecoveryClaim.java b/android/security/keystore/recovery/RecoveryClaim.java
deleted file mode 100644
index 45c6b4f..0000000
--- a/android/security/keystore/recovery/RecoveryClaim.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore.recovery;
-
-/**
- * An attempt to recover a keychain protected by remote secure hardware.
- *
- * @hide
- * Deprecated
- */
-public class RecoveryClaim {
-
-    private final RecoverySession mRecoverySession;
-    private final byte[] mClaimBytes;
-
-    RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) {
-        mRecoverySession = recoverySession;
-        mClaimBytes = claimBytes;
-    }
-
-    /**
-     * Returns the session associated with the recovery attempt. This is used to match the symmetric
-     * key, which remains internal to the framework, for decrypting the claim response.
-     *
-     * @return The session data.
-     */
-    public RecoverySession getRecoverySession() {
-        return mRecoverySession;
-    }
-
-    /**
-     * Returns the encrypted claim's bytes.
-     *
-     * <p>This should be sent by the recovery agent to the remote secure hardware, which will use
-     * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key
-     * to the device.
-     */
-    public byte[] getClaimBytes() {
-        return mClaimBytes;
-    }
-}
diff --git a/android/security/keystore/recovery/RecoveryController.java b/android/security/keystore/recovery/RecoveryController.java
index 71a36f1..281822a 100644
--- a/android/security/keystore/recovery/RecoveryController.java
+++ b/android/security/keystore/recovery/RecoveryController.java
@@ -26,30 +26,148 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
 
 import com.android.internal.widget.ILockSettings;
 
+import java.security.Key;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 /**
- * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
- * other Android devices belonging to the user. The exported keychain is protected by the user's
- * lock screen.
+ * Backs up cryptographic keys to remote secure hardware, encrypted with the user's lock screen.
  *
- * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
- * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
- * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
- * After  that number of incorrect guesses, the trusted hardware no longer allows access to the
- * key chain.
+ * <p>A system app with the {@code android.permission.RECOVER_KEYSTORE} permission may generate or
+ * import recoverable keys using this class. To generate a key, the app must call
+ * {@link #generateKey(String)} with the desired alias for the key. This returns an AndroidKeyStore
+ * reference to a 256-bit {@link javax.crypto.SecretKey}, which can be used for AES/GCM/NoPadding.
+ * In order to get the same key again at a later time, the app can call {@link #getKey(String)} with
+ * the same alias. If a key is generated in this way the key's raw material is never directly
+ * exposed to the calling app. The system app may also import key material using
+ * {@link #importKey(String, byte[])}. The app may only generate and import keys for its own
+ * {@code uid}.
  *
- * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
- * recovery agent is itself the system app.
+ * <p>The same system app must also register a Recovery Agent to manage syncing recoverable keys to
+ * remote secure hardware. The Recovery Agent is a service that registers itself with the controller
+ * as follows:
  *
- * <p>A recovery agent requires the privileged permission
- * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
+ * <ul>
+ *     <li>Invokes {@link #initRecoveryService(String, byte[], byte[])}
+ *     <ul>
+ *         <li>The first argument is the alias of the root certificate used to verify trusted
+ *         hardware modules. Each trusted hardware module must have a public key signed with this
+ *         root of trust. Roots of trust must be shipped with the framework. The app can list all
+ *         valid roots of trust by calling {@link #getRootCertificates()}.
+ *         <li>The second argument is the UTF-8 bytes of the XML listing file. It lists the X509
+ *         certificates containing the public keys of all available remote trusted hardware modules.
+ *         Each of the X509 certificates can be validated against the chosen root of trust.
+ *         <li>The third argument is the UTF-8 bytes of the XML signing file. The file contains a
+ *         signature of the XML listing file. The signature can be validated against the chosen root
+ *         of trust.
+ *     </ul>
+ *     <p>This will cause the controller to choose a random public key from the list. From then
+ *     on the controller will attempt to sync the key chain with the trusted hardware module to whom
+ *     that key belongs.
+ *     <li>Invokes {@link #setServerParams(byte[])} with a byte string that identifies the device
+ *     to a remote server. This server may act as the front-end to the trusted hardware modules. It
+ *     is up to the Recovery Agent to decide how best to identify devices, but this could be, e.g.,
+ *     based on the <a href="https://developers.google.com/instance-id/">Instance ID</a> of the
+ *     system app.
+ *     <li>Invokes {@link #setRecoverySecretTypes(int[])} with a list of types of secret used to
+ *     secure the recoverable key chain. For now only
+ *     {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} is supported.
+ *     <li>Invokes {@link #setSnapshotCreatedPendingIntent(PendingIntent)} with a
+ *     {@link PendingIntent} that is to be invoked whenever a new snapshot is created. Although the
+ *     controller can create snapshots without the Recovery Agent registering this intent, it is a
+ *     good idea to register the intent so that the Recovery Agent is able to sync this snapshot to
+ *     the trusted hardware module as soon as it is available.
+ * </ul>
+ *
+ * <p>The trusted hardware module's public key MUST be generated on secure hardware with protections
+ * equivalent to those described in the
+ * <a href="https://developer.android.com/preview/features/security/ckv-whitepaper.html">Google
+ * Cloud Key Vault Service whitepaper</a>. The trusted hardware module itself must protect the key
+ * chain from brute-forcing using the methods also described in the whitepaper: i.e., it should
+ * limit the number of allowed attempts to enter the lock screen. If the number of attempts is
+ * exceeded the key material must no longer be recoverable.
+ *
+ * <p>A recoverable key chain snapshot is considered pending if any of the following conditions
+ * are met:
+ *
+ * <ul>
+ *     <li>The system app mutates the key chain. i.e., generates, imports, or removes a key.
+ *     <li>The user changes their lock screen.
+ * </ul>
+ *
+ * <p>Whenever the user unlocks their device, if a snapshot is pending, the Recovery Controller
+ * generates a new snapshot. It follows these steps to do so:
+ *
+ * <ul>
+ *     <li>Generates a 256-bit AES key using {@link java.security.SecureRandom}. This is the
+ *     Recovery Key.
+ *     <li>Wraps the key material of all keys in the recoverable key chain with the Recovery Key.
+ *     <li>Encrypts the Recovery Key with both the public key of the trusted hardware module and a
+ *     symmetric key derived from the user's lock screen.
+ * </ul>
+ *
+ * <p>The controller then writes this snapshot to disk, and uses the {@link PendingIntent} that was
+ * set by the Recovery Agent during initialization to inform it that a new snapshot is available.
+ * The snapshot only contains keys for that Recovery Agent's {@code uid} - i.e., keys the agent's
+ * app itself generated. If multiple Recovery Agents exist on the device, each will be notified of
+ * their new snapshots, and each snapshots' keys will be only those belonging to the same
+ * {@code uid}.
+ *
+ * <p>The Recovery Agent retrieves its most recent snapshot by calling
+ * {@link #getKeyChainSnapshot()}. It syncs the snapshot to the remote server. The snapshot contains
+ * the public key used for encryption, which the server uses to forward the encrypted recovery key
+ * to the correct trusted hardware module. The snapshot also contains the server params, which are
+ * used to identify this device to the server.
+ *
+ * <p>The client uses the server params to identify a device whose key chain it wishes to restore.
+ * This may be on a different device to the device that originally synced the key chain. The client
+ * sends the server params identifying the previous device to the server. The server returns the
+ * X509 certificate identifying the trusted hardware module in which the encrypted Recovery Key is
+ * stored. It also returns some vault parameters identifying that particular Recovery Key to the
+ * trusted hardware module. And it also returns a vault challenge, which is used as part of the
+ * vault opening protocol to ensure the recovery claim is fresh. See the whitepaper for more
+ * details.
+ *
+ * <p>The key chain is recovered via a {@link RecoverySession}. A Recovery Agent creates one by
+ * invoking {@link #createRecoverySession()}. It then invokes
+ * {@link RecoverySession#start(String, CertPath, byte[], byte[], List)} with these arguments:
+ *
+ * <ul>
+ *     <li>The alias of the root of trust used to verify the trusted hardware module.
+ *     <li>The X509 certificate of the trusted hardware module.
+ *     <li>The vault parameters used to identify the Recovery Key to the trusted hardware module.
+ *     <li>The vault challenge, as issued by the trusted hardware module.
+ *     <li>A list of secrets, corresponding to the secrets used to protect the key chain. At the
+ *     moment this is a single {@link KeyChainProtectionParams} containing the lock screen of the
+ *     device whose key chain is to be recovered.
+ * </ul>
+ *
+ * <p>This method returns a byte array containing the Recovery Claim, which can be issued to the
+ * remote trusted hardware module. It is encrypted with the trusted hardware module's public key
+ * (which has itself been certified with the root of trust). It also contains an ephemeral symmetric
+ * key generated for this recovery session, which the remote trusted hardware module uses to encrypt
+ * its responses. This is the Session Key.
+ *
+ * <p>If the lock screen provided is correct, the remote trusted hardware module decrypts one of the
+ * layers of lock-screen encryption from the Recovery Key. It then returns this key, encrypted with
+ * the Session Key to the Recovery Agent. As the Recovery Agent does not know the Session Key, it
+ * must then invoke {@link RecoverySession#recoverKeyChainSnapshot(byte[], List)} with the encrypted
+ * Recovery Key and the list of wrapped application keys. The controller then decrypts the layer of
+ * encryption provided by the Session Key, and uses the lock screen to decrypt the final layer of
+ * encryption. It then uses the Recovery Key to decrypt all of the wrapped application keys, and
+ * imports them into its own KeyStore. The Recovery Agent's app may then access these keys by
+ * calling {@link #getKey(String)}. Only this app's {@code uid} may access the keys that have been
+ * recovered.
  *
  * @hide
  */
@@ -61,8 +179,6 @@
     public static final int RECOVERY_STATUS_SYNCED = 0;
     /** Waiting for recovery agent to sync the key. */
     public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
-    /** Recovery account is not available. */
-    public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
     /** Key cannot be synced. */
     public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;
 
@@ -97,7 +213,11 @@
     public static final int ERROR_SESSION_EXPIRED = 24;
 
     /**
-     * Failed because the provided certificate was not a valid X509 certificate.
+     * Failed because the format of the provided certificate is incorrect, e.g., cannot be decoded
+     * properly or misses necessary fields.
+     *
+     * <p>Note that this is different from {@link #ERROR_INVALID_CERTIFICATE}, which implies the
+     * certificate has a correct format but cannot be validated.
      *
      * @hide
      */
@@ -111,11 +231,31 @@
      */
     public static final int ERROR_DECRYPTION_FAILED = 26;
 
+    /**
+     * Error thrown if the format of a given key is invalid. This might be because the key has a
+     * wrong length, invalid content, etc.
+     *
+     * @hide
+     */
+    public static final int ERROR_INVALID_KEY_FORMAT = 27;
+
+    /**
+     * Failed because the provided certificate cannot be validated, e.g., is expired or has invalid
+     * signatures.
+     *
+     * <p>Note that this is different from {@link #ERROR_BAD_CERTIFICATE_FORMAT}, which denotes
+     * incorrect certificate formats, e.g., due to wrong encoding or structure.
+     *
+     * @hide
+     */
+    public static final int ERROR_INVALID_CERTIFICATE = 28;
 
     private final ILockSettings mBinder;
+    private final KeyStore mKeyStore;
 
-    private RecoveryController(ILockSettings binder) {
+    private RecoveryController(ILockSettings binder, KeyStore keystore) {
         mBinder = binder;
+        mKeyStore = keystore;
     }
 
     /**
@@ -130,30 +270,17 @@
     /**
      * Gets a new instance of the class.
      */
-    public static RecoveryController getInstance(Context context) {
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public static RecoveryController getInstance(@NonNull Context context) {
         ILockSettings lockSettings =
                 ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
-        return new RecoveryController(lockSettings);
+        return new RecoveryController(lockSettings, KeyStore.getInstance());
     }
 
     /**
-     * Initializes key recovery service for the calling application. RecoveryController
-     * randomly chooses one of the keys from the list and keeps it to use for future key export
-     * operations. Collection of all keys in the list must be signed by the provided {@code
-     * rootCertificateAlias}, which must also be present in the list of root certificates
-     * preinstalled on the device. The random selection allows RecoveryController to select
-     * which of a set of remote recovery service devices will be used.
-     *
-     * <p>In addition, RecoveryController enforces a delay of three months between
-     * consecutive initialization attempts, to limit the ability of an attacker to often switch
-     * remote recovery devices and significantly increase number of recovery attempts.
-     *
-     * @param rootCertificateAlias alias of a root certificate preinstalled on the device
-     * @param signedPublicKeyList binary blob a list of X509 certificates and signature
-     * @throws CertificateException if the {@code signedPublicKeyList} is in a bad format.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
+     * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void initRecoveryService(
             @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
@@ -163,26 +290,82 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
-                throw new CertificateException(e.getMessage());
+            if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException("Invalid certificate for recovery service", e);
             }
             throw wrapUnexpectedServiceSpecificException(e);
         }
     }
 
     /**
-     * Returns data necessary to store all recoverable keys. Key material is
-     * encrypted with user secret and recovery public key.
+     * Initializes the recovery service for the calling application. The detailed steps should be:
+     * <ol>
+     *     <li>Parse {@code signatureFile} to get relevant information.
+     *     <li>Validate the signer's X509 certificate, contained in {@code signatureFile}, against
+     *         the root certificate pre-installed in the OS and chosen by {@code
+     *         rootCertificateAlias}.
+     *     <li>Verify the public-key signature, contained in {@code signatureFile}, and verify it
+     *         against the entire {@code certificateFile}.
+     *     <li>Parse {@code certificateFile} to get relevant information.
+     *     <li>Check the serial number, contained in {@code certificateFile}, and skip the following
+     *         steps if the serial number is not larger than the one previously stored.
+     *     <li>Randomly choose a X509 certificate from the endpoint X509 certificates, contained in
+     *         {@code certificateFile}, and validate it against the root certificate pre-installed
+     *         in the OS and chosen by {@code rootCertificateAlias}.
+     *     <li>Store the chosen X509 certificate and the serial in local database for later use.
+     * </ol>
      *
-     * @return Data necessary to recover keystore.
+     * @param rootCertificateAlias the alias of a root certificate pre-installed in the OS
+     * @param certificateFile the binary content of the XML file containing a list of recovery
+     *     service X509 certificates, and other metadata including the serial number
+     * @param signatureFile the binary content of the XML file containing the public-key signature
+     *     of the entire certificate file, and a signer's X509 certificate
+     * @throws CertificateException if the given certificate files cannot be parsed or validated
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public @NonNull KeyChainSnapshot getRecoveryData()
+    public void initRecoveryService(
+            @NonNull String rootCertificateAlias, @NonNull byte[] certificateFile,
+            @NonNull byte[] signatureFile)
+            throws CertificateException, InternalRecoveryServiceException {
+        try {
+            mBinder.initRecoveryServiceWithSigFile(
+                    rootCertificateAlias, certificateFile, signatureFile);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException("Invalid certificate for recovery service", e);
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #getKeyChainSnapshot()}
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @Nullable KeyChainSnapshot getRecoveryData() throws InternalRecoveryServiceException {
+        return getKeyChainSnapshot();
+    }
+
+    /**
+     * Returns data necessary to store all recoverable keys. Key material is
+     * encrypted with user secret and recovery public key.
+     *
+     * @return Data necessary to recover keystore or {@code null} if snapshot is not available.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @Nullable KeyChainSnapshot getKeyChainSnapshot()
             throws InternalRecoveryServiceException {
         try {
-            return mBinder.getRecoveryData(/*account=*/ new byte[]{});
+            return mBinder.getKeyChainSnapshot();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -195,8 +378,8 @@
 
     /**
      * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
-     * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
-     * most one registered listener at any time.
+     * #getKeyChainSnapshot} can be used to get the snapshot. Note that every recovery agent can
+     * have at most one registered listener at any time.
      *
      * @param intent triggered when new snapshot is available. Unregisters listener if the value is
      *     {@code null}.
@@ -218,15 +401,16 @@
     /**
      * Server parameters used to generate new recovery key blobs. This value will be included in
      * {@code KeyChainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
-     * in vaultParams {@link #startRecoverySession}
+     * in vaultParams {@link RecoverySession#start(CertPath, byte[], byte[], List)}.
      *
      * @param serverParams included in recovery key blob.
-     * @see #getRecoveryData
+     * @see #getKeyChainSnapshot
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
+    public void setServerParams(@NonNull byte[] serverParams)
+            throws InternalRecoveryServiceException {
         try {
             mBinder.setServerParams(serverParams);
         } catch (RemoteException e) {
@@ -237,17 +421,22 @@
     }
 
     /**
-     * Gets aliases of recoverable keys for the application.
-     *
-     * @param packageName which recoverable keys' aliases will be returned.
-     *
-     * @return {@code List} of all aliases.
+     * @deprecated Use {@link #getAliases()}.
      */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public List<String> getAliases(@Nullable String packageName)
             throws InternalRecoveryServiceException {
+        return getAliases();
+    }
+
+    /**
+     * Returns a list of aliases of keys belonging to the application.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull List<String> getAliases() throws InternalRecoveryServiceException {
         try {
-            // TODO: update aidl
-            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
             return new ArrayList<>(allStatuses.keySet());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -257,24 +446,32 @@
     }
 
     /**
-     * Updates recovery status for given key. It is used to notify keystore that key was
-     * successfully stored on the server or there were an error. Application can check this value
-     * using {@code getRecoveyStatus}.
-     *
-     * @param packageName Application whose recoverable key's status are to be updated.
-     * @param alias Application-specific key alias.
-     * @param status Status specific to recovery agent.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
+     * @deprecated Use {@link #setRecoveryStatus(String, int)}
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void setRecoveryStatus(
             @NonNull String packageName, String alias, int status)
             throws NameNotFoundException, InternalRecoveryServiceException {
+        setRecoveryStatus(alias, status);
+    }
+
+    /**
+     * Sets the recovery status for given key. It is used to notify the keystore that the key was
+     * successfully stored on the server or that there was an error. An application can check this
+     * value using {@link #getRecoveryStatus(String, String)}.
+     *
+     * @param alias The alias of the key whose status to set.
+     * @param status The status of the key. One of {@link #RECOVERY_STATUS_SYNCED},
+     *     {@link #RECOVERY_STATUS_SYNC_IN_PROGRESS} or {@link #RECOVERY_STATUS_PERMANENT_FAILURE}.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public void setRecoveryStatus(@NonNull String alias, int status)
+            throws InternalRecoveryServiceException {
         try {
-            // TODO: update aidl
-            String[] aliases = alias == null ? null : new String[]{alias};
-            mBinder.setRecoveryStatus(packageName, aliases, status);
+            mBinder.setRecoveryStatus(alias, status);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -283,28 +480,32 @@
     }
 
     /**
-     * Returns recovery status for Application's KeyStore key.
-     * Negative status values are reserved for recovery agent specific codes. List of common codes:
+     * @deprecated Use {@link #getRecoveryStatus(String)}.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public int getRecoveryStatus(String packageName, String alias)
+            throws InternalRecoveryServiceException {
+        return getRecoveryStatus(alias);
+    }
+
+    /**
+     * Returns the recovery status for the key with the given {@code alias}.
      *
      * <ul>
      *   <li>{@link #RECOVERY_STATUS_SYNCED}
      *   <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
-     *   <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
      *   <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
      * </ul>
      *
-     * @param packageName Application whose recoverable key status is returned.
-     * @param alias Application-specific key alias.
-     * @return Recovery status.
-     * @see #setRecoveryStatus
+     * @see #setRecoveryStatus(String, int)
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
-    public int getRecoveryStatus(String packageName, String alias)
-            throws InternalRecoveryServiceException {
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public int getRecoveryStatus(@NonNull String alias) throws InternalRecoveryServiceException {
         try {
-            // TODO: update aidl
-            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
             Integer status = allStatuses.get(alias);
             if (status == null) {
                 return RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE;
@@ -322,11 +523,11 @@
      * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
      * is necessary to recover data.
      *
-     * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} or {@link
-     *     KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}
+     * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN}
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void setRecoverySecretTypes(
             @NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
             throws InternalRecoveryServiceException {
@@ -348,6 +549,7 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @NonNull @KeyChainProtectionParams.UserSecretType int[] getRecoverySecretTypes()
             throws InternalRecoveryServiceException {
         try {
@@ -360,49 +562,7 @@
     }
 
     /**
-     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
-     * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
-     * called.
-     *
-     * @return list of recovery secret types
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    @NonNull
-    public @KeyChainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
-            throws InternalRecoveryServiceException {
-        try {
-            return mBinder.getPendingRecoverySecretTypes();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Method notifies KeyStore that a user-generated secret is available. This method generates a
-     * symmetric session key which a trusted remote device can use to return a recovery key. Caller
-     * should use {@link KeyChainProtectionParams#clearSecret} to override the secret value in
-     * memory.
-     *
-     * @param recoverySecret user generated secret together with parameters necessary to regenerate
-     *     it on a new device.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
-            throws InternalRecoveryServiceException {
-        try {
-            mBinder.recoverySecretAvailable(recoverySecret);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
+     * Deprecated.
      * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
      * key store. Returns the raw material of the key.
      *
@@ -414,13 +574,43 @@
      *     to generate recoverable keys, as the snapshots are encrypted using a key derived from the
      *     lock screen.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
+        throw new UnsupportedOperationException("Operation is not supported, use generateKey");
+    }
+
+    /**
+     * @deprecated Use {@link #generateKey(String)}.
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public Key generateKey(@NonNull String alias, byte[] account)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        return generateKey(alias);
+    }
+
+    /**
+     * Generates a recoverable key with the given {@code alias}.
+     *
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+     *     screen is required to generate recoverable keys.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull Key generateKey(@NonNull String alias) throws InternalRecoveryServiceException,
+            LockScreenRequiredException {
         try {
-            // TODO: add account
-            return mBinder.generateAndStoreKey(alias);
+            String grantAlias = mBinder.generateKey(alias);
+            if (grantAlias == null) {
+                throw new InternalRecoveryServiceException("null grant alias");
+            }
+            return getKeyFromGrant(grantAlias);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } catch (UnrecoverableKeyException e) {
+            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
         } catch (ServiceSpecificException e) {
             if (e.errorCode == ERROR_INSECURE_USER) {
                 throw new LockScreenRequiredException(e.getMessage());
@@ -430,12 +620,79 @@
     }
 
     /**
+     * Imports a 256-bit recoverable AES key with the given {@code alias} and the raw bytes {@code
+     * keyBytes}.
+     *
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
+     *     screen is required to generate recoverable keys.
+     *
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull Key importKey(@NonNull String alias, @NonNull byte[] keyBytes)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        try {
+            String grantAlias = mBinder.importKey(alias, keyBytes);
+            if (grantAlias == null) {
+                throw new InternalRecoveryServiceException("Null grant alias");
+            }
+            return getKeyFromGrant(grantAlias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (UnrecoverableKeyException e) {
+            throw new InternalRecoveryServiceException("Failed to get key from keystore", e);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_INSECURE_USER) {
+                throw new LockScreenRequiredException(e.getMessage());
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Gets a key called {@code alias} from the recoverable key store.
+     *
+     * @param alias The key alias.
+     * @return The key.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws UnrecoverableKeyException if key is permanently invalidated or not found.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @Nullable Key getKey(@NonNull String alias)
+            throws InternalRecoveryServiceException, UnrecoverableKeyException {
+        try {
+            String grantAlias = mBinder.getKey(alias);
+            if (grantAlias == null || "".equals(grantAlias)) {
+                return null;
+            }
+            return getKeyFromGrant(grantAlias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Returns the key with the given {@code grantAlias}.
+     */
+    @NonNull Key getKeyFromGrant(@NonNull String grantAlias) throws UnrecoverableKeyException {
+        return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
+                mKeyStore,
+                grantAlias,
+                KeyStore.UID_SELF);
+    }
+
+    /**
      * Removes a key called {@code alias} from the recoverable key store.
      *
      * @param alias The key alias.
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void removeKey(@NonNull String alias) throws InternalRecoveryServiceException {
         try {
             mBinder.removeKey(alias);
@@ -446,6 +703,21 @@
         }
     }
 
+    /**
+     * Returns a new {@link RecoverySession}.
+     *
+     * <p>A recovery session is required to restore keys from a remote store.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull RecoverySession createRecoverySession() {
+        return RecoverySession.newInstance(this);
+    }
+
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull Map<String, X509Certificate> getRootCertificates() {
+        return TrustedRootCertificates.getRootCertificates();
+    }
+
     InternalRecoveryServiceException wrapUnexpectedServiceSpecificException(
             ServiceSpecificException e) {
         if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) {
diff --git a/android/security/keystore/recovery/RecoveryControllerException.java b/android/security/keystore/recovery/RecoveryControllerException.java
deleted file mode 100644
index 2733aca..0000000
--- a/android/security/keystore/recovery/RecoveryControllerException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore.recovery;
-
-import java.security.GeneralSecurityException;
-
-/**
- * Base exception for errors thrown by {@link RecoveryController}.
- *
- * @hide
- * Deprecated
- */
-public abstract class RecoveryControllerException extends GeneralSecurityException {
-    RecoveryControllerException() { }
-
-    RecoveryControllerException(String msg) {
-        super(msg);
-    }
-
-    public RecoveryControllerException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/android/security/keystore/recovery/RecoverySession.java b/android/security/keystore/recovery/RecoverySession.java
index 4db5d6e..8353389 100644
--- a/android/security/keystore/recovery/RecoverySession.java
+++ b/android/security/keystore/recovery/RecoverySession.java
@@ -16,16 +16,22 @@
 
 package android.security.keystore.recovery;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import java.security.Key;
 import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -43,23 +49,24 @@
     private final String mSessionId;
     private final RecoveryController mRecoveryController;
 
-    private RecoverySession(RecoveryController recoveryController, String sessionId) {
+    private RecoverySession(@NonNull RecoveryController recoveryController,
+            @NonNull String sessionId) {
         mRecoveryController = recoveryController;
         mSessionId = sessionId;
     }
 
     /**
-     * A new session, started by {@code recoveryManager}.
+     * A new session, started by the {@link RecoveryController}.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    static RecoverySession newInstance(RecoveryController recoveryController) {
+    static @NonNull RecoverySession newInstance(RecoveryController recoveryController) {
         return new RecoverySession(recoveryController, newSessionId());
     }
 
     /**
      * Returns a new random session ID.
      */
-    private static String newSessionId() {
+    private static @NonNull String newSessionId() {
         SecureRandom secureRandom = new SecureRandom();
         byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
         secureRandom.nextBytes(sessionId);
@@ -71,27 +78,9 @@
     }
 
     /**
-     * Starts a recovery session and returns a blob with proof of recovery secret possession.
-     * The method generates a symmetric key for a session, which trusted remote device can use to
-     * return recovery key.
-     *
-     * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key
-     *     used to create the recovery blob on the source device.
-     *     Keystore will verify the certificate using root of trust.
-     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
-     *     Used to limit number of guesses.
-     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
-     *     replay attacks
-     * @param secrets Secrets provided by user, the method only uses type and secret fields.
-     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
-     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
-     *     key and parameters necessary to identify the counter with the number of failed recovery
-     *     attempts.
-     * @throws CertificateException if the {@code verifierPublicKey} is in an incorrect
-     *     format.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
+     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     @NonNull public byte[] start(
             @NonNull byte[] verifierPublicKey,
@@ -111,25 +100,107 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
-            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) {
-                throw new CertificateException(e.getMessage());
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException("Invalid certificate for recovery session", e);
             }
             throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
         }
     }
 
     /**
-     * Imports keys.
-     *
-     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
-     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
-     *     and session. KeyStore only uses package names from the application info in {@link
-     *     WrappedApplicationKey}. Caller is responsibility to perform certificates check.
-     * @return Map from alias to raw key material.
-     * @throws SessionExpiredException if {@code session} has since been closed.
-     * @throws DecryptionFailedException if unable to decrypt the snapshot.
-     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
      */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public byte[] start(
+            @NonNull CertPath verifierCertPath,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws CertificateException, InternalRecoveryServiceException {
+        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
+        RecoveryCertPath recoveryCertPath =
+                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
+        try {
+            byte[] recoveryClaim =
+                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
+                            mSessionId,
+                            /*rootCertificateAlias=*/ "",  // Use the default root cert
+                            recoveryCertPath,
+                            vaultParams,
+                            vaultChallenge,
+                            secrets);
+            return recoveryClaim;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException("Invalid certificate for recovery session", e);
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Starts a recovery session and returns a blob with proof of recovery secret possession.
+     * The method generates a symmetric key for a session, which trusted remote device can use to
+     * return recovery key.
+     *
+     * @param rootCertificateAlias The alias of the root certificate that is already in the Android
+     *     OS. The root certificate will be used for validating {@code verifierCertPath}.
+     * @param verifierCertPath The certificate path used to create the recovery blob on the source
+     *     device. Keystore will verify the certificate path by using the root of trust.
+     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+     *     Used to limit number of guesses.
+     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+     *     replay attacks.
+     * @param secrets Secrets provided by user, the method only uses type and secret fields.
+     * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey
+     * and contains a proof of user secrets possession, session symmetric
+     *     key and parameters necessary to identify the counter with the number of failed recovery
+     *     attempts.
+     * @throws CertificateException if the {@code verifierCertPath} is invalid.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public byte[] start(
+            @NonNull String rootCertificateAlias,
+            @NonNull CertPath verifierCertPath,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws CertificateException, InternalRecoveryServiceException {
+        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
+        RecoveryCertPath recoveryCertPath =
+                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
+        try {
+            byte[] recoveryClaim =
+                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
+                            mSessionId,
+                            rootCertificateAlias,
+                            recoveryCertPath,
+                            vaultParams,
+                            vaultChallenge,
+                            secrets);
+            return recoveryClaim;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
+                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
+                throw new CertificateException("Invalid certificate for recovery session", e);
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
+     */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public Map<String, byte[]> recoverKeys(
             @NonNull byte[] recoveryKeyBlob,
@@ -153,6 +224,62 @@
     }
 
     /**
+     * Imports key chain snapshot recovered from a remote vault.
+     *
+     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+     *     and session key generated by {@link #start}.
+     * @return {@code Map} from recovered keys aliases to their references.
+     * @throws SessionExpiredException if {@code session} has since been closed.
+     * @throws DecryptionFailedException if unable to decrypt the snapshot.
+     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+     */
+    @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public Map<String, Key> recoverKeyChainSnapshot(
+            @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<WrappedApplicationKey> applicationKeys
+    ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException {
+        try {
+            Map<String, String> grantAliases = mRecoveryController
+                    .getBinder()
+                    .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys);
+            return getKeysFromGrants(grantAliases);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
+                throw new DecryptionFailedException(e.getMessage());
+            }
+            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
+                throw new SessionExpiredException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */
+    private @NonNull Map<String, Key> getKeysFromGrants(@NonNull Map<String, String> grantAliases)
+            throws InternalRecoveryServiceException {
+        ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size());
+        for (String alias : grantAliases.keySet()) {
+            String grantAlias = grantAliases.get(alias);
+            Key key;
+            try {
+                key = mRecoveryController.getKeyFromGrant(grantAlias);
+            } catch (UnrecoverableKeyException e) {
+                throw new InternalRecoveryServiceException(
+                        String.format(
+                                Locale.US,
+                                "Failed to get key '%s' from grant '%s'",
+                                alias,
+                                grantAlias), e);
+            }
+            keysByAlias.put(alias, key);
+        }
+        return keysByAlias;
+    }
+
+    /**
      * An internal session ID, used by the framework to match recovery claims to snapshot responses.
      *
      * @hide
@@ -162,8 +289,7 @@
     }
 
     /**
-     * Deletes all data associated with {@code session}. Should not be invoked directly but via
-     * {@link RecoverySession#close()}.
+     * Deletes all data associated with {@code session}.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     @Override
diff --git a/android/security/keystore/recovery/TrustedRootCertificates.java b/android/security/keystore/recovery/TrustedRootCertificates.java
new file mode 100644
index 0000000..a96dab6
--- /dev/null
+++ b/android/security/keystore/recovery/TrustedRootCertificates.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import static android.security.keystore.recovery.X509CertificateParsingUtils.decodeBase64Cert;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+/**
+ * Trusted root certificates for use by the
+ * {@link android.security.keystore.recovery.RecoveryController}. These certificates are used to
+ * verify the public keys of remote secure hardware modules. This is to prevent AOSP backing up keys
+ * to untrusted devices.
+ *
+ * @hide
+ */
+public final class TrustedRootCertificates {
+
+    public static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS =
+            "GoogleCloudKeyVaultServiceV1";
+    /**
+     * Certificate used for client-side end-to-end encryption tests.
+     * When recovery controller is initialized with the certificate, recovery snapshots will only
+     * contain application keys started with {@link #INSECURE_KEY_ALIAS_PREFIX}.
+     * Recovery snapshot will only be created if device is unlocked with password started with
+     * {@link #INSECURE_PASSWORD_PREFIX}.
+     *
+     * @hide
+     */
+    public static final String TEST_ONLY_INSECURE_CERTIFICATE_ALIAS =
+            "TEST_ONLY_INSECURE_CERTIFICATE_ALIAS";
+
+    /**
+     * TODO: Add insecure certificate to TestApi.
+     * @hide
+     */
+    public static @NonNull X509Certificate getTestOnlyInsecureCertificate() {
+        return parseBase64Certificate(TEST_ONLY_INSECURE_CERTIFICATE_BASE64);
+    }
+    /**
+     * Keys, which alias starts with the prefix are not protected if
+     * recovery agent uses {@link #TEST_ONLY_INSECURE_CERTIFICATE_ALIAS} root certificate.
+     * @hide
+     */
+    public static final String INSECURE_KEY_ALIAS_PREFIX =
+            "INSECURE_KEY_ALIAS_KEY_MATERIAL_IS_NOT_PROTECTED_";
+    /**
+     * Prefix for insecure passwords with length 14.
+     * Passwords started with the prefix are not protected if recovery agent uses
+     * {@link #TEST_ONLY_INSECURE_CERTIFICATE_ALIAS} root certificate.
+     * @hide
+     */
+    public static final String INSECURE_PASSWORD_PREFIX =
+            "INSECURE_PSWD_";
+
+    private static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64 = ""
+            + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
+            + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx"
+            + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
+            + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2OT5i40/H7LINg/lq/0G0hR65P"
+            + "Q4Mud3OnuVt6UIYV2T18+v6qW1yJd5FcnND/ZKPau4aUAYklqJuSVjOXQD0BjgS2"
+            + "98Xa4dSn8Ci1rUR+5tdmrxqbYUdT2ZvJIUMMR6fRoqi+LlAbKECrV+zYQTyLU68w"
+            + "V66hQpAButjJKiZzkXjmKLfJ5IWrNEn17XM988rk6qAQn/BYCCQGf3rQuJeksGmA"
+            + "N1lJOwNYxmWUyouVwqwZthNEWqTuEyBFMkAT+99PXW7oVDc7oU5cevuihxQWNTYq"
+            + "viGB8cck6RW3cmqrDSaJF/E+N0cXFKyYC7FDcggt6k3UrxNKTuySdDEa8+2RTQqU"
+            + "Y9npxBlQE+x9Ig56OI1BG3bSBsGdPgjpyHadZeh2tgk+oqlGsSsum24YxaxuSysT"
+            + "Qfcu/XhyfUXavfmGrBOXerTzIl5oBh/F5aHTV85M2tYEG0qsPPvSpZAWtdJ/2rca"
+            + "OxvhwOL+leZKr8McjXVR00lBsRuKXX4nTUMwya09CO3QHFPFZtZvqjy2HaMOnVLQ"
+            + "I6b6dHEfmsHybzVOe3yPEoFQSU9UhUdmi71kwwoanPD3j9fJHmXTx4PzYYBRf1ZE"
+            + "o+uPgMPk7CDKQFZLjnR40z1uzu3O8aZ3AKZzP+j7T4XQKJLQLmllKtPgLgNdJyib"
+            + "2Glg7QhXH/jBTL6hAgMBAAGjYzBhMB0GA1UdDgQWBBSbZfrqOYH54EJpkdKMZjMc"
+            + "z/Hp+DAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DAPBgNVHRMBAf8E"
+            + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQ0FAAOCAgEAKh9nm/vW"
+            + "glMWp3vcCwWwJW286ecREDlI+CjGh5h+f2N4QRrXd/tKE3qQJWCqGx8sFfIUjmI7"
+            + "KYdsC2gyQ2cA2zl0w7pB2QkuqE6zVbnh1D17Hwl19IMyAakFaM9ad4/EoH7oQmqX"
+            + "nF/f5QXGZw4kf1HcgKgoCHWXjqR8MqHOcXR8n6WFqxjzJf1jxzi6Yo2dZ7PJbnE6"
+            + "+kHIJuiCpiHL75v5g1HM41gT3ddFFSrn88ThNPWItT5Z8WpFjryVzank2Yt02LLl"
+            + "WqZg9IC375QULc5B58NMnaiVJIDJQ8zoNgj1yaxqtUMnJX570lotO2OXe4ec9aCQ"
+            + "DIJ84YLM/qStFdeZ9416E80dchskbDG04GuVJKlzWjxAQNMRFhyaPUSBTLLg+kwP"
+            + "t9+AMmc+A7xjtFQLZ9fBYHOBsndJOmeSQeYeckl+z/1WQf7DdwXn/yijon7mxz4z"
+            + "cCczfKwTJTwBh3wR5SQr2vQm7qaXM87qxF8PCAZrdZaw5I80QwkgTj0WTZ2/GdSw"
+            + "d3o5SyzzBAjpwtG+4bO/BD9h9wlTsHpT6yWOZs4OYAKU5ykQrncI8OyavMggArh3"
+            + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
+            + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
+
+    private static final String TEST_ONLY_INSECURE_CERTIFICATE_BASE64 = ""
+            + "MIIFMDCCAxigAwIBAgIJAIZ9/G8KQie9MA0GCSqGSIb3DQEBDQUAMCUxIzAhBgNV"
+            + "BAMMGlRlc3QgT25seSBVbnNlY3VyZSBSb290IENBMB4XDTE4MDMyODAwMzIyM1oX"
+            + "DTM4MDMyMzAwMzIyM1owJTEjMCEGA1UEAwwaVGVzdCBPbmx5IFVuc2VjdXJlIFJv"
+            + "b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGxFNzAEyzSPmw"
+            + "E5gfuBXdXq++bl9Ep62V7Xn1UiejvmS+pRHT39pf/M7sl4Zr9ezanJTrFvf9+B85"
+            + "VGehdsD32TgfEjThcqaoQCI6pKkHYsUo7FZ5n+G3eE8oabWRZJMVo3QDjnnFYp7z"
+            + "20vnpjDofI2oQyxHcb/1yep+ca1+4lIvbUp/ybhNFqhRXAMcDXo7pyH38eUQ1JdK"
+            + "Q/QlBbShpFEqx1Y6KilKfTDf7Wenqr67LkaEim//yLZjlHzn/BpuRTrpo+XmJZx1"
+            + "P9CX9LGOXTtmsaCcYgD4yijOvV8aEsIJaf1kCIO558oH0oQc+0JG5aXeLN7BDlyZ"
+            + "vH0RdSx5nQLS9kj2I6nthOw/q00/L+S6A0m5jyNZOAl1SY78p+wO0d9eHbqQzJwf"
+            + "EsSq3qGAqlgQyyjp6oxHBqT9hZtN4rxw+iq0K1S4kmTLNF1FvmIB1BE+lNvvoGdY"
+            + "5G0b6Pe4R5JFn9LV3C3PEmSYnae7iG0IQlKmRADIuvfJ7apWAVanJPJAAWh2Akfp"
+            + "8Uxr02cHoY6o7vsEhJJOeMkipaBHThESm/XeFVubQzNfZ9gjQnB9ZX2v+lyj+WYZ"
+            + "SAz3RuXx6TlLrmWccMpQDR1ibcgyyjLUtX3kwZl2OxmJXitjuD7xlxvAXYob15N+"
+            + "K4xKHgxUDrbt2zU/tY0vgepAUg/xbwIDAQABo2MwYTAdBgNVHQ4EFgQUwyeNpYgs"
+            + "XXYvh9z0/lFrja7sV+swHwYDVR0jBBgwFoAUwyeNpYgsXXYvh9z0/lFrja7sV+sw"
+            + "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQENBQAD"
+            + "ggIBAGuOsvMN5SD3RIQnMJtBpcHNrxun+QFjPZFlYCLfIPrUkHpn5O1iIIq8tVLd"
+            + "2V+12VKnToUEANsYBD3MP8XjP+6GZ7ZQ2rwLGvUABKSX4YXvmjEEXZUZp0y3tIV4"
+            + "kUDlbACzguPneZDp5Qo7YWH4orgqzHkn0sD/ikO5XrAqmzc245ewJlrf+V11mjcu"
+            + "ELfDrEejpPhi7Hk/ZNR0ftP737Hs/dNoCLCIaVNgYzBZhgo4kd220TeJu2ttW0XZ"
+            + "ldyShtpcOmyWKBgVseixR6L/3sspPHyAPXkSuRo0Eh1xvzDKCg9ttb0qoacTlXMF"
+            + "GkBpNzmVq67NWFGGa9UElift1mv6RfktPCAGZ+Ai8xUiKAUB0Eookpt/8gX9Senq"
+            + "yP/jMxkxXmHWxUu8+KnLvj6WLrfftuuD7u3cfc7j5kkrheDz3O4h4477GnqL5wdo"
+            + "9DuEsNc4FxJVz8Iy8RS6cJuW4pihYpM1Tyn7uopLnImpYzEY+R5aQqqr+q/A1diq"
+            + "ogbEKPH6oUiqJUwq3nD70gPBUKJmIzS4vLwLouqUHEm1k/MgHV/BkEU0uVHszPFa"
+            + "XUMMCHb0iT9P8LuZ7Ajer3SR/0TRVApCrk/6OV68e+6k/OFpM5kcZnNMD5ANyBri"
+            + "Tsz3NrDwSw4i4+Dsfh6A9dB/cEghw4skLaBxnQLQIgVeqCzK";
+
+    /**
+     * The X509 certificate of the trusted root CA cert for the recoverable key store service.
+     *
+     * TODO: Change it to the production certificate root CA before the final launch.
+     */
+    private static final X509Certificate GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_CERTIFICATE =
+            parseBase64Certificate(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64);
+
+    private static final int NUMBER_OF_ROOT_CERTIFICATES = 1;
+
+    private static final ArrayMap<String, X509Certificate> ALL_ROOT_CERTIFICATES =
+            constructRootCertificateMap();
+
+    /**
+     * Returns all available root certificates, keyed by alias.
+     */
+    public static @NonNull Map<String, X509Certificate> getRootCertificates() {
+        return new ArrayMap(ALL_ROOT_CERTIFICATES);
+    }
+
+    /**
+     * Gets a root certificate referenced by the given {@code alias}.
+     *
+     * @param alias the alias of the certificate
+     * @return the certificate referenced by the alias, or null if such a certificate doesn't exist.
+     */
+    public static @NonNull X509Certificate getRootCertificate(String alias) {
+        return ALL_ROOT_CERTIFICATES.get(alias);
+    }
+
+    private static ArrayMap<String, X509Certificate> constructRootCertificateMap() {
+        ArrayMap<String, X509Certificate> certificates =
+                new ArrayMap<>(NUMBER_OF_ROOT_CERTIFICATES);
+        certificates.put(
+                GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS,
+                GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_CERTIFICATE);
+        return certificates;
+    }
+
+    private static X509Certificate parseBase64Certificate(String base64Certificate) {
+        try {
+            return decodeBase64Cert(base64Certificate);
+        } catch (CertificateException e) {
+            // Should not happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    // Statics only
+    private TrustedRootCertificates() {}
+}
diff --git a/android/security/keystore/recovery/WrappedApplicationKey.java b/android/security/keystore/recovery/WrappedApplicationKey.java
index f360bbe..32952db 100644
--- a/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,7 +28,6 @@
  *
  * <ul>
  *   <li>Alias - Keystore alias of the key.
- *   <li>Account Recovery Agent specific account associated with the key.
  *   <li>Encrypted key material.
  * </ul>
  *
@@ -43,7 +41,21 @@
     private String mAlias;
     // The only supported format is AES-256 symmetric key.
     private byte[] mEncryptedKeyMaterial;
-    private byte[] mAccount;
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
 
     /**
      * Builder for creating {@link WrappedApplicationKey}.
@@ -63,13 +75,10 @@
         }
 
         /**
-         * Sets Recovery agent specific account.
-         *
-         * @param account The account.
-         * @return This builder.
+         * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
          */
+        @Deprecated
         public Builder setAccount(@NonNull byte[] account) {
-            mInstance.mAccount = account;
             return this;
         }
 
@@ -94,15 +103,11 @@
         @NonNull public WrappedApplicationKey build() {
             Preconditions.checkNotNull(mInstance.mAlias);
             Preconditions.checkNotNull(mInstance.mEncryptedKeyMaterial);
-            if (mInstance.mAccount == null) {
-                mInstance.mAccount = new byte[]{};
-            }
             return mInstance;
         }
     }
 
-    private WrappedApplicationKey() {
-    }
+    private WrappedApplicationKey() { }
 
     /**
      * Deprecated - consider using Builder.
@@ -127,12 +132,12 @@
         return mEncryptedKeyMaterial;
     }
 
-    /** Account, default value is empty array */
+    /**
+     * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
+     */
+    @Deprecated
     public @NonNull byte[] getAccount() {
-        if (mAccount == null) {
-            return new byte[]{};
-        }
-        return mAccount;
+        return new byte[0];
     }
 
     public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
@@ -150,7 +155,6 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(mAlias);
         out.writeByteArray(mEncryptedKeyMaterial);
-        out.writeByteArray(mAccount);
     }
 
     /**
@@ -159,7 +163,6 @@
     protected WrappedApplicationKey(Parcel in) {
         mAlias = in.readString();
         mEncryptedKeyMaterial = in.createByteArray();
-        mAccount = in.createByteArray();
     }
 
     @Override
diff --git a/android/security/keystore/recovery/X509CertificateParsingUtils.java b/android/security/keystore/recovery/X509CertificateParsingUtils.java
new file mode 100644
index 0000000..fa72c83
--- /dev/null
+++ b/android/security/keystore/recovery/X509CertificateParsingUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+
+/**
+ * Static helper methods for decoding {@link X509Certificate} instances.
+ *
+ * @hide
+ */
+public class X509CertificateParsingUtils {
+    private static final String CERT_FORMAT = "X.509";
+
+    /**
+     * Decodes an {@link X509Certificate} encoded as a base-64 string.
+     */
+    public static X509Certificate decodeBase64Cert(String string) throws CertificateException {
+        try {
+            return decodeCert(decodeBase64(string));
+        } catch (IllegalArgumentException e) {
+            throw new CertificateException(e);
+        }
+    }
+
+    /**
+     * Decodes a base-64 string.
+     *
+     * @throws IllegalArgumentException if not a valid base-64 string.
+     */
+    private static byte[] decodeBase64(String string) {
+        return Base64.getDecoder().decode(string);
+    }
+
+    /**
+     * Decodes a byte array containing an encoded X509 certificate.
+     *
+     * @param certBytes the byte array containing the encoded X509 certificate
+     * @return the decoded X509 certificate
+     * @throws CertificateException if any parsing error occurs
+     */
+    private static X509Certificate decodeCert(byte[] certBytes) throws CertificateException {
+        return decodeCert(new ByteArrayInputStream(certBytes));
+    }
+
+    /**
+     * Decodes an X509 certificate from an {@code InputStream}.
+     *
+     * @param inStream the input stream containing the encoded X509 certificate
+     * @return the decoded X509 certificate
+     * @throws CertificateException if any parsing error occurs
+     */
+    private static X509Certificate decodeCert(InputStream inStream) throws CertificateException {
+        CertificateFactory certFactory;
+        try {
+            certFactory = CertificateFactory.getInstance(CERT_FORMAT);
+        } catch (CertificateException e) {
+            // Should not happen, as X.509 is mandatory for all providers.
+            throw new RuntimeException(e);
+        }
+        return (X509Certificate) certFactory.generateCertificate(inStream);
+    }
+}
diff --git a/android/service/autofill/AutofillFieldClassificationService.java b/android/service/autofill/AutofillFieldClassificationService.java
index 1ef6100..1cd76d2 100644
--- a/android/service/autofill/AutofillFieldClassificationService.java
+++ b/android/service/autofill/AutofillFieldClassificationService.java
@@ -15,12 +15,15 @@
  */
 package android.service.autofill;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcel;
@@ -30,9 +33,6 @@
 import android.util.Log;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
-
 import java.util.Arrays;
 import java.util.List;
 
@@ -41,11 +41,11 @@
  *
  * <p>A field classification score is a {@code float} representing how well an
  * {@link AutofillValue} filled matches a expected value predicted by an autofill service
- * &mdash;a full-match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
+ * &mdash;a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
  *
- * <p>The exact score depends on the algorithm used to calculate it&mdash; the service must provide
+ * <p>The exact score depends on the algorithm used to calculate it&mdash;the service must provide
  * at least one default algorithm (which is used when the algorithm is not specified or is invalid),
- * but it could provide more (in which case the algorithm name should be specifiied by the caller
+ * but it could provide more (in which case the algorithm name should be specified by the caller
  * when calculating the scores).
  *
  * {@hide}
@@ -55,8 +55,6 @@
 
     private static final String TAG = "AutofillFieldClassificationService";
 
-    private static final int MSG_GET_SCORES = 1;
-
     /**
      * The {@link Intent} action that must be declared as handled by a service
      * in its manifest for the system to recognize it as a quota providing service.
@@ -83,35 +81,18 @@
 
     private AutofillFieldClassificationServiceWrapper mWrapper;
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        final int action = msg.what;
+    private void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
+            List<AutofillValue> actualValues, String[] userDataValues) {
         final Bundle data = new Bundle();
-        final RemoteCallback callback;
-        switch (action) {
-            case MSG_GET_SCORES:
-                final SomeArgs args = (SomeArgs) msg.obj;
-                callback = (RemoteCallback) args.arg1;
-                final String algorithmName = (String) args.arg2;
-                final Bundle algorithmArgs = (Bundle) args.arg3;
-                @SuppressWarnings("unchecked")
-                final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4);
-                @SuppressWarnings("unchecked")
-                final String[] userDataValues = (String[]) args.arg5;
-                final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
-                        Arrays.asList(userDataValues));
-                if (scores != null) {
-                    data.putParcelable(EXTRA_SCORES, new Scores(scores));
-                }
-                break;
-            default:
-                Log.w(TAG, "Handling unknown message: " + action);
-                return;
+        final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
+                Arrays.asList(userDataValues));
+        if (scores != null) {
+            data.putParcelable(EXTRA_SCORES, new Scores(scores));
         }
         callback.sendResult(data);
-    };
+    }
 
-    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
-            mHandlerCallback, true);
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     /** @hide */
     public AutofillFieldClassificationService() {
@@ -132,23 +113,67 @@
     /**
      * Calculates field classification scores in a batch.
      *
-     * <p>See {@link AutofillFieldClassificationService} for more info about field classification
-     * scores.
+     * <p>A field classification score is a {@code float} representing how well an
+     * {@link AutofillValue} filled matches a expected value predicted by an autofill service
+     * &mdash;a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
      *
-     * @param algorithm name of the algorithm to be used to calculate the scores. If invalid, the
-     * default algorithm will be used instead.
-     * @param args optional arguments to be passed to the algorithm.
+     * <p>The exact score depends on the algorithm used to calculate it&mdash;the service must
+     * provide at least one default algorithm (which is used when the algorithm is not specified
+     * or is invalid), but it could provide more (in which case the algorithm name should be
+     * specified by the caller when calculating the scores).
+     *
+     * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that
+     * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to:
+     *
+     * <pre>
+     * service.onGetScores("EXACT_MATCH", null,
+     *   Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+     *   Arrays.asList("email1", "phone1"));
+     * </pre>
+     *
+     * <p>Returns:
+     *
+     * <pre>
+     * [
+     *   [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+     *   [0.0, 0.0]  // "PHONE1" compared against ["email1", "phone1"]
+     * ];
+     * </pre>
+     *
+     * <p>If the same algorithm allows the caller to specify whether the comparisons should be
+     * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to:
+     *
+     * <pre>
+     * Bundle algorithmOptions = new Bundle();
+     * algorithmOptions.putBoolean("case_sensitive", false);
+     *
+     * service.onGetScores("EXACT_MATCH", algorithmOptions,
+     *   Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+     *   Arrays.asList("email1", "phone1"));
+     * </pre>
+     *
+     * <p>Returns:
+     *
+     * <pre>
+     * [
+     *   [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+     *   [0.0, 1.0]  // "PHONE1" compared against ["email1", "phone1"]
+     * ];
+     * </pre>
+     *
+     * @param algorithm name of the algorithm to be used to calculate the scores. If invalid or
+     * {@code null}, the default algorithm is used instead.
+     * @param algorithmOptions optional arguments to be passed to the algorithm.
      * @param actualValues values entered by the user.
      * @param userDataValues values predicted from the user data.
-     * @return the calculated scores, with the first dimension representing actual values and the
-     * second dimension values from {@link UserData}.
+     * @return the calculated scores of {@code actualValues} x {@code userDataValues}.
      *
      * {@hide}
      */
     @Nullable
     @SystemApi
     public float[][] onGetScores(@Nullable String algorithm,
-            @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
+            @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
         Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
         return null;
@@ -160,9 +185,10 @@
         public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
                 List<AutofillValue> actualValues, String[] userDataValues)
                         throws RemoteException {
-            // TODO(b/70939974): refactor to use PooledLambda
-            mHandlerCaller.obtainMessageOOOOO(MSG_GET_SCORES, callback, algorithmName,
-                    algorithmArgs, actualValues, userDataValues).sendToTarget();
+            mHandler.sendMessage(obtainMessage(
+                    AutofillFieldClassificationService::getScores,
+                    AutofillFieldClassificationService.this,
+                    callback, algorithmName, algorithmArgs, actualValues, userDataValues));
         }
     }
 
diff --git a/android/service/autofill/AutofillService.java b/android/service/autofill/AutofillService.java
index 917efa8..60537a4 100644
--- a/android/service/autofill/AutofillService.java
+++ b/android/service/autofill/AutofillService.java
@@ -15,6 +15,8 @@
  */
 package android.service.autofill;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -22,6 +24,7 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.CancellationSignal;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.Looper;
@@ -34,9 +37,6 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
-
 /**
  * An {@code AutofillService} is a service used to automatically fill the contents of the screen
  * on behalf of a given user - for more information about autofill, read
@@ -468,9 +468,8 @@
  * <p>Typically, field classification can be used to detect fields that can be autofilled with
  * user data that is not associated with a specific app&mdash;such as email and physical
  * address. Once the service identifies that a such field was manually filled by the user, the
- * service could use this signal to improve its heuristics, either locally (i.e., in the same
- * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
- * be used by other users).
+ * service could use this signal to improve its heuristics on subsequent requests (for example, by
+ * infering which resource ids are associated with known fields).
  *
  * <p>The field classification workflow involves 4 steps:
  *
@@ -481,8 +480,8 @@
  *   <li>Identify which fields should be analysed by calling
  *   {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
  *   <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
- *   <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
- *   requests.
+ *   <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in
+ *   subsequent requests.
  * </ol>
  *
  * <p>The field classification is an expensive operation and should be used carefully, otherwise it
@@ -491,8 +490,47 @@
  * strong suspicious that it could. For example, if an activity has four or more fields and one of
  * them is a list, chances are that these are address fields (like address, city, state, and
  * zip code).
+ *
+ * <a name="CompatibilityMode"></a>
+ * <h3>Compatibility mode</h3>
+ *
+ * <p>Apps that use standard Android widgets support autofill out-of-the-box and need to do
+ * very little to improve their user experience (annotating autofillable views and providing
+ * autofill hints). However, some apps do their own rendering and the rendered content may
+ * contain semantic structure that needs to be surfaced to the autofill framework. The platform
+ * exposes APIs to achieve this, however it could take some time until these apps implement
+ * autofill support.
+ *
+ * <p>To enable autofill for such apps the platform provides a compatibility mode in which the
+ * platform would fall back to the accessibility APIs to generate the state reported to autofill
+ * services and fill data. This mode needs to be explicitly requested for a given package up
+ * to a specified max version code allowing clean migration path when the target app begins to
+ * support autofill natively. Note that enabling compatibility may degrade performance for the
+ * target package and should be used with caution. The platform supports whitelisting which packages
+ * can be targeted in compatibility mode to ensure this mode is used only when needed and as long
+ * as needed.
+ *
+ * <p>You can request compatibility mode for packages of interest in the meta-data resource
+ * associated with your service. Below is a sample service declaration:
+ *
+ * <pre> &lt;service android:name=".MyAutofillService"
+ *              android:permission="android.permission.BIND_AUTOFILL_SERVICE"&gt;
+ *     &lt;intent-filter&gt;
+ *         &lt;action android:name="android.service.autofill.AutofillService" /&gt;
+ *     &lt;/intent-filter&gt;
+ *     &lt;meta-data android:name="android.autofill" android:resource="@xml/autofillservice" /&gt;
+ * &lt;/service&gt;</pre>
+ *
+ * <P>In the XML file you can specify one or more packages for which to enable compatibility
+ * mode. Below is a sample meta-data declaration:
+ *
+ * <pre> &lt;autofill-service xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *     &lt;compatibility-package android:name="foo.bar.baz" android:maxLongVersionCode="1000000000"/&gt;
+ * &lt;/autofill-service&gt;</pre>
+ *
+ * <p>When using compatibility mode, the {@link SaveInfo.Builder#setFlags(int) SaveInfo flags}
+ * automatically include {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE}.
  */
-// TODO(b/70407264): add code snippets above???
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
 
@@ -518,20 +556,12 @@
      */
     public static final String SERVICE_META_DATA = "android.autofill";
 
-    // Handler messages.
-    private static final int MSG_CONNECT = 1;
-    private static final int MSG_DISCONNECT = 2;
-    private static final int MSG_ON_FILL_REQUEST = 3;
-    private static final int MSG_ON_SAVE_REQUEST = 4;
-
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
         @Override
         public void onConnectedStateChanged(boolean connected) {
-            if (connected) {
-                mHandlerCaller.obtainMessage(MSG_CONNECT).sendToTarget();
-            } else {
-                mHandlerCaller.obtainMessage(MSG_DISCONNECT).sendToTarget();
-            }
+            mHandler.sendMessage(obtainMessage(
+                    connected ? AutofillService::onConnected : AutofillService::onDisconnected,
+                    AutofillService.this));
         }
 
         @Override
@@ -542,56 +572,27 @@
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
-            mHandlerCaller.obtainMessageOOO(MSG_ON_FILL_REQUEST, request,
-                    CancellationSignal.fromTransport(transport), callback)
-                    .sendToTarget();
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onFillRequest,
+                    AutofillService.this, request, CancellationSignal.fromTransport(transport),
+                    new FillCallback(callback, request.getId())));
         }
 
         @Override
         public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
-            mHandlerCaller.obtainMessageOO(MSG_ON_SAVE_REQUEST, request,
-                    callback).sendToTarget();
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onSaveRequest,
+                    AutofillService.this, request, new SaveCallback(callback)));
         }
     };
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        switch (msg.what) {
-            case MSG_CONNECT: {
-                onConnected();
-                break;
-            } case MSG_ON_FILL_REQUEST: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final FillRequest request = (FillRequest) args.arg1;
-                final CancellationSignal cancellation = (CancellationSignal) args.arg2;
-                final IFillCallback callback = (IFillCallback) args.arg3;
-                final FillCallback fillCallback = new FillCallback(callback, request.getId());
-                args.recycle();
-                onFillRequest(request, cancellation, fillCallback);
-                break;
-            } case MSG_ON_SAVE_REQUEST: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final SaveRequest request = (SaveRequest) args.arg1;
-                final ISaveCallback callback = (ISaveCallback) args.arg2;
-                final SaveCallback saveCallback = new SaveCallback(callback);
-                args.recycle();
-                onSaveRequest(request, saveCallback);
-                break;
-            } case MSG_DISCONNECT: {
-                onDisconnected();
-                break;
-            } default: {
-                Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
-            }
-        }
-    };
-
-    private HandlerCaller mHandlerCaller;
+    private Handler mHandler;
 
     @CallSuper
     @Override
     public void onCreate() {
         super.onCreate();
-        mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
     }
 
     @Override
diff --git a/android/service/autofill/AutofillServiceHelper.java b/android/service/autofill/AutofillServiceHelper.java
new file mode 100644
index 0000000..13fedba
--- /dev/null
+++ b/android/service/autofill/AutofillServiceHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.Nullable;
+import android.view.autofill.AutofillId;
+
+import com.android.internal.util.Preconditions;
+
+/** @hide */
+final class AutofillServiceHelper {
+
+    static AutofillId[] assertValid(@Nullable AutofillId[] ids) {
+        Preconditions.checkArgument(ids != null && ids.length > 0, "must have at least one id");
+        // Can't use Preconditions.checkArrayElementsNotNull() because it throws NPE instead of IAE
+        for (int i = 0; i < ids.length; ++i) {
+            if (ids[i] == null) {
+                throw new IllegalArgumentException("ids[" + i + "] must not be null");
+            }
+        }
+        return ids;
+    }
+
+    private AutofillServiceHelper() {
+        throw new UnsupportedOperationException("contains static members only");
+    }
+}
diff --git a/android/service/autofill/AutofillServiceInfo.java b/android/service/autofill/AutofillServiceInfo.java
index 5c7388f..b7ec281 100644
--- a/android/service/autofill/AutofillServiceInfo.java
+++ b/android/service/autofill/AutofillServiceInfo.java
@@ -20,22 +20,30 @@
 import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.metrics.LogMaker;
 import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 
 /**
  * {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -45,6 +53,9 @@
 public final class AutofillServiceInfo {
     private static final String TAG = "AutofillServiceInfo";
 
+    private static final String TAG_AUTOFILL_SERVICE = "autofill-service";
+    private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package";
+
     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
         try {
@@ -66,76 +77,141 @@
     @Nullable
     private final String mSettingsActivity;
 
-    public AutofillServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
-            throws PackageManager.NameNotFoundException {
-        this(pm, getServiceInfoOrThrow(comp, userHandle));
-    }
-
-    public AutofillServiceInfo(PackageManager pm, ServiceInfo si) {
-        mServiceInfo = si;
-        final TypedArray metaDataArray = getMetaDataArray(pm, si);
-        if (metaDataArray != null) {
-            mSettingsActivity = metaDataArray
-                    .getString(R.styleable.AutofillService_settingsActivity);
-            metaDataArray.recycle();
-        } else {
-            mSettingsActivity = null;
-        }
-    }
-
-    /**
-     * Gets the meta-data as a {@link TypedArray}, or {@code null} if not provided,
-     * or throws if invalid.
-     */
     @Nullable
-    private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
+    private final ArrayMap<String, Long> mCompatibilityPackages;
+
+    public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
+            throws PackageManager.NameNotFoundException {
+        this(context, getServiceInfoOrThrow(comp, userHandle));
+    }
+
+    public AutofillServiceInfo(Context context, ServiceInfo si) {
         // Check for permissions.
         if (!Manifest.permission.BIND_AUTOFILL_SERVICE.equals(si.permission)) {
-            Log.w(TAG, "AutofillService from '" + si.packageName + "' does not require permission "
-                    + Manifest.permission.BIND_AUTOFILL_SERVICE);
-            throw new SecurityException("Service does not require permission "
-                    + Manifest.permission.BIND_AUTOFILL_SERVICE);
+            if (Manifest.permission.BIND_AUTOFILL.equals(si.permission)) {
+                // Let it go for now...
+                Log.w(TAG, "AutofillService from '" + si.packageName + "' uses unsupported "
+                        + "permission " + Manifest.permission.BIND_AUTOFILL + ". It works for "
+                        + "now, but might not be supported on future releases");
+                new MetricsLogger().write(new LogMaker(MetricsEvent.AUTOFILL_INVALID_PERMISSION)
+                        .setPackageName(si.packageName));
+            } else {
+                Log.w(TAG, "AutofillService from '" + si.packageName
+                        + "' does not require permission "
+                        + Manifest.permission.BIND_AUTOFILL_SERVICE);
+                throw new SecurityException("Service does not require permission "
+                        + Manifest.permission.BIND_AUTOFILL_SERVICE);
+            }
         }
 
+        mServiceInfo = si;
+
         // Get the AutoFill metadata, if declared.
-        XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA);
+        final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
+                AutofillService.SERVICE_META_DATA);
         if (parser == null) {
-            return null;
+            mSettingsActivity = null;
+            mCompatibilityPackages = null;
+            return;
         }
 
-        // Parse service info and get the <autofill-service> tag as an AttributeSet.
-        AttributeSet attrs;
+        String settingsActivity = null;
+        ArrayMap<String, Long> compatibilityPackages = null;
+
         try {
-            // Move the XML parser to the first tag.
-            try {
-                int type;
-                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                        && type != XmlPullParser.START_TAG) {
+            final Resources resources = context.getPackageManager().getResourcesForApplication(
+                    si.applicationInfo);
+
+            int type = 0;
+            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+                type = parser.next();
+            }
+
+            if (TAG_AUTOFILL_SERVICE.equals(parser.getName())) {
+                final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+                TypedArray afsAttributes = null;
+                try {
+                    afsAttributes = resources.obtainAttributes(allAttributes,
+                            com.android.internal.R.styleable.AutofillService);
+                    settingsActivity = afsAttributes.getString(
+                            R.styleable.AutofillService_settingsActivity);
+                } finally {
+                    if (afsAttributes != null) {
+                        afsAttributes.recycle();
+                    }
                 }
-            } catch (XmlPullParserException | IOException e) {
-                Log.e(TAG, "Error parsing auto fill service meta-data", e);
-                return null;
-            }
-
-            if (!"autofill-service".equals(parser.getName())) {
+                compatibilityPackages = parseCompatibilityPackages(parser, resources);
+            } else {
                 Log.e(TAG, "Meta-data does not start with autofill-service tag");
-                return null;
             }
-            attrs = Xml.asAttributeSet(parser);
-
-            // Get resources required to read the AttributeSet.
-            Resources res;
-            try {
-                res = pm.getResourcesForApplication(si.applicationInfo);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "Error getting application resources", e);
-                return null;
-            }
-
-            return res.obtainAttributes(attrs, R.styleable.AutofillService);
-        } finally {
-            parser.close();
+        } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+            Log.e(TAG, "Error parsing auto fill service meta-data", e);
         }
+
+        mSettingsActivity = settingsActivity;
+        mCompatibilityPackages = compatibilityPackages;
+    }
+
+    private ArrayMap<String, Long> parseCompatibilityPackages(XmlPullParser parser,
+            Resources resources) throws IOException, XmlPullParserException {
+        ArrayMap<String, Long> compatibilityPackages = null;
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (TAG_COMPATIBILITY_PACKAGE.equals(parser.getName())) {
+                TypedArray cpAttributes = null;
+                try {
+                    final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+
+                    cpAttributes = resources.obtainAttributes(allAttributes,
+                           R.styleable.AutofillService_CompatibilityPackage);
+
+                    final String name = cpAttributes.getString(
+                            R.styleable.AutofillService_CompatibilityPackage_name);
+                    if (TextUtils.isEmpty(name)) {
+                        Log.e(TAG, "Invalid compatibility package:" + name);
+                        break;
+                    }
+
+                    final String maxVersionCodeStr = cpAttributes.getString(
+                            R.styleable.AutofillService_CompatibilityPackage_maxLongVersionCode);
+                    final Long maxVersionCode;
+                    if (maxVersionCodeStr != null) {
+                        try {
+                            maxVersionCode = Long.parseLong(maxVersionCodeStr);
+                        } catch (NumberFormatException e) {
+                            Log.e(TAG, "Invalid compatibility max version code:"
+                                    + maxVersionCodeStr);
+                            break;
+                        }
+                        if (maxVersionCode < 0) {
+                            Log.e(TAG, "Invalid compatibility max version code:"
+                                    + maxVersionCode);
+                            break;
+                        }
+                    } else {
+                        maxVersionCode = Long.MAX_VALUE;
+                    }
+                    if (compatibilityPackages == null) {
+                        compatibilityPackages = new ArrayMap<>();
+                    }
+                    compatibilityPackages.put(name, maxVersionCode);
+                } finally {
+                    XmlUtils.skipCurrentTag(parser);
+                    if (cpAttributes != null) {
+                        cpAttributes.recycle();
+                    }
+                }
+            }
+        }
+
+        return compatibilityPackages;
     }
 
     public ServiceInfo getServiceInfo() {
@@ -147,8 +223,27 @@
         return mSettingsActivity;
     }
 
+    public ArrayMap<String, Long> getCompatibilityPackages() {
+        return mCompatibilityPackages;
+    }
+
     @Override
     public String toString() {
-        return mServiceInfo == null ? "null" : mServiceInfo.toString();
+        final StringBuilder builder = new StringBuilder();
+        builder.append(getClass().getSimpleName());
+        builder.append("[").append(mServiceInfo);
+        builder.append(", settings:").append(mSettingsActivity);
+        builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null
+                && !mCompatibilityPackages.isEmpty()).append("]");
+        return builder.toString();
+    }
+
+    /**
+     * Dumps it!
+     */
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName());
+        pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity);
+        pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages);
     }
 }
diff --git a/android/service/autofill/CustomDescription.java b/android/service/autofill/CustomDescription.java
index b8e8b19..fb468a8 100644
--- a/android/service/autofill/CustomDescription.java
+++ b/android/service/autofill/CustomDescription.java
@@ -173,7 +173,10 @@
         }
 
         /**
-         * Updates the {@link RemoteViews presentation template} when a condition is satisfied.
+         * Updates the {@link RemoteViews presentation template} when a condition is satisfied by
+         * applying a series of remote view operations. This allows dynamic customization of the
+         * portion of the save UI that is controlled by the autofill service. Such dynamic
+         * customization is based on the content of target views.
          *
          * <p>The updates are applied in the sequence they are added, after the
          * {@link #addChild(int, Transformation) transformations} are applied to the children
diff --git a/android/service/autofill/Dataset.java b/android/service/autofill/Dataset.java
index 266bcda..ccec483 100644
--- a/android/service/autofill/Dataset.java
+++ b/android/service/autofill/Dataset.java
@@ -29,7 +29,6 @@
 
 import com.android.internal.util.Preconditions;
 
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.regex.Pattern;
 
@@ -99,7 +98,7 @@
     private final ArrayList<AutofillId> mFieldIds;
     private final ArrayList<AutofillValue> mFieldValues;
     private final ArrayList<RemoteViews> mFieldPresentations;
-    private final ArrayList<Pattern> mFieldFilters;
+    private final ArrayList<DatasetFieldFilter> mFieldFilters;
     private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
     @Nullable String mId;
@@ -132,7 +131,7 @@
 
     /** @hide */
     @Nullable
-    public Pattern getFilter(int index) {
+    public DatasetFieldFilter getFilter(int index) {
         return mFieldFilters.get(index);
     }
 
@@ -150,24 +149,33 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("Dataset[id=");
+        final StringBuilder builder = new StringBuilder("Dataset[");
         if (mId == null) {
-            builder.append("null");
+            builder.append("noId");
         } else {
             // Cannot disclose id because it could contain PII.
-            builder.append(mId.length()).append("_chars");
+            builder.append("id=").append(mId.length()).append("_chars");
         }
+        if (mFieldIds != null) {
+            builder.append(", fieldIds=").append(mFieldIds);
+        }
+        if (mFieldValues != null) {
+            builder.append(", fieldValues=").append(mFieldValues);
+        }
+        if (mFieldPresentations != null) {
+            builder.append(", fieldPresentations=").append(mFieldPresentations.size());
 
-        return builder
-                .append(", fieldIds=").append(mFieldIds)
-                .append(", fieldValues=").append(mFieldValues)
-                .append(", fieldPresentations=")
-                .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
-                .append(", fieldFilters=")
-                .append(mFieldFilters == null ? 0 : mFieldFilters.size())
-                .append(", hasPresentation=").append(mPresentation != null)
-                .append(", hasAuthentication=").append(mAuthentication != null)
-                .append(']').toString();
+        }
+        if (mFieldFilters != null) {
+            builder.append(", fieldFilters=").append(mFieldFilters.size());
+        }
+        if (mPresentation != null) {
+            builder.append(", hasPresentation");
+        }
+        if (mAuthentication != null) {
+            builder.append(", hasAuthentication");
+        }
+        return builder.append(']').toString();
     }
 
     /**
@@ -189,7 +197,7 @@
         private ArrayList<AutofillId> mFieldIds;
         private ArrayList<AutofillValue> mFieldValues;
         private ArrayList<RemoteViews> mFieldPresentations;
-        private ArrayList<Pattern> mFieldFilters;
+        private ArrayList<DatasetFieldFilter> mFieldFilters;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
@@ -363,19 +371,21 @@
          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
          *        the dataset needs authentication and you have no access to the value.
-         * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+         *        when {@code null}, it disables filtering on that dataset (this is the recommended
+         *        approach when {@code value} is not {@code null} and field contains sensitive data
+         *        such as passwords).
          *
          * @return this builder.
          * @throws IllegalStateException if the builder was constructed without a
          *         {@link RemoteViews presentation}.
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
-                @NonNull Pattern filter) {
+                @Nullable Pattern filter) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(filter, "filter cannot be null");
             Preconditions.checkState(mPresentation != null,
                     "Dataset presentation not set on constructor");
-            setLifeTheUniverseAndEverything(id, value, null, filter);
+            setLifeTheUniverseAndEverything(id, value, null, new DatasetFieldFilter(filter));
             return this;
         }
 
@@ -398,23 +408,26 @@
          * @param value the value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
          *        the dataset needs authentication and you have no access to the value.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+         *        when {@code null}, it disables filtering on that dataset (this is the recommended
+         *        approach when {@code value} is not {@code null} and field contains sensitive data
+         *        such as passwords).
          * @param presentation the presentation used to visualize this field.
-         * @param filter regex used to determine if the dataset should be shown in the autofill UI.
          *
          * @return this builder.
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
-                @NonNull Pattern filter, @NonNull RemoteViews presentation) {
+                @Nullable Pattern filter, @NonNull RemoteViews presentation) {
             throwIfDestroyed();
-            Preconditions.checkNotNull(filter, "filter cannot be null");
             Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, filter);
+            setLifeTheUniverseAndEverything(id, value, presentation,
+                    new DatasetFieldFilter(filter));
             return this;
         }
 
         private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
-                @Nullable Pattern filter) {
+                @Nullable DatasetFieldFilter filter) {
             Preconditions.checkNotNull(id, "id cannot be null");
             if (mFieldIds != null) {
                 final int existingIdx = mFieldIds.indexOf(id);
@@ -477,8 +490,8 @@
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeTypedList(mFieldIds, flags);
         parcel.writeTypedList(mFieldValues, flags);
-        parcel.writeParcelableList(mFieldPresentations, flags);
-        parcel.writeSerializable(mFieldFilters);
+        parcel.writeTypedList(mFieldPresentations, flags);
+        parcel.writeTypedList(mFieldFilters, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeString(mId);
     }
@@ -493,22 +506,19 @@
             final Builder builder = (presentation == null)
                     ? new Builder()
                     : new Builder(presentation);
-            final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR);
+            final ArrayList<AutofillId> ids =
+                    parcel.createTypedArrayList(AutofillId.CREATOR);
             final ArrayList<AutofillValue> values =
                     parcel.createTypedArrayList(AutofillValue.CREATOR);
-            final ArrayList<RemoteViews> presentations = new ArrayList<>();
-            parcel.readParcelableList(presentations, null);
-            @SuppressWarnings("unchecked")
-            final ArrayList<Serializable> filters =
-                    (ArrayList<Serializable>) parcel.readSerializable();
-            final int idCount = (ids != null) ? ids.size() : 0;
-            final int valueCount = (values != null) ? values.size() : 0;
-            for (int i = 0; i < idCount; i++) {
+            final ArrayList<RemoteViews> presentations =
+                    parcel.createTypedArrayList(RemoteViews.CREATOR);
+            final ArrayList<DatasetFieldFilter> filters =
+                    parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+            for (int i = 0; i < ids.size(); i++) {
                 final AutofillId id = ids.get(i);
-                final AutofillValue value = (valueCount > i) ? values.get(i) : null;
-                final RemoteViews fieldPresentation = presentations.isEmpty() ? null
-                        : presentations.get(i);
-                final Pattern filter = (Pattern) filters.get(i);
+                final AutofillValue value = values.get(i);
+                final RemoteViews fieldPresentation = presentations.get(i);
+                final DatasetFieldFilter filter = filters.get(i);
                 builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
             }
             builder.setAuthentication(parcel.readParcelable(null));
@@ -521,4 +531,55 @@
             return new Dataset[size];
         }
     };
+
+    /**
+     * Helper class used to indicate when the service explicitly set a {@link Pattern} filter for a
+     * dataset field&dash; we cannot use a {@link Pattern} directly because then we wouldn't be
+     * able to differentiate whether the service explicitly passed a {@code null} filter to disable
+     * filter, or when it called the methods that does not take a filter {@link Pattern}.
+     *
+     * @hide
+     */
+    public static final class DatasetFieldFilter implements Parcelable {
+
+        @Nullable
+        public final Pattern pattern;
+
+        private DatasetFieldFilter(@Nullable Pattern pattern) {
+            this.pattern = pattern;
+        }
+
+        @Override
+        public String toString() {
+            if (!sDebug) return super.toString();
+
+            // Cannot log pattern because it could contain PII
+            return pattern == null ? "null" : pattern.pattern().length() + "_chars";
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeSerializable(pattern);
+        }
+
+        @SuppressWarnings("hiding")
+        public static final Creator<DatasetFieldFilter> CREATOR =
+                new Creator<DatasetFieldFilter>() {
+
+            @Override
+            public DatasetFieldFilter createFromParcel(Parcel parcel) {
+                return new DatasetFieldFilter((Pattern) parcel.readSerializable());
+            }
+
+            @Override
+            public DatasetFieldFilter[] newArray(int size) {
+                return new DatasetFieldFilter[size];
+            }
+        };
+    }
 }
diff --git a/android/service/autofill/DateTransformation.java b/android/service/autofill/DateTransformation.java
new file mode 100644
index 0000000..ec24a09
--- /dev/null
+++ b/android/service/autofill/DateTransformation.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.icu.text.DateFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Date;
+
+/**
+ * Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of a field
+ * that is expected to have a {@link AutofillValue#forDate(long) date value}.
+ *
+ * <p>For example, a transformation to display a credit card expiration date as month/year would be:
+ *
+ * <pre class="prettyprint">
+ * new DateTransformation(ccExpDate, new java.text.SimpleDateFormat("MM/yyyy")
+ * </pre>
+ */
+public final class DateTransformation extends InternalTransformation implements
+        Transformation, Parcelable {
+    private static final String TAG = "DateTransformation";
+
+    private final AutofillId mFieldId;
+    private final DateFormat mDateFormat;
+
+    /**
+     * Creates a new transformation.
+     *
+     * @param id id of the screen field.
+     * @param dateFormat object used to transform the date value of the field to a String.
+     */
+    public DateTransformation(@NonNull AutofillId id, @NonNull DateFormat dateFormat) {
+        mFieldId = Preconditions.checkNotNull(id);
+        mDateFormat = Preconditions.checkNotNull(dateFormat);
+    }
+
+    /** @hide */
+    @Override
+    @TestApi
+    public void apply(@NonNull ValueFinder finder, @NonNull RemoteViews parentTemplate,
+            int childViewId) throws Exception {
+        final AutofillValue value = finder.findRawValueByAutofillId(mFieldId);
+        if (value == null) {
+            Log.w(TAG, "No value for id " + mFieldId);
+            return;
+        }
+        if (!value.isDate()) {
+            Log.w(TAG, "Value for " + mFieldId + " is not date: " + value);
+            return;
+        }
+
+        try {
+            final Date date = new Date(value.getDateValue());
+            final String transformed = mDateFormat.format(date);
+            if (sDebug) Log.d(TAG, "Transformed " + date + " to " + transformed);
+
+            parentTemplate.setCharSequence(childViewId, "setText", transformed);
+        } catch (Exception e) {
+            Log.w(TAG, "Could not apply " + mDateFormat + " to " + value + ": " + e);
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "DateTransformation: [id=" + mFieldId + ", format=" + mDateFormat + "]";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mFieldId, flags);
+        parcel.writeSerializable(mDateFormat);
+    }
+
+    public static final Parcelable.Creator<DateTransformation> CREATOR =
+            new Parcelable.Creator<DateTransformation>() {
+        @Override
+        public DateTransformation createFromParcel(Parcel parcel) {
+            return new DateTransformation(parcel.readParcelable(null),
+                    (DateFormat) parcel.readSerializable());
+        }
+
+        @Override
+        public DateTransformation[] newArray(int size) {
+            return new DateTransformation[size];
+        }
+    };
+}
diff --git a/android/service/autofill/DateValueSanitizer.java b/android/service/autofill/DateValueSanitizer.java
new file mode 100644
index 0000000..4f797f4
--- /dev/null
+++ b/android/service/autofill/DateValueSanitizer.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.icu.text.DateFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Date;
+
+/**
+ * Sanitizes a date {@link AutofillValue} using a {@link DateFormat}.
+ *
+ * <p>For example, to sanitize a credit card expiration date to just its month and year:
+ *
+ * <pre class="prettyprint">
+ * new DateValueSanitizer(new java.text.SimpleDateFormat("MM/yyyy");
+ * </pre>
+ */
+public final class DateValueSanitizer extends InternalSanitizer implements Sanitizer, Parcelable {
+
+    private static final String TAG = "DateValueSanitizer";
+
+    private final DateFormat mDateFormat;
+
+    /**
+     * Default constructor.
+     *
+     * @param dateFormat date format applied to the actual date value of an input field.
+      */
+    public DateValueSanitizer(@NonNull DateFormat dateFormat) {
+        mDateFormat = Preconditions.checkNotNull(dateFormat);
+    }
+
+    /** @hide */
+    @Override
+    @TestApi
+    @Nullable
+    public AutofillValue sanitize(@NonNull AutofillValue value) {
+        if (value == null) {
+            Log.w(TAG, "sanitize() called with null value");
+            return null;
+        }
+        if (!value.isDate()) {
+            if (sDebug) Log.d(TAG, value + " is not a date");
+            return null;
+        }
+
+        try {
+            final Date date = new Date(value.getDateValue());
+
+            // First convert it to string
+            final String converted = mDateFormat.format(date);
+            if (sDebug) Log.d(TAG, "Transformed " + date + " to " + converted);
+            // Then parse it back to date
+            final Date sanitized = mDateFormat.parse(converted);
+            if (sDebug) Log.d(TAG, "Sanitized to " + sanitized);
+            return AutofillValue.forDate(sanitized.getTime());
+        } catch (Exception e) {
+            Log.w(TAG, "Could not apply " + mDateFormat + " to " + value + ": " + e);
+            return null;
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "DateValueSanitizer: [dateFormat=" + mDateFormat + "]";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeSerializable(mDateFormat);
+    }
+
+    public static final Parcelable.Creator<DateValueSanitizer> CREATOR =
+            new Parcelable.Creator<DateValueSanitizer>() {
+        @Override
+        public DateValueSanitizer createFromParcel(Parcel parcel) {
+            return new DateValueSanitizer((DateFormat) parcel.readSerializable());
+        }
+
+        @Override
+        public DateValueSanitizer[] newArray(int size) {
+            return new DateValueSanitizer[size];
+        }
+    };
+}
diff --git a/android/service/autofill/FieldClassification.java b/android/service/autofill/FieldClassification.java
index cd1efd6..5bf56cb 100644
--- a/android/service/autofill/FieldClassification.java
+++ b/android/service/autofill/FieldClassification.java
@@ -108,21 +108,21 @@
      */
     public static final class Match {
 
-        private final String mRemoteId;
+        private final String mCategoryId;
         private final float mScore;
 
         /** @hide */
-        public Match(String remoteId, float score) {
-            mRemoteId = Preconditions.checkNotNull(remoteId);
+        public Match(String categoryId, float score) {
+            mCategoryId = Preconditions.checkNotNull(categoryId);
             mScore = score;
         }
 
         /**
-         * Gets the remote id of the {@link UserData} entry.
+         * Gets the category id of the {@link UserData} entry.
          */
         @NonNull
-        public String getRemoteId() {
-            return mRemoteId;
+        public String getCategoryId() {
+            return mCategoryId;
         }
 
         /**
@@ -149,13 +149,13 @@
         public String toString() {
             if (!sDebug) return super.toString();
 
-            final StringBuilder string = new StringBuilder("Match: remoteId=");
-            Helper.appendRedacted(string, mRemoteId);
+            final StringBuilder string = new StringBuilder("Match: categoryId=");
+            Helper.appendRedacted(string, mCategoryId);
             return string.append(", score=").append(mScore).toString();
         }
 
         private void writeToParcel(@NonNull Parcel parcel) {
-            parcel.writeString(mRemoteId);
+            parcel.writeString(mCategoryId);
             parcel.writeFloat(mScore);
         }
 
diff --git a/android/service/autofill/FillContext.java b/android/service/autofill/FillContext.java
index cda2f4a..535c00b 100644
--- a/android/service/autofill/FillContext.java
+++ b/android/service/autofill/FillContext.java
@@ -177,30 +177,6 @@
         return foundNodes;
     }
 
-    /**
-     * Finds the {@link ViewNode} that has the requested {@code id}, if any.
-     *
-     * @hide
-     */
-    @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) {
-        final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
-        final int numWindowNodes = mStructure.getWindowNodeCount();
-        for (int i = 0; i < numWindowNodes; i++) {
-            nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode());
-        }
-        while (!nodesToProcess.isEmpty()) {
-            final ViewNode node = nodesToProcess.removeFirst();
-            if (id.equals(node.getAutofillId())) {
-                return node;
-            }
-            for (int i = 0; i < node.getChildCount(); i++) {
-                nodesToProcess.addLast(node.getChildAt(i));
-            }
-        }
-
-        return null;
-    }
-
     public static final Parcelable.Creator<FillContext> CREATOR =
             new Parcelable.Creator<FillContext>() {
         @Override
diff --git a/android/service/autofill/FillEventHistory.java b/android/service/autofill/FillEventHistory.java
index df62446..6e5bacf 100644
--- a/android/service/autofill/FillEventHistory.java
+++ b/android/service/autofill/FillEventHistory.java
@@ -424,7 +424,7 @@
          * @return map map whose key is the id of the manually-entered field, and value is the
          * ids of the datasets that have that value but were not selected by the user.
          */
-        @Nullable public Map<AutofillId, Set<String>> getManuallyEnteredField() {
+        @NonNull public Map<AutofillId, Set<String>> getManuallyEnteredField() {
             if (mManuallyFilledFieldIds == null || mManuallyFilledDatasetIds == null) {
                 return Collections.emptyMap();
             }
diff --git a/android/service/autofill/FillResponse.java b/android/service/autofill/FillResponse.java
index 3a4b6bb..2bc4b8f 100644
--- a/android/service/autofill/FillResponse.java
+++ b/android/service/autofill/FillResponse.java
@@ -16,6 +16,7 @@
 
 package android.service.autofill;
 
+import static android.service.autofill.AutofillServiceHelper.assertValid;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 import static android.view.autofill.Helper.sDebug;
 
@@ -245,10 +246,15 @@
          * @param ids id of Views that when focused will display the authentication UI.
          *
          * @return This builder.
-
-         * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
-         * both {@code authentication} and {@code presentation} are {@code null}, or if
-         * both {@code authentication} and {@code presentation} are non-{@code null}
+         *
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ul>
+         *   <li>{@code ids} is {@code null}</li>
+         *   <li>{@code ids} is empty</li>
+         *   <li>{@code ids} contains a {@code null} element</li>
+         *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
+         *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
+         * </ul>
          *
          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
@@ -263,16 +269,13 @@
                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
             }
 
-            if (ids == null || ids.length == 0) {
-                throw new IllegalArgumentException("ids cannot be null or empry");
-            }
             if (authentication == null ^ presentation == null) {
                 throw new IllegalArgumentException("authentication and presentation"
                         + " must be both non-null or null");
             }
             mAuthentication = authentication;
             mPresentation = presentation;
-            mAuthenticationIds = ids;
+            mAuthenticationIds = assertValid(ids);
             return this;
         }
 
@@ -554,23 +557,40 @@
         if (!sDebug) return super.toString();
 
         // TODO: create a dump() method instead
-        return new StringBuilder(
-                "FillResponse : [mRequestId=" + mRequestId)
-                .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
-                .append(", saveInfo=").append(mSaveInfo)
-                .append(", clientState=").append(mClientState != null)
-                .append(", hasPresentation=").append(mPresentation != null)
-                .append(", hasHeader=").append(mHeader != null)
-                .append(", hasFooter=").append(mFooter != null)
-                .append(", hasAuthentication=").append(mAuthentication != null)
-                .append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds))
-                .append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
-                .append(", disableDuration=").append(mDisableDuration)
-                .append(", flags=").append(mFlags)
-                .append(", fieldClassificationIds=")
-                    .append(Arrays.toString(mFieldClassificationIds))
-                .append("]")
-                .toString();
+        final StringBuilder builder = new StringBuilder(
+                "FillResponse : [mRequestId=" + mRequestId);
+        if (mDatasets != null) {
+            builder.append(", datasets=").append(mDatasets.getList());
+        }
+        if (mSaveInfo != null) {
+            builder.append(", saveInfo=").append(mSaveInfo);
+        }
+        if (mClientState != null) {
+            builder.append(", hasClientState");
+        }
+        if (mPresentation != null) {
+            builder.append(", hasPresentation");
+        }
+        if (mHeader != null) {
+            builder.append(", hasHeader");
+        }
+        if (mFooter != null) {
+            builder.append(", hasFooter");
+        }
+        if (mAuthentication != null) {
+            builder.append(", hasAuthentication");
+        }
+        if (mAuthenticationIds != null) {
+            builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
+        }
+        builder.append(", disableDuration=").append(mDisableDuration);
+        if (mFlags != 0) {
+            builder.append(", flags=").append(mFlags);
+        }
+        if (mFieldClassificationIds != null) {
+            builder.append(Arrays.toString(mFieldClassificationIds));
+        }
+        return builder.append("]").toString();
     }
 
     /////////////////////////////////////
diff --git a/android/service/autofill/SaveInfo.java b/android/service/autofill/SaveInfo.java
index a5a6177..4943fc8 100644
--- a/android/service/autofill/SaveInfo.java
+++ b/android/service/autofill/SaveInfo.java
@@ -16,6 +16,7 @@
 
 package android.service.autofill;
 
+import static android.service.autofill.AutofillServiceHelper.assertValid;
 import static android.view.autofill.Helper.sDebug;
 
 import android.annotation.IntDef;
@@ -405,17 +406,6 @@
             mRequiredIds = null;
         }
 
-        private AutofillId[] assertValid(AutofillId[] ids) {
-            Preconditions.checkArgument(ids != null && ids.length > 0,
-                    "must have at least one id: " + Arrays.toString(ids));
-            for (int i = 0; i < ids.length; i++) {
-                final AutofillId id = ids[i];
-                Preconditions.checkArgument(id != null,
-                        "cannot have null id: " + Arrays.toString(ids));
-            }
-            return ids;
-        }
-
         /**
          * Sets flags changing the save behavior.
          *
@@ -699,22 +689,37 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        return new StringBuilder("SaveInfo: [type=")
+        final StringBuilder builder = new StringBuilder("SaveInfo: [type=")
                 .append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType))
                 .append(", requiredIds=").append(Arrays.toString(mRequiredIds))
-                .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
-                .append(", description=").append(mDescription)
-                .append(DebugUtils.flagsToString(SaveInfo.class, "NEGATIVE_BUTTON_STYLE_",
-                        mNegativeButtonStyle))
-                .append(", flags=").append(mFlags)
-                .append(", customDescription=").append(mCustomDescription)
-                .append(", validator=").append(mValidator)
-                .append(", sanitizerKeys=")
-                    .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
-                .append(", sanitizerValues=")
-                    .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
-                .append(", triggerId=").append(mTriggerId)
-                .append("]").toString();
+                .append(", style=").append(DebugUtils.flagsToString(SaveInfo.class,
+                        "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle));
+        if (mOptionalIds != null) {
+            builder.append(", optionalIds=").append(Arrays.toString(mOptionalIds));
+        }
+        if (mDescription != null) {
+            builder.append(", description=").append(mDescription);
+        }
+        if (mFlags != 0) {
+            builder.append(", flags=").append(mFlags);
+        }
+        if (mCustomDescription != null) {
+            builder.append(", customDescription=").append(mCustomDescription);
+        }
+        if (mValidator != null) {
+            builder.append(", validator=").append(mValidator);
+        }
+        if (mSanitizerKeys != null) {
+            builder.append(", sanitizerKeys=").append(mSanitizerKeys.length);
+        }
+        if (mSanitizerValues != null) {
+            builder.append(", sanitizerValues=").append(mSanitizerValues.length);
+        }
+        if (mTriggerId != null) {
+            builder.append(", triggerId=").append(mTriggerId);
+        }
+
+        return builder.append("]").toString();
     }
 
     /////////////////////////////////////
diff --git a/android/service/autofill/UserData.java b/android/service/autofill/UserData.java
index 9017848..55aecdd 100644
--- a/android/service/autofill/UserData.java
+++ b/android/service/autofill/UserData.java
@@ -15,6 +15,7 @@
  */
 package android.service.autofill;
 
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
@@ -30,6 +31,8 @@
 import android.os.Parcelable;
 import android.provider.Settings;
 import android.service.autofill.FieldClassification.Match;
+import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.Helper;
@@ -47,21 +50,24 @@
 
     private static final String TAG = "UserData";
 
-    private static final int DEFAULT_MAX_USER_DATA_SIZE = 10;
+    private static final int DEFAULT_MAX_USER_DATA_SIZE = 50;
+    private static final int DEFAULT_MAX_CATEGORY_COUNT = 10;
     private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
-    private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
+    private static final int DEFAULT_MIN_VALUE_LENGTH = 3;
     private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
 
+    private final String mId;
     private final String mAlgorithm;
     private final Bundle mAlgorithmArgs;
-    private final String[] mRemoteIds;
+    private final String[] mCategoryIds;
     private final String[] mValues;
 
     private UserData(Builder builder) {
+        mId = builder.mId;
         mAlgorithm = builder.mAlgorithm;
         mAlgorithmArgs = builder.mAlgorithmArgs;
-        mRemoteIds = new String[builder.mRemoteIds.size()];
-        builder.mRemoteIds.toArray(mRemoteIds);
+        mCategoryIds = new String[builder.mCategoryIds.size()];
+        builder.mCategoryIds.toArray(mCategoryIds);
         mValues = new String[builder.mValues.size()];
         builder.mValues.toArray(mValues);
     }
@@ -75,14 +81,21 @@
         return mAlgorithm;
     }
 
+    /**
+     * Gets the id.
+     */
+    public String getId() {
+        return mId;
+    }
+
     /** @hide */
     public Bundle getAlgorithmArgs() {
         return mAlgorithmArgs;
     }
 
     /** @hide */
-    public String[] getRemoteIds() {
-        return mRemoteIds;
+    public String[] getCategoryIds() {
+        return mCategoryIds;
     }
 
     /** @hide */
@@ -92,14 +105,15 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("id: "); pw.print(mId);
         pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
         pw.print(" Args: "); pw.println(mAlgorithmArgs);
 
-        // Cannot disclose remote ids or values because they could contain PII
-        pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
-        for (int i = 0; i < mRemoteIds.length; i++) {
+        // Cannot disclose field ids or values because they could contain PII
+        pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
+        for (int i = 0; i < mCategoryIds.length; i++) {
             pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
-            pw.println(Helper.getRedacted(mRemoteIds[i]));
+            pw.println(Helper.getRedacted(mCategoryIds[i]));
         }
         pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
         for (int i = 0; i < mValues.length; i++) {
@@ -113,6 +127,7 @@
         pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize());
         pw.print(prefix); pw.print("maxFieldClassificationIdsSize: ");
         pw.println(getMaxFieldClassificationIdsSize());
+        pw.print(prefix); pw.print("maxCategoryCount: "); pw.println(getMaxCategoryCount());
         pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
         pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
     }
@@ -121,32 +136,58 @@
      * A builder for {@link UserData} objects.
      */
     public static final class Builder {
-        private final ArrayList<String> mRemoteIds;
+        private final String mId;
+        private final ArrayList<String> mCategoryIds;
         private final ArrayList<String> mValues;
         private String mAlgorithm;
         private Bundle mAlgorithmArgs;
         private boolean mDestroyed;
 
+        // Non-persistent array used to limit the number of unique ids.
+        private final ArraySet<String> mUniqueCategoryIds;
+
         /**
          * Creates a new builder for the user data used for <a href="#FieldClassification">field
          * classification</a>.
          *
+         * <p>The user data must contain at least one pair of {@code value} -> {@code categoryId},
+         * and more pairs can be added through the {@link #add(String, String)} method. For example:
+         *
+         * <pre class="prettyprint">
+         * new UserData.Builder("v1", "Bart Simpson", "name")
+         *   .add("[email protected]", "email")
+         *   .add("[email protected]", "email")
+         *   .build();
+         * </pre>
+         *
+         * @param id id used to identify the whole {@link UserData} object. This id is also returned
+         * by {@link AutofillManager#getUserDataId()}, which can be used to check if the
+         * {@link UserData} is up-to-date without fetching the whole object (through
+         * {@link AutofillManager#getUserData()}).
+         *
+         * @param value value of the user data.
+         * @param categoryId string used to identify the category the value is associated with.
+         *
          * @throws IllegalArgumentException if any of the following occurs:
-         * <ol>
-         *   <li>{@code remoteId} is empty
-         *   <li>{@code value} is empty
-         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
-         *   <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
-         * </ol>
+         * <ul>
+         *   <li>{@code id} is empty</li>
+         *   <li>{@code categoryId} is empty</li>
+         *   <li>{@code value} is empty</li>
+         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+         *   <li>the length of {@code value} is higher than
+         *       {@link UserData#getMaxValueLength()}</li>
+         * </ul>
          */
-        public Builder(@NonNull String remoteId, @NonNull String value) {
-            checkValidRemoteId(remoteId);
+        public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) {
+            mId = checkNotEmpty("id", id);
+            checkNotEmpty("categoryId", categoryId);
             checkValidValue(value);
-            final int capacity = getMaxUserDataSize();
-            mRemoteIds = new ArrayList<>(capacity);
-            mValues = new ArrayList<>(capacity);
-            mRemoteIds.add(remoteId);
-            mValues.add(value);
+            final int maxUserDataSize = getMaxUserDataSize();
+            mCategoryIds = new ArrayList<>(maxUserDataSize);
+            mValues = new ArrayList<>(maxUserDataSize);
+            mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
+
+            addMapping(value, categoryId);
         }
 
         /**
@@ -166,6 +207,7 @@
          */
         public Builder setFieldClassificationAlgorithm(@Nullable String name,
                 @Nullable Bundle args) {
+            throwIfDestroyed();
             mAlgorithm = name;
             mAlgorithmArgs = args;
             return this;
@@ -174,40 +216,61 @@
         /**
          * Adds a new value for user data.
          *
-         * @param remoteId unique string used to identify the user data.
          * @param value value of the user data.
+         * @param categoryId string used to identify the category the value is associated with.
          *
-         * @throws IllegalStateException if {@link #build()} or
-         * {@link #add(String, String)} with the same {@code remoteId} has already
-         * been called, or if the number of values add (i.e., calls made to this method plus
-         * constructor) is more than {@link UserData#getMaxUserDataSize()}.
+         * @throws IllegalStateException if:
+         * <ul>
+         *   <li>{@link #build()} already called</li>
+         *   <li>the {@code value} has already been added</li>
+         *   <li>the number of unique {@code categoryId} values added so far is more than
+         *       {@link UserData#getMaxCategoryCount()}</li>
+         *   <li>the number of {@code values} added so far is is more than
+         *       {@link UserData#getMaxUserDataSize()}</li>
+         * </ul>
          *
-         * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
-         * length of {@code value} is lower than {@link UserData#getMinValueLength()}
-         * or higher than {@link UserData#getMaxValueLength()}.
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ul>
+         *   <li>{@code id} is empty</li>
+         *   <li>{@code categoryId} is empty</li>
+         *   <li>{@code value} is empty</li>
+         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+         *   <li>the length of {@code value} is higher than
+         *       {@link UserData#getMaxValueLength()}</li>
+         * </ul>
          */
-        public Builder add(@NonNull String remoteId, @NonNull String value) {
+        public Builder add(@NonNull String value, @NonNull String categoryId) {
             throwIfDestroyed();
-            checkValidRemoteId(remoteId);
+            checkNotEmpty("categoryId", categoryId);
             checkValidValue(value);
 
-            Preconditions.checkState(!mRemoteIds.contains(remoteId),
-                    // Don't include remoteId on message because it could contain PII
-                    "already has entry with same remoteId");
+            if (!mUniqueCategoryIds.contains(categoryId)) {
+                // New category - check size
+                Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
+                        "already added " + mUniqueCategoryIds.size() + " unique category ids");
+
+            }
+
             Preconditions.checkState(!mValues.contains(value),
-                    // Don't include remoteId on message because it could contain PII
+                    // Don't include value on message because it could contain PII
                     "already has entry with same value");
-            Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
-                    "already added " + mRemoteIds.size() + " elements");
-            mRemoteIds.add(remoteId);
-            mValues.add(value);
+            Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
+                    "already added " + mValues.size() + " elements");
+            addMapping(value, categoryId);
 
             return this;
         }
 
-        private void checkValidRemoteId(@Nullable String remoteId) {
-            Preconditions.checkNotNull(remoteId);
-            Preconditions.checkArgument(!remoteId.isEmpty(), "remoteId cannot be empty");
+        private void addMapping(@NonNull String value, @NonNull String categoryId) {
+            mCategoryIds.add(categoryId);
+            mValues.add(value);
+            mUniqueCategoryIds.add(categoryId);
+        }
+
+        private String checkNotEmpty(@NonNull String name, @Nullable String value) {
+            Preconditions.checkNotNull(value);
+            Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
+            return value;
         }
 
         private void checkValidValue(@Nullable String value) {
@@ -246,10 +309,11 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("UserData: [algorithm=").append(mAlgorithm);
-        // Cannot disclose remote ids or values because they could contain PII
-        builder.append(", remoteIds=");
-        Helper.appendRedacted(builder, mRemoteIds);
+        final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
+                .append(", algorithm=").append(mAlgorithm);
+        // Cannot disclose category ids or values because they could contain PII
+        builder.append(", categoryIds=");
+        Helper.appendRedacted(builder, mCategoryIds);
         builder.append(", values=");
         Helper.appendRedacted(builder, mValues);
         return builder.append("]").toString();
@@ -266,7 +330,8 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeStringArray(mRemoteIds);
+        parcel.writeString(mId);
+        parcel.writeStringArray(mCategoryIds);
         parcel.writeStringArray(mValues);
         parcel.writeString(mAlgorithm);
         parcel.writeBundle(mAlgorithmArgs);
@@ -279,12 +344,13 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final String[] remoteIds = parcel.readStringArray();
+            final String id = parcel.readString();
+            final String[] categoryIds = parcel.readStringArray();
             final String[] values = parcel.readStringArray();
-            final Builder builder = new Builder(remoteIds[0], values[0])
+            final Builder builder = new Builder(id, values[0], categoryIds[0])
                     .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
-            for (int i = 1; i < remoteIds.length; i++) {
-                builder.add(remoteIds[i], values[i]);
+            for (int i = 1; i < categoryIds.length; i++) {
+                builder.add(values[i], categoryIds[i]);
             }
             return builder.build();
         }
@@ -312,6 +378,14 @@
     }
 
     /**
+     * Gets the maximum number of unique category ids that can be passed to
+     * the builder's constructor and {@link Builder#add(String, String)}.
+     */
+    public static int getMaxCategoryCount() {
+        return getInt(AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, DEFAULT_MAX_CATEGORY_COUNT);
+    }
+
+    /**
      * Gets the minimum length of values passed to the builder's constructor or
      * or {@link Builder#add(String, String)}.
      */
diff --git a/android/service/autofill/ValueFinder.java b/android/service/autofill/ValueFinder.java
index 1705b7d..7f195d6 100644
--- a/android/service/autofill/ValueFinder.java
+++ b/android/service/autofill/ValueFinder.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
 
 /**
  * Helper object used to obtain the value of a field in the screen being autofilled.
@@ -29,7 +30,17 @@
 public interface ValueFinder {
 
     /**
+     * Gets the value of a field as String, or {@code null} when not found.
+     */
+    @Nullable
+    default String findByAutofillId(@NonNull AutofillId id) {
+        final AutofillValue value = findRawValueByAutofillId(id);
+        return (value == null || !value.isText()) ? null : value.getTextValue().toString();
+    }
+
+    /**
      * Gets the value of a field, or {@code null} when not found.
      */
-    @Nullable String findByAutofillId(@NonNull AutofillId id);
+    @Nullable
+    AutofillValue findRawValueByAutofillId(@NonNull AutofillId id);
 }
diff --git a/android/service/dreams/DreamService.java b/android/service/dreams/DreamService.java
index 99e2c62..2b114d5 100644
--- a/android/service/dreams/DreamService.java
+++ b/android/service/dreams/DreamService.java
@@ -665,6 +665,11 @@
     }
 
     private void updateDoze() {
+        if (mWindowToken == null) {
+            Slog.w(TAG, "Updating doze without a window token.");
+            return;
+        }
+
         if (mDozing) {
             try {
                 mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
diff --git a/android/service/euicc/EuiccProfileInfo.java b/android/service/euicc/EuiccProfileInfo.java
index 8e752d1..4bbee61 100644
--- a/android/service/euicc/EuiccProfileInfo.java
+++ b/android/service/euicc/EuiccProfileInfo.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.carrier.CarrierIdentifier;
@@ -26,15 +27,15 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
  * Information about an embedded profile (subscription) on an eUICC.
  *
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public final class EuiccProfileInfo implements Parcelable {
 
     /** Profile policy rules (bit mask) */
@@ -44,6 +45,7 @@
             POLICY_RULE_DO_NOT_DELETE,
             POLICY_RULE_DELETE_AFTER_DISABLING
     })
+    /** @hide */
     public @interface PolicyRule {}
     /** Once this profile is enabled, it cannot be disabled. */
     public static final int POLICY_RULE_DO_NOT_DISABLE = 1;
@@ -60,6 +62,7 @@
             PROFILE_CLASS_OPERATIONAL,
             PROFILE_CLASS_UNSET
     })
+    /** @hide */
     public @interface ProfileClass {}
     /** Testing profiles */
     public static final int PROFILE_CLASS_TESTING = 0;
@@ -80,6 +83,7 @@
             PROFILE_STATE_ENABLED,
             PROFILE_STATE_UNSET
     })
+    /** @hide */
     public @interface ProfileState {}
     /** Disabled profiles */
     public static final int PROFILE_STATE_DISABLED = 0;
@@ -92,34 +96,34 @@
     public static final int PROFILE_STATE_UNSET = -1;
 
     /** The iccid of the subscription. */
-    public final String iccid;
+    private final String mIccid;
 
     /** An optional nickname for the subscription. */
-    public final @Nullable String nickname;
+    private final @Nullable String mNickname;
 
     /** The service provider name for the subscription. */
-    public final String serviceProviderName;
+    private final String mServiceProviderName;
 
     /** The profile name for the subscription. */
-    public final String profileName;
+    private final String mProfileName;
 
     /** Profile class for the subscription. */
-    @ProfileClass public final int profileClass;
+    @ProfileClass private final int mProfileClass;
 
     /** The profile state of the subscription. */
-    @ProfileState public final int state;
+    @ProfileState private final int mState;
 
     /** The operator Id of the subscription. */
-    public final CarrierIdentifier carrierIdentifier;
+    private final CarrierIdentifier mCarrierIdentifier;
 
     /** The policy rules of the subscription. */
-    @PolicyRule public final int policyRules;
+    @PolicyRule private final int mPolicyRules;
 
     /**
      * Optional access rules defining which apps can manage this subscription. If unset, only the
      * platform can manage it.
      */
-    public final @Nullable UiccAccessRule[] accessRules;
+    private final @Nullable UiccAccessRule[] mAccessRules;
 
     public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() {
         @Override
@@ -144,51 +148,51 @@
         if (!TextUtils.isDigitsOnly(iccid)) {
             throw new IllegalArgumentException("iccid contains invalid characters: " + iccid);
         }
-        this.iccid = iccid;
-        this.accessRules = accessRules;
-        this.nickname = nickname;
+        this.mIccid = iccid;
+        this.mAccessRules = accessRules;
+        this.mNickname = nickname;
 
-        this.serviceProviderName = null;
-        this.profileName = null;
-        this.profileClass = PROFILE_CLASS_UNSET;
-        this.state = PROFILE_CLASS_UNSET;
-        this.carrierIdentifier = null;
-        this.policyRules = 0;
+        this.mServiceProviderName = null;
+        this.mProfileName = null;
+        this.mProfileClass = PROFILE_CLASS_UNSET;
+        this.mState = PROFILE_STATE_UNSET;
+        this.mCarrierIdentifier = null;
+        this.mPolicyRules = 0;
     }
 
     private EuiccProfileInfo(Parcel in) {
-        iccid = in.readString();
-        nickname = in.readString();
-        serviceProviderName = in.readString();
-        profileName = in.readString();
-        profileClass = in.readInt();
-        state = in.readInt();
+        mIccid = in.readString();
+        mNickname = in.readString();
+        mServiceProviderName = in.readString();
+        mProfileName = in.readString();
+        mProfileClass = in.readInt();
+        mState = in.readInt();
         byte exist = in.readByte();
         if (exist == (byte) 1) {
-            carrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
+            mCarrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
         } else {
-            carrierIdentifier = null;
+            mCarrierIdentifier = null;
         }
-        policyRules = in.readInt();
-        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
+        mPolicyRules = in.readInt();
+        mAccessRules = in.createTypedArray(UiccAccessRule.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(iccid);
-        dest.writeString(nickname);
-        dest.writeString(serviceProviderName);
-        dest.writeString(profileName);
-        dest.writeInt(profileClass);
-        dest.writeInt(state);
-        if (carrierIdentifier != null) {
+        dest.writeString(mIccid);
+        dest.writeString(mNickname);
+        dest.writeString(mServiceProviderName);
+        dest.writeString(mProfileName);
+        dest.writeInt(mProfileClass);
+        dest.writeInt(mState);
+        if (mCarrierIdentifier != null) {
             dest.writeByte((byte) 1);
-            carrierIdentifier.writeToParcel(dest, flags);
+            mCarrierIdentifier.writeToParcel(dest, flags);
         } else {
             dest.writeByte((byte) 0);
         }
-        dest.writeInt(policyRules);
-        dest.writeTypedArray(accessRules, flags);
+        dest.writeInt(mPolicyRules);
+        dest.writeTypedArray(mAccessRules, flags);
     }
 
     @Override
@@ -198,45 +202,50 @@
 
     /** The builder to build a new {@link EuiccProfileInfo} instance. */
     public static final class Builder {
-        public String iccid;
-        public UiccAccessRule[] accessRules;
-        public String nickname;
-        public String serviceProviderName;
-        public String profileName;
-        @ProfileClass public int profileClass;
-        @ProfileState public int state;
-        public CarrierIdentifier carrierIdentifier;
-        @PolicyRule public int policyRules;
+        private String mIccid;
+        private List<UiccAccessRule> mAccessRules;
+        private String mNickname;
+        private String mServiceProviderName;
+        private String mProfileName;
+        @ProfileClass private int mProfileClass;
+        @ProfileState private int mState;
+        private CarrierIdentifier mCarrierIdentifier;
+        @PolicyRule private int mPolicyRules;
 
-        public Builder() {}
+        public Builder(String value) {
+            if (!TextUtils.isDigitsOnly(value)) {
+                throw new IllegalArgumentException("iccid contains invalid characters: " + value);
+            }
+            mIccid = value;
+        }
 
         public Builder(EuiccProfileInfo baseProfile) {
-            iccid = baseProfile.iccid;
-            nickname = baseProfile.nickname;
-            serviceProviderName = baseProfile.serviceProviderName;
-            profileName = baseProfile.profileName;
-            profileClass = baseProfile.profileClass;
-            state = baseProfile.state;
-            carrierIdentifier = baseProfile.carrierIdentifier;
-            policyRules = baseProfile.policyRules;
-            accessRules = baseProfile.accessRules;
+            mIccid = baseProfile.mIccid;
+            mNickname = baseProfile.mNickname;
+            mServiceProviderName = baseProfile.mServiceProviderName;
+            mProfileName = baseProfile.mProfileName;
+            mProfileClass = baseProfile.mProfileClass;
+            mState = baseProfile.mState;
+            mCarrierIdentifier = baseProfile.mCarrierIdentifier;
+            mPolicyRules = baseProfile.mPolicyRules;
+            mAccessRules = Arrays.asList(baseProfile.mAccessRules);
         }
 
         /** Builds the profile instance. */
         public EuiccProfileInfo build() {
-            if (iccid == null) {
+            if (mIccid == null) {
                 throw new IllegalStateException("ICCID must be set for a profile.");
             }
             return new EuiccProfileInfo(
-                    iccid,
-                    nickname,
-                    serviceProviderName,
-                    profileName,
-                    profileClass,
-                    state,
-                    carrierIdentifier,
-                    policyRules,
-                    accessRules);
+                    mIccid,
+                    mNickname,
+                    mServiceProviderName,
+                    mProfileName,
+                    mProfileClass,
+                    mState,
+                    mCarrierIdentifier,
+                    mPolicyRules,
+                    mAccessRules);
         }
 
         /** Sets the iccId of the subscription. */
@@ -244,55 +253,55 @@
             if (!TextUtils.isDigitsOnly(value)) {
                 throw new IllegalArgumentException("iccid contains invalid characters: " + value);
             }
-            iccid = value;
+            mIccid = value;
             return this;
         }
 
         /** Sets the nickname of the subscription. */
         public Builder setNickname(String value) {
-            nickname = value;
+            mNickname = value;
             return this;
         }
 
         /** Sets the service provider name of the subscription. */
         public Builder setServiceProviderName(String value) {
-            serviceProviderName = value;
+            mServiceProviderName = value;
             return this;
         }
 
         /** Sets the profile name of the subscription. */
         public Builder setProfileName(String value) {
-            profileName = value;
+            mProfileName = value;
             return this;
         }
 
         /** Sets the profile class of the subscription. */
         public Builder setProfileClass(@ProfileClass int value) {
-            profileClass = value;
+            mProfileClass = value;
             return this;
         }
 
         /** Sets the state of the subscription. */
         public Builder setState(@ProfileState int value) {
-            state = value;
+            mState = value;
             return this;
         }
 
         /** Sets the carrier identifier of the subscription. */
         public Builder setCarrierIdentifier(CarrierIdentifier value) {
-            carrierIdentifier = value;
+            mCarrierIdentifier = value;
             return this;
         }
 
         /** Sets the policy rules of the subscription. */
         public Builder setPolicyRules(@PolicyRule int value) {
-            policyRules = value;
+            mPolicyRules = value;
             return this;
         }
 
         /** Sets the access rules of the subscription. */
-        public Builder setUiccAccessRule(@Nullable UiccAccessRule[] value) {
-            accessRules = value;
+        public Builder setUiccAccessRule(@Nullable List<UiccAccessRule> value) {
+            mAccessRules = value;
             return this;
         }
     }
@@ -306,75 +315,81 @@
             @ProfileState int state,
             CarrierIdentifier carrierIdentifier,
             @PolicyRule int policyRules,
-            @Nullable UiccAccessRule[] accessRules) {
-        this.iccid = iccid;
-        this.nickname = nickname;
-        this.serviceProviderName = serviceProviderName;
-        this.profileName = profileName;
-        this.profileClass = profileClass;
-        this.state = state;
-        this.carrierIdentifier = carrierIdentifier;
-        this.policyRules = policyRules;
-        this.accessRules = accessRules;
+            @Nullable List<UiccAccessRule> accessRules) {
+        this.mIccid = iccid;
+        this.mNickname = nickname;
+        this.mServiceProviderName = serviceProviderName;
+        this.mProfileName = profileName;
+        this.mProfileClass = profileClass;
+        this.mState = state;
+        this.mCarrierIdentifier = carrierIdentifier;
+        this.mPolicyRules = policyRules;
+        if (accessRules != null && accessRules.size() > 0) {
+            this.mAccessRules = accessRules.toArray(new UiccAccessRule[accessRules.size()]);
+        } else {
+            this.mAccessRules = null;
+        }
     }
 
     /** Gets the ICCID string. */
     public String getIccid() {
-        return iccid;
+        return mIccid;
     }
 
     /** Gets the access rules. */
     @Nullable
-    public UiccAccessRule[] getUiccAccessRules() {
-        return accessRules;
+    public List<UiccAccessRule> getUiccAccessRules() {
+        if (mAccessRules == null) return null;
+        return Arrays.asList(mAccessRules);
     }
 
     /** Gets the nickname. */
+    @Nullable
     public String getNickname() {
-        return nickname;
+        return mNickname;
     }
 
     /** Gets the service provider name. */
     public String getServiceProviderName() {
-        return serviceProviderName;
+        return mServiceProviderName;
     }
 
     /** Gets the profile name. */
     public String getProfileName() {
-        return profileName;
+        return mProfileName;
     }
 
     /** Gets the profile class. */
     @ProfileClass
     public int getProfileClass() {
-        return profileClass;
+        return mProfileClass;
     }
 
     /** Gets the state of the subscription. */
     @ProfileState
     public int getState() {
-        return state;
+        return mState;
     }
 
     /** Gets the carrier identifier. */
     public CarrierIdentifier getCarrierIdentifier() {
-        return carrierIdentifier;
+        return mCarrierIdentifier;
     }
 
     /** Gets the policy rules. */
     @PolicyRule
     public int getPolicyRules() {
-        return policyRules;
+        return mPolicyRules;
     }
 
     /** Returns whether any policy rule exists. */
     public boolean hasPolicyRules() {
-        return policyRules != 0;
+        return mPolicyRules != 0;
     }
 
     /** Checks whether a certain policy rule exists. */
     public boolean hasPolicyRule(@PolicyRule int policy) {
-        return (policyRules & policy) != 0;
+        return (mPolicyRules & policy) != 0;
     }
 
     @Override
@@ -387,50 +402,50 @@
         }
 
         EuiccProfileInfo that = (EuiccProfileInfo) obj;
-        return Objects.equals(iccid, that.iccid)
-                && Objects.equals(nickname, that.nickname)
-                && Objects.equals(serviceProviderName, that.serviceProviderName)
-                && Objects.equals(profileName, that.profileName)
-                && profileClass == that.profileClass
-                && state == that.state
-                && Objects.equals(carrierIdentifier, that.carrierIdentifier)
-                && policyRules == that.policyRules
-                && Arrays.equals(accessRules, that.accessRules);
+        return Objects.equals(mIccid, that.mIccid)
+                && Objects.equals(mNickname, that.mNickname)
+                && Objects.equals(mServiceProviderName, that.mServiceProviderName)
+                && Objects.equals(mProfileName, that.mProfileName)
+                && mProfileClass == that.mProfileClass
+                && mState == that.mState
+                && Objects.equals(mCarrierIdentifier, that.mCarrierIdentifier)
+                && mPolicyRules == that.mPolicyRules
+                && Arrays.equals(mAccessRules, that.mAccessRules);
     }
 
     @Override
     public int hashCode() {
         int result = 1;
-        result = 31 * result + Objects.hashCode(iccid);
-        result = 31 * result + Objects.hashCode(nickname);
-        result = 31 * result + Objects.hashCode(serviceProviderName);
-        result = 31 * result + Objects.hashCode(profileName);
-        result = 31 * result + profileClass;
-        result = 31 * result + state;
-        result = 31 * result + Objects.hashCode(carrierIdentifier);
-        result = 31 * result + policyRules;
-        result = 31 * result + Arrays.hashCode(accessRules);
+        result = 31 * result + Objects.hashCode(mIccid);
+        result = 31 * result + Objects.hashCode(mNickname);
+        result = 31 * result + Objects.hashCode(mServiceProviderName);
+        result = 31 * result + Objects.hashCode(mProfileName);
+        result = 31 * result + mProfileClass;
+        result = 31 * result + mState;
+        result = 31 * result + Objects.hashCode(mCarrierIdentifier);
+        result = 31 * result + mPolicyRules;
+        result = 31 * result + Arrays.hashCode(mAccessRules);
         return result;
     }
 
     @Override
     public String toString() {
         return "EuiccProfileInfo (nickname="
-                + nickname
+                + mNickname
                 + ", serviceProviderName="
-                + serviceProviderName
+                + mServiceProviderName
                 + ", profileName="
-                + profileName
+                + mProfileName
                 + ", profileClass="
-                + profileClass
+                + mProfileClass
                 + ", state="
-                + state
+                + mState
                 + ", CarrierIdentifier="
-                + carrierIdentifier.toString()
+                + mCarrierIdentifier
                 + ", policyRules="
-                + policyRules
+                + mPolicyRules
                 + ", accessRules="
-                + Arrays.toString(accessRules)
+                + Arrays.toString(mAccessRules)
                 + ")";
     }
 }
diff --git a/android/service/euicc/EuiccService.java b/android/service/euicc/EuiccService.java
index be85800..b87faef 100644
--- a/android/service/euicc/EuiccService.java
+++ b/android/service/euicc/EuiccService.java
@@ -17,6 +17,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
@@ -41,8 +42,11 @@
  * <p>To implement the LPA backend, you must extend this class and declare this service in your
  * manifest file. The service must require the
  * {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter
- * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set
- * to a non-zero value in case multiple implementations are present on the device. For example:
+ * with the {@link #EUICC_SERVICE_INTERFACE} action. It's suggested that the priority of the intent
+ * filter to be set to a non-zero value in case multiple implementations are present on the device.
+ * See the below example. Note that there will be problem if two LPAs are present and they have the
+ * same priority.
+ * Example:
  *
  * <pre>{@code
  * <service android:name=".MyEuiccService"
@@ -65,9 +69,9 @@
  * filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
  * priority.
  *
- * TODO(b/35851809): Make this a SystemApi.
  * @hide
  */
+@SystemApi
 public abstract class EuiccService extends Service {
     /** Action which must be included in this service's intent filter. */
     public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
@@ -77,7 +81,10 @@
 
     // LUI actions. These are passthroughs of the corresponding EuiccManager actions.
 
-    /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */
+    /**
+     * @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS
+     * The difference is this one is used by system to bring up the LUI.
+     */
     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
             "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
     /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
@@ -88,7 +95,10 @@
     // require user interaction.
     // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
     // more scoped out.
-    /** Alert the user that this action will result in an active SIM being deactivated. */
+    /**
+     * Alert the user that this action will result in an active SIM being deactivated.
+     * To implement the LUI triggered by the system, you need to define this in AndroidManifest.xml.
+     */
     public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
             "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
     /**
@@ -102,7 +112,11 @@
     public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
             "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
 
-    /** Intent extra set for resolution requests containing the package name of the calling app. */
+    /**
+     * Intent extra set for resolution requests containing the package name of the calling app.
+     * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM,
+     * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE.
+     */
     public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
             "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
 
@@ -136,10 +150,18 @@
         RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
     }
 
-    /** Boolean extra for resolution actions indicating whether the user granted consent. */
-    public static final String RESOLUTION_EXTRA_CONSENT = "consent";
-    /** String extra for resolution actions indicating the carrier confirmation code. */
-    public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code";
+    /**
+     * Boolean extra for resolution actions indicating whether the user granted consent.
+     * This is used and set by the implementation and used in {@code EuiccOperation}.
+     */
+    public static final String EXTRA_RESOLUTION_CONSENT =
+            "android.service.euicc.extra.RESOLUTION_CONSENT";
+    /**
+     * String extra for resolution actions indicating the carrier confirmation code.
+     * This is used and set by the implementation and used in {@code EuiccOperation}.
+     */
+    public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE =
+            "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
 
     private final IEuiccService.Stub mStubWrapper;
 
@@ -199,9 +221,9 @@
      *
      * @see IEuiccService#startOtaIfNecessary
      */
-    public interface OtaStatusChangedCallback {
+    public abstract static class OtaStatusChangedCallback {
         /** Called when OTA status is changed. */
-        void onOtaStatusChanged(int status);
+        public abstract void onOtaStatusChanged(int status);
     }
 
     /**
@@ -238,8 +260,7 @@
     /**
      * Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param subscription A subscription whose metadata needs to be populated.
      * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
      *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
@@ -267,8 +288,7 @@
     /**
      * Download the given subscription.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param subscription The subscription to download.
      * @param switchAfterDownload If true, the subscription should be enabled upon successful
      *     download.
@@ -286,8 +306,7 @@
     /**
      * Return a list of all @link EuiccProfileInfo}s.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @return The result of the operation.
      * @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
      * @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
@@ -297,8 +316,7 @@
     /**
      * Return info about the eUICC chip/device.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @return the {@link EuiccInfo} for the eUICC chip/device.
      * @see android.telephony.euicc.EuiccManager#getEuiccInfo
      */
@@ -310,8 +328,7 @@
      * <p>If the subscription is currently active, it should be deactivated first (equivalent to a
      * physical SIM being ejected).
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param iccid the ICCID of the subscription to delete.
      * @return the result of the delete operation. May be one of the predefined {@code RESULT_}
      *     constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
@@ -322,8 +339,7 @@
     /**
      * Switch to the given subscription.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param iccid the ICCID of the subscription to enable. May be null, in which case the current
      *     profile should be deactivated and no profile should be activated to replace it - this is
      *     equivalent to a physical SIM being ejected.
@@ -340,8 +356,7 @@
     /**
      * Update the nickname of the given subscription.
      *
-     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
-     *     but is here to future-proof the APIs.
+     * @param slotId ID of the SIM slot to use for the operation.
      * @param iccid the ICCID of the subscription to update.
      * @param nickname the new nickname to apply.
      * @return the result of the update operation. May be one of the predefined {@code RESULT_}
diff --git a/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
index 5a24492..e2171ae 100644
--- a/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
+++ b/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
@@ -16,16 +16,19 @@
 package android.service.euicc;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.euicc.DownloadableSubscription;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Result of a {@link EuiccService#onGetDefaultDownloadableSubscriptionList} operation.
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public final class GetDefaultDownloadableSubscriptionListResult implements Parcelable {
 
     public static final Creator<GetDefaultDownloadableSubscriptionListResult> CREATOR =
@@ -42,20 +45,35 @@
     };
 
     /**
-     * Result of the operation.
+     * @hide
+     * @deprecated - Do no use. Use getResult() instead.
+     */
+    @Deprecated
+    public final int result;
+
+    @Nullable
+    private final DownloadableSubscription[] mSubscriptions;
+
+    /**
+     * Gets the result of the operation.
      *
      * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
      * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
      */
-    public final int result;
+    public int getResult() {
+        return result;
+    }
 
     /**
-     * The available {@link DownloadableSubscription}s (with filled-in metadata).
+     * Gets the available {@link DownloadableSubscription}s (with filled-in metadata).
      *
      * <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}.
      */
     @Nullable
-    public final DownloadableSubscription[] subscriptions;
+    public List<DownloadableSubscription> getDownloadableSubscriptions() {
+        if (mSubscriptions == null) return null;
+        return Arrays.asList(mSubscriptions);
+    }
 
     /**
      * Construct a new {@link GetDefaultDownloadableSubscriptionListResult}.
@@ -70,25 +88,25 @@
             @Nullable DownloadableSubscription[] subscriptions) {
         this.result = result;
         if (this.result == EuiccService.RESULT_OK) {
-            this.subscriptions = subscriptions;
+            this.mSubscriptions = subscriptions;
         } else {
             if (subscriptions != null) {
                 throw new IllegalArgumentException(
                         "Error result with non-null subscriptions: " + result);
             }
-            this.subscriptions = null;
+            this.mSubscriptions = null;
         }
     }
 
     private GetDefaultDownloadableSubscriptionListResult(Parcel in) {
         this.result = in.readInt();
-        this.subscriptions = in.createTypedArray(DownloadableSubscription.CREATOR);
+        this.mSubscriptions = in.createTypedArray(DownloadableSubscription.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(result);
-        dest.writeTypedArray(subscriptions, flags);
+        dest.writeTypedArray(mSubscriptions, flags);
     }
 
     @Override
diff --git a/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
index de8a307..1edb539 100644
--- a/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
+++ b/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
@@ -16,6 +16,7 @@
 package android.service.euicc;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.euicc.DownloadableSubscription;
@@ -23,9 +24,8 @@
 /**
  * Result of a {@link EuiccService#onGetDownloadableSubscriptionMetadata} operation.
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public final class GetDownloadableSubscriptionMetadataResult implements Parcelable {
 
     public static final Creator<GetDownloadableSubscriptionMetadataResult> CREATOR =
@@ -42,20 +42,34 @@
     };
 
     /**
-     * Result of the operation.
+     * @hide
+     * @deprecated - Do no use. Use getResult() instead.
+     */
+    @Deprecated
+    public final int result;
+
+    @Nullable
+    private final DownloadableSubscription mSubscription;
+
+    /**
+     * Gets the result of the operation.
      *
      * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
      * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
      */
-    public final int result;
+    public int getResult() {
+        return result;
+    }
 
     /**
-     * The {@link DownloadableSubscription} with filled-in metadata.
+     * Gets the {@link DownloadableSubscription} with filled-in metadata.
      *
      * <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}.
      */
     @Nullable
-    public final DownloadableSubscription subscription;
+    public DownloadableSubscription getDownloadableSubscription() {
+        return mSubscription;
+    }
 
     /**
      * Construct a new {@link GetDownloadableSubscriptionMetadataResult}.
@@ -70,25 +84,25 @@
             @Nullable DownloadableSubscription subscription) {
         this.result = result;
         if (this.result == EuiccService.RESULT_OK) {
-            this.subscription = subscription;
+            this.mSubscription = subscription;
         } else {
             if (subscription != null) {
                 throw new IllegalArgumentException(
                         "Error result with non-null subscription: " + result);
             }
-            this.subscription = null;
+            this.mSubscription = null;
         }
     }
 
     private GetDownloadableSubscriptionMetadataResult(Parcel in) {
         this.result = in.readInt();
-        this.subscription = in.readTypedObject(DownloadableSubscription.CREATOR);
+        this.mSubscription = in.readTypedObject(DownloadableSubscription.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(result);
-        dest.writeTypedObject(this.subscription, flags);
+        dest.writeTypedObject(this.mSubscription, flags);
     }
 
     @Override
diff --git a/android/service/euicc/GetEuiccProfileInfoListResult.java b/android/service/euicc/GetEuiccProfileInfoListResult.java
index 7ad8488..464d136 100644
--- a/android/service/euicc/GetEuiccProfileInfoListResult.java
+++ b/android/service/euicc/GetEuiccProfileInfoListResult.java
@@ -16,15 +16,18 @@
 package android.service.euicc;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Result of a {@link EuiccService#onGetEuiccProfileInfoList} operation.
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public final class GetEuiccProfileInfoListResult implements Parcelable {
 
     public static final Creator<GetEuiccProfileInfoListResult> CREATOR =
@@ -41,19 +44,38 @@
             };
 
     /**
-     * Result of the operation.
+     * @hide
+     * @deprecated - Do no use. Use getResult() instead.
+     */
+    @Deprecated
+    public final int result;
+
+    @Nullable
+    private final EuiccProfileInfo[] mProfiles;
+
+    private final boolean mIsRemovable;
+
+    /**
+     * Gets the result of the operation.
      *
      * <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
      * implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
      */
-    public final int result;
+    public int getResult() {
+        return result;
+    }
 
-    /** The profile list (only upon success). */
+    /** Gets the profile list (only upon success). */
     @Nullable
-    public final EuiccProfileInfo[] profiles;
+    public List<EuiccProfileInfo> getProfiles() {
+        if (mProfiles == null) return null;
+        return Arrays.asList(mProfiles);
+    }
 
-    /** Whether the eUICC is removable. */
-    public final boolean isRemovable;
+    /** Gets whether the eUICC is removable. */
+    public boolean getIsRemovable() {
+        return mIsRemovable;
+    }
 
     /**
      * Construct a new {@link GetEuiccProfileInfoListResult}.
@@ -71,30 +93,29 @@
     public GetEuiccProfileInfoListResult(
             int result, @Nullable EuiccProfileInfo[] profiles, boolean isRemovable) {
         this.result = result;
-        this.isRemovable = isRemovable;
+        this.mIsRemovable = isRemovable;
         if (this.result == EuiccService.RESULT_OK) {
-            this.profiles = profiles;
+            this.mProfiles = profiles;
         } else {
             if (profiles != null) {
                 throw new IllegalArgumentException(
                         "Error result with non-null profiles: " + result);
             }
-            this.profiles = null;
+            this.mProfiles = null;
         }
-
     }
 
     private GetEuiccProfileInfoListResult(Parcel in) {
         this.result = in.readInt();
-        this.profiles = in.createTypedArray(EuiccProfileInfo.CREATOR);
-        this.isRemovable = in.readBoolean();
+        this.mProfiles = in.createTypedArray(EuiccProfileInfo.CREATOR);
+        this.mIsRemovable = in.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(result);
-        dest.writeTypedArray(profiles, flags);
-        dest.writeBoolean(isRemovable);
+        dest.writeTypedArray(mProfiles, flags);
+        dest.writeBoolean(mIsRemovable);
     }
 
     @Override
diff --git a/android/service/media/MediaBrowserService.java b/android/service/media/MediaBrowserService.java
index 4fc43ea..6d1bd45 100644
--- a/android/service/media/MediaBrowserService.java
+++ b/android/service/media/MediaBrowserService.java
@@ -31,6 +31,8 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -112,6 +114,8 @@
      */
     private class ConnectionRecord implements IBinder.DeathRecipient {
         String pkg;
+        int uid;
+        int pid;
         Bundle rootHints;
         IMediaBrowserServiceCallbacks callbacks;
         BrowserRoot root;
@@ -199,6 +203,7 @@
         public void connect(final String pkg, final Bundle rootHints,
                 final IMediaBrowserServiceCallbacks callbacks) {
 
+            final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             if (!isValidPackage(pkg, uid)) {
                 throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
@@ -215,9 +220,14 @@
 
                         final ConnectionRecord connection = new ConnectionRecord();
                         connection.pkg = pkg;
+                        connection.pid = pid;
+                        connection.uid = uid;
                         connection.rootHints = rootHints;
                         connection.callbacks = callbacks;
+
+                        mCurConnection = connection;
                         connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
+                        mCurConnection = null;
 
                         // If they didn't return something, don't allow this client.
                         if (connection.root == null) {
@@ -505,21 +515,36 @@
      * media browser service when connecting and retrieving the root id for browsing, or null if
      * none. The contents of this bundle may affect the information returned when browsing.
      *
-     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} or
-     *             {@link #onLoadItem}.
+     * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+     *             {@link #onLoadChildren} or {@link #onLoadItem}.
      * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
      * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
      * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
      */
     public final Bundle getBrowserRootHints() {
         if (mCurConnection == null) {
-            throw new IllegalStateException("This should be called inside of onLoadChildren or"
-                    + " onLoadItem methods");
+            throw new IllegalStateException("This should be called inside of onGetRoot or"
+                    + " onLoadChildren or onLoadItem methods");
         }
         return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
     }
 
     /**
+     * Gets the browser information who sent the current request.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+     *             {@link #onLoadChildren} or {@link #onLoadItem}.
+     * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public final RemoteUserInfo getCurrentBrowserInfo() {
+        if (mCurConnection == null) {
+            throw new IllegalStateException("This should be called inside of onGetRoot or"
+                    + " onLoadChildren or onLoadItem methods");
+        }
+        return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid);
+    }
+
+    /**
      * Notifies all connected media browsers that the children of
      * the specified parent id have changed in some way.
      * This will cause browsers to fetch subscribed content again.
diff --git a/android/service/notification/Condition.java b/android/service/notification/Condition.java
index 2a352ad..b6c6bdc 100644
--- a/android/service/notification/Condition.java
+++ b/android/service/notification/Condition.java
@@ -18,11 +18,11 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
-import android.app.AutomaticZenRule;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -161,6 +161,22 @@
             .append(']').toString();
     }
 
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        // id is guarantreed not to be null.
+        proto.write(ConditionProto.ID, id.toString());
+        proto.write(ConditionProto.SUMMARY, summary);
+        proto.write(ConditionProto.LINE_1, line1);
+        proto.write(ConditionProto.LINE_2, line2);
+        proto.write(ConditionProto.ICON, icon);
+        proto.write(ConditionProto.STATE, state);
+        proto.write(ConditionProto.FLAGS, flags);
+
+        proto.end(token);
+    }
+
     @SystemApi
     public static String stateToString(int state) {
         if (state == STATE_FALSE) return "STATE_FALSE";
diff --git a/android/service/notification/NotificationListenerService.java b/android/service/notification/NotificationListenerService.java
index b7b2b2d..3726e66 100644
--- a/android/service/notification/NotificationListenerService.java
+++ b/android/service/notification/NotificationListenerService.java
@@ -85,7 +85,10 @@
  * or after {@link #onListenerDisconnected()}.
  * </p>
  * <p> Notification listeners cannot get notification access or be bound by the system on
- * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
+ * {@linkplain ActivityManager#isLowRamDevice() low-RAM} devices. The system also ignores
+ * notification listeners running in a work profile. A
+ * {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work
+ * profile.</p>
  */
 public abstract class NotificationListenerService extends Service {
 
@@ -146,13 +149,19 @@
     /**
      * Whether notification suppressed by DND should not interruption visually when the screen is
      * off.
+     *
+     * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}.
      */
+    @Deprecated
     public static final int SUPPRESSED_EFFECT_SCREEN_OFF =
             NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
     /**
      * Whether notification suppressed by DND should not interruption visually when the screen is
      * on.
+     *
+     * @deprecated Use the more specific visual effects in {@link NotificationManager.Policy}.
      */
+    @Deprecated
     public static final int SUPPRESSED_EFFECT_SCREEN_ON =
             NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 
@@ -1217,6 +1226,7 @@
                 // convert icon metadata to legacy format for older clients
                 createLegacyIconExtras(sbn.getNotification());
                 maybePopulateRemoteViews(sbn.getNotification());
+                maybePopulatePeople(sbn.getNotification());
             } catch (IllegalArgumentException e) {
                 // warn and drop corrupt notification
                 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
@@ -1343,6 +1353,7 @@
     /**
      * @hide
      */
+    @GuardedBy("mLock")
     public final void applyUpdateLocked(NotificationRankingUpdate update) {
         mRankingMap = new RankingMap(update);
     }
@@ -1407,6 +1418,7 @@
         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
         private boolean mShowBadge;
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
+        private boolean mHidden;
 
         public Ranking() {}
 
@@ -1448,7 +1460,8 @@
 
         /**
          * Returns the type(s) of visual effects that should be suppressed for this notification.
-         * See {@link #SUPPRESSED_EFFECT_SCREEN_OFF}, {@link #SUPPRESSED_EFFECT_SCREEN_ON}.
+         * See {@link NotificationManager.Policy}, e.g.
+         * {@link NotificationManager.Policy#SUPPRESSED_EFFECT_LIGHTS}.
          */
         public int getSuppressedVisualEffects() {
             return mSuppressedVisualEffects;
@@ -1545,6 +1558,16 @@
         }
 
         /**
+         * Returns whether the app that posted this notification is suspended, so this notification
+         * should be hidden.
+         *
+         * @return true if the notification should be hidden, false otherwise.
+         */
+        public boolean isSuspended() {
+            return mHidden;
+        }
+
+        /**
          * @hide
          */
         @VisibleForTesting
@@ -1553,7 +1576,7 @@
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
-                int userSentiment) {
+                int userSentiment, boolean hidden) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1568,6 +1591,7 @@
             mSnoozeCriteria = snoozeCriteria;
             mShowBadge = showBadge;
             mUserSentiment = userSentiment;
+            mHidden = hidden;
         }
 
         /**
@@ -1616,6 +1640,7 @@
         private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
         private ArrayMap<String, Boolean> mShowBadge;
         private ArrayMap<String, Integer> mUserSentiment;
+        private ArrayMap<String, Boolean> mHidden;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1644,7 +1669,7 @@
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
-                    getShowBadge(key), getUserSentiment(key));
+                    getShowBadge(key), getUserSentiment(key), getHidden(key));
             return rank >= 0;
         }
 
@@ -1772,6 +1797,16 @@
                     ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
         }
 
+        private boolean getHidden(String key) {
+            synchronized (this) {
+                if (mHidden == null) {
+                    buildHiddenLocked();
+                }
+            }
+            Boolean hidden = mHidden.get(key);
+            return hidden == null ? false : hidden.booleanValue();
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1880,6 +1915,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildHiddenLocked() {
+            Bundle hidden = mRankingUpdate.getHidden();
+            mHidden = new ArrayMap<>(hidden.size());
+            for (String key : hidden.keySet()) {
+                mHidden.put(key, hidden.getBoolean(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/android/service/notification/NotificationRankingUpdate.java b/android/service/notification/NotificationRankingUpdate.java
index 6d51db0..00c47ec 100644
--- a/android/service/notification/NotificationRankingUpdate.java
+++ b/android/service/notification/NotificationRankingUpdate.java
@@ -36,12 +36,13 @@
     private final Bundle mSnoozeCriteria;
     private final Bundle mShowBadge;
     private final Bundle mUserSentiment;
+    private final Bundle mHidden;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
-            Bundle showBadge, Bundle userSentiment) {
+            Bundle showBadge, Bundle userSentiment, Bundle hidden) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -54,6 +55,7 @@
         mSnoozeCriteria = snoozeCriteria;
         mShowBadge = showBadge;
         mUserSentiment = userSentiment;
+        mHidden = hidden;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -70,6 +72,7 @@
         mSnoozeCriteria = in.readBundle();
         mShowBadge = in.readBundle();
         mUserSentiment = in.readBundle();
+        mHidden = in.readBundle();
     }
 
     @Override
@@ -91,6 +94,7 @@
         out.writeBundle(mSnoozeCriteria);
         out.writeBundle(mShowBadge);
         out.writeBundle(mUserSentiment);
+        out.writeBundle(mHidden);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -151,4 +155,8 @@
     public Bundle getUserSentiment() {
         return mUserSentiment;
     }
+
+    public Bundle getHidden() {
+        return mHidden;
+    }
 }
diff --git a/android/service/notification/ScheduleCalendar.java b/android/service/notification/ScheduleCalendar.java
index 8a7ff4d..0128710 100644
--- a/android/service/notification/ScheduleCalendar.java
+++ b/android/service/notification/ScheduleCalendar.java
@@ -144,7 +144,8 @@
         }
         return mSchedule.exitAtAlarm
                 && mSchedule.nextAlarm != 0
-                && time >= mSchedule.nextAlarm;
+                && time >= mSchedule.nextAlarm
+                && isInSchedule(mSchedule.nextAlarm);
     }
 
     private boolean isInSchedule(int daysOffset, long time, long start, long end) {
diff --git a/android/service/notification/ZenModeConfig.java b/android/service/notification/ZenModeConfig.java
index f658ae0..3830b7a 100644
--- a/android/service/notification/ZenModeConfig.java
+++ b/android/service/notification/ZenModeConfig.java
@@ -17,6 +17,8 @@
 package android.service.notification;
 
 import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.Context;
@@ -33,6 +35,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
 
@@ -82,22 +85,26 @@
 
     // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values:
     private static final boolean DEFAULT_ALLOW_ALARMS = true;
-    private static final boolean DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER = true;
+    private static final boolean DEFAULT_ALLOW_MEDIA = true;
+    private static final boolean DEFAULT_ALLOW_SYSTEM = false;
     private static final boolean DEFAULT_ALLOW_CALLS = false;
     private static final boolean DEFAULT_ALLOW_MESSAGES = false;
     private static final boolean DEFAULT_ALLOW_REMINDERS = false;
     private static final boolean DEFAULT_ALLOW_EVENTS = false;
     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
-    private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
-    private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
+    private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false;
+    private static final boolean DEFAULT_ALLOW_SCREEN_ON = false;
+    private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
+            Policy.getAllSuppressedVisualEffects();
 
-    private static final int XML_VERSION = 2;
+    public static final int XML_VERSION = 6;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
     private static final String ALLOW_TAG = "allow";
     private static final String ALLOW_ATT_ALARMS = "alarms";
-    private static final String ALLOW_ATT_MEDIA_SYSTEM_OTHER = "media_system_other";
+    private static final String ALLOW_ATT_MEDIA = "media";
+    private static final String ALLOW_ATT_SYSTEM = "system";
     private static final String ALLOW_ATT_CALLS = "calls";
     private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
     private static final String ALLOW_ATT_MESSAGES = "messages";
@@ -108,6 +115,8 @@
     private static final String ALLOW_ATT_EVENTS = "events";
     private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
     private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
+    private static final String DISALLOW_TAG = "disallow";
+    private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
 
     private static final String CONDITION_ATT_ID = "id";
     private static final String CONDITION_ATT_SUMMARY = "summary";
@@ -131,7 +140,8 @@
     private static final String RULE_ATT_ENABLER = "enabler";
 
     public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
-    public boolean allowMediaSystemOther = DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER;
+    public boolean allowMedia = DEFAULT_ALLOW_MEDIA;
+    public boolean allowSystem = DEFAULT_ALLOW_SYSTEM;
     public boolean allowCalls = DEFAULT_ALLOW_CALLS;
     public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
     public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
@@ -140,8 +150,10 @@
     public int allowCallsFrom = DEFAULT_SOURCE;
     public int allowMessagesFrom = DEFAULT_SOURCE;
     public int user = UserHandle.USER_SYSTEM;
+    public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
     public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
     public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+    public int version;
 
     public ZenRule manualRule;
     public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -171,7 +183,9 @@
         allowWhenScreenOff = source.readInt() == 1;
         allowWhenScreenOn = source.readInt() == 1;
         allowAlarms = source.readInt() == 1;
-        allowMediaSystemOther = source.readInt() == 1;
+        allowMedia = source.readInt() == 1;
+        allowSystem = source.readInt() == 1;
+        suppressedVisualEffects = source.readInt();
     }
 
     @Override
@@ -202,7 +216,9 @@
         dest.writeInt(allowWhenScreenOff ? 1 : 0);
         dest.writeInt(allowWhenScreenOn ? 1 : 0);
         dest.writeInt(allowAlarms ? 1 : 0);
-        dest.writeInt(allowMediaSystemOther ? 1 : 0);
+        dest.writeInt(allowMedia ? 1 : 0);
+        dest.writeInt(allowSystem ? 1 : 0);
+        dest.writeInt(suppressedVisualEffects);
     }
 
     @Override
@@ -210,7 +226,8 @@
         return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
                 .append("user=").append(user)
                 .append(",allowAlarms=").append(allowAlarms)
-                .append(",allowMediaSystemOther=").append(allowMediaSystemOther)
+                .append(",allowMedia=").append(allowMedia)
+                .append(",allowSystem=").append(allowSystem)
                 .append(",allowReminders=").append(allowReminders)
                 .append(",allowEvents=").append(allowEvents)
                 .append(",allowCalls=").append(allowCalls)
@@ -220,6 +237,7 @@
                 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
                 .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
                 .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
+                .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
                 .append(",automaticRules=").append(automaticRules)
                 .append(",manualRule=").append(manualRule)
                 .append(']').toString();
@@ -236,8 +254,11 @@
         if (allowAlarms != to.allowAlarms) {
             d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
         }
-        if (allowMediaSystemOther != to.allowMediaSystemOther) {
-            d.addLine("allowMediaSystemOther", allowMediaSystemOther, to.allowMediaSystemOther);
+        if (allowMedia != to.allowMedia) {
+            d.addLine("allowMedia", allowMedia, to.allowMedia);
+        }
+        if (allowSystem != to.allowSystem) {
+            d.addLine("allowSystem", allowSystem, to.allowSystem);
         }
         if (allowCalls != to.allowCalls) {
             d.addLine("allowCalls", allowCalls, to.allowCalls);
@@ -266,6 +287,10 @@
         if (allowWhenScreenOn != to.allowWhenScreenOn) {
             d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn);
         }
+        if (suppressedVisualEffects != to.suppressedVisualEffects) {
+            d.addLine("suppressedVisualEffects", suppressedVisualEffects,
+                    to.suppressedVisualEffects);
+        }
         final ArraySet<String> allRules = new ArraySet<>();
         addKeys(allRules, automaticRules);
         addKeys(allRules, to.automaticRules);
@@ -357,7 +382,8 @@
         if (o == this) return true;
         final ZenModeConfig other = (ZenModeConfig) o;
         return other.allowAlarms == allowAlarms
-                && other.allowMediaSystemOther == allowMediaSystemOther
+                && other.allowMedia == allowMedia
+                && other.allowSystem == allowSystem
                 && other.allowCalls == allowCalls
                 && other.allowRepeatCallers == allowRepeatCallers
                 && other.allowMessages == allowMessages
@@ -369,15 +395,17 @@
                 && other.allowWhenScreenOn == allowWhenScreenOn
                 && other.user == user
                 && Objects.equals(other.automaticRules, automaticRules)
-                && Objects.equals(other.manualRule, manualRule);
+                && Objects.equals(other.manualRule, manualRule)
+                && other.suppressedVisualEffects == suppressedVisualEffects;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(allowAlarms, allowMediaSystemOther, allowCalls,
+        return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
                 allowRepeatCallers, allowMessages,
                 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
-                allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule);
+                allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule,
+                suppressedVisualEffects);
     }
 
     private static String toDayList(int[] days) {
@@ -428,6 +456,7 @@
         String tag = parser.getName();
         if (!ZEN_TAG.equals(tag)) return null;
         final ZenModeConfig rt = new ZenModeConfig();
+        rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
         rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
             tag = parser.getName();
@@ -459,13 +488,19 @@
                         rt.allowCallsFrom = DEFAULT_SOURCE;
                         rt.allowMessagesFrom = DEFAULT_SOURCE;
                     }
+                    // continue to read even though we now have suppressedVisualEffects, in case
+                    // we need to revert to users' previous settings
                     rt.allowWhenScreenOff =
                             safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
                     rt.allowWhenScreenOn =
                             safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
                     rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
-                    rt.allowMediaSystemOther = safeBoolean(parser, ALLOW_ATT_MEDIA_SYSTEM_OTHER,
-                            DEFAULT_ALLOW_MEDIA_SYSTEM_OTHER);
+                    rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
+                            DEFAULT_ALLOW_MEDIA);
+                    rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
+                } else if (DISALLOW_TAG.equals(tag)) {
+                    rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
+                            DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
                 } else if (MANUAL_TAG.equals(tag)) {
                     rt.manualRule = readRuleXml(parser);
                 } else if (AUTOMATIC_TAG.equals(tag)) {
@@ -481,11 +516,17 @@
         throw new IllegalStateException("Failed to reach END_DOCUMENT");
     }
 
-    public void writeXml(XmlSerializer out) throws IOException {
+    /**
+     * Writes XML of current ZenModeConfig
+     * @param out serializer
+     * @param version uses XML_VERSION if version is null
+     * @throws IOException
+     */
+    public void writeXml(XmlSerializer out, Integer version) throws IOException {
         out.startTag(null, ZEN_TAG);
-        out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
+        out.attribute(null, ZEN_ATT_VERSION, version == null
+                ? Integer.toString(XML_VERSION) : Integer.toString(version));
         out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
-
         out.startTag(null, ALLOW_TAG);
         out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
         out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
@@ -497,9 +538,14 @@
         out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
         out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
         out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
-        out.attribute(null, ALLOW_ATT_MEDIA_SYSTEM_OTHER, Boolean.toString(allowMediaSystemOther));
+        out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
+        out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
         out.endTag(null, ALLOW_TAG);
 
+        out.startTag(null, DISALLOW_TAG);
+        out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects));
+        out.endTag(null, DISALLOW_TAG);
+
         if (manualRule != null) {
             out.startTag(null, MANUAL_TAG);
             writeRuleXml(manualRule, out);
@@ -684,18 +730,14 @@
         if (allowRepeatCallers) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
         }
-        int suppressedVisualEffects = 0;
-        if (!allowWhenScreenOff) {
-            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
-        }
-        if (!allowWhenScreenOn) {
-            suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
-        }
         if (allowAlarms) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
         }
-        if (allowMediaSystemOther) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER;
+        if (allowMedia) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
+        }
+        if (allowSystem) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
         }
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
@@ -738,8 +780,8 @@
     public void applyNotificationPolicy(Policy policy) {
         if (policy == null) return;
         allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
-        allowMediaSystemOther = (policy.priorityCategories
-                & Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) != 0;
+        allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
+        allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
         allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
         allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
         allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
@@ -750,10 +792,7 @@
         allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
                 allowMessagesFrom);
         if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
-            allowWhenScreenOff =
-                    (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_OFF) == 0;
-            allowWhenScreenOn =
-                    (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0;
+            suppressedVisualEffects = policy.suppressedVisualEffects;
         }
     }
 
@@ -1262,6 +1301,30 @@
                     .append(']').toString();
         }
 
+        /** @hide */
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(ZenRuleProto.ID, id);
+            proto.write(ZenRuleProto.NAME, name);
+            proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
+            proto.write(ZenRuleProto.ENABLED, enabled);
+            proto.write(ZenRuleProto.ENABLER, enabler);
+            proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+            proto.write(ZenRuleProto.ZEN_MODE, zenMode);
+            if (conditionId != null) {
+                proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
+            }
+            if (condition != null) {
+                condition.writeToProto(proto, ZenRuleProto.CONDITION);
+            }
+            if (component != null) {
+                component.writeToProto(proto, ZenRuleProto.COMPONENT);
+            }
+
+            proto.end(token);
+        }
+
         private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
             if (d == null) return;
             if (from == null) {
@@ -1385,4 +1448,134 @@
         }
     }
 
+    /**
+     * Determines whether dnd behavior should mute all notification/ringer sounds
+     * (sounds associated with ringer volume discluding system)
+     */
+    public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
+            policy) {
+        boolean allowReminders = (policy.priorityCategories
+                & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
+        boolean allowCalls = (policy.priorityCategories
+                & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0;
+        boolean allowMessages = (policy.priorityCategories
+                & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
+        boolean allowEvents = (policy.priorityCategories
+                & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
+        boolean allowRepeatCallers = (policy.priorityCategories
+                & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
+        return !allowReminders && !allowCalls && !allowMessages && !allowEvents
+                && !allowRepeatCallers;
+    }
+
+    /**
+     * Determines whether dnd behavior should mute all sounds controlled by ringer
+     */
+    public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
+        return !config.allowReminders && !config.allowCalls && !config.allowMessages
+                && !config.allowEvents && !config.allowRepeatCallers;
+    }
+
+    /**
+     * Determines whether all dnd mutes all sounds
+     */
+    public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
+        return !config.allowAlarms  && !config.allowMedia && !config.allowSystem
+                && areAllPriorityOnlyNotificationZenSoundsMuted(config);
+    }
+
+    /**
+     * Returns a description of the current do not disturb settings from config.
+     * - If turned on manually and end time is known, returns end time.
+     * - If turned on by an automatic rule, returns the automatic rule name.
+     * - If on due to an app, returns the app name.
+     * - If there's a combination of rules/apps that trigger, then shows the one that will
+     *  last the longest if applicable.
+     * @return null if do not disturb is off.
+     */
+    public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) {
+        if (!zenOn) {
+            return null;
+        }
+
+        String secondaryText = "";
+        long latestEndTime = -1;
+
+        // DND turned on by manual rule
+        if (config.manualRule != null) {
+            final Uri id = config.manualRule.conditionId;
+            if (config.manualRule.enabler != null) {
+                // app triggered manual rule
+                String appName = getOwnerCaption(context, config.manualRule.enabler);
+                if (!appName.isEmpty()) {
+                    secondaryText = appName;
+                }
+            } else {
+                if (id == null) {
+                    // Do not disturb manually triggered to remain on forever until turned off
+                    // No subtext
+                    return null;
+                } else {
+                    latestEndTime = tryParseCountdownConditionId(id);
+                    if (latestEndTime > 0) {
+                        final CharSequence formattedTime = getFormattedTime(context,
+                                latestEndTime, isToday(latestEndTime),
+                                context.getUserId());
+                        secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
+                    }
+                }
+            }
+        }
+
+        // DND turned on by an automatic rule
+        for (ZenRule automaticRule : config.automaticRules.values()) {
+            if (automaticRule.isAutomaticActive()) {
+                if (isValidEventConditionId(automaticRule.conditionId)
+                        || isValidScheduleConditionId(automaticRule.conditionId)) {
+                    // set text if automatic rule end time is the latest active rule end time
+                    long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
+                    if (endTime > latestEndTime) {
+                        latestEndTime = endTime;
+                        secondaryText = automaticRule.name;
+                    }
+                } else {
+                    // set text if 3rd party rule
+                    return automaticRule.name;
+                }
+            }
+        }
+
+        return !secondaryText.equals("") ? secondaryText : null;
+    }
+
+    private static long parseAutomaticRuleEndTime(Context context, Uri id) {
+        if (isValidEventConditionId(id)) {
+            // cannot look up end times for events
+            return Long.MAX_VALUE;
+        }
+
+        if (isValidScheduleConditionId(id)) {
+            ScheduleCalendar schedule = toScheduleCalendar(id);
+            long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
+
+            // check if automatic rule will end on next alarm
+            if (schedule.exitAtAlarm()) {
+                long nextAlarm = getNextAlarm(context);
+                schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
+                if (schedule.shouldExitForAlarm(endTimeMs)) {
+                    return nextAlarm;
+                }
+            }
+
+            return endTimeMs;
+        }
+
+        return -1;
+    }
+
+    private static long getNextAlarm(Context context) {
+        final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
+        return info != null ? info.getTriggerTime() : 0;
+    }
 }
diff --git a/android/service/settings/suggestions/Suggestion.java b/android/service/settings/suggestions/Suggestion.java
index 11e1e67..e97f963 100644
--- a/android/service/settings/suggestions/Suggestion.java
+++ b/android/service/settings/suggestions/Suggestion.java
@@ -40,6 +40,7 @@
      */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             FLAG_HAS_BUTTON,
+            FLAG_ICON_TINTABLE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {
@@ -49,6 +50,10 @@
      * Flag for suggestion type with a single button
      */
     public static final int FLAG_HAS_BUTTON = 1 << 0;
+    /**
+     * @hide
+     */
+    public static final int FLAG_ICON_TINTABLE = 1 << 1;
 
     private final String mId;
     private final CharSequence mTitle;
diff --git a/android/service/textclassifier/TextClassifierService.java b/android/service/textclassifier/TextClassifierService.java
new file mode 100644
index 0000000..f1bb72c
--- /dev/null
+++ b/android/service/textclassifier/TextClassifierService.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.textclassifier;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextSelection;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Abstract base class for the TextClassifier service.
+ *
+ * <p>A TextClassifier service provides text classification related features for the system.
+ * The system's default TextClassifierService is configured in
+ * {@code config_defaultTextClassifierService}. If this config has no value, a
+ * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
+ *
+ * <p>See: {@link TextClassifier}.
+ * See: {@link TextClassificationManager}.
+ *
+ * <p>Include the following in the manifest:
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourTextClassifierService"
+ *          android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
+ *     <intent-filter>
+ *         <action android:name="android.service.textclassifier.TextClassifierService" />
+ *     </intent-filter>
+ * </service>}</pre>
+ *
+ * @see TextClassifier
+ * @hide
+ */
+@SystemApi
+public abstract class TextClassifierService extends Service {
+
+    private static final String LOG_TAG = "TextClassifierService";
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE} permission so
+     * that other applications can not abuse it.
+     */
+    @SystemApi
+    public static final String SERVICE_INTERFACE =
+            "android.service.textclassifier.TextClassifierService";
+
+    private final ITextClassifierService.Stub mBinder = new ITextClassifierService.Stub() {
+
+        // TODO(b/72533911): Implement cancellation signal
+        @NonNull private final CancellationSignal mCancellationSignal = new CancellationSignal();
+
+        /** {@inheritDoc} */
+        @Override
+        public void onSuggestSelection(
+                TextClassificationSessionId sessionId,
+                TextSelection.Request request, ITextSelectionCallback callback)
+                throws RemoteException {
+            Preconditions.checkNotNull(request);
+            Preconditions.checkNotNull(callback);
+            TextClassifierService.this.onSuggestSelection(
+                    sessionId, request, mCancellationSignal,
+                    new Callback<TextSelection>() {
+                        @Override
+                        public void onSuccess(TextSelection result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                if (callback.asBinder().isBinderAlive()) {
+                                    callback.onFailure();
+                                }
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onClassifyText(
+                TextClassificationSessionId sessionId,
+                TextClassification.Request request, ITextClassificationCallback callback)
+                throws RemoteException {
+            Preconditions.checkNotNull(request);
+            Preconditions.checkNotNull(callback);
+            TextClassifierService.this.onClassifyText(
+                    sessionId, request, mCancellationSignal,
+                    new Callback<TextClassification>() {
+                        @Override
+                        public void onSuccess(TextClassification result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                callback.onFailure();
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onGenerateLinks(
+                TextClassificationSessionId sessionId,
+                TextLinks.Request request, ITextLinksCallback callback)
+                throws RemoteException {
+            Preconditions.checkNotNull(request);
+            Preconditions.checkNotNull(callback);
+            TextClassifierService.this.onGenerateLinks(
+                    sessionId, request, mCancellationSignal,
+                    new Callback<TextLinks>() {
+                        @Override
+                        public void onSuccess(TextLinks result) {
+                            try {
+                                callback.onSuccess(result);
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(CharSequence error) {
+                            try {
+                                callback.onFailure();
+                            } catch (RemoteException e) {
+                                Slog.d(LOG_TAG, "Error calling callback");
+                            }
+                        }
+                    });
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onSelectionEvent(
+                TextClassificationSessionId sessionId,
+                SelectionEvent event) throws RemoteException {
+            Preconditions.checkNotNull(event);
+            TextClassifierService.this.onSelectionEvent(sessionId, event);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onCreateTextClassificationSession(
+                TextClassificationContext context, TextClassificationSessionId sessionId)
+                throws RemoteException {
+            Preconditions.checkNotNull(context);
+            Preconditions.checkNotNull(sessionId);
+            TextClassifierService.this.onCreateTextClassificationSession(context, sessionId);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId)
+                throws RemoteException {
+            TextClassifierService.this.onDestroyTextClassificationSession(sessionId);
+        }
+    };
+
+    @Nullable
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * Returns suggested text selection start and end indices, recognized entity types, and their
+     * associated confidence scores. The entity types are ordered from highest to lowest scoring.
+     *
+     * @param sessionId the session id
+     * @param request the text selection request
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public abstract void onSuggestSelection(
+            @Nullable TextClassificationSessionId sessionId,
+            @NonNull TextSelection.Request request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextSelection> callback);
+
+    /**
+     * Classifies the specified text and returns a {@link TextClassification} object that can be
+     * used to generate a widget for handling the classified text.
+     *
+     * @param sessionId the session id
+     * @param request the text classification request
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public abstract void onClassifyText(
+            @Nullable TextClassificationSessionId sessionId,
+            @NonNull TextClassification.Request request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextClassification> callback);
+
+    /**
+     * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
+     * links information.
+     *
+     * @param sessionId the session id
+     * @param request the text classification request
+     * @param cancellationSignal object to watch for canceling the current operation
+     * @param callback the callback to return the result to
+     */
+    public abstract void onGenerateLinks(
+            @Nullable TextClassificationSessionId sessionId,
+            @NonNull TextLinks.Request request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextLinks> callback);
+
+    /**
+     * Writes the selection event.
+     * This is called when a selection event occurs. e.g. user changed selection; or smart selection
+     * happened.
+     *
+     * <p>The default implementation ignores the event.
+     *
+     * @param sessionId the session id
+     * @param event the selection event
+     */
+    public void onSelectionEvent(
+            @Nullable TextClassificationSessionId sessionId, @NonNull SelectionEvent event) {}
+
+    /**
+     * Creates a new text classification session for the specified context.
+     *
+     * @param context the text classification context
+     * @param sessionId the session's Id
+     */
+    public abstract void onCreateTextClassificationSession(
+            @NonNull TextClassificationContext context,
+            @NonNull TextClassificationSessionId sessionId);
+
+    /**
+     * Destroys the text classification session identified by the specified sessionId.
+     *
+     * @param sessionId the id of the session to destroy
+     */
+    public abstract void onDestroyTextClassificationSession(
+            @NonNull TextClassificationSessionId sessionId);
+
+    /**
+     * Returns a TextClassifier that runs in this service's process.
+     * If the local TextClassifier is disabled, this returns {@link TextClassifier#NO_OP}.
+     */
+    public final TextClassifier getLocalTextClassifier() {
+        final TextClassificationManager tcm = getSystemService(TextClassificationManager.class);
+        if (tcm != null) {
+            return tcm.getTextClassifier(TextClassifier.LOCAL);
+        }
+        return TextClassifier.NO_OP;
+    }
+
+    /**
+     * Callbacks for TextClassifierService results.
+     *
+     * @param <T> the type of the result
+     * @hide
+     */
+    @SystemApi
+    public interface Callback<T> {
+        /**
+         * Returns the result.
+         */
+        void onSuccess(T result);
+
+        /**
+         * Signals a failure.
+         */
+        void onFailure(CharSequence error);
+    }
+
+    /**
+     * Returns the component name of the system default textclassifier service if it can be found
+     * on the system. Otherwise, returns null.
+     * @hide
+     */
+    @Nullable
+    public static ComponentName getServiceComponentName(Context context) {
+        final String packageName = context.getPackageManager().getSystemTextClassifierPackageName();
+        if (TextUtils.isEmpty(packageName)) {
+            Slog.d(LOG_TAG, "No configured system TextClassifierService");
+            return null;
+        }
+
+        final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
+
+        final ResolveInfo ri = context.getPackageManager().resolveService(intent,
+                PackageManager.MATCH_SYSTEM_ONLY);
+
+        if ((ri == null) || (ri.serviceInfo == null)) {
+            Slog.w(LOG_TAG, String.format("Package or service not found in package %s",
+                    packageName));
+            return null;
+        }
+        final ServiceInfo si = ri.serviceInfo;
+
+        final String permission = si.permission;
+        if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
+            return si.getComponentName();
+        }
+        Slog.w(LOG_TAG, String.format(
+                "Service %s should require %s permission. Found %s permission",
+                si.getComponentName(),
+                Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
+                si.permission));
+        return null;
+    }
+}
diff --git a/android/service/trust/TrustAgentService.java b/android/service/trust/TrustAgentService.java
index 40e84b9..61277e2 100644
--- a/android/service/trust/TrustAgentService.java
+++ b/android/service/trust/TrustAgentService.java
@@ -545,7 +545,7 @@
      */
     public final void unlockUserWithToken(long handle, byte[] token, UserHandle user) {
         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
-        if (um.isUserUnlocked()) {
+        if (um.isUserUnlocked(user)) {
             Slog.i(TAG, "User already unlocked");
             return;
         }
diff --git a/android/service/wallpaper/WallpaperService.java b/android/service/wallpaper/WallpaperService.java
index 8588df7..a132730 100644
--- a/android/service/wallpaper/WallpaperService.java
+++ b/android/service/wallpaper/WallpaperService.java
@@ -788,8 +788,8 @@
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
-                            Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
-                                mDisplayCutout, mInputChannel) < 0) {
+                                Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets,
+                                mOutsets, mDisplayCutout, mInputChannel) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
diff --git a/android/speech/RecognitionService.java b/android/speech/RecognitionService.java
index 674f809..70dfef5 100644
--- a/android/speech/RecognitionService.java
+++ b/android/speech/RecognitionService.java
@@ -20,7 +20,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Service;
 import android.content.Intent;
-import android.content.pm.PackageManager;
+import android.content.PermissionChecker;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -174,8 +174,8 @@
      */
     private boolean checkPermissions(IRecognitionListener listener) {
         if (DBG) Log.d(TAG, "checkPermissions");
-        if (RecognitionService.this.checkCallingOrSelfPermission(android.Manifest.permission.
-                RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
+        if (PermissionChecker.checkCallingOrSelfPermission(this,
+                android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) {
             return true;
         }
         try {
diff --git a/android/speech/tts/SynthesisPlaybackQueueItem.java b/android/speech/tts/SynthesisPlaybackQueueItem.java
index 704a1da..2ed618b 100644
--- a/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -15,17 +15,17 @@
  */
 package android.speech.tts;
 
+import android.media.AudioTrack;
 import android.speech.tts.TextToSpeechService.AudioOutputParams;
 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
-import android.media.AudioTrack;
 import android.util.Log;
 
 import java.util.LinkedList;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * Manages the playback of a list of byte arrays representing audio data that are queued by the
@@ -157,9 +157,16 @@
             mStopped = true;
             mStatusCode = statusCode;
 
+            // Wake up the synthesis thread if it was waiting on put(). Its
+            // buffers will no longer be copied since mStopped is true. The
+            // PlaybackSynthesisCallback that this synthesis corresponds to
+            // would also have been stopped, and so all calls to
+            // Callback.onDataAvailable( ) will return errors too.
+            mNotFull.signal();
+
             if (mRunState.getAndSet(STOP_CALLED) == NOT_RUN) {
-                // Dispatch the status code and just finish without signaling
-                // if run() has not even started.
+                // Dispatch the status code and just finish. Signaling audio
+                // playback is not necessary because run() hasn't started.
                 dispatchEndStatus();
                 return;
             }
@@ -168,13 +175,6 @@
             // take() will return null since mStopped was true, and will then
             // break out of the data write loop.
             mReadReady.signal();
-
-            // Wake up the synthesis thread if it was waiting on put(). Its
-            // buffers will no longer be copied since mStopped is true. The
-            // PlaybackSynthesisCallback that this synthesis corresponds to
-            // would also have been stopped, and so all calls to
-            // Callback.onDataAvailable( ) will return errors too.
-            mNotFull.signal();
         } finally {
             mListLock.unlock();
         }
diff --git a/android/support/LibraryGroups.java b/android/support/LibraryGroups.java
deleted file mode 100644
index 19c0a92..0000000
--- a/android/support/LibraryGroups.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support;
-
-/**
- * The list of maven group names of all the libraries in this project.
- */
-public class LibraryGroups {
-    public static final String SUPPORT = "com.android.support";
-    public static final String ROOM = "android.arch.persistence.room";
-    public static final String PERSISTENCE = "android.arch.persistence";
-    public static final String LIFECYCLE = "android.arch.lifecycle";
-    public static final String ARCH_CORE = "android.arch.core";
-    public static final String PAGING = "android.arch.paging";
-    public static final String NAVIGATION = "android.arch.navigation";
-    public static final String SLICES = "androidx.app.slice";
-}
diff --git a/android/support/LibraryVersions.java b/android/support/LibraryVersions.java
deleted file mode 100644
index 813d9a8..0000000
--- a/android/support/LibraryVersions.java
+++ /dev/null
@@ -1,79 +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;
-
-/**
- * The list of versions codes of all the libraries in this project.
- */
-public class LibraryVersions {
-    /**
-     * Version code of the support library components.
-     */
-    public static final Version SUPPORT_LIBRARY = new Version("28.0.0-SNAPSHOT");
-
-    /**
-     * Version code for Room
-     */
-    public static final Version ROOM = new Version("1.1.0-alpha1");
-
-    /**
-     * Version code for Lifecycle extensions (ProcessLifecycleOwner, Fragment support)
-     */
-    public static final Version LIFECYCLES_EXT = new Version("1.1.0");
-
-    /**
-     * Version code for Lifecycle LiveData
-     */
-    public static final Version LIFECYCLES_LIVEDATA = LIFECYCLES_EXT;
-
-    /**
-     * Version code for Lifecycle ViewModel
-     */
-    public static final Version LIFECYCLES_VIEWMODEL = LIFECYCLES_EXT;
-
-    /**
-     * Version code for RecyclerView & Room paging
-     */
-    public static final Version PAGING = new Version("1.0.0-alpha5");
-
-    private static final Version LIFECYCLES = new Version("1.1.0");
-
-    /**
-     * Version code for Lifecycle libs that are required by the support library
-     */
-    public static final Version LIFECYCLES_CORE = LIFECYCLES;
-
-    /**
-     * Version code for Lifecycle runtime libs that are required by the support library
-     */
-    public static final Version LIFECYCLES_RUNTIME = LIFECYCLES;
-
-    /**
-     * Version code for shared code of flatfoot
-     */
-    public static final Version ARCH_CORE = new Version("1.1.0");
-
-    /**
-     * Version code for shared code of flatfoot runtime
-     */
-    public static final Version ARCH_RUNTIME = ARCH_CORE;
-
-    /**
-     * Version code for shared testing code of flatfoot
-     */
-    public static final Version ARCH_CORE_TESTING = ARCH_CORE;
-}
diff --git a/android/support/SourceJarTaskHelper.java b/android/support/SourceJarTaskHelper.java
deleted file mode 100644
index 9fbd1db..0000000
--- a/android/support/SourceJarTaskHelper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support;
-
-import com.android.build.gradle.LibraryExtension;
-import com.android.builder.core.BuilderConstants;
-
-import org.gradle.api.Project;
-import org.gradle.api.plugins.JavaPluginConvention;
-import org.gradle.api.tasks.bundling.Jar;
-
-/**
- * Helper class to handle creation of source jars.
- */
-public class SourceJarTaskHelper {
-    /**
-     * Sets up a source jar task for an Android library project.
-     */
-    public static void setUpAndroidProject(Project project, LibraryExtension extension) {
-        // Create sources jar for release builds
-        extension.getLibraryVariants().all(libraryVariant -> {
-            if (!libraryVariant.getBuildType().getName().equals(BuilderConstants.RELEASE)) {
-                return; // Skip non-release builds.
-            }
-
-            Jar sourceJar = project.getTasks().create("sourceJarRelease", Jar.class);
-            sourceJar.setPreserveFileTimestamps(false);
-            sourceJar.setClassifier("sources");
-            sourceJar.from(extension.getSourceSets().findByName("main").getJava().getSrcDirs());
-            project.getArtifacts().add("archives", sourceJar);
-        });
-    }
-
-    /**
-     * Sets up a source jar task for a Java library project.
-     */
-    public static void setUpJavaProject(Project project) {
-        Jar sourceJar = project.getTasks().create("sourceJar", Jar.class);
-        sourceJar.setPreserveFileTimestamps(false);
-        sourceJar.setClassifier("sources");
-        JavaPluginConvention convention =
-                project.getConvention().getPlugin(JavaPluginConvention.class);
-        sourceJar.from(convention.getSourceSets().findByName("main").getAllSource().getSrcDirs());
-        project.getArtifacts().add("archives", sourceJar);
-    }
-}
diff --git a/android/support/animation/AnimationHandler.java b/android/support/animation/AnimationHandler.java
deleted file mode 100644
index 24bc43a..0000000
--- a/android/support/animation/AnimationHandler.java
+++ /dev/null
@@ -1,257 +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.animation;
-
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.support.annotation.RequiresApi;
-import android.support.v4.util.SimpleArrayMap;
-import android.view.Choreographer;
-
-import java.util.ArrayList;
-
-/**
- * This custom, static handler handles the timing pulse that is shared by all active
- * ValueAnimators. This approach ensures that the setting of animation values will happen on the
- * same thread that animations start on, and that all animations will share the same times for
- * calculating their values, which makes synchronizing animations possible.
- *
- * The handler uses the Choreographer by default for doing periodic callbacks. A custom
- * AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
- * may be independent of UI frame update. This could be useful in testing.
- */
-class AnimationHandler {
-    /**
-     * Callbacks that receives notifications for animation timing
-     */
-    interface AnimationFrameCallback {
-        /**
-         * Run animation based on the frame time.
-         * @param frameTime The frame start time
-         */
-        boolean doAnimationFrame(long frameTime);
-    }
-
-    /**
-     * This class is responsible for interacting with the available frame provider by either
-     * registering frame callback or posting runnable, and receiving a callback for when a
-     * new frame has arrived. This dispatcher class then notifies all the on-going animations of
-     * the new frame, so that they can update animation values as needed.
-     */
-    class AnimationCallbackDispatcher {
-        void dispatchAnimationFrame() {
-            mCurrentFrameTime = SystemClock.uptimeMillis();
-            AnimationHandler.this.doAnimationFrame(mCurrentFrameTime);
-            if (mAnimationCallbacks.size() > 0) {
-                getProvider().postFrameCallback();
-            }
-        }
-    }
-
-    private static final long FRAME_DELAY_MS = 10;
-    public static final ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
-
-    /**
-     * Internal per-thread collections used to avoid set collisions as animations start and end
-     * while being processed.
-     */
-    private final SimpleArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
-            new SimpleArrayMap<>();
-    private final ArrayList<AnimationFrameCallback> mAnimationCallbacks = new ArrayList<>();
-    private final AnimationCallbackDispatcher mCallbackDispatcher =
-            new AnimationCallbackDispatcher();
-
-    private AnimationFrameCallbackProvider mProvider;
-    private long mCurrentFrameTime = 0;
-    private boolean mListDirty = false;
-
-    public static AnimationHandler getInstance() {
-        if (sAnimatorHandler.get() == null) {
-            sAnimatorHandler.set(new AnimationHandler());
-        }
-        return sAnimatorHandler.get();
-    }
-
-    public static long getFrameTime() {
-        if (sAnimatorHandler.get() == null) {
-            return 0;
-        }
-        return sAnimatorHandler.get().mCurrentFrameTime;
-    }
-
-    /**
-     * By default, the Choreographer is used to provide timing for frame callbacks. A custom
-     * provider can be used here to provide different timing pulse.
-     */
-    public void setProvider(AnimationFrameCallbackProvider provider) {
-        mProvider = provider;
-    }
-
-    private AnimationFrameCallbackProvider getProvider() {
-        if (mProvider == null) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-                mProvider = new FrameCallbackProvider16(mCallbackDispatcher);
-            } else {
-                mProvider = new FrameCallbackProvider14(mCallbackDispatcher);
-            }
-        }
-        return mProvider;
-    }
-
-    /**
-     * Register to get a callback on the next frame after the delay.
-     */
-    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
-        if (mAnimationCallbacks.size() == 0) {
-            getProvider().postFrameCallback();
-        }
-        if (!mAnimationCallbacks.contains(callback)) {
-            mAnimationCallbacks.add(callback);
-        }
-
-        if (delay > 0) {
-            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
-        }
-    }
-    /**
-     * Removes the given callback from the list, so it will no longer be called for frame related
-     * timing.
-     */
-    public void removeCallback(AnimationFrameCallback callback) {
-        mDelayedCallbackStartTime.remove(callback);
-        int id = mAnimationCallbacks.indexOf(callback);
-        if (id >= 0) {
-            mAnimationCallbacks.set(id, null);
-            mListDirty = true;
-        }
-    }
-
-    private void doAnimationFrame(long frameTime) {
-        long currentTime = SystemClock.uptimeMillis();
-        for (int i = 0; i < mAnimationCallbacks.size(); i++) {
-            final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
-            if (callback == null) {
-                continue;
-            }
-            if (isCallbackDue(callback, currentTime)) {
-                callback.doAnimationFrame(frameTime);
-            }
-        }
-        cleanUpList();
-    }
-
-    /**
-     * Remove the callbacks from mDelayedCallbackStartTime once they have passed the initial delay
-     * so that they can start getting frame callbacks.
-     *
-     * @return true if they have passed the initial delay or have no delay, false otherwise.
-     */
-    private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) {
-        Long startTime = mDelayedCallbackStartTime.get(callback);
-        if (startTime == null) {
-            return true;
-        }
-        if (startTime < currentTime) {
-            mDelayedCallbackStartTime.remove(callback);
-            return true;
-        }
-        return false;
-    }
-
-    private void cleanUpList() {
-        if (mListDirty) {
-            for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
-                if (mAnimationCallbacks.get(i) == null) {
-                    mAnimationCallbacks.remove(i);
-                }
-            }
-            mListDirty = false;
-        }
-    }
-
-    /**
-     * Default provider of timing pulse that uses Choreographer for frame callbacks.
-     */
-    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
-    private static class FrameCallbackProvider16 extends AnimationFrameCallbackProvider {
-
-        private final Choreographer mChoreographer = Choreographer.getInstance();
-        private final Choreographer.FrameCallback mChoreographerCallback;
-
-        FrameCallbackProvider16(AnimationCallbackDispatcher dispatcher) {
-            super(dispatcher);
-            mChoreographerCallback = new Choreographer.FrameCallback() {
-                    @Override
-                    public void doFrame(long frameTimeNanos) {
-                        mDispatcher.dispatchAnimationFrame();
-                    }
-                };
-        }
-
-        @Override
-        void postFrameCallback() {
-            mChoreographer.postFrameCallback(mChoreographerCallback);
-        }
-    }
-
-    /**
-     * Frame provider for ICS and ICS-MR1 releases. The frame callback is achieved via posting
-     * a Runnable to the main thread Handler with a delay.
-     */
-    private static class FrameCallbackProvider14 extends AnimationFrameCallbackProvider {
-
-        private final Runnable mRunnable;
-        private final Handler mHandler;
-        private long mLastFrameTime = -1;
-
-        FrameCallbackProvider14(AnimationCallbackDispatcher dispatcher) {
-            super(dispatcher);
-            mRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    mLastFrameTime = SystemClock.uptimeMillis();
-                    mDispatcher.dispatchAnimationFrame();
-                }
-            };
-            mHandler = new Handler(Looper.myLooper());
-        }
-
-        @Override
-        void postFrameCallback() {
-            long delay = FRAME_DELAY_MS - (SystemClock.uptimeMillis() - mLastFrameTime);
-            delay = Math.max(delay, 0);
-            mHandler.postDelayed(mRunnable, delay);
-        }
-    }
-
-    /**
-     * The intention for having this interface is to increase the testability of ValueAnimator.
-     * Specifically, we can have a custom implementation of the interface below and provide
-     * timing pulse without using Choreographer. That way we could use any arbitrary interval for
-     * our timing pulse in the tests.
-     */
-    abstract static class AnimationFrameCallbackProvider {
-        final AnimationCallbackDispatcher mDispatcher;
-        AnimationFrameCallbackProvider(AnimationCallbackDispatcher dispatcher) {
-            mDispatcher = dispatcher;
-        }
-
-        abstract void postFrameCallback();
-    }
-}
diff --git a/android/support/animation/DynamicAnimation.java b/android/support/animation/DynamicAnimation.java
deleted file mode 100644
index 7cbd5bb..0000000
--- a/android/support/animation/DynamicAnimation.java
+++ /dev/null
@@ -1,762 +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.animation;
-
-import android.os.Looper;
-import android.support.annotation.FloatRange;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.util.AndroidRuntimeException;
-import android.view.View;
-
-import java.util.ArrayList;
-
-/**
- * This class is the base class of physics-based animations. It manages the animation's
- * lifecycle such as {@link #start()} and {@link #cancel()}. This base class also handles the common
- * setup for all the subclass animations. For example, DynamicAnimation supports adding
- * {@link OnAnimationEndListener} and {@link OnAnimationUpdateListener} so that the important
- * animation events can be observed through the callbacks. The start conditions for any subclass of
- * DynamicAnimation can be set using {@link #setStartValue(float)} and
- * {@link #setStartVelocity(float)}.
- *
- * @param <T> subclass of DynamicAnimation
- */
-public abstract class DynamicAnimation<T extends DynamicAnimation<T>>
-        implements AnimationHandler.AnimationFrameCallback {
-
-    /**
-     * ViewProperty holds the access of a property of a {@link View}. When an animation is
-     * created with a {@link ViewProperty} instance, the corresponding property value of the view
-     * will be updated through this ViewProperty instance.
-     */
-    public abstract static class ViewProperty extends FloatPropertyCompat<View> {
-        private ViewProperty(String name) {
-            super(name);
-        }
-    }
-
-    /**
-     * View's translationX property.
-     */
-    public static final ViewProperty TRANSLATION_X = new ViewProperty("translationX") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setTranslationX(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getTranslationX();
-        }
-    };
-
-    /**
-     * View's translationY property.
-     */
-    public static final ViewProperty TRANSLATION_Y = new ViewProperty("translationY") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setTranslationY(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getTranslationY();
-        }
-    };
-
-    /**
-     * View's translationZ property.
-     */
-    public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") {
-        @Override
-        public void setValue(View view, float value) {
-            ViewCompat.setTranslationZ(view, value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return ViewCompat.getTranslationZ(view);
-        }
-    };
-
-    /**
-     * View's scaleX property.
-     */
-    public static final ViewProperty SCALE_X = new ViewProperty("scaleX") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setScaleX(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getScaleX();
-        }
-    };
-
-    /**
-     * View's scaleY property.
-     */
-    public static final ViewProperty SCALE_Y = new ViewProperty("scaleY") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setScaleY(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getScaleY();
-        }
-    };
-
-    /**
-     * View's rotation property.
-     */
-    public static final ViewProperty ROTATION = new ViewProperty("rotation") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setRotation(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getRotation();
-        }
-    };
-
-    /**
-     * View's rotationX property.
-     */
-    public static final ViewProperty ROTATION_X = new ViewProperty("rotationX") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setRotationX(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getRotationX();
-        }
-    };
-
-    /**
-     * View's rotationY property.
-     */
-    public static final ViewProperty ROTATION_Y = new ViewProperty("rotationY") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setRotationY(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getRotationY();
-        }
-    };
-
-    /**
-     * View's x property.
-     */
-    public static final ViewProperty X = new ViewProperty("x") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setX(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getX();
-        }
-    };
-
-    /**
-     * View's y property.
-     */
-    public static final ViewProperty Y = new ViewProperty("y") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setY(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getY();
-        }
-    };
-
-    /**
-     * View's z property.
-     */
-    public static final ViewProperty Z = new ViewProperty("z") {
-        @Override
-        public void setValue(View view, float value) {
-            ViewCompat.setZ(view, value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return ViewCompat.getZ(view);
-        }
-    };
-
-    /**
-     * View's alpha property.
-     */
-    public static final ViewProperty ALPHA = new ViewProperty("alpha") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setAlpha(value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getAlpha();
-        }
-    };
-
-    // Properties below are not RenderThread compatible
-    /**
-     * View's scrollX property.
-     */
-    public static final ViewProperty SCROLL_X = new ViewProperty("scrollX") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setScrollX((int) value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getScrollX();
-        }
-    };
-
-    /**
-     * View's scrollY property.
-     */
-    public static final ViewProperty SCROLL_Y = new ViewProperty("scrollY") {
-        @Override
-        public void setValue(View view, float value) {
-            view.setScrollY((int) value);
-        }
-
-        @Override
-        public float getValue(View view) {
-            return view.getScrollY();
-        }
-    };
-
-    /**
-     * The minimum visible change in pixels that can be visible to users.
-     */
-    public static final float MIN_VISIBLE_CHANGE_PIXELS = 1f;
-    /**
-     * The minimum visible change in degrees that can be visible to users.
-     */
-    public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 1f / 10f;
-    /**
-     * The minimum visible change in alpha that can be visible to users.
-     */
-    public static final float MIN_VISIBLE_CHANGE_ALPHA = 1f / 256f;
-    /**
-     * The minimum visible change in scale that can be visible to users.
-     */
-    public static final float MIN_VISIBLE_CHANGE_SCALE = 1f / 500f;
-
-    // Use the max value of float to indicate an unset state.
-    private static final float UNSET = Float.MAX_VALUE;
-
-    // Multiplier to the min visible change value for value threshold
-    private static final float THRESHOLD_MULTIPLIER = 0.75f;
-
-    // Internal tracking for velocity.
-    float mVelocity = 0;
-
-    // Internal tracking for value.
-    float mValue = UNSET;
-
-    // Tracks whether start value is set. If not, the animation will obtain the value at the time
-    // of starting through the getter and use that as the starting value of the animation.
-    boolean mStartValueIsSet = false;
-
-    // Target to be animated.
-    final Object mTarget;
-
-    // View property id.
-    final FloatPropertyCompat mProperty;
-
-    // Package private tracking of animation lifecycle state. Visible to subclass animations.
-    boolean mRunning = false;
-
-    // Min and max values that defines the range of the animation values.
-    float mMaxValue = Float.MAX_VALUE;
-    float mMinValue = -mMaxValue;
-
-    // Last frame time. Always gets reset to -1  at the end of the animation.
-    private long mLastFrameTime = 0;
-
-    private float mMinVisibleChange;
-
-    // List of end listeners
-    private final ArrayList<OnAnimationEndListener> mEndListeners = new ArrayList<>();
-
-    // List of update listeners
-    private final ArrayList<OnAnimationUpdateListener> mUpdateListeners = new ArrayList<>();
-
-    // Internal state for value/velocity pair.
-    static class MassState {
-        float mValue;
-        float mVelocity;
-    }
-
-    /**
-     * Creates a dynamic animation with the given FloatValueHolder instance.
-     *
-     * @param floatValueHolder the FloatValueHolder instance to be animated.
-     */
-    DynamicAnimation(final FloatValueHolder floatValueHolder) {
-        mTarget = null;
-        mProperty = new FloatPropertyCompat("FloatValueHolder") {
-            @Override
-            public float getValue(Object object) {
-                return floatValueHolder.getValue();
-            }
-
-            @Override
-            public void setValue(Object object, float value) {
-                floatValueHolder.setValue(value);
-            }
-        };
-        mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS;
-    }
-
-    /**
-     * Creates a dynamic animation to animate the given property for the given {@link View}
-     *
-     * @param object the Object whose property is to be animated
-     * @param property the property to be animated
-     */
-
-    <K> DynamicAnimation(K object, FloatPropertyCompat<K> property) {
-        mTarget = object;
-        mProperty = property;
-        if (mProperty == ROTATION || mProperty == ROTATION_X
-                || mProperty == ROTATION_Y) {
-            mMinVisibleChange = MIN_VISIBLE_CHANGE_ROTATION_DEGREES;
-        } else if (mProperty == ALPHA) {
-            mMinVisibleChange = MIN_VISIBLE_CHANGE_ALPHA;
-        } else if (mProperty == SCALE_X || mProperty == SCALE_Y) {
-            mMinVisibleChange = MIN_VISIBLE_CHANGE_ALPHA;
-        } else {
-            mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS;
-        }
-    }
-
-    /**
-     * Sets the start value of the animation. If start value is not set, the animation will get
-     * the current value for the view's property, and use that as the start value.
-     *
-     * @param startValue start value for the animation
-     * @return the Animation whose start value is being set
-     */
-    public T setStartValue(float startValue) {
-        mValue = startValue;
-        mStartValueIsSet = true;
-        return (T) this;
-    }
-
-    /**
-     * 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
-     * to pixel/second based on the density of the screen to achieve a consistent look across
-     * different screens.
-     *
-     * <p>To convert from dp/second to pixel/second:
-     * <pre class="prettyprint">
-     * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
-     *         getResources().getDisplayMetrics());
-     * </pre>
-     *
-     * @param startVelocity start velocity of the animation
-     * @return the Animation whose start velocity is being set
-     */
-    public T setStartVelocity(float startVelocity) {
-        mVelocity = startVelocity;
-        return (T) this;
-    }
-
-    /**
-     * Sets the max value of the animation. Animations will not animate beyond their max value.
-     * Whether or not animation will come to an end when max value is reached is dependent on the
-     * child animation's implementation.
-     *
-     * @param max maximum value of the property to be animated
-     * @return the Animation whose max value is being set
-     */
-    public T setMaxValue(float max) {
-        // This max value should be checked and handled in the subclass animations, instead of
-        // assuming the end of the animations when the max/min value is hit in the base class.
-        // The reason is that hitting max/min value may just be a transient state, such as during
-        // the spring oscillation.
-        mMaxValue = max;
-        return (T) this;
-    }
-
-    /**
-     * Sets the min value of the animation. Animations will not animate beyond their min value.
-     * Whether or not animation will come to an end when min value is reached is dependent on the
-     * child animation's implementation.
-     *
-     * @param min minimum value of the property to be animated
-     * @return the Animation whose min value is being set
-     */
-    public T setMinValue(float min) {
-        mMinValue = min;
-        return (T) this;
-    }
-
-    /**
-     * Adds an end listener to the animation for receiving onAnimationEnd callbacks. If the listener
-     * is {@code null} or has already been added to the list of listeners for the animation, no op.
-     *
-     * @param listener the listener to be added
-     * @return the animation to which the listener is added
-     */
-    public T addEndListener(OnAnimationEndListener listener) {
-        if (!mEndListeners.contains(listener)) {
-            mEndListeners.add(listener);
-        }
-        return (T) this;
-    }
-
-    /**
-     * Removes the end listener from the animation, so as to stop receiving animation end callbacks.
-     *
-     * @param listener the listener to be removed
-     */
-    public void removeEndListener(OnAnimationEndListener listener) {
-        removeEntry(mEndListeners, listener);
-    }
-
-    /**
-     * Adds an update listener to the animation for receiving per-frame animation update callbacks.
-     * If the listener is {@code null} or has already been added to the list of listeners for the
-     * animation, no op.
-     *
-     * <p>Note that update listener should only be added before the start of the animation.
-     *
-     * @param listener the listener to be added
-     * @return the animation to which the listener is added
-     * @throws UnsupportedOperationException if the update listener is added after the animation has
-     *                                       started
-     */
-    public T addUpdateListener(OnAnimationUpdateListener listener) {
-        if (isRunning()) {
-            // Require update listener to be added before the animation, such as when we start
-            // the animation, we know whether the animation is RenderThread compatible.
-            throw new UnsupportedOperationException("Error: Update listeners must be added before"
-                    + "the animation.");
-        }
-        if (!mUpdateListeners.contains(listener)) {
-            mUpdateListeners.add(listener);
-        }
-        return (T) this;
-    }
-
-    /**
-     * Removes the update listener from the animation, so as to stop receiving animation update
-     * callbacks.
-     *
-     * @param listener the listener to be removed
-     */
-    public void removeUpdateListener(OnAnimationUpdateListener listener) {
-        removeEntry(mUpdateListeners, listener);
-    }
-
-
-    /**
-     * This method sets the minimal change of animation value that is visible to users, which helps
-     * determine a reasonable threshold for the animation's termination condition. It is critical
-     * to set the minimal visible change for custom properties (i.e. non-<code>ViewProperty</code>s)
-     * unless the custom property is in pixels.
-     *
-     * <p>For custom properties, this minimum visible change defaults to change in pixel
-     * (i.e. {@link #MIN_VISIBLE_CHANGE_PIXELS}. It is recommended to adjust this value that is
-     * reasonable for the property to be animated. A general rule of thumb to calculate such a value
-     * is: minimum visible change = range of custom property value / corresponding pixel range. For
-     * example, if the property to be animated is a progress (from 0 to 100) that corresponds to a
-     * 200-pixel change. Then the min visible change should be 100 / 200. (i.e. 0.5).
-     *
-     * <p>It's not necessary to call this method when animating {@link ViewProperty}s, as the
-     * minimum visible change will be derived from the property. For example, if the property to be
-     * animated is in pixels (i.e. {@link #TRANSLATION_X}, {@link #TRANSLATION_Y},
-     * {@link #TRANSLATION_Z}, @{@link #SCROLL_X} or {@link #SCROLL_Y}), the default minimum visible
-     * change is 1 (pixel). For {@link #ROTATION}, {@link #ROTATION_X} or {@link #ROTATION_Y}, the
-     * animation will use {@link #MIN_VISIBLE_CHANGE_ROTATION_DEGREES} as the min visible change,
-     * which is 1/10. Similarly, the minimum visible change for alpha (
-     * i.e. {@link #MIN_VISIBLE_CHANGE_ALPHA} is defined as 1 / 256.
-     *
-     * @param minimumVisibleChange minimum change in property value that is visible to users
-     * @return the animation whose min visible change is being set
-     * @throws IllegalArgumentException if the given threshold is not positive
-     */
-    public T setMinimumVisibleChange(@FloatRange(from = 0.0, fromInclusive = false)
-            float minimumVisibleChange) {
-        if (minimumVisibleChange <= 0) {
-            throw new IllegalArgumentException("Minimum visible change must be positive.");
-        }
-        mMinVisibleChange = minimumVisibleChange;
-        setValueThreshold(minimumVisibleChange * THRESHOLD_MULTIPLIER);
-        return (T) this;
-    }
-
-    /**
-     * Returns the minimum change in the animation property that could be visibly different to
-     * users.
-     *
-     * @return minimum change in property value that is visible to users
-     */
-    public float getMinimumVisibleChange() {
-        return mMinVisibleChange;
-    }
-
-    /**
-     * Remove {@code null} entries from the list.
-     */
-    private static <T> void removeNullEntries(ArrayList<T> list) {
-        // Clean up null entries
-        for (int i = list.size() - 1; i >= 0; i--) {
-            if (list.get(i) == null) {
-                list.remove(i);
-            }
-        }
-    }
-
-    /**
-     * Remove an entry from the list by marking it {@code null} and clean up later.
-     */
-    private static <T> void removeEntry(ArrayList<T> list, T entry) {
-        int id = list.indexOf(entry);
-        if (id >= 0) {
-            list.set(id, null);
-        }
-    }
-
-    /****************Animation Lifecycle Management***************/
-
-    /**
-     * Starts an animation. If the animation has already been started, no op. Note that calling
-     * {@link #start()} will not immediately set the property value to start value of the animation.
-     * The property values will be changed at each animation pulse, which happens before the draw
-     * pass. As a result, the changes will be reflected in the next frame, the same as if the values
-     * were set immediately. This method should only be called on main thread.
-     *
-     * @throws AndroidRuntimeException if this method is not called on the main thread
-     */
-    public void start() {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new AndroidRuntimeException("Animations may only be started on the main thread");
-        }
-        if (!mRunning) {
-            startAnimationInternal();
-        }
-    }
-
-    /**
-     * Cancels the on-going animation. If the animation hasn't started, no op. Note that this method
-     * should only be called on main thread.
-     *
-     * @throws AndroidRuntimeException if this method is not called on the main thread
-     */
-    public void cancel() {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new AndroidRuntimeException("Animations may only be canceled on the main thread");
-        }
-        if (mRunning) {
-            endAnimationInternal(true);
-        }
-    }
-
-    /**
-     * Returns whether the animation is currently running.
-     *
-     * @return {@code true} if the animation is currently running, {@code false} otherwise
-     */
-    public boolean isRunning() {
-        return mRunning;
-    }
-
-    /************************** Private APIs below ********************************/
-
-    // This gets called when the animation is started, to finish the setup of the animation
-    // before the animation pulsing starts.
-    private void startAnimationInternal() {
-        if (!mRunning) {
-            mRunning = true;
-            if (!mStartValueIsSet) {
-                mValue = getPropertyValue();
-            }
-            // Sanity check:
-            if (mValue > mMaxValue || mValue < mMinValue) {
-                throw new IllegalArgumentException("Starting value need to be in between min"
-                        + " value and max value");
-            }
-            AnimationHandler.getInstance().addAnimationFrameCallback(this, 0);
-        }
-    }
-
-    /**
-     * This gets call on each frame of the animation. Animation value and velocity are updated
-     * in this method based on the new frame time. The property value of the view being animated
-     * is then updated. The animation's ending conditions are also checked in this method. Once
-     * the animation reaches equilibrium, the animation will come to its end, and end listeners
-     * will be notified, if any.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Override
-    public boolean doAnimationFrame(long frameTime) {
-        if (mLastFrameTime == 0) {
-            // First frame.
-            mLastFrameTime = frameTime;
-            setPropertyValue(mValue);
-            return false;
-        }
-        long deltaT = frameTime - mLastFrameTime;
-        mLastFrameTime = frameTime;
-        boolean finished = updateValueAndVelocity(deltaT);
-        // Clamp value & velocity.
-        mValue = Math.min(mValue, mMaxValue);
-        mValue = Math.max(mValue, mMinValue);
-
-        setPropertyValue(mValue);
-
-        if (finished) {
-            endAnimationInternal(false);
-        }
-        return finished;
-    }
-
-    /**
-     * Updates the animation state (i.e. value and velocity). This method is package private, so
-     * subclasses can override this method to calculate the new value and velocity in their custom
-     * way.
-     *
-     * @param deltaT time elapsed in millisecond since last frame
-     * @return whether the animation has finished
-     */
-    abstract boolean updateValueAndVelocity(long deltaT);
-
-    /**
-     * Internal method to reset the animation states when animation is finished/canceled.
-     */
-    private void endAnimationInternal(boolean canceled) {
-        mRunning = false;
-        AnimationHandler.getInstance().removeCallback(this);
-        mLastFrameTime = 0;
-        mStartValueIsSet = false;
-        for (int i = 0; i < mEndListeners.size(); i++) {
-            if (mEndListeners.get(i) != null) {
-                mEndListeners.get(i).onAnimationEnd(this, canceled, mValue, mVelocity);
-            }
-        }
-        removeNullEntries(mEndListeners);
-    }
-
-    /**
-     * Updates the property value through the corresponding setter.
-     */
-    void setPropertyValue(float value) {
-        mProperty.setValue(mTarget, value);
-        for (int i = 0; i < mUpdateListeners.size(); i++) {
-            if (mUpdateListeners.get(i) != null) {
-                mUpdateListeners.get(i).onAnimationUpdate(this, mValue, mVelocity);
-            }
-        }
-        removeNullEntries(mUpdateListeners);
-    }
-
-    /**
-     * Returns the default threshold.
-     */
-    float getValueThreshold() {
-        return mMinVisibleChange * THRESHOLD_MULTIPLIER;
-    }
-
-    /**
-     * Obtain the property value through the corresponding getter.
-     */
-    private float getPropertyValue() {
-        return mProperty.getValue(mTarget);
-    }
-
-    /****************Sub class animations**************/
-    /**
-     * Returns the acceleration at the given value with the given velocity.
-     **/
-    abstract float getAcceleration(float value, float velocity);
-
-    /**
-     * Returns whether the animation has reached equilibrium.
-     */
-    abstract boolean isAtEquilibrium(float value, float velocity);
-
-    /**
-     * Updates the default value threshold for the animation based on the property to be animated.
-     */
-    abstract void setValueThreshold(float threshold);
-
-    /**
-     * An animation listener that receives end notifications from an animation.
-     */
-    public interface OnAnimationEndListener {
-        /**
-         * Notifies the end of an animation. Note that this callback will be invoked not only when
-         * an animation reach equilibrium, but also when the animation is canceled.
-         *
-         * @param animation animation that has ended or was canceled
-         * @param canceled whether the animation has been canceled
-         * @param value the final value when the animation stopped
-         * @param velocity the final velocity when the animation stopped
-         */
-        void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
-                              float velocity);
-    }
-
-    /**
-     * Implementors of this interface can add themselves as update listeners
-     * to an <code>DynamicAnimation</code> instance to receive callbacks on every animation
-     * frame, after the current frame's values have been calculated for that
-     * <code>DynamicAnimation</code>.
-     */
-    public interface OnAnimationUpdateListener {
-
-        /**
-         * Notifies the occurrence of another frame of the animation.
-         *
-         * @param animation animation that the update listener is added to
-         * @param value the current value of the animation
-         * @param velocity the current velocity of the animation
-         */
-        void onAnimationUpdate(DynamicAnimation animation, float value, float velocity);
-    }
-}
diff --git a/android/support/animation/FlingAnimation.java b/android/support/animation/FlingAnimation.java
deleted file mode 100644
index f8d19a2..0000000
--- a/android/support/animation/FlingAnimation.java
+++ /dev/null
@@ -1,241 +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.animation;
-
-import android.support.annotation.FloatRange;
-
-/**
- * <p>Fling animation is an animation that continues an initial momentum (most often from gesture
- * velocity) and gradually slows down. The fling animation will come to a stop when the velocity of
- * the animation is below the threshold derived from {@link #setMinimumVisibleChange(float)},
- * or when the value of the animation has gone beyond the min or max value defined via
- * {@link DynamicAnimation#setMinValue(float)} or {@link DynamicAnimation#setMaxValue(float)}.
- * It is recommended to restrict the fling animation with min and/or max value, such that the
- * animation can end when it goes beyond screen bounds, thus preserving CPU cycles and resources.
- *
- * <p>For example, you can create a fling animation that animates the translationX of a view:
- * <pre class="prettyprint">
- * FlingAnimation flingAnim = new FlingAnimation(view, DynamicAnimation.TRANSLATION_X)
- *         // Sets the start velocity to -2000 (pixel/s)
- *         .setStartVelocity(-2000)
- *         // Optional but recommended to set a reasonable min and max range for the animation.
- *         // In this particular case, we set the min and max to -200 and 2000 respectively.
- *         .setMinValue(-200).setMaxValue(2000);
- * flingAnim.start();
- * </pre>
- */
-public final class FlingAnimation extends DynamicAnimation<FlingAnimation> {
-
-    private final DragForce mFlingForce = new DragForce();
-
-    /**
-     * <p>This creates a FlingAnimation that animates a {@link FloatValueHolder} instance. During
-     * the animation, the {@link FloatValueHolder} instance will be updated via
-     * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date
-     * animation value via {@link FloatValueHolder#getValue()}.
-     *
-     * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via
-     * {@link FloatValueHolder#setValue(float)} outside of the animation during an
-     * animation run will not have any effect on the on-going animation.
-     *
-     * @param floatValueHolder the property to be animated
-     */
-    public FlingAnimation(FloatValueHolder floatValueHolder) {
-        super(floatValueHolder);
-        mFlingForce.setValueThreshold(getValueThreshold());
-    }
-
-    /**
-     * This creates a FlingAnimation that animates the property of the given object.
-     *
-     * @param object the Object whose property will be animated
-     * @param property the property to be animated
-     * @param <K> the class on which the property is declared
-     */
-    public <K> FlingAnimation(K object, FloatPropertyCompat<K> property) {
-        super(object, property);
-        mFlingForce.setValueThreshold(getValueThreshold());
-    }
-
-    /**
-     * Sets the friction for the fling animation. The greater the friction is, the sooner the
-     * animation will slow down. When not set, the friction defaults to 1.
-     *
-     * @param friction the friction used in the animation
-     * @return the animation whose friction will be scaled
-     * @throws IllegalArgumentException if the input friction is not positive
-     */
-    public FlingAnimation setFriction(
-            @FloatRange(from = 0.0, fromInclusive = false) float friction) {
-        if (friction <= 0) {
-            throw new IllegalArgumentException("Friction must be positive");
-        }
-        mFlingForce.setFrictionScalar(friction);
-        return this;
-    }
-
-    /**
-     * Returns the friction being set on the animation via {@link #setFriction(float)}. If the
-     * friction has not been set, the default friction of 1 will be returned.
-     *
-     * @return friction being used in the animation
-     */
-    public float getFriction() {
-        return mFlingForce.getFrictionScalar();
-    }
-
-    /**
-     * Sets the min value of the animation. When a fling animation reaches the min value, the
-     * animation will end immediately. Animations will not animate beyond the min value.
-     *
-     * @param minValue minimum value of the property to be animated
-     * @return the Animation whose min value is being set
-     */
-    @Override
-    public FlingAnimation setMinValue(float minValue) {
-        super.setMinValue(minValue);
-        return this;
-    }
-
-    /**
-     * Sets the max value of the animation. When a fling animation reaches the max value, the
-     * animation will end immediately. Animations will not animate beyond the max value.
-     *
-     * @param maxValue maximum value of the property to be animated
-     * @return the Animation whose max value is being set
-     */
-    @Override
-    public FlingAnimation setMaxValue(float maxValue) {
-        super.setMaxValue(maxValue);
-        return this;
-    }
-
-    /**
-     * Start velocity of the animation. Default velocity is 0. Unit: pixel/second
-     *
-     * <p>A <b>non-zero</b> start velocity is required for a FlingAnimation. If no start velocity is
-     * set through {@link #setStartVelocity(float)}, the start velocity defaults to 0. In that
-     * case, the fling animation will consider itself done in the next frame.
-     *
-     * <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
-     * to pixel/second based on the density of the screen to achieve a consistent look across
-     * different screens.
-     *
-     * <p>To convert from dp/second to pixel/second:
-     * <pre class="prettyprint">
-     * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
-     *         getResources().getDisplayMetrics());
-     * </pre>
-     *
-     * @param startVelocity start velocity of the animation in pixel/second
-     * @return the Animation whose start velocity is being set
-     */
-    @Override
-    public FlingAnimation setStartVelocity(float startVelocity) {
-        super.setStartVelocity(startVelocity);
-        return this;
-    }
-
-    @Override
-    boolean updateValueAndVelocity(long deltaT) {
-
-        MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT);
-        mValue = state.mValue;
-        mVelocity = state.mVelocity;
-
-        // When the animation hits the max/min value, consider animation done.
-        if (mValue < mMinValue) {
-            mValue = mMinValue;
-            return true;
-        }
-        if (mValue > mMaxValue) {
-            mValue = mMaxValue;
-            return true;
-        }
-
-        if (isAtEquilibrium(mValue, mVelocity)) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    float getAcceleration(float value, float velocity) {
-        return mFlingForce.getAcceleration(value, velocity);
-    }
-
-    @Override
-    boolean isAtEquilibrium(float value, float velocity) {
-        return value >= mMaxValue
-                || value <= mMinValue
-                || mFlingForce.isAtEquilibrium(value, velocity);
-    }
-
-    @Override
-    void setValueThreshold(float threshold) {
-        mFlingForce.setValueThreshold(threshold);
-    }
-
-    private static final class DragForce implements Force {
-
-        private static final float DEFAULT_FRICTION = -4.2f;
-
-        // This multiplier is used to calculate the velocity threshold given a certain value
-        // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
-        // then the velocity is a reasonable threshold.
-        private static final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f;
-        private float mFriction = DEFAULT_FRICTION;
-        private float mVelocityThreshold;
-
-        // Internal state to hold a value/velocity pair.
-        private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState();
-
-        void setFrictionScalar(float frictionScalar) {
-            mFriction = frictionScalar * DEFAULT_FRICTION;
-        }
-
-        float getFrictionScalar() {
-            return mFriction / DEFAULT_FRICTION;
-        }
-
-        MassState updateValueAndVelocity(float value, float velocity, long deltaT) {
-            mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction));
-            mMassState.mValue = (float) (value - velocity / mFriction
-                    + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f));
-            if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) {
-                mMassState.mVelocity = 0f;
-            }
-            return mMassState;
-        }
-
-        @Override
-        public float getAcceleration(float position, float velocity) {
-            return velocity * mFriction;
-        }
-
-        @Override
-        public boolean isAtEquilibrium(float value, float velocity) {
-            return Math.abs(velocity) < mVelocityThreshold;
-        }
-
-        void setValueThreshold(float threshold) {
-            mVelocityThreshold = threshold * VELOCITY_THRESHOLD_MULTIPLIER;
-        }
-    }
-
-}
diff --git a/android/support/animation/FloatPropertyCompat.java b/android/support/animation/FloatPropertyCompat.java
deleted file mode 100644
index ec8d0ca..0000000
--- a/android/support/animation/FloatPropertyCompat.java
+++ /dev/null
@@ -1,85 +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.animation;
-
-import android.support.annotation.RequiresApi;
-import android.util.FloatProperty;
-
-/**
- * <p>FloatPropertyCompat is an abstraction that can be used to represent a mutable float value that
- * is held in a host object. To access this float value, {@link #setValue(Object, float)} and getter
- * {@link #getValue(Object)} need to be implemented. Both the setter and the getter take the
- * primitive <code>float</code> type and avoids autoboxing and other overhead associated with the
- * <code>Float</code> class.
- *
- * <p>For API 24 and later, {@link FloatProperty} instances can be converted to
- * {@link FloatPropertyCompat} through
- * {@link FloatPropertyCompat#createFloatPropertyCompat(FloatProperty)}.
- *
- * @param <T> the class on which the Property is declared
- */
-public abstract class FloatPropertyCompat<T> {
-    final String mPropertyName;
-
-    /**
-     * A constructor that takes an identifying name.
-     */
-    public FloatPropertyCompat(String name) {
-        mPropertyName = name;
-    }
-
-    /**
-     * Create a {@link FloatPropertyCompat} wrapper for a {@link FloatProperty} object. The new
-     * {@link FloatPropertyCompat} instance will access and modify the property value of
-     * {@link FloatProperty} through the {@link FloatProperty} instance's setter and getter.
-     *
-     * @param property FloatProperty instance to be wrapped
-     * @param <T> the class on which the Property is declared
-     * @return a new {@link FloatPropertyCompat} wrapper for the given {@link FloatProperty} object
-     */
-    @RequiresApi(24)
-    public static <T> FloatPropertyCompat<T> createFloatPropertyCompat(
-            final FloatProperty<T> property) {
-        return new FloatPropertyCompat<T>(property.getName()) {
-            @Override
-            public float getValue(T object) {
-                return property.get(object);
-            }
-
-            @Override
-            public void setValue(T object, float value) {
-                property.setValue(object, value);
-            }
-        };
-    }
-
-    /**
-     * Returns the current value that this property represents on the given <code>object</code>.
-     *
-     * @param object object which this property represents
-     * @return the current property value of the given object
-     */
-    public abstract float getValue(T object);
-
-    /**
-     * Sets the value on <code>object</code> which this property represents.
-     *
-     * @param object object which this property represents
-     * @param value new value of the property
-     */
-    public abstract void setValue(T object, float value);
-}
diff --git a/android/support/animation/FloatValueHolder.java b/android/support/animation/FloatValueHolder.java
deleted file mode 100644
index 08dcc9c..0000000
--- a/android/support/animation/FloatValueHolder.java
+++ /dev/null
@@ -1,74 +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.animation;
-
-/**
- * <p>FloatValueHolder holds a float value. FloatValueHolder provides a setter and a getter (
- * i.e. {@link #setValue(float)} and {@link #getValue()}) to access this float value. Animations can
- * be performed on a FloatValueHolder instance. During each frame of the animation, the
- * FloatValueHolder will have its value updated via {@link #setValue(float)}. The caller can
- * obtain the up-to-date animation value via {@link FloatValueHolder#getValue()}.
- *
- * <p> Here is an example for creating a {@link FlingAnimation} with a FloatValueHolder:
- * <pre class="prettyprint">
- * // Create a fling animation with an initial velocity of 5000 (pixel/s) and an initial position
- * // of 20f.
- * FloatValueHolder floatValueHolder = new FloatValueHolder(20f);
- * FlingAnimation anim = new FlingAnimation(floatValueHolder).setStartVelocity(5000);
- * anim.start();
- * </pre>
- *
- * @see SpringAnimation#SpringAnimation(FloatValueHolder)
- * @see FlingAnimation#FlingAnimation(FloatValueHolder)
- */
-
-public final class FloatValueHolder {
-    private float mValue = 0.0f;
-
-    /**
-     * Constructs a holder for a float value that is initialized to 0.
-     */
-    public FloatValueHolder() {
-    }
-
-    /**
-     * Constructs a holder for a float value that is initialized to the input value.
-     *
-     * @param value the value to initialize the value held in the FloatValueHolder
-     */
-    public FloatValueHolder(float value) {
-        setValue(value);
-    }
-
-    /**
-     * Sets the value held in the FloatValueHolder instance.
-     *
-     * @param value float value held in the FloatValueHolder instance
-     */
-    public void setValue(float value) {
-        mValue = value;
-    }
-
-    /**
-     * Returns the float value held in the FloatValueHolder instance.
-     *
-     * @return float value held in the FloatValueHolder instance
-     */
-    public float getValue() {
-        return mValue;
-    }
-}
diff --git a/android/support/animation/Force.java b/android/support/animation/Force.java
deleted file mode 100644
index afcf84d..0000000
--- a/android/support/animation/Force.java
+++ /dev/null
@@ -1,27 +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.animation;
-
-/**
- * Hide this for now, in case we want to change the API.
- */
-interface Force {
-    // Acceleration based on position.
-    float getAcceleration(float position, float velocity);
-
-    boolean isAtEquilibrium(float value, float velocity);
-}
diff --git a/android/support/animation/SpringAnimation.java b/android/support/animation/SpringAnimation.java
deleted file mode 100644
index 5a51d95..0000000
--- a/android/support/animation/SpringAnimation.java
+++ /dev/null
@@ -1,268 +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.animation;
-
-import android.os.Looper;
-import android.util.AndroidRuntimeException;
-
-/**
- * SpringAnimation is an animation that is driven by a {@link SpringForce}. The spring force defines
- * the spring's stiffness, damping ratio, as well as the rest position. Once the SpringAnimation is
- * started, on each frame the spring force will update the animation's value and velocity.
- * The animation will continue to run until the spring force reaches equilibrium. If the spring used
- * in the animation is undamped, the animation will never reach equilibrium. Instead, it will
- * oscillate forever.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * </div>
- *
- * <p>To create a simple {@link SpringAnimation} that uses the default {@link SpringForce}:</p>
- * <pre class="prettyprint">
- * // Create an animation to animate view's X property, set the rest position of the
- * // default spring to 0, and start the animation with a starting velocity of 5000 (pixel/s).
- * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.X, 0)
- *         .setStartVelocity(5000);
- * anim.start();
- * </pre>
- *
- * <p>Alternatively, a {@link SpringAnimation} can take a pre-configured {@link SpringForce}, and
- * use that to drive the animation. </p>
- * <pre class="prettyprint">
- * // Create a low stiffness, low bounce spring at position 0.
- * SpringForce spring = new SpringForce(0)
- *         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
- *         .setStiffness(SpringForce.STIFFNESS_LOW);
- * // Create an animation to animate view's scaleY property, and start the animation using
- * // the spring above and a starting value of 0.5. Additionally, constrain the range of value for
- * // the animation to be non-negative, effectively preventing any spring overshoot.
- * final SpringAnimation anim = new SpringAnimation(view, DynamicAnimation.SCALE_Y)
- *         .setMinValue(0).setSpring(spring).setStartValue(1);
- * anim.start();
- * </pre>
- */
-public final class SpringAnimation extends DynamicAnimation<SpringAnimation> {
-
-    private SpringForce mSpring = null;
-    private float mPendingPosition = UNSET;
-    private static final float UNSET = Float.MAX_VALUE;
-    private boolean mEndRequested = false;
-
-    /**
-     * <p>This creates a SpringAnimation that animates a {@link FloatValueHolder} instance. During
-     * the animation, the {@link FloatValueHolder} instance will be updated via
-     * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date
-     * animation value via {@link FloatValueHolder#getValue()}.
-     *
-     * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via
-     * {@link FloatValueHolder#setValue(float)} outside of the animation during an
-     * animation run will not have any effect on the on-going animation.
-     *
-     * @param floatValueHolder the property to be animated
-     */
-    public SpringAnimation(FloatValueHolder floatValueHolder) {
-        super(floatValueHolder);
-    }
-
-    /**
-     * This creates a SpringAnimation that animates the property of the given object.
-     * Note, a spring will need to setup through {@link #setSpring(SpringForce)} before
-     * the animation starts.
-     *
-     * @param object the Object whose property will be animated
-     * @param property the property to be animated
-     * @param <K> the class on which the Property is declared
-     */
-    public <K> SpringAnimation(K object, FloatPropertyCompat<K> property) {
-        super(object, property);
-    }
-
-    /**
-     * This creates a SpringAnimation that animates the property of the given object. A Spring will
-     * be created with the given final position and default stiffness and damping ratio.
-     * This spring can be accessed and reconfigured through {@link #setSpring(SpringForce)}.
-     *
-     * @param object the Object whose property will be animated
-     * @param property the property to be animated
-     * @param finalPosition the final position of the spring to be created.
-     * @param <K> the class on which the Property is declared
-     */
-    public <K> SpringAnimation(K object, FloatPropertyCompat<K> property,
-            float finalPosition) {
-        super(object, property);
-        mSpring = new SpringForce(finalPosition);
-    }
-
-    /**
-     * Returns the spring that the animation uses for animations.
-     *
-     * @return the spring that the animation uses for animations
-     */
-    public SpringForce getSpring() {
-        return mSpring;
-    }
-
-    /**
-     * Uses the given spring as the force that drives this animation. If this spring force has its
-     * parameters re-configured during the animation, the new configuration will be reflected in the
-     * animation immediately.
-     *
-     * @param force a pre-defined spring force that drives the animation
-     * @return the animation that the spring force is set on
-     */
-    public SpringAnimation setSpring(SpringForce force) {
-        mSpring = force;
-        return this;
-    }
-
-    @Override
-    public void start() {
-        sanityCheck();
-        mSpring.setValueThreshold(getValueThreshold());
-        super.start();
-    }
-
-    /**
-     * Updates the final position of the spring.
-     * <p/>
-     * When the animation is running, calling this method would assume the position change of the
-     * spring as a continuous movement since last frame, which yields more accurate results than
-     * changing the spring position directly through {@link SpringForce#setFinalPosition(float)}.
-     * <p/>
-     * If the animation hasn't started, calling this method will change the spring position, and
-     * immediately start the animation.
-     *
-     * @param finalPosition rest position of the spring
-     */
-    public void animateToFinalPosition(float finalPosition) {
-        if (isRunning()) {
-            mPendingPosition = finalPosition;
-        } else {
-            if (mSpring == null) {
-                mSpring = new SpringForce(finalPosition);
-            }
-            mSpring.setFinalPosition(finalPosition);
-            start();
-        }
-    }
-
-    /**
-     * Skips to the end of the animation. If the spring is undamped, an
-     * {@link IllegalStateException} will be thrown, as the animation would never reach to an end.
-     * It is recommended to check {@link #canSkipToEnd()} before calling this method. This method
-     * should only be called on main thread. If animation is not running, no-op.
-     *
-     * @throws IllegalStateException if the spring is undamped (i.e. damping ratio = 0)
-     * @throws AndroidRuntimeException if this method is not called on the main thread
-     */
-    public void skipToEnd() {
-        if (!canSkipToEnd()) {
-            throw new UnsupportedOperationException("Spring animations can only come to an end"
-                    + " when there is damping");
-        }
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new AndroidRuntimeException("Animations may only be started on the main thread");
-        }
-        if (mRunning) {
-            mEndRequested = true;
-        }
-    }
-
-    /**
-     * Queries whether the spring can eventually come to the rest position.
-     *
-     * @return {@code true} if the spring is damped, otherwise {@code false}
-     */
-    public boolean canSkipToEnd() {
-        return mSpring.mDampingRatio > 0;
-    }
-
-    /************************ Below are private APIs *************************/
-
-    private void sanityCheck() {
-        if (mSpring == null) {
-            throw new UnsupportedOperationException("Incomplete SpringAnimation: Either final"
-                    + " position or a spring force needs to be set.");
-        }
-        double finalPosition = mSpring.getFinalPosition();
-        if (finalPosition > mMaxValue) {
-            throw new UnsupportedOperationException("Final position of the spring cannot be greater"
-                    + " than the max value.");
-        } else if (finalPosition < mMinValue) {
-            throw new UnsupportedOperationException("Final position of the spring cannot be less"
-                    + " than the min value.");
-        }
-    }
-
-    @Override
-    boolean updateValueAndVelocity(long deltaT) {
-        // If user had requested end, then update the value and velocity to end state and consider
-        // animation done.
-        if (mEndRequested) {
-            if (mPendingPosition != UNSET) {
-                mSpring.setFinalPosition(mPendingPosition);
-                mPendingPosition = UNSET;
-            }
-            mValue = mSpring.getFinalPosition();
-            mVelocity = 0;
-            mEndRequested = false;
-            return true;
-        }
-
-        if (mPendingPosition != UNSET) {
-            double lastPosition = mSpring.getFinalPosition();
-            // Approximate by considering half of the time spring position stayed at the old
-            // position, half of the time it's at the new position.
-            MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT / 2);
-            mSpring.setFinalPosition(mPendingPosition);
-            mPendingPosition = UNSET;
-
-            massState = mSpring.updateValues(massState.mValue, massState.mVelocity, deltaT / 2);
-            mValue = massState.mValue;
-            mVelocity = massState.mVelocity;
-
-        } else {
-            MassState massState = mSpring.updateValues(mValue, mVelocity, deltaT);
-            mValue = massState.mValue;
-            mVelocity = massState.mVelocity;
-        }
-
-        mValue = Math.max(mValue, mMinValue);
-        mValue = Math.min(mValue, mMaxValue);
-
-        if (isAtEquilibrium(mValue, mVelocity)) {
-            mValue = mSpring.getFinalPosition();
-            mVelocity = 0f;
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    float getAcceleration(float value, float velocity) {
-        return mSpring.getAcceleration(value, velocity);
-    }
-
-    @Override
-    boolean isAtEquilibrium(float value, float velocity) {
-        return mSpring.isAtEquilibrium(value, velocity);
-    }
-
-    @Override
-    void setValueThreshold(float threshold) {
-    }
-}
diff --git a/android/support/animation/SpringForce.java b/android/support/animation/SpringForce.java
deleted file mode 100644
index dfb4c67..0000000
--- a/android/support/animation/SpringForce.java
+++ /dev/null
@@ -1,332 +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.animation;
-
-import android.support.annotation.FloatRange;
-import android.support.annotation.RestrictTo;
-
-/**
- * Spring Force defines the characteristics of the spring being used in the animation.
- * <p>
- * By configuring the stiffness and damping ratio, callers can create a spring with the look and
- * feel suits their use case. Stiffness corresponds to the spring constant. The stiffer the spring
- * is, the harder it is to stretch it, the faster it undergoes dampening.
- * <p>
- * Spring damping ratio describes how oscillations in a system decay after a disturbance.
- * When damping ratio > 1* (i.e. over-damped), the object will quickly return to the rest position
- * without overshooting. If damping ratio equals to 1 (i.e. critically damped), the object will
- * return to equilibrium within the shortest amount of time. When damping ratio is less than 1
- * (i.e. under-damped), the mass tends to overshoot, and return, and overshoot again. Without any
- * damping (i.e. damping ratio = 0), the mass will oscillate forever.
- */
-public final class SpringForce implements Force {
-    /**
-     * Stiffness constant for extremely stiff spring.
-     */
-    public static final float STIFFNESS_HIGH = 10_000f;
-    /**
-     * Stiffness constant for medium stiff spring. This is the default stiffness for spring force.
-     */
-    public static final float STIFFNESS_MEDIUM = 1500f;
-    /**
-     * Stiffness constant for a spring with low stiffness.
-     */
-    public static final float STIFFNESS_LOW = 200f;
-    /**
-     * Stiffness constant for a spring with very low stiffness.
-     */
-    public static final float STIFFNESS_VERY_LOW = 50f;
-
-    /**
-     * Damping ratio for a very bouncy spring. Note for under-damped springs
-     * (i.e. damping ratio < 1), the lower the damping ratio, the more bouncy the spring.
-     */
-    public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
-    /**
-     * Damping ratio for a medium bouncy spring. This is also the default damping ratio for spring
-     * force. Note for under-damped springs (i.e. damping ratio < 1), the lower the damping ratio,
-     * the more bouncy the spring.
-     */
-    public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
-    /**
-     * Damping ratio for a spring with low bounciness. Note for under-damped springs
-     * (i.e. damping ratio < 1), the lower the damping ratio, the higher the bounciness.
-     */
-    public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
-    /**
-     * Damping ratio for a spring with no bounciness. This damping ratio will create a critically
-     * damped spring that returns to equilibrium within the shortest amount of time without
-     * oscillating.
-     */
-    public static final float DAMPING_RATIO_NO_BOUNCY = 1f;
-
-    // This multiplier is used to calculate the velocity threshold given a certain value threshold.
-    // The idea is that if it takes >= 1 frame to move the value threshold amount, then the velocity
-    // is a reasonable threshold.
-    private static final double VELOCITY_THRESHOLD_MULTIPLIER = 1000.0 / 16.0;
-
-    // Natural frequency
-    double mNaturalFreq = Math.sqrt(STIFFNESS_MEDIUM);
-    // Damping ratio.
-    double mDampingRatio = DAMPING_RATIO_MEDIUM_BOUNCY;
-
-    // Value to indicate an unset state.
-    private static final double UNSET = Double.MAX_VALUE;
-
-    // Indicates whether the spring has been initialized
-    private boolean mInitialized = false;
-
-    // Threshold for velocity and value to determine when it's reasonable to assume that the spring
-    // is approximately at rest.
-    private double mValueThreshold;
-    private double mVelocityThreshold;
-
-    // Intermediate values to simplify the spring function calculation per frame.
-    private double mGammaPlus;
-    private double mGammaMinus;
-    private double mDampedFreq;
-
-    // Final position of the spring. This must be set before the start of the animation.
-    private double mFinalPosition = UNSET;
-
-    // Internal state to hold a value/velocity pair.
-    private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState();
-
-    /**
-     * Creates a spring force. Note that final position of the spring must be set through
-     * {@link #setFinalPosition(float)} before the spring animation starts.
-     */
-    public SpringForce() {
-        // No op.
-    }
-
-    /**
-     * Creates a spring with a given final rest position.
-     *
-     * @param finalPosition final position of the spring when it reaches equilibrium
-     */
-    public SpringForce(float finalPosition) {
-        mFinalPosition = finalPosition;
-    }
-
-    /**
-     * Sets the stiffness of a spring. The more stiff a spring is, the more force it applies to
-     * the object attached when the spring is not at the final position. Default stiffness is
-     * {@link #STIFFNESS_MEDIUM}.
-     *
-     * @param stiffness non-negative stiffness constant of a spring
-     * @return the spring force that the given stiffness is set on
-     * @throws IllegalArgumentException if the given spring stiffness is not positive
-     */
-    public SpringForce setStiffness(
-            @FloatRange(from = 0.0, fromInclusive = false) float stiffness) {
-        if (stiffness <= 0) {
-            throw new IllegalArgumentException("Spring stiffness constant must be positive.");
-        }
-        mNaturalFreq = Math.sqrt(stiffness);
-        // All the intermediate values need to be recalculated.
-        mInitialized = false;
-        return this;
-    }
-
-    /**
-     * Gets the stiffness of the spring.
-     *
-     * @return the stiffness of the spring
-     */
-    public float getStiffness() {
-        return (float) (mNaturalFreq * mNaturalFreq);
-    }
-
-    /**
-     * Spring damping ratio describes how oscillations in a system decay after a disturbance.
-     * <p>
-     * When damping ratio > 1 (over-damped), the object will quickly return to the rest position
-     * without overshooting. If damping ratio equals to 1 (i.e. critically damped), the object will
-     * return to equilibrium within the shortest amount of time. When damping ratio is less than 1
-     * (i.e. under-damped), the mass tends to overshoot, and return, and overshoot again. Without
-     * any damping (i.e. damping ratio = 0), the mass will oscillate forever.
-     * <p>
-     * Default damping ratio is {@link #DAMPING_RATIO_MEDIUM_BOUNCY}.
-     *
-     * @param dampingRatio damping ratio of the spring, it should be non-negative
-     * @return the spring force that the given damping ratio is set on
-     * @throws IllegalArgumentException if the {@param dampingRatio} is negative.
-     */
-    public SpringForce setDampingRatio(@FloatRange(from = 0.0) float dampingRatio) {
-        if (dampingRatio < 0) {
-            throw new IllegalArgumentException("Damping ratio must be non-negative");
-        }
-        mDampingRatio = dampingRatio;
-        // All the intermediate values need to be recalculated.
-        mInitialized = false;
-        return this;
-    }
-
-    /**
-     * Returns the damping ratio of the spring.
-     *
-     * @return damping ratio of the spring
-     */
-    public float getDampingRatio() {
-        return (float) mDampingRatio;
-    }
-
-    /**
-     * Sets the rest position of the spring.
-     *
-     * @param finalPosition rest position of the spring
-     * @return the spring force that the given final position is set on
-     */
-    public SpringForce setFinalPosition(float finalPosition) {
-        mFinalPosition = finalPosition;
-        return this;
-    }
-
-    /**
-     * Returns the rest position of the spring.
-     *
-     * @return rest position of the spring
-     */
-    public float getFinalPosition() {
-        return (float) mFinalPosition;
-    }
-
-    /*********************** Below are private APIs *********************/
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Override
-    public float getAcceleration(float lastDisplacement, float lastVelocity) {
-
-        lastDisplacement -= getFinalPosition();
-
-        double k = mNaturalFreq * mNaturalFreq;
-        double c = 2 * mNaturalFreq * mDampingRatio;
-
-        return (float) (-k * lastDisplacement - c * lastVelocity);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Override
-    public boolean isAtEquilibrium(float value, float velocity) {
-        if (Math.abs(velocity) < mVelocityThreshold
-                && Math.abs(value - getFinalPosition()) < mValueThreshold) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Initialize the string by doing the necessary pre-calculation as well as some sanity check
-     * on the setup.
-     *
-     * @throws IllegalStateException if the final position is not yet set by the time the spring
-     *                               animation has started
-     */
-    private void init() {
-        if (mInitialized) {
-            return;
-        }
-
-        if (mFinalPosition == UNSET) {
-            throw new IllegalStateException("Error: Final position of the spring must be"
-                    + " set before the animation starts");
-        }
-
-        if (mDampingRatio > 1) {
-            // Over damping
-            mGammaPlus = -mDampingRatio * mNaturalFreq
-                    + mNaturalFreq * Math.sqrt(mDampingRatio * mDampingRatio - 1);
-            mGammaMinus = -mDampingRatio * mNaturalFreq
-                    - mNaturalFreq * Math.sqrt(mDampingRatio * mDampingRatio - 1);
-        } else if (mDampingRatio >= 0 && mDampingRatio < 1) {
-            // Under damping
-            mDampedFreq = mNaturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
-        }
-
-        mInitialized = true;
-    }
-
-    /**
-     * Internal only call for Spring to calculate the spring position/velocity using
-     * an analytical approach.
-     */
-    DynamicAnimation.MassState updateValues(double lastDisplacement, double lastVelocity,
-            long timeElapsed) {
-        init();
-
-        double deltaT = timeElapsed / 1000d; // unit: seconds
-        lastDisplacement -= mFinalPosition;
-        double displacement;
-        double currentVelocity;
-        if (mDampingRatio > 1) {
-            // Overdamped
-            double coeffA =  lastDisplacement - (mGammaMinus * lastDisplacement - lastVelocity)
-                    / (mGammaMinus - mGammaPlus);
-            double coeffB =  (mGammaMinus * lastDisplacement - lastVelocity)
-                    / (mGammaMinus - mGammaPlus);
-            displacement = coeffA * Math.pow(Math.E, mGammaMinus * deltaT)
-                    + coeffB * Math.pow(Math.E, mGammaPlus * deltaT);
-            currentVelocity = coeffA * mGammaMinus * Math.pow(Math.E, mGammaMinus * deltaT)
-                    + coeffB * mGammaPlus * Math.pow(Math.E, mGammaPlus * deltaT);
-        } else if (mDampingRatio == 1) {
-            // Critically damped
-            double coeffA = lastDisplacement;
-            double coeffB = lastVelocity + mNaturalFreq * lastDisplacement;
-            displacement = (coeffA + coeffB * deltaT) * Math.pow(Math.E, -mNaturalFreq * deltaT);
-            currentVelocity = (coeffA + coeffB * deltaT) * Math.pow(Math.E, -mNaturalFreq * deltaT)
-                    * (-mNaturalFreq) + coeffB * Math.pow(Math.E, -mNaturalFreq * deltaT);
-        } else {
-            // Underdamped
-            double cosCoeff = lastDisplacement;
-            double sinCoeff = (1 / mDampedFreq) * (mDampingRatio * mNaturalFreq
-                    * lastDisplacement + lastVelocity);
-            displacement = Math.pow(Math.E, -mDampingRatio * mNaturalFreq * deltaT)
-                    * (cosCoeff * Math.cos(mDampedFreq * deltaT)
-                    + sinCoeff * Math.sin(mDampedFreq * deltaT));
-            currentVelocity = displacement * (-mNaturalFreq) * mDampingRatio
-                    + Math.pow(Math.E, -mDampingRatio * mNaturalFreq * deltaT)
-                    * (-mDampedFreq * cosCoeff * Math.sin(mDampedFreq * deltaT)
-                    + mDampedFreq * sinCoeff * Math.cos(mDampedFreq * deltaT));
-        }
-
-        mMassState.mValue = (float) (displacement + mFinalPosition);
-        mMassState.mVelocity = (float) currentVelocity;
-        return mMassState;
-    }
-
-    /**
-     * This threshold defines how close the animation value needs to be before the animation can
-     * finish. This default value is based on the property being animated, e.g. animations on alpha,
-     * scale, translation or rotation would have different thresholds. This value should be small
-     * enough to avoid visual glitch of "jumping to the end". But it shouldn't be so small that
-     * animations take seconds to finish.
-     *
-     * @param threshold the difference between the animation value and final spring position that
-     *                  is allowed to end the animation when velocity is very low
-     */
-    void setValueThreshold(double threshold) {
-        mValueThreshold = Math.abs(threshold);
-        mVelocityThreshold = mValueThreshold * VELOCITY_THRESHOLD_MULTIPLIER;
-    }
-}
diff --git a/android/support/annotation/AnimRes.java b/android/support/annotation/AnimRes.java
deleted file mode 100644
index d5cbaca..0000000
--- a/android/support/annotation/AnimRes.java
+++ /dev/null
@@ -1,36 +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.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 an anim resource reference (e.g. {@code android.R.anim.fade_in}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface AnimRes {
-}
diff --git a/android/support/annotation/AnimatorRes.java b/android/support/annotation/AnimatorRes.java
deleted file mode 100644
index 03918b3..0000000
--- a/android/support/annotation/AnimatorRes.java
+++ /dev/null
@@ -1,36 +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.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 an animator resource reference (e.g. {@code android.R.animator.fade_in}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface AnimatorRes {
-}
diff --git a/android/support/annotation/AnyRes.java b/android/support/annotation/AnyRes.java
deleted file mode 100644
index 2efe0da..0000000
--- a/android/support/annotation/AnyRes.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.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 resource reference of any type. If the specific type is known, use
- * one of the more specific annotations instead, such as {@link StringRes} or
- * {@link DrawableRes}.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface AnyRes {
-}
diff --git a/android/support/annotation/AnyThread.java b/android/support/annotation/AnyThread.java
deleted file mode 100644
index b006922..0000000
--- a/android/support/annotation/AnyThread.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.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-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
- * from any thread.
- * <p>
- * The main purpose of this method is to indicate that you believe a method can be called
- * from any thread; static tools can then check that nothing you call from within this method
- * or class have more strict threading requirements.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;AnyThread
- *  public void deliverResult(D data) { ... }
- * </code></pre>
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
-public @interface AnyThread {
-}
diff --git a/android/support/annotation/ArrayRes.java b/android/support/annotation/ArrayRes.java
deleted file mode 100644
index d9fd9fa..0000000
--- a/android/support/annotation/ArrayRes.java
+++ /dev/null
@@ -1,36 +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.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 an array resource reference (e.g. {@code android.R.array.phoneTypes}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface ArrayRes {
-}
diff --git a/android/support/annotation/AttrRes.java b/android/support/annotation/AttrRes.java
deleted file mode 100644
index db9a76d..0000000
--- a/android/support/annotation/AttrRes.java
+++ /dev/null
@@ -1,36 +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.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 an attribute reference (e.g. {@code android.R.attr.action}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface AttrRes {
-}
diff --git a/android/support/annotation/BinderThread.java b/android/support/annotation/BinderThread.java
deleted file mode 100644
index 5d9a3c2..0000000
--- a/android/support/annotation/BinderThread.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.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-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
- * on the binder thread.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;BinderThread
- *  public BeamShareData createBeamShareData() { ... }
- * </code></pre>
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
-public @interface BinderThread {
-}
\ No newline at end of file
diff --git a/android/support/annotation/BoolRes.java b/android/support/annotation/BoolRes.java
deleted file mode 100644
index 9206ec1..0000000
--- a/android/support/annotation/BoolRes.java
+++ /dev/null
@@ -1,36 +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.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 boolean resource reference.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface BoolRes {
-}
diff --git a/android/support/annotation/CallSuper.java b/android/support/annotation/CallSuper.java
deleted file mode 100644
index 6bf5f55..0000000
--- a/android/support/annotation/CallSuper.java
+++ /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.
- */
-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;
-
-/**
- * Denotes that any overriding methods should invoke this method as well.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;CallSuper
- *  public abstract void onFocusLost();
- * </code></pre>
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD})
-public @interface CallSuper {
-}
\ No newline at end of file
diff --git a/android/support/annotation/CheckResult.java b/android/support/annotation/CheckResult.java
deleted file mode 100644
index cb49eb7..0000000
--- a/android/support/annotation/CheckResult.java
+++ /dev/null
@@ -1,58 +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.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;
-
-/**
- * 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,
- * so calling it without actually looking at the result usually means the developer
- * has misunderstood what the method does.
- * <p>
- * Example:
- * <pre>{@code
- *  public @CheckResult String trim(String s) { return s.trim(); }
- *  ...
- *  s.trim(); // this is probably an error
- *  s = s.trim(); // ok
- * }</pre>
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD})
-public @interface CheckResult {
-    /** Defines the name of the suggested method to use instead, if applicable (using
-     * the same signature format as javadoc.) If there is more than one possibility,
-     * list them all separated by commas.
-     * <p>
-     * For example, ProcessBuilder has a method named {@code redirectErrorStream()}
-     * which sounds like it might redirect the error stream. It does not. It's just
-     * a getter which returns whether the process builder will redirect the error stream,
-     * and to actually set it, you must call {@code redirectErrorStream(boolean)}.
-     * In that case, the method should be defined like this:
-     * <pre>
-     *  &#64;CheckResult(suggest="#redirectErrorStream(boolean)")
-     *  public boolean redirectErrorStream() { ... }
-     * </pre>
-     */
-    String suggest() default "";
-}
\ No newline at end of file
diff --git a/android/support/annotation/ColorInt.java b/android/support/annotation/ColorInt.java
deleted file mode 100644
index 948a5b5..0000000
--- a/android/support/annotation/ColorInt.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.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.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
- * in the array represents a color integer.
- * <p>
- * Example:
- * <pre>{@code
- *  public abstract void setTextColor(@ColorInt int color);
- * }</pre>
- */
-@Retention(CLASS)
-@Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD})
-public @interface ColorInt {
-}
\ No newline at end of file
diff --git a/android/support/annotation/ColorLong.java b/android/support/annotation/ColorLong.java
deleted file mode 100644
index bb78138..0000000
--- a/android/support/annotation/ColorLong.java
+++ /dev/null
@@ -1,46 +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.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/android/support/annotation/ColorRes.java b/android/support/annotation/ColorRes.java
deleted file mode 100644
index 17d1e1d..0000000
--- a/android/support/annotation/ColorRes.java
+++ /dev/null
@@ -1,36 +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.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 color resource reference (e.g. {@code android.R.color.black}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface ColorRes {
-}
diff --git a/android/support/annotation/DimenRes.java b/android/support/annotation/DimenRes.java
deleted file mode 100644
index 7da7d57..0000000
--- a/android/support/annotation/DimenRes.java
+++ /dev/null
@@ -1,36 +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.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 dimension resource reference (e.g. {@code android.R.dimen.app_icon_size}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface DimenRes {
-}
diff --git a/android/support/annotation/Dimension.java b/android/support/annotation/Dimension.java
deleted file mode 100644
index 5c20edf..0000000
--- a/android/support/annotation/Dimension.java
+++ /dev/null
@@ -1,44 +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.annotation;
-
-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.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.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
-public @interface Dimension {
-    @DimensionUnit
-    int unit() default PX;
-
-    int DP = 0;
-    int PX = 1;
-    int SP = 2;
-}
diff --git a/android/support/annotation/DimensionUnit.java b/android/support/annotation/DimensionUnit.java
deleted file mode 100644
index 9ad8807..0000000
--- a/android/support/annotation/DimensionUnit.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.annotation;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-
-/**
- * Typedef for the {@link Dimension#unit} attribute.
- *
- * @hide
- */
-@IntDef({Dimension.PX,
-         Dimension.DP,
-         Dimension.SP}
-// Important: If updating these constants, also update
-// ../../../../external-annotations/android/support/annotation/annotations.xml
-)
-@Retention(SOURCE)
-@interface DimensionUnit {
-}
diff --git a/android/support/annotation/DrawableRes.java b/android/support/annotation/DrawableRes.java
deleted file mode 100644
index 8ecf604..0000000
--- a/android/support/annotation/DrawableRes.java
+++ /dev/null
@@ -1,36 +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.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 drawable resource reference (e.g. {@code android.R.attr.alertDialogIcon}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface DrawableRes {
-}
diff --git a/android/support/annotation/FloatRange.java b/android/support/annotation/FloatRange.java
deleted file mode 100644
index 7e75933..0000000
--- a/android/support/annotation/FloatRange.java
+++ /dev/null
@@ -1,54 +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.annotation;
-
-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.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>
- * Example:
- * <pre><code>
- *  &#64;FloatRange(from=0.0,to=1.0)
- *  public float getAlpha() {
- *      ...
- *  }
- * </code></pre>
- */
-@Retention(CLASS)
-@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
-public @interface FloatRange {
-    /** Smallest value. Whether it is inclusive or not is determined
-     * by {@link #fromInclusive} */
-    double from() default Double.NEGATIVE_INFINITY;
-    /** Largest value. Whether it is inclusive or not is determined
-     * by {@link #toInclusive} */
-    double to() default Double.POSITIVE_INFINITY;
-
-    /** Whether the from value is included in the range */
-    boolean fromInclusive() default true;
-
-    /** Whether the to value is included in the range */
-    boolean toInclusive() default true;
-}
\ No newline at end of file
diff --git a/android/support/annotation/FontRes.java b/android/support/annotation/FontRes.java
deleted file mode 100644
index 4baa482..0000000
--- a/android/support/annotation/FontRes.java
+++ /dev/null
@@ -1,36 +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.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/android/support/annotation/FractionRes.java b/android/support/annotation/FractionRes.java
deleted file mode 100644
index 6854c60..0000000
--- a/android/support/annotation/FractionRes.java
+++ /dev/null
@@ -1,36 +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.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 fraction resource reference.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface FractionRes {
-}
diff --git a/android/support/annotation/GuardedBy.java b/android/support/annotation/GuardedBy.java
deleted file mode 100644
index ee7d77c..0000000
--- a/android/support/annotation/GuardedBy.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.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/android/support/annotation/HalfFloat.java b/android/support/annotation/HalfFloat.java
deleted file mode 100644
index d97fe90..0000000
--- a/android/support/annotation/HalfFloat.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.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/android/support/annotation/IdRes.java b/android/support/annotation/IdRes.java
deleted file mode 100644
index 16fbac3..0000000
--- a/android/support/annotation/IdRes.java
+++ /dev/null
@@ -1,36 +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.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 an id resource reference (e.g. {@code android.R.id.copy}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface IdRes {
-}
diff --git a/android/support/annotation/IntDef.java b/android/support/annotation/IntDef.java
deleted file mode 100644
index 9945726..0000000
--- a/android/support/annotation/IntDef.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.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;
-
-/**
- * Denotes that the annotated element of integer type, represents
- * a logical type and that its value should be one of the explicitly
- * named constants. If the IntDef#flag() attribute is set to true,
- * multiple constants can be combined.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;Retention(SOURCE)
- *  &#64;IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- *  public @interface NavigationMode {}
- *  public static final int NAVIGATION_MODE_STANDARD = 0;
- *  public static final int NAVIGATION_MODE_LIST = 1;
- *  public static final int NAVIGATION_MODE_TABS = 2;
- *  ...
- *  public abstract void setNavigationMode(@NavigationMode int mode);
- *  &#64;NavigationMode
- *  public abstract int getNavigationMode();
- * </code></pre>
- * For a flag, set the flag attribute:
- * <pre><code>
- *  &#64;IntDef(
- *      flag = true,
- *      value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- * </code></pre>
- *
- * @see LongDef
- */
-@Retention(SOURCE)
-@Target({ANNOTATION_TYPE})
-public @interface IntDef {
-    /** Defines the allowed constants for this element */
-    int[] value() default {};
-
-    /** Defines whether the constants can be used as a flag, or just as an enum (the default) */
-    boolean flag() default false;
-}
diff --git a/android/support/annotation/IntRange.java b/android/support/annotation/IntRange.java
deleted file mode 100644
index 960ffcd..0000000
--- a/android/support/annotation/IntRange.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.annotation;
-
-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.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>
- * Example:
- * <pre><code>
- *  &#64;IntRange(from=0,to=255)
- *  public int getAlpha() {
- *      ...
- *  }
- * </code></pre>
- */
-@Retention(CLASS)
-@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
-public @interface IntRange {
-    /** Smallest value, inclusive */
-    long from() default Long.MIN_VALUE;
-    /** Largest value, inclusive */
-    long to() default Long.MAX_VALUE;
-}
\ No newline at end of file
diff --git a/android/support/annotation/IntegerRes.java b/android/support/annotation/IntegerRes.java
deleted file mode 100644
index a17be6f..0000000
--- a/android/support/annotation/IntegerRes.java
+++ /dev/null
@@ -1,36 +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.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 an integer resource reference (e.g. {@code android.R.integer.config_shortAnimTime}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface IntegerRes {
-}
diff --git a/android/support/annotation/InterpolatorRes.java b/android/support/annotation/InterpolatorRes.java
deleted file mode 100644
index ccd8517..0000000
--- a/android/support/annotation/InterpolatorRes.java
+++ /dev/null
@@ -1,36 +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.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 an interpolator resource reference (e.g. {@code android.R.interpolator.cycle}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface InterpolatorRes {
-}
diff --git a/android/support/annotation/Keep.java b/android/support/annotation/Keep.java
deleted file mode 100644
index 52ede05..0000000
--- a/android/support/annotation/Keep.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.annotation;
-
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-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.PACKAGE;
-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
- * on methods and classes that are accessed only via reflection
- * so a compiler may think that the code is unused.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;Keep
- *  public void foo() {
- *      ...
- *  }
- * </code></pre>
- */
-@Retention(CLASS)
-@Target({PACKAGE,TYPE,ANNOTATION_TYPE,CONSTRUCTOR,METHOD,FIELD})
-public @interface Keep {
-}
diff --git a/android/support/annotation/LayoutRes.java b/android/support/annotation/LayoutRes.java
deleted file mode 100644
index e3413b8..0000000
--- a/android/support/annotation/LayoutRes.java
+++ /dev/null
@@ -1,36 +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.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 layout resource reference (e.g. {@code android.R.layout.list_content}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface LayoutRes {
-}
diff --git a/android/support/annotation/LongDef.java b/android/support/annotation/LongDef.java
deleted file mode 100644
index 3dea338..0000000
--- a/android/support/annotation/LongDef.java
+++ /dev/null
@@ -1,60 +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.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;
-
-/**
- * Denotes that the annotated long element represents
- * a logical type and that its value should be one of the explicitly
- * named constants. If the LongDef#flag() attribute is set to true,
- * multiple constants can be combined.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;Retention(SOURCE)
- *  &#64;LongDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- *  public @interface NavigationMode {}
- *  public static final long NAVIGATION_MODE_STANDARD = 0;
- *  public static final long NAVIGATION_MODE_LIST = 1;
- *  public static final long NAVIGATION_MODE_TABS = 2;
- *  ...
- *  public abstract void setNavigationMode(@NavigationMode long mode);
- *  &#64;NavigationMode
- *  public abstract long getNavigationMode();
- * </code></pre>
- * For a flag, set the flag attribute:
- * <pre><code>
- *  &#64;LongDef(
- *      flag = true,
- *      value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
- * </code></pre>
- *
- * @see IntDef
- */
-@Retention(SOURCE)
-@Target({ANNOTATION_TYPE})
-public @interface LongDef {
-    /** Defines the allowed constants for this element */
-    long[] value() default {};
-
-    /** Defines whether the constants can be used as a flag, or just as an enum (the default) */
-    boolean flag() default false;
-}
\ No newline at end of file
diff --git a/android/support/annotation/MainThread.java b/android/support/annotation/MainThread.java
deleted file mode 100644
index 78541d5..0000000
--- a/android/support/annotation/MainThread.java
+++ /dev/null
@@ -1,51 +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.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-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
- * on the main thread.
- * <p>
- * Example:
- * <pre><code>
- *  &#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,PARAMETER})
-public @interface MainThread {
-}
diff --git a/android/support/annotation/MenuRes.java b/android/support/annotation/MenuRes.java
deleted file mode 100644
index 117c0e0..0000000
--- a/android/support/annotation/MenuRes.java
+++ /dev/null
@@ -1,36 +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.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 menu resource reference.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface MenuRes {
-}
diff --git a/android/support/annotation/NavigationRes.java b/android/support/annotation/NavigationRes.java
deleted file mode 100644
index a051026..0000000
--- a/android/support/annotation/NavigationRes.java
+++ /dev/null
@@ -1,36 +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.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 navigation resource reference (e.g. {@code R.navigation.flow}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface NavigationRes {
-}
diff --git a/android/support/annotation/NonNull.java b/android/support/annotation/NonNull.java
deleted file mode 100644
index 600165a..0000000
--- a/android/support/annotation/NonNull.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.annotation;
-
-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;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that a parameter, field or method return value can never be null.
- * <p>
- * This is a marker annotation and it has no specific attributes.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
-public @interface NonNull {
-}
diff --git a/android/support/annotation/Nullable.java b/android/support/annotation/Nullable.java
deleted file mode 100644
index 25e5b28..0000000
--- a/android/support/annotation/Nullable.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.annotation;
-
-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;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Denotes that a parameter, field or method return value can be null.
- * <p>
- * When decorating a method call parameter, this denotes that the parameter can
- * legitimately be null and the method will gracefully deal with it. Typically
- * used on optional parameters.
- * <p>
- * When decorating a method, this denotes the method might legitimately return
- * null.
- * <p>
- * This is a marker annotation and it has no specific attributes.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
-public @interface Nullable {
-}
diff --git a/android/support/annotation/PluralsRes.java b/android/support/annotation/PluralsRes.java
deleted file mode 100644
index ff20405..0000000
--- a/android/support/annotation/PluralsRes.java
+++ /dev/null
@@ -1,36 +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.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 plurals resource reference.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface PluralsRes {
-}
diff --git a/android/support/annotation/ProductionVisibility.java b/android/support/annotation/ProductionVisibility.java
deleted file mode 100644
index 4ae1600..0000000
--- a/android/support/annotation/ProductionVisibility.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.annotation;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-
-/**
- * Typedef for the {@link VisibleForTesting#otherwise} attribute.
- *
- * @hide
- */
-@IntDef({VisibleForTesting.PRIVATE,
-         VisibleForTesting.PACKAGE_PRIVATE,
-         VisibleForTesting.PROTECTED,
-         VisibleForTesting.NONE}
-// Important: If updating these constants, also update
-// ../../../../external-annotations/android/support/annotation/annotations.xml
-)
-@Retention(SOURCE)
-@interface ProductionVisibility {
-}
diff --git a/android/support/annotation/Px.java b/android/support/annotation/Px.java
deleted file mode 100644
index b9e8e2b..0000000
--- a/android/support/annotation/Px.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.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 represent a pixel dimension.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-@Dimension(unit = Dimension.PX)
-public @interface Px {
-}
diff --git a/android/support/annotation/RawRes.java b/android/support/annotation/RawRes.java
deleted file mode 100644
index b482d0c..0000000
--- a/android/support/annotation/RawRes.java
+++ /dev/null
@@ -1,36 +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.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 raw resource reference.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface RawRes {
-}
diff --git a/android/support/annotation/RequiresApi.java b/android/support/annotation/RequiresApi.java
deleted file mode 100644
index bbe386e..0000000
--- a/android/support/annotation/RequiresApi.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.annotation;
-
-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.
- * <p>
- * This is similar in purpose to the older {@code @TargetApi} annotation, but more
- * clearly expresses that this is a requirement on the caller, rather than being
- * used to "suppress" warnings within the method that exceed the {@code minSdkVersion}.
- */
-@Retention(CLASS)
-@Target({TYPE,METHOD,CONSTRUCTOR,FIELD})
-public @interface RequiresApi {
-    /**
-     * The API level to require. Alias for {@link #api} which allows you to leave out the
-     * {@code api=} part.
-     */
-    @IntRange(from=1)
-    int value() default 1;
-
-    /** The API level to require */
-    @IntRange(from=1)
-    int api() default 1;
-}
diff --git a/android/support/annotation/RequiresPermission.java b/android/support/annotation/RequiresPermission.java
deleted file mode 100644
index d1cc9ad..0000000
--- a/android/support/annotation/RequiresPermission.java
+++ /dev/null
@@ -1,133 +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.annotation;
-
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-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.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>
- * Example of requiring a single permission:
- * <pre><code>
- *   &#64;RequiresPermission(Manifest.permission.SET_WALLPAPER)
- *   public abstract void setWallpaper(Bitmap bitmap) throws IOException;
- *
- *   &#64;RequiresPermission(ACCESS_COARSE_LOCATION)
- *   public abstract Location getLastKnownLocation(String provider);
- * </code></pre>
- * Example of requiring at least one permission from a set:
- * <pre><code>
- *   &#64;RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- *   public abstract Location getLastKnownLocation(String provider);
- * </code></pre>
- * Example of requiring multiple permissions:
- * <pre><code>
- *   &#64;RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- *   public abstract Location getLastKnownLocation(String provider);
- * </code></pre>
- * Example of requiring separate read and write permissions for a content provider:
- * <pre><code>
- *   &#64;RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
- *   &#64;RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
- *   public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
- * </code></pre>
- * <p>
- * When specified on a parameter, the annotation indicates that the method requires
- * a permission which depends on the value of the parameter. For example, consider
- * {@code android.app.Activity.startActivity(android.content.Intent)}:
- * <pre>{@code
- *   public void startActivity(@RequiresPermission Intent intent) { ... }
- * }</pre>
- * Notice how there are no actual permission names listed in the annotation. The actual
- * permissions required will depend on the particular intent passed in. For example,
- * the code may look like this:
- * <pre>{@code
- *   Intent intent = new Intent(Intent.ACTION_CALL);
- *   startActivity(intent);
- * }</pre>
- * and the actual permission requirement for this particular intent is described on
- * the Intent name itself:
- * <pre><code>
- *   &#64;RequiresPermission(Manifest.permission.CALL_PHONE)
- *   public static final String ACTION_CALL = "android.intent.action.CALL";
- * </code></pre>
- */
-@Retention(CLASS)
-@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
-public @interface RequiresPermission {
-    /**
-     * The name of the permission that is required, if precisely one permission
-     * is required. If more than one permission is required, specify either
-     * {@link #allOf()} or {@link #anyOf()} instead.
-     * <p>
-     * If specified, {@link #anyOf()} and {@link #allOf()} must both be null.
-     */
-    String value() default "";
-
-    /**
-     * Specifies a list of permission names that are all required.
-     * <p>
-     * If specified, {@link #anyOf()} and {@link #value()} must both be null.
-     */
-    String[] allOf() default {};
-
-    /**
-     * Specifies a list of permission names where at least one is required
-     * <p>
-     * If specified, {@link #allOf()} and {@link #value()} must both be null.
-     */
-    String[] anyOf() default {};
-
-    /**
-     * If true, the permission may not be required in all cases (e.g. it may only be
-     * enforced on certain platforms, or for certain call parameters, etc.
-     */
-    boolean conditional() default false;
-
-    /**
-     * Specifies that the given permission is required for read operations.
-     * <p>
-     * When specified on a parameter, the annotation indicates that the method requires
-     * a permission which depends on the value of the parameter (and typically
-     * the corresponding field passed in will be one of a set of constants which have
-     * been annotated with a {@code @RequiresPermission} annotation.)
-     */
-    @Target({FIELD, METHOD, PARAMETER})
-    @interface Read {
-        RequiresPermission value() default @RequiresPermission;
-    }
-
-    /**
-     * Specifies that the given permission is required for write operations.
-     * <p>
-     * When specified on a parameter, the annotation indicates that the method requires
-     * a permission which depends on the value of the parameter (and typically
-     * the corresponding field passed in will be one of a set of constants which have
-     * been annotated with a {@code @RequiresPermission} annotation.)
-     */
-    @Target({FIELD, METHOD, PARAMETER})
-    @interface Write {
-        RequiresPermission value() default @RequiresPermission;
-    }
-}
diff --git a/android/support/annotation/RestrictTo.java b/android/support/annotation/RestrictTo.java
deleted file mode 100644
index 0aff690..0000000
--- a/android/support/annotation/RestrictTo.java
+++ /dev/null
@@ -1,93 +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.annotation;
-
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-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.PACKAGE;
-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 accessed from within a
- * specific scope (as defined by {@link Scope}).
- * <p>
- * Example of restricting usage within a library (based on gradle group ID):
- * <pre><code>
- *   &#64;RestrictTo(GROUP_ID)
- *   public void resetPaddingToInitialValues() { ...
- * </code></pre>
- * Example of restricting usage to tests:
- * <pre><code>
- *   &#64;RestrictScope(TESTS)
- *   public abstract int getUserId();
- * </code></pre>
- * Example of restricting usage to subclasses:
- * <pre><code>
- *   &#64;RestrictScope(SUBCLASSES)
- *   public void onDrawForeground(Canvas canvas) { ...
- * </code></pre>
- */
-@Retention(CLASS)
-@Target({ANNOTATION_TYPE,TYPE,METHOD,CONSTRUCTOR,FIELD,PACKAGE})
-public @interface RestrictTo {
-
-    /**
-     * The scope to which usage should be restricted.
-     */
-    Scope[] value();
-
-    enum Scope {
-        /**
-         * Restrict usage to code within the same library (e.g. the same
-         * gradle group ID and artifact ID).
-         */
-        LIBRARY,
-
-        /**
-         * Restrict usage to code within the same group of libraries.
-         * This corresponds to the gradle group ID.
-         */
-        LIBRARY_GROUP,
-
-        /**
-         * Restrict usage to code within the same group ID (based on gradle
-         * group ID). This is an alias for {@link #LIBRARY_GROUP}.
-         *
-         * @deprecated Use {@link #LIBRARY_GROUP} instead
-         */
-        @Deprecated
-        GROUP_ID,
-
-        /**
-         * Restrict usage to tests.
-         */
-        TESTS,
-
-        /**
-         * Restrict usage to subclasses of the enclosing class.
-         * <p>
-         * <strong>Note:</strong> This scope should not be used to annotate
-         * packages.
-         */
-        SUBCLASSES,
-    }
-}
diff --git a/android/support/annotation/Size.java b/android/support/annotation/Size.java
deleted file mode 100644
index 6ee72dc..0000000
--- a/android/support/annotation/Size.java
+++ /dev/null
@@ -1,51 +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.annotation;
-
-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.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
- * return value of type array or collection.
- * <p>
- * Example:
- * <pre>{@code
- *  public void getLocationInWindow(@Size(2) int[] location) {
- *      ...
- *  }
- * }</pre>
- */
-@Retention(CLASS)
-@Target({PARAMETER,LOCAL_VARIABLE,METHOD,FIELD,ANNOTATION_TYPE})
-public @interface Size {
-    /** An exact size (or -1 if not specified) */
-    long value() default -1;
-    /** A minimum size, inclusive */
-    long min() default Long.MIN_VALUE;
-    /** A maximum size, inclusive */
-    long max() default Long.MAX_VALUE;
-    /** The size must be a multiple of this factor */
-    long multiple() default 1;
-}
\ No newline at end of file
diff --git a/android/support/annotation/StringDef.java b/android/support/annotation/StringDef.java
deleted file mode 100644
index a8067b7..0000000
--- a/android/support/annotation/StringDef.java
+++ /dev/null
@@ -1,49 +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.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;
-
-/**
- * Denotes that the annotated String element, represents a logical
- * type and that its value should be one of the explicitly named constants.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;Retention(SOURCE)
- *  &#64;StringDef({
- *     POWER_SERVICE,
- *     WINDOW_SERVICE,
- *     LAYOUT_INFLATER_SERVICE
- *  })
- *  public @interface ServiceName {}
- *  public static final String POWER_SERVICE = "power";
- *  public static final String WINDOW_SERVICE = "window";
- *  public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
- *  ...
- *  public abstract Object getSystemService(@ServiceName String name);
- * </code></pre>
- */
-@Retention(SOURCE)
-@Target({ANNOTATION_TYPE})
-public @interface StringDef {
-    /** Defines the allowed constants for this element */
-    String[] value() default {};
-}
diff --git a/android/support/annotation/StringRes.java b/android/support/annotation/StringRes.java
deleted file mode 100644
index baf9853..0000000
--- a/android/support/annotation/StringRes.java
+++ /dev/null
@@ -1,36 +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.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 String resource reference (e.g. {@code android.R.string.ok}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface StringRes {
-}
diff --git a/android/support/annotation/StyleRes.java b/android/support/annotation/StyleRes.java
deleted file mode 100644
index 11d16c8..0000000
--- a/android/support/annotation/StyleRes.java
+++ /dev/null
@@ -1,36 +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.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 style resource reference (e.g. {@code android.R.style.TextAppearance}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface StyleRes {
-}
diff --git a/android/support/annotation/StyleableRes.java b/android/support/annotation/StyleableRes.java
deleted file mode 100644
index 48881cc..0000000
--- a/android/support/annotation/StyleableRes.java
+++ /dev/null
@@ -1,36 +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.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 styleable resource reference (e.g. {@code android.R.styleable.TextView_text}).
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface StyleableRes {
-}
diff --git a/android/support/annotation/TransitionRes.java b/android/support/annotation/TransitionRes.java
deleted file mode 100644
index 114a6b0..0000000
--- a/android/support/annotation/TransitionRes.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.annotation;
-
-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.
- */
-@Documented
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD})
-public @interface TransitionRes {
-}
diff --git a/android/support/annotation/UiThread.java b/android/support/annotation/UiThread.java
deleted file mode 100644
index 1d7aeca..0000000
--- a/android/support/annotation/UiThread.java
+++ /dev/null
@@ -1,52 +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.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-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
- * on the UI thread.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;UiThread
- *
- *  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,PARAMETER})
-public @interface UiThread {
-}
diff --git a/android/support/annotation/VisibleForTesting.java b/android/support/annotation/VisibleForTesting.java
deleted file mode 100644
index ed685b6..0000000
--- a/android/support/annotation/VisibleForTesting.java
+++ /dev/null
@@ -1,68 +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.annotation;
-
-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.
- * <p>
- * You can optionally specify what the visibility <b>should</b> have been if not for
- * testing; this allows tools to catch unintended access from within production
- * code.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
- *  public String printDiagnostics() { ... }
- * </code></pre>
- *
- * If not specified, the intended visibility is assumed to be private.
- */
-@Retention(CLASS)
-public @interface VisibleForTesting {
-    /**
-     * The visibility the annotated element would have if it did not need to be made visible for
-     * testing.
-     */
-    @ProductionVisibility
-    int otherwise() default PRIVATE;
-
-    /**
-     * The annotated element would have "private" visibility
-     */
-    int PRIVATE = 2; // Happens to be the same as Modifier.PRIVATE
-
-    /**
-     * The annotated element would have "package private" visibility
-     */
-    int PACKAGE_PRIVATE = 3;
-
-    /**
-     * The annotated element would have "protected" visibility
-     */
-    int PROTECTED = 4; // Happens to be the same as Modifier.PROTECTED
-
-    /**
-     * The annotated element should never be called from production code, only from tests.
-     * <p>
-     * This is equivalent to {@code @RestrictTo.Scope.TESTS}.
-     */
-    int NONE = 5;
-}
diff --git a/android/support/annotation/WorkerThread.java b/android/support/annotation/WorkerThread.java
deleted file mode 100644
index 8b08b14..0000000
--- a/android/support/annotation/WorkerThread.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.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-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
- * on a worker thread.
- * <p>
- * Example:
- * <pre><code>
- *  &#64;WorkerThread
- *  protected abstract FilterResults performFiltering(CharSequence constraint);
- * </code></pre>
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
-public @interface WorkerThread {
-}
\ No newline at end of file
diff --git a/android/support/annotation/XmlRes.java b/android/support/annotation/XmlRes.java
deleted file mode 100644
index 5f06266..0000000
--- a/android/support/annotation/XmlRes.java
+++ /dev/null
@@ -1,36 +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.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 an XML resource reference.
- */
-@Documented
-@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
-public @interface XmlRes {
-}
diff --git a/android/support/app/recommendation/ContentRecommendation.java b/android/support/app/recommendation/ContentRecommendation.java
deleted file mode 100644
index dadc7ba..0000000
--- a/android/support/app/recommendation/ContentRecommendation.java
+++ /dev/null
@@ -1,1162 +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.app.recommendation;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.Nullable;
-import android.support.annotation.StringDef;
-import android.text.TextUtils;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-
-/**
- * The ContentRecommendation object encapsulates all application provided data for a single content
- * recommendation item.
- */
-public final class ContentRecommendation
-{
-    @StringDef({
-        CONTENT_TYPE_VIDEO,
-        CONTENT_TYPE_MOVIE,
-        CONTENT_TYPE_TRAILER,
-        CONTENT_TYPE_SERIAL,
-        CONTENT_TYPE_MUSIC,
-        CONTENT_TYPE_RADIO,
-        CONTENT_TYPE_PODCAST,
-        CONTENT_TYPE_NEWS,
-        CONTENT_TYPE_SPORTS,
-        CONTENT_TYPE_APP,
-        CONTENT_TYPE_GAME,
-        CONTENT_TYPE_BOOK,
-        CONTENT_TYPE_COMIC,
-        CONTENT_TYPE_MAGAZINE,
-        CONTENT_TYPE_WEBSITE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ContentType {}
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a video clip.
-     */
-    public static final String CONTENT_TYPE_VIDEO = "android.contentType.video";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a movie.
-     */
-    public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a trailer.
-     */
-    public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is serial. It can refer to an entire show, a single season or
-     * series, or a single episode.
-     */
-    public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a song or album.
-     */
-    public static final String CONTENT_TYPE_MUSIC = "android.contentType.music";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a radio station.
-     */
-    public static final String CONTENT_TYPE_RADIO = "android.contentType.radio";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a podcast.
-     */
-    public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a news item.
-     */
-    public static final String CONTENT_TYPE_NEWS = "android.contentType.news";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is sports.
-     */
-    public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is an application.
-     */
-    public static final String CONTENT_TYPE_APP = "android.contentType.app";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a game.
-     */
-    public static final String CONTENT_TYPE_GAME = "android.contentType.game";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a book.
-     */
-    public static final String CONTENT_TYPE_BOOK = "android.contentType.book";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a comic book.
-     */
-    public static final String CONTENT_TYPE_COMIC = "android.contentType.comic";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a magazine.
-     */
-    public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
-
-    /**
-     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
-     * by the notification item is a website.
-     */
-    public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website";
-
-    @StringDef({
-        CONTENT_PRICING_FREE,
-        CONTENT_PRICING_RENTAL,
-        CONTENT_PRICING_PURCHASE,
-        CONTENT_PRICING_PREORDER,
-        CONTENT_PRICING_SUBSCRIPTION,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ContentPricing {}
-
-    /**
-     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
-     * referred by the notification item is free to consume.
-     */
-    public static final String CONTENT_PRICING_FREE = "android.contentPrice.free";
-
-    /**
-     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
-     * referred by the notification item is available as a rental, and the price value provided is
-     * the rental price for the item.
-     */
-    public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
-
-    /**
-     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
-     * referred by the notification item is available for purchase, and the price value provided is
-     * the purchase price for the item.
-     */
-    public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
-
-    /**
-     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
-     * referred by the notification item is available currently as a pre-order, and the price value
-     * provided is the purchase price for the item.
-     */
-    public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
-
-    /**
-     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
-     * referred by the notification item is available as part of a subscription based service, and
-     * the price value provided is the subscription price for the service.
-     */
-    public static final String CONTENT_PRICING_SUBSCRIPTION =
-            "android.contentPrice.subscription";
-
-    @IntDef({
-        CONTENT_STATUS_READY,
-        CONTENT_STATUS_PENDING,
-        CONTENT_STATUS_AVAILABLE,
-        CONTENT_STATUS_UNAVAILABLE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ContentStatus {}
-
-    /**
-     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
-     * notification is available and ready to be consumed immediately.
-     */
-    public static final int CONTENT_STATUS_READY = 0;
-
-    /**
-     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
-     * notification is pending, waiting on either a download or purchase operation to complete
-     * before it can be consumed.
-     */
-    public static final int CONTENT_STATUS_PENDING = 1;
-
-    /**
-     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
-     * notification is available, but needs to be first purchased, rented, subscribed or downloaded
-     * before it can be consumed.
-     */
-    public static final int CONTENT_STATUS_AVAILABLE = 2;
-
-    /**
-     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
-     * notification is not available. This could be content not available in a certain region or
-     * incompatible with the device in use.
-     */
-    public static final int CONTENT_STATUS_UNAVAILABLE = 3;
-
-    @StringDef({
-        CONTENT_MATURITY_ALL,
-        CONTENT_MATURITY_LOW,
-        CONTENT_MATURITY_MEDIUM,
-        CONTENT_MATURITY_HIGH,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ContentMaturity {}
-
-    /**
-     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
-     * by the notification is suitable for all audiences.
-     */
-    public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
-
-    /**
-     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
-     * by the notification is suitable for audiences of low maturity and above.
-     */
-    public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
-
-    /**
-     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
-     * by the notification is suitable for audiences of medium maturity and above.
-     */
-    public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
-
-    /**
-     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
-     * by the notification is suitable for audiences of high maturity and above.
-     */
-    public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
-
-    @IntDef({
-            INTENT_TYPE_ACTIVITY,
-            INTENT_TYPE_BROADCAST,
-            INTENT_TYPE_SERVICE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface IntentType {
-    }
-
-    /**
-     * Value to be used with {@link Builder#setContentIntentData} and
-     * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for an Activity
-     * should be created when posting the recommendation to the HomeScreen.
-     */
-    public static final int INTENT_TYPE_ACTIVITY = 1;
-
-    /**
-     * Value to be used with {@link Builder#setContentIntentData} and
-     * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for a Broadcast
-     * should be created when posting the recommendation to the HomeScreen.
-     */
-    public static final int INTENT_TYPE_BROADCAST = 2;
-
-    /**
-     * Value to be used with {@link Builder#setContentIntentData} and
-     * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for a Service
-     * should be created when posting the recommendation to the HomeScreen.
-     */
-    public static final int INTENT_TYPE_SERVICE = 3;
-
-    /**
-     * Object used to encapsulate the data to be used to build the {@link PendingIntent} object
-     * associated with a given content recommendation, at the time this recommendation gets posted
-     * to the home Screen.
-     * <p>
-     * The members of this object correspond to the fields passed into the {@link PendingIntent}
-     * factory methods, when creating a new PendingIntent.
-     */
-    public static class IntentData {
-        int mType;
-        Intent mIntent;
-        int mRequestCode;
-        Bundle mOptions;
-    }
-
-    private final String mIdTag;
-    private final String mTitle;
-    private final String mText;
-    private final String mSourceName;
-    private final Bitmap mContentImage;
-    private final int mBadgeIconId;
-    private final String mBackgroundImageUri;
-    private final int mColor;
-    private final IntentData mContentIntentData;
-    private final IntentData mDismissIntentData;
-    private final String[] mContentTypes;
-    private final String[] mContentGenres;
-    private final String mPriceType;
-    private final String mPriceValue;
-    private final String mMaturityRating;
-    private final long mRunningTime;
-
-    // Mutable fields
-    private String mGroup;
-    private String mSortKey;
-    private int mProgressAmount;
-    private int mProgressMax;
-    private boolean mAutoDismiss;
-    private int mStatus;
-
-    private ContentRecommendation(Builder builder) {
-        mIdTag = builder.mBuilderIdTag;
-        mTitle = builder.mBuilderTitle;
-        mText = builder.mBuilderText;
-        mSourceName = builder.mBuilderSourceName;
-        mContentImage = builder.mBuilderContentImage;
-        mBadgeIconId = builder.mBuilderBadgeIconId;
-        mBackgroundImageUri = builder.mBuilderBackgroundImageUri;
-        mColor = builder.mBuilderColor;
-        mContentIntentData = builder.mBuilderContentIntentData;
-        mDismissIntentData = builder.mBuilderDismissIntentData;
-        mContentTypes = builder.mBuilderContentTypes;
-        mContentGenres = builder.mBuilderContentGenres;
-        mPriceType = builder.mBuilderPriceType;
-        mPriceValue = builder.mBuilderPriceValue;
-        mMaturityRating = builder.mBuilderMaturityRating;
-        mRunningTime = builder.mBuilderRunningTime;
-
-        mGroup = builder.mBuilderGroup;
-        mSortKey = builder.mBuilderSortKey;
-        mProgressAmount = builder.mBuilderProgressAmount;
-        mProgressMax = builder.mBuilderProgressMax;
-        mAutoDismiss = builder.mBuilderAutoDismiss;
-        mStatus = builder.mBuilderStatus;
-    }
-
-    /**
-     * Returns the String Id tag which uniquely identifies this recommendation.
-     *
-     * @return The String Id tag for this recommendation.
-     */
-    public String getIdTag() {
-        return mIdTag;
-    }
-
-    /**
-     * Returns the content title for this recommendation.
-     *
-     * @return A String containing the recommendation content title.
-     */
-    public String getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Returns the description text for this recommendation.
-     *
-     * @return A String containing the recommendation description text.
-     */
-    public String getText() {
-        return mText;
-    }
-
-    /**
-     * Returns the source application name for this recommendation.
-     *
-     * @return A String containing the recommendation source name.
-     */
-    public String getSourceName() {
-        return mSourceName;
-    }
-
-    /**
-     * Returns the Bitmap containing the recommendation image.
-     *
-     * @return A Bitmap containing the recommendation image.
-     */
-    public Bitmap getContentImage() {
-        return mContentImage;
-    }
-
-    /**
-     * Returns the resource id for the recommendation badging icon.
-     * <p>
-     * The resource id represents the icon resource in the source application package.
-     *
-     * @return An integer id for the badge icon resource.
-     */
-    public int getBadgeImageResourceId() {
-        return mBadgeIconId;
-    }
-
-    /**
-     * Returns a Content URI that can be used to retrieve the background image for this
-     * recommendation.
-     *
-     * @return A Content URI pointing to the recommendation background image.
-     */
-    public String getBackgroundImageUri() {
-        return mBackgroundImageUri;
-    }
-
-    /**
-     * Returns the accent color value to be used in the UI when displaying this content
-     * recommendation to the user.
-     *
-     * @return An integer value representing the accent color for this recommendation.
-     */
-    public int getColor() {
-        return mColor;
-    }
-
-    /**
-     * Sets the String group ID tag for this recommendation.
-     * <p>
-     * Recommendations in the same group are ranked by the Home Screen together, and the sort order
-     * within a group is respected. This can be useful if the application has different sources for
-     * recommendations, like "trending", "subscriptions", and "new music" categories for YouTube,
-     * where the user can be more interested in recommendations from one group than another.
-     *
-     * @param groupTag A String containing the group ID tag for this recommendation.
-     */
-    public void setGroup(String groupTag) {
-        mGroup = groupTag;
-    }
-
-    /**
-     * Returns the String group ID tag for this recommendation.
-     *
-     * @return A String containing the group ID tag for this recommendation.
-     */
-    public String getGroup() {
-        return mGroup;
-    }
-
-    /**
-     * Sets the String sort key for this recommendation.
-     * <p>
-     * The sort key must be a String representation of a float number between 0.0 and 1.0, and is
-     * used to indicate the relative importance (and sort order) of a single recommendation within
-     * its specified group. The recommendations will be ordered in decreasing order of importance
-     * within a given group.
-     *
-     * @param sortKey A String containing the sort key for this recommendation.
-     */
-    public void setSortKey(String sortKey) {
-        mSortKey = sortKey;
-    }
-
-    /**
-     * Returns the String sort key for this recommendation.
-     *
-     * @return A String containing the sort key for this recommendation.
-     */
-    public String getSortKey() {
-        return mSortKey;
-    }
-
-    /**
-     * Sets the progress information for the content pointed to by this recommendation.
-     *
-     * @param max The maximum value for the progress of this content.
-     * @param progress The progress amount for this content. Must be in the range (0 - max).
-     */
-    public void setProgress(int max, int progress) {
-        if (max < 0 || progress < 0) {
-            throw new IllegalArgumentException();
-        }
-        mProgressMax = max;
-        mProgressAmount = progress;
-    }
-
-    /**
-     * Indicates if this recommendation contains valid progress information.
-     *
-     * @return true if the recommendation contains valid progress data, false otherwise.
-     */
-    public boolean hasProgressInfo() {
-        return mProgressMax != 0;
-    }
-
-    /**
-     * Returns the maximum value for the progress data of this recommendation.
-     *
-     * @return An integer representing the maximum progress value.
-     */
-    public int getProgressMax() {
-        return mProgressMax;
-    }
-
-    /**
-     * Returns the progress amount for this recommendation.
-     *
-     * @return An integer representing the recommendation progress amount.
-     */
-    public int getProgressValue() {
-        return mProgressAmount;
-    }
-
-    /**
-     * Sets the flag indicating if this recommendation should be dismissed automatically.
-     * <p>
-     * Auto-dismiss notifications are automatically removed from the Home Screen when the user
-     * clicks on them.
-     *
-     * @param autoDismiss A boolean indicating if the recommendation should be auto dismissed or
-     *            not.
-     */
-    public void setAutoDismiss(boolean autoDismiss) {
-        mAutoDismiss = autoDismiss;
-    }
-
-    /**
-     * Indicates whether this recommendation should be dismissed automatically.
-     * <p>
-     * Auto-dismiss notifications are automatically removed from the Home Screen when the user
-     * clicks on them.
-     *
-     * @return true if the recommendation is marked for auto dismissal, or false otherwise.
-     */
-    public boolean isAutoDismiss() {
-        return mAutoDismiss;
-    }
-
-    /**
-     * Returns the data for the Intent that will be issued when the user clicks on the
-     * recommendation.
-     *
-     * @return An IntentData object, containing the data for the Intent that gets issued when the
-     *         recommendation is clicked on.
-     */
-    public IntentData getContentIntent() {
-        return mContentIntentData;
-    }
-
-    /**
-     * Returns the data for the Intent that will be issued when the recommendation gets dismissed
-     * from the Home Screen, due to an user action.
-     *
-     * @return An IntentData object, containing the data for the Intent that gets issued when the
-     *         recommendation is dismissed from the Home Screen.
-     */
-    public IntentData getDismissIntent() {
-        return mDismissIntentData;
-    }
-
-    /**
-     * Returns an array containing the content types tags that describe the content. The first tag
-     * entry is considered the primary type for the content, and is used for content ranking
-     * purposes.
-     *
-     * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants) that
-     *         describe the recommended content.
-     */
-    public String[] getContentTypes() {
-        if (mContentTypes != null) {
-            return Arrays.copyOf(mContentTypes, mContentTypes.length);
-        }
-        return mContentTypes;
-    }
-
-    /**
-     * Returns the primary content type tag for the recommendation, or null if no content types have
-     * been specified.
-     *
-     * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating the
-     *         primary content type for the recommendation.
-     */
-    public String getPrimaryContentType() {
-        if (mContentTypes != null && mContentTypes.length > 0) {
-            return mContentTypes[0];
-        }
-        return null;
-    }
-
-    /**
-     * Returns an array containing the genres that describe the content. Genres are open ended
-     * String tags.
-     *
-     * @return An array of genre tags that describe the recommended content.
-     */
-    public String[] getGenres() {
-        if (mContentGenres != null) {
-            return Arrays.copyOf(mContentGenres, mContentGenres.length);
-        }
-        return mContentGenres;
-    }
-
-    /**
-     * Gets the pricing type for the content.
-     *
-     * @return A predefined tag indicating the pricing type for the content (see the <code>
-     *         CONTENT_PRICING_*</code> constants).
-     */
-    public String getPricingType() {
-        return mPriceType;
-    }
-
-    /**
-     * Gets the price value (when applicable) for the content. The value will be provided as a
-     * String containing the price in the appropriate currency for the current locale.
-     *
-     * @return A string containing a representation of the content price in the current locale and
-     *         currency.
-     */
-    public String getPricingValue() {
-        return mPriceValue;
-    }
-
-    /**
-     * Sets the availability status value for the content. This status indicates whether the content
-     * is ready to be consumed on the device, or if the user must first purchase, rent, subscribe
-     * to, or download the content.
-     *
-     * @param status The status value for the content. (see the <code>CONTENT_STATUS_*</code> for
-     *            the valid status values).
-     */
-    public void setStatus(@ContentStatus int status) {
-        mStatus = status;
-    }
-
-    /**
-     * Returns availability status value for the content. This status indicates whether the content
-     * is ready to be consumed on the device, or if the user must first purchase, rent, subscribe
-     * to, or download the content.
-     *
-     * @return The status value for the content, or -1 is a valid status has not been specified (see
-     *         the <code>CONTENT_STATUS_*</code> constants for the valid status values).
-     */
-    public int getStatus() {
-        return mStatus;
-    }
-
-    /**
-     * Returns the maturity level rating for the content.
-     *
-     * @return returns a predefined tag indicating the maturity level rating for the content (see
-     *         the <code>CONTENT_MATURITY_*</code> constants).
-     */
-    public String getMaturityRating() {
-        return mMaturityRating;
-    }
-
-    /**
-     * Returns the running time for the content.
-     *
-     * @return The run length, in seconds, of the content associated with the notification.
-     */
-    public long getRunningTime() {
-        return mRunningTime;
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (other instanceof ContentRecommendation) {
-            return TextUtils.equals(mIdTag, ((ContentRecommendation) other).getIdTag());
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        if (mIdTag != null) {
-            return mIdTag.hashCode();
-        }
-        return Integer.MAX_VALUE;
-    }
-
-    /**
-     * Builder class for {@link ContentRecommendation} objects. Provides a convenient way to set the
-     * various fields of a {@link ContentRecommendation}.
-     * <p>
-     * Example:
-     *
-     * <pre class="prettyprint">
-     * ContentRecommendation rec = new ContentRecommendation.Builder()
-     *         .setIdInfo(id, &quot;MyTagId&quot;)
-     *         .setTitle(&quot;My Content Recommendation&quot;)
-     *         .setText(&quot;An example of content recommendation&quot;)
-     *         .setContentImage(myBitmap)
-     *         .setBadgeIcon(R.drawable.app_icon)
-     *         .setGroup(&quot;Trending&quot;)
-     *         .build();
-     * </pre>
-     */
-    public static final class Builder {
-        private String mBuilderIdTag;
-        private String mBuilderTitle;
-        private String mBuilderText;
-        private String mBuilderSourceName;
-        private Bitmap mBuilderContentImage;
-        private int mBuilderBadgeIconId;
-        private String mBuilderBackgroundImageUri;
-        private int mBuilderColor;
-        private String mBuilderGroup;
-        private String mBuilderSortKey;
-        private int mBuilderProgressAmount;
-        private int mBuilderProgressMax;
-        private boolean mBuilderAutoDismiss;
-        private IntentData mBuilderContentIntentData;
-        private IntentData mBuilderDismissIntentData;
-        private String[] mBuilderContentTypes;
-        private String[] mBuilderContentGenres;
-        private String mBuilderPriceType;
-        private String mBuilderPriceValue;
-        private int mBuilderStatus;
-        private String mBuilderMaturityRating;
-        private long mBuilderRunningTime;
-
-        /**
-         * Constructs a new Builder.
-         *
-         */
-        public Builder() {
-        }
-
-        /**
-         * Sets the Id tag that uniquely identifies this recommendation object.
-         *
-         * @param idTag A String tag identifier for this recommendation.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setIdTag(String idTag) {
-            mBuilderIdTag = checkNotNull(idTag);
-            return this;
-        }
-
-        /**
-         * Sets the content title for the recommendation.
-         *
-         * @param title A String containing the recommendation content title.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setTitle(String title) {
-            mBuilderTitle = checkNotNull(title);
-            return this;
-        }
-
-        /**
-         * Sets the description text for the recommendation.
-         *
-         * @param description A String containing the recommendation description text.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setText(@Nullable String description) {
-            mBuilderText = description;
-            return this;
-        }
-
-        /**
-         * Sets the source application name for the recommendation.
-         * <P>
-         * If the source name is never set, or set to null, the application name retrieved from its
-         * package will be used by default.
-         *
-         * @param source A String containing the recommendation source name.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setSourceName(@Nullable String source) {
-            mBuilderSourceName = source;
-            return this;
-        }
-
-        /**
-         * Sets the recommendation image.
-         *
-         * @param image A Bitmap containing the recommendation image.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setContentImage(Bitmap image) {
-            mBuilderContentImage = checkNotNull(image);
-            return this;
-        }
-
-        /**
-         * Sets the resource ID for the recommendation badging icon.
-         * <p>
-         * The resource id represents the icon resource in the source application package. If not
-         * set, or an invalid resource ID is specified, the application icon retrieved from its
-         * package will be used by default.
-         *
-         * @param iconResourceId An integer id for the badge icon resource.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setBadgeIcon(@DrawableRes int iconResourceId) {
-            mBuilderBadgeIconId = iconResourceId;
-            return this;
-        }
-
-        /**
-         * Sets the Content URI that will be used to retrieve the background image for the
-         * recommendation.
-         *
-         * @param imageUri A Content URI pointing to the recommendation background image.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setBackgroundImageUri(@Nullable String imageUri) {
-            mBuilderBackgroundImageUri = imageUri;
-            return this;
-        }
-
-        /**
-         * Sets the accent color value to be used in the UI when displaying this content
-         * recommendation to the user.
-         *
-         * @param color An integer value representing the accent color for this recommendation.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setColor(@ColorInt int color) {
-            mBuilderColor = color;
-            return this;
-        }
-
-        /**
-         * Sets the String group ID tag for the recommendation.
-         * <p>
-         * Recommendations in the same group are ranked by the Home Screen together, and the sort
-         * order within a group is respected. This can be useful if the application has different
-         * sources for recommendations, like "trending", "subscriptions", and "new music" categories
-         * for YouTube, where the user can be more interested in recommendations from one group than
-         * another.
-         *
-         * @param groupTag A String containing the group ID tag for this recommendation.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setGroup(@Nullable String groupTag) {
-            mBuilderGroup = groupTag;
-            return this;
-        }
-
-        /**
-         * Sets the String sort key for the recommendation.
-         * <p>
-         * The sort key must be a String representation of a float number between 0.0 and 1.0, and
-         * is used to indicate the relative importance (and sort order) of a single recommendation
-         * within its specified group. The recommendations will be ordered in decreasing order of
-         * importance within a given group.
-         *
-         * @param sortKey A String containing the sort key for this recommendation.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setSortKey(@Nullable String sortKey) {
-            mBuilderSortKey = sortKey;
-            return this;
-        }
-
-        /**
-         * Sets the progress information for the content pointed to by the recommendation.
-         *
-         * @param max The maximum value for the progress of this content.
-         * @param progress The progress amount for this content. Must be in the range (0 - max).
-         * @return The Builder object, for chaining.
-         */
-        public Builder setProgress(int max, int progress) {
-            if (max < 0 || progress < 0) {
-                throw new IllegalArgumentException();
-            }
-            mBuilderProgressMax = max;
-            mBuilderProgressAmount = progress;
-            return this;
-        }
-
-        /**
-         * Sets the flag indicating if the recommendation should be dismissed automatically.
-         * <p>
-         * Auto-dismiss notifications are automatically removed from the Home Screen when the user
-         * clicks on them.
-         *
-         * @param autoDismiss A boolean indicating if the recommendation should be auto dismissed or
-         *            not.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setAutoDismiss(boolean autoDismiss) {
-            mBuilderAutoDismiss = autoDismiss;
-            return this;
-        }
-
-        /**
-         * Sets the data for the Intent that will be issued when the user clicks on the
-         * recommendation.
-         * <p>
-         * The Intent data fields provided correspond to the fields passed into the
-         * {@link PendingIntent} factory methods, when creating a new PendingIntent. The actual
-         * PengindIntent object will only be created at the time a recommendation is posted to the
-         * Home Screen.
-         *
-         * @param intentType The type of {@link PendingIntent} to be created when posting this
-         *            recommendation.
-         * @param intent The Intent which to be issued when the recommendation is clicked on.
-         * @param requestCode The private request code to be used when creating the
-         *            {@link PendingIntent}
-         * @param options Only used for the Activity Intent type. Additional options for how the
-         *            Activity should be started. May be null if there are no options.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setContentIntentData(@IntentType int intentType, Intent intent,
-                int requestCode, @Nullable Bundle options) {
-            if (intentType != INTENT_TYPE_ACTIVITY &&
-                    intentType != INTENT_TYPE_BROADCAST &&
-                    intentType != INTENT_TYPE_SERVICE) {
-                throw new IllegalArgumentException("Invalid Intent type specified.");
-            }
-
-            mBuilderContentIntentData = new IntentData();
-            mBuilderContentIntentData.mType = intentType;
-            mBuilderContentIntentData.mIntent = checkNotNull(intent);
-            mBuilderContentIntentData.mRequestCode = requestCode;
-            mBuilderContentIntentData.mOptions = options;
-
-            return this;
-        }
-
-        /**
-         * Sets the data for the Intent that will be issued when the recommendation gets dismissed
-         * from the Home Screen, due to an user action.
-         * <p>
-         * The Intent data fields provided correspond to the fields passed into the
-         * {@link PendingIntent} factory methods, when creating a new PendingIntent. The actual
-         * PengindIntent object will only be created at the time a recommendation is posted to the
-         * Home Screen.
-         *
-         * @param intentType The type of {@link PendingIntent} to be created when posting this
-         *            recommendation.
-         * @param intent The Intent which gets issued when the recommendation is dismissed from the
-         *            Home Screen.
-         * @param requestCode The private request code to be used when creating the
-         *            {@link PendingIntent}
-         * @param options Only used for the Activity Intent type. Additional options for how the
-         *            Activity should be started. May be null if there are no options.
-         * @return The Builder object, for chaining.
-         */
-        public Builder setDismissIntentData(@IntentType int intentType, @Nullable Intent intent,
-                int requestCode, @Nullable Bundle options) {
-            if (intent != null) {
-                if (intentType != INTENT_TYPE_ACTIVITY &&
-                        intentType != INTENT_TYPE_BROADCAST &&
-                        intentType != INTENT_TYPE_SERVICE) {
-                    throw new IllegalArgumentException("Invalid Intent type specified.");
-                }
-
-                mBuilderDismissIntentData = new IntentData();
-                mBuilderDismissIntentData.mType = intentType;
-                mBuilderDismissIntentData.mIntent = intent;
-                mBuilderDismissIntentData.mRequestCode = requestCode;
-                mBuilderDismissIntentData.mOptions = options;
-            } else {
-                mBuilderDismissIntentData = null;
-            }
-            return this;
-        }
-
-        /**
-         * Sets the content types associated with the content recommendation. The first tag entry
-         * will be considered the primary type for the content and will be used for ranking
-         * purposes. Other secondary type tags may be provided, if applicable, and may be used for
-         * filtering purposes.
-         *
-         * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code>
-         *            constants) that describe the recommended content.
-         */
-        public Builder setContentTypes(String[] types) {
-            mBuilderContentTypes = checkNotNull(types);
-            return this;
-        }
-
-        /**
-         * Sets the content genres for the recommendation. These genres may be used for content
-         * ranking. Genres are open ended String tags.
-         * <p>
-         * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
-         *
-         * @param genres Array of genre string tags that describe the recommended content.
-         */
-        public Builder setGenres(String[] genres) {
-            mBuilderContentGenres = genres;
-            return this;
-        }
-
-        /**
-         * Sets the pricing and availability information for the recommendation. The provided
-         * information will indicate the access model for the content (free, rental, purchase or
-         * subscription) and the price value (if not free).
-         *
-         * @param priceType Pricing type for this content. Must be one of the predefined pricing
-         *            type tags (see the <code>CONTENT_PRICING_*</code> constants).
-         * @param priceValue A string containing a representation of the content price in the
-         *            current locale and currency.
-         */
-        public Builder setPricingInformation(@ContentPricing String priceType,
-                @Nullable String priceValue) {
-            mBuilderPriceType = checkNotNull(priceType);
-            mBuilderPriceValue = priceValue;
-            return this;
-        }
-
-        /**
-         * Sets the availability status for the content. This status indicates whether the referred
-         * content is ready to be consumed on the device, or if the user must first purchase, rent,
-         * subscribe to, or download the content.
-         *
-         * @param contentStatus The status value for this content. Must be one of the predefined
-         *            content status values (see the <code>CONTENT_STATUS_*</code> constants).
-         */
-        public Builder setStatus(@ContentStatus int contentStatus) {
-            mBuilderStatus = contentStatus;
-            return this;
-        }
-
-        /**
-         * Sets the maturity level rating for the content.
-         *
-         * @param maturityRating A tag indicating the maturity level rating for the content. This
-         *            tag must be one of the predefined maturity rating tags (see the <code>
-         *            CONTENT_MATURITY_*</code> constants).
-         */
-        public Builder setMaturityRating(@ContentMaturity String maturityRating) {
-            mBuilderMaturityRating = checkNotNull(maturityRating);
-            return this;
-        }
-
-        /**
-         * Sets the running time (when applicable) for the content.
-         *
-         * @param length The running time, in seconds, of the content.
-         */
-        public Builder setRunningTime(long length) {
-            if (length < 0) {
-                throw new IllegalArgumentException();
-            }
-            mBuilderRunningTime = length;
-            return this;
-        }
-
-        /**
-         * Combine all of the options that have been set and return a new
-         * {@link ContentRecommendation} object.
-         */
-        public ContentRecommendation build() {
-            return new ContentRecommendation(this);
-        }
-    }
-
-    /**
-     * Returns a {@link android.app.Notification Notification} object which contains the content
-     * recommendation data encapsulated by this object, which can be used for posting the
-     * recommendation via the {@link android.app.NotificationManager NotificationManager}.
-     *
-     * @param context A {@link Context} that will be used to construct the
-     *            {@link android.app.Notification Notification} object which will carry the
-     *            recommendation data.
-     * @return A {@link android.app.Notification Notification} containing the stored recommendation
-     *         data.
-     */
-    public Notification getNotificationObject(Context context) {
-        Notification.Builder builder = new Notification.Builder(context);
-        RecommendationExtender recExtender = new RecommendationExtender();
-
-        // Encode all the content recommendation data in a Notification object
-
-        builder.setCategory(Notification.CATEGORY_RECOMMENDATION);
-        builder.setContentTitle(mTitle);
-        builder.setContentText(mText);
-        builder.setContentInfo(mSourceName);
-        builder.setLargeIcon(mContentImage);
-        builder.setSmallIcon(mBadgeIconId);
-        if (mBackgroundImageUri != null) {
-            builder.getExtras().putString(Notification.EXTRA_BACKGROUND_IMAGE_URI,
-                    mBackgroundImageUri);
-        }
-        builder.setColor(mColor);
-        builder.setGroup(mGroup);
-        builder.setSortKey(mSortKey);
-        builder.setProgress(mProgressMax, mProgressAmount, false);
-        builder.setAutoCancel(mAutoDismiss);
-
-        if (mContentIntentData != null) {
-            PendingIntent contentPending;
-            if (mContentIntentData.mType == INTENT_TYPE_ACTIVITY) {
-                contentPending = PendingIntent.getActivity(context, mContentIntentData.mRequestCode,
-                        mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
-                        mContentIntentData.mOptions);
-            } else if (mContentIntentData.mType == INTENT_TYPE_SERVICE) {
-                contentPending = PendingIntent.getService(context, mContentIntentData.mRequestCode,
-                        mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            } else { // Default:INTENT_TYPE_BROADCAST{
-                contentPending = PendingIntent.getBroadcast(context,
-                        mContentIntentData.mRequestCode,
-                        mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            }
-            builder.setContentIntent(contentPending);
-        }
-
-        if (mDismissIntentData != null) {
-            PendingIntent dismissPending;
-            if (mDismissIntentData.mType == INTENT_TYPE_ACTIVITY) {
-                dismissPending = PendingIntent.getActivity(context, mDismissIntentData.mRequestCode,
-                        mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
-                        mDismissIntentData.mOptions);
-            } else if (mDismissIntentData.mType == INTENT_TYPE_SERVICE) {
-                dismissPending = PendingIntent.getService(context, mDismissIntentData.mRequestCode,
-                        mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            } else { // Default:INTENT_TYPE_BROADCAST{
-                dismissPending = PendingIntent.getBroadcast(context,
-                        mDismissIntentData.mRequestCode,
-                        mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            }
-            builder.setDeleteIntent(dismissPending);
-        }
-
-        recExtender.setContentTypes(mContentTypes);
-        recExtender.setGenres(mContentGenres);
-        recExtender.setPricingInformation(mPriceType, mPriceValue);
-        recExtender.setStatus(mStatus);
-        recExtender.setMaturityRating(mMaturityRating);
-        recExtender.setRunningTime(mRunningTime);
-
-        builder.extend(recExtender);
-        Notification notif = builder.build();
-        return notif;
-    }
-
-    /**
-     * 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
-     */
-    private static <T> T checkNotNull(final T reference) {
-        if (reference == null) {
-            throw new NullPointerException();
-        }
-        return reference;
-    }
-
-}
\ No newline at end of file
diff --git a/android/support/app/recommendation/RecommendationExtender.java b/android/support/app/recommendation/RecommendationExtender.java
deleted file mode 100644
index 5762a28..0000000
--- a/android/support/app/recommendation/RecommendationExtender.java
+++ /dev/null
@@ -1,315 +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.app.recommendation;
-
-import android.app.Notification;
-import android.os.Bundle;
-
-/**
- * <p>
- * Helper class to add content info extensions to notifications. To create a notification with
- * content info extensions:
- * <ol>
- * <li>Create an {@link Notification.Builder}, setting any desired properties.
- * <li>Create a {@link RecommendationExtender}.
- * <li>Set content info specific properties using the {@code add} and {@code set} methods of
- * {@link RecommendationExtender}.
- * <li>Call {@link android.app.Notification.Builder#extend(Notification.Extender) 
- * Notification.Builder.extend(Notification.Extender)} to apply the extensions to a notification.
- * </ol>
- *
- * <pre class="prettyprint">Notification notification = new Notification.Builder(context) * ... * .extend(new RecommendationExtender() * .set*(...)) * .build(); * </pre>
- * <p>
- * Content info extensions can be accessed on an existing notification by using the
- * {@code RecommendationExtender(Notification)} constructor, and then using the {@code get} methods
- * to access values.
- */
-public final class RecommendationExtender implements Notification.Extender {
-    private static final String TAG = "RecommendationExtender";
-
-    // Key for the Content info extensions bundle in the main Notification extras bundle
-    private static final String EXTRA_CONTENT_INFO_EXTENDER = "android.CONTENT_INFO_EXTENSIONS";
-
-    // Keys within EXTRA_CONTENT_INFO_EXTENDER for individual content info options.
-
-    private static final String KEY_CONTENT_TYPE = "android.contentType";
-
-    private static final String KEY_CONTENT_GENRES = "android.contentGenre";
-
-    private static final String KEY_CONTENT_PRICING_TYPE = "android.contentPricing.type";
-
-    private static final String KEY_CONTENT_PRICING_VALUE = "android.contentPricing.value";
-
-    private static final String KEY_CONTENT_STATUS = "android.contentStatus";
-
-    private static final String KEY_CONTENT_MATURITY_RATING = "android.contentMaturity";
-
-    private static final String KEY_CONTENT_RUN_LENGTH = "android.contentLength";
-
-    private String[] mTypes;
-    private String[] mGenres;
-    private String mPricingType;
-    private String mPricingValue;
-    private int mContentStatus = -1;
-    private String mMaturityRating;
-    private long mRunLength = -1;
-
-    /**
-     * Create a {@link RecommendationExtender} with default options.
-     */
-    public RecommendationExtender() {
-    }
-
-    /**
-     * Create a {@link RecommendationExtender} from the RecommendationExtender options of an
-     * existing Notification.
-     *
-     * @param notif The notification from which to copy options.
-     */
-    public RecommendationExtender(Notification notif) {
-        Bundle contentBundle = notif.extras == null ?
-                null : notif.extras.getBundle(EXTRA_CONTENT_INFO_EXTENDER);
-        if (contentBundle != null) {
-            mTypes = contentBundle.getStringArray(KEY_CONTENT_TYPE);
-            mGenres = contentBundle.getStringArray(KEY_CONTENT_GENRES);
-            mPricingType = contentBundle.getString(KEY_CONTENT_PRICING_TYPE);
-            mPricingValue = contentBundle.getString(KEY_CONTENT_PRICING_VALUE);
-            mContentStatus = contentBundle.getInt(KEY_CONTENT_STATUS, -1);
-            mMaturityRating = contentBundle.getString(KEY_CONTENT_MATURITY_RATING);
-            mRunLength = contentBundle.getLong(KEY_CONTENT_RUN_LENGTH, -1);
-        }
-    }
-
-    /**
-     * Apply content extensions to a notification that is being built. This is typically called by
-     * the {@link android.app.Notification.Builder#extend(Notification.Extender) 
-     * Notification.Builder.extend(Notification.Extender)} method of {@link Notification.Builder}.
-     */
-    @Override
-    public Notification.Builder extend(Notification.Builder builder) {
-        Bundle contentBundle = new Bundle();
-
-        if (mTypes != null) {
-            contentBundle.putStringArray(KEY_CONTENT_TYPE, mTypes);
-        }
-        if (mGenres != null) {
-            contentBundle.putStringArray(KEY_CONTENT_GENRES, mGenres);
-        }
-        if (mPricingType != null) {
-            contentBundle.putString(KEY_CONTENT_PRICING_TYPE, mPricingType);
-        }
-        if (mPricingValue != null) {
-            contentBundle.putString(KEY_CONTENT_PRICING_VALUE, mPricingValue);
-        }
-        if (mContentStatus != -1) {
-            contentBundle.putInt(KEY_CONTENT_STATUS, mContentStatus);
-        }
-        if (mMaturityRating != null) {
-            contentBundle.putString(KEY_CONTENT_MATURITY_RATING, mMaturityRating);
-        }
-        if (mRunLength > 0) {
-            contentBundle.putLong(KEY_CONTENT_RUN_LENGTH, mRunLength);
-        }
-
-        builder.getExtras().putBundle(EXTRA_CONTENT_INFO_EXTENDER, contentBundle);
-        return builder;
-    }
-
-    /**
-     * Sets the content types associated with the notification content. The first tag entry will be
-     * considered the primary type for the content and will be used for ranking purposes. Other
-     * secondary type tags may be provided, if applicable, and may be used for filtering purposes.
-     *
-     * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants)
-     *            that describe the content referred to by a notification.
-     */
-    public RecommendationExtender setContentTypes(String[] types) {
-        mTypes = types;
-        return this;
-    }
-
-    /**
-     * Returns an array containing the content types that describe the content associated with the
-     * notification. The first tag entry is considered the primary type for the content, and is used
-     * for content ranking purposes.
-     *
-     * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants) that
-     *         describe the content associated with the notification.
-     * @see RecommendationExtender#setContentTypes
-     */
-    public String[] getContentTypes() {
-        return mTypes;
-    }
-
-    /**
-     * Returns the primary content type tag for the content associated with the notification.
-     *
-     * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating the
-     *         primary type for the content associated with the notification.
-     * @see RecommendationExtender#setContentTypes
-     */
-    public String getPrimaryContentType() {
-        if (mTypes == null || mTypes.length == 0) {
-            return null;
-        }
-        return mTypes[0];
-    }
-
-    /**
-     * Sets the content genres associated with the notification content. These genres may be used
-     * for content ranking. Genres are open ended String tags.
-     * <p>
-     * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
-     *
-     * @param genres Array of genre string tags that describe the content referred to by a
-     *            notification.
-     */
-    public RecommendationExtender setGenres(String[] genres) {
-        mGenres = genres;
-        return this;
-    }
-
-    /**
-     * Returns an array containing the content genres that describe the content associated with the
-     * notification.
-     *
-     * @return An array of genre tags that describe the content associated with the notification.
-     * @see RecommendationExtender#setGenres
-     */
-    public String[] getGenres() {
-        return mGenres;
-    }
-
-    /**
-     * Sets the pricing and availability information for the content associated with the
-     * notification. The provided information will indicate the access model for the content (free,
-     * rental, purchase or subscription) and the price value (if not free).
-     *
-     * @param priceType Pricing type for this content. Must be one of the predefined pricing type
-     *            tags (see the <code>CONTENT_PRICING_*</code> constants).
-     * @param priceValue A string containing a representation of the content price in the current
-     *            locale and currency.
-     * @return This object for method chaining.
-     */
-    public RecommendationExtender setPricingInformation(
-            @ContentRecommendation.ContentPricing String priceType, String priceValue) {
-        mPricingType = priceType;
-        mPricingValue = priceValue;
-        return this;
-    }
-
-    /**
-     * Gets the pricing type for the content associated with the notification.
-     *
-     * @return A predefined tag indicating the pricing type for the content (see the <code> CONTENT_PRICING_*</code>
-     *         constants).
-     * @see RecommendationExtender#setPricingInformation
-     */
-    public String getPricingType() {
-        return mPricingType;
-    }
-
-    /**
-     * Gets the price value (when applicable) for the content associated with a notification. The
-     * value will be provided as a String containing the price in the appropriate currency for the
-     * current locale.
-     *
-     * @return A string containing a representation of the content price in the current locale and
-     *         currency.
-     * @see RecommendationExtender#setPricingInformation
-     */
-    public String getPricingValue() {
-        if (mPricingType == null) {
-            return null;
-        }
-        return mPricingValue;
-    }
-
-    /**
-     * Sets the availability status for the content associated with the notification. This status
-     * indicates whether the referred content is ready to be consumed on the device, or if the user
-     * must first purchase, rent, subscribe to, or download the content.
-     *
-     * @param contentStatus The status value for this content. Must be one of the predefined content
-     *            status values (see the <code>CONTENT_STATUS_*</code> constants).
-     */
-    public RecommendationExtender setStatus(
-            @ContentRecommendation.ContentStatus int contentStatus) {
-        mContentStatus = contentStatus;
-        return this;
-    }
-
-    /**
-     * Returns status value for the content associated with the notification. This status indicates
-     * whether the referred content is ready to be consumed on the device, or if the user must first
-     * purchase, rent, subscribe to, or download the content.
-     *
-     * @return The status value for this content, or -1 is a valid status has not been specified
-     *         (see the <code>CONTENT_STATUS_*</code> for the defined valid status values).
-     * @see RecommendationExtender#setStatus
-     */
-    public int getStatus() {
-        return mContentStatus;
-    }
-
-    /**
-     * Sets the maturity level rating for the content associated with the notification.
-     *
-     * @param maturityRating A tag indicating the maturity level rating for the content. This tag
-     *            must be one of the predefined maturity rating tags (see the <code> CONTENT_MATURITY_*</code>
-     *            constants).
-     */
-    public RecommendationExtender setMaturityRating(
-            @ContentRecommendation.ContentMaturity String maturityRating) {
-        mMaturityRating = maturityRating;
-        return this;
-    }
-
-    /**
-     * Returns the maturity level rating for the content associated with the notification.
-     *
-     * @return returns a predefined tag indicating the maturity level rating for the content (see
-     *         the <code>CONTENT_MATURITY_*</code> constants).
-     * @see RecommendationExtender#setMaturityRating
-     */
-    public String getMaturityRating() {
-        return mMaturityRating;
-    }
-
-    /**
-     * Sets the running time (when applicable) for the content associated with the notification.
-     *
-     * @param length The runing time, in seconds, of the content associated with the notification.
-     */
-    public RecommendationExtender setRunningTime(long length) {
-        if (length < 0) {
-            throw new IllegalArgumentException("Invalid value for Running Time");
-        }
-        mRunLength = length;
-        return this;
-    }
-
-    /**
-     * Returns the running time for the content associated with the notification.
-     *
-     * @return The running time, in seconds, of the content associated with the notification.
-     * @see RecommendationExtender#setRunningTime
-     */
-    public long getRunningTime() {
-        return mRunLength;
-    }
-}
diff --git a/android/support/checkapi/ApiXmlConversionTask.java b/android/support/checkapi/ApiXmlConversionTask.java
deleted file mode 100644
index 1790b8b..0000000
--- a/android/support/checkapi/ApiXmlConversionTask.java
+++ /dev/null
@@ -1,80 +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.checkapi;
-
-import org.gradle.api.tasks.InputFile;
-import org.gradle.api.tasks.JavaExec;
-import org.gradle.api.tasks.OutputFile;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Task that converts the given API file to XML format.
- */
-public class ApiXmlConversionTask extends JavaExec {
-
-    private File mInputApiFile;
-
-    private File mOutputApiXmlFile;
-
-    @InputFile
-    public File getInputApiFile() {
-        return mInputApiFile;
-    }
-
-    public void setInputApiFile(File inputApiFile) {
-        this.mInputApiFile = inputApiFile;
-    }
-
-    @OutputFile
-    public File getOutputApiXmlFile() {
-        return mOutputApiXmlFile;
-    }
-
-    public void setOutputApiXmlFile(File outputApiXmlFile) {
-        this.mOutputApiXmlFile = outputApiXmlFile;
-    }
-
-
-    public ApiXmlConversionTask() {
-        setMaxHeapSize("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 void configureApiXmlConversionTask() {
-        List<String> arguments = new ArrayList<>();
-        arguments.add("-convert2xml");
-        arguments.add(getInputApiFile().getAbsolutePath());
-        arguments.add(getOutputApiXmlFile().getAbsolutePath());
-        setArgs(arguments);
-    }
-
-    @Override
-    public void exec() {
-        configureApiXmlConversionTask();
-        super.exec();
-    }
-}
diff --git a/android/support/checkapi/UpdateApiTask.java b/android/support/checkapi/UpdateApiTask.java
deleted file mode 100644
index 15e9104..0000000
--- a/android/support/checkapi/UpdateApiTask.java
+++ /dev/null
@@ -1,133 +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.checkapi;
-
-import com.google.common.io.Files;
-
-import org.gradle.api.DefaultTask;
-import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.InputFile;
-import org.gradle.api.tasks.Optional;
-import org.gradle.api.tasks.OutputFile;
-import org.gradle.api.tasks.TaskAction;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.nio.charset.Charset;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Task for updating the checked in API file with the newly generated one.
- */
-public class UpdateApiTask extends DefaultTask {
-    private File mNewApiFile;
-    private File mNewRemovedApiFile;
-
-    private Set<String> mWhitelistErrors = new HashSet<>();
-
-    private File mOldApiFile;
-    private File mOldRemovedApiFile;
-
-    private File mWhitelistErrorsFile;
-
-    @InputFile
-    public File getNewApiFile() {
-        return mNewApiFile;
-    }
-
-    public void setNewApiFile(File newApiFile) {
-        this.mNewApiFile = newApiFile;
-    }
-
-    @InputFile
-    @Optional
-    public File getNewRemovedApiFile() {
-        return mNewRemovedApiFile;
-    }
-
-    public void setNewRemovedApiFile(File newRemovedApiFile) {
-        this.mNewRemovedApiFile = newRemovedApiFile;
-    }
-
-    @Input
-    @Optional
-    public Set<String> getWhitelistErrors() {
-        return mWhitelistErrors;
-    }
-
-    public void setWhitelistErrors(Set<String> whitelistErrors) {
-        this.mWhitelistErrors = whitelistErrors;
-    }
-
-    @OutputFile
-    public File getOldApiFile() {
-        return mOldApiFile;
-    }
-
-    public void setOldApiFile(File oldApiFile) {
-        this.mOldApiFile = oldApiFile;
-    }
-
-    @OutputFile
-    @Optional
-    public File getOldRemovedApiFile() {
-        return mOldRemovedApiFile;
-    }
-
-    public void setOldRemovedApiFile(File oldRemovedApiFile) {
-        this.mOldRemovedApiFile = oldRemovedApiFile;
-    }
-
-    @OutputFile
-    @Optional
-    public File getWhitelistErrorsFile() {
-        return mWhitelistErrorsFile;
-    }
-
-    public void setWhitelistErrorsFile(File whitelistErrorsFile) {
-        this.mWhitelistErrorsFile = whitelistErrorsFile;
-    }
-
-    /**
-     * Actually copy the file to the desired location and update the whitelist warnings file.
-     */
-    @TaskAction
-    public void doUpdate() throws Exception {
-        Files.copy(getNewApiFile(), getOldApiFile());
-
-        if (getOldRemovedApiFile() != null) {
-            if (getNewRemovedApiFile() != null) {
-                Files.copy(getNewRemovedApiFile(), getOldRemovedApiFile());
-            } else {
-                getOldRemovedApiFile().delete();
-            }
-        }
-
-        if (mWhitelistErrorsFile != null && !mWhitelistErrors.isEmpty()) {
-            try (BufferedWriter writer = Files.newWriter(
-                    mWhitelistErrorsFile, Charset.defaultCharset())) {
-                for (String error : mWhitelistErrors) {
-                    writer.write(error + "\n");
-                }
-            }
-            getLogger().lifecycle("Whitelisted " + mWhitelistErrors.size() + " error(s)...");
-        }
-
-        getLogger().lifecycle("Wrote public API definition to " + mOldApiFile.getName());
-    }
-}
diff --git a/android/support/content/ContentPager.java b/android/support/content/ContentPager.java
deleted file mode 100644
index 71cb832..0000000
--- a/android/support/content/ContentPager.java
+++ /dev/null
@@ -1,707 +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.content;
-
-import static android.support.v4.util.Preconditions.checkArgument;
-import static android.support.v4.util.Preconditions.checkState;
-
-import android.content.ContentResolver;
-import android.database.CrossProcessCursor;
-import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.CursorWrapper;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
-import android.support.annotation.GuardedBy;
-import android.support.annotation.IntDef;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresPermission;
-import android.support.annotation.VisibleForTesting;
-import android.support.annotation.WorkerThread;
-import android.support.v4.util.LruCache;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * {@link ContentPager} provides support for loading "paged" data on a background thread
- * using the {@link ContentResolver} framework. This provides an effective compatibility
- * layer for the ContentResolver "paging" support added in Android O. Those Android O changes,
- * like this class, help reduce or eliminate the occurrence of expensive inter-process
- * shared memory operations (aka "CursorWindow swaps") happening on the UI thread when
- * working with remote providers.
- *
- * <p>The list of terms used in this document:
- *
- * <ol>"The provider" is a {@link android.content.ContentProvider} supplying data identified
- * by a specific content {@link Uri}. A provider is the source of data, and for the sake of
- * this documents, the provider resides in a remote process.
-
- * <ol>"supports paging" A provider supports paging when it returns a pre-paged {@link Cursor}
- * that honors the paging contract. See @link ContentResolver#QUERY_ARG_OFFSET} and
- * {@link ContentResolver#QUERY_ARG_LIMIT} for details on the contract.
-
- * <ol>"CursorWindow swaps" The process by which new data is loaded into a shared memory
- * via a CursorWindow instance. This is a prominent contributor to UI jank in applications
- * that use Cursor as backing data for UI elements like {@code RecyclerView}.
- *
- * <p><b>Details</b>
- *
- * <p>Data will be loaded from a content uri in one of two ways, depending on the runtime
- * environment and if the provider supports paging.
- *
- * <li>If the system is Android O and greater and the provider supports paging, the Cursor
- * will be returned, effectively unmodified, to a {@link ContentCallback} supplied by
- * your application.
- *
- * <li>If the system is less than Android O or the provider does not support paging, the
- * loader will fetch an unpaged Cursor from the provider. The unpaged Cursor will be held
- * by the ContentPager, and data will be copied into a new cursor in a background thread.
- * The new cursor will be returned to a {@link ContentCallback} supplied by your application.
- *
- * <p>In either cases, when an application employs this library it can generally assume
- * that there will be no CursorWindow swap. But picking the right limit for records can
- * help reduce or even eliminate some heavy lifting done to guard against swaps.
- *
- * <p>How do we avoid that entirely?
- *
- * <p><b>Picking a reasonable item limit</b>
- *
- * <p>Authors are encouraged to experiment with limits using real data and the widest column
- * projection they'll use in their app. The total number of records that will fit into shared
- * memory varies depending on multiple factors.
- *
- * <li>The number of columns being requested in the cursor projection. Limit the number
- * of columns, to reduce the size of each row.
- * <li>The size of the data in each column.
- * <li>the Cursor type.
- *
- * <p>If the cursor is running in-process, there may be no need for paging. Depending on
- * the Cursor implementation chosen there may be no shared memory/CursorWindow in use.
- * NOTE: If the provider is running in your process, you should implement paging support
- * inorder to make your app run fast and to consume the fewest resources possible.
- *
- * <p>In common cases where there is a low volume (in the hundreds) of records in the dataset
- * being queried, all of the data should easily fit in shared memory. A debugger can be handy
- * to understand with greater accuracy how many results can fit in shared memory. Inspect
- * the Cursor object returned from a call to
- * {@link ContentResolver#query(Uri, String[], String, String[], String)}. If the underlying
- * type is a {@link android.database.CrossProcessCursor} or
- * {@link android.database.AbstractWindowedCursor} it'll have a {@link CursorWindow} field.
- * Check {@link CursorWindow#getNumRows()}. If getNumRows returns less than
- * {@link Cursor#getCount}, then you've found something close to the max rows that'll
- * fit in a page. If the data in row is expected to be relatively stable in size, reduce
- * row count by 15-20% to get a reasonable max page size.
- *
- * <p><b>What if the limit I guessed was wrong?</b>
-
- * <p>The library includes safeguards that protect against situations where an author
- * specifies a record limit that exceeds the number of rows accessible without a CursorWindow swap.
- * In such a circumstance, the Cursor will be adapted to report a count ({Cursor#getCount})
- * that reflects only records available without CursorWindow swap. But this involves
- * extra work that can be eliminated with a correct limit.
- *
- * <p>In addition to adjusted coujnt, {@link #EXTRA_SUGGESTED_LIMIT} will be included
- * in cursor extras. When EXTRA_SUGGESTED_LIMIT is present in extras, the client should
- * strongly consider using this value as the limit for subsequent queries as doing so should
- * help avoid the ned to wrap pre-paged cursors.
- *
- * <p><b>Lifecycle and cleanup</b>
- *
- * <p>Cursors resulting from queries are owned by the requesting client. So they must be closed
- * by the client at the appropriate time.
- *
- * <p>However, the library retains an internal cache of content that needs to be cleaned up.
- * In order to cleanup, call {@link #reset()}.
- *
- * <p><b>Projections</b>
- *
- * <p>Note that projection is ignored when determining the identity of a query. When
- * adding or removing projection, clients should call {@link #reset()} to clear
- * cached data.
- */
-public class ContentPager {
-
-    @VisibleForTesting
-    static final String CURSOR_DISPOSITION = "android.support.v7.widget.CURSOR_DISPOSITION";
-
-    @IntDef(value = {
-            ContentPager.CURSOR_DISPOSITION_COPIED,
-            ContentPager.CURSOR_DISPOSITION_PAGED,
-            ContentPager.CURSOR_DISPOSITION_REPAGED,
-            ContentPager.CURSOR_DISPOSITION_WRAPPED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CursorDisposition {}
-
-    /** The cursor size exceeded page size. A new cursor with with page data was created. */
-    public static final int CURSOR_DISPOSITION_COPIED = 1;
-
-    /**
-     * The cursor was provider paged.
-     */
-    public static final int CURSOR_DISPOSITION_PAGED = 2;
-
-    /** The cursor was pre-paged, but total size was larger than CursorWindow size. */
-    public static final int CURSOR_DISPOSITION_REPAGED = 3;
-
-    /**
-     * The cursor was not pre-paged, but total size was smaller than page size.
-     * Cursor wrapped to supply data in extras only.
-     */
-    public static final int CURSOR_DISPOSITION_WRAPPED = 4;
-
-    /** @see ContentResolver#EXTRA_HONORED_ARGS */
-    public static final String EXTRA_HONORED_ARGS = ContentResolver.EXTRA_HONORED_ARGS;
-
-    /** @see ContentResolver#EXTRA_TOTAL_COUNT */
-    public static final String EXTRA_TOTAL_COUNT = ContentResolver.EXTRA_TOTAL_COUNT;
-
-    /** @see ContentResolver#QUERY_ARG_OFFSET */
-    public static final String QUERY_ARG_OFFSET = ContentResolver.QUERY_ARG_OFFSET;
-
-    /** @see ContentResolver#QUERY_ARG_LIMIT */
-    public static final String QUERY_ARG_LIMIT = ContentResolver.QUERY_ARG_LIMIT;
-
-    /** Denotes the requested limit, if the limit was not-honored. */
-    public static final String EXTRA_REQUESTED_LIMIT = "android-support:extra-ignored-limit";
-
-    /** Specifies a limit likely to fit in CursorWindow limit. */
-    public static final String EXTRA_SUGGESTED_LIMIT = "android-support:extra-suggested-limit";
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "ContentPager";
-    private static final int DEFAULT_CURSOR_CACHE_SIZE = 1;
-
-    private final QueryRunner mQueryRunner;
-    private final QueryRunner.Callback mQueryCallback;
-    private final ContentResolver mResolver;
-    private final Object mContentLock = new Object();
-    private final @GuardedBy("mContentLock") Set<Query> mActiveQueries = new HashSet<>();
-    private final @GuardedBy("mContentLock") CursorCache mCursorCache;
-
-    private final Stats mStats = new Stats();
-
-    /**
-     * Creates a new ContentPager with a default cursor cache size of 1.
-     */
-    public ContentPager(ContentResolver resolver, QueryRunner queryRunner) {
-        this(resolver, queryRunner, DEFAULT_CURSOR_CACHE_SIZE);
-    }
-
-    /**
-     * Creates a new ContentPager.
-     *
-     * @param cursorCacheSize Specifies the size of the unpaged cursor cache. If you will
-     *     only be querying a single content Uri, 1 is sufficient. If you wish to use
-     *     a single ContentPager for queries against several independent Uris this number
-     *     should be increased to reflect that. Remember that adding or modifying a
-     *     query argument creates a new Uri.
-     * @param resolver The content resolver to use when performing queries.
-     * @param queryRunner The query running to use. This provides a means of executing
-     *         queries on a background thread.
-     */
-    public ContentPager(
-            @NonNull ContentResolver resolver,
-            @NonNull QueryRunner queryRunner,
-            int cursorCacheSize) {
-
-        checkArgument(resolver != null, "'resolver' argument cannot be null.");
-        checkArgument(queryRunner != null, "'queryRunner' argument cannot be null.");
-        checkArgument(cursorCacheSize > 0, "'cursorCacheSize' argument must be greater than 0.");
-
-        mResolver = resolver;
-        mQueryRunner = queryRunner;
-        mQueryCallback = new QueryRunner.Callback() {
-
-            @WorkerThread
-            @Override
-            public @Nullable Cursor runQueryInBackground(Query query) {
-                return loadContentInBackground(query);
-            }
-
-            @MainThread
-            @Override
-            public void onQueryFinished(Query query, Cursor cursor) {
-                ContentPager.this.onCursorReady(query, cursor);
-            }
-        };
-
-        mCursorCache = new CursorCache(cursorCacheSize);
-    }
-
-    /**
-     * Initiates loading of content.
-     * For details on all params but callback, see
-     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
-     *
-     * @param uri The URI, using the content:// scheme, for the content to retrieve.
-     * @param projection A list of which columns to return. Passing null will return
-     *         the default project as determined by the provider. This can be inefficient,
-     *         so it is best to supply a projection.
-     * @param queryArgs A Bundle containing any arguments to the query.
-     * @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.
-     * @param callback The callback that will receive the query results.
-     *
-     * @return A Query object describing the query.
-     */
-    @MainThread
-    public @NonNull Query query(
-            @NonNull @RequiresPermission.Read Uri uri,
-            @Nullable String[] projection,
-            @NonNull Bundle queryArgs,
-            @Nullable CancellationSignal cancellationSignal,
-            @NonNull ContentCallback callback) {
-
-        checkArgument(uri != null, "'uri' argument cannot be null.");
-        checkArgument(queryArgs != null, "'queryArgs' argument cannot be null.");
-        checkArgument(callback != null, "'callback' argument cannot be null.");
-
-        Query query = new Query(uri, projection, queryArgs, cancellationSignal, callback);
-
-        if (DEBUG) Log.d(TAG, "Handling query: " + query);
-
-        if (!mQueryRunner.isRunning(query)) {
-            synchronized (mContentLock) {
-                mActiveQueries.add(query);
-            }
-            mQueryRunner.query(query, mQueryCallback);
-        }
-
-        return query;
-    }
-
-    /**
-     * Clears any cached data. This method must be called in order to cleanup runtime state
-     * (like cursors).
-     */
-    @MainThread
-    public void reset() {
-        synchronized (mContentLock) {
-            if (DEBUG) Log.d(TAG, "Clearing un-paged cursor cache.");
-            mCursorCache.evictAll();
-
-            for (Query query : mActiveQueries) {
-                if (DEBUG) Log.d(TAG, "Canceling running query: " + query);
-                mQueryRunner.cancel(query);
-                query.cancel();
-            }
-
-            mActiveQueries.clear();
-        }
-    }
-
-    @WorkerThread
-    private Cursor loadContentInBackground(Query query) {
-        if (DEBUG) Log.v(TAG, "Loading cursor for query: " + query);
-        mStats.increment(Stats.EXTRA_TOTAL_QUERIES);
-
-        synchronized (mContentLock) {
-            // We have a existing unpaged-cursor for this query. Instead of running a new query
-            // via ContentResolver, we'll just copy results from that.
-            // This is the "compat" behavior.
-            if (mCursorCache.hasEntry(query.getUri())) {
-                if (DEBUG) Log.d(TAG, "Found unpaged results in cache for: " + query);
-                return createPagedCursor(query);
-            }
-        }
-
-        // We don't have an unpaged query, so we run the query using ContentResolver.
-        // It may be that no query for this URI has ever been run, so no unpaged
-        // results have been saved. Or, it may be the the provider supports paging
-        // directly, and is returning a pre-paged result set...so no unpaged
-        // cursor will ever be set.
-        Cursor cursor = query.run(mResolver);
-        mStats.increment(Stats.EXTRA_RESOLVED_QUERIES);
-
-        //       for the window. If so, communicate the overflow back to the client.
-        if (cursor == null) {
-            Log.e(TAG, "Query resulted in null cursor. " + query);
-            return null;
-        }
-
-        if (isProviderPaged(cursor)) {
-            return processProviderPagedCursor(query, cursor);
-        }
-
-        // Cache the unpaged results so we can generate pages from them on subsequent queries.
-        synchronized (mContentLock) {
-            mCursorCache.put(query.getUri(), cursor);
-            return createPagedCursor(query);
-        }
-    }
-
-    @WorkerThread
-    @GuardedBy("mContentLock")
-    private Cursor createPagedCursor(Query query) {
-        Cursor unpaged = mCursorCache.get(query.getUri());
-        checkState(unpaged != null, "No un-paged cursor in cache, or can't retrieve it.");
-
-        mStats.increment(Stats.EXTRA_COMPAT_PAGED);
-
-        if (DEBUG) Log.d(TAG, "Synthesizing cursor for page: " + query);
-        int count = Math.min(query.getLimit(), unpaged.getCount());
-
-        // don't wander off the end of the cursor.
-        if (query.getOffset() + query.getLimit() > unpaged.getCount()) {
-            count = unpaged.getCount() % query.getLimit();
-        }
-
-        if (DEBUG) Log.d(TAG, "Cursor count: " + count);
-
-        Cursor result = null;
-        // If the cursor isn't advertising support for paging, but is in-fact smaller
-        // than the page size requested, we just decorate the cursor with paging data,
-        // and wrap it without copy.
-        if (query.getOffset() == 0 && unpaged.getCount() < query.getLimit()) {
-            result = new CursorView(
-                    unpaged, unpaged.getCount(), CURSOR_DISPOSITION_WRAPPED);
-        } else {
-            // This creates an in-memory copy of the data that fits the requested page.
-            // ContentObservers registered on InMemoryCursor are directly registered
-            // on the unpaged cursor.
-            result = new InMemoryCursor(
-                    unpaged, query.getOffset(), count, CURSOR_DISPOSITION_COPIED);
-        }
-
-        mStats.includeStats(result.getExtras());
-        return result;
-    }
-
-    @WorkerThread
-    private @Nullable Cursor processProviderPagedCursor(Query query, Cursor cursor) {
-
-        CursorWindow window = getWindow(cursor);
-        int windowSize = cursor.getCount();
-        if (window != null) {
-            if (DEBUG) Log.d(TAG, "Returning provider-paged cursor.");
-            windowSize = window.getNumRows();
-        }
-
-        // Android O paging APIs are *all* about avoiding CursorWindow swaps,
-        // because the swaps need to happen on the UI thread in jank-inducing ways.
-        // But, the APIs don't *guarantee* that no window-swapping will happen
-        // when traversing a cursor.
-        //
-        // Here in the support lib, we can guarantee there is no window swapping
-        // by detecting mismatches between requested sizes and window sizes.
-        // When a mismatch is detected we can return a cursor that reports
-        // a size bounded by its CursorWindow size, and includes a suggested
-        // size to use for subsequent queries.
-
-        if (DEBUG) Log.d(TAG, "Cursor window overflow detected. Returning re-paged cursor.");
-
-        int disposition = (cursor.getCount() <= windowSize)
-                ? CURSOR_DISPOSITION_PAGED
-                : CURSOR_DISPOSITION_REPAGED;
-
-        Cursor result = new CursorView(cursor, windowSize, disposition);
-        Bundle extras = result.getExtras();
-
-        // If the orig cursor reports a size larger than the window, suggest a better limit.
-        if (cursor.getCount() > windowSize) {
-            extras.putInt(EXTRA_REQUESTED_LIMIT, query.getLimit());
-            extras.putInt(EXTRA_SUGGESTED_LIMIT, (int) (windowSize * .85));
-        }
-
-        mStats.increment(Stats.EXTRA_PROVIDER_PAGED);
-        mStats.includeStats(extras);
-        return result;
-    }
-
-    private CursorWindow getWindow(Cursor cursor) {
-        if (cursor instanceof CursorWrapper) {
-            return getWindow(((CursorWrapper) cursor).getWrappedCursor());
-        }
-        if (cursor instanceof CrossProcessCursor) {
-            return ((CrossProcessCursor) cursor).getWindow();
-        }
-        // TODO: Any other ways we can find/access windows?
-        return null;
-    }
-
-    // Called in the foreground when the cursor is ready for the client.
-    @MainThread
-    private void onCursorReady(Query query, Cursor cursor) {
-        synchronized (mContentLock) {
-            mActiveQueries.remove(query);
-        }
-
-        query.getCallback().onCursorReady(query, cursor);
-    }
-
-    /**
-     * @return true if the cursor extras contains all of the signs of being paged.
-     *     Technically we could also check SDK version since facilities for paging
-     *     were added in SDK 26, but if it looks like a duck and talks like a duck
-     *     itsa duck (especially if it helps with testing).
-     */
-    @WorkerThread
-    private boolean isProviderPaged(Cursor cursor) {
-        Bundle extras = cursor.getExtras();
-        extras = extras != null ? extras : Bundle.EMPTY;
-        String[] honoredArgs = extras.getStringArray(EXTRA_HONORED_ARGS);
-
-        return (extras.containsKey(EXTRA_TOTAL_COUNT)
-                && honoredArgs != null
-                && contains(honoredArgs, QUERY_ARG_OFFSET)
-                && contains(honoredArgs, QUERY_ARG_LIMIT));
-    }
-
-    private static <T> boolean contains(T[] array, T value) {
-        for (T element : array) {
-            if (value.equals(element)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * @return Bundle populated with existing extras (if any) as well as
-     * all usefule paging related extras.
-     */
-    static Bundle buildExtras(
-            @Nullable Bundle extras, int recordCount, @CursorDisposition int cursorDisposition) {
-
-        if (extras == null || extras == Bundle.EMPTY) {
-            extras = new Bundle();
-        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            extras = extras.deepCopy();
-        }
-        // else we modify cursor extras directly, cuz that's our only choice.
-
-        extras.putInt(CURSOR_DISPOSITION, cursorDisposition);
-        if (!extras.containsKey(EXTRA_TOTAL_COUNT)) {
-            extras.putInt(EXTRA_TOTAL_COUNT, recordCount);
-        }
-
-        if (!extras.containsKey(EXTRA_HONORED_ARGS)) {
-            extras.putStringArray(EXTRA_HONORED_ARGS, new String[]{
-                    ContentPager.QUERY_ARG_OFFSET,
-                    ContentPager.QUERY_ARG_LIMIT
-            });
-        }
-
-        return extras;
-    }
-
-    /**
-     * Builds a Bundle with offset and limit values suitable for with
-     * {@link #query(Uri, String[], Bundle, CancellationSignal, ContentCallback)}.
-     *
-     * @param offset must be greater than or equal to 0.
-     * @param limit can be any value. Only values greater than or equal to 0 are respected.
-     *         If any other value results in no upper limit on results. Note that a well
-     *         behaved client should probably supply a reasonable limit. See class
-     *         documentation on how to select a limit.
-     *
-     * @return Mutable Bundle pre-populated with offset and limits vales.
-     */
-    public static @NonNull Bundle createArgs(int offset, int limit) {
-        checkArgument(offset >= 0);
-        Bundle args = new Bundle();
-        args.putInt(ContentPager.QUERY_ARG_OFFSET, offset);
-        args.putInt(ContentPager.QUERY_ARG_LIMIT, limit);
-        return args;
-    }
-
-    /**
-     * Callback by which a client receives results of a query.
-     */
-    public interface ContentCallback {
-        /**
-         * Called when paged cursor is ready. Null, if query failed.
-         * @param query The query having been executed.
-         * @param cursor the query results. Null if query couldn't be executed.
-         */
-        @MainThread
-        void onCursorReady(@NonNull Query query, @Nullable Cursor cursor);
-    }
-
-    /**
-     * Provides support for adding extras to a cursor. This is necessary
-     * as a cursor returning an extras Bundle that is either Bundle.EMPTY
-     * or null, cannot have information added to the cursor. On SDKs earlier
-     * than M, there is no facility to replace the Bundle.
-     */
-    private static final class CursorView extends CursorWrapper {
-        private final Bundle mExtras;
-        private final int mSize;
-
-        CursorView(Cursor delegate, int size, @CursorDisposition int disposition) {
-            super(delegate);
-            mSize = size;
-
-            mExtras = buildExtras(delegate.getExtras(), delegate.getCount(), disposition);
-        }
-
-        @Override
-        public int getCount() {
-            return mSize;
-        }
-
-        @Override
-        public Bundle getExtras() {
-            return mExtras;
-        }
-    }
-
-    /**
-     * LruCache holding at most {@code maxSize} cursors. Once evicted a cursor
-     * is immediately closed. The only cursor's held in this cache are
-     * unpaged results. For this purpose the cache is keyed by the URI,
-     * not the entire query. Cursors that are pre-paged by the provider
-     * are never cached.
-     */
-    private static final class CursorCache extends LruCache<Uri, Cursor> {
-        CursorCache(int maxSize) {
-            super(maxSize);
-        }
-
-        @WorkerThread
-        @Override
-        protected void entryRemoved(
-                boolean evicted, Uri uri, Cursor oldCursor, Cursor newCursor) {
-            if (!oldCursor.isClosed()) {
-                oldCursor.close();
-            }
-        }
-
-        /** @return true if an entry is present for the Uri. */
-        @WorkerThread
-        @GuardedBy("mContentLock")
-        boolean hasEntry(Uri uri) {
-            return get(uri) != null;
-        }
-    }
-
-    /**
-     * Implementations of this interface provide the mechanism
-     * for execution of queries off the UI thread.
-     */
-    public interface QueryRunner {
-        /**
-         * Execute a query.
-         * @param query The query that will be run. This value should be handed
-         *         back to the callback when ready to run in the background.
-         * @param callback The callback that should be called to both execute
-         *         the query (in the background) and to receive the results
-         *         (in the foreground).
-         */
-        void query(@NonNull Query query, @NonNull Callback callback);
-
-        /**
-         * @param query The query in question.
-         * @return true if the query is already running.
-         */
-        boolean isRunning(@NonNull Query query);
-
-        /**
-         * Attempt to cancel a (presumably) running query.
-         * @param query The query in question.
-         */
-        void cancel(@NonNull Query query);
-
-        /**
-         * Callback that receives a cursor once a query as been executed on the Runner.
-         */
-        interface Callback {
-            /**
-             * Method called on background thread where actual query is executed. This is provided
-             * by ContentPager.
-             * @param query The query to be executed.
-             */
-            @Nullable Cursor runQueryInBackground(@NonNull Query query);
-
-            /**
-             * Called on main thread when query has completed.
-             * @param query The completed query.
-             * @param cursor The results in Cursor form. Null if not successfully completed.
-             */
-            void onQueryFinished(@NonNull Query query, @Nullable Cursor cursor);
-        }
-    }
-
-    static final class Stats {
-
-        /** Identifes the total number of queries handled by ContentPager. */
-        static final String EXTRA_TOTAL_QUERIES = "android-support:extra-total-queries";
-
-        /** Identifes the number of queries handled by content resolver. */
-        static final String EXTRA_RESOLVED_QUERIES = "android-support:extra-resolved-queries";
-
-        /** Identifes the number of pages produced by way of copying. */
-        static final String EXTRA_COMPAT_PAGED = "android-support:extra-compat-paged";
-
-        /** Identifes the number of pages produced directly by a page-supporting provider. */
-        static final String EXTRA_PROVIDER_PAGED = "android-support:extra-provider-paged";
-
-        // simple stats objects tracking paged result handling.
-        private int mTotalQueries;
-        private int mResolvedQueries;
-        private int mCompatPaged;
-        private int mProviderPaged;
-
-        private void increment(String prop) {
-            switch (prop) {
-                case EXTRA_TOTAL_QUERIES:
-                    ++mTotalQueries;
-                    break;
-
-                case EXTRA_RESOLVED_QUERIES:
-                    ++mResolvedQueries;
-                    break;
-
-                case EXTRA_COMPAT_PAGED:
-                    ++mCompatPaged;
-                    break;
-
-                case EXTRA_PROVIDER_PAGED:
-                    ++mProviderPaged;
-                    break;
-
-                default:
-                    throw new IllegalArgumentException("Unknown property: " + prop);
-            }
-        }
-
-        private void reset() {
-            mTotalQueries = 0;
-            mResolvedQueries = 0;
-            mCompatPaged = 0;
-            mProviderPaged = 0;
-        }
-
-        void includeStats(Bundle bundle) {
-            bundle.putInt(EXTRA_TOTAL_QUERIES, mTotalQueries);
-            bundle.putInt(EXTRA_RESOLVED_QUERIES, mResolvedQueries);
-            bundle.putInt(EXTRA_COMPAT_PAGED, mCompatPaged);
-            bundle.putInt(EXTRA_PROVIDER_PAGED, mProviderPaged);
-        }
-    }
-}
diff --git a/android/support/content/InMemoryCursor.java b/android/support/content/InMemoryCursor.java
deleted file mode 100644
index 097709a..0000000
--- a/android/support/content/InMemoryCursor.java
+++ /dev/null
@@ -1,279 +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.content;
-
-import static android.support.v4.util.Preconditions.checkArgument;
-
-import android.database.AbstractCursor;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.CursorIndexOutOfBoundsException;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.RestrictTo;
-
-/**
- * A {@link Cursor} implementation that stores information in-memory, in a type-safe fashion.
- * Values are stored, when possible, as primitives to avoid the need for the autoboxing (as is
- * necessary when working with MatrixCursor).
- *
- * <p>Unlike {@link android.database.MatrixCursor}, this cursor is not mutable at runtime.
- * It exists solely as a destination for data copied by {@link ContentPager} from a source
- * Cursor when a page is being synthesized. It is not anticipated at this time that this
- * will be useful outside of this package. As such it is immutable once constructed.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-final class InMemoryCursor extends AbstractCursor {
-
-    private static final int NUM_TYPES = 5;
-
-    private final String[] mColumnNames;
-    private final int mRowCount;
-
-    // This is an index of column, by type. Maps back to position
-    // in native array.
-    // E.g. if we have columns typed like [string, int, int, string, int, int]
-    // the values in this index will be:
-    // mTypedColumnIndex[string][0] == 0
-    // mTypedColumnIndex[int][1] == 0
-    // mTypedColumnIndex[int][2] == 1
-    // mTypedColumnIndex[string][3] == 1
-    // mTypedColumnIndex[int][4] == 2
-    // mTypedColumnIndex[int][4] == 3
-    // This allows us to calculate the number of cells by type in a row
-    // which, in turn, allows us to calculate the size of the primitive storage arrays.
-    // We also use this information to lookup a particular typed value given
-    // the row offset and column offset.
-    private final int[][] mTypedColumnIndex;
-
-    // simple index to column to type.
-    private final int[] mColumnType;
-
-    // count of number of columns by type.
-    private final int[] mColumnTypeCount;
-
-    private final Bundle mExtras;
-
-    private final ObserverRelay mObserverRelay;
-
-    // Row data decomposed by type.
-    private long[] mLongs;
-    private double[] mDoubles;
-    private byte[][] mBlobs;
-    private String[] mStrings;
-
-    /**
-     * @param cursor source of data to copy. Ownership is reserved to the called, meaning
-     *               we won't ever close it.
-     */
-    InMemoryCursor(Cursor cursor, int offset, int length, int disposition) {
-        checkArgument(offset < cursor.getCount());
-
-        // NOTE: The cursor could simply be saved to a field, but we choose to wrap
-        // in a dedicated relay class to avoid hanging directly onto a reference
-        // to the cursor...so future authors are not enticed to think there's
-        // a live link between the delegate cursor and this cursor.
-        mObserverRelay = new ObserverRelay(cursor);
-
-        mColumnNames = cursor.getColumnNames();
-        mRowCount = Math.min(length, cursor.getCount() - offset);
-        int numColumns = cursor.getColumnCount();
-
-        mExtras = ContentPager.buildExtras(cursor.getExtras(), cursor.getCount(), disposition);
-
-        mColumnType = new int[numColumns];
-        mTypedColumnIndex = new int[NUM_TYPES][numColumns];
-        mColumnTypeCount = new int[NUM_TYPES];
-
-        if (!cursor.moveToFirst()) {
-            throw new RuntimeException("Can't position cursor to first row.");
-        }
-
-        for (int col = 0; col < numColumns; col++) {
-            int type = cursor.getType(col);
-            mColumnType[col] = type;
-            mTypedColumnIndex[type][col] = mColumnTypeCount[type]++;
-        }
-
-        mLongs = new long[mRowCount * mColumnTypeCount[FIELD_TYPE_INTEGER]];
-        mDoubles = new double[mRowCount * mColumnTypeCount[FIELD_TYPE_FLOAT]];
-        mBlobs = new byte[mRowCount * mColumnTypeCount[FIELD_TYPE_BLOB]][];
-        mStrings = new String[mRowCount * mColumnTypeCount[FIELD_TYPE_STRING]];
-
-        for (int row = 0; row < mRowCount; row++) {
-            if (!cursor.moveToPosition(offset + row)) {
-                throw new RuntimeException("Unable to position cursor.");
-            }
-
-            // Now copy data from the row into primitive arrays.
-            for (int col = 0; col < mColumnType.length; col++) {
-                int type = mColumnType[col];
-                int position = getCellPosition(row, col, type);
-
-                switch(type) {
-                    case FIELD_TYPE_NULL:
-                        throw new UnsupportedOperationException("Not implemented.");
-                    case FIELD_TYPE_INTEGER:
-                        mLongs[position] = cursor.getLong(col);
-                        break;
-                    case FIELD_TYPE_FLOAT:
-                        mDoubles[position] = cursor.getDouble(col);
-                        break;
-                    case FIELD_TYPE_BLOB:
-                        mBlobs[position] = cursor.getBlob(col);
-                        break;
-                    case FIELD_TYPE_STRING:
-                        mStrings[position] = cursor.getString(col);
-                        break;
-                }
-            }
-        }
-    }
-
-    @Override
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    // Returns the "cell" position for a specific row+column+type.
-    private int getCellPosition(int row,  int col, int type) {
-        return (row * mColumnTypeCount[type]) + mTypedColumnIndex[type][col];
-    }
-
-    @Override
-    public int getCount() {
-        return mRowCount;
-    }
-
-    @Override
-    public String[] getColumnNames() {
-        return mColumnNames;
-    }
-
-    @Override
-    public short getShort(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return (short) mLongs[getCellPosition(getPosition(), column, FIELD_TYPE_INTEGER)];
-    }
-
-    @Override
-    public int getInt(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return (int) mLongs[getCellPosition(getPosition(), column, FIELD_TYPE_INTEGER)];
-    }
-
-    @Override
-    public long getLong(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return mLongs[getCellPosition(getPosition(), column, FIELD_TYPE_INTEGER)];
-    }
-
-    @Override
-    public float getFloat(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return (float) mDoubles[getCellPosition(getPosition(), column, FIELD_TYPE_FLOAT)];
-    }
-
-    @Override
-    public double getDouble(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return mDoubles[getCellPosition(getPosition(), column, FIELD_TYPE_FLOAT)];
-    }
-
-    @Override
-    public byte[] getBlob(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return mBlobs[getCellPosition(getPosition(), column, FIELD_TYPE_BLOB)];
-    }
-
-    @Override
-    public String getString(int column) {
-        checkValidColumn(column);
-        checkValidPosition();
-        return mStrings[getCellPosition(getPosition(), column, FIELD_TYPE_STRING)];
-    }
-
-    @Override
-    public int getType(int column) {
-        checkValidColumn(column);
-        return mColumnType[column];
-    }
-
-    @Override
-    public boolean isNull(int column) {
-        checkValidColumn(column);
-        switch (mColumnType[column]) {
-            case FIELD_TYPE_STRING:
-                return getString(column) != null;
-            case FIELD_TYPE_BLOB:
-                return getBlob(column) != null;
-            default:
-                return false;
-        }
-    }
-
-    private void checkValidPosition() {
-        if (getPosition() < 0) {
-            throw new CursorIndexOutOfBoundsException("Before first row.");
-        }
-        if (getPosition() >= mRowCount) {
-            throw new CursorIndexOutOfBoundsException("After last row.");
-        }
-    }
-
-    private void checkValidColumn(int column) {
-        if (column < 0 || column >= mColumnNames.length) {
-            throw new CursorIndexOutOfBoundsException(
-                    "Requested column: " + column + ", # of columns: " + mColumnNames.length);
-        }
-    }
-
-    @Override
-    public void registerContentObserver(ContentObserver observer) {
-        mObserverRelay.registerContentObserver(observer);
-    }
-
-    @Override
-    public void unregisterContentObserver(ContentObserver observer) {
-        mObserverRelay.unregisterContentObserver(observer);
-    }
-
-    private static class ObserverRelay extends ContentObserver {
-        private final Cursor mCursor;
-
-        ObserverRelay(Cursor cursor) {
-            super(new Handler(Looper.getMainLooper()));
-            mCursor = cursor;
-        }
-
-        void registerContentObserver(ContentObserver observer) {
-            mCursor.registerContentObserver(observer);
-        }
-
-        void unregisterContentObserver(ContentObserver observer) {
-            mCursor.unregisterContentObserver(observer);
-        }
-    }
-}
diff --git a/android/support/content/LoaderQueryRunner.java b/android/support/content/LoaderQueryRunner.java
deleted file mode 100644
index 800307b..0000000
--- a/android/support/content/LoaderQueryRunner.java
+++ /dev/null
@@ -1,95 +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.content;
-
-import static android.support.v4.util.Preconditions.checkArgument;
-
-import android.app.LoaderManager;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.Context;
-import android.content.Loader;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-/**
- * A {@link ContentPager.QueryRunner} that executes queries using a {@link LoaderManager}.
- * Use this when preparing {@link ContentPager} to run in an Activity or Fragment scope.
- */
-public final class LoaderQueryRunner implements ContentPager.QueryRunner {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "LoaderQueryRunner";
-    private static final String CONTENT_URI_KEY = "contentUri";
-
-    private final Context mContext;
-    private final LoaderManager mLoaderMgr;
-
-    public LoaderQueryRunner(@NonNull Context context, @NonNull LoaderManager loaderMgr) {
-        mContext = context;
-        mLoaderMgr = loaderMgr;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")  // feels spurious. But can't commit line :80 w/o this.
-    public void query(final @NonNull Query query, @NonNull final Callback callback) {
-        if (DEBUG) Log.d(TAG, "Handling query: " + query);
-
-        LoaderCallbacks callbacks = new LoaderCallbacks<Cursor>() {
-            @Override
-            public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
-                if (DEBUG) Log.i(TAG, "Loading results for query: " + query);
-                checkArgument(id == query.getId(), "Id doesn't match query id.");
-
-                return new android.content.CursorLoader(mContext) {
-                    @Override
-                    public Cursor loadInBackground() {
-                        return callback.runQueryInBackground(query);
-                    }
-                };
-            }
-
-            @Override
-            public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
-                if (DEBUG) Log.i(TAG, "Finished loading: " + query);
-                mLoaderMgr.destroyLoader(query.getId());
-                callback.onQueryFinished(query, cursor);
-            }
-
-            @Override
-            public void onLoaderReset(Loader<Cursor> loader) {
-                if (DEBUG) Log.w(TAG, "Ignoring loader reset for query: " + query);
-            }
-        };
-
-        mLoaderMgr.restartLoader(query.getId(), null, callbacks);
-    }
-
-    @Override
-    public boolean isRunning(@NonNull Query query) {
-        Loader<Cursor> loader = mLoaderMgr.getLoader(query.getId());
-        return loader != null && loader.isStarted();
-        // Hmm, when exactly would the loader not be started? Does it imply that it will
-        // be starting at some point?
-    }
-
-    @Override
-    public void cancel(@NonNull Query query) {
-        mLoaderMgr.destroyLoader(query.getId());
-    }
-}
diff --git a/android/support/content/Query.java b/android/support/content/Query.java
deleted file mode 100644
index a5d1ee5..0000000
--- a/android/support/content/Query.java
+++ /dev/null
@@ -1,194 +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.content;
-
-import static android.support.v4.util.Preconditions.checkArgument;
-
-import android.content.ContentResolver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.util.Log;
-
-import java.util.Arrays;
-
-/**
- * Encapsulates information related to calling {@link ContentResolver#query},
- * including the logic determining the best query method to call.
- */
-public final class Query {
-
-    private static final boolean DEBUG = true;
-    private static final String TAG = "Query";
-
-    private final Uri mUri;
-    private final @Nullable String[] mProjection;
-    private final Bundle mQueryArgs;
-
-    private final int mId;
-    private final int mOffset;
-    private final int mLimit;
-
-    private final CancellationSignal mCancellationSignal;
-    private final ContentPager.ContentCallback mCallback;
-
-    @VisibleForTesting
-    Query(
-            @NonNull Uri uri,
-            @Nullable String[] projection,
-            @NonNull Bundle args,
-            @Nullable CancellationSignal cancellationSignal,
-            @NonNull ContentPager.ContentCallback callback) {
-
-        checkArgument(uri != null);
-        checkArgument(args != null);
-        checkArgument(callback != null);
-
-        this.mUri = uri;
-        this.mProjection = projection;
-        this.mQueryArgs = args;
-        this.mCancellationSignal = cancellationSignal;
-        this.mCallback = callback;
-
-        this.mOffset = args.getInt(ContentPager.QUERY_ARG_OFFSET, -1);
-        this.mLimit = args.getInt(ContentPager.QUERY_ARG_LIMIT, -1);
-
-        // NOTE: We omit mProjection and other details from ID. If a client wishes
-        // to request a page with a different mProjection or sorting, they should
-        // wait for first request to finish. Same goes for mCallback.
-        this.mId = uri.hashCode() << 16 | (mOffset | (mLimit << 8));
-
-        checkArgument(mOffset >= 0);  // mOffset must be set, mLimit is optional.
-    }
-
-    /**
-     * @return the id for this query. Derived from Uri as well as paging arguments.
-     */
-    public int getId() {
-        return mId;
-    }
-
-    /**
-     * @return the Uri.
-     */
-    public @NonNull Uri getUri() {
-        return mUri;
-    }
-
-    /**
-     * @return the offset.
-     */
-    public int getOffset() {
-        return mOffset;
-    }
-
-    /**
-     * @return the limit.
-     */
-    public int getLimit() {
-        return mLimit;
-    }
-
-    @NonNull ContentPager.ContentCallback getCallback() {
-        return mCallback;
-    }
-
-    @Nullable Cursor run(@NonNull ContentResolver resolver) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            return resolver.query(
-                    mUri,
-                    mProjection,
-                    mQueryArgs,
-                    mCancellationSignal);
-        }
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-            if (DEBUG) Log.d(TAG, "Falling back to pre-O query method.");
-            return resolver.query(
-                    mUri,
-                    mProjection,
-                    null,
-                    null,
-                    null,
-                    mCancellationSignal);
-        }
-
-        if (DEBUG) Log.d(TAG, "Falling back to pre-jellybean query method.");
-        return resolver.query(
-                mUri,
-                mProjection,
-                null,
-                null,
-                null);
-    }
-
-    void cancel() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-            if (mCancellationSignal != null && !mCancellationSignal.isCanceled()) {
-                if (DEBUG) {
-                    Log.d(TAG, "Attemping to cancel query provider processings: " + this);
-                }
-                mCancellationSignal.cancel();
-            }
-        }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-
-        if (this == obj) {
-            return true;
-        }
-
-        if (!(obj instanceof Query)) {
-            return false;
-        }
-
-        Query other = (Query) obj;
-
-        return mId == other.mId
-                && mUri.equals(other.mUri)
-                && mOffset == other.mOffset
-                && mLimit == other.mLimit;
-    }
-
-    @Override
-    public int hashCode() {
-        return getId();
-    }
-
-    @Override
-    public String toString() {
-        return "Query{"
-                + "id:" + mId
-                + " uri:" + mUri
-                + " projection:" + Arrays.toString(mProjection)
-                + " offset:" + mOffset
-                + " limit:" + mLimit
-                + " cancellationSignal:" + mCancellationSignal
-                + " callback:" + mCallback
-                + "}";
-    }
-}
diff --git a/android/support/customtabs/CustomTabsCallback.java b/android/support/customtabs/CustomTabsCallback.java
deleted file mode 100644
index f8d349a..0000000
--- a/android/support/customtabs/CustomTabsCallback.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.customtabs;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.customtabs.CustomTabsService.Relation;
-
-/**
- * A callback class for custom tabs client to get messages regarding events in their custom tabs. In
- * the implementation, all callbacks are sent to the UI thread for the client.
- */
-public class CustomTabsCallback {
-    /**
-     * Sent when the tab has started loading a page.
-     */
-    public static final int NAVIGATION_STARTED = 1;
-
-    /**
-     * Sent when the tab has finished loading a page.
-     */
-    public static final int NAVIGATION_FINISHED = 2;
-
-    /**
-     * Sent when the tab couldn't finish loading due to a failure.
-     */
-    public static final int NAVIGATION_FAILED = 3;
-
-    /**
-     * Sent when loading was aborted by a user action before it finishes like clicking on a link
-     * or refreshing the page.
-     */
-    public static final int NAVIGATION_ABORTED = 4;
-
-    /**
-     * Sent when the tab becomes visible.
-     */
-    public static final int TAB_SHOWN = 5;
-
-    /**
-     * Sent when the tab becomes hidden.
-     */
-    public static final int TAB_HIDDEN = 6;
-
-    /**
-     * To be called when a navigation event happens.
-     *
-     * @param navigationEvent The code corresponding to the navigation event.
-     * @param extras Reserved for future use.
-     */
-    public void onNavigationEvent(int navigationEvent, Bundle extras) {}
-
-    /**
-     * Unsupported callbacks that may be provided by the implementation.
-     *
-     * <p>
-     * <strong>Note:</strong>Clients should <strong>never</strong> rely on this callback to be
-     * called and/or to have a defined behavior, as it is entirely implementation-defined and not
-     * supported.
-     *
-     * <p> This can be used by implementations to add extra callbacks, for testing or experimental
-     * purposes.
-     *
-     * @param callbackName Name of the extra callback.
-     * @param args Arguments for the calback
-     */
-    public void extraCallback(String callbackName, Bundle args) {}
-
-    /**
-     * Called when {@link CustomTabsSession} has requested a postMessage channel through
-     * {@link CustomTabsService#requestPostMessageChannel(
-     * CustomTabsSessionToken, android.net.Uri)} and the channel
-     * is ready for sending and receiving messages on both ends.
-     *
-     * @param extras Reserved for future use.
-     */
-    public void onMessageChannelReady(Bundle extras) {}
-
-    /**
-     * Called when a tab controlled by this {@link CustomTabsSession} has sent a postMessage.
-     * If postMessage() is called from a single thread, then the messages will be posted in the
-     * same order. When received on the client side, it is the client's responsibility to preserve
-     * the ordering further.
-     *
-     * @param message The message sent.
-     * @param extras Reserved for future use.
-     */
-    public void onPostMessage(String message, Bundle extras) {}
-
-    /**
-     * Called when a relationship validation result is available.
-     *
-     * @param relation Relation for which the result is available. Value previously passed to
-     *                 {@link CustomTabsSession#validateRelationship(int, Uri, Bundle)}. Must be one
-     *                 of the {@code CustomTabsService#RELATION_* } constants.
-     * @param requestedOrigin Origin requested. Value previously passed to
-     *                        {@link CustomTabsSession#validateRelationship(int, Uri, Bundle)}.
-     * @param result Whether the relation was validated.
-     * @param extras Reserved for future use.
-     */
-    public void onRelationshipValidationResult(@Relation int relation, Uri requestedOrigin,
-                                               boolean result, Bundle extras) {}
-}
diff --git a/android/support/customtabs/CustomTabsClient.java b/android/support/customtabs/CustomTabsClient.java
deleted file mode 100644
index 371b5a1..0000000
--- a/android/support/customtabs/CustomTabsClient.java
+++ /dev/null
@@ -1,269 +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.customtabs;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.customtabs.CustomTabsService.Relation;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class to communicate with a {@link CustomTabsService} and create
- * {@link CustomTabsSession} from it.
- */
-public class CustomTabsClient {
-    private final ICustomTabsService mService;
-    private final ComponentName mServiceComponentName;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    CustomTabsClient(ICustomTabsService service, ComponentName componentName) {
-        mService = service;
-        mServiceComponentName = componentName;
-    }
-
-    /**
-     * Bind to a {@link CustomTabsService} using the given package name and
-     * {@link ServiceConnection}.
-     * @param context     {@link Context} to use while calling
-     *                    {@link Context#bindService(Intent, ServiceConnection, int)}
-     * @param packageName Package name to set on the {@link Intent} for binding.
-     * @param connection  {@link CustomTabsServiceConnection} to use when binding. This will
-     *                    return a {@link CustomTabsClient} on
-     *                    {@link CustomTabsServiceConnection
-     *                    #onCustomTabsServiceConnected(ComponentName, CustomTabsClient)}
-     * @return Whether the binding was successful.
-     */
-    public static boolean bindCustomTabsService(Context context,
-            String packageName, CustomTabsServiceConnection connection) {
-        Intent intent = new Intent(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
-        if (!TextUtils.isEmpty(packageName)) intent.setPackage(packageName);
-        return context.bindService(intent, connection,
-                Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY);
-    }
-
-    /**
-     * Returns the preferred package to use for Custom Tabs, preferring the default VIEW handler.
-     *
-     * @see #getPackageName(Context, List<String>, boolean)
-     */
-    public static String getPackageName(Context context, @Nullable List<String> packages) {
-        return getPackageName(context, packages, false);
-    }
-
-    /**
-     * Returns the preferred package to use for Custom Tabs.
-     *
-     * The preferred package name is the default VIEW intent handler as long as it supports Custom
-     * Tabs. To modify this preferred behavior, set <code>ignoreDefault</code> to true and give a
-     * non empty list of package names in <code>packages</code>.
-     *
-     * @param context       {@link Context} to use for querying the packages.
-     * @param packages      Ordered list of packages to test for Custom Tabs support, in
-     *                      decreasing order of priority.
-     * @param ignoreDefault If set, the default VIEW handler won't get priority over other browsers.
-     * @return The preferred package name for handling Custom Tabs, or <code>null</code>.
-     */
-    public static String getPackageName(
-        Context context, @Nullable List<String> packages, boolean ignoreDefault) {
-        PackageManager pm = context.getPackageManager();
-
-        List<String> packageNames = packages == null ? new ArrayList<String>() : packages;
-        Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://"));
-
-        if (!ignoreDefault) {
-            ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
-            if (defaultViewHandlerInfo != null) {
-                String packageName = defaultViewHandlerInfo.activityInfo.packageName;
-                packageNames = new ArrayList<String>(packageNames.size() + 1);
-                packageNames.add(packageName);
-                if (packages != null) packageNames.addAll(packages);
-            }
-        }
-
-        Intent serviceIntent = new Intent(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
-        for (String packageName : packageNames) {
-            serviceIntent.setPackage(packageName);
-            if (pm.resolveService(serviceIntent, 0) != null) return packageName;
-        }
-        return null;
-    }
-
-    /**
-     * Connects to the Custom Tabs warmup service, and initializes the browser.
-     *
-     * This convenience method connects to the service, and immediately warms up the Custom Tabs
-     * implementation. Since service connection is asynchronous, the return code is not the return
-     * code of warmup.
-     * This call is optional, and clients are encouraged to connect to the service, call
-     * <code>warmup()</code> and create a session. In this case, calling this method is not
-     * necessary.
-     *
-     * @param context     {@link Context} to use to connect to the remote service.
-     * @param packageName Package name of the target implementation.
-     * @return Whether the binding was successful.
-     */
-    public static boolean connectAndInitialize(Context context, String packageName) {
-        if (packageName == null) return false;
-        final Context applicationContext = context.getApplicationContext();
-        CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
-            @Override
-            public final void onCustomTabsServiceConnected(
-                    ComponentName name, CustomTabsClient client) {
-                client.warmup(0);
-                // Unbinding immediately makes the target process "Empty", provided that it is
-                // not used by anyone else, and doesn't contain any Activity. This makes it
-                // likely to get killed, but is preferable to keeping the connection around.
-                applicationContext.unbindService(this);
-            }
-
-           @Override
-           public final void onServiceDisconnected(ComponentName componentName) { }
-        };
-        try {
-            return bindCustomTabsService(applicationContext, packageName, connection);
-        } catch (SecurityException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Warm up the browser process.
-     *
-     * Allows the browser application to pre-initialize itself in the background. Significantly
-     * speeds up URL opening in the browser. This is asynchronous and can be called several times.
-     *
-     * @param flags Reserved for future use.
-     * @return      Whether the warmup was successful.
-     */
-    public boolean warmup(long flags) {
-        try {
-            return mService.warmup(flags);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Creates a new session through an ICustomTabsService with the optional callback. This session
-     * can be used to associate any related communication through the service with an intent and
-     * then later with a Custom Tab. The client can then send later service calls or intents to
-     * through same session-intent-Custom Tab association.
-     * @param callback The callback through which the client will receive updates about the created
-     *                 session. Can be null. All the callbacks will be received on the UI thread.
-     * @return The session object that was created as a result of the transaction. The client can
-     *         use this to relay session specific calls.
-     *         Null on error.
-     */
-    public CustomTabsSession newSession(final CustomTabsCallback callback) {
-        ICustomTabsCallback.Stub wrapper = new ICustomTabsCallback.Stub() {
-            private Handler mHandler = new Handler(Looper.getMainLooper());
-
-            @Override
-            public void onNavigationEvent(final int navigationEvent, final Bundle extras) {
-                if (callback == null) return;
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onNavigationEvent(navigationEvent, extras);
-                    }
-                });
-            }
-
-            @Override
-            public void extraCallback(final String callbackName, final Bundle args)
-                    throws RemoteException {
-                if (callback == null) return;
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.extraCallback(callbackName, args);
-                    }
-                });
-            }
-
-            @Override
-            public void onMessageChannelReady(final Bundle extras)
-                    throws RemoteException {
-                if (callback == null) return;
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onMessageChannelReady(extras);
-                    }
-                });
-            }
-
-            @Override
-            public void onPostMessage(final String message, final Bundle extras)
-                    throws RemoteException {
-                if (callback == null) return;
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onPostMessage(message, extras);
-                    }
-                });
-            }
-
-            @Override
-            public void onRelationshipValidationResult(
-                    final @Relation int relation, final Uri requestedOrigin, final boolean result,
-                    final @Nullable Bundle extras) throws RemoteException {
-                if (callback == null) return;
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        callback.onRelationshipValidationResult(
-                                relation, requestedOrigin, result, extras);
-                    }
-                });
-            }
-        };
-
-        try {
-            if (!mService.newSession(wrapper)) return null;
-        } catch (RemoteException e) {
-            return null;
-        }
-        return new CustomTabsSession(mService, wrapper, mServiceComponentName);
-    }
-
-    public Bundle extraCommand(String commandName, Bundle args) {
-        try {
-            return mService.extraCommand(commandName, args);
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-}
diff --git a/android/support/customtabs/CustomTabsIntent.java b/android/support/customtabs/CustomTabsIntent.java
deleted file mode 100644
index 009182a..0000000
--- a/android/support/customtabs/CustomTabsIntent.java
+++ /dev/null
@@ -1,562 +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.customtabs;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.AnimRes;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.ActivityOptionsCompat;
-import android.support.v4.app.BundleCompat;
-import android.support.v4.content.ContextCompat;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import java.util.ArrayList;
-
-/**
- * Class holding the {@link Intent} and start bundle for a Custom Tabs Activity.
- *
- * <p>
- * <strong>Note:</strong> The constants below are public for the browser implementation's benefit.
- * You are strongly encouraged to use {@link CustomTabsIntent.Builder}.</p>
- */
-public final class CustomTabsIntent {
-
-    /**
-     * Indicates that the user explicitly opted out of Custom Tabs in the calling application.
-     * <p>
-     * If an application provides a mechanism for users to opt out of Custom Tabs, this extra should
-     * be provided with {@link Intent#FLAG_ACTIVITY_NEW_TASK} to ensure the browser does not attempt
-     * to trigger any Custom Tab-like experiences as a result of the VIEW intent.
-     * <p>
-     * If this extra is present with {@link Intent#FLAG_ACTIVITY_NEW_TASK}, all Custom Tabs
-     * customizations will be ignored.
-     */
-    private static final String EXTRA_USER_OPT_OUT_FROM_CUSTOM_TABS =
-            "android.support.customtabs.extra.user_opt_out";
-
-    /**
-     * Extra used to match the session. This has to be included in the intent to open in
-     * a custom tab. This is the same IBinder that gets passed to ICustomTabsService#newSession.
-     * Null if there is no need to match any service side sessions with the intent.
-     */
-    public static final String EXTRA_SESSION = "android.support.customtabs.extra.SESSION";
-
-    /**
-     * Extra that changes the background color for the toolbar. colorRes is an int that specifies a
-     * {@link Color}, not a resource id.
-     */
-    public static final String EXTRA_TOOLBAR_COLOR =
-            "android.support.customtabs.extra.TOOLBAR_COLOR";
-
-    /**
-     * Boolean extra that enables the url bar to hide as the user scrolls down the page
-     */
-    public static final String EXTRA_ENABLE_URLBAR_HIDING =
-            "android.support.customtabs.extra.ENABLE_URLBAR_HIDING";
-
-    /**
-     * Extra bitmap that specifies the icon of the back button on the toolbar. If the client chooses
-     * not to customize it, a default close button will be used.
-     */
-    public static final String EXTRA_CLOSE_BUTTON_ICON =
-            "android.support.customtabs.extra.CLOSE_BUTTON_ICON";
-
-    /**
-     * Extra (int) that specifies state for showing the page title. Default is {@link #NO_TITLE}.
-     */
-    public static final String EXTRA_TITLE_VISIBILITY_STATE =
-            "android.support.customtabs.extra.TITLE_VISIBILITY";
-
-    /**
-     * Don't show any title. Shows only the domain.
-     */
-    public static final int NO_TITLE = 0;
-
-    /**
-     * Shows the page title and the domain.
-     */
-    public static final int SHOW_PAGE_TITLE = 1;
-
-    /**
-     * Bundle used for adding a custom action button to the custom tab toolbar. The client should
-     * provide a description, an icon {@link Bitmap} and a {@link PendingIntent} for the button.
-     * All three keys must be present.
-     */
-    public static final String EXTRA_ACTION_BUTTON_BUNDLE =
-            "android.support.customtabs.extra.ACTION_BUTTON_BUNDLE";
-
-    /**
-     * List<Bundle> used for adding items to the top and bottom toolbars. The client should
-     * provide an ID, a description, an icon {@link Bitmap} for each item. They may also provide a
-     * {@link PendingIntent} if the item is a button.
-     */
-    public static final String EXTRA_TOOLBAR_ITEMS =
-            "android.support.customtabs.extra.TOOLBAR_ITEMS";
-
-    /**
-     * Extra that changes the background color for the secondary toolbar. The value should be an
-     * int that specifies a {@link Color}, not a resource id.
-     */
-    public static final String EXTRA_SECONDARY_TOOLBAR_COLOR =
-            "android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR";
-
-    /**
-     * Key that specifies the {@link Bitmap} to be used as the image source for the action button.
-     *  The icon should't be more than 24dp in height (No padding needed. The button itself will be
-     *  48dp in height) and have a width/height ratio of less than 2.
-     */
-    public static final String KEY_ICON = "android.support.customtabs.customaction.ICON";
-
-    /**
-     * Key that specifies the content description for the custom action button.
-     */
-    public static final String KEY_DESCRIPTION =
-            "android.support.customtabs.customaction.DESCRIPTION";
-
-    /**
-     * Key that specifies the PendingIntent to launch when the action button or menu item was
-     * clicked. The custom tab will be calling {@link PendingIntent#send()} on clicks after adding
-     * the url as data. The client app can call {@link Intent#getDataString()} to get the url.
-     */
-    public static final String KEY_PENDING_INTENT =
-            "android.support.customtabs.customaction.PENDING_INTENT";
-
-    /**
-     * Extra boolean that specifies whether the custom action button should be tinted. Default is
-     * false and the action button will not be tinted.
-     */
-    public static final String EXTRA_TINT_ACTION_BUTTON =
-            "android.support.customtabs.extra.TINT_ACTION_BUTTON";
-
-    /**
-     * Use an {@code ArrayList<Bundle>} for specifying menu related params. There should be a
-     * separate {@link Bundle} for each custom menu item.
-     */
-    public static final String EXTRA_MENU_ITEMS = "android.support.customtabs.extra.MENU_ITEMS";
-
-    /**
-     * Key for specifying the title of a menu item.
-     */
-    public static final String KEY_MENU_ITEM_TITLE =
-            "android.support.customtabs.customaction.MENU_ITEM_TITLE";
-
-    /**
-     * Bundle constructed out of {@link ActivityOptionsCompat} that will be running when the
-     * {@link Activity} that holds the custom tab gets finished. A similar ActivityOptions
-     * for creation should be constructed and given to the startActivity() call that
-     * launches the custom tab.
-     */
-    public static final String EXTRA_EXIT_ANIMATION_BUNDLE =
-            "android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE";
-
-    /**
-     * Boolean extra that specifies whether a default share button will be shown in the menu.
-     */
-    public static final String EXTRA_DEFAULT_SHARE_MENU_ITEM =
-            "android.support.customtabs.extra.SHARE_MENU_ITEM";
-
-    /**
-     * Extra that specifies the {@link RemoteViews} showing on the secondary toolbar. If this extra
-     * is set, the other secondary toolbar configurations will be overriden. The height of the
-     * {@link RemoteViews} should not exceed 56dp.
-     * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
-     */
-    public static final String EXTRA_REMOTEVIEWS =
-            "android.support.customtabs.extra.EXTRA_REMOTEVIEWS";
-
-    /**
-     * Extra that specifies an array of {@link View} ids. When these {@link View}s are clicked, a
-     * {@link PendingIntent} will be sent, carrying the current url of the custom tab as data.
-     * <p>
-     * Note that Custom Tabs will override the default onClick behavior of the listed {@link View}s.
-     * If you do not care about the current url, you can safely ignore this extra and use
-     * {@link RemoteViews#setOnClickPendingIntent(int, PendingIntent)} instead.
-     * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
-     */
-    public static final String EXTRA_REMOTEVIEWS_VIEW_IDS =
-            "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS";
-
-    /**
-     * Extra that specifies the {@link PendingIntent} to be sent when the user clicks on the
-     * {@link View}s that is listed by {@link #EXTRA_REMOTEVIEWS_VIEW_IDS}.
-     * <p>
-     * Note when this {@link PendingIntent} is triggered, it will have the current url as data
-     * field, also the id of the clicked {@link View}, specified by
-     * {@link #EXTRA_REMOTEVIEWS_CLICKED_ID}.
-     * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
-     */
-    public static final String EXTRA_REMOTEVIEWS_PENDINGINTENT =
-            "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT";
-
-    /**
-     * Extra that specifies which {@link View} has been clicked. This extra will be put to the
-     * {@link PendingIntent} sent from Custom Tabs when a view in the {@link RemoteViews} is clicked
-     * @see CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
-     */
-    public static final String EXTRA_REMOTEVIEWS_CLICKED_ID =
-            "android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID";
-
-    /**
-     * Extra that specifies whether Instant Apps is enabled.
-     */
-    public static final String EXTRA_ENABLE_INSTANT_APPS =
-            "android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS";
-
-    /**
-     * Key that specifies the unique ID for an action button. To make a button to show on the
-     * toolbar, use {@link #TOOLBAR_ACTION_BUTTON_ID} as its ID.
-     */
-    public static final String KEY_ID = "android.support.customtabs.customaction.ID";
-
-    /**
-     * The ID allocated to the custom action button that is shown on the toolbar.
-     */
-    public static final int TOOLBAR_ACTION_BUTTON_ID = 0;
-
-    /**
-     * The maximum allowed number of toolbar items.
-     */
-    private static final int MAX_TOOLBAR_ITEMS = 5;
-
-    /**
-     * An {@link Intent} used to start the Custom Tabs Activity.
-     */
-    @NonNull public final Intent intent;
-
-    /**
-     * A {@link Bundle} containing the start animation for the Custom Tabs Activity.
-     */
-    @Nullable public final Bundle startAnimationBundle;
-
-    /**
-     * Convenience method to launch a Custom Tabs Activity.
-     * @param context The source Context.
-     * @param url The URL to load in the Custom Tab.
-     */
-    public void launchUrl(Context context, Uri url) {
-        intent.setData(url);
-        ContextCompat.startActivity(context, intent, startAnimationBundle);
-    }
-
-    private CustomTabsIntent(Intent intent, Bundle startAnimationBundle) {
-        this.intent = intent;
-        this.startAnimationBundle = startAnimationBundle;
-    }
-
-    /**
-     * Builder class for {@link CustomTabsIntent} objects.
-     */
-    public static final class Builder {
-        private final Intent mIntent = new Intent(Intent.ACTION_VIEW);
-        private ArrayList<Bundle> mMenuItems = null;
-        private Bundle mStartAnimationBundle = null;
-        private ArrayList<Bundle> mActionButtons = null;
-        private boolean mInstantAppsEnabled = true;
-
-        /**
-         * Creates a {@link CustomTabsIntent.Builder} object associated with no
-         * {@link CustomTabsSession}.
-         */
-        public Builder() {
-            this(null);
-        }
-
-        /**
-         * Creates a {@link CustomTabsIntent.Builder} object associated with a given
-         * {@link CustomTabsSession}.
-         *
-         * Guarantees that the {@link Intent} will be sent to the same component as the one the
-         * session is associated with.
-         *
-         * @param session The session to associate this Builder with.
-         */
-        public Builder(@Nullable CustomTabsSession session) {
-            if (session != null) mIntent.setPackage(session.getComponentName().getPackageName());
-            Bundle bundle = new Bundle();
-            BundleCompat.putBinder(
-                    bundle, EXTRA_SESSION, session == null ? null : session.getBinder());
-            mIntent.putExtras(bundle);
-        }
-
-        /**
-         * Sets the toolbar color.
-         *
-         * @param color {@link Color}
-         */
-        public Builder setToolbarColor(@ColorInt int color) {
-            mIntent.putExtra(EXTRA_TOOLBAR_COLOR, color);
-            return this;
-        }
-
-        /**
-         * Enables the url bar to hide as the user scrolls down on the page.
-         */
-        public Builder enableUrlBarHiding() {
-            mIntent.putExtra(EXTRA_ENABLE_URLBAR_HIDING, true);
-            return this;
-        }
-
-        /**
-         * Sets the Close button icon for the custom tab.
-         *
-         * @param icon The icon {@link Bitmap}
-         */
-        public Builder setCloseButtonIcon(@NonNull Bitmap icon) {
-            mIntent.putExtra(EXTRA_CLOSE_BUTTON_ICON, icon);
-            return this;
-        }
-
-        /**
-         * Sets whether the title should be shown in the custom tab.
-         *
-         * @param showTitle Whether the title should be shown.
-         */
-        public Builder setShowTitle(boolean showTitle) {
-            mIntent.putExtra(EXTRA_TITLE_VISIBILITY_STATE,
-                    showTitle ? SHOW_PAGE_TITLE : NO_TITLE);
-            return this;
-        }
-
-        /**
-         * Adds a menu item.
-         *
-         * @param label Menu label.
-         * @param pendingIntent Pending intent delivered when the menu item is clicked.
-         */
-        public Builder addMenuItem(@NonNull String label, @NonNull PendingIntent pendingIntent) {
-            if (mMenuItems == null) mMenuItems = new ArrayList<>();
-            Bundle bundle = new Bundle();
-            bundle.putString(KEY_MENU_ITEM_TITLE, label);
-            bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
-            mMenuItems.add(bundle);
-            return this;
-        }
-
-        /**
-         * Adds a default share item to the menu.
-         */
-        public Builder addDefaultShareMenuItem() {
-            mIntent.putExtra(EXTRA_DEFAULT_SHARE_MENU_ITEM, true);
-            return this;
-        }
-
-        /**
-         * Sets the action button that is displayed in the Toolbar.
-         * <p>
-         * This is equivalent to calling
-         * {@link CustomTabsIntent.Builder#addToolbarItem(int, Bitmap, String, PendingIntent)}
-         * with {@link #TOOLBAR_ACTION_BUTTON_ID} as id.
-         *
-         * @param icon The icon.
-         * @param description The description for the button. To be used for accessibility.
-         * @param pendingIntent pending intent delivered when the button is clicked.
-         * @param shouldTint Whether the action button should be tinted.
-         *
-         * @see CustomTabsIntent.Builder#addToolbarItem(int, Bitmap, String, PendingIntent)
-         */
-        public Builder setActionButton(@NonNull Bitmap icon, @NonNull String description,
-                @NonNull PendingIntent pendingIntent, boolean shouldTint) {
-            Bundle bundle = new Bundle();
-            bundle.putInt(KEY_ID, TOOLBAR_ACTION_BUTTON_ID);
-            bundle.putParcelable(KEY_ICON, icon);
-            bundle.putString(KEY_DESCRIPTION, description);
-            bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
-            mIntent.putExtra(EXTRA_ACTION_BUTTON_BUNDLE, bundle);
-            mIntent.putExtra(EXTRA_TINT_ACTION_BUTTON, shouldTint);
-            return this;
-        }
-
-        /**
-         * Sets the action button that is displayed in the Toolbar with default tinting behavior.
-         *
-         * @see CustomTabsIntent.Builder#setActionButton(
-         * Bitmap, String, PendingIntent, boolean)
-         */
-        public Builder setActionButton(@NonNull Bitmap icon, @NonNull String description,
-                @NonNull PendingIntent pendingIntent) {
-            return setActionButton(icon, description, pendingIntent, false);
-        }
-
-        /**
-         * Adds an action button to the custom tab. Multiple buttons can be added via this method.
-         * If the given id equals {@link #TOOLBAR_ACTION_BUTTON_ID}, the button will be placed on
-         * the toolbar; if the bitmap is too wide, it will be put to the bottom bar instead. If
-         * the id is not {@link #TOOLBAR_ACTION_BUTTON_ID}, it will be directly put on secondary
-         * toolbar. The maximum number of allowed toolbar items in a single intent is
-         * {@link CustomTabsIntent#getMaxToolbarItems()}. Throws an
-         * {@link IllegalStateException} when that number is exceeded per intent.
-         *
-         * @param id The unique id of the action button. This should be non-negative.
-         * @param icon The icon.
-         * @param description The description for the button. To be used for accessibility.
-         * @param pendingIntent The pending intent delivered when the button is clicked.
-         *
-         * @see CustomTabsIntent#getMaxToolbarItems()
-         * @deprecated Use
-         * CustomTabsIntent.Builder#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent).
-         */
-        @Deprecated
-        public Builder addToolbarItem(int id, @NonNull Bitmap icon, @NonNull String description,
-                PendingIntent pendingIntent) throws IllegalStateException {
-            if (mActionButtons == null) {
-                mActionButtons = new ArrayList<>();
-            }
-            if (mActionButtons.size() >= MAX_TOOLBAR_ITEMS) {
-                throw new IllegalStateException(
-                        "Exceeded maximum toolbar item count of " + MAX_TOOLBAR_ITEMS);
-            }
-            Bundle bundle = new Bundle();
-            bundle.putInt(KEY_ID, id);
-            bundle.putParcelable(KEY_ICON, icon);
-            bundle.putString(KEY_DESCRIPTION, description);
-            bundle.putParcelable(KEY_PENDING_INTENT, pendingIntent);
-            mActionButtons.add(bundle);
-            return this;
-        }
-
-        /**
-         * Sets the color of the secondary toolbar.
-         * @param color The color for the secondary toolbar.
-         */
-        public Builder setSecondaryToolbarColor(@ColorInt int color) {
-            mIntent.putExtra(EXTRA_SECONDARY_TOOLBAR_COLOR, color);
-            return this;
-        }
-
-        /**
-         * Sets the remote views displayed in the secondary toolbar in a custom tab.
-         *
-         * @param remoteViews   The {@link RemoteViews} that will be shown on the secondary toolbar.
-         * @param clickableIDs  The IDs of clickable views. The onClick event of these views will be
-         *                      handled by custom tabs.
-         * @param pendingIntent The {@link PendingIntent} that will be sent when the user clicks on
-         *                      one of the {@link View}s in clickableIDs. When the
-         *                      {@link PendingIntent} is sent, it will have the current URL as its
-         *                      intent data.
-         * @see CustomTabsIntent#EXTRA_REMOTEVIEWS
-         * @see CustomTabsIntent#EXTRA_REMOTEVIEWS_VIEW_IDS
-         * @see CustomTabsIntent#EXTRA_REMOTEVIEWS_PENDINGINTENT
-         * @see CustomTabsIntent#EXTRA_REMOTEVIEWS_CLICKED_ID
-         */
-        public Builder setSecondaryToolbarViews(@NonNull RemoteViews remoteViews,
-                @Nullable int[] clickableIDs, @Nullable PendingIntent pendingIntent) {
-            mIntent.putExtra(EXTRA_REMOTEVIEWS, remoteViews);
-            mIntent.putExtra(EXTRA_REMOTEVIEWS_VIEW_IDS, clickableIDs);
-            mIntent.putExtra(EXTRA_REMOTEVIEWS_PENDINGINTENT, pendingIntent);
-            return this;
-        }
-
-        /**
-         * Sets whether Instant Apps is enabled for this Custom Tab.
-
-         * @param enabled Whether Instant Apps should be enabled.
-         */
-        public Builder setInstantAppsEnabled(boolean enabled) {
-            mInstantAppsEnabled = enabled;
-            return this;
-        }
-
-        /**
-         * Sets the start animations.
-         *
-         * @param context Application context.
-         * @param enterResId Resource ID of the "enter" animation for the browser.
-         * @param exitResId Resource ID of the "exit" animation for the application.
-         */
-        public Builder setStartAnimations(
-                @NonNull Context context, @AnimRes int enterResId, @AnimRes int exitResId) {
-            mStartAnimationBundle = ActivityOptionsCompat.makeCustomAnimation(
-                    context, enterResId, exitResId).toBundle();
-            return this;
-        }
-
-        /**
-         * Sets the exit animations.
-         *
-         * @param context Application context.
-         * @param enterResId Resource ID of the "enter" animation for the application.
-         * @param exitResId Resource ID of the "exit" animation for the browser.
-         */
-        public Builder setExitAnimations(
-                @NonNull Context context, @AnimRes int enterResId, @AnimRes int exitResId) {
-            Bundle bundle = ActivityOptionsCompat.makeCustomAnimation(
-                    context, enterResId, exitResId).toBundle();
-            mIntent.putExtra(EXTRA_EXIT_ANIMATION_BUNDLE, bundle);
-            return this;
-        }
-
-        /**
-         * Combines all the options that have been set and returns a new {@link CustomTabsIntent}
-         * object.
-         */
-        public CustomTabsIntent build() {
-            if (mMenuItems != null) {
-                mIntent.putParcelableArrayListExtra(CustomTabsIntent.EXTRA_MENU_ITEMS, mMenuItems);
-            }
-            if (mActionButtons != null) {
-                mIntent.putParcelableArrayListExtra(EXTRA_TOOLBAR_ITEMS, mActionButtons);
-            }
-            mIntent.putExtra(EXTRA_ENABLE_INSTANT_APPS, mInstantAppsEnabled);
-            return new CustomTabsIntent(mIntent, mStartAnimationBundle);
-        }
-    }
-
-    /**
-     * @return The maximum number of allowed toolbar items for
-     * {@link CustomTabsIntent.Builder#addToolbarItem(int, Bitmap, String, PendingIntent)} and
-     * {@link CustomTabsIntent#EXTRA_TOOLBAR_ITEMS}.
-     */
-    public static int getMaxToolbarItems() {
-        return MAX_TOOLBAR_ITEMS;
-    }
-
-    /**
-     * Adds the necessary flags and extras to signal any browser supporting custom tabs to use the
-     * browser UI at all times and avoid showing custom tab like UI. Calling this with an intent
-     * will override any custom tabs related customizations.
-     * @param intent The intent to modify for always showing browser UI.
-     * @return The same intent with the necessary flags and extras added.
-     */
-    public static Intent setAlwaysUseBrowserUI(Intent intent) {
-        if (intent == null) intent = new Intent(Intent.ACTION_VIEW);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra(EXTRA_USER_OPT_OUT_FROM_CUSTOM_TABS, true);
-        return intent;
-    }
-
-    /**
-     * Whether a browser receiving the given intent should always use browser UI and avoid using any
-     * custom tabs UI.
-     *
-     * @param intent The intent to check for the required flags and extras.
-     * @return Whether the browser UI should be used exclusively.
-     */
-    public static boolean shouldAlwaysUseBrowserUI(Intent intent) {
-        return intent.getBooleanExtra(EXTRA_USER_OPT_OUT_FROM_CUSTOM_TABS, false)
-                && (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0;
-    }
-}
diff --git a/android/support/customtabs/CustomTabsService.java b/android/support/customtabs/CustomTabsService.java
deleted file mode 100644
index aad174c..0000000
--- a/android/support/customtabs/CustomTabsService.java
+++ /dev/null
@@ -1,314 +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.customtabs;
-
-import android.app.Service;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.support.annotation.IntDef;
-import android.support.v4.util.ArrayMap;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-/**
- * Abstract service class for implementing Custom Tabs related functionality. The service should
- * be responding to the action ACTION_CUSTOM_TABS_CONNECTION. This class should be used by
- * implementers that want to provide Custom Tabs functionality, not by clients that want to launch
- * Custom Tabs.
- */
-public abstract class CustomTabsService extends Service {
-    /**
-     * The Intent action that a CustomTabsService must respond to.
-     */
-    public static final String ACTION_CUSTOM_TABS_CONNECTION =
-            "android.support.customtabs.action.CustomTabsService";
-
-    /**
-     * For {@link CustomTabsService#mayLaunchUrl} calls that wants to specify more than one url,
-     * this key can be used with {@link Bundle#putParcelable(String, android.os.Parcelable)}
-     * to insert a new url to each bundle inside list of bundles.
-     */
-    public static final String KEY_URL =
-            "android.support.customtabs.otherurls.URL";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RESULT_SUCCESS, RESULT_FAILURE_DISALLOWED,
-            RESULT_FAILURE_REMOTE_ERROR, RESULT_FAILURE_MESSAGING_ERROR})
-    public @interface Result {
-    }
-
-    /**
-     * Indicates that the postMessage request was accepted.
-     */
-    public static final int RESULT_SUCCESS = 0;
-    /**
-     * Indicates that the postMessage request was not allowed due to a bad argument or requesting
-     * at a disallowed time like when in background.
-     */
-    public static final int RESULT_FAILURE_DISALLOWED = -1;
-    /**
-     * Indicates that the postMessage request has failed due to a {@link RemoteException} .
-     */
-    public static final int RESULT_FAILURE_REMOTE_ERROR = -2;
-    /**
-     * Indicates that the postMessage request has failed due to an internal error on the browser
-     * message channel.
-     */
-    public static final int RESULT_FAILURE_MESSAGING_ERROR = -3;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({RELATION_USE_AS_ORIGIN, RELATION_HANDLE_ALL_URLS})
-    public @interface Relation {
-    }
-
-    /**
-     * Used for {@link CustomTabsSession#validateRelationship(int, Uri, Bundle)}. For
-     * App -> Web transitions, requests the app to use the declared origin to be used as origin for
-     * the client app in the web APIs context.
-     */
-    public static final int RELATION_USE_AS_ORIGIN = 1;
-    /**
-     * Used for {@link CustomTabsSession#validateRelationship(int, Uri, Bundle)}. Requests the
-     * ability to handle all URLs from a given origin.
-     */
-    public static final int RELATION_HANDLE_ALL_URLS = 2;
-
-    private final Map<IBinder, DeathRecipient> mDeathRecipientMap = new ArrayMap<>();
-
-    private ICustomTabsService.Stub mBinder = new ICustomTabsService.Stub() {
-
-        @Override
-        public boolean warmup(long flags) {
-            return CustomTabsService.this.warmup(flags);
-        }
-
-        @Override
-        public boolean newSession(ICustomTabsCallback callback) {
-            final CustomTabsSessionToken sessionToken = new CustomTabsSessionToken(callback);
-            try {
-                DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
-                    @Override
-                    public void binderDied() {
-                        cleanUpSession(sessionToken);
-                    }
-                };
-                synchronized (mDeathRecipientMap) {
-                    callback.asBinder().linkToDeath(deathRecipient, 0);
-                    mDeathRecipientMap.put(callback.asBinder(), deathRecipient);
-                }
-                return CustomTabsService.this.newSession(sessionToken);
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-
-        @Override
-        public boolean mayLaunchUrl(ICustomTabsCallback callback, Uri url,
-                                    Bundle extras, List<Bundle> otherLikelyBundles) {
-            return CustomTabsService.this.mayLaunchUrl(
-                    new CustomTabsSessionToken(callback), url, extras, otherLikelyBundles);
-        }
-
-        @Override
-        public Bundle extraCommand(String commandName, Bundle args) {
-            return CustomTabsService.this.extraCommand(commandName, args);
-        }
-
-        @Override
-        public boolean updateVisuals(ICustomTabsCallback callback, Bundle bundle) {
-            return CustomTabsService.this.updateVisuals(
-                    new CustomTabsSessionToken(callback), bundle);
-        }
-
-        @Override
-        public boolean requestPostMessageChannel(ICustomTabsCallback callback,
-                                                 Uri postMessageOrigin) {
-            return CustomTabsService.this.requestPostMessageChannel(
-                    new CustomTabsSessionToken(callback), postMessageOrigin);
-        }
-
-        @Override
-        public int postMessage(ICustomTabsCallback callback, String message, Bundle extras) {
-            return CustomTabsService.this.postMessage(
-                    new CustomTabsSessionToken(callback), message, extras);
-        }
-
-        @Override
-        public boolean validateRelationship(
-                ICustomTabsCallback callback, @Relation int relation, Uri origin, Bundle extras) {
-            return CustomTabsService.this.validateRelationship(
-                    new CustomTabsSessionToken(callback), relation, origin, extras);
-        }
-    };
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    /**
-     * Called when the client side {@link IBinder} for this {@link CustomTabsSessionToken} is dead.
-     * Can also be used to clean up {@link DeathRecipient} instances allocated for the given token.
-     *
-     * @param sessionToken The session token for which the {@link DeathRecipient} call has been
-     *                     received.
-     * @return Whether the clean up was successful. Multiple calls with two tokens holdings the
-     * same binder will return false.
-     */
-    protected boolean cleanUpSession(CustomTabsSessionToken sessionToken) {
-        try {
-            synchronized (mDeathRecipientMap) {
-                IBinder binder = sessionToken.getCallbackBinder();
-                DeathRecipient deathRecipient =
-                        mDeathRecipientMap.get(binder);
-                binder.unlinkToDeath(deathRecipient, 0);
-                mDeathRecipientMap.remove(binder);
-            }
-        } catch (NoSuchElementException e) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Warms up the browser process asynchronously.
-     *
-     * @param flags Reserved for future use.
-     * @return Whether warmup was/had been completed successfully. Multiple successful
-     * calls will return true.
-     */
-    protected abstract boolean warmup(long flags);
-
-    /**
-     * Creates a new session through an ICustomTabsService with the optional callback. This session
-     * can be used to associate any related communication through the service with an intent and
-     * then later with a Custom Tab. The client can then send later service calls or intents to
-     * through same session-intent-Custom Tab association.
-     *
-     * @param sessionToken Session token to be used as a unique identifier. This also has access
-     *                     to the {@link CustomTabsCallback} passed from the client side through
-     *                     {@link CustomTabsSessionToken#getCallback()}.
-     * @return Whether a new session was successfully created.
-     */
-    protected abstract boolean newSession(CustomTabsSessionToken sessionToken);
-
-    /**
-     * Tells the browser of a likely future navigation to a URL.
-     * <p>
-     * The method {@link CustomTabsService#warmup(long)} has to be called beforehand.
-     * The most likely URL has to be specified explicitly. Optionally, a list of
-     * other likely URLs can be provided. They are treated as less likely than
-     * the first one, and have to be sorted in decreasing priority order. These
-     * additional URLs may be ignored.
-     * All previous calls to this method will be deprioritized.
-     *
-     * @param sessionToken       The unique identifier for the session. Can not be null.
-     * @param url                Most likely URL.
-     * @param extras             Reserved for future use.
-     * @param otherLikelyBundles Other likely destinations, sorted in decreasing
-     *                           likelihood order. Each Bundle has to provide a url.
-     * @return Whether the call was successful.
-     */
-    protected abstract boolean mayLaunchUrl(CustomTabsSessionToken sessionToken, Uri url,
-                                            Bundle extras, List<Bundle> otherLikelyBundles);
-
-    /**
-     * Unsupported commands that may be provided by the implementation.
-     * <p>
-     * <p>
-     * <strong>Note:</strong>Clients should <strong>never</strong> rely on this method to have a
-     * defined behavior, as it is entirely implementation-defined and not supported.
-     * <p>
-     * <p> This call can be used by implementations to add extra commands, for testing or
-     * experimental purposes.
-     *
-     * @param commandName Name of the extra command to execute.
-     * @param args        Arguments for the command
-     * @return The result {@link Bundle}, or null.
-     */
-    protected abstract Bundle extraCommand(String commandName, Bundle args);
-
-    /**
-     * Updates the visuals of custom tabs for the given session. Will only succeed if the given
-     * session matches the currently active one.
-     *
-     * @param sessionToken The currently active session that the custom tab belongs to.
-     * @param bundle       The action button configuration bundle. This bundle should be constructed
-     *                     with the same structure in {@link CustomTabsIntent.Builder}.
-     * @return Whether the operation was successful.
-     */
-    protected abstract boolean updateVisuals(CustomTabsSessionToken sessionToken,
-                                             Bundle bundle);
-
-    /**
-     * Sends a request to create a two way postMessage channel between the client and the browser
-     * linked with the given {@link CustomTabsSession}.
-     *
-     * @param sessionToken      The unique identifier for the session. Can not be null.
-     * @param postMessageOrigin A origin that the client is requesting to be identified as
-     *                          during the postMessage communication.
-     * @return Whether the implementation accepted the request. Note that returning true
-     * here doesn't mean an origin has already been assigned as the validation is
-     * asynchronous.
-     */
-    protected abstract boolean requestPostMessageChannel(CustomTabsSessionToken sessionToken,
-                                                         Uri postMessageOrigin);
-
-    /**
-     * Sends a postMessage request using the origin communicated via
-     * {@link CustomTabsService#requestPostMessageChannel(
-     *CustomTabsSessionToken, Uri)}. Fails when called before
-     * {@link PostMessageServiceConnection#notifyMessageChannelReady(Bundle)} is received on the
-     * client side.
-     *
-     * @param sessionToken The unique identifier for the session. Can not be null.
-     * @param message      The message that is being sent.
-     * @param extras       Reserved for future use.
-     * @return An integer constant about the postMessage request result. Will return
-     * {@link CustomTabsService#RESULT_SUCCESS} if successful.
-     */
-    @Result
-    protected abstract int postMessage(
-            CustomTabsSessionToken sessionToken, String message, Bundle extras);
-
-    /**
-     * Request to validate a relationship between the application and an origin.
-     *
-     * If this method returns true, the validation result will be provided through
-     * {@link CustomTabsCallback#onRelationshipValidationResult(int, Uri, boolean, Bundle)}.
-     * Otherwise the request didn't succeed. The client must call
-     * {@link CustomTabsClient#warmup(long)} before this.
-     *
-     * @param sessionToken The unique identifier for the session. Can not be null.
-     * @param relation Relation to check, must be one of the {@code CustomTabsService#RELATION_* }
-     *                 constants.
-     * @param origin Origin for the relation query.
-     * @param extras Reserved for future use.
-     * @return true if the request has been submitted successfully.
-     */
-    protected abstract boolean validateRelationship(
-            CustomTabsSessionToken sessionToken, @Relation int relation, Uri origin,
-            Bundle extras);
-}
diff --git a/android/support/customtabs/CustomTabsServiceConnection.java b/android/support/customtabs/CustomTabsServiceConnection.java
deleted file mode 100644
index ff52e97..0000000
--- a/android/support/customtabs/CustomTabsServiceConnection.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.customtabs;
-
-import android.content.ComponentName;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-
-/**
- * Abstract {@link ServiceConnection} to use while binding to a {@link CustomTabsService}. Any
- * client implementing this is responsible for handling changes related with the lifetime of the
- * connection like rebinding on disconnect.
- */
-public abstract class CustomTabsServiceConnection implements ServiceConnection {
-
-    @Override
-    public final void onServiceConnected(ComponentName name, IBinder service) {
-        onCustomTabsServiceConnected(name, new CustomTabsClient(
-                ICustomTabsService.Stub.asInterface(service), name) {
-        });
-    }
-
-    /**
-     * Called when a connection to the {@link CustomTabsService} has been established.
-     * @param name   The concrete component name of the service that has been connected.
-     * @param client {@link CustomTabsClient} that contains the {@link IBinder} with which the
-     *               connection have been established. All further communication should be initiated
-     *               using this client.
-     */
-    public abstract void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client);
-}
diff --git a/android/support/customtabs/CustomTabsSession.java b/android/support/customtabs/CustomTabsSession.java
deleted file mode 100644
index a84d63c..0000000
--- a/android/support/customtabs/CustomTabsSession.java
+++ /dev/null
@@ -1,245 +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.customtabs;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.customtabs.CustomTabsService.Relation;
-import android.support.customtabs.CustomTabsService.Result;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import java.util.List;
-
-/**
- * A class to be used for Custom Tabs related communication. Clients that want to launch Custom Tabs
- * can use this class exclusively to handle all related communication.
- */
-public final class CustomTabsSession {
-    private static final String TAG = "CustomTabsSession";
-    private final Object mLock = new Object();
-    private final ICustomTabsService mService;
-    private final ICustomTabsCallback mCallback;
-    private final ComponentName mComponentName;
-
-    /**
-     * Provides browsers a way to generate a mock {@link CustomTabsSession} for testing
-     * purposes.
-     *
-     * @param componentName The component the session should be created for.
-     * @return A mock session with no functionality.
-     */
-    @VisibleForTesting
-    @NonNull
-    public static CustomTabsSession createMockSessionForTesting(
-            @NonNull ComponentName componentName) {
-        return new CustomTabsSession(
-                null, new CustomTabsSessionToken.MockCallback(), componentName);
-    }
-
-    /* package */ CustomTabsSession(
-            ICustomTabsService service, ICustomTabsCallback callback, ComponentName componentName) {
-        mService = service;
-        mCallback = callback;
-        mComponentName = componentName;
-    }
-
-    /**
-     * Tells the browser of a likely future navigation to a URL.
-     * The most likely URL has to be specified first. Optionally, a list of
-     * other likely URLs can be provided. They are treated as less likely than
-     * the first one, and have to be sorted in decreasing priority order. These
-     * additional URLs may be ignored.
-     * All previous calls to this method will be deprioritized.
-     *
-     * @param url                Most likely URL.
-     * @param extras             Reserved for future use.
-     * @param otherLikelyBundles Other likely destinations, sorted in decreasing
-     *                           likelihood order. Inside each Bundle, the client should provide a
-     *                           {@link Uri} using {@link CustomTabsService#KEY_URL} with
-     *                           {@link Bundle#putParcelable(String, android.os.Parcelable)}.
-     * @return                   true for success.
-     */
-    public boolean mayLaunchUrl(Uri url, Bundle extras, List<Bundle> otherLikelyBundles) {
-        try {
-            return mService.mayLaunchUrl(mCallback, url, extras, otherLikelyBundles);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * This sets the action button on the toolbar with ID
-     * {@link CustomTabsIntent#TOOLBAR_ACTION_BUTTON_ID}.
-     *
-     * @param icon          The new icon of the action button.
-     * @param description   Content description of the action button.
-     *
-     * @see CustomTabsSession#setToolbarItem(int, Bitmap, String)
-     */
-    public boolean setActionButton(@NonNull Bitmap icon, @NonNull String description) {
-        Bundle bundle = new Bundle();
-        bundle.putParcelable(CustomTabsIntent.KEY_ICON, icon);
-        bundle.putString(CustomTabsIntent.KEY_DESCRIPTION, description);
-
-        Bundle metaBundle = new Bundle();
-        metaBundle.putBundle(CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE, bundle);
-        try {
-            return mService.updateVisuals(mCallback, metaBundle);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Updates the {@link RemoteViews} of the secondary toolbar in an existing custom tab session.
-     * @param remoteViews   The updated {@link RemoteViews} that will be shown in secondary toolbar.
-     *                      If null, the current secondary toolbar will be dismissed.
-     * @param clickableIDs  The ids of clickable views. The onClick event of these views will be
-     *                      handled by custom tabs.
-     * @param pendingIntent The {@link PendingIntent} that will be sent when the user clicks on one
-     *                      of the {@link View}s in clickableIDs.
-     */
-    public boolean setSecondaryToolbarViews(@Nullable RemoteViews remoteViews,
-            @Nullable int[] clickableIDs, @Nullable PendingIntent pendingIntent) {
-        Bundle bundle = new Bundle();
-        bundle.putParcelable(CustomTabsIntent.EXTRA_REMOTEVIEWS, remoteViews);
-        bundle.putIntArray(CustomTabsIntent.EXTRA_REMOTEVIEWS_VIEW_IDS, clickableIDs);
-        bundle.putParcelable(CustomTabsIntent.EXTRA_REMOTEVIEWS_PENDINGINTENT, pendingIntent);
-        try {
-            return mService.updateVisuals(mCallback, bundle);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Updates the visuals for toolbar items. Will only succeed if a custom tab created using this
-     * session is in the foreground in browser and the given id is valid.
-     * @param id            The id for the item to update.
-     * @param icon          The new icon of the toolbar item.
-     * @param description   Content description of the toolbar item.
-     * @return              Whether the update succeeded.
-     * @deprecated Use
-     * CustomTabsSession#setSecondaryToolbarViews(RemoteViews, int[], PendingIntent)
-     */
-    @Deprecated
-    public boolean setToolbarItem(int id, @NonNull Bitmap icon, @NonNull String description) {
-        Bundle bundle = new Bundle();
-        bundle.putInt(CustomTabsIntent.KEY_ID, id);
-        bundle.putParcelable(CustomTabsIntent.KEY_ICON, icon);
-        bundle.putString(CustomTabsIntent.KEY_DESCRIPTION, description);
-
-        Bundle metaBundle = new Bundle();
-        metaBundle.putBundle(CustomTabsIntent.EXTRA_ACTION_BUTTON_BUNDLE, bundle);
-        try {
-            return mService.updateVisuals(mCallback, metaBundle);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Sends a request to create a two way postMessage channel between the client and the browser.
-     *
-     * @param postMessageOrigin      A origin that the client is requesting to be identified as
-     *                               during the postMessage communication.
-     * @return Whether the implementation accepted the request. Note that returning true
-     *         here doesn't mean an origin has already been assigned as the validation is
-     *         asynchronous.
-     */
-    public boolean requestPostMessageChannel(Uri postMessageOrigin) {
-        try {
-            return mService.requestPostMessageChannel(
-                    mCallback, postMessageOrigin);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Sends a postMessage request using the origin communicated via
-     * {@link CustomTabsService#requestPostMessageChannel(
-     * CustomTabsSessionToken, Uri)}. Fails when called before
-     * {@link PostMessageServiceConnection#notifyMessageChannelReady(Bundle)} is received on
-     * the client side.
-     *
-     * @param message The message that is being sent.
-     * @param extras Reserved for future use.
-     * @return An integer constant about the postMessage request result. Will return
-      *        {@link CustomTabsService#RESULT_SUCCESS} if successful.
-     */
-    @Result
-    public int postMessage(String message, Bundle extras) {
-        synchronized (mLock) {
-            try {
-                return mService.postMessage(mCallback, message, extras);
-            } catch (RemoteException e) {
-                return CustomTabsService.RESULT_FAILURE_REMOTE_ERROR;
-            }
-        }
-    }
-
-    /**
-     * Requests to validate a relationship between the application and an origin.
-     *
-     * <p>
-     * See <a href="https://developers.google.com/digital-asset-links/v1/getting-started">here</a>
-     * for documentation about Digital Asset Links. This methods requests the browser to verify
-     * a relation with the calling application, to grant the associated rights.
-     *
-     * <p>
-     * If this method returns {@code true}, the validation result will be provided through
-     * {@link CustomTabsCallback#onRelationshipValidationResult(int, Uri, boolean, Bundle)}.
-     * Otherwise the request didn't succeed. The client must call
-     * {@link CustomTabsClient#warmup(long)} before this.
-     *
-     * @param relation Relation to check, must be one of the {@code CustomTabsService#RELATION_* }
-     *                 constants.
-     * @param origin Origin.
-     * @param extras Reserved for future use.
-     * @return {@code true} if the request has been submitted successfully.
-     */
-    public boolean validateRelationship(@Relation int relation, @NonNull Uri origin,
-                                        @Nullable Bundle extras) {
-        if (relation < CustomTabsService.RELATION_USE_AS_ORIGIN
-                || relation > CustomTabsService.RELATION_HANDLE_ALL_URLS) {
-            return false;
-        }
-        try {
-            return mService.validateRelationship(mCallback, relation, origin, extras);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /* package */ IBinder getBinder() {
-        return mCallback.asBinder();
-    }
-
-    /* package */ ComponentName getComponentName() {
-        return mComponentName;
-    }
-}
diff --git a/android/support/customtabs/CustomTabsSessionToken.java b/android/support/customtabs/CustomTabsSessionToken.java
deleted file mode 100644
index 5a9e1b6..0000000
--- a/android/support/customtabs/CustomTabsSessionToken.java
+++ /dev/null
@@ -1,170 +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.customtabs;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.customtabs.CustomTabsService.Relation;
-import android.support.v4.app.BundleCompat;
-import android.util.Log;
-
-/**
- * Wrapper class that can be used as a unique identifier for a session. Also contains an accessor
- * for the {@link CustomTabsCallback} for the session if there was any.
- */
-public class CustomTabsSessionToken {
-    private static final String TAG = "CustomTabsSessionToken";
-    private final ICustomTabsCallback mCallbackBinder;
-    private final CustomTabsCallback mCallback;
-
-    /* package */ static class MockCallback extends ICustomTabsCallback.Stub {
-        @Override
-        public void onNavigationEvent(int navigationEvent, Bundle extras) {}
-
-        @Override
-        public void extraCallback(String callbackName, Bundle args) {}
-
-        @Override
-        public void onMessageChannelReady(Bundle extras) {}
-
-        @Override
-        public void onPostMessage(String message, Bundle extras) {}
-
-        @Override
-        public void onRelationshipValidationResult(@Relation int relation, Uri requestedOrigin,
-                boolean result, Bundle extras) {}
-
-        @Override
-        public IBinder asBinder() {
-            return this;
-        }
-    }
-
-    /**
-     * Obtain a {@link CustomTabsSessionToken} from an intent. See {@link CustomTabsIntent.Builder}
-     * for ways to generate an intent for custom tabs.
-     * @param intent The intent to generate the token from. This has to include an extra for
-     *               {@link CustomTabsIntent#EXTRA_SESSION}.
-     * @return The token that was generated.
-     */
-    public static CustomTabsSessionToken getSessionTokenFromIntent(Intent intent) {
-        Bundle b = intent.getExtras();
-        IBinder binder = BundleCompat.getBinder(b, CustomTabsIntent.EXTRA_SESSION);
-        if (binder == null) return null;
-        return new CustomTabsSessionToken(ICustomTabsCallback.Stub.asInterface(binder));
-    }
-
-    /**
-     * Provides browsers a way to generate a mock {@link CustomTabsSessionToken} for testing
-     * purposes.
-     *
-     * @return A mock token with no functionality.
-     */
-    @NonNull
-    public static CustomTabsSessionToken createMockSessionTokenForTesting() {
-        return new CustomTabsSessionToken(new MockCallback());
-    }
-
-    CustomTabsSessionToken(ICustomTabsCallback callbackBinder) {
-        mCallbackBinder = callbackBinder;
-        mCallback = new CustomTabsCallback() {
-
-            @Override
-            public void onNavigationEvent(int navigationEvent, Bundle extras) {
-                try {
-                    mCallbackBinder.onNavigationEvent(navigationEvent, extras);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
-                }
-            }
-
-            @Override
-            public void extraCallback(String callbackName, Bundle args) {
-                try {
-                    mCallbackBinder.extraCallback(callbackName, args);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
-                }
-            }
-
-            @Override
-            public void onMessageChannelReady(Bundle extras) {
-                try {
-                    mCallbackBinder.onMessageChannelReady(extras);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
-                }
-            }
-
-            @Override
-            public void onPostMessage(String message, Bundle extras) {
-                try {
-                    mCallbackBinder.onPostMessage(message, extras);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
-                }
-            }
-
-            @Override
-            public void onRelationshipValidationResult(@Relation int relation, Uri origin,
-                                                       boolean result, Bundle extras) {
-                try {
-                    mCallbackBinder.onRelationshipValidationResult(
-                            relation, origin, result, extras);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException during ICustomTabsCallback transaction");
-                }
-            }
-
-        };
-    }
-
-    IBinder getCallbackBinder() {
-        return mCallbackBinder.asBinder();
-    }
-
-    @Override
-    public int hashCode() {
-        return getCallbackBinder().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof CustomTabsSessionToken)) return false;
-        CustomTabsSessionToken token = (CustomTabsSessionToken) o;
-        return token.getCallbackBinder().equals(mCallbackBinder.asBinder());
-    }
-
-    /**
-     * @return {@link CustomTabsCallback} corresponding to this session if there was any non-null
-     *         callbacks passed by the client.
-     */
-    public CustomTabsCallback getCallback() {
-        return mCallback;
-    }
-
-    /**
-     * @return Whether this token is associated with the given session.
-     */
-    public boolean isAssociatedWith(CustomTabsSession session) {
-        return session.getBinder().equals(mCallbackBinder);
-    }
-}
diff --git a/android/support/customtabs/PostMessageService.java b/android/support/customtabs/PostMessageService.java
deleted file mode 100644
index 7355f4e..0000000
--- a/android/support/customtabs/PostMessageService.java
+++ /dev/null
@@ -1,48 +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 android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-/**
- * A service to receive postMessage related communication from a Custom Tabs provider.
- */
-public class PostMessageService extends Service {
-    private IPostMessageService.Stub mBinder = new IPostMessageService.Stub() {
-
-        @Override
-        public void onMessageChannelReady(
-                ICustomTabsCallback callback, Bundle extras) throws RemoteException {
-            callback.onMessageChannelReady(extras);
-        }
-
-        @Override
-        public void onPostMessage(ICustomTabsCallback callback,
-                                  String message, Bundle extras) throws RemoteException {
-            callback.onPostMessage(message, extras);
-        }
-    };
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-}
diff --git a/android/support/customtabs/PostMessageServiceConnection.java b/android/support/customtabs/PostMessageServiceConnection.java
deleted file mode 100644
index 4eef50c..0000000
--- a/android/support/customtabs/PostMessageServiceConnection.java
+++ /dev/null
@@ -1,126 +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 android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-/**
- * A {@link ServiceConnection} for Custom Tabs providers to use while connecting to a
- * {@link PostMessageService} on the client side.
- */
-public abstract class PostMessageServiceConnection implements ServiceConnection {
-    private final Object mLock = new Object();
-    private final ICustomTabsCallback mSessionBinder;
-    private IPostMessageService mService;
-
-    public PostMessageServiceConnection(CustomTabsSessionToken session) {
-        mSessionBinder = ICustomTabsCallback.Stub.asInterface(session.getCallbackBinder());
-    }
-
-    /**
-     * Binds the browser side to the client app through the given {@link PostMessageService} name.
-     * After this, this {@link PostMessageServiceConnection} can be used for sending postMessage
-     * related communication back to the client.
-     * @param context A context to bind to the service.
-     * @param packageName The name of the package to be bound to.
-     * @return Whether the binding was successful.
-     */
-    public boolean bindSessionToPostMessageService(Context context, String packageName) {
-        Intent intent = new Intent();
-        intent.setClassName(packageName, PostMessageService.class.getName());
-        return context.bindService(intent, this, Context.BIND_AUTO_CREATE);
-    }
-
-    /**
-     * Unbinds this service connection from the given context.
-     * @param context The context to be unbound from.
-     */
-    public void unbindFromContext(Context context) {
-        context.unbindService(this);
-    }
-
-    @Override
-    public final void onServiceConnected(ComponentName name, IBinder service) {
-        mService = IPostMessageService.Stub.asInterface(service);
-        onPostMessageServiceConnected();
-    }
-
-    @Override
-    public final void onServiceDisconnected(ComponentName name) {
-        mService = null;
-        onPostMessageServiceDisconnected();
-    }
-
-    /**
-     * Notifies the client that the postMessage channel requested with
-     * {@link CustomTabsService#requestPostMessageChannel(
-     * CustomTabsSessionToken, android.net.Uri)} is ready. This method should be
-     * called when the browser binds to the client side {@link PostMessageService} and also readies
-     * a connection to the web frame.
-     *
-     * @param extras Reserved for future use.
-     * @return Whether the notification was sent to the remote successfully.
-     */
-    public final boolean notifyMessageChannelReady(Bundle extras) {
-        if (mService == null) return false;
-        synchronized (mLock) {
-            try {
-                mService.onMessageChannelReady(mSessionBinder, extras);
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Posts a message to the client. This should be called when a tab controlled by related
-     * {@link CustomTabsSession} has sent a postMessage. If postMessage() is called from a single
-     * thread, then the messages will be posted in the same order.
-     *
-     * @param message The message sent.
-     * @param extras Reserved for future use.
-     * @return Whether the postMessage was sent to the remote successfully.
-     */
-    public final boolean postMessage(String message, Bundle extras) {
-        if (mService == null) return false;
-        synchronized (mLock) {
-            try {
-                mService.onPostMessage(mSessionBinder, message, extras);
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Called when the {@link PostMessageService} connection is established.
-     */
-    public void onPostMessageServiceConnected() {}
-
-    /**
-     * Called when the connection is lost with the {@link PostMessageService}.
-     */
-    public void onPostMessageServiceDisconnected() {}
-}
diff --git a/android/support/customtabs/TrustedWebUtils.java b/android/support/customtabs/TrustedWebUtils.java
deleted file mode 100644
index e9a2233..0000000
--- a/android/support/customtabs/TrustedWebUtils.java
+++ /dev/null
@@ -1,82 +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 android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.BundleCompat;
-
-/**
- * Class for utilities and convenience calls for opening a qualifying web page as a
- * Trusted Web Activity.
- *
- * Trusted Web Activity is a fullscreen UI with no visible browser controls that hosts web pages
- * meeting certain criteria. The full list of qualifications is at the implementing browser's
- * discretion, but minimum recommended set is for the web page :
- *  <ul>
- *      <li>To have declared delegate_permission/common.handle_all_urls relationship with the
- *      launching client application ensuring 1:1 trust between the Android native and web
- *      components. See https://developers.google.com/digital-asset-links/ for details.</li>
- *      <li>To work as a reliable, fast and engaging standalone component within the launching app's
- *      flow.</li>
- *      <li>To be accessible and operable even when offline.</li>
- *  </ul>
- *
- *  Fallback behaviors may also differ with implementation. Possibilities are launching the page in
- *  a custom tab, or showing it in browser UI. Browsers are encouraged to use
- *  {@link CustomTabsCallback#onRelationshipValidationResult(int, Uri, boolean, Bundle)}
- *  for sending details of the verification results.
- */
-public class TrustedWebUtils {
-
-    /**
-     * Boolean extra that triggers a {@link CustomTabsIntent} launch to be in a fullscreen UI with
-     * no browser controls.
-     *
-     * @see TrustedWebUtils#launchAsTrustedWebActivity(Context, CustomTabsIntent, Uri).
-     */
-    public static final String EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY =
-            "android.support.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY";
-
-    private TrustedWebUtils() {}
-
-    /**
-     * Launch the given {@link CustomTabsIntent} as a Trusted Web Activity. The given
-     * {@link CustomTabsIntent} should have a valid {@link CustomTabsSession} associated with it
-     * during construction. Once the Trusted Web Activity is launched, browser side implementations
-     * may have their own fallback behavior (e.g. Showing the page in a custom tab UI with toolbar)
-     * based on qualifications listed above or more.
-     *
-     * @param context {@link Context} to use while launching the {@link CustomTabsIntent}.
-     * @param customTabsIntent The {@link CustomTabsIntent} to use for launching the
-     *                         Trusted Web Activity. Note that all customizations in the given
-     *                         associated with browser toolbar controls will be ignored.
-     * @param uri The web page to launch as Trusted Web Activity.
-     */
-    public static void launchAsTrustedWebActivity(@NonNull Context context,
-            @NonNull CustomTabsIntent customTabsIntent, @NonNull Uri uri) {
-        if (BundleCompat.getBinder(
-                customTabsIntent.intent.getExtras(), CustomTabsIntent.EXTRA_SESSION) == null) {
-            throw new IllegalArgumentException(
-                    "Given CustomTabsIntent should be associated with a valid CustomTabsSession");
-        }
-        customTabsIntent.intent.putExtra(EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY, true);
-        customTabsIntent.launchUrl(context, uri);
-    }
-}
diff --git a/android/support/design/widget/CoordinatorLayout.java b/android/support/design/widget/CoordinatorLayout.java
deleted file mode 100644
index b7f47f4..0000000
--- a/android/support/design/widget/CoordinatorLayout.java
+++ /dev/null
@@ -1,3254 +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 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.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-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.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.coreui.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.util.Pools;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.GravityCompat;
-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.support.v4.widget.DirectedAcyclicGraph;
-import android.support.v4.widget.ViewGroupUtils;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.ViewTreeObserver;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * CoordinatorLayout is a super-powered {@link android.widget.FrameLayout FrameLayout}.
- *
- * <p>CoordinatorLayout is intended for two primary use cases:</p>
- * <ol>
- *     <li>As a top-level application decor or chrome layout</li>
- *     <li>As a container for a specific interaction with one or more child views</li>
- * </ol>
- *
- * <p>By specifying {@link Behavior Behaviors} for child views of a
- * CoordinatorLayout you can provide many different interactions within a single parent and those
- * views can also interact with one another. View classes can specify a default behavior when
- * used as a child of a CoordinatorLayout using the
- * {@link DefaultBehavior} annotation.</p>
- *
- * <p>Behaviors may be used to implement a variety of interactions and additional layout
- * modifications ranging from sliding drawers and panels to swipe-dismissable elements and buttons
- * that stick to other elements as they move and animate.</p>
- *
- * <p>Children of a CoordinatorLayout may have an
- * {@link LayoutParams#setAnchorId(int) anchor}. This view id must correspond
- * to an arbitrary descendant of the CoordinatorLayout, but it may not be the anchored child itself
- * or a descendant of the anchored child. This can be used to place floating views relative to
- * other arbitrary content panes.</p>
- *
- * <p>Children can specify {@link LayoutParams#insetEdge} to describe how the
- * view insets the CoordinatorLayout. Any child views which are set to dodge the same inset edges by
- * {@link LayoutParams#dodgeInsetEdges} will be moved appropriately so that the
- * views do not overlap.</p>
- */
-public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent2 {
-    static final String TAG = "CoordinatorLayout";
-    static final String WIDGET_PACKAGE_NAME;
-
-    static {
-        final Package pkg = CoordinatorLayout.class.getPackage();
-        WIDGET_PACKAGE_NAME = pkg != null ? pkg.getName() : null;
-    }
-
-    private static final int TYPE_ON_INTERCEPT = 0;
-    private static final int TYPE_ON_TOUCH = 1;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            TOP_SORTED_CHILDREN_COMPARATOR = new ViewElevationComparator();
-        } else {
-            TOP_SORTED_CHILDREN_COMPARATOR = null;
-        }
-    }
-
-    static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
-            Context.class,
-            AttributeSet.class
-    };
-
-    static final ThreadLocal<Map<String, Constructor<Behavior>>> sConstructors =
-            new ThreadLocal<>();
-
-    static final int EVENT_PRE_DRAW = 0;
-    static final int EVENT_NESTED_SCROLL = 1;
-    static final int EVENT_VIEW_REMOVED = 2;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({EVENT_PRE_DRAW, EVENT_NESTED_SCROLL, EVENT_VIEW_REMOVED})
-    public @interface DispatchChangeEvent {}
-
-    static final Comparator<View> TOP_SORTED_CHILDREN_COMPARATOR;
-    private static final Pools.Pool<Rect> sRectPool = new Pools.SynchronizedPool<>(12);
-
-    @NonNull
-    private static Rect acquireTempRect() {
-        Rect rect = sRectPool.acquire();
-        if (rect == null) {
-            rect = new Rect();
-        }
-        return rect;
-    }
-
-    private static void releaseTempRect(@NonNull Rect rect) {
-        rect.setEmpty();
-        sRectPool.release(rect);
-    }
-
-    private final List<View> mDependencySortedChildren = new ArrayList<>();
-    private final DirectedAcyclicGraph<View> mChildDag = new DirectedAcyclicGraph<>();
-
-    private final List<View> mTempList1 = new ArrayList<>();
-    private final List<View> mTempDependenciesList = new ArrayList<>();
-    private final int[] mTempIntPair = new int[2];
-    private Paint mScrimPaint;
-
-    private boolean mDisallowInterceptReset;
-
-    private boolean mIsAttachedToWindow;
-
-    private int[] mKeylines;
-
-    private View mBehaviorTouchView;
-    private View mNestedScrollingTarget;
-
-    private OnPreDrawListener mOnPreDrawListener;
-    private boolean mNeedsPreDrawListener;
-
-    private WindowInsetsCompat mLastInsets;
-    private boolean mDrawStatusBarBackground;
-    private Drawable mStatusBarBackground;
-
-    OnHierarchyChangeListener mOnHierarchyChangeListener;
-    private android.support.v4.view.OnApplyWindowInsetsListener mApplyWindowInsetsListener;
-
-    private final NestedScrollingParentHelper mNestedScrollingParentHelper =
-            new NestedScrollingParentHelper(this);
-
-    public CoordinatorLayout(Context context) {
-        this(context, null);
-    }
-
-    public CoordinatorLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.coordinatorLayoutStyle);
-    }
-
-    public CoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        final TypedArray a = (defStyleAttr == 0)
-                ? context.obtainStyledAttributes(attrs, R.styleable.CoordinatorLayout,
-                    0, R.style.Widget_Support_CoordinatorLayout)
-                : context.obtainStyledAttributes(attrs, R.styleable.CoordinatorLayout,
-                    defStyleAttr, 0);
-        final int keylineArrayRes = a.getResourceId(R.styleable.CoordinatorLayout_keylines, 0);
-        if (keylineArrayRes != 0) {
-            final Resources res = context.getResources();
-            mKeylines = res.getIntArray(keylineArrayRes);
-            final float density = res.getDisplayMetrics().density;
-            final int count = mKeylines.length;
-            for (int i = 0; i < count; i++) {
-                mKeylines[i] = (int) (mKeylines[i] * density);
-            }
-        }
-        mStatusBarBackground = a.getDrawable(R.styleable.CoordinatorLayout_statusBarBackground);
-        a.recycle();
-
-        setupForInsets();
-        super.setOnHierarchyChangeListener(new HierarchyChangeListener());
-    }
-
-    @Override
-    public void setOnHierarchyChangeListener(OnHierarchyChangeListener onHierarchyChangeListener) {
-        mOnHierarchyChangeListener = onHierarchyChangeListener;
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        resetTouchBehaviors(false);
-        if (mNeedsPreDrawListener) {
-            if (mOnPreDrawListener == null) {
-                mOnPreDrawListener = new OnPreDrawListener();
-            }
-            final ViewTreeObserver vto = getViewTreeObserver();
-            vto.addOnPreDrawListener(mOnPreDrawListener);
-        }
-        if (mLastInsets == null && ViewCompat.getFitsSystemWindows(this)) {
-            // We're set to fitSystemWindows but we haven't had any insets yet...
-            // We should request a new dispatch of window insets
-            ViewCompat.requestApplyInsets(this);
-        }
-        mIsAttachedToWindow = true;
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        resetTouchBehaviors(false);
-        if (mNeedsPreDrawListener && mOnPreDrawListener != null) {
-            final ViewTreeObserver vto = getViewTreeObserver();
-            vto.removeOnPreDrawListener(mOnPreDrawListener);
-        }
-        if (mNestedScrollingTarget != null) {
-            onStopNestedScroll(mNestedScrollingTarget);
-        }
-        mIsAttachedToWindow = false;
-    }
-
-    /**
-     * Set a drawable to draw in the insets area for the status bar.
-     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
-     *
-     * @param bg Background drawable to draw behind the status bar
-     */
-    public void setStatusBarBackground(@Nullable final Drawable bg) {
-        if (mStatusBarBackground != bg) {
-            if (mStatusBarBackground != null) {
-                mStatusBarBackground.setCallback(null);
-            }
-            mStatusBarBackground = bg != null ? bg.mutate() : null;
-            if (mStatusBarBackground != null) {
-                if (mStatusBarBackground.isStateful()) {
-                    mStatusBarBackground.setState(getDrawableState());
-                }
-                DrawableCompat.setLayoutDirection(mStatusBarBackground,
-                        ViewCompat.getLayoutDirection(this));
-                mStatusBarBackground.setVisible(getVisibility() == VISIBLE, false);
-                mStatusBarBackground.setCallback(this);
-            }
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    /**
-     * Gets the drawable used to draw in the insets area for the status bar.
-     *
-     * @return The status bar background drawable, or null if none set
-     */
-    @Nullable
-    public Drawable getStatusBarBackground() {
-        return mStatusBarBackground;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        final int[] state = getDrawableState();
-        boolean changed = false;
-
-        Drawable d = mStatusBarBackground;
-        if (d != null && d.isStateful()) {
-            changed |= d.setState(state);
-        }
-
-        if (changed) {
-            invalidate();
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mStatusBarBackground;
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-
-        final boolean visible = visibility == VISIBLE;
-        if (mStatusBarBackground != null && mStatusBarBackground.isVisible() != visible) {
-            mStatusBarBackground.setVisible(visible, false);
-        }
-    }
-
-    /**
-     * Set a drawable to draw in the insets area for the status bar.
-     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
-     *
-     * @param resId Resource id of a background drawable to draw behind the status bar
-     */
-    public void setStatusBarBackgroundResource(@DrawableRes int resId) {
-        setStatusBarBackground(resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null);
-    }
-
-    /**
-     * Set a drawable to draw in the insets area for the status bar.
-     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
-     *
-     * @param color Color to use as a background drawable to draw behind the status bar
-     *              in 0xAARRGGBB format.
-     */
-    public void setStatusBarBackgroundColor(@ColorInt int color) {
-        setStatusBarBackground(new ColorDrawable(color));
-    }
-
-    final WindowInsetsCompat setWindowInsets(WindowInsetsCompat insets) {
-        if (!ObjectsCompat.equals(mLastInsets, insets)) {
-            mLastInsets = insets;
-            mDrawStatusBarBackground = insets != null && insets.getSystemWindowInsetTop() > 0;
-            setWillNotDraw(!mDrawStatusBarBackground && getBackground() == null);
-
-            // Now dispatch to the Behaviors
-            insets = dispatchApplyWindowInsetsToBehaviors(insets);
-            requestLayout();
-        }
-        return insets;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public final WindowInsetsCompat getLastWindowInsets() {
-        return mLastInsets;
-    }
-
-    /**
-     * Reset all Behavior-related tracking records either to clean up or in preparation
-     * for a new event stream. This should be called when attached or detached from a window,
-     * in response to an UP or CANCEL event, when intercept is request-disallowed
-     * and similar cases where an event stream in progress will be aborted.
-     */
-    private void resetTouchBehaviors(boolean notifyOnInterceptTouchEvent) {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final Behavior b = lp.getBehavior();
-            if (b != null) {
-                final long now = SystemClock.uptimeMillis();
-                final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
-                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-                if (notifyOnInterceptTouchEvent) {
-                    b.onInterceptTouchEvent(this, child, cancelEvent);
-                } else {
-                    b.onTouchEvent(this, child, cancelEvent);
-                }
-                cancelEvent.recycle();
-            }
-        }
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.resetTouchBehaviorTracking();
-        }
-        mBehaviorTouchView = null;
-        mDisallowInterceptReset = false;
-    }
-
-    /**
-     * Populate a list with the current child views, sorted such that the topmost views
-     * in z-order are at the front of the list. Useful for hit testing and event dispatch.
-     */
-    private void getTopSortedChildren(List<View> out) {
-        out.clear();
-
-        final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
-        final int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            final int childIndex = useCustomOrder ? getChildDrawingOrder(childCount, i) : i;
-            final View child = getChildAt(childIndex);
-            out.add(child);
-        }
-
-        if (TOP_SORTED_CHILDREN_COMPARATOR != null) {
-            Collections.sort(out, TOP_SORTED_CHILDREN_COMPARATOR);
-        }
-    }
-
-    private boolean performIntercept(MotionEvent ev, final int type) {
-        boolean intercepted = false;
-        boolean newBlock = false;
-
-        MotionEvent cancelEvent = null;
-
-        final int action = ev.getActionMasked();
-
-        final List<View> topmostChildList = mTempList1;
-        getTopSortedChildren(topmostChildList);
-
-        // Let topmost child views inspect first
-        final int childCount = topmostChildList.size();
-        for (int i = 0; i < childCount; i++) {
-            final View child = topmostChildList.get(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final Behavior b = lp.getBehavior();
-
-            if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {
-                // Cancel all behaviors beneath the one that intercepted.
-                // If the event is "down" then we don't have anything to cancel yet.
-                if (b != null) {
-                    if (cancelEvent == null) {
-                        final long now = SystemClock.uptimeMillis();
-                        cancelEvent = MotionEvent.obtain(now, now,
-                                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-                    }
-                    switch (type) {
-                        case TYPE_ON_INTERCEPT:
-                            b.onInterceptTouchEvent(this, child, cancelEvent);
-                            break;
-                        case TYPE_ON_TOUCH:
-                            b.onTouchEvent(this, child, cancelEvent);
-                            break;
-                    }
-                }
-                continue;
-            }
-
-            if (!intercepted && b != null) {
-                switch (type) {
-                    case TYPE_ON_INTERCEPT:
-                        intercepted = b.onInterceptTouchEvent(this, child, ev);
-                        break;
-                    case TYPE_ON_TOUCH:
-                        intercepted = b.onTouchEvent(this, child, ev);
-                        break;
-                }
-                if (intercepted) {
-                    mBehaviorTouchView = child;
-                }
-            }
-
-            // Don't keep going if we're not allowing interaction below this.
-            // Setting newBlock will make sure we cancel the rest of the behaviors.
-            final boolean wasBlocking = lp.didBlockInteraction();
-            final boolean isBlocking = lp.isBlockingInteractionBelow(this, child);
-            newBlock = isBlocking && !wasBlocking;
-            if (isBlocking && !newBlock) {
-                // Stop here since we don't have anything more to cancel - we already did
-                // when the behavior first started blocking things below this point.
-                break;
-            }
-        }
-
-        topmostChildList.clear();
-
-        return intercepted;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        MotionEvent cancelEvent = null;
-
-        final int action = ev.getActionMasked();
-
-        // Make sure we reset in case we had missed a previous important event.
-        if (action == MotionEvent.ACTION_DOWN) {
-            resetTouchBehaviors(true);
-        }
-
-        final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);
-
-        if (cancelEvent != null) {
-            cancelEvent.recycle();
-        }
-
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            resetTouchBehaviors(true);
-        }
-
-        return intercepted;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        boolean handled = false;
-        boolean cancelSuper = false;
-        MotionEvent cancelEvent = null;
-
-        final int action = ev.getActionMasked();
-
-        if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) {
-            // Safe since performIntercept guarantees that
-            // mBehaviorTouchView != null if it returns true
-            final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams();
-            final Behavior b = lp.getBehavior();
-            if (b != null) {
-                handled = b.onTouchEvent(this, mBehaviorTouchView, ev);
-            }
-        }
-
-        // Keep the super implementation correct
-        if (mBehaviorTouchView == null) {
-            handled |= super.onTouchEvent(ev);
-        } else if (cancelSuper) {
-            if (cancelEvent == null) {
-                final long now = SystemClock.uptimeMillis();
-                cancelEvent = MotionEvent.obtain(now, now,
-                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-            }
-            super.onTouchEvent(cancelEvent);
-        }
-
-        if (!handled && action == MotionEvent.ACTION_DOWN) {
-
-        }
-
-        if (cancelEvent != null) {
-            cancelEvent.recycle();
-        }
-
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            resetTouchBehaviors(false);
-        }
-
-        return handled;
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-        if (disallowIntercept && !mDisallowInterceptReset) {
-            resetTouchBehaviors(false);
-            mDisallowInterceptReset = true;
-        }
-    }
-
-    private int getKeyline(int index) {
-        if (mKeylines == null) {
-            Log.e(TAG, "No keylines defined for " + this + " - attempted index lookup " + index);
-            return 0;
-        }
-
-        if (index < 0 || index >= mKeylines.length) {
-            Log.e(TAG, "Keyline index " + index + " out of range for " + this);
-            return 0;
-        }
-
-        return mKeylines[index];
-    }
-
-    static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
-        if (TextUtils.isEmpty(name)) {
-            return null;
-        }
-
-        final String fullName;
-        if (name.startsWith(".")) {
-            // Relative to the app package. Prepend the app package name.
-            fullName = context.getPackageName() + name;
-        } else if (name.indexOf('.') >= 0) {
-            // Fully qualified package name.
-            fullName = name;
-        } else {
-            // Assume stock behavior in this package (if we have one)
-            fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
-                    ? (WIDGET_PACKAGE_NAME + '.' + name)
-                    : name;
-        }
-
-        try {
-            Map<String, Constructor<Behavior>> constructors = sConstructors.get();
-            if (constructors == null) {
-                constructors = new HashMap<>();
-                sConstructors.set(constructors);
-            }
-            Constructor<Behavior> c = constructors.get(fullName);
-            if (c == null) {
-                final Class<Behavior> clazz = (Class<Behavior>) context.getClassLoader()
-                        .loadClass(fullName);
-                c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
-                c.setAccessible(true);
-                constructors.put(fullName, c);
-            }
-            return c.newInstance(context, attrs);
-        } catch (Exception e) {
-            throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
-        }
-    }
-
-    LayoutParams getResolvedLayoutParams(View child) {
-        final LayoutParams result = (LayoutParams) child.getLayoutParams();
-        if (!result.mBehaviorResolved) {
-            Class<?> childClass = child.getClass();
-            DefaultBehavior defaultBehavior = null;
-            while (childClass != null &&
-                    (defaultBehavior = childClass.getAnnotation(DefaultBehavior.class)) == null) {
-                childClass = childClass.getSuperclass();
-            }
-            if (defaultBehavior != null) {
-                try {
-                    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);
-                }
-            }
-            result.mBehaviorResolved = true;
-        }
-        return result;
-    }
-
-    private void prepareChildren() {
-        mDependencySortedChildren.clear();
-        mChildDag.clear();
-
-        for (int i = 0, count = getChildCount(); i < count; i++) {
-            final View view = getChildAt(i);
-
-            final LayoutParams lp = getResolvedLayoutParams(view);
-            lp.findAnchorView(this, view);
-
-            mChildDag.addNode(view);
-
-            // Now iterate again over the other children, adding any dependencies to the graph
-            for (int j = 0; j < count; j++) {
-                if (j == i) {
-                    continue;
-                }
-                final View other = getChildAt(j);
-                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(other, view);
-                }
-            }
-        }
-
-        // Finally add the sorted graph list to our list
-        mDependencySortedChildren.addAll(mChildDag.getSortedList());
-        // We also need to reverse the result since we want the start of the list to contain
-        // Views which have no dependencies, then dependent views after that
-        Collections.reverse(mDependencySortedChildren);
-    }
-
-    /**
-     * Retrieve the transformed bounding rect of an arbitrary descendant view.
-     * This does not need to be a direct child.
-     *
-     * @param descendant descendant view to reference
-     * @param out rect to set to the bounds of the descendant view
-     */
-    void getDescendantRect(View descendant, Rect out) {
-        ViewGroupUtils.getDescendantRect(this, descendant, out);
-    }
-
-    @Override
-    protected int getSuggestedMinimumWidth() {
-        return Math.max(super.getSuggestedMinimumWidth(), getPaddingLeft() + getPaddingRight());
-    }
-
-    @Override
-    protected int getSuggestedMinimumHeight() {
-        return Math.max(super.getSuggestedMinimumHeight(), getPaddingTop() + getPaddingBottom());
-    }
-
-    /**
-     * Called to measure each individual child view unless a
-     * {@link Behavior Behavior} is present. The Behavior may choose to delegate
-     * child measurement to this method.
-     *
-     * @param child the child to measure
-     * @param parentWidthMeasureSpec the width requirements for this view
-     * @param widthUsed extra space that has been used up by the parent
-     *        horizontally (possibly by other children of the parent)
-     * @param parentHeightMeasureSpec the height requirements for this view
-     * @param heightUsed extra space that has been used up by the parent
-     *        vertically (possibly by other children of the parent)
-     */
-    public void onMeasureChild(View child, int parentWidthMeasureSpec, int widthUsed,
-            int parentHeightMeasureSpec, int heightUsed) {
-        measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed,
-                parentHeightMeasureSpec, heightUsed);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        prepareChildren();
-        ensurePreDrawListener();
-
-        final int paddingLeft = getPaddingLeft();
-        final int paddingTop = getPaddingTop();
-        final int paddingRight = getPaddingRight();
-        final int paddingBottom = getPaddingBottom();
-        final int layoutDirection = ViewCompat.getLayoutDirection(this);
-        final boolean isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        final int widthPadding = paddingLeft + paddingRight;
-        final int heightPadding = paddingTop + paddingBottom;
-        int widthUsed = getSuggestedMinimumWidth();
-        int heightUsed = getSuggestedMinimumHeight();
-        int childState = 0;
-
-        final boolean applyInsets = mLastInsets != null && ViewCompat.getFitsSystemWindows(this);
-
-        final int childCount = mDependencySortedChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            final View child = mDependencySortedChildren.get(i);
-            if (child.getVisibility() == GONE) {
-                // If the child is GONE, skip...
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            int keylineWidthUsed = 0;
-            if (lp.keyline >= 0 && widthMode != MeasureSpec.UNSPECIFIED) {
-                final int keylinePos = getKeyline(lp.keyline);
-                final int keylineGravity = GravityCompat.getAbsoluteGravity(
-                        resolveKeylineGravity(lp.gravity), layoutDirection)
-                        & Gravity.HORIZONTAL_GRAVITY_MASK;
-                if ((keylineGravity == Gravity.LEFT && !isRtl)
-                        || (keylineGravity == Gravity.RIGHT && isRtl)) {
-                    keylineWidthUsed = Math.max(0, widthSize - paddingRight - keylinePos);
-                } else if ((keylineGravity == Gravity.RIGHT && !isRtl)
-                        || (keylineGravity == Gravity.LEFT && isRtl)) {
-                    keylineWidthUsed = Math.max(0, keylinePos - paddingLeft);
-                }
-            }
-
-            int childWidthMeasureSpec = widthMeasureSpec;
-            int childHeightMeasureSpec = heightMeasureSpec;
-            if (applyInsets && !ViewCompat.getFitsSystemWindows(child)) {
-                // We're set to handle insets but this child isn't, so we will measure the
-                // child as if there are no insets
-                final int horizInsets = mLastInsets.getSystemWindowInsetLeft()
-                        + mLastInsets.getSystemWindowInsetRight();
-                final int vertInsets = mLastInsets.getSystemWindowInsetTop()
-                        + mLastInsets.getSystemWindowInsetBottom();
-
-                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                        widthSize - horizInsets, widthMode);
-                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                        heightSize - vertInsets, heightMode);
-            }
-
-            final Behavior b = lp.getBehavior();
-            if (b == null || !b.onMeasureChild(this, child, childWidthMeasureSpec, keylineWidthUsed,
-                    childHeightMeasureSpec, 0)) {
-                onMeasureChild(child, childWidthMeasureSpec, keylineWidthUsed,
-                        childHeightMeasureSpec, 0);
-            }
-
-            widthUsed = Math.max(widthUsed, widthPadding + child.getMeasuredWidth() +
-                    lp.leftMargin + lp.rightMargin);
-
-            heightUsed = Math.max(heightUsed, heightPadding + child.getMeasuredHeight() +
-                    lp.topMargin + lp.bottomMargin);
-            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
-        }
-
-        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);
-    }
-
-    private WindowInsetsCompat dispatchApplyWindowInsetsToBehaviors(WindowInsetsCompat insets) {
-        if (insets.isConsumed()) {
-            return insets;
-        }
-
-        for (int i = 0, z = getChildCount(); i < z; i++) {
-            final View child = getChildAt(i);
-            if (ViewCompat.getFitsSystemWindows(child)) {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                final Behavior b = lp.getBehavior();
-
-                if (b != null) {
-                    // If the view has a behavior, let it try first
-                    insets = b.onApplyWindowInsets(this, child, insets);
-                    if (insets.isConsumed()) {
-                        // If it consumed the insets, break
-                        break;
-                    }
-                }
-            }
-        }
-
-        return insets;
-    }
-
-    /**
-     * Called to lay out each individual child view unless a
-     * {@link Behavior Behavior} is present. The Behavior may choose to
-     * delegate child measurement to this method.
-     *
-     * @param child child view to lay out
-     * @param layoutDirection the resolved layout direction for the CoordinatorLayout, such as
-     *                        {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
-     *                        {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
-     */
-    public void onLayoutChild(View child, int layoutDirection) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (lp.checkAnchorChanged()) {
-            throw new IllegalStateException("An anchor may not be changed after CoordinatorLayout"
-                    + " measurement begins before layout is complete.");
-        }
-        if (lp.mAnchorView != null) {
-            layoutChildWithAnchor(child, lp.mAnchorView, layoutDirection);
-        } else if (lp.keyline >= 0) {
-            layoutChildWithKeyline(child, lp.keyline, layoutDirection);
-        } else {
-            layoutChild(child, layoutDirection);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        final int layoutDirection = ViewCompat.getLayoutDirection(this);
-        final int childCount = mDependencySortedChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            final View child = mDependencySortedChildren.get(i);
-            if (child.getVisibility() == GONE) {
-                // If the child is GONE, skip...
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final Behavior behavior = lp.getBehavior();
-
-            if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
-                onLayoutChild(child, layoutDirection);
-            }
-        }
-    }
-
-    @Override
-    public void onDraw(Canvas c) {
-        super.onDraw(c);
-        if (mDrawStatusBarBackground && mStatusBarBackground != null) {
-            final int inset = mLastInsets != null ? mLastInsets.getSystemWindowInsetTop() : 0;
-            if (inset > 0) {
-                mStatusBarBackground.setBounds(0, 0, getWidth(), inset);
-                mStatusBarBackground.draw(c);
-            }
-        }
-    }
-
-    @Override
-    public void setFitsSystemWindows(boolean fitSystemWindows) {
-        super.setFitsSystemWindows(fitSystemWindows);
-        setupForInsets();
-    }
-
-    /**
-     * Mark the last known child position rect for the given child view.
-     * This will be used when checking if a child view's position has changed between frames.
-     * The rect used here should be one returned by
-     * {@link #getChildRect(View, boolean, Rect)}, with translation
-     * disabled.
-     *
-     * @param child child view to set for
-     * @param r rect to set
-     */
-    void recordLastChildRect(View child, Rect r) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        lp.setLastChildRect(r);
-    }
-
-    /**
-     * Get the last known child rect recorded by
-     * {@link #recordLastChildRect(View, Rect)}.
-     *
-     * @param child child view to retrieve from
-     * @param out rect to set to the outpur values
-     */
-    void getLastChildRect(View child, Rect out) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        out.set(lp.getLastChildRect());
-    }
-
-    /**
-     * Get the position rect for the given child. If the child has currently requested layout
-     * or has a visibility of GONE.
-     *
-     * @param child child view to check
-     * @param transform true to include transformation in the output rect, false to
-     *                        only account for the base position
-     * @param out rect to set to the output values
-     */
-    void getChildRect(View child, boolean transform, Rect out) {
-        if (child.isLayoutRequested() || child.getVisibility() == View.GONE) {
-            out.setEmpty();
-            return;
-        }
-        if (transform) {
-            getDescendantRect(child, out);
-        } else {
-            out.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
-        }
-    }
-
-    private void getDesiredAnchoredChildRectWithoutConstraints(View child, int layoutDirection,
-            Rect anchorRect, Rect out, LayoutParams lp, int childWidth, int childHeight) {
-        final int absGravity = GravityCompat.getAbsoluteGravity(
-                resolveAnchoredChildGravity(lp.gravity), layoutDirection);
-        final int absAnchorGravity = GravityCompat.getAbsoluteGravity(
-                resolveGravity(lp.anchorGravity),
-                layoutDirection);
-
-        final int hgrav = absGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        final int vgrav = absGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int anchorHgrav = absAnchorGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        final int anchorVgrav = absAnchorGravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-        int left;
-        int top;
-
-        // Align to the anchor. This puts us in an assumed right/bottom child view gravity.
-        // If this is not the case we will subtract out the appropriate portion of
-        // the child size below.
-        switch (anchorHgrav) {
-            default:
-            case Gravity.LEFT:
-                left = anchorRect.left;
-                break;
-            case Gravity.RIGHT:
-                left = anchorRect.right;
-                break;
-            case Gravity.CENTER_HORIZONTAL:
-                left = anchorRect.left + anchorRect.width() / 2;
-                break;
-        }
-
-        switch (anchorVgrav) {
-            default:
-            case Gravity.TOP:
-                top = anchorRect.top;
-                break;
-            case Gravity.BOTTOM:
-                top = anchorRect.bottom;
-                break;
-            case Gravity.CENTER_VERTICAL:
-                top = anchorRect.top + anchorRect.height() / 2;
-                break;
-        }
-
-        // Offset by the child view's gravity itself. The above assumed right/bottom gravity.
-        switch (hgrav) {
-            default:
-            case Gravity.LEFT:
-                left -= childWidth;
-                break;
-            case Gravity.RIGHT:
-                // Do nothing, we're already in position.
-                break;
-            case Gravity.CENTER_HORIZONTAL:
-                left -= childWidth / 2;
-                break;
-        }
-
-        switch (vgrav) {
-            default:
-            case Gravity.TOP:
-                top -= childHeight;
-                break;
-            case Gravity.BOTTOM:
-                // Do nothing, we're already in position.
-                break;
-            case Gravity.CENTER_VERTICAL:
-                top -= childHeight / 2;
-                break;
-        }
-
-        out.set(left, top, left + childWidth, top + childHeight);
-    }
-
-    private void constrainChildRect(LayoutParams lp, Rect out, int childWidth, int childHeight) {
-        final int width = getWidth();
-        final int height = getHeight();
-
-        // Obey margins and padding
-        int left = Math.max(getPaddingLeft() + lp.leftMargin,
-                Math.min(out.left,
-                        width - getPaddingRight() - childWidth - lp.rightMargin));
-        int top = Math.max(getPaddingTop() + lp.topMargin,
-                Math.min(out.top,
-                        height - getPaddingBottom() - childHeight - lp.bottomMargin));
-
-        out.set(left, top, left + childWidth, top + childHeight);
-    }
-
-    /**
-     * Calculate the desired child rect relative to an anchor rect, respecting both
-     * gravity and anchorGravity.
-     *
-     * @param child child view to calculate a rect for
-     * @param layoutDirection the desired layout direction for the CoordinatorLayout
-     * @param anchorRect rect in CoordinatorLayout coordinates of the anchor view area
-     * @param out rect to set to the output values
-     */
-    void getDesiredAnchoredChildRect(View child, int layoutDirection, Rect anchorRect, Rect out) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final int childWidth = child.getMeasuredWidth();
-        final int childHeight = child.getMeasuredHeight();
-        getDesiredAnchoredChildRectWithoutConstraints(child, layoutDirection, anchorRect, out, lp,
-                childWidth, childHeight);
-        constrainChildRect(lp, out, childWidth, childHeight);
-    }
-
-    /**
-     * CORE ASSUMPTION: anchor has been laid out by the time this is called for a given child view.
-     *
-     * @param child child to lay out
-     * @param anchor view to anchor child relative to; already laid out.
-     * @param layoutDirection ViewCompat constant for layout direction
-     */
-    private void layoutChildWithAnchor(View child, View anchor, int layoutDirection) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-        final Rect anchorRect = acquireTempRect();
-        final Rect childRect = acquireTempRect();
-        try {
-            getDescendantRect(anchor, anchorRect);
-            getDesiredAnchoredChildRect(child, layoutDirection, anchorRect, childRect);
-            child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
-        } finally {
-            releaseTempRect(anchorRect);
-            releaseTempRect(childRect);
-        }
-    }
-
-    /**
-     * Lay out a child view with respect to a keyline.
-     *
-     * <p>The keyline represents a horizontal offset from the unpadded starting edge of
-     * the CoordinatorLayout. The child's gravity will affect how it is positioned with
-     * respect to the keyline.</p>
-     *
-     * @param child child to lay out
-     * @param keyline offset from the starting edge in pixels of the keyline to align with
-     * @param layoutDirection ViewCompat constant for layout direction
-     */
-    private void layoutChildWithKeyline(View child, int keyline, int layoutDirection) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final int absGravity = GravityCompat.getAbsoluteGravity(
-                resolveKeylineGravity(lp.gravity), layoutDirection);
-
-        final int hgrav = absGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        final int vgrav = absGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int width = getWidth();
-        final int height = getHeight();
-        final int childWidth = child.getMeasuredWidth();
-        final int childHeight = child.getMeasuredHeight();
-
-        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
-            keyline = width - keyline;
-        }
-
-        int left = getKeyline(keyline) - childWidth;
-        int top = 0;
-
-        switch (hgrav) {
-            default:
-            case Gravity.LEFT:
-                // Nothing to do.
-                break;
-            case Gravity.RIGHT:
-                left += childWidth;
-                break;
-            case Gravity.CENTER_HORIZONTAL:
-                left += childWidth / 2;
-                break;
-        }
-
-        switch (vgrav) {
-            default:
-            case Gravity.TOP:
-                // Do nothing, we're already in position.
-                break;
-            case Gravity.BOTTOM:
-                top += childHeight;
-                break;
-            case Gravity.CENTER_VERTICAL:
-                top += childHeight / 2;
-                break;
-        }
-
-        // Obey margins and padding
-        left = Math.max(getPaddingLeft() + lp.leftMargin,
-                Math.min(left,
-                        width - getPaddingRight() - childWidth - lp.rightMargin));
-        top = Math.max(getPaddingTop() + lp.topMargin,
-                Math.min(top,
-                        height - getPaddingBottom() - childHeight - lp.bottomMargin));
-
-        child.layout(left, top, left + childWidth, top + childHeight);
-    }
-
-    /**
-     * Lay out a child view with no special handling. This will position the child as
-     * if it were within a FrameLayout or similar simple frame.
-     *
-     * @param child child view to lay out
-     * @param layoutDirection ViewCompat constant for the desired layout direction
-     */
-    private void layoutChild(View child, int layoutDirection) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final Rect parent = acquireTempRect();
-        parent.set(getPaddingLeft() + lp.leftMargin,
-                getPaddingTop() + lp.topMargin,
-                getWidth() - getPaddingRight() - lp.rightMargin,
-                getHeight() - getPaddingBottom() - lp.bottomMargin);
-
-        if (mLastInsets != null && ViewCompat.getFitsSystemWindows(this)
-                && !ViewCompat.getFitsSystemWindows(child)) {
-            // If we're set to handle insets but this child isn't, then it has been measured as
-            // if there are no insets. We need to lay it out to match.
-            parent.left += mLastInsets.getSystemWindowInsetLeft();
-            parent.top += mLastInsets.getSystemWindowInsetTop();
-            parent.right -= mLastInsets.getSystemWindowInsetRight();
-            parent.bottom -= mLastInsets.getSystemWindowInsetBottom();
-        }
-
-        final Rect out = acquireTempRect();
-        GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
-                child.getMeasuredHeight(), parent, out, layoutDirection);
-        child.layout(out.left, out.top, out.right, out.bottom);
-
-        releaseTempRect(parent);
-        releaseTempRect(out);
-    }
-
-    /**
-     * Return the given gravity value, but if either or both of the axes doesn't have any gravity
-     * specified, the default value (start or top) is specified. This should be used for children
-     * that are not anchored to another view or a keyline.
-     */
-    private static int resolveGravity(int gravity) {
-        if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.NO_GRAVITY) {
-            gravity |= GravityCompat.START;
-        }
-        if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.NO_GRAVITY) {
-            gravity |= Gravity.TOP;
-        }
-        return gravity;
-    }
-
-    /**
-     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
-     * This should be used for children that are positioned relative to a keyline.
-     */
-    private static int resolveKeylineGravity(int gravity) {
-        return gravity == Gravity.NO_GRAVITY ? GravityCompat.END | Gravity.TOP : gravity;
-    }
-
-    /**
-     * Return the given gravity value or the default if the passed value is NO_GRAVITY.
-     * This should be used for children that are anchored to another view.
-     */
-    private static int resolveAnchoredChildGravity(int gravity) {
-        return gravity == Gravity.NO_GRAVITY ? Gravity.CENTER : gravity;
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (lp.mBehavior != null) {
-            final float scrimAlpha = lp.mBehavior.getScrimOpacity(this, child);
-            if (scrimAlpha > 0f) {
-                if (mScrimPaint == null) {
-                    mScrimPaint = new Paint();
-                }
-                mScrimPaint.setColor(lp.mBehavior.getScrimColor(this, child));
-                mScrimPaint.setAlpha(MathUtils.clamp(Math.round(255 * scrimAlpha), 0, 255));
-
-                final int saved = canvas.save();
-                if (child.isOpaque()) {
-                    // If the child is opaque, there is no need to draw behind it so we'll inverse
-                    // clip the canvas
-                    canvas.clipRect(child.getLeft(), child.getTop(), child.getRight(),
-                            child.getBottom(), Region.Op.DIFFERENCE);
-                }
-                // Now draw the rectangle for the scrim
-                canvas.drawRect(getPaddingLeft(), getPaddingTop(),
-                        getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(),
-                        mScrimPaint);
-                canvas.restoreToCount(saved);
-            }
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    /**
-     * Dispatch any dependent view changes to the relevant {@link Behavior} instances.
-     *
-     * Usually run as part of the pre-draw step when at least one child view has a reported
-     * dependency on another view. This allows CoordinatorLayout to account for layout
-     * changes and animations that occur outside of the normal layout pass.
-     *
-     * It can also be ran as part of the nested scrolling dispatch to ensure that any offsetting
-     * is completed within the correct coordinate window.
-     *
-     * The offsetting behavior implemented here does not store the computed offset in
-     * the LayoutParams; instead it expects that the layout process will always reconstruct
-     * the proper positioning.
-     *
-     * @param type the type of event which has caused this call
-     */
-    final void onChildViewsChanged(@DispatchChangeEvent final int type) {
-        final int layoutDirection = ViewCompat.getLayoutDirection(this);
-        final int childCount = mDependencySortedChildren.size();
-        final Rect inset = acquireTempRect();
-        final Rect drawRect = acquireTempRect();
-        final Rect lastDrawRect = acquireTempRect();
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = mDependencySortedChildren.get(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (type == EVENT_PRE_DRAW && child.getVisibility() == View.GONE) {
-                // Do not try to update GONE child views in pre draw updates.
-                continue;
-            }
-
-            // Check child views before for anchor
-            for (int j = 0; j < i; j++) {
-                final View checkChild = mDependencySortedChildren.get(j);
-
-                if (lp.mAnchorDirectChild == checkChild) {
-                    offsetChildToAnchor(child, layoutDirection);
-                }
-            }
-
-            // Get the current draw rect of the view
-            getChildRect(child, true, drawRect);
-
-            // Accumulate inset sizes
-            if (lp.insetEdge != Gravity.NO_GRAVITY && !drawRect.isEmpty()) {
-                final int absInsetEdge = GravityCompat.getAbsoluteGravity(
-                        lp.insetEdge, layoutDirection);
-                switch (absInsetEdge & Gravity.VERTICAL_GRAVITY_MASK) {
-                    case Gravity.TOP:
-                        inset.top = Math.max(inset.top, drawRect.bottom);
-                        break;
-                    case Gravity.BOTTOM:
-                        inset.bottom = Math.max(inset.bottom, getHeight() - drawRect.top);
-                        break;
-                }
-                switch (absInsetEdge & Gravity.HORIZONTAL_GRAVITY_MASK) {
-                    case Gravity.LEFT:
-                        inset.left = Math.max(inset.left, drawRect.right);
-                        break;
-                    case Gravity.RIGHT:
-                        inset.right = Math.max(inset.right, getWidth() - drawRect.left);
-                        break;
-                }
-            }
-
-            // Dodge inset edges if necessary
-            if (lp.dodgeInsetEdges != Gravity.NO_GRAVITY && child.getVisibility() == View.VISIBLE) {
-                offsetChildByInset(child, inset, layoutDirection);
-            }
-
-            if (type != EVENT_VIEW_REMOVED) {
-                // Did it change? if not continue
-                getLastChildRect(child, lastDrawRect);
-                if (lastDrawRect.equals(drawRect)) {
-                    continue;
-                }
-                recordLastChildRect(child, drawRect);
-            }
-
-            // Update any behavior-dependent views for the change
-            for (int j = i + 1; j < childCount; j++) {
-                final View checkChild = mDependencySortedChildren.get(j);
-                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
-                final Behavior b = checkLp.getBehavior();
-
-                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
-                    if (type == EVENT_PRE_DRAW && checkLp.getChangedAfterNestedScroll()) {
-                        // If this is from a pre-draw and we have already been changed
-                        // from a nested scroll, skip the dispatch and reset the flag
-                        checkLp.resetChangedAfterNestedScroll();
-                        continue;
-                    }
-
-                    final boolean handled;
-                    switch (type) {
-                        case EVENT_VIEW_REMOVED:
-                            // EVENT_VIEW_REMOVED means that we need to dispatch
-                            // onDependentViewRemoved() instead
-                            b.onDependentViewRemoved(this, checkChild, child);
-                            handled = true;
-                            break;
-                        default:
-                            // Otherwise we dispatch onDependentViewChanged()
-                            handled = b.onDependentViewChanged(this, checkChild, child);
-                            break;
-                    }
-
-                    if (type == EVENT_NESTED_SCROLL) {
-                        // If this is from a nested scroll, set the flag so that we may skip
-                        // any resulting onPreDraw dispatch (if needed)
-                        checkLp.setChangedAfterNestedScroll(handled);
-                    }
-                }
-            }
-        }
-
-        releaseTempRect(inset);
-        releaseTempRect(drawRect);
-        releaseTempRect(lastDrawRect);
-    }
-
-    private void offsetChildByInset(final View child, final Rect inset, final int layoutDirection) {
-        if (!ViewCompat.isLaidOut(child)) {
-            // The view has not been laid out yet, so we can't obtain its bounds.
-            return;
-        }
-
-        if (child.getWidth() <= 0 || child.getHeight() <= 0) {
-            // Bounds are empty so there is nothing to dodge against, skip...
-            return;
-        }
-
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final Behavior behavior = lp.getBehavior();
-        final Rect dodgeRect = acquireTempRect();
-        final Rect bounds = acquireTempRect();
-        bounds.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
-
-        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());
-            }
-        } else {
-            dodgeRect.set(bounds);
-        }
-
-        // We can release the bounds rect now
-        releaseTempRect(bounds);
-
-        if (dodgeRect.isEmpty()) {
-            // Rect is empty so there is nothing to dodge against, skip...
-            releaseTempRect(dodgeRect);
-            return;
-        }
-
-        final int absDodgeInsetEdges = GravityCompat.getAbsoluteGravity(lp.dodgeInsetEdges,
-                layoutDirection);
-
-        boolean offsetY = false;
-        if ((absDodgeInsetEdges & Gravity.TOP) == Gravity.TOP) {
-            int distance = dodgeRect.top - lp.topMargin - lp.mInsetOffsetY;
-            if (distance < inset.top) {
-                setInsetOffsetY(child, inset.top - distance);
-                offsetY = true;
-            }
-        }
-        if ((absDodgeInsetEdges & Gravity.BOTTOM) == Gravity.BOTTOM) {
-            int distance = getHeight() - dodgeRect.bottom - lp.bottomMargin + lp.mInsetOffsetY;
-            if (distance < inset.bottom) {
-                setInsetOffsetY(child, distance - inset.bottom);
-                offsetY = true;
-            }
-        }
-        if (!offsetY) {
-            setInsetOffsetY(child, 0);
-        }
-
-        boolean offsetX = false;
-        if ((absDodgeInsetEdges & Gravity.LEFT) == Gravity.LEFT) {
-            int distance = dodgeRect.left - lp.leftMargin - lp.mInsetOffsetX;
-            if (distance < inset.left) {
-                setInsetOffsetX(child, inset.left - distance);
-                offsetX = true;
-            }
-        }
-        if ((absDodgeInsetEdges & Gravity.RIGHT) == Gravity.RIGHT) {
-            int distance = getWidth() - dodgeRect.right - lp.rightMargin + lp.mInsetOffsetX;
-            if (distance < inset.right) {
-                setInsetOffsetX(child, distance - inset.right);
-                offsetX = true;
-            }
-        }
-        if (!offsetX) {
-            setInsetOffsetX(child, 0);
-        }
-
-        releaseTempRect(dodgeRect);
-    }
-
-    private void setInsetOffsetX(View child, int offsetX) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (lp.mInsetOffsetX != offsetX) {
-            final int dx = offsetX - lp.mInsetOffsetX;
-            ViewCompat.offsetLeftAndRight(child, dx);
-            lp.mInsetOffsetX = offsetX;
-        }
-    }
-
-    private void setInsetOffsetY(View child, int offsetY) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (lp.mInsetOffsetY != offsetY) {
-            final int dy = offsetY - lp.mInsetOffsetY;
-            ViewCompat.offsetTopAndBottom(child, dy);
-            lp.mInsetOffsetY = offsetY;
-        }
-    }
-
-    /**
-     * Allows the caller to manually dispatch
-     * {@link Behavior#onDependentViewChanged(CoordinatorLayout, View, View)} to the associated
-     * {@link Behavior} instances of views which depend on the provided {@link View}.
-     *
-     * <p>You should not normally need to call this method as the it will be automatically done
-     * when the view has changed.
-     *
-     * @param view the View to find dependents of to dispatch the call.
-     */
-    public void dispatchDependentViewsChanged(View view) {
-        final List<View> dependents = mChildDag.getIncomingEdges(view);
-        if (dependents != null && !dependents.isEmpty()) {
-            for (int i = 0; i < dependents.size(); i++) {
-                final View child = dependents.get(i);
-                LayoutParams lp = (LayoutParams)
-                        child.getLayoutParams();
-                Behavior b = lp.getBehavior();
-                if (b != null) {
-                    b.onDependentViewChanged(this, child, view);
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns the list of views which the provided view depends on. Do not store this list as its
-     * contents may not be valid beyond the caller.
-     *
-     * @param child the view to find dependencies for.
-     *
-     * @return the list of views which {@code child} depends on.
-     */
-    @NonNull
-    public List<View> getDependencies(@NonNull View child) {
-        final List<View> dependencies = mChildDag.getOutgoingEdges(child);
-        mTempDependenciesList.clear();
-        if (dependencies != null) {
-            mTempDependenciesList.addAll(dependencies);
-        }
-        return mTempDependenciesList;
-    }
-
-    /**
-     * Returns the list of views which depend on the provided view. Do not store this list as its
-     * contents may not be valid beyond the caller.
-     *
-     * @param child the view to find dependents of.
-     *
-     * @return the list of views which depend on {@code child}.
-     */
-    @NonNull
-    public List<View> getDependents(@NonNull View child) {
-        final List<View> edges = mChildDag.getIncomingEdges(child);
-        mTempDependenciesList.clear();
-        if (edges != null) {
-            mTempDependenciesList.addAll(edges);
-        }
-        return mTempDependenciesList;
-    }
-
-    @VisibleForTesting
-    final List<View> getDependencySortedChildren() {
-        prepareChildren();
-        return Collections.unmodifiableList(mDependencySortedChildren);
-    }
-
-    /**
-     * Add or remove the pre-draw listener as necessary.
-     */
-    void ensurePreDrawListener() {
-        boolean hasDependencies = false;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (hasDependencies(child)) {
-                hasDependencies = true;
-                break;
-            }
-        }
-
-        if (hasDependencies != mNeedsPreDrawListener) {
-            if (hasDependencies) {
-                addPreDrawListener();
-            } else {
-                removePreDrawListener();
-            }
-        }
-    }
-
-    /**
-     * Check if the given child has any layout dependencies on other child views.
-     */
-    private boolean hasDependencies(View child) {
-        return mChildDag.hasOutgoingEdges(child);
-    }
-
-    /**
-     * Add the pre-draw listener if we're attached to a window and mark that we currently
-     * need it when attached.
-     */
-    void addPreDrawListener() {
-        if (mIsAttachedToWindow) {
-            // Add the listener
-            if (mOnPreDrawListener == null) {
-                mOnPreDrawListener = new OnPreDrawListener();
-            }
-            final ViewTreeObserver vto = getViewTreeObserver();
-            vto.addOnPreDrawListener(mOnPreDrawListener);
-        }
-
-        // Record that we need the listener regardless of whether or not we're attached.
-        // We'll add the real listener when we become attached.
-        mNeedsPreDrawListener = true;
-    }
-
-    /**
-     * Remove the pre-draw listener if we're attached to a window and mark that we currently
-     * do not need it when attached.
-     */
-    void removePreDrawListener() {
-        if (mIsAttachedToWindow) {
-            if (mOnPreDrawListener != null) {
-                final ViewTreeObserver vto = getViewTreeObserver();
-                vto.removeOnPreDrawListener(mOnPreDrawListener);
-            }
-        }
-        mNeedsPreDrawListener = false;
-    }
-
-    /**
-     * Adjust the child left, top, right, bottom rect to the correct anchor view position,
-     * respecting gravity and anchor gravity.
-     *
-     * Note that child translation properties are ignored in this process, allowing children
-     * to be animated away from their anchor. However, if the anchor view is animated,
-     * the child will be offset to match the anchor's translated position.
-     */
-    void offsetChildToAnchor(View child, int layoutDirection) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (lp.mAnchorView != null) {
-            final Rect anchorRect = acquireTempRect();
-            final Rect childRect = acquireTempRect();
-            final Rect desiredChildRect = acquireTempRect();
-
-            getDescendantRect(lp.mAnchorView, anchorRect);
-            getChildRect(child, false, childRect);
-
-            int childWidth = child.getMeasuredWidth();
-            int childHeight = child.getMeasuredHeight();
-            getDesiredAnchoredChildRectWithoutConstraints(child, layoutDirection, anchorRect,
-                    desiredChildRect, lp, childWidth, childHeight);
-            boolean changed = desiredChildRect.left != childRect.left ||
-                    desiredChildRect.top != childRect.top;
-            constrainChildRect(lp, desiredChildRect, childWidth, childHeight);
-
-            final int dx = desiredChildRect.left - childRect.left;
-            final int dy = desiredChildRect.top - childRect.top;
-
-            if (dx != 0) {
-                ViewCompat.offsetLeftAndRight(child, dx);
-            }
-            if (dy != 0) {
-                ViewCompat.offsetTopAndBottom(child, dy);
-            }
-
-            if (changed) {
-                // If we have needed to move, make sure to notify the child's Behavior
-                final Behavior b = lp.getBehavior();
-                if (b != null) {
-                    b.onDependentViewChanged(this, child, lp.mAnchorView);
-                }
-            }
-
-            releaseTempRect(anchorRect);
-            releaseTempRect(childRect);
-            releaseTempRect(desiredChildRect);
-        }
-    }
-
-    /**
-     * Check if a given point in the CoordinatorLayout's coordinates are within the view bounds
-     * of the given direct child view.
-     *
-     * @param child child view to test
-     * @param x X coordinate to test, in the CoordinatorLayout's coordinate system
-     * @param y Y coordinate to test, in the CoordinatorLayout's coordinate system
-     * @return true if the point is within the child view's bounds, false otherwise
-     */
-    public boolean isPointInChildBounds(View child, int x, int y) {
-        final Rect r = acquireTempRect();
-        getDescendantRect(child, r);
-        try {
-            return r.contains(x, y);
-        } finally {
-            releaseTempRect(r);
-        }
-    }
-
-    /**
-     * Check whether two views overlap each other. The views need to be descendants of this
-     * {@link CoordinatorLayout} in the view hierarchy.
-     *
-     * @param first first child view to test
-     * @param second second child view to test
-     * @return true if both views are visible and overlap each other
-     */
-    public boolean doViewsOverlap(View first, View second) {
-        if (first.getVisibility() == VISIBLE && second.getVisibility() == VISIBLE) {
-            final Rect firstRect = acquireTempRect();
-            getChildRect(first, first.getParent() != this, firstRect);
-            final Rect secondRect = acquireTempRect();
-            getChildRect(second, second.getParent() != this, secondRect);
-            try {
-                return !(firstRect.left > secondRect.right || firstRect.top > secondRect.bottom
-                        || firstRect.right < secondRect.left || firstRect.bottom < secondRect.top);
-            } finally {
-                releaseTempRect(firstRect);
-                releaseTempRect(secondRect);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        if (p instanceof LayoutParams) {
-            return new LayoutParams((LayoutParams) p);
-        } else if (p instanceof MarginLayoutParams) {
-            return new LayoutParams((MarginLayoutParams) p);
-        }
-        return new LayoutParams(p);
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams && super.checkLayoutParams(p);
-    }
-
-    @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();
-        for (int i = 0; i < childCount; i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() == View.GONE) {
-                // If it's GONE, don't dispatch
-                continue;
-            }
-            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            final Behavior viewBehavior = lp.getBehavior();
-            if (viewBehavior != null) {
-                final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child,
-                        target, axes, type);
-                handled |= accepted;
-                lp.setNestedScrollAccepted(type, accepted);
-            } else {
-                lp.setNestedScrollAccepted(type, false);
-            }
-        }
-        return handled;
-    }
-
-    @Override
-    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
-        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(type)) {
-                continue;
-            }
-
-            final Behavior viewBehavior = lp.getBehavior();
-            if (viewBehavior != null) {
-                viewBehavior.onNestedScrollAccepted(this, view, child, target,
-                        nestedScrollAxes, type);
-            }
-        }
-    }
-
-    @Override
-    public void onStopNestedScroll(View 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(type)) {
-                continue;
-            }
-
-            final Behavior viewBehavior = lp.getBehavior();
-            if (viewBehavior != null) {
-                viewBehavior.onStopNestedScroll(this, view, target, type);
-            }
-            lp.resetNestedScroll(type);
-            lp.resetChangedAfterNestedScroll();
-        }
-        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;
-
-        for (int i = 0; i < childCount; i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() == GONE) {
-                // If the child is GONE, skip...
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted(type)) {
-                continue;
-            }
-
-            final Behavior viewBehavior = lp.getBehavior();
-            if (viewBehavior != null) {
-                viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed,
-                        dxUnconsumed, dyUnconsumed, type);
-                accepted = true;
-            }
-        }
-
-        if (accepted) {
-            onChildViewsChanged(EVENT_NESTED_SCROLL);
-        }
-    }
-
-    @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;
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() == GONE) {
-                // If the child is GONE, skip...
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            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, type);
-
-                xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
-                        : Math.min(xConsumed, mTempIntPair[0]);
-                yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1])
-                        : Math.min(yConsumed, mTempIntPair[1]);
-
-                accepted = true;
-            }
-        }
-
-        consumed[0] = xConsumed;
-        consumed[1] = yConsumed;
-
-        if (accepted) {
-            onChildViewsChanged(EVENT_NESTED_SCROLL);
-        }
-    }
-
-    @Override
-    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
-        boolean handled = false;
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() == GONE) {
-                // If the child is GONE, skip...
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted(ViewCompat.TYPE_TOUCH)) {
-                continue;
-            }
-
-            final Behavior viewBehavior = lp.getBehavior();
-            if (viewBehavior != null) {
-                handled |= viewBehavior.onNestedFling(this, view, target, velocityX, velocityY,
-                        consumed);
-            }
-        }
-        if (handled) {
-            onChildViewsChanged(EVENT_NESTED_SCROLL);
-        }
-        return handled;
-    }
-
-    @Override
-    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
-        boolean handled = false;
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View view = getChildAt(i);
-            if (view.getVisibility() == GONE) {
-                // If the child is GONE, skip...
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            if (!lp.isNestedScrollAccepted(ViewCompat.TYPE_TOUCH)) {
-                continue;
-            }
-
-            final Behavior viewBehavior = lp.getBehavior();
-            if (viewBehavior != null) {
-                handled |= viewBehavior.onNestedPreFling(this, view, target, velocityX, velocityY);
-            }
-        }
-        return handled;
-    }
-
-    @Override
-    public int getNestedScrollAxes() {
-        return mNestedScrollingParentHelper.getNestedScrollAxes();
-    }
-
-    class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
-        @Override
-        public boolean onPreDraw() {
-            onChildViewsChanged(EVENT_PRE_DRAW);
-            return true;
-        }
-    }
-
-    /**
-     * Sorts child views with higher Z values to the beginning of a collection.
-     */
-    static class ViewElevationComparator implements Comparator<View> {
-        @Override
-        public int compare(View lhs, View rhs) {
-            final float lz = ViewCompat.getZ(lhs);
-            final float rz = ViewCompat.getZ(rhs);
-            if (lz > rz) {
-                return -1;
-            } else if (lz < rz) {
-                return 1;
-            }
-            return 0;
-        }
-    }
-
-    /**
-     * Defines the default {@link Behavior} of a {@link View} class.
-     *
-     * <p>When writing a custom view, use this annotation to define the default behavior
-     * when used as a direct child of an {@link CoordinatorLayout}. The default behavior
-     * can be overridden using {@link LayoutParams#setBehavior}.</p>
-     *
-     * <p>Example: <code>@DefaultBehavior(MyBehavior.class)</code></p>
-     */
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface DefaultBehavior {
-        Class<? extends Behavior> value();
-    }
-
-    /**
-     * Interaction behavior plugin for child views of {@link CoordinatorLayout}.
-     *
-     * <p>A Behavior implements one or more interactions that a user can take on a child view.
-     * These interactions may include drags, swipes, flings, or any other gestures.</p>
-     *
-     * @param <V> The View type that this Behavior operates on
-     */
-    public static abstract class Behavior<V extends View> {
-
-        /**
-         * Default constructor for instantiating Behaviors.
-         */
-        public Behavior() {
-        }
-
-        /**
-         * Default constructor for inflating Behaviors from layout. The Behavior will have
-         * the opportunity to parse specially defined layout parameters. These parameters will
-         * appear on the child view tag.
-         *
-         * @param context
-         * @param attrs
-         */
-        public Behavior(Context context, AttributeSet attrs) {
-        }
-
-        /**
-         * Called when the Behavior has been attached to a LayoutParams instance.
-         *
-         * <p>This will be called after the LayoutParams has been instantiated and can be
-         * modified.</p>
-         *
-         * @param params the LayoutParams instance that this Behavior has been attached to
-         */
-        public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
-        }
-
-        /**
-         * Called when the Behavior has been detached from its holding LayoutParams instance.
-         *
-         * <p>This will only be called if the Behavior has been explicitly removed from the
-         * LayoutParams instance via {@link LayoutParams#setBehavior(Behavior)}. It will not be
-         * called if the associated view is removed from the CoordinatorLayout or similar.</p>
-         */
-        public void onDetachedFromLayoutParams() {
-        }
-
-        /**
-         * Respond to CoordinatorLayout touch events before they are dispatched to child views.
-         *
-         * <p>Behaviors can use this to monitor inbound touch events until one decides to
-         * intercept the rest of the event stream to take an action on its associated child view.
-         * This method will return false until it detects the proper intercept conditions, then
-         * return true once those conditions have occurred.</p>
-         *
-         * <p>Once a Behavior intercepts touch events, the rest of the event stream will
-         * be sent to the {@link #onTouchEvent} method.</p>
-         *
-         * <p>This method will be called regardless of the visibility of the associated child
-         * of the behavior. If you only wish to handle touch events when the child is visible, you
-         * should add a check to {@link View#isShown()} on the given child.</p>
-         *
-         * <p>The default implementation of this method always returns false.</p>
-         *
-         * @param parent the parent view currently receiving this touch event
-         * @param child the child view associated with this Behavior
-         * @param ev the MotionEvent describing the touch event being processed
-         * @return true if this Behavior would like to intercept and take over the event stream.
-         *         The default always returns false.
-         */
-        public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
-            return false;
-        }
-
-        /**
-         * Respond to CoordinatorLayout touch events after this Behavior has started
-         * {@link #onInterceptTouchEvent intercepting} them.
-         *
-         * <p>Behaviors may intercept touch events in order to help the CoordinatorLayout
-         * manipulate its child views. For example, a Behavior may allow a user to drag a
-         * UI pane open or closed. This method should perform actual mutations of view
-         * layout state.</p>
-         *
-         * <p>This method will be called regardless of the visibility of the associated child
-         * of the behavior. If you only wish to handle touch events when the child is visible, you
-         * should add a check to {@link View#isShown()} on the given child.</p>
-         *
-         * @param parent the parent view currently receiving this touch event
-         * @param child the child view associated with this Behavior
-         * @param ev the MotionEvent describing the touch event being processed
-         * @return true if this Behavior handled this touch event and would like to continue
-         *         receiving events in this stream. The default always returns false.
-         */
-        public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
-            return false;
-        }
-
-        /**
-         * Supply a scrim color that will be painted behind the associated child view.
-         *
-         * <p>A scrim may be used to indicate that the other elements beneath it are not currently
-         * interactive or actionable, drawing user focus and attention to the views above the scrim.
-         * </p>
-         *
-         * <p>The default implementation returns {@link Color#BLACK}.</p>
-         *
-         * @param parent the parent view of the given child
-         * @param child the child view above the scrim
-         * @return the desired scrim color in 0xAARRGGBB format. The default return value is
-         *         {@link Color#BLACK}.
-         * @see #getScrimOpacity(CoordinatorLayout, View)
-         */
-        @ColorInt
-        public int getScrimColor(CoordinatorLayout parent, V child) {
-            return Color.BLACK;
-        }
-
-        /**
-         * Determine the current opacity of the scrim behind a given child view
-         *
-         * <p>A scrim may be used to indicate that the other elements beneath it are not currently
-         * interactive or actionable, drawing user focus and attention to the views above the scrim.
-         * </p>
-         *
-         * <p>The default implementation returns 0.0f.</p>
-         *
-         * @param parent the parent view of the given child
-         * @param child the child view above the scrim
-         * @return the desired scrim opacity from 0.0f to 1.0f. The default return value is 0.0f.
-         */
-        @FloatRange(from = 0, to = 1)
-        public float getScrimOpacity(CoordinatorLayout parent, V child) {
-            return 0.f;
-        }
-
-        /**
-         * Determine whether interaction with views behind the given child in the child order
-         * should be blocked.
-         *
-         * <p>The default implementation returns true if
-         * {@link #getScrimOpacity(CoordinatorLayout, View)} would return > 0.0f.</p>
-         *
-         * @param parent the parent view of the given child
-         * @param child the child view to test
-         * @return true if {@link #getScrimOpacity(CoordinatorLayout, View)} would
-         *         return > 0.0f.
-         */
-        public boolean blocksInteractionBelow(CoordinatorLayout parent, V child) {
-            return getScrimOpacity(parent, child) > 0.f;
-        }
-
-        /**
-         * Determine whether the supplied child view has another specific sibling view as a
-         * layout dependency.
-         *
-         * <p>This method will be called at least once in response to a layout request. If it
-         * returns true for a given child and dependency view pair, the parent CoordinatorLayout
-         * will:</p>
-         * <ol>
-         *     <li>Always lay out this child after the dependent child is laid out, regardless
-         *     of child order.</li>
-         *     <li>Call {@link #onDependentViewChanged} when the dependency view's layout or
-         *     position changes.</li>
-         * </ol>
-         *
-         * @param parent the parent view of the given child
-         * @param child the child view to test
-         * @param dependency the proposed dependency of child
-         * @return true if child's layout depends on the proposed dependency's layout,
-         *         false otherwise
-         *
-         * @see #onDependentViewChanged(CoordinatorLayout, View, View)
-         */
-        public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
-            return false;
-        }
-
-        /**
-         * Respond to a change in a child's dependent view
-         *
-         * <p>This method is called whenever a dependent view changes in size or position outside
-         * of the standard layout flow. A Behavior may use this method to appropriately update
-         * the child view in response.</p>
-         *
-         * <p>A view's dependency is determined by
-         * {@link #layoutDependsOn(CoordinatorLayout, View, View)} or
-         * if {@code child} has set another view as it's anchor.</p>
-         *
-         * <p>Note that if a Behavior changes the layout of a child via this method, it should
-         * also be able to reconstruct the correct position in
-         * {@link #onLayoutChild(CoordinatorLayout, View, int) onLayoutChild}.
-         * <code>onDependentViewChanged</code> will not be called during normal layout since
-         * the layout of each child view will always happen in dependency order.</p>
-         *
-         * <p>If the Behavior changes the child view's size or position, it should return true.
-         * The default implementation returns false.</p>
-         *
-         * @param parent the parent view of the given child
-         * @param child the child view to manipulate
-         * @param dependency the dependent view that changed
-         * @return true if the Behavior changed the child view's size or position, false otherwise
-         */
-        public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
-            return false;
-        }
-
-        /**
-         * Respond to a child's dependent view being removed.
-         *
-         * <p>This method is called after a dependent view has been removed from the parent.
-         * A Behavior may use this method to appropriately update the child view in response.</p>
-         *
-         * <p>A view's dependency is determined by
-         * {@link #layoutDependsOn(CoordinatorLayout, View, View)} or
-         * if {@code child} has set another view as it's anchor.</p>
-         *
-         * @param parent the parent view of the given child
-         * @param child the child view to manipulate
-         * @param dependency the dependent view that has been removed
-         */
-        public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {
-        }
-
-        /**
-         * 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
-         * in place of the default child measurement behavior. The Behavior's implementation
-         * can delegate to the standard CoordinatorLayout measurement behavior by calling
-         * {@link CoordinatorLayout#onMeasureChild(View, int, int, int, int)
-         * parent.onMeasureChild}.</p>
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param child the child to measure
-         * @param parentWidthMeasureSpec the width requirements for this view
-         * @param widthUsed extra space that has been used up by the parent
-         *        horizontally (possibly by other children of the parent)
-         * @param parentHeightMeasureSpec the height requirements for this view
-         * @param heightUsed extra space that has been used up by the parent
-         *        vertically (possibly by other children of the parent)
-         * @return true if the Behavior measured the child view, false if the CoordinatorLayout
-         *         should perform its default measurement
-         */
-        public boolean onMeasureChild(CoordinatorLayout parent, V child,
-                int parentWidthMeasureSpec, int widthUsed,
-                int parentHeightMeasureSpec, int heightUsed) {
-            return false;
-        }
-
-        /**
-         * Called when the parent CoordinatorLayout is about the lay out the given child view.
-         *
-         * <p>This method can be used to perform custom or modified layout of a child view
-         * in place of the default child layout behavior. The Behavior's implementation can
-         * delegate to the standard CoordinatorLayout measurement behavior by calling
-         * {@link CoordinatorLayout#onLayoutChild(View, int)
-         * parent.onLayoutChild}.</p>
-         *
-         * <p>If a Behavior implements
-         * {@link #onDependentViewChanged(CoordinatorLayout, View, View)}
-         * to change the position of a view in response to a dependent view changing, it
-         * should also implement <code>onLayoutChild</code> in such a way that respects those
-         * dependent views. <code>onLayoutChild</code> will always be called for a dependent view
-         * <em>after</em> its dependency has been laid out.</p>
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param child child view to lay out
-         * @param layoutDirection the resolved layout direction for the CoordinatorLayout, such as
-         *                        {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
-         *                        {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
-         * @return true if the Behavior performed layout of the child view, false to request
-         *         default layout behavior
-         */
-        public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
-            return false;
-        }
-
-        // Utility methods for accessing child-specific, behavior-modifiable properties.
-
-        /**
-         * Associate a Behavior-specific tag object with the given child view.
-         * This object will be stored with the child view's LayoutParams.
-         *
-         * @param child child view to set tag with
-         * @param tag tag object to set
-         */
-        public static void setTag(View child, Object tag) {
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.mBehaviorTag = tag;
-        }
-
-        /**
-         * Get the behavior-specific tag object with the given child view.
-         * This object is stored with the child view's LayoutParams.
-         *
-         * @param child child view to get tag with
-         * @return the previously stored tag object
-         */
-        public static Object getTag(View child) {
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            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.
-         *
-         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may respond
-         * to this event and return true to indicate that the CoordinatorLayout should act as
-         * a nested scrolling parent for this scroll. Only Behaviors that return true from
-         * this method will receive subsequent nested scroll events.</p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @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 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 NestedScrollingParent2#onStartNestedScroll(View, View, int, int)
-         */
-        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
-         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
-         * that returned true will receive subsequent nested scroll events for that nested scroll.
-         * </p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @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 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 NestedScrollingParent2#onNestedScrollAccepted(View, View, int, int)
-         */
-        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
-        }
-
-        /**
-         * Called when a nested scroll has ended.
-         *
-         * <p>Any Behavior associated with any direct child of the CoordinatorLayout may elect
-         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
-         * that returned true will receive subsequent nested scroll events for that nested scroll.
-         * </p>
-         *
-         * <p><code>onStopNestedScroll</code> marks the end of a single nested scroll event
-         * sequence. This is a good place to clean up any state related to the nested scroll.
-         * </p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @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 NestedScrollingParent2#onStopNestedScroll(View, int)
-         */
-        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
-        }
-
-        /**
-         * Called when a nested scroll in progress has updated and the target has scrolled or
-         * attempted to scroll.
-         *
-         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
-         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
-         * that returned true will receive subsequent nested scroll events for that nested scroll.
-         * </p>
-         *
-         * <p><code>onNestedScroll</code> is called each time the nested scroll is updated by the
-         * nested scrolling child, with both consumed and unconsumed components of the scroll
-         * supplied in pixels. <em>Each Behavior responding to the nested scroll will receive the
-         * same values.</em>
-         * </p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
-         * @param dxConsumed horizontal pixels consumed by the target's own scrolling operation
-         * @param dyConsumed vertical pixels consumed by the target's own scrolling operation
-         * @param dxUnconsumed horizontal pixels not consumed by the target's own scrolling
-         *                     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 NestedScrollingParent2#onNestedScroll(View, int, int, int, int, int)
-         */
-        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
-        }
-
-        /**
-         * Called when a nested scroll in progress is about to update, before the target has
-         * consumed any of the scrolled distance.
-         *
-         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
-         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
-         * that returned true will receive subsequent nested scroll events for that nested scroll.
-         * </p>
-         *
-         * <p><code>onNestedPreScroll</code> is called each time the nested scroll is updated
-         * by the nested scrolling child, before the nested scrolling child has consumed the scroll
-         * distance itself. <em>Each Behavior responding to the nested scroll will receive the
-         * same values.</em> The CoordinatorLayout will report as consumed the maximum number
-         * of pixels in either direction that any Behavior responding to the nested scroll reported
-         * as consumed.</p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
-         * @param dx the raw horizontal number of pixels that the user attempted to scroll
-         * @param dy the raw vertical number of pixels that the user attempted to scroll
-         * @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 NestedScrollingParent2#onNestedPreScroll(View, int, int, int[], int)
-         */
-        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);
-            }
-        }
-
-        /**
-         * Called when a nested scrolling child is starting a fling or an action that would
-         * be a fling.
-         *
-         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
-         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
-         * that returned true will receive subsequent nested scroll events for that nested scroll.
-         * </p>
-         *
-         * <p><code>onNestedFling</code> is called when the current nested scrolling child view
-         * detects the proper conditions for a fling. It reports if the child itself consumed
-         * the fling. If it did not, the child is expected to show some sort of overscroll
-         * indication. This method should return true if it consumes the fling, so that a child
-         * that did not itself take an action in response can choose not to show an overfling
-         * indication.</p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
-         * @param velocityX horizontal velocity of the attempted fling
-         * @param velocityY vertical velocity of the attempted fling
-         * @param consumed true if the nested child view consumed the fling
-         * @return true if the Behavior consumed the fling
-         *
-         * @see NestedScrollingParent#onNestedFling(View, float, float, boolean)
-         */
-        public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout,
-                @NonNull V child, @NonNull View target, float velocityX, float velocityY,
-                boolean consumed) {
-            return false;
-        }
-
-        /**
-         * Called when a nested scrolling child is about to start a fling.
-         *
-         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
-         * to accept the nested scroll as part of {@link #onStartNestedScroll}. Each Behavior
-         * that returned true will receive subsequent nested scroll events for that nested scroll.
-         * </p>
-         *
-         * <p><code>onNestedPreFling</code> is called when the current nested scrolling child view
-         * detects the proper conditions for a fling, but it has not acted on it yet. A
-         * Behavior can return true to indicate that it consumed the fling. If at least one
-         * Behavior returns true, the fling should not be acted upon by the child.</p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @param target the descendant view of the CoordinatorLayout performing the nested scroll
-         * @param velocityX horizontal velocity of the attempted fling
-         * @param velocityY vertical velocity of the attempted fling
-         * @return true if the Behavior consumed the fling
-         *
-         * @see NestedScrollingParent#onNestedPreFling(View, float, float)
-         */
-        public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout,
-                @NonNull V child, @NonNull View target, float velocityX, float velocityY) {
-            return false;
-        }
-
-        /**
-         * Called when the window insets have changed.
-         *
-         * <p>Any Behavior associated with the direct child of the CoordinatorLayout may elect
-         * to handle the window inset change on behalf of it's associated view.
-         * </p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child the child view of the CoordinatorLayout this Behavior is associated with
-         * @param insets the new window insets.
-         *
-         * @return The insets supplied, minus any insets that were consumed
-         */
-        @NonNull
-        public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout,
-                V child, WindowInsetsCompat insets) {
-            return insets;
-        }
-
-        /**
-         * Called when a child of the view associated with this behavior wants a particular
-         * rectangle to be positioned onto the screen.
-         *
-         * <p>The contract for this method is the same as
-         * {@link ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)}.</p>
-         *
-         * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is
-         *                          associated with
-         * @param child             the child view of the CoordinatorLayout this Behavior is
-         *                          associated with
-         * @param rectangle         The rectangle which the child wishes to be on the screen
-         *                          in the child's coordinates
-         * @param immediate         true to forbid animated or delayed scrolling, false otherwise
-         * @return true if the Behavior handled the request
-         * @see ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)
-         */
-        public boolean onRequestChildRectangleOnScreen(CoordinatorLayout coordinatorLayout,
-                V child, Rect rectangle, boolean immediate) {
-            return false;
-        }
-
-        /**
-         * Hook allowing a behavior to re-apply a representation of its internal state that had
-         * previously been generated by {@link #onSaveInstanceState}. This function will never
-         * be called with a null state.
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param child child view to restore from
-         * @param state The frozen state that had previously been returned by
-         *        {@link #onSaveInstanceState}.
-         *
-         * @see #onSaveInstanceState()
-         */
-        public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
-            // no-op
-        }
-
-        /**
-         * Hook allowing a behavior to generate a representation of its internal state
-         * that can later be used to create a new instance with that same state.
-         * This state should only contain information that is not persistent or can
-         * not be reconstructed later.
-         *
-         * <p>Behavior state is only saved when both the parent {@link CoordinatorLayout} and
-         * a view using this behavior have valid IDs set.</p>
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param child child view to restore from
-         *
-         * @return Returns a Parcelable object containing the behavior's current dynamic
-         *         state.
-         *
-         * @see #onRestoreInstanceState(Parcelable)
-         * @see View#onSaveInstanceState()
-         */
-        public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
-            return BaseSavedState.EMPTY_STATE;
-        }
-
-        /**
-         * Called when a view is set to dodge view insets.
-         *
-         * <p>This method allows a behavior to update the rectangle that should be dodged.
-         * The rectangle should be in the parent's coordinate system and within the child's
-         * bounds. If not, a {@link IllegalArgumentException} is thrown.</p>
-         *
-         * @param parent the CoordinatorLayout parent of the view this Behavior is
-         *               associated with
-         * @param child  the child view of the CoordinatorLayout this Behavior is associated with
-         * @param rect   the rect to update with the dodge rectangle
-         * @return true the rect was updated, false if we should use the child's bounds
-         */
-        public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child,
-                @NonNull Rect rect) {
-            return false;
-        }
-    }
-
-    /**
-     * Parameters describing the desired layout for a child of a {@link CoordinatorLayout}.
-     */
-    public static class LayoutParams extends MarginLayoutParams {
-        /**
-         * A {@link Behavior} that the child view should obey.
-         */
-        Behavior mBehavior;
-
-        boolean mBehaviorResolved = false;
-
-        /**
-         * A {@link Gravity} value describing how this child view should lay out.
-         * If either or both of the axes are not specified, they are treated by CoordinatorLayout
-         * as {@link Gravity#TOP} or {@link GravityCompat#START}. If an
-         * {@link #setAnchorId(int) anchor} is also specified, the gravity describes how this child
-         * view should be positioned relative to its anchored position.
-         */
-        public int gravity = Gravity.NO_GRAVITY;
-
-        /**
-         * A {@link Gravity} value describing which edge of a child view's
-         * {@link #getAnchorId() anchor} view the child should position itself relative to.
-         */
-        public int anchorGravity = Gravity.NO_GRAVITY;
-
-        /**
-         * The index of the horizontal keyline specified to the parent CoordinatorLayout that this
-         * child should align relative to. If an {@link #setAnchorId(int) anchor} is present the
-         * keyline will be ignored.
-         */
-        public int keyline = -1;
-
-        /**
-         * A {@link View#getId() view id} of a descendant view of the CoordinatorLayout that
-         * this child should position relative to.
-         */
-        int mAnchorId = View.NO_ID;
-
-        /**
-         * A {@link Gravity} value describing how this child view insets the CoordinatorLayout.
-         * Other child views which are set to dodge the same inset edges will be moved appropriately
-         * so that the views do not overlap.
-         */
-        public int insetEdge = Gravity.NO_GRAVITY;
-
-        /**
-         * A {@link Gravity} value describing how this child view dodges any inset child views in
-         * the CoordinatorLayout. Any views which are inset on the same edge as this view is set to
-         * dodge will result in this view being moved so that the views do not overlap.
-         */
-        public int dodgeInsetEdges = Gravity.NO_GRAVITY;
-
-        int mInsetOffsetX;
-        int mInsetOffsetY;
-
-        View mAnchorView;
-        View mAnchorDirectChild;
-
-        private boolean mDidBlockInteraction;
-        private boolean mDidAcceptNestedScrollTouch;
-        private boolean mDidAcceptNestedScrollNonTouch;
-        private boolean mDidChangeAfterNestedScroll;
-
-        final Rect mLastChildRect = new Rect();
-
-        Object mBehaviorTag;
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        LayoutParams(Context context, AttributeSet attrs) {
-            super(context, attrs);
-
-            final TypedArray a = context.obtainStyledAttributes(attrs,
-                    R.styleable.CoordinatorLayout_Layout);
-
-            this.gravity = a.getInteger(
-                    R.styleable.CoordinatorLayout_Layout_android_layout_gravity,
-                    Gravity.NO_GRAVITY);
-            mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_Layout_layout_anchor,
-                    View.NO_ID);
-            this.anchorGravity = a.getInteger(
-                    R.styleable.CoordinatorLayout_Layout_layout_anchorGravity,
-                    Gravity.NO_GRAVITY);
-
-            this.keyline = a.getInteger(R.styleable.CoordinatorLayout_Layout_layout_keyline,
-                    -1);
-
-            insetEdge = a.getInt(R.styleable.CoordinatorLayout_Layout_layout_insetEdge, 0);
-            dodgeInsetEdges = a.getInt(
-                    R.styleable.CoordinatorLayout_Layout_layout_dodgeInsetEdges, 0);
-            mBehaviorResolved = a.hasValue(
-                    R.styleable.CoordinatorLayout_Layout_layout_behavior);
-            if (mBehaviorResolved) {
-                mBehavior = parseBehavior(context, attrs, a.getString(
-                        R.styleable.CoordinatorLayout_Layout_layout_behavior));
-            }
-            a.recycle();
-
-            if (mBehavior != null) {
-                // If we have a Behavior, dispatch that it has been attached
-                mBehavior.onAttachedToLayoutParams(this);
-            }
-        }
-
-        public LayoutParams(LayoutParams p) {
-            super(p);
-        }
-
-        public LayoutParams(MarginLayoutParams p) {
-            super(p);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams p) {
-            super(p);
-        }
-
-        /**
-         * Get the id of this view's anchor.
-         *
-         * @return A {@link View#getId() view id} or {@link View#NO_ID} if there is no anchor
-         */
-        @IdRes
-        public int getAnchorId() {
-            return mAnchorId;
-        }
-
-        /**
-         * Set the id of this view's anchor.
-         *
-         * <p>The view with this id must be a descendant of the CoordinatorLayout containing
-         * the child view this LayoutParams belongs to. It may not be the child view with
-         * this LayoutParams or a descendant of it.</p>
-         *
-         * @param id The {@link View#getId() view id} of the anchor or
-         *           {@link View#NO_ID} if there is no anchor
-         */
-        public void setAnchorId(@IdRes int id) {
-            invalidateAnchor();
-            mAnchorId = id;
-        }
-
-        /**
-         * Get the behavior governing the layout and interaction of the child view within
-         * a parent CoordinatorLayout.
-         *
-         * @return The current behavior or null if no behavior is specified
-         */
-        @Nullable
-        public Behavior getBehavior() {
-            return mBehavior;
-        }
-
-        /**
-         * Set the behavior governing the layout and interaction of the child view within
-         * a parent CoordinatorLayout.
-         *
-         * <p>Setting a new behavior will remove any currently associated
-         * {@link Behavior#setTag(View, Object) Behavior tag}.</p>
-         *
-         * @param behavior The behavior to set or null for no special behavior
-         */
-        public void setBehavior(@Nullable Behavior behavior) {
-            if (mBehavior != behavior) {
-                if (mBehavior != null) {
-                    // First detach any old behavior
-                    mBehavior.onDetachedFromLayoutParams();
-                }
-
-                mBehavior = behavior;
-                mBehaviorTag = null;
-                mBehaviorResolved = true;
-
-                if (behavior != null) {
-                    // Now dispatch that the Behavior has been attached
-                    behavior.onAttachedToLayoutParams(this);
-                }
-            }
-        }
-
-        /**
-         * Set the last known position rect for this child view
-         * @param r the rect to set
-         */
-        void setLastChildRect(Rect r) {
-            mLastChildRect.set(r);
-        }
-
-        /**
-         * Get the last known position rect for this child view.
-         * Note: do not mutate the result of this call.
-         */
-        Rect getLastChildRect() {
-            return mLastChildRect;
-        }
-
-        /**
-         * Returns true if the anchor id changed to another valid view id since the anchor view
-         * was resolved.
-         */
-        boolean checkAnchorChanged() {
-            return mAnchorView == null && mAnchorId != View.NO_ID;
-        }
-
-        /**
-         * Returns true if the associated Behavior previously blocked interaction with other views
-         * below the associated child since the touch behavior tracking was last
-         * {@link #resetTouchBehaviorTracking() reset}.
-         *
-         * @see #isBlockingInteractionBelow(CoordinatorLayout, View)
-         */
-        boolean didBlockInteraction() {
-            if (mBehavior == null) {
-                mDidBlockInteraction = false;
-            }
-            return mDidBlockInteraction;
-        }
-
-        /**
-         * Check if the associated Behavior wants to block interaction below the given child
-         * view. The given child view should be the child this LayoutParams is associated with.
-         *
-         * <p>Once interaction is blocked, it will remain blocked until touch interaction tracking
-         * is {@link #resetTouchBehaviorTracking() reset}.</p>
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param child the child view this LayoutParams is associated with
-         * @return true to block interaction below the given child
-         */
-        boolean isBlockingInteractionBelow(CoordinatorLayout parent, View child) {
-            if (mDidBlockInteraction) {
-                return true;
-            }
-
-            return mDidBlockInteraction |= mBehavior != null
-                    ? mBehavior.blocksInteractionBelow(parent, child)
-                    : false;
-        }
-
-        /**
-         * Reset tracking of Behavior-specific touch interactions. This includes
-         * interaction blocking.
-         *
-         * @see #isBlockingInteractionBelow(CoordinatorLayout, View)
-         * @see #didBlockInteraction()
-         */
-        void resetTouchBehaviorTracking() {
-            mDidBlockInteraction = false;
-        }
-
-        void resetNestedScroll(int type) {
-            setNestedScrollAccepted(type, false);
-        }
-
-        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(int type) {
-            switch (type) {
-                case ViewCompat.TYPE_TOUCH:
-                    return mDidAcceptNestedScrollTouch;
-                case ViewCompat.TYPE_NON_TOUCH:
-                    return mDidAcceptNestedScrollNonTouch;
-            }
-            return false;
-        }
-
-        boolean getChangedAfterNestedScroll() {
-            return mDidChangeAfterNestedScroll;
-        }
-
-        void setChangedAfterNestedScroll(boolean changed) {
-            mDidChangeAfterNestedScroll = changed;
-        }
-
-        void resetChangedAfterNestedScroll() {
-            mDidChangeAfterNestedScroll = false;
-        }
-
-        /**
-         * Check if an associated child view depends on another child view of the CoordinatorLayout.
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param child the child to check
-         * @param dependency the proposed dependency to check
-         * @return true if child depends on dependency
-         */
-        boolean dependsOn(CoordinatorLayout parent, View child, View dependency) {
-            return dependency == mAnchorDirectChild
-                    || shouldDodge(dependency, ViewCompat.getLayoutDirection(parent))
-                    || (mBehavior != null && mBehavior.layoutDependsOn(parent, child, dependency));
-        }
-
-        /**
-         * Invalidate the cached anchor view and direct child ancestor of that anchor.
-         * The anchor will need to be
-         * {@link #findAnchorView(CoordinatorLayout, View) found} before
-         * being used again.
-         */
-        void invalidateAnchor() {
-            mAnchorView = mAnchorDirectChild = null;
-        }
-
-        /**
-         * Locate the appropriate anchor view by the current {@link #setAnchorId(int) anchor id}
-         * or return the cached anchor view if already known.
-         *
-         * @param parent the parent CoordinatorLayout
-         * @param forChild the child this LayoutParams is associated with
-         * @return the located descendant anchor view, or null if the anchor id is
-         *         {@link View#NO_ID}.
-         */
-        View findAnchorView(CoordinatorLayout parent, View forChild) {
-            if (mAnchorId == View.NO_ID) {
-                mAnchorView = mAnchorDirectChild = null;
-                return null;
-            }
-
-            if (mAnchorView == null || !verifyAnchorView(forChild, parent)) {
-                resolveAnchorView(forChild, parent);
-            }
-            return mAnchorView;
-        }
-
-        /**
-         * Determine the anchor view for the child view this LayoutParams is assigned to.
-         * Assumes mAnchorId is valid.
-         */
-        private void resolveAnchorView(final View forChild, final CoordinatorLayout parent) {
-            mAnchorView = parent.findViewById(mAnchorId);
-            if (mAnchorView != null) {
-                if (mAnchorView == parent) {
-                    if (parent.isInEditMode()) {
-                        mAnchorView = mAnchorDirectChild = null;
-                        return;
-                    }
-                    throw new IllegalStateException(
-                            "View can not be anchored to the the parent CoordinatorLayout");
-                }
-
-                View directChild = mAnchorView;
-                for (ViewParent p = mAnchorView.getParent();
-                        p != parent && p != null;
-                        p = p.getParent()) {
-                    if (p == forChild) {
-                        if (parent.isInEditMode()) {
-                            mAnchorView = mAnchorDirectChild = null;
-                            return;
-                        }
-                        throw new IllegalStateException(
-                                "Anchor must not be a descendant of the anchored view");
-                    }
-                    if (p instanceof View) {
-                        directChild = (View) p;
-                    }
-                }
-                mAnchorDirectChild = directChild;
-            } else {
-                if (parent.isInEditMode()) {
-                    mAnchorView = mAnchorDirectChild = null;
-                    return;
-                }
-                throw new IllegalStateException("Could not find CoordinatorLayout descendant view"
-                        + " with id " + parent.getResources().getResourceName(mAnchorId)
-                        + " to anchor view " + forChild);
-            }
-        }
-
-        /**
-         * Verify that the previously resolved anchor view is still valid - that it is still
-         * a descendant of the expected parent view, it is not the child this LayoutParams
-         * is assigned to or a descendant of it, and it has the expected id.
-         */
-        private boolean verifyAnchorView(View forChild, CoordinatorLayout parent) {
-            if (mAnchorView.getId() != mAnchorId) {
-                return false;
-            }
-
-            View directChild = mAnchorView;
-            for (ViewParent p = mAnchorView.getParent();
-                    p != parent;
-                    p = p.getParent()) {
-                if (p == null || p == forChild) {
-                    mAnchorView = mAnchorDirectChild = null;
-                    return false;
-                }
-                if (p instanceof View) {
-                    directChild = (View) p;
-                }
-            }
-            mAnchorDirectChild = directChild;
-            return true;
-        }
-
-        /**
-         * Checks whether the view with this LayoutParams should dodge the specified view.
-         */
-        private boolean shouldDodge(View other, int layoutDirection) {
-            LayoutParams lp = (LayoutParams) other.getLayoutParams();
-            final int absInset = GravityCompat.getAbsoluteGravity(lp.insetEdge, layoutDirection);
-            return absInset != Gravity.NO_GRAVITY && (absInset &
-                    GravityCompat.getAbsoluteGravity(dodgeInsetEdges, layoutDirection)) == absInset;
-        }
-    }
-
-    private class HierarchyChangeListener implements OnHierarchyChangeListener {
-        HierarchyChangeListener() {
-        }
-
-        @Override
-        public void onChildViewAdded(View parent, View child) {
-            if (mOnHierarchyChangeListener != null) {
-                mOnHierarchyChangeListener.onChildViewAdded(parent, child);
-            }
-        }
-
-        @Override
-        public void onChildViewRemoved(View parent, View child) {
-            onChildViewsChanged(EVENT_VIEW_REMOVED);
-
-            if (mOnHierarchyChangeListener != null) {
-                mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
-            }
-        }
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        final SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-
-        final SparseArray<Parcelable> behaviorStates = ss.behaviorStates;
-
-        for (int i = 0, count = getChildCount(); i < count; i++) {
-            final View child = getChildAt(i);
-            final int childId = child.getId();
-            final LayoutParams lp = getResolvedLayoutParams(child);
-            final Behavior b = lp.getBehavior();
-
-            if (childId != NO_ID && b != null) {
-                Parcelable savedState = behaviorStates.get(childId);
-                if (savedState != null) {
-                    b.onRestoreInstanceState(this, child, savedState);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final SavedState ss = new SavedState(super.onSaveInstanceState());
-
-        final SparseArray<Parcelable> behaviorStates = new SparseArray<>();
-        for (int i = 0, count = getChildCount(); i < count; i++) {
-            final View child = getChildAt(i);
-            final int childId = child.getId();
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final Behavior b = lp.getBehavior();
-
-            if (childId != NO_ID && b != null) {
-                // If the child has an ID and a Behavior, let it save some state...
-                Parcelable state = b.onSaveInstanceState(this, child);
-                if (state != null) {
-                    behaviorStates.append(childId, state);
-                }
-            }
-        }
-        ss.behaviorStates = behaviorStates;
-        return ss;
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final Behavior behavior = lp.getBehavior();
-
-        if (behavior != null
-                && behavior.onRequestChildRectangleOnScreen(this, child, rectangle, immediate)) {
-            return true;
-        }
-
-        return super.requestChildRectangleOnScreen(child, rectangle, immediate);
-    }
-
-    private void setupForInsets() {
-        if (Build.VERSION.SDK_INT < 21) {
-            return;
-        }
-
-        if (ViewCompat.getFitsSystemWindows(this)) {
-            if (mApplyWindowInsetsListener == null) {
-                mApplyWindowInsetsListener =
-                        new android.support.v4.view.OnApplyWindowInsetsListener() {
-                            @Override
-                            public WindowInsetsCompat onApplyWindowInsets(View v,
-                                    WindowInsetsCompat insets) {
-                                return setWindowInsets(insets);
-                            }
-                        };
-            }
-            // First apply the insets listener
-            ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
-
-            // Now set the sys ui flags to enable us to lay out in the window insets
-            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
-        } else {
-            ViewCompat.setOnApplyWindowInsetsListener(this, null);
-        }
-    }
-
-    protected static class SavedState extends AbsSavedState {
-        SparseArray<Parcelable> behaviorStates;
-
-        public SavedState(Parcel source, ClassLoader loader) {
-            super(source, loader);
-
-            final int size = source.readInt();
-
-            final int[] ids = new int[size];
-            source.readIntArray(ids);
-
-            final Parcelable[] states = source.readParcelableArray(loader);
-
-            behaviorStates = new SparseArray<>(size);
-            for (int i = 0; i < size; i++) {
-                behaviorStates.append(ids[i], states[i]);
-            }
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-
-            final int size = behaviorStates != null ? behaviorStates.size() : 0;
-            dest.writeInt(size);
-
-            final int[] ids = new int[size];
-            final Parcelable[] states = new Parcelable[size];
-
-            for (int i = 0; i < size; i++) {
-                ids[i] = behaviorStates.keyAt(i);
-                states[i] = behaviorStates.valueAt(i);
-            }
-            dest.writeIntArray(ids);
-            dest.writeParcelableArray(states, flags);
-
-        }
-
-        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];
-                    }
-                };
-    }
-}
diff --git a/android/support/graphics/drawable/AndroidResources.java b/android/support/graphics/drawable/AndroidResources.java
deleted file mode 100644
index 804c623..0000000
--- a/android/support/graphics/drawable/AndroidResources.java
+++ /dev/null
@@ -1,149 +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.support.annotation.StyleableRes;
-
-class AndroidResources {
-
-    // Resources ID generated in the latest R.java for framework.
-    static final int[] STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY = {
-            android.R.attr.name, android.R.attr.tint, android.R.attr.height,
-            android.R.attr.width, android.R.attr.alpha, android.R.attr.autoMirrored,
-            android.R.attr.tintMode, android.R.attr.viewportWidth, android.R.attr.viewportHeight
-    };
-    static final int STYLEABLE_VECTOR_DRAWABLE_ALPHA = 4;
-    static final int STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED = 5;
-    static final int STYLEABLE_VECTOR_DRAWABLE_HEIGHT = 2;
-    static final int STYLEABLE_VECTOR_DRAWABLE_NAME = 0;
-    static final int STYLEABLE_VECTOR_DRAWABLE_TINT = 1;
-    static final int STYLEABLE_VECTOR_DRAWABLE_TINT_MODE = 6;
-    static final int STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT = 8;
-    static final int STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH = 7;
-    static final int STYLEABLE_VECTOR_DRAWABLE_WIDTH = 3;
-    static final int[] STYLEABLE_VECTOR_DRAWABLE_GROUP = {
-            android.R.attr.name, android.R.attr.pivotX, android.R.attr.pivotY,
-            android.R.attr.scaleX, android.R.attr.scaleY, android.R.attr.rotation,
-            android.R.attr.translateX, android.R.attr.translateY
-    };
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME = 0;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X = 1;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y = 2;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION = 5;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X = 3;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y = 4;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X = 6;
-    static final int STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y = 7;
-    static final int[] STYLEABLE_VECTOR_DRAWABLE_PATH = {
-            android.R.attr.name, android.R.attr.fillColor, android.R.attr.pathData,
-            android.R.attr.strokeColor, android.R.attr.strokeWidth, android.R.attr.trimPathStart,
-            android.R.attr.trimPathEnd, android.R.attr.trimPathOffset, android.R.attr.strokeLineCap,
-            android.R.attr.strokeLineJoin, android.R.attr.strokeMiterLimit,
-            android.R.attr.strokeAlpha, android.R.attr.fillAlpha, android.R.attr.fillType
-    };
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA = 12;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR = 1;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_NAME = 0;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA = 2;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA = 11;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR = 3;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP = 8;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN = 9;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT = 10;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH = 4;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END = 6;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET = 7;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START = 5;
-    static final int STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE = 13;
-    static final int[] STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH = {
-            android.R.attr.name, android.R.attr.pathData
-    };
-    static final int STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_NAME = 0;
-    static final int STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_PATH_DATA = 1;
-
-    static final int[] STYLEABLE_ANIMATED_VECTOR_DRAWABLE = {
-            android.R.attr.drawable
-    };
-    static final int STYLEABLE_ANIMATED_VECTOR_DRAWABLE_DRAWABLE = 0;
-    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;
-
-    /////////////////////////////////////////////////////////////////////
-
-    public static final int[] STYLEABLE_ANIMATOR = {
-            0x01010141, 0x01010198, 0x010101be, 0x010101bf,
-            0x010101c0, 0x010102de, 0x010102df, 0x010102e0
-    };
-
-    public static final int STYLEABLE_ANIMATOR_INTERPOLATOR = 0;
-    public static final int STYLEABLE_ANIMATOR_DURATION = 1;
-    public static final int STYLEABLE_ANIMATOR_START_OFFSET = 2;
-    public static final int STYLEABLE_ANIMATOR_REPEAT_COUNT = 3;
-    public static final int STYLEABLE_ANIMATOR_REPEAT_MODE = 4;
-    public static final int STYLEABLE_ANIMATOR_VALUE_FROM = 5;
-    public static final int STYLEABLE_ANIMATOR_VALUE_TO = 6;
-    public static final int STYLEABLE_ANIMATOR_VALUE_TYPE = 7;
-    public static final int[] STYLEABLE_ANIMATOR_SET = {
-            0x010102e2
-    };
-    public static final int STYLEABLE_ANIMATOR_SET_ORDERING = 0;
-
-    public static final int[] STYLEABLE_PROPERTY_VALUES_HOLDER = {
-            0x010102de, 0x010102df, 0x010102e0, 0x010102e1
-    };
-    public static final int STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_FROM = 0;
-    public static final int STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_TO = 1;
-    public static final int STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_TYPE = 2;
-    public static final int STYLEABLE_PROPERTY_VALUES_HOLDER_PROPERTY_NAME = 3;
-
-    public static final int[] STYLEABLE_KEYFRAME = {
-            0x01010024, 0x01010141, 0x010102e0, 0x010104d8
-    };
-    public static final int STYLEABLE_KEYFRAME_VALUE = 0;
-    public static final int STYLEABLE_KEYFRAME_INTERPOLATOR = 1;
-    public static final int STYLEABLE_KEYFRAME_VALUE_TYPE = 2;
-    public static final int STYLEABLE_KEYFRAME_FRACTION = 3;
-
-    public static final int[] STYLEABLE_PROPERTY_ANIMATOR = {
-            0x010102e1, 0x01010405, 0x01010474, 0x01010475
-    };
-    public static final int STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_NAME = 0;
-    public static final int STYLEABLE_PROPERTY_ANIMATOR_PATH_DATA = 1;
-    public static final int STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_X_NAME = 2;
-    public static final int STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_Y_NAME = 3;
-
-
-    public static final int[] STYLEABLE_PATH_INTERPOLATOR = {
-            0x010103fc, 0x010103fd, 0x010103fe, 0x010103ff,
-            0x01010405
-    };
-
-    public static final int STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_1 = 0;
-    public static final int STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_1 = 1;
-
-    public static final int STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_2 = 2;
-    public static final int STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_2 = 3;
-
-    public static final int STYLEABLE_PATH_INTERPOLATOR_PATH_DATA = 4;
-
-    public static final int FAST_OUT_LINEAR_IN = 0x010c000f;
-    public static final int FAST_OUT_SLOW_IN = 0x010c000d;
-    public static final int LINEAR_OUT_SLOW_IN = 0x010c000e;
-}
diff --git a/android/support/graphics/drawable/Animatable2Compat.java b/android/support/graphics/drawable/Animatable2Compat.java
deleted file mode 100644
index 232dc81..0000000
--- a/android/support/graphics/drawable/Animatable2Compat.java
+++ /dev/null
@@ -1,92 +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.graphics.drawable;
-
-import static android.os.Build.VERSION_CODES.M;
-
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Animatable2;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-
-/**
- * Interface that drawables supporting animations and callbacks should extend in support lib.
- */
-public interface Animatable2Compat extends Animatable {
-
-    /**
-     * Adds a callback to listen to the animation events.
-     *
-     * @param callback Callback to add.
-     */
-    void registerAnimationCallback(@NonNull AnimationCallback callback);
-
-    /**
-     * Removes the specified animation callback.
-     *
-     * @param callback Callback to remove.
-     * @return {@code false} if callback didn't exist in the call back list, or {@code true} if
-     *         callback has been removed successfully.
-     */
-    boolean unregisterAnimationCallback(@NonNull AnimationCallback callback);
-
-    /**
-     * Removes all existing animation callbacks.
-     */
-    void clearAnimationCallbacks();
-
-    /**
-     * Abstract class for animation callback. Used to notify animation events.
-     */
-    abstract class AnimationCallback {
-        /**
-         * Called when the animation starts.
-         *
-         * @param drawable The drawable started the animation.
-         */
-        public void onAnimationStart(Drawable drawable) {};
-        /**
-         * Called when the animation ends.
-         *
-         * @param drawable The drawable finished the animation.
-         */
-        public void onAnimationEnd(Drawable drawable) {};
-
-        // Only when passing this Animatable2Compat.AnimationCallback to a frameworks' AVD, we need
-        // to bridge this compat version callback with the frameworks' callback.
-        Animatable2.AnimationCallback mPlatformCallback;
-
-        @RequiresApi(M)
-        Animatable2.AnimationCallback getPlatformCallback() {
-            if (mPlatformCallback == null) {
-                mPlatformCallback = new Animatable2.AnimationCallback() {
-                    @Override
-                    public void onAnimationStart(Drawable drawable) {
-                        AnimationCallback.this.onAnimationStart(drawable);
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Drawable drawable) {
-                        AnimationCallback.this.onAnimationEnd(drawable);
-                    }
-                };
-            }
-            return mPlatformCallback;
-        }
-    }
-}
diff --git a/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
deleted file mode 100644
index bc521cc..0000000
--- a/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ /dev/null
@@ -1,919 +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.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
-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.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;
-import android.util.Log;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * For API 24 and above, this class is delegating to the framework's {@link
- * AnimatedVectorDrawable}.
- * For older API version, this class uses {@link android.animation.ObjectAnimator} and
- * {@link android.animation.AnimatorSet} to animate the properties of a
- * {@link VectorDrawableCompat} to create an animated drawable.
- * <p/>
- * AnimatedVectorDrawableCompat are defined in the same XML format as
- * {@link AnimatedVectorDrawable}.
- * <p/>
- * Here are all the animatable attributes in {@link VectorDrawableCompat}:
- * <table border="2" align="center" cellpadding="5">
- *     <thead>
- *         <tr>
- *             <th>Element Name</th>
- *             <th>Animatable attribute name</th>
- *         </tr>
- *     </thead>
- *     <tr>
- *         <td>&lt;vector&gt;</td>
- *         <td>alpha</td>
- *     </tr>
- *     <tr>
- *         <td rowspan="7">&lt;group&gt;</td>
- *         <td>rotation</td>
- *     </tr>
- *     <tr>
- *         <td>pivotX</td>
- *     </tr>
- *     <tr>
- *         <td>pivotY</td>
- *     </tr>
- *     <tr>
- *         <td>scaleX</td>
- *     </tr>
- *     <tr>
- *         <td>scaleY</td>
- *     </tr>
- *     <tr>
- *         <td>translateX</td>
- *     </tr>
- *     <tr>
- *         <td>translateY</td>
- *     </tr>
- *     <tr>
- *         <td rowspan="8">&lt;path&gt;</td>
- *         <td>fillColor</td>
- *     </tr>
- *     <tr>
- *         <td>pathData</td>
- *     </tr>
- *     <tr>
- *         <td>strokeColor</td>
- *     </tr>
- *     <tr>
- *         <td>strokeWidth</td>
- *     </tr>
- *     <tr>
- *         <td>strokeAlpha</td>
- *     </tr>
- *     <tr>
- *         <td>fillAlpha</td>
- *     </tr>
- *     <tr>
- *         <td>trimPathStart</td>
- *     </tr>
- *     <tr>
- *         <td>trimPathEnd</td>
- *     </tr>
- *     <tr>
- *         <td>trimPathOffset</td>
- *     </tr>
- * </table>
- * <p/>
- * You can always create a AnimatedVectorDrawableCompat object and use it as a Drawable by the Java
- * API. In order to refer to AnimatedVectorDrawableCompat inside a XML file, you can use
- * app:srcCompat attribute in AppCompat library's ImageButton or ImageView.
- * <p/>
- * Note that the animation in AnimatedVectorDrawableCompat now can support the following features:
- * <ul>
- * <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>
- * <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>
- * </ul>
- */
-
-public class AnimatedVectorDrawableCompat extends VectorDrawableCommon
-        implements Animatable2Compat {
-    private static final String LOGTAG = "AnimatedVDCompat";
-
-    private static final String ANIMATED_VECTOR = "animated-vector";
-    private static final String TARGET = "target";
-
-    private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
-
-    private AnimatedVectorDrawableCompatState mAnimatedVectorState;
-
-    private Context mContext;
-
-    private ArgbEvaluator mArgbEvaluator = null;
-
-    AnimatedVectorDrawableDelegateState mCachedConstantStateDelegate;
-
-    // Use internal listener to support AVDC's callback.
-    private Animator.AnimatorListener mAnimatorListener = null;
-
-    // Use an array to keep track of multiple call back associated with one drawable.
-    private ArrayList<Animatable2Compat.AnimationCallback> mAnimationCallbacks = null;
-
-
-    AnimatedVectorDrawableCompat() {
-        this(null, null, null);
-    }
-
-    private AnimatedVectorDrawableCompat(@Nullable Context context) {
-        this(context, null, null);
-    }
-
-    private AnimatedVectorDrawableCompat(@Nullable Context context,
-            @Nullable AnimatedVectorDrawableCompatState state,
-            @Nullable Resources res) {
-        mContext = context;
-        if (state != null) {
-            mAnimatedVectorState = state;
-        } else {
-            mAnimatedVectorState = new AnimatedVectorDrawableCompatState(context, state, mCallback,
-                    res);
-        }
-    }
-
-    /**
-     * mutate() will be effective only if the getConstantState() is returning non-null.
-     * Otherwise, it just return the current object without modification.
-     */
-    @Override
-    public Drawable mutate() {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.mutate();
-        }
-        // For older platforms that there is no delegated drawable, we just return this without
-        // any modification here, and the getConstantState() will return null in this case.
-        return this;
-    }
-
-
-    /**
-     * Create a AnimatedVectorDrawableCompat object.
-     *
-     * @param context the context for creating the animators.
-     * @param resId   the resource ID for AnimatedVectorDrawableCompat object.
-     * @return a new AnimatedVectorDrawableCompat or null if parsing error is found.
-     */
-    @Nullable
-    public static AnimatedVectorDrawableCompat create(@NonNull Context context,
-            @DrawableRes int resId) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context);
-            drawable.mDelegateDrawable = ResourcesCompat.getDrawable(context.getResources(), resId,
-                    context.getTheme());
-            drawable.mDelegateDrawable.setCallback(drawable.mCallback);
-            drawable.mCachedConstantStateDelegate = new AnimatedVectorDrawableDelegateState(
-                    drawable.mDelegateDrawable.getConstantState());
-            return drawable;
-        }
-        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;
-            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 createFromXmlInner(context, context.getResources(), parser, attrs,
-                    context.getTheme());
-        } catch (XmlPullParserException e) {
-            Log.e(LOGTAG, "parser error", e);
-        } catch (IOException e) {
-            Log.e(LOGTAG, "parser error", e);
-        }
-        return null;
-    }
-
-    /**
-     * Create a AnimatedVectorDrawableCompat from inside an XML document using an optional
-     * {@link Theme}. Called on a parser positioned at a tag in an XML
-     * document, tries to create a Drawable from that tag. Returns {@code null}
-     * if the tag is not a valid drawable.
-     */
-    public static AnimatedVectorDrawableCompat createFromXmlInner(Context context, Resources r,
-            XmlPullParser parser, AttributeSet attrs, Theme theme)
-            throws XmlPullParserException, IOException {
-        final AnimatedVectorDrawableCompat drawable = new AnimatedVectorDrawableCompat(context);
-        drawable.inflate(r, parser, attrs, theme);
-        return drawable;
-    }
-
-    /**
-     * {@inheritDoc}
-     * <strong>Note</strong> that we don't support constant state when SDK < 24.
-     * Make sure you check the return value before using it.
-     */
-    @Override
-    public ConstantState getConstantState() {
-        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
-            return new AnimatedVectorDrawableDelegateState(mDelegateDrawable.getConstantState());
-        }
-        // We can't support constant state in older platform.
-        // We need Context to create the animator, and we can't save the context in the constant
-        // state.
-        return null;
-    }
-
-    @Override
-    public int getChangingConfigurations() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getChangingConfigurations();
-        }
-        return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.draw(canvas);
-            return;
-        }
-        mAnimatedVectorState.mVectorDrawable.draw(canvas);
-        if (mAnimatedVectorState.mAnimatorSet.isStarted()) {
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setBounds(bounds);
-            return;
-        }
-        mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
-    }
-
-    @Override
-    protected boolean onStateChange(int[] state) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setState(state);
-        }
-        return mAnimatedVectorState.mVectorDrawable.setState(state);
-    }
-
-    @Override
-    protected boolean onLevelChange(int level) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setLevel(level);
-        }
-        return mAnimatedVectorState.mVectorDrawable.setLevel(level);
-    }
-
-    @Override
-    public int getAlpha() {
-        if (mDelegateDrawable != null) {
-            return DrawableCompat.getAlpha(mDelegateDrawable);
-        }
-        return mAnimatedVectorState.mVectorDrawable.getAlpha();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setAlpha(alpha);
-            return;
-        }
-        mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setColorFilter(colorFilter);
-            return;
-        }
-        mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
-    }
-
-    @Override
-    public void setTint(int tint) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setTint(mDelegateDrawable, tint);
-            return;
-        }
-
-        mAnimatedVectorState.mVectorDrawable.setTint(tint);
-    }
-
-    @Override
-    public void setTintList(ColorStateList tint) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setTintList(mDelegateDrawable, tint);
-            return;
-        }
-
-        mAnimatedVectorState.mVectorDrawable.setTintList(tint);
-    }
-
-    @Override
-    public void setTintMode(PorterDuff.Mode tintMode) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setTintMode(mDelegateDrawable, tintMode);
-            return;
-        }
-
-        mAnimatedVectorState.mVectorDrawable.setTintMode(tintMode);
-    }
-
-    @Override
-    public boolean setVisible(boolean visible, boolean restart) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setVisible(visible, restart);
-        }
-        mAnimatedVectorState.mVectorDrawable.setVisible(visible, restart);
-        return super.setVisible(visible, restart);
-    }
-
-    @Override
-    public boolean isStateful() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.isStateful();
-        }
-        return mAnimatedVectorState.mVectorDrawable.isStateful();
-    }
-
-    @Override
-    public int getOpacity() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getOpacity();
-        }
-        return mAnimatedVectorState.mVectorDrawable.getOpacity();
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getIntrinsicWidth();
-        }
-        return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getIntrinsicHeight();
-        }
-        return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
-    }
-
-    @Override
-    public boolean isAutoMirrored() {
-        if (mDelegateDrawable != null) {
-            return DrawableCompat.isAutoMirrored(mDelegateDrawable);
-        }
-        return mAnimatedVectorState.mVectorDrawable.isAutoMirrored();
-    }
-
-    @Override
-    public void setAutoMirrored(boolean mirrored) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setAutoMirrored(mDelegateDrawable, mirrored);
-            return;
-        }
-        mAnimatedVectorState.mVectorDrawable.setAutoMirrored(mirrored);
-    }
-
-    @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
-            throws XmlPullParserException, IOException {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.inflate(mDelegateDrawable, res, parser, attrs, theme);
-            return;
-        }
-        int eventType = parser.getEventType();
-        final int innerDepth = parser.getDepth() + 1;
-
-        // Parse everything until the end of the animated-vector element.
-        while (eventType != XmlPullParser.END_DOCUMENT
-                && (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)) {
-            if (eventType == XmlPullParser.START_TAG) {
-                final String tagName = parser.getName();
-                if (DBG_ANIMATION_VECTOR_DRAWABLE) {
-                    Log.v(LOGTAG, "tagName is " + tagName);
-                }
-                if (ANIMATED_VECTOR.equals(tagName)) {
-                    final TypedArray a =
-                            TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                                    AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE);
-
-                    int drawableRes = a.getResourceId(
-                            AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_DRAWABLE, 0);
-                    if (DBG_ANIMATION_VECTOR_DRAWABLE) {
-                        Log.v(LOGTAG, "drawableRes is " + drawableRes);
-                    }
-                    if (drawableRes != 0) {
-                        VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(res,
-                                drawableRes, theme);
-                        vectorDrawable.setAllowCaching(false);
-                        vectorDrawable.setCallback(mCallback);
-                        if (mAnimatedVectorState.mVectorDrawable != null) {
-                            mAnimatedVectorState.mVectorDrawable.setCallback(null);
-                        }
-                        mAnimatedVectorState.mVectorDrawable = vectorDrawable;
-                    }
-                    a.recycle();
-                } else if (TARGET.equals(tagName)) {
-                    final TypedArray a =
-                            res.obtainAttributes(attrs,
-                                    AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET);
-                    final String target = a.getString(
-                            AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_NAME);
-
-                    int id = a.getResourceId(
-                            AndroidResources.STYLEABLE_ANIMATED_VECTOR_DRAWABLE_TARGET_ANIMATION,
-                            0);
-                    if (id != 0) {
-                        if (mContext != null) {
-                            // There are some important features (like path morphing), added into
-                            // Animator code to support AVD at API 21.
-                            Animator objectAnimator = AnimatorInflaterCompat.loadAnimator(
-                                    mContext, id);
-                            setupAnimatorsForTarget(target, objectAnimator);
-                        } else {
-                            a.recycle();
-                            throw new IllegalStateException("Context can't be null when inflating" +
-                                    " animators");
-                        }
-                    }
-                    a.recycle();
-                }
-            }
-            eventType = parser.next();
-        }
-
-        mAnimatedVectorState.setupAnimatorSet();
-    }
-
-    @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
-            throws XmlPullParserException, IOException {
-        inflate(res, parser, attrs, null);
-    }
-
-    @Override
-    public void applyTheme(Theme t) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.applyTheme(mDelegateDrawable, t);
-            return;
-        }
-        // TODO: support theming in older platform.
-        return;
-    }
-
-    @Override
-    public boolean canApplyTheme() {
-        if (mDelegateDrawable != null) {
-            return DrawableCompat.canApplyTheme(mDelegateDrawable);
-        }
-        // TODO: support theming in older platform.
-        return false;
-    }
-
-    /**
-     * Constant state for delegating the creating drawable job.
-     * 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;
-
-        public AnimatedVectorDrawableDelegateState(ConstantState state) {
-            mDelegateState = state;
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            AnimatedVectorDrawableCompat drawableCompat =
-                    new AnimatedVectorDrawableCompat();
-            drawableCompat.mDelegateDrawable = mDelegateState.newDrawable();
-            drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback);
-            return drawableCompat;
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res) {
-            AnimatedVectorDrawableCompat drawableCompat =
-                    new AnimatedVectorDrawableCompat();
-            drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(res);
-            drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback);
-            return drawableCompat;
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res, Theme theme) {
-            AnimatedVectorDrawableCompat drawableCompat =
-                    new AnimatedVectorDrawableCompat();
-            drawableCompat.mDelegateDrawable = mDelegateState.newDrawable(res, theme);
-            drawableCompat.mDelegateDrawable.setCallback(drawableCompat.mCallback);
-            return drawableCompat;
-        }
-
-        @Override
-        public boolean canApplyTheme() {
-            return mDelegateState.canApplyTheme();
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return mDelegateState.getChangingConfigurations();
-        }
-    }
-
-    private static class AnimatedVectorDrawableCompatState extends ConstantState {
-        int mChangingConfigurations;
-        VectorDrawableCompat mVectorDrawable;
-        // Combining the array of Animators into a single AnimatorSet to hook up listener easier.
-        AnimatorSet mAnimatorSet;
-        private ArrayList<Animator> mAnimators;
-        ArrayMap<Animator, String> mTargetNameMap;
-
-        public AnimatedVectorDrawableCompatState(Context context,
-                AnimatedVectorDrawableCompatState copy, Callback owner, Resources res) {
-            if (copy != null) {
-                mChangingConfigurations = copy.mChangingConfigurations;
-                if (copy.mVectorDrawable != null) {
-                    final ConstantState cs = copy.mVectorDrawable.getConstantState();
-                    if (res != null) {
-                        mVectorDrawable = (VectorDrawableCompat) cs.newDrawable(res);
-                    } else {
-                        mVectorDrawable = (VectorDrawableCompat) cs.newDrawable();
-                    }
-                    mVectorDrawable = (VectorDrawableCompat) mVectorDrawable.mutate();
-                    mVectorDrawable.setCallback(owner);
-                    mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
-                    mVectorDrawable.setAllowCaching(false);
-                }
-                if (copy.mAnimators != null) {
-                    final int numAnimators = copy.mAnimators.size();
-                    mAnimators = new ArrayList<Animator>(numAnimators);
-                    mTargetNameMap = new ArrayMap<Animator, String>(numAnimators);
-                    for (int i = 0; i < numAnimators; ++i) {
-                        Animator anim = copy.mAnimators.get(i);
-                        Animator animClone = anim.clone();
-                        String targetName = copy.mTargetNameMap.get(anim);
-                        Object targetObject = mVectorDrawable.getTargetByName(targetName);
-                        animClone.setTarget(targetObject);
-                        mAnimators.add(animClone);
-                        mTargetNameMap.put(animClone, targetName);
-                    }
-                    setupAnimatorSet();
-                }
-            }
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            throw new IllegalStateException("No constant state support for SDK < 24.");
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res) {
-            throw new IllegalStateException("No constant state support for SDK < 24.");
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return mChangingConfigurations;
-        }
-
-        public void setupAnimatorSet() {
-            if (mAnimatorSet == null) {
-                mAnimatorSet = new AnimatorSet();
-            }
-            mAnimatorSet.playTogether(mAnimators);
-        }
-    }
-
-    /**
-     * Utility function to fix color interpolation prior to Lollipop. Without this fix, colors
-     * are evaluated as raw integers instead of as colors, which leads to artifacts during
-     * fillColor animations.
-     */
-    private void setupColorAnimator(Animator animator) {
-        if (animator instanceof AnimatorSet) {
-            List<Animator> childAnimators = ((AnimatorSet) animator).getChildAnimations();
-            if (childAnimators != null) {
-                for (int i = 0; i < childAnimators.size(); ++i) {
-                    setupColorAnimator(childAnimators.get(i));
-                }
-            }
-        }
-        if (animator instanceof ObjectAnimator) {
-            ObjectAnimator objectAnim = (ObjectAnimator) animator;
-            final String propertyName = objectAnim.getPropertyName();
-            if ("fillColor".equals(propertyName) || "strokeColor".equals(propertyName)) {
-                if (mArgbEvaluator == null) {
-                    mArgbEvaluator = new ArgbEvaluator();
-                }
-                objectAnim.setEvaluator(mArgbEvaluator);
-            }
-        }
-    }
-
-    private void setupAnimatorsForTarget(String name, Animator animator) {
-        Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
-        animator.setTarget(target);
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
-            setupColorAnimator(animator);
-        }
-        if (mAnimatedVectorState.mAnimators == null) {
-            mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
-            mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
-        }
-        mAnimatedVectorState.mAnimators.add(animator);
-        mAnimatedVectorState.mTargetNameMap.put(animator, name);
-        if (DBG_ANIMATION_VECTOR_DRAWABLE) {
-            Log.v(LOGTAG, "add animator  for target " + name + " " + animator);
-        }
-    }
-
-    @Override
-    public boolean isRunning() {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning();
-        }
-        return mAnimatedVectorState.mAnimatorSet.isRunning();
-    }
-
-    @Override
-    public void start() {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            ((AnimatedVectorDrawable) mDelegateDrawable).start();
-            return;
-        }
-        // If any one of the animator has not ended, do nothing.
-        if (mAnimatedVectorState.mAnimatorSet.isStarted()) {
-            return;
-        }
-        // Otherwise, kick off animatorSet.
-        mAnimatedVectorState.mAnimatorSet.start();
-        invalidateSelf();
-    }
-
-    @Override
-    public void stop() {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            ((AnimatedVectorDrawable) mDelegateDrawable).stop();
-            return;
-        }
-        mAnimatedVectorState.mAnimatorSet.end();
-    }
-
-    final Callback mCallback = new Callback() {
-        @Override
-        public void invalidateDrawable(Drawable who) {
-            invalidateSelf();
-        }
-
-        @Override
-        public void scheduleDrawable(Drawable who, Runnable what, long when) {
-            scheduleSelf(what, when);
-        }
-
-        @Override
-        public void unscheduleDrawable(Drawable who, Runnable what) {
-            unscheduleSelf(what);
-        }
-    };
-
-    /**
-     * 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());
-    }
-
-    @Override
-    public void registerAnimationCallback(@NonNull Animatable2Compat.AnimationCallback
-            callback) {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            registerPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
-            return;
-        }
-
-        if (callback == null) {
-            return;
-        }
-
-        // Add listener accordingly.
-        if (mAnimationCallbacks == null) {
-            mAnimationCallbacks = new ArrayList<>();
-        }
-
-        if (mAnimationCallbacks.contains(callback)) {
-            // If this call back is already in, then don't need to append another copy.
-            return;
-        }
-
-        mAnimationCallbacks.add(callback);
-
-        if (mAnimatorListener == null) {
-            // Create a animator listener and trigger the callback events when listener is
-            // triggered.
-            mAnimatorListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    ArrayList<Animatable2Compat.AnimationCallback> tmpCallbacks =
-                            new ArrayList<>(mAnimationCallbacks);
-                    int size = tmpCallbacks.size();
-                    for (int i = 0; i < size; i++) {
-                        tmpCallbacks.get(i).onAnimationStart(AnimatedVectorDrawableCompat.this);
-                    }
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    ArrayList<Animatable2Compat.AnimationCallback> tmpCallbacks =
-                            new ArrayList<>(mAnimationCallbacks);
-                    int size = tmpCallbacks.size();
-                    for (int i = 0; i < size; i++) {
-                        tmpCallbacks.get(i).onAnimationEnd(AnimatedVectorDrawableCompat.this);
-                    }
-                }
-            };
-        }
-        mAnimatedVectorState.mAnimatorSet.addListener(mAnimatorListener);
-    }
-
-    /**
-     * 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());
-    }
-
-    /**
-     * A helper function to clean up the animator listener in the mAnimatorSet.
-     */
-    private void removeAnimatorSetListener() {
-        if (mAnimatorListener != null) {
-            mAnimatedVectorState.mAnimatorSet.removeListener(mAnimatorListener);
-            mAnimatorListener = null;
-        }
-    }
-
-    @Override
-    public boolean unregisterAnimationCallback(
-            @NonNull Animatable2Compat.AnimationCallback callback) {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            unregisterPlatformCallback((AnimatedVectorDrawable) mDelegateDrawable, callback);
-        }
-
-        if (mAnimationCallbacks == null || callback == null) {
-            // Nothing to be removed.
-            return false;
-        }
-        boolean removed = mAnimationCallbacks.remove(callback);
-
-        //  When the last call back unregistered, remove the listener accordingly.
-        if (mAnimationCallbacks.size() == 0) {
-            removeAnimatorSetListener();
-        }
-        return removed;
-    }
-
-    @Override
-    public void clearAnimationCallbacks() {
-        if (mDelegateDrawable != null) {
-            //noinspection AndroidLintNewApi - Implicit when delegate is non-null.
-            ((AnimatedVectorDrawable) mDelegateDrawable).clearAnimationCallbacks();
-            return;
-        }
-        removeAnimatorSetListener();
-        if (mAnimationCallbacks == null) {
-            return;
-        }
-
-        mAnimationCallbacks.clear();
-    }
-
-    /**
-     * Utility function to register callback to Drawable, when the drawable is created from XML and
-     * referred in Java code, e.g: ImageView.getDrawable().
-     * From API 24 on, the drawable is treated as an AnimatedVectorDrawable.
-     * Otherwise, it is treated as AnimatedVectorDrawableCompat.
-     */
-    public static void registerAnimationCallback(Drawable dr,
-            Animatable2Compat.AnimationCallback callback) {
-        if (dr == null || callback == null) {
-            return;
-        }
-        if (!(dr instanceof Animatable)) {
-            return;
-        }
-
-        if (Build.VERSION.SDK_INT >= 24) {
-            registerPlatformCallback((AnimatedVectorDrawable) dr, callback);
-        } else {
-            ((AnimatedVectorDrawableCompat) dr).registerAnimationCallback(callback);
-        }
-    }
-
-    /**
-     * Utility function to unregister animation callback from Drawable, when the drawable is
-     * created from XML and referred in Java code, e.g: ImageView.getDrawable().
-     * From API 24 on, the drawable is treated as an AnimatedVectorDrawable.
-     * Otherwise, it is treated as AnimatedVectorDrawableCompat.
-     */
-    public static boolean unregisterAnimationCallback(Drawable dr,
-            Animatable2Compat.AnimationCallback callback) {
-        if (dr == null || callback == null) {
-            return false;
-        }
-        if (!(dr instanceof Animatable)) {
-            return false;
-        }
-
-        if (Build.VERSION.SDK_INT >= 24) {
-            return unregisterPlatformCallback((AnimatedVectorDrawable) dr, callback);
-        } else {
-            return ((AnimatedVectorDrawableCompat) dr).unregisterAnimationCallback(callback);
-        }
-    }
-
-    /**
-     * Utility function to clear animation callbacks from Drawable, when the drawable is
-     * created from XML and referred in Java code, e.g: ImageView.getDrawable().
-     * From API 24 on, the drawable is treated as an AnimatedVectorDrawable.
-     * Otherwise, it is treated as AnimatedVectorDrawableCompat.
-     */
-    public static void clearAnimationCallbacks(Drawable dr) {
-        if (dr == null || !(dr instanceof Animatable)) {
-            return;
-        }
-        if (Build.VERSION.SDK_INT >= 24) {
-            ((AnimatedVectorDrawable) dr).clearAnimationCallbacks();
-        } else {
-            ((AnimatedVectorDrawableCompat) dr).clearAnimationCallbacks();
-        }
-
-    }
-}
diff --git a/android/support/graphics/drawable/AnimationUtilsCompat.java b/android/support/graphics/drawable/AnimationUtilsCompat.java
deleted file mode 100644
index 695cc26..0000000
--- a/android/support/graphics/drawable/AnimationUtilsCompat.java
+++ /dev/null
@@ -1,147 +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.graphics.drawable;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.support.v4.view.animation.LinearOutSlowInInterpolator;
-import android.util.AttributeSet;
-import android.util.Xml;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.AnticipateInterpolator;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.BounceInterpolator;
-import android.view.animation.CycleInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.OvershootInterpolator;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * Defines common utilities for working with animations.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class AnimationUtilsCompat {
-    /**
-     * Loads an {@link Interpolator} object from a resource
-     *
-     * @param context Application context used to access resources
-     * @param id      The resource id of the animation to load
-     * @return The animation object reference by the specified id
-     */
-    public static Interpolator loadInterpolator(Context context, int id)
-            throws NotFoundException {
-        // From API 21, we added path Interpolator .
-        if (Build.VERSION.SDK_INT >= 21) {
-            return AnimationUtils.loadInterpolator(context, id);
-        }
-
-        XmlResourceParser parser = null;
-        try {
-            // Special treatment for the interpolator introduced at API 21.
-            if (id == AndroidResources.FAST_OUT_LINEAR_IN) {
-                return new FastOutLinearInInterpolator();
-            } else if (id == AndroidResources.FAST_OUT_SLOW_IN) {
-                return new FastOutSlowInInterpolator();
-            } else if (id == AndroidResources.LINEAR_OUT_SLOW_IN) {
-                return new LinearOutSlowInInterpolator();
-            }
-            parser = context.getResources().getAnimation(id);
-            return createInterpolatorFromXml(context, context.getResources(), context.getTheme(),
-                    parser);
-        } catch (XmlPullParserException ex) {
-            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x"
-                    + Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } catch (IOException ex) {
-            NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x"
-                    + Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } finally {
-            if (parser != null) parser.close();
-        }
-
-    }
-
-    private static Interpolator createInterpolatorFromXml(Context context, Resources res,
-            Theme theme,
-            XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-
-        Interpolator interpolator = null;
-
-        // 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;
-            }
-
-            AttributeSet attrs = Xml.asAttributeSet(parser);
-
-            String name = parser.getName();
-
-            if (name.equals("linearInterpolator")) {
-                interpolator = new LinearInterpolator();
-            } else if (name.equals("accelerateInterpolator")) {
-                interpolator = new AccelerateInterpolator(context, attrs);
-            } else if (name.equals("decelerateInterpolator")) {
-                interpolator = new DecelerateInterpolator(context, attrs);
-            } else if (name.equals("accelerateDecelerateInterpolator")) {
-                interpolator = new AccelerateDecelerateInterpolator();
-            } else if (name.equals("cycleInterpolator")) {
-                interpolator = new CycleInterpolator(context, attrs);
-            } else if (name.equals("anticipateInterpolator")) {
-                interpolator = new AnticipateInterpolator(context, attrs);
-            } else if (name.equals("overshootInterpolator")) {
-                interpolator = new OvershootInterpolator(context, attrs);
-            } else if (name.equals("anticipateOvershootInterpolator")) {
-                interpolator = new AnticipateOvershootInterpolator(context, attrs);
-            } else if (name.equals("bounceInterpolator")) {
-                interpolator = new BounceInterpolator();
-            } else if (name.equals("pathInterpolator")) {
-                interpolator = new PathInterpolatorCompat(context, attrs, parser);
-            } else {
-                throw new RuntimeException("Unknown interpolator name: " + parser.getName());
-            }
-        }
-        return interpolator;
-    }
-}
diff --git a/android/support/graphics/drawable/AnimatorInflaterCompat.java b/android/support/graphics/drawable/AnimatorInflaterCompat.java
deleted file mode 100644
index da522f6..0000000
--- a/android/support/graphics/drawable/AnimatorInflaterCompat.java
+++ /dev/null
@@ -1,920 +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.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;
-import android.animation.Keyframe;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TypeEvaluator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-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;
-import android.util.Xml;
-import android.view.InflateException;
-import android.view.animation.Interpolator;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-/**
- * This class is used to instantiate animator XML files into Animator objects.
- * <p>
- * For performance reasons, inflation relies heavily on pre-processing of
- * XML files that is done at build time. Therefore, it is not currently possible
- * to use this inflater with an XmlPullParser over a plain XML file at runtime;
- * it only works with an XmlPullParser returned from a compiled resource (R.
- * <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 MAX_NUM_POINTS = 100;
-    /**
-     * Enum values used in XML attributes to indicate the value for mValueType
-     */
-    private static final int VALUE_TYPE_FLOAT = 0;
-    private static final int VALUE_TYPE_INT = 1;
-    private static final int VALUE_TYPE_PATH = 2;
-    private static final int VALUE_TYPE_COLOR = 3;
-    private static final int VALUE_TYPE_UNDEFINED = 4;
-
-    private static final boolean DBG_ANIMATOR_INFLATER = false;
-
-    /**
-     * Loads an {@link Animator} object from a context
-     *
-     * @param context Application context used to access resources
-     * @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
-     */
-    public static Animator loadAnimator(Context context, @AnimatorRes int id)
-            throws NotFoundException {
-        Animator objectAnimator;
-        // Since AVDC will fall back onto AVD when API is >= 24, therefore, PathParser will need
-        // to match the accordingly to be able to call into the right setter/ getter for animation.
-        if (Build.VERSION.SDK_INT >= 24) {
-            objectAnimator = AnimatorInflater.loadAnimator(context, id);
-        } else {
-            objectAnimator = loadAnimator(context, context.getResources(), context.getTheme(), id);
-        }
-        return objectAnimator;
-    }
-
-    /**
-     * 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
-     */
-    public static Animator loadAnimator(Context context, Resources resources, Theme theme,
-            @AnimatorRes int id) throws NotFoundException {
-        return loadAnimator(context, resources, theme, id, 1);
-    }
-
-    /**
-     * Loads an {@link Animator} object from a resource, context is for loading interpolator.
-     */
-    public static Animator loadAnimator(Context context, Resources resources, Theme theme,
-            @AnimatorRes int id, float pathErrorScale) throws NotFoundException {
-        Animator animator;
-
-        XmlResourceParser parser = null;
-        try {
-            parser = resources.getAnimation(id);
-            animator = createAnimatorFromXml(context, resources, theme, parser, pathErrorScale);
-            return animator;
-        } catch (XmlPullParserException ex) {
-            Resources.NotFoundException rnf =
-                    new Resources.NotFoundException("Can't load animation resource ID #0x"
-                            + Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } catch (IOException ex) {
-            Resources.NotFoundException rnf =
-                    new Resources.NotFoundException("Can't load animation resource ID #0x"
-                            + Integer.toHexString(id));
-            rnf.initCause(ex);
-            throw rnf;
-        } finally {
-            if (parser != null) parser.close();
-        }
-    }
-
-    /**
-     * PathDataEvaluator is used to interpolate between two paths which are
-     * represented in the same format but different control points' values.
-     * The path is represented as an array of PathDataNode here, which is
-     * fundamentally an array of floating point numbers.
-     */
-    private static class PathDataEvaluator implements
-            TypeEvaluator<PathParser.PathDataNode[]> {
-        private PathParser.PathDataNode[] mNodeArray;
-
-        /**
-         * Create a PathParser.PathDataNode[] that does not reuse the animated value.
-         * Care must be taken when using this option because on every evaluation
-         * a new <code>PathParser.PathDataNode[]</code> will be allocated.
-         */
-        private PathDataEvaluator() {
-        }
-
-        /**
-         * Create a PathDataEvaluator that reuses <code>nodeArray</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 nodeArray The array to modify and return from <code>evaluate</code>.
-         */
-        PathDataEvaluator(PathParser.PathDataNode[] nodeArray) {
-            mNodeArray = nodeArray;
-        }
-
-        @Override
-        public PathParser.PathDataNode[] evaluate(float fraction,
-                PathParser.PathDataNode[] startPathData,
-                PathParser.PathDataNode[] endPathData) {
-            if (!PathParser.canMorph(startPathData, endPathData)) {
-                throw new IllegalArgumentException("Can't interpolate between"
-                        + " two incompatible pathData");
-            }
-
-            if (mNodeArray == null || !PathParser.canMorph(mNodeArray, startPathData)) {
-                mNodeArray = PathParser.deepCopyNodes(startPathData);
-            }
-
-            for (int i = 0; i < startPathData.length; i++) {
-                mNodeArray[i].interpolatePathDataNode(startPathData[i],
-                        endPathData[i], fraction);
-            }
-
-            return mNodeArray;
-        }
-    }
-
-
-    private static PropertyValuesHolder getPVH(TypedArray styledAttributes, int valueType,
-            int valueFromId, int valueToId, String propertyName) {
-
-        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
-        boolean hasFrom = (tvFrom != null);
-        int fromType = hasFrom ? tvFrom.type : 0;
-        TypedValue tvTo = styledAttributes.peekValue(valueToId);
-        boolean hasTo = (tvTo != null);
-        int toType = hasTo ? tvTo.type : 0;
-
-        if (valueType == VALUE_TYPE_UNDEFINED) {
-            // Check whether it's color type. If not, fall back to default type (i.e. float type)
-            if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
-                valueType = VALUE_TYPE_COLOR;
-            } else {
-                valueType = VALUE_TYPE_FLOAT;
-            }
-        }
-
-        boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
-
-        PropertyValuesHolder returnValue = null;
-
-        if (valueType == VALUE_TYPE_PATH) {
-            String fromString = styledAttributes.getString(valueFromId);
-            String toString = styledAttributes.getString(valueToId);
-
-            PathParser.PathDataNode[] nodesFrom =
-                    PathParser.createNodesFromPathData(fromString);
-            PathParser.PathDataNode[] nodesTo =
-                    PathParser.createNodesFromPathData(toString);
-            if (nodesFrom != null || nodesTo != null) {
-                if (nodesFrom != null) {
-                    TypeEvaluator evaluator = new PathDataEvaluator();
-                    if (nodesTo != null) {
-                        if (!PathParser.canMorph(nodesFrom, nodesTo)) {
-                            throw new InflateException(" Can't morph from " + fromString + " to "
-                                    + toString);
-                        }
-                        returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
-                                nodesFrom, nodesTo);
-                    } else {
-                        returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
-                                (Object) nodesFrom);
-                    }
-                } else if (nodesTo != null) {
-                    TypeEvaluator evaluator = new PathDataEvaluator();
-                    returnValue = PropertyValuesHolder.ofObject(propertyName, evaluator,
-                            (Object) nodesTo);
-                }
-            }
-        } else {
-            TypeEvaluator evaluator = null;
-            // Integer and float value types are handled here.
-            if (valueType == VALUE_TYPE_COLOR) {
-                // special case for colors: ignore valueType and get ints
-                evaluator = ArgbEvaluator.getInstance();
-            }
-            if (getFloats) {
-                float valueFrom;
-                float valueTo;
-                if (hasFrom) {
-                    if (fromType == TypedValue.TYPE_DIMENSION) {
-                        valueFrom = styledAttributes.getDimension(valueFromId, 0f);
-                    } else {
-                        valueFrom = styledAttributes.getFloat(valueFromId, 0f);
-                    }
-                    if (hasTo) {
-                        if (toType == TypedValue.TYPE_DIMENSION) {
-                            valueTo = styledAttributes.getDimension(valueToId, 0f);
-                        } else {
-                            valueTo = styledAttributes.getFloat(valueToId, 0f);
-                        }
-                        returnValue = PropertyValuesHolder.ofFloat(propertyName,
-                                valueFrom, valueTo);
-                    } else {
-                        returnValue = PropertyValuesHolder.ofFloat(propertyName, valueFrom);
-                    }
-                } else {
-                    if (toType == TypedValue.TYPE_DIMENSION) {
-                        valueTo = styledAttributes.getDimension(valueToId, 0f);
-                    } else {
-                        valueTo = styledAttributes.getFloat(valueToId, 0f);
-                    }
-                    returnValue = PropertyValuesHolder.ofFloat(propertyName, valueTo);
-                }
-            } else {
-                int valueFrom;
-                int valueTo;
-                if (hasFrom) {
-                    if (fromType == TypedValue.TYPE_DIMENSION) {
-                        valueFrom = (int) styledAttributes.getDimension(valueFromId, 0f);
-                    } else if (isColorType(fromType)) {
-                        valueFrom = styledAttributes.getColor(valueFromId, 0);
-                    } else {
-                        valueFrom = styledAttributes.getInt(valueFromId, 0);
-                    }
-                    if (hasTo) {
-                        if (toType == TypedValue.TYPE_DIMENSION) {
-                            valueTo = (int) styledAttributes.getDimension(valueToId, 0f);
-                        } else if (isColorType(toType)) {
-                            valueTo = styledAttributes.getColor(valueToId, 0);
-                        } else {
-                            valueTo = styledAttributes.getInt(valueToId, 0);
-                        }
-                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueFrom, valueTo);
-                    } else {
-                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueFrom);
-                    }
-                } else {
-                    if (hasTo) {
-                        if (toType == TypedValue.TYPE_DIMENSION) {
-                            valueTo = (int) styledAttributes.getDimension(valueToId, 0f);
-                        } else if (isColorType(toType)) {
-                            valueTo = styledAttributes.getColor(valueToId, 0);
-                        } else {
-                            valueTo = styledAttributes.getInt(valueToId, 0);
-                        }
-                        returnValue = PropertyValuesHolder.ofInt(propertyName, valueTo);
-                    }
-                }
-            }
-            if (returnValue != null && evaluator != null) {
-                returnValue.setEvaluator(evaluator);
-            }
-        }
-
-        return returnValue;
-    }
-
-    /**
-     * @param anim                The animator, must not be null
-     * @param arrayAnimator       Incoming typed array for Animator's attributes.
-     * @param arrayObjectAnimator Incoming typed array for Object Animator's
-     *                            attributes.
-     * @param pixelSize           The relative pixel size, used to calculate the
-     *                            maximum error for path animations.
-     */
-    private static void parseAnimatorFromTypeArray(ValueAnimator anim,
-            TypedArray arrayAnimator, TypedArray arrayObjectAnimator, float pixelSize,
-            XmlPullParser parser) {
-        long duration = TypedArrayUtils.getNamedInt(arrayAnimator, parser, "duration",
-                AndroidResources.STYLEABLE_ANIMATOR_DURATION, 300);
-        long startDelay = TypedArrayUtils.getNamedInt(arrayAnimator, parser, "startOffset",
-                AndroidResources.STYLEABLE_ANIMATOR_START_OFFSET, 0);
-        int valueType = TypedArrayUtils.getNamedInt(arrayAnimator, parser, "valueType",
-                AndroidResources.STYLEABLE_ANIMATOR_VALUE_TYPE, VALUE_TYPE_UNDEFINED);
-
-        // Change to requiring both value from and to, otherwise, throw exception for now.
-        if (TypedArrayUtils.hasAttribute(parser, "valueFrom")
-                && TypedArrayUtils.hasAttribute(parser, "valueTo")) {
-            if (valueType == VALUE_TYPE_UNDEFINED) {
-                valueType = inferValueTypeFromValues(arrayAnimator,
-                        AndroidResources.STYLEABLE_ANIMATOR_VALUE_FROM,
-                        AndroidResources.STYLEABLE_ANIMATOR_VALUE_TO);
-            }
-            PropertyValuesHolder pvh = getPVH(arrayAnimator, valueType,
-                    AndroidResources.STYLEABLE_ANIMATOR_VALUE_FROM,
-                    AndroidResources.STYLEABLE_ANIMATOR_VALUE_TO, "");
-            if (pvh != null) {
-                anim.setValues(pvh);
-            }
-        }
-        anim.setDuration(duration);
-        anim.setStartDelay(startDelay);
-
-        anim.setRepeatCount(TypedArrayUtils.getNamedInt(arrayAnimator, parser, "repeatCount",
-                AndroidResources.STYLEABLE_ANIMATOR_REPEAT_COUNT, 0));
-        anim.setRepeatMode(TypedArrayUtils.getNamedInt(arrayAnimator, parser, "repeatMode",
-                AndroidResources.STYLEABLE_ANIMATOR_REPEAT_MODE, ValueAnimator.RESTART));
-
-        if (arrayObjectAnimator != null) {
-            setupObjectAnimator(anim, arrayObjectAnimator, valueType, pixelSize, parser);
-        }
-    }
-
-
-    /**
-     * Setup ObjectAnimator's property or values from pathData.
-     *
-     * @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
-     */
-    private static void setupObjectAnimator(ValueAnimator anim, TypedArray arrayObjectAnimator,
-            int valueType, float pixelSize, XmlPullParser parser) {
-        ObjectAnimator oa = (ObjectAnimator) anim;
-        String pathData = TypedArrayUtils.getNamedString(arrayObjectAnimator, parser, "pathData",
-                AndroidResources.STYLEABLE_PROPERTY_ANIMATOR_PATH_DATA);
-
-        // Path can be involved in an ObjectAnimator in the following 3 ways:
-        // 1) Path morphing: the property to be animated is pathData, and valueFrom and valueTo
-        //    are both of pathType. valueType = pathType needs to be explicitly defined.
-        // 2) A property in X or Y dimension can be animated along a path: the property needs to be
-        //    defined in propertyXName or propertyYName attribute, the path will be defined in the
-        //    pathData attribute. valueFrom and valueTo will not be necessary for this animation.
-        // 3) PathInterpolator can also define a path (in pathData) for its interpolation curve.
-        // Here we are dealing with case 2:
-        if (pathData != null) {
-            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",
-                            AndroidResources.STYLEABLE_PROPERTY_ANIMATOR_PROPERTY_NAME);
-            oa.setPropertyName(propertyName);
-        }
-
-
-        return;
-
-    }
-
-    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);
-
-            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)
-            throws XmlPullParserException, IOException {
-        return createAnimatorFromXml(context, res, theme, parser, Xml.asAttributeSet(parser), null,
-                0, pixelSize);
-    }
-
-    private static Animator createAnimatorFromXml(Context context, Resources res, Theme theme,
-            XmlPullParser parser,
-            AttributeSet attrs, AnimatorSet parent, int sequenceOrdering, float pixelSize)
-            throws XmlPullParserException, IOException {
-        Animator anim = null;
-        ArrayList<Animator> childAnims = null;
-
-        // 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();
-            boolean gotValues = false;
-
-            if (name.equals("objectAnimator")) {
-                anim = loadObjectAnimator(context, res, theme, attrs, pixelSize, parser);
-            } else if (name.equals("animator")) {
-                anim = loadAnimator(context, res, theme, attrs, null, pixelSize, parser);
-            } else if (name.equals("set")) {
-                anim = new AnimatorSet();
-                TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                        AndroidResources.STYLEABLE_ANIMATOR_SET);
-
-                int ordering = TypedArrayUtils.getNamedInt(a, parser, "ordering",
-                        AndroidResources.STYLEABLE_ANIMATOR_SET_ORDERING, TOGETHER);
-
-                createAnimatorFromXml(context, res, theme, parser, attrs, (AnimatorSet) anim,
-                        ordering, pixelSize);
-                a.recycle();
-            } else if (name.equals("propertyValuesHolder")) {
-                PropertyValuesHolder[] values = loadValues(context, res, theme, parser,
-                        Xml.asAttributeSet(parser));
-                if (values != null && anim != null && (anim instanceof ValueAnimator)) {
-                    ((ValueAnimator) anim).setValues(values);
-                }
-                gotValues = true;
-            } else {
-                throw new RuntimeException("Unknown animator name: " + parser.getName());
-            }
-
-            if (parent != null && !gotValues) {
-                if (childAnims == null) {
-                    childAnims = new ArrayList<Animator>();
-                }
-                childAnims.add(anim);
-            }
-        }
-        if (parent != null && childAnims != null) {
-            Animator[] animsArray = new Animator[childAnims.size()];
-            int index = 0;
-            for (Animator a : childAnims) {
-                animsArray[index++] = a;
-            }
-            if (sequenceOrdering == TOGETHER) {
-                parent.playTogether(animsArray);
-            } else {
-                parent.playSequentially(animsArray);
-            }
-        }
-        return anim;
-    }
-
-    private static PropertyValuesHolder[] loadValues(Context context, Resources res, Theme theme,
-            XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {
-        ArrayList<PropertyValuesHolder> values = null;
-
-        int type;
-        while ((type = parser.getEventType()) != XmlPullParser.END_TAG
-                && type != XmlPullParser.END_DOCUMENT) {
-
-            if (type != XmlPullParser.START_TAG) {
-                parser.next();
-                continue;
-            }
-
-            String name = parser.getName();
-
-            if (name.equals("propertyValuesHolder")) {
-                TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                        AndroidResources.STYLEABLE_PROPERTY_VALUES_HOLDER);
-
-                String propertyName = TypedArrayUtils.getNamedString(a, parser, "propertyName",
-                        AndroidResources.STYLEABLE_PROPERTY_VALUES_HOLDER_PROPERTY_NAME);
-                int valueType = TypedArrayUtils.getNamedInt(a, parser, "valueType",
-                        AndroidResources.STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_TYPE,
-                        VALUE_TYPE_UNDEFINED);
-
-                PropertyValuesHolder pvh = loadPvh(context, res, theme, parser, propertyName,
-                        valueType);
-                if (pvh == null) {
-                    pvh = getPVH(a, valueType,
-                            AndroidResources.STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_FROM,
-                            AndroidResources.STYLEABLE_PROPERTY_VALUES_HOLDER_VALUE_TO,
-                            propertyName);
-                }
-                if (pvh != null) {
-                    if (values == null) {
-                        values = new ArrayList<PropertyValuesHolder>();
-                    }
-                    values.add(pvh);
-                }
-                a.recycle();
-            }
-
-            parser.next();
-        }
-
-        PropertyValuesHolder[] valuesArray = null;
-        if (values != null) {
-            int count = values.size();
-            valuesArray = new PropertyValuesHolder[count];
-            for (int i = 0; i < count; ++i) {
-                valuesArray[i] = values.get(i);
-            }
-        }
-        return valuesArray;
-    }
-
-    // When no value type is provided in keyframe, we need to infer the type from the value. i.e.
-    // if value is defined in the style of a color value, then the color type is returned.
-    // Otherwise, default float type is returned.
-    private static int inferValueTypeOfKeyframe(Resources res, Theme theme, AttributeSet attrs,
-            XmlPullParser parser) {
-        int valueType;
-        TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                AndroidResources.STYLEABLE_KEYFRAME);
-
-        TypedValue keyframeValue = TypedArrayUtils.peekNamedValue(a, parser, "value",
-                AndroidResources.STYLEABLE_KEYFRAME_VALUE);
-        boolean hasValue = (keyframeValue != null);
-        // When no value type is provided, check whether it's a color type first.
-        // If not, fall back to default value type (i.e. float type).
-        if (hasValue && isColorType(keyframeValue.type)) {
-            valueType = VALUE_TYPE_COLOR;
-        } else {
-            valueType = VALUE_TYPE_FLOAT;
-        }
-        a.recycle();
-        return valueType;
-    }
-
-    private static int inferValueTypeFromValues(TypedArray styledAttributes, int valueFromId,
-            int valueToId) {
-        TypedValue tvFrom = styledAttributes.peekValue(valueFromId);
-        boolean hasFrom = (tvFrom != null);
-        int fromType = hasFrom ? tvFrom.type : 0;
-        TypedValue tvTo = styledAttributes.peekValue(valueToId);
-        boolean hasTo = (tvTo != null);
-        int toType = hasTo ? tvTo.type : 0;
-
-        int valueType;
-        // Check whether it's color type. If not, fall back to default type (i.e. float type)
-        if ((hasFrom && isColorType(fromType)) || (hasTo && isColorType(toType))) {
-            valueType = VALUE_TYPE_COLOR;
-        } else {
-            valueType = VALUE_TYPE_FLOAT;
-        }
-        return valueType;
-    }
-
-    private static void dumpKeyframes(Object[] keyframes, String header) {
-        if (keyframes == null || keyframes.length == 0) {
-            return;
-        }
-        Log.d(TAG, header);
-        int count = keyframes.length;
-        for (int i = 0; i < count; ++i) {
-            Keyframe keyframe = (Keyframe) keyframes[i];
-            Log.d(TAG, "Keyframe " + i + ": fraction "
-                    + (keyframe.getFraction() < 0 ? "null" : keyframe.getFraction()) + ", "
-                    + ", value : " + ((keyframe.hasValue()) ? keyframe.getValue() : "null"));
-        }
-    }
-
-    // Load property values holder if there are keyframes defined in it. Otherwise return null.
-    private static PropertyValuesHolder loadPvh(Context context, Resources res, Theme theme,
-            XmlPullParser parser,
-            String propertyName, int valueType)
-            throws XmlPullParserException, IOException {
-
-        PropertyValuesHolder value = null;
-        ArrayList<Keyframe> keyframes = null;
-
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_TAG
-                && type != XmlPullParser.END_DOCUMENT) {
-            String name = parser.getName();
-            if (name.equals("keyframe")) {
-                if (valueType == VALUE_TYPE_UNDEFINED) {
-                    valueType = inferValueTypeOfKeyframe(res, theme, Xml.asAttributeSet(parser),
-                            parser);
-                }
-                Keyframe keyframe = loadKeyframe(context, res, theme, Xml.asAttributeSet(parser),
-                        valueType, parser);
-                if (keyframe != null) {
-                    if (keyframes == null) {
-                        keyframes = new ArrayList<Keyframe>();
-                    }
-                    keyframes.add(keyframe);
-                }
-                parser.next();
-            }
-        }
-
-        int count;
-        if (keyframes != null && (count = keyframes.size()) > 0) {
-            // make sure we have keyframes at 0 and 1
-            // If we have keyframes with set fractions, add keyframes at start/end
-            // appropriately. If start/end have no set fractions:
-            // if there's only one keyframe, set its fraction to 1 and add one at 0
-            // if >1 keyframe, set the last fraction to 1, the first fraction to 0
-            Keyframe firstKeyframe = keyframes.get(0);
-            Keyframe lastKeyframe = keyframes.get(count - 1);
-            float endFraction = lastKeyframe.getFraction();
-            if (endFraction < 1) {
-                if (endFraction < 0) {
-                    lastKeyframe.setFraction(1);
-                } else {
-                    keyframes.add(keyframes.size(), createNewKeyframe(lastKeyframe, 1));
-                    ++count;
-                }
-            }
-            float startFraction = firstKeyframe.getFraction();
-            if (startFraction != 0) {
-                if (startFraction < 0) {
-                    firstKeyframe.setFraction(0);
-                } else {
-                    keyframes.add(0, createNewKeyframe(firstKeyframe, 0));
-                    ++count;
-                }
-            }
-            Keyframe[] keyframeArray = new Keyframe[count];
-            keyframes.toArray(keyframeArray);
-            for (int i = 0; i < count; ++i) {
-                Keyframe keyframe = keyframeArray[i];
-                if (keyframe.getFraction() < 0) {
-                    if (i == 0) {
-                        keyframe.setFraction(0);
-                    } else if (i == count - 1) {
-                        keyframe.setFraction(1);
-                    } else {
-                        // figure out the start/end parameters of the current gap
-                        // in fractions and distribute the gap among those keyframes
-                        int startIndex = i;
-                        int endIndex = i;
-                        for (int j = startIndex + 1; j < count - 1; ++j) {
-                            if (keyframeArray[j].getFraction() >= 0) {
-                                break;
-                            }
-                            endIndex = j;
-                        }
-                        float gap = keyframeArray[endIndex + 1].getFraction()
-                                - keyframeArray[startIndex - 1].getFraction();
-                        distributeKeyframes(keyframeArray, gap, startIndex, endIndex);
-                    }
-                }
-            }
-            value = PropertyValuesHolder.ofKeyframe(propertyName, keyframeArray);
-            if (valueType == VALUE_TYPE_COLOR) {
-                value.setEvaluator(ArgbEvaluator.getInstance());
-            }
-        }
-
-        return value;
-    }
-
-    private static Keyframe createNewKeyframe(Keyframe sampleKeyframe, float fraction) {
-        return sampleKeyframe.getType() == float.class
-                ? Keyframe.ofFloat(fraction) :
-                (sampleKeyframe.getType() == int.class)
-                        ? Keyframe.ofInt(fraction) :
-                        Keyframe.ofObject(fraction);
-    }
-
-    /**
-     * Utility function to set fractions on keyframes to cover a gap in which the
-     * fractions are not currently set. Keyframe fractions will be distributed evenly
-     * in this gap. For example, a gap of 1 keyframe in the range 0-1 will be at .5, a gap
-     * of .6 spread between two keyframes will be at .2 and .4 beyond the fraction at the
-     * keyframe before startIndex.
-     * Assumptions:
-     * - First and last keyframe fractions (bounding this spread) are already set. So,
-     * for example, if no fractions are set, we will already set first and last keyframe
-     * fraction values to 0 and 1.
-     * - startIndex must be >0 (which follows from first assumption).
-     * - endIndex must be >= startIndex.
-     *
-     * @param keyframes  the array of keyframes
-     * @param gap        The total gap we need to distribute
-     * @param startIndex The index of the first keyframe whose fraction must be set
-     * @param endIndex   The index of the last keyframe whose fraction must be set
-     */
-    private static void distributeKeyframes(Keyframe[] keyframes, float gap,
-            int startIndex, int endIndex) {
-        int count = endIndex - startIndex + 2;
-        float increment = gap / count;
-        for (int i = startIndex; i <= endIndex; ++i) {
-            keyframes[i].setFraction(keyframes[i - 1].getFraction() + increment);
-        }
-    }
-
-    private static Keyframe loadKeyframe(Context context, Resources res, Theme theme,
-            AttributeSet attrs,
-            int valueType, XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-
-        TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                AndroidResources.STYLEABLE_KEYFRAME);
-
-        Keyframe keyframe = null;
-
-        float fraction = TypedArrayUtils.getNamedFloat(a, parser, "fraction",
-                AndroidResources.STYLEABLE_KEYFRAME_FRACTION, -1);
-
-        TypedValue keyframeValue = TypedArrayUtils.peekNamedValue(a, parser, "value",
-                AndroidResources.STYLEABLE_KEYFRAME_VALUE);
-        boolean hasValue = (keyframeValue != null);
-        if (valueType == VALUE_TYPE_UNDEFINED) {
-            // When no value type is provided, check whether it's a color type first.
-            // If not, fall back to default value type (i.e. float type).
-            if (hasValue && isColorType(keyframeValue.type)) {
-                valueType = VALUE_TYPE_COLOR;
-            } else {
-                valueType = VALUE_TYPE_FLOAT;
-            }
-        }
-
-        if (hasValue) {
-            switch (valueType) {
-                case VALUE_TYPE_FLOAT:
-                    float value = TypedArrayUtils.getNamedFloat(a, parser, "value",
-                            AndroidResources.STYLEABLE_KEYFRAME_VALUE, 0);
-                    keyframe = Keyframe.ofFloat(fraction, value);
-                    break;
-                case VALUE_TYPE_COLOR:
-                case VALUE_TYPE_INT:
-                    int intValue = TypedArrayUtils.getNamedInt(a, parser, "value",
-                            AndroidResources.STYLEABLE_KEYFRAME_VALUE, 0);
-                    keyframe = Keyframe.ofInt(fraction, intValue);
-                    break;
-            }
-        } else {
-            keyframe = (valueType == VALUE_TYPE_FLOAT) ? Keyframe.ofFloat(fraction) :
-                    Keyframe.ofInt(fraction);
-        }
-
-        final int resID = TypedArrayUtils.getNamedResourceId(a, parser, "interpolator",
-                AndroidResources.STYLEABLE_KEYFRAME_INTERPOLATOR, 0);
-        if (resID > 0) {
-            final Interpolator interpolator = AnimationUtilsCompat.loadInterpolator(context, resID);
-            keyframe.setInterpolator(interpolator);
-        }
-        a.recycle();
-
-        return keyframe;
-    }
-
-    private static ObjectAnimator loadObjectAnimator(Context context, Resources res, Theme theme,
-            AttributeSet attrs,
-            float pathErrorScale, XmlPullParser parser) throws NotFoundException {
-        ObjectAnimator anim = new ObjectAnimator();
-
-        loadAnimator(context, res, theme, attrs, anim, pathErrorScale, parser);
-
-        return anim;
-    }
-
-    /**
-     * Creates a new animation whose parameters come from the specified context
-     * and attributes set.
-     *
-     * @param res   The resources
-     * @param attrs The set of attributes holding the animation parameters
-     * @param anim  Null if this is a ValueAnimator, otherwise this is an
-     */
-    private static ValueAnimator loadAnimator(Context context, Resources res, Theme theme,
-            AttributeSet attrs, ValueAnimator anim, float pathErrorScale, XmlPullParser parser)
-            throws NotFoundException {
-        TypedArray arrayAnimator = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                AndroidResources.STYLEABLE_ANIMATOR);
-        TypedArray arrayObjectAnimator = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                AndroidResources.STYLEABLE_PROPERTY_ANIMATOR);
-
-        if (anim == null) {
-            anim = new ValueAnimator();
-        }
-
-        parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator, pathErrorScale,
-                parser);
-
-        final int resID = TypedArrayUtils.getNamedResourceId(arrayAnimator, parser, "interpolator",
-                AndroidResources.STYLEABLE_ANIMATOR_INTERPOLATOR, 0);
-        if (resID > 0) {
-            final Interpolator interpolator = AnimationUtilsCompat.loadInterpolator(context, resID);
-            anim.setInterpolator(interpolator);
-        }
-
-        arrayAnimator.recycle();
-        if (arrayObjectAnimator != null) {
-            arrayObjectAnimator.recycle();
-        }
-        return anim;
-    }
-
-    private static boolean isColorType(int type) {
-        return (type >= TypedValue.TYPE_FIRST_COLOR_INT) && (type
-                <= TypedValue.TYPE_LAST_COLOR_INT);
-    }
-}
-
diff --git a/android/support/graphics/drawable/ArgbEvaluator.java b/android/support/graphics/drawable/ArgbEvaluator.java
deleted file mode 100644
index f9e2985..0000000
--- a/android/support/graphics/drawable/ArgbEvaluator.java
+++ /dev/null
@@ -1,98 +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.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();
-
-    /**
-     * Returns an instance of <code>ArgbEvaluator</code> that may be used in
-     * {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
-     * be used in multiple <code>Animator</code>s because it holds no state.
-     *
-     * @return An instance of <code>ArgbEvalutor</code>.
-     */
-    public static ArgbEvaluator getInstance() {
-        return sInstance;
-    }
-
-    /**
-     * This function returns the calculated in-between value for a color
-     * given integers that represent the start and end values in the four
-     * bytes of the 32-bit int. Each channel is separately linearly interpolated
-     * and the resulting calculated values are recombined into the return value.
-     *
-     * @param fraction   The fraction from the starting to the ending values
-     * @param startValue A 32-bit int value representing colors in the
-     *                   separate bytes of the parameter
-     * @param endValue   A 32-bit int value representing colors in the
-     *                   separate bytes of the parameter
-     * @return A value that is calculated to be the linearly interpolated
-     * result, derived by separating the start and end values into separate
-     * 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;
-        float startR = ((startInt >> 16) & 0xff) / 255.0f;
-        float startG = ((startInt >> 8) & 0xff) / 255.0f;
-        float startB = ((startInt) & 0xff) / 255.0f;
-
-        int endInt = (Integer) endValue;
-        float endA = ((endInt >> 24) & 0xff) / 255.0f;
-        float endR = ((endInt >> 16) & 0xff) / 255.0f;
-        float endG = ((endInt >> 8) & 0xff) / 255.0f;
-        float endB = ((endInt) & 0xff) / 255.0f;
-
-        // convert from sRGB to linear
-        startR = (float) Math.pow(startR, 2.2);
-        startG = (float) Math.pow(startG, 2.2);
-        startB = (float) Math.pow(startB, 2.2);
-
-        endR = (float) Math.pow(endR, 2.2);
-        endG = (float) Math.pow(endG, 2.2);
-        endB = (float) Math.pow(endB, 2.2);
-
-        // compute the interpolated color in linear space
-        float a = startA + fraction * (endA - startA);
-        float r = startR + fraction * (endR - startR);
-        float g = startG + fraction * (endG - startG);
-        float b = startB + fraction * (endB - startB);
-
-        // convert back to sRGB in the [0..255] range
-        a = a * 255.0f;
-        r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
-        g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
-        b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
-
-        return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
-    }
-}
diff --git a/android/support/graphics/drawable/PathInterpolatorCompat.java b/android/support/graphics/drawable/PathInterpolatorCompat.java
deleted file mode 100644
index 081676e..0000000
--- a/android/support/graphics/drawable/PathInterpolatorCompat.java
+++ /dev/null
@@ -1,224 +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.graphics.drawable;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import static java.lang.Math.abs;
-import static java.lang.Math.min;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-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;
-
-import org.xmlpull.v1.XmlPullParser;
-
-/**
- * An interpolator that can traverse a Path that extends from <code>Point</code>
- * <code>(0, 0)</code> to <code>(1, 1)</code>. The x coordinate along the <code>Path</code>
- * is the input value and the output is the y coordinate of the line at that point.
- * This means that the Path must conform to a function <code>y = f(x)</code>.
- *
- * <p>The <code>Path</code> must not have gaps in the x direction and must not
- * loop back on itself such that there can be two points sharing the same x coordinate.
- * It is alright to have a disjoint line in the vertical direction:</p>
- * <p><blockquote><pre>
- *     Path path = new Path();
- *     path.lineTo(0.25f, 0.25f);
- *     path.moveTo(0.25f, 0.5f);
- *     path.lineTo(1f, 1f);
- * </pre></blockquote></p>
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class PathInterpolatorCompat implements Interpolator {
-
-    // This governs how accurate the approximation of the Path is.
-    private static final float PRECISION = 0.002f;
-    public static final int MAX_NUM_POINTS = 3000;
-    public static final double EPSILON = 0.00001;
-
-    private float[] mX; // x coordinates in the line
-
-    private float[] mY; // y coordinates in the line
-
-    public PathInterpolatorCompat(Context context, AttributeSet attrs, XmlPullParser parser) {
-        this(context.getResources(), context.getTheme(), attrs, parser);
-    }
-
-    public PathInterpolatorCompat(Resources res, Resources.Theme theme, AttributeSet attrs,
-            XmlPullParser parser) {
-        TypedArray a = TypedArrayUtils.obtainAttributes(res, theme,
-                attrs, AndroidResources.STYLEABLE_PATH_INTERPOLATOR);
-        parseInterpolatorFromTypeArray(a, parser);
-        a.recycle();
-    }
-
-    private void parseInterpolatorFromTypeArray(TypedArray a, XmlPullParser parser) {
-        // If there is pathData defined in the xml file, then the controls points
-        // will be all coming from pathData.
-        if (TypedArrayUtils.hasAttribute(parser, "pathData")) {
-            String pathData = TypedArrayUtils.getNamedString(a, parser, "pathData",
-                    AndroidResources.STYLEABLE_PATH_INTERPOLATOR_PATH_DATA);
-            Path path = PathParser.createPathFromPathData(pathData);
-            if (path == null) {
-                throw new InflateException("The path is null, which is created"
-                        + " from " + pathData);
-            }
-            initPath(path);
-        } else {
-            if (!TypedArrayUtils.hasAttribute(parser, "controlX1")) {
-                throw new InflateException("pathInterpolator requires the controlX1 attribute");
-            } else if (!TypedArrayUtils.hasAttribute(parser, "controlY1")) {
-                throw new InflateException("pathInterpolator requires the controlY1 attribute");
-            }
-            float x1 = TypedArrayUtils.getNamedFloat(a, parser, "controlX1",
-                    AndroidResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_1, 0);
-            float y1 = TypedArrayUtils.getNamedFloat(a, parser, "controlY1",
-                    AndroidResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_1, 0);
-
-            boolean hasX2 = TypedArrayUtils.hasAttribute(parser, "controlX2");
-            boolean hasY2 = TypedArrayUtils.hasAttribute(parser, "controlY2");
-
-            if (hasX2 != hasY2) {
-                throw new InflateException("pathInterpolator requires both controlX2 and"
-                        + " controlY2 for cubic Beziers.");
-            }
-
-            if (!hasX2) {
-                initQuad(x1, y1);
-            } else {
-                float x2 = TypedArrayUtils.getNamedFloat(a, parser, "controlX2",
-                        AndroidResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_X_2, 0);
-                float y2 = TypedArrayUtils.getNamedFloat(a, parser, "controlY2",
-                        AndroidResources.STYLEABLE_PATH_INTERPOLATOR_CONTROL_Y_2, 0);
-                initCubic(x1, y1, x2, y2);
-            }
-        }
-    }
-
-    private void initQuad(float controlX, float controlY) {
-        Path path = new Path();
-        path.moveTo(0, 0);
-        path.quadTo(controlX, controlY, 1f, 1f);
-        initPath(path);
-    }
-
-    private void initCubic(float x1, float y1, float x2, float y2) {
-        Path path = new Path();
-        path.moveTo(0, 0);
-        path.cubicTo(x1, y1, x2, y2, 1f, 1f);
-        initPath(path);
-    }
-
-    private void initPath(Path path) {
-        final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
-
-        final float pathLength = pathMeasure.getLength();
-        final int numPoints = min(MAX_NUM_POINTS, (int) (pathLength / PRECISION) + 1);
-
-        if (numPoints <= 0) {
-            throw new IllegalArgumentException("The Path has a invalid length " + pathLength);
-        }
-
-        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];
-        }
-
-        if (abs(mX[0]) > EPSILON || abs(mY[0]) > EPSILON || abs(mX[numPoints - 1] - 1) > EPSILON
-                || abs(mY[numPoints - 1] - 1) > EPSILON) {
-            throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)"
-                    + " start: " + mX[0] + "," + mY[0] + " end:" + mX[numPoints - 1] + ","
-                    + mY[numPoints - 1]);
-
-        }
-
-        float prevX = 0;
-        int componentIndex = 0;
-        for (int i = 0; i < numPoints; i++) {
-            float x = mX[componentIndex++];
-            if (x < prevX) {
-                throw new IllegalArgumentException("The Path cannot loop back on itself, x :" + x);
-            }
-            mX[i] = x;
-            prevX = x;
-        }
-
-        if (pathMeasure.nextContour()) {
-            throw new IllegalArgumentException("The Path should be continuous,"
-                    + " can't have 2+ contours");
-        }
-    }
-
-    /**
-     * Using the line in the Path in this interpolator that can be described as
-     * <code>y = f(x)</code>, finds the y coordinate of the line given <code>t</code>
-     * as the x coordinate. Values less than 0 will always return 0 and values greater
-     * than 1 will always return 1.
-     *
-     * @param t Treated as the x coordinate along the line.
-     * @return The y coordinate of the Path along the line where x = <code>t</code>.
-     * @see Interpolator#getInterpolation(float)
-     */
-    @Override
-    public float getInterpolation(float t) {
-        if (t <= 0) {
-            return 0;
-        } else if (t >= 1) {
-            return 1;
-        }
-        // 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;
-            }
-        }
-
-        float xRange = mX[endIndex] - mX[startIndex];
-        if (xRange == 0) {
-            return mY[startIndex];
-        }
-
-        float tInRange = t - mX[startIndex];
-        float fraction = tInRange / xRange;
-
-        float startY = mY[startIndex];
-        float endY = mY[endIndex];
-        return startY + (fraction * (endY - startY));
-    }
-}
diff --git a/android/support/graphics/drawable/VectorDrawableCommon.java b/android/support/graphics/drawable/VectorDrawableCommon.java
deleted file mode 100644
index 57d9b2b..0000000
--- a/android/support/graphics/drawable/VectorDrawableCommon.java
+++ /dev/null
@@ -1,183 +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.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.graphics.drawable.TintAwareDrawable;
-
-/**
- * Internal common delegation shared by VectorDrawableCompat and AnimatedVectorDrawableCompat
- */
-abstract class VectorDrawableCommon extends Drawable implements TintAwareDrawable {
-
-    // Drawable delegation for supported API levels.
-    Drawable mDelegateDrawable;
-
-    @Override
-    public void setColorFilter(int color, PorterDuff.Mode mode) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setColorFilter(color, mode);
-            return;
-        }
-        super.setColorFilter(color, mode);
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        if (mDelegateDrawable != null) {
-            return DrawableCompat.getColorFilter(mDelegateDrawable);
-        }
-        return null;
-    }
-
-    @Override
-    protected boolean onLevelChange(int level) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setLevel(level);
-        }
-        return super.onLevelChange(level);
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setBounds(bounds);
-            return;
-        }
-        super.onBoundsChange(bounds);
-    }
-
-    @Override
-    public void setHotspot(float x, float y) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setHotspot(mDelegateDrawable, x, y);
-        }
-        return;
-    }
-
-    @Override
-    public void setHotspotBounds(int left, int top, int right, int bottom) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setHotspotBounds(mDelegateDrawable, left, top, right, bottom);
-            return;
-        }
-    }
-
-    @Override
-    public void setFilterBitmap(boolean filter) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setFilterBitmap(filter);
-            return;
-        }
-    }
-
-    @Override
-    public void jumpToCurrentState() {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.jumpToCurrentState(mDelegateDrawable);
-            return;
-        }
-    }
-
-    @Override
-    public void applyTheme(Resources.Theme t) {
-        // API >= 21 only.
-        if (mDelegateDrawable != null) {
-            DrawableCompat.applyTheme(mDelegateDrawable, t);
-            return;
-        }
-    }
-
-    @Override
-    public void clearColorFilter() {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.clearColorFilter();
-            return;
-        }
-        super.clearColorFilter();
-    }
-
-    @Override
-    public Drawable getCurrent() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getCurrent();
-        }
-        return super.getCurrent();
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getMinimumWidth();
-        }
-        return super.getMinimumWidth();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getMinimumHeight();
-        }
-        return super.getMinimumHeight();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getPadding(padding);
-        }
-        return super.getPadding(padding);
-    }
-
-    @Override
-    public int[] getState() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getState();
-        }
-        return super.getState();
-    }
-
-
-    @Override
-    public Region getTransparentRegion() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getTransparentRegion();
-        }
-        return super.getTransparentRegion();
-    }
-
-    @Override
-    public void setChangingConfigurations(int configs) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setChangingConfigurations(configs);
-            return;
-        }
-        super.setChangingConfigurations(configs);
-    }
-
-    @Override
-    public boolean setState(int[] stateSet) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setState(stateSet);
-        }
-        return super.setState(stateSet);
-    }
-}
diff --git a/android/support/graphics/drawable/VectorDrawableCompat.java b/android/support/graphics/drawable/VectorDrawableCompat.java
deleted file mode 100644
index 943f1aa..0000000
--- a/android/support/graphics/drawable/VectorDrawableCompat.java
+++ /dev/null
@@ -1,1876 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.SuppressLint;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.VectorDrawable;
-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.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.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-
-/**
- * For API 24 and above, this class is delegating to the framework's {@link VectorDrawable}.
- * For older API version, this class lets you create a drawable based on an XML vector graphic.
- * <p/>
- * You can always create a VectorDrawableCompat object and use it as a Drawable by the Java API.
- * In order to refer to VectorDrawableCompat inside a XML file,  you can use app:srcCompat attribute
- * in AppCompat library's ImageButton or ImageView.
- * <p/>
- * <strong>Note:</strong> To optimize for the re-drawing performance, one bitmap cache is created
- * for each VectorDrawableCompat. Therefore, referring to the same VectorDrawableCompat means
- * sharing the same bitmap cache. If these references don't agree upon on the same size, the bitmap
- * will be recreated and redrawn every time size is changed. In other words, if a VectorDrawable is
- * used for different sizes, it is more efficient to create multiple VectorDrawables, one for each
- * size.
- * <p/>
- * VectorDrawableCompat can be defined in an XML file with the <code>&lt;vector></code> element.
- * <p/>
- * The VectorDrawableCompat has the following elements:
- * <p/>
- * <dt><code>&lt;vector></code></dt>
- * <dl>
- * <dd>Used to define a vector drawable
- * <dl>
- * <dt><code>android:name</code></dt>
- * <dd>Defines the name of this vector drawable.</dd>
- * <dt><code>android:width</code></dt>
- * <dd>Used to define the intrinsic width of the drawable.
- * This support all the dimension units, normally specified with dp.</dd>
- * <dt><code>android:height</code></dt>
- * <dd>Used to define the intrinsic height the drawable.
- * This support all the dimension units, normally specified with dp.</dd>
- * <dt><code>android:viewportWidth</code></dt>
- * <dd>Used to define the width of the viewport space. Viewport is basically
- * the virtual canvas where the paths are drawn on.</dd>
- * <dt><code>android:viewportHeight</code></dt>
- * <dd>Used to define the height of the viewport space. Viewport is basically
- * the virtual canvas where the paths are drawn on.</dd>
- * <dt><code>android:tint</code></dt>
- * <dd>The color to apply to the drawable as a tint. By default, no tint is applied.</dd>
- * <dt><code>android:tintMode</code></dt>
- * <dd>The Porter-Duff blending mode for the tint color. Default is src_in.</dd>
- * <dt><code>android:autoMirrored</code></dt>
- * <dd>Indicates if the drawable needs to be mirrored when its layout direction is
- * RTL (right-to-left). Default is false.</dd>
- * <dt><code>android:alpha</code></dt>
- * <dd>The opacity of this drawable. Default is 1.</dd>
- * </dl></dd>
- * </dl>
- *
- * <dl>
- * <dt><code>&lt;group></code></dt>
- * <dd>Defines a group of paths or subgroups, plus transformation information.
- * The transformations are defined in the same coordinates as the viewport.
- * And the transformations are applied in the order of scale, rotate then translate.
- * <dl>
- * <dt><code>android:name</code></dt>
- * <dd>Defines the name of the group.</dd>
- * <dt><code>android:rotation</code></dt>
- * <dd>The degrees of rotation of the group. Default is 0.</dd>
- * <dt><code>android:pivotX</code></dt>
- * <dd>The X coordinate of the pivot for the scale and rotation of the group.
- * This is defined in the viewport space. Default is 0.</dd>
- * <dt><code>android:pivotY</code></dt>
- * <dd>The Y coordinate of the pivot for the scale and rotation of the group.
- * This is defined in the viewport space. Default is 0.</dd>
- * <dt><code>android:scaleX</code></dt>
- * <dd>The amount of scale on the X Coordinate. Default is 1.</dd>
- * <dt><code>android:scaleY</code></dt>
- * <dd>The amount of scale on the Y coordinate. Default is 1.</dd>
- * <dt><code>android:translateX</code></dt>
- * <dd>The amount of translation on the X coordinate.
- * This is defined in the viewport space. Default is 0.</dd>
- * <dt><code>android:translateY</code></dt>
- * <dd>The amount of translation on the Y coordinate.
- * This is defined in the viewport space. Default is 0.</dd>
- * </dl></dd>
- * </dl>
- *
- * <dl>
- * <dt><code>&lt;path></code></dt>
- * <dd>Defines paths to be drawn.
- * <dl>
- * <dt><code>android:name</code></dt>
- * <dd>Defines the name of the path.</dd>
- * <dt><code>android:pathData</code></dt>
- * <dd>Defines path data using exactly same format as "d" attribute
- * in the SVG's path data. This is defined in the viewport space.</dd>
- * <dt><code>android:fillColor</code></dt>
- * <dd>Specifies the color used to fill the path.
- * If this property is animated, any value set by the animation will override the original value.
- * No path fill is drawn if this property is not specified.</dd>
- * <dt><code>android:strokeColor</code></dt>
- * <dd>Specifies the color used to draw the path outline.
- * If this property is animated, any value set by the animation will override the original value.
- * No path outline is drawn if this property is not specified.</dd>
- * <dt><code>android:strokeWidth</code></dt>
- * <dd>The width a path stroke. Default is 0.</dd>
- * <dt><code>android:strokeAlpha</code></dt>
- * <dd>The opacity of a path stroke. Default is 1.</dd>
- * <dt><code>android:fillAlpha</code></dt>
- * <dd>The opacity to fill the path with. Default is 1.</dd>
- * <dt><code>android:trimPathStart</code></dt>
- * <dd>The fraction of the path to trim from the start, in the range from 0 to 1. Default is 0.</dd>
- * <dt><code>android:trimPathEnd</code></dt>
- * <dd>The fraction of the path to trim from the end, in the range from 0 to 1. Default is 1.</dd>
- * <dt><code>android:trimPathOffset</code></dt>
- * <dd>Shift trim region (allows showed region to include the start and end), in the range
- * from 0 to 1. Default is 0.</dd>
- * <dt><code>android:strokeLineCap</code></dt>
- * <dd>Sets the linecap for a stroked path: butt, round, square. Default is butt.</dd>
- * <dt><code>android:strokeLineJoin</code></dt>
- * <dd>Sets the lineJoin for a stroked path: miter,round,bevel. Default is miter.</dd>
- * <dt><code>android:strokeMiterLimit</code></dt>
- * <dd>Sets the Miter limit for a stroked path. Default is 4.</dd>
- * <dt><code>android:fillType</code></dt>
- * <dd>Sets the fillType for a path. The types can be either "evenOdd" or "nonZero". They behave the
- * same as SVG's "fill-rule" properties. Default is nonZero. For more details, see
- * <a href="https://www.w3.org/TR/SVG/painting.html#FillRuleProperty">FillRuleProperty</a></dd>
- * </dl></dd>
- * </dl>
- *
- * <dl>
- * <dt><code>&lt;clip-path></code></dt>
- * <dd>Defines path to be the current clip. Note that the clip path only apply to
- * the current group and its children.
- * <dl>
- * <dt><code>android:name</code></dt>
- * <dd>Defines the name of the clip path.</dd>
- * <dt><code>android:pathData</code></dt>
- * <dd>Defines clip path using the same format as "d" attribute
- * in the SVG's path data.</dd>
- * </dl></dd>
- * </dl>
- * <p/>
- * Note that theme attributes in XML file are supported through
- * <code>{@link #inflate(Resources, XmlPullParser, AttributeSet, Theme)}</code>.
- */
-public class VectorDrawableCompat extends VectorDrawableCommon {
-    static final String LOGTAG = "VectorDrawableCompat";
-
-    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
-
-    private static final String SHAPE_CLIP_PATH = "clip-path";
-    private static final String SHAPE_GROUP = "group";
-    private static final String SHAPE_PATH = "path";
-    private static final String SHAPE_VECTOR = "vector";
-
-    private static final int LINECAP_BUTT = 0;
-    private static final int LINECAP_ROUND = 1;
-    private static final int LINECAP_SQUARE = 2;
-
-    private static final int LINEJOIN_MITER = 0;
-    private static final int LINEJOIN_ROUND = 1;
-    private static final int LINEJOIN_BEVEL = 2;
-
-    // Cap the bitmap size, such that it won't hurt the performance too much
-    // and it won't crash due to a very large scale.
-    // The drawable will look blurry above this size.
-    private static final int MAX_CACHED_BITMAP_SIZE = 2048;
-
-    private static final boolean DBG_VECTOR_DRAWABLE = false;
-
-    private VectorDrawableCompatState mVectorState;
-
-    private PorterDuffColorFilter mTintFilter;
-    private ColorFilter mColorFilter;
-
-    private boolean mMutated;
-
-    // AnimatedVectorDrawable needs to turn off the cache all the time, otherwise,
-    // caching the bitmap by default is allowed.
-    private boolean mAllowCaching = true;
-
-    // The Constant state associated with the <code>mDelegateDrawable</code>.
-    private ConstantState mCachedConstantStateDelegate;
-
-    // Temp variable, only for saving "new" operation at the draw() time.
-    private final float[] mTmpFloats = new float[9];
-    private final Matrix mTmpMatrix = new Matrix();
-    private final Rect mTmpBounds = new Rect();
-
-    VectorDrawableCompat() {
-        mVectorState = new VectorDrawableCompatState();
-    }
-
-    VectorDrawableCompat(@NonNull VectorDrawableCompatState state) {
-        mVectorState = state;
-        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
-    }
-
-    @Override
-    public Drawable mutate() {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.mutate();
-            return this;
-        }
-
-        if (!mMutated && super.mutate() == this) {
-            mVectorState = new VectorDrawableCompatState(mVectorState);
-            mMutated = true;
-        }
-        return this;
-    }
-
-    Object getTargetByName(String name) {
-        return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
-    }
-
-    @Override
-    public ConstantState getConstantState() {
-        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
-            // Such that the configuration can be refreshed.
-            return new VectorDrawableDelegateState(mDelegateDrawable.getConstantState());
-        }
-        mVectorState.mChangingConfigurations = getChangingConfigurations();
-        return mVectorState;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.draw(canvas);
-            return;
-        }
-        // We will offset the bounds for drawBitmap, so copyBounds() here instead
-        // of getBounds().
-        copyBounds(mTmpBounds);
-        if (mTmpBounds.width() <= 0 || mTmpBounds.height() <= 0) {
-            // Nothing to draw
-            return;
-        }
-
-        // Color filters always override tint filters.
-        final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter);
-
-        // The imageView can scale the canvas in different ways, in order to
-        // avoid blurry scaling, we have to draw into a bitmap with exact pixel
-        // size first. This bitmap size is determined by the bounds and the
-        // canvas scale.
-        canvas.getMatrix(mTmpMatrix);
-        mTmpMatrix.getValues(mTmpFloats);
-        float canvasScaleX = Math.abs(mTmpFloats[Matrix.MSCALE_X]);
-        float canvasScaleY = Math.abs(mTmpFloats[Matrix.MSCALE_Y]);
-
-        float canvasSkewX = Math.abs(mTmpFloats[Matrix.MSKEW_X]);
-        float canvasSkewY = Math.abs(mTmpFloats[Matrix.MSKEW_Y]);
-
-        // When there is any rotation / skew, then the scale value is not valid.
-        if (canvasSkewX != 0 || canvasSkewY != 0) {
-            canvasScaleX = 1.0f;
-            canvasScaleY = 1.0f;
-        }
-
-        int scaledWidth = (int) (mTmpBounds.width() * canvasScaleX);
-        int scaledHeight = (int) (mTmpBounds.height() * canvasScaleY);
-        scaledWidth = Math.min(MAX_CACHED_BITMAP_SIZE, scaledWidth);
-        scaledHeight = Math.min(MAX_CACHED_BITMAP_SIZE, scaledHeight);
-
-        if (scaledWidth <= 0 || scaledHeight <= 0) {
-            return;
-        }
-
-        final int saveCount = canvas.save();
-        canvas.translate(mTmpBounds.left, mTmpBounds.top);
-
-        // Handle RTL mirroring.
-        final boolean needMirroring = needMirroring();
-        if (needMirroring) {
-            canvas.translate(mTmpBounds.width(), 0);
-            canvas.scale(-1.0f, 1.0f);
-        }
-
-        // At this point, canvas has been translated to the right position.
-        // And we use this bound for the destination rect for the drawBitmap, so
-        // we offset to (0, 0);
-        mTmpBounds.offsetTo(0, 0);
-
-        mVectorState.createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
-        if (!mAllowCaching) {
-            mVectorState.updateCachedBitmap(scaledWidth, scaledHeight);
-        } else {
-            if (!mVectorState.canReuseCache()) {
-                mVectorState.updateCachedBitmap(scaledWidth, scaledHeight);
-                mVectorState.updateCacheStates();
-            }
-        }
-        mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter, mTmpBounds);
-        canvas.restoreToCount(saveCount);
-    }
-
-    @Override
-    public int getAlpha() {
-        if (mDelegateDrawable != null) {
-            return DrawableCompat.getAlpha(mDelegateDrawable);
-        }
-
-        return mVectorState.mVPathRenderer.getRootAlpha();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setAlpha(alpha);
-            return;
-        }
-
-        if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) {
-            mVectorState.mVPathRenderer.setRootAlpha(alpha);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setColorFilter(colorFilter);
-            return;
-        }
-
-        mColorFilter = colorFilter;
-        invalidateSelf();
-    }
-
-    /**
-     * Ensures the tint filter is consistent with the current tint color and
-     * mode.
-     */
-    PorterDuffColorFilter updateTintFilter(PorterDuffColorFilter tintFilter, ColorStateList tint,
-                                           PorterDuff.Mode tintMode) {
-        if (tint == null || tintMode == null) {
-            return null;
-        }
-        // setMode, setColor of PorterDuffColorFilter are not public method in SDK v7.
-        // Therefore we create a new one all the time here. Don't expect this is called often.
-        final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
-        return new PorterDuffColorFilter(color, tintMode);
-    }
-
-    @Override
-    public void setTint(int tint) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setTint(mDelegateDrawable, tint);
-            return;
-        }
-
-        setTintList(ColorStateList.valueOf(tint));
-    }
-
-    @Override
-    public void setTintList(ColorStateList tint) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setTintList(mDelegateDrawable, tint);
-            return;
-        }
-
-        final VectorDrawableCompatState state = mVectorState;
-        if (state.mTint != tint) {
-            state.mTint = tint;
-            mTintFilter = updateTintFilter(mTintFilter, tint, state.mTintMode);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void setTintMode(Mode tintMode) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setTintMode(mDelegateDrawable, tintMode);
-            return;
-        }
-
-        final VectorDrawableCompatState state = mVectorState;
-        if (state.mTintMode != tintMode) {
-            state.mTintMode = tintMode;
-            mTintFilter = updateTintFilter(mTintFilter, state.mTint, tintMode);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public boolean isStateful() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.isStateful();
-        }
-
-        return super.isStateful() || (mVectorState != null && mVectorState.mTint != null
-                && mVectorState.mTint.isStateful());
-    }
-
-    @Override
-    protected boolean onStateChange(int[] stateSet) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setState(stateSet);
-        }
-
-        final VectorDrawableCompatState state = mVectorState;
-        if (state.mTint != null && state.mTintMode != null) {
-            mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
-            invalidateSelf();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public int getOpacity() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getOpacity();
-        }
-
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getIntrinsicWidth();
-        }
-
-        return (int) mVectorState.mVPathRenderer.mBaseWidth;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getIntrinsicHeight();
-        }
-
-        return (int) mVectorState.mVPathRenderer.mBaseHeight;
-    }
-
-    // Don't support re-applying themes. The initial theme loading is working.
-    @Override
-    public boolean canApplyTheme() {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.canApplyTheme(mDelegateDrawable);
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean isAutoMirrored() {
-        if (mDelegateDrawable != null) {
-            return DrawableCompat.isAutoMirrored(mDelegateDrawable);
-        }
-        return mVectorState.mAutoMirrored;
-    }
-
-    @Override
-    public void setAutoMirrored(boolean mirrored) {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.setAutoMirrored(mDelegateDrawable, mirrored);
-            return;
-        }
-        mVectorState.mAutoMirrored = mirrored;
-    }
-    /**
-     * The size of a pixel when scaled from the intrinsic dimension to the viewport dimension. This
-     * is used to calculate the path animation accuracy.
-     *
-     * @hide
-     */
-    @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) {
-            return 1; // fall back to 1:1 pixel mapping.
-        }
-        float intrinsicWidth = mVectorState.mVPathRenderer.mBaseWidth;
-        float intrinsicHeight = mVectorState.mVPathRenderer.mBaseHeight;
-        float viewportWidth = mVectorState.mVPathRenderer.mViewportWidth;
-        float viewportHeight = mVectorState.mVPathRenderer.mViewportHeight;
-        float scaleX = viewportWidth / intrinsicWidth;
-        float scaleY = viewportHeight / intrinsicHeight;
-        return Math.min(scaleX, scaleY);
-    }
-
-    /**
-     * Create a VectorDrawableCompat object.
-     *
-     * @param res   the resources.
-     * @param resId the resource ID for VectorDrawableCompat object.
-     * @param theme the theme of this vector drawable, it can be null.
-     * @return a new VectorDrawableCompat or null if parsing error is found.
-     */
-    @Nullable
-    public static VectorDrawableCompat create(@NonNull Resources res, @DrawableRes int resId,
-                                              @Nullable Theme theme) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            final VectorDrawableCompat drawable = new VectorDrawableCompat();
-            drawable.mDelegateDrawable = ResourcesCompat.getDrawable(res, resId, theme);
-            drawable.mCachedConstantStateDelegate = new VectorDrawableDelegateState(
-                    drawable.mDelegateDrawable.getConstantState());
-            return drawable;
-        }
-
-        try {
-            @SuppressLint("ResourceType") final XmlPullParser parser = res.getXml(resId);
-            final AttributeSet attrs = Xml.asAttributeSet(parser);
-            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 createFromXmlInner(res, parser, attrs, theme);
-        } catch (XmlPullParserException e) {
-            Log.e(LOGTAG, "parser error", e);
-        } catch (IOException e) {
-            Log.e(LOGTAG, "parser error", e);
-        }
-        return null;
-    }
-
-    /**
-     * Create a VectorDrawableCompat from inside an XML document using an optional
-     * {@link Theme}. Called on a parser positioned at a tag in an XML
-     * document, tries to create a Drawable from that tag. Returns {@code null}
-     * if the tag is not a valid drawable.
-     */
-    public static VectorDrawableCompat createFromXmlInner(Resources r, XmlPullParser parser,
-            AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
-        final VectorDrawableCompat drawable = new VectorDrawableCompat();
-        drawable.inflate(r, parser, attrs, theme);
-        return drawable;
-    }
-
-    static int applyAlpha(int color, float alpha) {
-        int alphaBytes = Color.alpha(color);
-        color &= 0x00FFFFFF;
-        color |= ((int) (alphaBytes * alpha)) << 24;
-        return color;
-    }
-
-    @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
-            throws XmlPullParserException, IOException {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.inflate(res, parser, attrs);
-            return;
-        }
-
-        inflate(res, parser, attrs, null);
-    }
-
-    @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
-            throws XmlPullParserException, IOException {
-        if (mDelegateDrawable != null) {
-            DrawableCompat.inflate(mDelegateDrawable, res, parser, attrs, theme);
-            return;
-        }
-
-        final VectorDrawableCompatState state = mVectorState;
-        final VPathRenderer pathRenderer = new VPathRenderer();
-        state.mVPathRenderer = pathRenderer;
-
-        final TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_TYPE_ARRAY);
-
-        updateStateFromTypedArray(a, parser);
-        a.recycle();
-        state.mChangingConfigurations = getChangingConfigurations();
-        state.mCacheDirty = true;
-        inflateInternal(res, parser, attrs, theme);
-
-        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
-    }
-
-
-    /**
-     * Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
-     * attribute's enum value.
-     */
-    private static PorterDuff.Mode parseTintModeCompat(int value, Mode defaultMode) {
-        switch (value) {
-            case 3:
-                return Mode.SRC_OVER;
-            case 5:
-                return Mode.SRC_IN;
-            case 9:
-                return Mode.SRC_ATOP;
-            case 14:
-                return Mode.MULTIPLY;
-            case 15:
-                return Mode.SCREEN;
-            case 16:
-                if (Build.VERSION.SDK_INT >= 11) {
-                    return Mode.ADD;
-                } else {
-                    return defaultMode;
-                }
-            default:
-                return defaultMode;
-        }
-    }
-
-    private void updateStateFromTypedArray(TypedArray a, XmlPullParser parser)
-            throws XmlPullParserException {
-        final VectorDrawableCompatState state = mVectorState;
-        final VPathRenderer pathRenderer = state.mVPathRenderer;
-
-        // Account for any configuration changes.
-        // state.mChangingConfigurations |= Utils.getChangingConfigurations(a);
-
-        final int mode = TypedArrayUtils.getNamedInt(a, parser, "tintMode",
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_TINT_MODE, -1);
-        state.mTintMode = parseTintModeCompat(mode, Mode.SRC_IN);
-
-        final ColorStateList tint =
-                a.getColorStateList(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_TINT);
-        if (tint != null) {
-            state.mTint = tint;
-        }
-
-        state.mAutoMirrored = TypedArrayUtils.getNamedBoolean(a, parser, "autoMirrored",
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_AUTO_MIRRORED, state.mAutoMirrored);
-
-        pathRenderer.mViewportWidth = TypedArrayUtils.getNamedFloat(a, parser, "viewportWidth",
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_WIDTH,
-                pathRenderer.mViewportWidth);
-
-        pathRenderer.mViewportHeight = TypedArrayUtils.getNamedFloat(a, parser, "viewportHeight",
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_VIEWPORT_HEIGHT,
-                pathRenderer.mViewportHeight);
-
-        if (pathRenderer.mViewportWidth <= 0) {
-            throw new XmlPullParserException(a.getPositionDescription() +
-                    "<vector> tag requires viewportWidth > 0");
-        } else if (pathRenderer.mViewportHeight <= 0) {
-            throw new XmlPullParserException(a.getPositionDescription() +
-                    "<vector> tag requires viewportHeight > 0");
-        }
-
-        pathRenderer.mBaseWidth = a.getDimension(
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_WIDTH, pathRenderer.mBaseWidth);
-        pathRenderer.mBaseHeight = a.getDimension(
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_HEIGHT, pathRenderer.mBaseHeight);
-        if (pathRenderer.mBaseWidth <= 0) {
-            throw new XmlPullParserException(a.getPositionDescription() +
-                    "<vector> tag requires width > 0");
-        } else if (pathRenderer.mBaseHeight <= 0) {
-            throw new XmlPullParserException(a.getPositionDescription() +
-                    "<vector> tag requires height > 0");
-        }
-
-        // shown up from API 11.
-        final float alphaInFloat = TypedArrayUtils.getNamedFloat(a, parser, "alpha",
-                AndroidResources.STYLEABLE_VECTOR_DRAWABLE_ALPHA, pathRenderer.getAlpha());
-        pathRenderer.setAlpha(alphaInFloat);
-
-        final String name = a.getString(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_NAME);
-        if (name != null) {
-            pathRenderer.mRootName = name;
-            pathRenderer.mVGTargetsMap.put(name, pathRenderer);
-        }
-    }
-
-    private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
-                                 Theme theme) throws XmlPullParserException, IOException {
-        final VectorDrawableCompatState state = mVectorState;
-        final VPathRenderer pathRenderer = state.mVPathRenderer;
-        boolean noPathTag = true;
-
-        // Use a stack to help to build the group tree.
-        // The top of the stack is always the current group.
-        final ArrayDeque<VGroup> groupStack = new ArrayDeque<>();
-        groupStack.push(pathRenderer.mRootGroup);
-
-        int eventType = parser.getEventType();
-        final int innerDepth = parser.getDepth() + 1;
-
-        // Parse everything until the end of the vector element.
-        while (eventType != XmlPullParser.END_DOCUMENT
-                && (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)) {
-            if (eventType == XmlPullParser.START_TAG) {
-                final String tagName = parser.getName();
-                final VGroup currentGroup = groupStack.peek();
-                if (SHAPE_PATH.equals(tagName)) {
-                    final VFullPath path = new VFullPath();
-                    path.inflate(res, attrs, theme, parser);
-                    currentGroup.mChildren.add(path);
-                    if (path.getPathName() != null) {
-                        pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
-                    }
-                    noPathTag = false;
-                    state.mChangingConfigurations |= path.mChangingConfigurations;
-                } else if (SHAPE_CLIP_PATH.equals(tagName)) {
-                    final VClipPath path = new VClipPath();
-                    path.inflate(res, attrs, theme, parser);
-                    currentGroup.mChildren.add(path);
-                    if (path.getPathName() != null) {
-                        pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
-                    }
-                    state.mChangingConfigurations |= path.mChangingConfigurations;
-                } else if (SHAPE_GROUP.equals(tagName)) {
-                    VGroup newChildGroup = new VGroup();
-                    newChildGroup.inflate(res, attrs, theme, parser);
-                    currentGroup.mChildren.add(newChildGroup);
-                    groupStack.push(newChildGroup);
-                    if (newChildGroup.getGroupName() != null) {
-                        pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
-                                newChildGroup);
-                    }
-                    state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
-                }
-            } else if (eventType == XmlPullParser.END_TAG) {
-                final String tagName = parser.getName();
-                if (SHAPE_GROUP.equals(tagName)) {
-                    groupStack.pop();
-                }
-            }
-            eventType = parser.next();
-        }
-
-        // Print the tree out for debug.
-        if (DBG_VECTOR_DRAWABLE) {
-            printGroupTree(pathRenderer.mRootGroup, 0);
-        }
-
-        if (noPathTag) {
-            throw new XmlPullParserException("no " + SHAPE_PATH + " defined");
-        }
-    }
-
-    private void printGroupTree(VGroup currentGroup, int level) {
-        String indent = "";
-        for (int i = 0; i < level; i++) {
-            indent += "    ";
-        }
-        // Print the current node
-        Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName()
-                + " rotation is " + currentGroup.mRotate);
-        Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
-        // Then print all the children groups
-        for (int i = 0; i < currentGroup.mChildren.size(); i++) {
-            Object child = currentGroup.mChildren.get(i);
-            if (child instanceof VGroup) {
-                printGroupTree((VGroup) child, level + 1);
-            } else {
-                ((VPath) child).printVPath(level + 1);
-            }
-        }
-    }
-
-    void setAllowCaching(boolean allowCaching) {
-        mAllowCaching = allowCaching;
-    }
-
-    // We don't support RTL auto mirroring since the getLayoutDirection() is for API 17+.
-    private boolean needMirroring() {
-        if (Build.VERSION.SDK_INT >= 17) {
-            return isAutoMirrored()
-                    && DrawableCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
-        } else {
-            return false;
-        }
-    }
-
-    // Extra override functions for delegation for SDK >= 7.
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.setBounds(bounds);
-        }
-    }
-
-    @Override
-    public int getChangingConfigurations() {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.getChangingConfigurations();
-        }
-        return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
-    }
-
-    @Override
-    public void invalidateSelf() {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.invalidateSelf();
-            return;
-        }
-        super.invalidateSelf();
-    }
-
-    @Override
-    public void scheduleSelf(Runnable what, long when) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.scheduleSelf(what, when);
-            return;
-        }
-        super.scheduleSelf(what, when);
-    }
-
-    @Override
-    public boolean setVisible(boolean visible, boolean restart) {
-        if (mDelegateDrawable != null) {
-            return mDelegateDrawable.setVisible(visible, restart);
-        }
-        return super.setVisible(visible, restart);
-    }
-
-    @Override
-    public void unscheduleSelf(Runnable what) {
-        if (mDelegateDrawable != null) {
-            mDelegateDrawable.unscheduleSelf(what);
-            return;
-        }
-        super.unscheduleSelf(what);
-    }
-
-    /**
-     * Constant state for delegating the creating drawable job for SDK >= 24.
-     * 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;
-
-        public VectorDrawableDelegateState(ConstantState state) {
-            mDelegateState = state;
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            VectorDrawableCompat drawableCompat = new VectorDrawableCompat();
-            drawableCompat.mDelegateDrawable = (VectorDrawable) mDelegateState.newDrawable();
-            return drawableCompat;
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res) {
-            VectorDrawableCompat drawableCompat = new VectorDrawableCompat();
-            drawableCompat.mDelegateDrawable = (VectorDrawable) mDelegateState.newDrawable(res);
-            return drawableCompat;
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res, Theme theme) {
-            VectorDrawableCompat drawableCompat = new VectorDrawableCompat();
-            drawableCompat.mDelegateDrawable =
-                    (VectorDrawable) mDelegateState.newDrawable(res, theme);
-            return drawableCompat;
-        }
-
-        @Override
-        public boolean canApplyTheme() {
-            return mDelegateState.canApplyTheme();
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return mDelegateState.getChangingConfigurations();
-        }
-    }
-
-    private static class VectorDrawableCompatState extends ConstantState {
-        int mChangingConfigurations;
-        VPathRenderer mVPathRenderer;
-        ColorStateList mTint = null;
-        Mode mTintMode = DEFAULT_TINT_MODE;
-        boolean mAutoMirrored;
-
-        Bitmap mCachedBitmap;
-        int[] mCachedThemeAttrs;
-        ColorStateList mCachedTint;
-        Mode mCachedTintMode;
-        int mCachedRootAlpha;
-        boolean mCachedAutoMirrored;
-        boolean mCacheDirty;
-
-        /**
-         * Temporary paint object used to draw cached bitmaps.
-         */
-        Paint mTempPaint;
-
-        // Deep copy for mutate() or implicitly mutate.
-        public VectorDrawableCompatState(VectorDrawableCompatState copy) {
-            if (copy != null) {
-                mChangingConfigurations = copy.mChangingConfigurations;
-                mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
-                if (copy.mVPathRenderer.mFillPaint != null) {
-                    mVPathRenderer.mFillPaint = new Paint(copy.mVPathRenderer.mFillPaint);
-                }
-                if (copy.mVPathRenderer.mStrokePaint != null) {
-                    mVPathRenderer.mStrokePaint = new Paint(copy.mVPathRenderer.mStrokePaint);
-                }
-                mTint = copy.mTint;
-                mTintMode = copy.mTintMode;
-                mAutoMirrored = copy.mAutoMirrored;
-            }
-        }
-
-        public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter,
-                                                  Rect originalBounds) {
-            // The bitmap's size is the same as the bounds.
-            final Paint p = getPaint(filter);
-            canvas.drawBitmap(mCachedBitmap, null, originalBounds, p);
-        }
-
-        public boolean hasTranslucentRoot() {
-            return mVPathRenderer.getRootAlpha() < 255;
-        }
-
-        /**
-         * @return null when there is no need for alpha paint.
-         */
-        public Paint getPaint(ColorFilter filter) {
-            if (!hasTranslucentRoot() && filter == null) {
-                return null;
-            }
-
-            if (mTempPaint == null) {
-                mTempPaint = new Paint();
-                mTempPaint.setFilterBitmap(true);
-            }
-            mTempPaint.setAlpha(mVPathRenderer.getRootAlpha());
-            mTempPaint.setColorFilter(filter);
-            return mTempPaint;
-        }
-
-        public void updateCachedBitmap(int width, int height) {
-            mCachedBitmap.eraseColor(Color.TRANSPARENT);
-            Canvas tmpCanvas = new Canvas(mCachedBitmap);
-            mVPathRenderer.draw(tmpCanvas, width, height, null);
-        }
-
-        public void createCachedBitmapIfNeeded(int width, int height) {
-            if (mCachedBitmap == null || !canReuseBitmap(width, height)) {
-                mCachedBitmap = Bitmap.createBitmap(width, height,
-                        Bitmap.Config.ARGB_8888);
-                mCacheDirty = true;
-            }
-
-        }
-
-        public boolean canReuseBitmap(int width, int height) {
-            if (width == mCachedBitmap.getWidth()
-                    && height == mCachedBitmap.getHeight()) {
-                return true;
-            }
-            return false;
-        }
-
-        public boolean canReuseCache() {
-            if (!mCacheDirty
-                    && mCachedTint == mTint
-                    && mCachedTintMode == mTintMode
-                    && mCachedAutoMirrored == mAutoMirrored
-                    && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
-                return true;
-            }
-            return false;
-        }
-
-        public void updateCacheStates() {
-            // Use shallow copy here and shallow comparison in canReuseCache(),
-            // likely hit cache miss more, but practically not much difference.
-            mCachedTint = mTint;
-            mCachedTintMode = mTintMode;
-            mCachedRootAlpha = mVPathRenderer.getRootAlpha();
-            mCachedAutoMirrored = mAutoMirrored;
-            mCacheDirty = false;
-        }
-
-        public VectorDrawableCompatState() {
-            mVPathRenderer = new VPathRenderer();
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            return new VectorDrawableCompat(this);
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res) {
-            return new VectorDrawableCompat(this);
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return mChangingConfigurations;
-        }
-    }
-
-    private static class VPathRenderer {
-        /* Right now the internal data structure is organized as a tree.
-         * Each node can be a group node, or a path.
-         * A group node can have groups or paths as children, but a path node has
-         * no children.
-         * One example can be:
-         *                 Root Group
-         *                /    |     \
-         *           Group    Path    Group
-         *          /     \             |
-         *         Path   Path         Path
-         *
-         */
-        // Variables that only used temporarily inside the draw() call, so there
-        // is no need for deep copying.
-        private final Path mPath;
-        private final Path mRenderPath;
-        private static final Matrix IDENTITY_MATRIX = new Matrix();
-        private final Matrix mFinalPathMatrix = new Matrix();
-
-        private Paint mStrokePaint;
-        private Paint mFillPaint;
-        private PathMeasure mPathMeasure;
-
-        /////////////////////////////////////////////////////
-        // Variables below need to be copied (deep copy if applicable) for mutation.
-        private int mChangingConfigurations;
-        final VGroup mRootGroup;
-        float mBaseWidth = 0;
-        float mBaseHeight = 0;
-        float mViewportWidth = 0;
-        float mViewportHeight = 0;
-        int mRootAlpha = 0xFF;
-        String mRootName = null;
-
-        final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
-
-        public VPathRenderer() {
-            mRootGroup = new VGroup();
-            mPath = new Path();
-            mRenderPath = new Path();
-        }
-
-        public void setRootAlpha(int alpha) {
-            mRootAlpha = alpha;
-        }
-
-        public int getRootAlpha() {
-            return mRootAlpha;
-        }
-
-        // setAlpha() and getAlpha() are used mostly for animation purpose, since
-        // Animator like to use alpha from 0 to 1.
-        public void setAlpha(float alpha) {
-            setRootAlpha((int) (alpha * 255));
-        }
-
-        @SuppressWarnings("unused")
-        public float getAlpha() {
-            return getRootAlpha() / 255.0f;
-        }
-
-        public VPathRenderer(VPathRenderer copy) {
-            mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
-            mPath = new Path(copy.mPath);
-            mRenderPath = new Path(copy.mRenderPath);
-            mBaseWidth = copy.mBaseWidth;
-            mBaseHeight = copy.mBaseHeight;
-            mViewportWidth = copy.mViewportWidth;
-            mViewportHeight = copy.mViewportHeight;
-            mChangingConfigurations = copy.mChangingConfigurations;
-            mRootAlpha = copy.mRootAlpha;
-            mRootName = copy.mRootName;
-            if (copy.mRootName != null) {
-                mVGTargetsMap.put(copy.mRootName, this);
-            }
-        }
-
-        private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix,
-                                   Canvas canvas, int w, int h, ColorFilter filter) {
-            // Calculate current group's matrix by preConcat the parent's and
-            // and the current one on the top of the stack.
-            // Basically the Mfinal = Mviewport * M0 * M1 * M2;
-            // Mi the local matrix at level i of the group tree.
-            currentGroup.mStackedMatrix.set(currentMatrix);
-
-            currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
-
-            // Save the current clip information, which is local to this group.
-            canvas.save();
-
-            // Draw the group tree in the same order as the XML file.
-            for (int i = 0; i < currentGroup.mChildren.size(); i++) {
-                Object child = currentGroup.mChildren.get(i);
-                if (child instanceof VGroup) {
-                    VGroup childGroup = (VGroup) child;
-                    drawGroupTree(childGroup, currentGroup.mStackedMatrix,
-                            canvas, w, h, filter);
-                } else if (child instanceof VPath) {
-                    VPath childPath = (VPath) child;
-                    drawPath(currentGroup, childPath, canvas, w, h, filter);
-                }
-            }
-
-            canvas.restore();
-        }
-
-        public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
-            // Traverse the tree in pre-order to draw.
-            drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h, filter);
-        }
-
-        private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h,
-                              ColorFilter filter) {
-            final float scaleX = w / mViewportWidth;
-            final float scaleY = h / mViewportHeight;
-            final float minScale = Math.min(scaleX, scaleY);
-            final Matrix groupStackedMatrix = vGroup.mStackedMatrix;
-
-            mFinalPathMatrix.set(groupStackedMatrix);
-            mFinalPathMatrix.postScale(scaleX, scaleY);
-
-
-            final float matrixScale = getMatrixScale(groupStackedMatrix);
-            if (matrixScale == 0) {
-                // When either x or y is scaled to 0, we don't need to draw anything.
-                return;
-            }
-            vPath.toPath(mPath);
-            final Path path = mPath;
-
-            mRenderPath.reset();
-
-            if (vPath.isClipPath()) {
-                mRenderPath.addPath(path, mFinalPathMatrix);
-                canvas.clipPath(mRenderPath);
-            } else {
-                VFullPath fullPath = (VFullPath) vPath;
-                if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) {
-                    float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f;
-                    float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f;
-
-                    if (mPathMeasure == null) {
-                        mPathMeasure = new PathMeasure();
-                    }
-                    mPathMeasure.setPath(mPath, false);
-
-                    float len = mPathMeasure.getLength();
-                    start = start * len;
-                    end = end * len;
-                    path.reset();
-                    if (start > end) {
-                        mPathMeasure.getSegment(start, len, path, true);
-                        mPathMeasure.getSegment(0f, end, path, true);
-                    } else {
-                        mPathMeasure.getSegment(start, end, path, true);
-                    }
-                    path.rLineTo(0, 0); // fix bug in measure
-                }
-                mRenderPath.addPath(path, mFinalPathMatrix);
-
-                if (fullPath.mFillColor != Color.TRANSPARENT) {
-                    if (mFillPaint == null) {
-                        mFillPaint = new Paint();
-                        mFillPaint.setStyle(Paint.Style.FILL);
-                        mFillPaint.setAntiAlias(true);
-                    }
-
-                    final Paint fillPaint = mFillPaint;
-                    fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha));
-                    fillPaint.setColorFilter(filter);
-                    mRenderPath.setFillType(fullPath.mFillRule == 0 ? Path.FillType.WINDING
-                            : Path.FillType.EVEN_ODD);
-                    canvas.drawPath(mRenderPath, fillPaint);
-                }
-
-                if (fullPath.mStrokeColor != Color.TRANSPARENT) {
-                    if (mStrokePaint == null) {
-                        mStrokePaint = new Paint();
-                        mStrokePaint.setStyle(Paint.Style.STROKE);
-                        mStrokePaint.setAntiAlias(true);
-                    }
-
-                    final Paint strokePaint = mStrokePaint;
-                    if (fullPath.mStrokeLineJoin != null) {
-                        strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin);
-                    }
-
-                    if (fullPath.mStrokeLineCap != null) {
-                        strokePaint.setStrokeCap(fullPath.mStrokeLineCap);
-                    }
-
-                    strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit);
-                    strokePaint.setColor(applyAlpha(fullPath.mStrokeColor, fullPath.mStrokeAlpha));
-                    strokePaint.setColorFilter(filter);
-                    final float finalStrokeScale = minScale * matrixScale;
-                    strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
-                    canvas.drawPath(mRenderPath, strokePaint);
-                }
-            }
-        }
-
-        private static float cross(float v1x, float v1y, float v2x, float v2y) {
-            return v1x * v2y - v1y * v2x;
-        }
-
-        private float getMatrixScale(Matrix groupStackedMatrix) {
-            // Given unit vectors A = (0, 1) and B = (1, 0).
-            // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
-            // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
-            // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
-            // If  max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
-            //
-            // For non-skew case, which is most of the cases, matrix scale is computing exactly the
-            // scale on x and y axis, and take the minimal of these two.
-            // For skew case, an unit square will mapped to a parallelogram. And this function will
-            // return the minimal height of the 2 bases.
-            float[] unitVectors = new float[]{0, 1, 1, 0};
-            groupStackedMatrix.mapVectors(unitVectors);
-            float scaleX = (float) Math.hypot(unitVectors[0], unitVectors[1]);
-            float scaleY = (float) Math.hypot(unitVectors[2], unitVectors[3]);
-            float crossProduct = cross(unitVectors[0], unitVectors[1], unitVectors[2],
-                    unitVectors[3]);
-            float maxScale = Math.max(scaleX, scaleY);
-
-            float matrixScale = 0;
-            if (maxScale > 0) {
-                matrixScale = Math.abs(crossProduct) / maxScale;
-            }
-            if (DBG_VECTOR_DRAWABLE) {
-                Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale);
-            }
-            return matrixScale;
-        }
-    }
-
-    private static class VGroup {
-        // mStackedMatrix is only used temporarily when drawing, it combines all
-        // the parents' local matrices with the current one.
-        private final Matrix mStackedMatrix = new Matrix();
-
-        /////////////////////////////////////////////////////
-        // Variables below need to be copied (deep copy if applicable) for mutation.
-        final ArrayList<Object> mChildren = new ArrayList<Object>();
-
-        float mRotate = 0;
-        private float mPivotX = 0;
-        private float mPivotY = 0;
-        private float mScaleX = 1;
-        private float mScaleY = 1;
-        private float mTranslateX = 0;
-        private float mTranslateY = 0;
-
-        // mLocalMatrix is updated based on the update of transformation information,
-        // either parsed from the XML or by animation.
-        private final Matrix mLocalMatrix = new Matrix();
-        int mChangingConfigurations;
-        private int[] mThemeAttrs;
-        private String mGroupName = null;
-
-        public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) {
-            mRotate = copy.mRotate;
-            mPivotX = copy.mPivotX;
-            mPivotY = copy.mPivotY;
-            mScaleX = copy.mScaleX;
-            mScaleY = copy.mScaleY;
-            mTranslateX = copy.mTranslateX;
-            mTranslateY = copy.mTranslateY;
-            mThemeAttrs = copy.mThemeAttrs;
-            mGroupName = copy.mGroupName;
-            mChangingConfigurations = copy.mChangingConfigurations;
-            if (mGroupName != null) {
-                targetsMap.put(mGroupName, this);
-            }
-
-            mLocalMatrix.set(copy.mLocalMatrix);
-
-            final ArrayList<Object> children = copy.mChildren;
-            for (int i = 0; i < children.size(); i++) {
-                Object copyChild = children.get(i);
-                if (copyChild instanceof VGroup) {
-                    VGroup copyGroup = (VGroup) copyChild;
-                    mChildren.add(new VGroup(copyGroup, targetsMap));
-                } else {
-                    VPath newPath = null;
-                    if (copyChild instanceof VFullPath) {
-                        newPath = new VFullPath((VFullPath) copyChild);
-                    } else if (copyChild instanceof VClipPath) {
-                        newPath = new VClipPath((VClipPath) copyChild);
-                    } else {
-                        throw new IllegalStateException("Unknown object in the tree!");
-                    }
-                    mChildren.add(newPath);
-                    if (newPath.mPathName != null) {
-                        targetsMap.put(newPath.mPathName, newPath);
-                    }
-                }
-            }
-        }
-
-        public VGroup() {
-        }
-
-        public String getGroupName() {
-            return mGroupName;
-        }
-
-        public Matrix getLocalMatrix() {
-            return mLocalMatrix;
-        }
-
-        public void inflate(Resources res, AttributeSet attrs, Theme theme, XmlPullParser parser) {
-            final TypedArray a = TypedArrayUtils.obtainAttributes(res, theme, attrs,
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP);
-            updateStateFromTypedArray(a, parser);
-            a.recycle();
-        }
-
-        private void updateStateFromTypedArray(TypedArray a, XmlPullParser parser) {
-            // Account for any configuration changes.
-            // mChangingConfigurations |= Utils.getChangingConfigurations(a);
-
-            // Extract the theme attributes, if any.
-            mThemeAttrs = null; // TODO TINT THEME Not supported yet a.extractThemeAttrs();
-
-            // This is added in API 11
-            mRotate = TypedArrayUtils.getNamedFloat(a, parser, "rotation",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_ROTATION, mRotate);
-
-            mPivotX = a.getFloat(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_X, mPivotX);
-            mPivotY = a.getFloat(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_PIVOT_Y, mPivotY);
-
-            // This is added in API 11
-            mScaleX = TypedArrayUtils.getNamedFloat(a, parser, "scaleX",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_X, mScaleX);
-
-            // This is added in API 11
-            mScaleY = TypedArrayUtils.getNamedFloat(a, parser, "scaleY",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_SCALE_Y, mScaleY);
-
-            mTranslateX = TypedArrayUtils.getNamedFloat(a, parser, "translateX",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_X, mTranslateX);
-            mTranslateY = TypedArrayUtils.getNamedFloat(a, parser, "translateY",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_TRANSLATE_Y, mTranslateY);
-
-            final String groupName =
-                    a.getString(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_GROUP_NAME);
-            if (groupName != null) {
-                mGroupName = groupName;
-            }
-
-            updateLocalMatrix();
-        }
-
-        private void updateLocalMatrix() {
-            // The order we apply is the same as the
-            // RenderNode.cpp::applyViewPropertyTransforms().
-            mLocalMatrix.reset();
-            mLocalMatrix.postTranslate(-mPivotX, -mPivotY);
-            mLocalMatrix.postScale(mScaleX, mScaleY);
-            mLocalMatrix.postRotate(mRotate, 0, 0);
-            mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
-        }
-
-        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
-        @SuppressWarnings("unused")
-        public float getRotation() {
-            return mRotate;
-        }
-
-        @SuppressWarnings("unused")
-        public void setRotation(float rotation) {
-            if (rotation != mRotate) {
-                mRotate = rotation;
-                updateLocalMatrix();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        public float getPivotX() {
-            return mPivotX;
-        }
-
-        @SuppressWarnings("unused")
-        public void setPivotX(float pivotX) {
-            if (pivotX != mPivotX) {
-                mPivotX = pivotX;
-                updateLocalMatrix();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        public float getPivotY() {
-            return mPivotY;
-        }
-
-        @SuppressWarnings("unused")
-        public void setPivotY(float pivotY) {
-            if (pivotY != mPivotY) {
-                mPivotY = pivotY;
-                updateLocalMatrix();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        public float getScaleX() {
-            return mScaleX;
-        }
-
-        @SuppressWarnings("unused")
-        public void setScaleX(float scaleX) {
-            if (scaleX != mScaleX) {
-                mScaleX = scaleX;
-                updateLocalMatrix();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        public float getScaleY() {
-            return mScaleY;
-        }
-
-        @SuppressWarnings("unused")
-        public void setScaleY(float scaleY) {
-            if (scaleY != mScaleY) {
-                mScaleY = scaleY;
-                updateLocalMatrix();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        public float getTranslateX() {
-            return mTranslateX;
-        }
-
-        @SuppressWarnings("unused")
-        public void setTranslateX(float translateX) {
-            if (translateX != mTranslateX) {
-                mTranslateX = translateX;
-                updateLocalMatrix();
-            }
-        }
-
-        @SuppressWarnings("unused")
-        public float getTranslateY() {
-            return mTranslateY;
-        }
-
-        @SuppressWarnings("unused")
-        public void setTranslateY(float translateY) {
-            if (translateY != mTranslateY) {
-                mTranslateY = translateY;
-                updateLocalMatrix();
-            }
-        }
-    }
-
-    /**
-     * Common Path information for clip path and normal path.
-     */
-    private static class VPath {
-        protected PathParser.PathDataNode[] mNodes = null;
-        String mPathName;
-        int mChangingConfigurations;
-
-        public VPath() {
-            // Empty constructor.
-        }
-
-        public void printVPath(int level) {
-            String indent = "";
-            for (int i = 0; i < level; i++) {
-                indent += "    ";
-            }
-            Log.v(LOGTAG, indent + "current path is :" + mPathName +
-                    " pathData is " + nodesToString(mNodes));
-
-        }
-
-        public String nodesToString(PathParser.PathDataNode[] nodes) {
-            String result = " ";
-            for (int i = 0; i < nodes.length; i++) {
-                result += nodes[i].mType + ":";
-                float[] params = nodes[i].mParams;
-                for (int j = 0; j < params.length; j++) {
-                    result += params[j] + ",";
-                }
-            }
-            return result;
-        }
-
-        public VPath(VPath copy) {
-            mPathName = copy.mPathName;
-            mChangingConfigurations = copy.mChangingConfigurations;
-            mNodes = PathParser.deepCopyNodes(copy.mNodes);
-        }
-
-        public void toPath(Path path) {
-            path.reset();
-            if (mNodes != null) {
-                PathParser.PathDataNode.nodesToPath(mNodes, path);
-            }
-        }
-
-        public String getPathName() {
-            return mPathName;
-        }
-
-        public boolean canApplyTheme() {
-            return false;
-        }
-
-        public void applyTheme(Theme t) {
-        }
-
-        public boolean isClipPath() {
-            return false;
-        }
-
-        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
-        @SuppressWarnings("unused")
-        public PathParser.PathDataNode[] getPathData() {
-            return mNodes;
-        }
-
-        @SuppressWarnings("unused")
-        public void setPathData(PathParser.PathDataNode[] nodes) {
-            if (!PathParser.canMorph(mNodes, nodes)) {
-                // This should not happen in the middle of animation.
-                mNodes = PathParser.deepCopyNodes(nodes);
-            } else {
-                PathParser.updateNodes(mNodes, nodes);
-            }
-        }
-    }
-
-    /**
-     * Clip path, which only has name and pathData.
-     */
-    private static class VClipPath extends VPath {
-        public VClipPath() {
-            // Empty constructor.
-        }
-
-        public VClipPath(VClipPath copy) {
-            super(copy);
-        }
-
-        public void inflate(Resources r, AttributeSet attrs, Theme theme, XmlPullParser parser) {
-            // TODO TINT THEME Not supported yet
-            final boolean hasPathData = TypedArrayUtils.hasAttribute(parser, "pathData");
-            if (!hasPathData) {
-                return;
-            }
-            final TypedArray a = TypedArrayUtils.obtainAttributes(r, theme, attrs,
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH);
-            updateStateFromTypedArray(a);
-            a.recycle();
-        }
-
-        private void updateStateFromTypedArray(TypedArray a) {
-            // Account for any configuration changes.
-            // mChangingConfigurations |= Utils.getChangingConfigurations(a);;
-
-            final String pathName =
-                    a.getString(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_NAME);
-            if (pathName != null) {
-                mPathName = pathName;
-            }
-
-            final String pathData =
-                    a.getString(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_CLIP_PATH_PATH_DATA);
-            if (pathData != null) {
-                mNodes = PathParser.createNodesFromPathData(pathData);
-            }
-        }
-
-        @Override
-        public boolean isClipPath() {
-            return true;
-        }
-    }
-
-    /**
-     * Normal path, which contains all the fill / paint information.
-     */
-    private static class VFullPath extends VPath {
-        /////////////////////////////////////////////////////
-        // Variables below need to be copied (deep copy if applicable) for mutation.
-        private int[] mThemeAttrs;
-        private static final int FILL_TYPE_WINDING = 0;
-        int mStrokeColor = Color.TRANSPARENT;
-        float mStrokeWidth = 0;
-
-        int mFillColor = Color.TRANSPARENT;
-        float mStrokeAlpha = 1.0f;
-        // Default fill rule is winding, or as known as "non-zero".
-        int mFillRule = FILL_TYPE_WINDING;
-        float mFillAlpha = 1.0f;
-        float mTrimPathStart = 0;
-        float mTrimPathEnd = 1;
-        float mTrimPathOffset = 0;
-
-        Paint.Cap mStrokeLineCap = Paint.Cap.BUTT;
-        Paint.Join mStrokeLineJoin = Paint.Join.MITER;
-        float mStrokeMiterlimit = 4;
-
-        public VFullPath() {
-            // Empty constructor.
-        }
-
-        public VFullPath(VFullPath copy) {
-            super(copy);
-            mThemeAttrs = copy.mThemeAttrs;
-
-            mStrokeColor = copy.mStrokeColor;
-            mStrokeWidth = copy.mStrokeWidth;
-            mStrokeAlpha = copy.mStrokeAlpha;
-            mFillColor = copy.mFillColor;
-            mFillRule = copy.mFillRule;
-            mFillAlpha = copy.mFillAlpha;
-            mTrimPathStart = copy.mTrimPathStart;
-            mTrimPathEnd = copy.mTrimPathEnd;
-            mTrimPathOffset = copy.mTrimPathOffset;
-
-            mStrokeLineCap = copy.mStrokeLineCap;
-            mStrokeLineJoin = copy.mStrokeLineJoin;
-            mStrokeMiterlimit = copy.mStrokeMiterlimit;
-        }
-
-        private Paint.Cap getStrokeLineCap(int id, Paint.Cap defValue) {
-            switch (id) {
-                case LINECAP_BUTT:
-                    return Paint.Cap.BUTT;
-                case LINECAP_ROUND:
-                    return Paint.Cap.ROUND;
-                case LINECAP_SQUARE:
-                    return Paint.Cap.SQUARE;
-                default:
-                    return defValue;
-            }
-        }
-
-        private Paint.Join getStrokeLineJoin(int id, Paint.Join defValue) {
-            switch (id) {
-                case LINEJOIN_MITER:
-                    return Paint.Join.MITER;
-                case LINEJOIN_ROUND:
-                    return Paint.Join.ROUND;
-                case LINEJOIN_BEVEL:
-                    return Paint.Join.BEVEL;
-                default:
-                    return defValue;
-            }
-        }
-
-        @Override
-        public boolean canApplyTheme() {
-            return mThemeAttrs != null;
-        }
-
-        public void inflate(Resources r, AttributeSet attrs, Theme theme, XmlPullParser parser) {
-            final TypedArray a = TypedArrayUtils.obtainAttributes(r, theme, attrs,
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH);
-            updateStateFromTypedArray(a, parser);
-            a.recycle();
-        }
-
-        private void updateStateFromTypedArray(TypedArray a, XmlPullParser parser) {
-            // Account for any configuration changes.
-            // mChangingConfigurations |= Utils.getChangingConfigurations(a);
-
-            // Extract the theme attributes, if any.
-            mThemeAttrs = null; // TODO TINT THEME Not supported yet a.extractThemeAttrs();
-
-            // In order to work around the conflicting id issue, we need to double check the
-            // existence of the attribute.
-            // B/c if the attribute existed in the compiled XML, then calling TypedArray will be
-            // safe since the framework will look up in the XML first.
-            // Note that each getAttributeValue take roughly 0.03ms, it is a price we have to pay.
-            final boolean hasPathData = TypedArrayUtils.hasAttribute(parser, "pathData");
-            if (!hasPathData) {
-                // If there is no pathData in the <path> tag, then this is an empty path,
-                // nothing need to be drawn.
-                return;
-            }
-
-            final String pathName = a.getString(
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_NAME);
-            if (pathName != null) {
-                mPathName = pathName;
-            }
-            final String pathData =
-                    a.getString(AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_PATH_DATA);
-            if (pathData != null) {
-                mNodes = PathParser.createNodesFromPathData(pathData);
-            }
-
-            mFillColor = TypedArrayUtils.getNamedColor(a, parser, "fillColor",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_COLOR, mFillColor);
-            mFillAlpha = TypedArrayUtils.getNamedFloat(a, parser, "fillAlpha",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_FILL_ALPHA, mFillAlpha);
-            final int lineCap = TypedArrayUtils.getNamedInt(a, parser, "strokeLineCap",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_CAP, -1);
-            mStrokeLineCap = getStrokeLineCap(lineCap, mStrokeLineCap);
-            final int lineJoin = TypedArrayUtils.getNamedInt(a, parser, "strokeLineJoin",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_LINE_JOIN, -1);
-            mStrokeLineJoin = getStrokeLineJoin(lineJoin, mStrokeLineJoin);
-            mStrokeMiterlimit = TypedArrayUtils.getNamedFloat(a, parser, "strokeMiterLimit",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_MITER_LIMIT,
-                    mStrokeMiterlimit);
-            mStrokeColor = TypedArrayUtils.getNamedColor(a, parser, "strokeColor",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_COLOR, mStrokeColor);
-            mStrokeAlpha = TypedArrayUtils.getNamedFloat(a, parser, "strokeAlpha",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_ALPHA, mStrokeAlpha);
-            mStrokeWidth = TypedArrayUtils.getNamedFloat(a, parser, "strokeWidth",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_STROKE_WIDTH, mStrokeWidth);
-            mTrimPathEnd = TypedArrayUtils.getNamedFloat(a, parser, "trimPathEnd",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_END, mTrimPathEnd);
-            mTrimPathOffset = TypedArrayUtils.getNamedFloat(a, parser, "trimPathOffset",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_OFFSET,
-                    mTrimPathOffset);
-            mTrimPathStart = TypedArrayUtils.getNamedFloat(a, parser, "trimPathStart",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_START,
-                    mTrimPathStart);
-            mFillRule = TypedArrayUtils.getNamedInt(a, parser, "fillType",
-                    AndroidResources.STYLEABLE_VECTOR_DRAWABLE_PATH_TRIM_PATH_FILLTYPE,
-                    mFillRule);
-        }
-
-        @Override
-        public void applyTheme(Theme t) {
-            if (mThemeAttrs == null) {
-                return;
-            }
-
-            /*
-             * TODO TINT THEME Not supported yet final TypedArray a =
-             * t.resolveAttributes(mThemeAttrs, styleable_VectorDrawablePath);
-             * updateStateFromTypedArray(a); a.recycle();
-             */
-        }
-
-        /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
-        @SuppressWarnings("unused")
-        int getStrokeColor() {
-            return mStrokeColor;
-        }
-
-        @SuppressWarnings("unused")
-        void setStrokeColor(int strokeColor) {
-            mStrokeColor = strokeColor;
-        }
-
-        @SuppressWarnings("unused")
-        float getStrokeWidth() {
-            return mStrokeWidth;
-        }
-
-        @SuppressWarnings("unused")
-        void setStrokeWidth(float strokeWidth) {
-            mStrokeWidth = strokeWidth;
-        }
-
-        @SuppressWarnings("unused")
-        float getStrokeAlpha() {
-            return mStrokeAlpha;
-        }
-
-        @SuppressWarnings("unused")
-        void setStrokeAlpha(float strokeAlpha) {
-            mStrokeAlpha = strokeAlpha;
-        }
-
-        @SuppressWarnings("unused")
-        int getFillColor() {
-            return mFillColor;
-        }
-
-        @SuppressWarnings("unused")
-        void setFillColor(int fillColor) {
-            mFillColor = fillColor;
-        }
-
-        @SuppressWarnings("unused")
-        float getFillAlpha() {
-            return mFillAlpha;
-        }
-
-        @SuppressWarnings("unused")
-        void setFillAlpha(float fillAlpha) {
-            mFillAlpha = fillAlpha;
-        }
-
-        @SuppressWarnings("unused")
-        float getTrimPathStart() {
-            return mTrimPathStart;
-        }
-
-        @SuppressWarnings("unused")
-        void setTrimPathStart(float trimPathStart) {
-            mTrimPathStart = trimPathStart;
-        }
-
-        @SuppressWarnings("unused")
-        float getTrimPathEnd() {
-            return mTrimPathEnd;
-        }
-
-        @SuppressWarnings("unused")
-        void setTrimPathEnd(float trimPathEnd) {
-            mTrimPathEnd = trimPathEnd;
-        }
-
-        @SuppressWarnings("unused")
-        float getTrimPathOffset() {
-            return mTrimPathOffset;
-        }
-
-        @SuppressWarnings("unused")
-        void setTrimPathOffset(float trimPathOffset) {
-            mTrimPathOffset = trimPathOffset;
-        }
-    }
-}
diff --git a/android/support/jdiff/JDiffTask.java b/android/support/jdiff/JDiffTask.java
deleted file mode 100644
index f89a375..0000000
--- a/android/support/jdiff/JDiffTask.java
+++ /dev/null
@@ -1,180 +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.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/android/support/media/ExifInterface.java b/android/support/media/ExifInterface.java
deleted file mode 100644
index 6b437a6..0000000
--- a/android/support/media/ExifInterface.java
+++ /dev/null
@@ -1,6482 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.media;
-
-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.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
-import java.io.DataInput;
-import java.io.DataInputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.text.ParsePosition;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
- * <p>
- * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
- * <p>
- * Attribute mutation is supported for JPEG image files.
- */
-public class ExifInterface {
-    private static final String TAG = "ExifInterface";
-    private static final boolean DEBUG = false;
-
-    // The Exif tag names. See JEITA CP-3451C specifications (Exif 2.3) Section 3-8.
-    // A. Tags related to image data structure
-    /**
-     *  <p>The number of columns of image data, equal to the number of pixels per row. In JPEG
-     *  compressed data, this tag shall not be used because a JPEG marker is used instead of it.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 256</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_IMAGE_WIDTH = "ImageWidth";
-    /**
-     *  <p>The number of rows of image data. In JPEG compressed data, this tag shall not be used
-     *  because a JPEG marker is used instead of it.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 257</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_IMAGE_LENGTH = "ImageLength";
-    /**
-     *  <p>The number of bits per image component. In this standard each component of the image is
-     *  8 bits, so the value for this tag is 8. See also {@link #TAG_SAMPLES_PER_PIXEL}. In JPEG
-     *  compressed data, this tag shall not be used because a JPEG marker is used instead of it.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 258</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 3</li>
-     *      <li>Default = {@link #BITS_PER_SAMPLE_RGB}</li>
-     *  </ul>
-     */
-    public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
-    /**
-     *  <p>The compression scheme used for the image data. When a primary image is JPEG compressed,
-     *  this designation is not necessary. So, this tag shall not be recorded. When thumbnails use
-     *  JPEG compression, this tag value is set to 6.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 259</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #DATA_UNCOMPRESSED
-     *  @see #DATA_JPEG
-     */
-    public static final String TAG_COMPRESSION = "Compression";
-    /**
-     *  <p>The pixel composition. In JPEG compressed data, this tag shall not be used because a JPEG
-     *  marker is used instead of it.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 262</li>
-     *      <li>Type = SHORT</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #PHOTOMETRIC_INTERPRETATION_RGB
-     *  @see #PHOTOMETRIC_INTERPRETATION_YCBCR
-     */
-    public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
-    /**
-     *  <p>The image orientation viewed in terms of rows and columns.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 274</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #ORIENTATION_NORMAL}</li>
-     *  </ul>
-     *
-     *  @see #ORIENTATION_UNDEFINED
-     *  @see #ORIENTATION_NORMAL
-     *  @see #ORIENTATION_FLIP_HORIZONTAL
-     *  @see #ORIENTATION_ROTATE_180
-     *  @see #ORIENTATION_FLIP_VERTICAL
-     *  @see #ORIENTATION_TRANSPOSE
-     *  @see #ORIENTATION_ROTATE_90
-     *  @see #ORIENTATION_TRANSVERSE
-     *  @see #ORIENTATION_ROTATE_270
-     */
-    public static final String TAG_ORIENTATION = "Orientation";
-    /**
-     *  <p>The number of components per pixel. Since this standard applies to RGB and YCbCr images,
-     *  the value set for this tag is 3. In JPEG compressed data, this tag shall not be used because
-     *  a JPEG marker is used instead of it.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 277</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = 3</li>
-     *  </ul>
-     */
-    public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
-    /**
-     *  <p>Indicates whether pixel components are recorded in chunky or planar format. In JPEG
-     *  compressed data, this tag shall not be used because a JPEG marker is used instead of it.
-     *  If this field does not exist, the TIFF default, {@link #FORMAT_CHUNKY}, is assumed.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 284</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *  </ul>
-     *
-     *  @see #FORMAT_CHUNKY
-     *  @see #FORMAT_PLANAR
-     */
-    public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
-    /**
-     *  <p>The sampling ratio of chrominance components in relation to the luminance component.
-     *  In JPEG compressed data a JPEG marker is used instead of this tag. So, this tag shall not
-     *  be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 530</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 2</li>
-     *      <ul>
-     *          <li>[2, 1] = YCbCr4:2:2</li>
-     *          <li>[2, 2] = YCbCr4:2:0</li>
-     *          <li>Other = reserved</li>
-     *      </ul>
-     *  </ul>
-     */
-    public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
-    /**
-     *  <p>The position of chrominance components in relation to the luminance component. This field
-     *  is designated only for JPEG compressed data or uncompressed YCbCr data. The TIFF default is
-     *  {@link #Y_CB_CR_POSITIONING_CENTERED}; but when Y:Cb:Cr = 4:2:2 it is recommended in this
-     *  standard that {@link #Y_CB_CR_POSITIONING_CO_SITED} be used to record data, in order to
-     *  improve the image quality when viewed on TV systems. When this field does not exist,
-     *  the reader shall assume the TIFF default. In the case of Y:Cb:Cr = 4:2:0, the TIFF default
-     *  ({@link #Y_CB_CR_POSITIONING_CENTERED}) is recommended. If the Exif/DCF reader does not
-     *  have the capability of supporting both kinds of positioning, it shall follow the TIFF
-     *  default regardless of the value in this field. It is preferable that readers can support
-     *  both centered and co-sited positioning.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 531</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #Y_CB_CR_POSITIONING_CENTERED}</li>
-     *  </ul>
-     *
-     *  @see #Y_CB_CR_POSITIONING_CENTERED
-     *  @see #Y_CB_CR_POSITIONING_CO_SITED
-     */
-    public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
-    /**
-     *  <p>The number of pixels per {@link #TAG_RESOLUTION_UNIT} in the {@link #TAG_IMAGE_WIDTH}
-     *  direction. When the image resolution is unknown, 72 [dpi] shall be designated.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 282</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = 72</li>
-     *  </ul>
-     *
-     *  @see #TAG_Y_RESOLUTION
-     *  @see #TAG_RESOLUTION_UNIT
-     */
-    public static final String TAG_X_RESOLUTION = "XResolution";
-    /**
-     *  <p>The number of pixels per {@link #TAG_RESOLUTION_UNIT} in the {@link #TAG_IMAGE_WIDTH}
-     *  direction. The same value as {@link #TAG_X_RESOLUTION} shall be designated.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 283</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = 72</li>
-     *  </ul>
-     *
-     *  @see #TAG_X_RESOLUTION
-     *  @see #TAG_RESOLUTION_UNIT
-     */
-    public static final String TAG_Y_RESOLUTION = "YResolution";
-    /**
-     *  <p>The unit for measuring {@link #TAG_X_RESOLUTION} and {@link #TAG_Y_RESOLUTION}. The same
-     *  unit is used for both {@link #TAG_X_RESOLUTION} and {@link #TAG_Y_RESOLUTION}. If the image
-     *  resolution is unknown, {@link #RESOLUTION_UNIT_INCHES} shall be designated.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 296</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #RESOLUTION_UNIT_INCHES}</li>
-     *  </ul>
-     *
-     *  @see #RESOLUTION_UNIT_INCHES
-     *  @see #RESOLUTION_UNIT_CENTIMETERS
-     *  @see #TAG_X_RESOLUTION
-     *  @see #TAG_Y_RESOLUTION
-     */
-    public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
-
-    // B. Tags related to recording offset
-    /**
-     *  <p>For each strip, the byte offset of that strip. It is recommended that this be selected
-     *  so the number of strip bytes does not exceed 64 KBytes.In the case of JPEG compressed data,
-     *  this designation is not necessary. So, this tag shall not be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 273</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = StripsPerImage (for {@link #FORMAT_CHUNKY})
-     *               or {@link #TAG_SAMPLES_PER_PIXEL} * StripsPerImage
-     *               (for {@link #FORMAT_PLANAR})</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  <p>StripsPerImage = floor(({@link #TAG_IMAGE_LENGTH} + {@link #TAG_ROWS_PER_STRIP} - 1)
-     *  / {@link #TAG_ROWS_PER_STRIP})</p>
-     *
-     *  @see #TAG_ROWS_PER_STRIP
-     *  @see #TAG_STRIP_BYTE_COUNTS
-     */
-    public static final String TAG_STRIP_OFFSETS = "StripOffsets";
-    /**
-     *  <p>The number of rows per strip. This is the number of rows in the image of one strip when
-     *  an image is divided into strips. In the case of JPEG compressed data, this designation is
-     *  not necessary. So, this tag shall not be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 278</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #TAG_STRIP_OFFSETS
-     *  @see #TAG_STRIP_BYTE_COUNTS
-     */
-    public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
-    /**
-     *  <p>The total number of bytes in each strip. In the case of JPEG compressed data, this
-     *  designation is not necessary. So, this tag shall not be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 279</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = StripsPerImage (when using {@link #FORMAT_CHUNKY})
-     *               or {@link #TAG_SAMPLES_PER_PIXEL} * StripsPerImage
-     *               (when using {@link #FORMAT_PLANAR})</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  <p>StripsPerImage = floor(({@link #TAG_IMAGE_LENGTH} + {@link #TAG_ROWS_PER_STRIP} - 1)
-     *  / {@link #TAG_ROWS_PER_STRIP})</p>
-     */
-    public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
-    /**
-     *  <p>The offset to the start byte (SOI) of JPEG compressed thumbnail data. This shall not be
-     *  used for primary image JPEG data.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 513</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
-    /**
-     *  <p>The number of bytes of JPEG compressed thumbnail data. This is not used for primary image
-     *  JPEG data. JPEG thumbnails are not divided but are recorded as a continuous JPEG bitstream
-     *  from SOI to EOI. APPn and COM markers should not be recorded. Compressed thumbnails shall be
-     *  recorded in no more than 64 KBytes, including all other data to be recorded in APP1.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 514</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
-
-    // C. Tags related to Image Data Characteristics
-    /**
-     *  <p>A transfer function for the image, described in tabular style. Normally this tag need not
-     *  be used, since color space is specified in {@link #TAG_COLOR_SPACE}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 301</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 3 * 256</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
-    /**
-     *  <p>The chromaticity of the white point of the image. Normally this tag need not be used,
-     *  since color space is specified in {@link #TAG_COLOR_SPACE}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 318</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 2</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_WHITE_POINT = "WhitePoint";
-    /**
-     *  <p>The chromaticity of the three primary colors of the image. Normally this tag need not
-     *  be used, since color space is specified in {@link #TAG_COLOR_SPACE}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 319</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 6</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
-    /**
-     *  <p>The matrix coefficients for transformation from RGB to YCbCr image data. About
-     *  the default value, please refer to JEITA CP-3451C Spec, Annex D.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 529</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 3</li>
-     *  </ul>
-     */
-    public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
-    /**
-     *  <p>The reference black point value and reference white point value. No defaults are given
-     *  in TIFF, but the values below are given as defaults here. The color space is declared in
-     *  a color space information tag, with the default being the value that gives the optimal image
-     *  characteristics Interoperability these conditions</p>
-     *
-     *  <ul>
-     *      <li>Tag = 532</li>
-     *      <li>Type = RATIONAL</li>
-     *      <li>Count = 6</li>
-     *      <li>Default = [0, 255, 0, 255, 0, 255] (when {@link #TAG_PHOTOMETRIC_INTERPRETATION}
-     *                 is {@link #PHOTOMETRIC_INTERPRETATION_RGB})
-     *                 or [0, 255, 0, 128, 0, 128] (when {@link #TAG_PHOTOMETRIC_INTERPRETATION}
-     *                 is {@link #PHOTOMETRIC_INTERPRETATION_YCBCR})</li>
-     *  </ul>
-     */
-    public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
-
-    // D. Other tags
-    /**
-     *  <p>The date and time of image creation. In this standard it is the date and time the file
-     *  was changed. The format is "YYYY:MM:DD HH:MM:SS" with time shown in 24-hour format, and
-     *  the date and time separated by one blank character ({@code 0x20}). When the date and time
-     *  are unknown, all the character spaces except colons (":") should be filled with blank
-     *  characters, or else the Interoperability field should be filled with blank characters.
-     *  The character string length is 20 Bytes including NULL for termination. When the field is
-     *  left blank, it is treated as unknown.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 306</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 19</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_DATETIME = "DateTime";
-    /**
-     *  <p>An ASCII string giving the title of the image. It is possible to be added a comment
-     *  such as "1988 company picnic" or the like. Two-byte character codes cannot be used. When
-     *  a 2-byte code is necessary, {@link #TAG_USER_COMMENT} is to be used.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 270</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
-    /**
-     *  <p>The manufacturer of the recording equipment. This is the manufacturer of the DSC,
-     *  scanner, video digitizer or other equipment that generated the image. When the field is left
-     *  blank, it is treated as unknown.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 271</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_MAKE = "Make";
-    /**
-     *  <p>The model name or model number of the equipment. This is the model name of number of
-     *  the DSC, scanner, video digitizer or other equipment that generated the image. When
-     *  the field is left blank, it is treated as unknown.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 272</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_MODEL = "Model";
-    /**
-     *  <p>This tag records the name and version of the software or firmware of the camera or image
-     *  input device used to generate the image. The detailed format is not specified, but it is
-     *  recommended that the example shown below be followed. When the field is left blank, it is
-     *  treated as unknown.</p>
-     *
-     *  <p>Ex.) "Exif Software Version 1.00a".</p>
-     *
-     *  <ul>
-     *      <li>Tag = 305</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SOFTWARE = "Software";
-    /**
-     *  <p>This tag records the name of the camera owner, photographer or image creator.
-     *  The detailed format is not specified, but it is recommended that the information be written
-     *  as in the example below for ease of Interoperability. When the field is left blank, it is
-     *  treated as unknown.</p>
-     *
-     *  <p>Ex.) "Camera owner, John Smith; Photographer, Michael Brown; Image creator,
-     *  Ken James"</p>
-     *
-     *  <ul>
-     *      <li>Tag = 315</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_ARTIST = "Artist";
-    /**
-     *  <p>Copyright information. In this standard the tag is used to indicate both the photographer
-     *  and editor copyrights. It is the copyright notice of the person or organization claiming
-     *  rights to the image. The Interoperability copyright statement including date and rights
-     *  should be written in this field; e.g., "Copyright, John Smith, 19xx. All rights reserved."
-     *  In this standard the field records both the photographer and editor copyrights, with each
-     *  recorded in a separate part of the statement. When there is a clear distinction between
-     *  the photographer and editor copyrights, these are to be written in the order of photographer
-     *  followed by editor copyright, separated by NULL (in this case, since the statement also ends
-     *  with a NULL, there are two NULL codes) (see example 1). When only the photographer copyright
-     *  is given, it is terminated by one NULL code (see example 2). When only the editor copyright
-     *  is given, the photographer copyright part consists of one space followed by a terminating
-     *  NULL code, then the editor copyright is given (see example 3). When the field is left blank,
-     *  it is treated as unknown.</p>
-     *
-     *  <p>Ex. 1) When both the photographer copyright and editor copyright are given.
-     *  <ul><li>Photographer copyright + NULL + editor copyright + NULL</li></ul></p>
-     *  <p>Ex. 2) When only the photographer copyright is given.
-     *  <ul><li>Photographer copyright + NULL</li></ul></p>
-     *  <p>Ex. 3) When only the editor copyright is given.
-     *  <ul><li>Space ({@code 0x20}) + NULL + editor copyright + NULL</li></ul></p>
-     *
-     *  <ul>
-     *      <li>Tag = 315</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_COPYRIGHT = "Copyright";
-
-    // Exif IFD Attribute Information
-    // A. Tags related to version
-    /**
-     *  <p>The version of this standard supported. Nonexistence of this field is taken to mean
-     *  nonconformance to the standard. In according with conformance to this standard, this tag
-     *  shall be recorded like "0230” as 4-byte ASCII.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 36864</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Length = 4</li>
-     *      <li>Default = "0230"</li>
-     *  </ul>
-     */
-    public static final String TAG_EXIF_VERSION = "ExifVersion";
-    /**
-     *  <p>The Flashpix format version supported by a FPXR file. If the FPXR function supports
-     *  Flashpix format Ver. 1.0, this is indicated similarly to {@link #TAG_EXIF_VERSION} by
-     *  recording "0100" as 4-byte ASCII.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 40960</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Length = 4</li>
-     *      <li>Default = "0100"</li>
-     *  </ul>
-     */
-    public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
-
-    // B. Tags related to image data characteristics
-    /**
-     *  <p>The color space information tag is always recorded as the color space specifier.
-     *  Normally {@link #COLOR_SPACE_S_RGB} is used to define the color space based on the PC
-     *  monitor conditions and environment. If a color space other than {@link #COLOR_SPACE_S_RGB}
-     *  is used, {@link #COLOR_SPACE_UNCALIBRATED} is set. Image data recorded as
-     *  {@link #COLOR_SPACE_UNCALIBRATED} may be treated as {@link #COLOR_SPACE_S_RGB} when it is
-     *  converted to Flashpix.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 40961</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *  </ul>
-     *
-     *  @see #COLOR_SPACE_S_RGB
-     *  @see #COLOR_SPACE_UNCALIBRATED
-     */
-    public static final String TAG_COLOR_SPACE = "ColorSpace";
-    /**
-     *  <p>Indicates the value of coefficient gamma. The formula of transfer function used for image
-     *  reproduction is expressed as follows.</p>
-     *
-     *  <p>(Reproduced value) = (Input value) ^ gamma</p>
-     *
-     *  <p>Both reproduced value and input value indicate normalized value, whose minimum value is
-     *  0 and maximum value is 1.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42240</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GAMMA = "Gamma";
-
-    // C. Tags related to image configuration
-    /**
-     *  <p>Information specific to compressed data. When a compressed file is recorded, the valid
-     *  width of the meaningful image shall be recorded in this tag, whether or not there is padding
-     *  data or a restart marker. This tag shall not exist in an uncompressed file.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 40962</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
-    /**
-     *  <p>Information specific to compressed data. When a compressed file is recorded, the valid
-     *  height of the meaningful image shall be recorded in this tag, whether or not there is
-     *  padding data or a restart marker. This tag shall not exist in an uncompressed file.
-     *  Since data padding is unnecessary in the vertical direction, the number of lines recorded
-     *  in this valid image height tag will in fact be the same as that recorded in the SOF.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 40963</li>
-     *      <li>Type = Unsigned short or Unsigned long</li>
-     *      <li>Count = 1</li>
-     *  </ul>
-     */
-    public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
-    /**
-     *  <p>Information specific to compressed data. The channels of each component are arranged
-     *  in order from the 1st component to the 4th. For uncompressed data the data arrangement is
-     *  given in the {@link #TAG_PHOTOMETRIC_INTERPRETATION}. However, since
-     *  {@link #TAG_PHOTOMETRIC_INTERPRETATION} can only express the order of Y, Cb and Cr, this tag
-     *  is provided for cases when compressed data uses components other than Y, Cb, and Cr and to
-     *  enable support of other sequences.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37121</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Length = 4</li>
-     *      <li>Default = 4 5 6 0 (if RGB uncompressed) or 1 2 3 0 (other cases)</li>
-     *      <ul>
-     *          <li>0 = does not exist</li>
-     *          <li>1 = Y</li>
-     *          <li>2 = Cb</li>
-     *          <li>3 = Cr</li>
-     *          <li>4 = R</li>
-     *          <li>5 = G</li>
-     *          <li>6 = B</li>
-     *          <li>other = reserved</li>
-     *      </ul>
-     *  </ul>
-     */
-    public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
-    /**
-     *  <p>Information specific to compressed data. The compression mode used for a compressed image
-     *  is indicated in unit bits per pixel.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37122</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
-
-    // D. Tags related to user information
-    /**
-     *  <p>A tag for manufacturers of Exif/DCF writers to record any desired information.
-     *  The contents are up to the manufacturer, but this tag shall not be used for any other than
-     *  its intended purpose.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37500</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_MAKER_NOTE = "MakerNote";
-    /**
-     *  <p>A tag for Exif users to write keywords or comments on the image besides those in
-     *  {@link #TAG_IMAGE_DESCRIPTION}, and without the character code limitations of it.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37510</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_USER_COMMENT = "UserComment";
-
-    // E. Tags related to related file information
-    /**
-     *  <p>This tag is used to record the name of an audio file related to the image data. The only
-     *  relational information recorded here is the Exif audio file name and extension (an ASCII
-     *  string consisting of 8 characters + '.' + 3 characters). The path is not recorded.</p>
-     *
-     *  <p>When using this tag, audio files shall be recorded in conformance to the Exif audio
-     *  format. Writers can also store the data such as Audio within APP2 as Flashpix extension
-     *  stream data. Audio files shall be recorded in conformance to the Exif audio format.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 40964</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 12</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
-
-    // F. Tags related to date and time
-    /**
-     *  <p>The date and time when the original image data was generated. For a DSC the date and time
-     *  the picture was taken are recorded. The format is "YYYY:MM:DD HH:MM:SS" with time shown in
-     *  24-hour format, and the date and time separated by one blank character ({@code 0x20}).
-     *  When the date and time are unknown, all the character spaces except colons (":") should be
-     *  filled with blank characters, or else the Interoperability field should be filled with blank
-     *  characters. When the field is left blank, it is treated as unknown.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 36867</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 19</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
-    /**
-     *  <p>The date and time when the image was stored as digital data. If, for example, an image
-     *  was captured by DSC and at the same time the file was recorded, then
-     *  {@link #TAG_DATETIME_ORIGINAL} and this tag will have the same contents. The format is
-     *  "YYYY:MM:DD HH:MM:SS" with time shown in 24-hour format, and the date and time separated by
-     *  one blank character ({@code 0x20}). When the date and time are unknown, all the character
-     *  spaces except colons (":")should be filled with blank characters, or else
-     *  the Interoperability field should be filled with blank characters. When the field is left
-     *  blank, it is treated as unknown.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 36868</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 19</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
-    /**
-     *  <p>A tag used to record fractions of seconds for {@link #TAG_DATETIME}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37520</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SUBSEC_TIME = "SubSecTime";
-    /**
-     *  <p>A tag used to record fractions of seconds for {@link #TAG_DATETIME_ORIGINAL}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37521</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
-    /**
-     *  <p>A tag used to record fractions of seconds for {@link #TAG_DATETIME_DIGITIZED}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37522</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
-
-    // G. Tags related to picture-taking condition
-    /**
-     *  <p>Exposure time, given in seconds.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 33434</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_EXPOSURE_TIME = "ExposureTime";
-    /**
-     *  <p>The F number.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 33437</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_F_NUMBER = "FNumber";
-    /**
-     *  <p>TThe class of the program used by the camera to set exposure when the picture is taken.
-     *  The tag values are as follows.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34850</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #EXPOSURE_PROGRAM_NOT_DEFINED}</li>
-     *  </ul>
-     *
-     *  @see #EXPOSURE_PROGRAM_NOT_DEFINED
-     *  @see #EXPOSURE_PROGRAM_MANUAL
-     *  @see #EXPOSURE_PROGRAM_NORMAL
-     *  @see #EXPOSURE_PROGRAM_APERTURE_PRIORITY
-     *  @see #EXPOSURE_PROGRAM_SHUTTER_PRIORITY
-     *  @see #EXPOSURE_PROGRAM_CREATIVE
-     *  @see #EXPOSURE_PROGRAM_ACTION
-     *  @see #EXPOSURE_PROGRAM_PORTRAIT_MODE
-     *  @see #EXPOSURE_PROGRAM_LANDSCAPE_MODE
-     */
-    public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
-    /**
-     *  <p>Indicates the spectral sensitivity of each channel of the camera used. The tag value is
-     *  an ASCII string compatible with the standard developed by the ASTM Technical committee.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34852</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
-    /**
-     *  @deprecated Use {@link #TAG_PHOTOGRAPHIC_SENSITIVITY} instead.
-     *  @see #TAG_PHOTOGRAPHIC_SENSITIVITY
-     */
-    @Deprecated public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
-    /**
-     *  <p>This tag indicates the sensitivity of the camera or input device when the image was shot.
-     *  More specifically, it indicates one of the following values that are parameters defined in
-     *  ISO 12232: standard output sensitivity (SOS), recommended exposure index (REI), or ISO
-     *  speed. Accordingly, if a tag corresponding to a parameter that is designated by
-     *  {@link #TAG_SENSITIVITY_TYPE} is recorded, the values of the tag and of this tag are
-     *  the same. However, if the value is 65535 or higher, the value of this tag shall be 65535.
-     *  When recording this tag, {@link #TAG_SENSITIVITY_TYPE} should also be recorded. In addition,
-     *  while “Count = Any”, only 1 count should be used when recording this tag.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34855</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = Any</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_PHOTOGRAPHIC_SENSITIVITY = "PhotographicSensitivity";
-    /**
-     *  <p>Indicates the Opto-Electric Conversion Function (OECF) specified in ISO 14524. OECF is
-     *  the relationship between the camera optical input and the image values.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34856</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_OECF = "OECF";
-    /**
-     *  <p>This tag indicates which one of the parameters of ISO12232 is
-     *  {@link #TAG_PHOTOGRAPHIC_SENSITIVITY}. Although it is an optional tag, it should be recorded
-     *  when {@link #TAG_PHOTOGRAPHIC_SENSITIVITY} is recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34864</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #SENSITIVITY_TYPE_UNKNOWN
-     *  @see #SENSITIVITY_TYPE_SOS
-     *  @see #SENSITIVITY_TYPE_REI
-     *  @see #SENSITIVITY_TYPE_ISO_SPEED
-     *  @see #SENSITIVITY_TYPE_SOS_AND_REI
-     *  @see #SENSITIVITY_TYPE_SOS_AND_ISO
-     *  @see #SENSITIVITY_TYPE_REI_AND_ISO
-     *  @see #SENSITIVITY_TYPE_SOS_AND_REI_AND_ISO
-     */
-    public static final String TAG_SENSITIVITY_TYPE = "SensitivityType";
-    /**
-     *  <p>This tag indicates the standard output sensitivity value of a camera or input device
-     *  defined in ISO 12232. When recording this tag, {@link #TAG_PHOTOGRAPHIC_SENSITIVITY} and
-     *  {@link #TAG_SENSITIVITY_TYPE} shall also be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34865</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_STANDARD_OUTPUT_SENSITIVITY = "StandardOutputSensitivity";
-    /**
-     *  <p>This tag indicates the recommended exposure index value of a camera or input device
-     *  defined in ISO 12232. When recording this tag, {@link #TAG_PHOTOGRAPHIC_SENSITIVITY} and
-     *  {@link #TAG_SENSITIVITY_TYPE} shall also be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34866</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_RECOMMENDED_EXPOSURE_INDEX = "RecommendedExposureIndex";
-    /**
-     *  <p>This tag indicates the ISO speed value of a camera or input device that is defined in
-     *  ISO 12232. When recording this tag, {@link #TAG_PHOTOGRAPHIC_SENSITIVITY} and
-     *  {@link #TAG_SENSITIVITY_TYPE} shall also be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34867</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_ISO_SPEED = "ISOSpeed";
-    /**
-     *  <p>This tag indicates the ISO speed latitude yyy value of a camera or input device that is
-     *  defined in ISO 12232. However, this tag shall not be recorded without {@link #TAG_ISO_SPEED}
-     *  and {@link #TAG_ISO_SPEED_LATITUDE_ZZZ}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34868</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_ISO_SPEED_LATITUDE_YYY = "ISOSpeedLatitudeyyy";
-    /**
-     *  <p>This tag indicates the ISO speed latitude zzz value of a camera or input device that is
-     *  defined in ISO 12232. However, this tag shall not be recorded without {@link #TAG_ISO_SPEED}
-     *  and {@link #TAG_ISO_SPEED_LATITUDE_YYY}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 34869</li>
-     *      <li>Type = Unsigned long</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_ISO_SPEED_LATITUDE_ZZZ = "ISOSpeedLatitudezzz";
-    /**
-     *  <p>Shutter speed. The unit is the APEX setting.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37377</li>
-     *      <li>Type = Signed rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
-    /**
-     *  <p>The lens aperture. The unit is the APEX value.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37378</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_APERTURE_VALUE = "ApertureValue";
-    /**
-     *  <p>The value of brightness. The unit is the APEX value. Ordinarily it is given in the range
-     *  of -99.99 to 99.99. Note that if the numerator of the recorded value is 0xFFFFFFFF,
-     *  Unknown shall be indicated.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37379</li>
-     *      <li>Type = Signed rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
-    /**
-     *  <p>The exposure bias. The unit is the APEX value. Ordinarily it is given in the range of
-     *  -99.99 to 99.99.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37380</li>
-     *      <li>Type = Signed rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
-    /**
-     *  <p>The smallest F number of the lens. The unit is the APEX value. Ordinarily it is given
-     *  in the range of 00.00 to 99.99, but it is not limited to this range.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37381</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
-    /**
-     *  <p>The distance to the subject, given in meters. Note that if the numerator of the recorded
-     *  value is 0xFFFFFFFF, Infinity shall be indicated; and if the numerator is 0, Distance
-     *  unknown shall be indicated.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37382</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
-    /**
-     *  <p>The metering mode.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37383</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #METERING_MODE_UNKNOWN}</li>
-     *  </ul>
-     *
-     *  @see #METERING_MODE_UNKNOWN
-     *  @see #METERING_MODE_AVERAGE
-     *  @see #METERING_MODE_CENTER_WEIGHT_AVERAGE
-     *  @see #METERING_MODE_SPOT
-     *  @see #METERING_MODE_MULTI_SPOT
-     *  @see #METERING_MODE_PATTERN
-     *  @see #METERING_MODE_PARTIAL
-     *  @see #METERING_MODE_OTHER
-     */
-    public static final String TAG_METERING_MODE = "MeteringMode";
-    /**
-     *  <p>The kind of light source.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37384</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #LIGHT_SOURCE_UNKNOWN}</li>
-     *  </ul>
-     *
-     *  @see #LIGHT_SOURCE_UNKNOWN
-     *  @see #LIGHT_SOURCE_DAYLIGHT
-     *  @see #LIGHT_SOURCE_FLUORESCENT
-     *  @see #LIGHT_SOURCE_TUNGSTEN
-     *  @see #LIGHT_SOURCE_FLASH
-     *  @see #LIGHT_SOURCE_FINE_WEATHER
-     *  @see #LIGHT_SOURCE_CLOUDY_WEATHER
-     *  @see #LIGHT_SOURCE_SHADE
-     *  @see #LIGHT_SOURCE_DAYLIGHT_FLUORESCENT
-     *  @see #LIGHT_SOURCE_DAY_WHITE_FLUORESCENT
-     *  @see #LIGHT_SOURCE_COOL_WHITE_FLUORESCENT
-     *  @see #LIGHT_SOURCE_WHITE_FLUORESCENT
-     *  @see #LIGHT_SOURCE_WARM_WHITE_FLUORESCENT
-     *  @see #LIGHT_SOURCE_STANDARD_LIGHT_A
-     *  @see #LIGHT_SOURCE_STANDARD_LIGHT_B
-     *  @see #LIGHT_SOURCE_STANDARD_LIGHT_C
-     *  @see #LIGHT_SOURCE_D55
-     *  @see #LIGHT_SOURCE_D65
-     *  @see #LIGHT_SOURCE_D75
-     *  @see #LIGHT_SOURCE_D50
-     *  @see #LIGHT_SOURCE_ISO_STUDIO_TUNGSTEN
-     *  @see #LIGHT_SOURCE_OTHER
-     */
-    public static final String TAG_LIGHT_SOURCE = "LightSource";
-    /**
-     *  <p>This tag indicates the status of flash when the image was shot. Bit 0 indicates the flash
-     *  firing status, bits 1 and 2 indicate the flash return status, bits 3 and 4 indicate
-     *  the flash mode, bit 5 indicates whether the flash function is present, and bit 6 indicates
-     *  "red eye" mode.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37385</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *  </ul>
-     *
-     *  @see #FLAG_FLASH_FIRED
-     *  @see #FLAG_FLASH_RETURN_LIGHT_NOT_DETECTED
-     *  @see #FLAG_FLASH_RETURN_LIGHT_DETECTED
-     *  @see #FLAG_FLASH_MODE_COMPULSORY_FIRING
-     *  @see #FLAG_FLASH_MODE_COMPULSORY_SUPPRESSION
-     *  @see #FLAG_FLASH_MODE_AUTO
-     *  @see #FLAG_FLASH_NO_FLASH_FUNCTION
-     *  @see #FLAG_FLASH_RED_EYE_SUPPORTED
-     */
-    public static final String TAG_FLASH = "Flash";
-    /**
-     *  <p>This tag indicates the location and area of the main subject in the overall scene.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37396</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 2 or 3 or 4</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  <p>The subject location and area are defined by Count values as follows.</p>
-     *
-     *  <ul>
-     *      <li>Count = 2 Indicates the location of the main subject as coordinates. The first value
-     *                    is the X coordinate and the second is the Y coordinate.</li>
-     *      <li>Count = 3 The area of the main subject is given as a circle. The circular area is
-     *                    expressed as center coordinates and diameter. The first value is
-     *                    the center X coordinate, the second is the center Y coordinate, and
-     *                    the third is the diameter.</li>
-     *      <li>Count = 4 The area of the main subject is given as a rectangle. The rectangular
-     *                    area is expressed as center coordinates and area dimensions. The first
-     *                    value is the center X coordinate, the second is the center Y coordinate,
-     *                    the third is the width of the area, and the fourth is the height of
-     *                    the area.</li>
-     *  </ul>
-     *
-     *  <p>Note that the coordinate values, width, and height are expressed in relation to the upper
-     *  left as origin, prior to rotation processing as per {@link #TAG_ORIENTATION}.</p>
-     */
-    public static final String TAG_SUBJECT_AREA = "SubjectArea";
-    /**
-     *  <p>The actual focal length of the lens, in mm. Conversion is not made to the focal length
-     *  of a 35mm film camera.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 37386</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_FOCAL_LENGTH = "FocalLength";
-    /**
-     *  <p>Indicates the strobe energy at the time the image is captured, as measured in Beam Candle
-     *  Power Seconds (BCPS).</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41483</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_FLASH_ENERGY = "FlashEnergy";
-    /**
-     *  <p>This tag records the camera or input device spatial frequency table and SFR values in
-     *  the direction of image width, image height, and diagonal direction, as specified in
-     *  ISO 12233.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41484</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
-    /**
-     *  <p>Indicates the number of pixels in the image width (X) direction per
-     *  {@link #TAG_FOCAL_PLANE_RESOLUTION_UNIT} on the camera focal plane.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41486</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
-    /**
-     *  <p>Indicates the number of pixels in the image height (Y) direction per
-     *  {@link #TAG_FOCAL_PLANE_RESOLUTION_UNIT} on the camera focal plane.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41487</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
-    /**
-     *  <p>Indicates the unit for measuring {@link #TAG_FOCAL_PLANE_X_RESOLUTION} and
-     *  {@link #TAG_FOCAL_PLANE_Y_RESOLUTION}. This value is the same as
-     *  {@link #TAG_RESOLUTION_UNIT}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41488</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #RESOLUTION_UNIT_INCHES}</li>
-     *  </ul>
-     *
-     *  @see #TAG_RESOLUTION_UNIT
-     *  @see #RESOLUTION_UNIT_INCHES
-     *  @see #RESOLUTION_UNIT_CENTIMETERS
-     */
-    public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
-    /**
-     *  <p>Indicates the location of the main subject in the scene. The value of this tag represents
-     *  the pixel at the center of the main subject relative to the left edge, prior to rotation
-     *  processing as per {@link #TAG_ORIENTATION}. The first value indicates the X column number
-     *  and second indicates the Y row number. When a camera records the main subject location,
-     *  it is recommended that {@link #TAG_SUBJECT_AREA} be used instead of this tag.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41492</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 2</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
-    /**
-     *  <p>Indicates the exposure index selected on the camera or input device at the time the image
-     *  is captured.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41493</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
-    /**
-     *  <p>Indicates the image sensor type on the camera or input device.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41495</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #SENSOR_TYPE_NOT_DEFINED
-     *  @see #SENSOR_TYPE_ONE_CHIP
-     *  @see #SENSOR_TYPE_TWO_CHIP
-     *  @see #SENSOR_TYPE_THREE_CHIP
-     *  @see #SENSOR_TYPE_COLOR_SEQUENTIAL
-     *  @see #SENSOR_TYPE_TRILINEAR
-     *  @see #SENSOR_TYPE_COLOR_SEQUENTIAL_LINEAR
-     */
-    public static final String TAG_SENSING_METHOD = "SensingMethod";
-    /**
-     *  <p>Indicates the image source. If a DSC recorded the image, this tag value always shall
-     *  be set to {@link #FILE_SOURCE_DSC}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41728</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = {@link #FILE_SOURCE_DSC}</li>
-     *  </ul>
-     *
-     *  @see #FILE_SOURCE_OTHER
-     *  @see #FILE_SOURCE_TRANSPARENT_SCANNER
-     *  @see #FILE_SOURCE_REFLEX_SCANNER
-     *  @see #FILE_SOURCE_DSC
-     */
-    public static final String TAG_FILE_SOURCE = "FileSource";
-    /**
-     *  <p>Indicates the type of scene. If a DSC recorded the image, this tag value shall always
-     *  be set to {@link #SCENE_TYPE_DIRECTLY_PHOTOGRAPHED}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41729</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = 1</li>
-     *  </ul>
-     *
-     *  @see #SCENE_TYPE_DIRECTLY_PHOTOGRAPHED
-     */
-    public static final String TAG_SCENE_TYPE = "SceneType";
-    /**
-     *  <p>Indicates the color filter array (CFA) geometric pattern of the image sensor when
-     *  a one-chip color area sensor is used. It does not apply to all sensing methods.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41730</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #TAG_SENSING_METHOD
-     *  @see #SENSOR_TYPE_ONE_CHIP
-     */
-    public static final String TAG_CFA_PATTERN = "CFAPattern";
-    /**
-     *  <p>This tag indicates the use of special processing on image data, such as rendering geared
-     *  to output. When special processing is performed, the Exif/DCF reader is expected to disable
-     *  or minimize any further processing.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41985</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #RENDERED_PROCESS_NORMAL}</li>
-     *  </ul>
-     *
-     *  @see #RENDERED_PROCESS_NORMAL
-     *  @see #RENDERED_PROCESS_CUSTOM
-     */
-    public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
-    /**
-     *  <p>This tag indicates the exposure mode set when the image was shot.
-     *  In {@link #EXPOSURE_MODE_AUTO_BRACKET}, the camera shoots a series of frames of the same
-     *  scene at different exposure settings.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41986</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #EXPOSURE_MODE_AUTO
-     *  @see #EXPOSURE_MODE_MANUAL
-     *  @see #EXPOSURE_MODE_AUTO_BRACKET
-     */
-    public static final String TAG_EXPOSURE_MODE = "ExposureMode";
-    /**
-     *  <p>This tag indicates the white balance mode set when the image was shot.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41987</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #WHITEBALANCE_AUTO
-     *  @see #WHITEBALANCE_MANUAL
-     */
-    public static final String TAG_WHITE_BALANCE = "WhiteBalance";
-    /**
-     *  <p>This tag indicates the digital zoom ratio when the image was shot. If the numerator of
-     *  the recorded value is 0, this indicates that digital zoom was not used.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41988</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
-    /**
-     *  <p>This tag indicates the equivalent focal length assuming a 35mm film camera, in mm.
-     *  A value of 0 means the focal length is unknown. Note that this tag differs from
-     *  {@link #TAG_FOCAL_LENGTH}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41989</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
-    /**
-     *  <p>This tag indicates the type of scene that was shot. It may also be used to record
-     *  the mode in which the image was shot. Note that this differs from
-     *  {@link #TAG_SCENE_TYPE}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41990</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = 0</li>
-     *  </ul>
-     *
-     *  @see #SCENE_CAPTURE_TYPE_STANDARD
-     *  @see #SCENE_CAPTURE_TYPE_LANDSCAPE
-     *  @see #SCENE_CAPTURE_TYPE_PORTRAIT
-     *  @see #SCENE_CAPTURE_TYPE_NIGHT
-     */
-    public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
-    /**
-     *  <p>This tag indicates the degree of overall image gain adjustment.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41991</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #GAIN_CONTROL_NONE
-     *  @see #GAIN_CONTROL_LOW_GAIN_UP
-     *  @see #GAIN_CONTROL_HIGH_GAIN_UP
-     *  @see #GAIN_CONTROL_LOW_GAIN_DOWN
-     *  @see #GAIN_CONTROL_HIGH_GAIN_DOWN
-     */
-    public static final String TAG_GAIN_CONTROL = "GainControl";
-    /**
-     *  <p>This tag indicates the direction of contrast processing applied by the camera when
-     *  the image was shot.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41992</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #CONTRAST_NORMAL}</li>
-     *  </ul>
-     *
-     *  @see #CONTRAST_NORMAL
-     *  @see #CONTRAST_SOFT
-     *  @see #CONTRAST_HARD
-     */
-    public static final String TAG_CONTRAST = "Contrast";
-    /**
-     *  <p>This tag indicates the direction of saturation processing applied by the camera when
-     *  the image was shot.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41993</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #SATURATION_NORMAL}</li>
-     *  </ul>
-     *
-     *  @see #SATURATION_NORMAL
-     *  @see #SATURATION_LOW
-     *  @see #SATURATION_HIGH
-     */
-    public static final String TAG_SATURATION = "Saturation";
-    /**
-     *  <p>This tag indicates the direction of sharpness processing applied by the camera when
-     *  the image was shot.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41994</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = {@link #SHARPNESS_NORMAL}</li>
-     *  </ul>
-     *
-     *  @see #SHARPNESS_NORMAL
-     *  @see #SHARPNESS_SOFT
-     *  @see #SHARPNESS_HARD
-     */
-    public static final String TAG_SHARPNESS = "Sharpness";
-    /**
-     *  <p>This tag indicates information on the picture-taking conditions of a particular camera
-     *  model. The tag is used only to indicate the picture-taking conditions in the Exif/DCF
-     *  reader.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41995</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
-    /**
-     *  <p>This tag indicates the distance to the subject.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 41996</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #SUBJECT_DISTANCE_RANGE_UNKNOWN
-     *  @see #SUBJECT_DISTANCE_RANGE_MACRO
-     *  @see #SUBJECT_DISTANCE_RANGE_CLOSE_VIEW
-     *  @see #SUBJECT_DISTANCE_RANGE_DISTANT_VIEW
-     */
-    public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
-
-    // H. Other tags
-    /**
-     *  <p>This tag indicates an identifier assigned uniquely to each image. It is recorded as
-     *  an ASCII string equivalent to hexadecimal notation and 128-bit fixed length.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42016</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 32</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
-    /**
-     *  <p>This tag records the owner of a camera used in photography as an ASCII string.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42032</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_CAMARA_OWNER_NAME = "CameraOwnerName";
-    /**
-     *  <p>This tag records the serial number of the body of the camera that was used in photography
-     *  as an ASCII string.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42033</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_BODY_SERIAL_NUMBER = "BodySerialNumber";
-    /**
-     *  <p>This tag notes minimum focal length, maximum focal length, minimum F number in the
-     *  minimum focal length, and minimum F number in the maximum focal length, which are
-     *  specification information for the lens that was used in photography. When the minimum
-     *  F number is unknown, the notation is 0/0.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42034</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 4</li>
-     *      <li>Default = None</li>
-     *      <ul>
-     *          <li>Value 1 := Minimum focal length (unit: mm)</li>
-     *          <li>Value 2 : = Maximum focal length (unit: mm)</li>
-     *          <li>Value 3 : = Minimum F number in the minimum focal length</li>
-     *          <li>Value 4 : = Minimum F number in the maximum focal length</li>
-     *      </ul>
-     *  </ul>
-     */
-    public static final String TAG_LENS_SPECIFICATION = "LensSpecification";
-    /**
-     *  <p>This tag records the lens manufacturer as an ASCII string.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42035</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_LENS_MAKE = "LensMake";
-    /**
-     *  <p>This tag records the lens’s model name and model number as an ASCII string.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42036</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_LENS_MODEL = "LensModel";
-    /**
-     *  <p>This tag records the serial number of the interchangeable lens that was used in
-     *  photography as an ASCII string.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 42037</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_LENS_SERIAL_NUMBER = "LensSerialNumber";
-
-    // GPS Attribute Information
-    /**
-     *  <p>Indicates the version of GPS Info IFD. The version is given as 2.3.0.0. This tag is
-     *  mandatory when GPS-related tags are present. Note that this tag is written as a different
-     *  byte than {@link #TAG_EXIF_VERSION}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 0</li>
-     *      <li>Type = Byte</li>
-     *      <li>Count = 4</li>
-     *      <li>Default = 2.3.0.0</li>
-     *      <ul>
-     *          <li>2300 = Version 2.3</li>
-     *          <li>Other = reserved</li>
-     *      </ul>
-     *  </ul>
-     */
-    public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
-    /**
-     *  <p>Indicates whether the latitude is north or south latitude.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 1</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #LATITUDE_NORTH
-     *  @see #LATITUDE_SOUTH
-     */
-    public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
-    /**
-     *  <p>Indicates the latitude. The latitude is expressed as three RATIONAL values giving
-     *  the degrees, minutes, and seconds, respectively. If latitude is expressed as degrees,
-     *  minutes and seconds, a typical format would be dd/1,mm/1,ss/1. When degrees and minutes are
-     *  used and, for example, fractions of minutes are given up to two decimal places, the format
-     *  would be dd/1,mmmm/100,0/1.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 2</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 3</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_LATITUDE = "GPSLatitude";
-    /**
-     *  <p>Indicates whether the longitude is east or west longitude.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 3</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #LONGITUDE_EAST
-     *  @see #LONGITUDE_WEST
-     */
-    public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
-    /**
-     *  <p>Indicates the longitude. The longitude is expressed as three RATIONAL values giving
-     *  the degrees, minutes, and seconds, respectively. If longitude is expressed as degrees,
-     *  minutes and seconds, a typical format would be ddd/1,mm/1,ss/1. When degrees and minutes
-     *  are used and, for example, fractions of minutes are given up to two decimal places,
-     *  the format would be ddd/1,mmmm/100,0/1.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 4</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 3</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
-    /**
-     *  <p>Indicates the altitude used as the reference altitude. If the reference is sea level
-     *  and the altitude is above sea level, 0 is given. If the altitude is below sea level,
-     *  a value of 1 is given and the altitude is indicated as an absolute value in
-     *  {@link #TAG_GPS_ALTITUDE}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 5</li>
-     *      <li>Type = Byte</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = 0</li>
-     *  </ul>
-     *
-     *  @see #ALTITUDE_ABOVE_SEA_LEVEL
-     *  @see #ALTITUDE_BELOW_SEA_LEVEL
-     */
-    public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
-    /**
-     *  <p>Indicates the altitude based on the reference in {@link #TAG_GPS_ALTITUDE_REF}.
-     *  The reference unit is meters.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 6</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
-    /**
-     *  <p>Indicates the time as UTC (Coordinated Universal Time). TimeStamp is expressed as three
-     *  unsigned rational values giving the hour, minute, and second.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 7</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 3</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
-    /**
-     *  <p>Indicates the GPS satellites used for measurements. This tag may be used to describe
-     *  the number of satellites, their ID number, angle of elevation, azimuth, SNR and other
-     *  information in ASCII notation. The format is not specified. If the GPS receiver is incapable
-     *  of taking measurements, value of the tag shall be set to {@code null}.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 8</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_SATELLITES = "GPSSatellites";
-    /**
-     *  <p>Indicates the status of the GPS receiver when the image is recorded. 'A' means
-     *  measurement is in progress, and 'V' means the measurement is interrupted.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 9</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #GPS_MEASUREMENT_IN_PROGRESS
-     *  @see #GPS_MEASUREMENT_INTERRUPTED
-     */
-    public static final String TAG_GPS_STATUS = "GPSStatus";
-    /**
-     *  <p>Indicates the GPS measurement mode. Originally it was defined for GPS, but it may
-     *  be used for recording a measure mode to record the position information provided from
-     *  a mobile base station or wireless LAN as well as GPS.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 10</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #GPS_MEASUREMENT_2D
-     *  @see #GPS_MEASUREMENT_3D
-     */
-    public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
-    /**
-     *  <p>Indicates the GPS DOP (data degree of precision). An HDOP value is written during
-     *  two-dimensional measurement, and PDOP during three-dimensional measurement.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 11</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_DOP = "GPSDOP";
-    /**
-     *  <p>Indicates the unit used to express the GPS receiver speed of movement.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 12</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = {@link #GPS_SPEED_KILOMETERS_PER_HOUR}</li>
-     *  </ul>
-     *
-     *  @see #GPS_SPEED_KILOMETERS_PER_HOUR
-     *  @see #GPS_SPEED_MILES_PER_HOUR
-     *  @see #GPS_SPEED_KNOTS
-     */
-    public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
-    /**
-     *  <p>Indicates the speed of GPS receiver movement.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 13</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_SPEED = "GPSSpeed";
-    /**
-     *  <p>Indicates the reference for giving the direction of GPS receiver movement.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 14</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = {@link #GPS_DIRECTION_TRUE}</li>
-     *  </ul>
-     *
-     *  @see #GPS_DIRECTION_TRUE
-     *  @see #GPS_DIRECTION_MAGNETIC
-     */
-    public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
-    /**
-     *  <p>Indicates the direction of GPS receiver movement.
-     *  The range of values is from 0.00 to 359.99.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 15</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_TRACK = "GPSTrack";
-    /**
-     *  <p>Indicates the reference for giving the direction of the image when it is captured.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 16</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = {@link #GPS_DIRECTION_TRUE}</li>
-     *  </ul>
-     *
-     *  @see #GPS_DIRECTION_TRUE
-     *  @see #GPS_DIRECTION_MAGNETIC
-     */
-    public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
-    /**
-     *  <p>ndicates the direction of the image when it was captured.
-     *  The range of values is from 0.00 to 359.99.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 17</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
-    /**
-     *  <p>Indicates the geodetic survey data used by the GPS receiver. If the survey data is
-     *  restricted to Japan,the value of this tag is 'TOKYO' or 'WGS-84'. If a GPS Info tag is
-     *  recorded, it is strongly recommended that this tag be recorded.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 18</li>
-     *      <li>Type = String</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
-    /**
-     *  <p>Indicates whether the latitude of the destination point is north or south latitude.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 19</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #LATITUDE_NORTH
-     *  @see #LATITUDE_SOUTH
-     */
-    public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
-    /**
-     *  <p>Indicates the latitude of the destination point. The latitude is expressed as three
-     *  unsigned rational values giving the degrees, minutes, and seconds, respectively.
-     *  If latitude is expressed as degrees, minutes and seconds, a typical format would be
-     *  dd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes
-     *  are given up to two decimal places, the format would be dd/1, mmmm/100, 0/1.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 20</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 3</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
-    /**
-     *  <p>Indicates whether the longitude of the destination point is east or west longitude.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 21</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #LONGITUDE_EAST
-     *  @see #LONGITUDE_WEST
-     */
-    public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
-    /**
-     *  <p>Indicates the longitude of the destination point. The longitude is expressed as three
-     *  unsigned rational values giving the degrees, minutes, and seconds, respectively.
-     *  If longitude is expressed as degrees, minutes and seconds, a typical format would be ddd/1,
-     *  mm/1, ss/1. When degrees and minutes are used and, for example, fractions of minutes are
-     *  given up to two decimal places, the format would be ddd/1, mmmm/100, 0/1.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 22</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 3</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
-    /**
-     *  <p>Indicates the reference used for giving the bearing to the destination point.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 23</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = {@link #GPS_DIRECTION_TRUE}</li>
-     *  </ul>
-     *
-     *  @see #GPS_DIRECTION_TRUE
-     *  @see #GPS_DIRECTION_MAGNETIC
-     */
-    public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
-    /**
-     *  <p>Indicates the bearing to the destination point.
-     *  The range of values is from 0.00 to 359.99.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 24</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
-    /**
-     *  <p>Indicates the unit used to express the distance to the destination point.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 25</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 1</li>
-     *      <li>Default = {@link #GPS_DISTANCE_KILOMETERS}</li>
-     *  </ul>
-     *
-     *  @see #GPS_DISTANCE_KILOMETERS
-     *  @see #GPS_DISTANCE_MILES
-     *  @see #GPS_DISTANCE_NAUTICAL_MILES
-     */
-    public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
-    /**
-     *  <p>Indicates the distance to the destination point.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 26</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
-    /**
-     *  <p>A character string recording the name of the method used for location finding.
-     *  The first byte indicates the character code used, and this is followed by the name of
-     *  the method.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 27</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
-    /**
-     *  <p>A character string recording the name of the GPS area. The first byte indicates
-     *  the character code used, and this is followed by the name of the GPS area.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 28</li>
-     *      <li>Type = Undefined</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
-    /**
-     *  <p>A character string recording date and time information relative to UTC (Coordinated
-     *  Universal Time). The format is "YYYY:MM:DD".</p>
-     *
-     *  <ul>
-     *      <li>Tag = 29</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 10</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
-    /**
-     *  <p>Indicates whether differential correction is applied to the GPS receiver.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 30</li>
-     *      <li>Type = Unsigned short</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     *
-     *  @see #GPS_MEASUREMENT_NO_DIFFERENTIAL
-     *  @see #GPS_MEASUREMENT_DIFFERENTIAL_CORRECTED
-     */
-    public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
-    /**
-     *  <p>This tag indicates horizontal positioning errors in meters.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 31</li>
-     *      <li>Type = Unsigned rational</li>
-     *      <li>Count = 1</li>
-     *      <li>Default = None</li>
-     *  </ul>
-     */
-    public static final String TAG_GPS_H_POSITIONING_ERROR = "GPSHPositioningError";
-
-    // Interoperability IFD Attribute Information
-    /**
-     *  <p>Indicates the identification of the Interoperability rule.</p>
-     *
-     *  <ul>
-     *      <li>Tag = 1</li>
-     *      <li>Type = String</li>
-     *      <li>Length = 4</li>
-     *      <li>Default = None</li>
-     *      <ul>
-     *          <li>"R98" = Indicates a file conforming to R98 file specification of Recommended
-     *                      Exif Interoperability Rules (Exif R 98) or to DCF basic file stipulated
-     *                      by Design Rule for Camera File System.</li>
-     *          <li>"THM" = Indicates a file conforming to DCF thumbnail file stipulated by Design
-     *                      rule for Camera File System.</li>
-     *          <li>“R03” = Indicates a file conforming to DCF Option File stipulated by Design rule
-     *                      for Camera File System.</li>
-     *      </ul>
-     *  </ul>
-     */
-    public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
-
-    /**
-     * @see #TAG_IMAGE_LENGTH
-     */
-    public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
-    /**
-     * @see #TAG_IMAGE_WIDTH
-     */
-    public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
-    /** Type is int. DNG Specification 1.4.0.0. Section 4 */
-    public static final String TAG_DNG_VERSION = "DNGVersion";
-    /** Type is int. DNG Specification 1.4.0.0. Section 4 */
-    public static final String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
-    /** Type is undefined. See Olympus MakerNote tags in http://www.exiv2.org/tags-olympus.html. */
-    public static final String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
-    /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
-    public static final String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
-    /** Type is int. See Olympus Camera Settings tags in http://www.exiv2.org/tags-olympus.html. */
-    public static final String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
-    /** Type is int. See Olympus Image Processing tags in http://www.exiv2.org/tags-olympus.html. */
-    public static final String TAG_ORF_ASPECT_FRAME = "AspectFrame";
-    /**
-     * Type is int. See PanasonicRaw tags in
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
-     */
-    public static final String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
-    /**
-     * Type is int. See PanasonicRaw tags in
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
-     */
-    public static final String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
-    /**
-     * Type is int. See PanasonicRaw tags in
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
-     */
-    public static final String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
-    /**
-     * Type is int. See PanasonicRaw tags in
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
-     */
-    public static final String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
-    /**
-     * Type is int. See PanasonicRaw tags in
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
-     */
-    public static final String TAG_RW2_ISO = "ISO";
-    /**
-     * Type is undefined. See PanasonicRaw tags in
-     * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html
-     */
-    public static final String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
-    /** Type is int. See JEITA CP-3451C Spec Section 3: Bilevel Images. */
-    public static final String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
-    /** Type is int. See JEITA CP-3451C Spec Section 3: Bilevel Images. */
-    public static final String TAG_SUBFILE_TYPE = "SubfileType";
-
-    /**
-     * Private tags used for pointing the other IFD offsets.
-     * The types of the following tags are int.
-     * See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
-     * For SubIFD, see Note 1 of Adobe PageMaker® 6.0 TIFF Technical Notes.
-     */
-    private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
-    private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
-    private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
-    private static final String TAG_SUB_IFD_POINTER = "SubIFDPointer";
-    // Proprietary pointer tags used for ORF files.
-    // See http://www.exiv2.org/tags-olympus.html
-    private static final String TAG_ORF_CAMERA_SETTINGS_IFD_POINTER = "CameraSettingsIFDPointer";
-    private static final String TAG_ORF_IMAGE_PROCESSING_IFD_POINTER = "ImageProcessingIFDPointer";
-
-    // Private tags used for thumbnail information.
-    private static final String TAG_HAS_THUMBNAIL = "HasThumbnail";
-    private static final String TAG_THUMBNAIL_OFFSET = "ThumbnailOffset";
-    private static final String TAG_THUMBNAIL_LENGTH = "ThumbnailLength";
-    private static final String TAG_THUMBNAIL_DATA = "ThumbnailData";
-    private static final int MAX_THUMBNAIL_SIZE = 512;
-
-    // Constants used for the Orientation Exif tag.
-    public static final int ORIENTATION_UNDEFINED = 0;
-    public static final int ORIENTATION_NORMAL = 1;
-    /**
-     * Indicates the image is left right reversed mirror.
-     */
-    public static final int ORIENTATION_FLIP_HORIZONTAL = 2;
-    /**
-     * Indicates the image is rotated by 180 degree clockwise.
-     */
-    public static final int ORIENTATION_ROTATE_180 = 3;
-    /**
-     * Indicates the image is upside down mirror, it can also be represented by flip
-     * horizontally firstly and rotate 180 degree clockwise.
-     */
-    public static final int ORIENTATION_FLIP_VERTICAL = 4;
-    /**
-     * Indicates the image is flipped about top-left <--> bottom-right axis, it can also be
-     * represented by flip horizontally firstly and rotate 270 degree clockwise.
-     */
-    public static final int ORIENTATION_TRANSPOSE = 5;
-    /**
-     * Indicates the image is rotated by 90 degree clockwise.
-     */
-    public static final int ORIENTATION_ROTATE_90 = 6;
-    /**
-     * Indicates the image is flipped about top-right <--> bottom-left axis, it can also be
-     * represented by flip horizontally firstly and rotate 90 degree clockwise.
-     */
-    public static final int ORIENTATION_TRANSVERSE = 7;
-    /**
-     * Indicates the image is rotated by 270 degree clockwise.
-     */
-    public static final int ORIENTATION_ROTATE_270 = 8;
-    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);
-
-    /**
-     * The contant used by {@link #TAG_PLANAR_CONFIGURATION} to denote Chunky format.
-     */
-    public static final short FORMAT_CHUNKY = 1;
-    /**
-     * The contant used by {@link #TAG_PLANAR_CONFIGURATION} to denote Planar format.
-     */
-    public static final short FORMAT_PLANAR = 2;
-
-    /**
-     * The contant used by {@link #TAG_Y_CB_CR_POSITIONING} to denote Centered positioning.
-     */
-    public static final short Y_CB_CR_POSITIONING_CENTERED = 1;
-    /**
-     * The contant used by {@link #TAG_Y_CB_CR_POSITIONING} to denote Co-sited positioning.
-     */
-    public static final short Y_CB_CR_POSITIONING_CO_SITED = 2;
-
-    /**
-     * The contant used to denote resolution unit as inches.
-     */
-    public static final short RESOLUTION_UNIT_INCHES = 2;
-    /**
-     * The contant used to denote resolution unit as centimeters.
-     */
-    public static final short RESOLUTION_UNIT_CENTIMETERS = 3;
-
-    /**
-     * The contant used by {@link #TAG_COLOR_SPACE} to denote sRGB color space.
-     */
-    public static final int COLOR_SPACE_S_RGB = 1;
-    /**
-     * The contant used by {@link #TAG_COLOR_SPACE} to denote Uncalibrated.
-     */
-    public static final int COLOR_SPACE_UNCALIBRATED = 65535;
-
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is not defined.
-     */
-    public static final short EXPOSURE_PROGRAM_NOT_DEFINED = 0;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is Manual.
-     */
-    public static final short EXPOSURE_PROGRAM_MANUAL = 1;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is Normal.
-     */
-    public static final short EXPOSURE_PROGRAM_NORMAL = 2;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is
-     * Aperture priority.
-     */
-    public static final short EXPOSURE_PROGRAM_APERTURE_PRIORITY = 3;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is
-     * Shutter priority.
-     */
-    public static final short EXPOSURE_PROGRAM_SHUTTER_PRIORITY = 4;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is Creative
-     * program (biased toward depth of field).
-     */
-    public static final short EXPOSURE_PROGRAM_CREATIVE = 5;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is Action
-     * program (biased toward fast shutter speed).
-     */
-    public static final short EXPOSURE_PROGRAM_ACTION = 6;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is Portrait mode
-     * (for closeup photos with the background out of focus).
-     */
-    public static final short EXPOSURE_PROGRAM_PORTRAIT_MODE = 7;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_PROGRAM} to denote exposure program is Landscape
-     * mode (for landscape photos with the background in focus).
-     */
-    public static final short EXPOSURE_PROGRAM_LANDSCAPE_MODE = 8;
-
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is unknown.
-     */
-    public static final short SENSITIVITY_TYPE_UNKNOWN = 0;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is Standard
-     * output sensitivity (SOS).
-     */
-    public static final short SENSITIVITY_TYPE_SOS = 1;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is Recommended
-     * exposure index (REI).
-     */
-    public static final short SENSITIVITY_TYPE_REI = 2;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is ISO speed.
-     */
-    public static final short SENSITIVITY_TYPE_ISO_SPEED = 3;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is Standard
-     * output sensitivity (SOS) and recommended exposure index (REI).
-     */
-    public static final short SENSITIVITY_TYPE_SOS_AND_REI = 4;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is Standard
-     * output sensitivity (SOS) and ISO speed.
-     */
-    public static final short SENSITIVITY_TYPE_SOS_AND_ISO = 5;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is Recommended
-     * exposure index (REI) and ISO speed.
-     */
-    public static final short SENSITIVITY_TYPE_REI_AND_ISO = 6;
-    /**
-     * The contant used by {@link #TAG_SENSITIVITY_TYPE} to denote sensitivity type is Standard
-     * output sensitivity (SOS) and recommended exposure index (REI) and ISO speed.
-     */
-    public static final short SENSITIVITY_TYPE_SOS_AND_REI_AND_ISO = 7;
-
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is unknown.
-     */
-    public static final short METERING_MODE_UNKNOWN = 0;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is Average.
-     */
-    public static final short METERING_MODE_AVERAGE = 1;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is
-     * CenterWeightedAverage.
-     */
-    public static final short METERING_MODE_CENTER_WEIGHT_AVERAGE = 2;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is Spot.
-     */
-    public static final short METERING_MODE_SPOT = 3;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is MultiSpot.
-     */
-    public static final short METERING_MODE_MULTI_SPOT = 4;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is Pattern.
-     */
-    public static final short METERING_MODE_PATTERN = 5;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is Partial.
-     */
-    public static final short METERING_MODE_PARTIAL = 6;
-    /**
-     * The contant used by {@link #TAG_METERING_MODE} to denote metering mode is other.
-     */
-    public static final short METERING_MODE_OTHER = 255;
-
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is unknown.
-     */
-    public static final short LIGHT_SOURCE_UNKNOWN = 0;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Daylight.
-     */
-    public static final short LIGHT_SOURCE_DAYLIGHT = 1;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Fluorescent.
-     */
-    public static final short LIGHT_SOURCE_FLUORESCENT = 2;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Tungsten
-     * (incandescent light).
-     */
-    public static final short LIGHT_SOURCE_TUNGSTEN = 3;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Flash.
-     */
-    public static final short LIGHT_SOURCE_FLASH = 4;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Fine weather.
-     */
-    public static final short LIGHT_SOURCE_FINE_WEATHER = 9;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Cloudy weather.
-     */
-    public static final short LIGHT_SOURCE_CLOUDY_WEATHER = 10;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Shade.
-     */
-    public static final short LIGHT_SOURCE_SHADE = 11;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Daylight fluorescent
-     * (D 5700 - 7100K).
-     */
-    public static final short LIGHT_SOURCE_DAYLIGHT_FLUORESCENT = 12;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Day white fluorescent
-     * (N 4600 - 5500K).
-     */
-    public static final short LIGHT_SOURCE_DAY_WHITE_FLUORESCENT = 13;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Cool white
-     * fluorescent (W 3800 - 4500K).
-     */
-    public static final short LIGHT_SOURCE_COOL_WHITE_FLUORESCENT = 14;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is White fluorescent
-     * (WW 3250 - 3800K).
-     */
-    public static final short LIGHT_SOURCE_WHITE_FLUORESCENT = 15;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Warm white
-     * fluorescent (L 2600 - 3250K).
-     */
-    public static final short LIGHT_SOURCE_WARM_WHITE_FLUORESCENT = 16;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Standard light A.
-     */
-    public static final short LIGHT_SOURCE_STANDARD_LIGHT_A = 17;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Standard light B.
-     */
-    public static final short LIGHT_SOURCE_STANDARD_LIGHT_B = 18;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is Standard light C.
-     */
-    public static final short LIGHT_SOURCE_STANDARD_LIGHT_C = 19;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is D55.
-     */
-    public static final short LIGHT_SOURCE_D55 = 20;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is D65.
-     */
-    public static final short LIGHT_SOURCE_D65 = 21;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is D75.
-     */
-    public static final short LIGHT_SOURCE_D75 = 22;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is D50.
-     */
-    public static final short LIGHT_SOURCE_D50 = 23;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is ISO studio tungsten.
-     */
-    public static final short LIGHT_SOURCE_ISO_STUDIO_TUNGSTEN = 24;
-    /**
-     * The contant used by {@link #TAG_LIGHT_SOURCE} to denote light source is other.
-     */
-    public static final short LIGHT_SOURCE_OTHER = 255;
-
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate whether the flash is fired.
-     */
-    public static final short FLAG_FLASH_FIRED = 0b0000_0001;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate strobe return light is not detected.
-     */
-    public static final short FLAG_FLASH_RETURN_LIGHT_NOT_DETECTED = 0b0000_0100;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate strobe return light is detected.
-     */
-    public static final short FLAG_FLASH_RETURN_LIGHT_DETECTED = 0b0000_0110;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate the camera's flash mode is Compulsory flash
-     * firing.
-     *
-     * @see #FLAG_FLASH_MODE_COMPULSORY_SUPPRESSION
-     * @see #FLAG_FLASH_MODE_AUTO
-     */
-    public static final short FLAG_FLASH_MODE_COMPULSORY_FIRING = 0b0000_1000;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate the camera's flash mode is Compulsory flash
-     * suppression.
-     *
-     * @see #FLAG_FLASH_MODE_COMPULSORY_FIRING
-     * @see #FLAG_FLASH_MODE_AUTO
-     */
-    public static final short FLAG_FLASH_MODE_COMPULSORY_SUPPRESSION = 0b0001_0000;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate the camera's flash mode is Auto.
-     *
-     * @see #FLAG_FLASH_MODE_COMPULSORY_FIRING
-     * @see #FLAG_FLASH_MODE_COMPULSORY_SUPPRESSION
-     */
-    public static final short FLAG_FLASH_MODE_AUTO = 0b0001_1000;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate no flash function is present.
-     */
-    public static final short FLAG_FLASH_NO_FLASH_FUNCTION = 0b0010_0000;
-    /**
-     * The flag used by {@link #TAG_FLASH} to indicate red-eye reduction is supported.
-     */
-    public static final short FLAG_FLASH_RED_EYE_SUPPORTED = 0b0100_0000;
-
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is not
-     * defined.
-     */
-    public static final short SENSOR_TYPE_NOT_DEFINED = 1;
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is One-chip
-     * color area sensor.
-     */
-    public static final short SENSOR_TYPE_ONE_CHIP = 2;
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is Two-chip
-     * color area sensor.
-     */
-    public static final short SENSOR_TYPE_TWO_CHIP = 3;
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is Three-chip
-     * color area sensor.
-     */
-    public static final short SENSOR_TYPE_THREE_CHIP = 4;
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is Color
-     * sequential area sensor.
-     */
-    public static final short SENSOR_TYPE_COLOR_SEQUENTIAL = 5;
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is Trilinear
-     * sensor.
-     */
-    public static final short SENSOR_TYPE_TRILINEAR = 7;
-    /**
-     * The contant used by {@link #TAG_SENSING_METHOD} to denote the image sensor type is Color
-     * sequential linear sensor.
-     */
-    public static final short SENSOR_TYPE_COLOR_SEQUENTIAL_LINEAR = 8;
-
-    /**
-     * The contant used by {@link #TAG_FILE_SOURCE} to denote the source is other.
-     */
-    public static final short FILE_SOURCE_OTHER = 0;
-    /**
-     * The contant used by {@link #TAG_FILE_SOURCE} to denote the source is scanner of transparent
-     * type.
-     */
-    public static final short FILE_SOURCE_TRANSPARENT_SCANNER = 1;
-    /**
-     * The contant used by {@link #TAG_FILE_SOURCE} to denote the source is scanner of reflex type.
-     */
-    public static final short FILE_SOURCE_REFLEX_SCANNER = 2;
-    /**
-     * The contant used by {@link #TAG_FILE_SOURCE} to denote the source is DSC.
-     */
-    public static final short FILE_SOURCE_DSC = 3;
-
-    /**
-     * The contant used by {@link #TAG_SCENE_TYPE} to denote the scene is directly photographed.
-     */
-    public static final short SCENE_TYPE_DIRECTLY_PHOTOGRAPHED = 1;
-
-    /**
-     * The contant used by {@link #TAG_CUSTOM_RENDERED} to denote no special processing is used.
-     */
-    public static final short RENDERED_PROCESS_NORMAL = 0;
-    /**
-     * The contant used by {@link #TAG_CUSTOM_RENDERED} to denote special processing is used.
-     */
-    public static final short RENDERED_PROCESS_CUSTOM = 1;
-
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_MODE} to denote the exposure mode is Auto.
-     */
-    public static final short EXPOSURE_MODE_AUTO = 0;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_MODE} to denote the exposure mode is Manual.
-     */
-    public static final short EXPOSURE_MODE_MANUAL = 1;
-    /**
-     * The contant used by {@link #TAG_EXPOSURE_MODE} to denote the exposure mode is Auto bracket.
-     */
-    public static final short EXPOSURE_MODE_AUTO_BRACKET = 2;
-
-    /**
-     * The contant used by {@link #TAG_WHITE_BALANCE} to denote the white balance is Auto.
-     *
-     * @deprecated Use {@link #WHITE_BALANCE_AUTO} instead.
-     */
-    @Deprecated public static final int WHITEBALANCE_AUTO = 0;
-    /**
-     * The contant used by {@link #TAG_WHITE_BALANCE} to denote the white balance is Manual.
-     *
-     * @deprecated Use {@link #WHITE_BALANCE_MANUAL} instead.
-     */
-    @Deprecated public static final int WHITEBALANCE_MANUAL = 1;
-    /**
-     * The contant used by {@link #TAG_WHITE_BALANCE} to denote the white balance is Auto.
-     */
-    public static final short WHITE_BALANCE_AUTO = 0;
-    /**
-     * The contant used by {@link #TAG_WHITE_BALANCE} to denote the white balance is Manual.
-     */
-    public static final short WHITE_BALANCE_MANUAL = 1;
-
-    /**
-     * The contant used by {@link #TAG_SCENE_CAPTURE_TYPE} to denote the scene capture type is
-     * Standard.
-     */
-    public static final short SCENE_CAPTURE_TYPE_STANDARD = 0;
-    /**
-     * The contant used by {@link #TAG_SCENE_CAPTURE_TYPE} to denote the scene capture type is
-     * Landscape.
-     */
-    public static final short SCENE_CAPTURE_TYPE_LANDSCAPE = 1;
-    /**
-     * The contant used by {@link #TAG_SCENE_CAPTURE_TYPE} to denote the scene capture type is
-     * Portrait.
-     */
-    public static final short SCENE_CAPTURE_TYPE_PORTRAIT = 2;
-    /**
-     * The contant used by {@link #TAG_SCENE_CAPTURE_TYPE} to denote the scene capture type is Night
-     * scene.
-     */
-    public static final short SCENE_CAPTURE_TYPE_NIGHT = 3;
-
-    /**
-     * The contant used by {@link #TAG_GAIN_CONTROL} to denote none gain adjustment.
-     */
-    public static final short GAIN_CONTROL_NONE = 0;
-    /**
-     * The contant used by {@link #TAG_GAIN_CONTROL} to denote low gain up.
-     */
-    public static final short GAIN_CONTROL_LOW_GAIN_UP = 1;
-    /**
-     * The contant used by {@link #TAG_GAIN_CONTROL} to denote high gain up.
-     */
-    public static final short GAIN_CONTROL_HIGH_GAIN_UP = 2;
-    /**
-     * The contant used by {@link #TAG_GAIN_CONTROL} to denote low gain down.
-     */
-    public static final short GAIN_CONTROL_LOW_GAIN_DOWN = 3;
-    /**
-     * The contant used by {@link #TAG_GAIN_CONTROL} to denote high gain down.
-     */
-    public static final short GAIN_CONTROL_HIGH_GAIN_DOWN = 4;
-
-    /**
-     * The contant used by {@link #TAG_CONTRAST} to denote normal contrast.
-     */
-    public static final short CONTRAST_NORMAL = 0;
-    /**
-     * The contant used by {@link #TAG_CONTRAST} to denote soft contrast.
-     */
-    public static final short CONTRAST_SOFT = 1;
-    /**
-     * The contant used by {@link #TAG_CONTRAST} to denote hard contrast.
-     */
-    public static final short CONTRAST_HARD = 2;
-
-    /**
-     * The contant used by {@link #TAG_SATURATION} to denote normal saturation.
-     */
-    public static final short SATURATION_NORMAL = 0;
-    /**
-     * The contant used by {@link #TAG_SATURATION} to denote low saturation.
-     */
-    public static final short SATURATION_LOW = 0;
-    /**
-     * The contant used by {@link #TAG_SHARPNESS} to denote high saturation.
-     */
-    public static final short SATURATION_HIGH = 0;
-
-    /**
-     * The contant used by {@link #TAG_SHARPNESS} to denote normal sharpness.
-     */
-    public static final short SHARPNESS_NORMAL = 0;
-    /**
-     * The contant used by {@link #TAG_SHARPNESS} to denote soft sharpness.
-     */
-    public static final short SHARPNESS_SOFT = 1;
-    /**
-     * The contant used by {@link #TAG_SHARPNESS} to denote hard sharpness.
-     */
-    public static final short SHARPNESS_HARD = 2;
-
-    /**
-     * The contant used by {@link #TAG_SUBJECT_DISTANCE_RANGE} to denote the subject distance range
-     * is unknown.
-     */
-    public static final short SUBJECT_DISTANCE_RANGE_UNKNOWN = 0;
-    /**
-     * The contant used by {@link #TAG_SUBJECT_DISTANCE_RANGE} to denote the subject distance range
-     * is Macro.
-     */
-    public static final short SUBJECT_DISTANCE_RANGE_MACRO = 1;
-    /**
-     * The contant used by {@link #TAG_SUBJECT_DISTANCE_RANGE} to denote the subject distance range
-     * is Close view.
-     */
-    public static final short SUBJECT_DISTANCE_RANGE_CLOSE_VIEW = 2;
-    /**
-     * The contant used by {@link #TAG_SUBJECT_DISTANCE_RANGE} to denote the subject distance range
-     * is Distant view.
-     */
-    public static final short SUBJECT_DISTANCE_RANGE_DISTANT_VIEW = 3;
-
-    /**
-     * The contant used by GPS latitude-related tags to denote the latitude is North latitude.
-     *
-     * @see #TAG_GPS_LATITUDE_REF
-     * @see #TAG_GPS_DEST_LATITUDE_REF
-     */
-    public static final String LATITUDE_NORTH = "N";
-    /**
-     * The contant used by GPS latitude-related tags to denote the latitude is South latitude.
-     *
-     * @see #TAG_GPS_LATITUDE_REF
-     * @see #TAG_GPS_DEST_LATITUDE_REF
-     */
-    public static final String LATITUDE_SOUTH = "S";
-
-    /**
-     * The contant used by GPS longitude-related tags to denote the longitude is East longitude.
-     *
-     * @see #TAG_GPS_LONGITUDE_REF
-     * @see #TAG_GPS_DEST_LONGITUDE_REF
-     */
-    public static final String LONGITUDE_EAST = "E";
-    /**
-     * The contant used by GPS longitude-related tags to denote the longitude is West longitude.
-     *
-     * @see #TAG_GPS_LONGITUDE_REF
-     * @see #TAG_GPS_DEST_LONGITUDE_REF
-     */
-    public static final String LONGITUDE_WEST = "W";
-
-    /**
-     * The contant used by {@link #TAG_GPS_ALTITUDE_REF} to denote the altitude is above sea level.
-     */
-    public static final short ALTITUDE_ABOVE_SEA_LEVEL = 0;
-    /**
-     * The contant used by {@link #TAG_GPS_ALTITUDE_REF} to denote the altitude is below sea level.
-     */
-    public static final short ALTITUDE_BELOW_SEA_LEVEL = 1;
-
-    /**
-     * The contant used by {@link #TAG_GPS_STATUS} to denote GPS measurement is in progress.
-     */
-    public static final String GPS_MEASUREMENT_IN_PROGRESS = "A";
-    /**
-     * The contant used by {@link #TAG_GPS_STATUS} to denote GPS measurement is interrupted.
-     */
-    public static final String GPS_MEASUREMENT_INTERRUPTED = "V";
-
-    /**
-     * The contant used by {@link #TAG_GPS_MEASURE_MODE} to denote GPS measurement is 2-dimensional.
-     */
-    public static final String GPS_MEASUREMENT_2D = "2";
-    /**
-     * The contant used by {@link #TAG_GPS_MEASURE_MODE} to denote GPS measurement is 3-dimensional.
-     */
-    public static final String GPS_MEASUREMENT_3D = "3";
-
-    /**
-     * The contant used by {@link #TAG_GPS_SPEED_REF} to denote the speed unit is kilometers per
-     * hour.
-     */
-    public static final String GPS_SPEED_KILOMETERS_PER_HOUR = "K";
-    /**
-     * The contant used by {@link #TAG_GPS_SPEED_REF} to denote the speed unit is miles per hour.
-     */
-    public static final String GPS_SPEED_MILES_PER_HOUR = "M";
-    /**
-     * The contant used by {@link #TAG_GPS_SPEED_REF} to denote the speed unit is knots.
-     */
-    public static final String GPS_SPEED_KNOTS = "N";
-
-    /**
-     * The contant used by GPS attributes to denote the direction is true direction.
-     */
-    public static final String GPS_DIRECTION_TRUE = "T";
-    /**
-     * The contant used by GPS attributes to denote the direction is magnetic direction.
-     */
-    public static final String GPS_DIRECTION_MAGNETIC = "M";
-
-    /**
-     * The contant used by {@link #TAG_GPS_DEST_DISTANCE_REF} to denote the distance unit is
-     * kilometers.
-     */
-    public static final String GPS_DISTANCE_KILOMETERS = "K";
-    /**
-     * The contant used by {@link #TAG_GPS_DEST_DISTANCE_REF} to denote the distance unit is miles.
-     */
-    public static final String GPS_DISTANCE_MILES = "M";
-    /**
-     * The contant used by {@link #TAG_GPS_DEST_DISTANCE_REF} to denote the distance unit is
-     * nautical miles.
-     */
-    public static final String GPS_DISTANCE_NAUTICAL_MILES = "N";
-
-    /**
-     * The contant used by {@link #TAG_GPS_DIFFERENTIAL} to denote no differential correction is
-     * applied.
-     */
-    public static final short GPS_MEASUREMENT_NO_DIFFERENTIAL = 0;
-    /**
-     * The contant used by {@link #TAG_GPS_DIFFERENTIAL} to denote differential correction is
-     * applied.
-     */
-    public static final short GPS_MEASUREMENT_DIFFERENTIAL_CORRECTED = 1;
-
-    /**
-     * The constant used by {@link #TAG_COMPRESSION} to denote the image is not compressed.
-     */
-    public static final int DATA_UNCOMPRESSED = 1;
-    /**
-     * The constant used by {@link #TAG_COMPRESSION} to denote the image is huffman compressed.
-     */
-    public static final int DATA_HUFFMAN_COMPRESSED = 2;
-    /**
-     * The constant used by {@link #TAG_COMPRESSION} to denote the image is JPEG.
-     */
-    public static final int DATA_JPEG = 6;
-    /**
-     * The constant used by {@link #TAG_COMPRESSION}, see DNG Specification 1.4.0.0.
-     * Section 3, Compression
-     */
-    public static final int DATA_JPEG_COMPRESSED = 7;
-    /**
-     * The constant used by {@link #TAG_COMPRESSION}, see DNG Specification 1.4.0.0.
-     * Section 3, Compression
-     */
-    public static final int DATA_DEFLATE_ZIP = 8;
-    /**
-     * The constant used by {@link #TAG_COMPRESSION} to denote the image is pack-bits compressed.
-     */
-    public static final int DATA_PACK_BITS_COMPRESSED = 32773;
-    /**
-     * The constant used by {@link #TAG_COMPRESSION}, see DNG Specification 1.4.0.0.
-     * Section 3, Compression
-     */
-    public static final int DATA_LOSSY_JPEG = 34892;
-
-    /**
-     * The constant used by {@link #TAG_BITS_PER_SAMPLE}.
-     * See JEITA CP-3451C Spec Section 6, Differences from Palette Color Images
-     */
-    public static final int[] BITS_PER_SAMPLE_RGB = new int[] { 8, 8, 8 };
-    /**
-     * The constant used by {@link #TAG_BITS_PER_SAMPLE}.
-     * See JEITA CP-3451C Spec Section 4, Differences from Bilevel Images
-     */
-    public static final int[] BITS_PER_SAMPLE_GREYSCALE_1 = new int[] { 4 };
-    /**
-     * The constant used by {@link #TAG_BITS_PER_SAMPLE}.
-     * See JEITA CP-3451C Spec Section 4, Differences from Bilevel Images
-     */
-    public static final int[] BITS_PER_SAMPLE_GREYSCALE_2 = new int[] { 8 };
-
-    /**
-     * The constant used by {@link #TAG_PHOTOMETRIC_INTERPRETATION}.
-     */
-    public static final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0;
-    /**
-     * The constant used by {@link #TAG_PHOTOMETRIC_INTERPRETATION}.
-     */
-    public static final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1;
-    /**
-     * The constant used by {@link #TAG_PHOTOMETRIC_INTERPRETATION}.
-     */
-    public static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
-    /**
-     * The constant used by {@link #TAG_PHOTOMETRIC_INTERPRETATION}.
-     */
-    public static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6;
-
-    /**
-     * The constant used by {@link #TAG_NEW_SUBFILE_TYPE}. See JEITA CP-3451C Spec Section 8.
-     */
-    public static final int ORIGINAL_RESOLUTION_IMAGE = 0;
-    /**
-     * The constant used by {@link #TAG_NEW_SUBFILE_TYPE}. See JEITA CP-3451C Spec Section 8.
-     */
-    public static final int REDUCED_RESOLUTION_IMAGE = 1;
-
-    // Maximum size for checking file type signature (see image_type_recognition_lite.cc)
-    private static final int SIGNATURE_CHECK_SIZE = 5000;
-
-    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;
-    private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
-
-    // See http://fileformats.archiveteam.org/wiki/Olympus_ORF
-    private static final short ORF_SIGNATURE_1 = 0x4f52;
-    private static final short ORF_SIGNATURE_2 = 0x5352;
-    // There are two formats for Olympus Makernote Headers. Each has different identifiers and
-    // offsets to the actual data.
-    // See http://www.exiv2.org/makernote.html#R1
-    private static final byte[] ORF_MAKER_NOTE_HEADER_1 = new byte[] {(byte) 0x4f, (byte) 0x4c,
-            (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x00}; // "OLYMP\0"
-    private static final byte[] ORF_MAKER_NOTE_HEADER_2 = new byte[] {(byte) 0x4f, (byte) 0x4c,
-            (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x55, (byte) 0x53, (byte) 0x00,
-            (byte) 0x49, (byte) 0x49}; // "OLYMPUS\0II"
-    private static final int ORF_MAKER_NOTE_HEADER_1_SIZE = 8;
-    private static final int ORF_MAKER_NOTE_HEADER_2_SIZE = 12;
-
-    // See http://fileformats.archiveteam.org/wiki/RW2
-    private static final short RW2_SIGNATURE = 0x0055;
-
-    // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
-    private static final String PEF_SIGNATURE = "PENTAX";
-    // See http://www.exiv2.org/makernote.html#R11
-    private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6;
-
-    private static SimpleDateFormat sFormatter;
-
-    // See Exchangeable image file format for digital still cameras: Exif version 2.2.
-    // The following values are for parsing EXIF data area. There are tag groups in EXIF data area.
-    // They are called "Image File Directory". They have multiple data formats to cover various
-    // image metadata from GPS longitude to camera model name.
-
-    // Types of Exif byte alignments (see JEITA CP-3451C Section 4.5.2)
-    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)
-    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".)
-    private static final int IFD_FORMAT_BYTE = 1;
-    private static final int IFD_FORMAT_STRING = 2;
-    private static final int IFD_FORMAT_USHORT = 3;
-    private static final int IFD_FORMAT_ULONG = 4;
-    private static final int IFD_FORMAT_URATIONAL = 5;
-    private static final int IFD_FORMAT_SBYTE = 6;
-    private static final int IFD_FORMAT_UNDEFINED = 7;
-    private static final int IFD_FORMAT_SSHORT = 8;
-    private static final int IFD_FORMAT_SLONG = 9;
-    private static final int IFD_FORMAT_SRATIONAL = 10;
-    private static final int IFD_FORMAT_SINGLE = 11;
-    private static final int IFD_FORMAT_DOUBLE = 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.
-    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
-    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[] {
-            0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
-    };
-
-    // A class for indicating EXIF rational type.
-    private static class Rational {
-        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) {
-                this.numerator = 0;
-                this.denominator = 1;
-                return;
-            }
-            this.numerator = numerator;
-            this.denominator = denominator;
-        }
-
-        @Override
-        public String toString() {
-            return numerator + "/" + denominator;
-        }
-
-        public double calculate() {
-            return (double) numerator / denominator;
-        }
-    }
-
-    // A class for indicating EXIF attribute.
-    private static class ExifAttribute {
-        public final int format;
-        public final int numberOfComponents;
-        public final byte[] bytes;
-
-        private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
-            this.format = format;
-            this.numberOfComponents = numberOfComponents;
-            this.bytes = bytes;
-        }
-
-        public static ExifAttribute createUShort(int[] values, ByteOrder byteOrder) {
-            final ByteBuffer buffer = ByteBuffer.wrap(
-                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]);
-            buffer.order(byteOrder);
-            for (int value : values) {
-                buffer.putShort((short) value);
-            }
-            return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array());
-        }
-
-        public static ExifAttribute createUShort(int value, ByteOrder byteOrder) {
-            return createUShort(new int[] {value}, byteOrder);
-        }
-
-        public static ExifAttribute createULong(long[] values, ByteOrder byteOrder) {
-            final ByteBuffer buffer = ByteBuffer.wrap(
-                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]);
-            buffer.order(byteOrder);
-            for (long value : values) {
-                buffer.putInt((int) value);
-            }
-            return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array());
-        }
-
-        public static ExifAttribute createULong(long value, ByteOrder byteOrder) {
-            return createULong(new long[] {value}, byteOrder);
-        }
-
-        public static ExifAttribute createSLong(int[] values, ByteOrder byteOrder) {
-            final ByteBuffer buffer = ByteBuffer.wrap(
-                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]);
-            buffer.order(byteOrder);
-            for (int value : values) {
-                buffer.putInt(value);
-            }
-            return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
-        }
-
-        public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
-            return createSLong(new int[] {value}, byteOrder);
-        }
-
-        public static ExifAttribute createByte(String value) {
-            // Exception for GPSAltitudeRef tag
-            if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
-                final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') };
-                return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes);
-            }
-            final byte[] ascii = value.getBytes(ASCII);
-            return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii);
-        }
-
-        public static ExifAttribute createString(String value) {
-            final byte[] ascii = (value + '\0').getBytes(ASCII);
-            return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii);
-        }
-
-        public static ExifAttribute createURational(Rational[] values, ByteOrder byteOrder) {
-            final ByteBuffer buffer = ByteBuffer.wrap(
-                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]);
-            buffer.order(byteOrder);
-            for (Rational value : values) {
-                buffer.putInt((int) value.numerator);
-                buffer.putInt((int) value.denominator);
-            }
-            return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array());
-        }
-
-        public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) {
-            return createURational(new Rational[] {value}, byteOrder);
-        }
-
-        public static ExifAttribute createSRational(Rational[] values, ByteOrder byteOrder) {
-            final ByteBuffer buffer = ByteBuffer.wrap(
-                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]);
-            buffer.order(byteOrder);
-            for (Rational value : values) {
-                buffer.putInt((int) value.numerator);
-                buffer.putInt((int) value.denominator);
-            }
-            return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
-        }
-
-        public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
-            return createSRational(new Rational[] {value}, byteOrder);
-        }
-
-        public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) {
-            final ByteBuffer buffer = ByteBuffer.wrap(
-                    new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
-            buffer.order(byteOrder);
-            for (double value : values) {
-                buffer.putDouble(value);
-            }
-            return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
-        }
-
-        public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
-            return createDouble(new double[] {value}, byteOrder);
-        }
-
-        @Override
-        public String toString() {
-            return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
-        }
-
-        private Object getValue(ByteOrder byteOrder) {
-            ByteOrderedDataInputStream inputStream = null;
-            try {
-                inputStream = new ByteOrderedDataInputStream(bytes);
-                inputStream.setByteOrder(byteOrder);
-                switch (format) {
-                    case IFD_FORMAT_BYTE:
-                    case IFD_FORMAT_SBYTE: {
-                        // Exception for GPSAltitudeRef tag
-                        if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) {
-                            return new String(new char[] { (char) (bytes[0] + '0') });
-                        }
-                        return new String(bytes, ASCII);
-                    }
-                    case IFD_FORMAT_UNDEFINED:
-                    case IFD_FORMAT_STRING: {
-                        int index = 0;
-                        if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
-                            boolean same = true;
-                            for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
-                                if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
-                                    same = false;
-                                    break;
-                                }
-                            }
-                            if (same) {
-                                index = EXIF_ASCII_PREFIX.length;
-                            }
-                        }
-
-                        StringBuilder stringBuilder = new StringBuilder();
-                        while (index < numberOfComponents) {
-                            int ch = bytes[index];
-                            if (ch == 0) {
-                                break;
-                            }
-                            if (ch >= 32) {
-                                stringBuilder.append((char) ch);
-                            } else {
-                                stringBuilder.append('?');
-                            }
-                            ++index;
-                        }
-                        return stringBuilder.toString();
-                    }
-                    case IFD_FORMAT_USHORT: {
-                        final int[] values = new int[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            values[i] = inputStream.readUnsignedShort();
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_ULONG: {
-                        final long[] values = new long[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            values[i] = inputStream.readUnsignedInt();
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_URATIONAL: {
-                        final Rational[] values = new Rational[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            final long numerator = inputStream.readUnsignedInt();
-                            final long denominator = inputStream.readUnsignedInt();
-                            values[i] = new Rational(numerator, denominator);
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_SSHORT: {
-                        final int[] values = new int[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            values[i] = inputStream.readShort();
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_SLONG: {
-                        final int[] values = new int[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            values[i] = inputStream.readInt();
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_SRATIONAL: {
-                        final Rational[] values = new Rational[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            final long numerator = inputStream.readInt();
-                            final long denominator = inputStream.readInt();
-                            values[i] = new Rational(numerator, denominator);
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_SINGLE: {
-                        final double[] values = new double[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            values[i] = inputStream.readFloat();
-                        }
-                        return values;
-                    }
-                    case IFD_FORMAT_DOUBLE: {
-                        final double[] values = new double[numberOfComponents];
-                        for (int i = 0; i < numberOfComponents; ++i) {
-                            values[i] = inputStream.readDouble();
-                        }
-                        return values;
-                    }
-                    default:
-                        return null;
-                }
-            } catch (IOException e) {
-                Log.w(TAG, "IOException occurred during reading a value", e);
-                return null;
-            } finally {
-                if (inputStream != null) {
-                    try {
-                        inputStream.close();
-                    } catch (IOException e) {
-                        Log.e(TAG, "IOException occurred while closing InputStream", e);
-                    }
-                }
-            }
-        }
-
-        public double getDoubleValue(ByteOrder byteOrder) {
-            Object value = getValue(byteOrder);
-            if (value == null) {
-                throw new NumberFormatException("NULL can't be converted to a double value");
-            }
-            if (value instanceof String) {
-                return Double.parseDouble((String) value);
-            }
-            if (value instanceof long[]) {
-                long[] array = (long[]) value;
-                if (array.length == 1) {
-                    return array[0];
-                }
-                throw new NumberFormatException("There are more than one component");
-            }
-            if (value instanceof int[]) {
-                int[] array = (int[]) value;
-                if (array.length == 1) {
-                    return array[0];
-                }
-                throw new NumberFormatException("There are more than one component");
-            }
-            if (value instanceof double[]) {
-                double[] array = (double[]) value;
-                if (array.length == 1) {
-                    return array[0];
-                }
-                throw new NumberFormatException("There are more than one component");
-            }
-            if (value instanceof Rational[]) {
-                Rational[] array = (Rational[]) value;
-                if (array.length == 1) {
-                    return array[0].calculate();
-                }
-                throw new NumberFormatException("There are more than one component");
-            }
-            throw new NumberFormatException("Couldn't find a double value");
-        }
-
-        public int getIntValue(ByteOrder byteOrder) {
-            Object value = getValue(byteOrder);
-            if (value == null) {
-                throw new NumberFormatException("NULL can't be converted to a integer value");
-            }
-            if (value instanceof String) {
-                return Integer.parseInt((String) value);
-            }
-            if (value instanceof long[]) {
-                long[] array = (long[]) value;
-                if (array.length == 1) {
-                    return (int) array[0];
-                }
-                throw new NumberFormatException("There are more than one component");
-            }
-            if (value instanceof int[]) {
-                int[] array = (int[]) value;
-                if (array.length == 1) {
-                    return array[0];
-                }
-                throw new NumberFormatException("There are more than one component");
-            }
-            throw new NumberFormatException("Couldn't find a integer value");
-        }
-
-        public String getStringValue(ByteOrder byteOrder) {
-            Object value = getValue(byteOrder);
-            if (value == null) {
-                return null;
-            }
-            if (value instanceof String) {
-                return (String) value;
-            }
-
-            final StringBuilder stringBuilder = new StringBuilder();
-            if (value instanceof long[]) {
-                long[] array = (long[]) value;
-                for (int i = 0; i < array.length; ++i) {
-                    stringBuilder.append(array[i]);
-                    if (i + 1 != array.length) {
-                        stringBuilder.append(",");
-                    }
-                }
-                return stringBuilder.toString();
-            }
-            if (value instanceof int[]) {
-                int[] array = (int[]) value;
-                for (int i = 0; i < array.length; ++i) {
-                    stringBuilder.append(array[i]);
-                    if (i + 1 != array.length) {
-                        stringBuilder.append(",");
-                    }
-                }
-                return stringBuilder.toString();
-            }
-            if (value instanceof double[]) {
-                double[] array = (double[]) value;
-                for (int i = 0; i < array.length; ++i) {
-                    stringBuilder.append(array[i]);
-                    if (i + 1 != array.length) {
-                        stringBuilder.append(",");
-                    }
-                }
-                return stringBuilder.toString();
-            }
-            if (value instanceof Rational[]) {
-                Rational[] array = (Rational[]) value;
-                for (int i = 0; i < array.length; ++i) {
-                    stringBuilder.append(array[i].numerator);
-                    stringBuilder.append('/');
-                    stringBuilder.append(array[i].denominator);
-                    if (i + 1 != array.length) {
-                        stringBuilder.append(",");
-                    }
-                }
-                return stringBuilder.toString();
-            }
-            return null;
-        }
-
-        public int size() {
-            return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents;
-        }
-    }
-
-    // A class for indicating EXIF tag.
-    static class ExifTag {
-        public final int number;
-        public final String name;
-        public final int primaryFormat;
-        public final int secondaryFormat;
-
-        private ExifTag(String name, int number, int format) {
-            this.name = name;
-            this.number = number;
-            this.primaryFormat = format;
-            this.secondaryFormat = -1;
-        }
-
-        private ExifTag(String name, int number, int primaryFormat, int secondaryFormat) {
-            this.name = name;
-            this.number = number;
-            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)
-    private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
-            // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
-            new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
-            new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
-            new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
-            new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
-            new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
-            new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
-            new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
-            // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1.
-            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
-            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
-            // RW2 file tags
-            // See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PanasonicRaw.html)
-            new ExifTag(TAG_RW2_SENSOR_TOP_BORDER, 4, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_RW2_SENSOR_LEFT_BORDER, 5, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_RW2_SENSOR_BOTTOM_BORDER, 6, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_RW2_SENSOR_RIGHT_BORDER, 7, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_RW2_ISO, 23, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_RW2_JPG_FROM_RAW, 46, IFD_FORMAT_UNDEFINED)
-    };
-
-    // Primary image IFD Exif Private tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
-    private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
-            new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_F_NUMBER, 33437, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING),
-            new ExifTag(TAG_PHOTOGRAPHIC_SENSITIVITY, 34855, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
-            new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
-            new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
-            new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
-            new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
-            new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
-            new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
-            new ExifTag(TAG_SUBSEC_TIME_ORIGINAL, 37521, IFD_FORMAT_STRING),
-            new ExifTag(TAG_SUBSEC_TIME_DIGITIZED, 37522, IFD_FORMAT_STRING),
-            new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING),
-            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING),
-            new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
-            new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
-    };
-
-    // Primary image IFD GPS Info tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
-    private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
-            new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE),
-            new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE),
-            new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_SATELLITES, 8, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_STATUS, 9, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_MEASURE_MODE, 10, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DOP, 11, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_SPEED, 13, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_TRACK, 15, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_IMG_DIRECTION, 17, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_MAP_DATUM, 18, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DEST_LATITUDE, 20, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DEST_LONGITUDE, 22, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DEST_BEARING, 24, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DEST_DISTANCE, 26, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_GPS_PROCESSING_METHOD, 27, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_GPS_AREA_INFORMATION, 28, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_GPS_DATESTAMP, 29, IFD_FORMAT_STRING),
-            new ExifTag(TAG_GPS_DIFFERENTIAL, 30, IFD_FORMAT_USHORT)
-    };
-    // Primary image IFD Interoperability tag (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
-    private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
-            new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING)
-    };
-    // IFD Thumbnail tags (See JEITA CP-3451C Section 4.6.8 Tag Support Levels)
-    private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
-            // For below two, see TIFF 6.0 Spec Section 3: Bilevel Images.
-            new ExifTag(TAG_NEW_SUBFILE_TYPE, 254, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_SUBFILE_TYPE, 255, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
-            new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
-            new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
-            new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
-            new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
-            new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
-            new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
-            // See Adobe PageMaker® 6.0 TIFF Technical Notes, Note 1.
-            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
-            new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
-            new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
-            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_DNG_VERSION, 50706, IFD_FORMAT_BYTE),
-            new ExifTag(TAG_DEFAULT_CROP_SIZE, 50720, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG)
-    };
-
-    // RAF file tag (See piex.cc line 372)
-    private static final ExifTag TAG_RAF_IMAGE_SIZE =
-            new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT);
-
-    // ORF file tags (See http://www.exiv2.org/tags-olympus.html)
-    private static final ExifTag[] ORF_MAKER_NOTE_TAGS = new ExifTag[] {
-            new ExifTag(TAG_ORF_THUMBNAIL_IMAGE, 256, IFD_FORMAT_UNDEFINED),
-            new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_ULONG)
-    };
-    private static final ExifTag[] ORF_CAMERA_SETTINGS_TAGS = new ExifTag[] {
-            new ExifTag(TAG_ORF_PREVIEW_IMAGE_START, 257, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_ORF_PREVIEW_IMAGE_LENGTH, 258, IFD_FORMAT_ULONG)
-    };
-    private static final ExifTag[] ORF_IMAGE_PROCESSING_TAGS = new ExifTag[] {
-            new ExifTag(TAG_ORF_ASPECT_FRAME, 4371, IFD_FORMAT_USHORT)
-    };
-    // PEF file tag (See http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html)
-    private static final ExifTag[] PEF_TAGS = new ExifTag[] {
-            new ExifTag(TAG_COLOR_SPACE, 55, IFD_FORMAT_USHORT)
-    };
-
-    // See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
-    // The following values are used for indicating pointers to the other Image File Directories.
-
-    // Indices of Exif Ifd tag groups
-    /** @hide */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY,
-            IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE,
-            IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF})
-    public @interface IfdType {}
-
-    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;
-    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
-    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
-    };
-    // List of tags for pointing to the other image file directory offset.
-    private static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[] {
-            new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE),
-            new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
-    };
-
-    // Tags for indicating the thumbnail offset and length
-    private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
-            new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
-    private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
-            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.
-    @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.
-    @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.
-    @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
-    // not only getting information from EXIF but also from some JPEG special segments such as
-    // MARKER_COM for user comment and MARKER_SOFx for image width and height.
-
-    private static final Charset ASCII = Charset.forName("US-ASCII");
-    // Identifier for EXIF APP1 segment in JPEG
-    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.
-    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;
-    private static final byte MARKER_SOF2 = (byte) 0xc2;
-    private static final byte MARKER_SOF3 = (byte) 0xc3;
-    private static final byte MARKER_SOF5 = (byte) 0xc5;
-    private static final byte MARKER_SOF6 = (byte) 0xc6;
-    private static final byte MARKER_SOF7 = (byte) 0xc7;
-    private static final byte MARKER_SOF9 = (byte) 0xc9;
-    private static final byte MARKER_SOF10 = (byte) 0xca;
-    private static final byte MARKER_SOF11 = (byte) 0xcb;
-    private static final byte MARKER_SOF13 = (byte) 0xcd;
-    private static final byte MARKER_SOF14 = (byte) 0xce;
-    private static final byte MARKER_SOF15 = (byte) 0xcf;
-    private static final byte MARKER_SOS = (byte) 0xda;
-    static final byte MARKER_APP1 = (byte) 0xe1;
-    private static final byte MARKER_COM = (byte) 0xfe;
-    static final byte MARKER_EOI = (byte) 0xd9;
-
-    // Supported Image File Types
-    private static final int IMAGE_TYPE_UNKNOWN = 0;
-    private static final int IMAGE_TYPE_ARW = 1;
-    private static final int IMAGE_TYPE_CR2 = 2;
-    private static final int IMAGE_TYPE_DNG = 3;
-    private static final int IMAGE_TYPE_JPEG = 4;
-    private static final int IMAGE_TYPE_NEF = 5;
-    private static final int IMAGE_TYPE_NRW = 6;
-    private static final int IMAGE_TYPE_ORF = 7;
-    private static final int IMAGE_TYPE_PEF = 8;
-    private static final int IMAGE_TYPE_RAF = 9;
-    private static final int IMAGE_TYPE_RW2 = 10;
-    private static final int IMAGE_TYPE_SRW = 11;
-
-    static {
-        sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
-        sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
-
-        // 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<>();
-            for (ExifTag tag : EXIF_TAGS[ifdType]) {
-                sExifTagMapsForReading[ifdType].put(tag.number, tag);
-                sExifTagMapsForWriting[ifdType].put(tag.name, tag);
-            }
-        }
-
-        // Build up the hash table to look up Exif pointer tags.
-        sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330
-        sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665
-        sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853
-        sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965
-        sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224
-        sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
-    }
-
-    private final String mFilename;
-    private final AssetManager.AssetInputStream mAssetInputStream;
-    private int mMimeType;
-    @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.
-    private int mThumbnailOffset;
-    private int mThumbnailLength;
-    private byte[] mThumbnailBytes;
-    private int mThumbnailCompression;
-    private int mExifOffset;
-    private int mOrfMakerNoteOffset;
-    private int mOrfThumbnailOffset;
-    private int mOrfThumbnailLength;
-    private int mRw2JpgFromRawOffset;
-    private boolean mIsSupportedFile;
-
-    // Pattern to check non zero timestamp
-    private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
-    // Pattern to check gps timestamp
-    private static final Pattern sGpsTimestampPattern =
-            Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
-
-    /**
-     * Reads Exif tags from the specified image file.
-     */
-    public ExifInterface(@NonNull String filename) throws IOException {
-        if (filename == null) {
-            throw new IllegalArgumentException("filename cannot be null");
-        }
-        FileInputStream in = null;
-        mAssetInputStream = null;
-        mFilename = filename;
-        try {
-            in = new FileInputStream(filename);
-            loadAttributes(in);
-        } finally {
-            closeQuietly(in);
-        }
-    }
-
-    /**
-     * Reads Exif tags from the specified image input stream. Attribute mutation is not supported
-     * for input streams. The given input stream will proceed its current position. Developers
-     * should close the input stream after use. This constructor is not intended to be used with
-     * an input stream that performs any networking operations.
-     */
-    public ExifInterface(@NonNull InputStream inputStream) throws IOException {
-        if (inputStream == null) {
-            throw new IllegalArgumentException("inputStream cannot be null");
-        }
-        mFilename = null;
-        if (inputStream instanceof AssetManager.AssetInputStream) {
-            mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
-        } else {
-            mAssetInputStream = null;
-        }
-        loadAttributes(inputStream);
-    }
-
-    /**
-     * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
-     * the image file.
-     *
-     * @param tag the name of the tag.
-     */
-    @Nullable
-    private ExifAttribute getExifAttribute(@NonNull String tag) {
-        if (TAG_ISO_SPEED_RATINGS.equals(tag)) {
-            if (DEBUG) {
-                Log.d(TAG, "getExifAttribute: Replacing TAG_ISO_SPEED_RATINGS with "
-                        + "TAG_PHOTOGRAPHIC_SENSITIVITY.");
-            }
-            tag = TAG_PHOTOGRAPHIC_SENSITIVITY;
-        }
-        // 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) {
-            ExifAttribute value = mAttributes[i].get(tag);
-            if (value != null) {
-                return value;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the value of the specified tag or {@code null} if there
-     * is no such tag in the image file.
-     *
-     * @param tag the name of the tag.
-     */
-    @Nullable
-    public String getAttribute(@NonNull String tag) {
-        ExifAttribute attribute = getExifAttribute(tag);
-        if (attribute != null) {
-            if (!sTagSetForCompatibility.contains(tag)) {
-                return attribute.getStringValue(mExifByteOrder);
-            }
-            if (tag.equals(TAG_GPS_TIMESTAMP)) {
-                // Convert the rational values to the custom formats for backwards compatibility.
-                if (attribute.format != IFD_FORMAT_URATIONAL
-                        && attribute.format != IFD_FORMAT_SRATIONAL) {
-                    Log.w(TAG, "GPS Timestamp format is not rational. format=" + attribute.format);
-                    return null;
-                }
-                Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
-                if (array == null || array.length != 3) {
-                    Log.w(TAG, "Invalid GPS Timestamp array. array=" + Arrays.toString(array));
-                    return null;
-                }
-                return String.format("%02d:%02d:%02d",
-                        (int) ((float) array[0].numerator / array[0].denominator),
-                        (int) ((float) array[1].numerator / array[1].denominator),
-                        (int) ((float) array[2].numerator / array[2].denominator));
-            }
-            try {
-                return Double.toString(attribute.getDoubleValue(mExifByteOrder));
-            } catch (NumberFormatException e) {
-                return null;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the integer value of the specified tag. If there is no such tag
-     * in the image file or the value cannot be parsed as integer, return
-     * <var>defaultValue</var>.
-     *
-     * @param tag the name of the tag.
-     * @param defaultValue the value to return if the tag is not available.
-     */
-    public int getAttributeInt(@NonNull String tag, int defaultValue) {
-        ExifAttribute exifAttribute = getExifAttribute(tag);
-        if (exifAttribute == null) {
-            return defaultValue;
-        }
-
-        try {
-            return exifAttribute.getIntValue(mExifByteOrder);
-        } catch (NumberFormatException e) {
-            return defaultValue;
-        }
-    }
-
-    /**
-     * Returns the double value of the tag that is specified as rational or contains a
-     * double-formatted value. If there is no such tag in the image file or the value cannot be
-     * parsed as double, return <var>defaultValue</var>.
-     *
-     * @param tag the name of the tag.
-     * @param defaultValue the value to return if the tag is not available.
-     */
-    public double getAttributeDouble(@NonNull String tag, double defaultValue) {
-        ExifAttribute exifAttribute = getExifAttribute(tag);
-        if (exifAttribute == null) {
-            return defaultValue;
-        }
-
-        try {
-            return exifAttribute.getDoubleValue(mExifByteOrder);
-        } catch (NumberFormatException e) {
-            return defaultValue;
-        }
-    }
-
-    /**
-     * Sets the value of the specified tag.
-     *
-     * @param tag the name of the tag.
-     * @param value the value of the tag.
-     */
-    public void setAttribute(@NonNull String tag, @Nullable String value) {
-        if (TAG_ISO_SPEED_RATINGS.equals(tag)) {
-            if (DEBUG) {
-                Log.d(TAG, "setAttribute: Replacing TAG_ISO_SPEED_RATINGS with "
-                        + "TAG_PHOTOGRAPHIC_SENSITIVITY.");
-            }
-            tag = TAG_PHOTOGRAPHIC_SENSITIVITY;
-        }
-        // Convert the given value to rational values for backwards compatibility.
-        if (value != null && sTagSetForCompatibility.contains(tag)) {
-            if (tag.equals(TAG_GPS_TIMESTAMP)) {
-                Matcher m = sGpsTimestampPattern.matcher(value);
-                if (!m.find()) {
-                    Log.w(TAG, "Invalid value for " + tag + " : " + value);
-                    return;
-                }
-                value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
-                        + Integer.parseInt(m.group(3)) + "/1";
-            } else {
-                try {
-                    double doubleValue = Double.parseDouble(value);
-                    value = new Rational(doubleValue).toString();
-                } catch (NumberFormatException e) {
-                    Log.w(TAG, "Invalid value for " + tag + " : " + value);
-                    return;
-                }
-            }
-        }
-
-        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
-            if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) {
-                continue;
-            }
-            final ExifTag exifTag = sExifTagMapsForWriting[i].get(tag);
-            if (exifTag != null) {
-                if (value == null) {
-                    mAttributes[i].remove(tag);
-                    continue;
-                }
-                Pair<Integer, Integer> guess = guessDataFormat(value);
-                int dataFormat;
-                if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
-                    dataFormat = exifTag.primaryFormat;
-                } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
-                        || exifTag.secondaryFormat == guess.second)) {
-                    dataFormat = exifTag.secondaryFormat;
-                } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
-                        || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
-                        || exifTag.primaryFormat == IFD_FORMAT_STRING) {
-                    dataFormat = exifTag.primaryFormat;
-                } else {
-                    Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
-                            + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
-                            + (exifTag.secondaryFormat == -1 ? "" : ", "
-                            + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
-                            + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
-                            + IFD_FORMAT_NAMES[guess.second]) + ")");
-                    continue;
-                }
-                switch (dataFormat) {
-                    case IFD_FORMAT_BYTE: {
-                        mAttributes[i].put(tag, ExifAttribute.createByte(value));
-                        break;
-                    }
-                    case IFD_FORMAT_UNDEFINED:
-                    case IFD_FORMAT_STRING: {
-                        mAttributes[i].put(tag, ExifAttribute.createString(value));
-                        break;
-                    }
-                    case IFD_FORMAT_USHORT: {
-                        final String[] values = value.split(",");
-                        final int[] intArray = new int[values.length];
-                        for (int j = 0; j < values.length; ++j) {
-                            intArray[j] = Integer.parseInt(values[j]);
-                        }
-                        mAttributes[i].put(tag,
-                                ExifAttribute.createUShort(intArray, mExifByteOrder));
-                        break;
-                    }
-                    case IFD_FORMAT_SLONG: {
-                        final String[] values = value.split(",");
-                        final int[] intArray = new int[values.length];
-                        for (int j = 0; j < values.length; ++j) {
-                            intArray[j] = Integer.parseInt(values[j]);
-                        }
-                        mAttributes[i].put(tag,
-                                ExifAttribute.createSLong(intArray, mExifByteOrder));
-                        break;
-                    }
-                    case IFD_FORMAT_ULONG: {
-                        final String[] values = value.split(",");
-                        final long[] longArray = new long[values.length];
-                        for (int j = 0; j < values.length; ++j) {
-                            longArray[j] = Long.parseLong(values[j]);
-                        }
-                        mAttributes[i].put(tag,
-                                ExifAttribute.createULong(longArray, mExifByteOrder));
-                        break;
-                    }
-                    case IFD_FORMAT_URATIONAL: {
-                        final String[] values = value.split(",");
-                        final Rational[] rationalArray = new Rational[values.length];
-                        for (int j = 0; j < values.length; ++j) {
-                            final String[] numbers = values[j].split("/");
-                            rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
-                                    (long) Double.parseDouble(numbers[1]));
-                        }
-                        mAttributes[i].put(tag,
-                                ExifAttribute.createURational(rationalArray, mExifByteOrder));
-                        break;
-                    }
-                    case IFD_FORMAT_SRATIONAL: {
-                        final String[] values = value.split(",");
-                        final Rational[] rationalArray = new Rational[values.length];
-                        for (int j = 0; j < values.length; ++j) {
-                            final String[] numbers = values[j].split("/");
-                            rationalArray[j] = new Rational((long) Double.parseDouble(numbers[0]),
-                                    (long) Double.parseDouble(numbers[1]));
-                        }
-                        mAttributes[i].put(tag,
-                                ExifAttribute.createSRational(rationalArray, mExifByteOrder));
-                        break;
-                    }
-                    case IFD_FORMAT_DOUBLE: {
-                        final String[] values = value.split(",");
-                        final double[] doubleArray = new double[values.length];
-                        for (int j = 0; j < values.length; ++j) {
-                            doubleArray[j] = Double.parseDouble(values[j]);
-                        }
-                        mAttributes[i].put(tag,
-                                ExifAttribute.createDouble(doubleArray, mExifByteOrder));
-                        break;
-                    }
-                    default:
-                        Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
-                        continue;
-                }
-            }
-        }
-    }
-
-    /**
-     * 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));
-    }
-
-    /**
-     * Returns if the current image orientation is flipped.
-     *
-     * @see #getRotationDegrees()
-     */
-    public boolean isFlipped() {
-        int orientation = getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
-        switch (orientation) {
-            case ORIENTATION_FLIP_HORIZONTAL:
-            case ORIENTATION_TRANSVERSE:
-            case ORIENTATION_FLIP_VERTICAL:
-            case ORIENTATION_TRANSPOSE:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Returns the rotation degrees for the current image orientation. If the image is flipped,
-     * i.e., {@link #isFlipped()} returns {@code true}, the rotation degrees will be base on
-     * the assumption that the image is first flipped horizontally (along Y-axis), and then do
-     * the rotation. For example, {@link #ORIENTATION_TRANSPOSE} will be interpreted as flipped
-     * horizontally first, and then rotate 270 degrees clockwise.
-     *
-     * @return The rotation degrees of the image after the horizontal flipping is applied, if any.
-     *
-     * @see #isFlipped()
-     */
-    public int getRotationDegrees() {
-        int orientation = getAttributeInt(TAG_ORIENTATION, ORIENTATION_NORMAL);
-        switch (orientation) {
-            case ORIENTATION_ROTATE_90:
-            case ORIENTATION_TRANSVERSE:
-                return 90;
-            case ORIENTATION_ROTATE_180:
-            case ORIENTATION_FLIP_VERTICAL:
-                return 180;
-            case ORIENTATION_ROTATE_270:
-            case ORIENTATION_TRANSPOSE:
-                return 270;
-            case ORIENTATION_UNDEFINED:
-            case ORIENTATION_NORMAL:
-            case ORIENTATION_FLIP_HORIZONTAL:
-            default:
-                return 0;
-        }
-    }
-
-    /**
-     * 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.
-     * @param value the value of the tag in a form of {@link ExifAttribute}.
-     * @return Returns {@code true} if updating is placed.
-     */
-    private boolean updateAttribute(String tag, ExifAttribute value) {
-        boolean updated = false;
-        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
-            if (mAttributes[i].containsKey(tag)) {
-                mAttributes[i].put(tag, value);
-                updated = true;
-            }
-        }
-        return updated;
-    }
-
-    /**
-     * Remove any values of the specified tag.
-     *
-     * @param tag the name of the tag.
-     */
-    private void removeAttribute(String tag) {
-        for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
-            mAttributes[i].remove(tag);
-        }
-    }
-
-    /**
-     * This function decides which parser to read the image data according to the given input stream
-     * type and the content of the input stream. In each case, it reads the first three bytes to
-     * determine whether the image data format is JPEG or not.
-     */
-    private void loadAttributes(@NonNull InputStream in) throws IOException {
-        try {
-            // Initialize mAttributes.
-            for (int i = 0; i < EXIF_TAGS.length; ++i) {
-                mAttributes[i] = new HashMap<>();
-            }
-
-            // Check file type
-            in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
-            mMimeType = getMimeType((BufferedInputStream) in);
-
-            // Create byte-ordered input stream
-            ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
-
-            switch (mMimeType) {
-                case IMAGE_TYPE_JPEG: {
-                    getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
-                    break;
-                }
-                case IMAGE_TYPE_RAF: {
-                    getRafAttributes(inputStream);
-                    break;
-                }
-                case IMAGE_TYPE_ORF: {
-                    getOrfAttributes(inputStream);
-                    break;
-                }
-                case IMAGE_TYPE_RW2: {
-                    getRw2Attributes(inputStream);
-                    break;
-                }
-                case IMAGE_TYPE_ARW:
-                case IMAGE_TYPE_CR2:
-                case IMAGE_TYPE_DNG:
-                case IMAGE_TYPE_NEF:
-                case IMAGE_TYPE_NRW:
-                case IMAGE_TYPE_PEF:
-                case IMAGE_TYPE_SRW:
-                case IMAGE_TYPE_UNKNOWN: {
-                    getRawAttributes(inputStream);
-                    break;
-                }
-                default: {
-                    break;
-                }
-            }
-            // Set thumbnail image offset and length
-            setThumbnailData(inputStream);
-            mIsSupportedFile = true;
-        } catch (IOException e) {
-            // Ignore exceptions in order to keep the compatibility with the old versions of
-            // ExifInterface.
-            mIsSupportedFile = false;
-            if (DEBUG) {
-                Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
-                        + "(ExifInterface supports JPEG and some RAW image formats only) "
-                        + "or a corrupted JPEG file to ExifInterface.", e);
-            }
-        } finally {
-            addDefaultValuesForCompatibility();
-
-            if (DEBUG) {
-                printAttributes();
-            }
-        }
-    }
-
-    // Prints out attributes for debugging.
-    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<String, ExifAttribute> entry : mAttributes[i].entrySet()) {
-                final ExifAttribute tagValue = entry.getValue();
-                Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
-                        + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
-            }
-        }
-    }
-
-    /**
-     * Save the tag data into the original image file. This is expensive because it involves
-     * copying all the data from one file to another and deleting the old file and renaming the
-     * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
-     * and make a single call rather than multiple calls for each attribute.
-     * <p>
-     * This method is only supported for JPEG files.
-     * </p>
-     */
-    public void saveAttributes() throws IOException {
-        if (!mIsSupportedFile || mMimeType != IMAGE_TYPE_JPEG) {
-            throw new IOException("ExifInterface only supports saving attributes on JPEG formats.");
-        }
-        if (mFilename == null) {
-            throw new IOException(
-                    "ExifInterface does not support saving attributes for the current input.");
-        }
-
-        // Keep the thumbnail in memory
-        mThumbnailBytes = getThumbnail();
-
-        File tempFile = new File(mFilename + ".tmp");
-        File originalFile = new File(mFilename);
-        if (!originalFile.renameTo(tempFile)) {
-            throw new IOException("Could not rename to " + tempFile.getAbsolutePath());
-        }
-
-        FileInputStream in = null;
-        FileOutputStream out = null;
-        try {
-            // Save the new file.
-            in = new FileInputStream(tempFile);
-            out = new FileOutputStream(mFilename);
-            saveJpegAttributes(in, out);
-        } finally {
-            closeQuietly(in);
-            closeQuietly(out);
-            tempFile.delete();
-        }
-
-        // Discard the thumbnail in memory
-        mThumbnailBytes = null;
-    }
-
-    /**
-     * Returns true if the image file has a thumbnail.
-     */
-    public boolean hasThumbnail() {
-        return mHasThumbnail;
-    }
-
-    /**
-     * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
-     * JPEG compressed thumbnail.
-     * The returned data can be decoded using
-     * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
-     */
-    @Nullable
-    public byte[] getThumbnail() {
-        if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
-            return getThumbnailBytes();
-        }
-        return null;
-    }
-
-    /**
-     * Returns the thumbnail bytes inside the image file, regardless of the compression type of the
-     * thumbnail image.
-     */
-    @Nullable
-    public byte[] getThumbnailBytes() {
-        if (!mHasThumbnail) {
-            return null;
-        }
-        if (mThumbnailBytes != null) {
-            return mThumbnailBytes;
-        }
-
-        // Read the thumbnail.
-        InputStream in = null;
-        try {
-            if (mAssetInputStream != null) {
-                in = mAssetInputStream;
-                if (in.markSupported()) {
-                    in.reset();
-                } else {
-                    Log.d(TAG, "Cannot read thumbnail from inputstream without mark/reset support");
-                    return null;
-                }
-            } else if (mFilename != null) {
-                in = new FileInputStream(mFilename);
-            }
-            if (in == null) {
-                // Should not be reached this.
-                throw new FileNotFoundException();
-            }
-            if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
-                throw new IOException("Corrupted image");
-            }
-            byte[] buffer = new byte[mThumbnailLength];
-            if (in.read(buffer) != mThumbnailLength) {
-                throw new IOException("Corrupted image");
-            }
-            mThumbnailBytes = buffer;
-            return buffer;
-        } catch (IOException e) {
-            // Couldn't get a thumbnail image.
-            Log.d(TAG, "Encountered exception while getting thumbnail", e);
-        } finally {
-            closeQuietly(in);
-        }
-        return null;
-    }
-
-    /**
-     * Creates and returns a Bitmap object of the thumbnail image based on the byte array and the
-     * thumbnail compression value, or {@code null} if the compression type is unsupported.
-     */
-    @Nullable
-    public Bitmap getThumbnailBitmap() {
-        if (!mHasThumbnail) {
-            return null;
-        } else if (mThumbnailBytes == null) {
-            mThumbnailBytes = getThumbnailBytes();
-        }
-
-        if (mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED) {
-            return BitmapFactory.decodeByteArray(mThumbnailBytes, 0, mThumbnailLength);
-        } else if (mThumbnailCompression == DATA_UNCOMPRESSED) {
-            int[] rgbValues = new int[mThumbnailBytes.length / 3];
-            byte alpha = (byte) 0xff000000;
-            for (int i = 0; i < rgbValues.length; i++) {
-                rgbValues[i] = alpha + (mThumbnailBytes[3 * i] << 16)
-                        + (mThumbnailBytes[3 * i + 1] << 8) + mThumbnailBytes[3 * i + 2];
-            }
-
-            ExifAttribute imageLengthAttribute =
-                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
-            ExifAttribute imageWidthAttribute =
-                    (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
-            if (imageLengthAttribute != null && imageWidthAttribute != null) {
-                int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
-                int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
-                return Bitmap.createBitmap(
-                        rgbValues, imageWidth, imageLength, Bitmap.Config.ARGB_8888);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns true if thumbnail image is JPEG Compressed, or false if either thumbnail image does
-     * not exist or thumbnail image is uncompressed.
-     */
-    public boolean isThumbnailCompressed() {
-        return mThumbnailCompression == DATA_JPEG || mThumbnailCompression == DATA_JPEG_COMPRESSED;
-    }
-
-    /**
-     * Returns the offset and length of thumbnail inside the image file, or
-     * {@code null} if there is no thumbnail.
-     *
-     * @return two-element array, the offset in the first value, and length in
-     *         the second, or {@code null} if no thumbnail was found.
-     */
-    @Nullable
-    public long[] getThumbnailRange() {
-        if (!mHasThumbnail) {
-            return null;
-        }
-
-        long[] range = new long[2];
-        range[0] = mThumbnailOffset;
-        range[1] = mThumbnailLength;
-
-        return range;
-    }
-
-    /**
-     * Stores the latitude and longitude value in a float array. The first element is the latitude,
-     * and the second element is the longitude. Returns false if the Exif tags are not available.
-     *
-     * @deprecated Use {@link #getLatLong()} instead.
-     */
-    @Deprecated
-    public boolean getLatLong(float output[]) {
-        double[] latLong = getLatLong();
-        if (latLong == null) {
-            return false;
-        }
-
-        output[0] = (float) latLong[0];
-        output[1] = (float) latLong[1];
-        return true;
-    }
-
-    /**
-     * Gets the latitude and longitude values.
-     * <p>
-     * If there are valid latitude and longitude values in the image, this method returns a double
-     * array where the first element is the latitude and the second element is the longitude.
-     * Otherwise, it returns null.
-     */
-    @Nullable
-    public double[] getLatLong() {
-        String latValue = getAttribute(TAG_GPS_LATITUDE);
-        String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
-        String lngValue = getAttribute(TAG_GPS_LONGITUDE);
-        String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
-
-        if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
-            try {
-                double latitude = convertRationalLatLonToDouble(latValue, latRef);
-                double longitude = convertRationalLatLonToDouble(lngValue, lngRef);
-                return new double[] {latitude, longitude};
-            } catch (IllegalArgumentException e) {
-                Log.w(TAG, "Latitude/longitude values are not parseable. " +
-                        String.format("latValue=%s, latRef=%s, lngValue=%s, lngRef=%s",
-                                latValue, latRef, lngValue, lngRef));
-            }
-        }
-        return null;
-    }
-
-    /**
-     * 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
-     *                 90.0.
-     * @param longitude the decimal value of longitude. Must be a valid double value between -180.0
-     *                  and 180.0.
-     * @throws IllegalArgumentException If {@code latitude} or {@code longitude} is outside the
-     *                                  specified range.
-     */
-    public void setLatLong(double latitude, double longitude) {
-        if (latitude < -90.0 || latitude > 90.0 || Double.isNaN(latitude)) {
-            throw new IllegalArgumentException("Latitude value " + latitude + " is not valid.");
-        }
-        if (longitude < -180.0 || longitude > 180.0 || Double.isNaN(longitude)) {
-            throw new IllegalArgumentException("Longitude value " + longitude + " is not valid.");
-        }
-        setAttribute(TAG_GPS_LATITUDE_REF, latitude >= 0 ? "N" : "S");
-        setAttribute(TAG_GPS_LATITUDE, convertDecimalDegree(Math.abs(latitude)));
-        setAttribute(TAG_GPS_LONGITUDE_REF, longitude >= 0 ? "E" : "W");
-        setAttribute(TAG_GPS_LONGITUDE, convertDecimalDegree(Math.abs(longitude)));
-    }
-
-    /**
-     * Return the altitude in meters. If the exif tag does not exist, return
-     * <var>defaultValue</var>.
-     *
-     * @param defaultValue the value to return if the tag is not available.
-     */
-    public double getAltitude(double defaultValue) {
-        double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
-        int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
-
-        if (altitude >= 0 && ref >= 0) {
-            return (altitude * ((ref == 1) ? -1 : 1));
-        } else {
-            return defaultValue;
-        }
-    }
-
-    /**
-     * 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
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    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
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public long getDateTime() {
-        String dateTimeString = getAttribute(TAG_DATETIME);
-        if (dateTimeString == null
-                || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
-
-        ParsePosition pos = new ParsePosition(0);
-        try {
-            // The exif field is in local time. Parsing it as if it is UTC will yield time
-            // since 1/1/1970 local time
-            Date datetime = sFormatter.parse(dateTimeString, pos);
-            if (datetime == null) return -1;
-            long msecs = datetime.getTime();
-
-            String subSecs = getAttribute(TAG_SUBSEC_TIME);
-            if (subSecs != null) {
-                try {
-                    long sub = Long.parseLong(subSecs);
-                    while (sub > 1000) {
-                        sub /= 10;
-                    }
-                    msecs += sub;
-                } catch (NumberFormatException e) {
-                    // Ignored
-                }
-            }
-            return msecs;
-        } catch (IllegalArgumentException e) {
-            return -1;
-        }
-    }
-
-    /**
-     * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
-     * Returns -1 if the date time information if not available.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public long getGpsDateTime() {
-        String date = getAttribute(TAG_GPS_DATESTAMP);
-        String time = getAttribute(TAG_GPS_TIMESTAMP);
-        if (date == null || time == null
-                || (!sNonZeroTimePattern.matcher(date).matches()
-                && !sNonZeroTimePattern.matcher(time).matches())) {
-            return -1;
-        }
-
-        String dateTimeString = date + ' ' + time;
-
-        ParsePosition pos = new ParsePosition(0);
-        try {
-            Date datetime = sFormatter.parse(dateTimeString, pos);
-            if (datetime == null) return -1;
-            return datetime.getTime();
-        } catch (IllegalArgumentException e) {
-            return -1;
-        }
-    }
-
-    private static double convertRationalLatLonToDouble(String rationalString, String ref) {
-        try {
-            String [] parts = rationalString.split(",");
-
-            String [] pair;
-            pair = parts[0].split("/");
-            double degrees = Double.parseDouble(pair[0].trim())
-                    / Double.parseDouble(pair[1].trim());
-
-            pair = parts[1].split("/");
-            double minutes = Double.parseDouble(pair[0].trim())
-                    / Double.parseDouble(pair[1].trim());
-
-            pair = parts[2].split("/");
-            double seconds = Double.parseDouble(pair[0].trim())
-                    / Double.parseDouble(pair[1].trim());
-
-            double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
-            if ((ref.equals("S") || ref.equals("W"))) {
-                return -result;
-            } else if (ref.equals("N") || ref.equals("E")) {
-                return result;
-            } else {
-                // Not valid
-                throw new IllegalArgumentException();
-            }
-        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
-            // Not valid
-            throw new IllegalArgumentException();
-        }
-    }
-
-    private String convertDecimalDegree(double decimalDegree) {
-        long degrees = (long) decimalDegree;
-        long minutes = (long) ((decimalDegree - degrees) * 60.0);
-        long seconds = Math.round((decimalDegree - degrees - minutes / 60.0) * 3600.0 * 1e7);
-        return degrees + "/1," + minutes + "/1," + seconds + "/10000000";
-    }
-
-    // Checks the type of image file
-    private int getMimeType(BufferedInputStream in) throws IOException {
-        in.mark(SIGNATURE_CHECK_SIZE);
-        byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
-        in.read(signatureCheckBytes);
-        in.reset();
-        if (isJpegFormat(signatureCheckBytes)) {
-            return IMAGE_TYPE_JPEG;
-        } else if (isRafFormat(signatureCheckBytes)) {
-            return IMAGE_TYPE_RAF;
-        } else if (isOrfFormat(signatureCheckBytes)) {
-            return IMAGE_TYPE_ORF;
-        } else if (isRw2Format(signatureCheckBytes)) {
-            return IMAGE_TYPE_RW2;
-        }
-        // Certain file formats (PEF) are identified in readImageFileDirectory()
-        return IMAGE_TYPE_UNKNOWN;
-    }
-
-    /**
-     * This method looks at the first 3 bytes to determine if this file is a JPEG file.
-     * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker"
-     */
-    private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException {
-        for (int i = 0; i < JPEG_SIGNATURE.length; i++) {
-            if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * This method looks at the first 15 bytes to determine if this file is a RAF file.
-     * There is no official specification for RAF files from Fuji, but there is an online archive of
-     * image file specifications:
-     * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
-     */
-    private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException {
-        byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes(Charset.defaultCharset());
-        for (int i = 0; i < rafSignatureBytes.length; i++) {
-            if (signatureCheckBytes[i] != rafSignatureBytes[i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
-     * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
-     * an ORF file.
-     * There is no official specification for ORF files from Olympus, but there is an online archive
-     * of image file specifications:
-     * http://fileformats.archiveteam.org/wiki/Olympus_ORF
-     */
-    private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
-        ByteOrderedDataInputStream signatureInputStream =
-                new ByteOrderedDataInputStream(signatureCheckBytes);
-        // Read byte order
-        mExifByteOrder = readByteOrder(signatureInputStream);
-        // Set byte order
-        signatureInputStream.setByteOrder(mExifByteOrder);
-
-        short orfSignature = signatureInputStream.readShort();
-        signatureInputStream.close();
-        return orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2;
-    }
-
-    /**
-     * RW2 is TIFF-based, but stores 0x55 signature byte instead of 0x42 at the header
-     * See http://lclevy.free.fr/raw/
-     */
-    private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException {
-        ByteOrderedDataInputStream signatureInputStream =
-                new ByteOrderedDataInputStream(signatureCheckBytes);
-        // Read byte order
-        mExifByteOrder = readByteOrder(signatureInputStream);
-        // Set byte order
-        signatureInputStream.setByteOrder(mExifByteOrder);
-
-        short signatureByte = signatureInputStream.readShort();
-        signatureInputStream.close();
-        return signatureByte == RW2_SIGNATURE;
-    }
-
-    /**
-     * Loads EXIF attributes from a JPEG input stream.
-     *
-     * @param in The input stream that starts with the JPEG data.
-     * @param jpegOffset The offset value in input stream for JPEG data.
-     * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for
-     *                   primary image, IFD_TYPE_PREVIEW for preview image, and
-     *                   IFD_TYPE_THUMBNAIL for thumbnail image.
-     * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
-     */
-    private void getJpegAttributes(ByteOrderedDataInputStream in, int jpegOffset, int imageType)
-            throws IOException {
-        // See JPEG File Interchange Format Specification, "JFIF Specification"
-        if (DEBUG) {
-            Log.d(TAG, "getJpegAttributes starting with: " + in);
-        }
-
-        // JPEG uses Big Endian by default. See https://people.cs.umass.edu/~verts/cs32/endian.html
-        in.setByteOrder(ByteOrder.BIG_ENDIAN);
-
-        // Skip to JPEG data
-        in.seek(jpegOffset);
-        int bytesRead = jpegOffset;
-
-        byte marker;
-        if ((marker = in.readByte()) != MARKER) {
-            throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
-        }
-        ++bytesRead;
-        if (in.readByte() != MARKER_SOI) {
-            throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
-        }
-        ++bytesRead;
-        while (true) {
-            marker = in.readByte();
-            if (marker != MARKER) {
-                throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
-            }
-            ++bytesRead;
-            marker = in.readByte();
-            if (DEBUG) {
-                Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
-            }
-            ++bytesRead;
-
-            // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
-            // the image data will terminate right after.
-            if (marker == MARKER_EOI || marker == MARKER_SOS) {
-                break;
-            }
-            int length = in.readUnsignedShort() - 2;
-            bytesRead += 2;
-            if (DEBUG) {
-                Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
-                        + (length + 2) + ")");
-            }
-            if (length < 0) {
-                throw new IOException("Invalid length");
-            }
-            switch (marker) {
-                case MARKER_APP1: {
-                    if (DEBUG) {
-                        Log.d(TAG, "MARKER_APP1");
-                    }
-                    if (length < 6) {
-                        // Skip if it's not an EXIF APP1 segment.
-                        break;
-                    }
-                    byte[] identifier = new byte[6];
-                    if (in.read(identifier) != 6) {
-                        throw new IOException("Invalid exif");
-                    }
-                    bytesRead += 6;
-                    length -= 6;
-                    if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
-                        // Skip if it's not an EXIF APP1 segment.
-                        break;
-                    }
-                    if (length <= 0) {
-                        throw new IOException("Invalid exif");
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
-                    }
-                    // Save offset values for createJpegThumbnailBitmap() function
-                    mExifOffset = bytesRead;
-
-                    byte[] bytes = new byte[length];
-                    if (in.read(bytes) != length) {
-                        throw new IOException("Invalid exif");
-                    }
-                    bytesRead += length;
-                    length = 0;
-
-                    readExifSegment(bytes, imageType);
-                    break;
-                }
-
-                case MARKER_COM: {
-                    byte[] bytes = new byte[length];
-                    if (in.read(bytes) != length) {
-                        throw new IOException("Invalid exif");
-                    }
-                    length = 0;
-                    if (getAttribute(TAG_USER_COMMENT) == null) {
-                        mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString(
-                                new String(bytes, ASCII)));
-                    }
-                    break;
-                }
-
-                case MARKER_SOF0:
-                case MARKER_SOF1:
-                case MARKER_SOF2:
-                case MARKER_SOF3:
-                case MARKER_SOF5:
-                case MARKER_SOF6:
-                case MARKER_SOF7:
-                case MARKER_SOF9:
-                case MARKER_SOF10:
-                case MARKER_SOF11:
-                case MARKER_SOF13:
-                case MARKER_SOF14:
-                case MARKER_SOF15: {
-                    if (in.skipBytes(1) != 1) {
-                        throw new IOException("Invalid SOFx");
-                    }
-                    mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
-                            in.readUnsignedShort(), mExifByteOrder));
-                    mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
-                            in.readUnsignedShort(), mExifByteOrder));
-                    length -= 5;
-                    break;
-                }
-
-                default: {
-                    break;
-                }
-            }
-            if (length < 0) {
-                throw new IOException("Invalid length");
-            }
-            if (in.skipBytes(length) != length) {
-                throw new IOException("Invalid JPEG segment");
-            }
-            bytesRead += length;
-        }
-        // Restore original byte order
-        in.setByteOrder(mExifByteOrder);
-    }
-
-    private void getRawAttributes(ByteOrderedDataInputStream in) throws IOException {
-        // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
-        parseTiffHeaders(in, in.available());
-
-        // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
-        readImageFileDirectory(in, IFD_TYPE_PRIMARY);
-
-        // Update ImageLength/Width tags for all image data.
-        updateImageSizeValues(in, IFD_TYPE_PRIMARY);
-        updateImageSizeValues(in, IFD_TYPE_PREVIEW);
-        updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
-
-        // Check if each image data is in valid position.
-        validateImages(in);
-
-        if (mMimeType == IMAGE_TYPE_PEF) {
-            // PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
-            // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData()
-            ExifAttribute makerNoteAttribute =
-                    (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
-            if (makerNoteAttribute != null) {
-                // Create an ordered DataInputStream for MakerNote
-                ByteOrderedDataInputStream makerNoteDataInputStream =
-                        new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
-                makerNoteDataInputStream.setByteOrder(mExifByteOrder);
-
-                // Seek to MakerNote data
-                makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE);
-
-                // Read IFD data from MakerNote
-                readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF);
-
-                // Update ColorSpace tag
-                ExifAttribute colorSpaceAttribute =
-                        (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE);
-                if (colorSpaceAttribute != null) {
-                    mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute);
-                }
-            }
-        }
-    }
-
-    /**
-     * RAF files contains a JPEG and a CFA data.
-     * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image.
-     * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length
-     * values for the JPEG and CFA data.
-     * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data,
-     * then parses the CFA metadata to retrieve the primary image length/width values.
-     * For data format details, see http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
-     */
-    private void getRafAttributes(ByteOrderedDataInputStream in) throws IOException {
-        // Retrieve offset & length values
-        in.skipBytes(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET);
-        byte[] jpegOffsetBytes = new byte[4];
-        byte[] cfaHeaderOffsetBytes = new byte[4];
-        in.read(jpegOffsetBytes);
-        // Skip JPEG length value since it is not needed
-        in.skipBytes(RAF_JPEG_LENGTH_VALUE_SIZE);
-        in.read(cfaHeaderOffsetBytes);
-        int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
-        int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
-
-        // Retrieve JPEG image metadata
-        getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW);
-
-        // Skip to CFA header offset.
-        in.seek(rafCfaHeaderOffset);
-
-        // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists
-        in.setByteOrder(ByteOrder.BIG_ENDIAN);
-        int numberOfDirectoryEntry = in.readInt();
-        if (DEBUG) {
-            Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
-        }
-        // CFA stores some metadata about the RAW image. Since CFA uses proprietary tags, can only
-        // find and retrieve image size information tags, while skipping others.
-        // See piex.cc RafGetDimension()
-        for (int i = 0; i < numberOfDirectoryEntry; ++i) {
-            int tagNumber = in.readUnsignedShort();
-            int numberOfBytes = in.readUnsignedShort();
-            if (tagNumber == TAG_RAF_IMAGE_SIZE.number) {
-                int imageLength = in.readShort();
-                int imageWidth = in.readShort();
-                ExifAttribute imageLengthAttribute =
-                        ExifAttribute.createUShort(imageLength, mExifByteOrder);
-                ExifAttribute imageWidthAttribute =
-                        ExifAttribute.createUShort(imageWidth, mExifByteOrder);
-                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
-                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
-                if (DEBUG) {
-                    Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth);
-                }
-                return;
-            }
-            in.skipBytes(numberOfBytes);
-        }
-    }
-
-    /**
-     * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
-     * images. Both data takes the form of IFDs and can therefore be read with the
-     * readImageFileDirectory() method.
-     * This method reads all the necessary data and updates the primary/preview/thumbnail image
-     * information according to the GetOlympusPreviewImage() method in piex.cc.
-     * For data format details, see the following:
-     * http://fileformats.archiveteam.org/wiki/Olympus_ORF
-     * https://libopenraw.freedesktop.org/wiki/Olympus_ORF
-     */
-    private void getOrfAttributes(ByteOrderedDataInputStream in) throws IOException {
-        // Retrieve primary image data
-        // Other Exif data will be located in the Makernote.
-        getRawAttributes(in);
-
-        // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains
-        // proprietary tags and therefore does not have offical documentation
-        // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
-        ExifAttribute makerNoteAttribute =
-                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE);
-        if (makerNoteAttribute != null) {
-            // Create an ordered DataInputStream for MakerNote
-            ByteOrderedDataInputStream makerNoteDataInputStream =
-                    new ByteOrderedDataInputStream(makerNoteAttribute.bytes);
-            makerNoteDataInputStream.setByteOrder(mExifByteOrder);
-
-            // There are two types of headers for Olympus MakerNotes
-            // See http://www.exiv2.org/makernote.html#R1
-            byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length];
-            makerNoteDataInputStream.readFully(makerNoteHeader1Bytes);
-            makerNoteDataInputStream.seek(0);
-            byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length];
-            makerNoteDataInputStream.readFully(makerNoteHeader2Bytes);
-            // Skip the corresponding amount of bytes for each header type
-            if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) {
-                makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE);
-            } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) {
-                makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE);
-            }
-
-            // Read IFD data from MakerNote
-            readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE);
-
-            // Retrieve & update preview image offset & length values
-            ExifAttribute imageStartAttribute = (ExifAttribute)
-                    mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START);
-            ExifAttribute imageLengthAttribute = (ExifAttribute)
-                    mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH);
-
-            if (imageStartAttribute != null && imageLengthAttribute != null) {
-                mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT,
-                        imageStartAttribute);
-                mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
-                        imageLengthAttribute);
-            }
-
-            // TODO: Check this behavior in other ORF files
-            // Retrieve primary image length & width values
-            // See piex.cc GetOlympusPreviewImage()
-            ExifAttribute aspectFrameAttribute = (ExifAttribute)
-                    mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME);
-            if (aspectFrameAttribute != null) {
-                int[] aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
-                if (aspectFrameValues == null || aspectFrameValues.length != 4) {
-                    Log.w(TAG, "Invalid aspect frame values. frame="
-                            + Arrays.toString(aspectFrameValues));
-                    return;
-                }
-                if (aspectFrameValues[2] > aspectFrameValues[0] &&
-                        aspectFrameValues[3] > aspectFrameValues[1]) {
-                    int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1;
-                    int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1;
-                    // Swap width & length values
-                    if (primaryImageWidth < primaryImageLength) {
-                        primaryImageWidth += primaryImageLength;
-                        primaryImageLength = primaryImageWidth - primaryImageLength;
-                        primaryImageWidth -= primaryImageLength;
-                    }
-                    ExifAttribute primaryImageWidthAttribute =
-                            ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder);
-                    ExifAttribute primaryImageLengthAttribute =
-                            ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
-
-                    mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
-                    mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
-                }
-            }
-        }
-    }
-
-    // RW2 contains the primary image data in IFD0 and the preview and/or thumbnail image data in
-    // the JpgFromRaw tag
-    // See https://libopenraw.freedesktop.org/wiki/Panasonic_RAW/ and piex.cc Rw2GetPreviewData()
-    private void getRw2Attributes(ByteOrderedDataInputStream in) throws IOException {
-        // Retrieve primary image data
-        getRawAttributes(in);
-
-        // Retrieve preview and/or thumbnail image data
-        ExifAttribute jpgFromRawAttribute =
-                (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW);
-        if (jpgFromRawAttribute != null) {
-            getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW);
-        }
-
-        // Set ISO tag value if necessary
-        ExifAttribute rw2IsoAttribute =
-                (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO);
-        ExifAttribute exifIsoAttribute =
-                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PHOTOGRAPHIC_SENSITIVITY);
-        if (rw2IsoAttribute != null && exifIsoAttribute == null) {
-            // Place this attribute only if it doesn't exist
-            mAttributes[IFD_TYPE_EXIF].put(TAG_PHOTOGRAPHIC_SENSITIVITY, rw2IsoAttribute);
-        }
-    }
-
-    // Stores a new JPEG image with EXIF attributes into a given output stream.
-    private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
-            throws IOException {
-        // See JPEG File Interchange Format Specification, "JFIF Specification"
-        if (DEBUG) {
-            Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
-                    + ", outputStream: " + outputStream + ")");
-        }
-        DataInputStream dataInputStream = new DataInputStream(inputStream);
-        ByteOrderedDataOutputStream dataOutputStream =
-                new ByteOrderedDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
-        if (dataInputStream.readByte() != MARKER) {
-            throw new IOException("Invalid marker");
-        }
-        dataOutputStream.writeByte(MARKER);
-        if (dataInputStream.readByte() != MARKER_SOI) {
-            throw new IOException("Invalid marker");
-        }
-        dataOutputStream.writeByte(MARKER_SOI);
-
-        // Write EXIF APP1 segment
-        dataOutputStream.writeByte(MARKER);
-        dataOutputStream.writeByte(MARKER_APP1);
-        writeExifSegment(dataOutputStream, 6);
-
-        byte[] bytes = new byte[4096];
-
-        while (true) {
-            byte marker = dataInputStream.readByte();
-            if (marker != MARKER) {
-                throw new IOException("Invalid marker");
-            }
-            marker = dataInputStream.readByte();
-            switch (marker) {
-                case MARKER_APP1: {
-                    int length = dataInputStream.readUnsignedShort() - 2;
-                    if (length < 0) {
-                        throw new IOException("Invalid length");
-                    }
-                    byte[] identifier = new byte[6];
-                    if (length >= 6) {
-                        if (dataInputStream.read(identifier) != 6) {
-                            throw new IOException("Invalid exif");
-                        }
-                        if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
-                            // Skip the original EXIF APP1 segment.
-                            if (dataInputStream.skipBytes(length - 6) != length - 6) {
-                                throw new IOException("Invalid length");
-                            }
-                            break;
-                        }
-                    }
-                    // Copy non-EXIF APP1 segment.
-                    dataOutputStream.writeByte(MARKER);
-                    dataOutputStream.writeByte(marker);
-                    dataOutputStream.writeUnsignedShort(length + 2);
-                    if (length >= 6) {
-                        length -= 6;
-                        dataOutputStream.write(identifier);
-                    }
-                    int read;
-                    while (length > 0 && (read = dataInputStream.read(
-                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
-                        dataOutputStream.write(bytes, 0, read);
-                        length -= read;
-                    }
-                    break;
-                }
-                case MARKER_EOI:
-                case MARKER_SOS: {
-                    dataOutputStream.writeByte(MARKER);
-                    dataOutputStream.writeByte(marker);
-                    // Copy all the remaining data
-                    copy(dataInputStream, dataOutputStream);
-                    return;
-                }
-                default: {
-                    // Copy JPEG segment
-                    dataOutputStream.writeByte(MARKER);
-                    dataOutputStream.writeByte(marker);
-                    int length = dataInputStream.readUnsignedShort();
-                    dataOutputStream.writeUnsignedShort(length);
-                    length -= 2;
-                    if (length < 0) {
-                        throw new IOException("Invalid length");
-                    }
-                    int read;
-                    while (length > 0 && (read = dataInputStream.read(
-                            bytes, 0, Math.min(length, bytes.length))) >= 0) {
-                        dataOutputStream.write(bytes, 0, read);
-                        length -= read;
-                    }
-                    break;
-                }
-            }
-        }
-    }
-
-    // Reads the given EXIF byte area and save its tag data into attributes.
-    private void readExifSegment(byte[] exifBytes, int imageType) throws IOException {
-        ByteOrderedDataInputStream dataInputStream =
-                new ByteOrderedDataInputStream(exifBytes);
-
-        // Parse TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
-        parseTiffHeaders(dataInputStream, exifBytes.length);
-
-        // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
-        readImageFileDirectory(dataInputStream, imageType);
-    }
-
-    private void addDefaultValuesForCompatibility() {
-        // If DATETIME tag has no value, then set the value to DATETIME_ORIGINAL tag's.
-        String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
-        if (valueOfDateTimeOriginal != null && getAttribute(TAG_DATETIME) == null) {
-            mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME,
-                    ExifAttribute.createString(valueOfDateTimeOriginal));
-        }
-
-        // Add the default value.
-        if (getAttribute(TAG_IMAGE_WIDTH) == null) {
-            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-        if (getAttribute(TAG_IMAGE_LENGTH) == null) {
-            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-        if (getAttribute(TAG_ORIENTATION) == null) {
-            mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-        if (getAttribute(TAG_LIGHT_SOURCE) == null) {
-            mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-    }
-
-    private ByteOrder readByteOrder(ByteOrderedDataInputStream dataInputStream)
-            throws IOException {
-        // Read byte order.
-        short byteOrder = dataInputStream.readShort();
-        switch (byteOrder) {
-            case BYTE_ALIGN_II:
-                if (DEBUG) {
-                    Log.d(TAG, "readExifSegment: Byte Align II");
-                }
-                return ByteOrder.LITTLE_ENDIAN;
-            case BYTE_ALIGN_MM:
-                if (DEBUG) {
-                    Log.d(TAG, "readExifSegment: Byte Align MM");
-                }
-                return ByteOrder.BIG_ENDIAN;
-            default:
-                throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
-        }
-    }
-
-    private void parseTiffHeaders(ByteOrderedDataInputStream dataInputStream,
-            int exifBytesLength) throws IOException {
-        // Read byte order
-        mExifByteOrder = readByteOrder(dataInputStream);
-        // Set byte order
-        dataInputStream.setByteOrder(mExifByteOrder);
-
-        // Check start code
-        int startCode = dataInputStream.readUnsignedShort();
-        if (mMimeType != IMAGE_TYPE_ORF && mMimeType != IMAGE_TYPE_RW2 && startCode != START_CODE) {
-            throw new IOException("Invalid start code: " + Integer.toHexString(startCode));
-        }
-
-        // Read and skip to first ifd offset
-        int firstIfdOffset = dataInputStream.readInt();
-        if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) {
-            throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
-        }
-        firstIfdOffset -= 8;
-        if (firstIfdOffset > 0) {
-            if (dataInputStream.skipBytes(firstIfdOffset) != firstIfdOffset) {
-                throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
-            }
-        }
-    }
-
-    // Reads image file directory, which is a tag group in EXIF.
-    private void readImageFileDirectory(ByteOrderedDataInputStream dataInputStream,
-            @IfdType int ifdType) throws IOException {
-        if (dataInputStream.mPosition + 2 > dataInputStream.mLength) {
-            // Return if there is no data from the offset.
-            return;
-        }
-        // 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;
-        }
-
-        // See TIFF 6.0 Section 2: TIFF Structure, "Image File Directory".
-        for (short i = 0; i < numberOfDirectoryEntry; ++i) {
-            int tagNumber = dataInputStream.readUnsignedShort();
-            int dataFormat = dataInputStream.readUnsignedShort();
-            int numberOfComponents = dataInputStream.readInt();
-            // Next four bytes is for data offset or value.
-            long nextEntryOffset = dataInputStream.peek() + 4L;
-
-            // Look up a corresponding tag from tag number
-            ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber);
-
-            if (DEBUG) {
-                Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, "
-                        + "numberOfComponents: %d", ifdType, tagNumber,
-                        tag != null ? tag.name : null, dataFormat, numberOfComponents));
-            }
-
-            long byteCount = 0;
-            boolean valid = false;
-            if (tag == null) {
-                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: "
-                            + numberOfComponents);
-                } else {
-                    valid = true;
-                }
-            }
-            if (!valid) {
-                dataInputStream.seek(nextEntryOffset);
-                continue;
-            }
-
-            // Read a value from data field or seek to the value offset which is stored in data
-            // field if the size of the entry value is bigger than 4.
-            if (byteCount > 4) {
-                int offset = dataInputStream.readInt();
-                if (DEBUG) {
-                    Log.d(TAG, "seek to data offset: " + offset);
-                }
-                if (mMimeType == IMAGE_TYPE_ORF) {
-                    if (TAG_MAKER_NOTE.equals(tag.name)) {
-                        // Save offset value for reading thumbnail
-                        mOrfMakerNoteOffset = offset;
-                    } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE
-                            && TAG_ORF_THUMBNAIL_IMAGE.equals(tag.name)) {
-                        // Retrieve & update values for thumbnail offset and length values for ORF
-                        mOrfThumbnailOffset = offset;
-                        mOrfThumbnailLength = numberOfComponents;
-
-                        ExifAttribute compressionAttribute =
-                                ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder);
-                        ExifAttribute jpegInterchangeFormatAttribute =
-                                ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder);
-                        ExifAttribute jpegInterchangeFormatLengthAttribute =
-                                ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
-
-                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute);
-                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
-                                jpegInterchangeFormatAttribute);
-                        mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
-                                jpegInterchangeFormatLengthAttribute);
-                    }
-                } else if (mMimeType == IMAGE_TYPE_RW2) {
-                    if (TAG_RW2_JPG_FROM_RAW.equals(tag.name)) {
-                        mRw2JpgFromRawOffset = offset;
-                    }
-                }
-                if (offset + byteCount <= dataInputStream.mLength) {
-                    dataInputStream.seek(offset);
-                } else {
-                    // Skip if invalid data offset.
-                    Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
-                    dataInputStream.seek(nextEntryOffset);
-                    continue;
-                }
-            }
-
-            // Recursively parse IFD when a IFD pointer tag appears.
-            Integer nextIfdType = sExifPointerTagMap.get(tagNumber);
-            if (DEBUG) {
-                Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount);
-            }
-
-            if (nextIfdType != null) {
-                long offset = -1L;
-                // Get offset from data field
-                switch (dataFormat) {
-                    case IFD_FORMAT_USHORT: {
-                        offset = dataInputStream.readUnsignedShort();
-                        break;
-                    }
-                    case IFD_FORMAT_SSHORT: {
-                        offset = dataInputStream.readShort();
-                        break;
-                    }
-                    case IFD_FORMAT_ULONG: {
-                        offset = dataInputStream.readUnsignedInt();
-                        break;
-                    }
-                    case IFD_FORMAT_SLONG:
-                    case IFD_FORMAT_IFD: {
-                        offset = dataInputStream.readInt();
-                        break;
-                    }
-                    default: {
-                        // Nothing to do
-                        break;
-                    }
-                }
-                if (DEBUG) {
-                    Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
-                }
-                if (offset > 0L && offset < dataInputStream.mLength) {
-                    dataInputStream.seek(offset);
-                    readImageFileDirectory(dataInputStream, nextIfdType);
-                } else {
-                    Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
-                }
-
-                dataInputStream.seek(nextEntryOffset);
-                continue;
-            }
-
-            byte[] bytes = new byte[(int) byteCount];
-            dataInputStream.readFully(bytes);
-            ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
-            mAttributes[ifdType].put(tag.name, attribute);
-
-            // DNG files have a DNG Version tag specifying the version of specifications that the
-            // image file is following.
-            // See http://fileformats.archiveteam.org/wiki/DNG
-            if (TAG_DNG_VERSION.equals(tag.name)) {
-                mMimeType = IMAGE_TYPE_DNG;
-            }
-
-            // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag
-            // that is 65535.
-            // See http://fileformats.archiveteam.org/wiki/Pentax_PEF
-            if (((TAG_MAKE.equals(tag.name) || TAG_MODEL.equals(tag.name))
-                    && attribute.getStringValue(mExifByteOrder).contains(PEF_SIGNATURE))
-                    || (TAG_COMPRESSION.equals(tag.name)
-                    && attribute.getIntValue(mExifByteOrder) == 65535)) {
-                mMimeType = IMAGE_TYPE_PEF;
-            }
-
-            // Seek to next tag offset
-            if (dataInputStream.peek() != nextEntryOffset) {
-                dataInputStream.seek(nextEntryOffset);
-            }
-        }
-
-        if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
-            int nextIfdOffset = dataInputStream.readInt();
-            if (DEBUG) {
-                Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
-            }
-            // The next IFD offset needs to be bigger than 8
-            // since the first IFD offset is at least 8.
-            if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
-                dataInputStream.seek(nextIfdOffset);
-                if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
-                    // Do not overwrite thumbnail IFD data if it alreay exists.
-                    readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL);
-                } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) {
-                    readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW);
-                }
-            }
-        }
-    }
-
-    /**
-     * JPEG compressed images do not contain IMAGE_LENGTH & IMAGE_WIDTH tags.
-     * This value uses JpegInterchangeFormat(JPEG data offset) value, and calls getJpegAttributes()
-     * to locate SOF(Start of Frame) marker and update the image length & width values.
-     * See JEITA CP-3451C Table 5 and Section 4.8.1. B.
-     */
-    private void retrieveJpegImageSize(ByteOrderedDataInputStream in, int imageType)
-            throws IOException {
-        // Check if image already has IMAGE_LENGTH & IMAGE_WIDTH values
-        ExifAttribute imageLengthAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_LENGTH);
-        ExifAttribute imageWidthAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_IMAGE_WIDTH);
-
-        if (imageLengthAttribute == null || imageWidthAttribute == null) {
-            // Find if offset for JPEG data exists
-            ExifAttribute jpegInterchangeFormatAttribute =
-                    (ExifAttribute) mAttributes[imageType].get(TAG_JPEG_INTERCHANGE_FORMAT);
-            if (jpegInterchangeFormatAttribute != null) {
-                int jpegInterchangeFormat =
-                        jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
-
-                // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags
-                getJpegAttributes(in, jpegInterchangeFormat, imageType);
-            }
-        }
-    }
-
-    // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags
-    private void setThumbnailData(ByteOrderedDataInputStream in) throws IOException {
-        HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL];
-
-        ExifAttribute compressionAttribute =
-                (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
-        if (compressionAttribute != null) {
-            mThumbnailCompression = compressionAttribute.getIntValue(mExifByteOrder);
-            switch (mThumbnailCompression) {
-                case DATA_JPEG: {
-                    handleThumbnailFromJfif(in, thumbnailData);
-                    break;
-                }
-                case DATA_UNCOMPRESSED:
-                case DATA_JPEG_COMPRESSED: {
-                    if (isSupportedDataType(thumbnailData)) {
-                        handleThumbnailFromStrips(in, thumbnailData);
-                    }
-                    break;
-                }
-            }
-        } else {
-            // Thumbnail data may not contain Compression tag value
-            mThumbnailCompression = DATA_JPEG;
-            handleThumbnailFromJfif(in, thumbnailData);
-        }
-    }
-
-    // Check JpegInterchangeFormat(JFIF) tags to retrieve thumbnail offset & length values
-    // and reads the corresponding bytes if stream does not support seek function
-    private void handleThumbnailFromJfif(ByteOrderedDataInputStream in, HashMap thumbnailData)
-            throws IOException {
-        ExifAttribute jpegInterchangeFormatAttribute =
-                (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT);
-        ExifAttribute jpegInterchangeFormatLengthAttribute =
-                (ExifAttribute) thumbnailData.get(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
-        if (jpegInterchangeFormatAttribute != null
-                && jpegInterchangeFormatLengthAttribute != null) {
-            int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
-            int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
-
-            // The following code limits the size of thumbnail size not to overflow EXIF data area.
-            thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset);
-            if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
-                    || mMimeType == IMAGE_TYPE_RW2) {
-                thumbnailOffset += mExifOffset;
-            } else if (mMimeType == IMAGE_TYPE_ORF) {
-                // Update offset value since RAF files have IFD data preceding MakerNote data.
-                thumbnailOffset += mOrfMakerNoteOffset;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
-                        + ", length: " + thumbnailLength);
-            }
-            if (thumbnailOffset > 0 && thumbnailLength > 0) {
-                mHasThumbnail = true;
-                mThumbnailOffset = thumbnailOffset;
-                mThumbnailLength = thumbnailLength;
-                if (mFilename == null && mAssetInputStream == null) {
-                    // Save the thumbnail in memory if the input doesn't support reading again.
-                    byte[] thumbnailBytes = new byte[thumbnailLength];
-                    in.seek(thumbnailOffset);
-                    in.readFully(thumbnailBytes);
-                    mThumbnailBytes = thumbnailBytes;
-                }
-            }
-        }
-    }
-
-    // Check StripOffsets & StripByteCounts tags to retrieve thumbnail offset & length values
-    private void handleThumbnailFromStrips(ByteOrderedDataInputStream in, HashMap thumbnailData)
-            throws IOException {
-        ExifAttribute stripOffsetsAttribute =
-                (ExifAttribute) thumbnailData.get(TAG_STRIP_OFFSETS);
-        ExifAttribute stripByteCountsAttribute =
-                (ExifAttribute) thumbnailData.get(TAG_STRIP_BYTE_COUNTS);
-
-        if (stripOffsetsAttribute != null && stripByteCountsAttribute != null) {
-            long[] stripOffsets =
-                    convertToLongArray(stripOffsetsAttribute.getValue(mExifByteOrder));
-            long[] stripByteCounts =
-                    convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
-
-            if (stripOffsets == null) {
-                Log.w(TAG, "stripOffsets should not be null.");
-                return;
-            }
-            if (stripByteCounts == null) {
-                Log.w(TAG, "stripByteCounts should not be null.");
-                return;
-            }
-
-            long totalStripByteCount = 0;
-            for (long byteCount : stripByteCounts) {
-                totalStripByteCount += byteCount;
-            }
-
-            // Set thumbnail byte array data for non-consecutive strip bytes
-            byte[] totalStripBytes = new byte[(int) totalStripByteCount];
-
-            int bytesRead = 0;
-            int bytesAdded = 0;
-            for (int i = 0; i < stripOffsets.length; i++) {
-                int stripOffset = (int) stripOffsets[i];
-                int stripByteCount = (int) stripByteCounts[i];
-
-                // Skip to offset
-                int skipBytes = stripOffset - bytesRead;
-                if (skipBytes < 0) {
-                    Log.d(TAG, "Invalid strip offset value");
-                }
-                in.seek(skipBytes);
-                bytesRead += skipBytes;
-
-                // Read strip bytes
-                byte[] stripBytes = new byte[stripByteCount];
-                in.read(stripBytes);
-                bytesRead += stripByteCount;
-
-                // Add bytes to array
-                System.arraycopy(stripBytes, 0, totalStripBytes, bytesAdded,
-                        stripBytes.length);
-                bytesAdded += stripBytes.length;
-            }
-
-            mHasThumbnail = true;
-            mThumbnailBytes = totalStripBytes;
-            mThumbnailLength = totalStripBytes.length;
-        }
-    }
-
-    // Check if thumbnail data type is currently supported or not
-    private boolean isSupportedDataType(HashMap thumbnailData) throws IOException {
-        ExifAttribute bitsPerSampleAttribute =
-                (ExifAttribute) thumbnailData.get(TAG_BITS_PER_SAMPLE);
-        if (bitsPerSampleAttribute != null) {
-            int[] bitsPerSampleValue = (int[]) bitsPerSampleAttribute.getValue(mExifByteOrder);
-
-            if (Arrays.equals(BITS_PER_SAMPLE_RGB, bitsPerSampleValue)) {
-                return true;
-            }
-
-            // See DNG Specification 1.4.0.0. Section 3, Compression.
-            if (mMimeType == IMAGE_TYPE_DNG) {
-                ExifAttribute photometricInterpretationAttribute =
-                        (ExifAttribute) thumbnailData.get(TAG_PHOTOMETRIC_INTERPRETATION);
-                if (photometricInterpretationAttribute != null) {
-                    int photometricInterpretationValue
-                            = photometricInterpretationAttribute.getIntValue(mExifByteOrder);
-                    if ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
-                            && Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_GREYSCALE_2))
-                            || ((photometricInterpretationValue == PHOTOMETRIC_INTERPRETATION_YCBCR)
-                            && (Arrays.equals(bitsPerSampleValue, BITS_PER_SAMPLE_RGB)))) {
-                        return true;
-                    } else {
-                        // TODO: Add support for lossless Huffman JPEG data
-                    }
-                }
-            }
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Unsupported data type value");
-        }
-        return false;
-    }
-
-    // Returns true if the image length and width values are <= 512.
-    // See Section 4.8 of http://standardsproposals.bsigroup.com/Home/getPDF/567
-    private boolean isThumbnail(HashMap map) throws IOException {
-        ExifAttribute imageLengthAttribute = (ExifAttribute) map.get(TAG_IMAGE_LENGTH);
-        ExifAttribute imageWidthAttribute = (ExifAttribute) map.get(TAG_IMAGE_WIDTH);
-
-        if (imageLengthAttribute != null && imageWidthAttribute != null) {
-            int imageLengthValue = imageLengthAttribute.getIntValue(mExifByteOrder);
-            int imageWidthValue = imageWidthAttribute.getIntValue(mExifByteOrder);
-            if (imageLengthValue <= MAX_THUMBNAIL_SIZE && imageWidthValue <= MAX_THUMBNAIL_SIZE) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Validate primary, preview, thumbnail image data by comparing image size
-    private void validateImages(InputStream in) throws IOException {
-        // Swap images based on size (primary > preview > thumbnail)
-        swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
-        swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
-        swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
-
-        // Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
-        // sizes, excluding padding at the right end or bottom end of the image to make sure that
-        // the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
-        ExifAttribute pixelXDimAttribute =
-                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION);
-        ExifAttribute pixelYDimAttribute =
-                (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION);
-        if (pixelXDimAttribute != null && pixelYDimAttribute != null) {
-            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, pixelXDimAttribute);
-            mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, pixelYDimAttribute);
-        }
-
-        // Check whether thumbnail image exists and whether preview image satisfies the thumbnail
-        // image requirements
-        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<>();
-            }
-        }
-
-        // Check if the thumbnail image satisfies the thumbnail size requirements
-        if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
-            Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
-        }
-    }
-
-    /**
-     * If image is uncompressed, ImageWidth/Length tags are used to store size info.
-     * However, uncompressed images often store extra pixels around the edges of the final image,
-     * which results in larger values for TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH tags.
-     * This method corrects those tag values by checking first the values of TAG_DEFAULT_CROP_SIZE
-     * See DNG Specification 1.4.0.0. Section 4. (DefaultCropSize)
-     *
-     * If image is a RW2 file, valid image sizes are stored in SensorBorder tags.
-     * See tiff_parser.cc GetFullDimension32()
-     * */
-    private void updateImageSizeValues(ByteOrderedDataInputStream in, int imageType)
-            throws IOException {
-        // Uncompressed image valid image size values
-        ExifAttribute defaultCropSizeAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_DEFAULT_CROP_SIZE);
-        // RW2 image valid image size values
-        ExifAttribute topBorderAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_TOP_BORDER);
-        ExifAttribute leftBorderAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_LEFT_BORDER);
-        ExifAttribute bottomBorderAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_BOTTOM_BORDER);
-        ExifAttribute rightBorderAttribute =
-                (ExifAttribute) mAttributes[imageType].get(TAG_RW2_SENSOR_RIGHT_BORDER);
-
-        if (defaultCropSizeAttribute != null) {
-            // Update for uncompressed image
-            ExifAttribute defaultCropSizeXAttribute, defaultCropSizeYAttribute;
-            if (defaultCropSizeAttribute.format == IFD_FORMAT_URATIONAL) {
-                Rational[] defaultCropSizeValue =
-                        (Rational[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
-                if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) {
-                    Log.w(TAG, "Invalid crop size values. cropSize="
-                            + Arrays.toString(defaultCropSizeValue));
-                    return;
-                }
-                defaultCropSizeXAttribute =
-                        ExifAttribute.createURational(defaultCropSizeValue[0], mExifByteOrder);
-                defaultCropSizeYAttribute =
-                        ExifAttribute.createURational(defaultCropSizeValue[1], mExifByteOrder);
-            } else {
-                int[] defaultCropSizeValue =
-                        (int[]) defaultCropSizeAttribute.getValue(mExifByteOrder);
-                if (defaultCropSizeValue == null || defaultCropSizeValue.length != 2) {
-                    Log.w(TAG, "Invalid crop size values. cropSize="
-                            + Arrays.toString(defaultCropSizeValue));
-                    return;
-                }
-                defaultCropSizeXAttribute =
-                        ExifAttribute.createUShort(defaultCropSizeValue[0], mExifByteOrder);
-                defaultCropSizeYAttribute =
-                        ExifAttribute.createUShort(defaultCropSizeValue[1], mExifByteOrder);
-            }
-            mAttributes[imageType].put(TAG_IMAGE_WIDTH, defaultCropSizeXAttribute);
-            mAttributes[imageType].put(TAG_IMAGE_LENGTH, defaultCropSizeYAttribute);
-        } else if (topBorderAttribute != null && leftBorderAttribute != null &&
-                bottomBorderAttribute != null && rightBorderAttribute != null) {
-            // Update for RW2 image
-            int topBorderValue = topBorderAttribute.getIntValue(mExifByteOrder);
-            int bottomBorderValue = bottomBorderAttribute.getIntValue(mExifByteOrder);
-            int rightBorderValue = rightBorderAttribute.getIntValue(mExifByteOrder);
-            int leftBorderValue = leftBorderAttribute.getIntValue(mExifByteOrder);
-            if (bottomBorderValue > topBorderValue && rightBorderValue > leftBorderValue) {
-                int length = bottomBorderValue - topBorderValue;
-                int width = rightBorderValue - leftBorderValue;
-                ExifAttribute imageLengthAttribute =
-                        ExifAttribute.createUShort(length, mExifByteOrder);
-                ExifAttribute imageWidthAttribute =
-                        ExifAttribute.createUShort(width, mExifByteOrder);
-                mAttributes[imageType].put(TAG_IMAGE_LENGTH, imageLengthAttribute);
-                mAttributes[imageType].put(TAG_IMAGE_WIDTH, imageWidthAttribute);
-            }
-        } else {
-            retrieveJpegImageSize(in, imageType);
-        }
-    }
-
-    // Writes an Exif segment into the given output stream.
-    private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
-            int exifOffsetFromBeginning) throws IOException {
-        // The following variables are for calculating each IFD tag group size in bytes.
-        int[] ifdOffsets = new int[EXIF_TAGS.length];
-        int[] ifdDataSizes = new int[EXIF_TAGS.length];
-
-        // Remove IFD pointer tags (we'll re-add it later.)
-        for (ExifTag tag : EXIF_POINTER_TAGS) {
-            removeAttribute(tag.name);
-        }
-        // Remove old thumbnail data
-        removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
-        removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
-
-        // Remove null value tags.
-        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
-            for (Object obj : mAttributes[ifdType].entrySet().toArray()) {
-                final Map.Entry entry = (Map.Entry) obj;
-                if (entry.getValue() == null) {
-                    mAttributes[ifdType].remove(entry.getKey());
-                }
-            }
-        }
-
-        // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
-        // offset when there is one or more tags in the thumbnail IFD.
-        if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
-            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-        if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
-            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-        if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
-            mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-        }
-        if (mHasThumbnail) {
-            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
-                    ExifAttribute.createULong(0, mExifByteOrder));
-            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
-                    ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
-        }
-
-        // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
-        // value which has a bigger size than 4 bytes.
-        for (int i = 0; i < EXIF_TAGS.length; ++i) {
-            int sum = 0;
-            for (Map.Entry<String, ExifAttribute> entry : mAttributes[i].entrySet()) {
-                final ExifAttribute exifAttribute = entry.getValue();
-                final int size = exifAttribute.size();
-                if (size > 4) {
-                    sum += size;
-                }
-            }
-            ifdDataSizes[i] += sum;
-        }
-
-        // Calculate IFD offsets.
-        int position = 8;
-        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
-            if (!mAttributes[ifdType].isEmpty()) {
-                ifdOffsets[ifdType] = position;
-                position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType];
-            }
-        }
-        if (mHasThumbnail) {
-            int thumbnailOffset = position;
-            mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
-                    ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
-            mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
-            position += mThumbnailLength;
-        }
-
-        // Calculate the total size
-        int totalSize = position + 8;  // eight bytes is for header part.
-        if (DEBUG) {
-            Log.d(TAG, "totalSize length: " + totalSize);
-            for (int i = 0; i < EXIF_TAGS.length; ++i) {
-                Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
-                        i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
-            }
-        }
-
-        // Update IFD pointer tags with the calculated offsets.
-        if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) {
-            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name,
-                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder));
-        }
-        if (!mAttributes[IFD_TYPE_GPS].isEmpty()) {
-            mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name,
-                    ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder));
-        }
-        if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) {
-            mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
-                    ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder));
-        }
-
-        // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1.
-        dataOutputStream.writeUnsignedShort(totalSize);
-        dataOutputStream.write(IDENTIFIER_EXIF_APP1);
-        dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
-                ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
-        dataOutputStream.setByteOrder(mExifByteOrder);
-        dataOutputStream.writeUnsignedShort(START_CODE);
-        dataOutputStream.writeUnsignedInt(IFD_OFFSET);
-
-        // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9.
-        for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
-            if (!mAttributes[ifdType].isEmpty()) {
-                // See JEITA CP-3451C Section 4.6.2: IFD structure.
-                // Write entry count
-                dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size());
-
-                // Write entry info
-                int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4;
-                for (Map.Entry<String, ExifAttribute> entry : mAttributes[ifdType].entrySet()) {
-                    // Convert tag name to tag number.
-                    final ExifTag tag = sExifTagMapsForWriting[ifdType].get(entry.getKey());
-                    final int tagNumber = tag.number;
-                    final ExifAttribute attribute = entry.getValue();
-                    final int size = attribute.size();
-
-                    dataOutputStream.writeUnsignedShort(tagNumber);
-                    dataOutputStream.writeUnsignedShort(attribute.format);
-                    dataOutputStream.writeInt(attribute.numberOfComponents);
-                    if (size > 4) {
-                        dataOutputStream.writeUnsignedInt(dataOffset);
-                        dataOffset += size;
-                    } else {
-                        dataOutputStream.write(attribute.bytes);
-                        // Fill zero up to 4 bytes
-                        if (size < 4) {
-                            for (int i = size; i < 4; ++i) {
-                                dataOutputStream.writeByte(0);
-                            }
-                        }
-                    }
-                }
-
-                // Write the next offset. It writes the offset of thumbnail IFD if there is one or
-                // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
-                // IFD; Otherwise 0.
-                if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) {
-                    dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]);
-                } else {
-                    dataOutputStream.writeUnsignedInt(0);
-                }
-
-                // Write values of data field exceeding 4 bytes after the next offset.
-                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);
-                    }
-                }
-            }
-        }
-
-        // Write thumbnail
-        if (mHasThumbnail) {
-            dataOutputStream.write(getThumbnailBytes());
-        }
-
-        // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
-        dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
-
-        return totalSize;
-    }
-
-    /**
-     * Determines the data format of EXIF entry value.
-     *
-     * @param entryValue The value to be determined.
-     * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
-               data formats for the given entry value, returns {@code -1} in the second of the pair.
-     */
-    private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
-        // See TIFF 6.0 Section 2, "Image File Directory".
-        // Take the first component if there are more than one component.
-        if (entryValue.contains(",")) {
-            String[] entryValues = entryValue.split(",");
-            Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
-            if (dataFormat.first == IFD_FORMAT_STRING) {
-                return dataFormat;
-            }
-            for (int i = 1; i < entryValues.length; ++i) {
-                final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
-                int first = -1, second = -1;
-                if (guessDataFormat.first.equals(dataFormat.first)
-                        || guessDataFormat.second.equals(dataFormat.first)) {
-                    first = dataFormat.first;
-                }
-                if (dataFormat.second != -1 && (guessDataFormat.first.equals(dataFormat.second)
-                        || guessDataFormat.second.equals(dataFormat.second))) {
-                    second = dataFormat.second;
-                }
-                if (first == -1 && second == -1) {
-                    return new Pair<>(IFD_FORMAT_STRING, -1);
-                }
-                if (first == -1) {
-                    dataFormat = new Pair<>(second, -1);
-                    continue;
-                }
-                if (second == -1) {
-                    dataFormat = new Pair<>(first, -1);
-                    continue;
-                }
-            }
-            return dataFormat;
-        }
-
-        if (entryValue.contains("/")) {
-            String[] rationalNumber = entryValue.split("/");
-            if (rationalNumber.length == 2) {
-                try {
-                    long numerator = (long) Double.parseDouble(rationalNumber[0]);
-                    long denominator = (long) Double.parseDouble(rationalNumber[1]);
-                    if (numerator < 0L || denominator < 0L) {
-                        return new Pair<>(IFD_FORMAT_SRATIONAL, -1);
-                    }
-                    if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
-                        return new Pair<>(IFD_FORMAT_URATIONAL, -1);
-                    }
-                    return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
-                } catch (NumberFormatException e)  {
-                    // Ignored
-                }
-            }
-            return new Pair<>(IFD_FORMAT_STRING, -1);
-        }
-        try {
-            Long longValue = Long.parseLong(entryValue);
-            if (longValue >= 0 && longValue <= 65535) {
-                return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
-            }
-            if (longValue < 0) {
-                return new Pair<>(IFD_FORMAT_SLONG, -1);
-            }
-            return new Pair<>(IFD_FORMAT_ULONG, -1);
-        } catch (NumberFormatException e) {
-            // Ignored
-        }
-        try {
-            Double.parseDouble(entryValue);
-            return new Pair<>(IFD_FORMAT_DOUBLE, -1);
-        } catch (NumberFormatException e) {
-            // Ignored
-        }
-        return new Pair<>(IFD_FORMAT_STRING, -1);
-    }
-
-    // An input stream to parse EXIF data area, which can be written in either little or big endian
-    // order.
-    private static class ByteOrderedDataInputStream extends InputStream implements DataInput {
-        private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
-        private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
-
-        private DataInputStream mDataInputStream;
-        private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
-        private final int mLength;
-        private int mPosition;
-
-        public ByteOrderedDataInputStream(InputStream in) throws IOException {
-            mDataInputStream = new DataInputStream(in);
-            mLength = mDataInputStream.available();
-            mPosition = 0;
-            mDataInputStream.mark(mLength);
-        }
-
-        public ByteOrderedDataInputStream(byte[] bytes) throws IOException {
-            this(new ByteArrayInputStream(bytes));
-        }
-
-        public void setByteOrder(ByteOrder byteOrder) {
-            mByteOrder = byteOrder;
-        }
-
-        public void seek(long byteCount) throws IOException {
-            if (mPosition > byteCount) {
-                mPosition = 0;
-                mDataInputStream.reset();
-                mDataInputStream.mark(mLength);
-            } else {
-                byteCount -= mPosition;
-            }
-
-            if (skipBytes((int) byteCount) != (int) byteCount) {
-                throw new IOException("Couldn't seek up to the byteCount");
-            }
-        }
-
-        public int peek() {
-            return mPosition;
-        }
-
-        @Override
-        public int available() throws IOException {
-            return mDataInputStream.available();
-        }
-
-        @Override
-        public int read() throws IOException {
-            ++mPosition;
-            return mDataInputStream.read();
-        }
-
-        @Override
-        public int read(byte[] b, int off, int len) throws IOException {
-            int bytesRead = mDataInputStream.read(b, off, len);
-            mPosition += bytesRead;
-            return bytesRead;
-        }
-
-        @Override
-        public int readUnsignedByte() throws IOException {
-            ++mPosition;
-            return mDataInputStream.readUnsignedByte();
-        }
-
-        @Override
-        public String readLine() throws IOException {
-            Log.d(TAG, "Currently unsupported");
-            return null;
-        }
-
-        @Override
-        public boolean readBoolean() throws IOException {
-            ++mPosition;
-            return mDataInputStream.readBoolean();
-        }
-
-        @Override
-        public char readChar() throws IOException {
-            mPosition += 2;
-            return mDataInputStream.readChar();
-        }
-
-        @Override
-        public String readUTF() throws IOException {
-            mPosition += 2;
-            return mDataInputStream.readUTF();
-        }
-
-        @Override
-        public void readFully(byte[] buffer, int offset, int length) throws IOException {
-            mPosition += length;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            if (mDataInputStream.read(buffer, offset, length) != length) {
-                throw new IOException("Couldn't read up to the length of buffer");
-            }
-        }
-
-        @Override
-        public void readFully(byte[] buffer) throws IOException {
-            mPosition += buffer.length;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            if (mDataInputStream.read(buffer, 0, buffer.length) != buffer.length) {
-                throw new IOException("Couldn't read up to the length of buffer");
-            }
-        }
-
-        @Override
-        public byte readByte() throws IOException {
-            ++mPosition;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            int ch = mDataInputStream.read();
-            if (ch < 0) {
-                throw new EOFException();
-            }
-            return (byte) ch;
-        }
-
-        @Override
-        public short readShort() throws IOException {
-            mPosition += 2;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            int ch1 = mDataInputStream.read();
-            int ch2 = mDataInputStream.read();
-            if ((ch1 | ch2) < 0) {
-                throw new EOFException();
-            }
-            if (mByteOrder == LITTLE_ENDIAN) {
-                return (short) ((ch2 << 8) + (ch1));
-            } else if (mByteOrder == BIG_ENDIAN) {
-                return (short) ((ch1 << 8) + (ch2));
-            }
-            throw new IOException("Invalid byte order: " + mByteOrder);
-        }
-
-        @Override
-        public int readInt() throws IOException {
-            mPosition += 4;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            int ch1 = mDataInputStream.read();
-            int ch2 = mDataInputStream.read();
-            int ch3 = mDataInputStream.read();
-            int ch4 = mDataInputStream.read();
-            if ((ch1 | ch2 | ch3 | ch4) < 0) {
-                throw new EOFException();
-            }
-            if (mByteOrder == LITTLE_ENDIAN) {
-                return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
-            } else if (mByteOrder == BIG_ENDIAN) {
-                return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
-            }
-            throw new IOException("Invalid byte order: " + mByteOrder);
-        }
-
-        @Override
-        public int skipBytes(int byteCount) throws IOException {
-            int totalSkip = Math.min(byteCount, mLength - mPosition);
-            int skipped = 0;
-            while (skipped < totalSkip) {
-                skipped += mDataInputStream.skipBytes(totalSkip - skipped);
-            }
-            mPosition += skipped;
-            return skipped;
-        }
-
-        @Override
-        public int readUnsignedShort() throws IOException {
-            mPosition += 2;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            int ch1 = mDataInputStream.read();
-            int ch2 = mDataInputStream.read();
-            if ((ch1 | ch2) < 0) {
-                throw new EOFException();
-            }
-            if (mByteOrder == LITTLE_ENDIAN) {
-                return ((ch2 << 8) + (ch1));
-            } else if (mByteOrder == BIG_ENDIAN) {
-                return ((ch1 << 8) + (ch2));
-            }
-            throw new IOException("Invalid byte order: " + mByteOrder);
-        }
-
-        public long readUnsignedInt() throws IOException {
-            return readInt() & 0xffffffffL;
-        }
-
-        @Override
-        public long readLong() throws IOException {
-            mPosition += 8;
-            if (mPosition > mLength) {
-                throw new EOFException();
-            }
-            int ch1 = mDataInputStream.read();
-            int ch2 = mDataInputStream.read();
-            int ch3 = mDataInputStream.read();
-            int ch4 = mDataInputStream.read();
-            int ch5 = mDataInputStream.read();
-            int ch6 = mDataInputStream.read();
-            int ch7 = mDataInputStream.read();
-            int ch8 = mDataInputStream.read();
-            if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
-                throw new EOFException();
-            }
-            if (mByteOrder == LITTLE_ENDIAN) {
-                return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
-                        + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
-                        + ((long) ch2 << 8) + (long) ch1);
-            } else if (mByteOrder == BIG_ENDIAN) {
-                return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
-                        + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
-                        + ((long) ch7 << 8) + (long) ch8);
-            }
-            throw new IOException("Invalid byte order: " + mByteOrder);
-        }
-
-        @Override
-        public float readFloat() throws IOException {
-            return Float.intBitsToFloat(readInt());
-        }
-
-        @Override
-        public double readDouble() throws IOException {
-            return Double.longBitsToDouble(readLong());
-        }
-    }
-
-    // An output stream to write EXIF data area, which can be written in either little or big endian
-    // order.
-    private static class ByteOrderedDataOutputStream extends FilterOutputStream {
-        private final OutputStream mOutputStream;
-        private ByteOrder mByteOrder;
-
-        public ByteOrderedDataOutputStream(OutputStream out, ByteOrder byteOrder) {
-            super(out);
-            mOutputStream = out;
-            mByteOrder = byteOrder;
-        }
-
-        public void setByteOrder(ByteOrder byteOrder) {
-            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);
-        }
-
-        public void writeByte(int val) throws IOException {
-            mOutputStream.write(val);
-        }
-
-        public void writeShort(short val) throws IOException {
-            if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
-                mOutputStream.write((val >>> 0) & 0xFF);
-                mOutputStream.write((val >>> 8) & 0xFF);
-            } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
-                mOutputStream.write((val >>> 8) & 0xFF);
-                mOutputStream.write((val >>> 0) & 0xFF);
-            }
-        }
-
-        public void writeInt(int val) throws IOException {
-            if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
-                mOutputStream.write((val >>> 0) & 0xFF);
-                mOutputStream.write((val >>> 8) & 0xFF);
-                mOutputStream.write((val >>> 16) & 0xFF);
-                mOutputStream.write((val >>> 24) & 0xFF);
-            } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
-                mOutputStream.write((val >>> 24) & 0xFF);
-                mOutputStream.write((val >>> 16) & 0xFF);
-                mOutputStream.write((val >>> 8) & 0xFF);
-                mOutputStream.write((val >>> 0) & 0xFF);
-            }
-        }
-
-        public void writeUnsignedShort(int val) throws IOException {
-            writeShort((short) val);
-        }
-
-        public void writeUnsignedInt(long val) throws IOException {
-            writeInt((int) val);
-        }
-    }
-
-    // Swaps image data based on image size
-    private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType)
-            throws IOException {
-        if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) {
-            if (DEBUG) {
-                Log.d(TAG, "Cannot perform swap since only one image data exists");
-            }
-            return;
-        }
-
-        ExifAttribute firstImageLengthAttribute =
-                (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH);
-        ExifAttribute firstImageWidthAttribute =
-                (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH);
-        ExifAttribute secondImageLengthAttribute =
-                (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH);
-        ExifAttribute secondImageWidthAttribute =
-                (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH);
-
-        if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) {
-            if (DEBUG) {
-                Log.d(TAG, "First image does not contain valid size information");
-            }
-        } else if (secondImageLengthAttribute == null || secondImageWidthAttribute == null) {
-            if (DEBUG) {
-                Log.d(TAG, "Second image does not contain valid size information");
-            }
-        } else {
-            int firstImageLengthValue = firstImageLengthAttribute.getIntValue(mExifByteOrder);
-            int firstImageWidthValue = firstImageWidthAttribute.getIntValue(mExifByteOrder);
-            int secondImageLengthValue = secondImageLengthAttribute.getIntValue(mExifByteOrder);
-            int secondImageWidthValue = secondImageWidthAttribute.getIntValue(mExifByteOrder);
-
-            if (firstImageLengthValue < secondImageLengthValue &&
-                    firstImageWidthValue < secondImageWidthValue) {
-                HashMap<String, ExifAttribute> tempMap = mAttributes[firstIfdType];
-                mAttributes[firstIfdType] = mAttributes[secondIfdType];
-                mAttributes[secondIfdType] = tempMap;
-            }
-        }
-    }
-
-    /**
-     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
-     */
-    private static void closeQuietly(Closeable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException rethrown) {
-                throw rethrown;
-            } catch (Exception ignored) {
-            }
-        }
-    }
-
-    /**
-     * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
-     * Returns the total number of bytes transferred.
-     */
-    private static int copy(InputStream in, OutputStream out) throws IOException {
-        int total = 0;
-        byte[] buffer = new byte[8192];
-        int c;
-        while ((c = in.read(buffer)) != -1) {
-            total += c;
-            out.write(buffer, 0, c);
-        }
-        return total;
-    }
-
-    /**
-     * Convert given int[] to long[]. If long[] is given, just return it.
-     * Return null for other types of input.
-     */
-    private static long[] convertToLongArray(Object inputObj) {
-        if (inputObj instanceof int[]) {
-            int[] input = (int[]) inputObj;
-            long[] result = new long[input.length];
-            for (int i = 0; i < input.length; i++) {
-                result[i] = input[i];
-            }
-            return result;
-        } else if (inputObj instanceof long[]) {
-            return (long[]) inputObj;
-        }
-        return null;
-    }
-}
diff --git a/android/support/media/ExifInterfaceTest.java b/android/support/media/ExifInterfaceTest.java
deleted file mode 100644
index f811d1a..0000000
--- a/android/support/media/ExifInterfaceTest.java
+++ /dev/null
@@ -1,898 +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.media;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-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.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-import android.util.Pair;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-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.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test {@link ExifInterface}.
- */
-@RunWith(AndroidJUnit4.class)
-public class ExifInterfaceTest {
-    private static final String TAG = ExifInterface.class.getSimpleName();
-    private static final boolean VERBOSE = false;  // lots of logging
-    private static final double DIFFERENCE_TOLERANCE = .001;
-
-    private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg";
-    private static final String EXIF_BYTE_ORDER_MM_JPEG = "image_exif_byte_order_mm.jpg";
-    private static final String LG_G4_ISO_800_DNG = "lg_g4_iso_800.dng";
-    private static final int[] IMAGE_RESOURCES = new int[] {
-            R.raw.image_exif_byte_order_ii, R.raw.image_exif_byte_order_mm, R.raw.lg_g4_iso_800};
-    private static final String[] IMAGE_FILENAMES = new String[] {
-            EXIF_BYTE_ORDER_II_JPEG, EXIF_BYTE_ORDER_MM_JPEG, LG_G4_ISO_800_DNG};
-
-    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 int TEST_NUMBER_OF_CORRUPTED_IMAGE_STREAMS = 30;
-    private static final double[] TEST_LATITUDE_VALID_VALUES = new double[]
-            {0, 45, 90, -60, 0.00000001, -89.999999999, 14.2465923626, -68.3434534737};
-    private static final double[] TEST_LONGITUDE_VALID_VALUES = new double[]
-            {0, -45, 90, -120, 180, 0.00000001, -179.99999999999, -58.57834236352};
-    private static final double[] TEST_LATITUDE_INVALID_VALUES = new double[]
-            {Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 90.0000000001,
-                    263.34763236326, -1e5, 347.32525, -176.346347754};
-    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 HashMap<Integer, Pair> FLIP_STATE_AND_ROTATION_DEGREES = new HashMap<>();
-    static {
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_UNDEFINED, new Pair(false, 0));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_NORMAL, new Pair(false, 0));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_ROTATE_90, new Pair(false, 90));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_ROTATE_180, new Pair(false, 180));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_ROTATE_270, new Pair(false, 270));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_FLIP_HORIZONTAL, new Pair(true, 0));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_TRANSVERSE, new Pair(true, 90));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_FLIP_VERTICAL, new Pair(true, 180));
-        FLIP_STATE_AND_ROTATION_DEGREES.put(
-                ExifInterface.ORIENTATION_TRANSPOSE, new Pair(true, 270));
-    }
-
-    private static final String[] EXIF_TAGS = {
-            ExifInterface.TAG_MAKE,
-            ExifInterface.TAG_MODEL,
-            ExifInterface.TAG_F_NUMBER,
-            ExifInterface.TAG_DATETIME_ORIGINAL,
-            ExifInterface.TAG_EXPOSURE_TIME,
-            ExifInterface.TAG_FLASH,
-            ExifInterface.TAG_FOCAL_LENGTH,
-            ExifInterface.TAG_GPS_ALTITUDE,
-            ExifInterface.TAG_GPS_ALTITUDE_REF,
-            ExifInterface.TAG_GPS_DATESTAMP,
-            ExifInterface.TAG_GPS_LATITUDE,
-            ExifInterface.TAG_GPS_LATITUDE_REF,
-            ExifInterface.TAG_GPS_LONGITUDE,
-            ExifInterface.TAG_GPS_LONGITUDE_REF,
-            ExifInterface.TAG_GPS_PROCESSING_METHOD,
-            ExifInterface.TAG_GPS_TIMESTAMP,
-            ExifInterface.TAG_IMAGE_LENGTH,
-            ExifInterface.TAG_IMAGE_WIDTH,
-            ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY,
-            ExifInterface.TAG_ORIENTATION,
-            ExifInterface.TAG_WHITE_BALANCE
-    };
-
-    private static class ExpectedValue {
-        // Thumbnail information.
-        public final boolean hasThumbnail;
-        public final int thumbnailWidth;
-        public final int thumbnailHeight;
-
-        // GPS information.
-        public final boolean hasLatLong;
-        public final float latitude;
-        public final float longitude;
-        public final float altitude;
-
-        // Values.
-        public final String make;
-        public final String model;
-        public final float aperture;
-        public final String dateTimeOriginal;
-        public final float exposureTime;
-        public final float flash;
-        public final String focalLength;
-        public final String gpsAltitude;
-        public final String gpsAltitudeRef;
-        public final String gpsDatestamp;
-        public final String gpsLatitude;
-        public final String gpsLatitudeRef;
-        public final String gpsLongitude;
-        public final String gpsLongitudeRef;
-        public final String gpsProcessingMethod;
-        public final String gpsTimestamp;
-        public final int imageLength;
-        public final int imageWidth;
-        public final String iso;
-        public final int orientation;
-        public final int whiteBalance;
-
-        private static String getString(TypedArray typedArray, int index) {
-            String stringValue = typedArray.getString(index);
-            if (stringValue == null || stringValue.equals("")) {
-                return null;
-            }
-            return stringValue.trim();
-        }
-
-        ExpectedValue(TypedArray typedArray) {
-            // Reads thumbnail information.
-            hasThumbnail = typedArray.getBoolean(0, false);
-            thumbnailWidth = typedArray.getInt(1, 0);
-            thumbnailHeight = typedArray.getInt(2, 0);
-
-            // Reads GPS information.
-            hasLatLong = typedArray.getBoolean(3, false);
-            latitude = typedArray.getFloat(4, 0f);
-            longitude = typedArray.getFloat(5, 0f);
-            altitude = typedArray.getFloat(6, 0f);
-
-            // Reads values.
-            make = getString(typedArray, 7);
-            model = getString(typedArray, 8);
-            aperture = typedArray.getFloat(9, 0f);
-            dateTimeOriginal = getString(typedArray, 10);
-            exposureTime = typedArray.getFloat(11, 0f);
-            flash = typedArray.getFloat(12, 0f);
-            focalLength = getString(typedArray, 13);
-            gpsAltitude = getString(typedArray, 14);
-            gpsAltitudeRef = getString(typedArray, 15);
-            gpsDatestamp = getString(typedArray, 16);
-            gpsLatitude = getString(typedArray, 17);
-            gpsLatitudeRef = getString(typedArray, 18);
-            gpsLongitude = getString(typedArray, 19);
-            gpsLongitudeRef = getString(typedArray, 20);
-            gpsProcessingMethod = getString(typedArray, 21);
-            gpsTimestamp = getString(typedArray, 22);
-            imageLength = typedArray.getInt(23, 0);
-            imageWidth = typedArray.getInt(24, 0);
-            iso = getString(typedArray, 25);
-            orientation = typedArray.getInt(26, 0);
-            whiteBalance = typedArray.getInt(27, 0);
-
-            typedArray.recycle();
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
-            String outputPath =
-                    new File(Environment.getExternalStorageDirectory(), IMAGE_FILENAMES[i])
-                            .getAbsolutePath();
-
-            InputStream inputStream = null;
-            FileOutputStream outputStream = null;
-            try {
-                inputStream = getContext().getResources().openRawResource(IMAGE_RESOURCES[i]);
-                outputStream = new FileOutputStream(outputPath);
-                copy(inputStream, outputStream);
-            } finally {
-                closeQuietly(inputStream);
-                closeQuietly(outputStream);
-            }
-        }
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
-            String imageFilePath =
-                    new File(Environment.getExternalStorageDirectory(), IMAGE_FILENAMES[i])
-                            .getAbsolutePath();
-            File imageFile = new File(imageFilePath);
-            if (imageFile.exists()) {
-                imageFile.delete();
-            }
-        }
-    }
-
-    @Test
-    @LargeTest
-    public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
-        testExifInterfaceForJpeg(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg);
-    }
-
-    @Test
-    @LargeTest
-    public void testReadExifDataFromExifByteOrderMMJpeg() throws Throwable {
-        testExifInterfaceForJpeg(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_jpg);
-    }
-
-    @Test
-    @LargeTest
-    public void testReadExifDataFromLgG4Iso800Dng() throws Throwable {
-        testExifInterfaceForRaw(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng);
-    }
-
-    @Test
-    @LargeTest
-    public void testDoNotFailOnCorruptedImage() throws Throwable {
-        // ExifInterface shouldn't raise any exceptions except an IOException when unable to open
-        // a file, even with a corrupted image. Generates randomly corrupted image stream for
-        // testing. Uses Epoch date count as random seed so that we can reproduce a broken test.
-        long seed = System.currentTimeMillis() / (86400 * 1000);
-        Log.d(TAG, "testDoNotFailOnCorruptedImage random seed: " + seed);
-        Random random = new Random(seed);
-        byte[] bytes = new byte[8096];
-        ByteBuffer buffer = ByteBuffer.wrap(bytes);
-        for (int i = 0; i < TEST_NUMBER_OF_CORRUPTED_IMAGE_STREAMS; 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();
-            exif.setLatLong(TEST_LATITUDE_VALID_VALUES[i], TEST_LONGITUDE_VALID_VALUES[i]);
-
-            double[] latLong = exif.getLatLong();
-            assertNotNull(latLong);
-            assertEquals(TEST_LATITUDE_VALID_VALUES[i], latLong[0], DELTA);
-            assertEquals(TEST_LONGITUDE_VALID_VALUES[i], latLong[1], DELTA);
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testSetLatLong_withInvalidLatitude() throws IOException {
-        for (int i = 0; i < TEST_LAT_LONG_VALUES_ARRAY_LENGTH; i++) {
-            ExifInterface exif = createTestExifInterface();
-            try {
-                exif.setLatLong(TEST_LATITUDE_INVALID_VALUES[i], TEST_LONGITUDE_VALID_VALUES[i]);
-                fail();
-            } catch (IllegalArgumentException e) {
-                // expected
-            }
-            assertNull(exif.getLatLong());
-            assertLatLongValuesAreNotSet(exif);
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testSetLatLong_withInvalidLongitude() throws IOException {
-        for (int i = 0; i < TEST_LAT_LONG_VALUES_ARRAY_LENGTH; i++) {
-            ExifInterface exif = createTestExifInterface();
-            try {
-                exif.setLatLong(TEST_LATITUDE_VALID_VALUES[i], TEST_LONGITUDE_INVALID_VALUES[i]);
-                fail();
-            } catch (IllegalArgumentException e) {
-                // expected
-            }
-            assertNull(exif.getLatLong());
-            assertLatLongValuesAreNotSet(exif);
-        }
-    }
-
-    @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 get flip state and rotation degrees.
-        for (Integer key : FLIP_STATE_AND_ROTATION_DEGREES.keySet()) {
-            exif.setAttribute(ExifInterface.TAG_ORIENTATION, key.toString());
-            exif.saveAttributes();
-            exif = new ExifInterface(imageFile.getAbsolutePath());
-            assertEquals(FLIP_STATE_AND_ROTATION_DEGREES.get(key).first, exif.isFlipped());
-            assertEquals(FLIP_STATE_AND_ROTATION_DEGREES.get(key).second,
-                    exif.getRotationDegrees());
-        }
-
-        // 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);
-
-    }
-
-    @Test
-    @SmallTest
-    public void testInterchangeabilityBetweenTwoIsoSpeedTags() throws IOException {
-        // Tests that two tags TAG_ISO_SPEED_RATINGS and TAG_PHOTOGRAPHIC_SENSITIVITY can be used
-        // interchangeably.
-        final String oldTag = ExifInterface.TAG_ISO_SPEED_RATINGS;
-        final String newTag = ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY;
-        final String isoValue = "50";
-
-        ExifInterface exif = createTestExifInterface();
-        exif.setAttribute(oldTag, isoValue);
-        assertEquals(isoValue, exif.getAttribute(oldTag));
-        assertEquals(isoValue, exif.getAttribute(newTag));
-
-        exif = createTestExifInterface();
-        exif.setAttribute(newTag, isoValue);
-        assertEquals(isoValue, exif.getAttribute(oldTag));
-        assertEquals(isoValue, exif.getAttribute(newTag));
-    }
-
-    private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
-        // Prints thumbnail information.
-        if (exifInterface.hasThumbnail()) {
-            byte[] thumbnailBytes = exifInterface.getThumbnailBytes();
-            if (thumbnailBytes != null) {
-                Log.v(TAG, fileName + " Thumbnail size = " + thumbnailBytes.length);
-                Bitmap bitmap = exifInterface.getThumbnailBitmap();
-                if (bitmap == null) {
-                    Log.e(TAG, fileName + " Corrupted thumbnail!");
-                } else {
-                    Log.v(TAG, fileName + " Thumbnail size: " + bitmap.getWidth() + ", "
-                            + bitmap.getHeight());
-                }
-            } else {
-                Log.e(TAG, fileName + " Unexpected result: No thumbnails were found. "
-                        + "A thumbnail is expected.");
-            }
-        } else {
-            if (exifInterface.getThumbnailBytes() != null) {
-                Log.e(TAG, fileName + " Unexpected result: A thumbnail was found. "
-                        + "No thumbnail is expected.");
-            } else {
-                Log.v(TAG, fileName + " No thumbnail");
-            }
-        }
-
-        // Prints GPS information.
-        Log.v(TAG, fileName + " Altitude = " + exifInterface.getAltitude(.0));
-
-        double[] latLong = exifInterface.getLatLong();
-        if (latLong != null) {
-            Log.v(TAG, fileName + " Latitude = " + latLong[0]);
-            Log.v(TAG, fileName + " Longitude = " + latLong[1]);
-        } else {
-            Log.v(TAG, fileName + " No latlong data");
-        }
-
-        // Prints values.
-        for (String tagKey : EXIF_TAGS) {
-            String tagValue = exifInterface.getAttribute(tagKey);
-            Log.v(TAG, fileName + " Key{" + tagKey + "} = '" + tagValue + "'");
-        }
-    }
-
-    private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) {
-        int intValue = exifInterface.getAttributeInt(tag, 0);
-        assertEquals(expectedValue, intValue);
-    }
-
-    private void assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue) {
-        double doubleValue = exifInterface.getAttributeDouble(tag, 0.0);
-        assertEquals(expectedValue, doubleValue, DIFFERENCE_TOLERANCE);
-    }
-
-    private void assertStringTag(ExifInterface exifInterface, String tag, String expectedValue) {
-        String stringValue = exifInterface.getAttribute(tag);
-        if (stringValue != null) {
-            stringValue = stringValue.trim();
-        }
-        stringValue = ("".equals(stringValue)) ? null : stringValue;
-
-        assertEquals(expectedValue, stringValue);
-    }
-
-    private void compareWithExpectedValue(ExifInterface exifInterface,
-            ExpectedValue expectedValue, String verboseTag) {
-        if (VERBOSE) {
-            printExifTagsAndValues(verboseTag, exifInterface);
-        }
-        // Checks a thumbnail image.
-        assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail());
-        if (expectedValue.hasThumbnail) {
-            byte[] thumbnailBytes = exifInterface.getThumbnailBytes();
-            assertNotNull(thumbnailBytes);
-            Bitmap thumbnailBitmap = exifInterface.getThumbnailBitmap();
-            assertNotNull(thumbnailBitmap);
-            assertEquals(expectedValue.thumbnailWidth, thumbnailBitmap.getWidth());
-            assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
-        } else {
-            assertNull(exifInterface.getThumbnail());
-        }
-
-        // Checks GPS information.
-        double[] latLong = exifInterface.getLatLong();
-        assertEquals(expectedValue.hasLatLong, latLong != null);
-        if (expectedValue.hasLatLong) {
-            assertEquals(expectedValue.latitude, latLong[0], DIFFERENCE_TOLERANCE);
-            assertEquals(expectedValue.longitude, latLong[1], DIFFERENCE_TOLERANCE);
-        }
-        assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE);
-
-        // Checks values.
-        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_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);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF,
-                expectedValue.gpsAltitudeRef);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, expectedValue.gpsDatestamp);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF,
-                expectedValue.gpsLatitudeRef);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, expectedValue.gpsLongitude);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF,
-                expectedValue.gpsLongitudeRef);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD,
-                expectedValue.gpsProcessingMethod);
-        assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp);
-        assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
-        assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
-        assertStringTag(exifInterface, ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY,
-                expectedValue.iso);
-        assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
-        assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance);
-    }
-
-    private void testExifInterfaceCommon(String fileName, ExpectedValue expectedValue)
-            throws IOException {
-        File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
-        String verboseTag = imageFile.getName();
-
-        // Creates via path.
-        ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
-        assertNotNull(exifInterface);
-        compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
-
-        InputStream in = null;
-        // Creates via InputStream.
-        try {
-            in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath()));
-            exifInterface = new ExifInterface(in);
-            compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
-        } finally {
-            closeQuietly(in);
-        }
-    }
-
-    private void testSaveAttributes_withFileName(String fileName, ExpectedValue expectedValue)
-            throws IOException {
-        File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
-        String verboseTag = imageFile.getName();
-
-        ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
-        exifInterface.saveAttributes();
-        exifInterface = new ExifInterface(imageFile.getAbsolutePath());
-        compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
-
-        // Test for modifying one attribute.
-        String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
-        exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
-        exifInterface.saveAttributes();
-        exifInterface = new ExifInterface(imageFile.getAbsolutePath());
-        assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
-        // Restore the backup value.
-        exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue);
-        exifInterface.saveAttributes();
-        exifInterface = new ExifInterface(imageFile.getAbsolutePath());
-        compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
-    }
-
-    private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)
-            throws IOException {
-        ExpectedValue expectedValue = new ExpectedValue(
-                getContext().getResources().obtainTypedArray(typedArrayResourceId));
-
-        // Test for reading from external data storage.
-        testExifInterfaceCommon(fileName, expectedValue);
-
-        // Test for saving attributes.
-        testSaveAttributes_withFileName(fileName, expectedValue);
-    }
-
-    private void testExifInterfaceForRaw(String fileName, int typedArrayResourceId)
-            throws IOException {
-        ExpectedValue expectedValue = new ExpectedValue(
-                getContext().getResources().obtainTypedArray(typedArrayResourceId));
-
-        // Test for reading from external data storage.
-        testExifInterfaceCommon(fileName, expectedValue);
-
-        // Since ExifInterface does not support for saving attributes for RAW files, do not test
-        // 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 {
-                closeable.close();
-            } catch (RuntimeException rethrown) {
-                throw rethrown;
-            } catch (Exception ignored) {
-            }
-        }
-    }
-
-    private int copy(InputStream in, OutputStream out) throws IOException {
-        int total = 0;
-        byte[] buffer = new byte[8192];
-        int c;
-        while ((c = in.read(buffer)) != -1) {
-            total += c;
-            out.write(buffer, 0, c);
-        }
-        return total;
-    }
-
-    private void assertLatLongValuesAreNotSet(ExifInterface exif) {
-        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
-        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
-        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
-        assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF));
-    }
-
-    private ExifInterface createTestExifInterface() throws IOException {
-        File image = File.createTempFile(TEST_TEMP_FILE_NAME, ".jpg");
-        image.deleteOnExit();
-        return new ExifInterface(image.getAbsolutePath());
-    }
-}
diff --git a/android/support/media/tv/BasePreviewProgram.java b/android/support/media/tv/BasePreviewProgram.java
deleted file mode 100644
index 816b1a1..0000000
--- a/android/support/media/tv/BasePreviewProgram.java
+++ /dev/null
@@ -1,1019 +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.media.tv;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.ContentValues;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Build;
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
-import android.support.media.tv.TvContractCompat.PreviewProgramColumns;
-import android.support.media.tv.TvContractCompat.PreviewPrograms;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-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
- */
-@RestrictTo(LIBRARY)
-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;
-
-    /** @hide */
-    @IntDef({
-            TYPE_UNKNOWN,
-            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,
-            PreviewProgramColumns.TYPE_GAME
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    public @interface Type {}
-
-    /**
-     * The unknown program type.
-     */
-    private static final int TYPE_UNKNOWN = -1;
-
-    /** @hide */
-    @IntDef({
-            ASPECT_RATIO_UNKNOWN,
-            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,
-            PreviewProgramColumns.ASPECT_RATIO_MOVIE_POSTER
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    public @interface AspectRatio {}
-
-    /**
-     * The aspect ratio for unknown aspect ratios.
-     */
-    private static final int ASPECT_RATIO_UNKNOWN = -1;
-
-    /** @hide */
-    @IntDef({
-            AVAILABILITY_UNKNOWN,
-            PreviewProgramColumns.AVAILABILITY_AVAILABLE,
-            PreviewProgramColumns.AVAILABILITY_FREE_WITH_SUBSCRIPTION,
-            PreviewProgramColumns.AVAILABILITY_PAID_CONTENT,
-            PreviewProgramColumns.AVAILABILITY_PURCHASED,
-            PreviewProgramColumns.AVAILABILITY_FREE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    public @interface Availability {}
-
-    /**
-     * The unknown availability.
-     */
-    private static final int AVAILABILITY_UNKNOWN = -1;
-
-    /** @hide */
-    @IntDef({
-            INTERACTION_TYPE_UNKNOWN,
-            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,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    public @interface InteractionType {}
-
-    /**
-     * The unknown interaction type.
-     */
-    private static final int INTERACTION_TYPE_UNKNOWN = -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 ? TYPE_UNKNOWN : 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 ? ASPECT_RATIO_UNKNOWN : 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 ? ASPECT_RATIO_UNKNOWN : 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 ? AVAILABILITY_UNKNOWN : 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 ? INTERACTION_TYPE_UNKNOWN : 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);
-    }
-
-    /**
-     * @return The logo content description for the program.
-     * @see PreviewPrograms#COLUMN_LOGO_CONTENT_DESCRIPTION
-     * @see PreviewPrograms#COLUMN_LOGO_URI
-     */
-    public String getLogoContentDescription() {
-        return mValues.getAsString(PreviewPrograms.COLUMN_LOGO_CONTENT_DESCRIPTION);
-    }
-
-    /**
-     * @return The genre for the program.
-     * @see PreviewPrograms#COLUMN_GENRE
-     */
-    public String getGenre() {
-        return mValues.getAsString(PreviewPrograms.COLUMN_GENRE);
-    }
-
-    /**
-     * @return The start time for the program.
-     * @see PreviewPrograms#COLUMN_START_TIME_UTC_MILLIS
-     */
-    public long getStartTimeUtcMillis() {
-        Long l = mValues.getAsLong(PreviewPrograms.COLUMN_START_TIME_UTC_MILLIS);
-        return l == null ? INVALID_LONG_VALUE : l;
-    }
-
-    /**
-     * @return The end time for the program.
-     * @see PreviewPrograms#COLUMN_END_TIME_UTC_MILLIS
-     */
-    public long getEndTimeUtcMillis() {
-        Long l = mValues.getAsLong(PreviewPrograms.COLUMN_END_TIME_UTC_MILLIS);
-        return l == null ? INVALID_LONG_VALUE : l;
-    }
-
-    /**
-     * @return The preview audio URI for the program.
-     * @see PreviewPrograms#COLUMN_PREVIEW_AUDIO_URI
-     */
-    public Uri getPreviewAudioUri() {
-        String uri = mValues.getAsString(PreviewPrograms.COLUMN_PREVIEW_AUDIO_URI);
-        return uri == null ? null : Uri.parse(uri);
-    }
-
-    @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);
-            values.remove(PreviewProgramColumns.COLUMN_LOGO_CONTENT_DESCRIPTION);
-            values.remove(PreviewProgramColumns.COLUMN_GENRE);
-            values.remove(PreviewProgramColumns.COLUMN_START_TIME_UTC_MILLIS);
-            values.remove(PreviewProgramColumns.COLUMN_END_TIME_UTC_MILLIS);
-            values.remove(PreviewProgramColumns.COLUMN_PREVIEW_AUDIO_URI);
-        }
-        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));
-            }
-            if ((index = cursor.getColumnIndex(
-                    PreviewProgramColumns.COLUMN_LOGO_CONTENT_DESCRIPTION)) >= 0
-                    && !cursor.isNull(index)) {
-                builder.setLogoContentDescription(cursor.getString(index));
-            }
-            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_GENRE)) >= 0
-                    && !cursor.isNull(index)) {
-                builder.setGenre(cursor.getString(index));
-            }
-            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_START_TIME_UTC_MILLIS))
-                    >= 0 && !cursor.isNull(index)) {
-                builder.setStartTimeUtcMillis(cursor.getLong(index));
-            }
-            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_END_TIME_UTC_MILLIS))
-                    >= 0 && !cursor.isNull(index)) {
-                builder.setEndTimeUtcMillis(cursor.getLong(index));
-            }
-            if ((index = cursor.getColumnIndex(PreviewProgramColumns.COLUMN_PREVIEW_AUDIO_URI)) >= 0
-                    && !cursor.isNull(index)) {
-                builder.setPreviewAudioUri(Uri.parse(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,
-                PreviewProgramColumns.COLUMN_LOGO_CONTENT_DESCRIPTION,
-                PreviewProgramColumns.COLUMN_GENRE,
-                PreviewProgramColumns.COLUMN_START_TIME_UTC_MILLIS,
-                PreviewProgramColumns.COLUMN_END_TIME_UTC_MILLIS,
-                PreviewProgramColumns.COLUMN_PREVIEW_AUDIO_URI,
-        };
-        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},
-         * {@link PreviewPrograms#TYPE_STATION}, and
-         * {@link PreviewPrograms#TYPE_GAME}.
-         *
-         * @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},
-         * {@link PreviewPrograms#ASPECT_RATIO_2_3}, and
-         * {@link PreviewPrograms#ASPECT_RATIO_MOVIE_POSTER}.
-         *
-         * @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},
-         * {@link PreviewPrograms#ASPECT_RATIO_2_3}, and
-         * {@link PreviewPrograms#ASPECT_RATIO_MOVIE_POSTER}.
-         *
-         * @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},
-         * {@link PreviewPrograms#AVAILABILITY_PAID_CONTENT},
-         * {@link PreviewPrograms#AVAILABILITY_PURCHASED}, and
-         * {@link PreviewPrograms#AVAILABILITY_FREE}.
-         *
-         * @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;
-        }
-
-        /**
-         * Sets the logo's content description for this program.
-         *
-         * @param logoContentDescription The content description for the logo displayed in the
-         *                               program.
-         * @return This Builder object to allow for chaining of calls to builder methods.
-         * @see PreviewPrograms#COLUMN_LOGO_CONTENT_DESCRIPTION
-         * @see PreviewPrograms#COLUMN_LOGO_URI
-         */
-        public T setLogoContentDescription(String logoContentDescription) {
-            mValues.put(PreviewPrograms.COLUMN_LOGO_CONTENT_DESCRIPTION, logoContentDescription);
-            return (T) this;
-        }
-
-        /**
-         * Sets the genre for this program.
-         *
-         * @param genre The genre for the program.
-         * @return This Builder object to allow for chaining of calls to builder methods.
-         * @see PreviewPrograms#COLUMN_GENRE
-         */
-        public T setGenre(String genre) {
-            mValues.put(PreviewPrograms.COLUMN_GENRE, genre);
-            return (T) this;
-        }
-
-        /**
-         * Sets the start time of the program (for live programs).
-         *
-         * @param startTime The start time for the program.
-         * @return This Builder object to allow for chaining of calls to builder methods.
-         * @see PreviewPrograms#COLUMN_START_TIME_UTC_MILLIS
-         */
-        public T setStartTimeUtcMillis(long startTime) {
-            mValues.put(PreviewPrograms.COLUMN_START_TIME_UTC_MILLIS, startTime);
-            return (T) this;
-        }
-
-        /**
-         * Sets the end time of the program (for live programs).
-         *
-         * @param endTime The end time for the program.
-         * @return This Builder object to allow for chaining of calls to builder methods.
-         * @see PreviewPrograms#COLUMN_END_TIME_UTC_MILLIS
-         */
-        public T setEndTimeUtcMillis(long endTime) {
-            mValues.put(PreviewPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime);
-            return (T) this;
-        }
-
-        /**
-         * Sets a URI for the preview audio.
-         *
-         * @param previewAudioUri The preview audio URI for the program.
-         * @return This Builder object to allow for chaining of calls to builder methods.
-         * @see PreviewPrograms#COLUMN_PREVIEW_AUDIO_URI
-         */
-        public T setPreviewAudioUri(Uri previewAudioUri) {
-            mValues.put(PreviewPrograms.COLUMN_PREVIEW_AUDIO_URI,
-                    previewAudioUri == null ? null : previewAudioUri.toString());
-            return (T) this;
-        }
-    }
-}
diff --git a/android/support/media/tv/BaseProgram.java b/android/support/media/tv/BaseProgram.java
deleted file mode 100644
index 4c7882d..0000000
--- a/android/support/media/tv/BaseProgram.java
+++ /dev/null
@@ -1,891 +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.media.tv;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY;
-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.IntDef;
-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.Programs;
-import android.support.media.tv.TvContractCompat.Programs.Genres.Genre;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Base class for derived classes that want to have common fields for programs defined in
- * {@link TvContractCompat}.
- * @hide
- */
-@RestrictTo(LIBRARY)
-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 */
-    @IntDef({
-            REVIEW_RATING_STYLE_UNKNOWN,
-            ProgramColumns.REVIEW_RATING_STYLE_STARS,
-            ProgramColumns.REVIEW_RATING_STYLE_THUMBS_UP_DOWN,
-            ProgramColumns.REVIEW_RATING_STYLE_PERCENTAGE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    @interface ReviewRatingStyle {}
-
-    /**
-     * The unknown review rating style.
-     */
-    private static final int REVIEW_RATING_STYLE_UNKNOWN = -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 ? REVIEW_RATING_STYLE_UNKNOWN : 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/android/support/media/tv/Channel.java b/android/support/media/tv/Channel.java
deleted file mode 100644
index a24d948..0000000
--- a/android/support/media/tv/Channel.java
+++ /dev/null
@@ -1,952 +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.media.tv;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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>
- */
-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/android/support/media/tv/ChannelLogoUtils.java b/android/support/media/tv/ChannelLogoUtils.java
deleted file mode 100644
index 20b79f3..0000000
--- a/android/support/media/tv/ChannelLogoUtils.java
+++ /dev/null
@@ -1,167 +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.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/android/support/media/tv/ChannelLogoUtilsTest.java b/android/support/media/tv/ChannelLogoUtilsTest.java
deleted file mode 100644
index ea315ab..0000000
--- a/android/support/media/tv/ChannelLogoUtilsTest.java
+++ /dev/null
@@ -1,99 +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.media.tv;
-
-import static android.support.test.InstrumentationRegistry.getContext;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-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.support.test.filters.MediumTest;
-import android.support.test.filters.Suppress;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Suppress // Test is failing b/70905391
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class ChannelLogoUtilsTest {
-    private static final String FAKE_INPUT_ID = "ChannelLogoUtils.test";
-
-    private ContentResolver mContentResolver;
-    private Uri mChannelUri;
-    private long mChannelId;
-
-    @Before
-    public void setUp() throws Exception {
-        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);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mContentResolver.delete(mChannelUri, null, null);
-    }
-
-    @Test
-    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));
-    }
-
-    @Test
-    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/android/support/media/tv/ChannelTest.java b/android/support/media/tv/ChannelTest.java
deleted file mode 100644
index 979a20a..0000000
--- a/android/support/media/tv/ChannelTest.java
+++ /dev/null
@@ -1,250 +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.media.tv;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-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 android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests that channels can be created using the Builder pattern and correctly obtain
- * values from them
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
-public class ChannelTest {
-    @After
-    public 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/android/support/media/tv/CollectionUtils.java b/android/support/media/tv/CollectionUtils.java
deleted file mode 100644
index 7aa1074..0000000
--- a/android/support/media/tv/CollectionUtils.java
+++ /dev/null
@@ -1,49 +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.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/android/support/media/tv/PreviewProgram.java b/android/support/media/tv/PreviewProgram.java
deleted file mode 100644
index 6d2fbaf..0000000
--- a/android/support/media/tv/PreviewProgram.java
+++ /dev/null
@@ -1,225 +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.media.tv;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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>
- */
-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/android/support/media/tv/PreviewProgramTest.java b/android/support/media/tv/PreviewProgramTest.java
deleted file mode 100644
index d0baa5f..0000000
--- a/android/support/media/tv/PreviewProgramTest.java
+++ /dev/null
@@ -1,387 +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.media.tv;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-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 android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-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
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = 26)
-public class PreviewProgramTest {
-
-    @After
-    public 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/android/support/media/tv/Program.java b/android/support/media/tv/Program.java
deleted file mode 100644
index 882916d..0000000
--- a/android/support/media/tv/Program.java
+++ /dev/null
@@ -1,309 +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.media.tv;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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;
-import android.support.media.tv.TvContractCompat.Programs.Genres.Genre;
-
-/**
- * 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>
- */
-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(@Genre 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/android/support/media/tv/ProgramTest.java b/android/support/media/tv/ProgramTest.java
deleted file mode 100644
index 6209383..0000000
--- a/android/support/media/tv/ProgramTest.java
+++ /dev/null
@@ -1,274 +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.media.tv;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-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 android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-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
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
-public class ProgramTest {
-    @After
-    public 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/android/support/media/tv/TvContractCompat.java b/android/support/media/tv/TvContractCompat.java
deleted file mode 100644
index bd03bf1..0000000
--- a/android/support/media/tv/TvContractCompat.java
+++ /dev/null
@@ -1,2904 +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.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.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 {
-        /**
-         * 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 {
-        /**
-         * 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;
-
-        /**
-         * The program type for game.
-         *
-         * @see #COLUMN_TYPE
-         */
-        int TYPE_GAME = 12;
-
-        /**
-         * 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;
-
-        /**
-         * The aspect ratio for movie poster which is 1:1.441.
-         *
-         * @see #COLUMN_POSTER_ART_ASPECT_RATIO
-         * @see #COLUMN_THUMBNAIL_ASPECT_RATIO
-         */
-        int ASPECT_RATIO_MOVIE_POSTER = 5;
-
-        /**
-         * 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;
-
-        /**
-         * The availability for content already purchased by the user.
-         *
-         * @see #COLUMN_AVAILABILITY
-         */
-        int AVAILABILITY_PURCHASED = 3;
-
-        /**
-         * The availability for free content.
-         *
-         * @see #COLUMN_AVAILABILITY
-         */
-        int AVAILABILITY_FREE = 4;
-
-        /**
-         * 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},
-         * {@link #TYPE_STATION}, and
-         * {@link #TYPE_GAME}.
-         *
-         * <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},
-         * {@link #ASPECT_RATIO_2_3}, and
-         * {@link #ASPECT_RATIO_MOVIE_POSTER}.
-         *
-         * <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},
-         * {@link #ASPECT_RATIO_2_3}, and
-         * {@link #ASPECT_RATIO_MOVIE_POSTER}.
-         *
-         * <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},
-         * {@link #AVAILABILITY_PAID_CONTENT},
-         * {@link #AVAILABILITY_PURCHASED} and
-         * {@link #AVAILABILITY_FREE}.
-         *
-         * <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} or
-         * {@link #AVAILABILITY_FREE}.
-         *
-         * <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";
-
-        /**
-         * The content description of the logo of this TV program.
-         *
-         * <p>A description of the logo shown on the program used in accessibility mode.
-         *
-         * <p>Can be empty.
-         *
-         * <p>Type: TEXT
-         * @see #COLUMN_LOGO_URI
-         */
-        String COLUMN_LOGO_CONTENT_DESCRIPTION = "logo_content_description";
-
-        /**
-         * A genre(s) that are related to this TV program.
-         *
-         * <p>A short freeform description of the genre(s) of the program. Usually a comma seperated
-         * list of a few genres. For example: Drama, Sci-Fi.
-         *
-         * <p>Can be empty.
-         *
-         * <p>Type: TEXT
-         */
-        String COLUMN_GENRE = "genre";
-
-        /**
-         * The start time of this TV program, in milliseconds since the epoch.
-         *
-         * <p>Should be empty if this program is not live.
-         *
-         * <p>Type: INTEGER (long)
-         * @see #COLUMN_LIVE
-         */
-        String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
-
-        /**
-         * The end time of this TV program, in milliseconds since the epoch.
-         *
-         * <p>Should be empty if this program is not live.
-         *
-         * <p>Type: INTEGER (long)
-         * @see #COLUMN_LIVE
-         */
-        String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
-
-        /**
-         * The URI for the preview audio.
-         *
-         * <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_AUDIO_URI = "preview_audio_uri";
-    }
-
-    /** 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 */
-            @RestrictTo(RestrictTo.Scope.LIBRARY)
-            @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";
-
-        /**
-         * 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/android/support/media/tv/TvContractUtils.java b/android/support/media/tv/TvContractUtils.java
deleted file mode 100644
index edcd917..0000000
--- a/android/support/media/tv/TvContractUtils.java
+++ /dev/null
@@ -1,116 +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.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/android/support/media/tv/TvContractUtilsTest.java b/android/support/media/tv/TvContractUtilsTest.java
deleted file mode 100644
index bdb739f..0000000
--- a/android/support/media/tv/TvContractUtilsTest.java
+++ /dev/null
@@ -1,159 +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.media.tv;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import android.media.tv.TvContentRating;
-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;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
-public class TvContractUtilsTest {
-
-    @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/android/support/media/tv/Utils.java b/android/support/media/tv/Utils.java
deleted file mode 100644
index a6ff0ad..0000000
--- a/android/support/media/tv/Utils.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.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/android/support/media/tv/WatchNextProgram.java b/android/support/media/tv/WatchNextProgram.java
deleted file mode 100644
index 61082aa..0000000
--- a/android/support/media/tv/WatchNextProgram.java
+++ /dev/null
@@ -1,258 +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.media.tv;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.IntDef;
-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 java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * 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>
- */
-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;
-
-    /** @hide */
-    @IntDef({
-            WATCH_NEXT_TYPE_UNKNOWN,
-            WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE,
-            WatchNextPrograms.WATCH_NEXT_TYPE_NEXT,
-            WatchNextPrograms.WATCH_NEXT_TYPE_NEW,
-            WatchNextPrograms.WATCH_NEXT_TYPE_WATCHLIST,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    public @interface WatchNextType {}
-
-    /**
-     * The unknown watch next type. Use this type when the actual type is not known.
-     */
-    public static final int WATCH_NEXT_TYPE_UNKNOWN = -1;
-
-    private WatchNextProgram(Builder builder) {
-        super(builder);
-    }
-
-    /**
-     * @return The value of {@link WatchNextPrograms#COLUMN_WATCH_NEXT_TYPE} for the program,
-     * or {@link #WATCH_NEXT_TYPE_UNKNOWN} if it's unknown.
-     */
-    public @WatchNextType int getWatchNextType() {
-        Integer i = mValues.getAsInteger(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE);
-        return i == null ? WATCH_NEXT_TYPE_UNKNOWN : 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/android/support/media/tv/WatchNextProgramTest.java b/android/support/media/tv/WatchNextProgramTest.java
deleted file mode 100644
index ecce068..0000000
--- a/android/support/media/tv/WatchNextProgramTest.java
+++ /dev/null
@@ -1,365 +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.media.tv;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-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 android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-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)
-@RunWith(AndroidJUnit4.class)
-public class WatchNextProgramTest {
-
-    @Before
-    public 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/android/support/mediacompat/client/AudioAttributesCompatTest.java b/android/support/mediacompat/client/AudioAttributesCompatTest.java
new file mode 100644
index 0000000..47e4559
--- /dev/null
+++ b/android/support/mediacompat/client/AudioAttributesCompatTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.mediacompat.client;
+
+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 androidx.media.AudioAttributesCompat;
+
+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 AudioAttributesCompat.Builder mkBuilder(
+            @AudioAttributesCompat.AttributeContentType int type,
+            @AudioAttributesCompat.AttributeUsage int usage) {
+        return new AudioAttributesCompat.Builder().setContentType(type).setUsage(usage);
+    }
+
+    static 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/android/support/mediacompat/client/ClientBroadcastReceiver.java b/android/support/mediacompat/client/ClientBroadcastReceiver.java
new file mode 100644
index 0000000..3227482
--- /dev/null
+++ b/android/support/mediacompat/client/ClientBroadcastReceiver.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.client;
+
+import static android.support.mediacompat.testlib.MediaControllerConstants.ADD_QUEUE_ITEM;
+import static android.support.mediacompat.testlib.MediaControllerConstants
+        .ADD_QUEUE_ITEM_WITH_INDEX;
+import static android.support.mediacompat.testlib.MediaControllerConstants.ADJUST_VOLUME;
+import static android.support.mediacompat.testlib.MediaControllerConstants.FAST_FORWARD;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PAUSE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY_FROM_MEDIA_ID;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY_FROM_SEARCH;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY_FROM_URI;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE_FROM_MEDIA_ID;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE_FROM_SEARCH;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE_FROM_URI;
+import static android.support.mediacompat.testlib.MediaControllerConstants.REMOVE_QUEUE_ITEM;
+import static android.support.mediacompat.testlib.MediaControllerConstants.REWIND;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SEEK_TO;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SEND_COMMAND;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SEND_CUSTOM_ACTION;
+import static android.support.mediacompat.testlib.MediaControllerConstants
+        .SEND_CUSTOM_ACTION_PARCELABLE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_CAPTIONING_ENABLED;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_RATING;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_REPEAT_MODE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_SHUFFLE_MODE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_VOLUME_TO;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SKIP_TO_NEXT;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SKIP_TO_PREVIOUS;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SKIP_TO_QUEUE_ITEM;
+import static android.support.mediacompat.testlib.MediaControllerConstants.STOP;
+import static android.support.mediacompat.testlib.util.IntentUtil
+        .ACTION_CALL_MEDIA_CONTROLLER_METHOD;
+import static android.support.mediacompat.testlib.util.IntentUtil
+        .ACTION_CALL_TRANSPORT_CONTROLS_METHOD;
+import static android.support.mediacompat.testlib.util.IntentUtil.KEY_ARGUMENT;
+import static android.support.mediacompat.testlib.util.IntentUtil.KEY_METHOD_ID;
+import static android.support.mediacompat.testlib.util.IntentUtil.KEY_SESSION_TOKEN;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.RatingCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaControllerCompat.TransportControls;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+
+public class ClientBroadcastReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Bundle extras = intent.getExtras();
+        MediaControllerCompat controller;
+        try {
+            controller = new MediaControllerCompat(context,
+                    (MediaSessionCompat.Token) extras.getParcelable(KEY_SESSION_TOKEN));
+        } catch (RemoteException ex) {
+            // Do nothing.
+            return;
+        }
+        int method = extras.getInt(KEY_METHOD_ID, 0);
+
+        if (ACTION_CALL_MEDIA_CONTROLLER_METHOD.equals(intent.getAction()) && extras != null) {
+            Bundle arguments;
+            switch (method) {
+                case SEND_COMMAND:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controller.sendCommand(
+                            arguments.getString("command"),
+                            arguments.getBundle("extras"),
+                            new ResultReceiver(null));
+                    break;
+                case ADD_QUEUE_ITEM:
+                    controller.addQueueItem(
+                            (MediaDescriptionCompat) extras.getParcelable(KEY_ARGUMENT));
+                    break;
+                case ADD_QUEUE_ITEM_WITH_INDEX:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controller.addQueueItem(
+                            (MediaDescriptionCompat) arguments.getParcelable("description"),
+                            arguments.getInt("index"));
+                    break;
+                case REMOVE_QUEUE_ITEM:
+                    controller.removeQueueItem(
+                            (MediaDescriptionCompat) extras.getParcelable(KEY_ARGUMENT));
+                    break;
+                case SET_VOLUME_TO:
+                    controller.setVolumeTo(extras.getInt(KEY_ARGUMENT), 0);
+                    break;
+                case ADJUST_VOLUME:
+                    controller.adjustVolume(extras.getInt(KEY_ARGUMENT), 0);
+                    break;
+            }
+        } else if (ACTION_CALL_TRANSPORT_CONTROLS_METHOD.equals(intent.getAction())
+                && extras != null) {
+            TransportControls controls = controller.getTransportControls();
+            Bundle arguments;
+            switch (method) {
+                case PLAY:
+                    controls.play();
+                    break;
+                case PAUSE:
+                    controls.pause();
+                    break;
+                case STOP:
+                    controls.stop();
+                    break;
+                case FAST_FORWARD:
+                    controls.fastForward();
+                    break;
+                case REWIND:
+                    controls.rewind();
+                    break;
+                case SKIP_TO_PREVIOUS:
+                    controls.skipToPrevious();
+                    break;
+                case SKIP_TO_NEXT:
+                    controls.skipToNext();
+                    break;
+                case SEEK_TO:
+                    controls.seekTo(extras.getLong(KEY_ARGUMENT));
+                    break;
+                case SET_RATING:
+                    controls.setRating((RatingCompat) extras.getParcelable(KEY_ARGUMENT));
+                    break;
+                case PLAY_FROM_MEDIA_ID:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.playFromMediaId(
+                            arguments.getString("mediaId"),
+                            arguments.getBundle("extras"));
+                    break;
+                case PLAY_FROM_SEARCH:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.playFromSearch(
+                            arguments.getString("query"),
+                            arguments.getBundle("extras"));
+                    break;
+                case PLAY_FROM_URI:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.playFromUri(
+                            (Uri) arguments.getParcelable("uri"),
+                            arguments.getBundle("extras"));
+                    break;
+                case SEND_CUSTOM_ACTION:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.sendCustomAction(
+                            arguments.getString("action"),
+                            arguments.getBundle("extras"));
+                    break;
+                case SEND_CUSTOM_ACTION_PARCELABLE:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.sendCustomAction(
+                            (PlaybackStateCompat.CustomAction)
+                                    arguments.getParcelable("action"),
+                            arguments.getBundle("extras"));
+                    break;
+                case SKIP_TO_QUEUE_ITEM:
+                    controls.skipToQueueItem(extras.getLong(KEY_ARGUMENT));
+                    break;
+                case PREPARE:
+                    controls.prepare();
+                    break;
+                case PREPARE_FROM_MEDIA_ID:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.prepareFromMediaId(
+                            arguments.getString("mediaId"),
+                            arguments.getBundle("extras"));
+                    break;
+                case PREPARE_FROM_SEARCH:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.prepareFromSearch(
+                            arguments.getString("query"),
+                            arguments.getBundle("extras"));
+                    break;
+                case PREPARE_FROM_URI:
+                    arguments = extras.getBundle(KEY_ARGUMENT);
+                    controls.prepareFromUri(
+                            (Uri) arguments.getParcelable("uri"),
+                            arguments.getBundle("extras"));
+                    break;
+                case SET_CAPTIONING_ENABLED:
+                    controls.setCaptioningEnabled(extras.getBoolean(KEY_ARGUMENT));
+                    break;
+                case SET_REPEAT_MODE:
+                    controls.setRepeatMode(extras.getInt(KEY_ARGUMENT));
+                    break;
+                case SET_SHUFFLE_MODE:
+                    controls.setShuffleMode(extras.getInt(KEY_ARGUMENT));
+                    break;
+            }
+        }
+    }
+}
diff --git a/android/support/mediacompat/client/MediaBrowserCompatTest.java b/android/support/mediacompat/client/MediaBrowserCompatTest.java
new file mode 100644
index 0000000..ba89f99
--- /dev/null
+++ b/android/support/mediacompat/client/MediaBrowserCompatTest.java
@@ -0,0 +1,1115 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.client;
+
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION_FOR_ERROR;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION_SEND_ERROR;
+import static android.support.mediacompat.testlib.MediaBrowserConstants
+        .CUSTOM_ACTION_SEND_PROGRESS_UPDATE;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION_SEND_RESULT;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.EXTRAS_KEY;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.EXTRAS_VALUE;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_CHILDREN;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_CHILDREN_DELAYED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_INCLUDE_METADATA;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_INVALID;
+import static android.support.mediacompat.testlib.MediaBrowserConstants
+        .MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_ROOT;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_METADATA;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.NOTIFY_CHILDREN_CHANGED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEARCH_QUERY;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEARCH_QUERY_FOR_ERROR;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEARCH_QUERY_FOR_NO_RESULT;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEND_DELAYED_ITEM_LOADED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants
+        .SEND_DELAYED_NOTIFY_CHILDREN_CHANGED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SET_SESSION_TOKEN;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_KEY_1;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_KEY_2;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_KEY_3;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_KEY_4;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_VALUE_1;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_VALUE_2;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_VALUE_3;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.TEST_VALUE_4;
+import static android.support.mediacompat.testlib.VersionConstants.KEY_SERVICE_VERSION;
+import static android.support.mediacompat.testlib.VersionConstants.VERSION_TOT;
+import static android.support.mediacompat.testlib.util.IntentUtil.SERVICE_PACKAGE_NAME;
+import static android.support.mediacompat.testlib.util.IntentUtil.callMediaBrowserServiceMethod;
+import static android.support.test.InstrumentationRegistry.getArguments;
+import static android.support.test.InstrumentationRegistry.getContext;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+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 static org.junit.Assert.fail;
+
+import android.content.ComponentName;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.mediacompat.testlib.util.PollingCheck;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.MediaBrowserCompat.MediaItem;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.RatingCompat;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.media.MediaBrowserServiceCompat;
+
+import org.junit.After;
+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.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test {@link MediaBrowserCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MediaBrowserCompatTest {
+
+    private static final String TAG = "MediaBrowserCompatTest";
+
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final long WAIT_TIME_FOR_NO_RESPONSE_MS = 300L;
+
+    /**
+     * To check {@link MediaBrowserCompat#unsubscribe} works properly,
+     * we notify to the browser after the unsubscription that the media items have changed.
+     * Then {@link MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded} should not be called.
+     *
+     * The measured time from calling {@link MediaBrowserServiceCompat#notifyChildrenChanged}
+     * to {@link MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded} being called is about
+     * 50ms.
+     * So we make the thread sleep for 100ms to properly check that the callback is not called.
+     */
+    private static final long SLEEP_MS = 100L;
+    private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+            SERVICE_PACKAGE_NAME,
+            "android.support.mediacompat.service.StubMediaBrowserServiceCompat");
+    private static final ComponentName TEST_BROWSER_SERVICE_DELAYED_MEDIA_SESSION =
+            new ComponentName(
+                    SERVICE_PACKAGE_NAME,
+                    "android.support.mediacompat.service"
+                            + ".StubMediaBrowserServiceCompatWithDelayedMediaSession");
+    private static final ComponentName TEST_INVALID_BROWSER_SERVICE = new ComponentName(
+            "invalid.package", "invalid.ServiceClassName");
+
+    private String mServiceVersion;
+    private MediaBrowserCompat mMediaBrowser;
+    private StubConnectionCallback mConnectionCallback;
+    private StubSubscriptionCallback mSubscriptionCallback;
+    private StubItemCallback mItemCallback;
+    private StubSearchCallback mSearchCallback;
+    private CustomActionCallback mCustomActionCallback;
+    private Bundle mRootHints;
+
+    @Before
+    public void setUp() {
+        // The version of the service app is provided through the instrumentation arguments.
+        mServiceVersion = getArguments().getString(KEY_SERVICE_VERSION, "");
+        Log.d(TAG, "Service app version: " + mServiceVersion);
+
+        mConnectionCallback = new StubConnectionCallback();
+        mSubscriptionCallback = new StubSubscriptionCallback();
+        mItemCallback = new StubItemCallback();
+        mSearchCallback = new StubSearchCallback();
+        mCustomActionCallback = new CustomActionCallback();
+
+        mRootHints = new Bundle();
+        mRootHints.putBoolean(MediaBrowserServiceCompat.BrowserRoot.EXTRA_RECENT, true);
+        mRootHints.putBoolean(MediaBrowserServiceCompat.BrowserRoot.EXTRA_OFFLINE, true);
+        mRootHints.putBoolean(MediaBrowserServiceCompat.BrowserRoot.EXTRA_SUGGESTED, true);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowserCompat(getInstrumentation().getTargetContext(),
+                        TEST_BROWSER_SERVICE, mConnectionCallback, mRootHints);
+            }
+        });
+    }
+
+    @After
+    public void tearDown() {
+        if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
+            mMediaBrowser.disconnect();
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBrowserRoot() {
+        final String id = "test-id";
+        final String key = "test-key";
+        final String val = "test-val";
+        final Bundle extras = new Bundle();
+        extras.putString(key, val);
+
+        MediaBrowserServiceCompat.BrowserRoot browserRoot =
+                new MediaBrowserServiceCompat.BrowserRoot(id, extras);
+        assertEquals(id, browserRoot.getRootId());
+        assertEquals(val, browserRoot.getExtras().getString(key));
+    }
+
+    @Test
+    @SmallTest
+    public void testMediaBrowser() throws Exception {
+        assertFalse(mMediaBrowser.isConnected());
+
+        connectMediaBrowserService();
+        assertTrue(mMediaBrowser.isConnected());
+
+        assertEquals(TEST_BROWSER_SERVICE, mMediaBrowser.getServiceComponent());
+        assertEquals(MEDIA_ID_ROOT, mMediaBrowser.getRoot());
+        assertEquals(EXTRAS_VALUE, mMediaBrowser.getExtras().getString(EXTRAS_KEY));
+
+        mMediaBrowser.disconnect();
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return !mMediaBrowser.isConnected();
+            }
+        }.run();
+    }
+
+    @Test
+    @SmallTest
+    public void testGetServiceComponentBeforeConnection() {
+        try {
+            ComponentName serviceComponent = mMediaBrowser.getServiceComponent();
+            fail();
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testConnectionFailed() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowserCompat(getInstrumentation().getTargetContext(),
+                        TEST_INVALID_BROWSER_SERVICE, mConnectionCallback, mRootHints);
+            }
+        });
+
+        synchronized (mConnectionCallback.mWaitLock) {
+            mMediaBrowser.connect();
+            mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+        }
+        assertEquals(1, mConnectionCallback.mConnectionFailedCount);
+        assertEquals(0, mConnectionCallback.mConnectedCount);
+        assertEquals(0, mConnectionCallback.mConnectionSuspendedCount);
+    }
+
+    @Test
+    @SmallTest
+    public void testConnectTwice() throws Exception {
+        connectMediaBrowserService();
+        try {
+            mMediaBrowser.connect();
+            fail();
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testReconnection() throws Exception {
+        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);
+        }
+
+        // Test subscribe.
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.subscribe(MEDIA_ID_ROOT, mSubscriptionCallback);
+        mSubscriptionCallback.await(TIME_OUT_MS);
+        assertEquals(1, mSubscriptionCallback.mChildrenLoadedCount);
+        assertEquals(MEDIA_ID_ROOT, mSubscriptionCallback.mLastParentId);
+
+        synchronized (mItemCallback.mWaitLock) {
+            // Test getItem.
+            mItemCallback.reset();
+            mMediaBrowser.getItem(MEDIA_ID_CHILDREN[0], mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(MEDIA_ID_CHILDREN[0], mItemCallback.mLastMediaItem.getMediaId());
+        }
+
+        // Reconnect after connection was established.
+        mMediaBrowser.disconnect();
+        connectMediaBrowserService();
+
+        synchronized (mItemCallback.mWaitLock) {
+            // Test getItem.
+            mItemCallback.reset();
+            mMediaBrowser.getItem(MEDIA_ID_CHILDREN[0], mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(MEDIA_ID_CHILDREN[0], mItemCallback.mLastMediaItem.getMediaId());
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testConnectionCallbackNotCalledAfterDisconnect() {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser.connect();
+                mMediaBrowser.disconnect();
+                mConnectionCallback.reset();
+            }
+        });
+
+        try {
+            Thread.sleep(SLEEP_MS);
+        } catch (InterruptedException e) {
+            fail("Unexpected InterruptedException occurred.");
+        }
+        assertEquals(0, mConnectionCallback.mConnectedCount);
+        assertEquals(0, mConnectionCallback.mConnectionFailedCount);
+        assertEquals(0, mConnectionCallback.mConnectionSuspendedCount);
+    }
+
+    @Test
+    @MediumTest
+    public void testSubscribe() throws Exception {
+        connectMediaBrowserService();
+
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.subscribe(MEDIA_ID_ROOT, mSubscriptionCallback);
+        mSubscriptionCallback.await(TIME_OUT_MS);
+        assertEquals(1, mSubscriptionCallback.mChildrenLoadedCount);
+        assertEquals(MEDIA_ID_ROOT, mSubscriptionCallback.mLastParentId);
+        assertEquals(MEDIA_ID_CHILDREN.length, mSubscriptionCallback.mLastChildMediaItems.size());
+        for (int i = 0; i < MEDIA_ID_CHILDREN.length; ++i) {
+            assertEquals(MEDIA_ID_CHILDREN[i],
+                    mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+        }
+
+        // Test MediaBrowserServiceCompat.notifyChildrenChanged()
+        mSubscriptionCallback.reset(1);
+        callMediaBrowserServiceMethod(NOTIFY_CHILDREN_CHANGED, MEDIA_ID_ROOT, getContext());
+        mSubscriptionCallback.await(TIME_OUT_MS);
+        assertEquals(1, mSubscriptionCallback.mChildrenLoadedCount);
+
+        // Test unsubscribe.
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.unsubscribe(MEDIA_ID_ROOT);
+
+        // After unsubscribing, make StubMediaBrowserServiceCompat notify that the children are
+        // changed.
+        callMediaBrowserServiceMethod(NOTIFY_CHILDREN_CHANGED, MEDIA_ID_ROOT, getContext());
+        mSubscriptionCallback.await(WAIT_TIME_FOR_NO_RESPONSE_MS);
+
+        // onChildrenLoaded should not be called.
+        assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
+    }
+
+    @Test
+    @MediumTest
+    public void testSubscribeWithOptions() throws Exception {
+        connectMediaBrowserService();
+        final int pageSize = 3;
+        final int lastPage = (MEDIA_ID_CHILDREN.length - 1) / pageSize;
+        Bundle options = new Bundle();
+        options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
+
+        for (int page = 0; page <= lastPage; ++page) {
+            mSubscriptionCallback.reset(1);
+            options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+            mMediaBrowser.subscribe(MEDIA_ID_ROOT, options, mSubscriptionCallback);
+            mSubscriptionCallback.await(TIME_OUT_MS);
+            assertEquals(1, mSubscriptionCallback.mChildrenLoadedWithOptionCount);
+            assertEquals(MEDIA_ID_ROOT, mSubscriptionCallback.mLastParentId);
+            if (page != lastPage) {
+                assertEquals(pageSize, mSubscriptionCallback.mLastChildMediaItems.size());
+            } else {
+                assertEquals((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(MEDIA_ID_CHILDREN[page * pageSize + i],
+                        mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+            }
+
+            // Test MediaBrowserServiceCompat.notifyChildrenChanged()
+            mSubscriptionCallback.reset(page + 1);
+            callMediaBrowserServiceMethod(NOTIFY_CHILDREN_CHANGED, MEDIA_ID_ROOT, getContext());
+            mSubscriptionCallback.await(TIME_OUT_MS);
+            assertEquals(page + 1, mSubscriptionCallback.mChildrenLoadedWithOptionCount);
+        }
+
+        // Test unsubscribe with callback argument.
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.unsubscribe(MEDIA_ID_ROOT, mSubscriptionCallback);
+
+        // After unsubscribing, make StubMediaBrowserServiceCompat notify that the children are
+        // changed.
+        callMediaBrowserServiceMethod(NOTIFY_CHILDREN_CHANGED, MEDIA_ID_ROOT, getContext());
+        try {
+            Thread.sleep(SLEEP_MS);
+        } catch (InterruptedException e) {
+            fail("Unexpected InterruptedException occurred.");
+        }
+        // onChildrenLoaded should not be called.
+        assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
+    }
+
+    @Test
+    @SmallTest
+    public void testSubscribeWithOptionsIncludingCompatParcelables() throws Exception {
+        if (Build.VERSION.SDK_INT >= 26 && !VERSION_TOT.equals(mServiceVersion)) {
+            // This test will fail on API 26 or newer APIs if the service application uses
+            // support library v27.0.1 or lower versions.
+            return;
+        }
+        connectMediaBrowserService();
+
+        final String mediaId = "1000";
+        final RatingCompat percentageRating = RatingCompat.newPercentageRating(0.5f);
+        final RatingCompat starRating =
+                RatingCompat.newStarRating(RatingCompat.RATING_5_STARS, 4.0f);
+        MediaMetadataCompat mediaMetadataCompat = new MediaMetadataCompat.Builder()
+                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, mediaId)
+                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title")
+                .putRating(MediaMetadataCompat.METADATA_KEY_RATING, percentageRating)
+                .putRating(MediaMetadataCompat.METADATA_KEY_USER_RATING, starRating)
+                .build();
+        Bundle options = new Bundle();
+        options.putParcelable(MEDIA_METADATA, mediaMetadataCompat);
+
+        // Remote MediaBrowserService will create a media item with the given MediaMetadataCompat.
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.subscribe(MEDIA_ID_INCLUDE_METADATA, options, mSubscriptionCallback);
+        mSubscriptionCallback.await(TIME_OUT_MS);
+
+        assertEquals(1, mSubscriptionCallback.mChildrenLoadedWithOptionCount);
+        assertEquals(1, mSubscriptionCallback.mLastChildMediaItems.size());
+        assertEquals(mediaId, mSubscriptionCallback.mLastChildMediaItems.get(0).getMediaId());
+
+        MediaMetadataCompat metadataOut = mSubscriptionCallback.mLastOptions
+                .getParcelable(MEDIA_METADATA);
+        assertEquals(mediaId, metadataOut.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID));
+        assertEquals("title", metadataOut.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
+        assertRatingEquals(percentageRating,
+                metadataOut.getRating(MediaMetadataCompat.METADATA_KEY_RATING));
+        assertRatingEquals(starRating,
+                metadataOut.getRating(MediaMetadataCompat.METADATA_KEY_USER_RATING));
+    }
+
+    @Test
+    @MediumTest
+    public void testSubscribeDelayedItems() throws Exception {
+        connectMediaBrowserService();
+
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.subscribe(MEDIA_ID_CHILDREN_DELAYED, mSubscriptionCallback);
+        mSubscriptionCallback.await(WAIT_TIME_FOR_NO_RESPONSE_MS);
+        assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
+
+        callMediaBrowserServiceMethod(
+                SEND_DELAYED_NOTIFY_CHILDREN_CHANGED, MEDIA_ID_CHILDREN_DELAYED, getContext());
+        mSubscriptionCallback.await(TIME_OUT_MS);
+        assertEquals(1, mSubscriptionCallback.mChildrenLoadedCount);
+    }
+
+    @Test
+    @SmallTest
+    public void testSubscribeInvalidItem() throws Exception {
+        connectMediaBrowserService();
+
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.subscribe(MEDIA_ID_INVALID, mSubscriptionCallback);
+        mSubscriptionCallback.await(TIME_OUT_MS);
+        assertEquals(MEDIA_ID_INVALID, mSubscriptionCallback.mLastErrorId);
+    }
+
+    @Test
+    @SmallTest
+    public void testSubscribeInvalidItemWithOptions() throws Exception {
+        connectMediaBrowserService();
+
+        final int pageSize = 5;
+        final int page = 2;
+        Bundle options = new Bundle();
+        options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
+        options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+
+        mSubscriptionCallback.reset(1);
+        mMediaBrowser.subscribe(MEDIA_ID_INVALID, options, mSubscriptionCallback);
+        mSubscriptionCallback.await(TIME_OUT_MS);
+        assertEquals(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
+    @MediumTest
+    public void testUnsubscribeForMultipleSubscriptions() throws Exception {
+        connectMediaBrowserService();
+        final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
+        final int pageSize = 1;
+
+        // Subscribe four pages, one item per page.
+        for (int page = 0; page < 4; page++) {
+            final StubSubscriptionCallback callback = new StubSubscriptionCallback();
+            subscriptionCallbacks.add(callback);
+
+            Bundle options = new Bundle();
+            options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+            options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
+            callback.reset(1);
+            mMediaBrowser.subscribe(MEDIA_ID_ROOT, options, callback);
+            callback.await(TIME_OUT_MS);
+
+            // Each onChildrenLoaded() must be called.
+            assertEquals(1, callback.mChildrenLoadedWithOptionCount);
+        }
+
+        // Reset callbacks and unsubscribe.
+        for (StubSubscriptionCallback callback : subscriptionCallbacks) {
+            callback.reset(1);
+        }
+        mMediaBrowser.unsubscribe(MEDIA_ID_ROOT);
+
+        // After unsubscribing, make StubMediaBrowserServiceCompat notify that the children are
+        // changed.
+        callMediaBrowserServiceMethod(NOTIFY_CHILDREN_CHANGED, MEDIA_ID_ROOT, getContext());
+        try {
+            Thread.sleep(SLEEP_MS);
+        } catch (InterruptedException e) {
+            fail("Unexpected InterruptedException occurred.");
+        }
+
+        // onChildrenLoaded should not be called.
+        for (StubSubscriptionCallback callback : subscriptionCallbacks) {
+            assertEquals(0, callback.mChildrenLoadedWithOptionCount);
+        }
+    }
+
+    @Test
+    @MediumTest
+    @FlakyTest(bugId = 74093976)
+    public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Exception {
+        connectMediaBrowserService();
+        final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
+        final int pageSize = 1;
+
+        // Subscribe four pages, one item per page.
+        for (int page = 0; page < 4; page++) {
+            final StubSubscriptionCallback callback = new StubSubscriptionCallback();
+            subscriptionCallbacks.add(callback);
+
+            Bundle options = new Bundle();
+            options.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
+            options.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
+            callback.reset(1);
+            mMediaBrowser.subscribe(MEDIA_ID_ROOT, options, callback);
+            callback.await(TIME_OUT_MS);
+
+            // Each onChildrenLoaded() must be called.
+            assertEquals(1, callback.mChildrenLoadedWithOptionCount);
+        }
+
+        // Unsubscribe existing subscriptions one-by-one.
+        final int[] orderOfRemovingCallbacks = {2, 0, 3, 1};
+        for (int i = 0; i < orderOfRemovingCallbacks.length; i++) {
+            // Reset callbacks
+            for (StubSubscriptionCallback callback : subscriptionCallbacks) {
+                callback.reset(1);
+            }
+
+            // Remove one subscription
+            mMediaBrowser.unsubscribe(MEDIA_ID_ROOT,
+                    subscriptionCallbacks.get(orderOfRemovingCallbacks[i]));
+
+            // Make StubMediaBrowserServiceCompat notify that the children are changed.
+            callMediaBrowserServiceMethod(NOTIFY_CHILDREN_CHANGED, MEDIA_ID_ROOT, getContext());
+            try {
+                Thread.sleep(SLEEP_MS);
+            } catch (InterruptedException e) {
+                fail("Unexpected InterruptedException occurred.");
+            }
+
+            // Only the remaining subscriptionCallbacks should be called.
+            for (int j = 0; j < 4; j++) {
+                int childrenLoadedWithOptionsCount = subscriptionCallbacks
+                        .get(orderOfRemovingCallbacks[j]).mChildrenLoadedWithOptionCount;
+                if (j <= i) {
+                    assertEquals(0, childrenLoadedWithOptionsCount);
+                } else {
+                    assertEquals(1, childrenLoadedWithOptionsCount);
+                }
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testGetItem() throws Exception {
+        connectMediaBrowserService();
+
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(MEDIA_ID_CHILDREN[0], mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertNotNull(mItemCallback.mLastMediaItem);
+            assertEquals(MEDIA_ID_CHILDREN[0], mItemCallback.mLastMediaItem.getMediaId());
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testGetItemDelayed() throws Exception {
+        connectMediaBrowserService();
+
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(MEDIA_ID_CHILDREN_DELAYED, mItemCallback);
+            mItemCallback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertNull(mItemCallback.mLastMediaItem);
+
+            mItemCallback.reset();
+            callMediaBrowserServiceMethod(SEND_DELAYED_ITEM_LOADED, new Bundle(), getContext());
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertNotNull(mItemCallback.mLastMediaItem);
+            assertEquals(MEDIA_ID_CHILDREN_DELAYED, mItemCallback.mLastMediaItem.getMediaId());
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testGetItemWhenOnLoadItemIsNotImplemented() throws Exception {
+        connectMediaBrowserService();
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED, mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(MEDIA_ID_ON_LOAD_ITEM_NOT_IMPLEMENTED, mItemCallback.mLastErrorId);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testGetItemWhenMediaIdIsInvalid() throws Exception {
+        mItemCallback.mLastMediaItem = new MediaItem(new MediaDescriptionCompat.Builder()
+                .setMediaId("dummy_id").build(), MediaItem.FLAG_BROWSABLE);
+
+        connectMediaBrowserService();
+        synchronized (mItemCallback.mWaitLock) {
+            mMediaBrowser.getItem(MEDIA_ID_INVALID, mItemCallback);
+            mItemCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertNull(mItemCallback.mLastMediaItem);
+            assertNull(mItemCallback.mLastErrorId);
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testSearch() throws Exception {
+        connectMediaBrowserService();
+
+        final String key = "test-key";
+        final String val = "test-val";
+
+        synchronized (mSearchCallback.mWaitLock) {
+            mSearchCallback.reset();
+            mMediaBrowser.search(SEARCH_QUERY_FOR_NO_RESULT, null, mSearchCallback);
+            mSearchCallback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertTrue(mSearchCallback.mOnSearchResult);
+            assertTrue(mSearchCallback.mSearchResults != null
+                    && mSearchCallback.mSearchResults.size() == 0);
+            assertEquals(null, mSearchCallback.mSearchExtras);
+
+            mSearchCallback.reset();
+            mMediaBrowser.search(SEARCH_QUERY_FOR_ERROR, null, mSearchCallback);
+            mSearchCallback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertTrue(mSearchCallback.mOnSearchResult);
+            assertNull(mSearchCallback.mSearchResults);
+            assertEquals(null, mSearchCallback.mSearchExtras);
+
+            mSearchCallback.reset();
+            Bundle extras = new Bundle();
+            extras.putString(key, val);
+            mMediaBrowser.search(SEARCH_QUERY, extras, mSearchCallback);
+            mSearchCallback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertTrue(mSearchCallback.mOnSearchResult);
+            assertNotNull(mSearchCallback.mSearchResults);
+            for (MediaItem item : mSearchCallback.mSearchResults) {
+                assertNotNull(item.getMediaId());
+                assertTrue(item.getMediaId().contains(SEARCH_QUERY));
+            }
+            assertNotNull(mSearchCallback.mSearchExtras);
+            assertEquals(val, mSearchCallback.mSearchExtras.getString(key));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testSendCustomAction() throws Exception {
+        connectMediaBrowserService();
+
+        synchronized (mCustomActionCallback.mWaitLock) {
+            Bundle customActionExtras = new Bundle();
+            customActionExtras.putString(TEST_KEY_1, TEST_VALUE_1);
+            mMediaBrowser.sendCustomAction(
+                    CUSTOM_ACTION, customActionExtras, mCustomActionCallback);
+            mCustomActionCallback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+
+            mCustomActionCallback.reset();
+            Bundle data1 = new Bundle();
+            data1.putString(TEST_KEY_2, TEST_VALUE_2);
+            callMediaBrowserServiceMethod(CUSTOM_ACTION_SEND_PROGRESS_UPDATE, data1, getContext());
+            mCustomActionCallback.mWaitLock.wait(TIME_OUT_MS);
+
+            assertTrue(mCustomActionCallback.mOnProgressUpdateCalled);
+            assertEquals(CUSTOM_ACTION, mCustomActionCallback.mAction);
+            assertNotNull(mCustomActionCallback.mExtras);
+            assertEquals(TEST_VALUE_1, mCustomActionCallback.mExtras.getString(TEST_KEY_1));
+            assertNotNull(mCustomActionCallback.mData);
+            assertEquals(TEST_VALUE_2, mCustomActionCallback.mData.getString(TEST_KEY_2));
+
+            mCustomActionCallback.reset();
+            Bundle data2 = new Bundle();
+            data2.putString(TEST_KEY_3, TEST_VALUE_3);
+            callMediaBrowserServiceMethod(CUSTOM_ACTION_SEND_PROGRESS_UPDATE, data2, getContext());
+            mCustomActionCallback.mWaitLock.wait(TIME_OUT_MS);
+
+            assertTrue(mCustomActionCallback.mOnProgressUpdateCalled);
+            assertEquals(CUSTOM_ACTION, mCustomActionCallback.mAction);
+            assertNotNull(mCustomActionCallback.mExtras);
+            assertEquals(TEST_VALUE_1, mCustomActionCallback.mExtras.getString(TEST_KEY_1));
+            assertNotNull(mCustomActionCallback.mData);
+            assertEquals(TEST_VALUE_3, mCustomActionCallback.mData.getString(TEST_KEY_3));
+
+            Bundle resultData = new Bundle();
+            resultData.putString(TEST_KEY_4, TEST_VALUE_4);
+            mCustomActionCallback.reset();
+            callMediaBrowserServiceMethod(CUSTOM_ACTION_SEND_RESULT, resultData, getContext());
+            mCustomActionCallback.mWaitLock.wait(TIME_OUT_MS);
+
+            assertTrue(mCustomActionCallback.mOnResultCalled);
+            assertEquals(CUSTOM_ACTION, mCustomActionCallback.mAction);
+            assertNotNull(mCustomActionCallback.mExtras);
+            assertEquals(TEST_VALUE_1, mCustomActionCallback.mExtras.getString(TEST_KEY_1));
+            assertNotNull(mCustomActionCallback.mData);
+            assertEquals(TEST_VALUE_4, mCustomActionCallback.mData.getString(TEST_KEY_4));
+        }
+    }
+
+
+    @Test
+    @MediumTest
+    public void testSendCustomActionWithDetachedError() throws Exception {
+        connectMediaBrowserService();
+
+        synchronized (mCustomActionCallback.mWaitLock) {
+            Bundle customActionExtras = new Bundle();
+            customActionExtras.putString(TEST_KEY_1, TEST_VALUE_1);
+            mMediaBrowser.sendCustomAction(
+                    CUSTOM_ACTION, customActionExtras, mCustomActionCallback);
+            mCustomActionCallback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+
+            mCustomActionCallback.reset();
+            Bundle progressUpdateData = new Bundle();
+            progressUpdateData.putString(TEST_KEY_2, TEST_VALUE_2);
+            callMediaBrowserServiceMethod(
+                    CUSTOM_ACTION_SEND_PROGRESS_UPDATE, progressUpdateData, getContext());
+            mCustomActionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCustomActionCallback.mOnProgressUpdateCalled);
+            assertEquals(CUSTOM_ACTION, mCustomActionCallback.mAction);
+            assertNotNull(mCustomActionCallback.mExtras);
+            assertEquals(TEST_VALUE_1, mCustomActionCallback.mExtras.getString(TEST_KEY_1));
+            assertNotNull(mCustomActionCallback.mData);
+            assertEquals(TEST_VALUE_2, mCustomActionCallback.mData.getString(TEST_KEY_2));
+
+            mCustomActionCallback.reset();
+            Bundle errorData = new Bundle();
+            errorData.putString(TEST_KEY_3, TEST_VALUE_3);
+            callMediaBrowserServiceMethod(CUSTOM_ACTION_SEND_ERROR, errorData, getContext());
+            mCustomActionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCustomActionCallback.mOnErrorCalled);
+            assertEquals(CUSTOM_ACTION, mCustomActionCallback.mAction);
+            assertNotNull(mCustomActionCallback.mExtras);
+            assertEquals(TEST_VALUE_1, mCustomActionCallback.mExtras.getString(TEST_KEY_1));
+            assertNotNull(mCustomActionCallback.mData);
+            assertEquals(TEST_VALUE_3, mCustomActionCallback.mData.getString(TEST_KEY_3));
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void testSendCustomActionWithNullCallback() throws Exception {
+        connectMediaBrowserService();
+
+        Bundle customActionExtras = new Bundle();
+        customActionExtras.putString(TEST_KEY_1, TEST_VALUE_1);
+        mMediaBrowser.sendCustomAction(CUSTOM_ACTION, customActionExtras, null);
+        // Wait some time so that the service can get a result receiver for the custom action.
+        Thread.sleep(WAIT_TIME_FOR_NO_RESPONSE_MS);
+
+        // These calls should not make any exceptions.
+        callMediaBrowserServiceMethod(CUSTOM_ACTION_SEND_PROGRESS_UPDATE, new Bundle(),
+                getContext());
+        callMediaBrowserServiceMethod(CUSTOM_ACTION_SEND_RESULT, new Bundle(), getContext());
+        Thread.sleep(WAIT_TIME_FOR_NO_RESPONSE_MS);
+    }
+
+    @Test
+    @SmallTest
+    public void testSendCustomActionWithError() throws Exception {
+        connectMediaBrowserService();
+
+        synchronized (mCustomActionCallback.mWaitLock) {
+            mMediaBrowser.sendCustomAction(CUSTOM_ACTION_FOR_ERROR, null, mCustomActionCallback);
+            mCustomActionCallback.mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCustomActionCallback.mOnErrorCalled);
+        }
+    }
+
+    @Test
+    @MediumTest
+    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;
+        }
+        final ConnectionCallbackForDelayedMediaSession callback =
+                new ConnectionCallbackForDelayedMediaSession();
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowserCompat(
+                        getInstrumentation().getTargetContext(),
+                        TEST_BROWSER_SERVICE_DELAYED_MEDIA_SESSION,
+                        callback,
+                        null);
+            }
+        });
+
+        synchronized (callback.mWaitLock) {
+            mMediaBrowser.connect();
+            callback.mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertEquals(0, callback.mConnectedCount);
+
+            callMediaBrowserServiceMethod(SET_SESSION_TOKEN, new Bundle(), getContext());
+            callback.mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(1, callback.mConnectedCount);
+
+            if (Build.VERSION.SDK_INT >= 21) {
+                assertNotNull(mMediaBrowser.getSessionToken().getExtraBinder());
+            }
+        }
+    }
+
+    private void connectMediaBrowserService() throws Exception {
+        synchronized (mConnectionCallback.mWaitLock) {
+            mMediaBrowser.connect();
+            mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+            if (!mMediaBrowser.isConnected()) {
+                fail("Browser failed to connect!");
+            }
+        }
+    }
+
+    private void assertRatingEquals(RatingCompat expected, RatingCompat observed) {
+        if (expected == null || observed == null) {
+            assertSame(expected, observed);
+        }
+        assertEquals(expected.getRatingStyle(), observed.getRatingStyle());
+
+        if (expected.getRatingStyle() == RatingCompat.RATING_PERCENTAGE) {
+            assertEquals(expected.getPercentRating(), observed.getPercentRating(), 0.01f);
+        } else if (expected.getRatingStyle() == RatingCompat.RATING_5_STARS) {
+            assertEquals(expected.getStarRating(), observed.getStarRating(), 0.01f);
+        } else {
+            // Currently, we use only star and percentage rating.
+            fail("Rating style should be either percentage rating or star rating.");
+        }
+    }
+
+    private class StubConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
+        final Object mWaitLock = new Object();
+        volatile int mConnectedCount;
+        volatile int mConnectionFailedCount;
+        volatile int mConnectionSuspendedCount;
+
+        public void reset() {
+            mConnectedCount = 0;
+            mConnectionFailedCount = 0;
+            mConnectionSuspendedCount = 0;
+        }
+
+        @Override
+        public void onConnected() {
+            synchronized (mWaitLock) {
+                mConnectedCount++;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            synchronized (mWaitLock) {
+                mConnectionFailedCount++;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            synchronized (mWaitLock) {
+                mConnectionSuspendedCount++;
+                mWaitLock.notify();
+            }
+        }
+    }
+
+    private class StubSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
+        private CountDownLatch mLatch;
+        private volatile int mChildrenLoadedCount;
+        private volatile int mChildrenLoadedWithOptionCount;
+        private volatile String mLastErrorId;
+        private volatile String mLastParentId;
+        private volatile Bundle mLastOptions;
+        private volatile List<MediaItem> mLastChildMediaItems;
+
+        public void reset(int count) {
+            mLatch = new CountDownLatch(count);
+            mChildrenLoadedCount = 0;
+            mChildrenLoadedWithOptionCount = 0;
+            mLastErrorId = null;
+            mLastParentId = null;
+            mLastOptions = null;
+            mLastChildMediaItems = null;
+        }
+
+        public boolean await(long timeoutMs) {
+            try {
+                return mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+
+        @Override
+        public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children) {
+            mChildrenLoadedCount++;
+            mLastParentId = parentId;
+            mLastChildMediaItems = children;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children,
+                @NonNull Bundle options) {
+            mChildrenLoadedWithOptionCount++;
+            mLastParentId = parentId;
+            mLastOptions = options;
+            mLastChildMediaItems = children;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onError(@NonNull String id) {
+            mLastErrorId = id;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onError(@NonNull String id, @NonNull Bundle options) {
+            mLastErrorId = id;
+            mLastOptions = options;
+            mLatch.countDown();
+        }
+    }
+
+    private class StubItemCallback extends MediaBrowserCompat.ItemCallback {
+        final Object mWaitLock = new Object();
+        private volatile MediaItem mLastMediaItem;
+        private volatile String mLastErrorId;
+
+        public void reset() {
+            mLastMediaItem = null;
+            mLastErrorId = null;
+        }
+
+        @Override
+        public void onItemLoaded(MediaItem item) {
+            synchronized (mWaitLock) {
+                mLastMediaItem = item;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onError(@NonNull String id) {
+            synchronized (mWaitLock) {
+                mLastErrorId = id;
+                mWaitLock.notify();
+            }
+        }
+    }
+
+    private class StubSearchCallback extends MediaBrowserCompat.SearchCallback {
+        final Object mWaitLock = new Object();
+        boolean mOnSearchResult;
+        Bundle mSearchExtras;
+        List<MediaItem> mSearchResults;
+
+        @Override
+        public void onSearchResult(@NonNull String query, Bundle extras,
+                @NonNull List<MediaItem> items) {
+            synchronized (mWaitLock) {
+                mOnSearchResult = true;
+                mSearchResults = items;
+                mSearchExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onError(@NonNull String query, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnSearchResult = true;
+                mSearchResults = null;
+                mSearchExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        public void reset() {
+            mOnSearchResult = false;
+            mSearchExtras = null;
+            mSearchResults = null;
+        }
+    }
+
+    private class CustomActionCallback extends MediaBrowserCompat.CustomActionCallback {
+        final Object mWaitLock = new Object();
+        String mAction;
+        Bundle mExtras;
+        Bundle mData;
+        boolean mOnProgressUpdateCalled;
+        boolean mOnResultCalled;
+        boolean mOnErrorCalled;
+
+        @Override
+        public void onProgressUpdate(String action, Bundle extras, Bundle data) {
+            synchronized (mWaitLock) {
+                mOnProgressUpdateCalled = true;
+                mAction = action;
+                mExtras = extras;
+                mData = data;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onResult(String action, Bundle extras, Bundle resultData) {
+            synchronized (mWaitLock) {
+                mOnResultCalled = true;
+                mAction = action;
+                mExtras = extras;
+                mData = resultData;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onError(String action, Bundle extras, Bundle data) {
+            synchronized (mWaitLock) {
+                mOnErrorCalled = true;
+                mAction = action;
+                mExtras = extras;
+                mData = data;
+                mWaitLock.notify();
+            }
+        }
+
+        public void reset() {
+            mOnResultCalled = false;
+            mOnProgressUpdateCalled = false;
+            mOnErrorCalled = false;
+            mAction = null;
+            mExtras = null;
+            mData = null;
+        }
+    }
+
+    private class ConnectionCallbackForDelayedMediaSession extends
+            MediaBrowserCompat.ConnectionCallback {
+        final Object mWaitLock = new Object();
+        private int mConnectedCount = 0;
+
+        @Override
+        public void onConnected() {
+            synchronized (mWaitLock) {
+                mConnectedCount++;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            synchronized (mWaitLock) {
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            synchronized (mWaitLock) {
+                mWaitLock.notify();
+            }
+        }
+    }
+}
diff --git a/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java b/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java
new file mode 100644
index 0000000..bc03f1a
--- /dev/null
+++ b/android/support/mediacompat/client/MediaControllerCompatCallbackTest.java
@@ -0,0 +1,774 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.client;
+
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.support.mediacompat.testlib.MediaSessionConstants.RELEASE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SEND_SESSION_EVENT;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_CAPTIONING_ENABLED;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_EXTRAS;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_FLAGS;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_METADATA;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_PLAYBACK_STATE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_PLAYBACK_TO_LOCAL;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_PLAYBACK_TO_REMOTE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_QUEUE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_QUEUE_TITLE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_RATING_TYPE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_REPEAT_MODE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_SESSION_ACTIVITY;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_SHUFFLE_MODE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_ACTION;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_CURRENT_VOLUME;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_ERROR_CODE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_ERROR_MSG;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_FLAGS;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_KEY;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MAX_VOLUME;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MEDIA_ID_1;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MEDIA_ID_2;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_QUEUE_ID_1;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_QUEUE_ID_2;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_SESSION_EVENT;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_VALUE;
+import static android.support.mediacompat.testlib.VersionConstants.KEY_SERVICE_VERSION;
+import static android.support.mediacompat.testlib.util.IntentUtil.SERVICE_PACKAGE_NAME;
+import static android.support.mediacompat.testlib.util.IntentUtil.callMediaSessionMethod;
+import static android.support.mediacompat.testlib.util.TestUtil.assertBundleEquals;
+import static android.support.test.InstrumentationRegistry.getArguments;
+import static android.support.test.InstrumentationRegistry.getContext;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_RATING;
+
+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 static org.junit.Assert.fail;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+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;
+import android.support.mediacompat.testlib.util.PollingCheck;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.RatingCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.MediaSessionCompat.QueueItem;
+import android.support.v4.media.session.ParcelableVolumeInfo;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
+
+import androidx.media.VolumeProviderCompat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test {@link MediaControllerCompat.Callback}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MediaControllerCompatCallbackTest {
+
+    private static final String TAG = "MediaControllerCompatCallbackTest";
+
+    // The maximum time to wait for an operation, that is expected to happen.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
+
+    private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+            SERVICE_PACKAGE_NAME,
+            "android.support.mediacompat.service.StubMediaBrowserServiceCompat");
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Object mWaitLock = new Object();
+
+    private String mServiceVersion;
+
+    // MediaBrowserCompat object to get the session token.
+    private MediaBrowserCompat mMediaBrowser;
+    private ConnectionCallback mConnectionCallback = new ConnectionCallback();
+
+    private MediaSessionCompat.Token mSessionToken;
+    private MediaControllerCompat mController;
+    private MediaControllerCallback mMediaControllerCallback = new MediaControllerCallback();
+
+    @Before
+    public void setUp() throws Exception {
+        // The version of the service app is provided through the instrumentation arguments.
+        mServiceVersion = getArguments().getString(KEY_SERVICE_VERSION, "");
+        Log.d(TAG, "Service app version: " + mServiceVersion);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mMediaBrowser = new MediaBrowserCompat(getInstrumentation().getTargetContext(),
+                        TEST_BROWSER_SERVICE, mConnectionCallback, new Bundle());
+            }
+        });
+
+        synchronized (mConnectionCallback.mWaitLock) {
+            mMediaBrowser.connect();
+            mConnectionCallback.mWaitLock.wait(TIME_OUT_MS);
+            if (!mMediaBrowser.isConnected()) {
+                fail("Browser failed to connect!");
+            }
+        }
+        mSessionToken = mMediaBrowser.getSessionToken();
+        mController = new MediaControllerCompat(getTargetContext(), mSessionToken);
+        mController.registerCallback(mMediaControllerCallback, mHandler);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
+            mMediaBrowser.disconnect();
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testGetPackageName() {
+        assertEquals(SERVICE_PACKAGE_NAME, mController.getPackageName());
+    }
+
+    @Test
+    @SmallTest
+    public void testIsSessionReady() throws Exception {
+        // mController already has the extra binder since it was created with the session token
+        // which holds the extra binder.
+        assertTrue(mController.isSessionReady());
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setExtras}.
+     */
+    @Test
+    @SmallTest
+    public void testSetExtras() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+
+            Bundle extras = new Bundle();
+            extras.putString(TEST_KEY, TEST_VALUE);
+            callMediaSessionMethod(SET_EXTRAS, extras, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnExtraChangedCalled);
+
+            assertBundleEquals(extras, mMediaControllerCallback.mExtras);
+            assertBundleEquals(extras, mController.getExtras());
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setFlags}.
+     */
+    @Test
+    @SmallTest
+    public void testSetFlags() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+
+            callMediaSessionMethod(SET_FLAGS, TEST_FLAGS, getContext());
+            new PollingCheck(TIME_OUT_MS) {
+                @Override
+                public boolean check() {
+                    return TEST_FLAGS == mController.getFlags();
+                }
+            }.run();
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setMetadata}.
+     */
+    @Test
+    @SmallTest
+    public void testSetMetadata() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            RatingCompat rating = RatingCompat.newHeartRating(true);
+            MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
+                    .putString(TEST_KEY, TEST_VALUE)
+                    .putRating(METADATA_KEY_RATING, rating)
+                    .build();
+
+            callMediaSessionMethod(SET_METADATA, metadata, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnMetadataChangedCalled);
+
+            MediaMetadataCompat metadataOut = mMediaControllerCallback.mMediaMetadata;
+            assertNotNull(metadataOut);
+            assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+            metadataOut = mController.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 {
+        // TODO: Add test with a large bitmap.
+        // Using large bitmap makes other tests that are executed after this fail.
+        final Bitmap bitmapSmall = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
+                    .putString(TEST_KEY, TEST_VALUE)
+                    .putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmapSmall)
+                    .build();
+
+            callMediaSessionMethod(SET_METADATA, metadata, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnMetadataChangedCalled);
+
+            MediaMetadataCompat metadataOut = mMediaControllerCallback.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());
+
+            bitmapSmallOut.recycle();
+        }
+        bitmapSmall.recycle();
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setPlaybackState}.
+     */
+    @Test
+    @SmallTest
+    public void testSetPlaybackState() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            PlaybackStateCompat state =
+                    new PlaybackStateCompat.Builder()
+                            .setActions(TEST_ACTION)
+                            .setErrorMessage(TEST_ERROR_CODE, TEST_ERROR_MSG)
+                            .build();
+
+            callMediaSessionMethod(SET_PLAYBACK_STATE, state, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnPlaybackStateChangedCalled);
+
+            PlaybackStateCompat stateOut = mMediaControllerCallback.mPlaybackState;
+            assertNotNull(stateOut);
+            assertEquals(TEST_ACTION, stateOut.getActions());
+            assertEquals(TEST_ERROR_CODE, stateOut.getErrorCode());
+            assertEquals(TEST_ERROR_MSG, stateOut.getErrorMessage().toString());
+
+            stateOut = mController.getPlaybackState();
+            assertNotNull(stateOut);
+            assertEquals(TEST_ACTION, stateOut.getActions());
+            assertEquals(TEST_ERROR_CODE, stateOut.getErrorCode());
+            assertEquals(TEST_ERROR_MSG, stateOut.getErrorMessage().toString());
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setQueue} and {@link MediaSessionCompat#setQueueTitle}.
+     */
+    @Test
+    @SmallTest
+    public void testSetQueueAndSetQueueTitle() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            List<QueueItem> queue = new ArrayList<>();
+
+            MediaDescriptionCompat description1 =
+                    new MediaDescriptionCompat.Builder().setMediaId(TEST_MEDIA_ID_1).build();
+            MediaDescriptionCompat description2 =
+                    new MediaDescriptionCompat.Builder().setMediaId(TEST_MEDIA_ID_2).build();
+            QueueItem item1 = new MediaSessionCompat.QueueItem(description1, TEST_QUEUE_ID_1);
+            QueueItem item2 = new MediaSessionCompat.QueueItem(description2, TEST_QUEUE_ID_2);
+            queue.add(item1);
+            queue.add(item2);
+
+            callMediaSessionMethod(SET_QUEUE, queue, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnQueueChangedCalled);
+
+            callMediaSessionMethod(SET_QUEUE_TITLE, TEST_VALUE, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnQueueTitleChangedCalled);
+
+            assertEquals(TEST_VALUE, mMediaControllerCallback.mTitle);
+            assertQueueEquals(queue, mMediaControllerCallback.mQueue);
+
+            assertEquals(TEST_VALUE, mController.getQueueTitle());
+            assertQueueEquals(queue, mController.getQueue());
+
+            mMediaControllerCallback.resetLocked();
+            callMediaSessionMethod(SET_QUEUE, null, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnQueueChangedCalled);
+
+            callMediaSessionMethod(SET_QUEUE_TITLE, null, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnQueueTitleChangedCalled);
+
+            assertNull(mMediaControllerCallback.mTitle);
+            assertNull(mMediaControllerCallback.mQueue);
+            assertNull(mController.getQueueTitle());
+            assertNull(mController.getQueue());
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setSessionActivity}.
+     */
+    @Test
+    @SmallTest
+    public void testSessionActivity() throws Exception {
+        synchronized (mWaitLock) {
+            Intent intent = new Intent("MEDIA_SESSION_ACTION");
+            final int requestCode = 555;
+            final PendingIntent pi =
+                    PendingIntent.getActivity(getTargetContext(), requestCode, intent, 0);
+
+            callMediaSessionMethod(SET_SESSION_ACTIVITY, pi, getContext());
+            new PollingCheck(TIME_OUT_MS) {
+                @Override
+                public boolean check() {
+                    return pi.equals(mController.getSessionActivity());
+                }
+            }.run();
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setCaptioningEnabled}.
+     */
+    @Test
+    @SmallTest
+    public void testSetCaptioningEnabled() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            callMediaSessionMethod(SET_CAPTIONING_ENABLED, true, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnCaptioningEnabledChangedCalled);
+            assertEquals(true, mMediaControllerCallback.mCaptioningEnabled);
+            assertEquals(true, mController.isCaptioningEnabled());
+
+            mMediaControllerCallback.resetLocked();
+            callMediaSessionMethod(SET_CAPTIONING_ENABLED, false, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnCaptioningEnabledChangedCalled);
+            assertEquals(false, mMediaControllerCallback.mCaptioningEnabled);
+            assertEquals(false, mController.isCaptioningEnabled());
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setRepeatMode}.
+     */
+    @Test
+    @SmallTest
+    public void testSetRepeatMode() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            final int repeatMode = PlaybackStateCompat.REPEAT_MODE_ALL;
+            callMediaSessionMethod(SET_REPEAT_MODE, repeatMode, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnRepeatModeChangedCalled);
+            assertEquals(repeatMode, mMediaControllerCallback.mRepeatMode);
+            assertEquals(repeatMode, mController.getRepeatMode());
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setShuffleMode}.
+     */
+    @Test
+    @SmallTest
+    public void testSetShuffleMode() throws Exception {
+        final int shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_ALL;
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            callMediaSessionMethod(SET_SHUFFLE_MODE, shuffleMode, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnShuffleModeChangedCalled);
+            assertEquals(shuffleMode, mMediaControllerCallback.mShuffleMode);
+            assertEquals(shuffleMode, mController.getShuffleMode());
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#sendSessionEvent}.
+     */
+    @Test
+    @SmallTest
+    public void testSendSessionEvent() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+
+            Bundle arguments = new Bundle();
+            arguments.putString("event", TEST_SESSION_EVENT);
+
+            Bundle extras = new Bundle();
+            extras.putString(TEST_KEY, TEST_VALUE);
+            arguments.putBundle("extras", extras);
+            callMediaSessionMethod(SEND_SESSION_EVENT, arguments, getContext());
+
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnSessionEventCalled);
+            assertEquals(TEST_SESSION_EVENT, mMediaControllerCallback.mEvent);
+            assertBundleEquals(extras, mMediaControllerCallback.mExtras);
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#release}.
+     */
+    @Test
+    @SmallTest
+    public void testRelease() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            callMediaSessionMethod(RELEASE, null, getContext());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mMediaControllerCallback.mOnSessionDestroyedCalled);
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setPlaybackToLocal} and
+     * {@link MediaSessionCompat#setPlaybackToRemote}.
+     */
+    @LargeTest
+    public void testPlaybackToLocalAndRemote() throws Exception {
+        synchronized (mWaitLock) {
+            mMediaControllerCallback.resetLocked();
+            ParcelableVolumeInfo volumeInfo = new ParcelableVolumeInfo(
+                    MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
+                    STREAM_MUSIC,
+                    VolumeProviderCompat.VOLUME_CONTROL_FIXED,
+                    TEST_MAX_VOLUME,
+                    TEST_CURRENT_VOLUME);
+
+            callMediaSessionMethod(SET_PLAYBACK_TO_REMOTE, volumeInfo, getContext());
+            MediaControllerCompat.PlaybackInfo info = null;
+            for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
+                mMediaControllerCallback.mOnAudioInfoChangedCalled = false;
+                mWaitLock.wait(TIME_OUT_MS);
+                assertTrue(mMediaControllerCallback.mOnAudioInfoChangedCalled);
+                info = mMediaControllerCallback.mPlaybackInfo;
+                if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
+                        && info.getMaxVolume() == TEST_MAX_VOLUME
+                        && info.getVolumeControl() == VolumeProviderCompat.VOLUME_CONTROL_FIXED
+                        && info.getPlaybackType()
+                                == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+                    break;
+                }
+            }
+            assertNotNull(info);
+            assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
+                    info.getPlaybackType());
+            assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+            assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+            assertEquals(VolumeProviderCompat.VOLUME_CONTROL_FIXED,
+                    info.getVolumeControl());
+
+            info = mController.getPlaybackInfo();
+            assertNotNull(info);
+            assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
+                    info.getPlaybackType());
+            assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+            assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+            assertEquals(VolumeProviderCompat.VOLUME_CONTROL_FIXED, info.getVolumeControl());
+
+            // test setPlaybackToLocal
+            mMediaControllerCallback.mOnAudioInfoChangedCalled = false;
+            callMediaSessionMethod(SET_PLAYBACK_TO_LOCAL, AudioManager.STREAM_RING, getContext());
+
+            // In API 21 and 22, onAudioInfoChanged is not called.
+            if (Build.VERSION.SDK_INT == 21 || Build.VERSION.SDK_INT == 22) {
+                Thread.sleep(TIME_OUT_MS);
+            } else {
+                mWaitLock.wait(TIME_OUT_MS);
+                assertTrue(mMediaControllerCallback.mOnAudioInfoChangedCalled);
+            }
+
+            info = mController.getPlaybackInfo();
+            assertNotNull(info);
+            assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
+                    info.getPlaybackType());
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testGetRatingType() {
+        assertEquals("Default rating type of a session must be RatingCompat.RATING_NONE",
+                RatingCompat.RATING_NONE, mController.getRatingType());
+
+        callMediaSessionMethod(SET_RATING_TYPE, RatingCompat.RATING_5_STARS, getContext());
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            public boolean check() {
+                return RatingCompat.RATING_5_STARS == mController.getRatingType();
+            }
+        }.run();
+    }
+
+    @Test
+    @SmallTest
+    public void testSessionReady() throws Exception {
+        if (android.os.Build.VERSION.SDK_INT < 21) {
+            return;
+        }
+
+        final MediaSessionCompat.Token tokenWithoutExtraBinder =
+                MediaSessionCompat.Token.fromToken(mSessionToken.getToken());
+
+        final MediaControllerCallback callback = new MediaControllerCallback();
+        synchronized (mWaitLock) {
+            getInstrumentation().runOnMainSync(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        MediaControllerCompat controller = new MediaControllerCompat(
+                                getInstrumentation().getTargetContext(), tokenWithoutExtraBinder);
+                        controller.registerCallback(callback, new Handler());
+                        assertFalse(controller.isSessionReady());
+                    } catch (Exception e) {
+                        fail();
+                    }
+                }
+            });
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(callback.mOnSessionReadyCalled);
+        }
+    }
+
+    private void assertQueueEquals(List<QueueItem> expected, List<QueueItem> observed) {
+        if (expected == null || observed == null) {
+            assertTrue(expected == observed);
+            return;
+        }
+
+        assertEquals(expected.size(), observed.size());
+        for (int i = 0; i < expected.size(); i++) {
+            QueueItem expectedItem = expected.get(i);
+            QueueItem observedItem = observed.get(i);
+
+            assertEquals(expectedItem.getQueueId(), observedItem.getQueueId());
+            assertEquals(expectedItem.getDescription().getMediaId(),
+                    observedItem.getDescription().getMediaId());
+        }
+    }
+
+    private class MediaControllerCallback extends MediaControllerCompat.Callback {
+        private volatile boolean mOnPlaybackStateChangedCalled;
+        private volatile boolean mOnMetadataChangedCalled;
+        private volatile boolean mOnQueueChangedCalled;
+        private volatile boolean mOnQueueTitleChangedCalled;
+        private volatile boolean mOnExtraChangedCalled;
+        private volatile boolean mOnAudioInfoChangedCalled;
+        private volatile boolean mOnSessionDestroyedCalled;
+        private volatile boolean mOnSessionEventCalled;
+        private volatile boolean mOnCaptioningEnabledChangedCalled;
+        private volatile boolean mOnRepeatModeChangedCalled;
+        private volatile boolean mOnShuffleModeChangedCalled;
+        private volatile boolean mOnSessionReadyCalled;
+
+        private volatile PlaybackStateCompat mPlaybackState;
+        private volatile MediaMetadataCompat mMediaMetadata;
+        private volatile List<QueueItem> mQueue;
+        private volatile CharSequence mTitle;
+        private volatile String mEvent;
+        private volatile Bundle mExtras;
+        private volatile MediaControllerCompat.PlaybackInfo mPlaybackInfo;
+        private volatile boolean mCaptioningEnabled;
+        private volatile int mRepeatMode;
+        private volatile int mShuffleMode;
+
+        public void resetLocked() {
+            mOnPlaybackStateChangedCalled = false;
+            mOnMetadataChangedCalled = false;
+            mOnQueueChangedCalled = false;
+            mOnQueueTitleChangedCalled = false;
+            mOnExtraChangedCalled = false;
+            mOnAudioInfoChangedCalled = false;
+            mOnSessionDestroyedCalled = false;
+            mOnSessionEventCalled = false;
+            mOnRepeatModeChangedCalled = false;
+            mOnShuffleModeChangedCalled = false;
+
+            mPlaybackState = null;
+            mMediaMetadata = null;
+            mQueue = null;
+            mTitle = null;
+            mExtras = null;
+            mPlaybackInfo = null;
+            mCaptioningEnabled = false;
+            mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
+            mShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackStateCompat state) {
+            synchronized (mWaitLock) {
+                mOnPlaybackStateChangedCalled = true;
+                mPlaybackState = state;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadataCompat metadata) {
+            synchronized (mWaitLock) {
+                mOnMetadataChangedCalled = true;
+                mMediaMetadata = metadata;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onQueueChanged(List<QueueItem> queue) {
+            synchronized (mWaitLock) {
+                mOnQueueChangedCalled = true;
+                mQueue = queue;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onQueueTitleChanged(CharSequence title) {
+            synchronized (mWaitLock) {
+                mOnQueueTitleChangedCalled = true;
+                mTitle = title;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onExtrasChanged(Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnExtraChangedCalled = true;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onAudioInfoChanged(MediaControllerCompat.PlaybackInfo info) {
+            synchronized (mWaitLock) {
+                mOnAudioInfoChangedCalled = true;
+                mPlaybackInfo = info;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSessionDestroyed() {
+            synchronized (mWaitLock) {
+                mOnSessionDestroyedCalled = true;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSessionEvent(String event, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnSessionEventCalled = true;
+                mEvent = event;
+                mExtras = (Bundle) extras.clone();
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onCaptioningEnabledChanged(boolean enabled) {
+            synchronized (mWaitLock) {
+                mOnCaptioningEnabledChangedCalled = true;
+                mCaptioningEnabled = enabled;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onRepeatModeChanged(int repeatMode) {
+            synchronized (mWaitLock) {
+                mOnRepeatModeChangedCalled = true;
+                mRepeatMode = repeatMode;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onShuffleModeChanged(int shuffleMode) {
+            synchronized (mWaitLock) {
+                mOnShuffleModeChangedCalled = true;
+                mShuffleMode = shuffleMode;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
+        public void onSessionReady() {
+            synchronized (mWaitLock) {
+                mOnSessionReadyCalled = true;
+                mWaitLock.notify();
+            }
+        }
+    }
+
+    private class ConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
+        final Object mWaitLock = new Object();
+
+        @Override
+        public void onConnected() {
+            synchronized (mWaitLock) {
+                mWaitLock.notify();
+            }
+        }
+    }
+}
diff --git a/android/support/mediacompat/client/MediaItemTest.java b/android/support/mediacompat/client/MediaItemTest.java
new file mode 100644
index 0000000..179a178
--- /dev/null
+++ b/android/support/mediacompat/client/MediaItemTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.mediacompat.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.v4.media.MediaBrowserCompat.MediaItem;
+import android.support.v4.media.MediaDescriptionCompat;
+
+import org.junit.Test;
+
+/**
+ * Test {@link MediaItem}.
+ */
+public class MediaItemTest {
+    private static final String DESCRIPTION = "test_description";
+    private static final String MEDIA_ID = "test_media_id";
+    private static final String TITLE = "test_title";
+    private static final String SUBTITLE = "test_subtitle";
+
+    @Test
+    @SmallTest
+    public void testBrowsableMediaItem() {
+        MediaDescriptionCompat description =
+                new MediaDescriptionCompat.Builder()
+                        .setDescription(DESCRIPTION)
+                        .setMediaId(MEDIA_ID)
+                        .setTitle(TITLE)
+                        .setSubtitle(SUBTITLE)
+                        .build();
+        MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_BROWSABLE);
+
+        assertEquals(description.toString(), mediaItem.getDescription().toString());
+        assertEquals(MEDIA_ID, mediaItem.getMediaId());
+        assertEquals(MediaItem.FLAG_BROWSABLE, mediaItem.getFlags());
+        assertTrue(mediaItem.isBrowsable());
+        assertFalse(mediaItem.isPlayable());
+        assertEquals(0, mediaItem.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        mediaItem.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        assertEquals(mediaItem.getFlags(), p.readInt());
+        assertEquals(
+                description.toString(),
+                MediaDescriptionCompat.CREATOR.createFromParcel(p).toString());
+        p.recycle();
+    }
+
+    @Test
+    @SmallTest
+    public void testPlayableMediaItem() {
+        MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
+                .setDescription(DESCRIPTION)
+                .setMediaId(MEDIA_ID)
+                .setTitle(TITLE)
+                .setSubtitle(SUBTITLE)
+                .build();
+        MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_PLAYABLE);
+
+        assertEquals(description.toString(), mediaItem.getDescription().toString());
+        assertEquals(MEDIA_ID, mediaItem.getMediaId());
+        assertEquals(MediaItem.FLAG_PLAYABLE, mediaItem.getFlags());
+        assertFalse(mediaItem.isBrowsable());
+        assertTrue(mediaItem.isPlayable());
+        assertEquals(0, mediaItem.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        mediaItem.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        assertEquals(mediaItem.getFlags(), p.readInt());
+        assertEquals(
+                description.toString(),
+                MediaDescriptionCompat.CREATOR.createFromParcel(p).toString());
+        p.recycle();
+    }
+}
diff --git a/android/support/mediacompat/client/PlaybackStateCompatTest.java b/android/support/mediacompat/client/PlaybackStateCompatTest.java
new file mode 100644
index 0000000..7962731
--- /dev/null
+++ b/android/support/mediacompat/client/PlaybackStateCompatTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Test {@link PlaybackStateCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PlaybackStateCompatTest {
+
+    private static final long TEST_POSITION = 20000L;
+    private static final long TEST_BUFFERED_POSITION = 15000L;
+    private static final long TEST_UPDATE_TIME = 100000L;
+    private static final long TEST_ACTIONS = PlaybackStateCompat.ACTION_PLAY
+            | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_SEEK_TO;
+    private static final long TEST_QUEUE_ITEM_ID = 23L;
+    private static final float TEST_PLAYBACK_SPEED = 3.0f;
+    private static final float TEST_PLAYBACK_SPEED_ON_REWIND = -2.0f;
+    private static final float DELTA = 1e-7f;
+
+    private static final int TEST_ERROR_CODE =
+            PlaybackStateCompat.ERROR_CODE_AUTHENTICATION_EXPIRED;
+    private static final String TEST_ERROR_MSG = "test-error-msg";
+    private static final String TEST_CUSTOM_ACTION = "test-custom-action";
+    private static final String TEST_CUSTOM_ACTION_NAME = "test-custom-action-name";
+    private static final int TEST_ICON_RESOURCE_ID = android.R.drawable.ic_media_next;
+
+    private static final String EXTRAS_KEY = "test-key";
+    private static final String EXTRAS_VALUE = "test-value";
+
+    /**
+     * Test default values of {@link PlaybackStateCompat}.
+     */
+    @Test
+    @SmallTest
+    public void testBuilder() {
+        PlaybackStateCompat state = new PlaybackStateCompat.Builder().build();
+
+        assertEquals(new ArrayList<PlaybackStateCompat.CustomAction>(), state.getCustomActions());
+        assertEquals(0, state.getState());
+        assertEquals(0L, state.getPosition());
+        assertEquals(0L, state.getBufferedPosition());
+        assertEquals(0.0f, state.getPlaybackSpeed(), DELTA);
+        assertEquals(0L, state.getActions());
+        assertEquals(0, state.getErrorCode());
+        assertNull(state.getErrorMessage());
+        assertEquals(0L, state.getLastPositionUpdateTime());
+        assertEquals(MediaSessionCompat.QueueItem.UNKNOWN_ID, state.getActiveQueueItemId());
+        assertNull(state.getExtras());
+    }
+
+    /**
+     * Test following setter methods of {@link PlaybackStateCompat.Builder}:
+     * {@link PlaybackStateCompat.Builder#setState(int, long, float)}
+     * {@link PlaybackStateCompat.Builder#setActions(long)}
+     * {@link PlaybackStateCompat.Builder#setActiveQueueItemId(long)}
+     * {@link PlaybackStateCompat.Builder#setBufferedPosition(long)}
+     * {@link PlaybackStateCompat.Builder#setErrorMessage(CharSequence)}
+     * {@link PlaybackStateCompat.Builder#setExtras(Bundle)}
+     */
+    @Test
+    @SmallTest
+    public void testBuilder_setterMethods() {
+        Bundle extras = new Bundle();
+        extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+        PlaybackStateCompat state = new PlaybackStateCompat.Builder()
+                .setState(PlaybackStateCompat.STATE_PLAYING, TEST_POSITION, TEST_PLAYBACK_SPEED)
+                .setActions(TEST_ACTIONS)
+                .setActiveQueueItemId(TEST_QUEUE_ITEM_ID)
+                .setBufferedPosition(TEST_BUFFERED_POSITION)
+                .setErrorMessage(TEST_ERROR_CODE, TEST_ERROR_MSG)
+                .setExtras(extras)
+                .build();
+        assertEquals(PlaybackStateCompat.STATE_PLAYING, state.getState());
+        assertEquals(TEST_POSITION, state.getPosition());
+        assertEquals(TEST_PLAYBACK_SPEED, state.getPlaybackSpeed(), DELTA);
+        assertEquals(TEST_ACTIONS, state.getActions());
+        assertEquals(TEST_QUEUE_ITEM_ID, state.getActiveQueueItemId());
+        assertEquals(TEST_BUFFERED_POSITION, state.getBufferedPosition());
+        assertEquals(TEST_ERROR_CODE, state.getErrorCode());
+        assertEquals(TEST_ERROR_MSG, state.getErrorMessage().toString());
+        assertNotNull(state.getExtras());
+        assertEquals(EXTRAS_VALUE, state.getExtras().get(EXTRAS_KEY));
+    }
+
+    /**
+     * Test {@link PlaybackStateCompat.Builder#setState(int, long, float, long)}.
+     */
+    @Test
+    @SmallTest
+    public void testBuilder_setStateWithUpdateTime() {
+        PlaybackStateCompat state = new PlaybackStateCompat.Builder()
+                .setState(
+                        PlaybackStateCompat.STATE_REWINDING,
+                        TEST_POSITION,
+                        TEST_PLAYBACK_SPEED_ON_REWIND,
+                        TEST_UPDATE_TIME)
+                .build();
+        assertEquals(PlaybackStateCompat.STATE_REWINDING, state.getState());
+        assertEquals(TEST_POSITION, state.getPosition());
+        assertEquals(TEST_PLAYBACK_SPEED_ON_REWIND, state.getPlaybackSpeed(), DELTA);
+        assertEquals(TEST_UPDATE_TIME, state.getLastPositionUpdateTime());
+    }
+
+    /**
+     * Test {@link PlaybackStateCompat.Builder#addCustomAction(String, String, int)}.
+     */
+    @Test
+    @SmallTest
+    public void testBuilder_addCustomAction() {
+        ArrayList<PlaybackStateCompat.CustomAction> actions = new ArrayList<>();
+        PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
+
+        for (int i = 0; i < 5; i++) {
+            actions.add(new PlaybackStateCompat.CustomAction.Builder(
+                    TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+                    .build());
+            builder.addCustomAction(
+                    TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i);
+        }
+
+        PlaybackStateCompat state = builder.build();
+        assertEquals(actions.size(), state.getCustomActions().size());
+        for (int i = 0; i < actions.size(); i++) {
+            assertCustomActionEquals(actions.get(i), state.getCustomActions().get(i));
+        }
+    }
+
+    /**
+     * Test {@link PlaybackStateCompat.Builder#addCustomAction(PlaybackStateCompat.CustomAction)}.
+     */
+    @Test
+    @SmallTest
+    public void testBuilder_addCustomActionWithCustomActionObject() {
+        Bundle extras = new Bundle();
+        extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+        ArrayList<PlaybackStateCompat.CustomAction> actions = new ArrayList<>();
+        PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
+
+        for (int i = 0; i < 5; i++) {
+            actions.add(new PlaybackStateCompat.CustomAction.Builder(
+                    TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+                    .setExtras(extras)
+                    .build());
+            builder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
+                    TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+                    .setExtras(extras)
+                    .build());
+        }
+
+        PlaybackStateCompat state = builder.build();
+        assertEquals(actions.size(), state.getCustomActions().size());
+        for (int i = 0; i < actions.size(); i++) {
+            assertCustomActionEquals(actions.get(i), state.getCustomActions().get(i));
+        }
+    }
+
+    /**
+     * Test {@link PlaybackStateCompat#writeToParcel(Parcel, int)}.
+     */
+    @Test
+    @SmallTest
+    public void testWriteToParcel() {
+        Bundle extras = new Bundle();
+        extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+        PlaybackStateCompat.Builder builder =
+                new PlaybackStateCompat.Builder()
+                        .setState(PlaybackStateCompat.STATE_CONNECTING, TEST_POSITION,
+                                TEST_PLAYBACK_SPEED, TEST_UPDATE_TIME)
+                        .setActions(TEST_ACTIONS)
+                        .setActiveQueueItemId(TEST_QUEUE_ITEM_ID)
+                        .setBufferedPosition(TEST_BUFFERED_POSITION)
+                        .setErrorMessage(TEST_ERROR_CODE, TEST_ERROR_MSG)
+                        .setExtras(extras);
+
+        for (int i = 0; i < 5; i++) {
+            builder.addCustomAction(
+                    new PlaybackStateCompat.CustomAction.Builder(
+                            TEST_CUSTOM_ACTION + i,
+                            TEST_CUSTOM_ACTION_NAME + i,
+                            TEST_ICON_RESOURCE_ID + i)
+                            .setExtras(extras)
+                            .build());
+        }
+        PlaybackStateCompat state = builder.build();
+
+        Parcel parcel = Parcel.obtain();
+        state.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        PlaybackStateCompat stateOut = PlaybackStateCompat.CREATOR.createFromParcel(parcel);
+        assertEquals(PlaybackStateCompat.STATE_CONNECTING, stateOut.getState());
+        assertEquals(TEST_POSITION, stateOut.getPosition());
+        assertEquals(TEST_PLAYBACK_SPEED, stateOut.getPlaybackSpeed(), DELTA);
+        assertEquals(TEST_UPDATE_TIME, stateOut.getLastPositionUpdateTime());
+        assertEquals(TEST_BUFFERED_POSITION, stateOut.getBufferedPosition());
+        assertEquals(TEST_ACTIONS, stateOut.getActions());
+        assertEquals(TEST_QUEUE_ITEM_ID, stateOut.getActiveQueueItemId());
+        assertEquals(TEST_ERROR_CODE, stateOut.getErrorCode());
+        assertEquals(TEST_ERROR_MSG, stateOut.getErrorMessage());
+        assertNotNull(stateOut.getExtras());
+        assertEquals(EXTRAS_VALUE, stateOut.getExtras().get(EXTRAS_KEY));
+
+        assertEquals(state.getCustomActions().size(), stateOut.getCustomActions().size());
+        for (int i = 0; i < state.getCustomActions().size(); i++) {
+            assertCustomActionEquals(
+                    state.getCustomActions().get(i), stateOut.getCustomActions().get(i));
+        }
+        parcel.recycle();
+    }
+
+    /**
+     * Test {@link PlaybackStateCompat#describeContents()}.
+     */
+    @Test
+    @SmallTest
+    public void testDescribeContents() {
+        assertEquals(0, new PlaybackStateCompat.Builder().build().describeContents());
+    }
+
+    /**
+     * Test {@link PlaybackStateCompat.CustomAction}.
+     */
+    @Test
+    @SmallTest
+    public void testCustomAction() {
+        Bundle extras = new Bundle();
+        extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+        // Test Builder/Getters
+        PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction
+                .Builder(TEST_CUSTOM_ACTION, TEST_CUSTOM_ACTION_NAME, TEST_ICON_RESOURCE_ID)
+                .setExtras(extras)
+                .build();
+        assertEquals(TEST_CUSTOM_ACTION, customAction.getAction());
+        assertEquals(TEST_CUSTOM_ACTION_NAME, customAction.getName().toString());
+        assertEquals(TEST_ICON_RESOURCE_ID, customAction.getIcon());
+        assertEquals(EXTRAS_VALUE, customAction.getExtras().get(EXTRAS_KEY));
+
+        // Test describeContents
+        assertEquals(0, customAction.describeContents());
+
+        // Test writeToParcel
+        Parcel parcel = Parcel.obtain();
+        customAction.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        assertCustomActionEquals(
+                customAction, PlaybackStateCompat.CustomAction.CREATOR.createFromParcel(parcel));
+        parcel.recycle();
+    }
+
+    private void assertCustomActionEquals(PlaybackStateCompat.CustomAction action1,
+            PlaybackStateCompat.CustomAction action2) {
+        assertEquals(action1.getAction(), action2.getAction());
+        assertEquals(action1.getName(), action2.getName());
+        assertEquals(action1.getIcon(), action2.getIcon());
+
+        // To be the same, two extras should be both null or both not null.
+        assertEquals(action1.getExtras() != null, action2.getExtras() != null);
+        if (action1.getExtras() != null) {
+            assertEquals(action1.getExtras().get(EXTRAS_KEY), action2.getExtras().get(EXTRAS_KEY));
+        }
+    }
+}
diff --git a/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java b/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
new file mode 100644
index 0000000..3519f2f
--- /dev/null
+++ b/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
@@ -0,0 +1,1099 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.mediacompat.service;
+
+import static android.support.mediacompat.testlib.MediaControllerConstants.ADD_QUEUE_ITEM;
+import static android.support.mediacompat.testlib.MediaControllerConstants
+        .ADD_QUEUE_ITEM_WITH_INDEX;
+import static android.support.mediacompat.testlib.MediaControllerConstants.ADJUST_VOLUME;
+import static android.support.mediacompat.testlib.MediaControllerConstants.FAST_FORWARD;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PAUSE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY_FROM_MEDIA_ID;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY_FROM_SEARCH;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PLAY_FROM_URI;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE_FROM_MEDIA_ID;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE_FROM_SEARCH;
+import static android.support.mediacompat.testlib.MediaControllerConstants.PREPARE_FROM_URI;
+import static android.support.mediacompat.testlib.MediaControllerConstants.REMOVE_QUEUE_ITEM;
+import static android.support.mediacompat.testlib.MediaControllerConstants.REWIND;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SEEK_TO;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SEND_COMMAND;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SEND_CUSTOM_ACTION;
+import static android.support.mediacompat.testlib.MediaControllerConstants
+        .SEND_CUSTOM_ACTION_PARCELABLE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_CAPTIONING_ENABLED;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_RATING;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_REPEAT_MODE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_SHUFFLE_MODE;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_VOLUME_TO;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SKIP_TO_NEXT;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SKIP_TO_PREVIOUS;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SKIP_TO_QUEUE_ITEM;
+import static android.support.mediacompat.testlib.MediaControllerConstants.STOP;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_COMMAND;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_KEY;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MEDIA_ID_1;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MEDIA_ID_2;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MEDIA_TITLE_1;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_MEDIA_TITLE_2;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_QUEUE_ID_1;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_SESSION_TAG;
+import static android.support.mediacompat.testlib.MediaSessionConstants.TEST_VALUE;
+import static android.support.mediacompat.testlib.VersionConstants.KEY_CLIENT_VERSION;
+import static android.support.mediacompat.testlib.util.IntentUtil.callMediaControllerMethod;
+import static android.support.mediacompat.testlib.util.IntentUtil.callTransportControlsMethod;
+import static android.support.mediacompat.testlib.util.TestUtil.assertBundleEquals;
+import static android.support.test.InstrumentationRegistry.getArguments;
+import static android.support.test.InstrumentationRegistry.getContext;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.InstrumentationRegistry.getTargetContext;
+
+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.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.RatingCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import androidx.media.VolumeProviderCompat;
+
+import org.junit.After;
+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.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test {@link MediaSessionCompat.Callback}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MediaSessionCompatCallbackTest {
+
+    private static final String TAG = "MediaSessionCompatCallbackTest";
+
+    // The maximum time to wait for an operation.
+    private static final long TIME_OUT_MS = 3000L;
+    private static final long WAIT_TIME_FOR_NO_RESPONSE_MS = 300L;
+
+    private static final long TEST_POSITION = 1000000L;
+    private static final float TEST_PLAYBACK_SPEED = 3.0f;
+    private static final float DELTA = 1e-4f;
+    private static final boolean ENABLED = true;
+
+    private final Object mWaitLock = new Object();
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private String mClientVersion;
+    private MediaSessionCompat mSession;
+    private MediaSessionCallback mCallback = new MediaSessionCallback();
+    private AudioManager mAudioManager;
+
+    @Before
+    public void setUp() throws Exception {
+        // The version of the client app is provided through the instrumentation arguments.
+        mClientVersion = getArguments().getString(KEY_CLIENT_VERSION, "");
+        Log.d(TAG, "Client app version: " + mClientVersion);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+                mSession = new MediaSessionCompat(getTargetContext(), TEST_SESSION_TAG);
+                mSession.setCallback(mCallback, mHandler);
+            }
+        });
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mSession.release();
+    }
+
+    /**
+     * Tests that a session can be created and that all the fields are initialized correctly.
+     */
+    @Test
+    @SmallTest
+    public void testCreateSession() throws Exception {
+        assertNotNull(mSession.getSessionToken());
+        assertFalse("New session should not be active", mSession.isActive());
+
+        // Verify by getting the controller and checking all its fields
+        MediaControllerCompat controller = mSession.getController();
+        assertNotNull(controller);
+
+        final String errorMsg = "New session has unexpected configuration.";
+        assertEquals(errorMsg, 0L, controller.getFlags());
+        assertNull(errorMsg, controller.getExtras());
+        assertNull(errorMsg, controller.getMetadata());
+        assertEquals(errorMsg, getContext().getPackageName(), controller.getPackageName());
+        assertNull(errorMsg, controller.getPlaybackState());
+        assertNull(errorMsg, controller.getQueue());
+        assertNull(errorMsg, controller.getQueueTitle());
+        assertEquals(errorMsg, RatingCompat.RATING_NONE, controller.getRatingType());
+        assertNull(errorMsg, controller.getSessionActivity());
+
+        assertNotNull(controller.getSessionToken());
+        assertNotNull(controller.getTransportControls());
+
+        MediaControllerCompat.PlaybackInfo info = controller.getPlaybackInfo();
+        assertNotNull(info);
+        assertEquals(errorMsg, MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
+                info.getPlaybackType());
+        assertEquals(errorMsg, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
+                info.getCurrentVolume());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetSessionToken() throws Exception {
+        assertEquals(mSession.getSessionToken(), mSession.getController().getSessionToken());
+    }
+
+    /**
+     * 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;
+        }
+        mCallback.reset(1);
+        mSession.setCallback(mCallback, new Handler(Looper.getMainLooper()));
+        MediaSessionCompat session = MediaSessionCompat.fromMediaSession(
+                getContext(), mSession.getMediaSession());
+        assertEquals(session.getSessionToken(), mSession.getSessionToken());
+
+        session.getController().getTransportControls().play();
+        mCallback.await(TIME_OUT_MS);
+        assertEquals(1, mCallback.mOnPlayCalledCount);
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat.Token} created in the constructor of MediaSessionCompat.
+     */
+    @Test
+    @SmallTest
+    public void testSessionToken() throws Exception {
+        MediaSessionCompat.Token sessionToken = mSession.getSessionToken();
+
+        assertNotNull(sessionToken);
+        assertEquals(0, sessionToken.describeContents());
+
+        // Test writeToParcel
+        Parcel p = Parcel.obtain();
+        sessionToken.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        MediaSessionCompat.Token token = MediaSessionCompat.Token.CREATOR.createFromParcel(p);
+        assertEquals(token, sessionToken);
+        p.recycle();
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat.QueueItem}.
+     */
+    @Test
+    @SmallTest
+    public void testQueueItem() {
+        MediaSessionCompat.QueueItem item = new MediaSessionCompat.QueueItem(
+                new MediaDescriptionCompat.Builder()
+                        .setMediaId(TEST_MEDIA_ID_1)
+                        .setTitle(TEST_MEDIA_TITLE_1)
+                        .build(),
+                TEST_QUEUE_ID_1);
+        assertEquals(TEST_QUEUE_ID_1, item.getQueueId());
+        assertEquals(TEST_MEDIA_ID_1, item.getDescription().getMediaId());
+        assertEquals(TEST_MEDIA_TITLE_1, item.getDescription().getTitle());
+        assertEquals(0, item.describeContents());
+
+        Parcel p = Parcel.obtain();
+        item.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        MediaSessionCompat.QueueItem other =
+                MediaSessionCompat.QueueItem.CREATOR.createFromParcel(p);
+        assertEquals(item.toString(), other.toString());
+        p.recycle();
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setActive}.
+     */
+    @Test
+    @SmallTest
+    public void testSetActive() throws Exception {
+        mSession.setActive(true);
+        assertTrue(mSession.isActive());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetPlaybackStateWithPositionUpdate() throws InterruptedException {
+        final long stateSetTime = SystemClock.elapsedRealtime();
+        PlaybackStateCompat stateIn = new PlaybackStateCompat.Builder()
+                .setState(PlaybackStateCompat.STATE_PLAYING, TEST_POSITION, TEST_PLAYBACK_SPEED,
+                        stateSetTime)
+                .build();
+        mSession.setPlaybackState(stateIn);
+
+        final long waitDuration = 100L;
+        Thread.sleep(waitDuration);
+
+        final long expectedUpdateTime = waitDuration + stateSetTime;
+        final long expectedPosition = (long) (TEST_PLAYBACK_SPEED * waitDuration) + TEST_POSITION;
+
+        final double updateTimeTolerance = 50L;
+        final double positionTolerance = updateTimeTolerance * TEST_PLAYBACK_SPEED;
+
+        PlaybackStateCompat stateOut = mSession.getController().getPlaybackState();
+        assertEquals(expectedUpdateTime, stateOut.getLastPositionUpdateTime(), updateTimeTolerance);
+        assertEquals(expectedPosition, stateOut.getPosition(), positionTolerance);
+
+        // Compare the result with MediaController.getPlaybackState().
+        if (Build.VERSION.SDK_INT >= 21) {
+            MediaController controller = new MediaController(
+                    getContext(), (MediaSession.Token) mSession.getSessionToken().getToken());
+            PlaybackState state = controller.getPlaybackState();
+            assertEquals(state.getLastPositionUpdateTime(), stateOut.getLastPositionUpdateTime(),
+                    updateTimeTolerance);
+            assertEquals(state.getPosition(), stateOut.getPosition(), positionTolerance);
+        }
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat#setCallback} with {@code null}.
+     * No callback should be called once {@code setCallback(null)} is done.
+     */
+    @Test
+    @SmallTest
+    public void testSetCallbackWithNull() throws Exception {
+        mSession.setActive(true);
+        mCallback.reset(1);
+        callTransportControlsMethod(PLAY, null, getContext(), mSession.getSessionToken());
+        mSession.setCallback(null, mHandler);
+        mCallback.await(WAIT_TIME_FOR_NO_RESPONSE_MS);
+        assertEquals("Callback shouldn't be called.", 0, mCallback.mOnPlayCalledCount);
+    }
+
+    @Test
+    @SmallTest
+    public void testSendCommand() throws Exception {
+        mCallback.reset(1);
+
+        Bundle arguments = new Bundle();
+        arguments.putString("command", TEST_COMMAND);
+        Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+        arguments.putBundle("extras", extras);
+        callMediaControllerMethod(
+                SEND_COMMAND, arguments, getContext(), mSession.getSessionToken());
+
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnCommandCalled);
+        assertNotNull(mCallback.mCommandCallback);
+        assertEquals(TEST_COMMAND, mCallback.mCommand);
+        assertBundleEquals(extras, mCallback.mExtras);
+    }
+
+    @Test
+    @SmallTest
+    public void testAddRemoveQueueItems() throws Exception {
+        mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS);
+
+        MediaDescriptionCompat itemDescription1 = new MediaDescriptionCompat.Builder()
+                .setMediaId(TEST_MEDIA_ID_1).setTitle(TEST_MEDIA_TITLE_1).build();
+
+        MediaDescriptionCompat itemDescription2 = new MediaDescriptionCompat.Builder()
+                .setMediaId(TEST_MEDIA_ID_2).setTitle(TEST_MEDIA_TITLE_2).build();
+
+        mCallback.reset(1);
+        callMediaControllerMethod(
+                ADD_QUEUE_ITEM, itemDescription1, getContext(), mSession.getSessionToken());
+
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnAddQueueItemCalled);
+        assertEquals(-1, mCallback.mQueueIndex);
+        assertEquals(TEST_MEDIA_ID_1, mCallback.mQueueDescription.getMediaId());
+        assertEquals(TEST_MEDIA_TITLE_1, mCallback.mQueueDescription.getTitle());
+
+        mCallback.reset(1);
+        Bundle arguments = new Bundle();
+        arguments.putParcelable("description", itemDescription2);
+        arguments.putInt("index", 0);
+        callMediaControllerMethod(
+                ADD_QUEUE_ITEM_WITH_INDEX, arguments, getContext(), mSession.getSessionToken());
+
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnAddQueueItemAtCalled);
+        assertEquals(0, mCallback.mQueueIndex);
+        assertEquals(TEST_MEDIA_ID_2, mCallback.mQueueDescription.getMediaId());
+        assertEquals(TEST_MEDIA_TITLE_2, mCallback.mQueueDescription.getTitle());
+
+        mCallback.reset(1);
+        callMediaControllerMethod(
+                REMOVE_QUEUE_ITEM, itemDescription1, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnRemoveQueueItemCalled);
+        assertEquals(TEST_MEDIA_ID_1, mCallback.mQueueDescription.getMediaId());
+        assertEquals(TEST_MEDIA_TITLE_1, mCallback.mQueueDescription.getTitle());
+    }
+
+    @Test
+    @SmallTest
+    public void testTransportControlsAndMediaSessionCallback() throws Exception {
+        mCallback.reset(1);
+        callTransportControlsMethod(PLAY, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertEquals(1, mCallback.mOnPlayCalledCount);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(PAUSE, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPauseCalled);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(STOP, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnStopCalled);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(
+                FAST_FORWARD, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnFastForwardCalled);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(REWIND, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnRewindCalled);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(
+                SKIP_TO_PREVIOUS, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSkipToPreviousCalled);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(
+                SKIP_TO_NEXT, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSkipToNextCalled);
+
+        mCallback.reset(1);
+        final long seekPosition = 1000;
+        callTransportControlsMethod(
+                SEEK_TO, seekPosition, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSeekToCalled);
+        assertEquals(seekPosition, mCallback.mSeekPosition);
+
+        mCallback.reset(1);
+        final RatingCompat rating =
+                RatingCompat.newStarRating(RatingCompat.RATING_5_STARS, 3f);
+        callTransportControlsMethod(
+                SET_RATING, rating, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSetRatingCalled);
+        assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+        assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating(), DELTA);
+
+        mCallback.reset(1);
+        final Bundle extras = new Bundle();
+        extras.putString(TEST_KEY, TEST_VALUE);
+        Bundle arguments = new Bundle();
+        arguments.putString("mediaId", TEST_MEDIA_ID_1);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                PLAY_FROM_MEDIA_ID, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPlayFromMediaIdCalled);
+        assertEquals(TEST_MEDIA_ID_1, mCallback.mMediaId);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        final String query = "test-query";
+        arguments = new Bundle();
+        arguments.putString("query", query);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                PLAY_FROM_SEARCH, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPlayFromSearchCalled);
+        assertEquals(query, mCallback.mQuery);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        final Uri uri = Uri.parse("content://test/popcorn.mod");
+        arguments = new Bundle();
+        arguments.putParcelable("uri", uri);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                PLAY_FROM_URI, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPlayFromUriCalled);
+        assertEquals(uri, mCallback.mUri);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        final String action = "test-action";
+        arguments = new Bundle();
+        arguments.putString("action", action);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                SEND_CUSTOM_ACTION, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnCustomActionCalled);
+        assertEquals(action, mCallback.mAction);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        mCallback.mOnCustomActionCalled = false;
+        final PlaybackStateCompat.CustomAction customAction =
+                new PlaybackStateCompat.CustomAction.Builder(action, action, -1)
+                        .setExtras(extras)
+                        .build();
+        arguments = new Bundle();
+        arguments.putParcelable("action", customAction);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                SEND_CUSTOM_ACTION_PARCELABLE,
+                arguments,
+                getContext(),
+                mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnCustomActionCalled);
+        assertEquals(action, mCallback.mAction);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        final long queueItemId = 1000;
+        callTransportControlsMethod(
+                SKIP_TO_QUEUE_ITEM, queueItemId, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSkipToQueueItemCalled);
+        assertEquals(queueItemId, mCallback.mQueueItemId);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(
+                PREPARE, null, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPrepareCalled);
+
+        mCallback.reset(1);
+        arguments = new Bundle();
+        arguments.putString("mediaId", TEST_MEDIA_ID_2);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                PREPARE_FROM_MEDIA_ID, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPrepareFromMediaIdCalled);
+        assertEquals(TEST_MEDIA_ID_2, mCallback.mMediaId);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        arguments = new Bundle();
+        arguments.putString("query", query);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                PREPARE_FROM_SEARCH, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPrepareFromSearchCalled);
+        assertEquals(query, mCallback.mQuery);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        arguments = new Bundle();
+        arguments.putParcelable("uri", uri);
+        arguments.putBundle("extras", extras);
+        callTransportControlsMethod(
+                PREPARE_FROM_URI, arguments, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnPrepareFromUriCalled);
+        assertEquals(uri, mCallback.mUri);
+        assertBundleEquals(extras, mCallback.mExtras);
+
+        mCallback.reset(1);
+        callTransportControlsMethod(
+                SET_CAPTIONING_ENABLED, ENABLED, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSetCaptioningEnabledCalled);
+        assertEquals(ENABLED, mCallback.mCaptioningEnabled);
+
+        mCallback.reset(1);
+        final int repeatMode = PlaybackStateCompat.REPEAT_MODE_ALL;
+        callTransportControlsMethod(
+                SET_REPEAT_MODE, repeatMode, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSetRepeatModeCalled);
+        assertEquals(repeatMode, mCallback.mRepeatMode);
+
+        mCallback.reset(1);
+        final int shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_ALL;
+        callTransportControlsMethod(
+                SET_SHUFFLE_MODE, shuffleMode, getContext(), mSession.getSessionToken());
+        mCallback.await(TIME_OUT_MS);
+        assertTrue(mCallback.mOnSetShuffleModeCalled);
+        assertEquals(shuffleMode, mCallback.mShuffleMode);
+    }
+
+    /**
+     * Tests {@link MediaSessionCompat.Callback#onMediaButtonEvent}.
+     */
+    @Test
+    @MediumTest
+    public void testCallbackOnMediaButtonEvent() throws Exception {
+        mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
+        mSession.setActive(true);
+
+        final long waitTimeForNoResponse = 30L;
+
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON)
+                .setComponent(new ComponentName(getContext(), getContext().getClass()));
+        PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0);
+        mSession.setMediaButtonReceiver(pi);
+
+        // Set state to STATE_PLAYING to get higher priority.
+        setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertEquals(1, mCallback.mOnPlayCalledCount);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PAUSE);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnPauseCalled);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_NEXT);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnSkipToNextCalled);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnSkipToPreviousCalled);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnStopCalled);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnFastForwardCalled);
+
+        mCallback.reset(1);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_REWIND);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnRewindCalled);
+
+        // Test PLAY_PAUSE button twice.
+        // First, send PLAY_PAUSE button event while in STATE_PAUSED.
+        mCallback.reset(1);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertEquals(1, mCallback.mOnPlayCalledCount);
+
+        // Next, send PLAY_PAUSE button event while in STATE_PLAYING.
+        mCallback.reset(1);
+        setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnPauseCalled);
+
+        // Double tap of PLAY_PAUSE is the next track.
+        mCallback.reset(2);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertFalse(mCallback.await(waitTimeForNoResponse));
+        assertTrue(mCallback.mOnSkipToNextCalled);
+        assertEquals(0, mCallback.mOnPlayCalledCount);
+        assertFalse(mCallback.mOnPauseCalled);
+
+        // Test PLAY_PAUSE button long-press.
+        // It should be the same as the single short-press.
+        mCallback.reset(1);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertEquals(1, mCallback.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.
+        mCallback.reset(2);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
+        assertFalse(mCallback.await(waitTimeForNoResponse));
+        assertTrue(mCallback.mOnSkipToNextCalled);
+        assertEquals(0, mCallback.mOnPlayCalledCount);
+        assertFalse(mCallback.mOnPauseCalled);
+
+        // Test PLAY_PAUSE button long-press followed by the short-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.
+        mCallback.reset(2);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, true);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        // onMediaButtonEvent() calls either onPlay() or onPause() depending on the playback state,
+        // so onPlay() should be called once and onPause() also should be called once.
+        assertEquals(1, mCallback.mOnPlayCalledCount);
+        assertTrue(mCallback.mOnPauseCalled);
+        assertFalse(mCallback.mOnSkipToNextCalled);
+
+        // If another media key is pressed while the double tap of PLAY_PAUSE,
+        // PLAY_PAUSE should be handled as normal.
+        mCallback.reset(3);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertFalse(mCallback.mOnSkipToNextCalled);
+        assertTrue(mCallback.mOnStopCalled);
+        assertEquals(2, mCallback.mOnPlayCalledCount);
+
+        // Test if media keys are handled in order.
+        mCallback.reset(2);
+        setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertEquals(1, mCallback.mOnPlayCalledCount);
+        assertTrue(mCallback.mOnStopCalled);
+        synchronized (mWaitLock) {
+            assertEquals(PlaybackStateCompat.STATE_STOPPED,
+                    mSession.getController().getPlaybackState().getState());
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testVolumeControl() throws Exception {
+        if (android.os.Build.VERSION.SDK_INT < 27) {
+            // This test causes an Exception on System UI in API < 27.
+            return;
+        }
+        VolumeProviderCompat vp =
+                new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 11, 5) {
+                    @Override
+                    public void onSetVolumeTo(int volume) {
+                        synchronized (mWaitLock) {
+                            setCurrentVolume(volume);
+                            mWaitLock.notify();
+                        }
+                    }
+
+                    @Override
+                    public void onAdjustVolume(int direction) {
+                        synchronized (mWaitLock) {
+                            switch (direction) {
+                                case AudioManager.ADJUST_LOWER:
+                                    setCurrentVolume(getCurrentVolume() - 1);
+                                    break;
+                                case AudioManager.ADJUST_RAISE:
+                                    setCurrentVolume(getCurrentVolume() + 1);
+                                    break;
+                            }
+                            mWaitLock.notify();
+                        }
+                    }
+                };
+        mSession.setPlaybackToRemote(vp);
+
+        synchronized (mWaitLock) {
+            // test setVolumeTo
+            callMediaControllerMethod(SET_VOLUME_TO,
+                    7 /* Target volume */, getContext(), mSession.getSessionToken());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(7, vp.getCurrentVolume());
+
+            // test adjustVolume
+            callMediaControllerMethod(ADJUST_VOLUME,
+                    AudioManager.ADJUST_LOWER, getContext(), mSession.getSessionToken());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(6, vp.getCurrentVolume());
+
+            callMediaControllerMethod(ADJUST_VOLUME,
+                    AudioManager.ADJUST_RAISE, getContext(), mSession.getSessionToken());
+            mWaitLock.wait(TIME_OUT_MS);
+            assertEquals(7, vp.getCurrentVolume());
+        }
+    }
+
+    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;
+        PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder().setActions(allActions)
+                .setState(state, 0L, 0.0f).build();
+        synchronized (mWaitLock) {
+            mSession.setPlaybackState(playbackState);
+        }
+    }
+
+    private void sendMediaKeyInputToController(int keyCode) {
+        sendMediaKeyInputToController(keyCode, false);
+    }
+
+    private void sendMediaKeyInputToController(int keyCode, boolean isLongPress) {
+        MediaControllerCompat controller = mSession.getController();
+        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 MediaSessionCallback extends MediaSessionCompat.Callback {
+        private CountDownLatch mLatch;
+        private long mSeekPosition;
+        private long mQueueItemId;
+        private RatingCompat mRating;
+        private String mMediaId;
+        private String mQuery;
+        private Uri mUri;
+        private String mAction;
+        private String mCommand;
+        private Bundle mExtras;
+        private ResultReceiver mCommandCallback;
+        private boolean mCaptioningEnabled;
+        private int mRepeatMode;
+        private int mShuffleMode;
+        private int mQueueIndex;
+        private MediaDescriptionCompat mQueueDescription;
+        private List<MediaSessionCompat.QueueItem> mQueue = new ArrayList<>();
+
+        private int mOnPlayCalledCount;
+        private boolean mOnPauseCalled;
+        private boolean mOnStopCalled;
+        private boolean mOnFastForwardCalled;
+        private boolean mOnRewindCalled;
+        private boolean mOnSkipToPreviousCalled;
+        private boolean mOnSkipToNextCalled;
+        private boolean mOnSeekToCalled;
+        private boolean mOnSkipToQueueItemCalled;
+        private boolean mOnSetRatingCalled;
+        private boolean mOnPlayFromMediaIdCalled;
+        private boolean mOnPlayFromSearchCalled;
+        private boolean mOnPlayFromUriCalled;
+        private boolean mOnCustomActionCalled;
+        private boolean mOnCommandCalled;
+        private boolean mOnPrepareCalled;
+        private boolean mOnPrepareFromMediaIdCalled;
+        private boolean mOnPrepareFromSearchCalled;
+        private boolean mOnPrepareFromUriCalled;
+        private boolean mOnSetCaptioningEnabledCalled;
+        private boolean mOnSetRepeatModeCalled;
+        private boolean mOnSetShuffleModeCalled;
+        private boolean mOnAddQueueItemCalled;
+        private boolean mOnAddQueueItemAtCalled;
+        private boolean mOnRemoveQueueItemCalled;
+
+        public void reset(int count) {
+            mLatch = new CountDownLatch(count);
+            mSeekPosition = -1;
+            mQueueItemId = -1;
+            mRating = null;
+            mMediaId = null;
+            mQuery = null;
+            mUri = null;
+            mAction = null;
+            mExtras = null;
+            mCommand = null;
+            mCommandCallback = null;
+            mCaptioningEnabled = false;
+            mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
+            mShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
+            mQueueIndex = -1;
+            mQueueDescription = null;
+
+            mOnPlayCalledCount = 0;
+            mOnPauseCalled = false;
+            mOnStopCalled = false;
+            mOnFastForwardCalled = false;
+            mOnRewindCalled = false;
+            mOnSkipToPreviousCalled = false;
+            mOnSkipToNextCalled = false;
+            mOnSkipToQueueItemCalled = false;
+            mOnSeekToCalled = false;
+            mOnSetRatingCalled = false;
+            mOnPlayFromMediaIdCalled = false;
+            mOnPlayFromSearchCalled = false;
+            mOnPlayFromUriCalled = false;
+            mOnCustomActionCalled = false;
+            mOnCommandCalled = false;
+            mOnPrepareCalled = false;
+            mOnPrepareFromMediaIdCalled = false;
+            mOnPrepareFromSearchCalled = false;
+            mOnPrepareFromUriCalled = false;
+            mOnSetCaptioningEnabledCalled = false;
+            mOnSetRepeatModeCalled = false;
+            mOnSetShuffleModeCalled = false;
+            mOnAddQueueItemCalled = false;
+            mOnAddQueueItemAtCalled = false;
+            mOnRemoveQueueItemCalled = false;
+        }
+
+        public boolean await(long timeoutMs) {
+            try {
+                return mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+
+        @Override
+        public void onPlay() {
+            mOnPlayCalledCount++;
+            setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPause() {
+            mOnPauseCalled = true;
+            setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onStop() {
+            mOnStopCalled = true;
+            setPlaybackState(PlaybackStateCompat.STATE_STOPPED);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onFastForward() {
+            mOnFastForwardCalled = true;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onRewind() {
+            mOnRewindCalled = true;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSkipToPrevious() {
+            mOnSkipToPreviousCalled = true;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSkipToNext() {
+            mOnSkipToNextCalled = true;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSeekTo(long pos) {
+            mOnSeekToCalled = true;
+            mSeekPosition = pos;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSetRating(RatingCompat rating) {
+            mOnSetRatingCalled = true;
+            mRating = rating;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+            mOnPlayFromMediaIdCalled = true;
+            mMediaId = mediaId;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPlayFromSearch(String query, Bundle extras) {
+            mOnPlayFromSearchCalled = true;
+            mQuery = query;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPlayFromUri(Uri uri, Bundle extras) {
+            mOnPlayFromUriCalled = true;
+            mUri = uri;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onCustomAction(String action, Bundle extras) {
+            mOnCustomActionCalled = true;
+            mAction = action;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSkipToQueueItem(long id) {
+            mOnSkipToQueueItemCalled = true;
+            mQueueItemId = id;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+            mOnCommandCalled = true;
+            mCommand = command;
+            mExtras = extras;
+            mCommandCallback = cb;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPrepare() {
+            mOnPrepareCalled = true;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+            mOnPrepareFromMediaIdCalled = true;
+            mMediaId = mediaId;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPrepareFromSearch(String query, Bundle extras) {
+            mOnPrepareFromSearchCalled = true;
+            mQuery = query;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onPrepareFromUri(Uri uri, Bundle extras) {
+            mOnPrepareFromUriCalled = true;
+            mUri = uri;
+            mExtras = extras;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSetRepeatMode(int repeatMode) {
+            mOnSetRepeatModeCalled = true;
+            mRepeatMode = repeatMode;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onAddQueueItem(MediaDescriptionCompat description) {
+            mOnAddQueueItemCalled = true;
+            mQueueDescription = description;
+            mQueue.add(new MediaSessionCompat.QueueItem(description, mQueue.size()));
+            mSession.setQueue(mQueue);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onAddQueueItem(MediaDescriptionCompat description, int index) {
+            mOnAddQueueItemAtCalled = true;
+            mQueueIndex = index;
+            mQueueDescription = description;
+            mQueue.add(index, new MediaSessionCompat.QueueItem(description, mQueue.size()));
+            mSession.setQueue(mQueue);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onRemoveQueueItem(MediaDescriptionCompat description) {
+            mOnRemoveQueueItemCalled = true;
+            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;
+                }
+            }
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSetCaptioningEnabled(boolean enabled) {
+            mOnSetCaptioningEnabledCalled = true;
+            mCaptioningEnabled = enabled;
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onSetShuffleMode(int shuffleMode) {
+            mOnSetShuffleModeCalled = true;
+            mShuffleMode = shuffleMode;
+            mLatch.countDown();
+        }
+    }
+}
diff --git a/android/support/mediacompat/service/ServiceBroadcastReceiver.java b/android/support/mediacompat/service/ServiceBroadcastReceiver.java
new file mode 100644
index 0000000..b6c7951
--- /dev/null
+++ b/android/support/mediacompat/service/ServiceBroadcastReceiver.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.service;
+
+
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION_SEND_ERROR;
+import static android.support.mediacompat.testlib.MediaBrowserConstants
+        .CUSTOM_ACTION_SEND_PROGRESS_UPDATE;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION_SEND_RESULT;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.NOTIFY_CHILDREN_CHANGED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEND_DELAYED_ITEM_LOADED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants
+        .SEND_DELAYED_NOTIFY_CHILDREN_CHANGED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SET_SESSION_TOKEN;
+import static android.support.mediacompat.testlib.MediaSessionConstants.RELEASE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SEND_SESSION_EVENT;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_ACTIVE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_CAPTIONING_ENABLED;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_EXTRAS;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_FLAGS;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_METADATA;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_PLAYBACK_STATE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_PLAYBACK_TO_LOCAL;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_PLAYBACK_TO_REMOTE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_QUEUE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_QUEUE_TITLE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_RATING_TYPE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_REPEAT_MODE;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_SESSION_ACTIVITY;
+import static android.support.mediacompat.testlib.MediaSessionConstants.SET_SHUFFLE_MODE;
+import static android.support.mediacompat.testlib.util.IntentUtil
+        .ACTION_CALL_MEDIA_BROWSER_SERVICE_METHOD;
+import static android.support.mediacompat.testlib.util.IntentUtil.ACTION_CALL_MEDIA_SESSION_METHOD;
+import static android.support.mediacompat.testlib.util.IntentUtil.KEY_ARGUMENT;
+import static android.support.mediacompat.testlib.util.IntentUtil.KEY_METHOD_ID;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.RatingCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.MediaSessionCompat.QueueItem;
+import android.support.v4.media.session.ParcelableVolumeInfo;
+import android.support.v4.media.session.PlaybackStateCompat;
+
+import androidx.media.VolumeProviderCompat;
+
+import java.util.List;
+
+public class ServiceBroadcastReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Bundle extras = intent.getExtras();
+        if (ACTION_CALL_MEDIA_BROWSER_SERVICE_METHOD.equals(intent.getAction()) && extras != null) {
+            StubMediaBrowserServiceCompat service = StubMediaBrowserServiceCompat.sInstance;
+            int method = extras.getInt(KEY_METHOD_ID, 0);
+
+            switch (method) {
+                case NOTIFY_CHILDREN_CHANGED:
+                    service.notifyChildrenChanged(extras.getString(KEY_ARGUMENT));
+                    break;
+                case SEND_DELAYED_NOTIFY_CHILDREN_CHANGED:
+                    service.sendDelayedNotifyChildrenChanged();
+                    break;
+                case SEND_DELAYED_ITEM_LOADED:
+                    service.sendDelayedItemLoaded();
+                    break;
+                case CUSTOM_ACTION_SEND_PROGRESS_UPDATE:
+                    service.mCustomActionResult.sendProgressUpdate(extras.getBundle(KEY_ARGUMENT));
+                    break;
+                case CUSTOM_ACTION_SEND_ERROR:
+                    service.mCustomActionResult.sendError(extras.getBundle(KEY_ARGUMENT));
+                    break;
+                case CUSTOM_ACTION_SEND_RESULT:
+                    service.mCustomActionResult.sendResult(extras.getBundle(KEY_ARGUMENT));
+                    break;
+                case SET_SESSION_TOKEN:
+                    StubMediaBrowserServiceCompatWithDelayedMediaSession.sInstance
+                            .callSetSessionToken();
+                    break;
+            }
+        } else if (ACTION_CALL_MEDIA_SESSION_METHOD.equals(intent.getAction()) && extras != null) {
+            MediaSessionCompat session = StubMediaBrowserServiceCompat.sSession;
+            int method = extras.getInt(KEY_METHOD_ID, 0);
+
+            switch (method) {
+                case SET_EXTRAS:
+                    session.setExtras(extras.getBundle(KEY_ARGUMENT));
+                    break;
+                case SET_FLAGS:
+                    session.setFlags(extras.getInt(KEY_ARGUMENT));
+                    break;
+                case SET_METADATA:
+                    session.setMetadata((MediaMetadataCompat) extras.getParcelable(KEY_ARGUMENT));
+                    break;
+                case SET_PLAYBACK_STATE:
+                    session.setPlaybackState(
+                            (PlaybackStateCompat) extras.getParcelable(KEY_ARGUMENT));
+                    break;
+                case SET_QUEUE:
+                    List<QueueItem> items = extras.getParcelableArrayList(KEY_ARGUMENT);
+                    session.setQueue(items);
+                    break;
+                case SET_QUEUE_TITLE:
+                    session.setQueueTitle(extras.getCharSequence(KEY_ARGUMENT));
+                    break;
+                case SET_SESSION_ACTIVITY:
+                    session.setSessionActivity((PendingIntent) extras.getParcelable(KEY_ARGUMENT));
+                    break;
+                case SET_CAPTIONING_ENABLED:
+                    session.setCaptioningEnabled(extras.getBoolean(KEY_ARGUMENT));
+                    break;
+                case SET_REPEAT_MODE:
+                    session.setRepeatMode(extras.getInt(KEY_ARGUMENT));
+                    break;
+                case SET_SHUFFLE_MODE:
+                    session.setShuffleMode(extras.getInt(KEY_ARGUMENT));
+                    break;
+                case SEND_SESSION_EVENT:
+                    Bundle arguments = extras.getBundle(KEY_ARGUMENT);
+                    session.sendSessionEvent(
+                            arguments.getString("event"), arguments.getBundle("extras"));
+                    break;
+                case SET_ACTIVE:
+                    session.setActive(extras.getBoolean(KEY_ARGUMENT));
+                    break;
+                case RELEASE:
+                    session.release();
+                    break;
+                case SET_PLAYBACK_TO_LOCAL:
+                    session.setPlaybackToLocal(extras.getInt(KEY_ARGUMENT));
+                    break;
+                case SET_PLAYBACK_TO_REMOTE:
+                    ParcelableVolumeInfo volumeInfo = extras.getParcelable(KEY_ARGUMENT);
+                    session.setPlaybackToRemote(new VolumeProviderCompat(
+                            volumeInfo.controlType,
+                            volumeInfo.maxVolume,
+                            volumeInfo.currentVolume) {});
+                    break;
+                case SET_RATING_TYPE:
+                    session.setRatingType(RatingCompat.RATING_5_STARS);
+                    break;
+            }
+        }
+    }
+}
diff --git a/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java b/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java
new file mode 100644
index 0000000..d70ce8b
--- /dev/null
+++ b/android/support/mediacompat/service/StubMediaBrowserServiceCompat.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.service;
+
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.CUSTOM_ACTION_FOR_ERROR;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.EXTRAS_KEY;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.EXTRAS_VALUE;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_CHILDREN;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_CHILDREN_DELAYED;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_INCLUDE_METADATA;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_INVALID;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_ID_ROOT;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.MEDIA_METADATA;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEARCH_QUERY;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEARCH_QUERY_FOR_ERROR;
+import static android.support.mediacompat.testlib.MediaBrowserConstants.SEARCH_QUERY_FOR_NO_RESULT;
+
+import static org.junit.Assert.assertNull;
+
+import android.os.Bundle;
+import android.support.v4.media.MediaBrowserCompat.MediaItem;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+
+import androidx.annotation.NonNull;
+import androidx.media.MediaBrowserServiceCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Stub implementation of {@link MediaBrowserServiceCompat}.
+ */
+public class StubMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
+
+    public static StubMediaBrowserServiceCompat sInstance;
+
+    public static MediaSessionCompat sSession;
+    private Bundle mExtras;
+    private Result<List<MediaItem>> mPendingLoadChildrenResult;
+    private Result<MediaItem> mPendingLoadItemResult;
+    private Bundle mPendingRootHints;
+
+    public Bundle mCustomActionExtras;
+    public Result<Bundle> mCustomActionResult;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sInstance = this;
+        sSession = new MediaSessionCompat(this, "StubMediaBrowserServiceCompat");
+        setSessionToken(sSession.getSessionToken());
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        sSession.release();
+        sSession = null;
+    }
+
+    @Override
+    public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+        mExtras = new Bundle();
+        mExtras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+        return new BrowserRoot(MEDIA_ID_ROOT, mExtras);
+    }
+
+    @Override
+    public void onLoadChildren(final String parentId, final Result<List<MediaItem>> result) {
+        List<MediaItem> mediaItems = new ArrayList<>();
+        if (MEDIA_ID_ROOT.equals(parentId)) {
+            Bundle rootHints = getBrowserRootHints();
+            for (String id : MEDIA_ID_CHILDREN) {
+                mediaItems.add(createMediaItem(id));
+            }
+            result.sendResult(mediaItems);
+        } else if (MEDIA_ID_CHILDREN_DELAYED.equals(parentId)) {
+            assertNull(mPendingLoadChildrenResult);
+            mPendingLoadChildrenResult = result;
+            mPendingRootHints = getBrowserRootHints();
+            result.detach();
+        } else if (MEDIA_ID_INVALID.equals(parentId)) {
+            result.sendResult(null);
+        }
+    }
+
+    @Override
+    public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaItem>> result,
+            @NonNull Bundle options) {
+        if (MEDIA_ID_INCLUDE_METADATA.equals(parentId)) {
+            // Test unparcelling the Bundle.
+            MediaMetadataCompat metadata = options.getParcelable(MEDIA_METADATA);
+            if (metadata == null) {
+                super.onLoadChildren(parentId, result, options);
+            } else {
+                List<MediaItem> mediaItems = new ArrayList<>();
+                mediaItems.add(new MediaItem(metadata.getDescription(), MediaItem.FLAG_PLAYABLE));
+                result.sendResult(mediaItems);
+            }
+        } else {
+            super.onLoadChildren(parentId, result, options);
+        }
+    }
+
+    @Override
+    public void onLoadItem(String itemId, Result<MediaItem> result) {
+        if (MEDIA_ID_CHILDREN_DELAYED.equals(itemId)) {
+            mPendingLoadItemResult = result;
+            mPendingRootHints = getBrowserRootHints();
+            result.detach();
+            return;
+        }
+
+        if (MEDIA_ID_INVALID.equals(itemId)) {
+            result.sendResult(null);
+            return;
+        }
+
+        for (String id : MEDIA_ID_CHILDREN) {
+            if (id.equals(itemId)) {
+                result.sendResult(createMediaItem(id));
+                return;
+            }
+        }
+
+        // Test the case where onLoadItem is not implemented.
+        super.onLoadItem(itemId, result);
+    }
+
+    @Override
+    public void onSearch(String query, Bundle extras, Result<List<MediaItem>> result) {
+        if (SEARCH_QUERY_FOR_NO_RESULT.equals(query)) {
+            result.sendResult(Collections.<MediaItem>emptyList());
+        } else if (SEARCH_QUERY_FOR_ERROR.equals(query)) {
+            result.sendResult(null);
+        } else if (SEARCH_QUERY.equals(query)) {
+            List<MediaItem> items = new ArrayList<>();
+            for (String id : MEDIA_ID_CHILDREN) {
+                if (id.contains(query)) {
+                    items.add(createMediaItem(id));
+                }
+            }
+            result.sendResult(items);
+        }
+    }
+
+    @Override
+    public void onCustomAction(String action, Bundle extras, Result<Bundle> result) {
+        mCustomActionResult = result;
+        mCustomActionExtras = extras;
+        if (CUSTOM_ACTION_FOR_ERROR.equals(action)) {
+            result.sendError(null);
+        } else if (CUSTOM_ACTION.equals(action)) {
+            result.detach();
+        }
+    }
+
+    public void sendDelayedNotifyChildrenChanged() {
+        if (mPendingLoadChildrenResult != null) {
+            mPendingLoadChildrenResult.sendResult(Collections.<MediaItem>emptyList());
+            mPendingRootHints = null;
+            mPendingLoadChildrenResult = null;
+        }
+    }
+
+    public void sendDelayedItemLoaded() {
+        if (mPendingLoadItemResult != null) {
+            mPendingLoadItemResult.sendResult(new MediaItem(new MediaDescriptionCompat.Builder()
+                    .setMediaId(MEDIA_ID_CHILDREN_DELAYED).setExtras(mPendingRootHints).build(),
+                    MediaItem.FLAG_BROWSABLE));
+            mPendingRootHints = null;
+            mPendingLoadItemResult = null;
+        }
+    }
+
+    private MediaItem createMediaItem(String id) {
+        return new MediaItem(new MediaDescriptionCompat.Builder().setMediaId(id).build(),
+                MediaItem.FLAG_BROWSABLE);
+    }
+}
diff --git a/android/support/mediacompat/service/StubMediaBrowserServiceCompatWithDelayedMediaSession.java b/android/support/mediacompat/service/StubMediaBrowserServiceCompatWithDelayedMediaSession.java
new file mode 100644
index 0000000..7b31196
--- /dev/null
+++ b/android/support/mediacompat/service/StubMediaBrowserServiceCompatWithDelayedMediaSession.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.mediacompat.service;
+
+import android.os.Bundle;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.media.MediaBrowserServiceCompat;
+
+import java.util.List;
+
+/**
+ * Stub implementation of {@link MediaBrowserServiceCompat}.
+ * This implementation does not call
+ * {@link MediaBrowserServiceCompat#setSessionToken(MediaSessionCompat.Token)} in its
+ * {@link android.app.Service#onCreate}.
+ */
+public class StubMediaBrowserServiceCompatWithDelayedMediaSession extends
+        MediaBrowserServiceCompat {
+
+    static StubMediaBrowserServiceCompatWithDelayedMediaSession sInstance;
+    private MediaSessionCompat mSession;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sInstance = this;
+        mSession = new MediaSessionCompat(
+                this, "StubMediaBrowserServiceCompatWithDelayedMediaSession");
+    }
+
+    @Nullable
+    @Override
+    public BrowserRoot onGetRoot(@NonNull String clientPackageName,
+            int clientUid, @Nullable Bundle rootHints) {
+        return new BrowserRoot("StubRootId", null);
+    }
+
+    @Override
+    public void onLoadChildren(@NonNull String parentId,
+            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
+        result.detach();
+    }
+
+    public void callSetSessionToken() {
+        setSessionToken(mSession.getSessionToken());
+    }
+}
diff --git a/android/support/mediacompat/testlib/MediaBrowserConstants.java b/android/support/mediacompat/testlib/MediaBrowserConstants.java
index f961308..b4bea4e 100644
--- a/android/support/mediacompat/testlib/MediaBrowserConstants.java
+++ b/android/support/mediacompat/testlib/MediaBrowserConstants.java
@@ -63,4 +63,7 @@
             "test_media_id_children_2", "test_media_id_children_3",
             MEDIA_ID_CHILDREN_DELAYED
     };
+
+    private MediaBrowserConstants() {
+    }
 }
diff --git a/android/support/mediacompat/testlib/MediaControllerConstants.java b/android/support/mediacompat/testlib/MediaControllerConstants.java
index 4978888..e81f7d9 100644
--- a/android/support/mediacompat/testlib/MediaControllerConstants.java
+++ b/android/support/mediacompat/testlib/MediaControllerConstants.java
@@ -52,4 +52,7 @@
     public static final int SET_CAPTIONING_ENABLED = 320;
     public static final int SET_REPEAT_MODE = 321;
     public static final int SET_SHUFFLE_MODE = 322;
+
+    private MediaControllerConstants() {
+    }
 }
diff --git a/android/support/mediacompat/testlib/MediaSessionConstants.java b/android/support/mediacompat/testlib/MediaSessionConstants.java
index c0a64d4..ffc24a6 100644
--- a/android/support/mediacompat/testlib/MediaSessionConstants.java
+++ b/android/support/mediacompat/testlib/MediaSessionConstants.java
@@ -57,4 +57,7 @@
 
     public static final int TEST_ERROR_CODE = 0x3;
     public static final String TEST_ERROR_MSG = "test-error-msg";
+
+    private MediaSessionConstants() {
+    }
 }
diff --git a/android/support/mediacompat/testlib/VersionConstants.java b/android/support/mediacompat/testlib/VersionConstants.java
index 4b217b1..5d877f2 100644
--- a/android/support/mediacompat/testlib/VersionConstants.java
+++ b/android/support/mediacompat/testlib/VersionConstants.java
@@ -25,4 +25,7 @@
 
     public static final String VERSION_TOT = "tot";
     public static final String VERSION_PREVIOUS = "previous";
+
+    private VersionConstants() {
+    }
 }
diff --git a/android/support/mediacompat/testlib/util/IntentUtil.java b/android/support/mediacompat/testlib/util/IntentUtil.java
index 8d58a6f..14ba14c 100644
--- a/android/support/mediacompat/testlib/util/IntentUtil.java
+++ b/android/support/mediacompat/testlib/util/IntentUtil.java
@@ -129,4 +129,7 @@
         }
         return intent;
     }
+
+    private IntentUtil() {
+    }
 }
diff --git a/android/support/mediacompat/testlib/util/TestUtil.java b/android/support/mediacompat/testlib/util/TestUtil.java
index 21fd223..b06b219 100644
--- a/android/support/mediacompat/testlib/util/TestUtil.java
+++ b/android/support/mediacompat/testlib/util/TestUtil.java
@@ -38,4 +38,7 @@
             assertEquals(expected.get(key), observed.get(key));
         }
     }
+
+    private TestUtil() {
+    }
 }
diff --git a/android/support/multidex/MultiDex.java b/android/support/multidex/MultiDex.java
deleted file mode 100644
index 2b681db..0000000
--- a/android/support/multidex/MultiDex.java
+++ /dev/null
@@ -1,680 +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.multidex;
-
-import android.app.Application;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.os.Build;
-import android.util.Log;
-
-import dalvik.system.DexFile;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Array;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.ZipFile;
-
-/**
- * MultiDex patches {@link Context#getClassLoader() the application context class
- * loader} in order to load classes from more than one dex file. The primary
- * {@code classes.dex} must contain the classes necessary for calling this
- * class methods. Secondary dex files named classes2.dex, classes3.dex... found
- * in the application apk will be added to the classloader after first call to
- * {@link #install(Context)}.
- *
- * <p/>
- * This library provides compatibility for platforms with API level 4 through 20. This library does
- * nothing on newer versions of the platform which provide built-in support for secondary dex files.
- */
-public final class MultiDex {
-
-    static final String TAG = "MultiDex";
-
-    private static final String OLD_SECONDARY_FOLDER_NAME = "secondary-dexes";
-
-    private static final String CODE_CACHE_NAME = "code_cache";
-
-    private static final String CODE_CACHE_SECONDARY_FOLDER_NAME = "secondary-dexes";
-
-    private static final int MAX_SUPPORTED_SDK_VERSION = 20;
-
-    private static final int MIN_SDK_VERSION = 4;
-
-    private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
-
-    private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
-
-    private static final String NO_KEY_PREFIX = "";
-
-    private static final Set<File> installedApk = new HashSet<File>();
-
-    private static final boolean IS_VM_MULTIDEX_CAPABLE =
-            isVMMultidexCapable(System.getProperty("java.vm.version"));
-
-    private MultiDex() {}
-
-    /**
-     * Patches the application context class loader by appending extra dex files
-     * loaded from the application apk. This method should be called in the
-     * attachBaseContext of your {@link Application}, see
-     * {@link MultiDexApplication} for more explanation and an example.
-     *
-     * @param context application context.
-     * @throws RuntimeException if an error occurred preventing the classloader
-     *         extension.
-     */
-    public static void install(Context context) {
-        Log.i(TAG, "Installing application");
-        if (IS_VM_MULTIDEX_CAPABLE) {
-            Log.i(TAG, "VM has multidex support, MultiDex support library is disabled.");
-            return;
-        }
-
-        if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {
-            throw new RuntimeException("MultiDex installation failed. SDK " + Build.VERSION.SDK_INT
-                    + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");
-        }
-
-        try {
-            ApplicationInfo applicationInfo = getApplicationInfo(context);
-            if (applicationInfo == null) {
-              Log.i(TAG, "No ApplicationInfo available, i.e. running on a test Context:"
-                  + " MultiDex support library is disabled.");
-              return;
-            }
-
-            doInstallation(context,
-                    new File(applicationInfo.sourceDir),
-                    new File(applicationInfo.dataDir),
-                    CODE_CACHE_SECONDARY_FOLDER_NAME,
-                    NO_KEY_PREFIX,
-                    true);
-
-        } catch (Exception e) {
-            Log.e(TAG, "MultiDex installation failure", e);
-            throw new RuntimeException("MultiDex installation failed (" + e.getMessage() + ").");
-        }
-        Log.i(TAG, "install done");
-    }
-
-    /**
-     * Patches the instrumentation context class loader by appending extra dex files
-     * loaded from the instrumentation apk and the application apk. This method should be called in
-     * the onCreate of your {@link Instrumentation}, see
-     * {@link com.android.test.runner.MultiDexTestRunner} for an example.
-     *
-     * @param instrumentationContext instrumentation context.
-     * @param targetContext target application context.
-     * @throws RuntimeException if an error occurred preventing the classloader
-     *         extension.
-     */
-    public static void installInstrumentation(Context instrumentationContext,
-            Context targetContext) {
-        Log.i(TAG, "Installing instrumentation");
-
-        if (IS_VM_MULTIDEX_CAPABLE) {
-            Log.i(TAG, "VM has multidex support, MultiDex support library is disabled.");
-            return;
-        }
-
-        if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {
-            throw new RuntimeException("MultiDex installation failed. SDK " + Build.VERSION.SDK_INT
-                    + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");
-        }
-        try {
-
-            ApplicationInfo instrumentationInfo = getApplicationInfo(instrumentationContext);
-            if (instrumentationInfo == null) {
-                Log.i(TAG, "No ApplicationInfo available for instrumentation, i.e. running on a"
-                    + " test Context: MultiDex support library is disabled.");
-                return;
-            }
-
-            ApplicationInfo applicationInfo = getApplicationInfo(targetContext);
-            if (applicationInfo == null) {
-                Log.i(TAG, "No ApplicationInfo available, i.e. running on a test Context:"
-                    + " MultiDex support library is disabled.");
-                return;
-            }
-
-            String instrumentationPrefix = instrumentationContext.getPackageName() + ".";
-
-            File dataDir = new File(applicationInfo.dataDir);
-
-            doInstallation(targetContext,
-                    new File(instrumentationInfo.sourceDir),
-                    dataDir,
-                    instrumentationPrefix + CODE_CACHE_SECONDARY_FOLDER_NAME,
-                    instrumentationPrefix,
-                    false);
-
-            doInstallation(targetContext,
-                    new File(applicationInfo.sourceDir),
-                    dataDir,
-                    CODE_CACHE_SECONDARY_FOLDER_NAME,
-                    NO_KEY_PREFIX,
-                    false);
-        } catch (Exception e) {
-            Log.e(TAG, "MultiDex installation failure", e);
-            throw new RuntimeException("MultiDex installation failed (" + e.getMessage() + ").");
-        }
-        Log.i(TAG, "Installation done");
-    }
-
-    /**
-     * @param mainContext context used to get filesDir, to save preference and to get the
-     * classloader to patch.
-     * @param sourceApk Apk file.
-     * @param dataDir data directory to use for code cache simulation.
-     * @param secondaryFolderName name of the folder for storing extractions.
-     * @param prefsKeyPrefix prefix of all stored preference keys.
-     * @param reinstallOnPatchRecoverableException if set to true, will attempt a clean extraction
-     * if a possibly recoverable exception occurs during classloader patching.
-     */
-    private static void doInstallation(Context mainContext, File sourceApk, File dataDir,
-            String secondaryFolderName, String prefsKeyPrefix,
-            boolean reinstallOnPatchRecoverableException) throws IOException,
-                IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
-                InvocationTargetException, NoSuchMethodException, SecurityException,
-                ClassNotFoundException, InstantiationException {
-        synchronized (installedApk) {
-            if (installedApk.contains(sourceApk)) {
-                return;
-            }
-            installedApk.add(sourceApk);
-
-            if (Build.VERSION.SDK_INT > MAX_SUPPORTED_SDK_VERSION) {
-                Log.w(TAG, "MultiDex is not guaranteed to work in SDK version "
-                        + Build.VERSION.SDK_INT + ": SDK version higher than "
-                        + MAX_SUPPORTED_SDK_VERSION + " should be backed by "
-                        + "runtime with built-in multidex capabilty but it's not the "
-                        + "case here: java.vm.version=\""
-                        + System.getProperty("java.vm.version") + "\"");
-            }
-
-            /* The patched class loader is expected to be a descendant of
-             * dalvik.system.BaseDexClassLoader. We modify its
-             * dalvik.system.DexPathList pathList field to append additional DEX
-             * file entries.
-             */
-            ClassLoader loader;
-            try {
-                loader = mainContext.getClassLoader();
-            } catch (RuntimeException e) {
-                /* Ignore those exceptions so that we don't break tests relying on Context like
-                 * a android.test.mock.MockContext or a android.content.ContextWrapper with a
-                 * null base Context.
-                 */
-                Log.w(TAG, "Failure while trying to obtain Context class loader. " +
-                        "Must be running in test mode. Skip patching.", e);
-                return;
-            }
-            if (loader == null) {
-                // Note, the context class loader is null when running Robolectric tests.
-                Log.e(TAG,
-                        "Context class loader is null. Must be running in test mode. "
-                        + "Skip patching.");
-                return;
-            }
-
-            try {
-              clearOldDexDir(mainContext);
-            } catch (Throwable t) {
-              Log.w(TAG, "Something went wrong when trying to clear old MultiDex extraction, "
-                  + "continuing without cleaning.", t);
-            }
-
-            File dexDir = getDexDir(mainContext, dataDir, secondaryFolderName);
-            // MultiDexExtractor is taking the file lock and keeping it until it is closed.
-            // Keep it open during installSecondaryDexes and through forced extraction to ensure no
-            // extraction or optimizing dexopt is running in parallel.
-            MultiDexExtractor extractor = new MultiDexExtractor(sourceApk, dexDir);
-            IOException closeException = null;
-            try {
-                List<? extends File> files =
-                        extractor.load(mainContext, prefsKeyPrefix, false);
-                try {
-                    installSecondaryDexes(loader, dexDir, files);
-                // Some IOException causes may be fixed by a clean extraction.
-                } catch (IOException e) {
-                    if (!reinstallOnPatchRecoverableException) {
-                        throw e;
-                    }
-                    Log.w(TAG, "Failed to install extracted secondary dex files, retrying with "
-                            + "forced extraction", e);
-                    files = extractor.load(mainContext, prefsKeyPrefix, true);
-                    installSecondaryDexes(loader, dexDir, files);
-                }
-            } finally {
-                try {
-                    extractor.close();
-                } catch (IOException e) {
-                    // Delay throw of close exception to ensure we don't override some exception
-                    // thrown during the try block.
-                    closeException = e;
-                }
-            }
-            if (closeException != null) {
-                throw closeException;
-            }
-        }
-    }
-
-    private static ApplicationInfo getApplicationInfo(Context context) {
-        try {
-            /* Due to package install races it is possible for a process to be started from an old
-             * apk even though that apk has been replaced. Querying for ApplicationInfo by package
-             * name may return information for the new apk, leading to a runtime with the old main
-             * dex file and new secondary dex files. This leads to various problems like
-             * ClassNotFoundExceptions. Using context.getApplicationInfo() should result in the
-             * process having a consistent view of the world (even if it is of the old world). The
-             * package install races are eventually resolved and old processes are killed.
-             */
-            return context.getApplicationInfo();
-        } catch (RuntimeException e) {
-            /* Ignore those exceptions so that we don't break tests relying on Context like
-             * a android.test.mock.MockContext or a android.content.ContextWrapper with a null
-             * base Context.
-             */
-            Log.w(TAG, "Failure while trying to obtain ApplicationInfo from Context. " +
-                    "Must be running in test mode. Skip patching.", e);
-            return null;
-        }
-    }
-
-    /**
-     * Identifies if the current VM has a native support for multidex, meaning there is no need for
-     * additional installation by this library.
-     * @return true if the VM handles multidex
-     */
-    /* package visible for test */
-    static boolean isVMMultidexCapable(String versionString) {
-        boolean isMultidexCapable = false;
-        if (versionString != null) {
-            Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
-            if (matcher.matches()) {
-                try {
-                    int major = Integer.parseInt(matcher.group(1));
-                    int minor = Integer.parseInt(matcher.group(2));
-                    isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)
-                            || ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)
-                                    && (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));
-                } catch (NumberFormatException e) {
-                    // let isMultidexCapable be false
-                }
-            }
-        }
-        Log.i(TAG, "VM with version " + versionString +
-                (isMultidexCapable ?
-                        " has multidex support" :
-                        " does not have multidex support"));
-        return isMultidexCapable;
-    }
-
-    private static void installSecondaryDexes(ClassLoader loader, File dexDir,
-        List<? extends File> files)
-            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,
-            InvocationTargetException, NoSuchMethodException, IOException, SecurityException,
-            ClassNotFoundException, InstantiationException {
-        if (!files.isEmpty()) {
-            if (Build.VERSION.SDK_INT >= 19) {
-                V19.install(loader, files, dexDir);
-            } else if (Build.VERSION.SDK_INT >= 14) {
-                V14.install(loader, files);
-            } else {
-                V4.install(loader, files);
-            }
-        }
-    }
-
-    /**
-     * Locates a given field anywhere in the class inheritance hierarchy.
-     *
-     * @param instance an object to search the field into.
-     * @param name field name
-     * @return a field object
-     * @throws NoSuchFieldException if the field cannot be located
-     */
-    private static Field findField(Object instance, String name) throws NoSuchFieldException {
-        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
-            try {
-                Field field = clazz.getDeclaredField(name);
-
-
-                if (!field.isAccessible()) {
-                    field.setAccessible(true);
-                }
-
-                return field;
-            } catch (NoSuchFieldException e) {
-                // ignore and search next
-            }
-        }
-
-        throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
-    }
-
-    /**
-     * Locates a given method anywhere in the class inheritance hierarchy.
-     *
-     * @param instance an object to search the method into.
-     * @param name method name
-     * @param parameterTypes method parameter types
-     * @return a method object
-     * @throws NoSuchMethodException if the method cannot be located
-     */
-    private static Method findMethod(Object instance, String name, Class<?>... parameterTypes)
-            throws NoSuchMethodException {
-        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
-            try {
-                Method method = clazz.getDeclaredMethod(name, parameterTypes);
-
-
-                if (!method.isAccessible()) {
-                    method.setAccessible(true);
-                }
-
-                return method;
-            } catch (NoSuchMethodException e) {
-                // ignore and search next
-            }
-        }
-
-        throw new NoSuchMethodException("Method " + name + " with parameters " +
-                Arrays.asList(parameterTypes) + " not found in " + instance.getClass());
-    }
-
-    /**
-     * Replace the value of a field containing a non null array, by a new array containing the
-     * elements of the original array plus the elements of extraElements.
-     * @param instance the instance whose field is to be modified.
-     * @param fieldName the field to modify.
-     * @param extraElements elements to append at the end of the array.
-     */
-    private static void expandFieldArray(Object instance, String fieldName,
-            Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
-            IllegalAccessException {
-        Field jlrField = findField(instance, fieldName);
-        Object[] original = (Object[]) jlrField.get(instance);
-        Object[] combined = (Object[]) Array.newInstance(
-                original.getClass().getComponentType(), original.length + extraElements.length);
-        System.arraycopy(original, 0, combined, 0, original.length);
-        System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
-        jlrField.set(instance, combined);
-    }
-
-    private static void clearOldDexDir(Context context) throws Exception {
-        File dexDir = new File(context.getFilesDir(), OLD_SECONDARY_FOLDER_NAME);
-        if (dexDir.isDirectory()) {
-            Log.i(TAG, "Clearing old secondary dex dir (" + dexDir.getPath() + ").");
-            File[] files = dexDir.listFiles();
-            if (files == null) {
-                Log.w(TAG, "Failed to list secondary dex dir content (" + dexDir.getPath() + ").");
-                return;
-            }
-            for (File oldFile : files) {
-                Log.i(TAG, "Trying to delete old file " + oldFile.getPath() + " of size "
-                        + oldFile.length());
-                if (!oldFile.delete()) {
-                    Log.w(TAG, "Failed to delete old file " + oldFile.getPath());
-                } else {
-                    Log.i(TAG, "Deleted old file " + oldFile.getPath());
-                }
-            }
-            if (!dexDir.delete()) {
-                Log.w(TAG, "Failed to delete secondary dex dir " + dexDir.getPath());
-            } else {
-                Log.i(TAG, "Deleted old secondary dex dir " + dexDir.getPath());
-            }
-        }
-    }
-
-    private static File getDexDir(Context context, File dataDir, String secondaryFolderName)
-            throws IOException {
-        File cache = new File(dataDir, CODE_CACHE_NAME);
-        try {
-            mkdirChecked(cache);
-        } catch (IOException e) {
-            /* If we can't emulate code_cache, then store to filesDir. This means abandoning useless
-             * files on disk if the device ever updates to android 5+. But since this seems to
-             * happen only on some devices running android 2, this should cause no pollution.
-             */
-            cache = new File(context.getFilesDir(), CODE_CACHE_NAME);
-            mkdirChecked(cache);
-        }
-        File dexDir = new File(cache, secondaryFolderName);
-        mkdirChecked(dexDir);
-        return dexDir;
-    }
-
-    private static void mkdirChecked(File dir) throws IOException {
-        dir.mkdir();
-        if (!dir.isDirectory()) {
-            File parent = dir.getParentFile();
-            if (parent == null) {
-                Log.e(TAG, "Failed to create dir " + dir.getPath() + ". Parent file is null.");
-            } else {
-                Log.e(TAG, "Failed to create dir " + dir.getPath() +
-                        ". parent file is a dir " + parent.isDirectory() +
-                        ", a file " + parent.isFile() +
-                        ", exists " + parent.exists() +
-                        ", readable " + parent.canRead() +
-                        ", writable " + parent.canWrite());
-            }
-            throw new IOException("Failed to create directory " + dir.getPath());
-        }
-    }
-
-    /**
-     * Installer for platform versions 19.
-     */
-    private static final class V19 {
-
-        static void install(ClassLoader loader,
-                List<? extends File> additionalClassPathEntries,
-                File optimizedDirectory)
-                        throws IllegalArgumentException, IllegalAccessException,
-                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException,
-                        IOException {
-            /* The patched class loader is expected to be a descendant of
-             * dalvik.system.BaseDexClassLoader. We modify its
-             * dalvik.system.DexPathList pathList field to append additional DEX
-             * file entries.
-             */
-            Field pathListField = findField(loader, "pathList");
-            Object dexPathList = pathListField.get(loader);
-            ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
-            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,
-                    new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
-                    suppressedExceptions));
-            if (suppressedExceptions.size() > 0) {
-                for (IOException e : suppressedExceptions) {
-                    Log.w(TAG, "Exception in makeDexElement", e);
-                }
-                Field suppressedExceptionsField =
-                        findField(dexPathList, "dexElementsSuppressedExceptions");
-                IOException[] dexElementsSuppressedExceptions =
-                        (IOException[]) suppressedExceptionsField.get(dexPathList);
-
-                if (dexElementsSuppressedExceptions == null) {
-                    dexElementsSuppressedExceptions =
-                            suppressedExceptions.toArray(
-                                    new IOException[suppressedExceptions.size()]);
-                } else {
-                    IOException[] combined =
-                            new IOException[suppressedExceptions.size() +
-                                            dexElementsSuppressedExceptions.length];
-                    suppressedExceptions.toArray(combined);
-                    System.arraycopy(dexElementsSuppressedExceptions, 0, combined,
-                            suppressedExceptions.size(), dexElementsSuppressedExceptions.length);
-                    dexElementsSuppressedExceptions = combined;
-                }
-
-                suppressedExceptionsField.set(dexPathList, dexElementsSuppressedExceptions);
-
-                IOException exception = new IOException("I/O exception during makeDexElement");
-                exception.initCause(suppressedExceptions.get(0));
-                throw exception;
-            }
-        }
-
-        /**
-         * A wrapper around
-         * {@code private static final dalvik.system.DexPathList#makeDexElements}.
-         */
-        private static Object[] makeDexElements(
-                Object dexPathList, ArrayList<File> files, File optimizedDirectory,
-                ArrayList<IOException> suppressedExceptions)
-                        throws IllegalAccessException, InvocationTargetException,
-                        NoSuchMethodException {
-            Method makeDexElements =
-                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class,
-                            ArrayList.class);
-
-            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory,
-                    suppressedExceptions);
-        }
-    }
-
-    /**
-     * Installer for platform versions 14, 15, 16, 17 and 18.
-     */
-    private static final class V14 {
-
-        private static final int EXTRACTED_SUFFIX_LENGTH =
-                MultiDexExtractor.EXTRACTED_SUFFIX.length();
-
-        private final Constructor<?> elementConstructor;
-
-        static void install(ClassLoader loader,
-                List<? extends File> additionalClassPathEntries)
-                        throws  IOException, SecurityException, IllegalArgumentException,
-                        ClassNotFoundException, NoSuchMethodException, InstantiationException,
-                        IllegalAccessException, InvocationTargetException, NoSuchFieldException {
-            /* The patched class loader is expected to be a descendant of
-             * dalvik.system.BaseDexClassLoader. We modify its
-             * dalvik.system.DexPathList pathList field to append additional DEX
-             * file entries.
-             */
-            Field pathListField = findField(loader, "pathList");
-            Object dexPathList = pathListField.get(loader);
-            expandFieldArray(dexPathList, "dexElements",
-                    new V14().makeDexElements(additionalClassPathEntries));
-        }
-
-        private  V14() throws ClassNotFoundException, SecurityException, NoSuchMethodException {
-            Class<?> elementClass = Class.forName("dalvik.system.DexPathList$Element");
-            elementConstructor =
-                    elementClass.getConstructor(File.class, ZipFile.class, DexFile.class);
-            elementConstructor.setAccessible(true);
-        }
-
-        /**
-         * An emulation of {@code private static final dalvik.system.DexPathList#makeDexElements}
-         * accepting only extracted secondary dex files.
-         * OS version is catching IOException and just logging some of them, this version is letting
-         * them through.
-         */
-        private Object[] makeDexElements(List<? extends File> files)
-                throws IOException, SecurityException, IllegalArgumentException,
-                InstantiationException, IllegalAccessException, InvocationTargetException {
-            Object[] elements = new Object[files.size()];
-            for (int i = 0; i < elements.length; i++) {
-                File file = files.get(i);
-                elements[i] = elementConstructor.newInstance(
-                        file,
-                        new ZipFile(file),
-                        DexFile.loadDex(file.getPath(), optimizedPathFor(file), 0));
-            }
-            return elements;
-        }
-
-        /**
-         * Converts a zip file path of an extracted secondary dex to an output file path for an
-         * associated optimized dex file.
-         */
-        private static String optimizedPathFor(File path) {
-            // Any reproducible name ending with ".dex" should do but lets keep the same name
-            // as DexPathList.optimizedPathFor
-
-            File optimizedDirectory = path.getParentFile();
-            String fileName = path.getName();
-            String optimizedFileName =
-                    fileName.substring(0, fileName.length() - EXTRACTED_SUFFIX_LENGTH)
-                    + MultiDexExtractor.DEX_SUFFIX;
-            File result = new File(optimizedDirectory, optimizedFileName);
-            return result.getPath();
-        }
-    }
-
-    /**
-     * Installer for platform versions 4 to 13.
-     */
-    private static final class V4 {
-        static void install(ClassLoader loader,
-                List<? extends File> additionalClassPathEntries)
-                        throws IllegalArgumentException, IllegalAccessException,
-                        NoSuchFieldException, IOException {
-            /* The patched class loader is expected to be a descendant of
-             * dalvik.system.DexClassLoader. We modify its
-             * fields mPaths, mFiles, mZips and mDexs to append additional DEX
-             * file entries.
-             */
-            int extraSize = additionalClassPathEntries.size();
-
-            Field pathField = findField(loader, "path");
-
-            StringBuilder path = new StringBuilder((String) pathField.get(loader));
-            String[] extraPaths = new String[extraSize];
-            File[] extraFiles = new File[extraSize];
-            ZipFile[] extraZips = new ZipFile[extraSize];
-            DexFile[] extraDexs = new DexFile[extraSize];
-            for (ListIterator<? extends File> iterator = additionalClassPathEntries.listIterator();
-                    iterator.hasNext();) {
-                File additionalEntry = iterator.next();
-                String entryPath = additionalEntry.getAbsolutePath();
-                path.append(':').append(entryPath);
-                int index = iterator.previousIndex();
-                extraPaths[index] = entryPath;
-                extraFiles[index] = additionalEntry;
-                extraZips[index] = new ZipFile(additionalEntry);
-                extraDexs[index] = DexFile.loadDex(entryPath, entryPath + ".dex", 0);
-            }
-
-            pathField.set(loader, path.toString());
-            expandFieldArray(loader, "mPaths", extraPaths);
-            expandFieldArray(loader, "mFiles", extraFiles);
-            expandFieldArray(loader, "mZips", extraZips);
-            expandFieldArray(loader, "mDexs", extraDexs);
-        }
-    }
-
-}
diff --git a/android/support/multidex/MultiDexApplication.java b/android/support/multidex/MultiDexApplication.java
deleted file mode 100644
index 8524646..0000000
--- a/android/support/multidex/MultiDexApplication.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.multidex;
-
-import android.app.Application;
-import android.content.Context;
-
-/**
- * Minimal MultiDex capable application. To use the legacy multidex library there is 3 possibility:
- * <ul>
- * <li>Declare this class as the application in your AndroidManifest.xml.</li>
- * <li>Have your {@link Application} extends this class.</li>
- * <li>Have your {@link Application} override attachBaseContext starting with<br>
- * <code>
-  protected void attachBaseContext(Context base) {<br>
-    super.attachBaseContext(base);<br>
-    MultiDex.install(this);
-    </code></li>
- *   <ul>
- */
-public class MultiDexApplication extends Application {
-  @Override
-  protected void attachBaseContext(Context base) {
-    super.attachBaseContext(base);
-    MultiDex.install(this);
-  }
-}
diff --git a/android/support/multidex/MultiDexExtractor.java b/android/support/multidex/MultiDexExtractor.java
deleted file mode 100644
index f0fd6d4..0000000
--- a/android/support/multidex/MultiDexExtractor.java
+++ /dev/null
@@ -1,427 +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.multidex;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Build;
-import android.util.Log;
-import java.io.BufferedOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
-
-/**
- * Exposes application secondary dex files as files in the application data
- * directory.
- * {@link MultiDexExtractor} is taking the file lock in the dex dir on creation and release it
- * during close.
- */
-final class MultiDexExtractor implements Closeable {
-
-    /**
-     * Zip file containing one secondary dex file.
-     */
-    private static class ExtractedDex extends File {
-        public long crc = NO_VALUE;
-
-        public ExtractedDex(File dexDir, String fileName) {
-            super(dexDir, fileName);
-        }
-    }
-
-    private static final String TAG = MultiDex.TAG;
-
-    /**
-     * We look for additional dex files named {@code classes2.dex},
-     * {@code classes3.dex}, etc.
-     */
-    private static final String DEX_PREFIX = "classes";
-    static final String DEX_SUFFIX = ".dex";
-
-    private static final String EXTRACTED_NAME_EXT = ".classes";
-    static final String EXTRACTED_SUFFIX = ".zip";
-    private static final int MAX_EXTRACT_ATTEMPTS = 3;
-
-    private static final String PREFS_FILE = "multidex.version";
-    private static final String KEY_TIME_STAMP = "timestamp";
-    private static final String KEY_CRC = "crc";
-    private static final String KEY_DEX_NUMBER = "dex.number";
-    private static final String KEY_DEX_CRC = "dex.crc.";
-    private static final String KEY_DEX_TIME = "dex.time.";
-
-    /**
-     * Size of reading buffers.
-     */
-    private static final int BUFFER_SIZE = 0x4000;
-    /* Keep value away from 0 because it is a too probable time stamp value */
-    private static final long NO_VALUE = -1L;
-
-    private static final String LOCK_FILENAME = "MultiDex.lock";
-    private final File sourceApk;
-    private final long sourceCrc;
-    private final File dexDir;
-    private final RandomAccessFile lockRaf;
-    private final FileChannel lockChannel;
-    private final FileLock cacheLock;
-
-    MultiDexExtractor(File sourceApk, File dexDir) throws IOException {
-        Log.i(TAG, "MultiDexExtractor(" + sourceApk.getPath() + ", " + dexDir.getPath() + ")");
-        this.sourceApk = sourceApk;
-        this.dexDir = dexDir;
-        sourceCrc = getZipCrc(sourceApk);
-        File lockFile = new File(dexDir, LOCK_FILENAME);
-        lockRaf = new RandomAccessFile(lockFile, "rw");
-        try {
-            lockChannel = lockRaf.getChannel();
-            try {
-                Log.i(TAG, "Blocking on lock " + lockFile.getPath());
-                cacheLock = lockChannel.lock();
-            } catch (IOException | RuntimeException | Error e) {
-                closeQuietly(lockChannel);
-                throw e;
-            }
-            Log.i(TAG, lockFile.getPath() + " locked");
-        } catch (IOException | RuntimeException | Error e) {
-            closeQuietly(lockRaf);
-            throw e;
-        }
-    }
-
-    /**
-     * Extracts application secondary dexes into files in the application data
-     * directory.
-     *
-     * @return a list of files that were created. The list may be empty if there
-     *         are no secondary dex files. Never return null.
-     * @throws IOException if encounters a problem while reading or writing
-     *         secondary dex files
-     */
-    List<? extends File> load(Context context, String prefsKeyPrefix, boolean forceReload)
-            throws IOException {
-        Log.i(TAG, "MultiDexExtractor.load(" + sourceApk.getPath() + ", " + forceReload + ", " +
-                prefsKeyPrefix + ")");
-
-        if (!cacheLock.isValid()) {
-            throw new IllegalStateException("MultiDexExtractor was closed");
-        }
-
-        List<ExtractedDex> files;
-        if (!forceReload && !isModified(context, sourceApk, sourceCrc, prefsKeyPrefix)) {
-            try {
-                files = loadExistingExtractions(context, prefsKeyPrefix);
-            } catch (IOException ioe) {
-                Log.w(TAG, "Failed to reload existing extracted secondary dex files,"
-                        + " falling back to fresh extraction", ioe);
-                files = performExtractions();
-                putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), sourceCrc,
-                        files);
-            }
-        } else {
-            if (forceReload) {
-                Log.i(TAG, "Forced extraction must be performed.");
-            } else {
-                Log.i(TAG, "Detected that extraction must be performed.");
-            }
-            files = performExtractions();
-            putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), sourceCrc,
-                    files);
-        }
-
-        Log.i(TAG, "load found " + files.size() + " secondary dex files");
-        return files;
-    }
-
-    @Override
-    public void close() throws IOException {
-        cacheLock.release();
-        lockChannel.close();
-        lockRaf.close();
-    }
-
-    /**
-     * Load previously extracted secondary dex files. Should be called only while owning the lock on
-     * {@link #LOCK_FILENAME}.
-     */
-    private List<ExtractedDex> loadExistingExtractions(
-            Context context,
-            String prefsKeyPrefix)
-            throws IOException {
-        Log.i(TAG, "loading existing secondary dex files");
-
-        final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
-        SharedPreferences multiDexPreferences = getMultiDexPreferences(context);
-        int totalDexNumber = multiDexPreferences.getInt(prefsKeyPrefix + KEY_DEX_NUMBER, 1);
-        final List<ExtractedDex> files = new ArrayList<ExtractedDex>(totalDexNumber - 1);
-
-        for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
-            String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
-            ExtractedDex extractedFile = new ExtractedDex(dexDir, fileName);
-            if (extractedFile.isFile()) {
-                extractedFile.crc = getZipCrc(extractedFile);
-                long expectedCrc = multiDexPreferences.getLong(
-                        prefsKeyPrefix + KEY_DEX_CRC + secondaryNumber, NO_VALUE);
-                long expectedModTime = multiDexPreferences.getLong(
-                        prefsKeyPrefix + KEY_DEX_TIME + secondaryNumber, NO_VALUE);
-                long lastModified = extractedFile.lastModified();
-                if ((expectedModTime != lastModified)
-                        || (expectedCrc != extractedFile.crc)) {
-                    throw new IOException("Invalid extracted dex: " + extractedFile +
-                            " (key \"" + prefsKeyPrefix + "\"), expected modification time: "
-                            + expectedModTime + ", modification time: "
-                            + lastModified + ", expected crc: "
-                            + expectedCrc + ", file crc: " + extractedFile.crc);
-                }
-                files.add(extractedFile);
-            } else {
-                throw new IOException("Missing extracted secondary dex file '" +
-                        extractedFile.getPath() + "'");
-            }
-        }
-
-        return files;
-    }
-
-
-    /**
-     * Compare current archive and crc with values stored in {@link SharedPreferences}. Should be
-     * called only while owning the lock on {@link #LOCK_FILENAME}.
-     */
-    private static boolean isModified(Context context, File archive, long currentCrc,
-            String prefsKeyPrefix) {
-        SharedPreferences prefs = getMultiDexPreferences(context);
-        return (prefs.getLong(prefsKeyPrefix + KEY_TIME_STAMP, NO_VALUE) != getTimeStamp(archive))
-                || (prefs.getLong(prefsKeyPrefix + KEY_CRC, NO_VALUE) != currentCrc);
-    }
-
-    private static long getTimeStamp(File archive) {
-        long timeStamp = archive.lastModified();
-        if (timeStamp == NO_VALUE) {
-            // never return NO_VALUE
-            timeStamp--;
-        }
-        return timeStamp;
-    }
-
-
-    private static long getZipCrc(File archive) throws IOException {
-        long computedValue = ZipUtil.getZipCrc(archive);
-        if (computedValue == NO_VALUE) {
-            // never return NO_VALUE
-            computedValue--;
-        }
-        return computedValue;
-    }
-
-    private List<ExtractedDex> performExtractions() throws IOException {
-
-        final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
-
-        // It is safe to fully clear the dex dir because we own the file lock so no other process is
-        // extracting or running optimizing dexopt. It may cause crash of already running
-        // applications if for whatever reason we end up extracting again over a valid extraction.
-        clearDexDir();
-
-        List<ExtractedDex> files = new ArrayList<ExtractedDex>();
-
-        final ZipFile apk = new ZipFile(sourceApk);
-        try {
-
-            int secondaryNumber = 2;
-
-            ZipEntry dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);
-            while (dexFile != null) {
-                String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
-                ExtractedDex extractedFile = new ExtractedDex(dexDir, fileName);
-                files.add(extractedFile);
-
-                Log.i(TAG, "Extraction is needed for file " + extractedFile);
-                int numAttempts = 0;
-                boolean isExtractionSuccessful = false;
-                while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isExtractionSuccessful) {
-                    numAttempts++;
-
-                    // Create a zip file (extractedFile) containing only the secondary dex file
-                    // (dexFile) from the apk.
-                    extract(apk, dexFile, extractedFile, extractedFilePrefix);
-
-                    // Read zip crc of extracted dex
-                    try {
-                        extractedFile.crc = getZipCrc(extractedFile);
-                        isExtractionSuccessful = true;
-                    } catch (IOException e) {
-                        isExtractionSuccessful = false;
-                        Log.w(TAG, "Failed to read crc from " + extractedFile.getAbsolutePath(), e);
-                    }
-
-                    // Log size and crc of the extracted zip file
-                    Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "succeeded" : "failed")
-                            + " '" + extractedFile.getAbsolutePath() + "': length "
-                            + extractedFile.length() + " - crc: " + extractedFile.crc);
-                    if (!isExtractionSuccessful) {
-                        // Delete the extracted file
-                        extractedFile.delete();
-                        if (extractedFile.exists()) {
-                            Log.w(TAG, "Failed to delete corrupted secondary dex '" +
-                                    extractedFile.getPath() + "'");
-                        }
-                    }
-                }
-                if (!isExtractionSuccessful) {
-                    throw new IOException("Could not create zip file " +
-                            extractedFile.getAbsolutePath() + " for secondary dex (" +
-                            secondaryNumber + ")");
-                }
-                secondaryNumber++;
-                dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);
-            }
-        } finally {
-            try {
-                apk.close();
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to close resource", e);
-            }
-        }
-
-        return files;
-    }
-
-    /**
-     * Save {@link SharedPreferences}. Should be called only while owning the lock on
-     * {@link #LOCK_FILENAME}.
-     */
-    private static void putStoredApkInfo(Context context, String keyPrefix, long timeStamp,
-            long crc, List<ExtractedDex> extractedDexes) {
-        SharedPreferences prefs = getMultiDexPreferences(context);
-        SharedPreferences.Editor edit = prefs.edit();
-        edit.putLong(keyPrefix + KEY_TIME_STAMP, timeStamp);
-        edit.putLong(keyPrefix + KEY_CRC, crc);
-        edit.putInt(keyPrefix + KEY_DEX_NUMBER, extractedDexes.size() + 1);
-
-        int extractedDexId = 2;
-        for (ExtractedDex dex : extractedDexes) {
-            edit.putLong(keyPrefix + KEY_DEX_CRC + extractedDexId, dex.crc);
-            edit.putLong(keyPrefix + KEY_DEX_TIME + extractedDexId, dex.lastModified());
-            extractedDexId++;
-        }
-        /* Use commit() and not apply() as advised by the doc because we need synchronous writing of
-         * the editor content and apply is doing an "asynchronous commit to disk".
-         */
-        edit.commit();
-    }
-
-    /**
-     * Get the MuliDex {@link SharedPreferences} for the current application. Should be called only
-     * while owning the lock on {@link #LOCK_FILENAME}.
-     */
-    private static SharedPreferences getMultiDexPreferences(Context context) {
-        return context.getSharedPreferences(PREFS_FILE,
-                Build.VERSION.SDK_INT < 11 /* Build.VERSION_CODES.HONEYCOMB */
-                        ? Context.MODE_PRIVATE
-                        : Context.MODE_PRIVATE | 0x0004 /* Context.MODE_MULTI_PROCESS */);
-    }
-
-    /**
-     * Clear the dex dir from all files but the lock.
-     */
-    private void clearDexDir() {
-        File[] files = dexDir.listFiles(new FileFilter() {
-            @Override
-            public boolean accept(File pathname) {
-                return !pathname.getName().equals(LOCK_FILENAME);
-            }
-        });
-        if (files == null) {
-            Log.w(TAG, "Failed to list secondary dex dir content (" + dexDir.getPath() + ").");
-            return;
-        }
-        for (File oldFile : files) {
-            Log.i(TAG, "Trying to delete old file " + oldFile.getPath() + " of size " +
-                    oldFile.length());
-            if (!oldFile.delete()) {
-                Log.w(TAG, "Failed to delete old file " + oldFile.getPath());
-            } else {
-                Log.i(TAG, "Deleted old file " + oldFile.getPath());
-            }
-        }
-    }
-
-    private static void extract(ZipFile apk, ZipEntry dexFile, File extractTo,
-            String extractedFilePrefix) throws IOException, FileNotFoundException {
-
-        InputStream in = apk.getInputStream(dexFile);
-        ZipOutputStream out = null;
-        // Temp files must not start with extractedFilePrefix to get cleaned up in prepareDexDir()
-        File tmp = File.createTempFile("tmp-" + extractedFilePrefix, EXTRACTED_SUFFIX,
-                extractTo.getParentFile());
-        Log.i(TAG, "Extracting " + tmp.getPath());
-        try {
-            out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tmp)));
-            try {
-                ZipEntry classesDex = new ZipEntry("classes.dex");
-                // keep zip entry time since it is the criteria used by Dalvik
-                classesDex.setTime(dexFile.getTime());
-                out.putNextEntry(classesDex);
-
-                byte[] buffer = new byte[BUFFER_SIZE];
-                int length = in.read(buffer);
-                while (length != -1) {
-                    out.write(buffer, 0, length);
-                    length = in.read(buffer);
-                }
-                out.closeEntry();
-            } finally {
-                out.close();
-            }
-            if (!tmp.setReadOnly()) {
-                throw new IOException("Failed to mark readonly \"" + tmp.getAbsolutePath() +
-                        "\" (tmp of \"" + extractTo.getAbsolutePath() + "\")");
-            }
-            Log.i(TAG, "Renaming to " + extractTo.getPath());
-            if (!tmp.renameTo(extractTo)) {
-                throw new IOException("Failed to rename \"" + tmp.getAbsolutePath() +
-                        "\" to \"" + extractTo.getAbsolutePath() + "\"");
-            }
-        } finally {
-            closeQuietly(in);
-            tmp.delete(); // return status ignored
-        }
-    }
-
-    /**
-     * Closes the given {@code Closeable}. Suppresses any IO exceptions.
-     */
-    private static void closeQuietly(Closeable closeable) {
-        try {
-            closeable.close();
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to close resource", e);
-        }
-    }
-}
diff --git a/android/support/multidex/MultiDexTest.java b/android/support/multidex/MultiDexTest.java
deleted file mode 100644
index 62bdc30..0000000
--- a/android/support/multidex/MultiDexTest.java
+++ /dev/null
@@ -1,47 +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.multidex;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Test for {@link MultiDex} class.
- */
-public class MultiDexTest {
-    @Test
-    public void testVersionCheck() {
-        Assert.assertFalse(MultiDex.isVMMultidexCapable(null));
-        Assert.assertFalse(MultiDex.isVMMultidexCapable("-1.32.54"));
-        Assert.assertFalse(MultiDex.isVMMultidexCapable("1.32.54"));
-        Assert.assertFalse(MultiDex.isVMMultidexCapable("1.32"));
-        Assert.assertFalse(MultiDex.isVMMultidexCapable("2.0"));
-        Assert.assertFalse(MultiDex.isVMMultidexCapable("2.000.1254"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("2.1.1254"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("2.1"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("2.2"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("2.1.0000"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("2.2.0000"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("002.0001.0010"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("3.0"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("3.0.0"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("3.0.1"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("3.1.0"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("03.1.132645"));
-        Assert.assertTrue(MultiDex.isVMMultidexCapable("03.2"));
-    }
-}
diff --git a/android/support/multidex/ZipEntryReader.java b/android/support/multidex/ZipEntryReader.java
deleted file mode 100644
index c10bec5..0000000
--- a/android/support/multidex/ZipEntryReader.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/* Apache Harmony HEADER because the code in this class comes mostly from ZipFile, ZipEntry and
- * ZipConstants from android libcore.
- */
-
-package android.support.multidex;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-
-class ZipEntryReader {
-    static final Charset UTF_8 = Charset.forName("UTF-8");
-   /**
-     * General Purpose Bit Flags, Bit 0.
-     * If set, indicates that the file is encrypted.
-     */
-    private static final int GPBF_ENCRYPTED_FLAG = 1 << 0;
-
-    /**
-     * Supported General Purpose Bit Flags Mask.
-     * Bit mask of bits not supported.
-     * Note: The only bit that we will enforce at this time
-     * is the encrypted bit. Although other bits are not supported,
-     * we must not enforce them as this could break some legitimate
-     * use cases (See http://b/8617715).
-     */
-    private static final int GPBF_UNSUPPORTED_MASK = GPBF_ENCRYPTED_FLAG;
-    private static final long CENSIG = 0x2014b50;
-
-    static ZipEntry readEntry(ByteBuffer in) throws IOException {
-
-        int sig = in.getInt();
-        if (sig != CENSIG) {
-             throw new ZipException("Central Directory Entry not found");
-        }
-
-        in.position(8);
-        int gpbf = in.getShort() & 0xffff;
-
-        if ((gpbf & GPBF_UNSUPPORTED_MASK) != 0) {
-            throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
-        }
-
-        int compressionMethod = in.getShort() & 0xffff;
-        int time = in.getShort() & 0xffff;
-        int modDate = in.getShort() & 0xffff;
-
-        // These are 32-bit values in the file, but 64-bit fields in this object.
-        long crc = ((long) in.getInt()) & 0xffffffffL;
-        long compressedSize = ((long) in.getInt()) & 0xffffffffL;
-        long size = ((long) in.getInt()) & 0xffffffffL;
-
-        int nameLength = in.getShort() & 0xffff;
-        int extraLength = in.getShort() & 0xffff;
-        int commentByteCount = in.getShort() & 0xffff;
-
-        // This is a 32-bit value in the file, but a 64-bit field in this object.
-        in.position(42);
-        long localHeaderRelOffset = ((long) in.getInt()) & 0xffffffffL;
-
-        byte[] nameBytes = new byte[nameLength];
-        in.get(nameBytes, 0, nameBytes.length);
-        String name = new String(nameBytes, 0, nameBytes.length, UTF_8);
-
-        ZipEntry entry = new ZipEntry(name);
-        entry.setMethod(compressionMethod);
-        entry.setTime(getTime(time, modDate));
-
-        entry.setCrc(crc);
-        entry.setCompressedSize(compressedSize);
-        entry.setSize(size);
-
-        // The RI has always assumed UTF-8. (If GPBF_UTF8_FLAG isn't set, the encoding is
-        // actually IBM-437.)
-        if (commentByteCount > 0) {
-            byte[] commentBytes = new byte[commentByteCount];
-            in.get(commentBytes, 0, commentByteCount);
-            entry.setComment(new String(commentBytes, 0, commentBytes.length, UTF_8));
-        }
-
-        if (extraLength > 0) {
-            byte[] extra = new byte[extraLength];
-            in.get(extra, 0, extraLength);
-            entry.setExtra(extra);
-        }
-
-        return entry;
-
-    }
-
-    private static long getTime(int time, int modDate) {
-        GregorianCalendar cal = new GregorianCalendar();
-        cal.set(Calendar.MILLISECOND, 0);
-        cal.set(1980 + ((modDate >> 9) & 0x7f), ((modDate >> 5) & 0xf) - 1,
-                modDate & 0x1f, (time >> 11) & 0x1f, (time >> 5) & 0x3f,
-                (time & 0x1f) << 1);
-        return cal.getTime().getTime();
-    }
-
-}
diff --git a/android/support/multidex/ZipUtil.java b/android/support/multidex/ZipUtil.java
deleted file mode 100644
index cd518cc..0000000
--- a/android/support/multidex/ZipUtil.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/* Apache Harmony HEADER because the code in this class comes mostly from ZipFile, ZipEntry and
- * ZipConstants from android libcore.
- */
-
-package android.support.multidex;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.zip.CRC32;
-import java.util.zip.ZipException;
-
-/**
- * Tools to build a quick partial crc of zip files.
- */
-final class ZipUtil {
-    static class CentralDirectory {
-        long offset;
-        long size;
-    }
-
-    /* redefine those constant here because of bug 13721174 preventing to compile using the
-     * constants defined in ZipFile */
-    private static final int ENDHDR = 22;
-    private static final int ENDSIG = 0x6054b50;
-
-    /**
-     * Size of reading buffers.
-     */
-    private static final int BUFFER_SIZE = 0x4000;
-
-    /**
-     * Compute crc32 of the central directory of an apk. The central directory contains
-     * the crc32 of each entries in the zip so the computed result is considered valid for the whole
-     * zip file. Does not support zip64 nor multidisk but it should be OK for now since ZipFile does
-     * not either.
-     */
-    static long getZipCrc(File apk) throws IOException {
-        RandomAccessFile raf = new RandomAccessFile(apk, "r");
-        try {
-            CentralDirectory dir = findCentralDirectory(raf);
-
-            return computeCrcOfCentralDir(raf, dir);
-        } finally {
-            raf.close();
-        }
-    }
-
-    /* Package visible for testing */
-    static CentralDirectory findCentralDirectory(RandomAccessFile raf) throws IOException,
-            ZipException {
-        long scanOffset = raf.length() - ENDHDR;
-        if (scanOffset < 0) {
-            throw new ZipException("File too short to be a zip file: " + raf.length());
-        }
-
-        long stopOffset = scanOffset - 0x10000 /* ".ZIP file comment"'s max length */;
-        if (stopOffset < 0) {
-            stopOffset = 0;
-        }
-
-        int endSig = Integer.reverseBytes(ENDSIG);
-        while (true) {
-            raf.seek(scanOffset);
-            if (raf.readInt() == endSig) {
-                break;
-            }
-
-            scanOffset--;
-            if (scanOffset < stopOffset) {
-                throw new ZipException("End Of Central Directory signature not found");
-            }
-        }
-        // Read the End Of Central Directory. ENDHDR includes the signature
-        // bytes,
-        // which we've already read.
-
-        // Pull out the information we need.
-        raf.skipBytes(2); // diskNumber
-        raf.skipBytes(2); // diskWithCentralDir
-        raf.skipBytes(2); // numEntries
-        raf.skipBytes(2); // totalNumEntries
-        CentralDirectory dir = new CentralDirectory();
-        dir.size = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL;
-        dir.offset = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL;
-        return dir;
-    }
-
-    /* Package visible for testing */
-    static long computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir)
-            throws IOException {
-        CRC32 crc = new CRC32();
-        long stillToRead = dir.size;
-        raf.seek(dir.offset);
-        int length = (int) Math.min(BUFFER_SIZE, stillToRead);
-        byte[] buffer = new byte[BUFFER_SIZE];
-        length = raf.read(buffer, 0, length);
-        while (length != -1) {
-            crc.update(buffer, 0, length);
-            stillToRead -= length;
-            if (stillToRead == 0) {
-                break;
-            }
-            length = (int) Math.min(BUFFER_SIZE, stillToRead);
-            length = raf.read(buffer, 0, length);
-        }
-        return crc.getValue();
-    }
-}
diff --git a/android/support/multidex/ZipUtilTest.java b/android/support/multidex/ZipUtilTest.java
deleted file mode 100644
index 985d97f..0000000
--- a/android/support/multidex/ZipUtilTest.java
+++ /dev/null
@@ -1,172 +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.multidex;
-
-import android.support.multidex.ZipUtil.CentralDirectory;
-
-import org.junit.Assert;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-
-/**
- * Tests of ZipUtil class.
- *
- * The test assumes that ANDROID_BUILD_TOP environment variable is defined and point to the top of a
- * built android tree. This is the case when the console used for running the tests is setup for
- * android tree compilation.
- */
-public class ZipUtilTest {
-    private static final File zipFile = new File(System.getenv("ANDROID_BUILD_TOP"),
-        "out/target/common/obj/JAVA_LIBRARIES/android-support-multidex_intermediates/javalib.jar");
-    @BeforeClass
-    public static void setupClass() throws ZipException, IOException {
-        // just verify the zip is valid
-        new ZipFile(zipFile).close();
-    }
-
-    @Test
-    public void testCrcDoNotCrash() throws IOException {
-
-        long crc =
-                ZipUtil.getZipCrc(zipFile);
-        System.out.println("crc is " + crc);
-
-    }
-
-    @Test
-    public void testCrcRange() throws IOException {
-        RandomAccessFile raf = new RandomAccessFile(zipFile, "r");
-        CentralDirectory dir = ZipUtil.findCentralDirectory(raf);
-        byte[] dirData = new byte[(int) dir.size];
-        int length = dirData.length;
-        int off = 0;
-        raf.seek(dir.offset);
-        while (length > 0) {
-            int read = raf.read(dirData, off, length);
-            if (length == -1) {
-                throw new EOFException();
-            }
-            length -= read;
-            off += read;
-        }
-        raf.close();
-        ByteBuffer buffer = ByteBuffer.wrap(dirData);
-        Map<String, ZipEntry> toCheck = new HashMap<String, ZipEntry>();
-        while (buffer.hasRemaining()) {
-            buffer = buffer.slice();
-            buffer.order(ByteOrder.LITTLE_ENDIAN);
-            ZipEntry entry = ZipEntryReader.readEntry(buffer);
-            toCheck.put(entry.getName(), entry);
-        }
-
-        ZipFile zip = new ZipFile(zipFile);
-        Assert.assertEquals(zip.size(), toCheck.size());
-        Enumeration<? extends ZipEntry> ref = zip.entries();
-        while (ref.hasMoreElements()) {
-            ZipEntry refEntry = ref.nextElement();
-            ZipEntry checkEntry = toCheck.get(refEntry.getName());
-            Assert.assertNotNull(checkEntry);
-            Assert.assertEquals(refEntry.getName(), checkEntry.getName());
-            Assert.assertEquals(refEntry.getComment(), checkEntry.getComment());
-            Assert.assertEquals(refEntry.getTime(), checkEntry.getTime());
-            Assert.assertEquals(refEntry.getCrc(), checkEntry.getCrc());
-            Assert.assertEquals(refEntry.getCompressedSize(), checkEntry.getCompressedSize());
-            Assert.assertEquals(refEntry.getSize(), checkEntry.getSize());
-            Assert.assertEquals(refEntry.getMethod(), checkEntry.getMethod());
-            Assert.assertArrayEquals(refEntry.getExtra(), checkEntry.getExtra());
-        }
-        zip.close();
-    }
-
-    @Test
-    public void testCrcValue() throws IOException {
-        ZipFile zip = new ZipFile(zipFile);
-        Enumeration<? extends ZipEntry> ref = zip.entries();
-        byte[] buffer = new byte[0x2000];
-        while (ref.hasMoreElements()) {
-            ZipEntry refEntry = ref.nextElement();
-            if (refEntry.getSize() > 0) {
-                File tmp = File.createTempFile("ZipUtilTest", ".fakezip");
-                InputStream in = zip.getInputStream(refEntry);
-                OutputStream out = new FileOutputStream(tmp);
-                int read = in.read(buffer);
-                while (read != -1) {
-                    out.write(buffer, 0, read);
-                    read = in.read(buffer);
-                }
-                in.close();
-                out.close();
-                RandomAccessFile raf = new RandomAccessFile(tmp, "r");
-                CentralDirectory dir = new CentralDirectory();
-                dir.offset = 0;
-                dir.size = raf.length();
-                long crc = ZipUtil.computeCrcOfCentralDir(raf, dir);
-                Assert.assertEquals(refEntry.getCrc(), crc);
-                raf.close();
-                tmp.delete();
-            }
-        }
-        zip.close();
-    }
-    @Test
-    public void testInvalidCrcValue() throws IOException {
-        ZipFile zip = new ZipFile(zipFile);
-        Enumeration<? extends ZipEntry> ref = zip.entries();
-        byte[] buffer = new byte[0x2000];
-        while (ref.hasMoreElements()) {
-            ZipEntry refEntry = ref.nextElement();
-            if (refEntry.getSize() > 0) {
-                File tmp = File.createTempFile("ZipUtilTest", ".fakezip");
-                InputStream in = zip.getInputStream(refEntry);
-                OutputStream out = new FileOutputStream(tmp);
-                int read = in.read(buffer);
-                while (read != -1) {
-                    out.write(buffer, 0, read);
-                    read = in.read(buffer);
-                }
-                in.close();
-                out.close();
-                RandomAccessFile raf = new RandomAccessFile(tmp, "r");
-                CentralDirectory dir = new CentralDirectory();
-                dir.offset = 0;
-                dir.size = raf.length() - 1;
-                long crc = ZipUtil.computeCrcOfCentralDir(raf, dir);
-                Assert.assertNotEquals(refEntry.getCrc(), crc);
-                raf.close();
-                tmp.delete();
-            }
-        }
-        zip.close();
-    }
-
-}
diff --git a/android/support/percent/PercentFrameLayout.java b/android/support/percent/PercentFrameLayout.java
deleted file mode 100644
index 4190858..0000000
--- a/android/support/percent/PercentFrameLayout.java
+++ /dev/null
@@ -1,227 +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.percent;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RequiresApi;
-import android.util.AttributeSet;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * Subclass of {@link android.widget.FrameLayout} that supports percentage based dimensions and
- * margins.
- *
- * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
- * this example:
- *
- * <pre class="prettyprint">
- * &lt;android.support.percent.PercentFrameLayout
- *         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;ImageView
- *         app:layout_widthPercent="50%"
- *         app:layout_heightPercent="50%"
- *         app:layout_marginTopPercent="25%"
- *         app:layout_marginLeftPercent="25%"/&gt
- * &lt;/android.support.percent.PercentFrameLayout&gt
- * </pre>
- *
- * The attributes that you can use are:
- * <ul>
- *     <li>{@code layout_widthPercent}
- *     <li>{@code layout_heightPercent}
- *     <li>{@code layout_marginPercent}
- *     <li>{@code layout_marginLeftPercent}
- *     <li>{@code layout_marginTopPercent}
- *     <li>{@code layout_marginRightPercent}
- *     <li>{@code layout_marginBottomPercent}
- *     <li>{@code layout_marginStartPercent}
- *     <li>{@code layout_marginEndPercent}
- *     <li>{@code layout_aspectRatio}
- * </ul>
- *
- * It is not necessary to specify {@code layout_width/height} if you specify {@code
- * layout_widthPercent.} However, if you want the view to be able to take up more space than what
- * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
- * if the percentage size is too small for the View's content, it will be resized using
- * {@code wrap_content} rule.
- *
- * <p>
- * You can also make one dimension be a fraction of the other by setting only width or height and
- * using {@code layout_aspectRatio} for the second one to be calculated automatically. For
- * example, if you would like to achieve 16:9 aspect ratio, you can write:
- * <pre class="prettyprint">
- *     android:layout_width="300dp"
- *     app:layout_aspectRatio="178%"
- * </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
- * </pre>
- */
-@Deprecated
-public class PercentFrameLayout extends FrameLayout {
-    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
-
-    public PercentFrameLayout(Context context) {
-        super(context);
-    }
-
-    public PercentFrameLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mHelper.handleMeasuredStateTooSmall()) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        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;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(int width, int height, int gravity) {
-            super(width, height, gravity);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(MarginLayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(FrameLayout.LayoutParams source) {
-            super((MarginLayoutParams) source);
-            gravity = source.gravity;
-        }
-
-        @RequiresApi(19)
-        public LayoutParams(LayoutParams source) {
-            // The copy constructor used here is only supported on API 19+.
-            this((FrameLayout.LayoutParams) source);
-            mPercentLayoutInfo = source.mPercentLayoutInfo;
-        }
-
-        @Override
-        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
-            if (mPercentLayoutInfo == null) {
-                mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo();
-            }
-
-            return mPercentLayoutInfo;
-        }
-
-        @Override
-        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
-            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
-        }
-    }
-}
diff --git a/android/support/percent/PercentLayoutHelper.java b/android/support/percent/PercentLayoutHelper.java
deleted file mode 100644
index 44a76c7..0000000
--- a/android/support/percent/PercentLayoutHelper.java
+++ /dev/null
@@ -1,620 +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.percent;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.NonNull;
-import android.support.v4.view.MarginLayoutParamsCompat;
-import android.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Helper for layouts that want to support percentage based dimensions.
- *
- * <p>This class collects utility methods that are involved in extracting percentage based dimension
- * attributes and applying them to ViewGroup's children. If you would like to implement a layout
- * that supports percentage based dimensions, you need to take several steps:
- *
- * <ol>
- * <li> You need a {@link ViewGroup.LayoutParams} subclass in your ViewGroup that implements
- * {@link android.support.percent.PercentLayoutHelper.PercentLayoutParams}.
- * <li> In your {@code LayoutParams(Context c, AttributeSet attrs)} constructor create an instance
- * of {@link PercentLayoutHelper.PercentLayoutInfo} by calling
- * {@link PercentLayoutHelper#getPercentLayoutInfo(Context, AttributeSet)}. Return this
- * object from {@code public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()}
- * method that you implemented for {@link android.support.percent.PercentLayoutHelper.PercentLayoutParams} interface.
- * <li> Override
- * {@link ViewGroup.LayoutParams#setBaseAttributes(TypedArray, int, int)}
- * with a single line implementation {@code PercentLayoutHelper.fetchWidthAndHeight(this, a,
- * widthAttr, heightAttr);}
- * <li> In your ViewGroup override {@link ViewGroup#generateLayoutParams(AttributeSet)} to return
- * your LayoutParams.
- * <li> In your {@link ViewGroup#onMeasure(int, int)} override, you need to implement following
- * pattern:
- * <pre class="prettyprint">
- * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- *     mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
- *     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- *     if (mHelper.handleMeasuredStateTooSmall()) {
- *         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- *     }
- * }
- * </pre>
- * <li>In your {@link ViewGroup#onLayout(boolean, int, int, int, int)} override, you need to
- * implement following pattern:
- * <pre class="prettyprint">
- * protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- *     super.onLayout(changed, left, top, right, bottom);
- *     mHelper.restoreOriginalParams();
- * }
- * </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";
-
-    private static final boolean DEBUG = false;
-    private static final boolean VERBOSE = false;
-
-    private final ViewGroup mHost;
-
-    public PercentLayoutHelper(@NonNull ViewGroup host) {
-        if (host == null) {
-            throw new IllegalArgumentException("host must be non-null");
-        }
-        mHost = host;
-    }
-
-    /**
-     * Helper method to be called from {@link ViewGroup.LayoutParams#setBaseAttributes} override
-     * that reads layout_width and layout_height attribute values without throwing an exception if
-     * they aren't present.
-     */
-    public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array,
-            int widthAttr, int heightAttr) {
-        params.width = array.getLayoutDimension(widthAttr, 0);
-        params.height = array.getLayoutDimension(heightAttr, 0);
-    }
-
-    /**
-     * Iterates over children and changes their width and height to one calculated from percentage
-     * values.
-     * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
-     * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
-     */
-    public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
-        if (DEBUG) {
-            Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
-                    + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
-                    + View.MeasureSpec.toString(heightMeasureSpec));
-        }
-
-        // Calculate available space, accounting for host's paddings
-        int widthHint = View.MeasureSpec.getSize(widthMeasureSpec) - mHost.getPaddingLeft()
-                - mHost.getPaddingRight();
-        int heightHint = View.MeasureSpec.getSize(heightMeasureSpec) - mHost.getPaddingTop()
-                - mHost.getPaddingBottom();
-        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
-            View view = mHost.getChildAt(i);
-            ViewGroup.LayoutParams params = view.getLayoutParams();
-            if (DEBUG) {
-                Log.d(TAG, "should adjust " + view + " " + params);
-            }
-            if (params instanceof PercentLayoutParams) {
-                PercentLayoutInfo info =
-                        ((PercentLayoutParams) params).getPercentLayoutInfo();
-                if (DEBUG) {
-                    Log.d(TAG, "using " + info);
-                }
-                if (info != null) {
-                    if (params instanceof ViewGroup.MarginLayoutParams) {
-                        info.fillMarginLayoutParams(view, (ViewGroup.MarginLayoutParams) params,
-                                widthHint, heightHint);
-                    } else {
-                        info.fillLayoutParams(params, widthHint, heightHint);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Constructs a PercentLayoutInfo from attributes associated with a View. Call this method from
-     * {@code LayoutParams(Context c, AttributeSet attrs)} constructor.
-     */
-    public static PercentLayoutInfo getPercentLayoutInfo(Context context,
-            AttributeSet attrs) {
-        PercentLayoutInfo info = null;
-        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
-        float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent width: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.widthPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent height: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.heightPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.leftMarginPercent = value;
-            info.topMarginPercent = value;
-            info.rightMarginPercent = value;
-            info.bottomMarginPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent left margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.leftMarginPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent top margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.topMarginPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent right margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.rightMarginPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent bottom margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.bottomMarginPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent start margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.startMarginPercent = value;
-        }
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
-                -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "percent end margin: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.endMarginPercent = value;
-        }
-
-        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_aspectRatio, 1, 1, -1f);
-        if (value != -1f) {
-            if (VERBOSE) {
-                Log.v(TAG, "aspect ratio: " + value);
-            }
-            info = info != null ? info : new PercentLayoutInfo();
-            info.aspectRatio = value;
-        }
-
-        array.recycle();
-        if (DEBUG) {
-            Log.d(TAG, "constructed: " + info);
-        }
-        return info;
-    }
-
-    /**
-     * Iterates over children and restores their original dimensions that were changed for
-     * percentage values. Calling this method only makes sense if you previously called
-     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
-     */
-    public void restoreOriginalParams() {
-        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
-            View view = mHost.getChildAt(i);
-            ViewGroup.LayoutParams params = view.getLayoutParams();
-            if (DEBUG) {
-                Log.d(TAG, "should restore " + view + " " + params);
-            }
-            if (params instanceof PercentLayoutParams) {
-                PercentLayoutInfo info =
-                        ((PercentLayoutParams) params).getPercentLayoutInfo();
-                if (DEBUG) {
-                    Log.d(TAG, "using " + info);
-                }
-                if (info != null) {
-                    if (params instanceof ViewGroup.MarginLayoutParams) {
-                        info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
-                    } else {
-                        info.restoreLayoutParams(params);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Iterates over children and checks if any of them would like to get more space than it
-     * received through the percentage dimension.
-     *
-     * If you are building a layout that supports percentage dimensions you are encouraged to take
-     * advantage of this method. The developer should be able to specify that a child should be
-     * remeasured by adding normal dimension attribute with {@code wrap_content} value. For example
-     * he might specify child's attributes as {@code app:layout_widthPercent="60%p"} and
-     * {@code android:layout_width="wrap_content"}. In this case if the child receives too little
-     * space, it will be remeasured with width set to {@code WRAP_CONTENT}.
-     *
-     * @return True if the measure phase needs to be rerun because one of the children would like
-     * to receive more space.
-     */
-    public boolean handleMeasuredStateTooSmall() {
-        boolean needsSecondMeasure = false;
-        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
-            View view = mHost.getChildAt(i);
-            ViewGroup.LayoutParams params = view.getLayoutParams();
-            if (DEBUG) {
-                Log.d(TAG, "should handle measured state too small " + view + " " + params);
-            }
-            if (params instanceof PercentLayoutParams) {
-                PercentLayoutInfo info =
-                        ((PercentLayoutParams) params).getPercentLayoutInfo();
-                if (info != null) {
-                    if (shouldHandleMeasuredWidthTooSmall(view, info)) {
-                        needsSecondMeasure = true;
-                        params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
-                    }
-                    if (shouldHandleMeasuredHeightTooSmall(view, info)) {
-                        needsSecondMeasure = true;
-                        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-                    }
-                }
-            }
-        }
-        if (DEBUG) {
-            Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
-        }
-        return needsSecondMeasure;
-    }
-
-    private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
-        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 = 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 {
-        // These two flags keep track of whether we're computing the LayoutParams width and height
-        // in the fill pass based on the aspect ratio. This allows the fill pass to be re-entrant
-        // as the framework code can call onMeasure() multiple times before the onLayout() is
-        // called. Those multiple invocations of onMeasure() are not guaranteed to be called with
-        // the same set of width / height.
-        private boolean mIsHeightComputedFromAspectRatio;
-        private boolean mIsWidthComputedFromAspectRatio;
-
-        public PercentMarginLayoutParams(int width, int height) {
-            super(width, height);
-        }
-    }
-
-    /**
-     * 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;
-
-        /** The decimal value of the percentage-based height. */
-        public float heightPercent;
-
-        /** The decimal value of the percentage-based left margin. */
-        public float leftMarginPercent;
-
-        /** The decimal value of the percentage-based top margin. */
-        public float topMarginPercent;
-
-        /** The decimal value of the percentage-based right margin. */
-        public float rightMarginPercent;
-
-        /** The decimal value of the percentage-based bottom margin. */
-        public float bottomMarginPercent;
-
-        /** The decimal value of the percentage-based start margin. */
-        public float startMarginPercent;
-
-        /** The decimal value of the percentage-based end margin. */
-        public float endMarginPercent;
-
-        /** The decimal value of the percentage-based aspect ratio. */
-        public float aspectRatio;
-
-        /* package */ final PercentMarginLayoutParams mPreservedParams;
-
-        public PercentLayoutInfo() {
-            widthPercent = -1f;
-            heightPercent = -1f;
-            leftMarginPercent = -1f;
-            topMarginPercent = -1f;
-            rightMarginPercent = -1f;
-            bottomMarginPercent = -1f;
-            startMarginPercent = -1f;
-            endMarginPercent = -1f;
-            mPreservedParams = new PercentMarginLayoutParams(0, 0);
-        }
-
-        /**
-         * Fills the {@link ViewGroup.LayoutParams#width} and {@link ViewGroup.LayoutParams#height}
-         * fields of the passed {@link ViewGroup.LayoutParams} object based on currently set
-         * percentage values.
-         */
-        public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
-                int heightHint) {
-            // Preserve the original layout params, so we can restore them after the measure step.
-            mPreservedParams.width = params.width;
-            mPreservedParams.height = params.height;
-
-            // We assume that width/height set to 0 means that value was unset. This might not
-            // necessarily be true, as the user might explicitly set it to 0. However, we use this
-            // information only for the aspect ratio. If the user set the aspect ratio attribute,
-            // it means they accept or soon discover that it will be disregarded.
-            final boolean widthNotSet =
-                    (mPreservedParams.mIsWidthComputedFromAspectRatio
-                            || mPreservedParams.width == 0) && (widthPercent < 0);
-            final boolean heightNotSet =
-                    (mPreservedParams.mIsHeightComputedFromAspectRatio
-                            || mPreservedParams.height == 0) && (heightPercent < 0);
-
-            if (widthPercent >= 0) {
-                params.width = Math.round(widthHint * widthPercent);
-            }
-
-            if (heightPercent >= 0) {
-                params.height = Math.round(heightHint * heightPercent);
-            }
-
-            if (aspectRatio >= 0) {
-                if (widthNotSet) {
-                    params.width = Math.round(params.height * aspectRatio);
-                    // Keep track that we've filled the width based on the height and aspect ratio.
-                    mPreservedParams.mIsWidthComputedFromAspectRatio = true;
-                }
-                if (heightNotSet) {
-                    params.height = Math.round(params.width / aspectRatio);
-                    // Keep track that we've filled the height based on the width and aspect ratio.
-                    mPreservedParams.mIsHeightComputedFromAspectRatio = true;
-                }
-            }
-
-            if (DEBUG) {
-                Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
-            }
-        }
-
-        /**
-         * @deprecated Use
-         * {@link #fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)}
-         * for proper RTL support.
-         */
-        @Deprecated
-        public void fillMarginLayoutParams(ViewGroup.MarginLayoutParams params,
-                int widthHint, int heightHint) {
-            fillMarginLayoutParams(null, params, widthHint, heightHint);
-        }
-
-        /**
-         * Fills the margin fields of the passed {@link ViewGroup.MarginLayoutParams} object based
-         * on currently set percentage values and the current layout direction of the passed
-         * {@link View}.
-         */
-        public void fillMarginLayoutParams(View view, ViewGroup.MarginLayoutParams params,
-                int widthHint, int heightHint) {
-            fillLayoutParams(params, widthHint, heightHint);
-
-            // Preserve the original margins, so we can restore them after the measure step.
-            mPreservedParams.leftMargin = params.leftMargin;
-            mPreservedParams.topMargin = params.topMargin;
-            mPreservedParams.rightMargin = params.rightMargin;
-            mPreservedParams.bottomMargin = params.bottomMargin;
-            MarginLayoutParamsCompat.setMarginStart(mPreservedParams,
-                    MarginLayoutParamsCompat.getMarginStart(params));
-            MarginLayoutParamsCompat.setMarginEnd(mPreservedParams,
-                    MarginLayoutParamsCompat.getMarginEnd(params));
-
-            if (leftMarginPercent >= 0) {
-                params.leftMargin = Math.round(widthHint * leftMarginPercent);
-            }
-            if (topMarginPercent >= 0) {
-                params.topMargin = Math.round(heightHint * topMarginPercent);
-            }
-            if (rightMarginPercent >= 0) {
-                params.rightMargin = Math.round(widthHint * rightMarginPercent);
-            }
-            if (bottomMarginPercent >= 0) {
-                params.bottomMargin = Math.round(heightHint * bottomMarginPercent);
-            }
-            boolean shouldResolveLayoutDirection = false;
-            if (startMarginPercent >= 0) {
-                MarginLayoutParamsCompat.setMarginStart(params,
-                        Math.round(widthHint * startMarginPercent));
-                shouldResolveLayoutDirection = true;
-            }
-            if (endMarginPercent >= 0) {
-                MarginLayoutParamsCompat.setMarginEnd(params,
-                        Math.round(widthHint * endMarginPercent));
-                shouldResolveLayoutDirection = true;
-            }
-            if (shouldResolveLayoutDirection && (view != null)) {
-                // Force the resolve pass so that start / end margins are propagated to the
-                // matching left / right fields
-                MarginLayoutParamsCompat.resolveLayoutDirection(params,
-                        ViewCompat.getLayoutDirection(view));
-            }
-            if (DEBUG) {
-                Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
-                        + ")");
-            }
-        }
-
-        @Override
-        public String toString() {
-            return String.format("PercentLayoutInformation width: %f height %f, margins (%f, %f, "
-                            + " %f, %f, %f, %f)", widthPercent, heightPercent, leftMarginPercent,
-                    topMarginPercent, rightMarginPercent, bottomMarginPercent, startMarginPercent,
-                    endMarginPercent);
-
-        }
-
-        /**
-         * Restores the original dimensions and margins after they were changed for percentage based
-         * values. You should call this method only if you previously called
-         * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)}.
-         */
-        public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) {
-            restoreLayoutParams(params);
-            params.leftMargin = mPreservedParams.leftMargin;
-            params.topMargin = mPreservedParams.topMargin;
-            params.rightMargin = mPreservedParams.rightMargin;
-            params.bottomMargin = mPreservedParams.bottomMargin;
-            MarginLayoutParamsCompat.setMarginStart(params,
-                    MarginLayoutParamsCompat.getMarginStart(mPreservedParams));
-            MarginLayoutParamsCompat.setMarginEnd(params,
-                    MarginLayoutParamsCompat.getMarginEnd(mPreservedParams));
-        }
-
-        /**
-         * Restores original dimensions after they were changed for percentage based values.
-         * You should call this method only if you previously called
-         * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams(ViewGroup.LayoutParams, int, int)}.
-         */
-        public void restoreLayoutParams(ViewGroup.LayoutParams params) {
-            if (!mPreservedParams.mIsWidthComputedFromAspectRatio) {
-                // Only restore the width if we didn't compute it based on the height and
-                // aspect ratio in the fill pass.
-                params.width = mPreservedParams.width;
-            }
-            if (!mPreservedParams.mIsHeightComputedFromAspectRatio) {
-                // Only restore the height if we didn't compute it based on the width and
-                // aspect ratio in the fill pass.
-                params.height = mPreservedParams.height;
-            }
-
-            // Reset the tracking flags.
-            mPreservedParams.mIsWidthComputedFromAspectRatio = false;
-            mPreservedParams.mIsHeightComputedFromAspectRatio = false;
-        }
-    }
-
-    /**
-     * If a layout wants to support percentage based dimensions and use this helper class, its
-     * {@code LayoutParams} subclass must implement this interface.
-     *
-     * 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/android/support/percent/PercentRelativeLayout.java b/android/support/percent/PercentRelativeLayout.java
deleted file mode 100644
index 5b10349..0000000
--- a/android/support/percent/PercentRelativeLayout.java
+++ /dev/null
@@ -1,209 +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.percent;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-
-/**
- * Subclass of {@link android.widget.RelativeLayout} that supports percentage based dimensions and
- * margins.
- *
- * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
- * this example:
- *
- * <pre class="prettyprint">
- * &lt;android.support.percent.PercentRelativeLayout
- *         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;ImageView
- *         app:layout_widthPercent="50%"
- *         app:layout_heightPercent="50%"
- *         app:layout_marginTopPercent="25%"
- *         app:layout_marginLeftPercent="25%"/&gt
- * &lt;/android.support.percent.PercentRelativeLayout&gt
- * </pre>
- *
- * The attributes that you can use are:
- * <ul>
- *     <li>{@code layout_widthPercent}
- *     <li>{@code layout_heightPercent}
- *     <li>{@code layout_marginPercent}
- *     <li>{@code layout_marginLeftPercent}
- *     <li>{@code layout_marginTopPercent}
- *     <li>{@code layout_marginRightPercent}
- *     <li>{@code layout_marginBottomPercent}
- *     <li>{@code layout_marginStartPercent}
- *     <li>{@code layout_marginEndPercent}
- *     <li>{@code layout_aspectRatio}
- * </ul>
- *
- * It is not necessary to specify {@code layout_width/height} if you specify {@code
- * layout_widthPercent.} However, if you want the view to be able to take up more space than what
- * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
- * if the percentage size is too small for the View's content, it will be resized using
- * {@code wrap_content} rule.
- *
- * <p>
- * You can also make one dimension be a fraction of the other by setting only width or height and
- * using {@code layout_aspectRatio} for the second one to be calculated automatically. For
- * example, if you would like to achieve 16:9 aspect ratio, you can write:
- * <pre class="prettyprint">
- *     android:layout_width="300dp"
- *     app:layout_aspectRatio="178%"
- * </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);
-
-    public PercentRelativeLayout(Context context) {
-        super(context);
-    }
-
-    public PercentRelativeLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mHelper.handleMeasuredStateTooSmall()) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        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;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(MarginLayoutParams source) {
-            super(source);
-        }
-
-        @Override
-        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
-            if (mPercentLayoutInfo == null) {
-                mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo();
-            }
-
-            return mPercentLayoutInfo;
-        }
-
-        @Override
-        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
-            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
-        }
-    }
-}
diff --git a/android/support/testutils/AppCompatActivityUtils.java b/android/support/testutils/AppCompatActivityUtils.java
deleted file mode 100644
index 49ccc1b..0000000
--- a/android/support/testutils/AppCompatActivityUtils.java
+++ /dev/null
@@ -1,95 +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.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 AppCompat activities.
- */
-public class AppCompatActivityUtils {
-    private static final Runnable DO_NOTHING = new Runnable() {
-        @Override
-        public void run() {
-        }
-    };
-
-    /**
-     * Waits for the execution of the provided activity test rule.
-     *
-     * @param rule Activity test rule to wait for.
-     */
-    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/android/support/testutils/FragmentActivityUtils.java b/android/support/testutils/FragmentActivityUtils.java
deleted file mode 100644
index 7d12deb..0000000
--- a/android/support/testutils/FragmentActivityUtils.java
+++ /dev/null
@@ -1,91 +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.testutils;
-
-import static org.junit.Assert.assertTrue;
-
-import android.app.Activity;
-import android.os.Looper;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v4.app.FragmentActivity;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility methods for testing fragment activities.
- */
-public class FragmentActivityUtils {
-    private static final Runnable DO_NOTHING = new Runnable() {
-        @Override
-        public void run() {
-        }
-    };
-
-    private static void waitForExecution(final ActivityTestRule<? extends FragmentActivity> 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 Activity> rule,
-            Runnable r) {
-        if (Looper.getMainLooper() == Looper.myLooper()) {
-            r.run();
-        } else {
-            try {
-                rule.runOnUiThread(r);
-            } catch (Throwable t) {
-                throw new RuntimeException(t);
-            }
-        }
-    }
-
-    /**
-     * 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/android/support/testutils/PollingCheck.java b/android/support/testutils/PollingCheck.java
deleted file mode 100644
index 8e85896..0000000
--- a/android/support/testutils/PollingCheck.java
+++ /dev/null
@@ -1,97 +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.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/android/support/testutils/RecreatedActivity.java b/android/support/testutils/RecreatedActivity.java
deleted file mode 100644
index aaea3a9..0000000
--- a/android/support/testutils/RecreatedActivity.java
+++ /dev/null
@@ -1,64 +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.testutils;
-
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v4.app.FragmentActivity;
-
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Extension of {@link FragmentActivity} that keeps track of when it is recreated.
- * In order to use this class, have your activity extend it and call
- * {@link FragmentActivityUtils#recreateActivity(ActivityTestRule, RecreatedActivity)} API.
- */
-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;
-
-    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/android/support/testutils/RecreatedAppCompatActivity.java b/android/support/testutils/RecreatedAppCompatActivity.java
deleted file mode 100644
index d5645a3..0000000
--- a/android/support/testutils/RecreatedAppCompatActivity.java
+++ /dev/null
@@ -1,65 +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.testutils;
-
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.test.rule.ActivityTestRule;
-import android.support.v7.app.AppCompatActivity;
-
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Extension of {@link AppCompatActivity} that keeps track of when it is recreated.
- * In order to use this class, have your activity extend it and call
- * {@link AppCompatActivityUtils#recreateActivity(ActivityTestRule, RecreatedAppCompatActivity)}
- * API.
- */
-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;
-
-    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/android/support/text/emoji/EmojiCompat.java b/android/support/text/emoji/EmojiCompat.java
deleted file mode 100644
index 413a9dd..0000000
--- a/android/support/text/emoji/EmojiCompat.java
+++ /dev/null
@@ -1,1128 +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.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#setUseEmojiAsDefaultStyle(boolean)
-     */
-    private final boolean mUseEmojiAsDefaultStyle;
-
-    /**
-     * @see Config#setUseEmojiAsDefaultStyle(boolean, List)
-     */
-    private final int[] mEmojiAsDefaultStyleExceptions;
-
-    /**
-     * @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;
-        mUseEmojiAsDefaultStyle = config.mUseEmojiAsDefaultStyle;
-        mEmojiAsDefaultStyleExceptions = config.mEmojiAsDefaultStyleExceptions;
-        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
-     */
-    @SuppressWarnings("GuardedBy")
-    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
-     */
-    @SuppressWarnings("GuardedBy")
-    @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
-     */
-    @SuppressWarnings("GuardedBy")
-    @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 boolean mUseEmojiAsDefaultStyle;
-        private int[] mEmojiAsDefaultStyleExceptions;
-        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 EmojiCompat should use the emoji presentation style for emojis
-         * that have text style as default. By default, the text style would be used, unless these
-         * are followed by the U+FE0F variation selector.
-         * Details about emoji presentation and text presentation styles can be found here:
-         * http://unicode.org/reports/tr51/#Presentation_Style
-         * If useEmojiAsDefaultStyle is true, the emoji presentation style will be used for all
-         * emojis, including potentially unexpected ones (such as digits or other keycap emojis). If
-         * this is not the expected behaviour, method
-         * {@link #setUseEmojiAsDefaultStyle(boolean, List)} can be used to specify the
-         * exception emojis that should be still presented as text style.
-         *
-         * @param useEmojiAsDefaultStyle whether to use the emoji style presentation for all emojis
-         *                               that would be presented as text style by default
-         */
-        public Config setUseEmojiAsDefaultStyle(final boolean useEmojiAsDefaultStyle) {
-            return setUseEmojiAsDefaultStyle(useEmojiAsDefaultStyle, null);
-        }
-
-        /**
-         * @see #setUseEmojiAsDefaultStyle(boolean)
-         *
-         * @param emojiAsDefaultStyleExceptions Contains the exception emojis which will be still
-         *                                      presented as text style even if the
-         *                                      useEmojiAsDefaultStyle flag is set to {@code true}.
-         *                                      This list will be ignored if useEmojiAsDefaultStyle
-         *                                      is {@code false}. Note that emojis with default
-         *                                      emoji style presentation will remain emoji style
-         *                                      regardless the value of useEmojiAsDefaultStyle or
-         *                                      whether they are included in the exceptions list or
-         *                                      not. When no exception is wanted, the method
-         *                                      {@link #setUseEmojiAsDefaultStyle(boolean)} should
-         *                                      be used instead.
-         */
-        public Config setUseEmojiAsDefaultStyle(final boolean useEmojiAsDefaultStyle,
-                @Nullable final List<Integer> emojiAsDefaultStyleExceptions) {
-            mUseEmojiAsDefaultStyle = useEmojiAsDefaultStyle;
-            if (mUseEmojiAsDefaultStyle && emojiAsDefaultStyleExceptions != null) {
-                mEmojiAsDefaultStyleExceptions = new int[emojiAsDefaultStyleExceptions.size()];
-                int i = 0;
-                for (Integer exception : emojiAsDefaultStyleExceptions) {
-                    mEmojiAsDefaultStyleExceptions[i++] = exception;
-                }
-                Arrays.sort(mEmojiAsDefaultStyleExceptions);
-            } else {
-                mEmojiAsDefaultStyleExceptions = null;
-            }
-            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.mUseEmojiAsDefaultStyle,
-                    mEmojiCompat.mEmojiAsDefaultStyleExceptions);
-
-            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/android/support/text/emoji/EmojiMetadata.java b/android/support/text/emoji/EmojiMetadata.java
deleted file mode 100644
index 7d495ed..0000000
--- a/android/support/text/emoji/EmojiMetadata.java
+++ /dev/null
@@ -1,235 +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.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 java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import androidx.text.emoji.flatbuffer.MetadataItem;
-import androidx.text.emoji.flatbuffer.MetadataList;
-
-/**
- * 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 return typeface to be used to render this metadata
-     */
-    public Typeface getTypeface() {
-        return mMetadataRepo.getTypeface();
-    }
-
-    /**
-     * @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();
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder builder = new StringBuilder();
-        builder.append(super.toString());
-        builder.append(", id:");
-        builder.append(Integer.toHexString(getId()));
-        builder.append(", codepoints:");
-        final int codepointsLength = getCodepointsLength();
-        for (int i = 0; i < codepointsLength; i++) {
-            builder.append(Integer.toHexString(getCodepointAt(i)));
-            builder.append(" ");
-        }
-        return builder.toString();
-    }
-}
diff --git a/android/support/text/emoji/EmojiProcessor.java b/android/support/text/emoji/EmojiProcessor.java
deleted file mode 100644
index f711704..0000000
--- a/android/support/text/emoji/EmojiProcessor.java
+++ /dev/null
@@ -1,830 +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.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.Nullable;
-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;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * 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();
-
-    /**
-     * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean)
-     */
-    private final boolean mUseEmojiAsDefaultStyle;
-
-    /**
-     * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean, List)
-     */
-    private final int[] mEmojiAsDefaultStyleExceptions;
-
-    EmojiProcessor(@NonNull final MetadataRepo metadataRepo,
-            @NonNull final EmojiCompat.SpanFactory spanFactory,
-            final boolean useEmojiAsDefaultStyle,
-            @Nullable final int[] emojiAsDefaultStyleExceptions) {
-        mSpanFactory = spanFactory;
-        mMetadataRepo = metadataRepo;
-        mUseEmojiAsDefaultStyle = useEmojiAsDefaultStyle;
-        mEmojiAsDefaultStyleExceptions = emojiAsDefaultStyleExceptions;
-    }
-
-    EmojiMetadata getEmojiMetadata(@NonNull final CharSequence charSequence) {
-        final ProcessorSm sm = new ProcessorSm(mMetadataRepo.getRootNode(),
-                mUseEmojiAsDefaultStyle, mEmojiAsDefaultStyleExceptions);
-        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(),
-                    mUseEmojiAsDefaultStyle, mEmojiAsDefaultStyleExceptions);
-
-            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:
-                        start += Character.charCount(Character.codePointAt(charSequence, start));
-                        currentOffset = start;
-                        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;
-
-        /**
-         * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean)
-         */
-        private final boolean mUseEmojiAsDefaultStyle;
-
-        /**
-         * @see EmojiCompat.Config#setUseEmojiAsDefaultStyle(boolean, List)
-         */
-        private final int[] mEmojiAsDefaultStyleExceptions;
-
-        ProcessorSm(MetadataRepo.Node rootNode, boolean useEmojiAsDefaultStyle,
-                int[] emojiAsDefaultStyleExceptions) {
-            mRootNode = rootNode;
-            mCurrentNode = rootNode;
-            mUseEmojiAsDefaultStyle = useEmojiAsDefaultStyle;
-            mEmojiAsDefaultStyleExceptions = emojiAsDefaultStyleExceptions;
-        }
-
-        @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 (shouldUseEmojiPresentationStyleForSingleCodepoint()) {
-                                    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
-                    && (mCurrentDepth > 1 || shouldUseEmojiPresentationStyleForSingleCodepoint());
-        }
-
-        private boolean shouldUseEmojiPresentationStyleForSingleCodepoint() {
-            if (mCurrentNode.getData().isDefaultEmoji()) {
-                // The codepoint is emoji style by default.
-                return true;
-            }
-            if (isEmojiStyle(mLastCodepoint)) {
-                // The codepoint was followed by the emoji style variation selector.
-                return true;
-            }
-            if (mUseEmojiAsDefaultStyle) {
-                // Emoji presentation style for text style default emojis is enabled. We have
-                // to check that the current codepoint is not an exception.
-                if (mEmojiAsDefaultStyleExceptions == null) {
-                    return true;
-                }
-                final int codepoint = mCurrentNode.getData().getCodepointAt(0);
-                final int index = Arrays.binarySearch(mEmojiAsDefaultStyleExceptions, codepoint);
-                if (index < 0) {
-                    // Index is negative, so the codepoint was not found in the array of exceptions.
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * @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/android/support/text/emoji/EmojiSpan.java b/android/support/text/emoji/EmojiSpan.java
deleted file mode 100644
index 1e76c78..0000000
--- a/android/support/text/emoji/EmojiSpan.java
+++ /dev/null
@@ -1,141 +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.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/android/support/text/emoji/FontRequestEmojiCompatConfig.java b/android/support/text/emoji/FontRequestEmojiCompatConfig.java
deleted file mode 100644
index 05f367c..0000000
--- a/android/support/text/emoji/FontRequestEmojiCompatConfig.java
+++ /dev/null
@@ -1,360 +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.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/android/support/text/emoji/MetadataListReader.java b/android/support/text/emoji/MetadataListReader.java
deleted file mode 100644
index 02856cb..0000000
--- a/android/support/text/emoji/MetadataListReader.java
+++ /dev/null
@@ -1,344 +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.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 java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-import androidx.text.emoji.flatbuffer.MetadataList;
-
-/**
- * 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) {
-                int skipped = (int) 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/android/support/text/emoji/MetadataRepo.java b/android/support/text/emoji/MetadataRepo.java
deleted file mode 100644
index f5afec8..0000000
--- a/android/support/text/emoji/MetadataRepo.java
+++ /dev/null
@@ -1,244 +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.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.v4.util.Preconditions;
-import android.util.SparseArray;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-import androidx.text.emoji.flatbuffer.MetadataList;
-
-/**
- * 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/android/support/text/emoji/TypefaceEmojiSpan.java b/android/support/text/emoji/TypefaceEmojiSpan.java
deleted file mode 100644
index f64330d..0000000
--- a/android/support/text/emoji/TypefaceEmojiSpan.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.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/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java b/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
deleted file mode 100644
index 97ea363..0000000
--- a/android/support/text/emoji/bundled/BundledEmojiCompatConfig.java
+++ /dev/null
@@ -1,87 +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.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/android/support/text/emoji/widget/EditTextAttributeHelper.java b/android/support/text/emoji/widget/EditTextAttributeHelper.java
deleted file mode 100644
index 381a02c..0000000
--- a/android/support/text/emoji/widget/EditTextAttributeHelper.java
+++ /dev/null
@@ -1,53 +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.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/android/support/text/emoji/widget/EmojiAppCompatButton.java b/android/support/text/emoji/widget/EmojiAppCompatButton.java
deleted file mode 100644
index b9e6df0..0000000
--- a/android/support/text/emoji/widget/EmojiAppCompatButton.java
+++ /dev/null
@@ -1,76 +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.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/android/support/text/emoji/widget/EmojiAppCompatEditText.java b/android/support/text/emoji/widget/EmojiAppCompatEditText.java
deleted file mode 100644
index 0ae4ea0..0000000
--- a/android/support/text/emoji/widget/EmojiAppCompatEditText.java
+++ /dev/null
@@ -1,118 +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.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.text.method.KeyListener;
-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(@Nullable KeyListener keyListener) {
-        if (keyListener != null) {
-            keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
-        }
-        super.setKeyListener(keyListener);
-    }
-
-    @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/android/support/text/emoji/widget/EmojiAppCompatTextView.java b/android/support/text/emoji/widget/EmojiAppCompatTextView.java
deleted file mode 100644
index 262cc07..0000000
--- a/android/support/text/emoji/widget/EmojiAppCompatTextView.java
+++ /dev/null
@@ -1,77 +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.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/android/support/text/emoji/widget/EmojiButton.java b/android/support/text/emoji/widget/EmojiButton.java
deleted file mode 100644
index 752e052..0000000
--- a/android/support/text/emoji/widget/EmojiButton.java
+++ /dev/null
@@ -1,83 +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.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.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();
-    }
-
-    @RequiresApi(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/android/support/text/emoji/widget/EmojiEditText.java b/android/support/text/emoji/widget/EmojiEditText.java
deleted file mode 100644
index e1057e8..0000000
--- a/android/support/text/emoji/widget/EmojiEditText.java
+++ /dev/null
@@ -1,124 +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.text.emoji.widget;
-
-import android.content.Context;
-import android.os.Build;
-import android.support.annotation.IntRange;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.text.emoji.EmojiCompat;
-import android.text.method.KeyListener;
-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*/);
-    }
-
-    @RequiresApi(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(@Nullable KeyListener keyListener) {
-        if (keyListener != null) {
-            keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
-        }
-        super.setKeyListener(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/android/support/text/emoji/widget/EmojiEditTextHelper.java b/android/support/text/emoji/widget/EmojiEditTextHelper.java
deleted file mode 100644
index a999e34..0000000
--- a/android/support/text/emoji/widget/EmojiEditTextHelper.java
+++ /dev/null
@@ -1,239 +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.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.Nullable;
-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. If
-     * {@code inputConnection} is {@code null}, returns {@code null}.
-     *
-     * @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}
-     */
-    @Nullable
-    public InputConnection onCreateInputConnection(@Nullable final InputConnection inputConnection,
-            @NonNull final EditorInfo outAttrs) {
-        if (inputConnection == null) return 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/android/support/text/emoji/widget/EmojiEditableFactory.java b/android/support/text/emoji/widget/EmojiEditableFactory.java
deleted file mode 100644
index 20cde4f..0000000
--- a/android/support/text/emoji/widget/EmojiEditableFactory.java
+++ /dev/null
@@ -1,77 +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.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
-        }
-    }
-
-    @SuppressWarnings("GuardedBy")
-    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/android/support/text/emoji/widget/EmojiExtractEditText.java b/android/support/text/emoji/widget/EmojiExtractEditText.java
deleted file mode 100644
index 1d5e2dd..0000000
--- a/android/support/text/emoji/widget/EmojiExtractEditText.java
+++ /dev/null
@@ -1,151 +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.text.emoji.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.text.emoji.EmojiCompat;
-import android.support.text.emoji.EmojiSpan;
-import android.text.method.KeyListener;
-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*/);
-    }
-
-    @RequiresApi(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(@Nullable KeyListener keyListener) {
-        if (keyListener != null) {
-            keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
-        }
-        super.setKeyListener(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/android/support/text/emoji/widget/EmojiExtractTextLayout.java b/android/support/text/emoji/widget/EmojiExtractTextLayout.java
deleted file mode 100644
index c75d6d0..0000000
--- a/android/support/text/emoji/widget/EmojiExtractTextLayout.java
+++ /dev/null
@@ -1,223 +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.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/android/support/text/emoji/widget/EmojiInputConnection.java b/android/support/text/emoji/widget/EmojiInputConnection.java
deleted file mode 100644
index 6232c52..0000000
--- a/android/support/text/emoji/widget/EmojiInputConnection.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.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/android/support/text/emoji/widget/EmojiInputFilter.java b/android/support/text/emoji/widget/EmojiInputFilter.java
deleted file mode 100644
index ba4201e..0000000
--- a/android/support/text/emoji/widget/EmojiInputFilter.java
+++ /dev/null
@@ -1,132 +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.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/android/support/text/emoji/widget/EmojiKeyListener.java b/android/support/text/emoji/widget/EmojiKeyListener.java
deleted file mode 100644
index 6d94f14..0000000
--- a/android/support/text/emoji/widget/EmojiKeyListener.java
+++ /dev/null
@@ -1,66 +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.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/android/support/text/emoji/widget/EmojiTextView.java b/android/support/text/emoji/widget/EmojiTextView.java
deleted file mode 100644
index 3e450dc..0000000
--- a/android/support/text/emoji/widget/EmojiTextView.java
+++ /dev/null
@@ -1,83 +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.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/android/support/text/emoji/widget/EmojiTextViewHelper.java b/android/support/text/emoji/widget/EmojiTextViewHelper.java
deleted file mode 100644
index abd7410..0000000
--- a/android/support/text/emoji/widget/EmojiTextViewHelper.java
+++ /dev/null
@@ -1,195 +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.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/android/support/text/emoji/widget/EmojiTextWatcher.java b/android/support/text/emoji/widget/EmojiTextWatcher.java
deleted file mode 100644
index de3aea1..0000000
--- a/android/support/text/emoji/widget/EmojiTextWatcher.java
+++ /dev/null
@@ -1,131 +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.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/android/support/text/emoji/widget/EmojiTransformationMethod.java b/android/support/text/emoji/widget/EmojiTransformationMethod.java
deleted file mode 100644
index dac9905..0000000
--- a/android/support/text/emoji/widget/EmojiTransformationMethod.java
+++ /dev/null
@@ -1,74 +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.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/android/support/text/emoji/widget/ExtractButtonCompat.java b/android/support/text/emoji/widget/ExtractButtonCompat.java
deleted file mode 100644
index fc8eb78..0000000
--- a/android/support/text/emoji/widget/ExtractButtonCompat.java
+++ /dev/null
@@ -1,61 +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.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/android/support/text/emoji/widget/SpannableBuilder.java b/android/support/text/emoji/widget/SpannableBuilder.java
deleted file mode 100644
index c60a90c..0000000
--- a/android/support/text/emoji/widget/SpannableBuilder.java
+++ /dev/null
@@ -1,434 +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.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/android/support/transition/AnimatorUtils.java b/android/support/transition/AnimatorUtils.java
deleted file mode 100644
index 215d768..0000000
--- a/android/support/transition/AnimatorUtils.java
+++ /dev/null
@@ -1,49 +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.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/android/support/transition/AnimatorUtilsApi14.java b/android/support/transition/AnimatorUtilsApi14.java
deleted file mode 100644
index d9f870b..0000000
--- a/android/support/transition/AnimatorUtilsApi14.java
+++ /dev/null
@@ -1,74 +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.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/android/support/transition/AnimatorUtilsApi19.java b/android/support/transition/AnimatorUtilsApi19.java
deleted file mode 100644
index 0f4ae6b..0000000
--- a/android/support/transition/AnimatorUtilsApi19.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.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/android/support/transition/AnimatorUtilsImpl.java b/android/support/transition/AnimatorUtilsImpl.java
deleted file mode 100644
index 68f222d..0000000
--- a/android/support/transition/AnimatorUtilsImpl.java
+++ /dev/null
@@ -1,31 +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.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/android/support/transition/ArcMotion.java b/android/support/transition/ArcMotion.java
deleted file mode 100644
index 42f667c..0000000
--- a/android/support/transition/ArcMotion.java
+++ /dev/null
@@ -1,276 +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.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/android/support/transition/ArcMotionTest.java b/android/support/transition/ArcMotionTest.java
deleted file mode 100644
index 75d6117..0000000
--- a/android/support/transition/ArcMotionTest.java
+++ /dev/null
@@ -1,203 +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.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/android/support/transition/AutoTransition.java b/android/support/transition/AutoTransition.java
deleted file mode 100644
index bf39c3c..0000000
--- a/android/support/transition/AutoTransition.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.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>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 {
-
-    /**
-     * 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 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())
-                .addTransition(new Fade(Fade.IN));
-    }
-
-}
diff --git a/android/support/transition/AutoTransitionTest.java b/android/support/transition/AutoTransitionTest.java
deleted file mode 100644
index 2c9a77f..0000000
--- a/android/support/transition/AutoTransitionTest.java
+++ /dev/null
@@ -1,116 +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 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.LargeTest;
-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));
-    }
-
-    @LargeTest
-    @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/android/support/transition/BaseTest.java b/android/support/transition/BaseTest.java
deleted file mode 100644
index 4ffb2f9..0000000
--- a/android/support/transition/BaseTest.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.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 BaseTest {
-
-    @Rule
-    public final ActivityTestRule<TransitionActivity> rule;
-
-    BaseTest() {
-        rule = new ActivityTestRule<>(TransitionActivity.class);
-    }
-
-}
diff --git a/android/support/transition/BaseTransitionTest.java b/android/support/transition/BaseTransitionTest.java
deleted file mode 100644
index 5d39d94..0000000
--- a/android/support/transition/BaseTransitionTest.java
+++ /dev/null
@@ -1,134 +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.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/android/support/transition/ChangeBounds.java b/android/support/transition/ChangeBounds.java
deleted file mode 100644
index 4108c8c..0000000
--- a/android/support/transition/ChangeBounds.java
+++ /dev/null
@@ -1,497 +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.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>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() {
-    }
-
-    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);
-    }
-
-    @Nullable
-    @Override
-    public String[] getTransitionProperties() {
-        return sTransitionProperties;
-    }
-
-    /**
-     * When <code>resizeClip</code> is true, ChangeBounds resizes the view using the clipBounds
-     * instead of changing the dimensions of the view during the animation. When
-     * <code>resizeClip</code> is false, ChangeBounds resizes the View by changing its dimensions.
-     *
-     * <p>When resizeClip is set to true, the clip bounds is modified by ChangeBounds. Therefore,
-     * {@link android.transition.ChangeClipBounds} is not compatible with ChangeBounds
-     * in this mode.</p>
-     *
-     * @param resizeClip Used to indicate whether the view bounds should be modified or the
-     *                   clip bounds should be modified by ChangeBounds.
-     * @see android.view.View#setClipBounds(android.graphics.Rect)
-     */
-    public void setResizeClip(boolean 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/android/support/transition/ChangeBoundsTest.java b/android/support/transition/ChangeBoundsTest.java
deleted file mode 100644
index 186017c..0000000
--- a/android/support/transition/ChangeBoundsTest.java
+++ /dev/null
@@ -1,102 +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.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/android/support/transition/ChangeClipBounds.java b/android/support/transition/ChangeClipBounds.java
deleted file mode 100644
index 9449a9c..0000000
--- a/android/support/transition/ChangeClipBounds.java
+++ /dev/null
@@ -1,120 +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.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/android/support/transition/ChangeClipBoundsTest.java b/android/support/transition/ChangeClipBoundsTest.java
deleted file mode 100644
index c227bca..0000000
--- a/android/support/transition/ChangeClipBoundsTest.java
+++ /dev/null
@@ -1,121 +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.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/android/support/transition/ChangeImageTransform.java b/android/support/transition/ChangeImageTransform.java
deleted file mode 100644
index aa9e35c..0000000
--- a/android/support/transition/ChangeImageTransform.java
+++ /dev/null
@@ -1,237 +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.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/android/support/transition/ChangeImageTransformTest.java b/android/support/transition/ChangeImageTransformTest.java
deleted file mode 100644
index 907e01e..0000000
--- a/android/support/transition/ChangeImageTransformTest.java
+++ /dev/null
@@ -1,302 +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.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/android/support/transition/ChangeScroll.java b/android/support/transition/ChangeScroll.java
deleted file mode 100644
index 1cdd3d4..0000000
--- a/android/support/transition/ChangeScroll.java
+++ /dev/null
@@ -1,95 +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.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/android/support/transition/ChangeScrollTest.java b/android/support/transition/ChangeScrollTest.java
deleted file mode 100644
index 0f383d3..0000000
--- a/android/support/transition/ChangeScrollTest.java
+++ /dev/null
@@ -1,76 +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.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/android/support/transition/ChangeTransform.java b/android/support/transition/ChangeTransform.java
deleted file mode 100644
index f0adc08..0000000
--- a/android/support/transition/ChangeTransform.java
+++ /dev/null
@@ -1,583 +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.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/android/support/transition/ChangeTransformTest.java b/android/support/transition/ChangeTransformTest.java
deleted file mode 100644
index 3e543aa..0000000
--- a/android/support/transition/ChangeTransformTest.java
+++ /dev/null
@@ -1,124 +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.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/android/support/transition/CheckCalledRunnable.java b/android/support/transition/CheckCalledRunnable.java
deleted file mode 100644
index 9eea608..0000000
--- a/android/support/transition/CheckCalledRunnable.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;
-
-class CheckCalledRunnable implements Runnable {
-
-    private boolean mWasCalled = false;
-
-    @Override
-    public void run() {
-        mWasCalled = true;
-    }
-
-    /**
-     * @return {@code true} if {@link #run()} was called at least once.
-     */
-    boolean wasCalled() {
-        return mWasCalled;
-    }
-
-}
diff --git a/android/support/transition/CircularPropagation.java b/android/support/transition/CircularPropagation.java
deleted file mode 100644
index ced4223..0000000
--- a/android/support/transition/CircularPropagation.java
+++ /dev/null
@@ -1,106 +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.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/android/support/transition/Explode.java b/android/support/transition/Explode.java
deleted file mode 100644
index 38d0fb7..0000000
--- a/android/support/transition/Explode.java
+++ /dev/null
@@ -1,175 +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.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/android/support/transition/ExplodeTest.java b/android/support/transition/ExplodeTest.java
deleted file mode 100644
index b421537..0000000
--- a/android/support/transition/ExplodeTest.java
+++ /dev/null
@@ -1,166 +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.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/android/support/transition/Fade.java b/android/support/transition/Fade.java
deleted file mode 100644
index eab0f23..0000000
--- a/android/support/transition/Fade.java
+++ /dev/null
@@ -1,205 +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.content.Context;
-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.view.ViewCompat;
-import android.util.AttributeSet;
-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 Scene} was
- * {@link Scene#getSceneForLayout(android.view.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 {@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 = 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 = Visibility.MODE_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 Fade(int fadingMode) {
-        setMode(fadingMode);
-    }
-
-    /**
-     * Constructs a Fade transition that will fade targets in and out.
-     */
-    public Fade() {
-    }
-
-    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) {
-        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
-    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/android/support/transition/FadeTest.java b/android/support/transition/FadeTest.java
deleted file mode 100644
index 3b171e2..0000000
--- a/android/support/transition/FadeTest.java
+++ /dev/null
@@ -1,275 +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 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;
-import android.view.ViewGroup;
-
-import org.junit.Before;
-import org.junit.Test;
-
-@MediumTest
-public class FadeTest 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() {
-        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();
-        final TransitionValues startValues = new TransitionValues();
-        startValues.view = mView;
-        fade.captureStartValues(startValues);
-        mView.setVisibility(View.INVISIBLE);
-        final TransitionValues endValues = new TransitionValues();
-        endValues.view = mView;
-        fade.captureEndValues(endValues);
-        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
-        assertThat(animator, is(notNullValue()));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testAppear() {
-        mView.setVisibility(View.INVISIBLE);
-        final Fade fade = new Fade();
-        final TransitionValues startValues = new TransitionValues();
-        startValues.view = mView;
-        fade.captureStartValues(startValues);
-        mView.setVisibility(View.VISIBLE);
-        final TransitionValues endValues = new TransitionValues();
-        endValues.view = mView;
-        fade.captureEndValues(endValues);
-        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
-        assertThat(animator, is(notNullValue()));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testNoChange() {
-        final Fade fade = new Fade();
-        final TransitionValues startValues = new TransitionValues();
-        startValues.view = mView;
-        fade.captureStartValues(startValues);
-        final TransitionValues endValues = new TransitionValues();
-        endValues.view = mView;
-        fade.captureEndValues(endValues);
-        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
-        // No visibility change; no animation should happen
-        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/android/support/transition/FloatArrayEvaluator.java b/android/support/transition/FloatArrayEvaluator.java
deleted file mode 100644
index 81b97b7..0000000
--- a/android/support/transition/FloatArrayEvaluator.java
+++ /dev/null
@@ -1,70 +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.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/android/support/transition/FragmentTransitionSupport.java b/android/support/transition/FragmentTransitionSupport.java
deleted file mode 100644
index b117946..0000000
--- a/android/support/transition/FragmentTransitionSupport.java
+++ /dev/null
@@ -1,320 +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.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.FragmentTransitionImpl;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * @hide
- */
-// This is instantiated in android.support.v4.app.FragmentTransition
-@SuppressWarnings("unused")
-@RestrictTo(LIBRARY_GROUP)
-public class FragmentTransitionSupport extends FragmentTransitionImpl {
-
-    @Override
-    public boolean canHandle(Object transition) {
-        return transition instanceof Transition;
-    }
-
-    @Override
-    public Object cloneTransition(Object transition) {
-        Transition copy = null;
-        if (transition != null) {
-            copy = ((Transition) transition).clone();
-        }
-        return copy;
-    }
-
-    @Override
-    public Object wrapTransitionInSet(Object transition) {
-        if (transition == null) {
-            return null;
-        }
-        TransitionSet transitionSet = new TransitionSet();
-        transitionSet.addTransition((Transition) transition);
-        return transitionSet;
-    }
-
-    @Override
-    public 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);
-    }
-
-    @Override
-    public 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(@NonNull Transition transition) {
-                    return epicenter;
-                }
-            });
-        }
-    }
-
-    @Override
-    public 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));
-                }
-            }
-        }
-    }
-
-    private static boolean hasSimpleTarget(Transition transition) {
-        return !isNullOrEmpty(transition.getTargetIds())
-                || !isNullOrEmpty(transition.getTargetNames())
-                || !isNullOrEmpty(transition.getTargetTypes());
-    }
-
-    @Override
-    public 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;
-    }
-
-    @Override
-    public void scheduleHideFragmentView(Object exitTransitionObj, final View fragmentView,
-            final ArrayList<View> exitingViews) {
-        Transition exitTransition = (Transition) exitTransitionObj;
-        exitTransition.addListener(new Transition.TransitionListener() {
-            @Override
-            public void onTransitionStart(@NonNull Transition transition) {
-            }
-
-            @Override
-            public void onTransitionEnd(@NonNull 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(@NonNull Transition transition) {
-            }
-
-            @Override
-            public void onTransitionPause(@NonNull Transition transition) {
-            }
-
-            @Override
-            public void onTransitionResume(@NonNull Transition transition) {
-            }
-        });
-    }
-
-    @Override
-    public 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;
-        }
-    }
-
-    @Override
-    public void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
-        TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
-    }
-
-    @Override
-    public 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(@NonNull 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(@NonNull Transition transition) {
-            }
-
-            @Override
-            public void onTransitionCancel(@NonNull Transition transition) {
-            }
-
-            @Override
-            public void onTransitionPause(@NonNull Transition transition) {
-            }
-
-            @Override
-            public void onTransitionResume(@NonNull Transition transition) {
-            }
-        });
-    }
-
-    @Override
-    public 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);
-        }
-    }
-
-    @Override
-    public 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.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));
-                }
-            }
-        }
-    }
-
-    @Override
-    public void addTarget(Object transitionObj, View view) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.addTarget(view);
-        }
-    }
-
-    @Override
-    public void removeTarget(Object transitionObj, View view) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.removeTarget(view);
-        }
-    }
-
-    @Override
-    public void setEpicenter(Object transitionObj, final Rect epicenter) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
-                @Override
-                public Rect onGetEpicenter(@NonNull Transition transition) {
-                    if (epicenter == null || epicenter.isEmpty()) {
-                        return null;
-                    }
-                    return epicenter;
-                }
-            });
-        }
-    }
-
-}
diff --git a/android/support/transition/FragmentTransitionTest.java b/android/support/transition/FragmentTransitionTest.java
deleted file mode 100644
index 893d4c6..0000000
--- a/android/support/transition/FragmentTransitionTest.java
+++ /dev/null
@@ -1,226 +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.transition;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.Nullable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.transition.test.R;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.util.Pair;
-import android.support.v4.util.SparseArrayCompat;
-import android.support.v4.view.ViewCompat;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-
-@MediumTest
-@RunWith(Parameterized.class)
-public class FragmentTransitionTest extends BaseTest {
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Boolean[]{
-                false, true
-        };
-    }
-
-    private final boolean mReorderingAllowed;
-
-    public FragmentTransitionTest(boolean reorderingAllowed) {
-        mReorderingAllowed = reorderingAllowed;
-    }
-
-    @Test
-    public void preconditions() {
-        final TransitionFragment fragment1 = TransitionFragment.newInstance(R.layout.scene2);
-        final TransitionFragment fragment2 = TransitionFragment.newInstance(R.layout.scene3);
-        showFragment(fragment1, false, null);
-        assertNull(fragment1.mRed);
-        assertNotNull(fragment1.mGreen);
-        assertNotNull(fragment1.mBlue);
-        showFragment(fragment2, true, new Pair<>(fragment1.mGreen, "green"));
-        assertNotNull(fragment2.mRed);
-        assertNotNull(fragment2.mGreen);
-        assertNotNull(fragment2.mBlue);
-    }
-
-    @Test
-    public void nonSharedTransition() {
-        final TransitionFragment fragment1 = TransitionFragment.newInstance(R.layout.scene2);
-        final TransitionFragment fragment2 = TransitionFragment.newInstance(R.layout.scene3);
-        showFragment(fragment1, false, null);
-        showFragment(fragment2, true, null);
-        verify(fragment1.mListeners.get(TransitionFragment.TRANSITION_EXIT))
-                .onTransitionStart(any(Transition.class));
-        verify(fragment1.mListeners.get(TransitionFragment.TRANSITION_EXIT), timeout(3000))
-                .onTransitionEnd(any(Transition.class));
-        verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_ENTER))
-                .onTransitionStart(any(Transition.class));
-        verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_ENTER), timeout(3000))
-                .onTransitionEnd(any(Transition.class));
-        popBackStack();
-        verify(fragment1.mListeners.get(TransitionFragment.TRANSITION_REENTER))
-                .onTransitionStart(any(Transition.class));
-        verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_RETURN))
-                .onTransitionStart(any(Transition.class));
-    }
-
-    @Test
-    public void sharedTransition() {
-        final TransitionFragment fragment1 = TransitionFragment.newInstance(R.layout.scene2);
-        final TransitionFragment fragment2 = TransitionFragment.newInstance(R.layout.scene3);
-        showFragment(fragment1, false, null);
-        showFragment(fragment2, true, new Pair<>(fragment1.mGreen, "green"));
-        verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_SHARED_ENTER))
-                .onTransitionStart(any(Transition.class));
-        verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_SHARED_ENTER), timeout(3000))
-                .onTransitionEnd(any(Transition.class));
-        popBackStack();
-        verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_SHARED_RETURN))
-                .onTransitionStart(any(Transition.class));
-    }
-
-    private void showFragment(final Fragment fragment, final boolean addToBackStack,
-            final Pair<View, String> sharedElement) {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final FragmentTransaction transaction = getFragmentManager().beginTransaction();
-                transaction.replace(R.id.root, fragment);
-                transaction.setReorderingAllowed(mReorderingAllowed);
-                if (sharedElement != null) {
-                    transaction.addSharedElement(sharedElement.first, sharedElement.second);
-                }
-                if (addToBackStack) {
-                    transaction.addToBackStack(null);
-                    transaction.commit();
-                    getFragmentManager().executePendingTransactions();
-                } else {
-                    transaction.commitNow();
-                }
-            }
-        });
-        instrumentation.waitForIdleSync();
-    }
-
-    private void popBackStack() {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        instrumentation.runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                getFragmentManager().popBackStackImmediate();
-            }
-        });
-        instrumentation.waitForIdleSync();
-    }
-
-    private FragmentManager getFragmentManager() {
-        return rule.getActivity().getSupportFragmentManager();
-    }
-
-    /**
-     * A {@link Fragment} with all kinds of {@link Transition} with tracking listeners.
-     */
-    public static class TransitionFragment extends Fragment {
-
-        static final int TRANSITION_ENTER = 1;
-        static final int TRANSITION_EXIT = 2;
-        static final int TRANSITION_REENTER = 3;
-        static final int TRANSITION_RETURN = 4;
-        static final int TRANSITION_SHARED_ENTER = 5;
-        static final int TRANSITION_SHARED_RETURN = 6;
-
-        private static final String ARG_LAYOUT_ID = "layout_id";
-
-        View mRed;
-        View mGreen;
-        View mBlue;
-
-        SparseArrayCompat<Transition.TransitionListener> mListeners = new SparseArrayCompat<>();
-
-        public static TransitionFragment newInstance(@LayoutRes int layout) {
-            final Bundle args = new Bundle();
-            args.putInt(ARG_LAYOUT_ID, layout);
-            final TransitionFragment fragment = new TransitionFragment();
-            fragment.setArguments(args);
-            return fragment;
-        }
-
-        public TransitionFragment() {
-            setEnterTransition(createTransition(TRANSITION_ENTER));
-            setExitTransition(createTransition(TRANSITION_EXIT));
-            setReenterTransition(createTransition(TRANSITION_REENTER));
-            setReturnTransition(createTransition(TRANSITION_RETURN));
-            setSharedElementEnterTransition(createTransition(TRANSITION_SHARED_ENTER));
-            setSharedElementReturnTransition(createTransition(TRANSITION_SHARED_RETURN));
-        }
-
-        @Nullable
-        @Override
-        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-                @Nullable Bundle savedInstanceState) {
-            return inflater.inflate(getArguments().getInt(ARG_LAYOUT_ID), container, false);
-        }
-
-        @Override
-        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-            mRed = view.findViewById(R.id.redSquare);
-            mGreen = view.findViewById(R.id.greenSquare);
-            mBlue = view.findViewById(R.id.blueSquare);
-            if (mRed != null) {
-                ViewCompat.setTransitionName(mRed, "red");
-            }
-            if (mGreen != null) {
-                ViewCompat.setTransitionName(mGreen, "green");
-            }
-            if (mBlue != null) {
-                ViewCompat.setTransitionName(mBlue, "blue");
-            }
-        }
-
-        private Transition createTransition(int type) {
-            final Transition.TransitionListener listener = mock(
-                    Transition.TransitionListener.class);
-            final AutoTransition transition = new AutoTransition();
-            transition.addListener(listener);
-            transition.setDuration(10);
-            mListeners.put(type, listener);
-            return transition;
-        }
-
-    }
-
-}
diff --git a/android/support/transition/GhostViewApi14.java b/android/support/transition/GhostViewApi14.java
deleted file mode 100644
index 38acef2..0000000
--- a/android/support/transition/GhostViewApi14.java
+++ /dev/null
@@ -1,201 +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.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/android/support/transition/GhostViewApi21.java b/android/support/transition/GhostViewApi21.java
deleted file mode 100644
index ace0016..0000000
--- a/android/support/transition/GhostViewApi21.java
+++ /dev/null
@@ -1,130 +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.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/android/support/transition/GhostViewImpl.java b/android/support/transition/GhostViewImpl.java
deleted file mode 100644
index 037b573..0000000
--- a/android/support/transition/GhostViewImpl.java
+++ /dev/null
@@ -1,43 +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.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/android/support/transition/GhostViewUtils.java b/android/support/transition/GhostViewUtils.java
deleted file mode 100644
index 66f01c3..0000000
--- a/android/support/transition/GhostViewUtils.java
+++ /dev/null
@@ -1,44 +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.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/android/support/transition/ImageViewUtils.java b/android/support/transition/ImageViewUtils.java
deleted file mode 100644
index 19f0938..0000000
--- a/android/support/transition/ImageViewUtils.java
+++ /dev/null
@@ -1,59 +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.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/android/support/transition/ImageViewUtilsApi14.java b/android/support/transition/ImageViewUtilsApi14.java
deleted file mode 100644
index 2036ac9..0000000
--- a/android/support/transition/ImageViewUtilsApi14.java
+++ /dev/null
@@ -1,63 +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.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/android/support/transition/ImageViewUtilsApi21.java b/android/support/transition/ImageViewUtilsApi21.java
deleted file mode 100644
index 9c2a6c3..0000000
--- a/android/support/transition/ImageViewUtilsApi21.java
+++ /dev/null
@@ -1,73 +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.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/android/support/transition/ImageViewUtilsImpl.java b/android/support/transition/ImageViewUtilsImpl.java
deleted file mode 100644
index 72a71b6..0000000
--- a/android/support/transition/ImageViewUtilsImpl.java
+++ /dev/null
@@ -1,33 +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.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/android/support/transition/MatrixUtils.java b/android/support/transition/MatrixUtils.java
deleted file mode 100644
index 1d9be29..0000000
--- a/android/support/transition/MatrixUtils.java
+++ /dev/null
@@ -1,207 +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.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/android/support/transition/ObjectAnimatorUtils.java b/android/support/transition/ObjectAnimatorUtils.java
deleted file mode 100644
index d8c132b..0000000
--- a/android/support/transition/ObjectAnimatorUtils.java
+++ /dev/null
@@ -1,41 +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.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/android/support/transition/ObjectAnimatorUtilsApi14.java b/android/support/transition/ObjectAnimatorUtilsApi14.java
deleted file mode 100644
index a1038de..0000000
--- a/android/support/transition/ObjectAnimatorUtilsApi14.java
+++ /dev/null
@@ -1,33 +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.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/android/support/transition/ObjectAnimatorUtilsApi21.java b/android/support/transition/ObjectAnimatorUtilsApi21.java
deleted file mode 100644
index 0012500..0000000
--- a/android/support/transition/ObjectAnimatorUtilsApi21.java
+++ /dev/null
@@ -1,33 +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.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/android/support/transition/ObjectAnimatorUtilsImpl.java b/android/support/transition/ObjectAnimatorUtilsImpl.java
deleted file mode 100644
index ff66f9b..0000000
--- a/android/support/transition/ObjectAnimatorUtilsImpl.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.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/android/support/transition/PathMotion.java b/android/support/transition/PathMotion.java
deleted file mode 100644
index d270a08..0000000
--- a/android/support/transition/PathMotion.java
+++ /dev/null
@@ -1,62 +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.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/android/support/transition/PathMotionTest.java b/android/support/transition/PathMotionTest.java
deleted file mode 100644
index 8bf738e..0000000
--- a/android/support/transition/PathMotionTest.java
+++ /dev/null
@@ -1,62 +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.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/android/support/transition/PathProperty.java b/android/support/transition/PathProperty.java
deleted file mode 100644
index c5e7429..0000000
--- a/android/support/transition/PathProperty.java
+++ /dev/null
@@ -1,65 +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.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/android/support/transition/PatternPathMotion.java b/android/support/transition/PatternPathMotion.java
deleted file mode 100644
index 2ac6e72..0000000
--- a/android/support/transition/PatternPathMotion.java
+++ /dev/null
@@ -1,148 +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.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/android/support/transition/PatternPathMotionTest.java b/android/support/transition/PatternPathMotionTest.java
deleted file mode 100644
index b14ceaa..0000000
--- a/android/support/transition/PatternPathMotionTest.java
+++ /dev/null
@@ -1,77 +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.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/android/support/transition/PropagationTest.java b/android/support/transition/PropagationTest.java
deleted file mode 100644
index 932c8d3..0000000
--- a/android/support/transition/PropagationTest.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.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/android/support/transition/PropertyValuesHolderUtils.java b/android/support/transition/PropertyValuesHolderUtils.java
deleted file mode 100644
index 7a8eeb5..0000000
--- a/android/support/transition/PropertyValuesHolderUtils.java
+++ /dev/null
@@ -1,51 +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.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/android/support/transition/PropertyValuesHolderUtilsApi14.java b/android/support/transition/PropertyValuesHolderUtilsApi14.java
deleted file mode 100644
index 072178b..0000000
--- a/android/support/transition/PropertyValuesHolderUtilsApi14.java
+++ /dev/null
@@ -1,33 +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.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/android/support/transition/PropertyValuesHolderUtilsApi21.java b/android/support/transition/PropertyValuesHolderUtilsApi21.java
deleted file mode 100644
index 5f6d117..0000000
--- a/android/support/transition/PropertyValuesHolderUtilsApi21.java
+++ /dev/null
@@ -1,33 +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.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/android/support/transition/PropertyValuesHolderUtilsImpl.java b/android/support/transition/PropertyValuesHolderUtilsImpl.java
deleted file mode 100644
index 8ec31eb..0000000
--- a/android/support/transition/PropertyValuesHolderUtilsImpl.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.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/android/support/transition/RectEvaluator.java b/android/support/transition/RectEvaluator.java
deleted file mode 100644
index 1e757a5..0000000
--- a/android/support/transition/RectEvaluator.java
+++ /dev/null
@@ -1,88 +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.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/android/support/transition/Scene.java b/android/support/transition/Scene.java
deleted file mode 100644
index cc40b2c..0000000
--- a/android/support/transition/Scene.java
+++ /dev/null
@@ -1,259 +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.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;
-
-/**
- * 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.
- */
-public class 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
-     * <code>layoutId</code> parameter. If such a Scene has already been created for
-     * the given <code>sceneRoot</code>, that same Scene will be returned.
-     * This caching of layoutId-based scenes enables sharing of common scenes
-     * between those created in code and those referenced by {@link TransitionManager}
-     * XML resource files.
-     *
-     * @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
-     */
-    @NonNull
-    public static Scene getSceneForLayout(@NonNull ViewGroup sceneRoot, @LayoutRes int layoutId,
-            @NonNull Context context) {
-        @SuppressWarnings("unchecked")
-        SparseArray<Scene> scenes =
-                (SparseArray<Scene>) sceneRoot.getTag(R.id.transition_scene_layoutid_cache);
-        if (scenes == null) {
-            scenes = new SparseArray<>();
-            sceneRoot.setTag(R.id.transition_scene_layoutid_cache, scenes);
-        }
-        Scene scene = scenes.get(layoutId);
-        if (scene != null) {
-            return scene;
-        } else {
-            scene = new Scene(sceneRoot, layoutId, context);
-            scenes.put(layoutId, scene);
-            return scene;
-        }
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * 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.
-     */
-    @NonNull
-    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 android.support.transition.TransitionManager} 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);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * 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 android.support.transition.Scene(android.view.ViewGroup, android.view.ViewGroup)
-     */
-    public void setEnterAction(@Nullable 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 android.support.transition.Scene(android.view.ViewGroup, android.view.ViewGroup)
-     */
-    public void setExitAction(@Nullable 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);
-    }
-
-}
diff --git a/android/support/transition/SceneTest.java b/android/support/transition/SceneTest.java
deleted file mode 100644
index 129a3eb..0000000
--- a/android/support/transition/SceneTest.java
+++ /dev/null
@@ -1,127 +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 org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.sameInstance;
-
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.MediumTest;
-import android.support.transition.test.R;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import org.junit.Test;
-
-@MediumTest
-public class SceneTest extends BaseTest {
-
-    @Test
-    public void testGetSceneRoot() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        Scene scene = new Scene(root);
-        assertThat(scene.getSceneRoot(), is(sameInstance(root)));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testSceneWithViewGroup() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        FrameLayout layout = new FrameLayout(activity);
-        Scene scene = new Scene(root, layout);
-        CheckCalledRunnable enterAction = new CheckCalledRunnable();
-        CheckCalledRunnable exitAction = new CheckCalledRunnable();
-        scene.setEnterAction(enterAction);
-        scene.setExitAction(exitAction);
-        scene.enter();
-        assertThat(enterAction.wasCalled(), is(true));
-        assertThat(exitAction.wasCalled(), is(false));
-        assertThat(root.getChildCount(), is(1));
-        assertThat(root.getChildAt(0), is((View) layout));
-        scene.exit();
-        assertThat(exitAction.wasCalled(), is(true));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testSceneWithView() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        View view = new View(activity);
-        Scene scene = new Scene(root, view);
-        CheckCalledRunnable enterAction = new CheckCalledRunnable();
-        CheckCalledRunnable exitAction = new CheckCalledRunnable();
-        scene.setEnterAction(enterAction);
-        scene.setExitAction(exitAction);
-        scene.enter();
-        assertThat(enterAction.wasCalled(), is(true));
-        assertThat(exitAction.wasCalled(), is(false));
-        assertThat(root.getChildCount(), is(1));
-        assertThat(root.getChildAt(0), is(view));
-        scene.exit();
-        assertThat(exitAction.wasCalled(), is(true));
-    }
-
-    @Test
-    public void testEnterAction() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        Scene scene = new Scene(root);
-        CheckCalledRunnable runnable = new CheckCalledRunnable();
-        scene.setEnterAction(runnable);
-        scene.enter();
-        assertThat(runnable.wasCalled(), is(true));
-    }
-
-    @Test
-    public void testExitAction() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        Scene scene = new Scene(root);
-        scene.enter();
-        CheckCalledRunnable runnable = new CheckCalledRunnable();
-        scene.setExitAction(runnable);
-        scene.exit();
-        assertThat(runnable.wasCalled(), is(true));
-    }
-
-    @Test
-    public void testExitAction_withoutEnter() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        Scene scene = new Scene(root);
-        CheckCalledRunnable runnable = new CheckCalledRunnable();
-        scene.setExitAction(runnable);
-        scene.exit();
-        assertThat(runnable.wasCalled(), is(false));
-    }
-
-    @Test
-    public void testGetSceneForLayout_cache() {
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        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.support_scene0, activity),
-                is(sameInstance(scene)));
-    }
-
-}
diff --git a/android/support/transition/SidePropagation.java b/android/support/transition/SidePropagation.java
deleted file mode 100644
index 64b5210..0000000
--- a/android/support/transition/SidePropagation.java
+++ /dev/null
@@ -1,165 +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.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/android/support/transition/Slide.java b/android/support/transition/Slide.java
deleted file mode 100644
index 49c5e5b..0000000
--- a/android/support/transition/Slide.java
+++ /dev/null
@@ -1,275 +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.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/android/support/transition/SlideBadEdgeTest.java b/android/support/transition/SlideBadEdgeTest.java
deleted file mode 100644
index e43d4f3..0000000
--- a/android/support/transition/SlideBadEdgeTest.java
+++ /dev/null
@@ -1,78 +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.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/android/support/transition/SlideDefaultEdgeTest.java b/android/support/transition/SlideDefaultEdgeTest.java
deleted file mode 100644
index a0e7eab..0000000
--- a/android/support/transition/SlideDefaultEdgeTest.java
+++ /dev/null
@@ -1,39 +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.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/android/support/transition/SlideEdgeTest.java b/android/support/transition/SlideEdgeTest.java
deleted file mode 100644
index af8d0b6..0000000
--- a/android/support/transition/SlideEdgeTest.java
+++ /dev/null
@@ -1,273 +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.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.LargeTest;
-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());
-        }
-    }
-
-    @LargeTest
-    @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());
-        }
-    }
-
-    @LargeTest
-    @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/android/support/transition/Styleable.java b/android/support/transition/Styleable.java
deleted file mode 100644
index 74ceb4a..0000000
--- a/android/support/transition/Styleable.java
+++ /dev/null
@@ -1,177 +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.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/android/support/transition/SyncRunnable.java b/android/support/transition/SyncRunnable.java
deleted file mode 100644
index 2e8a2e1..0000000
--- a/android/support/transition/SyncRunnable.java
+++ /dev/null
@@ -1,40 +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.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/android/support/transition/SyncTransitionListener.java b/android/support/transition/SyncTransitionListener.java
deleted file mode 100644
index 4d7e02e..0000000
--- a/android/support/transition/SyncTransitionListener.java
+++ /dev/null
@@ -1,87 +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.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/android/support/transition/Transition.java b/android/support/transition/Transition.java
deleted file mode 100644
index 9c198a9..0000000
--- a/android/support/transition/Transition.java
+++ /dev/null
@@ -1,2437 +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.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
- * targets during a scene change. Subclasses of this abstract class may
- * choreograph several child transitions ({@link TransitionSet} or they may
- * perform custom animations themselves. Any Transition has two main jobs:
- * (1) capture property values, and (2) play animations based on changes to
- * captured property values. A custom transition knows what property values
- * on View objects are of interest to it, and also knows how to animate
- * changes to those values. For example, the {@link Fade} transition tracks
- * changes to visibility-related properties and is able to construct and run
- * animations that fade items in or out based on changes to those properties.
- *
- * <p>Note: Transitions may not work correctly with either {@link SurfaceView}
- * or {@link TextureView}, due to the way that these views are displayed
- * on the screen. For SurfaceView, the problem is that the view is updated from
- * a non-UI thread, so changes to the view due to transitions (such as moving
- * and resizing the view) may be out of sync with the display inside those bounds.
- * TextureView is more compatible with transitions in general, but some
- * specific transitions (such as {@link Fade}) may not be compatible
- * with TextureView because they rely on {@link android.view.ViewOverlay}
- * functionality, which does not currently work with TextureView.</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 Cloneable {
-
-    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
-     * 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 Transition() {
-    }
-
-    /**
-     * 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 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 setDuration(long duration) {
-        mDuration = duration;
-        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 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 startDelay The length of the delay, in milliseconds.
-     * @return This transition object.
-     */
-    @NonNull
-    public Transition setStartDelay(long startDelay) {
-        mStartDelay = startDelay;
-        return this;
-    }
-
-    /**
-     * 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 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 interpolator The time interpolator used by the transition
-     * @return This transition object.
-     */
-    @NonNull
-    public Transition setInterpolator(@Nullable TimeInterpolator interpolator) {
-        mInterpolator = interpolator;
-        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.
-     */
-    @Nullable
-    public TimeInterpolator getInterpolator() {
-        return mInterpolator;
-    }
-
-    /**
-     * 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 null;
-    }
-
-    /**
-     * This method creates an animation that will be run for this transition
-     * given the information in the startValues and endValues structures captured
-     * earlier for the start and end scenes. Subclasses of Transition should override
-     * this method. The method should only be called by the transition system; it is
-     * not intended to be called from external classes.
-     *
-     * <p>This method is called by the transition's parent (all the way up to the
-     * topmost Transition in the hierarchy) with the sceneRoot and start/end
-     * values that the transition may need to set up initial target values
-     * and construct an appropriate animation. For example, if an overall
-     * Transition is a {@link TransitionSet} consisting of several
-     * child transitions in sequence, then some of the child transitions may
-     * want to set initial values on target views prior to the overall
-     * Transition commencing, to put them in an appropriate state for the
-     * delay between that start and the child Transition start time. For
-     * example, a transition that fades an item in may wish to set the starting
-     * alpha value to 0, to avoid it blinking in prior to the transition
-     * actually starting the animation. This is necessary because the scene
-     * change that triggers the Transition will automatically set the end-scene
-     * on all target views, so a Transition that wants to animate from a
-     * different value should set that value prior to returning from this method.</p>
-     *
-     * <p>Additionally, a Transition can perform logic to determine whether
-     * the transition needs to run on the given target and start/end values.
-     * For example, a transition that resizes objects on the screen may wish
-     * to avoid running for views which are not present in either the start
-     * or end scenes.</p>
-     *
-     * <p>If there is an animator created and returned from this method, the
-     * transition mechanism will apply any applicable duration, startDelay,
-     * and interpolator to that animation and start it. A return value of
-     * <code>null</code> indicates that no animation should run. The default
-     * implementation returns null.</p>
-     *
-     * <p>The method is called for every applicable target object, which is
-     * stored in the {@link TransitionValues#view} field.</p>
-     *
-     * @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 A 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.
-     */
-    @Nullable
-    public Animator createAnimator(@NonNull ViewGroup sceneRoot,
-            @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
-        return null;
-    }
-
-    /**
-     * 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.
-     *
-     * <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 #excludeTarget(View, boolean)
-     * @see #excludeChildren(int, boolean)
-     * @see #excludeChildren(Class, boolean)
-     */
-    @NonNull
-    public Transition excludeChildren(@NonNull View target, boolean exclude) {
-        mTargetChildExcludes = excludeView(mTargetChildExcludes, target, exclude);
-        return this;
-    }
-
-    /**
-     * Whether to add the children of the given id to the list of targets to exclude
-     * from this transition. The <code>exclude</code> parameter specifies whether
-     * the children of the target should be added to or removed from the excluded list.
-     * Excluding children in this way provides a simple mechanism for excluding all
-     * children of specific targets, rather than individually excluding each
-     * child individually.
-     *
-     * <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 whose children should be ignored when running
-     *                 this transition.
-     * @param exclude  Whether to add the target to or remove the target from the
-     *                 current list of excluded-child targets.
-     * @return This transition object.
-     * @see #excludeTarget(int, boolean)
-     * @see #excludeChildren(View, boolean)
-     * @see #excludeChildren(Class, boolean)
-     */
-    @NonNull
-    public Transition excludeChildren(@IdRes 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;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Whether to add the given type to the list of types whose children should
-     * be excluded 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 #excludeTarget(Class, boolean)
-     * @see #excludeChildren(int, boolean)
-     * @see #excludeChildren(View, boolean)
-     */
-    @NonNull
-    public Transition excludeChildren(@NonNull 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;
-    }
-
-    /**
-     * 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 mTargetIds;
-    }
-
-    /**
-     * 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 mTargets;
-    }
-
-    /**
-     * 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.
-     *
-     * @return the list of target transitionNames
-     */
-    @Nullable
-    public List<String> getTargetNames() {
-        return mTargetNames;
-    }
-
-    /**
-     * 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.
-     *
-     * @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.
-     */
-    @NonNull
-    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
-     * should determine whether the old transition is animating to different end values
-     * and should be canceled in favor of the new transition.
-     *
-     * <p>By default, a Transition's name is simply the value of {@link Class#getName()},
-     * but subclasses are free to override and return something different.</p>
-     *
-     * @return The name of this transition.
-     */
-    @NonNull
-    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;
-    }
-
-    /**
-     * A transition listener receives notifications from a transition.
-     * Notifications indicate transition lifecycle events.
-     */
-    public interface TransitionListener {
-
-        /**
-         * Notification about the start of the transition.
-         *
-         * @param transition The started transition.
-         */
-        void onTransitionStart(@NonNull Transition 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(Transition)} 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(@NonNull Transition transition);
-
-        /**
-         * Notification about the cancellation of the transition.
-         * Note that cancel may be called by a parent {@link TransitionSet} 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(android.view.ViewGroup, TransitionValues, TransitionValues)
-         * createAnimator()} time.
-         *
-         * @param transition The transition which was canceled.
-         */
-        void onTransitionCancel(@NonNull Transition transition);
-
-        /**
-         * Notification when a transition is paused.
-         * Note that createAnimator() may be called by a parent {@link TransitionSet} 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(android.view.ViewGroup, TransitionValues, TransitionValues)
-         * createAnimator()} time.
-         *
-         * @param transition The transition which was paused.
-         */
-        void onTransitionPause(@NonNull Transition transition);
-
-        /**
-         * Notification when a transition is resumed.
-         * Note that resume() may be called by a parent {@link TransitionSet} 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(Transition)}.
-         *
-         * @param transition The transition which was resumed.
-         */
-        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/android/support/transition/TransitionActivity.java b/android/support/transition/TransitionActivity.java
deleted file mode 100644
index ecb9355..0000000
--- a/android/support/transition/TransitionActivity.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.transition;
-
-import android.os.Bundle;
-import android.support.transition.test.R;
-import android.support.v4.app.FragmentActivity;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-public class TransitionActivity extends FragmentActivity {
-
-    private LinearLayout mRoot;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_transition);
-        mRoot = findViewById(R.id.root);
-    }
-
-    ViewGroup getRoot() {
-        return mRoot;
-    }
-
-}
diff --git a/android/support/transition/TransitionInflater.java b/android/support/transition/TransitionInflater.java
deleted file mode 100644
index f22579a..0000000
--- a/android/support/transition/TransitionInflater.java
+++ /dev/null
@@ -1,336 +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.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/android/support/transition/TransitionInflaterTest.java b/android/support/transition/TransitionInflaterTest.java
deleted file mode 100644
index f9bd23f..0000000
--- a/android/support/transition/TransitionInflaterTest.java
+++ /dev/null
@@ -1,286 +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.transition;
-
-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.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/android/support/transition/TransitionListenerAdapter.java b/android/support/transition/TransitionListenerAdapter.java
deleted file mode 100644
index 333fbfb..0000000
--- a/android/support/transition/TransitionListenerAdapter.java
+++ /dev/null
@@ -1,49 +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.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/android/support/transition/TransitionManager.java b/android/support/transition/TransitionManager.java
deleted file mode 100644
index d5f46ab..0000000
--- a/android/support/transition/TransitionManager.java
+++ /dev/null
@@ -1,430 +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.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
- * change of {@link Scene}. To use the manager, add scenes along with
- * transition objects with calls to {@link #setTransition(Scene, Transition)}
- * or {@link #setTransition(Scene, Scene, Transition)}. Setting specific
- * transitions for scene changes is not required; by default, a Scene change
- * will use {@link AutoTransition} to do something reasonable for most
- * situations. Specifying other transitions for particular scene changes is
- * only necessary if the application wants different transition behavior
- * in these situations.
- *
- * <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 final String LOG_TAG = "TransitionManager";
-
-    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);
-            }
-        }
-    }
-
-    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));
-    }
-
-    /**
-     * Convenience method to simply change to the given scene using
-     * the default transition for TransitionManager.
-     *
-     * @param scene The Scene to change to
-     */
-    public static void go(@NonNull Scene scene) {
-        changeScene(scene, sDefaultTransition);
-    }
-
-    /**
-     * Convenience method to simply change to the given scene using
-     * the given transition.
-     *
-     * <p>Passing in <code>null</code> for the transition parameter will
-     * result in the scene changing without any transition running, and is
-     * equivalent to calling {@link Scene#exit()} on the scene root's
-     * current scene, followed by {@link Scene#enter()} on the scene
-     * specified by the <code>scene</code> parameter.</p>
-     *
-     * @param scene      The Scene to change to
-     * @param transition The transition to use for this scene change. A
-     *                   value of null causes the scene change to happen with no transition.
-     */
-    public static void go(@NonNull Scene scene, @Nullable Transition transition) {
-        changeScene(scene, transition);
-    }
-
-    /**
-     * Convenience method to animate, using the default transition,
-     * to a new scene defined by all changes within the given scene root between
-     * calling this method and the next rendering frame.
-     * Equivalent to calling {@link #beginDelayedTransition(ViewGroup, Transition)}
-     * with a value of <code>null</code> for the <code>transition</code> parameter.
-     *
-     * @param sceneRoot The root of the View hierarchy to run the transition on.
-     */
-    public static void beginDelayedTransition(@NonNull final ViewGroup sceneRoot) {
-        beginDelayedTransition(sceneRoot, null);
-    }
-
-    /**
-     * Convenience method to animate to a new scene defined by all changes within
-     * the given scene root between calling this method and the next rendering frame.
-     * Calling this method causes TransitionManager to capture current values in the
-     * scene root and then post a request to run a transition on the next frame.
-     * At that time, the new values in the scene root will be captured and changes
-     * will be animated. There is no need to create a Scene; it is implied by
-     * changes which take place between calling this method and the next frame when
-     * the transition begins.
-     *
-     * <p>Calling this method several times before the next frame (for example, if
-     * unrelated code also wants to make dynamic changes and run a transition on
-     * the same scene root), only the first call will trigger capturing values
-     * and exiting the current scene. Subsequent calls to the method with the
-     * same scene root during the same frame will be ignored.</p>
-     *
-     * <p>Passing in <code>null</code> for the transition parameter will
-     * cause the TransitionManager to use its default transition.</p>
-     *
-     * @param sceneRoot  The root of the View hierarchy to run the transition on.
-     * @param transition The transition to use for this change. A
-     *                   value of null causes the TransitionManager to use the default transition.
-     */
-    public static void beginDelayedTransition(@NonNull final ViewGroup sceneRoot,
-            @Nullable Transition transition) {
-        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);
-        }
-    }
-
-    /**
-     * Ends all pending and ongoing transitions on the specified scene root.
-     *
-     * @param sceneRoot The root of the View hierarchy to end transitions on.
-     */
-    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/android/support/transition/TransitionManagerTest.java b/android/support/transition/TransitionManagerTest.java
deleted file mode 100644
index dc4f983..0000000
--- a/android/support/transition/TransitionManagerTest.java
+++ /dev/null
@@ -1,183 +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 org.hamcrest.CoreMatchers.is;
-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;
-import android.support.transition.test.R;
-import android.view.ViewGroup;
-
-import org.junit.Before;
-import org.junit.Test;
-
-@MediumTest
-public class TransitionManagerTest extends BaseTest {
-
-    private Scene[] mScenes = new Scene[2];
-
-    @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 testSetup() {
-        assertThat(mScenes[0], is(notNullValue()));
-        assertThat(mScenes[1], is(notNullValue()));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testGo_enterAction() {
-        CheckCalledRunnable runnable = new CheckCalledRunnable();
-        mScenes[0].setEnterAction(runnable);
-        assertThat(runnable.wasCalled(), is(false));
-        TransitionManager.go(mScenes[0]);
-        assertThat(runnable.wasCalled(), is(true));
-    }
-
-    @Test
-    public void testGo_exitAction() throws Throwable {
-        final CheckCalledRunnable enter = new CheckCalledRunnable();
-        final CheckCalledRunnable exit = new CheckCalledRunnable();
-        mScenes[0].setEnterAction(enter);
-        mScenes[0].setExitAction(exit);
-        rule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertThat(enter.wasCalled(), is(false));
-                assertThat(exit.wasCalled(), is(false));
-                TransitionManager.go(mScenes[0]);
-                assertThat(enter.wasCalled(), is(true));
-                assertThat(exit.wasCalled(), is(false));
-            }
-        });
-        // Let the main thread catch up with the scene change
-        rule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                TransitionManager.go(mScenes[1]);
-                assertThat(exit.wasCalled(), is(true));
-            }
-        });
-    }
-
-    @Test
-    public void testGo_transitionListenerStart() throws Throwable {
-        final SyncTransitionListener listener =
-                new SyncTransitionListener(SyncTransitionListener.EVENT_START);
-        rule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Transition transition = new AutoTransition();
-                transition.setDuration(0);
-                assertThat(transition.addListener(listener), is(sameInstance(transition)));
-                TransitionManager.go(mScenes[0], transition);
-            }
-        });
-        assertThat("Timed out waiting for the TransitionListener",
-                listener.await(), is(true));
-    }
-
-    @Test
-    public void testGo_transitionListenerEnd() throws Throwable {
-        final SyncTransitionListener listener =
-                new SyncTransitionListener(SyncTransitionListener.EVENT_END);
-        rule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Transition transition = new AutoTransition();
-                transition.setDuration(0);
-                assertThat(transition.addListener(listener), is(sameInstance(transition)));
-                TransitionManager.go(mScenes[0], transition);
-            }
-        });
-        assertThat("Timed out waiting for the TransitionListener",
-                listener.await(), is(true));
-    }
-
-    @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]));
-            }
-        });
-    }
-
-    @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));
-    }
-
-    @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);
-            }
-        });
-        verify(listener, never()).onTransitionStart(any(Transition.class));
-        verify(listener, never()).onTransitionEnd(any(Transition.class));
-    }
-
-}
diff --git a/android/support/transition/TransitionPropagation.java b/android/support/transition/TransitionPropagation.java
deleted file mode 100644
index 76ce9b2..0000000
--- a/android/support/transition/TransitionPropagation.java
+++ /dev/null
@@ -1,91 +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.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/android/support/transition/TransitionSet.java b/android/support/transition/TransitionSet.java
deleted file mode 100644
index 24075bb..0000000
--- a/android/support/transition/TransitionSet.java
+++ /dev/null
@@ -1,601 +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.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
- * choreography of transitions, where some sets play {@link #ORDERING_TOGETHER} and
- * others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
- * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
- * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
- *
- * <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:transitionOrdering="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.
-     */
-    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;
-
-    /**
-     * Constructs an empty transition set. Add child transitions to the
-     * set by calling {@link #addTransition(Transition)} )}. By default,
-     * child transitions will play {@link #ORDERING_TOGETHER together}.
-     */
-    public TransitionSet() {
-    }
-
-    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();
-    }
-
-    /**
-     * Sets the play order of this set's child transitions.
-     *
-     * @param ordering {@link #ORDERING_TOGETHER} to play this set's child
-     *                 transitions together, {@link #ORDERING_SEQUENTIAL} to play the child
-     *                 transitions in sequence.
-     * @return This transitionSet object.
-     */
-    @NonNull
-    public TransitionSet 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;
-    }
-
-    /**
-     * 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
-     * order in which the transitions are started.
-     *
-     * <p>If this transitionSet has a {@link #getDuration() duration} set on it, the
-     * child transition will inherit that duration. Transitions are assumed to have
-     * a maximum of one transitionSet parent.</p>
-     *
-     * @param transition A non-null child transition to be added to this set.
-     * @return This transitionSet object.
-     */
-    @NonNull
-    public TransitionSet addTransition(@NonNull Transition transition) {
-        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.
-     * @return This transitionSet object.
-     */
-    @NonNull
-    public TransitionSet removeTransition(@NonNull Transition 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 (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
-    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) {
-        if (isValidTarget(transitionValues.view)) {
-            for (Transition childTransition : mTransitions) {
-                if (childTransition.isValidTarget(transitionValues.view)) {
-                    childTransition.captureStartValues(transitionValues);
-                    transitionValues.mTargetedTransitions.add(childTransition);
-                }
-            }
-        }
-    }
-
-    @Override
-    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 setPropagation(TransitionPropagation propagation) {
-        super.setPropagation(propagation);
-        int numTransitions = mTransitions.size();
-        for (int i = 0; i < numTransitions; ++i) {
-            mTransitions.get(i).setPropagation(propagation);
-        }
-    }
-
-    @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/android/support/transition/TransitionSetTest.java b/android/support/transition/TransitionSetTest.java
deleted file mode 100644
index d82cd49..0000000
--- a/android/support/transition/TransitionSetTest.java
+++ /dev/null
@@ -1,131 +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.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));
-    }
-
-    @Test
-    public void testSetPropagation() {
-        final TransitionPropagation propagation = new SidePropagation();
-        mTransitionSet.setPropagation(propagation);
-        assertThat(mTransitionSet.getPropagation(), is(propagation));
-        assertThat(mTransition.getPropagation(), is(propagation));
-    }
-
-}
diff --git a/android/support/transition/TransitionTest.java b/android/support/transition/TransitionTest.java
deleted file mode 100644
index 72f6dae..0000000
--- a/android/support/transition/TransitionTest.java
+++ /dev/null
@@ -1,442 +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 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;
-
-@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();
-        long duration = 12345;
-        assertThat(transition.setDuration(duration), is(sameInstance(transition)));
-        assertThat(transition.getDuration(), is(duration));
-    }
-
-    @Test
-    public void testInterpolator() {
-        Transition transition = new EmptyTransition();
-        TimeInterpolator interpolator = new LinearInterpolator();
-        assertThat(transition.setInterpolator(interpolator), is(sameInstance(transition)));
-        assertThat(transition.getInterpolator(), is(interpolator));
-    }
-
-    @Test
-    public void testStartDelay() {
-        Transition transition = new EmptyTransition();
-        long startDelay = 12345;
-        assertThat(transition.setStartDelay(startDelay), is(sameInstance(transition)));
-        assertThat(transition.getStartDelay(), is(startDelay));
-    }
-
-    @Test
-    public void testTargetIds() {
-        Transition transition = new EmptyTransition();
-        assertThat(transition.addTarget(R.id.view0), is(sameInstance(transition)));
-        assertThat(transition.addTarget(R.id.view1), is(sameInstance(transition)));
-        List<Integer> targetIds = transition.getTargetIds();
-        assertThat(targetIds.size(), is(2));
-        assertThat(targetIds, hasItem(R.id.view0));
-        assertThat(targetIds, hasItem(R.id.view1));
-        assertThat(transition.removeTarget(R.id.view0), is(sameInstance(transition)));
-        targetIds = transition.getTargetIds();
-        assertThat(targetIds.size(), is(1));
-        assertThat(targetIds, not(hasItem(R.id.view0)));
-        assertThat(targetIds, hasItem(R.id.view1));
-    }
-
-    @Test
-    @UiThreadTest
-    public void testTargetView() {
-        // Set up views
-        TransitionActivity activity = rule.getActivity();
-        ViewGroup root = activity.getRoot();
-        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);
-        // Test transition targets
-        Transition transition = new EmptyTransition();
-        assertThat(transition.addTarget(view0), is(sameInstance(transition)));
-        assertThat(transition.addTarget(view1), is(sameInstance(transition)));
-        List<View> targets = transition.getTargets();
-        assertThat(targets.size(), is(2));
-        assertThat(targets, hasItem(sameInstance(view0)));
-        assertThat(targets, hasItem(sameInstance(view1)));
-        assertThat(transition.removeTarget(view0), is(sameInstance(transition)));
-        targets = transition.getTargets();
-        assertThat(targets.size(), is(1));
-        assertThat(targets, not(hasItem(sameInstance(view0))));
-        assertThat(targets, hasItem(sameInstance(view1)));
-    }
-
-    @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();
-        assertThat(transition.addListener(listener), is(sameInstance(transition)));
-        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 {
-
-        @Override
-        public void captureEndValues(@NonNull TransitionValues transitionValues) {
-        }
-
-        @Override
-        public void captureStartValues(@NonNull TransitionValues transitionValues) {
-        }
-
-        @Override
-        public Animator createAnimator(@NonNull ViewGroup sceneRoot,
-                @Nullable TransitionValues startValues,
-                @Nullable TransitionValues endValues) {
-            return null;
-        }
-
-    }
-
-    public static class EmptyTransitionListener 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) {
-        }
-
-    }
-
-    /**
-     * 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/android/support/transition/TransitionUtils.java b/android/support/transition/TransitionUtils.java
deleted file mode 100644
index e1fe644..0000000
--- a/android/support/transition/TransitionUtils.java
+++ /dev/null
@@ -1,133 +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.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/android/support/transition/TransitionValues.java b/android/support/transition/TransitionValues.java
deleted file mode 100644
index aaec8d0..0000000
--- a/android/support/transition/TransitionValues.java
+++ /dev/null
@@ -1,88 +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.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/android/support/transition/TransitionValuesMaps.java b/android/support/transition/TransitionValuesMaps.java
deleted file mode 100644
index 98db792..0000000
--- a/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.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/android/support/transition/TranslationAnimationCreator.java b/android/support/transition/TranslationAnimationCreator.java
deleted file mode 100644
index 7f2fd1d..0000000
--- a/android/support/transition/TranslationAnimationCreator.java
+++ /dev/null
@@ -1,136 +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.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/android/support/transition/ViewGroupOverlayApi14.java b/android/support/transition/ViewGroupOverlayApi14.java
deleted file mode 100644
index 4c5579d..0000000
--- a/android/support/transition/ViewGroupOverlayApi14.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.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/android/support/transition/ViewGroupOverlayApi18.java b/android/support/transition/ViewGroupOverlayApi18.java
deleted file mode 100644
index a32d8be..0000000
--- a/android/support/transition/ViewGroupOverlayApi18.java
+++ /dev/null
@@ -1,60 +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.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/android/support/transition/ViewGroupOverlayImpl.java b/android/support/transition/ViewGroupOverlayImpl.java
deleted file mode 100644
index 82b1f6b..0000000
--- a/android/support/transition/ViewGroupOverlayImpl.java
+++ /dev/null
@@ -1,60 +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.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/android/support/transition/ViewGroupUtils.java b/android/support/transition/ViewGroupUtils.java
deleted file mode 100644
index 370e3a3..0000000
--- a/android/support/transition/ViewGroupUtils.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.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/android/support/transition/ViewGroupUtilsApi14.java b/android/support/transition/ViewGroupUtilsApi14.java
deleted file mode 100644
index e37a1cc..0000000
--- a/android/support/transition/ViewGroupUtilsApi14.java
+++ /dev/null
@@ -1,136 +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.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/android/support/transition/ViewGroupUtilsApi18.java b/android/support/transition/ViewGroupUtilsApi18.java
deleted file mode 100644
index 7aad4e1..0000000
--- a/android/support/transition/ViewGroupUtilsApi18.java
+++ /dev/null
@@ -1,67 +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.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/android/support/transition/ViewGroupUtilsImpl.java b/android/support/transition/ViewGroupUtilsImpl.java
deleted file mode 100644
index 8b8d8a2..0000000
--- a/android/support/transition/ViewGroupUtilsImpl.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.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/android/support/transition/ViewOverlayApi14.java b/android/support/transition/ViewOverlayApi14.java
deleted file mode 100644
index f163e45..0000000
--- a/android/support/transition/ViewOverlayApi14.java
+++ /dev/null
@@ -1,356 +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.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/android/support/transition/ViewOverlayApi18.java b/android/support/transition/ViewOverlayApi18.java
deleted file mode 100644
index c2bc4f0..0000000
--- a/android/support/transition/ViewOverlayApi18.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.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/android/support/transition/ViewOverlayImpl.java b/android/support/transition/ViewOverlayImpl.java
deleted file mode 100644
index b699970..0000000
--- a/android/support/transition/ViewOverlayImpl.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.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/android/support/transition/ViewUtils.java b/android/support/transition/ViewUtils.java
deleted file mode 100644
index 66c3076..0000000
--- a/android/support/transition/ViewUtils.java
+++ /dev/null
@@ -1,220 +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.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/android/support/transition/ViewUtilsApi14.java b/android/support/transition/ViewUtilsApi14.java
deleted file mode 100644
index f038c52..0000000
--- a/android/support/transition/ViewUtilsApi14.java
+++ /dev/null
@@ -1,151 +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.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/android/support/transition/ViewUtilsApi18.java b/android/support/transition/ViewUtilsApi18.java
deleted file mode 100644
index 9cfa668..0000000
--- a/android/support/transition/ViewUtilsApi18.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.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/android/support/transition/ViewUtilsApi19.java b/android/support/transition/ViewUtilsApi19.java
deleted file mode 100644
index 58814b7..0000000
--- a/android/support/transition/ViewUtilsApi19.java
+++ /dev/null
@@ -1,103 +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.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/android/support/transition/ViewUtilsApi21.java b/android/support/transition/ViewUtilsApi21.java
deleted file mode 100644
index c403235..0000000
--- a/android/support/transition/ViewUtilsApi21.java
+++ /dev/null
@@ -1,121 +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.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/android/support/transition/ViewUtilsApi22.java b/android/support/transition/ViewUtilsApi22.java
deleted file mode 100644
index f995422..0000000
--- a/android/support/transition/ViewUtilsApi22.java
+++ /dev/null
@@ -1,64 +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.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/android/support/transition/ViewUtilsImpl.java b/android/support/transition/ViewUtilsImpl.java
deleted file mode 100644
index 5fa4b75..0000000
--- a/android/support/transition/ViewUtilsImpl.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.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/android/support/transition/Visibility.java b/android/support/transition/Visibility.java
deleted file mode 100644
index 70fc0d7..0000000
--- a/android/support/transition/Visibility.java
+++ /dev/null
@@ -1,573 +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.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
- * {@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 Fade}, 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)} or
- * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},
- * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.
- */
-public abstract class Visibility extends Transition {
-
-    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 {
-    }
-
-    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 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) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(@NonNull 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
-     * Transition#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.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;
-    }
-
-    /**
-     * 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.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int 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;
-    }
-
-    /**
-     * 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.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues,
-            int startVisibility, TransitionValues endValues, int 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;
-    }
-
-    /**
-     * 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/android/support/transition/VisibilityPropagation.java b/android/support/transition/VisibilityPropagation.java
deleted file mode 100644
index c9ada2e..0000000
--- a/android/support/transition/VisibilityPropagation.java
+++ /dev/null
@@ -1,118 +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.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/android/support/transition/VisibilityTest.java b/android/support/transition/VisibilityTest.java
deleted file mode 100644
index dcfcbdc..0000000
--- a/android/support/transition/VisibilityTest.java
+++ /dev/null
@@ -1,200 +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 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/android/support/transition/WindowIdApi14.java b/android/support/transition/WindowIdApi14.java
deleted file mode 100644
index f112786..0000000
--- a/android/support/transition/WindowIdApi14.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.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/android/support/transition/WindowIdApi18.java b/android/support/transition/WindowIdApi18.java
deleted file mode 100644
index b8808f0..0000000
--- a/android/support/transition/WindowIdApi18.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.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/android/support/transition/WindowIdImpl.java b/android/support/transition/WindowIdImpl.java
deleted file mode 100644
index 2b5aa21..0000000
--- a/android/support/transition/WindowIdImpl.java
+++ /dev/null
@@ -1,23 +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.support.annotation.RequiresApi;
-
-@RequiresApi(14)
-interface WindowIdImpl {
-}
diff --git a/android/support/v13/app/ActivityCompat.java b/android/support/v13/app/ActivityCompat.java
deleted file mode 100644
index 7c1546a..0000000
--- a/android/support/v13/app/ActivityCompat.java
+++ /dev/null
@@ -1,38 +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;
-
-/**
- * Helper for accessing features in {@link android.app.Activity} in a backwards compatible fashion.
- *
- * @deprecated Use {@link android.support.v4.app.ActivityCompat
- * android.support.v4.app.ActivityCompat}.
- */
-@Deprecated
-public class ActivityCompat extends android.support.v4.app.ActivityCompat {
-    /**
-     * This class should not be instantiated, but the constructor must be
-     * visible for the class to be extended.
-     *
-     * @deprecated Use {@link android.support.v4.app.ActivityCompat
-     * android.support.v4.app.ActivityCompat}.
-     */
-    @Deprecated
-    protected ActivityCompat() {
-        // Not publicly instantiable, but may be extended.
-    }
-}
diff --git a/android/support/v13/app/FragmentCompat.java b/android/support/v13/app/FragmentCompat.java
deleted file mode 100644
index e8915fb..0000000
--- a/android/support/v13/app/FragmentCompat.java
+++ /dev/null
@@ -1,349 +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.app.Fragment;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-
-import java.util.Arrays;
-
-/**
- * Helper for accessing features in {@link Fragment} in a backwards compatible fashion.
- *
- * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework fragment.
- */
-@Deprecated
-public class FragmentCompat {
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework fragment.
-     */
-    @Deprecated
-    public FragmentCompat() {
-    }
-
-    interface FragmentCompatImpl {
-        void setUserVisibleHint(Fragment f, boolean deferStart);
-        void requestPermissions(Fragment fragment, String[] permissions, int requestCode);
-        boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission);
-    }
-
-    /**
-     * Customizable delegate that allows delegating permission related compatibility methods
-     * to a custom implementation.
-     *
-     * <p>
-     *     To delegate fragment compatibility methods to a custom class, implement this interface,
-     *     and call {@code FragmentCompat.setPermissionCompatDelegate(delegate);}. All future calls
-     *     to the compatibility methods in this class will first check whether the delegate can
-     *     handle the method call, and invoke the corresponding method if it can.
-     * </p>
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public interface PermissionCompatDelegate {
-
-        /**
-         * Determines whether the delegate should handle
-         * {@link FragmentCompat#requestPermissions(Fragment, String[], int)}, and request
-         * permissions if applicable. If this method returns true, it means that permission
-         * request is successfully handled by the delegate, and platform should not perform any
-         * further requests for permission.
-         *
-         * @param fragment The target fragment.
-         * @param permissions The requested permissions.
-         * @param requestCode Application specific request code to match with a result
-         *    reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
-         *    int, String[], int[])}.
-         *
-         * @return Whether the delegate has handled the permission request.
-         * @see FragmentCompat#requestPermissions(Fragment, String[], int)
-         *
-         * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-         * {@link Fragment}.
-         */
-        @Deprecated
-        boolean requestPermissions(Fragment fragment, String[] permissions, int requestCode);
-    }
-
-    static class FragmentCompatBaseImpl implements FragmentCompatImpl {
-        @Override
-        public void setUserVisibleHint(Fragment f, boolean deferStart) {
-        }
-        @Override
-        public void requestPermissions(final Fragment fragment, final String[] permissions,
-                final int requestCode) {
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    final int[] grantResults = new int[permissions.length];
-
-                    Context context = fragment.getActivity();
-                    if (context != null) {
-                        PackageManager packageManager = context.getPackageManager();
-                        String packageName = context.getPackageName();
-
-                        final int permissionCount = permissions.length;
-                        for (int i = 0; i < permissionCount; i++) {
-                            grantResults[i] = packageManager.checkPermission(
-                                    permissions[i], packageName);
-                        }
-                    } else {
-                        Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED);
-                    }
-
-                    ((OnRequestPermissionsResultCallback) fragment).onRequestPermissionsResult(
-                            requestCode, permissions, grantResults);
-                }
-            });
-        }
-        @Override
-        public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) {
-            return false;
-        }
-    }
-
-    @RequiresApi(15)
-    static class FragmentCompatApi15Impl extends FragmentCompatBaseImpl {
-        @Override
-        public void setUserVisibleHint(Fragment f, boolean deferStart) {
-            f.setUserVisibleHint(deferStart);
-        }
-    }
-
-    @RequiresApi(23)
-    static class FragmentCompatApi23Impl extends FragmentCompatApi15Impl {
-        @Override
-        public void requestPermissions(Fragment fragment, String[] permissions, int requestCode) {
-            fragment.requestPermissions(permissions, requestCode);
-        }
-
-        @Override
-        public boolean shouldShowRequestPermissionRationale(Fragment fragment, String permission) {
-            return fragment.shouldShowRequestPermissionRationale(permission);
-        }
-    }
-
-    @RequiresApi(24)
-    static class FragmentCompatApi24Impl extends FragmentCompatApi23Impl {
-        @Override
-        public void setUserVisibleHint(Fragment f, boolean deferStart) {
-            f.setUserVisibleHint(deferStart);
-        }
-    }
-
-    static final FragmentCompatImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 24) {
-            IMPL = new FragmentCompatApi24Impl();
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            IMPL = new FragmentCompatApi23Impl();
-        } else if (android.os.Build.VERSION.SDK_INT >= 15) {
-            IMPL = new FragmentCompatApi15Impl();
-        } else {
-            IMPL = new FragmentCompatBaseImpl();
-        }
-    }
-
-    private static PermissionCompatDelegate sDelegate;
-
-    /**
-     * Sets the permission delegate for {@code FragmentCompat}. Replaces the previously set
-     * delegate.
-     *
-     * @param delegate The delegate to be set. {@code null} to clear the set delegate.
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public static void setPermissionCompatDelegate(PermissionCompatDelegate delegate) {
-        sDelegate = delegate;
-    }
-
-    /**
-     * @hide
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @Deprecated
-    public static PermissionCompatDelegate getPermissionCompatDelegate() {
-        return sDelegate;
-    }
-
-    /**
-     * This interface is the contract for receiving the results for permission requests.
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public interface OnRequestPermissionsResultCallback {
-
-        /**
-         * Callback for the result from requesting permissions. This method
-         * is invoked for every call on {@link #requestPermissions(android.app.Fragment,
-         * String[], int)}
-         *
-         * @param requestCode The request code passed in {@link #requestPermissions(
-         *     android.app.Fragment, String[], int)}
-         * @param permissions The requested permissions. Never null.
-         * @param grantResults The grant results for the corresponding permissions
-         *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
-         *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
-         *
-         * @see #requestPermissions(android.app.Fragment, String[], int)
-         *
-         * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-         * {@link Fragment}.
-         */
-        @Deprecated
-        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
-                @NonNull int[] grantResults);
-    }
-
-    /**
-     * Call {@link Fragment#setMenuVisibility(boolean) Fragment.setMenuVisibility(boolean)}
-     * if running on an appropriate version of the platform.
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public static void setMenuVisibility(Fragment f, boolean visible) {
-        f.setMenuVisibility(visible);
-    }
-
-    /**
-     * Call {@link Fragment#setUserVisibleHint(boolean) setUserVisibleHint(boolean)}
-     * if running on an appropriate version of the platform.
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public static void setUserVisibleHint(Fragment f, boolean deferStart) {
-        IMPL.setUserVisibleHint(f, deferStart);
-    }
-
-    /**
-     * Requests permissions to be granted to this application. These permissions
-     * must be requested in your manifest, they should not be granted to your app,
-     * and they should have protection level {@link android.content.pm.PermissionInfo
-     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
-     * the platform or a third-party app.
-     * <p>
-     * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
-     * are granted at install time if requested in the manifest. Signature permissions
-     * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
-     * install time if requested in the manifest and the signature of your app matches
-     * the signature of the app declaring the permissions.
-     * </p>
-     * <p>
-     * If your app does not have the requested permissions the user will be presented
-     * with UI for accepting them. After the user has accepted or rejected the
-     * requested permissions you will receive a callback reporting whether the
-     * permissions were granted or not. Your fragment has to implement {@link
-     * OnRequestPermissionsResultCallback}
-     * and the results of permission requests will be delivered to its
-     * {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
-     * int, String[], int[])}.
-     * </p>
-     * <p>
-     * Note that requesting a permission does not guarantee it will be granted and
-     * your app should be able to run without having this permission.
-     * </p>
-     * <p>
-     * This method may start an activity allowing the user to choose which permissions
-     * to grant and which to reject. Hence, you should be prepared that your activity
-     * may be paused and resumed. Further, granting some permissions may require
-     * a restart of you application. In such a case, the system will recreate the
-     * activity stack before delivering the result to your onRequestPermissionsResult(
-     * int, String[], int[]).
-     * </p>
-     * <p>
-     * When checking whether you have a permission you should use {@link
-     * android.support.v4.content.ContextCompat#checkSelfPermission(
-     * android.content.Context, String)}.
-     * </p>
-     *
-     * @param fragment The target fragment.
-     * @param permissions The requested permissions.
-     * @param requestCode Application specific request code to match with a result
-     *    reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(
-     *    int, String[], int[])}.
-     *
-     * @see android.support.v4.content.ContextCompat#checkSelfPermission(
-     *     android.content.Context, String)
-     * @see #shouldShowRequestPermissionRationale(android.app.Fragment, String)
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public static void requestPermissions(@NonNull Fragment fragment,
-            @NonNull String[] permissions, int requestCode) {
-        if (sDelegate != null && sDelegate.requestPermissions(fragment, permissions, requestCode)) {
-            // Delegate has handled the request.
-            return;
-        }
-
-        IMPL.requestPermissions(fragment, permissions, requestCode);
-    }
-
-    /**
-     * Gets whether you should show UI with rationale for requesting a permission.
-     * You should do this only if you do not have the permission and the context in
-     * which the permission is requested does not clearly communicate to the user
-     * what would be the benefit from granting this permission.
-     * <p>
-     * For example, if you write a camera app, requesting the camera permission
-     * would be expected by the user and no rationale for why it is requested is
-     * needed. If however, the app needs location for tagging photos then a non-tech
-     * savvy user may wonder how location is related to taking photos. In this case
-     * you may choose to show UI with rationale of requesting this permission.
-     * </p>
-     *
-     * @param fragment The target fragment.
-     * @param permission A permission your app wants to request.
-     * @return Whether you can show permission rationale UI.
-     *
-     * @see android.support.v4.content.ContextCompat#checkSelfPermission(
-     *     android.content.Context, String)
-     * @see #requestPermissions(android.app.Fragment, String[], int)
-     *
-     * @deprecated Use {@link android.support.v4.app.Fragment} instead of the framework
-     * {@link Fragment}.
-     */
-    @Deprecated
-    public static boolean shouldShowRequestPermissionRationale(@NonNull Fragment fragment,
-            @NonNull String permission) {
-        return IMPL.shouldShowRequestPermissionRationale(fragment, permission);
-    }
-}
diff --git a/android/support/v13/app/FragmentPagerAdapter.java b/android/support/v13/app/FragmentPagerAdapter.java
deleted file mode 100644
index 112ed02..0000000
--- a/android/support/v13/app/FragmentPagerAdapter.java
+++ /dev/null
@@ -1,230 +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.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.os.Parcelable;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.support.v4.view.PagerAdapter;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Implementation of {@link android.support.v4.view.PagerAdapter} that
- * represents each page as a {@link android.app.Fragment} that is persistently
- * kept in the fragment manager as long as the user can return to the page.
- *
- * <p>This version of the pager is best for use when there are a handful of
- * typically more static fragments to be paged through, such as a set of tabs.
- * The fragment of each page the user visits will be kept in memory, though its
- * view hierarchy may be destroyed when not visible.  This can result in using
- * a significant amount of memory since fragment instances can hold on to an
- * arbitrary amount of state.  For larger sets of pages, consider
- * {@link FragmentStatePagerAdapter}.
- *
- * <p>When using FragmentPagerAdapter the host ViewPager must have a
- * valid ID set.</p>
- *
- * <p>Subclasses only need to implement {@link #getItem(int)}
- * and {@link #getCount()} to have a working adapter.
- *
- * <p>Here is an example implementation of a pager containing fragments of
- * lists:
- *
- * {@sample frameworks/support/samples/Support13Demos/src/main/java/com/example/android/supportv13/app/FragmentPagerSupport.java
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
- *
- * {@sample frameworks/support/samples/Support13Demos/src/main/res/layout/fragment_pager.xml
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager_list</code> resource containing each
- * individual fragment's layout is:
- *
- * {@sample frameworks/support/samples/Support13Demos/src/main/res/layout/fragment_pager_list.xml
- *      complete}
- *
- * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
- */
-@Deprecated
-public abstract class FragmentPagerAdapter extends PagerAdapter {
-    private static final String TAG = "FragmentPagerAdapter";
-    private static final boolean DEBUG = false;
-
-    private final FragmentManager mFragmentManager;
-    private FragmentTransaction mCurTransaction = null;
-    private Fragment mCurrentPrimaryItem = null;
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    public FragmentPagerAdapter(FragmentManager fm) {
-        mFragmentManager = fm;
-    }
-
-    /**
-     * Return the Fragment associated with a specified position.
-     *
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    public abstract Fragment getItem(int position);
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void startUpdate(ViewGroup container) {
-        if (container.getId() == View.NO_ID) {
-            throw new IllegalStateException("ViewPager with adapter " + this
-                    + " requires a view id");
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @SuppressWarnings("ReferenceEquality")
-    @Override
-    public Object instantiateItem(ViewGroup container, int position) {
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-
-        final long itemId = getItemId(position);
-
-        // Do we already have this fragment?
-        String name = makeFragmentName(container.getId(), itemId);
-        Fragment fragment = mFragmentManager.findFragmentByTag(name);
-        if (fragment != null) {
-            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
-            mCurTransaction.attach(fragment);
-        } else {
-            fragment = getItem(position);
-            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
-            mCurTransaction.add(container.getId(), fragment,
-                    makeFragmentName(container.getId(), itemId));
-        }
-        if (fragment != mCurrentPrimaryItem) {
-            fragment.setMenuVisibility(false);
-            FragmentCompat.setUserVisibleHint(fragment, false);
-        }
-
-        return fragment;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void destroyItem(ViewGroup container, int position, Object object) {
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
-                + " v=" + ((Fragment)object).getView());
-        mCurTransaction.detach((Fragment)object);
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @SuppressWarnings("ReferenceEquality")
-    @Override
-    public void setPrimaryItem(ViewGroup container, int position, Object object) {
-        Fragment fragment = (Fragment)object;
-        if (fragment != mCurrentPrimaryItem) {
-            if (mCurrentPrimaryItem != null) {
-                mCurrentPrimaryItem.setMenuVisibility(false);
-                FragmentCompat.setUserVisibleHint(mCurrentPrimaryItem, false);
-            }
-            if (fragment != null) {
-                fragment.setMenuVisibility(true);
-                FragmentCompat.setUserVisibleHint(fragment, true);
-            }
-            mCurrentPrimaryItem = fragment;
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void finishUpdate(ViewGroup container) {
-        if (mCurTransaction != null) {
-            mCurTransaction.commitAllowingStateLoss();
-            mCurTransaction = null;
-            mFragmentManager.executePendingTransactions();
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public boolean isViewFromObject(View view, Object object) {
-        return ((Fragment)object).getView() == view;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public Parcelable saveState() {
-        return null;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void restoreState(Parcelable state, ClassLoader loader) {
-    }
-
-    /**
-     * Return a unique identifier for the item at the given position.
-     *
-     * <p>The default implementation returns the given position.
-     * Subclasses should override this method if the positions of items can change.</p>
-     *
-     * @param position Position within this adapter
-     * @return Unique identifier for the item at position
-     *
-     * @deprecated Use {@link android.support.v4.app.FragmentPagerAdapter} instead.
-     */
-    @Deprecated
-    public long getItemId(int position) {
-        return position;
-    }
-
-    private static String makeFragmentName(int viewId, long id) {
-        return "android:switcher:" + viewId + ":" + id;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v13/app/FragmentStatePagerAdapter.java b/android/support/v13/app/FragmentStatePagerAdapter.java
deleted file mode 100644
index 76a3224..0000000
--- a/android/support/v13/app/FragmentStatePagerAdapter.java
+++ /dev/null
@@ -1,277 +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.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.PagerAdapter;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * Implementation of {@link android.support.v4.view.PagerAdapter} that
- * uses a {@link Fragment} to manage each page. This class also handles
- * saving and restoring of fragment's state.
- *
- * <p>This version of the pager is more useful when there are a large number
- * of pages, working more like a list view.  When pages are not visible to
- * the user, their entire fragment may be destroyed, only keeping the saved
- * state of that fragment.  This allows the pager to hold on to much less
- * memory associated with each visited page as compared to
- * {@link FragmentPagerAdapter} at the cost of potentially more overhead when
- * switching between pages.
- *
- * <p>When using FragmentPagerAdapter the host ViewPager must have a
- * valid ID set.</p>
- *
- * <p>Subclasses only need to implement {@link #getItem(int)}
- * and {@link #getCount()} to have a working adapter.
- *
- * <p>Here is an example implementation of a pager containing fragments of
- * lists:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentStatePagerSupport.java
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager.xml
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager_list</code> resource containing each
- * individual fragment's layout is:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager_list.xml
- *      complete}
- *
- * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
- */
-@Deprecated
-public abstract class FragmentStatePagerAdapter extends PagerAdapter {
-    private static final String TAG = "FragStatePagerAdapter";
-    private static final boolean DEBUG = false;
-
-    private final FragmentManager mFragmentManager;
-    private FragmentTransaction mCurTransaction = null;
-
-    private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
-    private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
-    private Fragment mCurrentPrimaryItem = null;
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    public FragmentStatePagerAdapter(FragmentManager fm) {
-        mFragmentManager = fm;
-    }
-
-    /**
-     * Return the Fragment associated with a specified position.
-     *
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    public abstract Fragment getItem(int position);
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void startUpdate(ViewGroup container) {
-        if (container.getId() == View.NO_ID) {
-            throw new IllegalStateException("ViewPager with adapter " + this
-                    + " requires a view id");
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public Object instantiateItem(ViewGroup container, int position) {
-        // If we already have this item instantiated, there is nothing
-        // to do.  This can happen when we are restoring the entire pager
-        // from its saved state, where the fragment manager has already
-        // taken care of restoring the fragments we previously had instantiated.
-        if (mFragments.size() > position) {
-            Fragment f = mFragments.get(position);
-            if (f != null) {
-                return f;
-            }
-        }
-
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-
-        Fragment fragment = getItem(position);
-        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
-        if (mSavedState.size() > position) {
-            Fragment.SavedState fss = mSavedState.get(position);
-            if (fss != null) {
-                fragment.setInitialSavedState(fss);
-            }
-        }
-        while (mFragments.size() <= position) {
-            mFragments.add(null);
-        }
-        fragment.setMenuVisibility(false);
-        FragmentCompat.setUserVisibleHint(fragment, false);
-        mFragments.set(position, fragment);
-        mCurTransaction.add(container.getId(), fragment);
-
-        return fragment;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void destroyItem(ViewGroup container, int position, Object object) {
-        Fragment fragment = (Fragment) object;
-
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
-                + " v=" + ((Fragment)object).getView());
-        while (mSavedState.size() <= position) {
-            mSavedState.add(null);
-        }
-        mSavedState.set(position, fragment.isAdded()
-                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
-        mFragments.set(position, null);
-
-        mCurTransaction.remove(fragment);
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @SuppressWarnings("ReferenceEquality")
-    @Override
-    public void setPrimaryItem(ViewGroup container, int position, Object object) {
-        Fragment fragment = (Fragment)object;
-        if (fragment != mCurrentPrimaryItem) {
-            if (mCurrentPrimaryItem != null) {
-                mCurrentPrimaryItem.setMenuVisibility(false);
-                FragmentCompat.setUserVisibleHint(mCurrentPrimaryItem, false);
-            }
-            if (fragment != null) {
-                fragment.setMenuVisibility(true);
-                FragmentCompat.setUserVisibleHint(fragment, true);
-            }
-            mCurrentPrimaryItem = fragment;
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void finishUpdate(ViewGroup container) {
-        if (mCurTransaction != null) {
-            mCurTransaction.commitAllowingStateLoss();
-            mCurTransaction = null;
-            mFragmentManager.executePendingTransactions();
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public boolean isViewFromObject(View view, Object object) {
-        return ((Fragment)object).getView() == view;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public Parcelable saveState() {
-        Bundle state = null;
-        if (mSavedState.size() > 0) {
-            state = new Bundle();
-            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
-            mSavedState.toArray(fss);
-            state.putParcelableArray("states", fss);
-        }
-        for (int i=0; i<mFragments.size(); i++) {
-            Fragment f = mFragments.get(i);
-            if (f != null && f.isAdded()) {
-                if (state == null) {
-                    state = new Bundle();
-                }
-                String key = "f" + i;
-                mFragmentManager.putFragment(state, key, f);
-            }
-        }
-        return state;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentStatePagerAdapter} instead.
-     */
-    @Deprecated
-    @Override
-    public void restoreState(Parcelable state, ClassLoader loader) {
-        if (state != null) {
-            Bundle bundle = (Bundle)state;
-            bundle.setClassLoader(loader);
-            Parcelable[] fss = bundle.getParcelableArray("states");
-            mSavedState.clear();
-            mFragments.clear();
-            if (fss != null) {
-                for (int i=0; i<fss.length; i++) {
-                    mSavedState.add((Fragment.SavedState)fss[i]);
-                }
-            }
-            Iterable<String> keys = bundle.keySet();
-            for (String key: keys) {
-                if (key.startsWith("f")) {
-                    int index = Integer.parseInt(key.substring(1));
-                    Fragment f = mFragmentManager.getFragment(bundle, key);
-                    if (f != null) {
-                        while (mFragments.size() <= index) {
-                            mFragments.add(null);
-                        }
-                        FragmentCompat.setMenuVisibility(f, false);
-                        mFragments.set(index, f);
-                    } else {
-                        Log.w(TAG, "Bad fragment at key " + key);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v13/app/FragmentTabHost.java b/android/support/v13/app/FragmentTabHost.java
deleted file mode 100644
index 5c34ab5..0000000
--- a/android/support/v13/app/FragmentTabHost.java
+++ /dev/null
@@ -1,398 +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.v13.app;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.TabHost;
-import android.widget.TabWidget;
-
-import java.util.ArrayList;
-
-/**
- * Version of {@link android.support.v4.app.FragmentTabHost} that can be
- * used with the platform {@link android.app.Fragment} APIs.  You will not
- * normally use this, instead using action bar tabs.
- *
- * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
- */
-@Deprecated
-public class FragmentTabHost extends TabHost implements TabHost.OnTabChangeListener {
-    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
-    private FrameLayout mRealTabContent;
-    private Context mContext;
-    private FragmentManager mFragmentManager;
-    private int mContainerId;
-    private TabHost.OnTabChangeListener mOnTabChangeListener;
-    private TabInfo mLastTab;
-    private boolean mAttached;
-
-    static final class TabInfo {
-        final String tag;
-        final Class<?> clss;
-        final Bundle args;
-        Fragment fragment;
-
-        TabInfo(String _tag, Class<?> _class, Bundle _args) {
-            tag = _tag;
-            clss = _class;
-            args = _args;
-        }
-    }
-
-    static class DummyTabFactory implements TabHost.TabContentFactory {
-        private final Context mContext;
-
-        public DummyTabFactory(Context context) {
-            mContext = context;
-        }
-
-        @Override
-        public View createTabContent(String tag) {
-            View v = new View(mContext);
-            v.setMinimumWidth(0);
-            v.setMinimumHeight(0);
-            return v;
-        }
-    }
-
-    static class SavedState extends BaseSavedState {
-        String curTab;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        SavedState(Parcel in) {
-            super(in);
-            curTab = in.readString();
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeString(curTab);
-        }
-
-        @Override
-        public String toString() {
-            return "FragmentTabHost.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " curTab=" + curTab + "}";
-        }
-
-        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];
-            }
-        };
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    public FragmentTabHost(Context context) {
-        // Note that we call through to the version that takes an AttributeSet,
-        // because the simple Context construct can result in a broken object!
-        super(context, null);
-        initFragmentTabHost(context, null);
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    public FragmentTabHost(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        initFragmentTabHost(context, attrs);
-    }
-
-    private void initFragmentTabHost(Context context, AttributeSet attrs) {
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                new int[] { android.R.attr.inflatedId }, 0, 0);
-        mContainerId = a.getResourceId(0, 0);
-        a.recycle();
-
-        super.setOnTabChangedListener(this);
-    }
-
-    private void ensureHierarchy(Context context) {
-        // If owner hasn't made its own view hierarchy, then as a convenience
-        // we will construct a standard one here.
-        if (findViewById(android.R.id.tabs) == null) {
-            LinearLayout ll = new LinearLayout(context);
-            ll.setOrientation(LinearLayout.VERTICAL);
-            addView(ll, new FrameLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.MATCH_PARENT));
-
-            TabWidget tw = new TabWidget(context);
-            tw.setId(android.R.id.tabs);
-            tw.setOrientation(TabWidget.HORIZONTAL);
-            ll.addView(tw, new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0));
-
-            FrameLayout fl = new FrameLayout(context);
-            fl.setId(android.R.id.tabcontent);
-            ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));
-
-            mRealTabContent = fl = new FrameLayout(context);
-            mRealTabContent.setId(mContainerId);
-            ll.addView(fl, new LinearLayout.LayoutParams(
-                    LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Override
-    @Deprecated
-    public void setup() {
-        throw new IllegalStateException(
-                "Must call setup() that takes a Context and FragmentManager");
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    public void setup(Context context, FragmentManager manager) {
-        ensureHierarchy(context);  // Ensure views required by super.setup()
-        super.setup();
-        mContext = context;
-        mFragmentManager = manager;
-        ensureContent();
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    public void setup(Context context, FragmentManager manager, int containerId) {
-        ensureHierarchy(context);  // Ensure views required by super.setup()
-        super.setup();
-        mContext = context;
-        mFragmentManager = manager;
-        mContainerId = containerId;
-        ensureContent();
-        mRealTabContent.setId(containerId);
-
-        // We must have an ID to be able to save/restore our state.  If
-        // the owner hasn't set one at this point, we will set it ourselves.
-        if (getId() == View.NO_ID) {
-            setId(android.R.id.tabhost);
-        }
-    }
-
-    private void ensureContent() {
-        if (mRealTabContent == null) {
-            mRealTabContent = (FrameLayout)findViewById(mContainerId);
-            if (mRealTabContent == null) {
-                throw new IllegalStateException(
-                        "No tab content FrameLayout found for id " + mContainerId);
-            }
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    @Override
-    public void setOnTabChangedListener(OnTabChangeListener l) {
-        mOnTabChangeListener = l;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
-        tabSpec.setContent(new DummyTabFactory(mContext));
-        String tag = tabSpec.getTag();
-
-        TabInfo info = new TabInfo(tag, clss, args);
-
-        if (mAttached) {
-            // If we are already attached to the window, then check to make
-            // sure this tab's fragment is inactive if it exists.  This shouldn't
-            // normally happen.
-            info.fragment = mFragmentManager.findFragmentByTag(tag);
-            if (info.fragment != null && !info.fragment.isDetached()) {
-                FragmentTransaction ft = mFragmentManager.beginTransaction();
-                ft.detach(info.fragment);
-                ft.commit();
-            }
-        }
-
-        mTabs.add(info);
-        addTab(tabSpec);
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        String currentTab = getCurrentTabTag();
-
-        // Go through all tabs and make sure their fragments match
-        // the correct state.
-        FragmentTransaction ft = null;
-        for (int i=0; i<mTabs.size(); i++) {
-            TabInfo tab = mTabs.get(i);
-            tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
-            if (tab.fragment != null && !tab.fragment.isDetached()) {
-                if (tab.tag.equals(currentTab)) {
-                    // The fragment for this tab is already there and
-                    // active, and it is what we really want to have
-                    // as the current tab.  Nothing to do.
-                    mLastTab = tab;
-                } else {
-                    // This fragment was restored in the active state,
-                    // but is not the current tab.  Deactivate it.
-                    if (ft == null) {
-                        ft = mFragmentManager.beginTransaction();
-                    }
-                    ft.detach(tab.fragment);
-                }
-            }
-        }
-
-        // We are now ready to go.  Make sure we are switched to the
-        // correct tab.
-        mAttached = true;
-        ft = doTabChanged(currentTab, ft);
-        if (ft != null) {
-            ft.commit();
-            mFragmentManager.executePendingTransactions();
-        }
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mAttached = false;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        SavedState ss = new SavedState(superState);
-        ss.curTab = getCurrentTabTag();
-        return ss;
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-        SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-        setCurrentTabByTag(ss.curTab);
-    }
-
-    /**
-     * @deprecated Use {@link android.support.v4.app.FragmentTabHost} instead.
-     */
-    @Deprecated
-    @Override
-    public void onTabChanged(String tabId) {
-        if (mAttached) {
-            FragmentTransaction ft = doTabChanged(tabId, null);
-            if (ft != null) {
-                ft.commit();
-            }
-        }
-        if (mOnTabChangeListener != null) {
-            mOnTabChangeListener.onTabChanged(tabId);
-        }
-    }
-
-    private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {
-        TabInfo newTab = null;
-        for (int i=0; i<mTabs.size(); i++) {
-            TabInfo tab = mTabs.get(i);
-            if (tab.tag.equals(tabId)) {
-                newTab = tab;
-            }
-        }
-        if (newTab == null) {
-            throw new IllegalStateException("No tab known for tag " + tabId);
-        }
-        if (mLastTab != newTab) {
-            if (ft == null) {
-                ft = mFragmentManager.beginTransaction();
-            }
-            if (mLastTab != null) {
-                if (mLastTab.fragment != null) {
-                    ft.detach(mLastTab.fragment);
-                }
-            }
-            if (newTab != null) {
-                if (newTab.fragment == null) {
-                    newTab.fragment = Fragment.instantiate(mContext,
-                            newTab.clss.getName(), newTab.args);
-                    ft.add(mContainerId, newTab.fragment, newTab.tag);
-                } else {
-                    ft.attach(newTab.fragment);
-                }
-            }
-
-            mLastTab = newTab;
-        }
-        return ft;
-    }
-}
diff --git a/android/support/v13/view/DragAndDropPermissionsCompat.java b/android/support/v13/view/DragAndDropPermissionsCompat.java
deleted file mode 100644
index 5fe61da..0000000
--- a/android/support/v13/view/DragAndDropPermissionsCompat.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.v13.view;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.view.DragAndDropPermissions;
-import android.view.DragEvent;
-
-/**
- * Helper for accessing features in {@link android.view.DragAndDropPermissions} a backwards
- * compatible fashion.
- */
-public final class DragAndDropPermissionsCompat {
-    private Object mDragAndDropPermissions;
-
-    private DragAndDropPermissionsCompat(Object dragAndDropPermissions) {
-        mDragAndDropPermissions = dragAndDropPermissions;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    public static DragAndDropPermissionsCompat request(Activity activity, DragEvent dragEvent) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            DragAndDropPermissions dragAndDropPermissions =
-                    activity.requestDragAndDropPermissions(dragEvent);
-            if (dragAndDropPermissions != null) {
-                return new DragAndDropPermissionsCompat(dragAndDropPermissions);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Revoke the permission grant explicitly.
-     */
-    public void release() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            ((DragAndDropPermissions) mDragAndDropPermissions).release();
-        }
-    }
-}
diff --git a/android/support/v13/view/DragStartHelper.java b/android/support/v13/view/DragStartHelper.java
deleted file mode 100644
index f8aed92..0000000
--- a/android/support/v13/view/DragStartHelper.java
+++ /dev/null
@@ -1,197 +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.graphics.Point;
-import android.support.v4.view.InputDeviceCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * DragStartHelper is a utility class for implementing drag and drop support.
- * <p>
- * It detects gestures commonly used to start drag (long click for any input source,
- * click and drag for mouse).
- * <p>
- * It also keeps track of the screen location where the drag started, and helps determining
- * the hot spot position for a drag shadow.
- * <p>
- * Implement {@link DragStartHelper.OnDragStartListener} to start the drag operation:
- * <pre>
- * DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener {
- *     protected void onDragStart(View view, DragStartHelper helper) {
- *         View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
- *             public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
- *                 super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
- *                 helper.getTouchPosition(shadowTouchPoint);
- *             }
- *         };
- *         view.startDrag(mClipData, shadowBuilder, mLocalState, mDragFlags);
- *     }
- * };
- * mDragStartHelper = new DragStartHelper(mDraggableView, listener);
- * </pre>
- * Once created, DragStartHelper can be attached to a view (this will replace existing long click
- * and touch listeners):
- * <pre>
- * mDragStartHelper.attach();
- * </pre>
- * It may also be used in combination with existing listeners:
- * <pre>
- * public boolean onTouch(View view, MotionEvent event) {
- *     if (mDragStartHelper.onTouch(view, event)) {
- *         return true;
- *     }
- *     return handleTouchEvent(view, event);
- * }
- * public boolean onLongClick(View view) {
- *     if (mDragStartHelper.onLongClick(view)) {
- *         return true;
- *     }
- *     return handleLongClickEvent(view);
- * }
- * </pre>
- */
-public class DragStartHelper {
-    private final View mView;
-    private final OnDragStartListener mListener;
-
-    private int mLastTouchX, mLastTouchY;
-    private boolean mDragging;
-
-    /**
-     * Interface definition for a callback to be invoked when a drag start gesture is detected.
-     */
-    public interface OnDragStartListener {
-        /**
-         * Called when a drag start gesture has been detected.
-         *
-         * @param v The view over which the drag start gesture has been detected.
-         * @param helper The DragStartHelper object which detected the gesture.
-         * @return True if the listener has started the drag operation, false otherwise.
-         */
-        boolean onDragStart(View v, DragStartHelper helper);
-    }
-
-    /**
-     * Create a DragStartHelper associated with the specified view.
-     * The newly created helper is not initially attached to the view, {@link #attach} must be
-     * called explicitly.
-     * @param view A View
-     */
-    public DragStartHelper(View view, OnDragStartListener listener) {
-        mView = view;
-        mListener = listener;
-    }
-
-    /**
-     * Attach the helper to the view.
-     * <p>
-     * This will replace previously existing touch and long click listeners.
-     */
-    public void attach() {
-        mView.setOnLongClickListener(mLongClickListener);
-        mView.setOnTouchListener(mTouchListener);
-    }
-
-    /**
-     * Detach the helper from the view.
-     * <p>
-     * This will reset touch and long click listeners to {@code null}.
-     */
-    public void detach() {
-        mView.setOnLongClickListener(null);
-        mView.setOnTouchListener(null);
-    }
-
-    /**
-     * Handle a touch event.
-     * @param v The view the touch event has been dispatched to.
-     * @param event The MotionEvent object containing full information about
-     *        the event.
-     * @return True if the listener has consumed the event, false otherwise.
-     */
-    public boolean onTouch(View v, MotionEvent event) {
-        final int x = (int) event.getX();
-        final int y = (int) event.getY();
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                mLastTouchX = x;
-                mLastTouchY = y;
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (!MotionEventCompat.isFromSource(event, InputDeviceCompat.SOURCE_MOUSE)
-                        || (event.getButtonState()
-                                & MotionEvent.BUTTON_PRIMARY) == 0) {
-                    break;
-                }
-                if (mDragging) {
-                    // Ignore ACTION_MOVE events once the drag operation is in progress.
-                    break;
-                }
-                if (mLastTouchX == x && mLastTouchY == y) {
-                    // Do not call the listener unless the pointer position has actually changed.
-                    break;
-                }
-                mLastTouchX = x;
-                mLastTouchY = y;
-                mDragging = mListener.onDragStart(v, this);
-                return mDragging;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mDragging = false;
-                break;
-        }
-        return false;
-    }
-
-    /**
-     * Handle a long click event.
-     * @param v The view that was clicked and held.
-     * @return true if the callback consumed the long click, false otherwise.
-     */
-    public boolean onLongClick(View v) {
-        return mListener.onDragStart(v, this);
-    }
-
-    /**
-     * Compute the position of the touch event that started the drag operation.
-     * @param point The position of the touch event that started the drag operation.
-     */
-    public void getTouchPosition(Point point) {
-        point.set(mLastTouchX, mLastTouchY);
-    }
-
-    private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
-        @Override
-        public boolean onLongClick(View v) {
-            return DragStartHelper.this.onLongClick(v);
-        }
-    };
-
-    private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View v, MotionEvent event) {
-            return DragStartHelper.this.onTouch(v, event);
-        }
-    };
-}
-
diff --git a/android/support/v13/view/ViewCompat.java b/android/support/v13/view/ViewCompat.java
deleted file mode 100644
index 2b7906a..0000000
--- a/android/support/v13/view/ViewCompat.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.v13.view;
-
-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.
- */
-@Deprecated
-public class ViewCompat extends android.support.v4.view.ViewCompat {
-    private ViewCompat() {
-    }
-}
diff --git a/android/support/v13/view/inputmethod/EditorInfoCompat.java b/android/support/v13/view/inputmethod/EditorInfoCompat.java
deleted file mode 100644
index 309877d..0000000
--- a/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ /dev/null
@@ -1,122 +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.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.inputmethod.EditorInfo;
-
-/**
- * Helper for accessing features in {@link EditorInfo} in a backwards compatible fashion.
- */
-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 static final String[] EMPTY_STRING_ARRAY = new String[0];
-
-    private static final String CONTENT_MIME_TYPES_KEY =
-            "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
-
-    /**
-     * Sets MIME types that can be accepted by the target editor if the IME calls
-     * {@link InputConnectionCompat#commitContent(InputConnection, EditorInfo,
-     * InputContentInfoCompat, int, Bundle)}.
-     *
-     * @param editorInfo the editor with which we associate supported MIME types
-     * @param contentMimeTypes an array of MIME types. {@code null} and an empty array means that
-     *                         {@link InputConnectionCompat#commitContent(
-     *                         InputConnection, EditorInfo, InputContentInfoCompat, int, Bundle)}
-     *                         is not supported on this Editor
-     */
-    public static void setContentMimeTypes(@NonNull EditorInfo editorInfo,
-            @Nullable String[] contentMimeTypes) {
-        if (Build.VERSION.SDK_INT >= 25) {
-            editorInfo.contentMimeTypes = contentMimeTypes;
-        } else {
-            if (editorInfo.extras == null) {
-                editorInfo.extras = new Bundle();
-            }
-            editorInfo.extras.putStringArray(CONTENT_MIME_TYPES_KEY, contentMimeTypes);
-        }
-    }
-
-    /**
-     * Gets MIME types that can be accepted by the target editor if the IME calls
-     * {@link InputConnectionCompat#commitContent(InputConnection, EditorInfo,
-     * InputContentInfoCompat, int, Bundle)}
-     *
-     * @param editorInfo the editor from which we get the MIME types
-     * @return an array of MIME types. An empty array means that {@link
-     * InputConnectionCompat#commitContent(InputConnection, EditorInfo, InputContentInfoCompat,
-     * int, Bundle)} is not supported on this editor
-     */
-    @NonNull
-    public static String[] getContentMimeTypes(EditorInfo editorInfo) {
-        if (Build.VERSION.SDK_INT >= 25) {
-            final String[] result = editorInfo.contentMimeTypes;
-            return result != null ? result : EMPTY_STRING_ARRAY;
-        } else {
-            if (editorInfo.extras == null) {
-                return EMPTY_STRING_ARRAY;
-            }
-            String[] result = editorInfo.extras.getStringArray(CONTENT_MIME_TYPES_KEY);
-            return result != null ? result : EMPTY_STRING_ARRAY;
-        }
-    }
-
-}
diff --git a/android/support/v13/view/inputmethod/InputConnectionCompat.java b/android/support/v13/view/inputmethod/InputConnectionCompat.java
deleted file mode 100644
index d77389b..0000000
--- a/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ /dev/null
@@ -1,233 +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.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.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.
- */
-public final class InputConnectionCompat {
-
-    private static final String COMMIT_CONTENT_ACTION =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.COMMIT_CONTENT";
-    private static final String COMMIT_CONTENT_CONTENT_URI_KEY =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_URI";
-    private static final String COMMIT_CONTENT_DESCRIPTION_KEY =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_DESCRIPTION";
-    private static final String COMMIT_CONTENT_LINK_URI_KEY =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_LINK_URI";
-    private static final String COMMIT_CONTENT_OPTS_KEY =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_OPTS";
-    private static final String COMMIT_CONTENT_FLAGS_KEY =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_FLAGS";
-    private static final String COMMIT_CONTENT_RESULT_RECEIVER =
-            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_RESULT_RECEIVER";
-
-    static boolean handlePerformPrivateCommand(
-            @Nullable String action,
-            @NonNull Bundle data,
-            @NonNull OnCommitContentListener onCommitContentListener) {
-        if (!TextUtils.equals(COMMIT_CONTENT_ACTION, action)) {
-            return false;
-        }
-        if (data == null) {
-            return false;
-        }
-        ResultReceiver resultReceiver = null;
-        boolean result = false;
-        try {
-            resultReceiver = data.getParcelable(COMMIT_CONTENT_RESULT_RECEIVER);
-            final Uri contentUri = data.getParcelable(COMMIT_CONTENT_CONTENT_URI_KEY);
-            final ClipDescription description = data.getParcelable(
-                    COMMIT_CONTENT_DESCRIPTION_KEY);
-            final Uri linkUri = data.getParcelable(COMMIT_CONTENT_LINK_URI_KEY);
-            final int flags = data.getInt(COMMIT_CONTENT_FLAGS_KEY);
-            final Bundle opts = data.getParcelable(COMMIT_CONTENT_OPTS_KEY);
-            final InputContentInfoCompat inputContentInfo =
-                    new InputContentInfoCompat(contentUri, description, linkUri);
-            result = onCommitContentListener.onCommitContent(inputContentInfo, flags, opts);
-        } finally {
-            if (resultReceiver != null) {
-                resultReceiver.send(result ? 1 : 0, null);
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Calls commitContent API, in a backwards compatible fashion.
-     *
-     * @param inputConnection {@link InputConnection} with which commitContent API will be called
-     * @param editorInfo {@link EditorInfo} associated with the given {@code inputConnection}
-     * @param inputContentInfo content information to be passed to the editor
-     * @param flags {@code 0} or {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION}
-     * @param opts optional bundle data. This can be {@code null}
-     * @return {@code true} if this request is accepted by the application, no matter if the request
-     * is already handled or still being handled in background
-     */
-    public static boolean commitContent(@NonNull InputConnection inputConnection,
-            @NonNull EditorInfo editorInfo, @NonNull InputContentInfoCompat inputContentInfo,
-            int flags, @Nullable Bundle opts) {
-        final ClipDescription description = inputContentInfo.getDescription();
-        boolean supported = false;
-        for (String mimeType : EditorInfoCompat.getContentMimeTypes(editorInfo)) {
-            if (description.hasMimeType(mimeType)) {
-                supported = true;
-                break;
-            }
-        }
-        if (!supported) {
-            return false;
-        }
-
-        if (Build.VERSION.SDK_INT >= 25) {
-            return inputConnection.commitContent(
-                    (InputContentInfo) inputContentInfo.unwrap(), flags, opts);
-        } else {
-            final Bundle params = new Bundle();
-            params.putParcelable(COMMIT_CONTENT_CONTENT_URI_KEY, inputContentInfo.getContentUri());
-            params.putParcelable(COMMIT_CONTENT_DESCRIPTION_KEY, inputContentInfo.getDescription());
-            params.putParcelable(COMMIT_CONTENT_LINK_URI_KEY, inputContentInfo.getLinkUri());
-            params.putInt(COMMIT_CONTENT_FLAGS_KEY, flags);
-            params.putParcelable(COMMIT_CONTENT_OPTS_KEY, opts);
-            // TODO: Support COMMIT_CONTENT_RESULT_RECEIVER.
-            return inputConnection.performPrivateCommand(COMMIT_CONTENT_ACTION, params);
-        }
-    }
-
-    /**
-     * When this flag is used, the editor will be able to request temporary access permissions to
-     * the content URI contained in the {@link InputContentInfoCompat} object, in a similar manner
-     * that has been recommended in
-     * <a href="{@docRoot}training/secure-file-sharing/index.html">Sharing Files</a>.
-     *
-     * <p>Make sure that the content provider owning the Uri sets the
-     * {@link android.R.attr#grantUriPermissions grantUriPermissions} attribute in its manifest or
-     * included the {@code &lt;grant-uri-permissions&gt;} tag.</p>
-     *
-     * <p>Supported only on API &gt;= 25.</p>
-     *
-     * <p>On API &lt;= 24 devices, IME developers need to ensure that the content URI is accessible
-     * only from the target application, for example, by generating a URL with a unique name that
-     * others cannot guess. IME developers can also rely on the following information of the target
-     * application to do additional access checks in their {@link android.content.ContentProvider}.
-     * </p>
-     * <ul>
-     *     <li>On API &gt;= 23 {@link EditorInfo#packageName} is guaranteed to not be spoofed, which
-     *     can later be compared with {@link android.content.ContentProvider#getCallingPackage()} in
-     *     the {@link android.content.ContentProvider}.
-     *     </li>
-     *     <li>{@link android.view.inputmethod.InputBinding#getUid()} is guaranteed to not be
-     *     spoofed, which can later be compared with {@link android.os.Binder#getCallingUid()} in
-     *     the {@link android.content.ContentProvider}.</li>
-     * </ul>
-     */
-    public static int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 0x00000001;
-
-    /**
-     * Listener for commitContent method call, in a backwards compatible fashion.
-     */
-    public interface OnCommitContentListener {
-        /**
-         * Intercepts InputConnection#commitContent API calls.
-         *
-         * @param inputContentInfo content to be committed
-         * @param flags {@code 0} or {@link #INPUT_CONTENT_GRANT_READ_URI_PERMISSION}
-         * @param opts optional bundle data. This can be {@code null}
-         * @return {@code true} if this request is accepted by the application, no matter if the
-         * request is already handled or still being handled in background. {@code false} to use the
-         * default implementation
-         */
-        boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts);
-    }
-
-    /**
-     * Creates a wrapper {@link InputConnection} object from an existing {@link InputConnection}
-     * and {@link OnCommitContentListener} that can be returned to the system.
-     *
-     * <p>By returning the wrapper object to the IME, the editor can be notified by
-     * {@link OnCommitContentListener#onCommitContent(InputContentInfoCompat, int, Bundle)}
-     * when the IME calls
-     * {@link InputConnectionCompat#commitContent(InputConnection, EditorInfo,
-     * InputContentInfoCompat, int, Bundle)} and the corresponding Framework API that is available
-     * on API &gt;= 25.</p>
-     *
-     * @param inputConnection {@link InputConnection} to be wrapped
-     * @param editorInfo {@link EditorInfo} associated with the given {@code inputConnection}
-     * @param onCommitContentListener the listener that the wrapper object will call
-     * @return a wrapper {@link InputConnection} object that can be returned to the IME
-     * @throws IllegalArgumentException when {@code inputConnection}, {@code editorInfo}, or
-     * {@code onCommitContentListener} is {@code null}
-     */
-    @NonNull
-    public static InputConnection createWrapper(@NonNull InputConnection inputConnection,
-            @NonNull EditorInfo editorInfo,
-            @NonNull OnCommitContentListener onCommitContentListener) {
-        if (inputConnection == null) {
-            throw new IllegalArgumentException("inputConnection must be non-null");
-        }
-        if (editorInfo == null) {
-            throw new IllegalArgumentException("editorInfo must be non-null");
-        }
-        if (onCommitContentListener == null) {
-            throw new IllegalArgumentException("onCommitContentListener must be non-null");
-        }
-        if (Build.VERSION.SDK_INT >= 25) {
-            final OnCommitContentListener listener = onCommitContentListener;
-            return new InputConnectionWrapper(inputConnection, false /* mutable */) {
-                @Override
-                public boolean commitContent(InputContentInfo inputContentInfo, int flags,
-                        Bundle opts) {
-                    if (listener.onCommitContent(InputContentInfoCompat.wrap(inputContentInfo),
-                            flags, opts)) {
-                        return true;
-                    }
-                    return super.commitContent(inputContentInfo, flags, opts);
-                }
-            };
-        } else {
-            String[] contentMimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);
-            if (contentMimeTypes.length == 0) {
-                return inputConnection;
-            }
-            final OnCommitContentListener listener = onCommitContentListener;
-            return new InputConnectionWrapper(inputConnection, false /* mutable */) {
-                @Override
-                public boolean performPrivateCommand(String action, Bundle data) {
-                    if (InputConnectionCompat.handlePerformPrivateCommand(action, data, listener)) {
-                        return true;
-                    }
-                    return super.performPrivateCommand(action, data);
-                }
-            };
-        }
-    }
-
-}
diff --git a/android/support/v13/view/inputmethod/InputContentInfoCompat.java b/android/support/v13/view/inputmethod/InputContentInfoCompat.java
deleted file mode 100644
index 74b4775..0000000
--- a/android/support/v13/view/inputmethod/InputContentInfoCompat.java
+++ /dev/null
@@ -1,257 +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.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.view.inputmethod.InputContentInfo;
-
-/**
- * Helper for accessing features in InputContentInfo introduced after API level 13 in a backwards
- * compatible fashion.
- */
-public final class InputContentInfoCompat {
-
-    private interface InputContentInfoCompatImpl {
-        @NonNull
-        Uri getContentUri();
-
-        @NonNull
-        ClipDescription getDescription();
-
-        @Nullable
-        Uri getLinkUri();
-
-        @Nullable
-        Object getInputContentInfo();
-
-        void requestPermission();
-
-        void releasePermission();
-    }
-
-    private static final class InputContentInfoCompatBaseImpl
-            implements InputContentInfoCompatImpl {
-        @NonNull
-        private final Uri mContentUri;
-        @NonNull
-        private final ClipDescription mDescription;
-        @Nullable
-        private final Uri mLinkUri;
-
-        InputContentInfoCompatBaseImpl(@NonNull Uri contentUri,
-                @NonNull ClipDescription description, @Nullable Uri linkUri) {
-            mContentUri = contentUri;
-            mDescription = description;
-            mLinkUri = linkUri;
-        }
-
-        @NonNull
-        @Override
-        public Uri getContentUri() {
-            return mContentUri;
-        }
-
-        @NonNull
-        @Override
-        public ClipDescription getDescription() {
-            return mDescription;
-        }
-
-        @Nullable
-        @Override
-        public Uri getLinkUri() {
-            return mLinkUri;
-        }
-
-        @Nullable
-        @Override
-        public Object getInputContentInfo() {
-            return null;
-        }
-
-        @Override
-        public void requestPermission() {
-            return;
-        }
-
-        @Override
-        public void releasePermission() {
-            return;
-        }
-    }
-
-    @RequiresApi(25)
-    private static final class InputContentInfoCompatApi25Impl
-            implements InputContentInfoCompatImpl {
-        @NonNull
-        final InputContentInfo mObject;
-
-        InputContentInfoCompatApi25Impl(@NonNull Object inputContentInfo) {
-            mObject = (InputContentInfo) inputContentInfo;
-        }
-
-        InputContentInfoCompatApi25Impl(@NonNull Uri contentUri,
-                @NonNull ClipDescription description, @Nullable Uri linkUri) {
-            mObject = new InputContentInfo(contentUri, description, linkUri);
-        }
-
-        @Override
-        @NonNull
-        public Uri getContentUri() {
-            return mObject.getContentUri();
-        }
-
-        @Override
-        @NonNull
-        public ClipDescription getDescription() {
-            return mObject.getDescription();
-        }
-
-        @Override
-        @Nullable
-        public Uri getLinkUri() {
-            return mObject.getLinkUri();
-        }
-
-        @Override
-        @Nullable
-        public Object getInputContentInfo() {
-            return mObject;
-        }
-
-        @Override
-        public void requestPermission() {
-            mObject.requestPermission();
-        }
-
-        @Override
-        public void releasePermission() {
-            mObject.releasePermission();
-        }
-    }
-
-    private final InputContentInfoCompatImpl mImpl;
-
-    /**
-     * Constructs {@link InputContentInfoCompat}.
-     *
-     * @param contentUri content URI to be exported from the input method. This cannot be
-     *                   {@code null}.
-     * @param description a {@link ClipDescription} object that contains the metadata of
-     *                    {@code contentUri} such as MIME type(s). This object cannot be
-     *                    {@code null}. Also {@link ClipDescription#getLabel()} should be describing
-     *                    the content specified by {@code contentUri} for accessibility reasons.
-     * @param linkUri an optional {@code http} or {@code https} URI. The editor author may provide
-     *                a way to navigate the user to the specified web page if this is not
-     *                {@code null}.
-     */
-    public InputContentInfoCompat(@NonNull Uri contentUri,
-            @NonNull ClipDescription description, @Nullable Uri linkUri) {
-        if (Build.VERSION.SDK_INT >= 25) {
-            mImpl = new InputContentInfoCompatApi25Impl(contentUri, description, linkUri);
-        } else {
-            mImpl = new InputContentInfoCompatBaseImpl(contentUri, description, linkUri);
-        }
-    }
-
-    private InputContentInfoCompat(@NonNull InputContentInfoCompatImpl impl) {
-        mImpl = impl;
-    }
-
-    /**
-     * @return content URI with which the content can be obtained.
-     */
-    @NonNull
-    public Uri getContentUri() {
-        return mImpl.getContentUri();
-    }
-
-    /**
-     * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
-     * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility
-     * purpose.
-     */
-    @NonNull
-    public ClipDescription getDescription() {
-        return mImpl.getDescription();
-    }
-
-    /**
-     * @return an optional {@code http} or {@code https} URI that is related to this content.
-     */
-    @Nullable
-    public Uri getLinkUri() {
-        return mImpl.getLinkUri();
-    }
-
-    /**
-     * Creates an instance from a framework android.view.inputmethod.InputContentInfo object.
-     *
-     * <p>This method always returns {@code null} on API &lt;= 24.</p>
-     *
-     * @param inputContentInfo an android.view.inputmethod.InputContentInfo object, or {@code null}
-     *                         if none.
-     * @return an equivalent {@link InputContentInfoCompat} object, or {@code null} if not
-     * supported.
-     */
-    @Nullable
-    public static InputContentInfoCompat wrap(@Nullable Object inputContentInfo) {
-        if (inputContentInfo == null) {
-            return null;
-        }
-        if (Build.VERSION.SDK_INT < 25) {
-            return null;
-        }
-        return new InputContentInfoCompat(new InputContentInfoCompatApi25Impl(inputContentInfo));
-    }
-
-    /**
-     * Gets the underlying framework android.view.inputmethod.InputContentInfo object.
-     *
-     * <p>This method always returns {@code null} on API &lt;= 24.</p>
-     *
-     * @return an equivalent android.view.inputmethod.InputContentInfo object, or {@code null} if
-     * not supported.
-     */
-    @Nullable
-    public Object unwrap() {
-        return mImpl.getInputContentInfo();
-    }
-
-    /**
-     * Requests a temporary read-only access permission for content URI associated with this object.
-     *
-     * <p>Does nothing if the temporary permission is already granted.</p>
-     */
-    public void requestPermission() {
-        mImpl.requestPermission();
-    }
-
-    /**
-     * Releases a temporary read-only access permission for content URI associated with this object.
-     *
-     * <p>Does nothing if the temporary permission is not granted.</p>
-     */
-    public void releasePermission() {
-        mImpl.releasePermission();
-    }
-}
diff --git a/android/support/v14/preference/EditTextPreferenceDialogFragment.java b/android/support/v14/preference/EditTextPreferenceDialogFragment.java
deleted file mode 100644
index 23b8828..0000000
--- a/android/support/v14/preference/EditTextPreferenceDialogFragment.java
+++ /dev/null
@@ -1,98 +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.v14.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v7.preference.EditTextPreference;
-import android.view.View;
-import android.widget.EditText;
-
-public class EditTextPreferenceDialogFragment extends PreferenceDialogFragment {
-
-    private static final String SAVE_STATE_TEXT = "EditTextPreferenceDialogFragment.text";
-
-    private EditText mEditText;
-
-    private CharSequence mText;
-
-    public static EditTextPreferenceDialogFragment newInstance(String key) {
-        final EditTextPreferenceDialogFragment
-                fragment = new EditTextPreferenceDialogFragment();
-        final Bundle b = new Bundle(1);
-        b.putString(ARG_KEY, key);
-        fragment.setArguments(b);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (savedInstanceState == null) {
-            mText = getEditTextPreference().getText();
-        } else {
-            mText = savedInstanceState.getCharSequence(SAVE_STATE_TEXT);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putCharSequence(SAVE_STATE_TEXT, mText);
-    }
-
-    @Override
-    protected void onBindDialogView(View view) {
-        super.onBindDialogView(view);
-
-        mEditText = (EditText) view.findViewById(android.R.id.edit);
-
-        if (mEditText == null) {
-            throw new IllegalStateException("Dialog view must contain an EditText with id"
-                    + " @android:id/edit");
-        }
-
-        mEditText.setText(mText);
-    }
-
-    private EditTextPreference getEditTextPreference() {
-        return (EditTextPreference) getPreference();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected boolean needInputMethod() {
-        // We want the input method to show, if possible, when dialog is displayed
-        return true;
-    }
-
-    @Override
-    public void onDialogClosed(boolean positiveResult) {
-
-        if (positiveResult) {
-            String value = mEditText.getText().toString();
-            if (getEditTextPreference().callChangeListener(value)) {
-                getEditTextPreference().setText(value);
-            }
-        }
-    }
-
-}
diff --git a/android/support/v14/preference/ListPreferenceDialogFragment.java b/android/support/v14/preference/ListPreferenceDialogFragment.java
deleted file mode 100644
index 5374cd5..0000000
--- a/android/support/v14/preference/ListPreferenceDialogFragment.java
+++ /dev/null
@@ -1,116 +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.v14.preference;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.preference.ListPreference;
-
-public class ListPreferenceDialogFragment extends PreferenceDialogFragment {
-
-    private static final String SAVE_STATE_INDEX = "ListPreferenceDialogFragment.index";
-    private static final String SAVE_STATE_ENTRIES = "ListPreferenceDialogFragment.entries";
-    private static final String SAVE_STATE_ENTRY_VALUES =
-            "ListPreferenceDialogFragment.entryValues";
-
-    private int mClickedDialogEntryIndex;
-    private CharSequence[] mEntries;
-    private CharSequence[] mEntryValues;
-
-    public static ListPreferenceDialogFragment newInstance(String key) {
-        final ListPreferenceDialogFragment fragment = new ListPreferenceDialogFragment();
-        final Bundle b = new Bundle(1);
-        b.putString(ARG_KEY, key);
-        fragment.setArguments(b);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (savedInstanceState == null) {
-            final ListPreference preference = getListPreference();
-
-            if (preference.getEntries() == null || preference.getEntryValues() == null) {
-                throw new IllegalStateException(
-                        "ListPreference requires an entries array and an entryValues array.");
-            }
-
-            mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
-            mEntries = preference.getEntries();
-            mEntryValues = preference.getEntryValues();
-        } else {
-            mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
-            mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
-            mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
-        outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
-        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
-    }
-
-    private ListPreference getListPreference() {
-        return (ListPreference) getPreference();
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
-        super.onPrepareDialogBuilder(builder);
-
-        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
-                new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mClickedDialogEntryIndex = which;
-
-                        /*
-                         * Clicking on an item simulates the positive button
-                         * click, and dismisses the dialog.
-                         */
-                        ListPreferenceDialogFragment.this.onClick(dialog,
-                                DialogInterface.BUTTON_POSITIVE);
-                        dialog.dismiss();
-                    }
-                });
-
-        /*
-         * The typical interaction for list-based dialogs is to have
-         * click-on-an-item dismiss the dialog instead of the user having to
-         * press 'Ok'.
-         */
-        builder.setPositiveButton(null, null);
-    }
-
-    @Override
-    public void onDialogClosed(boolean positiveResult) {
-        final ListPreference preference = getListPreference();
-        if (positiveResult && mClickedDialogEntryIndex >= 0) {
-            String value = mEntryValues[mClickedDialogEntryIndex].toString();
-            if (preference.callChangeListener(value)) {
-                preference.setValue(value);
-            }
-        }
-    }
-
-}
diff --git a/android/support/v14/preference/MultiSelectListPreference.java b/android/support/v14/preference/MultiSelectListPreference.java
deleted file mode 100644
index 16351fe..0000000
--- a/android/support/v14/preference/MultiSelectListPreference.java
+++ /dev/null
@@ -1,277 +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.v14.preference;
-
-import android.content.Context;
-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.v4.content.res.TypedArrayUtils;
-import android.support.v7.preference.R;
-import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
-import android.util.AttributeSet;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A {@link android.support.v7.preference.Preference} that displays a list of entries as
- * a dialog.
- * <p>
- * This preference will store a set of strings into the SharedPreferences.
- * This set will contain one or more mValues from the
- * {@link #setEntryValues(CharSequence[])} array.
- *
- * @attr name android:entries
- * @attr name android:entryValues
- */
-public class MultiSelectListPreference extends AbstractMultiSelectListPreference {
-    private CharSequence[] mEntries;
-    private CharSequence[] mEntryValues;
-    private Set<String> mValues = new HashSet<>();
-
-    public MultiSelectListPreference(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.MultiSelectListPreference, defStyleAttr,
-                defStyleRes);
-
-        mEntries = TypedArrayUtils.getTextArray(a,
-                R.styleable.MultiSelectListPreference_entries,
-                R.styleable.MultiSelectListPreference_android_entries);
-
-        mEntryValues = TypedArrayUtils.getTextArray(a,
-                R.styleable.MultiSelectListPreference_entryValues,
-                R.styleable.MultiSelectListPreference_android_entryValues);
-
-        a.recycle();
-    }
-
-    public MultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public MultiSelectListPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context,
-                R.attr.dialogPreferenceStyle,
-                android.R.attr.dialogPreferenceStyle));
-    }
-
-    public MultiSelectListPreference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Sets the human-readable entries to be shown in the list. This will be
-     * shown in subsequent dialogs.
-     * <p>
-     * Each entry must have a corresponding index in
-     * {@link #setEntryValues(CharSequence[])}.
-     *
-     * @param entries The entries.
-     * @see #setEntryValues(CharSequence[])
-     */
-    public void setEntries(CharSequence[] entries) {
-        mEntries = entries;
-    }
-
-    /**
-     * @see #setEntries(CharSequence[])
-     * @param entriesResId The entries array as a resource.
-     */
-    public void setEntries(@ArrayRes int entriesResId) {
-        setEntries(getContext().getResources().getTextArray(entriesResId));
-    }
-
-    /**
-     * The list of entries to be shown in the list in subsequent dialogs.
-     *
-     * @return The list as an array.
-     */
-    @Override
-    public CharSequence[] getEntries() {
-        return mEntries;
-    }
-
-    /**
-     * The array to find the value to save for a preference when an entry from
-     * entries is selected. If a user clicks on the second item in entries, the
-     * second item in this array will be saved to the preference.
-     *
-     * @param entryValues The array to be used as mValues to save for the preference.
-     */
-    public void setEntryValues(CharSequence[] entryValues) {
-        mEntryValues = entryValues;
-    }
-
-    /**
-     * @see #setEntryValues(CharSequence[])
-     * @param entryValuesResId The entry mValues array as a resource.
-     */
-    public void setEntryValues(@ArrayRes int entryValuesResId) {
-        setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
-    }
-
-    /**
-     * Returns the array of mValues to be saved for the preference.
-     *
-     * @return The array of mValues.
-     */
-    @Override
-    public CharSequence[] getEntryValues() {
-        return mEntryValues;
-    }
-
-    /**
-     * Sets the value of the key. This should contain entries in
-     * {@link #getEntryValues()}.
-     *
-     * @param values The mValues to set for the key.
-     */
-    @Override
-    public void setValues(Set<String> values) {
-        mValues.clear();
-        mValues.addAll(values);
-
-        persistStringSet(values);
-    }
-
-    /**
-     * Retrieves the current value of the key.
-     */
-    @Override
-    public Set<String> getValues() {
-        return mValues;
-    }
-
-    /**
-     * Returns the index of the given value (in the entry mValues array).
-     *
-     * @param value The value whose index should be returned.
-     * @return The index of the value, or -1 if not found.
-     */
-    public int findIndexOfValue(String value) {
-        if (value != null && mEntryValues != null) {
-            for (int i = mEntryValues.length - 1; i >= 0; i--) {
-                if (mEntryValues[i].equals(value)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    protected boolean[] getSelectedItems() {
-        final CharSequence[] entries = mEntryValues;
-        final int entryCount = entries.length;
-        final Set<String> values = mValues;
-        boolean[] result = new boolean[entryCount];
-
-        for (int i = 0; i < entryCount; i++) {
-            result[i] = values.contains(entries[i].toString());
-        }
-
-        return result;
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        final CharSequence[] defaultValues = a.getTextArray(index);
-        final Set<String> result = new HashSet<>();
-
-        for (final CharSequence defaultValue : defaultValues) {
-            result.add(defaultValue.toString());
-        }
-
-        return result;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
-        setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (isPersistent()) {
-            // No need to save instance state
-            return superState;
-        }
-
-        final SavedState myState = new SavedState(superState);
-        myState.mValues = getValues();
-        return myState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state == null || !state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in onSaveInstanceState
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState myState = (SavedState) state;
-        super.onRestoreInstanceState(myState.getSuperState());
-        setValues(myState.mValues);
-    }
-
-    private static class SavedState extends BaseSavedState {
-        Set<String> mValues;
-
-        SavedState(Parcel source) {
-            super(source);
-            final int size = source.readInt();
-            mValues = new HashSet<>();
-            String[] strings = new String[size];
-            source.readStringArray(strings);
-
-            Collections.addAll(mValues, strings);
-        }
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(@NonNull Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mValues.size());
-            dest.writeStringArray(mValues.toArray(new String[mValues.size()]));
-        }
-
-        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/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java b/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
deleted file mode 100644
index db81644..0000000
--- a/android/support/v14/preference/MultiSelectListPreferenceDialogFragment.java
+++ /dev/null
@@ -1,129 +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.v14.preference;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-public class MultiSelectListPreferenceDialogFragment extends PreferenceDialogFragment {
-
-    private static final String SAVE_STATE_VALUES =
-            "MultiSelectListPreferenceDialogFragment.values";
-    private static final String SAVE_STATE_CHANGED =
-            "MultiSelectListPreferenceDialogFragment.changed";
-    private static final String SAVE_STATE_ENTRIES =
-            "MultiSelectListPreferenceDialogFragment.entries";
-    private static final String SAVE_STATE_ENTRY_VALUES =
-            "MultiSelectListPreferenceDialogFragment.entryValues";
-
-    private Set<String> mNewValues = new HashSet<>();
-    private boolean mPreferenceChanged;
-    private CharSequence[] mEntries;
-    private CharSequence[] mEntryValues;
-
-    public static MultiSelectListPreferenceDialogFragment newInstance(String key) {
-        final MultiSelectListPreferenceDialogFragment fragment =
-                new MultiSelectListPreferenceDialogFragment();
-        final Bundle b = new Bundle(1);
-        b.putString(ARG_KEY, key);
-        fragment.setArguments(b);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState == null) {
-            final AbstractMultiSelectListPreference preference = getListPreference();
-
-            if (preference.getEntries() == null || preference.getEntryValues() == null) {
-                throw new IllegalStateException(
-                        "MultiSelectListPreference requires an entries array and "
-                                + "an entryValues array.");
-            }
-
-            mNewValues.clear();
-            mNewValues.addAll(preference.getValues());
-            mPreferenceChanged = false;
-            mEntries = preference.getEntries();
-            mEntryValues = preference.getEntryValues();
-        } else {
-            mNewValues.clear();
-            mNewValues.addAll(savedInstanceState.getStringArrayList(SAVE_STATE_VALUES));
-            mPreferenceChanged = savedInstanceState.getBoolean(SAVE_STATE_CHANGED, false);
-            mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
-            mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putStringArrayList(SAVE_STATE_VALUES, new ArrayList<>(mNewValues));
-        outState.putBoolean(SAVE_STATE_CHANGED, mPreferenceChanged);
-        outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
-        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
-    }
-
-    private AbstractMultiSelectListPreference getListPreference() {
-        return (AbstractMultiSelectListPreference) getPreference();
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
-        super.onPrepareDialogBuilder(builder);
-
-        final int entryCount = mEntryValues.length;
-        final boolean[] checkedItems = new boolean[entryCount];
-        for (int i = 0; i < entryCount; i++) {
-            checkedItems[i] = mNewValues.contains(mEntryValues[i].toString());
-        }
-        builder.setMultiChoiceItems(mEntries, checkedItems,
-                new DialogInterface.OnMultiChoiceClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
-                        if (isChecked) {
-                            mPreferenceChanged |= mNewValues.add(
-                                    mEntryValues[which].toString());
-                        } else {
-                            mPreferenceChanged |= mNewValues.remove(
-                                    mEntryValues[which].toString());
-                        }
-                    }
-                });
-    }
-
-    @Override
-    public void onDialogClosed(boolean positiveResult) {
-        final AbstractMultiSelectListPreference preference = getListPreference();
-        if (positiveResult && mPreferenceChanged) {
-            final Set<String> values = mNewValues;
-            if (preference.callChangeListener(values)) {
-                preference.setValues(values);
-            }
-        }
-        mPreferenceChanged = false;
-    }
-}
diff --git a/android/support/v14/preference/PreferenceDialogFragment.java b/android/support/v14/preference/PreferenceDialogFragment.java
deleted file mode 100644
index a4ae4a9..0000000
--- a/android/support/v14/preference/PreferenceDialogFragment.java
+++ /dev/null
@@ -1,269 +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.v14.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v7.preference.DialogPreference;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-/**
- * Abstract base class which presents a dialog associated with a
- * {@link android.support.v7.preference.DialogPreference}. Since the preference object may
- * not be available during fragment re-creation, the necessary information for displaying the dialog
- * is read once during the initial call to {@link #onCreate(Bundle)} and saved/restored in the saved
- * instance state. Custom subclasses should also follow this pattern.
- */
-public abstract class PreferenceDialogFragment extends DialogFragment implements
-        DialogInterface.OnClickListener {
-
-    protected static final String ARG_KEY = "key";
-
-    private static final String SAVE_STATE_TITLE = "PreferenceDialogFragment.title";
-    private static final String SAVE_STATE_POSITIVE_TEXT = "PreferenceDialogFragment.positiveText";
-    private static final String SAVE_STATE_NEGATIVE_TEXT = "PreferenceDialogFragment.negativeText";
-    private static final String SAVE_STATE_MESSAGE = "PreferenceDialogFragment.message";
-    private static final String SAVE_STATE_LAYOUT = "PreferenceDialogFragment.layout";
-    private static final String SAVE_STATE_ICON = "PreferenceDialogFragment.icon";
-
-    private DialogPreference mPreference;
-
-    private CharSequence mDialogTitle;
-    private CharSequence mPositiveButtonText;
-    private CharSequence mNegativeButtonText;
-    private CharSequence mDialogMessage;
-    private @LayoutRes int mDialogLayoutRes;
-
-    private BitmapDrawable mDialogIcon;
-
-    /** Which button was clicked. */
-    private int mWhichButtonClicked;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final Fragment rawFragment = getTargetFragment();
-        if (!(rawFragment instanceof DialogPreference.TargetFragment)) {
-            throw new IllegalStateException("Target fragment must implement TargetFragment"
-                    + " interface");
-        }
-
-        final DialogPreference.TargetFragment fragment =
-                (DialogPreference.TargetFragment) rawFragment;
-
-        final String key = getArguments().getString(ARG_KEY);
-        if (savedInstanceState == null) {
-            mPreference = (DialogPreference) fragment.findPreference(key);
-            mDialogTitle = mPreference.getDialogTitle();
-            mPositiveButtonText = mPreference.getPositiveButtonText();
-            mNegativeButtonText = mPreference.getNegativeButtonText();
-            mDialogMessage = mPreference.getDialogMessage();
-            mDialogLayoutRes = mPreference.getDialogLayoutResource();
-
-            final Drawable icon = mPreference.getDialogIcon();
-            if (icon == null || icon instanceof BitmapDrawable) {
-                mDialogIcon = (BitmapDrawable) icon;
-            } else {
-                final Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
-                        icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-                final Canvas canvas = new Canvas(bitmap);
-                icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-                icon.draw(canvas);
-                mDialogIcon = new BitmapDrawable(getResources(), bitmap);
-            }
-        } else {
-            mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE);
-            mPositiveButtonText = savedInstanceState.getCharSequence(SAVE_STATE_POSITIVE_TEXT);
-            mNegativeButtonText = savedInstanceState.getCharSequence(SAVE_STATE_NEGATIVE_TEXT);
-            mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE);
-            mDialogLayoutRes = savedInstanceState.getInt(SAVE_STATE_LAYOUT, 0);
-            final Bitmap bitmap = savedInstanceState.getParcelable(SAVE_STATE_ICON);
-            if (bitmap != null) {
-                mDialogIcon = new BitmapDrawable(getResources(), bitmap);
-            }
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle);
-        outState.putCharSequence(SAVE_STATE_POSITIVE_TEXT, mPositiveButtonText);
-        outState.putCharSequence(SAVE_STATE_NEGATIVE_TEXT, mNegativeButtonText);
-        outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage);
-        outState.putInt(SAVE_STATE_LAYOUT, mDialogLayoutRes);
-        if (mDialogIcon != null) {
-            outState.putParcelable(SAVE_STATE_ICON, mDialogIcon.getBitmap());
-        }
-    }
-
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        final Context context = getActivity();
-        mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
-                .setTitle(mDialogTitle)
-                .setIcon(mDialogIcon)
-                .setPositiveButton(mPositiveButtonText, this)
-                .setNegativeButton(mNegativeButtonText, this);
-
-        View contentView = onCreateDialogView(context);
-        if (contentView != null) {
-            onBindDialogView(contentView);
-            builder.setView(contentView);
-        } else {
-            builder.setMessage(mDialogMessage);
-        }
-
-        onPrepareDialogBuilder(builder);
-
-        // Create the dialog
-        final Dialog dialog = builder.create();
-        if (needInputMethod()) {
-            requestInputMethod(dialog);
-        }
-
-        return dialog;
-    }
-
-    /**
-     * Get the preference that requested this dialog. Available after {@link #onCreate(Bundle)} has
-     * been called on the {@link PreferenceFragment} which launched this dialog.
-     *
-     * @return The {@link DialogPreference} associated with this
-     * dialog.
-     */
-    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;
-    }
-
-    /**
-     * Prepares the dialog builder to be shown when the preference is clicked.
-     * Use this to set custom properties on the dialog.
-     * <p>
-     * Do not {@link AlertDialog.Builder#create()} or
-     * {@link AlertDialog.Builder#show()}.
-     */
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {}
-
-    /**
-     * Returns whether the preference needs to display a soft input method when the dialog
-     * is displayed. Default is false. Subclasses should override this method if they need
-     * the soft input method brought up automatically.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean needInputMethod() {
-        return false;
-    }
-
-    /**
-     * Sets the required flags on the dialog window to enable input method window to show up.
-     */
-    private void requestInputMethod(Dialog dialog) {
-        Window window = dialog.getWindow();
-        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
-    }
-
-    /**
-     * Creates the content view for the dialog (if a custom content view is
-     * required). By default, it inflates the dialog layout resource if it is
-     * set.
-     *
-     * @return The content View for the dialog.
-     * @see DialogPreference#setLayoutResource(int)
-     */
-    protected View onCreateDialogView(Context context) {
-        final int resId = mDialogLayoutRes;
-        if (resId == 0) {
-            return null;
-        }
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        return inflater.inflate(resId, null);
-    }
-
-    /**
-     * Binds views in the content View of the dialog to data.
-     * <p>
-     * Make sure to call through to the superclass implementation.
-     *
-     * @param view The content View of the dialog, if it is custom.
-     */
-    protected void onBindDialogView(View view) {
-        View dialogMessageView = view.findViewById(android.R.id.message);
-
-        if (dialogMessageView != null) {
-            final CharSequence message = mDialogMessage;
-            int newVisibility = View.GONE;
-
-            if (!TextUtils.isEmpty(message)) {
-                if (dialogMessageView instanceof TextView) {
-                    ((TextView) dialogMessageView).setText(message);
-                }
-
-                newVisibility = View.VISIBLE;
-            }
-
-            if (dialogMessageView.getVisibility() != newVisibility) {
-                dialogMessageView.setVisibility(newVisibility);
-            }
-        }
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        mWhichButtonClicked = which;
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        super.onDismiss(dialog);
-        onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
-    }
-
-    public abstract void onDialogClosed(boolean positiveResult);
-}
diff --git a/android/support/v14/preference/PreferenceFragment.java b/android/support/v14/preference/PreferenceFragment.java
deleted file mode 100644
index 406465f..0000000
--- a/android/support/v14/preference/PreferenceFragment.java
+++ /dev/null
@@ -1,851 +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.v14.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-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.v7.preference.AndroidResources;
-import android.support.v7.preference.DialogPreference;
-import android.support.v7.preference.EditTextPreference;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceGroupAdapter;
-import android.support.v7.preference.PreferenceManager;
-import android.support.v7.preference.PreferenceRecyclerViewAccessibilityDelegate;
-import android.support.v7.preference.PreferenceScreen;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.support.v7.preference.R;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Shows a hierarchy of {@link Preference} objects as
- * lists. These preferences will
- * automatically save to {@link android.content.SharedPreferences} as the user interacts with
- * them. To retrieve an instance of {@link android.content.SharedPreferences} that the
- * preference hierarchy in this fragment will use, call
- * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
- * with a context in the same package as this fragment.
- * <p>
- * Furthermore, the preferences shown will follow the visual style of system
- * preferences. It is easy to create a hierarchy of preferences (that can be
- * shown on multiple screens) via XML. For these reasons, it is recommended to
- * use this fragment (as a superclass) to deal with preferences in applications.
- * <p>
- * A {@link PreferenceScreen} object should be at the top of the preference
- * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
- * denote a screen break--that is the preferences contained within subsequent
- * {@link PreferenceScreen} should be shown on another screen. The preference
- * framework handles this by calling {@link #onNavigateToScreen(PreferenceScreen)}.
- * <p>
- * The preference hierarchy can be formed in multiple ways:
- * <li> From an XML file specifying the hierarchy
- * <li> From different {@link android.app.Activity Activities} that each specify its own
- * preferences in an XML file via {@link android.app.Activity} meta-data
- * <li> From an object hierarchy rooted with {@link PreferenceScreen}
- * <p>
- * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
- * root element should be a {@link PreferenceScreen}. Subsequent elements can point
- * to actual {@link Preference} subclasses. As mentioned above, subsequent
- * {@link PreferenceScreen} in the hierarchy will result in the screen break.
- * <p>
- * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
- * {@link #setPreferenceScreen(PreferenceScreen)}.
- * <p>
- * As a convenience, this fragment implements a click listener for any
- * preference in the current hierarchy, see
- * {@link #onPreferenceTreeClick(Preference)}.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about using {@code PreferenceFragment},
- * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
- * guide.</p>
- * </div>
- *
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference fragment that is
- * populated from a resource.  The resource it loads is:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences}
- *
- * <p>The fragment implementation itself simply populates the preferences
- * when created.  Note that the preferences framework takes care of loading
- * the current values out of the app preferences and writing them when changed:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/android/supportpreference/FragmentSupportPreferences.java
- *      support_fragment}
- *
- * @see Preference
- * @see PreferenceScreen
- */
-public abstract class PreferenceFragment extends Fragment implements
-        PreferenceManager.OnPreferenceTreeClickListener,
-        PreferenceManager.OnDisplayPreferenceDialogListener,
-        PreferenceManager.OnNavigateToScreenListener,
-        DialogPreference.TargetFragment {
-
-    /**
-     * Fragment argument used to specify the tag of the desired root
-     * {@link android.support.v7.preference.PreferenceScreen} object.
-     */
-    public static final String ARG_PREFERENCE_ROOT =
-            "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
-
-    private static final String PREFERENCES_TAG = "android:preferences";
-
-    private static final String DIALOG_FRAGMENT_TAG =
-            "android.support.v14.preference.PreferenceFragment.DIALOG";
-
-    private PreferenceManager mPreferenceManager;
-    private RecyclerView mList;
-    private boolean mHavePrefs;
-    private boolean mInitDone;
-
-    private Context mStyledContext;
-
-    private int mLayoutResId = android.support.v7.preference.R.layout.preference_list_fragment;
-
-    private final DividerDecoration mDividerDecoration = new DividerDecoration();
-
-    private static final int MSG_BIND_PREFERENCES = 1;
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-
-                case MSG_BIND_PREFERENCES:
-                    bindPreferences();
-                    break;
-            }
-        }
-    };
-
-    private final Runnable mRequestFocus = new Runnable() {
-        @Override
-        public void run() {
-            mList.focusableViewAvailable(mList);
-        }
-    };
-
-    private Runnable mSelectPreferenceRunnable;
-
-    /**
-     * Interface that PreferenceFragment's containing activity should
-     * implement to be able to process preference items that wish to
-     * switch to a specified fragment.
-     */
-    public interface OnPreferenceStartFragmentCallback {
-        /**
-         * Called when the user has clicked on a Preference that has
-         * a fragment class name associated with it.  The implementation
-         * should instantiate and switch to an instance of the given
-         * fragment.
-         * @param caller The fragment requesting navigation.
-         * @param pref The preference requesting the fragment.
-         * @return true if the fragment creation has been handled
-         */
-        boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref);
-    }
-
-    /**
-     * Interface that PreferenceFragment's containing activity should
-     * implement to be able to process preference items that wish to
-     * switch to a new screen of preferences.
-     */
-    public interface OnPreferenceStartScreenCallback {
-        /**
-         * Called when the user has clicked on a PreferenceScreen item in order to navigate to a new
-         * screen of preferences.
-         * @param caller The fragment requesting navigation.
-         * @param pref The preference screen to navigate to.
-         * @return true if the screen navigation has been handled
-         */
-        boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref);
-    }
-
-    public interface OnPreferenceDisplayDialogCallback {
-
-        /**
-         *
-         * @param caller The fragment containing the preference requesting the dialog.
-         * @param pref The preference requesting the dialog.
-         * @return true if the dialog creation has been handled.
-         */
-        boolean onPreferenceDisplayDialog(@NonNull PreferenceFragment caller, Preference pref);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final TypedValue tv = new TypedValue();
-        getActivity().getTheme().resolveAttribute(
-                android.support.v7.preference.R.attr.preferenceTheme, tv, true);
-        final int theme = tv.resourceId;
-        if (theme == 0) {
-            throw new IllegalStateException("Must specify preferenceTheme in theme");
-        }
-        mStyledContext = new ContextThemeWrapper(getActivity(), theme);
-
-        mPreferenceManager = new PreferenceManager(mStyledContext);
-        mPreferenceManager.setOnNavigateToScreenListener(this);
-        final Bundle args = getArguments();
-        final String rootKey;
-        if (args != null) {
-            rootKey = getArguments().getString(ARG_PREFERENCE_ROOT);
-        } else {
-            rootKey = null;
-        }
-        onCreatePreferences(savedInstanceState, rootKey);
-    }
-
-    /**
-     * Called during {@link #onCreate(Bundle)} to supply the preferences for this fragment.
-     * Subclasses are expected to call {@link #setPreferenceScreen(PreferenceScreen)} either
-     * directly or via helper methods such as {@link #addPreferencesFromResource(int)}.
-     *
-     * @param savedInstanceState If the fragment is being re-created from
-     *                           a previous saved state, this is the state.
-     * @param rootKey If non-null, this preference fragment should be rooted at the
-     *                {@link android.support.v7.preference.PreferenceScreen} with this key.
-     */
-    public abstract void onCreatePreferences(Bundle savedInstanceState, String rootKey);
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-
-        TypedArray a = mStyledContext.obtainStyledAttributes(null,
-                R.styleable.PreferenceFragment,
-                TypedArrayUtils.getAttr(mStyledContext,
-                        android.support.v7.preference.R.attr.preferenceFragmentStyle,
-                        AndroidResources.ANDROID_R_PREFERENCE_FRAGMENT_STYLE),
-                0);
-
-        mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_android_layout, mLayoutResId);
-
-        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
-        final TypedValue tv = new TypedValue();
-        getActivity().getTheme().resolveAttribute(
-                android.support.v7.preference.R.attr.preferenceTheme, tv, true);
-        final int theme = tv.resourceId;
-
-        final Context themedContext = new ContextThemeWrapper(inflater.getContext(), theme);
-        final LayoutInflater themedInflater = inflater.cloneInContext(themedContext);
-
-        final View view = themedInflater.inflate(mLayoutResId, container, false);
-
-        final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
-        if (!(rawListContainer instanceof ViewGroup)) {
-            throw new RuntimeException("Content has view with id attribute "
-                    + "'android.R.id.list_container' that is not a ViewGroup class");
-        }
-
-        final ViewGroup listContainer = (ViewGroup) rawListContainer;
-
-        final RecyclerView listView = onCreateRecyclerView(themedInflater, listContainer,
-                savedInstanceState);
-        if (listView == null) {
-            throw new RuntimeException("Could not create RecyclerView");
-        }
-
-        mList = listView;
-
-        listView.addItemDecoration(mDividerDecoration);
-        setDivider(divider);
-        if (dividerHeight != -1) {
-            setDividerHeight(dividerHeight);
-        }
-        mDividerDecoration.setAllowDividerAfterLastItem(allowDividerAfterLastItem);
-
-        listContainer.addView(mList);
-        mHandler.post(mRequestFocus);
-
-        return view;
-    }
-
-    /**
-     * Sets the drawable that will be drawn between each item in the list.
-     * <p>
-     * <strong>Note:</strong> If the drawable does not have an intrinsic
-     * height, you should also call {@link #setDividerHeight(int)}.
-     *
-     * @param divider the drawable to use
-     * @attr ref R.styleable#PreferenceFragment_android_divider
-     */
-    public void setDivider(Drawable divider) {
-        mDividerDecoration.setDivider(divider);
-    }
-
-    /**
-     * Sets the height of the divider that will be drawn between each item in the list. Calling
-     * this will override the intrinsic height as set by {@link #setDivider(Drawable)}
-     *
-     * @param height The new height of the divider in pixels.
-     * @attr ref R.styleable#PreferenceFragment_android_dividerHeight
-     */
-    public void setDividerHeight(int height) {
-        mDividerDecoration.setDividerHeight(height);
-    }
-
-    @Override
-    public void onViewCreated(View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-
-        if (mHavePrefs) {
-            bindPreferences();
-            if (mSelectPreferenceRunnable != null) {
-                mSelectPreferenceRunnable.run();
-                mSelectPreferenceRunnable = null;
-            }
-        }
-
-        mInitDone = true;
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        if (savedInstanceState != null) {
-            Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
-            if (container != null) {
-                final PreferenceScreen preferenceScreen = getPreferenceScreen();
-                if (preferenceScreen != null) {
-                    preferenceScreen.restoreHierarchyState(container);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mPreferenceManager.setOnPreferenceTreeClickListener(this);
-        mPreferenceManager.setOnDisplayPreferenceDialogListener(this);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        mPreferenceManager.setOnPreferenceTreeClickListener(null);
-        mPreferenceManager.setOnDisplayPreferenceDialogListener(null);
-    }
-
-    @Override
-    public void onDestroyView() {
-        mHandler.removeCallbacks(mRequestFocus);
-        mHandler.removeMessages(MSG_BIND_PREFERENCES);
-        if (mHavePrefs) {
-            unbindPreferences();
-        }
-        mList = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            Bundle container = new Bundle();
-            preferenceScreen.saveHierarchyState(container);
-            outState.putBundle(PREFERENCES_TAG, container);
-        }
-    }
-
-    /**
-     * Returns the {@link PreferenceManager} used by this fragment.
-     * @return The {@link PreferenceManager}.
-     */
-    public PreferenceManager getPreferenceManager() {
-        return mPreferenceManager;
-    }
-
-    /**
-     * Sets the root of the preference hierarchy that this fragment is showing.
-     *
-     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
-     */
-    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
-        if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
-            onUnbindPreferences();
-            mHavePrefs = true;
-            if (mInitDone) {
-                postBindPreferences();
-            }
-        }
-    }
-
-    /**
-     * Gets the root of the preference hierarchy that this fragment is showing.
-     *
-     * @return The {@link PreferenceScreen} that is the root of the preference
-     *         hierarchy.
-     */
-    public PreferenceScreen getPreferenceScreen() {
-        return mPreferenceManager.getPreferenceScreen();
-    }
-
-    /**
-     * Inflates the given XML resource and adds the preference hierarchy to the current
-     * preference hierarchy.
-     *
-     * @param preferencesResId The XML resource ID to inflate.
-     */
-    public void addPreferencesFromResource(@XmlRes int preferencesResId) {
-        requirePreferenceManager();
-
-        setPreferenceScreen(mPreferenceManager.inflateFromResource(mStyledContext,
-                preferencesResId, getPreferenceScreen()));
-    }
-
-    /**
-     * Inflates the given XML resource and replaces the current preference hierarchy (if any) with
-     * the preference hierarchy rooted at {@code key}.
-     *
-     * @param preferencesResId The XML resource ID to inflate.
-     * @param key The preference key of the {@link android.support.v7.preference.PreferenceScreen}
-     *            to use as the root of the preference hierarchy, or null to use the root
-     *            {@link android.support.v7.preference.PreferenceScreen}.
-     */
-    public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key) {
-        requirePreferenceManager();
-
-        final PreferenceScreen xmlRoot = mPreferenceManager.inflateFromResource(mStyledContext,
-                preferencesResId, null);
-
-        final Preference root;
-        if (key != null) {
-            root = xmlRoot.findPreference(key);
-            if (!(root instanceof PreferenceScreen)) {
-                throw new IllegalArgumentException("Preference object with key " + key
-                        + " is not a PreferenceScreen");
-            }
-        } else {
-            root = xmlRoot;
-        }
-
-        setPreferenceScreen((PreferenceScreen) root);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (preference.getFragment() != null) {
-            boolean handled = false;
-            if (getCallbackFragment() instanceof OnPreferenceStartFragmentCallback) {
-                handled = ((OnPreferenceStartFragmentCallback) getCallbackFragment())
-                        .onPreferenceStartFragment(this, preference);
-            }
-            if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback) {
-                handled = ((OnPreferenceStartFragmentCallback) getActivity())
-                        .onPreferenceStartFragment(this, preference);
-            }
-            return handled;
-        }
-        return false;
-    }
-
-    /**
-     * Called by
-     * {@link android.support.v7.preference.PreferenceScreen#onClick()} in order to navigate to a
-     * new screen of preferences. Calls
-     * {@link PreferenceFragment.OnPreferenceStartScreenCallback#onPreferenceStartScreen}
-     * if the target fragment or containing activity implements
-     * {@link PreferenceFragment.OnPreferenceStartScreenCallback}.
-     * @param preferenceScreen The {@link android.support.v7.preference.PreferenceScreen} to
-     *                         navigate to.
-     */
-    @Override
-    public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
-        boolean handled = false;
-        if (getCallbackFragment() instanceof OnPreferenceStartScreenCallback) {
-            handled = ((OnPreferenceStartScreenCallback) getCallbackFragment())
-                    .onPreferenceStartScreen(this, preferenceScreen);
-        }
-        if (!handled && getActivity() instanceof OnPreferenceStartScreenCallback) {
-            ((OnPreferenceStartScreenCallback) getActivity())
-                    .onPreferenceStartScreen(this, preferenceScreen);
-        }
-    }
-
-    /**
-     * Finds a {@link Preference} based on its key.
-     *
-     * @param key The key of the preference to retrieve.
-     * @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;
-        }
-        return mPreferenceManager.findPreference(key);
-    }
-
-    private void requirePreferenceManager() {
-        if (mPreferenceManager == null) {
-            throw new RuntimeException("This should be called after super.onCreate.");
-        }
-    }
-
-    private void postBindPreferences() {
-        if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
-        mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
-    }
-
-    private void bindPreferences() {
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            getListView().setAdapter(onCreateAdapter(preferenceScreen));
-            preferenceScreen.onAttached();
-        }
-        onBindPreferences();
-    }
-
-    private void unbindPreferences() {
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            preferenceScreen.onDetached();
-        }
-        onUnbindPreferences();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void onBindPreferences() {
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void onUnbindPreferences() {
-    }
-
-    public final RecyclerView getListView() {
-        return mList;
-    }
-
-    /**
-     * Creates the {@link android.support.v7.widget.RecyclerView} used to display the preferences.
-     * Subclasses may override this to return a customized
-     * {@link android.support.v7.widget.RecyclerView}.
-     * @param inflater The LayoutInflater object that can be used to inflate the
-     *                 {@link android.support.v7.widget.RecyclerView}.
-     * @param parent The parent {@link android.view.View} that the RecyclerView will be attached to.
-     *               This method should not add the view itself, but this can be used to generate
-     *               the LayoutParams of the view.
-     * @param savedInstanceState If non-null, this view is being re-constructed from a previous
-     *                           saved state as given here
-     * @return A new RecyclerView object to be placed into the view hierarchy
-     */
-    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        RecyclerView recyclerView = (RecyclerView) inflater
-                .inflate(android.support.v7.preference.R.layout.preference_recyclerview,
-                        parent, false);
-
-        recyclerView.setLayoutManager(onCreateLayoutManager());
-        recyclerView.setAccessibilityDelegateCompat(
-                new PreferenceRecyclerViewAccessibilityDelegate(recyclerView));
-
-        return recyclerView;
-    }
-
-    /**
-     * Called from {@link #onCreateRecyclerView} to create the
-     * {@link android.support.v7.widget.RecyclerView.LayoutManager} for the created
-     * {@link android.support.v7.widget.RecyclerView}.
-     * @return A new {@link android.support.v7.widget.RecyclerView.LayoutManager} instance.
-     */
-    public RecyclerView.LayoutManager onCreateLayoutManager() {
-        return new LinearLayoutManager(getActivity());
-    }
-
-    /**
-     * Creates the root adapter.
-     *
-     * @param preferenceScreen Preference screen object to create the adapter for.
-     * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}.
-     */
-    protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
-        return new PreferenceGroupAdapter(preferenceScreen);
-    }
-
-    /**
-     * Called when a preference in the tree requests to display a dialog. Subclasses should
-     * override this method to display custom dialogs or to handle dialogs for custom preference
-     * classes.
-     *
-     * @param preference The Preference object requesting the dialog.
-     */
-    @Override
-    public void onDisplayPreferenceDialog(Preference preference) {
-
-        boolean handled = false;
-        if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) {
-            handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment())
-                    .onPreferenceDisplayDialog(this, preference);
-        }
-        if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) {
-            handled = ((OnPreferenceDisplayDialogCallback) getActivity())
-                    .onPreferenceDisplayDialog(this, preference);
-        }
-
-        if (handled) {
-            return;
-        }
-
-        // check if dialog is already showing
-        if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
-            return;
-        }
-
-        final DialogFragment f;
-        if (preference instanceof EditTextPreference) {
-            f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
-        } else if (preference instanceof ListPreference) {
-            f = ListPreferenceDialogFragment.newInstance(preference.getKey());
-        } else if (preference instanceof MultiSelectListPreference) {
-            f = MultiSelectListPreferenceDialogFragment.newInstance(preference.getKey());
-        } else {
-            throw new IllegalArgumentException("Tried to display dialog for unknown "
-                    + "preference type. Did you forget to override onDisplayPreferenceDialog()?");
-        }
-        f.setTargetFragment(this, 0);
-        f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
-    }
-
-    /**
-     * Basically a wrapper for getParentFragment which is v17+. Used by the leanback preference lib.
-     * @return Fragment to possibly use as a callback
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public Fragment getCallbackFragment() {
-        return null;
-    }
-
-    public void scrollToPreference(final String key) {
-        scrollToPreferenceInternal(null, key);
-    }
-
-    public void scrollToPreference(final Preference preference) {
-        scrollToPreferenceInternal(preference, null);
-    }
-
-    private void scrollToPreferenceInternal(final Preference preference, final String key) {
-        final Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                final RecyclerView.Adapter adapter = mList.getAdapter();
-                if (!(adapter instanceof PreferenceGroup.PreferencePositionCallback)) {
-                    if (adapter != null) {
-                        throw new IllegalStateException("Adapter must implement "
-                                + "PreferencePositionCallback");
-                    } else {
-                        // Adapter was set to null, so don't scroll I guess?
-                        return;
-                    }
-                }
-                final int position;
-                if (preference != null) {
-                    position = ((PreferenceGroup.PreferencePositionCallback) adapter)
-                            .getPreferenceAdapterPosition(preference);
-                } else {
-                    position = ((PreferenceGroup.PreferencePositionCallback) adapter)
-                            .getPreferenceAdapterPosition(key);
-                }
-                if (position != RecyclerView.NO_POSITION) {
-                    mList.scrollToPosition(position);
-                } else {
-                    // Item not found, wait for an update and try again
-                    adapter.registerAdapterDataObserver(
-                            new ScrollToPreferenceObserver(adapter, mList, preference, key));
-                }
-            }
-        };
-        if (mList == null) {
-            mSelectPreferenceRunnable = r;
-        } else {
-            r.run();
-        }
-    }
-
-    private static class ScrollToPreferenceObserver extends RecyclerView.AdapterDataObserver {
-        private final RecyclerView.Adapter mAdapter;
-        private final RecyclerView mList;
-        private final Preference mPreference;
-        private final String mKey;
-
-        ScrollToPreferenceObserver(RecyclerView.Adapter adapter, RecyclerView list,
-                Preference preference, String key) {
-            mAdapter = adapter;
-            mList = list;
-            mPreference = preference;
-            mKey = key;
-        }
-
-        private void scrollToPreference() {
-            mAdapter.unregisterAdapterDataObserver(this);
-            final int position;
-            if (mPreference != null) {
-                position = ((PreferenceGroup.PreferencePositionCallback) mAdapter)
-                        .getPreferenceAdapterPosition(mPreference);
-            } else {
-                position = ((PreferenceGroup.PreferencePositionCallback) mAdapter)
-                        .getPreferenceAdapterPosition(mKey);
-            }
-            if (position != RecyclerView.NO_POSITION) {
-                mList.scrollToPosition(position);
-            }
-        }
-
-        @Override
-        public void onChanged() {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            scrollToPreference();
-        }
-    }
-
-    private class DividerDecoration extends RecyclerView.ItemDecoration {
-
-        private Drawable mDivider;
-        private int mDividerHeight;
-        private boolean mAllowDividerAfterLastItem = true;
-
-        @Override
-        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
-            if (mDivider == null) {
-                return;
-            }
-            final int childCount = parent.getChildCount();
-            final int width = parent.getWidth();
-            for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
-                final View view = parent.getChildAt(childViewIndex);
-                if (shouldDrawDividerBelow(view, parent)) {
-                    int top = (int) view.getY() + view.getHeight();
-                    mDivider.setBounds(0, top, width, top + mDividerHeight);
-                    mDivider.draw(c);
-                }
-            }
-        }
-
-        @Override
-        public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
-                RecyclerView.State state) {
-            if (shouldDrawDividerBelow(view, parent)) {
-                outRect.bottom = mDividerHeight;
-            }
-        }
-
-        private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
-            final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
-            final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder
-                    && ((PreferenceViewHolder) holder).isDividerAllowedBelow();
-            if (!dividerAllowedBelow) {
-                return false;
-            }
-            boolean nextAllowed = mAllowDividerAfterLastItem;
-            int index = parent.indexOfChild(view);
-            if (index < parent.getChildCount() - 1) {
-                final View nextView = parent.getChildAt(index + 1);
-                final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView);
-                nextAllowed = nextHolder instanceof PreferenceViewHolder
-                        && ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove();
-            }
-            return nextAllowed;
-        }
-
-        public void setDivider(Drawable divider) {
-            if (divider != null) {
-                mDividerHeight = divider.getIntrinsicHeight();
-            } else {
-                mDividerHeight = 0;
-            }
-            mDivider = divider;
-            mList.invalidateItemDecorations();
-        }
-
-        public void setDividerHeight(int dividerHeight) {
-            mDividerHeight = dividerHeight;
-            mList.invalidateItemDecorations();
-        }
-
-        public void setAllowDividerAfterLastItem(boolean allowDividerAfterLastItem) {
-            mAllowDividerAfterLastItem = allowDividerAfterLastItem;
-        }
-    }
-}
diff --git a/android/support/v14/preference/SwitchPreference.java b/android/support/v14/preference/SwitchPreference.java
deleted file mode 100644
index 197de4e..0000000
--- a/android/support/v14/preference/SwitchPreference.java
+++ /dev/null
@@ -1,247 +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.v14.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v7.preference.AndroidResources;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.support.v7.preference.R;
-import android.support.v7.preference.TwoStatePreference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.Checkable;
-import android.widget.CompoundButton;
-import android.widget.Switch;
-
-/**
- * A {@link android.support.v7.preference.Preference} that provides a two-state toggleable option.
- * <p>
- * This preference will store a boolean into the SharedPreferences.
- *
- * @attr name android:summaryOff
- * @attr name android:summaryOn
- * @attr name android:switchTextOff
- * @attr name android:switchTextOn
- * @attr name android:disableDependentsState
- */
-public class SwitchPreference extends TwoStatePreference {
-    private final Listener mListener = new Listener();
-
-    // Switch text for on and off states
-    private CharSequence mSwitchOn;
-    private CharSequence mSwitchOff;
-
-    private class Listener implements CompoundButton.OnCheckedChangeListener {
-        @Override
-        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            if (!callChangeListener(isChecked)) {
-                // Listener didn't like it, change it back.
-                // CompoundButton will make sure we don't recurse.
-                buttonView.setChecked(!isChecked);
-                return;
-            }
-
-            SwitchPreference.this.setChecked(isChecked);
-        }
-    }
-
-    /**
-     * Construct a new SwitchPreference with the given style options.
-     *
-     * @param context The Context that will style this preference
-     * @param attrs Style attributes that differ from the default
-     * @param defStyleAttr 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 A resource identifier of a style resource that
-     *        supplies default values for the view, used only if
-     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
-     *        to not look for defaults.
-     */
-    public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.SwitchPreference, defStyleAttr, defStyleRes);
-
-        setSummaryOn(TypedArrayUtils.getString(a, R.styleable.SwitchPreference_summaryOn,
-                R.styleable.SwitchPreference_android_summaryOn));
-
-        setSummaryOff(TypedArrayUtils.getString(a, R.styleable.SwitchPreference_summaryOff,
-                R.styleable.SwitchPreference_android_summaryOff));
-
-        setSwitchTextOn(TypedArrayUtils.getString(a,
-                R.styleable.SwitchPreference_switchTextOn,
-                R.styleable.SwitchPreference_android_switchTextOn));
-
-        setSwitchTextOff(TypedArrayUtils.getString(a,
-                R.styleable.SwitchPreference_switchTextOff,
-                R.styleable.SwitchPreference_android_switchTextOff));
-
-        setDisableDependentsState(TypedArrayUtils.getBoolean(a,
-                R.styleable.SwitchPreference_disableDependentsState,
-                R.styleable.SwitchPreference_android_disableDependentsState, false));
-
-        a.recycle();
-    }
-
-    /**
-     * Construct a new SwitchPreference with the given style options.
-     *
-     * @param context The Context that will style this preference
-     * @param attrs Style attributes that differ from the default
-     * @param defStyleAttr 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 SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    /**
-     * Construct a new SwitchPreference with the given style options.
-     *
-     * @param context The Context that will style this preference
-     * @param attrs Style attributes that differ from the default
-     */
-    public SwitchPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context,
-                android.support.v7.preference.R.attr.switchPreferenceStyle,
-                android.R.attr.switchPreferenceStyle));
-    }
-
-    /**
-     * Construct a new SwitchPreference with default style options.
-     *
-     * @param context The Context that will style this preference
-     */
-    public SwitchPreference(Context context) {
-        this(context, null);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-        View switchView = holder.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
-        syncSwitchView(switchView);
-        syncSummaryView(holder);
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the on state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param onText Text to display in the on state
-     */
-    public void setSwitchTextOn(CharSequence onText) {
-        mSwitchOn = onText;
-        notifyChanged();
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the off state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param offText Text to display in the off state
-     */
-    public void setSwitchTextOff(CharSequence offText) {
-        mSwitchOff = offText;
-        notifyChanged();
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the on state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param resId The text as a string resource ID
-     */
-    public void setSwitchTextOn(int resId) {
-        setSwitchTextOn(getContext().getString(resId));
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the off state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param resId The text as a string resource ID
-     */
-    public void setSwitchTextOff(int resId) {
-        setSwitchTextOff(getContext().getString(resId));
-    }
-
-    /**
-     * @return The text that will be displayed on the switch widget in the on state
-     */
-    public CharSequence getSwitchTextOn() {
-        return mSwitchOn;
-    }
-
-    /**
-     * @return The text that will be displayed on the switch widget in the off state
-     */
-    public CharSequence getSwitchTextOff() {
-        return mSwitchOff;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected void performClick(View view) {
-        super.performClick(view);
-        syncViewIfAccessibilityEnabled(view);
-    }
-
-
-    private void syncViewIfAccessibilityEnabled(View view) {
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (!accessibilityManager.isEnabled()) {
-            return;
-        }
-
-        View switchView = view.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);
-        syncSwitchView(switchView);
-
-        View summaryView = view.findViewById(android.R.id.summary);
-        syncSummaryView(summaryView);
-    }
-
-    private void syncSwitchView(View view) {
-        if (view instanceof Switch) {
-            final Switch switchView = (Switch) view;
-            switchView.setOnCheckedChangeListener(null);
-        }
-        if (view instanceof Checkable) {
-            ((Checkable) view).setChecked(mChecked);
-        }
-        if (view instanceof Switch) {
-            final Switch switchView = (Switch) view;
-            switchView.setTextOn(mSwitchOn);
-            switchView.setTextOff(mSwitchOff);
-            switchView.setOnCheckedChangeListener(mListener);
-        }
-    }
-}
diff --git a/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java b/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
deleted file mode 100644
index 0396469..0000000
--- a/android/support/v17/internal/widget/OutlineOnlyWithChildrenFrameLayout.java
+++ /dev/null
@@ -1,86 +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.v17.internal.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.Outline;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
-
-/**
- * {@link FrameLayout} subclass that provides an outline only when it has children, so that it does
- * not cast a shadow when empty.
- *
- * @hide
- */
-@RequiresApi(21)
-@RestrictTo(LIBRARY_GROUP)
-public class OutlineOnlyWithChildrenFrameLayout extends FrameLayout {
-
-    private ViewOutlineProvider mMagicalOutlineProvider;
-    private ViewOutlineProvider mInnerOutlineProvider;
-
-    public OutlineOnlyWithChildrenFrameLayout(Context context) {
-        super(context);
-    }
-
-    public OutlineOnlyWithChildrenFrameLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public OutlineOnlyWithChildrenFrameLayout(Context context, AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public OutlineOnlyWithChildrenFrameLayout(Context context, AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        invalidateOutline();
-    }
-
-    @Override
-    public void setOutlineProvider(ViewOutlineProvider provider) {
-        mInnerOutlineProvider = provider;
-        if (mMagicalOutlineProvider == null) {
-            // Can't initialize this directly because this method is called from the superclass's
-            // constructor.
-            mMagicalOutlineProvider = new ViewOutlineProvider() {
-                @Override
-                public void getOutline(View view, Outline outline) {
-                    if (getChildCount() > 0) {
-                        mInnerOutlineProvider.getOutline(view, outline);
-                    } else {
-                        ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
-                    }
-                }
-            };
-        }
-        super.setOutlineProvider(mMagicalOutlineProvider);
-    }
-}
diff --git a/android/support/v17/leanback/animation/LogAccelerateInterpolator.java b/android/support/v17/leanback/animation/LogAccelerateInterpolator.java
deleted file mode 100644
index 9a41f4a..0000000
--- a/android/support/v17/leanback/animation/LogAccelerateInterpolator.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.v17.leanback.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.TimeInterpolator;
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class LogAccelerateInterpolator implements TimeInterpolator {
-
-    int mBase;
-    int mDrift;
-    final float mLogScale;
-
-    public LogAccelerateInterpolator(int base, int drift) {
-        mBase = base;
-        mDrift = drift;
-        mLogScale = 1f / computeLog(1, mBase, mDrift);
-    }
-
-    static float computeLog(float t, int base, int drift) {
-        return (float) -Math.pow(base, -t) + 1 + (drift * t);
-    }
-
-    @Override
-    public float getInterpolation(float t) {
-        return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
-    }
-}
diff --git a/android/support/v17/leanback/animation/LogDecelerateInterpolator.java b/android/support/v17/leanback/animation/LogDecelerateInterpolator.java
deleted file mode 100644
index 3a11ffd..0000000
--- a/android/support/v17/leanback/animation/LogDecelerateInterpolator.java
+++ /dev/null
@@ -1,46 +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.v17.leanback.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.TimeInterpolator;
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class LogDecelerateInterpolator implements TimeInterpolator {
-
-    int mBase;
-    int mDrift;
-    final float mLogScale;
-
-    public LogDecelerateInterpolator(int base, int drift) {
-        mBase = base;
-        mDrift = drift;
-
-        mLogScale = 1f / computeLog(1, mBase, mDrift);
-    }
-
-    static float computeLog(float t, int base, int drift) {
-        return (float) -Math.pow(base, -t) + 1 + (drift * t);
-    }
-
-    @Override
-    public float getInterpolation(float t) {
-        return computeLog(t, mBase, mDrift) * mLogScale;
-    }
-}
diff --git a/android/support/v17/leanback/app/BackgroundFragment.java b/android/support/v17/leanback/app/BackgroundFragment.java
deleted file mode 100644
index 705124a..0000000
--- a/android/support/v17/leanback/app/BackgroundFragment.java
+++ /dev/null
@@ -1,77 +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.v17.leanback.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Fragment;
-import android.support.annotation.RestrictTo;
-
-/**
- * Fragment used by the background manager.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class BackgroundFragment extends Fragment {
-    private BackgroundManager mBackgroundManager;
-
-    void setBackgroundManager(BackgroundManager backgroundManager) {
-        mBackgroundManager = backgroundManager;
-    }
-
-    BackgroundManager getBackgroundManager() {
-        return mBackgroundManager;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        // mBackgroundManager might be null:
-        // if BackgroundFragment is just restored by FragmentManager,
-        // and user does not call BackgroundManager.getInstance() yet.
-        if (mBackgroundManager != null) {
-            mBackgroundManager.onActivityStart();
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        // mBackgroundManager might be null:
-        // if BackgroundFragment is just restored by FragmentManager,
-        // and user does not call BackgroundManager.getInstance() yet.
-        if (mBackgroundManager != null) {
-            mBackgroundManager.onResume();
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mBackgroundManager != null) {
-            mBackgroundManager.onStop();
-        }
-        super.onStop();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        // mBackgroundManager might be null:
-        // if BackgroundFragment is just restored by FragmentManager,
-        // and user does not call BackgroundManager.getInstance() yet.
-        if (mBackgroundManager != null) {
-            mBackgroundManager.detach();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/BackgroundManager.java b/android/support/v17/leanback/app/BackgroundManager.java
deleted file mode 100644
index 262a5a6..0000000
--- a/android/support/v17/leanback/app/BackgroundManager.java
+++ /dev/null
@@ -1,1064 +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.v17.leanback.app;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.os.Handler;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.BackgroundHelper;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.animation.FastOutLinearInInterpolator;
-import android.util.Log;
-import android.view.View;
-import android.view.Window;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Supports background image continuity between multiple Activities.
- *
- * <p>An Activity should instantiate a BackgroundManager and {@link #attach}
- * to the Activity's window.  When the Activity is started, the background is
- * initialized to the current background values stored in a continuity service.
- * The background continuity service is updated as the background is updated.
- *
- * <p>At some point, for example when it is stopped, the Activity may release
- * its background state.
- *
- * <p>When an Activity is resumed, if the BackgroundManager has not been
- * released, the continuity service is updated from the BackgroundManager state.
- * If the BackgroundManager was released, the BackgroundManager inherits the
- * current state from the continuity service.
- *
- * <p>When the last Activity is destroyed, the background state is reset.
- *
- * <p>Backgrounds consist of several layers, from back to front:
- * <ul>
- *   <li>the background Drawable of the theme</li>
- *   <li>a solid color (set via {@link #setColor})</li>
- *   <li>two Drawables, previous and current (set via {@link #setBitmap} or
- *   {@link #setDrawable}), which may be in transition</li>
- * </ul>
- *
- * <p>BackgroundManager holds references to potentially large bitmap Drawables.
- * Call {@link #release} to release these references when the Activity is not
- * visible.
- */
-// TODO: support for multiple app processes requires a proper android service
-// instead of the shared memory "service" implemented here. Such a service could
-// support continuity between fragments of different applications if desired.
-public final class BackgroundManager {
-
-    static final String TAG = "BackgroundManager";
-    static final boolean DEBUG = false;
-
-    static final int FULL_ALPHA = 255;
-    private static final int CHANGE_BG_DELAY_MS = 500;
-    private static final int FADE_DURATION = 500;
-
-    private static final String FRAGMENT_TAG = BackgroundManager.class.getCanonicalName();
-
-    Activity mContext;
-    Handler mHandler;
-    private View mBgView;
-    private BackgroundContinuityService mService;
-    private int mThemeDrawableResourceId;
-    private BackgroundFragment mFragmentState;
-    private boolean mAutoReleaseOnStop = true;
-
-    private int mHeightPx;
-    private int mWidthPx;
-    int mBackgroundColor;
-    Drawable mBackgroundDrawable;
-    private boolean mAttached;
-    private long mLastSetTime;
-
-    private final Interpolator mAccelerateInterpolator;
-    private final Interpolator mDecelerateInterpolator;
-    final ValueAnimator mAnimator;
-
-    static class BitmapDrawable extends Drawable {
-
-        static final class ConstantState extends Drawable.ConstantState {
-            final Bitmap mBitmap;
-            final Matrix mMatrix;
-            final Paint mPaint = new Paint();
-
-            ConstantState(Bitmap bitmap, Matrix matrix) {
-                mBitmap = bitmap;
-                mMatrix = matrix != null ? matrix : new Matrix();
-                mPaint.setFilterBitmap(true);
-            }
-
-            ConstantState(ConstantState copyFrom) {
-                mBitmap = copyFrom.mBitmap;
-                mMatrix = copyFrom.mMatrix != null ? new Matrix(copyFrom.mMatrix) : new Matrix();
-                if (copyFrom.mPaint.getAlpha() != FULL_ALPHA) {
-                    mPaint.setAlpha(copyFrom.mPaint.getAlpha());
-                }
-                if (copyFrom.mPaint.getColorFilter() != null) {
-                    mPaint.setColorFilter(copyFrom.mPaint.getColorFilter());
-                }
-                mPaint.setFilterBitmap(true);
-            }
-
-            @Override
-            public Drawable newDrawable() {
-                return new BitmapDrawable(this);
-            }
-
-            @Override
-            public int getChangingConfigurations() {
-                return 0;
-            }
-        }
-
-        ConstantState mState;
-        boolean mMutated;
-
-        BitmapDrawable(Resources resources, Bitmap bitmap) {
-            this(resources, bitmap, null);
-        }
-
-        BitmapDrawable(Resources resources, Bitmap bitmap, Matrix matrix) {
-            mState = new ConstantState(bitmap, matrix);
-        }
-
-        BitmapDrawable(ConstantState state) {
-            mState = state;
-        }
-
-        Bitmap getBitmap() {
-            return mState.mBitmap;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            if (mState.mBitmap == null) {
-                return;
-            }
-            if (mState.mPaint.getAlpha() < FULL_ALPHA && mState.mPaint.getColorFilter() != null) {
-                throw new IllegalStateException("Can't draw with translucent alpha and color filter");
-            }
-            canvas.drawBitmap(mState.mBitmap, mState.mMatrix, mState.mPaint);
-        }
-
-        @Override
-        public int getOpacity() {
-            return android.graphics.PixelFormat.TRANSLUCENT;
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-            mutate();
-            if (mState.mPaint.getAlpha() != alpha) {
-                mState.mPaint.setAlpha(alpha);
-                invalidateSelf();
-            }
-        }
-
-        /**
-         * Does not invalidateSelf to avoid recursion issues.
-         * Caller must ensure appropriate invalidation.
-         */
-        @Override
-        public void setColorFilter(ColorFilter cf) {
-            mutate();
-            mState.mPaint.setColorFilter(cf);
-            invalidateSelf();
-        }
-
-        @Override
-        public ColorFilter getColorFilter() {
-            return mState.mPaint.getColorFilter();
-        }
-
-        @Override
-        public ConstantState getConstantState() {
-            return mState;
-        }
-
-        @NonNull
-        @Override
-        public Drawable mutate() {
-            if (!mMutated) {
-                mMutated = true;
-                mState = new ConstantState(mState);
-            }
-            return this;
-        }
-    }
-
-    static final class DrawableWrapper {
-        int mAlpha = FULL_ALPHA;
-        final Drawable mDrawable;
-
-        public DrawableWrapper(Drawable drawable) {
-            mDrawable = drawable;
-        }
-        public DrawableWrapper(DrawableWrapper wrapper, Drawable drawable) {
-            mDrawable = drawable;
-            mAlpha = wrapper.mAlpha;
-        }
-
-        public Drawable getDrawable() {
-            return mDrawable;
-        }
-
-        public void setColor(int color) {
-            ((ColorDrawable) mDrawable).setColor(color);
-        }
-    }
-
-    static final class TranslucentLayerDrawable extends LayerDrawable {
-        DrawableWrapper[] mWrapper;
-        int mAlpha = FULL_ALPHA;
-        boolean mSuspendInvalidation;
-        WeakReference<BackgroundManager> mManagerWeakReference;
-
-        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++) {
-                mWrapper[i] = new DrawableWrapper(drawables[i]);
-            }
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-            if (mAlpha != alpha) {
-                mAlpha = alpha;
-                invalidateSelf();
-                BackgroundManager manager = mManagerWeakReference.get();
-                if (manager != null) {
-                    manager.postChangeRunnable();
-                }
-            }
-        }
-
-        void setWrapperAlpha(int wrapperIndex, int alpha) {
-            if (mWrapper[wrapperIndex] != null) {
-                mWrapper[wrapperIndex].mAlpha = alpha;
-                invalidateSelf();
-            }
-        }
-
-        // Queried by system transitions
-        @Override
-        public int getAlpha() {
-            return mAlpha;
-        }
-
-        @Override
-        public Drawable mutate() {
-            Drawable drawable = super.mutate();
-            int count = getNumberOfLayers();
-            for (int i = 0; i < count; i++) {
-                if (mWrapper[i] != null) {
-                    mWrapper[i] = new DrawableWrapper(mWrapper[i], getDrawable(i));
-                }
-            }
-            return drawable;
-        }
-
-        @Override
-        public int getOpacity() {
-            return PixelFormat.TRANSLUCENT;
-        }
-
-        @Override
-        public boolean setDrawableByLayerId(int id, Drawable drawable) {
-            return updateDrawable(id, drawable) != null;
-        }
-
-        public DrawableWrapper updateDrawable(int id, Drawable drawable) {
-            super.setDrawableByLayerId(id, drawable);
-            for (int i = 0; i < getNumberOfLayers(); i++) {
-                if (getId(i) == id) {
-                    mWrapper[i] = new DrawableWrapper(drawable);
-                    // Must come after mWrapper was updated so it can be seen by updateColorFilter
-                    invalidateSelf();
-                    return mWrapper[i];
-                }
-            }
-            return null;
-        }
-
-        public void clearDrawable(int id, Context context) {
-            for (int i = 0; i < getNumberOfLayers(); i++) {
-                if (getId(i) == id) {
-                    mWrapper[i] = null;
-                    if (!(getDrawable(i) instanceof EmptyDrawable)) {
-                        super.setDrawableByLayerId(id, createEmptyDrawable(context));
-                    }
-                    break;
-                }
-            }
-        }
-
-        public int findWrapperIndexById(int id) {
-            for (int i = 0; i < getNumberOfLayers(); i++) {
-                if (getId(i) == id) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        @Override
-        public void invalidateDrawable(Drawable who) {
-            // Prevent invalidate when temporarily change child drawable's alpha in draw()
-            if (!mSuspendInvalidation) {
-                super.invalidateDrawable(who);
-            }
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            for (int i = 0; i < mWrapper.length; i++) {
-                final Drawable d;
-                // For each child drawable, we multiple Wrapper's alpha and LayerDrawable's alpha
-                // temporarily using mSuspendInvalidation to suppress invalidate event.
-                if (mWrapper[i] != null && (d = mWrapper[i].getDrawable()) != null) {
-                    int alpha = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
-                            ? DrawableCompat.getAlpha(d) : FULL_ALPHA;
-                    final int savedAlpha = alpha;
-                    int multiple = 0;
-                    if (mAlpha < FULL_ALPHA) {
-                        alpha = alpha * mAlpha;
-                        multiple++;
-                    }
-                    if (mWrapper[i].mAlpha < FULL_ALPHA) {
-                        alpha = alpha * mWrapper[i].mAlpha;
-                        multiple++;
-                    }
-                    if (multiple == 0) {
-                        d.draw(canvas);
-                    } else {
-                        if (multiple == 1) {
-                            alpha = alpha / FULL_ALPHA;
-                        } else if (multiple == 2) {
-                            alpha = alpha / (FULL_ALPHA * FULL_ALPHA);
-                        }
-                        try {
-                            mSuspendInvalidation = true;
-                            d.setAlpha(alpha);
-                            d.draw(canvas);
-                            d.setAlpha(savedAlpha);
-                        } finally {
-                            mSuspendInvalidation = false;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    TranslucentLayerDrawable createTranslucentLayerDrawable(
-            LayerDrawable layerDrawable) {
-        int numChildren = layerDrawable.getNumberOfLayers();
-        Drawable[] drawables = new Drawable[numChildren];
-        for (int i = 0; i < numChildren; i++) {
-            drawables[i] = layerDrawable.getDrawable(i);
-        }
-        TranslucentLayerDrawable result = new TranslucentLayerDrawable(this, drawables);
-        for (int i = 0; i < numChildren; i++) {
-            result.setId(i, layerDrawable.getId(i));
-        }
-        return result;
-    }
-
-    TranslucentLayerDrawable mLayerDrawable;
-    int mImageInWrapperIndex;
-    int mImageOutWrapperIndex;
-    ChangeBackgroundRunnable mChangeRunnable;
-    private boolean mChangeRunnablePending;
-
-    private final Animator.AnimatorListener mAnimationListener = new Animator.AnimatorListener() {
-        final Runnable mRunnable = new Runnable() {
-            @Override
-            public void run() {
-                postChangeRunnable();
-            }
-        };
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-        }
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-        }
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (mLayerDrawable != null) {
-                mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
-            }
-            mHandler.post(mRunnable);
-        }
-        @Override
-        public void onAnimationCancel(Animator animation) {
-        }
-    };
-
-    private final ValueAnimator.AnimatorUpdateListener mAnimationUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            int fadeInAlpha = (Integer) animation.getAnimatedValue();
-            if (mImageInWrapperIndex != -1) {
-                mLayerDrawable.setWrapperAlpha(mImageInWrapperIndex, fadeInAlpha);
-            }
-        }
-    };
-
-    /**
-     * Shared memory continuity service.
-     */
-    private static class BackgroundContinuityService {
-        private static final String TAG = "BackgroundContinuity";
-        private static boolean DEBUG = BackgroundManager.DEBUG;
-
-        private static BackgroundContinuityService sService = new BackgroundContinuityService();
-
-        private int mColor;
-        private Drawable mDrawable;
-        private int mCount;
-
-        /** Single cache of theme drawable */
-        private int mLastThemeDrawableId;
-        private WeakReference<Drawable.ConstantState> mLastThemeDrawableState;
-
-        private BackgroundContinuityService() {
-            reset();
-        }
-
-        private void reset() {
-            mColor = Color.TRANSPARENT;
-            mDrawable = null;
-        }
-
-        public static BackgroundContinuityService getInstance() {
-            final int count = sService.mCount++;
-            if (DEBUG) Log.v(TAG, "Returning instance with new count " + count);
-            return sService;
-        }
-
-        public void unref() {
-            if (mCount <= 0) throw new IllegalStateException("Can't unref, count " + mCount);
-            if (--mCount == 0) {
-                if (DEBUG) Log.v(TAG, "mCount is zero, resetting");
-                reset();
-            }
-        }
-        public int getColor() {
-            return mColor;
-        }
-        public Drawable getDrawable() {
-            return mDrawable;
-        }
-        public void setColor(int color) {
-            mColor = color;
-            mDrawable = null;
-        }
-        public void setDrawable(Drawable drawable) {
-            mDrawable = drawable;
-        }
-        public Drawable getThemeDrawable(Context context, int themeDrawableId) {
-            Drawable drawable = null;
-            if (mLastThemeDrawableState != null && mLastThemeDrawableId == themeDrawableId) {
-                Drawable.ConstantState drawableState = mLastThemeDrawableState.get();
-                if (DEBUG) Log.v(TAG, "got cached theme drawable state " + drawableState);
-                if (drawableState != null) {
-                    drawable = drawableState.newDrawable();
-                }
-            }
-            if (drawable == null) {
-                drawable = ContextCompat.getDrawable(context, themeDrawableId);
-                if (DEBUG) Log.v(TAG, "loaded theme drawable " + drawable);
-                mLastThemeDrawableState = new WeakReference<Drawable.ConstantState>(
-                        drawable.getConstantState());
-                mLastThemeDrawableId = themeDrawableId;
-            }
-            // No mutate required because this drawable is never manipulated.
-            return drawable;
-        }
-    }
-
-    Drawable getDefaultDrawable() {
-        if (mBackgroundColor != Color.TRANSPARENT) {
-            return new ColorDrawable(mBackgroundColor);
-        } else {
-            return getThemeDrawable();
-        }
-    }
-
-    private Drawable getThemeDrawable() {
-        Drawable drawable = null;
-        if (mThemeDrawableResourceId != -1) {
-            drawable = mService.getThemeDrawable(mContext, mThemeDrawableResourceId);
-        }
-        if (drawable == null) {
-            drawable = createEmptyDrawable(mContext);
-        }
-        return drawable;
-    }
-
-    /**
-     * Returns the BackgroundManager associated with the given Activity.
-     * <p>
-     * The BackgroundManager will be created on-demand for each individual
-     * Activity. Subsequent calls will return the same BackgroundManager created
-     * for this Activity.
-     */
-    public static BackgroundManager getInstance(Activity activity) {
-        BackgroundFragment fragment = (BackgroundFragment) activity.getFragmentManager()
-                .findFragmentByTag(FRAGMENT_TAG);
-        if (fragment != null) {
-            BackgroundManager manager = fragment.getBackgroundManager();
-            if (manager != null) {
-                return manager;
-            }
-            // manager is null: this is a fragment restored by FragmentManager,
-            // fall through to create a BackgroundManager attach to it.
-        }
-        return new BackgroundManager(activity);
-    }
-
-    private BackgroundManager(Activity activity) {
-        mContext = activity;
-        mService = BackgroundContinuityService.getInstance();
-        mHeightPx = mContext.getResources().getDisplayMetrics().heightPixels;
-        mWidthPx = mContext.getResources().getDisplayMetrics().widthPixels;
-        mHandler = new Handler();
-
-        Interpolator defaultInterpolator = new FastOutLinearInInterpolator();
-        mAccelerateInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.anim.accelerate_interpolator);
-        mDecelerateInterpolator = AnimationUtils.loadInterpolator(mContext,
-                android.R.anim.decelerate_interpolator);
-
-        mAnimator = ValueAnimator.ofInt(0, FULL_ALPHA);
-        mAnimator.addListener(mAnimationListener);
-        mAnimator.addUpdateListener(mAnimationUpdateListener);
-        mAnimator.setInterpolator(defaultInterpolator);
-
-        TypedArray ta = activity.getTheme().obtainStyledAttributes(new int[] {
-                android.R.attr.windowBackground });
-        mThemeDrawableResourceId = ta.getResourceId(0, -1);
-        if (mThemeDrawableResourceId < 0) {
-            if (DEBUG) Log.v(TAG, "BackgroundManager no window background resource!");
-        }
-        ta.recycle();
-
-        createFragment(activity);
-    }
-
-    private void createFragment(Activity activity) {
-        // Use a fragment to ensure the background manager gets detached properly.
-        BackgroundFragment fragment = (BackgroundFragment) activity.getFragmentManager()
-                .findFragmentByTag(FRAGMENT_TAG);
-        if (fragment == null) {
-            fragment = new BackgroundFragment();
-            activity.getFragmentManager().beginTransaction().add(fragment, FRAGMENT_TAG).commit();
-        } else {
-            if (fragment.getBackgroundManager() != null) {
-                throw new IllegalStateException("Created duplicated BackgroundManager for same "
-                        + "activity, please use getInstance() instead");
-            }
-        }
-        fragment.setBackgroundManager(this);
-        mFragmentState = fragment;
-    }
-
-    DrawableWrapper getImageInWrapper() {
-        return mLayerDrawable == null
-                ? null : mLayerDrawable.mWrapper[mImageInWrapperIndex];
-    }
-
-    DrawableWrapper getImageOutWrapper() {
-        return mLayerDrawable == null
-                ? null : mLayerDrawable.mWrapper[mImageOutWrapperIndex];
-    }
-
-    /**
-     * Synchronizes state when the owning Activity is started.
-     * At that point the view becomes visible.
-     */
-    void onActivityStart() {
-        updateImmediate();
-    }
-
-    void onStop() {
-        if (isAutoReleaseOnStop()) {
-            release();
-        }
-    }
-
-    void onResume() {
-        if (DEBUG) Log.v(TAG, "onResume " + this);
-        postChangeRunnable();
-    }
-
-    private void syncWithService() {
-        int color = mService.getColor();
-        Drawable drawable = mService.getDrawable();
-
-        if (DEBUG) Log.v(TAG, "syncWithService color " + Integer.toHexString(color)
-                + " drawable " + drawable);
-
-        mBackgroundColor = color;
-        mBackgroundDrawable = drawable == null ? null :
-            drawable.getConstantState().newDrawable().mutate();
-
-        updateImmediate();
-    }
-
-    /**
-     * Makes the background visible on the given Window. The background manager must be attached
-     * when the background is set.
-     */
-    public void attach(Window window) {
-        attachToViewInternal(window.getDecorView());
-    }
-
-    /**
-     * Sets the resource id for the drawable to be shown when there is no background set.
-     * Overrides the window background drawable from the theme. This should
-     * be called before attaching.
-     */
-    public void setThemeDrawableResourceId(int resourceId) {
-        mThemeDrawableResourceId = resourceId;
-    }
-
-    /**
-     * Adds the composite drawable to the given view.
-     */
-    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) {
-        if (mAttached) {
-            throw new IllegalStateException("Already attached to " + mBgView);
-        }
-        mBgView = sceneRoot;
-        mAttached = true;
-        syncWithService();
-    }
-
-    /**
-     * Returns true if the background manager is currently attached; false otherwise.
-     */
-    public boolean isAttached() {
-        return mAttached;
-    }
-
-    /**
-     * Release references to Drawables and put the BackgroundManager into the
-     * detached state. Called when the associated Activity is destroyed.
-     */
-    void detach() {
-        if (DEBUG) Log.v(TAG, "detach " + this);
-        release();
-
-        mBgView = null;
-        mAttached = false;
-
-        if (mService != null) {
-            mService.unref();
-            mService = null;
-        }
-    }
-
-    /**
-     * Release references to Drawable/Bitmap. Typically called in Activity onStop() to reduce memory
-     * overhead when not visible. It's app's responsibility to restore the drawable/bitmap in
-     * Activity onStart(). The method is automatically called in onStop() when
-     * {@link #isAutoReleaseOnStop()} is true.
-     * @see #setAutoReleaseOnStop(boolean)
-     */
-    public void release() {
-        if (DEBUG) Log.v(TAG, "release " + this);
-        if (mChangeRunnable != null) {
-            mHandler.removeCallbacks(mChangeRunnable);
-            mChangeRunnable = null;
-        }
-        if (mAnimator.isStarted()) {
-            mAnimator.cancel();
-        }
-        if (mLayerDrawable != null) {
-            mLayerDrawable.clearDrawable(R.id.background_imagein, mContext);
-            mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
-            mLayerDrawable = null;
-        }
-        mBackgroundDrawable = null;
-    }
-
-    /**
-     * Sets the drawable used as a dim layer.
-     * @deprecated No longer support dim layer.
-     */
-    @Deprecated
-    public void setDimLayer(Drawable drawable) {
-    }
-
-    /**
-     * Returns the drawable used as a dim layer.
-     * @deprecated No longer support dim layer.
-     */
-    @Deprecated
-    public Drawable getDimLayer() {
-        return null;
-    }
-
-    /**
-     * Returns the default drawable used as a dim layer.
-     * @deprecated No longer support dim layer.
-     */
-    @Deprecated
-    public Drawable getDefaultDimLayer() {
-        return ContextCompat.getDrawable(mContext, R.color.lb_background_protection);
-    }
-
-    void postChangeRunnable() {
-        if (mChangeRunnable == null || !mChangeRunnablePending) {
-            return;
-        }
-
-        // Postpone a pending change runnable until: no existing change animation in progress &&
-        // activity is resumed (in the foreground) && layerdrawable fully opaque.
-        // If the layerdrawable is translucent then an activity transition is in progress
-        // and we want to use the optimized drawing path for performance reasons (see
-        // OptimizedTranslucentLayerDrawable).
-        if (mAnimator.isStarted()) {
-            if (DEBUG) Log.v(TAG, "animation in progress");
-        } else if (!mFragmentState.isResumed()) {
-            if (DEBUG) Log.v(TAG, "not resumed");
-        } else if (mLayerDrawable.getAlpha() < FULL_ALPHA) {
-            if (DEBUG) Log.v(TAG, "in transition, alpha " + mLayerDrawable.getAlpha());
-        } else {
-            long delayMs = getRunnableDelay();
-            if (DEBUG) Log.v(TAG, "posting runnable delayMs " + delayMs);
-            mLastSetTime = System.currentTimeMillis();
-            mHandler.postDelayed(mChangeRunnable, delayMs);
-            mChangeRunnablePending = false;
-        }
-    }
-
-    private void lazyInit() {
-        if (mLayerDrawable != null) {
-            return;
-        }
-
-        LayerDrawable layerDrawable = (LayerDrawable)
-                ContextCompat.getDrawable(mContext, R.drawable.lb_background).mutate();
-        mLayerDrawable = createTranslucentLayerDrawable(layerDrawable);
-        mImageInWrapperIndex = mLayerDrawable.findWrapperIndexById(R.id.background_imagein);
-        mImageOutWrapperIndex = mLayerDrawable.findWrapperIndexById(R.id.background_imageout);
-        BackgroundHelper.setBackgroundPreservingAlpha(mBgView, mLayerDrawable);
-    }
-
-    private void updateImmediate() {
-        if (!mAttached) {
-            return;
-        }
-        lazyInit();
-
-        if (mBackgroundDrawable == null) {
-            if (DEBUG) Log.v(TAG, "Use defefault background");
-            mLayerDrawable.updateDrawable(R.id.background_imagein, getDefaultDrawable());
-        } else {
-            if (DEBUG) Log.v(TAG, "Background drawable is available " + mBackgroundDrawable);
-            mLayerDrawable.updateDrawable(R.id.background_imagein, mBackgroundDrawable);
-        }
-        mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
-    }
-
-    /**
-     * Sets the background to the given color. The timing for when this becomes
-     * visible in the app is undefined and may take place after a small delay.
-     */
-    public void setColor(@ColorInt int color) {
-        if (DEBUG) Log.v(TAG, "setColor " + Integer.toHexString(color));
-
-        mService.setColor(color);
-        mBackgroundColor = color;
-        mBackgroundDrawable = null;
-        if (mLayerDrawable == null) {
-            return;
-        }
-        setDrawableInternal(getDefaultDrawable());
-    }
-
-    /**
-     * Sets the given drawable into the background. The provided Drawable will be
-     * used unmodified as the background, without any scaling or cropping
-     * applied to it. The timing for when this becomes visible in the app is
-     * undefined and may take place after a small delay.
-     */
-    public void setDrawable(Drawable drawable) {
-        if (DEBUG) Log.v(TAG, "setBackgroundDrawable " + drawable);
-
-        mService.setDrawable(drawable);
-        mBackgroundDrawable = drawable;
-        if (mLayerDrawable == null) {
-            return;
-        }
-        if (drawable == null) {
-            setDrawableInternal(getDefaultDrawable());
-        } else {
-            setDrawableInternal(drawable);
-        }
-    }
-
-    /**
-     * Clears the Drawable set by {@link #setDrawable(Drawable)} or {@link #setBitmap(Bitmap)}.
-     * BackgroundManager will show a solid color set by {@link #setColor(int)} or theme drawable
-     * if color is not provided.
-     */
-    public void clearDrawable() {
-        setDrawable(null);
-    }
-
-    private void setDrawableInternal(Drawable drawable) {
-        if (!mAttached) {
-            throw new IllegalStateException("Must attach before setting background drawable");
-        }
-
-        if (mChangeRunnable != null) {
-            if (sameDrawable(drawable, mChangeRunnable.mDrawable)) {
-                if (DEBUG) Log.v(TAG, "new drawable same as pending");
-                return;
-            }
-            mHandler.removeCallbacks(mChangeRunnable);
-            mChangeRunnable = null;
-        }
-
-        mChangeRunnable = new ChangeBackgroundRunnable(drawable);
-        mChangeRunnablePending = true;
-
-        postChangeRunnable();
-    }
-
-    private long getRunnableDelay() {
-        return Math.max(0, mLastSetTime + CHANGE_BG_DELAY_MS - System.currentTimeMillis());
-    }
-
-    /**
-     * Sets the given bitmap into the background. When using setCoverImageBitmap to set the
-     * background, the provided bitmap will be scaled and cropped to correctly
-     * fit within the dimensions of the view. The timing for when this becomes
-     * visible in the app is undefined and may take place after a small delay.
-     */
-    public void setBitmap(Bitmap bitmap) {
-        if (DEBUG) {
-            Log.v(TAG, "setCoverImageBitmap " + bitmap);
-        }
-
-        if (bitmap == null) {
-            setDrawable(null);
-            return;
-        }
-
-        if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
-            if (DEBUG) {
-                Log.v(TAG, "invalid bitmap width or height");
-            }
-            return;
-        }
-
-        Matrix matrix = null;
-
-        if ((bitmap.getWidth() != mWidthPx || bitmap.getHeight() != mHeightPx)) {
-            int dwidth = bitmap.getWidth();
-            int dheight = bitmap.getHeight();
-            float scale;
-
-            // Scale proportionately to fit width and height.
-            if (dwidth * mHeightPx > mWidthPx * dheight) {
-                scale = (float) mHeightPx / (float) dheight;
-            } else {
-                scale = (float) mWidthPx / (float) dwidth;
-            }
-
-            int subX = Math.min((int) (mWidthPx / scale), dwidth);
-            int dx = Math.max(0, (dwidth - subX) / 2);
-
-            matrix = new Matrix();
-            matrix.setScale(scale, scale);
-            matrix.preTranslate(-dx, 0);
-
-            if (DEBUG) {
-                Log.v(TAG, "original image size " + bitmap.getWidth() + "x" + bitmap.getHeight()
-                        + " scale " + scale + " dx " + dx);
-            }
-        }
-
-        BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap, matrix);
-
-        setDrawable(bitmapDrawable);
-    }
-
-    /**
-     * Enable or disable call release() in Activity onStop(). Default is true.
-     * @param autoReleaseOnStop True to call release() in Activity onStop(), false otherwise.
-     */
-    public void setAutoReleaseOnStop(boolean autoReleaseOnStop) {
-        mAutoReleaseOnStop = autoReleaseOnStop;
-    }
-
-    /**
-     * @return True if release() in Activity.onStop(), false otherwise.
-     */
-    public boolean isAutoReleaseOnStop() {
-        return mAutoReleaseOnStop;
-    }
-
-    /**
-     * Returns the current background color.
-     */
-    @ColorInt
-    public final int getColor() {
-        return mBackgroundColor;
-    }
-
-    /**
-     * Returns the current background {@link Drawable}.
-     */
-    public Drawable getDrawable() {
-        return mBackgroundDrawable;
-    }
-
-    boolean sameDrawable(Drawable first, Drawable second) {
-        if (first == null || second == null) {
-            return false;
-        }
-        if (first == second) {
-            return true;
-        }
-        if (first instanceof BitmapDrawable && second instanceof BitmapDrawable) {
-            if (((BitmapDrawable) first).getBitmap().sameAs(((BitmapDrawable) second).getBitmap())) {
-                return true;
-            }
-        }
-        if (first instanceof ColorDrawable && second instanceof ColorDrawable) {
-            if (((ColorDrawable) first).getColor() == ((ColorDrawable) second).getColor()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Task which changes the background.
-     */
-    final class ChangeBackgroundRunnable implements Runnable {
-        final Drawable mDrawable;
-
-        ChangeBackgroundRunnable(Drawable drawable) {
-            mDrawable = drawable;
-        }
-
-        @Override
-        public void run() {
-            runTask();
-            mChangeRunnable = null;
-        }
-
-        private void runTask() {
-            if (mLayerDrawable == null) {
-                if (DEBUG) Log.v(TAG, "runTask while released - should not happen");
-                return;
-            }
-
-            DrawableWrapper imageInWrapper = getImageInWrapper();
-            if (imageInWrapper != null) {
-                if (sameDrawable(mDrawable, imageInWrapper.getDrawable())) {
-                    if (DEBUG) Log.v(TAG, "new drawable same as current");
-                    return;
-                }
-
-                if (DEBUG) Log.v(TAG, "moving image in to image out");
-                // Order is important! Setting a drawable "removes" the
-                // previous one from the view
-                mLayerDrawable.clearDrawable(R.id.background_imagein, mContext);
-                mLayerDrawable.updateDrawable(R.id.background_imageout,
-                        imageInWrapper.getDrawable());
-            }
-
-            applyBackgroundChanges();
-        }
-
-        void applyBackgroundChanges() {
-            if (!mAttached) {
-                return;
-            }
-
-            if (DEBUG) Log.v(TAG, "applyBackgroundChanges drawable " + mDrawable);
-
-            DrawableWrapper imageInWrapper = getImageInWrapper();
-            if (imageInWrapper == null && mDrawable != null) {
-                if (DEBUG) Log.v(TAG, "creating new imagein drawable");
-                imageInWrapper = mLayerDrawable.updateDrawable(
-                        R.id.background_imagein, mDrawable);
-                if (DEBUG) Log.v(TAG, "imageInWrapper animation starting");
-                mLayerDrawable.setWrapperAlpha(mImageInWrapperIndex, 0);
-            }
-
-            mAnimator.setDuration(FADE_DURATION);
-            mAnimator.start();
-
-        }
-
-    }
-
-    static class EmptyDrawable extends BitmapDrawable {
-        EmptyDrawable(Resources res) {
-            super(res, (Bitmap) null);
-        }
-    }
-
-    static Drawable createEmptyDrawable(Context context) {
-        return new EmptyDrawable(context.getResources());
-    }
-
-}
diff --git a/android/support/v17/leanback/app/BaseFragment.java b/android/support/v17/leanback/app/BaseFragment.java
deleted file mode 100644
index ea46011..0000000
--- a/android/support/v17/leanback/app/BaseFragment.java
+++ /dev/null
@@ -1,323 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from BaseSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.util.StateMachine;
-import android.support.v17.leanback.util.StateMachine.Condition;
-import android.support.v17.leanback.util.StateMachine.Event;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-/**
- * Base class for leanback Fragments. This class is not intended to be subclassed by apps.
- * @deprecated use {@link BaseSupportFragment}
- */
-@Deprecated
-@SuppressWarnings("FragmentNotInstantiable")
-public class BaseFragment extends BrandedFragment {
-
-    /**
-     * The start state for all
-     */
-    final State STATE_START = new State("START", true, false);
-
-    /**
-     * Initial State for ENTRNACE transition.
-     */
-    final State STATE_ENTRANCE_INIT = new State("ENTRANCE_INIT");
-
-    /**
-     * prepareEntranceTransition is just called, but view not ready yet. We can enable the
-     * busy spinner.
-     */
-    final State STATE_ENTRANCE_ON_PREPARED = new State("ENTRANCE_ON_PREPARED", true, false) {
-        @Override
-        public void run() {
-            mProgressBarManager.show();
-        }
-    };
-
-    /**
-     * prepareEntranceTransition is called and main content view to slide in was created, so we can
-     * call {@link #onEntranceTransitionPrepare}. Note that we dont set initial content to invisible
-     * in this State, the process is very different in subclass, e.g. BrowseFragment hide header
-     * views and hide main fragment view in two steps.
-     */
-    final State STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW = new State(
-            "ENTRANCE_ON_PREPARED_ON_CREATEVIEW") {
-        @Override
-        public void run() {
-            onEntranceTransitionPrepare();
-        }
-    };
-
-    /**
-     * execute the entrance transition.
-     */
-    final State STATE_ENTRANCE_PERFORM = new State("STATE_ENTRANCE_PERFORM") {
-        @Override
-        public void run() {
-            mProgressBarManager.hide();
-            onExecuteEntranceTransition();
-        }
-    };
-
-    /**
-     * execute onEntranceTransitionEnd.
-     */
-    final State STATE_ENTRANCE_ON_ENDED = new State("ENTRANCE_ON_ENDED") {
-        @Override
-        public void run() {
-            onEntranceTransitionEnd();
-        }
-    };
-
-    /**
-     * either entrance transition completed or skipped
-     */
-    final State STATE_ENTRANCE_COMPLETE = new State("ENTRANCE_COMPLETE", true, false);
-
-    /**
-     * Event fragment.onCreate()
-     */
-    final Event EVT_ON_CREATE = new Event("onCreate");
-
-    /**
-     * Event fragment.onViewCreated()
-     */
-    final Event EVT_ON_CREATEVIEW = new Event("onCreateView");
-
-    /**
-     * Event for {@link #prepareEntranceTransition()} is called.
-     */
-    final Event EVT_PREPARE_ENTRANCE = new Event("prepareEntranceTransition");
-
-    /**
-     * Event for {@link #startEntranceTransition()} is called.
-     */
-    final Event EVT_START_ENTRANCE = new Event("startEntranceTransition");
-
-    /**
-     * Event for entrance transition is ended through Transition listener.
-     */
-    final Event EVT_ENTRANCE_END = new Event("onEntranceTransitionEnd");
-
-    /**
-     * Event for skipping entrance transition if not supported.
-     */
-    final Condition COND_TRANSITION_NOT_SUPPORTED = new Condition("EntranceTransitionNotSupport") {
-        @Override
-        public boolean canProceed() {
-            return !TransitionHelper.systemSupportsEntranceTransitions();
-        }
-    };
-
-    final StateMachine mStateMachine = new StateMachine();
-
-    Object mEntranceTransition;
-    final ProgressBarManager mProgressBarManager = new ProgressBarManager();
-
-    @SuppressLint("ValidFragment")
-    BaseFragment() {
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        createStateMachineStates();
-        createStateMachineTransitions();
-        mStateMachine.start();
-        super.onCreate(savedInstanceState);
-        mStateMachine.fireEvent(EVT_ON_CREATE);
-    }
-
-    void createStateMachineStates() {
-        mStateMachine.addState(STATE_START);
-        mStateMachine.addState(STATE_ENTRANCE_INIT);
-        mStateMachine.addState(STATE_ENTRANCE_ON_PREPARED);
-        mStateMachine.addState(STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW);
-        mStateMachine.addState(STATE_ENTRANCE_PERFORM);
-        mStateMachine.addState(STATE_ENTRANCE_ON_ENDED);
-        mStateMachine.addState(STATE_ENTRANCE_COMPLETE);
-    }
-
-    void createStateMachineTransitions() {
-        mStateMachine.addTransition(STATE_START, STATE_ENTRANCE_INIT, EVT_ON_CREATE);
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_ENTRANCE_COMPLETE,
-                COND_TRANSITION_NOT_SUPPORTED);
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_ENTRANCE_COMPLETE,
-                EVT_ON_CREATEVIEW);
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_ENTRANCE_ON_PREPARED,
-                EVT_PREPARE_ENTRANCE);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW,
-                EVT_ON_CREATEVIEW);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_ENTRANCE_PERFORM,
-                EVT_START_ENTRANCE);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW,
-                STATE_ENTRANCE_PERFORM);
-        mStateMachine.addTransition(STATE_ENTRANCE_PERFORM,
-                STATE_ENTRANCE_ON_ENDED,
-                EVT_ENTRANCE_END);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_ENDED, STATE_ENTRANCE_COMPLETE);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        mStateMachine.fireEvent(EVT_ON_CREATEVIEW);
-    }
-
-    /**
-     * Enables entrance transition.<p>
-     * Entrance transition is the standard slide-in transition that shows rows of data in
-     * browse screen and details screen.
-     * <p>
-     * The method is ignored before LOLLIPOP (API21).
-     * <p>
-     * This method must be called in or
-     * before onCreate().  Typically entrance transition should be enabled when savedInstance is
-     * null so that fragment restored from instanceState does not run an extra entrance transition.
-     * When the entrance transition is enabled, the fragment will make headers and content
-     * hidden initially.
-     * When data of rows are ready, app must call {@link #startEntranceTransition()} to kick off
-     * the transition, otherwise the rows will be invisible forever.
-     * <p>
-     * It is similar to android:windowsEnterTransition and can be considered a late-executed
-     * android:windowsEnterTransition controlled by app.  There are two reasons that app needs it:
-     * <li> Workaround the problem that activity transition is not available between launcher and
-     * app.  Browse activity must programmatically start the slide-in transition.</li>
-     * <li> Separates DetailsOverviewRow transition from other rows transition.  So that
-     * the DetailsOverviewRow transition can be executed earlier without waiting for all rows
-     * to be loaded.</li>
-     * <p>
-     * Transition object is returned by createEntranceTransition().  Typically the app does not need
-     * override the default transition that browse and details provides.
-     */
-    public void prepareEntranceTransition() {
-        mStateMachine.fireEvent(EVT_PREPARE_ENTRANCE);
-    }
-
-    /**
-     * Create entrance transition.  Subclass can override to load transition from
-     * resource or construct manually.  Typically app does not need to
-     * override the default transition that browse and details provides.
-     */
-    protected Object createEntranceTransition() {
-        return null;
-    }
-
-    /**
-     * Run entrance transition.  Subclass may use TransitionManager to perform
-     * go(Scene) or beginDelayedTransition().  App should not override the default
-     * implementation of browse and details fragment.
-     */
-    protected void runEntranceTransition(Object entranceTransition) {
-    }
-
-    /**
-     * Callback when entrance transition is prepared.  This is when fragment should
-     * stop user input and animations.
-     */
-    protected void onEntranceTransitionPrepare() {
-    }
-
-    /**
-     * Callback when entrance transition is started.  This is when fragment should
-     * stop processing layout.
-     */
-    protected void onEntranceTransitionStart() {
-    }
-
-    /**
-     * Callback when entrance transition is ended.
-     */
-    protected void onEntranceTransitionEnd() {
-    }
-
-    /**
-     * When fragment finishes loading data, it should call startEntranceTransition()
-     * to execute the entrance transition.
-     * startEntranceTransition() will start transition only if both two conditions
-     * are satisfied:
-     * <li> prepareEntranceTransition() was called.</li>
-     * <li> has not executed entrance transition yet.</li>
-     * <p>
-     * If startEntranceTransition() is called before onViewCreated(), it will be pending
-     * and executed when view is created.
-     */
-    public void startEntranceTransition() {
-        mStateMachine.fireEvent(EVT_START_ENTRANCE);
-    }
-
-    void onExecuteEntranceTransition() {
-        // wait till views get their initial position before start transition
-        final View view = getView();
-        if (view == null) {
-            // fragment view destroyed, transition not needed
-            return;
-        }
-        view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                view.getViewTreeObserver().removeOnPreDrawListener(this);
-                if (FragmentUtil.getContext(BaseFragment.this) == null || getView() == null) {
-                    // bail out if fragment is destroyed immediately after startEntranceTransition
-                    return true;
-                }
-                internalCreateEntranceTransition();
-                onEntranceTransitionStart();
-                if (mEntranceTransition != null) {
-                    runEntranceTransition(mEntranceTransition);
-                } else {
-                    mStateMachine.fireEvent(EVT_ENTRANCE_END);
-                }
-                return false;
-            }
-        });
-        view.invalidate();
-    }
-
-    void internalCreateEntranceTransition() {
-        mEntranceTransition = createEntranceTransition();
-        if (mEntranceTransition == null) {
-            return;
-        }
-        TransitionHelper.addTransitionListener(mEntranceTransition, new TransitionListener() {
-            @Override
-            public void onTransitionEnd(Object transition) {
-                mEntranceTransition = null;
-                mStateMachine.fireEvent(EVT_ENTRANCE_END);
-            }
-        });
-    }
-
-    /**
-     * Returns the {@link ProgressBarManager}.
-     * @return The {@link ProgressBarManager}.
-     */
-    public final ProgressBarManager getProgressBarManager() {
-        return mProgressBarManager;
-    }
-}
diff --git a/android/support/v17/leanback/app/BaseRowFragment.java b/android/support/v17/leanback/app/BaseRowFragment.java
deleted file mode 100644
index 97a5b84..0000000
--- a/android/support/v17/leanback/app/BaseRowFragment.java
+++ /dev/null
@@ -1,308 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from BaseRowSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnChildViewHolderSelectedListener;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.app.Fragment;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * An internal base class for a fragment containing a list of rows.
- * @deprecated use {@link BaseRowSupportFragment}
- */
-@Deprecated
-abstract class BaseRowFragment extends Fragment {
-    private static final String CURRENT_SELECTED_POSITION = "currentSelectedPosition";
-    private ObjectAdapter mAdapter;
-    VerticalGridView mVerticalGridView;
-    private PresenterSelector mPresenterSelector;
-    final ItemBridgeAdapter mBridgeAdapter = new ItemBridgeAdapter();
-    int mSelectedPosition = -1;
-    private boolean mPendingTransitionPrepare;
-    private LateSelectionObserver mLateSelectionObserver = new LateSelectionObserver();
-
-    abstract int getLayoutResourceId();
-
-    private final OnChildViewHolderSelectedListener mRowSelectedListener =
-            new OnChildViewHolderSelectedListener() {
-                @Override
-                public void onChildViewHolderSelected(RecyclerView parent,
-                        RecyclerView.ViewHolder view, int position, int subposition) {
-                    if (!mLateSelectionObserver.mIsLateSelection) {
-                        mSelectedPosition = position;
-                        onRowSelected(parent, view, position, subposition);
-                    }
-                }
-            };
-
-    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder view,
-            int position, int subposition) {
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View view = inflater.inflate(getLayoutResourceId(), container, false);
-        mVerticalGridView = findGridViewFromRoot(view);
-        if (mPendingTransitionPrepare) {
-            mPendingTransitionPrepare = false;
-            onTransitionPrepare();
-        }
-        return view;
-    }
-
-    VerticalGridView findGridViewFromRoot(View view) {
-        return (VerticalGridView) view;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        if (savedInstanceState != null) {
-            mSelectedPosition = savedInstanceState.getInt(CURRENT_SELECTED_POSITION, -1);
-        }
-        setAdapterAndSelection();
-        mVerticalGridView.setOnChildViewHolderSelectedListener(mRowSelectedListener);
-    }
-
-    /**
-     * This class waits for the adapter to be updated before setting the selected
-     * row.
-     */
-    private class LateSelectionObserver extends RecyclerView.AdapterDataObserver {
-        boolean mIsLateSelection = false;
-
-        LateSelectionObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            performLateSelection();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            performLateSelection();
-        }
-
-        void startLateSelection() {
-            mIsLateSelection = true;
-            mBridgeAdapter.registerAdapterDataObserver(this);
-        }
-
-        void performLateSelection() {
-            clear();
-            if (mVerticalGridView != null) {
-                mVerticalGridView.setSelectedPosition(mSelectedPosition);
-            }
-        }
-
-        void clear() {
-            if (mIsLateSelection) {
-                mIsLateSelection = false;
-                mBridgeAdapter.unregisterAdapterDataObserver(this);
-            }
-        }
-    }
-
-    void setAdapterAndSelection() {
-        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) {
-            mLateSelectionObserver.startLateSelection();
-        } else if (mSelectedPosition >= 0) {
-            mVerticalGridView.setSelectedPosition(mSelectedPosition);
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mLateSelectionObserver.clear();
-        mVerticalGridView = null;
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(CURRENT_SELECTED_POSITION, mSelectedPosition);
-    }
-
-    /**
-     * Set the presenter selector used to create and bind views.
-     */
-    public final void setPresenterSelector(PresenterSelector presenterSelector) {
-        if (mPresenterSelector != presenterSelector) {
-            mPresenterSelector = presenterSelector;
-            updateAdapter();
-        }
-    }
-
-    /**
-     * Get the presenter selector used to create and bind views.
-     */
-    public final PresenterSelector getPresenterSelector() {
-        return mPresenterSelector;
-    }
-
-    /**
-     * Sets the adapter that represents a list of rows.
-     * @param rowsAdapter Adapter that represents list of rows.
-     */
-    public final void setAdapter(ObjectAdapter rowsAdapter) {
-        if (mAdapter != rowsAdapter) {
-            mAdapter = rowsAdapter;
-            updateAdapter();
-        }
-    }
-
-    /**
-     * Returns the Adapter that represents list of rows.
-     * @return Adapter that represents list of rows.
-     */
-    public final ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Returns the RecyclerView.Adapter that wraps {@link #getAdapter()}.
-     * @return The RecyclerView.Adapter that wraps {@link #getAdapter()}.
-     */
-    public final ItemBridgeAdapter getBridgeAdapter() {
-        return mBridgeAdapter;
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Gets position of currently selected row.
-     * @return Position of currently selected row.
-     */
-    public int getSelectedPosition() {
-        return mSelectedPosition;
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        if (mSelectedPosition == position) {
-            return;
-        }
-        mSelectedPosition = position;
-        if (mVerticalGridView != null) {
-            if (mLateSelectionObserver.mIsLateSelection) {
-                return;
-            }
-            if (smooth) {
-                mVerticalGridView.setSelectedPositionSmooth(position);
-            } else {
-                mVerticalGridView.setSelectedPosition(position);
-            }
-        }
-    }
-
-    public final VerticalGridView getVerticalGridView() {
-        return mVerticalGridView;
-    }
-
-    void updateAdapter() {
-        mBridgeAdapter.setAdapter(mAdapter);
-        mBridgeAdapter.setPresenter(mPresenterSelector);
-
-        if (mVerticalGridView != null) {
-            setAdapterAndSelection();
-        }
-    }
-
-    Object getItem(Row row, int position) {
-        if (row instanceof ListRow) {
-            return ((ListRow) row).getAdapter().get(position);
-        } else {
-            return null;
-        }
-    }
-
-    public boolean onTransitionPrepare() {
-        if (mVerticalGridView != null) {
-            mVerticalGridView.setAnimateChildLayout(false);
-            mVerticalGridView.setScrollEnabled(false);
-            return true;
-        }
-        mPendingTransitionPrepare = true;
-        return false;
-    }
-
-    public void onTransitionStart() {
-        if (mVerticalGridView != null) {
-            mVerticalGridView.setPruneChild(false);
-            mVerticalGridView.setLayoutFrozen(true);
-            mVerticalGridView.setFocusSearchDisabled(true);
-        }
-    }
-
-    public void onTransitionEnd() {
-        // be careful that fragment might be destroyed before header transition ends.
-        if (mVerticalGridView != null) {
-            mVerticalGridView.setLayoutFrozen(false);
-            mVerticalGridView.setAnimateChildLayout(true);
-            mVerticalGridView.setPruneChild(true);
-            mVerticalGridView.setFocusSearchDisabled(false);
-            mVerticalGridView.setScrollEnabled(true);
-        }
-    }
-
-    public void setAlignment(int windowAlignOffsetTop) {
-        if (mVerticalGridView != null) {
-            // align the top edge of item
-            mVerticalGridView.setItemAlignmentOffset(0);
-            mVerticalGridView.setItemAlignmentOffsetPercent(
-                    VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-
-            // align to a fixed position from top
-            mVerticalGridView.setWindowAlignmentOffset(windowAlignOffsetTop);
-            mVerticalGridView.setWindowAlignmentOffsetPercent(
-                    VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-            mVerticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/BaseRowSupportFragment.java b/android/support/v17/leanback/app/BaseRowSupportFragment.java
deleted file mode 100644
index 6a477ab..0000000
--- a/android/support/v17/leanback/app/BaseRowSupportFragment.java
+++ /dev/null
@@ -1,303 +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.v17.leanback.app;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnChildViewHolderSelectedListener;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v4.app.Fragment;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * An internal base class for a fragment containing a list of rows.
- */
-abstract class BaseRowSupportFragment extends Fragment {
-    private static final String CURRENT_SELECTED_POSITION = "currentSelectedPosition";
-    private ObjectAdapter mAdapter;
-    VerticalGridView mVerticalGridView;
-    private PresenterSelector mPresenterSelector;
-    final ItemBridgeAdapter mBridgeAdapter = new ItemBridgeAdapter();
-    int mSelectedPosition = -1;
-    private boolean mPendingTransitionPrepare;
-    private LateSelectionObserver mLateSelectionObserver = new LateSelectionObserver();
-
-    abstract int getLayoutResourceId();
-
-    private final OnChildViewHolderSelectedListener mRowSelectedListener =
-            new OnChildViewHolderSelectedListener() {
-                @Override
-                public void onChildViewHolderSelected(RecyclerView parent,
-                        RecyclerView.ViewHolder view, int position, int subposition) {
-                    if (!mLateSelectionObserver.mIsLateSelection) {
-                        mSelectedPosition = position;
-                        onRowSelected(parent, view, position, subposition);
-                    }
-                }
-            };
-
-    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder view,
-            int position, int subposition) {
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View view = inflater.inflate(getLayoutResourceId(), container, false);
-        mVerticalGridView = findGridViewFromRoot(view);
-        if (mPendingTransitionPrepare) {
-            mPendingTransitionPrepare = false;
-            onTransitionPrepare();
-        }
-        return view;
-    }
-
-    VerticalGridView findGridViewFromRoot(View view) {
-        return (VerticalGridView) view;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        if (savedInstanceState != null) {
-            mSelectedPosition = savedInstanceState.getInt(CURRENT_SELECTED_POSITION, -1);
-        }
-        setAdapterAndSelection();
-        mVerticalGridView.setOnChildViewHolderSelectedListener(mRowSelectedListener);
-    }
-
-    /**
-     * This class waits for the adapter to be updated before setting the selected
-     * row.
-     */
-    private class LateSelectionObserver extends RecyclerView.AdapterDataObserver {
-        boolean mIsLateSelection = false;
-
-        LateSelectionObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            performLateSelection();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            performLateSelection();
-        }
-
-        void startLateSelection() {
-            mIsLateSelection = true;
-            mBridgeAdapter.registerAdapterDataObserver(this);
-        }
-
-        void performLateSelection() {
-            clear();
-            if (mVerticalGridView != null) {
-                mVerticalGridView.setSelectedPosition(mSelectedPosition);
-            }
-        }
-
-        void clear() {
-            if (mIsLateSelection) {
-                mIsLateSelection = false;
-                mBridgeAdapter.unregisterAdapterDataObserver(this);
-            }
-        }
-    }
-
-    void setAdapterAndSelection() {
-        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) {
-            mLateSelectionObserver.startLateSelection();
-        } else if (mSelectedPosition >= 0) {
-            mVerticalGridView.setSelectedPosition(mSelectedPosition);
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mLateSelectionObserver.clear();
-        mVerticalGridView = null;
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(CURRENT_SELECTED_POSITION, mSelectedPosition);
-    }
-
-    /**
-     * Set the presenter selector used to create and bind views.
-     */
-    public final void setPresenterSelector(PresenterSelector presenterSelector) {
-        if (mPresenterSelector != presenterSelector) {
-            mPresenterSelector = presenterSelector;
-            updateAdapter();
-        }
-    }
-
-    /**
-     * Get the presenter selector used to create and bind views.
-     */
-    public final PresenterSelector getPresenterSelector() {
-        return mPresenterSelector;
-    }
-
-    /**
-     * Sets the adapter that represents a list of rows.
-     * @param rowsAdapter Adapter that represents list of rows.
-     */
-    public final void setAdapter(ObjectAdapter rowsAdapter) {
-        if (mAdapter != rowsAdapter) {
-            mAdapter = rowsAdapter;
-            updateAdapter();
-        }
-    }
-
-    /**
-     * Returns the Adapter that represents list of rows.
-     * @return Adapter that represents list of rows.
-     */
-    public final ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Returns the RecyclerView.Adapter that wraps {@link #getAdapter()}.
-     * @return The RecyclerView.Adapter that wraps {@link #getAdapter()}.
-     */
-    public final ItemBridgeAdapter getBridgeAdapter() {
-        return mBridgeAdapter;
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Gets position of currently selected row.
-     * @return Position of currently selected row.
-     */
-    public int getSelectedPosition() {
-        return mSelectedPosition;
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        if (mSelectedPosition == position) {
-            return;
-        }
-        mSelectedPosition = position;
-        if (mVerticalGridView != null) {
-            if (mLateSelectionObserver.mIsLateSelection) {
-                return;
-            }
-            if (smooth) {
-                mVerticalGridView.setSelectedPositionSmooth(position);
-            } else {
-                mVerticalGridView.setSelectedPosition(position);
-            }
-        }
-    }
-
-    public final VerticalGridView getVerticalGridView() {
-        return mVerticalGridView;
-    }
-
-    void updateAdapter() {
-        mBridgeAdapter.setAdapter(mAdapter);
-        mBridgeAdapter.setPresenter(mPresenterSelector);
-
-        if (mVerticalGridView != null) {
-            setAdapterAndSelection();
-        }
-    }
-
-    Object getItem(Row row, int position) {
-        if (row instanceof ListRow) {
-            return ((ListRow) row).getAdapter().get(position);
-        } else {
-            return null;
-        }
-    }
-
-    public boolean onTransitionPrepare() {
-        if (mVerticalGridView != null) {
-            mVerticalGridView.setAnimateChildLayout(false);
-            mVerticalGridView.setScrollEnabled(false);
-            return true;
-        }
-        mPendingTransitionPrepare = true;
-        return false;
-    }
-
-    public void onTransitionStart() {
-        if (mVerticalGridView != null) {
-            mVerticalGridView.setPruneChild(false);
-            mVerticalGridView.setLayoutFrozen(true);
-            mVerticalGridView.setFocusSearchDisabled(true);
-        }
-    }
-
-    public void onTransitionEnd() {
-        // be careful that fragment might be destroyed before header transition ends.
-        if (mVerticalGridView != null) {
-            mVerticalGridView.setLayoutFrozen(false);
-            mVerticalGridView.setAnimateChildLayout(true);
-            mVerticalGridView.setPruneChild(true);
-            mVerticalGridView.setFocusSearchDisabled(false);
-            mVerticalGridView.setScrollEnabled(true);
-        }
-    }
-
-    public void setAlignment(int windowAlignOffsetTop) {
-        if (mVerticalGridView != null) {
-            // align the top edge of item
-            mVerticalGridView.setItemAlignmentOffset(0);
-            mVerticalGridView.setItemAlignmentOffsetPercent(
-                    VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-
-            // align to a fixed position from top
-            mVerticalGridView.setWindowAlignmentOffset(windowAlignOffsetTop);
-            mVerticalGridView.setWindowAlignmentOffsetPercent(
-                    VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-            mVerticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/BaseSupportFragment.java b/android/support/v17/leanback/app/BaseSupportFragment.java
deleted file mode 100644
index d89cf39..0000000
--- a/android/support/v17/leanback/app/BaseSupportFragment.java
+++ /dev/null
@@ -1,318 +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.v17.leanback.app;
-
-import android.annotation.SuppressLint;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.util.StateMachine;
-import android.support.v17.leanback.util.StateMachine.Condition;
-import android.support.v17.leanback.util.StateMachine.Event;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-/**
- * Base class for leanback Fragments. This class is not intended to be subclassed by apps.
- */
-@SuppressWarnings("FragmentNotInstantiable")
-public class BaseSupportFragment extends BrandedSupportFragment {
-
-    /**
-     * The start state for all
-     */
-    final State STATE_START = new State("START", true, false);
-
-    /**
-     * Initial State for ENTRNACE transition.
-     */
-    final State STATE_ENTRANCE_INIT = new State("ENTRANCE_INIT");
-
-    /**
-     * prepareEntranceTransition is just called, but view not ready yet. We can enable the
-     * busy spinner.
-     */
-    final State STATE_ENTRANCE_ON_PREPARED = new State("ENTRANCE_ON_PREPARED", true, false) {
-        @Override
-        public void run() {
-            mProgressBarManager.show();
-        }
-    };
-
-    /**
-     * prepareEntranceTransition is called and main content view to slide in was created, so we can
-     * call {@link #onEntranceTransitionPrepare}. Note that we dont set initial content to invisible
-     * in this State, the process is very different in subclass, e.g. BrowseSupportFragment hide header
-     * views and hide main fragment view in two steps.
-     */
-    final State STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW = new State(
-            "ENTRANCE_ON_PREPARED_ON_CREATEVIEW") {
-        @Override
-        public void run() {
-            onEntranceTransitionPrepare();
-        }
-    };
-
-    /**
-     * execute the entrance transition.
-     */
-    final State STATE_ENTRANCE_PERFORM = new State("STATE_ENTRANCE_PERFORM") {
-        @Override
-        public void run() {
-            mProgressBarManager.hide();
-            onExecuteEntranceTransition();
-        }
-    };
-
-    /**
-     * execute onEntranceTransitionEnd.
-     */
-    final State STATE_ENTRANCE_ON_ENDED = new State("ENTRANCE_ON_ENDED") {
-        @Override
-        public void run() {
-            onEntranceTransitionEnd();
-        }
-    };
-
-    /**
-     * either entrance transition completed or skipped
-     */
-    final State STATE_ENTRANCE_COMPLETE = new State("ENTRANCE_COMPLETE", true, false);
-
-    /**
-     * Event fragment.onCreate()
-     */
-    final Event EVT_ON_CREATE = new Event("onCreate");
-
-    /**
-     * Event fragment.onViewCreated()
-     */
-    final Event EVT_ON_CREATEVIEW = new Event("onCreateView");
-
-    /**
-     * Event for {@link #prepareEntranceTransition()} is called.
-     */
-    final Event EVT_PREPARE_ENTRANCE = new Event("prepareEntranceTransition");
-
-    /**
-     * Event for {@link #startEntranceTransition()} is called.
-     */
-    final Event EVT_START_ENTRANCE = new Event("startEntranceTransition");
-
-    /**
-     * Event for entrance transition is ended through Transition listener.
-     */
-    final Event EVT_ENTRANCE_END = new Event("onEntranceTransitionEnd");
-
-    /**
-     * Event for skipping entrance transition if not supported.
-     */
-    final Condition COND_TRANSITION_NOT_SUPPORTED = new Condition("EntranceTransitionNotSupport") {
-        @Override
-        public boolean canProceed() {
-            return !TransitionHelper.systemSupportsEntranceTransitions();
-        }
-    };
-
-    final StateMachine mStateMachine = new StateMachine();
-
-    Object mEntranceTransition;
-    final ProgressBarManager mProgressBarManager = new ProgressBarManager();
-
-    @SuppressLint("ValidFragment")
-    BaseSupportFragment() {
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        createStateMachineStates();
-        createStateMachineTransitions();
-        mStateMachine.start();
-        super.onCreate(savedInstanceState);
-        mStateMachine.fireEvent(EVT_ON_CREATE);
-    }
-
-    void createStateMachineStates() {
-        mStateMachine.addState(STATE_START);
-        mStateMachine.addState(STATE_ENTRANCE_INIT);
-        mStateMachine.addState(STATE_ENTRANCE_ON_PREPARED);
-        mStateMachine.addState(STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW);
-        mStateMachine.addState(STATE_ENTRANCE_PERFORM);
-        mStateMachine.addState(STATE_ENTRANCE_ON_ENDED);
-        mStateMachine.addState(STATE_ENTRANCE_COMPLETE);
-    }
-
-    void createStateMachineTransitions() {
-        mStateMachine.addTransition(STATE_START, STATE_ENTRANCE_INIT, EVT_ON_CREATE);
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_ENTRANCE_COMPLETE,
-                COND_TRANSITION_NOT_SUPPORTED);
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_ENTRANCE_COMPLETE,
-                EVT_ON_CREATEVIEW);
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_ENTRANCE_ON_PREPARED,
-                EVT_PREPARE_ENTRANCE);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW,
-                EVT_ON_CREATEVIEW);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_ENTRANCE_PERFORM,
-                EVT_START_ENTRANCE);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW,
-                STATE_ENTRANCE_PERFORM);
-        mStateMachine.addTransition(STATE_ENTRANCE_PERFORM,
-                STATE_ENTRANCE_ON_ENDED,
-                EVT_ENTRANCE_END);
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_ENDED, STATE_ENTRANCE_COMPLETE);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        mStateMachine.fireEvent(EVT_ON_CREATEVIEW);
-    }
-
-    /**
-     * Enables entrance transition.<p>
-     * Entrance transition is the standard slide-in transition that shows rows of data in
-     * browse screen and details screen.
-     * <p>
-     * The method is ignored before LOLLIPOP (API21).
-     * <p>
-     * This method must be called in or
-     * before onCreate().  Typically entrance transition should be enabled when savedInstance is
-     * null so that fragment restored from instanceState does not run an extra entrance transition.
-     * When the entrance transition is enabled, the fragment will make headers and content
-     * hidden initially.
-     * When data of rows are ready, app must call {@link #startEntranceTransition()} to kick off
-     * the transition, otherwise the rows will be invisible forever.
-     * <p>
-     * It is similar to android:windowsEnterTransition and can be considered a late-executed
-     * android:windowsEnterTransition controlled by app.  There are two reasons that app needs it:
-     * <li> Workaround the problem that activity transition is not available between launcher and
-     * app.  Browse activity must programmatically start the slide-in transition.</li>
-     * <li> Separates DetailsOverviewRow transition from other rows transition.  So that
-     * the DetailsOverviewRow transition can be executed earlier without waiting for all rows
-     * to be loaded.</li>
-     * <p>
-     * Transition object is returned by createEntranceTransition().  Typically the app does not need
-     * override the default transition that browse and details provides.
-     */
-    public void prepareEntranceTransition() {
-        mStateMachine.fireEvent(EVT_PREPARE_ENTRANCE);
-    }
-
-    /**
-     * Create entrance transition.  Subclass can override to load transition from
-     * resource or construct manually.  Typically app does not need to
-     * override the default transition that browse and details provides.
-     */
-    protected Object createEntranceTransition() {
-        return null;
-    }
-
-    /**
-     * Run entrance transition.  Subclass may use TransitionManager to perform
-     * go(Scene) or beginDelayedTransition().  App should not override the default
-     * implementation of browse and details fragment.
-     */
-    protected void runEntranceTransition(Object entranceTransition) {
-    }
-
-    /**
-     * Callback when entrance transition is prepared.  This is when fragment should
-     * stop user input and animations.
-     */
-    protected void onEntranceTransitionPrepare() {
-    }
-
-    /**
-     * Callback when entrance transition is started.  This is when fragment should
-     * stop processing layout.
-     */
-    protected void onEntranceTransitionStart() {
-    }
-
-    /**
-     * Callback when entrance transition is ended.
-     */
-    protected void onEntranceTransitionEnd() {
-    }
-
-    /**
-     * When fragment finishes loading data, it should call startEntranceTransition()
-     * to execute the entrance transition.
-     * startEntranceTransition() will start transition only if both two conditions
-     * are satisfied:
-     * <li> prepareEntranceTransition() was called.</li>
-     * <li> has not executed entrance transition yet.</li>
-     * <p>
-     * If startEntranceTransition() is called before onViewCreated(), it will be pending
-     * and executed when view is created.
-     */
-    public void startEntranceTransition() {
-        mStateMachine.fireEvent(EVT_START_ENTRANCE);
-    }
-
-    void onExecuteEntranceTransition() {
-        // wait till views get their initial position before start transition
-        final View view = getView();
-        if (view == null) {
-            // fragment view destroyed, transition not needed
-            return;
-        }
-        view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
-            @Override
-            public boolean onPreDraw() {
-                view.getViewTreeObserver().removeOnPreDrawListener(this);
-                if (getContext() == null || getView() == null) {
-                    // bail out if fragment is destroyed immediately after startEntranceTransition
-                    return true;
-                }
-                internalCreateEntranceTransition();
-                onEntranceTransitionStart();
-                if (mEntranceTransition != null) {
-                    runEntranceTransition(mEntranceTransition);
-                } else {
-                    mStateMachine.fireEvent(EVT_ENTRANCE_END);
-                }
-                return false;
-            }
-        });
-        view.invalidate();
-    }
-
-    void internalCreateEntranceTransition() {
-        mEntranceTransition = createEntranceTransition();
-        if (mEntranceTransition == null) {
-            return;
-        }
-        TransitionHelper.addTransitionListener(mEntranceTransition, new TransitionListener() {
-            @Override
-            public void onTransitionEnd(Object transition) {
-                mEntranceTransition = null;
-                mStateMachine.fireEvent(EVT_ENTRANCE_END);
-            }
-        });
-    }
-
-    /**
-     * Returns the {@link ProgressBarManager}.
-     * @return The {@link ProgressBarManager}.
-     */
-    public final ProgressBarManager getProgressBarManager() {
-        return mProgressBarManager;
-    }
-}
diff --git a/android/support/v17/leanback/app/BrandedFragment.java b/android/support/v17/leanback/app/BrandedFragment.java
deleted file mode 100644
index 415c13e..0000000
--- a/android/support/v17/leanback/app/BrandedFragment.java
+++ /dev/null
@@ -1,340 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from BrandedSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v17.leanback.widget.TitleHelper;
-import android.support.v17.leanback.widget.TitleViewAdapter;
-import android.app.Fragment;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Fragment class for managing search and branding using a view that implements
- * {@link TitleViewAdapter.Provider}.
- * @deprecated use {@link BrandedSupportFragment}
- */
-@Deprecated
-public class BrandedFragment extends Fragment {
-
-    // BUNDLE attribute for title is showing
-    private static final String TITLE_SHOW = "titleShow";
-
-    private boolean mShowingTitle = true;
-    private CharSequence mTitle;
-    private Drawable mBadgeDrawable;
-    private View mTitleView;
-    private TitleViewAdapter mTitleViewAdapter;
-    private SearchOrbView.Colors mSearchAffordanceColors;
-    private boolean mSearchAffordanceColorSet;
-    private View.OnClickListener mExternalOnSearchClickedListener;
-    private TitleHelper mTitleHelper;
-
-    /**
-     * Called by {@link #installTitleView(LayoutInflater, ViewGroup, Bundle)} to inflate
-     * title view.  Default implementation uses layout file lb_browse_title.
-     * Subclass may override and use its own layout, the layout must have a descendant with id
-     * browse_title_group that implements {@link TitleViewAdapter.Provider}. Subclass may return
-     * null if no title is needed.
-     *
-     * @param inflater           The LayoutInflater object that can be used to inflate
-     *                           any views in the fragment,
-     * @param parent             Parent of title view.
-     * @param savedInstanceState If non-null, this fragment is being re-constructed
-     *                           from a previous saved state as given here.
-     * @return Title view which must have a descendant with id browse_title_group that implements
-     *         {@link TitleViewAdapter.Provider}, or null for no title view.
-     */
-    public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
-                                Bundle savedInstanceState) {
-        TypedValue typedValue = new TypedValue();
-        boolean found = parent.getContext().getTheme().resolveAttribute(
-                R.attr.browseTitleViewLayout, typedValue, true);
-        return inflater.inflate(found ? typedValue.resourceId : R.layout.lb_browse_title,
-                parent, false);
-    }
-
-    /**
-     * Inflate title view and add to parent.  This method should be called in
-     * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
-     * @param inflater The LayoutInflater object that can be used to inflate
-     * any views in the fragment,
-     * @param parent Parent of title view.
-     * @param savedInstanceState If non-null, this fragment is being re-constructed
-     * from a previous saved state as given here.
-     */
-    public void installTitleView(LayoutInflater inflater, ViewGroup parent,
-                            Bundle savedInstanceState) {
-        View titleLayoutRoot = onInflateTitleView(inflater, parent, savedInstanceState);
-        if (titleLayoutRoot != null) {
-            parent.addView(titleLayoutRoot);
-            setTitleView(titleLayoutRoot.findViewById(R.id.browse_title_group));
-        } else {
-            setTitleView(null);
-        }
-    }
-
-    /**
-     * Sets the view that implemented {@link TitleViewAdapter}.
-     * @param titleView The view that implemented {@link TitleViewAdapter.Provider}.
-     */
-    public void setTitleView(View titleView) {
-        mTitleView = titleView;
-        if (mTitleView == null) {
-            mTitleViewAdapter = null;
-            mTitleHelper = null;
-        } else {
-            mTitleViewAdapter = ((TitleViewAdapter.Provider) mTitleView).getTitleViewAdapter();
-            mTitleViewAdapter.setTitle(mTitle);
-            mTitleViewAdapter.setBadgeDrawable(mBadgeDrawable);
-            if (mSearchAffordanceColorSet) {
-                mTitleViewAdapter.setSearchAffordanceColors(mSearchAffordanceColors);
-            }
-            if (mExternalOnSearchClickedListener != null) {
-                setOnSearchClickedListener(mExternalOnSearchClickedListener);
-            }
-            if (getView() instanceof ViewGroup) {
-                mTitleHelper = new TitleHelper((ViewGroup) getView(), mTitleView);
-            }
-        }
-    }
-
-    /**
-     * Returns the view that implements {@link TitleViewAdapter.Provider}.
-     * @return The view that implements {@link TitleViewAdapter.Provider}.
-     */
-    public View getTitleView() {
-        return mTitleView;
-    }
-
-    /**
-     * Returns the {@link TitleViewAdapter} implemented by title view.
-     * @return The {@link TitleViewAdapter} implemented by title view.
-     */
-    public TitleViewAdapter getTitleViewAdapter() {
-        return mTitleViewAdapter;
-    }
-
-    /**
-     * Returns the {@link TitleHelper}.
-     */
-    TitleHelper getTitleHelper() {
-        return mTitleHelper;
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putBoolean(TITLE_SHOW, mShowingTitle);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        if (savedInstanceState != null) {
-            mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
-        }
-        if (mTitleView != null && view instanceof ViewGroup) {
-            mTitleHelper = new TitleHelper((ViewGroup) view, mTitleView);
-            mTitleHelper.showTitle(mShowingTitle);
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mTitleHelper = null;
-    }
-
-    /**
-     * Shows or hides the title view.
-     * @param show True to show title view, false to hide title view.
-     */
-    public void showTitle(boolean show) {
-        // TODO: handle interruptions?
-        if (show == mShowingTitle) {
-            return;
-        }
-        mShowingTitle = show;
-        if (mTitleHelper != null) {
-            mTitleHelper.showTitle(show);
-        }
-    }
-
-    /**
-     * Changes title view's components visibility and shows title.
-     * @param flags Flags representing the visibility of components inside title view.
-     * @see TitleViewAdapter#SEARCH_VIEW_VISIBLE
-     * @see TitleViewAdapter#BRANDING_VIEW_VISIBLE
-     * @see TitleViewAdapter#FULL_VIEW_VISIBLE
-     * @see TitleViewAdapter#updateComponentsVisibility(int)
-     */
-    public void showTitle(int flags) {
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.updateComponentsVisibility(flags);
-        }
-        showTitle(true);
-    }
-
-    /**
-     * Sets the drawable displayed in the fragment title.
-     *
-     * @param drawable The Drawable to display in the fragment title.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        if (mBadgeDrawable != drawable) {
-            mBadgeDrawable = drawable;
-            if (mTitleViewAdapter != null) {
-                mTitleViewAdapter.setBadgeDrawable(drawable);
-            }
-        }
-    }
-
-    /**
-     * Returns the badge drawable used in the fragment title.
-     * @return The badge drawable used in the fragment title.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Sets title text for the fragment.
-     *
-     * @param title The title text of the fragment.
-     */
-    public void setTitle(CharSequence title) {
-        mTitle = title;
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setTitle(title);
-        }
-    }
-
-    /**
-     * Returns the title text for the fragment.
-     * @return Title text for the fragment.
-     */
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Sets a click listener for the search affordance.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the fragment title. When set to non-null, the title will
-     * contain an element that a user may click to begin a search.
-     *
-     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
-     * will be invoked when the user clicks on the search element.
-     *
-     * @param listener The listener to call when the search element is clicked.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mExternalOnSearchClickedListener = listener;
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setOnSearchClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link android.support.v17.leanback.widget.SearchOrbView.Colors} used to draw the
-     * search affordance.
-     *
-     * @param colors Colors used to draw search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchAffordanceColors = colors;
-        mSearchAffordanceColorSet = true;
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-    }
-
-    /**
-     * Returns the {@link android.support.v17.leanback.widget.SearchOrbView.Colors}
-     * used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        if (mSearchAffordanceColorSet) {
-            return mSearchAffordanceColors;
-        }
-        if (mTitleViewAdapter == null) {
-            throw new IllegalStateException("Fragment views not yet created");
-        }
-        return mTitleViewAdapter.getSearchAffordanceColors();
-    }
-
-    /**
-     * Sets the color used to draw the search affordance.
-     * A default brighter color will be set by the framework.
-     *
-     * @param color The color to use for the search affordance.
-     */
-    public void setSearchAffordanceColor(int color) {
-        setSearchAffordanceColors(new SearchOrbView.Colors(color));
-    }
-
-    /**
-     * Returns the color used to draw the search affordance.
-     */
-    public int getSearchAffordanceColor() {
-        return getSearchAffordanceColors().color;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mTitleViewAdapter != null) {
-            showTitle(mShowingTitle);
-            mTitleViewAdapter.setAnimationEnabled(true);
-        }
-    }
-
-    @Override
-    public void onPause() {
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setAnimationEnabled(false);
-        }
-        super.onPause();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setAnimationEnabled(true);
-        }
-    }
-
-    /**
-     * Returns true/false to indicate the visibility of TitleView.
-     *
-     * @return boolean to indicate whether or not it's showing the title.
-     */
-    public final boolean isShowingTitle() {
-        return mShowingTitle;
-    }
-
-}
diff --git a/android/support/v17/leanback/app/BrandedSupportFragment.java b/android/support/v17/leanback/app/BrandedSupportFragment.java
deleted file mode 100644
index 306e1f1..0000000
--- a/android/support/v17/leanback/app/BrandedSupportFragment.java
+++ /dev/null
@@ -1,335 +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.v17.leanback.app;
-
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v17.leanback.widget.TitleHelper;
-import android.support.v17.leanback.widget.TitleViewAdapter;
-import android.support.v4.app.Fragment;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Fragment class for managing search and branding using a view that implements
- * {@link TitleViewAdapter.Provider}.
- */
-public class BrandedSupportFragment extends Fragment {
-
-    // BUNDLE attribute for title is showing
-    private static final String TITLE_SHOW = "titleShow";
-
-    private boolean mShowingTitle = true;
-    private CharSequence mTitle;
-    private Drawable mBadgeDrawable;
-    private View mTitleView;
-    private TitleViewAdapter mTitleViewAdapter;
-    private SearchOrbView.Colors mSearchAffordanceColors;
-    private boolean mSearchAffordanceColorSet;
-    private View.OnClickListener mExternalOnSearchClickedListener;
-    private TitleHelper mTitleHelper;
-
-    /**
-     * Called by {@link #installTitleView(LayoutInflater, ViewGroup, Bundle)} to inflate
-     * title view.  Default implementation uses layout file lb_browse_title.
-     * Subclass may override and use its own layout, the layout must have a descendant with id
-     * browse_title_group that implements {@link TitleViewAdapter.Provider}. Subclass may return
-     * null if no title is needed.
-     *
-     * @param inflater           The LayoutInflater object that can be used to inflate
-     *                           any views in the fragment,
-     * @param parent             Parent of title view.
-     * @param savedInstanceState If non-null, this fragment is being re-constructed
-     *                           from a previous saved state as given here.
-     * @return Title view which must have a descendant with id browse_title_group that implements
-     *         {@link TitleViewAdapter.Provider}, or null for no title view.
-     */
-    public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
-                                Bundle savedInstanceState) {
-        TypedValue typedValue = new TypedValue();
-        boolean found = parent.getContext().getTheme().resolveAttribute(
-                R.attr.browseTitleViewLayout, typedValue, true);
-        return inflater.inflate(found ? typedValue.resourceId : R.layout.lb_browse_title,
-                parent, false);
-    }
-
-    /**
-     * Inflate title view and add to parent.  This method should be called in
-     * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
-     * @param inflater The LayoutInflater object that can be used to inflate
-     * any views in the fragment,
-     * @param parent Parent of title view.
-     * @param savedInstanceState If non-null, this fragment is being re-constructed
-     * from a previous saved state as given here.
-     */
-    public void installTitleView(LayoutInflater inflater, ViewGroup parent,
-                            Bundle savedInstanceState) {
-        View titleLayoutRoot = onInflateTitleView(inflater, parent, savedInstanceState);
-        if (titleLayoutRoot != null) {
-            parent.addView(titleLayoutRoot);
-            setTitleView(titleLayoutRoot.findViewById(R.id.browse_title_group));
-        } else {
-            setTitleView(null);
-        }
-    }
-
-    /**
-     * Sets the view that implemented {@link TitleViewAdapter}.
-     * @param titleView The view that implemented {@link TitleViewAdapter.Provider}.
-     */
-    public void setTitleView(View titleView) {
-        mTitleView = titleView;
-        if (mTitleView == null) {
-            mTitleViewAdapter = null;
-            mTitleHelper = null;
-        } else {
-            mTitleViewAdapter = ((TitleViewAdapter.Provider) mTitleView).getTitleViewAdapter();
-            mTitleViewAdapter.setTitle(mTitle);
-            mTitleViewAdapter.setBadgeDrawable(mBadgeDrawable);
-            if (mSearchAffordanceColorSet) {
-                mTitleViewAdapter.setSearchAffordanceColors(mSearchAffordanceColors);
-            }
-            if (mExternalOnSearchClickedListener != null) {
-                setOnSearchClickedListener(mExternalOnSearchClickedListener);
-            }
-            if (getView() instanceof ViewGroup) {
-                mTitleHelper = new TitleHelper((ViewGroup) getView(), mTitleView);
-            }
-        }
-    }
-
-    /**
-     * Returns the view that implements {@link TitleViewAdapter.Provider}.
-     * @return The view that implements {@link TitleViewAdapter.Provider}.
-     */
-    public View getTitleView() {
-        return mTitleView;
-    }
-
-    /**
-     * Returns the {@link TitleViewAdapter} implemented by title view.
-     * @return The {@link TitleViewAdapter} implemented by title view.
-     */
-    public TitleViewAdapter getTitleViewAdapter() {
-        return mTitleViewAdapter;
-    }
-
-    /**
-     * Returns the {@link TitleHelper}.
-     */
-    TitleHelper getTitleHelper() {
-        return mTitleHelper;
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putBoolean(TITLE_SHOW, mShowingTitle);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        if (savedInstanceState != null) {
-            mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
-        }
-        if (mTitleView != null && view instanceof ViewGroup) {
-            mTitleHelper = new TitleHelper((ViewGroup) view, mTitleView);
-            mTitleHelper.showTitle(mShowingTitle);
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mTitleHelper = null;
-    }
-
-    /**
-     * Shows or hides the title view.
-     * @param show True to show title view, false to hide title view.
-     */
-    public void showTitle(boolean show) {
-        // TODO: handle interruptions?
-        if (show == mShowingTitle) {
-            return;
-        }
-        mShowingTitle = show;
-        if (mTitleHelper != null) {
-            mTitleHelper.showTitle(show);
-        }
-    }
-
-    /**
-     * Changes title view's components visibility and shows title.
-     * @param flags Flags representing the visibility of components inside title view.
-     * @see TitleViewAdapter#SEARCH_VIEW_VISIBLE
-     * @see TitleViewAdapter#BRANDING_VIEW_VISIBLE
-     * @see TitleViewAdapter#FULL_VIEW_VISIBLE
-     * @see TitleViewAdapter#updateComponentsVisibility(int)
-     */
-    public void showTitle(int flags) {
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.updateComponentsVisibility(flags);
-        }
-        showTitle(true);
-    }
-
-    /**
-     * Sets the drawable displayed in the fragment title.
-     *
-     * @param drawable The Drawable to display in the fragment title.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        if (mBadgeDrawable != drawable) {
-            mBadgeDrawable = drawable;
-            if (mTitleViewAdapter != null) {
-                mTitleViewAdapter.setBadgeDrawable(drawable);
-            }
-        }
-    }
-
-    /**
-     * Returns the badge drawable used in the fragment title.
-     * @return The badge drawable used in the fragment title.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Sets title text for the fragment.
-     *
-     * @param title The title text of the fragment.
-     */
-    public void setTitle(CharSequence title) {
-        mTitle = title;
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setTitle(title);
-        }
-    }
-
-    /**
-     * Returns the title text for the fragment.
-     * @return Title text for the fragment.
-     */
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Sets a click listener for the search affordance.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the fragment title. When set to non-null, the title will
-     * contain an element that a user may click to begin a search.
-     *
-     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
-     * will be invoked when the user clicks on the search element.
-     *
-     * @param listener The listener to call when the search element is clicked.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mExternalOnSearchClickedListener = listener;
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setOnSearchClickedListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link android.support.v17.leanback.widget.SearchOrbView.Colors} used to draw the
-     * search affordance.
-     *
-     * @param colors Colors used to draw search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchAffordanceColors = colors;
-        mSearchAffordanceColorSet = true;
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setSearchAffordanceColors(mSearchAffordanceColors);
-        }
-    }
-
-    /**
-     * Returns the {@link android.support.v17.leanback.widget.SearchOrbView.Colors}
-     * used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        if (mSearchAffordanceColorSet) {
-            return mSearchAffordanceColors;
-        }
-        if (mTitleViewAdapter == null) {
-            throw new IllegalStateException("Fragment views not yet created");
-        }
-        return mTitleViewAdapter.getSearchAffordanceColors();
-    }
-
-    /**
-     * Sets the color used to draw the search affordance.
-     * A default brighter color will be set by the framework.
-     *
-     * @param color The color to use for the search affordance.
-     */
-    public void setSearchAffordanceColor(int color) {
-        setSearchAffordanceColors(new SearchOrbView.Colors(color));
-    }
-
-    /**
-     * Returns the color used to draw the search affordance.
-     */
-    public int getSearchAffordanceColor() {
-        return getSearchAffordanceColors().color;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (mTitleViewAdapter != null) {
-            showTitle(mShowingTitle);
-            mTitleViewAdapter.setAnimationEnabled(true);
-        }
-    }
-
-    @Override
-    public void onPause() {
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setAnimationEnabled(false);
-        }
-        super.onPause();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mTitleViewAdapter != null) {
-            mTitleViewAdapter.setAnimationEnabled(true);
-        }
-    }
-
-    /**
-     * Returns true/false to indicate the visibility of TitleView.
-     *
-     * @return boolean to indicate whether or not it's showing the title.
-     */
-    public final boolean isShowingTitle() {
-        return mShowingTitle;
-    }
-
-}
diff --git a/android/support/v17/leanback/app/BrowseFragment.java b/android/support/v17/leanback/app/BrowseFragment.java
deleted file mode 100644
index a2439e4..0000000
--- a/android/support/v17/leanback/app/BrowseFragment.java
+++ /dev/null
@@ -1,1871 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from BrowseSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.util.StateMachine.Event;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.support.v17.leanback.widget.BrowseFrameLayout;
-import android.support.v17.leanback.widget.InvisibleRowPresenter;
-import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.PageRow;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowHeaderPresenter;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.ScaleFrameLayout;
-import android.support.v17.leanback.widget.TitleViewAdapter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentManager.BackStackEntry;
-import android.app.FragmentTransaction;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.view.ViewTreeObserver;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A fragment for creating Leanback browse screens. It is composed of a
- * RowsFragment and a HeadersFragment.
- * <p>
- * A BrowseFragment renders the elements of its {@link ObjectAdapter} as a set
- * of rows in a vertical list. The elements in this adapter must be subclasses
- * of {@link Row}.
- * <p>
- * The HeadersFragment can be set to be either shown or hidden by default, or
- * may be disabled entirely. See {@link #setHeadersState} for details.
- * <p>
- * By default the BrowseFragment includes support for returning to the headers
- * when the user presses Back. For Activities that customize {@link
- * android.app.Activity#onBackPressed()}, you must disable this default Back key support by
- * calling {@link #setHeadersTransitionOnBackEnabled(boolean)} with false and
- * use {@link BrowseFragment.BrowseTransitionListener} and
- * {@link #startHeadersTransition(boolean)}.
- * <p>
- * The recommended theme to use with a BrowseFragment is
- * {@link android.support.v17.leanback.R.style#Theme_Leanback_Browse}.
- * </p>
- * @deprecated use {@link BrowseSupportFragment}
- */
-@Deprecated
-public class BrowseFragment extends BaseFragment {
-
-    // BUNDLE attribute for saving header show/hide status when backstack is used:
-    static final String HEADER_STACK_INDEX = "headerStackIndex";
-    // BUNDLE attribute for saving header show/hide status when backstack is not used:
-    static final String HEADER_SHOW = "headerShow";
-    private static final String IS_PAGE_ROW = "isPageRow";
-    private static final String CURRENT_SELECTED_POSITION = "currentSelectedPosition";
-
-    /**
-     * State to hide headers fragment.
-     */
-    final State STATE_SET_ENTRANCE_START_STATE = new State("SET_ENTRANCE_START_STATE") {
-        @Override
-        public void run() {
-            setEntranceTransitionStartState();
-        }
-    };
-
-    /**
-     * Event for Header fragment view is created, we could perform
-     * {@link #setEntranceTransitionStartState()} to hide headers fragment initially.
-     */
-    final Event EVT_HEADER_VIEW_CREATED = new Event("headerFragmentViewCreated");
-
-    /**
-     * Event for {@link #getMainFragment()} view is created, it's additional requirement to execute
-     * {@link #onEntranceTransitionPrepare()}.
-     */
-    final Event EVT_MAIN_FRAGMENT_VIEW_CREATED = new Event("mainFragmentViewCreated");
-
-    /**
-     * Event that data for the screen is ready, this is additional requirement to launch entrance
-     * transition.
-     */
-    final Event EVT_SCREEN_DATA_READY = new Event("screenDataReady");
-
-    @Override
-    void createStateMachineStates() {
-        super.createStateMachineStates();
-        mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
-    }
-
-    @Override
-    void createStateMachineTransitions() {
-        super.createStateMachineTransitions();
-        // when headers fragment view is created we could setEntranceTransitionStartState()
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED, STATE_SET_ENTRANCE_START_STATE,
-                EVT_HEADER_VIEW_CREATED);
-
-        // add additional requirement for onEntranceTransitionPrepare()
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW,
-                EVT_MAIN_FRAGMENT_VIEW_CREATED);
-        // add additional requirement to launch entrance transition.
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,  STATE_ENTRANCE_PERFORM,
-                EVT_SCREEN_DATA_READY);
-    }
-
-    final class BackStackListener implements FragmentManager.OnBackStackChangedListener {
-        int mLastEntryCount;
-        int mIndexOfHeadersBackStack;
-
-        BackStackListener() {
-            mLastEntryCount = getFragmentManager().getBackStackEntryCount();
-            mIndexOfHeadersBackStack = -1;
-        }
-
-        void load(Bundle savedInstanceState) {
-            if (savedInstanceState != null) {
-                mIndexOfHeadersBackStack = savedInstanceState.getInt(HEADER_STACK_INDEX, -1);
-                mShowingHeaders = mIndexOfHeadersBackStack == -1;
-            } else {
-                if (!mShowingHeaders) {
-                    getFragmentManager().beginTransaction()
-                            .addToBackStack(mWithHeadersBackStackName).commit();
-                }
-            }
-        }
-
-        void save(Bundle outState) {
-            outState.putInt(HEADER_STACK_INDEX, mIndexOfHeadersBackStack);
-        }
-
-
-        @Override
-        public void onBackStackChanged() {
-            if (getFragmentManager() == null) {
-                Log.w(TAG, "getFragmentManager() is null, stack:", new Exception());
-                return;
-            }
-            int count = getFragmentManager().getBackStackEntryCount();
-            // if backstack is growing and last pushed entry is "headers" backstack,
-            // remember the index of the entry.
-            if (count > mLastEntryCount) {
-                BackStackEntry entry = getFragmentManager().getBackStackEntryAt(count - 1);
-                if (mWithHeadersBackStackName.equals(entry.getName())) {
-                    mIndexOfHeadersBackStack = count - 1;
-                }
-            } else if (count < mLastEntryCount) {
-                // if popped "headers" backstack, initiate the show header transition if needed
-                if (mIndexOfHeadersBackStack >= count) {
-                    if (!isHeadersDataReady()) {
-                        // if main fragment was restored first before BrowseFragment's adapter gets
-                        // restored: don't start header transition, but add the entry back.
-                        getFragmentManager().beginTransaction()
-                                .addToBackStack(mWithHeadersBackStackName).commit();
-                        return;
-                    }
-                    mIndexOfHeadersBackStack = -1;
-                    if (!mShowingHeaders) {
-                        startHeadersTransitionInternal(true);
-                    }
-                }
-            }
-            mLastEntryCount = count;
-        }
-    }
-
-    /**
-     * Listener for transitions between browse headers and rows.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public static class BrowseTransitionListener {
-        /**
-         * Callback when headers transition starts.
-         *
-         * @param withHeaders True if the transition will result in headers
-         *        being shown, false otherwise.
-         */
-        public void onHeadersTransitionStart(boolean withHeaders) {
-        }
-        /**
-         * Callback when headers transition stops.
-         *
-         * @param withHeaders True if the transition will result in headers
-         *        being shown, false otherwise.
-         */
-        public void onHeadersTransitionStop(boolean withHeaders) {
-        }
-    }
-
-    private class SetSelectionRunnable implements Runnable {
-        static final int TYPE_INVALID = -1;
-        static final int TYPE_INTERNAL_SYNC = 0;
-        static final int TYPE_USER_REQUEST = 1;
-
-        private int mPosition;
-        private int mType;
-        private boolean mSmooth;
-
-        SetSelectionRunnable() {
-            reset();
-        }
-
-        void post(int position, int type, boolean smooth) {
-            // Posting the set selection, rather than calling it immediately, prevents an issue
-            // with adapter changes.  Example: a row is added before the current selected row;
-            // first the fast lane view updates its selection, then the rows fragment has that
-            // new selection propagated immediately; THEN the rows view processes the same adapter
-            // change and moves the selection again.
-            if (type >= mType) {
-                mPosition = position;
-                mType = type;
-                mSmooth = smooth;
-                mBrowseFrame.removeCallbacks(this);
-                mBrowseFrame.post(this);
-            }
-        }
-
-        @Override
-        public void run() {
-            setSelection(mPosition, mSmooth);
-            reset();
-        }
-
-        private void reset() {
-            mPosition = -1;
-            mType = TYPE_INVALID;
-            mSmooth = false;
-        }
-    }
-
-    /**
-     * Possible set of actions that {@link BrowseFragment} exposes to clients. Custom
-     * fragments can interact with {@link BrowseFragment} using this interface.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public interface FragmentHost {
-        /**
-         * Fragments are required to invoke this callback once their view is created
-         * inside {@link Fragment#onViewCreated} method. {@link BrowseFragment} starts the entrance
-         * animation only after receiving this callback. Failure to invoke this method
-         * will lead to fragment not showing up.
-         *
-         * @param fragmentAdapter {@link MainFragmentAdapter} used by the current fragment.
-         */
-        void notifyViewCreated(MainFragmentAdapter fragmentAdapter);
-
-        /**
-         * Fragments mapped to {@link PageRow} are required to invoke this callback once their data
-         * is created for transition, the entrance animation only after receiving this callback.
-         * Failure to invoke this method will lead to fragment not showing up.
-         *
-         * @param fragmentAdapter {@link MainFragmentAdapter} used by the current fragment.
-         */
-        void notifyDataReady(MainFragmentAdapter fragmentAdapter);
-
-        /**
-         * Show or hide title view in {@link BrowseFragment} for fragments mapped to
-         * {@link PageRow}.  Otherwise the request is ignored, in that case BrowseFragment is fully
-         * in control of showing/hiding title view.
-         * <p>
-         * When HeadersFragment is visible, BrowseFragment will hide search affordance view if
-         * there are other focusable rows above currently focused row.
-         *
-         * @param show Boolean indicating whether or not to show the title view.
-         */
-        void showTitleView(boolean show);
-    }
-
-    /**
-     * Default implementation of {@link FragmentHost} that is used only by
-     * {@link BrowseFragment}.
-     */
-    private final class FragmentHostImpl implements FragmentHost {
-        boolean mShowTitleView = true;
-
-        FragmentHostImpl() {
-        }
-
-        @Override
-        public void notifyViewCreated(MainFragmentAdapter fragmentAdapter) {
-            mStateMachine.fireEvent(EVT_MAIN_FRAGMENT_VIEW_CREATED);
-            if (!mIsPageRow) {
-                // If it's not a PageRow: it's a ListRow, so we already have data ready.
-                mStateMachine.fireEvent(EVT_SCREEN_DATA_READY);
-            }
-        }
-
-        @Override
-        public void notifyDataReady(MainFragmentAdapter fragmentAdapter) {
-            // If fragment host is not the currently active fragment (in BrowseFragment), then
-            // ignore the request.
-            if (mMainFragmentAdapter == null || mMainFragmentAdapter.getFragmentHost() != this) {
-                return;
-            }
-
-            // We only honor showTitle request for PageRows.
-            if (!mIsPageRow) {
-                return;
-            }
-
-            mStateMachine.fireEvent(EVT_SCREEN_DATA_READY);
-        }
-
-        @Override
-        public void showTitleView(boolean show) {
-            mShowTitleView = show;
-
-            // If fragment host is not the currently active fragment (in BrowseFragment), then
-            // ignore the request.
-            if (mMainFragmentAdapter == null || mMainFragmentAdapter.getFragmentHost() != this) {
-                return;
-            }
-
-            // We only honor showTitle request for PageRows.
-            if (!mIsPageRow) {
-                return;
-            }
-
-            updateTitleViewVisibility();
-        }
-    }
-
-    /**
-     * Interface that defines the interaction between {@link BrowseFragment} and its main
-     * content fragment. The key method is {@link MainFragmentAdapter#getFragment()},
-     * it will be used to get the fragment to be shown in the content section. Clients can
-     * provide any implementation of fragment and customize its interaction with
-     * {@link BrowseFragment} by overriding the necessary methods.
-     *
-     * <p>
-     * Clients are expected to provide
-     * an instance of {@link MainFragmentAdapterRegistry} which will be responsible for providing
-     * implementations of {@link MainFragmentAdapter} for given content types. Currently
-     * we support different types of content - {@link ListRow}, {@link PageRow} or any subtype
-     * of {@link Row}. We provide an out of the box adapter implementation for any rows other than
-     * {@link PageRow} - {@link android.support.v17.leanback.app.RowsFragment.MainFragmentAdapter}.
-     *
-     * <p>
-     * {@link PageRow} is intended to give full flexibility to developers in terms of Fragment
-     * design. Users will have to provide an implementation of {@link MainFragmentAdapter}
-     * and provide that through {@link MainFragmentAdapterRegistry}.
-     * {@link MainFragmentAdapter} implementation can supply any fragment and override
-     * just those interactions that makes sense.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public static class MainFragmentAdapter<T extends Fragment> {
-        private boolean mScalingEnabled;
-        private final T mFragment;
-        FragmentHostImpl mFragmentHost;
-
-        public MainFragmentAdapter(T fragment) {
-            this.mFragment = fragment;
-        }
-
-        public final T getFragment() {
-            return mFragment;
-        }
-
-        /**
-         * Returns whether its scrolling.
-         */
-        public boolean isScrolling() {
-            return false;
-        }
-
-        /**
-         * Set the visibility of titles/hover card of browse rows.
-         */
-        public void setExpand(boolean expand) {
-        }
-
-        /**
-         * For rows that willing to participate entrance transition,  this function
-         * hide views if afterTransition is true,  show views if afterTransition is false.
-         */
-        public void setEntranceTransitionState(boolean state) {
-        }
-
-        /**
-         * Sets the window alignment and also the pivots for scale operation.
-         */
-        public void setAlignment(int windowAlignOffsetFromTop) {
-        }
-
-        /**
-         * Callback indicating transition prepare start.
-         */
-        public boolean onTransitionPrepare() {
-            return false;
-        }
-
-        /**
-         * Callback indicating transition start.
-         */
-        public void onTransitionStart() {
-        }
-
-        /**
-         * Callback indicating transition end.
-         */
-        public void onTransitionEnd() {
-        }
-
-        /**
-         * Returns whether row scaling is enabled.
-         */
-        public boolean isScalingEnabled() {
-            return mScalingEnabled;
-        }
-
-        /**
-         * Sets the row scaling property.
-         */
-        public void setScalingEnabled(boolean scalingEnabled) {
-            this.mScalingEnabled = scalingEnabled;
-        }
-
-        /**
-         * Returns the current host interface so that main fragment can interact with
-         * {@link BrowseFragment}.
-         */
-        public final FragmentHost getFragmentHost() {
-            return mFragmentHost;
-        }
-
-        void setFragmentHost(FragmentHostImpl fragmentHost) {
-            this.mFragmentHost = fragmentHost;
-        }
-    }
-
-    /**
-     * Interface to be implemented by all fragments for providing an instance of
-     * {@link MainFragmentAdapter}. Both {@link RowsFragment} and custom fragment provided
-     * against {@link PageRow} will need to implement this interface.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public interface MainFragmentAdapterProvider {
-        /**
-         * Returns an instance of {@link MainFragmentAdapter} that {@link BrowseFragment}
-         * would use to communicate with the target fragment.
-         */
-        MainFragmentAdapter getMainFragmentAdapter();
-    }
-
-    /**
-     * Interface to be implemented by {@link RowsFragment} and its subclasses for providing
-     * an instance of {@link MainFragmentRowsAdapter}.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public interface MainFragmentRowsAdapterProvider {
-        /**
-         * Returns an instance of {@link MainFragmentRowsAdapter} that {@link BrowseFragment}
-         * would use to communicate with the target fragment.
-         */
-        MainFragmentRowsAdapter getMainFragmentRowsAdapter();
-    }
-
-    /**
-     * This is used to pass information to {@link RowsFragment} or its subclasses.
-     * {@link BrowseFragment} uses this interface to pass row based interaction events to
-     * the target fragment.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public static class MainFragmentRowsAdapter<T extends Fragment> {
-        private final T mFragment;
-
-        public MainFragmentRowsAdapter(T fragment) {
-            if (fragment == null) {
-                throw new IllegalArgumentException("Fragment can't be null");
-            }
-            this.mFragment = fragment;
-        }
-
-        public final T getFragment() {
-            return mFragment;
-        }
-        /**
-         * Set the visibility titles/hover of browse rows.
-         */
-        public void setAdapter(ObjectAdapter adapter) {
-        }
-
-        /**
-         * Sets an item clicked listener on the fragment.
-         */
-        public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        }
-
-        /**
-         * Sets an item selection listener.
-         */
-        public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        }
-
-        /**
-         * Selects a Row and perform an optional task on the Row.
-         */
-        public void setSelectedPosition(int rowPosition,
-                                        boolean smooth,
-                                        final Presenter.ViewHolderTask rowHolderTask) {
-        }
-
-        /**
-         * Selects a Row.
-         */
-        public void setSelectedPosition(int rowPosition, boolean smooth) {
-        }
-
-        /**
-         * @return The position of selected row.
-         */
-        public int getSelectedPosition() {
-            return 0;
-        }
-
-        /**
-         * @param position Position of Row.
-         * @return Row ViewHolder.
-         */
-        public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
-            return null;
-        }
-    }
-
-    private boolean createMainFragment(ObjectAdapter adapter, int position) {
-        Object item = null;
-        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) {
-                position = 0;
-            } else if (position >= adapter.size()) {
-                throw new IllegalArgumentException(
-                        String.format("Invalid position %d requested", position));
-            }
-            item = adapter.get(position);
-        }
-
-        boolean oldIsPageRow = mIsPageRow;
-        Object oldPageRow = mPageRow;
-        mIsPageRow = mCanShowHeaders && item instanceof PageRow;
-        mPageRow = mIsPageRow ? item : null;
-        boolean swap;
-
-        if (mMainFragment == null) {
-            swap = true;
-        } else {
-            if (oldIsPageRow) {
-                if (mIsPageRow) {
-                    if (oldPageRow == null) {
-                        // fragment is restored, page row object not yet set, so just set the
-                        // mPageRow object and there is no need to replace the fragment
-                        swap = false;
-                    } else {
-                        // swap if page row object changes
-                        swap = oldPageRow != mPageRow;
-                    }
-                } else {
-                    swap = true;
-                }
-            } else {
-                swap = mIsPageRow;
-            }
-        }
-
-        if (swap) {
-            mMainFragment = mMainFragmentAdapterRegistry.createFragment(item);
-            if (!(mMainFragment instanceof MainFragmentAdapterProvider)) {
-                throw new IllegalArgumentException(
-                        "Fragment must implement MainFragmentAdapterProvider");
-            }
-
-            setMainFragmentAdapter();
-        }
-
-        return swap;
-    }
-
-    void setMainFragmentAdapter() {
-        mMainFragmentAdapter = ((MainFragmentAdapterProvider) mMainFragment)
-                .getMainFragmentAdapter();
-        mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
-        if (!mIsPageRow) {
-            if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
-                setMainFragmentRowsAdapter(((MainFragmentRowsAdapterProvider) mMainFragment)
-                        .getMainFragmentRowsAdapter());
-            } else {
-                setMainFragmentRowsAdapter(null);
-            }
-            mIsPageRow = mMainFragmentRowsAdapter == null;
-        } else {
-            setMainFragmentRowsAdapter(null);
-        }
-    }
-
-    /**
-     * Factory class responsible for creating fragment given the current item. {@link ListRow}
-     * should return {@link RowsFragment} or its subclass whereas {@link PageRow}
-     * can return any fragment class.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public abstract static class FragmentFactory<T extends Fragment> {
-        public abstract T createFragment(Object row);
-    }
-
-    /**
-     * FragmentFactory implementation for {@link ListRow}.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public static class ListRowFragmentFactory extends FragmentFactory<RowsFragment> {
-        @Override
-        public RowsFragment createFragment(Object row) {
-            return new RowsFragment();
-        }
-    }
-
-    /**
-     * Registry class maintaining the mapping of {@link Row} subclasses to {@link FragmentFactory}.
-     * BrowseRowFragment automatically registers {@link ListRowFragmentFactory} for
-     * handling {@link ListRow}. Developers can override that and also if they want to
-     * use custom fragment, they can register a custom {@link FragmentFactory}
-     * against {@link PageRow}.
-     * @deprecated use {@link BrowseSupportFragment}
-     */
-    @Deprecated
-    public final static class MainFragmentAdapterRegistry {
-        private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap<>();
-        private final static FragmentFactory sDefaultFragmentFactory = new ListRowFragmentFactory();
-
-        public MainFragmentAdapterRegistry() {
-            registerFragment(ListRow.class, sDefaultFragmentFactory);
-        }
-
-        public void registerFragment(Class rowClass, FragmentFactory factory) {
-            mItemToFragmentFactoryMapping.put(rowClass, factory);
-        }
-
-        public Fragment createFragment(Object item) {
-            FragmentFactory fragmentFactory = item == null ? sDefaultFragmentFactory :
-                    mItemToFragmentFactoryMapping.get(item.getClass());
-            if (fragmentFactory == null && !(item instanceof PageRow)) {
-                fragmentFactory = sDefaultFragmentFactory;
-            }
-
-            return fragmentFactory.createFragment(item);
-        }
-    }
-
-    static final String TAG = "BrowseFragment";
-
-    private static final String LB_HEADERS_BACKSTACK = "lbHeadersBackStack_";
-
-    static boolean DEBUG = false;
-
-    /** The headers fragment is enabled and shown by default. */
-    public static final int HEADERS_ENABLED = 1;
-
-    /** The headers fragment is enabled and hidden by default. */
-    public static final int HEADERS_HIDDEN = 2;
-
-    /** The headers fragment is disabled and will never be shown. */
-    public static final int HEADERS_DISABLED = 3;
-
-    private MainFragmentAdapterRegistry mMainFragmentAdapterRegistry =
-            new MainFragmentAdapterRegistry();
-    MainFragmentAdapter mMainFragmentAdapter;
-    Fragment mMainFragment;
-    HeadersFragment mHeadersFragment;
-    MainFragmentRowsAdapter mMainFragmentRowsAdapter;
-    ListRowDataAdapter mMainFragmentListRowDataAdapter;
-
-    private ObjectAdapter mAdapter;
-    private PresenterSelector mAdapterPresenter;
-
-    private int mHeadersState = HEADERS_ENABLED;
-    private int mBrandColor = Color.TRANSPARENT;
-    private boolean mBrandColorSet;
-
-    BrowseFrameLayout mBrowseFrame;
-    private ScaleFrameLayout mScaleFrameLayout;
-    boolean mHeadersBackStackEnabled = true;
-    String mWithHeadersBackStackName;
-    boolean mShowingHeaders = true;
-    boolean mCanShowHeaders = true;
-    private int mContainerListMarginStart;
-    private int mContainerListAlignTop;
-    private boolean mMainFragmentScaleEnabled = true;
-    OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    private int mSelectedPosition = -1;
-    private float mScaleFactor;
-    boolean mIsPageRow;
-    Object mPageRow;
-
-    private PresenterSelector mHeaderPresenterSelector;
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
-    // transition related:
-    Object mSceneWithHeaders;
-    Object mSceneWithoutHeaders;
-    private Object mSceneAfterEntranceTransition;
-    Object mHeadersTransition;
-    BackStackListener mBackStackChangedListener;
-    BrowseTransitionListener mBrowseTransitionListener;
-
-    private static final String ARG_TITLE = BrowseFragment.class.getCanonicalName() + ".title";
-    private static final String ARG_HEADERS_STATE =
-        BrowseFragment.class.getCanonicalName() + ".headersState";
-
-    /**
-     * Creates arguments for a browse fragment.
-     *
-     * @param args The Bundle to place arguments into, or null if the method
-     *        should return a new Bundle.
-     * @param title The title of the BrowseFragment.
-     * @param headersState The initial state of the headers of the
-     *        BrowseFragment. Must be one of {@link #HEADERS_ENABLED}, {@link
-     *        #HEADERS_HIDDEN}, or {@link #HEADERS_DISABLED}.
-     * @return A Bundle with the given arguments for creating a BrowseFragment.
-     */
-    public static Bundle createArgs(Bundle args, String title, int headersState) {
-        if (args == null) {
-            args = new Bundle();
-        }
-        args.putString(ARG_TITLE, title);
-        args.putInt(ARG_HEADERS_STATE, headersState);
-        return args;
-    }
-
-    /**
-     * Sets the brand color for the browse fragment. The brand color is used as
-     * the primary color for UI elements in the browse fragment. For example,
-     * the background color of the headers fragment uses the brand color.
-     *
-     * @param color The color to use as the brand color of the fragment.
-     */
-    public void setBrandColor(@ColorInt int color) {
-        mBrandColor = color;
-        mBrandColorSet = true;
-
-        if (mHeadersFragment != null) {
-            mHeadersFragment.setBackgroundColor(mBrandColor);
-        }
-    }
-
-    /**
-     * Returns the brand color for the browse fragment.
-     * The default is transparent.
-     */
-    @ColorInt
-    public int getBrandColor() {
-        return mBrandColor;
-    }
-
-    /**
-     * Wrapping app provided PresenterSelector to support InvisibleRowPresenter for SectionRow
-     * DividerRow and PageRow.
-     */
-    private void updateWrapperPresenter() {
-        if (mAdapter == null) {
-            mAdapterPresenter = null;
-            return;
-        }
-        final PresenterSelector adapterPresenter = mAdapter.getPresenterSelector();
-        if (adapterPresenter == null) {
-            throw new IllegalArgumentException("Adapter.getPresenterSelector() is null");
-        }
-        if (adapterPresenter == mAdapterPresenter) {
-            return;
-        }
-        mAdapterPresenter = adapterPresenter;
-
-        Presenter[] presenters = adapterPresenter.getPresenters();
-        final Presenter invisibleRowPresenter = new InvisibleRowPresenter();
-        final Presenter[] allPresenters = new Presenter[presenters.length + 1];
-        System.arraycopy(allPresenters, 0, presenters, 0, presenters.length);
-        allPresenters[allPresenters.length - 1] = invisibleRowPresenter;
-        mAdapter.setPresenterSelector(new PresenterSelector() {
-            @Override
-            public Presenter getPresenter(Object item) {
-                Row row = (Row) item;
-                if (row.isRenderedAsRowView()) {
-                    return adapterPresenter.getPresenter(item);
-                } else {
-                    return invisibleRowPresenter;
-                }
-            }
-
-            @Override
-            public Presenter[] getPresenters() {
-                return allPresenters;
-            }
-        });
-    }
-
-    /**
-     * Sets the adapter containing the rows for the fragment.
-     *
-     * <p>The items referenced by the adapter must be be derived from
-     * {@link Row}. These rows will be used by the rows fragment and the headers
-     * fragment (if not disabled) to render the browse rows.
-     *
-     * @param adapter An ObjectAdapter for the browse rows. All items must
-     *        derive from {@link Row}.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        updateWrapperPresenter();
-        if (getView() == null) {
-            return;
-        }
-
-        updateMainFragmentRowsAdapter();
-        mHeadersFragment.setAdapter(mAdapter);
-    }
-
-    void setMainFragmentRowsAdapter(MainFragmentRowsAdapter mainFragmentRowsAdapter) {
-        if (mainFragmentRowsAdapter == mMainFragmentRowsAdapter) {
-            return;
-        }
-        // first clear previous mMainFragmentRowsAdapter and set a new mMainFragmentRowsAdapter
-        if (mMainFragmentRowsAdapter != null) {
-            // RowsFragment cannot change click/select listeners after view created.
-            // The main fragment and adapter should be GCed as long as there is no reference from
-            // BrowseFragment to it.
-            mMainFragmentRowsAdapter.setAdapter(null);
-        }
-        mMainFragmentRowsAdapter = mainFragmentRowsAdapter;
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setOnItemViewSelectedListener(
-                    new MainFragmentItemViewSelectedListener(mMainFragmentRowsAdapter));
-            mMainFragmentRowsAdapter.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-        // second update mMainFragmentListRowDataAdapter set on mMainFragmentRowsAdapter
-        updateMainFragmentRowsAdapter();
-    }
-
-    /**
-     * Update mMainFragmentListRowDataAdapter and set it on mMainFragmentRowsAdapter.
-     * It also clears old mMainFragmentListRowDataAdapter.
-     */
-    void updateMainFragmentRowsAdapter() {
-        if (mMainFragmentListRowDataAdapter != null) {
-            mMainFragmentListRowDataAdapter.detach();
-            mMainFragmentListRowDataAdapter = null;
-        }
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentListRowDataAdapter = mAdapter == null
-                    ? null : new ListRowDataAdapter(mAdapter);
-            mMainFragmentRowsAdapter.setAdapter(mMainFragmentListRowDataAdapter);
-        }
-    }
-
-    public final MainFragmentAdapterRegistry getMainFragmentRegistry() {
-        return mMainFragmentAdapterRegistry;
-    }
-
-    /**
-     * Returns the adapter containing the rows for the fragment.
-     */
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mExternalOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Returns an item selection listener.
-     */
-    public OnItemViewSelectedListener getOnItemViewSelectedListener() {
-        return mExternalOnItemViewSelectedListener;
-    }
-
-    /**
-     * Get RowsFragment if it's bound to BrowseFragment or null if either BrowseFragment has
-     * not been created yet or a different fragment is bound to it.
-     *
-     * @return RowsFragment if it's bound to BrowseFragment or null otherwise.
-     */
-    public RowsFragment getRowsFragment() {
-        if (mMainFragment instanceof RowsFragment) {
-            return (RowsFragment) mMainFragment;
-        }
-
-        return null;
-    }
-
-    /**
-     * @return Current main fragment or null if not created.
-     */
-    public Fragment getMainFragment() {
-        return mMainFragment;
-    }
-
-    /**
-     * Get currently bound HeadersFragment or null if HeadersFragment has not been created yet.
-     * @return Currently bound HeadersFragment or null if HeadersFragment has not been created yet.
-     */
-    public HeadersFragment getHeadersFragment() {
-        return mHeadersFragment;
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
-     * OnItemViewClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general, developer should choose one of the listeners but not both.
-     */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setOnItemViewClickedListener(listener);
-        }
-    }
-
-    /**
-     * Returns the item Clicked listener.
-     */
-    public OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    /**
-     * Starts a headers transition.
-     *
-     * <p>This method will begin a transition to either show or hide the
-     * headers, depending on the value of withHeaders. If headers are disabled
-     * for this browse fragment, this method will throw an exception.
-     *
-     * @param withHeaders True if the headers should transition to being shown,
-     *        false if the transition should result in headers being hidden.
-     */
-    public void startHeadersTransition(boolean withHeaders) {
-        if (!mCanShowHeaders) {
-            throw new IllegalStateException("Cannot start headers transition");
-        }
-        if (isInHeadersTransition() || mShowingHeaders == withHeaders) {
-            return;
-        }
-        startHeadersTransitionInternal(withHeaders);
-    }
-
-    /**
-     * Returns true if the headers transition is currently running.
-     */
-    public boolean isInHeadersTransition() {
-        return mHeadersTransition != null;
-    }
-
-    /**
-     * Returns true if headers are shown.
-     */
-    public boolean isShowingHeaders() {
-        return mShowingHeaders;
-    }
-
-    /**
-     * Sets a listener for browse fragment transitions.
-     *
-     * @param listener The listener to call when a browse headers transition
-     *        begins or ends.
-     */
-    public void setBrowseTransitionListener(BrowseTransitionListener listener) {
-        mBrowseTransitionListener = listener;
-    }
-
-    /**
-     * @deprecated use {@link BrowseFragment#enableMainFragmentScaling(boolean)} instead.
-     *
-     * @param enable true to enable row scaling
-     */
-    @Deprecated
-    public void enableRowScaling(boolean enable) {
-        enableMainFragmentScaling(enable);
-    }
-
-    /**
-     * Enables scaling of main fragment when headers are present. For the page/row fragment,
-     * scaling is enabled only when both this method and
-     * {@link MainFragmentAdapter#isScalingEnabled()} are enabled.
-     *
-     * @param enable true to enable row scaling
-     */
-    public void enableMainFragmentScaling(boolean enable) {
-        mMainFragmentScaleEnabled = enable;
-    }
-
-    void startHeadersTransitionInternal(final boolean withHeaders) {
-        if (getFragmentManager().isDestroyed()) {
-            return;
-        }
-        if (!isHeadersDataReady()) {
-            return;
-        }
-        mShowingHeaders = withHeaders;
-        mMainFragmentAdapter.onTransitionPrepare();
-        mMainFragmentAdapter.onTransitionStart();
-        onExpandTransitionStart(!withHeaders, new Runnable() {
-            @Override
-            public void run() {
-                mHeadersFragment.onTransitionPrepare();
-                mHeadersFragment.onTransitionStart();
-                createHeadersTransition();
-                if (mBrowseTransitionListener != null) {
-                    mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
-                }
-                TransitionHelper.runTransition(
-                        withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders, mHeadersTransition);
-                if (mHeadersBackStackEnabled) {
-                    if (!withHeaders) {
-                        getFragmentManager().beginTransaction()
-                                .addToBackStack(mWithHeadersBackStackName).commit();
-                    } else {
-                        int index = mBackStackChangedListener.mIndexOfHeadersBackStack;
-                        if (index >= 0) {
-                            BackStackEntry entry = getFragmentManager().getBackStackEntryAt(index);
-                            getFragmentManager().popBackStackImmediate(entry.getId(),
-                                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
-                        }
-                    }
-                }
-            }
-        });
-    }
-
-    boolean isVerticalScrolling() {
-        // don't run transition
-        return mHeadersFragment.isScrolling() || mMainFragmentAdapter.isScrolling();
-    }
-
-
-    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
-            new BrowseFrameLayout.OnFocusSearchListener() {
-        @Override
-        public View onFocusSearch(View focused, int direction) {
-            // if headers is running transition,  focus stays
-            if (mCanShowHeaders && isInHeadersTransition()) {
-                return focused;
-            }
-            if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
-
-            if (getTitleView() != null && focused != getTitleView()
-                    && direction == View.FOCUS_UP) {
-                return getTitleView();
-            }
-            if (getTitleView() != null && getTitleView().hasFocus()
-                    && direction == View.FOCUS_DOWN) {
-                return mCanShowHeaders && mShowingHeaders
-                        ? mHeadersFragment.getVerticalGridView() : mMainFragment.getView();
-            }
-
-            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) {
-                if (isVerticalScrolling() || mShowingHeaders || !isHeadersDataReady()) {
-                    return focused;
-                }
-                return mHeadersFragment.getVerticalGridView();
-            } else if (direction == towardEnd) {
-                if (isVerticalScrolling()) {
-                    return focused;
-                } else if (mMainFragment != null && mMainFragment.getView() != null) {
-                    return mMainFragment.getView();
-                }
-                return focused;
-            } else if (direction == View.FOCUS_DOWN && mShowingHeaders) {
-                // disable focus_down moving into PageFragment.
-                return focused;
-            } else {
-                return null;
-            }
-        }
-    };
-
-    final boolean isHeadersDataReady() {
-        return mAdapter != null && mAdapter.size() != 0;
-    }
-
-    private final BrowseFrameLayout.OnChildFocusListener mOnChildFocusListener =
-            new BrowseFrameLayout.OnChildFocusListener() {
-
-        @Override
-        public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-            if (getChildFragmentManager().isDestroyed()) {
-                return true;
-            }
-            // Make sure not changing focus when requestFocus() is called.
-            if (mCanShowHeaders && mShowingHeaders) {
-                if (mHeadersFragment != null && mHeadersFragment.getView() != null
-                        && mHeadersFragment.getView().requestFocus(
-                                direction, previouslyFocusedRect)) {
-                    return true;
-                }
-            }
-            if (mMainFragment != null && mMainFragment.getView() != null
-                    && mMainFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
-                return true;
-            }
-            return getTitleView() != null
-                    && getTitleView().requestFocus(direction, previouslyFocusedRect);
-        }
-
-        @Override
-        public void onRequestChildFocus(View child, View focused) {
-            if (getChildFragmentManager().isDestroyed()) {
-                return;
-            }
-            if (!mCanShowHeaders || isInHeadersTransition()) return;
-            int childId = child.getId();
-            if (childId == R.id.browse_container_dock && mShowingHeaders) {
-                startHeadersTransitionInternal(false);
-            } else if (childId == R.id.browse_headers_dock && !mShowingHeaders) {
-                startHeadersTransitionInternal(true);
-            }
-        }
-    };
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(CURRENT_SELECTED_POSITION, mSelectedPosition);
-        outState.putBoolean(IS_PAGE_ROW, mIsPageRow);
-
-        if (mBackStackChangedListener != null) {
-            mBackStackChangedListener.save(outState);
-        } else {
-            outState.putBoolean(HEADER_SHOW, mShowingHeaders);
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final Context context = FragmentUtil.getContext(BrowseFragment.this);
-        TypedArray ta = context.obtainStyledAttributes(R.styleable.LeanbackTheme);
-        mContainerListMarginStart = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginStart, context.getResources()
-                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_start));
-        mContainerListAlignTop = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginTop, context.getResources()
-                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_top));
-        ta.recycle();
-
-        readArguments(getArguments());
-
-        if (mCanShowHeaders) {
-            if (mHeadersBackStackEnabled) {
-                mWithHeadersBackStackName = LB_HEADERS_BACKSTACK + this;
-                mBackStackChangedListener = new BackStackListener();
-                getFragmentManager().addOnBackStackChangedListener(mBackStackChangedListener);
-                mBackStackChangedListener.load(savedInstanceState);
-            } else {
-                if (savedInstanceState != null) {
-                    mShowingHeaders = savedInstanceState.getBoolean(HEADER_SHOW);
-                }
-            }
-        }
-
-        mScaleFactor = getResources().getFraction(R.fraction.lb_browse_rows_scale, 1, 1);
-    }
-
-    @Override
-    public void onDestroyView() {
-        setMainFragmentRowsAdapter(null);
-        mPageRow = null;
-        mMainFragmentAdapter = null;
-        mMainFragment = null;
-        mHeadersFragment = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mBackStackChangedListener != null) {
-            getFragmentManager().removeOnBackStackChangedListener(mBackStackChangedListener);
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * Creates a new {@link HeadersFragment} instance. Subclass of BrowseFragment may override and
-     * return an instance of subclass of HeadersFragment, e.g. when app wants to replace presenter
-     * to render HeaderItem.
-     *
-     * @return A new instance of {@link HeadersFragment} or its subclass.
-     */
-    public HeadersFragment onCreateHeadersFragment() {
-        return new HeadersFragment();
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-
-        if (getChildFragmentManager().findFragmentById(R.id.scale_frame) == null) {
-            mHeadersFragment = onCreateHeadersFragment();
-
-            createMainFragment(mAdapter, mSelectedPosition);
-            FragmentTransaction ft = getChildFragmentManager().beginTransaction()
-                    .replace(R.id.browse_headers_dock, mHeadersFragment);
-
-            if (mMainFragment != null) {
-                ft.replace(R.id.scale_frame, mMainFragment);
-            } else {
-                // Empty adapter used to guard against lazy adapter loading. When this
-                // fragment is instantiated, mAdapter might not have the data or might not
-                // have been set. In either of those cases mFragmentAdapter will be null.
-                // This way we can maintain the invariant that mMainFragmentAdapter is never
-                // null and it avoids doing null checks all over the code.
-                mMainFragmentAdapter = new MainFragmentAdapter(null);
-                mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
-            }
-
-            ft.commit();
-        } else {
-            mHeadersFragment = (HeadersFragment) getChildFragmentManager()
-                    .findFragmentById(R.id.browse_headers_dock);
-            mMainFragment = getChildFragmentManager().findFragmentById(R.id.scale_frame);
-
-            mIsPageRow = savedInstanceState != null
-                    && savedInstanceState.getBoolean(IS_PAGE_ROW, false);
-            // mPageRow object is unable to restore, if its null and mIsPageRow is true, this is
-            // the case for restoring, later if setSelection() triggers a createMainFragment(),
-            // should not create fragment.
-
-            mSelectedPosition = savedInstanceState != null
-                    ? savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
-
-            setMainFragmentAdapter();
-        }
-
-        mHeadersFragment.setHeadersGone(!mCanShowHeaders);
-        if (mHeaderPresenterSelector != null) {
-            mHeadersFragment.setPresenterSelector(mHeaderPresenterSelector);
-        }
-        mHeadersFragment.setAdapter(mAdapter);
-        mHeadersFragment.setOnHeaderViewSelectedListener(mHeaderViewSelectedListener);
-        mHeadersFragment.setOnHeaderClickedListener(mHeaderClickedListener);
-
-        View root = inflater.inflate(R.layout.lb_browse_fragment, container, false);
-
-        getProgressBarManager().setRootView((ViewGroup)root);
-
-        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
-        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
-        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
-
-        installTitleView(inflater, mBrowseFrame, savedInstanceState);
-
-        mScaleFrameLayout = (ScaleFrameLayout) root.findViewById(R.id.scale_frame);
-        mScaleFrameLayout.setPivotX(0);
-        mScaleFrameLayout.setPivotY(mContainerListAlignTop);
-
-        if (mBrandColorSet) {
-            mHeadersFragment.setBackgroundColor(mBrandColor);
-        }
-
-        mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                showHeaders(true);
-            }
-        });
-        mSceneWithoutHeaders =  TransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                showHeaders(false);
-            }
-        });
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                setEntranceTransitionEndState();
-            }
-        });
-
-        return root;
-    }
-
-    void createHeadersTransition() {
-        mHeadersTransition = TransitionHelper.loadTransition(FragmentUtil.getContext(BrowseFragment.this),
-                mShowingHeaders
-                        ? R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
-
-        TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
-            @Override
-            public void onTransitionStart(Object transition) {
-            }
-            @Override
-            public void onTransitionEnd(Object transition) {
-                mHeadersTransition = null;
-                if (mMainFragmentAdapter != null) {
-                    mMainFragmentAdapter.onTransitionEnd();
-                    if (!mShowingHeaders && mMainFragment != null) {
-                        View mainFragmentView = mMainFragment.getView();
-                        if (mainFragmentView != null && !mainFragmentView.hasFocus()) {
-                            mainFragmentView.requestFocus();
-                        }
-                    }
-                }
-                if (mHeadersFragment != null) {
-                    mHeadersFragment.onTransitionEnd();
-                    if (mShowingHeaders) {
-                        VerticalGridView headerGridView = mHeadersFragment.getVerticalGridView();
-                        if (headerGridView != null && !headerGridView.hasFocus()) {
-                            headerGridView.requestFocus();
-                        }
-                    }
-                }
-
-                // Animate TitleView once header animation is complete.
-                updateTitleViewVisibility();
-
-                if (mBrowseTransitionListener != null) {
-                    mBrowseTransitionListener.onHeadersTransitionStop(mShowingHeaders);
-                }
-            }
-        });
-    }
-
-    void updateTitleViewVisibility() {
-        if (!mShowingHeaders) {
-            boolean showTitleView;
-            if (mIsPageRow && mMainFragmentAdapter != null) {
-                // page fragment case:
-                showTitleView = mMainFragmentAdapter.mFragmentHost.mShowTitleView;
-            } else {
-                // regular row view case:
-                showTitleView = isFirstRowWithContent(mSelectedPosition);
-            }
-            if (showTitleView) {
-                showTitle(TitleViewAdapter.FULL_VIEW_VISIBLE);
-            } else {
-                showTitle(false);
-            }
-        } else {
-            // when HeaderFragment is showing,  showBranding and showSearch are slightly different
-            boolean showBranding;
-            boolean showSearch;
-            if (mIsPageRow && mMainFragmentAdapter != null) {
-                showBranding = mMainFragmentAdapter.mFragmentHost.mShowTitleView;
-            } else {
-                showBranding = isFirstRowWithContent(mSelectedPosition);
-            }
-            showSearch = isFirstRowWithContentOrPageRow(mSelectedPosition);
-            int flags = 0;
-            if (showBranding) flags |= TitleViewAdapter.BRANDING_VIEW_VISIBLE;
-            if (showSearch) flags |= TitleViewAdapter.SEARCH_VIEW_VISIBLE;
-            if (flags != 0) {
-                showTitle(flags);
-            } else {
-                showTitle(false);
-            }
-        }
-    }
-
-    boolean isFirstRowWithContentOrPageRow(int rowPosition) {
-        if (mAdapter == null || mAdapter.size() == 0) {
-            return true;
-        }
-        for (int i = 0; i < mAdapter.size(); i++) {
-            final Row row = (Row) mAdapter.get(i);
-            if (row.isRenderedAsRowView() || row instanceof PageRow) {
-                return rowPosition == i;
-            }
-        }
-        return true;
-    }
-
-    boolean isFirstRowWithContent(int rowPosition) {
-        if (mAdapter == null || mAdapter.size() == 0) {
-            return true;
-        }
-        for (int i = 0; i < mAdapter.size(); i++) {
-            final Row row = (Row) mAdapter.get(i);
-            if (row.isRenderedAsRowView()) {
-                return rowPosition == i;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Sets the {@link PresenterSelector} used to render the row headers.
-     *
-     * @param headerPresenterSelector The PresenterSelector that will determine
-     *        the Presenter for each row header.
-     */
-    public void setHeaderPresenterSelector(PresenterSelector headerPresenterSelector) {
-        mHeaderPresenterSelector = headerPresenterSelector;
-        if (mHeadersFragment != null) {
-            mHeadersFragment.setPresenterSelector(mHeaderPresenterSelector);
-        }
-    }
-
-    private void setHeadersOnScreen(boolean onScreen) {
-        MarginLayoutParams lp;
-        View containerList;
-        containerList = mHeadersFragment.getView();
-        lp = (MarginLayoutParams) containerList.getLayoutParams();
-        lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
-        containerList.setLayoutParams(lp);
-    }
-
-    void showHeaders(boolean show) {
-        if (DEBUG) Log.v(TAG, "showHeaders " + show);
-        mHeadersFragment.setHeadersEnabled(show);
-        setHeadersOnScreen(show);
-        expandMainFragment(!show);
-    }
-
-    private void expandMainFragment(boolean expand) {
-        MarginLayoutParams params = (MarginLayoutParams) mScaleFrameLayout.getLayoutParams();
-        params.setMarginStart(!expand ? mContainerListMarginStart : 0);
-        mScaleFrameLayout.setLayoutParams(params);
-        mMainFragmentAdapter.setExpand(expand);
-
-        setMainFragmentAlignment();
-        final float scaleFactor = !expand
-                && mMainFragmentScaleEnabled
-                && mMainFragmentAdapter.isScalingEnabled() ? mScaleFactor : 1;
-        mScaleFrameLayout.setLayoutScaleY(scaleFactor);
-        mScaleFrameLayout.setChildScale(scaleFactor);
-    }
-
-    private HeadersFragment.OnHeaderClickedListener mHeaderClickedListener =
-        new HeadersFragment.OnHeaderClickedListener() {
-            @Override
-            public void onHeaderClicked(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
-                if (!mCanShowHeaders || !mShowingHeaders || isInHeadersTransition()) {
-                    return;
-                }
-                if (mMainFragment == null || mMainFragment.getView() == null) {
-                    return;
-                }
-                startHeadersTransitionInternal(false);
-                mMainFragment.getView().requestFocus();
-            }
-        };
-
-    class MainFragmentItemViewSelectedListener implements OnItemViewSelectedListener {
-        MainFragmentRowsAdapter mMainFragmentRowsAdapter;
-
-        public MainFragmentItemViewSelectedListener(MainFragmentRowsAdapter fragmentRowsAdapter) {
-            mMainFragmentRowsAdapter = fragmentRowsAdapter;
-        }
-
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                RowPresenter.ViewHolder rowViewHolder, Row row) {
-            int position = mMainFragmentRowsAdapter.getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "row selected position " + position);
-            onRowSelected(position);
-            if (mExternalOnItemViewSelectedListener != null) {
-                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                        rowViewHolder, row);
-            }
-        }
-    };
-
-    private HeadersFragment.OnHeaderViewSelectedListener mHeaderViewSelectedListener =
-            new HeadersFragment.OnHeaderViewSelectedListener() {
-        @Override
-        public void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
-            int position = mHeadersFragment.getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "header selected position " + position);
-            onRowSelected(position);
-        }
-    };
-
-    void onRowSelected(int position) {
-        // even position is same, it could be data changed, always post selection runnable
-        // to possibly swap main fragment.
-        mSetSelectionRunnable.post(
-                position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
-    }
-
-    void setSelection(int position, boolean smooth) {
-        if (position == NO_POSITION) {
-            return;
-        }
-
-        mSelectedPosition = position;
-        if (mHeadersFragment == null || mMainFragmentAdapter == null) {
-            // onDestroyView() called
-            return;
-        }
-        mHeadersFragment.setSelectedPosition(position, smooth);
-        replaceMainFragment(position);
-
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setSelectedPosition(position, smooth);
-        }
-
-        updateTitleViewVisibility();
-    }
-
-    private void replaceMainFragment(int position) {
-        if (createMainFragment(mAdapter, position)) {
-            swapToMainFragment();
-            expandMainFragment(!(mCanShowHeaders && mShowingHeaders));
-        }
-    }
-
-    private void swapToMainFragment() {
-        final VerticalGridView gridView = mHeadersFragment.getVerticalGridView();
-        if (isShowingHeaders() && gridView != null
-                && gridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            // if user is scrolling HeadersFragment,  swap to empty fragment and wait scrolling
-            // finishes.
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.scale_frame, new Fragment()).commit();
-            gridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-                @SuppressWarnings("ReferenceEquality")
-                @Override
-                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
-                        gridView.removeOnScrollListener(this);
-                        FragmentManager fm = getChildFragmentManager();
-                        Fragment currentFragment = fm.findFragmentById(R.id.scale_frame);
-                        if (currentFragment != mMainFragment) {
-                            fm.beginTransaction().replace(R.id.scale_frame, mMainFragment).commit();
-                        }
-                    }
-                }
-            });
-        } else {
-            // Otherwise swap immediately
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.scale_frame, mMainFragment).commit();
-        }
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Gets position of currently selected row.
-     * @return Position of currently selected row.
-     */
-    public int getSelectedPosition() {
-        return mSelectedPosition;
-    }
-
-    /**
-     * @return selected row ViewHolder inside fragment created by {@link MainFragmentRowsAdapter}.
-     */
-    public RowPresenter.ViewHolder getSelectedRowViewHolder() {
-        if (mMainFragmentRowsAdapter != null) {
-            int rowPos = mMainFragmentRowsAdapter.getSelectedPosition();
-            return mMainFragmentRowsAdapter.findRowViewHolderByPosition(rowPos);
-        }
-        return null;
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.post(
-                position, SetSelectionRunnable.TYPE_USER_REQUEST, smooth);
-    }
-
-    /**
-     * Selects a Row and perform an optional task on the Row. For example
-     * <code>setSelectedPosition(10, true, new ListRowPresenterSelectItemViewHolderTask(5))</code>
-     * scrolls to 11th row and selects 6th item on that row.  The method will be ignored if
-     * RowsFragment has not been created (i.e. before {@link #onCreateView(LayoutInflater,
-     * ViewGroup, Bundle)}).
-     *
-     * @param rowPosition Which row to select.
-     * @param smooth True to scroll to the row, false for no animation.
-     * @param rowHolderTask Optional task to perform on the Row.  When the task is not null, headers
-     * fragment will be collapsed.
-     */
-    public void setSelectedPosition(int rowPosition, boolean smooth,
-            final Presenter.ViewHolderTask rowHolderTask) {
-        if (mMainFragmentAdapterRegistry == null) {
-            return;
-        }
-        if (rowHolderTask != null) {
-            startHeadersTransition(false);
-        }
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setSelectedPosition(rowPosition, smooth, rowHolderTask);
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mHeadersFragment.setAlignment(mContainerListAlignTop);
-        setMainFragmentAlignment();
-
-        if (mCanShowHeaders && mShowingHeaders && mHeadersFragment != null
-                && mHeadersFragment.getView() != null) {
-            mHeadersFragment.getView().requestFocus();
-        } else if ((!mCanShowHeaders || !mShowingHeaders) && mMainFragment != null
-                && mMainFragment.getView() != null) {
-            mMainFragment.getView().requestFocus();
-        }
-
-        if (mCanShowHeaders) {
-            showHeaders(mShowingHeaders);
-        }
-
-        mStateMachine.fireEvent(EVT_HEADER_VIEW_CREATED);
-    }
-
-    private void onExpandTransitionStart(boolean expand, final Runnable callback) {
-        if (expand) {
-            callback.run();
-            return;
-        }
-        // Run a "pre" layout when we go non-expand, in order to get the initial
-        // positions of added rows.
-        new ExpandPreLayout(callback, mMainFragmentAdapter, getView()).execute();
-    }
-
-    private void setMainFragmentAlignment() {
-        int alignOffset = mContainerListAlignTop;
-        if (mMainFragmentScaleEnabled
-                && mMainFragmentAdapter.isScalingEnabled()
-                && mShowingHeaders) {
-            alignOffset = (int) (alignOffset / mScaleFactor + 0.5f);
-        }
-        mMainFragmentAdapter.setAlignment(alignOffset);
-    }
-
-    /**
-     * Enables/disables headers transition on back key support. This is enabled by
-     * default. The BrowseFragment will add a back stack entry when headers are
-     * showing. Running a headers transition when the back key is pressed only
-     * works when the headers state is {@link #HEADERS_ENABLED} or
-     * {@link #HEADERS_HIDDEN}.
-     * <p>
-     * NOTE: If an Activity has its own onBackPressed() handling, you must
-     * disable this feature. You may use {@link #startHeadersTransition(boolean)}
-     * and {@link BrowseTransitionListener} in your own back stack handling.
-     */
-    public final void setHeadersTransitionOnBackEnabled(boolean headersBackStackEnabled) {
-        mHeadersBackStackEnabled = headersBackStackEnabled;
-    }
-
-    /**
-     * Returns true if headers transition on back key support is enabled.
-     */
-    public final boolean isHeadersTransitionOnBackEnabled() {
-        return mHeadersBackStackEnabled;
-    }
-
-    private void readArguments(Bundle args) {
-        if (args == null) {
-            return;
-        }
-        if (args.containsKey(ARG_TITLE)) {
-            setTitle(args.getString(ARG_TITLE));
-        }
-        if (args.containsKey(ARG_HEADERS_STATE)) {
-            setHeadersState(args.getInt(ARG_HEADERS_STATE));
-        }
-    }
-
-    /**
-     * Sets the state for the headers column in the browse fragment. Must be one
-     * of {@link #HEADERS_ENABLED}, {@link #HEADERS_HIDDEN}, or
-     * {@link #HEADERS_DISABLED}.
-     *
-     * @param headersState The state of the headers for the browse fragment.
-     */
-    public void setHeadersState(int headersState) {
-        if (headersState < HEADERS_ENABLED || headersState > HEADERS_DISABLED) {
-            throw new IllegalArgumentException("Invalid headers state: " + headersState);
-        }
-        if (DEBUG) Log.v(TAG, "setHeadersState " + headersState);
-
-        if (headersState != mHeadersState) {
-            mHeadersState = headersState;
-            switch (headersState) {
-                case HEADERS_ENABLED:
-                    mCanShowHeaders = true;
-                    mShowingHeaders = true;
-                    break;
-                case HEADERS_HIDDEN:
-                    mCanShowHeaders = true;
-                    mShowingHeaders = false;
-                    break;
-                case HEADERS_DISABLED:
-                    mCanShowHeaders = false;
-                    mShowingHeaders = false;
-                    break;
-                default:
-                    Log.w(TAG, "Unknown headers state: " + headersState);
-                    break;
-            }
-            if (mHeadersFragment != null) {
-                mHeadersFragment.setHeadersGone(!mCanShowHeaders);
-            }
-        }
-    }
-
-    /**
-     * Returns the state of the headers column in the browse fragment.
-     */
-    public int getHeadersState() {
-        return mHeadersState;
-    }
-
-    @Override
-    protected Object createEntranceTransition() {
-        return TransitionHelper.loadTransition(FragmentUtil.getContext(BrowseFragment.this),
-                R.transition.lb_browse_entrance_transition);
-    }
-
-    @Override
-    protected void runEntranceTransition(Object entranceTransition) {
-        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
-    }
-
-    @Override
-    protected void onEntranceTransitionPrepare() {
-        mHeadersFragment.onTransitionPrepare();
-        mMainFragmentAdapter.setEntranceTransitionState(false);
-        mMainFragmentAdapter.onTransitionPrepare();
-    }
-
-    @Override
-    protected void onEntranceTransitionStart() {
-        mHeadersFragment.onTransitionStart();
-        mMainFragmentAdapter.onTransitionStart();
-    }
-
-    @Override
-    protected void onEntranceTransitionEnd() {
-        if (mMainFragmentAdapter != null) {
-            mMainFragmentAdapter.onTransitionEnd();
-        }
-
-        if (mHeadersFragment != null) {
-            mHeadersFragment.onTransitionEnd();
-        }
-    }
-
-    void setSearchOrbViewOnScreen(boolean onScreen) {
-        View searchOrbView = getTitleViewAdapter().getSearchAffordanceView();
-        if (searchOrbView != null) {
-            MarginLayoutParams lp = (MarginLayoutParams) searchOrbView.getLayoutParams();
-            lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
-            searchOrbView.setLayoutParams(lp);
-        }
-    }
-
-    void setEntranceTransitionStartState() {
-        setHeadersOnScreen(false);
-        setSearchOrbViewOnScreen(false);
-        // NOTE that mMainFragmentAdapter.setEntranceTransitionState(false) will be called
-        // in onEntranceTransitionPrepare() because mMainFragmentAdapter is still the dummy
-        // one when setEntranceTransitionStartState() is called.
-    }
-
-    void setEntranceTransitionEndState() {
-        setHeadersOnScreen(mShowingHeaders);
-        setSearchOrbViewOnScreen(true);
-        mMainFragmentAdapter.setEntranceTransitionState(true);
-    }
-
-    private class ExpandPreLayout implements ViewTreeObserver.OnPreDrawListener {
-
-        private final View mView;
-        private final Runnable mCallback;
-        private int mState;
-        private MainFragmentAdapter mainFragmentAdapter;
-
-        final static int STATE_INIT = 0;
-        final static int STATE_FIRST_DRAW = 1;
-        final static int STATE_SECOND_DRAW = 2;
-
-        ExpandPreLayout(Runnable callback, MainFragmentAdapter adapter, View view) {
-            mView = view;
-            mCallback = callback;
-            mainFragmentAdapter = adapter;
-        }
-
-        void execute() {
-            mView.getViewTreeObserver().addOnPreDrawListener(this);
-            mainFragmentAdapter.setExpand(false);
-            // always trigger onPreDraw even adapter setExpand() does nothing.
-            mView.invalidate();
-            mState = STATE_INIT;
-        }
-
-        @Override
-        public boolean onPreDraw() {
-            if (getView() == null || FragmentUtil.getContext(BrowseFragment.this) == null) {
-                mView.getViewTreeObserver().removeOnPreDrawListener(this);
-                return true;
-            }
-            if (mState == STATE_INIT) {
-                mainFragmentAdapter.setExpand(true);
-                // always trigger onPreDraw even adapter setExpand() does nothing.
-                mView.invalidate();
-                mState = STATE_FIRST_DRAW;
-            } else if (mState == STATE_FIRST_DRAW) {
-                mCallback.run();
-                mView.getViewTreeObserver().removeOnPreDrawListener(this);
-                mState = STATE_SECOND_DRAW;
-            }
-            return false;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/BrowseSupportFragment.java b/android/support/v17/leanback/app/BrowseSupportFragment.java
deleted file mode 100644
index 114e0a7..0000000
--- a/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ /dev/null
@@ -1,1848 +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.v17.leanback.app;
-
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.util.StateMachine.Event;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.support.v17.leanback.widget.BrowseFrameLayout;
-import android.support.v17.leanback.widget.InvisibleRowPresenter;
-import android.support.v17.leanback.widget.ListRow;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.PageRow;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowHeaderPresenter;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.ScaleFrameLayout;
-import android.support.v17.leanback.widget.TitleViewAdapter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentManager.BackStackEntry;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.view.ViewTreeObserver;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A fragment for creating Leanback browse screens. It is composed of a
- * RowsSupportFragment and a HeadersSupportFragment.
- * <p>
- * A BrowseSupportFragment renders the elements of its {@link ObjectAdapter} as a set
- * of rows in a vertical list. The elements in this adapter must be subclasses
- * of {@link Row}.
- * <p>
- * The HeadersSupportFragment can be set to be either shown or hidden by default, or
- * may be disabled entirely. See {@link #setHeadersState} for details.
- * <p>
- * By default the BrowseSupportFragment includes support for returning to the headers
- * when the user presses Back. For Activities that customize {@link
- * android.support.v4.app.FragmentActivity#onBackPressed()}, you must disable this default Back key support by
- * calling {@link #setHeadersTransitionOnBackEnabled(boolean)} with false and
- * use {@link BrowseSupportFragment.BrowseTransitionListener} and
- * {@link #startHeadersTransition(boolean)}.
- * <p>
- * The recommended theme to use with a BrowseSupportFragment is
- * {@link android.support.v17.leanback.R.style#Theme_Leanback_Browse}.
- * </p>
- */
-public class BrowseSupportFragment extends BaseSupportFragment {
-
-    // BUNDLE attribute for saving header show/hide status when backstack is used:
-    static final String HEADER_STACK_INDEX = "headerStackIndex";
-    // BUNDLE attribute for saving header show/hide status when backstack is not used:
-    static final String HEADER_SHOW = "headerShow";
-    private static final String IS_PAGE_ROW = "isPageRow";
-    private static final String CURRENT_SELECTED_POSITION = "currentSelectedPosition";
-
-    /**
-     * State to hide headers fragment.
-     */
-    final State STATE_SET_ENTRANCE_START_STATE = new State("SET_ENTRANCE_START_STATE") {
-        @Override
-        public void run() {
-            setEntranceTransitionStartState();
-        }
-    };
-
-    /**
-     * Event for Header fragment view is created, we could perform
-     * {@link #setEntranceTransitionStartState()} to hide headers fragment initially.
-     */
-    final Event EVT_HEADER_VIEW_CREATED = new Event("headerFragmentViewCreated");
-
-    /**
-     * Event for {@link #getMainFragment()} view is created, it's additional requirement to execute
-     * {@link #onEntranceTransitionPrepare()}.
-     */
-    final Event EVT_MAIN_FRAGMENT_VIEW_CREATED = new Event("mainFragmentViewCreated");
-
-    /**
-     * Event that data for the screen is ready, this is additional requirement to launch entrance
-     * transition.
-     */
-    final Event EVT_SCREEN_DATA_READY = new Event("screenDataReady");
-
-    @Override
-    void createStateMachineStates() {
-        super.createStateMachineStates();
-        mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
-    }
-
-    @Override
-    void createStateMachineTransitions() {
-        super.createStateMachineTransitions();
-        // when headers fragment view is created we could setEntranceTransitionStartState()
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED, STATE_SET_ENTRANCE_START_STATE,
-                EVT_HEADER_VIEW_CREATED);
-
-        // add additional requirement for onEntranceTransitionPrepare()
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_ENTRANCE_ON_PREPARED_ON_CREATEVIEW,
-                EVT_MAIN_FRAGMENT_VIEW_CREATED);
-        // add additional requirement to launch entrance transition.
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,  STATE_ENTRANCE_PERFORM,
-                EVT_SCREEN_DATA_READY);
-    }
-
-    final class BackStackListener implements FragmentManager.OnBackStackChangedListener {
-        int mLastEntryCount;
-        int mIndexOfHeadersBackStack;
-
-        BackStackListener() {
-            mLastEntryCount = getFragmentManager().getBackStackEntryCount();
-            mIndexOfHeadersBackStack = -1;
-        }
-
-        void load(Bundle savedInstanceState) {
-            if (savedInstanceState != null) {
-                mIndexOfHeadersBackStack = savedInstanceState.getInt(HEADER_STACK_INDEX, -1);
-                mShowingHeaders = mIndexOfHeadersBackStack == -1;
-            } else {
-                if (!mShowingHeaders) {
-                    getFragmentManager().beginTransaction()
-                            .addToBackStack(mWithHeadersBackStackName).commit();
-                }
-            }
-        }
-
-        void save(Bundle outState) {
-            outState.putInt(HEADER_STACK_INDEX, mIndexOfHeadersBackStack);
-        }
-
-
-        @Override
-        public void onBackStackChanged() {
-            if (getFragmentManager() == null) {
-                Log.w(TAG, "getFragmentManager() is null, stack:", new Exception());
-                return;
-            }
-            int count = getFragmentManager().getBackStackEntryCount();
-            // if backstack is growing and last pushed entry is "headers" backstack,
-            // remember the index of the entry.
-            if (count > mLastEntryCount) {
-                BackStackEntry entry = getFragmentManager().getBackStackEntryAt(count - 1);
-                if (mWithHeadersBackStackName.equals(entry.getName())) {
-                    mIndexOfHeadersBackStack = count - 1;
-                }
-            } else if (count < mLastEntryCount) {
-                // if popped "headers" backstack, initiate the show header transition if needed
-                if (mIndexOfHeadersBackStack >= count) {
-                    if (!isHeadersDataReady()) {
-                        // if main fragment was restored first before BrowseSupportFragment's adapter gets
-                        // restored: don't start header transition, but add the entry back.
-                        getFragmentManager().beginTransaction()
-                                .addToBackStack(mWithHeadersBackStackName).commit();
-                        return;
-                    }
-                    mIndexOfHeadersBackStack = -1;
-                    if (!mShowingHeaders) {
-                        startHeadersTransitionInternal(true);
-                    }
-                }
-            }
-            mLastEntryCount = count;
-        }
-    }
-
-    /**
-     * Listener for transitions between browse headers and rows.
-     */
-    public static class BrowseTransitionListener {
-        /**
-         * Callback when headers transition starts.
-         *
-         * @param withHeaders True if the transition will result in headers
-         *        being shown, false otherwise.
-         */
-        public void onHeadersTransitionStart(boolean withHeaders) {
-        }
-        /**
-         * Callback when headers transition stops.
-         *
-         * @param withHeaders True if the transition will result in headers
-         *        being shown, false otherwise.
-         */
-        public void onHeadersTransitionStop(boolean withHeaders) {
-        }
-    }
-
-    private class SetSelectionRunnable implements Runnable {
-        static final int TYPE_INVALID = -1;
-        static final int TYPE_INTERNAL_SYNC = 0;
-        static final int TYPE_USER_REQUEST = 1;
-
-        private int mPosition;
-        private int mType;
-        private boolean mSmooth;
-
-        SetSelectionRunnable() {
-            reset();
-        }
-
-        void post(int position, int type, boolean smooth) {
-            // Posting the set selection, rather than calling it immediately, prevents an issue
-            // with adapter changes.  Example: a row is added before the current selected row;
-            // first the fast lane view updates its selection, then the rows fragment has that
-            // new selection propagated immediately; THEN the rows view processes the same adapter
-            // change and moves the selection again.
-            if (type >= mType) {
-                mPosition = position;
-                mType = type;
-                mSmooth = smooth;
-                mBrowseFrame.removeCallbacks(this);
-                mBrowseFrame.post(this);
-            }
-        }
-
-        @Override
-        public void run() {
-            setSelection(mPosition, mSmooth);
-            reset();
-        }
-
-        private void reset() {
-            mPosition = -1;
-            mType = TYPE_INVALID;
-            mSmooth = false;
-        }
-    }
-
-    /**
-     * Possible set of actions that {@link BrowseSupportFragment} exposes to clients. Custom
-     * fragments can interact with {@link BrowseSupportFragment} using this interface.
-     */
-    public interface FragmentHost {
-        /**
-         * Fragments are required to invoke this callback once their view is created
-         * inside {@link Fragment#onViewCreated} method. {@link BrowseSupportFragment} starts the entrance
-         * animation only after receiving this callback. Failure to invoke this method
-         * will lead to fragment not showing up.
-         *
-         * @param fragmentAdapter {@link MainFragmentAdapter} used by the current fragment.
-         */
-        void notifyViewCreated(MainFragmentAdapter fragmentAdapter);
-
-        /**
-         * Fragments mapped to {@link PageRow} are required to invoke this callback once their data
-         * is created for transition, the entrance animation only after receiving this callback.
-         * Failure to invoke this method will lead to fragment not showing up.
-         *
-         * @param fragmentAdapter {@link MainFragmentAdapter} used by the current fragment.
-         */
-        void notifyDataReady(MainFragmentAdapter fragmentAdapter);
-
-        /**
-         * Show or hide title view in {@link BrowseSupportFragment} for fragments mapped to
-         * {@link PageRow}.  Otherwise the request is ignored, in that case BrowseSupportFragment is fully
-         * in control of showing/hiding title view.
-         * <p>
-         * When HeadersSupportFragment is visible, BrowseSupportFragment will hide search affordance view if
-         * there are other focusable rows above currently focused row.
-         *
-         * @param show Boolean indicating whether or not to show the title view.
-         */
-        void showTitleView(boolean show);
-    }
-
-    /**
-     * Default implementation of {@link FragmentHost} that is used only by
-     * {@link BrowseSupportFragment}.
-     */
-    private final class FragmentHostImpl implements FragmentHost {
-        boolean mShowTitleView = true;
-
-        FragmentHostImpl() {
-        }
-
-        @Override
-        public void notifyViewCreated(MainFragmentAdapter fragmentAdapter) {
-            mStateMachine.fireEvent(EVT_MAIN_FRAGMENT_VIEW_CREATED);
-            if (!mIsPageRow) {
-                // If it's not a PageRow: it's a ListRow, so we already have data ready.
-                mStateMachine.fireEvent(EVT_SCREEN_DATA_READY);
-            }
-        }
-
-        @Override
-        public void notifyDataReady(MainFragmentAdapter fragmentAdapter) {
-            // If fragment host is not the currently active fragment (in BrowseSupportFragment), then
-            // ignore the request.
-            if (mMainFragmentAdapter == null || mMainFragmentAdapter.getFragmentHost() != this) {
-                return;
-            }
-
-            // We only honor showTitle request for PageRows.
-            if (!mIsPageRow) {
-                return;
-            }
-
-            mStateMachine.fireEvent(EVT_SCREEN_DATA_READY);
-        }
-
-        @Override
-        public void showTitleView(boolean show) {
-            mShowTitleView = show;
-
-            // If fragment host is not the currently active fragment (in BrowseSupportFragment), then
-            // ignore the request.
-            if (mMainFragmentAdapter == null || mMainFragmentAdapter.getFragmentHost() != this) {
-                return;
-            }
-
-            // We only honor showTitle request for PageRows.
-            if (!mIsPageRow) {
-                return;
-            }
-
-            updateTitleViewVisibility();
-        }
-    }
-
-    /**
-     * Interface that defines the interaction between {@link BrowseSupportFragment} and its main
-     * content fragment. The key method is {@link MainFragmentAdapter#getFragment()},
-     * it will be used to get the fragment to be shown in the content section. Clients can
-     * provide any implementation of fragment and customize its interaction with
-     * {@link BrowseSupportFragment} by overriding the necessary methods.
-     *
-     * <p>
-     * Clients are expected to provide
-     * an instance of {@link MainFragmentAdapterRegistry} which will be responsible for providing
-     * implementations of {@link MainFragmentAdapter} for given content types. Currently
-     * we support different types of content - {@link ListRow}, {@link PageRow} or any subtype
-     * of {@link Row}. We provide an out of the box adapter implementation for any rows other than
-     * {@link PageRow} - {@link android.support.v17.leanback.app.RowsSupportFragment.MainFragmentAdapter}.
-     *
-     * <p>
-     * {@link PageRow} is intended to give full flexibility to developers in terms of Fragment
-     * design. Users will have to provide an implementation of {@link MainFragmentAdapter}
-     * and provide that through {@link MainFragmentAdapterRegistry}.
-     * {@link MainFragmentAdapter} implementation can supply any fragment and override
-     * just those interactions that makes sense.
-     */
-    public static class MainFragmentAdapter<T extends Fragment> {
-        private boolean mScalingEnabled;
-        private final T mFragment;
-        FragmentHostImpl mFragmentHost;
-
-        public MainFragmentAdapter(T fragment) {
-            this.mFragment = fragment;
-        }
-
-        public final T getFragment() {
-            return mFragment;
-        }
-
-        /**
-         * Returns whether its scrolling.
-         */
-        public boolean isScrolling() {
-            return false;
-        }
-
-        /**
-         * Set the visibility of titles/hover card of browse rows.
-         */
-        public void setExpand(boolean expand) {
-        }
-
-        /**
-         * For rows that willing to participate entrance transition,  this function
-         * hide views if afterTransition is true,  show views if afterTransition is false.
-         */
-        public void setEntranceTransitionState(boolean state) {
-        }
-
-        /**
-         * Sets the window alignment and also the pivots for scale operation.
-         */
-        public void setAlignment(int windowAlignOffsetFromTop) {
-        }
-
-        /**
-         * Callback indicating transition prepare start.
-         */
-        public boolean onTransitionPrepare() {
-            return false;
-        }
-
-        /**
-         * Callback indicating transition start.
-         */
-        public void onTransitionStart() {
-        }
-
-        /**
-         * Callback indicating transition end.
-         */
-        public void onTransitionEnd() {
-        }
-
-        /**
-         * Returns whether row scaling is enabled.
-         */
-        public boolean isScalingEnabled() {
-            return mScalingEnabled;
-        }
-
-        /**
-         * Sets the row scaling property.
-         */
-        public void setScalingEnabled(boolean scalingEnabled) {
-            this.mScalingEnabled = scalingEnabled;
-        }
-
-        /**
-         * Returns the current host interface so that main fragment can interact with
-         * {@link BrowseSupportFragment}.
-         */
-        public final FragmentHost getFragmentHost() {
-            return mFragmentHost;
-        }
-
-        void setFragmentHost(FragmentHostImpl fragmentHost) {
-            this.mFragmentHost = fragmentHost;
-        }
-    }
-
-    /**
-     * Interface to be implemented by all fragments for providing an instance of
-     * {@link MainFragmentAdapter}. Both {@link RowsSupportFragment} and custom fragment provided
-     * against {@link PageRow} will need to implement this interface.
-     */
-    public interface MainFragmentAdapterProvider {
-        /**
-         * Returns an instance of {@link MainFragmentAdapter} that {@link BrowseSupportFragment}
-         * would use to communicate with the target fragment.
-         */
-        MainFragmentAdapter getMainFragmentAdapter();
-    }
-
-    /**
-     * Interface to be implemented by {@link RowsSupportFragment} and its subclasses for providing
-     * an instance of {@link MainFragmentRowsAdapter}.
-     */
-    public interface MainFragmentRowsAdapterProvider {
-        /**
-         * Returns an instance of {@link MainFragmentRowsAdapter} that {@link BrowseSupportFragment}
-         * would use to communicate with the target fragment.
-         */
-        MainFragmentRowsAdapter getMainFragmentRowsAdapter();
-    }
-
-    /**
-     * This is used to pass information to {@link RowsSupportFragment} or its subclasses.
-     * {@link BrowseSupportFragment} uses this interface to pass row based interaction events to
-     * the target fragment.
-     */
-    public static class MainFragmentRowsAdapter<T extends Fragment> {
-        private final T mFragment;
-
-        public MainFragmentRowsAdapter(T fragment) {
-            if (fragment == null) {
-                throw new IllegalArgumentException("Fragment can't be null");
-            }
-            this.mFragment = fragment;
-        }
-
-        public final T getFragment() {
-            return mFragment;
-        }
-        /**
-         * Set the visibility titles/hover of browse rows.
-         */
-        public void setAdapter(ObjectAdapter adapter) {
-        }
-
-        /**
-         * Sets an item clicked listener on the fragment.
-         */
-        public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        }
-
-        /**
-         * Sets an item selection listener.
-         */
-        public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        }
-
-        /**
-         * Selects a Row and perform an optional task on the Row.
-         */
-        public void setSelectedPosition(int rowPosition,
-                                        boolean smooth,
-                                        final Presenter.ViewHolderTask rowHolderTask) {
-        }
-
-        /**
-         * Selects a Row.
-         */
-        public void setSelectedPosition(int rowPosition, boolean smooth) {
-        }
-
-        /**
-         * @return The position of selected row.
-         */
-        public int getSelectedPosition() {
-            return 0;
-        }
-
-        /**
-         * @param position Position of Row.
-         * @return Row ViewHolder.
-         */
-        public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
-            return null;
-        }
-    }
-
-    private boolean createMainFragment(ObjectAdapter adapter, int position) {
-        Object item = null;
-        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) {
-                position = 0;
-            } else if (position >= adapter.size()) {
-                throw new IllegalArgumentException(
-                        String.format("Invalid position %d requested", position));
-            }
-            item = adapter.get(position);
-        }
-
-        boolean oldIsPageRow = mIsPageRow;
-        Object oldPageRow = mPageRow;
-        mIsPageRow = mCanShowHeaders && item instanceof PageRow;
-        mPageRow = mIsPageRow ? item : null;
-        boolean swap;
-
-        if (mMainFragment == null) {
-            swap = true;
-        } else {
-            if (oldIsPageRow) {
-                if (mIsPageRow) {
-                    if (oldPageRow == null) {
-                        // fragment is restored, page row object not yet set, so just set the
-                        // mPageRow object and there is no need to replace the fragment
-                        swap = false;
-                    } else {
-                        // swap if page row object changes
-                        swap = oldPageRow != mPageRow;
-                    }
-                } else {
-                    swap = true;
-                }
-            } else {
-                swap = mIsPageRow;
-            }
-        }
-
-        if (swap) {
-            mMainFragment = mMainFragmentAdapterRegistry.createFragment(item);
-            if (!(mMainFragment instanceof MainFragmentAdapterProvider)) {
-                throw new IllegalArgumentException(
-                        "Fragment must implement MainFragmentAdapterProvider");
-            }
-
-            setMainFragmentAdapter();
-        }
-
-        return swap;
-    }
-
-    void setMainFragmentAdapter() {
-        mMainFragmentAdapter = ((MainFragmentAdapterProvider) mMainFragment)
-                .getMainFragmentAdapter();
-        mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
-        if (!mIsPageRow) {
-            if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
-                setMainFragmentRowsAdapter(((MainFragmentRowsAdapterProvider) mMainFragment)
-                        .getMainFragmentRowsAdapter());
-            } else {
-                setMainFragmentRowsAdapter(null);
-            }
-            mIsPageRow = mMainFragmentRowsAdapter == null;
-        } else {
-            setMainFragmentRowsAdapter(null);
-        }
-    }
-
-    /**
-     * Factory class responsible for creating fragment given the current item. {@link ListRow}
-     * should return {@link RowsSupportFragment} or its subclass whereas {@link PageRow}
-     * can return any fragment class.
-     */
-    public abstract static class FragmentFactory<T extends Fragment> {
-        public abstract T createFragment(Object row);
-    }
-
-    /**
-     * FragmentFactory implementation for {@link ListRow}.
-     */
-    public static class ListRowFragmentFactory extends FragmentFactory<RowsSupportFragment> {
-        @Override
-        public RowsSupportFragment createFragment(Object row) {
-            return new RowsSupportFragment();
-        }
-    }
-
-    /**
-     * Registry class maintaining the mapping of {@link Row} subclasses to {@link FragmentFactory}.
-     * BrowseRowFragment automatically registers {@link ListRowFragmentFactory} for
-     * handling {@link ListRow}. Developers can override that and also if they want to
-     * use custom fragment, they can register a custom {@link FragmentFactory}
-     * against {@link PageRow}.
-     */
-    public final static class MainFragmentAdapterRegistry {
-        private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap<>();
-        private final static FragmentFactory sDefaultFragmentFactory = new ListRowFragmentFactory();
-
-        public MainFragmentAdapterRegistry() {
-            registerFragment(ListRow.class, sDefaultFragmentFactory);
-        }
-
-        public void registerFragment(Class rowClass, FragmentFactory factory) {
-            mItemToFragmentFactoryMapping.put(rowClass, factory);
-        }
-
-        public Fragment createFragment(Object item) {
-            FragmentFactory fragmentFactory = item == null ? sDefaultFragmentFactory :
-                    mItemToFragmentFactoryMapping.get(item.getClass());
-            if (fragmentFactory == null && !(item instanceof PageRow)) {
-                fragmentFactory = sDefaultFragmentFactory;
-            }
-
-            return fragmentFactory.createFragment(item);
-        }
-    }
-
-    static final String TAG = "BrowseSupportFragment";
-
-    private static final String LB_HEADERS_BACKSTACK = "lbHeadersBackStack_";
-
-    static boolean DEBUG = false;
-
-    /** The headers fragment is enabled and shown by default. */
-    public static final int HEADERS_ENABLED = 1;
-
-    /** The headers fragment is enabled and hidden by default. */
-    public static final int HEADERS_HIDDEN = 2;
-
-    /** The headers fragment is disabled and will never be shown. */
-    public static final int HEADERS_DISABLED = 3;
-
-    private MainFragmentAdapterRegistry mMainFragmentAdapterRegistry =
-            new MainFragmentAdapterRegistry();
-    MainFragmentAdapter mMainFragmentAdapter;
-    Fragment mMainFragment;
-    HeadersSupportFragment mHeadersSupportFragment;
-    MainFragmentRowsAdapter mMainFragmentRowsAdapter;
-    ListRowDataAdapter mMainFragmentListRowDataAdapter;
-
-    private ObjectAdapter mAdapter;
-    private PresenterSelector mAdapterPresenter;
-
-    private int mHeadersState = HEADERS_ENABLED;
-    private int mBrandColor = Color.TRANSPARENT;
-    private boolean mBrandColorSet;
-
-    BrowseFrameLayout mBrowseFrame;
-    private ScaleFrameLayout mScaleFrameLayout;
-    boolean mHeadersBackStackEnabled = true;
-    String mWithHeadersBackStackName;
-    boolean mShowingHeaders = true;
-    boolean mCanShowHeaders = true;
-    private int mContainerListMarginStart;
-    private int mContainerListAlignTop;
-    private boolean mMainFragmentScaleEnabled = true;
-    OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    private int mSelectedPosition = -1;
-    private float mScaleFactor;
-    boolean mIsPageRow;
-    Object mPageRow;
-
-    private PresenterSelector mHeaderPresenterSelector;
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
-    // transition related:
-    Object mSceneWithHeaders;
-    Object mSceneWithoutHeaders;
-    private Object mSceneAfterEntranceTransition;
-    Object mHeadersTransition;
-    BackStackListener mBackStackChangedListener;
-    BrowseTransitionListener mBrowseTransitionListener;
-
-    private static final String ARG_TITLE = BrowseSupportFragment.class.getCanonicalName() + ".title";
-    private static final String ARG_HEADERS_STATE =
-        BrowseSupportFragment.class.getCanonicalName() + ".headersState";
-
-    /**
-     * Creates arguments for a browse fragment.
-     *
-     * @param args The Bundle to place arguments into, or null if the method
-     *        should return a new Bundle.
-     * @param title The title of the BrowseSupportFragment.
-     * @param headersState The initial state of the headers of the
-     *        BrowseSupportFragment. Must be one of {@link #HEADERS_ENABLED}, {@link
-     *        #HEADERS_HIDDEN}, or {@link #HEADERS_DISABLED}.
-     * @return A Bundle with the given arguments for creating a BrowseSupportFragment.
-     */
-    public static Bundle createArgs(Bundle args, String title, int headersState) {
-        if (args == null) {
-            args = new Bundle();
-        }
-        args.putString(ARG_TITLE, title);
-        args.putInt(ARG_HEADERS_STATE, headersState);
-        return args;
-    }
-
-    /**
-     * Sets the brand color for the browse fragment. The brand color is used as
-     * the primary color for UI elements in the browse fragment. For example,
-     * the background color of the headers fragment uses the brand color.
-     *
-     * @param color The color to use as the brand color of the fragment.
-     */
-    public void setBrandColor(@ColorInt int color) {
-        mBrandColor = color;
-        mBrandColorSet = true;
-
-        if (mHeadersSupportFragment != null) {
-            mHeadersSupportFragment.setBackgroundColor(mBrandColor);
-        }
-    }
-
-    /**
-     * Returns the brand color for the browse fragment.
-     * The default is transparent.
-     */
-    @ColorInt
-    public int getBrandColor() {
-        return mBrandColor;
-    }
-
-    /**
-     * Wrapping app provided PresenterSelector to support InvisibleRowPresenter for SectionRow
-     * DividerRow and PageRow.
-     */
-    private void updateWrapperPresenter() {
-        if (mAdapter == null) {
-            mAdapterPresenter = null;
-            return;
-        }
-        final PresenterSelector adapterPresenter = mAdapter.getPresenterSelector();
-        if (adapterPresenter == null) {
-            throw new IllegalArgumentException("Adapter.getPresenterSelector() is null");
-        }
-        if (adapterPresenter == mAdapterPresenter) {
-            return;
-        }
-        mAdapterPresenter = adapterPresenter;
-
-        Presenter[] presenters = adapterPresenter.getPresenters();
-        final Presenter invisibleRowPresenter = new InvisibleRowPresenter();
-        final Presenter[] allPresenters = new Presenter[presenters.length + 1];
-        System.arraycopy(allPresenters, 0, presenters, 0, presenters.length);
-        allPresenters[allPresenters.length - 1] = invisibleRowPresenter;
-        mAdapter.setPresenterSelector(new PresenterSelector() {
-            @Override
-            public Presenter getPresenter(Object item) {
-                Row row = (Row) item;
-                if (row.isRenderedAsRowView()) {
-                    return adapterPresenter.getPresenter(item);
-                } else {
-                    return invisibleRowPresenter;
-                }
-            }
-
-            @Override
-            public Presenter[] getPresenters() {
-                return allPresenters;
-            }
-        });
-    }
-
-    /**
-     * Sets the adapter containing the rows for the fragment.
-     *
-     * <p>The items referenced by the adapter must be be derived from
-     * {@link Row}. These rows will be used by the rows fragment and the headers
-     * fragment (if not disabled) to render the browse rows.
-     *
-     * @param adapter An ObjectAdapter for the browse rows. All items must
-     *        derive from {@link Row}.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        updateWrapperPresenter();
-        if (getView() == null) {
-            return;
-        }
-
-        updateMainFragmentRowsAdapter();
-        mHeadersSupportFragment.setAdapter(mAdapter);
-    }
-
-    void setMainFragmentRowsAdapter(MainFragmentRowsAdapter mainFragmentRowsAdapter) {
-        if (mainFragmentRowsAdapter == mMainFragmentRowsAdapter) {
-            return;
-        }
-        // first clear previous mMainFragmentRowsAdapter and set a new mMainFragmentRowsAdapter
-        if (mMainFragmentRowsAdapter != null) {
-            // RowsFragment cannot change click/select listeners after view created.
-            // The main fragment and adapter should be GCed as long as there is no reference from
-            // BrowseSupportFragment to it.
-            mMainFragmentRowsAdapter.setAdapter(null);
-        }
-        mMainFragmentRowsAdapter = mainFragmentRowsAdapter;
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setOnItemViewSelectedListener(
-                    new MainFragmentItemViewSelectedListener(mMainFragmentRowsAdapter));
-            mMainFragmentRowsAdapter.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-        // second update mMainFragmentListRowDataAdapter set on mMainFragmentRowsAdapter
-        updateMainFragmentRowsAdapter();
-    }
-
-    /**
-     * Update mMainFragmentListRowDataAdapter and set it on mMainFragmentRowsAdapter.
-     * It also clears old mMainFragmentListRowDataAdapter.
-     */
-    void updateMainFragmentRowsAdapter() {
-        if (mMainFragmentListRowDataAdapter != null) {
-            mMainFragmentListRowDataAdapter.detach();
-            mMainFragmentListRowDataAdapter = null;
-        }
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentListRowDataAdapter = mAdapter == null
-                    ? null : new ListRowDataAdapter(mAdapter);
-            mMainFragmentRowsAdapter.setAdapter(mMainFragmentListRowDataAdapter);
-        }
-    }
-
-    public final MainFragmentAdapterRegistry getMainFragmentRegistry() {
-        return mMainFragmentAdapterRegistry;
-    }
-
-    /**
-     * Returns the adapter containing the rows for the fragment.
-     */
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mExternalOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Returns an item selection listener.
-     */
-    public OnItemViewSelectedListener getOnItemViewSelectedListener() {
-        return mExternalOnItemViewSelectedListener;
-    }
-
-    /**
-     * Get RowsSupportFragment if it's bound to BrowseSupportFragment or null if either BrowseSupportFragment has
-     * not been created yet or a different fragment is bound to it.
-     *
-     * @return RowsSupportFragment if it's bound to BrowseSupportFragment or null otherwise.
-     */
-    public RowsSupportFragment getRowsSupportFragment() {
-        if (mMainFragment instanceof RowsSupportFragment) {
-            return (RowsSupportFragment) mMainFragment;
-        }
-
-        return null;
-    }
-
-    /**
-     * @return Current main fragment or null if not created.
-     */
-    public Fragment getMainFragment() {
-        return mMainFragment;
-    }
-
-    /**
-     * Get currently bound HeadersSupportFragment or null if HeadersSupportFragment has not been created yet.
-     * @return Currently bound HeadersSupportFragment or null if HeadersSupportFragment has not been created yet.
-     */
-    public HeadersSupportFragment getHeadersSupportFragment() {
-        return mHeadersSupportFragment;
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
-     * OnItemViewClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general, developer should choose one of the listeners but not both.
-     */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setOnItemViewClickedListener(listener);
-        }
-    }
-
-    /**
-     * Returns the item Clicked listener.
-     */
-    public OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    /**
-     * Starts a headers transition.
-     *
-     * <p>This method will begin a transition to either show or hide the
-     * headers, depending on the value of withHeaders. If headers are disabled
-     * for this browse fragment, this method will throw an exception.
-     *
-     * @param withHeaders True if the headers should transition to being shown,
-     *        false if the transition should result in headers being hidden.
-     */
-    public void startHeadersTransition(boolean withHeaders) {
-        if (!mCanShowHeaders) {
-            throw new IllegalStateException("Cannot start headers transition");
-        }
-        if (isInHeadersTransition() || mShowingHeaders == withHeaders) {
-            return;
-        }
-        startHeadersTransitionInternal(withHeaders);
-    }
-
-    /**
-     * Returns true if the headers transition is currently running.
-     */
-    public boolean isInHeadersTransition() {
-        return mHeadersTransition != null;
-    }
-
-    /**
-     * Returns true if headers are shown.
-     */
-    public boolean isShowingHeaders() {
-        return mShowingHeaders;
-    }
-
-    /**
-     * Sets a listener for browse fragment transitions.
-     *
-     * @param listener The listener to call when a browse headers transition
-     *        begins or ends.
-     */
-    public void setBrowseTransitionListener(BrowseTransitionListener listener) {
-        mBrowseTransitionListener = listener;
-    }
-
-    /**
-     * @deprecated use {@link BrowseSupportFragment#enableMainFragmentScaling(boolean)} instead.
-     *
-     * @param enable true to enable row scaling
-     */
-    @Deprecated
-    public void enableRowScaling(boolean enable) {
-        enableMainFragmentScaling(enable);
-    }
-
-    /**
-     * Enables scaling of main fragment when headers are present. For the page/row fragment,
-     * scaling is enabled only when both this method and
-     * {@link MainFragmentAdapter#isScalingEnabled()} are enabled.
-     *
-     * @param enable true to enable row scaling
-     */
-    public void enableMainFragmentScaling(boolean enable) {
-        mMainFragmentScaleEnabled = enable;
-    }
-
-    void startHeadersTransitionInternal(final boolean withHeaders) {
-        if (getFragmentManager().isDestroyed()) {
-            return;
-        }
-        if (!isHeadersDataReady()) {
-            return;
-        }
-        mShowingHeaders = withHeaders;
-        mMainFragmentAdapter.onTransitionPrepare();
-        mMainFragmentAdapter.onTransitionStart();
-        onExpandTransitionStart(!withHeaders, new Runnable() {
-            @Override
-            public void run() {
-                mHeadersSupportFragment.onTransitionPrepare();
-                mHeadersSupportFragment.onTransitionStart();
-                createHeadersTransition();
-                if (mBrowseTransitionListener != null) {
-                    mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
-                }
-                TransitionHelper.runTransition(
-                        withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders, mHeadersTransition);
-                if (mHeadersBackStackEnabled) {
-                    if (!withHeaders) {
-                        getFragmentManager().beginTransaction()
-                                .addToBackStack(mWithHeadersBackStackName).commit();
-                    } else {
-                        int index = mBackStackChangedListener.mIndexOfHeadersBackStack;
-                        if (index >= 0) {
-                            BackStackEntry entry = getFragmentManager().getBackStackEntryAt(index);
-                            getFragmentManager().popBackStackImmediate(entry.getId(),
-                                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
-                        }
-                    }
-                }
-            }
-        });
-    }
-
-    boolean isVerticalScrolling() {
-        // don't run transition
-        return mHeadersSupportFragment.isScrolling() || mMainFragmentAdapter.isScrolling();
-    }
-
-
-    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
-            new BrowseFrameLayout.OnFocusSearchListener() {
-        @Override
-        public View onFocusSearch(View focused, int direction) {
-            // if headers is running transition,  focus stays
-            if (mCanShowHeaders && isInHeadersTransition()) {
-                return focused;
-            }
-            if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
-
-            if (getTitleView() != null && focused != getTitleView()
-                    && direction == View.FOCUS_UP) {
-                return getTitleView();
-            }
-            if (getTitleView() != null && getTitleView().hasFocus()
-                    && direction == View.FOCUS_DOWN) {
-                return mCanShowHeaders && mShowingHeaders
-                        ? mHeadersSupportFragment.getVerticalGridView() : mMainFragment.getView();
-            }
-
-            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) {
-                if (isVerticalScrolling() || mShowingHeaders || !isHeadersDataReady()) {
-                    return focused;
-                }
-                return mHeadersSupportFragment.getVerticalGridView();
-            } else if (direction == towardEnd) {
-                if (isVerticalScrolling()) {
-                    return focused;
-                } else if (mMainFragment != null && mMainFragment.getView() != null) {
-                    return mMainFragment.getView();
-                }
-                return focused;
-            } else if (direction == View.FOCUS_DOWN && mShowingHeaders) {
-                // disable focus_down moving into PageFragment.
-                return focused;
-            } else {
-                return null;
-            }
-        }
-    };
-
-    final boolean isHeadersDataReady() {
-        return mAdapter != null && mAdapter.size() != 0;
-    }
-
-    private final BrowseFrameLayout.OnChildFocusListener mOnChildFocusListener =
-            new BrowseFrameLayout.OnChildFocusListener() {
-
-        @Override
-        public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-            if (getChildFragmentManager().isDestroyed()) {
-                return true;
-            }
-            // Make sure not changing focus when requestFocus() is called.
-            if (mCanShowHeaders && mShowingHeaders) {
-                if (mHeadersSupportFragment != null && mHeadersSupportFragment.getView() != null
-                        && mHeadersSupportFragment.getView().requestFocus(
-                                direction, previouslyFocusedRect)) {
-                    return true;
-                }
-            }
-            if (mMainFragment != null && mMainFragment.getView() != null
-                    && mMainFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
-                return true;
-            }
-            return getTitleView() != null
-                    && getTitleView().requestFocus(direction, previouslyFocusedRect);
-        }
-
-        @Override
-        public void onRequestChildFocus(View child, View focused) {
-            if (getChildFragmentManager().isDestroyed()) {
-                return;
-            }
-            if (!mCanShowHeaders || isInHeadersTransition()) return;
-            int childId = child.getId();
-            if (childId == R.id.browse_container_dock && mShowingHeaders) {
-                startHeadersTransitionInternal(false);
-            } else if (childId == R.id.browse_headers_dock && !mShowingHeaders) {
-                startHeadersTransitionInternal(true);
-            }
-        }
-    };
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(CURRENT_SELECTED_POSITION, mSelectedPosition);
-        outState.putBoolean(IS_PAGE_ROW, mIsPageRow);
-
-        if (mBackStackChangedListener != null) {
-            mBackStackChangedListener.save(outState);
-        } else {
-            outState.putBoolean(HEADER_SHOW, mShowingHeaders);
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final Context context = getContext();
-        TypedArray ta = context.obtainStyledAttributes(R.styleable.LeanbackTheme);
-        mContainerListMarginStart = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginStart, context.getResources()
-                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_start));
-        mContainerListAlignTop = (int) ta.getDimension(
-                R.styleable.LeanbackTheme_browseRowsMarginTop, context.getResources()
-                .getDimensionPixelSize(R.dimen.lb_browse_rows_margin_top));
-        ta.recycle();
-
-        readArguments(getArguments());
-
-        if (mCanShowHeaders) {
-            if (mHeadersBackStackEnabled) {
-                mWithHeadersBackStackName = LB_HEADERS_BACKSTACK + this;
-                mBackStackChangedListener = new BackStackListener();
-                getFragmentManager().addOnBackStackChangedListener(mBackStackChangedListener);
-                mBackStackChangedListener.load(savedInstanceState);
-            } else {
-                if (savedInstanceState != null) {
-                    mShowingHeaders = savedInstanceState.getBoolean(HEADER_SHOW);
-                }
-            }
-        }
-
-        mScaleFactor = getResources().getFraction(R.fraction.lb_browse_rows_scale, 1, 1);
-    }
-
-    @Override
-    public void onDestroyView() {
-        setMainFragmentRowsAdapter(null);
-        mPageRow = null;
-        mMainFragmentAdapter = null;
-        mMainFragment = null;
-        mHeadersSupportFragment = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mBackStackChangedListener != null) {
-            getFragmentManager().removeOnBackStackChangedListener(mBackStackChangedListener);
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * Creates a new {@link HeadersSupportFragment} instance. Subclass of BrowseSupportFragment may override and
-     * return an instance of subclass of HeadersSupportFragment, e.g. when app wants to replace presenter
-     * to render HeaderItem.
-     *
-     * @return A new instance of {@link HeadersSupportFragment} or its subclass.
-     */
-    public HeadersSupportFragment onCreateHeadersSupportFragment() {
-        return new HeadersSupportFragment();
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-
-        if (getChildFragmentManager().findFragmentById(R.id.scale_frame) == null) {
-            mHeadersSupportFragment = onCreateHeadersSupportFragment();
-
-            createMainFragment(mAdapter, mSelectedPosition);
-            FragmentTransaction ft = getChildFragmentManager().beginTransaction()
-                    .replace(R.id.browse_headers_dock, mHeadersSupportFragment);
-
-            if (mMainFragment != null) {
-                ft.replace(R.id.scale_frame, mMainFragment);
-            } else {
-                // Empty adapter used to guard against lazy adapter loading. When this
-                // fragment is instantiated, mAdapter might not have the data or might not
-                // have been set. In either of those cases mFragmentAdapter will be null.
-                // This way we can maintain the invariant that mMainFragmentAdapter is never
-                // null and it avoids doing null checks all over the code.
-                mMainFragmentAdapter = new MainFragmentAdapter(null);
-                mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
-            }
-
-            ft.commit();
-        } else {
-            mHeadersSupportFragment = (HeadersSupportFragment) getChildFragmentManager()
-                    .findFragmentById(R.id.browse_headers_dock);
-            mMainFragment = getChildFragmentManager().findFragmentById(R.id.scale_frame);
-
-            mIsPageRow = savedInstanceState != null
-                    && savedInstanceState.getBoolean(IS_PAGE_ROW, false);
-            // mPageRow object is unable to restore, if its null and mIsPageRow is true, this is
-            // the case for restoring, later if setSelection() triggers a createMainFragment(),
-            // should not create fragment.
-
-            mSelectedPosition = savedInstanceState != null
-                    ? savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
-
-            setMainFragmentAdapter();
-        }
-
-        mHeadersSupportFragment.setHeadersGone(!mCanShowHeaders);
-        if (mHeaderPresenterSelector != null) {
-            mHeadersSupportFragment.setPresenterSelector(mHeaderPresenterSelector);
-        }
-        mHeadersSupportFragment.setAdapter(mAdapter);
-        mHeadersSupportFragment.setOnHeaderViewSelectedListener(mHeaderViewSelectedListener);
-        mHeadersSupportFragment.setOnHeaderClickedListener(mHeaderClickedListener);
-
-        View root = inflater.inflate(R.layout.lb_browse_fragment, container, false);
-
-        getProgressBarManager().setRootView((ViewGroup)root);
-
-        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
-        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
-        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
-
-        installTitleView(inflater, mBrowseFrame, savedInstanceState);
-
-        mScaleFrameLayout = (ScaleFrameLayout) root.findViewById(R.id.scale_frame);
-        mScaleFrameLayout.setPivotX(0);
-        mScaleFrameLayout.setPivotY(mContainerListAlignTop);
-
-        if (mBrandColorSet) {
-            mHeadersSupportFragment.setBackgroundColor(mBrandColor);
-        }
-
-        mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                showHeaders(true);
-            }
-        });
-        mSceneWithoutHeaders =  TransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                showHeaders(false);
-            }
-        });
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
-            @Override
-            public void run() {
-                setEntranceTransitionEndState();
-            }
-        });
-
-        return root;
-    }
-
-    void createHeadersTransition() {
-        mHeadersTransition = TransitionHelper.loadTransition(getContext(),
-                mShowingHeaders
-                        ? R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
-
-        TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
-            @Override
-            public void onTransitionStart(Object transition) {
-            }
-            @Override
-            public void onTransitionEnd(Object transition) {
-                mHeadersTransition = null;
-                if (mMainFragmentAdapter != null) {
-                    mMainFragmentAdapter.onTransitionEnd();
-                    if (!mShowingHeaders && mMainFragment != null) {
-                        View mainFragmentView = mMainFragment.getView();
-                        if (mainFragmentView != null && !mainFragmentView.hasFocus()) {
-                            mainFragmentView.requestFocus();
-                        }
-                    }
-                }
-                if (mHeadersSupportFragment != null) {
-                    mHeadersSupportFragment.onTransitionEnd();
-                    if (mShowingHeaders) {
-                        VerticalGridView headerGridView = mHeadersSupportFragment.getVerticalGridView();
-                        if (headerGridView != null && !headerGridView.hasFocus()) {
-                            headerGridView.requestFocus();
-                        }
-                    }
-                }
-
-                // Animate TitleView once header animation is complete.
-                updateTitleViewVisibility();
-
-                if (mBrowseTransitionListener != null) {
-                    mBrowseTransitionListener.onHeadersTransitionStop(mShowingHeaders);
-                }
-            }
-        });
-    }
-
-    void updateTitleViewVisibility() {
-        if (!mShowingHeaders) {
-            boolean showTitleView;
-            if (mIsPageRow && mMainFragmentAdapter != null) {
-                // page fragment case:
-                showTitleView = mMainFragmentAdapter.mFragmentHost.mShowTitleView;
-            } else {
-                // regular row view case:
-                showTitleView = isFirstRowWithContent(mSelectedPosition);
-            }
-            if (showTitleView) {
-                showTitle(TitleViewAdapter.FULL_VIEW_VISIBLE);
-            } else {
-                showTitle(false);
-            }
-        } else {
-            // when HeaderFragment is showing,  showBranding and showSearch are slightly different
-            boolean showBranding;
-            boolean showSearch;
-            if (mIsPageRow && mMainFragmentAdapter != null) {
-                showBranding = mMainFragmentAdapter.mFragmentHost.mShowTitleView;
-            } else {
-                showBranding = isFirstRowWithContent(mSelectedPosition);
-            }
-            showSearch = isFirstRowWithContentOrPageRow(mSelectedPosition);
-            int flags = 0;
-            if (showBranding) flags |= TitleViewAdapter.BRANDING_VIEW_VISIBLE;
-            if (showSearch) flags |= TitleViewAdapter.SEARCH_VIEW_VISIBLE;
-            if (flags != 0) {
-                showTitle(flags);
-            } else {
-                showTitle(false);
-            }
-        }
-    }
-
-    boolean isFirstRowWithContentOrPageRow(int rowPosition) {
-        if (mAdapter == null || mAdapter.size() == 0) {
-            return true;
-        }
-        for (int i = 0; i < mAdapter.size(); i++) {
-            final Row row = (Row) mAdapter.get(i);
-            if (row.isRenderedAsRowView() || row instanceof PageRow) {
-                return rowPosition == i;
-            }
-        }
-        return true;
-    }
-
-    boolean isFirstRowWithContent(int rowPosition) {
-        if (mAdapter == null || mAdapter.size() == 0) {
-            return true;
-        }
-        for (int i = 0; i < mAdapter.size(); i++) {
-            final Row row = (Row) mAdapter.get(i);
-            if (row.isRenderedAsRowView()) {
-                return rowPosition == i;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Sets the {@link PresenterSelector} used to render the row headers.
-     *
-     * @param headerPresenterSelector The PresenterSelector that will determine
-     *        the Presenter for each row header.
-     */
-    public void setHeaderPresenterSelector(PresenterSelector headerPresenterSelector) {
-        mHeaderPresenterSelector = headerPresenterSelector;
-        if (mHeadersSupportFragment != null) {
-            mHeadersSupportFragment.setPresenterSelector(mHeaderPresenterSelector);
-        }
-    }
-
-    private void setHeadersOnScreen(boolean onScreen) {
-        MarginLayoutParams lp;
-        View containerList;
-        containerList = mHeadersSupportFragment.getView();
-        lp = (MarginLayoutParams) containerList.getLayoutParams();
-        lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
-        containerList.setLayoutParams(lp);
-    }
-
-    void showHeaders(boolean show) {
-        if (DEBUG) Log.v(TAG, "showHeaders " + show);
-        mHeadersSupportFragment.setHeadersEnabled(show);
-        setHeadersOnScreen(show);
-        expandMainFragment(!show);
-    }
-
-    private void expandMainFragment(boolean expand) {
-        MarginLayoutParams params = (MarginLayoutParams) mScaleFrameLayout.getLayoutParams();
-        params.setMarginStart(!expand ? mContainerListMarginStart : 0);
-        mScaleFrameLayout.setLayoutParams(params);
-        mMainFragmentAdapter.setExpand(expand);
-
-        setMainFragmentAlignment();
-        final float scaleFactor = !expand
-                && mMainFragmentScaleEnabled
-                && mMainFragmentAdapter.isScalingEnabled() ? mScaleFactor : 1;
-        mScaleFrameLayout.setLayoutScaleY(scaleFactor);
-        mScaleFrameLayout.setChildScale(scaleFactor);
-    }
-
-    private HeadersSupportFragment.OnHeaderClickedListener mHeaderClickedListener =
-        new HeadersSupportFragment.OnHeaderClickedListener() {
-            @Override
-            public void onHeaderClicked(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
-                if (!mCanShowHeaders || !mShowingHeaders || isInHeadersTransition()) {
-                    return;
-                }
-                if (mMainFragment == null || mMainFragment.getView() == null) {
-                    return;
-                }
-                startHeadersTransitionInternal(false);
-                mMainFragment.getView().requestFocus();
-            }
-        };
-
-    class MainFragmentItemViewSelectedListener implements OnItemViewSelectedListener {
-        MainFragmentRowsAdapter mMainFragmentRowsAdapter;
-
-        public MainFragmentItemViewSelectedListener(MainFragmentRowsAdapter fragmentRowsAdapter) {
-            mMainFragmentRowsAdapter = fragmentRowsAdapter;
-        }
-
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                RowPresenter.ViewHolder rowViewHolder, Row row) {
-            int position = mMainFragmentRowsAdapter.getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "row selected position " + position);
-            onRowSelected(position);
-            if (mExternalOnItemViewSelectedListener != null) {
-                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                        rowViewHolder, row);
-            }
-        }
-    };
-
-    private HeadersSupportFragment.OnHeaderViewSelectedListener mHeaderViewSelectedListener =
-            new HeadersSupportFragment.OnHeaderViewSelectedListener() {
-        @Override
-        public void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
-            int position = mHeadersSupportFragment.getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "header selected position " + position);
-            onRowSelected(position);
-        }
-    };
-
-    void onRowSelected(int position) {
-        // even position is same, it could be data changed, always post selection runnable
-        // to possibly swap main fragment.
-        mSetSelectionRunnable.post(
-                position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
-    }
-
-    void setSelection(int position, boolean smooth) {
-        if (position == NO_POSITION) {
-            return;
-        }
-
-        mSelectedPosition = position;
-        if (mHeadersSupportFragment == null || mMainFragmentAdapter == null) {
-            // onDestroyView() called
-            return;
-        }
-        mHeadersSupportFragment.setSelectedPosition(position, smooth);
-        replaceMainFragment(position);
-
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setSelectedPosition(position, smooth);
-        }
-
-        updateTitleViewVisibility();
-    }
-
-    private void replaceMainFragment(int position) {
-        if (createMainFragment(mAdapter, position)) {
-            swapToMainFragment();
-            expandMainFragment(!(mCanShowHeaders && mShowingHeaders));
-        }
-    }
-
-    private void swapToMainFragment() {
-        final VerticalGridView gridView = mHeadersSupportFragment.getVerticalGridView();
-        if (isShowingHeaders() && gridView != null
-                && gridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            // if user is scrolling HeadersSupportFragment,  swap to empty fragment and wait scrolling
-            // finishes.
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.scale_frame, new Fragment()).commit();
-            gridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-                @SuppressWarnings("ReferenceEquality")
-                @Override
-                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
-                        gridView.removeOnScrollListener(this);
-                        FragmentManager fm = getChildFragmentManager();
-                        Fragment currentFragment = fm.findFragmentById(R.id.scale_frame);
-                        if (currentFragment != mMainFragment) {
-                            fm.beginTransaction().replace(R.id.scale_frame, mMainFragment).commit();
-                        }
-                    }
-                }
-            });
-        } else {
-            // Otherwise swap immediately
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.scale_frame, mMainFragment).commit();
-        }
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Gets position of currently selected row.
-     * @return Position of currently selected row.
-     */
-    public int getSelectedPosition() {
-        return mSelectedPosition;
-    }
-
-    /**
-     * @return selected row ViewHolder inside fragment created by {@link MainFragmentRowsAdapter}.
-     */
-    public RowPresenter.ViewHolder getSelectedRowViewHolder() {
-        if (mMainFragmentRowsAdapter != null) {
-            int rowPos = mMainFragmentRowsAdapter.getSelectedPosition();
-            return mMainFragmentRowsAdapter.findRowViewHolderByPosition(rowPos);
-        }
-        return null;
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.post(
-                position, SetSelectionRunnable.TYPE_USER_REQUEST, smooth);
-    }
-
-    /**
-     * Selects a Row and perform an optional task on the Row. For example
-     * <code>setSelectedPosition(10, true, new ListRowPresenterSelectItemViewHolderTask(5))</code>
-     * scrolls to 11th row and selects 6th item on that row.  The method will be ignored if
-     * RowsSupportFragment has not been created (i.e. before {@link #onCreateView(LayoutInflater,
-     * ViewGroup, Bundle)}).
-     *
-     * @param rowPosition Which row to select.
-     * @param smooth True to scroll to the row, false for no animation.
-     * @param rowHolderTask Optional task to perform on the Row.  When the task is not null, headers
-     * fragment will be collapsed.
-     */
-    public void setSelectedPosition(int rowPosition, boolean smooth,
-            final Presenter.ViewHolderTask rowHolderTask) {
-        if (mMainFragmentAdapterRegistry == null) {
-            return;
-        }
-        if (rowHolderTask != null) {
-            startHeadersTransition(false);
-        }
-        if (mMainFragmentRowsAdapter != null) {
-            mMainFragmentRowsAdapter.setSelectedPosition(rowPosition, smooth, rowHolderTask);
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mHeadersSupportFragment.setAlignment(mContainerListAlignTop);
-        setMainFragmentAlignment();
-
-        if (mCanShowHeaders && mShowingHeaders && mHeadersSupportFragment != null
-                && mHeadersSupportFragment.getView() != null) {
-            mHeadersSupportFragment.getView().requestFocus();
-        } else if ((!mCanShowHeaders || !mShowingHeaders) && mMainFragment != null
-                && mMainFragment.getView() != null) {
-            mMainFragment.getView().requestFocus();
-        }
-
-        if (mCanShowHeaders) {
-            showHeaders(mShowingHeaders);
-        }
-
-        mStateMachine.fireEvent(EVT_HEADER_VIEW_CREATED);
-    }
-
-    private void onExpandTransitionStart(boolean expand, final Runnable callback) {
-        if (expand) {
-            callback.run();
-            return;
-        }
-        // Run a "pre" layout when we go non-expand, in order to get the initial
-        // positions of added rows.
-        new ExpandPreLayout(callback, mMainFragmentAdapter, getView()).execute();
-    }
-
-    private void setMainFragmentAlignment() {
-        int alignOffset = mContainerListAlignTop;
-        if (mMainFragmentScaleEnabled
-                && mMainFragmentAdapter.isScalingEnabled()
-                && mShowingHeaders) {
-            alignOffset = (int) (alignOffset / mScaleFactor + 0.5f);
-        }
-        mMainFragmentAdapter.setAlignment(alignOffset);
-    }
-
-    /**
-     * Enables/disables headers transition on back key support. This is enabled by
-     * default. The BrowseSupportFragment will add a back stack entry when headers are
-     * showing. Running a headers transition when the back key is pressed only
-     * works when the headers state is {@link #HEADERS_ENABLED} or
-     * {@link #HEADERS_HIDDEN}.
-     * <p>
-     * NOTE: If an Activity has its own onBackPressed() handling, you must
-     * disable this feature. You may use {@link #startHeadersTransition(boolean)}
-     * and {@link BrowseTransitionListener} in your own back stack handling.
-     */
-    public final void setHeadersTransitionOnBackEnabled(boolean headersBackStackEnabled) {
-        mHeadersBackStackEnabled = headersBackStackEnabled;
-    }
-
-    /**
-     * Returns true if headers transition on back key support is enabled.
-     */
-    public final boolean isHeadersTransitionOnBackEnabled() {
-        return mHeadersBackStackEnabled;
-    }
-
-    private void readArguments(Bundle args) {
-        if (args == null) {
-            return;
-        }
-        if (args.containsKey(ARG_TITLE)) {
-            setTitle(args.getString(ARG_TITLE));
-        }
-        if (args.containsKey(ARG_HEADERS_STATE)) {
-            setHeadersState(args.getInt(ARG_HEADERS_STATE));
-        }
-    }
-
-    /**
-     * Sets the state for the headers column in the browse fragment. Must be one
-     * of {@link #HEADERS_ENABLED}, {@link #HEADERS_HIDDEN}, or
-     * {@link #HEADERS_DISABLED}.
-     *
-     * @param headersState The state of the headers for the browse fragment.
-     */
-    public void setHeadersState(int headersState) {
-        if (headersState < HEADERS_ENABLED || headersState > HEADERS_DISABLED) {
-            throw new IllegalArgumentException("Invalid headers state: " + headersState);
-        }
-        if (DEBUG) Log.v(TAG, "setHeadersState " + headersState);
-
-        if (headersState != mHeadersState) {
-            mHeadersState = headersState;
-            switch (headersState) {
-                case HEADERS_ENABLED:
-                    mCanShowHeaders = true;
-                    mShowingHeaders = true;
-                    break;
-                case HEADERS_HIDDEN:
-                    mCanShowHeaders = true;
-                    mShowingHeaders = false;
-                    break;
-                case HEADERS_DISABLED:
-                    mCanShowHeaders = false;
-                    mShowingHeaders = false;
-                    break;
-                default:
-                    Log.w(TAG, "Unknown headers state: " + headersState);
-                    break;
-            }
-            if (mHeadersSupportFragment != null) {
-                mHeadersSupportFragment.setHeadersGone(!mCanShowHeaders);
-            }
-        }
-    }
-
-    /**
-     * Returns the state of the headers column in the browse fragment.
-     */
-    public int getHeadersState() {
-        return mHeadersState;
-    }
-
-    @Override
-    protected Object createEntranceTransition() {
-        return TransitionHelper.loadTransition(getContext(),
-                R.transition.lb_browse_entrance_transition);
-    }
-
-    @Override
-    protected void runEntranceTransition(Object entranceTransition) {
-        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
-    }
-
-    @Override
-    protected void onEntranceTransitionPrepare() {
-        mHeadersSupportFragment.onTransitionPrepare();
-        mMainFragmentAdapter.setEntranceTransitionState(false);
-        mMainFragmentAdapter.onTransitionPrepare();
-    }
-
-    @Override
-    protected void onEntranceTransitionStart() {
-        mHeadersSupportFragment.onTransitionStart();
-        mMainFragmentAdapter.onTransitionStart();
-    }
-
-    @Override
-    protected void onEntranceTransitionEnd() {
-        if (mMainFragmentAdapter != null) {
-            mMainFragmentAdapter.onTransitionEnd();
-        }
-
-        if (mHeadersSupportFragment != null) {
-            mHeadersSupportFragment.onTransitionEnd();
-        }
-    }
-
-    void setSearchOrbViewOnScreen(boolean onScreen) {
-        View searchOrbView = getTitleViewAdapter().getSearchAffordanceView();
-        if (searchOrbView != null) {
-            MarginLayoutParams lp = (MarginLayoutParams) searchOrbView.getLayoutParams();
-            lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
-            searchOrbView.setLayoutParams(lp);
-        }
-    }
-
-    void setEntranceTransitionStartState() {
-        setHeadersOnScreen(false);
-        setSearchOrbViewOnScreen(false);
-        // NOTE that mMainFragmentAdapter.setEntranceTransitionState(false) will be called
-        // in onEntranceTransitionPrepare() because mMainFragmentAdapter is still the dummy
-        // one when setEntranceTransitionStartState() is called.
-    }
-
-    void setEntranceTransitionEndState() {
-        setHeadersOnScreen(mShowingHeaders);
-        setSearchOrbViewOnScreen(true);
-        mMainFragmentAdapter.setEntranceTransitionState(true);
-    }
-
-    private class ExpandPreLayout implements ViewTreeObserver.OnPreDrawListener {
-
-        private final View mView;
-        private final Runnable mCallback;
-        private int mState;
-        private MainFragmentAdapter mainFragmentAdapter;
-
-        final static int STATE_INIT = 0;
-        final static int STATE_FIRST_DRAW = 1;
-        final static int STATE_SECOND_DRAW = 2;
-
-        ExpandPreLayout(Runnable callback, MainFragmentAdapter adapter, View view) {
-            mView = view;
-            mCallback = callback;
-            mainFragmentAdapter = adapter;
-        }
-
-        void execute() {
-            mView.getViewTreeObserver().addOnPreDrawListener(this);
-            mainFragmentAdapter.setExpand(false);
-            // always trigger onPreDraw even adapter setExpand() does nothing.
-            mView.invalidate();
-            mState = STATE_INIT;
-        }
-
-        @Override
-        public boolean onPreDraw() {
-            if (getView() == null || getContext() == null) {
-                mView.getViewTreeObserver().removeOnPreDrawListener(this);
-                return true;
-            }
-            if (mState == STATE_INIT) {
-                mainFragmentAdapter.setExpand(true);
-                // always trigger onPreDraw even adapter setExpand() does nothing.
-                mView.invalidate();
-                mState = STATE_FIRST_DRAW;
-            } else if (mState == STATE_FIRST_DRAW) {
-                mCallback.run();
-                mView.getViewTreeObserver().removeOnPreDrawListener(this);
-                mState = STATE_SECOND_DRAW;
-            }
-            return false;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java b/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
deleted file mode 100644
index 48bf74c..0000000
--- a/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
+++ /dev/null
@@ -1,245 +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.v17.leanback.app;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.graphics.drawable.Drawable;
-import android.support.v17.leanback.media.PlaybackGlue;
-import android.support.v17.leanback.widget.DetailsParallax;
-import android.support.v17.leanback.widget.Parallax;
-import android.support.v17.leanback.widget.ParallaxEffect;
-import android.support.v17.leanback.widget.ParallaxTarget;
-
-/**
- * Helper class responsible for controlling video playback in {@link DetailsFragment}. This
- * takes {@link DetailsParallax}, {@link PlaybackGlue} and a drawable as input.
- * Video is played when {@link DetailsParallax#getOverviewRowTop()} moved bellow top edge of screen.
- * Video is stopped when {@link DetailsParallax#getOverviewRowTop()} reaches or scrolls above top
- * edge of screen. The drawable will change alpha to 0 when video is ready to play.
- * App does not directly use this class.
- * @see DetailsFragmentBackgroundController
- * @see DetailsSupportFragmentBackgroundController
- */
-final class DetailsBackgroundVideoHelper {
-    private static final long BACKGROUND_CROSS_FADE_DURATION = 500;
-    // Temporarily add CROSSFADE_DELAY waiting for video surface ready.
-    // We will remove this delay once PlaybackGlue have a callback for videoRenderingReady event.
-    private static final long CROSSFADE_DELAY = 1000;
-
-    /**
-     * Different states {@link DetailsFragment} can be in.
-     */
-    static final int INITIAL = 0;
-    static final int PLAY_VIDEO = 1;
-    static final int NO_VIDEO = 2;
-
-    private final DetailsParallax mDetailsParallax;
-    private ParallaxEffect mParallaxEffect;
-
-    private int mCurrentState = INITIAL;
-
-    private ValueAnimator mBackgroundAnimator;
-    private Drawable mBackgroundDrawable;
-    private PlaybackGlue mPlaybackGlue;
-    private boolean mBackgroundDrawableVisible;
-
-    /**
-     * Constructor to setup a Helper for controlling video playback in DetailsFragment.
-     * @param playbackGlue The PlaybackGlue used to control underlying player.
-     * @param detailsParallax The DetailsParallax to add special parallax effect to control video
-     *                        start/stop. Video is played when
-     *                        {@link DetailsParallax#getOverviewRowTop()} moved bellow top edge of
-     *                        screen. Video is stopped when
-     *                        {@link DetailsParallax#getOverviewRowTop()} reaches or scrolls above
-     *                        top edge of screen.
-     * @param backgroundDrawable The drawable will change alpha to 0 when video is ready to play.
-     */
-    DetailsBackgroundVideoHelper(
-            PlaybackGlue playbackGlue,
-            DetailsParallax detailsParallax,
-            Drawable backgroundDrawable) {
-        this.mPlaybackGlue = playbackGlue;
-        this.mDetailsParallax = detailsParallax;
-        this.mBackgroundDrawable = backgroundDrawable;
-        mBackgroundDrawableVisible = true;
-        mBackgroundDrawable.setAlpha(255);
-        startParallax();
-    }
-
-    void startParallax() {
-        if (mParallaxEffect != null) {
-            return;
-        }
-        Parallax.IntProperty frameTop = mDetailsParallax.getOverviewRowTop();
-        final float maxFrameTop = 1f;
-        final float minFrameTop = 0f;
-        mParallaxEffect = mDetailsParallax
-                .addEffect(frameTop.atFraction(maxFrameTop), frameTop.atFraction(minFrameTop))
-                .target(new ParallaxTarget() {
-                    @Override
-                    public void update(float fraction) {
-                        if (fraction == maxFrameTop) {
-                            updateState(NO_VIDEO);
-                        } else {
-                            updateState(PLAY_VIDEO);
-                        }
-                    }
-                });
-        // In case the VideoHelper is created after RecyclerView is created: perform initial
-        // parallax effect.
-        mDetailsParallax.updateValues();
-    }
-
-    void stopParallax() {
-        mDetailsParallax.removeEffect(mParallaxEffect);
-    }
-
-    boolean isVideoVisible() {
-        return mCurrentState == PLAY_VIDEO;
-    }
-
-    private void updateState(int state) {
-        if (state == mCurrentState) {
-            return;
-        }
-        mCurrentState = state;
-        applyState();
-    }
-
-    private void applyState() {
-        switch (mCurrentState) {
-            case PLAY_VIDEO:
-                if (mPlaybackGlue != null) {
-                    if (mPlaybackGlue.isPrepared()) {
-                        internalStartPlayback();
-                    } else {
-                        mPlaybackGlue.addPlayerCallback(mControlStateCallback);
-                    }
-                } else {
-                    crossFadeBackgroundToVideo(false);
-                }
-                break;
-            case NO_VIDEO:
-                crossFadeBackgroundToVideo(false);
-                if (mPlaybackGlue != null) {
-                    mPlaybackGlue.removePlayerCallback(mControlStateCallback);
-                    mPlaybackGlue.pause();
-                }
-                break;
-        }
-    }
-
-    void setPlaybackGlue(PlaybackGlue playbackGlue) {
-        if (mPlaybackGlue != null) {
-            mPlaybackGlue.removePlayerCallback(mControlStateCallback);
-        }
-        mPlaybackGlue = playbackGlue;
-        applyState();
-    }
-
-    private void internalStartPlayback() {
-        if (mPlaybackGlue != null) {
-            mPlaybackGlue.play();
-        }
-        mDetailsParallax.getRecyclerView().postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                crossFadeBackgroundToVideo(true);
-            }
-        }, CROSSFADE_DELAY);
-    }
-
-    void crossFadeBackgroundToVideo(boolean crossFadeToVideo) {
-        crossFadeBackgroundToVideo(crossFadeToVideo, false);
-    }
-
-    void crossFadeBackgroundToVideo(boolean crossFadeToVideo, boolean immediate) {
-        final boolean newVisible = !crossFadeToVideo;
-        if (mBackgroundDrawableVisible == newVisible) {
-            if (immediate) {
-                if (mBackgroundAnimator != null) {
-                    mBackgroundAnimator.cancel();
-                    mBackgroundAnimator = null;
-                }
-                if (mBackgroundDrawable != null) {
-                    mBackgroundDrawable.setAlpha(crossFadeToVideo ? 0 : 255);
-                    return;
-                }
-            }
-            return;
-        }
-        mBackgroundDrawableVisible = newVisible;
-        if (mBackgroundAnimator != null) {
-            mBackgroundAnimator.cancel();
-            mBackgroundAnimator = null;
-        }
-
-        float startAlpha = crossFadeToVideo ? 1f : 0f;
-        float endAlpha = crossFadeToVideo ? 0f : 1f;
-
-        if (mBackgroundDrawable == null) {
-            return;
-        }
-        if (immediate) {
-            mBackgroundDrawable.setAlpha(crossFadeToVideo ? 0 : 255);
-            return;
-        }
-        mBackgroundAnimator = ValueAnimator.ofFloat(startAlpha, endAlpha);
-        mBackgroundAnimator.setDuration(BACKGROUND_CROSS_FADE_DURATION);
-        mBackgroundAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                mBackgroundDrawable.setAlpha(
-                        (int) ((Float) (valueAnimator.getAnimatedValue()) * 255));
-            }
-        });
-
-        mBackgroundAnimator.addListener(new Animator.AnimatorListener() {
-            @Override
-            public void onAnimationStart(Animator animator) {
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animator) {
-                mBackgroundAnimator = null;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animator) {
-            }
-
-            @Override
-            public void onAnimationRepeat(Animator animator) {
-            }
-        });
-
-        mBackgroundAnimator.start();
-    }
-
-    private class PlaybackControlStateCallback extends PlaybackGlue.PlayerCallback {
-
-        @Override
-        public void onPreparedStateChanged(PlaybackGlue glue) {
-            if (glue.isPrepared()) {
-                internalStartPlayback();
-            }
-        }
-    }
-
-    PlaybackControlStateCallback mControlStateCallback = new PlaybackControlStateCallback();
-}
diff --git a/android/support/v17/leanback/app/DetailsFragment.java b/android/support/v17/leanback/app/DetailsFragment.java
deleted file mode 100644
index 18934f4..0000000
--- a/android/support/v17/leanback/app/DetailsFragment.java
+++ /dev/null
@@ -1,934 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from DetailsSupportFragment.java.  DO NOT MODIFY. */
-
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from DetailsFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.util.StateMachine.Event;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
-import android.support.v17.leanback.widget.BrowseFrameLayout;
-import android.support.v17.leanback.widget.DetailsParallax;
-import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
-import android.support.v17.leanback.widget.ItemAlignmentFacet;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-
-import java.lang.ref.WeakReference;
-
-/**
- * A fragment for creating Leanback details screens.
- *
- * <p>
- * A DetailsFragment renders the elements of its {@link ObjectAdapter} as a set
- * of rows in a vertical list.The Adapter's {@link PresenterSelector} must maintain subclasses
- * of {@link RowPresenter}.
- * </p>
- *
- * When {@link FullWidthDetailsOverviewRowPresenter} is found in adapter,  DetailsFragment will
- * setup default behavior of the DetailsOverviewRow:
- * <li>
- * The alignment of FullWidthDetailsOverviewRowPresenter is setup in
- * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}.
- * </li>
- * <li>
- * The view status switching of FullWidthDetailsOverviewRowPresenter is done in
- * {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
- * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)}.
- * </li>
- *
- * <p>
- * The recommended activity themes to use with a DetailsFragment are
- * <li>
- * {@link android.support.v17.leanback.R.style#Theme_Leanback_Details} with activity
- * shared element transition for {@link FullWidthDetailsOverviewRowPresenter}.
- * </li>
- * <li>
- * {@link android.support.v17.leanback.R.style#Theme_Leanback_Details_NoSharedElementTransition}
- * if shared element transition is not needed, for example if first row is not rendered by
- * {@link FullWidthDetailsOverviewRowPresenter}.
- * </li>
- * </p>
- *
- * <p>
- * DetailsFragment can use {@link DetailsFragmentBackgroundController} to add a parallax drawable
- * background and embedded video playing fragment.
- * </p>
- * @deprecated use {@link DetailsSupportFragment}
- */
-@Deprecated
-public class DetailsFragment extends BaseFragment {
-    static final String TAG = "DetailsFragment";
-    static boolean DEBUG = false;
-
-    final State STATE_SET_ENTRANCE_START_STATE = new State("STATE_SET_ENTRANCE_START_STATE") {
-        @Override
-        public void run() {
-            mRowsFragment.setEntranceTransitionState(false);
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_INIT = new State("STATE_ENTER_TRANSIITON_INIT");
-
-    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.switchToVideoBeforeCreate();
-        showTitle(false);
-        mPendingFocusOnVideo = true;
-        slideOutGridView();
-    }
-
-    final State STATE_SWITCH_TO_VIDEO_IN_ON_CREATE = new State("STATE_SWITCH_TO_VIDEO_IN_ON_CREATE",
-            false, false) {
-        @Override
-        public void run() {
-            switchToVideoBeforeVideoFragmentCreated();
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_CANCEL = new State("STATE_ENTER_TRANSITION_CANCEL",
-            false, false) {
-        @Override
-        public void run() {
-            if (mWaitEnterTransitionTimeout != null) {
-                mWaitEnterTransitionTimeout.mRef.clear();
-            }
-            // clear the activity enter/sharedElement transition, return transitions are kept.
-            // keep the return transitions and clear enter transition
-            if (getActivity() != null) {
-                Window window = getActivity().getWindow();
-                Object returnTransition = TransitionHelper.getReturnTransition(window);
-                Object sharedReturnTransition = TransitionHelper
-                        .getSharedElementReturnTransition(window);
-                TransitionHelper.setEnterTransition(window, null);
-                TransitionHelper.setSharedElementEnterTransition(window, null);
-                TransitionHelper.setReturnTransition(window, returnTransition);
-                TransitionHelper.setSharedElementReturnTransition(window, sharedReturnTransition);
-            }
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_COMPLETE = new State("STATE_ENTER_TRANSIITON_COMPLETE",
-            true, false);
-
-    final State STATE_ENTER_TRANSITION_ADDLISTENER = new State("STATE_ENTER_TRANSITION_PENDING") {
-        @Override
-        public void run() {
-            Object transition = TransitionHelper.getEnterTransition(getActivity().getWindow());
-            TransitionHelper.addTransitionListener(transition, mEnterTransitionListener);
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_PENDING = new State("STATE_ENTER_TRANSITION_PENDING") {
-        @Override
-        public void run() {
-            if (mWaitEnterTransitionTimeout == null) {
-                new WaitEnterTransitionTimeout(DetailsFragment.this);
-            }
-        }
-    };
-
-    /**
-     * Start this task when first DetailsOverviewRow is created, if there is no entrance transition
-     * started, it will clear PF_ENTRANCE_TRANSITION_PENDING.
-     */
-    static class WaitEnterTransitionTimeout implements Runnable {
-        static final long WAIT_ENTERTRANSITION_START = 200;
-
-        final WeakReference<DetailsFragment> mRef;
-
-        WaitEnterTransitionTimeout(DetailsFragment f) {
-            mRef = new WeakReference<>(f);
-            f.getView().postDelayed(this, WAIT_ENTERTRANSITION_START);
-        }
-
-        @Override
-        public void run() {
-            DetailsFragment f = mRef.get();
-            if (f != null) {
-                f.mStateMachine.fireEvent(f.EVT_ENTER_TRANSIITON_DONE);
-            }
-        }
-    }
-
-    final State STATE_ON_SAFE_START = new State("STATE_ON_SAFE_START") {
-        @Override
-        public void run() {
-            onSafeStart();
-        }
-    };
-
-    final Event EVT_ONSTART = new Event("onStart");
-
-    final Event EVT_NO_ENTER_TRANSITION = new Event("EVT_NO_ENTER_TRANSITION");
-
-    final Event EVT_DETAILS_ROW_LOADED = new Event("onFirstRowLoaded");
-
-    final Event EVT_ENTER_TRANSIITON_DONE = new Event("onEnterTransitionDone");
-
-    final Event EVT_SWITCH_TO_VIDEO = new Event("switchToVideo");
-
-    @Override
-    void createStateMachineStates() {
-        super.createStateMachineStates();
-        mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
-        mStateMachine.addState(STATE_ON_SAFE_START);
-        mStateMachine.addState(STATE_SWITCH_TO_VIDEO_IN_ON_CREATE);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_INIT);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_ADDLISTENER);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_CANCEL);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_PENDING);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_COMPLETE);
-    }
-
-    @Override
-    void createStateMachineTransitions() {
-        super.createStateMachineTransitions();
-        /**
-         * Part 1: Processing enter transitions after fragment.onCreate
-         */
-        mStateMachine.addTransition(STATE_START, STATE_ENTER_TRANSITION_INIT, EVT_ON_CREATE);
-        // if transition is not supported, skip to complete
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_COMPLETE,
-                COND_TRANSITION_NOT_SUPPORTED);
-        // if transition is not set on Activity, skip to complete
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_COMPLETE,
-                EVT_NO_ENTER_TRANSITION);
-        // if switchToVideo is called before EVT_ON_CREATEVIEW, clear enter transition and skip to
-        // complete.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_CANCEL,
-                EVT_SWITCH_TO_VIDEO);
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_CANCEL, STATE_ENTER_TRANSITION_COMPLETE);
-        // once after onCreateView, we cannot skip the enter transition, add a listener and wait
-        // it to finish
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_ADDLISTENER,
-                EVT_ON_CREATEVIEW);
-        // when enter transition finishes, go to complete, however this might never happen if
-        // the activity is not giving transition options in startActivity, there is no API to query
-        // if this activity is started in a enter transition mode. So we rely on a timer below:
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_ADDLISTENER,
-                STATE_ENTER_TRANSITION_COMPLETE, EVT_ENTER_TRANSIITON_DONE);
-        // we are expecting app to start delayed enter transition shortly after details row is
-        // loaded, so create a timer and wait for enter transition start.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_ADDLISTENER,
-                STATE_ENTER_TRANSITION_PENDING, EVT_DETAILS_ROW_LOADED);
-        // if enter transition not started in the timer, skip to DONE, this can be also true when
-        // startActivity is not giving transition option.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_PENDING, STATE_ENTER_TRANSITION_COMPLETE,
-                EVT_ENTER_TRANSIITON_DONE);
-
-        /**
-         * Part 2: modification to the entrance transition defined in BaseFragment
-         */
-        // Must finish enter transition before perform entrance transition.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_COMPLETE, STATE_ENTRANCE_PERFORM);
-        // Calling switch to video would hide immediately and skip entrance transition
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_SWITCH_TO_VIDEO_IN_ON_CREATE,
-                EVT_SWITCH_TO_VIDEO);
-        mStateMachine.addTransition(STATE_SWITCH_TO_VIDEO_IN_ON_CREATE, STATE_ENTRANCE_COMPLETE);
-        // if the entrance transition is skipped to complete by COND_TRANSITION_NOT_SUPPORTED, we
-        // still need to do the switchToVideo.
-        mStateMachine.addTransition(STATE_ENTRANCE_COMPLETE, STATE_SWITCH_TO_VIDEO_IN_ON_CREATE,
-                EVT_SWITCH_TO_VIDEO);
-
-        // for once the view is created in onStart and prepareEntranceTransition was called, we
-        // could setEntranceStartState:
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_SET_ENTRANCE_START_STATE, EVT_ONSTART);
-
-        /**
-         * Part 3: onSafeStart()
-         */
-        // for onSafeStart: the condition is onStart called, entrance transition complete
-        mStateMachine.addTransition(STATE_START, STATE_ON_SAFE_START, EVT_ONSTART);
-        mStateMachine.addTransition(STATE_ENTRANCE_COMPLETE, STATE_ON_SAFE_START);
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_COMPLETE, STATE_ON_SAFE_START);
-    }
-
-    private class SetSelectionRunnable implements Runnable {
-        int mPosition;
-        boolean mSmooth = true;
-
-        SetSelectionRunnable() {
-        }
-
-        @Override
-        public void run() {
-            if (mRowsFragment == null) {
-                return;
-            }
-            mRowsFragment.setSelectedPosition(mPosition, mSmooth);
-        }
-    }
-
-    TransitionListener mEnterTransitionListener = new TransitionListener() {
-        @Override
-        public void onTransitionStart(Object transition) {
-            if (mWaitEnterTransitionTimeout != null) {
-                // cancel task of WaitEnterTransitionTimeout, we will clearPendingEnterTransition
-                // when transition finishes.
-                mWaitEnterTransitionTimeout.mRef.clear();
-            }
-        }
-
-        @Override
-        public void onTransitionCancel(Object transition) {
-            mStateMachine.fireEvent(EVT_ENTER_TRANSIITON_DONE);
-        }
-
-        @Override
-        public void onTransitionEnd(Object transition) {
-            mStateMachine.fireEvent(EVT_ENTER_TRANSIITON_DONE);
-        }
-    };
-
-    TransitionListener mReturnTransitionListener = new TransitionListener() {
-        @Override
-        public void onTransitionStart(Object transition) {
-            onReturnTransitionStart();
-        }
-    };
-
-    BrowseFrameLayout mRootView;
-    View mBackgroundView;
-    Drawable mBackgroundDrawable;
-    Fragment mVideoFragment;
-    DetailsParallax mDetailsParallax;
-    RowsFragment mRowsFragment;
-    ObjectAdapter mAdapter;
-    int mContainerListAlignTop;
-    BaseOnItemViewSelectedListener mExternalOnItemViewSelectedListener;
-    BaseOnItemViewClickedListener mOnItemViewClickedListener;
-    DetailsFragmentBackgroundController mDetailsBackgroundController;
-
-    // A temporarily flag when switchToVideo() is called in onCreate(), if mPendingFocusOnVideo is
-    // true, we will focus to VideoFragment immediately after video fragment's view is created.
-    boolean mPendingFocusOnVideo = false;
-
-    WaitEnterTransitionTimeout mWaitEnterTransitionTimeout;
-
-    Object mSceneAfterEntranceTransition;
-
-    final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
-    final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
-            new BaseOnItemViewSelectedListener<Object>() {
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                                   RowPresenter.ViewHolder rowViewHolder, Object row) {
-            int position = mRowsFragment.getVerticalGridView().getSelectedPosition();
-            int subposition = mRowsFragment.getVerticalGridView().getSelectedSubPosition();
-            if (DEBUG) Log.v(TAG, "row selected position " + position
-                    + " subposition " + subposition);
-            onRowSelected(position, subposition);
-            if (mExternalOnItemViewSelectedListener != null) {
-                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                        rowViewHolder, row);
-            }
-        }
-    };
-
-    /**
-     * Sets the list of rows for the fragment.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        Presenter[] presenters = adapter.getPresenterSelector().getPresenters();
-        if (presenters != null) {
-            for (int i = 0; i < presenters.length; i++) {
-                setupPresenter(presenters[i]);
-            }
-        } else {
-            Log.e(TAG, "PresenterSelector.getPresenters() not implemented");
-        }
-        if (mRowsFragment != null) {
-            mRowsFragment.setAdapter(adapter);
-        }
-    }
-
-    /**
-     * Returns the list of rows.
-     */
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
-        mExternalOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item clicked listener.
-     */
-    public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
-        if (mOnItemViewClickedListener != listener) {
-            mOnItemViewClickedListener = listener;
-            if (mRowsFragment != null) {
-                mRowsFragment.setOnItemViewClickedListener(listener);
-            }
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public BaseOnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mContainerListAlignTop =
-            getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top);
-
-        Activity activity = getActivity();
-        if (activity != null) {
-            Object transition = TransitionHelper.getEnterTransition(activity.getWindow());
-            if (transition == null) {
-                mStateMachine.fireEvent(EVT_NO_ENTER_TRANSITION);
-            }
-            transition = TransitionHelper.getReturnTransition(activity.getWindow());
-            if (transition != null) {
-                TransitionHelper.addTransitionListener(transition, mReturnTransitionListener);
-            }
-        } else {
-            mStateMachine.fireEvent(EVT_NO_ENTER_TRANSITION);
-        }
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        mRootView = (BrowseFrameLayout) inflater.inflate(
-                R.layout.lb_details_fragment, container, false);
-        mBackgroundView = mRootView.findViewById(R.id.details_background_view);
-        if (mBackgroundView != null) {
-            mBackgroundView.setBackground(mBackgroundDrawable);
-        }
-        mRowsFragment = (RowsFragment) getChildFragmentManager().findFragmentById(
-                R.id.details_rows_dock);
-        if (mRowsFragment == null) {
-            mRowsFragment = new RowsFragment();
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.details_rows_dock, mRowsFragment).commit();
-        }
-        installTitleView(inflater, mRootView, savedInstanceState);
-        mRowsFragment.setAdapter(mAdapter);
-        mRowsFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-        mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(mRootView, new Runnable() {
-            @Override
-            public void run() {
-                mRowsFragment.setEntranceTransitionState(true);
-            }
-        });
-
-        setupDpadNavigation();
-
-        if (Build.VERSION.SDK_INT >= 21) {
-            // Setup adapter listener to work with ParallaxTransition (>= API 21).
-            mRowsFragment.setExternalAdapterListener(new ItemBridgeAdapter.AdapterListener() {
-                @Override
-                public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
-                    if (mDetailsParallax != null && vh.getViewHolder()
-                            instanceof FullWidthDetailsOverviewRowPresenter.ViewHolder) {
-                        FullWidthDetailsOverviewRowPresenter.ViewHolder rowVh =
-                                (FullWidthDetailsOverviewRowPresenter.ViewHolder)
-                                        vh.getViewHolder();
-                        rowVh.getOverviewView().setTag(R.id.lb_parallax_source,
-                                mDetailsParallax);
-                    }
-                }
-            });
-        }
-        return mRootView;
-    }
-
-    /**
-     * @deprecated override {@link #onInflateTitleView(LayoutInflater,ViewGroup,Bundle)} instead.
-     */
-    @Deprecated
-    protected View inflateTitle(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        return super.onInflateTitleView(inflater, parent, savedInstanceState);
-    }
-
-    @Override
-    public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
-                                   Bundle savedInstanceState) {
-        return inflateTitle(inflater, parent, savedInstanceState);
-    }
-
-    void setVerticalGridViewLayout(VerticalGridView listview) {
-        // align the top edge of item to a fixed position
-        listview.setItemAlignmentOffset(-mContainerListAlignTop);
-        listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-        listview.setWindowAlignmentOffset(0);
-        listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-    }
-
-    /**
-     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.Note
-     * that setup should only change the Presenter behavior that is meaningful in DetailsFragment.
-     * For example how a row is aligned in details Fragment.   The default implementation invokes
-     * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}
-     *
-     */
-    protected void setupPresenter(Presenter rowPresenter) {
-        if (rowPresenter instanceof FullWidthDetailsOverviewRowPresenter) {
-            setupDetailsOverviewRowPresenter((FullWidthDetailsOverviewRowPresenter) rowPresenter);
-        }
-    }
-
-    /**
-     * Called to setup {@link FullWidthDetailsOverviewRowPresenter}.  The default implementation
-     * adds two alignment positions({@link ItemAlignmentFacet}) for ViewHolder of
-     * FullWidthDetailsOverviewRowPresenter to align in fragment.
-     */
-    protected void setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter presenter) {
-        ItemAlignmentFacet facet = new ItemAlignmentFacet();
-        // by default align details_frame to half window height
-        ItemAlignmentFacet.ItemAlignmentDef alignDef1 = new ItemAlignmentFacet.ItemAlignmentDef();
-        alignDef1.setItemAlignmentViewId(R.id.details_frame);
-        alignDef1.setItemAlignmentOffset(- getResources()
-                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions));
-        alignDef1.setItemAlignmentOffsetPercent(0);
-        // when description is selected, align details_frame to top edge
-        ItemAlignmentFacet.ItemAlignmentDef alignDef2 = new ItemAlignmentFacet.ItemAlignmentDef();
-        alignDef2.setItemAlignmentViewId(R.id.details_frame);
-        alignDef2.setItemAlignmentFocusViewId(R.id.details_overview_description);
-        alignDef2.setItemAlignmentOffset(- getResources()
-                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description));
-        alignDef2.setItemAlignmentOffsetPercent(0);
-        ItemAlignmentFacet.ItemAlignmentDef[] defs =
-                new ItemAlignmentFacet.ItemAlignmentDef[] {alignDef1, alignDef2};
-        facet.setAlignmentDefs(defs);
-        presenter.setFacet(ItemAlignmentFacet.class, facet);
-    }
-
-    VerticalGridView getVerticalGridView() {
-        return mRowsFragment == null ? null : mRowsFragment.getVerticalGridView();
-    }
-
-    /**
-     * Gets embedded RowsFragment showing multiple rows for DetailsFragment.  If view of
-     * DetailsFragment is not created, the method returns null.
-     * @return Embedded RowsFragment showing multiple rows for DetailsFragment.
-     */
-    public RowsFragment getRowsFragment() {
-        return mRowsFragment;
-    }
-
-    /**
-     * Setup dimensions that are only meaningful when the child Fragments are inside
-     * DetailsFragment.
-     */
-    private void setupChildFragmentLayout() {
-        setVerticalGridViewLayout(mRowsFragment.getVerticalGridView());
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.mPosition = position;
-        mSetSelectionRunnable.mSmooth = smooth;
-        if (getView() != null && getView().getHandler() != null) {
-            getView().getHandler().post(mSetSelectionRunnable);
-        }
-    }
-
-    void switchToVideo() {
-        if (mVideoFragment != null && mVideoFragment.getView() != null) {
-            mVideoFragment.getView().requestFocus();
-        } else {
-            mStateMachine.fireEvent(EVT_SWITCH_TO_VIDEO);
-        }
-    }
-
-    void switchToRows() {
-        mPendingFocusOnVideo = false;
-        VerticalGridView verticalGridView = getVerticalGridView();
-        if (verticalGridView != null && verticalGridView.getChildCount() > 0) {
-            verticalGridView.requestFocus();
-        }
-    }
-
-    /**
-     * This method asks DetailsFragmentBackgroundController to add a fragment for rendering video.
-     * In case the fragment is already there, it will return the existing one. The method must be
-     * called after calling super.onCreate(). App usually does not call this method directly.
-     *
-     * @return Fragment the added or restored fragment responsible for rendering video.
-     * @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) {
-            FragmentTransaction ft2 = getChildFragmentManager().beginTransaction();
-            ft2.add(android.support.v17.leanback.R.id.video_surface_container,
-                    fragment = mDetailsBackgroundController.onCreateVideoFragment());
-            ft2.commit();
-            if (mPendingFocusOnVideo) {
-                // 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();
-                        }
-                        mPendingFocusOnVideo = false;
-                    }
-                });
-            }
-        }
-        mVideoFragment = fragment;
-        return mVideoFragment;
-    }
-
-    void onRowSelected(int selectedPosition, int selectedSubPosition) {
-        ObjectAdapter adapter = getAdapter();
-        if (( mRowsFragment != null && mRowsFragment.getView() != null
-                && mRowsFragment.getView().hasFocus() && !mPendingFocusOnVideo)
-                && (adapter == null || adapter.size() == 0
-                || (getVerticalGridView().getSelectedPosition() == 0
-                && getVerticalGridView().getSelectedSubPosition() == 0))) {
-            showTitle(true);
-        } else {
-            showTitle(false);
-        }
-        if (adapter != null && adapter.size() > selectedPosition) {
-            final VerticalGridView gridView = getVerticalGridView();
-            final int count = gridView.getChildCount();
-            if (count > 0) {
-                mStateMachine.fireEvent(EVT_DETAILS_ROW_LOADED);
-            }
-            for (int i = 0; i < count; i++) {
-                ItemBridgeAdapter.ViewHolder bridgeViewHolder = (ItemBridgeAdapter.ViewHolder)
-                        gridView.getChildViewHolder(gridView.getChildAt(i));
-                RowPresenter rowPresenter = (RowPresenter) bridgeViewHolder.getPresenter();
-                onSetRowStatus(rowPresenter,
-                        rowPresenter.getRowViewHolder(bridgeViewHolder.getViewHolder()),
-                        bridgeViewHolder.getAdapterPosition(),
-                        selectedPosition, selectedSubPosition);
-            }
-        }
-    }
-
-    /**
-     * Called when onStart and enter transition (postponed/none postponed) and entrance transition
-     * are all finished.
-     */
-    @CallSuper
-    void onSafeStart() {
-        if (mDetailsBackgroundController != null) {
-            mDetailsBackgroundController.onStart();
-        }
-    }
-
-    @CallSuper
-    void onReturnTransitionStart() {
-        if (mDetailsBackgroundController != null) {
-            // first disable parallax effect that auto-start PlaybackGlue.
-            boolean isVideoVisible = mDetailsBackgroundController.disableVideoParallax();
-            // if video is not visible we can safely remove VideoFragment,
-            // otherwise let video playing during return transition.
-            if (!isVideoVisible && mVideoFragment != null) {
-                FragmentTransaction ft2 = getChildFragmentManager().beginTransaction();
-                ft2.remove(mVideoFragment);
-                ft2.commit();
-                mVideoFragment = null;
-            }
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mDetailsBackgroundController != null) {
-            mDetailsBackgroundController.onStop();
-        }
-        super.onStop();
-    }
-
-    /**
-     * Called on every visible row to change view status when current selected row position
-     * or selected sub position changed.  Subclass may override.   The default
-     * implementation calls {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
-     * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)} if presenter is
-     * instance of {@link FullWidthDetailsOverviewRowPresenter}.
-     *
-     * @param presenter   The presenter used to create row ViewHolder.
-     * @param viewHolder  The visible (attached) row ViewHolder, note that it may or may not
-     *                    be selected.
-     * @param adapterPosition  The adapter position of viewHolder inside adapter.
-     * @param selectedPosition The adapter position of currently selected row.
-     * @param selectedSubPosition The sub position within currently selected row.  This is used
-     *                            When a row has multiple alignment positions.
-     */
-    protected void onSetRowStatus(RowPresenter presenter, RowPresenter.ViewHolder viewHolder, int
-            adapterPosition, int selectedPosition, int selectedSubPosition) {
-        if (presenter instanceof FullWidthDetailsOverviewRowPresenter) {
-            onSetDetailsOverviewRowStatus((FullWidthDetailsOverviewRowPresenter) presenter,
-                    (FullWidthDetailsOverviewRowPresenter.ViewHolder) viewHolder,
-                    adapterPosition, selectedPosition, selectedSubPosition);
-        }
-    }
-
-    /**
-     * Called to change DetailsOverviewRow view status when current selected row position
-     * or selected sub position changed.  Subclass may override.   The default
-     * implementation switches between three states based on the positions:
-     * {@link FullWidthDetailsOverviewRowPresenter#STATE_HALF},
-     * {@link FullWidthDetailsOverviewRowPresenter#STATE_FULL} and
-     * {@link FullWidthDetailsOverviewRowPresenter#STATE_SMALL}.
-     *
-     * @param presenter   The presenter used to create row ViewHolder.
-     * @param viewHolder  The visible (attached) row ViewHolder, note that it may or may not
-     *                    be selected.
-     * @param adapterPosition  The adapter position of viewHolder inside adapter.
-     * @param selectedPosition The adapter position of currently selected row.
-     * @param selectedSubPosition The sub position within currently selected row.  This is used
-     *                            When a row has multiple alignment positions.
-     */
-    protected void onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter presenter,
-            FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int adapterPosition,
-            int selectedPosition, int selectedSubPosition) {
-        if (selectedPosition > adapterPosition) {
-            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
-        } else if (selectedPosition == adapterPosition && selectedSubPosition == 1) {
-            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
-        } else if (selectedPosition == adapterPosition && selectedSubPosition == 0){
-            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_FULL);
-        } else {
-            presenter.setState(viewHolder,
-                    FullWidthDetailsOverviewRowPresenter.STATE_SMALL);
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        setupChildFragmentLayout();
-        mStateMachine.fireEvent(EVT_ONSTART);
-        if (mDetailsParallax != null) {
-            mDetailsParallax.setRecyclerView(mRowsFragment.getVerticalGridView());
-        }
-        if (mPendingFocusOnVideo) {
-            slideOutGridView();
-        } else if (!getView().hasFocus()) {
-            mRowsFragment.getVerticalGridView().requestFocus();
-        }
-    }
-
-    @Override
-    protected Object createEntranceTransition() {
-        return TransitionHelper.loadTransition(FragmentUtil.getContext(DetailsFragment.this),
-                R.transition.lb_details_enter_transition);
-    }
-
-    @Override
-    protected void runEntranceTransition(Object entranceTransition) {
-        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
-    }
-
-    @Override
-    protected void onEntranceTransitionEnd() {
-        mRowsFragment.onTransitionEnd();
-    }
-
-    @Override
-    protected void onEntranceTransitionPrepare() {
-        mRowsFragment.onTransitionPrepare();
-    }
-
-    @Override
-    protected void onEntranceTransitionStart() {
-        mRowsFragment.onTransitionStart();
-    }
-
-    /**
-     * Returns the {@link DetailsParallax} instance used by
-     * {@link DetailsFragmentBackgroundController} to configure parallax effect of background and
-     * control embedded video playback. App usually does not use this method directly.
-     * App may use this method for other custom parallax tasks.
-     *
-     * @return The DetailsParallax instance attached to the DetailsFragment.
-     */
-    public DetailsParallax getParallax() {
-        if (mDetailsParallax == null) {
-            mDetailsParallax = new DetailsParallax();
-            if (mRowsFragment != null && mRowsFragment.getView() != null) {
-                mDetailsParallax.setRecyclerView(mRowsFragment.getVerticalGridView());
-            }
-        }
-        return mDetailsParallax;
-    }
-
-    /**
-     * Set background drawable shown below foreground rows UI and above
-     * {@link #findOrCreateVideoFragment()}.
-     *
-     * @see DetailsFragmentBackgroundController
-     */
-    void setBackgroundDrawable(Drawable drawable) {
-        if (mBackgroundView != null) {
-            mBackgroundView.setBackground(drawable);
-        }
-        mBackgroundDrawable = drawable;
-    }
-
-    /**
-     * This method does the following
-     * <ul>
-     * <li>sets up focus search handling logic in the root view to enable transitioning between
-     * half screen/full screen/no video mode.</li>
-     *
-     * <li>Sets up the key listener in the root view to intercept events like UP/DOWN and
-     * transition to appropriate mode like half/full screen video.</li>
-     * </ul>
-     */
-    void setupDpadNavigation() {
-        mRootView.setOnChildFocusListener(new BrowseFrameLayout.OnChildFocusListener() {
-
-            @Override
-            public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-                return false;
-            }
-
-            @Override
-            public void onRequestChildFocus(View child, View focused) {
-                if (child != mRootView.getFocusedChild()) {
-                    if (child.getId() == R.id.details_fragment_root) {
-                        if (!mPendingFocusOnVideo) {
-                            slideInGridView();
-                            showTitle(true);
-                        }
-                    } else if (child.getId() == R.id.video_surface_container) {
-                        slideOutGridView();
-                        showTitle(false);
-                    } else {
-                        showTitle(true);
-                    }
-                }
-            }
-        });
-        mRootView.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
-            @Override
-            public View onFocusSearch(View focused, int direction) {
-                if (mRowsFragment.getVerticalGridView() != null
-                        && mRowsFragment.getVerticalGridView().hasFocus()) {
-                    if (direction == View.FOCUS_UP) {
-                        if (mDetailsBackgroundController != null
-                                && mDetailsBackgroundController.canNavigateToVideoFragment()
-                                && mVideoFragment != null && mVideoFragment.getView() != null) {
-                            return mVideoFragment.getView();
-                        } else if (getTitleView() != null && getTitleView().hasFocusable()) {
-                            return getTitleView();
-                        }
-                    }
-                } else if (getTitleView() != null && getTitleView().hasFocus()) {
-                    if (direction == View.FOCUS_DOWN) {
-                        if (mRowsFragment.getVerticalGridView() != null) {
-                            return mRowsFragment.getVerticalGridView();
-                        }
-                    }
-                }
-                return focused;
-            }
-        });
-
-        // If we press BACK on remote while in full screen video mode, we should
-        // transition back to half screen video playback mode.
-        mRootView.setOnDispatchKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // This is used to check if we are in full screen video mode. This is somewhat
-                // hacky and relies on the behavior of the video helper class to update the
-                // focusability of the video surface view.
-                if (mVideoFragment != null && mVideoFragment.getView() != null
-                        && mVideoFragment.getView().hasFocus()) {
-                    if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
-                        if (getVerticalGridView().getChildCount() > 0) {
-                            getVerticalGridView().requestFocus();
-                            return true;
-                        }
-                    }
-                }
-
-                return false;
-            }
-        });
-    }
-
-    /**
-     * Slides vertical grid view (displaying media item details) out of the screen from below.
-     */
-    void slideOutGridView() {
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().animateOut();
-        }
-    }
-
-    void slideInGridView() {
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().animateIn();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java b/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
deleted file mode 100644
index 25ed723..0000000
--- a/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
+++ /dev/null
@@ -1,497 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from {}DetailsSupportFragmentBackgroundController.java.  DO NOT MODIFY. */
-
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 android.animation.PropertyValuesHolder;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
-import android.support.v17.leanback.media.PlaybackGlue;
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.widget.DetailsParallaxDrawable;
-import android.support.v17.leanback.widget.ParallaxTarget;
-import android.app.Fragment;
-
-/**
- * Controller for DetailsFragment parallax background and embedded video play.
- * <p>
- * The parallax background drawable is made of two parts: cover drawable (by default
- * {@link FitWidthBitmapDrawable}) above the details overview row and bottom drawable (by default
- * {@link ColorDrawable}) below the details overview row. While vertically scrolling rows, the size
- * of cover drawable and bottom drawable will be updated and the cover drawable will by default
- * perform a parallax shift using {@link FitWidthBitmapDrawable#PROPERTY_VERTICAL_OFFSET}.
- * </p>
- * <pre>
- *        ***************************
- *        *      Cover Drawable     *
- *        * (FitWidthBitmapDrawable)*
- *        *                         *
- *        ***************************
- *        *    DetailsOverviewRow   *
- *        *                         *
- *        ***************************
- *        *     Bottom Drawable     *
- *        *      (ColorDrawable)    *
- *        *         Related         *
- *        *         Content         *
- *        ***************************
- * </pre>
- * Both parallax background drawable and embedded video play are optional. App must call
- * {@link #enableParallax()} and/or {@link #setupVideoPlayback(PlaybackGlue)} explicitly.
- * The PlaybackGlue is automatically {@link PlaybackGlue#play()} when fragment starts and
- * {@link PlaybackGlue#pause()} when fragment stops. When video is ready to play, cover drawable
- * will be faded out.
- * Example:
- * <pre>
- * DetailsFragmentBackgroundController mController = new DetailsFragmentBackgroundController(this);
- *
- * public void onCreate(Bundle savedInstance) {
- *     super.onCreate(savedInstance);
- *     MediaPlayerGlue player = new MediaPlayerGlue(..);
- *     player.setUrl(...);
- *     mController.enableParallax();
- *     mController.setupVideoPlayback(player);
- * }
- *
- * static class MyLoadBitmapTask extends ... {
- *     WeakReference<MyFragment> mFragmentRef;
- *     MyLoadBitmapTask(MyFragment fragment) {
- *         mFragmentRef = new WeakReference(fragment);
- *     }
- *     protected void onPostExecute(Bitmap bitmap) {
- *         MyFragment fragment = mFragmentRef.get();
- *         if (fragment != null) {
- *             fragment.mController.setCoverBitmap(bitmap);
- *         }
- *     }
- * }
- *
- * public void onStart() {
- *     new MyLoadBitmapTask(this).execute(url);
- * }
- *
- * public void onStop() {
- *     mController.setCoverBitmap(null);
- * }
- * </pre>
- * <p>
- * To customize cover drawable and/or bottom drawable, app should call
- * {@link #enableParallax(Drawable, Drawable, ParallaxTarget.PropertyValuesHolderTarget)}.
- * If app supplies a custom cover Drawable, it should not call {@link #setCoverBitmap(Bitmap)}.
- * If app supplies a custom bottom Drawable, it should not call {@link #setSolidColor(int)}.
- * </p>
- * <p>
- * To customize playback fragment, app should override {@link #onCreateVideoFragment()} and
- * {@link #onCreateGlueHost()}.
- * </p>
- *
- * @deprecated use {@link DetailsSupportFragmentBackgroundController}
- */
-@Deprecated
-public class DetailsFragmentBackgroundController {
-
-    final DetailsFragment mFragment;
-    DetailsParallaxDrawable mParallaxDrawable;
-    int mParallaxDrawableMaxOffset;
-    PlaybackGlue mPlaybackGlue;
-    DetailsBackgroundVideoHelper mVideoHelper;
-    Bitmap mCoverBitmap;
-    int mSolidColor;
-    boolean mCanUseHost = false;
-    boolean mInitialControlVisible = false;
-
-    private Fragment mLastVideoFragmentForGlueHost;
-
-    /**
-     * Creates a DetailsFragmentBackgroundController for a DetailsFragment. Note that
-     * each DetailsFragment can only associate with one DetailsFragmentBackgroundController.
-     *
-     * @param fragment The DetailsFragment to control background and embedded video playing.
-     * @throws IllegalStateException If fragment was already associated with another controller.
-     */
-    public DetailsFragmentBackgroundController(DetailsFragment fragment) {
-        if (fragment.mDetailsBackgroundController != null) {
-            throw new IllegalStateException("Each DetailsFragment is allowed to initialize "
-                    + "DetailsFragmentBackgroundController once");
-        }
-        fragment.mDetailsBackgroundController = this;
-        mFragment = fragment;
-    }
-
-    /**
-     * Enables default parallax background using a {@link FitWidthBitmapDrawable} as cover drawable
-     * and {@link ColorDrawable} as bottom drawable. A vertical parallax movement will be applied
-     * to the FitWidthBitmapDrawable. App may use {@link #setSolidColor(int)} and
-     * {@link #setCoverBitmap(Bitmap)} to change the content of bottom drawable and cover drawable.
-     * This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}.
-     *
-     * @see #setCoverBitmap(Bitmap)
-     * @see #setSolidColor(int)
-     * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called.
-     */
-    public void enableParallax() {
-        int offset = mParallaxDrawableMaxOffset;
-        if (offset == 0) {
-            offset = FragmentUtil.getContext(mFragment).getResources()
-                    .getDimensionPixelSize(R.dimen.lb_details_cover_drawable_parallax_movement);
-        }
-        Drawable coverDrawable = new FitWidthBitmapDrawable();
-        ColorDrawable colorDrawable = new ColorDrawable();
-        enableParallax(coverDrawable, colorDrawable,
-                new ParallaxTarget.PropertyValuesHolderTarget(
-                        coverDrawable,
-                        PropertyValuesHolder.ofInt(FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET,
-                                0, -offset)
-                ));
-    }
-
-    /**
-     * Enables parallax background using a custom cover drawable at top and a custom bottom
-     * drawable. This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}.
-     *
-     * @param coverDrawable Custom cover drawable shown at top. {@link #setCoverBitmap(Bitmap)}
-     *                      will not work if coverDrawable is not {@link FitWidthBitmapDrawable};
-     *                      in that case it's app's responsibility to set content into
-     *                      coverDrawable.
-     * @param bottomDrawable Drawable shown at bottom. {@link #setSolidColor(int)} will not work
-     *                       if bottomDrawable is not {@link ColorDrawable}; in that case it's app's
-     *                       responsibility to set content of bottomDrawable.
-     * @param coverDrawableParallaxTarget Target to perform parallax effect within coverDrawable.
-     *                                    Use null for no parallax movement effect.
-     *                                    Example to move bitmap within FitWidthBitmapDrawable:
-     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
-     *                                        coverDrawable, PropertyValuesHolder.ofInt(
-     *                                            FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET,
-     *                                            0, -120))
-     * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called.
-     */
-    public void enableParallax(@NonNull Drawable coverDrawable, @NonNull Drawable bottomDrawable,
-                               @Nullable ParallaxTarget.PropertyValuesHolderTarget
-                                       coverDrawableParallaxTarget) {
-        if (mParallaxDrawable != null) {
-            return;
-        }
-        // if bitmap is set before enableParallax, use it as initial value.
-        if (mCoverBitmap != null && coverDrawable instanceof FitWidthBitmapDrawable) {
-            ((FitWidthBitmapDrawable) coverDrawable).setBitmap(mCoverBitmap);
-        }
-        // if solid color is set before enableParallax, use it as initial value.
-        if (mSolidColor != Color.TRANSPARENT && bottomDrawable instanceof ColorDrawable) {
-            ((ColorDrawable) bottomDrawable).setColor(mSolidColor);
-        }
-        if (mPlaybackGlue != null) {
-            throw new IllegalStateException("enableParallaxDrawable must be called before "
-                    + "enableVideoPlayback");
-        }
-        mParallaxDrawable = new DetailsParallaxDrawable(
-                FragmentUtil.getContext(mFragment),
-                mFragment.getParallax(),
-                coverDrawable,
-                bottomDrawable,
-                coverDrawableParallaxTarget);
-        mFragment.setBackgroundDrawable(mParallaxDrawable);
-        // create a VideoHelper with null PlaybackGlue for changing CoverDrawable visibility
-        // before PlaybackGlue is ready.
-        mVideoHelper = new DetailsBackgroundVideoHelper(null,
-                mFragment.getParallax(), mParallaxDrawable.getCoverDrawable());
-    }
-
-    /**
-     * Enable video playback and set proper {@link PlaybackGlueHost}. This method by default
-     * creates a VideoFragment and VideoFragmentGlueHost to host the PlaybackGlue.
-     * This method must be called after calling details Fragment super.onCreate(). This method
-     * can be called multiple times to replace existing PlaybackGlue or calling
-     * setupVideoPlayback(null) to clear. Note a typical {@link PlaybackGlue} subclass releases
-     * resources in {@link PlaybackGlue#onDetachedFromHost()}, when the {@link PlaybackGlue}
-     * subclass is not doing that, it's app's responsibility to release the resources.
-     *
-     * @param playbackGlue The new PlaybackGlue to set as background or null to clear existing one.
-     * @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) {
-            if (playbackGlueHost == null
-                    || mLastVideoFragmentForGlueHost != findOrCreateVideoFragment()) {
-                mPlaybackGlue.setHost(createGlueHost());
-                mLastVideoFragmentForGlueHost = findOrCreateVideoFragment();
-            } else {
-                mPlaybackGlue.setHost(playbackGlueHost);
-            }
-        }
-    }
-
-    /**
-     * Returns current PlaybackGlue or null if not set or cleared.
-     *
-     * @return Current PlaybackGlue or null
-     */
-    public final PlaybackGlue getPlaybackGlue() {
-        return mPlaybackGlue;
-    }
-
-    /**
-     * 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#isPrepared()} is true. Note this method does not block
-     * app calls {@link #switchToVideo}.
-     *
-     * @return True allow to navigate to video fragment.
-     */
-    public boolean canNavigateToVideoFragment() {
-        return mPlaybackGlue != null;
-    }
-
-    void switchToVideoBeforeCreate() {
-        mVideoHelper.crossFadeBackgroundToVideo(true, true);
-        mInitialControlVisible = true;
-    }
-
-    /**
-     * Switch to video fragment, note that this method is not affected by result of
-     * {@link #canNavigateToVideoFragment()}. If the method is called in DetailsFragment.onCreate()
-     * it will make video fragment to be initially focused once it is created.
-     * <p>
-     * Calling switchToVideo() in DetailsFragment.onCreate() will clear the activity enter
-     * transition and shared element transition.
-     * </p>
-     * <p>
-     * If switchToVideo() is called after {@link DetailsFragment#prepareEntranceTransition()} and
-     * before {@link DetailsFragment#onEntranceTransitionEnd()}, it will be ignored.
-     * </p>
-     * <p>
-     * If {@link DetailsFragment#prepareEntranceTransition()} is called after switchToVideo(), an
-     * IllegalStateException will be thrown.
-     * </p>
-     */
-    public final void switchToVideo() {
-        mFragment.switchToVideo();
-    }
-
-    /**
-     * Switch to rows fragment.
-     */
-    public final void switchToRows() {
-        mFragment.switchToRows();
-    }
-
-    /**
-     * When fragment is started and no running transition. First set host if not yet set, second
-     * start playing if it was paused before.
-     */
-    void onStart() {
-        if (!mCanUseHost) {
-            mCanUseHost = true;
-            if (mPlaybackGlue != null) {
-                mPlaybackGlue.setHost(createGlueHost());
-                mLastVideoFragmentForGlueHost = findOrCreateVideoFragment();
-            }
-        }
-        if (mPlaybackGlue != null && mPlaybackGlue.isPrepared()) {
-            mPlaybackGlue.play();
-        }
-    }
-
-    void onStop() {
-        if (mPlaybackGlue != null) {
-            mPlaybackGlue.pause();
-        }
-    }
-
-    /**
-     * Disable parallax that would auto-start video playback
-     * @return true if video fragment is visible or false otherwise.
-     */
-    boolean disableVideoParallax() {
-        if (mVideoHelper != null) {
-            mVideoHelper.stopParallax();
-            return mVideoHelper.isVideoVisible();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the cover drawable at top. Returns null if {@link #enableParallax()} is not called.
-     * By default it's a {@link FitWidthBitmapDrawable}.
-     *
-     * @return The cover drawable at top.
-     */
-    public final Drawable getCoverDrawable() {
-        if (mParallaxDrawable == null) {
-            return null;
-        }
-        return mParallaxDrawable.getCoverDrawable();
-    }
-
-    /**
-     * Returns the drawable at bottom. Returns null if {@link #enableParallax()} is not called.
-     * By default it's a {@link ColorDrawable}.
-     *
-     * @return The bottom drawable.
-     */
-    public final Drawable getBottomDrawable() {
-        if (mParallaxDrawable == null) {
-            return null;
-        }
-        return mParallaxDrawable.getBottomDrawable();
-    }
-
-    /**
-     * Creates a Fragment to host {@link PlaybackGlue}. Returns a new {@link VideoFragment} by
-     * default. App may override and return a different fragment and it also must override
-     * {@link #onCreateGlueHost()}.
-     *
-     * @return A new fragment used in {@link #onCreateGlueHost()}.
-     * @see #onCreateGlueHost()
-     * @see #setupVideoPlayback(PlaybackGlue)
-     */
-    public Fragment onCreateVideoFragment() {
-        return new VideoFragment();
-    }
-
-    /**
-     * Creates a PlaybackGlueHost to host PlaybackGlue. App may override this if it overrides
-     * {@link #onCreateVideoFragment()}. This method must be called after calling Fragment
-     * super.onCreate(). When override this method, app may call
-     * {@link #findOrCreateVideoFragment()} to get or create a fragment.
-     *
-     * @return A new PlaybackGlueHost to host PlaybackGlue.
-     * @see #onCreateVideoFragment()
-     * @see #findOrCreateVideoFragment()
-     * @see #setupVideoPlayback(PlaybackGlue)
-     */
-    public PlaybackGlueHost onCreateGlueHost() {
-        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
-     * a {@link PlaybackGlueHost}.
-     *
-     * @return Fragment the added or restored fragment responsible for rendering video.
-     * @see #onCreateGlueHost()
-     */
-    public final Fragment findOrCreateVideoFragment() {
-        return mFragment.findOrCreateVideoFragment();
-    }
-
-    /**
-     * Convenient method to set Bitmap in cover drawable. If app is not using default
-     * {@link FitWidthBitmapDrawable}, app should not use this method  It's safe to call
-     * setCoverBitmap() before calling {@link #enableParallax()}.
-     *
-     * @param bitmap bitmap to set as cover.
-     */
-    public final void setCoverBitmap(Bitmap bitmap) {
-        mCoverBitmap = bitmap;
-        Drawable drawable = getCoverDrawable();
-        if (drawable instanceof FitWidthBitmapDrawable) {
-            ((FitWidthBitmapDrawable) drawable).setBitmap(mCoverBitmap);
-        }
-    }
-
-    /**
-     * Returns Bitmap set by {@link #setCoverBitmap(Bitmap)}.
-     *
-     * @return Bitmap for cover drawable.
-     */
-    public final Bitmap getCoverBitmap() {
-        return mCoverBitmap;
-    }
-
-    /**
-     * Returns color set by {@link #setSolidColor(int)}.
-     *
-     * @return Solid color used for bottom drawable.
-     */
-    public final @ColorInt int getSolidColor() {
-        return mSolidColor;
-    }
-
-    /**
-     * Convenient method to set color in bottom drawable. If app is not using default
-     * {@link ColorDrawable}, app should not use this method. It's safe to call setSolidColor()
-     * before calling {@link #enableParallax()}.
-     *
-     * @param color color for bottom drawable.
-     */
-    public final void setSolidColor(@ColorInt int color) {
-        mSolidColor = color;
-        Drawable bottomDrawable = getBottomDrawable();
-        if (bottomDrawable instanceof ColorDrawable) {
-            ((ColorDrawable) bottomDrawable).setColor(color);
-        }
-    }
-
-    /**
-     * Sets default parallax offset in pixels for bitmap moving vertically. This method must
-     * be called before {@link #enableParallax()}.
-     *
-     * @param offset Offset in pixels (e.g. 120).
-     * @see #enableParallax()
-     */
-    public final void setParallaxDrawableMaxOffset(int offset) {
-        if (mParallaxDrawable != null) {
-            throw new IllegalStateException("enableParallax already called");
-        }
-        mParallaxDrawableMaxOffset = offset;
-    }
-
-    /**
-     * Returns Default parallax offset in pixels for bitmap moving vertically.
-     * When 0, a default value would be used.
-     *
-     * @return Default parallax offset in pixels for bitmap moving vertically.
-     * @see #enableParallax()
-     */
-    public final int getParallaxDrawableMaxOffset() {
-        return mParallaxDrawableMaxOffset;
-    }
-
-}
diff --git a/android/support/v17/leanback/app/DetailsSupportFragment.java b/android/support/v17/leanback/app/DetailsSupportFragment.java
deleted file mode 100644
index 1f0c259..0000000
--- a/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ /dev/null
@@ -1,929 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from DetailsFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.util.StateMachine.Event;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
-import android.support.v17.leanback.widget.BrowseFrameLayout;
-import android.support.v17.leanback.widget.DetailsParallax;
-import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
-import android.support.v17.leanback.widget.ItemAlignmentFacet;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-
-import java.lang.ref.WeakReference;
-
-/**
- * A fragment for creating Leanback details screens.
- *
- * <p>
- * A DetailsSupportFragment renders the elements of its {@link ObjectAdapter} as a set
- * of rows in a vertical list.The Adapter's {@link PresenterSelector} must maintain subclasses
- * of {@link RowPresenter}.
- * </p>
- *
- * When {@link FullWidthDetailsOverviewRowPresenter} is found in adapter,  DetailsSupportFragment will
- * setup default behavior of the DetailsOverviewRow:
- * <li>
- * The alignment of FullWidthDetailsOverviewRowPresenter is setup in
- * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}.
- * </li>
- * <li>
- * The view status switching of FullWidthDetailsOverviewRowPresenter is done in
- * {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
- * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)}.
- * </li>
- *
- * <p>
- * The recommended activity themes to use with a DetailsSupportFragment are
- * <li>
- * {@link android.support.v17.leanback.R.style#Theme_Leanback_Details} with activity
- * shared element transition for {@link FullWidthDetailsOverviewRowPresenter}.
- * </li>
- * <li>
- * {@link android.support.v17.leanback.R.style#Theme_Leanback_Details_NoSharedElementTransition}
- * if shared element transition is not needed, for example if first row is not rendered by
- * {@link FullWidthDetailsOverviewRowPresenter}.
- * </li>
- * </p>
- *
- * <p>
- * DetailsSupportFragment can use {@link DetailsSupportFragmentBackgroundController} to add a parallax drawable
- * background and embedded video playing fragment.
- * </p>
- */
-public class DetailsSupportFragment extends BaseSupportFragment {
-    static final String TAG = "DetailsSupportFragment";
-    static boolean DEBUG = false;
-
-    final State STATE_SET_ENTRANCE_START_STATE = new State("STATE_SET_ENTRANCE_START_STATE") {
-        @Override
-        public void run() {
-            mRowsSupportFragment.setEntranceTransitionState(false);
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_INIT = new State("STATE_ENTER_TRANSIITON_INIT");
-
-    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.switchToVideoBeforeCreate();
-        showTitle(false);
-        mPendingFocusOnVideo = true;
-        slideOutGridView();
-    }
-
-    final State STATE_SWITCH_TO_VIDEO_IN_ON_CREATE = new State("STATE_SWITCH_TO_VIDEO_IN_ON_CREATE",
-            false, false) {
-        @Override
-        public void run() {
-            switchToVideoBeforeVideoSupportFragmentCreated();
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_CANCEL = new State("STATE_ENTER_TRANSITION_CANCEL",
-            false, false) {
-        @Override
-        public void run() {
-            if (mWaitEnterTransitionTimeout != null) {
-                mWaitEnterTransitionTimeout.mRef.clear();
-            }
-            // clear the activity enter/sharedElement transition, return transitions are kept.
-            // keep the return transitions and clear enter transition
-            if (getActivity() != null) {
-                Window window = getActivity().getWindow();
-                Object returnTransition = TransitionHelper.getReturnTransition(window);
-                Object sharedReturnTransition = TransitionHelper
-                        .getSharedElementReturnTransition(window);
-                TransitionHelper.setEnterTransition(window, null);
-                TransitionHelper.setSharedElementEnterTransition(window, null);
-                TransitionHelper.setReturnTransition(window, returnTransition);
-                TransitionHelper.setSharedElementReturnTransition(window, sharedReturnTransition);
-            }
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_COMPLETE = new State("STATE_ENTER_TRANSIITON_COMPLETE",
-            true, false);
-
-    final State STATE_ENTER_TRANSITION_ADDLISTENER = new State("STATE_ENTER_TRANSITION_PENDING") {
-        @Override
-        public void run() {
-            Object transition = TransitionHelper.getEnterTransition(getActivity().getWindow());
-            TransitionHelper.addTransitionListener(transition, mEnterTransitionListener);
-        }
-    };
-
-    final State STATE_ENTER_TRANSITION_PENDING = new State("STATE_ENTER_TRANSITION_PENDING") {
-        @Override
-        public void run() {
-            if (mWaitEnterTransitionTimeout == null) {
-                new WaitEnterTransitionTimeout(DetailsSupportFragment.this);
-            }
-        }
-    };
-
-    /**
-     * Start this task when first DetailsOverviewRow is created, if there is no entrance transition
-     * started, it will clear PF_ENTRANCE_TRANSITION_PENDING.
-     */
-    static class WaitEnterTransitionTimeout implements Runnable {
-        static final long WAIT_ENTERTRANSITION_START = 200;
-
-        final WeakReference<DetailsSupportFragment> mRef;
-
-        WaitEnterTransitionTimeout(DetailsSupportFragment f) {
-            mRef = new WeakReference<>(f);
-            f.getView().postDelayed(this, WAIT_ENTERTRANSITION_START);
-        }
-
-        @Override
-        public void run() {
-            DetailsSupportFragment f = mRef.get();
-            if (f != null) {
-                f.mStateMachine.fireEvent(f.EVT_ENTER_TRANSIITON_DONE);
-            }
-        }
-    }
-
-    final State STATE_ON_SAFE_START = new State("STATE_ON_SAFE_START") {
-        @Override
-        public void run() {
-            onSafeStart();
-        }
-    };
-
-    final Event EVT_ONSTART = new Event("onStart");
-
-    final Event EVT_NO_ENTER_TRANSITION = new Event("EVT_NO_ENTER_TRANSITION");
-
-    final Event EVT_DETAILS_ROW_LOADED = new Event("onFirstRowLoaded");
-
-    final Event EVT_ENTER_TRANSIITON_DONE = new Event("onEnterTransitionDone");
-
-    final Event EVT_SWITCH_TO_VIDEO = new Event("switchToVideo");
-
-    @Override
-    void createStateMachineStates() {
-        super.createStateMachineStates();
-        mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
-        mStateMachine.addState(STATE_ON_SAFE_START);
-        mStateMachine.addState(STATE_SWITCH_TO_VIDEO_IN_ON_CREATE);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_INIT);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_ADDLISTENER);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_CANCEL);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_PENDING);
-        mStateMachine.addState(STATE_ENTER_TRANSITION_COMPLETE);
-    }
-
-    @Override
-    void createStateMachineTransitions() {
-        super.createStateMachineTransitions();
-        /**
-         * Part 1: Processing enter transitions after fragment.onCreate
-         */
-        mStateMachine.addTransition(STATE_START, STATE_ENTER_TRANSITION_INIT, EVT_ON_CREATE);
-        // if transition is not supported, skip to complete
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_COMPLETE,
-                COND_TRANSITION_NOT_SUPPORTED);
-        // if transition is not set on Activity, skip to complete
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_COMPLETE,
-                EVT_NO_ENTER_TRANSITION);
-        // if switchToVideo is called before EVT_ON_CREATEVIEW, clear enter transition and skip to
-        // complete.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_CANCEL,
-                EVT_SWITCH_TO_VIDEO);
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_CANCEL, STATE_ENTER_TRANSITION_COMPLETE);
-        // once after onCreateView, we cannot skip the enter transition, add a listener and wait
-        // it to finish
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_ADDLISTENER,
-                EVT_ON_CREATEVIEW);
-        // when enter transition finishes, go to complete, however this might never happen if
-        // the activity is not giving transition options in startActivity, there is no API to query
-        // if this activity is started in a enter transition mode. So we rely on a timer below:
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_ADDLISTENER,
-                STATE_ENTER_TRANSITION_COMPLETE, EVT_ENTER_TRANSIITON_DONE);
-        // we are expecting app to start delayed enter transition shortly after details row is
-        // loaded, so create a timer and wait for enter transition start.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_ADDLISTENER,
-                STATE_ENTER_TRANSITION_PENDING, EVT_DETAILS_ROW_LOADED);
-        // if enter transition not started in the timer, skip to DONE, this can be also true when
-        // startActivity is not giving transition option.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_PENDING, STATE_ENTER_TRANSITION_COMPLETE,
-                EVT_ENTER_TRANSIITON_DONE);
-
-        /**
-         * Part 2: modification to the entrance transition defined in BaseSupportFragment
-         */
-        // Must finish enter transition before perform entrance transition.
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_COMPLETE, STATE_ENTRANCE_PERFORM);
-        // Calling switch to video would hide immediately and skip entrance transition
-        mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_SWITCH_TO_VIDEO_IN_ON_CREATE,
-                EVT_SWITCH_TO_VIDEO);
-        mStateMachine.addTransition(STATE_SWITCH_TO_VIDEO_IN_ON_CREATE, STATE_ENTRANCE_COMPLETE);
-        // if the entrance transition is skipped to complete by COND_TRANSITION_NOT_SUPPORTED, we
-        // still need to do the switchToVideo.
-        mStateMachine.addTransition(STATE_ENTRANCE_COMPLETE, STATE_SWITCH_TO_VIDEO_IN_ON_CREATE,
-                EVT_SWITCH_TO_VIDEO);
-
-        // for once the view is created in onStart and prepareEntranceTransition was called, we
-        // could setEntranceStartState:
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_SET_ENTRANCE_START_STATE, EVT_ONSTART);
-
-        /**
-         * Part 3: onSafeStart()
-         */
-        // for onSafeStart: the condition is onStart called, entrance transition complete
-        mStateMachine.addTransition(STATE_START, STATE_ON_SAFE_START, EVT_ONSTART);
-        mStateMachine.addTransition(STATE_ENTRANCE_COMPLETE, STATE_ON_SAFE_START);
-        mStateMachine.addTransition(STATE_ENTER_TRANSITION_COMPLETE, STATE_ON_SAFE_START);
-    }
-
-    private class SetSelectionRunnable implements Runnable {
-        int mPosition;
-        boolean mSmooth = true;
-
-        SetSelectionRunnable() {
-        }
-
-        @Override
-        public void run() {
-            if (mRowsSupportFragment == null) {
-                return;
-            }
-            mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth);
-        }
-    }
-
-    TransitionListener mEnterTransitionListener = new TransitionListener() {
-        @Override
-        public void onTransitionStart(Object transition) {
-            if (mWaitEnterTransitionTimeout != null) {
-                // cancel task of WaitEnterTransitionTimeout, we will clearPendingEnterTransition
-                // when transition finishes.
-                mWaitEnterTransitionTimeout.mRef.clear();
-            }
-        }
-
-        @Override
-        public void onTransitionCancel(Object transition) {
-            mStateMachine.fireEvent(EVT_ENTER_TRANSIITON_DONE);
-        }
-
-        @Override
-        public void onTransitionEnd(Object transition) {
-            mStateMachine.fireEvent(EVT_ENTER_TRANSIITON_DONE);
-        }
-    };
-
-    TransitionListener mReturnTransitionListener = new TransitionListener() {
-        @Override
-        public void onTransitionStart(Object transition) {
-            onReturnTransitionStart();
-        }
-    };
-
-    BrowseFrameLayout mRootView;
-    View mBackgroundView;
-    Drawable mBackgroundDrawable;
-    Fragment mVideoSupportFragment;
-    DetailsParallax mDetailsParallax;
-    RowsSupportFragment mRowsSupportFragment;
-    ObjectAdapter mAdapter;
-    int mContainerListAlignTop;
-    BaseOnItemViewSelectedListener mExternalOnItemViewSelectedListener;
-    BaseOnItemViewClickedListener mOnItemViewClickedListener;
-    DetailsSupportFragmentBackgroundController mDetailsBackgroundController;
-
-    // A temporarily flag when switchToVideo() is called in onCreate(), if mPendingFocusOnVideo is
-    // true, we will focus to VideoSupportFragment immediately after video fragment's view is created.
-    boolean mPendingFocusOnVideo = false;
-
-    WaitEnterTransitionTimeout mWaitEnterTransitionTimeout;
-
-    Object mSceneAfterEntranceTransition;
-
-    final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
-    final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
-            new BaseOnItemViewSelectedListener<Object>() {
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                                   RowPresenter.ViewHolder rowViewHolder, Object row) {
-            int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
-            int subposition = mRowsSupportFragment.getVerticalGridView().getSelectedSubPosition();
-            if (DEBUG) Log.v(TAG, "row selected position " + position
-                    + " subposition " + subposition);
-            onRowSelected(position, subposition);
-            if (mExternalOnItemViewSelectedListener != null) {
-                mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                        rowViewHolder, row);
-            }
-        }
-    };
-
-    /**
-     * Sets the list of rows for the fragment.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        Presenter[] presenters = adapter.getPresenterSelector().getPresenters();
-        if (presenters != null) {
-            for (int i = 0; i < presenters.length; i++) {
-                setupPresenter(presenters[i]);
-            }
-        } else {
-            Log.e(TAG, "PresenterSelector.getPresenters() not implemented");
-        }
-        if (mRowsSupportFragment != null) {
-            mRowsSupportFragment.setAdapter(adapter);
-        }
-    }
-
-    /**
-     * Returns the list of rows.
-     */
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
-        mExternalOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item clicked listener.
-     */
-    public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
-        if (mOnItemViewClickedListener != listener) {
-            mOnItemViewClickedListener = listener;
-            if (mRowsSupportFragment != null) {
-                mRowsSupportFragment.setOnItemViewClickedListener(listener);
-            }
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public BaseOnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mContainerListAlignTop =
-            getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top);
-
-        FragmentActivity activity = getActivity();
-        if (activity != null) {
-            Object transition = TransitionHelper.getEnterTransition(activity.getWindow());
-            if (transition == null) {
-                mStateMachine.fireEvent(EVT_NO_ENTER_TRANSITION);
-            }
-            transition = TransitionHelper.getReturnTransition(activity.getWindow());
-            if (transition != null) {
-                TransitionHelper.addTransitionListener(transition, mReturnTransitionListener);
-            }
-        } else {
-            mStateMachine.fireEvent(EVT_NO_ENTER_TRANSITION);
-        }
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        mRootView = (BrowseFrameLayout) inflater.inflate(
-                R.layout.lb_details_fragment, container, false);
-        mBackgroundView = mRootView.findViewById(R.id.details_background_view);
-        if (mBackgroundView != null) {
-            mBackgroundView.setBackground(mBackgroundDrawable);
-        }
-        mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
-                R.id.details_rows_dock);
-        if (mRowsSupportFragment == null) {
-            mRowsSupportFragment = new RowsSupportFragment();
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.details_rows_dock, mRowsSupportFragment).commit();
-        }
-        installTitleView(inflater, mRootView, savedInstanceState);
-        mRowsSupportFragment.setAdapter(mAdapter);
-        mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-        mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(mRootView, new Runnable() {
-            @Override
-            public void run() {
-                mRowsSupportFragment.setEntranceTransitionState(true);
-            }
-        });
-
-        setupDpadNavigation();
-
-        if (Build.VERSION.SDK_INT >= 21) {
-            // Setup adapter listener to work with ParallaxTransition (>= API 21).
-            mRowsSupportFragment.setExternalAdapterListener(new ItemBridgeAdapter.AdapterListener() {
-                @Override
-                public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
-                    if (mDetailsParallax != null && vh.getViewHolder()
-                            instanceof FullWidthDetailsOverviewRowPresenter.ViewHolder) {
-                        FullWidthDetailsOverviewRowPresenter.ViewHolder rowVh =
-                                (FullWidthDetailsOverviewRowPresenter.ViewHolder)
-                                        vh.getViewHolder();
-                        rowVh.getOverviewView().setTag(R.id.lb_parallax_source,
-                                mDetailsParallax);
-                    }
-                }
-            });
-        }
-        return mRootView;
-    }
-
-    /**
-     * @deprecated override {@link #onInflateTitleView(LayoutInflater,ViewGroup,Bundle)} instead.
-     */
-    @Deprecated
-    protected View inflateTitle(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        return super.onInflateTitleView(inflater, parent, savedInstanceState);
-    }
-
-    @Override
-    public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
-                                   Bundle savedInstanceState) {
-        return inflateTitle(inflater, parent, savedInstanceState);
-    }
-
-    void setVerticalGridViewLayout(VerticalGridView listview) {
-        // align the top edge of item to a fixed position
-        listview.setItemAlignmentOffset(-mContainerListAlignTop);
-        listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-        listview.setWindowAlignmentOffset(0);
-        listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-    }
-
-    /**
-     * Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.Note
-     * that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment.
-     * For example how a row is aligned in details Fragment.   The default implementation invokes
-     * {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}
-     *
-     */
-    protected void setupPresenter(Presenter rowPresenter) {
-        if (rowPresenter instanceof FullWidthDetailsOverviewRowPresenter) {
-            setupDetailsOverviewRowPresenter((FullWidthDetailsOverviewRowPresenter) rowPresenter);
-        }
-    }
-
-    /**
-     * Called to setup {@link FullWidthDetailsOverviewRowPresenter}.  The default implementation
-     * adds two alignment positions({@link ItemAlignmentFacet}) for ViewHolder of
-     * FullWidthDetailsOverviewRowPresenter to align in fragment.
-     */
-    protected void setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter presenter) {
-        ItemAlignmentFacet facet = new ItemAlignmentFacet();
-        // by default align details_frame to half window height
-        ItemAlignmentFacet.ItemAlignmentDef alignDef1 = new ItemAlignmentFacet.ItemAlignmentDef();
-        alignDef1.setItemAlignmentViewId(R.id.details_frame);
-        alignDef1.setItemAlignmentOffset(- getResources()
-                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions));
-        alignDef1.setItemAlignmentOffsetPercent(0);
-        // when description is selected, align details_frame to top edge
-        ItemAlignmentFacet.ItemAlignmentDef alignDef2 = new ItemAlignmentFacet.ItemAlignmentDef();
-        alignDef2.setItemAlignmentViewId(R.id.details_frame);
-        alignDef2.setItemAlignmentFocusViewId(R.id.details_overview_description);
-        alignDef2.setItemAlignmentOffset(- getResources()
-                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description));
-        alignDef2.setItemAlignmentOffsetPercent(0);
-        ItemAlignmentFacet.ItemAlignmentDef[] defs =
-                new ItemAlignmentFacet.ItemAlignmentDef[] {alignDef1, alignDef2};
-        facet.setAlignmentDefs(defs);
-        presenter.setFacet(ItemAlignmentFacet.class, facet);
-    }
-
-    VerticalGridView getVerticalGridView() {
-        return mRowsSupportFragment == null ? null : mRowsSupportFragment.getVerticalGridView();
-    }
-
-    /**
-     * Gets embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment.  If view of
-     * DetailsSupportFragment is not created, the method returns null.
-     * @return Embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment.
-     */
-    public RowsSupportFragment getRowsSupportFragment() {
-        return mRowsSupportFragment;
-    }
-
-    /**
-     * Setup dimensions that are only meaningful when the child Fragments are inside
-     * DetailsSupportFragment.
-     */
-    private void setupChildFragmentLayout() {
-        setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.mPosition = position;
-        mSetSelectionRunnable.mSmooth = smooth;
-        if (getView() != null && getView().getHandler() != null) {
-            getView().getHandler().post(mSetSelectionRunnable);
-        }
-    }
-
-    void switchToVideo() {
-        if (mVideoSupportFragment != null && mVideoSupportFragment.getView() != null) {
-            mVideoSupportFragment.getView().requestFocus();
-        } else {
-            mStateMachine.fireEvent(EVT_SWITCH_TO_VIDEO);
-        }
-    }
-
-    void switchToRows() {
-        mPendingFocusOnVideo = false;
-        VerticalGridView verticalGridView = getVerticalGridView();
-        if (verticalGridView != null && verticalGridView.getChildCount() > 0) {
-            verticalGridView.requestFocus();
-        }
-    }
-
-    /**
-     * This method asks DetailsSupportFragmentBackgroundController to add a fragment for rendering video.
-     * In case the fragment is already there, it will return the existing one. The method must be
-     * called after calling super.onCreate(). App usually does not call this method directly.
-     *
-     * @return Fragment the added or restored fragment responsible for rendering video.
-     * @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) {
-            FragmentTransaction ft2 = getChildFragmentManager().beginTransaction();
-            ft2.add(android.support.v17.leanback.R.id.video_surface_container,
-                    fragment = mDetailsBackgroundController.onCreateVideoSupportFragment());
-            ft2.commit();
-            if (mPendingFocusOnVideo) {
-                // 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();
-                        }
-                        mPendingFocusOnVideo = false;
-                    }
-                });
-            }
-        }
-        mVideoSupportFragment = fragment;
-        return mVideoSupportFragment;
-    }
-
-    void onRowSelected(int selectedPosition, int selectedSubPosition) {
-        ObjectAdapter adapter = getAdapter();
-        if (( mRowsSupportFragment != null && mRowsSupportFragment.getView() != null
-                && mRowsSupportFragment.getView().hasFocus() && !mPendingFocusOnVideo)
-                && (adapter == null || adapter.size() == 0
-                || (getVerticalGridView().getSelectedPosition() == 0
-                && getVerticalGridView().getSelectedSubPosition() == 0))) {
-            showTitle(true);
-        } else {
-            showTitle(false);
-        }
-        if (adapter != null && adapter.size() > selectedPosition) {
-            final VerticalGridView gridView = getVerticalGridView();
-            final int count = gridView.getChildCount();
-            if (count > 0) {
-                mStateMachine.fireEvent(EVT_DETAILS_ROW_LOADED);
-            }
-            for (int i = 0; i < count; i++) {
-                ItemBridgeAdapter.ViewHolder bridgeViewHolder = (ItemBridgeAdapter.ViewHolder)
-                        gridView.getChildViewHolder(gridView.getChildAt(i));
-                RowPresenter rowPresenter = (RowPresenter) bridgeViewHolder.getPresenter();
-                onSetRowStatus(rowPresenter,
-                        rowPresenter.getRowViewHolder(bridgeViewHolder.getViewHolder()),
-                        bridgeViewHolder.getAdapterPosition(),
-                        selectedPosition, selectedSubPosition);
-            }
-        }
-    }
-
-    /**
-     * Called when onStart and enter transition (postponed/none postponed) and entrance transition
-     * are all finished.
-     */
-    @CallSuper
-    void onSafeStart() {
-        if (mDetailsBackgroundController != null) {
-            mDetailsBackgroundController.onStart();
-        }
-    }
-
-    @CallSuper
-    void onReturnTransitionStart() {
-        if (mDetailsBackgroundController != null) {
-            // first disable parallax effect that auto-start PlaybackGlue.
-            boolean isVideoVisible = mDetailsBackgroundController.disableVideoParallax();
-            // if video is not visible we can safely remove VideoSupportFragment,
-            // otherwise let video playing during return transition.
-            if (!isVideoVisible && mVideoSupportFragment != null) {
-                FragmentTransaction ft2 = getChildFragmentManager().beginTransaction();
-                ft2.remove(mVideoSupportFragment);
-                ft2.commit();
-                mVideoSupportFragment = null;
-            }
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mDetailsBackgroundController != null) {
-            mDetailsBackgroundController.onStop();
-        }
-        super.onStop();
-    }
-
-    /**
-     * Called on every visible row to change view status when current selected row position
-     * or selected sub position changed.  Subclass may override.   The default
-     * implementation calls {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
-     * FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)} if presenter is
-     * instance of {@link FullWidthDetailsOverviewRowPresenter}.
-     *
-     * @param presenter   The presenter used to create row ViewHolder.
-     * @param viewHolder  The visible (attached) row ViewHolder, note that it may or may not
-     *                    be selected.
-     * @param adapterPosition  The adapter position of viewHolder inside adapter.
-     * @param selectedPosition The adapter position of currently selected row.
-     * @param selectedSubPosition The sub position within currently selected row.  This is used
-     *                            When a row has multiple alignment positions.
-     */
-    protected void onSetRowStatus(RowPresenter presenter, RowPresenter.ViewHolder viewHolder, int
-            adapterPosition, int selectedPosition, int selectedSubPosition) {
-        if (presenter instanceof FullWidthDetailsOverviewRowPresenter) {
-            onSetDetailsOverviewRowStatus((FullWidthDetailsOverviewRowPresenter) presenter,
-                    (FullWidthDetailsOverviewRowPresenter.ViewHolder) viewHolder,
-                    adapterPosition, selectedPosition, selectedSubPosition);
-        }
-    }
-
-    /**
-     * Called to change DetailsOverviewRow view status when current selected row position
-     * or selected sub position changed.  Subclass may override.   The default
-     * implementation switches between three states based on the positions:
-     * {@link FullWidthDetailsOverviewRowPresenter#STATE_HALF},
-     * {@link FullWidthDetailsOverviewRowPresenter#STATE_FULL} and
-     * {@link FullWidthDetailsOverviewRowPresenter#STATE_SMALL}.
-     *
-     * @param presenter   The presenter used to create row ViewHolder.
-     * @param viewHolder  The visible (attached) row ViewHolder, note that it may or may not
-     *                    be selected.
-     * @param adapterPosition  The adapter position of viewHolder inside adapter.
-     * @param selectedPosition The adapter position of currently selected row.
-     * @param selectedSubPosition The sub position within currently selected row.  This is used
-     *                            When a row has multiple alignment positions.
-     */
-    protected void onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter presenter,
-            FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int adapterPosition,
-            int selectedPosition, int selectedSubPosition) {
-        if (selectedPosition > adapterPosition) {
-            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
-        } else if (selectedPosition == adapterPosition && selectedSubPosition == 1) {
-            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
-        } else if (selectedPosition == adapterPosition && selectedSubPosition == 0){
-            presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_FULL);
-        } else {
-            presenter.setState(viewHolder,
-                    FullWidthDetailsOverviewRowPresenter.STATE_SMALL);
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        setupChildFragmentLayout();
-        mStateMachine.fireEvent(EVT_ONSTART);
-        if (mDetailsParallax != null) {
-            mDetailsParallax.setRecyclerView(mRowsSupportFragment.getVerticalGridView());
-        }
-        if (mPendingFocusOnVideo) {
-            slideOutGridView();
-        } else if (!getView().hasFocus()) {
-            mRowsSupportFragment.getVerticalGridView().requestFocus();
-        }
-    }
-
-    @Override
-    protected Object createEntranceTransition() {
-        return TransitionHelper.loadTransition(getContext(),
-                R.transition.lb_details_enter_transition);
-    }
-
-    @Override
-    protected void runEntranceTransition(Object entranceTransition) {
-        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
-    }
-
-    @Override
-    protected void onEntranceTransitionEnd() {
-        mRowsSupportFragment.onTransitionEnd();
-    }
-
-    @Override
-    protected void onEntranceTransitionPrepare() {
-        mRowsSupportFragment.onTransitionPrepare();
-    }
-
-    @Override
-    protected void onEntranceTransitionStart() {
-        mRowsSupportFragment.onTransitionStart();
-    }
-
-    /**
-     * Returns the {@link DetailsParallax} instance used by
-     * {@link DetailsSupportFragmentBackgroundController} to configure parallax effect of background and
-     * control embedded video playback. App usually does not use this method directly.
-     * App may use this method for other custom parallax tasks.
-     *
-     * @return The DetailsParallax instance attached to the DetailsSupportFragment.
-     */
-    public DetailsParallax getParallax() {
-        if (mDetailsParallax == null) {
-            mDetailsParallax = new DetailsParallax();
-            if (mRowsSupportFragment != null && mRowsSupportFragment.getView() != null) {
-                mDetailsParallax.setRecyclerView(mRowsSupportFragment.getVerticalGridView());
-            }
-        }
-        return mDetailsParallax;
-    }
-
-    /**
-     * Set background drawable shown below foreground rows UI and above
-     * {@link #findOrCreateVideoSupportFragment()}.
-     *
-     * @see DetailsSupportFragmentBackgroundController
-     */
-    void setBackgroundDrawable(Drawable drawable) {
-        if (mBackgroundView != null) {
-            mBackgroundView.setBackground(drawable);
-        }
-        mBackgroundDrawable = drawable;
-    }
-
-    /**
-     * This method does the following
-     * <ul>
-     * <li>sets up focus search handling logic in the root view to enable transitioning between
-     * half screen/full screen/no video mode.</li>
-     *
-     * <li>Sets up the key listener in the root view to intercept events like UP/DOWN and
-     * transition to appropriate mode like half/full screen video.</li>
-     * </ul>
-     */
-    void setupDpadNavigation() {
-        mRootView.setOnChildFocusListener(new BrowseFrameLayout.OnChildFocusListener() {
-
-            @Override
-            public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-                return false;
-            }
-
-            @Override
-            public void onRequestChildFocus(View child, View focused) {
-                if (child != mRootView.getFocusedChild()) {
-                    if (child.getId() == R.id.details_fragment_root) {
-                        if (!mPendingFocusOnVideo) {
-                            slideInGridView();
-                            showTitle(true);
-                        }
-                    } else if (child.getId() == R.id.video_surface_container) {
-                        slideOutGridView();
-                        showTitle(false);
-                    } else {
-                        showTitle(true);
-                    }
-                }
-            }
-        });
-        mRootView.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
-            @Override
-            public View onFocusSearch(View focused, int direction) {
-                if (mRowsSupportFragment.getVerticalGridView() != null
-                        && mRowsSupportFragment.getVerticalGridView().hasFocus()) {
-                    if (direction == View.FOCUS_UP) {
-                        if (mDetailsBackgroundController != null
-                                && mDetailsBackgroundController.canNavigateToVideoSupportFragment()
-                                && mVideoSupportFragment != null && mVideoSupportFragment.getView() != null) {
-                            return mVideoSupportFragment.getView();
-                        } else if (getTitleView() != null && getTitleView().hasFocusable()) {
-                            return getTitleView();
-                        }
-                    }
-                } else if (getTitleView() != null && getTitleView().hasFocus()) {
-                    if (direction == View.FOCUS_DOWN) {
-                        if (mRowsSupportFragment.getVerticalGridView() != null) {
-                            return mRowsSupportFragment.getVerticalGridView();
-                        }
-                    }
-                }
-                return focused;
-            }
-        });
-
-        // If we press BACK on remote while in full screen video mode, we should
-        // transition back to half screen video playback mode.
-        mRootView.setOnDispatchKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // This is used to check if we are in full screen video mode. This is somewhat
-                // hacky and relies on the behavior of the video helper class to update the
-                // focusability of the video surface view.
-                if (mVideoSupportFragment != null && mVideoSupportFragment.getView() != null
-                        && mVideoSupportFragment.getView().hasFocus()) {
-                    if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
-                        if (getVerticalGridView().getChildCount() > 0) {
-                            getVerticalGridView().requestFocus();
-                            return true;
-                        }
-                    }
-                }
-
-                return false;
-            }
-        });
-    }
-
-    /**
-     * Slides vertical grid view (displaying media item details) out of the screen from below.
-     */
-    void slideOutGridView() {
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().animateOut();
-        }
-    }
-
-    void slideInGridView() {
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().animateIn();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java b/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java
deleted file mode 100644
index aac74ba..0000000
--- a/android/support/v17/leanback/app/DetailsSupportFragmentBackgroundController.java
+++ /dev/null
@@ -1,492 +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.v17.leanback.app;
-
-import android.animation.PropertyValuesHolder;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
-import android.support.v17.leanback.media.PlaybackGlue;
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.widget.DetailsParallaxDrawable;
-import android.support.v17.leanback.widget.ParallaxTarget;
-import android.support.v4.app.Fragment;
-
-/**
- * Controller for DetailsSupportFragment parallax background and embedded video play.
- * <p>
- * The parallax background drawable is made of two parts: cover drawable (by default
- * {@link FitWidthBitmapDrawable}) above the details overview row and bottom drawable (by default
- * {@link ColorDrawable}) below the details overview row. While vertically scrolling rows, the size
- * of cover drawable and bottom drawable will be updated and the cover drawable will by default
- * perform a parallax shift using {@link FitWidthBitmapDrawable#PROPERTY_VERTICAL_OFFSET}.
- * </p>
- * <pre>
- *        ***************************
- *        *      Cover Drawable     *
- *        * (FitWidthBitmapDrawable)*
- *        *                         *
- *        ***************************
- *        *    DetailsOverviewRow   *
- *        *                         *
- *        ***************************
- *        *     Bottom Drawable     *
- *        *      (ColorDrawable)    *
- *        *         Related         *
- *        *         Content         *
- *        ***************************
- * </pre>
- * Both parallax background drawable and embedded video play are optional. App must call
- * {@link #enableParallax()} and/or {@link #setupVideoPlayback(PlaybackGlue)} explicitly.
- * The PlaybackGlue is automatically {@link PlaybackGlue#play()} when fragment starts and
- * {@link PlaybackGlue#pause()} when fragment stops. When video is ready to play, cover drawable
- * will be faded out.
- * Example:
- * <pre>
- * DetailsSupportFragmentBackgroundController mController = new DetailsSupportFragmentBackgroundController(this);
- *
- * public void onCreate(Bundle savedInstance) {
- *     super.onCreate(savedInstance);
- *     MediaPlayerGlue player = new MediaPlayerGlue(..);
- *     player.setUrl(...);
- *     mController.enableParallax();
- *     mController.setupVideoPlayback(player);
- * }
- *
- * static class MyLoadBitmapTask extends ... {
- *     WeakReference<MyFragment> mFragmentRef;
- *     MyLoadBitmapTask(MyFragment fragment) {
- *         mFragmentRef = new WeakReference(fragment);
- *     }
- *     protected void onPostExecute(Bitmap bitmap) {
- *         MyFragment fragment = mFragmentRef.get();
- *         if (fragment != null) {
- *             fragment.mController.setCoverBitmap(bitmap);
- *         }
- *     }
- * }
- *
- * public void onStart() {
- *     new MyLoadBitmapTask(this).execute(url);
- * }
- *
- * public void onStop() {
- *     mController.setCoverBitmap(null);
- * }
- * </pre>
- * <p>
- * To customize cover drawable and/or bottom drawable, app should call
- * {@link #enableParallax(Drawable, Drawable, ParallaxTarget.PropertyValuesHolderTarget)}.
- * If app supplies a custom cover Drawable, it should not call {@link #setCoverBitmap(Bitmap)}.
- * If app supplies a custom bottom Drawable, it should not call {@link #setSolidColor(int)}.
- * </p>
- * <p>
- * To customize playback fragment, app should override {@link #onCreateVideoSupportFragment()} and
- * {@link #onCreateGlueHost()}.
- * </p>
- *
- */
-public class DetailsSupportFragmentBackgroundController {
-
-    final DetailsSupportFragment mFragment;
-    DetailsParallaxDrawable mParallaxDrawable;
-    int mParallaxDrawableMaxOffset;
-    PlaybackGlue mPlaybackGlue;
-    DetailsBackgroundVideoHelper mVideoHelper;
-    Bitmap mCoverBitmap;
-    int mSolidColor;
-    boolean mCanUseHost = false;
-    boolean mInitialControlVisible = false;
-
-    private Fragment mLastVideoSupportFragmentForGlueHost;
-
-    /**
-     * Creates a DetailsSupportFragmentBackgroundController for a DetailsSupportFragment. Note that
-     * each DetailsSupportFragment can only associate with one DetailsSupportFragmentBackgroundController.
-     *
-     * @param fragment The DetailsSupportFragment to control background and embedded video playing.
-     * @throws IllegalStateException If fragment was already associated with another controller.
-     */
-    public DetailsSupportFragmentBackgroundController(DetailsSupportFragment fragment) {
-        if (fragment.mDetailsBackgroundController != null) {
-            throw new IllegalStateException("Each DetailsSupportFragment is allowed to initialize "
-                    + "DetailsSupportFragmentBackgroundController once");
-        }
-        fragment.mDetailsBackgroundController = this;
-        mFragment = fragment;
-    }
-
-    /**
-     * Enables default parallax background using a {@link FitWidthBitmapDrawable} as cover drawable
-     * and {@link ColorDrawable} as bottom drawable. A vertical parallax movement will be applied
-     * to the FitWidthBitmapDrawable. App may use {@link #setSolidColor(int)} and
-     * {@link #setCoverBitmap(Bitmap)} to change the content of bottom drawable and cover drawable.
-     * This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}.
-     *
-     * @see #setCoverBitmap(Bitmap)
-     * @see #setSolidColor(int)
-     * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called.
-     */
-    public void enableParallax() {
-        int offset = mParallaxDrawableMaxOffset;
-        if (offset == 0) {
-            offset = mFragment.getContext().getResources()
-                    .getDimensionPixelSize(R.dimen.lb_details_cover_drawable_parallax_movement);
-        }
-        Drawable coverDrawable = new FitWidthBitmapDrawable();
-        ColorDrawable colorDrawable = new ColorDrawable();
-        enableParallax(coverDrawable, colorDrawable,
-                new ParallaxTarget.PropertyValuesHolderTarget(
-                        coverDrawable,
-                        PropertyValuesHolder.ofInt(FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET,
-                                0, -offset)
-                ));
-    }
-
-    /**
-     * Enables parallax background using a custom cover drawable at top and a custom bottom
-     * drawable. This method must be called before {@link #setupVideoPlayback(PlaybackGlue)}.
-     *
-     * @param coverDrawable Custom cover drawable shown at top. {@link #setCoverBitmap(Bitmap)}
-     *                      will not work if coverDrawable is not {@link FitWidthBitmapDrawable};
-     *                      in that case it's app's responsibility to set content into
-     *                      coverDrawable.
-     * @param bottomDrawable Drawable shown at bottom. {@link #setSolidColor(int)} will not work
-     *                       if bottomDrawable is not {@link ColorDrawable}; in that case it's app's
-     *                       responsibility to set content of bottomDrawable.
-     * @param coverDrawableParallaxTarget Target to perform parallax effect within coverDrawable.
-     *                                    Use null for no parallax movement effect.
-     *                                    Example to move bitmap within FitWidthBitmapDrawable:
-     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
-     *                                        coverDrawable, PropertyValuesHolder.ofInt(
-     *                                            FitWidthBitmapDrawable.PROPERTY_VERTICAL_OFFSET,
-     *                                            0, -120))
-     * @throws IllegalStateException If {@link #setupVideoPlayback(PlaybackGlue)} was called.
-     */
-    public void enableParallax(@NonNull Drawable coverDrawable, @NonNull Drawable bottomDrawable,
-                               @Nullable ParallaxTarget.PropertyValuesHolderTarget
-                                       coverDrawableParallaxTarget) {
-        if (mParallaxDrawable != null) {
-            return;
-        }
-        // if bitmap is set before enableParallax, use it as initial value.
-        if (mCoverBitmap != null && coverDrawable instanceof FitWidthBitmapDrawable) {
-            ((FitWidthBitmapDrawable) coverDrawable).setBitmap(mCoverBitmap);
-        }
-        // if solid color is set before enableParallax, use it as initial value.
-        if (mSolidColor != Color.TRANSPARENT && bottomDrawable instanceof ColorDrawable) {
-            ((ColorDrawable) bottomDrawable).setColor(mSolidColor);
-        }
-        if (mPlaybackGlue != null) {
-            throw new IllegalStateException("enableParallaxDrawable must be called before "
-                    + "enableVideoPlayback");
-        }
-        mParallaxDrawable = new DetailsParallaxDrawable(
-                mFragment.getContext(),
-                mFragment.getParallax(),
-                coverDrawable,
-                bottomDrawable,
-                coverDrawableParallaxTarget);
-        mFragment.setBackgroundDrawable(mParallaxDrawable);
-        // create a VideoHelper with null PlaybackGlue for changing CoverDrawable visibility
-        // before PlaybackGlue is ready.
-        mVideoHelper = new DetailsBackgroundVideoHelper(null,
-                mFragment.getParallax(), mParallaxDrawable.getCoverDrawable());
-    }
-
-    /**
-     * Enable video playback and set proper {@link PlaybackGlueHost}. This method by default
-     * creates a VideoSupportFragment and VideoSupportFragmentGlueHost to host the PlaybackGlue.
-     * This method must be called after calling details Fragment super.onCreate(). This method
-     * can be called multiple times to replace existing PlaybackGlue or calling
-     * setupVideoPlayback(null) to clear. Note a typical {@link PlaybackGlue} subclass releases
-     * resources in {@link PlaybackGlue#onDetachedFromHost()}, when the {@link PlaybackGlue}
-     * subclass is not doing that, it's app's responsibility to release the resources.
-     *
-     * @param playbackGlue The new PlaybackGlue to set as background or null to clear existing one.
-     * @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) {
-            if (playbackGlueHost == null
-                    || mLastVideoSupportFragmentForGlueHost != findOrCreateVideoSupportFragment()) {
-                mPlaybackGlue.setHost(createGlueHost());
-                mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment();
-            } else {
-                mPlaybackGlue.setHost(playbackGlueHost);
-            }
-        }
-    }
-
-    /**
-     * Returns current PlaybackGlue or null if not set or cleared.
-     *
-     * @return Current PlaybackGlue or null
-     */
-    public final PlaybackGlue getPlaybackGlue() {
-        return mPlaybackGlue;
-    }
-
-    /**
-     * 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#isPrepared()} is true. Note this method does not block
-     * app calls {@link #switchToVideo}.
-     *
-     * @return True allow to navigate to video fragment.
-     */
-    public boolean canNavigateToVideoSupportFragment() {
-        return mPlaybackGlue != null;
-    }
-
-    void switchToVideoBeforeCreate() {
-        mVideoHelper.crossFadeBackgroundToVideo(true, true);
-        mInitialControlVisible = true;
-    }
-
-    /**
-     * Switch to video fragment, note that this method is not affected by result of
-     * {@link #canNavigateToVideoSupportFragment()}. If the method is called in DetailsSupportFragment.onCreate()
-     * it will make video fragment to be initially focused once it is created.
-     * <p>
-     * Calling switchToVideo() in DetailsSupportFragment.onCreate() will clear the activity enter
-     * transition and shared element transition.
-     * </p>
-     * <p>
-     * If switchToVideo() is called after {@link DetailsSupportFragment#prepareEntranceTransition()} and
-     * before {@link DetailsSupportFragment#onEntranceTransitionEnd()}, it will be ignored.
-     * </p>
-     * <p>
-     * If {@link DetailsSupportFragment#prepareEntranceTransition()} is called after switchToVideo(), an
-     * IllegalStateException will be thrown.
-     * </p>
-     */
-    public final void switchToVideo() {
-        mFragment.switchToVideo();
-    }
-
-    /**
-     * Switch to rows fragment.
-     */
-    public final void switchToRows() {
-        mFragment.switchToRows();
-    }
-
-    /**
-     * When fragment is started and no running transition. First set host if not yet set, second
-     * start playing if it was paused before.
-     */
-    void onStart() {
-        if (!mCanUseHost) {
-            mCanUseHost = true;
-            if (mPlaybackGlue != null) {
-                mPlaybackGlue.setHost(createGlueHost());
-                mLastVideoSupportFragmentForGlueHost = findOrCreateVideoSupportFragment();
-            }
-        }
-        if (mPlaybackGlue != null && mPlaybackGlue.isPrepared()) {
-            mPlaybackGlue.play();
-        }
-    }
-
-    void onStop() {
-        if (mPlaybackGlue != null) {
-            mPlaybackGlue.pause();
-        }
-    }
-
-    /**
-     * Disable parallax that would auto-start video playback
-     * @return true if video fragment is visible or false otherwise.
-     */
-    boolean disableVideoParallax() {
-        if (mVideoHelper != null) {
-            mVideoHelper.stopParallax();
-            return mVideoHelper.isVideoVisible();
-        }
-        return false;
-    }
-
-    /**
-     * Returns the cover drawable at top. Returns null if {@link #enableParallax()} is not called.
-     * By default it's a {@link FitWidthBitmapDrawable}.
-     *
-     * @return The cover drawable at top.
-     */
-    public final Drawable getCoverDrawable() {
-        if (mParallaxDrawable == null) {
-            return null;
-        }
-        return mParallaxDrawable.getCoverDrawable();
-    }
-
-    /**
-     * Returns the drawable at bottom. Returns null if {@link #enableParallax()} is not called.
-     * By default it's a {@link ColorDrawable}.
-     *
-     * @return The bottom drawable.
-     */
-    public final Drawable getBottomDrawable() {
-        if (mParallaxDrawable == null) {
-            return null;
-        }
-        return mParallaxDrawable.getBottomDrawable();
-    }
-
-    /**
-     * Creates a Fragment to host {@link PlaybackGlue}. Returns a new {@link VideoSupportFragment} by
-     * default. App may override and return a different fragment and it also must override
-     * {@link #onCreateGlueHost()}.
-     *
-     * @return A new fragment used in {@link #onCreateGlueHost()}.
-     * @see #onCreateGlueHost()
-     * @see #setupVideoPlayback(PlaybackGlue)
-     */
-    public Fragment onCreateVideoSupportFragment() {
-        return new VideoSupportFragment();
-    }
-
-    /**
-     * Creates a PlaybackGlueHost to host PlaybackGlue. App may override this if it overrides
-     * {@link #onCreateVideoSupportFragment()}. This method must be called after calling Fragment
-     * super.onCreate(). When override this method, app may call
-     * {@link #findOrCreateVideoSupportFragment()} to get or create a fragment.
-     *
-     * @return A new PlaybackGlueHost to host PlaybackGlue.
-     * @see #onCreateVideoSupportFragment()
-     * @see #findOrCreateVideoSupportFragment()
-     * @see #setupVideoPlayback(PlaybackGlue)
-     */
-    public PlaybackGlueHost onCreateGlueHost() {
-        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
-     * a {@link PlaybackGlueHost}.
-     *
-     * @return Fragment the added or restored fragment responsible for rendering video.
-     * @see #onCreateGlueHost()
-     */
-    public final Fragment findOrCreateVideoSupportFragment() {
-        return mFragment.findOrCreateVideoSupportFragment();
-    }
-
-    /**
-     * Convenient method to set Bitmap in cover drawable. If app is not using default
-     * {@link FitWidthBitmapDrawable}, app should not use this method  It's safe to call
-     * setCoverBitmap() before calling {@link #enableParallax()}.
-     *
-     * @param bitmap bitmap to set as cover.
-     */
-    public final void setCoverBitmap(Bitmap bitmap) {
-        mCoverBitmap = bitmap;
-        Drawable drawable = getCoverDrawable();
-        if (drawable instanceof FitWidthBitmapDrawable) {
-            ((FitWidthBitmapDrawable) drawable).setBitmap(mCoverBitmap);
-        }
-    }
-
-    /**
-     * Returns Bitmap set by {@link #setCoverBitmap(Bitmap)}.
-     *
-     * @return Bitmap for cover drawable.
-     */
-    public final Bitmap getCoverBitmap() {
-        return mCoverBitmap;
-    }
-
-    /**
-     * Returns color set by {@link #setSolidColor(int)}.
-     *
-     * @return Solid color used for bottom drawable.
-     */
-    public final @ColorInt int getSolidColor() {
-        return mSolidColor;
-    }
-
-    /**
-     * Convenient method to set color in bottom drawable. If app is not using default
-     * {@link ColorDrawable}, app should not use this method. It's safe to call setSolidColor()
-     * before calling {@link #enableParallax()}.
-     *
-     * @param color color for bottom drawable.
-     */
-    public final void setSolidColor(@ColorInt int color) {
-        mSolidColor = color;
-        Drawable bottomDrawable = getBottomDrawable();
-        if (bottomDrawable instanceof ColorDrawable) {
-            ((ColorDrawable) bottomDrawable).setColor(color);
-        }
-    }
-
-    /**
-     * Sets default parallax offset in pixels for bitmap moving vertically. This method must
-     * be called before {@link #enableParallax()}.
-     *
-     * @param offset Offset in pixels (e.g. 120).
-     * @see #enableParallax()
-     */
-    public final void setParallaxDrawableMaxOffset(int offset) {
-        if (mParallaxDrawable != null) {
-            throw new IllegalStateException("enableParallax already called");
-        }
-        mParallaxDrawableMaxOffset = offset;
-    }
-
-    /**
-     * Returns Default parallax offset in pixels for bitmap moving vertically.
-     * When 0, a default value would be used.
-     *
-     * @return Default parallax offset in pixels for bitmap moving vertically.
-     * @see #enableParallax()
-     */
-    public final int getParallaxDrawableMaxOffset() {
-        return mParallaxDrawableMaxOffset;
-    }
-
-}
diff --git a/android/support/v17/leanback/app/ErrorFragment.java b/android/support/v17/leanback/app/ErrorFragment.java
deleted file mode 100644
index eda0de1..0000000
--- a/android/support/v17/leanback/app/ErrorFragment.java
+++ /dev/null
@@ -1,247 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from ErrorSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetricsInt;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * A fragment for displaying an error indication.
- * @deprecated use {@link ErrorSupportFragment}
- */
-@Deprecated
-public class ErrorFragment extends BrandedFragment {
-
-    private ViewGroup mErrorFrame;
-    private ImageView mImageView;
-    private TextView mTextView;
-    private Button mButton;
-    private Drawable mDrawable;
-    private CharSequence mMessage;
-    private String mButtonText;
-    private View.OnClickListener mButtonClickListener;
-    private Drawable mBackgroundDrawable;
-    private boolean mIsBackgroundTranslucent = true;
-
-    /**
-     * Sets the default background.
-     *
-     * @param translucent True to set a translucent background.
-     */
-    public void setDefaultBackground(boolean translucent) {
-        mBackgroundDrawable = null;
-        mIsBackgroundTranslucent = translucent;
-        updateBackground();
-        updateMessage();
-    }
-
-    /**
-     * Returns true if the background is translucent.
-     */
-    public boolean isBackgroundTranslucent() {
-        return mIsBackgroundTranslucent;
-    }
-
-    /**
-     * Sets a drawable for the fragment background.
-     *
-     * @param drawable The drawable used for the background.
-     */
-    public void setBackgroundDrawable(Drawable drawable) {
-        mBackgroundDrawable = drawable;
-        if (drawable != null) {
-            final int opacity = drawable.getOpacity();
-            mIsBackgroundTranslucent = (opacity == PixelFormat.TRANSLUCENT
-                    || opacity == PixelFormat.TRANSPARENT);
-        }
-        updateBackground();
-        updateMessage();
-    }
-
-    /**
-     * Returns the background drawable.  May be null if a default is used.
-     */
-    public Drawable getBackgroundDrawable() {
-        return mBackgroundDrawable;
-    }
-
-    /**
-     * Sets the drawable to be used for the error image.
-     *
-     * @param drawable The drawable used for the error image.
-     */
-    public void setImageDrawable(Drawable drawable) {
-        mDrawable = drawable;
-        updateImageDrawable();
-    }
-
-    /**
-     * Returns the drawable used for the error image.
-     */
-    public Drawable getImageDrawable() {
-        return mDrawable;
-    }
-
-    /**
-     * Sets the error message.
-     *
-     * @param message The error message.
-     */
-    public void setMessage(CharSequence message) {
-        mMessage = message;
-        updateMessage();
-    }
-
-    /**
-     * Returns the error message.
-     */
-    public CharSequence getMessage() {
-        return mMessage;
-    }
-
-    /**
-     * Sets the button text.
-     *
-     * @param text The button text.
-     */
-    public void setButtonText(String text) {
-        mButtonText = text;
-        updateButton();
-    }
-
-    /**
-     * Returns the button text.
-     */
-    public String getButtonText() {
-        return mButtonText;
-    }
-
-    /**
-     * Set the button click listener.
-     *
-     * @param clickListener The click listener for the button.
-     */
-    public void setButtonClickListener(View.OnClickListener clickListener) {
-        mButtonClickListener = clickListener;
-        updateButton();
-    }
-
-    /**
-     * Returns the button click listener.
-     */
-    public View.OnClickListener getButtonClickListener() {
-        return mButtonClickListener;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View root = inflater.inflate(R.layout.lb_error_fragment, container, false);
-
-        mErrorFrame = (ViewGroup) root.findViewById(R.id.error_frame);
-        updateBackground();
-
-        installTitleView(inflater, mErrorFrame, savedInstanceState);
-
-        mImageView = (ImageView) root.findViewById(R.id.image);
-        updateImageDrawable();
-
-        mTextView = (TextView) root.findViewById(R.id.message);
-        updateMessage();
-
-        mButton = (Button) root.findViewById(R.id.button);
-        updateButton();
-
-        FontMetricsInt metrics = getFontMetricsInt(mTextView);
-        int underImageBaselineMargin = container.getResources().getDimensionPixelSize(
-                R.dimen.lb_error_under_image_baseline_margin);
-        setTopMargin(mTextView, underImageBaselineMargin + metrics.ascent);
-
-        int underMessageBaselineMargin = container.getResources().getDimensionPixelSize(
-                R.dimen.lb_error_under_message_baseline_margin);
-        setTopMargin(mButton, underMessageBaselineMargin - metrics.descent);
-
-        return root;
-    }
-
-    private void updateBackground() {
-        if (mErrorFrame != null) {
-            if (mBackgroundDrawable != null) {
-                mErrorFrame.setBackground(mBackgroundDrawable);
-            } else {
-                mErrorFrame.setBackgroundColor(mErrorFrame.getResources().getColor(
-                        mIsBackgroundTranslucent
-                                ? R.color.lb_error_background_color_translucent
-                                : R.color.lb_error_background_color_opaque));
-            }
-        }
-    }
-
-    private void updateMessage() {
-        if (mTextView != null) {
-            mTextView.setText(mMessage);
-            mTextView.setVisibility(TextUtils.isEmpty(mMessage) ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    private void updateImageDrawable() {
-        if (mImageView != null) {
-            mImageView.setImageDrawable(mDrawable);
-            mImageView.setVisibility(mDrawable == null ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    private void updateButton() {
-        if (mButton != null) {
-            mButton.setText(mButtonText);
-            mButton.setOnClickListener(mButtonClickListener);
-            mButton.setVisibility(TextUtils.isEmpty(mButtonText) ? View.GONE : View.VISIBLE);
-            mButton.requestFocus();
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mErrorFrame.requestFocus();
-    }
-
-    private static FontMetricsInt getFontMetricsInt(TextView textView) {
-        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        paint.setTextSize(textView.getTextSize());
-        paint.setTypeface(textView.getTypeface());
-        return paint.getFontMetricsInt();
-    }
-
-    private static void setTopMargin(TextView textView, int topMargin) {
-        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) textView.getLayoutParams();
-        lp.topMargin = topMargin;
-        textView.setLayoutParams(lp);
-    }
-
-}
diff --git a/android/support/v17/leanback/app/ErrorSupportFragment.java b/android/support/v17/leanback/app/ErrorSupportFragment.java
deleted file mode 100644
index 55e7d92..0000000
--- a/android/support/v17/leanback/app/ErrorSupportFragment.java
+++ /dev/null
@@ -1,242 +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.v17.leanback.app;
-
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetricsInt;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * A fragment for displaying an error indication.
- */
-public class ErrorSupportFragment extends BrandedSupportFragment {
-
-    private ViewGroup mErrorFrame;
-    private ImageView mImageView;
-    private TextView mTextView;
-    private Button mButton;
-    private Drawable mDrawable;
-    private CharSequence mMessage;
-    private String mButtonText;
-    private View.OnClickListener mButtonClickListener;
-    private Drawable mBackgroundDrawable;
-    private boolean mIsBackgroundTranslucent = true;
-
-    /**
-     * Sets the default background.
-     *
-     * @param translucent True to set a translucent background.
-     */
-    public void setDefaultBackground(boolean translucent) {
-        mBackgroundDrawable = null;
-        mIsBackgroundTranslucent = translucent;
-        updateBackground();
-        updateMessage();
-    }
-
-    /**
-     * Returns true if the background is translucent.
-     */
-    public boolean isBackgroundTranslucent() {
-        return mIsBackgroundTranslucent;
-    }
-
-    /**
-     * Sets a drawable for the fragment background.
-     *
-     * @param drawable The drawable used for the background.
-     */
-    public void setBackgroundDrawable(Drawable drawable) {
-        mBackgroundDrawable = drawable;
-        if (drawable != null) {
-            final int opacity = drawable.getOpacity();
-            mIsBackgroundTranslucent = (opacity == PixelFormat.TRANSLUCENT
-                    || opacity == PixelFormat.TRANSPARENT);
-        }
-        updateBackground();
-        updateMessage();
-    }
-
-    /**
-     * Returns the background drawable.  May be null if a default is used.
-     */
-    public Drawable getBackgroundDrawable() {
-        return mBackgroundDrawable;
-    }
-
-    /**
-     * Sets the drawable to be used for the error image.
-     *
-     * @param drawable The drawable used for the error image.
-     */
-    public void setImageDrawable(Drawable drawable) {
-        mDrawable = drawable;
-        updateImageDrawable();
-    }
-
-    /**
-     * Returns the drawable used for the error image.
-     */
-    public Drawable getImageDrawable() {
-        return mDrawable;
-    }
-
-    /**
-     * Sets the error message.
-     *
-     * @param message The error message.
-     */
-    public void setMessage(CharSequence message) {
-        mMessage = message;
-        updateMessage();
-    }
-
-    /**
-     * Returns the error message.
-     */
-    public CharSequence getMessage() {
-        return mMessage;
-    }
-
-    /**
-     * Sets the button text.
-     *
-     * @param text The button text.
-     */
-    public void setButtonText(String text) {
-        mButtonText = text;
-        updateButton();
-    }
-
-    /**
-     * Returns the button text.
-     */
-    public String getButtonText() {
-        return mButtonText;
-    }
-
-    /**
-     * Set the button click listener.
-     *
-     * @param clickListener The click listener for the button.
-     */
-    public void setButtonClickListener(View.OnClickListener clickListener) {
-        mButtonClickListener = clickListener;
-        updateButton();
-    }
-
-    /**
-     * Returns the button click listener.
-     */
-    public View.OnClickListener getButtonClickListener() {
-        return mButtonClickListener;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View root = inflater.inflate(R.layout.lb_error_fragment, container, false);
-
-        mErrorFrame = (ViewGroup) root.findViewById(R.id.error_frame);
-        updateBackground();
-
-        installTitleView(inflater, mErrorFrame, savedInstanceState);
-
-        mImageView = (ImageView) root.findViewById(R.id.image);
-        updateImageDrawable();
-
-        mTextView = (TextView) root.findViewById(R.id.message);
-        updateMessage();
-
-        mButton = (Button) root.findViewById(R.id.button);
-        updateButton();
-
-        FontMetricsInt metrics = getFontMetricsInt(mTextView);
-        int underImageBaselineMargin = container.getResources().getDimensionPixelSize(
-                R.dimen.lb_error_under_image_baseline_margin);
-        setTopMargin(mTextView, underImageBaselineMargin + metrics.ascent);
-
-        int underMessageBaselineMargin = container.getResources().getDimensionPixelSize(
-                R.dimen.lb_error_under_message_baseline_margin);
-        setTopMargin(mButton, underMessageBaselineMargin - metrics.descent);
-
-        return root;
-    }
-
-    private void updateBackground() {
-        if (mErrorFrame != null) {
-            if (mBackgroundDrawable != null) {
-                mErrorFrame.setBackground(mBackgroundDrawable);
-            } else {
-                mErrorFrame.setBackgroundColor(mErrorFrame.getResources().getColor(
-                        mIsBackgroundTranslucent
-                                ? R.color.lb_error_background_color_translucent
-                                : R.color.lb_error_background_color_opaque));
-            }
-        }
-    }
-
-    private void updateMessage() {
-        if (mTextView != null) {
-            mTextView.setText(mMessage);
-            mTextView.setVisibility(TextUtils.isEmpty(mMessage) ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    private void updateImageDrawable() {
-        if (mImageView != null) {
-            mImageView.setImageDrawable(mDrawable);
-            mImageView.setVisibility(mDrawable == null ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    private void updateButton() {
-        if (mButton != null) {
-            mButton.setText(mButtonText);
-            mButton.setOnClickListener(mButtonClickListener);
-            mButton.setVisibility(TextUtils.isEmpty(mButtonText) ? View.GONE : View.VISIBLE);
-            mButton.requestFocus();
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mErrorFrame.requestFocus();
-    }
-
-    private static FontMetricsInt getFontMetricsInt(TextView textView) {
-        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        paint.setTextSize(textView.getTextSize());
-        paint.setTypeface(textView.getTypeface());
-        return paint.getFontMetricsInt();
-    }
-
-    private static void setTopMargin(TextView textView, int topMargin) {
-        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) textView.getLayoutParams();
-        lp.topMargin = topMargin;
-        textView.setLayoutParams(lp);
-    }
-
-}
diff --git a/android/support/v17/leanback/app/FragmentUtil.java b/android/support/v17/leanback/app/FragmentUtil.java
deleted file mode 100644
index b555698..0000000
--- a/android/support/v17/leanback/app/FragmentUtil.java
+++ /dev/null
@@ -1,37 +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.v17.leanback.app;
-
-import android.support.annotation.RequiresApi;
-import android.app.Fragment;
-import android.content.Context;
-import android.os.Build;
-
-class FragmentUtil {
-
-    @RequiresApi(23)
-    private static Context getContextNew(Fragment fragment) {
-        return fragment.getContext();
-    }
-
-    public static Context getContext(Fragment fragment) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return getContextNew(fragment);
-        } else {
-            return fragment.getActivity();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/GuidedStepFragment.java b/android/support/v17/leanback/app/GuidedStepFragment.java
deleted file mode 100644
index 9be350d..0000000
--- a/android/support/v17/leanback/app/GuidedStepFragment.java
+++ /dev/null
@@ -1,1420 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from GuidedStepSupportFragment.java.  DO NOT MODIFY. */
-
-/*
- * 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.v17.leanback.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.widget.DiffCallback;
-import android.support.v17.leanback.widget.GuidanceStylist;
-import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
-import android.support.v17.leanback.widget.GuidedAction;
-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.app.Fragment;
-import android.app.Activity;
-import android.app.FragmentManager;
-import android.app.FragmentManager.BackStackEntry;
-import android.app.FragmentTransaction;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A GuidedStepFragment is used to guide the user through a decision or series of decisions.
- * It is composed of a guidance view on the left and a view on the right containing a list of
- * possible actions.
- * <p>
- * <h3>Basic Usage</h3>
- * <p>
- * Clients of GuidedStepFragment must create a custom subclass to attach to their Activities.
- * This custom subclass provides the information necessary to construct the user interface and
- * respond to user actions. At a minimum, subclasses should override:
- * <ul>
- * <li>{@link #onCreateGuidance}, to provide instructions to the user</li>
- * <li>{@link #onCreateActions}, to provide a set of {@link GuidedAction}s the user can take</li>
- * <li>{@link #onGuidedActionClicked}, to respond to those actions</li>
- * </ul>
- * <p>
- * Clients use following helper functions to add GuidedStepFragment to Activity or FragmentManager:
- * <ul>
- * <li>{@link #addAsRoot(Activity, GuidedStepFragment, int)}, to be called during Activity onCreate,
- * adds GuidedStepFragment as the first Fragment in activity.</li>
- * <li>{@link #add(FragmentManager, GuidedStepFragment)} or {@link #add(FragmentManager,
- * GuidedStepFragment, int)}, to add GuidedStepFragment on top of existing Fragments or
- * replacing existing GuidedStepFragment when moving forward to next step.</li>
- * <li>{@link #finishGuidedStepFragments()} can either finish the activity or pop all
- * GuidedStepFragment from stack.
- * <li>If app chooses not to use the helper function, it is the app's responsibility to call
- * {@link #setUiStyle(int)} to select fragment transition and remember the stack entry where it
- * need pops to.
- * </ul>
- * <h3>Theming and Stylists</h3>
- * <p>
- * GuidedStepFragment delegates its visual styling to classes called stylists. The {@link
- * GuidanceStylist} is responsible for the left guidance view, while the {@link
- * GuidedActionsStylist} is responsible for the right actions view. The stylists use theme
- * attributes to derive values associated with the presentation, such as colors, animations, etc.
- * Most simple visual aspects of GuidanceStylist and GuidedActionsStylist can be customized
- * via theming; see their documentation for more information.
- * <p>
- * GuidedStepFragments must have access to an appropriate theme in order for the stylists to
- * function properly.  Specifically, the fragment must receive {@link
- * android.support.v17.leanback.R.style#Theme_Leanback_GuidedStep}, or a theme whose parent is
- * is set to that theme. Themes can be provided in one of three ways:
- * <ul>
- * <li>The simplest way is to set the theme for the host Activity to the GuidedStep theme or a
- * theme that derives from it.</li>
- * <li>If the Activity already has a theme and setting its parent theme is inconvenient, the
- * existing Activity theme can have an entry added for the attribute {@link
- * android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme}. If present,
- * this theme will be used by GuidedStepFragment as an overlay to the Activity's theme.</li>
- * <li>Finally, custom subclasses of GuidedStepFragment may provide a theme through the {@link
- * #onProvideTheme} method. This can be useful if a subclass is used across multiple
- * Activities.</li>
- * </ul>
- * <p>
- * If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
- * the Activity's theme.  (Themes whose parent theme is already set to the guided step theme do not
- * need to set the guidedStepTheme attribute; if set, it will be ignored.)
- * <p>
- * If themes do not provide enough customizability, the stylists themselves may be subclassed and
- * provided to the GuidedStepFragment through the {@link #onCreateGuidanceStylist} and {@link
- * #onCreateActionsStylist} methods.  The stylists have simple hooks so that subclasses
- * may override layout files; subclasses may also have more complex logic to determine styling.
- * <p>
- * <h3>Guided sequences</h3>
- * <p>
- * GuidedStepFragments can be grouped together to provide a guided sequence. GuidedStepFragments
- * grouped as a sequence use custom animations provided by {@link GuidanceStylist} and
- * {@link GuidedActionsStylist} (or subclasses) during transitions between steps. Clients
- * should use {@link #add} to place subsequent GuidedFragments onto the fragment stack so that
- * custom animations are properly configured. (Custom animations are triggered automatically when
- * the fragment stack is subsequently popped by any normal mechanism.)
- * <p>
- * <i>Note: Currently GuidedStepFragments grouped in this way must all be defined programmatically,
- * rather than in XML. This restriction may be removed in the future.</i>
- *
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepBackground
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeight
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeightTwoPanels
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackground
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackgroundDark
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsElevation
- * @see GuidanceStylist
- * @see GuidanceStylist.Guidance
- * @see GuidedAction
- * @see GuidedActionsStylist
- * @deprecated use {@link GuidedStepSupportFragment}
- */
-@Deprecated
-public class GuidedStepFragment extends Fragment implements GuidedActionAdapter.FocusListener {
-
-    private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepFragment";
-    private static final String EXTRA_ACTION_PREFIX = "action_";
-    private static final String EXTRA_BUTTON_ACTION_PREFIX = "buttonaction_";
-
-    private static final String ENTRY_NAME_REPLACE = "GuidedStepDefault";
-
-    private static final String ENTRY_NAME_ENTRANCE = "GuidedStepEntrance";
-
-    private static final boolean IS_FRAMEWORK_FRAGMENT = true;
-
-    /**
-     * Fragment argument name for UI style.  The argument value is persisted in fragment state and
-     * used to select fragment transition. The value is initially {@link #UI_STYLE_ENTRANCE} and
-     * might be changed in one of the three helper functions:
-     * <ul>
-     * <li>{@link #addAsRoot(Activity, GuidedStepFragment, int)} sets to
-     * {@link #UI_STYLE_ACTIVITY_ROOT}</li>
-     * <li>{@link #add(FragmentManager, GuidedStepFragment)} or {@link #add(FragmentManager,
-     * GuidedStepFragment, int)} sets it to {@link #UI_STYLE_REPLACE} if there is already a
-     * GuidedStepFragment on stack.</li>
-     * <li>{@link #finishGuidedStepFragments()} changes current GuidedStepFragment to
-     * {@link #UI_STYLE_ENTRANCE} for the non activity case.  This is a special case that changes
-     * the transition settings after fragment has been created,  in order to force current
-     * GuidedStepFragment run a return transition of {@link #UI_STYLE_ENTRANCE}</li>
-     * </ul>
-     * <p>
-     * Argument value can be either:
-     * <ul>
-     * <li>{@link #UI_STYLE_REPLACE}</li>
-     * <li>{@link #UI_STYLE_ENTRANCE}</li>
-     * <li>{@link #UI_STYLE_ACTIVITY_ROOT}</li>
-     * </ul>
-     */
-    public static final String EXTRA_UI_STYLE = "uiStyle";
-
-    /**
-     * This is the case that we use GuidedStepFragment to replace another existing
-     * GuidedStepFragment when moving forward to next step. Default behavior of this style is:
-     * <ul>
-     * <li>Enter transition slides in from END(right), exit transition same as
-     * {@link #UI_STYLE_ENTRANCE}.
-     * </li>
-     * </ul>
-     */
-    public static final int UI_STYLE_REPLACE = 0;
-
-    /**
-     * @deprecated Same value as {@link #UI_STYLE_REPLACE}.
-     */
-    @Deprecated
-    public static final int UI_STYLE_DEFAULT = 0;
-
-    /**
-     * Default value for argument {@link #EXTRA_UI_STYLE}. The default value is assigned in
-     * GuidedStepFragment constructor. This is the case that we show GuidedStepFragment on top of
-     * other content. The default behavior of this style:
-     * <ul>
-     * <li>Enter transition slides in from two sides, exit transition slide out to START(left).
-     * Background will be faded in. Note: Changing exit transition by UI style is not working
-     * because fragment transition asks for exit transition before UI style is restored in Fragment
-     * .onCreate().</li>
-     * </ul>
-     * When popping multiple GuidedStepFragment, {@link #finishGuidedStepFragments()} also changes
-     * the top GuidedStepFragment to UI_STYLE_ENTRANCE in order to run the return transition
-     * (reverse of enter transition) of UI_STYLE_ENTRANCE.
-     */
-    public static final int UI_STYLE_ENTRANCE = 1;
-
-    /**
-     * One possible value of argument {@link #EXTRA_UI_STYLE}. This is the case that we show first
-     * GuidedStepFragment in a separate activity. The default behavior of this style:
-     * <ul>
-     * <li>Enter transition is assigned null (will rely on activity transition), exit transition is
-     * same as {@link #UI_STYLE_ENTRANCE}. Note: Changing exit transition by UI style is not working
-     * because fragment transition asks for exit transition before UI style is restored in
-     * Fragment.onCreate().</li>
-     * </ul>
-     */
-    public static final int UI_STYLE_ACTIVITY_ROOT = 2;
-
-    /**
-     * Animation to slide the contents from the side (left/right).
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final int SLIDE_FROM_SIDE = 0;
-
-    /**
-     * Animation to slide the contents from the bottom.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final int SLIDE_FROM_BOTTOM = 1;
-
-    private static final String TAG = "GuidedStepF";
-    private static final boolean DEBUG = false;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class DummyFragment extends Fragment {
-        @Override
-        public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                Bundle savedInstanceState) {
-            final View v = new View(inflater.getContext());
-            v.setVisibility(View.GONE);
-            return v;
-        }
-    }
-
-    private ContextThemeWrapper mThemeWrapper;
-    private GuidanceStylist mGuidanceStylist;
-    GuidedActionsStylist mActionsStylist;
-    private GuidedActionsStylist mButtonActionsStylist;
-    private GuidedActionAdapter mAdapter;
-    private GuidedActionAdapter mSubAdapter;
-    private GuidedActionAdapter mButtonAdapter;
-    private GuidedActionAdapterGroup mAdapterGroup;
-    private List<GuidedAction> mActions = new ArrayList<GuidedAction>();
-    private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>();
-    private int entranceTransitionType = SLIDE_FROM_SIDE;
-
-    public GuidedStepFragment() {
-        mGuidanceStylist = onCreateGuidanceStylist();
-        mActionsStylist = onCreateActionsStylist();
-        mButtonActionsStylist = onCreateButtonActionsStylist();
-        onProvideFragmentTransitions();
-    }
-
-    /**
-     * Creates the presenter used to style the guidance panel. The default implementation returns
-     * a basic GuidanceStylist.
-     * @return The GuidanceStylist used in this fragment.
-     */
-    public GuidanceStylist onCreateGuidanceStylist() {
-        return new GuidanceStylist();
-    }
-
-    /**
-     * Creates the presenter used to style the guided actions panel. The default implementation
-     * returns a basic GuidedActionsStylist.
-     * @return The GuidedActionsStylist used in this fragment.
-     */
-    public GuidedActionsStylist onCreateActionsStylist() {
-        return new GuidedActionsStylist();
-    }
-
-    /**
-     * Creates the presenter used to style a sided actions panel for button only.
-     * The default implementation returns a basic GuidedActionsStylist.
-     * @return The GuidedActionsStylist used in this fragment.
-     */
-    public GuidedActionsStylist onCreateButtonActionsStylist() {
-        GuidedActionsStylist stylist = new GuidedActionsStylist();
-        stylist.setAsButtonActions();
-        return stylist;
-    }
-
-    /**
-     * Returns the theme used for styling the fragment. The default returns -1, indicating that the
-     * host Activity's theme should be used.
-     * @return The theme resource ID of the theme to use in this fragment, or -1 to use the
-     * host Activity's theme.
-     */
-    public int onProvideTheme() {
-        return -1;
-    }
-
-    /**
-     * Returns the information required to provide guidance to the user. This hook is called during
-     * {@link #onCreateView}.  May be overridden to return a custom subclass of {@link
-     * GuidanceStylist.Guidance} for use in a subclass of {@link GuidanceStylist}. The default
-     * returns a Guidance object with empty fields; subclasses should override.
-     * @param savedInstanceState The saved instance state from onCreateView.
-     * @return The Guidance object representing the information used to guide the user.
-     */
-    public @NonNull Guidance onCreateGuidance(Bundle savedInstanceState) {
-        return new Guidance("", "", "", null);
-    }
-
-    /**
-     * Fills out the set of actions available to the user. This hook is called during {@link
-     * #onCreate}. The default leaves the list of actions empty; subclasses should override.
-     * @param actions A non-null, empty list ready to be populated.
-     * @param savedInstanceState The saved instance state from onCreate.
-     */
-    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
-    }
-
-    /**
-     * Fills out the set of actions shown at right available to the user. This hook is called during
-     * {@link #onCreate}. The default leaves the list of actions empty; subclasses may override.
-     * @param actions A non-null, empty list ready to be populated.
-     * @param savedInstanceState The saved instance state from onCreate.
-     */
-    public void onCreateButtonActions(@NonNull List<GuidedAction> actions,
-            Bundle savedInstanceState) {
-    }
-
-    /**
-     * Callback invoked when an action is taken by the user. Subclasses should override in
-     * order to act on the user's decisions.
-     * @param action The chosen action.
-     */
-    public void onGuidedActionClicked(GuidedAction action) {
-    }
-
-    /**
-     * Callback invoked when an action in sub actions is taken by the user. Subclasses should
-     * override in order to act on the user's decisions.  Default return value is true to close
-     * the sub actions list.
-     * @param action The chosen action.
-     * @return true to collapse the sub actions list, false to keep it expanded.
-     */
-    public boolean onSubGuidedActionClicked(GuidedAction action) {
-        return true;
-    }
-
-    /**
-     * @return True if is current expanded including subactions list or
-     * action with {@link GuidedAction#hasEditableActivatorView()} is true.
-     */
-    public boolean isExpanded() {
-        return mActionsStylist.isExpanded();
-    }
-
-    /**
-     * @return True if the sub actions list is expanded, false otherwise.
-     */
-    public boolean isSubActionsExpanded() {
-        return mActionsStylist.isSubActionsExpanded();
-    }
-
-    /**
-     * Expand a given action's sub actions list.
-     * @param action GuidedAction to expand.
-     * @see #expandAction(GuidedAction, boolean)
-     */
-    public void expandSubActions(GuidedAction action) {
-        if (!action.hasSubActions()) {
-            return;
-        }
-        expandAction(action, true);
-    }
-
-    /**
-     * Expand a given action with sub actions list or
-     * {@link GuidedAction#hasEditableActivatorView()} is true. The method must be called after
-     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} creates fragment view.
-     *
-     * @param action GuidedAction to expand.
-     * @param withTransition True to run transition animation, false otherwise.
-     */
-    public void expandAction(GuidedAction action, boolean withTransition) {
-        mActionsStylist.expandAction(action, withTransition);
-    }
-
-    /**
-     * Collapse sub actions list.
-     * @see GuidedAction#getSubActions()
-     */
-    public void collapseSubActions() {
-        collapseAction(true);
-    }
-
-    /**
-     * Collapse action which either has a sub actions list or action with
-     * {@link GuidedAction#hasEditableActivatorView()} is true.
-     *
-     * @param withTransition True to run transition animation, false otherwise.
-     */
-    public void collapseAction(boolean withTransition) {
-        if (mActionsStylist != null && mActionsStylist.getActionsGridView() != null) {
-            mActionsStylist.collapseAction(withTransition);
-        }
-    }
-
-    /**
-     * Callback invoked when an action is focused (made to be the current selection) by the user.
-     */
-    @Override
-    public void onGuidedActionFocused(GuidedAction action) {
-    }
-
-    /**
-     * Callback invoked when an action's title or description has been edited, this happens either
-     * when user clicks confirm button in IME or user closes IME window by BACK key.
-     * @deprecated Override {@link #onGuidedActionEditedAndProceed(GuidedAction)} and/or
-     *             {@link #onGuidedActionEditCanceled(GuidedAction)}.
-     */
-    @Deprecated
-    public void onGuidedActionEdited(GuidedAction action) {
-    }
-
-    /**
-     * Callback invoked when an action has been canceled editing, for example when user closes
-     * IME window by BACK key.  Default implementation calls deprecated method
-     * {@link #onGuidedActionEdited(GuidedAction)}.
-     * @param action The action which has been canceled editing.
-     */
-    public void onGuidedActionEditCanceled(GuidedAction action) {
-        onGuidedActionEdited(action);
-    }
-
-    /**
-     * Callback invoked when an action has been edited, for example when user clicks confirm button
-     * in IME window.  Default implementation calls deprecated method
-     * {@link #onGuidedActionEdited(GuidedAction)} and returns {@link GuidedAction#ACTION_ID_NEXT}.
-     *
-     * @param action The action that has been edited.
-     * @return ID of the action will be focused or {@link GuidedAction#ACTION_ID_NEXT},
-     * {@link GuidedAction#ACTION_ID_CURRENT}.
-     */
-    public long onGuidedActionEditedAndProceed(GuidedAction action) {
-        onGuidedActionEdited(action);
-        return GuidedAction.ACTION_ID_NEXT;
-    }
-
-    /**
-     * Adds the specified GuidedStepFragment to the fragment stack, replacing any existing
-     * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom
-     * transitions.  A backstack entry is added, so the fragment will be dismissed when BACK key
-     * is pressed.
-     * <li>If current fragment on stack is GuidedStepFragment: assign {@link #UI_STYLE_REPLACE}
-     * <li>If current fragment on stack is not GuidedStepFragment: assign {@link #UI_STYLE_ENTRANCE}
-     * <p>
-     * Note: currently fragments added using this method must be created programmatically rather
-     * than via XML.
-     * @param fragmentManager The FragmentManager to be used in the transaction.
-     * @param fragment The GuidedStepFragment to be inserted into the fragment stack.
-     * @return The ID returned by the call FragmentTransaction.commit.
-     */
-    public static int add(FragmentManager fragmentManager, GuidedStepFragment fragment) {
-        return add(fragmentManager, fragment, android.R.id.content);
-    }
-
-    /**
-     * Adds the specified GuidedStepFragment to the fragment stack, replacing any existing
-     * GuidedStepFragments in the stack, and configuring the fragment-to-fragment custom
-     * transitions.  A backstack entry is added, so the fragment will be dismissed when BACK key
-     * is pressed.
-     * <li>If current fragment on stack is GuidedStepFragment: assign {@link #UI_STYLE_REPLACE} and
-     * {@link #onAddSharedElementTransition(FragmentTransaction, GuidedStepFragment)} will be called
-     * to perform shared element transition between GuidedStepFragments.
-     * <li>If current fragment on stack is not GuidedStepFragment: assign {@link #UI_STYLE_ENTRANCE}
-     * <p>
-     * Note: currently fragments added using this method must be created programmatically rather
-     * than via XML.
-     * @param fragmentManager The FragmentManager to be used in the transaction.
-     * @param fragment The GuidedStepFragment to be inserted into the fragment stack.
-     * @param id The id of container to add GuidedStepFragment, can be android.R.id.content.
-     * @return The ID returned by the call FragmentTransaction.commit.
-     */
-    public static int add(FragmentManager fragmentManager, GuidedStepFragment fragment, int id) {
-        GuidedStepFragment current = getCurrentGuidedStepFragment(fragmentManager);
-        boolean inGuidedStep = current != null;
-        if (IS_FRAMEWORK_FRAGMENT && Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23
-                && !inGuidedStep) {
-            // workaround b/22631964 for framework fragment
-            fragmentManager.beginTransaction()
-                .replace(id, new DummyFragment(), TAG_LEAN_BACK_ACTIONS_FRAGMENT)
-                .commit();
-        }
-        FragmentTransaction ft = fragmentManager.beginTransaction();
-
-        fragment.setUiStyle(inGuidedStep ? UI_STYLE_REPLACE : UI_STYLE_ENTRANCE);
-        ft.addToBackStack(fragment.generateStackEntryName());
-        if (current != null) {
-            fragment.onAddSharedElementTransition(ft, current);
-        }
-        return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
-    }
-
-    /**
-     * Called when this fragment is added to FragmentTransaction with {@link #UI_STYLE_REPLACE} (aka
-     * when the GuidedStepFragment replacing an existing GuidedStepFragment). Default implementation
-     * establishes connections between action background views to morph action background bounds
-     * change from disappearing GuidedStepFragment into this GuidedStepFragment. The default
-     * implementation heavily relies on {@link GuidedActionsStylist}'s layout, app may override this
-     * method when modifying the default layout of {@link GuidedActionsStylist}.
-     *
-     * @see GuidedActionsStylist
-     * @see #onProvideFragmentTransitions()
-     * @param ft The FragmentTransaction to add shared element.
-     * @param disappearing The disappearing fragment.
-     */
-    protected void onAddSharedElementTransition(FragmentTransaction ft, GuidedStepFragment
-            disappearing) {
-        View fragmentView = disappearing.getView();
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.action_fragment_root), "action_fragment_root");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.action_fragment_background), "action_fragment_background");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.action_fragment), "action_fragment");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_root), "guidedactions_root");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_content), "guidedactions_content");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_list_background), "guidedactions_list_background");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_root2), "guidedactions_root2");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_content2), "guidedactions_content2");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_list_background2), "guidedactions_list_background2");
-    }
-
-    private static void addNonNullSharedElementTransition (FragmentTransaction ft, View subView,
-                                                           String transitionName)
-    {
-        if (subView != null)
-            TransitionHelper.addSharedElement(ft, subView, transitionName);
-    }
-
-    /**
-     * Returns BackStackEntry name for the GuidedStepFragment or empty String if no entry is
-     * associated.  Note {@link #UI_STYLE_ACTIVITY_ROOT} will return empty String.  The method
-     * returns undefined value if the fragment is not in FragmentManager.
-     * @return BackStackEntry name for the GuidedStepFragment or empty String if no entry is
-     * associated.
-     */
-    final String generateStackEntryName() {
-        return generateStackEntryName(getUiStyle(), getClass());
-    }
-
-    /**
-     * Generates BackStackEntry name for GuidedStepFragment class or empty String if no entry is
-     * associated.  Note {@link #UI_STYLE_ACTIVITY_ROOT} is not allowed and returns empty String.
-     * @param uiStyle {@link #UI_STYLE_REPLACE} or {@link #UI_STYLE_ENTRANCE}
-     * @return BackStackEntry name for the GuidedStepFragment or empty String if no entry is
-     * associated.
-     */
-    static String generateStackEntryName(int uiStyle, Class guidedStepFragmentClass) {
-        switch (uiStyle) {
-        case UI_STYLE_REPLACE:
-            return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName();
-        case UI_STYLE_ENTRANCE:
-            return ENTRY_NAME_ENTRANCE + guidedStepFragmentClass.getName();
-        case UI_STYLE_ACTIVITY_ROOT:
-        default:
-            return "";
-        }
-    }
-
-    /**
-     * Returns true if the backstack entry represents GuidedStepFragment with
-     * {@link #UI_STYLE_ENTRANCE}, i.e. this is the first GuidedStepFragment pushed to stack; false
-     * otherwise.
-     * @see #generateStackEntryName(int, Class)
-     * @param backStackEntryName Name of BackStackEntry.
-     * @return True if the backstack represents GuidedStepFragment with {@link #UI_STYLE_ENTRANCE};
-     * false otherwise.
-     */
-    static boolean isStackEntryUiStyleEntrance(String backStackEntryName) {
-        return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE);
-    }
-
-    /**
-     * Extract Class name from BackStackEntry name.
-     * @param backStackEntryName Name of BackStackEntry.
-     * @return Class name of GuidedStepFragment.
-     */
-    static String getGuidedStepFragmentClassName(String backStackEntryName) {
-        if (backStackEntryName.startsWith(ENTRY_NAME_REPLACE)) {
-            return backStackEntryName.substring(ENTRY_NAME_REPLACE.length());
-        } else if (backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE)) {
-            return backStackEntryName.substring(ENTRY_NAME_ENTRANCE.length());
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * Adds the specified GuidedStepFragment as content of Activity; no backstack entry is added so
-     * the activity will be dismissed when BACK key is pressed.  The method is typically called in
-     * Activity.onCreate() when savedInstanceState is null.  When savedInstanceState is not null,
-     * the Activity is being restored,  do not call addAsRoot() to duplicate the Fragment restored
-     * by FragmentManager.
-     * {@link #UI_STYLE_ACTIVITY_ROOT} is assigned.
-     *
-     * Note: currently fragments added using this method must be created programmatically rather
-     * than via XML.
-     * @param activity The Activity to be used to insert GuidedstepFragment.
-     * @param fragment The GuidedStepFragment to be inserted into the fragment stack.
-     * @param id The id of container to add GuidedStepFragment, can be android.R.id.content.
-     * @return The ID returned by the call FragmentTransaction.commit, or -1 there is already
-     *         GuidedStepFragment.
-     */
-    public static int addAsRoot(Activity activity, GuidedStepFragment fragment, int id) {
-        // Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition.
-        activity.getWindow().getDecorView();
-        FragmentManager fragmentManager = activity.getFragmentManager();
-        if (fragmentManager.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT) != null) {
-            Log.w(TAG, "Fragment is already exists, likely calling "
-                    + "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
-            return -1;
-        }
-        FragmentTransaction ft = fragmentManager.beginTransaction();
-        fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT);
-        return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
-    }
-
-    /**
-     * Returns the current GuidedStepFragment on the fragment transaction stack.
-     * @return The current GuidedStepFragment, if any, on the fragment transaction stack.
-     */
-    public static GuidedStepFragment getCurrentGuidedStepFragment(FragmentManager fm) {
-        Fragment f = fm.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT);
-        if (f instanceof GuidedStepFragment) {
-            return (GuidedStepFragment) f;
-        }
-        return null;
-    }
-
-    /**
-     * Returns the GuidanceStylist that displays guidance information for the user.
-     * @return The GuidanceStylist for this fragment.
-     */
-    public GuidanceStylist getGuidanceStylist() {
-        return mGuidanceStylist;
-    }
-
-    /**
-     * Returns the GuidedActionsStylist that displays the actions the user may take.
-     * @return The GuidedActionsStylist for this fragment.
-     */
-    public GuidedActionsStylist getGuidedActionsStylist() {
-        return mActionsStylist;
-    }
-
-    /**
-     * Returns the list of button GuidedActions that the user may take in this fragment.
-     * @return The list of button GuidedActions for this fragment.
-     */
-    public List<GuidedAction> getButtonActions() {
-        return mButtonActions;
-    }
-
-    /**
-     * Find button GuidedAction by Id.
-     * @param id  Id of the button action to search.
-     * @return  GuidedAction object or null if not found.
-     */
-    public GuidedAction findButtonActionById(long id) {
-        int index = findButtonActionPositionById(id);
-        return index >= 0 ? mButtonActions.get(index) : null;
-    }
-
-    /**
-     * Find button GuidedAction position in array by Id.
-     * @param id  Id of the button action to search.
-     * @return  position of GuidedAction object in array or -1 if not found.
-     */
-    public int findButtonActionPositionById(long id) {
-        if (mButtonActions != null) {
-            for (int i = 0; i < mButtonActions.size(); i++) {
-                GuidedAction action = mButtonActions.get(i);
-                if (mButtonActions.get(i).getId() == id) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Returns the GuidedActionsStylist that displays the button actions the user may take.
-     * @return The GuidedActionsStylist for this fragment.
-     */
-    public GuidedActionsStylist getGuidedButtonActionsStylist() {
-        return mButtonActionsStylist;
-    }
-
-    /**
-     * Sets the list of button GuidedActions that the user may take in this fragment.
-     * @param actions The list of button GuidedActions for this fragment.
-     */
-    public void setButtonActions(List<GuidedAction> actions) {
-        mButtonActions = actions;
-        if (mButtonAdapter != null) {
-            mButtonAdapter.setActions(mButtonActions);
-        }
-    }
-
-    /**
-     * Notify an button action has changed and update its UI.
-     * @param position Position of the button GuidedAction in array.
-     */
-    public void notifyButtonActionChanged(int position) {
-        if (mButtonAdapter != null) {
-            mButtonAdapter.notifyItemChanged(position);
-        }
-    }
-
-    /**
-     * Returns the view corresponding to the button action at the indicated position in the list of
-     * actions for this fragment.
-     * @param position The integer position of the button action of interest.
-     * @return The View corresponding to the button action at the indicated position, or null if
-     * that action is not currently onscreen.
-     */
-    public View getButtonActionItemView(int position) {
-        final RecyclerView.ViewHolder holder = mButtonActionsStylist.getActionsGridView()
-                    .findViewHolderForPosition(position);
-        return holder == null ? null : holder.itemView;
-    }
-
-    /**
-     * Scrolls the action list to the position indicated, selecting that button action's view.
-     * @param position The integer position of the button action of interest.
-     */
-    public void setSelectedButtonActionPosition(int position) {
-        mButtonActionsStylist.getActionsGridView().setSelectedPosition(position);
-    }
-
-    /**
-     * Returns the position if the currently selected button GuidedAction.
-     * @return position The integer position of the currently selected button action.
-     */
-    public int getSelectedButtonActionPosition() {
-        return mButtonActionsStylist.getActionsGridView().getSelectedPosition();
-    }
-
-    /**
-     * Returns the list of GuidedActions that the user may take in this fragment.
-     * @return The list of GuidedActions for this fragment.
-     */
-    public List<GuidedAction> getActions() {
-        return mActions;
-    }
-
-    /**
-     * Find GuidedAction by Id.
-     * @param id  Id of the action to search.
-     * @return  GuidedAction object or null if not found.
-     */
-    public GuidedAction findActionById(long id) {
-        int index = findActionPositionById(id);
-        return index >= 0 ? mActions.get(index) : null;
-    }
-
-    /**
-     * Find GuidedAction position in array by Id.
-     * @param id  Id of the action to search.
-     * @return  position of GuidedAction object in array or -1 if not found.
-     */
-    public int findActionPositionById(long id) {
-        if (mActions != null) {
-            for (int i = 0; i < mActions.size(); i++) {
-                GuidedAction action = mActions.get(i);
-                if (mActions.get(i).getId() == id) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Sets the list of GuidedActions that the user may take in this fragment.
-     * Uses DiffCallback set by {@link #setActionsDiffCallback(DiffCallback)}.
-     *
-     * @param actions The list of GuidedActions for this fragment.
-     */
-    public void setActions(List<GuidedAction> actions) {
-        mActions = actions;
-        if (mAdapter != null) {
-            mAdapter.setActions(mActions);
-        }
-    }
-
-    /**
-     * Sets the RecyclerView DiffCallback used when {@link #setActions(List)} is called. By default
-     * GuidedStepFragment uses
-     * {@link android.support.v17.leanback.widget.GuidedActionDiffCallback}.
-     * Sets it to null if app wants to refresh the whole list.
-     *
-     * @param diffCallback DiffCallback used in {@link #setActions(List)}.
-     */
-    public void setActionsDiffCallback(DiffCallback<GuidedAction> diffCallback) {
-        mAdapter.setDiffCallback(diffCallback);
-    }
-
-    /**
-     * Notify an action has changed and update its UI.
-     * @param position Position of the GuidedAction in array.
-     */
-    public void notifyActionChanged(int position) {
-        if (mAdapter != null) {
-            mAdapter.notifyItemChanged(position);
-        }
-    }
-
-    /**
-     * Returns the view corresponding to the action at the indicated position in the list of
-     * actions for this fragment.
-     * @param position The integer position of the action of interest.
-     * @return The View corresponding to the action at the indicated position, or null if that
-     * action is not currently onscreen.
-     */
-    public View getActionItemView(int position) {
-        final RecyclerView.ViewHolder holder = mActionsStylist.getActionsGridView()
-                    .findViewHolderForPosition(position);
-        return holder == null ? null : holder.itemView;
-    }
-
-    /**
-     * Scrolls the action list to the position indicated, selecting that action's view.
-     * @param position The integer position of the action of interest.
-     */
-    public void setSelectedActionPosition(int position) {
-        mActionsStylist.getActionsGridView().setSelectedPosition(position);
-    }
-
-    /**
-     * Returns the position if the currently selected GuidedAction.
-     * @return position The integer position of the currently selected action.
-     */
-    public int getSelectedActionPosition() {
-        return mActionsStylist.getActionsGridView().getSelectedPosition();
-    }
-
-    /**
-     * Called by Constructor to provide fragment transitions.  The default implementation assigns
-     * transitions based on {@link #getUiStyle()}:
-     * <ul>
-     * <li> {@link #UI_STYLE_REPLACE} Slide from/to end(right) for enter transition, slide from/to
-     * start(left) for exit transition, shared element enter transition is set to ChangeBounds.
-     * <li> {@link #UI_STYLE_ENTRANCE} Enter transition is set to slide from both sides, exit
-     * transition is same as {@link #UI_STYLE_REPLACE}, no shared element enter transition.
-     * <li> {@link #UI_STYLE_ACTIVITY_ROOT} Enter transition is set to null and app should rely on
-     * activity transition, exit transition is same as {@link #UI_STYLE_REPLACE}, no shared element
-     * enter transition.
-     * </ul>
-     * <p>
-     * The default implementation heavily relies on {@link GuidedActionsStylist} and
-     * {@link GuidanceStylist} layout, app may override this method when modifying the default
-     * layout of {@link GuidedActionsStylist} or {@link GuidanceStylist}.
-     * <p>
-     * TIP: because the fragment view is removed during fragment transition, in general app cannot
-     * use two Visibility transition together. Workaround is to create your own Visibility
-     * transition that controls multiple animators (e.g. slide and fade animation in one Transition
-     * class).
-     */
-    protected void onProvideFragmentTransitions() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            final int uiStyle = getUiStyle();
-            if (uiStyle == UI_STYLE_REPLACE) {
-                Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END);
-                TransitionHelper.exclude(enterTransition, R.id.guidedstep_background, true);
-                TransitionHelper.exclude(enterTransition, R.id.guidedactions_sub_list_background,
-                        true);
-                TransitionHelper.setEnterTransition(this, enterTransition);
-
-                Object fade = TransitionHelper.createFadeTransition(
-                        TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
-                TransitionHelper.include(fade, R.id.guidedactions_sub_list_background);
-                Object changeBounds = TransitionHelper.createChangeBounds(false);
-                Object sharedElementTransition = TransitionHelper.createTransitionSet(false);
-                TransitionHelper.addTransition(sharedElementTransition, fade);
-                TransitionHelper.addTransition(sharedElementTransition, changeBounds);
-                TransitionHelper.setSharedElementEnterTransition(this, sharedElementTransition);
-            } else if (uiStyle == UI_STYLE_ENTRANCE) {
-                if (entranceTransitionType == SLIDE_FROM_SIDE) {
-                    Object fade = TransitionHelper.createFadeTransition(
-                            TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
-                    TransitionHelper.include(fade, R.id.guidedstep_background);
-                    Object slideFromSide = TransitionHelper.createFadeAndShortSlide(
-                            Gravity.END | Gravity.START);
-                    TransitionHelper.include(slideFromSide, R.id.content_fragment);
-                    TransitionHelper.include(slideFromSide, R.id.action_fragment_root);
-                    Object enterTransition = TransitionHelper.createTransitionSet(false);
-                    TransitionHelper.addTransition(enterTransition, fade);
-                    TransitionHelper.addTransition(enterTransition, slideFromSide);
-                    TransitionHelper.setEnterTransition(this, enterTransition);
-                } else {
-                    Object slideFromBottom = TransitionHelper.createFadeAndShortSlide(
-                            Gravity.BOTTOM);
-                    TransitionHelper.include(slideFromBottom, R.id.guidedstep_background_view_root);
-                    Object enterTransition = TransitionHelper.createTransitionSet(false);
-                    TransitionHelper.addTransition(enterTransition, slideFromBottom);
-                    TransitionHelper.setEnterTransition(this, enterTransition);
-                }
-                // No shared element transition
-                TransitionHelper.setSharedElementEnterTransition(this, null);
-            } else if (uiStyle == UI_STYLE_ACTIVITY_ROOT) {
-                // for Activity root, we don't need enter transition, use activity transition
-                TransitionHelper.setEnterTransition(this, null);
-                // No shared element transition
-                TransitionHelper.setSharedElementEnterTransition(this, null);
-            }
-            // exitTransition is same for all style
-            Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START);
-            TransitionHelper.exclude(exitTransition, R.id.guidedstep_background, true);
-            TransitionHelper.exclude(exitTransition, R.id.guidedactions_sub_list_background,
-                    true);
-            TransitionHelper.setExitTransition(this, exitTransition);
-        }
-    }
-
-    /**
-     * Called by onCreateView to inflate background view.  Default implementation loads view
-     * from {@link R.layout#lb_guidedstep_background} which holds a reference to
-     * guidedStepBackground.
-     * @param inflater LayoutInflater to load background view.
-     * @param container Parent view of background view.
-     * @param savedInstanceState
-     * @return Created background view or null if no background.
-     */
-    public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        return inflater.inflate(R.layout.lb_guidedstep_background, container, false);
-    }
-
-    /**
-     * Set UI style to fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when fragment
-     * is first initialized. UI style is used to choose different fragment transition animations and
-     * determine if this is the first GuidedStepFragment on backstack. In most cases app does not
-     * directly call this method, app calls helper function
-     * {@link #add(FragmentManager, GuidedStepFragment, int)}. However if the app creates Fragment
-     * transaction and controls backstack by itself, it would need call setUiStyle() to select the
-     * fragment transition to use.
-     *
-     * @param style {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or
-     *        {@link #UI_STYLE_ENTRANCE}.
-     */
-    public void setUiStyle(int style) {
-        int oldStyle = getUiStyle();
-        Bundle arguments = getArguments();
-        boolean isNew = false;
-        if (arguments == null) {
-            arguments = new Bundle();
-            isNew = true;
-        }
-        arguments.putInt(EXTRA_UI_STYLE, style);
-        // call setArgument() will validate if the fragment is already added.
-        if (isNew) {
-            setArguments(arguments);
-        }
-        if (style != oldStyle) {
-            onProvideFragmentTransitions();
-        }
-    }
-
-    /**
-     * Read UI style from fragment arguments.  Default value is {@link #UI_STYLE_ENTRANCE} when
-     * fragment is first initialized.  UI style is used to choose different fragment transition
-     * animations and determine if this is the first GuidedStepFragment on backstack.
-     *
-     * @return {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or
-     * {@link #UI_STYLE_ENTRANCE}.
-     * @see #onProvideFragmentTransitions()
-     */
-    public int getUiStyle() {
-        Bundle b = getArguments();
-        if (b == null) return UI_STYLE_ENTRANCE;
-        return b.getInt(EXTRA_UI_STYLE, UI_STYLE_ENTRANCE);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (DEBUG) Log.v(TAG, "onCreate");
-        // Set correct transition from saved arguments.
-        onProvideFragmentTransitions();
-
-        ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>();
-        onCreateActions(actions, savedInstanceState);
-        if (savedInstanceState != null) {
-            onRestoreActions(actions, savedInstanceState);
-        }
-        setActions(actions);
-        ArrayList<GuidedAction> buttonActions = new ArrayList<GuidedAction>();
-        onCreateButtonActions(buttonActions, savedInstanceState);
-        if (savedInstanceState != null) {
-            onRestoreButtonActions(buttonActions, savedInstanceState);
-        }
-        setButtonActions(buttonActions);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onDestroyView() {
-        mGuidanceStylist.onDestroyView();
-        mActionsStylist.onDestroyView();
-        mButtonActionsStylist.onDestroyView();
-        mAdapter = null;
-        mSubAdapter =  null;
-        mButtonAdapter = null;
-        mAdapterGroup = null;
-        super.onDestroyView();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        if (DEBUG) Log.v(TAG, "onCreateView");
-
-        resolveTheme();
-        inflater = getThemeInflater(inflater);
-
-        GuidedStepRootLayout root = (GuidedStepRootLayout) inflater.inflate(
-                R.layout.lb_guidedstep_fragment, container, false);
-
-        root.setFocusOutStart(isFocusOutStartAllowed());
-        root.setFocusOutEnd(isFocusOutEndAllowed());
-
-        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);
-        guidanceContainer.addView(guidanceView);
-
-        View actionsView = mActionsStylist.onCreateView(inflater, actionContainer);
-        actionContainer.addView(actionsView);
-
-        View buttonActionsView = mButtonActionsStylist.onCreateView(inflater, actionContainer);
-        actionContainer.addView(buttonActionsView);
-
-        GuidedActionAdapter.EditListener editListener = new GuidedActionAdapter.EditListener() {
-
-                @Override
-                public void onImeOpen() {
-                    runImeAnimations(true);
-                }
-
-                @Override
-                public void onImeClose() {
-                    runImeAnimations(false);
-                }
-
-                @Override
-                public long onGuidedActionEditedAndProceed(GuidedAction action) {
-                    return GuidedStepFragment.this.onGuidedActionEditedAndProceed(action);
-                }
-
-                @Override
-                public void onGuidedActionEditCanceled(GuidedAction action) {
-                    GuidedStepFragment.this.onGuidedActionEditCanceled(action);
-                }
-        };
-
-        mAdapter = new GuidedActionAdapter(mActions, new GuidedActionAdapter.ClickListener() {
-            @Override
-            public void onGuidedActionClicked(GuidedAction action) {
-                GuidedStepFragment.this.onGuidedActionClicked(action);
-                if (isExpanded()) {
-                    collapseAction(true);
-                } else if (action.hasSubActions() || action.hasEditableActivatorView()) {
-                    expandAction(action, true);
-                }
-            }
-        }, this, mActionsStylist, false);
-        mButtonAdapter =
-                new GuidedActionAdapter(mButtonActions, new GuidedActionAdapter.ClickListener() {
-                    @Override
-                    public void onGuidedActionClicked(GuidedAction action) {
-                        GuidedStepFragment.this.onGuidedActionClicked(action);
-                    }
-                }, this, mButtonActionsStylist, false);
-        mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
-            @Override
-            public void onGuidedActionClicked(GuidedAction action) {
-                if (mActionsStylist.isInExpandTransition()) {
-                    return;
-                }
-                if (GuidedStepFragment.this.onSubGuidedActionClicked(action)) {
-                    collapseSubActions();
-                }
-            }
-        }, this, mActionsStylist, true);
-        mAdapterGroup = new GuidedActionAdapterGroup();
-        mAdapterGroup.addAdpter(mAdapter, mButtonAdapter);
-        mAdapterGroup.addAdpter(mSubAdapter, null);
-        mAdapterGroup.setEditListener(editListener);
-        mActionsStylist.setEditListener(editListener);
-
-        mActionsStylist.getActionsGridView().setAdapter(mAdapter);
-        if (mActionsStylist.getSubActionsGridView() != null) {
-            mActionsStylist.getSubActionsGridView().setAdapter(mSubAdapter);
-        }
-        mButtonActionsStylist.getActionsGridView().setAdapter(mButtonAdapter);
-        if (mButtonActions.size() == 0) {
-            // when there is no button actions, we don't need show the second panel, but keep
-            // the width zero to run ChangeBounds transition.
-            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                    buttonActionsView.getLayoutParams();
-            lp.weight = 0;
-            buttonActionsView.setLayoutParams(lp);
-        } else {
-            // when there are two actions panel, we need adjust the weight of action to
-            // guidedActionContentWidthWeightTwoPanels.
-            Context ctx = mThemeWrapper != null ? mThemeWrapper : FragmentUtil.getContext(GuidedStepFragment.this);
-            TypedValue typedValue = new TypedValue();
-            if (ctx.getTheme().resolveAttribute(R.attr.guidedActionContentWidthWeightTwoPanels,
-                    typedValue, true)) {
-                View actionsRoot = root.findViewById(R.id.action_fragment_root);
-                float weight = typedValue.getFloat();
-                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) actionsRoot
-                        .getLayoutParams();
-                lp.weight = weight;
-                actionsRoot.setLayoutParams(lp);
-            }
-        }
-
-        // Add the background view.
-        View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
-        if (backgroundView != null) {
-            FrameLayout backgroundViewRoot = (FrameLayout)root.findViewById(
-                R.id.guidedstep_background_view_root);
-            backgroundViewRoot.addView(backgroundView, 0);
-        }
-
-        return root;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        getView().findViewById(R.id.action_fragment).requestFocus();
-    }
-
-    /**
-     * Get the key will be used to save GuidedAction with Fragment.
-     * @param action GuidedAction to get key.
-     * @return Key to save the GuidedAction.
-     */
-    final String getAutoRestoreKey(GuidedAction action) {
-        return EXTRA_ACTION_PREFIX + action.getId();
-    }
-
-    /**
-     * Get the key will be used to save GuidedAction with Fragment.
-     * @param action GuidedAction to get key.
-     * @return Key to save the GuidedAction.
-     */
-    final String getButtonAutoRestoreKey(GuidedAction action) {
-        return EXTRA_BUTTON_ACTION_PREFIX + action.getId();
-    }
-
-    final static boolean isSaveEnabled(GuidedAction action) {
-        return action.isAutoSaveRestoreEnabled() && action.getId() != GuidedAction.NO_ID;
-    }
-
-    final void onRestoreActions(List<GuidedAction> actions, Bundle savedInstanceState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onRestoreInstanceState(savedInstanceState, getAutoRestoreKey(action));
-            }
-        }
-    }
-
-    final void onRestoreButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onRestoreInstanceState(savedInstanceState, getButtonAutoRestoreKey(action));
-            }
-        }
-    }
-
-    final void onSaveActions(List<GuidedAction> actions, Bundle outState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onSaveInstanceState(outState, getAutoRestoreKey(action));
-            }
-        }
-    }
-
-    final void onSaveButtonActions(List<GuidedAction> actions, Bundle outState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onSaveInstanceState(outState, getButtonAutoRestoreKey(action));
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        onSaveActions(mActions, outState);
-        onSaveButtonActions(mButtonActions, outState);
-    }
-
-    private static boolean isGuidedStepTheme(Context context) {
-        int resId = R.attr.guidedStepThemeFlag;
-        TypedValue typedValue = new TypedValue();
-        boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
-        if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found);
-        return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0;
-    }
-
-    /**
-     * Convenient method to close GuidedStepFragments on top of other content or finish Activity if
-     * GuidedStepFragments were started in a separate activity.  Pops all stack entries including
-     * {@link #UI_STYLE_ENTRANCE}; if {@link #UI_STYLE_ENTRANCE} is not found, finish the activity.
-     * Note that this method must be paired with {@link #add(FragmentManager, GuidedStepFragment,
-     * int)} which sets up the stack entry name for finding which fragment we need to pop back to.
-     */
-    public void finishGuidedStepFragments() {
-        final FragmentManager fragmentManager = getFragmentManager();
-        final int entryCount = fragmentManager.getBackStackEntryCount();
-        if (entryCount > 0) {
-            for (int i = entryCount - 1; i >= 0; i--) {
-                BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
-                if (isStackEntryUiStyleEntrance(entry.getName())) {
-                    GuidedStepFragment top = getCurrentGuidedStepFragment(fragmentManager);
-                    if (top != null) {
-                        top.setUiStyle(UI_STYLE_ENTRANCE);
-                    }
-                    fragmentManager.popBackStackImmediate(entry.getId(),
-                            FragmentManager.POP_BACK_STACK_INCLUSIVE);
-                    return;
-                }
-            }
-        }
-        ActivityCompat.finishAfterTransition(getActivity());
-    }
-
-    /**
-     * Convenient method to pop to fragment with Given class.
-     * @param  guidedStepFragmentClass  Name of the Class of GuidedStepFragment to pop to.
-     * @param flags Either 0 or {@link FragmentManager#POP_BACK_STACK_INCLUSIVE}.
-     */
-    public void popBackStackToGuidedStepFragment(Class guidedStepFragmentClass, int flags) {
-        if (!GuidedStepFragment.class.isAssignableFrom(guidedStepFragmentClass)) {
-            return;
-        }
-        final FragmentManager fragmentManager = getFragmentManager();
-        final int entryCount = fragmentManager.getBackStackEntryCount();
-        String className = guidedStepFragmentClass.getName();
-        if (entryCount > 0) {
-            for (int i = entryCount - 1; i >= 0; i--) {
-                BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
-                String entryClassName = getGuidedStepFragmentClassName(entry.getName());
-                if (className.equals(entryClassName)) {
-                    fragmentManager.popBackStackImmediate(entry.getId(), flags);
-                    return;
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if allows focus out of start edge of GuidedStepFragment, false otherwise.
-     * Default value is false, the reason is to disable FocusFinder to find focusable views
-     * beneath content of GuidedStepFragment.  Subclass may override.
-     * @return True if allows focus out of start edge of GuidedStepFragment.
-     */
-    public boolean isFocusOutStartAllowed() {
-        return false;
-    }
-
-    /**
-     * Returns true if allows focus out of end edge of GuidedStepFragment, false otherwise.
-     * Default value is false, the reason is to disable FocusFinder to find focusable views
-     * beneath content of GuidedStepFragment.  Subclass may override.
-     * @return True if allows focus out of end edge of GuidedStepFragment.
-     */
-    public boolean isFocusOutEndAllowed() {
-        return false;
-    }
-
-    /**
-     * Sets the transition type to be used for {@link #UI_STYLE_ENTRANCE} animation.
-     * Currently we provide 2 different variations for animation - slide in from
-     * side (default) or bottom.
-     *
-     * Ideally we can retrieve the screen mode settings from the theme attribute
-     * {@code Theme.Leanback.GuidedStep#guidedStepHeightWeight} and use that to
-     * determine the transition. But the fragment context to retrieve the theme
-     * isn't available on platform v23 or earlier.
-     *
-     * For now clients(subclasses) can call this method inside the constructor.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setEntranceTransitionType(int transitionType) {
-      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.
-        Context context = FragmentUtil.getContext(GuidedStepFragment.this);
-        int theme = onProvideTheme();
-        if (theme == -1 && !isGuidedStepTheme(context)) {
-            // Look up the guidedStepTheme in the activity's currently specified theme.  If it
-            // exists, replace the theme with its value.
-            int resId = R.attr.guidedStepTheme;
-            TypedValue typedValue = new TypedValue();
-            boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
-            if (DEBUG) Log.v(TAG, "Found guided step theme reference? " + found);
-            if (found) {
-                ContextThemeWrapper themeWrapper =
-                        new ContextThemeWrapper(context, typedValue.resourceId);
-                if (isGuidedStepTheme(themeWrapper)) {
-                    mThemeWrapper = themeWrapper;
-                } else {
-                    found = false;
-                    mThemeWrapper = null;
-                }
-            }
-            if (!found) {
-                Log.e(TAG, "GuidedStepFragment does not have an appropriate theme set.");
-            }
-        } else if (theme != -1) {
-            mThemeWrapper = new ContextThemeWrapper(context, theme);
-        }
-    }
-
-    private LayoutInflater getThemeInflater(LayoutInflater inflater) {
-        if (mThemeWrapper == null) {
-            return inflater;
-        } else {
-            return inflater.cloneInContext(mThemeWrapper);
-        }
-    }
-
-    private int getFirstCheckedAction() {
-        for (int i = 0, size = mActions.size(); i < size; i++) {
-            if (mActions.get(i).isChecked()) {
-                return i;
-            }
-        }
-        return 0;
-    }
-
-    void runImeAnimations(boolean entering) {
-        ArrayList<Animator> animators = new ArrayList<Animator>();
-        if (entering) {
-            mGuidanceStylist.onImeAppearing(animators);
-            mActionsStylist.onImeAppearing(animators);
-            mButtonActionsStylist.onImeAppearing(animators);
-        } else {
-            mGuidanceStylist.onImeDisappearing(animators);
-            mActionsStylist.onImeDisappearing(animators);
-            mButtonActionsStylist.onImeDisappearing(animators);
-        }
-        AnimatorSet set = new AnimatorSet();
-        set.playTogether(animators);
-        set.start();
-    }
-}
diff --git a/android/support/v17/leanback/app/GuidedStepRootLayout.java b/android/support/v17/leanback/app/GuidedStepRootLayout.java
deleted file mode 100644
index ec34bba..0000000
--- a/android/support/v17/leanback/app/GuidedStepRootLayout.java
+++ /dev/null
@@ -1,67 +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.v17.leanback.app;
-
-import android.content.Context;
-import android.support.v17.leanback.widget.Util;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-/**
- * Utility class used by GuidedStepFragment to disable focus out left/right.
- */
-class GuidedStepRootLayout extends LinearLayout {
-
-    private boolean mFocusOutStart = false;
-    private boolean mFocusOutEnd = false;
-
-    public GuidedStepRootLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public GuidedStepRootLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public void setFocusOutStart(boolean focusOutStart) {
-        mFocusOutStart = focusOutStart;
-    }
-
-    public void setFocusOutEnd(boolean focusOutEnd) {
-        mFocusOutEnd = focusOutEnd;
-    }
-
-    @Override
-    public View focusSearch(View focused, int direction) {
-        View newFocus = super.focusSearch(focused, direction);
-        if (direction == FOCUS_LEFT || direction == FOCUS_RIGHT) {
-            if (Util.isDescendant(this, newFocus)) {
-                return newFocus;
-            }
-            if (getLayoutDirection() == ViewGroup.LAYOUT_DIRECTION_LTR
-                    ? direction == FOCUS_LEFT : direction == FOCUS_RIGHT) {
-                if (!mFocusOutStart) {
-                    return focused;
-                }
-            } else {
-                if (!mFocusOutEnd) {
-                    return focused;
-                }
-            }
-        }
-        return newFocus;
-    }
-}
diff --git a/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/android/support/v17/leanback/app/GuidedStepSupportFragment.java
deleted file mode 100644
index e276d07..0000000
--- a/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ /dev/null
@@ -1,1415 +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.v17.leanback.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.widget.DiffCallback;
-import android.support.v17.leanback.widget.GuidanceStylist;
-import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
-import android.support.v17.leanback.widget.GuidedAction;
-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.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentManager.BackStackEntry;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A GuidedStepSupportFragment is used to guide the user through a decision or series of decisions.
- * It is composed of a guidance view on the left and a view on the right containing a list of
- * possible actions.
- * <p>
- * <h3>Basic Usage</h3>
- * <p>
- * Clients of GuidedStepSupportFragment must create a custom subclass to attach to their Activities.
- * This custom subclass provides the information necessary to construct the user interface and
- * respond to user actions. At a minimum, subclasses should override:
- * <ul>
- * <li>{@link #onCreateGuidance}, to provide instructions to the user</li>
- * <li>{@link #onCreateActions}, to provide a set of {@link GuidedAction}s the user can take</li>
- * <li>{@link #onGuidedActionClicked}, to respond to those actions</li>
- * </ul>
- * <p>
- * Clients use following helper functions to add GuidedStepSupportFragment to Activity or FragmentManager:
- * <ul>
- * <li>{@link #addAsRoot(FragmentActivity, GuidedStepSupportFragment, int)}, to be called during Activity onCreate,
- * adds GuidedStepSupportFragment as the first Fragment in activity.</li>
- * <li>{@link #add(FragmentManager, GuidedStepSupportFragment)} or {@link #add(FragmentManager,
- * GuidedStepSupportFragment, int)}, to add GuidedStepSupportFragment on top of existing Fragments or
- * replacing existing GuidedStepSupportFragment when moving forward to next step.</li>
- * <li>{@link #finishGuidedStepSupportFragments()} can either finish the activity or pop all
- * GuidedStepSupportFragment from stack.
- * <li>If app chooses not to use the helper function, it is the app's responsibility to call
- * {@link #setUiStyle(int)} to select fragment transition and remember the stack entry where it
- * need pops to.
- * </ul>
- * <h3>Theming and Stylists</h3>
- * <p>
- * GuidedStepSupportFragment delegates its visual styling to classes called stylists. The {@link
- * GuidanceStylist} is responsible for the left guidance view, while the {@link
- * GuidedActionsStylist} is responsible for the right actions view. The stylists use theme
- * attributes to derive values associated with the presentation, such as colors, animations, etc.
- * Most simple visual aspects of GuidanceStylist and GuidedActionsStylist can be customized
- * via theming; see their documentation for more information.
- * <p>
- * GuidedStepSupportFragments must have access to an appropriate theme in order for the stylists to
- * function properly.  Specifically, the fragment must receive {@link
- * android.support.v17.leanback.R.style#Theme_Leanback_GuidedStep}, or a theme whose parent is
- * is set to that theme. Themes can be provided in one of three ways:
- * <ul>
- * <li>The simplest way is to set the theme for the host Activity to the GuidedStep theme or a
- * theme that derives from it.</li>
- * <li>If the Activity already has a theme and setting its parent theme is inconvenient, the
- * existing Activity theme can have an entry added for the attribute {@link
- * android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme}. If present,
- * this theme will be used by GuidedStepSupportFragment as an overlay to the Activity's theme.</li>
- * <li>Finally, custom subclasses of GuidedStepSupportFragment may provide a theme through the {@link
- * #onProvideTheme} method. This can be useful if a subclass is used across multiple
- * Activities.</li>
- * </ul>
- * <p>
- * If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
- * the Activity's theme.  (Themes whose parent theme is already set to the guided step theme do not
- * need to set the guidedStepTheme attribute; if set, it will be ignored.)
- * <p>
- * If themes do not provide enough customizability, the stylists themselves may be subclassed and
- * provided to the GuidedStepSupportFragment through the {@link #onCreateGuidanceStylist} and {@link
- * #onCreateActionsStylist} methods.  The stylists have simple hooks so that subclasses
- * may override layout files; subclasses may also have more complex logic to determine styling.
- * <p>
- * <h3>Guided sequences</h3>
- * <p>
- * GuidedStepSupportFragments can be grouped together to provide a guided sequence. GuidedStepSupportFragments
- * grouped as a sequence use custom animations provided by {@link GuidanceStylist} and
- * {@link GuidedActionsStylist} (or subclasses) during transitions between steps. Clients
- * should use {@link #add} to place subsequent GuidedFragments onto the fragment stack so that
- * custom animations are properly configured. (Custom animations are triggered automatically when
- * the fragment stack is subsequently popped by any normal mechanism.)
- * <p>
- * <i>Note: Currently GuidedStepSupportFragments grouped in this way must all be defined programmatically,
- * rather than in XML. This restriction may be removed in the future.</i>
- *
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepBackground
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeight
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthWeightTwoPanels
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackground
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsBackgroundDark
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsElevation
- * @see GuidanceStylist
- * @see GuidanceStylist.Guidance
- * @see GuidedAction
- * @see GuidedActionsStylist
- */
-public class GuidedStepSupportFragment extends Fragment implements GuidedActionAdapter.FocusListener {
-
-    private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepSupportFragment";
-    private static final String EXTRA_ACTION_PREFIX = "action_";
-    private static final String EXTRA_BUTTON_ACTION_PREFIX = "buttonaction_";
-
-    private static final String ENTRY_NAME_REPLACE = "GuidedStepDefault";
-
-    private static final String ENTRY_NAME_ENTRANCE = "GuidedStepEntrance";
-
-    private static final boolean IS_FRAMEWORK_FRAGMENT = false;
-
-    /**
-     * Fragment argument name for UI style.  The argument value is persisted in fragment state and
-     * used to select fragment transition. The value is initially {@link #UI_STYLE_ENTRANCE} and
-     * might be changed in one of the three helper functions:
-     * <ul>
-     * <li>{@link #addAsRoot(FragmentActivity, GuidedStepSupportFragment, int)} sets to
-     * {@link #UI_STYLE_ACTIVITY_ROOT}</li>
-     * <li>{@link #add(FragmentManager, GuidedStepSupportFragment)} or {@link #add(FragmentManager,
-     * GuidedStepSupportFragment, int)} sets it to {@link #UI_STYLE_REPLACE} if there is already a
-     * GuidedStepSupportFragment on stack.</li>
-     * <li>{@link #finishGuidedStepSupportFragments()} changes current GuidedStepSupportFragment to
-     * {@link #UI_STYLE_ENTRANCE} for the non activity case.  This is a special case that changes
-     * the transition settings after fragment has been created,  in order to force current
-     * GuidedStepSupportFragment run a return transition of {@link #UI_STYLE_ENTRANCE}</li>
-     * </ul>
-     * <p>
-     * Argument value can be either:
-     * <ul>
-     * <li>{@link #UI_STYLE_REPLACE}</li>
-     * <li>{@link #UI_STYLE_ENTRANCE}</li>
-     * <li>{@link #UI_STYLE_ACTIVITY_ROOT}</li>
-     * </ul>
-     */
-    public static final String EXTRA_UI_STYLE = "uiStyle";
-
-    /**
-     * This is the case that we use GuidedStepSupportFragment to replace another existing
-     * GuidedStepSupportFragment when moving forward to next step. Default behavior of this style is:
-     * <ul>
-     * <li>Enter transition slides in from END(right), exit transition same as
-     * {@link #UI_STYLE_ENTRANCE}.
-     * </li>
-     * </ul>
-     */
-    public static final int UI_STYLE_REPLACE = 0;
-
-    /**
-     * @deprecated Same value as {@link #UI_STYLE_REPLACE}.
-     */
-    @Deprecated
-    public static final int UI_STYLE_DEFAULT = 0;
-
-    /**
-     * Default value for argument {@link #EXTRA_UI_STYLE}. The default value is assigned in
-     * GuidedStepSupportFragment constructor. This is the case that we show GuidedStepSupportFragment on top of
-     * other content. The default behavior of this style:
-     * <ul>
-     * <li>Enter transition slides in from two sides, exit transition slide out to START(left).
-     * Background will be faded in. Note: Changing exit transition by UI style is not working
-     * because fragment transition asks for exit transition before UI style is restored in Fragment
-     * .onCreate().</li>
-     * </ul>
-     * When popping multiple GuidedStepSupportFragment, {@link #finishGuidedStepSupportFragments()} also changes
-     * the top GuidedStepSupportFragment to UI_STYLE_ENTRANCE in order to run the return transition
-     * (reverse of enter transition) of UI_STYLE_ENTRANCE.
-     */
-    public static final int UI_STYLE_ENTRANCE = 1;
-
-    /**
-     * One possible value of argument {@link #EXTRA_UI_STYLE}. This is the case that we show first
-     * GuidedStepSupportFragment in a separate activity. The default behavior of this style:
-     * <ul>
-     * <li>Enter transition is assigned null (will rely on activity transition), exit transition is
-     * same as {@link #UI_STYLE_ENTRANCE}. Note: Changing exit transition by UI style is not working
-     * because fragment transition asks for exit transition before UI style is restored in
-     * Fragment.onCreate().</li>
-     * </ul>
-     */
-    public static final int UI_STYLE_ACTIVITY_ROOT = 2;
-
-    /**
-     * Animation to slide the contents from the side (left/right).
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final int SLIDE_FROM_SIDE = 0;
-
-    /**
-     * Animation to slide the contents from the bottom.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final int SLIDE_FROM_BOTTOM = 1;
-
-    private static final String TAG = "GuidedStepF";
-    private static final boolean DEBUG = false;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class DummyFragment extends Fragment {
-        @Override
-        public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                Bundle savedInstanceState) {
-            final View v = new View(inflater.getContext());
-            v.setVisibility(View.GONE);
-            return v;
-        }
-    }
-
-    private ContextThemeWrapper mThemeWrapper;
-    private GuidanceStylist mGuidanceStylist;
-    GuidedActionsStylist mActionsStylist;
-    private GuidedActionsStylist mButtonActionsStylist;
-    private GuidedActionAdapter mAdapter;
-    private GuidedActionAdapter mSubAdapter;
-    private GuidedActionAdapter mButtonAdapter;
-    private GuidedActionAdapterGroup mAdapterGroup;
-    private List<GuidedAction> mActions = new ArrayList<GuidedAction>();
-    private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>();
-    private int entranceTransitionType = SLIDE_FROM_SIDE;
-
-    public GuidedStepSupportFragment() {
-        mGuidanceStylist = onCreateGuidanceStylist();
-        mActionsStylist = onCreateActionsStylist();
-        mButtonActionsStylist = onCreateButtonActionsStylist();
-        onProvideFragmentTransitions();
-    }
-
-    /**
-     * Creates the presenter used to style the guidance panel. The default implementation returns
-     * a basic GuidanceStylist.
-     * @return The GuidanceStylist used in this fragment.
-     */
-    public GuidanceStylist onCreateGuidanceStylist() {
-        return new GuidanceStylist();
-    }
-
-    /**
-     * Creates the presenter used to style the guided actions panel. The default implementation
-     * returns a basic GuidedActionsStylist.
-     * @return The GuidedActionsStylist used in this fragment.
-     */
-    public GuidedActionsStylist onCreateActionsStylist() {
-        return new GuidedActionsStylist();
-    }
-
-    /**
-     * Creates the presenter used to style a sided actions panel for button only.
-     * The default implementation returns a basic GuidedActionsStylist.
-     * @return The GuidedActionsStylist used in this fragment.
-     */
-    public GuidedActionsStylist onCreateButtonActionsStylist() {
-        GuidedActionsStylist stylist = new GuidedActionsStylist();
-        stylist.setAsButtonActions();
-        return stylist;
-    }
-
-    /**
-     * Returns the theme used for styling the fragment. The default returns -1, indicating that the
-     * host Activity's theme should be used.
-     * @return The theme resource ID of the theme to use in this fragment, or -1 to use the
-     * host Activity's theme.
-     */
-    public int onProvideTheme() {
-        return -1;
-    }
-
-    /**
-     * Returns the information required to provide guidance to the user. This hook is called during
-     * {@link #onCreateView}.  May be overridden to return a custom subclass of {@link
-     * GuidanceStylist.Guidance} for use in a subclass of {@link GuidanceStylist}. The default
-     * returns a Guidance object with empty fields; subclasses should override.
-     * @param savedInstanceState The saved instance state from onCreateView.
-     * @return The Guidance object representing the information used to guide the user.
-     */
-    public @NonNull Guidance onCreateGuidance(Bundle savedInstanceState) {
-        return new Guidance("", "", "", null);
-    }
-
-    /**
-     * Fills out the set of actions available to the user. This hook is called during {@link
-     * #onCreate}. The default leaves the list of actions empty; subclasses should override.
-     * @param actions A non-null, empty list ready to be populated.
-     * @param savedInstanceState The saved instance state from onCreate.
-     */
-    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
-    }
-
-    /**
-     * Fills out the set of actions shown at right available to the user. This hook is called during
-     * {@link #onCreate}. The default leaves the list of actions empty; subclasses may override.
-     * @param actions A non-null, empty list ready to be populated.
-     * @param savedInstanceState The saved instance state from onCreate.
-     */
-    public void onCreateButtonActions(@NonNull List<GuidedAction> actions,
-            Bundle savedInstanceState) {
-    }
-
-    /**
-     * Callback invoked when an action is taken by the user. Subclasses should override in
-     * order to act on the user's decisions.
-     * @param action The chosen action.
-     */
-    public void onGuidedActionClicked(GuidedAction action) {
-    }
-
-    /**
-     * Callback invoked when an action in sub actions is taken by the user. Subclasses should
-     * override in order to act on the user's decisions.  Default return value is true to close
-     * the sub actions list.
-     * @param action The chosen action.
-     * @return true to collapse the sub actions list, false to keep it expanded.
-     */
-    public boolean onSubGuidedActionClicked(GuidedAction action) {
-        return true;
-    }
-
-    /**
-     * @return True if is current expanded including subactions list or
-     * action with {@link GuidedAction#hasEditableActivatorView()} is true.
-     */
-    public boolean isExpanded() {
-        return mActionsStylist.isExpanded();
-    }
-
-    /**
-     * @return True if the sub actions list is expanded, false otherwise.
-     */
-    public boolean isSubActionsExpanded() {
-        return mActionsStylist.isSubActionsExpanded();
-    }
-
-    /**
-     * Expand a given action's sub actions list.
-     * @param action GuidedAction to expand.
-     * @see #expandAction(GuidedAction, boolean)
-     */
-    public void expandSubActions(GuidedAction action) {
-        if (!action.hasSubActions()) {
-            return;
-        }
-        expandAction(action, true);
-    }
-
-    /**
-     * Expand a given action with sub actions list or
-     * {@link GuidedAction#hasEditableActivatorView()} is true. The method must be called after
-     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} creates fragment view.
-     *
-     * @param action GuidedAction to expand.
-     * @param withTransition True to run transition animation, false otherwise.
-     */
-    public void expandAction(GuidedAction action, boolean withTransition) {
-        mActionsStylist.expandAction(action, withTransition);
-    }
-
-    /**
-     * Collapse sub actions list.
-     * @see GuidedAction#getSubActions()
-     */
-    public void collapseSubActions() {
-        collapseAction(true);
-    }
-
-    /**
-     * Collapse action which either has a sub actions list or action with
-     * {@link GuidedAction#hasEditableActivatorView()} is true.
-     *
-     * @param withTransition True to run transition animation, false otherwise.
-     */
-    public void collapseAction(boolean withTransition) {
-        if (mActionsStylist != null && mActionsStylist.getActionsGridView() != null) {
-            mActionsStylist.collapseAction(withTransition);
-        }
-    }
-
-    /**
-     * Callback invoked when an action is focused (made to be the current selection) by the user.
-     */
-    @Override
-    public void onGuidedActionFocused(GuidedAction action) {
-    }
-
-    /**
-     * Callback invoked when an action's title or description has been edited, this happens either
-     * when user clicks confirm button in IME or user closes IME window by BACK key.
-     * @deprecated Override {@link #onGuidedActionEditedAndProceed(GuidedAction)} and/or
-     *             {@link #onGuidedActionEditCanceled(GuidedAction)}.
-     */
-    @Deprecated
-    public void onGuidedActionEdited(GuidedAction action) {
-    }
-
-    /**
-     * Callback invoked when an action has been canceled editing, for example when user closes
-     * IME window by BACK key.  Default implementation calls deprecated method
-     * {@link #onGuidedActionEdited(GuidedAction)}.
-     * @param action The action which has been canceled editing.
-     */
-    public void onGuidedActionEditCanceled(GuidedAction action) {
-        onGuidedActionEdited(action);
-    }
-
-    /**
-     * Callback invoked when an action has been edited, for example when user clicks confirm button
-     * in IME window.  Default implementation calls deprecated method
-     * {@link #onGuidedActionEdited(GuidedAction)} and returns {@link GuidedAction#ACTION_ID_NEXT}.
-     *
-     * @param action The action that has been edited.
-     * @return ID of the action will be focused or {@link GuidedAction#ACTION_ID_NEXT},
-     * {@link GuidedAction#ACTION_ID_CURRENT}.
-     */
-    public long onGuidedActionEditedAndProceed(GuidedAction action) {
-        onGuidedActionEdited(action);
-        return GuidedAction.ACTION_ID_NEXT;
-    }
-
-    /**
-     * Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
-     * GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
-     * transitions.  A backstack entry is added, so the fragment will be dismissed when BACK key
-     * is pressed.
-     * <li>If current fragment on stack is GuidedStepSupportFragment: assign {@link #UI_STYLE_REPLACE}
-     * <li>If current fragment on stack is not GuidedStepSupportFragment: assign {@link #UI_STYLE_ENTRANCE}
-     * <p>
-     * Note: currently fragments added using this method must be created programmatically rather
-     * than via XML.
-     * @param fragmentManager The FragmentManager to be used in the transaction.
-     * @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack.
-     * @return The ID returned by the call FragmentTransaction.commit.
-     */
-    public static int add(FragmentManager fragmentManager, GuidedStepSupportFragment fragment) {
-        return add(fragmentManager, fragment, android.R.id.content);
-    }
-
-    /**
-     * Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
-     * GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
-     * transitions.  A backstack entry is added, so the fragment will be dismissed when BACK key
-     * is pressed.
-     * <li>If current fragment on stack is GuidedStepSupportFragment: assign {@link #UI_STYLE_REPLACE} and
-     * {@link #onAddSharedElementTransition(FragmentTransaction, GuidedStepSupportFragment)} will be called
-     * to perform shared element transition between GuidedStepSupportFragments.
-     * <li>If current fragment on stack is not GuidedStepSupportFragment: assign {@link #UI_STYLE_ENTRANCE}
-     * <p>
-     * Note: currently fragments added using this method must be created programmatically rather
-     * than via XML.
-     * @param fragmentManager The FragmentManager to be used in the transaction.
-     * @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack.
-     * @param id The id of container to add GuidedStepSupportFragment, can be android.R.id.content.
-     * @return The ID returned by the call FragmentTransaction.commit.
-     */
-    public static int add(FragmentManager fragmentManager, GuidedStepSupportFragment fragment, int id) {
-        GuidedStepSupportFragment current = getCurrentGuidedStepSupportFragment(fragmentManager);
-        boolean inGuidedStep = current != null;
-        if (IS_FRAMEWORK_FRAGMENT && Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23
-                && !inGuidedStep) {
-            // workaround b/22631964 for framework fragment
-            fragmentManager.beginTransaction()
-                .replace(id, new DummyFragment(), TAG_LEAN_BACK_ACTIONS_FRAGMENT)
-                .commit();
-        }
-        FragmentTransaction ft = fragmentManager.beginTransaction();
-
-        fragment.setUiStyle(inGuidedStep ? UI_STYLE_REPLACE : UI_STYLE_ENTRANCE);
-        ft.addToBackStack(fragment.generateStackEntryName());
-        if (current != null) {
-            fragment.onAddSharedElementTransition(ft, current);
-        }
-        return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
-    }
-
-    /**
-     * Called when this fragment is added to FragmentTransaction with {@link #UI_STYLE_REPLACE} (aka
-     * when the GuidedStepSupportFragment replacing an existing GuidedStepSupportFragment). Default implementation
-     * establishes connections between action background views to morph action background bounds
-     * change from disappearing GuidedStepSupportFragment into this GuidedStepSupportFragment. The default
-     * implementation heavily relies on {@link GuidedActionsStylist}'s layout, app may override this
-     * method when modifying the default layout of {@link GuidedActionsStylist}.
-     *
-     * @see GuidedActionsStylist
-     * @see #onProvideFragmentTransitions()
-     * @param ft The FragmentTransaction to add shared element.
-     * @param disappearing The disappearing fragment.
-     */
-    protected void onAddSharedElementTransition(FragmentTransaction ft, GuidedStepSupportFragment
-            disappearing) {
-        View fragmentView = disappearing.getView();
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.action_fragment_root), "action_fragment_root");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.action_fragment_background), "action_fragment_background");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.action_fragment), "action_fragment");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_root), "guidedactions_root");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_content), "guidedactions_content");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_list_background), "guidedactions_list_background");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_root2), "guidedactions_root2");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_content2), "guidedactions_content2");
-        addNonNullSharedElementTransition(ft, fragmentView.findViewById(
-                R.id.guidedactions_list_background2), "guidedactions_list_background2");
-    }
-
-    private static void addNonNullSharedElementTransition (FragmentTransaction ft, View subView,
-                                                           String transitionName)
-    {
-        if (subView != null)
-            TransitionHelper.addSharedElement(ft, subView, transitionName);
-    }
-
-    /**
-     * Returns BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
-     * associated.  Note {@link #UI_STYLE_ACTIVITY_ROOT} will return empty String.  The method
-     * returns undefined value if the fragment is not in FragmentManager.
-     * @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
-     * associated.
-     */
-    final String generateStackEntryName() {
-        return generateStackEntryName(getUiStyle(), getClass());
-    }
-
-    /**
-     * Generates BackStackEntry name for GuidedStepSupportFragment class or empty String if no entry is
-     * associated.  Note {@link #UI_STYLE_ACTIVITY_ROOT} is not allowed and returns empty String.
-     * @param uiStyle {@link #UI_STYLE_REPLACE} or {@link #UI_STYLE_ENTRANCE}
-     * @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
-     * associated.
-     */
-    static String generateStackEntryName(int uiStyle, Class guidedStepFragmentClass) {
-        switch (uiStyle) {
-        case UI_STYLE_REPLACE:
-            return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName();
-        case UI_STYLE_ENTRANCE:
-            return ENTRY_NAME_ENTRANCE + guidedStepFragmentClass.getName();
-        case UI_STYLE_ACTIVITY_ROOT:
-        default:
-            return "";
-        }
-    }
-
-    /**
-     * Returns true if the backstack entry represents GuidedStepSupportFragment with
-     * {@link #UI_STYLE_ENTRANCE}, i.e. this is the first GuidedStepSupportFragment pushed to stack; false
-     * otherwise.
-     * @see #generateStackEntryName(int, Class)
-     * @param backStackEntryName Name of BackStackEntry.
-     * @return True if the backstack represents GuidedStepSupportFragment with {@link #UI_STYLE_ENTRANCE};
-     * false otherwise.
-     */
-    static boolean isStackEntryUiStyleEntrance(String backStackEntryName) {
-        return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE);
-    }
-
-    /**
-     * Extract Class name from BackStackEntry name.
-     * @param backStackEntryName Name of BackStackEntry.
-     * @return Class name of GuidedStepSupportFragment.
-     */
-    static String getGuidedStepSupportFragmentClassName(String backStackEntryName) {
-        if (backStackEntryName.startsWith(ENTRY_NAME_REPLACE)) {
-            return backStackEntryName.substring(ENTRY_NAME_REPLACE.length());
-        } else if (backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE)) {
-            return backStackEntryName.substring(ENTRY_NAME_ENTRANCE.length());
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * Adds the specified GuidedStepSupportFragment as content of Activity; no backstack entry is added so
-     * the activity will be dismissed when BACK key is pressed.  The method is typically called in
-     * Activity.onCreate() when savedInstanceState is null.  When savedInstanceState is not null,
-     * the Activity is being restored,  do not call addAsRoot() to duplicate the Fragment restored
-     * by FragmentManager.
-     * {@link #UI_STYLE_ACTIVITY_ROOT} is assigned.
-     *
-     * Note: currently fragments added using this method must be created programmatically rather
-     * than via XML.
-     * @param activity The Activity to be used to insert GuidedstepFragment.
-     * @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack.
-     * @param id The id of container to add GuidedStepSupportFragment, can be android.R.id.content.
-     * @return The ID returned by the call FragmentTransaction.commit, or -1 there is already
-     *         GuidedStepSupportFragment.
-     */
-    public static int addAsRoot(FragmentActivity activity, GuidedStepSupportFragment fragment, int id) {
-        // Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition.
-        activity.getWindow().getDecorView();
-        FragmentManager fragmentManager = activity.getSupportFragmentManager();
-        if (fragmentManager.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT) != null) {
-            Log.w(TAG, "Fragment is already exists, likely calling "
-                    + "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
-            return -1;
-        }
-        FragmentTransaction ft = fragmentManager.beginTransaction();
-        fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT);
-        return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
-    }
-
-    /**
-     * Returns the current GuidedStepSupportFragment on the fragment transaction stack.
-     * @return The current GuidedStepSupportFragment, if any, on the fragment transaction stack.
-     */
-    public static GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(FragmentManager fm) {
-        Fragment f = fm.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT);
-        if (f instanceof GuidedStepSupportFragment) {
-            return (GuidedStepSupportFragment) f;
-        }
-        return null;
-    }
-
-    /**
-     * Returns the GuidanceStylist that displays guidance information for the user.
-     * @return The GuidanceStylist for this fragment.
-     */
-    public GuidanceStylist getGuidanceStylist() {
-        return mGuidanceStylist;
-    }
-
-    /**
-     * Returns the GuidedActionsStylist that displays the actions the user may take.
-     * @return The GuidedActionsStylist for this fragment.
-     */
-    public GuidedActionsStylist getGuidedActionsStylist() {
-        return mActionsStylist;
-    }
-
-    /**
-     * Returns the list of button GuidedActions that the user may take in this fragment.
-     * @return The list of button GuidedActions for this fragment.
-     */
-    public List<GuidedAction> getButtonActions() {
-        return mButtonActions;
-    }
-
-    /**
-     * Find button GuidedAction by Id.
-     * @param id  Id of the button action to search.
-     * @return  GuidedAction object or null if not found.
-     */
-    public GuidedAction findButtonActionById(long id) {
-        int index = findButtonActionPositionById(id);
-        return index >= 0 ? mButtonActions.get(index) : null;
-    }
-
-    /**
-     * Find button GuidedAction position in array by Id.
-     * @param id  Id of the button action to search.
-     * @return  position of GuidedAction object in array or -1 if not found.
-     */
-    public int findButtonActionPositionById(long id) {
-        if (mButtonActions != null) {
-            for (int i = 0; i < mButtonActions.size(); i++) {
-                GuidedAction action = mButtonActions.get(i);
-                if (mButtonActions.get(i).getId() == id) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Returns the GuidedActionsStylist that displays the button actions the user may take.
-     * @return The GuidedActionsStylist for this fragment.
-     */
-    public GuidedActionsStylist getGuidedButtonActionsStylist() {
-        return mButtonActionsStylist;
-    }
-
-    /**
-     * Sets the list of button GuidedActions that the user may take in this fragment.
-     * @param actions The list of button GuidedActions for this fragment.
-     */
-    public void setButtonActions(List<GuidedAction> actions) {
-        mButtonActions = actions;
-        if (mButtonAdapter != null) {
-            mButtonAdapter.setActions(mButtonActions);
-        }
-    }
-
-    /**
-     * Notify an button action has changed and update its UI.
-     * @param position Position of the button GuidedAction in array.
-     */
-    public void notifyButtonActionChanged(int position) {
-        if (mButtonAdapter != null) {
-            mButtonAdapter.notifyItemChanged(position);
-        }
-    }
-
-    /**
-     * Returns the view corresponding to the button action at the indicated position in the list of
-     * actions for this fragment.
-     * @param position The integer position of the button action of interest.
-     * @return The View corresponding to the button action at the indicated position, or null if
-     * that action is not currently onscreen.
-     */
-    public View getButtonActionItemView(int position) {
-        final RecyclerView.ViewHolder holder = mButtonActionsStylist.getActionsGridView()
-                    .findViewHolderForPosition(position);
-        return holder == null ? null : holder.itemView;
-    }
-
-    /**
-     * Scrolls the action list to the position indicated, selecting that button action's view.
-     * @param position The integer position of the button action of interest.
-     */
-    public void setSelectedButtonActionPosition(int position) {
-        mButtonActionsStylist.getActionsGridView().setSelectedPosition(position);
-    }
-
-    /**
-     * Returns the position if the currently selected button GuidedAction.
-     * @return position The integer position of the currently selected button action.
-     */
-    public int getSelectedButtonActionPosition() {
-        return mButtonActionsStylist.getActionsGridView().getSelectedPosition();
-    }
-
-    /**
-     * Returns the list of GuidedActions that the user may take in this fragment.
-     * @return The list of GuidedActions for this fragment.
-     */
-    public List<GuidedAction> getActions() {
-        return mActions;
-    }
-
-    /**
-     * Find GuidedAction by Id.
-     * @param id  Id of the action to search.
-     * @return  GuidedAction object or null if not found.
-     */
-    public GuidedAction findActionById(long id) {
-        int index = findActionPositionById(id);
-        return index >= 0 ? mActions.get(index) : null;
-    }
-
-    /**
-     * Find GuidedAction position in array by Id.
-     * @param id  Id of the action to search.
-     * @return  position of GuidedAction object in array or -1 if not found.
-     */
-    public int findActionPositionById(long id) {
-        if (mActions != null) {
-            for (int i = 0; i < mActions.size(); i++) {
-                GuidedAction action = mActions.get(i);
-                if (mActions.get(i).getId() == id) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Sets the list of GuidedActions that the user may take in this fragment.
-     * Uses DiffCallback set by {@link #setActionsDiffCallback(DiffCallback)}.
-     *
-     * @param actions The list of GuidedActions for this fragment.
-     */
-    public void setActions(List<GuidedAction> actions) {
-        mActions = actions;
-        if (mAdapter != null) {
-            mAdapter.setActions(mActions);
-        }
-    }
-
-    /**
-     * Sets the RecyclerView DiffCallback used when {@link #setActions(List)} is called. By default
-     * GuidedStepSupportFragment uses
-     * {@link android.support.v17.leanback.widget.GuidedActionDiffCallback}.
-     * Sets it to null if app wants to refresh the whole list.
-     *
-     * @param diffCallback DiffCallback used in {@link #setActions(List)}.
-     */
-    public void setActionsDiffCallback(DiffCallback<GuidedAction> diffCallback) {
-        mAdapter.setDiffCallback(diffCallback);
-    }
-
-    /**
-     * Notify an action has changed and update its UI.
-     * @param position Position of the GuidedAction in array.
-     */
-    public void notifyActionChanged(int position) {
-        if (mAdapter != null) {
-            mAdapter.notifyItemChanged(position);
-        }
-    }
-
-    /**
-     * Returns the view corresponding to the action at the indicated position in the list of
-     * actions for this fragment.
-     * @param position The integer position of the action of interest.
-     * @return The View corresponding to the action at the indicated position, or null if that
-     * action is not currently onscreen.
-     */
-    public View getActionItemView(int position) {
-        final RecyclerView.ViewHolder holder = mActionsStylist.getActionsGridView()
-                    .findViewHolderForPosition(position);
-        return holder == null ? null : holder.itemView;
-    }
-
-    /**
-     * Scrolls the action list to the position indicated, selecting that action's view.
-     * @param position The integer position of the action of interest.
-     */
-    public void setSelectedActionPosition(int position) {
-        mActionsStylist.getActionsGridView().setSelectedPosition(position);
-    }
-
-    /**
-     * Returns the position if the currently selected GuidedAction.
-     * @return position The integer position of the currently selected action.
-     */
-    public int getSelectedActionPosition() {
-        return mActionsStylist.getActionsGridView().getSelectedPosition();
-    }
-
-    /**
-     * Called by Constructor to provide fragment transitions.  The default implementation assigns
-     * transitions based on {@link #getUiStyle()}:
-     * <ul>
-     * <li> {@link #UI_STYLE_REPLACE} Slide from/to end(right) for enter transition, slide from/to
-     * start(left) for exit transition, shared element enter transition is set to ChangeBounds.
-     * <li> {@link #UI_STYLE_ENTRANCE} Enter transition is set to slide from both sides, exit
-     * transition is same as {@link #UI_STYLE_REPLACE}, no shared element enter transition.
-     * <li> {@link #UI_STYLE_ACTIVITY_ROOT} Enter transition is set to null and app should rely on
-     * activity transition, exit transition is same as {@link #UI_STYLE_REPLACE}, no shared element
-     * enter transition.
-     * </ul>
-     * <p>
-     * The default implementation heavily relies on {@link GuidedActionsStylist} and
-     * {@link GuidanceStylist} layout, app may override this method when modifying the default
-     * layout of {@link GuidedActionsStylist} or {@link GuidanceStylist}.
-     * <p>
-     * TIP: because the fragment view is removed during fragment transition, in general app cannot
-     * use two Visibility transition together. Workaround is to create your own Visibility
-     * transition that controls multiple animators (e.g. slide and fade animation in one Transition
-     * class).
-     */
-    protected void onProvideFragmentTransitions() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            final int uiStyle = getUiStyle();
-            if (uiStyle == UI_STYLE_REPLACE) {
-                Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END);
-                TransitionHelper.exclude(enterTransition, R.id.guidedstep_background, true);
-                TransitionHelper.exclude(enterTransition, R.id.guidedactions_sub_list_background,
-                        true);
-                TransitionHelper.setEnterTransition(this, enterTransition);
-
-                Object fade = TransitionHelper.createFadeTransition(
-                        TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
-                TransitionHelper.include(fade, R.id.guidedactions_sub_list_background);
-                Object changeBounds = TransitionHelper.createChangeBounds(false);
-                Object sharedElementTransition = TransitionHelper.createTransitionSet(false);
-                TransitionHelper.addTransition(sharedElementTransition, fade);
-                TransitionHelper.addTransition(sharedElementTransition, changeBounds);
-                TransitionHelper.setSharedElementEnterTransition(this, sharedElementTransition);
-            } else if (uiStyle == UI_STYLE_ENTRANCE) {
-                if (entranceTransitionType == SLIDE_FROM_SIDE) {
-                    Object fade = TransitionHelper.createFadeTransition(
-                            TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
-                    TransitionHelper.include(fade, R.id.guidedstep_background);
-                    Object slideFromSide = TransitionHelper.createFadeAndShortSlide(
-                            Gravity.END | Gravity.START);
-                    TransitionHelper.include(slideFromSide, R.id.content_fragment);
-                    TransitionHelper.include(slideFromSide, R.id.action_fragment_root);
-                    Object enterTransition = TransitionHelper.createTransitionSet(false);
-                    TransitionHelper.addTransition(enterTransition, fade);
-                    TransitionHelper.addTransition(enterTransition, slideFromSide);
-                    TransitionHelper.setEnterTransition(this, enterTransition);
-                } else {
-                    Object slideFromBottom = TransitionHelper.createFadeAndShortSlide(
-                            Gravity.BOTTOM);
-                    TransitionHelper.include(slideFromBottom, R.id.guidedstep_background_view_root);
-                    Object enterTransition = TransitionHelper.createTransitionSet(false);
-                    TransitionHelper.addTransition(enterTransition, slideFromBottom);
-                    TransitionHelper.setEnterTransition(this, enterTransition);
-                }
-                // No shared element transition
-                TransitionHelper.setSharedElementEnterTransition(this, null);
-            } else if (uiStyle == UI_STYLE_ACTIVITY_ROOT) {
-                // for Activity root, we don't need enter transition, use activity transition
-                TransitionHelper.setEnterTransition(this, null);
-                // No shared element transition
-                TransitionHelper.setSharedElementEnterTransition(this, null);
-            }
-            // exitTransition is same for all style
-            Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START);
-            TransitionHelper.exclude(exitTransition, R.id.guidedstep_background, true);
-            TransitionHelper.exclude(exitTransition, R.id.guidedactions_sub_list_background,
-                    true);
-            TransitionHelper.setExitTransition(this, exitTransition);
-        }
-    }
-
-    /**
-     * Called by onCreateView to inflate background view.  Default implementation loads view
-     * from {@link R.layout#lb_guidedstep_background} which holds a reference to
-     * guidedStepBackground.
-     * @param inflater LayoutInflater to load background view.
-     * @param container Parent view of background view.
-     * @param savedInstanceState
-     * @return Created background view or null if no background.
-     */
-    public View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        return inflater.inflate(R.layout.lb_guidedstep_background, container, false);
-    }
-
-    /**
-     * Set UI style to fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when fragment
-     * is first initialized. UI style is used to choose different fragment transition animations and
-     * determine if this is the first GuidedStepSupportFragment on backstack. In most cases app does not
-     * directly call this method, app calls helper function
-     * {@link #add(FragmentManager, GuidedStepSupportFragment, int)}. However if the app creates Fragment
-     * transaction and controls backstack by itself, it would need call setUiStyle() to select the
-     * fragment transition to use.
-     *
-     * @param style {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or
-     *        {@link #UI_STYLE_ENTRANCE}.
-     */
-    public void setUiStyle(int style) {
-        int oldStyle = getUiStyle();
-        Bundle arguments = getArguments();
-        boolean isNew = false;
-        if (arguments == null) {
-            arguments = new Bundle();
-            isNew = true;
-        }
-        arguments.putInt(EXTRA_UI_STYLE, style);
-        // call setArgument() will validate if the fragment is already added.
-        if (isNew) {
-            setArguments(arguments);
-        }
-        if (style != oldStyle) {
-            onProvideFragmentTransitions();
-        }
-    }
-
-    /**
-     * Read UI style from fragment arguments.  Default value is {@link #UI_STYLE_ENTRANCE} when
-     * fragment is first initialized.  UI style is used to choose different fragment transition
-     * animations and determine if this is the first GuidedStepSupportFragment on backstack.
-     *
-     * @return {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or
-     * {@link #UI_STYLE_ENTRANCE}.
-     * @see #onProvideFragmentTransitions()
-     */
-    public int getUiStyle() {
-        Bundle b = getArguments();
-        if (b == null) return UI_STYLE_ENTRANCE;
-        return b.getInt(EXTRA_UI_STYLE, UI_STYLE_ENTRANCE);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (DEBUG) Log.v(TAG, "onCreate");
-        // Set correct transition from saved arguments.
-        onProvideFragmentTransitions();
-
-        ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>();
-        onCreateActions(actions, savedInstanceState);
-        if (savedInstanceState != null) {
-            onRestoreActions(actions, savedInstanceState);
-        }
-        setActions(actions);
-        ArrayList<GuidedAction> buttonActions = new ArrayList<GuidedAction>();
-        onCreateButtonActions(buttonActions, savedInstanceState);
-        if (savedInstanceState != null) {
-            onRestoreButtonActions(buttonActions, savedInstanceState);
-        }
-        setButtonActions(buttonActions);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onDestroyView() {
-        mGuidanceStylist.onDestroyView();
-        mActionsStylist.onDestroyView();
-        mButtonActionsStylist.onDestroyView();
-        mAdapter = null;
-        mSubAdapter =  null;
-        mButtonAdapter = null;
-        mAdapterGroup = null;
-        super.onDestroyView();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        if (DEBUG) Log.v(TAG, "onCreateView");
-
-        resolveTheme();
-        inflater = getThemeInflater(inflater);
-
-        GuidedStepRootLayout root = (GuidedStepRootLayout) inflater.inflate(
-                R.layout.lb_guidedstep_fragment, container, false);
-
-        root.setFocusOutStart(isFocusOutStartAllowed());
-        root.setFocusOutEnd(isFocusOutEndAllowed());
-
-        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);
-        guidanceContainer.addView(guidanceView);
-
-        View actionsView = mActionsStylist.onCreateView(inflater, actionContainer);
-        actionContainer.addView(actionsView);
-
-        View buttonActionsView = mButtonActionsStylist.onCreateView(inflater, actionContainer);
-        actionContainer.addView(buttonActionsView);
-
-        GuidedActionAdapter.EditListener editListener = new GuidedActionAdapter.EditListener() {
-
-                @Override
-                public void onImeOpen() {
-                    runImeAnimations(true);
-                }
-
-                @Override
-                public void onImeClose() {
-                    runImeAnimations(false);
-                }
-
-                @Override
-                public long onGuidedActionEditedAndProceed(GuidedAction action) {
-                    return GuidedStepSupportFragment.this.onGuidedActionEditedAndProceed(action);
-                }
-
-                @Override
-                public void onGuidedActionEditCanceled(GuidedAction action) {
-                    GuidedStepSupportFragment.this.onGuidedActionEditCanceled(action);
-                }
-        };
-
-        mAdapter = new GuidedActionAdapter(mActions, new GuidedActionAdapter.ClickListener() {
-            @Override
-            public void onGuidedActionClicked(GuidedAction action) {
-                GuidedStepSupportFragment.this.onGuidedActionClicked(action);
-                if (isExpanded()) {
-                    collapseAction(true);
-                } else if (action.hasSubActions() || action.hasEditableActivatorView()) {
-                    expandAction(action, true);
-                }
-            }
-        }, this, mActionsStylist, false);
-        mButtonAdapter =
-                new GuidedActionAdapter(mButtonActions, new GuidedActionAdapter.ClickListener() {
-                    @Override
-                    public void onGuidedActionClicked(GuidedAction action) {
-                        GuidedStepSupportFragment.this.onGuidedActionClicked(action);
-                    }
-                }, this, mButtonActionsStylist, false);
-        mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
-            @Override
-            public void onGuidedActionClicked(GuidedAction action) {
-                if (mActionsStylist.isInExpandTransition()) {
-                    return;
-                }
-                if (GuidedStepSupportFragment.this.onSubGuidedActionClicked(action)) {
-                    collapseSubActions();
-                }
-            }
-        }, this, mActionsStylist, true);
-        mAdapterGroup = new GuidedActionAdapterGroup();
-        mAdapterGroup.addAdpter(mAdapter, mButtonAdapter);
-        mAdapterGroup.addAdpter(mSubAdapter, null);
-        mAdapterGroup.setEditListener(editListener);
-        mActionsStylist.setEditListener(editListener);
-
-        mActionsStylist.getActionsGridView().setAdapter(mAdapter);
-        if (mActionsStylist.getSubActionsGridView() != null) {
-            mActionsStylist.getSubActionsGridView().setAdapter(mSubAdapter);
-        }
-        mButtonActionsStylist.getActionsGridView().setAdapter(mButtonAdapter);
-        if (mButtonActions.size() == 0) {
-            // when there is no button actions, we don't need show the second panel, but keep
-            // the width zero to run ChangeBounds transition.
-            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                    buttonActionsView.getLayoutParams();
-            lp.weight = 0;
-            buttonActionsView.setLayoutParams(lp);
-        } else {
-            // when there are two actions panel, we need adjust the weight of action to
-            // guidedActionContentWidthWeightTwoPanels.
-            Context ctx = mThemeWrapper != null ? mThemeWrapper : getContext();
-            TypedValue typedValue = new TypedValue();
-            if (ctx.getTheme().resolveAttribute(R.attr.guidedActionContentWidthWeightTwoPanels,
-                    typedValue, true)) {
-                View actionsRoot = root.findViewById(R.id.action_fragment_root);
-                float weight = typedValue.getFloat();
-                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) actionsRoot
-                        .getLayoutParams();
-                lp.weight = weight;
-                actionsRoot.setLayoutParams(lp);
-            }
-        }
-
-        // Add the background view.
-        View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
-        if (backgroundView != null) {
-            FrameLayout backgroundViewRoot = (FrameLayout)root.findViewById(
-                R.id.guidedstep_background_view_root);
-            backgroundViewRoot.addView(backgroundView, 0);
-        }
-
-        return root;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        getView().findViewById(R.id.action_fragment).requestFocus();
-    }
-
-    /**
-     * Get the key will be used to save GuidedAction with Fragment.
-     * @param action GuidedAction to get key.
-     * @return Key to save the GuidedAction.
-     */
-    final String getAutoRestoreKey(GuidedAction action) {
-        return EXTRA_ACTION_PREFIX + action.getId();
-    }
-
-    /**
-     * Get the key will be used to save GuidedAction with Fragment.
-     * @param action GuidedAction to get key.
-     * @return Key to save the GuidedAction.
-     */
-    final String getButtonAutoRestoreKey(GuidedAction action) {
-        return EXTRA_BUTTON_ACTION_PREFIX + action.getId();
-    }
-
-    final static boolean isSaveEnabled(GuidedAction action) {
-        return action.isAutoSaveRestoreEnabled() && action.getId() != GuidedAction.NO_ID;
-    }
-
-    final void onRestoreActions(List<GuidedAction> actions, Bundle savedInstanceState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onRestoreInstanceState(savedInstanceState, getAutoRestoreKey(action));
-            }
-        }
-    }
-
-    final void onRestoreButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onRestoreInstanceState(savedInstanceState, getButtonAutoRestoreKey(action));
-            }
-        }
-    }
-
-    final void onSaveActions(List<GuidedAction> actions, Bundle outState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onSaveInstanceState(outState, getAutoRestoreKey(action));
-            }
-        }
-    }
-
-    final void onSaveButtonActions(List<GuidedAction> actions, Bundle outState) {
-        for (int i = 0, size = actions.size(); i < size; i++) {
-            GuidedAction action = actions.get(i);
-            if (isSaveEnabled(action)) {
-                action.onSaveInstanceState(outState, getButtonAutoRestoreKey(action));
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        onSaveActions(mActions, outState);
-        onSaveButtonActions(mButtonActions, outState);
-    }
-
-    private static boolean isGuidedStepTheme(Context context) {
-        int resId = R.attr.guidedStepThemeFlag;
-        TypedValue typedValue = new TypedValue();
-        boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
-        if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found);
-        return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0;
-    }
-
-    /**
-     * Convenient method to close GuidedStepSupportFragments on top of other content or finish Activity if
-     * GuidedStepSupportFragments were started in a separate activity.  Pops all stack entries including
-     * {@link #UI_STYLE_ENTRANCE}; if {@link #UI_STYLE_ENTRANCE} is not found, finish the activity.
-     * Note that this method must be paired with {@link #add(FragmentManager, GuidedStepSupportFragment,
-     * int)} which sets up the stack entry name for finding which fragment we need to pop back to.
-     */
-    public void finishGuidedStepSupportFragments() {
-        final FragmentManager fragmentManager = getFragmentManager();
-        final int entryCount = fragmentManager.getBackStackEntryCount();
-        if (entryCount > 0) {
-            for (int i = entryCount - 1; i >= 0; i--) {
-                BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
-                if (isStackEntryUiStyleEntrance(entry.getName())) {
-                    GuidedStepSupportFragment top = getCurrentGuidedStepSupportFragment(fragmentManager);
-                    if (top != null) {
-                        top.setUiStyle(UI_STYLE_ENTRANCE);
-                    }
-                    fragmentManager.popBackStackImmediate(entry.getId(),
-                            FragmentManager.POP_BACK_STACK_INCLUSIVE);
-                    return;
-                }
-            }
-        }
-        ActivityCompat.finishAfterTransition(getActivity());
-    }
-
-    /**
-     * Convenient method to pop to fragment with Given class.
-     * @param  guidedStepFragmentClass  Name of the Class of GuidedStepSupportFragment to pop to.
-     * @param flags Either 0 or {@link FragmentManager#POP_BACK_STACK_INCLUSIVE}.
-     */
-    public void popBackStackToGuidedStepSupportFragment(Class guidedStepFragmentClass, int flags) {
-        if (!GuidedStepSupportFragment.class.isAssignableFrom(guidedStepFragmentClass)) {
-            return;
-        }
-        final FragmentManager fragmentManager = getFragmentManager();
-        final int entryCount = fragmentManager.getBackStackEntryCount();
-        String className = guidedStepFragmentClass.getName();
-        if (entryCount > 0) {
-            for (int i = entryCount - 1; i >= 0; i--) {
-                BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
-                String entryClassName = getGuidedStepSupportFragmentClassName(entry.getName());
-                if (className.equals(entryClassName)) {
-                    fragmentManager.popBackStackImmediate(entry.getId(), flags);
-                    return;
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if allows focus out of start edge of GuidedStepSupportFragment, false otherwise.
-     * Default value is false, the reason is to disable FocusFinder to find focusable views
-     * beneath content of GuidedStepSupportFragment.  Subclass may override.
-     * @return True if allows focus out of start edge of GuidedStepSupportFragment.
-     */
-    public boolean isFocusOutStartAllowed() {
-        return false;
-    }
-
-    /**
-     * Returns true if allows focus out of end edge of GuidedStepSupportFragment, false otherwise.
-     * Default value is false, the reason is to disable FocusFinder to find focusable views
-     * beneath content of GuidedStepSupportFragment.  Subclass may override.
-     * @return True if allows focus out of end edge of GuidedStepSupportFragment.
-     */
-    public boolean isFocusOutEndAllowed() {
-        return false;
-    }
-
-    /**
-     * Sets the transition type to be used for {@link #UI_STYLE_ENTRANCE} animation.
-     * Currently we provide 2 different variations for animation - slide in from
-     * side (default) or bottom.
-     *
-     * Ideally we can retrieve the screen mode settings from the theme attribute
-     * {@code Theme.Leanback.GuidedStep#guidedStepHeightWeight} and use that to
-     * determine the transition. But the fragment context to retrieve the theme
-     * isn't available on platform v23 or earlier.
-     *
-     * For now clients(subclasses) can call this method inside the constructor.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setEntranceTransitionType(int transitionType) {
-      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.
-        Context context = getContext();
-        int theme = onProvideTheme();
-        if (theme == -1 && !isGuidedStepTheme(context)) {
-            // Look up the guidedStepTheme in the activity's currently specified theme.  If it
-            // exists, replace the theme with its value.
-            int resId = R.attr.guidedStepTheme;
-            TypedValue typedValue = new TypedValue();
-            boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
-            if (DEBUG) Log.v(TAG, "Found guided step theme reference? " + found);
-            if (found) {
-                ContextThemeWrapper themeWrapper =
-                        new ContextThemeWrapper(context, typedValue.resourceId);
-                if (isGuidedStepTheme(themeWrapper)) {
-                    mThemeWrapper = themeWrapper;
-                } else {
-                    found = false;
-                    mThemeWrapper = null;
-                }
-            }
-            if (!found) {
-                Log.e(TAG, "GuidedStepSupportFragment does not have an appropriate theme set.");
-            }
-        } else if (theme != -1) {
-            mThemeWrapper = new ContextThemeWrapper(context, theme);
-        }
-    }
-
-    private LayoutInflater getThemeInflater(LayoutInflater inflater) {
-        if (mThemeWrapper == null) {
-            return inflater;
-        } else {
-            return inflater.cloneInContext(mThemeWrapper);
-        }
-    }
-
-    private int getFirstCheckedAction() {
-        for (int i = 0, size = mActions.size(); i < size; i++) {
-            if (mActions.get(i).isChecked()) {
-                return i;
-            }
-        }
-        return 0;
-    }
-
-    void runImeAnimations(boolean entering) {
-        ArrayList<Animator> animators = new ArrayList<Animator>();
-        if (entering) {
-            mGuidanceStylist.onImeAppearing(animators);
-            mActionsStylist.onImeAppearing(animators);
-            mButtonActionsStylist.onImeAppearing(animators);
-        } else {
-            mGuidanceStylist.onImeDisappearing(animators);
-            mActionsStylist.onImeDisappearing(animators);
-            mButtonActionsStylist.onImeDisappearing(animators);
-        }
-        AnimatorSet set = new AnimatorSet();
-        set.playTogether(animators);
-        set.start();
-    }
-}
diff --git a/android/support/v17/leanback/app/HeadersFragment.java b/android/support/v17/leanback/app/HeadersFragment.java
deleted file mode 100644
index 08780a5..0000000
--- a/android/support/v17/leanback/app/HeadersFragment.java
+++ /dev/null
@@ -1,309 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from HeadersSupportFragment.java.  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.
- */
-
-package android.support.v17.leanback.app;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.ClassPresenterSelector;
-import android.support.v17.leanback.widget.DividerPresenter;
-import android.support.v17.leanback.widget.DividerRow;
-import android.support.v17.leanback.widget.FocusHighlightHelper;
-import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowHeaderPresenter;
-import android.support.v17.leanback.widget.SectionRow;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * An fragment containing a list of row headers. Implementation must support three types of rows:
- * <ul>
- *     <li>{@link DividerRow} rendered by {@link DividerPresenter}.</li>
- *     <li>{@link Row} rendered by {@link RowHeaderPresenter}.</li>
- *     <li>{@link SectionRow} rendered by {@link RowHeaderPresenter}.</li>
- * </ul>
- * Use {@link #setPresenterSelector(PresenterSelector)} in subclass constructor to customize
- * Presenters. App may override {@link BrowseFragment#onCreateHeadersFragment()}.
- * @deprecated use {@link HeadersSupportFragment}
- */
-@Deprecated
-public class HeadersFragment extends BaseRowFragment {
-
-    /**
-     * Interface definition for a callback to be invoked when a header item is clicked.
-     * @deprecated use {@link HeadersSupportFragment}
-     */
-    @Deprecated
-    public interface OnHeaderClickedListener {
-        /**
-         * Called when a header item has been clicked.
-         *
-         * @param viewHolder Row ViewHolder object corresponding to the selected Header.
-         * @param row Row object corresponding to the selected Header.
-         */
-        void onHeaderClicked(RowHeaderPresenter.ViewHolder viewHolder, Row row);
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when a header item is selected.
-     * @deprecated use {@link HeadersSupportFragment}
-     */
-    @Deprecated
-    public interface OnHeaderViewSelectedListener {
-        /**
-         * Called when a header item has been selected.
-         *
-         * @param viewHolder Row ViewHolder object corresponding to the selected Header.
-         * @param row Row object corresponding to the selected Header.
-         */
-        void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row);
-    }
-
-    private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener;
-    OnHeaderClickedListener mOnHeaderClickedListener;
-    private boolean mHeadersEnabled = true;
-    private boolean mHeadersGone = false;
-    private int mBackgroundColor;
-    private boolean mBackgroundColorSet;
-
-    private static final PresenterSelector sHeaderPresenter = new ClassPresenterSelector()
-            .addClassPresenter(DividerRow.class, new DividerPresenter())
-            .addClassPresenter(SectionRow.class,
-                    new RowHeaderPresenter(R.layout.lb_section_header, false))
-            .addClassPresenter(Row.class, new RowHeaderPresenter(R.layout.lb_header));
-
-    public HeadersFragment() {
-        setPresenterSelector(sHeaderPresenter);
-        FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter());
-    }
-
-    public void setOnHeaderClickedListener(OnHeaderClickedListener listener) {
-        mOnHeaderClickedListener = listener;
-    }
-
-    public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) {
-        mOnHeaderViewSelectedListener = listener;
-    }
-
-    @Override
-    VerticalGridView findGridViewFromRoot(View view) {
-        return (VerticalGridView) view.findViewById(R.id.browse_headers);
-    }
-
-    @Override
-    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder,
-            int position, int subposition) {
-        if (mOnHeaderViewSelectedListener != null) {
-            if (viewHolder != null && position >= 0) {
-                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) viewHolder;
-                mOnHeaderViewSelectedListener.onHeaderSelected(
-                        (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), (Row) vh.getItem());
-            } else {
-                mOnHeaderViewSelectedListener.onHeaderSelected(null, null);
-            }
-        }
-    }
-
-    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
-            new ItemBridgeAdapter.AdapterListener() {
-        @Override
-        public void onCreate(final ItemBridgeAdapter.ViewHolder viewHolder) {
-            View headerView = viewHolder.getViewHolder().view;
-            headerView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mOnHeaderClickedListener != null) {
-                        mOnHeaderClickedListener.onHeaderClicked(
-                                (RowHeaderPresenter.ViewHolder) viewHolder.getViewHolder(),
-                                (Row) viewHolder.getItem());
-                    }
-                }
-            });
-            if (mWrapper != null) {
-                viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener);
-            } else {
-                headerView.addOnLayoutChangeListener(sLayoutChangeListener);
-            }
-        }
-
-    };
-
-    static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() {
-        @Override
-        public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-            v.setPivotX(v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? v.getWidth() : 0);
-            v.setPivotY(v.getMeasuredHeight() / 2);
-        }
-    };
-
-    @Override
-    int getLayoutResourceId() {
-        return R.layout.lb_headers_fragment;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        final VerticalGridView listView = getVerticalGridView();
-        if (listView == null) {
-            return;
-        }
-        if (mBackgroundColorSet) {
-            listView.setBackgroundColor(mBackgroundColor);
-            updateFadingEdgeToBrandColor(mBackgroundColor);
-        } else {
-            Drawable d = listView.getBackground();
-            if (d instanceof ColorDrawable) {
-                updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
-            }
-        }
-        updateListViewVisibility();
-    }
-
-    private void updateListViewVisibility() {
-        final VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE);
-            if (!mHeadersGone) {
-                if (mHeadersEnabled) {
-                    listView.setChildrenVisibility(View.VISIBLE);
-                } else {
-                    listView.setChildrenVisibility(View.INVISIBLE);
-                }
-            }
-        }
-    }
-
-    void setHeadersEnabled(boolean enabled) {
-        mHeadersEnabled = enabled;
-        updateListViewVisibility();
-    }
-
-    void setHeadersGone(boolean gone) {
-        mHeadersGone = gone;
-        updateListViewVisibility();
-    }
-
-    static class NoOverlappingFrameLayout extends FrameLayout {
-
-        public NoOverlappingFrameLayout(Context context) {
-            super(context);
-        }
-
-        /**
-         * Avoid creating hardware layer for header dock.
-         */
-        @Override
-        public boolean hasOverlappingRendering() {
-            return false;
-        }
-    }
-
-    // Wrapper needed because of conflict between RecyclerView's use of alpha
-    // for ADD animations, and RowHeaderPresenter's use of alpha for selected level.
-    final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() {
-        @Override
-        public void wrap(View wrapper, View wrapped) {
-            ((FrameLayout) wrapper).addView(wrapped);
-        }
-
-        @Override
-        public View createWrapper(View root) {
-            return new NoOverlappingFrameLayout(root.getContext());
-        }
-    };
-    @Override
-    void updateAdapter() {
-        super.updateAdapter();
-        ItemBridgeAdapter adapter = getBridgeAdapter();
-        adapter.setAdapterListener(mAdapterListener);
-        adapter.setWrapper(mWrapper);
-    }
-
-    void setBackgroundColor(int color) {
-        mBackgroundColor = color;
-        mBackgroundColorSet = true;
-
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().setBackgroundColor(mBackgroundColor);
-            updateFadingEdgeToBrandColor(mBackgroundColor);
-        }
-    }
-
-    private void updateFadingEdgeToBrandColor(int backgroundColor) {
-        View fadingView = getView().findViewById(R.id.fade_out_edge);
-        Drawable background = fadingView.getBackground();
-        if (background instanceof GradientDrawable) {
-            background.mutate();
-            ((GradientDrawable) background).setColors(
-                    new int[] {Color.TRANSPARENT, backgroundColor});
-        }
-    }
-
-    @Override
-    public void onTransitionStart() {
-        super.onTransitionStart();
-        if (!mHeadersEnabled) {
-            // When enabling headers fragment,  the RowHeaderView gets a focus but
-            // isShown() is still false because its parent is INVISIBLE, accessibility
-            // event is not sent.
-            // Workaround is: prevent focus to a child view during transition and put
-            // focus on it after transition is done.
-            final VerticalGridView listView = getVerticalGridView();
-            if (listView != null) {
-                listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-                if (listView.hasFocus()) {
-                    listView.requestFocus();
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onTransitionEnd() {
-        if (mHeadersEnabled) {
-            final VerticalGridView listView = getVerticalGridView();
-            if (listView != null) {
-                listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-                if (listView.hasFocus()) {
-                    listView.requestFocus();
-                }
-            }
-        }
-        super.onTransitionEnd();
-    }
-
-    public boolean isScrolling() {
-        return getVerticalGridView().getScrollState()
-                != HorizontalGridView.SCROLL_STATE_IDLE;
-    }
-}
diff --git a/android/support/v17/leanback/app/HeadersSupportFragment.java b/android/support/v17/leanback/app/HeadersSupportFragment.java
deleted file mode 100644
index 56c85af..0000000
--- a/android/support/v17/leanback/app/HeadersSupportFragment.java
+++ /dev/null
@@ -1,300 +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.v17.leanback.app;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.ClassPresenterSelector;
-import android.support.v17.leanback.widget.DividerPresenter;
-import android.support.v17.leanback.widget.DividerRow;
-import android.support.v17.leanback.widget.FocusHighlightHelper;
-import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowHeaderPresenter;
-import android.support.v17.leanback.widget.SectionRow;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * An fragment containing a list of row headers. Implementation must support three types of rows:
- * <ul>
- *     <li>{@link DividerRow} rendered by {@link DividerPresenter}.</li>
- *     <li>{@link Row} rendered by {@link RowHeaderPresenter}.</li>
- *     <li>{@link SectionRow} rendered by {@link RowHeaderPresenter}.</li>
- * </ul>
- * Use {@link #setPresenterSelector(PresenterSelector)} in subclass constructor to customize
- * Presenters. App may override {@link BrowseSupportFragment#onCreateHeadersSupportFragment()}.
- */
-public class HeadersSupportFragment extends BaseRowSupportFragment {
-
-    /**
-     * Interface definition for a callback to be invoked when a header item is clicked.
-     */
-    public interface OnHeaderClickedListener {
-        /**
-         * Called when a header item has been clicked.
-         *
-         * @param viewHolder Row ViewHolder object corresponding to the selected Header.
-         * @param row Row object corresponding to the selected Header.
-         */
-        void onHeaderClicked(RowHeaderPresenter.ViewHolder viewHolder, Row row);
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when a header item is selected.
-     */
-    public interface OnHeaderViewSelectedListener {
-        /**
-         * Called when a header item has been selected.
-         *
-         * @param viewHolder Row ViewHolder object corresponding to the selected Header.
-         * @param row Row object corresponding to the selected Header.
-         */
-        void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row);
-    }
-
-    private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener;
-    OnHeaderClickedListener mOnHeaderClickedListener;
-    private boolean mHeadersEnabled = true;
-    private boolean mHeadersGone = false;
-    private int mBackgroundColor;
-    private boolean mBackgroundColorSet;
-
-    private static final PresenterSelector sHeaderPresenter = new ClassPresenterSelector()
-            .addClassPresenter(DividerRow.class, new DividerPresenter())
-            .addClassPresenter(SectionRow.class,
-                    new RowHeaderPresenter(R.layout.lb_section_header, false))
-            .addClassPresenter(Row.class, new RowHeaderPresenter(R.layout.lb_header));
-
-    public HeadersSupportFragment() {
-        setPresenterSelector(sHeaderPresenter);
-        FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter());
-    }
-
-    public void setOnHeaderClickedListener(OnHeaderClickedListener listener) {
-        mOnHeaderClickedListener = listener;
-    }
-
-    public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) {
-        mOnHeaderViewSelectedListener = listener;
-    }
-
-    @Override
-    VerticalGridView findGridViewFromRoot(View view) {
-        return (VerticalGridView) view.findViewById(R.id.browse_headers);
-    }
-
-    @Override
-    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder,
-            int position, int subposition) {
-        if (mOnHeaderViewSelectedListener != null) {
-            if (viewHolder != null && position >= 0) {
-                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) viewHolder;
-                mOnHeaderViewSelectedListener.onHeaderSelected(
-                        (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), (Row) vh.getItem());
-            } else {
-                mOnHeaderViewSelectedListener.onHeaderSelected(null, null);
-            }
-        }
-    }
-
-    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
-            new ItemBridgeAdapter.AdapterListener() {
-        @Override
-        public void onCreate(final ItemBridgeAdapter.ViewHolder viewHolder) {
-            View headerView = viewHolder.getViewHolder().view;
-            headerView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mOnHeaderClickedListener != null) {
-                        mOnHeaderClickedListener.onHeaderClicked(
-                                (RowHeaderPresenter.ViewHolder) viewHolder.getViewHolder(),
-                                (Row) viewHolder.getItem());
-                    }
-                }
-            });
-            if (mWrapper != null) {
-                viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener);
-            } else {
-                headerView.addOnLayoutChangeListener(sLayoutChangeListener);
-            }
-        }
-
-    };
-
-    static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() {
-        @Override
-        public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-            v.setPivotX(v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? v.getWidth() : 0);
-            v.setPivotY(v.getMeasuredHeight() / 2);
-        }
-    };
-
-    @Override
-    int getLayoutResourceId() {
-        return R.layout.lb_headers_fragment;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        final VerticalGridView listView = getVerticalGridView();
-        if (listView == null) {
-            return;
-        }
-        if (mBackgroundColorSet) {
-            listView.setBackgroundColor(mBackgroundColor);
-            updateFadingEdgeToBrandColor(mBackgroundColor);
-        } else {
-            Drawable d = listView.getBackground();
-            if (d instanceof ColorDrawable) {
-                updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
-            }
-        }
-        updateListViewVisibility();
-    }
-
-    private void updateListViewVisibility() {
-        final VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE);
-            if (!mHeadersGone) {
-                if (mHeadersEnabled) {
-                    listView.setChildrenVisibility(View.VISIBLE);
-                } else {
-                    listView.setChildrenVisibility(View.INVISIBLE);
-                }
-            }
-        }
-    }
-
-    void setHeadersEnabled(boolean enabled) {
-        mHeadersEnabled = enabled;
-        updateListViewVisibility();
-    }
-
-    void setHeadersGone(boolean gone) {
-        mHeadersGone = gone;
-        updateListViewVisibility();
-    }
-
-    static class NoOverlappingFrameLayout extends FrameLayout {
-
-        public NoOverlappingFrameLayout(Context context) {
-            super(context);
-        }
-
-        /**
-         * Avoid creating hardware layer for header dock.
-         */
-        @Override
-        public boolean hasOverlappingRendering() {
-            return false;
-        }
-    }
-
-    // Wrapper needed because of conflict between RecyclerView's use of alpha
-    // for ADD animations, and RowHeaderPresenter's use of alpha for selected level.
-    final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() {
-        @Override
-        public void wrap(View wrapper, View wrapped) {
-            ((FrameLayout) wrapper).addView(wrapped);
-        }
-
-        @Override
-        public View createWrapper(View root) {
-            return new NoOverlappingFrameLayout(root.getContext());
-        }
-    };
-    @Override
-    void updateAdapter() {
-        super.updateAdapter();
-        ItemBridgeAdapter adapter = getBridgeAdapter();
-        adapter.setAdapterListener(mAdapterListener);
-        adapter.setWrapper(mWrapper);
-    }
-
-    void setBackgroundColor(int color) {
-        mBackgroundColor = color;
-        mBackgroundColorSet = true;
-
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().setBackgroundColor(mBackgroundColor);
-            updateFadingEdgeToBrandColor(mBackgroundColor);
-        }
-    }
-
-    private void updateFadingEdgeToBrandColor(int backgroundColor) {
-        View fadingView = getView().findViewById(R.id.fade_out_edge);
-        Drawable background = fadingView.getBackground();
-        if (background instanceof GradientDrawable) {
-            background.mutate();
-            ((GradientDrawable) background).setColors(
-                    new int[] {Color.TRANSPARENT, backgroundColor});
-        }
-    }
-
-    @Override
-    public void onTransitionStart() {
-        super.onTransitionStart();
-        if (!mHeadersEnabled) {
-            // When enabling headers fragment,  the RowHeaderView gets a focus but
-            // isShown() is still false because its parent is INVISIBLE, accessibility
-            // event is not sent.
-            // Workaround is: prevent focus to a child view during transition and put
-            // focus on it after transition is done.
-            final VerticalGridView listView = getVerticalGridView();
-            if (listView != null) {
-                listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-                if (listView.hasFocus()) {
-                    listView.requestFocus();
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onTransitionEnd() {
-        if (mHeadersEnabled) {
-            final VerticalGridView listView = getVerticalGridView();
-            if (listView != null) {
-                listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-                if (listView.hasFocus()) {
-                    listView.requestFocus();
-                }
-            }
-        }
-        super.onTransitionEnd();
-    }
-
-    public boolean isScrolling() {
-        return getVerticalGridView().getScrollState()
-                != HorizontalGridView.SCROLL_STATE_IDLE;
-    }
-}
diff --git a/android/support/v17/leanback/app/ListRowDataAdapter.java b/android/support/v17/leanback/app/ListRowDataAdapter.java
deleted file mode 100644
index 03d948b..0000000
--- a/android/support/v17/leanback/app/ListRowDataAdapter.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package android.support.v17.leanback.app;
-
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.Row;
-
-/**
- * Wrapper class for {@link ObjectAdapter} used by {@link BrowseFragment} to initialize
- * {@link RowsFragment}. We use invisible rows to represent
- * {@link android.support.v17.leanback.widget.DividerRow},
- * {@link android.support.v17.leanback.widget.SectionRow} and
- * {@link android.support.v17.leanback.widget.PageRow} in RowsFragment. In case we have an
- * invisible row at the end of a RowsFragment, it creates a jumping effect as the layout manager
- * thinks there are items even though they're invisible. This class takes care of filtering out
- * the invisible rows at the end. In case the data inside the adapter changes, it adjusts the
- * bounds to reflect the latest data.
- * {@link #detach()} must be called to release DataObserver from Adapter.
- */
-class ListRowDataAdapter extends ObjectAdapter {
-    public static final int ON_ITEM_RANGE_CHANGED = 2;
-    public static final int ON_ITEM_RANGE_INSERTED = 4;
-    public static final int ON_ITEM_RANGE_REMOVED = 8;
-    public static final int ON_CHANGED = 16;
-
-    private final ObjectAdapter mAdapter;
-    int mLastVisibleRowIndex;
-    final DataObserver mDataObserver;
-
-    public ListRowDataAdapter(ObjectAdapter adapter) {
-        super(adapter.getPresenterSelector());
-        this.mAdapter = adapter;
-        initialize();
-
-        // If an user implements its own ObjectAdapter, notification corresponding to data
-        // updates can be batched e.g. remove, add might be followed by notifyRemove, notifyAdd.
-        // But underlying data would have changed during the notifyRemove call by the previous add
-        // operation. To handle this case, we use QueueBasedDataObserver which forces
-        // recyclerview to do a full data refresh after each update operation.
-        if (adapter.isImmediateNotifySupported()) {
-            mDataObserver = new SimpleDataObserver();
-        } else {
-            mDataObserver = new QueueBasedDataObserver();
-        }
-        attach();
-    }
-
-    void detach() {
-        mAdapter.unregisterObserver(mDataObserver);
-    }
-
-    void attach() {
-        initialize();
-        mAdapter.registerObserver(mDataObserver);
-    }
-
-    void initialize() {
-        mLastVisibleRowIndex = -1;
-        int i = mAdapter.size() - 1;
-        while (i >= 0) {
-            Row item = (Row) mAdapter.get(i);
-            if (item.isRenderedAsRowView()) {
-                mLastVisibleRowIndex = i;
-                break;
-            }
-            i--;
-        }
-    }
-
-    @Override
-    public int size() {
-        return mLastVisibleRowIndex + 1;
-    }
-
-    @Override
-    public Object get(int index) {
-        return mAdapter.get(index);
-    }
-
-    void doNotify(int eventType, int positionStart, int itemCount) {
-        switch (eventType) {
-            case ON_ITEM_RANGE_CHANGED:
-                notifyItemRangeChanged(positionStart, itemCount);
-                break;
-            case ON_ITEM_RANGE_INSERTED:
-                notifyItemRangeInserted(positionStart, itemCount);
-                break;
-            case ON_ITEM_RANGE_REMOVED:
-                notifyItemRangeRemoved(positionStart, itemCount);
-                break;
-            case ON_CHANGED:
-                notifyChanged();
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid event type " + eventType);
-        }
-    }
-
-    private class SimpleDataObserver extends DataObserver {
-
-        SimpleDataObserver() {
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            if (positionStart <= mLastVisibleRowIndex) {
-                onEventFired(ON_ITEM_RANGE_CHANGED, positionStart,
-                        Math.min(itemCount, mLastVisibleRowIndex - positionStart + 1));
-            }
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            if (positionStart <= mLastVisibleRowIndex) {
-                mLastVisibleRowIndex += itemCount;
-                onEventFired(ON_ITEM_RANGE_INSERTED, positionStart, itemCount);
-                return;
-            }
-
-            int lastVisibleRowIndex = mLastVisibleRowIndex;
-            initialize();
-            if (mLastVisibleRowIndex > lastVisibleRowIndex) {
-                int totalItems = mLastVisibleRowIndex - lastVisibleRowIndex;
-                onEventFired(ON_ITEM_RANGE_INSERTED, lastVisibleRowIndex + 1, totalItems);
-            }
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            if (positionStart + itemCount - 1 < mLastVisibleRowIndex) {
-                mLastVisibleRowIndex -= itemCount;
-                onEventFired(ON_ITEM_RANGE_REMOVED, positionStart, itemCount);
-                return;
-            }
-
-            int lastVisibleRowIndex = mLastVisibleRowIndex;
-            initialize();
-            int totalItems = lastVisibleRowIndex - mLastVisibleRowIndex;
-            if (totalItems > 0) {
-                onEventFired(ON_ITEM_RANGE_REMOVED,
-                        Math.min(mLastVisibleRowIndex + 1, positionStart),
-                        totalItems);
-            }
-        }
-
-        @Override
-        public void onChanged() {
-            initialize();
-            onEventFired(ON_CHANGED, -1, -1);
-        }
-
-        protected void onEventFired(int eventType, int positionStart, int itemCount) {
-            doNotify(eventType, positionStart, itemCount);
-        }
-    }
-
-
-    /**
-     * When using custom {@link ObjectAdapter}, it's possible that the user may make multiple
-     * changes to the underlying data at once. The notifications about those updates may be
-     * batched and the underlying data would have changed to reflect latest updates as opposed
-     * to intermediate changes. In order to force RecyclerView to refresh the view with access
-     * only to the final data, we call notifyChange().
-     */
-    private class QueueBasedDataObserver extends DataObserver {
-
-        QueueBasedDataObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            initialize();
-            notifyChanged();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/OnboardingFragment.java b/android/support/v17/leanback/app/OnboardingFragment.java
deleted file mode 100644
index f352c41..0000000
--- a/android/support/v17/leanback/app/OnboardingFragment.java
+++ /dev/null
@@ -1,1027 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from OnboardingSupportFragment.java.  DO NOT MODIFY. */
-
-/*
- * 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.v17.leanback.app;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.PagingIndicator;
-import android.app.Fragment;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnKeyListener;
-import android.view.ViewGroup;
-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;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An OnboardingFragment provides a common and simple way to build onboarding screen for
- * applications.
- * <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 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>
- * <li>{@link #onCreateBackgroundView} to provide the background view. Background view has the same
- * size as the screen and the lowest z-order.</li>
- * <li>{@link #onCreateContentView} to provide the contents view. The content view is located in
- * the content area at the center of the screen.</li>
- * <li>{@link #onCreateForegroundView} to provide the foreground view. Foreground view has the same
- * size as the screen and the highest z-order</li>
- * </ul>
- * <p>
- * Each of these methods can return {@code null} if the application doesn't want to provide it.
- * <p>
- * <h3>Page information</h3>
- * The onboarding screen may have several pages which explain the functionality of the application.
- * The inherited class should provide the page information by overriding the methods:
- * <p>
- * <ul>
- * <li>{@link #getPageCount} to provide the number of pages.</li>
- * <li>{@link #getPageTitle} to provide the title of the page.</li>
- * <li>{@link #getPageDescription} to provide the description of the page.</li>
- * </ul>
- * <p>
- * Note that the information is used in {@link #onCreateView}, so should be initialized before
- * calling {@code super.onCreateView}.
- * <p>
- * <h3>Animation</h3>
- * Onboarding screen has three kinds of animations:
- * <p>
- * <h4>Logo Splash Animation</a></h4>
- * When onboarding screen appears, the logo splash animation is played by default. The animation
- * fades in the logo image, pauses in a few seconds and fades it out.
- * <p>
- * In most cases, the logo animation needs to be customized because the logo images of applications
- * are different from each other, or some applications may want to show their own animations.
- * <p>
- * The logo animation can be customized in two ways:
- * <ul>
- * <li>The simplest way is to provide the logo image by calling {@link #setLogoResourceId} to show
- * the default logo animation. This method should be called in {@link Fragment#onCreateView}.</li>
- * <li>If the logo animation is complex, then override {@link #onCreateLogoAnimation} and return the
- * {@link Animator} object to run.</li>
- * </ul>
- * <p>
- * 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, 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.
- * <p>
- * <h3>Finishing the screen</h3>
- * <p>
- * If the user finishes the onboarding screen after navigating all the pages,
- * {@link #onFinishFragment} is called. The inherited class can override this method to show another
- * fragment or activity, or just remove this fragment.
- * <p>
- * <h3>Theming</h3>
- * <p>
- * OnboardingFragment must have access to an appropriate theme. Specifically, the fragment must
- * receive  {@link R.style#Theme_Leanback_Onboarding}, or a theme whose parent is set to that theme.
- * Themes can be provided in one of three ways:
- * <ul>
- * <li>The simplest way is to set the theme for the host Activity to the Onboarding theme or a theme
- * that derives from it.</li>
- * <li>If the Activity already has a theme and setting its parent theme is inconvenient, the
- * existing Activity theme can have an entry added for the attribute
- * {@link R.styleable#LeanbackOnboardingTheme_onboardingTheme}. If present, this theme will be used
- * by OnboardingFragment as an overlay to the Activity's theme.</li>
- * <li>Finally, custom subclasses of OnboardingFragment may provide a theme through the
- * {@link #onProvideTheme} method. This can be useful if a subclass is used across multiple
- * Activities.</li>
- * </ul>
- * <p>
- * If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
- * the Activity's theme. (Themes whose parent theme is already set to the onboarding theme do not
- * need to set the onboardingTheme attribute; if set, it will be ignored.)
- *
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingTheme
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingHeaderStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingTitleStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingDescriptionStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingNavigatorContainerStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingPageIndicatorStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingStartButtonStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
- * @deprecated use {@link OnboardingSupportFragment}
- */
-@Deprecated
-abstract public class OnboardingFragment extends Fragment {
-    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 HEADER_ANIMATION_DURATION_MS = 417;
-    private static final long DESCRIPTION_START_DELAY_MS = 33;
-    private static final long HEADER_APPEAR_DELAY_MS = 500;
-    private static final int SLIDE_DISTANCE = 60;
-
-    private static int sSlideDistance;
-
-    private static final TimeInterpolator HEADER_APPEAR_INTERPOLATOR = new DecelerateInterpolator();
-    private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR =
-            new AccelerateInterpolator();
-
-    // 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;
-
-    boolean mIsLtr;
-
-    // No need to save/restore the logo resource ID, because the logo animation will not appear when
-    // 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() {
-        @Override
-        public void onClick(View view) {
-            if (!mLogoAnimationFinished) {
-                // Do not change page until the enter transition finishes.
-                return;
-            }
-            if (mCurrentPageIndex == getPageCount() - 1) {
-                onFinishFragment();
-            } else {
-                moveToNextPage();
-            }
-        }
-    };
-
-    private final OnKeyListener mOnKeyListener = new OnKeyListener() {
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent event) {
-            if (!mLogoAnimationFinished) {
-                // Ignore key event until the enter transition finishes.
-                return keyCode != KeyEvent.KEYCODE_BACK;
-            }
-            if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                return false;
-            }
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_BACK:
-                    if (mCurrentPageIndex == 0) {
-                        return false;
-                    }
-                    moveToPreviousPage();
-                    return true;
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                    if (mIsLtr) {
-                        moveToPreviousPage();
-                    } else {
-                        moveToNextPage();
-                    }
-                    return true;
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    if (mIsLtr) {
-                        moveToNextPage();
-                    } else {
-                        moveToPreviousPage();
-                    }
-                    return true;
-            }
-            return false;
-        }
-    };
-
-    /**
-     * Navigates to the previous page.
-     */
-    protected void moveToPreviousPage() {
-        if (!mLogoAnimationFinished) {
-            // Ignore if the logo enter transition is in progress.
-            return;
-        }
-        if (mCurrentPageIndex > 0) {
-            --mCurrentPageIndex;
-            onPageChangedInternal(mCurrentPageIndex + 1);
-        }
-    }
-
-    /**
-     * Navigates to the next page.
-     */
-    protected void moveToNextPage() {
-        if (!mLogoAnimationFinished) {
-            // Ignore if the logo enter transition is in progress.
-            return;
-        }
-        if (mCurrentPageIndex < getPageCount() - 1) {
-            ++mCurrentPageIndex;
-            onPageChangedInternal(mCurrentPageIndex - 1);
-        }
-    }
-
-    @Nullable
-    @Override
-    public View onCreateView(LayoutInflater inflater, final ViewGroup container,
-            Bundle savedInstanceState) {
-        resolveTheme();
-        LayoutInflater localInflater = getThemeInflater(inflater);
-        final ViewGroup view = (ViewGroup) localInflater.inflate(R.layout.lb_onboarding_fragment,
-                container, false);
-        mIsLtr = getResources().getConfiguration().getLayoutDirection()
-                == View.LAYOUT_DIRECTION_LTR;
-        mPageIndicator = (PagingIndicator) view.findViewById(R.id.page_indicator);
-        mPageIndicator.setOnClickListener(mOnClickListener);
-        mPageIndicator.setOnKeyListener(mOnKeyListener);
-        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(OnboardingFragment.this);
-        if (sSlideDistance == 0) {
-            sSlideDistance = (int) (SLIDE_DISTANCE * context.getResources()
-                    .getDisplayMetrics().scaledDensity);
-        }
-        view.requestFocus();
-        return view;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable 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);
-        }
-    }
-
-    /**
-     * Returns the theme used for styling the fragment. The default returns -1, indicating that the
-     * host Activity's theme should be used.
-     *
-     * @return The theme resource ID of the theme to use in this fragment, or -1 to use the host
-     *         Activity's theme.
-     */
-    public int onProvideTheme() {
-        return -1;
-    }
-
-    private void resolveTheme() {
-        final Context context = FragmentUtil.getContext(OnboardingFragment.this);
-        int theme = onProvideTheme();
-        if (theme == -1) {
-            // Look up the onboardingTheme in the activity's currently specified theme. If it
-            // exists, wrap the theme with its value.
-            int resId = R.attr.onboardingTheme;
-            TypedValue typedValue = new TypedValue();
-            boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
-            if (DEBUG) Log.v(TAG, "Found onboarding theme reference? " + found);
-            if (found) {
-                mThemeWrapper = new ContextThemeWrapper(context, typedValue.resourceId);
-            }
-        } else {
-            mThemeWrapper = new ContextThemeWrapper(context, theme);
-        }
-    }
-
-    private LayoutInflater getThemeInflater(LayoutInflater inflater) {
-        return mThemeWrapper == null ? inflater : inflater.cloneInContext(mThemeWrapper);
-    }
-
-    /**
-     * Sets the resource ID of the splash logo image. If the logo resource id set, the default logo
-     * splash animation will be played.
-     *
-     * @param id The resource ID of the logo image.
-     */
-    public final void setLogoResourceId(int id) {
-        mLogoResourceId = id;
-    }
-
-    /**
-     * Returns the resource ID of the splash logo image.
-     *
-     * @return The resource ID of the splash logo image.
-     */
-    public final int getLogoResourceId() {
-        return mLogoResourceId;
-    }
-
-    /**
-     * Called to have the inherited class create its own logo animation.
-     * <p>
-     * This is called only if the logo image resource ID is not set by {@link #setLogoResourceId}.
-     * If this returns {@code null}, the logo animation is skipped.
-     *
-     * @return The {@link Animator} object which runs the logo animation.
-     */
-    @Nullable
-    protected Animator onCreateLogoAnimation() {
-        return null;
-    }
-
-    boolean startLogoAnimation() {
-        final Context context = FragmentUtil.getContext(OnboardingFragment.this);
-        if (context == null) {
-            return false;
-        }
-        Animator animator = null;
-        if (mLogoResourceId != 0) {
-            mLogoView.setVisibility(View.VISIBLE);
-            mLogoView.setImageResource(mLogoResourceId);
-            Animator inAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_logo_enter);
-            Animator outAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_logo_exit);
-            outAnimator.setStartDelay(LOGO_SPLASH_PAUSE_DURATION_MS);
-            AnimatorSet logoAnimator = new AnimatorSet();
-            logoAnimator.playSequentially(inAnimator, outAnimator);
-            logoAnimator.setTarget(mLogoView);
-            animator = logoAnimator;
-        } else {
-            animator = onCreateLogoAnimation();
-        }
-        if (animator != null) {
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (context != null) {
-                        mLogoAnimationFinished = true;
-                        onLogoAnimationFinished();
-                    }
-                }
-            });
-            animator.start();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Called to have the inherited class create its enter animation. The start animation runs after
-     * logo animation ends.
-     *
-     * @return The {@link Animator} object which runs the page enter animation.
-     */
-    @Nullable
-    protected Animator onCreateEnterAnimation() {
-        return null;
-    }
-
-
-    /**
-     * 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(OnboardingFragment.this)));
-        ViewGroup backgroundContainer = (ViewGroup) container.findViewById(
-                R.id.background_container);
-        View background = onCreateBackgroundView(inflater, backgroundContainer);
-        if (background != null) {
-            backgroundContainer.setVisibility(View.VISIBLE);
-            backgroundContainer.addView(background);
-        }
-        ViewGroup contentContainer = (ViewGroup) container.findViewById(R.id.content_container);
-        View content = onCreateContentView(inflater, contentContainer);
-        if (content != null) {
-            contentContainer.setVisibility(View.VISIBLE);
-            contentContainer.addView(content);
-        }
-        ViewGroup foregroundContainer = (ViewGroup) container.findViewById(
-                R.id.foreground_container);
-        View foreground = onCreateForegroundView(inflater, foregroundContainer);
-        if (foreground != null) {
-            foregroundContainer.setVisibility(View.VISIBLE);
-            foregroundContainer.addView(foreground);
-        }
-        // Make views visible which were invisible while logo animation is running.
-        container.findViewById(R.id.page_container).setVisibility(View.VISIBLE);
-        container.findViewById(R.id.content_container).setVisibility(View.VISIBLE);
-        if (getPageCount() > 1) {
-            mPageIndicator.setPageCount(getPageCount());
-            mPageIndicator.onPageSelected(mCurrentPageIndex, false);
-        }
-        if (mCurrentPageIndex == getPageCount() - 1) {
-            mStartButton.setVisibility(View.VISIBLE);
-        } else {
-            mPageIndicator.setVisibility(View.VISIBLE);
-        }
-        // Header views.
-        mTitleView.setText(getPageTitle(mCurrentPageIndex));
-        mDescriptionView.setText(getPageDescription(mCurrentPageIndex));
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * 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(OnboardingFragment.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);
-
-        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();
-    }
-
-    /**
-     * 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(OnboardingFragment.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(OnboardingFragment.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
-     */
-    protected final boolean isLogoAnimationFinished() {
-        return mLogoAnimationFinished;
-    }
-
-    /**
-     * Returns the page count.
-     *
-     * @return The page count.
-     */
-    abstract protected int getPageCount();
-
-    /**
-     * Returns the title of the given page.
-     *
-     * @param pageIndex The page index.
-     *
-     * @return The title of the page.
-     */
-    abstract protected CharSequence getPageTitle(int pageIndex);
-
-    /**
-     * Returns the description of the given page.
-     *
-     * @param pageIndex The page index.
-     *
-     * @return The description of the page.
-     */
-    abstract protected CharSequence getPageDescription(int pageIndex);
-
-    /**
-     * Returns the index of the current page.
-     *
-     * @return The index of the current page.
-     */
-    protected final int getCurrentPageIndex() {
-        return mCurrentPageIndex;
-    }
-
-    /**
-     * Called to have the inherited class create background view. This is optional and the fragment
-     * which doesn't have the background view can return {@code null}. This is called inside
-     * {@link #onCreateView}.
-     *
-     * @param inflater The LayoutInflater object that can be used to inflate the views,
-     * @param container The parent view that the additional views are attached to.The fragment
-     *        should not add the view by itself.
-     *
-     * @return The background view for the onboarding screen, or {@code null}.
-     */
-    @Nullable
-    abstract protected View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container);
-
-    /**
-     * Called to have the inherited class create content view. This is optional and the fragment
-     * which doesn't have the content view can return {@code null}. This is called inside
-     * {@link #onCreateView}.
-     *
-     * <p>The content view would be located at the center of the screen.
-     *
-     * @param inflater The LayoutInflater object that can be used to inflate the views,
-     * @param container The parent view that the additional views are attached to.The fragment
-     *        should not add the view by itself.
-     *
-     * @return The content view for the onboarding screen, or {@code null}.
-     */
-    @Nullable
-    abstract protected View onCreateContentView(LayoutInflater inflater, ViewGroup container);
-
-    /**
-     * Called to have the inherited class create foreground view. This is optional and the fragment
-     * which doesn't need the foreground view can return {@code null}. This is called inside
-     * {@link #onCreateView}.
-     *
-     * <p>This foreground view would have the highest z-order.
-     *
-     * @param inflater The LayoutInflater object that can be used to inflate the views,
-     * @param container The parent view that the additional views are attached to.The fragment
-     *        should not add the view by itself.
-     *
-     * @return The foreground view for the onboarding screen, or {@code null}.
-     */
-    @Nullable
-    abstract protected View onCreateForegroundView(LayoutInflater inflater, ViewGroup container);
-
-    /**
-     * Called when the onboarding flow finishes.
-     */
-    protected void onFinishFragment() { }
-
-    /**
-     * Called when the page changes.
-     */
-    private void onPageChangedInternal(int previousPage) {
-        if (mAnimator != null) {
-            mAnimator.end();
-        }
-        mPageIndicator.onPageSelected(mCurrentPageIndex, true);
-
-        List<Animator> animators = new ArrayList<>();
-        // Header animation
-        Animator fadeAnimator = null;
-        if (previousPage < getCurrentPageIndex()) {
-            // sliding to left
-            animators.add(createAnimator(mTitleView, false, Gravity.START, 0));
-            animators.add(fadeAnimator = createAnimator(mDescriptionView, false, Gravity.START,
-                    DESCRIPTION_START_DELAY_MS));
-            animators.add(createAnimator(mTitleView, true, Gravity.END,
-                    HEADER_APPEAR_DELAY_MS));
-            animators.add(createAnimator(mDescriptionView, true, Gravity.END,
-                    HEADER_APPEAR_DELAY_MS + DESCRIPTION_START_DELAY_MS));
-        } else {
-            // sliding to right
-            animators.add(createAnimator(mTitleView, false, Gravity.END, 0));
-            animators.add(fadeAnimator = createAnimator(mDescriptionView, false, Gravity.END,
-                    DESCRIPTION_START_DELAY_MS));
-            animators.add(createAnimator(mTitleView, true, Gravity.START,
-                    HEADER_APPEAR_DELAY_MS));
-            animators.add(createAnimator(mDescriptionView, true, Gravity.START,
-                    HEADER_APPEAR_DELAY_MS + DESCRIPTION_START_DELAY_MS));
-        }
-        final int currentPageIndex = getCurrentPageIndex();
-        fadeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mTitleView.setText(getPageTitle(currentPageIndex));
-                mDescriptionView.setText(getPageDescription(currentPageIndex));
-            }
-        });
-
-        final Context context = FragmentUtil.getContext(OnboardingFragment.this);
-        // Animator for switching between page indicator and button.
-        if (getCurrentPageIndex() == getPageCount() - 1) {
-            mStartButton.setVisibility(View.VISIBLE);
-            Animator navigatorFadeOutAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_page_indicator_fade_out);
-            navigatorFadeOutAnimator.setTarget(mPageIndicator);
-            navigatorFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mPageIndicator.setVisibility(View.GONE);
-                }
-            });
-            animators.add(navigatorFadeOutAnimator);
-            Animator buttonFadeInAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_start_button_fade_in);
-            buttonFadeInAnimator.setTarget(mStartButton);
-            animators.add(buttonFadeInAnimator);
-        } else if (previousPage == getPageCount() - 1) {
-            mPageIndicator.setVisibility(View.VISIBLE);
-            Animator navigatorFadeInAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_page_indicator_fade_in);
-            navigatorFadeInAnimator.setTarget(mPageIndicator);
-            animators.add(navigatorFadeInAnimator);
-            Animator buttonFadeOutAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_start_button_fade_out);
-            buttonFadeOutAnimator.setTarget(mStartButton);
-            buttonFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mStartButton.setVisibility(View.GONE);
-                }
-            });
-            animators.add(buttonFadeOutAnimator);
-        }
-        mAnimator = new AnimatorSet();
-        mAnimator.playTogether(animators);
-        mAnimator.start();
-        onPageChanged(mCurrentPageIndex, previousPage);
-    }
-
-    /**
-     * Called when the page has been changed.
-     *
-     * @param newPage The new page.
-     * @param previousPage The previous page.
-     */
-    protected void onPageChanged(int newPage, int previousPage) { }
-
-    private Animator createAnimator(View view, boolean fadeIn, int slideDirection,
-            long startDelay) {
-        boolean isLtr = getView().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
-        boolean slideRight = (isLtr && slideDirection == Gravity.END)
-                || (!isLtr && slideDirection == Gravity.START)
-                || slideDirection == Gravity.RIGHT;
-        Animator fadeAnimator;
-        Animator slideAnimator;
-        if (fadeIn) {
-            fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);
-            slideAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
-                    slideRight ? sSlideDistance : -sSlideDistance, 0);
-            fadeAnimator.setInterpolator(HEADER_APPEAR_INTERPOLATOR);
-            slideAnimator.setInterpolator(HEADER_APPEAR_INTERPOLATOR);
-        } else {
-            fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f);
-            slideAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0,
-                    slideRight ? sSlideDistance : -sSlideDistance);
-            fadeAnimator.setInterpolator(HEADER_DISAPPEAR_INTERPOLATOR);
-            slideAnimator.setInterpolator(HEADER_DISAPPEAR_INTERPOLATOR);
-        }
-        fadeAnimator.setDuration(HEADER_ANIMATION_DURATION_MS);
-        fadeAnimator.setTarget(view);
-        slideAnimator.setDuration(HEADER_ANIMATION_DURATION_MS);
-        slideAnimator.setTarget(view);
-        AnimatorSet animator = new AnimatorSet();
-        animator.playTogether(fadeAnimator, slideAnimator);
-        if (startDelay > 0) {
-            animator.setStartDelay(startDelay);
-        }
-        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/android/support/v17/leanback/app/OnboardingSupportFragment.java b/android/support/v17/leanback/app/OnboardingSupportFragment.java
deleted file mode 100644
index 51cb2de..0000000
--- a/android/support/v17/leanback/app/OnboardingSupportFragment.java
+++ /dev/null
@@ -1,1022 +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.v17.leanback.app;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.PagingIndicator;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnKeyListener;
-import android.view.ViewGroup;
-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;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An OnboardingSupportFragment provides a common and simple way to build onboarding screen for
- * applications.
- * <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 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>
- * <li>{@link #onCreateBackgroundView} to provide the background view. Background view has the same
- * size as the screen and the lowest z-order.</li>
- * <li>{@link #onCreateContentView} to provide the contents view. The content view is located in
- * the content area at the center of the screen.</li>
- * <li>{@link #onCreateForegroundView} to provide the foreground view. Foreground view has the same
- * size as the screen and the highest z-order</li>
- * </ul>
- * <p>
- * Each of these methods can return {@code null} if the application doesn't want to provide it.
- * <p>
- * <h3>Page information</h3>
- * The onboarding screen may have several pages which explain the functionality of the application.
- * The inherited class should provide the page information by overriding the methods:
- * <p>
- * <ul>
- * <li>{@link #getPageCount} to provide the number of pages.</li>
- * <li>{@link #getPageTitle} to provide the title of the page.</li>
- * <li>{@link #getPageDescription} to provide the description of the page.</li>
- * </ul>
- * <p>
- * Note that the information is used in {@link #onCreateView}, so should be initialized before
- * calling {@code super.onCreateView}.
- * <p>
- * <h3>Animation</h3>
- * Onboarding screen has three kinds of animations:
- * <p>
- * <h4>Logo Splash Animation</a></h4>
- * When onboarding screen appears, the logo splash animation is played by default. The animation
- * fades in the logo image, pauses in a few seconds and fades it out.
- * <p>
- * In most cases, the logo animation needs to be customized because the logo images of applications
- * are different from each other, or some applications may want to show their own animations.
- * <p>
- * The logo animation can be customized in two ways:
- * <ul>
- * <li>The simplest way is to provide the logo image by calling {@link #setLogoResourceId} to show
- * the default logo animation. This method should be called in {@link Fragment#onCreateView}.</li>
- * <li>If the logo animation is complex, then override {@link #onCreateLogoAnimation} and return the
- * {@link Animator} object to run.</li>
- * </ul>
- * <p>
- * 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, 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.
- * <p>
- * <h3>Finishing the screen</h3>
- * <p>
- * If the user finishes the onboarding screen after navigating all the pages,
- * {@link #onFinishFragment} is called. The inherited class can override this method to show another
- * fragment or activity, or just remove this fragment.
- * <p>
- * <h3>Theming</h3>
- * <p>
- * OnboardingSupportFragment must have access to an appropriate theme. Specifically, the fragment must
- * receive  {@link R.style#Theme_Leanback_Onboarding}, or a theme whose parent is set to that theme.
- * Themes can be provided in one of three ways:
- * <ul>
- * <li>The simplest way is to set the theme for the host Activity to the Onboarding theme or a theme
- * that derives from it.</li>
- * <li>If the Activity already has a theme and setting its parent theme is inconvenient, the
- * existing Activity theme can have an entry added for the attribute
- * {@link R.styleable#LeanbackOnboardingTheme_onboardingTheme}. If present, this theme will be used
- * by OnboardingSupportFragment as an overlay to the Activity's theme.</li>
- * <li>Finally, custom subclasses of OnboardingSupportFragment may provide a theme through the
- * {@link #onProvideTheme} method. This can be useful if a subclass is used across multiple
- * Activities.</li>
- * </ul>
- * <p>
- * If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
- * the Activity's theme. (Themes whose parent theme is already set to the onboarding theme do not
- * need to set the onboardingTheme attribute; if set, it will be ignored.)
- *
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingTheme
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingHeaderStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingTitleStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingDescriptionStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingNavigatorContainerStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingPageIndicatorStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingStartButtonStyle
- * @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
- */
-abstract public class OnboardingSupportFragment extends Fragment {
-    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 HEADER_ANIMATION_DURATION_MS = 417;
-    private static final long DESCRIPTION_START_DELAY_MS = 33;
-    private static final long HEADER_APPEAR_DELAY_MS = 500;
-    private static final int SLIDE_DISTANCE = 60;
-
-    private static int sSlideDistance;
-
-    private static final TimeInterpolator HEADER_APPEAR_INTERPOLATOR = new DecelerateInterpolator();
-    private static final TimeInterpolator HEADER_DISAPPEAR_INTERPOLATOR =
-            new AccelerateInterpolator();
-
-    // 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;
-
-    boolean mIsLtr;
-
-    // No need to save/restore the logo resource ID, because the logo animation will not appear when
-    // 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() {
-        @Override
-        public void onClick(View view) {
-            if (!mLogoAnimationFinished) {
-                // Do not change page until the enter transition finishes.
-                return;
-            }
-            if (mCurrentPageIndex == getPageCount() - 1) {
-                onFinishFragment();
-            } else {
-                moveToNextPage();
-            }
-        }
-    };
-
-    private final OnKeyListener mOnKeyListener = new OnKeyListener() {
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent event) {
-            if (!mLogoAnimationFinished) {
-                // Ignore key event until the enter transition finishes.
-                return keyCode != KeyEvent.KEYCODE_BACK;
-            }
-            if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                return false;
-            }
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_BACK:
-                    if (mCurrentPageIndex == 0) {
-                        return false;
-                    }
-                    moveToPreviousPage();
-                    return true;
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                    if (mIsLtr) {
-                        moveToPreviousPage();
-                    } else {
-                        moveToNextPage();
-                    }
-                    return true;
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    if (mIsLtr) {
-                        moveToNextPage();
-                    } else {
-                        moveToPreviousPage();
-                    }
-                    return true;
-            }
-            return false;
-        }
-    };
-
-    /**
-     * Navigates to the previous page.
-     */
-    protected void moveToPreviousPage() {
-        if (!mLogoAnimationFinished) {
-            // Ignore if the logo enter transition is in progress.
-            return;
-        }
-        if (mCurrentPageIndex > 0) {
-            --mCurrentPageIndex;
-            onPageChangedInternal(mCurrentPageIndex + 1);
-        }
-    }
-
-    /**
-     * Navigates to the next page.
-     */
-    protected void moveToNextPage() {
-        if (!mLogoAnimationFinished) {
-            // Ignore if the logo enter transition is in progress.
-            return;
-        }
-        if (mCurrentPageIndex < getPageCount() - 1) {
-            ++mCurrentPageIndex;
-            onPageChangedInternal(mCurrentPageIndex - 1);
-        }
-    }
-
-    @Nullable
-    @Override
-    public View onCreateView(LayoutInflater inflater, final ViewGroup container,
-            Bundle savedInstanceState) {
-        resolveTheme();
-        LayoutInflater localInflater = getThemeInflater(inflater);
-        final ViewGroup view = (ViewGroup) localInflater.inflate(R.layout.lb_onboarding_fragment,
-                container, false);
-        mIsLtr = getResources().getConfiguration().getLayoutDirection()
-                == View.LAYOUT_DIRECTION_LTR;
-        mPageIndicator = (PagingIndicator) view.findViewById(R.id.page_indicator);
-        mPageIndicator.setOnClickListener(mOnClickListener);
-        mPageIndicator.setOnKeyListener(mOnKeyListener);
-        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);
-        }
-        view.requestFocus();
-        return view;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable 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);
-        }
-    }
-
-    /**
-     * Returns the theme used for styling the fragment. The default returns -1, indicating that the
-     * host Activity's theme should be used.
-     *
-     * @return The theme resource ID of the theme to use in this fragment, or -1 to use the host
-     *         Activity's theme.
-     */
-    public int onProvideTheme() {
-        return -1;
-    }
-
-    private void resolveTheme() {
-        final Context context = getContext();
-        int theme = onProvideTheme();
-        if (theme == -1) {
-            // Look up the onboardingTheme in the activity's currently specified theme. If it
-            // exists, wrap the theme with its value.
-            int resId = R.attr.onboardingTheme;
-            TypedValue typedValue = new TypedValue();
-            boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
-            if (DEBUG) Log.v(TAG, "Found onboarding theme reference? " + found);
-            if (found) {
-                mThemeWrapper = new ContextThemeWrapper(context, typedValue.resourceId);
-            }
-        } else {
-            mThemeWrapper = new ContextThemeWrapper(context, theme);
-        }
-    }
-
-    private LayoutInflater getThemeInflater(LayoutInflater inflater) {
-        return mThemeWrapper == null ? inflater : inflater.cloneInContext(mThemeWrapper);
-    }
-
-    /**
-     * Sets the resource ID of the splash logo image. If the logo resource id set, the default logo
-     * splash animation will be played.
-     *
-     * @param id The resource ID of the logo image.
-     */
-    public final void setLogoResourceId(int id) {
-        mLogoResourceId = id;
-    }
-
-    /**
-     * Returns the resource ID of the splash logo image.
-     *
-     * @return The resource ID of the splash logo image.
-     */
-    public final int getLogoResourceId() {
-        return mLogoResourceId;
-    }
-
-    /**
-     * Called to have the inherited class create its own logo animation.
-     * <p>
-     * This is called only if the logo image resource ID is not set by {@link #setLogoResourceId}.
-     * If this returns {@code null}, the logo animation is skipped.
-     *
-     * @return The {@link Animator} object which runs the logo animation.
-     */
-    @Nullable
-    protected Animator onCreateLogoAnimation() {
-        return null;
-    }
-
-    boolean startLogoAnimation() {
-        final Context context = getContext();
-        if (context == null) {
-            return false;
-        }
-        Animator animator = null;
-        if (mLogoResourceId != 0) {
-            mLogoView.setVisibility(View.VISIBLE);
-            mLogoView.setImageResource(mLogoResourceId);
-            Animator inAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_logo_enter);
-            Animator outAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_logo_exit);
-            outAnimator.setStartDelay(LOGO_SPLASH_PAUSE_DURATION_MS);
-            AnimatorSet logoAnimator = new AnimatorSet();
-            logoAnimator.playSequentially(inAnimator, outAnimator);
-            logoAnimator.setTarget(mLogoView);
-            animator = logoAnimator;
-        } else {
-            animator = onCreateLogoAnimation();
-        }
-        if (animator != null) {
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (context != null) {
-                        mLogoAnimationFinished = true;
-                        onLogoAnimationFinished();
-                    }
-                }
-            });
-            animator.start();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Called to have the inherited class create its enter animation. The start animation runs after
-     * logo animation ends.
-     *
-     * @return The {@link Animator} object which runs the page enter animation.
-     */
-    @Nullable
-    protected Animator onCreateEnterAnimation() {
-        return null;
-    }
-
-
-    /**
-     * 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()));
-        ViewGroup backgroundContainer = (ViewGroup) container.findViewById(
-                R.id.background_container);
-        View background = onCreateBackgroundView(inflater, backgroundContainer);
-        if (background != null) {
-            backgroundContainer.setVisibility(View.VISIBLE);
-            backgroundContainer.addView(background);
-        }
-        ViewGroup contentContainer = (ViewGroup) container.findViewById(R.id.content_container);
-        View content = onCreateContentView(inflater, contentContainer);
-        if (content != null) {
-            contentContainer.setVisibility(View.VISIBLE);
-            contentContainer.addView(content);
-        }
-        ViewGroup foregroundContainer = (ViewGroup) container.findViewById(
-                R.id.foreground_container);
-        View foreground = onCreateForegroundView(inflater, foregroundContainer);
-        if (foreground != null) {
-            foregroundContainer.setVisibility(View.VISIBLE);
-            foregroundContainer.addView(foreground);
-        }
-        // Make views visible which were invisible while logo animation is running.
-        container.findViewById(R.id.page_container).setVisibility(View.VISIBLE);
-        container.findViewById(R.id.content_container).setVisibility(View.VISIBLE);
-        if (getPageCount() > 1) {
-            mPageIndicator.setPageCount(getPageCount());
-            mPageIndicator.onPageSelected(mCurrentPageIndex, false);
-        }
-        if (mCurrentPageIndex == getPageCount() - 1) {
-            mStartButton.setVisibility(View.VISIBLE);
-        } else {
-            mPageIndicator.setVisibility(View.VISIBLE);
-        }
-        // Header views.
-        mTitleView.setText(getPageTitle(mCurrentPageIndex));
-        mDescriptionView.setText(getPageDescription(mCurrentPageIndex));
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * 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);
-
-        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();
-    }
-
-    /**
-     * 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
-     */
-    protected final boolean isLogoAnimationFinished() {
-        return mLogoAnimationFinished;
-    }
-
-    /**
-     * Returns the page count.
-     *
-     * @return The page count.
-     */
-    abstract protected int getPageCount();
-
-    /**
-     * Returns the title of the given page.
-     *
-     * @param pageIndex The page index.
-     *
-     * @return The title of the page.
-     */
-    abstract protected CharSequence getPageTitle(int pageIndex);
-
-    /**
-     * Returns the description of the given page.
-     *
-     * @param pageIndex The page index.
-     *
-     * @return The description of the page.
-     */
-    abstract protected CharSequence getPageDescription(int pageIndex);
-
-    /**
-     * Returns the index of the current page.
-     *
-     * @return The index of the current page.
-     */
-    protected final int getCurrentPageIndex() {
-        return mCurrentPageIndex;
-    }
-
-    /**
-     * Called to have the inherited class create background view. This is optional and the fragment
-     * which doesn't have the background view can return {@code null}. This is called inside
-     * {@link #onCreateView}.
-     *
-     * @param inflater The LayoutInflater object that can be used to inflate the views,
-     * @param container The parent view that the additional views are attached to.The fragment
-     *        should not add the view by itself.
-     *
-     * @return The background view for the onboarding screen, or {@code null}.
-     */
-    @Nullable
-    abstract protected View onCreateBackgroundView(LayoutInflater inflater, ViewGroup container);
-
-    /**
-     * Called to have the inherited class create content view. This is optional and the fragment
-     * which doesn't have the content view can return {@code null}. This is called inside
-     * {@link #onCreateView}.
-     *
-     * <p>The content view would be located at the center of the screen.
-     *
-     * @param inflater The LayoutInflater object that can be used to inflate the views,
-     * @param container The parent view that the additional views are attached to.The fragment
-     *        should not add the view by itself.
-     *
-     * @return The content view for the onboarding screen, or {@code null}.
-     */
-    @Nullable
-    abstract protected View onCreateContentView(LayoutInflater inflater, ViewGroup container);
-
-    /**
-     * Called to have the inherited class create foreground view. This is optional and the fragment
-     * which doesn't need the foreground view can return {@code null}. This is called inside
-     * {@link #onCreateView}.
-     *
-     * <p>This foreground view would have the highest z-order.
-     *
-     * @param inflater The LayoutInflater object that can be used to inflate the views,
-     * @param container The parent view that the additional views are attached to.The fragment
-     *        should not add the view by itself.
-     *
-     * @return The foreground view for the onboarding screen, or {@code null}.
-     */
-    @Nullable
-    abstract protected View onCreateForegroundView(LayoutInflater inflater, ViewGroup container);
-
-    /**
-     * Called when the onboarding flow finishes.
-     */
-    protected void onFinishFragment() { }
-
-    /**
-     * Called when the page changes.
-     */
-    private void onPageChangedInternal(int previousPage) {
-        if (mAnimator != null) {
-            mAnimator.end();
-        }
-        mPageIndicator.onPageSelected(mCurrentPageIndex, true);
-
-        List<Animator> animators = new ArrayList<>();
-        // Header animation
-        Animator fadeAnimator = null;
-        if (previousPage < getCurrentPageIndex()) {
-            // sliding to left
-            animators.add(createAnimator(mTitleView, false, Gravity.START, 0));
-            animators.add(fadeAnimator = createAnimator(mDescriptionView, false, Gravity.START,
-                    DESCRIPTION_START_DELAY_MS));
-            animators.add(createAnimator(mTitleView, true, Gravity.END,
-                    HEADER_APPEAR_DELAY_MS));
-            animators.add(createAnimator(mDescriptionView, true, Gravity.END,
-                    HEADER_APPEAR_DELAY_MS + DESCRIPTION_START_DELAY_MS));
-        } else {
-            // sliding to right
-            animators.add(createAnimator(mTitleView, false, Gravity.END, 0));
-            animators.add(fadeAnimator = createAnimator(mDescriptionView, false, Gravity.END,
-                    DESCRIPTION_START_DELAY_MS));
-            animators.add(createAnimator(mTitleView, true, Gravity.START,
-                    HEADER_APPEAR_DELAY_MS));
-            animators.add(createAnimator(mDescriptionView, true, Gravity.START,
-                    HEADER_APPEAR_DELAY_MS + DESCRIPTION_START_DELAY_MS));
-        }
-        final int currentPageIndex = getCurrentPageIndex();
-        fadeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mTitleView.setText(getPageTitle(currentPageIndex));
-                mDescriptionView.setText(getPageDescription(currentPageIndex));
-            }
-        });
-
-        final Context context = getContext();
-        // Animator for switching between page indicator and button.
-        if (getCurrentPageIndex() == getPageCount() - 1) {
-            mStartButton.setVisibility(View.VISIBLE);
-            Animator navigatorFadeOutAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_page_indicator_fade_out);
-            navigatorFadeOutAnimator.setTarget(mPageIndicator);
-            navigatorFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mPageIndicator.setVisibility(View.GONE);
-                }
-            });
-            animators.add(navigatorFadeOutAnimator);
-            Animator buttonFadeInAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_start_button_fade_in);
-            buttonFadeInAnimator.setTarget(mStartButton);
-            animators.add(buttonFadeInAnimator);
-        } else if (previousPage == getPageCount() - 1) {
-            mPageIndicator.setVisibility(View.VISIBLE);
-            Animator navigatorFadeInAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_page_indicator_fade_in);
-            navigatorFadeInAnimator.setTarget(mPageIndicator);
-            animators.add(navigatorFadeInAnimator);
-            Animator buttonFadeOutAnimator = AnimatorInflater.loadAnimator(context,
-                    R.animator.lb_onboarding_start_button_fade_out);
-            buttonFadeOutAnimator.setTarget(mStartButton);
-            buttonFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mStartButton.setVisibility(View.GONE);
-                }
-            });
-            animators.add(buttonFadeOutAnimator);
-        }
-        mAnimator = new AnimatorSet();
-        mAnimator.playTogether(animators);
-        mAnimator.start();
-        onPageChanged(mCurrentPageIndex, previousPage);
-    }
-
-    /**
-     * Called when the page has been changed.
-     *
-     * @param newPage The new page.
-     * @param previousPage The previous page.
-     */
-    protected void onPageChanged(int newPage, int previousPage) { }
-
-    private Animator createAnimator(View view, boolean fadeIn, int slideDirection,
-            long startDelay) {
-        boolean isLtr = getView().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
-        boolean slideRight = (isLtr && slideDirection == Gravity.END)
-                || (!isLtr && slideDirection == Gravity.START)
-                || slideDirection == Gravity.RIGHT;
-        Animator fadeAnimator;
-        Animator slideAnimator;
-        if (fadeIn) {
-            fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);
-            slideAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
-                    slideRight ? sSlideDistance : -sSlideDistance, 0);
-            fadeAnimator.setInterpolator(HEADER_APPEAR_INTERPOLATOR);
-            slideAnimator.setInterpolator(HEADER_APPEAR_INTERPOLATOR);
-        } else {
-            fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.0f, 0.0f);
-            slideAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0,
-                    slideRight ? sSlideDistance : -sSlideDistance);
-            fadeAnimator.setInterpolator(HEADER_DISAPPEAR_INTERPOLATOR);
-            slideAnimator.setInterpolator(HEADER_DISAPPEAR_INTERPOLATOR);
-        }
-        fadeAnimator.setDuration(HEADER_ANIMATION_DURATION_MS);
-        fadeAnimator.setTarget(view);
-        slideAnimator.setDuration(HEADER_ANIMATION_DURATION_MS);
-        slideAnimator.setTarget(view);
-        AnimatorSet animator = new AnimatorSet();
-        animator.playTogether(fadeAnimator, slideAnimator);
-        if (startDelay > 0) {
-            animator.setStartDelay(startDelay);
-        }
-        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/android/support/v17/leanback/app/PermissionHelper.java b/android/support/v17/leanback/app/PermissionHelper.java
deleted file mode 100644
index ba4242b..0000000
--- a/android/support/v17/leanback/app/PermissionHelper.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.v17.leanback.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class PermissionHelper {
-
-    public static void requestPermissions(android.app.Fragment fragment, String[] permissions,
-            int requestCode) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            fragment.requestPermissions(permissions, requestCode);
-        }
-    }
-
-    public static void requestPermissions(android.support.v4.app.Fragment fragment,
-            String[] permissions, int requestCode) {
-        fragment.requestPermissions(permissions, requestCode);
-    }
-
-}
diff --git a/android/support/v17/leanback/app/PlaybackFragment.java b/android/support/v17/leanback/app/PlaybackFragment.java
deleted file mode 100644
index dc59e0e..0000000
--- a/android/support/v17/leanback/app/PlaybackFragment.java
+++ /dev/null
@@ -1,1183 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from PlaybackSupportFragment.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 android.support.v17.leanback.app;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-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.v17.leanback.R;
-import android.support.v17.leanback.animation.LogAccelerateInterpolator;
-import android.support.v17.leanback.animation.LogDecelerateInterpolator;
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
-import android.support.v17.leanback.widget.ClassPresenterSelector;
-import android.support.v17.leanback.widget.ItemAlignmentFacet;
-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;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.app.Fragment;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-
-/**
- * A fragment for displaying playback controls and related content.
- *
- * <p>
- * A PlaybackFragment renders the elements of its {@link ObjectAdapter} as a set
- * of rows in a vertical list.  The Adapter's {@link PresenterSelector} must maintain subclasses
- * of {@link RowPresenter}.
- * </p>
- * <p>
- * A playback row is a row rendered by {@link PlaybackRowPresenter}.
- * App can call {@link #setPlaybackRow(Row)} to set playback row for the first element of adapter.
- * App can call {@link #setPlaybackRowPresenter(PlaybackRowPresenter)} to set presenter for it.
- * {@link #setPlaybackRow(Row)} and {@link #setPlaybackRowPresenter(PlaybackRowPresenter)} are
- * 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>
- * @deprecated use {@link PlaybackSupportFragment}
- */
-@Deprecated
-public class PlaybackFragment extends Fragment {
-    static final String BUNDLE_CONTROL_VISIBLE_ON_CREATEVIEW = "controlvisible_oncreateview";
-
-    /**
-     * No background.
-     */
-    public static final int BG_NONE = 0;
-
-    /**
-     * A dark translucent background.
-     */
-    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
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public void resetFocus() {
-        ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView()
-                .findViewHolderForAdapterPosition(0);
-        if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
-            ((PlaybackRowPresenter) vh.getPresenter()).onReappear(
-                    (RowPresenter.ViewHolder) vh.getViewHolder());
-        }
-    }
-
-    private class SetSelectionRunnable implements Runnable {
-        int mPosition;
-        boolean mSmooth = true;
-
-        @Override
-        public void run() {
-            if (mRowsFragment == null) {
-                return;
-            }
-            mRowsFragment.setSelectedPosition(mPosition, mSmooth);
-        }
-    }
-
-    /**
-     * A light translucent background.
-     */
-    public static final int BG_LIGHT = 2;
-    RowsFragment mRowsFragment;
-    ObjectAdapter mAdapter;
-    PlaybackRowPresenter mPresenter;
-    Row mRow;
-    BaseOnItemViewSelectedListener mExternalItemSelectedListener;
-    BaseOnItemViewClickedListener mExternalItemClickedListener;
-    BaseOnItemViewClickedListener mPlaybackItemClickedListener;
-
-    private final BaseOnItemViewClickedListener mOnItemViewClickedListener =
-            new BaseOnItemViewClickedListener() {
-                @Override
-                public void onItemClicked(Presenter.ViewHolder itemViewHolder,
-                                          Object item,
-                                          RowPresenter.ViewHolder rowViewHolder,
-                                          Object row) {
-                    if (mPlaybackItemClickedListener != null
-                            && rowViewHolder instanceof PlaybackRowPresenter.ViewHolder) {
-                        mPlaybackItemClickedListener.onItemClicked(
-                                itemViewHolder, item, rowViewHolder, row);
-                    }
-                    if (mExternalItemClickedListener != null) {
-                        mExternalItemClickedListener.onItemClicked(
-                                itemViewHolder, item, rowViewHolder, row);
-                    }
-                }
-            };
-
-    private final BaseOnItemViewSelectedListener mOnItemViewSelectedListener =
-            new BaseOnItemViewSelectedListener() {
-                @Override
-                public void onItemSelected(Presenter.ViewHolder itemViewHolder,
-                                           Object item,
-                                           RowPresenter.ViewHolder rowViewHolder,
-                                           Object row) {
-                    if (mExternalItemSelectedListener != null) {
-                        mExternalItemSelectedListener.onItemSelected(
-                                itemViewHolder, item, rowViewHolder, row);
-                    }
-                }
-            };
-
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Listener allowing the application to receive notification of fade in and/or fade out
-     * completion events.
-     * @hide
-     * @deprecated use {@link PlaybackSupportFragment}
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @Deprecated
-    public static class OnFadeCompleteListener {
-        public void onFadeInComplete() {
-        }
-
-        public void onFadeOutComplete() {
-        }
-    }
-
-    private static final String TAG = "PlaybackFragment";
-    private static final boolean DEBUG = false;
-    private static final int ANIMATION_MULTIPLIER = 1;
-
-    private static int START_FADE_OUT = 1;
-
-    // Fading status
-    private static final int IDLE = 0;
-    private static final int ANIMATING = 1;
-
-    int mPaddingBottom;
-    int mOtherRowsCenterToBottom;
-    View mRootView;
-    View mBackgroundView;
-    int mBackgroundType = BG_DARK;
-    int mBgDarkColor;
-    int mBgLightColor;
-    int mShowTimeMs;
-    int mMajorFadeTranslateY, mMinorFadeTranslateY;
-    int mAnimationTranslateY;
-    OnFadeCompleteListener mFadeCompleteListener;
-    View.OnKeyListener mInputEventHandler;
-    boolean mFadingEnabled = true;
-    boolean mControlVisibleBeforeOnCreateView = true;
-    boolean mControlVisible = true;
-    int mBgAlpha;
-    ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator;
-    ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator;
-    ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator;
-
-    private final Animator.AnimatorListener mFadeListener =
-            new Animator.AnimatorListener() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    enableVerticalGridAnimations(false);
-                }
-
-                @Override
-                public void onAnimationRepeat(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha);
-                    if (mBgAlpha > 0) {
-                        enableVerticalGridAnimations(true);
-                        if (mFadeCompleteListener != null) {
-                            mFadeCompleteListener.onFadeInComplete();
-                        }
-                    } else {
-                        VerticalGridView verticalView = getVerticalGridView();
-                        // reset focus to the primary actions only if the selected row was the controls row
-                        if (verticalView != null && verticalView.getSelectedPosition() == 0) {
-                            ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
-                                    verticalView.findViewHolderForAdapterPosition(0);
-                            if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
-                                ((PlaybackRowPresenter)vh.getPresenter()).onReappear(
-                                        (RowPresenter.ViewHolder) vh.getViewHolder());
-                            }
-                        }
-                        if (mFadeCompleteListener != null) {
-                            mFadeCompleteListener.onFadeOutComplete();
-                        }
-                    }
-                }
-            };
-
-    public PlaybackFragment() {
-        mProgressBarManager.setInitialDelay(500);
-    }
-
-    VerticalGridView getVerticalGridView() {
-        if (mRowsFragment == null) {
-            return null;
-        }
-        return mRowsFragment.getVerticalGridView();
-    }
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            if (message.what == START_FADE_OUT && mFadingEnabled) {
-                hideControlsOverlay(true);
-            }
-        }
-    };
-
-    private final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener =
-            new VerticalGridView.OnTouchInterceptListener() {
-                @Override
-                public boolean onInterceptTouchEvent(MotionEvent event) {
-                    return onInterceptInputEvent(event);
-                }
-            };
-
-    private final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener =
-            new VerticalGridView.OnKeyInterceptListener() {
-                @Override
-                public boolean onInterceptKeyEvent(KeyEvent event) {
-                    return onInterceptInputEvent(event);
-                }
-            };
-
-    private void setBgAlpha(int alpha) {
-        mBgAlpha = alpha;
-        if (mBackgroundView != null) {
-            mBackgroundView.getBackground().setAlpha(alpha);
-        }
-    }
-
-    private void enableVerticalGridAnimations(boolean enable) {
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().setAnimateChildLayout(enable);
-        }
-    }
-
-    /**
-     * 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 setControlsOverlayAutoHideEnabled(boolean enabled) {
-        if (DEBUG) Log.v(TAG, "setControlsOverlayAutoHideEnabled " + enabled);
-        if (enabled != mFadingEnabled) {
-            mFadingEnabled = enabled;
-            if (isResumed() && getView().hasFocus()) {
-                showControlsOverlay(true);
-                if (enabled) {
-                    // StateGraph 7->2 5->2
-                    startFadeTimer();
-                } else {
-                    // StateGraph 4->5 2->5
-                    stopFadeTimer();
-                }
-            } else {
-                // StateGraph 6->1 1->6
-            }
-        }
-    }
-
-    /**
-     * Returns true if controls will be auto hidden after a delay when fragment is resumed.
-     */
-    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
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public void setFadeCompleteListener(OnFadeCompleteListener listener) {
-        mFadeCompleteListener = listener;
-    }
-
-    /**
-     * Returns the listener to be called when fade in or out has completed.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public OnFadeCompleteListener getFadeCompleteListener() {
-        return mFadeCompleteListener;
-    }
-
-    /**
-     * Sets the input event handler.
-     */
-    public final void setOnKeyInterceptListener(View.OnKeyListener handler) {
-        mInputEventHandler = handler;
-    }
-
-    /**
-     * 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());
-        //StateGraph 2->4
-        stopFadeTimer();
-        showControlsOverlay(true);
-    }
-
-    private boolean onInterceptInputEvent(InputEvent event) {
-        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);
-            }
-        }
-
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_CENTER:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                // Event may be consumed; regardless, if controls are hidden then these keys will
-                // bring up the controls.
-                if (controlsHidden) {
-                    consumeEvent = true;
-                }
-                if (keyAction == KeyEvent.ACTION_DOWN) {
-                    tickle();
-                }
-                break;
-            case KeyEvent.KEYCODE_BACK:
-            case KeyEvent.KEYCODE_ESCAPE:
-                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 (!controlsHidden) {
-                    consumeEvent = true;
-
-                    if (((KeyEvent) event).getAction() == KeyEvent.ACTION_UP) {
-                        hideControlsOverlay(true);
-                    }
-                }
-                break;
-            default:
-                if (consumeEvent) {
-                    if (keyAction == KeyEvent.ACTION_DOWN) {
-                        tickle();
-                    }
-                }
-        }
-        return consumeEvent;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable 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 (mControlVisible) {
-            //StateGraph: 6->5 1->2
-            if (mFadingEnabled) {
-                // StateGraph 1->2
-                startFadeTimer();
-            }
-        } else {
-            //StateGraph: 6->7 1->3
-        }
-        getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
-        getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
-        if (mHostCallback != null) {
-            mHostCallback.onHostResume();
-        }
-    }
-
-    private void stopFadeTimer() {
-        if (mHandler != null) {
-            mHandler.removeMessages(START_FADE_OUT);
-        }
-    }
-
-    private void startFadeTimer() {
-        if (mHandler != null) {
-            mHandler.removeMessages(START_FADE_OUT);
-            mHandler.sendEmptyMessageDelayed(START_FADE_OUT, mShowTimeMs);
-        }
-    }
-
-    private static ValueAnimator loadAnimator(Context context, int resId) {
-        ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, resId);
-        animator.setDuration(animator.getDuration() * ANIMATION_MULTIPLIER);
-        return animator;
-    }
-
-    private void loadBgAnimator() {
-        AnimatorUpdateListener listener = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                setBgAlpha((Integer) arg0.getAnimatedValue());
-            }
-        };
-
-        Context context = FragmentUtil.getContext(PlaybackFragment.this);
-        mBgFadeInAnimator = loadAnimator(context, R.animator.lb_playback_bg_fade_in);
-        mBgFadeInAnimator.addUpdateListener(listener);
-        mBgFadeInAnimator.addListener(mFadeListener);
-
-        mBgFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_bg_fade_out);
-        mBgFadeOutAnimator.addUpdateListener(listener);
-        mBgFadeOutAnimator.addListener(mFadeListener);
-    }
-
-    private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0);
-    private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0);
-
-    private void loadControlRowAnimator() {
-        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                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);
-                    view.setAlpha(fraction);
-                    view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
-                }
-            }
-        };
-
-        Context context = FragmentUtil.getContext(PlaybackFragment.this);
-        mControlRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
-        mControlRowFadeInAnimator.addUpdateListener(updateListener);
-        mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
-
-        mControlRowFadeOutAnimator = loadAnimator(context,
-                R.animator.lb_playback_controls_fade_out);
-        mControlRowFadeOutAnimator.addUpdateListener(updateListener);
-        mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator);
-    }
-
-    private void loadOtherRowAnimator() {
-        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                if (getVerticalGridView() == null) {
-                    return;
-                }
-                final float fraction = (Float) arg0.getAnimatedValue();
-                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));
-                    }
-                }
-            }
-        };
-
-        Context context = FragmentUtil.getContext(PlaybackFragment.this);
-        mOtherRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
-        mOtherRowFadeInAnimator.addUpdateListener(updateListener);
-        mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
-
-        mOtherRowFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_out);
-        mOtherRowFadeOutAnimator.addUpdateListener(updateListener);
-        mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator());
-    }
-
-    /**
-     * Fades out the playback overlay immediately.
-     * @deprecated Call {@link #hideControlsOverlay(boolean)}
-     */
-    @Deprecated
-    public void fadeOut() {
-        showControlsOverlay(false, false);
-    }
-
-    /**
-     * Show controls overlay.
-     *
-     * @param runAnimation True to run animation, false otherwise.
-     */
-    public void showControlsOverlay(boolean runAnimation) {
-        showControlsOverlay(true, runAnimation);
-    }
-
-    /**
-     * 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 {
-            second.start();
-            if (!runAnimation) {
-                second.end();
-            }
-        }
-    }
-
-    /**
-     * 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();
-        }
-
-        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));
-        }
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.mPosition = position;
-        mSetSelectionRunnable.mSmooth = smooth;
-        if (getView() != null && getView().getHandler() != null) {
-            getView().getHandler().post(mSetSelectionRunnable);
-        }
-    }
-
-    private void setupChildFragmentLayout() {
-        setVerticalGridViewLayout(mRowsFragment.getVerticalGridView());
-    }
-
-    void setVerticalGridViewLayout(VerticalGridView listview) {
-        if (listview == null) {
-            return;
-        }
-
-        // we set the base line of alignment to -paddingBottom
-        listview.setWindowAlignmentOffset(-mPaddingBottom);
-        listview.setWindowAlignmentOffsetPercent(
-                VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-
-        // align other rows that arent the last to center of screen, since our baseline is
-        // -mPaddingBottom, we need subtract that from mOtherRowsCenterToBottom.
-        listview.setItemAlignmentOffset(mOtherRowsCenterToBottom - mPaddingBottom);
-        listview.setItemAlignmentOffsetPercent(50);
-
-        // Push last row to the bottom padding
-        // Padding affects alignment when last row is focused
-        listview.setPadding(listview.getPaddingLeft(), listview.getPaddingTop(),
-                listview.getPaddingRight(), mPaddingBottom);
-        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mOtherRowsCenterToBottom = getResources()
-                .getDimensionPixelSize(R.dimen.lb_playback_other_rows_center_to_bottom);
-        mPaddingBottom =
-                getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_bottom);
-        mBgDarkColor =
-                getResources().getColor(R.color.lb_playback_controls_background_dark);
-        mBgLightColor =
-                getResources().getColor(R.color.lb_playback_controls_background_light);
-        mShowTimeMs =
-                getResources().getInteger(R.integer.lb_playback_controls_show_time_ms);
-        mMajorFadeTranslateY =
-                getResources().getDimensionPixelSize(R.dimen.lb_playback_major_fade_translate_y);
-        mMinorFadeTranslateY =
-                getResources().getDimensionPixelSize(R.dimen.lb_playback_minor_fade_translate_y);
-
-        loadBgAnimator();
-        loadControlRowAnimator();
-        loadOtherRowAnimator();
-    }
-
-    /**
-     * Sets the background type.
-     *
-     * @param type One of BG_LIGHT, BG_DARK, or BG_NONE.
-     */
-    public void setBackgroundType(int type) {
-        switch (type) {
-            case BG_LIGHT:
-            case BG_DARK:
-            case BG_NONE:
-                if (type != mBackgroundType) {
-                    mBackgroundType = type;
-                    updateBackground();
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid background type");
-        }
-    }
-
-    /**
-     * Returns the background type.
-     */
-    public int getBackgroundType() {
-        return mBackgroundType;
-    }
-
-    private void updateBackground() {
-        if (mBackgroundView != null) {
-            int color = mBgDarkColor;
-            switch (mBackgroundType) {
-                case BG_DARK:
-                    break;
-                case BG_LIGHT:
-                    color = mBgLightColor;
-                    break;
-                case BG_NONE:
-                    color = Color.TRANSPARENT;
-                    break;
-            }
-            mBackgroundView.setBackground(new ColorDrawable(color));
-            setBgAlpha(mBgAlpha);
-        }
-    }
-
-    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
-            new ItemBridgeAdapter.AdapterListener() {
-                @Override
-                public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
-                    if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view);
-                    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
-                    vh.getViewHolder().view.setAlpha(1f);
-                    vh.getViewHolder().view.setTranslationY(0);
-                    vh.getViewHolder().view.setAlpha(1f);
-                }
-
-                @Override
-                public void onBind(ItemBridgeAdapter.ViewHolder vh) {
-                }
-            };
-
-    @Override
-    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) {
-            mRowsFragment = new RowsFragment();
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.playback_controls_dock, mRowsFragment)
-                    .commit();
-        }
-        if (mAdapter == null) {
-            setAdapter(new ArrayObjectAdapter(new ClassPresenterSelector()));
-        } else {
-            mRowsFragment.setAdapter(mAdapter);
-        }
-        mRowsFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-        mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-
-        mBgAlpha = 255;
-        updateBackground();
-        mRowsFragment.setExternalAdapterListener(mAdapterListener);
-        ProgressBarManager progressBarManager = getProgressBarManager();
-        if (progressBarManager != null) {
-            progressBarManager.setRootView((ViewGroup) mRootView);
-        }
-        return mRootView;
-    }
-
-    /**
-     * Sets the {@link PlaybackGlueHost.HostCallback}. Implementor of this interface will
-     * take appropriate actions to take action when the hosting fragment starts/stops processing.
-     */
-    public void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) {
-        this.mHostCallback = hostCallback;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        setupChildFragmentLayout();
-        mRowsFragment.setAdapter(mAdapter);
-        if (mHostCallback != null) {
-            mHostCallback.onHostStart();
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mHostCallback != null) {
-            mHostCallback.onHostStop();
-        }
-        super.onStop();
-    }
-
-    @Override
-    public void onPause() {
-        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();
-    }
-
-    /**
-     * This listener is called every time there is a selection in {@link RowsFragment}. This can
-     * be used by users to take additional actions such as animations.
-     */
-    public void setOnItemViewSelectedListener(final BaseOnItemViewSelectedListener listener) {
-        mExternalItemSelectedListener = listener;
-    }
-
-    /**
-     * This listener is called every time there is a click in {@link RowsFragment}. This can
-     * be used by users to take additional actions such as animations.
-     */
-    public void setOnItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
-        mExternalItemClickedListener = listener;
-    }
-
-    /**
-     * Sets the {@link BaseOnItemViewClickedListener} that would be invoked for clicks
-     * only on {@link android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder}.
-     */
-    public void setOnPlaybackItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
-        mPlaybackItemClickedListener = listener;
-    }
-
-    @Override
-    public void onDestroyView() {
-        mRootView = null;
-        mBackgroundView = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mHostCallback != null) {
-            mHostCallback.onHostDestroy();
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * Sets the playback row for the playback controls. The row will be set as first element
-     * of adapter if the adapter is {@link ArrayObjectAdapter} or {@link SparseArrayObjectAdapter}.
-     * @param row The row that represents the playback.
-     */
-    public void setPlaybackRow(Row row) {
-        this.mRow = row;
-        setupRow();
-        setupPresenter();
-    }
-
-    /**
-     * Sets the presenter for rendering the playback row set by {@link #setPlaybackRow(Row)}. If
-     * adapter does not set a {@link PresenterSelector}, {@link #setAdapter(ObjectAdapter)} will
-     * create a {@link ClassPresenterSelector} by default and map from the row object class to this
-     * {@link PlaybackRowPresenter}.
-     *
-     * @param  presenter Presenter used to render {@link #setPlaybackRow(Row)}.
-     */
-    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
-        this.mPresenter = presenter;
-        setupPresenter();
-        setPlaybackRowPresenterAlignment();
-    }
-
-    void setPlaybackRowPresenterAlignment() {
-        if (mAdapter != null && mAdapter.getPresenterSelector() != null) {
-            Presenter[] presenters = mAdapter.getPresenterSelector().getPresenters();
-            if (presenters != null) {
-                for (int i = 0; i < presenters.length; i++) {
-                    if (presenters[i] instanceof PlaybackRowPresenter
-                            && presenters[i].getFacet(ItemAlignmentFacet.class) == null) {
-                        ItemAlignmentFacet itemAlignment = new ItemAlignmentFacet();
-                        ItemAlignmentFacet.ItemAlignmentDef def =
-                                new ItemAlignmentFacet.ItemAlignmentDef();
-                        def.setItemAlignmentOffset(0);
-                        def.setItemAlignmentOffsetPercent(100);
-                        itemAlignment.setAlignmentDefs(new ItemAlignmentFacet.ItemAlignmentDef[]
-                                {def});
-                        presenters[i].setFacet(ItemAlignmentFacet.class, itemAlignment);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Updates the ui when the row data changes.
-     */
-    public void notifyPlaybackRowChanged() {
-        if (mAdapter == null) {
-            return;
-        }
-        mAdapter.notifyItemRangeChanged(0, 1);
-    }
-
-    /**
-     * Sets the list of rows for the fragment. A default {@link ClassPresenterSelector} will be
-     * created if {@link ObjectAdapter#getPresenterSelector()} is null. if user provides
-     * {@link #setPlaybackRow(Row)} and {@link #setPlaybackRowPresenter(PlaybackRowPresenter)},
-     * the row and presenter will be set onto the adapter.
-     *
-     * @param adapter The adapter that contains related rows and optional playback row.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        setupRow();
-        setupPresenter();
-        setPlaybackRowPresenterAlignment();
-
-        if (mRowsFragment != null) {
-            mRowsFragment.setAdapter(adapter);
-        }
-    }
-
-    private void setupRow() {
-        if (mAdapter instanceof ArrayObjectAdapter && mRow != null) {
-            ArrayObjectAdapter adapter = ((ArrayObjectAdapter) mAdapter);
-            if (adapter.size() == 0) {
-                adapter.add(mRow);
-            } else {
-                adapter.replace(0, mRow);
-            }
-        } else if (mAdapter instanceof SparseArrayObjectAdapter && mRow != null) {
-            SparseArrayObjectAdapter adapter = ((SparseArrayObjectAdapter) mAdapter);
-            adapter.set(0, mRow);
-        }
-    }
-
-    private void setupPresenter() {
-        if (mAdapter != null && mRow != null && mPresenter != null) {
-            PresenterSelector selector = mAdapter.getPresenterSelector();
-            if (selector == null) {
-                selector = new ClassPresenterSelector();
-                ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter);
-                mAdapter.setPresenterSelector(selector);
-            } else if (selector instanceof ClassPresenterSelector) {
-                ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter);
-            }
-        }
-    }
-
-    final PlaybackSeekUi.Client mChainedClient = new PlaybackSeekUi.Client() {
-        @Override
-        public boolean isSeekEnabled() {
-            return mSeekUiClient == null ? false : mSeekUiClient.isSeekEnabled();
-        }
-
-        @Override
-        public void onSeekStarted() {
-            if (mSeekUiClient != null) {
-                mSeekUiClient.onSeekStarted();
-            }
-            setSeekMode(true);
-        }
-
-        @Override
-        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
-            return mSeekUiClient == null ? null : mSeekUiClient.getPlaybackSeekDataProvider();
-        }
-
-        @Override
-        public void onSeekPositionChanged(long pos) {
-            if (mSeekUiClient != null) {
-                mSeekUiClient.onSeekPositionChanged(pos);
-            }
-        }
-
-        @Override
-        public void onSeekFinished(boolean cancelled) {
-            if (mSeekUiClient != null) {
-                mSeekUiClient.onSeekFinished(cancelled);
-            }
-            setSeekMode(false);
-        }
-    };
-
-    /**
-     * 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/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java b/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
deleted file mode 100644
index 9e342fd..0000000
--- a/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
+++ /dev/null
@@ -1,142 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from {}PlaybackSupportFragmentGlueHost.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 android.support.v17.leanback.app;
-
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.widget.Action;
-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;
-import android.view.View;
-
-/**
- * {@link PlaybackGlueHost} implementation
- * the interaction between this class and {@link PlaybackFragment}.
- * @deprecated use {@link PlaybackSupportFragmentGlueHost}
- */
-@Deprecated
-public class PlaybackFragmentGlueHost extends PlaybackGlueHost implements PlaybackSeekUi {
-    private final PlaybackFragment mFragment;
-
-    public PlaybackFragmentGlueHost(PlaybackFragment fragment) {
-        this.mFragment = fragment;
-    }
-
-    @Override
-    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
-        mFragment.setControlsOverlayAutoHideEnabled(enabled);
-    }
-
-    @Override
-    public boolean isControlsOverlayAutoHideEnabled() {
-        return mFragment.isControlsOverlayAutoHideEnabled();
-    }
-
-    @Override
-    public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
-        mFragment.setOnKeyInterceptListener(onKeyListener);
-    }
-
-    @Override
-    public void setOnActionClickedListener(final OnActionClickedListener listener) {
-        if (listener == null) {
-            mFragment.setOnPlaybackItemViewClickedListener(null);
-        } else {
-            mFragment.setOnPlaybackItemViewClickedListener(new OnItemViewClickedListener() {
-                @Override
-                public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                                          RowPresenter.ViewHolder rowViewHolder, Row row) {
-                    if (item instanceof Action) {
-                        listener.onActionClicked((Action) item);
-                    }
-                }
-            });
-        }
-    }
-
-    @Override
-    public void setHostCallback(HostCallback callback) {
-        mFragment.setHostCallback(callback);
-    }
-
-    @Override
-    public void notifyPlaybackRowChanged() {
-        mFragment.notifyPlaybackRowChanged();
-    }
-
-    @Override
-    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
-        mFragment.setPlaybackRowPresenter(presenter);
-    }
-
-    @Override
-    public void setPlaybackRow(Row row) {
-        mFragment.setPlaybackRow(row);
-    }
-
-    @Override
-    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/android/support/v17/leanback/app/PlaybackSupportFragment.java b/android/support/v17/leanback/app/PlaybackSupportFragment.java
deleted file mode 100644
index ee17e84..0000000
--- a/android/support/v17/leanback/app/PlaybackSupportFragment.java
+++ /dev/null
@@ -1,1176 +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.v17.leanback.app;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-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.v17.leanback.R;
-import android.support.v17.leanback.animation.LogAccelerateInterpolator;
-import android.support.v17.leanback.animation.LogDecelerateInterpolator;
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
-import android.support.v17.leanback.widget.ClassPresenterSelector;
-import android.support.v17.leanback.widget.ItemAlignmentFacet;
-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;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v4.app.Fragment;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-
-/**
- * A fragment for displaying playback controls and related content.
- *
- * <p>
- * A PlaybackSupportFragment renders the elements of its {@link ObjectAdapter} as a set
- * of rows in a vertical list.  The Adapter's {@link PresenterSelector} must maintain subclasses
- * of {@link RowPresenter}.
- * </p>
- * <p>
- * A playback row is a row rendered by {@link PlaybackRowPresenter}.
- * App can call {@link #setPlaybackRow(Row)} to set playback row for the first element of adapter.
- * App can call {@link #setPlaybackRowPresenter(PlaybackRowPresenter)} to set presenter for it.
- * {@link #setPlaybackRow(Row)} and {@link #setPlaybackRowPresenter(PlaybackRowPresenter)} are
- * 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.
-     */
-    public static final int BG_NONE = 0;
-
-    /**
-     * A dark translucent background.
-     */
-    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
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public void resetFocus() {
-        ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView()
-                .findViewHolderForAdapterPosition(0);
-        if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
-            ((PlaybackRowPresenter) vh.getPresenter()).onReappear(
-                    (RowPresenter.ViewHolder) vh.getViewHolder());
-        }
-    }
-
-    private class SetSelectionRunnable implements Runnable {
-        int mPosition;
-        boolean mSmooth = true;
-
-        @Override
-        public void run() {
-            if (mRowsSupportFragment == null) {
-                return;
-            }
-            mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth);
-        }
-    }
-
-    /**
-     * A light translucent background.
-     */
-    public static final int BG_LIGHT = 2;
-    RowsSupportFragment mRowsSupportFragment;
-    ObjectAdapter mAdapter;
-    PlaybackRowPresenter mPresenter;
-    Row mRow;
-    BaseOnItemViewSelectedListener mExternalItemSelectedListener;
-    BaseOnItemViewClickedListener mExternalItemClickedListener;
-    BaseOnItemViewClickedListener mPlaybackItemClickedListener;
-
-    private final BaseOnItemViewClickedListener mOnItemViewClickedListener =
-            new BaseOnItemViewClickedListener() {
-                @Override
-                public void onItemClicked(Presenter.ViewHolder itemViewHolder,
-                                          Object item,
-                                          RowPresenter.ViewHolder rowViewHolder,
-                                          Object row) {
-                    if (mPlaybackItemClickedListener != null
-                            && rowViewHolder instanceof PlaybackRowPresenter.ViewHolder) {
-                        mPlaybackItemClickedListener.onItemClicked(
-                                itemViewHolder, item, rowViewHolder, row);
-                    }
-                    if (mExternalItemClickedListener != null) {
-                        mExternalItemClickedListener.onItemClicked(
-                                itemViewHolder, item, rowViewHolder, row);
-                    }
-                }
-            };
-
-    private final BaseOnItemViewSelectedListener mOnItemViewSelectedListener =
-            new BaseOnItemViewSelectedListener() {
-                @Override
-                public void onItemSelected(Presenter.ViewHolder itemViewHolder,
-                                           Object item,
-                                           RowPresenter.ViewHolder rowViewHolder,
-                                           Object row) {
-                    if (mExternalItemSelectedListener != null) {
-                        mExternalItemSelectedListener.onItemSelected(
-                                itemViewHolder, item, rowViewHolder, row);
-                    }
-                }
-            };
-
-    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
-
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Listener allowing the application to receive notification of fade in and/or fade out
-     * completion events.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public static class OnFadeCompleteListener {
-        public void onFadeInComplete() {
-        }
-
-        public void onFadeOutComplete() {
-        }
-    }
-
-    private static final String TAG = "PlaybackSupportFragment";
-    private static final boolean DEBUG = false;
-    private static final int ANIMATION_MULTIPLIER = 1;
-
-    private static int START_FADE_OUT = 1;
-
-    // Fading status
-    private static final int IDLE = 0;
-    private static final int ANIMATING = 1;
-
-    int mPaddingBottom;
-    int mOtherRowsCenterToBottom;
-    View mRootView;
-    View mBackgroundView;
-    int mBackgroundType = BG_DARK;
-    int mBgDarkColor;
-    int mBgLightColor;
-    int mShowTimeMs;
-    int mMajorFadeTranslateY, mMinorFadeTranslateY;
-    int mAnimationTranslateY;
-    OnFadeCompleteListener mFadeCompleteListener;
-    View.OnKeyListener mInputEventHandler;
-    boolean mFadingEnabled = true;
-    boolean mControlVisibleBeforeOnCreateView = true;
-    boolean mControlVisible = true;
-    int mBgAlpha;
-    ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator;
-    ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator;
-    ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator;
-
-    private final Animator.AnimatorListener mFadeListener =
-            new Animator.AnimatorListener() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    enableVerticalGridAnimations(false);
-                }
-
-                @Override
-                public void onAnimationRepeat(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha);
-                    if (mBgAlpha > 0) {
-                        enableVerticalGridAnimations(true);
-                        if (mFadeCompleteListener != null) {
-                            mFadeCompleteListener.onFadeInComplete();
-                        }
-                    } else {
-                        VerticalGridView verticalView = getVerticalGridView();
-                        // reset focus to the primary actions only if the selected row was the controls row
-                        if (verticalView != null && verticalView.getSelectedPosition() == 0) {
-                            ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder)
-                                    verticalView.findViewHolderForAdapterPosition(0);
-                            if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) {
-                                ((PlaybackRowPresenter)vh.getPresenter()).onReappear(
-                                        (RowPresenter.ViewHolder) vh.getViewHolder());
-                            }
-                        }
-                        if (mFadeCompleteListener != null) {
-                            mFadeCompleteListener.onFadeOutComplete();
-                        }
-                    }
-                }
-            };
-
-    public PlaybackSupportFragment() {
-        mProgressBarManager.setInitialDelay(500);
-    }
-
-    VerticalGridView getVerticalGridView() {
-        if (mRowsSupportFragment == null) {
-            return null;
-        }
-        return mRowsSupportFragment.getVerticalGridView();
-    }
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            if (message.what == START_FADE_OUT && mFadingEnabled) {
-                hideControlsOverlay(true);
-            }
-        }
-    };
-
-    private final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener =
-            new VerticalGridView.OnTouchInterceptListener() {
-                @Override
-                public boolean onInterceptTouchEvent(MotionEvent event) {
-                    return onInterceptInputEvent(event);
-                }
-            };
-
-    private final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener =
-            new VerticalGridView.OnKeyInterceptListener() {
-                @Override
-                public boolean onInterceptKeyEvent(KeyEvent event) {
-                    return onInterceptInputEvent(event);
-                }
-            };
-
-    private void setBgAlpha(int alpha) {
-        mBgAlpha = alpha;
-        if (mBackgroundView != null) {
-            mBackgroundView.getBackground().setAlpha(alpha);
-        }
-    }
-
-    private void enableVerticalGridAnimations(boolean enable) {
-        if (getVerticalGridView() != null) {
-            getVerticalGridView().setAnimateChildLayout(enable);
-        }
-    }
-
-    /**
-     * 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 setControlsOverlayAutoHideEnabled(boolean enabled) {
-        if (DEBUG) Log.v(TAG, "setControlsOverlayAutoHideEnabled " + enabled);
-        if (enabled != mFadingEnabled) {
-            mFadingEnabled = enabled;
-            if (isResumed() && getView().hasFocus()) {
-                showControlsOverlay(true);
-                if (enabled) {
-                    // StateGraph 7->2 5->2
-                    startFadeTimer();
-                } else {
-                    // StateGraph 4->5 2->5
-                    stopFadeTimer();
-                }
-            } else {
-                // StateGraph 6->1 1->6
-            }
-        }
-    }
-
-    /**
-     * Returns true if controls will be auto hidden after a delay when fragment is resumed.
-     */
-    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
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public void setFadeCompleteListener(OnFadeCompleteListener listener) {
-        mFadeCompleteListener = listener;
-    }
-
-    /**
-     * Returns the listener to be called when fade in or out has completed.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public OnFadeCompleteListener getFadeCompleteListener() {
-        return mFadeCompleteListener;
-    }
-
-    /**
-     * Sets the input event handler.
-     */
-    public final void setOnKeyInterceptListener(View.OnKeyListener handler) {
-        mInputEventHandler = handler;
-    }
-
-    /**
-     * 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());
-        //StateGraph 2->4
-        stopFadeTimer();
-        showControlsOverlay(true);
-    }
-
-    private boolean onInterceptInputEvent(InputEvent event) {
-        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);
-            }
-        }
-
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_CENTER:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                // Event may be consumed; regardless, if controls are hidden then these keys will
-                // bring up the controls.
-                if (controlsHidden) {
-                    consumeEvent = true;
-                }
-                if (keyAction == KeyEvent.ACTION_DOWN) {
-                    tickle();
-                }
-                break;
-            case KeyEvent.KEYCODE_BACK:
-            case KeyEvent.KEYCODE_ESCAPE:
-                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 (!controlsHidden) {
-                    consumeEvent = true;
-
-                    if (((KeyEvent) event).getAction() == KeyEvent.ACTION_UP) {
-                        hideControlsOverlay(true);
-                    }
-                }
-                break;
-            default:
-                if (consumeEvent) {
-                    if (keyAction == KeyEvent.ACTION_DOWN) {
-                        tickle();
-                    }
-                }
-        }
-        return consumeEvent;
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable 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 (mControlVisible) {
-            //StateGraph: 6->5 1->2
-            if (mFadingEnabled) {
-                // StateGraph 1->2
-                startFadeTimer();
-            }
-        } else {
-            //StateGraph: 6->7 1->3
-        }
-        getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
-        getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
-        if (mHostCallback != null) {
-            mHostCallback.onHostResume();
-        }
-    }
-
-    private void stopFadeTimer() {
-        if (mHandler != null) {
-            mHandler.removeMessages(START_FADE_OUT);
-        }
-    }
-
-    private void startFadeTimer() {
-        if (mHandler != null) {
-            mHandler.removeMessages(START_FADE_OUT);
-            mHandler.sendEmptyMessageDelayed(START_FADE_OUT, mShowTimeMs);
-        }
-    }
-
-    private static ValueAnimator loadAnimator(Context context, int resId) {
-        ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, resId);
-        animator.setDuration(animator.getDuration() * ANIMATION_MULTIPLIER);
-        return animator;
-    }
-
-    private void loadBgAnimator() {
-        AnimatorUpdateListener listener = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                setBgAlpha((Integer) arg0.getAnimatedValue());
-            }
-        };
-
-        Context context = getContext();
-        mBgFadeInAnimator = loadAnimator(context, R.animator.lb_playback_bg_fade_in);
-        mBgFadeInAnimator.addUpdateListener(listener);
-        mBgFadeInAnimator.addListener(mFadeListener);
-
-        mBgFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_bg_fade_out);
-        mBgFadeOutAnimator.addUpdateListener(listener);
-        mBgFadeOutAnimator.addListener(mFadeListener);
-    }
-
-    private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0);
-    private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0);
-
-    private void loadControlRowAnimator() {
-        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                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);
-                    view.setAlpha(fraction);
-                    view.setTranslationY((float) mAnimationTranslateY * (1f - fraction));
-                }
-            }
-        };
-
-        Context context = getContext();
-        mControlRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
-        mControlRowFadeInAnimator.addUpdateListener(updateListener);
-        mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
-
-        mControlRowFadeOutAnimator = loadAnimator(context,
-                R.animator.lb_playback_controls_fade_out);
-        mControlRowFadeOutAnimator.addUpdateListener(updateListener);
-        mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator);
-    }
-
-    private void loadOtherRowAnimator() {
-        final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                if (getVerticalGridView() == null) {
-                    return;
-                }
-                final float fraction = (Float) arg0.getAnimatedValue();
-                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));
-                    }
-                }
-            }
-        };
-
-        Context context = getContext();
-        mOtherRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in);
-        mOtherRowFadeInAnimator.addUpdateListener(updateListener);
-        mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator);
-
-        mOtherRowFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_out);
-        mOtherRowFadeOutAnimator.addUpdateListener(updateListener);
-        mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator());
-    }
-
-    /**
-     * Fades out the playback overlay immediately.
-     * @deprecated Call {@link #hideControlsOverlay(boolean)}
-     */
-    @Deprecated
-    public void fadeOut() {
-        showControlsOverlay(false, false);
-    }
-
-    /**
-     * Show controls overlay.
-     *
-     * @param runAnimation True to run animation, false otherwise.
-     */
-    public void showControlsOverlay(boolean runAnimation) {
-        showControlsOverlay(true, runAnimation);
-    }
-
-    /**
-     * 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 {
-            second.start();
-            if (!runAnimation) {
-                second.end();
-            }
-        }
-    }
-
-    /**
-     * 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();
-        }
-
-        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));
-        }
-    }
-
-    /**
-     * Sets the selected row position with smooth animation.
-     */
-    public void setSelectedPosition(int position) {
-        setSelectedPosition(position, true);
-    }
-
-    /**
-     * Sets the selected row position.
-     */
-    public void setSelectedPosition(int position, boolean smooth) {
-        mSetSelectionRunnable.mPosition = position;
-        mSetSelectionRunnable.mSmooth = smooth;
-        if (getView() != null && getView().getHandler() != null) {
-            getView().getHandler().post(mSetSelectionRunnable);
-        }
-    }
-
-    private void setupChildFragmentLayout() {
-        setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
-    }
-
-    void setVerticalGridViewLayout(VerticalGridView listview) {
-        if (listview == null) {
-            return;
-        }
-
-        // we set the base line of alignment to -paddingBottom
-        listview.setWindowAlignmentOffset(-mPaddingBottom);
-        listview.setWindowAlignmentOffsetPercent(
-                VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-
-        // align other rows that arent the last to center of screen, since our baseline is
-        // -mPaddingBottom, we need subtract that from mOtherRowsCenterToBottom.
-        listview.setItemAlignmentOffset(mOtherRowsCenterToBottom - mPaddingBottom);
-        listview.setItemAlignmentOffsetPercent(50);
-
-        // Push last row to the bottom padding
-        // Padding affects alignment when last row is focused
-        listview.setPadding(listview.getPaddingLeft(), listview.getPaddingTop(),
-                listview.getPaddingRight(), mPaddingBottom);
-        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_HIGH_EDGE);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mOtherRowsCenterToBottom = getResources()
-                .getDimensionPixelSize(R.dimen.lb_playback_other_rows_center_to_bottom);
-        mPaddingBottom =
-                getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_bottom);
-        mBgDarkColor =
-                getResources().getColor(R.color.lb_playback_controls_background_dark);
-        mBgLightColor =
-                getResources().getColor(R.color.lb_playback_controls_background_light);
-        mShowTimeMs =
-                getResources().getInteger(R.integer.lb_playback_controls_show_time_ms);
-        mMajorFadeTranslateY =
-                getResources().getDimensionPixelSize(R.dimen.lb_playback_major_fade_translate_y);
-        mMinorFadeTranslateY =
-                getResources().getDimensionPixelSize(R.dimen.lb_playback_minor_fade_translate_y);
-
-        loadBgAnimator();
-        loadControlRowAnimator();
-        loadOtherRowAnimator();
-    }
-
-    /**
-     * Sets the background type.
-     *
-     * @param type One of BG_LIGHT, BG_DARK, or BG_NONE.
-     */
-    public void setBackgroundType(int type) {
-        switch (type) {
-            case BG_LIGHT:
-            case BG_DARK:
-            case BG_NONE:
-                if (type != mBackgroundType) {
-                    mBackgroundType = type;
-                    updateBackground();
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid background type");
-        }
-    }
-
-    /**
-     * Returns the background type.
-     */
-    public int getBackgroundType() {
-        return mBackgroundType;
-    }
-
-    private void updateBackground() {
-        if (mBackgroundView != null) {
-            int color = mBgDarkColor;
-            switch (mBackgroundType) {
-                case BG_DARK:
-                    break;
-                case BG_LIGHT:
-                    color = mBgLightColor;
-                    break;
-                case BG_NONE:
-                    color = Color.TRANSPARENT;
-                    break;
-            }
-            mBackgroundView.setBackground(new ColorDrawable(color));
-            setBgAlpha(mBgAlpha);
-        }
-    }
-
-    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
-            new ItemBridgeAdapter.AdapterListener() {
-                @Override
-                public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
-                    if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view);
-                    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
-                    vh.getViewHolder().view.setAlpha(1f);
-                    vh.getViewHolder().view.setTranslationY(0);
-                    vh.getViewHolder().view.setAlpha(1f);
-                }
-
-                @Override
-                public void onBind(ItemBridgeAdapter.ViewHolder vh) {
-                }
-            };
-
-    @Override
-    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) {
-            mRowsSupportFragment = new RowsSupportFragment();
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.playback_controls_dock, mRowsSupportFragment)
-                    .commit();
-        }
-        if (mAdapter == null) {
-            setAdapter(new ArrayObjectAdapter(new ClassPresenterSelector()));
-        } else {
-            mRowsSupportFragment.setAdapter(mAdapter);
-        }
-        mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-        mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-
-        mBgAlpha = 255;
-        updateBackground();
-        mRowsSupportFragment.setExternalAdapterListener(mAdapterListener);
-        ProgressBarManager progressBarManager = getProgressBarManager();
-        if (progressBarManager != null) {
-            progressBarManager.setRootView((ViewGroup) mRootView);
-        }
-        return mRootView;
-    }
-
-    /**
-     * Sets the {@link PlaybackGlueHost.HostCallback}. Implementor of this interface will
-     * take appropriate actions to take action when the hosting fragment starts/stops processing.
-     */
-    public void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) {
-        this.mHostCallback = hostCallback;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        setupChildFragmentLayout();
-        mRowsSupportFragment.setAdapter(mAdapter);
-        if (mHostCallback != null) {
-            mHostCallback.onHostStart();
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mHostCallback != null) {
-            mHostCallback.onHostStop();
-        }
-        super.onStop();
-    }
-
-    @Override
-    public void onPause() {
-        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();
-    }
-
-    /**
-     * This listener is called every time there is a selection in {@link RowsSupportFragment}. This can
-     * be used by users to take additional actions such as animations.
-     */
-    public void setOnItemViewSelectedListener(final BaseOnItemViewSelectedListener listener) {
-        mExternalItemSelectedListener = listener;
-    }
-
-    /**
-     * This listener is called every time there is a click in {@link RowsSupportFragment}. This can
-     * be used by users to take additional actions such as animations.
-     */
-    public void setOnItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
-        mExternalItemClickedListener = listener;
-    }
-
-    /**
-     * Sets the {@link BaseOnItemViewClickedListener} that would be invoked for clicks
-     * only on {@link android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder}.
-     */
-    public void setOnPlaybackItemViewClickedListener(final BaseOnItemViewClickedListener listener) {
-        mPlaybackItemClickedListener = listener;
-    }
-
-    @Override
-    public void onDestroyView() {
-        mRootView = null;
-        mBackgroundView = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mHostCallback != null) {
-            mHostCallback.onHostDestroy();
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * Sets the playback row for the playback controls. The row will be set as first element
-     * of adapter if the adapter is {@link ArrayObjectAdapter} or {@link SparseArrayObjectAdapter}.
-     * @param row The row that represents the playback.
-     */
-    public void setPlaybackRow(Row row) {
-        this.mRow = row;
-        setupRow();
-        setupPresenter();
-    }
-
-    /**
-     * Sets the presenter for rendering the playback row set by {@link #setPlaybackRow(Row)}. If
-     * adapter does not set a {@link PresenterSelector}, {@link #setAdapter(ObjectAdapter)} will
-     * create a {@link ClassPresenterSelector} by default and map from the row object class to this
-     * {@link PlaybackRowPresenter}.
-     *
-     * @param  presenter Presenter used to render {@link #setPlaybackRow(Row)}.
-     */
-    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
-        this.mPresenter = presenter;
-        setupPresenter();
-        setPlaybackRowPresenterAlignment();
-    }
-
-    void setPlaybackRowPresenterAlignment() {
-        if (mAdapter != null && mAdapter.getPresenterSelector() != null) {
-            Presenter[] presenters = mAdapter.getPresenterSelector().getPresenters();
-            if (presenters != null) {
-                for (int i = 0; i < presenters.length; i++) {
-                    if (presenters[i] instanceof PlaybackRowPresenter
-                            && presenters[i].getFacet(ItemAlignmentFacet.class) == null) {
-                        ItemAlignmentFacet itemAlignment = new ItemAlignmentFacet();
-                        ItemAlignmentFacet.ItemAlignmentDef def =
-                                new ItemAlignmentFacet.ItemAlignmentDef();
-                        def.setItemAlignmentOffset(0);
-                        def.setItemAlignmentOffsetPercent(100);
-                        itemAlignment.setAlignmentDefs(new ItemAlignmentFacet.ItemAlignmentDef[]
-                                {def});
-                        presenters[i].setFacet(ItemAlignmentFacet.class, itemAlignment);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Updates the ui when the row data changes.
-     */
-    public void notifyPlaybackRowChanged() {
-        if (mAdapter == null) {
-            return;
-        }
-        mAdapter.notifyItemRangeChanged(0, 1);
-    }
-
-    /**
-     * Sets the list of rows for the fragment. A default {@link ClassPresenterSelector} will be
-     * created if {@link ObjectAdapter#getPresenterSelector()} is null. if user provides
-     * {@link #setPlaybackRow(Row)} and {@link #setPlaybackRowPresenter(PlaybackRowPresenter)},
-     * the row and presenter will be set onto the adapter.
-     *
-     * @param adapter The adapter that contains related rows and optional playback row.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        setupRow();
-        setupPresenter();
-        setPlaybackRowPresenterAlignment();
-
-        if (mRowsSupportFragment != null) {
-            mRowsSupportFragment.setAdapter(adapter);
-        }
-    }
-
-    private void setupRow() {
-        if (mAdapter instanceof ArrayObjectAdapter && mRow != null) {
-            ArrayObjectAdapter adapter = ((ArrayObjectAdapter) mAdapter);
-            if (adapter.size() == 0) {
-                adapter.add(mRow);
-            } else {
-                adapter.replace(0, mRow);
-            }
-        } else if (mAdapter instanceof SparseArrayObjectAdapter && mRow != null) {
-            SparseArrayObjectAdapter adapter = ((SparseArrayObjectAdapter) mAdapter);
-            adapter.set(0, mRow);
-        }
-    }
-
-    private void setupPresenter() {
-        if (mAdapter != null && mRow != null && mPresenter != null) {
-            PresenterSelector selector = mAdapter.getPresenterSelector();
-            if (selector == null) {
-                selector = new ClassPresenterSelector();
-                ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter);
-                mAdapter.setPresenterSelector(selector);
-            } else if (selector instanceof ClassPresenterSelector) {
-                ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter);
-            }
-        }
-    }
-
-    final PlaybackSeekUi.Client mChainedClient = new PlaybackSeekUi.Client() {
-        @Override
-        public boolean isSeekEnabled() {
-            return mSeekUiClient == null ? false : mSeekUiClient.isSeekEnabled();
-        }
-
-        @Override
-        public void onSeekStarted() {
-            if (mSeekUiClient != null) {
-                mSeekUiClient.onSeekStarted();
-            }
-            setSeekMode(true);
-        }
-
-        @Override
-        public PlaybackSeekDataProvider getPlaybackSeekDataProvider() {
-            return mSeekUiClient == null ? null : mSeekUiClient.getPlaybackSeekDataProvider();
-        }
-
-        @Override
-        public void onSeekPositionChanged(long pos) {
-            if (mSeekUiClient != null) {
-                mSeekUiClient.onSeekPositionChanged(pos);
-            }
-        }
-
-        @Override
-        public void onSeekFinished(boolean cancelled) {
-            if (mSeekUiClient != null) {
-                mSeekUiClient.onSeekFinished(cancelled);
-            }
-            setSeekMode(false);
-        }
-    };
-
-    /**
-     * 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/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java b/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
deleted file mode 100644
index e745094..0000000
--- a/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
+++ /dev/null
@@ -1,137 +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.v17.leanback.app;
-
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.widget.Action;
-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;
-import android.view.View;
-
-/**
- * {@link PlaybackGlueHost} implementation
- * the interaction between this class and {@link PlaybackSupportFragment}.
- */
-public class PlaybackSupportFragmentGlueHost extends PlaybackGlueHost implements PlaybackSeekUi {
-    private final PlaybackSupportFragment mFragment;
-
-    public PlaybackSupportFragmentGlueHost(PlaybackSupportFragment fragment) {
-        this.mFragment = fragment;
-    }
-
-    @Override
-    public void setControlsOverlayAutoHideEnabled(boolean enabled) {
-        mFragment.setControlsOverlayAutoHideEnabled(enabled);
-    }
-
-    @Override
-    public boolean isControlsOverlayAutoHideEnabled() {
-        return mFragment.isControlsOverlayAutoHideEnabled();
-    }
-
-    @Override
-    public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
-        mFragment.setOnKeyInterceptListener(onKeyListener);
-    }
-
-    @Override
-    public void setOnActionClickedListener(final OnActionClickedListener listener) {
-        if (listener == null) {
-            mFragment.setOnPlaybackItemViewClickedListener(null);
-        } else {
-            mFragment.setOnPlaybackItemViewClickedListener(new OnItemViewClickedListener() {
-                @Override
-                public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                                          RowPresenter.ViewHolder rowViewHolder, Row row) {
-                    if (item instanceof Action) {
-                        listener.onActionClicked((Action) item);
-                    }
-                }
-            });
-        }
-    }
-
-    @Override
-    public void setHostCallback(HostCallback callback) {
-        mFragment.setHostCallback(callback);
-    }
-
-    @Override
-    public void notifyPlaybackRowChanged() {
-        mFragment.notifyPlaybackRowChanged();
-    }
-
-    @Override
-    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
-        mFragment.setPlaybackRowPresenter(presenter);
-    }
-
-    @Override
-    public void setPlaybackRow(Row row) {
-        mFragment.setPlaybackRow(row);
-    }
-
-    @Override
-    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/android/support/v17/leanback/app/ProgressBarManager.java b/android/support/v17/leanback/app/ProgressBarManager.java
deleted file mode 100644
index 895ed6b..0000000
--- a/android/support/v17/leanback/app/ProgressBarManager.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package android.support.v17.leanback.app;
-
-import android.os.Handler;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ProgressBar;
-
-/**
- * Manager for showing/hiding progress bar widget. This class lets user specify an initial
- * delay after which the progress bar will be shown. This is currently being used in
- * {@link BrowseFragment} & {@link VerticalGridFragment} to show {@link ProgressBar}
- * while the data is being loaded.
- */
-public final class ProgressBarManager {
-    // Default delay for progress bar widget.
-    private static final long DEFAULT_PROGRESS_BAR_DELAY = 1000;
-
-    private long mInitialDelay = DEFAULT_PROGRESS_BAR_DELAY;
-    ViewGroup rootView;
-    View mProgressBarView;
-    private Handler mHandler = new Handler();
-    boolean mEnableProgressBar = true;
-    boolean mUserProvidedProgressBar;
-    boolean mIsShowing;
-
-    private Runnable runnable = new Runnable() {
-        @Override
-        public void run() {
-            if (!mEnableProgressBar || (!mUserProvidedProgressBar && rootView == null)) {
-                return;
-            }
-
-            if (mIsShowing) {
-                if (mProgressBarView == null) {
-                    mProgressBarView = new ProgressBar(
-                            rootView.getContext(), null, android.R.attr.progressBarStyleLarge);
-                    FrameLayout.LayoutParams progressBarParams = new FrameLayout.LayoutParams(
-                            FrameLayout.LayoutParams.WRAP_CONTENT,
-                            FrameLayout.LayoutParams.WRAP_CONTENT);
-                    progressBarParams.gravity = Gravity.CENTER;
-                    rootView.addView(mProgressBarView, progressBarParams);
-                } else if (mUserProvidedProgressBar) {
-                    mProgressBarView.setVisibility(View.VISIBLE);
-                }
-            }
-        }
-    };
-
-    /**
-     * Sets the root view on which the progress bar will be attached. This class assumes the
-     * root view to be {@link FrameLayout} in order to position the progress bar widget
-     * in the center of the screen.
-     *
-     * @param rootView view that will contain the progress bar.
-     */
-    public void setRootView(ViewGroup rootView) {
-        this.rootView = rootView;
-    }
-
-    /**
-     * Displays the progress bar.
-     */
-    public void show() {
-        if (mEnableProgressBar) {
-            mIsShowing = true;
-            mHandler.postDelayed(runnable, mInitialDelay);
-        }
-    }
-
-    /**
-     * Hides the progress bar.
-     */
-    public void hide() {
-        mIsShowing = false;
-        if (mUserProvidedProgressBar) {
-            mProgressBarView.setVisibility(View.INVISIBLE);
-        } else if (mProgressBarView != null) {
-            rootView.removeView(mProgressBarView);
-            mProgressBarView = null;
-        }
-
-        mHandler.removeCallbacks(runnable);
-    }
-
-    /**
-     * Sets a custom view to be shown in place of the default {@link ProgressBar}. This
-     * view must have a parent. Once set, we maintain the visibility property of this view.
-     *
-     * @param progressBarView custom view that will be shown to indicate progress.
-     */
-    public void setProgressBarView(View progressBarView) {
-        if (progressBarView.getParent() == null) {
-            throw new IllegalArgumentException("Must have a parent");
-        }
-
-        this.mProgressBarView = progressBarView;
-        this.mProgressBarView.setVisibility(View.INVISIBLE);
-        mUserProvidedProgressBar = true;
-    }
-
-    /**
-     * Returns the initial delay.
-     */
-    public long getInitialDelay() {
-        return mInitialDelay;
-    }
-
-    /**
-     * Sets the initial delay. Progress bar will be shown after this delay has elapsed.
-     *
-     * @param initialDelay millisecond representing the initial delay.
-     */
-    public void setInitialDelay(long initialDelay) {
-        this.mInitialDelay = initialDelay;
-    }
-
-    /**
-     * Disables progress bar.
-     */
-    public void disableProgressBar() {
-        mEnableProgressBar = false;
-    }
-
-    /**
-     * Enables progress bar.
-     */
-    public void enableProgressBar() {
-        mEnableProgressBar = true;
-    }
-}
diff --git a/android/support/v17/leanback/app/RowsFragment.java b/android/support/v17/leanback/app/RowsFragment.java
deleted file mode 100644
index aa346bd..0000000
--- a/android/support/v17/leanback/app/RowsFragment.java
+++ /dev/null
@@ -1,689 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from RowsSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.animation.TimeAnimator;
-import android.animation.TimeAnimator.TimeListener;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
-import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-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.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v17.leanback.widget.ViewHolderTask;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-
-/**
- * An ordered set of rows of leanback widgets.
- * <p>
- * A RowsFragment renders the elements of its
- * {@link android.support.v17.leanback.widget.ObjectAdapter} as a set
- * of rows in a vertical list. The Adapter's {@link PresenterSelector} must maintain subclasses
- * of {@link RowPresenter}.
- * </p>
- * @deprecated use {@link RowsSupportFragment}
- */
-@Deprecated
-public class RowsFragment extends BaseRowFragment implements
-        BrowseFragment.MainFragmentRowsAdapterProvider,
-        BrowseFragment.MainFragmentAdapterProvider {
-
-    private MainFragmentAdapter mMainFragmentAdapter;
-    private MainFragmentRowsAdapter mMainFragmentRowsAdapter;
-
-    @Override
-    public BrowseFragment.MainFragmentAdapter getMainFragmentAdapter() {
-        if (mMainFragmentAdapter == null) {
-            mMainFragmentAdapter = new MainFragmentAdapter(this);
-        }
-        return mMainFragmentAdapter;
-    }
-
-    @Override
-    public BrowseFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter() {
-        if (mMainFragmentRowsAdapter == null) {
-            mMainFragmentRowsAdapter = new MainFragmentRowsAdapter(this);
-        }
-        return mMainFragmentRowsAdapter;
-    }
-
-    /**
-     * Internal helper class that manages row select animation and apply a default
-     * dim to each row.
-     */
-    final class RowViewHolderExtra implements TimeListener {
-        final RowPresenter mRowPresenter;
-        final Presenter.ViewHolder mRowViewHolder;
-
-        final TimeAnimator mSelectAnimator = new TimeAnimator();
-
-        int mSelectAnimatorDurationInUse;
-        Interpolator mSelectAnimatorInterpolatorInUse;
-        float mSelectLevelAnimStart;
-        float mSelectLevelAnimDelta;
-
-        RowViewHolderExtra(ItemBridgeAdapter.ViewHolder ibvh) {
-            mRowPresenter = (RowPresenter) ibvh.getPresenter();
-            mRowViewHolder = ibvh.getViewHolder();
-            mSelectAnimator.setTimeListener(this);
-        }
-
-        @Override
-        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-            if (mSelectAnimator.isRunning()) {
-                updateSelect(totalTime, deltaTime);
-            }
-        }
-
-        void updateSelect(long totalTime, long deltaTime) {
-            float fraction;
-            if (totalTime >= mSelectAnimatorDurationInUse) {
-                fraction = 1;
-                mSelectAnimator.end();
-            } else {
-                fraction = (float) (totalTime / (double) mSelectAnimatorDurationInUse);
-            }
-            if (mSelectAnimatorInterpolatorInUse != null) {
-                fraction = mSelectAnimatorInterpolatorInUse.getInterpolation(fraction);
-            }
-            float level = mSelectLevelAnimStart + fraction * mSelectLevelAnimDelta;
-            mRowPresenter.setSelectLevel(mRowViewHolder, level);
-        }
-
-        void animateSelect(boolean select, boolean immediate) {
-            mSelectAnimator.end();
-            final float end = select ? 1 : 0;
-            if (immediate) {
-                mRowPresenter.setSelectLevel(mRowViewHolder, end);
-            } else if (mRowPresenter.getSelectLevel(mRowViewHolder) != end) {
-                mSelectAnimatorDurationInUse = mSelectAnimatorDuration;
-                mSelectAnimatorInterpolatorInUse = mSelectAnimatorInterpolator;
-                mSelectLevelAnimStart = mRowPresenter.getSelectLevel(mRowViewHolder);
-                mSelectLevelAnimDelta = end - mSelectLevelAnimStart;
-                mSelectAnimator.start();
-            }
-        }
-
-    }
-
-    static final String TAG = "RowsFragment";
-    static final boolean DEBUG = false;
-    static final int ALIGN_TOP_NOT_SET = Integer.MIN_VALUE;
-
-    ItemBridgeAdapter.ViewHolder mSelectedViewHolder;
-    private int mSubPosition;
-    boolean mExpand = true;
-    boolean mViewsCreated;
-    private int mAlignedTop = ALIGN_TOP_NOT_SET;
-    boolean mAfterEntranceTransition = true;
-    boolean mFreezeRows;
-
-    BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
-    BaseOnItemViewClickedListener mOnItemViewClickedListener;
-
-    // Select animation and interpolator are not intended to be
-    // exposed at this moment. They might be synced with vertical scroll
-    // animation later.
-    int mSelectAnimatorDuration;
-    Interpolator mSelectAnimatorInterpolator = new DecelerateInterpolator(2);
-
-    private RecyclerView.RecycledViewPool mRecycledViewPool;
-    private ArrayList<Presenter> mPresenterMapper;
-
-    ItemBridgeAdapter.AdapterListener mExternalAdapterListener;
-
-    @Override
-    protected VerticalGridView findGridViewFromRoot(View view) {
-        return (VerticalGridView) view.findViewById(R.id.container_list);
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
-     * OnItemViewClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general, developer should choose one of the listeners but not both.
-     */
-    public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mViewsCreated) {
-            throw new IllegalStateException(
-                    "Item clicked listener must be set before views are created");
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public BaseOnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    /**
-     * @deprecated use {@link BrowseFragment#enableRowScaling(boolean)} instead.
-     *
-     * @param enable true to enable row scaling
-     */
-    @Deprecated
-    public void enableRowScaling(boolean enable) {
-    }
-
-    /**
-     * Set the visibility of titles/hovercard of browse rows.
-     */
-    public void setExpand(boolean expand) {
-        mExpand = expand;
-        VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            final int count = listView.getChildCount();
-            if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count);
-            for (int i = 0; i < count; i++) {
-                View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh =
-                        (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
-                setRowViewExpanded(vh, mExpand);
-            }
-        }
-    }
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-        VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            final int count = listView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
-                        listView.getChildViewHolder(view);
-                getRowViewHolder(ibvh).setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-            }
-        }
-    }
-
-    /**
-     * Returns an item selection listener.
-     */
-    public BaseOnItemViewSelectedListener getOnItemViewSelectedListener() {
-        return mOnItemViewSelectedListener;
-    }
-
-    @Override
-    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder,
-            int position, int subposition) {
-        if (mSelectedViewHolder != viewHolder || mSubPosition != subposition) {
-            if (DEBUG) Log.v(TAG, "new row selected position " + position + " subposition "
-                    + subposition + " view " + viewHolder.itemView);
-            mSubPosition = subposition;
-            if (mSelectedViewHolder != null) {
-                setRowViewSelected(mSelectedViewHolder, false, false);
-            }
-            mSelectedViewHolder = (ItemBridgeAdapter.ViewHolder) viewHolder;
-            if (mSelectedViewHolder != null) {
-                setRowViewSelected(mSelectedViewHolder, true, false);
-            }
-        }
-        // When RowsFragment is embedded inside a page fragment, we want to show
-        // the title view only when we're on the first row or there is no data.
-        if (mMainFragmentAdapter != null) {
-            mMainFragmentAdapter.getFragmentHost().showTitleView(position <= 0);
-        }
-    }
-
-    /**
-     * Get row ViewHolder at adapter position.  Returns null if the row object is not in adapter or
-     * the row object has not been bound to a row view.
-     *
-     * @param position Position of row in adapter.
-     * @return Row ViewHolder at a given adapter position.
-     */
-    public RowPresenter.ViewHolder getRowViewHolder(int position) {
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView == null) {
-            return null;
-        }
-        return getRowViewHolder((ItemBridgeAdapter.ViewHolder)
-                verticalView.findViewHolderForAdapterPosition(position));
-    }
-
-    @Override
-    int getLayoutResourceId() {
-        return R.layout.lb_rows_fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mSelectAnimatorDuration = getResources().getInteger(
-                R.integer.lb_browse_rows_anim_duration);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        if (DEBUG) Log.v(TAG, "onViewCreated");
-        super.onViewCreated(view, savedInstanceState);
-        // Align the top edge of child with id row_content.
-        // Need set this for directly using RowsFragment.
-        getVerticalGridView().setItemAlignmentViewId(R.id.row_content);
-        getVerticalGridView().setSaveChildrenPolicy(VerticalGridView.SAVE_LIMITED_CHILD);
-
-        setAlignment(mAlignedTop);
-
-        mRecycledViewPool = null;
-        mPresenterMapper = null;
-        if (mMainFragmentAdapter != null) {
-            mMainFragmentAdapter.getFragmentHost().notifyViewCreated(mMainFragmentAdapter);
-        }
-
-    }
-
-    @Override
-    public void onDestroyView() {
-        mViewsCreated = false;
-        super.onDestroyView();
-    }
-
-    void setExternalAdapterListener(ItemBridgeAdapter.AdapterListener listener) {
-        mExternalAdapterListener = listener;
-    }
-
-    static void setRowViewExpanded(ItemBridgeAdapter.ViewHolder vh, boolean expanded) {
-        ((RowPresenter) vh.getPresenter()).setRowViewExpanded(vh.getViewHolder(), expanded);
-    }
-
-    static void setRowViewSelected(ItemBridgeAdapter.ViewHolder vh, boolean selected,
-            boolean immediate) {
-        RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
-        extra.animateSelect(selected, immediate);
-        ((RowPresenter) vh.getPresenter()).setRowViewSelected(vh.getViewHolder(), selected);
-    }
-
-    private final ItemBridgeAdapter.AdapterListener mBridgeAdapterListener =
-            new ItemBridgeAdapter.AdapterListener() {
-        @Override
-        public void onAddPresenter(Presenter presenter, int type) {
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onAddPresenter(presenter, type);
-            }
-        }
-
-        @Override
-        public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
-            VerticalGridView listView = getVerticalGridView();
-            if (listView != null) {
-                // set clip children false for slide animation
-                listView.setClipChildren(false);
-            }
-            setupSharedViewPool(vh);
-            mViewsCreated = true;
-            vh.setExtraObject(new RowViewHolderExtra(vh));
-            // selected state is initialized to false, then driven by grid view onChildSelected
-            // events.  When there is rebind, grid view fires onChildSelected event properly.
-            // So we don't need do anything special later in onBind or onAttachedToWindow.
-            setRowViewSelected(vh, false, true);
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onCreate(vh);
-            }
-            RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
-            RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
-            rowVh.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-            rowVh.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-
-        @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
-            if (DEBUG) Log.v(TAG, "onAttachToWindow");
-            // All views share the same mExpand value.  When we attach a view to grid view,
-            // we should make sure it pick up the latest mExpand value we set early on other
-            // attached views.  For no-structure-change update,  the view is rebound to new data,
-            // but again it should use the unchanged mExpand value,  so we don't need do any
-            // thing in onBind.
-            setRowViewExpanded(vh, mExpand);
-            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);
-            }
-        }
-
-        @Override
-        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
-            if (mSelectedViewHolder == vh) {
-                setRowViewSelected(mSelectedViewHolder, false, true);
-                mSelectedViewHolder = null;
-            }
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onDetachedFromWindow(vh);
-            }
-        }
-
-        @Override
-        public void onBind(ItemBridgeAdapter.ViewHolder vh) {
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onBind(vh);
-            }
-        }
-
-        @Override
-        public void onUnbind(ItemBridgeAdapter.ViewHolder vh) {
-            setRowViewSelected(vh, false, true);
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onUnbind(vh);
-            }
-        }
-    };
-
-    void setupSharedViewPool(ItemBridgeAdapter.ViewHolder bridgeVh) {
-        RowPresenter rowPresenter = (RowPresenter) bridgeVh.getPresenter();
-        RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(bridgeVh.getViewHolder());
-
-        if (rowVh instanceof ListRowPresenter.ViewHolder) {
-            HorizontalGridView view = ((ListRowPresenter.ViewHolder) rowVh).getGridView();
-            // Recycled view pool is shared between all list rows
-            if (mRecycledViewPool == null) {
-                mRecycledViewPool = view.getRecycledViewPool();
-            } else {
-                view.setRecycledViewPool(mRecycledViewPool);
-            }
-
-            ItemBridgeAdapter bridgeAdapter =
-                    ((ListRowPresenter.ViewHolder) rowVh).getBridgeAdapter();
-            if (mPresenterMapper == null) {
-                mPresenterMapper = bridgeAdapter.getPresenterMapper();
-            } else {
-                bridgeAdapter.setPresenterMapper(mPresenterMapper);
-            }
-        }
-    }
-
-    @Override
-    void updateAdapter() {
-        super.updateAdapter();
-        mSelectedViewHolder = null;
-        mViewsCreated = false;
-
-        ItemBridgeAdapter adapter = getBridgeAdapter();
-        if (adapter != null) {
-            adapter.setAdapterListener(mBridgeAdapterListener);
-        }
-    }
-
-    @Override
-    public boolean onTransitionPrepare() {
-        boolean prepared = super.onTransitionPrepare();
-        if (prepared) {
-            freezeRows(true);
-        }
-        return prepared;
-    }
-
-    @Override
-    public void onTransitionEnd() {
-        super.onTransitionEnd();
-        freezeRows(false);
-    }
-
-    private void freezeRows(boolean freeze) {
-        mFreezeRows = freeze;
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView != null) {
-            final int count = verticalView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
-                        verticalView.getChildViewHolder(verticalView.getChildAt(i));
-                RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
-                RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
-                rowPresenter.freeze(vh, freeze);
-            }
-        }
-    }
-
-    /**
-     * For rows that willing to participate entrance transition,  this function
-     * hide views if afterTransition is true,  show views if afterTransition is false.
-     */
-    public void setEntranceTransitionState(boolean afterTransition) {
-        mAfterEntranceTransition = afterTransition;
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView != null) {
-            final int count = verticalView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
-                        verticalView.getChildViewHolder(verticalView.getChildAt(i));
-                RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
-                RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
-                rowPresenter.setEntranceTransitionState(vh, mAfterEntranceTransition);
-            }
-        }
-    }
-
-    /**
-     * Selects a Row and perform an optional task on the Row. For example
-     * <code>setSelectedPosition(10, true, new ListRowPresenterSelectItemViewHolderTask(5))</code>
-     * Scroll to 11th row and selects 6th item on that row.  The method will be ignored if
-     * RowsFragment has not been created (i.e. before {@link #onCreateView(LayoutInflater,
-     * ViewGroup, Bundle)}).
-     *
-     * @param rowPosition Which row to select.
-     * @param smooth True to scroll to the row, false for no animation.
-     * @param rowHolderTask Task to perform on the Row.
-     */
-    public void setSelectedPosition(int rowPosition, boolean smooth,
-            final Presenter.ViewHolderTask rowHolderTask) {
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView == null) {
-            return;
-        }
-        ViewHolderTask task = null;
-        if (rowHolderTask != null) {
-            // This task will execute once the scroll completes. Once the scrolling finishes,
-            // we will get a success callback to update selected row position. Since the
-            // update to selected row position happens in a post, we want to ensure that this
-            // gets called after that.
-            task = new ViewHolderTask() {
-                @Override
-                public void run(final RecyclerView.ViewHolder rvh) {
-                    rvh.itemView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            rowHolderTask.run(
-                                    getRowViewHolder((ItemBridgeAdapter.ViewHolder) rvh));
-                        }
-                    });
-                }
-            };
-        }
-
-        if (smooth) {
-            verticalView.setSelectedPositionSmooth(rowPosition, task);
-        } else {
-            verticalView.setSelectedPosition(rowPosition, task);
-        }
-    }
-
-    static RowPresenter.ViewHolder getRowViewHolder(ItemBridgeAdapter.ViewHolder ibvh) {
-        if (ibvh == null) {
-            return null;
-        }
-        RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
-        return rowPresenter.getRowViewHolder(ibvh.getViewHolder());
-    }
-
-    public boolean isScrolling() {
-        if (getVerticalGridView() == null) {
-            return false;
-        }
-        return getVerticalGridView().getScrollState() != HorizontalGridView.SCROLL_STATE_IDLE;
-    }
-
-    @Override
-    public void setAlignment(int windowAlignOffsetFromTop) {
-        if (windowAlignOffsetFromTop == ALIGN_TOP_NOT_SET) {
-            return;
-        }
-        mAlignedTop = windowAlignOffsetFromTop;
-        final VerticalGridView gridView = getVerticalGridView();
-
-        if (gridView != null) {
-            gridView.setItemAlignmentOffset(0);
-            gridView.setItemAlignmentOffsetPercent(
-                    VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-            gridView.setItemAlignmentOffsetWithPadding(true);
-            gridView.setWindowAlignmentOffset(mAlignedTop);
-            // align to a fixed position from top
-            gridView.setWindowAlignmentOffsetPercent(
-                    VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-            gridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-        }
-    }
-
-    /**
-     * Find row ViewHolder by position in adapter.
-     * @param position Position of row.
-     * @return ViewHolder of Row.
-     */
-    public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
-        if (mVerticalGridView == null) {
-            return null;
-        }
-        return getRowViewHolder((ItemBridgeAdapter.ViewHolder) mVerticalGridView
-                .findViewHolderForAdapterPosition(position));
-    }
-
-    public static class MainFragmentAdapter extends BrowseFragment.MainFragmentAdapter<RowsFragment> {
-
-        public MainFragmentAdapter(RowsFragment fragment) {
-            super(fragment);
-            setScalingEnabled(true);
-        }
-
-        @Override
-        public boolean isScrolling() {
-            return getFragment().isScrolling();
-        }
-
-        @Override
-        public void setExpand(boolean expand) {
-            getFragment().setExpand(expand);
-        }
-
-        @Override
-        public void setEntranceTransitionState(boolean state) {
-            getFragment().setEntranceTransitionState(state);
-        }
-
-        @Override
-        public void setAlignment(int windowAlignOffsetFromTop) {
-            getFragment().setAlignment(windowAlignOffsetFromTop);
-        }
-
-        @Override
-        public boolean onTransitionPrepare() {
-            return getFragment().onTransitionPrepare();
-        }
-
-        @Override
-        public void onTransitionStart() {
-            getFragment().onTransitionStart();
-        }
-
-        @Override
-        public void onTransitionEnd() {
-            getFragment().onTransitionEnd();
-        }
-
-    }
-
-    /**
-     * The adapter that RowsFragment implements
-     * BrowseFragment.MainFragmentRowsAdapter.
-     * @see #getMainFragmentRowsAdapter().
-     * @deprecated use {@link RowsSupportFragment}
-     */
-    @Deprecated
-    public static class MainFragmentRowsAdapter
-            extends BrowseFragment.MainFragmentRowsAdapter<RowsFragment> {
-
-        public MainFragmentRowsAdapter(RowsFragment fragment) {
-            super(fragment);
-        }
-
-        @Override
-        public void setAdapter(ObjectAdapter adapter) {
-            getFragment().setAdapter(adapter);
-        }
-
-        /**
-         * Sets an item clicked listener on the fragment.
-         */
-        @Override
-        public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-            getFragment().setOnItemViewClickedListener(listener);
-        }
-
-        @Override
-        public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-            getFragment().setOnItemViewSelectedListener(listener);
-        }
-
-        @Override
-        public void setSelectedPosition(int rowPosition,
-                                        boolean smooth,
-                                        final Presenter.ViewHolderTask rowHolderTask) {
-            getFragment().setSelectedPosition(rowPosition, smooth, rowHolderTask);
-        }
-
-        @Override
-        public void setSelectedPosition(int rowPosition, boolean smooth) {
-            getFragment().setSelectedPosition(rowPosition, smooth);
-        }
-
-        @Override
-        public int getSelectedPosition() {
-            return getFragment().getSelectedPosition();
-        }
-
-        @Override
-        public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
-            return getFragment().findRowViewHolderByPosition(position);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/RowsSupportFragment.java b/android/support/v17/leanback/app/RowsSupportFragment.java
deleted file mode 100644
index 05e3813..0000000
--- a/android/support/v17/leanback/app/RowsSupportFragment.java
+++ /dev/null
@@ -1,682 +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.v17.leanback.app;
-
-import android.animation.TimeAnimator;
-import android.animation.TimeAnimator.TimeListener;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
-import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
-import android.support.v17.leanback.widget.HorizontalGridView;
-import android.support.v17.leanback.widget.ItemBridgeAdapter;
-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.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v17.leanback.widget.ViewHolderTask;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-
-/**
- * An ordered set of rows of leanback widgets.
- * <p>
- * A RowsSupportFragment renders the elements of its
- * {@link android.support.v17.leanback.widget.ObjectAdapter} as a set
- * of rows in a vertical list. The Adapter's {@link PresenterSelector} must maintain subclasses
- * of {@link RowPresenter}.
- * </p>
- */
-public class RowsSupportFragment extends BaseRowSupportFragment implements
-        BrowseSupportFragment.MainFragmentRowsAdapterProvider,
-        BrowseSupportFragment.MainFragmentAdapterProvider {
-
-    private MainFragmentAdapter mMainFragmentAdapter;
-    private MainFragmentRowsAdapter mMainFragmentRowsAdapter;
-
-    @Override
-    public BrowseSupportFragment.MainFragmentAdapter getMainFragmentAdapter() {
-        if (mMainFragmentAdapter == null) {
-            mMainFragmentAdapter = new MainFragmentAdapter(this);
-        }
-        return mMainFragmentAdapter;
-    }
-
-    @Override
-    public BrowseSupportFragment.MainFragmentRowsAdapter getMainFragmentRowsAdapter() {
-        if (mMainFragmentRowsAdapter == null) {
-            mMainFragmentRowsAdapter = new MainFragmentRowsAdapter(this);
-        }
-        return mMainFragmentRowsAdapter;
-    }
-
-    /**
-     * Internal helper class that manages row select animation and apply a default
-     * dim to each row.
-     */
-    final class RowViewHolderExtra implements TimeListener {
-        final RowPresenter mRowPresenter;
-        final Presenter.ViewHolder mRowViewHolder;
-
-        final TimeAnimator mSelectAnimator = new TimeAnimator();
-
-        int mSelectAnimatorDurationInUse;
-        Interpolator mSelectAnimatorInterpolatorInUse;
-        float mSelectLevelAnimStart;
-        float mSelectLevelAnimDelta;
-
-        RowViewHolderExtra(ItemBridgeAdapter.ViewHolder ibvh) {
-            mRowPresenter = (RowPresenter) ibvh.getPresenter();
-            mRowViewHolder = ibvh.getViewHolder();
-            mSelectAnimator.setTimeListener(this);
-        }
-
-        @Override
-        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-            if (mSelectAnimator.isRunning()) {
-                updateSelect(totalTime, deltaTime);
-            }
-        }
-
-        void updateSelect(long totalTime, long deltaTime) {
-            float fraction;
-            if (totalTime >= mSelectAnimatorDurationInUse) {
-                fraction = 1;
-                mSelectAnimator.end();
-            } else {
-                fraction = (float) (totalTime / (double) mSelectAnimatorDurationInUse);
-            }
-            if (mSelectAnimatorInterpolatorInUse != null) {
-                fraction = mSelectAnimatorInterpolatorInUse.getInterpolation(fraction);
-            }
-            float level = mSelectLevelAnimStart + fraction * mSelectLevelAnimDelta;
-            mRowPresenter.setSelectLevel(mRowViewHolder, level);
-        }
-
-        void animateSelect(boolean select, boolean immediate) {
-            mSelectAnimator.end();
-            final float end = select ? 1 : 0;
-            if (immediate) {
-                mRowPresenter.setSelectLevel(mRowViewHolder, end);
-            } else if (mRowPresenter.getSelectLevel(mRowViewHolder) != end) {
-                mSelectAnimatorDurationInUse = mSelectAnimatorDuration;
-                mSelectAnimatorInterpolatorInUse = mSelectAnimatorInterpolator;
-                mSelectLevelAnimStart = mRowPresenter.getSelectLevel(mRowViewHolder);
-                mSelectLevelAnimDelta = end - mSelectLevelAnimStart;
-                mSelectAnimator.start();
-            }
-        }
-
-    }
-
-    static final String TAG = "RowsSupportFragment";
-    static final boolean DEBUG = false;
-    static final int ALIGN_TOP_NOT_SET = Integer.MIN_VALUE;
-
-    ItemBridgeAdapter.ViewHolder mSelectedViewHolder;
-    private int mSubPosition;
-    boolean mExpand = true;
-    boolean mViewsCreated;
-    private int mAlignedTop = ALIGN_TOP_NOT_SET;
-    boolean mAfterEntranceTransition = true;
-    boolean mFreezeRows;
-
-    BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
-    BaseOnItemViewClickedListener mOnItemViewClickedListener;
-
-    // Select animation and interpolator are not intended to be
-    // exposed at this moment. They might be synced with vertical scroll
-    // animation later.
-    int mSelectAnimatorDuration;
-    Interpolator mSelectAnimatorInterpolator = new DecelerateInterpolator(2);
-
-    private RecyclerView.RecycledViewPool mRecycledViewPool;
-    private ArrayList<Presenter> mPresenterMapper;
-
-    ItemBridgeAdapter.AdapterListener mExternalAdapterListener;
-
-    @Override
-    protected VerticalGridView findGridViewFromRoot(View view) {
-        return (VerticalGridView) view.findViewById(R.id.container_list);
-    }
-
-    /**
-     * Sets an item clicked listener on the fragment.
-     * OnItemViewClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general, developer should choose one of the listeners but not both.
-     */
-    public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mViewsCreated) {
-            throw new IllegalStateException(
-                    "Item clicked listener must be set before views are created");
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public BaseOnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    /**
-     * @deprecated use {@link BrowseSupportFragment#enableRowScaling(boolean)} instead.
-     *
-     * @param enable true to enable row scaling
-     */
-    @Deprecated
-    public void enableRowScaling(boolean enable) {
-    }
-
-    /**
-     * Set the visibility of titles/hovercard of browse rows.
-     */
-    public void setExpand(boolean expand) {
-        mExpand = expand;
-        VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            final int count = listView.getChildCount();
-            if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count);
-            for (int i = 0; i < count; i++) {
-                View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder vh =
-                        (ItemBridgeAdapter.ViewHolder) listView.getChildViewHolder(view);
-                setRowViewExpanded(vh, mExpand);
-            }
-        }
-    }
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-        VerticalGridView listView = getVerticalGridView();
-        if (listView != null) {
-            final int count = listView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View view = listView.getChildAt(i);
-                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
-                        listView.getChildViewHolder(view);
-                getRowViewHolder(ibvh).setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-            }
-        }
-    }
-
-    /**
-     * Returns an item selection listener.
-     */
-    public BaseOnItemViewSelectedListener getOnItemViewSelectedListener() {
-        return mOnItemViewSelectedListener;
-    }
-
-    @Override
-    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder,
-            int position, int subposition) {
-        if (mSelectedViewHolder != viewHolder || mSubPosition != subposition) {
-            if (DEBUG) Log.v(TAG, "new row selected position " + position + " subposition "
-                    + subposition + " view " + viewHolder.itemView);
-            mSubPosition = subposition;
-            if (mSelectedViewHolder != null) {
-                setRowViewSelected(mSelectedViewHolder, false, false);
-            }
-            mSelectedViewHolder = (ItemBridgeAdapter.ViewHolder) viewHolder;
-            if (mSelectedViewHolder != null) {
-                setRowViewSelected(mSelectedViewHolder, true, false);
-            }
-        }
-        // When RowsSupportFragment is embedded inside a page fragment, we want to show
-        // the title view only when we're on the first row or there is no data.
-        if (mMainFragmentAdapter != null) {
-            mMainFragmentAdapter.getFragmentHost().showTitleView(position <= 0);
-        }
-    }
-
-    /**
-     * Get row ViewHolder at adapter position.  Returns null if the row object is not in adapter or
-     * the row object has not been bound to a row view.
-     *
-     * @param position Position of row in adapter.
-     * @return Row ViewHolder at a given adapter position.
-     */
-    public RowPresenter.ViewHolder getRowViewHolder(int position) {
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView == null) {
-            return null;
-        }
-        return getRowViewHolder((ItemBridgeAdapter.ViewHolder)
-                verticalView.findViewHolderForAdapterPosition(position));
-    }
-
-    @Override
-    int getLayoutResourceId() {
-        return R.layout.lb_rows_fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mSelectAnimatorDuration = getResources().getInteger(
-                R.integer.lb_browse_rows_anim_duration);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        if (DEBUG) Log.v(TAG, "onViewCreated");
-        super.onViewCreated(view, savedInstanceState);
-        // Align the top edge of child with id row_content.
-        // Need set this for directly using RowsSupportFragment.
-        getVerticalGridView().setItemAlignmentViewId(R.id.row_content);
-        getVerticalGridView().setSaveChildrenPolicy(VerticalGridView.SAVE_LIMITED_CHILD);
-
-        setAlignment(mAlignedTop);
-
-        mRecycledViewPool = null;
-        mPresenterMapper = null;
-        if (mMainFragmentAdapter != null) {
-            mMainFragmentAdapter.getFragmentHost().notifyViewCreated(mMainFragmentAdapter);
-        }
-
-    }
-
-    @Override
-    public void onDestroyView() {
-        mViewsCreated = false;
-        super.onDestroyView();
-    }
-
-    void setExternalAdapterListener(ItemBridgeAdapter.AdapterListener listener) {
-        mExternalAdapterListener = listener;
-    }
-
-    static void setRowViewExpanded(ItemBridgeAdapter.ViewHolder vh, boolean expanded) {
-        ((RowPresenter) vh.getPresenter()).setRowViewExpanded(vh.getViewHolder(), expanded);
-    }
-
-    static void setRowViewSelected(ItemBridgeAdapter.ViewHolder vh, boolean selected,
-            boolean immediate) {
-        RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
-        extra.animateSelect(selected, immediate);
-        ((RowPresenter) vh.getPresenter()).setRowViewSelected(vh.getViewHolder(), selected);
-    }
-
-    private final ItemBridgeAdapter.AdapterListener mBridgeAdapterListener =
-            new ItemBridgeAdapter.AdapterListener() {
-        @Override
-        public void onAddPresenter(Presenter presenter, int type) {
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onAddPresenter(presenter, type);
-            }
-        }
-
-        @Override
-        public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
-            VerticalGridView listView = getVerticalGridView();
-            if (listView != null) {
-                // set clip children false for slide animation
-                listView.setClipChildren(false);
-            }
-            setupSharedViewPool(vh);
-            mViewsCreated = true;
-            vh.setExtraObject(new RowViewHolderExtra(vh));
-            // selected state is initialized to false, then driven by grid view onChildSelected
-            // events.  When there is rebind, grid view fires onChildSelected event properly.
-            // So we don't need do anything special later in onBind or onAttachedToWindow.
-            setRowViewSelected(vh, false, true);
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onCreate(vh);
-            }
-            RowPresenter rowPresenter = (RowPresenter) vh.getPresenter();
-            RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(vh.getViewHolder());
-            rowVh.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
-            rowVh.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-
-        @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) {
-            if (DEBUG) Log.v(TAG, "onAttachToWindow");
-            // All views share the same mExpand value.  When we attach a view to grid view,
-            // we should make sure it pick up the latest mExpand value we set early on other
-            // attached views.  For no-structure-change update,  the view is rebound to new data,
-            // but again it should use the unchanged mExpand value,  so we don't need do any
-            // thing in onBind.
-            setRowViewExpanded(vh, mExpand);
-            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);
-            }
-        }
-
-        @Override
-        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) {
-            if (mSelectedViewHolder == vh) {
-                setRowViewSelected(mSelectedViewHolder, false, true);
-                mSelectedViewHolder = null;
-            }
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onDetachedFromWindow(vh);
-            }
-        }
-
-        @Override
-        public void onBind(ItemBridgeAdapter.ViewHolder vh) {
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onBind(vh);
-            }
-        }
-
-        @Override
-        public void onUnbind(ItemBridgeAdapter.ViewHolder vh) {
-            setRowViewSelected(vh, false, true);
-            if (mExternalAdapterListener != null) {
-                mExternalAdapterListener.onUnbind(vh);
-            }
-        }
-    };
-
-    void setupSharedViewPool(ItemBridgeAdapter.ViewHolder bridgeVh) {
-        RowPresenter rowPresenter = (RowPresenter) bridgeVh.getPresenter();
-        RowPresenter.ViewHolder rowVh = rowPresenter.getRowViewHolder(bridgeVh.getViewHolder());
-
-        if (rowVh instanceof ListRowPresenter.ViewHolder) {
-            HorizontalGridView view = ((ListRowPresenter.ViewHolder) rowVh).getGridView();
-            // Recycled view pool is shared between all list rows
-            if (mRecycledViewPool == null) {
-                mRecycledViewPool = view.getRecycledViewPool();
-            } else {
-                view.setRecycledViewPool(mRecycledViewPool);
-            }
-
-            ItemBridgeAdapter bridgeAdapter =
-                    ((ListRowPresenter.ViewHolder) rowVh).getBridgeAdapter();
-            if (mPresenterMapper == null) {
-                mPresenterMapper = bridgeAdapter.getPresenterMapper();
-            } else {
-                bridgeAdapter.setPresenterMapper(mPresenterMapper);
-            }
-        }
-    }
-
-    @Override
-    void updateAdapter() {
-        super.updateAdapter();
-        mSelectedViewHolder = null;
-        mViewsCreated = false;
-
-        ItemBridgeAdapter adapter = getBridgeAdapter();
-        if (adapter != null) {
-            adapter.setAdapterListener(mBridgeAdapterListener);
-        }
-    }
-
-    @Override
-    public boolean onTransitionPrepare() {
-        boolean prepared = super.onTransitionPrepare();
-        if (prepared) {
-            freezeRows(true);
-        }
-        return prepared;
-    }
-
-    @Override
-    public void onTransitionEnd() {
-        super.onTransitionEnd();
-        freezeRows(false);
-    }
-
-    private void freezeRows(boolean freeze) {
-        mFreezeRows = freeze;
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView != null) {
-            final int count = verticalView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
-                        verticalView.getChildViewHolder(verticalView.getChildAt(i));
-                RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
-                RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
-                rowPresenter.freeze(vh, freeze);
-            }
-        }
-    }
-
-    /**
-     * For rows that willing to participate entrance transition,  this function
-     * hide views if afterTransition is true,  show views if afterTransition is false.
-     */
-    public void setEntranceTransitionState(boolean afterTransition) {
-        mAfterEntranceTransition = afterTransition;
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView != null) {
-            final int count = verticalView.getChildCount();
-            for (int i = 0; i < count; i++) {
-                ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder)
-                        verticalView.getChildViewHolder(verticalView.getChildAt(i));
-                RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
-                RowPresenter.ViewHolder vh = rowPresenter.getRowViewHolder(ibvh.getViewHolder());
-                rowPresenter.setEntranceTransitionState(vh, mAfterEntranceTransition);
-            }
-        }
-    }
-
-    /**
-     * Selects a Row and perform an optional task on the Row. For example
-     * <code>setSelectedPosition(10, true, new ListRowPresenterSelectItemViewHolderTask(5))</code>
-     * Scroll to 11th row and selects 6th item on that row.  The method will be ignored if
-     * RowsSupportFragment has not been created (i.e. before {@link #onCreateView(LayoutInflater,
-     * ViewGroup, Bundle)}).
-     *
-     * @param rowPosition Which row to select.
-     * @param smooth True to scroll to the row, false for no animation.
-     * @param rowHolderTask Task to perform on the Row.
-     */
-    public void setSelectedPosition(int rowPosition, boolean smooth,
-            final Presenter.ViewHolderTask rowHolderTask) {
-        VerticalGridView verticalView = getVerticalGridView();
-        if (verticalView == null) {
-            return;
-        }
-        ViewHolderTask task = null;
-        if (rowHolderTask != null) {
-            // This task will execute once the scroll completes. Once the scrolling finishes,
-            // we will get a success callback to update selected row position. Since the
-            // update to selected row position happens in a post, we want to ensure that this
-            // gets called after that.
-            task = new ViewHolderTask() {
-                @Override
-                public void run(final RecyclerView.ViewHolder rvh) {
-                    rvh.itemView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            rowHolderTask.run(
-                                    getRowViewHolder((ItemBridgeAdapter.ViewHolder) rvh));
-                        }
-                    });
-                }
-            };
-        }
-
-        if (smooth) {
-            verticalView.setSelectedPositionSmooth(rowPosition, task);
-        } else {
-            verticalView.setSelectedPosition(rowPosition, task);
-        }
-    }
-
-    static RowPresenter.ViewHolder getRowViewHolder(ItemBridgeAdapter.ViewHolder ibvh) {
-        if (ibvh == null) {
-            return null;
-        }
-        RowPresenter rowPresenter = (RowPresenter) ibvh.getPresenter();
-        return rowPresenter.getRowViewHolder(ibvh.getViewHolder());
-    }
-
-    public boolean isScrolling() {
-        if (getVerticalGridView() == null) {
-            return false;
-        }
-        return getVerticalGridView().getScrollState() != HorizontalGridView.SCROLL_STATE_IDLE;
-    }
-
-    @Override
-    public void setAlignment(int windowAlignOffsetFromTop) {
-        if (windowAlignOffsetFromTop == ALIGN_TOP_NOT_SET) {
-            return;
-        }
-        mAlignedTop = windowAlignOffsetFromTop;
-        final VerticalGridView gridView = getVerticalGridView();
-
-        if (gridView != null) {
-            gridView.setItemAlignmentOffset(0);
-            gridView.setItemAlignmentOffsetPercent(
-                    VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-            gridView.setItemAlignmentOffsetWithPadding(true);
-            gridView.setWindowAlignmentOffset(mAlignedTop);
-            // align to a fixed position from top
-            gridView.setWindowAlignmentOffsetPercent(
-                    VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-            gridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-        }
-    }
-
-    /**
-     * Find row ViewHolder by position in adapter.
-     * @param position Position of row.
-     * @return ViewHolder of Row.
-     */
-    public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
-        if (mVerticalGridView == null) {
-            return null;
-        }
-        return getRowViewHolder((ItemBridgeAdapter.ViewHolder) mVerticalGridView
-                .findViewHolderForAdapterPosition(position));
-    }
-
-    public static class MainFragmentAdapter extends BrowseSupportFragment.MainFragmentAdapter<RowsSupportFragment> {
-
-        public MainFragmentAdapter(RowsSupportFragment fragment) {
-            super(fragment);
-            setScalingEnabled(true);
-        }
-
-        @Override
-        public boolean isScrolling() {
-            return getFragment().isScrolling();
-        }
-
-        @Override
-        public void setExpand(boolean expand) {
-            getFragment().setExpand(expand);
-        }
-
-        @Override
-        public void setEntranceTransitionState(boolean state) {
-            getFragment().setEntranceTransitionState(state);
-        }
-
-        @Override
-        public void setAlignment(int windowAlignOffsetFromTop) {
-            getFragment().setAlignment(windowAlignOffsetFromTop);
-        }
-
-        @Override
-        public boolean onTransitionPrepare() {
-            return getFragment().onTransitionPrepare();
-        }
-
-        @Override
-        public void onTransitionStart() {
-            getFragment().onTransitionStart();
-        }
-
-        @Override
-        public void onTransitionEnd() {
-            getFragment().onTransitionEnd();
-        }
-
-    }
-
-    /**
-     * The adapter that RowsSupportFragment implements
-     * BrowseSupportFragment.MainFragmentRowsAdapter.
-     * @see #getMainFragmentRowsAdapter().
-     */
-    public static class MainFragmentRowsAdapter
-            extends BrowseSupportFragment.MainFragmentRowsAdapter<RowsSupportFragment> {
-
-        public MainFragmentRowsAdapter(RowsSupportFragment fragment) {
-            super(fragment);
-        }
-
-        @Override
-        public void setAdapter(ObjectAdapter adapter) {
-            getFragment().setAdapter(adapter);
-        }
-
-        /**
-         * Sets an item clicked listener on the fragment.
-         */
-        @Override
-        public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-            getFragment().setOnItemViewClickedListener(listener);
-        }
-
-        @Override
-        public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-            getFragment().setOnItemViewSelectedListener(listener);
-        }
-
-        @Override
-        public void setSelectedPosition(int rowPosition,
-                                        boolean smooth,
-                                        final Presenter.ViewHolderTask rowHolderTask) {
-            getFragment().setSelectedPosition(rowPosition, smooth, rowHolderTask);
-        }
-
-        @Override
-        public void setSelectedPosition(int rowPosition, boolean smooth) {
-            getFragment().setSelectedPosition(rowPosition, smooth);
-        }
-
-        @Override
-        public int getSelectedPosition() {
-            return getFragment().getSelectedPosition();
-        }
-
-        @Override
-        public RowPresenter.ViewHolder findRowViewHolderByPosition(int position) {
-            return getFragment().findRowViewHolderByPosition(position);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/SearchFragment.java b/android/support/v17/leanback/app/SearchFragment.java
deleted file mode 100644
index 00f2cca..0000000
--- a/android/support/v17/leanback/app/SearchFragment.java
+++ /dev/null
@@ -1,774 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from SearchSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.Manifest;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.speech.RecognizerIntent;
-import android.speech.SpeechRecognizer;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.ObjectAdapter.DataObserver;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.Presenter.ViewHolder;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.SearchBar;
-import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v17.leanback.widget.SpeechRecognitionCallback;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.app.Fragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.CompletionInfo;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A fragment to handle searches. An application will supply an implementation
- * of the {@link SearchResultProvider} interface to handle the search and return
- * an {@link ObjectAdapter} containing the results. The results are rendered
- * into a {@link RowsFragment}, in the same way that they are in a {@link
- * BrowseFragment}.
- *
- * <p>A SpeechRecognizer object will be created for which your application will need to declare
- * android.permission.RECORD_AUDIO in AndroidManifest file. If app's target version is >= 23 and
- * the device version is >= 23, a permission dialog will show first time using speech recognition.
- * 0 will be used as requestCode in requestPermissions() call.
- * {@link #setSpeechRecognitionCallback(SpeechRecognitionCallback)} is deprecated.
- * </p>
- * <p>
- * Speech recognition is automatically started when fragment is created, but
- * not when fragment is restored from an instance state.  Activity may manually
- * call {@link #startRecognition()}, typically in onNewIntent().
- * </p>
- * @deprecated use {@link SearchSupportFragment}
- */
-@Deprecated
-public class SearchFragment extends Fragment {
-    static final String TAG = SearchFragment.class.getSimpleName();
-    static final boolean DEBUG = false;
-
-    private static final String EXTRA_LEANBACK_BADGE_PRESENT = "LEANBACK_BADGE_PRESENT";
-    private static final String ARG_PREFIX = SearchFragment.class.getCanonicalName();
-    private static final String ARG_QUERY =  ARG_PREFIX + ".query";
-    private static final String ARG_TITLE = ARG_PREFIX  + ".title";
-
-    static final long SPEECH_RECOGNITION_DELAY_MS = 300;
-
-    static final int RESULTS_CHANGED = 0x1;
-    static final int QUERY_COMPLETE = 0x2;
-
-    static final int AUDIO_PERMISSION_REQUEST_CODE = 0;
-
-    /**
-     * Search API to be provided by the application.
-     */
-    public static interface SearchResultProvider {
-        /**
-         * <p>Method invoked some time prior to the first call to onQueryTextChange to retrieve
-         * an ObjectAdapter that will contain the results to future updates of the search query.</p>
-         *
-         * <p>As results are retrieved, the application should use the data set notification methods
-         * on the ObjectAdapter to instruct the SearchFragment to update the results.</p>
-         *
-         * @return ObjectAdapter The result object adapter.
-         */
-        public ObjectAdapter getResultsAdapter();
-
-        /**
-         * <p>Method invoked when the search query is updated.</p>
-         *
-         * <p>This is called as soon as the query changes; it is up to the application to add a
-         * delay before actually executing the queries if needed.
-         *
-         * <p>This method might not always be called before onQueryTextSubmit gets called, in
-         * particular for voice input.
-         *
-         * @param newQuery The current search query.
-         * @return whether the results changed as a result of the new query.
-         */
-        public boolean onQueryTextChange(String newQuery);
-
-        /**
-         * Method invoked when the search query is submitted, either by dismissing the keyboard,
-         * pressing search or next on the keyboard or when voice has detected the end of the query.
-         *
-         * @param query The query entered.
-         * @return whether the results changed as a result of the query.
-         */
-        public boolean onQueryTextSubmit(String query);
-    }
-
-    final DataObserver mAdapterObserver = new DataObserver() {
-        @Override
-        public void onChanged() {
-            // onChanged() may be called multiple times e.g. the provider add
-            // rows to ArrayObjectAdapter one by one.
-            mHandler.removeCallbacks(mResultsChangedCallback);
-            mHandler.post(mResultsChangedCallback);
-        }
-    };
-
-    final Handler mHandler = new Handler();
-
-    final Runnable mResultsChangedCallback = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG) Log.v(TAG, "results changed, new size " + mResultAdapter.size());
-            if (mRowsFragment != null
-                    && mRowsFragment.getAdapter() != mResultAdapter) {
-                if (!(mRowsFragment.getAdapter() == null && mResultAdapter.size() == 0)) {
-                    mRowsFragment.setAdapter(mResultAdapter);
-                    mRowsFragment.setSelectedPosition(0);
-                }
-            }
-            updateSearchBarVisibility();
-            mStatus |= RESULTS_CHANGED;
-            if ((mStatus & QUERY_COMPLETE) != 0) {
-                updateFocus();
-            }
-            updateSearchBarNextFocusId();
-        }
-    };
-
-    /**
-     * Runs when a new provider is set AND when the fragment view is created.
-     */
-    private final Runnable mSetSearchResultProvider = new Runnable() {
-        @Override
-        public void run() {
-            if (mRowsFragment == null) {
-                // We'll retry once we have a rows fragment
-                return;
-            }
-            // Retrieve the result adapter
-            ObjectAdapter adapter = mProvider.getResultsAdapter();
-            if (DEBUG) Log.v(TAG, "Got results adapter " + adapter);
-            if (adapter != mResultAdapter) {
-                boolean firstTime = mResultAdapter == null;
-                releaseAdapter();
-                mResultAdapter = adapter;
-                if (mResultAdapter != null) {
-                    mResultAdapter.registerObserver(mAdapterObserver);
-                }
-                if (DEBUG) {
-                    Log.v(TAG, "mResultAdapter " + mResultAdapter + " size "
-                            + (mResultAdapter == null ? 0 : mResultAdapter.size()));
-                }
-                // delay the first time to avoid setting a empty result adapter
-                // until we got first onChange() from the provider
-                if (!(firstTime && (mResultAdapter == null || mResultAdapter.size() == 0))) {
-                    mRowsFragment.setAdapter(mResultAdapter);
-                }
-                executePendingQuery();
-            }
-            updateSearchBarNextFocusId();
-
-            if (DEBUG) {
-                Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition
-                        + " mResultAdapter " + mResultAdapter
-                        + " adapter " + mRowsFragment.getAdapter());
-            }
-            if (mAutoStartRecognition) {
-                mHandler.removeCallbacks(mStartRecognitionRunnable);
-                mHandler.postDelayed(mStartRecognitionRunnable, SPEECH_RECOGNITION_DELAY_MS);
-            } else {
-                updateFocus();
-            }
-        }
-    };
-
-    final Runnable mStartRecognitionRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mAutoStartRecognition = false;
-            mSearchBar.startRecognition();
-        }
-    };
-
-    RowsFragment mRowsFragment;
-    SearchBar mSearchBar;
-    SearchResultProvider mProvider;
-    String mPendingQuery = null;
-
-    OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    ObjectAdapter mResultAdapter;
-    private SpeechRecognitionCallback mSpeechRecognitionCallback;
-
-    private String mTitle;
-    private Drawable mBadgeDrawable;
-    private ExternalQuery mExternalQuery;
-
-    private SpeechRecognizer mSpeechRecognizer;
-
-    int mStatus;
-    boolean mAutoStartRecognition = true;
-
-    private boolean mIsPaused;
-    private boolean mPendingStartRecognitionWhenPaused;
-    private SearchBar.SearchBarPermissionListener mPermissionListener =
-            new SearchBar.SearchBarPermissionListener() {
-        @Override
-        public void requestAudioPermission() {
-            PermissionHelper.requestPermissions(SearchFragment.this,
-                    new String[]{Manifest.permission.RECORD_AUDIO}, AUDIO_PERMISSION_REQUEST_CODE);
-        }
-    };
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, String[] permissions,
-                                           int[] grantResults) {
-        if (requestCode == AUDIO_PERMISSION_REQUEST_CODE && permissions.length > 0) {
-            if (permissions[0].equals(Manifest.permission.RECORD_AUDIO)
-                    && grantResults[0] == PERMISSION_GRANTED) {
-                startRecognition();
-            }
-        }
-    }
-
-    /**
-     * @param args Bundle to use for the arguments, if null a new Bundle will be created.
-     */
-    public static Bundle createArgs(Bundle args, String query) {
-        return createArgs(args, query, null);
-    }
-
-    public static Bundle createArgs(Bundle args, String query, String title)  {
-        if (args == null) {
-            args = new Bundle();
-        }
-        args.putString(ARG_QUERY, query);
-        args.putString(ARG_TITLE, title);
-        return args;
-    }
-
-    /**
-     * Creates a search fragment with a given search query.
-     *
-     * <p>You should only use this if you need to start the search fragment with a
-     * pre-filled query.
-     *
-     * @param query The search query to begin with.
-     * @return A new SearchFragment.
-     */
-    public static SearchFragment newInstance(String query) {
-        SearchFragment fragment = new SearchFragment();
-        Bundle args = createArgs(null, query);
-        fragment.setArguments(args);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        if (mAutoStartRecognition) {
-            mAutoStartRecognition = savedInstanceState == null;
-        }
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-        View root = inflater.inflate(R.layout.lb_search_fragment, container, false);
-
-        FrameLayout searchFrame = (FrameLayout) root.findViewById(R.id.lb_search_frame);
-        mSearchBar = (SearchBar) searchFrame.findViewById(R.id.lb_search_bar);
-        mSearchBar.setSearchBarListener(new SearchBar.SearchBarListener() {
-            @Override
-            public void onSearchQueryChange(String query) {
-                if (DEBUG) Log.v(TAG, String.format("onSearchQueryChange %s %s", query,
-                        null == mProvider ? "(null)" : mProvider));
-                if (null != mProvider) {
-                    retrieveResults(query);
-                } else {
-                    mPendingQuery = query;
-                }
-            }
-
-            @Override
-            public void onSearchQuerySubmit(String query) {
-                if (DEBUG) Log.v(TAG, String.format("onSearchQuerySubmit %s", query));
-                submitQuery(query);
-            }
-
-            @Override
-            public void onKeyboardDismiss(String query) {
-                if (DEBUG) Log.v(TAG, String.format("onKeyboardDismiss %s", query));
-                queryComplete();
-            }
-        });
-        mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback);
-        mSearchBar.setPermissionListener(mPermissionListener);
-        applyExternalQuery();
-
-        readArguments(getArguments());
-        if (null != mBadgeDrawable) {
-            setBadgeDrawable(mBadgeDrawable);
-        }
-        if (null != mTitle) {
-            setTitle(mTitle);
-        }
-
-        // Inject the RowsFragment in the results container
-        if (getChildFragmentManager().findFragmentById(R.id.lb_results_frame) == null) {
-            mRowsFragment = new RowsFragment();
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.lb_results_frame, mRowsFragment).commit();
-        } else {
-            mRowsFragment = (RowsFragment) getChildFragmentManager()
-                    .findFragmentById(R.id.lb_results_frame);
-        }
-        mRowsFragment.setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
-            @Override
-            public void onItemSelected(ViewHolder itemViewHolder, Object item,
-                                       RowPresenter.ViewHolder rowViewHolder, Row row) {
-                if (DEBUG) {
-                    int position = mRowsFragment.getSelectedPosition();
-                    Log.v(TAG, String.format("onItemSelected %d", position));
-                }
-                updateSearchBarVisibility();
-                if (null != mOnItemViewSelectedListener) {
-                    mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                            rowViewHolder, row);
-                }
-            }
-        });
-        mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        mRowsFragment.setExpand(true);
-        if (null != mProvider) {
-            onSetSearchResultProvider();
-        }
-        return root;
-    }
-
-    private void resultsAvailable() {
-        if ((mStatus & QUERY_COMPLETE) != 0) {
-            focusOnResults();
-        }
-        updateSearchBarNextFocusId();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        VerticalGridView list = mRowsFragment.getVerticalGridView();
-        int mContainerListAlignTop =
-                getResources().getDimensionPixelSize(R.dimen.lb_search_browse_rows_align_top);
-        list.setItemAlignmentOffset(0);
-        list.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-        list.setWindowAlignmentOffset(mContainerListAlignTop);
-        list.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-        list.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-        // VerticalGridView should not be focusable (see b/26894680 for details).
-        list.setFocusable(false);
-        list.setFocusableInTouchMode(false);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mIsPaused = false;
-        if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer) {
-            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(
-                    FragmentUtil.getContext(SearchFragment.this));
-            mSearchBar.setSpeechRecognizer(mSpeechRecognizer);
-        }
-        if (mPendingStartRecognitionWhenPaused) {
-            mPendingStartRecognitionWhenPaused = false;
-            mSearchBar.startRecognition();
-        } else {
-            // Ensure search bar state consistency when using external recognizer
-            mSearchBar.stopRecognition();
-        }
-    }
-
-    @Override
-    public void onPause() {
-        releaseRecognizer();
-        mIsPaused = true;
-        super.onPause();
-    }
-
-    @Override
-    public void onDestroy() {
-        releaseAdapter();
-        super.onDestroy();
-    }
-
-    /**
-     * Returns RowsFragment that shows result rows. RowsFragment is initialized after
-     * SearchFragment.onCreateView().
-     *
-     * @return RowsFragment that shows result rows.
-     */
-    public RowsFragment getRowsFragment() {
-        return mRowsFragment;
-    }
-
-    private void releaseRecognizer() {
-        if (null != mSpeechRecognizer) {
-            mSearchBar.setSpeechRecognizer(null);
-            mSpeechRecognizer.destroy();
-            mSpeechRecognizer = null;
-        }
-    }
-
-    /**
-     * Starts speech recognition.  Typical use case is that
-     * activity receives onNewIntent() call when user clicks a MIC button.
-     * Note that SearchFragment automatically starts speech recognition
-     * at first time created, there is no need to call startRecognition()
-     * when fragment is created.
-     */
-    public void startRecognition() {
-        if (mIsPaused) {
-            mPendingStartRecognitionWhenPaused = true;
-        } else {
-            mSearchBar.startRecognition();
-        }
-    }
-
-    /**
-     * Sets the search provider that is responsible for returning results for the
-     * search query.
-     */
-    public void setSearchResultProvider(SearchResultProvider searchResultProvider) {
-        if (mProvider != searchResultProvider) {
-            mProvider = searchResultProvider;
-            onSetSearchResultProvider();
-        }
-    }
-
-    /**
-     * Sets an item selection listener for the results.
-     *
-     * @param listener The item selection listener to be invoked when an item in
-     *        the search results is selected.
-     */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item clicked listener for the results.
-     *
-     * @param listener The item clicked listener to be invoked when an item in
-     *        the search results is clicked.
-     */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        if (listener != mOnItemViewClickedListener) {
-            mOnItemViewClickedListener = listener;
-            if (mRowsFragment != null) {
-                mRowsFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-            }
-        }
-    }
-
-    /**
-     * Sets the title string to be be shown in an empty search bar. The title
-     * may be placed in a call-to-action, such as "Search <i>title</i>" or
-     * "Speak to search <i>title</i>".
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        if (null != mSearchBar) {
-            mSearchBar.setTitle(title);
-        }
-    }
-
-    /**
-     * Returns the title set in the search bar.
-     */
-    public String getTitle() {
-        if (null != mSearchBar) {
-            return mSearchBar.getTitle();
-        }
-        return null;
-    }
-
-    /**
-     * Sets the badge drawable that will be shown inside the search bar next to
-     * the title.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        mBadgeDrawable = drawable;
-        if (null != mSearchBar) {
-            mSearchBar.setBadgeDrawable(drawable);
-        }
-    }
-
-    /**
-     * Returns the badge drawable in the search bar.
-     */
-    public Drawable getBadgeDrawable() {
-        if (null != mSearchBar) {
-            return mSearchBar.getBadgeDrawable();
-        }
-        return null;
-    }
-
-    /**
-     * Sets background color of not-listening state search orb.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        if (mSearchBar != null) {
-            mSearchBar.setSearchAffordanceColors(colors);
-        }
-    }
-
-    /**
-     * Sets background color of listening state search orb.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
-        if (mSearchBar != null) {
-            mSearchBar.setSearchAffordanceColorsInListening(colors);
-        }
-    }
-
-    /**
-     * Displays the completions shown by the IME. An application may provide
-     * a list of query completions that the system will show in the IME.
-     *
-     * @param completions A list of completions to show in the IME. Setting to
-     *        null or empty will clear the list.
-     */
-    public void displayCompletions(List<String> completions) {
-        mSearchBar.displayCompletions(completions);
-    }
-
-    /**
-     * Displays the completions shown by the IME. An application may provide
-     * a list of query completions that the system will show in the IME.
-     *
-     * @param completions A list of completions to show in the IME. Setting to
-     *        null or empty will clear the list.
-     */
-    public void displayCompletions(CompletionInfo[] completions) {
-        mSearchBar.displayCompletions(completions);
-    }
-
-    /**
-     * Sets this callback to have the fragment pass speech recognition requests
-     * to the activity rather than using a SpeechRecognizer object.
-     * @deprecated Launching voice recognition activity is no longer supported. App should declare
-     *             android.permission.RECORD_AUDIO in AndroidManifest file.
-     */
-    @Deprecated
-    public void setSpeechRecognitionCallback(SpeechRecognitionCallback callback) {
-        mSpeechRecognitionCallback = callback;
-        if (mSearchBar != null) {
-            mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback);
-        }
-        if (callback != null) {
-            releaseRecognizer();
-        }
-    }
-
-    /**
-     * Sets the text of the search query and optionally submits the query. Either
-     * {@link SearchResultProvider#onQueryTextChange onQueryTextChange} or
-     * {@link SearchResultProvider#onQueryTextSubmit onQueryTextSubmit} will be
-     * called on the provider if it is set.
-     *
-     * @param query The search query to set.
-     * @param submit Whether to submit the query.
-     */
-    public void setSearchQuery(String query, boolean submit) {
-        if (DEBUG) Log.v(TAG, "setSearchQuery " + query + " submit " + submit);
-        if (query == null) {
-            return;
-        }
-        mExternalQuery = new ExternalQuery(query, submit);
-        applyExternalQuery();
-        if (mAutoStartRecognition) {
-            mAutoStartRecognition = false;
-            mHandler.removeCallbacks(mStartRecognitionRunnable);
-        }
-    }
-
-    /**
-     * Sets the text of the search query based on the {@link RecognizerIntent#EXTRA_RESULTS} in
-     * the given intent, and optionally submit the query.  If more than one result is present
-     * in the results list, the first will be used.
-     *
-     * @param intent Intent received from a speech recognition service.
-     * @param submit Whether to submit the query.
-     */
-    public void setSearchQuery(Intent intent, boolean submit) {
-        ArrayList<String> matches = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
-        if (matches != null && matches.size() > 0) {
-            setSearchQuery(matches.get(0), submit);
-        }
-    }
-
-    /**
-     * Returns an intent that can be used to request speech recognition.
-     * Built from the base {@link RecognizerIntent#ACTION_RECOGNIZE_SPEECH} plus
-     * extras:
-     *
-     * <ul>
-     * <li>{@link RecognizerIntent#EXTRA_LANGUAGE_MODEL} set to
-     * {@link RecognizerIntent#LANGUAGE_MODEL_FREE_FORM}</li>
-     * <li>{@link RecognizerIntent#EXTRA_PARTIAL_RESULTS} set to true</li>
-     * <li>{@link RecognizerIntent#EXTRA_PROMPT} set to the search bar hint text</li>
-     * </ul>
-     *
-     * For handling the intent returned from the service, see
-     * {@link #setSearchQuery(Intent, boolean)}.
-     */
-    public Intent getRecognizerIntent() {
-        Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
-        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
-                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
-        recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
-        if (mSearchBar != null && mSearchBar.getHint() != null) {
-            recognizerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, mSearchBar.getHint());
-        }
-        recognizerIntent.putExtra(EXTRA_LEANBACK_BADGE_PRESENT, mBadgeDrawable != null);
-        return recognizerIntent;
-    }
-
-    void retrieveResults(String searchQuery) {
-        if (DEBUG) Log.v(TAG, "retrieveResults " + searchQuery);
-        if (mProvider.onQueryTextChange(searchQuery)) {
-            mStatus &= ~QUERY_COMPLETE;
-        }
-    }
-
-    void submitQuery(String query) {
-        queryComplete();
-        if (null != mProvider) {
-            mProvider.onQueryTextSubmit(query);
-        }
-    }
-
-    void queryComplete() {
-        if (DEBUG) Log.v(TAG, "queryComplete");
-        mStatus |= QUERY_COMPLETE;
-        focusOnResults();
-    }
-
-    void updateSearchBarVisibility() {
-        int position = mRowsFragment != null ? mRowsFragment.getSelectedPosition() : -1;
-        mSearchBar.setVisibility(position <=0 || mResultAdapter == null
-                || mResultAdapter.size() == 0 ? View.VISIBLE : View.GONE);
-    }
-
-    void updateSearchBarNextFocusId() {
-        if (mSearchBar == null || mResultAdapter == null) {
-            return;
-        }
-        final int viewId = (mResultAdapter.size() == 0 || mRowsFragment == null
-                || mRowsFragment.getVerticalGridView() == null)
-                        ? 0 : mRowsFragment.getVerticalGridView().getId();
-        mSearchBar.setNextFocusDownId(viewId);
-    }
-
-    void updateFocus() {
-        if (mResultAdapter != null && mResultAdapter.size() > 0
-                && mRowsFragment != null && mRowsFragment.getAdapter() == mResultAdapter) {
-            focusOnResults();
-        } else {
-            mSearchBar.requestFocus();
-        }
-    }
-
-    private void focusOnResults() {
-        if (mRowsFragment == null || mRowsFragment.getVerticalGridView() == null
-                || mResultAdapter.size() == 0) {
-            return;
-        }
-        if (mRowsFragment.getVerticalGridView().requestFocus()) {
-            mStatus &= ~RESULTS_CHANGED;
-        }
-    }
-
-    private void onSetSearchResultProvider() {
-        mHandler.removeCallbacks(mSetSearchResultProvider);
-        mHandler.post(mSetSearchResultProvider);
-    }
-
-    void releaseAdapter() {
-        if (mResultAdapter != null) {
-            mResultAdapter.unregisterObserver(mAdapterObserver);
-            mResultAdapter = null;
-        }
-    }
-
-    void executePendingQuery() {
-        if (null != mPendingQuery && null != mResultAdapter) {
-            String query = mPendingQuery;
-            mPendingQuery = null;
-            retrieveResults(query);
-        }
-    }
-
-    private void applyExternalQuery() {
-        if (mExternalQuery == null || mSearchBar == null) {
-            return;
-        }
-        mSearchBar.setSearchQuery(mExternalQuery.mQuery);
-        if (mExternalQuery.mSubmit) {
-            submitQuery(mExternalQuery.mQuery);
-        }
-        mExternalQuery = null;
-    }
-
-    private void readArguments(Bundle args) {
-        if (null == args) {
-            return;
-        }
-        if (args.containsKey(ARG_QUERY)) {
-            setSearchQuery(args.getString(ARG_QUERY));
-        }
-
-        if (args.containsKey(ARG_TITLE)) {
-            setTitle(args.getString(ARG_TITLE));
-        }
-    }
-
-    private void setSearchQuery(String query) {
-        mSearchBar.setSearchQuery(query);
-    }
-
-    static class ExternalQuery {
-        String mQuery;
-        boolean mSubmit;
-
-        ExternalQuery(String query, boolean submit) {
-            mQuery = query;
-            mSubmit = submit;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/SearchSupportFragment.java b/android/support/v17/leanback/app/SearchSupportFragment.java
deleted file mode 100644
index ed2a679..0000000
--- a/android/support/v17/leanback/app/SearchSupportFragment.java
+++ /dev/null
@@ -1,769 +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.v17.leanback.app;
-
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.Manifest;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.speech.RecognizerIntent;
-import android.speech.SpeechRecognizer;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.ObjectAdapter.DataObserver;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.Presenter.ViewHolder;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.SearchBar;
-import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v17.leanback.widget.SpeechRecognitionCallback;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v4.app.Fragment;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.CompletionInfo;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A fragment to handle searches. An application will supply an implementation
- * of the {@link SearchResultProvider} interface to handle the search and return
- * an {@link ObjectAdapter} containing the results. The results are rendered
- * into a {@link RowsSupportFragment}, in the same way that they are in a {@link
- * BrowseSupportFragment}.
- *
- * <p>A SpeechRecognizer object will be created for which your application will need to declare
- * android.permission.RECORD_AUDIO in AndroidManifest file. If app's target version is >= 23 and
- * the device version is >= 23, a permission dialog will show first time using speech recognition.
- * 0 will be used as requestCode in requestPermissions() call.
- * {@link #setSpeechRecognitionCallback(SpeechRecognitionCallback)} is deprecated.
- * </p>
- * <p>
- * Speech recognition is automatically started when fragment is created, but
- * not when fragment is restored from an instance state.  Activity may manually
- * call {@link #startRecognition()}, typically in onNewIntent().
- * </p>
- */
-public class SearchSupportFragment extends Fragment {
-    static final String TAG = SearchSupportFragment.class.getSimpleName();
-    static final boolean DEBUG = false;
-
-    private static final String EXTRA_LEANBACK_BADGE_PRESENT = "LEANBACK_BADGE_PRESENT";
-    private static final String ARG_PREFIX = SearchSupportFragment.class.getCanonicalName();
-    private static final String ARG_QUERY =  ARG_PREFIX + ".query";
-    private static final String ARG_TITLE = ARG_PREFIX  + ".title";
-
-    static final long SPEECH_RECOGNITION_DELAY_MS = 300;
-
-    static final int RESULTS_CHANGED = 0x1;
-    static final int QUERY_COMPLETE = 0x2;
-
-    static final int AUDIO_PERMISSION_REQUEST_CODE = 0;
-
-    /**
-     * Search API to be provided by the application.
-     */
-    public static interface SearchResultProvider {
-        /**
-         * <p>Method invoked some time prior to the first call to onQueryTextChange to retrieve
-         * an ObjectAdapter that will contain the results to future updates of the search query.</p>
-         *
-         * <p>As results are retrieved, the application should use the data set notification methods
-         * on the ObjectAdapter to instruct the SearchSupportFragment to update the results.</p>
-         *
-         * @return ObjectAdapter The result object adapter.
-         */
-        public ObjectAdapter getResultsAdapter();
-
-        /**
-         * <p>Method invoked when the search query is updated.</p>
-         *
-         * <p>This is called as soon as the query changes; it is up to the application to add a
-         * delay before actually executing the queries if needed.
-         *
-         * <p>This method might not always be called before onQueryTextSubmit gets called, in
-         * particular for voice input.
-         *
-         * @param newQuery The current search query.
-         * @return whether the results changed as a result of the new query.
-         */
-        public boolean onQueryTextChange(String newQuery);
-
-        /**
-         * Method invoked when the search query is submitted, either by dismissing the keyboard,
-         * pressing search or next on the keyboard or when voice has detected the end of the query.
-         *
-         * @param query The query entered.
-         * @return whether the results changed as a result of the query.
-         */
-        public boolean onQueryTextSubmit(String query);
-    }
-
-    final DataObserver mAdapterObserver = new DataObserver() {
-        @Override
-        public void onChanged() {
-            // onChanged() may be called multiple times e.g. the provider add
-            // rows to ArrayObjectAdapter one by one.
-            mHandler.removeCallbacks(mResultsChangedCallback);
-            mHandler.post(mResultsChangedCallback);
-        }
-    };
-
-    final Handler mHandler = new Handler();
-
-    final Runnable mResultsChangedCallback = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG) Log.v(TAG, "results changed, new size " + mResultAdapter.size());
-            if (mRowsSupportFragment != null
-                    && mRowsSupportFragment.getAdapter() != mResultAdapter) {
-                if (!(mRowsSupportFragment.getAdapter() == null && mResultAdapter.size() == 0)) {
-                    mRowsSupportFragment.setAdapter(mResultAdapter);
-                    mRowsSupportFragment.setSelectedPosition(0);
-                }
-            }
-            updateSearchBarVisibility();
-            mStatus |= RESULTS_CHANGED;
-            if ((mStatus & QUERY_COMPLETE) != 0) {
-                updateFocus();
-            }
-            updateSearchBarNextFocusId();
-        }
-    };
-
-    /**
-     * Runs when a new provider is set AND when the fragment view is created.
-     */
-    private final Runnable mSetSearchResultProvider = new Runnable() {
-        @Override
-        public void run() {
-            if (mRowsSupportFragment == null) {
-                // We'll retry once we have a rows fragment
-                return;
-            }
-            // Retrieve the result adapter
-            ObjectAdapter adapter = mProvider.getResultsAdapter();
-            if (DEBUG) Log.v(TAG, "Got results adapter " + adapter);
-            if (adapter != mResultAdapter) {
-                boolean firstTime = mResultAdapter == null;
-                releaseAdapter();
-                mResultAdapter = adapter;
-                if (mResultAdapter != null) {
-                    mResultAdapter.registerObserver(mAdapterObserver);
-                }
-                if (DEBUG) {
-                    Log.v(TAG, "mResultAdapter " + mResultAdapter + " size "
-                            + (mResultAdapter == null ? 0 : mResultAdapter.size()));
-                }
-                // delay the first time to avoid setting a empty result adapter
-                // until we got first onChange() from the provider
-                if (!(firstTime && (mResultAdapter == null || mResultAdapter.size() == 0))) {
-                    mRowsSupportFragment.setAdapter(mResultAdapter);
-                }
-                executePendingQuery();
-            }
-            updateSearchBarNextFocusId();
-
-            if (DEBUG) {
-                Log.v(TAG, "mAutoStartRecognition " + mAutoStartRecognition
-                        + " mResultAdapter " + mResultAdapter
-                        + " adapter " + mRowsSupportFragment.getAdapter());
-            }
-            if (mAutoStartRecognition) {
-                mHandler.removeCallbacks(mStartRecognitionRunnable);
-                mHandler.postDelayed(mStartRecognitionRunnable, SPEECH_RECOGNITION_DELAY_MS);
-            } else {
-                updateFocus();
-            }
-        }
-    };
-
-    final Runnable mStartRecognitionRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mAutoStartRecognition = false;
-            mSearchBar.startRecognition();
-        }
-    };
-
-    RowsSupportFragment mRowsSupportFragment;
-    SearchBar mSearchBar;
-    SearchResultProvider mProvider;
-    String mPendingQuery = null;
-
-    OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    ObjectAdapter mResultAdapter;
-    private SpeechRecognitionCallback mSpeechRecognitionCallback;
-
-    private String mTitle;
-    private Drawable mBadgeDrawable;
-    private ExternalQuery mExternalQuery;
-
-    private SpeechRecognizer mSpeechRecognizer;
-
-    int mStatus;
-    boolean mAutoStartRecognition = true;
-
-    private boolean mIsPaused;
-    private boolean mPendingStartRecognitionWhenPaused;
-    private SearchBar.SearchBarPermissionListener mPermissionListener =
-            new SearchBar.SearchBarPermissionListener() {
-        @Override
-        public void requestAudioPermission() {
-            PermissionHelper.requestPermissions(SearchSupportFragment.this,
-                    new String[]{Manifest.permission.RECORD_AUDIO}, AUDIO_PERMISSION_REQUEST_CODE);
-        }
-    };
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, String[] permissions,
-                                           int[] grantResults) {
-        if (requestCode == AUDIO_PERMISSION_REQUEST_CODE && permissions.length > 0) {
-            if (permissions[0].equals(Manifest.permission.RECORD_AUDIO)
-                    && grantResults[0] == PERMISSION_GRANTED) {
-                startRecognition();
-            }
-        }
-    }
-
-    /**
-     * @param args Bundle to use for the arguments, if null a new Bundle will be created.
-     */
-    public static Bundle createArgs(Bundle args, String query) {
-        return createArgs(args, query, null);
-    }
-
-    public static Bundle createArgs(Bundle args, String query, String title)  {
-        if (args == null) {
-            args = new Bundle();
-        }
-        args.putString(ARG_QUERY, query);
-        args.putString(ARG_TITLE, title);
-        return args;
-    }
-
-    /**
-     * Creates a search fragment with a given search query.
-     *
-     * <p>You should only use this if you need to start the search fragment with a
-     * pre-filled query.
-     *
-     * @param query The search query to begin with.
-     * @return A new SearchSupportFragment.
-     */
-    public static SearchSupportFragment newInstance(String query) {
-        SearchSupportFragment fragment = new SearchSupportFragment();
-        Bundle args = createArgs(null, query);
-        fragment.setArguments(args);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        if (mAutoStartRecognition) {
-            mAutoStartRecognition = savedInstanceState == null;
-        }
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-        View root = inflater.inflate(R.layout.lb_search_fragment, container, false);
-
-        FrameLayout searchFrame = (FrameLayout) root.findViewById(R.id.lb_search_frame);
-        mSearchBar = (SearchBar) searchFrame.findViewById(R.id.lb_search_bar);
-        mSearchBar.setSearchBarListener(new SearchBar.SearchBarListener() {
-            @Override
-            public void onSearchQueryChange(String query) {
-                if (DEBUG) Log.v(TAG, String.format("onSearchQueryChange %s %s", query,
-                        null == mProvider ? "(null)" : mProvider));
-                if (null != mProvider) {
-                    retrieveResults(query);
-                } else {
-                    mPendingQuery = query;
-                }
-            }
-
-            @Override
-            public void onSearchQuerySubmit(String query) {
-                if (DEBUG) Log.v(TAG, String.format("onSearchQuerySubmit %s", query));
-                submitQuery(query);
-            }
-
-            @Override
-            public void onKeyboardDismiss(String query) {
-                if (DEBUG) Log.v(TAG, String.format("onKeyboardDismiss %s", query));
-                queryComplete();
-            }
-        });
-        mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback);
-        mSearchBar.setPermissionListener(mPermissionListener);
-        applyExternalQuery();
-
-        readArguments(getArguments());
-        if (null != mBadgeDrawable) {
-            setBadgeDrawable(mBadgeDrawable);
-        }
-        if (null != mTitle) {
-            setTitle(mTitle);
-        }
-
-        // Inject the RowsSupportFragment in the results container
-        if (getChildFragmentManager().findFragmentById(R.id.lb_results_frame) == null) {
-            mRowsSupportFragment = new RowsSupportFragment();
-            getChildFragmentManager().beginTransaction()
-                    .replace(R.id.lb_results_frame, mRowsSupportFragment).commit();
-        } else {
-            mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager()
-                    .findFragmentById(R.id.lb_results_frame);
-        }
-        mRowsSupportFragment.setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
-            @Override
-            public void onItemSelected(ViewHolder itemViewHolder, Object item,
-                                       RowPresenter.ViewHolder rowViewHolder, Row row) {
-                if (DEBUG) {
-                    int position = mRowsSupportFragment.getSelectedPosition();
-                    Log.v(TAG, String.format("onItemSelected %d", position));
-                }
-                updateSearchBarVisibility();
-                if (null != mOnItemViewSelectedListener) {
-                    mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                            rowViewHolder, row);
-                }
-            }
-        });
-        mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        mRowsSupportFragment.setExpand(true);
-        if (null != mProvider) {
-            onSetSearchResultProvider();
-        }
-        return root;
-    }
-
-    private void resultsAvailable() {
-        if ((mStatus & QUERY_COMPLETE) != 0) {
-            focusOnResults();
-        }
-        updateSearchBarNextFocusId();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        VerticalGridView list = mRowsSupportFragment.getVerticalGridView();
-        int mContainerListAlignTop =
-                getResources().getDimensionPixelSize(R.dimen.lb_search_browse_rows_align_top);
-        list.setItemAlignmentOffset(0);
-        list.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-        list.setWindowAlignmentOffset(mContainerListAlignTop);
-        list.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-        list.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-        // VerticalGridView should not be focusable (see b/26894680 for details).
-        list.setFocusable(false);
-        list.setFocusableInTouchMode(false);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mIsPaused = false;
-        if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer) {
-            mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(
-                    getContext());
-            mSearchBar.setSpeechRecognizer(mSpeechRecognizer);
-        }
-        if (mPendingStartRecognitionWhenPaused) {
-            mPendingStartRecognitionWhenPaused = false;
-            mSearchBar.startRecognition();
-        } else {
-            // Ensure search bar state consistency when using external recognizer
-            mSearchBar.stopRecognition();
-        }
-    }
-
-    @Override
-    public void onPause() {
-        releaseRecognizer();
-        mIsPaused = true;
-        super.onPause();
-    }
-
-    @Override
-    public void onDestroy() {
-        releaseAdapter();
-        super.onDestroy();
-    }
-
-    /**
-     * Returns RowsSupportFragment that shows result rows. RowsSupportFragment is initialized after
-     * SearchSupportFragment.onCreateView().
-     *
-     * @return RowsSupportFragment that shows result rows.
-     */
-    public RowsSupportFragment getRowsSupportFragment() {
-        return mRowsSupportFragment;
-    }
-
-    private void releaseRecognizer() {
-        if (null != mSpeechRecognizer) {
-            mSearchBar.setSpeechRecognizer(null);
-            mSpeechRecognizer.destroy();
-            mSpeechRecognizer = null;
-        }
-    }
-
-    /**
-     * Starts speech recognition.  Typical use case is that
-     * activity receives onNewIntent() call when user clicks a MIC button.
-     * Note that SearchSupportFragment automatically starts speech recognition
-     * at first time created, there is no need to call startRecognition()
-     * when fragment is created.
-     */
-    public void startRecognition() {
-        if (mIsPaused) {
-            mPendingStartRecognitionWhenPaused = true;
-        } else {
-            mSearchBar.startRecognition();
-        }
-    }
-
-    /**
-     * Sets the search provider that is responsible for returning results for the
-     * search query.
-     */
-    public void setSearchResultProvider(SearchResultProvider searchResultProvider) {
-        if (mProvider != searchResultProvider) {
-            mProvider = searchResultProvider;
-            onSetSearchResultProvider();
-        }
-    }
-
-    /**
-     * Sets an item selection listener for the results.
-     *
-     * @param listener The item selection listener to be invoked when an item in
-     *        the search results is selected.
-     */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Sets an item clicked listener for the results.
-     *
-     * @param listener The item clicked listener to be invoked when an item in
-     *        the search results is clicked.
-     */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        if (listener != mOnItemViewClickedListener) {
-            mOnItemViewClickedListener = listener;
-            if (mRowsSupportFragment != null) {
-                mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
-            }
-        }
-    }
-
-    /**
-     * Sets the title string to be be shown in an empty search bar. The title
-     * may be placed in a call-to-action, such as "Search <i>title</i>" or
-     * "Speak to search <i>title</i>".
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        if (null != mSearchBar) {
-            mSearchBar.setTitle(title);
-        }
-    }
-
-    /**
-     * Returns the title set in the search bar.
-     */
-    public String getTitle() {
-        if (null != mSearchBar) {
-            return mSearchBar.getTitle();
-        }
-        return null;
-    }
-
-    /**
-     * Sets the badge drawable that will be shown inside the search bar next to
-     * the title.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        mBadgeDrawable = drawable;
-        if (null != mSearchBar) {
-            mSearchBar.setBadgeDrawable(drawable);
-        }
-    }
-
-    /**
-     * Returns the badge drawable in the search bar.
-     */
-    public Drawable getBadgeDrawable() {
-        if (null != mSearchBar) {
-            return mSearchBar.getBadgeDrawable();
-        }
-        return null;
-    }
-
-    /**
-     * Sets background color of not-listening state search orb.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        if (mSearchBar != null) {
-            mSearchBar.setSearchAffordanceColors(colors);
-        }
-    }
-
-    /**
-     * Sets background color of listening state search orb.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
-        if (mSearchBar != null) {
-            mSearchBar.setSearchAffordanceColorsInListening(colors);
-        }
-    }
-
-    /**
-     * Displays the completions shown by the IME. An application may provide
-     * a list of query completions that the system will show in the IME.
-     *
-     * @param completions A list of completions to show in the IME. Setting to
-     *        null or empty will clear the list.
-     */
-    public void displayCompletions(List<String> completions) {
-        mSearchBar.displayCompletions(completions);
-    }
-
-    /**
-     * Displays the completions shown by the IME. An application may provide
-     * a list of query completions that the system will show in the IME.
-     *
-     * @param completions A list of completions to show in the IME. Setting to
-     *        null or empty will clear the list.
-     */
-    public void displayCompletions(CompletionInfo[] completions) {
-        mSearchBar.displayCompletions(completions);
-    }
-
-    /**
-     * Sets this callback to have the fragment pass speech recognition requests
-     * to the activity rather than using a SpeechRecognizer object.
-     * @deprecated Launching voice recognition activity is no longer supported. App should declare
-     *             android.permission.RECORD_AUDIO in AndroidManifest file.
-     */
-    @Deprecated
-    public void setSpeechRecognitionCallback(SpeechRecognitionCallback callback) {
-        mSpeechRecognitionCallback = callback;
-        if (mSearchBar != null) {
-            mSearchBar.setSpeechRecognitionCallback(mSpeechRecognitionCallback);
-        }
-        if (callback != null) {
-            releaseRecognizer();
-        }
-    }
-
-    /**
-     * Sets the text of the search query and optionally submits the query. Either
-     * {@link SearchResultProvider#onQueryTextChange onQueryTextChange} or
-     * {@link SearchResultProvider#onQueryTextSubmit onQueryTextSubmit} will be
-     * called on the provider if it is set.
-     *
-     * @param query The search query to set.
-     * @param submit Whether to submit the query.
-     */
-    public void setSearchQuery(String query, boolean submit) {
-        if (DEBUG) Log.v(TAG, "setSearchQuery " + query + " submit " + submit);
-        if (query == null) {
-            return;
-        }
-        mExternalQuery = new ExternalQuery(query, submit);
-        applyExternalQuery();
-        if (mAutoStartRecognition) {
-            mAutoStartRecognition = false;
-            mHandler.removeCallbacks(mStartRecognitionRunnable);
-        }
-    }
-
-    /**
-     * Sets the text of the search query based on the {@link RecognizerIntent#EXTRA_RESULTS} in
-     * the given intent, and optionally submit the query.  If more than one result is present
-     * in the results list, the first will be used.
-     *
-     * @param intent Intent received from a speech recognition service.
-     * @param submit Whether to submit the query.
-     */
-    public void setSearchQuery(Intent intent, boolean submit) {
-        ArrayList<String> matches = intent.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
-        if (matches != null && matches.size() > 0) {
-            setSearchQuery(matches.get(0), submit);
-        }
-    }
-
-    /**
-     * Returns an intent that can be used to request speech recognition.
-     * Built from the base {@link RecognizerIntent#ACTION_RECOGNIZE_SPEECH} plus
-     * extras:
-     *
-     * <ul>
-     * <li>{@link RecognizerIntent#EXTRA_LANGUAGE_MODEL} set to
-     * {@link RecognizerIntent#LANGUAGE_MODEL_FREE_FORM}</li>
-     * <li>{@link RecognizerIntent#EXTRA_PARTIAL_RESULTS} set to true</li>
-     * <li>{@link RecognizerIntent#EXTRA_PROMPT} set to the search bar hint text</li>
-     * </ul>
-     *
-     * For handling the intent returned from the service, see
-     * {@link #setSearchQuery(Intent, boolean)}.
-     */
-    public Intent getRecognizerIntent() {
-        Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
-        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
-                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
-        recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
-        if (mSearchBar != null && mSearchBar.getHint() != null) {
-            recognizerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, mSearchBar.getHint());
-        }
-        recognizerIntent.putExtra(EXTRA_LEANBACK_BADGE_PRESENT, mBadgeDrawable != null);
-        return recognizerIntent;
-    }
-
-    void retrieveResults(String searchQuery) {
-        if (DEBUG) Log.v(TAG, "retrieveResults " + searchQuery);
-        if (mProvider.onQueryTextChange(searchQuery)) {
-            mStatus &= ~QUERY_COMPLETE;
-        }
-    }
-
-    void submitQuery(String query) {
-        queryComplete();
-        if (null != mProvider) {
-            mProvider.onQueryTextSubmit(query);
-        }
-    }
-
-    void queryComplete() {
-        if (DEBUG) Log.v(TAG, "queryComplete");
-        mStatus |= QUERY_COMPLETE;
-        focusOnResults();
-    }
-
-    void updateSearchBarVisibility() {
-        int position = mRowsSupportFragment != null ? mRowsSupportFragment.getSelectedPosition() : -1;
-        mSearchBar.setVisibility(position <=0 || mResultAdapter == null
-                || mResultAdapter.size() == 0 ? View.VISIBLE : View.GONE);
-    }
-
-    void updateSearchBarNextFocusId() {
-        if (mSearchBar == null || mResultAdapter == null) {
-            return;
-        }
-        final int viewId = (mResultAdapter.size() == 0 || mRowsSupportFragment == null
-                || mRowsSupportFragment.getVerticalGridView() == null)
-                        ? 0 : mRowsSupportFragment.getVerticalGridView().getId();
-        mSearchBar.setNextFocusDownId(viewId);
-    }
-
-    void updateFocus() {
-        if (mResultAdapter != null && mResultAdapter.size() > 0
-                && mRowsSupportFragment != null && mRowsSupportFragment.getAdapter() == mResultAdapter) {
-            focusOnResults();
-        } else {
-            mSearchBar.requestFocus();
-        }
-    }
-
-    private void focusOnResults() {
-        if (mRowsSupportFragment == null || mRowsSupportFragment.getVerticalGridView() == null
-                || mResultAdapter.size() == 0) {
-            return;
-        }
-        if (mRowsSupportFragment.getVerticalGridView().requestFocus()) {
-            mStatus &= ~RESULTS_CHANGED;
-        }
-    }
-
-    private void onSetSearchResultProvider() {
-        mHandler.removeCallbacks(mSetSearchResultProvider);
-        mHandler.post(mSetSearchResultProvider);
-    }
-
-    void releaseAdapter() {
-        if (mResultAdapter != null) {
-            mResultAdapter.unregisterObserver(mAdapterObserver);
-            mResultAdapter = null;
-        }
-    }
-
-    void executePendingQuery() {
-        if (null != mPendingQuery && null != mResultAdapter) {
-            String query = mPendingQuery;
-            mPendingQuery = null;
-            retrieveResults(query);
-        }
-    }
-
-    private void applyExternalQuery() {
-        if (mExternalQuery == null || mSearchBar == null) {
-            return;
-        }
-        mSearchBar.setSearchQuery(mExternalQuery.mQuery);
-        if (mExternalQuery.mSubmit) {
-            submitQuery(mExternalQuery.mQuery);
-        }
-        mExternalQuery = null;
-    }
-
-    private void readArguments(Bundle args) {
-        if (null == args) {
-            return;
-        }
-        if (args.containsKey(ARG_QUERY)) {
-            setSearchQuery(args.getString(ARG_QUERY));
-        }
-
-        if (args.containsKey(ARG_TITLE)) {
-            setTitle(args.getString(ARG_TITLE));
-        }
-    }
-
-    private void setSearchQuery(String query) {
-        mSearchBar.setSearchQuery(query);
-    }
-
-    static class ExternalQuery {
-        String mQuery;
-        boolean mSubmit;
-
-        ExternalQuery(String query, boolean submit) {
-            mQuery = query;
-            mSubmit = submit;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/app/VerticalGridFragment.java b/android/support/v17/leanback/app/VerticalGridFragment.java
deleted file mode 100644
index bff3dba..0000000
--- a/android/support/v17/leanback/app/VerticalGridFragment.java
+++ /dev/null
@@ -1,260 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from VerticalGridSupportFragment.java.  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.
- */
-package android.support.v17.leanback.app;
-
-import android.os.Bundle;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.support.v17.leanback.widget.BrowseFrameLayout;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnChildLaidOutListener;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-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.VerticalGridPresenter;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A fragment for creating leanback vertical grids.
- *
- * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
- * an {@link ObjectAdapter}.
- * @deprecated use {@link VerticalGridSupportFragment}
- */
-@Deprecated
-public class VerticalGridFragment extends BaseFragment {
-    static final String TAG = "VerticalGF";
-    static boolean DEBUG = false;
-
-    private ObjectAdapter mAdapter;
-    private VerticalGridPresenter mGridPresenter;
-    VerticalGridPresenter.ViewHolder mGridViewHolder;
-    OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    private Object mSceneAfterEntranceTransition;
-    private int mSelectedPosition = -1;
-
-    /**
-     * State to setEntranceTransitionState(false)
-     */
-    final State STATE_SET_ENTRANCE_START_STATE = new State("SET_ENTRANCE_START_STATE") {
-        @Override
-        public void run() {
-            setEntranceTransitionState(false);
-        }
-    };
-
-    @Override
-    void createStateMachineStates() {
-        super.createStateMachineStates();
-        mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
-    }
-
-    @Override
-    void createStateMachineTransitions() {
-        super.createStateMachineTransitions();
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_SET_ENTRANCE_START_STATE, EVT_ON_CREATEVIEW);
-    }
-
-    /**
-     * Sets the grid presenter.
-     */
-    public void setGridPresenter(VerticalGridPresenter gridPresenter) {
-        if (gridPresenter == null) {
-            throw new IllegalArgumentException("Grid presenter may not be null");
-        }
-        mGridPresenter = gridPresenter;
-        mGridPresenter.setOnItemViewSelectedListener(mViewSelectedListener);
-        if (mOnItemViewClickedListener != null) {
-            mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-    }
-
-    /**
-     * Returns the grid presenter.
-     */
-    public VerticalGridPresenter getGridPresenter() {
-        return mGridPresenter;
-    }
-
-    /**
-     * Sets the object adapter for the fragment.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        updateAdapter();
-    }
-
-    /**
-     * Returns the object adapter.
-     */
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    final private OnItemViewSelectedListener mViewSelectedListener =
-            new OnItemViewSelectedListener() {
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                RowPresenter.ViewHolder rowViewHolder, Row row) {
-            int position = mGridViewHolder.getGridView().getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "grid selected position " + position);
-            gridOnItemSelected(position);
-            if (mOnItemViewSelectedListener != null) {
-                mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                        rowViewHolder, row);
-            }
-        }
-    };
-
-    final private OnChildLaidOutListener mChildLaidOutListener =
-            new OnChildLaidOutListener() {
-        @Override
-        public void onChildLaidOut(ViewGroup parent, View view, int position, long id) {
-            if (position == 0) {
-                showOrHideTitle();
-            }
-        }
-    };
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-    }
-
-    void gridOnItemSelected(int position) {
-        if (position != mSelectedPosition) {
-            mSelectedPosition = position;
-            showOrHideTitle();
-        }
-    }
-
-    void showOrHideTitle() {
-        if (mGridViewHolder.getGridView().findViewHolderForAdapterPosition(mSelectedPosition)
-                == null) {
-            return;
-        }
-        if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(mSelectedPosition)) {
-            showTitle(true);
-        } else {
-            showTitle(false);
-        }
-    }
-
-    /**
-     * Sets an item clicked listener.
-     */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mGridPresenter != null) {
-            mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
-                container, false);
-        ViewGroup gridFrame = (ViewGroup) root.findViewById(R.id.grid_frame);
-        installTitleView(inflater, gridFrame, savedInstanceState);
-        getProgressBarManager().setRootView(root);
-
-        ViewGroup gridDock = (ViewGroup) root.findViewById(R.id.browse_grid_dock);
-        mGridViewHolder = mGridPresenter.onCreateViewHolder(gridDock);
-        gridDock.addView(mGridViewHolder.view);
-        mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
-
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(gridDock, new Runnable() {
-            @Override
-            public void run() {
-                setEntranceTransitionState(true);
-            }
-        });
-
-        updateAdapter();
-        return root;
-    }
-
-    private void setupFocusSearchListener() {
-        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
-                R.id.grid_frame);
-        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        setupFocusSearchListener();
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mGridViewHolder = null;
-    }
-
-    /**
-     * Sets the selected item position.
-     */
-    public void setSelectedPosition(int position) {
-        mSelectedPosition = position;
-        if(mGridViewHolder != null && mGridViewHolder.getGridView().getAdapter() != null) {
-            mGridViewHolder.getGridView().setSelectedPositionSmooth(position);
-        }
-    }
-
-    private void updateAdapter() {
-        if (mGridViewHolder != null) {
-            mGridPresenter.onBindViewHolder(mGridViewHolder, mAdapter);
-            if (mSelectedPosition != -1) {
-                mGridViewHolder.getGridView().setSelectedPosition(mSelectedPosition);
-            }
-        }
-    }
-
-    @Override
-    protected Object createEntranceTransition() {
-        return TransitionHelper.loadTransition(FragmentUtil.getContext(VerticalGridFragment.this),
-                R.transition.lb_vertical_grid_entrance_transition);
-    }
-
-    @Override
-    protected void runEntranceTransition(Object entranceTransition) {
-        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
-    }
-
-    void setEntranceTransitionState(boolean afterTransition) {
-        mGridPresenter.setEntranceTransitionState(mGridViewHolder, afterTransition);
-    }
-}
diff --git a/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/android/support/v17/leanback/app/VerticalGridSupportFragment.java
deleted file mode 100644
index 4cfe981..0000000
--- a/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ /dev/null
@@ -1,255 +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.v17.leanback.app;
-
-import android.os.Bundle;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.util.StateMachine.State;
-import android.support.v17.leanback.widget.BrowseFrameLayout;
-import android.support.v17.leanback.widget.ObjectAdapter;
-import android.support.v17.leanback.widget.OnChildLaidOutListener;
-import android.support.v17.leanback.widget.OnItemViewClickedListener;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-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.VerticalGridPresenter;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A fragment for creating leanback vertical grids.
- *
- * <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
- * an {@link ObjectAdapter}.
- */
-public class VerticalGridSupportFragment extends BaseSupportFragment {
-    static final String TAG = "VerticalGF";
-    static boolean DEBUG = false;
-
-    private ObjectAdapter mAdapter;
-    private VerticalGridPresenter mGridPresenter;
-    VerticalGridPresenter.ViewHolder mGridViewHolder;
-    OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    private Object mSceneAfterEntranceTransition;
-    private int mSelectedPosition = -1;
-
-    /**
-     * State to setEntranceTransitionState(false)
-     */
-    final State STATE_SET_ENTRANCE_START_STATE = new State("SET_ENTRANCE_START_STATE") {
-        @Override
-        public void run() {
-            setEntranceTransitionState(false);
-        }
-    };
-
-    @Override
-    void createStateMachineStates() {
-        super.createStateMachineStates();
-        mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
-    }
-
-    @Override
-    void createStateMachineTransitions() {
-        super.createStateMachineTransitions();
-        mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
-                STATE_SET_ENTRANCE_START_STATE, EVT_ON_CREATEVIEW);
-    }
-
-    /**
-     * Sets the grid presenter.
-     */
-    public void setGridPresenter(VerticalGridPresenter gridPresenter) {
-        if (gridPresenter == null) {
-            throw new IllegalArgumentException("Grid presenter may not be null");
-        }
-        mGridPresenter = gridPresenter;
-        mGridPresenter.setOnItemViewSelectedListener(mViewSelectedListener);
-        if (mOnItemViewClickedListener != null) {
-            mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-    }
-
-    /**
-     * Returns the grid presenter.
-     */
-    public VerticalGridPresenter getGridPresenter() {
-        return mGridPresenter;
-    }
-
-    /**
-     * Sets the object adapter for the fragment.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        mAdapter = adapter;
-        updateAdapter();
-    }
-
-    /**
-     * Returns the object adapter.
-     */
-    public ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    final private OnItemViewSelectedListener mViewSelectedListener =
-            new OnItemViewSelectedListener() {
-        @Override
-        public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                RowPresenter.ViewHolder rowViewHolder, Row row) {
-            int position = mGridViewHolder.getGridView().getSelectedPosition();
-            if (DEBUG) Log.v(TAG, "grid selected position " + position);
-            gridOnItemSelected(position);
-            if (mOnItemViewSelectedListener != null) {
-                mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
-                        rowViewHolder, row);
-            }
-        }
-    };
-
-    final private OnChildLaidOutListener mChildLaidOutListener =
-            new OnChildLaidOutListener() {
-        @Override
-        public void onChildLaidOut(ViewGroup parent, View view, int position, long id) {
-            if (position == 0) {
-                showOrHideTitle();
-            }
-        }
-    };
-
-    /**
-     * Sets an item selection listener.
-     */
-    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-    }
-
-    void gridOnItemSelected(int position) {
-        if (position != mSelectedPosition) {
-            mSelectedPosition = position;
-            showOrHideTitle();
-        }
-    }
-
-    void showOrHideTitle() {
-        if (mGridViewHolder.getGridView().findViewHolderForAdapterPosition(mSelectedPosition)
-                == null) {
-            return;
-        }
-        if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(mSelectedPosition)) {
-            showTitle(true);
-        } else {
-            showTitle(false);
-        }
-    }
-
-    /**
-     * Sets an item clicked listener.
-     */
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-        if (mGridPresenter != null) {
-            mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
-        }
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
-                container, false);
-        ViewGroup gridFrame = (ViewGroup) root.findViewById(R.id.grid_frame);
-        installTitleView(inflater, gridFrame, savedInstanceState);
-        getProgressBarManager().setRootView(root);
-
-        ViewGroup gridDock = (ViewGroup) root.findViewById(R.id.browse_grid_dock);
-        mGridViewHolder = mGridPresenter.onCreateViewHolder(gridDock);
-        gridDock.addView(mGridViewHolder.view);
-        mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
-
-        mSceneAfterEntranceTransition = TransitionHelper.createScene(gridDock, new Runnable() {
-            @Override
-            public void run() {
-                setEntranceTransitionState(true);
-            }
-        });
-
-        updateAdapter();
-        return root;
-    }
-
-    private void setupFocusSearchListener() {
-        BrowseFrameLayout browseFrameLayout = (BrowseFrameLayout) getView().findViewById(
-                R.id.grid_frame);
-        browseFrameLayout.setOnFocusSearchListener(getTitleHelper().getOnFocusSearchListener());
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        setupFocusSearchListener();
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mGridViewHolder = null;
-    }
-
-    /**
-     * Sets the selected item position.
-     */
-    public void setSelectedPosition(int position) {
-        mSelectedPosition = position;
-        if(mGridViewHolder != null && mGridViewHolder.getGridView().getAdapter() != null) {
-            mGridViewHolder.getGridView().setSelectedPositionSmooth(position);
-        }
-    }
-
-    private void updateAdapter() {
-        if (mGridViewHolder != null) {
-            mGridPresenter.onBindViewHolder(mGridViewHolder, mAdapter);
-            if (mSelectedPosition != -1) {
-                mGridViewHolder.getGridView().setSelectedPosition(mSelectedPosition);
-            }
-        }
-    }
-
-    @Override
-    protected Object createEntranceTransition() {
-        return TransitionHelper.loadTransition(getContext(),
-                R.transition.lb_vertical_grid_entrance_transition);
-    }
-
-    @Override
-    protected void runEntranceTransition(Object entranceTransition) {
-        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
-    }
-
-    void setEntranceTransitionState(boolean afterTransition) {
-        mGridPresenter.setEntranceTransitionState(mGridViewHolder, afterTransition);
-    }
-}
diff --git a/android/support/v17/leanback/app/VideoFragment.java b/android/support/v17/leanback/app/VideoFragment.java
deleted file mode 100644
index e4d75f3..0000000
--- a/android/support/v17/leanback/app/VideoFragment.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from VideoSupportFragment.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 android.support.v17.leanback.app;
-
-import android.os.Bundle;
-import android.support.v17.leanback.R;
-import android.view.LayoutInflater;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Subclass of {@link PlaybackFragment} that is responsible for providing a {@link SurfaceView}
- * and rendering video.
- * @deprecated use {@link VideoSupportFragment}
- */
-@Deprecated
-public class VideoFragment extends PlaybackFragment {
-    static final int SURFACE_NOT_CREATED = 0;
-    static final int SURFACE_CREATED = 1;
-
-    SurfaceView mVideoSurface;
-    SurfaceHolder.Callback mMediaPlaybackCallback;
-
-    int mState = SURFACE_NOT_CREATED;
-
-    @Override
-    public View onCreateView(
-            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        ViewGroup root = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
-        mVideoSurface = (SurfaceView) LayoutInflater.from(FragmentUtil.getContext(VideoFragment.this)).inflate(
-                R.layout.lb_video_surface, root, false);
-        root.addView(mVideoSurface, 0);
-        mVideoSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
-
-            @Override
-            public void surfaceCreated(SurfaceHolder holder) {
-                if (mMediaPlaybackCallback != null) {
-                    mMediaPlaybackCallback.surfaceCreated(holder);
-                }
-                mState = SURFACE_CREATED;
-            }
-
-            @Override
-            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-                if (mMediaPlaybackCallback != null) {
-                    mMediaPlaybackCallback.surfaceChanged(holder, format, width, height);
-                }
-            }
-
-            @Override
-            public void surfaceDestroyed(SurfaceHolder holder) {
-                if (mMediaPlaybackCallback != null) {
-                    mMediaPlaybackCallback.surfaceDestroyed(holder);
-                }
-                mState = SURFACE_NOT_CREATED;
-            }
-        });
-        setBackgroundType(PlaybackFragment.BG_LIGHT);
-        return root;
-    }
-
-    /**
-     * Adds {@link SurfaceHolder.Callback} to {@link android.view.SurfaceView}.
-     */
-    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
-        mMediaPlaybackCallback = callback;
-
-        if (callback != null) {
-            if (mState == SURFACE_CREATED) {
-                mMediaPlaybackCallback.surfaceCreated(mVideoSurface.getHolder());
-            }
-        }
-    }
-
-    @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.
-     */
-    public SurfaceView getSurfaceView() {
-        return mVideoSurface;
-    }
-
-    @Override
-    public void onDestroyView() {
-        mVideoSurface = null;
-        mState = SURFACE_NOT_CREATED;
-        super.onDestroyView();
-    }
-}
diff --git a/android/support/v17/leanback/app/VideoFragmentGlueHost.java b/android/support/v17/leanback/app/VideoFragmentGlueHost.java
deleted file mode 100644
index 546e581..0000000
--- a/android/support/v17/leanback/app/VideoFragmentGlueHost.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// CHECKSTYLE:OFF Generated code
-/* This file is auto-generated from VideoSupportFragmentGlueHost.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 android.support.v17.leanback.app;
-
-import android.support.v17.leanback.media.PlaybackGlue;
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.media.SurfaceHolderGlueHost;
-import android.view.SurfaceHolder;
-
-/**
- * {@link PlaybackGlueHost} implementation
- * the interaction between {@link PlaybackGlue} and {@link VideoFragment}.
- * @deprecated use {@link VideoSupportFragmentGlueHost}
- */
-@Deprecated
-public class VideoFragmentGlueHost extends PlaybackFragmentGlueHost
-        implements SurfaceHolderGlueHost {
-    private final VideoFragment mFragment;
-
-    public VideoFragmentGlueHost(VideoFragment fragment) {
-        super(fragment);
-        this.mFragment = fragment;
-    }
-
-    /**
-     * Sets the {@link android.view.SurfaceHolder.Callback} on the host.
-     * {@link PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
-     * have a reference to the component hosting it for rendering the video.
-     */
-    @Override
-    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
-        mFragment.setSurfaceHolderCallback(callback);
-    }
-
-}
diff --git a/android/support/v17/leanback/app/VideoSupportFragment.java b/android/support/v17/leanback/app/VideoSupportFragment.java
deleted file mode 100644
index 51003d3..0000000
--- a/android/support/v17/leanback/app/VideoSupportFragment.java
+++ /dev/null
@@ -1,117 +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.v17.leanback.app;
-
-import android.os.Bundle;
-import android.support.v17.leanback.R;
-import android.view.LayoutInflater;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Subclass of {@link PlaybackSupportFragment} that is responsible for providing a {@link SurfaceView}
- * and rendering video.
- */
-public class VideoSupportFragment extends PlaybackSupportFragment {
-    static final int SURFACE_NOT_CREATED = 0;
-    static final int SURFACE_CREATED = 1;
-
-    SurfaceView mVideoSurface;
-    SurfaceHolder.Callback mMediaPlaybackCallback;
-
-    int mState = SURFACE_NOT_CREATED;
-
-    @Override
-    public View onCreateView(
-            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        ViewGroup root = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
-        mVideoSurface = (SurfaceView) LayoutInflater.from(getContext()).inflate(
-                R.layout.lb_video_surface, root, false);
-        root.addView(mVideoSurface, 0);
-        mVideoSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
-
-            @Override
-            public void surfaceCreated(SurfaceHolder holder) {
-                if (mMediaPlaybackCallback != null) {
-                    mMediaPlaybackCallback.surfaceCreated(holder);
-                }
-                mState = SURFACE_CREATED;
-            }
-
-            @Override
-            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-                if (mMediaPlaybackCallback != null) {
-                    mMediaPlaybackCallback.surfaceChanged(holder, format, width, height);
-                }
-            }
-
-            @Override
-            public void surfaceDestroyed(SurfaceHolder holder) {
-                if (mMediaPlaybackCallback != null) {
-                    mMediaPlaybackCallback.surfaceDestroyed(holder);
-                }
-                mState = SURFACE_NOT_CREATED;
-            }
-        });
-        setBackgroundType(PlaybackSupportFragment.BG_LIGHT);
-        return root;
-    }
-
-    /**
-     * Adds {@link SurfaceHolder.Callback} to {@link android.view.SurfaceView}.
-     */
-    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
-        mMediaPlaybackCallback = callback;
-
-        if (callback != null) {
-            if (mState == SURFACE_CREATED) {
-                mMediaPlaybackCallback.surfaceCreated(mVideoSurface.getHolder());
-            }
-        }
-    }
-
-    @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.
-     */
-    public SurfaceView getSurfaceView() {
-        return mVideoSurface;
-    }
-
-    @Override
-    public void onDestroyView() {
-        mVideoSurface = null;
-        mState = SURFACE_NOT_CREATED;
-        super.onDestroyView();
-    }
-}
diff --git a/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java b/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
deleted file mode 100644
index 66aabc4..0000000
--- a/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
+++ /dev/null
@@ -1,44 +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.v17.leanback.app;
-
-import android.support.v17.leanback.media.PlaybackGlue;
-import android.support.v17.leanback.media.PlaybackGlueHost;
-import android.support.v17.leanback.media.SurfaceHolderGlueHost;
-import android.view.SurfaceHolder;
-
-/**
- * {@link PlaybackGlueHost} implementation
- * the interaction between {@link PlaybackGlue} and {@link VideoSupportFragment}.
- */
-public class VideoSupportFragmentGlueHost extends PlaybackSupportFragmentGlueHost
-        implements SurfaceHolderGlueHost {
-    private final VideoSupportFragment mFragment;
-
-    public VideoSupportFragmentGlueHost(VideoSupportFragment fragment) {
-        super(fragment);
-        this.mFragment = fragment;
-    }
-
-    /**
-     * Sets the {@link android.view.SurfaceHolder.Callback} on the host.
-     * {@link PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
-     * have a reference to the component hosting it for rendering the video.
-     */
-    @Override
-    public void setSurfaceHolderCallback(SurfaceHolder.Callback callback) {
-        mFragment.setSurfaceHolderCallback(callback);
-    }
-
-}
diff --git a/android/support/v17/leanback/app/package-info.java b/android/support/v17/leanback/app/package-info.java
deleted file mode 100644
index b736909..0000000
--- a/android/support/v17/leanback/app/package-info.java
+++ /dev/null
@@ -1,67 +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.
- */
-
-/**
- * <p>Support classes providing high level Leanback user interface building blocks.</p>
- * <p>
- * Leanback fragments are available both as support fragments (subclassed from
- * {@link android.support.v4.app.Fragment android.support.v4.app.Fragment}) and as platform
- * fragments (subclassed from {@link android.app.Fragment android.app.Fragment}). A few of the most
- * commonly used leanback fragments are described here.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.app.BrowseSupportFragment} by default operates in the "row"
- * mode. It includes an optional “fastlane”
- * navigation side panel and a list of rows, with one-to-one correspondance between each header
- * in the fastlane and a row.  The application supplies the
- * {@link android.support.v17.leanback.widget.ObjectAdapter} containing the list of
- * rows and a {@link android.support.v17.leanback.widget.PresenterSelector} of row presenters.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.app.BrowseSupportFragment} also works in a "page" mode when
- * each row of fastlane is mapped to a fragment that the app registers in
- * {@link android.support.v17.leanback.app.BrowseSupportFragment#getMainFragmentRegistry()}.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.app.DetailsSupportFragment} will typically consist of a
- * large overview of an item at the top,
- * some actions that a user can perform, and possibly rows of additional or related items.
- * The content for this fragment is specified in the same way as for the BrowseSupportFragment, with
- * the convention that the first element in the ObjectAdapter corresponds to the overview row.
- * The {@link android.support.v17.leanback.widget.DetailsOverviewRow} and
- * {@link android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter} provide a
- * default template for this row.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.app.PlaybackSupportFragment} or its subclass
- * {@link android.support.v17.leanback.app.VideoSupportFragment} hosts
- * {@link android.support.v17.leanback.media.PlaybackTransportControlGlue}
- * or {@link android.support.v17.leanback.media.PlaybackBannerControlGlue} with a Leanback
- * look and feel.  It is recommended to use an instance of
- * {@link android.support.v17.leanback.media.PlaybackTransportControlGlue}.
- * This helper implements a standard behavior for user interaction with
- * the most commonly used controls as well as video scrubbing.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.app.SearchSupportFragment} allows the developer to accept a
- * query from a user and display the results
- * using the familiar list rows.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.app.GuidedStepSupportFragment} is used to guide the user
- * through a decision or series of decisions.
- * </p>
- **/
-
-package android.support.v17.leanback.app;
diff --git a/android/support/v17/leanback/database/CursorMapper.java b/android/support/v17/leanback/database/CursorMapper.java
deleted file mode 100644
index 20b4a36..0000000
--- a/android/support/v17/leanback/database/CursorMapper.java
+++ /dev/null
@@ -1,50 +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.v17.leanback.database;
-
-import android.database.Cursor;
-
-/**
- * Abstract class used to convert the current {@link Cursor} row to a single
- * object.
- */
-public abstract class CursorMapper {
-
-    private Cursor mCursor;
-
-    /**
-     * Called once when the associated {@link Cursor} is changed. A subclass
-     * should bind column indexes to column names in this method. This method is
-     * not intended to be called outside of CursorMapper.
-     */
-    protected abstract void bindColumns(Cursor cursor);
-
-    /**
-     * A subclass should implement this method to create a single object using
-     * binding information. This method is not intended to be called
-     * outside of CursorMapper.
-     */
-    protected abstract Object bind(Cursor cursor);
-
-    /**
-     * Convert a {@link Cursor} at its current position to an Object.
-     */
-    public Object convert(Cursor cursor) {
-        if (cursor != mCursor) {
-            mCursor = cursor;
-            bindColumns(mCursor);
-        }
-        return bind(mCursor);
-    }
-}
diff --git a/android/support/v17/leanback/graphics/BoundsRule.java b/android/support/v17/leanback/graphics/BoundsRule.java
deleted file mode 100644
index 700f7f0..0000000
--- a/android/support/v17/leanback/graphics/BoundsRule.java
+++ /dev/null
@@ -1,166 +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.v17.leanback.graphics;
-
-import android.graphics.Rect;
-
-/**
- * This class contains the rules for updating the bounds of a
- * {@link CompositeDrawable.ChildDrawable}. It contains four rules, one for each value of the
- * rectangular bound - left/top/right/bottom.
- */
-public class BoundsRule {
-
-    /**
-     * This class represents individual rules for updating the bounds.
-     */
-    public final static class ValueRule {
-        float mFraction;
-        int mAbsoluteValue;
-
-        /**
-         * Creates ValueRule using a fraction of parent size.
-         *
-         * @param fraction Percentage of parent.
-         * @return Newly created ValueRule.
-         */
-        public static ValueRule inheritFromParent(float fraction) {
-            return new ValueRule(0, fraction);
-        }
-
-        /**
-         * Creates ValueRule using an absolute value.
-         *
-         * @param absoluteValue Absolute value.
-         * @return Newly created ValueRule.
-         */
-        public static ValueRule absoluteValue(int absoluteValue) {
-            return new ValueRule(absoluteValue, 0);
-        }
-
-        /**
-         * Creates ValueRule of fraction and offset.
-         *
-         * @param fraction Percentage of parent.
-         * @param value    Offset
-         * @return Newly created ValueRule.
-         */
-        public static ValueRule inheritFromParentWithOffset(float fraction, int value) {
-            return new ValueRule(value, fraction);
-        }
-
-        ValueRule(int absoluteValue, float fraction) {
-            this.mAbsoluteValue = absoluteValue;
-            this.mFraction = fraction;
-        }
-
-        ValueRule(ValueRule rule) {
-            this.mFraction = rule.mFraction;
-            this.mAbsoluteValue = rule.mAbsoluteValue;
-        }
-
-        /**
-         * Sets the fractional value (percentage of parent) for this rule.
-         *
-         * @param fraction Percentage of parent.
-         */
-        public void setFraction(float fraction) {
-            this.mFraction = fraction;
-        }
-
-        /**
-         * @return The current fractional value.
-         */
-        public float getFraction() {
-            return mFraction;
-        }
-
-        /**
-         * Sets the absolute/offset value for rule.
-         *
-         * @param absoluteValue Absolute value.
-         */
-        public void setAbsoluteValue(int absoluteValue) {
-            this.mAbsoluteValue = absoluteValue;
-        }
-
-        /**
-         * @return The current absolute/offset value forrule.
-         */
-        public int getAbsoluteValue() {
-            return mAbsoluteValue;
-        }
-
-    }
-
-    /**
-     * Takes in the current bounds and sets the final values based on the individual rules in the
-     * result object.
-     *
-     * @param rect Represents the current bounds.
-     * @param result Represents the final bounds.
-     */
-    public void calculateBounds(Rect rect, Rect result) {
-        if (left == null) {
-            result.left = rect.left;
-        } else {
-            result.left = doCalculate(rect.left, left, rect.width());
-        }
-
-        if (right == null) {
-            result.right = rect.right;
-        } else {
-            result.right = doCalculate(rect.left, right, rect.width());
-        }
-
-        if (top == null) {
-            result.top = rect.top;
-        } else {
-            result.top = doCalculate(rect.top, top, rect.height());
-        }
-
-        if (bottom == null) {
-            result.bottom = rect.bottom;
-        } else {
-            result.bottom = doCalculate(rect.top, bottom, rect.height());
-        }
-    }
-
-    public BoundsRule() {}
-
-    public BoundsRule(BoundsRule boundsRule) {
-        this.left = boundsRule.left != null ? new ValueRule(boundsRule.left) : null;
-        this.right = boundsRule.right != null ? new ValueRule(boundsRule.right) : null;
-        this.top = boundsRule.top != null ? new ValueRule(boundsRule.top) : null;
-        this.bottom = boundsRule.bottom != null ? new ValueRule(boundsRule.bottom) : null;
-    }
-
-    private int doCalculate(int value, ValueRule rule, int size) {
-        return value + rule.mAbsoluteValue + (int) (rule.mFraction * size);
-    }
-
-    /** {@link ValueRule} for left attribute of {@link BoundsRule} */
-    public ValueRule left;
-
-    /** {@link ValueRule} for top attribute of {@link BoundsRule} */
-    public ValueRule top;
-
-    /** {@link ValueRule} for right attribute of {@link BoundsRule} */
-    public ValueRule right;
-
-    /** {@link ValueRule} for bottom attribute of {@link BoundsRule} */
-    public ValueRule bottom;
-}
diff --git a/android/support/v17/leanback/graphics/ColorFilterCache.java b/android/support/v17/leanback/graphics/ColorFilterCache.java
deleted file mode 100644
index 872d282..0000000
--- a/android/support/v17/leanback/graphics/ColorFilterCache.java
+++ /dev/null
@@ -1,76 +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.v17.leanback.graphics;
-
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.util.SparseArray;
-
-/**
- * Cache of {@link ColorFilter}s for a given color at different alpha levels.
- */
-public final class ColorFilterCache {
-
-    private static final SparseArray<ColorFilterCache> sColorToFiltersMap =
-            new SparseArray<ColorFilterCache>();
-
-    private final PorterDuffColorFilter[] mFilters = new PorterDuffColorFilter[0x100];
-
-    /**
-     * Get a ColorDimmer for a given color.  Only the RGB values are used; the 
-     * alpha channel is ignored in color. Subsequent calls to this method
-     * with the same color value will return the same cache.
-     *
-     * @param color The color to use for the color filters.
-     * @return A cache of ColorFilters at different alpha levels for the color.
-     */
-    public static ColorFilterCache getColorFilterCache(int color) {
-        final int r = Color.red(color);
-        final int g = Color.green(color);
-        final int b = Color.blue(color);
-        color = Color.rgb(r, g, b);
-        ColorFilterCache filters = sColorToFiltersMap.get(color);
-        if (filters == null) {
-            filters = new ColorFilterCache(r, g, b);
-            sColorToFiltersMap.put(color, filters);
-        }
-        return filters;
-    }
-
-    private ColorFilterCache(int r, int g, int b) {
-        // Pre cache all 256 filter levels
-        for (int i = 0x00; i <= 0xFF; i++) {
-            int color = Color.argb(i, r, g, b);
-            mFilters[i] = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
-        }
-    }
-
-    /**
-     * Returns a ColorFilter for a given alpha level between 0 and 1.0.
-     *
-     * @param level The alpha level the filter should apply.
-     * @return A ColorFilter at the alpha level for the color represented by the
-     *         cache.
-     */
-    public ColorFilter getFilterForLevel(float level) {
-        if (level >= 0 && level <= 1.0) {
-            int filterIndex = (int) (0xFF * level);
-            return mFilters[filterIndex];
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/graphics/ColorFilterDimmer.java b/android/support/v17/leanback/graphics/ColorFilterDimmer.java
deleted file mode 100644
index 6c03625..0000000
--- a/android/support/v17/leanback/graphics/ColorFilterDimmer.java
+++ /dev/null
@@ -1,134 +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.v17.leanback.graphics;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.view.View;
-import android.support.v17.leanback.R;
-
-/**
- * Helper class for applying a dim level to a View.  The ColorFilterDimmer
- * uses a ColorFilter in a Paint object to dim the view according to the
- * currently active level.
- */
-public final class ColorFilterDimmer {
-
-    private final ColorFilterCache mColorDimmer;
-
-    private final float mActiveLevel;
-    private final float mDimmedLevel;
-
-    private final Paint mPaint;
-    private ColorFilter mFilter;
-
-    /**
-     * Creates a default ColorFilterDimmer. Uses the default color and level for
-     * the dimmer.
-     *
-     * @param context A Context used to retrieve Resources.
-     * @return A ColorFilterDimmer with the default dim color and levels.
-     */
-    public static ColorFilterDimmer createDefault(Context context) {
-        TypedArray a = context.obtainStyledAttributes(R.styleable.LeanbackTheme);
-
-        int dimColor = a.getColor(R.styleable.LeanbackTheme_overlayDimMaskColor,
-                context.getResources().getColor(R.color.lb_view_dim_mask_color));
-        float activeLevel = a.getFraction(R.styleable.LeanbackTheme_overlayDimActiveLevel, 1, 1,
-                context.getResources().getFraction(R.fraction.lb_view_active_level, 1, 0));
-        float dimmedLevel = a.getFraction(R.styleable.LeanbackTheme_overlayDimDimmedLevel, 1, 1,
-                context.getResources().getFraction(R.fraction.lb_view_dimmed_level, 1, 1));
-        a.recycle();
-        return new ColorFilterDimmer(ColorFilterCache.getColorFilterCache(
-                    dimColor), activeLevel, dimmedLevel);
-    }
-
-    /**
-     * Creates a ColorFilterDimmer for the given color and levels..
-     *
-     * @param dimmer      The ColorFilterCache for dim color.
-     * @param activeLevel The level of dimming when the View is in its active
-     *                    state. Must be a float value between 0.0 and 1.0.
-     * @param dimmedLevel The level of dimming when the View is in its dimmed
-     *                    state. Must be a float value between 0.0 and 1.0.
-     */
-    public static ColorFilterDimmer create(ColorFilterCache dimmer,
-            float activeLevel, float dimmedLevel) {
-        return new ColorFilterDimmer(dimmer, activeLevel, dimmedLevel);
-    }
-
-    private ColorFilterDimmer(ColorFilterCache dimmer, float activeLevel, float dimmedLevel) {
-        mColorDimmer = dimmer;
-        if (activeLevel > 1.0f) activeLevel = 1.0f;
-        if (activeLevel < 0.0f) activeLevel = 0.0f;
-        if (dimmedLevel > 1.0f) dimmedLevel = 1.0f;
-        if (dimmedLevel < 0.0f) dimmedLevel = 0.0f;
-        mActiveLevel = activeLevel;
-        mDimmedLevel = dimmedLevel;
-        mPaint = new Paint();
-    }
-
-    /**
-     * Apply current the ColorFilter to a View. This method will set the
-     * hardware layer of the view when applying a filter, and remove it when not
-     * applying a filter.
-     *
-     * @param view The View to apply the ColorFilter to.
-     */
-    public void applyFilterToView(View view) {
-        if (mFilter != null) {
-            view.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
-        } else {
-            view.setLayerType(View.LAYER_TYPE_NONE, null);
-        }
-        // FIXME: Current framework has bug that not triggering invalidate when change layer
-        // paint.  Will add conditional sdk version check once bug is fixed in released
-        // framework.
-        view.invalidate();
-    }
-
-    /**
-     * Sets the active level of the dimmer. Updates the ColorFilter based on the
-     * level.
-     *
-     * @param level A float between 0 (fully dim) and 1 (fully active).
-     */
-    public void setActiveLevel(float level) {
-        if (level < 0.0f) level = 0.0f;
-        if (level > 1.0f) level = 1.0f;
-        mFilter = mColorDimmer.getFilterForLevel(
-                mDimmedLevel + level * (mActiveLevel - mDimmedLevel));
-        mPaint.setColorFilter(mFilter);
-    }
-
-    /**
-     * Gets the ColorFilter set to the current dim level.
-     *
-     * @return The current ColorFilter.
-     */
-    public ColorFilter getColorFilter() {
-        return mFilter;
-    }
-
-    /**
-     * Gets the Paint object set to the current dim level.
-     *
-     * @return The current Paint object.
-     */
-    public Paint getPaint() {
-        return mPaint;
-    }
-}
diff --git a/android/support/v17/leanback/graphics/ColorOverlayDimmer.java b/android/support/v17/leanback/graphics/ColorOverlayDimmer.java
deleted file mode 100644
index 7a93d24..0000000
--- a/android/support/v17/leanback/graphics/ColorOverlayDimmer.java
+++ /dev/null
@@ -1,165 +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.v17.leanback.graphics;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.support.v17.leanback.R;
-import android.view.View;
-
-/**
- * Helper class for assigning a dim color to Paint. It holds the alpha value for
- * the current active level.
- */
-public final class ColorOverlayDimmer {
-
-    private final float mActiveLevel;
-    private final float mDimmedLevel;
-
-    private final Paint mPaint;
-
-    private int mAlpha;
-    private float mAlphaFloat;
-
-    /**
-     * Creates a default ColorOverlayDimmer.
-     */
-    public static ColorOverlayDimmer createDefault(Context context) {
-        TypedArray a = context.obtainStyledAttributes(R.styleable.LeanbackTheme);
-
-        int dimColor = a.getColor(R.styleable.LeanbackTheme_overlayDimMaskColor,
-                context.getResources().getColor(R.color.lb_view_dim_mask_color));
-        float activeLevel = a.getFraction(R.styleable.LeanbackTheme_overlayDimActiveLevel, 1, 1,
-                context.getResources().getFraction(R.fraction.lb_view_active_level, 1, 0));
-        float dimmedLevel = a.getFraction(R.styleable.LeanbackTheme_overlayDimDimmedLevel, 1, 1,
-                context.getResources().getFraction(R.fraction.lb_view_dimmed_level, 1, 1));
-        a.recycle();
-        return new ColorOverlayDimmer(dimColor, activeLevel, dimmedLevel);
-    }
-
-    /**
-     * Creates a ColorOverlayDimmer for the given color and levels.
-     *
-     * @param dimColor    The color for fully dimmed. Only the RGB values are
-     *                    used; the alpha channel is ignored.
-     * @param activeLevel The level of dimming when the View is in its active
-     *                    state. Must be a float value between 0.0 and 1.0.
-     * @param dimmedLevel The level of dimming when the View is in its dimmed
-     *                    state. Must be a float value between 0.0 and 1.0.
-     */
-    public static ColorOverlayDimmer createColorOverlayDimmer(int dimColor, float activeLevel,
-            float dimmedLevel) {
-        return new ColorOverlayDimmer(dimColor, activeLevel, dimmedLevel);
-    }
-
-    private ColorOverlayDimmer(int dimColor, float activeLevel, float dimmedLevel) {
-        if (activeLevel > 1.0f) activeLevel = 1.0f;
-        if (activeLevel < 0.0f) activeLevel = 0.0f;
-        if (dimmedLevel > 1.0f) dimmedLevel = 1.0f;
-        if (dimmedLevel < 0.0f) dimmedLevel = 0.0f;
-        mPaint = new Paint();
-        dimColor = Color.rgb(Color.red(dimColor), Color.green(dimColor), Color.blue(dimColor));
-        mPaint.setColor(dimColor);
-        mActiveLevel = activeLevel;
-        mDimmedLevel = dimmedLevel;
-        setActiveLevel(1);
-    }
-
-    /**
-     * Sets the active level of the dimmer. Updates the alpha value based on the
-     * level.
-     *
-     * @param level A float between 0 (fully dim) and 1 (fully active).
-     */
-    public void setActiveLevel(float level) {
-        mAlphaFloat = (mDimmedLevel + level * (mActiveLevel - mDimmedLevel));
-        mAlpha = (int) (255 * mAlphaFloat);
-        mPaint.setAlpha(mAlpha);
-    }
-
-    /**
-     * Returns whether the dimmer needs to draw.
-     */
-    public boolean needsDraw() {
-        return mAlpha != 0;
-    }
-
-    /**
-     * Returns the alpha value for the dimmer.
-     */
-    public int getAlpha() {
-        return mAlpha;
-    }
-
-    /**
-     * Returns the float value between 0 and 1 corresponding to alpha between
-     * 0 and 255.
-     */
-    public float getAlphaFloat() {
-        return mAlphaFloat;
-    }
-
-    /**
-     * Returns the Paint object set to the current alpha value.
-     */
-    public Paint getPaint() {
-        return mPaint;
-    }
-
-    /**
-     * Change the RGB of the color according to current dim level. Maintains the
-     * alpha value of the color.
-     *
-     * @param color The color to apply the dim level to.
-     * @return A color with the RGB values adjusted by the alpha of the current
-     *         dim level.
-     */
-    public int applyToColor(int color) {
-        float f = 1 - mAlphaFloat;
-        return Color.argb(Color.alpha(color),
-                (int)(Color.red(color) * f),
-                (int)(Color.green(color) * f),
-                (int)(Color.blue(color) * f));
-    }
-
-    /**
-     * Draw a dim color overlay on top of a child View inside the canvas of
-     * the parent View.
-     *
-     * @param c Canvas of the parent View.
-     * @param v A child of the parent View.
-     * @param includePadding Set to true to draw overlay on padding area of the
-     *        View.
-     */
-    public void drawColorOverlay(Canvas c, View v, boolean includePadding) {
-        c.save();
-        float dx = v.getLeft() + v.getTranslationX();
-        float dy = v.getTop() + v.getTranslationY();
-        c.translate(dx, dy);
-        c.concat(v.getMatrix());
-        c.translate(-dx, -dy);
-        if (includePadding) {
-            c.drawRect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom(), mPaint);
-        } else {
-            c.drawRect(v.getLeft() + v.getPaddingLeft(),
-                    v.getTop() + v.getPaddingTop(),
-                    v.getRight() - v.getPaddingRight(),
-                    v.getBottom() - v.getPaddingBottom(), mPaint);
-        }
-        c.restore();
-    }
-}
diff --git a/android/support/v17/leanback/graphics/CompositeDrawable.java b/android/support/v17/leanback/graphics/CompositeDrawable.java
deleted file mode 100644
index 4b68e69..0000000
--- a/android/support/v17/leanback/graphics/CompositeDrawable.java
+++ /dev/null
@@ -1,534 +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.v17.leanback.graphics;
-
-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.support.annotation.NonNull;
-import android.support.v17.leanback.graphics.BoundsRule.ValueRule;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.util.Property;
-
-import java.util.ArrayList;
-
-/**
- * Generic drawable class that can be composed of multiple children. Whenever the bounds changes
- * for this class, it updates those of its children.
- */
-public class CompositeDrawable extends Drawable implements Drawable.Callback {
-
-    static class CompositeState extends Drawable.ConstantState {
-
-        final ArrayList<ChildDrawable> mChildren;
-
-        CompositeState() {
-            mChildren = new ArrayList<ChildDrawable>();
-        }
-
-        CompositeState(CompositeState other, CompositeDrawable parent, Resources res) {
-            final int n = other.mChildren.size();
-            mChildren = new ArrayList<ChildDrawable>(n);
-            for (int k = 0; k < n; k++) {
-                mChildren.add(new ChildDrawable(other.mChildren.get(k), parent, res));
-            }
-        }
-
-        @NonNull
-        @Override
-        public Drawable newDrawable() {
-            return new CompositeDrawable(this);
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return 0;
-        }
-
-    }
-
-    CompositeState mState;
-    boolean mMutated = false;
-
-    public CompositeDrawable() {
-        mState = new CompositeState();
-    }
-
-    CompositeDrawable(CompositeState state) {
-        mState = state;
-    }
-
-    @Override
-    public ConstantState getConstantState() {
-        return mState;
-    }
-
-    @Override
-    public Drawable mutate() {
-        if (!mMutated && super.mutate() == this) {
-            mState = new CompositeState(mState, this, null);
-            final ArrayList<ChildDrawable> children = mState.mChildren;
-            for (int i = 0, n = children.size(); i < n; i++) {
-                final Drawable dr = children.get(i).mDrawable;
-                if (dr != null) {
-                    dr.mutate();
-                }
-            }
-            mMutated = true;
-        }
-        return this;
-    }
-
-    /**
-     * Adds the supplied region.
-     */
-    public void addChildDrawable(Drawable drawable) {
-        mState.mChildren.add(new ChildDrawable(drawable, this));
-    }
-
-    /**
-     * Sets the supplied region at given index.
-     */
-    public void setChildDrawableAt(int index, Drawable drawable) {
-        mState.mChildren.set(index, new ChildDrawable(drawable, this));
-    }
-
-    /**
-     * Returns the {@link Drawable} for the given index.
-     */
-    public Drawable getDrawable(int index) {
-        return mState.mChildren.get(index).mDrawable;
-    }
-
-    /**
-     * Returns the {@link ChildDrawable} at the given index.
-     */
-    public ChildDrawable getChildAt(int index) {
-        return mState.mChildren.get(index);
-    }
-
-    /**
-     * Removes the child corresponding to the given index.
-     */
-    public void removeChild(int index) {
-        mState.mChildren.remove(index);
-    }
-
-    /**
-     * Removes the given region.
-     */
-    public void removeDrawable(Drawable drawable) {
-        final ArrayList<ChildDrawable> children = mState.mChildren;
-        for (int i = 0; i < children.size(); i++) {
-            if (drawable == children.get(i).mDrawable) {
-                children.get(i).mDrawable.setCallback(null);
-                children.remove(i);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Returns the total number of children.
-     */
-    public int getChildCount() {
-        return mState.mChildren.size();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        final ArrayList<ChildDrawable> children = mState.mChildren;
-        for (int i = 0; i < children.size(); i++) {
-            children.get(i).mDrawable.draw(canvas);
-        }
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
-        updateBounds(bounds);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        final ArrayList<ChildDrawable> children = mState.mChildren;
-        for (int i = 0; i < children.size(); i++) {
-            children.get(i).mDrawable.setColorFilter(colorFilter);
-        }
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.UNKNOWN;
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        final ArrayList<ChildDrawable> children = mState.mChildren;
-        for (int i = 0; i < children.size(); i++) {
-            children.get(i).mDrawable.setAlpha(alpha);
-        }
-    }
-
-    /**
-     * @return Alpha value between 0(inclusive) and 255(inclusive)
-     */
-    @Override
-    public int getAlpha() {
-        final Drawable dr = getFirstNonNullDrawable();
-        if (dr != null) {
-            return DrawableCompat.getAlpha(dr);
-        } else {
-            return 0xFF;
-        }
-    }
-
-    final Drawable getFirstNonNullDrawable() {
-        final ArrayList<ChildDrawable> children = mState.mChildren;
-        for (int i = 0, n = children.size(); i < n; i++) {
-            final Drawable dr = children.get(i).mDrawable;
-            if (dr != null) {
-                return dr;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void invalidateDrawable(Drawable who) {
-        invalidateSelf();
-    }
-
-    @Override
-    public void scheduleDrawable(Drawable who, Runnable what, long when) {
-        scheduleSelf(what, when);
-    }
-
-    @Override
-    public void unscheduleDrawable(Drawable who, Runnable what) {
-        unscheduleSelf(what);
-    }
-
-    /**
-     * Updates the bounds based on the {@link BoundsRule}.
-     */
-    void updateBounds(Rect bounds) {
-        final ArrayList<ChildDrawable> children = mState.mChildren;
-        for (int i = 0; i < children.size(); i++) {
-            ChildDrawable childDrawable = children.get(i);
-            childDrawable.updateBounds(bounds);
-        }
-    }
-
-    /**
-     * Wrapper class holding a drawable object and {@link BoundsRule} to update drawable bounds
-     * when parent bound changes.
-     */
-    public static final class ChildDrawable {
-        private final BoundsRule mBoundsRule;
-        private final Drawable mDrawable;
-        private final Rect adjustedBounds = new Rect();
-        final CompositeDrawable mParent;
-
-        public ChildDrawable(Drawable drawable, CompositeDrawable parent) {
-            this.mDrawable = drawable;
-            this.mParent = parent;
-            this.mBoundsRule = new BoundsRule();
-            drawable.setCallback(parent);
-        }
-
-        ChildDrawable(ChildDrawable orig, CompositeDrawable parent, Resources res) {
-            final Drawable dr = orig.mDrawable;
-            final Drawable clone;
-            if (dr != null) {
-                final ConstantState cs = dr.getConstantState();
-                if (res != null) {
-                    clone = cs.newDrawable(res);
-                } else {
-                    clone = cs.newDrawable();
-                }
-                clone.setCallback(parent);
-                DrawableCompat.setLayoutDirection(clone, DrawableCompat.getLayoutDirection(dr));
-                clone.setBounds(dr.getBounds());
-                clone.setLevel(dr.getLevel());
-            } else {
-                clone = null;
-            }
-            if (orig.mBoundsRule != null) {
-                this.mBoundsRule = new BoundsRule(orig.mBoundsRule);
-            } else {
-                this.mBoundsRule = new BoundsRule();
-            }
-            mDrawable = clone;
-            mParent = parent;
-        }
-
-        /**
-         * Returns the instance of {@link BoundsRule}.
-         */
-        public BoundsRule getBoundsRule() {
-            return this.mBoundsRule;
-        }
-
-        /**
-         * Returns the {@link Drawable}.
-         */
-        public Drawable getDrawable() {
-            return mDrawable;
-        }
-
-        /**
-         * Updates the bounds based on the {@link BoundsRule}.
-         */
-        void updateBounds(Rect bounds) {
-            mBoundsRule.calculateBounds(bounds, adjustedBounds);
-            mDrawable.setBounds(adjustedBounds);
-        }
-
-        /**
-         * After changing the {@link BoundsRule}, user should call this function
-         * for the drawable to recalculate its bounds.
-         */
-        public void recomputeBounds() {
-            updateBounds(mParent.getBounds());
-        }
-
-        /**
-         * Implementation of {@link Property} for overrideTop attribute.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Integer> TOP_ABSOLUTE =
-                new Property<CompositeDrawable.ChildDrawable, Integer>(
-                        Integer.class, "absoluteTop") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
-                if (obj.getBoundsRule().top == null) {
-                    obj.getBoundsRule().top = ValueRule.absoluteValue(value);
-                } else {
-                    obj.getBoundsRule().top.setAbsoluteValue(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Integer get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().top == null) {
-                    return obj.mParent.getBounds().top;
-                }
-                return obj.getBoundsRule().top.getAbsoluteValue();
-            }
-        };
-
-        /**
-         * Implementation of {@link Property} for overrideBottom attribute.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Integer> BOTTOM_ABSOLUTE =
-                new Property<CompositeDrawable.ChildDrawable, Integer>(
-                        Integer.class, "absoluteBottom") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
-                if (obj.getBoundsRule().bottom == null) {
-                    obj.getBoundsRule().bottom = ValueRule.absoluteValue(value);
-                } else {
-                    obj.getBoundsRule().bottom.setAbsoluteValue(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Integer get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().bottom == null) {
-                    return obj.mParent.getBounds().bottom;
-                }
-                return obj.getBoundsRule().bottom.getAbsoluteValue();
-            }
-        };
-
-
-        /**
-         * Implementation of {@link Property} for overrideLeft attribute.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Integer> LEFT_ABSOLUTE =
-                new Property<CompositeDrawable.ChildDrawable, Integer>(
-                        Integer.class, "absoluteLeft") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
-                if (obj.getBoundsRule().left == null) {
-                    obj.getBoundsRule().left = ValueRule.absoluteValue(value);
-                } else {
-                    obj.getBoundsRule().left.setAbsoluteValue(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Integer get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().left == null) {
-                    return obj.mParent.getBounds().left;
-                }
-                return obj.getBoundsRule().left.getAbsoluteValue();
-            }
-        };
-
-        /**
-         * Implementation of {@link Property} for overrideRight attribute.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Integer> RIGHT_ABSOLUTE =
-                new Property<CompositeDrawable.ChildDrawable, Integer>(
-                        Integer.class, "absoluteRight") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
-                if (obj.getBoundsRule().right == null) {
-                    obj.getBoundsRule().right = ValueRule.absoluteValue(value);
-                } else {
-                    obj.getBoundsRule().right.setAbsoluteValue(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Integer get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().right == null) {
-                    return obj.mParent.getBounds().right;
-                }
-                return obj.getBoundsRule().right.getAbsoluteValue();
-            }
-        };
-
-        /**
-         * Implementation of {@link Property} for overwriting the bottom attribute of
-         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
-         * change the bounds rules as a percentage of parent size. This is preferable over
-         * {@see PROPERTY_TOP_ABSOLUTE} when the exact start/end position of scroll movement
-         * isn't available at compile time.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Float> TOP_FRACTION =
-                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractionTop") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
-                if (obj.getBoundsRule().top == null) {
-                    obj.getBoundsRule().top = ValueRule.inheritFromParent(value);
-                } else {
-                    obj.getBoundsRule().top.setFraction(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Float get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().top == null) {
-                    return 0f;
-                }
-                return obj.getBoundsRule().top.getFraction();
-            }
-        };
-
-        /**
-         * Implementation of {@link Property} for overwriting the bottom attribute of
-         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
-         * change the bounds rules as a percentage of parent size. This is preferable over
-         * {@see PROPERTY_BOTTOM_ABSOLUTE} when the exact start/end position of scroll movement
-         * isn't available at compile time.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Float> BOTTOM_FRACTION =
-                new Property<CompositeDrawable.ChildDrawable, Float>(
-                        Float.class, "fractionBottom") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
-                if (obj.getBoundsRule().bottom == null) {
-                    obj.getBoundsRule().bottom = ValueRule.inheritFromParent(value);
-                } else {
-                    obj.getBoundsRule().bottom.setFraction(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Float get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().bottom == null) {
-                    return 1f;
-                }
-                return obj.getBoundsRule().bottom.getFraction();
-            }
-        };
-
-        /**
-         * Implementation of {@link Property} for overwriting the bottom attribute of
-         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
-         * change the bounds rules as a percentage of parent size. This is preferable over
-         * {@see PROPERTY_LEFT_ABSOLUTE} when the exact start/end position of scroll movement
-         * isn't available at compile time.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Float> LEFT_FRACTION =
-                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractionLeft") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
-                if (obj.getBoundsRule().left == null) {
-                    obj.getBoundsRule().left = ValueRule.inheritFromParent(value);
-                } else {
-                    obj.getBoundsRule().left.setFraction(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Float get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().left == null) {
-                    return 0f;
-                }
-                return obj.getBoundsRule().left.getFraction();
-            }
-        };
-
-        /**
-         * Implementation of {@link Property} for overwriting the bottom attribute of
-         * {@link BoundsRule} associated with this {@link ChildDrawable}. This allows users to
-         * change the bounds rules as a percentage of parent size. This is preferable over
-         * {@see PROPERTY_RIGHT_ABSOLUTE} when the exact start/end position of scroll movement
-         * isn't available at compile time.
-         */
-        public static final Property<CompositeDrawable.ChildDrawable, Float> RIGHT_FRACTION =
-                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractionRight") {
-            @Override
-            public void set(CompositeDrawable.ChildDrawable obj, Float value) {
-                if (obj.getBoundsRule().right == null) {
-                    obj.getBoundsRule().right = ValueRule.inheritFromParent(value);
-                } else {
-                    obj.getBoundsRule().right.setFraction(value);
-                }
-
-                obj.recomputeBounds();
-            }
-
-            @Override
-            public Float get(CompositeDrawable.ChildDrawable obj) {
-                if (obj.getBoundsRule().right == null) {
-                    return 1f;
-                }
-                return obj.getBoundsRule().right.getFraction();
-            }
-        };
-    }
-}
diff --git a/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java b/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
deleted file mode 100644
index 6300ff2..0000000
--- a/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
+++ /dev/null
@@ -1,245 +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.v17.leanback.graphics;
-
-import android.support.annotation.RequiresApi;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.util.IntProperty;
-import android.util.Property;
-
-/**
- * Subclass of {@link Drawable} that can be used to draw a bitmap into a region. Bitmap
- * will be scaled to fit the full width of the region and will be aligned to the top left corner.
- * Any region outside the bounds will be clipped during {@link #draw(Canvas)} call. Top
- * position of the bitmap can be controlled by {@link #setVerticalOffset(int)} call or
- * {@link #PROPERTY_VERTICAL_OFFSET}.
- */
-public class FitWidthBitmapDrawable extends Drawable {
-
-    static class BitmapState extends Drawable.ConstantState {
-        Paint mPaint;
-        Bitmap mBitmap;
-        Rect mSource;
-        final Rect mDefaultSource = new Rect();
-        int mOffset;
-
-        BitmapState() {
-            mPaint = new Paint();
-        }
-
-        BitmapState(BitmapState other) {
-            mBitmap = other.mBitmap;
-            mPaint = new Paint(other.mPaint);
-            mSource = other.mSource != null ? new Rect(other.mSource) : null;
-            mDefaultSource.set(other.mDefaultSource);
-            mOffset = other.mOffset;
-        }
-
-        @NonNull
-        @Override
-        public Drawable newDrawable() {
-            return new FitWidthBitmapDrawable(this);
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return 0;
-        }
-    }
-
-    final Rect mDest = new Rect();
-    BitmapState mBitmapState;
-    boolean mMutated = false;
-
-    public FitWidthBitmapDrawable() {
-        mBitmapState = new BitmapState();
-    }
-
-    FitWidthBitmapDrawable(BitmapState state) {
-        mBitmapState = state;
-    }
-
-    @Override
-    public ConstantState getConstantState() {
-        return mBitmapState;
-    }
-
-    @Override
-    public Drawable mutate() {
-        if (!mMutated && super.mutate() == this) {
-            mBitmapState = new BitmapState(mBitmapState);
-            mMutated = true;
-        }
-        return this;
-    }
-
-    /**
-     * Sets the bitmap.
-     */
-    public void setBitmap(Bitmap bitmap) {
-        mBitmapState.mBitmap = bitmap;
-        if (bitmap != null) {
-            mBitmapState.mDefaultSource.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
-        } else {
-            mBitmapState.mDefaultSource.set(0, 0, 0, 0);
-        }
-        mBitmapState.mSource = null;
-    }
-
-    /**
-     * Returns the bitmap.
-     */
-    public Bitmap getBitmap() {
-        return mBitmapState.mBitmap;
-    }
-
-    /**
-     * Sets the {@link Rect} used for extracting the bitmap.
-     */
-    public void setSource(Rect source) {
-        mBitmapState.mSource = source;
-    }
-
-    /**
-     * Returns the {@link Rect} used for extracting the bitmap.
-     */
-    public Rect getSource() {
-        return mBitmapState.mSource;
-    }
-
-    /**
-     * Sets the vertical offset which will be used for drawing the bitmap. The bitmap drawing
-     * will start the provided vertical offset.
-     * @see #PROPERTY_VERTICAL_OFFSET
-     */
-    public void setVerticalOffset(int offset) {
-        mBitmapState.mOffset = offset;
-        invalidateSelf();
-    }
-
-    /**
-     * Returns the current vertical offset.
-     * @see #PROPERTY_VERTICAL_OFFSET
-     */
-    public int getVerticalOffset() {
-        return mBitmapState.mOffset;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mBitmapState.mBitmap != null) {
-            Rect bounds = getBounds();
-            mDest.left = 0;
-            mDest.top = mBitmapState.mOffset;
-            mDest.right = bounds.width();
-
-            Rect source = validateSource();
-            float scale = (float) bounds.width() / source.width();
-            mDest.bottom = mDest.top + (int) (source.height() * scale);
-            int i = canvas.save();
-            canvas.clipRect(bounds);
-            canvas.drawBitmap(mBitmapState.mBitmap, source, mDest, mBitmapState.mPaint);
-            canvas.restoreToCount(i);
-        }
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        final int oldAlpha = mBitmapState.mPaint.getAlpha();
-        if (alpha != oldAlpha) {
-            mBitmapState.mPaint.setAlpha(alpha);
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * @return Alpha value between 0(inclusive) and 255(inclusive)
-     */
-    @Override
-    public int getAlpha() {
-        return mBitmapState.mPaint.getAlpha();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mBitmapState.mPaint.setColorFilter(colorFilter);
-        invalidateSelf();
-    }
-
-    @Override
-    public int getOpacity() {
-        final Bitmap bitmap = mBitmapState.mBitmap;
-        return (bitmap == null || bitmap.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255)
-                ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
-    }
-
-    private Rect validateSource() {
-        if (mBitmapState.mSource == null) {
-            return mBitmapState.mDefaultSource;
-        } else {
-            return mBitmapState.mSource;
-        }
-    }
-
-    /**
-     * Property for {@link #setVerticalOffset(int)} and {@link #getVerticalOffset()}.
-     */
-    public static final Property<FitWidthBitmapDrawable, Integer> PROPERTY_VERTICAL_OFFSET;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 24) {
-            // use IntProperty
-            PROPERTY_VERTICAL_OFFSET = getVerticalOffsetIntProperty();
-        } else {
-            // 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();
-                }
-            };
-        }
-    }
-
-    @RequiresApi(24)
-    static IntProperty<FitWidthBitmapDrawable> getVerticalOffsetIntProperty() {
-        return new IntProperty<FitWidthBitmapDrawable>("verticalOffset") {
-            @Override
-            public void setValue(FitWidthBitmapDrawable fitWidthBitmapDrawable, int value) {
-                fitWidthBitmapDrawable.setVerticalOffset(value);
-            }
-
-            @Override
-            public Integer get(FitWidthBitmapDrawable fitWidthBitmapDrawable) {
-                return fitWidthBitmapDrawable.getVerticalOffset();
-            }
-        };
-    }
-}
diff --git a/android/support/v17/leanback/media/MediaControllerAdapter.java b/android/support/v17/leanback/media/MediaControllerAdapter.java
deleted file mode 100644
index 79c561d..0000000
--- a/android/support/v17/leanback/media/MediaControllerAdapter.java
+++ /dev/null
@@ -1,348 +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.v17.leanback.media;
-
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_FAST_FORWARD;
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_PLAY_PAUSE;
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_REPEAT;
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_REWIND;
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_SHUFFLE;
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_SKIP_TO_NEXT;
-import static android.support.v17.leanback.media.PlaybackBaseControlGlue.ACTION_SKIP_TO_PREVIOUS;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v4.media.MediaMetadataCompat;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import android.util.Log;
-
-/**
- * A helper class for implementing a adapter layer for {@link MediaControllerCompat}.
- */
-public class MediaControllerAdapter extends PlayerAdapter {
-
-    private static final String TAG = "MediaControllerAdapter";
-    private static final boolean DEBUG = false;
-
-    private MediaControllerCompat mController;
-    private Handler mHandler = new Handler();
-
-    // Runnable object to update current media's playing position.
-    private final Runnable mPositionUpdaterRunnable = new Runnable() {
-        @Override
-        public void run() {
-            getCallback().onCurrentPositionChanged(MediaControllerAdapter.this);
-            mHandler.postDelayed(this, getUpdatePeriod());
-        }
-    };
-
-    // Update period to post runnable.
-    private int getUpdatePeriod() {
-        return 16;
-    }
-
-    private boolean mIsBuffering = false;
-
-    MediaControllerCompat.Callback mMediaControllerCallback =
-            new MediaControllerCompat.Callback() {
-                @Override
-                public void onPlaybackStateChanged(PlaybackStateCompat state) {
-                    if (mIsBuffering && state.getState() != PlaybackStateCompat.STATE_BUFFERING) {
-                        getCallback().onBufferingStateChanged(MediaControllerAdapter.this, false);
-                        getCallback().onBufferedPositionChanged(MediaControllerAdapter.this);
-                        mIsBuffering = false;
-                    }
-                    if (state.getState() == PlaybackStateCompat.STATE_NONE) {
-                        // The STATE_NONE playback state will only occurs when initialize the player
-                        // at first time.
-                        if (DEBUG) {
-                            Log.d(TAG, "Playback state is none");
-                        }
-                    } else if (state.getState() == PlaybackStateCompat.STATE_STOPPED) {
-                        // STATE_STOPPED is associated with onPlayCompleted() callback.
-                        // STATE_STOPPED playback state will only occurs when the last item in
-                        // play list is finished. And repeat mode is not enabled.
-                        getCallback().onPlayCompleted(MediaControllerAdapter.this);
-                    } else if (state.getState() == PlaybackStateCompat.STATE_PAUSED) {
-                        getCallback().onPlayStateChanged(MediaControllerAdapter.this);
-                        getCallback().onCurrentPositionChanged(MediaControllerAdapter.this);
-                    } else if (state.getState() == PlaybackStateCompat.STATE_PLAYING) {
-                        getCallback().onPlayStateChanged(MediaControllerAdapter.this);
-                        getCallback().onCurrentPositionChanged(MediaControllerAdapter.this);
-                    } else if (state.getState() == PlaybackStateCompat.STATE_BUFFERING) {
-                        mIsBuffering = true;
-                        getCallback().onBufferingStateChanged(MediaControllerAdapter.this, true);
-                        getCallback().onBufferedPositionChanged(MediaControllerAdapter.this);
-                    } else if (state.getState() == PlaybackStateCompat.STATE_ERROR) {
-                        CharSequence errorMessage = state.getErrorMessage();
-                        if (errorMessage == null) {
-                            getCallback().onError(MediaControllerAdapter.this, state.getErrorCode(),
-                                    "");
-                        } else {
-                            getCallback().onError(MediaControllerAdapter.this, state.getErrorCode(),
-                                    state.getErrorMessage().toString());
-                        }
-                    } else if (state.getState() == PlaybackStateCompat.STATE_FAST_FORWARDING) {
-                        getCallback().onPlayStateChanged(MediaControllerAdapter.this);
-                        getCallback().onCurrentPositionChanged(MediaControllerAdapter.this);
-                    } else if (state.getState() == PlaybackStateCompat.STATE_REWINDING) {
-                        getCallback().onPlayStateChanged(MediaControllerAdapter.this);
-                        getCallback().onCurrentPositionChanged(MediaControllerAdapter.this);
-                    }
-                }
-
-                @Override
-                public void onMetadataChanged(MediaMetadataCompat metadata) {
-                    getCallback().onMetadataChanged(MediaControllerAdapter.this);
-                }
-            };
-
-    /**
-     * Constructor for the adapter using {@link MediaControllerCompat}.
-     *
-     * @param controller Object of MediaControllerCompat..
-     */
-    public MediaControllerAdapter(MediaControllerCompat controller) {
-        if (controller == null) {
-            throw new NullPointerException("Object of MediaControllerCompat is null");
-        }
-        mController = controller;
-    }
-
-    /**
-     * Return the object of {@link MediaControllerCompat} from this class.
-     *
-     * @return Media Controller Compat object owned by this class.
-     */
-    public MediaControllerCompat getMediaController() {
-        return mController;
-    }
-
-    @Override
-    public void play() {
-        mController.getTransportControls().play();
-    }
-
-    @Override
-    public void pause() {
-        mController.getTransportControls().pause();
-    }
-
-    @Override
-    public void seekTo(long positionInMs) {
-        mController.getTransportControls().seekTo(positionInMs);
-    }
-
-    @Override
-    public void next() {
-        mController.getTransportControls().skipToNext();
-    }
-
-    @Override
-    public void previous() {
-        mController.getTransportControls().skipToPrevious();
-    }
-
-    @Override
-    public void fastForward() {
-        mController.getTransportControls().fastForward();
-    }
-
-    @Override
-    public void rewind() {
-        mController.getTransportControls().rewind();
-    }
-
-    @Override
-    public void setRepeatAction(int repeatActionIndex) {
-        int repeatMode = mapRepeatActionToRepeatMode(repeatActionIndex);
-        mController.getTransportControls().setRepeatMode(repeatMode);
-    }
-
-    @Override
-    public void setShuffleAction(int shuffleActionIndex) {
-        int shuffleMode = mapShuffleActionToShuffleMode(shuffleActionIndex);
-        mController.getTransportControls().setShuffleMode(shuffleMode);
-    }
-
-    @Override
-    public boolean isPlaying() {
-        if (mController.getPlaybackState() == null) {
-            return false;
-        }
-        return mController.getPlaybackState().getState()
-                == PlaybackStateCompat.STATE_PLAYING
-                || mController.getPlaybackState().getState()
-                == PlaybackStateCompat.STATE_FAST_FORWARDING
-                || mController.getPlaybackState().getState() == PlaybackStateCompat.STATE_REWINDING;
-    }
-
-    @Override
-    public long getCurrentPosition() {
-        if (mController.getPlaybackState() == null) {
-            return 0;
-        }
-        return mController.getPlaybackState().getPosition();
-    }
-
-    @Override
-    public long getBufferedPosition() {
-        if (mController.getPlaybackState() == null) {
-            return 0;
-        }
-        return mController.getPlaybackState().getBufferedPosition();
-    }
-
-    /**
-     * Get current media's title.
-     *
-     * @return Title of current media.
-     */
-    public CharSequence getMediaTitle() {
-        if (mController.getMetadata() == null) {
-            return "";
-        }
-        return mController.getMetadata().getDescription().getTitle();
-    }
-
-    /**
-     * Get current media's subtitle.
-     *
-     * @return Subtitle of current media.
-     */
-    public CharSequence getMediaSubtitle() {
-        if (mController.getMetadata() == null) {
-            return "";
-        }
-        return mController.getMetadata().getDescription().getSubtitle();
-    }
-
-    /**
-     * Get current media's drawable art.
-     *
-     * @return Drawable art of current media.
-     */
-    public Drawable getMediaArt(Context context) {
-        if (mController.getMetadata() == null) {
-            return null;
-        }
-        Bitmap bitmap = mController.getMetadata().getDescription().getIconBitmap();
-        return bitmap == null ? null : new BitmapDrawable(context.getResources(), bitmap);
-    }
-
-    @Override
-    public long getDuration() {
-        if (mController.getMetadata() == null) {
-            return 0;
-        }
-        return (int) mController.getMetadata().getLong(
-                MediaMetadataCompat.METADATA_KEY_DURATION);
-    }
-
-    @Override
-    public void onAttachedToHost(PlaybackGlueHost host) {
-        mController.registerCallback(mMediaControllerCallback);
-    }
-
-    @Override
-    public void onDetachedFromHost() {
-        mController.unregisterCallback(mMediaControllerCallback);
-    }
-
-    @Override
-    public void setProgressUpdatingEnabled(boolean enabled) {
-        mHandler.removeCallbacks(mPositionUpdaterRunnable);
-        if (!enabled) {
-            return;
-        }
-        mHandler.postDelayed(mPositionUpdaterRunnable, getUpdatePeriod());
-    }
-
-    @Override
-    public long getSupportedActions() {
-        long supportedActions = 0;
-        if (mController.getPlaybackState() == null) {
-            return supportedActions;
-        }
-        long actionsFromController = mController.getPlaybackState().getActions();
-        // Translation.
-        if ((actionsFromController & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
-            supportedActions |= ACTION_PLAY_PAUSE;
-        }
-        if ((actionsFromController & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
-            supportedActions |= ACTION_SKIP_TO_NEXT;
-        }
-        if ((actionsFromController & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
-            supportedActions |= ACTION_SKIP_TO_PREVIOUS;
-        }
-        if ((actionsFromController & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
-            supportedActions |= ACTION_FAST_FORWARD;
-        }
-        if ((actionsFromController & PlaybackStateCompat.ACTION_REWIND) != 0) {
-            supportedActions |= ACTION_REWIND;
-        }
-        if ((actionsFromController & PlaybackStateCompat.ACTION_SET_REPEAT_MODE) != 0) {
-            supportedActions |= ACTION_REPEAT;
-        }
-        if ((actionsFromController & PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) != 0) {
-            supportedActions |= ACTION_SHUFFLE;
-        }
-        return supportedActions;
-    }
-
-    /**
-     * This function will translate the index of RepeatAction in PlaybackControlsRow to
-     * the repeat mode which is defined by PlaybackStateCompat.
-     *
-     * @param repeatActionIndex Index of RepeatAction in PlaybackControlsRow.
-     * @return Repeat Mode in playback state.
-     */
-    private int mapRepeatActionToRepeatMode(int repeatActionIndex) {
-        switch (repeatActionIndex) {
-            case PlaybackControlsRow.RepeatAction.INDEX_NONE:
-                return PlaybackStateCompat.REPEAT_MODE_NONE;
-            case PlaybackControlsRow.RepeatAction.INDEX_ALL:
-                return PlaybackStateCompat.REPEAT_MODE_ALL;
-            case PlaybackControlsRow.RepeatAction.INDEX_ONE:
-                return PlaybackStateCompat.REPEAT_MODE_ONE;
-        }
-        return -1;
-    }
-
-    /**
-     * This function will translate the index of RepeatAction in PlaybackControlsRow to
-     * the repeat mode which is defined by PlaybackStateCompat.
-     *
-     * @param shuffleActionIndex Index of RepeatAction in PlaybackControlsRow.
-     * @return Repeat Mode in playback state.
-     */
-    private int mapShuffleActionToShuffleMode(int shuffleActionIndex) {
-        switch (shuffleActionIndex) {
-            case PlaybackControlsRow.ShuffleAction.INDEX_OFF:
-                return PlaybackStateCompat.SHUFFLE_MODE_NONE;
-            case PlaybackControlsRow.ShuffleAction.INDEX_ON:
-                return PlaybackStateCompat.SHUFFLE_MODE_ALL;
-        }
-        return -1;
-    }
-}
-
diff --git a/android/support/v17/leanback/media/MediaControllerGlue.java b/android/support/v17/leanback/media/MediaControllerGlue.java
deleted file mode 100644
index b8e9b74..0000000
--- a/android/support/v17/leanback/media/MediaControllerGlue.java
+++ /dev/null
@@ -1,224 +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.v17.leanback.media;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.v4.media.MediaMetadataCompat;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import android.util.Log;
-
-/**
- * A helper class for implementing a glue layer for {@link MediaControllerCompat}.
- * @deprecated Use {@link MediaControllerAdapter} with {@link PlaybackTransportControlGlue} or
- *             {@link PlaybackBannerControlGlue}.
- */
-@Deprecated
-public abstract class MediaControllerGlue extends PlaybackControlGlue {
-    static final String TAG = "MediaControllerGlue";
-    static final boolean DEBUG = false;
-
-    MediaControllerCompat mMediaController;
-
-    private final MediaControllerCompat.Callback mCallback = new MediaControllerCompat.Callback() {
-        @Override
-        public void onMetadataChanged(MediaMetadataCompat metadata) {
-            if (DEBUG) Log.v(TAG, "onMetadataChanged");
-            MediaControllerGlue.this.onMetadataChanged();
-        }
-        @Override
-        public void onPlaybackStateChanged(PlaybackStateCompat state) {
-            if (DEBUG) Log.v(TAG, "onPlaybackStateChanged");
-            onStateChanged();
-        }
-        @Override
-        public void onSessionDestroyed() {
-            if (DEBUG) Log.v(TAG, "onSessionDestroyed");
-            mMediaController = null;
-        }
-        @Override
-        public void onSessionEvent(String event, Bundle extras) {
-            if (DEBUG) Log.v(TAG, "onSessionEvent");
-        }
-    };
-
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param fastForwardSpeeds Array of seek speeds for fast forward.
-     * @param rewindSpeeds Array of seek speeds for rewind.
-     */
-    public MediaControllerGlue(Context context,
-                               int[] fastForwardSpeeds,
-                               int[] rewindSpeeds) {
-        super(context, fastForwardSpeeds, rewindSpeeds);
-    }
-
-    /**
-     * Attaches to the given media controller.
-     */
-    public void attachToMediaController(MediaControllerCompat mediaController) {
-        if (mediaController != mMediaController) {
-            if (DEBUG) Log.v(TAG, "New media controller " + mediaController);
-            detach();
-            mMediaController = mediaController;
-            if (mMediaController != null) {
-                mMediaController.registerCallback(mCallback);
-            }
-            onMetadataChanged();
-            onStateChanged();
-        }
-    }
-
-    /**
-     * Detaches from the media controller.  Must be called when the object is no longer
-     * needed.
-     */
-    public void detach() {
-        if (mMediaController != null) {
-            mMediaController.unregisterCallback(mCallback);
-        }
-        mMediaController = null;
-    }
-
-    /**
-     * Returns the media controller currently attached.
-     */
-    public final MediaControllerCompat getMediaController() {
-        return mMediaController;
-    }
-
-    @Override
-    public boolean hasValidMedia() {
-        return mMediaController != null && mMediaController.getMetadata() != null;
-    }
-
-    @Override
-    public boolean isMediaPlaying() {
-        return mMediaController.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING;
-    }
-
-    @Override
-    public int getCurrentSpeedId() {
-        int speed = (int) mMediaController.getPlaybackState().getPlaybackSpeed();
-        if (speed == 0) {
-            return PLAYBACK_SPEED_PAUSED;
-        } else if (speed == 1) {
-            return PLAYBACK_SPEED_NORMAL;
-        } else if (speed > 0) {
-            int[] seekSpeeds = getFastForwardSpeeds();
-            for (int index = 0; index < seekSpeeds.length; index++) {
-                if (speed == seekSpeeds[index]) {
-                    return PLAYBACK_SPEED_FAST_L0 + index;
-                }
-            }
-        } else {
-            int[] seekSpeeds = getRewindSpeeds();
-            for (int index = 0; index < seekSpeeds.length; index++) {
-                if (-speed == seekSpeeds[index]) {
-                    return -PLAYBACK_SPEED_FAST_L0 - index;
-                }
-            }
-        }
-        Log.w(TAG, "Couldn't find index for speed " + speed);
-        return PLAYBACK_SPEED_INVALID;
-    }
-
-    @Override
-    public CharSequence getMediaTitle() {
-        return mMediaController.getMetadata().getDescription().getTitle();
-    }
-
-    @Override
-    public CharSequence getMediaSubtitle() {
-        return mMediaController.getMetadata().getDescription().getSubtitle();
-    }
-
-    @Override
-    public int getMediaDuration() {
-        return (int) mMediaController.getMetadata().getLong(
-                MediaMetadataCompat.METADATA_KEY_DURATION);
-    }
-
-    @Override
-    public int getCurrentPosition() {
-        return (int) mMediaController.getPlaybackState().getPosition();
-    }
-
-    @Override
-    public Drawable getMediaArt() {
-        Bitmap bitmap = mMediaController.getMetadata().getDescription().getIconBitmap();
-        return bitmap == null ? null : new BitmapDrawable(getContext().getResources(), bitmap);
-    }
-
-    @Override
-    public long getSupportedActions() {
-        long result = 0;
-        long actions = mMediaController.getPlaybackState().getActions();
-        if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
-            result |= ACTION_PLAY_PAUSE;
-        }
-        if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
-            result |= ACTION_SKIP_TO_NEXT;
-        }
-        if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
-            result |= ACTION_SKIP_TO_PREVIOUS;
-        }
-        if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
-            result |= ACTION_FAST_FORWARD;
-        }
-        if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
-            result |= ACTION_REWIND;
-        }
-        return result;
-    }
-
-    @Override
-    public void play(int speed) {
-        if (DEBUG) Log.v(TAG, "startPlayback speed " + speed);
-        if (speed == PLAYBACK_SPEED_NORMAL) {
-            mMediaController.getTransportControls().play();
-        } else if (speed > 0) {
-            mMediaController.getTransportControls().fastForward();
-        } else {
-            mMediaController.getTransportControls().rewind();
-        }
-    }
-
-    @Override
-    public void pause() {
-        if (DEBUG) Log.v(TAG, "pausePlayback");
-        mMediaController.getTransportControls().pause();
-    }
-
-    @Override
-    public void next() {
-        if (DEBUG) Log.v(TAG, "skipToNext");
-        mMediaController.getTransportControls().skipToNext();
-    }
-
-    @Override
-    public void previous() {
-        if (DEBUG) Log.v(TAG, "skipToPrevious");
-        mMediaController.getTransportControls().skipToPrevious();
-    }
-}
diff --git a/android/support/v17/leanback/media/MediaPlayerAdapter.java b/android/support/v17/leanback/media/MediaPlayerAdapter.java
deleted file mode 100644
index 3de7aa1..0000000
--- a/android/support/v17/leanback/media/MediaPlayerAdapter.java
+++ /dev/null
@@ -1,395 +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.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/android/support/v17/leanback/media/MediaPlayerGlue.java b/android/support/v17/leanback/media/MediaPlayerGlue.java
deleted file mode 100644
index 73bca97..0000000
--- a/android/support/v17/leanback/media/MediaPlayerGlue.java
+++ /dev/null
@@ -1,516 +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.v17.leanback.media;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.Handler;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.widget.Action;
-import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.OnItemViewSelectedListener;
-import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.view.KeyEvent;
-import android.view.SurfaceHolder;
-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
- * {@link MediaPlayer} synchronization. It supports 7 actions:
- *
- * <ul>
- * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction}</li>
- * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}</li>
- * <li>{@link  android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction}</li>
- * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction}</li>
- * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction}</li>
- * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction}</li>
- * </ul>
- *
- * @hide
- * @deprecated Use {@link MediaPlayerAdapter} with {@link PlaybackTransportControlGlue} or
- *             {@link PlaybackBannerControlGlue}.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@Deprecated
-public class MediaPlayerGlue extends PlaybackControlGlue implements
-        OnItemViewSelectedListener {
-
-    public static final int NO_REPEAT = 0;
-    public static final int REPEAT_ONE = 1;
-    public static final int REPEAT_ALL = 2;
-
-    public static final int FAST_FORWARD_REWIND_STEP = 10 * 1000; // in milliseconds
-    public static final int FAST_FORWARD_REWIND_REPEAT_DELAY = 200; // in milliseconds
-    private static final String TAG = "MediaPlayerGlue";
-    protected final PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
-    protected final PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
-    MediaPlayer mPlayer = new MediaPlayer();
-    private final PlaybackControlsRow.RepeatAction mRepeatAction;
-    private Runnable mRunnable;
-    private Handler mHandler = new Handler();
-    private boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
-    private Action mSelectedAction; // the action which is currently selected by the user
-    private long mLastKeyDownEvent = 0L; // timestamp when the last DPAD_CENTER KEY_DOWN occurred
-    private Uri mMediaSourceUri = null;
-    private String mMediaSourcePath = null;
-    private MediaPlayer.OnCompletionListener mOnCompletionListener;
-    private String mArtist;
-    private String mTitle;
-    private Drawable mCover;
-
-    /**
-     * Sets the drawable representing cover image.
-     */
-    public void setCover(Drawable cover) {
-        this.mCover = cover;
-    }
-
-    /**
-     * Sets the artist name.
-     */
-    public void setArtist(String artist) {
-        this.mArtist = artist;
-    }
-
-    /**
-     * Sets the media title.
-     */
-    public void setTitle(String title) {
-        this.mTitle = title;
-    }
-
-    /**
-     * Sets the url for the video.
-     */
-    public void setVideoUrl(String videoUrl) {
-        setMediaSource(videoUrl);
-        onMetadataChanged();
-    }
-
-    /**
-     * Constructor.
-     */
-    public MediaPlayerGlue(Context context) {
-        this(context, new int[]{1}, new int[]{1});
-    }
-
-    /**
-     * Constructor.
-     */
-    public MediaPlayerGlue(
-            Context context, int[] fastForwardSpeeds, int[] rewindSpeeds) {
-        super(context, fastForwardSpeeds, rewindSpeeds);
-
-        // Instantiate secondary actions
-        mRepeatAction = new PlaybackControlsRow.RepeatAction(getContext());
-        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(getContext());
-        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(getContext());
-        mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
-        mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
-    }
-
-    @Override
-    protected void onAttachedToHost(PlaybackGlueHost host) {
-        super.onAttachedToHost(host);
-        if (host instanceof SurfaceHolderGlueHost) {
-            ((SurfaceHolderGlueHost) host).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;
-            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() {
-        changeToUnitialized();
-        mPlayer.release();
-    }
-
-    @Override
-    protected void onDetachedFromHost() {
-        if (getHost() instanceof SurfaceHolderGlueHost) {
-            ((SurfaceHolderGlueHost) getHost()).setSurfaceHolderCallback(null);
-        }
-        reset();
-        release();
-        super.onDetachedFromHost();
-    }
-
-    @Override
-    protected void onCreateSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
-        secondaryActionsAdapter.add(mRepeatAction);
-        secondaryActionsAdapter.add(mThumbsDownAction);
-        secondaryActionsAdapter.add(mThumbsUpAction);
-    }
-
-    /**
-     * @see MediaPlayer#setDisplay(SurfaceHolder)
-     */
-    public void setDisplay(SurfaceHolder surfaceHolder) {
-        mPlayer.setDisplay(surfaceHolder);
-    }
-
-    @Override
-    public void enableProgressUpdating(final boolean enabled) {
-        if (mRunnable != null) mHandler.removeCallbacks(mRunnable);
-        if (!enabled) {
-            return;
-        }
-        if (mRunnable == null) {
-            mRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    updateProgress();
-                    mHandler.postDelayed(this, getUpdatePeriod());
-                }
-            };
-        }
-        mHandler.postDelayed(mRunnable, getUpdatePeriod());
-    }
-
-    @Override
-    public void onActionClicked(Action action) {
-        // If either 'Shuffle' or 'Repeat' has been clicked we need to make sure the actions index
-        // is incremented and the UI updated such that we can display the new state.
-        super.onActionClicked(action);
-        if (action instanceof PlaybackControlsRow.RepeatAction) {
-            ((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.INDEX_SOLID);
-                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
-            }
-        } else if (action == mThumbsDownAction) {
-            if (mThumbsDownAction.getIndex() == PlaybackControlsRow.ThumbsAction.INDEX_SOLID) {
-                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
-            } else {
-                mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_SOLID);
-                mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.INDEX_OUTLINE);
-            }
-        }
-        onMetadataChanged();
-    }
-
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        // This method is overridden in order to make implement fast forwarding and rewinding when
-        // the user keeps the corresponding action pressed.
-        // We only consume DPAD_CENTER Action_DOWN events on the Fast-Forward and Rewind action and
-        // only if it has not been pressed in the last X milliseconds.
-        boolean consume = mSelectedAction instanceof PlaybackControlsRow.RewindAction;
-        consume = consume || mSelectedAction instanceof PlaybackControlsRow.FastForwardAction;
-        consume = consume && mInitialized;
-        consume = consume && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER;
-        consume = consume && event.getAction() == KeyEvent.ACTION_DOWN;
-        consume = consume && System
-                .currentTimeMillis() - mLastKeyDownEvent > FAST_FORWARD_REWIND_REPEAT_DELAY;
-
-        if (consume) {
-            mLastKeyDownEvent = System.currentTimeMillis();
-            int newPosition = getCurrentPosition() + FAST_FORWARD_REWIND_STEP;
-            if (mSelectedAction instanceof PlaybackControlsRow.RewindAction) {
-                newPosition = getCurrentPosition() - FAST_FORWARD_REWIND_STEP;
-            }
-            // Make sure the new calculated duration is in the range 0 >= X >= MediaDuration
-            if (newPosition < 0) newPosition = 0;
-            if (newPosition > getMediaDuration()) newPosition = getMediaDuration();
-            seekTo(newPosition);
-            return true;
-        }
-
-        return super.onKey(v, keyCode, event);
-    }
-
-    @Override
-    public boolean hasValidMedia() {
-        return mTitle != null && (mMediaSourcePath != null || mMediaSourceUri != null);
-    }
-
-    @Override
-    public boolean isMediaPlaying() {
-        return mInitialized && mPlayer.isPlaying();
-    }
-
-    @Override
-    public boolean isPlaying() {
-        return isMediaPlaying();
-    }
-
-    @Override
-    public CharSequence getMediaTitle() {
-        return mTitle != null ? mTitle : "N/a";
-    }
-
-    @Override
-    public CharSequence getMediaSubtitle() {
-        return mArtist != null ? mArtist : "N/a";
-    }
-
-    @Override
-    public int getMediaDuration() {
-        return mInitialized ? mPlayer.getDuration() : 0;
-    }
-
-    @Override
-    public Drawable getMediaArt() {
-        return mCover;
-    }
-
-    @Override
-    public long getSupportedActions() {
-        return PlaybackControlGlue.ACTION_PLAY_PAUSE
-                | PlaybackControlGlue.ACTION_FAST_FORWARD
-                | PlaybackControlGlue.ACTION_REWIND;
-    }
-
-    @Override
-    public int getCurrentSpeedId() {
-        // 0 = Pause, 1 = Normal Playback Speed
-        return isMediaPlaying() ? 1 : 0;
-    }
-
-    @Override
-    public int getCurrentPosition() {
-        return mInitialized ? mPlayer.getCurrentPosition() : 0;
-    }
-
-    @Override
-    public void play(int speed) {
-        if (!mInitialized || mPlayer.isPlaying()) {
-            return;
-        }
-        mPlayer.start();
-        onMetadataChanged();
-        onStateChanged();
-        updateProgress();
-    }
-
-    @Override
-    public void pause() {
-        if (isMediaPlaying()) {
-            mPlayer.pause();
-            onStateChanged();
-        }
-    }
-
-    /**
-     * Sets the playback mode. It currently support no repeat, repeat once and infinite
-     * loop mode.
-     */
-    public void setMode(int mode) {
-        switch(mode) {
-            case NO_REPEAT:
-                mOnCompletionListener = null;
-                break;
-            case REPEAT_ONE:
-                mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
-                    public boolean mFirstRepeat;
-
-                    @Override
-                    public void onCompletion(MediaPlayer mediaPlayer) {
-                        if (!mFirstRepeat) {
-                            mFirstRepeat = true;
-                            mediaPlayer.setOnCompletionListener(null);
-                        }
-                        play();
-                    }
-                };
-                break;
-            case REPEAT_ALL:
-                mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
-                    @Override
-                    public void onCompletion(MediaPlayer mediaPlayer) {
-                        play();
-                    }
-                };
-                break;
-        }
-    }
-
-    /**
-     * Called whenever the user presses fast-forward/rewind or when the user keeps the
-     * corresponding action pressed.
-     *
-     * @param newPosition The new position of the media track in milliseconds.
-     */
-    protected void seekTo(int newPosition) {
-        if (!mInitialized) {
-            return;
-        }
-        mPlayer.seekTo(newPosition);
-    }
-
-    /**
-     * 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 setMediaSource(Uri uri) {
-        if (mMediaSourceUri != null ? mMediaSourceUri.equals(uri) : uri == null) {
-            return false;
-        }
-        mMediaSourceUri = uri;
-        mMediaSourcePath = null;
-        prepareMediaForPlaying();
-        return true;
-    }
-
-    /**
-     * Sets the media source of the player with a String path URL.
-     *
-     * @return Returns <code>true</code> if path represents a new media; <code>false</code>
-     * otherwise.
-     * @see MediaPlayer#setDataSource(String)
-     */
-    public boolean setMediaSource(String path) {
-        if (mMediaSourcePath != null ? mMediaSourcePath.equals(path) : path == null) {
-            return false;
-        }
-        mMediaSourceUri = null;
-        mMediaSourcePath = path;
-        prepareMediaForPlaying();
-        return true;
-    }
-
-    private void prepareMediaForPlaying() {
-        reset();
-        try {
-            if (mMediaSourceUri != null) {
-                mPlayer.setDataSource(getContext(), mMediaSourceUri);
-            } else if (mMediaSourcePath != null) {
-                mPlayer.setDataSource(mMediaSourcePath);
-            } else {
-                return;
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-            throw new RuntimeException(e);
-        }
-        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
-        mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
-            @Override
-            public void onPrepared(MediaPlayer mp) {
-                mInitialized = true;
-                List<PlayerCallback> callbacks = getPlayerCallbacks();
-                if (callbacks != null) {
-                    for (PlayerCallback callback: callbacks) {
-                        callback.onPreparedStateChanged(MediaPlayerGlue.this);
-                    }
-                }
-            }
-        });
-
-        if (mOnCompletionListener != null) {
-            mPlayer.setOnCompletionListener(mOnCompletionListener);
-        }
-
-        mPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
-            @Override
-            public void onBufferingUpdate(MediaPlayer mp, int percent) {
-                if (getControlsRow() == null) {
-                    return;
-                }
-                getControlsRow().setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
-            }
-        });
-        mPlayer.prepareAsync();
-        onStateChanged();
-    }
-
-    /**
-     * This is a listener implementation for the {@link OnItemViewSelectedListener}.
-     * This implementation is required in order to detect KEY_DOWN events
-     * on the {@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction} and
-     * {@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}. Thus you
-     * should <u>NOT</u> set another {@link OnItemViewSelectedListener} on your
-     * Fragment. Instead, override this method and call its super (this)
-     * implementation.
-     *
-     * @see OnItemViewSelectedListener#onItemSelected(
-     *Presenter.ViewHolder, Object, RowPresenter.ViewHolder, Object)
-     */
-    @Override
-    public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                               RowPresenter.ViewHolder rowViewHolder, Row row) {
-        if (item instanceof Action) {
-            mSelectedAction = (Action) item;
-        } else {
-            mSelectedAction = null;
-        }
-    }
-
-    @Override
-    public boolean isPrepared() {
-        return mInitialized;
-    }
-
-    /**
-     * 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/android/support/v17/leanback/media/PlaybackBannerControlGlue.java b/android/support/v17/leanback/media/PlaybackBannerControlGlue.java
deleted file mode 100644
index e644632..0000000
--- a/android/support/v17/leanback/media/PlaybackBannerControlGlue.java
+++ /dev/null
@@ -1,703 +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.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>
- *
- * Sample Code:
- * <pre><code>
- * public class MyVideoFragment extends VideoFragment {
- *     &#64;Override
- *     public void onCreate(Bundle savedInstanceState) {
- *         super.onCreate(savedInstanceState);
- *         PlaybackBannerControlGlue<MediaPlayerAdapter> playerGlue =
- *                 new PlaybackBannerControlGlue(getActivity(),
- *                         new MediaPlayerAdapter(getActivity()));
- *         playerGlue.setHost(new VideoFragmentGlueHost(this));
- *         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));
- *         playerGlue.playWhenPrepared();
- *     }
- * }
- * </code></pre>
- * @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 =
-            PlaybackBaseControlGlue.ACTION_CUSTOM_LEFT_FIRST;
-
-    /**
-     * The adapter key for the skip to previous control.
-     */
-    public static final int ACTION_SKIP_TO_PREVIOUS =
-            PlaybackBaseControlGlue.ACTION_SKIP_TO_PREVIOUS;
-
-    /**
-     * The adapter key for the rewind control.
-     */
-    public static final int ACTION_REWIND = PlaybackBaseControlGlue.ACTION_REWIND;
-
-    /**
-     * The adapter key for the play/pause control.
-     */
-    public static final int ACTION_PLAY_PAUSE = PlaybackBaseControlGlue.ACTION_PLAY_PAUSE;
-
-    /**
-     * The adapter key for the fast forward control.
-     */
-    public static final int ACTION_FAST_FORWARD = PlaybackBaseControlGlue.ACTION_FAST_FORWARD;
-
-    /**
-     * The adapter key for the skip to next control.
-     */
-    public static final int ACTION_SKIP_TO_NEXT = PlaybackBaseControlGlue.ACTION_SKIP_TO_NEXT;
-
-    /**
-     * 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 =
-            PlaybackBaseControlGlue.ACTION_CUSTOM_RIGHT_FIRST;
-
-
-    /** @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;
-
-    // Flag for is customized FastForward/ Rewind Action supported.
-    // If customized actions are not supported, the adapter can still use default behavior through
-    // setting ACTION_REWIND and ACTION_FAST_FORWARD as supported actions.
-    private boolean mIsCustomizedFastForwardSupported;
-    private boolean mIsCustomizedRewindSupported;
-
-    /**
-     * 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;
-        if ((mPlayerAdapter.getSupportedActions() & ACTION_FAST_FORWARD) != 0) {
-            mIsCustomizedFastForwardSupported = true;
-        }
-        if ((mPlayerAdapter.getSupportedActions() & ACTION_REWIND) != 0) {
-            mIsCustomizedRewindSupported = true;
-        }
-    }
-
-    @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);
-    }
-
-    // Helper function to increment mPlaybackSpeed when necessary. The mPlaybackSpeed will control
-    // the UI of fast forward button in control row.
-    private void incrementFastForwardPlaybackSpeed() {
-        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;
-        }
-    }
-
-    // Helper function to decrement mPlaybackSpeed when necessary. The mPlaybackSpeed will control
-    // the UI of rewind button in control row.
-    private void decrementRewindPlaybackSpeed() {
-        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;
-        }
-    }
-
-    /**
-     * 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()) {
-                // When the customized fast forward action is available, it will be executed
-                // when fast forward button is pressed. If current media item is not playing, the UI
-                // will be updated to PLAYING status.
-                if (mIsCustomizedFastForwardSupported) {
-                    // Change UI to Playing status.
-                    mIsPlaying = true;
-                    // Execute customized fast forward action.
-                    mPlayerAdapter.fastForward();
-                } else {
-                    // When the customized fast forward action is not supported, the fakePause
-                    // operation is needed to stop the media item but still indicating the media
-                    // item is playing from the UI perspective
-                    // Also the fakePause() method must be called before
-                    // incrementFastForwardPlaybackSpeed() method to make sure fake fast forward
-                    // computation is accurate.
-                    fakePause();
-                }
-                // Change mPlaybackSpeed to control the UI.
-                incrementFastForwardPlaybackSpeed();
-                onUpdatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        } else if (action == mRewindAction) {
-            if (mPlayerAdapter.isPrepared() && mPlaybackSpeed > -getMaxRewindSpeedId()) {
-                if (mIsCustomizedFastForwardSupported) {
-                    mIsPlaying = true;
-                    mPlayerAdapter.rewind();
-                } else {
-                    fakePause();
-                }
-                decrementRewindPlaybackSpeed();
-                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) {
-            // If fast forward operation is supported in this scenario, current player position
-            // can be get from mPlayerAdapter.getCurrentPosition() directly
-            if (mIsCustomizedFastForwardSupported) {
-                return mPlayerAdapter.getCurrentPosition();
-            }
-            int index = mPlaybackSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
-            speed = getFastForwardSpeeds()[index];
-        } else if (mPlaybackSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
-            // If fast rewind is supported in this scenario, current player position
-            // can be get from mPlayerAdapter.getCurrentPosition() directly
-            if (mIsCustomizedRewindSupported) {
-                return mPlayerAdapter.getCurrentPosition();
-            }
-            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;
-    }
-
-
-    @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/android/support/v17/leanback/media/PlaybackBaseControlGlue.java b/android/support/v17/leanback/media/PlaybackBaseControlGlue.java
deleted file mode 100644
index 81dfedc..0000000
--- a/android/support/v17/leanback/media/PlaybackBaseControlGlue.java
+++ /dev/null
@@ -1,619 +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.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 {
-
-    /**
-     * 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 repeat control.
-     */
-    public static final int ACTION_REPEAT = 0x200;
-
-    /**
-     * The adapter key for the shuffle control.
-     */
-    public static final int ACTION_SHUFFLE = 0x400;
-
-    /**
-     * 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;
-
-    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);
-            }
-        }
-
-        @Override
-        public void onMetadataChanged(PlayerAdapter wrapper) {
-            PlaybackBaseControlGlue.this.onMetadataChanged();
-        }
-    };
-
-    /**
-     * 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();
-    }
-
-    @Override
-    public void next() {
-        mPlayerAdapter.next();
-    }
-
-    @Override
-    public void previous() {
-        mPlayerAdapter.previous();
-    }
-
-    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) {
-    }
-
-    @CallSuper
-    protected void onUpdateProgress() {
-        if (mControlsRow != null) {
-            mControlsRow.setCurrentPosition(mPlayerAdapter.isPrepared()
-                    ? getCurrentPosition() : -1);
-        }
-    }
-
-    @CallSuper
-    protected void onUpdateBufferedProgress() {
-        if (mControlsRow != null) {
-            mControlsRow.setBufferedPosition(mPlayerAdapter.getBufferedPosition());
-        }
-    }
-
-    @CallSuper
-    protected 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
-     */
-    protected void onMetadataChanged() {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        if (DEBUG) Log.v(TAG, "updateRowMetadata");
-
-        mControlsRow.setImageDrawable(getArt());
-        mControlsRow.setDuration(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);
-    }
-
-    /**
-     * Returns a bitmask of actions supported by the media player.
-     */
-    public long getSupportedActions() {
-        return mPlayerAdapter.getSupportedActions();
-    }
-}
diff --git a/android/support/v17/leanback/media/PlaybackControlGlue.java b/android/support/v17/leanback/media/PlaybackControlGlue.java
deleted file mode 100644
index 0a788f6..0000000
--- a/android/support/v17/leanback/media/PlaybackControlGlue.java
+++ /dev/null
@@ -1,866 +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.v17.leanback.media;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-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.ControlButtonPresenterSelector;
-import android.support.v17.leanback.widget.OnActionClickedListener;
-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.PresenterSelector;
-import android.support.v17.leanback.widget.RowPresenter;
-import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/**
- * A helper class for managing a {@link PlaybackControlsRow}
- * and {@link PlaybackGlueHost} that implements a
- * recommended approach to handling standard playback control actions such as play/pause,
- * fast forward/rewind at progressive speed levels, and skip to next/previous. This helper class
- * is a glue layer in that manages the configuration of and interaction between the
- * leanback UI components by defining a functional interface to the media player.
- *
- * <p>You can instantiate a concrete subclass such as MediaPlayerGlue or you must
- * subclass this abstract helper.  To create a subclass you must implement all of the
- * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and
- * {@link #onStateChanged()} appropriately.
- * </p>
- *
- * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
- * inform the glue what speed levels are supported for fast forward/rewind.
- * </p>
- *
- * <p>You may override {@link #onCreateControlsRowAndPresenter()} which will create a
- * {@link PlaybackControlsRow} and a {@link PlaybackControlsRowPresenter}. You may call
- * {@link #setControlsRow(PlaybackControlsRow)} and
- * {@link #setPlaybackRowPresenter(PlaybackRowPresenter)} to customize your own row and presenter.
- * </p>
- *
- * <p>The helper sets a {@link SparseArrayObjectAdapter}
- * on the controls row as the primary actions adapter, and adds actions to it. You can provide
- * additional actions by overriding {@link #onCreatePrimaryActions}. This helper does not
- * deal in secondary actions so those you may add separately.
- * </p>
- *
- * <p>Provide a click listener on your fragment and if an action is clicked, call
- * {@link #onActionClicked}.
- * </p>
- *
- * <p>This helper implements a key event handler. If you pass a
- * {@link PlaybackGlueHost}, it will configure its
- * fragment to intercept all key events.  Otherwise, you should set the glue object as key event
- * handler to the ViewHolder when bound by your row presenter; see
- * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
- * </p>
- *
- * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating}
- * to manage the lifecycle of a periodic callback to {@link #updateProgress()}.
- * {@link #getUpdatePeriod()} provides a recommended update period.
- * </p>
- *
- */
-public abstract class PlaybackControlGlue extends PlaybackGlue
-        implements OnActionClickedListener, View.OnKeyListener {
-    /**
-     * 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;
-
-    /**
-     * 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;
-
-    static final String TAG = "PlaybackControlGlue";
-    static final boolean DEBUG = false;
-
-    static final int MSG_UPDATE_PLAYBACK_STATE = 100;
-    private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
-    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 mControlsRow;
-    private PlaybackRowPresenter mControlsRowPresenter;
-    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
-    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
-    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
-    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
-    private PlaybackControlsRow.RewindAction mRewindAction;
-    private int mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-    private boolean mFadeWhenPlaying = true;
-
-    static class UpdatePlaybackStateHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_UPDATE_PLAYBACK_STATE) {
-                PlaybackControlGlue glue = ((WeakReference<PlaybackControlGlue>) msg.obj).get();
-                if (glue != null) {
-                    glue.updatePlaybackState();
-                }
-            }
-        }
-    }
-
-    static final Handler sHandler = new UpdatePlaybackStateHandler();
-
-    final WeakReference<PlaybackControlGlue> mGlueWeakReference =  new WeakReference(this);
-
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
-     */
-    public PlaybackControlGlue(Context context, int[] seekSpeeds) {
-        this(context, seekSpeeds, seekSpeeds);
-    }
-
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param fastForwardSpeeds Array of seek speeds for fast forward.
-     * @param rewindSpeeds Array of seek speeds for rewind.
-     */
-    public PlaybackControlGlue(Context context,
-                               int[] fastForwardSpeeds,
-                               int[] rewindSpeeds) {
-        super(context);
-        if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
-            throw new IllegalStateException("invalid fastForwardSpeeds array size");
-        }
-        mFastForwardSpeeds = fastForwardSpeeds;
-        if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
-            throw new IllegalStateException("invalid rewindSpeeds array size");
-        }
-        mRewindSpeeds = rewindSpeeds;
-    }
-
-    @Override
-    protected void onAttachedToHost(PlaybackGlueHost host) {
-        super.onAttachedToHost(host);
-        host.setOnKeyInterceptListener(this);
-        host.setOnActionClickedListener(this);
-        if (getControlsRow() == null || getPlaybackRowPresenter() == null) {
-            onCreateControlsRowAndPresenter();
-        }
-        host.setPlaybackRowPresenter(getPlaybackRowPresenter());
-        host.setPlaybackRow(getControlsRow());
-    }
-
-    @Override
-    protected void onHostStart() {
-        enableProgressUpdating(true);
-    }
-
-    @Override
-    protected void onHostStop() {
-        enableProgressUpdating(false);
-    }
-
-    @Override
-    protected void onDetachedFromHost() {
-        enableProgressUpdating(false);
-        super.onDetachedFromHost();
-    }
-
-    /**
-     * Instantiating a {@link PlaybackControlsRow} and corresponding
-     * {@link PlaybackControlsRowPresenter}. Subclass may override.
-     */
-    protected void onCreateControlsRowAndPresenter() {
-        if (getControlsRow() == null) {
-            PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
-            setControlsRow(controlsRow);
-        }
-        if (getPlaybackRowPresenter() == null) {
-            final AbstractDetailsDescriptionPresenter detailsPresenter =
-                    new AbstractDetailsDescriptionPresenter() {
-                        @Override
-                        protected void onBindDescription(ViewHolder
-                                viewHolder, Object object) {
-                            PlaybackControlGlue glue = (PlaybackControlGlue) object;
-                            if (glue.hasValidMedia()) {
-                                viewHolder.getTitle().setText(glue.getMediaTitle());
-                                viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
-                            } else {
-                                viewHolder.getTitle().setText("");
-                                viewHolder.getSubtitle().setText("");
-                            }
-                        }
-                    };
-
-            setPlaybackRowPresenter(new PlaybackControlsRowPresenter(detailsPresenter) {
-                @Override
-                protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
-                    super.onBindRowViewHolder(vh, item);
-                    vh.setOnKeyListener(PlaybackControlGlue.this);
-                }
-                @Override
-                protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
-                    super.onUnbindRowViewHolder(vh);
-                    vh.setOnKeyListener(null);
-                }
-            });
-        }
-    }
-
-    /**
-     * Returns the fast forward speeds.
-     */
-    public int[] getFastForwardSpeeds() {
-        return mFastForwardSpeeds;
-    }
-
-    /**
-     * Returns the rewind speeds.
-     */
-    public int[] getRewindSpeeds() {
-        return mRewindSpeeds;
-    }
-
-    /**
-     * Sets the controls to fade after a timeout when media is playing.
-     */
-    public void setFadingEnabled(boolean enable) {
-        mFadeWhenPlaying = enable;
-        if (!mFadeWhenPlaying && getHost() != null) {
-            getHost().setControlsOverlayAutoHideEnabled(false);
-        }
-    }
-
-    /**
-     * Returns true if controls are set to fade when media is playing.
-     */
-    public boolean isFadingEnabled() {
-        return mFadeWhenPlaying;
-    }
-
-    /**
-     * Sets the controls row to be managed by the glue layer.
-     * The primary actions and playback state related aspects of the row
-     * are updated by the glue.
-     */
-    public void setControlsRow(PlaybackControlsRow controlsRow) {
-        mControlsRow = controlsRow;
-        mControlsRow.setPrimaryActionsAdapter(
-                createPrimaryActionsAdapter(new ControlButtonPresenterSelector()));
-        // Add secondary actions
-        ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
-                new ControlButtonPresenterSelector());
-        onCreateSecondaryActions(secondaryActions);
-        getControlsRow().setSecondaryActionsAdapter(secondaryActions);
-        updateControlsRow();
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
-            PresenterSelector presenterSelector) {
-        SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
-        onCreatePrimaryActions(adapter);
-        return adapter;
-    }
-
-    /**
-     * Sets the controls row Presenter to be managed by the glue layer.
-     * @deprecated PlaybackControlGlue supports any PlaybackRowPresenter, use
-     * {@link #setPlaybackRowPresenter(PlaybackRowPresenter)}.
-     */
-    @Deprecated
-    public void setControlsRowPresenter(PlaybackControlsRowPresenter 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.
-     * @deprecated PlaybackControlGlue supports any PlaybackRowPresenter, use
-     * {@link #getPlaybackRowPresenter()}.
-     */
-    @Deprecated
-    public PlaybackControlsRowPresenter getControlsRowPresenter() {
-        return mControlsRowPresenter instanceof PlaybackControlsRowPresenter
-                ? (PlaybackControlsRowPresenter) mControlsRowPresenter : null;
-    }
-
-    /**
-     * Sets the controls row Presenter to be passed to {@link PlaybackGlueHost} in
-     * {@link #onAttachedToHost(PlaybackGlueHost)}.
-     */
-    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {
-        mControlsRowPresenter = presenter;
-    }
-
-    /**
-     * Returns the playback row Presenter to be passed to {@link PlaybackGlueHost} in
-     * {@link #onAttachedToHost(PlaybackGlueHost)}.
-     */
-    public PlaybackRowPresenter getPlaybackRowPresenter() {
-        return mControlsRowPresenter;
-    }
-
-    /**
-     * Override this to start/stop a runnable to call {@link #updateProgress} at
-     * an interval such as {@link #getUpdatePeriod}.
-     */
-    public void enableProgressUpdating(boolean enable) {
-    }
-
-    /**
-     * Returns the time period in milliseconds that should be used
-     * to update the progress.  See {@link #updateProgress()}.
-     */
-    public int getUpdatePeriod() {
-        // TODO: calculate a better update period based on total duration and screen size
-        return 500;
-    }
-
-    /**
-     * Updates the progress bar based on the current media playback position.
-     */
-    public void updateProgress() {
-        int position = getCurrentPosition();
-        if (DEBUG) Log.v(TAG, "updateProgress " + position);
-        if (mControlsRow != null) {
-            mControlsRow.setCurrentTime(position);
-        }
-    }
-
-    /**
-     * 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) {
-                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                    play(mPlaybackSpeed);
-                    updatePlaybackStatusAfterUserAction();
-                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
-                }
-                return false;
-        }
-        final SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
-                mControlsRow.getPrimaryActionsAdapter();
-        Action action = mControlsRow.getActionForKeyCode(primaryActionsAdapter, keyCode);
-
-        if (action != null) {
-            if (action == primaryActionsAdapter.lookup(ACTION_PLAY_PAUSE)
-                    || action == primaryActionsAdapter.lookup(ACTION_REWIND)
-                    || action == primaryActionsAdapter.lookup(ACTION_FAST_FORWARD)
-                    || action == primaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS)
-                    || action == primaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
-                if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                    dispatchAction(action, event);
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called when the given action is invoked, either by click or keyevent.
-     */
-    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)) {
-                mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
-                pause();
-            } else if (canPlay && mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
-                mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                play(mPlaybackSpeed);
-            }
-            updatePlaybackStatusAfterUserAction();
-            handled = true;
-        } else if (action == mSkipNextAction) {
-            next();
-            handled = true;
-        } else if (action == mSkipPreviousAction) {
-            previous();
-            handled = true;
-        } else if (action == mFastForwardAction) {
-            if (mPlaybackSpeed < getMaxForwardSpeedId()) {
-                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;
-                }
-                play(mPlaybackSpeed);
-                updatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        } else if (action == mRewindAction) {
-            if (mPlaybackSpeed > -getMaxRewindSpeedId()) {
-                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;
-                }
-                play(mPlaybackSpeed);
-                updatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        }
-        return handled;
-    }
-
-    private int getMaxForwardSpeedId() {
-        return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
-    }
-
-    private int getMaxRewindSpeedId() {
-        return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
-    }
-
-    private void updateControlsRow() {
-        updateRowMetadata();
-        updateControlButtons();
-        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
-        updatePlaybackState();
-    }
-
-    private void updatePlaybackStatusAfterUserAction() {
-        updatePlaybackState(mPlaybackSpeed);
-        // 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);
-    }
-
-    /**
-     * Start playback at the given speed.
-     *
-     * @param speed The desired playback speed.  For normal playback this will be
-     *              {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward,
-     *              and negative values for rewind.
-     */
-    public void play(int speed) {
-    }
-
-    @Override
-    public final void play() {
-        play(PLAYBACK_SPEED_NORMAL);
-    }
-
-    private void updateRowMetadata() {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        if (DEBUG) Log.v(TAG, "updateRowMetadata");
-
-        if (!hasValidMedia()) {
-            mControlsRow.setImageDrawable(null);
-            mControlsRow.setTotalTime(0);
-            mControlsRow.setCurrentTime(0);
-        } else {
-            mControlsRow.setImageDrawable(getMediaArt());
-            mControlsRow.setTotalTime(getMediaDuration());
-            mControlsRow.setCurrentTime(getCurrentPosition());
-        }
-
-        if (getHost() != null) {
-            getHost().notifyPlaybackRowChanged();
-        }
-    }
-
-    void updatePlaybackState() {
-        if (hasValidMedia()) {
-            mPlaybackSpeed = getCurrentSpeedId();
-            updatePlaybackState(mPlaybackSpeed);
-        }
-    }
-
-    void updateControlButtons() {
-        final SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
-                getControlsRow().getPrimaryActionsAdapter();
-        final long actions = getSupportedActions();
-        if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0 && mSkipPreviousAction == null) {
-            mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(getContext());
-            primaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction);
-        } else if ((actions & ACTION_SKIP_TO_PREVIOUS) == 0 && mSkipPreviousAction != null) {
-            primaryActionsAdapter.clear(ACTION_SKIP_TO_PREVIOUS);
-            mSkipPreviousAction = null;
-        }
-        if ((actions & ACTION_REWIND) != 0 && mRewindAction == null) {
-            mRewindAction = new PlaybackControlsRow.RewindAction(getContext(),
-                    mRewindSpeeds.length);
-            primaryActionsAdapter.set(ACTION_REWIND, mRewindAction);
-        } else if ((actions & ACTION_REWIND) == 0 && mRewindAction != null) {
-            primaryActionsAdapter.clear(ACTION_REWIND);
-            mRewindAction = null;
-        }
-        if ((actions & ACTION_PLAY_PAUSE) != 0 && mPlayPauseAction == null) {
-            mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(getContext());
-            primaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction);
-        } else if ((actions & ACTION_PLAY_PAUSE) == 0 && mPlayPauseAction != null) {
-            primaryActionsAdapter.clear(ACTION_PLAY_PAUSE);
-            mPlayPauseAction = null;
-        }
-        if ((actions & ACTION_FAST_FORWARD) != 0 && mFastForwardAction == null) {
-            mFastForwardAction = new PlaybackControlsRow.FastForwardAction(getContext(),
-                    mFastForwardSpeeds.length);
-            primaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction);
-        } else if ((actions & ACTION_FAST_FORWARD) == 0 && mFastForwardAction != null) {
-            primaryActionsAdapter.clear(ACTION_FAST_FORWARD);
-            mFastForwardAction = null;
-        }
-        if ((actions & ACTION_SKIP_TO_NEXT) != 0 && mSkipNextAction == null) {
-            mSkipNextAction = new PlaybackControlsRow.SkipNextAction(getContext());
-            primaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction);
-        } else if ((actions & ACTION_SKIP_TO_NEXT) == 0 && mSkipNextAction != null) {
-            primaryActionsAdapter.clear(ACTION_SKIP_TO_NEXT);
-            mSkipNextAction = null;
-        }
-    }
-
-    private void updatePlaybackState(int playbackSpeed) {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        final SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
-                getControlsRow().getPrimaryActionsAdapter();
-
-        if (mFastForwardAction != null) {
-            int index = 0;
-            if (playbackSpeed >= PLAYBACK_SPEED_FAST_L0) {
-                index = playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
-            }
-            if (mFastForwardAction.getIndex() != index) {
-                mFastForwardAction.setIndex(index);
-                notifyItemChanged(primaryActionsAdapter, mFastForwardAction);
-            }
-        }
-        if (mRewindAction != null) {
-            int index = 0;
-            if (playbackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
-                index = -playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
-            }
-            if (mRewindAction.getIndex() != index) {
-                mRewindAction.setIndex(index);
-                notifyItemChanged(primaryActionsAdapter, mRewindAction);
-            }
-        }
-
-        if (playbackSpeed == PLAYBACK_SPEED_PAUSED) {
-            updateProgress();
-            enableProgressUpdating(false);
-        } else {
-            enableProgressUpdating(true);
-        }
-
-        if (mFadeWhenPlaying && getHost() != null) {
-            getHost().setControlsOverlayAutoHideEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
-        }
-
-        if (mPlayPauseAction != null) {
-            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED
-                    ? 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) {
-        int index = adapter.indexOf(object);
-        if (index >= 0) {
-            adapter.notifyArrayItemRangeChanged(index, 1);
-        }
-    }
-
-    private static String getSpeedString(int speed) {
-        switch (speed) {
-            case PLAYBACK_SPEED_INVALID:
-                return "PLAYBACK_SPEED_INVALID";
-            case PLAYBACK_SPEED_PAUSED:
-                return "PLAYBACK_SPEED_PAUSED";
-            case PLAYBACK_SPEED_NORMAL:
-                return "PLAYBACK_SPEED_NORMAL";
-            case PLAYBACK_SPEED_FAST_L0:
-                return "PLAYBACK_SPEED_FAST_L0";
-            case PLAYBACK_SPEED_FAST_L1:
-                return "PLAYBACK_SPEED_FAST_L1";
-            case PLAYBACK_SPEED_FAST_L2:
-                return "PLAYBACK_SPEED_FAST_L2";
-            case PLAYBACK_SPEED_FAST_L3:
-                return "PLAYBACK_SPEED_FAST_L3";
-            case PLAYBACK_SPEED_FAST_L4:
-                return "PLAYBACK_SPEED_FAST_L4";
-            case -PLAYBACK_SPEED_FAST_L0:
-                return "-PLAYBACK_SPEED_FAST_L0";
-            case -PLAYBACK_SPEED_FAST_L1:
-                return "-PLAYBACK_SPEED_FAST_L1";
-            case -PLAYBACK_SPEED_FAST_L2:
-                return "-PLAYBACK_SPEED_FAST_L2";
-            case -PLAYBACK_SPEED_FAST_L3:
-                return "-PLAYBACK_SPEED_FAST_L3";
-            case -PLAYBACK_SPEED_FAST_L4:
-                return "-PLAYBACK_SPEED_FAST_L4";
-        }
-        return null;
-    }
-
-    /**
-     * Returns true if there is a valid media item.
-     */
-    public abstract boolean hasValidMedia();
-
-    /**
-     * Returns true if media is currently playing.
-     */
-    public abstract boolean isMediaPlaying();
-
-    @Override
-    public boolean isPlaying() {
-        return isMediaPlaying();
-    }
-
-    /**
-     * Returns the title of the media item.
-     */
-    public abstract CharSequence getMediaTitle();
-
-    /**
-     * Returns the subtitle of the media item.
-     */
-    public abstract CharSequence getMediaSubtitle();
-
-    /**
-     * Returns the duration of the media item in milliseconds.
-     */
-    public abstract int getMediaDuration();
-
-    /**
-     * Returns a bitmap of the art for the media item.
-     */
-    public abstract Drawable getMediaArt();
-
-    /**
-     * Returns a bitmask of actions supported by the media player.
-     */
-    public abstract long getSupportedActions();
-
-    /**
-     * Returns the current playback speed.  When playing normally,
-     * {@link #PLAYBACK_SPEED_NORMAL} should be returned.
-     */
-    public abstract int getCurrentSpeedId();
-
-    /**
-     * Returns the current position of the media item in milliseconds.
-     */
-    public abstract int getCurrentPosition();
-
-    /**
-     * May be overridden to add primary actions to the adapter.
-     *
-     * @param primaryActionsAdapter The adapter to add primary {@link Action}s.
-     */
-    protected void onCreatePrimaryActions(SparseArrayObjectAdapter 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) {
-    }
-
-    /**
-     * Must be called appropriately by a subclass when the playback state has changed.
-     * It updates the playback state displayed on the media player.
-     */
-    protected void onStateChanged() {
-        if (DEBUG) Log.v(TAG, "onStateChanged");
-        // If a pending control button update is present, delay
-        // the update until the state settles.
-        if (!hasValidMedia()) {
-            return;
-        }
-        if (sHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference)) {
-            sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
-            if (getCurrentSpeedId() != mPlaybackSpeed) {
-                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");
-                updatePlaybackState();
-            }
-        } else {
-            updatePlaybackState();
-        }
-    }
-
-    /**
-     * Must be called appropriately by a subclass when the metadata state has changed.
-     */
-    protected void onMetadataChanged() {
-        if (DEBUG) Log.v(TAG, "onMetadataChanged");
-        updateRowMetadata();
-    }
-}
diff --git a/android/support/v17/leanback/media/PlaybackGlue.java b/android/support/v17/leanback/media/PlaybackGlue.java
deleted file mode 100644
index 7c59573..0000000
--- a/android/support/v17/leanback/media/PlaybackGlue.java
+++ /dev/null
@@ -1,278 +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.v17.leanback.media;
-
-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
- * PlaybackGlue subclass, associated it with a {@link PlaybackGlueHost}. {@link PlaybackGlueHost}
- * is typically implemented by a Fragment or an Activity, it provides the environment to render UI
- * for PlaybackGlue object, it optionally provides SurfaceHolder via {@link SurfaceHolderGlueHost}
- * to render video. A typical PlaybackGlue should release resources (e.g. MediaPlayer or connection
- * to playback Service) in {@link #onDetachedFromHost()}.
- * {@link #onDetachedFromHost()} is called in two cases:
- * <ul>
- * <li> app manually change it using {@link #setHost(PlaybackGlueHost)} call</li>
- * <li> When host (fragment or activity) is destroyed </li>
- * </ul>
- * In rare case if an PlaybackGlue wants to live outside fragment / activity life cycle, it may
- * manages resource release by itself.
- *
- * @see PlaybackGlueHost
- */
-public abstract class PlaybackGlue {
-    private final Context mContext;
-    private PlaybackGlueHost mPlaybackGlueHost;
-
-    /**
-     * Interface to allow clients to take action once the video is ready to play and start stop.
-     */
-    public abstract static class PlayerCallback {
-        /**
-         * Event for {@link #isPrepared()} changed.
-         * @param glue The PlaybackGlue that has changed {@link #isPrepared()}.
-         */
-        public void onPreparedStateChanged(PlaybackGlue glue) {
-        }
-
-        /**
-         * 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.
-     */
-    public PlaybackGlue(Context context) {
-        this.mContext = context;
-    }
-
-    /**
-     * Returns the context.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * 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 true;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Starts the media player. Does nothing if {@link #isPrepared()} is false. To wait
-     * {@link #isPrepared()} to be true before playing, use {@link #playWhenPrepared()}.
-     */
-    public void play() {
-    }
-
-    /**
-     * Starts play when {@link #isPrepared()} becomes true.
-     */
-    public void playWhenPrepared() {
-        if (isPrepared()) {
-            play();
-        } else {
-            addPlayerCallback(new PlayerCallback() {
-                @Override
-                public void onPreparedStateChanged(PlaybackGlue glue) {
-                    if (glue.isPrepared()) {
-                        removePlayerCallback(this);
-                        play();
-                    }
-                }
-            });
-        }
-    }
-
-    /**
-     * Pauses the media player.
-     */
-    public void pause() {
-    }
-
-    /**
-     * Goes to the next media item. This method is optional.
-     */
-    public void next() {
-    }
-
-    /**
-     * Goes to the previous media item. This method is optional.
-     */
-    public void previous() {
-    }
-
-    /**
-     * This method is used to associate a PlaybackGlue with the {@link PlaybackGlueHost} which
-     * provides UI and optional {@link SurfaceHolderGlueHost}.
-     *
-     * @param host The host for the PlaybackGlue. Set to null to detach from the host.
-     */
-    public final void setHost(PlaybackGlueHost host) {
-        if (mPlaybackGlueHost == host) {
-            return;
-        }
-        if (mPlaybackGlueHost != null) {
-            mPlaybackGlueHost.attachToGlue(null);
-        }
-        mPlaybackGlueHost = host;
-        if (mPlaybackGlueHost != null) {
-            mPlaybackGlueHost.attachToGlue(this);
-        }
-    }
-
-    /**
-     * This method is called when {@link PlaybackGlueHost is started. Subclass may override.
-     */
-    protected void onHostStart() {
-    }
-
-    /**
-     * This method is called when {@link PlaybackGlueHost is stopped. Subclass may override.
-     */
-    protected void onHostStop() {
-    }
-
-    /**
-     * This method is called when {@link PlaybackGlueHost is resumed. Subclass may override.
-     */
-    protected void onHostResume() {
-    }
-
-    /**
-     * This method is called when {@link PlaybackGlueHost is paused. Subclass may override.
-     */
-    protected void onHostPause() {
-    }
-
-    /**
-     * This method is called attached to associated {@link PlaybackGlueHost}. Subclass may override
-     * and call super.onAttachedToHost().
-     */
-    @CallSuper
-    protected void onAttachedToHost(PlaybackGlueHost host) {
-        mPlaybackGlueHost = host;
-        mPlaybackGlueHost.setHostCallback(new PlaybackGlueHost.HostCallback() {
-            @Override
-            public void onHostStart() {
-                PlaybackGlue.this.onHostStart();
-            }
-
-            @Override
-            public void onHostStop() {
-                PlaybackGlue.this.onHostStop();
-            }
-
-            @Override
-            public void onHostResume() {
-                PlaybackGlue.this.onHostResume();
-            }
-
-            @Override
-            public void onHostPause() {
-                PlaybackGlue.this.onHostPause();
-            }
-
-            @Override
-            public void onHostDestroy() {
-                setHost(null);
-            }
-        });
-    }
-
-    /**
-     * This method is called when current associated {@link PlaybackGlueHost} is attached to a
-     * different {@link PlaybackGlue} or {@link PlaybackGlueHost} is destroyed . Subclass may
-     * override and call super.onDetachedFromHost() at last. A typical PlaybackGlue will release
-     * resources (e.g. MediaPlayer or connection to playback service) in this method.
-     */
-    @CallSuper
-    protected void onDetachedFromHost() {
-        if (mPlaybackGlueHost != null) {
-            mPlaybackGlueHost.setHostCallback(null);
-            mPlaybackGlueHost = null;
-        }
-    }
-
-    /**
-     * @return Associated {@link PlaybackGlueHost} or null if not attached to host.
-     */
-    public PlaybackGlueHost getHost() {
-        return mPlaybackGlueHost;
-    }
-}
diff --git a/android/support/v17/leanback/media/PlaybackGlueHost.java b/android/support/v17/leanback/media/PlaybackGlueHost.java
deleted file mode 100644
index 8985b5d..0000000
--- a/android/support/v17/leanback/media/PlaybackGlueHost.java
+++ /dev/null
@@ -1,232 +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.v17.leanback.media;
-
-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;
-
-/**
- * This class represents the UI (e.g. Fragment/Activity) hosting playback controls and
- * defines the interaction between {@link PlaybackGlue} and the host.
- * PlaybackGlueHost provides the following functions:
- * <li>Render UI of PlaybackGlue: {@link #setPlaybackRow(Row)},
- * {@link #setPlaybackRowPresenter(PlaybackRowPresenter)}.
- * </li>
- * <li>Client for fragment/activity onStart/onStop: {@link #setHostCallback(HostCallback)}.
- * </li>
- * <li>Auto fade out controls after a short period: {@link #setFadingEnabled(boolean)}.
- * </li>
- * <li>Key listener and ActionListener. {@link #setOnKeyInterceptListener(View.OnKeyListener)},
- * {@link #setOnActionClickedListener(OnActionClickedListener)}.
- * </li>
- *
- * 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;
-
-    /**
-     * Callbacks triggered by the host(e.g. fragment) hosting the video controls/surface.
-     *
-     * @see #setHostCallback(HostCallback)
-     */
-    public abstract static class HostCallback {
-        /**
-         * Client triggered once the host(fragment) has started.
-         */
-        public void onHostStart() {
-        }
-
-        /**
-         * Client triggered once the host(fragment) has stopped.
-         */
-        public void onHostStop() {
-        }
-
-        /**
-         * Client triggered once the host(fragment) has paused.
-         */
-        public void onHostPause() {
-        }
-
-        /**
-         * Client triggered once the host(fragment) has resumed.
-         */
-        public void onHostResume() {
-        }
-
-        /**
-         * 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) {
-    }
-
-    /**
-     * 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.
-     */
-    public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
-    }
-
-    /**
-     * Sets the {@link View.OnClickListener} on this fragment.
-     */
-    public void setOnActionClickedListener(OnActionClickedListener listener) {}
-
-    /**
-     * Sets the host {@link HostCallback} callback on the host. This method should only be called
-     * by {@link PlaybackGlue}. App should not directly call this method, app should override
-     * {@link PlaybackGlue#onHostStart()} etc.
-     */
-    public void setHostCallback(HostCallback callback) {
-    }
-
-    /**
-     * Notifies host about a change so it can update the view.
-     */
-    public void notifyPlaybackRowChanged() {}
-
-    /**
-     * Sets {@link PlaybackRowPresenter} for rendering the playback controls.
-     */
-    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {}
-
-    /**
-     * Sets the {@link Row} that represents the information on control items that needs
-     * to be rendered.
-     */
-    public void setPlaybackRow(Row row) {}
-
-    final void attachToGlue(PlaybackGlue glue) {
-        if (mGlue != null) {
-            mGlue.onDetachedFromHost();
-        }
-        mGlue = glue;
-        if (mGlue != null) {
-            mGlue.onAttachedToHost(this);
-        }
-    }
-
-    /**
-     * 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/android/support/v17/leanback/media/PlaybackTransportControlGlue.java b/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
deleted file mode 100644
index b81f979..0000000
--- a/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
+++ /dev/null
@@ -1,419 +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.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);
- *         PlaybackTransportControlGlue<MediaPlayerAdapter> playerGlue =
- *                 new PlaybackTransportControlGlue(getActivity(),
- *                         new MediaPlayerAdapter(getActivity()));
- *         playerGlue.setHost(new VideoFragmentGlueHost(this));
- *         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));
- *         playerGlue.playWhenPrepared();
- *     }
- * }
- * </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
-    protected void onUpdateProgress() {
-        if (!mPlaybackSeekUiClient.mIsSeek) {
-            super.onUpdateProgress();
-        }
-    }
-
-    @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 && 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/android/support/v17/leanback/media/PlayerAdapter.java b/android/support/v17/leanback/media/PlayerAdapter.java
deleted file mode 100644
index 983b4a9..0000000
--- a/android/support/v17/leanback/media/PlayerAdapter.java
+++ /dev/null
@@ -1,269 +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.v17.leanback.media;
-
-import static android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction.INDEX_OFF;
-import static android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction.INDEX_ON;
-import static android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction.INDEX_NONE;
-import static android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction.INDEX_ALL;
-import static android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction.INDEX_ONE;
-
-/**
- * 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) {
-        }
-
-        /**
-         * Event for meta data changed.
-         * @param adapter The adapter that finishes current media item.
-         */
-        public void onMetadataChanged(PlayerAdapter adapter) {
-        }
-    }
-
-    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();
-
-    /**
-     * Optional method. Override this method if {@link #getSupportedActions()} include
-     * {@link PlaybackBaseControlGlue#ACTION_SKIP_TO_NEXT} to skip
-     * to next item.
-     */
-    public void next() {
-    }
-
-    /**
-     * Optional method. Override this method if {@link #getSupportedActions()} include
-     * {@link PlaybackBaseControlGlue#ACTION_SKIP_TO_PREVIOUS} to skip
-     * to previous item.
-     */
-    public void previous() {
-    }
-
-    /**
-     * Optional method. Override this method if {@link #getSupportedActions()} include
-     * {@link PlaybackBaseControlGlue#ACTION_FAST_FORWARD} to fast
-     * forward current media item.
-     */
-    public void fastForward() {
-    }
-
-    /**
-     * Optional method. Override this method if {@link #getSupportedActions()} include
-     * {@link PlaybackBaseControlGlue#ACTION_REWIND} to rewind in
-     * current media item.
-     */
-    public void rewind() {
-    }
-
-    /**
-     * 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) {
-    }
-
-    /**
-     * Optional method. Override this method if {@link #getSupportedActions()} include
-     * {@link PlaybackBaseControlGlue#ACTION_SHUFFLE} to set the shuffle action.
-     *
-     * @param shuffleActionIndex The repeat action. Must be one of the followings:
-     *                           {@link android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction#INDEX_OFF}
-     *                           {@link android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction#INDEX_ON}
-     */
-    public void setShuffleAction(int shuffleActionIndex) {
-    }
-
-    /**
-     * Optional method. Override this method if {@link #getSupportedActions()} include
-     * {@link PlaybackBaseControlGlue#ACTION_REPEAT} to set the repeat action.
-     *
-     * @param repeatActionIndex The shuffle action. Must be one of the followings:
-     *                          {@link android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction#INDEX_ONE}
-     *                          {@link android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction#INDEX_ALL},
-     *                          {@link android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction#INDEX_NONE},
-     */
-    public void setRepeatAction(int repeatActionIndex) {
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Return xor combination of values defined in PlaybackBaseControlGlue.
-     * Default is PLAY_PAUSE (unless subclass enforce to be 0)
-     */
-    public long getSupportedActions() {
-        return PlaybackBaseControlGlue.ACTION_PLAY_PAUSE;
-    }
-
-    /**
-     * 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/android/support/v17/leanback/media/SurfaceHolderGlueHost.java b/android/support/v17/leanback/media/SurfaceHolderGlueHost.java
deleted file mode 100644
index 679a19a..0000000
--- a/android/support/v17/leanback/media/SurfaceHolderGlueHost.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.v17.leanback.media;
-
-import android.view.SurfaceHolder;
-
-/**
- * Optional interface to be implemented by any subclass of {@link PlaybackGlueHost} that contains
- * a {@link android.view.SurfaceView}. This will allow subclass of {@link PlaybackGlue} to setup
- * the surface holder callback during {@link PlaybackGlue#setHost(PlaybackGlueHost)}.
- *
- * @see PlaybackGlue#setHost(PlaybackGlueHost)
- */
-public interface SurfaceHolderGlueHost {
-    /**
-     * Sets the {@link SurfaceHolder.Callback} on the the host.
-     */
-    void setSurfaceHolderCallback(SurfaceHolder.Callback callback);
-}
diff --git a/android/support/v17/leanback/package-info.java b/android/support/v17/leanback/package-info.java
deleted file mode 100644
index 80b26e9..0000000
--- a/android/support/v17/leanback/package-info.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.
- */
-
-/**
- * <p>Support classes for building Leanback user experiences.</p>
- * <p>
- * Many apps intended for a 10-foot, or 'Leanback', experience are centered around media and games.
- * Games tend to have custom user interfaces, but media applications may benefit from a common set of
- * user interface components that work well in a Leanback environment.  Following is an overview of
- * the Leanback Support Library.
- * </p>
- * <p>
- * Leanback provides a model-view-presenter approach to building applications:
- * <ul>
- * <li>The model is primarily provided by the application developer. Leanback imposes very few
- * restrictions on how this model is implemented: anything extending Object in Java is
- * supported.
- * </li>
- * <li>The view is handled by the existing {@link android.view} package. Developers
- * may continue to use their existing knowledge and experience to create visually compelling
- * applications with Leanback.
- * </li>
- * <li>The presenter is based on the existing Adapter concept in the Android framework, but has
- * been updated to add more flexibility and composability. In particular, the interface for
- * binding data to views has been separated from the adapter that traverses the data, allowing
- * presenters to be used in more places.  See {@link android.support.v17.leanback.widget.Presenter}
- * for more details.
- * </li>
- * </ul>
- * <p>
- * Leanback contains a mixture of higher level building blocks such as Fragments in the
- * {@link android.support.v17.leanback.app} package. Notable examples are the
- * {@link android.support.v17.leanback.app.BrowseSupportFragment},
- * {@link android.support.v17.leanback.app.DetailsSupportFragment},
- * {@link android.support.v17.leanback.app.PlaybackSupportFragment} and the
- * {@link android.support.v17.leanback.app.GuidedStepSupportFragment}.  Helper classes are also
- * provided that work with the leanback fragments, for example the
- * {@link android.support.v17.leanback.media.PlaybackTransportControlGlue} and
- * {@link android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost}.
- * </p>
- * <p>
- * Many lower level building blocks are also provided in the {@link android.support.v17.leanback.widget} package.
- * These allow applications to easily incorporate Leanback look and feel while allowing for a
- * high degree of customization.  Primary examples include the UI widget
- * {@link android.support.v17.leanback.widget.HorizontalGridView} and
- * {@link android.support.v17.leanback.widget.VerticalGridView}.
- */
-
-package android.support.v17.leanback;
\ No newline at end of file
diff --git a/android/support/v17/leanback/system/Settings.java b/android/support/v17/leanback/system/Settings.java
deleted file mode 100644
index 2155358..0000000
--- a/android/support/v17/leanback/system/Settings.java
+++ /dev/null
@@ -1,183 +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.v17.leanback.system;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.widget.ShadowOverlayContainer;
-import android.util.Log;
-
-/**
- * Provides various preferences affecting Leanback runtime behavior.
- * <p>Note this class is not thread safe and its methods should only
- * be invoked from the UI thread
- * </p>
- */
-public class Settings {
-    static private final String TAG = "Settings";
-    static private final boolean DEBUG = false;
-
-    // The intent action that must be provided by a broadcast receiver
-    // in a customization package.
-    private static final String ACTION_PARTNER_CUSTOMIZATION =
-            "android.support.v17.leanback.action.PARTNER_CUSTOMIZATION";
-
-    public static final String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
-
-    public static final String OUTLINE_CLIPPING_DISABLED = "OUTLINE_CLIPPING_DISABLED";
-
-    static private Settings sInstance;
-
-    private boolean mPreferStaticShadows;
-    private boolean mOutlineClippingDisabled;
-
-    /**
-     * Returns the singleton Settings instance.
-     */
-    static public Settings getInstance(Context context) {
-        if (sInstance == null) {
-            sInstance = new Settings(context);
-        }
-        return sInstance;
-    }
-
-    private Settings(Context context) {
-        if (DEBUG) Log.v(TAG, "generating preferences");
-        Customizations customizations = getCustomizations(context);
-        generateSetting(customizations);
-    }
-
-    /**
-     * Returns true if static shadows are recommended.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean preferStaticShadows() {
-        return mPreferStaticShadows;
-    }
-
-    /**
-     * Returns true if view outline is disabled on low power chipset.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isOutlineClippingDisabled() {
-        return mOutlineClippingDisabled;
-    }
-
-    /**
-     * Returns the boolean preference for the given key.
-     */
-    public boolean getBoolean(String key) {
-        return getOrSetBoolean(key, false, false);
-    }
-
-    /**
-     * Sets the boolean preference for the given key.  If an app uses this api to override
-     * a default preference, it must do so on every activity create.
-     */
-    public void setBoolean(String key, boolean value) {
-        getOrSetBoolean(key, true, value);
-    }
-
-    boolean getOrSetBoolean(String key, boolean set, boolean value) {
-        if (key.compareTo(PREFER_STATIC_SHADOWS) == 0) {
-            return set ? (mPreferStaticShadows = value) : mPreferStaticShadows;
-        } else if (key.compareTo(OUTLINE_CLIPPING_DISABLED) == 0) {
-            return set ? (mOutlineClippingDisabled = value) : mOutlineClippingDisabled;
-        }
-        throw new IllegalArgumentException("Invalid key");
-    }
-
-    private void generateSetting(Customizations customizations) {
-        if (ShadowOverlayContainer.supportsDynamicShadow()) {
-            mPreferStaticShadows = false;
-            if (customizations != null) {
-                mPreferStaticShadows = customizations.getBoolean(
-                        "leanback_prefer_static_shadows", mPreferStaticShadows);
-            }
-        } else {
-            mPreferStaticShadows = true;
-        }
-
-        if (Build.VERSION.SDK_INT >= 21) {
-            mOutlineClippingDisabled = false;
-            if (customizations != null) {
-                mOutlineClippingDisabled = customizations.getBoolean(
-                        "leanback_outline_clipping_disabled", mOutlineClippingDisabled);
-            }
-        } else {
-            mOutlineClippingDisabled = true;
-        }
-        if (DEBUG) Log.v(TAG, "generated preference " + PREFER_STATIC_SHADOWS + ": "
-                + mPreferStaticShadows + " "
-                + OUTLINE_CLIPPING_DISABLED + " : " + mOutlineClippingDisabled);
-    }
-
-    static class Customizations {
-        Resources mResources;
-        String mPackageName;
-
-        public Customizations(Resources resources, String packageName) {
-            mResources = resources;
-            mPackageName = packageName;
-        }
-
-        public boolean getBoolean(String resourceName, boolean defaultValue) {
-            int resId = mResources.getIdentifier(resourceName, "bool", mPackageName);
-            return resId > 0 ? mResources.getBoolean(resId) : defaultValue;
-        }
-    };
-
-    private Customizations getCustomizations(Context context) {
-        final PackageManager pm = context.getPackageManager();
-        final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
-        if (DEBUG) {
-            Log.v(TAG, "getting oem customizations by intent: " + ACTION_PARTNER_CUSTOMIZATION);
-        }
-
-        Resources resources = null;
-        String packageName = null;
-        for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
-            packageName = info.activityInfo.packageName;
-            if (DEBUG) Log.v(TAG, "got package " + packageName);
-            if (packageName != null && isSystemApp(info)) try {
-                resources = pm.getResourcesForApplication(packageName);
-            } catch (PackageManager.NameNotFoundException ex) {
-                // Do nothing
-            }
-            if (resources != null) {
-                if (DEBUG) Log.v(TAG, "found customization package: " + packageName);
-                break;
-            }
-        }
-        return resources == null ? null : new Customizations(resources, packageName);
-    }
-
-    private static boolean isSystemApp(ResolveInfo info) {
-        return (info.activityInfo != null
-                && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
-    }
-}
diff --git a/android/support/v17/leanback/transition/FadeAndShortSlide.java b/android/support/v17/leanback/transition/FadeAndShortSlide.java
deleted file mode 100644
index 0e135a9..0000000
--- a/android/support/v17/leanback/transition/FadeAndShortSlide.java
+++ /dev/null
@@ -1,321 +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.v17.leanback.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.transition.Fade;
-import android.transition.Transition;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * Execute horizontal slide of 1/4 width and fade (to workaround bug 23718734)
- * @hide
- */
-@RequiresApi(21)
-@RestrictTo(LIBRARY_GROUP)
-public class FadeAndShortSlide extends Visibility {
-
-    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
-    // private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
-    private static final String PROPNAME_SCREEN_POSITION =
-            "android:fadeAndShortSlideTransition:screenPosition";
-
-    private CalculateSlide mSlideCalculator;
-    private Visibility mFade = new Fade();
-    private float mDistance = -1;
-
-    private static abstract class CalculateSlide {
-
-        CalculateSlide() {
-        }
-
-        /** Returns the translation X value for view when it goes out of the scene */
-        float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            return view.getTranslationX();
-        }
-
-        /** Returns the translation Y value for view when it goes out of the scene */
-        float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            return view.getTranslationY();
-        }
-    }
-
-    float getHorizontalDistance(ViewGroup sceneRoot) {
-        return mDistance >= 0 ? mDistance : (sceneRoot.getWidth() / 4);
-    }
-
-    float getVerticalDistance(ViewGroup sceneRoot) {
-        return mDistance >= 0 ? mDistance : (sceneRoot.getHeight() / 4);
-    }
-
-    final static CalculateSlide sCalculateStart = new CalculateSlide() {
-        @Override
-        public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-            final float x;
-            if (isRtl) {
-                x = view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
-            } else {
-                x = view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
-            }
-            return x;
-        }
-    };
-
-    final static CalculateSlide sCalculateEnd = new CalculateSlide() {
-        @Override
-        public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-            final float x;
-            if (isRtl) {
-                x = view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
-            } else {
-                x = view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
-            }
-            return x;
-        }
-    };
-
-    final static CalculateSlide sCalculateStartEnd = new CalculateSlide() {
-        @Override
-        public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            final int viewCenter = position[0] + view.getWidth() / 2;
-            sceneRoot.getLocationOnScreen(position);
-            Rect center = t.getEpicenter();
-            final int sceneRootCenter = center == null ? (position[0] + sceneRoot.getWidth() / 2)
-                    : center.centerX();
-            if (viewCenter < sceneRootCenter) {
-                return view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
-            } else {
-                return view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
-            }
-        }
-    };
-
-    final static CalculateSlide sCalculateBottom = new CalculateSlide() {
-        @Override
-        public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            return view.getTranslationY() + t.getVerticalDistance(sceneRoot);
-        }
-    };
-
-    final static CalculateSlide sCalculateTop = new CalculateSlide() {
-        @Override
-        public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            return view.getTranslationY() - t.getVerticalDistance(sceneRoot);
-        }
-    };
-
-    final CalculateSlide sCalculateTopBottom = new CalculateSlide() {
-        @Override
-        public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
-            final int viewCenter = position[1] + view.getHeight() / 2;
-            sceneRoot.getLocationOnScreen(position);
-            Rect center = getEpicenter();
-            final int sceneRootCenter = center == null ? (position[1] + sceneRoot.getHeight() / 2)
-                    : center.centerY();
-            if (viewCenter < sceneRootCenter) {
-                return view.getTranslationY() - t.getVerticalDistance(sceneRoot);
-            } else {
-                return view.getTranslationY() + t.getVerticalDistance(sceneRoot);
-            }
-        }
-    };
-
-    public FadeAndShortSlide() {
-        this(Gravity.START);
-    }
-
-    public FadeAndShortSlide(int slideEdge) {
-        setSlideEdge(slideEdge);
-    }
-
-    public FadeAndShortSlide(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSlide);
-        int edge = a.getInt(R.styleable.lbSlide_lb_slideEdge, Gravity.START);
-        setSlideEdge(edge);
-        a.recycle();
-    }
-
-    @Override
-    public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
-        mFade.setEpicenterCallback(epicenterCallback);
-        super.setEpicenterCallback(epicenterCallback);
-    }
-
-    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(TransitionValues transitionValues) {
-        mFade.captureStartValues(transitionValues);
-        super.captureStartValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        mFade.captureEndValues(transitionValues);
-        super.captureEndValues(transitionValues);
-        captureValues(transitionValues);
-    }
-
-    public void setSlideEdge(int slideEdge) {
-        switch (slideEdge) {
-            case Gravity.START:
-                mSlideCalculator = sCalculateStart;
-                break;
-            case Gravity.END:
-                mSlideCalculator = sCalculateEnd;
-                break;
-            case Gravity.START | Gravity.END:
-                mSlideCalculator = sCalculateStartEnd;
-                break;
-            case Gravity.TOP:
-                mSlideCalculator = sCalculateTop;
-                break;
-            case Gravity.BOTTOM:
-                mSlideCalculator = sCalculateBottom;
-                break;
-            case Gravity.TOP | Gravity.BOTTOM:
-                mSlideCalculator = sCalculateTopBottom;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid slide direction");
-        }
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
-            TransitionValues endValues) {
-        if (endValues == null) {
-            return null;
-        }
-        if (sceneRoot == view) {
-            // workaround b/25375640, avoid run animation on sceneRoot
-            return null;
-        }
-        int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
-        int left = position[0];
-        int top = position[1];
-        float endX = view.getTranslationX();
-        float startX = mSlideCalculator.getGoneX(this, sceneRoot, view, position);
-        float endY = view.getTranslationY();
-        float startY = mSlideCalculator.getGoneY(this, sceneRoot, view, position);
-        final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, endValues,
-                left, top, startX, startY, endX, endY, sDecelerate, this);
-        final Animator fadeAnimator = mFade.onAppear(sceneRoot, view, startValues, endValues);
-        if (slideAnimator == null) {
-            return fadeAnimator;
-        } else if (fadeAnimator == null) {
-            return slideAnimator;
-        }
-        final AnimatorSet set = new AnimatorSet();
-        set.play(slideAnimator).with(fadeAnimator);
-
-        return set;
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
-            TransitionValues endValues) {
-        if (startValues == null) {
-            return null;
-        }
-        if (sceneRoot == view) {
-            // workaround b/25375640, avoid run animation on sceneRoot
-            return null;
-        }
-        int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
-        int left = position[0];
-        int top = position[1];
-        float startX = view.getTranslationX();
-        float endX = mSlideCalculator.getGoneX(this, sceneRoot, view, position);
-        float startY = view.getTranslationY();
-        float endY = mSlideCalculator.getGoneY(this, sceneRoot, view, position);
-        final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view,
-                startValues, left, top, startX, startY, endX, endY, sDecelerate /* sAccelerate */,
-                this);
-        final Animator fadeAnimator = mFade.onDisappear(sceneRoot, view, startValues, endValues);
-        if (slideAnimator == null) {
-            return fadeAnimator;
-        } else if (fadeAnimator == null) {
-            return slideAnimator;
-        }
-        final AnimatorSet set = new AnimatorSet();
-        set.play(slideAnimator).with(fadeAnimator);
-
-        return set;
-    }
-
-    @Override
-    public Transition addListener(TransitionListener listener) {
-        mFade.addListener(listener);
-        return super.addListener(listener);
-    }
-
-    @Override
-    public Transition removeListener(TransitionListener listener) {
-        mFade.removeListener(listener);
-        return super.removeListener(listener);
-    }
-
-    /**
-     * Returns distance to slide.  When negative value is returned, it will use 1/4 of
-     * sceneRoot dimension.
-     */
-    public float getDistance() {
-        return mDistance;
-    }
-
-    /**
-     * Set distance to slide, default value is -1.  when negative value is set, it will use 1/4 of
-     * sceneRoot dimension.
-     * @param distance Pixels to slide.
-     */
-    public void setDistance(float distance) {
-        mDistance = distance;
-    }
-
-    @Override
-    public Transition clone() {
-        FadeAndShortSlide clone = null;
-        clone = (FadeAndShortSlide) super.clone();
-        clone.mFade = (Visibility) mFade.clone();
-        return clone;
-    }
-}
diff --git a/android/support/v17/leanback/transition/LeanbackTransitionHelper.java b/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
deleted file mode 100644
index f97d64e..0000000
--- a/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
+++ /dev/null
@@ -1,90 +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.v17.leanback.transition;
-
-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.support.v17.leanback.R;
-
-/**
- * Helper class to load Leanback specific transition.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class LeanbackTransitionHelper {
-
-    interface LeanbackTransitionHelperVersion {
-        Object loadTitleInTransition(Context context);
-        Object loadTitleOutTransition(Context context);
-    }
-
-    /*
-     * 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
-        public Object loadTitleInTransition(Context context) {
-            return LeanbackTransitionHelperKitKat.loadTitleInTransition(context);
-        }
-
-        @Override
-        public Object loadTitleOutTransition(Context context) {
-            return LeanbackTransitionHelperKitKat.loadTitleOutTransition(context);
-        }
-    }
-
-    /*
-     * Load transition from resource or just return stub for API17.
-     */
-    static class LeanbackTransitionHelperDefault implements LeanbackTransitionHelperVersion {
-
-        @Override
-        public Object loadTitleInTransition(Context context) {
-            return TransitionHelper.loadTransition(context, R.transition.lb_title_in);
-        }
-
-        @Override
-        public Object loadTitleOutTransition(Context context) {
-            return TransitionHelper.loadTransition(context, R.transition.lb_title_out);
-        }
-    }
-
-    static LeanbackTransitionHelperVersion sImpl;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            sImpl = new LeanbackTransitionHelperDefault();
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            sImpl = new LeanbackTransitionHelperKitKatImpl();
-        } else {
-            // Helper will create a stub object for transition in this case.
-            sImpl = new LeanbackTransitionHelperDefault();
-        }
-    }
-
-    static public Object loadTitleInTransition(Context context) {
-        return sImpl.loadTitleInTransition(context);
-    }
-
-    static public Object loadTitleOutTransition(Context context) {
-        return sImpl.loadTitleOutTransition(context);
-    }
-}
diff --git a/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java b/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
deleted file mode 100644
index bb30b00..0000000
--- a/android/support/v17/leanback/transition/LeanbackTransitionHelperKitKat.java
+++ /dev/null
@@ -1,43 +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.v17.leanback.transition;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.support.v17.leanback.R;
-import android.view.Gravity;
-import android.view.animation.AnimationUtils;
-
-@RequiresApi(19)
-class LeanbackTransitionHelperKitKat {
-
-    static public Object loadTitleInTransition(Context context) {
-        SlideKitkat slide = new SlideKitkat();
-        slide.setSlideEdge(Gravity.TOP);
-        slide.setInterpolator(AnimationUtils.loadInterpolator(context,
-                android.R.anim.decelerate_interpolator));
-        slide.addTarget(R.id.browse_title_group);
-        return slide;
-    }
-
-    static public Object loadTitleOutTransition(Context context) {
-        SlideKitkat slide = new SlideKitkat();
-        slide.setSlideEdge(Gravity.TOP);
-        slide.setInterpolator(AnimationUtils.loadInterpolator(context,
-                R.anim.lb_decelerator_4));
-        slide.addTarget(R.id.browse_title_group);
-        return slide;
-    }
-
-}
diff --git a/android/support/v17/leanback/transition/ParallaxTransition.java b/android/support/v17/leanback/transition/ParallaxTransition.java
deleted file mode 100644
index d8ee734..0000000
--- a/android/support/v17/leanback/transition/ParallaxTransition.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.v17.leanback.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.Parallax;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-/**
- * A slide transition changes TRANSLATION attribute of view which is not exposed by any event.
- * In order to run a parallax effect with Slide transition, ParallaxTransition is running on the
- * side, calling ParallaxSource.updateValues() on every frame. User should make sure slide
- * and ParallaxTransition are using same duration and startDelay.
- *
- * @hide
- */
-@RequiresApi(21)
-@RestrictTo(LIBRARY_GROUP)
-public class ParallaxTransition extends Visibility {
-
-    static Interpolator sInterpolator = new LinearInterpolator();
-
-    public ParallaxTransition() {
-    }
-
-    public ParallaxTransition(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    Animator createAnimator(View view) {
-        final Parallax source = (Parallax) view.getTag(R.id.lb_parallax_source);
-        if (source == null) {
-            return null;
-        }
-        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
-        animator.setInterpolator(sInterpolator);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                source.updateValues();
-            }
-        });
-        return animator;
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot, View view,
-                             TransitionValues startValues, TransitionValues endValues) {
-        if (endValues == null) {
-            return null;
-        }
-        return createAnimator(view);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot, View view,
-                                TransitionValues startValues, TransitionValues endValues) {
-        if (startValues == null) {
-            return null;
-        }
-        return createAnimator(view);
-    }
-}
diff --git a/android/support/v17/leanback/transition/Scale.java b/android/support/v17/leanback/transition/Scale.java
deleted file mode 100644
index 2fe4c9a..0000000
--- a/android/support/v17/leanback/transition/Scale.java
+++ /dev/null
@@ -1,73 +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.v17.leanback.transition;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-import android.transition.Transition;
-import android.transition.TransitionValues;
-
-@RequiresApi(19)
-class Scale extends Transition {
-    private static final String PROPNAME_SCALE = "android:leanback:scale";
-
-    public Scale() {
-    }
-
-    private void captureValues(TransitionValues values) {
-        View view = values.view;
-        values.values.put(PROPNAME_SCALE, view.getScaleX());
-    }
-
-    @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;
-        }
-
-        final float startScale = (Float) startValues.values.get(PROPNAME_SCALE);
-        final float endScale = (Float) endValues.values.get(PROPNAME_SCALE);
-
-        final View view = startValues.view;
-        view.setScaleX(startScale);
-        view.setScaleY(startScale);
-
-        ValueAnimator animator = ValueAnimator.ofFloat(startScale, endScale);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float scale = (Float) animation.getAnimatedValue();
-                view.setScaleX(scale);
-                view.setScaleY(scale);
-            }
-        });
-        return animator;
-    }
-}
diff --git a/android/support/v17/leanback/transition/SlideKitkat.java b/android/support/v17/leanback/transition/SlideKitkat.java
deleted file mode 100644
index 2d74958..0000000
--- a/android/support/v17/leanback/transition/SlideKitkat.java
+++ /dev/null
@@ -1,307 +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.v17.leanback.transition;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RequiresApi;
-import android.support.v17.leanback.R;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * Slide distance toward/from a edge.
- * This is a limited Slide implementation for KitKat without propagation support.
- */
-@RequiresApi(19)
-class SlideKitkat extends Visibility {
-    private static final String TAG = "SlideKitkat";
-
-    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
-    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
-
-    private int mSlideEdge;
-    private CalculateSlide mSlideCalculator;
-
-    private interface CalculateSlide {
-        /** Returns the translation value for view when it out of the scene */
-        float getGone(View view);
-
-        /** Returns the translation value for view when it is in the scene */
-        float getHere(View view);
-
-        /** Returns the property to animate translation */
-        Property<View, Float> getProperty();
-    }
-
-    private static abstract class CalculateSlideHorizontal implements CalculateSlide {
-        CalculateSlideHorizontal() {
-        }
-
-        @Override
-        public float getHere(View view) {
-            return view.getTranslationX();
-        }
-
-        @Override
-        public Property<View, Float> getProperty() {
-            return View.TRANSLATION_X;
-        }
-    }
-
-    private static abstract class CalculateSlideVertical implements CalculateSlide {
-        CalculateSlideVertical() {
-        }
-
-        @Override
-        public float getHere(View view) {
-            return view.getTranslationY();
-        }
-
-        @Override
-        public Property<View, Float> getProperty() {
-            return View.TRANSLATION_Y;
-        }
-    }
-
-    private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
-        @Override
-        public float getGone(View view) {
-            return view.getTranslationX() - view.getWidth();
-        }
-    };
-
-    private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
-        @Override
-        public float getGone(View view) {
-            return view.getTranslationY() - view.getHeight();
-        }
-    };
-
-    private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
-        @Override
-        public float getGone(View view) {
-            return view.getTranslationX() + view.getWidth();
-        }
-    };
-
-    private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
-        @Override
-        public float getGone(View view) {
-            return view.getTranslationY() + view.getHeight();
-        }
-    };
-
-    private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
-        @Override
-        public float getGone(View view) {
-            if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-                return view.getTranslationX() + view.getWidth();
-            } else {
-                return view.getTranslationX() - view.getWidth();
-            }
-        }
-    };
-
-    private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
-        @Override
-        public float getGone(View view) {
-            if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-                return view.getTranslationX() - view.getWidth();
-            } else {
-                return view.getTranslationX() + view.getWidth();
-            }
-        }
-    };
-
-    public SlideKitkat() {
-        setSlideEdge(Gravity.BOTTOM);
-    }
-
-    public SlideKitkat(Context context, AttributeSet attrs) {
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSlide);
-        int edge = a.getInt(R.styleable.lbSlide_lb_slideEdge, Gravity.BOTTOM);
-        setSlideEdge(edge);
-        long duration = a.getInt(R.styleable.lbSlide_android_duration, -1);
-        if (duration >= 0) {
-            setDuration(duration);
-        }
-        long startDelay = a.getInt(R.styleable.lbSlide_android_startDelay, -1);
-        if (startDelay > 0) {
-            setStartDelay(startDelay);
-        }
-        final int resID = a.getResourceId(R.styleable.lbSlide_android_interpolator, 0);
-        if (resID > 0) {
-            setInterpolator(AnimationUtils.loadInterpolator(context, resID));
-        }
-        a.recycle();
-    }
-
-    /**
-     * 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(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;
-    }
-
-    /**
-     * 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}.
-     */
-    public int getSlideEdge() {
-        return mSlideEdge;
-    }
-
-    private Animator createAnimation(final View view, Property<View, Float> property,
-            float start, float end, float terminalValue, TimeInterpolator interpolator,
-            int finalVisibility) {
-        float[] startPosition = (float[]) view.getTag(R.id.lb_slide_transition_value);
-        if (startPosition != null) {
-            start = View.TRANSLATION_Y == property ? startPosition[1] : startPosition[0];
-            view.setTag(R.id.lb_slide_transition_value, null);
-        }
-        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);
-
-        SlideAnimatorListener listener = new SlideAnimatorListener(view, property, terminalValue, end,
-                finalVisibility);
-        anim.addListener(listener);
-        anim.addPauseListener(listener);
-        anim.setInterpolator(interpolator);
-        return anim;
-    }
-
-    @Override
-    public Animator onAppear(ViewGroup sceneRoot,
-            TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        View view = (endValues != null) ? endValues.view : null;
-        if (view == null) {
-            return null;
-        }
-        float end = mSlideCalculator.getHere(view);
-        float start = mSlideCalculator.getGone(view);
-        return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate,
-                View.VISIBLE);
-    }
-
-    @Override
-    public Animator onDisappear(ViewGroup sceneRoot,
-            TransitionValues startValues, int startVisibility,
-            TransitionValues endValues, int endVisibility) {
-        View view = (startValues != null) ? startValues.view : null;
-        if (view == null) {
-            return null;
-        }
-        float start = mSlideCalculator.getHere(view);
-        float end = mSlideCalculator.getGone(view);
-
-        return createAnimation(view, mSlideCalculator.getProperty(), start, end, start,
-                sAccelerate, View.INVISIBLE);
-    }
-
-    private static class SlideAnimatorListener extends AnimatorListenerAdapter {
-        private boolean mCanceled = false;
-        private float mPausedValue;
-        private final View mView;
-        private final float mEndValue;
-        private final float mTerminalValue;
-        private final int mFinalVisibility;
-        private final Property<View, Float> mProp;
-
-        public SlideAnimatorListener(View view, Property<View, Float> prop,
-                float terminalValue, float endValue, int finalVisibility) {
-            mProp = prop;
-            mView = view;
-            mTerminalValue = terminalValue;
-            mEndValue = endValue;
-            mFinalVisibility = finalVisibility;
-            view.setVisibility(View.VISIBLE);
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            float[] transitionPosition = new float[2];
-            transitionPosition[0] = mView.getTranslationX();
-            transitionPosition[1] = mView.getTranslationY();
-            mView.setTag(R.id.lb_slide_transition_value, transitionPosition);
-            mProp.set(mView, mTerminalValue);
-            mCanceled = true;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            if (!mCanceled) {
-                mProp.set(mView, mTerminalValue);
-            }
-            mView.setVisibility(mFinalVisibility);
-        }
-
-        @Override
-        public void onAnimationPause(Animator animator) {
-            mPausedValue = mProp.get(mView);
-            mProp.set(mView, mEndValue);
-            mView.setVisibility(mFinalVisibility);
-        }
-
-        @Override
-        public void onAnimationResume(Animator animator) {
-            mProp.set(mView, mPausedValue);
-            mView.setVisibility(View.VISIBLE);
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/transition/SlideNoPropagation.java b/android/support/v17/leanback/transition/SlideNoPropagation.java
deleted file mode 100644
index 46c67ac..0000000
--- a/android/support/v17/leanback/transition/SlideNoPropagation.java
+++ /dev/null
@@ -1,47 +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.v17.leanback.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.transition.Slide;
-import android.util.AttributeSet;
-
-/**
- * @hide
- */
-@RequiresApi(21)
-@RestrictTo(LIBRARY_GROUP)
-public class SlideNoPropagation extends Slide {
-
-    public SlideNoPropagation() {
-    }
-
-    public SlideNoPropagation(int slideEdge) {
-        super(slideEdge);
-    }
-
-    public SlideNoPropagation(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void setSlideEdge(int slideEdge) {
-        super.setSlideEdge(slideEdge);
-        setPropagation(null);
-    }
-}
diff --git a/android/support/v17/leanback/transition/TransitionEpicenterCallback.java b/android/support/v17/leanback/transition/TransitionEpicenterCallback.java
deleted file mode 100644
index bb8e686..0000000
--- a/android/support/v17/leanback/transition/TransitionEpicenterCallback.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.v17.leanback.transition;
-
-import android.graphics.Rect;
-import android.support.annotation.RestrictTo;
-
-/**
- * Class to get the epicenter of Transition.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public abstract class TransitionEpicenterCallback {
-
-    /**
-     * Implementers must override to return the epicenter of the Transition in screen
-     * coordinates.
-     *
-     * @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(Object transition);
-}
diff --git a/android/support/v17/leanback/transition/TransitionHelper.java b/android/support/v17/leanback/transition/TransitionHelper.java
deleted file mode 100644
index de8b374..0000000
--- a/android/support/v17/leanback/transition/TransitionHelper.java
+++ /dev/null
@@ -1,943 +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.v17.leanback.transition;
-
-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.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-
-import java.util.ArrayList;
-
-/**
- * Helper for view transitions.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class TransitionHelper {
-
-    public static final int FADE_IN = 0x1;
-    public static final int FADE_OUT = 0x2;
-
-    public static final int SLIDE_LEFT = Gravity.LEFT;
-    public static final int SLIDE_TOP = Gravity.TOP;
-    public static final int SLIDE_RIGHT = Gravity.RIGHT;
-    public static final int SLIDE_BOTTOM = Gravity.BOTTOM;
-
-    private static TransitionHelperVersionImpl sImpl;
-
-    /**
-     * Gets whether the system supports Transition animations.
-     *
-     * @return True if Transition animations are supported.
-     */
-    public static boolean systemSupportsTransitions() {
-        // Supported on Android 4.4 or later.
-        return Build.VERSION.SDK_INT >= 19;
-    }
-
-    /**
-     * Returns true if system supports entrance Transition animations.
-     */
-    public static boolean systemSupportsEntranceTransitions() {
-        return Build.VERSION.SDK_INT >= 21;
-    }
-
-    /**
-     * Interface implemented by classes that support Transition animations.
-     */
-    interface TransitionHelperVersionImpl {
-
-        void setEnterTransition(android.app.Fragment fragment, Object transition);
-
-        void setExitTransition(android.app.Fragment fragment, Object transition);
-
-        void setSharedElementEnterTransition(android.app.Fragment fragment,
-                Object transition);
-
-        void addSharedElement(android.app.FragmentTransaction ft,
-                View view, String transitionName);
-
-        Object getSharedElementEnterTransition(Window window);
-
-        void setSharedElementEnterTransition(Window window, Object transition);
-
-        Object getSharedElementReturnTransition(Window window);
-
-        void setSharedElementReturnTransition(Window window, Object transition);
-
-        Object getSharedElementExitTransition(Window window);
-
-        Object getSharedElementReenterTransition(Window window);
-
-        Object getEnterTransition(Window window);
-
-        void setEnterTransition(Window window, Object transition);
-
-        Object getReturnTransition(Window window);
-
-        void setReturnTransition(Window window, Object transition);
-
-        Object getExitTransition(Window window);
-
-        Object getReenterTransition(Window window);
-
-        Object createScene(ViewGroup sceneRoot, Runnable r);
-
-        Object createAutoTransition();
-
-        Object createSlide(int slideEdge);
-
-        Object createScale();
-
-        Object createFadeTransition(int fadingMode);
-
-        Object createChangeTransform();
-
-        Object createChangeBounds(boolean reparent);
-
-        Object createFadeAndShortSlide(int edge);
-
-        Object createFadeAndShortSlide(int edge, float distance);
-
-        void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay);
-
-        void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay);
-
-        void setChangeBoundsStartDelay(Object changeBounds, String className,
-                int startDelay);
-
-        void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay);
-
-        Object createTransitionSet(boolean sequential);
-
-        void addTransition(Object transitionSet, Object transition);
-
-        void addTransitionListener(Object transition, TransitionListener listener);
-
-        void removeTransitionListener(Object transition, TransitionListener listener);
-
-        void runTransition(Object scene, Object transition);
-
-        void exclude(Object transition, int targetId, boolean exclude);
-
-        void exclude(Object transition, View targetView, boolean exclude);
-
-        void excludeChildren(Object transition, int targetId, boolean exclude);
-
-        void excludeChildren(Object transition, View target, boolean exclude);
-
-        void include(Object transition, int targetId);
-
-        void include(Object transition, View targetView);
-
-        void setStartDelay(Object transition, long startDelay);
-
-        void setDuration(Object transition, long duration);
-
-        void setInterpolator(Object transition, Object timeInterpolator);
-
-        void addTarget(Object transition, View view);
-
-        Object createDefaultInterpolator(Context context);
-
-        Object loadTransition(Context context, int resId);
-
-        void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject);
-
-        void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup);
-
-        void setEpicenterCallback(Object transitionObject,
-                TransitionEpicenterCallback callback);
-    }
-
-    /**
-     * Interface used when we do not support Transition animations.
-     */
-    static class TransitionHelperStubImpl implements TransitionHelperVersionImpl {
-
-        private static class TransitionStub {
-            ArrayList<TransitionListener> mTransitionListeners;
-
-            TransitionStub() {
-            }
-        }
-
-        @Override
-        public void setEnterTransition(android.app.Fragment fragment, Object transition) {
-        }
-
-        @Override
-        public void setExitTransition(android.app.Fragment fragment, Object transition) {
-        }
-
-        @Override
-        public void setSharedElementEnterTransition(android.app.Fragment fragment,
-                Object transition) {
-        }
-
-        @Override
-        public void addSharedElement(android.app.FragmentTransaction ft,
-                View view, String transitionName) {
-        }
-
-        @Override
-        public Object getSharedElementEnterTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public void setSharedElementEnterTransition(Window window, Object object) {
-        }
-
-        @Override
-        public Object getSharedElementReturnTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public void setSharedElementReturnTransition(Window window, Object transition) {
-        }
-
-        @Override
-        public Object getSharedElementExitTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public Object getSharedElementReenterTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public Object getEnterTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public void setEnterTransition(Window window, Object transition) {
-        }
-
-        @Override
-        public Object getReturnTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public void setReturnTransition(Window window, Object transition) {
-        }
-
-        @Override
-        public Object getExitTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public Object getReenterTransition(Window window) {
-            return null;
-        }
-
-        @Override
-        public Object createScene(ViewGroup sceneRoot, Runnable r) {
-            return r;
-        }
-
-        @Override
-        public Object createAutoTransition() {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createFadeTransition(int fadingMode) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createChangeBounds(boolean reparent) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createChangeTransform() {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createFadeAndShortSlide(int edge) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createFadeAndShortSlide(int edge, float distance) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createSlide(int slideEdge) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public Object createScale() {
-            return new TransitionStub();
-        }
-
-        @Override
-        public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
-        }
-
-        @Override
-        public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) {
-        }
-
-        @Override
-        public void setChangeBoundsStartDelay(Object changeBounds, String className,
-                int startDelay) {
-        }
-
-        @Override
-        public void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) {
-        }
-
-        @Override
-        public Object createTransitionSet(boolean sequential) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public void addTransition(Object transitionSet, Object transition) {
-        }
-
-        @Override
-        public void exclude(Object transition, int targetId, boolean exclude) {
-        }
-
-        @Override
-        public void exclude(Object transition, View targetView, boolean exclude) {
-        }
-
-        @Override
-        public void excludeChildren(Object transition, int targetId, boolean exclude) {
-        }
-
-        @Override
-        public void excludeChildren(Object transition, View targetView, boolean exclude) {
-        }
-
-        @Override
-        public void include(Object transition, int targetId) {
-        }
-
-        @Override
-        public void include(Object transition, View targetView) {
-        }
-
-        @Override
-        public void setStartDelay(Object transition, long startDelay) {
-        }
-
-        @Override
-        public void setDuration(Object transition, long duration) {
-        }
-
-        @Override
-        public void addTransitionListener(Object transition, TransitionListener listener) {
-            TransitionStub stub = (TransitionStub) transition;
-            if (stub.mTransitionListeners == null) {
-                stub.mTransitionListeners = new ArrayList<TransitionListener>();
-            }
-            stub.mTransitionListeners.add(listener);
-        }
-
-        @Override
-        public void removeTransitionListener(Object transition, TransitionListener listener) {
-            TransitionStub stub = (TransitionStub) transition;
-            if (stub.mTransitionListeners != null) {
-                stub.mTransitionListeners.remove(listener);
-            }
-        }
-
-        @Override
-        public void runTransition(Object scene, Object transition) {
-            TransitionStub transitionStub = (TransitionStub) transition;
-            if (transitionStub != null && transitionStub.mTransitionListeners != null) {
-                for (int i = 0, size = transitionStub.mTransitionListeners.size(); i < size; i++) {
-                    transitionStub.mTransitionListeners.get(i).onTransitionStart(transition);
-                }
-            }
-            Runnable r = ((Runnable) scene);
-            if (r != null) {
-                r.run();
-            }
-            if (transitionStub != null && transitionStub.mTransitionListeners != null) {
-                for (int i = 0, size = transitionStub.mTransitionListeners.size(); i < size; i++) {
-                    transitionStub.mTransitionListeners.get(i).onTransitionEnd(transition);
-                }
-            }
-        }
-
-        @Override
-        public void setInterpolator(Object transition, Object timeInterpolator) {
-        }
-
-        @Override
-        public void addTarget(Object transition, View view) {
-        }
-
-        @Override
-        public Object createDefaultInterpolator(Context context) {
-            return null;
-        }
-
-        @Override
-        public Object loadTransition(Context context, int resId) {
-            return new TransitionStub();
-        }
-
-        @Override
-        public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
-        }
-
-        @Override
-        public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
-        }
-
-        @Override
-        public void setEpicenterCallback(Object transitionObject,
-                TransitionEpicenterCallback callback) {
-        }
-    }
-
-    /**
-     * Implementation used on KitKat (and above).
-     */
-    @RequiresApi(19)
-    static class TransitionHelperKitkatImpl extends TransitionHelperStubImpl {
-
-        @Override
-        public Object createScene(ViewGroup sceneRoot, Runnable r) {
-            return TransitionHelperKitkat.createScene(sceneRoot, r);
-        }
-
-        @Override
-        public Object createAutoTransition() {
-            return TransitionHelperKitkat.createAutoTransition();
-        }
-
-        @Override
-        public Object createFadeTransition(int fadingMode) {
-            return TransitionHelperKitkat.createFadeTransition(fadingMode);
-        }
-
-        @Override
-        public Object createChangeBounds(boolean reparent) {
-            return TransitionHelperKitkat.createChangeBounds(reparent);
-        }
-
-        @Override
-        public Object createSlide(int slideEdge) {
-            return TransitionHelperKitkat.createSlide(slideEdge);
-        }
-
-        @Override
-        public Object createScale() {
-            return TransitionHelperKitkat.createScale();
-        }
-
-        @Override
-        public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
-            TransitionHelperKitkat.setChangeBoundsStartDelay(changeBounds, view, startDelay);
-        }
-
-        @Override
-        public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) {
-            TransitionHelperKitkat.setChangeBoundsStartDelay(changeBounds, viewId, startDelay);
-        }
-
-        @Override
-        public void setChangeBoundsStartDelay(Object changeBounds, String className,
-                int startDelay) {
-            TransitionHelperKitkat.setChangeBoundsStartDelay(changeBounds, className, startDelay);
-        }
-
-        @Override
-        public void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) {
-            TransitionHelperKitkat.setChangeBoundsDefaultStartDelay(changeBounds, startDelay);
-        }
-
-        @Override
-        public Object createTransitionSet(boolean sequential) {
-            return TransitionHelperKitkat.createTransitionSet(sequential);
-        }
-
-        @Override
-        public void addTransition(Object transitionSet, Object transition) {
-            TransitionHelperKitkat.addTransition(transitionSet, transition);
-        }
-
-        @Override
-        public void exclude(Object transition, int targetId, boolean exclude) {
-            TransitionHelperKitkat.exclude(transition, targetId, exclude);
-        }
-
-        @Override
-        public void exclude(Object transition, View targetView, boolean exclude) {
-            TransitionHelperKitkat.exclude(transition, targetView, exclude);
-        }
-
-        @Override
-        public void excludeChildren(Object transition, int targetId, boolean exclude) {
-            TransitionHelperKitkat.excludeChildren(transition, targetId, exclude);
-        }
-
-        @Override
-        public void excludeChildren(Object transition, View targetView, boolean exclude) {
-            TransitionHelperKitkat.excludeChildren(transition, targetView, exclude);
-        }
-
-        @Override
-        public void include(Object transition, int targetId) {
-            TransitionHelperKitkat.include(transition, targetId);
-        }
-
-        @Override
-        public void include(Object transition, View targetView) {
-            TransitionHelperKitkat.include(transition, targetView);
-        }
-
-        @Override
-        public void setStartDelay(Object transition, long startDelay) {
-            TransitionHelperKitkat.setStartDelay(transition, startDelay);
-        }
-
-        @Override
-        public void setDuration(Object transition, long duration) {
-            TransitionHelperKitkat.setDuration(transition, duration);
-        }
-
-        @Override
-        public void addTransitionListener(Object transition, TransitionListener listener) {
-            TransitionHelperKitkat.addTransitionListener(transition, listener);
-        }
-
-        @Override
-        public void removeTransitionListener(Object transition, TransitionListener listener) {
-            TransitionHelperKitkat.removeTransitionListener(transition, listener);
-        }
-
-        @Override
-        public void runTransition(Object scene, Object transition) {
-            TransitionHelperKitkat.runTransition(scene, transition);
-        }
-
-        @Override
-        public void setInterpolator(Object transition, Object timeInterpolator) {
-            TransitionHelperKitkat.setInterpolator(transition, timeInterpolator);
-        }
-
-        @Override
-        public void addTarget(Object transition, View view) {
-            TransitionHelperKitkat.addTarget(transition, view);
-        }
-
-        @Override
-        public Object createDefaultInterpolator(Context context) {
-            return null;
-        }
-
-        @Override
-        public Object loadTransition(Context context, int resId) {
-            return TransitionHelperKitkat.loadTransition(context, resId);
-        }
-    }
-
-    @RequiresApi(21)
-    static final class TransitionHelperApi21Impl extends TransitionHelperKitkatImpl {
-
-        @Override
-        public void setEnterTransition(android.app.Fragment fragment, Object transition) {
-            TransitionHelperApi21.setEnterTransition(fragment, transition);
-        }
-
-        @Override
-        public void setExitTransition(android.app.Fragment fragment, Object transition) {
-            TransitionHelperApi21.setExitTransition(fragment, transition);
-        }
-
-        @Override
-        public void setSharedElementEnterTransition(android.app.Fragment fragment,
-                Object transition) {
-            TransitionHelperApi21.setSharedElementEnterTransition(fragment, transition);
-        }
-
-        @Override
-        public void addSharedElement(android.app.FragmentTransaction ft,
-                View view, String transitionName) {
-            TransitionHelperApi21.addSharedElement(ft, view, transitionName);
-        }
-
-        @Override
-        public Object getSharedElementEnterTransition(Window window) {
-            return TransitionHelperApi21.getSharedElementEnterTransition(window);
-        }
-
-        @Override
-        public void setSharedElementEnterTransition(Window window, Object object) {
-            TransitionHelperApi21.setSharedElementEnterTransition(window, object);
-        }
-
-        @Override
-        public Object getSharedElementReturnTransition(Window window) {
-            return TransitionHelperApi21.getSharedElementReturnTransition(window);
-        }
-
-        @Override
-        public void setSharedElementReturnTransition(Window window, Object transition) {
-            TransitionHelperApi21.setSharedElementReturnTransition(window, transition);
-        }
-
-        @Override
-        public Object getSharedElementExitTransition(Window window) {
-            return TransitionHelperApi21.getSharedElementExitTransition(window);
-        }
-
-        @Override
-        public Object getSharedElementReenterTransition(Window window) {
-            return TransitionHelperApi21.getSharedElementReenterTransition(window);
-        }
-
-        @Override
-        public Object createFadeAndShortSlide(int edge) {
-            return TransitionHelperApi21.createFadeAndShortSlide(edge);
-        }
-
-        @Override
-        public Object createFadeAndShortSlide(int edge, float distance) {
-            return TransitionHelperApi21.createFadeAndShortSlide(edge, distance);
-        }
-
-        @Override
-        public void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
-            TransitionHelperApi21.beginDelayedTransition(sceneRoot, transition);
-        }
-
-        @Override
-        public Object getEnterTransition(Window window) {
-            return TransitionHelperApi21.getEnterTransition(window);
-        }
-
-        @Override
-        public void setEnterTransition(Window window, Object transition) {
-            TransitionHelperApi21.setEnterTransition(window, transition);
-        }
-
-        @Override
-        public Object getReturnTransition(Window window) {
-            return TransitionHelperApi21.getReturnTransition(window);
-        }
-
-        @Override
-        public void setReturnTransition(Window window, Object transition) {
-            TransitionHelperApi21.setReturnTransition(window, transition);
-        }
-
-        @Override
-        public Object getExitTransition(Window window) {
-            return TransitionHelperApi21.getExitTransition(window);
-        }
-
-        @Override
-        public Object getReenterTransition(Window window) {
-            return TransitionHelperApi21.getReenterTransition(window);
-        }
-
-        @Override
-        public Object createScale() {
-            return TransitionHelperApi21.createScale();
-        }
-
-        @Override
-        public Object createDefaultInterpolator(Context context) {
-            return TransitionHelperApi21.createDefaultInterpolator(context);
-        }
-
-        @Override
-        public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
-            TransitionHelperApi21.setTransitionGroup(viewGroup, transitionGroup);
-        }
-
-        @Override
-        public Object createChangeTransform() {
-            return TransitionHelperApi21.createChangeTransform();
-        }
-
-        @Override
-        public void setEpicenterCallback(Object transitionObject,
-                TransitionEpicenterCallback callback) {
-            TransitionHelperApi21.setEpicenterCallback(transitionObject, callback);
-        }
-    }
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            sImpl = new TransitionHelperApi21Impl();
-        } else  if (systemSupportsTransitions()) {
-            sImpl = new TransitionHelperKitkatImpl();
-        } else {
-            sImpl = new TransitionHelperStubImpl();
-        }
-    }
-
-    public static Object getSharedElementEnterTransition(Window window) {
-        return sImpl.getSharedElementEnterTransition(window);
-    }
-
-    public static void setSharedElementEnterTransition(Window window, Object transition) {
-        sImpl.setSharedElementEnterTransition(window, transition);
-    }
-
-    public static Object getSharedElementReturnTransition(Window window) {
-        return sImpl.getSharedElementReturnTransition(window);
-    }
-
-    public static void setSharedElementReturnTransition(Window window, Object transition) {
-        sImpl.setSharedElementReturnTransition(window, transition);
-    }
-
-    public static Object getSharedElementExitTransition(Window window) {
-        return sImpl.getSharedElementExitTransition(window);
-    }
-
-    public static Object getSharedElementReenterTransition(Window window) {
-        return sImpl.getSharedElementReenterTransition(window);
-    }
-
-    public static Object getEnterTransition(Window window) {
-        return sImpl.getEnterTransition(window);
-    }
-
-    public static void setEnterTransition(Window window, Object transition) {
-        sImpl.setEnterTransition(window, transition);
-    }
-
-    public static Object getReturnTransition(Window window) {
-        return sImpl.getReturnTransition(window);
-    }
-
-    public static void setReturnTransition(Window window, Object transition) {
-        sImpl.setReturnTransition(window, transition);
-    }
-
-    public static Object getExitTransition(Window window) {
-        return sImpl.getExitTransition(window);
-    }
-
-    public static Object getReenterTransition(Window window) {
-        return sImpl.getReenterTransition(window);
-    }
-
-    public static Object createScene(ViewGroup sceneRoot, Runnable r) {
-        return sImpl.createScene(sceneRoot, r);
-    }
-
-    public static Object createChangeBounds(boolean reparent) {
-        return sImpl.createChangeBounds(reparent);
-    }
-
-    public static Object createChangeTransform() {
-        return sImpl.createChangeTransform();
-    }
-
-    public static void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
-        sImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay);
-    }
-
-    public static void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) {
-        sImpl.setChangeBoundsStartDelay(changeBounds, viewId, startDelay);
-    }
-
-    public static void setChangeBoundsStartDelay(Object changeBounds, String className,
-            int startDelay) {
-        sImpl.setChangeBoundsStartDelay(changeBounds, className, startDelay);
-    }
-
-    public static void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) {
-        sImpl.setChangeBoundsDefaultStartDelay(changeBounds, startDelay);
-    }
-
-    public static Object createTransitionSet(boolean sequential) {
-        return sImpl.createTransitionSet(sequential);
-    }
-
-    public static Object createSlide(int slideEdge) {
-        return sImpl.createSlide(slideEdge);
-    }
-
-    public static Object createScale() {
-        return sImpl.createScale();
-    }
-
-    public static void addTransition(Object transitionSet, Object transition) {
-        sImpl.addTransition(transitionSet, transition);
-    }
-
-    public static void exclude(Object transition, int targetId, boolean exclude) {
-        sImpl.exclude(transition, targetId, exclude);
-    }
-
-    public static void exclude(Object transition, View targetView, boolean exclude) {
-        sImpl.exclude(transition, targetView, exclude);
-    }
-
-    public static void excludeChildren(Object transition, int targetId, boolean exclude) {
-        sImpl.excludeChildren(transition, targetId, exclude);
-    }
-
-    public static void excludeChildren(Object transition, View targetView, boolean exclude) {
-        sImpl.excludeChildren(transition, targetView, exclude);
-    }
-
-    public static void include(Object transition, int targetId) {
-        sImpl.include(transition, targetId);
-    }
-
-    public static void include(Object transition, View targetView) {
-        sImpl.include(transition, targetView);
-    }
-
-    public static void setStartDelay(Object transition, long startDelay) {
-        sImpl.setStartDelay(transition, startDelay);
-    }
-
-    public static void setDuration(Object transition, long duration) {
-        sImpl.setDuration(transition, duration);
-    }
-
-    public static Object createAutoTransition() {
-        return sImpl.createAutoTransition();
-    }
-
-    public static Object createFadeTransition(int fadeMode) {
-        return sImpl.createFadeTransition(fadeMode);
-    }
-
-    public static void addTransitionListener(Object transition, TransitionListener listener) {
-        sImpl.addTransitionListener(transition, listener);
-    }
-
-    public static void removeTransitionListener(Object transition, TransitionListener listener) {
-        sImpl.removeTransitionListener(transition, listener);
-    }
-
-    public static void runTransition(Object scene, Object transition) {
-        sImpl.runTransition(scene, transition);
-    }
-
-    public static void setInterpolator(Object transition, Object timeInterpolator) {
-        sImpl.setInterpolator(transition, timeInterpolator);
-    }
-
-    public static void addTarget(Object transition, View view) {
-        sImpl.addTarget(transition, view);
-    }
-
-    public static Object createDefaultInterpolator(Context context) {
-        return sImpl.createDefaultInterpolator(context);
-    }
-
-    public static Object loadTransition(Context context, int resId) {
-        return sImpl.loadTransition(context, resId);
-    }
-
-    public static void setEnterTransition(android.app.Fragment fragment, Object transition) {
-        sImpl.setEnterTransition(fragment, transition);
-    }
-
-    public static void setExitTransition(android.app.Fragment fragment, Object transition) {
-        sImpl.setExitTransition(fragment, transition);
-    }
-
-    public static void setSharedElementEnterTransition(android.app.Fragment fragment,
-            Object transition) {
-        sImpl.setSharedElementEnterTransition(fragment, transition);
-    }
-
-    public static void addSharedElement(android.app.FragmentTransaction ft,
-            View view, String transitionName) {
-        sImpl.addSharedElement(ft, view, transitionName);
-    }
-
-    public static void setEnterTransition(android.support.v4.app.Fragment fragment,
-            Object transition) {
-        fragment.setEnterTransition(transition);
-    }
-
-    public static void setExitTransition(android.support.v4.app.Fragment fragment,
-            Object transition) {
-        fragment.setExitTransition(transition);
-    }
-
-    public static void setSharedElementEnterTransition(android.support.v4.app.Fragment fragment,
-            Object transition) {
-        fragment.setSharedElementEnterTransition(transition);
-    }
-
-    public static void addSharedElement(android.support.v4.app.FragmentTransaction ft,
-            View view, String transitionName) {
-        ft.addSharedElement(view, transitionName);
-    }
-
-    public static Object createFadeAndShortSlide(int edge) {
-        return sImpl.createFadeAndShortSlide(edge);
-    }
-
-    public static Object createFadeAndShortSlide(int edge, float distance) {
-        return sImpl.createFadeAndShortSlide(edge, distance);
-    }
-
-    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
-        sImpl.beginDelayedTransition(sceneRoot, transitionObject);
-    }
-
-    public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
-        sImpl.setTransitionGroup(viewGroup, transitionGroup);
-    }
-
-    public static void setEpicenterCallback(Object transition,
-            TransitionEpicenterCallback callback) {
-        sImpl.setEpicenterCallback(transition, callback);
-    }
-
-    /**
-     * @deprecated Use static calls.
-     */
-    @Deprecated
-    public static TransitionHelper getInstance() {
-        return new TransitionHelper();
-    }
-
-    /**
-     * @deprecated Use {@link #addTransitionListener(Object, TransitionListener)}
-     */
-    @Deprecated
-    public static void setTransitionListener(Object transition, TransitionListener listener) {
-        sImpl.addTransitionListener(transition, listener);
-    }
-}
diff --git a/android/support/v17/leanback/transition/TransitionHelperApi21.java b/android/support/v17/leanback/transition/TransitionHelperApi21.java
deleted file mode 100644
index dd1253a..0000000
--- a/android/support/v17/leanback/transition/TransitionHelperApi21.java
+++ /dev/null
@@ -1,145 +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.v17.leanback.transition;
-
-import android.R;
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.transition.ChangeTransform;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.animation.AnimationUtils;
-
-@RequiresApi(21)
-final class TransitionHelperApi21 {
-
-    TransitionHelperApi21() {
-    }
-
-    public static void setEnterTransition(android.app.Fragment fragment, Object transition) {
-        fragment.setEnterTransition((Transition)transition);
-    }
-
-    public static void setExitTransition(android.app.Fragment fragment, Object transition) {
-       fragment.setExitTransition((Transition)transition);
-    }
-
-    public static void setSharedElementEnterTransition(android.app.Fragment fragment,
-            Object transition) {
-        fragment.setSharedElementEnterTransition((Transition)transition);
-     }
-
-    public static void addSharedElement(android.app.FragmentTransaction ft,
-            View view, String transitionName) {
-        ft.addSharedElement(view, transitionName);
-    }
-
-    public static Object getSharedElementEnterTransition(Window window) {
-        return window.getSharedElementEnterTransition();
-    }
-
-    public static void setSharedElementEnterTransition(Window window, Object transition) {
-        window.setSharedElementEnterTransition((Transition) transition);
-    }
-
-    public static Object getSharedElementReturnTransition(Window window) {
-        return window.getSharedElementReturnTransition();
-    }
-
-    public static void setSharedElementReturnTransition(Window window, Object transition) {
-        window.setSharedElementReturnTransition((Transition) transition);
-    }
-
-    public static Object getSharedElementExitTransition(Window window) {
-        return window.getSharedElementExitTransition();
-    }
-
-    public static Object getSharedElementReenterTransition(Window window) {
-        return window.getSharedElementReenterTransition();
-    }
-
-    public static Object getEnterTransition(Window window) {
-        return window.getEnterTransition();
-    }
-
-    public static void setEnterTransition(Window window, Object transition) {
-        window.setEnterTransition((Transition) transition);
-    }
-
-    public static Object getReturnTransition(Window window) {
-        return window.getReturnTransition();
-    }
-
-    public static void setReturnTransition(Window window, Object transition) {
-        window.setReturnTransition((Transition) transition);
-    }
-
-    public static Object getExitTransition(Window window) {
-        return window.getExitTransition();
-    }
-
-    public static Object getReenterTransition(Window window) {
-        return window.getReenterTransition();
-    }
-
-    public static Object createScale() {
-        return new ChangeTransform();
-    }
-
-    public static Object createDefaultInterpolator(Context context) {
-        return AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_linear_in);
-    }
-
-    public static Object createChangeTransform() {
-        return new ChangeTransform();
-    }
-
-    public static Object createFadeAndShortSlide(int edge) {
-        return new FadeAndShortSlide(edge);
-    }
-
-    public static Object createFadeAndShortSlide(int edge, float distance) {
-        FadeAndShortSlide slide = new FadeAndShortSlide(edge);
-        slide.setDistance(distance);
-        return slide;
-    }
-
-    public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
-        Transition transition = (Transition) transitionObject;
-        TransitionManager.beginDelayedTransition(sceneRoot, transition);
-    }
-
-    public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
-        viewGroup.setTransitionGroup(transitionGroup);
-    }
-
-    public static void setEpicenterCallback(Object transitionObject,
-            final TransitionEpicenterCallback callback) {
-        Transition transition = (Transition) transitionObject;
-        if (callback == null) {
-            transition.setEpicenterCallback(null);
-        } else {
-            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
-                @Override
-                public Rect onGetEpicenter(Transition transition) {
-                    return callback.onGetEpicenter(transition);
-                }
-            });
-        }
-    }
-}
diff --git a/android/support/v17/leanback/transition/TransitionHelperKitkat.java b/android/support/v17/leanback/transition/TransitionHelperKitkat.java
deleted file mode 100644
index 211e8fc..0000000
--- a/android/support/v17/leanback/transition/TransitionHelperKitkat.java
+++ /dev/null
@@ -1,247 +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.v17.leanback.transition;
-
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.transition.AutoTransition;
-import android.transition.ChangeBounds;
-import android.transition.Fade;
-import android.transition.Scene;
-import android.transition.Transition;
-import android.transition.TransitionInflater;
-import android.transition.TransitionManager;
-import android.transition.TransitionSet;
-import android.transition.TransitionValues;
-import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-
-@RequiresApi(19)
-final class TransitionHelperKitkat {
-
-    TransitionHelperKitkat() {
-    }
-
-    static Object createScene(ViewGroup sceneRoot, Runnable enterAction) {
-        Scene scene = new Scene(sceneRoot);
-        scene.setEnterAction(enterAction);
-        return scene;
-    }
-
-    static Object createTransitionSet(boolean sequential) {
-        TransitionSet set = new TransitionSet();
-        set.setOrdering(sequential ? TransitionSet.ORDERING_SEQUENTIAL :
-            TransitionSet.ORDERING_TOGETHER);
-        return set;
-    }
-
-    static void addTransition(Object transitionSet, Object transition) {
-        ((TransitionSet) transitionSet).addTransition((Transition) transition);
-    }
-
-    static Object createAutoTransition() {
-        return new AutoTransition();
-    }
-
-    static Object createSlide(int slideEdge) {
-        SlideKitkat slide = new SlideKitkat();
-        slide.setSlideEdge(slideEdge);
-        return slide;
-    }
-
-    static Object createScale() {
-        Scale scale = new Scale();
-        return scale;
-    }
-
-    static Object createFadeTransition(int fadingMode) {
-        Fade fade = new Fade(fadingMode);
-        return fade;
-    }
-
-    /**
-     * change bounds that support customized start delay.
-     */
-    static class CustomChangeBounds extends ChangeBounds {
-
-        int mDefaultStartDelay;
-        // View -> delay
-        final HashMap<View, Integer> mViewStartDelays = new HashMap<View, Integer>();
-        // id -> delay
-        final SparseIntArray mIdStartDelays = new SparseIntArray();
-        // Class.getName() -> delay
-        final HashMap<String, Integer> mClassStartDelays = new HashMap<String, Integer>();
-
-        private int getDelay(View view) {
-            Integer delay = mViewStartDelays.get(view);
-            if (delay != null) {
-                return delay;
-            }
-            int idStartDelay = mIdStartDelays.get(view.getId(), -1);
-            if (idStartDelay != -1) {
-                return idStartDelay;
-            }
-            delay = mClassStartDelays.get(view.getClass().getName());
-            if (delay != null) {
-                return delay;
-            }
-            return mDefaultStartDelay;
-        }
-
-        @Override
-        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-                TransitionValues endValues) {
-            Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
-            if (animator != null && endValues != null && endValues.view != null) {
-                animator.setStartDelay(getDelay(endValues.view));
-            }
-            return animator;
-        }
-
-        public void setStartDelay(View view, int startDelay) {
-            mViewStartDelays.put(view, startDelay);
-        }
-
-        public void setStartDelay(int viewId, int startDelay) {
-            mIdStartDelays.put(viewId, startDelay);
-        }
-
-        public void setStartDelay(String className, int startDelay) {
-            mClassStartDelays.put(className, startDelay);
-        }
-
-        public void setDefaultStartDelay(int startDelay) {
-            mDefaultStartDelay = startDelay;
-        }
-    }
-
-    static Object createChangeBounds(boolean reparent) {
-        CustomChangeBounds changeBounds = new CustomChangeBounds();
-        changeBounds.setReparent(reparent);
-        return changeBounds;
-    }
-
-    static void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) {
-        ((CustomChangeBounds) changeBounds).setStartDelay(viewId, startDelay);
-    }
-
-    static void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
-        ((CustomChangeBounds) changeBounds).setStartDelay(view, startDelay);
-    }
-
-    static void setChangeBoundsStartDelay(Object changeBounds, String className, int startDelay) {
-        ((CustomChangeBounds) changeBounds).setStartDelay(className, startDelay);
-    }
-
-    static void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) {
-        ((CustomChangeBounds) changeBounds).setDefaultStartDelay(startDelay);
-    }
-
-    static void setStartDelay(Object transition, long startDelay) {
-        ((Transition)transition).setStartDelay(startDelay);
-    }
-
-    static void setDuration(Object transition, long duration) {
-        ((Transition)transition).setDuration(duration);
-    }
-
-    static void exclude(Object transition, int targetId, boolean exclude) {
-        ((Transition) transition).excludeTarget(targetId, exclude);
-    }
-
-    static void exclude(Object transition, View targetView, boolean exclude) {
-        ((Transition) transition).excludeTarget(targetView, exclude);
-    }
-
-    static void excludeChildren(Object transition, int targetId, boolean exclude) {
-        ((Transition) transition).excludeChildren(targetId, exclude);
-    }
-
-    static void excludeChildren(Object transition, View targetView, boolean exclude) {
-        ((Transition) transition).excludeChildren(targetView, exclude);
-    }
-
-    static void include(Object transition, int targetId) {
-        ((Transition) transition).addTarget(targetId);
-    }
-
-    static void include(Object transition, View targetView) {
-        ((Transition) transition).addTarget(targetView);
-    }
-
-    static void addTransitionListener(Object transition, final TransitionListener listener) {
-        if (listener == null) {
-            return;
-        }
-        Transition t = (Transition) transition;
-        listener.mImpl = new Transition.TransitionListener() {
-
-            @Override
-            public void onTransitionStart(Transition transition) {
-                listener.onTransitionStart(transition);
-            }
-
-            @Override
-            public void onTransitionResume(Transition transition) {
-                listener.onTransitionResume(transition);
-            }
-
-            @Override
-            public void onTransitionPause(Transition transition) {
-                listener.onTransitionPause(transition);
-            }
-
-            @Override
-            public void onTransitionEnd(Transition transition) {
-                listener.onTransitionEnd(transition);
-            }
-
-            @Override
-            public void onTransitionCancel(Transition transition) {
-                listener.onTransitionCancel(transition);
-            }
-        };
-        t.addListener((Transition.TransitionListener) listener.mImpl);
-    }
-
-    static void removeTransitionListener(Object transition, final TransitionListener listener) {
-        if (listener == null || listener.mImpl == null) {
-            return;
-        }
-        Transition t = (Transition) transition;
-        t.removeListener((Transition.TransitionListener) listener.mImpl);
-        listener.mImpl = null;
-    }
-
-    static void runTransition(Object scene, Object transition) {
-        TransitionManager.go((Scene) scene, (Transition) transition);
-    }
-
-    static void setInterpolator(Object transition, Object timeInterpolator) {
-        ((Transition) transition).setInterpolator((TimeInterpolator) timeInterpolator);
-    }
-
-    static void addTarget(Object transition, View view) {
-        ((Transition) transition).addTarget(view);
-    }
-
-    static Object loadTransition(Context context, int resId) {
-        return TransitionInflater.from(context).inflateTransition(resId);
-    }
-}
diff --git a/android/support/v17/leanback/transition/TransitionListener.java b/android/support/v17/leanback/transition/TransitionListener.java
deleted file mode 100644
index b3f5672..0000000
--- a/android/support/v17/leanback/transition/TransitionListener.java
+++ /dev/null
@@ -1,43 +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.v17.leanback.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Listeners for transition start and stop.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class TransitionListener {
-
-    protected Object mImpl;
-
-    public void onTransitionStart(Object transition) {
-    }
-
-    public void onTransitionEnd(Object transition) {
-    }
-
-    public void onTransitionCancel(Object transition) {
-    }
-
-    public void onTransitionPause(Object transition) {
-    }
-
-    public void onTransitionResume(Object transition) {
-    }
-}
diff --git a/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/android/support/v17/leanback/transition/TranslationAnimationCreator.java
deleted file mode 100644
index 157118e..0000000
--- a/android/support/v17/leanback/transition/TranslationAnimationCreator.java
+++ /dev/null
@@ -1,156 +0,0 @@
-package android.support.v17.leanback.transition;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.graphics.Path;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.transition.Transition;
-import android.transition.Transition.TransitionListener;
-import android.transition.TransitionValues;
-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.
- * @hide
- */
-@RequiresApi(21)
-@RestrictTo(LIBRARY_GROUP)
-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,
-            Transition transition) {
-        float terminalX = view.getTranslationX();
-        float terminalY = view.getTranslationY();
-        int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
-        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;
-        }
-        Path path = new Path();
-        path.moveTo(startX, startY);
-        path.lineTo(endX, endY);
-        ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
-                path);
-
-        TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
-                startPosX, startPosY, terminalX, terminalY);
-        transition.addListener(listener);
-        anim.addListener(listener);
-        anim.addPauseListener(listener);
-        anim.setInterpolator(interpolator);
-        return anim;
-    }
-
-    private static class TransitionPositionListener extends AnimatorListenerAdapter implements
-            TransitionListener {
-
-        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;
-
-        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.transitionPosition);
-            if (mTransitionPosition != null) {
-                mViewInHierarchy.setTag(R.id.transitionPosition, 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.transitionPosition, mTransitionPosition);
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-        }
-
-        @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);
-        }
-
-        @Override
-        public void onTransitionStart(Transition transition) {
-        }
-
-        @Override
-        public void onTransitionEnd(Transition transition) {
-            mMovingView.setTranslationX(mTerminalX);
-            mMovingView.setTranslationY(mTerminalY);
-        }
-
-        @Override
-        public void onTransitionCancel(Transition transition) {
-        }
-
-        @Override
-        public void onTransitionPause(Transition transition) {
-        }
-
-        @Override
-        public void onTransitionResume(Transition transition) {
-        }
-    }
-
-}
diff --git a/android/support/v17/leanback/util/MathUtil.java b/android/support/v17/leanback/util/MathUtil.java
deleted file mode 100644
index bf74e40..0000000
--- a/android/support/v17/leanback/util/MathUtil.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.v17.leanback.util;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Math Utilities for leanback library.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public final class MathUtil {
-
-    private MathUtil() {
-        // Prevent construction of this util class
-    }
-
-    /**
-     * Convert long to int safely. Similar with Math.toIntExact() in Java 8.
-     * @param numLong Number of type long to convert.
-     * @return int version of input.
-     * @throws ArithmeticException If input overflows int.
-     */
-    public static int safeLongToInt(long numLong) {
-        if ((int) numLong != numLong) {
-            throw new ArithmeticException("Input overflows int.\n");
-        }
-        return (int) numLong;
-    }
-}
diff --git a/android/support/v17/leanback/util/StateMachine.java b/android/support/v17/leanback/util/StateMachine.java
deleted file mode 100644
index dfc228c..0000000
--- a/android/support/v17/leanback/util/StateMachine.java
+++ /dev/null
@@ -1,379 +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.v17.leanback.util;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * State: each State has incoming Transitions and outgoing Transitions.
- * When {@link State#mBranchStart} is true, all the outgoing Transitions may be triggered, when
- * {@link State#mBranchStart} is false, only first outgoing Transition will be triggered.
- * When {@link State#mBranchEnd} is true, all the incoming Transitions must be triggered for the
- * State to run. When {@link State#mBranchEnd} is false, only need one incoming Transition triggered
- * for the State to run.
- * Transition: three types:
- * 1. Event based transition, transition will be triggered when {@link #fireEvent(Event)} is called.
- * 2. Auto transition, transition will be triggered when {@link Transition#mFromState} is executed.
- * 3. Condiitonal Auto transition, transition will be triggered when {@link Transition#mFromState}
- * is executed and {@link Transition#mCondition} passes.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class StateMachine {
-
-    static boolean DEBUG = false;
-    static final String TAG = "StateMachine";
-
-    /**
-     * No request on the State
-     */
-    public static final int STATUS_ZERO = 0;
-
-    /**
-     * Has been executed
-     */
-    public static final int STATUS_INVOKED = 1;
-
-    /**
-     * Used in Transition
-     */
-    public static class Event {
-        final String mName;
-
-        public Event(String name) {
-            mName = name;
-        }
-    }
-
-    /**
-     * Used in transition
-     */
-    public static class Condition {
-        final String mName;
-
-        public Condition(String name) {
-            mName = name;
-        }
-
-        /**
-         * @return True if can proceed and mark the transition INVOKED
-         */
-        public boolean canProceed() {
-            return true;
-        }
-    }
-
-    static class Transition {
-        final State mFromState;
-        final State mToState;
-        final Event mEvent;
-        final Condition mCondition;
-        int mState = STATUS_ZERO;
-
-        Transition(State fromState, State toState, Event event) {
-            if (event == null) {
-                throw new IllegalArgumentException();
-            }
-            mFromState = fromState;
-            mToState = toState;
-            mEvent = event;
-            mCondition = null;
-        }
-
-        Transition(State fromState, State toState) {
-            mFromState = fromState;
-            mToState = toState;
-            mEvent = null;
-            mCondition = null;
-        }
-
-        Transition(State fromState, State toState, Condition condition) {
-            if (condition == null) {
-                throw new IllegalArgumentException();
-            }
-            mFromState = fromState;
-            mToState = toState;
-            mEvent = null;
-            mCondition = condition;
-        }
-
-        @Override
-        public String toString() {
-            String signalName;
-            if (mEvent != null) {
-                signalName = mEvent.mName;
-            } else if (mCondition != null) {
-                signalName = mCondition.mName;
-            } else {
-                signalName = "auto";
-            }
-            return "[" + mFromState.mName + " -> " + mToState.mName + " <"
-                    + signalName + ">]";
-        }
-    }
-
-    /**
-     * @see StateMachine
-     */
-    public static class State {
-
-        final String mName;
-        final boolean mBranchStart;
-        final boolean mBranchEnd;
-        int mStatus = STATUS_ZERO;
-        int mInvokedOutTransitions = 0;
-        ArrayList<Transition> mIncomings;
-        ArrayList<Transition> mOutgoings;
-
-        @Override
-        public String toString() {
-            return "[" + mName + " " + mStatus + "]";
-        }
-
-        /**
-         * Create a State which is not branch start and a branch end.
-         */
-        public State(String name) {
-            this(name, false, true);
-        }
-
-        /**
-         * Create a State
-         * @param branchStart True if can run all out going transitions or false execute the first
-         *                    out going transition.
-         * @param branchEnd True if wait all incoming transitions executed or false
-         *                              only need one of the transition executed.
-         */
-        public State(String name, boolean branchStart, boolean branchEnd) {
-            mName = name;
-            mBranchStart = branchStart;
-            mBranchEnd = branchEnd;
-        }
-
-        void addIncoming(Transition t) {
-            if (mIncomings == null) {
-                mIncomings = new ArrayList();
-            }
-            mIncomings.add(t);
-        }
-
-        void addOutgoing(Transition t) {
-            if (mOutgoings == null) {
-                mOutgoings = new ArrayList();
-            }
-            mOutgoings.add(t);
-        }
-
-        /**
-         * Run State, Subclass may override.
-         */
-        public void run() {
-        }
-
-        final boolean checkPreCondition() {
-            if (mIncomings == null) {
-                return true;
-            }
-            if (mBranchEnd) {
-                for (Transition t: mIncomings) {
-                    if (t.mState != STATUS_INVOKED) {
-                        return false;
-                    }
-                }
-                return true;
-            } else {
-                for (Transition t: mIncomings) {
-                    if (t.mState == STATUS_INVOKED) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        }
-
-        /**
-         * @return True if the State has been executed.
-         */
-        final boolean runIfNeeded() {
-            if (mStatus != STATUS_INVOKED) {
-                if (checkPreCondition()) {
-                    if (DEBUG) {
-                        Log.d(TAG, "execute " + this);
-                    }
-                    mStatus = STATUS_INVOKED;
-                    run();
-                    signalAutoTransitionsAfterRun();
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        final void signalAutoTransitionsAfterRun() {
-            if (mOutgoings != null) {
-                for (Transition t: mOutgoings) {
-                    if (t.mEvent == null) {
-                        if (t.mCondition == null || t.mCondition.canProceed()) {
-                            if (DEBUG) {
-                                Log.d(TAG, "signal " + t);
-                            }
-                            mInvokedOutTransitions++;
-                            t.mState = STATUS_INVOKED;
-                            if (!mBranchStart) {
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        /**
-         * Get status, return one of {@link #STATUS_ZERO}, {@link #STATUS_INVOKED}.
-         * @return Status of the State.
-         */
-        public final int getStatus() {
-            return mStatus;
-        }
-    }
-
-    final ArrayList<State> mStates = new ArrayList<State>();
-    final ArrayList<State> mFinishedStates = new ArrayList();
-    final ArrayList<State> mUnfinishedStates = new ArrayList();
-
-    public StateMachine() {
-    }
-
-    /**
-     * Add a State to StateMachine, ignore if it is already added.
-     * @param state The state to add.
-     */
-    public void addState(State state) {
-        if (!mStates.contains(state)) {
-            mStates.add(state);
-        }
-    }
-
-    /**
-     * Add event-triggered transition between two states.
-     * @param fromState The from state.
-     * @param toState The to state.
-     * @param event The event that needed to perform the transition.
-     */
-    public void addTransition(State fromState, State toState, Event event) {
-        Transition transition = new Transition(fromState, toState, event);
-        toState.addIncoming(transition);
-        fromState.addOutgoing(transition);
-    }
-
-    /**
-     * Add a conditional auto transition between two states.
-     * @param fromState The from state.
-     * @param toState The to state.
-     */
-    public void addTransition(State fromState, State toState, Condition condition) {
-        Transition transition = new Transition(fromState, toState, condition);
-        toState.addIncoming(transition);
-        fromState.addOutgoing(transition);
-    }
-
-    /**
-     * Add an auto transition between two states.
-     * @param fromState The from state to add.
-     * @param toState The to state to add.
-     */
-    public void addTransition(State fromState, State toState) {
-        Transition transition = new Transition(fromState, toState);
-        toState.addIncoming(transition);
-        fromState.addOutgoing(transition);
-    }
-
-    /**
-     * Start the state machine.
-     */
-    public void start() {
-        if (DEBUG) {
-            Log.d(TAG, "start");
-        }
-        mUnfinishedStates.addAll(mStates);
-        runUnfinishedStates();
-    }
-
-    void runUnfinishedStates() {
-        boolean changed;
-        do {
-            changed = false;
-            for (int i = mUnfinishedStates.size() - 1; i >= 0; i--) {
-                State state = mUnfinishedStates.get(i);
-                if (state.runIfNeeded()) {
-                    mUnfinishedStates.remove(i);
-                    mFinishedStates.add(state);
-                    changed = true;
-                }
-            }
-        } while (changed);
-    }
-
-    /**
-     * Find outgoing Transitions of invoked State whose Event matches, mark the Transition invoked.
-     */
-    public void fireEvent(Event event) {
-        for (int i = 0; i < mFinishedStates.size(); i++) {
-            State state = mFinishedStates.get(i);
-            if (state.mOutgoings != null) {
-                if (!state.mBranchStart && state.mInvokedOutTransitions > 0) {
-                    continue;
-                }
-                for (Transition t : state.mOutgoings) {
-                    if (t.mState != STATUS_INVOKED && t.mEvent == event) {
-                        if (DEBUG) {
-                            Log.d(TAG, "signal " + t);
-                        }
-                        t.mState = STATUS_INVOKED;
-                        state.mInvokedOutTransitions++;
-                        if (!state.mBranchStart) {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        runUnfinishedStates();
-    }
-
-    /**
-     * Reset status to orignal status
-     */
-    public void reset() {
-        if (DEBUG) {
-            Log.d(TAG, "reset");
-        }
-        mUnfinishedStates.clear();
-        mFinishedStates.clear();
-        for (State state: mStates) {
-            state.mStatus = STATUS_ZERO;
-            state.mInvokedOutTransitions = 0;
-            if (state.mOutgoings != null) {
-                for (Transition t: state.mOutgoings) {
-                    t.mState = STATUS_ZERO;
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java b/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
deleted file mode 100644
index 8d176c4..0000000
--- a/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
+++ /dev/null
@@ -1,241 +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.v17.leanback.widget;
-
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetricsInt;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.widget.TextView;
-
-/**
- * An abstract {@link Presenter} for rendering a detailed description of an
- * item. Typically this Presenter will be used in a {@link DetailsOverviewRowPresenter}
- * or {@link PlaybackControlsRowPresenter}.
- *
- * <p>Subclasses must override {@link #onBindDescription} to implement the data
- * binding for this Presenter.
- */
-public abstract class AbstractDetailsDescriptionPresenter extends Presenter {
-
-    /**
-     * The ViewHolder for the {@link AbstractDetailsDescriptionPresenter}.
-     */
-    public static class ViewHolder extends Presenter.ViewHolder {
-        final TextView mTitle;
-        final TextView mSubtitle;
-        final TextView mBody;
-        final int mTitleMargin;
-        final int mUnderTitleBaselineMargin;
-        final int mUnderSubtitleBaselineMargin;
-        final int mTitleLineSpacing;
-        final int mBodyLineSpacing;
-        final int mBodyMaxLines;
-        final int mBodyMinLines;
-        final FontMetricsInt mTitleFontMetricsInt;
-        final FontMetricsInt mSubtitleFontMetricsInt;
-        final FontMetricsInt mBodyFontMetricsInt;
-        final int mTitleMaxLines;
-        private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
-
-        public ViewHolder(final View view) {
-            super(view);
-            mTitle = (TextView) view.findViewById(R.id.lb_details_description_title);
-            mSubtitle = (TextView) view.findViewById(R.id.lb_details_description_subtitle);
-            mBody = (TextView) view.findViewById(R.id.lb_details_description_body);
-
-            FontMetricsInt titleFontMetricsInt = getFontMetricsInt(mTitle);
-            final int titleAscent = view.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_description_title_baseline);
-            // Ascent is negative
-            mTitleMargin = titleAscent + titleFontMetricsInt.ascent;
-
-            mUnderTitleBaselineMargin = view.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_description_under_title_baseline_margin);
-            mUnderSubtitleBaselineMargin = view.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_description_under_subtitle_baseline_margin);
-
-            mTitleLineSpacing = view.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_description_title_line_spacing);
-            mBodyLineSpacing = view.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_description_body_line_spacing);
-
-            mBodyMaxLines = view.getResources().getInteger(
-                    R.integer.lb_details_description_body_max_lines);
-            mBodyMinLines = view.getResources().getInteger(
-                    R.integer.lb_details_description_body_min_lines);
-            mTitleMaxLines = mTitle.getMaxLines();
-
-            mTitleFontMetricsInt = getFontMetricsInt(mTitle);
-            mSubtitleFontMetricsInt = getFontMetricsInt(mSubtitle);
-            mBodyFontMetricsInt = getFontMetricsInt(mBody);
-
-            mTitle.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) {
-                    addPreDrawListener();
-                }
-            });
-        }
-
-        void addPreDrawListener() {
-            if (mPreDrawListener != null) {
-                return;
-            }
-            mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    if (mSubtitle.getVisibility() == View.VISIBLE
-                            && mSubtitle.getTop() > view.getHeight()
-                            && mTitle.getLineCount() > 1) {
-                        mTitle.setMaxLines(mTitle.getLineCount() - 1);
-                        return false;
-                    }
-                    final int titleLines = mTitle.getLineCount();
-                    final int maxLines = titleLines > 1 ? mBodyMinLines : mBodyMaxLines;
-                    if (mBody.getMaxLines() != maxLines) {
-                        mBody.setMaxLines(maxLines);
-                        return false;
-                    } else {
-                        removePreDrawListener();
-                        return true;
-                    }
-                }
-            };
-            view.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
-        }
-
-        void removePreDrawListener() {
-            if (mPreDrawListener != null) {
-                view.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
-                mPreDrawListener = null;
-            }
-        }
-
-        public TextView getTitle() {
-            return mTitle;
-        }
-
-        public TextView getSubtitle() {
-            return mSubtitle;
-        }
-
-        public TextView getBody() {
-            return mBody;
-        }
-
-        private FontMetricsInt getFontMetricsInt(TextView textView) {
-            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            paint.setTextSize(textView.getTextSize());
-            paint.setTypeface(textView.getTypeface());
-            return paint.getFontMetricsInt();
-        }
-    }
-
-    @Override
-    public final ViewHolder onCreateViewHolder(ViewGroup parent) {
-        View v = LayoutInflater.from(parent.getContext())
-            .inflate(R.layout.lb_details_description, parent, false);
-        return new ViewHolder(v);
-    }
-
-    @Override
-    public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-        ViewHolder vh = (ViewHolder) viewHolder;
-        onBindDescription(vh, item);
-
-        boolean hasTitle = true;
-        if (TextUtils.isEmpty(vh.mTitle.getText())) {
-            vh.mTitle.setVisibility(View.GONE);
-            hasTitle = false;
-        } else {
-            vh.mTitle.setVisibility(View.VISIBLE);
-            vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight()
-                    + vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier());
-            vh.mTitle.setMaxLines(vh.mTitleMaxLines);
-        }
-        setTopMargin(vh.mTitle, vh.mTitleMargin);
-
-        boolean hasSubtitle = true;
-        if (TextUtils.isEmpty(vh.mSubtitle.getText())) {
-            vh.mSubtitle.setVisibility(View.GONE);
-            hasSubtitle = false;
-        } else {
-            vh.mSubtitle.setVisibility(View.VISIBLE);
-            if (hasTitle) {
-                setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin
-                        + vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
-            } else {
-                setTopMargin(vh.mSubtitle, 0);
-            }
-        }
-
-        if (TextUtils.isEmpty(vh.mBody.getText())) {
-            vh.mBody.setVisibility(View.GONE);
-        } else {
-            vh.mBody.setVisibility(View.VISIBLE);
-            vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight()
-                    + vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier());
-
-            if (hasSubtitle) {
-                setTopMargin(vh.mBody, vh.mUnderSubtitleBaselineMargin
-                        + vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent);
-            } else if (hasTitle) {
-                setTopMargin(vh.mBody, vh.mUnderTitleBaselineMargin
-                        + vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
-            } else {
-                setTopMargin(vh.mBody, 0);
-            }
-        }
-    }
-
-    /**
-     * Binds the data from the item to the ViewHolder.  The item is typically associated with
-     * a {@link DetailsOverviewRow} or {@link PlaybackControlsRow}.
-     *
-     * @param vh The ViewHolder for this details description view.
-     * @param item The item being presented.
-     */
-    protected abstract void onBindDescription(ViewHolder vh, Object item);
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {}
-
-    @Override
-    public void onViewAttachedToWindow(Presenter.ViewHolder holder) {
-        // In case predraw listener was removed in detach, make sure
-        // we have the proper layout.
-        ViewHolder vh = (ViewHolder) holder;
-        vh.addPreDrawListener();
-        super.onViewAttachedToWindow(holder);
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(Presenter.ViewHolder holder) {
-        ViewHolder vh = (ViewHolder) holder;
-        vh.removePreDrawListener();
-        super.onViewDetachedFromWindow(holder);
-    }
-
-    private void setTopMargin(TextView textView, int topMargin) {
-        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) textView.getLayoutParams();
-        lp.topMargin = topMargin;
-        textView.setLayoutParams(lp);
-    }
-}
diff --git a/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java b/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
deleted file mode 100644
index 196fa93..0000000
--- a/android/support/v17/leanback/widget/AbstractMediaItemPresenter.java
+++ /dev/null
@@ -1,660 +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.v17.leanback.widget;
-
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.support.v17.leanback.R;
-import android.support.v4.view.ViewCompat;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.TextView;
-import android.widget.ViewFlipper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Abstract {@link Presenter} class for rendering media items in a playlist format.
- * Media item data provided for this presenter can implement the interface
- * {@link MultiActionsProvider}, if the media rows wish to contain custom actions.
- * Media items in the playlist are arranged as a vertical list with each row holding each media
- * item's details provided by the user of this class and a set of optional custom actions.
- * Each media item's details and actions are separately focusable.
- * The appearance of each one of the media row components can be controlled through setting
- * theme's attributes.
- * Each media item row provides a view flipper for switching between different views depending on
- * the playback state.
- * A default layout is provided by this presenter for rendering different playback states, or a
- * custom layout can be provided by the user by overriding the
- * playbackMediaItemNumberViewFlipperLayout attribute in the currently specified theme.
- * Subclasses should also override {@link #getMediaPlayState(Object)} to provide the current play
- * state of their media item model in case they wish to use different views depending on the
- * playback state.
- * The presenter can optionally provide line separators between media rows by setting
- * {@link #setHasMediaRowSeparator(boolean)} to true.
- * <p>
- *     Subclasses must override {@link #onBindMediaDetails} to implement their media item model
- *     data binding to each row view.
- * </p>
- * <p>
- *     The {@link OnItemViewClickedListener} and {@link OnItemViewSelectedListener}
- *     can be used in the same fashion to handle selection or click events on either of
- *     media details or each individual action views.
- * </p>
- * <p>
- *     {@link AbstractMediaListHeaderPresenter} can be used in conjunction with this presenter in
- *     order to display a playlist with a header view.
- * </p>
- */
-public abstract class AbstractMediaItemPresenter extends RowPresenter {
-
-    /**
-     * Different playback states of a media item
-     */
-
-    /**
-     * Indicating that the media item is currently neither playing nor paused.
-     */
-    public static final int PLAY_STATE_INITIAL = 0;
-    /**
-     * Indicating that the media item is currently paused.
-     */
-    public static final int PLAY_STATE_PAUSED = 1;
-    /**
-     * Indicating that the media item is currently playing
-     */
-    public static final int PLAY_STATE_PLAYING = 2;
-
-    final static Rect sTempRect = new Rect();
-    private int mBackgroundColor = Color.TRANSPARENT;
-    private boolean mBackgroundColorSet;
-    private boolean mMediaRowSeparator;
-    private int mThemeId;
-
-    private Presenter mMediaItemActionPresenter = new MediaItemActionPresenter();
-
-    /**
-     * Constructor used for creating an abstract media item presenter.
-     */
-    public AbstractMediaItemPresenter() {
-        this(0);
-    }
-
-    /**
-     * Constructor used for creating an abstract media item presenter.
-     * @param themeId The resource id of the theme that defines attributes controlling the
-     *                appearance of different widgets in a media item row.
-     */
-    public AbstractMediaItemPresenter(int themeId) {
-        mThemeId = themeId;
-        setHeaderPresenter(null);
-    }
-
-    /**
-     * Sets the theme used to style a media item row components.
-     * @param themeId The resource id of the theme that defines attributes controlling the
-     *                appearance of different widgets in a media item row.
-     */
-    public void setThemeId(int themeId) {
-        mThemeId = themeId;
-    }
-
-    /**
-     * Return The resource id of the theme that defines attributes controlling the appearance of
-     * different widgets in a media item row.
-     *
-     * @return The resource id of the theme that defines attributes controlling the appearance of
-     * different widgets in a media item row.
-     */
-    public int getThemeId() {
-        return mThemeId;
-    }
-
-    /**
-     * Sets the action presenter rendering each optional custom action within each media item row.
-     * @param actionPresenter the presenter to be used for rendering a media item row actions.
-     */
-    public void setActionPresenter(Presenter actionPresenter) {
-        mMediaItemActionPresenter = actionPresenter;
-    }
-
-    /**
-     * Return the presenter used to render a media item row actions.
-     *
-     * @return the presenter used to render a media item row actions.
-     */
-    public Presenter getActionPresenter() {
-        return mMediaItemActionPresenter;
-    }
-
-    /**
-     * The ViewHolder for the {@link AbstractMediaItemPresenter}. It references different views
-     * that place different meta-data corresponding to a media item details, actions, selector,
-     * listeners, and presenters,
-     */
-    public static class ViewHolder extends RowPresenter.ViewHolder {
-
-        final View mMediaRowView;
-        final View mSelectorView;
-        private final View mMediaItemDetailsView;
-        final ViewFlipper mMediaItemNumberViewFlipper;
-        final TextView mMediaItemNumberView;
-        final View mMediaItemPausedView;
-
-        final View mMediaItemPlayingView;
-        private final TextView mMediaItemNameView;
-        private final TextView mMediaItemDurationView;
-        private final View mMediaItemRowSeparator;
-        private final ViewGroup mMediaItemActionsContainer;
-        private final List<Presenter.ViewHolder> mActionViewHolders;
-        MultiActionsProvider.MultiAction[] mMediaItemRowActions;
-        AbstractMediaItemPresenter mRowPresenter;
-        ValueAnimator mFocusViewAnimator;
-
-        public ViewHolder(View view) {
-            super(view);
-            mSelectorView = view.findViewById(R.id.mediaRowSelector);
-            mMediaRowView  = view.findViewById(R.id.mediaItemRow);
-            mMediaItemDetailsView = view.findViewById(R.id.mediaItemDetails);
-            mMediaItemNameView = (TextView) view.findViewById(R.id.mediaItemName);
-            mMediaItemDurationView = (TextView) view.findViewById(R.id.mediaItemDuration);
-            mMediaItemRowSeparator = view.findViewById(R.id.mediaRowSeparator);
-            mMediaItemActionsContainer = (ViewGroup) view.findViewById(
-                    R.id.mediaItemActionsContainer);
-            mActionViewHolders = new ArrayList<Presenter.ViewHolder>();
-            getMediaItemDetailsView().setOnClickListener(new View.OnClickListener(){
-                @Override
-                public void onClick(View view) {
-                    if (getOnItemViewClickedListener() != null) {
-                        getOnItemViewClickedListener().onItemClicked(null, null,
-                                ViewHolder.this, getRowObject());
-                    }
-                }
-            });
-            getMediaItemDetailsView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
-                @Override
-                public void onFocusChange(View view, boolean hasFocus) {
-                    mFocusViewAnimator = updateSelector(mSelectorView, view, mFocusViewAnimator,
-                            true);
-                }
-            });
-            mMediaItemNumberViewFlipper =
-                    (ViewFlipper) view.findViewById(R.id.mediaItemNumberViewFlipper);
-
-            TypedValue typedValue = new TypedValue();
-            boolean found = view.getContext().getTheme().resolveAttribute(
-                    R.attr.playbackMediaItemNumberViewFlipperLayout, typedValue, true);
-            View mergeView = LayoutInflater.from(view.getContext())
-                    .inflate(found
-                            ? typedValue.resourceId
-                            : R.layout.lb_media_item_number_view_flipper,
-                            mMediaItemNumberViewFlipper, true);
-
-            mMediaItemNumberView = (TextView) mergeView.findViewById(R.id.initial);
-            mMediaItemPausedView = mergeView.findViewById(R.id.paused);
-            mMediaItemPlayingView = mergeView.findViewById(R.id.playing);
-        }
-
-        /**
-         * Binds the actions in a media item row object to their views. This consists of creating
-         * (or reusing the existing) action view holders, and populating them with the actions'
-         * icons.
-         */
-        public void onBindRowActions() {
-            for (int i = getMediaItemActionsContainer().getChildCount() - 1;
-                 i >= mActionViewHolders.size(); i--) {
-                getMediaItemActionsContainer().removeViewAt(i);
-                mActionViewHolders.remove(i);
-            }
-            mMediaItemRowActions = null;
-
-            Object rowObject = getRowObject();
-            final MultiActionsProvider.MultiAction[] actionList;
-            if (rowObject instanceof MultiActionsProvider) {
-                actionList = ((MultiActionsProvider) rowObject).getActions();
-            } else {
-                return;
-            }
-            Presenter actionPresenter = mRowPresenter.getActionPresenter();
-            if (actionPresenter == null) {
-                return;
-            }
-
-            mMediaItemRowActions = actionList;
-            for (int i = mActionViewHolders.size(); i < actionList.length; i++) {
-                final int actionIndex = i;
-                final Presenter.ViewHolder actionViewHolder =
-                        actionPresenter.onCreateViewHolder(getMediaItemActionsContainer());
-                getMediaItemActionsContainer().addView(actionViewHolder.view);
-                mActionViewHolders.add(actionViewHolder);
-                actionViewHolder.view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-                    @Override
-                    public void onFocusChange(View view, boolean hasFocus) {
-                        mFocusViewAnimator = updateSelector(mSelectorView, view,
-                                mFocusViewAnimator, false);
-                    }
-                });
-                actionViewHolder.view.setOnClickListener(
-                        new View.OnClickListener() {
-                            @Override
-                            public void onClick(View view) {
-                                if (getOnItemViewClickedListener() != null) {
-                                    getOnItemViewClickedListener().onItemClicked(
-                                            actionViewHolder, mMediaItemRowActions[actionIndex],
-                                            ViewHolder.this, getRowObject());
-                                }
-                            }
-                        });
-            }
-
-            if (mMediaItemActionsContainer != null) {
-                for (int i = 0; i < actionList.length; i++) {
-                    Presenter.ViewHolder avh = mActionViewHolders.get(i);
-                    actionPresenter.onUnbindViewHolder(avh);
-                    actionPresenter.onBindViewHolder(avh, mMediaItemRowActions[i]);
-                }
-            }
-
-        }
-
-        int findActionIndex(MultiActionsProvider.MultiAction action) {
-            if (mMediaItemRowActions != null) {
-                for (int i = 0; i < mMediaItemRowActions.length; i++) {
-                    if (mMediaItemRowActions[i] == action) {
-                        return i;
-                    }
-                }
-            }
-            return -1;
-        }
-
-        /**
-         * Notifies an action has changed in this media row and the UI needs to be updated
-         * @param action The action whose state has changed
-         */
-        public void notifyActionChanged(MultiActionsProvider.MultiAction action) {
-            Presenter actionPresenter = mRowPresenter.getActionPresenter();
-            if (actionPresenter == null) {
-                return;
-            }
-            int actionIndex = findActionIndex(action);
-            if (actionIndex >= 0) {
-                Presenter.ViewHolder actionViewHolder = mActionViewHolders.get(actionIndex);
-                actionPresenter.onUnbindViewHolder(actionViewHolder);
-                actionPresenter.onBindViewHolder(actionViewHolder, action);
-            }
-        }
-
-        /**
-         * Notifies the content of the media item details in a row has changed and triggers updating
-         * the UI. This causes {@link #onBindMediaDetails(ViewHolder, Object)}
-         * on the user's provided presenter to be called back, allowing them to update UI
-         * accordingly.
-         */
-        public void notifyDetailsChanged() {
-            mRowPresenter.onUnbindMediaDetails(this);
-            mRowPresenter.onBindMediaDetails(this, getRowObject());
-        }
-
-        /**
-         * Notifies the playback state of the media item row has changed. This in turn triggers
-         * updating of the UI for that media item row if corresponding views are specified for each
-         * playback state.
-         * By default, 3 views are provided for each playback state, or these views can be provided
-         * by the user.
-         */
-        public void notifyPlayStateChanged() {
-            mRowPresenter.onBindMediaPlayState(this);
-        }
-
-        /**
-         * @return The SelectorView responsible for highlighting the in-focus view within each
-         * media item row
-         */
-        public View getSelectorView() {
-            return mSelectorView;
-        }
-
-        /**
-         * @return The FlipperView responsible for flipping between different media item number
-         * views depending on the playback state
-         */
-        public ViewFlipper getMediaItemNumberViewFlipper() {
-            return mMediaItemNumberViewFlipper;
-        }
-
-        /**
-         * @return The TextView responsible for rendering the media item number.
-         * This view is rendered when the media item row is neither playing nor paused.
-         */
-        public TextView getMediaItemNumberView() {
-            return mMediaItemNumberView;
-        }
-
-        /**
-         * @return The view rendered when the media item row is paused.
-         */
-        public View getMediaItemPausedView() {
-            return mMediaItemPausedView;
-        }
-
-        /**
-         * @return The view rendered when the media item row is playing.
-         */
-        public View getMediaItemPlayingView() {
-            return mMediaItemPlayingView;
-        }
-
-
-        /**
-         * Flips to the view at index 'position'. This position corresponds to the index of a
-         * particular view within the ViewFlipper layout specified for the MediaItemNumberView
-         * (see playbackMediaItemNumberViewFlipperLayout attribute).
-         * @param position The index of the child view to display.
-         */
-        public void setSelectedMediaItemNumberView(int position) {
-            if (position >= 0 && position < mMediaItemNumberViewFlipper.getChildCount()) {
-                mMediaItemNumberViewFlipper.setDisplayedChild(position);
-            }
-        }
-        /**
-         * Returns the view displayed when the media item is neither playing nor paused,
-         * corresponding to the playback state of PLAY_STATE_INITIAL.
-         * @return The TextView responsible for rendering the media item name.
-         */
-        public TextView getMediaItemNameView() {
-            return mMediaItemNameView;
-        }
-
-        /**
-         * @return The TextView responsible for rendering the media item duration
-         */
-        public TextView getMediaItemDurationView() {
-            return mMediaItemDurationView;
-        }
-
-        /**
-         * @return The view container of media item details
-         */
-        public View getMediaItemDetailsView() {
-            return mMediaItemDetailsView;
-        }
-
-        /**
-         * @return The view responsible for rendering the separator line between media rows
-         */
-        public View getMediaItemRowSeparator() {
-            return mMediaItemRowSeparator;
-        }
-
-        /**
-         * @return The view containing the set of custom actions
-         */
-        public ViewGroup getMediaItemActionsContainer() {
-            return mMediaItemActionsContainer;
-        }
-
-        /**
-         * @return Array of MultiActions displayed for this media item row
-         */
-        public MultiActionsProvider.MultiAction[] getMediaItemRowActions() {
-            return mMediaItemRowActions;
-        }
-    }
-
-    @Override
-    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
-        Context context = parent.getContext();
-        if (mThemeId != 0) {
-            context = new ContextThemeWrapper(context, mThemeId);
-        }
-        View view =
-                LayoutInflater.from(context).inflate(R.layout.lb_row_media_item, parent, false);
-        final ViewHolder vh = new ViewHolder(view);
-        vh.mRowPresenter = this;
-        if (mBackgroundColorSet) {
-            vh.mMediaRowView.setBackgroundColor(mBackgroundColor);
-        }
-        return vh;
-    }
-
-    @Override
-    public boolean isUsingDefaultSelectEffect() {
-        return false;
-    }
-
-    @Override
-    protected boolean isClippingChildren() {
-        return true;
-    }
-
-    @Override
-    protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
-        super.onBindRowViewHolder(vh, item);
-
-        final ViewHolder mvh = (ViewHolder) vh;
-
-        onBindRowActions(mvh);
-
-        mvh.getMediaItemRowSeparator().setVisibility(hasMediaRowSeparator() ? View.VISIBLE :
-                View.GONE);
-
-        onBindMediaPlayState(mvh);
-        onBindMediaDetails((ViewHolder) vh, item);
-    }
-
-    /**
-     * Binds the given media item object action to the given ViewHolder's action views.
-     * @param vh ViewHolder for the media item.
-     */
-    protected void onBindRowActions(ViewHolder vh) {
-        vh.onBindRowActions();
-    }
-
-    /**
-     * Sets the background color for the row views within the playlist.
-     * If this is not set, a default color, defaultBrandColor, from theme is used.
-     * This defaultBrandColor defaults to android:attr/colorPrimary on v21, if it's specified.
-     * @param color The ARGB color used to set as the media list background color.
-     */
-    public void setBackgroundColor(int color) {
-        mBackgroundColorSet = true;
-        mBackgroundColor = color;
-    }
-
-    /**
-     * Specifies whether a line separator should be used between media item rows.
-     * @param hasSeparator true if a separator should be displayed, false otherwise.
-     */
-    public void setHasMediaRowSeparator(boolean hasSeparator) {
-        mMediaRowSeparator = hasSeparator;
-    }
-
-    public boolean hasMediaRowSeparator() {
-        return mMediaRowSeparator;
-    }
-    /**
-     * Binds the media item details to their views provided by the
-     * {@link AbstractMediaItemPresenter}.
-     * This method is to be overridden by the users of this presenter.
-     * The subclasses of this presenter can access and bind individual views for either of the
-     * media item number, name, or duration (depending on whichever views are visible according to
-     * the providing theme attributes), by calling {@link ViewHolder#getMediaItemNumberView()},
-     * {@link ViewHolder#getMediaItemNameView()}, and {@link ViewHolder#getMediaItemDurationView()},
-     * on the {@link ViewHolder} provided as the argument {@code vh} of this presenter.
-     *
-     * @param vh The ViewHolder for this {@link AbstractMediaItemPresenter}.
-     * @param item The media item row object being presented.
-     */
-    protected abstract void onBindMediaDetails(ViewHolder vh, Object item);
-
-    /**
-     * Unbinds the media item details from their views provided by the
-     * {@link AbstractMediaItemPresenter}.
-     * This method can be overridden by the subclasses of this presenter if required.
-     * @param vh ViewHolder to unbind from.
-     */
-    protected void onUnbindMediaDetails(ViewHolder vh) {
-    }
-
-    /**
-     * Binds the media item number view to the appropriate play state view of the media item.
-     * The play state of the media item is extracted by calling {@link #getMediaPlayState(Object)} for
-     * the media item embedded within this view.
-     * This method triggers updating of the playback state UI if corresponding views are specified
-     * for the current playback state.
-     * By default, 3 views are provided for each playback state, or these views can be provided
-     * by the user.
-     */
-    public void onBindMediaPlayState(ViewHolder vh) {
-        int childIndex = calculateMediaItemNumberFlipperIndex(vh);
-        if (childIndex != -1 && vh.mMediaItemNumberViewFlipper.getDisplayedChild() != childIndex) {
-            vh.mMediaItemNumberViewFlipper.setDisplayedChild(childIndex);
-        }
-    }
-
-    static int calculateMediaItemNumberFlipperIndex(ViewHolder vh) {
-        int childIndex = -1;
-        int newPlayState = vh.mRowPresenter.getMediaPlayState(vh.getRowObject());
-        switch (newPlayState) {
-            case PLAY_STATE_INITIAL:
-                childIndex = (vh.mMediaItemNumberView == null) ? -1 :
-                        vh.mMediaItemNumberViewFlipper.indexOfChild(vh.mMediaItemNumberView);
-                break;
-            case PLAY_STATE_PAUSED:
-                childIndex = (vh.mMediaItemPausedView == null) ? -1 :
-                        vh.mMediaItemNumberViewFlipper.indexOfChild(vh.mMediaItemPausedView);
-                break;
-            case PLAY_STATE_PLAYING:
-                childIndex = (vh.mMediaItemPlayingView == null) ? -1 :
-                        vh.mMediaItemNumberViewFlipper.indexOfChild(vh.mMediaItemPlayingView);
-        }
-        return childIndex;
-    }
-
-    /**
-     * Called when the given ViewHolder wants to unbind the play state view.
-     * @param vh The ViewHolder to unbind from.
-     */
-    public void onUnbindMediaPlayState(ViewHolder vh) {
-    }
-
-    /**
-     * Returns the current play state of the given media item. By default, this method returns
-     * PLAY_STATE_INITIAL which causes the media item number
-     * {@link ViewHolder#getMediaItemNameView()} to be displayed for different
-     * playback states. Users of this class should override this method in order to provide the
-     * play state of their custom media item data model.
-     * @param item The media item
-     * @return The current play state of this media item
-     */
-    protected int getMediaPlayState(Object item) {
-        return PLAY_STATE_INITIAL;
-    }
-    /**
-     * Each media item row can have multiple focusable elements; the details on the left and a set
-     * of optional custom actions on the right.
-     * The selector is a highlight that moves to highlight to cover whichever views is in focus.
-     *
-     * @param selectorView the selector view used to highlight an individual element within a row.
-     * @param focusChangedView The component within the media row whose focus got changed.
-     * @param layoutAnimator the ValueAnimator producing animation frames for the selector's width
-     *                       and x-translation, generated by this method and stored for the each
-     *                       {@link ViewHolder}.
-     * @param isDetails Whether the changed-focused view is for a media item details (true) or
-     *                  an action (false).
-     */
-    static ValueAnimator updateSelector(final View selectorView,
-            View focusChangedView, ValueAnimator layoutAnimator, boolean isDetails) {
-        int animationDuration = focusChangedView.getContext().getResources()
-                .getInteger(android.R.integer.config_shortAnimTime);
-        DecelerateInterpolator interpolator = new DecelerateInterpolator();
-
-        int layoutDirection = ViewCompat.getLayoutDirection(selectorView);
-        if (!focusChangedView.hasFocus()) {
-            // if neither of the details or action views are in focus (ie. another row is in focus),
-            // animate the selector out.
-            selectorView.animate().cancel();
-            selectorView.animate().alpha(0f).setDuration(animationDuration)
-                    .setInterpolator(interpolator).start();
-            // keep existing layout animator
-            return layoutAnimator;
-        } else {
-            // cancel existing layout animator
-            if (layoutAnimator != null) {
-                layoutAnimator.cancel();
-                layoutAnimator = null;
-            }
-            float currentAlpha = selectorView.getAlpha();
-            selectorView.animate().alpha(1f).setDuration(animationDuration)
-                    .setInterpolator(interpolator).start();
-
-            final ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
-                    selectorView.getLayoutParams();
-            ViewGroup rootView = (ViewGroup) selectorView.getParent();
-            sTempRect.set(0, 0, focusChangedView.getWidth(), focusChangedView.getHeight());
-            rootView.offsetDescendantRectToMyCoords(focusChangedView, sTempRect);
-            if (isDetails) {
-                if (layoutDirection == View.LAYOUT_DIRECTION_RTL ) {
-                    sTempRect.right += rootView.getHeight();
-                    sTempRect.left -= rootView.getHeight() / 2;
-                } else {
-                    sTempRect.left -= rootView.getHeight();
-                    sTempRect.right += rootView.getHeight() / 2;
-                }
-            }
-            final int targetLeft = sTempRect.left;
-            final int targetWidth = sTempRect.width();
-            final float deltaWidth = lp.width - targetWidth;
-            final float deltaLeft = lp.leftMargin - targetLeft;
-
-            if (deltaLeft == 0f && deltaWidth == 0f)
-            {
-                // no change needed
-            } else if (currentAlpha == 0f) {
-                // change selector to the proper width and marginLeft without animation.
-                lp.width = targetWidth;
-                lp.leftMargin = targetLeft;
-                selectorView.requestLayout();
-            } else {
-                // animate the selector to the proper width and marginLeft.
-                layoutAnimator = ValueAnimator.ofFloat(0f, 1f);
-                layoutAnimator.setDuration(animationDuration);
-                layoutAnimator.setInterpolator(interpolator);
-
-                layoutAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                        // Set width to the proper width for this animation step.
-                        float fractionToEnd = 1f - valueAnimator.getAnimatedFraction();
-                        lp.leftMargin = Math.round(targetLeft + deltaLeft * fractionToEnd);
-                        lp.width = Math.round(targetWidth + deltaWidth * fractionToEnd);
-                        selectorView.requestLayout();
-                    }
-                });
-                layoutAnimator.start();
-            }
-            return layoutAnimator;
-
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java b/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
deleted file mode 100644
index 9f2e452..0000000
--- a/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
+++ /dev/null
@@ -1,134 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.support.v17.leanback.R;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-/**
- * Abstract presenter class for rendering the header for a list of media items in a playlist.
- * The presenter creates a {@link ViewHolder} for the TextView holding the header text.
- * <p>
- *    Subclasses of this class must override {@link
- *    #onBindMediaListHeaderViewHolder(ViewHolder, Object)} in order to bind their header text to
- *    the media list header view.
- * </p>
- * <p>
- * {@link AbstractMediaItemPresenter} can be used in conjunction with this presenter in order to
- * display a playlist with a header view.
- * </p>
- */
-public abstract class AbstractMediaListHeaderPresenter extends RowPresenter{
-
-    private final Context mContext;
-    private int mBackgroundColor = Color.TRANSPARENT;
-    private boolean mBackgroundColorSet;
-
-    /**
-     * The ViewHolder for the {@link AbstractMediaListHeaderPresenter}. It references the TextView
-     * that places the header text provided by the data binder.
-     */
-    public static class ViewHolder extends RowPresenter.ViewHolder {
-
-        private final TextView mHeaderView;
-
-        public ViewHolder(View view) {
-            super(view);
-            mHeaderView = (TextView) view.findViewById(R.id.mediaListHeader);
-        }
-
-        /**
-         *
-         * @return the header {@link TextView} responsible for rendering the playlist header text.
-         */
-        public TextView getHeaderView() {
-            return mHeaderView;
-        }
-    }
-
-    /**
-     * Constructor used for creating an abstract media-list header presenter of a given theme.
-     * @param context The context the user of this presenter is running in.
-     * @param mThemeResId The resource id of the desired theme used for styling of this presenter.
-     */
-    public AbstractMediaListHeaderPresenter(Context context, int mThemeResId) {
-        mContext = new ContextThemeWrapper(context.getApplicationContext(), mThemeResId);
-        setHeaderPresenter(null);
-    }
-
-    /**
-     * Constructor used for creating an abstract media-list header presenter.
-     * The styling for this presenter is extracted from Context of parent in
-     * {@link #createRowViewHolder(ViewGroup)}.
-     */
-    public AbstractMediaListHeaderPresenter() {
-        mContext = null;
-        setHeaderPresenter(null);
-    }
-
-    @Override
-    public boolean isUsingDefaultSelectEffect() {
-        return false;
-    }
-
-    @Override
-    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
-        Context context = (mContext != null) ? mContext : parent.getContext();
-        View view = LayoutInflater.from(context).inflate(R.layout.lb_media_list_header,
-                parent, false);
-        view.setFocusable(false);
-        view.setFocusableInTouchMode(false);
-        ViewHolder vh = new ViewHolder(view);
-        if (mBackgroundColorSet) {
-            vh.view.setBackgroundColor(mBackgroundColor);
-        }
-        return vh;
-    }
-
-    @Override
-    protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
-        super.onBindRowViewHolder(vh, item);
-        onBindMediaListHeaderViewHolder((ViewHolder) vh, item);
-    }
-
-    /**
-     * Sets the background color for the row views within the playlist.
-     * If this is not set, a default color, defaultBrandColor, from theme is used.
-     * This defaultBrandColor defaults to android:attr/colorPrimary on v21, if it's specified.
-     * @param color The ARGB color used to set as the header text background color.
-     */
-    public void setBackgroundColor(int color) {
-        mBackgroundColorSet = true;
-        mBackgroundColor = color;
-    }
-
-    /**
-     * Binds the playlist header data model provided by the user to the {@link ViewHolder}
-     * provided by the {@link AbstractMediaListHeaderPresenter}.
-     * The subclasses of this presenter can access and bind the text view corresponding to the
-     * header by calling {@link ViewHolder#getHeaderView()}, on the
-     * {@link ViewHolder} provided as the argument {@code vh} by this presenter.
-     *
-     * @param vh The ViewHolder for this {@link AbstractMediaListHeaderPresenter}.
-     * @param item The header data object being presented.
-     */
-    protected abstract void onBindMediaListHeaderViewHolder(ViewHolder vh, Object item);
-
-}
diff --git a/android/support/v17/leanback/widget/Action.java b/android/support/v17/leanback/widget/Action.java
deleted file mode 100644
index 715fdd9..0000000
--- a/android/support/v17/leanback/widget/Action.java
+++ /dev/null
@@ -1,175 +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.v17.leanback.widget;
-
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-
-/**
- * An action contains one or two lines of text, an optional image and an optional id. It may also
- * be invoked by one or more keycodes.
- */
-public class Action {
-
-    /** Indicates that an id has not been set. */
-    public static final long NO_ID = -1;
-
-    private long mId = NO_ID;
-    private Drawable mIcon;
-    private CharSequence mLabel1;
-    private CharSequence mLabel2;
-    private ArrayList<Integer> mKeyCodes = new ArrayList<>();
-
-    /**
-     * Constructor for an Action.
-     *
-     * @param id The id of the Action.
-     */
-    public Action(long id) {
-        this(id, "");
-    }
-
-    /**
-     * Constructor for an Action.
-     *
-     * @param id The id of the Action.
-     * @param label The label to display for the Action.
-     */
-    public Action(long id, CharSequence label) {
-        this(id, label, null);
-    }
-
-    /**
-     * Constructor for an Action.
-     *
-     * @param id The id of the Action.
-     * @param label1 The label to display on the first line of the Action.
-     * @param label2 The label to display on the second line of the Action.
-     */
-    public Action(long id, CharSequence label1, CharSequence label2) {
-        this(id, label1, label2, null);
-    }
-
-    /**
-     * Constructor for an Action.
-     *
-     * @param id The id of the Action.
-     * @param label1 The label to display on the first line of the Action.
-     * @param label2 The label to display on the second line of the Action.
-     * @param icon The icon to display for the Action.
-     */
-    public Action(long id, CharSequence label1, CharSequence label2, Drawable icon) {
-        setId(id);
-        setLabel1(label1);
-        setLabel2(label2);
-        setIcon(icon);
-    }
-
-    /**
-     * Sets the id for this Action.
-     */
-    public final void setId(long id) {
-        mId = id;
-    }
-
-    /**
-     * Returns the id for this Action.
-     */
-    public final long getId() {
-        return mId;
-    }
-
-    /**
-     * Sets the first line label for this Action.
-     */
-    public final void setLabel1(CharSequence label) {
-        mLabel1 = label;
-    }
-
-    /**
-     * Returns the first line label for this Action.
-     */
-    public final CharSequence getLabel1() {
-        return mLabel1;
-    }
-
-    /**
-     * Sets the second line label for this Action.
-     */
-    public final void setLabel2(CharSequence label) {
-        mLabel2 = label;
-    }
-
-    /**
-     * Returns the second line label for this Action.
-     */
-    public final CharSequence getLabel2() {
-        return mLabel2;
-    }
-
-    /**
-     * Sets the icon drawable for this Action.
-     */
-    public final void setIcon(Drawable icon) {
-        mIcon = icon;
-    }
-
-    /**
-     * Returns the icon drawable for this Action.
-     */
-    public final Drawable getIcon() {
-        return mIcon;
-    }
-
-    /**
-     * Adds a keycode used to invoke this Action.
-     */
-    public final void addKeyCode(int keyCode) {
-        mKeyCodes.add(keyCode);
-    }
-
-    /**
-     * Removes a keycode used to invoke this Action.
-     */
-    public final void removeKeyCode(int keyCode) {
-        mKeyCodes.remove(keyCode);
-    }
-
-    /**
-     * Returns true if the Action should respond to the given keycode.
-     */
-    public final boolean respondsToKeyCode(int keyCode) {
-        return mKeyCodes.contains(keyCode);
-    }
-
-    @Override
-    public String toString(){
-        StringBuilder sb = new StringBuilder();
-        if (!TextUtils.isEmpty(mLabel1)) {
-            sb.append(mLabel1);
-        }
-        if (!TextUtils.isEmpty(mLabel2)) {
-            if (!TextUtils.isEmpty(mLabel1)) {
-                sb.append(" ");
-            }
-            sb.append(mLabel2);
-        }
-        if (mIcon != null && sb.length() == 0) {
-            sb.append("(action icon)");
-        }
-        return sb.toString();
-    }
-}
diff --git a/android/support/v17/leanback/widget/ActionPresenterSelector.java b/android/support/v17/leanback/widget/ActionPresenterSelector.java
deleted file mode 100644
index a018c2e..0000000
--- a/android/support/v17/leanback/widget/ActionPresenterSelector.java
+++ /dev/null
@@ -1,134 +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.v17.leanback.widget;
-
-import android.graphics.drawable.Drawable;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-class ActionPresenterSelector extends PresenterSelector {
-
-    private final Presenter mOneLineActionPresenter = new OneLineActionPresenter();
-    private final Presenter mTwoLineActionPresenter = new TwoLineActionPresenter();
-    private final Presenter[] mPresenters = new Presenter[] {
-            mOneLineActionPresenter, mTwoLineActionPresenter};
-
-    @Override
-    public Presenter getPresenter(Object item) {
-        Action action = (Action) item;
-        if (TextUtils.isEmpty(action.getLabel2())) {
-            return mOneLineActionPresenter;
-        } else {
-            return mTwoLineActionPresenter;
-        }
-    }
-
-    @Override
-    public Presenter[] getPresenters() {
-        return mPresenters;
-    }
-
-    static class ActionViewHolder extends Presenter.ViewHolder {
-        Action mAction;
-        Button mButton;
-        int mLayoutDirection;
-
-        public ActionViewHolder(View view, int layoutDirection) {
-            super(view);
-            mButton = (Button) view.findViewById(R.id.lb_action_button);
-            mLayoutDirection = layoutDirection;
-        }
-    }
-
-    abstract static class ActionPresenter extends Presenter {
-        @Override
-        public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-            Action action = (Action) item;
-            ActionViewHolder vh = (ActionViewHolder) viewHolder;
-            vh.mAction = action;
-            Drawable icon = action.getIcon();
-            if (icon != null) {
-                final int startPadding = vh.view.getResources()
-                        .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_start);
-                final int endPadding = vh.view.getResources()
-                        .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_end);
-                vh.view.setPaddingRelative(startPadding, 0, endPadding, 0);
-            } else {
-                final int padding = vh.view.getResources()
-                        .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal);
-                vh.view.setPaddingRelative(padding, 0, padding, 0);
-            }
-            if (vh.mLayoutDirection == View.LAYOUT_DIRECTION_RTL) {
-                vh.mButton.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null);
-            } else {
-                vh.mButton.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
-            }
-        }
-
-        @Override
-        public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-            ActionViewHolder vh = (ActionViewHolder) viewHolder;
-            vh.mButton.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
-            vh.view.setPadding(0, 0, 0, 0);
-            vh.mAction = null;
-        }
-    }
-
-    static class OneLineActionPresenter extends ActionPresenter {
-        @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent) {
-            View v = LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.lb_action_1_line, parent, false);
-            return new ActionViewHolder(v, parent.getLayoutDirection());
-        }
-
-        @Override
-        public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-            super.onBindViewHolder(viewHolder, item);
-            ActionViewHolder vh = ((ActionViewHolder) viewHolder);
-            Action action = (Action) item;
-            vh.mButton.setText(action.getLabel1());
-        }
-    }
-
-    static class TwoLineActionPresenter extends ActionPresenter {
-        @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent) {
-            View v = LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.lb_action_2_lines, parent, false);
-            return new ActionViewHolder(v, parent.getLayoutDirection());
-        }
-
-        @Override
-        public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-            super.onBindViewHolder(viewHolder, item);
-            Action action = (Action) item;
-            ActionViewHolder vh = (ActionViewHolder) viewHolder;
-
-            CharSequence line1 = action.getLabel1();
-            CharSequence line2 = action.getLabel2();
-            if (TextUtils.isEmpty(line1)) {
-                vh.mButton.setText(line2);
-            } else if (TextUtils.isEmpty(line2)) {
-                vh.mButton.setText(line1);
-            } else {
-                vh.mButton.setText(line1 + "\n" + line2);
-            }
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/ArrayObjectAdapter.java b/android/support/v17/leanback/widget/ArrayObjectAdapter.java
deleted file mode 100644
index 2dcf51f..0000000
--- a/android/support/v17/leanback/widget/ArrayObjectAdapter.java
+++ /dev/null
@@ -1,324 +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.v17.leanback.widget;
-
-import android.support.annotation.Nullable;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.util.ListUpdateCallback;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * An {@link ObjectAdapter} implemented with an {@link ArrayList}.
- */
-public class ArrayObjectAdapter extends ObjectAdapter {
-
-    private static final Boolean DEBUG = false;
-    private static final String TAG = "ArrayObjectAdapter";
-
-    private final List mItems = new ArrayList<Object>();
-
-    // To compute the payload correctly, we should use a temporary list to hold all the old items.
-    private final List mOldItems = new ArrayList<Object>();
-
-    // Un modifiable version of mItems;
-    private List mUnmodifiableItems;
-
-    /**
-     * Constructs an adapter with the given {@link PresenterSelector}.
-     */
-    public ArrayObjectAdapter(PresenterSelector presenterSelector) {
-        super(presenterSelector);
-    }
-
-    /**
-     * Constructs an adapter that uses the given {@link Presenter} for all items.
-     */
-    public ArrayObjectAdapter(Presenter presenter) {
-        super(presenter);
-    }
-
-    /**
-     * Constructs an adapter.
-     */
-    public ArrayObjectAdapter() {
-        super();
-    }
-
-    @Override
-    public int size() {
-        return mItems.size();
-    }
-
-    @Override
-    public Object get(int index) {
-        return mItems.get(index);
-    }
-
-    /**
-     * Returns the index for the first occurrence of item in the adapter, or -1 if
-     * not found.
-     *
-     * @param item The item to find in the list.
-     * @return Index of the first occurrence of the item in the adapter, or -1
-     * if not found.
-     */
-    public int indexOf(Object item) {
-        return mItems.indexOf(item);
-    }
-
-    /**
-     * Notify that the content of a range of items changed. Note that this is
-     * not same as items being added or removed.
-     *
-     * @param positionStart The position of first item that has changed.
-     * @param itemCount     The count of how many items have changed.
-     */
-    public void notifyArrayItemRangeChanged(int positionStart, int itemCount) {
-        notifyItemRangeChanged(positionStart, itemCount);
-    }
-
-    /**
-     * Adds an item to the end of the adapter.
-     *
-     * @param item The item to add to the end of the adapter.
-     */
-    public void add(Object item) {
-        add(mItems.size(), item);
-    }
-
-    /**
-     * Inserts an item into this adapter at the specified index.
-     * If the index is > {@link #size} an exception will be thrown.
-     *
-     * @param index The index at which the item should be inserted.
-     * @param item  The item to insert into the adapter.
-     */
-    public void add(int index, Object item) {
-        mItems.add(index, item);
-        notifyItemRangeInserted(index, 1);
-    }
-
-    /**
-     * Adds the objects in the given collection to the adapter, starting at the
-     * given index.  If the index is >= {@link #size} an exception will be thrown.
-     *
-     * @param index The index at which the items should be inserted.
-     * @param items A {@link Collection} of items to insert.
-     */
-    public void addAll(int index, Collection items) {
-        int itemsCount = items.size();
-        if (itemsCount == 0) {
-            return;
-        }
-        mItems.addAll(index, items);
-        notifyItemRangeInserted(index, itemsCount);
-    }
-
-    /**
-     * Removes the first occurrence of the given item from the adapter.
-     *
-     * @param item The item to remove from the adapter.
-     * @return True if the item was found and thus removed from the adapter.
-     */
-    public boolean remove(Object item) {
-        int index = mItems.indexOf(item);
-        if (index >= 0) {
-            mItems.remove(index);
-            notifyItemRangeRemoved(index, 1);
-        }
-        return index >= 0;
-    }
-
-    /**
-     * Moved the item at fromPosition to toPosition.
-     *
-     * @param fromPosition Previous position of the item.
-     * @param toPosition   New position of the item.
-     */
-    public void move(int fromPosition, int toPosition) {
-        if (fromPosition == toPosition) {
-            // no-op
-            return;
-        }
-        Object item = mItems.remove(fromPosition);
-        mItems.add(toPosition, item);
-        notifyItemMoved(fromPosition, toPosition);
-    }
-
-    /**
-     * Replaces item at position with a new item and calls notifyItemRangeChanged()
-     * at the given position.  Note that this method does not compare new item to
-     * existing item.
-     *
-     * @param position The index of item to replace.
-     * @param item     The new item to be placed at given position.
-     */
-    public void replace(int position, Object item) {
-        mItems.set(position, item);
-        notifyItemRangeChanged(position, 1);
-    }
-
-    /**
-     * Removes a range of items from the adapter. The range is specified by giving
-     * the starting position and the number of elements to remove.
-     *
-     * @param position The index of the first item to remove.
-     * @param count    The number of items to remove.
-     * @return The number of items removed.
-     */
-    public int removeItems(int position, int count) {
-        int itemsToRemove = Math.min(count, mItems.size() - position);
-        if (itemsToRemove <= 0) {
-            return 0;
-        }
-
-        for (int i = 0; i < itemsToRemove; i++) {
-            mItems.remove(position);
-        }
-        notifyItemRangeRemoved(position, itemsToRemove);
-        return itemsToRemove;
-    }
-
-    /**
-     * Removes all items from this adapter, leaving it empty.
-     */
-    public void clear() {
-        int itemCount = mItems.size();
-        if (itemCount == 0) {
-            return;
-        }
-        mItems.clear();
-        notifyItemRangeRemoved(0, itemCount);
-    }
-
-    /**
-     * Gets a read-only view of the list of object of this ArrayObjectAdapter.
-     */
-    public <E> List<E> unmodifiableList() {
-
-        // The mUnmodifiableItems will only be created once as long as the content of mItems has not
-        // been changed.
-        if (mUnmodifiableItems == null) {
-            mUnmodifiableItems = Collections.unmodifiableList(mItems);
-        }
-        return mUnmodifiableItems;
-    }
-
-    @Override
-    public boolean isImmediateNotifySupported() {
-        return true;
-    }
-
-    ListUpdateCallback mListUpdateCallback;
-
-    /**
-     * Set a new item list to adapter. The DiffUtil will compute the difference and dispatch it to
-     * specified position.
-     *
-     * @param itemList List of new Items
-     * @param callback Optional DiffCallback Object to compute the difference between the old data
-     *                 set and new data set. When null, {@link #notifyChanged()} will be fired.
-     */
-    public void setItems(final List itemList, final DiffCallback callback) {
-        if (callback == null) {
-            // shortcut when DiffCallback is not provided
-            mItems.clear();
-            mItems.addAll(itemList);
-            notifyChanged();
-            return;
-        }
-        mOldItems.clear();
-        mOldItems.addAll(mItems);
-
-        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
-            @Override
-            public int getOldListSize() {
-                return mOldItems.size();
-            }
-
-            @Override
-            public int getNewListSize() {
-                return itemList.size();
-            }
-
-            @Override
-            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-                return callback.areItemsTheSame(mOldItems.get(oldItemPosition),
-                        itemList.get(newItemPosition));
-            }
-
-            @Override
-            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
-                return callback.areContentsTheSame(mOldItems.get(oldItemPosition),
-                        itemList.get(newItemPosition));
-            }
-
-            @Nullable
-            @Override
-            public Object getChangePayload(int oldItemPosition, int newItemPosition) {
-                return callback.getChangePayload(mOldItems.get(oldItemPosition),
-                        itemList.get(newItemPosition));
-            }
-        });
-
-        // update items.
-        mItems.clear();
-        mItems.addAll(itemList);
-
-        // dispatch diff result
-        if (mListUpdateCallback == null) {
-            mListUpdateCallback = new ListUpdateCallback() {
-
-                @Override
-                public void onInserted(int position, int count) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onInserted");
-                    }
-                    notifyItemRangeInserted(position, count);
-                }
-
-                @Override
-                public void onRemoved(int position, int count) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onRemoved");
-                    }
-                    notifyItemRangeRemoved(position, count);
-                }
-
-                @Override
-                public void onMoved(int fromPosition, int toPosition) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onMoved");
-                    }
-                    notifyItemMoved(fromPosition, toPosition);
-                }
-
-                @Override
-                public void onChanged(int position, int count, Object payload) {
-                    if (DEBUG) {
-                        Log.d(TAG, "onChanged");
-                    }
-                    notifyItemRangeChanged(position, count, payload);
-                }
-            };
-        }
-        diffResult.dispatchUpdatesTo(mListUpdateCallback);
-        mOldItems.clear();
-    }
-}
diff --git a/android/support/v17/leanback/widget/BackgroundHelper.java b/android/support/v17/leanback/widget/BackgroundHelper.java
deleted file mode 100644
index 487799d..0000000
--- a/android/support/v17/leanback/widget/BackgroundHelper.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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.view.View;
-
-/**
- * Helper for view backgrounds.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class BackgroundHelper {
-    public static void setBackgroundPreservingAlpha(View view, Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            if (view.getBackground() != null) {
-                drawable.setAlpha(view.getBackground().getAlpha());
-            }
-            view.setBackground(drawable);
-        } else {
-            // Cannot query drawable alpha
-            view.setBackground(drawable);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/BaseCardView.java b/android/support/v17/leanback/widget/BaseCardView.java
deleted file mode 100644
index 735bb99..0000000
--- a/android/support/v17/leanback/widget/BaseCardView.java
+++ /dev/null
@@ -1,1007 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Animation;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Transformation;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-
-/**
- * A card style layout that responds to certain state changes. It arranges its
- * children in a vertical column, with different regions becoming visible at
- * different times.
- *
- * <p>
- * A BaseCardView will draw its children based on its type, the region
- * visibilities of the child types, and the state of the widget. A child may be
- * marked as belonging to one of three regions: main, info, or extra. The main
- * region is always visible, while the info and extra regions can be set to
- * display based on the activated or selected state of the View. The card states
- * are set by calling {@link #setActivated(boolean) setActivated} and
- * {@link #setSelected(boolean) setSelected}.
- * <p>
- * See {@link BaseCardView.LayoutParams} for layout attributes.
- * </p>
- */
-public class BaseCardView extends FrameLayout {
-    private static final String TAG = "BaseCardView";
-    private static final boolean DEBUG = false;
-
-    /**
-     * A simple card type with a single layout area. This card type does not
-     * change its layout or size as it transitions between
-     * Activated/Not-Activated or Selected/Unselected states.
-     *
-     * @see #getCardType()
-     */
-    public static final int CARD_TYPE_MAIN_ONLY = 0;
-
-    /**
-     * A Card type with 2 layout areas: A main area which is always visible, and
-     * an info area that fades in over the main area when it is visible.
-     * The card height will not change.
-     *
-     * @see #getCardType()
-     */
-    public static final int CARD_TYPE_INFO_OVER = 1;
-
-    /**
-     * A Card type with 2 layout areas: A main area which is always visible, and
-     * an info area that appears below the main area. When the info area is visible
-     * the total card height will change.
-     *
-     * @see #getCardType()
-     */
-    public static final int CARD_TYPE_INFO_UNDER = 2;
-
-    /**
-     * A Card type with 3 layout areas: A main area which is always visible; an
-     * info area which will appear below the main area, and an extra area that
-     * only appears after a short delay. The info area appears below the main
-     * area, causing the total card height to change. The extra area animates in
-     * at the bottom of the card, shifting up the info view without affecting
-     * the card height.
-     *
-     * @see #getCardType()
-     */
-    public static final int CARD_TYPE_INFO_UNDER_WITH_EXTRA = 3;
-
-    /**
-     * Indicates that a card region is always visible.
-     */
-    public static final int CARD_REGION_VISIBLE_ALWAYS = 0;
-
-    /**
-     * Indicates that a card region is visible when the card is activated.
-     */
-    public static final int CARD_REGION_VISIBLE_ACTIVATED = 1;
-
-    /**
-     * Indicates that a card region is visible when the card is selected.
-     */
-    public static final int CARD_REGION_VISIBLE_SELECTED = 2;
-
-    private static final int CARD_TYPE_INVALID = 4;
-
-    private int mCardType;
-    private int mInfoVisibility;
-    private int mExtraVisibility;
-
-    private ArrayList<View> mMainViewList;
-    ArrayList<View> mInfoViewList;
-    ArrayList<View> mExtraViewList;
-
-    private int mMeasuredWidth;
-    private int mMeasuredHeight;
-    private boolean mDelaySelectedAnim;
-    private int mSelectedAnimationDelay;
-    private final int mActivatedAnimDuration;
-    private final int mSelectedAnimDuration;
-
-    /**
-     * Distance of top of info view to bottom of MainView, it will shift up when extra view appears.
-     */
-    float mInfoOffset;
-    float mInfoVisFraction;
-    float mInfoAlpha;
-    private Animation mAnim;
-
-    private final static int[] LB_PRESSED_STATE_SET = new int[]{
-        android.R.attr.state_pressed};
-
-    private final Runnable mAnimationTrigger = new Runnable() {
-        @Override
-        public void run() {
-            animateInfoOffset(true);
-        }
-    };
-
-    public BaseCardView(Context context) {
-        this(context, null);
-    }
-
-    public BaseCardView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.baseCardViewStyle);
-    }
-
-    public BaseCardView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseCardView,
-                defStyleAttr, 0);
-
-        try {
-            mCardType = a.getInteger(R.styleable.lbBaseCardView_cardType, CARD_TYPE_MAIN_ONLY);
-            Drawable cardForeground = a.getDrawable(R.styleable.lbBaseCardView_cardForeground);
-            if (cardForeground != null) {
-                setForeground(cardForeground);
-            }
-            Drawable cardBackground = a.getDrawable(R.styleable.lbBaseCardView_cardBackground);
-            if (cardBackground != null) {
-                setBackground(cardBackground);
-            }
-            mInfoVisibility = a.getInteger(R.styleable.lbBaseCardView_infoVisibility,
-                    CARD_REGION_VISIBLE_ACTIVATED);
-            mExtraVisibility = a.getInteger(R.styleable.lbBaseCardView_extraVisibility,
-                    CARD_REGION_VISIBLE_SELECTED);
-            // Extra region should never show before info region.
-            if (mExtraVisibility < mInfoVisibility) {
-                mExtraVisibility = mInfoVisibility;
-            }
-
-            mSelectedAnimationDelay = a.getInteger(
-                    R.styleable.lbBaseCardView_selectedAnimationDelay,
-                    getResources().getInteger(R.integer.lb_card_selected_animation_delay));
-
-            mSelectedAnimDuration = a.getInteger(
-                    R.styleable.lbBaseCardView_selectedAnimationDuration,
-                    getResources().getInteger(R.integer.lb_card_selected_animation_duration));
-
-            mActivatedAnimDuration =
-                    a.getInteger(R.styleable.lbBaseCardView_activatedAnimationDuration,
-                    getResources().getInteger(R.integer.lb_card_activated_animation_duration));
-        } finally {
-            a.recycle();
-        }
-
-        mDelaySelectedAnim = true;
-
-        mMainViewList = new ArrayList<View>();
-        mInfoViewList = new ArrayList<View>();
-        mExtraViewList = new ArrayList<View>();
-
-        mInfoOffset = 0.0f;
-        mInfoVisFraction = getFinalInfoVisFraction();
-        mInfoAlpha = getFinalInfoAlpha();
-    }
-
-    /**
-     * Sets a flag indicating if the Selected animation (if the selected card
-     * type implements one) should run immediately after the card is selected,
-     * or if it should be delayed. The default behavior is to delay this
-     * animation. This is a one-shot override. If set to false, after the card
-     * is selected and the selected animation is triggered, this flag is
-     * automatically reset to true. This is useful when you want to change the
-     * default behavior, and have the selected animation run immediately. One
-     * such case could be when focus moves from one row to the other, when
-     * instead of delaying the selected animation until the user pauses on a
-     * card, it may be desirable to trigger the animation for that card
-     * immediately.
-     *
-     * @param delay True (default) if the selected animation should be delayed
-     *            after the card is selected, or false if the animation should
-     *            run immediately the next time the card is Selected.
-     */
-    public void setSelectedAnimationDelayed(boolean delay) {
-        mDelaySelectedAnim = delay;
-    }
-
-    /**
-     * Returns a boolean indicating if the selected animation will run
-     * immediately or be delayed the next time the card is Selected.
-     *
-     * @return true if this card is set to delay the selected animation the next
-     *         time it is selected, or false if the selected animation will run
-     *         immediately the next time the card is selected.
-     */
-    public boolean isSelectedAnimationDelayed() {
-        return mDelaySelectedAnim;
-    }
-
-    /**
-     * Sets the type of this Card.
-     *
-     * @param type The desired card type.
-     */
-    public void setCardType(int type) {
-        if (mCardType != type) {
-            if (type >= CARD_TYPE_MAIN_ONLY && type < CARD_TYPE_INVALID) {
-                // Valid card type
-                mCardType = type;
-            } else {
-                Log.e(TAG, "Invalid card type specified: " + type
-                        + ". Defaulting to type CARD_TYPE_MAIN_ONLY.");
-                mCardType = CARD_TYPE_MAIN_ONLY;
-            }
-            requestLayout();
-        }
-    }
-
-    /**
-     * Returns the type of this Card.
-     *
-     * @return The type of this card.
-     */
-    public int getCardType() {
-        return mCardType;
-    }
-
-    /**
-     * Sets the visibility of the info region of the card.
-     *
-     * @param visibility The region visibility to use for the info region. Must
-     *     be one of {@link #CARD_REGION_VISIBLE_ALWAYS},
-     *     {@link #CARD_REGION_VISIBLE_SELECTED}, or
-     *     {@link #CARD_REGION_VISIBLE_ACTIVATED}.
-     */
-    public void setInfoVisibility(int visibility) {
-        if (mInfoVisibility != visibility) {
-            cancelAnimations();
-            mInfoVisibility = visibility;
-            mInfoVisFraction = getFinalInfoVisFraction();
-            requestLayout();
-            float newInfoAlpha = getFinalInfoAlpha();
-            if (newInfoAlpha != mInfoAlpha) {
-                mInfoAlpha = newInfoAlpha;
-                for (int i = 0; i < mInfoViewList.size(); i++) {
-                    mInfoViewList.get(i).setAlpha(mInfoAlpha);
-                }
-            }
-        }
-    }
-
-    final float getFinalInfoVisFraction() {
-        return mCardType == CARD_TYPE_INFO_UNDER && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED
-                && !isSelected() ? 0.0f : 1.0f;
-    }
-
-    final float getFinalInfoAlpha() {
-        return mCardType == CARD_TYPE_INFO_OVER && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED
-                && !isSelected() ? 0.0f : 1.0f;
-    }
-
-    /**
-     * Returns the visibility of the info region of the card.
-     */
-    public int getInfoVisibility() {
-        return mInfoVisibility;
-    }
-
-    /**
-     * Sets the visibility of the extra region of the card.
-     *
-     * @param visibility The region visibility to use for the extra region. Must
-     *     be one of {@link #CARD_REGION_VISIBLE_ALWAYS},
-     *     {@link #CARD_REGION_VISIBLE_SELECTED}, or
-     *     {@link #CARD_REGION_VISIBLE_ACTIVATED}.
-     * @deprecated Extra view's visibility is controlled by {@link #setInfoVisibility(int)}
-     */
-    @Deprecated
-    public void setExtraVisibility(int visibility) {
-        if (mExtraVisibility != visibility) {
-            mExtraVisibility = visibility;
-        }
-    }
-
-    /**
-     * Returns the visibility of the extra region of the card.
-     * @deprecated Extra view's visibility is controlled by {@link #getInfoVisibility()}
-     */
-    @Deprecated
-    public int getExtraVisibility() {
-        return mExtraVisibility;
-    }
-
-    /**
-     * Sets the Activated state of this Card. This can trigger changes in the
-     * card layout, resulting in views to become visible or hidden. A card is
-     * normally set to Activated state when its parent container (like a Row)
-     * receives focus, and then activates all of its children.
-     *
-     * @param activated True if the card is ACTIVE, or false if INACTIVE.
-     * @see #isActivated()
-     */
-    @Override
-    public void setActivated(boolean activated) {
-        if (activated != isActivated()) {
-            super.setActivated(activated);
-            applyActiveState(isActivated());
-        }
-    }
-
-    /**
-     * Sets the Selected state of this Card. This can trigger changes in the
-     * card layout, resulting in views to become visible or hidden. A card is
-     * normally set to Selected state when it receives input focus.
-     *
-     * @param selected True if the card is Selected, or false otherwise.
-     * @see #isSelected()
-     */
-    @Override
-    public void setSelected(boolean selected) {
-        if (selected != isSelected()) {
-            super.setSelected(selected);
-            applySelectedState(isSelected());
-        }
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return false;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mMeasuredWidth = 0;
-        mMeasuredHeight = 0;
-        int state = 0;
-        int mainHeight = 0;
-        int infoHeight = 0;
-        int extraHeight = 0;
-
-        findChildrenViews();
-
-        final int unspecifiedSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        // MAIN is always present
-        for (int i = 0; i < mMainViewList.size(); i++) {
-            View mainView = mMainViewList.get(i);
-            if (mainView.getVisibility() != View.GONE) {
-                measureChild(mainView, unspecifiedSpec, unspecifiedSpec);
-                mMeasuredWidth = Math.max(mMeasuredWidth, mainView.getMeasuredWidth());
-                mainHeight += mainView.getMeasuredHeight();
-                state = View.combineMeasuredStates(state, mainView.getMeasuredState());
-            }
-        }
-        setPivotX(mMeasuredWidth / 2);
-        setPivotY(mainHeight / 2);
-
-
-        // The MAIN area determines the card width
-        int cardWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMeasuredWidth, MeasureSpec.EXACTLY);
-
-        if (hasInfoRegion()) {
-            for (int i = 0; i < mInfoViewList.size(); i++) {
-                View infoView = mInfoViewList.get(i);
-                if (infoView.getVisibility() != View.GONE) {
-                    measureChild(infoView, cardWidthMeasureSpec, unspecifiedSpec);
-                    if (mCardType != CARD_TYPE_INFO_OVER) {
-                        infoHeight += infoView.getMeasuredHeight();
-                    }
-                    state = View.combineMeasuredStates(state, infoView.getMeasuredState());
-                }
-            }
-
-            if (hasExtraRegion()) {
-                for (int i = 0; i < mExtraViewList.size(); i++) {
-                    View extraView = mExtraViewList.get(i);
-                    if (extraView.getVisibility() != View.GONE) {
-                        measureChild(extraView, cardWidthMeasureSpec, unspecifiedSpec);
-                        extraHeight += extraView.getMeasuredHeight();
-                        state = View.combineMeasuredStates(state, extraView.getMeasuredState());
-                    }
-                }
-            }
-        }
-
-        boolean infoAnimating = hasInfoRegion() && mInfoVisibility == CARD_REGION_VISIBLE_SELECTED;
-        mMeasuredHeight = (int) (mainHeight
-                + (infoAnimating ? (infoHeight * mInfoVisFraction) : infoHeight)
-                + extraHeight - (infoAnimating ? 0 : mInfoOffset));
-
-        // Report our final dimensions.
-        setMeasuredDimension(View.resolveSizeAndState(mMeasuredWidth + getPaddingLeft()
-                + getPaddingRight(), widthMeasureSpec, state),
-                View.resolveSizeAndState(mMeasuredHeight + getPaddingTop() + getPaddingBottom(),
-                        heightMeasureSpec, state << View.MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        float currBottom = getPaddingTop();
-
-        // MAIN is always present
-        for (int i = 0; i < mMainViewList.size(); i++) {
-            View mainView = mMainViewList.get(i);
-            if (mainView.getVisibility() != View.GONE) {
-                mainView.layout(getPaddingLeft(),
-                        (int) currBottom,
-                                mMeasuredWidth + getPaddingLeft(),
-                        (int) (currBottom + mainView.getMeasuredHeight()));
-                currBottom += mainView.getMeasuredHeight();
-            }
-        }
-
-        if (hasInfoRegion()) {
-            float infoHeight = 0f;
-            for (int i = 0; i < mInfoViewList.size(); i++) {
-                infoHeight += mInfoViewList.get(i).getMeasuredHeight();
-            }
-
-            if (mCardType == CARD_TYPE_INFO_OVER) {
-                // retract currBottom to overlap the info views on top of main
-                currBottom -= infoHeight;
-                if (currBottom < 0) {
-                    currBottom = 0;
-                }
-            } else if (mCardType == CARD_TYPE_INFO_UNDER) {
-                if (mInfoVisibility == CARD_REGION_VISIBLE_SELECTED) {
-                    infoHeight = infoHeight * mInfoVisFraction;
-                }
-            } else {
-                currBottom -= mInfoOffset;
-            }
-
-            for (int i = 0; i < mInfoViewList.size(); i++) {
-                View infoView = mInfoViewList.get(i);
-                if (infoView.getVisibility() != View.GONE) {
-                    int viewHeight = infoView.getMeasuredHeight();
-                    if (viewHeight > infoHeight) {
-                        viewHeight = (int) infoHeight;
-                    }
-                    infoView.layout(getPaddingLeft(),
-                            (int) currBottom,
-                                    mMeasuredWidth + getPaddingLeft(),
-                            (int) (currBottom + viewHeight));
-                    currBottom += viewHeight;
-                    infoHeight -= viewHeight;
-                    if (infoHeight <= 0) {
-                        break;
-                    }
-                }
-            }
-
-            if (hasExtraRegion()) {
-                for (int i = 0; i < mExtraViewList.size(); i++) {
-                    View extraView = mExtraViewList.get(i);
-                    if (extraView.getVisibility() != View.GONE) {
-                        extraView.layout(getPaddingLeft(),
-                                (int) currBottom,
-                                        mMeasuredWidth + getPaddingLeft(),
-                                (int) (currBottom + extraView.getMeasuredHeight()));
-                        currBottom += extraView.getMeasuredHeight();
-                    }
-                }
-            }
-        }
-        // Force update drawable bounds.
-        onSizeChanged(0, 0, right - left, bottom - top);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        removeCallbacks(mAnimationTrigger);
-        cancelAnimations();
-    }
-
-    private boolean hasInfoRegion() {
-        return mCardType != CARD_TYPE_MAIN_ONLY;
-    }
-
-    private boolean hasExtraRegion() {
-        return mCardType == CARD_TYPE_INFO_UNDER_WITH_EXTRA;
-    }
-
-    /**
-     * Returns target visibility of info region.
-     */
-    private boolean isRegionVisible(int regionVisibility) {
-        switch (regionVisibility) {
-            case CARD_REGION_VISIBLE_ALWAYS:
-                return true;
-            case CARD_REGION_VISIBLE_ACTIVATED:
-                return isActivated();
-            case CARD_REGION_VISIBLE_SELECTED:
-                return isSelected();
-            default:
-                if (DEBUG) Log.e(TAG, "invalid region visibility state: " + regionVisibility);
-                return false;
-        }
-    }
-
-    /**
-     * Unlike isRegionVisible(), this method returns true when it is fading out when unselected.
-     */
-    private boolean isCurrentRegionVisible(int regionVisibility) {
-        switch (regionVisibility) {
-            case CARD_REGION_VISIBLE_ALWAYS:
-                return true;
-            case CARD_REGION_VISIBLE_ACTIVATED:
-                return isActivated();
-            case CARD_REGION_VISIBLE_SELECTED:
-                if (mCardType == CARD_TYPE_INFO_UNDER) {
-                    return mInfoVisFraction > 0f;
-                } else {
-                    return isSelected();
-                }
-            default:
-                if (DEBUG) Log.e(TAG, "invalid region visibility state: " + regionVisibility);
-                return false;
-        }
-    }
-
-    private void findChildrenViews() {
-        mMainViewList.clear();
-        mInfoViewList.clear();
-        mExtraViewList.clear();
-
-        final int count = getChildCount();
-
-        boolean infoVisible = hasInfoRegion() && isCurrentRegionVisible(mInfoVisibility);
-        boolean extraVisible = hasExtraRegion() && mInfoOffset > 0f;
-
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-
-            if (child == null) {
-                continue;
-            }
-
-            BaseCardView.LayoutParams lp = (BaseCardView.LayoutParams) child
-                    .getLayoutParams();
-            if (lp.viewType == LayoutParams.VIEW_TYPE_INFO) {
-                child.setAlpha(mInfoAlpha);
-                mInfoViewList.add(child);
-                child.setVisibility(infoVisible ? View.VISIBLE : View.GONE);
-            } else if (lp.viewType == LayoutParams.VIEW_TYPE_EXTRA) {
-                mExtraViewList.add(child);
-                child.setVisibility(extraVisible ? View.VISIBLE : View.GONE);
-            } else {
-                // Default to MAIN
-                mMainViewList.add(child);
-                child.setVisibility(View.VISIBLE);
-            }
-        }
-
-    }
-
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-        // filter out focus states,  since leanback does not fade foreground on focus.
-        final int[] s = super.onCreateDrawableState(extraSpace);
-        final int N = s.length;
-        boolean pressed = false;
-        boolean enabled = false;
-        for (int i = 0; i < N; i++) {
-            if (s[i] == android.R.attr.state_pressed) {
-                pressed = true;
-            }
-            if (s[i] == android.R.attr.state_enabled) {
-                enabled = true;
-            }
-        }
-        if (pressed && enabled) {
-            return View.PRESSED_ENABLED_STATE_SET;
-        } else if (pressed) {
-            return LB_PRESSED_STATE_SET;
-        } else if (enabled) {
-            return View.ENABLED_STATE_SET;
-        } else {
-            return View.EMPTY_STATE_SET;
-        }
-    }
-
-    private void applyActiveState(boolean active) {
-        if (hasInfoRegion() && mInfoVisibility == CARD_REGION_VISIBLE_ACTIVATED) {
-            setInfoViewVisibility(isRegionVisible(mInfoVisibility));
-        }
-    }
-
-    private void setInfoViewVisibility(boolean visible) {
-        if (mCardType == CARD_TYPE_INFO_UNDER_WITH_EXTRA) {
-            // Active state changes for card type
-            // CARD_TYPE_INFO_UNDER_WITH_EXTRA
-            if (visible) {
-                for (int i = 0; i < mInfoViewList.size(); i++) {
-                    mInfoViewList.get(i).setVisibility(View.VISIBLE);
-                }
-            } else {
-                for (int i = 0; i < mInfoViewList.size(); i++) {
-                    mInfoViewList.get(i).setVisibility(View.GONE);
-                }
-                for (int i = 0; i < mExtraViewList.size(); i++) {
-                    mExtraViewList.get(i).setVisibility(View.GONE);
-                }
-                mInfoOffset = 0.0f;
-            }
-        } else if (mCardType == CARD_TYPE_INFO_UNDER) {
-            // Active state changes for card type CARD_TYPE_INFO_UNDER
-            if (mInfoVisibility == CARD_REGION_VISIBLE_SELECTED) {
-                animateInfoHeight(visible);
-            } else {
-                for (int i = 0; i < mInfoViewList.size(); i++) {
-                    mInfoViewList.get(i).setVisibility(visible ? View.VISIBLE : View.GONE);
-                }
-            }
-        } else if (mCardType == CARD_TYPE_INFO_OVER) {
-            // Active state changes for card type CARD_TYPE_INFO_OVER
-            animateInfoAlpha(visible);
-        }
-    }
-
-    private void applySelectedState(boolean focused) {
-        removeCallbacks(mAnimationTrigger);
-
-        if (mCardType == CARD_TYPE_INFO_UNDER_WITH_EXTRA) {
-            // Focus changes for card type CARD_TYPE_INFO_UNDER_WITH_EXTRA
-            if (focused) {
-                if (!mDelaySelectedAnim) {
-                    post(mAnimationTrigger);
-                    mDelaySelectedAnim = true;
-                } else {
-                    postDelayed(mAnimationTrigger, mSelectedAnimationDelay);
-                }
-            } else {
-                animateInfoOffset(false);
-            }
-        } else if (mInfoVisibility == CARD_REGION_VISIBLE_SELECTED) {
-            setInfoViewVisibility(focused);
-        }
-    }
-
-    private void cancelAnimations() {
-        if (mAnim != null) {
-            mAnim.cancel();
-            mAnim = null;
-            // force-clear the animation, as Animation#cancel() doesn't work prior to N,
-            // and will instead cause the animation to infinitely loop
-            clearAnimation();
-        }
-    }
-
-    // This animation changes the Y offset of the info and extra views,
-    // so that they animate UP to make the extra info area visible when a
-    // card is selected.
-    void animateInfoOffset(boolean shown) {
-        cancelAnimations();
-
-        int extraHeight = 0;
-        if (shown) {
-            int widthSpec = MeasureSpec.makeMeasureSpec(mMeasuredWidth, MeasureSpec.EXACTLY);
-            int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-            for (int i = 0; i < mExtraViewList.size(); i++) {
-                View extraView = mExtraViewList.get(i);
-                extraView.setVisibility(View.VISIBLE);
-                extraView.measure(widthSpec, heightSpec);
-                extraHeight = Math.max(extraHeight, extraView.getMeasuredHeight());
-            }
-        }
-
-        mAnim = new InfoOffsetAnimation(mInfoOffset, shown ? extraHeight : 0);
-        mAnim.setDuration(mSelectedAnimDuration);
-        mAnim.setInterpolator(new AccelerateDecelerateInterpolator());
-        mAnim.setAnimationListener(new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) {
-            }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                if (mInfoOffset == 0f) {
-                    for (int i = 0; i < mExtraViewList.size(); i++) {
-                        mExtraViewList.get(i).setVisibility(View.GONE);
-                    }
-                }
-            }
-
-                @Override
-            public void onAnimationRepeat(Animation animation) {
-            }
-
-        });
-        startAnimation(mAnim);
-    }
-
-    // This animation changes the visible height of the info views,
-    // so that they animate in and out of view.
-    private void animateInfoHeight(boolean shown) {
-        cancelAnimations();
-
-        if (shown) {
-            for (int i = 0; i < mInfoViewList.size(); i++) {
-                View extraView = mInfoViewList.get(i);
-                extraView.setVisibility(View.VISIBLE);
-            }
-        }
-
-        float targetFraction = shown ? 1.0f : 0f;
-        if (mInfoVisFraction == targetFraction) {
-            return;
-        }
-        mAnim = new InfoHeightAnimation(mInfoVisFraction, targetFraction);
-        mAnim.setDuration(mSelectedAnimDuration);
-        mAnim.setInterpolator(new AccelerateDecelerateInterpolator());
-        mAnim.setAnimationListener(new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) {
-            }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                if (mInfoVisFraction == 0f) {
-                    for (int i = 0; i < mInfoViewList.size(); i++) {
-                        mInfoViewList.get(i).setVisibility(View.GONE);
-                    }
-                }
-            }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) {
-            }
-
-        });
-        startAnimation(mAnim);
-    }
-
-    // This animation changes the alpha of the info views, so they animate in
-    // and out. It's meant to be used when the info views are overlaid on top of
-    // the main view area. It gets triggered by a change in the Active state of
-    // the card.
-    private void animateInfoAlpha(boolean shown) {
-        cancelAnimations();
-
-        if (shown) {
-            for (int i = 0; i < mInfoViewList.size(); i++) {
-                mInfoViewList.get(i).setVisibility(View.VISIBLE);
-            }
-        }
-        float targetAlpha = shown ? 1.0f : 0.0f;
-        if (targetAlpha == mInfoAlpha) {
-            return;
-        }
-
-        mAnim = new InfoAlphaAnimation(mInfoAlpha, shown ? 1.0f : 0.0f);
-        mAnim.setDuration(mActivatedAnimDuration);
-        mAnim.setInterpolator(new DecelerateInterpolator());
-        mAnim.setAnimationListener(new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) {
-            }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                if (mInfoAlpha == 0.0) {
-                    for (int i = 0; i < mInfoViewList.size(); i++) {
-                        mInfoViewList.get(i).setVisibility(View.GONE);
-                    }
-                }
-            }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) {
-            }
-
-        });
-        startAnimation(mAnim);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new BaseCardView.LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new BaseCardView.LayoutParams(
-                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
-        if (lp instanceof LayoutParams) {
-            return new LayoutParams((LayoutParams) lp);
-        } else {
-            return new LayoutParams(lp);
-        }
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof BaseCardView.LayoutParams;
-    }
-
-    /**
-     * Per-child layout information associated with BaseCardView.
-     */
-    public static class LayoutParams extends FrameLayout.LayoutParams {
-        public static final int VIEW_TYPE_MAIN = 0;
-        public static final int VIEW_TYPE_INFO = 1;
-        public static final int VIEW_TYPE_EXTRA = 2;
-
-        /**
-         * Card component type for the view associated with these LayoutParams.
-         */
-        @ViewDebug.ExportedProperty(category = "layout", mapping = {
-                @ViewDebug.IntToString(from = VIEW_TYPE_MAIN, to = "MAIN"),
-                @ViewDebug.IntToString(from = VIEW_TYPE_INFO, to = "INFO"),
-                @ViewDebug.IntToString(from = VIEW_TYPE_EXTRA, to = "EXTRA")
-        })
-        public int viewType = VIEW_TYPE_MAIN;
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.lbBaseCardView_Layout);
-
-            viewType = a.getInt(
-                    R.styleable.lbBaseCardView_Layout_layout_viewType, VIEW_TYPE_MAIN);
-
-            a.recycle();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.LayoutParams p) {
-            super(p);
-        }
-
-        /**
-         * Copy constructor. Clones the width, height, and View Type of the
-         * source.
-         *
-         * @param source The layout params to copy from.
-         */
-        public LayoutParams(LayoutParams source) {
-            super((ViewGroup.MarginLayoutParams) source);
-
-            this.viewType = source.viewType;
-        }
-    }
-
-    class AnimationBase extends Animation {
-
-        @VisibleForTesting
-        final void mockStart() {
-            getTransformation(0, null);
-        }
-
-        @VisibleForTesting
-        final void mockEnd() {
-            applyTransformation(1f, null);
-            cancelAnimations();
-        }
-    }
-
-    // Helper animation class used in the animation of the info and extra
-    // fields vertically within the card
-    final class InfoOffsetAnimation extends AnimationBase {
-        private float mStartValue;
-        private float mDelta;
-
-        public InfoOffsetAnimation(float start, float end) {
-            mStartValue = start;
-            mDelta = end - start;
-        }
-
-        @Override
-        protected void applyTransformation(float interpolatedTime, Transformation t) {
-            mInfoOffset = mStartValue + (interpolatedTime * mDelta);
-            requestLayout();
-        }
-    }
-
-    // Helper animation class used in the animation of the visible height
-    // for the info fields.
-    final class InfoHeightAnimation extends AnimationBase {
-        private float mStartValue;
-        private float mDelta;
-
-        public InfoHeightAnimation(float start, float end) {
-            mStartValue = start;
-            mDelta = end - start;
-        }
-
-        @Override
-        protected void applyTransformation(float interpolatedTime, Transformation t) {
-            mInfoVisFraction = mStartValue + (interpolatedTime * mDelta);
-            requestLayout();
-        }
-    }
-
-    // Helper animation class used to animate the alpha for the info views
-    // when they are fading in or out of view.
-    final class InfoAlphaAnimation extends AnimationBase {
-        private float mStartValue;
-        private float mDelta;
-
-        public InfoAlphaAnimation(float start, float end) {
-            mStartValue = start;
-            mDelta = end - start;
-        }
-
-        @Override
-        protected void applyTransformation(float interpolatedTime, Transformation t) {
-            mInfoAlpha = mStartValue + (interpolatedTime * mDelta);
-            for (int i = 0; i < mInfoViewList.size(); i++) {
-                mInfoViewList.get(i).setAlpha(mInfoAlpha);
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        if (DEBUG) {
-            StringBuilder sb = new StringBuilder();
-            sb.append(this.getClass().getSimpleName()).append(" : ");
-            sb.append("cardType=");
-            switch(mCardType) {
-                case CARD_TYPE_MAIN_ONLY:
-                    sb.append("MAIN_ONLY");
-                    break;
-                case CARD_TYPE_INFO_OVER:
-                    sb.append("INFO_OVER");
-                    break;
-                case CARD_TYPE_INFO_UNDER:
-                    sb.append("INFO_UNDER");
-                    break;
-                case CARD_TYPE_INFO_UNDER_WITH_EXTRA:
-                    sb.append("INFO_UNDER_WITH_EXTRA");
-                    break;
-                default:
-                    sb.append("INVALID");
-                    break;
-            }
-            sb.append(" : ");
-            sb.append(mMainViewList.size()).append(" main views, ");
-            sb.append(mInfoViewList.size()).append(" info views, ");
-            sb.append(mExtraViewList.size()).append(" extra views : ");
-            sb.append("infoVisibility=").append(mInfoVisibility).append(" ");
-            sb.append("extraVisibility=").append(mExtraVisibility).append(" ");
-            sb.append("isActivated=").append(isActivated());
-            sb.append(" : ");
-            sb.append("isSelected=").append(isSelected());
-            return sb.toString();
-        } else {
-            return super.toString();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/BaseGridView.java b/android/support/v17/leanback/widget/BaseGridView.java
deleted file mode 100644
index 2ebec47..0000000
--- a/android/support/v17/leanback/widget/BaseGridView.java
+++ /dev/null
@@ -1,1202 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SimpleItemAnimator;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * 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}.
- * The class is not intended to be subclassed other than {@link VerticalGridView} and
- * {@link HorizontalGridView}.
- */
-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 item is aligned to a key line location.
-     * <p>
-     * 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 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 item is aligned to a key line location.
-     * <p>
-     * 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 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
-     * key line location.
-     * <p>
-     * 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.
-     */
-    public final static int WINDOW_ALIGN_BOTH_EDGE =
-            WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE;
-
-    /**
-     * The focused item always stays in a key line location.
-     * <p>
-     * 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.
-     */
-    public final static int WINDOW_ALIGN_NO_EDGE = 0;
-
-    /**
-     * Value indicates that percent is not used.
-     */
-    public final static float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1;
-
-    /**
-     * Value indicates that percent is not used.
-     */
-    public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED =
-            ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
-
-    /**
-     * Dont save states of any child views.
-     */
-    public static final int SAVE_NO_CHILD = 0;
-
-    /**
-     * Only save on screen child views, the states are lost when they become off screen.
-     */
-    public static final int SAVE_ON_SCREEN_CHILD = 1;
-
-    /**
-     * Save on screen views plus save off screen child views states up to
-     * {@link #getSaveChildrenLimitNumber()}.
-     */
-    public static final int SAVE_LIMITED_CHILD = 2;
-
-    /**
-     * Save on screen views plus save off screen child views without any limitation.
-     * This might cause out of memory, only use it when you are dealing with limited data.
-     */
-    public static final int SAVE_ALL_CHILD = 3;
-
-    /**
-     * Listener for intercepting touch dispatch events.
-     */
-    public interface OnTouchInterceptListener {
-        /**
-         * Returns true if the touch dispatch event should be consumed.
-         */
-        public boolean onInterceptTouchEvent(MotionEvent event);
-    }
-
-    /**
-     * Listener for intercepting generic motion dispatch events.
-     */
-    public interface OnMotionInterceptListener {
-        /**
-         * Returns true if the touch dispatch event should be consumed.
-         */
-        public boolean onInterceptMotionEvent(MotionEvent event);
-    }
-
-    /**
-     * Listener for intercepting key dispatch events.
-     */
-    public interface OnKeyInterceptListener {
-        /**
-         * Returns true if the key dispatch event should be consumed.
-         */
-        public boolean onInterceptKeyEvent(KeyEvent event);
-    }
-
-    public interface OnUnhandledKeyListener {
-        /**
-         * Returns true if the key event should be consumed.
-         */
-        public boolean onUnhandledKey(KeyEvent event);
-    }
-
-    final GridLayoutManager mLayoutManager;
-
-    /**
-     * Animate layout changes from a child resizing or adding/removing a child.
-     */
-    private boolean mAnimateChildLayout = true;
-
-    private boolean mHasOverlappingRendering = true;
-
-    private RecyclerView.ItemAnimator mSavedItemAnimator;
-
-    private OnTouchInterceptListener mOnTouchInterceptListener;
-    private OnMotionInterceptListener mOnMotionInterceptListener;
-    private OnKeyInterceptListener mOnKeyInterceptListener;
-    RecyclerView.RecyclerListener mChainedRecyclerListener;
-    private OnUnhandledKeyListener mOnUnhandledKeyListener;
-
-    /**
-     * Number of items to prefetch when first coming on screen with new data.
-     */
-    int mInitialPrefetchItemCount = 4;
-
-    BaseGridView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mLayoutManager = new GridLayoutManager(this);
-        setLayoutManager(mLayoutManager);
-        // leanback LayoutManager already restores focus inside onLayoutChildren().
-        setPreserveFocusAfterLayout(false);
-        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
-        setHasFixedSize(true);
-        setChildrenDrawingOrderEnabled(true);
-        setWillNotDraw(true);
-        setOverScrollMode(View.OVER_SCROLL_NEVER);
-        // Disable change animation by default on leanback.
-        // Change animation will create a new view and cause undesired
-        // focus animation between the old view and new view.
-        ((SimpleItemAnimator)getItemAnimator()).setSupportsChangeAnimations(false);
-        super.setRecyclerListener(new RecyclerView.RecyclerListener() {
-            @Override
-            public void onViewRecycled(RecyclerView.ViewHolder holder) {
-                mLayoutManager.onChildRecycled(holder);
-                if (mChainedRecyclerListener != null) {
-                    mChainedRecyclerListener.onViewRecycled(holder);
-                }
-            }
-        });
-    }
-
-    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);
-        mLayoutManager.setFocusOutAllowed(throughFront, throughEnd);
-        boolean throughSideStart = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideStart, true);
-        boolean throughSideEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutSideEnd, true);
-        mLayoutManager.setFocusOutSideAllowed(throughSideStart, throughSideEnd);
-        mLayoutManager.setVerticalSpacing(
-                a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_verticalSpacing,
-                        a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0)));
-        mLayoutManager.setHorizontalSpacing(
-                a.getDimensionPixelSize(R.styleable.lbBaseGridView_android_horizontalSpacing,
-                        a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0)));
-        if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
-            setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
-        }
-        a.recycle();
-    }
-
-    /**
-     * Sets the strategy used to scroll in response to item focus changing:
-     * <ul>
-     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
-     * <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) {
-            throw new IllegalArgumentException("Invalid scrollStrategy");
-        }
-        mLayoutManager.setFocusScrollStrategy(scrollStrategy);
-        requestLayout();
-    }
-
-    /**
-     * Returns the strategy used to scroll in response to item focus changing.
-     * <ul>
-     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
-     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
-     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
-     * </ul>
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int getFocusScrollStrategy() {
-        return mLayoutManager.getFocusScrollStrategy();
-    }
-
-    /**
-     * Sets the method for focused item alignment in the view.
-     *
-     * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE},
-     *        {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or
-     *        {@link #WINDOW_ALIGN_NO_EDGE}.
-     */
-    public void setWindowAlignment(int windowAlignment) {
-        mLayoutManager.setWindowAlignment(windowAlignment);
-        requestLayout();
-    }
-
-    /**
-     * Returns the method for focused item alignment in the view.
-     *
-     * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE},
-     *         {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}.
-     */
-    public int getWindowAlignment() {
-        return mLayoutManager.getWindowAlignment();
-    }
-
-    /**
-     * 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});
-     *        if the offset is negative, the absolute value is distance from high
-     *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
-     *        Default value is 0.
-     */
-    public void setWindowAlignmentOffset(int offset) {
-        mLayoutManager.setWindowAlignmentOffset(offset);
-        requestLayout();
-    }
-
-    /**
-     * 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});
-     *        if the offset is negative, the absolute value is distance from high
-     *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
-     *        Default value is 0.
-     */
-    public int getWindowAlignmentOffset() {
-        return mLayoutManager.getWindowAlignmentOffset();
-    }
-
-    /**
-     * 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
-     *        width from low edge. Use
-     *        {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
-     *         Default value is 50.
-     */
-    public void setWindowAlignmentOffsetPercent(float offsetPercent) {
-        mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent);
-        requestLayout();
-    }
-
-    /**
-     * 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
-     *         low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if
-     *         disabled. Default value is 50.
-     */
-    public float getWindowAlignmentOffsetPercent() {
-        return mLayoutManager.getWindowAlignmentOffsetPercent();
-    }
-
-    /**
-     * 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 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);
-        requestLayout();
-    }
-
-    /**
-     * 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 the end of low edge.
-     */
-    public int getItemAlignmentOffset() {
-        return mLayoutManager.getItemAlignmentOffset();
-    }
-
-    /**
-     * 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);
-        requestLayout();
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * Sets the offset percent for item alignment in addition to {@link
-     * #getItemAlignmentOffset()}.
-     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
-     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
-     *
-     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
-     *        width from the low edge. Use
-     *        {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
-     */
-    public void setItemAlignmentOffsetPercent(float offsetPercent) {
-        mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent);
-        requestLayout();
-    }
-
-    /**
-     * Returns the offset percent for item alignment in addition to {@link
-     * #getItemAlignmentOffset()}.
-     *
-     * @return Percentage to offset. E.g., 40 means 40% of the width from the
-     *         low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if
-     *         disabled. Default value is 50.
-     */
-    public float getItemAlignmentOffsetPercent() {
-        return mLayoutManager.getItemAlignmentOffsetPercent();
-    }
-
-    /**
-     * Sets the id of the view to align with. Use {@link android.view.View#NO_ID} (default)
-     * 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) {
-        mLayoutManager.setItemAlignmentViewId(viewId);
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * Sets the spacing in pixels between two child items.
-     * @deprecated use {@link #setItemSpacing(int)}
-     */
-    @Deprecated
-    public void setItemMargin(int margin) {
-        setItemSpacing(margin);
-    }
-
-    /**
-     * 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);
-        requestLayout();
-    }
-
-    /**
-     * Sets the spacing in pixels between two child items vertically.
-     * @deprecated Use {@link #setVerticalSpacing(int)}
-     */
-    @Deprecated
-    public void setVerticalMargin(int margin) {
-        setVerticalSpacing(margin);
-    }
-
-    /**
-     * Returns the spacing in pixels between two child items vertically.
-     * @deprecated Use {@link #getVerticalSpacing()}
-     */
-    @Deprecated
-    public int getVerticalMargin() {
-        return mLayoutManager.getVerticalSpacing();
-    }
-
-    /**
-     * Sets the spacing in pixels between two child items horizontally.
-     * @deprecated Use {@link #setHorizontalSpacing(int)}
-     */
-    @Deprecated
-    public void setHorizontalMargin(int margin) {
-        setHorizontalSpacing(margin);
-    }
-
-    /**
-     * Returns the spacing in pixels between two child items horizontally.
-     * @deprecated Use {@link #getHorizontalSpacing()}
-     */
-    @Deprecated
-    public int getHorizontalMargin() {
-        return mLayoutManager.getHorizontalSpacing();
-    }
-
-    /**
-     * 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);
-        requestLayout();
-    }
-
-    /**
-     * 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 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);
-        requestLayout();
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * Registers a callback to be invoked when an item in BaseGridView has
-     * been laid out.
-     *
-     * @param listener The listener to be invoked.
-     */
-    public void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
-        mLayoutManager.setOnChildLaidOutListener(listener);
-    }
-
-    /**
-     * Registers a callback to be invoked when an item in BaseGridView has
-     * been selected.  Note that the listener may be invoked when there is a
-     * layout pending on the view, affording the listener an opportunity to
-     * adjust the upcoming layout based on the selection state.
-     *
-     * @param listener The listener to be invoked.
-     */
-    public void setOnChildSelectedListener(OnChildSelectedListener listener) {
-        mLayoutManager.setOnChildSelectedListener(listener);
-    }
-
-    /**
-     * Registers a callback to be invoked when an item in BaseGridView has
-     * been selected.  Note that the listener may be invoked when there is a
-     * layout pending on the view, affording the listener an opportunity to
-     * adjust the upcoming layout based on the selection state.
-     * This method will clear all existing listeners added by
-     * {@link #addOnChildViewHolderSelectedListener}.
-     *
-     * @param listener The listener to be invoked.
-     */
-    public void setOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
-        mLayoutManager.setOnChildViewHolderSelectedListener(listener);
-    }
-
-    /**
-     * Registers a callback to be invoked when an item in BaseGridView has
-     * been selected.  Note that the listener may be invoked when there is a
-     * layout pending on the view, affording the listener an opportunity to
-     * adjust the upcoming layout based on the selection state.
-     *
-     * @param listener The listener to be invoked.
-     */
-    public void addOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
-        mLayoutManager.addOnChildViewHolderSelectedListener(listener);
-    }
-
-    /**
-     * Remove the callback invoked when an item in BaseGridView has been selected.
-     *
-     * @param listener The listener to be removed.
-     */
-    public void removeOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener)
-            {
-        mLayoutManager.removeOnChildViewHolderSelectedListener(listener);
-    }
-
-    /**
-     * Changes the selected item immediately without animation.
-     */
-    public void setSelectedPosition(int position) {
-        mLayoutManager.setSelection(position, 0);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Changes the selected item immediately without animation, scrollExtra is
-     * applied in primary scroll direction.  The scrollExtra will be kept until
-     * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
-     */
-    public void setSelectedPosition(int position, int scrollExtra) {
-        mLayoutManager.setSelection(position, scrollExtra);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Perform a task on ViewHolder at given position after smooth scrolling to it.
-     * @param position Position of item in adapter.
-     * @param task Task to executed on the ViewHolder at a given position.
-     */
-    public void setSelectedPositionSmooth(final int position, final ViewHolderTask task) {
-        if (task != null) {
-            RecyclerView.ViewHolder vh = findViewHolderForPosition(position);
-            if (vh == null || hasPendingAdapterUpdates()) {
-                addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
-                    @Override
-                    public void onChildViewHolderSelected(RecyclerView parent,
-                            RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
-                        if (selectedPosition == position) {
-                            removeOnChildViewHolderSelectedListener(this);
-                            task.run(child);
-                        }
-                    }
-                });
-            } else {
-                task.run(vh);
-            }
-        }
-        setSelectedPositionSmooth(position);
-    }
-
-    /**
-     * Perform a task on ViewHolder at given position after scroll to it.
-     * @param position Position of item in adapter.
-     * @param task Task to executed on the ViewHolder at a given position.
-     */
-    public void setSelectedPosition(final int position, final ViewHolderTask task) {
-        if (task != null) {
-            RecyclerView.ViewHolder vh = findViewHolderForPosition(position);
-            if (vh == null || hasPendingAdapterUpdates()) {
-                addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() {
-                    @Override
-                    public void onChildViewHolderSelectedAndPositioned(RecyclerView parent,
-                            RecyclerView.ViewHolder child, int selectedPosition, int subposition) {
-                        if (selectedPosition == position) {
-                            removeOnChildViewHolderSelectedListener(this);
-                            task.run(child);
-                        }
-                    }
-                });
-            } else {
-                task.run(vh);
-            }
-        }
-        setSelectedPosition(position);
-    }
-
-    /**
-     * Returns the adapter position of selected item.
-     * @return The adapter position of selected item.
-     */
-    public int getSelectedPosition() {
-        return mLayoutManager.getSelection();
-    }
-
-    /**
-     * Returns the sub selected item position started from zero.  An item can have
-     * 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 ItemAnimator should run when a child changes size or when adding
-     * or removing a child.
-     * @param animateChildLayout True to enable ItemAnimator, false to disable.
-     */
-    public void setAnimateChildLayout(boolean animateChildLayout) {
-        if (mAnimateChildLayout != animateChildLayout) {
-            mAnimateChildLayout = animateChildLayout;
-            if (!mAnimateChildLayout) {
-                mSavedItemAnimator = getItemAnimator();
-                super.setItemAnimator(null);
-            } else {
-                super.setItemAnimator(mSavedItemAnimator);
-            }
-        }
-    }
-
-    /**
-     * Returns true if an animation will run when a child changes size or when
-     * adding or removing a child.
-     * @return True if ItemAnimator is enabled, false otherwise.
-     */
-    public boolean isChildLayoutAnimated() {
-        return mAnimateChildLayout;
-    }
-
-    /**
-     * Sets the gravity used for child view positioning. Defaults to
-     * GRAVITY_TOP|GRAVITY_START.
-     *
-     * @param gravity See {@link android.view.Gravity}
-     */
-    public void setGravity(int gravity) {
-        mLayoutManager.setGravity(gravity);
-        requestLayout();
-    }
-
-    @Override
-    public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-        return mLayoutManager.gridOnRequestFocusInDescendants(this, direction,
-                previouslyFocusedRect);
-    }
-
-    /**
-     * Returns the x/y offsets to final position from current position if the view
-     * is selected.
-     *
-     * @param view The view to get offsets.
-     * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of Y.
-     */
-    public void getViewSelectedOffsets(View view, int[] offsets) {
-        mLayoutManager.getViewSelectedOffsets(view, offsets);
-    }
-
-    @Override
-    public int getChildDrawingOrder(int childCount, int i) {
-        return mLayoutManager.getChildDrawingOrder(this, childCount, i);
-    }
-
-    final boolean isChildrenDrawingOrderEnabledInternal() {
-        return isChildrenDrawingOrderEnabled();
-    }
-
-    @Override
-    public View focusSearch(int direction) {
-        if (isFocused()) {
-            // focusSearch(int) is called when GridView itself is focused.
-            // Calling focusSearch(view, int) to get next sibling of current selected child.
-            View view = mLayoutManager.findViewByPosition(mLayoutManager.getSelection());
-            if (view != null) {
-                return focusSearch(view, direction);
-            }
-        }
-        // otherwise, go to mParent to perform focusSearch
-        return super.focusSearch(direction);
-    }
-
-    @Override
-    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        mLayoutManager.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-    }
-
-    /**
-     * 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
-        // re-gain focus after a BACK key pressed, so block children focus during transition.
-        setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS: FOCUS_AFTER_DESCENDANTS);
-        mLayoutManager.setFocusSearchDisabled(disabled);
-    }
-
-    /**
-     * Returns true if focus search is disabled.
-     * @return True if focus search is disabled.
-     */
-    public final boolean isFocusSearchDisabled() {
-        return mLayoutManager.isFocusSearchDisabled();
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Changes and overrides children's visibility.
-     * @param visibility See {@link View#getVisibility()}.
-     */
-    public void setChildrenVisibility(int visibility) {
-        mLayoutManager.setChildrenVisibility(visibility);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * 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, false otherwise.
-     * @return True if scrolling is enabled, false otherwise.
-     */
-    public boolean isScrollEnabled() {
-        return mLayoutManager.isScrollEnabled();
-    }
-
-    /**
-     * 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.
-     *
-     * @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. 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 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();
-    }
-
-    /**
-     * Sets the touch intercept listener.
-     * @param listener The touch intercept listener.
-     */
-    public void setOnTouchInterceptListener(OnTouchInterceptListener listener) {
-        mOnTouchInterceptListener = listener;
-    }
-
-    /**
-     * Sets the generic motion intercept listener.
-     * @param listener The motion intercept listener.
-     */
-    public void setOnMotionInterceptListener(OnMotionInterceptListener listener) {
-        mOnMotionInterceptListener = listener;
-    }
-
-    /**
-     * Sets the key intercept listener.
-     * @param listener The key intercept listener.
-     */
-    public void setOnKeyInterceptListener(OnKeyInterceptListener listener) {
-        mOnKeyInterceptListener = listener;
-    }
-
-    /**
-     * Sets the unhandled key listener.
-     * @param listener The unhandled key intercept listener.
-     */
-    public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
-        mOnUnhandledKeyListener = listener;
-    }
-
-    /**
-     * Returns the unhandled key listener.
-     * @return The unhandled key listener.
-     */
-    public OnUnhandledKeyListener getOnUnhandledKeyListener() {
-        return mOnUnhandledKeyListener;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (mOnKeyInterceptListener != null && mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
-            return true;
-        }
-        if (super.dispatchKeyEvent(event)) {
-            return true;
-        }
-        return mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event);
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        if (mOnTouchInterceptListener != null) {
-            if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) {
-                return true;
-            }
-        }
-        return super.dispatchTouchEvent(event);
-    }
-
-    @Override
-    protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
-        if (mOnMotionInterceptListener != null) {
-            if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
-                return true;
-            }
-        }
-        return super.dispatchGenericFocusedEvent(event);
-    }
-
-    /**
-     * Returns the policy for saving children.
-     *
-     * @return policy, one of {@link #SAVE_NO_CHILD}
-     * {@link #SAVE_ON_SCREEN_CHILD} {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
-     */
-    public final int getSaveChildrenPolicy() {
-        return mLayoutManager.mChildrenStates.getSavePolicy();
-    }
-
-    /**
-     * Returns the limit used when when {@link #getSaveChildrenPolicy()} is
-     *         {@link #SAVE_LIMITED_CHILD}
-     */
-    public final int getSaveChildrenLimitNumber() {
-        return mLayoutManager.mChildrenStates.getLimitNumber();
-    }
-
-    /**
-     * Sets the policy for saving children.
-     * @param savePolicy One of {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
-     * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
-     */
-    public final void setSaveChildrenPolicy(int savePolicy) {
-        mLayoutManager.mChildrenStates.setSavePolicy(savePolicy);
-    }
-
-    /**
-     * Sets the limit number when {@link #getSaveChildrenPolicy()} is {@link #SAVE_LIMITED_CHILD}.
-     */
-    public final void setSaveChildrenLimitNumber(int limitNumber) {
-        mLayoutManager.mChildrenStates.setLimitNumber(limitNumber);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return mHasOverlappingRendering;
-    }
-
-    public void setHasOverlappingRendering(boolean hasOverlapping) {
-        mHasOverlappingRendering = hasOverlapping;
-    }
-
-    /**
-     * Notify layout manager that layout directionality has been updated
-     */
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        mLayoutManager.onRtlPropertiesChanged(layoutDirection);
-    }
-
-    @Override
-    public void setRecyclerListener(RecyclerView.RecyclerListener listener) {
-        mChainedRecyclerListener = listener;
-    }
-
-    /**
-     * Sets pixels of extra space for layout child in invisible area.
-     *
-     * @param extraLayoutSpace  Pixels of extra space for layout invisible child.
-     *                          Must be bigger or equals to 0.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setExtraLayoutSpace(int extraLayoutSpace) {
-        mLayoutManager.setExtraLayoutSpace(extraLayoutSpace);
-    }
-
-    /**
-     * Returns pixels of extra space for layout child in invisible area.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int getExtraLayoutSpace() {
-        return mLayoutManager.getExtraLayoutSpace();
-    }
-
-    /**
-     * Temporarily slide out child views to bottom (for VerticalGridView) or end
-     * (for HorizontalGridView). Layout and scrolling will be suppressed until
-     * {@link #animateIn()} is called.
-     */
-    public void animateOut() {
-        mLayoutManager.slideOut();
-    }
-
-    /**
-     * Undo animateOut() and slide in child views.
-     */
-    public void animateIn() {
-        mLayoutManager.slideIn();
-    }
-
-    @Override
-    public void scrollToPosition(int position) {
-        // dont abort the animateOut() animation, just record the position
-        if (mLayoutManager.isSlidingChildViews()) {
-            mLayoutManager.setSelectionWithSub(position, 0, 0);
-            return;
-        }
-        super.scrollToPosition(position);
-    }
-
-    @Override
-    public void smoothScrollToPosition(int position) {
-        // dont abort the animateOut() animation, just record the position
-        if (mLayoutManager.isSlidingChildViews()) {
-            mLayoutManager.setSelectionWithSub(position, 0, 0);
-            return;
-        }
-        super.smoothScrollToPosition(position);
-    }
-
-    /**
-     * Sets the number of items to prefetch in
-     * {@link RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)},
-     * which defines how many inner items should be prefetched when this GridView is nested inside
-     * another RecyclerView.
-     *
-     * <p>Set this value to the number of items this inner GridView will display when it is
-     * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items
-     * so they are ready, avoiding jank as the inner GridView is scrolled into the viewport.</p>
-     *
-     * <p>For example, take a VerticalGridView of scrolling HorizontalGridViews. The rows always
-     * have 6 items visible in them (or 7 if not aligned). Passing <code>6</code> to this method
-     * for each inner GridView will enable RecyclerView's prefetching feature to do create/bind work
-     * for 6 views within a row early, before it is scrolled on screen, instead of just the default
-     * 4.</p>
-     *
-     * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView
-     * nested in another RecyclerView.</p>
-     *
-     * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of
-     * views that will be visible in this view can incur unnecessary bind work, and an increase to
-     * the number of Views created and in active use.</p>
-     *
-     * @param itemCount Number of items to prefetch
-     *
-     * @see #getInitialPrefetchItemCount()
-     * @see RecyclerView.LayoutManager#isItemPrefetchEnabled()
-     * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)
-     */
-    public void setInitialPrefetchItemCount(int itemCount) {
-        mInitialPrefetchItemCount = itemCount;
-    }
-
-    /**
-     * Gets the number of items to prefetch in
-     * {@link RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)},
-     * which defines how many inner items should be prefetched when this GridView is nested inside
-     * another RecyclerView.
-     *
-     * @see RecyclerView.LayoutManager#isItemPrefetchEnabled()
-     * @see #setInitialPrefetchItemCount(int)
-     * @see RecyclerView.LayoutManager#collectInitialPrefetchPositions(int, RecyclerView.LayoutManager.LayoutPrefetchRegistry)
-     *
-     * @return number of items to prefetch.
-     */
-    public int getInitialPrefetchItemCount() {
-        return mInitialPrefetchItemCount;
-    }
-}
diff --git a/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.java b/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.java
deleted file mode 100644
index 738d0e9..0000000
--- a/android/support/v17/leanback/widget/BaseOnItemViewClickedListener.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.v17.leanback.widget;
-
-/**
- * Interface for receiving notification when an item view holder is clicked.
- */
-public interface BaseOnItemViewClickedListener<T> {
-
-    /**
-     * Called when an item inside a row gets clicked.
-     * @param itemViewHolder The view holder of the item that is clicked.
-     * @param item The item that is currently selected.
-     * @param rowViewHolder The view holder of the row which the clicked item belongs to.
-     * @param row The row which the clicked item belongs to.
-     */
-    void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
-                              RowPresenter.ViewHolder rowViewHolder, T row);
-}
diff --git a/android/support/v17/leanback/widget/BaseOnItemViewSelectedListener.java b/android/support/v17/leanback/widget/BaseOnItemViewSelectedListener.java
deleted file mode 100644
index b43e146..0000000
--- a/android/support/v17/leanback/widget/BaseOnItemViewSelectedListener.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.v17.leanback.widget;
-
-/**
- * Interface for receiving notification when a row or item becomes selected. The concept of
- * current selection is different than focus.  A row or item can be selected without having focus;
- * for example, when a row header view gains focus then the corresponding row view becomes selected.
- */
-public interface BaseOnItemViewSelectedListener<T> {
-
-    /**
-     * Called when a row or a new item becomes selected.
-     * <p>
-     * For a non {@link ListRow} case, parameter item may be null.  Event is fired when
-     * selection changes between rows, regardless if row view has focus or not.
-     * <p>
-     * For a {@link ListRow} case, parameter item is null if the list row is empty.
-     * </p>
-     * <p>
-     * In the case of a grid, the row parameter is always null.
-     * </p>
-     * <li>
-     * Row has focus: event is fired when focus changes between children of the row.
-     * </li>
-     * <li>
-     * No row has focus: the event is fired with the currently selected row and last
-     * focused item in the row.
-     * </li>
-     *
-     * @param itemViewHolder The view holder of the item that is currently selected.
-     * @param item The item that is currently selected.
-     * @param rowViewHolder The view holder of the row that is currently selected.
-     * @param row The row that is currently selected.
-     */
-    public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
-                               RowPresenter.ViewHolder rowViewHolder, T row);
-}
diff --git a/android/support/v17/leanback/widget/BrowseFrameLayout.java b/android/support/v17/leanback/widget/BrowseFrameLayout.java
deleted file mode 100644
index 43c4ffb..0000000
--- a/android/support/v17/leanback/widget/BrowseFrameLayout.java
+++ /dev/null
@@ -1,154 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * A ViewGroup for managing focus behavior between overlapping views.
- */
-public class BrowseFrameLayout extends FrameLayout {
-
-    /**
-     * Interface for selecting a focused view in a BrowseFrameLayout when the system focus finder
-     * couldn't find a view to focus.
-     */
-    public interface OnFocusSearchListener {
-        /**
-         * Returns the view where focus should be requested given the current focused view and
-         * the direction of focus search.
-         */
-        View onFocusSearch(View focused, int direction);
-    }
-
-    /**
-     * Interface for managing child focus in a BrowseFrameLayout.
-     */
-    public interface OnChildFocusListener {
-        /**
-         * See {@link android.view.ViewGroup#onRequestFocusInDescendants(
-         * int, android.graphics.Rect)}.
-         * @return True if handled by listener, otherwise returns {@link
-         * android.view.ViewGroup#onRequestFocusInDescendants(int, android.graphics.Rect)}.
-         */
-        boolean onRequestFocusInDescendants(int direction,
-                Rect previouslyFocusedRect);
-        /**
-         * See {@link android.view.ViewGroup#requestChildFocus(
-         * android.view.View, android.view.View)}.
-         */
-        void onRequestChildFocus(View child, View focused);
-    }
-
-    public BrowseFrameLayout(Context context) {
-        this(context, null, 0);
-    }
-
-    public BrowseFrameLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public BrowseFrameLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    private OnFocusSearchListener mListener;
-    private OnChildFocusListener mOnChildFocusListener;
-    private OnKeyListener mOnDispatchKeyListener;
-
-    /**
-     * Sets a {@link OnFocusSearchListener}.
-     */
-    public void setOnFocusSearchListener(OnFocusSearchListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Returns the {@link OnFocusSearchListener}.
-     */
-    public OnFocusSearchListener getOnFocusSearchListener() {
-        return mListener;
-    }
-
-    /**
-     * Sets a {@link OnChildFocusListener}.
-     */
-    public void setOnChildFocusListener(OnChildFocusListener listener) {
-        mOnChildFocusListener = listener;
-    }
-
-    /**
-     * Returns the {@link OnChildFocusListener}.
-     */
-    public OnChildFocusListener getOnChildFocusListener() {
-        return mOnChildFocusListener;
-    }
-
-    @Override
-    protected boolean onRequestFocusInDescendants(int direction,
-            Rect previouslyFocusedRect) {
-        if (mOnChildFocusListener != null) {
-            if (mOnChildFocusListener.onRequestFocusInDescendants(direction,
-                    previouslyFocusedRect)) {
-                return true;
-            }
-        }
-        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    public View focusSearch(View focused, int direction) {
-        if (mListener != null) {
-            View view = mListener.onFocusSearch(focused, direction);
-            if (view != null) {
-                return view;
-            }
-        }
-        return super.focusSearch(focused, direction);
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        if (mOnChildFocusListener != null) {
-            mOnChildFocusListener.onRequestChildFocus(child, focused);
-        }
-        super.requestChildFocus(child, focused);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        boolean consumed = super.dispatchKeyEvent(event);
-        if (mOnDispatchKeyListener != null) {
-            if (!consumed) {
-                return mOnDispatchKeyListener.onKey(getRootView(), event.getKeyCode(), event);
-            }
-        }
-        return consumed;
-    }
-
-    /**
-     * Sets the {@link android.view.View.OnKeyListener} on this view. This listener would fire
-     * only for unhandled {@link KeyEvent}s. We need to provide an external key listener to handle
-     * back button clicks when we are in full screen video mode because
-     * {@link View#setOnKeyListener(OnKeyListener)} doesn't fire as the focus is not on this view.
-     */
-    public void setOnDispatchKeyListener(OnKeyListener listener) {
-        this.mOnDispatchKeyListener = listener;
-    }
-}
diff --git a/android/support/v17/leanback/widget/BrowseRowsFrameLayout.java b/android/support/v17/leanback/widget/BrowseRowsFrameLayout.java
deleted file mode 100644
index 9c65bd1..0000000
--- a/android/support/v17/leanback/widget/BrowseRowsFrameLayout.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.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.FrameLayout;
-
-/**
- * Customized FrameLayout excludes margin of child from calculating the child size.
- * So we can change left margin of rows while keep the width of rows unchanged without
- * using hardcoded DIPS.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class BrowseRowsFrameLayout extends FrameLayout {
-
-    public BrowseRowsFrameLayout(Context context) {
-        this(context ,null);
-    }
-
-    public BrowseRowsFrameLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public BrowseRowsFrameLayout(Context context, AttributeSet attrs,
-            int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected void measureChildWithMargins(View child,
-            int parentWidthMeasureSpec, int widthUsed,
-            int parentHeightMeasureSpec, int heightUsed) {
-        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
-                getPaddingLeft() + getPaddingRight() + widthUsed, lp.width);
-        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
-                getPaddingTop() + getPaddingBottom() + heightUsed, lp.height);
-        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/CheckableImageView.java b/android/support/v17/leanback/widget/CheckableImageView.java
deleted file mode 100644
index 627bbd4..0000000
--- a/android/support/v17/leanback/widget/CheckableImageView.java
+++ /dev/null
@@ -1,69 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Checkable;
-import android.widget.ImageView;
-
-/**
- * ImageView that supports Checkable states.
- */
-class CheckableImageView extends ImageView implements Checkable {
-
-    private boolean mChecked;
-
-    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
-
-    public CheckableImageView(Context context) {
-        this(context, null);
-    }
-
-    public CheckableImageView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public CheckableImageView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    public int[] onCreateDrawableState(final int extraSpace) {
-        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
-        if (isChecked()) {
-            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
-        }
-        return drawableState;
-    }
-
-    @Override
-    public void toggle() {
-        setChecked(!mChecked);
-    }
-
-    @Override
-    public boolean isChecked() {
-        return mChecked;
-    }
-
-    @Override
-    public void setChecked(final boolean checked) {
-        if (mChecked != checked) {
-            mChecked = checked;
-            refreshDrawableState();
-        }
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ClassPresenterSelector.java b/android/support/v17/leanback/widget/ClassPresenterSelector.java
deleted file mode 100644
index 7a0ac20..0000000
--- a/android/support/v17/leanback/widget/ClassPresenterSelector.java
+++ /dev/null
@@ -1,84 +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.v17.leanback.widget;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * A ClassPresenterSelector selects a {@link Presenter} based on the item's
- * Java class.
- */
-public final class ClassPresenterSelector extends PresenterSelector {
-
-    private final ArrayList<Presenter> mPresenters = new ArrayList<Presenter>();
-
-    private final HashMap<Class<?>, Object> mClassMap = new HashMap<Class<?>, Object>();
-
-    /**
-     * Sets a presenter to be used for the given class.
-     * @param cls The data model class to be rendered.
-     * @param presenter The presenter that renders the objects of the given class.
-     * @return This ClassPresenterSelector object.
-     */
-    public ClassPresenterSelector addClassPresenter(Class<?> cls, Presenter presenter) {
-        mClassMap.put(cls, presenter);
-        if (!mPresenters.contains(presenter)) {
-            mPresenters.add(presenter);
-        }
-        return this;
-    }
-
-    /**
-     * Sets a presenter selector to be used for the given class.
-     * @param cls The data model class to be rendered.
-     * @param presenterSelector The presenter selector that finds the right presenter for a given
-     *                          class.
-     * @return This ClassPresenterSelector object.
-     */
-    public ClassPresenterSelector addClassPresenterSelector(Class<?> cls,
-            PresenterSelector presenterSelector) {
-        mClassMap.put(cls, presenterSelector);
-        Presenter[] innerPresenters = presenterSelector.getPresenters();
-        for (int i = 0; i < innerPresenters.length; i++)
-        if (!mPresenters.contains(innerPresenters[i])) {
-            mPresenters.add(innerPresenters[i]);
-        }
-        return this;
-    }
-
-    @Override
-    public Presenter getPresenter(Object item) {
-        Class<?> cls = item.getClass();
-        Object presenter = null;
-
-        do {
-            presenter = mClassMap.get(cls);
-            if (presenter instanceof PresenterSelector) {
-                Presenter innerPresenter = ((PresenterSelector) presenter).getPresenter(item);
-                if (innerPresenter != null) {
-                    return innerPresenter;
-                }
-            }
-            cls = cls.getSuperclass();
-        } while (presenter == null && cls != null);
-
-        return (Presenter) presenter;
-    }
-
-    @Override
-    public Presenter[] getPresenters() {
-        return mPresenters.toArray(new Presenter[mPresenters.size()]);
-    }
-}
diff --git a/android/support/v17/leanback/widget/ControlBar.java b/android/support/v17/leanback/widget/ControlBar.java
deleted file mode 100644
index 1942ae0..0000000
--- a/android/support/v17/leanback/widget/ControlBar.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import java.util.ArrayList;
-
-class ControlBar extends LinearLayout {
-
-    public interface OnChildFocusedListener {
-        public void onChildFocusedListener(View child, View focused);
-    }
-
-    private int mChildMarginFromCenter;
-    private OnChildFocusedListener mOnChildFocusedListener;
-    int mLastFocusIndex = -1;
-    boolean mDefaultFocusToMiddle = true;
-
-    public ControlBar(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public ControlBar(Context context, AttributeSet attrs, int defStyle) {
-        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 : getDefaultFocusIndex();
-            if (getChildAt(index).requestFocus(direction, previouslyFocusedRect)) {
-                return true;
-            }
-        }
-        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if ((direction == ViewGroup.FOCUS_UP || direction == ViewGroup.FOCUS_DOWN)) {
-            if (mLastFocusIndex >= 0 && mLastFocusIndex < getChildCount()) {
-                views.add(getChildAt(mLastFocusIndex));
-            } else if (getChildCount() > 0) {
-                views.add(getChildAt(getDefaultFocusIndex()));
-            }
-        } else {
-            super.addFocusables(views, direction, focusableMode);
-        }
-    }
-
-    public void setOnChildFocusedListener(OnChildFocusedListener listener) {
-        mOnChildFocusedListener = listener;
-    }
-
-    public void setChildMarginFromCenter(int marginFromCenter) {
-        mChildMarginFromCenter = marginFromCenter;
-    }
-
-    @Override
-    public void requestChildFocus (View child, View focused) {
-        super.requestChildFocus(child, focused);
-        mLastFocusIndex = indexOfChild(child);
-        if (mOnChildFocusedListener != null) {
-            mOnChildFocusedListener.onChildFocusedListener(child, focused);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mChildMarginFromCenter <= 0) {
-            return;
-        }
-
-        int totalExtraMargin = 0;
-        for (int i = 0; i < getChildCount() - 1; i++) {
-            View first = getChildAt(i);
-            View second = getChildAt(i+1);
-            int measuredWidth = first.getMeasuredWidth() + second.getMeasuredWidth();
-            int marginStart = mChildMarginFromCenter - measuredWidth / 2;
-            LayoutParams lp = (LayoutParams) second.getLayoutParams();
-            int extraMargin = marginStart - lp.getMarginStart();
-            lp.setMarginStart(marginStart);
-            second.setLayoutParams(lp);
-            totalExtraMargin += extraMargin;
-        }
-        setMeasuredDimension(getMeasuredWidth() + totalExtraMargin, getMeasuredHeight());
-    }
-}
diff --git a/android/support/v17/leanback/widget/ControlBarPresenter.java b/android/support/v17/leanback/widget/ControlBarPresenter.java
deleted file mode 100644
index a3319ba..0000000
--- a/android/support/v17/leanback/widget/ControlBarPresenter.java
+++ /dev/null
@@ -1,294 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.support.v17.leanback.R;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * A presenter that assumes a LinearLayout container for a series
- * of control buttons backed by objects of type {@link Action}.
- *
- * Different layouts may be passed to the presenter constructor.
- * The layout must contain a view with id control_bar.
- */
-class ControlBarPresenter extends Presenter {
-
-    static final int MAX_CONTROLS = 7;
-
-    /**
-     * The data type expected by this presenter.
-     */
-    static class BoundData {
-        /**
-         * Adapter containing objects of type {@link Action}.
-         */
-        ObjectAdapter adapter;
-
-        /**
-         * The presenter to be used for the adapter objects.
-         */
-        Presenter presenter;
-    }
-
-    /**
-     * Listener for control selected events.
-     */
-    interface OnControlSelectedListener {
-        void onControlSelected(Presenter.ViewHolder controlViewHolder, Object item,
-                BoundData data);
-    }
-
-    /**
-     * Listener for control clicked events.
-     */
-    interface OnControlClickedListener {
-        void onControlClicked(Presenter.ViewHolder controlViewHolder, Object item,
-                BoundData data);
-    }
-
-    class ViewHolder extends Presenter.ViewHolder {
-        ObjectAdapter mAdapter;
-        BoundData mData;
-        Presenter mPresenter;
-        ControlBar mControlBar;
-        View mControlsContainer;
-        SparseArray<Presenter.ViewHolder> mViewHolders =
-                new SparseArray<Presenter.ViewHolder>();
-        ObjectAdapter.DataObserver mDataObserver;
-
-        /**
-         * Constructor for the ViewHolder.
-         */
-        ViewHolder(View rootView) {
-            super(rootView);
-            mControlsContainer = rootView.findViewById(R.id.controls_container);
-            mControlBar = (ControlBar) rootView.findViewById(R.id.control_bar);
-            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) {
-                    if (mOnControlSelectedListener == null) {
-                        return;
-                    }
-                    for (int position = 0; position < mViewHolders.size(); position++) {
-                        if (mViewHolders.get(position).view == child) {
-                            mOnControlSelectedListener.onControlSelected(
-                                    mViewHolders.get(position),
-                                    getDisplayedAdapter().get(position), mData);
-                            break;
-                        }
-                    }
-                }
-            });
-            mDataObserver = new ObjectAdapter.DataObserver() {
-                @Override
-                public void onChanged() {
-                    if (mAdapter == getDisplayedAdapter()) {
-                        showControls(mPresenter);
-                    }
-                }
-                @Override
-                public void onItemRangeChanged(int positionStart, int itemCount) {
-                    if (mAdapter == getDisplayedAdapter()) {
-                        for (int i = 0; i < itemCount; i++) {
-                            bindControlToAction(positionStart + i, mPresenter);
-                        }
-                    }
-                }
-            };
-        }
-
-        int getChildMarginFromCenter(Context context, int numControls) {
-            // Includes margin between icons plus two times half the icon width.
-            return getChildMarginDefault(context) + getControlIconWidth(context);
-        }
-
-        void showControls(Presenter presenter) {
-            ObjectAdapter adapter = getDisplayedAdapter();
-            int adapterSize = adapter == null ? 0 : adapter.size();
-            // Shrink the number of attached views
-            View focusedView = mControlBar.getFocusedChild();
-            if (focusedView != null && adapterSize > 0
-                    && mControlBar.indexOfChild(focusedView) >= adapterSize) {
-                mControlBar.getChildAt(adapter.size() - 1).requestFocus();
-            }
-            for (int i = mControlBar.getChildCount() - 1; i >= adapterSize; i--) {
-                mControlBar.removeViewAt(i);
-            }
-            for (int position = 0; position < adapterSize && position < MAX_CONTROLS;
-                    position++) {
-                bindControlToAction(position, adapter, presenter);
-            }
-            mControlBar.setChildMarginFromCenter(
-                    getChildMarginFromCenter(mControlBar.getContext(), adapterSize));
-        }
-
-        void bindControlToAction(int position, Presenter presenter) {
-            bindControlToAction(position, getDisplayedAdapter(), presenter);
-        }
-
-        private void bindControlToAction(final int position,
-                ObjectAdapter adapter, Presenter presenter) {
-            Presenter.ViewHolder vh = mViewHolders.get(position);
-            Object item = adapter.get(position);
-            if (vh == null) {
-                vh = presenter.onCreateViewHolder(mControlBar);
-                mViewHolders.put(position, vh);
-
-                final Presenter.ViewHolder itemViewHolder = vh;
-                presenter.setOnClickListener(vh, new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        Object item = getDisplayedAdapter().get(position);
-                        if (mOnControlClickedListener != null) {
-                            mOnControlClickedListener.onControlClicked(itemViewHolder, item,
-                                    mData);
-                        }
-                    }
-                });
-            }
-            if (vh.view.getParent() == null) {
-                mControlBar.addView(vh.view);
-            }
-            presenter.onBindViewHolder(vh, item);
-        }
-
-        /**
-         * Returns the adapter currently bound to the displayed controls.
-         * May be overridden in a subclass.
-         */
-        ObjectAdapter getDisplayedAdapter() {
-            return mAdapter;
-        }
-    }
-
-    OnControlClickedListener mOnControlClickedListener;
-    OnControlSelectedListener mOnControlSelectedListener;
-    private int mLayoutResourceId;
-    private static int sChildMarginDefault;
-    private static int sControlIconWidth;
-    boolean mDefaultFocusToMiddle = true;
-
-    /**
-     * Constructor for a ControlBarPresenter.
-     *
-     * @param layoutResourceId The resource id of the layout for this presenter.
-     */
-    public ControlBarPresenter(int layoutResourceId) {
-        mLayoutResourceId = layoutResourceId;
-    }
-
-    /**
-     * Returns the layout resource id.
-     */
-    public int getLayoutResourceId() {
-        return mLayoutResourceId;
-    }
-
-    /**
-     * Sets the listener for control clicked events.
-     */
-    public void setOnControlClickedListener(OnControlClickedListener listener) {
-        mOnControlClickedListener = listener;
-    }
-
-    /**
-     * Returns the listener for control clicked events.
-     */
-    public OnControlClickedListener getOnItemViewClickedListener() {
-        return mOnControlClickedListener;
-    }
-
-    /**
-     * Sets the listener for control selection.
-     */
-    public void setOnControlSelectedListener(OnControlSelectedListener listener) {
-        mOnControlSelectedListener = listener;
-    }
-
-    /**
-     * Returns the listener for control selection.
-     */
-    public OnControlSelectedListener getOnItemControlListener() {
-        return mOnControlSelectedListener;
-    }
-
-    public void setBackgroundColor(ViewHolder vh, int color) {
-        vh.mControlsContainer.setBackgroundColor(color);
-    }
-
-    @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        View v = LayoutInflater.from(parent.getContext())
-            .inflate(getLayoutResourceId(), parent, false);
-        return new ViewHolder(v);
-    }
-
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
-        ViewHolder vh = (ViewHolder) holder;
-        BoundData data = (BoundData) item;
-        if (vh.mAdapter != data.adapter) {
-            vh.mAdapter = data.adapter;
-            if (vh.mAdapter != null) {
-                vh.mAdapter.registerObserver(vh.mDataObserver);
-            }
-        }
-        vh.mPresenter = data.presenter;
-        vh.mData = data;
-        vh.showControls(vh.mPresenter);
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
-        ViewHolder vh = (ViewHolder) holder;
-        if (vh.mAdapter != null) {
-            vh.mAdapter.unregisterObserver(vh.mDataObserver);
-            vh.mAdapter = null;
-        }
-        vh.mData = null;
-    }
-
-    int getChildMarginDefault(Context context) {
-        if (sChildMarginDefault == 0) {
-            sChildMarginDefault = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_playback_controls_child_margin_default);
-        }
-        return sChildMarginDefault;
-    }
-
-    int getControlIconWidth(Context context) {
-        if (sControlIconWidth == 0) {
-            sControlIconWidth = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_control_icon_width);
-        }
-        return sControlIconWidth;
-    }
-
-    /**
-     * @param defaultFocusToMiddle True for middle item, false for 0.
-     */
-    void setDefaultFocusToMiddle(boolean defaultFocusToMiddle) {
-        mDefaultFocusToMiddle = defaultFocusToMiddle;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java b/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
deleted file mode 100644
index eef6c9c..0000000
--- a/android/support/v17/leanback/widget/ControlButtonPresenterSelector.java
+++ /dev/null
@@ -1,129 +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.v17.leanback.widget;
-
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * Displays primary and secondary controls for a {@link PlaybackControlsRow}.
- *
- * Binds to items of type {@link Action}.
- */
-public class ControlButtonPresenterSelector extends PresenterSelector {
-
-    private final Presenter mPrimaryPresenter =
-            new ControlButtonPresenter(R.layout.lb_control_button_primary);
-    private final Presenter mSecondaryPresenter =
-            new ControlButtonPresenter(R.layout.lb_control_button_secondary);
-    private final Presenter[] mPresenters = new Presenter[]{mPrimaryPresenter};
-
-    /**
-     * Returns the presenter for primary controls.
-     */
-    public Presenter getPrimaryPresenter() {
-        return mPrimaryPresenter;
-    }
-
-    /**
-     * Returns the presenter for secondary controls.
-     */
-    public Presenter getSecondaryPresenter() {
-        return mSecondaryPresenter;
-    }
-
-    /**
-     * Always returns the presenter for primary controls.
-     */
-    @Override
-    public Presenter getPresenter(Object item) {
-        return mPrimaryPresenter;
-    }
-
-    @Override
-    public Presenter[] getPresenters() {
-        return mPresenters;
-    }
-
-    static class ActionViewHolder extends Presenter.ViewHolder {
-        ImageView mIcon;
-        TextView mLabel;
-        View mFocusableView;
-
-        public ActionViewHolder(View view) {
-            super(view);
-            mIcon = (ImageView) view.findViewById(R.id.icon);
-            mLabel = (TextView) view.findViewById(R.id.label);
-            mFocusableView = view.findViewById(R.id.button);
-        }
-    }
-
-    static class ControlButtonPresenter extends Presenter {
-        private int mLayoutResourceId;
-
-        ControlButtonPresenter(int layoutResourceId) {
-            mLayoutResourceId = layoutResourceId;
-        }
-
-        @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent) {
-            View v = LayoutInflater.from(parent.getContext())
-                    .inflate(mLayoutResourceId, parent, false);
-            return new ActionViewHolder(v);
-        }
-
-        @Override
-        public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-            Action action = (Action) item;
-            ActionViewHolder vh = (ActionViewHolder) viewHolder;
-            vh.mIcon.setImageDrawable(action.getIcon());
-            if (vh.mLabel != null) {
-                if (action.getIcon() == null) {
-                    vh.mLabel.setText(action.getLabel1());
-                } else {
-                    vh.mLabel.setText(null);
-                }
-            }
-            CharSequence contentDescription = TextUtils.isEmpty(action.getLabel2())
-                    ? action.getLabel1() : action.getLabel2();
-            if (!TextUtils.equals(vh.mFocusableView.getContentDescription(), contentDescription)) {
-                vh.mFocusableView.setContentDescription(contentDescription);
-                vh.mFocusableView.sendAccessibilityEvent(
-                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
-            }
-        }
-
-        @Override
-        public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-            ActionViewHolder vh = (ActionViewHolder) viewHolder;
-            vh.mIcon.setImageDrawable(null);
-            if (vh.mLabel != null) {
-                vh.mLabel.setText(null);
-            }
-            vh.mFocusableView.setContentDescription(null);
-        }
-
-        @Override
-        public void setOnClickListener(Presenter.ViewHolder viewHolder,
-                View.OnClickListener listener) {
-            ((ActionViewHolder) viewHolder).mFocusableView.setOnClickListener(listener);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/CursorObjectAdapter.java b/android/support/v17/leanback/widget/CursorObjectAdapter.java
deleted file mode 100644
index c07f3b0..0000000
--- a/android/support/v17/leanback/widget/CursorObjectAdapter.java
+++ /dev/null
@@ -1,192 +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.v17.leanback.widget;
-
-import android.database.Cursor;
-import android.support.v17.leanback.database.CursorMapper;
-import android.util.LruCache;
-
-/**
- * An {@link ObjectAdapter} implemented with a {@link Cursor}.
- */
-public class CursorObjectAdapter extends ObjectAdapter {
-    private static final int CACHE_SIZE = 100;
-    private Cursor mCursor;
-    private CursorMapper mMapper;
-    private final LruCache<Integer, Object> mItemCache = new LruCache<Integer, Object>(CACHE_SIZE);
-
-    /**
-     * Constructs an adapter with the given {@link PresenterSelector}.
-     */
-    public CursorObjectAdapter(PresenterSelector presenterSelector) {
-        super(presenterSelector);
-    }
-
-    /**
-     * Constructs an adapter that uses the given {@link Presenter} for all items.
-     */
-    public CursorObjectAdapter(Presenter presenter) {
-        super(presenter);
-    }
-
-    /**
-     * Constructs an adapter.
-     */
-    public CursorObjectAdapter() {
-        super();
-    }
-
-    /**
-     * Changes the underlying cursor to a new cursor. If there is
-     * an existing cursor it will be closed if it is different than the new
-     * cursor.
-     *
-     * @param cursor The new cursor to be used.
-     */
-    public void changeCursor(Cursor cursor) {
-        if (cursor == mCursor) {
-            return;
-        }
-        if (mCursor != null) {
-            mCursor.close();
-        }
-        mCursor = cursor;
-        mItemCache.trimToSize(0);
-        onCursorChanged();
-    }
-
-    /**
-     * Swap in a new Cursor, returning the old Cursor. Unlike changeCursor(Cursor),
-     * the returned old Cursor is not closed.
-     *
-     * @param cursor The new cursor to be used.
-     */
-    public Cursor swapCursor(Cursor cursor) {
-        if (cursor == mCursor) {
-            return mCursor;
-        }
-        Cursor oldCursor = mCursor;
-        mCursor = cursor;
-        mItemCache.trimToSize(0);
-        onCursorChanged();
-        return oldCursor;
-    }
-
-    /**
-     * Called whenever the cursor changes.
-     */
-    protected void onCursorChanged() {
-        notifyChanged();
-    }
-
-    /**
-     * Returns the {@link Cursor} backing the adapter.
-     */
-     public final Cursor getCursor() {
-        return mCursor;
-    }
-
-    /**
-     * Sets the {@link CursorMapper} used to convert {@link Cursor} rows into
-     * Objects.
-     */
-    public final void setMapper(CursorMapper mapper) {
-        boolean changed = mMapper != mapper;
-        mMapper = mapper;
-
-        if (changed) {
-            onMapperChanged();
-        }
-    }
-
-    /**
-     * Called when {@link #setMapper(CursorMapper)} is called and a different
-     * mapper is provided.
-     */
-    protected void onMapperChanged() {
-    }
-
-    /**
-     * Returns the {@link CursorMapper} used to convert {@link Cursor} rows into
-     * Objects.
-     */
-    public final CursorMapper getMapper() {
-        return mMapper;
-    }
-
-    @Override
-    public int size() {
-        if (mCursor == null) {
-            return 0;
-        }
-        return mCursor.getCount();
-    }
-
-    @Override
-    public Object get(int index) {
-        if (mCursor == null) {
-            return null;
-        }
-        if (!mCursor.moveToPosition(index)) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        Object item = mItemCache.get(index);
-        if (item != null) {
-            return item;
-        }
-        item = mMapper.convert(mCursor);
-        mItemCache.put(index, item);
-        return item;
-    }
-
-    /**
-     * Closes this adapter, closing the backing {@link Cursor} as well.
-     */
-    public void close() {
-        if (mCursor != null) {
-            mCursor.close();
-            mCursor = null;
-        }
-    }
-
-    /**
-     * Returns true if the adapter, and hence the backing {@link Cursor}, is closed; false
-     * otherwise.
-     */
-    public boolean isClosed() {
-        return mCursor == null || mCursor.isClosed();
-    }
-
-    /**
-     * Removes an item from the cache. This will force the item to be re-read
-     * from the data source the next time {@link #get(int)} is called.
-     */
-    protected final void invalidateCache(int index) {
-        mItemCache.remove(index);
-    }
-
-    /**
-     * Removes {@code count} items starting at {@code index}.
-     */
-    protected final void invalidateCache(int index, int count) {
-        for (int limit = count + index; index < limit; index++) {
-            invalidateCache(index);
-        }
-    }
-
-    @Override
-    public boolean isImmediateNotifySupported() {
-        return true;
-    }
-}
diff --git a/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java b/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
deleted file mode 100644
index f23acbc..0000000
--- a/android/support/v17/leanback/widget/DetailsOverviewLogoPresenter.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.support.v17.leanback.R;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-/**
- * Presenter that responsible to create a ImageView and bind to DetailsOverviewRow. The default
- * implementation uses {@link DetailsOverviewRow#getImageDrawable()} and binds to {@link ImageView}.
- * <p>
- * Default implementation assumes no scaleType on ImageView and uses intrinsic width and height of
- * {@link DetailsOverviewRow#getImageDrawable()} to initialize ImageView's layout params.  To
- * specify a fixed size and/or specify a scapeType, subclass should change ImageView's layout params
- * and scaleType in {@link #onCreateView(ViewGroup)}.
- * <p>
- * Subclass may override and has its own image view. Subclass may also download image from URL
- * instead of using {@link DetailsOverviewRow#getImageDrawable()}. It's subclass's responsibility to
- * call {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder)}
- * whenever {@link #isBoundToImage(ViewHolder, DetailsOverviewRow)} turned to true so that activity
- * transition can be started.
- */
-public class DetailsOverviewLogoPresenter extends Presenter {
-
-    /**
-     * ViewHolder for Logo view of DetailsOverviewRow.
-     */
-    public static class ViewHolder extends Presenter.ViewHolder {
-
-        protected FullWidthDetailsOverviewRowPresenter mParentPresenter;
-        protected FullWidthDetailsOverviewRowPresenter.ViewHolder mParentViewHolder;
-        private boolean mSizeFromDrawableIntrinsic;
-
-        public ViewHolder(View view) {
-            super(view);
-        }
-
-        public FullWidthDetailsOverviewRowPresenter getParentPresenter() {
-            return mParentPresenter;
-        }
-
-        public FullWidthDetailsOverviewRowPresenter.ViewHolder getParentViewHolder() {
-            return mParentViewHolder;
-        }
-
-        /**
-         * @return True if layout size of ImageView should be changed to intrinsic size of Drawable,
-         *         false otherwise. Used by
-         *         {@link DetailsOverviewLogoPresenter#onBindViewHolder(Presenter.ViewHolder, Object)}
-         *         .
-         *
-         * @see DetailsOverviewLogoPresenter#onCreateView(ViewGroup)
-         * @see DetailsOverviewLogoPresenter#onBindViewHolder(Presenter.ViewHolder, Object)
-         */
-        public boolean isSizeFromDrawableIntrinsic() {
-            return mSizeFromDrawableIntrinsic;
-        }
-
-        /**
-         * Change if the ImageView layout size should be synchronized to Drawable intrinsic size.
-         * Used by
-         * {@link DetailsOverviewLogoPresenter#onBindViewHolder(Presenter.ViewHolder, Object)}.
-         *
-         * @param sizeFromDrawableIntrinsic True if layout size of ImageView should be changed to
-         *        intrinsic size of Drawable, false otherwise.
-         *
-         * @see DetailsOverviewLogoPresenter#onCreateView(ViewGroup)
-         * @see DetailsOverviewLogoPresenter#onBindViewHolder(Presenter.ViewHolder, Object)
-         */
-        public void setSizeFromDrawableIntrinsic(boolean sizeFromDrawableIntrinsic) {
-            mSizeFromDrawableIntrinsic = sizeFromDrawableIntrinsic;
-        }
-    }
-
-    /**
-     * Create a View for the Logo, default implementation loads from
-     * {@link R.layout#lb_fullwidth_details_overview_logo}. Subclass may override this method to use
-     * a fixed layout size and change ImageView scaleType. If the layout params is WRAP_CONTENT for
-     * both width and size, the ViewHolder would be using intrinsic size of Drawable in
-     * {@link #onBindViewHolder(Presenter.ViewHolder, Object)}.
-     *
-     * @param parent Parent view.
-     * @return View created for the logo.
-     */
-    public View onCreateView(ViewGroup parent) {
-        return LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.lb_fullwidth_details_overview_logo, parent, false);
-    }
-
-    @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        View view = onCreateView(parent);
-        ViewHolder vh = new ViewHolder(view);
-        ViewGroup.LayoutParams lp = view.getLayoutParams();
-        vh.setSizeFromDrawableIntrinsic(lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
-                && lp.height == ViewGroup.LayoutParams.WRAP_CONTENT);
-        return vh;
-    }
-
-    /**
-     * Called from {@link FullWidthDetailsOverviewRowPresenter} to setup FullWidthDetailsOverviewRowPresenter
-     * and FullWidthDetailsOverviewRowPresenter.ViewHolder that hosts the logo.
-     * @param viewHolder
-     * @param parentViewHolder
-     * @param parentPresenter
-     */
-    public void setContext(ViewHolder viewHolder,
-            FullWidthDetailsOverviewRowPresenter.ViewHolder parentViewHolder,
-            FullWidthDetailsOverviewRowPresenter parentPresenter) {
-        viewHolder.mParentViewHolder = parentViewHolder;
-        viewHolder.mParentPresenter = parentPresenter;
-    }
-
-    /**
-     * Returns true if the logo view is bound to image. Subclass may override. The default
-     * implementation returns true when {@link DetailsOverviewRow#getImageDrawable()} is not null.
-     * If subclass of DetailsOverviewLogoPresenter manages its own image drawable, it should
-     * override this function to report status correctly and invoke
-     * {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(FullWidthDetailsOverviewRowPresenter.ViewHolder)}
-     * when image view is bound to the drawable.
-     */
-    public boolean isBoundToImage(ViewHolder viewHolder, DetailsOverviewRow row) {
-        return row != null && row.getImageDrawable() != null;
-    }
-
-    /**
-     * Bind logo View to drawable of DetailsOverviewRow and call notifyOnBindLogo().  The
-     * default implementation assumes the Logo View is an ImageView and change layout size to
-     * intrinsic size of ImageDrawable if {@link ViewHolder#isSizeFromDrawableIntrinsic()} is true.
-     * @param viewHolder ViewHolder to bind.
-     * @param item DetailsOverviewRow object to bind.
-     */
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-        DetailsOverviewRow row = (DetailsOverviewRow) item;
-        ImageView imageView = ((ImageView) viewHolder.view);
-        imageView.setImageDrawable(row.getImageDrawable());
-        if (isBoundToImage((ViewHolder) viewHolder, row)) {
-            ViewHolder vh = (ViewHolder) viewHolder;
-            if (vh.isSizeFromDrawableIntrinsic()) {
-                ViewGroup.LayoutParams lp = imageView.getLayoutParams();
-                lp.width = row.getImageDrawable().getIntrinsicWidth();
-                lp.height = row.getImageDrawable().getIntrinsicHeight();
-                if (imageView.getMaxWidth() > 0 || imageView.getMaxHeight() > 0) {
-                    float maxScaleWidth = 1f;
-                    if (imageView.getMaxWidth() > 0) {
-                        if (lp.width > imageView.getMaxWidth()) {
-                            maxScaleWidth = imageView.getMaxWidth() / (float) lp.width;
-                        }
-                    }
-                    float maxScaleHeight = 1f;
-                    if (imageView.getMaxHeight() > 0) {
-                        if (lp.height > imageView.getMaxHeight()) {
-                            maxScaleHeight = imageView.getMaxHeight() / (float) lp.height;
-                        }
-                    }
-                    float scale = Math.min(maxScaleWidth, maxScaleHeight);
-                    lp.width = (int) (lp.width * scale);
-                    lp.height = (int) (lp.height * scale);
-                }
-                imageView.setLayoutParams(lp);
-            }
-            vh.mParentPresenter.notifyOnBindLogo(vh.mParentViewHolder);
-        }
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/DetailsOverviewRow.java b/android/support/v17/leanback/widget/DetailsOverviewRow.java
deleted file mode 100644
index 9e8dbcd..0000000
--- a/android/support/v17/leanback/widget/DetailsOverviewRow.java
+++ /dev/null
@@ -1,358 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An overview {@link Row} for a details fragment. This row consists of an image, a
- * description view, and optionally a series of {@link Action}s that can be taken for
- * the item.
- *
- * <h3>Actions</h3>
- * Application uses {@link #setActionsAdapter(ObjectAdapter)} to set actions on the overview
- * row.  {@link SparseArrayObjectAdapter} is recommended for easily updating actions while
- * maintaining the order.  The application can add or remove actions on the UI thread after the
- * row is bound to a view.
- *
- * <h3>Updating main item</h3>
- * After the row is bound to a view, the application may call {@link #setItem(Object)}
- * on UI thread and the view will be updated.
- *
- * <h3>Updating image</h3>
- * After the row is bound to view, the application may change the image by calling {@link
- * #setImageBitmap(Context, Bitmap)} or {@link #setImageDrawable(Drawable)} on the UI thread,
- * and the view will be updated.
- */
-public class DetailsOverviewRow extends Row {
-
-    /**
-     * Listener for changes of DetailsOverviewRow.
-     */
-    public static class Listener {
-
-        /**
-         * Called when DetailsOverviewRow has changed image drawable.
-         */
-        public void onImageDrawableChanged(DetailsOverviewRow row) {
-        }
-
-        /**
-         * Called when DetailsOverviewRow has changed main item.
-         */
-        public void onItemChanged(DetailsOverviewRow row) {
-        }
-
-        /**
-         * Called when DetailsOverviewRow has changed actions adapter.
-         */
-        public void onActionsAdapterChanged(DetailsOverviewRow row) {
-        }
-    }
-
-    private Object mItem;
-    private Drawable mImageDrawable;
-    private boolean mImageScaleUpAllowed = true;
-    private ArrayList<WeakReference<Listener>> mListeners;
-    private PresenterSelector mDefaultActionPresenter = new ActionPresenterSelector();
-    private ObjectAdapter mActionsAdapter = new ArrayObjectAdapter(mDefaultActionPresenter);
-
-    /**
-     * Constructor for a DetailsOverviewRow.
-     *
-     * @param item The main item for the details page.
-     */
-    public DetailsOverviewRow(Object item) {
-        super(null);
-        mItem = item;
-        verify();
-    }
-
-    /**
-     * Adds listener for the details page.
-     */
-    final void addListener(Listener listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<WeakReference<Listener>>();
-        } else {
-            for (int i = 0; i < mListeners.size();) {
-                Listener l = mListeners.get(i).get();
-                if (l == null) {
-                    mListeners.remove(i);
-                } else {
-                    if (l == listener) {
-                        return;
-                    }
-                    i++;
-                }
-            }
-        }
-        mListeners.add(new WeakReference<Listener>(listener));
-    }
-
-    /**
-     * Removes listener of the details page.
-     */
-    final void removeListener(Listener listener) {
-        if (mListeners != null) {
-            for (int i = 0; i < mListeners.size();) {
-                Listener l = mListeners.get(i).get();
-                if (l == null) {
-                    mListeners.remove(i);
-                } else {
-                    if (l == listener) {
-                        mListeners.remove(i);
-                        return;
-                    }
-                    i++;
-                }
-            }
-        }
-    }
-
-    /**
-     * Notifies listeners for main item change on UI thread.
-     */
-    final void notifyItemChanged() {
-        if (mListeners != null) {
-            for (int i = 0; i < mListeners.size();) {
-                Listener l = mListeners.get(i).get();
-                if (l == null) {
-                    mListeners.remove(i);
-                } else {
-                    l.onItemChanged(this);
-                    i++;
-                }
-            }
-        }
-    }
-
-    /**
-     * Notifies listeners for image related change on UI thread.
-     */
-    final void notifyImageDrawableChanged() {
-        if (mListeners != null) {
-            for (int i = 0; i < mListeners.size();) {
-                Listener l = mListeners.get(i).get();
-                if (l == null) {
-                    mListeners.remove(i);
-                } else {
-                    l.onImageDrawableChanged(this);
-                    i++;
-                }
-            }
-        }
-    }
-
-    /**
-     * Notifies listeners for actions adapter changed on UI thread.
-     */
-    final void notifyActionsAdapterChanged() {
-        if (mListeners != null) {
-            for (int i = 0; i < mListeners.size();) {
-                Listener l = mListeners.get(i).get();
-                if (l == null) {
-                    mListeners.remove(i);
-                } else {
-                    l.onActionsAdapterChanged(this);
-                    i++;
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns the main item for the details page.
-     */
-    public final Object getItem() {
-        return mItem;
-    }
-
-    /**
-     * Sets the main item for the details page.  Must be called on UI thread after
-     * row is bound to view.
-     */
-    public final void setItem(Object item) {
-        if (item != mItem) {
-            mItem = item;
-            notifyItemChanged();
-        }
-    }
-
-    /**
-     * Sets a drawable as the image of this details overview.  Must be called on UI thread
-     * after row is bound to view.
-     *
-     * @param drawable The drawable to set.
-     */
-    public final void setImageDrawable(Drawable drawable) {
-        if (mImageDrawable != drawable) {
-            mImageDrawable = drawable;
-            notifyImageDrawableChanged();
-        }
-    }
-
-    /**
-     * Sets a Bitmap as the image of this details overview.  Must be called on UI thread
-     * after row is bound to view.
-     *
-     * @param context The context to retrieve display metrics from.
-     * @param bm The bitmap to set.
-     */
-    public final void setImageBitmap(Context context, Bitmap bm) {
-        mImageDrawable = new BitmapDrawable(context.getResources(), bm);
-        notifyImageDrawableChanged();
-    }
-
-    /**
-     * Returns the image drawable of this details overview.
-     *
-     * @return The overview's image drawable, or null if no drawable has been
-     *         assigned.
-     */
-    public final Drawable getImageDrawable() {
-        return mImageDrawable;
-    }
-
-    /**
-     * Allows or disallows scaling up of images.
-     * Images will always be scaled down if necessary.  Must be called on UI thread
-     * after row is bound to view.
-     */
-    public void setImageScaleUpAllowed(boolean allowed) {
-        if (allowed != mImageScaleUpAllowed) {
-            mImageScaleUpAllowed = allowed;
-            notifyImageDrawableChanged();
-        }
-    }
-
-    /**
-     * Returns true if the image may be scaled up; false otherwise.
-     */
-    public boolean isImageScaleUpAllowed() {
-        return mImageScaleUpAllowed;
-    }
-
-    /**
-     * Returns the actions adapter.  Throws ClassCastException if the current
-     * actions adapter is not an instance of {@link ArrayObjectAdapter}.
-     */
-    private ArrayObjectAdapter getArrayObjectAdapter() {
-        return (ArrayObjectAdapter) mActionsAdapter;
-    }
-
-    /**
-     * Adds an Action to the overview. It will throw ClassCastException if the current actions
-     * adapter is not an instance of {@link ArrayObjectAdapter}. Must be called on the UI thread.
-     *
-     * @param action The Action to add.
-     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
-     */
-    @Deprecated
-    public final void addAction(Action action) {
-        getArrayObjectAdapter().add(action);
-    }
-
-    /**
-     * Adds an Action to the overview at the specified position. It will throw ClassCastException if
-     * current actions adapter is not an instance of f{@link ArrayObjectAdapter}. Must be called
-     * on the UI thread.
-     *
-     * @param pos The position to insert the Action.
-     * @param action The Action to add.
-     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
-     */
-    @Deprecated
-    public final void addAction(int pos, Action action) {
-        getArrayObjectAdapter().add(pos, action);
-    }
-
-    /**
-     * Removes the given Action from the overview. It will throw ClassCastException if current
-     * actions adapter is not {@link ArrayObjectAdapter}. Must be called on UI thread.
-     *
-     * @param action The Action to remove.
-     * @return true if the overview contained the specified Action.
-     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
-     */
-    @Deprecated
-    public final boolean removeAction(Action action) {
-        return getArrayObjectAdapter().remove(action);
-    }
-
-    /**
-     * Returns a read-only view of the list of Actions of this details overview. It will throw
-     * ClassCastException if current actions adapter is not {@link ArrayObjectAdapter}. Must be
-     * called on UI thread.
-     *
-     * @return An unmodifiable view of the list of Actions.
-     * @deprecated Use {@link #setActionsAdapter(ObjectAdapter)} and {@link #getActionsAdapter()}
-     */
-    @Deprecated
-    public final List<Action> getActions() {
-        return getArrayObjectAdapter().unmodifiableList();
-    }
-
-    /**
-     * Returns the {@link ObjectAdapter} for actions.
-     */
-    public final ObjectAdapter getActionsAdapter() {
-        return mActionsAdapter;
-    }
-
-    /**
-     * Sets the {@link ObjectAdapter} for actions.  A default {@link PresenterSelector} will be
-     * attached to the adapter if it doesn't have one.
-     *
-     * @param adapter  Adapter for actions.
-     */
-    public final void setActionsAdapter(ObjectAdapter adapter) {
-        if (adapter != mActionsAdapter) {
-            mActionsAdapter = adapter;
-            if (mActionsAdapter.getPresenterSelector() == null) {
-                mActionsAdapter.setPresenterSelector(mDefaultActionPresenter);
-            }
-            notifyActionsAdapterChanged();
-        }
-    }
-
-    /**
-     * Returns the Action associated with the given keycode, or null if no associated action exists.
-     */
-    public Action getActionForKeyCode(int keyCode) {
-        ObjectAdapter adapter = getActionsAdapter();
-        if (adapter != null) {
-            for (int i = 0; i < adapter.size(); i++) {
-                Action action = (Action) adapter.get(i);
-                if (action.respondsToKeyCode(keyCode)) {
-                    return action;
-                }
-            }
-        }
-        return null;
-    }
-
-    private void verify() {
-        if (mItem == null) {
-            throw new IllegalArgumentException("Object cannot be null");
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java b/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
deleted file mode 100644
index 98b9f78..0000000
--- a/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
+++ /dev/null
@@ -1,599 +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.v17.leanback.widget;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * Renders a {@link DetailsOverviewRow} to display an overview of an item.
- * Typically this row will be the first row in a fragment
- * such as the {@link android.support.v17.leanback.app.DetailsFragment
- * DetailsFragment}.  The View created by the DetailsOverviewRowPresenter is made in three parts:
- * ImageView on the left, action list view on the bottom and a customizable detailed
- * description view on the right.
- *
- * <p>The detailed description is rendered using a {@link Presenter} passed in
- * {@link #DetailsOverviewRowPresenter(Presenter)}.  Typically this will be an instance of
- * {@link AbstractDetailsDescriptionPresenter}.  The application can access the
- * detailed description ViewHolder from {@link ViewHolder#mDetailsDescriptionViewHolder}.
- * </p>
- *
- * <p>
- * To participate in activity transition, call {@link #setSharedElementEnterTransition(Activity,
- * String)} during Activity's onCreate().
- * </p>
- *
- * <p>
- * Because transition support and layout are fully controlled by DetailsOverviewRowPresenter,
- * developer can not override DetailsOverviewRowPresenter.ViewHolder for adding/replacing views
- * of DetailsOverviewRowPresenter.  If further customization is required beyond replacing
- * the detailed description, the application should create a new row presenter class.
- * </p>
- * @deprecated  Use {@link FullWidthDetailsOverviewRowPresenter}
- */
-@Deprecated
-public class DetailsOverviewRowPresenter extends RowPresenter {
-
-    static final String TAG = "DetailsOverviewRowP";
-    static final boolean DEBUG = false;
-
-    private static final int MORE_ACTIONS_FADE_MS = 100;
-    private static final long DEFAULT_TIMEOUT = 5000;
-
-    class ActionsItemBridgeAdapter extends ItemBridgeAdapter {
-        DetailsOverviewRowPresenter.ViewHolder mViewHolder;
-
-        ActionsItemBridgeAdapter(DetailsOverviewRowPresenter.ViewHolder viewHolder) {
-            mViewHolder = viewHolder;
-        }
-
-        @Override
-        public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null
-                    || mActionClickedListener != null) {
-                ibvh.getPresenter().setOnClickListener(
-                        ibvh.getViewHolder(), new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                if (mViewHolder.getOnItemViewClickedListener() != null) {
-                                    mViewHolder.getOnItemViewClickedListener().onItemClicked(
-                                            ibvh.getViewHolder(), ibvh.getItem(),
-                                            mViewHolder, mViewHolder.getRow());
-                                }
-                                if (mActionClickedListener != null) {
-                                    mActionClickedListener.onActionClicked((Action) ibvh.getItem());
-                                }
-                            }
-                        });
-            }
-        }
-        @Override
-        public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null
-                    || mActionClickedListener != null) {
-                ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
-            }
-        }
-        @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            // Remove first to ensure we don't add ourselves more than once.
-            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
-            viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
-        }
-        @Override
-        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
-            mViewHolder.checkFirstAndLastPosition(false);
-        }
-    }
-
-    /**
-     * A ViewHolder for the DetailsOverviewRow.
-     */
-    public final class ViewHolder extends RowPresenter.ViewHolder {
-        final FrameLayout mOverviewFrame;
-        final ViewGroup mOverviewView;
-        final ImageView mImageView;
-        final ViewGroup mRightPanel;
-        final FrameLayout mDetailsDescriptionFrame;
-        final HorizontalGridView mActionsRow;
-        public final Presenter.ViewHolder mDetailsDescriptionViewHolder;
-        int mNumItems;
-        boolean mShowMoreRight;
-        boolean mShowMoreLeft;
-        ItemBridgeAdapter mActionBridgeAdapter;
-        final Handler mHandler = new Handler();
-
-        final Runnable mUpdateDrawableCallback = new Runnable() {
-            @Override
-            public void run() {
-                bindImageDrawable(ViewHolder.this);
-            }
-        };
-
-        final DetailsOverviewRow.Listener mListener = new DetailsOverviewRow.Listener() {
-            @Override
-            public void onImageDrawableChanged(DetailsOverviewRow row) {
-                mHandler.removeCallbacks(mUpdateDrawableCallback);
-                mHandler.post(mUpdateDrawableCallback);
-            }
-
-            @Override
-            public void onItemChanged(DetailsOverviewRow row) {
-                if (mDetailsDescriptionViewHolder != null) {
-                    mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder);
-                }
-                mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem());
-            }
-
-            @Override
-            public void onActionsAdapterChanged(DetailsOverviewRow row) {
-                bindActions(row.getActionsAdapter());
-            }
-        };
-
-        void bindActions(ObjectAdapter adapter) {
-            mActionBridgeAdapter.setAdapter(adapter);
-            mActionsRow.setAdapter(mActionBridgeAdapter);
-            mNumItems = mActionBridgeAdapter.getItemCount();
-
-            mShowMoreRight = false;
-            mShowMoreLeft = true;
-            showMoreLeft(false);
-        }
-
-        final View.OnLayoutChangeListener mLayoutChangeListener =
-                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) {
-                if (DEBUG) Log.v(TAG, "onLayoutChange " + v);
-                checkFirstAndLastPosition(false);
-            }
-        };
-
-        final OnChildSelectedListener mChildSelectedListener = new OnChildSelectedListener() {
-            @Override
-            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
-                dispatchItemSelection(view);
-            }
-        };
-
-        void dispatchItemSelection(View view) {
-            if (!isSelected()) {
-                return;
-            }
-            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null
-                    ? mActionsRow.getChildViewHolder(view)
-                    : mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
-            if (ibvh == null) {
-                if (getOnItemViewSelectedListener() != null) {
-                    getOnItemViewSelectedListener().onItemSelected(null, null,
-                            ViewHolder.this, getRow());
-                }
-            } else {
-                if (getOnItemViewSelectedListener() != null) {
-                    getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(),
-                            ViewHolder.this, getRow());
-                }
-            }
-        };
-
-        final RecyclerView.OnScrollListener mScrollListener =
-                new RecyclerView.OnScrollListener() {
-
-            @Override
-            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-            }
-            @Override
-            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-                checkFirstAndLastPosition(true);
-            }
-        };
-
-        private int getViewCenter(View view) {
-            return (view.getRight() - view.getLeft()) / 2;
-        }
-
-        void checkFirstAndLastPosition(boolean fromScroll) {
-            RecyclerView.ViewHolder viewHolder;
-
-            viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
-            boolean showRight = (viewHolder == null
-                    || viewHolder.itemView.getRight() > mActionsRow.getWidth());
-
-            viewHolder = mActionsRow.findViewHolderForPosition(0);
-            boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
-
-            if (DEBUG) {
-                Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll
-                        + " showRight " + showRight + " showLeft " + showLeft);
-            }
-
-            showMoreRight(showRight);
-            showMoreLeft(showLeft);
-        }
-
-        private void showMoreLeft(boolean show) {
-            if (show != mShowMoreLeft) {
-                mActionsRow.setFadingLeftEdge(show);
-                mShowMoreLeft = show;
-            }
-        }
-
-        private void showMoreRight(boolean show) {
-            if (show != mShowMoreRight) {
-                mActionsRow.setFadingRightEdge(show);
-                mShowMoreRight = show;
-            }
-        }
-
-        /**
-         * Constructor for the ViewHolder.
-         *
-         * @param rootView The root View that this view holder will be attached
-         *        to.
-         */
-        public ViewHolder(View rootView, Presenter detailsPresenter) {
-            super(rootView);
-            mOverviewFrame = (FrameLayout) rootView.findViewById(R.id.details_frame);
-            mOverviewView = (ViewGroup) rootView.findViewById(R.id.details_overview);
-            mImageView = (ImageView) rootView.findViewById(R.id.details_overview_image);
-            mRightPanel = (ViewGroup) rootView.findViewById(R.id.details_overview_right_panel);
-            mDetailsDescriptionFrame =
-                    (FrameLayout) mRightPanel.findViewById(R.id.details_overview_description);
-            mActionsRow =
-                    (HorizontalGridView) mRightPanel.findViewById(R.id.details_overview_actions);
-            mActionsRow.setHasOverlappingRendering(false);
-            mActionsRow.setOnScrollListener(mScrollListener);
-            mActionsRow.setAdapter(mActionBridgeAdapter);
-            mActionsRow.setOnChildSelectedListener(mChildSelectedListener);
-
-            final int fadeLength = rootView.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_overview_actions_fade_size);
-            mActionsRow.setFadingRightEdgeLength(fadeLength);
-            mActionsRow.setFadingLeftEdgeLength(fadeLength);
-            mDetailsDescriptionViewHolder =
-                    detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame);
-            mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view);
-        }
-    }
-
-    final Presenter mDetailsPresenter;
-    OnActionClickedListener mActionClickedListener;
-
-    private int mBackgroundColor = Color.TRANSPARENT;
-    private boolean mBackgroundColorSet;
-    private boolean mIsStyleLarge = true;
-
-    private DetailsOverviewSharedElementHelper mSharedElementHelper;
-
-    /**
-     * Constructor for a DetailsOverviewRowPresenter.
-     *
-     * @param detailsPresenter The {@link Presenter} used to render the detailed
-     *        description of the row.
-     */
-    public DetailsOverviewRowPresenter(Presenter detailsPresenter) {
-        setHeaderPresenter(null);
-        setSelectEffectEnabled(false);
-        mDetailsPresenter = detailsPresenter;
-    }
-
-    /**
-     * Sets the listener for Action click events.
-     */
-    public void setOnActionClickedListener(OnActionClickedListener listener) {
-        mActionClickedListener = listener;
-    }
-
-    /**
-     * Returns the listener for Action click events.
-     */
-    public OnActionClickedListener getOnActionClickedListener() {
-        return mActionClickedListener;
-    }
-
-    /**
-     * Sets the background color.  If not set, a default from the theme will be used.
-     */
-    public void setBackgroundColor(@ColorInt int color) {
-        mBackgroundColor = color;
-        mBackgroundColorSet = true;
-    }
-
-    /**
-     * Returns the background color.  If no background color was set, transparent
-     * is returned.
-     */
-    @ColorInt
-    public int getBackgroundColor() {
-        return mBackgroundColor;
-    }
-
-    /**
-     * Sets the layout style to be large or small. This affects the height of
-     * the overview, including the text description. The default is large.
-     */
-    public void setStyleLarge(boolean large) {
-        mIsStyleLarge = large;
-    }
-
-    /**
-     * Returns true if the layout style is large.
-     */
-    public boolean isStyleLarge() {
-        return mIsStyleLarge;
-    }
-
-    /**
-     * Sets the enter transition of target activity to be
-     * transiting into overview row created by this presenter.  The transition will
-     * be cancelled if the overview image is not loaded in the timeout period.
-     * <p>
-     * It assumes shared element passed from calling activity is an ImageView;
-     * the shared element transits to overview image on the starting edge of the detail
-     * overview row, while bounds of overview row grows and reveals text
-     * and action buttons.
-     * <p>
-     * The method must be invoked in target Activity's onCreate().
-     */
-    public final void setSharedElementEnterTransition(Activity activity,
-            String sharedElementName, long timeoutMs) {
-        if (mSharedElementHelper == null) {
-            mSharedElementHelper = new DetailsOverviewSharedElementHelper();
-        }
-        mSharedElementHelper.setSharedElementEnterTransition(activity, sharedElementName,
-                timeoutMs);
-    }
-
-    /**
-     * Sets the enter transition of target activity to be
-     * transiting into overview row created by this presenter.  The transition will
-     * be cancelled if overview image is not loaded in a default timeout period.
-     * <p>
-     * It assumes shared element passed from calling activity is an ImageView;
-     * the shared element transits to overview image on the starting edge of the detail
-     * overview row, while bounds of overview row grows and reveals text
-     * and action buttons.
-     * <p>
-     * The method must be invoked in target Activity's onCreate().
-     */
-    public final void setSharedElementEnterTransition(Activity activity,
-            String sharedElementName) {
-        setSharedElementEnterTransition(activity, sharedElementName, DEFAULT_TIMEOUT);
-    }
-
-    private int getDefaultBackgroundColor(Context context) {
-        TypedValue outValue = new TypedValue();
-        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
-            return context.getResources().getColor(outValue.resourceId);
-        }
-        return context.getResources().getColor(R.color.lb_default_brand_color);
-    }
-
-    @Override
-    protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
-        super.onRowViewSelected(vh, selected);
-        if (selected) {
-            ((ViewHolder) vh).dispatchItemSelection(null);
-        }
-    }
-
-    @Override
-    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
-        View v = LayoutInflater.from(parent.getContext())
-            .inflate(R.layout.lb_details_overview, parent, false);
-        ViewHolder vh = new ViewHolder(v, mDetailsPresenter);
-
-        initDetailsOverview(vh);
-
-        return vh;
-    }
-
-    private int getCardHeight(Context context) {
-        int resId = mIsStyleLarge ? R.dimen.lb_details_overview_height_large :
-            R.dimen.lb_details_overview_height_small;
-        return context.getResources().getDimensionPixelSize(resId);
-    }
-
-    private void initDetailsOverview(final ViewHolder vh) {
-        vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh);
-        final View overview = vh.mOverviewFrame;
-        ViewGroup.LayoutParams lp = overview.getLayoutParams();
-        lp.height = getCardHeight(overview.getContext());
-        overview.setLayoutParams(lp);
-
-        if (!getSelectEffectEnabled()) {
-            vh.mOverviewFrame.setForeground(null);
-        }
-        vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() {
-            @Override
-            public boolean onUnhandledKey(KeyEvent event) {
-                if (vh.getOnKeyListener() != null) {
-                    if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        });
-    }
-
-    private static int getNonNegativeWidth(Drawable drawable) {
-        final int width = (drawable == null) ? 0 : drawable.getIntrinsicWidth();
-        return (width > 0 ? width : 0);
-    }
-
-    private static int getNonNegativeHeight(Drawable drawable) {
-        final int height = (drawable == null) ? 0 : drawable.getIntrinsicHeight();
-        return (height > 0 ? height : 0);
-    }
-
-    void bindImageDrawable(ViewHolder vh) {
-        DetailsOverviewRow row = (DetailsOverviewRow) vh.getRow();
-
-        ViewGroup.MarginLayoutParams layoutParams =
-                (ViewGroup.MarginLayoutParams) vh.mImageView.getLayoutParams();
-        final int cardHeight = getCardHeight(vh.mImageView.getContext());
-        final int verticalMargin = vh.mImageView.getResources().getDimensionPixelSize(
-                R.dimen.lb_details_overview_image_margin_vertical);
-        final int horizontalMargin = vh.mImageView.getResources().getDimensionPixelSize(
-                R.dimen.lb_details_overview_image_margin_horizontal);
-        final int drawableWidth = getNonNegativeWidth(row.getImageDrawable());
-        final int drawableHeight = getNonNegativeHeight(row.getImageDrawable());
-
-        boolean scaleImage = row.isImageScaleUpAllowed();
-        boolean useMargin = false;
-
-        if (row.getImageDrawable() != null) {
-            boolean landscape = false;
-
-            // If large style and landscape image we always use margin.
-            if (drawableWidth > drawableHeight) {
-                landscape = true;
-                if (mIsStyleLarge) {
-                    useMargin = true;
-                }
-            }
-            // If long dimension bigger than the card height we scale down.
-            if ((landscape && drawableWidth > cardHeight)
-                    || (!landscape && drawableHeight > cardHeight)) {
-                scaleImage = true;
-            }
-            // If we're not scaling to fit the card height then we always use margin.
-            if (!scaleImage) {
-                useMargin = true;
-            }
-            // If using margin than may need to scale down.
-            if (useMargin && !scaleImage) {
-                if (landscape && drawableWidth > cardHeight - horizontalMargin) {
-                    scaleImage = true;
-                } else if (!landscape && drawableHeight > cardHeight - 2 * verticalMargin) {
-                    scaleImage = true;
-                }
-            }
-        }
-
-        final int bgColor = mBackgroundColorSet ? mBackgroundColor :
-            getDefaultBackgroundColor(vh.mOverviewView.getContext());
-
-        if (useMargin) {
-            layoutParams.setMarginStart(horizontalMargin);
-            layoutParams.topMargin = layoutParams.bottomMargin = verticalMargin;
-            vh.mOverviewFrame.setBackgroundColor(bgColor);
-            vh.mRightPanel.setBackground(null);
-            vh.mImageView.setBackground(null);
-        } else {
-            layoutParams.leftMargin = layoutParams.topMargin = layoutParams.bottomMargin = 0;
-            vh.mRightPanel.setBackgroundColor(bgColor);
-            vh.mImageView.setBackgroundColor(bgColor);
-            vh.mOverviewFrame.setBackground(null);
-        }
-        RoundedRectHelper.getInstance().setClipToRoundedOutline(vh.mOverviewFrame, true);
-
-        if (scaleImage) {
-            vh.mImageView.setScaleType(ImageView.ScaleType.FIT_START);
-            vh.mImageView.setAdjustViewBounds(true);
-            vh.mImageView.setMaxWidth(cardHeight);
-            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
-            layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
-        } else {
-            vh.mImageView.setScaleType(ImageView.ScaleType.CENTER);
-            vh.mImageView.setAdjustViewBounds(false);
-            layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-            // Limit width to the card height
-            layoutParams.width = Math.min(cardHeight, drawableWidth);
-        }
-        vh.mImageView.setLayoutParams(layoutParams);
-        vh.mImageView.setImageDrawable(row.getImageDrawable());
-        if (row.getImageDrawable() != null && mSharedElementHelper != null) {
-            mSharedElementHelper.onBindToDrawable(vh);
-        }
-    }
-
-    @Override
-    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
-        super.onBindRowViewHolder(holder, item);
-
-        DetailsOverviewRow row = (DetailsOverviewRow) item;
-        ViewHolder vh = (ViewHolder) holder;
-
-        bindImageDrawable(vh);
-        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
-        vh.bindActions(row.getActionsAdapter());
-        row.addListener(vh.mListener);
-    }
-
-    @Override
-    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
-        ViewHolder vh = (ViewHolder) holder;
-        DetailsOverviewRow dor = (DetailsOverviewRow) vh.getRow();
-        dor.removeListener(vh.mListener);
-        if (vh.mDetailsDescriptionViewHolder != null) {
-            mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
-        }
-        super.onUnbindRowViewHolder(holder);
-    }
-
-    @Override
-    public final boolean isUsingDefaultSelectEffect() {
-        return false;
-    }
-
-    @Override
-    protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
-        super.onSelectLevelChanged(holder);
-        if (getSelectEffectEnabled()) {
-            ViewHolder vh = (ViewHolder) holder;
-            int dimmedColor = vh.mColorDimmer.getPaint().getColor();
-            ((ColorDrawable) vh.mOverviewFrame.getForeground().mutate()).setColor(dimmedColor);
-        }
-    }
-
-    @Override
-    protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
-        super.onRowViewAttachedToWindow(vh);
-        if (mDetailsPresenter != null) {
-            mDetailsPresenter.onViewAttachedToWindow(
-                    ((ViewHolder) vh).mDetailsDescriptionViewHolder);
-        }
-    }
-
-    @Override
-    protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
-        super.onRowViewDetachedFromWindow(vh);
-        if (mDetailsPresenter != null) {
-            mDetailsPresenter.onViewDetachedFromWindow(
-                    ((ViewHolder) vh).mDetailsDescriptionViewHolder);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
deleted file mode 100644
index 0d86cfd..0000000
--- a/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ /dev/null
@@ -1,278 +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.v17.leanback.widget;
-
-import android.app.Activity;
-import android.graphics.Matrix;
-import android.os.Handler;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.widget.DetailsOverviewRowPresenter.ViewHolder;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.SharedElementCallback;
-import android.support.v4.view.ViewCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-final class DetailsOverviewSharedElementHelper extends SharedElementCallback {
-
-    static final String TAG = "DetailsTransitionHelper";
-    static final boolean DEBUG = false;
-
-    static class TransitionTimeOutRunnable implements Runnable {
-        WeakReference<DetailsOverviewSharedElementHelper> mHelperRef;
-
-        TransitionTimeOutRunnable(DetailsOverviewSharedElementHelper helper) {
-            mHelperRef = new WeakReference<DetailsOverviewSharedElementHelper>(helper);
-        }
-
-        @Override
-        public void run() {
-            DetailsOverviewSharedElementHelper helper = mHelperRef.get();
-            if (helper == null) {
-                return;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "timeout " + helper.mActivityToRunTransition);
-            }
-            helper.startPostponedEnterTransition();
-        }
-    }
-
-    ViewHolder mViewHolder;
-    Activity mActivityToRunTransition;
-    boolean mStartedPostpone;
-    String mSharedElementName;
-    int mRightPanelWidth;
-    int mRightPanelHeight;
-
-    private ScaleType mSavedScaleType;
-    private Matrix mSavedMatrix;
-
-    private boolean hasImageViewScaleChange(View snapshotView) {
-        return snapshotView instanceof ImageView;
-    }
-
-    private void saveImageViewScale() {
-        if (mSavedScaleType == null) {
-            // only save first time after initialize/restoreImageViewScale()
-            ImageView imageView = mViewHolder.mImageView;
-            mSavedScaleType = imageView.getScaleType();
-            mSavedMatrix = mSavedScaleType == ScaleType.MATRIX ? imageView.getMatrix() : null;
-            if (DEBUG) {
-                Log.d(TAG, "saveImageViewScale: "+mSavedScaleType);
-            }
-        }
-    }
-
-    private static void updateImageViewAfterScaleTypeChange(ImageView imageView) {
-        // enforcing imageView to update its internal bounds/matrix immediately
-        imageView.measure(
-                MeasureSpec.makeMeasureSpec(imageView.getMeasuredWidth(), MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(imageView.getMeasuredHeight(), MeasureSpec.EXACTLY));
-        imageView.layout(imageView.getLeft(), imageView.getTop(),
-                imageView.getRight(), imageView.getBottom());
-    }
-
-    private void changeImageViewScale(View snapshotView) {
-        ImageView snapshotImageView = (ImageView) snapshotView;
-        ImageView imageView = mViewHolder.mImageView;
-        if (DEBUG) {
-            Log.d(TAG, "changeImageViewScale to "+snapshotImageView.getScaleType());
-        }
-        imageView.setScaleType(snapshotImageView.getScaleType());
-        if (snapshotImageView.getScaleType() == ScaleType.MATRIX) {
-            imageView.setImageMatrix(snapshotImageView.getImageMatrix());
-        }
-        updateImageViewAfterScaleTypeChange(imageView);
-    }
-
-    private void restoreImageViewScale() {
-        if (mSavedScaleType != null) {
-            if (DEBUG) {
-                Log.d(TAG, "restoreImageViewScale to "+mSavedScaleType);
-            }
-            ImageView imageView = mViewHolder.mImageView;
-            imageView.setScaleType(mSavedScaleType);
-            if (mSavedScaleType == ScaleType.MATRIX) {
-                imageView.setImageMatrix(mSavedMatrix);
-            }
-            // only restore once unless another save happens
-            mSavedScaleType = null;
-            updateImageViewAfterScaleTypeChange(imageView);
-        }
-    }
-
-    @Override
-    public void onSharedElementStart(List<String> sharedElementNames,
-            List<View> sharedElements, List<View> sharedElementSnapshots) {
-        if (DEBUG) {
-            Log.d(TAG, "onSharedElementStart " + mActivityToRunTransition);
-        }
-        if (sharedElements.size() < 1) {
-            return;
-        }
-        View overviewView = sharedElements.get(0);
-        if (mViewHolder == null || mViewHolder.mOverviewFrame != overviewView) {
-            return;
-        }
-        View snapshot = sharedElementSnapshots.get(0);
-        if (hasImageViewScaleChange(snapshot)) {
-            saveImageViewScale();
-            changeImageViewScale(snapshot);
-        }
-        View imageView = mViewHolder.mImageView;
-        final int width = overviewView.getWidth();
-        final int height = overviewView.getHeight();
-        imageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
-        imageView.layout(0, 0, width, height);
-        final View rightPanel = mViewHolder.mRightPanel;
-        if (mRightPanelWidth != 0 && mRightPanelHeight != 0) {
-            rightPanel.measure(MeasureSpec.makeMeasureSpec(mRightPanelWidth, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(mRightPanelHeight, MeasureSpec.EXACTLY));
-            rightPanel.layout(width, rightPanel.getTop(), width + mRightPanelWidth,
-                    rightPanel.getTop() + mRightPanelHeight);
-        } else {
-            rightPanel.offsetLeftAndRight(width - rightPanel.getLeft());
-        }
-        mViewHolder.mActionsRow.setVisibility(View.INVISIBLE);
-        mViewHolder.mDetailsDescriptionFrame.setVisibility(View.INVISIBLE);
-    }
-
-    @Override
-    public void onSharedElementEnd(List<String> sharedElementNames,
-            List<View> sharedElements, List<View> sharedElementSnapshots) {
-        if (DEBUG) {
-            Log.d(TAG, "onSharedElementEnd " + mActivityToRunTransition);
-        }
-        if (sharedElements.size() < 1) {
-            return;
-        }
-        View overviewView = sharedElements.get(0);
-        if (mViewHolder == null || mViewHolder.mOverviewFrame != overviewView) {
-            return;
-        }
-        restoreImageViewScale();
-        // temporary let action row take focus so we defer button background animation
-        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);
-    }
-
-    void setSharedElementEnterTransition(Activity activity, String sharedElementName,
-            long timeoutMs) {
-        if ((activity == null && !TextUtils.isEmpty(sharedElementName))
-                || (activity != null && TextUtils.isEmpty(sharedElementName))) {
-            throw new IllegalArgumentException();
-        }
-        if (activity == mActivityToRunTransition
-                && TextUtils.equals(sharedElementName, mSharedElementName)) {
-            return;
-        }
-        if (mActivityToRunTransition != null) {
-            ActivityCompat.setEnterSharedElementCallback(mActivityToRunTransition, null);
-        }
-        mActivityToRunTransition = activity;
-        mSharedElementName = sharedElementName;
-        if (DEBUG) {
-            Log.d(TAG, "postponeEnterTransition " + mActivityToRunTransition);
-        }
-        ActivityCompat.setEnterSharedElementCallback(mActivityToRunTransition, this);
-        ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
-        if (timeoutMs > 0) {
-            new Handler().postDelayed(new TransitionTimeOutRunnable(this), timeoutMs);
-        }
-    }
-
-    void onBindToDrawable(ViewHolder vh) {
-        if (DEBUG) {
-            Log.d(TAG, "onBindToDrawable, could start transition of " + mActivityToRunTransition);
-        }
-        if (mViewHolder != null) {
-            if (DEBUG) {
-                Log.d(TAG, "rebind? clear transitionName on current viewHolder "
-                        + mViewHolder.mOverviewFrame);
-            }
-            ViewCompat.setTransitionName(mViewHolder.mOverviewFrame, null);
-        }
-        // After we got a image drawable,  we can determine size of right panel.
-        // We want right panel to have fixed size so that the right panel don't change size
-        // when the overview is layout as a small bounds in transition.
-        mViewHolder = vh;
-        mViewHolder.mRightPanel.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) {
-                mViewHolder.mRightPanel.removeOnLayoutChangeListener(this);
-                mRightPanelWidth = mViewHolder.mRightPanel.getWidth();
-                mRightPanelHeight = mViewHolder.mRightPanel.getHeight();
-                if (DEBUG) {
-                    Log.d(TAG, "onLayoutChange records size of right panel as "
-                            + mRightPanelWidth + ", "+ mRightPanelHeight);
-                }
-            }
-        });
-        mViewHolder.mRightPanel.postOnAnimation(new Runnable() {
-            @Override
-            public void run() {
-                if (DEBUG) {
-                    Log.d(TAG, "setTransitionName "+mViewHolder.mOverviewFrame);
-                }
-                ViewCompat.setTransitionName(mViewHolder.mOverviewFrame, mSharedElementName);
-                Object transition = TransitionHelper.getSharedElementEnterTransition(
-                        mActivityToRunTransition.getWindow());
-                if (transition != null) {
-                    TransitionHelper.addTransitionListener(transition, new TransitionListener() {
-                        @Override
-                        public void onTransitionEnd(Object transition) {
-                            if (DEBUG) {
-                                Log.d(TAG, "onTransitionEnd " + mActivityToRunTransition);
-                            }
-                            // after transition if the action row still focused, transfer
-                            // focus to its children
-                            if (mViewHolder.mActionsRow.isFocused()) {
-                                mViewHolder.mActionsRow.requestFocus();
-                            }
-                            TransitionHelper.removeTransitionListener(transition, this);
-                        }
-                    });
-                }
-                startPostponedEnterTransition();
-            }
-        });
-    }
-
-    void startPostponedEnterTransition() {
-        if (!mStartedPostpone) {
-            if (DEBUG) {
-                Log.d(TAG, "startPostponedEnterTransition " + mActivityToRunTransition);
-            }
-            ActivityCompat.startPostponedEnterTransition(mActivityToRunTransition);
-            mStartedPostpone = true;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/DetailsParallax.java b/android/support/v17/leanback/widget/DetailsParallax.java
deleted file mode 100644
index ad5f13a..0000000
--- a/android/support/v17/leanback/widget/DetailsParallax.java
+++ /dev/null
@@ -1,69 +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.v17.leanback.widget;
-
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.app.DetailsFragment;
-import android.support.v17.leanback.app.DetailsSupportFragment;
-
-/**
- * Subclass of Parallax object that tracks overview row's top and bottom edge in DetailsFragment
- * or DetailsSupportFragment.
- * <p>
- * It can be used for both creating cover image parallax effect and controlling video playing
- * when transitioning to/from half/full screen.  A direct use case is
- * {@link android.support.v17.leanback.app.DetailsFragmentBackgroundController}.
- * </p>
- * @see DetailsFragment#getParallax()
- * @see android.support.v17.leanback.app.DetailsFragmentBackgroundController
- * @see DetailsSupportFragment#getParallax()
- * @see android.support.v17.leanback.app.DetailsSupportFragmentBackgroundController
- */
-public class DetailsParallax extends RecyclerViewParallax {
-    final IntProperty mFrameTop;
-    final IntProperty mFrameBottom;
-
-    public DetailsParallax() {
-        // track the top edge of details_frame of first item of adapter
-        mFrameTop = addProperty("overviewRowTop")
-                .adapterPosition(0)
-                .viewId(R.id.details_frame);
-
-        // track the bottom edge of details_frame of first item of adapter
-        mFrameBottom = addProperty("overviewRowBottom")
-                .adapterPosition(0)
-                .viewId(R.id.details_frame)
-                .fraction(1.0f);
-
-    }
-
-    /**
-     * Returns the top of the details overview row. This is tracked for implementing the
-     * parallax effect.
-     */
-    public Parallax.IntProperty getOverviewRowTop() {
-        return mFrameTop;
-    }
-
-    /**
-     * Returns the bottom of the details overview row. This is tracked for implementing the
-     * parallax effect.
-     */
-    public Parallax.IntProperty getOverviewRowBottom() {
-        return mFrameBottom;
-    }
-}
diff --git a/android/support/v17/leanback/widget/DetailsParallaxDrawable.java b/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
deleted file mode 100644
index 1eea797..0000000
--- a/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
+++ /dev/null
@@ -1,195 +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.v17.leanback.widget;
-
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.graphics.CompositeDrawable;
-import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
-import android.util.TypedValue;
-
-/**
- * Helper class responsible for wiring in parallax effect in
- * {@link android.support.v17.leanback.app.DetailsFragment}. The default effect will render
- * a drawable like the following two parts, cover drawable above DetailsOverviewRow and solid
- * color below DetailsOverviewRow.
- * <pre>
- *        ***************************
- *        *        Cover Drawable   *
- *        ***************************
- *        *    DetailsOverviewRow   *
- *        *                         *
- *        ***************************
- *        *     Bottom Drawable     *
- *        *      (Solid Color)      *
- *        *         Related         *
- *        *         Content         *
- *        ***************************
- * </pre>
- * <ul>
- * <li>
- * Call {@link #DetailsParallaxDrawable(Context, DetailsParallax)} to create DetailsParallaxDrawable
- * using {@link FitWidthBitmapDrawable} for cover drawable.
- * </li>
- * </ul>
- * <li>
- * In case the solid color is not set, it will use defaultBrandColorDark from LeanbackTheme.
- * </li>
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class DetailsParallaxDrawable extends CompositeDrawable {
-    private Drawable mBottomDrawable;
-
-    /**
-     * Creates a DetailsParallaxDrawable using a cover drawable.
-     * @param context Context to get resource values.
-     * @param parallax DetailsParallax to add background parallax effect.
-     * @param coverDrawable Cover drawable at top
-     * @param coverDrawableParallaxTarget Define a ParallaxTarget that would be performed on cover
-     *                                    Drawable. e.g. To change "verticalOffset" of cover
-     *                                    Drawable from 0 to 120 pixels above screen, uses:
-     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
-     *                                        coverDrawable,
-     *                                        PropertyValuesHolder.ofInt("verticalOffset", 0, -120))
-     */
-    public DetailsParallaxDrawable(Context context, DetailsParallax parallax,
-                                   Drawable coverDrawable,
-                                   ParallaxTarget coverDrawableParallaxTarget) {
-        init(context, parallax, coverDrawable, new ColorDrawable(), coverDrawableParallaxTarget);
-    }
-
-    /**
-     * Creates a DetailsParallaxDrawable using a cover drawable and bottom drawable.
-     * @param context Context to get resource values.
-     * @param parallax DetailsParallax to add background parallax effect.
-     * @param coverDrawable Cover drawable at top
-     * @param bottomDrawable Bottom drawable, when null it will create a default ColorDrawable.
-     * @param coverDrawableParallaxTarget Define a ParallaxTarget that would be performed on cover
-     *                                    Drawable. e.g. To change "verticalOffset" of cover
-     *                                    Drawable from 0 to 120 pixels above screen, uses:
-     *                                    new ParallaxTarget.PropertyValuesHolderTarget(
-     *                                        coverDrawable,
-     *                                        PropertyValuesHolder.ofInt("verticalOffset", 0, -120))
-     */
-    public DetailsParallaxDrawable(Context context, DetailsParallax parallax,
-                                   Drawable coverDrawable, Drawable bottomDrawable,
-                                   ParallaxTarget coverDrawableParallaxTarget) {
-
-        init(context, parallax, coverDrawable, bottomDrawable, coverDrawableParallaxTarget);
-    }
-
-    /**
-     * Creates DetailsParallaxDrawable using {@link FitWidthBitmapDrawable} for cover drawable.
-     * @param context Context to get resource values.
-     * @param parallax DetailsParallax to add background parallax effect.
-     */
-    public DetailsParallaxDrawable(Context context, DetailsParallax parallax) {
-        int verticalMovementMax = -context.getResources().getDimensionPixelSize(
-                R.dimen.lb_details_cover_drawable_parallax_movement);
-        Drawable coverDrawable = new FitWidthBitmapDrawable();
-        ParallaxTarget coverDrawableParallaxTarget = new ParallaxTarget.PropertyValuesHolderTarget(
-                coverDrawable, PropertyValuesHolder.ofInt("verticalOffset", 0,
-                verticalMovementMax));
-        init(context, parallax, coverDrawable, new ColorDrawable(), coverDrawableParallaxTarget);
-    }
-
-    void init(Context context, DetailsParallax parallax,
-              Drawable coverDrawable, Drawable bottomDrawable,
-              ParallaxTarget coverDrawableParallaxTarget) {
-        if (bottomDrawable instanceof ColorDrawable) {
-            ColorDrawable colorDrawable = ((ColorDrawable) bottomDrawable);
-            if (colorDrawable.getColor() == Color.TRANSPARENT) {
-                colorDrawable.setColor(getDefaultBackgroundColor(context));
-            }
-        }
-        addChildDrawable(coverDrawable);
-        addChildDrawable(mBottomDrawable = bottomDrawable);
-        connect(context, parallax, coverDrawableParallaxTarget);
-    }
-
-    private static int getDefaultBackgroundColor(Context context) {
-        TypedValue outValue = new TypedValue();
-        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColorDark, outValue, true)) {
-            return context.getResources().getColor(outValue.resourceId);
-        }
-        return context.getResources().getColor(R.color.lb_default_brand_color_dark);
-    }
-
-    /**
-     * @return First child which is cover drawable appearing at top.
-     */
-    public Drawable getCoverDrawable() {
-        return getChildAt(0).getDrawable();
-    }
-
-    /**
-     * @return Second child which is ColorDrawable by default.
-     */
-    public Drawable getBottomDrawable() {
-        return mBottomDrawable;
-    }
-
-    /**
-     * Changes the solid background color of the related content section.
-     */
-    public void setSolidColor(@ColorInt int color) {
-        ((ColorDrawable) mBottomDrawable).setColor(color);
-    }
-
-    /**
-     * @return Returns the solid background color of the related content section.
-     */
-    public @ColorInt int getSolidColor() {
-        return ((ColorDrawable) mBottomDrawable).getColor();
-    }
-
-    /**
-     * Connects DetailsParallaxDrawable to DetailsParallax object.
-     * @param parallax The DetailsParallax object to add ParallaxEffects for the drawable.
-     */
-    void connect(Context context, DetailsParallax parallax,
-                        ParallaxTarget coverDrawableParallaxTarget) {
-
-        Parallax.IntProperty frameTop = parallax.getOverviewRowTop();
-        Parallax.IntProperty frameBottom = parallax.getOverviewRowBottom();
-
-        final int fromValue = context.getResources()
-                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions);
-        final int toValue = context.getResources()
-                .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description);
-        parallax.addEffect(frameTop.atAbsolute(fromValue), frameTop.atAbsolute(toValue))
-                .target(coverDrawableParallaxTarget);
-
-        // Add solid color parallax effect:
-        // When frameBottom moves from bottom of the screen to top of the screen,
-        // change solid ColorDrawable's top from bottom of screen to top of the screen.
-        parallax.addEffect(frameBottom.atMax(), frameBottom.atMin())
-                .target(getChildAt(1), ChildDrawable.TOP_ABSOLUTE);
-        // Also when frameTop moves from bottom of screen to top of the screen,
-        // we are changing bottom of the bitmap from bottom of screen to top of screen.
-        parallax.addEffect(frameTop.atMax(), frameTop.atMin())
-                .target(getChildAt(0), ChildDrawable.BOTTOM_ABSOLUTE);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/DiffCallback.java b/android/support/v17/leanback/widget/DiffCallback.java
deleted file mode 100644
index c513e73..0000000
--- a/android/support/v17/leanback/widget/DiffCallback.java
+++ /dev/null
@@ -1,67 +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.v17.leanback.widget;
-
-import android.support.annotation.NonNull;
-
-import java.util.List;
-
-/**
- * Callback that informs {@link ArrayObjectAdapter} how to compute list updates when using
- * {@link android.support.v7.util.DiffUtil} in {@link ArrayObjectAdapter#setItems(List,
- * DiffCallback)} method.
- * <p>
- * The {@link ArrayObjectAdapter#setItems(List,
- * DiffCallback)} method 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.
-     * @see android.support.v7.util.DiffUtil.Callback#areItemsTheSame(int, int)
-     */
-    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.
-     * @see android.support.v7.util.DiffUtil.Callback#areContentsTheSame(int, int)
-     */
-    public abstract boolean areContentsTheSame(@NonNull Value oldItem, @NonNull Value newItem);
-
-    /**
-     * Called to get a change payload between an old and new version of an item.
-     *
-     * @see android.support.v7.util.DiffUtil.Callback#getChangePayload(int, int)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public Object getChangePayload(@NonNull Value oldItem, @NonNull Value newItem) {
-        return null;
-    }
-}
diff --git a/android/support/v17/leanback/widget/DividerPresenter.java b/android/support/v17/leanback/widget/DividerPresenter.java
deleted file mode 100644
index 42da2ff..0000000
--- a/android/support/v17/leanback/widget/DividerPresenter.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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * DividerPresenter provides a default presentation for {@link DividerRow} in HeadersFragment.
- */
-public class DividerPresenter extends Presenter {
-
-    private final int mLayoutResourceId;
-
-    public DividerPresenter() {
-        this(R.layout.lb_divider);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public DividerPresenter(int layoutResourceId) {
-        mLayoutResourceId = layoutResourceId;
-    }
-
-    @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        View headerView = LayoutInflater.from(parent.getContext())
-                .inflate(mLayoutResourceId, parent, false);
-
-        return new ViewHolder(headerView);
-    }
-
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/DividerRow.java b/android/support/v17/leanback/widget/DividerRow.java
deleted file mode 100644
index 1b3a016..0000000
--- a/android/support/v17/leanback/widget/DividerRow.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.v17.leanback.widget;
-
-/**
- * Used to represent divider in HeadersFragment.
- */
-public class DividerRow extends Row {
-
-    public DividerRow() {
-    }
-
-    @Override
-    final public boolean isRenderedAsRowView() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/FacetProvider.java b/android/support/v17/leanback/widget/FacetProvider.java
deleted file mode 100644
index f00f611..0000000
--- a/android/support/v17/leanback/widget/FacetProvider.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.v17.leanback.widget;
-
-/**
- * This is the query interface to supply optional features(aka facets) on an object without the need
- * of letting the object to subclass or implement java interfaces.
- */
-public interface FacetProvider {
-
-    /**
-     * Queries optional implemented facet.
-     * @param facetClass  Facet classes to query,  examples are: class of
-     *                    {@link ItemAlignmentFacet}.
-     * @return Facet implementation for the facetClass or null if feature not implemented.
-     */
-    public Object getFacet(Class<?> facetClass);
-
-}
diff --git a/android/support/v17/leanback/widget/FacetProviderAdapter.java b/android/support/v17/leanback/widget/FacetProviderAdapter.java
deleted file mode 100644
index b818d65..0000000
--- a/android/support/v17/leanback/widget/FacetProviderAdapter.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.v17.leanback.widget;
-
-import android.support.v7.widget.RecyclerView;
-
-/**
- * Optional interface that implemented by {@link RecyclerView.Adapter} to
- * query {@link FacetProvider} for a given type within Adapter.  Note that
- * {@link RecyclerView.ViewHolder} may also implement {@link FacetProvider} which
- * has a higher priority than the one returned from the FacetProviderAdapter.
- */
-public interface FacetProviderAdapter {
-
-    /**
-     * Queries {@link FacetProvider} for a given type within Adapter.
-     * @param type        type of the item.
-     * @return Facet provider for the type.
-     */
-    public FacetProvider getFacetProvider(int type);
-
-}
diff --git a/android/support/v17/leanback/widget/FocusHighlight.java b/android/support/v17/leanback/widget/FocusHighlight.java
deleted file mode 100644
index 5b1ad6c..0000000
--- a/android/support/v17/leanback/widget/FocusHighlight.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.v17.leanback.widget;
-
-/**
- * Interface for highlighting the item that has focus.
- *
- */
-public interface FocusHighlight {
-    /**
-     * No zoom factor.
-     */
-    public static final int ZOOM_FACTOR_NONE = 0;
-
-    /**
-     * A small zoom factor, recommended for large item views.
-     */
-    public static final int ZOOM_FACTOR_SMALL = 1;
-
-    /**
-     * A medium zoom factor, recommended for medium sized item views.
-     */
-    public static final int ZOOM_FACTOR_MEDIUM = 2;
-
-    /**
-     * A large zoom factor, recommended for small item views.
-     */
-    public static final int ZOOM_FACTOR_LARGE = 3;
-
-    /**
-     * An extra small zoom factor.
-     */
-    public static final int ZOOM_FACTOR_XSMALL = 4;
-}
diff --git a/android/support/v17/leanback/widget/FocusHighlightHandler.java b/android/support/v17/leanback/widget/FocusHighlightHandler.java
deleted file mode 100644
index 83c248a..0000000
--- a/android/support/v17/leanback/widget/FocusHighlightHandler.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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.View;
-
-/**
- * Interface for highlighting the item that has focus.
- */
-interface FocusHighlightHandler {
-    /**
-     * Called when an item gains or loses focus.
-     * @hide
-     *
-     * @param view The view whose focus is changing.
-     * @param hasFocus True if focus is gained; false otherwise.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    void onItemFocused(View view, boolean hasFocus);
-
-    /**
-     * Called when the view is being created.
-     */
-    void onInitializeView(View view);
-}
diff --git a/android/support/v17/leanback/widget/FocusHighlightHelper.java b/android/support/v17/leanback/widget/FocusHighlightHelper.java
deleted file mode 100644
index 49565ab..0000000
--- a/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ /dev/null
@@ -1,341 +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.v17.leanback.widget;
-
-import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_LARGE;
-import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_MEDIUM;
-import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_NONE;
-import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_SMALL;
-import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_XSMALL;
-
-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;
-import android.view.animation.Interpolator;
-
-/**
- * Sets up the highlighting behavior when an item gains focus.
- */
-public class FocusHighlightHelper {
-
-    static boolean isValidZoomIndex(int zoomIndex) {
-        return zoomIndex == ZOOM_FACTOR_NONE || getResId(zoomIndex) > 0;
-    }
-
-    static int getResId(int zoomIndex) {
-        switch (zoomIndex) {
-            case ZOOM_FACTOR_SMALL:
-                return R.fraction.lb_focus_zoom_factor_small;
-            case ZOOM_FACTOR_XSMALL:
-                return R.fraction.lb_focus_zoom_factor_xsmall;
-            case ZOOM_FACTOR_MEDIUM:
-                return R.fraction.lb_focus_zoom_factor_medium;
-            case ZOOM_FACTOR_LARGE:
-                return R.fraction.lb_focus_zoom_factor_large;
-            default:
-                return 0;
-        }
-    }
-
-
-    static class FocusAnimator implements TimeAnimator.TimeListener {
-        private final View mView;
-        private final int mDuration;
-        private final ShadowOverlayContainer mWrapper;
-        private final float mScaleDiff;
-        private float mFocusLevel = 0f;
-        private float mFocusLevelStart;
-        private float mFocusLevelDelta;
-        private final TimeAnimator mAnimator = new TimeAnimator();
-        private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
-        private final ColorOverlayDimmer mDimmer;
-
-        void animateFocus(boolean select, boolean immediate) {
-            endAnimation();
-            final float end = select ? 1 : 0;
-            if (immediate) {
-                setFocusLevel(end);
-            } else if (mFocusLevel != end) {
-                mFocusLevelStart = mFocusLevel;
-                mFocusLevelDelta = end - mFocusLevelStart;
-                mAnimator.start();
-            }
-        }
-
-        FocusAnimator(View view, float scale, boolean useDimmer, int duration) {
-            mView = view;
-            mDuration = duration;
-            mScaleDiff = scale - 1f;
-            if (view instanceof ShadowOverlayContainer) {
-                mWrapper = (ShadowOverlayContainer) view;
-            } else {
-                mWrapper = null;
-            }
-            mAnimator.setTimeListener(this);
-            if (useDimmer) {
-                mDimmer = ColorOverlayDimmer.createDefault(view.getContext());
-            } else {
-                mDimmer = null;
-            }
-        }
-
-        void setFocusLevel(float level) {
-            mFocusLevel = level;
-            float scale = 1f + mScaleDiff * level;
-            mView.setScaleX(scale);
-            mView.setScaleY(scale);
-            if (mWrapper != null) {
-                mWrapper.setShadowFocusLevel(level);
-            } else {
-                ShadowOverlayHelper.setNoneWrapperShadowFocusLevel(mView, level);
-            }
-            if (mDimmer != null) {
-                mDimmer.setActiveLevel(level);
-                int color = mDimmer.getPaint().getColor();
-                if (mWrapper != null) {
-                    mWrapper.setOverlayColor(color);
-                } else {
-                    ShadowOverlayHelper.setNoneWrapperOverlayColor(mView, color);
-                }
-            }
-        }
-
-        float getFocusLevel() {
-            return mFocusLevel;
-        }
-
-        void endAnimation() {
-            mAnimator.end();
-        }
-
-        @Override
-        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-            float fraction;
-            if (totalTime >= mDuration) {
-                fraction = 1;
-                mAnimator.end();
-            } else {
-                fraction = (float) (totalTime / (double) mDuration);
-            }
-            if (mInterpolator != null) {
-                fraction = mInterpolator.getInterpolation(fraction);
-            }
-            setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta);
-        }
-    }
-
-    static class BrowseItemFocusHighlight implements FocusHighlightHandler {
-        private static final int DURATION_MS = 150;
-
-        private int mScaleIndex;
-        private final boolean mUseDimmer;
-
-        BrowseItemFocusHighlight(int zoomIndex, boolean useDimmer) {
-            if (!isValidZoomIndex(zoomIndex)) {
-                throw new IllegalArgumentException("Unhandled zoom index");
-            }
-            mScaleIndex = zoomIndex;
-            mUseDimmer = useDimmer;
-        }
-
-        private float getScale(Resources res) {
-            return mScaleIndex == ZOOM_FACTOR_NONE ? 1f :
-                    res.getFraction(getResId(mScaleIndex), 1, 1);
-        }
-
-        @Override
-        public void onItemFocused(View view, boolean hasFocus) {
-            view.setSelected(hasFocus);
-            getOrCreateAnimator(view).animateFocus(hasFocus, false);
-        }
-
-        @Override
-        public void onInitializeView(View view) {
-            getOrCreateAnimator(view).animateFocus(false, true);
-        }
-
-        private FocusAnimator getOrCreateAnimator(View view) {
-            FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
-            if (animator == null) {
-                animator = new FocusAnimator(
-                        view, getScale(view.getResources()), mUseDimmer, DURATION_MS);
-                view.setTag(R.id.lb_focus_animator, animator);
-            }
-            return animator;
-        }
-
-    }
-
-    /**
-     * Sets up the focus highlight behavior of a focused item in browse list row. App usually does
-     * not call this method, it uses {@link ListRowPresenter#ListRowPresenter(int, boolean)}.
-     *
-     * @param zoomIndex One of {@link FocusHighlight#ZOOM_FACTOR_SMALL}
-     * {@link FocusHighlight#ZOOM_FACTOR_XSMALL}
-     * {@link FocusHighlight#ZOOM_FACTOR_MEDIUM}
-     * {@link FocusHighlight#ZOOM_FACTOR_LARGE}
-     * {@link FocusHighlight#ZOOM_FACTOR_NONE}.
-     * @param useDimmer Allow dimming browse item when unselected.
-     * @param adapter  adapter of the list row.
-     */
-    public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex,
-            boolean useDimmer) {
-        adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex, useDimmer));
-    }
-
-    /**
-     * 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).
-     *
-     * @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.
-     *
-     * @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) {
-            ((ItemBridgeAdapter) gridView.getAdapter())
-                    .setFocusHighlight(new HeaderItemFocusHighlight(scaleEnabled));
-        }
-    }
-
-    /**
-     * 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;
-        private int mDuration;
-        boolean mScaleEnabled;
-
-        HeaderItemFocusHighlight(boolean scaleEnabled) {
-            mScaleEnabled = scaleEnabled;
-        }
-
-        void lazyInit(View view) {
-            if (!mInitialized) {
-                Resources res = view.getResources();
-                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;
-            }
-        }
-
-        static class HeaderFocusAnimator extends FocusAnimator {
-
-            ItemBridgeAdapter.ViewHolder mViewHolder;
-            HeaderFocusAnimator(View view, float scale, int duration) {
-                super(view, scale, false, duration);
-
-                ViewParent parent = view.getParent();
-                while (parent != null) {
-                    if (parent instanceof RecyclerView) {
-                        break;
-                    }
-                    parent = parent.getParent();
-                }
-                if (parent != null) {
-                    mViewHolder = (ItemBridgeAdapter.ViewHolder) ((RecyclerView) parent)
-                            .getChildViewHolder(view);
-                }
-            }
-
-            @Override
-            void setFocusLevel(float level) {
-                Presenter presenter = mViewHolder.getPresenter();
-                if (presenter instanceof RowHeaderPresenter) {
-                    ((RowHeaderPresenter) presenter).setSelectLevel(
-                            ((RowHeaderPresenter.ViewHolder) mViewHolder.getViewHolder()), level);
-                }
-                super.setFocusLevel(level);
-            }
-
-        }
-
-        private void viewFocused(View view, boolean hasFocus) {
-            lazyInit(view);
-            view.setSelected(hasFocus);
-            FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator);
-            if (animator == null) {
-                animator = new HeaderFocusAnimator(view, mSelectScale, mDuration);
-                view.setTag(R.id.lb_focus_animator, animator);
-            }
-            animator.animateFocus(hasFocus, false);
-        }
-
-        @Override
-        public void onItemFocused(View view, boolean hasFocus) {
-            viewFocused(view, hasFocus);
-        }
-
-        @Override
-        public void onInitializeView(View view) {
-        }
-
-    }
-}
diff --git a/android/support/v17/leanback/widget/ForegroundHelper.java b/android/support/v17/leanback/widget/ForegroundHelper.java
deleted file mode 100644
index 807d441..0000000
--- a/android/support/v17/leanback/widget/ForegroundHelper.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.view.View;
-
-final class ForegroundHelper {
-    /**
-     * Returns true if view.setForeground() is supported.
-     */
-    static boolean supportsForeground() {
-        return Build.VERSION.SDK_INT >= 23;
-    }
-
-    static Drawable getForeground(View view) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return view.getForeground();
-        } else {
-            return null;
-        }
-    }
-
-    static void setForeground(View view, Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            view.setForeground(drawable);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/FragmentAnimationProvider.java b/android/support/v17/leanback/widget/FragmentAnimationProvider.java
deleted file mode 100644
index 1c5dcb5..0000000
--- a/android/support/v17/leanback/widget/FragmentAnimationProvider.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.v17.leanback.widget;
-
-import android.animation.Animator;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-
-/**
- * FragmentAnimationProvider supplies animations for use during a fragment's onCreateAnimator
- * callback. Animators added here will be added to an animation set and played together. This
- * allows presenters used by a fragment to control their own fragment lifecycle animations.
- */
-public interface FragmentAnimationProvider {
-
-    /**
-     * Animates the fragment in response to the IME appearing.
-     * @param animators A list of animations to which this provider's animations should be added.
-     */
-    public abstract void onImeAppearing(@NonNull List<Animator> animators);
-
-    /**
-     * Animates the fragment in response to the IME disappearing.
-     * @param animators A list of animations to which this provider's animations should be added.
-     */
-    public abstract void onImeDisappearing(@NonNull List<Animator> animators);
-
-}
diff --git a/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java b/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
deleted file mode 100644
index dad4414..0000000
--- a/android/support/v17/leanback/widget/FullWidthDetailsOverviewRowPresenter.java
+++ /dev/null
@@ -1,787 +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.v17.leanback.widget;
-
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.v17.leanback.R;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.FrameLayout;
-
-/**
- * Renders a {@link DetailsOverviewRow} to display an overview of an item. Typically this row will
- * be the first row in a fragment such as the
- * {@link android.support.v17.leanback.app.DetailsFragment}. The View created by the
- * FullWidthDetailsOverviewRowPresenter is made in three parts: logo view on the left, action list view on
- * the top and a customizable detailed description view on the right.
- *
- * <p>The detailed description is rendered using a {@link Presenter} passed in
- * {@link #FullWidthDetailsOverviewRowPresenter(Presenter)}. Typically this will be an instance of
- * {@link AbstractDetailsDescriptionPresenter}. The application can access the detailed description
- * ViewHolder from {@link ViewHolder#getDetailsDescriptionViewHolder()}.
- * </p>
- *
- * <p>The logo view is rendered using a customizable {@link DetailsOverviewLogoPresenter} passed in
- * {@link #FullWidthDetailsOverviewRowPresenter(Presenter, DetailsOverviewLogoPresenter)}. The application
- * can access the logo ViewHolder from {@link ViewHolder#getLogoViewHolder()}.
- * </p>
- *
- * <p>
- * To support activity shared element transition, call {@link #setListener(Listener)} with
- * {@link FullWidthDetailsOverviewSharedElementHelper} during Activity's onCreate(). Application is free to
- * create its own "shared element helper" class using the Listener for image binding.
- * Call {@link #setParticipatingEntranceTransition(boolean)} with false
- * </p>
- *
- * <p>
- * The view has three states: {@link #STATE_HALF} {@link #STATE_FULL} and {@link #STATE_SMALL}. See
- * {@link android.support.v17.leanback.app.DetailsFragment} where it switches states based on
- * selected row position.
- * </p>
- */
-public class FullWidthDetailsOverviewRowPresenter extends RowPresenter {
-
-    static final String TAG = "FullWidthDetailsRP";
-    static final boolean DEBUG = false;
-
-    private static Rect sTmpRect = new Rect();
-    static final Handler sHandler = new Handler();
-
-    /**
-     * This is the default state corresponding to layout file.  The view takes full width
-     * of screen and covers bottom half of the screen.
-     */
-    public static final int STATE_HALF = 0;
-    /**
-     * This is the state when the view covers full width and height of screen.
-     */
-    public static final int STATE_FULL = 1;
-    /**
-     * This is the state where the view shrinks to a small banner.
-     */
-    public static final int STATE_SMALL = 2;
-
-    /**
-     * This is the alignment mode that the logo and description align to the starting edge of the
-     * overview view.
-     */
-    public static final int ALIGN_MODE_START = 0;
-    /**
-     * This is the alignment mode that the ending edge of logo and the starting edge of description
-     * align to the middle of the overview view. Note that this might not be the exact horizontal
-     * center of the overview view.
-     */
-    public static final int ALIGN_MODE_MIDDLE = 1;
-
-    /**
-     * Listeners for events on ViewHolder.
-     */
-    public static abstract class Listener {
-
-        /**
-         * {@link FullWidthDetailsOverviewRowPresenter#notifyOnBindLogo(ViewHolder)} is called.
-         * @param vh  The ViewHolder that has bound logo view.
-         */
-        public void onBindLogo(ViewHolder vh) {
-        }
-
-    }
-
-    class ActionsItemBridgeAdapter extends ItemBridgeAdapter {
-        FullWidthDetailsOverviewRowPresenter.ViewHolder mViewHolder;
-
-        ActionsItemBridgeAdapter(FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder) {
-            mViewHolder = viewHolder;
-        }
-
-        @Override
-        public void onBind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null
-                    || mActionClickedListener != null) {
-                ibvh.getPresenter().setOnClickListener(
-                        ibvh.getViewHolder(), new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                if (mViewHolder.getOnItemViewClickedListener() != null) {
-                                    mViewHolder.getOnItemViewClickedListener().onItemClicked(
-                                            ibvh.getViewHolder(), ibvh.getItem(),
-                                            mViewHolder, mViewHolder.getRow());
-                                }
-                                if (mActionClickedListener != null) {
-                                    mActionClickedListener.onActionClicked((Action) ibvh.getItem());
-                                }
-                            }
-                        });
-            }
-        }
-        @Override
-        public void onUnbind(final ItemBridgeAdapter.ViewHolder ibvh) {
-            if (mViewHolder.getOnItemViewClickedListener() != null
-                    || mActionClickedListener != null) {
-                ibvh.getPresenter().setOnClickListener(ibvh.getViewHolder(), null);
-            }
-        }
-        @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            // Remove first to ensure we don't add ourselves more than once.
-            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
-            viewHolder.itemView.addOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
-        }
-        @Override
-        public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            viewHolder.itemView.removeOnLayoutChangeListener(mViewHolder.mLayoutChangeListener);
-            mViewHolder.checkFirstAndLastPosition(false);
-        }
-    }
-
-    /**
-     * A ViewHolder for the DetailsOverviewRow.
-     */
-    public class ViewHolder extends RowPresenter.ViewHolder {
-
-        protected final DetailsOverviewRow.Listener mRowListener = createRowListener();
-
-        protected DetailsOverviewRow.Listener createRowListener() {
-            return new DetailsOverviewRowListener();
-        }
-
-        public class DetailsOverviewRowListener extends DetailsOverviewRow.Listener {
-            @Override
-            public void onImageDrawableChanged(DetailsOverviewRow row) {
-                sHandler.removeCallbacks(mUpdateDrawableCallback);
-                sHandler.post(mUpdateDrawableCallback);
-            }
-
-            @Override
-            public void onItemChanged(DetailsOverviewRow row) {
-                if (mDetailsDescriptionViewHolder != null) {
-                    mDetailsPresenter.onUnbindViewHolder(mDetailsDescriptionViewHolder);
-                }
-                mDetailsPresenter.onBindViewHolder(mDetailsDescriptionViewHolder, row.getItem());
-            }
-
-            @Override
-            public void onActionsAdapterChanged(DetailsOverviewRow row) {
-                bindActions(row.getActionsAdapter());
-            }
-        };
-
-        final ViewGroup mOverviewRoot;
-        final FrameLayout mOverviewFrame;
-        final ViewGroup mDetailsDescriptionFrame;
-        final HorizontalGridView mActionsRow;
-        final Presenter.ViewHolder mDetailsDescriptionViewHolder;
-        final DetailsOverviewLogoPresenter.ViewHolder mDetailsLogoViewHolder;
-        int mNumItems;
-        ItemBridgeAdapter mActionBridgeAdapter;
-        int mState = STATE_HALF;
-
-        final Runnable mUpdateDrawableCallback = new Runnable() {
-            @Override
-            public void run() {
-                Row row = getRow();
-                if (row == null) {
-                    return;
-                }
-                mDetailsOverviewLogoPresenter.onBindViewHolder(mDetailsLogoViewHolder, row);
-            }
-        };
-
-        void bindActions(ObjectAdapter adapter) {
-            mActionBridgeAdapter.setAdapter(adapter);
-            mActionsRow.setAdapter(mActionBridgeAdapter);
-            mNumItems = mActionBridgeAdapter.getItemCount();
-
-        }
-
-        void onBind() {
-            DetailsOverviewRow row = (DetailsOverviewRow) getRow();
-            bindActions(row.getActionsAdapter());
-            row.addListener(mRowListener);
-        }
-
-        void onUnbind() {
-            DetailsOverviewRow row = (DetailsOverviewRow) getRow();
-            row.removeListener(mRowListener);
-            sHandler.removeCallbacks(mUpdateDrawableCallback);
-        }
-
-        final View.OnLayoutChangeListener mLayoutChangeListener =
-                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) {
-                if (DEBUG) Log.v(TAG, "onLayoutChange " + v);
-                checkFirstAndLastPosition(false);
-            }
-        };
-
-        final OnChildSelectedListener mChildSelectedListener = new OnChildSelectedListener() {
-            @Override
-            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
-                dispatchItemSelection(view);
-            }
-        };
-
-        void dispatchItemSelection(View view) {
-            if (!isSelected()) {
-                return;
-            }
-            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) (view != null
-                    ? mActionsRow.getChildViewHolder(view)
-                    : mActionsRow.findViewHolderForPosition(mActionsRow.getSelectedPosition()));
-            if (ibvh == null) {
-                if (getOnItemViewSelectedListener() != null) {
-                    getOnItemViewSelectedListener().onItemSelected(null, null,
-                            ViewHolder.this, getRow());
-                }
-            } else {
-                if (getOnItemViewSelectedListener() != null) {
-                    getOnItemViewSelectedListener().onItemSelected(ibvh.getViewHolder(), ibvh.getItem(),
-                            ViewHolder.this, getRow());
-                }
-            }
-        };
-
-        final RecyclerView.OnScrollListener mScrollListener =
-                new RecyclerView.OnScrollListener() {
-
-            @Override
-            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-            }
-            @Override
-            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-                checkFirstAndLastPosition(true);
-            }
-        };
-
-        private int getViewCenter(View view) {
-            return (view.getRight() - view.getLeft()) / 2;
-        }
-
-        void checkFirstAndLastPosition(boolean fromScroll) {
-            RecyclerView.ViewHolder viewHolder;
-
-            viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
-            boolean showRight = (viewHolder == null
-                    || viewHolder.itemView.getRight() > mActionsRow.getWidth());
-
-            viewHolder = mActionsRow.findViewHolderForPosition(0);
-            boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
-
-            if (DEBUG) {
-                Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll
-                        + " showRight " + showRight + " showLeft " + showLeft);
-            }
-
-        }
-
-        /**
-         * Constructor for the ViewHolder.
-         *
-         * @param rootView The root View that this view holder will be attached
-         *        to.
-         */
-        public ViewHolder(View rootView, Presenter detailsPresenter,
-                DetailsOverviewLogoPresenter logoPresenter) {
-            super(rootView);
-            mOverviewRoot = (ViewGroup) rootView.findViewById(R.id.details_root);
-            mOverviewFrame = (FrameLayout) rootView.findViewById(R.id.details_frame);
-            mDetailsDescriptionFrame =
-                    (ViewGroup) rootView.findViewById(R.id.details_overview_description);
-            mActionsRow =
-                    (HorizontalGridView) mOverviewFrame.findViewById(R.id.details_overview_actions);
-            mActionsRow.setHasOverlappingRendering(false);
-            mActionsRow.setOnScrollListener(mScrollListener);
-            mActionsRow.setAdapter(mActionBridgeAdapter);
-            mActionsRow.setOnChildSelectedListener(mChildSelectedListener);
-
-            final int fadeLength = rootView.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_overview_actions_fade_size);
-            mActionsRow.setFadingRightEdgeLength(fadeLength);
-            mActionsRow.setFadingLeftEdgeLength(fadeLength);
-            mDetailsDescriptionViewHolder =
-                    detailsPresenter.onCreateViewHolder(mDetailsDescriptionFrame);
-            mDetailsDescriptionFrame.addView(mDetailsDescriptionViewHolder.view);
-            mDetailsLogoViewHolder = (DetailsOverviewLogoPresenter.ViewHolder)
-                    logoPresenter.onCreateViewHolder(mOverviewRoot);
-            mOverviewRoot.addView(mDetailsLogoViewHolder.view);
-        }
-
-        /**
-         * Returns the rectangle area with a color background.
-         */
-        public final ViewGroup getOverviewView() {
-            return mOverviewFrame;
-        }
-
-        /**
-         * Returns the ViewHolder for logo.
-         */
-        public final DetailsOverviewLogoPresenter.ViewHolder getLogoViewHolder() {
-            return mDetailsLogoViewHolder;
-        }
-
-        /**
-         * Returns the ViewHolder for DetailsDescription.
-         */
-        public final Presenter.ViewHolder getDetailsDescriptionViewHolder() {
-            return mDetailsDescriptionViewHolder;
-        }
-
-        /**
-         * Returns the root view for inserting details description.
-         */
-        public final ViewGroup getDetailsDescriptionFrame() {
-            return mDetailsDescriptionFrame;
-        }
-
-        /**
-         * Returns the view of actions row.
-         */
-        public final ViewGroup getActionsRow() {
-            return mActionsRow;
-        }
-
-        /**
-         * Returns current state of the ViewHolder set by
-         * {@link FullWidthDetailsOverviewRowPresenter#setState(ViewHolder, int)}.
-         */
-        public final int getState() {
-            return mState;
-        }
-    }
-
-    protected int mInitialState = STATE_HALF;
-
-    final Presenter mDetailsPresenter;
-    final DetailsOverviewLogoPresenter mDetailsOverviewLogoPresenter;
-    OnActionClickedListener mActionClickedListener;
-
-    private int mBackgroundColor = Color.TRANSPARENT;
-    private int mActionsBackgroundColor = Color.TRANSPARENT;
-    private boolean mBackgroundColorSet;
-    private boolean mActionsBackgroundColorSet;
-
-    private Listener mListener;
-    private boolean mParticipatingEntranceTransition;
-
-    private int mAlignmentMode;
-
-    /**
-     * Constructor for a FullWidthDetailsOverviewRowPresenter.
-     *
-     * @param detailsPresenter The {@link Presenter} used to render the detailed
-     *        description of the row.
-     */
-    public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter) {
-        this(detailsPresenter, new DetailsOverviewLogoPresenter());
-    }
-
-    /**
-     * Constructor for a FullWidthDetailsOverviewRowPresenter.
-     *
-     * @param detailsPresenter The {@link Presenter} used to render the detailed
-     *        description of the row.
-     * @param logoPresenter  The {@link Presenter} used to render the logo view.
-     */
-    public FullWidthDetailsOverviewRowPresenter(Presenter detailsPresenter,
-            DetailsOverviewLogoPresenter logoPresenter) {
-        setHeaderPresenter(null);
-        setSelectEffectEnabled(false);
-        mDetailsPresenter = detailsPresenter;
-        mDetailsOverviewLogoPresenter = logoPresenter;
-    }
-
-    /**
-     * Sets the listener for Action click events.
-     */
-    public void setOnActionClickedListener(OnActionClickedListener listener) {
-        mActionClickedListener = listener;
-    }
-
-    /**
-     * Returns the listener for Action click events.
-     */
-    public OnActionClickedListener getOnActionClickedListener() {
-        return mActionClickedListener;
-    }
-
-    /**
-     * Sets the background color.  If not set, a default from the theme will be used.
-     */
-    public final void setBackgroundColor(int color) {
-        mBackgroundColor = color;
-        mBackgroundColorSet = true;
-    }
-
-    /**
-     * Returns the background color.  If {@link #setBackgroundColor(int)}, transparent
-     * is returned.
-     */
-    public final int getBackgroundColor() {
-        return mBackgroundColor;
-    }
-
-    /**
-     * Sets the background color for Action Bar.  If not set, a default from the theme will be
-     * used.
-     */
-    public final void setActionsBackgroundColor(int color) {
-        mActionsBackgroundColor = color;
-        mActionsBackgroundColorSet = true;
-    }
-
-    /**
-     * Returns the background color of actions.  If {@link #setActionsBackgroundColor(int)}
-     * is not called,  transparent is returned.
-     */
-    public final int getActionsBackgroundColor() {
-        return mActionsBackgroundColor;
-    }
-
-    /**
-     * Returns true if the overview should be part of shared element transition.
-     */
-    public final boolean isParticipatingEntranceTransition() {
-        return mParticipatingEntranceTransition;
-    }
-
-    /**
-     * Sets if the overview should be part of shared element transition.
-     */
-    public final void setParticipatingEntranceTransition(boolean participating) {
-        mParticipatingEntranceTransition = participating;
-    }
-
-    /**
-     * Change the initial state used to create ViewHolder.
-     */
-    public final void setInitialState(int state) {
-        mInitialState = state;
-    }
-
-    /**
-     * Returns the initial state used to create ViewHolder.
-     */
-    public final int getInitialState() {
-        return mInitialState;
-    }
-
-    /**
-     * Set alignment mode of Description.
-     *
-     * @param alignmentMode  One of {@link #ALIGN_MODE_MIDDLE} or {@link #ALIGN_MODE_START}
-     */
-    public final void setAlignmentMode(int alignmentMode) {
-        mAlignmentMode = alignmentMode;
-    }
-
-    /**
-     * Returns alignment mode of Description.
-     *
-     * @return  One of {@link #ALIGN_MODE_MIDDLE} or {@link #ALIGN_MODE_START}.
-     */
-    public final int getAlignmentMode() {
-        return mAlignmentMode;
-    }
-
-    @Override
-    protected boolean isClippingChildren() {
-        return true;
-    }
-
-    /**
-     * Set listener for details overview presenter. Must be called before creating
-     * ViewHolder.
-     */
-    public final void setListener(Listener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Get resource id to inflate the layout.  The layout must match {@link #STATE_HALF}
-     */
-    protected int getLayoutResourceId() {
-        return R.layout.lb_fullwidth_details_overview;
-    }
-
-    @Override
-    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
-        View v = LayoutInflater.from(parent.getContext())
-            .inflate(getLayoutResourceId(), parent, false);
-        final ViewHolder vh = new ViewHolder(v, mDetailsPresenter, mDetailsOverviewLogoPresenter);
-        mDetailsOverviewLogoPresenter.setContext(vh.mDetailsLogoViewHolder, vh, this);
-        setState(vh, mInitialState);
-
-        vh.mActionBridgeAdapter = new ActionsItemBridgeAdapter(vh);
-        final View overview = vh.mOverviewFrame;
-        if (mBackgroundColorSet) {
-            overview.setBackgroundColor(mBackgroundColor);
-        }
-        if (mActionsBackgroundColorSet) {
-            overview.findViewById(R.id.details_overview_actions_background)
-                    .setBackgroundColor(mActionsBackgroundColor);
-        }
-        RoundedRectHelper.getInstance().setClipToRoundedOutline(overview, true);
-
-        if (!getSelectEffectEnabled()) {
-            vh.mOverviewFrame.setForeground(null);
-        }
-
-        vh.mActionsRow.setOnUnhandledKeyListener(new BaseGridView.OnUnhandledKeyListener() {
-            @Override
-            public boolean onUnhandledKey(KeyEvent event) {
-                if (vh.getOnKeyListener() != null) {
-                    if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-        });
-        return vh;
-    }
-
-    private static int getNonNegativeWidth(Drawable drawable) {
-        final int width = (drawable == null) ? 0 : drawable.getIntrinsicWidth();
-        return (width > 0 ? width : 0);
-    }
-
-    private static int getNonNegativeHeight(Drawable drawable) {
-        final int height = (drawable == null) ? 0 : drawable.getIntrinsicHeight();
-        return (height > 0 ? height : 0);
-    }
-
-    @Override
-    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
-        super.onBindRowViewHolder(holder, item);
-
-        DetailsOverviewRow row = (DetailsOverviewRow) item;
-        ViewHolder vh = (ViewHolder) holder;
-
-        mDetailsOverviewLogoPresenter.onBindViewHolder(vh.mDetailsLogoViewHolder, row);
-        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row.getItem());
-        vh.onBind();
-    }
-
-    @Override
-    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
-        ViewHolder vh = (ViewHolder) holder;
-        vh.onUnbind();
-        mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
-        mDetailsOverviewLogoPresenter.onUnbindViewHolder(vh.mDetailsLogoViewHolder);
-        super.onUnbindRowViewHolder(holder);
-    }
-
-    @Override
-    public final boolean isUsingDefaultSelectEffect() {
-        return false;
-    }
-
-    @Override
-    protected void onSelectLevelChanged(RowPresenter.ViewHolder holder) {
-        super.onSelectLevelChanged(holder);
-        if (getSelectEffectEnabled()) {
-            ViewHolder vh = (ViewHolder) holder;
-            int dimmedColor = vh.mColorDimmer.getPaint().getColor();
-            ((ColorDrawable) vh.mOverviewFrame.getForeground().mutate()).setColor(dimmedColor);
-        }
-    }
-
-    @Override
-    protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
-        super.onRowViewAttachedToWindow(vh);
-        ViewHolder viewHolder = (ViewHolder) vh;
-        mDetailsPresenter.onViewAttachedToWindow(viewHolder.mDetailsDescriptionViewHolder);
-        mDetailsOverviewLogoPresenter.onViewAttachedToWindow(viewHolder.mDetailsLogoViewHolder);
-    }
-
-    @Override
-    protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
-        super.onRowViewDetachedFromWindow(vh);
-        ViewHolder viewHolder = (ViewHolder) vh;
-        mDetailsPresenter.onViewDetachedFromWindow(viewHolder.mDetailsDescriptionViewHolder);
-        mDetailsOverviewLogoPresenter.onViewDetachedFromWindow(viewHolder.mDetailsLogoViewHolder);
-    }
-
-    /**
-     * Called by {@link DetailsOverviewLogoPresenter} to notify logo was bound to view.
-     * Application should not directly call this method.
-     * @param viewHolder  The row ViewHolder that has logo bound to view.
-     */
-    public final void notifyOnBindLogo(ViewHolder viewHolder) {
-        onLayoutOverviewFrame(viewHolder, viewHolder.getState(), true);
-        onLayoutLogo(viewHolder, viewHolder.getState(), true);
-        if (mListener != null) {
-            mListener.onBindLogo(viewHolder);
-        }
-    }
-
-    /**
-     * Layout logo position based on current state.  Subclass may override.
-     * The method is called when a logo is bound to view or state changes.
-     * @param viewHolder  The row ViewHolder that contains the logo.
-     * @param oldState    The old state,  can be same as current viewHolder.getState()
-     * @param logoChanged Whether logo was changed.
-     */
-    protected void onLayoutLogo(ViewHolder viewHolder, int oldState, boolean logoChanged) {
-        View v = viewHolder.getLogoViewHolder().view;
-        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
-                v.getLayoutParams();
-        switch (mAlignmentMode) {
-            case ALIGN_MODE_START:
-            default:
-                lp.setMarginStart(v.getResources().getDimensionPixelSize(
-                        R.dimen.lb_details_v2_logo_margin_start));
-                break;
-            case ALIGN_MODE_MIDDLE:
-                lp.setMarginStart(v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_left)
-                        - lp.width);
-                break;
-        }
-
-        switch (viewHolder.getState()) {
-        case STATE_FULL:
-        default:
-            lp.topMargin =
-                    v.getResources().getDimensionPixelSize(R.dimen.lb_details_v2_blank_height)
-                    - lp.height / 2;
-            break;
-        case STATE_HALF:
-            lp.topMargin = v.getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_v2_blank_height) + v.getResources()
-                    .getDimensionPixelSize(R.dimen.lb_details_v2_actions_height) + v
-                    .getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_v2_description_margin_top);
-            break;
-        case STATE_SMALL:
-            lp.topMargin = 0;
-            break;
-        }
-        v.setLayoutParams(lp);
-    }
-
-    /**
-     * Layout overview frame based on current state.  Subclass may override.
-     * The method is called when a logo is bound to view or state changes.
-     * @param viewHolder  The row ViewHolder that contains the logo.
-     * @param oldState    The old state,  can be same as current viewHolder.getState()
-     * @param logoChanged Whether logo was changed.
-     */
-    protected void onLayoutOverviewFrame(ViewHolder viewHolder, int oldState, boolean logoChanged) {
-        boolean wasBanner = oldState == STATE_SMALL;
-        boolean isBanner = viewHolder.getState() == STATE_SMALL;
-        if (wasBanner != isBanner || logoChanged) {
-            Resources res = viewHolder.view.getResources();
-
-            int frameMarginStart;
-            int descriptionMarginStart = 0;
-            int logoWidth = 0;
-            if (mDetailsOverviewLogoPresenter.isBoundToImage(viewHolder.getLogoViewHolder(),
-                    (DetailsOverviewRow) viewHolder.getRow())) {
-                logoWidth = viewHolder.getLogoViewHolder().view.getLayoutParams().width;
-            }
-            switch (mAlignmentMode) {
-                case ALIGN_MODE_START:
-                default:
-                    if (isBanner) {
-                        frameMarginStart = res.getDimensionPixelSize(
-                                R.dimen.lb_details_v2_logo_margin_start);
-                        descriptionMarginStart = logoWidth;
-                    } else {
-                        frameMarginStart = 0;
-                        descriptionMarginStart = logoWidth + res.getDimensionPixelSize(
-                                R.dimen.lb_details_v2_logo_margin_start);
-                    }
-                    break;
-                case ALIGN_MODE_MIDDLE:
-                    if (isBanner) {
-                        frameMarginStart = res.getDimensionPixelSize(R.dimen.lb_details_v2_left)
-                                - logoWidth;
-                        descriptionMarginStart = logoWidth;
-                    } else {
-                        frameMarginStart = 0;
-                        descriptionMarginStart = res.getDimensionPixelSize(
-                                R.dimen.lb_details_v2_left);
-                    }
-                    break;
-            }
-            MarginLayoutParams lpFrame =
-                    (MarginLayoutParams) viewHolder.getOverviewView().getLayoutParams();
-            lpFrame.topMargin = isBanner ? 0
-                    : res.getDimensionPixelSize(R.dimen.lb_details_v2_blank_height);
-            lpFrame.leftMargin = lpFrame.rightMargin = frameMarginStart;
-            viewHolder.getOverviewView().setLayoutParams(lpFrame);
-
-            View description = viewHolder.getDetailsDescriptionFrame();
-            MarginLayoutParams lpDesc = (MarginLayoutParams) description.getLayoutParams();
-            lpDesc.setMarginStart(descriptionMarginStart);
-            description.setLayoutParams(lpDesc);
-
-            View action = viewHolder.getActionsRow();
-            MarginLayoutParams lpActions = (MarginLayoutParams) action.getLayoutParams();
-            lpActions.setMarginStart(descriptionMarginStart);
-            lpActions.height =
-                    isBanner ? 0 : res.getDimensionPixelSize(R.dimen.lb_details_v2_actions_height);
-            action.setLayoutParams(lpActions);
-        }
-    }
-
-    /**
-     * Switch state of a ViewHolder.
-     * @param viewHolder   The ViewHolder to change state.
-     * @param state        New state, can be {@link #STATE_FULL}, {@link #STATE_HALF}
-     *                     or {@link #STATE_SMALL}.
-     */
-    public final void setState(ViewHolder viewHolder, int state) {
-        if (viewHolder.getState() != state) {
-            int oldState = viewHolder.getState();
-            viewHolder.mState = state;
-            onStateChanged(viewHolder, oldState);
-        }
-    }
-
-    /**
-     * Called when {@link ViewHolder#getState()} changes.  Subclass may override.
-     * The default implementation calls {@link #onLayoutLogo(ViewHolder, int, boolean)} and
-     * {@link #onLayoutOverviewFrame(ViewHolder, int, boolean)}.
-     * @param viewHolder   The ViewHolder which state changed.
-     * @param oldState     The old state.
-     */
-    protected void onStateChanged(ViewHolder viewHolder, int oldState) {
-        onLayoutOverviewFrame(viewHolder, oldState, false);
-        onLayoutLogo(viewHolder, oldState, false);
-    }
-
-    @Override
-    public void setEntranceTransitionState(RowPresenter.ViewHolder holder,
-            boolean afterEntrance) {
-        super.setEntranceTransitionState(holder, afterEntrance);
-        if (mParticipatingEntranceTransition) {
-            holder.view.setVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java b/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
deleted file mode 100644
index c2d57b6..0000000
--- a/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
+++ /dev/null
@@ -1,189 +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.v17.leanback.widget;
-
-import android.app.Activity;
-import android.os.Handler;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.view.ViewCompat;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Helper class to assist delayed shared element activity transition for view created by
- * {@link FullWidthDetailsOverviewRowPresenter}. User must call
- * {@link #setSharedElementEnterTransition(Activity, String, long)} during activity onCreate() and
- * call {@link FullWidthDetailsOverviewRowPresenter#setListener(FullWidthDetailsOverviewRowPresenter.Listener)}.
- * The helper implements {@link FullWidthDetailsOverviewRowPresenter.Listener} and starts delayed
- * activity transition once {@link FullWidthDetailsOverviewRowPresenter.Listener#onBindLogo(ViewHolder)}
- * is called.
- */
-public class FullWidthDetailsOverviewSharedElementHelper extends
-        FullWidthDetailsOverviewRowPresenter.Listener {
-
-    static final String TAG = "DetailsTransitionHelper";
-    static final boolean DEBUG = false;
-
-    private static final long DEFAULT_TIMEOUT = 5000;
-
-    static class TransitionTimeOutRunnable implements Runnable {
-        WeakReference<FullWidthDetailsOverviewSharedElementHelper> mHelperRef;
-
-        TransitionTimeOutRunnable(FullWidthDetailsOverviewSharedElementHelper helper) {
-            mHelperRef = new WeakReference<FullWidthDetailsOverviewSharedElementHelper>(helper);
-        }
-
-        @Override
-        public void run() {
-            FullWidthDetailsOverviewSharedElementHelper helper = mHelperRef.get();
-            if (helper == null) {
-                return;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "timeout " + helper.mActivityToRunTransition);
-            }
-            helper.startPostponedEnterTransition();
-        }
-    }
-
-    ViewHolder mViewHolder;
-    Activity mActivityToRunTransition;
-    private boolean mStartedPostpone;
-    String mSharedElementName;
-    private boolean mAutoStartSharedElementTransition = true;
-
-    public void setSharedElementEnterTransition(Activity activity, String sharedElementName) {
-        setSharedElementEnterTransition(activity, sharedElementName, DEFAULT_TIMEOUT);
-    }
-
-    public void setSharedElementEnterTransition(Activity activity, String sharedElementName,
-            long timeoutMs) {
-        if ((activity == null && !TextUtils.isEmpty(sharedElementName))
-                || (activity != null && TextUtils.isEmpty(sharedElementName))) {
-            throw new IllegalArgumentException();
-        }
-        if (activity == mActivityToRunTransition
-                && TextUtils.equals(sharedElementName, mSharedElementName)) {
-            return;
-        }
-        mActivityToRunTransition = activity;
-        mSharedElementName = sharedElementName;
-        if (DEBUG) {
-            Log.d(TAG, "postponeEnterTransition " + mActivityToRunTransition);
-        }
-        Object transition = TransitionHelper.getSharedElementEnterTransition(activity.getWindow());
-        setAutoStartSharedElementTransition(transition != null);
-        ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
-        if (timeoutMs > 0) {
-            new Handler().postDelayed(new TransitionTimeOutRunnable(this), timeoutMs);
-        }
-    }
-
-    /**
-     * Enable or disable auto startPostponedEnterTransition() when bound to logo. When it's
-     * disabled, app must call {@link #startPostponedEnterTransition()} to kick off
-     * windowEnterTransition. By default, it is disabled when there is no
-     * windowEnterSharedElementTransition set on the activity.
-     */
-    public void setAutoStartSharedElementTransition(boolean enabled) {
-        mAutoStartSharedElementTransition = enabled;
-    }
-
-    /**
-     * Returns true if auto startPostponedEnterTransition() when bound to logo. When it's
-     * disabled, app must call {@link #startPostponedEnterTransition()} to kick off
-     * windowEnterTransition. By default, it is disabled when there is no
-     * windowEnterSharedElementTransition set on the activity.
-     */
-    public boolean getAutoStartSharedElementTransition() {
-        return mAutoStartSharedElementTransition;
-    }
-
-    @Override
-    public void onBindLogo(ViewHolder vh) {
-        if (DEBUG) {
-            Log.d(TAG, "onBindLogo, could start transition of " + mActivityToRunTransition);
-        }
-        mViewHolder = vh;
-        if (!mAutoStartSharedElementTransition) {
-            return;
-        }
-        if (mViewHolder != null) {
-            if (DEBUG) {
-                Log.d(TAG, "rebind? clear transitionName on current viewHolder "
-                        + mViewHolder.getOverviewView());
-            }
-            ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view, null);
-        }
-        // After we got a image drawable,  we can determine size of right panel.
-        // We want right panel to have fixed size so that the right panel don't change size
-        // when the overview is layout as a small bounds in transition.
-        mViewHolder.getDetailsDescriptionFrame().postOnAnimation(new Runnable() {
-            @Override
-            public void run() {
-                if (DEBUG) {
-                    Log.d(TAG, "setTransitionName "+mViewHolder.getOverviewView());
-                }
-                ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view,
-                        mSharedElementName);
-                Object transition = TransitionHelper.getSharedElementEnterTransition(
-                        mActivityToRunTransition.getWindow());
-                if (transition != null) {
-                    TransitionHelper.addTransitionListener(transition, new TransitionListener() {
-                        @Override
-                        public void onTransitionEnd(Object transition) {
-                            if (DEBUG) {
-                                Log.d(TAG, "onTransitionEnd " + mActivityToRunTransition);
-                            }
-                            // after transition if the action row still focused, transfer
-                            // focus to its children
-                            if (mViewHolder.getActionsRow().isFocused()) {
-                                mViewHolder.getActionsRow().requestFocus();
-                            }
-                            TransitionHelper.removeTransitionListener(transition, this);
-                        }
-                    });
-                }
-                startPostponedEnterTransitionInternal();
-            }
-        });
-    }
-
-    /**
-     * Manually start postponed enter transition.
-     */
-    public void startPostponedEnterTransition() {
-        new Handler().post(new Runnable(){
-            @Override
-            public void run() {
-                startPostponedEnterTransitionInternal();
-            }
-        });
-    }
-
-    void startPostponedEnterTransitionInternal() {
-        if (!mStartedPostpone && mViewHolder != null) {
-            if (DEBUG) {
-                Log.d(TAG, "startPostponedEnterTransition " + mActivityToRunTransition);
-            }
-            ActivityCompat.startPostponedEnterTransition(mActivityToRunTransition);
-            mStartedPostpone = true;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/Grid.java b/android/support/v17/leanback/widget/Grid.java
deleted file mode 100644
index 332a7bc..0000000
--- a/android/support/v17/leanback/widget/Grid.java
+++ /dev/null
@@ -1,529 +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.v17.leanback.widget;
-
-import android.support.annotation.NonNull;
-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.
- * Grid is the base class for single row, non-staggered grid and staggered grid.
- * <p>
- * To use the Grid, user must implement a Provider to create or remove visible item.
- * Grid maintains a list of visible items.  Visible items are created when
- * user calls appendVisibleItems() or prependVisibleItems() with certain limitation
- * (e.g. a max edge that append up to).  Visible items are deleted when user calls
- * removeInvisibleItemsAtEnd() or removeInvisibleItemsAtFront().  Grid's algorithm
- * uses size of visible item returned from Provider.createItem() to decide which row
- * to add a new visible item and may cache the algorithm results.   User must call
- * invalidateItemsAfter() when it detects item size changed to ask Grid to remove cached
- * results.
- */
-abstract class Grid {
-
-    /**
-     * A constant representing a default starting index, indicating that the
-     * developer did not provide a start index.
-     */
-    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.
-     */
-    public static interface Provider {
-
-        /**
-         * Return how many items (are in the adapter).
-         */
-        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.
-         * The call is always followed by addItem().
-         * @param index     0-based index of the item in provider
-         * @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.
-         */
-        int createItem(int index, boolean append, Object[] item, boolean disappearingItem);
-
-        /**
-         * add item to given row and given edge.  The call is always after createItem().
-         * @param item      The object returned by createItem()
-         * @param index     0-based index of the item in provider
-         * @param length    The size of the object
-         * @param rowIndex  Row index to put the item
-         * @param edge      min_edge if not reversed or max_edge if reversed.
-         */
-        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
-         */
-        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
-         */
-        int getEdge(int index);
-
-        /**
-         * Get size of an existing visible item.
-         * @param index     0-based index of the item in provider
-         */
-        int getSize(int index);
-    }
-
-    /**
-     * Cached representation of an item in Grid.  May be subclassed.
-     */
-    public static class Location {
-        /**
-         * The index of the row for this Location.
-         */
-        public int row;
-
-        public Location(int row) {
-            this.row = row;
-        }
-    }
-
-    protected Provider mProvider;
-    protected boolean mReversedFlow;
-    protected int mSpacing;
-    protected int mNumRows;
-    protected int mFirstVisibleIndex = -1;
-    protected int mLastVisibleIndex = -1;
-
-    protected CircularIntArray[] mTmpItemPositionsInRows;
-
-    // the first index that grid will layout
-    protected int mStartIndex = START_DEFAULT;
-
-    /**
-     * Creates a single or multiple rows (can be staggered or not staggered) grid
-     */
-    public static Grid createGrid(int rows) {
-        Grid grid;
-        if (rows == 1) {
-            grid = new SingleRow();
-        } else {
-            // TODO support non staggered multiple rows grid
-            grid = new StaggeredGridDefault();
-            grid.setNumRows(rows);
-        }
-        return grid;
-    }
-
-    /**
-     * Sets the space between items in a row
-     */
-    public final void setSpacing(int spacing) {
-        mSpacing = spacing;
-    }
-
-    /**
-     * Sets if reversed flow (rtl)
-     */
-    public final void setReversedFlow(boolean reversedFlow) {
-        mReversedFlow = reversedFlow;
-    }
-
-    /**
-     * Returns true if reversed flow (rtl)
-     */
-    public boolean isReversedFlow() {
-        return mReversedFlow;
-    }
-
-    /**
-     * Sets the {@link Provider} for this grid.
-     *
-     * @param provider The provider for this grid.
-     */
-    public void setProvider(Provider provider) {
-        mProvider = provider;
-    }
-
-    /**
-     * Sets the first item index to create when there are no items.
-     *
-     * @param startIndex the index of the first item
-     */
-    public void setStart(int startIndex) {
-        mStartIndex = startIndex;
-    }
-
-    /**
-     * Returns the number of rows in the grid.
-     */
-    public int getNumRows() {
-        return mNumRows;
-    }
-
-    /**
-     * Sets number of rows to fill into. For views that represent a
-     * horizontal list, this will be the rows of the view. For views that
-     * represent a vertical list, this will be the columns.
-     *
-     * @param numRows numberOfRows
-     */
-    void setNumRows(int numRows) {
-        if (numRows <= 0) {
-            throw new IllegalArgumentException();
-        }
-        if (mNumRows == numRows) {
-            return;
-        }
-        mNumRows = numRows;
-        mTmpItemPositionsInRows = new CircularIntArray[mNumRows];
-        for (int i = 0; i < mNumRows; i++) {
-            mTmpItemPositionsInRows[i] = new CircularIntArray();
-        }
-    }
-
-    /**
-     * Returns index of first visible item in the staggered grid.  Returns negative value
-     * if no visible item.
-     */
-    public final int getFirstVisibleIndex() {
-        return mFirstVisibleIndex;
-    }
-
-    /**
-     * Returns index of last visible item in the staggered grid.  Returns negative value
-     * if no visible item.
-     */
-    public final int getLastVisibleIndex() {
-        return mLastVisibleIndex;
-    }
-
-    /**
-     * Reset visible indices and keep cache (if exists)
-     */
-    public void resetVisibleIndex() {
-        mFirstVisibleIndex = mLastVisibleIndex = -1;
-    }
-
-    /**
-     * Invalidate items after or equal to index. This will remove visible items
-     * 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) {
-            return;
-        }
-        if (mLastVisibleIndex < 0) {
-            return;
-        }
-        if (mLastVisibleIndex >= index) {
-            mLastVisibleIndex = index - 1;
-        }
-        resetVisibleIndexIfEmpty();
-        if (getFirstVisibleIndex() < 0) {
-            setStart(index);
-        }
-    }
-
-    /**
-     * Gets the row index of item at given index.
-     */
-    public final int getRowIndex(int index) {
-        Location location = getLocation(index);
-        if (location == null) {
-            return -1;
-        }
-        return location.row;
-    }
-
-    /**
-     * Gets {@link Location} of item.  The return object is read only and temporarily.
-     */
-    public abstract Location getLocation(int index);
-
-    /**
-     * Finds the largest or smallest row min edge of visible items,
-     * the row index is returned in indices[0], the item index is returned in indices[1].
-     */
-    public final int findRowMin(boolean findLarge, @Nullable int[] indices) {
-        return findRowMin(findLarge, mReversedFlow ? mLastVisibleIndex : mFirstVisibleIndex,
-                indices);
-    }
-
-    /**
-     * Finds the largest or smallest row min edge of visible items, starts searching from
-     * indexLimit, the row index is returned in indices[0], the item index is returned in indices[1].
-     */
-    protected abstract int findRowMin(boolean findLarge, int indexLimit, int[] rowIndex);
-
-    /**
-     * Finds the largest or smallest row max edge of visible items, the row index is returned in
-     * indices[0], the item index is returned in indices[1].
-     */
-    public final int findRowMax(boolean findLarge, @Nullable int[] indices) {
-        return findRowMax(findLarge, mReversedFlow ? mFirstVisibleIndex : mLastVisibleIndex,
-                indices);
-    }
-
-    /**
-     * Find largest or smallest row max edge of visible items, starts searching from indexLimit,
-     * the row index is returned in indices[0], the item index is returned in indices[1].
-     */
-    protected abstract int findRowMax(boolean findLarge, int indexLimit, int[] indices);
-
-    /**
-     * Returns true if appending item has reached "toLimit"
-     */
-    protected final boolean checkAppendOverLimit(int toLimit) {
-        if (mLastVisibleIndex < 0) {
-            return false;
-        }
-        return mReversedFlow ? findRowMin(true, null) <= toLimit + mSpacing :
-                    findRowMax(false, null) >= toLimit - mSpacing;
-    }
-
-    /**
-     * Returns true if prepending item has reached "toLimit"
-     */
-    protected final boolean checkPrependOverLimit(int toLimit) {
-        if (mLastVisibleIndex < 0) {
-            return false;
-        }
-        return mReversedFlow ? findRowMax(false, null) >= toLimit - mSpacing :
-                    findRowMin(true, null) <= toLimit + mSpacing;
-    }
-
-    /**
-     * Return array of int array for all rows, each int array contains visible item positions
-     * in pair on that row between startPos(included) and endPositions(included).
-     * Returned value is read only, do not change it.
-     * <p>
-     * E.g. First row has 3,7,8, second row has 4,5,6.
-     * getItemPositionsInRows(3, 8) returns { {3,3,7,8}, {4,6} }
-     */
-    public abstract CircularIntArray[] getItemPositionsInRows(int startPos, int endPos);
-
-    /**
-     * Return array of int array for all rows, each int array contains visible item positions
-     * in pair on that row.
-     * Returned value is read only, do not change it.
-     * <p>
-     * E.g. First row has 3,7,8, second row has 4,5,6  { {3,3,7,8}, {4,6} }
-     */
-    public final CircularIntArray[] getItemPositionsInRows() {
-        return getItemPositionsInRows(getFirstVisibleIndex(), getLastVisibleIndex());
-    }
-
-    /**
-     * Prepends items and stops after one column is filled.
-     * (i.e. filled items from row 0 to row mNumRows - 1)
-     * @return true if at least one item is filled.
-     */
-    public final boolean prependOneColumnVisibleItems() {
-        return prependVisibleItems(mReversedFlow ? Integer.MIN_VALUE : Integer.MAX_VALUE, true);
-    }
-
-    /**
-     * Prepends items until first item or reaches toLimit (min edge when not reversed or
-     * max edge when reversed)
-     */
-    public final void prependVisibleItems(int toLimit) {
-        prependVisibleItems(toLimit, false);
-    }
-
-    /**
-     * Prepends items until first item or reaches toLimit (min edge when not reversed or
-     * max edge when reversed).
-     * @param oneColumnMode  true when fills one column and stops,  false
-     * when checks if condition matches before filling first column.
-     * @return true if at least one item is filled.
-     */
-    protected abstract boolean prependVisibleItems(int toLimit, boolean oneColumnMode);
-
-    /**
-     * Appends items and stops after one column is filled.
-     * (i.e. filled items from row 0 to row mNumRows - 1)
-     * @return true if at least one item is filled.
-     */
-    public boolean appendOneColumnVisibleItems() {
-        return appendVisibleItems(mReversedFlow ? Integer.MAX_VALUE : Integer.MIN_VALUE, true);
-    }
-
-    /**
-     * Append items until last item or reaches toLimit (max edge when not
-     * reversed or min edge when reversed)
-     */
-    public final void appendVisibleItems(int toLimit) {
-        appendVisibleItems(toLimit, false);
-    }
-
-    /**
-     * Appends items until last or reaches toLimit (high edge when not
-     * reversed or low edge when reversed).
-     * @param oneColumnMode True when fills one column and stops,  false
-     * when checks if condition matches before filling first column.
-     * @return true if filled at least one item
-     */
-    protected abstract boolean appendVisibleItems(int toLimit, boolean oneColumnMode);
-
-    /**
-     * 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) {
-            boolean offEnd = !mReversedFlow ? mProvider.getEdge(mLastVisibleIndex) >= toLimit
-                    : mProvider.getEdge(mLastVisibleIndex) <= toLimit;
-            if (offEnd) {
-                mProvider.removeItem(mLastVisibleIndex);
-                mLastVisibleIndex--;
-            } else {
-                break;
-            }
-        }
-        resetVisibleIndexIfEmpty();
-    }
-
-    /**
-     * 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) {
-            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++;
-            } else {
-                break;
-            }
-        }
-        resetVisibleIndexIfEmpty();
-    }
-
-    private void resetVisibleIndexIfEmpty() {
-        if (mLastVisibleIndex < mFirstVisibleIndex) {
-            resetVisibleIndex();
-        }
-    }
-
-    /**
-     * 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,
-            @NonNull RecyclerView.LayoutManager.LayoutPrefetchRegistry layoutPrefetchRegistry) {
-    }
-
-    public abstract void debugPrint(PrintWriter pw);
-}
diff --git a/android/support/v17/leanback/widget/GridLayoutManager.java b/android/support/v17/leanback/widget/GridLayoutManager.java
deleted file mode 100644
index 810cb3b..0000000
--- a/android/support/v17/leanback/widget/GridLayoutManager.java
+++ /dev/null
@@ -1,3800 +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.v17.leanback.widget;
-
-import static android.support.v7.widget.RecyclerView.HORIZONTAL;
-import static android.support.v7.widget.RecyclerView.NO_ID;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
-import static android.support.v7.widget.RecyclerView.VERTICAL;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.os.TraceCompat;
-import android.support.v4.util.CircularIntArray;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v7.widget.LinearSmoothScroller;
-import android.support.v7.widget.OrientationHelper;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Recycler;
-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;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.view.animation.AccelerateDecelerateInterpolator;
-
-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 {
-
-    /*
-     * LayoutParams for {@link HorizontalGridView} and {@link VerticalGridView}.
-     * The class currently does two internal jobs:
-     * - Saves optical bounds insets.
-     * - Caches focus align view center.
-     */
-    final static class LayoutParams extends RecyclerView.LayoutParams {
-
-        // For placement
-        int mLeftInset;
-        int mTopInset;
-        int mRightInset;
-        int mBottomInset;
-
-        // For alignment
-        private int mAlignX;
-        private int mAlignY;
-        private int[] mAlignMultiple;
-        private ItemAlignmentFacet mAlignmentFacet;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(MarginLayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(RecyclerView.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(LayoutParams source) {
-            super(source);
-        }
-
-        int getAlignX() {
-            return mAlignX;
-        }
-
-        int getAlignY() {
-            return mAlignY;
-        }
-
-        int getOpticalLeft(View view) {
-            return view.getLeft() + mLeftInset;
-        }
-
-        int getOpticalTop(View view) {
-            return view.getTop() + mTopInset;
-        }
-
-        int getOpticalRight(View view) {
-            return view.getRight() - mRightInset;
-        }
-
-        int getOpticalBottom(View view) {
-            return view.getBottom() - mBottomInset;
-        }
-
-        int getOpticalWidth(View view) {
-            return view.getWidth() - mLeftInset - mRightInset;
-        }
-
-        int getOpticalHeight(View view) {
-            return view.getHeight() - mTopInset - mBottomInset;
-        }
-
-        int getOpticalLeftInset() {
-            return mLeftInset;
-        }
-
-        int getOpticalRightInset() {
-            return mRightInset;
-        }
-
-        int getOpticalTopInset() {
-            return mTopInset;
-        }
-
-        int getOpticalBottomInset() {
-            return mBottomInset;
-        }
-
-        void setAlignX(int alignX) {
-            mAlignX = alignX;
-        }
-
-        void setAlignY(int alignY) {
-            mAlignY = alignY;
-        }
-
-        void setItemAlignmentFacet(ItemAlignmentFacet facet) {
-            mAlignmentFacet = facet;
-        }
-
-        ItemAlignmentFacet getItemAlignmentFacet() {
-            return mAlignmentFacet;
-        }
-
-        void calculateItemAlignments(int orientation, View view) {
-            ItemAlignmentFacet.ItemAlignmentDef[] defs = mAlignmentFacet.getAlignmentDefs();
-            if (mAlignMultiple == null || mAlignMultiple.length != defs.length) {
-                mAlignMultiple = new int[defs.length];
-            }
-            for (int i = 0; i < defs.length; i++) {
-                mAlignMultiple[i] = ItemAlignmentFacetHelper
-                        .getAlignmentPosition(view, defs[i], orientation);
-            }
-            if (orientation == HORIZONTAL) {
-                mAlignX = mAlignMultiple[0];
-            } else {
-                mAlignY = mAlignMultiple[0];
-            }
-        }
-
-        int[] getAlignMultiple() {
-            return mAlignMultiple;
-        }
-
-        void setOpticalInsets(int leftInset, int topInset, int rightInset, int bottomInset) {
-            mLeftInset = leftInset;
-            mTopInset = topInset;
-            mRightInset = rightInset;
-            mBottomInset = bottomInset;
-        }
-
-    }
-
-    /**
-     * Base class which scrolls to selected view in onStop().
-     */
-    abstract class GridLinearSmoothScroller extends LinearSmoothScroller {
-        GridLinearSmoothScroller() {
-            super(mBaseGridView.getContext());
-        }
-
-        @Override
-        protected void onStop() {
-            // onTargetFound() may not be called if we hit the "wall" first or get cancelled.
-            View targetView = findViewByPosition(getTargetPosition());
-            if (targetView == null) {
-                if (getTargetPosition() >= 0) {
-                    // if smooth scroller is stopped without target, immediately jumps
-                    // to the target position.
-                    scrollToSelection(getTargetPosition(), 0, false, 0);
-                }
-                super.onStop();
-                return;
-            }
-            if (mFocusPosition != getTargetPosition()) {
-                // This should not happen since we cropped value in startPositionSmoothScroller()
-                mFocusPosition = getTargetPosition();
-            }
-            if (hasFocus()) {
-                mFlag |= PF_IN_SELECTION;
-                targetView.requestFocus();
-                mFlag &= ~PF_IN_SELECTION;
-            }
-            dispatchChildSelected();
-            dispatchChildSelectedAndPositioned();
-            super.onStop();
-        }
-
-        @Override
-        protected int calculateTimeForScrolling(int dx) {
-            int ms = super.calculateTimeForScrolling(dx);
-            if (mWindowAlignment.mainAxis().getSize() > 0) {
-                float minMs = (float) MIN_MS_SMOOTH_SCROLL_MAIN_SCREEN
-                        / mWindowAlignment.mainAxis().getSize() * dx;
-                if (ms < minMs) {
-                    ms = (int) minMs;
-                }
-            }
-            return ms;
-        }
-
-        @Override
-        protected void onTargetFound(View targetView,
-                RecyclerView.State state, Action action) {
-            if (getScrollPosition(targetView, null, sTwoInts)) {
-                int dx, dy;
-                if (mOrientation == HORIZONTAL) {
-                    dx = sTwoInts[0];
-                    dy = sTwoInts[1];
-                } else {
-                    dx = sTwoInts[1];
-                    dy = sTwoInts[0];
-                }
-                final int distance = (int) Math.sqrt(dx * dx + dy * dy);
-                final int time = calculateTimeForDeceleration(distance);
-                action.update(dx, dy, time, mDecelerateInterpolator);
-            }
-        }
-    }
-
-    /**
-     * The SmoothScroller that remembers pending DPAD keys and consume pending keys
-     * during scroll.
-     */
-    final class PendingMoveSmoothScroller extends GridLinearSmoothScroller {
-        // -2 is a target position that LinearSmoothScroller can never find until
-        // consumePendingMovesXXX() sets real targetPosition.
-        final static int TARGET_UNDEFINED = -2;
-        // whether the grid is staggered.
-        private final boolean mStaggeredGrid;
-        // Number of pending movements on primary direction, negative if PREV_ITEM.
-        private int mPendingMoves;
-
-        PendingMoveSmoothScroller(int initialPendingMoves, boolean staggeredGrid) {
-            mPendingMoves = initialPendingMoves;
-            mStaggeredGrid = staggeredGrid;
-            setTargetPosition(TARGET_UNDEFINED);
-        }
-
-        void increasePendingMoves() {
-            if (mPendingMoves < mMaxPendingMoves) {
-                mPendingMoves++;
-            }
-        }
-
-        void decreasePendingMoves() {
-            if (mPendingMoves > -mMaxPendingMoves) {
-                mPendingMoves--;
-            }
-        }
-
-        /**
-         * Called before laid out an item when non-staggered grid can handle pending movements
-         * by skipping "mNumRows" per movement;  staggered grid will have to wait the item
-         * has been laid out in consumePendingMovesAfterLayout().
-         */
-        void consumePendingMovesBeforeLayout() {
-            if (mStaggeredGrid || mPendingMoves == 0) {
-                return;
-            }
-            View newSelected = null;
-            int startPos = mPendingMoves > 0 ? mFocusPosition + mNumRows :
-                    mFocusPosition - mNumRows;
-            for (int pos = startPos; mPendingMoves != 0;
-                    pos = mPendingMoves > 0 ? pos + mNumRows: pos - mNumRows) {
-                View v = findViewByPosition(pos);
-                if (v == null) {
-                    break;
-                }
-                if (!canScrollTo(v)) {
-                    continue;
-                }
-                newSelected = v;
-                mFocusPosition = pos;
-                mSubFocusPosition = 0;
-                if (mPendingMoves > 0) {
-                    mPendingMoves--;
-                } else {
-                    mPendingMoves++;
-                }
-            }
-            if (newSelected != null && hasFocus()) {
-                mFlag |= PF_IN_SELECTION;
-                newSelected.requestFocus();
-                mFlag &= ~PF_IN_SELECTION;
-            }
-        }
-
-        /**
-         * Called after laid out an item.  Staggered grid should find view on same
-         * Row and consume pending movements.
-         */
-        void consumePendingMovesAfterLayout() {
-            if (mStaggeredGrid && mPendingMoves != 0) {
-                // consume pending moves, focus to item on the same row.
-                mPendingMoves = processSelectionMoves(true, mPendingMoves);
-            }
-            if (mPendingMoves == 0 || (mPendingMoves > 0 && hasCreatedLastItem())
-                    || (mPendingMoves < 0 && hasCreatedFirstItem())) {
-                setTargetPosition(mFocusPosition);
-                stop();
-            }
-        }
-
-        @Override
-        protected void updateActionForInterimTarget(Action action) {
-            if (mPendingMoves == 0) {
-                return;
-            }
-            super.updateActionForInterimTarget(action);
-        }
-
-        @Override
-        public PointF computeScrollVectorForPosition(int targetPosition) {
-            if (mPendingMoves == 0) {
-                return null;
-            }
-            int direction = ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
-                    ? mPendingMoves > 0 : mPendingMoves < 0)
-                    ? -1 : 1;
-            if (mOrientation == HORIZONTAL) {
-                return new PointF(direction, 0);
-            } else {
-                return new PointF(0, direction);
-            }
-        }
-
-        @Override
-        protected void onStop() {
-            super.onStop();
-            // if we hit wall,  need clear the remaining pending moves.
-            mPendingMoves = 0;
-            mPendingMoveSmoothScroller = null;
-            View v = findViewByPosition(getTargetPosition());
-            if (v != null) scrollToView(v, true);
-        }
-    };
-
-    private static final String TAG = "GridLayoutManager";
-    static final boolean DEBUG = false;
-    static final boolean TRACE = false;
-
-    // maximum pending movement in one direction.
-    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;
-
-    String getTag() {
-        return TAG + ":" + mBaseGridView.getId();
-    }
-
-    final BaseGridView mBaseGridView;
-
-    /**
-     * Note on conventions in the presence of RTL layout directions:
-     * Many properties and method names reference entities related to the
-     * beginnings and ends of things.  In the presence of RTL flows,
-     * it may not be clear whether this is intended to reference a
-     * quantity that changes direction in RTL cases, or a quantity that
-     * does not.  Here are the conventions in use:
-     *
-     * start/end: coordinate quantities - do reverse
-     * (optical) left/right: coordinate quantities - do not reverse
-     * low/high: coordinate quantities - do not reverse
-     * min/max: coordinate quantities - do not reverse
-     * scroll offset - coordinate quantities - do not reverse
-     * first/last: positional indices - do not reverse
-     * front/end: positional indices - do not reverse
-     * prepend/append: related to positional indices - do not reverse
-     *
-     * Note that although quantities do not reverse in RTL flows, their
-     * relationship does.  In LTR flows, the first positional index is
-     * leftmost; in RTL flows, it is rightmost.  Thus, anywhere that
-     * positional quantities are mapped onto coordinate quantities,
-     * the flow must be checked and the logic reversed.
-     */
-
-    /**
-     * The orientation of a "row".
-     */
-    @RecyclerView.Orientation
-    int mOrientation = HORIZONTAL;
-    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();
-
-    // 2 bits mask is for 3 STAGEs: 0, PF_STAGE_LAYOUT or PF_STAGE_SCROLL.
-    static final int PF_STAGE_MASK = 0x3;
-    static final int PF_STAGE_LAYOUT = 0x1;
-    static final int PF_STAGE_SCROLL = 0x2;
-
-    // Flag for "in fast relayout", determined by layoutInit() result.
-    static final int PF_FAST_RELAYOUT = 1 << 2;
-
-    // Flag for the selected item being updated in fast relayout.
-    static final int PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION = 1 << 3;
-    /**
-     * During full layout pass, when GridView had focus: onLayoutChildren will
-     * skip non-focusable child and adjust mFocusPosition.
-     */
-    static final int PF_IN_LAYOUT_SEARCH_FOCUS = 1 << 4;
-
-    // flag to prevent reentry if it's already processing selection request.
-    static final int PF_IN_SELECTION = 1 << 5;
-
-    // Represents whether child views are temporarily sliding out
-    static final int PF_SLIDING = 1 << 6;
-    static final int PF_LAYOUT_EATEN_IN_SLIDING = 1 << 7;
-
-    /**
-     * Force a full layout under certain situations.  E.g. Rows change, jump to invisible child.
-     */
-    static final int PF_FORCE_FULL_LAYOUT = 1 << 8;
-
-    /**
-     * True if layout is enabled.
-     */
-    static final int PF_LAYOUT_ENABLED = 1 << 9;
-
-    /**
-     * Flag controlling whether the current/next layout should
-     * be updating the secondary size of rows.
-     */
-    static final int PF_ROW_SECONDARY_SIZE_REFRESH = 1 << 10;
-
-    /**
-     *  Allow DPAD key to navigate out at the front of the View (where position = 0),
-     *  default is false.
-     */
-    static final int PF_FOCUS_OUT_FRONT = 1 << 11;
-
-    /**
-     * Allow DPAD key to navigate out at the end of the view, default is false.
-     */
-    static final int PF_FOCUS_OUT_END = 1 << 12;
-
-    static final int PF_FOCUS_OUT_MASKS = PF_FOCUS_OUT_FRONT | PF_FOCUS_OUT_END;
-
-    /**
-     *  Allow DPAD key to navigate out of second axis.
-     *  default is true.
-     */
-    static final int PF_FOCUS_OUT_SIDE_START = 1 << 13;
-
-    /**
-     * Allow DPAD key to navigate out of second axis.
-     */
-    static final int PF_FOCUS_OUT_SIDE_END = 1 << 14;
-
-    static final int PF_FOCUS_OUT_SIDE_MASKS = PF_FOCUS_OUT_SIDE_START | PF_FOCUS_OUT_SIDE_END;
-
-    /**
-     * True if focus search is disabled.
-     */
-    static final int PF_FOCUS_SEARCH_DISABLED = 1 << 15;
-
-    /**
-     * True if prune child,  might be disabled during transition.
-     */
-    static final int PF_PRUNE_CHILD = 1 << 16;
-
-    /**
-     * True if scroll content,  might be disabled during transition.
-     */
-    static final int PF_SCROLL_ENABLED = 1 << 17;
-
-    /**
-     * Set to true for RTL layout in horizontal orientation
-     */
-    static final int PF_REVERSE_FLOW_PRIMARY = 1 << 18;
-
-    /**
-     * Set to true for RTL layout in vertical orientation
-     */
-    static final int PF_REVERSE_FLOW_SECONDARY = 1 << 19;
-
-    static final int PF_REVERSE_FLOW_MASK = PF_REVERSE_FLOW_PRIMARY | PF_REVERSE_FLOW_SECONDARY;
-
-    int mFlag = PF_LAYOUT_ENABLED
-            | PF_FOCUS_OUT_SIDE_START | PF_FOCUS_OUT_SIDE_END
-            | PF_PRUNE_CHILD | PF_SCROLL_ENABLED;
-
-    private OnChildSelectedListener mChildSelectedListener = null;
-
-    private ArrayList<OnChildViewHolderSelectedListener> mChildViewHolderSelectedListeners = null;
-
-    OnChildLaidOutListener mChildLaidOutListener = null;
-
-    /**
-     * The focused position, it's not the currently visually aligned position
-     * but it is the final position that we intend to focus on. If there are
-     * multiple setSelection() called, mFocusPosition saves last value.
-     */
-    int mFocusPosition = NO_POSITION;
-
-    /**
-     * A view can have multiple alignment position,  this is the index of which
-     * alignment is used,  by default is 0.
-     */
-    int mSubFocusPosition = 0;
-
-    /**
-     * LinearSmoothScroller that consume pending DPAD movements.
-     */
-    PendingMoveSmoothScroller mPendingMoveSmoothScroller;
-
-    /**
-     * The offset to be applied to mFocusPosition, due to adapter change, on the next
-     * layout.  Set to Integer.MIN_VALUE means we should stop adding delta to mFocusPosition
-     * until next layout cycler.
-     * TODO:  This is somewhat duplication of RecyclerView getOldPosition() which is
-     * unfortunately cleared after prelayout.
-     */
-    private int mFocusPositionOffset = 0;
-
-    /**
-     * Extra pixels applied on primary direction.
-     */
-    private int mPrimaryScrollExtra;
-
-    /**
-     * override child visibility
-     */
-    @Visibility
-    int mChildVisibility;
-
-    /**
-     * 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()}.
-     */
-    int mScrollOffsetSecondary;
-
-    /**
-     * User-specified row height/column width.  Can be WRAP_CONTENT.
-     */
-    private int mRowSizeSecondaryRequested;
-
-    /**
-     * The fixed size of each grid item in the secondary direction. This corresponds to
-     * the row height, equal for all rows. Grid items may have variable length
-     * in the primary direction.
-     */
-    private int mFixedRowSizeSecondary;
-
-    /**
-     * Tracks the secondary size of each row.
-     */
-    private int[] mRowSizeSecondary;
-
-    /**
-     * The maximum measured size of the view.
-     */
-    private int mMaxSizeSecondary;
-
-    /**
-     * Margin between items.
-     */
-    private int mHorizontalSpacing;
-    /**
-     * Margin between items vertically.
-     */
-    private int mVerticalSpacing;
-    /**
-     * Margin in main direction.
-     */
-    private int mSpacingPrimary;
-    /**
-     * Margin in second direction.
-     */
-    private int mSpacingSecondary;
-    /**
-     * How to position child in secondary direction.
-     */
-    private int mGravity = Gravity.START | Gravity.TOP;
-    /**
-     * The number of rows in the grid.
-     */
-    int mNumRows;
-    /**
-     * Number of rows requested, can be 0 to be determined by parent size and
-     * rowHeight.
-     */
-    private int mNumRowsRequested = 1;
-
-    /**
-     * Saves grid information of each view.
-     */
-    Grid mGrid;
-
-    /**
-     * Focus Scroll strategy.
-     */
-    private int mFocusScrollStrategy = BaseGridView.FOCUS_SCROLL_ALIGNED;
-    /**
-     * Defines how item view is aligned in the window.
-     */
-    final WindowAlignment mWindowAlignment = new WindowAlignment();
-
-    /**
-     * Defines how item view is aligned.
-     */
-    private final ItemAlignment mItemAlignment = new ItemAlignment();
-
-    /**
-     * Dimensions of the view, width or height depending on orientation.
-     */
-    private int mSizePrimary;
-
-    /**
-     * Pixels of extra space for layout item (outside the widget)
-     */
-    private int mExtraLayoutSpace;
-
-    /**
-     * Temporary variable: an int array of length=2.
-     */
-    static int[] sTwoInts = new int[2];
-
-    /**
-     * Temporaries used for measuring.
-     */
-    private int[] mMeasuredDimension = new int[2];
-
-    final ViewsStateBundle mChildrenStates = new ViewsStateBundle();
-
-    /**
-     * Optional interface implemented by Adapter.
-     */
-    private FacetProviderAdapter mFacetProviderAdapter;
-
-    public GridLayoutManager(BaseGridView baseGridView) {
-        mBaseGridView = baseGridView;
-        mChildVisibility = -1;
-        // disable prefetch by default, prefetch causes regression on low power chipset
-        setItemPrefetchEnabled(false);
-    }
-
-    public void setOrientation(@RecyclerView.Orientation int orientation) {
-        if (orientation != HORIZONTAL && orientation != VERTICAL) {
-            if (DEBUG) Log.v(getTag(), "invalid orientation: " + orientation);
-            return;
-        }
-
-        mOrientation = orientation;
-        mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
-        mWindowAlignment.setOrientation(orientation);
-        mItemAlignment.setOrientation(orientation);
-        mFlag |= PF_FORCE_FULL_LAYOUT;
-    }
-
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        final int flags;
-        if (mOrientation == HORIZONTAL) {
-            flags = layoutDirection == View.LAYOUT_DIRECTION_RTL ? PF_REVERSE_FLOW_PRIMARY : 0;
-        } else {
-            flags = layoutDirection == View.LAYOUT_DIRECTION_RTL ? PF_REVERSE_FLOW_SECONDARY : 0;
-        }
-        if ((mFlag & PF_REVERSE_FLOW_MASK) == flags) {
-            return;
-        }
-        mFlag = (mFlag & ~PF_REVERSE_FLOW_MASK) | flags;
-        mFlag |= PF_FORCE_FULL_LAYOUT;
-        mWindowAlignment.horizontal.setReversedFlow(layoutDirection == View.LAYOUT_DIRECTION_RTL);
-    }
-
-    public int getFocusScrollStrategy() {
-        return mFocusScrollStrategy;
-    }
-
-    public void setFocusScrollStrategy(int focusScrollStrategy) {
-        mFocusScrollStrategy = focusScrollStrategy;
-    }
-
-    public void setWindowAlignment(int windowAlignment) {
-        mWindowAlignment.mainAxis().setWindowAlignment(windowAlignment);
-    }
-
-    public int getWindowAlignment() {
-        return mWindowAlignment.mainAxis().getWindowAlignment();
-    }
-
-    public void setWindowAlignmentOffset(int alignmentOffset) {
-        mWindowAlignment.mainAxis().setWindowAlignmentOffset(alignmentOffset);
-    }
-
-    public int getWindowAlignmentOffset() {
-        return mWindowAlignment.mainAxis().getWindowAlignmentOffset();
-    }
-
-    public void setWindowAlignmentOffsetPercent(float offsetPercent) {
-        mWindowAlignment.mainAxis().setWindowAlignmentOffsetPercent(offsetPercent);
-    }
-
-    public float getWindowAlignmentOffsetPercent() {
-        return mWindowAlignment.mainAxis().getWindowAlignmentOffsetPercent();
-    }
-
-    public void setItemAlignmentOffset(int alignmentOffset) {
-        mItemAlignment.mainAxis().setItemAlignmentOffset(alignmentOffset);
-        updateChildAlignments();
-    }
-
-    public int getItemAlignmentOffset() {
-        return mItemAlignment.mainAxis().getItemAlignmentOffset();
-    }
-
-    public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
-        mItemAlignment.mainAxis().setItemAlignmentOffsetWithPadding(withPadding);
-        updateChildAlignments();
-    }
-
-    public boolean isItemAlignmentOffsetWithPadding() {
-        return mItemAlignment.mainAxis().isItemAlignmentOffsetWithPadding();
-    }
-
-    public void setItemAlignmentOffsetPercent(float offsetPercent) {
-        mItemAlignment.mainAxis().setItemAlignmentOffsetPercent(offsetPercent);
-        updateChildAlignments();
-    }
-
-    public float getItemAlignmentOffsetPercent() {
-        return mItemAlignment.mainAxis().getItemAlignmentOffsetPercent();
-    }
-
-    public void setItemAlignmentViewId(int viewId) {
-        mItemAlignment.mainAxis().setItemAlignmentViewId(viewId);
-        updateChildAlignments();
-    }
-
-    public int getItemAlignmentViewId() {
-        return mItemAlignment.mainAxis().getItemAlignmentViewId();
-    }
-
-    public void setFocusOutAllowed(boolean throughFront, boolean throughEnd) {
-        mFlag = (mFlag & ~PF_FOCUS_OUT_MASKS)
-                | (throughFront ? PF_FOCUS_OUT_FRONT : 0)
-                | (throughEnd ? PF_FOCUS_OUT_END : 0);
-    }
-
-    public void setFocusOutSideAllowed(boolean throughStart, boolean throughEnd) {
-        mFlag = (mFlag & ~PF_FOCUS_OUT_SIDE_MASKS)
-                | (throughStart ? PF_FOCUS_OUT_SIDE_START : 0)
-                | (throughEnd ? PF_FOCUS_OUT_SIDE_END : 0);
-    }
-
-    public void setNumRows(int numRows) {
-        if (numRows < 0) throw new IllegalArgumentException();
-        mNumRowsRequested = numRows;
-    }
-
-    /**
-     * Set the row height. May be WRAP_CONTENT, or a size in pixels.
-     */
-    public void setRowHeight(int height) {
-        if (height >= 0 || height == ViewGroup.LayoutParams.WRAP_CONTENT) {
-            mRowSizeSecondaryRequested = height;
-        } else {
-            throw new IllegalArgumentException("Invalid row height: " + height);
-        }
-    }
-
-    public void setItemSpacing(int space) {
-        mVerticalSpacing = mHorizontalSpacing = space;
-        mSpacingPrimary = mSpacingSecondary = space;
-    }
-
-    public void setVerticalSpacing(int space) {
-        if (mOrientation == VERTICAL) {
-            mSpacingPrimary = mVerticalSpacing = space;
-        } else {
-            mSpacingSecondary = mVerticalSpacing = space;
-        }
-    }
-
-    public void setHorizontalSpacing(int space) {
-        if (mOrientation == HORIZONTAL) {
-            mSpacingPrimary = mHorizontalSpacing = space;
-        } else {
-            mSpacingSecondary = mHorizontalSpacing = space;
-        }
-    }
-
-    public int getVerticalSpacing() {
-        return mVerticalSpacing;
-    }
-
-    public int getHorizontalSpacing() {
-        return mHorizontalSpacing;
-    }
-
-    public void setGravity(int gravity) {
-        mGravity = gravity;
-    }
-
-    protected boolean hasDoneFirstLayout() {
-        return mGrid != null;
-    }
-
-    public void setOnChildSelectedListener(OnChildSelectedListener listener) {
-        mChildSelectedListener = listener;
-    }
-
-    public void setOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
-        if (listener == null) {
-            mChildViewHolderSelectedListeners = null;
-            return;
-        }
-        if (mChildViewHolderSelectedListeners == null) {
-            mChildViewHolderSelectedListeners = new ArrayList<OnChildViewHolderSelectedListener>();
-        } else {
-            mChildViewHolderSelectedListeners.clear();
-        }
-        mChildViewHolderSelectedListeners.add(listener);
-    }
-
-    public void addOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
-        if (mChildViewHolderSelectedListeners == null) {
-            mChildViewHolderSelectedListeners = new ArrayList<OnChildViewHolderSelectedListener>();
-        }
-        mChildViewHolderSelectedListeners.add(listener);
-    }
-
-    public void removeOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener
-            listener) {
-        if (mChildViewHolderSelectedListeners != null) {
-            mChildViewHolderSelectedListeners.remove(listener);
-        }
-    }
-
-    boolean hasOnChildViewHolderSelectedListener() {
-        return mChildViewHolderSelectedListeners != null
-                && mChildViewHolderSelectedListeners.size() > 0;
-    }
-
-    void fireOnChildViewHolderSelected(RecyclerView parent, RecyclerView.ViewHolder child,
-            int position, int subposition) {
-        if (mChildViewHolderSelectedListeners == null) {
-            return;
-        }
-        for (int i = mChildViewHolderSelectedListeners.size() - 1; i >= 0 ; i--) {
-            mChildViewHolderSelectedListeners.get(i).onChildViewHolderSelected(parent, child,
-                    position, subposition);
-        }
-    }
-
-    void fireOnChildViewHolderSelectedAndPositioned(RecyclerView parent, RecyclerView.ViewHolder
-            child, int position, int subposition) {
-        if (mChildViewHolderSelectedListeners == null) {
-            return;
-        }
-        for (int i = mChildViewHolderSelectedListeners.size() - 1; i >= 0 ; i--) {
-            mChildViewHolderSelectedListeners.get(i).onChildViewHolderSelectedAndPositioned(parent,
-                    child, position, subposition);
-        }
-    }
-
-    void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
-        mChildLaidOutListener = listener;
-    }
-
-    private int getAdapterPositionByView(View view) {
-        if (view == null) {
-            return NO_POSITION;
-        }
-        LayoutParams params = (LayoutParams) view.getLayoutParams();
-        if (params == null || params.isItemRemoved()) {
-            // when item is removed, the position value can be any value.
-            return NO_POSITION;
-        }
-        return params.getViewAdapterPosition();
-    }
-
-    int getSubPositionByView(View view, View childView) {
-        if (view == null || childView == null) {
-            return 0;
-        }
-        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        final ItemAlignmentFacet facet = lp.getItemAlignmentFacet();
-        if (facet != null) {
-            final ItemAlignmentFacet.ItemAlignmentDef[] defs = facet.getAlignmentDefs();
-            if (defs.length > 1) {
-                while (childView != view) {
-                    int id = childView.getId();
-                    if (id != View.NO_ID) {
-                        for (int i = 1; i < defs.length; i++) {
-                            if (defs[i].getItemAlignmentFocusViewId() == id) {
-                                return i;
-                            }
-                        }
-                    }
-                    childView = (View) childView.getParent();
-                }
-            }
-        }
-        return 0;
-    }
-
-    private int getAdapterPositionByIndex(int index) {
-        return getAdapterPositionByView(getChildAt(index));
-    }
-
-    void dispatchChildSelected() {
-        if (mChildSelectedListener == null && !hasOnChildViewHolderSelectedListener()) {
-            return;
-        }
-
-        if (TRACE) TraceCompat.beginSection("onChildSelected");
-        View view = mFocusPosition == NO_POSITION ? null : findViewByPosition(mFocusPosition);
-        if (view != null) {
-            RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
-            if (mChildSelectedListener != null) {
-                mChildSelectedListener.onChildSelected(mBaseGridView, view, mFocusPosition,
-                        vh == null? NO_ID: vh.getItemId());
-            }
-            fireOnChildViewHolderSelected(mBaseGridView, vh, mFocusPosition, mSubFocusPosition);
-        } else {
-            if (mChildSelectedListener != null) {
-                mChildSelectedListener.onChildSelected(mBaseGridView, null, NO_POSITION, NO_ID);
-            }
-            fireOnChildViewHolderSelected(mBaseGridView, null, NO_POSITION, 0);
-        }
-        if (TRACE) TraceCompat.endSection();
-
-        // Children may request layout when a child selection event occurs (such as a change of
-        // padding on the current and previously selected rows).
-        // If in layout, a child requesting layout may have been laid out before the selection
-        // callback.
-        // If it was not, the child will be laid out after the selection callback.
-        // If so, the layout request will be honoured though the view system will emit a double-
-        // layout warning.
-        // If not in layout, we may be scrolling in which case the child layout request will be
-        // eaten by recyclerview.  Post a requestLayout.
-        if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT && !mBaseGridView.isLayoutRequested()) {
-            int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                if (getChildAt(i).isLayoutRequested()) {
-                    forceRequestLayout();
-                    break;
-                }
-            }
-        }
-    }
-
-    private void dispatchChildSelectedAndPositioned() {
-        if (!hasOnChildViewHolderSelectedListener()) {
-            return;
-        }
-
-        if (TRACE) TraceCompat.beginSection("onChildSelectedAndPositioned");
-        View view = mFocusPosition == NO_POSITION ? null : findViewByPosition(mFocusPosition);
-        if (view != null) {
-            RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
-            fireOnChildViewHolderSelectedAndPositioned(mBaseGridView, vh, mFocusPosition,
-                    mSubFocusPosition);
-        } else {
-            if (mChildSelectedListener != null) {
-                mChildSelectedListener.onChildSelected(mBaseGridView, null, NO_POSITION, NO_ID);
-            }
-            fireOnChildViewHolderSelectedAndPositioned(mBaseGridView, null, NO_POSITION, 0);
-        }
-        if (TRACE) TraceCompat.endSection();
-
-    }
-
-    @Override
-    public boolean canScrollHorizontally() {
-        // We can scroll horizontally if we have horizontal orientation, or if
-        // we are vertical and have more than one column.
-        return mOrientation == HORIZONTAL || mNumRows > 1;
-    }
-
-    @Override
-    public boolean canScrollVertically() {
-        // We can scroll vertically if we have vertical orientation, or if we
-        // are horizontal and have more than one row.
-        return mOrientation == VERTICAL || mNumRows > 1;
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateLayoutParams(Context context, AttributeSet attrs) {
-        return new LayoutParams(context, attrs);
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
-        if (lp instanceof LayoutParams) {
-            return new LayoutParams((LayoutParams) lp);
-        } else if (lp instanceof RecyclerView.LayoutParams) {
-            return new LayoutParams((RecyclerView.LayoutParams) lp);
-        } else if (lp instanceof MarginLayoutParams) {
-            return new LayoutParams((MarginLayoutParams) lp);
-        } else {
-            return new LayoutParams(lp);
-        }
-    }
-
-    protected View getViewForPosition(int position) {
-        return mRecycler.getViewForPosition(position);
-    }
-
-    final int getOpticalLeft(View v) {
-        return ((LayoutParams) v.getLayoutParams()).getOpticalLeft(v);
-    }
-
-    final int getOpticalRight(View v) {
-        return ((LayoutParams) v.getLayoutParams()).getOpticalRight(v);
-    }
-
-    final int getOpticalTop(View v) {
-        return ((LayoutParams) v.getLayoutParams()).getOpticalTop(v);
-    }
-
-    final int getOpticalBottom(View v) {
-        return ((LayoutParams) v.getLayoutParams()).getOpticalBottom(v);
-    }
-
-    @Override
-    public int getDecoratedLeft(View child) {
-        return super.getDecoratedLeft(child) + ((LayoutParams) child.getLayoutParams()).mLeftInset;
-    }
-
-    @Override
-    public int getDecoratedTop(View child) {
-        return super.getDecoratedTop(child) + ((LayoutParams) child.getLayoutParams()).mTopInset;
-    }
-
-    @Override
-    public int getDecoratedRight(View child) {
-        return super.getDecoratedRight(child)
-                - ((LayoutParams) child.getLayoutParams()).mRightInset;
-    }
-
-    @Override
-    public int getDecoratedBottom(View child) {
-        return super.getDecoratedBottom(child)
-                - ((LayoutParams) child.getLayoutParams()).mBottomInset;
-    }
-
-    @Override
-    public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
-        super.getDecoratedBoundsWithMargins(view, outBounds);
-        LayoutParams params = ((LayoutParams) view.getLayoutParams());
-        outBounds.left += params.mLeftInset;
-        outBounds.top += params.mTopInset;
-        outBounds.right -= params.mRightInset;
-        outBounds.bottom -= params.mBottomInset;
-    }
-
-    int getViewMin(View v) {
-        return mOrientationHelper.getDecoratedStart(v);
-    }
-
-    int getViewMax(View v) {
-        return mOrientationHelper.getDecoratedEnd(v);
-    }
-
-    int getViewPrimarySize(View view) {
-        getDecoratedBoundsWithMargins(view, sTempRect);
-        return mOrientation == HORIZONTAL ? sTempRect.width() : sTempRect.height();
-    }
-
-    private int getViewCenter(View view) {
-        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);
-    }
-
-    private int getViewCenterX(View v) {
-        LayoutParams p = (LayoutParams) v.getLayoutParams();
-        return p.getOpticalLeft(v) + p.getAlignX();
-    }
-
-    private int getViewCenterY(View v) {
-        LayoutParams p = (LayoutParams) v.getLayoutParams();
-        return p.getOpticalTop(v) + p.getAlignY();
-    }
-
-    /**
-     * Save Recycler and State for convenience.  Must be paired with leaveContext().
-     */
-    private void saveContext(Recycler recycler, State state) {
-        if (mRecycler != null || mState != null) {
-            Log.e(TAG, "Recycler information was not released, bug!");
-        }
-        mRecycler = recycler;
-        mState = state;
-        mPositionDeltaInPreLayout = 0;
-        mExtraLayoutSpaceInPreLayout = 0;
-    }
-
-    /**
-     * Discard saved Recycler and State.
-     */
-    private void leaveContext() {
-        mRecycler = null;
-        mState = null;
-        mPositionDeltaInPreLayout = 0;
-        mExtraLayoutSpaceInPreLayout = 0;
-    }
-
-    /**
-     * Re-initialize data structures for a data change or handling invisible
-     * selection. The method tries its best to preserve position information so
-     * that staggered grid looks same before and after re-initialize.
-     * @return true if can fastRelayout()
-     */
-    private boolean layoutInit() {
-        final int newItemCount = mState.getItemCount();
-        if (newItemCount == 0) {
-            mFocusPosition = NO_POSITION;
-            mSubFocusPosition = 0;
-        } else if (mFocusPosition >= newItemCount) {
-            mFocusPosition = newItemCount - 1;
-            mSubFocusPosition = 0;
-        } else if (mFocusPosition == NO_POSITION && newItemCount > 0) {
-            // if focus position is never set before,  initialize it to 0
-            mFocusPosition = 0;
-            mSubFocusPosition = 0;
-        }
-        if (!mState.didStructureChange() && mGrid != null && mGrid.getFirstVisibleIndex() >= 0
-                && (mFlag & PF_FORCE_FULL_LAYOUT) == 0 && mGrid.getNumRows() == mNumRows) {
-            updateScrollController();
-            updateSecondaryScrollLimits();
-            mGrid.setSpacing(mSpacingPrimary);
-            return true;
-        } else {
-            mFlag &= ~PF_FORCE_FULL_LAYOUT;
-
-            if (mGrid == null || mNumRows != mGrid.getNumRows()
-                    || ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) != mGrid.isReversedFlow()) {
-                mGrid = Grid.createGrid(mNumRows);
-                mGrid.setProvider(mGridProvider);
-                mGrid.setReversedFlow((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0);
-            }
-            initScrollController();
-            updateSecondaryScrollLimits();
-            mGrid.setSpacing(mSpacingPrimary);
-            detachAndScrapAttachedViews(mRecycler);
-            mGrid.resetVisibleIndex();
-            mWindowAlignment.mainAxis().invalidateScrollMin();
-            mWindowAlignment.mainAxis().invalidateScrollMax();
-            return false;
-        }
-    }
-
-    private int getRowSizeSecondary(int rowIndex) {
-        if (mFixedRowSizeSecondary != 0) {
-            return mFixedRowSizeSecondary;
-        }
-        if (mRowSizeSecondary == null) {
-            return 0;
-        }
-        return mRowSizeSecondary[rowIndex];
-    }
-
-    int getRowStartSecondary(int rowIndex) {
-        int start = 0;
-        // Iterate from left to right, which is a different index traversal
-        // in RTL flow
-        if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) {
-            for (int i = mNumRows-1; i > rowIndex; i--) {
-                start += getRowSizeSecondary(i) + mSpacingSecondary;
-            }
-        } else {
-            for (int i = 0; i < rowIndex; i++) {
-                start += getRowSizeSecondary(i) + mSpacingSecondary;
-            }
-        }
-        return start;
-    }
-
-    private int getSizeSecondary() {
-        int rightmostIndex = (mFlag & PF_REVERSE_FLOW_SECONDARY) != 0 ? 0 : mNumRows - 1;
-        return getRowStartSecondary(rightmostIndex) + getRowSizeSecondary(rightmostIndex);
-    }
-
-    int getDecoratedMeasuredWidthWithMargin(View v) {
-        final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-        return getDecoratedMeasuredWidth(v) + lp.leftMargin + lp.rightMargin;
-    }
-
-    int getDecoratedMeasuredHeightWithMargin(View v) {
-        final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-        return getDecoratedMeasuredHeight(v) + lp.topMargin + lp.bottomMargin;
-    }
-
-    private void measureScrapChild(int position, int widthSpec, int heightSpec,
-            int[] measuredDimension) {
-        View view = mRecycler.getViewForPosition(position);
-        if (view != null) {
-            final LayoutParams p = (LayoutParams) view.getLayoutParams();
-            calculateItemDecorationsForChild(view, sTempRect);
-            int widthUsed = p.leftMargin + p.rightMargin + sTempRect.left + sTempRect.right;
-            int heightUsed = p.topMargin + p.bottomMargin + sTempRect.top + sTempRect.bottom;
-
-            int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
-                    getPaddingLeft() + getPaddingRight() + widthUsed, p.width);
-            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
-                    getPaddingTop() + getPaddingBottom() + heightUsed, p.height);
-            view.measure(childWidthSpec, childHeightSpec);
-
-            measuredDimension[0] = getDecoratedMeasuredWidthWithMargin(view);
-            measuredDimension[1] = getDecoratedMeasuredHeightWithMargin(view);
-            mRecycler.recycleView(view);
-        }
-    }
-
-    private boolean processRowSizeSecondary(boolean measure) {
-        if (mFixedRowSizeSecondary != 0 || mRowSizeSecondary == null) {
-            return false;
-        }
-
-        if (TRACE) TraceCompat.beginSection("processRowSizeSecondary");
-        CircularIntArray[] rows = mGrid == null ? null : mGrid.getItemPositionsInRows();
-        boolean changed = false;
-        int scrapeChildSize = -1;
-
-        for (int rowIndex = 0; rowIndex < mNumRows; rowIndex++) {
-            CircularIntArray row = rows == null ? null : rows[rowIndex];
-            final int rowItemsPairCount = row == null ? 0 : row.size();
-            int rowSize = -1;
-            for (int rowItemPairIndex = 0; rowItemPairIndex < rowItemsPairCount;
-                    rowItemPairIndex += 2) {
-                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 - mPositionDeltaInPreLayout);
-                    if (view == null) {
-                        continue;
-                    }
-                    if (measure) {
-                        measureChild(view);
-                    }
-                    final int secondarySize = mOrientation == HORIZONTAL
-                            ? getDecoratedMeasuredHeightWithMargin(view)
-                            : getDecoratedMeasuredWidthWithMargin(view);
-                    if (secondarySize > rowSize) {
-                        rowSize = secondarySize;
-                    }
-                }
-            }
-
-            final int itemCount = mState.getItemCount();
-            if (!mBaseGridView.hasFixedSize() && measure && rowSize < 0 && itemCount > 0) {
-                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 (position >= itemCount) {
-                        position = itemCount - 1;
-                    }
-                    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[1] :
-                                mMeasuredDimension[0];
-                        if (DEBUG) {
-                            Log.v(TAG, "measured scrap child: " + mMeasuredDimension[0] + " "
-                                    + mMeasuredDimension[1]);
-                        }
-                    }
-                }
-                if (scrapeChildSize >= 0) {
-                    rowSize = scrapeChildSize;
-                }
-            }
-            if (rowSize < 0) {
-                rowSize = 0;
-            }
-            if (mRowSizeSecondary[rowIndex] != rowSize) {
-                if (DEBUG) {
-                    Log.v(getTag(), "row size secondary changed: " + mRowSizeSecondary[rowIndex]
-                            + ", " + rowSize);
-                }
-                mRowSizeSecondary[rowIndex] = rowSize;
-                changed = true;
-            }
-        }
-
-        if (TRACE) TraceCompat.endSection();
-        return changed;
-    }
-
-    /**
-     * Checks if we need to update row secondary sizes.
-     */
-    private void updateRowSecondarySizeRefresh() {
-        mFlag = (mFlag & ~PF_ROW_SECONDARY_SIZE_REFRESH)
-                | (processRowSizeSecondary(false) ? PF_ROW_SECONDARY_SIZE_REFRESH : 0);
-        if ((mFlag & PF_ROW_SECONDARY_SIZE_REFRESH) != 0) {
-            if (DEBUG) Log.v(getTag(), "mRowSecondarySizeRefresh now set");
-            forceRequestLayout();
-        }
-    }
-
-    private void forceRequestLayout() {
-        if (DEBUG) Log.v(getTag(), "forceRequestLayout");
-        // RecyclerView prevents us from requesting layout in many cases
-        // (during layout, during scroll, etc.)
-        // For secondary row size wrap_content support we currently need a
-        // second layout pass to update the measured size after having measured
-        // and added child views in layoutChildren.
-        // Force the second layout by posting a delayed runnable.
-        // TODO: investigate allowing a second layout pass,
-        // or move child add/measure logic to the measure phase.
-        ViewCompat.postOnAnimation(mBaseGridView, mRequestLayoutRunnable);
-    }
-
-    private final Runnable mRequestLayoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG) Log.v(getTag(), "request Layout from runnable");
-            requestLayout();
-        }
-    };
-
-    @Override
-    public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
-        saveContext(recycler, state);
-
-        int sizePrimary, sizeSecondary, modeSecondary, paddingSecondary;
-        int measuredSizeSecondary;
-        if (mOrientation == HORIZONTAL) {
-            sizePrimary = MeasureSpec.getSize(widthSpec);
-            sizeSecondary = MeasureSpec.getSize(heightSpec);
-            modeSecondary = MeasureSpec.getMode(heightSpec);
-            paddingSecondary = getPaddingTop() + getPaddingBottom();
-        } else {
-            sizeSecondary = MeasureSpec.getSize(widthSpec);
-            sizePrimary = MeasureSpec.getSize(heightSpec);
-            modeSecondary = MeasureSpec.getMode(widthSpec);
-            paddingSecondary = getPaddingLeft() + getPaddingRight();
-        }
-        if (DEBUG) {
-            Log.v(getTag(), "onMeasure widthSpec " + Integer.toHexString(widthSpec)
-                    + " heightSpec " + Integer.toHexString(heightSpec)
-                    + " modeSecondary " + Integer.toHexString(modeSecondary)
-                    + " sizeSecondary " + sizeSecondary + " " + this);
-        }
-
-        mMaxSizeSecondary = sizeSecondary;
-
-        if (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT) {
-            mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested;
-            mFixedRowSizeSecondary = 0;
-
-            if (mRowSizeSecondary == null || mRowSizeSecondary.length != mNumRows) {
-                mRowSizeSecondary = new int[mNumRows];
-            }
-
-            if (mState.isPreLayout()) {
-                updatePositionDeltaInPreLayout();
-            }
-            // Measure all current children and update cached row height or column width
-            processRowSizeSecondary(true);
-
-            switch (modeSecondary) {
-                case MeasureSpec.UNSPECIFIED:
-                    measuredSizeSecondary = getSizeSecondary() + paddingSecondary;
-                    break;
-                case MeasureSpec.AT_MOST:
-                    measuredSizeSecondary = Math.min(getSizeSecondary() + paddingSecondary,
-                            mMaxSizeSecondary);
-                    break;
-                case MeasureSpec.EXACTLY:
-                    measuredSizeSecondary = mMaxSizeSecondary;
-                    break;
-                default:
-                    throw new IllegalStateException("wrong spec");
-            }
-
-        } else {
-            switch (modeSecondary) {
-                case MeasureSpec.UNSPECIFIED:
-                    mFixedRowSizeSecondary = mRowSizeSecondaryRequested == 0
-                            ? sizeSecondary - paddingSecondary : mRowSizeSecondaryRequested;
-                    mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested;
-                    measuredSizeSecondary = mFixedRowSizeSecondary * mNumRows + mSpacingSecondary
-                            * (mNumRows - 1) + paddingSecondary;
-                    break;
-                case MeasureSpec.AT_MOST:
-                case MeasureSpec.EXACTLY:
-                    if (mNumRowsRequested == 0 && mRowSizeSecondaryRequested == 0) {
-                        mNumRows = 1;
-                        mFixedRowSizeSecondary = sizeSecondary - paddingSecondary;
-                    } else if (mNumRowsRequested == 0) {
-                        mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
-                        mNumRows = (sizeSecondary + mSpacingSecondary)
-                                / (mRowSizeSecondaryRequested + mSpacingSecondary);
-                    } else if (mRowSizeSecondaryRequested == 0) {
-                        mNumRows = mNumRowsRequested;
-                        mFixedRowSizeSecondary = (sizeSecondary - paddingSecondary
-                                - mSpacingSecondary * (mNumRows - 1)) / mNumRows;
-                    } else {
-                        mNumRows = mNumRowsRequested;
-                        mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
-                    }
-                    measuredSizeSecondary = sizeSecondary;
-                    if (modeSecondary == MeasureSpec.AT_MOST) {
-                        int childrenSize = mFixedRowSizeSecondary * mNumRows + mSpacingSecondary
-                                * (mNumRows - 1) + paddingSecondary;
-                        if (childrenSize < measuredSizeSecondary) {
-                            measuredSizeSecondary = childrenSize;
-                        }
-                    }
-                    break;
-                default:
-                    throw new IllegalStateException("wrong spec");
-            }
-        }
-        if (mOrientation == HORIZONTAL) {
-            setMeasuredDimension(sizePrimary, measuredSizeSecondary);
-        } else {
-            setMeasuredDimension(measuredSizeSecondary, sizePrimary);
-        }
-        if (DEBUG) {
-            Log.v(getTag(), "onMeasure sizePrimary " + sizePrimary
-                    + " measuredSizeSecondary " + measuredSizeSecondary
-                    + " mFixedRowSizeSecondary " + mFixedRowSizeSecondary
-                    + " mNumRows " + mNumRows);
-        }
-        leaveContext();
-    }
-
-    void measureChild(View child) {
-        if (TRACE) TraceCompat.beginSection("measureChild");
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        calculateItemDecorationsForChild(child, sTempRect);
-        int widthUsed = lp.leftMargin + lp.rightMargin + sTempRect.left + sTempRect.right;
-        int heightUsed = lp.topMargin + lp.bottomMargin + sTempRect.top + sTempRect.bottom;
-
-        final int secondarySpec =
-                (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT)
-                        ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
-                        : MeasureSpec.makeMeasureSpec(mFixedRowSizeSecondary, MeasureSpec.EXACTLY);
-        int widthSpec, heightSpec;
-
-        if (mOrientation == HORIZONTAL) {
-            widthSpec = ViewGroup.getChildMeasureSpec(
-                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), widthUsed, lp.width);
-            heightSpec = ViewGroup.getChildMeasureSpec(secondarySpec, heightUsed, lp.height);
-        } else {
-            heightSpec = ViewGroup.getChildMeasureSpec(
-                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightUsed, lp.height);
-            widthSpec = ViewGroup.getChildMeasureSpec(secondarySpec, widthUsed, lp.width);
-        }
-        child.measure(widthSpec, heightSpec);
-        if (DEBUG) {
-            Log.v(getTag(), "measureChild secondarySpec " + Integer.toHexString(secondarySpec)
-                    + " widthSpec " + Integer.toHexString(widthSpec)
-                    + " heightSpec " + Integer.toHexString(heightSpec)
-                    + " measuredWidth " + child.getMeasuredWidth()
-                    + " measuredHeight " + child.getMeasuredHeight());
-        }
-        if (DEBUG) Log.v(getTag(), "child lp width " + lp.width + " height " + lp.height);
-        if (TRACE) TraceCompat.endSection();
-    }
-
-    /**
-     * Get facet from the ViewHolder or the viewType.
-     */
-    <E> E getFacet(RecyclerView.ViewHolder vh, Class<? extends E> facetClass) {
-        E facet = null;
-        if (vh instanceof FacetProvider) {
-            facet = (E) ((FacetProvider) vh).getFacet(facetClass);
-        }
-        if (facet == null && mFacetProviderAdapter != null) {
-            FacetProvider p = mFacetProviderAdapter.getFacetProvider(vh.getItemViewType());
-            if (p != null) {
-                facet = (E) p.getFacet(facetClass);
-            }
-        }
-        return facet;
-    }
-
-    private Grid.Provider mGridProvider = new Grid.Provider() {
-
-        @Override
-        public int getMinIndex() {
-            return mPositionDeltaInPreLayout;
-        }
-
-        @Override
-        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 - mPositionDeltaInPreLayout);
-            if (TRACE) TraceCompat.endSection();
-            LayoutParams lp = (LayoutParams) v.getLayoutParams();
-            RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(v);
-            lp.setItemAlignmentFacet((ItemAlignmentFacet)getFacet(vh, ItemAlignmentFacet.class));
-            // See recyclerView docs:  we don't need re-add scraped view if it was removed.
-            if (!lp.isItemRemoved()) {
-                if (TRACE) TraceCompat.beginSection("addView");
-                if (disappearingItem) {
-                    if (append) {
-                        addDisappearingView(v);
-                    } else {
-                        addDisappearingView(v, 0);
-                    }
-                } else {
-                    if (append) {
-                        addView(v);
-                    } else {
-                        addView(v, 0);
-                    }
-                }
-                if (TRACE) TraceCompat.endSection();
-                if (mChildVisibility != -1) {
-                    v.setVisibility(mChildVisibility);
-                }
-
-                if (mPendingMoveSmoothScroller != null) {
-                    mPendingMoveSmoothScroller.consumePendingMovesBeforeLayout();
-                }
-                int subindex = getSubPositionByView(v, v.findFocus());
-                if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT) {
-                    // when we are appending item during scroll pass and the item's position
-                    // matches the mFocusPosition,  we should signal a childSelected event.
-                    // However if we are still running PendingMoveSmoothScroller,  we defer and
-                    // signal the event in PendingMoveSmoothScroller.onStop().  This can
-                    // avoid lots of childSelected events during a long smooth scrolling and
-                    // increase performance.
-                    if (index == mFocusPosition && subindex == mSubFocusPosition
-                            && mPendingMoveSmoothScroller == null) {
-                        dispatchChildSelected();
-                    }
-                } else if ((mFlag & PF_FAST_RELAYOUT) == 0) {
-                    // fastRelayout will dispatch event at end of onLayoutChildren().
-                    // For full layout, two situations here:
-                    // 1. mInLayoutSearchFocus is false, dispatchChildSelected() at mFocusPosition.
-                    // 2. mInLayoutSearchFocus is true:  dispatchChildSelected() on first child
-                    //    equal to or after mFocusPosition that can take focus.
-                    if ((mFlag & PF_IN_LAYOUT_SEARCH_FOCUS) == 0 && index == mFocusPosition
-                            && subindex == mSubFocusPosition) {
-                        dispatchChildSelected();
-                    } else if ((mFlag & PF_IN_LAYOUT_SEARCH_FOCUS) != 0 && index >= mFocusPosition
-                            && v.hasFocusable()) {
-                        mFocusPosition = index;
-                        mSubFocusPosition = subindex;
-                        mFlag &= ~PF_IN_LAYOUT_SEARCH_FOCUS;
-                        dispatchChildSelected();
-                    }
-                }
-                measureChild(v);
-            }
-            item[0] = v;
-            return mOrientation == HORIZONTAL ? getDecoratedMeasuredWidthWithMargin(v)
-                    : getDecoratedMeasuredHeightWithMargin(v);
-        }
-
-        @Override
-        public void addItem(Object item, int index, int length, int rowIndex, int edge) {
-            View v = (View) item;
-            int start, end;
-            if (edge == Integer.MIN_VALUE || edge == Integer.MAX_VALUE) {
-                edge = !mGrid.isReversedFlow() ? mWindowAlignment.mainAxis().getPaddingMin()
-                        : mWindowAlignment.mainAxis().getSize()
-                                - mWindowAlignment.mainAxis().getPaddingMax();
-            }
-            boolean edgeIsMin = !mGrid.isReversedFlow();
-            if (edgeIsMin) {
-                start = edge;
-                end = edge + length;
-            } else {
-                start = edge - length;
-                end = edge;
-            }
-            int startSecondary = getRowStartSecondary(rowIndex)
-                    + mWindowAlignment.secondAxis().getPaddingMin() - mScrollOffsetSecondary;
-            mChildrenStates.loadView(v, index);
-            layoutChild(rowIndex, v, start, end, startSecondary);
-            if (DEBUG) {
-                Log.d(getTag(), "addView " + index + " " + v);
-            }
-            if (TRACE) TraceCompat.endSection();
-
-            if (!mState.isPreLayout()) {
-                updateScrollLimits();
-            }
-            if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT && mPendingMoveSmoothScroller != null) {
-                mPendingMoveSmoothScroller.consumePendingMovesAfterLayout();
-            }
-            if (mChildLaidOutListener != null) {
-                RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(v);
-                mChildLaidOutListener.onChildLaidOut(mBaseGridView, v, index,
-                        vh == null ? NO_ID : vh.getItemId());
-            }
-        }
-
-        @Override
-        public void removeItem(int index) {
-            if (TRACE) TraceCompat.beginSection("removeItem");
-            View v = findViewByPosition(index - mPositionDeltaInPreLayout);
-            if ((mFlag & PF_STAGE_MASK) == PF_STAGE_LAYOUT) {
-                detachAndScrapView(v, mRecycler);
-            } else {
-                removeAndRecycleView(v, mRecycler);
-            }
-            if (TRACE) TraceCompat.endSection();
-        }
-
-        @Override
-        public int getEdge(int index) {
-            View v = findViewByPosition(index - mPositionDeltaInPreLayout);
-            return (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0 ? getViewMax(v) : getViewMin(v);
-        }
-
-        @Override
-        public int getSize(int index) {
-            return getViewPrimarySize(findViewByPosition(index - mPositionDeltaInPreLayout));
-        }
-    };
-
-    void layoutChild(int rowIndex, View v, int start, int end, int startSecondary) {
-        if (TRACE) TraceCompat.beginSection("layoutChild");
-        int sizeSecondary = mOrientation == HORIZONTAL ? getDecoratedMeasuredHeightWithMargin(v)
-                : getDecoratedMeasuredWidthWithMargin(v);
-        if (mFixedRowSizeSecondary > 0) {
-            sizeSecondary = Math.min(sizeSecondary, mFixedRowSizeSecondary);
-        }
-        final int verticalGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int horizontalGravity = (mFlag & PF_REVERSE_FLOW_MASK) != 0
-                ? Gravity.getAbsoluteGravity(mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK,
-                View.LAYOUT_DIRECTION_RTL)
-                : mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        if ((mOrientation == HORIZONTAL && verticalGravity == Gravity.TOP)
-                || (mOrientation == VERTICAL && horizontalGravity == Gravity.LEFT)) {
-            // do nothing
-        } else if ((mOrientation == HORIZONTAL && verticalGravity == Gravity.BOTTOM)
-                || (mOrientation == VERTICAL && horizontalGravity == Gravity.RIGHT)) {
-            startSecondary += getRowSizeSecondary(rowIndex) - sizeSecondary;
-        } else if ((mOrientation == HORIZONTAL && verticalGravity == Gravity.CENTER_VERTICAL)
-                || (mOrientation == VERTICAL && horizontalGravity == Gravity.CENTER_HORIZONTAL)) {
-            startSecondary += (getRowSizeSecondary(rowIndex) - sizeSecondary) / 2;
-        }
-        int left, top, right, bottom;
-        if (mOrientation == HORIZONTAL) {
-            left = start;
-            top = startSecondary;
-            right = end;
-            bottom = startSecondary + sizeSecondary;
-        } else {
-            top = start;
-            left = startSecondary;
-            bottom = end;
-            right = startSecondary + sizeSecondary;
-        }
-        LayoutParams params = (LayoutParams) v.getLayoutParams();
-        layoutDecoratedWithMargins(v, left, top, right, bottom);
-        // Now super.getDecoratedBoundsWithMargins() includes the extra space for optical bounds,
-        // subtracting it from value passed in layoutDecoratedWithMargins(), we can get the optical
-        // bounds insets.
-        super.getDecoratedBoundsWithMargins(v, sTempRect);
-        params.setOpticalInsets(left - sTempRect.left, top - sTempRect.top,
-                sTempRect.right - right, sTempRect.bottom - bottom);
-        updateChildAlignments(v);
-        if (TRACE) TraceCompat.endSection();
-    }
-
-    private void updateChildAlignments(View v) {
-        final LayoutParams p = (LayoutParams) v.getLayoutParams();
-        if (p.getItemAlignmentFacet() == null) {
-            // Fallback to global settings on grid view
-            p.setAlignX(mItemAlignment.horizontal.getAlignmentPosition(v));
-            p.setAlignY(mItemAlignment.vertical.getAlignmentPosition(v));
-        } else {
-            // Use ItemAlignmentFacet defined on specific ViewHolder
-            p.calculateItemAlignments(mOrientation, v);
-            if (mOrientation == HORIZONTAL) {
-                p.setAlignY(mItemAlignment.vertical.getAlignmentPosition(v));
-            } else {
-                p.setAlignX(mItemAlignment.horizontal.getAlignmentPosition(v));
-            }
-        }
-    }
-
-    private void updateChildAlignments() {
-        for (int i = 0, c = getChildCount(); i < c; i++) {
-            updateChildAlignments(getChildAt(i));
-        }
-    }
-
-    void setExtraLayoutSpace(int extraLayoutSpace) {
-        if (mExtraLayoutSpace == extraLayoutSpace) {
-            return;
-        } else if (mExtraLayoutSpace < 0) {
-            throw new IllegalArgumentException("ExtraLayoutSpace must >= 0");
-        }
-        mExtraLayoutSpace = extraLayoutSpace;
-        requestLayout();
-    }
-
-    int getExtraLayoutSpace() {
-        return mExtraLayoutSpace;
-    }
-
-    private void removeInvisibleViewsAtEnd() {
-        if ((mFlag & (PF_PRUNE_CHILD | PF_SLIDING)) == PF_PRUNE_CHILD) {
-            mGrid.removeInvisibleItemsAtEnd(mFocusPosition, (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
-                    ? -mExtraLayoutSpace : mSizePrimary + mExtraLayoutSpace);
-        }
-    }
-
-    private void removeInvisibleViewsAtFront() {
-        if ((mFlag & (PF_PRUNE_CHILD | PF_SLIDING)) == PF_PRUNE_CHILD) {
-            mGrid.removeInvisibleItemsAtFront(mFocusPosition, (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
-                    ? mSizePrimary + mExtraLayoutSpace : -mExtraLayoutSpace);
-        }
-    }
-
-    private boolean appendOneColumnVisibleItems() {
-        return mGrid.appendOneColumnVisibleItems();
-    }
-
-    void slideIn() {
-        if ((mFlag & PF_SLIDING) != 0) {
-            mFlag &= ~PF_SLIDING;
-            if (mFocusPosition >= 0) {
-                scrollToSelection(mFocusPosition, mSubFocusPosition, true, mPrimaryScrollExtra);
-            } else {
-                mFlag &= ~PF_LAYOUT_EATEN_IN_SLIDING;
-                requestLayout();
-            }
-            if ((mFlag & PF_LAYOUT_EATEN_IN_SLIDING) != 0) {
-                mFlag &= ~PF_LAYOUT_EATEN_IN_SLIDING;
-                if (mBaseGridView.getScrollState() != SCROLL_STATE_IDLE || isSmoothScrolling()) {
-                    mBaseGridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-                        @Override
-                        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-                            if (newState == SCROLL_STATE_IDLE) {
-                                mBaseGridView.removeOnScrollListener(this);
-                                requestLayout();
-                            }
-                        }
-                    });
-                } else {
-                    requestLayout();
-                }
-            }
-        }
-    }
-
-    int getSlideOutDistance() {
-        int distance;
-        if (mOrientation == VERTICAL) {
-            distance = -getHeight();
-            if (getChildCount() > 0) {
-                int top = getChildAt(0).getTop();
-                if (top < 0) {
-                    // scroll more if first child is above top edge
-                    distance = distance + top;
-                }
-            }
-        } else {
-            if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) {
-                distance = getWidth();
-                if (getChildCount() > 0) {
-                    int start = getChildAt(0).getRight();
-                    if (start > distance) {
-                        // scroll more if first child is outside right edge
-                        distance = start;
-                    }
-                }
-            } else {
-                distance = -getWidth();
-                if (getChildCount() > 0) {
-                    int start = getChildAt(0).getLeft();
-                    if (start < 0) {
-                        // scroll more if first child is out side left edge
-                        distance = distance + start;
-                    }
-                }
-            }
-        }
-        return distance;
-    }
-
-    boolean isSlidingChildViews() {
-        return (mFlag & PF_SLIDING) != 0;
-    }
-
-    /**
-     * Temporarily slide out child and block layout and scroll requests.
-     */
-    void slideOut() {
-        if ((mFlag & PF_SLIDING) != 0) {
-            return;
-        }
-        mFlag |= PF_SLIDING;
-        if (getChildCount() == 0) {
-            return;
-        }
-        if (mOrientation == VERTICAL) {
-            mBaseGridView.smoothScrollBy(0, getSlideOutDistance(),
-                    new AccelerateDecelerateInterpolator());
-        } else {
-            mBaseGridView.smoothScrollBy(getSlideOutDistance(), 0,
-                    new AccelerateDecelerateInterpolator());
-        }
-    }
-
-    private boolean prependOneColumnVisibleItems() {
-        return mGrid.prependOneColumnVisibleItems();
-    }
-
-    private void appendVisibleItems() {
-        mGrid.appendVisibleItems((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
-                ? -mExtraLayoutSpace - mExtraLayoutSpaceInPreLayout
-                : mSizePrimary + mExtraLayoutSpace + mExtraLayoutSpaceInPreLayout);
-    }
-
-    private void prependVisibleItems() {
-        mGrid.prependVisibleItems((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
-                ? mSizePrimary + mExtraLayoutSpace + mExtraLayoutSpaceInPreLayout
-                : -mExtraLayoutSpace - mExtraLayoutSpaceInPreLayout);
-    }
-
-    /**
-     * Fast layout when there is no structure change, adapter change, etc.
-     * It will layout all views was layout requested or updated, until hit a view
-     * with different size,  then it break and detachAndScrap all views after that.
-     */
-    private void fastRelayout() {
-        boolean invalidateAfter = false;
-        final int childCount = getChildCount();
-        int position = mGrid.getFirstVisibleIndex();
-        int index = 0;
-        mFlag &= ~PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION;
-        for (; index < childCount; index++, position++) {
-            View view = getChildAt(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) {
-                invalidateAfter = true;
-                break;
-            }
-
-            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()) {
-                mFlag |= PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION;
-                detachAndScrapView(view, mRecycler);
-                view = getViewForPosition(position);
-                addView(view, index);
-            }
-
-            measureChild(view);
-            if (mOrientation == HORIZONTAL) {
-                primarySize = getDecoratedMeasuredWidthWithMargin(view);
-                end = start + primarySize;
-            } else {
-                primarySize = getDecoratedMeasuredHeightWithMargin(view);
-                end = start + primarySize;
-            }
-            layoutChild(location.row, view, start, end, startSecondary);
-            if (oldPrimarySize != primarySize) {
-                // size changed invalidate remaining Locations
-                if (DEBUG) Log.d(getTag(), "fastRelayout: view size changed at " + position);
-                invalidateAfter = true;
-                break;
-            }
-        }
-        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 ((mFlag & PF_PRUNE_CHILD) != 0) {
-                // in regular prune child mode, we just append items up to edge limit
-                appendVisibleItems();
-                if (mFocusPosition >= 0 && mFocusPosition <= savedLastPos) {
-                    // make sure add focus view back:  the view might be outside edge limit
-                    // when there is delta in onLayoutChildren().
-                    while (mGrid.getLastVisibleIndex() < mFocusPosition) {
-                        mGrid.appendOneColumnVisibleItems();
-                    }
-                }
-            } else {
-                // prune disabled(e.g. in RowsFragment transition): append all removed items
-                while (mGrid.appendOneColumnVisibleItems()
-                        && mGrid.getLastVisibleIndex() < savedLastPos);
-            }
-        }
-        updateScrollLimits();
-        updateSecondaryScrollLimits();
-    }
-
-    @Override
-    public void removeAndRecycleAllViews(RecyclerView.Recycler recycler) {
-        if (TRACE) TraceCompat.beginSection("removeAndRecycleAllViews");
-        if (DEBUG) Log.v(TAG, "removeAndRecycleAllViews " + getChildCount());
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            removeAndRecycleViewAt(i, recycler);
-        }
-        if (TRACE) TraceCompat.endSection();
-    }
-
-    // called by onLayoutChildren, either focus to FocusPosition or declare focusViewAvailable
-    // and scroll to the view if framework focus on it.
-    private void focusToViewInLayout(boolean hadFocus, boolean alignToView, int extraDelta,
-            int extraDeltaSecondary) {
-        View focusView = findViewByPosition(mFocusPosition);
-        if (focusView != null && alignToView) {
-            scrollToView(focusView, false, extraDelta, extraDeltaSecondary);
-        }
-        if (focusView != null && hadFocus && !focusView.hasFocus()) {
-            focusView.requestFocus();
-        } else if (!hadFocus && !mBaseGridView.hasFocus()) {
-            if (focusView != null && focusView.hasFocusable()) {
-                mBaseGridView.focusableViewAvailable(focusView);
-            } else {
-                for (int i = 0, count = getChildCount(); i < count; i++) {
-                    focusView = getChildAt(i);
-                    if (focusView != null && focusView.hasFocusable()) {
-                        mBaseGridView.focusableViewAvailable(focusView);
-                        break;
-                    }
-                }
-            }
-            // focusViewAvailable() might focus to the view, scroll to it if that is the case.
-            if (alignToView && focusView != null && focusView.hasFocus()) {
-                scrollToView(focusView, false, extraDelta, extraDeltaSecondary);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    public static class OnLayoutCompleteListener {
-        public void onLayoutCompleted(RecyclerView.State state) {
-        }
-    }
-
-    @VisibleForTesting
-    OnLayoutCompleteListener mLayoutCompleteListener;
-
-    @Override
-    public void onLayoutCompleted(State state) {
-        if (mLayoutCompleteListener != null) {
-            mLayoutCompleteListener.onLayoutCompleted(state);
-        }
-    }
-
-    @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
-                    + " inPreLayout " + state.isPreLayout()
-                    + " didStructureChange " + state.didStructureChange()
-                    + " mForceFullLayout " + ((mFlag & PF_FORCE_FULL_LAYOUT) != 0));
-            Log.v(getTag(), "width " + getWidth() + " height " + getHeight());
-        }
-
-        if (mNumRows == 0) {
-            // haven't done measure yet
-            return;
-        }
-        final int itemCount = state.getItemCount();
-        if (itemCount < 0) {
-            return;
-        }
-
-        if ((mFlag & PF_SLIDING) != 0) {
-            // if there is already children, delay the layout process until slideIn(), if it's
-            // first time layout children: scroll them offscreen at end of onLayoutChildren()
-            if (getChildCount() > 0) {
-                mFlag |= PF_LAYOUT_EATEN_IN_SLIDING;
-                return;
-            }
-        }
-        if ((mFlag & PF_LAYOUT_ENABLED) == 0) {
-            discardLayoutInfo();
-            removeAndRecycleAllViews(recycler);
-            return;
-        }
-        mFlag = (mFlag & ~PF_STAGE_MASK) | PF_STAGE_LAYOUT;
-
-        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();
-            }
-            mFlag &= ~PF_STAGE_MASK;
-            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) {
-            mFocusPosition = mFocusPosition + mFocusPositionOffset;
-            mSubFocusPosition = 0;
-        }
-        mFocusPositionOffset = 0;
-
-        View savedFocusView = findViewByPosition(mFocusPosition);
-        int savedFocusPos = mFocusPosition;
-        int savedSubFocusPos = mSubFocusPosition;
-        boolean hadFocus = mBaseGridView.hasFocus();
-        final int firstVisibleIndex = mGrid != null ? mGrid.getFirstVisibleIndex() : NO_POSITION;
-        final int lastVisibleIndex = mGrid != null ? mGrid.getLastVisibleIndex() : NO_POSITION;
-        final int deltaPrimary;
-        final int deltaSecondary;
-        if (mOrientation == HORIZONTAL) {
-            deltaPrimary = state.getRemainingScrollHorizontal();
-            deltaSecondary = state.getRemainingScrollVertical();
-        } else {
-            deltaSecondary = state.getRemainingScrollHorizontal();
-            deltaPrimary = state.getRemainingScrollVertical();
-        }
-        if (layoutInit()) {
-            mFlag |= PF_FAST_RELAYOUT;
-            // If grid view is empty, we will start from mFocusPosition
-            mGrid.setStart(mFocusPosition);
-            fastRelayout();
-        } else {
-            mFlag &= ~PF_FAST_RELAYOUT;
-            // layoutInit() has detached all views, so start from scratch
-            mFlag = (mFlag & ~PF_IN_LAYOUT_SEARCH_FOCUS)
-                    | (hadFocus ? PF_IN_LAYOUT_SEARCH_FOCUS : 0);
-            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
-        // "visible window" and we update scrollMin/scrollMax then run second scrollToView
-        // we must do this for fastRelayout() for the append item case
-        int oldFirstVisible;
-        int oldLastVisible;
-        do {
-            updateScrollLimits();
-            oldFirstVisible = mGrid.getFirstVisibleIndex();
-            oldLastVisible = mGrid.getLastVisibleIndex();
-            focusToViewInLayout(hadFocus, scrollToFocus, -deltaPrimary, -deltaSecondary);
-            appendVisibleItems();
-            prependVisibleItems();
-            // b/67370222: do not removeInvisibleViewsAtFront/End() in the loop, otherwise
-            // loop may bounce between scroll forward and scroll backward forever. Example:
-            // Assuming there are 19 items, child#18 and child#19 are both in RV, we are
-            // trying to focus to child#18 and there are 200px remaining scroll distance.
-            //   1  focusToViewInLayout() tries scroll forward 50 px to align focused child#18 on
-            //      right edge, but there to compensate remaining scroll 200px, also scroll
-            //      backward 200px, 150px pushes last child#19 out side of right edge.
-            //   2  removeInvisibleViewsAtEnd() remove last child#19, updateScrollLimits()
-            //      invalidates scroll max
-            //   3  In next iteration, when scroll max/min is unknown, focusToViewInLayout() will
-            //      align focused child#18 at center of screen.
-            //   4  Because #18 is aligned at center, appendVisibleItems() will fill child#19 to
-            //      the right.
-            //   5  (back to 1 and loop forever)
-        } while (mGrid.getFirstVisibleIndex() != oldFirstVisible
-                || mGrid.getLastVisibleIndex() != oldLastVisible);
-        removeInvisibleViewsAtFront();
-        removeInvisibleViewsAtEnd();
-
-        if (state.willRunPredictiveAnimations()) {
-            fillScrapViewsInPostLayout();
-        }
-
-        if (DEBUG) {
-            StringWriter sw = new StringWriter();
-            PrintWriter pw = new PrintWriter(sw);
-            mGrid.debugPrint(pw);
-            Log.d(getTag(), sw.toString());
-        }
-
-        if ((mFlag & PF_ROW_SECONDARY_SIZE_REFRESH) != 0) {
-            mFlag &= ~PF_ROW_SECONDARY_SIZE_REFRESH;
-        } else {
-            updateRowSecondarySizeRefresh();
-        }
-
-        // For fastRelayout, only dispatch event when focus position changes or selected item
-        // being updated.
-        if ((mFlag & PF_FAST_RELAYOUT) != 0 && (mFocusPosition != savedFocusPos || mSubFocusPosition
-                != savedSubFocusPos || findViewByPosition(mFocusPosition) != savedFocusView
-                || (mFlag & PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION) != 0)) {
-            dispatchChildSelected();
-        } else if ((mFlag & (PF_FAST_RELAYOUT | PF_IN_LAYOUT_SEARCH_FOCUS))
-                == PF_IN_LAYOUT_SEARCH_FOCUS) {
-            // For full layout we dispatchChildSelected() in createItem() unless searched all
-            // children and found none is focusable then dispatchChildSelected() here.
-            dispatchChildSelected();
-        }
-        dispatchChildSelectedAndPositioned();
-        if ((mFlag & PF_SLIDING) != 0) {
-            scrollDirectionPrimary(getSlideOutDistance());
-        }
-
-        mFlag &= ~PF_STAGE_MASK;
-        leaveContext();
-        if (DEBUG) Log.v(getTag(), "layoutChildren end");
-    }
-
-    private void offsetChildrenSecondary(int increment) {
-        final int childCount = getChildCount();
-        if (mOrientation == HORIZONTAL) {
-            for (int i = 0; i < childCount; i++) {
-                getChildAt(i).offsetTopAndBottom(increment);
-            }
-        } else {
-            for (int i = 0; i < childCount; i++) {
-                getChildAt(i).offsetLeftAndRight(increment);
-            }
-        }
-    }
-
-    private void offsetChildrenPrimary(int increment) {
-        final int childCount = getChildCount();
-        if (mOrientation == VERTICAL) {
-            for (int i = 0; i < childCount; i++) {
-                getChildAt(i).offsetTopAndBottom(increment);
-            }
-        } else {
-            for (int i = 0; i < childCount; i++) {
-                getChildAt(i).offsetLeftAndRight(increment);
-            }
-        }
-    }
-
-    @Override
-    public int scrollHorizontallyBy(int dx, Recycler recycler, RecyclerView.State state) {
-        if (DEBUG) Log.v(getTag(), "scrollHorizontallyBy " + dx);
-        if ((mFlag & PF_LAYOUT_ENABLED) == 0 || !hasDoneFirstLayout()) {
-            return 0;
-        }
-        saveContext(recycler, state);
-        mFlag = (mFlag & ~PF_STAGE_MASK) | PF_STAGE_SCROLL;
-        int result;
-        if (mOrientation == HORIZONTAL) {
-            result = scrollDirectionPrimary(dx);
-        } else {
-            result = scrollDirectionSecondary(dx);
-        }
-        leaveContext();
-        mFlag &= ~PF_STAGE_MASK;
-        return result;
-    }
-
-    @Override
-    public int scrollVerticallyBy(int dy, Recycler recycler, RecyclerView.State state) {
-        if (DEBUG) Log.v(getTag(), "scrollVerticallyBy " + dy);
-        if ((mFlag & PF_LAYOUT_ENABLED) == 0 || !hasDoneFirstLayout()) {
-            return 0;
-        }
-        mFlag = (mFlag & ~PF_STAGE_MASK) | PF_STAGE_SCROLL;
-        saveContext(recycler, state);
-        int result;
-        if (mOrientation == VERTICAL) {
-            result = scrollDirectionPrimary(dy);
-        } else {
-            result = scrollDirectionSecondary(dy);
-        }
-        leaveContext();
-        mFlag &= ~PF_STAGE_MASK;
-        return result;
-    }
-
-    // scroll in main direction may add/prune views
-    private int scrollDirectionPrimary(int da) {
-        if (TRACE) TraceCompat.beginSection("scrollPrimary");
-        // We apply the cap of maxScroll/minScroll to the delta, except for two cases:
-        // 1. when children are in sliding out mode
-        // 2. During onLayoutChildren(), it may compensate the remaining scroll delta,
-        //    we should honor the request regardless if it goes over minScroll / maxScroll.
-        //    (see b/64931938 testScrollAndRemove and testScrollAndRemoveSample1)
-        if ((mFlag & PF_SLIDING) == 0 && (mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT) {
-            if (da > 0) {
-                if (!mWindowAlignment.mainAxis().isMaxUnknown()) {
-                    int maxScroll = mWindowAlignment.mainAxis().getMaxScroll();
-                    if (da > maxScroll) {
-                        da = maxScroll;
-                    }
-                }
-            } else if (da < 0) {
-                if (!mWindowAlignment.mainAxis().isMinUnknown()) {
-                    int minScroll = mWindowAlignment.mainAxis().getMinScroll();
-                    if (da < minScroll) {
-                        da = minScroll;
-                    }
-                }
-            }
-        }
-        if (da == 0) {
-            if (TRACE) TraceCompat.endSection();
-            return 0;
-        }
-        offsetChildrenPrimary(-da);
-        if ((mFlag & PF_STAGE_MASK) == PF_STAGE_LAYOUT) {
-            updateScrollLimits();
-            if (TRACE) TraceCompat.endSection();
-            return da;
-        }
-
-        int childCount = getChildCount();
-        boolean updated;
-
-        if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0 ? da > 0 : da < 0) {
-            prependVisibleItems();
-        } else {
-            appendVisibleItems();
-        }
-        updated = getChildCount() > childCount;
-        childCount = getChildCount();
-
-        if (TRACE) TraceCompat.beginSection("remove");
-        if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0 ? da > 0 : da < 0) {
-            removeInvisibleViewsAtEnd();
-        } else {
-            removeInvisibleViewsAtFront();
-        }
-        if (TRACE) TraceCompat.endSection();
-        updated |= getChildCount() < childCount;
-        if (updated) {
-            updateRowSecondarySizeRefresh();
-        }
-
-        mBaseGridView.invalidate();
-        updateScrollLimits();
-        if (TRACE) TraceCompat.endSection();
-        return da;
-    }
-
-    // scroll in second direction will not add/prune views
-    private int scrollDirectionSecondary(int dy) {
-        if (dy == 0) {
-            return 0;
-        }
-        offsetChildrenSecondary(-dy);
-        mScrollOffsetSecondary += dy;
-        updateSecondaryScrollLimits();
-        mBaseGridView.invalidate();
-        return dy;
-    }
-
-    @Override
-    public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        try {
-            saveContext(null, state);
-            int da = (mOrientation == HORIZONTAL) ? dx : dy;
-            if (getChildCount() == 0 || da == 0) {
-                // can't support this scroll, so don't bother prefetching
-                return;
-            }
-
-            int fromLimit = da < 0
-                    ? -mExtraLayoutSpace
-                    : mSizePrimary + mExtraLayoutSpace;
-            mGrid.collectAdjacentPrefetchPositions(fromLimit, da, layoutPrefetchRegistry);
-        } finally {
-            leaveContext();
-        }
-    }
-
-    @Override
-    public void collectInitialPrefetchPositions(int adapterItemCount,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        int numToPrefetch = mBaseGridView.mInitialPrefetchItemCount;
-        if (adapterItemCount != 0 && numToPrefetch != 0) {
-            // prefetch items centered around mFocusPosition
-            int initialPos = Math.max(0, Math.min(mFocusPosition - (numToPrefetch - 1)/ 2,
-                    adapterItemCount - numToPrefetch));
-            for (int i = initialPos; i < adapterItemCount && i < initialPos + numToPrefetch; i++) {
-                layoutPrefetchRegistry.addPosition(i, 0);
-            }
-        }
-    }
-
-    void updateScrollLimits() {
-        if (mState.getItemCount() == 0) {
-            return;
-        }
-        int highVisiblePos, lowVisiblePos;
-        int highMaxPos, lowMinPos;
-        if ((mFlag & PF_REVERSE_FLOW_PRIMARY) == 0) {
-            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 lowAvailable = lowVisiblePos == lowMinPos;
-        if (!highAvailable && mWindowAlignment.mainAxis().isMaxUnknown()
-                && !lowAvailable && mWindowAlignment.mainAxis().isMinUnknown()) {
-            return;
-        }
-        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 {
-            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);
-    }
-
-    /**
-     * 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() {
-        mWindowAlignment.reset();
-        mWindowAlignment.horizontal.setSize(getWidth());
-        mWindowAlignment.vertical.setSize(getHeight());
-        mWindowAlignment.horizontal.setPadding(getPaddingLeft(), getPaddingRight());
-        mWindowAlignment.vertical.setPadding(getPaddingTop(), getPaddingBottom());
-        mSizePrimary = mWindowAlignment.mainAxis().getSize();
-        mScrollOffsetSecondary = 0;
-
-        if (DEBUG) {
-            Log.v(getTag(), "initScrollController mSizePrimary " + mSizePrimary
-                    + " mWindowAlignment " + mWindowAlignment);
-        }
-    }
-
-    private void updateScrollController() {
-        mWindowAlignment.horizontal.setSize(getWidth());
-        mWindowAlignment.vertical.setSize(getHeight());
-        mWindowAlignment.horizontal.setPadding(getPaddingLeft(), getPaddingRight());
-        mWindowAlignment.vertical.setPadding(getPaddingTop(), getPaddingBottom());
-        mSizePrimary = mWindowAlignment.mainAxis().getSize();
-
-        if (DEBUG) {
-            Log.v(getTag(), "updateScrollController mSizePrimary " + mSizePrimary
-                    + " mWindowAlignment " + mWindowAlignment);
-        }
-    }
-
-    @Override
-    public void scrollToPosition(int position) {
-        setSelection(position, 0, false, 0);
-    }
-
-    @Override
-    public void smoothScrollToPosition(RecyclerView recyclerView, State state,
-            int position) {
-        setSelection(position, 0, true, 0);
-    }
-
-    public void setSelection(int position,
-            int primaryScrollExtra) {
-        setSelection(position, 0, false, primaryScrollExtra);
-    }
-
-    public void setSelectionSmooth(int position) {
-        setSelection(position, 0, true, 0);
-    }
-
-    public void setSelectionWithSub(int position, int subposition,
-            int primaryScrollExtra) {
-        setSelection(position, subposition, false, primaryScrollExtra);
-    }
-
-    public void setSelectionSmoothWithSub(int position, int subposition) {
-        setSelection(position, subposition, true, 0);
-    }
-
-    public int getSelection() {
-        return mFocusPosition;
-    }
-
-    public int getSubSelection() {
-        return mSubFocusPosition;
-    }
-
-    public void setSelection(int position, int subposition, boolean smooth,
-            int primaryScrollExtra) {
-        if ((mFocusPosition != position && position != NO_POSITION)
-                || subposition != mSubFocusPosition || primaryScrollExtra != mPrimaryScrollExtra) {
-            scrollToSelection(position, subposition, smooth, primaryScrollExtra);
-        }
-    }
-
-    void scrollToSelection(int position, int subposition,
-            boolean smooth, int primaryScrollExtra) {
-        if (TRACE) TraceCompat.beginSection("scrollToSelection");
-        mPrimaryScrollExtra = primaryScrollExtra;
-        View view = findViewByPosition(position);
-        // scrollToView() is based on Adapter position. Only call scrollToView() when item
-        // is still valid and no layout is requested, otherwise defer to next layout pass.
-        if (!mBaseGridView.isLayoutRequested()
-                && view != null && getAdapterPositionByView(view) == position) {
-            mFlag |= PF_IN_SELECTION;
-            scrollToView(view, smooth);
-            mFlag &= ~PF_IN_SELECTION;
-        } else {
-            mFocusPosition = position;
-            mSubFocusPosition = subposition;
-            mFocusPositionOffset = Integer.MIN_VALUE;
-            if ((mFlag & PF_LAYOUT_ENABLED) == 0 || (mFlag & PF_SLIDING) != 0) {
-                return;
-            }
-            if (smooth) {
-                if (!hasDoneFirstLayout()) {
-                    Log.w(getTag(), "setSelectionSmooth should "
-                            + "not be called before first layout pass");
-                    return;
-                }
-                position = startPositionSmoothScroller(position);
-                if (position != mFocusPosition) {
-                    // gets cropped by adapter size
-                    mFocusPosition = position;
-                    mSubFocusPosition = 0;
-                }
-            } else {
-                mFlag |= PF_FORCE_FULL_LAYOUT;
-                requestLayout();
-            }
-        }
-        if (TRACE) TraceCompat.endSection();
-    }
-
-    int startPositionSmoothScroller(int position) {
-        LinearSmoothScroller linearSmoothScroller = new GridLinearSmoothScroller() {
-            @Override
-            public PointF computeScrollVectorForPosition(int targetPosition) {
-                if (getChildCount() == 0) {
-                    return null;
-                }
-                final int firstChildPos = getPosition(getChildAt(0));
-                // TODO We should be able to deduce direction from bounds of current and target
-                // focus, rather than making assumptions about positions and directionality
-                final boolean isStart = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
-                        ? targetPosition > firstChildPos
-                        : targetPosition < firstChildPos;
-                final int direction = isStart ? -1 : 1;
-                if (mOrientation == HORIZONTAL) {
-                    return new PointF(direction, 0);
-                } else {
-                    return new PointF(0, direction);
-                }
-            }
-
-        };
-        linearSmoothScroller.setTargetPosition(position);
-        startSmoothScroll(linearSmoothScroller);
-        return linearSmoothScroller.getTargetPosition();
-    }
-
-    private void processPendingMovement(boolean forward) {
-        if (forward ? hasCreatedLastItem() : hasCreatedFirstItem()) {
-            return;
-        }
-        if (mPendingMoveSmoothScroller == null) {
-            // Stop existing scroller and create a new PendingMoveSmoothScroller.
-            mBaseGridView.stopScroll();
-            PendingMoveSmoothScroller linearSmoothScroller = new PendingMoveSmoothScroller(
-                    forward ? 1 : -1, mNumRows > 1);
-            mFocusPositionOffset = 0;
-            startSmoothScroll(linearSmoothScroller);
-            if (linearSmoothScroller.isRunning()) {
-                mPendingMoveSmoothScroller = linearSmoothScroller;
-            }
-        } else {
-            if (forward) {
-                mPendingMoveSmoothScroller.increasePendingMoves();
-            } else {
-                mPendingMoveSmoothScroller.decreasePendingMoves();
-            }
-        }
-    }
-
-    @Override
-    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
-        if (DEBUG) Log.v(getTag(), "onItemsAdded positionStart "
-                + positionStart + " itemCount " + itemCount);
-        if (mFocusPosition != NO_POSITION && mGrid != null && mGrid.getFirstVisibleIndex() >= 0
-                && mFocusPositionOffset != Integer.MIN_VALUE) {
-            int pos = mFocusPosition + mFocusPositionOffset;
-            if (positionStart <= pos) {
-                mFocusPositionOffset += itemCount;
-            }
-        }
-        mChildrenStates.clear();
-    }
-
-    @Override
-    public void onItemsChanged(RecyclerView recyclerView) {
-        if (DEBUG) Log.v(getTag(), "onItemsChanged");
-        mFocusPositionOffset = 0;
-        mChildrenStates.clear();
-    }
-
-    @Override
-    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
-        if (DEBUG) Log.v(getTag(), "onItemsRemoved positionStart "
-                + positionStart + " itemCount " + itemCount);
-        if (mFocusPosition != NO_POSITION  && mGrid != null && mGrid.getFirstVisibleIndex() >= 0
-                && mFocusPositionOffset != Integer.MIN_VALUE) {
-            int pos = mFocusPosition + mFocusPositionOffset;
-            if (positionStart <= pos) {
-                if (positionStart + itemCount > pos) {
-                    // stop updating offset after the focus item was removed
-                    mFocusPositionOffset += positionStart - pos;
-                    mFocusPosition += mFocusPositionOffset;
-                    mFocusPositionOffset = Integer.MIN_VALUE;
-                } else {
-                    mFocusPositionOffset -= itemCount;
-                }
-            }
-        }
-        mChildrenStates.clear();
-    }
-
-    @Override
-    public void onItemsMoved(RecyclerView recyclerView, int fromPosition, int toPosition,
-            int itemCount) {
-        if (DEBUG) Log.v(getTag(), "onItemsMoved fromPosition "
-                + fromPosition + " toPosition " + toPosition);
-        if (mFocusPosition != NO_POSITION && mFocusPositionOffset != Integer.MIN_VALUE) {
-            int pos = mFocusPosition + mFocusPositionOffset;
-            if (fromPosition <= pos && pos < fromPosition + itemCount) {
-                // moved items include focused position
-                mFocusPositionOffset += toPosition - fromPosition;
-            } else if (fromPosition < pos && toPosition > pos - itemCount) {
-                // move items before focus position to after focused position
-                mFocusPositionOffset -= itemCount;
-            } else if (fromPosition > pos && toPosition < pos) {
-                // move items after focus position to before focused position
-                mFocusPositionOffset += itemCount;
-            }
-        }
-        mChildrenStates.clear();
-    }
-
-    @Override
-    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
-        if (DEBUG) Log.v(getTag(), "onItemsUpdated positionStart "
-                + positionStart + " itemCount " + itemCount);
-        for (int i = positionStart, end = positionStart + itemCount; i < end; i++) {
-            mChildrenStates.remove(i);
-        }
-    }
-
-    @Override
-    public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
-        if ((mFlag & PF_FOCUS_SEARCH_DISABLED) != 0) {
-            return true;
-        }
-        if (getAdapterPositionByView(child) == NO_POSITION) {
-            // This is could be the last view in DISAPPEARING animation.
-            return true;
-        }
-        if ((mFlag & (PF_STAGE_MASK | PF_IN_SELECTION)) == 0) {
-            scrollToView(child, focused, true);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(RecyclerView parent, View view, Rect rect,
-            boolean immediate) {
-        if (DEBUG) Log.v(getTag(), "requestChildRectangleOnScreen " + view + " " + rect);
-        return false;
-    }
-
-    public void getViewSelectedOffsets(View view, int[] offsets) {
-        if (mOrientation == HORIZONTAL) {
-            offsets[0] = getPrimaryAlignedScrollDistance(view);
-            offsets[1] = getSecondaryScrollDistance(view);
-        } else {
-            offsets[1] = getPrimaryAlignedScrollDistance(view);
-            offsets[0] = getSecondaryScrollDistance(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 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();
-            scrollPrimary += lp.getAlignMultiple()[subindex] - lp.getAlignMultiple()[0];
-        }
-        return scrollPrimary;
-    }
-
-    private int getSecondaryScrollDistance(View view) {
-        int viewCenterSecondary = getViewCenterSecondary(view);
-        return mWindowAlignment.secondAxis().getScroll(viewCenterSecondary);
-    }
-
-    /**
-     * Scroll to a given child view and change mFocusPosition. Ignored when in slideOut() state.
-     */
-    void scrollToView(View view, boolean smooth) {
-        scrollToView(view, view == null ? null : view.findFocus(), smooth);
-    }
-
-    void scrollToView(View view, boolean smooth, int extraDelta, int extraDeltaSecondary) {
-        scrollToView(view, view == null ? null : view.findFocus(), smooth, extraDelta,
-                extraDeltaSecondary);
-    }
-
-    private void scrollToView(View view, View childView, boolean smooth) {
-        scrollToView(view, childView, smooth, 0, 0);
-    }
-    /**
-     * Scroll to a given child view and change mFocusPosition. Ignored when in slideOut() state.
-     */
-    private void scrollToView(View view, View childView, boolean smooth, int extraDelta,
-            int extraDeltaSecondary) {
-        if ((mFlag & PF_SLIDING) != 0) {
-            return;
-        }
-        int newFocusPosition = getAdapterPositionByView(view);
-        int newSubFocusPosition = getSubPositionByView(view, childView);
-        if (newFocusPosition != mFocusPosition || newSubFocusPosition != mSubFocusPosition) {
-            mFocusPosition = newFocusPosition;
-            mSubFocusPosition = newSubFocusPosition;
-            mFocusPositionOffset = 0;
-            if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT) {
-                dispatchChildSelected();
-            }
-            if (mBaseGridView.isChildrenDrawingOrderEnabledInternal()) {
-                mBaseGridView.invalidate();
-            }
-        }
-        if (view == null) {
-            return;
-        }
-        if (!view.hasFocus() && mBaseGridView.hasFocus()) {
-            // transfer focus to the child if it does not have focus yet (e.g. triggered
-            // by setSelection())
-            view.requestFocus();
-        }
-        if ((mFlag & PF_SCROLL_ENABLED) == 0 && smooth) {
-            return;
-        }
-        if (getScrollPosition(view, childView, sTwoInts)
-                || extraDelta != 0 || extraDeltaSecondary != 0) {
-            scrollGrid(sTwoInts[0] + extraDelta, sTwoInts[1] + extraDeltaSecondary, smooth);
-        }
-    }
-
-    boolean getScrollPosition(View view, View childView, int[] deltas) {
-        switch (mFocusScrollStrategy) {
-            case BaseGridView.FOCUS_SCROLL_ALIGNED:
-            default:
-                return getAlignedPosition(view, childView, deltas);
-            case BaseGridView.FOCUS_SCROLL_ITEM:
-            case BaseGridView.FOCUS_SCROLL_PAGE:
-                return getNoneAlignedPosition(view, deltas);
-        }
-    }
-
-    private boolean getNoneAlignedPosition(View view, int[] deltas) {
-        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 paddingMin = mWindowAlignment.mainAxis().getPaddingMin();
-        int clientSize = mWindowAlignment.mainAxis().getClientSize();
-        final int row = mGrid.getRowIndex(pos);
-        if (viewMin < paddingMin) {
-            // view enters low padding area:
-            firstView = view;
-            if (mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_PAGE) {
-                // scroll one "page" left/top,
-                // align first visible item of the "page" at the low padding edge.
-                while (prependOneColumnVisibleItems()) {
-                    CircularIntArray positions =
-                            mGrid.getItemPositionsInRows(mGrid.getFirstVisibleIndex(), pos)[row];
-                    firstView = findViewByPosition(positions.get(0));
-                    if (viewMax - getViewMin(firstView) > clientSize) {
-                        if (positions.size() > 2) {
-                            firstView = findViewByPosition(positions.get(2));
-                        }
-                        break;
-                    }
-                }
-            }
-        } 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.
-                firstView = view;
-                do {
-                    CircularIntArray positions =
-                            mGrid.getItemPositionsInRows(pos, mGrid.getLastVisibleIndex())[row];
-                    lastView = findViewByPosition(positions.get(positions.size() - 1));
-                    if (getViewMax(lastView) - viewMin > clientSize) {
-                        lastView = null;
-                        break;
-                    }
-                } while (appendOneColumnVisibleItems());
-                if (lastView != null) {
-                    // however if we reached end,  we should align last view.
-                    firstView = null;
-                }
-            } else {
-                lastView = view;
-            }
-        }
-        int scrollPrimary = 0;
-        int scrollSecondary = 0;
-        if (firstView != null) {
-            scrollPrimary = getViewMin(firstView) - paddingMin;
-        } else if (lastView != null) {
-            scrollPrimary = getViewMax(lastView) - (paddingMin + clientSize);
-        }
-        View secondaryAlignedView;
-        if (firstView != null) {
-            secondaryAlignedView = firstView;
-        } else if (lastView != null) {
-            secondaryAlignedView = lastView;
-        } else {
-            secondaryAlignedView = view;
-        }
-        scrollSecondary = getSecondaryScrollDistance(secondaryAlignedView);
-        if (scrollPrimary != 0 || scrollSecondary != 0) {
-            deltas[0] = scrollPrimary;
-            deltas[1] = scrollSecondary;
-            return true;
-        }
-        return false;
-    }
-
-    private boolean getAlignedPosition(View view, View childView, int[] deltas) {
-        int scrollPrimary = getPrimaryAlignedScrollDistance(view);
-        if (childView != null) {
-            scrollPrimary = getAdjustedPrimaryAlignedScrollDistance(scrollPrimary, view, childView);
-        }
-        int scrollSecondary = getSecondaryScrollDistance(view);
-        if (DEBUG) {
-            Log.v(getTag(), "getAlignedPosition " + scrollPrimary + " " + scrollSecondary
-                    + " " + mPrimaryScrollExtra + " " + mWindowAlignment);
-        }
-        scrollPrimary += mPrimaryScrollExtra;
-        if (scrollPrimary != 0 || scrollSecondary != 0) {
-            deltas[0] = scrollPrimary;
-            deltas[1] = scrollSecondary;
-            return true;
-        } else {
-            deltas[0] = 0;
-            deltas[1] = 0;
-        }
-        return false;
-    }
-
-    private void scrollGrid(int scrollPrimary, int scrollSecondary, boolean smooth) {
-        if ((mFlag & PF_STAGE_MASK) == PF_STAGE_LAYOUT) {
-            scrollDirectionPrimary(scrollPrimary);
-            scrollDirectionSecondary(scrollSecondary);
-        } else {
-            int scrollX;
-            int scrollY;
-            if (mOrientation == HORIZONTAL) {
-                scrollX = scrollPrimary;
-                scrollY = scrollSecondary;
-            } else {
-                scrollX = scrollSecondary;
-                scrollY = scrollPrimary;
-            }
-            if (smooth) {
-                mBaseGridView.smoothScrollBy(scrollX, scrollY);
-            } else {
-                mBaseGridView.scrollBy(scrollX, scrollY);
-                dispatchChildSelectedAndPositioned();
-            }
-        }
-    }
-
-    public void setPruneChild(boolean pruneChild) {
-        if (((mFlag & PF_PRUNE_CHILD) != 0) != pruneChild) {
-            mFlag = (mFlag & ~PF_PRUNE_CHILD) | (pruneChild ? PF_PRUNE_CHILD : 0);
-            if (pruneChild) {
-                requestLayout();
-            }
-        }
-    }
-
-    public boolean getPruneChild() {
-        return (mFlag & PF_PRUNE_CHILD) != 0;
-    }
-
-    public void setScrollEnabled(boolean scrollEnabled) {
-        if (((mFlag & PF_SCROLL_ENABLED) != 0) != scrollEnabled) {
-            mFlag = (mFlag & ~PF_SCROLL_ENABLED) | (scrollEnabled ? PF_SCROLL_ENABLED : 0);
-            if (((mFlag & PF_SCROLL_ENABLED) != 0)
-                    && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED
-                    && mFocusPosition != NO_POSITION) {
-                scrollToSelection(mFocusPosition, mSubFocusPosition,
-                        true, mPrimaryScrollExtra);
-            }
-        }
-    }
-
-    public boolean isScrollEnabled() {
-        return (mFlag & PF_SCROLL_ENABLED) != 0;
-    }
-
-    private int findImmediateChildIndex(View view) {
-        if (mBaseGridView != null && view != mBaseGridView) {
-            view = findContainingItemView(view);
-            if (view != null) {
-                for (int i = 0, count = getChildCount(); i < count; i++) {
-                    if (getChildAt(i) == view) {
-                        return i;
-                    }
-                }
-            }
-        }
-        return NO_POSITION;
-    }
-
-    void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        if (gainFocus) {
-            // if gridview.requestFocus() is called, select first focusable child.
-            for (int i = mFocusPosition; ;i++) {
-                View view = findViewByPosition(i);
-                if (view == null) {
-                    break;
-                }
-                if (view.getVisibility() == View.VISIBLE && view.hasFocusable()) {
-                    view.requestFocus();
-                    break;
-                }
-            }
-        }
-    }
-
-    void setFocusSearchDisabled(boolean disabled) {
-        mFlag = (mFlag & ~PF_FOCUS_SEARCH_DISABLED) | (disabled ? PF_FOCUS_SEARCH_DISABLED : 0);
-    }
-
-    boolean isFocusSearchDisabled() {
-        return (mFlag & PF_FOCUS_SEARCH_DISABLED) != 0;
-    }
-
-    @Override
-    public View onInterceptFocusSearch(View focused, int direction) {
-        if ((mFlag & PF_FOCUS_SEARCH_DISABLED) != 0) {
-            return focused;
-        }
-
-        final FocusFinder ff = FocusFinder.getInstance();
-        View result = null;
-        if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
-            // convert direction to absolute direction and see if we have a view there and if not
-            // tell LayoutManager to add if it can.
-            if (canScrollVertically()) {
-                final int absDir =
-                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
-                result = ff.findNextFocus(mBaseGridView, focused, absDir);
-            }
-            if (canScrollHorizontally()) {
-                boolean rtl = getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
-                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
-                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
-                result = ff.findNextFocus(mBaseGridView, focused, absDir);
-            }
-        } else {
-            result = ff.findNextFocus(mBaseGridView, focused, direction);
-        }
-        if (result != null) {
-            return result;
-        }
-
-        if (mBaseGridView.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
-            return mBaseGridView.getParent().focusSearch(focused, direction);
-        }
-
-        if (DEBUG) Log.v(getTag(), "regular focusSearch failed direction " + direction);
-        int movement = getMovement(direction);
-        final boolean isScroll = mBaseGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
-        if (movement == NEXT_ITEM) {
-            if (isScroll || (mFlag & PF_FOCUS_OUT_END) == 0) {
-                result = focused;
-            }
-            if ((mFlag & PF_SCROLL_ENABLED) != 0 && !hasCreatedLastItem()) {
-                processPendingMovement(true);
-                result = focused;
-            }
-        } else if (movement == PREV_ITEM) {
-            if (isScroll || (mFlag & PF_FOCUS_OUT_FRONT) == 0) {
-                result = focused;
-            }
-            if ((mFlag & PF_SCROLL_ENABLED) != 0 && !hasCreatedFirstItem()) {
-                processPendingMovement(false);
-                result = focused;
-            }
-        } else if (movement == NEXT_ROW) {
-            if (isScroll || (mFlag & PF_FOCUS_OUT_SIDE_END) == 0) {
-                result = focused;
-            }
-        } else if (movement == PREV_ROW) {
-            if (isScroll || (mFlag & PF_FOCUS_OUT_SIDE_START) == 0) {
-                result = focused;
-            }
-        }
-        if (result != null) {
-            return result;
-        }
-
-        if (DEBUG) Log.v(getTag(), "now focusSearch in parent");
-        result = mBaseGridView.getParent().focusSearch(focused, direction);
-        if (result != null) {
-            return result;
-        }
-        return focused != null ? focused : mBaseGridView;
-    }
-
-    boolean hasPreviousViewInSameRow(int pos) {
-        if (mGrid == null || pos == NO_POSITION || mGrid.getFirstVisibleIndex() < 0) {
-            return false;
-        }
-        if (mGrid.getFirstVisibleIndex() > 0) {
-            return true;
-        }
-        final int focusedRow = mGrid.getLocation(pos).row;
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            int position = getAdapterPositionByIndex(i);
-            Grid.Location loc = mGrid.getLocation(position);
-            if (loc != null && loc.row == focusedRow) {
-                if (position < pos) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onAddFocusables(RecyclerView recyclerView,
-            ArrayList<View> views, int direction, int focusableMode) {
-        if ((mFlag & PF_FOCUS_SEARCH_DISABLED) != 0) {
-            return true;
-        }
-        // If this viewgroup or one of its children currently has focus then we
-        // consider our children for focus searching in main direction on the same row.
-        // If this viewgroup has no focus and using focus align, we want the system
-        // to ignore our children and pass focus to the viewgroup, which will pass
-        // focus on to its children appropriately.
-        // If this viewgroup has no focus and not using focus align, we want to
-        // consider the child that does not overlap with padding area.
-        if (recyclerView.hasFocus()) {
-            if (mPendingMoveSmoothScroller != null) {
-                // don't find next focusable if has pending movement.
-                return true;
-            }
-            final int movement = getMovement(direction);
-            final View focused = recyclerView.findFocus();
-            final int focusedIndex = findImmediateChildIndex(focused);
-            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 (immediateFocusedChild != null) {
-                immediateFocusedChild.addFocusables(views,  direction, focusableMode);
-            }
-            if (mGrid == null || getChildCount() == 0) {
-                // no grid information, or no child, bail out.
-                return true;
-            }
-            if ((movement == NEXT_ROW || movement == PREV_ROW) && mGrid.getNumRows() <= 1) {
-                // For single row, cannot navigate to previous/next row.
-                return true;
-            }
-            // Add focusables of neighbor depending on the focus search direction.
-            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;
-            int loop_end = inc > 0 ? getChildCount() - 1 : 0;
-            int loop_start;
-            if (focusedIndex == NO_POSITION) {
-                loop_start = inc > 0 ? 0 : getChildCount() - 1;
-            } else {
-                loop_start = focusedIndex + inc;
-            }
-            for (int i = loop_start; inc > 0 ? i <= loop_end : i >= loop_end; i += inc) {
-                final View child = getChildAt(i);
-                if (child.getVisibility() != View.VISIBLE || !child.hasFocusable()) {
-                    continue;
-                }
-                // if there wasn't any focused item, add the very first focusable
-                // items and stop.
-                if (immediateFocusedChild == null) {
-                    child.addFocusables(views,  direction, focusableMode);
-                    if (views.size() > focusableCount) {
-                        break;
-                    }
-                    continue;
-                }
-                int position = getAdapterPositionByIndex(i);
-                Grid.Location loc = mGrid.getLocation(position);
-                if (loc == null) {
-                    continue;
-                }
-                if (movement == NEXT_ITEM) {
-                    // Add first focusable item on the same row
-                    if (loc.row == focusedRow && position > focusedPos) {
-                        child.addFocusables(views,  direction, focusableMode);
-                        if (views.size() > focusableCount) {
-                            break;
-                        }
-                    }
-                } else if (movement == PREV_ITEM) {
-                    // Add first focusable item on the same row
-                    if (loc.row == focusedRow && position < focusedPos) {
-                        child.addFocusables(views,  direction, focusableMode);
-                        if (views.size() > focusableCount) {
-                            break;
-                        }
-                    }
-                } else if (movement == NEXT_ROW) {
-                    // Add all focusable items after this item whose row index is bigger
-                    if (loc.row == focusedRow) {
-                        continue;
-                    } else if (loc.row < focusedRow) {
-                        break;
-                    }
-                    child.addFocusables(views,  direction, focusableMode);
-                } else if (movement == PREV_ROW) {
-                    // Add all focusable items before this item whose row index is smaller
-                    if (loc.row == focusedRow) {
-                        continue;
-                    } else if (loc.row > focusedRow) {
-                        break;
-                    }
-                    child.addFocusables(views,  direction, focusableMode);
-                }
-            }
-        } else {
-            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().getPaddingMin();
-                int right = mWindowAlignment.mainAxis().getClientSize() + left;
-                for (int i = 0, count = getChildCount(); i < count; i++) {
-                    View child = getChildAt(i);
-                    if (child.getVisibility() == View.VISIBLE) {
-                        if (getViewMin(child) >= left && getViewMax(child) <= right) {
-                            child.addFocusables(views, direction, focusableMode);
-                        }
-                    }
-                }
-                // if we cannot find any, then just add all children.
-                if (views.size() == focusableCount) {
-                    for (int i = 0, count = getChildCount(); i < count; i++) {
-                        View child = getChildAt(i);
-                        if (child.getVisibility() == View.VISIBLE) {
-                            child.addFocusables(views, direction, focusableMode);
-                        }
-                    }
-                }
-            } else {
-                View view = findViewByPosition(mFocusPosition);
-                if (view != null) {
-                    view.addFocusables(views, direction, focusableMode);
-                }
-            }
-            // if still cannot find any, fall through and add itself
-            if (views.size() != focusableCount) {
-                return true;
-            }
-            if (recyclerView.isFocusable()) {
-                views.add(recyclerView);
-            }
-        }
-        return true;
-    }
-
-    boolean hasCreatedLastItem() {
-        int count = getItemCount();
-        return count == 0 || mBaseGridView.findViewHolderForAdapterPosition(count - 1) != null;
-    }
-
-    boolean hasCreatedFirstItem() {
-        int count = getItemCount();
-        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());
-    }
-
-    boolean gridOnRequestFocusInDescendants(RecyclerView recyclerView, int direction,
-            Rect previouslyFocusedRect) {
-        switch (mFocusScrollStrategy) {
-            case BaseGridView.FOCUS_SCROLL_ALIGNED:
-            default:
-                return gridOnRequestFocusInDescendantsAligned(recyclerView,
-                        direction, previouslyFocusedRect);
-            case BaseGridView.FOCUS_SCROLL_PAGE:
-            case BaseGridView.FOCUS_SCROLL_ITEM:
-                return gridOnRequestFocusInDescendantsUnaligned(recyclerView,
-                        direction, previouslyFocusedRect);
-        }
-    }
-
-    private boolean gridOnRequestFocusInDescendantsAligned(RecyclerView recyclerView,
-            int direction, Rect previouslyFocusedRect) {
-        View view = findViewByPosition(mFocusPosition);
-        if (view != null) {
-            boolean result = view.requestFocus(direction, previouslyFocusedRect);
-            if (!result && DEBUG) {
-                Log.w(getTag(), "failed to request focus on " + view);
-            }
-            return result;
-        }
-        return false;
-    }
-
-    private boolean gridOnRequestFocusInDescendantsUnaligned(RecyclerView recyclerView,
-            int direction, Rect previouslyFocusedRect) {
-        // focus to view not overlapping padding area to avoid scrolling in gaining focus
-        int index;
-        int increment;
-        int end;
-        int count = getChildCount();
-        if ((direction & View.FOCUS_FORWARD) != 0) {
-            index = 0;
-            increment = 1;
-            end = count;
-        } else {
-            index = count - 1;
-            increment = -1;
-            end = -1;
-        }
-        int left = mWindowAlignment.mainAxis().getPaddingMin();
-        int right = mWindowAlignment.mainAxis().getClientSize() + left;
-        for (int i = index; i != end; i += increment) {
-            View child = getChildAt(i);
-            if (child.getVisibility() == View.VISIBLE) {
-                if (getViewMin(child) >= left && getViewMax(child) <= right) {
-                    if (child.requestFocus(direction, previouslyFocusedRect)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    private final static int PREV_ITEM = 0;
-    private final static int NEXT_ITEM = 1;
-    private final static int PREV_ROW = 2;
-    private final static int NEXT_ROW = 3;
-
-    private int getMovement(int direction) {
-        int movement = View.FOCUS_LEFT;
-
-        if (mOrientation == HORIZONTAL) {
-            switch(direction) {
-                case View.FOCUS_LEFT:
-                    movement = (mFlag & PF_REVERSE_FLOW_PRIMARY) == 0 ? PREV_ITEM : NEXT_ITEM;
-                    break;
-                case View.FOCUS_RIGHT:
-                    movement = (mFlag & PF_REVERSE_FLOW_PRIMARY) == 0 ? NEXT_ITEM : PREV_ITEM;
-                    break;
-                case View.FOCUS_UP:
-                    movement = PREV_ROW;
-                    break;
-                case View.FOCUS_DOWN:
-                    movement = NEXT_ROW;
-                    break;
-            }
-        } else if (mOrientation == VERTICAL) {
-            switch(direction) {
-                case View.FOCUS_LEFT:
-                    movement = (mFlag & PF_REVERSE_FLOW_SECONDARY) == 0 ? PREV_ROW : NEXT_ROW;
-                    break;
-                case View.FOCUS_RIGHT:
-                    movement = (mFlag & PF_REVERSE_FLOW_SECONDARY) == 0 ? NEXT_ROW : PREV_ROW;
-                    break;
-                case View.FOCUS_UP:
-                    movement = PREV_ITEM;
-                    break;
-                case View.FOCUS_DOWN:
-                    movement = NEXT_ITEM;
-                    break;
-            }
-        }
-
-        return movement;
-    }
-
-    int getChildDrawingOrder(RecyclerView recyclerView, int childCount, int i) {
-        View view = findViewByPosition(mFocusPosition);
-        if (view == null) {
-            return i;
-        }
-        int focusIndex = recyclerView.indexOfChild(view);
-        // supposely 0 1 2 3 4 5 6 7 8 9, 4 is the center item
-        // drawing order is 0 1 2 3 9 8 7 6 5 4
-        if (i < focusIndex) {
-            return i;
-        } else if (i < childCount - 1) {
-            return focusIndex + childCount - 1 - i;
-        } else {
-            return focusIndex;
-        }
-    }
-
-    @Override
-    public void onAdapterChanged(RecyclerView.Adapter oldAdapter,
-            RecyclerView.Adapter newAdapter) {
-        if (DEBUG) Log.v(getTag(), "onAdapterChanged to " + newAdapter);
-        if (oldAdapter != null) {
-            discardLayoutInfo();
-            mFocusPosition = NO_POSITION;
-            mFocusPositionOffset = 0;
-            mChildrenStates.clear();
-        }
-        if (newAdapter instanceof FacetProviderAdapter) {
-            mFacetProviderAdapter = (FacetProviderAdapter) newAdapter;
-        } else {
-            mFacetProviderAdapter = null;
-        }
-        super.onAdapterChanged(oldAdapter, newAdapter);
-    }
-
-    private void discardLayoutInfo() {
-        mGrid = null;
-        mRowSizeSecondary = null;
-        mFlag &= ~PF_ROW_SECONDARY_SIZE_REFRESH;
-    }
-
-    public void setLayoutEnabled(boolean layoutEnabled) {
-        if (((mFlag & PF_LAYOUT_ENABLED) != 0) != layoutEnabled) {
-            mFlag = (mFlag & ~PF_LAYOUT_ENABLED) | (layoutEnabled ? PF_LAYOUT_ENABLED : 0);
-            requestLayout();
-        }
-    }
-
-    void setChildrenVisibility(int visibility) {
-        mChildVisibility = visibility;
-        if (mChildVisibility != -1) {
-            int count = getChildCount();
-            for (int i= 0; i < count; i++) {
-                getChildAt(i).setVisibility(mChildVisibility);
-            }
-        }
-    }
-
-    final static class SavedState implements Parcelable {
-
-        int index; // index inside adapter of the current view
-        Bundle childStates = Bundle.EMPTY;
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(index);
-            out.writeBundle(childStates);
-        }
-
-        @SuppressWarnings("hiding")
-        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 int describeContents() {
-            return 0;
-        }
-
-        SavedState(Parcel in) {
-            index = in.readInt();
-            childStates = in.readBundle(GridLayoutManager.class.getClassLoader());
-        }
-
-        SavedState() {
-        }
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        if (DEBUG) Log.v(getTag(), "onSaveInstanceState getSelection() " + getSelection());
-        SavedState ss = new SavedState();
-        // save selected index
-        ss.index = getSelection();
-        // save offscreen child (state when they are recycled)
-        Bundle bundle = mChildrenStates.saveAsBundle();
-        // 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 = getAdapterPositionByView(view);
-            if (position != NO_POSITION) {
-                bundle = mChildrenStates.saveOnScreenView(bundle, view, position);
-            }
-        }
-        ss.childStates = bundle;
-        return ss;
-    }
-
-    void onChildRecycled(RecyclerView.ViewHolder holder) {
-        final int position = holder.getAdapterPosition();
-        if (position != NO_POSITION) {
-            mChildrenStates.saveOffscreenView(holder.itemView, position);
-        }
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            return;
-        }
-        SavedState loadingState = (SavedState)state;
-        mFocusPosition = loadingState.index;
-        mFocusPositionOffset = 0;
-        mChildrenStates.loadFromBundle(loadingState.childStates);
-        mFlag |= PF_FORCE_FULL_LAYOUT;
-        requestLayout();
-        if (DEBUG) Log.v(getTag(), "onRestoreInstanceState mFocusPosition " + mFocusPosition);
-    }
-
-    @Override
-    public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == HORIZONTAL && mGrid != null) {
-            return mGrid.getNumRows();
-        }
-        return super.getRowCountForAccessibility(recycler, state);
-    }
-
-    @Override
-    public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == VERTICAL && mGrid != null) {
-            return mGrid.getNumRows();
-        }
-        return super.getColumnCountForAccessibility(recycler, state);
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
-            RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
-        ViewGroup.LayoutParams lp = host.getLayoutParams();
-        if (mGrid == null || !(lp instanceof LayoutParams)) {
-            return;
-        }
-        LayoutParams glp = (LayoutParams) lp;
-        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(
-                    rowIndex, 1, guessSpanIndex, 1, false, false));
-        } else {
-            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-                    guessSpanIndex, 1, rowIndex, 1, false, false));
-        }
-    }
-
-    /*
-     * Leanback widget is different than the default implementation because the "scroll" is driven
-     * by selection change.
-     */
-    @Override
-    public boolean performAccessibilityAction(Recycler recycler, State state, int action,
-            Bundle args) {
-        saveContext(recycler, state);
-        int translatedAction = action;
-        boolean reverseFlowPrimary = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0;
-        if (Build.VERSION.SDK_INT >= 23) {
-            if (mOrientation == HORIZONTAL) {
-                if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                        .ACTION_SCROLL_LEFT.getId()) {
-                    translatedAction = reverseFlowPrimary
-                            ? AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD :
-                            AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD;
-                } else if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                        .ACTION_SCROLL_RIGHT.getId()) {
-                    translatedAction = reverseFlowPrimary
-                            ? AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD :
-                            AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD;
-                }
-            } else { // VERTICAL layout
-                if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP
-                        .getId()) {
-                    translatedAction = AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD;
-                } else if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                        .ACTION_SCROLL_DOWN.getId()) {
-                    translatedAction = AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD;
-                }
-            }
-        }
-        switch (translatedAction) {
-            case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
-                processSelectionMoves(false, -1);
-                break;
-            case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
-                processSelectionMoves(false, 1);
-                break;
-        }
-        leaveContext();
-        return true;
-    }
-
-    /*
-     * Move mFocusPosition multiple steps on the same row in main direction.
-     * Stops when moves are all consumed or reach first/last visible item.
-     * Returning remaining moves.
-     */
-    int processSelectionMoves(boolean preventScroll, int moves) {
-        if (mGrid == null) {
-            return moves;
-        }
-        int focusPosition = mFocusPosition;
-        int focusedRow = focusPosition != NO_POSITION
-                ? mGrid.getRowIndex(focusPosition) : NO_POSITION;
-        View newSelected = null;
-        for (int i = 0, count = getChildCount(); i < count && moves != 0; i++) {
-            int index = moves > 0 ? i : count - 1 - i;
-            final View child = getChildAt(index);
-            if (!canScrollTo(child)) {
-                continue;
-            }
-            int position = getAdapterPositionByIndex(index);
-            int rowIndex = mGrid.getRowIndex(position);
-            if (focusedRow == NO_POSITION) {
-                focusPosition = position;
-                newSelected = child;
-                focusedRow = rowIndex;
-            } else if (rowIndex == focusedRow) {
-                if ((moves > 0 && position > focusPosition)
-                        || (moves < 0 && position < focusPosition)) {
-                    focusPosition = position;
-                    newSelected = child;
-                    if (moves > 0) {
-                        moves--;
-                    } else {
-                        moves++;
-                    }
-                }
-            }
-        }
-        if (newSelected != null) {
-            if (preventScroll) {
-                if (hasFocus()) {
-                    mFlag |= PF_IN_SELECTION;
-                    newSelected.requestFocus();
-                    mFlag &= ~PF_IN_SELECTION;
-                }
-                mFocusPosition = focusPosition;
-                mSubFocusPosition = 0;
-            } else {
-                scrollToView(newSelected, true);
-            }
-        }
-        return moves;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
-            AccessibilityNodeInfoCompat info) {
-        saveContext(recycler, state);
-        int count = state.getItemCount();
-        boolean reverseFlowPrimary = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0;
-        if (count > 1 && !isItemFullyVisible(0)) {
-            if (Build.VERSION.SDK_INT >= 23) {
-                if (mOrientation == HORIZONTAL) {
-                    info.addAction(reverseFlowPrimary
-                            ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_RIGHT :
-                            AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_LEFT);
-                } else {
-                    info.addAction(
-                            AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP);
-                }
-            } else {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
-            }
-            info.setScrollable(true);
-        }
-        if (count > 1 && !isItemFullyVisible(count - 1)) {
-            if (Build.VERSION.SDK_INT >= 23) {
-                if (mOrientation == HORIZONTAL) {
-                    info.addAction(reverseFlowPrimary
-                            ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_LEFT :
-                            AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_RIGHT);
-                } else {
-                    info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat
-                                    .ACTION_SCROLL_DOWN);
-                }
-            } else {
-                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));
-        info.setCollectionInfo(collectionInfo);
-        leaveContext();
-    }
-}
diff --git a/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java b/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
deleted file mode 100644
index e3de87c..0000000
--- a/android/support/v17/leanback/widget/GuidanceStylingRelativeLayout.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-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;
-
-/**
- * Relative layout implementation that lays out child views based on provided keyline percent(
- * distance of TitleView baseline from the top).
- *
- * Repositioning child views in PreDraw callback in {@link GuidanceStylist} was interfering with
- * fragment transition. To avoid that, we do that in the onLayout pass.
- */
-class GuidanceStylingRelativeLayout extends RelativeLayout {
-    private float mTitleKeylinePercent;
-
-    public GuidanceStylingRelativeLayout(Context context) {
-        this(context, null);
-    }
-
-    public GuidanceStylingRelativeLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public GuidanceStylingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mTitleKeylinePercent = getKeyLinePercent(context);
-    }
-
-    public static float getKeyLinePercent(Context context) {
-        TypedArray ta = context.getTheme().obtainStyledAttributes(
-                R.styleable.LeanbackGuidedStepTheme);
-        float percent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline, 40);
-        ta.recycle();
-        return percent;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        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 = getRootView().findViewById(R.id.guidance_icon);
-        int mTitleKeylinePixels = (int) (getMeasuredHeight() * mTitleKeylinePercent / 100);
-
-        if (mTitleView != null && mTitleView.getParent() == this) {
-            int titleViewBaseline = mTitleView.getBaseline();
-            int mBreadcrumbViewHeight = mBreadcrumbView.getMeasuredHeight();
-            int guidanceTextContainerTop = mTitleKeylinePixels
-                    - titleViewBaseline - mBreadcrumbViewHeight - mTitleView.getPaddingTop();
-            int offset = guidanceTextContainerTop - mBreadcrumbView.getTop();
-
-            if (mBreadcrumbView != null && mBreadcrumbView.getParent() == this) {
-                mBreadcrumbView.offsetTopAndBottom(offset);
-            }
-
-            mTitleView.offsetTopAndBottom(offset);
-
-            if (mDescriptionView != null && mDescriptionView.getParent() == this) {
-                mDescriptionView.offsetTopAndBottom(offset);
-            }
-        }
-
-        if (mIconView != null && mIconView.getParent() == this) {
-            Drawable drawable = mIconView.getDrawable();
-            if (drawable != null) {
-                mIconView.offsetTopAndBottom(
-                        mTitleKeylinePixels - mIconView.getMeasuredHeight() / 2);
-            }
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/GuidanceStylist.java b/android/support/v17/leanback/widget/GuidanceStylist.java
deleted file mode 100644
index edcec42..0000000
--- a/android/support/v17/leanback/widget/GuidanceStylist.java
+++ /dev/null
@@ -1,269 +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.v17.leanback.widget;
-
-import android.animation.Animator;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.List;
-
-/**
- * GuidanceStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
- * to display contextual information for the decision(s) required at that step.
- * <p>
- * Many aspects of the base GuidanceStylist can be customized through theming; see the theme
- * attributes below. Note that these attributes are not set on individual elements in layout
- * XML, but instead would be set in a custom theme. See
- * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
- * for more information.
- * <p>
- * If these hooks are insufficient, this class may also be subclassed. Subclasses
- * may wish to override the {@link #onProvideLayoutId} method to change the layout file used to
- * display the guidance; more complex layouts may be supported by also providing a subclass of
- * {@link GuidanceStylist.Guidance} with extra fields.
- * <p>
- * Note: If an alternate layout is provided, the following view IDs should be used to refer to base
- * elements:
- * <ul>
- * <li>{@link android.support.v17.leanback.R.id#guidance_title}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidance_description}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidance_breadcrumb}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidance_icon}</li>
- * </ul><p>
- * View IDs are allowed to be missing, in which case the corresponding views will be null.
- *
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceBreadcrumbStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceIconStyle
- * @see android.support.v17.leanback.app.GuidedStepFragment
- * @see GuidanceStylist.Guidance
- */
-public class GuidanceStylist implements FragmentAnimationProvider {
-
-    /**
-     * A data class representing contextual information for a {@link
-     * android.support.v17.leanback.app.GuidedStepFragment}. Guidance consists of a short title,
-     * a longer description, a breadcrumb to help with global navigation (often indicating where
-     * the back button will lead), and an optional icon.  All this information is intended to
-     * provide users with the appropriate context to make the decision(s) required by the current
-     * step.
-     * <p>
-     * Clients may provide a subclass of this if they wish to remember auxiliary data for use in
-     * a customized GuidanceStylist.
-     */
-    public static class Guidance {
-        private final String mTitle;
-        private final String mDescription;
-        private final String mBreadcrumb;
-        private final Drawable mIconDrawable;
-
-        /**
-         * Constructs a Guidance object with the specified title, description, breadcrumb, and
-         * icon drawable.
-         * @param title The title for the current guided step.
-         * @param description The description for the current guided step.
-         * @param breadcrumb The breadcrumb for the current guided step.
-         * @param icon The icon drawable representing the current guided step.
-         */
-        public Guidance(String title, String description, String breadcrumb, Drawable icon) {
-            mBreadcrumb = breadcrumb;
-            mTitle = title;
-            mDescription = description;
-            mIconDrawable = icon;
-        }
-
-        /**
-         * Returns the title specified when this Guidance was constructed.
-         * @return The title for this Guidance.
-         */
-        public String getTitle() {
-            return mTitle;
-        }
-
-        /**
-         * Returns the description specified when this Guidance was constructed.
-         * @return The description for this Guidance.
-         */
-        public String getDescription() {
-            return mDescription;
-        }
-
-        /**
-         * Returns the breadcrumb specified when this Guidance was constructed.
-         * @return The breadcrumb for this Guidance.
-         */
-        public String getBreadcrumb() {
-            return mBreadcrumb;
-        }
-
-        /**
-         * Returns the icon drawable specified when this Guidance was constructed.
-         * @return The icon for this Guidance.
-         */
-        public Drawable getIconDrawable() {
-            return mIconDrawable;
-        }
-    }
-
-    private TextView mTitleView;
-    private TextView mDescriptionView;
-    private TextView mBreadcrumbView;
-    private ImageView mIconView;
-    private View mGuidanceContainer;
-
-    /**
-     * Creates an appropriately configured view for the given Guidance, using the provided
-     * inflater and container.
-     * <p>
-     * <i>Note: Does not actually add the created view to the container; the caller should do
-     * this.</i>
-     * @param inflater The layout inflater to be used when constructing the view.
-     * @param container The view group to be passed in the call to
-     * <code>LayoutInflater.inflate</code>.
-     * @param guidance The guidance data for the view.
-     * @return The view to be added to the caller's view hierarchy.
-     */
-    public View onCreateView(
-            final LayoutInflater inflater, ViewGroup container, Guidance guidance) {
-
-        View guidanceView = inflater.inflate(onProvideLayoutId(), container, false);
-        mTitleView = (TextView) guidanceView.findViewById(R.id.guidance_title);
-        mBreadcrumbView = (TextView) guidanceView.findViewById(R.id.guidance_breadcrumb);
-        mDescriptionView = (TextView) guidanceView.findViewById(R.id.guidance_description);
-        mIconView = (ImageView) guidanceView.findViewById(R.id.guidance_icon);
-        mGuidanceContainer = guidanceView.findViewById(R.id.guidance_container);
-
-        // We allow any of the cached subviews to be null, so that subclasses can choose not to
-        // display a particular piece of information.
-        if (mTitleView != null) {
-            mTitleView.setText(guidance.getTitle());
-        }
-
-        if (mBreadcrumbView != null) {
-            mBreadcrumbView.setText(guidance.getBreadcrumb());
-        }
-
-        if (mDescriptionView != null) {
-            mDescriptionView.setText(guidance.getDescription());
-        }
-
-        if (mIconView != null) {
-            if (guidance.getIconDrawable() != null) {
-                mIconView.setImageDrawable(guidance.getIconDrawable());
-            } else {
-                mIconView.setVisibility(View.GONE);
-            }
-        }
-
-        if (mGuidanceContainer != null) {
-            CharSequence contentDescription = mGuidanceContainer.getContentDescription();
-            if (TextUtils.isEmpty(contentDescription)) {
-                StringBuilder builder = new StringBuilder();
-                if (!TextUtils.isEmpty(guidance.getBreadcrumb())) {
-                    builder.append(guidance.getBreadcrumb()).append('\n');
-                }
-                if (!TextUtils.isEmpty(guidance.getTitle())) {
-                    builder.append(guidance.getTitle()).append('\n');
-                }
-                if (!TextUtils.isEmpty(guidance.getDescription())) {
-                    builder.append(guidance.getDescription()).append('\n');
-                }
-                mGuidanceContainer.setContentDescription(builder);
-            }
-        }
-
-        return guidanceView;
-    }
-
-    /**
-     * Called when destroy the View created by GuidanceStylist.
-     */
-    public void onDestroyView() {
-        mBreadcrumbView = null;
-        mDescriptionView = null;
-        mIconView = null;
-        mTitleView = null;
-    }
-
-    /**
-     * Provides the resource ID of the layout defining the guidance view. Subclasses may override
-     * to provide their own customized layouts. The base implementation returns
-     * {@link android.support.v17.leanback.R.layout#lb_guidance}. If overridden, the substituted
-     * layout should contain matching IDs for any views that should be managed by the base class;
-     * this can be achieved by starting with a copy of the base layout file.
-     * @return The resource ID of the layout to be inflated to define the guidance view.
-     */
-    public int onProvideLayoutId() {
-        return R.layout.lb_guidance;
-    }
-
-    /**
-     * Returns the view displaying the title of the guidance.
-     * @return The text view object for the title.
-     */
-    public TextView getTitleView() {
-        return mTitleView;
-    }
-
-    /**
-     * Returns the view displaying the description of the guidance.
-     * @return The text view object for the description.
-     */
-    public TextView getDescriptionView() {
-        return mDescriptionView;
-    }
-
-    /**
-     * Returns the view displaying the breadcrumb of the guidance.
-     * @return The text view object for the breadcrumb.
-     */
-    public TextView getBreadcrumbView() {
-        return mBreadcrumbView;
-    }
-
-    /**
-     * Returns the view displaying the icon of the guidance.
-     * @return The image view object for the icon.
-     */
-    public ImageView getIconView() {
-        return mIconView;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onImeAppearing(@NonNull List<Animator> animators) {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onImeDisappearing(@NonNull List<Animator> animators) {
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/GuidedAction.java b/android/support/v17/leanback/widget/GuidedAction.java
deleted file mode 100644
index 8898d48..0000000
--- a/android/support/v17/leanback/widget/GuidedAction.java
+++ /dev/null
@@ -1,956 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.StringRes;
-import android.support.v17.leanback.R;
-import android.support.v4.content.ContextCompat;
-import android.text.InputType;
-
-import java.util.List;
-
-/**
- * A data class which represents an action within a {@link
- * android.support.v17.leanback.app.GuidedStepFragment}. GuidedActions contain at minimum a title
- * and a description, and typically also an icon.
- * <p>
- * A GuidedAction typically represents a single action a user may take, but may also represent a
- * possible choice out of a group of mutually exclusive choices (similar to radio buttons), or an
- * information-only label (in which case the item cannot be clicked).
- * <p>
- * GuidedActions may optionally be checked. They may also indicate that they will request further
- * user input on selection, in which case they will be displayed with a chevron indicator.
- * <p>
- * GuidedAction recommends to use {@link Builder}. When application subclass GuidedAction, it
- * can subclass {@link BuilderBase}, implement its own builder() method where it should
- * call {@link BuilderBase#applyValues(GuidedAction)}.
- */
-public class GuidedAction extends Action {
-
-    private static final String TAG = "GuidedAction";
-
-    /**
-     * Special check set Id that is neither checkbox nor radio.
-     */
-    public static final int NO_CHECK_SET = 0;
-    /**
-     * Default checkset Id for radio.
-     */
-    public static final int DEFAULT_CHECK_SET_ID = 1;
-    /**
-     * Checkset Id for checkbox.
-     */
-    public static final int CHECKBOX_CHECK_SET_ID = -1;
-
-    /**
-     * When finishing editing, goes to next action.
-     */
-    public static final long ACTION_ID_NEXT = -2;
-    /**
-     * When finishing editing, stay on current action.
-     */
-    public static final long ACTION_ID_CURRENT = -3;
-
-    /**
-     * Id of standard OK action.
-     */
-    public static final long ACTION_ID_OK = -4;
-
-    /**
-     * Id of standard Cancel action.
-     */
-    public static final long ACTION_ID_CANCEL = -5;
-
-    /**
-     * Id of standard Finish action.
-     */
-    public static final long ACTION_ID_FINISH = -6;
-
-    /**
-     * Id of standard Finish action.
-     */
-    public static final long ACTION_ID_CONTINUE = -7;
-
-    /**
-     * Id of standard Yes action.
-     */
-    public static final long ACTION_ID_YES = -8;
-
-    /**
-     * Id of standard No action.
-     */
-    public static final long ACTION_ID_NO = -9;
-
-    static final int EDITING_NONE = 0;
-    static final int EDITING_TITLE = 1;
-    static final int EDITING_DESCRIPTION = 2;
-    static final int EDITING_ACTIVATOR_VIEW = 3;
-
-    /**
-     * Base builder class to build a {@link GuidedAction} object.  When subclass GuidedAction, you
-     * can override this BuilderBase class, implements your build() method which should call
-     * {@link #applyValues(GuidedAction)}.  When using GuidedAction directly, use {@link Builder}.
-     */
-    public abstract static class BuilderBase<B extends BuilderBase> {
-        private Context mContext;
-        private long mId;
-        private CharSequence mTitle;
-        private CharSequence mEditTitle;
-        private CharSequence mDescription;
-        private CharSequence mEditDescription;
-        private Drawable mIcon;
-        /**
-         * The mActionFlags holds various action states such as whether title or description are
-         * editable, or the action is focusable.
-         *
-         */
-        private int mActionFlags;
-
-        private int mEditable = EDITING_NONE;
-        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;
-        private List<GuidedAction> mSubActions;
-        private Intent mIntent;
-
-        /**
-         * Creates a BuilderBase for GuidedAction or its subclass.
-         * @param context Context object used to build the GuidedAction.
-         */
-        public BuilderBase(Context context) {
-            mContext = context;
-            mActionFlags = PF_ENABLED | PF_FOCUSABLE | PF_AUTORESTORE;
-        }
-
-        /**
-         * Returns Context of this Builder.
-         * @return Context of this Builder.
-         */
-        public Context getContext() {
-            return mContext;
-        }
-
-        private void setFlags(int flag, int mask) {
-            mActionFlags = (mActionFlags & ~mask) | (flag & mask);
-        }
-
-        /**
-         * Subclass of BuilderBase should call this function to apply values.
-         * @param action GuidedAction to apply BuilderBase values.
-         */
-        protected final void applyValues(GuidedAction action) {
-            // Base Action values
-            action.setId(mId);
-            action.setLabel1(mTitle);
-            action.setEditTitle(mEditTitle);
-            action.setLabel2(mDescription);
-            action.setEditDescription(mEditDescription);
-            action.setIcon(mIcon);
-
-            // Subclass values
-            action.mIntent = mIntent;
-            action.mEditable = mEditable;
-            action.mInputType = mInputType;
-            action.mDescriptionInputType = mDescriptionInputType;
-            action.mEditInputType = mEditInputType;
-            action.mDescriptionEditInputType = mDescriptionEditInputType;
-            action.mActionFlags = mActionFlags;
-            action.mCheckSetId = mCheckSetId;
-            action.mSubActions = mSubActions;
-        }
-
-        /**
-         * Construct a clickable action with associated id and auto assign pre-defined title for the
-         * action. If the id is not supported, the method simply does nothing.
-         * @param id One of {@link GuidedAction#ACTION_ID_OK} {@link GuidedAction#ACTION_ID_CANCEL}
-         * {@link GuidedAction#ACTION_ID_FINISH} {@link GuidedAction#ACTION_ID_CONTINUE}
-         * {@link GuidedAction#ACTION_ID_YES} {@link GuidedAction#ACTION_ID_NO}.
-         * @return The same BuilderBase object.
-         */
-        public B clickAction(long id) {
-            if (id == ACTION_ID_OK) {
-                mId = ACTION_ID_OK;
-                mTitle = mContext.getString(android.R.string.ok);
-            } else if (id == ACTION_ID_CANCEL) {
-                mId = ACTION_ID_CANCEL;
-                mTitle = mContext.getString(android.R.string.cancel);
-            } else if (id == ACTION_ID_FINISH) {
-                mId = ACTION_ID_FINISH;
-                mTitle = mContext.getString(R.string.lb_guidedaction_finish_title);
-            } else if (id == ACTION_ID_CONTINUE) {
-                mId = ACTION_ID_CONTINUE;
-                mTitle = mContext.getString(R.string.lb_guidedaction_continue_title);
-            } else if (id == ACTION_ID_YES) {
-                mId = ACTION_ID_YES;
-                mTitle = mContext.getString(android.R.string.ok);
-            } else if (id == ACTION_ID_NO) {
-                mId = ACTION_ID_NO;
-                mTitle = mContext.getString(android.R.string.cancel);
-            }
-            return (B) this;
-        }
-
-        /**
-         * Sets the ID associated with this action.  The ID can be any value the client wishes;
-         * it is typically used to determine what to do when an action is clicked.
-         * @param id The ID to associate with this action.
-         */
-        public B id(long id) {
-            mId = id;
-            return (B) this;
-        }
-
-        /**
-         * Sets the title for this action.  The title is typically a short string indicating the
-         * action to be taken on click, e.g. "Continue" or "Cancel".
-         * @param title The title for this action.
-         */
-        public B title(CharSequence title) {
-            mTitle = title;
-            return (B) this;
-        }
-
-        /**
-         * Sets the title for this action.  The title is typically a short string indicating the
-         * action to be taken on click, e.g. "Continue" or "Cancel".
-         * @param titleResourceId The resource id of title for this action.
-         */
-        public B title(@StringRes int titleResourceId) {
-            mTitle = getContext().getString(titleResourceId);
-            return (B) this;
-        }
-
-        /**
-         * Sets the optional title text to edit.  When TextView is activated, the edit title
-         * replaces the string of title.
-         * @param editTitle The optional title text to edit when TextView is activated.
-         */
-        public B editTitle(CharSequence editTitle) {
-            mEditTitle = editTitle;
-            return (B) this;
-        }
-
-        /**
-         * Sets the optional title text to edit.  When TextView is activated, the edit title
-         * replaces the string of title.
-         * @param editTitleResourceId String resource id of the optional title text to edit when
-         * TextView is activated.
-         */
-        public B editTitle(@StringRes int editTitleResourceId) {
-            mEditTitle = getContext().getString(editTitleResourceId);
-            return (B) this;
-        }
-
-        /**
-         * Sets the description for this action.  The description is typically a longer string
-         * providing extra information on what the action will do.
-         * @param description The description for this action.
-         */
-        public B description(CharSequence description) {
-            mDescription = description;
-            return (B) this;
-        }
-
-        /**
-         * Sets the description for this action.  The description is typically a longer string
-         * providing extra information on what the action will do.
-         * @param descriptionResourceId String resource id of the description for this action.
-         */
-        public B description(@StringRes int descriptionResourceId) {
-            mDescription = getContext().getString(descriptionResourceId);
-            return (B) this;
-        }
-
-        /**
-         * Sets the optional description text to edit.  When TextView is activated, the edit
-         * description replaces the string of description.
-         * @param description The description to edit for this action.
-         */
-        public B editDescription(CharSequence description) {
-            mEditDescription = description;
-            return (B) this;
-        }
-
-        /**
-         * Sets the optional description text to edit.  When TextView is activated, the edit
-         * description replaces the string of description.
-         * @param descriptionResourceId String resource id of the description to edit for this
-         * action.
-         */
-        public B editDescription(@StringRes int descriptionResourceId) {
-            mEditDescription = getContext().getString(descriptionResourceId);
-            return (B) this;
-        }
-
-        /**
-         * Sets the intent associated with this action.  Clients would typically fire this intent
-         * directly when the action is clicked.
-         * @param intent The intent associated with this action.
-         */
-        public B intent(Intent intent) {
-            mIntent = intent;
-            return (B) this;
-        }
-
-        /**
-         * Sets the action's icon drawable.
-         * @param icon The drawable for the icon associated with this action.
-         */
-        public B icon(Drawable icon) {
-            mIcon = icon;
-            return (B) this;
-        }
-
-        /**
-         * Sets the action's icon drawable by retrieving it by resource ID from the specified
-         * context. This is a convenience function that simply looks up the drawable and calls
-         * {@link #icon(Drawable)}.
-         * @param iconResourceId The resource ID for the icon associated with this action.
-         * @param context The context whose resource ID should be retrieved.
-         * @deprecated Use {@link #icon(int)}.
-         */
-        @Deprecated
-        public B iconResourceId(@DrawableRes int iconResourceId, Context context) {
-            return icon(ContextCompat.getDrawable(context, iconResourceId));
-        }
-
-        /**
-         * Sets the action's icon drawable by retrieving it by resource ID from Builder's
-         * context. This is a convenience function that simply looks up the drawable and calls
-         * {@link #icon(Drawable)}.
-         * @param iconResourceId The resource ID for the icon associated with this action.
-         */
-        public B icon(@DrawableRes int iconResourceId) {
-            return icon(ContextCompat.getDrawable(getContext(), iconResourceId));
-        }
-
-        /**
-         * Indicates whether this action title is editable. Note: Editable actions cannot also be
-         * checked, or belong to a check set.
-         * @param editable Whether this action is editable.
-         */
-        public B editable(boolean editable) {
-            if (!editable) {
-                if (mEditable == EDITING_TITLE) {
-                    mEditable = EDITING_NONE;
-                }
-                return (B) this;
-            }
-            mEditable = EDITING_TITLE;
-            if (isChecked() || mCheckSetId != NO_CHECK_SET) {
-                throw new IllegalArgumentException("Editable actions cannot also be checked");
-            }
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action's description is editable
-         * @param editable Whether this action description is editable.
-         */
-        public B descriptionEditable(boolean editable) {
-            if (!editable) {
-                if (mEditable == EDITING_DESCRIPTION) {
-                    mEditable = EDITING_NONE;
-                }
-                return (B) this;
-            }
-            mEditable = EDITING_DESCRIPTION;
-            if (isChecked() || mCheckSetId != NO_CHECK_SET) {
-                throw new IllegalArgumentException("Editable actions cannot also be checked");
-            }
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action has a view can be activated to edit, e.g. a DatePicker.
-         * @param editable Whether this action has view can be activated to edit.
-         */
-        public B hasEditableActivatorView(boolean editable) {
-            if (!editable) {
-                if (mEditable == EDITING_ACTIVATOR_VIEW) {
-                    mEditable = EDITING_NONE;
-                }
-                return (B) this;
-            }
-            mEditable = EDITING_ACTIVATOR_VIEW;
-            if (isChecked() || mCheckSetId != NO_CHECK_SET) {
-                throw new IllegalArgumentException("Editable actions cannot also be checked");
-            }
-            return (B) this;
-        }
-
-        /**
-         * Sets {@link InputType} of this action title not in editing.
-         *
-         * @param inputType InputType for the action title not in editing.
-         */
-        public B inputType(int inputType) {
-            mInputType = inputType;
-            return (B) this;
-        }
-
-        /**
-         * Sets {@link InputType} of this action description not in editing.
-         *
-         * @param inputType InputType for the action description not in editing.
-         */
-        public B descriptionInputType(int inputType) {
-            mDescriptionInputType = inputType;
-            return (B) this;
-        }
-
-
-        /**
-         * Sets {@link InputType} of this action title in editing.
-         *
-         * @param inputType InputType for the action title in editing.
-         */
-        public B editInputType(int inputType) {
-            mEditInputType = inputType;
-            return (B) this;
-        }
-
-        /**
-         * Sets {@link InputType} of this action description in editing.
-         *
-         * @param inputType InputType for the action description in editing.
-         */
-        public B descriptionEditInputType(int inputType) {
-            mDescriptionEditInputType = inputType;
-            return (B) this;
-        }
-
-
-        private boolean isChecked() {
-            return (mActionFlags & PF_CHECKED) == PF_CHECKED;
-        }
-        /**
-         * Indicates whether this action is initially checked.
-         * @param checked Whether this action is checked.
-         */
-        public B checked(boolean checked) {
-            setFlags(checked ? PF_CHECKED : 0, PF_CHECKED);
-            if (mEditable != EDITING_NONE) {
-                throw new IllegalArgumentException("Editable actions cannot also be checked");
-            }
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action is part of a single-select group similar to radio buttons
-         * or this action is a checkbox. When one item in a check set is checked, all others with
-         * the same check set ID will be checked automatically.
-         * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not
-         * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox.
-         */
-        public B checkSetId(int checkSetId) {
-            mCheckSetId = checkSetId;
-            if (mEditable != EDITING_NONE) {
-                throw new IllegalArgumentException("Editable actions cannot also be in check sets");
-            }
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether the title and description are long, and should be displayed
-         * appropriately.
-         * @param multilineDescription Whether this action has a multiline description.
-         */
-        public B multilineDescription(boolean multilineDescription) {
-            setFlags(multilineDescription ? PF_MULTI_lINE_DESCRIPTION : 0,
-                    PF_MULTI_lINE_DESCRIPTION);
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action has a next state and should display a chevron.
-         * @param hasNext Whether this action has a next state.
-         */
-        public B hasNext(boolean hasNext) {
-            setFlags(hasNext ? PF_HAS_NEXT : 0, PF_HAS_NEXT);
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action is for information purposes only and cannot be clicked.
-         * @param infoOnly Whether this action has a next state.
-         */
-        public B infoOnly(boolean infoOnly) {
-            setFlags(infoOnly ? PF_INFO_ONLY : 0, PF_INFO_ONLY);
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action is enabled.  If not enabled, an action cannot be clicked.
-         * @param enabled Whether the action is enabled.
-         */
-        public B enabled(boolean enabled) {
-            setFlags(enabled ? PF_ENABLED : 0, PF_ENABLED);
-            return (B) this;
-        }
-
-        /**
-         * Indicates whether this action can take focus.
-         * @param focusable
-         * @return The same BuilderBase object.
-         */
-        public B focusable(boolean focusable) {
-            setFlags(focusable ? PF_FOCUSABLE : 0, PF_FOCUSABLE);
-            return (B) this;
-        }
-
-        /**
-         * Sets sub actions list.
-         * @param subActions
-         * @return The same BuilderBase object.
-         */
-        public B subActions(List<GuidedAction> subActions) {
-            mSubActions = subActions;
-            return (B) this;
-        }
-
-        /**
-         * Explicitly sets auto restore feature on the GuidedAction.  It's by default true.
-         * @param autoSaveRestoreEnabled True if turn on auto save/restore of GuidedAction content,
-         *                                false otherwise.
-         * @return The same BuilderBase object.
-         * @see GuidedAction#isAutoSaveRestoreEnabled()
-         */
-        public B autoSaveRestoreEnabled(boolean autoSaveRestoreEnabled) {
-            setFlags(autoSaveRestoreEnabled ? PF_AUTORESTORE : 0, PF_AUTORESTORE);
-            return (B) this;
-        }
-
-    }
-
-    /**
-     * Builds a {@link GuidedAction} object.
-     */
-    public static class Builder extends BuilderBase<Builder> {
-
-        /**
-         * @deprecated Use {@link GuidedAction.Builder#GuidedAction.Builder(Context)}.
-         */
-        @Deprecated
-        public Builder() {
-            super(null);
-        }
-
-        /**
-         * Creates a Builder for GuidedAction.
-         * @param context Context to build GuidedAction.
-         */
-        public Builder(Context context) {
-            super(context);
-        }
-
-        /**
-         * Builds the GuidedAction corresponding to this Builder.
-         * @return The GuidedAction as configured through this Builder.
-         */
-        public GuidedAction build() {
-            GuidedAction action = new GuidedAction();
-            applyValues(action);
-            return action;
-        }
-
-    }
-
-    static final int PF_CHECKED = 0x00000001;
-    static final int PF_MULTI_lINE_DESCRIPTION = 0x00000002;
-    static final int PF_HAS_NEXT = 0x00000004;
-    static final int PF_INFO_ONLY = 0x00000008;
-    static final int PF_ENABLED = 0x00000010;
-    static final int PF_FOCUSABLE = 0x00000020;
-    static final int PF_AUTORESTORE = 0x00000040;
-    int mActionFlags;
-
-    private CharSequence mEditTitle;
-    private CharSequence mEditDescription;
-    int mEditable;
-    int mInputType;
-    int mDescriptionInputType;
-    int mEditInputType;
-    int mDescriptionEditInputType;
-
-    int mCheckSetId;
-
-    List<GuidedAction> mSubActions;
-
-    Intent mIntent;
-
-    protected GuidedAction() {
-        super(0);
-    }
-
-    private void setFlags(int flag, int mask) {
-        mActionFlags = (mActionFlags & ~mask) | (flag & mask);
-    }
-
-    /**
-     * Returns the title of this action.
-     * @return The title set when this action was built.
-     */
-    public CharSequence getTitle() {
-        return getLabel1();
-    }
-
-    /**
-     * Sets the title of this action.
-     * @param title The title set when this action was built.
-     */
-    public void setTitle(CharSequence title) {
-        setLabel1(title);
-    }
-
-    /**
-     * Returns the optional title text to edit.  When not null, it is being edited instead of
-     * {@link #getTitle()}.
-     * @return Optional title text to edit instead of {@link #getTitle()}.
-     */
-    public CharSequence getEditTitle() {
-        return mEditTitle;
-    }
-
-    /**
-     * Sets the optional title text to edit instead of {@link #setTitle(CharSequence)}.
-     * @param editTitle Optional title text to edit instead of {@link #setTitle(CharSequence)}.
-     */
-    public void setEditTitle(CharSequence editTitle) {
-        mEditTitle = editTitle;
-    }
-
-    /**
-     * Returns the optional description text to edit.  When not null, it is being edited instead of
-     * {@link #getDescription()}.
-     * @return Optional description text to edit instead of {@link #getDescription()}.
-     */
-    public CharSequence getEditDescription() {
-        return mEditDescription;
-    }
-
-    /**
-     * Sets the optional description text to edit instead of {@link #setDescription(CharSequence)}.
-     * @param editDescription Optional description text to edit instead of
-     * {@link #setDescription(CharSequence)}.
-     */
-    public void setEditDescription(CharSequence editDescription) {
-        mEditDescription = editDescription;
-    }
-
-    /**
-     * Returns true if {@link #getEditTitle()} is not null.  When true, the {@link #getEditTitle()}
-     * is being edited instead of {@link #getTitle()}.
-     * @return true if {@link #getEditTitle()} is not null.
-     */
-    public boolean isEditTitleUsed() {
-        return mEditTitle != null;
-    }
-
-    /**
-     * Returns the description of this action.
-     * @return The description of this action.
-     */
-    public CharSequence getDescription() {
-        return getLabel2();
-    }
-
-    /**
-     * Sets the description of this action.
-     * @param description The description of the action.
-     */
-    public void setDescription(CharSequence description) {
-        setLabel2(description);
-    }
-
-    /**
-     * Returns the intent associated with this action.
-     * @return The intent set when this action was built.
-     */
-    public Intent getIntent() {
-        return mIntent;
-    }
-
-    /**
-     * Sets the intent of this action.
-     * @param intent New intent to set on this action.
-     */
-    public void setIntent(Intent intent) {
-        mIntent = intent;
-    }
-
-    /**
-     * Returns whether this action title is editable.
-     * @return true if the action title is editable, false otherwise.
-     */
-    public boolean isEditable() {
-        return mEditable == EDITING_TITLE;
-    }
-
-    /**
-     * Returns whether this action description is editable.
-     * @return true if the action description is editable, false otherwise.
-     */
-    public boolean isDescriptionEditable() {
-        return mEditable == EDITING_DESCRIPTION;
-    }
-
-    /**
-     * Returns if this action has editable title or editable description.
-     * @return True if this action has editable title or editable description, false otherwise.
-     */
-    public boolean hasTextEditable() {
-        return mEditable == EDITING_TITLE || mEditable == EDITING_DESCRIPTION;
-    }
-
-    /**
-     * Returns whether this action can be activated to edit, e.g. a DatePicker.
-     * @return true if the action can be activated to edit.
-     */
-    public boolean hasEditableActivatorView() {
-        return mEditable == EDITING_ACTIVATOR_VIEW;
-    }
-
-    /**
-     * Returns InputType of action title in editing; only valid when {@link #isEditable()} is true.
-     * @return InputType of action title in editing.
-     */
-    public int getEditInputType() {
-        return mEditInputType;
-    }
-
-    /**
-     * Returns InputType of action description in editing; only valid when
-     * {@link #isDescriptionEditable()} is true.
-     * @return InputType of action description in editing.
-     */
-    public int getDescriptionEditInputType() {
-        return mDescriptionEditInputType;
-    }
-
-    /**
-     * Returns InputType of action title not in editing.
-     * @return InputType of action title not in editing.
-     */
-    public int getInputType() {
-        return mInputType;
-    }
-
-    /**
-     * Returns InputType of action description not in editing.
-     * @return InputType of action description not in editing.
-     */
-    public int getDescriptionInputType() {
-        return mDescriptionInputType;
-    }
-
-    /**
-     * Returns whether this action is checked.
-     * @return true if the action is currently checked, false otherwise.
-     */
-    public boolean isChecked() {
-        return (mActionFlags & PF_CHECKED) == PF_CHECKED;
-    }
-
-    /**
-     * Sets whether this action is checked.
-     * @param checked Whether this action should be checked.
-     */
-    public void setChecked(boolean checked) {
-        setFlags(checked ? PF_CHECKED : 0, PF_CHECKED);
-    }
-
-    /**
-     * Returns the check set id this action is a part of. All actions in the same list with the same
-     * check set id are considered linked. When one of the actions within that set is selected, that
-     * action becomes checked, while all the other actions become unchecked.
-     *
-     * @return an integer representing the check set this action is a part of, or
-     *         {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if
-     *         this action is not a checkbox or radiobutton.
-     */
-    public int getCheckSetId() {
-        return mCheckSetId;
-    }
-
-    /**
-     * Returns whether this action is has a multiline description.
-     * @return true if the action was constructed as having a multiline description, false
-     * otherwise.
-     */
-    public boolean hasMultilineDescription() {
-        return (mActionFlags & PF_MULTI_lINE_DESCRIPTION) == PF_MULTI_lINE_DESCRIPTION;
-    }
-
-    /**
-     * Returns whether this action is enabled.
-     * @return true if the action is currently enabled, false otherwise.
-     */
-    public boolean isEnabled() {
-        return (mActionFlags & PF_ENABLED) == PF_ENABLED;
-    }
-
-    /**
-     * Sets whether this action is enabled.
-     * @param enabled Whether this action should be enabled.
-     */
-    public void setEnabled(boolean enabled) {
-        setFlags(enabled ? PF_ENABLED : 0, PF_ENABLED);
-    }
-
-    /**
-     * Returns whether this action is focusable.
-     * @return true if the action is currently focusable, false otherwise.
-     */
-    public boolean isFocusable() {
-        return (mActionFlags & PF_FOCUSABLE) == PF_FOCUSABLE;
-    }
-
-    /**
-     * Sets whether this action is focusable.
-     * @param focusable Whether this action should be focusable.
-     */
-    public void setFocusable(boolean focusable) {
-        setFlags(focusable ? PF_FOCUSABLE : 0, PF_FOCUSABLE);
-    }
-
-    /**
-     * Returns whether this action will request further user input when selected, such as showing
-     * another GuidedStepFragment or launching a new activity. Configured during construction.
-     * @return true if the action will request further user input when selected, false otherwise.
-     */
-    public boolean hasNext() {
-        return (mActionFlags & PF_HAS_NEXT) == PF_HAS_NEXT;
-    }
-
-    /**
-     * Returns whether the action will only display information and is thus not clickable. If both
-     * this and {@link #hasNext()} are true, infoOnly takes precedence. The default is false. For
-     * example, this might represent e.g. the amount of storage a document uses, or the cost of an
-     * app.
-     * @return true if will only display information, false otherwise.
-     */
-    public boolean infoOnly() {
-        return (mActionFlags & PF_INFO_ONLY) == PF_INFO_ONLY;
-    }
-
-    /**
-     * Change sub actions list.
-     * @param actions Sub actions list to set on this action.  Sets null to disable sub actions.
-     */
-    public void setSubActions(List<GuidedAction> actions) {
-        mSubActions = actions;
-    }
-
-    /**
-     * @return List of sub actions or null if sub actions list is not enabled.
-     */
-    public List<GuidedAction> getSubActions() {
-        return mSubActions;
-    }
-
-    /**
-     * @return True if has sub actions list, even it's currently empty.
-     */
-    public boolean hasSubActions() {
-        return mSubActions != null;
-    }
-
-    /**
-     * Returns true if Action will be saved to instanceState and restored later, false otherwise.
-     * The default value is true.  When isAutoSaveRestoreEnabled() is true and {@link #getId()} is
-     * not {@link #NO_ID}:
-     * <li>{@link #isEditable()} is true: save text of {@link #getTitle()}</li>
-     * <li>{@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}</li>
-     * <li>{@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}</li>
-     * <li>{@link GuidedDatePickerAction} will be saved</li>
-     * App may explicitly disable auto restore and handle by itself. App should override Fragment
-     * onSaveInstanceState() and onCreateActions()
-     * @return True if Action will be saved to instanceState and restored later, false otherwise.
-     */
-    public final boolean isAutoSaveRestoreEnabled() {
-        return (mActionFlags & PF_AUTORESTORE) == PF_AUTORESTORE;
-    }
-
-    /**
-     * Save action into a bundle using a given key. When isAutoRestoreEna() is true:
-     * <li>{@link #isEditable()} is true: save text of {@link #getTitle()}</li>
-     * <li>{@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}</li>
-     * <li>{@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}</li>
-     * <li>{@link GuidedDatePickerAction} will be saved</li>
-     * Subclass may override this method.
-     * @param bundle  Bundle to save the Action.
-     * @param key Key used to save the Action.
-     */
-    public void onSaveInstanceState(Bundle bundle, String key) {
-        if (needAutoSaveTitle() && getTitle() != null) {
-            bundle.putString(key, getTitle().toString());
-        } else if (needAutoSaveDescription() && getDescription() != null) {
-            bundle.putString(key, getDescription().toString());
-        } else if (getCheckSetId() != NO_CHECK_SET) {
-            bundle.putBoolean(key, isChecked());
-        }
-    }
-
-    /**
-     * Restore action from a bundle using a given key. When isAutoRestore() is true:
-     * <li>{@link #isEditable()} is true: save text of {@link #getTitle()}</li>
-     * <li>{@link #isDescriptionEditable()} is true: save text of {@link #getDescription()}</li>
-     * <li>{@link #getCheckSetId()} is not {@link #NO_CHECK_SET}: save {@link #isChecked()}}</li>
-     * <li>{@link GuidedDatePickerAction} will be saved</li>
-     * Subclass may override this method.
-     * @param bundle  Bundle to restore the Action from.
-     * @param key Key used to restore the Action.
-     */
-    public void onRestoreInstanceState(Bundle bundle, String key) {
-        if (needAutoSaveTitle()) {
-            String title = bundle.getString(key);
-            if (title != null) {
-                setTitle(title);
-            }
-        } else if (needAutoSaveDescription()) {
-            String description = bundle.getString(key);
-            if (description != null) {
-                setDescription(description);
-            }
-        } else if (getCheckSetId() != NO_CHECK_SET) {
-            setChecked(bundle.getBoolean(key, isChecked()));
-        }
-    }
-
-    final static boolean isPasswordVariant(int inputType) {
-        final int variation = inputType & InputType.TYPE_MASK_VARIATION;
-        return variation == InputType.TYPE_TEXT_VARIATION_PASSWORD
-                || variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
-                || variation == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD;
-    }
-
-    final boolean needAutoSaveTitle() {
-        return isEditable() && !isPasswordVariant(getEditInputType());
-    }
-
-    final boolean needAutoSaveDescription() {
-        return isDescriptionEditable() && !isPasswordVariant(getDescriptionEditInputType());
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionAdapter.java b/android/support/v17/leanback/widget/GuidedActionAdapter.java
deleted file mode 100644
index 51b29e2..0000000
--- a/android/support/v17/leanback/widget/GuidedActionAdapter.java
+++ /dev/null
@@ -1,557 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.inputmethod.EditorInfo;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * GuidedActionAdapter instantiates views for guided actions, and manages their interactions.
- * Presentation (view creation and state animation) is delegated to a {@link
- * GuidedActionsStylist}, while clients are notified of interactions via
- * {@link GuidedActionAdapter.ClickListener} and {@link GuidedActionAdapter.FocusListener}.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class GuidedActionAdapter extends RecyclerView.Adapter {
-    static final String TAG = "GuidedActionAdapter";
-    static final boolean DEBUG = false;
-
-    static final String TAG_EDIT = "EditableAction";
-    static final boolean DEBUG_EDIT = false;
-
-    /**
-     * Object listening for click events within a {@link GuidedActionAdapter}.
-     */
-    public interface ClickListener {
-
-        /**
-         * Called when the user clicks on an action.
-         */
-        void onGuidedActionClicked(GuidedAction action);
-
-    }
-
-    /**
-     * Object listening for focus events within a {@link GuidedActionAdapter}.
-     */
-    public interface FocusListener {
-
-        /**
-         * Called when the user focuses on an action.
-         */
-        void onGuidedActionFocused(GuidedAction action);
-    }
-
-    /**
-     * Object listening for edit events within a {@link GuidedActionAdapter}.
-     */
-    public interface EditListener {
-
-        /**
-         * Called when the user exits edit mode on an action.
-         */
-        void onGuidedActionEditCanceled(GuidedAction action);
-
-        /**
-         * Called when the user exits edit mode on an action and process confirm button in IME.
-         */
-        long onGuidedActionEditedAndProceed(GuidedAction action);
-
-        /**
-         * Called when Ime Open
-         */
-        void onImeOpen();
-
-        /**
-         * Called when Ime Close
-         */
-        void onImeClose();
-    }
-
-    private final boolean mIsSubAdapter;
-    private final ActionOnKeyListener mActionOnKeyListener;
-    private final ActionOnFocusListener mActionOnFocusListener;
-    private final ActionEditListener mActionEditListener;
-    private final List<GuidedAction> mActions;
-    private ClickListener mClickListener;
-    final GuidedActionsStylist mStylist;
-    GuidedActionAdapterGroup mGroup;
-    DiffCallback<GuidedAction> mDiffCallback;
-
-    private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (v != null && v.getWindowToken() != null && getRecyclerView() != null) {
-                GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)
-                        getRecyclerView().getChildViewHolder(v);
-                GuidedAction action = avh.getAction();
-                if (action.hasTextEditable()) {
-                    if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme by click");
-                    mGroup.openIme(GuidedActionAdapter.this, avh);
-                } else if (action.hasEditableActivatorView()) {
-                    if (DEBUG_EDIT) Log.v(TAG_EDIT, "toggle editing mode by click");
-                    performOnActionClick(avh);
-                } else {
-                    handleCheckedActions(avh);
-                    if (action.isEnabled() && !action.infoOnly()) {
-                        performOnActionClick(avh);
-                    }
-                }
-            }
-        }
-    };
-
-    /**
-     * Constructs a GuidedActionAdapter with the given list of guided actions, the given click and
-     * focus listeners, and the given presenter.
-     * @param actions The list of guided actions this adapter will manage.
-     * @param focusListener The focus listener for items in this adapter.
-     * @param presenter The presenter that will manage the display of items in this adapter.
-     */
-    public GuidedActionAdapter(List<GuidedAction> actions, ClickListener clickListener,
-            FocusListener focusListener, GuidedActionsStylist presenter, boolean isSubAdapter) {
-        super();
-        mActions = actions == null ? new ArrayList<GuidedAction>() :
-                new ArrayList<GuidedAction>(actions);
-        mClickListener = clickListener;
-        mStylist = presenter;
-        mActionOnKeyListener = new ActionOnKeyListener();
-        mActionOnFocusListener = new ActionOnFocusListener(focusListener);
-        mActionEditListener = new ActionEditListener();
-        mIsSubAdapter = isSubAdapter;
-        if (!isSubAdapter) {
-            mDiffCallback = GuidedActionDiffCallback.getInstance();
-        }
-    }
-
-    /**
-     * Change DiffCallback used in {@link #setActions(List)}. Set to null for firing a
-     * general {@link #notifyDataSetChanged()}.
-     *
-     * @param diffCallback
-     */
-    public void setDiffCallback(DiffCallback<GuidedAction> diffCallback) {
-        mDiffCallback = diffCallback;
-    }
-
-    /**
-     * Sets the list of actions managed by this adapter. Use {@link #setDiffCallback(DiffCallback)}
-     * to change DiffCallback.
-     * @param actions The list of actions to be managed.
-     */
-    public void setActions(final List<GuidedAction> actions) {
-        if (!mIsSubAdapter) {
-            mStylist.collapseAction(false);
-        }
-        mActionOnFocusListener.unFocus();
-        if (mDiffCallback != null) {
-            // temporary variable used for DiffCallback
-            final List<GuidedAction> oldActions = new ArrayList();
-            oldActions.addAll(mActions);
-
-            // update items.
-            mActions.clear();
-            mActions.addAll(actions);
-
-            DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
-                @Override
-                public int getOldListSize() {
-                    return oldActions.size();
-                }
-
-                @Override
-                public int getNewListSize() {
-                    return mActions.size();
-                }
-
-                @Override
-                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-                    return mDiffCallback.areItemsTheSame(oldActions.get(oldItemPosition),
-                            mActions.get(newItemPosition));
-                }
-
-                @Override
-                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
-                    return mDiffCallback.areContentsTheSame(oldActions.get(oldItemPosition),
-                            mActions.get(newItemPosition));
-                }
-
-                @Nullable
-                @Override
-                public Object getChangePayload(int oldItemPosition, int newItemPosition) {
-                    return mDiffCallback.getChangePayload(oldActions.get(oldItemPosition),
-                            mActions.get(newItemPosition));
-                }
-            });
-
-            // dispatch diff result
-            diffResult.dispatchUpdatesTo(this);
-        } else {
-            mActions.clear();
-            mActions.addAll(actions);
-            notifyDataSetChanged();
-        }
-    }
-
-    /**
-     * Returns the count of actions managed by this adapter.
-     * @return The count of actions managed by this adapter.
-     */
-    public int getCount() {
-        return mActions.size();
-    }
-
-    /**
-     * Returns the GuidedAction at the given position in the managed list.
-     * @param position The position of the desired GuidedAction.
-     * @return The GuidedAction at the given position.
-     */
-    public GuidedAction getItem(int position) {
-        return mActions.get(position);
-    }
-
-    /**
-     * Return index of action in array
-     * @param action Action to search index.
-     * @return Index of Action in array.
-     */
-    public int indexOf(GuidedAction action) {
-        return mActions.indexOf(action);
-    }
-
-    /**
-     * @return GuidedActionsStylist used to build the actions list UI.
-     */
-    public GuidedActionsStylist getGuidedActionsStylist() {
-        return mStylist;
-    }
-
-    /**
-     * Sets the click listener for items managed by this adapter.
-     * @param clickListener The click listener for this adapter.
-     */
-    public void setClickListener(ClickListener clickListener) {
-        mClickListener = clickListener;
-    }
-
-    /**
-     * Sets the focus listener for items managed by this adapter.
-     * @param focusListener The focus listener for this adapter.
-     */
-    public void setFocusListener(FocusListener focusListener) {
-        mActionOnFocusListener.setFocusListener(focusListener);
-    }
-
-    /**
-     * Used for serialization only.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public List<GuidedAction> getActions() {
-        return new ArrayList<GuidedAction>(mActions);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getItemViewType(int position) {
-        return mStylist.getItemViewType(mActions.get(position));
-    }
-
-    RecyclerView getRecyclerView() {
-        return mIsSubAdapter ? mStylist.getSubActionsGridView() : mStylist.getActionsGridView();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        GuidedActionsStylist.ViewHolder vh = mStylist.onCreateViewHolder(parent, viewType);
-        View v = vh.itemView;
-        v.setOnKeyListener(mActionOnKeyListener);
-        v.setOnClickListener(mOnClickListener);
-        v.setOnFocusChangeListener(mActionOnFocusListener);
-
-        setupListeners(vh.getEditableTitleView());
-        setupListeners(vh.getEditableDescriptionView());
-
-        return vh;
-    }
-
-    private void setupListeners(EditText edit) {
-        if (edit != null) {
-            edit.setPrivateImeOptions("EscapeNorth=1;");
-            edit.setOnEditorActionListener(mActionEditListener);
-            if (edit instanceof ImeKeyMonitor) {
-                ImeKeyMonitor monitor = (ImeKeyMonitor)edit;
-                monitor.setImeKeyListener(mActionEditListener);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onBindViewHolder(ViewHolder holder, int position) {
-        if (position >= mActions.size()) {
-            return;
-        }
-        final GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)holder;
-        GuidedAction action = mActions.get(position);
-        mStylist.onBindViewHolder(avh, action);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getItemCount() {
-        return mActions.size();
-    }
-
-    private class ActionOnFocusListener implements View.OnFocusChangeListener {
-
-        private FocusListener mFocusListener;
-        private View mSelectedView;
-
-        ActionOnFocusListener(FocusListener focusListener) {
-            mFocusListener = focusListener;
-        }
-
-        public void setFocusListener(FocusListener focusListener) {
-            mFocusListener = focusListener;
-        }
-
-        public void unFocus() {
-            if (mSelectedView != null && getRecyclerView() != null) {
-                ViewHolder vh = getRecyclerView().getChildViewHolder(mSelectedView);
-                if (vh != null) {
-                    GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)vh;
-                    mStylist.onAnimateItemFocused(avh, false);
-                } else {
-                    Log.w(TAG, "RecyclerView returned null view holder",
-                            new Throwable());
-                }
-            }
-        }
-
-        @Override
-        public void onFocusChange(View v, boolean hasFocus) {
-            if (getRecyclerView() == null) {
-                return;
-            }
-            GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)
-                    getRecyclerView().getChildViewHolder(v);
-            if (hasFocus) {
-                mSelectedView = v;
-                if (mFocusListener != null) {
-                    // We still call onGuidedActionFocused so that listeners can clear
-                    // state if they want.
-                    mFocusListener.onGuidedActionFocused(avh.getAction());
-                }
-            } else {
-                if (mSelectedView == v) {
-                    mStylist.onAnimateItemPressedCancelled(avh);
-                    mSelectedView = null;
-                }
-            }
-            mStylist.onAnimateItemFocused(avh, hasFocus);
-        }
-    }
-
-    public GuidedActionsStylist.ViewHolder findSubChildViewHolder(View v) {
-        // Needed because RecyclerView.getChildViewHolder does not traverse the hierarchy
-        if (getRecyclerView() == null) {
-            return null;
-        }
-        GuidedActionsStylist.ViewHolder result = null;
-        ViewParent parent = v.getParent();
-        while (parent != getRecyclerView() && parent != null && v != null) {
-            v = (View)parent;
-            parent = parent.getParent();
-        }
-        if (parent != null && v != null) {
-            result = (GuidedActionsStylist.ViewHolder)getRecyclerView().getChildViewHolder(v);
-        }
-        return result;
-    }
-
-    public void handleCheckedActions(GuidedActionsStylist.ViewHolder avh) {
-        GuidedAction action = avh.getAction();
-        int actionCheckSetId = action.getCheckSetId();
-        if (getRecyclerView() != null && actionCheckSetId != GuidedAction.NO_CHECK_SET) {
-            // Find any actions that are checked and are in the same group
-            // as the selected action. Fade their checkmarks out.
-            if (actionCheckSetId != GuidedAction.CHECKBOX_CHECK_SET_ID) {
-                for (int i = 0, size = mActions.size(); i < size; i++) {
-                    GuidedAction a = mActions.get(i);
-                    if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) {
-                        a.setChecked(false);
-                        GuidedActionsStylist.ViewHolder vh = (GuidedActionsStylist.ViewHolder)
-                                getRecyclerView().findViewHolderForPosition(i);
-                        if (vh != null) {
-                            mStylist.onAnimateItemChecked(vh, false);
-                        }
-                    }
-                }
-            }
-
-            // If we we'ren't already checked, fade our checkmark in.
-            if (!action.isChecked()) {
-                action.setChecked(true);
-                mStylist.onAnimateItemChecked(avh, true);
-            } else {
-                if (actionCheckSetId == GuidedAction.CHECKBOX_CHECK_SET_ID) {
-                    action.setChecked(false);
-                    mStylist.onAnimateItemChecked(avh, false);
-                }
-            }
-        }
-    }
-
-    public void performOnActionClick(GuidedActionsStylist.ViewHolder avh) {
-        if (mClickListener != null) {
-            mClickListener.onGuidedActionClicked(avh.getAction());
-        }
-    }
-
-    private class ActionOnKeyListener implements View.OnKeyListener {
-
-        private boolean mKeyPressed = false;
-
-        ActionOnKeyListener() {
-        }
-
-        /**
-         * Now only handles KEYCODE_ENTER and KEYCODE_NUMPAD_ENTER key event.
-         */
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent event) {
-            if (v == null || event == null || getRecyclerView() == null) {
-                return false;
-            }
-            boolean handled = false;
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_DPAD_CENTER:
-                case KeyEvent.KEYCODE_NUMPAD_ENTER:
-                case KeyEvent.KEYCODE_BUTTON_X:
-                case KeyEvent.KEYCODE_BUTTON_Y:
-                case KeyEvent.KEYCODE_ENTER:
-
-                    GuidedActionsStylist.ViewHolder avh = (GuidedActionsStylist.ViewHolder)
-                            getRecyclerView().getChildViewHolder(v);
-                    GuidedAction action = avh.getAction();
-
-                    if (!action.isEnabled() || action.infoOnly()) {
-                        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                            // TODO: requires API 19
-                            //playSound(v, AudioManager.FX_KEYPRESS_INVALID);
-                        }
-                        return true;
-                    }
-
-                    switch (event.getAction()) {
-                        case KeyEvent.ACTION_DOWN:
-                            if (DEBUG) {
-                                Log.d(TAG, "Enter Key down");
-                            }
-                            if (!mKeyPressed) {
-                                mKeyPressed = true;
-                                mStylist.onAnimateItemPressed(avh, mKeyPressed);
-                            }
-                            break;
-                        case KeyEvent.ACTION_UP:
-                            if (DEBUG) {
-                                Log.d(TAG, "Enter Key up");
-                            }
-                            // Sometimes we are losing ACTION_DOWN for the first ENTER after pressed
-                            // Escape in IME.
-                            if (mKeyPressed) {
-                                mKeyPressed = false;
-                                mStylist.onAnimateItemPressed(avh, mKeyPressed);
-                            }
-                            break;
-                        default:
-                            break;
-                    }
-                    break;
-                default:
-                    break;
-            }
-            return handled;
-        }
-
-    }
-
-    private class ActionEditListener implements OnEditorActionListener,
-            ImeKeyMonitor.ImeKeyListener {
-
-        ActionEditListener() {
-        }
-
-        @Override
-        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-            if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME action: " + actionId);
-            boolean handled = false;
-            if (actionId == EditorInfo.IME_ACTION_NEXT
-                    || actionId == EditorInfo.IME_ACTION_DONE) {
-                mGroup.fillAndGoNext(GuidedActionAdapter.this, v);
-                handled = true;
-            } else if (actionId == EditorInfo.IME_ACTION_NONE) {
-                if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme escape north");
-                // Escape north handling: stay on current item, but close editor
-                handled = true;
-                mGroup.fillAndStay(GuidedActionAdapter.this, v);
-            }
-            return handled;
-        }
-
-        @Override
-        public boolean onKeyPreIme(EditText editText, int keyCode, KeyEvent event) {
-            if (DEBUG_EDIT) Log.v(TAG_EDIT, "IME key: " + keyCode);
-            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
-                mGroup.fillAndStay(GuidedActionAdapter.this, editText);
-                return true;
-            } else if (keyCode == KeyEvent.KEYCODE_ENTER
-                    && event.getAction() == KeyEvent.ACTION_UP) {
-                mGroup.fillAndGoNext(GuidedActionAdapter.this, editText);
-                return true;
-            }
-            return false;
-        }
-
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java b/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java
deleted file mode 100644
index 287ae3b..0000000
--- a/android/support/v17/leanback/widget/GuidedActionAdapterGroup.java
+++ /dev/null
@@ -1,195 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.widget.GuidedActionAdapter.EditListener;
-import android.util.Log;
-import android.util.Pair;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * Internal implementation manages a group of GuidedActionAdapters, control the next action after
- * editing finished, maintain the Ime open/close status.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class GuidedActionAdapterGroup {
-
-    private static final String TAG_EDIT = "EditableAction";
-    private static final boolean DEBUG_EDIT = false;
-
-    ArrayList<Pair<GuidedActionAdapter, GuidedActionAdapter>> mAdapters =
-            new ArrayList<Pair<GuidedActionAdapter, GuidedActionAdapter>>();
-    private boolean mImeOpened;
-    private EditListener mEditListener;
-
-    public void addAdpter(GuidedActionAdapter adapter1, GuidedActionAdapter adapter2) {
-        mAdapters.add(new Pair<GuidedActionAdapter, GuidedActionAdapter>(adapter1, adapter2));
-        if (adapter1 != null) {
-            adapter1.mGroup = this;
-        }
-        if (adapter2 != null) {
-            adapter2.mGroup = this;
-        }
-    }
-
-    public GuidedActionAdapter getNextAdapter(GuidedActionAdapter adapter) {
-        for (int i = 0; i < mAdapters.size(); i++) {
-            Pair<GuidedActionAdapter, GuidedActionAdapter> pair = mAdapters.get(i);
-            if (pair.first == adapter) {
-                return pair.second;
-            }
-        }
-        return null;
-    }
-
-    public void setEditListener(EditListener listener) {
-        mEditListener = listener;
-    }
-
-    boolean focusToNextAction(GuidedActionAdapter adapter, GuidedAction action, long nextActionId) {
-        // for ACTION_ID_NEXT, we first find out the matching index in Actions list.
-        int index = 0;
-        if (nextActionId == GuidedAction.ACTION_ID_NEXT) {
-            index = adapter.indexOf(action);
-            if (index < 0) {
-                return false;
-            }
-            // start from next, if reach end, will go next Adapter below
-            index++;
-        }
-
-        do {
-            int size = adapter.getCount();
-            if (nextActionId == GuidedAction.ACTION_ID_NEXT) {
-                while (index < size && !adapter.getItem(index).isFocusable()) {
-                    index++;
-                }
-            } else {
-                while (index < size && adapter.getItem(index).getId() != nextActionId) {
-                    index++;
-                }
-            }
-            if (index < size) {
-                GuidedActionsStylist.ViewHolder vh =
-                        (GuidedActionsStylist.ViewHolder) adapter.getGuidedActionsStylist()
-                                .getActionsGridView().findViewHolderForPosition(index);
-                if (vh != null) {
-                    if (vh.getAction().hasTextEditable()) {
-                        if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme of next Action");
-                        // open Ime on next action.
-                        openIme(adapter, vh);
-                    } else {
-                        if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme and focus to next Action");
-                        // close IME and focus to next (not editable) action
-                        closeIme(vh.itemView);
-                        vh.itemView.requestFocus();
-                    }
-                    return true;
-                }
-                return false;
-            }
-            // search from index 0 of next Adapter
-            adapter = getNextAdapter(adapter);
-            if (adapter == null) {
-                break;
-            }
-            index = 0;
-        } while (true);
-        return false;
-    }
-
-    public void openIme(GuidedActionAdapter adapter, GuidedActionsStylist.ViewHolder avh) {
-        adapter.getGuidedActionsStylist().setEditingMode(avh, true);
-        View v = avh.getEditingView();
-        if (v == null || !avh.isInEditingText()) {
-            return;
-        }
-        InputMethodManager mgr = (InputMethodManager)
-                v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-        // Make the TextView focusable during editing, avoid the TextView gets accessibility focus
-        // before editing started. see also GuidedActionEditText where setFocusable(false).
-        v.setFocusable(true);
-        v.requestFocus();
-        mgr.showSoftInput(v, 0);
-        if (!mImeOpened) {
-            mImeOpened = true;
-            mEditListener.onImeOpen();
-        }
-    }
-
-    public void closeIme(View v) {
-        if (mImeOpened) {
-            mImeOpened = false;
-            InputMethodManager mgr = (InputMethodManager)
-                    v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-            mgr.hideSoftInputFromWindow(v.getWindowToken(), 0);
-            mEditListener.onImeClose();
-        }
-    }
-
-    public void fillAndStay(GuidedActionAdapter adapter, TextView v) {
-        GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v);
-        updateTextIntoAction(avh, v);
-        mEditListener.onGuidedActionEditCanceled(avh.getAction());
-        adapter.getGuidedActionsStylist().setEditingMode(avh, false);
-        closeIme(v);
-        avh.itemView.requestFocus();
-    }
-
-    public void fillAndGoNext(GuidedActionAdapter adapter, TextView v) {
-        boolean handled = false;
-        GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v);
-        updateTextIntoAction(avh, v);
-        adapter.performOnActionClick(avh);
-        long nextActionId = mEditListener.onGuidedActionEditedAndProceed(avh.getAction());
-        adapter.getGuidedActionsStylist().setEditingMode(avh, false);
-        if (nextActionId != GuidedAction.ACTION_ID_CURRENT
-                && nextActionId != avh.getAction().getId()) {
-            handled = focusToNextAction(adapter, avh.getAction(), nextActionId);
-        }
-        if (!handled) {
-            if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme no next action");
-            handled = true;
-            closeIme(v);
-            avh.itemView.requestFocus();
-        }
-    }
-
-    private void updateTextIntoAction(GuidedActionsStylist.ViewHolder avh, TextView v) {
-        GuidedAction action = avh.getAction();
-        if (v == avh.getDescriptionView()) {
-            if (action.getEditDescription() != null) {
-                action.setEditDescription(v.getText());
-            } else {
-                action.setDescription(v.getText());
-            }
-        } else if (v == avh.getTitleView()) {
-            if (action.getEditTitle() != null) {
-                action.setEditTitle(v.getText());
-            } else {
-                action.setTitle(v.getText());
-            }
-        }
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionDiffCallback.java b/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
deleted file mode 100644
index d4d4d77..0000000
--- a/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.support.v17.leanback.widget;
-
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-
-/**
- * DiffCallback used for GuidedActions, see {@link
- * android.support.v17.leanback.app.GuidedStepSupportFragment#setActionsDiffCallback(DiffCallback)}.
- */
-public class GuidedActionDiffCallback extends DiffCallback<GuidedAction> {
-
-    static final GuidedActionDiffCallback sInstance = new GuidedActionDiffCallback();
-
-    /**
-     * Returns the singleton GuidedActionDiffCallback.
-     * @return The singleton GuidedActionDiffCallback.
-     */
-    public static final GuidedActionDiffCallback getInstance() {
-        return sInstance;
-    }
-
-    @Override
-    public boolean areItemsTheSame(@NonNull GuidedAction oldItem, @NonNull GuidedAction newItem) {
-        if (oldItem == null) {
-            return newItem == null;
-        } else if (newItem == null) {
-            return false;
-        }
-        return oldItem.getId() == newItem.getId();
-    }
-
-    @Override
-    public boolean areContentsTheSame(@NonNull GuidedAction oldItem,
-            @NonNull GuidedAction newItem) {
-        if (oldItem == null) {
-            return newItem == null;
-        } else if (newItem == null) {
-            return false;
-        }
-        return oldItem.getCheckSetId() == newItem.getCheckSetId()
-                && oldItem.mActionFlags == newItem.mActionFlags
-                && TextUtils.equals(oldItem.getTitle(), newItem.getTitle())
-                && TextUtils.equals(oldItem.getDescription(), newItem.getDescription())
-                && oldItem.getInputType() == newItem.getInputType()
-                && TextUtils.equals(oldItem.getEditTitle(), newItem.getEditTitle())
-                && TextUtils.equals(oldItem.getEditDescription(), newItem.getEditDescription())
-                && oldItem.getEditInputType() == newItem.getEditInputType()
-                && oldItem.getDescriptionEditInputType() == newItem.getDescriptionEditInputType();
-    }
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionEditText.java b/android/support/v17/leanback/widget/GuidedActionEditText.java
deleted file mode 100644
index f6a0eab..0000000
--- a/android/support/v17/leanback/widget/GuidedActionEditText.java
+++ /dev/null
@@ -1,118 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.EditText;
-import android.widget.TextView;
-
-/**
- * A custom EditText that satisfies the IME key monitoring requirements of GuidedStepFragment.
- */
-public class GuidedActionEditText extends EditText implements ImeKeyMonitor {
-
-    /**
-     * Workaround for b/26990627 forcing recompute the padding for the View when we turn on/off
-     * the default background of EditText
-     */
-    static final class NoPaddingDrawable extends Drawable {
-        @Override
-        public boolean getPadding(Rect padding) {
-            padding.set(0, 0, 0, 0);
-            return true;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-        }
-
-        @Override
-        public void setAlpha(int alpha) {
-        }
-
-        @Override
-        public void setColorFilter(ColorFilter colorFilter) {
-        }
-
-        @Override
-        public int getOpacity() {
-            return PixelFormat.TRANSPARENT;
-        }
-    }
-
-    private ImeKeyListener mKeyListener;
-    private final Drawable mSavedBackground;
-    private final Drawable mNoPaddingDrawable;
-
-    public GuidedActionEditText(Context ctx) {
-        this(ctx, null);
-    }
-
-    public GuidedActionEditText(Context ctx, AttributeSet attrs) {
-        this(ctx, attrs, android.R.attr.editTextStyle);
-    }
-
-    public GuidedActionEditText(Context ctx, AttributeSet attrs, int defStyleAttr) {
-        super(ctx, attrs, defStyleAttr);
-        mSavedBackground = getBackground();
-        mNoPaddingDrawable = new NoPaddingDrawable();
-        setBackground(mNoPaddingDrawable);
-    }
-
-    @Override
-    public void setImeKeyListener(ImeKeyListener listener) {
-        mKeyListener = listener;
-    }
-
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        boolean result = false;
-        if (mKeyListener != null) {
-            result = mKeyListener.onKeyPreIme(this, keyCode, event);
-        }
-        if (!result) {
-            result = super.onKeyPreIme(keyCode, event);
-        }
-        return result;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(isFocused() ? EditText.class.getName() : TextView.class.getName());
-    }
-
-    @Override
-    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
-        if (focused) {
-            setBackground(mSavedBackground);
-        } else {
-            setBackground(mNoPaddingDrawable);
-        }
-        // Make the TextView focusable during editing, avoid the TextView gets accessibility focus
-        // before editing started. see also GuidedActionAdapterGroup where setFocusable(true).
-        if (!focused) {
-            setFocusable(false);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionItemContainer.java b/android/support/v17/leanback/widget/GuidedActionItemContainer.java
deleted file mode 100644
index bbb0865..0000000
--- a/android/support/v17/leanback/widget/GuidedActionItemContainer.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Root view of GuidedAction item, it supports a foreground drawable and can disable focus out
- * of view.
- */
-class GuidedActionItemContainer extends NonOverlappingLinearLayoutWithForeground {
-
-    private boolean mFocusOutAllowed = true;
-
-    public GuidedActionItemContainer(Context context) {
-        this(context, null);
-    }
-
-    public GuidedActionItemContainer(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public GuidedActionItemContainer(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    public View focusSearch(View focused, int direction) {
-        if (mFocusOutAllowed || !Util.isDescendant(this, focused)) {
-            return super.focusSearch(focused, direction);
-        }
-        View view = super.focusSearch(focused, direction);
-        if (Util.isDescendant(this, view)) {
-            return view;
-        }
-        return null;
-    }
-
-    public void setFocusOutAllowed(boolean focusOutAllowed) {
-        mFocusOutAllowed = focusOutAllowed;
-    }
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionsRelativeLayout.java b/android/support/v17/leanback/widget/GuidedActionsRelativeLayout.java
deleted file mode 100644
index 6870b48..0000000
--- a/android/support/v17/leanback/widget/GuidedActionsRelativeLayout.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-
-/**
- * Relative layout implementation that assign subactions list topMargin based on a percentage
- * given by "guidedStepKeyline" theme attribute when the topMargin is set to a negative value.
- */
-class GuidedActionsRelativeLayout extends RelativeLayout {
-
-    interface InterceptKeyEventListener {
-        public boolean onInterceptKeyEvent(KeyEvent event);
-    }
-
-    private float mKeyLinePercent;
-    private boolean mInOverride = false;
-    private InterceptKeyEventListener mInterceptKeyEventListener;
-
-    public GuidedActionsRelativeLayout(Context context) {
-        this(context, null);
-    }
-
-    public GuidedActionsRelativeLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public GuidedActionsRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mKeyLinePercent = GuidanceStylingRelativeLayout.getKeyLinePercent(context);
-    }
-
-    private void init() {
-        TypedArray ta = getContext().getTheme().obtainStyledAttributes(
-                R.styleable.LeanbackGuidedStepTheme);
-        mKeyLinePercent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline,
-                40);
-        ta.recycle();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        if (heightSize > 0) {
-            View view = findViewById(R.id.guidedactions_sub_list);
-            if (view != null) {
-                ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
-                        view.getLayoutParams();
-                if (lp.topMargin < 0 && !mInOverride) {
-                    mInOverride = true;
-                }
-                if (mInOverride) {
-                    lp.topMargin = (int) (mKeyLinePercent * heightSize / 100);
-                }
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        mInOverride = false;
-    }
-
-    public void setInterceptKeyEventListener(InterceptKeyEventListener l) {
-        mInterceptKeyEventListener = l;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (mInterceptKeyEventListener != null) {
-            if (mInterceptKeyEventListener.onInterceptKeyEvent(event)) {
-                return true;
-            }
-        }
-        return super.dispatchKeyEvent(event);
-    }
-}
diff --git a/android/support/v17/leanback/widget/GuidedActionsStylist.java b/android/support/v17/leanback/widget/GuidedActionsStylist.java
deleted file mode 100644
index a2ece56..0000000
--- a/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ /dev/null
@@ -1,1523 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_ACTIVATOR_VIEW;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_DESCRIPTION;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_NONE;
-import static android.support.v17.leanback.widget.GuidedAction.EDITING_TITLE;
-
-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;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION;
-import android.support.annotation.CallSuper;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.transition.TransitionEpicenterCallback;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v17.leanback.transition.TransitionListener;
-import android.support.v17.leanback.widget.GuidedActionAdapter.EditListener;
-import android.support.v17.leanback.widget.picker.DatePicker;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.RecyclerView;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.inputmethod.EditorInfo;
-import android.widget.Checkable;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * GuidedActionsStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
- * to supply the right-side panel where users can take actions. It consists of a container for the
- * list of actions, and a stationary selector view that indicates visually the location of focus.
- * GuidedActionsStylist has two different layouts: default is for normal actions including text,
- * radio, checkbox, DatePicker, etc, the other when {@link #setAsButtonActions()} is called is
- * recommended for button actions such as "yes", "no".
- * <p>
- * Many aspects of the base GuidedActionsStylist can be customized through theming; see the
- * theme attributes below. Note that these attributes are not set on individual elements in layout
- * XML, but instead would be set in a custom theme. See
- * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
- * for more information.
- * <p>
- * If these hooks are insufficient, this class may also be subclassed. Subclasses may wish to
- * override the {@link #onProvideLayoutId} method to change the layout used to display the
- * list container and selector; override {@link #onProvideItemLayoutId(int)} and
- * {@link #getItemViewType(GuidedAction)} method to change the layout used to display each action.
- * <p>
- * To support a "click to activate" view similar to DatePicker, app needs:
- * <li> Override {@link #onProvideItemLayoutId(int)} and {@link #getItemViewType(GuidedAction)},
- * provides a layout id for the action.
- * <li> The layout must include a widget with id "guidedactions_activator_item", the widget is
- * toggled edit mode by {@link View#setActivated(boolean)}.
- * <li> Override {@link #onBindActivatorView(ViewHolder, GuidedAction)} to populate values into View.
- * <li> Override {@link #onUpdateActivatorView(ViewHolder, GuidedAction)} to update action.
- * <p>
- * Note: If an alternate list layout is provided, the following view IDs must be supplied:
- * <ul>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_list}</li>
- * </ul><p>
- * These view IDs must be present in order for the stylist to function. The list ID must correspond
- * to a {@link VerticalGridView} or subclass.
- * <p>
- * If an alternate item layout is provided, the following view IDs should be used to refer to base
- * elements:
- * <ul>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_content}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_title}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_description}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_icon}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_checkmark}</li>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_chevron}</li>
- * </ul><p>
- * These view IDs are allowed to be missing, in which case the corresponding views in {@link
- * GuidedActionsStylist.ViewHolder} will be null.
- * <p>
- * In order to support editable actions, the view associated with guidedactions_item_title should
- * be a subclass of {@link android.widget.EditText}, and should satisfy the {@link
- * ImeKeyMonitor} interface.
- *
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorDrawable
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedSubActionsListStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedButtonActionsListStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContainerStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemCheckmarkStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemIconStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContentStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemTitleStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemDescriptionStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemChevronStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionPressedAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUnpressedAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionEnabledChevronAlpha
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDisabledChevronAlpha
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMinLines
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMaxLines
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDescriptionMinLines
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionVerticalPadding
- * @see android.R.styleable#Theme_listChoiceIndicatorSingle
- * @see android.R.styleable#Theme_listChoiceIndicatorMultiple
- * @see android.support.v17.leanback.app.GuidedStepFragment
- * @see GuidedAction
- */
-public class GuidedActionsStylist implements FragmentAnimationProvider {
-
-    /**
-     * Default viewType that associated with default layout Id for the action item.
-     * @see #getItemViewType(GuidedAction)
-     * @see #onProvideItemLayoutId(int)
-     * @see #onCreateViewHolder(ViewGroup, int)
-     */
-    public static final int VIEW_TYPE_DEFAULT = 0;
-
-    /**
-     * ViewType for DatePicker.
-     */
-    public static final int VIEW_TYPE_DATE_PICKER = 1;
-
-    final static ItemAlignmentFacet sGuidedActionItemAlignFacet;
-
-    static {
-        sGuidedActionItemAlignFacet = new ItemAlignmentFacet();
-        ItemAlignmentFacet.ItemAlignmentDef alignedDef = new ItemAlignmentFacet.ItemAlignmentDef();
-        alignedDef.setItemAlignmentViewId(R.id.guidedactions_item_title);
-        alignedDef.setAlignedToTextViewBaseline(true);
-        alignedDef.setItemAlignmentOffset(0);
-        alignedDef.setItemAlignmentOffsetWithPadding(true);
-        alignedDef.setItemAlignmentOffsetPercent(0);
-        sGuidedActionItemAlignFacet.setAlignmentDefs(new ItemAlignmentFacet.ItemAlignmentDef[]{alignedDef});
-    }
-
-    /**
-     * ViewHolder caches information about the action item layouts' subviews. Subclasses of {@link
-     * GuidedActionsStylist} may also wish to subclass this in order to add fields.
-     * @see GuidedAction
-     */
-    public static class ViewHolder extends RecyclerView.ViewHolder implements FacetProvider {
-
-        GuidedAction mAction;
-        private View mContentView;
-        TextView mTitleView;
-        TextView mDescriptionView;
-        View mActivatorView;
-        ImageView mIconView;
-        ImageView mCheckmarkView;
-        ImageView mChevronView;
-        int mEditingMode = EDITING_NONE;
-        private final boolean mIsSubAction;
-        Animator mPressAnimator;
-
-        final AccessibilityDelegate mDelegate = new AccessibilityDelegate() {
-            @Override
-            public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-                super.onInitializeAccessibilityEvent(host, event);
-                event.setChecked(mAction != null && mAction.isChecked());
-            }
-
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                info.setCheckable(
-                        mAction != null && mAction.getCheckSetId() != GuidedAction.NO_CHECK_SET);
-                info.setChecked(mAction != null && mAction.isChecked());
-            }
-        };
-
-        /**
-         * Constructs an ViewHolder and caches the relevant subviews.
-         */
-        public ViewHolder(View v) {
-            this(v, false);
-        }
-
-        /**
-         * Constructs an ViewHolder for sub action and caches the relevant subviews.
-         */
-        public ViewHolder(View v, boolean isSubAction) {
-            super(v);
-
-            mContentView = v.findViewById(R.id.guidedactions_item_content);
-            mTitleView = (TextView) v.findViewById(R.id.guidedactions_item_title);
-            mActivatorView = v.findViewById(R.id.guidedactions_activator_item);
-            mDescriptionView = (TextView) v.findViewById(R.id.guidedactions_item_description);
-            mIconView = (ImageView) v.findViewById(R.id.guidedactions_item_icon);
-            mCheckmarkView = (ImageView) v.findViewById(R.id.guidedactions_item_checkmark);
-            mChevronView = (ImageView) v.findViewById(R.id.guidedactions_item_chevron);
-            mIsSubAction = isSubAction;
-
-            v.setAccessibilityDelegate(mDelegate);
-        }
-
-        /**
-         * Returns the content view within this view holder's view, where title and description are
-         * shown.
-         */
-        public View getContentView() {
-            return mContentView;
-        }
-
-        /**
-         * Returns the title view within this view holder's view.
-         */
-        public TextView getTitleView() {
-            return mTitleView;
-        }
-
-        /**
-         * Convenience method to return an editable version of the title, if possible,
-         * or null if the title view isn't an EditText.
-         */
-        public EditText getEditableTitleView() {
-            return (mTitleView instanceof EditText) ? (EditText)mTitleView : null;
-        }
-
-        /**
-         * Returns the description view within this view holder's view.
-         */
-        public TextView getDescriptionView() {
-            return mDescriptionView;
-        }
-
-        /**
-         * Convenience method to return an editable version of the description, if possible,
-         * or null if the description view isn't an EditText.
-         */
-        public EditText getEditableDescriptionView() {
-            return (mDescriptionView instanceof EditText) ? (EditText)mDescriptionView : null;
-        }
-
-        /**
-         * Returns the icon view within this view holder's view.
-         */
-        public ImageView getIconView() {
-            return mIconView;
-        }
-
-        /**
-         * Returns the checkmark view within this view holder's view.
-         */
-        public ImageView getCheckmarkView() {
-            return mCheckmarkView;
-        }
-
-        /**
-         * Returns the chevron view within this view holder's view.
-         */
-        public ImageView getChevronView() {
-            return mChevronView;
-        }
-
-        /**
-         * Returns true if in editing title, description, or activator View, false otherwise.
-         */
-        public boolean isInEditing() {
-            return mEditingMode != EDITING_NONE;
-        }
-
-        /**
-         * Returns true if in editing title, description, so IME would be open.
-         * @return True if in editing title, description, so IME would be open, false otherwise.
-         */
-        public boolean isInEditingText() {
-            return mEditingMode == EDITING_TITLE || mEditingMode == EDITING_DESCRIPTION;
-        }
-
-        /**
-         * Returns true if the TextView is in editing title, false otherwise.
-         */
-        public boolean isInEditingTitle() {
-            return mEditingMode == EDITING_TITLE;
-        }
-
-        /**
-         * Returns true if the TextView is in editing description, false otherwise.
-         */
-        public boolean isInEditingDescription() {
-            return mEditingMode == EDITING_DESCRIPTION;
-        }
-
-        /**
-         * Returns true if is in editing activator view with id guidedactions_activator_item, false
-         * otherwise.
-         */
-        public boolean isInEditingActivatorView() {
-            return mEditingMode == EDITING_ACTIVATOR_VIEW;
-        }
-
-        /**
-         * @return Current editing title view or description view or activator view or null if not
-         * in editing.
-         */
-        public View getEditingView() {
-            switch(mEditingMode) {
-            case EDITING_TITLE:
-                return mTitleView;
-            case EDITING_DESCRIPTION:
-                return mDescriptionView;
-            case EDITING_ACTIVATOR_VIEW:
-                return mActivatorView;
-            case EDITING_NONE:
-            default:
-                return null;
-            }
-        }
-
-        /**
-         * @return True if bound action is inside {@link GuidedAction#getSubActions()}, false
-         * otherwise.
-         */
-        public boolean isSubAction() {
-            return mIsSubAction;
-        }
-
-        /**
-         * @return Currently bound action.
-         */
-        public GuidedAction getAction() {
-            return mAction;
-        }
-
-        void setActivated(boolean activated) {
-            mActivatorView.setActivated(activated);
-            if (itemView instanceof GuidedActionItemContainer) {
-                ((GuidedActionItemContainer) itemView).setFocusOutAllowed(!activated);
-            }
-        }
-
-        @Override
-        public Object getFacet(Class<?> facetClass) {
-            if (facetClass == ItemAlignmentFacet.class) {
-                return sGuidedActionItemAlignFacet;
-            }
-            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";
-
-    ViewGroup mMainView;
-    private VerticalGridView mActionsGridView;
-    VerticalGridView mSubActionsGridView;
-    private View mSubActionsBackground;
-    private View mBgView;
-    private View mContentView;
-    private boolean mButtonActions;
-
-    // Cached values from resources
-    private float mEnabledTextAlpha;
-    private float mDisabledTextAlpha;
-    private float mEnabledDescriptionAlpha;
-    private float mDisabledDescriptionAlpha;
-    private float mEnabledChevronAlpha;
-    private float mDisabledChevronAlpha;
-    private int mTitleMinLines;
-    private int mTitleMaxLines;
-    private int mDescriptionMinLines;
-    private int mVerticalPadding;
-    private int mDisplayHeight;
-
-    private EditListener mEditListener;
-
-    private GuidedAction mExpandedAction = null;
-    Object mExpandTransition;
-    private boolean mBackToCollapseSubActions = true;
-    private boolean mBackToCollapseActivatorView = true;
-
-    private float mKeyLinePercent;
-
-    /**
-     * Creates a view appropriate for displaying a list of GuidedActions, using the provided
-     * inflater and container.
-     * <p>
-     * <i>Note: Does not actually add the created view to the container; the caller should do
-     * this.</i>
-     * @param inflater The layout inflater to be used when constructing the view.
-     * @param container The view group to be passed in the call to
-     * <code>LayoutInflater.inflate</code>.
-     * @return The view to be added to the caller's view hierarchy.
-     */
-    public View onCreateView(LayoutInflater inflater, final ViewGroup container) {
-        TypedArray ta = inflater.getContext().getTheme().obtainStyledAttributes(
-                R.styleable.LeanbackGuidedStepTheme);
-        float keylinePercent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline,
-                40);
-        mMainView = (ViewGroup) inflater.inflate(onProvideLayoutId(), container, false);
-        mContentView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_content2 :
-                R.id.guidedactions_content);
-        mBgView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_list_background2 :
-                R.id.guidedactions_list_background);
-        if (mMainView instanceof VerticalGridView) {
-            mActionsGridView = (VerticalGridView) mMainView;
-        } else {
-            mActionsGridView = (VerticalGridView) mMainView.findViewById(mButtonActions
-                    ? R.id.guidedactions_list2 : R.id.guidedactions_list);
-            if (mActionsGridView == null) {
-                throw new IllegalStateException("No ListView exists.");
-            }
-            mActionsGridView.setWindowAlignmentOffsetPercent(keylinePercent);
-            mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-            if (!mButtonActions) {
-                mSubActionsGridView = (VerticalGridView) mMainView.findViewById(
-                        R.id.guidedactions_sub_list);
-                mSubActionsBackground = mMainView.findViewById(
-                        R.id.guidedactions_sub_list_background);
-            }
-        }
-        mActionsGridView.setFocusable(false);
-        mActionsGridView.setFocusableInTouchMode(false);
-
-        // Cache widths, chevron alpha values, max and min text lines, etc
-        Context ctx = mMainView.getContext();
-        TypedValue val = new TypedValue();
-        mEnabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionEnabledChevronAlpha);
-        mDisabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionDisabledChevronAlpha);
-        mTitleMinLines = getInteger(ctx, val, R.attr.guidedActionTitleMinLines);
-        mTitleMaxLines = getInteger(ctx, val, R.attr.guidedActionTitleMaxLines);
-        mDescriptionMinLines = getInteger(ctx, val, R.attr.guidedActionDescriptionMinLines);
-        mVerticalPadding = getDimension(ctx, val, R.attr.guidedActionVerticalPadding);
-        mDisplayHeight = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE))
-                .getDefaultDisplay().getHeight();
-
-        mEnabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string
-                .lb_guidedactions_item_unselected_text_alpha));
-        mDisabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string
-                .lb_guidedactions_item_disabled_text_alpha));
-        mEnabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
-                .lb_guidedactions_item_unselected_description_text_alpha));
-        mDisabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
-                .lb_guidedactions_item_disabled_description_text_alpha));
-
-        mKeyLinePercent = GuidanceStylingRelativeLayout.getKeyLinePercent(ctx);
-        if (mContentView instanceof GuidedActionsRelativeLayout) {
-            ((GuidedActionsRelativeLayout) mContentView).setInterceptKeyEventListener(
-                    new GuidedActionsRelativeLayout.InterceptKeyEventListener() {
-                        @Override
-                        public boolean onInterceptKeyEvent(KeyEvent event) {
-                            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
-                                    && event.getAction() == KeyEvent.ACTION_UP
-                                    && mExpandedAction != null) {
-                                if ((mExpandedAction.hasSubActions()
-                                        && isBackKeyToCollapseSubActions())
-                                        || (mExpandedAction.hasEditableActivatorView()
-                                        && isBackKeyToCollapseActivatorView())) {
-                                    collapseAction(true);
-                                    return true;
-                                }
-                            }
-                            return false;
-                        }
-                    }
-            );
-        }
-        return mMainView;
-    }
-
-    /**
-     * Choose the layout resource for button actions in {@link #onProvideLayoutId()}.
-     */
-    public void setAsButtonActions() {
-        if (mMainView != null) {
-            throw new IllegalStateException("setAsButtonActions() must be called before creating "
-                    + "views");
-        }
-        mButtonActions = true;
-    }
-
-    /**
-     * Returns true if it is button actions list, false for normal actions list.
-     * @return True if it is button actions list, false for normal actions list.
-     */
-    public boolean isButtonActions() {
-        return mButtonActions;
-    }
-
-    /**
-     * Called when destroy the View created by GuidedActionsStylist.
-     */
-    public void onDestroyView() {
-        mExpandedAction = null;
-        mExpandTransition = null;
-        mActionsGridView = null;
-        mSubActionsGridView = null;
-        mSubActionsBackground = null;
-        mContentView = null;
-        mBgView = null;
-        mMainView = null;
-    }
-
-    /**
-     * Returns the VerticalGridView that displays the list of GuidedActions.
-     * @return The VerticalGridView for this presenter.
-     */
-    public VerticalGridView getActionsGridView() {
-        return mActionsGridView;
-    }
-
-    /**
-     * Returns the VerticalGridView that displays the sub actions list of an expanded action.
-     * @return The VerticalGridView that displays the sub actions list of an expanded action.
-     */
-    public VerticalGridView getSubActionsGridView() {
-        return mSubActionsGridView;
-    }
-
-    /**
-     * Provides the resource ID of the layout defining the host view for the list of guided actions.
-     * Subclasses may override to provide their own customized layouts. The base implementation
-     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions} or
-     * {@link android.support.v17.leanback.R.layout#lb_guidedbuttonactions} if
-     * {@link #isButtonActions()} is true. If overridden, the substituted layout should contain
-     * matching IDs for any views that should be managed by the base class; this can be achieved by
-     * starting with a copy of the base layout file.
-     *
-     * @return The resource ID of the layout to be inflated to define the host view for the list of
-     *         GuidedActions.
-     */
-    public int onProvideLayoutId() {
-        return mButtonActions ? R.layout.lb_guidedbuttonactions : R.layout.lb_guidedactions;
-    }
-
-    /**
-     * Return view type of action, each different type can have differently associated layout Id.
-     * Default implementation returns {@link #VIEW_TYPE_DEFAULT}.
-     * @param action  The action object.
-     * @return View type that used in {@link #onProvideItemLayoutId(int)}.
-     */
-    public int getItemViewType(GuidedAction action) {
-        if (action instanceof GuidedDatePickerAction) {
-            return VIEW_TYPE_DATE_PICKER;
-        }
-        return VIEW_TYPE_DEFAULT;
-    }
-
-    /**
-     * Provides the resource ID of the layout defining the view for an individual guided actions.
-     * Subclasses may override to provide their own customized layouts. The base implementation
-     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden,
-     * the substituted layout should contain matching IDs for any views that should be managed by
-     * the base class; this can be achieved by starting with a copy of the base layout file. Note
-     * that in order for the item to support editing, the title view should both subclass {@link
-     * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link
-     * GuidedActionEditText}.  To support different types of Layouts, override {@link
-     * #onProvideItemLayoutId(int)}.
-     * @return The resource ID of the layout to be inflated to define the view to display an
-     * individual GuidedAction.
-     */
-    public int onProvideItemLayoutId() {
-        return R.layout.lb_guidedactions_item;
-    }
-
-    /**
-     * Provides the resource ID of the layout defining the view for an individual guided actions.
-     * Subclasses may override to provide their own customized layouts. The base implementation
-     * supports:
-     * <li>{@link android.support.v17.leanback.R.layout#lb_guidedactions_item}
-     * <li>{{@link android.support.v17.leanback.R.layout#lb_guidedactions_datepicker_item}. If
-     * overridden, the substituted layout should contain matching IDs for any views that should be
-     * managed by the base class; this can be achieved by starting with a copy of the base layout
-     * file. Note that in order for the item to support editing, the title view should both subclass
-     * {@link android.widget.EditText} and implement {@link ImeKeyMonitor}; see
-     * {@link GuidedActionEditText}.
-     *
-     * @param viewType View type returned by {@link #getItemViewType(GuidedAction)}
-     * @return The resource ID of the layout to be inflated to define the view to display an
-     *         individual GuidedAction.
-     */
-    public int onProvideItemLayoutId(int viewType) {
-        if (viewType == VIEW_TYPE_DEFAULT) {
-            return onProvideItemLayoutId();
-        } else if (viewType == VIEW_TYPE_DATE_PICKER) {
-            return R.layout.lb_guidedactions_datepicker_item;
-        } else {
-            throw new RuntimeException("ViewType " + viewType
-                    + " not supported in GuidedActionsStylist");
-        }
-    }
-
-    /**
-     * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
-     * may choose to return a subclass of ViewHolder.  To support different view types, override
-     * {@link #onCreateViewHolder(ViewGroup, int)}
-     * <p>
-     * <i>Note: Should not actually add the created view to the parent; the caller will do
-     * this.</i>
-     * @param parent The view group to be used as the parent of the new view.
-     * @return The view to be added to the caller's view hierarchy.
-     */
-    public ViewHolder onCreateViewHolder(ViewGroup parent) {
-        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-        View v = inflater.inflate(onProvideItemLayoutId(), parent, false);
-        return new ViewHolder(v, parent == mSubActionsGridView);
-    }
-
-    /**
-     * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
-     * may choose to return a subclass of ViewHolder.
-     * <p>
-     * <i>Note: Should not actually add the created view to the parent; the caller will do
-     * this.</i>
-     * @param parent The view group to be used as the parent of the new view.
-     * @param viewType The viewType returned by {@link #getItemViewType(GuidedAction)}
-     * @return The view to be added to the caller's view hierarchy.
-     */
-    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        if (viewType == VIEW_TYPE_DEFAULT) {
-            return onCreateViewHolder(parent);
-        }
-        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-        View v = inflater.inflate(onProvideItemLayoutId(viewType), parent, false);
-        return new ViewHolder(v, parent == mSubActionsGridView);
-    }
-
-    /**
-     * Binds a {@link ViewHolder} to a particular {@link GuidedAction}.
-     * @param vh The view holder to be associated with the given action.
-     * @param action The guided action to be displayed by the view holder's view.
-     * @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);
-            vh.mTitleView.setClickable(false);
-            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);
-            vh.mDescriptionView.setAlpha(action.isEnabled() ? mEnabledDescriptionAlpha :
-                mDisabledDescriptionAlpha);
-            vh.mDescriptionView.setFocusable(false);
-            vh.mDescriptionView.setClickable(false);
-            vh.mDescriptionView.setLongClickable(false);
-        }
-        // Clients might want the check mark view to be gone entirely, in which case, ignore it.
-        if (vh.mCheckmarkView != null) {
-            onBindCheckMarkView(vh, action);
-        }
-        setIcon(vh.mIconView, action);
-
-        if (action.hasMultilineDescription()) {
-            if (vh.mTitleView != null) {
-                setMaxLines(vh.mTitleView, mTitleMaxLines);
-                vh.mTitleView.setInputType(
-                        vh.mTitleView.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
-                if (vh.mDescriptionView != null) {
-                    vh.mDescriptionView.setInputType(vh.mDescriptionView.getInputType()
-                            | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
-                    vh.mDescriptionView.setMaxHeight(getDescriptionMaxHeight(
-                            vh.itemView.getContext(), vh.mTitleView));
-                }
-            }
-        } else {
-            if (vh.mTitleView != null) {
-                setMaxLines(vh.mTitleView, mTitleMinLines);
-            }
-            if (vh.mDescriptionView != null) {
-                setMaxLines(vh.mDescriptionView, mDescriptionMinLines);
-            }
-        }
-        if (vh.mActivatorView != null) {
-            onBindActivatorView(vh, action);
-        }
-        setEditingMode(vh, false /*editing*/, false /*withTransition*/);
-        if (action.isFocusable()) {
-            vh.itemView.setFocusable(true);
-            ((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-        } else {
-            vh.itemView.setFocusable(false);
-            ((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        }
-        setupImeOptions(vh, action);
-
-        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.
-        if (maxLines == 1) {
-            view.setSingleLine(true);
-        } else {
-            view.setSingleLine(false);
-            view.setMaxLines(maxLines);
-        }
-    }
-
-    /**
-     * Called by {@link #onBindViewHolder(ViewHolder, GuidedAction)} to setup IME options.  Default
-     * implementation assigns {@link EditorInfo#IME_ACTION_DONE}.  Subclass may override.
-     * @param vh The view holder to be associated with the given action.
-     * @param action The guided action to be displayed by the view holder's view.
-     */
-    protected void setupImeOptions(ViewHolder vh, GuidedAction action) {
-        setupNextImeOptions(vh.getEditableTitleView());
-        setupNextImeOptions(vh.getEditableDescriptionView());
-    }
-
-    private void setupNextImeOptions(EditText edit) {
-        if (edit != null) {
-            edit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
-        }
-    }
-
-    /**
-     * @deprecated This method is for internal library use only and should not
-     *             be called directly.
-     */
-    @Deprecated
-    public void setEditingMode(ViewHolder vh, GuidedAction action, boolean editing) {
-        if (editing != vh.isInEditing() && isInExpandTransition()) {
-            onEditingModeChange(vh, action, editing);
-        }
-    }
-
-    void setEditingMode(ViewHolder vh, boolean editing) {
-        setEditingMode(vh, editing, true /*withTransition*/);
-    }
-
-    void setEditingMode(ViewHolder vh, boolean editing, boolean withTransition) {
-        if (editing != vh.isInEditing() && !isInExpandTransition()) {
-            onEditingModeChange(vh, editing, withTransition);
-        }
-    }
-
-    /**
-     * @deprecated Use {@link #onEditingModeChange(ViewHolder, boolean, boolean)}.
-     */
-    @Deprecated
-    protected void onEditingModeChange(ViewHolder vh, GuidedAction action, boolean editing) {
-    }
-
-    /**
-     * Called when editing mode of an ViewHolder is changed.  Subclass must call
-     * <code>super.onEditingModeChange(vh,editing,withTransition)</code>.
-     *
-     * @param vh                ViewHolder to change editing mode.
-     * @param editing           True to enable editing, false to stop editing
-     * @param withTransition    True to run expand transiiton, false otherwise.
-     */
-    @CallSuper
-    protected void onEditingModeChange(ViewHolder vh, boolean editing, boolean withTransition) {
-        GuidedAction action = vh.getAction();
-        TextView titleView = vh.getTitleView();
-        TextView descriptionView = vh.getDescriptionView();
-        if (editing) {
-            CharSequence editTitle = action.getEditTitle();
-            if (titleView != null && editTitle != null) {
-                titleView.setText(editTitle);
-            }
-            CharSequence editDescription = action.getEditDescription();
-            if (descriptionView != null && editDescription != null) {
-                descriptionView.setText(editDescription);
-            }
-            if (action.isDescriptionEditable()) {
-                if (descriptionView != null) {
-                    descriptionView.setVisibility(View.VISIBLE);
-                    descriptionView.setInputType(action.getDescriptionEditInputType());
-                }
-                vh.mEditingMode = EDITING_DESCRIPTION;
-            } else if (action.isEditable()){
-                if (titleView != null) {
-                    titleView.setInputType(action.getEditInputType());
-                }
-                vh.mEditingMode = EDITING_TITLE;
-            } else if (vh.mActivatorView != null) {
-                onEditActivatorView(vh, editing, withTransition);
-                vh.mEditingMode = EDITING_ACTIVATOR_VIEW;
-            }
-        } else {
-            if (titleView != null) {
-                titleView.setText(action.getTitle());
-            }
-            if (descriptionView != null) {
-                descriptionView.setText(action.getDescription());
-            }
-            if (vh.mEditingMode == EDITING_DESCRIPTION) {
-                if (descriptionView != null) {
-                    descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription())
-                            ? View.GONE : View.VISIBLE);
-                    descriptionView.setInputType(action.getDescriptionInputType());
-                }
-            } else if (vh.mEditingMode == EDITING_TITLE) {
-                if (titleView != null) {
-                    titleView.setInputType(action.getInputType());
-                }
-            } else if (vh.mEditingMode == EDITING_ACTIVATOR_VIEW) {
-                if (vh.mActivatorView != null) {
-                    onEditActivatorView(vh, editing, withTransition);
-                }
-            }
-            vh.mEditingMode = EDITING_NONE;
-        }
-        // call deprecated method for backward compatible
-        onEditingModeChange(vh, action, editing);
-    }
-
-    /**
-     * Animates the view holder's view (or subviews thereof) when the action has had its focus
-     * state changed.
-     * @param vh The view holder associated with the relevant action.
-     * @param focused True if the action has become focused, false if it has lost focus.
-     */
-    public void onAnimateItemFocused(ViewHolder vh, boolean focused) {
-        // No animations for this, currently, because the animation is done on
-        // mSelectorView
-    }
-
-    /**
-     * Animates the view holder's view (or subviews thereof) when the action has had its press
-     * state changed.
-     * @param vh The view holder associated with the relevant action.
-     * @param pressed True if the action has been pressed, false if it has been unpressed.
-     */
-    public void onAnimateItemPressed(ViewHolder vh, boolean pressed) {
-        vh.press(pressed);
-    }
-
-    /**
-     * Resets the view holder's view to unpressed state.
-     * @param vh The view holder associated with the relevant action.
-     */
-    public void onAnimateItemPressedCancelled(ViewHolder vh) {
-        vh.press(false);
-    }
-
-    /**
-     * Animates the view holder's view (or subviews thereof) when the action has had its check state
-     * changed. Default implementation calls setChecked() if {@link ViewHolder#getCheckmarkView()}
-     * is instance of {@link Checkable}.
-     *
-     * @param vh The view holder associated with the relevant action.
-     * @param checked True if the action has become checked, false if it has become unchecked.
-     * @see #onBindCheckMarkView(ViewHolder, GuidedAction)
-     */
-    public void onAnimateItemChecked(ViewHolder vh, boolean checked) {
-        if (vh.mCheckmarkView instanceof Checkable) {
-            ((Checkable) vh.mCheckmarkView).setChecked(checked);
-        }
-    }
-
-    /**
-     * Sets states of check mark view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}
-     * when action's checkset Id is other than {@link GuidedAction#NO_CHECK_SET}. Default
-     * implementation assigns drawable loaded from theme attribute
-     * {@link android.R.attr#listChoiceIndicatorMultiple} for checkbox or
-     * {@link android.R.attr#listChoiceIndicatorSingle} for radio button. Subclass rarely needs
-     * override the method, instead app can provide its own drawable that supports transition
-     * animations, change theme attributes {@link android.R.attr#listChoiceIndicatorMultiple} and
-     * {@link android.R.attr#listChoiceIndicatorSingle} in {android.support.v17.leanback.R.
-     * styleable#LeanbackGuidedStepTheme}.
-     *
-     * @param vh The view holder associated with the relevant action.
-     * @param action The GuidedAction object to bind to.
-     * @see #onAnimateItemChecked(ViewHolder, boolean)
-     */
-    public void onBindCheckMarkView(ViewHolder vh, GuidedAction action) {
-        if (action.getCheckSetId() != GuidedAction.NO_CHECK_SET) {
-            vh.mCheckmarkView.setVisibility(View.VISIBLE);
-            int attrId = action.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID
-                    ? android.R.attr.listChoiceIndicatorMultiple
-                    : android.R.attr.listChoiceIndicatorSingle;
-            final Context context = vh.mCheckmarkView.getContext();
-            Drawable drawable = null;
-            TypedValue typedValue = new TypedValue();
-            if (context.getTheme().resolveAttribute(attrId, typedValue, true)) {
-                drawable = ContextCompat.getDrawable(context, typedValue.resourceId);
-            }
-            vh.mCheckmarkView.setImageDrawable(drawable);
-            if (vh.mCheckmarkView instanceof Checkable) {
-                ((Checkable) vh.mCheckmarkView).setChecked(action.isChecked());
-            }
-        } else {
-            vh.mCheckmarkView.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Performs binding activator view value to action.  Default implementation supports
-     * GuidedDatePickerAction, subclass may override to add support of other views.
-     * @param vh ViewHolder of activator view.
-     * @param action GuidedAction to bind.
-     */
-    public void onBindActivatorView(ViewHolder vh, GuidedAction action) {
-        if (action instanceof GuidedDatePickerAction) {
-            GuidedDatePickerAction dateAction = (GuidedDatePickerAction) action;
-            DatePicker dateView = (DatePicker) vh.mActivatorView;
-            dateView.setDatePickerFormat(dateAction.getDatePickerFormat());
-            if (dateAction.getMinDate() != Long.MIN_VALUE) {
-                dateView.setMinDate(dateAction.getMinDate());
-            }
-            if (dateAction.getMaxDate() != Long.MAX_VALUE) {
-                dateView.setMaxDate(dateAction.getMaxDate());
-            }
-            Calendar c = Calendar.getInstance();
-            c.setTimeInMillis(dateAction.getDate());
-            dateView.updateDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH),
-                    c.get(Calendar.DAY_OF_MONTH), false);
-        }
-    }
-
-    /**
-     * Performs updating GuidedAction from activator view.  Default implementation supports
-     * GuidedDatePickerAction, subclass may override to add support of other views.
-     * @param vh ViewHolder of activator view.
-     * @param action GuidedAction to update.
-     * @return True if value has been updated, false otherwise.
-     */
-    public boolean onUpdateActivatorView(ViewHolder vh, GuidedAction action) {
-        if (action instanceof GuidedDatePickerAction) {
-            GuidedDatePickerAction dateAction = (GuidedDatePickerAction) action;
-            DatePicker dateView = (DatePicker) vh.mActivatorView;
-            if (dateAction.getDate() != dateView.getDate()) {
-                dateAction.setDate(dateView.getDate());
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Sets listener for reporting view being edited.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setEditListener(EditListener listener) {
-        mEditListener = listener;
-    }
-
-    void onEditActivatorView(final ViewHolder vh, boolean editing, final boolean withTransition) {
-        if (editing) {
-            startExpanded(vh, withTransition);
-            vh.itemView.setFocusable(false);
-            vh.mActivatorView.requestFocus();
-            vh.mActivatorView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (!isInExpandTransition()) {
-                        ((GuidedActionAdapter) getActionsGridView().getAdapter())
-                                .performOnActionClick(vh);
-                    }
-                }
-            });
-        } else {
-            if (onUpdateActivatorView(vh, vh.getAction())) {
-                if (mEditListener != null) {
-                    mEditListener.onGuidedActionEditedAndProceed(vh.getAction());
-                }
-            }
-            vh.itemView.setFocusable(true);
-            vh.itemView.requestFocus();
-            startExpanded(null, withTransition);
-            vh.mActivatorView.setOnClickListener(null);
-            vh.mActivatorView.setClickable(false);
-        }
-    }
-
-    /**
-     * Sets states of chevron view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}.
-     * Subclass may override.
-     *
-     * @param vh The view holder associated with the relevant action.
-     * @param action The GuidedAction object to bind to.
-     */
-    public void onBindChevronView(ViewHolder vh, GuidedAction action) {
-        final boolean hasNext = action.hasNext();
-        final boolean hasSubActions = action.hasSubActions();
-        if (hasNext || hasSubActions) {
-            vh.mChevronView.setVisibility(View.VISIBLE);
-            vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
-                    mDisabledChevronAlpha);
-            if (hasNext) {
-                float r = mMainView != null
-                        && mMainView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? 180f : 0f;
-                vh.mChevronView.setRotation(r);
-            } else if (action == mExpandedAction) {
-                vh.mChevronView.setRotation(270);
-            } else {
-                vh.mChevronView.setRotation(90);
-            }
-        } else {
-            vh.mChevronView.setVisibility(View.GONE);
-
-        }
-    }
-
-    /**
-     * Expands or collapse the sub actions list view with transition animation
-     * @param avh When not null, fill sub actions list of this ViewHolder into sub actions list and
-     * hide the other items in main list.  When null, collapse the sub actions list.
-     * @deprecated use {@link #expandAction(GuidedAction, boolean)} and
-     * {@link #collapseAction(boolean)}
-     */
-    @Deprecated
-    public void setExpandedViewHolder(ViewHolder avh) {
-        expandAction(avh == null ? null : avh.getAction(), isExpandTransitionSupported());
-    }
-
-    /**
-     * Returns true if it is running an expanding or collapsing transition, false otherwise.
-     * @return True if it is running an expanding or collapsing transition, false otherwise.
-     */
-    public boolean isInExpandTransition() {
-        return mExpandTransition != null;
-    }
-
-    /**
-     * Returns if expand/collapse animation is supported.  When this method returns true,
-     * {@link #startExpandedTransition(ViewHolder)} will be used.  When this method returns false,
-     * {@link #onUpdateExpandedViewHolder(ViewHolder)} will be called.
-     * @return True if it is running an expanding or collapsing transition, false otherwise.
-     */
-    public boolean isExpandTransitionSupported() {
-        return VERSION.SDK_INT >= 21;
-    }
-
-    /**
-     * Start transition to expand or collapse GuidedActionStylist.
-     * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
-     * the GuidedActionStylist will collapse sub actions.
-     * @deprecated use {@link #expandAction(GuidedAction, boolean)} and
-     * {@link #collapseAction(boolean)}
-     */
-    @Deprecated
-    public void startExpandedTransition(ViewHolder avh) {
-        expandAction(avh == null ? null : avh.getAction(), isExpandTransitionSupported());
-    }
-
-    /**
-     * Enable or disable using BACK key to collapse sub actions list. Default is enabled.
-     *
-     * @param backToCollapse True to enable using BACK key to collapse sub actions list, false
-     *                       to disable.
-     * @see GuidedAction#hasSubActions
-     * @see GuidedAction#getSubActions
-     */
-    public final void setBackKeyToCollapseSubActions(boolean backToCollapse) {
-        mBackToCollapseSubActions = backToCollapse;
-    }
-
-    /**
-     * @return True if using BACK key to collapse sub actions list, false otherwise. Default value
-     * is true.
-     *
-     * @see GuidedAction#hasSubActions
-     * @see GuidedAction#getSubActions
-     */
-    public final boolean isBackKeyToCollapseSubActions() {
-        return mBackToCollapseSubActions;
-    }
-
-    /**
-     * Enable or disable using BACK key to collapse {@link GuidedAction} with editable activator
-     * view. Default is enabled.
-     *
-     * @param backToCollapse True to enable using BACK key to collapse {@link GuidedAction} with
-     *                       editable activator view.
-     * @see GuidedAction#hasEditableActivatorView
-     */
-    public final void setBackKeyToCollapseActivatorView(boolean backToCollapse) {
-        mBackToCollapseActivatorView = backToCollapse;
-    }
-
-    /**
-     * @return True if using BACK key to collapse {@link GuidedAction} with editable activator
-     * view, false otherwise. Default value is true.
-     *
-     * @see GuidedAction#hasEditableActivatorView
-     */
-    public final boolean isBackKeyToCollapseActivatorView() {
-        return mBackToCollapseActivatorView;
-    }
-
-    /**
-     * Expand an action. Do nothing if it is in animation or there is action expanded.
-     *
-     * @param action         Action to expand.
-     * @param withTransition True to run transition animation, false otherwsie.
-     */
-    public void expandAction(GuidedAction action, final boolean withTransition) {
-        if (isInExpandTransition() || mExpandedAction != null) {
-            return;
-        }
-        int actionPosition =
-                ((GuidedActionAdapter) getActionsGridView().getAdapter()).indexOf(action);
-        if (actionPosition < 0) {
-            return;
-        }
-        boolean runTransition = isExpandTransitionSupported() && withTransition;
-        if (!runTransition) {
-            getActionsGridView().setSelectedPosition(actionPosition,
-                    new ViewHolderTask() {
-                        @Override
-                        public void run(RecyclerView.ViewHolder vh) {
-                            GuidedActionsStylist.ViewHolder avh =
-                                    (GuidedActionsStylist.ViewHolder)vh;
-                            if (avh.getAction().hasEditableActivatorView()) {
-                                setEditingMode(avh, true /*editing*/, false /*withTransition*/);
-                            } else {
-                                onUpdateExpandedViewHolder(avh);
-                            }
-                        }
-                    });
-            if (action.hasSubActions()) {
-                onUpdateSubActionsGridView(action, true);
-            }
-        } else {
-            getActionsGridView().setSelectedPosition(actionPosition,
-                    new ViewHolderTask() {
-                        @Override
-                        public void run(RecyclerView.ViewHolder vh) {
-                            GuidedActionsStylist.ViewHolder avh =
-                                    (GuidedActionsStylist.ViewHolder)vh;
-                            if (avh.getAction().hasEditableActivatorView()) {
-                                setEditingMode(avh, true /*editing*/, true /*withTransition*/);
-                            } else {
-                                startExpanded(avh, true);
-                            }
-                        }
-                    });
-        }
-
-    }
-
-    /**
-     * Collapse expanded action. Do nothing if it is in animation or there is no action expanded.
-     *
-     * @param withTransition True to run transition animation, false otherwsie.
-     */
-    public void collapseAction(boolean withTransition) {
-        if (isInExpandTransition() || mExpandedAction == null) {
-            return;
-        }
-        boolean runTransition = isExpandTransitionSupported() && withTransition;
-        int actionPosition =
-                ((GuidedActionAdapter) getActionsGridView().getAdapter()).indexOf(mExpandedAction);
-        if (actionPosition < 0) {
-            return;
-        }
-        if (mExpandedAction.hasEditableActivatorView()) {
-            setEditingMode(
-                    ((ViewHolder) getActionsGridView().findViewHolderForPosition(actionPosition)),
-                    false /*editing*/,
-                    runTransition);
-        } else {
-            startExpanded(null, runTransition);
-        }
-    }
-
-    int getKeyLine() {
-        return (int) (mKeyLinePercent * mActionsGridView.getHeight() / 100);
-    }
-
-    /**
-     * Internal method with assumption we already scroll to the new ViewHolder or is currently
-     * expanded.
-     */
-    void startExpanded(ViewHolder avh, final boolean withTransition) {
-        ViewHolder focusAvh = null; // expand / collapse view holder
-        final int count = mActionsGridView.getChildCount();
-        for (int i = 0; i < count; i++) {
-            ViewHolder vh = (ViewHolder) mActionsGridView
-                    .getChildViewHolder(mActionsGridView.getChildAt(i));
-            if (avh == null && vh.itemView.getVisibility() == View.VISIBLE) {
-                // going to collapse this one.
-                focusAvh = vh;
-                break;
-            } else if (avh != null && vh.getAction() == avh.getAction()) {
-                // going to expand this one.
-                focusAvh = vh;
-                break;
-            }
-        }
-        if (focusAvh == null) {
-            // huh?
-            return;
-        }
-        boolean isExpand = avh != null;
-        boolean isSubActionTransition = focusAvh.getAction().hasSubActions();
-        if (withTransition) {
-            Object set = TransitionHelper.createTransitionSet(false);
-            float slideDistance = isSubActionTransition ? focusAvh.itemView.getHeight()
-                    : focusAvh.itemView.getHeight() * 0.5f;
-            Object slideAndFade = TransitionHelper.createFadeAndShortSlide(
-                    Gravity.TOP | Gravity.BOTTOM,
-                    slideDistance);
-            TransitionHelper.setEpicenterCallback(slideAndFade, new TransitionEpicenterCallback() {
-                Rect mRect = new Rect();
-                @Override
-                public Rect onGetEpicenter(Object transition) {
-                    int centerY = getKeyLine();
-                    int centerX = 0;
-                    mRect.set(centerX, centerY, centerX, centerY);
-                    return mRect;
-                }
-            });
-            Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
-            Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
-            Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN
-                    | TransitionHelper.FADE_OUT);
-            Object changeGridBounds = TransitionHelper.createChangeBounds(false);
-            if (avh == null) {
-                TransitionHelper.setStartDelay(slideAndFade, 150);
-                TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
-                TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
-                TransitionHelper.setStartDelay(changeGridBounds, 100);
-            } else {
-                TransitionHelper.setStartDelay(fade, 100);
-                TransitionHelper.setStartDelay(changeGridBounds, 50);
-                TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
-                TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
-            }
-            for (int i = 0; i < count; i++) {
-                ViewHolder vh = (ViewHolder) mActionsGridView
-                        .getChildViewHolder(mActionsGridView.getChildAt(i));
-                if (vh == focusAvh) {
-                    // going to expand/collapse this one.
-                    if (isSubActionTransition) {
-                        TransitionHelper.include(changeFocusItemTransform, vh.itemView);
-                        TransitionHelper.include(changeFocusItemBounds, vh.itemView);
-                    }
-                } else {
-                    // going to slide this item to top / bottom.
-                    TransitionHelper.include(slideAndFade, vh.itemView);
-                    TransitionHelper.exclude(fade, vh.itemView, true);
-                }
-            }
-            TransitionHelper.include(changeGridBounds, mSubActionsGridView);
-            TransitionHelper.include(changeGridBounds, mSubActionsBackground);
-            TransitionHelper.addTransition(set, slideAndFade);
-            // note that we don't run ChangeBounds for activating view due to the rounding problem
-            // of multiple level views ChangeBounds animation causing vertical jittering.
-            if (isSubActionTransition) {
-                TransitionHelper.addTransition(set, changeFocusItemTransform);
-                TransitionHelper.addTransition(set, changeFocusItemBounds);
-            }
-            TransitionHelper.addTransition(set, fade);
-            TransitionHelper.addTransition(set, changeGridBounds);
-            mExpandTransition = set;
-            TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
-                @Override
-                public void onTransitionEnd(Object transition) {
-                    mExpandTransition = null;
-                }
-            });
-            if (isExpand && isSubActionTransition) {
-                // To expand sub actions, move original position of sub actions to bottom of item
-                int startY = avh.itemView.getBottom();
-                mSubActionsGridView.offsetTopAndBottom(startY - mSubActionsGridView.getTop());
-                mSubActionsBackground.offsetTopAndBottom(startY - mSubActionsBackground.getTop());
-            }
-            TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
-        }
-        onUpdateExpandedViewHolder(avh);
-        if (isSubActionTransition) {
-            onUpdateSubActionsGridView(focusAvh.getAction(), isExpand);
-        }
-    }
-
-    /**
-     * @return True if sub actions list is expanded.
-     */
-    public boolean isSubActionsExpanded() {
-        return mExpandedAction != null && mExpandedAction.hasSubActions();
-    }
-
-    /**
-     * @return True if there is {@link #getExpandedAction()} is not null, false otherwise.
-     */
-    public boolean isExpanded() {
-        return mExpandedAction != null;
-    }
-
-    /**
-     * @return Current expanded GuidedAction or null if not expanded.
-     */
-    public GuidedAction getExpandedAction() {
-        return mExpandedAction;
-    }
-
-    /**
-     * Expand or collapse GuidedActionStylist.
-     * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
-     * the GuidedActionStylist will collapse sub actions.
-     */
-    public void onUpdateExpandedViewHolder(ViewHolder avh) {
-
-        // Note about setting the prune child flag back & forth here: without this, the actions that
-        // go off the screen from the top or bottom become invisible forever. This is because once
-        // an action is expanded, it takes more space which in turn kicks out some other actions
-        // off of the screen. Once, this action is collapsed (after the second click) and the
-        // visibility flag is set back to true for all existing actions,
-        // the off-the-screen actions are pruned from the view, thus
-        // could not be accessed, had we not disabled pruning prior to this.
-        if (avh == null) {
-            mExpandedAction = null;
-            mActionsGridView.setPruneChild(true);
-        } else if (avh.getAction() != mExpandedAction) {
-            mExpandedAction = avh.getAction();
-            mActionsGridView.setPruneChild(false);
-        }
-        // In expanding mode, notifyItemChange on expanded item will reset the translationY by
-        // the default ItemAnimator.  So disable ItemAnimation in expanding mode.
-        mActionsGridView.setAnimateChildLayout(false);
-        final int count = mActionsGridView.getChildCount();
-        for (int i = 0; i < count; i++) {
-            ViewHolder vh = (ViewHolder) mActionsGridView
-                    .getChildViewHolder(mActionsGridView.getChildAt(i));
-            updateChevronAndVisibility(vh);
-        }
-    }
-
-    void onUpdateSubActionsGridView(GuidedAction action, boolean expand) {
-        if (mSubActionsGridView != null) {
-            ViewGroup.MarginLayoutParams lp =
-                    (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
-            GuidedActionAdapter adapter = (GuidedActionAdapter) mSubActionsGridView.getAdapter();
-            if (expand) {
-                // set to negative value so GuidedActionRelativeLayout will override with
-                // keyLine percentage.
-                lp.topMargin = -2;
-                lp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
-                mSubActionsGridView.setLayoutParams(lp);
-                mSubActionsGridView.setVisibility(View.VISIBLE);
-                mSubActionsBackground.setVisibility(View.VISIBLE);
-                mSubActionsGridView.requestFocus();
-                adapter.setActions(action.getSubActions());
-            } else {
-                // set to explicit value, which will disable the keyLine percentage calculation
-                // in GuidedRelativeLayout.
-                int actionPosition = ((GuidedActionAdapter) mActionsGridView.getAdapter())
-                        .indexOf(action);
-                lp.topMargin = mActionsGridView.getLayoutManager()
-                        .findViewByPosition(actionPosition).getBottom();
-                lp.height = 0;
-                mSubActionsGridView.setVisibility(View.INVISIBLE);
-                mSubActionsBackground.setVisibility(View.INVISIBLE);
-                mSubActionsGridView.setLayoutParams(lp);
-                adapter.setActions(Collections.EMPTY_LIST);
-                mActionsGridView.requestFocus();
-            }
-        }
-    }
-
-    private void updateChevronAndVisibility(ViewHolder vh) {
-        if (!vh.isSubAction()) {
-            if (mExpandedAction == null) {
-                vh.itemView.setVisibility(View.VISIBLE);
-                vh.itemView.setTranslationY(0);
-                if (vh.mActivatorView != null) {
-                    vh.setActivated(false);
-                }
-            } else if (vh.getAction() == mExpandedAction) {
-                vh.itemView.setVisibility(View.VISIBLE);
-                if (vh.getAction().hasSubActions()) {
-                    vh.itemView.setTranslationY(getKeyLine() - vh.itemView.getBottom());
-                } else if (vh.mActivatorView != null) {
-                    vh.itemView.setTranslationY(0);
-                    vh.setActivated(true);
-                }
-            } else {
-                vh.itemView.setVisibility(View.INVISIBLE);
-                vh.itemView.setTranslationY(0);
-            }
-        }
-        if (vh.mChevronView != null) {
-            onBindChevronView(vh, vh.getAction());
-        }
-    }
-
-    /*
-     * ==========================================
-     * FragmentAnimationProvider overrides
-     * ==========================================
-     */
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onImeAppearing(@NonNull List<Animator> animators) {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onImeDisappearing(@NonNull List<Animator> animators) {
-    }
-
-    /*
-     * ==========================================
-     * Private methods
-     * ==========================================
-     */
-
-    private float getFloat(Context ctx, TypedValue typedValue, int attrId) {
-        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
-        // Android resources don't have a native float type, so we have to use strings.
-        return Float.valueOf(ctx.getResources().getString(typedValue.resourceId));
-    }
-
-    private int getInteger(Context ctx, TypedValue typedValue, int attrId) {
-        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
-        return ctx.getResources().getInteger(typedValue.resourceId);
-    }
-
-    private int getDimension(Context ctx, TypedValue typedValue, int attrId) {
-        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
-        return ctx.getResources().getDimensionPixelSize(typedValue.resourceId);
-    }
-
-    private boolean setIcon(final ImageView iconView, GuidedAction action) {
-        Drawable icon = null;
-        if (iconView != null) {
-            icon = action.getIcon();
-            if (icon != null) {
-                // setImageDrawable resets the drawable's level unless we set the view level first.
-                iconView.setImageLevel(icon.getLevel());
-                iconView.setImageDrawable(icon);
-                iconView.setVisibility(View.VISIBLE);
-            } else {
-                iconView.setVisibility(View.GONE);
-            }
-        }
-        return icon != null;
-    }
-
-    /**
-     * @return the max height in pixels the description can be such that the
-     *         action nicely takes up the entire screen.
-     */
-    private int getDescriptionMaxHeight(Context context, TextView title) {
-        // The 2 multiplier on the title height calculation is a
-        // conservative estimate for font padding which can not be
-        // calculated at this stage since the view hasn't been rendered yet.
-        return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/GuidedDatePickerAction.java b/android/support/v17/leanback/widget/GuidedDatePickerAction.java
deleted file mode 100644
index 5871247..0000000
--- a/android/support/v17/leanback/widget/GuidedDatePickerAction.java
+++ /dev/null
@@ -1,188 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.os.Bundle;
-
-import java.util.Calendar;
-import java.util.TimeZone;
-
-/**
- * Subclass of GuidedAction that can choose a date.  The Action is editable by default; to make it
- * read only, call hasEditableActivatorView(false) on the Builder.
- */
-public class GuidedDatePickerAction extends GuidedAction {
-
-    /**
-     * Base Builder class to build GuidedDatePickerAction.  Subclass this BuilderBase when app needs
-     * to subclass GuidedDatePickerAction, implement your build() which should call
-     * {@link #applyDatePickerValues(GuidedDatePickerAction)}.  When using GuidedDatePickerAction
-     * directly, use {@link Builder}.
-     */
-    public abstract static class BuilderBase<B extends BuilderBase>
-            extends GuidedAction.BuilderBase<B> {
-
-        private String mDatePickerFormat;
-        private long mDate;
-        private long mMinDate = Long.MIN_VALUE;
-        private long mMaxDate = Long.MAX_VALUE;
-
-        public BuilderBase(Context context) {
-            super(context);
-            Calendar c = Calendar.getInstance();
-            mDate = c.getTimeInMillis();
-            hasEditableActivatorView(true);
-        }
-
-        /**
-         * Sets format of date Picker or null for default.  The format is a case insensitive String
-         * containing the day ('d'), month ('m'), and year ('y').  When the format is not specified,
-         * a default format of current locale will be used.
-         * @param format Format of showing Date, e.g. "YMD".
-         * @return This Builder object.
-         */
-        public B datePickerFormat(String format) {
-            mDatePickerFormat = format;
-            return (B) this;
-        }
-
-        /**
-         * Sets a Date for date picker in milliseconds since January 1, 1970 00:00:00 in
-         * {@link TimeZone#getDefault()} time zone.
-         * @return This Builder Object.
-         */
-        public B date(long date) {
-            mDate = date;
-            return (B) this;
-        }
-
-        /**
-         * Sets minimal Date for date picker in milliseconds since January 1, 1970 00:00:00 in
-         * {@link TimeZone#getDefault()} time zone.
-         * @return This Builder Object.
-         */
-        public B minDate(long minDate) {
-            mMinDate = minDate;
-            return (B) this;
-        }
-
-        /**
-         * Sets maximum Date for date picker in milliseconds since January 1, 1970 00:00:00 in
-         * {@link TimeZone#getDefault()} time zone.
-         * @return This Builder Object.
-         */
-        public B maxDate(long maxDate) {
-            mMaxDate = maxDate;
-            return (B) this;
-        }
-
-        /**
-         * Apply values to GuidedDatePickerAction.
-         * @param action GuidedDatePickerAction to apply values.
-         */
-        protected final void applyDatePickerValues(GuidedDatePickerAction action) {
-            super.applyValues(action);
-            action.mDatePickerFormat = mDatePickerFormat;
-            action.mDate = mDate;
-            if (mMinDate > mMaxDate) {
-                throw new IllegalArgumentException("MinDate cannot be larger than MaxDate");
-            }
-            action.mMinDate = mMinDate;
-            action.mMaxDate = mMaxDate;
-        }
-
-    }
-
-    /**
-     * Builder class to build a GuidedDatePickerAction.
-     */
-    public final static class Builder extends BuilderBase<Builder> {
-        public Builder(Context context) {
-            super(context);
-        }
-
-        /**
-         * Builds the GuidedDatePickerAction corresponding to this Builder.
-         * @return The GuidedDatePickerAction as configured through this Builder.
-         */
-        public GuidedDatePickerAction build() {
-            GuidedDatePickerAction action = new GuidedDatePickerAction();
-            applyDatePickerValues(action);
-            return action;
-        }
-    }
-
-    String mDatePickerFormat;
-    long mDate;
-    long mMinDate = Long.MIN_VALUE;
-    long mMaxDate = Long.MAX_VALUE;
-
-    /**
-     * Returns format of date Picker or null if not specified.  The format is a case insensitive
-     * String containing the * day ('d'), month ('m'), and year ('y'). When the format is not
-     * specified, a default format of current locale will
-     * be used.
-     * @return Format of showing Date, e.g. "YMD".  Returns null if using current locale's default.
-     */
-    public String getDatePickerFormat() {
-        return mDatePickerFormat;
-    }
-
-    /**
-     * Get current value of DatePicker in milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     * @return Current value of DatePicker Action.
-     */
-    public long getDate() {
-        return mDate;
-    }
-
-    /**
-     * Sets current value of DatePicker in milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     * @param date New value to update current value of DatePicker Action.
-     */
-    public void setDate(long date) {
-        mDate = date;
-    }
-
-    /**
-     * Get minimal value of DatePicker in milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.  -1 if not set.
-     * @return Minimal value of DatePicker Action or Long.MIN_VALUE if not set.
-     */
-    public long getMinDate() {
-        return mMinDate;
-    }
-
-    /**
-     * Get maximum value of DatePicker in milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     * @return Maximum value of DatePicker Action or Long.MAX_VALUE if not set.
-     */
-    public long getMaxDate() {
-        return mMaxDate;
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle bundle, String key) {
-        bundle.putLong(key, getDate());
-    }
-
-    @Override
-    public void onRestoreInstanceState(Bundle bundle, String key) {
-        setDate(bundle.getLong(key, getDate()));
-    }
-}
diff --git a/android/support/v17/leanback/widget/HeaderItem.java b/android/support/v17/leanback/widget/HeaderItem.java
deleted file mode 100644
index 00c4660..0000000
--- a/android/support/v17/leanback/widget/HeaderItem.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.v17.leanback.widget;
-
-import static android.support.v17.leanback.widget.ObjectAdapter.NO_ID;
-
-/**
- * A header item describes the metadata of a {@link Row}, such as a category
- * of media items.  May be subclassed to add more information.
- */
-public class HeaderItem {
-
-    private final long mId;
-    private final String mName;
-    private CharSequence mDescription;
-    private CharSequence mContentDescription;
-
-    /**
-     * Create a header item.  All fields are optional.
-     */
-    public HeaderItem(long id, String name) {
-        mId = id;
-        mName = name;
-    }
-
-    /**
-     * Create a header item.
-     */
-    public HeaderItem(String name) {
-        this(NO_ID, name);
-    }
-
-    /**
-     * Returns a unique identifier for this item.
-     */
-    public final long getId() {
-        return mId;
-    }
-
-    /**
-     * Returns the name of this header item.
-     */
-    public final String getName() {
-        return mName;
-    }
-
-    /**
-     * Returns optional content description for the HeaderItem.  When it is null, {@link #getName()}
-     * should be used for the content description.
-     * @return Content description for the HeaderItem.
-     */
-    public CharSequence getContentDescription() {
-        return mContentDescription;
-    }
-
-    /**
-     * Sets optional content description for the HeaderItem.
-     * @param contentDescription Content description sets on the HeaderItem.
-     */
-    public void setContentDescription(CharSequence contentDescription) {
-        mContentDescription = contentDescription;
-    }
-
-    /**
-     * Sets the description for the current header item. This will be visible when
-     * the row receives focus.
-     */
-    public void setDescription(CharSequence description) {
-        this.mDescription = description;
-    }
-
-    /**
-     * Returns the description for the current row.
-     */
-    public CharSequence getDescription() {
-        return mDescription;
-    }
-}
diff --git a/android/support/v17/leanback/widget/HorizontalGridView.java b/android/support/v17/leanback/widget/HorizontalGridView.java
deleted file mode 100644
index 4d6a7dc..0000000
--- a/android/support/v17/leanback/widget/HorizontalGridView.java
+++ /dev/null
@@ -1,395 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.support.v17.leanback.R;
-import android.support.v7.widget.RecyclerView;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.View;
-
-/**
- * A {@link android.view.ViewGroup} that shows items in a horizontal scrolling list. The items come from
- * the {@link RecyclerView.Adapter} associated with this view.
- * <p>
- * {@link RecyclerView.Adapter} can optionally implement {@link FacetProviderAdapter} which
- * provides {@link FacetProvider} for a given view type;  {@link RecyclerView.ViewHolder}
- * can also implement {@link FacetProvider}.  Facet from ViewHolder
- * has a higher priority than the one from FacetProviderAdapter associated with viewType.
- * Supported optional facets are:
- * <ol>
- * <li> {@link ItemAlignmentFacet}
- * When this facet is provided by ViewHolder or FacetProviderAdapter,  it will
- * override the item alignment settings set on HorizontalGridView.  This facet also allows multiple
- * alignment positions within one ViewHolder.
- * </li>
- * </ol>
- */
-public class HorizontalGridView extends BaseGridView {
-
-    private boolean mFadingLowEdge;
-    private boolean mFadingHighEdge;
-
-    private Paint mTempPaint = new Paint();
-    private Bitmap mTempBitmapLow;
-    private LinearGradient mLowFadeShader;
-    private int mLowFadeShaderLength;
-    private int mLowFadeShaderOffset;
-    private Bitmap mTempBitmapHigh;
-    private LinearGradient mHighFadeShader;
-    private int mHighFadeShaderLength;
-    private int mHighFadeShaderOffset;
-    private Rect mTempRect = new Rect();
-
-    public HorizontalGridView(Context context) {
-        this(context, null);
-    }
-
-    public HorizontalGridView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public HorizontalGridView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
-        initAttributes(context, attrs);
-    }
-
-    protected void initAttributes(Context context, AttributeSet attrs) {
-        initBaseGridViewAttributes(context, attrs);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbHorizontalGridView);
-        setRowHeight(a);
-        setNumRows(a.getInt(R.styleable.lbHorizontalGridView_numberOfRows, 1));
-        a.recycle();
-        updateLayerType();
-        mTempPaint = new Paint();
-        mTempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
-    }
-
-    void setRowHeight(TypedArray array) {
-        TypedValue typedValue = array.peekValue(R.styleable.lbHorizontalGridView_rowHeight);
-        if (typedValue != null) {
-            int size = array.getLayoutDimension(R.styleable.lbHorizontalGridView_rowHeight, 0);
-            setRowHeight(size);
-        }
-    }
-
-    /**
-     * Sets the number of rows.  Defaults to one.
-     */
-    public void setNumRows(int numRows) {
-        mLayoutManager.setNumRows(numRows);
-        requestLayout();
-    }
-
-    /**
-     * Sets the row height.
-     *
-     * @param height May be {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT},
-     *               or a size in pixels. If zero, row height will be fixed based on number of
-     *               rows and view height.
-     */
-    public void setRowHeight(int height) {
-        mLayoutManager.setRowHeight(height);
-        requestLayout();
-    }
-
-    /**
-     * Sets the fade out left edge to transparent.   Note turn on fading edge is very expensive
-     * that you should turn off when HorizontalGridView is scrolling.
-     */
-    public final void setFadingLeftEdge(boolean fading) {
-        if (mFadingLowEdge != fading) {
-            mFadingLowEdge = fading;
-            if (!mFadingLowEdge) {
-                mTempBitmapLow = null;
-            }
-            invalidate();
-            updateLayerType();
-        }
-    }
-
-    /**
-     * Returns true if left edge fading is enabled.
-     */
-    public final boolean getFadingLeftEdge() {
-        return mFadingLowEdge;
-    }
-
-    /**
-     * Sets the left edge fading length in pixels.
-     */
-    public final void setFadingLeftEdgeLength(int fadeLength) {
-        if (mLowFadeShaderLength != fadeLength) {
-            mLowFadeShaderLength = fadeLength;
-            if (mLowFadeShaderLength != 0) {
-                mLowFadeShader = new LinearGradient(0, 0, mLowFadeShaderLength, 0,
-                        Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP);
-            } else {
-                mLowFadeShader = null;
-            }
-            invalidate();
-        }
-    }
-
-    /**
-     * Returns the left edge fading length in pixels.
-     */
-    public final int getFadingLeftEdgeLength() {
-        return mLowFadeShaderLength;
-    }
-
-    /**
-     * Sets the distance in pixels between fading start position and left padding edge.
-     * The fading start position is positive when start position is inside left padding
-     * area.  Default value is 0, means that the fading starts from left padding edge.
-     */
-    public final void setFadingLeftEdgeOffset(int fadeOffset) {
-        if (mLowFadeShaderOffset != fadeOffset) {
-            mLowFadeShaderOffset = fadeOffset;
-            invalidate();
-        }
-    }
-
-    /**
-     * Returns the distance in pixels between fading start position and left padding edge.
-     * The fading start position is positive when start position is inside left padding
-     * area.  Default value is 0, means that the fading starts from left padding edge.
-     */
-    public final int getFadingLeftEdgeOffset() {
-        return mLowFadeShaderOffset;
-    }
-
-    /**
-     * Sets the fade out right edge to transparent.   Note turn on fading edge is very expensive
-     * that you should turn off when HorizontalGridView is scrolling.
-     */
-    public final void setFadingRightEdge(boolean fading) {
-        if (mFadingHighEdge != fading) {
-            mFadingHighEdge = fading;
-            if (!mFadingHighEdge) {
-                mTempBitmapHigh = null;
-            }
-            invalidate();
-            updateLayerType();
-        }
-    }
-
-    /**
-     * Returns true if fading right edge is enabled.
-     */
-    public final boolean getFadingRightEdge() {
-        return mFadingHighEdge;
-    }
-
-    /**
-     * Sets the right edge fading length in pixels.
-     */
-    public final void setFadingRightEdgeLength(int fadeLength) {
-        if (mHighFadeShaderLength != fadeLength) {
-            mHighFadeShaderLength = fadeLength;
-            if (mHighFadeShaderLength != 0) {
-                mHighFadeShader = new LinearGradient(0, 0, mHighFadeShaderLength, 0,
-                        Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP);
-            } else {
-                mHighFadeShader = null;
-            }
-            invalidate();
-        }
-    }
-
-    /**
-     * Returns the right edge fading length in pixels.
-     */
-    public final int getFadingRightEdgeLength() {
-        return mHighFadeShaderLength;
-    }
-
-    /**
-     * Returns the distance in pixels between fading start position and right padding edge.
-     * The fading start position is positive when start position is inside right padding
-     * area.  Default value is 0, means that the fading starts from right padding edge.
-     */
-    public final void setFadingRightEdgeOffset(int fadeOffset) {
-        if (mHighFadeShaderOffset != fadeOffset) {
-            mHighFadeShaderOffset = fadeOffset;
-            invalidate();
-        }
-    }
-
-    /**
-     * Sets the distance in pixels between fading start position and right padding edge.
-     * The fading start position is positive when start position is inside right padding
-     * area.  Default value is 0, means that the fading starts from right padding edge.
-     */
-    public final int getFadingRightEdgeOffset() {
-        return mHighFadeShaderOffset;
-    }
-
-    private boolean needsFadingLowEdge() {
-        if (!mFadingLowEdge) {
-            return false;
-        }
-        final int c = getChildCount();
-        for (int i = 0; i < c; i++) {
-            View view = getChildAt(i);
-            if (mLayoutManager.getOpticalLeft(view) < getPaddingLeft() - mLowFadeShaderOffset) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean needsFadingHighEdge() {
-        if (!mFadingHighEdge) {
-            return false;
-        }
-        final int c = getChildCount();
-        for (int i = c - 1; i >= 0; i--) {
-            View view = getChildAt(i);
-            if (mLayoutManager.getOpticalRight(view) > getWidth()
-                    - getPaddingRight() + mHighFadeShaderOffset) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private Bitmap getTempBitmapLow() {
-        if (mTempBitmapLow == null
-                || mTempBitmapLow.getWidth() != mLowFadeShaderLength
-                || mTempBitmapLow.getHeight() != getHeight()) {
-            mTempBitmapLow = Bitmap.createBitmap(mLowFadeShaderLength, getHeight(),
-                    Bitmap.Config.ARGB_8888);
-        }
-        return mTempBitmapLow;
-    }
-
-    private Bitmap getTempBitmapHigh() {
-        if (mTempBitmapHigh == null
-                || mTempBitmapHigh.getWidth() != mHighFadeShaderLength
-                || mTempBitmapHigh.getHeight() != getHeight()) {
-            // TODO: fix logic for sharing mTempBitmapLow
-            if (false && mTempBitmapLow != null
-                    && mTempBitmapLow.getWidth() == mHighFadeShaderLength
-                    && mTempBitmapLow.getHeight() == getHeight()) {
-                // share same bitmap for low edge fading and high edge fading.
-                mTempBitmapHigh = mTempBitmapLow;
-            } else {
-                mTempBitmapHigh = Bitmap.createBitmap(mHighFadeShaderLength, getHeight(),
-                        Bitmap.Config.ARGB_8888);
-            }
-        }
-        return mTempBitmapHigh;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        final boolean needsFadingLow = needsFadingLowEdge();
-        final boolean needsFadingHigh = needsFadingHighEdge();
-        if (!needsFadingLow) {
-            mTempBitmapLow = null;
-        }
-        if (!needsFadingHigh) {
-            mTempBitmapHigh = null;
-        }
-        if (!needsFadingLow && !needsFadingHigh) {
-            super.draw(canvas);
-            return;
-        }
-
-        int lowEdge = mFadingLowEdge? getPaddingLeft() - mLowFadeShaderOffset - mLowFadeShaderLength : 0;
-        int highEdge = mFadingHighEdge ? getWidth() - getPaddingRight()
-                + mHighFadeShaderOffset + mHighFadeShaderLength : getWidth();
-
-        // draw not-fade content
-        int save = canvas.save();
-        canvas.clipRect(lowEdge + (mFadingLowEdge ? mLowFadeShaderLength : 0), 0,
-                highEdge - (mFadingHighEdge ? mHighFadeShaderLength : 0), getHeight());
-        super.draw(canvas);
-        canvas.restoreToCount(save);
-
-        Canvas tmpCanvas = new Canvas();
-        mTempRect.top = 0;
-        mTempRect.bottom = getHeight();
-        if (needsFadingLow && mLowFadeShaderLength > 0) {
-            Bitmap tempBitmap = getTempBitmapLow();
-            tempBitmap.eraseColor(Color.TRANSPARENT);
-            tmpCanvas.setBitmap(tempBitmap);
-            // draw original content
-            int tmpSave = tmpCanvas.save();
-            tmpCanvas.clipRect(0, 0, mLowFadeShaderLength, getHeight());
-            tmpCanvas.translate(-lowEdge, 0);
-            super.draw(tmpCanvas);
-            tmpCanvas.restoreToCount(tmpSave);
-            // draw fading out
-            mTempPaint.setShader(mLowFadeShader);
-            tmpCanvas.drawRect(0, 0, mLowFadeShaderLength, getHeight(), mTempPaint);
-            // copy back to canvas
-            mTempRect.left = 0;
-            mTempRect.right = mLowFadeShaderLength;
-            canvas.translate(lowEdge, 0);
-            canvas.drawBitmap(tempBitmap, mTempRect, mTempRect, null);
-            canvas.translate(-lowEdge, 0);
-        }
-        if (needsFadingHigh && mHighFadeShaderLength > 0) {
-            Bitmap tempBitmap = getTempBitmapHigh();
-            tempBitmap.eraseColor(Color.TRANSPARENT);
-            tmpCanvas.setBitmap(tempBitmap);
-            // draw original content
-            int tmpSave = tmpCanvas.save();
-            tmpCanvas.clipRect(0, 0, mHighFadeShaderLength, getHeight());
-            tmpCanvas.translate(-(highEdge - mHighFadeShaderLength), 0);
-            super.draw(tmpCanvas);
-            tmpCanvas.restoreToCount(tmpSave);
-            // draw fading out
-            mTempPaint.setShader(mHighFadeShader);
-            tmpCanvas.drawRect(0, 0, mHighFadeShaderLength, getHeight(), mTempPaint);
-            // copy back to canvas
-            mTempRect.left = 0;
-            mTempRect.right = mHighFadeShaderLength;
-            canvas.translate(highEdge - mHighFadeShaderLength, 0);
-            canvas.drawBitmap(tempBitmap, mTempRect, mTempRect, null);
-            canvas.translate(-(highEdge - mHighFadeShaderLength), 0);
-        }
-    }
-
-    /**
-     * Updates the layer type for this view.
-     * If fading edges are needed, use a hardware layer.  This works around the problem
-     * that when a child invalidates itself (for example has an animated background),
-     * the parent view must also be invalidated to refresh the display list which
-     * updates the the caching bitmaps used to draw the fading edges.
-     */
-    private void updateLayerType() {
-        if (mFadingLowEdge || mFadingHighEdge) {
-            setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            setWillNotDraw(false);
-        } else {
-            setLayerType(View.LAYER_TYPE_NONE, null);
-            setWillNotDraw(true);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java b/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
deleted file mode 100644
index 8aead19..0000000
--- a/android/support/v17/leanback/widget/HorizontalHoverCardSwitcher.java
+++ /dev/null
@@ -1,77 +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.v17.leanback.widget;
-
-import android.graphics.Rect;
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-
-/**
- * A helper class for showing a hover card view below a {@link HorizontalGridView}.  The hover card
- * is aligned to the starting edge of the selected child view.  If there is no space when scrolling
- * to the end, the ending edge of the hover card will be aligned to the ending edge of the parent
- * view, excluding padding.
- */
-public final class HorizontalHoverCardSwitcher extends PresenterSwitcher {
-    // left and right of selected card view
-    int mCardLeft, mCardRight;
-
-    private int[] mTmpOffsets = new int[2];
-    private Rect mTmpRect = new Rect();
-
-    @Override
-    protected void insertView(View view) {
-        // append hovercard to the end of container
-        getParentViewGroup().addView(view);
-    }
-
-    @Override
-    protected void onViewSelected(View view) {
-        int rightLimit = getParentViewGroup().getWidth() - getParentViewGroup().getPaddingRight();
-        int leftLimit = getParentViewGroup().getPaddingLeft();
-        // measure the hover card width; if it's too large, align hover card
-        // 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) == ViewCompat.LAYOUT_DIRECTION_RTL;
-        if (!isRtl && mCardLeft + view.getMeasuredWidth() > rightLimit) {
-            params.leftMargin = rightLimit  - view.getMeasuredWidth();
-        } else if (isRtl && mCardLeft < leftLimit) {
-            params.leftMargin = leftLimit;
-        } else if (isRtl) {
-            params.leftMargin = mCardRight - view.getMeasuredWidth();
-        } else {
-            params.leftMargin = mCardLeft;
-        }
-        view.requestLayout();
-    }
-
-    /**
-     * Select a childView inside a grid view and create/bind a corresponding hover card view
-     * for the object.
-     */
-    public void select(HorizontalGridView gridView, View childView, Object object) {
-        ViewGroup parent = getParentViewGroup();
-        gridView.getViewSelectedOffsets(childView, mTmpOffsets);
-        mTmpRect.set(0, 0, childView.getWidth(), childView.getHeight());
-        parent.offsetDescendantRectToMyCoords(childView, mTmpRect);
-        mCardLeft = mTmpRect.left - mTmpOffsets[0];
-        mCardRight = mTmpRect.right - mTmpOffsets[0];
-        select(object);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ImageCardView.java b/android/support/v17/leanback/widget/ImageCardView.java
deleted file mode 100644
index 53b27c6..0000000
--- a/android/support/v17/leanback/widget/ImageCardView.java
+++ /dev/null
@@ -1,494 +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.v17.leanback.widget;
-
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-/**
- * A subclass of {@link BaseCardView} with an {@link ImageView} as its main region. The
- * {@link ImageCardView} is highly customizable and can be used for various use-cases by adjusting
- * the ImageViewCard's type to any combination of Title, Content, Badge or ImageOnly.
- * <p>
- * <h3>Styling</h3> There are two different ways to style the ImageCardView. <br>
- * No matter what way you use, all your styles applied to an ImageCardView have to extend the style
- * {@link R.style#Widget_Leanback_ImageCardViewStyle}.
- * <p>
- * <u>Example:</u><br>
- * 
- * <pre>
- * {@code
- * <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
-        <item name="cardBackground">#F0F</item>
-        <item name="lbImageCardViewType">Title|Content</item>
-   </style>
-   <style name="CustomImageCardTheme" parent="Theme.Leanback">
-        <item name="imageCardViewStyle">@style/CustomImageCardViewStyle</item>
-        <item name="imageCardViewInfoAreaStyle">@style/ImageCardViewColoredInfoArea</item>
-        <item name="imageCardViewTitleStyle">@style/ImageCardViewColoredTitle</item>
-    </style>}
- * </pre>
- * <p>
- * The first possibility is to set custom Styles in the Leanback Theme's attributes
- * <code>imageCardViewStyle</code>, <code>imageCardViewTitleStyle</code> etc. The styles set here,
- * is the default style for all ImageCardViews.
- * <p>
- * The second possibility allows you to style a particular ImageCardView. This is useful if you
- * want to create multiple types of cards. E.g. you might want to display a card with only a title
- * and another one with title and content. Thus you need to define two different
- * <code>ImageCardViewStyles</code> and two different themes and apply them to the ImageCardViews.
- * You can do this by using a the {@link #ImageCardView(Context)} constructor and passing a
- * ContextThemeWrapper with the custom ImageCardView theme id.
- * <p>
- * <u>Example (using constructor):</u><br>
- * 
- * <pre>
- * {@code
- *     new ImageCardView(new ContextThemeWrapper(context, R.style.CustomImageCardTheme));
- * }
- * </pre>
- * 
- * <p>
- * You can style all ImageCardView's components such as the title, content, badge, infoArea and the
- * image itself by extending the corresponding style and overriding the specific attribute in your
- * custom ImageCardView theme.
- * 
- * <h3>Components</h3> The ImageCardView contains three components which can be combined in any
- * combination:
- * <ul>
- * <li>Title: The card's title</li>
- * <li>Content: A short description</li>
- * <li>Badge: An icon which can be displayed on the right or left side of the card.</li>
- * </ul>
- * In order to choose the components you want to use in your ImageCardView, you have to specify them
- * in the <code>lbImageCardViewType</code> attribute of your custom <code>ImageCardViewStyle</code>.
- * You can combine the following values:
- * <code>Title, Content, IconOnRight, IconOnLeft, ImageOnly</code>.
- * <p>
- * <u>Examples:</u><br>
- * 
- * <pre>
- * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
-        ...
-        <item name="lbImageCardViewType">Title|Content|IconOnLeft</item>
-        ...
-    </style>}
- * </pre>
- * 
- * <pre>
- * {@code <style name="CustomImageCardViewStyle" parent="Widget.Leanback.ImageCardViewStyle">
-        ...
-        <item name="lbImageCardViewType">ImageOnly</item>
-        ...
-    </style>}
- * </pre>
- * 
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewStyle
- * @attr ref android.support.v17.leanback.R.styleable#lbImageCardView_lbImageCardViewType
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewTitleStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewContentStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewBadgeStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewImageStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackTheme_imageCardViewInfoAreaStyle
- */
-public class ImageCardView extends BaseCardView {
-
-    public static final int CARD_TYPE_FLAG_IMAGE_ONLY = 0;
-    public static final int CARD_TYPE_FLAG_TITLE = 1;
-    public static final int CARD_TYPE_FLAG_CONTENT = 2;
-    public static final int CARD_TYPE_FLAG_ICON_RIGHT = 4;
-    public static final int CARD_TYPE_FLAG_ICON_LEFT = 8;
-
-    private static final String ALPHA = "alpha";
-
-    private ImageView mImageView;
-    private ViewGroup mInfoArea;
-    private TextView mTitleView;
-    private TextView mContentView;
-    private ImageView mBadgeImage;
-    private boolean mAttachedToWindow;
-    ObjectAnimator mFadeInAnimator;
-
-    /**
-     * Create an ImageCardView using a given theme for customization.
-     * 
-     * @param context
-     *            The Context the view is running in, through which it can
-     *            access the current theme, resources, etc.
-     * @param themeResId
-     *            The resourceId of the theme you want to apply to the ImageCardView. The theme
-     *            includes attributes "imageCardViewStyle", "imageCardViewTitleStyle",
-     *            "imageCardViewContentStyle" etc. to customize individual part of ImageCardView.
-     * @deprecated Calling this constructor inefficiently creates one ContextThemeWrapper per card,
-     * you should share it in card Presenter: wrapper = new ContextThemeWrapper(context, themResId);
-     * return new ImageCardView(wrapper);
-     */
-    @Deprecated
-    public ImageCardView(Context context, int themeResId) {
-        this(new ContextThemeWrapper(context, themeResId));
-    }
-
-    /**
-     * @see #View(Context, AttributeSet, int)
-     */
-    public ImageCardView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        buildImageCardView(attrs, defStyleAttr, R.style.Widget_Leanback_ImageCardView);
-    }
-
-    private void buildImageCardView(AttributeSet attrs, int defStyleAttr, int defStyle) {
-        // Make sure the ImageCardView is focusable.
-        setFocusable(true);
-        setFocusableInTouchMode(true);
-
-        LayoutInflater inflater = LayoutInflater.from(getContext());
-        inflater.inflate(R.layout.lb_image_card_view, this);
-        TypedArray cardAttrs = getContext().obtainStyledAttributes(attrs,
-                R.styleable.lbImageCardView, defStyleAttr, defStyle);
-        int cardType = cardAttrs
-                .getInt(R.styleable.lbImageCardView_lbImageCardViewType, CARD_TYPE_FLAG_IMAGE_ONLY);
-
-        boolean hasImageOnly = cardType == CARD_TYPE_FLAG_IMAGE_ONLY;
-        boolean hasTitle = (cardType & CARD_TYPE_FLAG_TITLE) == CARD_TYPE_FLAG_TITLE;
-        boolean hasContent = (cardType & CARD_TYPE_FLAG_CONTENT) == CARD_TYPE_FLAG_CONTENT;
-        boolean hasIconRight = (cardType & CARD_TYPE_FLAG_ICON_RIGHT) == CARD_TYPE_FLAG_ICON_RIGHT;
-        boolean hasIconLeft =
-                !hasIconRight && (cardType & CARD_TYPE_FLAG_ICON_LEFT) == CARD_TYPE_FLAG_ICON_LEFT;
-
-        mImageView = findViewById(R.id.main_image);
-        if (mImageView.getDrawable() == null) {
-            mImageView.setVisibility(View.INVISIBLE);
-        }
-        // Set Object Animator for image view.
-        mFadeInAnimator = ObjectAnimator.ofFloat(mImageView, ALPHA, 1f);
-        mFadeInAnimator.setDuration(
-                mImageView.getResources().getInteger(android.R.integer.config_shortAnimTime));
-
-        mInfoArea = findViewById(R.id.info_field);
-        if (hasImageOnly) {
-            removeView(mInfoArea);
-            cardAttrs.recycle();
-            return;
-        }
-        // Create children
-        if (hasTitle) {
-            mTitleView = (TextView) inflater.inflate(R.layout.lb_image_card_view_themed_title,
-                    mInfoArea, false);
-            mInfoArea.addView(mTitleView);
-        }
-
-        if (hasContent) {
-            mContentView = (TextView) inflater.inflate(R.layout.lb_image_card_view_themed_content,
-                    mInfoArea, false);
-            mInfoArea.addView(mContentView);
-        }
-
-        if (hasIconRight || hasIconLeft) {
-            int layoutId = R.layout.lb_image_card_view_themed_badge_right;
-            if (hasIconLeft) {
-                layoutId = R.layout.lb_image_card_view_themed_badge_left;
-            }
-            mBadgeImage = (ImageView) inflater.inflate(layoutId, mInfoArea, false);
-            mInfoArea.addView(mBadgeImage);
-        }
-
-        // Set up LayoutParams for children
-        if (hasTitle && !hasContent && mBadgeImage != null) {
-            RelativeLayout.LayoutParams relativeLayoutParams =
-                    (RelativeLayout.LayoutParams) mTitleView.getLayoutParams();
-            // Adjust title TextView if there is an icon but no content
-            if (hasIconLeft) {
-                relativeLayoutParams.addRule(RelativeLayout.END_OF, mBadgeImage.getId());
-            } else {
-                relativeLayoutParams.addRule(RelativeLayout.START_OF, mBadgeImage.getId());
-            }
-            mTitleView.setLayoutParams(relativeLayoutParams);
-        }
-
-        // Set up LayoutParams for children
-        if (hasContent) {
-            RelativeLayout.LayoutParams relativeLayoutParams =
-                    (RelativeLayout.LayoutParams) mContentView.getLayoutParams();
-            if (!hasTitle) {
-                relativeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
-            }
-            // Adjust content TextView if icon is on the left
-            if (hasIconLeft) {
-                relativeLayoutParams.removeRule(RelativeLayout.START_OF);
-                relativeLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
-                relativeLayoutParams.addRule(RelativeLayout.END_OF, mBadgeImage.getId());
-            }
-            mContentView.setLayoutParams(relativeLayoutParams);
-        }
-
-        if (mBadgeImage != null) {
-            RelativeLayout.LayoutParams relativeLayoutParams =
-                    (RelativeLayout.LayoutParams) mBadgeImage.getLayoutParams();
-            if (hasContent) {
-                relativeLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mContentView.getId());
-            } else if (hasTitle) {
-                relativeLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mTitleView.getId());
-            }
-            mBadgeImage.setLayoutParams(relativeLayoutParams);
-        }
-
-        // Backward compatibility: Newly created ImageCardViews should change
-        // the InfoArea's background color in XML using the corresponding style.
-        // However, since older implementations might make use of the
-        // 'infoAreaBackground' attribute, we have to make sure to support it.
-        // If the user has set a specific value here, it will differ from null.
-        // In this case, we do want to override the value set in the style.
-        Drawable background = cardAttrs.getDrawable(R.styleable.lbImageCardView_infoAreaBackground);
-        if (null != background) {
-            setInfoAreaBackground(background);
-        }
-        // Backward compatibility: There has to be an icon in the default
-        // version. If there is one, we have to set its visibility to 'GONE'.
-        // Disabling 'adjustIconVisibility' allows the user to set the icon's
-        // visibility state in XML rather than code.
-        if (mBadgeImage != null && mBadgeImage.getDrawable() == null) {
-            mBadgeImage.setVisibility(View.GONE);
-        }
-        cardAttrs.recycle();
-    }
-
-    /**
-     * @see #View(Context)
-     */
-    public ImageCardView(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * @see #View(Context, AttributeSet)
-     */
-    public ImageCardView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.imageCardViewStyle);
-    }
-
-    /**
-     * Returns the main image view.
-     */
-    public final ImageView getMainImageView() {
-        return mImageView;
-    }
-
-    /**
-     * Enables or disables adjustment of view bounds on the main image.
-     */
-    public void setMainImageAdjustViewBounds(boolean adjustViewBounds) {
-        if (mImageView != null) {
-            mImageView.setAdjustViewBounds(adjustViewBounds);
-        }
-    }
-
-    /**
-     * Sets the ScaleType of the main image.
-     */
-    public void setMainImageScaleType(ScaleType scaleType) {
-        if (mImageView != null) {
-            mImageView.setScaleType(scaleType);
-        }
-    }
-
-    /**
-     * Sets the image drawable with fade-in animation.
-     */
-    public void setMainImage(Drawable drawable) {
-        setMainImage(drawable, true);
-    }
-
-    /**
-     * Sets the image drawable with optional fade-in animation.
-     */
-    public void setMainImage(Drawable drawable, boolean fade) {
-        if (mImageView == null) {
-            return;
-        }
-
-        mImageView.setImageDrawable(drawable);
-        if (drawable == null) {
-            mFadeInAnimator.cancel();
-            mImageView.setAlpha(1f);
-            mImageView.setVisibility(View.INVISIBLE);
-        } else {
-            mImageView.setVisibility(View.VISIBLE);
-            if (fade) {
-                fadeIn();
-            } else {
-                mFadeInAnimator.cancel();
-                mImageView.setAlpha(1f);
-            }
-        }
-    }
-
-    /**
-     * Sets the layout dimensions of the ImageView.
-     */
-    public void setMainImageDimensions(int width, int height) {
-        ViewGroup.LayoutParams lp = mImageView.getLayoutParams();
-        lp.width = width;
-        lp.height = height;
-        mImageView.setLayoutParams(lp);
-    }
-
-    /**
-     * Returns the ImageView drawable.
-     */
-    public Drawable getMainImage() {
-        if (mImageView == null) {
-            return null;
-        }
-
-        return mImageView.getDrawable();
-    }
-
-    /**
-     * Returns the info area background drawable.
-     */
-    public Drawable getInfoAreaBackground() {
-        if (mInfoArea != null) {
-            return mInfoArea.getBackground();
-        }
-        return null;
-    }
-
-    /**
-     * Sets the info area background drawable.
-     */
-    public void setInfoAreaBackground(Drawable drawable) {
-        if (mInfoArea != null) {
-            mInfoArea.setBackground(drawable);
-        }
-    }
-
-    /**
-     * Sets the info area background color.
-     */
-    public void setInfoAreaBackgroundColor(@ColorInt int color) {
-        if (mInfoArea != null) {
-            mInfoArea.setBackgroundColor(color);
-        }
-    }
-
-    /**
-     * Sets the title text.
-     */
-    public void setTitleText(CharSequence text) {
-        if (mTitleView == null) {
-            return;
-        }
-        mTitleView.setText(text);
-    }
-
-    /**
-     * Returns the title text.
-     */
-    public CharSequence getTitleText() {
-        if (mTitleView == null) {
-            return null;
-        }
-
-        return mTitleView.getText();
-    }
-
-    /**
-     * Sets the content text.
-     */
-    public void setContentText(CharSequence text) {
-        if (mContentView == null) {
-            return;
-        }
-        mContentView.setText(text);
-    }
-
-    /**
-     * Returns the content text.
-     */
-    public CharSequence getContentText() {
-        if (mContentView == null) {
-            return null;
-        }
-
-        return mContentView.getText();
-    }
-
-    /**
-     * Sets the badge image drawable.
-     */
-    public void setBadgeImage(Drawable drawable) {
-        if (mBadgeImage == null) {
-            return;
-        }
-        mBadgeImage.setImageDrawable(drawable);
-        if (drawable != null) {
-            mBadgeImage.setVisibility(View.VISIBLE);
-        } else {
-            mBadgeImage.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Returns the badge image drawable.
-     */
-    public Drawable getBadgeImage() {
-        if (mBadgeImage == null) {
-            return null;
-        }
-
-        return mBadgeImage.getDrawable();
-    }
-
-    private void fadeIn() {
-        mImageView.setAlpha(0f);
-        if (mAttachedToWindow) {
-            mFadeInAnimator.start();
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mAttachedToWindow = true;
-        if (mImageView.getAlpha() == 0) {
-            fadeIn();
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mAttachedToWindow = false;
-        mFadeInAnimator.cancel();
-        mImageView.setAlpha(1f);
-        super.onDetachedFromWindow();
-    }
-}
diff --git a/android/support/v17/leanback/widget/ImeKeyMonitor.java b/android/support/v17/leanback/widget/ImeKeyMonitor.java
deleted file mode 100644
index 4691ad2..0000000
--- a/android/support/v17/leanback/widget/ImeKeyMonitor.java
+++ /dev/null
@@ -1,47 +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.v17.leanback.widget;
-
-import android.widget.EditText;
-import android.view.KeyEvent;
-
-/**
- * Interface for an EditText subclass that can delegate calls to onKeyPreIme up to a registered
- * listener.
- * <p>
- * Used in editable actions within {@link android.support.v17.leanback.app.GuidedStepFragment} to
- * allow for custom back key handling. Specifically, this is used to implement the behavior that
- * dismissing the IME also clears edit text focus. Clients who need to supply custom layouts for
- * {@link GuidedActionsStylist} with their own EditText classes should satisfy this interface in
- * order to inherit this behavior.
- */
-public interface ImeKeyMonitor {
-
-    /**
-     * Listener interface for key events intercepted pre-IME by edit text objects.
-     */
-    public interface ImeKeyListener {
-        /**
-         * Callback invoked from EditText's onKeyPreIme method override. Returning true tells the
-         * caller that the key event is handled and should not be propagated.
-         */
-        public abstract boolean onKeyPreIme(EditText editText, int keyCode, KeyEvent event);
-    }
-
-    /**
-     * Set the listener for this edit text object. The listener's onKeyPreIme method will be
-     * invoked from the host edit text's onKeyPreIme method.
-     */
-    public void setImeKeyListener(ImeKeyListener listener);
-}
diff --git a/android/support/v17/leanback/widget/InvisibleRowPresenter.java b/android/support/v17/leanback/widget/InvisibleRowPresenter.java
deleted file mode 100644
index bf898c2..0000000
--- a/android/support/v17/leanback/widget/InvisibleRowPresenter.java
+++ /dev/null
@@ -1,38 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class InvisibleRowPresenter extends RowPresenter {
-
-    public InvisibleRowPresenter() {
-        setHeaderPresenter(null);
-    }
-
-    @Override
-    protected ViewHolder createRowViewHolder(ViewGroup parent) {
-        RelativeLayout root = new RelativeLayout(parent.getContext());
-        root.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
-        return new ViewHolder(root);
-    }
-}
diff --git a/android/support/v17/leanback/widget/ItemAlignment.java b/android/support/v17/leanback/widget/ItemAlignment.java
deleted file mode 100644
index e1b7d13..0000000
--- a/android/support/v17/leanback/widget/ItemAlignment.java
+++ /dev/null
@@ -1,79 +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.v17.leanback.widget;
-
-import static android.support.v7.widget.RecyclerView.HORIZONTAL;
-import static android.support.v7.widget.RecyclerView.VERTICAL;
-
-import android.view.View;
-
-/**
- * Defines alignment position on two directions of an item view. Typically item
- * view alignment is at the center of the view. The class allows defining
- * alignment at left/right or fixed offset/percentage position; it also allows
- * using descendant view by id match.
- */
-class ItemAlignment {
-
-    final static class Axis extends ItemAlignmentFacet.ItemAlignmentDef {
-        private int mOrientation;
-
-        Axis(int orientation) {
-            mOrientation = orientation;
-        }
-
-        /**
-         * get alignment position relative to optical left/top of itemView.
-         */
-        public int getAlignmentPosition(View itemView) {
-            return ItemAlignmentFacetHelper.getAlignmentPosition(itemView, this, mOrientation);
-        }
-    }
-
-    private int mOrientation = HORIZONTAL;
-
-    final public Axis vertical = new Axis(VERTICAL);
-
-    final public Axis horizontal = new Axis(HORIZONTAL);
-
-    private Axis mMainAxis = horizontal;
-
-    private Axis mSecondAxis = vertical;
-
-    final public Axis mainAxis() {
-        return mMainAxis;
-    }
-
-    final public Axis secondAxis() {
-        return mSecondAxis;
-    }
-
-    final public void setOrientation(int orientation) {
-        mOrientation = orientation;
-        if (mOrientation == HORIZONTAL) {
-            mMainAxis = horizontal;
-            mSecondAxis = vertical;
-        } else {
-            mMainAxis = vertical;
-            mSecondAxis = horizontal;
-        }
-    }
-
-    final public int getOrientation() {
-        return mOrientation;
-    }
-
-
-}
diff --git a/android/support/v17/leanback/widget/ItemAlignmentFacet.java b/android/support/v17/leanback/widget/ItemAlignmentFacet.java
deleted file mode 100644
index 1ca735e..0000000
--- a/android/support/v17/leanback/widget/ItemAlignmentFacet.java
+++ /dev/null
@@ -1,209 +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.v17.leanback.widget;
-
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-/**
- * Optional facet provided by {@link RecyclerView.Adapter} or {@link RecyclerView.ViewHolder} for
- * use in {@link HorizontalGridView} and {@link VerticalGridView}. Apps using {@link Presenter} may
- * set facet using {@link Presenter#setFacet(Class, Object)} or
- * {@link Presenter.ViewHolder#setFacet(Class, Object)}. Facet on ViewHolder has a higher priority
- * than Presenter or Adapter.
- * <p>
- * 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} (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. Equivalent to 0.
-     */
-    public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1;
-
-    /**
-     * Definition of an alignment position under a view.
-     */
-    public static class ItemAlignmentDef {
-        int mViewId = View.NO_ID;
-        int mFocusViewId = View.NO_ID;
-        int mOffset = 0;
-        float mOffsetPercent = 50f;
-        boolean mOffsetWithPadding = false;
-        private boolean mAlignToBaseline;
-
-        /**
-         * 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;
-        }
-
-        /**
-         * 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 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;
-        }
-
-        /**
-         * 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;
-        }
-
-        /**
-         * Sets the offset percent for item alignment in addition to offset.  E.g., 40
-         * 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)
-                    && percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
-                throw new IllegalArgumentException();
-            }
-            mOffsetPercent = percent;
-        }
-
-        /**
-         * Gets the offset percent for item alignment in addition to offset. E.g., 40
-         * 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;
-        }
-
-        /**
-         * Sets 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.
-         *
-         * @param viewId The id of child view that will be aligned to.
-         * @see #setItemAlignmentFocusViewId(int)
-         */
-        public final void setItemAlignmentViewId(int viewId) {
-            mViewId = viewId;
-        }
-
-        /**
-         * 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;
-        }
-
-        /**
-         * Sets Id of which child view take focus for alignment.  When not set, it will use
-         * 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;
-        }
-
-        /**
-         * Returns Id of which child view take focus for alignment.  When not set, it will use
-         * use same id of {@link #getItemAlignmentViewId()}
-         */
-        public final int getItemAlignmentFocusViewId() {
-            return mFocusViewId != View.NO_ID ? mFocusViewId : mViewId;
-        }
-
-        /**
-         * 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 View should be aligned to {@link View#getBaseline()}
-         */
-        public boolean isAlignedToTextViewBaseLine() {
-            return mAlignToBaseline;
-        }
-    }
-
-    private ItemAlignmentDef[] mAlignmentDefs = new ItemAlignmentDef[]{new ItemAlignmentDef()};
-
-    public boolean isMultiAlignment() {
-        return mAlignmentDefs.length > 1;
-    }
-
-    /**
-     * Sets definitions of alignment positions.
-     */
-    public void setAlignmentDefs(ItemAlignmentDef[] defs) {
-        if (defs == null || defs.length < 1) {
-            throw new IllegalArgumentException();
-        }
-        mAlignmentDefs = defs;
-    }
-
-    /**
-     * Returns read only definitions of alignment positions.
-     */
-    public ItemAlignmentDef[] getAlignmentDefs() {
-        return mAlignmentDefs;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java b/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
deleted file mode 100644
index 7dc946e..0000000
--- a/android/support/v17/leanback/widget/ItemAlignmentFacetHelper.java
+++ /dev/null
@@ -1,107 +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.v17.leanback.widget;
-
-import static android.support.v17.leanback.widget.ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
-import static android.support.v7.widget.RecyclerView.HORIZONTAL;
-
-import android.graphics.Rect;
-import android.support.v17.leanback.widget.GridLayoutManager.LayoutParams;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Helper class to handle ItemAlignmentFacet in a grid view.
- */
-class ItemAlignmentFacetHelper {
-
-    private static Rect sRect = new Rect();
-
-    /**
-     * get alignment position relative to optical left/top of itemView.
-     */
-    static int getAlignmentPosition(View itemView, ItemAlignmentFacet.ItemAlignmentDef facet,
-            int orientation) {
-        LayoutParams p = (LayoutParams) itemView.getLayoutParams();
-        View view = itemView;
-        if (facet.mViewId != 0) {
-            view = itemView.findViewById(facet.mViewId);
-            if (view == null) {
-                view = itemView;
-            }
-        }
-        int alignPos = facet.mOffset;
-        if (orientation == HORIZONTAL) {
-            if (itemView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-                alignPos = (view == itemView ? p.getOpticalWidth(view)
-                        : view.getWidth()) - alignPos;
-                if (facet.mOffsetWithPadding) {
-                    if (facet.mOffsetPercent == 0f) {
-                        alignPos -= view.getPaddingRight();
-                    } else if (facet.mOffsetPercent == 100f) {
-                        alignPos += view.getPaddingLeft();
-                    }
-                }
-                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) {
-                    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();
-                }
-            }
-        } else {
-            if (facet.mOffsetWithPadding) {
-                if (facet.mOffsetPercent == 0f) {
-                    alignPos += view.getPaddingTop();
-                } else if (facet.mOffsetPercent == 100f) {
-                    alignPos -= view.getPaddingBottom();
-                }
-            }
-            if (facet.mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
-                alignPos += (int) (((view == itemView ? p.getOpticalHeight(view) : view.getHeight())
-                        * facet.mOffsetPercent) / 100f);
-            }
-            if (itemView != view) {
-                sRect.top = alignPos;
-                ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, sRect);
-                alignPos = sRect.top - p.getOpticalTopInset();
-            }
-            if (facet.isAlignedToTextViewBaseLine()) {
-                alignPos += view.getBaseline();
-            }
-        }
-        return alignPos;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ItemBridgeAdapter.java b/android/support/v17/leanback/widget/ItemBridgeAdapter.java
deleted file mode 100644
index 9f68e04..0000000
--- a/android/support/v17/leanback/widget/ItemBridgeAdapter.java
+++ /dev/null
@@ -1,447 +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.v17.leanback.widget;
-
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Bridge from {@link Presenter} to {@link RecyclerView.Adapter}. Public to allow use by third
- * party Presenters.
- */
-public class ItemBridgeAdapter extends RecyclerView.Adapter implements FacetProviderAdapter {
-    static final String TAG = "ItemBridgeAdapter";
-    static final boolean DEBUG = false;
-
-    /**
-     * Interface for listening to ViewHolder operations.
-     */
-    public static class AdapterListener {
-        public void onAddPresenter(Presenter presenter, int type) {
-        }
-
-        public void onCreate(ViewHolder viewHolder) {
-        }
-
-        public void onBind(ViewHolder viewHolder) {
-        }
-
-        public void onBind(ViewHolder viewHolder, List payloads) {
-            onBind(viewHolder);
-        }
-
-        public void onUnbind(ViewHolder viewHolder) {
-        }
-
-        public void onAttachedToWindow(ViewHolder viewHolder) {
-        }
-
-        public void onDetachedFromWindow(ViewHolder viewHolder) {
-        }
-    }
-
-    /**
-     * Interface for wrapping a view created by a Presenter into another view.
-     * The wrapper must be the immediate parent of the wrapped view.
-     */
-    public static abstract class Wrapper {
-        public abstract View createWrapper(View root);
-
-        public abstract void wrap(View wrapper, View wrapped);
-    }
-
-    private ObjectAdapter mAdapter;
-    Wrapper mWrapper;
-    private PresenterSelector mPresenterSelector;
-    FocusHighlightHandler mFocusHighlight;
-    private AdapterListener mAdapterListener;
-    private ArrayList<Presenter> mPresenters = new ArrayList<Presenter>();
-
-    final class OnFocusChangeListener implements View.OnFocusChangeListener {
-        View.OnFocusChangeListener mChainedListener;
-
-        @Override
-        public void onFocusChange(View view, boolean hasFocus) {
-            if (DEBUG) {
-                Log.v(TAG, "onFocusChange " + hasFocus + " " + view
-                        + " mFocusHighlight" + mFocusHighlight);
-            }
-            if (mWrapper != null) {
-                view = (View) view.getParent();
-            }
-            if (mFocusHighlight != null) {
-                mFocusHighlight.onItemFocused(view, hasFocus);
-            }
-            if (mChainedListener != null) {
-                mChainedListener.onFocusChange(view, hasFocus);
-            }
-        }
-    }
-
-    /**
-     * ViewHolder for the ItemBridgeAdapter.
-     */
-    public class ViewHolder extends RecyclerView.ViewHolder implements FacetProvider {
-        final Presenter mPresenter;
-        final Presenter.ViewHolder mHolder;
-        final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener();
-        Object mItem;
-        Object mExtraObject;
-
-        /**
-         * Get {@link Presenter}.
-         */
-        public final Presenter getPresenter() {
-            return mPresenter;
-        }
-
-        /**
-         * Get {@link Presenter.ViewHolder}.
-         */
-        public final Presenter.ViewHolder getViewHolder() {
-            return mHolder;
-        }
-
-        /**
-         * Get currently bound object.
-         */
-        public final Object getItem() {
-            return mItem;
-        }
-
-        /**
-         * Get extra object associated with the view.  Developer can attach
-         * any customized UI object in addition to {@link Presenter.ViewHolder}.
-         * A typical use case is attaching an animator object.
-         */
-        public final Object getExtraObject() {
-            return mExtraObject;
-        }
-
-        /**
-         * Set extra object associated with the view.  Developer can attach
-         * any customized UI object in addition to {@link Presenter.ViewHolder}.
-         * A typical use case is attaching an animator object.
-         */
-        public void setExtraObject(Object object) {
-            mExtraObject = object;
-        }
-
-        @Override
-        public Object getFacet(Class<?> facetClass) {
-            return mHolder.getFacet(facetClass);
-        }
-
-        ViewHolder(Presenter presenter, View view, Presenter.ViewHolder holder) {
-            super(view);
-            mPresenter = presenter;
-            mHolder = holder;
-        }
-    }
-
-    private ObjectAdapter.DataObserver mDataObserver = new ObjectAdapter.DataObserver() {
-        @Override
-        public void onChanged() {
-            ItemBridgeAdapter.this.notifyDataSetChanged();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount);
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
-            ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount, payload);
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            ItemBridgeAdapter.this.notifyItemRangeInserted(positionStart, itemCount);
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            ItemBridgeAdapter.this.notifyItemRangeRemoved(positionStart, itemCount);
-        }
-
-        @Override
-        public void onItemMoved(int fromPosition, int toPosition) {
-            ItemBridgeAdapter.this.notifyItemMoved(fromPosition, toPosition);
-        }
-    };
-
-    public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
-        setAdapter(adapter);
-        mPresenterSelector = presenterSelector;
-    }
-
-    public ItemBridgeAdapter(ObjectAdapter adapter) {
-        this(adapter, null);
-    }
-
-    public ItemBridgeAdapter() {
-    }
-
-    /**
-     * Sets the {@link ObjectAdapter}.
-     */
-    public void setAdapter(ObjectAdapter adapter) {
-        if (adapter == mAdapter) {
-            return;
-        }
-        if (mAdapter != null) {
-            mAdapter.unregisterObserver(mDataObserver);
-        }
-        mAdapter = adapter;
-        if (mAdapter == null) {
-            notifyDataSetChanged();
-            return;
-        }
-
-        mAdapter.registerObserver(mDataObserver);
-        if (hasStableIds() != mAdapter.hasStableIds()) {
-            setHasStableIds(mAdapter.hasStableIds());
-        }
-        notifyDataSetChanged();
-    }
-
-    /**
-     * Changes Presenter that creates and binds the view.
-     *
-     * @param presenterSelector Presenter that creates and binds the view.
-     */
-    public void setPresenter(PresenterSelector presenterSelector) {
-        mPresenterSelector = presenterSelector;
-        notifyDataSetChanged();
-    }
-
-    /**
-     * Sets the {@link Wrapper}.
-     */
-    public void setWrapper(Wrapper wrapper) {
-        mWrapper = wrapper;
-    }
-
-    /**
-     * Returns the {@link Wrapper}.
-     */
-    public Wrapper getWrapper() {
-        return mWrapper;
-    }
-
-    void setFocusHighlight(FocusHighlightHandler listener) {
-        mFocusHighlight = listener;
-        if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight);
-    }
-
-    /**
-     * Clears the adapter.
-     */
-    public void clear() {
-        setAdapter(null);
-    }
-
-    /**
-     * Sets the presenter mapper array.
-     */
-    public void setPresenterMapper(ArrayList<Presenter> presenters) {
-        mPresenters = presenters;
-    }
-
-    /**
-     * Returns the presenter mapper array.
-     */
-    public ArrayList<Presenter> getPresenterMapper() {
-        return mPresenters;
-    }
-
-    @Override
-    public int getItemCount() {
-        return mAdapter != null ? mAdapter.size() : 0;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        PresenterSelector presenterSelector = mPresenterSelector != null
-                ? mPresenterSelector : mAdapter.getPresenterSelector();
-        Object item = mAdapter.get(position);
-        Presenter presenter = presenterSelector.getPresenter(item);
-        int type = mPresenters.indexOf(presenter);
-        if (type < 0) {
-            mPresenters.add(presenter);
-            type = mPresenters.indexOf(presenter);
-            if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
-            onAddPresenter(presenter, type);
-            if (mAdapterListener != null) {
-                mAdapterListener.onAddPresenter(presenter, type);
-            }
-        }
-        return type;
-    }
-
-    /**
-     * Called when presenter is added to Adapter.
-     */
-    protected void onAddPresenter(Presenter presenter, int type) {
-    }
-
-    /**
-     * Called when ViewHolder is created.
-     */
-    protected void onCreate(ViewHolder viewHolder) {
-    }
-
-    /**
-     * Called when ViewHolder has been bound to data.
-     */
-    protected void onBind(ViewHolder viewHolder) {
-    }
-
-    /**
-     * Called when ViewHolder has been unbound from data.
-     */
-    protected void onUnbind(ViewHolder viewHolder) {
-    }
-
-    /**
-     * Called when ViewHolder has been attached to window.
-     */
-    protected void onAttachedToWindow(ViewHolder viewHolder) {
-    }
-
-    /**
-     * Called when ViewHolder has been detached from window.
-     */
-    protected void onDetachedFromWindow(ViewHolder viewHolder) {
-    }
-
-    /**
-     * {@link View.OnFocusChangeListener} that assigned in
-     * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
-     * {@link View.OnFocusChangeListener} after that.
-     */
-    @Override
-    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
-        Presenter presenter = mPresenters.get(viewType);
-        Presenter.ViewHolder presenterVh;
-        View view;
-        if (mWrapper != null) {
-            view = mWrapper.createWrapper(parent);
-            presenterVh = presenter.onCreateViewHolder(parent);
-            mWrapper.wrap(view, presenterVh.view);
-        } else {
-            presenterVh = presenter.onCreateViewHolder(parent);
-            view = presenterVh.view;
-        }
-        ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
-        onCreate(viewHolder);
-        if (mAdapterListener != null) {
-            mAdapterListener.onCreate(viewHolder);
-        }
-        View presenterView = viewHolder.mHolder.view;
-        if (presenterView != null) {
-            viewHolder.mFocusChangeListener.mChainedListener =
-                    presenterView.getOnFocusChangeListener();
-            presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
-        }
-        if (mFocusHighlight != null) {
-            mFocusHighlight.onInitializeView(view);
-        }
-        return viewHolder;
-    }
-
-    /**
-     * Sets the AdapterListener.
-     */
-    public void setAdapterListener(AdapterListener listener) {
-        mAdapterListener = listener;
-    }
-
-    @Override
-    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
-        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
-        ViewHolder viewHolder = (ViewHolder) holder;
-        viewHolder.mItem = mAdapter.get(position);
-
-        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
-
-        onBind(viewHolder);
-        if (mAdapterListener != null) {
-            mAdapterListener.onBind(viewHolder);
-        }
-    }
-
-    @Override
-    public final  void onBindViewHolder(RecyclerView.ViewHolder holder, int position,
-            List payloads) {
-        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
-        ViewHolder viewHolder = (ViewHolder) holder;
-        viewHolder.mItem = mAdapter.get(position);
-
-        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem, payloads);
-
-        onBind(viewHolder);
-        if (mAdapterListener != null) {
-            mAdapterListener.onBind(viewHolder, payloads);
-        }
-    }
-
-    @Override
-    public final void onViewRecycled(RecyclerView.ViewHolder holder) {
-        ViewHolder viewHolder = (ViewHolder) holder;
-        viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
-        onUnbind(viewHolder);
-        if (mAdapterListener != null) {
-            mAdapterListener.onUnbind(viewHolder);
-        }
-        viewHolder.mItem = null;
-    }
-
-    @Override
-    public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
-        ViewHolder viewHolder = (ViewHolder) holder;
-        onAttachedToWindow(viewHolder);
-        if (mAdapterListener != null) {
-            mAdapterListener.onAttachedToWindow(viewHolder);
-        }
-        viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
-    }
-
-    @Override
-    public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
-        ViewHolder viewHolder = (ViewHolder) holder;
-        viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
-        onDetachedFromWindow(viewHolder);
-        if (mAdapterListener != null) {
-            mAdapterListener.onDetachedFromWindow(viewHolder);
-        }
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return mAdapter.getId(position);
-    }
-
-    @Override
-    public FacetProvider getFacetProvider(int type) {
-        return mPresenters.get(type);
-    }
-}
diff --git a/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java b/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
deleted file mode 100644
index ff152f7..0000000
--- a/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.view.View;
-
-/**
- * A wrapper class working with {@link ItemBridgeAdapter} to wrap item view in a
- * {@link ShadowOverlayContainer}.  The ShadowOverlayContainer is created from conditions
- * of {@link ShadowOverlayHelper}.
- */
-public class ItemBridgeAdapterShadowOverlayWrapper extends ItemBridgeAdapter.Wrapper {
-
-    private final ShadowOverlayHelper mHelper;
-
-    public ItemBridgeAdapterShadowOverlayWrapper(ShadowOverlayHelper helper) {
-        mHelper = helper;
-    }
-
-    @Override
-    public View createWrapper(View root) {
-        Context context = root.getContext();
-        ShadowOverlayContainer wrapper = mHelper.createShadowOverlayContainer(context);
-        return wrapper;
-    }
-    @Override
-    public void wrap(View wrapper, View wrapped) {
-        ((ShadowOverlayContainer) wrapper).wrap(wrapped);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ListRow.java b/android/support/v17/leanback/widget/ListRow.java
deleted file mode 100644
index 8f95c04..0000000
--- a/android/support/v17/leanback/widget/ListRow.java
+++ /dev/null
@@ -1,86 +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.v17.leanback.widget;
-
-/**
- * A {@link Row} composed of a optional {@link HeaderItem}, and an {@link ObjectAdapter}
- * describing the items in the list.
- */
-public class ListRow extends Row {
-    private final ObjectAdapter mAdapter;
-    private CharSequence mContentDescription;
-
-    /**
-     * Returns the {@link ObjectAdapter} that represents a list of objects.
-     */
-    public final ObjectAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    public ListRow(HeaderItem header, ObjectAdapter adapter) {
-        super(header);
-        mAdapter = adapter;
-        verify();
-    }
-
-    public ListRow(long id, HeaderItem header, ObjectAdapter adapter) {
-        super(id, header);
-        mAdapter = adapter;
-        verify();
-    }
-
-    public ListRow(ObjectAdapter adapter) {
-        super();
-        mAdapter = adapter;
-        verify();
-    }
-
-    private void verify() {
-        if (mAdapter == null) {
-            throw new IllegalArgumentException("ObjectAdapter cannot be null");
-        }
-    }
-
-    /**
-     * Returns content description for the ListRow.  By default it returns
-     * {@link HeaderItem#getContentDescription()} or {@link HeaderItem#getName()},
-     * unless {@link #setContentDescription(CharSequence)} was explicitly called.
-     *
-     * @return Content description for the ListRow.
-     */
-    public CharSequence getContentDescription() {
-        if (mContentDescription != null) {
-            return mContentDescription;
-        }
-        final HeaderItem headerItem = getHeaderItem();
-        if (headerItem != null) {
-            CharSequence contentDescription = headerItem.getContentDescription();
-            if (contentDescription != null) {
-                return contentDescription;
-            }
-            return headerItem.getName();
-        }
-        return null;
-    }
-
-    /**
-     * Explicitly set content description for the ListRow, {@link #getContentDescription()} will
-     * ignore values from HeaderItem.
-     *
-     * @param contentDescription Content description sets on the ListRow.
-     */
-    public void setContentDescription(CharSequence contentDescription) {
-        mContentDescription = contentDescription;
-    }
-}
diff --git a/android/support/v17/leanback/widget/ListRowHoverCardView.java b/android/support/v17/leanback/widget/ListRowHoverCardView.java
deleted file mode 100644
index bc4ecc1..0000000
--- a/android/support/v17/leanback/widget/ListRowHoverCardView.java
+++ /dev/null
@@ -1,86 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * ListRowHoverCardView contains a title and description.
- */
-public final class ListRowHoverCardView extends LinearLayout {
-
-    private final TextView mTitleView;
-    private final TextView mDescriptionView;
-
-    public ListRowHoverCardView(Context context) {
-       this(context, null);
-    }
-
-    public ListRowHoverCardView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ListRowHoverCardView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        LayoutInflater inflater = LayoutInflater.from(context);
-        inflater.inflate(R.layout.lb_list_row_hovercard, this);
-        mTitleView = findViewById(R.id.title);
-        mDescriptionView = findViewById(R.id.description);
-    }
-
-    /**
-     * Returns the title text.
-     */
-    public final CharSequence getTitle() {
-        return mTitleView.getText();
-    }
-
-    /**
-     * Sets the title text.
-     */
-    public final void setTitle(CharSequence text) {
-        if (!TextUtils.isEmpty(text)) {
-            mTitleView.setText(text);
-            mTitleView.setVisibility(View.VISIBLE);
-        } else {
-            mTitleView.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Returns the description text.
-     */
-    public final CharSequence getDescription() {
-        return mDescriptionView.getText();
-    }
-
-    /**
-     * Sets the description text.
-     */
-    public final void setDescription(CharSequence text) {
-        if (!TextUtils.isEmpty(text)) {
-            mDescriptionView.setText(text);
-            mDescriptionView.setVisibility(View.VISIBLE);
-        } else {
-            mDescriptionView.setVisibility(View.GONE);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/ListRowPresenter.java b/android/support/v17/leanback/widget/ListRowPresenter.java
deleted file mode 100644
index fde874a..0000000
--- a/android/support/v17/leanback/widget/ListRowPresenter.java
+++ /dev/null
@@ -1,855 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.system.Settings;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v7.widget.RecyclerView;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-
-/**
- * ListRowPresenter renders {@link ListRow} using a
- * {@link HorizontalGridView} hosted in a {@link ListRowView}.
- *
- * <h3>Hover card</h3>
- * Optionally, {@link #setHoverCardPresenterSelector(PresenterSelector)} can be used to
- * display a view for the currently focused list item below the rendered
- * list. This view is known as a hover card.
- *
- * <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
- * {@link #setShadowEnabled(boolean)} to disable shadows.  A subclass may override and return
- * false in {@link #isUsingDefaultShadow()} and replace with its own shadow implementation.
- */
-public class ListRowPresenter extends RowPresenter {
-
-    private static final String TAG = "ListRowPresenter";
-    private static final boolean DEBUG = false;
-
-    private static final int DEFAULT_RECYCLED_POOL_SIZE = 24;
-
-    /**
-     * ViewHolder for the ListRowPresenter.
-     */
-    public static class ViewHolder extends RowPresenter.ViewHolder {
-        final ListRowPresenter mListRowPresenter;
-        final HorizontalGridView mGridView;
-        ItemBridgeAdapter mItemBridgeAdapter;
-        final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
-        final int mPaddingTop;
-        final int mPaddingBottom;
-        final int mPaddingLeft;
-        final int mPaddingRight;
-
-        public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) {
-            super(rootView);
-            mGridView = gridView;
-            mListRowPresenter = p;
-            mPaddingTop = mGridView.getPaddingTop();
-            mPaddingBottom = mGridView.getPaddingBottom();
-            mPaddingLeft = mGridView.getPaddingLeft();
-            mPaddingRight = mGridView.getPaddingRight();
-        }
-
-        /**
-         * Gets ListRowPresenter that creates this ViewHolder.
-         * @return ListRowPresenter that creates this ViewHolder.
-         */
-        public final ListRowPresenter getListRowPresenter() {
-            return mListRowPresenter;
-        }
-
-        /**
-         * Gets HorizontalGridView that shows a list of items.
-         * @return HorizontalGridView that shows a list of items.
-         */
-        public final HorizontalGridView getGridView() {
-            return mGridView;
-        }
-
-        /**
-         * Gets ItemBridgeAdapter that creates the list of items.
-         * @return ItemBridgeAdapter that creates the list of items.
-         */
-        public final ItemBridgeAdapter getBridgeAdapter() {
-            return mItemBridgeAdapter;
-        }
-
-        /**
-         * Gets selected item position in adapter.
-         * @return Selected item position in adapter.
-         */
-        public int getSelectedPosition() {
-            return mGridView.getSelectedPosition();
-        }
-
-        /**
-         * Gets ViewHolder at a position in adapter.  Returns null if the item does not exist
-         * or the item is not bound to a view.
-         * @param position Position of the item in adapter.
-         * @return ViewHolder bounds to the item.
-         */
-        public Presenter.ViewHolder getItemViewHolder(int position) {
-            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView
-                    .findViewHolderForAdapterPosition(position);
-            if (ibvh == null) {
-                return null;
-            }
-            return ibvh.getViewHolder();
-        }
-
-        @Override
-        public Presenter.ViewHolder getSelectedItemViewHolder() {
-            return getItemViewHolder(getSelectedPosition());
-        }
-
-        @Override
-        public Object getSelectedItem() {
-            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) mGridView
-                    .findViewHolderForAdapterPosition(getSelectedPosition());
-            if (ibvh == null) {
-                return null;
-            }
-            return ibvh.getItem();
-        }
-    }
-
-    /**
-     * A task on the ListRowPresenter.ViewHolder that can select an item by position in the
-     * HorizontalGridView and perform an optional item task on it.
-     */
-    public static class SelectItemViewHolderTask extends Presenter.ViewHolderTask {
-
-        private int mItemPosition;
-        private boolean mSmoothScroll = true;
-        Presenter.ViewHolderTask mItemTask;
-
-        public SelectItemViewHolderTask(int itemPosition) {
-            setItemPosition(itemPosition);
-        }
-
-        /**
-         * Sets the adapter position of item to select.
-         * @param itemPosition Position of the item in adapter.
-         */
-        public void setItemPosition(int itemPosition) {
-            mItemPosition = itemPosition;
-        }
-
-        /**
-         * Returns the adapter position of item to select.
-         * @return The adapter position of item to select.
-         */
-        public int getItemPosition() {
-            return mItemPosition;
-        }
-
-        /**
-         * Sets smooth scrolling to the item or jump to the item without scrolling.  By default it is
-         * true.
-         * @param smoothScroll True for smooth scrolling to the item, false otherwise.
-         */
-        public void setSmoothScroll(boolean smoothScroll) {
-            mSmoothScroll = smoothScroll;
-        }
-
-        /**
-         * Returns true if smooth scrolling to the item false otherwise.  By default it is true.
-         * @return True for smooth scrolling to the item, false otherwise.
-         */
-        public boolean isSmoothScroll() {
-            return mSmoothScroll;
-        }
-
-        /**
-         * Returns optional task to run when the item is selected, null for no task.
-         * @return Optional task to run when the item is selected, null for no task.
-         */
-        public Presenter.ViewHolderTask getItemTask() {
-            return mItemTask;
-        }
-
-        /**
-         * Sets task to run when the item is selected, null for no task.
-         * @param itemTask Optional task to run when the item is selected, null for no task.
-         */
-        public void setItemTask(Presenter.ViewHolderTask itemTask) {
-            mItemTask = itemTask;
-        }
-
-        @Override
-        public void run(Presenter.ViewHolder holder) {
-            if (holder instanceof ListRowPresenter.ViewHolder) {
-                HorizontalGridView gridView = ((ListRowPresenter.ViewHolder) holder).getGridView();
-                android.support.v17.leanback.widget.ViewHolderTask task = null;
-                if (mItemTask != null) {
-                    task = new android.support.v17.leanback.widget.ViewHolderTask() {
-                        final Presenter.ViewHolderTask itemTask = mItemTask;
-                        @Override
-                        public void run(RecyclerView.ViewHolder rvh) {
-                            ItemBridgeAdapter.ViewHolder ibvh = (ItemBridgeAdapter.ViewHolder) rvh;
-                            itemTask.run(ibvh.getViewHolder());
-                        }
-                    };
-                }
-                if (isSmoothScroll()) {
-                    gridView.setSelectedPositionSmooth(mItemPosition, task);
-                } else {
-                    gridView.setSelectedPosition(mItemPosition, task);
-                }
-            }
-        }
-    }
-
-    class ListRowPresenterItemBridgeAdapter extends ItemBridgeAdapter {
-        ListRowPresenter.ViewHolder mRowViewHolder;
-
-        ListRowPresenterItemBridgeAdapter(ListRowPresenter.ViewHolder rowViewHolder) {
-            mRowViewHolder = rowViewHolder;
-        }
-
-        @Override
-        protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
-            if (viewHolder.itemView instanceof ViewGroup) {
-                TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, true);
-            }
-            if (mShadowOverlayHelper != null) {
-                mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
-            }
-        }
-
-        @Override
-        public void onBind(final ItemBridgeAdapter.ViewHolder viewHolder) {
-            // Only when having an OnItemClickListener, we will attach the OnClickListener.
-            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
-                viewHolder.mHolder.view.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
-                                mRowViewHolder.mGridView.getChildViewHolder(viewHolder.itemView);
-                        if (mRowViewHolder.getOnItemViewClickedListener() != null) {
-                            mRowViewHolder.getOnItemViewClickedListener().onItemClicked(viewHolder.mHolder,
-                                    ibh.mItem, mRowViewHolder, (ListRow) mRowViewHolder.mRow);
-                        }
-                    }
-                });
-            }
-        }
-
-        @Override
-        public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
-            if (mRowViewHolder.getOnItemViewClickedListener() != null) {
-                viewHolder.mHolder.view.setOnClickListener(null);
-            }
-        }
-
-        @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            applySelectLevelToChild(mRowViewHolder, viewHolder.itemView);
-            mRowViewHolder.syncActivatedStatus(viewHolder.itemView);
-        }
-
-        @Override
-        public void onAddPresenter(Presenter presenter, int type) {
-            mRowViewHolder.getGridView().getRecycledViewPool().setMaxRecycledViews(
-                    type, getRecycledPoolSize(presenter));
-        }
-    }
-
-    private int mNumRows = 1;
-    private int mRowHeight;
-    private int mExpandedRowHeight;
-    private PresenterSelector mHoverCardPresenterSelector;
-    private int mFocusZoomFactor;
-    private boolean mUseFocusDimmer;
-    private boolean mShadowEnabled = true;
-    private int mBrowseRowsFadingEdgeLength = -1;
-    private boolean mRoundedCornersEnabled = true;
-    private boolean mKeepChildForeground = true;
-    private HashMap<Presenter, Integer> mRecycledPoolSize = new HashMap<Presenter, Integer>();
-    ShadowOverlayHelper mShadowOverlayHelper;
-    private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper;
-
-    private static int sSelectedRowTopPadding;
-    private static int sExpandedSelectedRowTopPadding;
-    private static int sExpandedRowNoHovercardBottomPadding;
-
-    /**
-     * Constructs a ListRowPresenter with defaults.
-     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
-     * disabled dimming on focus.
-     */
-    public ListRowPresenter() {
-        this(FocusHighlight.ZOOM_FACTOR_MEDIUM);
-    }
-
-    /**
-     * Constructs a ListRowPresenter with the given parameters.
-     *
-     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
-     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
-     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
-     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
-     * Dimming on focus defaults to disabled.
-     */
-    public ListRowPresenter(int focusZoomFactor) {
-        this(focusZoomFactor, false);
-    }
-
-    /**
-     * Constructs a ListRowPresenter with the given parameters.
-     *
-     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
-     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
-     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
-     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
-     * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
-     */
-    public ListRowPresenter(int focusZoomFactor, boolean useFocusDimmer) {
-        if (!FocusHighlightHelper.isValidZoomIndex(focusZoomFactor)) {
-            throw new IllegalArgumentException("Unhandled zoom factor");
-        }
-        mFocusZoomFactor = focusZoomFactor;
-        mUseFocusDimmer = useFocusDimmer;
-    }
-
-    /**
-     * Sets the row height for rows created by this Presenter. Rows
-     * created before calling this method will not be updated.
-     *
-     * @param rowHeight Row height in pixels, or WRAP_CONTENT, or 0
-     * to use the default height.
-     */
-    public void setRowHeight(int rowHeight) {
-        mRowHeight = rowHeight;
-    }
-
-    /**
-     * Returns the row height for list rows created by this Presenter.
-     */
-    public int getRowHeight() {
-        return mRowHeight;
-    }
-
-    /**
-     * Sets the expanded row height for rows created by this Presenter.
-     * If not set, expanded rows have the same height as unexpanded
-     * rows.
-     *
-     * @param rowHeight The row height in to use when the row is expanded,
-     *        in pixels, or WRAP_CONTENT, or 0 to use the default.
-     */
-    public void setExpandedRowHeight(int rowHeight) {
-        mExpandedRowHeight = rowHeight;
-    }
-
-    /**
-     * Returns the expanded row height for rows created by this Presenter.
-     */
-    public int getExpandedRowHeight() {
-        return mExpandedRowHeight != 0 ? mExpandedRowHeight : mRowHeight;
-    }
-
-    /**
-     * Returns the zoom factor used for focus highlighting.
-     */
-    public final int getFocusZoomFactor() {
-        return mFocusZoomFactor;
-    }
-
-    /**
-     * Returns the zoom factor used for focus highlighting.
-     * @deprecated use {@link #getFocusZoomFactor} instead.
-     */
-    @Deprecated
-    public final int getZoomFactor() {
-        return mFocusZoomFactor;
-    }
-
-    /**
-     * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
-     */
-    public final boolean isFocusDimmerUsed() {
-        return mUseFocusDimmer;
-    }
-
-    /**
-     * Sets the numbers of rows for rendering the list of items. By default, it is
-     * set to 1.
-     */
-    public void setNumRows(int numRows) {
-        this.mNumRows = numRows;
-    }
-
-    @Override
-    protected void initializeRowViewHolder(RowPresenter.ViewHolder holder) {
-        super.initializeRowViewHolder(holder);
-        final ViewHolder rowViewHolder = (ViewHolder) holder;
-        Context context = holder.view.getContext();
-        if (mShadowOverlayHelper == null) {
-            mShadowOverlayHelper = new ShadowOverlayHelper.Builder()
-                    .needsOverlay(needsDefaultListSelectEffect())
-                    .needsShadow(needsDefaultShadow())
-                    .needsRoundedCorner(isUsingOutlineClipping(context)
-                            && areChildRoundedCornersEnabled())
-                    .preferZOrder(isUsingZOrder(context))
-                    .keepForegroundDrawable(mKeepChildForeground)
-                    .options(createShadowOverlayOptions())
-                    .build(context);
-            if (mShadowOverlayHelper.needsWrapper()) {
-                mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper(
-                        mShadowOverlayHelper);
-            }
-        }
-        rowViewHolder.mItemBridgeAdapter = new ListRowPresenterItemBridgeAdapter(rowViewHolder);
-        // set wrapper if needed
-        rowViewHolder.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper);
-        mShadowOverlayHelper.prepareParentForShadow(rowViewHolder.mGridView);
-
-        FocusHighlightHelper.setupBrowseItemFocusHighlight(rowViewHolder.mItemBridgeAdapter,
-                mFocusZoomFactor, mUseFocusDimmer);
-        rowViewHolder.mGridView.setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
-                != ShadowOverlayHelper.SHADOW_DYNAMIC);
-        rowViewHolder.mGridView.setOnChildSelectedListener(
-                new OnChildSelectedListener() {
-            @Override
-            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
-                selectChildView(rowViewHolder, view, true);
-            }
-        });
-        rowViewHolder.mGridView.setOnUnhandledKeyListener(
-                new BaseGridView.OnUnhandledKeyListener() {
-                @Override
-                public boolean onUnhandledKey(KeyEvent event) {
-                    return rowViewHolder.getOnKeyListener() != null
-                            && rowViewHolder.getOnKeyListener().onKey(
-                                    rowViewHolder.view, event.getKeyCode(), event);
-                }
-            });
-        rowViewHolder.mGridView.setNumRows(mNumRows);
-    }
-
-    final boolean needsDefaultListSelectEffect() {
-        return isUsingDefaultListSelectEffect() && getSelectEffectEnabled();
-    }
-
-    /**
-     * Sets the recycled pool size for the given presenter.
-     */
-    public void setRecycledPoolSize(Presenter presenter, int size) {
-        mRecycledPoolSize.put(presenter, size);
-    }
-
-    /**
-     * Returns the recycled pool size for the given presenter.
-     */
-    public int getRecycledPoolSize(Presenter presenter) {
-        return mRecycledPoolSize.containsKey(presenter) ? mRecycledPoolSize.get(presenter) :
-                DEFAULT_RECYCLED_POOL_SIZE;
-    }
-
-    /**
-     * Sets the {@link PresenterSelector} used for showing a select object in a hover card.
-     */
-    public final void setHoverCardPresenterSelector(PresenterSelector selector) {
-        mHoverCardPresenterSelector = selector;
-    }
-
-    /**
-     * Returns the {@link PresenterSelector} used for showing a select object in a hover card.
-     */
-    public final PresenterSelector getHoverCardPresenterSelector() {
-        return mHoverCardPresenterSelector;
-    }
-
-    /*
-     * Perform operations when a child of horizontal grid view is selected.
-     */
-    void selectChildView(ViewHolder rowViewHolder, View view, boolean fireEvent) {
-        if (view != null) {
-            if (rowViewHolder.mSelected) {
-                ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
-                        rowViewHolder.mGridView.getChildViewHolder(view);
-
-                if (mHoverCardPresenterSelector != null) {
-                    rowViewHolder.mHoverCardViewSwitcher.select(
-                            rowViewHolder.mGridView, view, ibh.mItem);
-                }
-                if (fireEvent && rowViewHolder.getOnItemViewSelectedListener() != null) {
-                    rowViewHolder.getOnItemViewSelectedListener().onItemSelected(
-                            ibh.mHolder, ibh.mItem, rowViewHolder, rowViewHolder.mRow);
-                }
-            }
-        } else {
-            if (mHoverCardPresenterSelector != null) {
-                rowViewHolder.mHoverCardViewSwitcher.unselect();
-            }
-            if (fireEvent && rowViewHolder.getOnItemViewSelectedListener() != null) {
-                rowViewHolder.getOnItemViewSelectedListener().onItemSelected(
-                        null, null, rowViewHolder, rowViewHolder.mRow);
-            }
-        }
-    }
-
-    private static void initStatics(Context context) {
-        if (sSelectedRowTopPadding == 0) {
-            sSelectedRowTopPadding = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_browse_selected_row_top_padding);
-            sExpandedSelectedRowTopPadding = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_browse_expanded_selected_row_top_padding);
-            sExpandedRowNoHovercardBottomPadding = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_browse_expanded_row_no_hovercard_bottom_padding);
-        }
-    }
-
-    private int getSpaceUnderBaseline(ListRowPresenter.ViewHolder vh) {
-        RowHeaderPresenter.ViewHolder headerViewHolder = vh.getHeaderViewHolder();
-        if (headerViewHolder != null) {
-            if (getHeaderPresenter() != null) {
-                return getHeaderPresenter().getSpaceUnderBaseline(headerViewHolder);
-            }
-            return headerViewHolder.view.getPaddingBottom();
-        }
-        return 0;
-    }
-
-    private void setVerticalPadding(ListRowPresenter.ViewHolder vh) {
-        int paddingTop, paddingBottom;
-        // Note: sufficient bottom padding needed for card shadows.
-        if (vh.isExpanded()) {
-            int headerSpaceUnderBaseline = getSpaceUnderBaseline(vh);
-            if (DEBUG) Log.v(TAG, "headerSpaceUnderBaseline " + headerSpaceUnderBaseline);
-            paddingTop = (vh.isSelected() ? sExpandedSelectedRowTopPadding : vh.mPaddingTop)
-                    - headerSpaceUnderBaseline;
-            paddingBottom = mHoverCardPresenterSelector == null
-                    ? sExpandedRowNoHovercardBottomPadding : vh.mPaddingBottom;
-        } else if (vh.isSelected()) {
-            paddingTop = sSelectedRowTopPadding - vh.mPaddingBottom;
-            paddingBottom = sSelectedRowTopPadding;
-        } else {
-            paddingTop = 0;
-            paddingBottom = vh.mPaddingBottom;
-        }
-        vh.getGridView().setPadding(vh.mPaddingLeft, paddingTop, vh.mPaddingRight,
-                paddingBottom);
-    }
-
-    @Override
-    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
-        initStatics(parent.getContext());
-        ListRowView rowView = new ListRowView(parent.getContext());
-        setupFadingEffect(rowView);
-        if (mRowHeight != 0) {
-            rowView.getGridView().setRowHeight(mRowHeight);
-        }
-        return new ViewHolder(rowView, rowView.getGridView(), this);
-    }
-
-    /**
-     * Dispatch item selected event using current selected item in the {@link HorizontalGridView}.
-     * The method should only be called from onRowViewSelected().
-     */
-    @Override
-    protected void dispatchItemSelectedListener(RowPresenter.ViewHolder holder, boolean selected) {
-        ViewHolder vh = (ViewHolder)holder;
-        ItemBridgeAdapter.ViewHolder itemViewHolder = (ItemBridgeAdapter.ViewHolder)
-                vh.mGridView.findViewHolderForPosition(vh.mGridView.getSelectedPosition());
-        if (itemViewHolder == null) {
-            super.dispatchItemSelectedListener(holder, selected);
-            return;
-        }
-
-        if (selected) {
-            if (holder.getOnItemViewSelectedListener() != null) {
-                holder.getOnItemViewSelectedListener().onItemSelected(
-                        itemViewHolder.getViewHolder(), itemViewHolder.mItem, vh, vh.getRow());
-            }
-        }
-    }
-
-    @Override
-    protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) {
-        super.onRowViewSelected(holder, selected);
-        ViewHolder vh = (ViewHolder) holder;
-        setVerticalPadding(vh);
-        updateFooterViewSwitcher(vh);
-    }
-
-    /*
-     * Show or hide hover card when row selection or expanded state is changed.
-     */
-    private void updateFooterViewSwitcher(ViewHolder vh) {
-        if (vh.mExpanded && vh.mSelected) {
-            if (mHoverCardPresenterSelector != null) {
-                vh.mHoverCardViewSwitcher.init((ViewGroup) vh.view,
-                        mHoverCardPresenterSelector);
-            }
-            ItemBridgeAdapter.ViewHolder ibh = (ItemBridgeAdapter.ViewHolder)
-                    vh.mGridView.findViewHolderForPosition(
-                            vh.mGridView.getSelectedPosition());
-            selectChildView(vh, ibh == null ? null : ibh.itemView, false);
-        } else {
-            if (mHoverCardPresenterSelector != null) {
-                vh.mHoverCardViewSwitcher.unselect();
-            }
-        }
-    }
-
-    private void setupFadingEffect(ListRowView rowView) {
-        // content is completely faded at 1/2 padding of left, fading length is 1/2 of padding.
-        HorizontalGridView gridView = rowView.getGridView();
-        if (mBrowseRowsFadingEdgeLength < 0) {
-            TypedArray ta = gridView.getContext()
-                    .obtainStyledAttributes(R.styleable.LeanbackTheme);
-            mBrowseRowsFadingEdgeLength = (int) ta.getDimension(
-                    R.styleable.LeanbackTheme_browseRowsFadingEdgeLength, 0);
-            ta.recycle();
-        }
-        gridView.setFadingLeftEdgeLength(mBrowseRowsFadingEdgeLength);
-    }
-
-    @Override
-    protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) {
-        super.onRowViewExpanded(holder, expanded);
-        ViewHolder vh = (ViewHolder) holder;
-        if (getRowHeight() != getExpandedRowHeight()) {
-            int newHeight = expanded ? getExpandedRowHeight() : getRowHeight();
-            vh.getGridView().setRowHeight(newHeight);
-        }
-        setVerticalPadding(vh);
-        updateFooterViewSwitcher(vh);
-    }
-
-    @Override
-    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
-        super.onBindRowViewHolder(holder, item);
-        ViewHolder vh = (ViewHolder) holder;
-        ListRow rowItem = (ListRow) item;
-        vh.mItemBridgeAdapter.setAdapter(rowItem.getAdapter());
-        vh.mGridView.setAdapter(vh.mItemBridgeAdapter);
-        vh.mGridView.setContentDescription(rowItem.getContentDescription());
-    }
-
-    @Override
-    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
-        ViewHolder vh = (ViewHolder) holder;
-        vh.mGridView.setAdapter(null);
-        vh.mItemBridgeAdapter.clear();
-        super.onUnbindRowViewHolder(holder);
-    }
-
-    /**
-     * ListRowPresenter overrides the default select effect of {@link RowPresenter}
-     * and return false.
-     */
-    @Override
-    public final boolean isUsingDefaultSelectEffect() {
-        return false;
-    }
-
-    /**
-     * 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 and implement {@link #applySelectLevelToChild(ViewHolder, View)}.
-     * @see #applySelectLevelToChild(ViewHolder, View)
-     * @see #onSelectLevelChanged(RowPresenter.ViewHolder)
-     */
-    public boolean isUsingDefaultListSelectEffect() {
-        return true;
-    }
-
-    /**
-     * Default implementation returns true if SDK version >= 21, shadow (either static or z-order
-     * based) will be applied to each individual child of {@link HorizontalGridView}.
-     * Subclass may return false to disable default implementation of shadow and provide its own.
-     */
-    public boolean isUsingDefaultShadow() {
-        return ShadowOverlayHelper.supportsShadow();
-    }
-
-    /**
-     * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
-     * on each child of horizontal list.   If subclass returns false in isUsingDefaultShadow()
-     * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
-     */
-    public boolean isUsingZOrder(Context context) {
-        return !Settings.getInstance(context).preferStaticShadows();
-    }
-
-    /**
-     * Returns true if leanback view outline is enabled on the system or false otherwise. When
-     * false, rounded corner will not be enabled even {@link #enableChildRoundedCorners(boolean)}
-     * is called with true.
-     *
-     * @param context Context to retrieve system settings.
-     * @return True if leanback view outline is enabled on the system or false otherwise.
-     */
-    public boolean isUsingOutlineClipping(Context context) {
-        return !Settings.getInstance(context).isOutlineClippingDisabled();
-    }
-
-    /**
-     * Enables or disables child shadow.
-     * This is not only for enable/disable default shadow implementation but also subclass must
-     * respect this flag.
-     */
-    public final void setShadowEnabled(boolean enabled) {
-        mShadowEnabled = enabled;
-    }
-
-    /**
-     * Returns true if child shadow is enabled.
-     * This is not only for enable/disable default shadow implementation but also subclass must
-     * respect this flag.
-     */
-    public final boolean getShadowEnabled() {
-        return mShadowEnabled;
-    }
-
-    /**
-     * Enables or disabled rounded corners on children of this row.
-     * Supported on Android SDK >= L.
-     */
-    public final void enableChildRoundedCorners(boolean enable) {
-        mRoundedCornersEnabled = enable;
-    }
-
-    /**
-     * Returns true if rounded corners are enabled for children of this row.
-     */
-    public final boolean areChildRoundedCornersEnabled() {
-        return mRoundedCornersEnabled;
-    }
-
-    final boolean needsDefaultShadow() {
-        return isUsingDefaultShadow() && getShadowEnabled();
-    }
-
-    /**
-     * When ListRowPresenter applies overlay color on the child,  it may change child's foreground
-     * Drawable.  If application uses child's foreground for other purposes such as ripple effect,
-     * it needs tell ListRowPresenter to keep the child's foreground.  The default value is true.
-     *
-     * @param keep true if keep foreground of child of this row, false ListRowPresenter might change
-     *             the foreground of the child.
-     */
-    public final void setKeepChildForeground(boolean keep) {
-        mKeepChildForeground = keep;
-    }
-
-    /**
-     * Returns true if keeps foreground of child of this row, false otherwise.  When
-     * ListRowPresenter applies overlay color on the child,  it may change child's foreground
-     * Drawable.  If application uses child's foreground for other purposes such as ripple effect,
-     * it needs tell ListRowPresenter to keep the child's foreground.  The default value is true.
-     *
-     * @return true if keeps foreground of child of this row, false otherwise.
-     */
-    public final boolean isKeepChildForeground() {
-        return mKeepChildForeground;
-    }
-
-    /**
-     * Create ShadowOverlayHelper Options.  Subclass may override.
-     * e.g.
-     * <code>
-     * return new ShadowOverlayHelper.Options().roundedCornerRadius(10);
-     * </code>
-     *
-     * @return The options to be used for shadow, overlay and rounded corner.
-     */
-    protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
-        return ShadowOverlayHelper.Options.DEFAULT;
-    }
-
-    /**
-     * Applies select level to header and draws a default color dim over each child
-     * of {@link HorizontalGridView}.
-     * <p>
-     * 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()) {
-            int dimmedColor = rowViewHolder.mColorDimmer.getPaint().getColor();
-            mShadowOverlayHelper.setOverlayColor(childView, dimmedColor);
-        }
-    }
-
-    @Override
-    public void freeze(RowPresenter.ViewHolder holder, boolean freeze) {
-        ViewHolder vh = (ViewHolder) holder;
-        vh.mGridView.setScrollEnabled(!freeze);
-        vh.mGridView.setAnimateChildLayout(!freeze);
-    }
-
-    @Override
-    public void setEntranceTransitionState(RowPresenter.ViewHolder holder,
-            boolean afterEntrance) {
-        super.setEntranceTransitionState(holder, afterEntrance);
-        ((ViewHolder) holder).mGridView.setChildrenVisibility(
-                afterEntrance? View.VISIBLE : View.INVISIBLE);
-    }
-}
diff --git a/android/support/v17/leanback/widget/ListRowView.java b/android/support/v17/leanback/widget/ListRowView.java
deleted file mode 100644
index 528e9aa..0000000
--- a/android/support/v17/leanback/widget/ListRowView.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.v17.leanback.widget;
-
-import android.support.v17.leanback.R;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-/**
- * ListRowView is a {@link android.view.ViewGroup} which always contains a
- * {@link HorizontalGridView}, and may optionally include a hover card.
- */
-public final class ListRowView extends LinearLayout {
-
-    private HorizontalGridView mGridView;
-
-    public ListRowView(Context context) {
-        this(context, null);
-    }
-
-    public ListRowView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ListRowView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        inflater.inflate(R.layout.lb_list_row, this);
-
-        mGridView = (HorizontalGridView) findViewById(R.id.row_content);
-        // since we use WRAP_CONTENT for height in lb_list_row, we need set fixed size to false
-        mGridView.setHasFixedSize(false);
-
-        // Uncomment this to experiment with page-based scrolling.
-        // mGridView.setFocusScrollStrategy(HorizontalGridView.FOCUS_SCROLL_PAGE);
-
-        setOrientation(LinearLayout.VERTICAL);
-        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-    }
-
-    /**
-     * Returns the HorizontalGridView.
-     */
-    public HorizontalGridView getGridView() {
-        return mGridView;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/MediaItemActionPresenter.java b/android/support/v17/leanback/widget/MediaItemActionPresenter.java
deleted file mode 100644
index 1a98059..0000000
--- a/android/support/v17/leanback/widget/MediaItemActionPresenter.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.support.v17.leanback.R;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-/**
- * The presenter displaying a custom action in {@link AbstractMediaItemPresenter}.
- * This is the default presenter for actions in media rows if no action presenter is provided by the
- * user.
- *
- * Binds to items of type {@link MultiActionsProvider.MultiAction}.
- */
-class MediaItemActionPresenter extends Presenter {
-
-    MediaItemActionPresenter() {
-    }
-
-    static class ViewHolder extends Presenter.ViewHolder {
-        final ImageView mIcon;
-
-        public ViewHolder(View view) {
-            super(view);
-            mIcon = (ImageView) view.findViewById(R.id.actionIcon);
-        }
-
-        public ImageView getIcon() {
-            return mIcon;
-        }
-    }
-
-    @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        Context context = parent.getContext();
-        View actionView = LayoutInflater.from(context)
-                .inflate(R.layout.lb_row_media_item_action, parent, false);
-        return new ViewHolder(actionView);
-    }
-
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-        ViewHolder actionViewHolder = (ViewHolder) viewHolder;
-        MultiActionsProvider.MultiAction action = (MultiActionsProvider.MultiAction) item;
-        actionViewHolder.getIcon().setImageDrawable(action.getCurrentDrawable());
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-    }
-}
diff --git a/android/support/v17/leanback/widget/MediaNowPlayingView.java b/android/support/v17/leanback/widget/MediaNowPlayingView.java
deleted file mode 100644
index 95f751b..0000000
--- a/android/support/v17/leanback/widget/MediaNowPlayingView.java
+++ /dev/null
@@ -1,146 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.LinearInterpolator;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-/**
- * The view displaying 3 animated peak meters next to each other when a media item is playing.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class MediaNowPlayingView extends LinearLayout{
-
-    private final ImageView mImage1;
-    private final ImageView mImage2;
-    private final ImageView mImage3;
-    private final ObjectAnimator mObjectAnimator1;
-    private final ObjectAnimator mObjectAnimator2;
-    private final ObjectAnimator mObjectAnimator3;
-    protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
-
-    public MediaNowPlayingView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        LayoutInflater.from(context).inflate(R.layout.lb_playback_now_playing_bars, this, true);
-        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());
-        mImage3.setPivotY(mImage3.getDrawable().getIntrinsicHeight());
-
-        setDropScale(mImage1);
-        setDropScale(mImage2);
-        setDropScale(mImage3);
-
-        mObjectAnimator1 = ObjectAnimator.ofFloat(mImage1, "scaleY", 5f / 12f, 3f / 12f, 5f / 12f,
-                7f / 12f, 9f / 12f, 10f / 12f, 11f / 12f, 12f / 12f, 11f / 12f, 12f / 12f,
-                10f / 12f, 8f / 12f, 6f / 12f, 4f / 12f, 2f / 12f, 4f / 12f, 6f / 12f, 7f / 12f,
-                9f / 12f, 11f / 12f, 9f / 12f, 7f / 12f, 5f / 12f, 3f / 12f, 5f / 12f, 8f / 12f,
-                5f / 12f, 3f / 12f, 4f / 12f, 5f / 12f);
-        mObjectAnimator1.setRepeatCount(ValueAnimator.INFINITE);
-        mObjectAnimator1.setDuration(2320);
-        mObjectAnimator1.setInterpolator(mLinearInterpolator);
-
-        mObjectAnimator2 = ObjectAnimator.ofFloat(mImage2, "scaleY", 12f / 12f, 11f / 12f,
-                10f / 12f, 11f / 12f, 12f / 12f, 11f / 12f, 9f / 12f, 7f / 12f, 9f / 12f, 11f / 12f,
-                12f / 12f, 10f / 12f, 8f / 12f, 10f / 12f, 12f / 12f, 11f / 12f, 9f / 12f, 5f / 12f,
-                3f / 12f, 5f / 12f, 8f / 12f, 10f / 12f, 12f / 12f, 10f / 12f, 9f / 12f, 8f / 12f,
-                12f / 12f);
-        mObjectAnimator2.setRepeatCount(ValueAnimator.INFINITE);
-        mObjectAnimator2.setDuration(2080);
-        mObjectAnimator2.setInterpolator(mLinearInterpolator);
-
-        mObjectAnimator3 = ObjectAnimator.ofFloat(mImage3, "scaleY", 8f / 12f, 9f / 12f, 10f / 12f,
-                12f / 12f, 11f / 12f, 9f / 12f, 7f / 12f, 5f / 12f, 7f / 12f, 8f / 12f, 9f / 12f,
-                12f / 12f, 11f / 12f, 12f / 12f, 9f / 12f, 7f / 12f, 9f / 12f, 11f / 12f, 12f / 12f,
-                10f / 12f, 8f / 12f, 9f / 12f, 7f / 12f, 5f / 12f, 3f / 12f, 8f / 12f);
-        mObjectAnimator3.setRepeatCount(ValueAnimator.INFINITE);
-        mObjectAnimator3.setDuration(2000);
-        mObjectAnimator3.setInterpolator(mLinearInterpolator);
-    }
-
-    static void setDropScale(View view) {
-        view.setScaleY(1f / 12f);
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        if (visibility == View.GONE) {
-            stopAnimation();
-        } else {
-            startAnimation();
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (getVisibility() == View.VISIBLE)
-            startAnimation();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        stopAnimation();
-    }
-
-    private void startAnimation() {
-        startAnimation(mObjectAnimator1);
-        startAnimation(mObjectAnimator2);
-        startAnimation(mObjectAnimator3);
-        mImage1.setVisibility(View.VISIBLE);
-        mImage2.setVisibility(View.VISIBLE);
-        mImage3.setVisibility(View.VISIBLE);
-    }
-
-    private void stopAnimation() {
-        stopAnimation(mObjectAnimator1, mImage1);
-        stopAnimation(mObjectAnimator2, mImage2);
-        stopAnimation(mObjectAnimator3, mImage3);
-        mImage1.setVisibility(View.GONE);
-        mImage2.setVisibility(View.GONE);
-        mImage3.setVisibility(View.GONE);
-    }
-
-    private void startAnimation(Animator animator) {
-        if (!animator.isStarted()) {
-            animator.start();
-        }
-    }
-
-    private void stopAnimation(Animator animator, View view) {
-        if (animator.isStarted()) {
-            animator.cancel();
-            setDropScale(view);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/MediaRowFocusView.java b/android/support/v17/leanback/widget/MediaRowFocusView.java
deleted file mode 100644
index 471f64e..0000000
--- a/android/support/v17/leanback/widget/MediaRowFocusView.java
+++ /dev/null
@@ -1,71 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Creates a view for a media item row in a playlist
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-class MediaRowFocusView extends View {
-
-    private final Paint mPaint;
-    private final RectF mRoundRectF = new RectF();
-    private int mRoundRectRadius;
-
-    public MediaRowFocusView(Context context) {
-        super(context);
-        mPaint = createPaint(context);
-    }
-
-    public MediaRowFocusView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mPaint = createPaint(context);
-    }
-
-    public MediaRowFocusView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mPaint = createPaint(context);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        mRoundRectRadius = getHeight() / 2;
-        int drawHeight = 2 * mRoundRectRadius;
-        int drawOffset = (drawHeight - getHeight()) / 2;
-        mRoundRectF.set(0, -drawOffset, getWidth(), getHeight() + drawOffset);
-        canvas.drawRoundRect(mRoundRectF, mRoundRectRadius, mRoundRectRadius, mPaint);
-    }
-
-    private Paint createPaint(Context context) {
-        Paint paint = new Paint();
-        paint.setColor(context.getResources().getColor(
-                R.color.lb_playback_media_row_highlight_color));
-        return paint;
-    }
-
-    public int getRoundRectRadius() {
-        return mRoundRectRadius;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/MultiActionsProvider.java b/android/support/v17/leanback/widget/MultiActionsProvider.java
deleted file mode 100644
index 8127012..0000000
--- a/android/support/v17/leanback/widget/MultiActionsProvider.java
+++ /dev/null
@@ -1,114 +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.v17.leanback.widget;
-
-import android.graphics.drawable.Drawable;
-
-/**
- * An interface implemented by the user if they wish to provide actions for a media item row to
- * be displayed by an {@link AbstractMediaItemPresenter}.
- *
- * A media row consists of media item details together with a number of custom actions,
- * following the media item details. Classes implementing {@link MultiActionsProvider} can define
- * their own media data model within their derived classes.
- * <p>
- *     The actions are provided by overriding {@link MultiActionsProvider#getActions()}
- *     Provided actions should be instances of {@link MultiAction}.
- * </p>
- */
-public interface MultiActionsProvider {
-
-    /**
-     * MultiAction represents an action that can have multiple states. {@link #getIndex()} returns
-     * the current index within the drawables. Both list of drawables and index can be updated
-     * dynamically in the program, and the UI could be updated by notifying the listeners
-     * provided in {@link AbstractMediaItemPresenter.ViewHolder}.
-     */
-    public static class MultiAction {
-        private long mId;
-        private int mIndex;
-        private Drawable[] mDrawables;
-
-        public MultiAction(long id) {
-            mId = id;
-            mIndex = 0;
-        }
-
-        /**
-         * Sets the drawables used for displaying different states within this {@link MultiAction}.
-         * The size of drawables determines the set of states this action represents.
-         * @param drawables Array of drawables for different MultiAction states.
-         */
-        public void setDrawables(Drawable[] drawables) {
-            mDrawables = drawables;
-            if (mIndex > drawables.length - 1) {
-                mIndex = drawables.length - 1;
-            }
-        }
-
-        /**
-         * Returns the drawables used for displaying different states within this
-         * {@link MultiAction}.
-         * @return The drawables used for displaying different states within this
-         *         {@link MultiAction}.
-         */
-        public Drawable[] getDrawables() {
-            return mDrawables;
-        }
-
-        /**
-         * Increments the index which this MultiAction currently represents. The index is wrapped
-         * around to zero when the end is reached.
-         */
-        public void incrementIndex() {
-            setIndex(mIndex < (mDrawables.length - 1) ? (mIndex + 1) : 0);
-        }
-
-        /**
-         * Sets the index which this MultiAction currently represents.
-         * @param index The current action index.
-         */
-        public void setIndex(int index) {
-            mIndex = index;
-        }
-
-        /**
-         * Returns the currently selected index in this MultiAction.
-         * @return The currently selected index in this MultiAction.
-         */
-        public int getIndex() {
-            return mIndex;
-        }
-
-        /**
-         * @return The icon drawable for the current state of this MultiAction.
-         */
-        public Drawable getCurrentDrawable() {
-            return mDrawables[mIndex];
-        }
-
-        /**
-         * @return The id for this MultiAction.
-         */
-        public long getId() {
-            return mId;
-        }
-    }
-
-    /**
-     * Should override this method in order to provide a custom set of actions for a media item row
-     * @return Array of MultiAction items to be displayed for this media item row.
-     */
-    public MultiAction[] getActions();
-}
diff --git a/android/support/v17/leanback/widget/NonOverlappingFrameLayout.java b/android/support/v17/leanback/widget/NonOverlappingFrameLayout.java
deleted file mode 100644
index 5255d95..0000000
--- a/android/support/v17/leanback/widget/NonOverlappingFrameLayout.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-class NonOverlappingFrameLayout extends FrameLayout {
-
-    public NonOverlappingFrameLayout(Context context) {
-        this(context, null);
-    }
-
-    public NonOverlappingFrameLayout(Context context, AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    public NonOverlappingFrameLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    /**
-     * Avoid creating hardware layer when Transition is animating alpha.
-     */
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java b/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java
deleted file mode 100644
index 8985f82..0000000
--- a/android/support/v17/leanback/widget/NonOverlappingLinearLayout.java
+++ /dev/null
@@ -1,112 +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.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;
-
-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);
-    }
-
-    public NonOverlappingLinearLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NonOverlappingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    /**
-     * Avoids creating a hardware layer when animating alpha.
-     */
-    @Override
-    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/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java b/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java
deleted file mode 100644
index 20c76aa..0000000
--- a/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java
+++ /dev/null
@@ -1,141 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-
-/**
- * Implements foreground drawable before M and falls back to M's foreground implementation.
- */
-class NonOverlappingLinearLayoutWithForeground extends LinearLayout {
-
-    private static final int VERSION_M = 23;
-
-    private Drawable mForeground;
-    private boolean mForegroundBoundsChanged;
-    private final Rect mSelfBounds = new Rect();
-
-    public NonOverlappingLinearLayoutWithForeground(Context context) {
-        this(context, null);
-    }
-
-    public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs,
-            int defStyle) {
-        super(context, attrs, defStyle);
-        if (context.getApplicationInfo().targetSdkVersion >= VERSION_M
-                && VERSION.SDK_INT >= VERSION_M) {
-            // don't need do anything, base View constructor >=M already reads the foreground if
-            // targetSDK is >= M.
-        } else {
-            // in other cases, including M but targetSDK is less than M, we need setForeground in
-            // code.
-            TypedArray a = context.obtainStyledAttributes(attrs,
-                    new int[] { android.R.attr.foreground });
-            Drawable d = a.getDrawable(0);
-            if (d != null) {
-                setForegroundCompat(d);
-            }
-            a.recycle();
-        }
-    }
-
-    public void setForegroundCompat(Drawable d) {
-        if (VERSION.SDK_INT >= VERSION_M) {
-            // From M,  foreground is naturally supported.
-            ForegroundHelper.setForeground(this, d);
-        } else {
-            // before M, do our own customized foreground draw.
-            if (mForeground != d) {
-                mForeground = d;
-                mForegroundBoundsChanged = true;
-                setWillNotDraw(false);
-                mForeground.setCallback(this);
-                if (mForeground.isStateful()) {
-                    mForeground.setState(getDrawableState());
-                }
-            }
-        }
-    }
-
-    public Drawable getForegroundCompat() {
-        if (VERSION.SDK_INT >= VERSION_M) {
-            return ForegroundHelper.getForeground(this);
-        } else {
-            return mForeground;
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mForeground != null) {
-            final Drawable foreground = mForeground;
-            if (mForegroundBoundsChanged) {
-                mForegroundBoundsChanged = false;
-                final Rect selfBounds = mSelfBounds;
-                final int w = getRight() - getLeft();
-                final int h = getBottom() - getTop();
-                selfBounds.set(0, 0, w, h);
-                foreground.setBounds(selfBounds);
-            }
-            foreground.draw(canvas);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mForegroundBoundsChanged |= changed;
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || (who == mForeground);
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        super.jumpDrawablesToCurrentState();
-        if (mForeground != null) {
-            mForeground.jumpToCurrentState();
-        }
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mForeground != null && mForeground.isStateful()) {
-            mForeground.setState(getDrawableState());
-        }
-    }
-
-    /**
-     * Avoids creating a hardware layer when animating alpha.
-     */
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/NonOverlappingRelativeLayout.java b/android/support/v17/leanback/widget/NonOverlappingRelativeLayout.java
deleted file mode 100644
index ee2ad35..0000000
--- a/android/support/v17/leanback/widget/NonOverlappingRelativeLayout.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.RelativeLayout;
-
-class NonOverlappingRelativeLayout extends RelativeLayout {
-
-    public NonOverlappingRelativeLayout(Context context) {
-        this(context, null);
-    }
-
-    public NonOverlappingRelativeLayout(Context context, AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    public NonOverlappingRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    /**
-     * Avoid creating hardware layer when Transition is animating alpha.
-     */
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/NonOverlappingView.java b/android/support/v17/leanback/widget/NonOverlappingView.java
deleted file mode 100644
index 60f6ab8..0000000
--- a/android/support/v17/leanback/widget/NonOverlappingView.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.v17.leanback.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
-class NonOverlappingView extends View {
-    public NonOverlappingView(Context context) {
-        this(context, null);
-    }
-
-    public NonOverlappingView(Context context, AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    public NonOverlappingView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    /**
-     * Avoids creating a hardware layer when Transition is animating alpha.
-     */
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/ObjectAdapter.java b/android/support/v17/leanback/widget/ObjectAdapter.java
deleted file mode 100644
index d411f9e..0000000
--- a/android/support/v17/leanback/widget/ObjectAdapter.java
+++ /dev/null
@@ -1,352 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.database.Observable;
-import android.support.annotation.RestrictTo;
-
-/**
- * Base class adapter to be used in leanback activities.  Provides access to a data model and is
- * decoupled from the presentation of the items via {@link PresenterSelector}.
- */
-public abstract class ObjectAdapter {
-
-    /** Indicates that an id has not been set. */
-    public static final int NO_ID = -1;
-
-    /**
-     * A DataObserver can be notified when an ObjectAdapter's underlying data
-     * changes. Separate methods provide notifications about different types of
-     * changes.
-     */
-    public static abstract class DataObserver {
-        /**
-         * Called whenever the ObjectAdapter's data has changed in some manner
-         * outside of the set of changes covered by the other range-based change
-         * notification methods.
-         */
-        public void onChanged() {
-        }
-
-        /**
-         * Called when a range of items in the ObjectAdapter has changed. The
-         * basic ordering and structure of the ObjectAdapter has not changed.
-         *
-         * @param positionStart The position of the first item that changed.
-         * @param itemCount     The number of items changed.
-         */
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        /**
-         * Called when a range of items in the ObjectAdapter has changed. The
-         * basic ordering and structure of the ObjectAdapter has not changed.
-         *
-         * @param positionStart The position of the first item that changed.
-         * @param itemCount     The number of items changed.
-         * @param payload       Optional parameter, use null to identify a "full" update.
-         */
-        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
-            onChanged();
-        }
-
-        /**
-         * Called when a range of items is inserted into the ObjectAdapter.
-         *
-         * @param positionStart The position of the first inserted item.
-         * @param itemCount     The number of items inserted.
-         */
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            onChanged();
-        }
-
-        /**
-         * Called when an item is moved from one position to another position
-         *
-         * @param fromPosition Previous position of the item.
-         * @param toPosition   New position of the item.
-         */
-        public void onItemMoved(int fromPosition, int toPosition) {
-            onChanged();
-        }
-
-        /**
-         * Called when a range of items is removed from the ObjectAdapter.
-         *
-         * @param positionStart The position of the first removed item.
-         * @param itemCount     The number of items removed.
-         */
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            onChanged();
-        }
-    }
-
-    private static final class DataObservable extends Observable<DataObserver> {
-
-        DataObservable() {
-        }
-
-        public void notifyChanged() {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onChanged();
-            }
-        }
-
-        public void notifyItemRangeChanged(int positionStart, int itemCount) {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeChanged(positionStart, itemCount);
-            }
-        }
-
-        public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
-            }
-        }
-
-        public void notifyItemRangeInserted(int positionStart, int itemCount) {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
-            }
-        }
-
-        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
-            }
-        }
-
-        public void notifyItemMoved(int positionStart, int toPosition) {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemMoved(positionStart, toPosition);
-            }
-        }
-
-        boolean hasObserver() {
-            return mObservers.size() > 0;
-        }
-    }
-
-    private final DataObservable mObservable = new DataObservable();
-    private boolean mHasStableIds;
-    private PresenterSelector mPresenterSelector;
-
-    /**
-     * Constructs an adapter with the given {@link PresenterSelector}.
-     */
-    public ObjectAdapter(PresenterSelector presenterSelector) {
-        setPresenterSelector(presenterSelector);
-    }
-
-    /**
-     * Constructs an adapter that uses the given {@link Presenter} for all items.
-     */
-    public ObjectAdapter(Presenter presenter) {
-        setPresenterSelector(new SinglePresenterSelector(presenter));
-    }
-
-    /**
-     * Constructs an adapter.
-     */
-    public ObjectAdapter() {
-    }
-
-    /**
-     * Sets the presenter selector.  May not be null.
-     */
-    public final void setPresenterSelector(PresenterSelector presenterSelector) {
-        if (presenterSelector == null) {
-            throw new IllegalArgumentException("Presenter selector must not be null");
-        }
-        final boolean update = (mPresenterSelector != null);
-        final boolean selectorChanged = update && mPresenterSelector != presenterSelector;
-
-        mPresenterSelector = presenterSelector;
-
-        if (selectorChanged) {
-            onPresenterSelectorChanged();
-        }
-        if (update) {
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Called when {@link #setPresenterSelector(PresenterSelector)} is called
-     * and the PresenterSelector differs from the previous one.
-     */
-    protected void onPresenterSelectorChanged() {
-    }
-
-    /**
-     * Returns the presenter selector for this ObjectAdapter.
-     */
-    public final PresenterSelector getPresenterSelector() {
-        return mPresenterSelector;
-    }
-
-    /**
-     * Registers a DataObserver for data change notifications.
-     */
-    public final void registerObserver(DataObserver observer) {
-        mObservable.registerObserver(observer);
-    }
-
-    /**
-     * Unregisters a DataObserver for data change notifications.
-     */
-    public final void unregisterObserver(DataObserver observer) {
-        mObservable.unregisterObserver(observer);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public final boolean hasObserver() {
-        return mObservable.hasObserver();
-    }
-
-    /**
-     * Unregisters all DataObservers for this ObjectAdapter.
-     */
-    public final void unregisterAllObservers() {
-        mObservable.unregisterAll();
-    }
-
-    /**
-     * Notifies UI that some items has changed.
-     *
-     * @param positionStart Starting position of the changed items.
-     * @param itemCount     Total number of items that changed.
-     */
-    public final void notifyItemRangeChanged(int positionStart, int itemCount) {
-        mObservable.notifyItemRangeChanged(positionStart, itemCount);
-    }
-
-    /**
-     * Notifies UI that some items has changed.
-     *
-     * @param positionStart Starting position of the changed items.
-     * @param itemCount     Total number of items that changed.
-     * @param payload       Optional parameter, use null to identify a "full" update.
-     */
-    public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
-        mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
-    }
-
-    /**
-     * Notifies UI that new items has been inserted.
-     *
-     * @param positionStart Position where new items has been inserted.
-     * @param itemCount     Count of the new items has been inserted.
-     */
-    final protected void notifyItemRangeInserted(int positionStart, int itemCount) {
-        mObservable.notifyItemRangeInserted(positionStart, itemCount);
-    }
-
-    /**
-     * Notifies UI that some items that has been removed.
-     *
-     * @param positionStart Starting position of the removed items.
-     * @param itemCount     Total number of items that has been removed.
-     */
-    final protected void notifyItemRangeRemoved(int positionStart, int itemCount) {
-        mObservable.notifyItemRangeRemoved(positionStart, itemCount);
-    }
-
-    /**
-     * Notifies UI that item at fromPosition has been moved to toPosition.
-     *
-     * @param fromPosition Previous position of the item.
-     * @param toPosition   New position of the item.
-     */
-    protected final void notifyItemMoved(int fromPosition, int toPosition) {
-        mObservable.notifyItemMoved(fromPosition, toPosition);
-    }
-
-    /**
-     * Notifies UI that the underlying data has changed.
-     */
-    final protected void notifyChanged() {
-        mObservable.notifyChanged();
-    }
-
-    /**
-     * Returns true if the item ids are stable across changes to the
-     * underlying data.  When this is true, clients of the ObjectAdapter can use
-     * {@link #getId(int)} to correlate Objects across changes.
-     */
-    public final boolean hasStableIds() {
-        return mHasStableIds;
-    }
-
-    /**
-     * Sets whether the item ids are stable across changes to the underlying
-     * data.
-     */
-    public final void setHasStableIds(boolean hasStableIds) {
-        boolean changed = mHasStableIds != hasStableIds;
-        mHasStableIds = hasStableIds;
-
-        if (changed) {
-            onHasStableIdsChanged();
-        }
-    }
-
-    /**
-     * Called when {@link #setHasStableIds(boolean)} is called and the status
-     * of stable ids has changed.
-     */
-    protected void onHasStableIdsChanged() {
-    }
-
-    /**
-     * Returns the {@link Presenter} for the given item from the adapter.
-     */
-    public final Presenter getPresenter(Object item) {
-        if (mPresenterSelector == null) {
-            throw new IllegalStateException("Presenter selector must not be null");
-        }
-        return mPresenterSelector.getPresenter(item);
-    }
-
-    /**
-     * Returns the number of items in the adapter.
-     */
-    public abstract int size();
-
-    /**
-     * Returns the item for the given position.
-     */
-    public abstract Object get(int position);
-
-    /**
-     * Returns the id for the given position.
-     */
-    public long getId(int position) {
-        return NO_ID;
-    }
-
-    /**
-     * Returns true if the adapter pairs each underlying data change with a call to notify and
-     * false otherwise.
-     */
-    public boolean isImmediateNotifySupported() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/OnActionClickedListener.java b/android/support/v17/leanback/widget/OnActionClickedListener.java
deleted file mode 100644
index d065119..0000000
--- a/android/support/v17/leanback/widget/OnActionClickedListener.java
+++ /dev/null
@@ -1,25 +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.v17.leanback.widget;
-
-/**
- * Interface for receiving notification when an {@link Action} is clicked.
- */
-public interface OnActionClickedListener {
-
-    /**
-     * Callback fired when the host fragment receives an action.
-     */
-    void onActionClicked(Action action);
-}
diff --git a/android/support/v17/leanback/widget/OnChildLaidOutListener.java b/android/support/v17/leanback/widget/OnChildLaidOutListener.java
deleted file mode 100644
index e7e18bf..0000000
--- a/android/support/v17/leanback/widget/OnChildLaidOutListener.java
+++ /dev/null
@@ -1,34 +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.v17.leanback.widget;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Interface for receiving notification when a child of this
- * ViewGroup has been laid out.
- */
-public interface OnChildLaidOutListener {
-    /**
-     * Callback method to be invoked when a child of this ViewGroup has been
-     * added to the view hierarchy and has been laid out.
-     *
-     * @param parent The ViewGroup where the layout happened.
-     * @param view The view within the ViewGroup that was laid out.
-     * @param position The position of the view in the adapter.
-     * @param id The id of the child.
-     */
-    void onChildLaidOut(ViewGroup parent, View view, int position, long id);
-}
diff --git a/android/support/v17/leanback/widget/OnChildSelectedListener.java b/android/support/v17/leanback/widget/OnChildSelectedListener.java
deleted file mode 100644
index e61f7e7..0000000
--- a/android/support/v17/leanback/widget/OnChildSelectedListener.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.v17.leanback.widget;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Interface for receiving notification when a child of this
- * ViewGroup has been selected.
- * @deprecated Use {@link OnChildViewHolderSelectedListener}
- */
-@Deprecated
-public interface OnChildSelectedListener {
-    /**
-     * Callback method to be invoked when a child of this ViewGroup has been
-     * selected.
-     *
-     * @param parent The ViewGroup where the selection happened.
-     * @param view The view within the ViewGroup that is selected, or null if no
-     *        view is selected.
-     * @param position The position of the view in the adapter, or NO_POSITION
-     *        if no view is selected.
-     * @param id The id of the child that is selected, or NO_ID if no view is
-     *        selected.
-     */
-    void onChildSelected(ViewGroup parent, View view, int position, long id);
-}
diff --git a/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java b/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
deleted file mode 100644
index ae170c0..0000000
--- a/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
+++ /dev/null
@@ -1,68 +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.v17.leanback.widget;
-
-import android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * Interface for receiving notification when a child of this ViewGroup has been selected.
- * There are two methods:
- * <li>
- *     {link {@link #onChildViewHolderSelected(RecyclerView, RecyclerView.ViewHolder, int, int)}}
- *     is called when the view holder is about to be selected.  The listener could change size
- *     of the view holder in this callback.
- * </li>
- * <li>
- *     {link {@link #onChildViewHolderSelectedAndPositioned(RecyclerView, RecyclerView.ViewHolder,
- *     int, int)} is called when view holder has been selected and laid out in RecyclerView.
- *
- * </li>
- */
-public abstract class OnChildViewHolderSelectedListener {
-    /**
-     * Callback method to be invoked when a child of this ViewGroup has been selected. Listener
-     * might change the size of the child and the position of the child is not finalized. To get
-     * the final layout position of child, overide {@link #onChildViewHolderSelectedAndPositioned(
-     * RecyclerView, RecyclerView.ViewHolder, int, int)}.
-     *
-     * @param parent The RecyclerView where the selection happened.
-     * @param child The ViewHolder within the RecyclerView that is selected, or null if no
-     *        view is selected.
-     * @param position The position of the view in the adapter, or NO_POSITION
-     *        if no view is selected.
-     * @param subposition The index of which {@link ItemAlignmentDef} being used,
-     *                    0 if there is no ItemAlignmentDef defined for the item.
-     */
-    public void onChildViewHolderSelected(RecyclerView parent, RecyclerView.ViewHolder child,
-            int position, int subposition) {
-    }
-
-    /**
-     * Callback method to be invoked when a child of this ViewGroup has been selected and
-     * positioned.
-     *
-     * @param parent The RecyclerView where the selection happened.
-     * @param child The ViewHolder within the RecyclerView that is selected, or null if no
-     *        view is selected.
-     * @param position The position of the view in the adapter, or NO_POSITION
-     *        if no view is selected.
-     * @param subposition The index of which {@link ItemAlignmentDef} being used,
-     *                    0 if there is no ItemAlignmentDef defined for the item.
-     */
-    public void onChildViewHolderSelectedAndPositioned(RecyclerView parent,
-            RecyclerView.ViewHolder child, int position, int subposition) {
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/OnItemViewClickedListener.java b/android/support/v17/leanback/widget/OnItemViewClickedListener.java
deleted file mode 100644
index f4bf37d..0000000
--- a/android/support/v17/leanback/widget/OnItemViewClickedListener.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.v17.leanback.widget;
-
-/**
- * Interface for receiving notification when a item view holder is clicked.  This interface expects
- * row object to be sub class of {@link Row}.
- */
-public interface OnItemViewClickedListener extends BaseOnItemViewClickedListener<Row> {
-
-}
diff --git a/android/support/v17/leanback/widget/OnItemViewSelectedListener.java b/android/support/v17/leanback/widget/OnItemViewSelectedListener.java
deleted file mode 100644
index 30fdb67..0000000
--- a/android/support/v17/leanback/widget/OnItemViewSelectedListener.java
+++ /dev/null
@@ -1,23 +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.v17.leanback.widget;
-
-/**
- * Interface for receiving notification when a row or item becomes selected. The concept of
- * current selection is different than focus.  A row or item can be selected without having focus;
- * for example, when a row header view gains focus then the corresponding row view becomes selected.
- * This interface expects row object to be sub class of {@link Row}.
- */
-public interface OnItemViewSelectedListener extends BaseOnItemViewSelectedListener<Row> {
-}
diff --git a/android/support/v17/leanback/widget/PageRow.java b/android/support/v17/leanback/widget/PageRow.java
deleted file mode 100644
index c7765b6..0000000
--- a/android/support/v17/leanback/widget/PageRow.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.v17.leanback.widget;
-
-/**
- * Used to represent content spanning full page.
- */
-public class PageRow extends Row {
-
-    public PageRow(HeaderItem headerItem) {
-        super(headerItem);
-    }
-
-    @Override
-    final public boolean isRenderedAsRowView() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/PagingIndicator.java b/android/support/v17/leanback/widget/PagingIndicator.java
deleted file mode 100644
index a16afd3..0000000
--- a/android/support/v17/leanback/widget/PagingIndicator.java
+++ /dev/null
@@ -1,552 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-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;
-import android.support.annotation.VisibleForTesting;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * A page indicator with dots.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class PagingIndicator extends View {
-    private static final long DURATION_ALPHA = 167;
-    private static final long DURATION_DIAMETER = 417;
-    private static final long DURATION_TRANSLATION_X = DURATION_DIAMETER;
-    private static final TimeInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
-
-    private static final Property<Dot, Float> DOT_ALPHA =
-            new Property<Dot, Float>(Float.class, "alpha") {
-        @Override
-        public Float get(Dot dot) {
-            return dot.getAlpha();
-        }
-
-        @Override
-        public void set(Dot dot, Float value) {
-            dot.setAlpha(value);
-        }
-    };
-
-    private static final Property<Dot, Float> DOT_DIAMETER =
-            new Property<Dot, Float>(Float.class, "diameter") {
-        @Override
-        public Float get(Dot dot) {
-            return dot.getDiameter();
-        }
-
-        @Override
-        public void set(Dot dot, Float value) {
-            dot.setDiameter(value);
-        }
-    };
-
-    private static final Property<Dot, Float> DOT_TRANSLATION_X =
-            new Property<Dot, Float>(Float.class, "translation_x") {
-        @Override
-        public Float get(Dot dot) {
-            return dot.getTranslationX();
-        }
-
-        @Override
-        public void set(Dot dot, Float value) {
-            dot.setTranslationX(value);
-        }
-    };
-
-    // attribute
-    boolean mIsLtr;
-    final int mDotDiameter;
-    final int mDotRadius;
-    private final int mDotGap;
-    final int mArrowDiameter;
-    final int mArrowRadius;
-    private final int mArrowGap;
-    private final int mShadowRadius;
-    private Dot[] mDots;
-    // X position when the dot is selected.
-    private int[] mDotSelectedX;
-    // X position when the dot is located to the left of the selected dot.
-    private int[] mDotSelectedPrevX;
-    // X position when the dot is located to the right of the selected dot.
-    private int[] mDotSelectedNextX;
-    int mDotCenterY;
-
-    // state
-    private int mPageCount;
-    private int mCurrentPage;
-    private int mPreviousPage;
-
-    // drawing
-    @ColorInt
-    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;
-
-    public PagingIndicator(Context context) {
-        this(context, null, 0);
-    }
-
-    public PagingIndicator(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PagingIndicator(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        Resources res = getResources();
-        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PagingIndicator,
-                defStyle, 0);
-        mDotRadius = getDimensionFromTypedArray(typedArray, R.styleable.PagingIndicator_lbDotRadius,
-                R.dimen.lb_page_indicator_dot_radius);
-        mDotDiameter = mDotRadius * 2;
-        mArrowRadius = getDimensionFromTypedArray(typedArray,
-                R.styleable.PagingIndicator_arrowRadius, R.dimen.lb_page_indicator_arrow_radius);
-        mArrowDiameter = mArrowRadius * 2;
-        mDotGap = getDimensionFromTypedArray(typedArray, R.styleable.PagingIndicator_dotToDotGap,
-                R.dimen.lb_page_indicator_dot_gap);
-        mArrowGap = getDimensionFromTypedArray(typedArray,
-                R.styleable.PagingIndicator_dotToArrowGap, R.dimen.lb_page_indicator_arrow_gap);
-
-        int dotBgColor = getColorFromTypedArray(typedArray, R.styleable.PagingIndicator_dotBgColor,
-                R.color.lb_page_indicator_dot);
-        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        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);
-        mFgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        int shadowOffset = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_offset);
-        mFgPaint.setShadowLayer(mShadowRadius, shadowOffset, shadowOffset, shadowColor);
-        mArrow = loadArrow();
-        mArrowRect = new Rect(0, 0, mArrow.getWidth(), mArrow.getHeight());
-        mArrowToBgRatio = (float) mArrow.getWidth() / (float) mArrowDiameter;
-        // Initialize animations.
-        mShowAnimator = new AnimatorSet();
-        mShowAnimator.playTogether(createDotAlphaAnimator(0.0f, 1.0f),
-                createDotDiameterAnimator(mDotRadius * 2, mArrowRadius * 2),
-                createDotTranslationXAnimator());
-        mHideAnimator = new AnimatorSet();
-        mHideAnimator.playTogether(createDotAlphaAnimator(1.0f, 0.0f),
-                createDotDiameterAnimator(mArrowRadius * 2, mDotRadius * 2),
-                createDotTranslationXAnimator());
-        mAnimator.playTogether(mShowAnimator, mHideAnimator);
-        // Use software layer to show shadows.
-        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-    }
-
-    private int getDimensionFromTypedArray(TypedArray typedArray, int attr, int defaultId) {
-        return typedArray.getDimensionPixelOffset(attr,
-                getResources().getDimensionPixelOffset(defaultId));
-    }
-
-    private int getColorFromTypedArray(TypedArray typedArray, int attr, int defaultId) {
-        return typedArray.getColor(attr, getResources().getColor(defaultId));
-    }
-
-    private Bitmap loadArrow() {
-        Bitmap arrow = BitmapFactory.decodeResource(getResources(), R.drawable.lb_ic_nav_arrow);
-        if (mIsLtr) {
-            return arrow;
-        } else {
-            Matrix matrix = new Matrix();
-            matrix.preScale(-1, 1);
-            return Bitmap.createBitmap(arrow, 0, 0, arrow.getWidth(), arrow.getHeight(), matrix,
-                    false);
-        }
-    }
-
-    /**
-     * 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);
-        animator.setInterpolator(DECELERATE_INTERPOLATOR);
-        return animator;
-    }
-
-    private Animator createDotDiameterAnimator(float from, float to) {
-        ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_DIAMETER, from, to);
-        animator.setDuration(DURATION_DIAMETER);
-        animator.setInterpolator(DECELERATE_INTERPOLATOR);
-        return animator;
-    }
-
-    private Animator createDotTranslationXAnimator() {
-        // The direction is determined in the Dot.
-        ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_TRANSLATION_X,
-                -mArrowGap + mDotGap, 0.0f);
-        animator.setDuration(DURATION_TRANSLATION_X);
-        animator.setInterpolator(DECELERATE_INTERPOLATOR);
-        return animator;
-    }
-
-    /**
-     * Sets the page count.
-     */
-    public void setPageCount(int pages) {
-        if (pages <= 0) {
-            throw new IllegalArgumentException("The page count should be a positive integer");
-        }
-        mPageCount = pages;
-        mDots = new Dot[mPageCount];
-        for (int i = 0; i < mPageCount; ++i) {
-            mDots[i] = new Dot();
-        }
-        calculateDotPositions();
-        setSelectedPage(0);
-    }
-
-    /**
-     * Called when the page has been selected.
-     */
-    public void onPageSelected(int pageIndex, boolean withAnimation) {
-        if (mCurrentPage == pageIndex) {
-            return;
-        }
-        if (mAnimator.isStarted()) {
-            mAnimator.end();
-        }
-        mPreviousPage = mCurrentPage;
-        if (withAnimation) {
-            mHideAnimator.setTarget(mDots[mPreviousPage]);
-            mShowAnimator.setTarget(mDots[pageIndex]);
-            mAnimator.start();
-        }
-        setSelectedPage(pageIndex);
-    }
-
-    private void calculateDotPositions() {
-        int left = getPaddingLeft();
-        int top = getPaddingTop();
-        int right = getWidth() - getPaddingRight();
-        int requiredWidth = getRequiredWidth();
-        int mid = (left + right) / 2;
-        mDotSelectedX = new int[mPageCount];
-        mDotSelectedPrevX = new int[mPageCount];
-        mDotSelectedNextX = new int[mPageCount];
-        if (mIsLtr) {
-            int startLeft = mid - requiredWidth / 2;
-            // mDotSelectedX[0] should be mDotSelectedPrevX[-1] + mArrowGap
-            mDotSelectedX[0] = startLeft + mDotRadius - mDotGap + mArrowGap;
-            mDotSelectedPrevX[0] = startLeft + mDotRadius;
-            mDotSelectedNextX[0] = startLeft + mDotRadius - 2 * mDotGap + 2 * mArrowGap;
-            for (int i = 1; i < mPageCount; i++) {
-                mDotSelectedX[i] = mDotSelectedPrevX[i - 1] + mArrowGap;
-                mDotSelectedPrevX[i] = mDotSelectedPrevX[i - 1] + mDotGap;
-                mDotSelectedNextX[i] = mDotSelectedX[i - 1] + mArrowGap;
-            }
-        } else {
-            int startRight = mid + requiredWidth / 2;
-            // mDotSelectedX[0] should be mDotSelectedPrevX[-1] - mArrowGap
-            mDotSelectedX[0] = startRight - mDotRadius + mDotGap - mArrowGap;
-            mDotSelectedPrevX[0] = startRight - mDotRadius;
-            mDotSelectedNextX[0] = startRight - mDotRadius + 2 * mDotGap - 2 * mArrowGap;
-            for (int i = 1; i < mPageCount; i++) {
-                mDotSelectedX[i] = mDotSelectedPrevX[i - 1] - mArrowGap;
-                mDotSelectedPrevX[i] = mDotSelectedPrevX[i - 1] - mDotGap;
-                mDotSelectedNextX[i] = mDotSelectedX[i - 1] - mArrowGap;
-            }
-        }
-        mDotCenterY = top + mArrowRadius;
-        adjustDotPosition();
-    }
-
-    @VisibleForTesting
-    int getPageCount() {
-        return mPageCount;
-    }
-
-    @VisibleForTesting
-    int[] getDotSelectedX() {
-        return mDotSelectedX;
-    }
-
-    @VisibleForTesting
-    int[] getDotSelectedLeftX() {
-        return mDotSelectedPrevX;
-    }
-
-    @VisibleForTesting
-    int[] getDotSelectedRightX() {
-        return mDotSelectedNextX;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int desiredHeight = getDesiredHeight();
-        int height;
-        switch (MeasureSpec.getMode(heightMeasureSpec)) {
-            case MeasureSpec.EXACTLY:
-                height = MeasureSpec.getSize(heightMeasureSpec);
-                break;
-            case MeasureSpec.AT_MOST:
-                height = Math.min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec));
-                break;
-            case MeasureSpec.UNSPECIFIED:
-            default:
-                height = desiredHeight;
-                break;
-        }
-        int desiredWidth = getDesiredWidth();
-        int width;
-        switch (MeasureSpec.getMode(widthMeasureSpec)) {
-            case MeasureSpec.EXACTLY:
-                width = MeasureSpec.getSize(widthMeasureSpec);
-                break;
-            case MeasureSpec.AT_MOST:
-                width = Math.min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec));
-                break;
-            case MeasureSpec.UNSPECIFIED:
-            default:
-                width = desiredWidth;
-                break;
-        }
-        setMeasuredDimension(width, height);
-    }
-
-    @Override
-    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
-        setMeasuredDimension(width, height);
-        calculateDotPositions();
-    }
-
-    private int getDesiredHeight() {
-        return getPaddingTop() + mArrowDiameter + getPaddingBottom() + mShadowRadius;
-    }
-
-    private int getRequiredWidth() {
-        return 2 * mDotRadius + 2 * mArrowGap + (mPageCount - 3) * mDotGap;
-    }
-
-    private int getDesiredWidth() {
-        return getPaddingLeft() + getRequiredWidth() + getPaddingRight();
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        for (int i = 0; i < mPageCount; ++i) {
-            mDots[i].draw(canvas);
-        }
-    }
-
-    private void setSelectedPage(int now) {
-        if (now == mCurrentPage) {
-            return;
-        }
-
-        mCurrentPage = now;
-        adjustDotPosition();
-    }
-
-    private void adjustDotPosition() {
-        for (int i = 0; i < mCurrentPage; ++i) {
-            mDots[i].deselect();
-            mDots[i].mDirection = i == mPreviousPage ? Dot.LEFT : Dot.RIGHT;
-            mDots[i].mCenterX = mDotSelectedPrevX[i];
-        }
-        mDots[mCurrentPage].select();
-        mDots[mCurrentPage].mDirection = mPreviousPage < mCurrentPage ? Dot.LEFT : Dot.RIGHT;
-        mDots[mCurrentPage].mCenterX = mDotSelectedX[mCurrentPage];
-        for (int i = mCurrentPage + 1; i < mPageCount; ++i) {
-            mDots[i].deselect();
-            mDots[i].mDirection = Dot.RIGHT;
-            mDots[i].mCenterX = mDotSelectedNextX[i];
-        }
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        boolean isLtr = layoutDirection == View.LAYOUT_DIRECTION_LTR;
-        if (mIsLtr != isLtr) {
-            mIsLtr = isLtr;
-            mArrow = loadArrow();
-            if (mDots != null) {
-                for (Dot dot : mDots) {
-                    dot.onRtlPropertiesChanged();
-                }
-            }
-            calculateDotPositions();
-            invalidate();
-        }
-    }
-
-    public class Dot {
-        static final float LEFT = -1;
-        static final float RIGHT = 1;
-        static final float LTR = 1;
-        static final float RTL = -1;
-
-        float mAlpha;
-        @ColorInt
-        int mFgColor;
-        float mTranslationX;
-        float mCenterX;
-        float mDiameter;
-        float mRadius;
-        float mArrowImageRadius;
-        float mDirection = RIGHT;
-        float mLayoutDirection = mIsLtr ? LTR : RTL;
-
-        void select() {
-            mTranslationX = 0.0f;
-            mCenterX = 0.0f;
-            mDiameter = mArrowDiameter;
-            mRadius = mArrowRadius;
-            mArrowImageRadius = mRadius * mArrowToBgRatio;
-            mAlpha = 1.0f;
-            adjustAlpha();
-        }
-
-        void deselect() {
-            mTranslationX = 0.0f;
-            mCenterX = 0.0f;
-            mDiameter = mDotDiameter;
-            mRadius = mDotRadius;
-            mArrowImageRadius = mRadius * mArrowToBgRatio;
-            mAlpha = 0.0f;
-            adjustAlpha();
-        }
-
-        public void adjustAlpha() {
-            int alpha = Math.round(0xFF * mAlpha);
-            int red = Color.red(mDotFgSelectColor);
-            int green = Color.green(mDotFgSelectColor);
-            int blue = Color.blue(mDotFgSelectColor);
-            mFgColor = Color.argb(alpha, red, green, blue);
-        }
-
-        public float getAlpha() {
-            return mAlpha;
-        }
-
-        public void setAlpha(float alpha) {
-            this.mAlpha = alpha;
-            adjustAlpha();
-            invalidate();
-        }
-
-        public float getTranslationX() {
-            return mTranslationX;
-        }
-
-        public void setTranslationX(float translationX) {
-            this.mTranslationX = translationX * mDirection * mLayoutDirection;
-            invalidate();
-        }
-
-        public float getDiameter() {
-            return mDiameter;
-        }
-
-        public void setDiameter(float diameter) {
-            this.mDiameter = diameter;
-            this.mRadius = diameter / 2;
-            this.mArrowImageRadius = diameter / 2 * mArrowToBgRatio;
-            invalidate();
-        }
-
-        void draw(Canvas canvas) {
-            float centerX = mCenterX + mTranslationX;
-            canvas.drawCircle(centerX, mDotCenterY, mRadius, mBgPaint);
-            if (mAlpha > 0) {
-                mFgPaint.setColor(mFgColor);
-                canvas.drawCircle(centerX, mDotCenterY, mRadius, mFgPaint);
-                canvas.drawBitmap(mArrow, mArrowRect, new Rect((int) (centerX - mArrowImageRadius),
-                        (int) (mDotCenterY - mArrowImageRadius),
-                        (int) (centerX + mArrowImageRadius),
-                        (int) (mDotCenterY + mArrowImageRadius)), mArrowPaint);
-            }
-        }
-
-        void onRtlPropertiesChanged() {
-            mLayoutDirection = mIsLtr ? LTR : RTL;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/Parallax.java b/android/support/v17/leanback/widget/Parallax.java
deleted file mode 100644
index aebf9b4..0000000
--- a/android/support/v17/leanback/widget/Parallax.java
+++ /dev/null
@@ -1,628 +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.v17.leanback.widget;
-
-import android.support.annotation.CallSuper;
-import android.support.v17.leanback.widget.ParallaxEffect.FloatEffect;
-import android.support.v17.leanback.widget.ParallaxEffect.IntEffect;
-import android.util.Property;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Parallax tracks a list of dynamic {@link Property}s typically representing foreground UI
- * element positions on screen. Parallax keeps a list of {@link ParallaxEffect} objects which define
- * rules to mapping property values to {@link ParallaxTarget}.
- *
- * <p>
- * Example:
- * <code>
- *     // when Property "var1" changes from 15 to max value, perform parallax effect to
- *     // change myView's translationY from 0 to 100.
- *     Parallax<IntProperty> parallax = new Parallax<IntProperty>() {...};
- *     p1 = parallax.addProperty("var1");
- *     parallax.addEffect(p1.at(15), p1.atMax())
- *             .target(myView, PropertyValuesHolder.ofFloat("translationY", 0, 100));
- * </code>
- * </p>
- *
- * <p>
- * To create a {@link ParallaxEffect}, user calls {@link #addEffect(PropertyMarkerValue[])} with a
- * list of {@link PropertyMarkerValue} which defines the range of {@link Parallax.IntProperty} or
- * {@link Parallax.FloatProperty}. Then user adds {@link ParallaxTarget} into
- * {@link ParallaxEffect}.
- * </p>
- * <p>
- * App may subclass {@link Parallax.IntProperty} or {@link Parallax.FloatProperty} to supply
- * additional information about how to retrieve Property value.  {@link RecyclerViewParallax} is
- * a great example of Parallax implementation tracking child view positions on screen.
- * </p>
- * <p>
- * <ul>Restrictions of properties
- * <li>FloatProperty and IntProperty cannot be mixed in one Parallax</li>
- * <li>Values must be in ascending order.</li>
- * <li>If the UI element is unknown above screen, use UNKNOWN_BEFORE.</li>
- * <li>if the UI element is unknown below screen, use UNKNOWN_AFTER.</li>
- * <li>UNKNOWN_BEFORE and UNKNOWN_AFTER are not allowed to be next to each other.</li>
- * </ul>
- * These rules will be verified at runtime.
- * </p>
- * <p>
- * Subclass must override {@link #updateValues()} to update property values and perform
- * {@link ParallaxEffect}s. Subclass may call {@link #updateValues()} automatically e.g.
- * {@link RecyclerViewParallax} calls {@link #updateValues()} in RecyclerView scrolling. App might
- * call {@link #updateValues()} manually when Parallax is unaware of the value change. For example,
- * when a slide transition is running, {@link RecyclerViewParallax} is unaware of translation value
- * changes; it's the app's responsibility to call {@link #updateValues()} in every frame of
- * animation.
- * </p>
- * @param <PropertyT> Subclass of {@link Parallax.IntProperty} or {@link Parallax.FloatProperty}
- */
-public abstract class Parallax<PropertyT extends android.util.Property> {
-
-    /**
-     * Class holding a fixed value for a Property in {@link Parallax}.
-     * @param <PropertyT> Class of the property, e.g. {@link IntProperty} or {@link FloatProperty}.
-     */
-    public static class PropertyMarkerValue<PropertyT> {
-        private final PropertyT mProperty;
-
-        public PropertyMarkerValue(PropertyT property) {
-            mProperty = property;
-        }
-
-        /**
-         * @return Associated property.
-         */
-        public PropertyT getProperty() {
-            return mProperty;
-        }
-    }
-
-    /**
-     * IntProperty provide access to an index based integer type property inside
-     * {@link Parallax}. The IntProperty typically represents UI element position inside
-     * {@link Parallax}.
-     */
-    public static class IntProperty extends Property<Parallax, Integer> {
-
-        /**
-         * Property value is unknown and it's smaller than minimal value of Parallax. For
-         * example if a child is not created and before the first visible child of RecyclerView.
-         */
-        public static final int UNKNOWN_BEFORE = Integer.MIN_VALUE;
-
-        /**
-         * Property value is unknown and it's larger than {@link Parallax#getMaxValue()}. For
-         * example if a child is not created and after the last visible child of RecyclerView.
-         */
-        public static final int UNKNOWN_AFTER = Integer.MAX_VALUE;
-
-        private final int mIndex;
-
-        /**
-         * Constructor.
-         *
-         * @param name Name of this Property.
-         * @param index Index of this Property inside {@link Parallax}.
-         */
-        public IntProperty(String name, int index) {
-            super(Integer.class, name);
-            mIndex = index;
-        }
-
-        @Override
-        public final Integer get(Parallax object) {
-            return object.getIntPropertyValue(mIndex);
-        }
-
-        @Override
-        public final void set(Parallax object, Integer value) {
-            object.setIntPropertyValue(mIndex, value);
-        }
-
-        /**
-         * @return Index of this Property in {@link Parallax}.
-         */
-        public final int getIndex() {
-            return mIndex;
-        }
-
-        /**
-         * Fast version of get() method that returns a primitive int value of the Property.
-         * @param object The Parallax object that owns this Property.
-         * @return Int value of the Property.
-         */
-        public final int getValue(Parallax object) {
-            return object.getIntPropertyValue(mIndex);
-        }
-
-        /**
-         * Fast version of set() method that takes a primitive int value into the Property.
-         *
-         * @param object The Parallax object that owns this Property.
-         * @param value Int value of the Property.
-         */
-        public final void setValue(Parallax object, int value) {
-            object.setIntPropertyValue(mIndex, value);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for the absolute marker value.
-         *
-         * @param absoluteValue The integer marker value.
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atAbsolute(int absoluteValue) {
-            return new IntPropertyMarkerValue(this, absoluteValue, 0f);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for the marker value representing
-         * {@link Parallax#getMaxValue()}.
-         *
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atMax() {
-            return new IntPropertyMarkerValue(this, 0, 1f);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for the marker value representing 0.
-         *
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atMin() {
-            return new IntPropertyMarkerValue(this, 0);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for a fraction of
-         * {@link Parallax#getMaxValue()}.
-         *
-         * @param fractionOfMaxValue 0 to 1 fraction to multiply with
-         *                                       {@link Parallax#getMaxValue()} for
-         *                                       the marker value.
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atFraction(float fractionOfMaxValue) {
-            return new IntPropertyMarkerValue(this, 0, fractionOfMaxValue);
-        }
-
-        /**
-         * Create an {@link PropertyMarkerValue} object by multiplying the fraction with
-         * {@link Parallax#getMaxValue()} and adding offsetValue to it.
-         *
-         * @param offsetValue                    An offset integer value to be added to marker
-         *                                       value.
-         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
-         *                                       {@link Parallax#getMaxValue()} for
-         *                                       the marker value.
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue at(int offsetValue,
-                float fractionOfMaxParentVisibleSize) {
-            return new IntPropertyMarkerValue(this, offsetValue, fractionOfMaxParentVisibleSize);
-        }
-    }
-
-    /**
-     * Implementation of {@link PropertyMarkerValue} for {@link IntProperty}.
-     */
-    static class IntPropertyMarkerValue extends PropertyMarkerValue<IntProperty> {
-        private final int mValue;
-        private final float mFactionOfMax;
-
-        IntPropertyMarkerValue(IntProperty property, int value) {
-            this(property, value, 0f);
-        }
-
-        IntPropertyMarkerValue(IntProperty property, int value, float fractionOfMax) {
-            super(property);
-            mValue = value;
-            mFactionOfMax = fractionOfMax;
-        }
-
-        /**
-         * @return The marker value of integer type.
-         */
-        final int getMarkerValue(Parallax source) {
-            return mFactionOfMax == 0 ? mValue : mValue + Math.round(source
-                    .getMaxValue() * mFactionOfMax);
-        }
-    }
-
-    /**
-     * FloatProperty provide access to an index based integer type property inside
-     * {@link Parallax}. The FloatProperty typically represents UI element position inside
-     * {@link Parallax}.
-     */
-    public static class FloatProperty extends Property<Parallax, Float> {
-
-        /**
-         * Property value is unknown and it's smaller than minimal value of Parallax. For
-         * example if a child is not created and before the first visible child of RecyclerView.
-         */
-        public static final float UNKNOWN_BEFORE = -Float.MAX_VALUE;
-
-        /**
-         * Property value is unknown and it's larger than {@link Parallax#getMaxValue()}. For
-         * example if a child is not created and after the last visible child of RecyclerView.
-         */
-        public static final float UNKNOWN_AFTER = Float.MAX_VALUE;
-
-        private final int mIndex;
-
-        /**
-         * Constructor.
-         *
-         * @param name Name of this Property.
-         * @param index Index of this Property inside {@link Parallax}.
-         */
-        public FloatProperty(String name, int index) {
-            super(Float.class, name);
-            mIndex = index;
-        }
-
-        @Override
-        public final Float get(Parallax object) {
-            return object.getFloatPropertyValue(mIndex);
-        }
-
-        @Override
-        public final void set(Parallax object, Float value) {
-            object.setFloatPropertyValue(mIndex, value);
-        }
-
-        /**
-         * @return Index of this Property in {@link Parallax}.
-         */
-        public final int getIndex() {
-            return mIndex;
-        }
-
-        /**
-         * Fast version of get() method that returns a primitive int value of the Property.
-         * @param object The Parallax object that owns this Property.
-         * @return Float value of the Property.
-         */
-        public final float getValue(Parallax object) {
-            return object.getFloatPropertyValue(mIndex);
-        }
-
-        /**
-         * Fast version of set() method that takes a primitive float value into the Property.
-         *
-         * @param object The Parallax object that owns this Property.
-         * @param value Float value of the Property.
-         */
-        public final void setValue(Parallax object, float value) {
-            object.setFloatPropertyValue(mIndex, value);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for the absolute marker value.
-         *
-         * @param markerValue The float marker value.
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atAbsolute(float markerValue) {
-            return new FloatPropertyMarkerValue(this, markerValue, 0f);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for the marker value representing
-         * {@link Parallax#getMaxValue()}.
-         *
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atMax() {
-            return new FloatPropertyMarkerValue(this, 0, 1f);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for the marker value representing 0.
-         *
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atMin() {
-            return new FloatPropertyMarkerValue(this, 0);
-        }
-
-        /**
-         * Creates an {@link PropertyMarkerValue} object for a fraction of
-         * {@link Parallax#getMaxValue()}.
-         *
-         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
-         *                                       {@link Parallax#getMaxValue()} for
-         *                                       the marker value.
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue atFraction(float fractionOfMaxParentVisibleSize) {
-            return new FloatPropertyMarkerValue(this, 0, fractionOfMaxParentVisibleSize);
-        }
-
-        /**
-         * Create an {@link PropertyMarkerValue} object by multiplying the fraction with
-         * {@link Parallax#getMaxValue()} and adding offsetValue to it.
-         *
-         * @param offsetValue                    An offset float value to be added to marker value.
-         * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
-         *                                       {@link Parallax#getMaxValue()} for
-         *                                       the marker value.
-         * @return A new {@link PropertyMarkerValue} object.
-         */
-        public final PropertyMarkerValue at(float offsetValue,
-                float fractionOfMaxParentVisibleSize) {
-            return new FloatPropertyMarkerValue(this, offsetValue, fractionOfMaxParentVisibleSize);
-        }
-    }
-
-    /**
-     * Implementation of {@link PropertyMarkerValue} for {@link FloatProperty}.
-     */
-    static class FloatPropertyMarkerValue extends PropertyMarkerValue<FloatProperty> {
-        private final float mValue;
-        private final float mFactionOfMax;
-
-        FloatPropertyMarkerValue(FloatProperty property, float value) {
-            this(property, value, 0f);
-        }
-
-        FloatPropertyMarkerValue(FloatProperty property, float value, float fractionOfMax) {
-            super(property);
-            mValue = value;
-            mFactionOfMax = fractionOfMax;
-        }
-
-        /**
-         * @return The marker value.
-         */
-        final float getMarkerValue(Parallax source) {
-            return mFactionOfMax == 0 ? mValue : mValue + source.getMaxValue()
-                    * mFactionOfMax;
-        }
-    }
-
-    final List<PropertyT> mProperties = new ArrayList<PropertyT>();
-    final List<PropertyT> mPropertiesReadOnly = Collections.unmodifiableList(mProperties);
-
-    private int[] mValues = new int[4];
-    private float[] mFloatValues = new float[4];
-
-    private final List<ParallaxEffect> mEffects = new ArrayList<ParallaxEffect>(4);
-
-    /**
-     * Return the max value which is typically size of parent visible area, e.g. RecyclerView's
-     * height if we are tracking Y position of a child. The size can be used to calculate marker
-     * value using the provided fraction of FloatPropertyMarkerValue.
-     *
-     * @return Size of parent visible area.
-     * @see IntPropertyMarkerValue#IntPropertyMarkerValue(IntProperty, int, float)
-     * @see FloatPropertyMarkerValue#FloatPropertyMarkerValue(FloatProperty, float, float)
-     */
-    public abstract float getMaxValue();
-
-    /**
-     * Get index based property value.
-     *
-     * @param index Index of the property.
-     * @return Value of the property.
-     */
-    final int getIntPropertyValue(int index) {
-        return mValues[index];
-    }
-
-    /**
-     * Set index based property value.
-     *
-     * @param index Index of the property.
-     * @param value Value of the property.
-     */
-    final void setIntPropertyValue(int index, int value) {
-        if (index >= mProperties.size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mValues[index] = value;
-    }
-
-    /**
-     * Add a new IntProperty in the Parallax object. App may override
-     * {@link #createProperty(String, int)}.
-     *
-     * @param name Name of the property.
-     * @return Newly created Property object.
-     * @see #createProperty(String, int)
-     */
-    public final PropertyT addProperty(String name) {
-        int newPropertyIndex = mProperties.size();
-        PropertyT property = createProperty(name, newPropertyIndex);
-        if (property instanceof IntProperty) {
-            int size = mValues.length;
-            if (size == newPropertyIndex) {
-                int[] newValues = new int[size * 2];
-                for (int i = 0; i < size; i++) {
-                    newValues[i] = mValues[i];
-                }
-                mValues = newValues;
-            }
-            mValues[newPropertyIndex] = IntProperty.UNKNOWN_AFTER;
-        } else if (property instanceof FloatProperty) {
-            int size = mFloatValues.length;
-            if (size == newPropertyIndex) {
-                float[] newValues = new float[size * 2];
-                for (int i = 0; i < size; i++) {
-                    newValues[i] = mFloatValues[i];
-                }
-                mFloatValues = newValues;
-            }
-            mFloatValues[newPropertyIndex] = FloatProperty.UNKNOWN_AFTER;
-        } else {
-            throw new IllegalArgumentException("Invalid Property type");
-        }
-        mProperties.add(property);
-        return property;
-    }
-
-    /**
-     * Verify sanity of property values, throws RuntimeException if fails. The property values
-     * must be in ascending order. UNKNOW_BEFORE and UNKNOWN_AFTER are not allowed to be next to
-     * each other.
-     */
-    void verifyIntProperties() throws IllegalStateException {
-        if (mProperties.size() < 2) {
-            return;
-        }
-        int last = getIntPropertyValue(0);
-        for (int i = 1; i < mProperties.size(); i++) {
-            int v = getIntPropertyValue(i);
-            if (v < last) {
-                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                + " smaller than Property[%d]\"%s\"",
-                        i, mProperties.get(i).getName(),
-                        i - 1, mProperties.get(i - 1).getName()));
-            } else if (last == IntProperty.UNKNOWN_BEFORE && v == IntProperty.UNKNOWN_AFTER) {
-                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                + " UNKNOWN_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
-                        i - 1, mProperties.get(i - 1).getName(),
-                        i, mProperties.get(i).getName()));
-            }
-            last = v;
-        }
-    }
-
-    final void verifyFloatProperties() throws IllegalStateException {
-        if (mProperties.size() < 2) {
-            return;
-        }
-        float last = getFloatPropertyValue(0);
-        for (int i = 1; i < mProperties.size(); i++) {
-            float v = getFloatPropertyValue(i);
-            if (v < last) {
-                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                + " smaller than Property[%d]\"%s\"",
-                        i, mProperties.get(i).getName(),
-                        i - 1, mProperties.get(i - 1).getName()));
-            } else if (last == FloatProperty.UNKNOWN_BEFORE && v
-                    == FloatProperty.UNKNOWN_AFTER) {
-                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                + " UNKNOWN_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
-                        i - 1, mProperties.get(i - 1).getName(),
-                        i, mProperties.get(i).getName()));
-            }
-            last = v;
-        }
-    }
-
-    /**
-     * Get index based property value.
-     *
-     * @param index Index of the property.
-     * @return Value of the property.
-     */
-    final float getFloatPropertyValue(int index) {
-        return mFloatValues[index];
-    }
-
-    /**
-     * Set index based property value.
-     *
-     * @param index Index of the property.
-     * @param value Value of the property.
-     */
-    final void setFloatPropertyValue(int index, float value) {
-        if (index >= mProperties.size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mFloatValues[index] = value;
-    }
-
-    /**
-     * @return A unmodifiable list of properties.
-     */
-    public final List<PropertyT> getProperties() {
-        return mPropertiesReadOnly;
-    }
-
-    /**
-     * Create a new Property object. App does not directly call this method.  See
-     * {@link #addProperty(String)}.
-     *
-     * @param index  Index of the property in this Parallax object.
-     * @return Newly created Property object.
-     */
-    public abstract PropertyT createProperty(String name, int index);
-
-    /**
-     * Update property values and perform {@link ParallaxEffect}s. Subclass may override and call
-     * super.updateValues() after updated properties values.
-     */
-    @CallSuper
-    public void updateValues() {
-        for (int i = 0; i < mEffects.size(); i++) {
-            mEffects.get(i).performMapping(this);
-        }
-    }
-
-    /**
-     * Returns a list of {@link ParallaxEffect} object which defines rules to perform mapping to
-     * multiple {@link ParallaxTarget}s.
-     *
-     * @return A list of {@link ParallaxEffect} object.
-     */
-    public List<ParallaxEffect> getEffects() {
-        return mEffects;
-    }
-
-    /**
-     * Remove the {@link ParallaxEffect} object.
-     *
-     * @param effect The {@link ParallaxEffect} object to remove.
-     */
-    public void removeEffect(ParallaxEffect effect) {
-        mEffects.remove(effect);
-    }
-
-    /**
-     * Remove all {@link ParallaxEffect} objects.
-     */
-    public void removeAllEffects() {
-        mEffects.clear();
-    }
-
-    /**
-     * Create a {@link ParallaxEffect} object that will track source variable changes within a
-     * provided set of ranges.
-     *
-     * @param ranges A list of marker values that defines the ranges.
-     * @return Newly created ParallaxEffect object.
-     */
-    public ParallaxEffect addEffect(PropertyMarkerValue... ranges) {
-        ParallaxEffect effect;
-        if (ranges[0].getProperty() instanceof IntProperty) {
-            effect = new IntEffect();
-        } else {
-            effect = new FloatEffect();
-        }
-        effect.setPropertyRanges(ranges);
-        mEffects.add(effect);
-        return effect;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ParallaxEffect.java b/android/support/v17/leanback/widget/ParallaxEffect.java
deleted file mode 100644
index e1af762..0000000
--- a/android/support/v17/leanback/widget/ParallaxEffect.java
+++ /dev/null
@@ -1,469 +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.v17.leanback.widget;
-
-import android.animation.PropertyValuesHolder;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.widget.Parallax.FloatProperty;
-import android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue;
-import android.support.v17.leanback.widget.Parallax.IntProperty;
-import android.support.v17.leanback.widget.Parallax.PropertyMarkerValue;
-import android.util.Property;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * ParallaxEffect class drives changes in {@link ParallaxTarget} in response to changes in
- * variables defined in {@link Parallax}.
- * <p>
- * ParallaxEffect has a list of {@link Parallax.PropertyMarkerValue}s which represents the range of
- * values that source variables can take. The main function is
- * {@link ParallaxEffect#performMapping(Parallax)} which computes a fraction between 0 and 1
- * based on the current values of variables in {@link Parallax}. As the parallax effect goes
- * on, the fraction increases from 0 at beginning to 1 at the end. Then the fraction is passed on
- * to {@link ParallaxTarget#update(float)}.
- * <p>
- * App use {@link Parallax#addEffect(PropertyMarkerValue...)} to create a ParallaxEffect.
- */
-public abstract class ParallaxEffect {
-
-    final List<Parallax.PropertyMarkerValue> mMarkerValues = new ArrayList(2);
-    final List<Float> mWeights = new ArrayList<Float>(2);
-    final List<Float> mTotalWeights = new ArrayList<Float>(2);
-    final List<ParallaxTarget> mTargets = new ArrayList<ParallaxTarget>(4);
-
-    /**
-     * Only accessible from package
-     */
-    ParallaxEffect() {
-    }
-
-    /**
-     * Returns the list of {@link PropertyMarkerValue}s, which represents the range of values that
-     * source variables can take.
-     *
-     * @return A list of {@link Parallax.PropertyMarkerValue}s.
-     * @see #performMapping(Parallax)
-     */
-    public final List<Parallax.PropertyMarkerValue> getPropertyRanges() {
-        return mMarkerValues;
-    }
-
-    /**
-     * Returns a list of Float objects that represents weight associated with each variable range.
-     * Weights are used when there are three or more marker values.
-     *
-     * @return A list of Float objects that represents weight associated with each variable range.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public final List<Float> getWeights() {
-        return mWeights;
-    }
-
-    /**
-     * Sets the list of {@link PropertyMarkerValue}s, which represents the range of values that
-     * source variables can take.
-     *
-     * @param markerValues A list of {@link PropertyMarkerValue}s.
-     * @see #performMapping(Parallax)
-     */
-    public final void setPropertyRanges(Parallax.PropertyMarkerValue... markerValues) {
-        mMarkerValues.clear();
-        for (Parallax.PropertyMarkerValue markerValue : markerValues) {
-            mMarkerValues.add(markerValue);
-        }
-    }
-
-    /**
-     * Sets a list of Float objects that represents weight associated with each variable range.
-     * Weights are used when there are three or more marker values.
-     *
-     * @param weights A list of Float objects that represents weight associated with each variable
-     *                range.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public final void setWeights(float... weights) {
-        for (float weight : weights) {
-            if (weight <= 0) {
-                throw new IllegalArgumentException();
-            }
-        }
-        mWeights.clear();
-        mTotalWeights.clear();
-        float totalWeight = 0f;
-        for (float weight : weights) {
-            mWeights.add(weight);
-            totalWeight += weight;
-            mTotalWeights.add(totalWeight);
-        }
-    }
-
-    /**
-     * Sets a list of Float objects that represents weight associated with each variable range.
-     * Weights are used when there are three or more marker values.
-     *
-     * @param weights A list of Float objects that represents weight associated with each variable
-     *                range.
-     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    public final ParallaxEffect weights(float... weights) {
-        setWeights(weights);
-        return this;
-    }
-
-    /**
-     * Add a ParallaxTarget to run parallax effect.
-     *
-     * @param target ParallaxTarget to add.
-     */
-    public final void addTarget(ParallaxTarget target) {
-        mTargets.add(target);
-    }
-
-    /**
-     * Add a ParallaxTarget to run parallax effect.
-     *
-     * @param target ParallaxTarget to add.
-     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
-     */
-    public final ParallaxEffect target(ParallaxTarget target) {
-        mTargets.add(target);
-        return this;
-    }
-
-    /**
-     * Creates a {@link ParallaxTarget} from {@link PropertyValuesHolder} and adds it to the list
-     * of targets.
-     *
-     * @param targetObject Target object for PropertyValuesHolderTarget.
-     * @param values       PropertyValuesHolder for PropertyValuesHolderTarget.
-     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
-     */
-    public final ParallaxEffect target(Object targetObject, PropertyValuesHolder values) {
-        mTargets.add(new ParallaxTarget.PropertyValuesHolderTarget(targetObject, values));
-        return this;
-    }
-
-    /**
-     * Creates a {@link ParallaxTarget} using direct mapping from source property into target
-     * property, the new {@link ParallaxTarget} will be added to its list of targets.
-     *
-     * @param targetObject Target object for property.
-     * @param targetProperty The target property that will receive values.
-     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
-     * @param <T> Type of target object.
-     * @param <V> Type of target property value, either Integer or Float.
-     * @see ParallaxTarget#isDirectMapping()
-     */
-    public final <T, V extends Number> ParallaxEffect target(T targetObject,
-            Property<T, V> targetProperty) {
-        mTargets.add(new ParallaxTarget.DirectPropertyTarget(targetObject, targetProperty));
-        return this;
-    }
-
-    /**
-     * Returns the list of {@link ParallaxTarget} objects.
-     *
-     * @return The list of {@link ParallaxTarget} objects.
-     */
-    public final List<ParallaxTarget> getTargets() {
-        return mTargets;
-    }
-
-    /**
-     * Remove a {@link ParallaxTarget} object from the list.
-     * @param target The {@link ParallaxTarget} object to be removed.
-     */
-    public final void removeTarget(ParallaxTarget target) {
-        mTargets.remove(target);
-    }
-
-    /**
-     * Perform mapping from {@link Parallax} to list of {@link ParallaxTarget}.
-     */
-    public final void performMapping(Parallax source) {
-        if (mMarkerValues.size() < 2) {
-            return;
-        }
-        if (this instanceof IntEffect) {
-            source.verifyIntProperties();
-        } else {
-            source.verifyFloatProperties();
-        }
-        boolean fractionCalculated = false;
-        float fraction = 0;
-        Number directValue = null;
-        for (int i = 0; i < mTargets.size(); i++) {
-            ParallaxTarget target = mTargets.get(i);
-            if (target.isDirectMapping()) {
-                if (directValue == null) {
-                    directValue = calculateDirectValue(source);
-                }
-                target.directUpdate(directValue);
-            } else {
-                if (!fractionCalculated) {
-                    fractionCalculated = true;
-                    fraction = calculateFraction(source);
-                }
-                target.update(fraction);
-            }
-        }
-    }
-
-    /**
-     * This method is expected to compute a fraction between 0 and 1 based on the current values of
-     * variables in {@link Parallax}. As the parallax effect goes on, the fraction increases
-     * from 0 at beginning to 1 at the end.
-     *
-     * @return Float value between 0 and 1.
-     */
-    abstract float calculateFraction(Parallax source);
-
-    /**
-     * This method is expected to get the current value of the single {@link IntProperty} or
-     * {@link FloatProperty}.
-     *
-     * @return Current value of the single {@link IntProperty} or {@link FloatProperty}.
-     */
-    abstract Number calculateDirectValue(Parallax source);
-
-    /**
-     * When there are multiple ranges (aka three or more markerValues),  this method adjust the
-     * fraction inside a range to fraction of whole range.
-     * e.g. four marker values, three weight values: 6, 2, 2.  totalWeights are 6, 8, 10
-     * When markerValueIndex is 3, the fraction is inside last range.
-     * adjusted_fraction = 8 / 10 + 2 / 10 * fraction.
-     */
-    final float getFractionWithWeightAdjusted(float fraction, int markerValueIndex) {
-        // when there are three or more markerValues, take weight into consideration.
-        if (mMarkerValues.size() >= 3) {
-            final boolean hasWeightsDefined = mWeights.size() == mMarkerValues.size() - 1;
-            if (hasWeightsDefined) {
-                // use weights user defined
-                final float allWeights = mTotalWeights.get(mTotalWeights.size() - 1);
-                fraction = fraction * mWeights.get(markerValueIndex - 1) / allWeights;
-                if (markerValueIndex >= 2) {
-                    fraction += mTotalWeights.get(markerValueIndex - 2) / allWeights;
-                }
-            } else {
-                // assume each range has same weight.
-                final float allWeights =  mMarkerValues.size() - 1;
-                fraction = fraction / allWeights;
-                if (markerValueIndex >= 2) {
-                    fraction += (float) (markerValueIndex - 1) / allWeights;
-                }
-            }
-        }
-        return fraction;
-    }
-
-    /**
-     * Implementation of {@link ParallaxEffect} for integer type.
-     */
-    static final class IntEffect extends ParallaxEffect {
-
-        @Override
-        Number calculateDirectValue(Parallax source) {
-            if (mMarkerValues.size() != 2) {
-                throw new RuntimeException("Must use two marker values for direct mapping");
-            }
-            if (mMarkerValues.get(0).getProperty() != mMarkerValues.get(1).getProperty()) {
-                throw new RuntimeException(
-                        "Marker value must use same Property for direct mapping");
-            }
-            int value1 = ((Parallax.IntPropertyMarkerValue) mMarkerValues.get(0))
-                    .getMarkerValue(source);
-            int value2 = ((Parallax.IntPropertyMarkerValue) mMarkerValues.get(1))
-                    .getMarkerValue(source);
-            if (value1 > value2) {
-                int swapValue = value2;
-                value2 = value1;
-                value1 = swapValue;
-            }
-
-            Number currentValue = ((IntProperty) mMarkerValues.get(0).getProperty()).get(source);
-            if (currentValue.intValue() < value1) {
-                currentValue = value1;
-            } else if (currentValue.intValue() > value2) {
-                currentValue = value2;
-            }
-            return currentValue;
-        }
-
-        @Override
-        float calculateFraction(Parallax source) {
-            int lastIndex = 0;
-            int lastValue = 0;
-            int lastMarkerValue = 0;
-            // go through all markerValues, find first markerValue that current value is less than.
-            for (int i = 0; i <  mMarkerValues.size(); i++) {
-                Parallax.IntPropertyMarkerValue k =  (Parallax.IntPropertyMarkerValue)
-                        mMarkerValues.get(i);
-                int index = k.getProperty().getIndex();
-                int markerValue = k.getMarkerValue(source);
-                int currentValue = source.getIntPropertyValue(index);
-
-                float fraction;
-                if (i == 0) {
-                    if (currentValue >= markerValue) {
-                        return 0f;
-                    }
-                } else {
-                    if (lastIndex == index && lastMarkerValue < markerValue) {
-                        throw new IllegalStateException("marker value of same variable must be "
-                                + "descendant order");
-                    }
-                    if (currentValue == IntProperty.UNKNOWN_AFTER) {
-                        // Implies lastValue is less than lastMarkerValue and lastValue is not
-                        // UNKNWON_AFTER.  Estimates based on distance of two variables is screen
-                        // size.
-                        fraction = (float) (lastMarkerValue - lastValue)
-                                / source.getMaxValue();
-                        return getFractionWithWeightAdjusted(fraction, i);
-                    } else if (currentValue >= markerValue) {
-                        if (lastIndex == index) {
-                            // same variable index,  same UI element at two different MarkerValues,
-                            // e.g. UI element moves from lastMarkerValue=500 to markerValue=0,
-                            // fraction moves from 0 to 1.
-                            fraction = (float) (lastMarkerValue - currentValue)
-                                    / (lastMarkerValue - markerValue);
-                        } else if (lastValue != IntProperty.UNKNOWN_BEFORE) {
-                            // e.g. UIElement_1 at 300 scroll to UIElement_2 at 400, figure out when
-                            // UIElement_1 is at markerValue=300,  markerValue of UIElement_2 by
-                            // adding delta of values to markerValue of UIElement_2.
-                            lastMarkerValue = lastMarkerValue + (currentValue - lastValue);
-                            fraction = (float) (lastMarkerValue - currentValue)
-                                    / (lastMarkerValue - markerValue);
-                        } else {
-                            // Last variable is UNKNOWN_BEFORE.  Estimates based on assumption total
-                            // travel distance from last variable to this variable is screen visible
-                            // size.
-                            fraction = 1f - (float) (currentValue - markerValue)
-                                    / source.getMaxValue();
-                        }
-                        return getFractionWithWeightAdjusted(fraction, i);
-                    }
-                }
-                lastValue = currentValue;
-                lastIndex = index;
-                lastMarkerValue = markerValue;
-            }
-            return 1f;
-        }
-    }
-
-    /**
-     * Implementation of {@link ParallaxEffect} for float type.
-     */
-    static final class FloatEffect extends ParallaxEffect {
-
-        @Override
-        Number calculateDirectValue(Parallax source) {
-            if (mMarkerValues.size() != 2) {
-                throw new RuntimeException("Must use two marker values for direct mapping");
-            }
-            if (mMarkerValues.get(0).getProperty() != mMarkerValues.get(1).getProperty()) {
-                throw new RuntimeException(
-                        "Marker value must use same Property for direct mapping");
-            }
-            float value1 = ((FloatPropertyMarkerValue) mMarkerValues.get(0))
-                    .getMarkerValue(source);
-            float value2 = ((FloatPropertyMarkerValue) mMarkerValues.get(1))
-                    .getMarkerValue(source);
-            if (value1 > value2) {
-                float swapValue = value2;
-                value2 = value1;
-                value1 = swapValue;
-            }
-
-            Number currentValue = ((FloatProperty) mMarkerValues.get(0).getProperty()).get(source);
-            if (currentValue.floatValue() < value1) {
-                currentValue = value1;
-            } else if (currentValue.floatValue() > value2) {
-                currentValue = value2;
-            }
-            return currentValue;
-        }
-
-        @Override
-        float calculateFraction(Parallax source) {
-            int lastIndex = 0;
-            float lastValue = 0;
-            float lastMarkerValue = 0;
-            // go through all markerValues, find first markerValue that current value is less than.
-            for (int i = 0; i <  mMarkerValues.size(); i++) {
-                FloatPropertyMarkerValue k = (FloatPropertyMarkerValue) mMarkerValues.get(i);
-                int index = k.getProperty().getIndex();
-                float markerValue = k.getMarkerValue(source);
-                float currentValue = source.getFloatPropertyValue(index);
-
-                float fraction;
-                if (i == 0) {
-                    if (currentValue >= markerValue) {
-                        return 0f;
-                    }
-                } else {
-                    if (lastIndex == index && lastMarkerValue < markerValue) {
-                        throw new IllegalStateException("marker value of same variable must be "
-                                + "descendant order");
-                    }
-                    if (currentValue == FloatProperty.UNKNOWN_AFTER) {
-                        // Implies lastValue is less than lastMarkerValue and lastValue is not
-                        // UNKNOWN_AFTER.  Estimates based on distance of two variables is screen
-                        // size.
-                        fraction = (float) (lastMarkerValue - lastValue)
-                                / source.getMaxValue();
-                        return getFractionWithWeightAdjusted(fraction, i);
-                    } else if (currentValue >= markerValue) {
-                        if (lastIndex == index) {
-                            // same variable index,  same UI element at two different MarkerValues,
-                            // e.g. UI element moves from lastMarkerValue=500 to markerValue=0,
-                            // fraction moves from 0 to 1.
-                            fraction = (float) (lastMarkerValue - currentValue)
-                                    / (lastMarkerValue - markerValue);
-                        } else if (lastValue != FloatProperty.UNKNOWN_BEFORE) {
-                            // e.g. UIElement_1 at 300 scroll to UIElement_2 at 400, figure out when
-                            // UIElement_1 is at markerValue=300,  markerValue of UIElement_2 by
-                            // adding delta of values to markerValue of UIElement_2.
-                            lastMarkerValue = lastMarkerValue + (currentValue - lastValue);
-                            fraction = (float) (lastMarkerValue - currentValue)
-                                    / (lastMarkerValue - markerValue);
-                        } else {
-                            // Last variable is UNKNOWN_BEFORE.  Estimates based on assumption total
-                            // travel distance from last variable to this variable is screen visible
-                            // size.
-                            fraction = 1f - (float) (currentValue - markerValue)
-                                    / source.getMaxValue();
-                        }
-                        return getFractionWithWeightAdjusted(fraction, i);
-                    }
-                }
-                lastValue = currentValue;
-                lastIndex = index;
-                lastMarkerValue = markerValue;
-            }
-            return 1f;
-        }
-    }
-
-}
-
diff --git a/android/support/v17/leanback/widget/ParallaxTarget.java b/android/support/v17/leanback/widget/ParallaxTarget.java
deleted file mode 100644
index 49783ab..0000000
--- a/android/support/v17/leanback/widget/ParallaxTarget.java
+++ /dev/null
@@ -1,132 +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.v17.leanback.widget;
-
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.util.Property;
-import android.view.animation.LinearInterpolator;
-
-/**
- * ParallaxTarget is responsible for updating the target through the {@link #update(float)} method
- * or the {@link #directUpdate(Number)} method when {@link #isDirectMapping()} is true.
- * When {@link #isDirectMapping()} is false, {@link ParallaxEffect} transforms the values of
- * {@link Parallax}, which represents the current state of UI, into a float value between 0 and 1.
- * That float value is passed into {@link #update(float)} method.
- */
-public abstract class ParallaxTarget {
-
-    /**
-     * Implementation class is supposed to update target with the provided fraction
-     * (between 0 and 1). The fraction represents percentage of completed change (e.g. scroll) on
-     * target. Called only when {@link #isDirectMapping()} is false.
-     *
-     * @param fraction Fraction between 0 to 1.
-     * @see #isDirectMapping()
-     */
-    public void update(float fraction) {
-    }
-
-    /**
-     * Returns true if the ParallaxTarget is directly mapping from source value,
-     * {@link #directUpdate(Number)} will be used to update value, otherwise update(fraction) will
-     * be called to update value. Default implementation returns false.
-     *
-     * @return True if direct mapping, false otherwise.
-     * @see #directUpdate(Number)
-     * @see #update(float)
-     */
-    public boolean isDirectMapping() {
-        return false;
-    }
-
-    /**
-     * Directly update the target using a float or int value. Called when {@link #isDirectMapping()}
-     * is true.
-     *
-     * @param value Either int or float value.
-     * @see #isDirectMapping()
-     */
-    public void directUpdate(Number value) {
-    }
-
-    /**
-     * PropertyValuesHolderTarget is an implementation of {@link ParallaxTarget} that uses
-     * {@link PropertyValuesHolder} to update the target object.
-     */
-    public static final class PropertyValuesHolderTarget extends ParallaxTarget {
-
-        /**
-         * We simulate a parallax effect on target object using an ObjectAnimator. PSEUDO_DURATION
-         * is used on the ObjectAnimator.
-         */
-        private static final long PSEUDO_DURATION = 1000000;
-
-        private final ObjectAnimator mAnimator;
-        private float mFraction;
-
-        public PropertyValuesHolderTarget(Object targetObject, PropertyValuesHolder values) {
-            mAnimator = ObjectAnimator.ofPropertyValuesHolder(targetObject, values);
-            mAnimator.setInterpolator(new LinearInterpolator());
-            mAnimator.setDuration(PSEUDO_DURATION);
-        }
-
-        @Override
-        public void update(float fraction) {
-            mFraction = fraction;
-            mAnimator.setCurrentPlayTime((long) (PSEUDO_DURATION * fraction));
-        }
-
-    }
-
-    /**
-     * DirectPropertyTarget is to support direct mapping into either Integer Property or Float
-     * Property. App uses convenient method {@link ParallaxEffect#target(Object, Property)} to
-     * add a direct mapping.
-     * @param <T> Type of target object.
-     * @param <V> Type of value, either Integer or Float.
-     */
-    public static final class DirectPropertyTarget<T extends Object, V extends Number>
-            extends ParallaxTarget {
-
-        Object mObject;
-        Property<T, V> mProperty;
-
-        /**
-         * @param targetObject Target object for perform Parallax
-         * @param property     Target property, either an Integer Property or a Float Property.
-         */
-        public DirectPropertyTarget(Object targetObject, Property<T, V> property) {
-            mObject = targetObject;
-            mProperty = property;
-        }
-
-        /**
-         * Returns true as DirectPropertyTarget receives a number to update Property in
-         * {@link #directUpdate(Number)}.
-         */
-        @Override
-        public boolean isDirectMapping() {
-            return true;
-        }
-
-        @Override
-        public void directUpdate(Number value) {
-            mProperty.set((T) mObject, (V) value);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/PersistentFocusWrapper.java b/android/support/v17/leanback/widget/PersistentFocusWrapper.java
deleted file mode 100644
index 8a3d886..0000000
--- a/android/support/v17/leanback/widget/PersistentFocusWrapper.java
+++ /dev/null
@@ -1,182 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-
-/**
- * Saves the focused grandchild position.
- * Helps add persistent focus feature to various ViewGroups.
- */
-class PersistentFocusWrapper extends FrameLayout {
-
-    private static final String TAG = "PersistentFocusWrapper";
-    private static final boolean DEBUG = false;
-
-    private int mSelectedPosition = -1;
-
-    /**
-     * By default, focus is persisted when searching vertically
-     * but not horizontally.
-     */
-    private boolean mPersistFocusVertical = true;
-
-    public PersistentFocusWrapper(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public PersistentFocusWrapper(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    int getGrandChildCount() {
-        ViewGroup wrapper = (ViewGroup) getChildAt(0);
-        return wrapper == null ? 0 : wrapper.getChildCount();
-    }
-
-    /**
-     * Clears the selected position and clears focus.
-     */
-    public void clearSelection() {
-        mSelectedPosition = -1;
-        if (hasFocus()) {
-            clearFocus();
-        }
-    }
-
-    /**
-     * Persist focus when focus search direction is up or down.
-     */
-    public void persistFocusVertical() {
-        mPersistFocusVertical = true;
-    }
-
-    /**
-     * Persist focus when focus search direction is left or right.
-     */
-    public void persistFocusHorizontal() {
-        mPersistFocusVertical = false;
-    }
-
-    private boolean shouldPersistFocusFromDirection(int direction) {
-        return ((mPersistFocusVertical && (direction == FOCUS_UP || direction == FOCUS_DOWN))
-                || (!mPersistFocusVertical
-                && (direction == FOCUS_LEFT || direction == FOCUS_RIGHT)));
-    }
-
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (DEBUG) Log.v(TAG, "addFocusables");
-        if (hasFocus() || getGrandChildCount() == 0
-                || !shouldPersistFocusFromDirection(direction)) {
-            super.addFocusables(views, direction, focusableMode);
-        } else {
-            // Select a child in requestFocus
-            views.add(this);
-        }
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        super.requestChildFocus(child, focused);
-        View view = focused;
-        while (view != null && view.getParent() != child) {
-            view = (View) view.getParent();
-        }
-        mSelectedPosition = view == null ? -1 : ((ViewGroup) child).indexOfChild(view);
-        if (DEBUG) {
-            Log.v(TAG, "requestChildFocus focused " + focused + " mSelectedPosition "
-                    + mSelectedPosition);
-        }
-    }
-
-    @Override
-    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
-        if (DEBUG) Log.v(TAG, "requestFocus mSelectedPosition " + mSelectedPosition);
-        ViewGroup wrapper = (ViewGroup) getChildAt(0);
-        if (wrapper != null && mSelectedPosition >= 0 && mSelectedPosition < getGrandChildCount()) {
-            if (wrapper.getChildAt(mSelectedPosition).requestFocus(
-                    direction, previouslyFocusedRect)) {
-                return true;
-            }
-        }
-        return super.requestFocus(direction, previouslyFocusedRect);
-    }
-
-    static class SavedState extends View.BaseSavedState {
-
-        int mSelectedPosition;
-
-        SavedState(Parcel in) {
-            super(in);
-            mSelectedPosition = in.readInt();
-        }
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mSelectedPosition);
-        }
-
-        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
-    protected Parcelable onSaveInstanceState() {
-        if (DEBUG) Log.v(TAG, "onSaveInstanceState");
-        SavedState savedState = new SavedState(super.onSaveInstanceState());
-        savedState.mSelectedPosition = mSelectedPosition;
-        return savedState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-        SavedState savedState = (SavedState) state;
-        mSelectedPosition = ((SavedState) state).mSelectedPosition;
-        if (DEBUG) Log.v(TAG, "onRestoreInstanceState mSelectedPosition " + mSelectedPosition);
-        super.onRestoreInstanceState(savedState.getSuperState());
-    }
-}
diff --git a/android/support/v17/leanback/widget/PlaybackControlsPresenter.java b/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
deleted file mode 100644
index 01e9de3..0000000
--- a/android/support/v17/leanback/widget/PlaybackControlsPresenter.java
+++ /dev/null
@@ -1,361 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.drawable.ClipDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.util.MathUtil;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.FrameLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-/**
- * A presenter for a control bar that supports "more actions",
- * and toggling the set of controls between primary and secondary
- * sets of {@link Actions}.
- */
-class PlaybackControlsPresenter extends ControlBarPresenter {
-
-    /**
-     * The data type expected by this presenter.
-     */
-    static class BoundData extends ControlBarPresenter.BoundData {
-        /**
-         * The adapter containing secondary actions.
-         */
-        ObjectAdapter secondaryActionsAdapter;
-    }
-
-    class ViewHolder extends ControlBarPresenter.ViewHolder {
-        ObjectAdapter mMoreActionsAdapter;
-        ObjectAdapter.DataObserver mMoreActionsObserver;
-        final FrameLayout mMoreActionsDock;
-        Presenter.ViewHolder mMoreActionsViewHolder;
-        boolean mMoreActionsShowing;
-        final TextView mCurrentTime;
-        final TextView mTotalTime;
-        final ProgressBar mProgressBar;
-        long mCurrentTimeInMs = -1;         // Hold current time in milliseconds
-        long mTotalTimeInMs = -1;           // Hold total time in milliseconds
-        long mSecondaryProgressInMs = -1;   // Hold secondary progress in milliseconds
-        StringBuilder mTotalTimeStringBuilder = new StringBuilder();
-        StringBuilder mCurrentTimeStringBuilder = new StringBuilder();
-        int mCurrentTimeMarginStart;
-        int mTotalTimeMarginEnd;
-
-        ViewHolder(View rootView) {
-            super(rootView);
-            mMoreActionsDock = (FrameLayout) rootView.findViewById(R.id.more_actions_dock);
-            mCurrentTime = (TextView) rootView.findViewById(R.id.current_time);
-            mTotalTime = (TextView) rootView.findViewById(R.id.total_time);
-            mProgressBar = (ProgressBar) rootView.findViewById(R.id.playback_progress);
-            mMoreActionsObserver = new ObjectAdapter.DataObserver() {
-                @Override
-                public void onChanged() {
-                    if (mMoreActionsShowing) {
-                        showControls(mPresenter);
-                    }
-                }
-                @Override
-                public void onItemRangeChanged(int positionStart, int itemCount) {
-                    if (mMoreActionsShowing) {
-                        for (int i = 0; i < itemCount; i++) {
-                            bindControlToAction(positionStart + i, mPresenter);
-                        }
-                    }
-                }
-            };
-            mCurrentTimeMarginStart =
-                    ((MarginLayoutParams) mCurrentTime.getLayoutParams()).getMarginStart();
-            mTotalTimeMarginEnd =
-                    ((MarginLayoutParams) mTotalTime.getLayoutParams()).getMarginEnd();
-        }
-
-        void showMoreActions(boolean show) {
-            if (show) {
-                if (mMoreActionsViewHolder == null) {
-                    Action action = new PlaybackControlsRow.MoreActions(mMoreActionsDock.getContext());
-                    mMoreActionsViewHolder = mPresenter.onCreateViewHolder(mMoreActionsDock);
-                    mPresenter.onBindViewHolder(mMoreActionsViewHolder, action);
-                    mPresenter.setOnClickListener(mMoreActionsViewHolder, new View.OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            toggleMoreActions();
-                        }
-                    });
-                }
-                if (mMoreActionsViewHolder.view.getParent() == null) {
-                    mMoreActionsDock.addView(mMoreActionsViewHolder.view);
-                }
-            } else if (mMoreActionsViewHolder != null
-                    && mMoreActionsViewHolder.view.getParent() != null) {
-                mMoreActionsDock.removeView(mMoreActionsViewHolder.view);
-            }
-        }
-
-        void toggleMoreActions() {
-            mMoreActionsShowing = !mMoreActionsShowing;
-            showControls(mPresenter);
-        }
-
-        @Override
-        ObjectAdapter getDisplayedAdapter() {
-            return mMoreActionsShowing ? mMoreActionsAdapter : mAdapter;
-        }
-
-        @Override
-        int getChildMarginFromCenter(Context context, int numControls) {
-            int margin = getControlIconWidth(context);
-            if (numControls < 4) {
-                margin += getChildMarginBiggest(context);
-            } else if (numControls < 6) {
-                margin += getChildMarginBigger(context);
-            } else {
-                margin += getChildMarginDefault(context);
-            }
-            return margin;
-        }
-
-        void setTotalTime(long totalTimeMs) {
-            if (totalTimeMs <= 0) {
-                mTotalTime.setVisibility(View.GONE);
-                mProgressBar.setVisibility(View.GONE);
-            } else {
-                mTotalTime.setVisibility(View.VISIBLE);
-                mProgressBar.setVisibility(View.VISIBLE);
-                mTotalTimeInMs = totalTimeMs;
-                formatTime(totalTimeMs / 1000, mTotalTimeStringBuilder);
-                mTotalTime.setText(mTotalTimeStringBuilder.toString());
-                mProgressBar.setMax(Integer.MAX_VALUE);//current progress will be a fraction of this
-            }
-        }
-
-        long getTotalTime() {
-            return mTotalTimeInMs;
-        }
-
-        void setCurrentTime(long currentTimeMs) {
-            long seconds = currentTimeMs / 1000;
-            if (currentTimeMs != mCurrentTimeInMs) {
-                mCurrentTimeInMs = currentTimeMs;
-                formatTime(seconds, mCurrentTimeStringBuilder);
-                mCurrentTime.setText(mCurrentTimeStringBuilder.toString());
-            }
-            // Use ratio to represent current progres
-            double ratio = (double) mCurrentTimeInMs / mTotalTimeInMs;     // Range: [0, 1]
-            double progressRatio = ratio * Integer.MAX_VALUE;   // Could safely cast to int
-            mProgressBar.setProgress((int)progressRatio);
-        }
-
-        long getCurrentTime() {
-            return mTotalTimeInMs;
-        }
-
-        void setSecondaryProgress(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);
-        }
-
-        long getSecondaryProgress() {
-            return mSecondaryProgressInMs;
-        }
-    }
-
-    static void formatTime(long seconds, StringBuilder sb) {
-        long minutes = seconds / 60;
-        long hours = minutes / 60;
-        seconds -= minutes * 60;
-        minutes -= hours * 60;
-
-        sb.setLength(0);
-        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);
-    }
-
-    private boolean mMoreActionsEnabled = true;
-    private static int sChildMarginBigger;
-    private static int sChildMarginBiggest;
-
-    /**
-     * Constructor for a PlaybackControlsRowPresenter.
-     *
-     * @param layoutResourceId The resource id of the layout for this presenter.
-     */
-    public PlaybackControlsPresenter(int layoutResourceId) {
-        super(layoutResourceId);
-    }
-
-    /**
-     * Enables the display of secondary actions.
-     * A "more actions" button will be displayed.  When "more actions" is selected,
-     * the primary actions are replaced with the secondary actions.
-     */
-    public void enableSecondaryActions(boolean enable) {
-        mMoreActionsEnabled = enable;
-    }
-
-    /**
-     * Returns true if secondary actions are enabled.
-     */
-    public boolean areMoreActionsEnabled() {
-        return mMoreActionsEnabled;
-    }
-
-    public void setProgressColor(ViewHolder vh, @ColorInt int color) {
-        Drawable drawable = new ClipDrawable(new ColorDrawable(color),
-                Gravity.LEFT, ClipDrawable.HORIZONTAL);
-        ((LayerDrawable) vh.mProgressBar.getProgressDrawable())
-                .setDrawableByLayerId(android.R.id.progress, drawable);
-    }
-
-    public void setTotalTime(ViewHolder vh, int ms) {
-        setTotalTimeLong(vh, (long) ms);
-    }
-
-    public void setTotalTimeLong(ViewHolder vh, long ms) {
-        vh.setTotalTime(ms);
-    }
-
-    public int getTotalTime(ViewHolder vh) {
-        return MathUtil.safeLongToInt(getTotalTimeLong(vh));
-    }
-
-    public long getTotalTimeLong(ViewHolder vh) {
-        return vh.getTotalTime();
-    }
-
-    public void setCurrentTime(ViewHolder vh, int ms) {
-        setCurrentTimeLong(vh, (long) ms);
-    }
-
-    public void setCurrentTimeLong(ViewHolder vh, long ms) {
-        vh.setCurrentTime(ms);
-    }
-
-    public int getCurrentTime(ViewHolder vh) {
-        return MathUtil.safeLongToInt(getCurrentTimeLong(vh));
-    }
-
-    public long getCurrentTimeLong(ViewHolder vh) {
-        return vh.getCurrentTime();
-    }
-
-    public void setSecondaryProgress(ViewHolder vh, int progressMs) {
-        setSecondaryProgressLong(vh, (long) progressMs);
-    }
-
-    public void setSecondaryProgressLong(ViewHolder vh, long progressMs) {
-        vh.setSecondaryProgress(progressMs);
-    }
-
-    public int getSecondaryProgress(ViewHolder vh) {
-        return MathUtil.safeLongToInt(getSecondaryProgressLong(vh));
-    }
-
-    public long getSecondaryProgressLong(ViewHolder vh) {
-        return vh.getSecondaryProgress();
-    }
-
-    public void showPrimaryActions(ViewHolder vh) {
-        if (vh.mMoreActionsShowing) {
-            vh.toggleMoreActions();
-        }
-    }
-
-    public void resetFocus(ViewHolder vh) {
-        vh.mControlBar.requestFocus();
-    }
-
-    public void enableTimeMargins(ViewHolder vh, boolean enable) {
-        MarginLayoutParams lp;
-        lp = (MarginLayoutParams) vh.mCurrentTime.getLayoutParams();
-        lp.setMarginStart(enable ? vh.mCurrentTimeMarginStart : 0);
-        vh.mCurrentTime.setLayoutParams(lp);
-
-        lp = (MarginLayoutParams) vh.mTotalTime.getLayoutParams();
-        lp.setMarginEnd(enable ? vh.mTotalTimeMarginEnd : 0);
-        vh.mTotalTime.setLayoutParams(lp);
-    }
-
-    @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        View v = LayoutInflater.from(parent.getContext())
-            .inflate(getLayoutResourceId(), parent, false);
-        return new ViewHolder(v);
-    }
-
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
-        ViewHolder vh = (ViewHolder) holder;
-        BoundData data = (BoundData) item;
-
-        // If binding to a new adapter, display primary actions.
-        if (vh.mMoreActionsAdapter != data.secondaryActionsAdapter) {
-            vh.mMoreActionsAdapter = data.secondaryActionsAdapter;
-            vh.mMoreActionsAdapter.registerObserver(vh.mMoreActionsObserver);
-            vh.mMoreActionsShowing = false;
-        }
-
-        super.onBindViewHolder(holder, item);
-        vh.showMoreActions(mMoreActionsEnabled);
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
-        super.onUnbindViewHolder(holder);
-        ViewHolder vh = (ViewHolder) holder;
-        if (vh.mMoreActionsAdapter != null) {
-            vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver);
-            vh.mMoreActionsAdapter = null;
-        }
-    }
-
-    int getChildMarginBigger(Context context) {
-        if (sChildMarginBigger == 0) {
-            sChildMarginBigger = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_playback_controls_child_margin_bigger);
-        }
-        return sChildMarginBigger;
-    }
-
-    int getChildMarginBiggest(Context context) {
-        if (sChildMarginBiggest == 0) {
-            sChildMarginBiggest = context.getResources().getDimensionPixelSize(
-                    R.dimen.lb_playback_controls_child_margin_biggest);
-        }
-        return sChildMarginBiggest;
-    }
-}
diff --git a/android/support/v17/leanback/widget/PlaybackControlsRow.java b/android/support/v17/leanback/widget/PlaybackControlsRow.java
deleted file mode 100644
index 5e00d99..0000000
--- a/android/support/v17/leanback/widget/PlaybackControlsRow.java
+++ /dev/null
@@ -1,1083 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-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;
-
-/**
- * A {@link Row} of playback controls to be displayed by a {@link PlaybackControlsRowPresenter}.
- *
- * This row consists of some optional item detail, a series of primary actions,
- * and an optional series of secondary actions.
- *
- * <p>
- * Controls are specified via an {@link ObjectAdapter} containing one or more
- * {@link Action}s.
- * </p>
- * <p>
- * Adapters should have their {@link PresenterSelector} set to an instance of
- * {@link ControlButtonPresenterSelector}.
- * </p>
- */
-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 {
-        private int mIndex;
-        private Drawable[] mDrawables;
-        private String[] mLabels;
-        private String[] mLabels2;
-
-        /**
-         * Constructor
-         * @param id The id of the Action.
-         */
-        public MultiAction(int id) {
-            super(id);
-        }
-
-        /**
-         * Sets the array of drawables.  The size of the array defines the range
-         * of valid indices for this action.
-         */
-        public void setDrawables(Drawable[] drawables) {
-            mDrawables = drawables;
-            setIndex(0);
-        }
-
-        /**
-         * Sets the array of strings used as labels.  The size of the array defines the range
-         * of valid indices for this action.  The labels are used to define the accessibility
-         * content description unless secondary labels are provided.
-         */
-        public void setLabels(String[] labels) {
-            mLabels = labels;
-            setIndex(0);
-        }
-
-        /**
-         * Sets the array of strings used as secondary labels.  These labels are used
-         * in place of the primary labels for accessibility content description only.
-         */
-        public void setSecondaryLabels(String[] labels) {
-            mLabels2 = labels;
-            setIndex(0);
-        }
-
-        /**
-         * Returns the number of actions.
-         */
-        public int getActionCount() {
-            if (mDrawables != null) {
-                return mDrawables.length;
-            }
-            if (mLabels != null) {
-                return mLabels.length;
-            }
-            return 0;
-        }
-
-        /**
-         * Returns the drawable at the given index.
-         */
-        public Drawable getDrawable(int index) {
-            return mDrawables == null ? null : mDrawables[index];
-        }
-
-        /**
-         * Returns the label at the given index.
-         */
-        public String getLabel(int index) {
-            return mLabels == null ? null : mLabels[index];
-        }
-
-        /**
-         * Returns the secondary label at the given index.
-         */
-        public String getSecondaryLabel(int index) {
-            return mLabels2 == null ? null : mLabels2[index];
-        }
-
-        /**
-         * Increments the index, wrapping to zero once the end is reached.
-         */
-        public void nextIndex() {
-            setIndex(mIndex < getActionCount() - 1 ? mIndex + 1 : 0);
-        }
-
-        /**
-         * Sets the current index.
-         */
-        public void setIndex(int index) {
-            mIndex = index;
-            if (mDrawables != null) {
-                setIcon(mDrawables[mIndex]);
-            }
-            if (mLabels != null) {
-                setLabel1(mLabels[mIndex]);
-            }
-            if (mLabels2 != null) {
-                setLabel2(mLabels2[mIndex]);
-            }
-        }
-
-        /**
-         * Returns the current index.
-         */
-        public int getIndex() {
-            return mIndex;
-        }
-    }
-
-    /**
-     * An action displaying icons for play and pause.
-     */
-    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[INDEX_PLAY] = getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_play);
-            drawables[INDEX_PAUSE] = getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_pause);
-            setDrawables(drawables);
-
-            String[] labels = new String[drawables.length];
-            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);
-            addKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
-        }
-    }
-
-    /**
-     * An action displaying an icon for fast forward.
-     */
-    public static class FastForwardAction extends MultiAction {
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         */
-        public FastForwardAction(Context context) {
-            this(context, 1);
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         * @param numSpeeds Number of supported fast forward speeds.
-         */
-        public FastForwardAction(Context context, int numSpeeds) {
-            super(R.id.lb_control_fast_forward);
-
-            if (numSpeeds < 1) {
-                throw new IllegalArgumentException("numSpeeds must be > 0");
-            }
-            Drawable[] drawables = new Drawable[numSpeeds + 1];
-            drawables[0] = getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_fast_forward);
-            setDrawables(drawables);
-
-            String[] labels = new String[getActionCount()];
-            labels[0] = context.getString(R.string.lb_playback_controls_fast_forward);
-
-            String[] labels2 = new String[getActionCount()];
-            labels2[0] = labels[0];
-
-            for (int i = 1; i <= numSpeeds; i++) {
-                int multiplier = i + 1;
-                labels[i] = context.getResources().getString(
-                        R.string.lb_control_display_fast_forward_multiplier, multiplier);
-                labels2[i] = context.getResources().getString(
-                        R.string.lb_playback_controls_fast_forward_multiplier, multiplier);
-            }
-            setLabels(labels);
-            setSecondaryLabels(labels2);
-            addKeyCode(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
-        }
-    }
-
-    /**
-     * An action displaying an icon for rewind.
-     */
-    public static class RewindAction extends MultiAction {
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         */
-        public RewindAction(Context context) {
-            this(context, 1);
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         * @param numSpeeds Number of supported fast forward speeds.
-         */
-        public RewindAction(Context context, int numSpeeds) {
-            super(R.id.lb_control_fast_rewind);
-
-            if (numSpeeds < 1) {
-                throw new IllegalArgumentException("numSpeeds must be > 0");
-            }
-            Drawable[] drawables = new Drawable[numSpeeds + 1];
-            drawables[0] = getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_rewind);
-            setDrawables(drawables);
-
-            String[] labels = new String[getActionCount()];
-            labels[0] = context.getString(R.string.lb_playback_controls_rewind);
-
-            String[] labels2 = new String[getActionCount()];
-            labels2[0] = labels[0];
-
-            for (int i = 1; i <= numSpeeds; i++) {
-                int multiplier = i + 1;
-                labels[i] = labels[i] = context.getResources().getString(
-                        R.string.lb_control_display_rewind_multiplier, multiplier);
-                labels2[i] = context.getResources().getString(
-                        R.string.lb_playback_controls_rewind_multiplier, multiplier);
-            }
-            setLabels(labels);
-            setSecondaryLabels(labels2);
-            addKeyCode(KeyEvent.KEYCODE_MEDIA_REWIND);
-        }
-    }
-
-    /**
-     * An action displaying an icon for skip next.
-     */
-    public static class SkipNextAction extends Action {
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         */
-        public SkipNextAction(Context context) {
-            super(R.id.lb_control_skip_next);
-            setIcon(getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_skip_next));
-            setLabel1(context.getString(R.string.lb_playback_controls_skip_next));
-            addKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
-        }
-    }
-
-    /**
-     * An action displaying an icon for skip previous.
-     */
-    public static class SkipPreviousAction extends Action {
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         */
-        public SkipPreviousAction(Context context) {
-            super(R.id.lb_control_skip_previous);
-            setIcon(getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_skip_previous));
-            setLabel1(context.getString(R.string.lb_playback_controls_skip_previous));
-            addKeyCode(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
-        }
-    }
-
-    /**
-     * An action displaying an icon for picture-in-picture.
-     */
-    public static class PictureInPictureAction extends Action {
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         */
-        public PictureInPictureAction(Context context) {
-            super(R.id.lb_control_picture_in_picture);
-            setIcon(getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_picture_in_picture));
-            setLabel1(context.getString(R.string.lb_playback_controls_picture_in_picture));
-            addKeyCode(KeyEvent.KEYCODE_WINDOW);
-        }
-    }
-
-    /**
-     * An action displaying an icon for "more actions".
-     */
-    public static class MoreActions extends Action {
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         */
-        public MoreActions(Context context) {
-            super(R.id.lb_control_more_actions);
-            setIcon(context.getResources().getDrawable(R.drawable.lb_ic_more));
-            setLabel1(context.getString(R.string.lb_playback_controls_more_actions));
-        }
-    }
-
-    /**
-     * A base class for displaying a thumbs action.
-     */
-    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[INDEX_SOLID] = getStyledDrawable(context, solidIconIndex);
-            drawables[INDEX_OUTLINE] = getStyledDrawable(context, outlineIconIndex);
-            setDrawables(drawables);
-        }
-    }
-
-    /**
-     * An action displaying an icon for thumbs up.
-     */
-    public static class ThumbsUpAction extends ThumbsAction {
-        public ThumbsUpAction(Context context) {
-            super(R.id.lb_control_thumbs_up, context,
-                    R.styleable.lbPlaybackControlsActionIcons_thumb_up,
-                    R.styleable.lbPlaybackControlsActionIcons_thumb_up_outline);
-            String[] labels = new String[getActionCount()];
-            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);
-        }
-    }
-
-    /**
-     * An action displaying an icon for thumbs down.
-     */
-    public static class ThumbsDownAction extends ThumbsAction {
-        public ThumbsDownAction(Context context) {
-            super(R.id.lb_control_thumbs_down, context,
-                    R.styleable.lbPlaybackControlsActionIcons_thumb_down,
-                    R.styleable.lbPlaybackControlsActionIcons_thumb_down_outline);
-            String[] labels = new String[getActionCount()];
-            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);
-        }
-    }
-
-    /**
-     * An action for displaying three repeat states: none, one, or all.
-     */
-    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.
-         */
-        public RepeatAction(Context context) {
-            this(context, getIconHighlightColor(context));
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources
-         * @param highlightColor Color to display the repeat-all and repeat0one icons.
-         */
-        public RepeatAction(Context context, int highlightColor) {
-            this(context, highlightColor, highlightColor);
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources
-         * @param repeatAllColor Color to display the repeat-all icon.
-         * @param repeatOneColor Color to display the repeat-one icon.
-         */
-        public RepeatAction(Context context, int repeatAllColor, int repeatOneColor) {
-            super(R.id.lb_control_repeat);
-            Drawable[] drawables = new Drawable[3];
-            BitmapDrawable repeatDrawable = (BitmapDrawable) getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_repeat);
-            BitmapDrawable repeatOneDrawable = (BitmapDrawable) getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_repeat_one);
-            drawables[INDEX_NONE] = repeatDrawable;
-            drawables[INDEX_ALL] = repeatDrawable == null ? null
-                    : new BitmapDrawable(context.getResources(),
-                            createBitmap(repeatDrawable.getBitmap(), repeatAllColor));
-            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[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);
-        }
-    }
-
-    /**
-     * 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.
-         */
-        public ShuffleAction(Context context) {
-            this(context, getIconHighlightColor(context));
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         * @param highlightColor Color for the highlighted icon state.
-         */
-        public ShuffleAction(Context context, int highlightColor) {
-            super(R.id.lb_control_shuffle);
-            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_shuffle);
-            Drawable[] drawables = new Drawable[2];
-            drawables[INDEX_OFF] = uncoloredDrawable;
-            drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
-                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
-            setDrawables(drawables);
-
-            String[] labels = new String[drawables.length];
-            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);
-        }
-    }
-
-    /**
-     * 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.
-         */
-        public HighQualityAction(Context context) {
-            this(context, getIconHighlightColor(context));
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         * @param highlightColor Color for the highlighted icon state.
-         */
-        public HighQualityAction(Context context, int highlightColor) {
-            super(R.id.lb_control_high_quality);
-            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_high_quality);
-            Drawable[] drawables = new Drawable[2];
-            drawables[INDEX_OFF] = uncoloredDrawable;
-            drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
-                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
-            setDrawables(drawables);
-
-            String[] labels = new String[drawables.length];
-            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);
-        }
-    }
-
-    /**
-     * 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.
-         */
-        public ClosedCaptioningAction(Context context) {
-            this(context, getIconHighlightColor(context));
-        }
-
-        /**
-         * Constructor
-         * @param context Context used for loading resources.
-         * @param highlightColor Color for the highlighted icon state.
-         */
-        public ClosedCaptioningAction(Context context, int highlightColor) {
-            super(R.id.lb_control_closed_captioning);
-            BitmapDrawable uncoloredDrawable = (BitmapDrawable) getStyledDrawable(context,
-                    R.styleable.lbPlaybackControlsActionIcons_closed_captioning);
-            Drawable[] drawables = new Drawable[2];
-            drawables[INDEX_OFF] = uncoloredDrawable;
-            drawables[INDEX_ON] = new BitmapDrawable(context.getResources(),
-                    createBitmap(uncoloredDrawable.getBitmap(), highlightColor));
-            setDrawables(drawables);
-
-            String[] labels = new String[drawables.length];
-            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);
-        }
-    }
-
-    static Bitmap createBitmap(Bitmap bitmap, int color) {
-        Bitmap dst = bitmap.copy(bitmap.getConfig(), true);
-        Canvas canvas = new Canvas(dst);
-        Paint paint = new Paint();
-        paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
-        canvas.drawBitmap(bitmap, 0, 0, paint);
-        return dst;
-    }
-
-    static int getIconHighlightColor(Context context) {
-        TypedValue outValue = new TypedValue();
-        if (context.getTheme().resolveAttribute(R.attr.playbackControlsIconHighlightColor,
-                outValue, true)) {
-            return outValue.data;
-        }
-        return context.getResources().getColor(R.color.lb_playback_icon_highlight_no_theme);
-    }
-
-    static Drawable getStyledDrawable(Context context, int index) {
-        TypedValue outValue = new TypedValue();
-        if (!context.getTheme().resolveAttribute(
-                R.attr.playbackControlsActionIcons, outValue, false)) {
-            return null;
-        }
-        TypedArray array = context.getTheme().obtainStyledAttributes(outValue.data,
-                R.styleable.lbPlaybackControlsActionIcons);
-        Drawable drawable = array.getDrawable(index);
-        array.recycle();
-        return drawable;
-    }
-
-    private Object mItem;
-    private Drawable mImageDrawable;
-    private ObjectAdapter mPrimaryActionsAdapter;
-    private ObjectAdapter mSecondaryActionsAdapter;
-    private long mTotalTimeMs;
-    private long mCurrentTimeMs;
-    private long mBufferedProgressMs;
-    private OnPlaybackProgressCallback mListener;
-
-    /**
-     * Constructor for a PlaybackControlsRow that displays some details from
-     * the given item.
-     *
-     * @param item The main item for the row.
-     */
-    public PlaybackControlsRow(Object item) {
-        mItem = item;
-    }
-
-    /**
-     * Constructor for a PlaybackControlsRow that has no item details.
-     */
-    public PlaybackControlsRow() {
-    }
-
-    /**
-     * Returns the main item for the details page.
-     */
-    public final Object getItem() {
-        return mItem;
-    }
-
-    /**
-     * Sets a {link @Drawable} image for this row.
-     * <p>If set after the row has been bound to a view, the adapter must be notified that
-     * this row has changed.</p>
-     *
-     * @param drawable The drawable to set.
-     */
-    public final void setImageDrawable(Drawable drawable) {
-        mImageDrawable = drawable;
-    }
-
-    /**
-     * Sets a {@link Bitmap} for this row.
-     * <p>If set after the row has been bound to a view, the adapter must be notified that
-     * this row has changed.</p>
-     *
-     * @param context The context to retrieve display metrics from.
-     * @param bm The bitmap to set.
-     */
-    public final void setImageBitmap(Context context, Bitmap bm) {
-        mImageDrawable = new BitmapDrawable(context.getResources(), bm);
-    }
-
-    /**
-     * Returns the image {@link Drawable} of this row.
-     *
-     * @return The overview's image drawable, or null if no drawable has been
-     *         assigned.
-     */
-    public final Drawable getImageDrawable() {
-        return mImageDrawable;
-    }
-
-    /**
-     * Sets the primary actions {@link ObjectAdapter}.
-     * <p>If set after the row has been bound to a view, the adapter must be notified that
-     * this row has changed.</p>
-     */
-    public final void setPrimaryActionsAdapter(ObjectAdapter adapter) {
-        mPrimaryActionsAdapter = adapter;
-    }
-
-    /**
-     * Sets the secondary actions {@link ObjectAdapter}.
-     * <p>If set after the row has been bound to a view, the adapter must be notified that
-     * this row has changed.</p>
-     */
-    public final void setSecondaryActionsAdapter(ObjectAdapter adapter) {
-        mSecondaryActionsAdapter = adapter;
-    }
-
-    /**
-     * Returns the primary actions {@link ObjectAdapter}.
-     */
-    public final ObjectAdapter getPrimaryActionsAdapter() {
-        return mPrimaryActionsAdapter;
-    }
-
-    /**
-     * Returns the secondary actions {@link ObjectAdapter}.
-     */
-    public final ObjectAdapter getSecondaryActionsAdapter() {
-        return mSecondaryActionsAdapter;
-    }
-
-    /**
-     * 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) {
-        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) {
-        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);
-    }
-
-    /**
-     * 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;
-            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) {
-        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 setBufferedPosition(long ms) {
-        if (mBufferedProgressMs != ms) {
-            mBufferedProgressMs = ms;
-            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(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 getBufferedPosition() {
-        return mBufferedProgressMs;
-    }
-
-    /**
-     * Returns the Action associated with the given keycode, or null if no associated action exists.
-     * Searches the primary adapter first, then the secondary adapter.
-     */
-    public Action getActionForKeyCode(int keyCode) {
-        Action action = getActionForKeyCode(getPrimaryActionsAdapter(), keyCode);
-        if (action != null) {
-            return action;
-        }
-        return getActionForKeyCode(getSecondaryActionsAdapter(), keyCode);
-    }
-
-    /**
-     * Returns the Action associated with the given keycode, or null if no associated action exists.
-     */
-    public Action getActionForKeyCode(ObjectAdapter adapter, int keyCode) {
-        if (adapter != mPrimaryActionsAdapter && adapter != mSecondaryActionsAdapter) {
-            throw new IllegalArgumentException("Invalid adapter");
-        }
-        for (int i = 0; i < adapter.size(); i++) {
-            Action action = (Action) adapter.get(i);
-            if (action.respondsToKeyCode(keyCode)) {
-                return action;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Sets a listener to be called when the playback state changes.
-     */
-    public void setOnPlaybackProgressChangedListener(OnPlaybackProgressCallback listener) {
-        mListener = listener;
-    }
-}
diff --git a/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java b/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
deleted file mode 100644
index 000db3c..0000000
--- a/android/support/v17/leanback/widget/PlaybackControlsRowPresenter.java
+++ /dev/null
@@ -1,487 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Color;
-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.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-/**
- * A PlaybackControlsRowPresenter 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.PlaybackFragment}.
- *
- * <p>The detailed description is rendered using a {@link Presenter} passed in
- * {@link #PlaybackControlsRowPresenter(Presenter)}.  Typically this will be an instance of
- * {@link AbstractDetailsDescriptionPresenter}.  The application can access the
- * detailed description ViewHolder from {@link ViewHolder#mDescriptionViewHolder}.
- * </p>
- */
-public class PlaybackControlsRowPresenter extends PlaybackRowPresenter {
-
-    static class BoundData extends PlaybackControlsPresenter.BoundData {
-        ViewHolder mRowViewHolder;
-    }
-
-    /**
-     * A ViewHolder for the PlaybackControlsRow.
-     */
-    public class ViewHolder extends PlaybackRowPresenter.ViewHolder {
-        public final Presenter.ViewHolder mDescriptionViewHolder;
-        final ViewGroup mCard;
-        final ViewGroup mCardRightPanel;
-        final ImageView mImageView;
-        final ViewGroup mDescriptionDock;
-        final ViewGroup mControlsDock;
-        final ViewGroup mSecondaryControlsDock;
-        final View mSpacer;
-        final View mBottomSpacer;
-        View mBgView;
-        int mControlsDockMarginStart;
-        int mControlsDockMarginEnd;
-        PlaybackControlsPresenter.ViewHolder mControlsVh;
-        Presenter.ViewHolder mSecondaryControlsVh;
-        BoundData mControlsBoundData = new BoundData();
-        BoundData mSecondaryBoundData = new BoundData();
-        Presenter.ViewHolder mSelectedViewHolder;
-        Object mSelectedItem;
-        final PlaybackControlsRow.OnPlaybackProgressCallback mListener =
-                new PlaybackControlsRow.OnPlaybackProgressCallback() {
-            @Override
-            public void onCurrentPositionChanged(PlaybackControlsRow row, long ms) {
-                mPlaybackControlsPresenter.setCurrentTimeLong(mControlsVh, ms);
-            }
-
-            @Override
-            public void onDurationChanged(PlaybackControlsRow row, long ms) {
-                mPlaybackControlsPresenter.setTotalTimeLong(mControlsVh, ms);
-            }
-
-            @Override
-            public void onBufferedPositionChanged(PlaybackControlsRow row, long ms) {
-                mPlaybackControlsPresenter.setSecondaryProgressLong(mControlsVh, ms);
-            }
-        };
-
-        ViewHolder(View rootView, Presenter descriptionPresenter) {
-            super(rootView);
-            mCard = (ViewGroup) rootView.findViewById(R.id.controls_card);
-            mCardRightPanel = (ViewGroup) rootView.findViewById(R.id.controls_card_right_panel);
-            mImageView = (ImageView) rootView.findViewById(R.id.image);
-            mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
-            mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
-            mSecondaryControlsDock =
-                    (ViewGroup) rootView.findViewById(R.id.secondary_controls_dock);
-            mSpacer = rootView.findViewById(R.id.spacer);
-            mBottomSpacer = rootView.findViewById(R.id.bottom_spacer);
-            mDescriptionViewHolder = descriptionPresenter == null ? null :
-                    descriptionPresenter.onCreateViewHolder(mDescriptionDock);
-            if (mDescriptionViewHolder != null) {
-                mDescriptionDock.addView(mDescriptionViewHolder.view);
-            }
-        }
-
-        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 primary ? selector.getPrimaryPresenter()
-                        : selector.getSecondaryPresenter();
-            }
-            return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null);
-        }
-
-        void setOutline(View view) {
-            if (mBgView != null) {
-                RoundedRectHelper.getInstance().setClipToRoundedOutline(mBgView, false);
-                ShadowHelper.getInstance().setZ(mBgView, 0);
-            }
-            mBgView = view;
-            RoundedRectHelper.getInstance().setClipToRoundedOutline(view, true);
-
-            if (sShadowZ == 0) {
-                sShadowZ = view.getResources().getDimensionPixelSize(
-                        R.dimen.lb_playback_controls_z);
-            }
-            ShadowHelper.getInstance().setZ(view, sShadowZ);
-        }
-    }
-
-    private int mBackgroundColor = Color.TRANSPARENT;
-    private boolean mBackgroundColorSet;
-    private int mProgressColor = Color.TRANSPARENT;
-    private boolean mProgressColorSet;
-    private boolean mSecondaryActionsHidden;
-    private Presenter mDescriptionPresenter;
-    PlaybackControlsPresenter mPlaybackControlsPresenter;
-    private ControlBarPresenter mSecondaryControlsPresenter;
-    OnActionClickedListener mOnActionClickedListener;
-    static float sShadowZ;
-
-    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);
-            }
-        }
-    };
-
-    /**
-     * Constructor for a PlaybackControlsRowPresenter.
-     *
-     * @param descriptionPresenter Presenter for displaying item details.
-     */
-    public PlaybackControlsRowPresenter(Presenter descriptionPresenter) {
-        setHeaderPresenter(null);
-        setSelectEffectEnabled(false);
-
-        mDescriptionPresenter = descriptionPresenter;
-        mPlaybackControlsPresenter = new PlaybackControlsPresenter(R.layout.lb_playback_controls);
-        mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
-
-        mPlaybackControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
-        mSecondaryControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
-        mPlaybackControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
-        mSecondaryControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
-    }
-
-    /**
-     * Constructor for a PlaybackControlsRowPresenter.
-     */
-    public PlaybackControlsRowPresenter() {
-        this(null);
-    }
-
-    /**
-     * 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 background color.  If not set, a default from the theme will be used.
-     */
-    public void setBackgroundColor(@ColorInt int color) {
-        mBackgroundColor = color;
-        mBackgroundColorSet = true;
-    }
-
-    /**
-     * Returns the background color.  If no background color was set, transparent
-     * is returned.
-     */
-    @ColorInt
-    public int getBackgroundColor() {
-        return mBackgroundColor;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Sets the secondary actions to be hidden behind a "more actions" button.
-     * When "more actions" is selected, the primary actions are replaced with
-     * the secondary actions.
-     */
-    public void setSecondaryActionsHidden(boolean hidden) {
-        mSecondaryActionsHidden = hidden;
-    }
-
-    /**
-     * Returns true if secondary actions are hidden.
-     */
-    public boolean areSecondaryActionsHidden() {
-        return mSecondaryActionsHidden;
-    }
-
-    /**
-     * Shows or hides space at the bottom of the playback controls row.
-     * This allows the row to hug the bottom of the display when no
-     * other rows are present.
-     */
-    public void showBottomSpace(ViewHolder vh, boolean show) {
-        vh.mBottomSpacer.setVisibility(show ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Displays the primary actions.  This will override the user having selected "more actions"
-     * to display the secondary actions; see {@link #setSecondaryActionsHidden(boolean)}.
-     */
-    public void showPrimaryActions(ViewHolder vh) {
-        mPlaybackControlsPresenter.showPrimaryActions(vh.mControlsVh);
-        if (vh.view.hasFocus()) {
-            mPlaybackControlsPresenter.resetFocus(vh.mControlsVh);
-        }
-    }
-
-    @Override
-    public void onReappear(RowPresenter.ViewHolder rowViewHolder) {
-        showPrimaryActions((ViewHolder) rowViewHolder);
-    }
-
-    private int getDefaultBackgroundColor(Context context) {
-        TypedValue outValue = new TypedValue();
-        if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
-            return context.getResources().getColor(outValue.resourceId);
-        }
-        return context.getResources().getColor(R.color.lb_default_brand_color);
-    }
-
-    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_controls_row, parent, false);
-        ViewHolder vh = new ViewHolder(v, mDescriptionPresenter);
-        initRow(vh);
-        return vh;
-    }
-
-    private void initRow(final ViewHolder vh) {
-        MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
-        vh.mControlsDockMarginStart = lp.getMarginStart();
-        vh.mControlsDockMarginEnd = lp.getMarginEnd();
-
-        vh.mControlsVh = (PlaybackControlsPresenter.ViewHolder)
-                mPlaybackControlsPresenter.onCreateViewHolder(vh.mControlsDock);
-        mPlaybackControlsPresenter.setProgressColor(vh.mControlsVh, mProgressColorSet
-                ? mProgressColor : getDefaultProgressColor(vh.mControlsDock.getContext()));
-        mPlaybackControlsPresenter.setBackgroundColor(vh.mControlsVh, mBackgroundColorSet
-                ? mBackgroundColor : getDefaultBackgroundColor(vh.view.getContext()));
-        vh.mControlsDock.addView(vh.mControlsVh.view);
-
-        vh.mSecondaryControlsVh =
-                mSecondaryControlsPresenter.onCreateViewHolder(vh.mSecondaryControlsDock);
-        if (!mSecondaryActionsHidden) {
-            vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
-        }
-        ((PlaybackControlsRowView) vh.view).setOnUnhandledKeyListener(
-                new PlaybackControlsRowView.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();
-
-        mPlaybackControlsPresenter.enableSecondaryActions(mSecondaryActionsHidden);
-
-        if (row.getItem() == null) {
-            vh.mDescriptionDock.setVisibility(View.GONE);
-            vh.mSpacer.setVisibility(View.GONE);
-        } else {
-            vh.mDescriptionDock.setVisibility(View.VISIBLE);
-            if (vh.mDescriptionViewHolder != null) {
-                mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem());
-            }
-            vh.mSpacer.setVisibility(View.VISIBLE);
-        }
-
-        if (row.getImageDrawable() == null || row.getItem() == null) {
-            vh.mImageView.setImageDrawable(null);
-            updateCardLayout(vh, LayoutParams.WRAP_CONTENT);
-        } else {
-            vh.mImageView.setImageDrawable(row.getImageDrawable());
-            updateCardLayout(vh, vh.mImageView.getLayoutParams().height);
-        }
-
-        vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
-        vh.mControlsBoundData.secondaryActionsAdapter = row.getSecondaryActionsAdapter();
-        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);
-
-        mPlaybackControlsPresenter.setTotalTime(vh.mControlsVh, row.getTotalTime());
-        mPlaybackControlsPresenter.setCurrentTime(vh.mControlsVh, row.getCurrentTime());
-        mPlaybackControlsPresenter.setSecondaryProgress(vh.mControlsVh, row.getBufferedProgress());
-        row.setOnPlaybackProgressChangedListener(vh.mListener);
-    }
-
-    private void updateCardLayout(ViewHolder vh, int height) {
-        LayoutParams lp = vh.mCardRightPanel.getLayoutParams();
-        lp.height = height;
-        vh.mCardRightPanel.setLayoutParams(lp);
-
-        MarginLayoutParams mlp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
-        LinearLayout.LayoutParams llp =
-                (LinearLayout.LayoutParams) vh.mDescriptionDock.getLayoutParams();
-
-        if (height == LayoutParams.WRAP_CONTENT) {
-            llp.height = LayoutParams.WRAP_CONTENT;
-            mlp.setMarginStart(0);
-            mlp.setMarginEnd(0);
-            vh.mCard.setBackground(null);
-            vh.setOutline(vh.mControlsDock);
-            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, true);
-        } else {
-            llp.height = 0;
-            llp.weight = 1;
-            mlp.setMarginStart(vh.mControlsDockMarginStart);
-            mlp.setMarginEnd(vh.mControlsDockMarginEnd);
-            vh.mCard.setBackgroundColor(mBackgroundColorSet ? mBackgroundColor :
-                    getDefaultBackgroundColor(vh.mCard.getContext()));
-            vh.setOutline(vh.mCard);
-            mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, false);
-        }
-        vh.mDescriptionDock.setLayoutParams(llp);
-        vh.mControlsDock.setLayoutParams(mlp);
-    }
-
-    @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);
-    }
-
-    @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/android/support/v17/leanback/widget/PlaybackControlsRowView.java b/android/support/v17/leanback/widget/PlaybackControlsRowView.java
deleted file mode 100644
index 1d0fce0..0000000
--- a/android/support/v17/leanback/widget/PlaybackControlsRowView.java
+++ /dev/null
@@ -1,76 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * A LinearLayout that preserves the focused child view.
- */
-class PlaybackControlsRowView extends LinearLayout {
-    public interface OnUnhandledKeyListener {
-        /**
-         * Returns true if the key event should be consumed.
-         */
-        public boolean onUnhandledKey(KeyEvent event);
-    }
-
-    private OnUnhandledKeyListener mOnUnhandledKeyListener;
-
-    public PlaybackControlsRowView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public PlaybackControlsRowView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
-         mOnUnhandledKeyListener = listener;
-    }
-
-    public 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;
-        }
-        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/PlaybackRowPresenter.java b/android/support/v17/leanback/widget/PlaybackRowPresenter.java
deleted file mode 100644
index bb1edb3..0000000
--- a/android/support/v17/leanback/widget/PlaybackRowPresenter.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.view.View;
-
-/**
- * Subclass of {@link RowPresenter} that can define the desired behavior when the view
- * reappears. This is presently used by {@link PlaybackControlsRowPresenter} to update the UI
- * after we show/hide the controls view.
- */
-public abstract class PlaybackRowPresenter extends RowPresenter {
-
-    /**
-     * This container is used for trapping click events and passing them to the
-     * playback controls.
-     */
-    public static class ViewHolder extends RowPresenter.ViewHolder {
-        public ViewHolder(View view) {
-            super(view);
-        }
-    }
-
-    /**
-     * Provides hook to update the UI when the view reappears.
-     */
-    public void onReappear(RowPresenter.ViewHolder rowViewHolder) {
-    }
-}
diff --git a/android/support/v17/leanback/widget/PlaybackSeekDataProvider.java b/android/support/v17/leanback/widget/PlaybackSeekDataProvider.java
deleted file mode 100644
index 95493b3..0000000
--- a/android/support/v17/leanback/widget/PlaybackSeekDataProvider.java
+++ /dev/null
@@ -1,76 +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.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/android/support/v17/leanback/widget/PlaybackSeekUi.java b/android/support/v17/leanback/widget/PlaybackSeekUi.java
deleted file mode 100644
index 3000498..0000000
--- a/android/support/v17/leanback/widget/PlaybackSeekUi.java
+++ /dev/null
@@ -1,84 +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.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/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java b/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
deleted file mode 100644
index 4505944..0000000
--- a/android/support/v17/leanback/widget/PlaybackTransportRowPresenter.java
+++ /dev/null
@@ -1,801 +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.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/android/support/v17/leanback/widget/PlaybackTransportRowView.java b/android/support/v17/leanback/widget/PlaybackTransportRowView.java
deleted file mode 100644
index 2af7ff4..0000000
--- a/android/support/v17/leanback/widget/PlaybackTransportRowView.java
+++ /dev/null
@@ -1,125 +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.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/android/support/v17/leanback/widget/Presenter.java b/android/support/v17/leanback/widget/Presenter.java
deleted file mode 100644
index f711c67..0000000
--- a/android/support/v17/leanback/widget/Presenter.java
+++ /dev/null
@@ -1,226 +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.v17.leanback.widget;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A Presenter is used to generate {@link View}s and bind Objects to them on
- * demand. It is closely related to the concept of an {@link
- * android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, but is
- * not position-based.  The leanback framework implements the adapter concept using
- * {@link ObjectAdapter} which refers to a Presenter (or {@link PresenterSelector}) instance.
- *
- * <p>
- * Presenters should be stateless.  Presenters typically extend {@link ViewHolder} to store all
- * necessary view state information, such as references to child views to be used when
- * binding to avoid expensive calls to {@link View#findViewById(int)}.
- * </p>
- *
- * <p>
- * A trivial Presenter that takes a string and renders it into a {@link
- * android.widget.TextView TextView}:
- *
- * <pre class="prettyprint">
- * public class StringTextViewPresenter extends Presenter {
- *     // This class does not need a custom ViewHolder, since it does not use
- *     // a complex layout.
- *
- *     {@literal @}Override
- *     public ViewHolder onCreateViewHolder(ViewGroup parent) {
- *         return new ViewHolder(new TextView(parent.getContext()));
- *     }
- *
- *     {@literal @}Override
- *     public void onBindViewHolder(ViewHolder viewHolder, Object item) {
- *         String str = (String) item;
- *         TextView textView = (TextView) viewHolder.mView;
- *
- *         textView.setText(item);
- *     }
- *
- *     {@literal @}Override
- *     public void onUnbindViewHolder(ViewHolder viewHolder) {
- *         // Nothing to unbind for TextView, but if this viewHolder had
- *         // allocated bitmaps, they can be released here.
- *     }
- * }
- * </pre>
- * In addition to view creation and binding, Presenter allows dynamic interface (facet) to
- * be added: {@link #setFacet(Class, Object)}.  Supported facets:
- * <li> {@link ItemAlignmentFacet} is used by {@link HorizontalGridView} and
- * {@link VerticalGridView} to customize child alignment.
- */
-public abstract class Presenter implements FacetProvider {
-    /**
-     * ViewHolder can be subclassed and used to cache any view accessors needed
-     * to improve binding performance (for example, results of findViewById)
-     * without needing to subclass a View.
-     */
-    public static class ViewHolder implements FacetProvider {
-        public final View view;
-        private Map<Class, Object> mFacets;
-
-        public ViewHolder(View view) {
-            this.view = view;
-        }
-
-        @Override
-        public final Object getFacet(Class<?> facetClass) {
-            if (mFacets == null) {
-                return null;
-            }
-            return mFacets.get(facetClass);
-        }
-
-        /**
-         * Sets dynamic implemented facet in addition to basic ViewHolder functions.
-         * @param facetClass   Facet classes to query,  can be class of {@link ItemAlignmentFacet}.
-         * @param facetImpl  Facet implementation.
-         */
-        public final void setFacet(Class<?> facetClass, Object facetImpl) {
-            if (mFacets == null) {
-                mFacets = new HashMap<Class, Object>();
-            }
-            mFacets.put(facetClass, facetImpl);
-        }
-    }
-
-    /**
-     * Base class to perform a task on Presenter.ViewHolder.
-     */
-    public static abstract class ViewHolderTask {
-        /**
-         * Called to perform a task on view holder.
-         * @param holder The view holder to perform task.
-         */
-        public void run(Presenter.ViewHolder holder) {
-        }
-    }
-
-    private Map<Class, Object> mFacets;
-
-    /**
-     * Creates a new {@link View}.
-     */
-    public abstract ViewHolder onCreateViewHolder(ViewGroup parent);
-
-    /**
-     * Binds a {@link View} to an item.
-     */
-    public abstract void onBindViewHolder(ViewHolder viewHolder, Object item);
-
-    /**
-     * Binds a {@link View} to an item with a list of payloads.
-     * @param viewHolder  The ViewHolder which should be updated to represent the contents of the
-     *                    item at the given position in the data set.
-     * @param item        The item which should be bound to view holder.
-     * @param payloads    A non-null list of merged payloads. Can be empty list if requires full
-     *                    update.
-     */
-    public void onBindViewHolder(ViewHolder viewHolder, Object item, List<Object> payloads) {
-        onBindViewHolder(viewHolder, item);
-    }
-
-    /**
-     * Unbinds a {@link View} from an item. Any expensive references may be
-     * released here, and any fields that are not bound for every item should be
-     * cleared here.
-     */
-    public abstract void onUnbindViewHolder(ViewHolder viewHolder);
-
-    /**
-     * Called when a view created by this presenter has been attached to a window.
-     *
-     * <p>This can be used as a reasonable signal that the view is about to be seen
-     * by the user. If the adapter previously freed any resources in
-     * {@link #onViewDetachedFromWindow(ViewHolder)}
-     * those resources should be restored here.</p>
-     *
-     * @param holder Holder of the view being attached
-     */
-    public void onViewAttachedToWindow(ViewHolder holder) {
-    }
-
-    /**
-     * Called when a view created by this presenter has been detached from its window.
-     *
-     * <p>Becoming detached from the window is not necessarily a permanent condition;
-     * the consumer of an presenter's views may choose to cache views offscreen while they
-     * are not visible, attaching and detaching them as appropriate.</p>
-     *
-     * Any view property animations should be cancelled here or the view may fail
-     * to be recycled.
-     *
-     * @param holder Holder of the view being detached
-     */
-    public void onViewDetachedFromWindow(ViewHolder holder) {
-        // If there are view property animations running then RecyclerView won't recycle.
-        cancelAnimationsRecursive(holder.view);
-    }
-
-    /**
-     * Utility method for removing all running animations on a view.
-     */
-    protected static void cancelAnimationsRecursive(View view) {
-        if (view != null && view.hasTransientState()) {
-            view.animate().cancel();
-            if (view instanceof ViewGroup) {
-                final int count = ((ViewGroup) view).getChildCount();
-                for (int i = 0; view.hasTransientState() && i < count; i++) {
-                    cancelAnimationsRecursive(((ViewGroup) view).getChildAt(i));
-                }
-            }
-        }
-    }
-
-    /**
-     * Called to set a click listener for the given view holder.
-     *
-     * The default implementation sets the click listener on the root view in the view holder.
-     * If the root view isn't focusable this method should be overridden to set the listener
-     * on the appropriate focusable child view(s).
-     *
-     * @param holder The view holder containing the view(s) on which the listener should be set.
-     * @param listener The click listener to be set.
-     */
-    public void setOnClickListener(ViewHolder holder, View.OnClickListener listener) {
-        holder.view.setOnClickListener(listener);
-    }
-
-    @Override
-    public final Object getFacet(Class<?> facetClass) {
-        if (mFacets == null) {
-            return null;
-        }
-        return mFacets.get(facetClass);
-    }
-
-    /**
-     * Sets dynamic implemented facet in addition to basic Presenter functions.
-     * @param facetClass   Facet classes to query,  can be class of {@link ItemAlignmentFacet}.
-     * @param facetImpl  Facet implementation.
-     */
-    public final void setFacet(Class<?> facetClass, Object facetImpl) {
-        if (mFacets == null) {
-            mFacets = new HashMap<Class, Object>();
-        }
-        mFacets.put(facetClass, facetImpl);
-    }
-}
diff --git a/android/support/v17/leanback/widget/PresenterSelector.java b/android/support/v17/leanback/widget/PresenterSelector.java
deleted file mode 100644
index 8fdc37a..0000000
--- a/android/support/v17/leanback/widget/PresenterSelector.java
+++ /dev/null
@@ -1,33 +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.v17.leanback.widget;
-
-/**
- * A PresenterSelector is used to obtain a {@link Presenter} for a given Object.
- * Similar to {@link Presenter},  PresenterSelector is stateless.
- */
-public abstract class PresenterSelector {
-    /**
-     * Returns a presenter for the given item.
-     */
-    public abstract Presenter getPresenter(Object item);
-
-    /**
-     * Returns an array of all possible presenters.  The returned array should
-     * not be modified.
-     */
-    public Presenter[] getPresenters() {
-        return null;
-    }
-}
diff --git a/android/support/v17/leanback/widget/PresenterSwitcher.java b/android/support/v17/leanback/widget/PresenterSwitcher.java
deleted file mode 100644
index a412b8b..0000000
--- a/android/support/v17/leanback/widget/PresenterSwitcher.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.v17.leanback.widget;
-
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * An abstract helper class that switches a view in its parent view using a
- * {@link PresenterSelector}.  A subclass should implement {@link #insertView(View)} to define
- * how to add the view in parent, and may optionally override {@link #onViewSelected(View)}.
- */
-public abstract class PresenterSwitcher {
-
-    private ViewGroup mParent;
-    private PresenterSelector mPresenterSelector;
-    private Presenter mCurrentPresenter;
-    private Presenter.ViewHolder mCurrentViewHolder;
-
-    /**
-     * Initializes the switcher with a parent view to insert view into and a
-     * {@link PresenterSelector} for choosing a {@link Presenter} for a given object.
-     * This will destroy any existing views.
-     */
-    public void init(ViewGroup parent, PresenterSelector presenterSelector) {
-        clear();
-        mParent = parent;
-        mPresenterSelector = presenterSelector;
-    }
-
-    /**
-     * Selects a view based on the given object and shows that view.
-     */
-    public void select(Object object) {
-        switchView(object);
-        showView(true);
-    }
-
-    /**
-     * Hides the view.
-     */
-    public void unselect() {
-        showView(false);
-    }
-
-    /**
-     * Returns the parent.
-     */
-    public final ViewGroup getParentViewGroup() {
-        return mParent;
-    }
-
-    private void showView(boolean show) {
-        if (mCurrentViewHolder != null) {
-            showView(mCurrentViewHolder.view, show);
-        }
-    }
-
-    private void switchView(Object object) {
-        Presenter presenter = mPresenterSelector.getPresenter(object);
-        if (presenter != mCurrentPresenter) {
-            showView(false);
-            clear();
-            mCurrentPresenter = presenter;
-            if (mCurrentPresenter == null) {
-                return;
-            }
-            mCurrentViewHolder = mCurrentPresenter.onCreateViewHolder(mParent);
-            insertView(mCurrentViewHolder.view);
-        } else {
-            if (mCurrentPresenter == null) {
-                return;
-            }
-            mCurrentPresenter.onUnbindViewHolder(mCurrentViewHolder);
-        }
-        mCurrentPresenter.onBindViewHolder(mCurrentViewHolder, object);
-        onViewSelected(mCurrentViewHolder.view);
-    }
-
-    protected abstract void insertView(View view);
-
-    /**
-     * Called when a view is bound to the object of {@link #select(Object)}.
-     */
-    protected void onViewSelected(View view) {
-    }
-
-    protected void showView(View view, boolean visible) {
-        view.setVisibility(visible ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Destroys created views.
-     */
-    public void clear() {
-        if (mCurrentPresenter != null) {
-            mCurrentPresenter.onUnbindViewHolder(mCurrentViewHolder);
-            mParent.removeView(mCurrentViewHolder.view);
-            mCurrentViewHolder = null;
-            mCurrentPresenter = null;
-        }
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/RecyclerViewParallax.java b/android/support/v17/leanback/widget/RecyclerViewParallax.java
deleted file mode 100644
index 7253d01..0000000
--- a/android/support/v17/leanback/widget/RecyclerViewParallax.java
+++ /dev/null
@@ -1,252 +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.v17.leanback.widget;
-
-import static android.support.v7.widget.RecyclerView.LayoutManager;
-import static android.support.v7.widget.RecyclerView.OnScrollListener;
-import static android.support.v7.widget.RecyclerView.ViewHolder;
-
-import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView;
-import android.util.Property;
-import android.view.View;
-
-/**
- * Implementation of {@link Parallax} class for {@link RecyclerView}. This class
- * allows users to track position of specific views inside {@link RecyclerView} relative to
- * itself. @see {@link ChildPositionProperty} for details.
- */
-public class RecyclerViewParallax extends Parallax<RecyclerViewParallax.ChildPositionProperty> {
-    RecyclerView mRecylerView;
-    boolean mIsVertical;
-
-    OnScrollListener mOnScrollListener = new OnScrollListener() {
-        @Override
-        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-            updateValues();
-        }
-    };
-
-    View.OnLayoutChangeListener mOnLayoutChangeListener = new View.OnLayoutChangeListener() {
-        @Override
-        public void onLayoutChange(View view, int l, int t, int r, int b,
-                int oldL, int oldT, int oldR, int oldB) {
-            updateValues();
-        }
-    };
-
-    /**
-     * Subclass of {@link Parallax.IntProperty}. Using this Property, users can track a
-     * RecylerView child's position inside recyclerview. i.e.
-     *
-     * tracking_pos = view.top + fraction * view.height() + offset
-     *
-     * This way we can track top using fraction 0 and bottom using fraction 1.
-     */
-    public static final class ChildPositionProperty extends Parallax.IntProperty {
-        int mAdapterPosition;
-        int mViewId;
-        int mOffset;
-        float mFraction;
-
-        ChildPositionProperty(String name, int index) {
-            super(name, index);
-        }
-
-        /**
-         * Sets adapter position of the recyclerview child to track.
-         *
-         * @param adapterPosition Zero based position in adapter.
-         * @return This ChildPositionProperty object.
-         */
-        public ChildPositionProperty adapterPosition(int adapterPosition) {
-            mAdapterPosition = adapterPosition;
-            return this;
-        };
-
-        /**
-         * Sets view Id of a descendant of recyclerview child to track.
-         *
-         * @param viewId Id of a descendant of recyclerview child.
-         * @return This ChildPositionProperty object.
-         */
-        public ChildPositionProperty viewId(int viewId) {
-            mViewId = viewId;
-            return this;
-        }
-
-        /**
-         * Sets offset in pixels added to the view's start position.
-         *
-         * @param offset Offset in pixels added to the view's start position.
-         * @return This ChildPositionProperty object.
-         */
-        public ChildPositionProperty offset(int offset) {
-            mOffset = offset;
-            return this;
-        }
-
-        /**
-         * Sets fraction of size to be added to view's start position.  e.g. to track the
-         * center position of the view, use fraction 0.5; to track the end position of the view
-         * use fraction 1.
-         *
-         * @param fraction Fraction of size of the view.
-         * @return This ChildPositionProperty object.
-         */
-        public ChildPositionProperty fraction(float fraction) {
-            mFraction = fraction;
-            return this;
-        }
-
-        /**
-         * Returns adapter position of the recyclerview child to track.
-         */
-        public int getAdapterPosition() {
-            return mAdapterPosition;
-        }
-
-        /**
-         * Returns view Id of a descendant of recyclerview child to track.
-         */
-        public int getViewId() {
-            return mViewId;
-        }
-
-        /**
-         * Returns offset in pixels added to the view's start position.
-         */
-        public int getOffset() {
-            return mOffset;
-        }
-
-        /**
-         * Returns fraction of size to be added to view's start position.  e.g. to track the
-         * center position of the view, use fraction 0.5; to track the end position of the view
-         * use fraction 1.
-         */
-        public float getFraction() {
-            return mFraction;
-        }
-
-        void updateValue(RecyclerViewParallax source) {
-            RecyclerView recyclerView = source.mRecylerView;
-            ViewHolder viewHolder = recyclerView == null ? null
-                    : recyclerView.findViewHolderForAdapterPosition(mAdapterPosition);
-            if (viewHolder == null) {
-                if (recyclerView == null || recyclerView.getLayoutManager().getChildCount() == 0) {
-                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
-                    return;
-                }
-                View firstChild = recyclerView.getLayoutManager().getChildAt(0);
-                ViewHolder vh = recyclerView.findContainingViewHolder(firstChild);
-                int firstPosition = vh.getAdapterPosition();
-                if (firstPosition < mAdapterPosition) {
-                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
-                } else {
-                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_BEFORE);
-                }
-            } else {
-                View trackingView = viewHolder.itemView.findViewById(mViewId);
-                if (trackingView == null) {
-                    return;
-                }
-
-                Rect rect = new Rect(
-                        0, 0, trackingView.getWidth(), trackingView.getHeight());
-                recyclerView.offsetDescendantRectToMyCoords(trackingView, rect);
-                // Slide transition may change the trackingView's translationX/translationY,
-                // add up translation values in parent.
-                float tx = 0, ty = 0;
-                while (trackingView != recyclerView && trackingView != null) {
-                    // In RecyclerView dispatchLayout() it may call onScrolled(0) with a move
-                    // ItemAnimation just created. We don't have any way to track the ItemAnimation
-                    // update listener, and in ideal use case, the tracking view should not be
-                    // animated in RecyclerView. Do not apply translation value for this case.
-                    if (!(trackingView.getParent() == recyclerView && recyclerView.isAnimating())) {
-                        tx += trackingView.getTranslationX();
-                        ty += trackingView.getTranslationY();
-                    }
-                    trackingView = (View) trackingView.getParent();
-                }
-                rect.offset((int) tx, (int) ty);
-                if (source.mIsVertical) {
-                    source.setIntPropertyValue(getIndex(), rect.top + mOffset
-                            + (int) (mFraction * rect.height()));
-                } else {
-                    source.setIntPropertyValue(getIndex(), rect.left + mOffset
-                            + (int) (mFraction * rect.width()));
-                }
-            }
-        }
-    }
-
-
-    @Override
-    public ChildPositionProperty createProperty(String name, int index) {
-        return new ChildPositionProperty(name, index);
-    }
-
-    @Override
-    public float getMaxValue() {
-        if (mRecylerView == null) {
-            return 0;
-        }
-        return mIsVertical ? mRecylerView.getHeight() : mRecylerView.getWidth();
-    }
-
-    /**
-     * Set RecyclerView that this Parallax will register onScrollListener.
-     * @param recyclerView RecyclerView to register onScrollListener.
-     */
-    public void setRecyclerView(RecyclerView recyclerView) {
-        if (mRecylerView == recyclerView) {
-            return;
-        }
-        if (mRecylerView != null) {
-            mRecylerView.removeOnScrollListener(mOnScrollListener);
-            mRecylerView.removeOnLayoutChangeListener(mOnLayoutChangeListener);
-        }
-        mRecylerView = recyclerView;
-        if (mRecylerView != null) {
-            LayoutManager.Properties properties = mRecylerView.getLayoutManager()
-                    .getProperties(mRecylerView.getContext(), null, 0, 0);
-            mIsVertical = properties.orientation == RecyclerView.VERTICAL;
-            mRecylerView.addOnScrollListener(mOnScrollListener);
-            mRecylerView.addOnLayoutChangeListener(mOnLayoutChangeListener);
-        }
-    }
-
-    /**
-     * Manually update values. This is used for changes not controlled by RecyclerView. E.g.
-     * called by a Slide transition that changes translation of the view.
-     */
-    @Override
-    public void updateValues() {
-        for (Property prop: getProperties()) {
-            ((ChildPositionProperty) prop).updateValue(RecyclerViewParallax.this);
-        }
-        super.updateValues();
-    }
-
-    /**
-     * @return Currently RecylerView that the source has registered onScrollListener.
-     */
-    public RecyclerView getRecyclerView() {
-        return mRecylerView;
-    }
-}
diff --git a/android/support/v17/leanback/widget/ResizingTextView.java b/android/support/v17/leanback/widget/ResizingTextView.java
deleted file mode 100644
index 5888552..0000000
--- a/android/support/v17/leanback/widget/ResizingTextView.java
+++ /dev/null
@@ -1,273 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v17.leanback.R;
-import android.text.Layout;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-/**
- * <p>A {@link android.widget.TextView} that adjusts text size automatically in response
- * to certain trigger conditions, such as text that wraps over multiple lines.</p>
- */
-class ResizingTextView extends TextView {
-
-    /**
-     * Trigger text resize when text flows into the last line of a multi-line text view.
-     */
-    public static final int TRIGGER_MAX_LINES = 0x01;
-
-    private int mTriggerConditions; // Union of trigger conditions
-    private int mResizedTextSize;
-    // Note: Maintaining line spacing turned out not to be useful, and will be removed in
-    // the next round of design for this class (b/18736630). For now it simply defaults to false.
-    private boolean mMaintainLineSpacing;
-    private int mResizedPaddingAdjustmentTop;
-    private int mResizedPaddingAdjustmentBottom;
-
-    private boolean mIsResized = false;
-    // Remember default properties in case we need to restore them
-    private boolean mDefaultsInitialized = false;
-    private int mDefaultTextSize;
-    private float mDefaultLineSpacingExtra;
-    private int mDefaultPaddingTop;
-    private int mDefaultPaddingBottom;
-
-    public ResizingTextView(Context ctx, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(ctx, attrs, defStyleAttr);
-        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.lbResizingTextView,
-                defStyleAttr, defStyleRes);
-
-        try {
-            mTriggerConditions = a.getInt(
-                    R.styleable.lbResizingTextView_resizeTrigger, TRIGGER_MAX_LINES);
-            mResizedTextSize = a.getDimensionPixelSize(
-                    R.styleable.lbResizingTextView_resizedTextSize, -1);
-            mMaintainLineSpacing = a.getBoolean(
-                    R.styleable.lbResizingTextView_maintainLineSpacing, false);
-            mResizedPaddingAdjustmentTop = a.getDimensionPixelOffset(
-                    R.styleable.lbResizingTextView_resizedPaddingAdjustmentTop, 0);
-            mResizedPaddingAdjustmentBottom = a.getDimensionPixelOffset(
-                    R.styleable.lbResizingTextView_resizedPaddingAdjustmentBottom, 0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    public ResizingTextView(Context ctx, AttributeSet attrs, int defStyleAttr) {
-        this(ctx, attrs, defStyleAttr, 0);
-    }
-
-    public ResizingTextView(Context ctx, AttributeSet attrs) {
-        // TODO We should define our own style that inherits from TextViewStyle, to set defaults
-        // for new styleables,  We then pass the appropriate R.attr up the constructor chain here.
-        this(ctx, attrs, android.R.attr.textViewStyle);
-    }
-
-    public ResizingTextView(Context ctx) {
-        this(ctx, null);
-    }
-
-    /**
-     * @return the trigger conditions used to determine whether resize occurs
-     */
-    public int getTriggerConditions() {
-        return mTriggerConditions;
-    }
-
-    /**
-     * Set the trigger conditions used to determine whether resize occurs. Pass
-     * a union of trigger condition constants, such as {@link ResizingTextView#TRIGGER_MAX_LINES}.
-     *
-     * @param conditions A union of trigger condition constants
-     */
-    public void setTriggerConditions(int conditions) {
-        if (mTriggerConditions != conditions) {
-            mTriggerConditions = conditions;
-            // Always request a layout when trigger conditions change
-            requestLayout();
-        }
-    }
-
-    /**
-     * @return the resized text size
-     */
-    public int getResizedTextSize() {
-        return mResizedTextSize;
-    }
-
-    /**
-     * Set the text size for resized text.
-     *
-     * @param size The text size for resized text
-     */
-    public void setResizedTextSize(int size) {
-        if (mResizedTextSize != size) {
-            mResizedTextSize = size;
-            resizeParamsChanged();
-        }
-    }
-
-    /**
-     * @return whether or not to maintain line spacing when resizing text.
-     * The default is true.
-     */
-    public boolean getMaintainLineSpacing() {
-        return mMaintainLineSpacing;
-    }
-
-    /**
-     * Set whether or not to maintain line spacing when resizing text.
-     * The default is true.
-     *
-     * @param maintain Whether or not to maintain line spacing
-     */
-    public void setMaintainLineSpacing(boolean maintain) {
-        if (mMaintainLineSpacing != maintain) {
-            mMaintainLineSpacing = maintain;
-            resizeParamsChanged();
-        }
-    }
-
-    /**
-     * @return desired adjustment to top padding for resized text
-     */
-    public int getResizedPaddingAdjustmentTop() {
-        return mResizedPaddingAdjustmentTop;
-    }
-
-    /**
-     * Set the desired adjustment to top padding for resized text.
-     *
-     * @param adjustment The adjustment to top padding, in pixels
-     */
-    public void setResizedPaddingAdjustmentTop(int adjustment) {
-        if (mResizedPaddingAdjustmentTop != adjustment) {
-            mResizedPaddingAdjustmentTop = adjustment;
-            resizeParamsChanged();
-        }
-    }
-
-    /**
-     * @return desired adjustment to bottom padding for resized text
-     */
-    public int getResizedPaddingAdjustmentBottom() {
-        return mResizedPaddingAdjustmentBottom;
-    }
-
-    /**
-     * Set the desired adjustment to bottom padding for resized text.
-     *
-     * @param adjustment The adjustment to bottom padding, in pixels
-     */
-    public void setResizedPaddingAdjustmentBottom(int adjustment) {
-        if (mResizedPaddingAdjustmentBottom != adjustment) {
-            mResizedPaddingAdjustmentBottom = adjustment;
-            resizeParamsChanged();
-        }
-    }
-
-    private void resizeParamsChanged() {
-        // If we're not resized, then changing resize parameters doesn't
-        // affect layout, so don't bother requesting.
-        if (mIsResized) {
-            requestLayout();
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (!mDefaultsInitialized) {
-            mDefaultTextSize = (int) getTextSize();
-            mDefaultLineSpacingExtra = getLineSpacingExtra();
-            mDefaultPaddingTop = getPaddingTop();
-            mDefaultPaddingBottom = getPaddingBottom();
-            mDefaultsInitialized = true;
-        }
-
-        // Always try first to measure with defaults. Otherwise, we may think we can get away
-        // with larger text sizes later when we actually can't.
-        setTextSize(TypedValue.COMPLEX_UNIT_PX, mDefaultTextSize);
-        setLineSpacing(mDefaultLineSpacingExtra, getLineSpacingMultiplier());
-        setPaddingTopAndBottom(mDefaultPaddingTop, mDefaultPaddingBottom);
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        boolean resizeText = false;
-
-        final Layout layout = getLayout();
-        if (layout != null) {
-            if ((mTriggerConditions & TRIGGER_MAX_LINES) > 0) {
-                final int lineCount = layout.getLineCount();
-                final int maxLines = getMaxLines();
-                if (maxLines > 1) {
-                    resizeText = lineCount == maxLines;
-                }
-            }
-        }
-
-        final int currentSizePx = (int) getTextSize();
-        boolean remeasure = false;
-        if (resizeText) {
-            if (mResizedTextSize != -1 && currentSizePx != mResizedTextSize) {
-                setTextSize(TypedValue.COMPLEX_UNIT_PX, mResizedTextSize);
-                remeasure = true;
-            }
-            // Check for other desired adjustments in addition to the text size
-            final float targetLineSpacingExtra = mDefaultLineSpacingExtra
-                    + mDefaultTextSize - mResizedTextSize;
-            if (mMaintainLineSpacing && getLineSpacingExtra() != targetLineSpacingExtra) {
-                setLineSpacing(targetLineSpacingExtra, getLineSpacingMultiplier());
-                remeasure = true;
-            }
-            final int paddingTop = mDefaultPaddingTop + mResizedPaddingAdjustmentTop;
-            final int paddingBottom = mDefaultPaddingBottom + mResizedPaddingAdjustmentBottom;
-            if (getPaddingTop() != paddingTop || getPaddingBottom() != paddingBottom) {
-                setPaddingTopAndBottom(paddingTop, paddingBottom);
-                remeasure = true;
-            }
-        } else {
-            // Use default size, line spacing, and padding
-            if (mResizedTextSize != -1 && currentSizePx != mDefaultTextSize) {
-                setTextSize(TypedValue.COMPLEX_UNIT_PX, mDefaultTextSize);
-                remeasure = true;
-            }
-            if (mMaintainLineSpacing && getLineSpacingExtra() != mDefaultLineSpacingExtra) {
-                setLineSpacing(mDefaultLineSpacingExtra, getLineSpacingMultiplier());
-                remeasure = true;
-            }
-            if (getPaddingTop() != mDefaultPaddingTop
-                    || getPaddingBottom() != mDefaultPaddingBottom) {
-                setPaddingTopAndBottom(mDefaultPaddingTop, mDefaultPaddingBottom);
-                remeasure = true;
-            }
-        }
-        mIsResized = resizeText;
-        if (remeasure) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    private void setPaddingTopAndBottom(int paddingTop, int paddingBottom) {
-        if (isPaddingRelative()) {
-            setPaddingRelative(getPaddingStart(), paddingTop, getPaddingEnd(), paddingBottom);
-        } else {
-            setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), paddingBottom);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/RoundedRectHelper.java b/android/support/v17/leanback/widget/RoundedRectHelper.java
deleted file mode 100644
index 60fedf2..0000000
--- a/android/support/v17/leanback/widget/RoundedRectHelper.java
+++ /dev/null
@@ -1,93 +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.v17.leanback.widget;
-
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.support.v17.leanback.R;
-import android.view.View;
-
-/**
- * Helper for setting rounded rectangle backgrounds on a view.
- */
-final class RoundedRectHelper {
-
-    private final static RoundedRectHelper sInstance = new RoundedRectHelper();
-    private final Impl mImpl;
-
-    /**
-     * Returns an instance of the helper.
-     */
-    public static RoundedRectHelper getInstance() {
-        return sInstance;
-    }
-
-    public static boolean supportsRoundedCorner() {
-        return Build.VERSION.SDK_INT >= 21;
-    }
-
-    /**
-     * Sets or removes a rounded rectangle outline on the given view.
-     */
-    public void setClipToRoundedOutline(View view, boolean clip, int radius) {
-        mImpl.setClipToRoundedOutline(view, clip, radius);
-    }
-
-    /**
-     * Sets or removes a rounded rectangle outline on the given view.
-     */
-    public void setClipToRoundedOutline(View view, boolean clip) {
-        mImpl.setClipToRoundedOutline(view, clip, view.getResources().getDimensionPixelSize(
-                R.dimen.lb_rounded_rect_corner_radius));
-    }
-
-    static interface Impl {
-        public void setClipToRoundedOutline(View view, boolean clip, int radius);
-    }
-
-    /**
-     * Implementation used prior to L.
-     */
-    private static final class StubImpl implements Impl {
-        StubImpl() {
-        }
-
-        @Override
-        public void setClipToRoundedOutline(View view, boolean clip, int radius) {
-            // Not supported
-        }
-    }
-
-    /**
-     * Implementation used on api 21 (and above).
-     */
-    @RequiresApi(21)
-    private static final class Api21Impl implements Impl {
-        Api21Impl() {
-        }
-
-        @Override
-        public void setClipToRoundedOutline(View view, boolean clip, int radius) {
-            RoundedRectHelperApi21.setClipToRoundedOutline(view, clip, radius);
-        }
-    }
-
-    private RoundedRectHelper() {
-        if (supportsRoundedCorner()) {
-            mImpl = new Api21Impl();
-        } else {
-            mImpl = new StubImpl();
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/RoundedRectHelperApi21.java b/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
deleted file mode 100644
index df1f0f3..0000000
--- a/android/support/v17/leanback/widget/RoundedRectHelperApi21.java
+++ /dev/null
@@ -1,61 +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.v17.leanback.widget;
-
-import android.graphics.Outline;
-import android.support.annotation.RequiresApi;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-@RequiresApi(21)
-class RoundedRectHelperApi21 {
-
-    private static SparseArray<ViewOutlineProvider> sRoundedRectProvider;
-    private static final int MAX_CACHED_PROVIDER = 32;
-
-    static final class RoundedRectOutlineProvider extends ViewOutlineProvider {
-
-        private int mRadius;
-
-        RoundedRectOutlineProvider(int radius) {
-            mRadius = radius;
-        }
-
-        @Override
-        public void getOutline(View view, Outline outline) {
-            outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mRadius);
-            outline.setAlpha(1f);
-        }
-    };
-
-    public static void setClipToRoundedOutline(View view, boolean clip, int roundedCornerRadius) {
-        if (clip) {
-            if (sRoundedRectProvider == null) {
-                sRoundedRectProvider = new SparseArray<ViewOutlineProvider>();
-            }
-            ViewOutlineProvider provider = sRoundedRectProvider.get(roundedCornerRadius);
-            if (provider == null) {
-                provider = new RoundedRectOutlineProvider(roundedCornerRadius);
-                if (sRoundedRectProvider.size() < MAX_CACHED_PROVIDER) {
-                    sRoundedRectProvider.put(roundedCornerRadius, provider);
-                }
-            }
-            view.setOutlineProvider(provider);
-        } else {
-            view.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
-        }
-        view.setClipToOutline(clip);
-    }
-}
diff --git a/android/support/v17/leanback/widget/Row.java b/android/support/v17/leanback/widget/Row.java
deleted file mode 100644
index ea52a82..0000000
--- a/android/support/v17/leanback/widget/Row.java
+++ /dev/null
@@ -1,129 +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.v17.leanback.widget;
-
-import static android.support.v17.leanback.widget.ObjectAdapter.NO_ID;
-
-/**
- * The base class for all rows.  A commonly used subclass is the {@link ListRow}.  Custom
- * subclasses may define other types of rows.
- */
-public class Row {
-
-    private static final int FLAG_ID_USE_MASK = 1;
-    private static final int FLAG_ID_USE_HEADER = 1;
-    private static final int FLAG_ID_USE_ID = 0;
-
-    private int mFlags = FLAG_ID_USE_HEADER;
-    private HeaderItem mHeaderItem;
-    private long mId = NO_ID;
-
-    /**
-     * Constructor for a Row.
-     *
-     * @param id The id of the row.
-     * @param headerItem The {@link HeaderItem} for this Row, or null if there
-     *        is no header.
-     */
-    public Row(long id, HeaderItem headerItem) {
-        setId(id);
-        setHeaderItem(headerItem);
-    }
-
-    /**
-     * Constructor for a Row.
-     *
-     * @param headerItem The {@link HeaderItem} for this Row, or null if there
-     *        is no header.
-     */
-    public Row(HeaderItem headerItem) {
-        setHeaderItem(headerItem);
-    }
-
-    /**
-     * Constructor for a Row.
-     */
-    public Row() {
-    }
-
-    /**
-     * Returns the {@link HeaderItem} that represents metadata for the row.
-     *
-     * @return The HeaderItem for this row, or null if unset.
-     */
-    public final HeaderItem getHeaderItem() {
-        return mHeaderItem;
-    }
-
-    /**
-     * Sets the {@link HeaderItem} that represents metadata for the row.
-     *
-     * @param headerItem The HeaderItem for this Row, or null if there is no
-     *        header.
-     */
-    public final void setHeaderItem(HeaderItem headerItem) {
-        mHeaderItem = headerItem;
-    }
-
-    /**
-     * Sets the id for this row.
-     *
-     * @param id The id of the row.
-     */
-    public final void setId(long id) {
-        mId = id;
-        setFlags(FLAG_ID_USE_ID, FLAG_ID_USE_MASK);
-    }
-
-    /**
-     * Returns a unique identifier for this row. This id can come from one of
-     * three places:
-     * <ul>
-     *   <li>If {@link #setId(long)} is ever called on this row, it will return
-     *   this id.
-     *   <li>If {@link #setId(long)} has not been called but the header item is
-     *   not null, the result of {@link HeaderItem#getId()} is returned.
-     *   <li>Otherwise {@link ObjectAdapter#NO_ID NO_ID} is returned.
-     * </ul>
-     */
-    public final long getId() {
-        if ( (mFlags & FLAG_ID_USE_MASK) == FLAG_ID_USE_HEADER) {
-            HeaderItem header = getHeaderItem();
-            if (header != null) {
-                return header.getId();
-            }
-            return NO_ID;
-        } else {
-            return mId;
-        }
-    }
-
-    final void setFlags(int flags, int mask) {
-        mFlags = (mFlags & ~mask) | (flags & mask);
-    }
-
-    final int getFlags() {
-        return mFlags;
-    }
-
-    /**
-     * Returns true if this Row can be rendered in a visible row view, false otherwise.  For example
-     * {@link ListRow} is rendered by {@link ListRowPresenter}.  {@link PageRow},
-     * {@link SectionRow}, {@link DividerRow} are rendered as invisible row views.
-     * @return True if this Row can be rendered in a visible row view, false otherwise.
-     */
-    public boolean isRenderedAsRowView() {
-        return true;
-    }
-}
diff --git a/android/support/v17/leanback/widget/RowContainerView.java b/android/support/v17/leanback/widget/RowContainerView.java
deleted file mode 100644
index dffcbb5..0000000
--- a/android/support/v17/leanback/widget/RowContainerView.java
+++ /dev/null
@@ -1,118 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-/**
- * RowContainerView wraps header and user defined row view
- */
-final class RowContainerView extends LinearLayout {
-
-    private ViewGroup mHeaderDock;
-    private Drawable mForeground;
-    private boolean mForegroundBoundsChanged = true;
-
-    public RowContainerView(Context context) {
-        this(context, null, 0);
-    }
-
-    public RowContainerView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public RowContainerView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        setOrientation(VERTICAL);
-        LayoutInflater inflater = LayoutInflater.from(context);
-        inflater.inflate(R.layout.lb_row_container, this);
-
-        mHeaderDock = findViewById(R.id.lb_row_container_header_dock);
-        setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-    }
-
-    public void addHeaderView(View headerView) {
-        if (mHeaderDock.indexOfChild(headerView) < 0) {
-            mHeaderDock.addView(headerView, 0);
-        }
-    }
-
-    public void removeHeaderView(View headerView) {
-        if (mHeaderDock.indexOfChild(headerView) >= 0) {
-            mHeaderDock.removeView(headerView);
-        }
-    }
-
-    public void addRowView(View view) {
-        addView(view);
-    }
-
-    public void showHeader(boolean show) {
-        mHeaderDock.setVisibility(show ? View.VISIBLE : View.GONE);
-    }
-
-    @Override
-    public void setForeground(Drawable d) {
-        mForeground = d;
-        setWillNotDraw(mForeground == null);
-        invalidate();
-    }
-
-    public void setForegroundColor(@ColorInt int color) {
-        if (mForeground instanceof ColorDrawable) {
-            ((ColorDrawable) mForeground.mutate()).setColor(color);
-            invalidate();
-        } else {
-            setForeground(new ColorDrawable(color));
-        }
-    }
-
-    @Override
-    public Drawable getForeground() {
-        return mForeground;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mForegroundBoundsChanged = true;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mForeground != null) {
-            if (mForegroundBoundsChanged) {
-                mForegroundBoundsChanged = false;
-                mForeground.setBounds(0, 0, getWidth(), getHeight());
-            }
-            mForeground.draw(canvas);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/RowHeaderPresenter.java b/android/support/v17/leanback/widget/RowHeaderPresenter.java
deleted file mode 100644
index 4bc00c6..0000000
--- a/android/support/v17/leanback/widget/RowHeaderPresenter.java
+++ /dev/null
@@ -1,226 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.Paint;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-/**
- * RowHeaderPresenter provides a default presentation for {@link HeaderItem} using a
- * {@link RowHeaderView} and optionally a TextView for description. If a subclass creates its own
- * view, the subclass must also override {@link #onCreateViewHolder(ViewGroup)},
- * {@link #onSelectLevelChanged(ViewHolder)}.
- */
-public class RowHeaderPresenter extends Presenter {
-
-    private final int mLayoutResourceId;
-    private final Paint mFontMeasurePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-    private boolean mNullItemVisibilityGone;
-    private final boolean mAnimateSelect;
-
-    /**
-     * Creates default RowHeaderPresenter using a title view and a description view.
-     * @see ViewHolder#ViewHolder(View)
-     */
-    public RowHeaderPresenter() {
-        this(R.layout.lb_row_header);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public RowHeaderPresenter(int layoutResourceId) {
-        this(layoutResourceId, true);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public RowHeaderPresenter(int layoutResourceId, boolean animateSelect) {
-        mLayoutResourceId = layoutResourceId;
-        mAnimateSelect = animateSelect;
-    }
-
-    /**
-     * Optionally sets the view visibility to {@link View#GONE} when bound to null.
-     */
-    public void setNullItemVisibilityGone(boolean nullItemVisibilityGone) {
-        mNullItemVisibilityGone = nullItemVisibilityGone;
-    }
-
-    /**
-     * Returns true if the view visibility is set to {@link View#GONE} when bound to null.
-     */
-    public boolean isNullItemVisibilityGone() {
-        return mNullItemVisibilityGone;
-    }
-
-    /**
-     * A ViewHolder for the RowHeaderPresenter.
-     */
-    public static class ViewHolder extends Presenter.ViewHolder {
-        float mSelectLevel;
-        int mOriginalTextColor;
-        float mUnselectAlpha;
-        RowHeaderView mTitleView;
-        TextView mDescriptionView;
-
-        /**
-         * Creating a new ViewHolder that supports title and description.
-         * @param view Root of Views.
-         */
-        public ViewHolder(View view) {
-            super(view);
-            mTitleView = (RowHeaderView)view.findViewById(R.id.row_header);
-            mDescriptionView = (TextView)view.findViewById(R.id.row_header_description);
-            initColors();
-        }
-
-        /**
-         * Uses a single {@link RowHeaderView} for creating a new ViewHolder.
-         * @param view The single RowHeaderView.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public ViewHolder(RowHeaderView view) {
-            super(view);
-            mTitleView = view;
-            initColors();
-        }
-
-        void initColors() {
-            if (mTitleView != null) {
-                mOriginalTextColor = mTitleView.getCurrentTextColor();
-            }
-
-            mUnselectAlpha = view.getResources().getFraction(
-                    R.fraction.lb_browse_header_unselect_alpha, 1, 1);
-        }
-
-        public final float getSelectLevel() {
-            return mSelectLevel;
-        }
-    }
-
-    @Override
-    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        View root = LayoutInflater.from(parent.getContext())
-                .inflate(mLayoutResourceId, parent, false);
-
-        ViewHolder viewHolder = new ViewHolder(root);
-        if (mAnimateSelect) {
-            setSelectLevel(viewHolder, 0);
-        }
-        return viewHolder;
-    }
-
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-        HeaderItem headerItem = item == null ? null : ((Row) item).getHeaderItem();
-        RowHeaderPresenter.ViewHolder vh = (RowHeaderPresenter.ViewHolder)viewHolder;
-        if (headerItem == null) {
-            if (vh.mTitleView != null) {
-                vh.mTitleView.setText(null);
-            }
-            if (vh.mDescriptionView != null) {
-                vh.mDescriptionView.setText(null);
-            }
-
-            viewHolder.view.setContentDescription(null);
-            if (mNullItemVisibilityGone) {
-                viewHolder.view.setVisibility(View.GONE);
-            }
-        } else {
-            if (vh.mTitleView != null) {
-                vh.mTitleView.setText(headerItem.getName());
-            }
-            if (vh.mDescriptionView != null) {
-                if (TextUtils.isEmpty(headerItem.getDescription())) {
-                    vh.mDescriptionView.setVisibility(View.GONE);
-                } else {
-                    vh.mDescriptionView.setVisibility(View.VISIBLE);
-                }
-                vh.mDescriptionView.setText(headerItem.getDescription());
-            }
-            viewHolder.view.setContentDescription(headerItem.getContentDescription());
-            viewHolder.view.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-        RowHeaderPresenter.ViewHolder vh = (ViewHolder)viewHolder;
-        if (vh.mTitleView != null) {
-            vh.mTitleView.setText(null);
-        }
-        if (vh.mDescriptionView != null) {
-            vh.mDescriptionView.setText(null);
-        }
-
-        if (mAnimateSelect) {
-            setSelectLevel((ViewHolder) viewHolder, 0);
-        }
-    }
-
-    /**
-     * Sets the select level.
-     */
-    public final void setSelectLevel(ViewHolder holder, float selectLevel) {
-        holder.mSelectLevel = selectLevel;
-        onSelectLevelChanged(holder);
-    }
-
-    /**
-     * Called when the select level changes.  The default implementation sets the alpha on the view.
-     */
-    protected void onSelectLevelChanged(ViewHolder holder) {
-        if (mAnimateSelect) {
-            holder.view.setAlpha(holder.mUnselectAlpha + holder.mSelectLevel
-                    * (1f - holder.mUnselectAlpha));
-        }
-    }
-
-    /**
-     * Returns the space (distance in pixels) below the baseline of the
-     * text view, if one exists; otherwise, returns 0.
-     */
-    public int getSpaceUnderBaseline(ViewHolder holder) {
-        int space = holder.view.getPaddingBottom();
-        if (holder.view instanceof TextView) {
-            space += (int) getFontDescent((TextView) holder.view, mFontMeasurePaint);
-        }
-        return space;
-    }
-
-    @SuppressWarnings("ReferenceEquality")
-    protected static float getFontDescent(TextView textView, Paint fontMeasurePaint) {
-        if (fontMeasurePaint.getTextSize() != textView.getTextSize()) {
-            fontMeasurePaint.setTextSize(textView.getTextSize());
-        }
-        if (fontMeasurePaint.getTypeface() != textView.getTypeface()) {
-            fontMeasurePaint.setTypeface(textView.getTypeface());
-        }
-        return fontMeasurePaint.descent();
-    }
-}
diff --git a/android/support/v17/leanback/widget/RowHeaderView.java b/android/support/v17/leanback/widget/RowHeaderView.java
deleted file mode 100644
index 0a8f98e..0000000
--- a/android/support/v17/leanback/widget/RowHeaderView.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.v17.leanback.widget;
-
-import android.support.v17.leanback.R;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-/**
- * RowHeaderView is a header text view.
- */
-public final class RowHeaderView extends TextView {
-
-    public RowHeaderView(Context context) {
-        this(context, null);
-    }
-
-    public RowHeaderView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.rowHeaderStyle);
-    }
-
-    public RowHeaderView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/RowPresenter.java b/android/support/v17/leanback/widget/RowPresenter.java
deleted file mode 100644
index ef4e281..0000000
--- a/android/support/v17/leanback/widget/RowPresenter.java
+++ /dev/null
@@ -1,687 +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.v17.leanback.widget;
-
-import android.support.v17.leanback.app.HeadersFragment;
-import android.support.v17.leanback.graphics.ColorOverlayDimmer;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * An abstract {@link Presenter} that renders an Object in RowsFragment, the object can be
- * subclass {@link Row} or a generic one.  When the object is not {@link Row} class,
- * {@link ViewHolder#getRow()} returns null.
- *
- * <h3>Customize UI widgets</h3>
- * When a subclass of RowPresenter adds UI widgets, it should subclass
- * {@link RowPresenter.ViewHolder} and override {@link #createRowViewHolder(ViewGroup)}
- * and {@link #initializeRowViewHolder(ViewHolder)}. The subclass must use layout id
- * "row_content" for the widget that will be aligned to the title of any {@link HeadersFragment}
- * that may exist in the parent fragment. RowPresenter contains an optional and
- * replaceable {@link RowHeaderPresenter} that renders the header. You can disable
- * the default rendering or replace the Presenter with a new header presenter
- * by calling {@link #setHeaderPresenter(RowHeaderPresenter)}.
- *
- * <h3>UI events from fragments</h3>
- * RowPresenter receives calls from its parent (typically a Fragment) when:
- * <ul>
- * <li>
- * A row is selected via {@link #setRowViewSelected(Presenter.ViewHolder, boolean)}.  The event
- * is triggered immediately when there is a row selection change before the selection
- * animation is started.  Selected status may control activated status of the row (see
- * "Activated status" below).
- * Subclasses of RowPresenter may override {@link #onRowViewSelected(ViewHolder, boolean)}.
- * </li>
- * <li>
- * A row is expanded to full height via {@link #setRowViewExpanded(Presenter.ViewHolder, boolean)}
- * when BrowseFragment hides fast lane on the left.
- * The event is triggered immediately before the expand animation is started.
- * Row title is shown when row is expanded.  Expanded status may control activated status
- * of the row (see "Activated status" below).
- * Subclasses of RowPresenter may override {@link #onRowViewExpanded(ViewHolder, boolean)}.
- * </li>
- * </ul>
- *
- * <h3>Activated status</h3>
- * The activated status of a row is applied to the row view and its children via
- * {@link View#setActivated(boolean)}.
- * The activated status is typically used to control {@link BaseCardView} info region visibility.
- * The row's activated status can be controlled by selected status and/or expanded status.
- * Call {@link #setSyncActivatePolicy(int)} and choose one of the four policies:
- * <ul>
- * <li>{@link #SYNC_ACTIVATED_TO_EXPANDED} Activated status is synced with row expanded status</li>
- * <li>{@link #SYNC_ACTIVATED_TO_SELECTED} Activated status is synced with row selected status</li>
- * <li>{@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED} Activated status is set to true
- *     when both expanded and selected status are true</li>
- * <li>{@link #SYNC_ACTIVATED_CUSTOM} Activated status is not controlled by selected status
- *     or expanded status, application can control activated status by its own.
- *     Application should call {@link RowPresenter.ViewHolder#setActivated(boolean)} to change
- *     activated status of row view.
- * </li>
- * </ul>
- *
- * <h3>User events</h3>
- * RowPresenter provides {@link OnItemViewSelectedListener} and {@link OnItemViewClickedListener}.
- * If a subclass wants to add its own {@link View.OnFocusChangeListener} or
- * {@link View.OnClickListener}, it must do that in {@link #createRowViewHolder(ViewGroup)}
- * to be properly chained by the library.  Adding View listeners after
- * {@link #createRowViewHolder(ViewGroup)} is undefined and may result in
- * incorrect behavior by the library's listeners.
- *
- * <h3>Selection animation</h3>
- * <p>
- * When a user scrolls through rows, a fragment will initiate animation and call
- * {@link #setSelectLevel(Presenter.ViewHolder, float)} with float value between
- * 0 and 1.  By default, the RowPresenter draws a dim overlay on top of the row
- * view for views that are not selected. Subclasses may override this default effect
- * by having {@link #isUsingDefaultSelectEffect()} return false and overriding
- * {@link #onSelectLevelChanged(ViewHolder)} to apply a different selection effect.
- * </p>
- * <p>
- * Call {@link #setSelectEffectEnabled(boolean)} to enable/disable the select effect,
- * This will not only enable/disable the default dim effect but also subclasses must
- * respect this flag as well.
- * </p>
- */
-public abstract class RowPresenter extends Presenter {
-
-    /**
-     * Don't synchronize row view activated status with selected status or expanded status,
-     * application will do its own through {@link RowPresenter.ViewHolder#setActivated(boolean)}.
-     */
-    public static final int SYNC_ACTIVATED_CUSTOM = 0;
-
-    /**
-     * Synchronizes row view's activated status to expand status of the row view holder.
-     */
-    public static final int SYNC_ACTIVATED_TO_EXPANDED = 1;
-
-    /**
-     * Synchronizes row view's activated status to selected status of the row view holder.
-     */
-    public static final int SYNC_ACTIVATED_TO_SELECTED = 2;
-
-    /**
-     * Sets the row view's activated status to true when both expand and selected are true.
-     */
-    public static final int SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED = 3;
-
-    static class ContainerViewHolder extends Presenter.ViewHolder {
-        /**
-         * wrapped row view holder
-         */
-        final ViewHolder mRowViewHolder;
-
-        public ContainerViewHolder(RowContainerView containerView, ViewHolder rowViewHolder) {
-            super(containerView);
-            containerView.addRowView(rowViewHolder.view);
-            if (rowViewHolder.mHeaderViewHolder != null) {
-                containerView.addHeaderView(rowViewHolder.mHeaderViewHolder.view);
-            }
-            mRowViewHolder = rowViewHolder;
-            mRowViewHolder.mContainerViewHolder = this;
-        }
-    }
-
-    /**
-     * A ViewHolder for a {@link Row}.
-     */
-    public static class ViewHolder extends Presenter.ViewHolder {
-        private static final int ACTIVATED_NOT_ASSIGNED = 0;
-        private static final int ACTIVATED = 1;
-        private static final int NOT_ACTIVATED = 2;
-
-        ContainerViewHolder mContainerViewHolder;
-        RowHeaderPresenter.ViewHolder mHeaderViewHolder;
-        Row mRow;
-        Object mRowObject;
-        int mActivated = ACTIVATED_NOT_ASSIGNED;
-        boolean mSelected;
-        boolean mExpanded;
-        boolean mInitialzed;
-        float mSelectLevel = 0f; // initially unselected
-        protected final ColorOverlayDimmer mColorDimmer;
-        private View.OnKeyListener mOnKeyListener;
-        BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
-        private BaseOnItemViewClickedListener mOnItemViewClickedListener;
-
-        /**
-         * Constructor for ViewHolder.
-         *
-         * @param view The View bound to the Row.
-         */
-        public ViewHolder(View view) {
-            super(view);
-            mColorDimmer = ColorOverlayDimmer.createDefault(view.getContext());
-        }
-
-        /**
-         * Returns the row bound to this ViewHolder. Returns null if the row is not an instance of
-         * {@link Row}.
-         * @return The row bound to this ViewHolder. Returns null if the row is not an instance of
-         * {@link Row}.
-         */
-        public final Row getRow() {
-            return mRow;
-        }
-
-        /**
-         * Returns the Row object bound to this ViewHolder.
-         * @return The row object bound to this ViewHolder.
-         */
-        public final Object getRowObject() {
-            return mRowObject;
-        }
-
-        /**
-         * Returns whether the Row is in its expanded state.
-         *
-         * @return true if the Row is expanded, false otherwise.
-         */
-        public final boolean isExpanded() {
-            return mExpanded;
-        }
-
-        /**
-         * Returns whether the Row is selected.
-         *
-         * @return true if the Row is selected, false otherwise.
-         */
-        public final boolean isSelected() {
-            return mSelected;
-        }
-
-        /**
-         * Returns the current selection level of the Row.
-         */
-        public final float getSelectLevel() {
-            return mSelectLevel;
-        }
-
-        /**
-         * Returns the view holder for the Row header for this Row.
-         */
-        public final RowHeaderPresenter.ViewHolder getHeaderViewHolder() {
-            return mHeaderViewHolder;
-        }
-
-        /**
-         * Sets the row view's activated status.  The status will be applied to children through
-         * {@link #syncActivatedStatus(View)}.  Application should only call this function
-         * when {@link RowPresenter#getSyncActivatePolicy()} is
-         * {@link RowPresenter#SYNC_ACTIVATED_CUSTOM}; otherwise the value will
-         * be overwritten when expanded or selected status changes.
-         */
-        public final void setActivated(boolean activated) {
-            mActivated = activated ? ACTIVATED : NOT_ACTIVATED;
-        }
-
-        /**
-         * Synchronizes the activated status of view to the last value passed through
-         * {@link RowPresenter.ViewHolder#setActivated(boolean)}. No operation if
-         * {@link RowPresenter.ViewHolder#setActivated(boolean)} is never called.  Normally
-         * application does not need to call this method,  {@link ListRowPresenter} automatically
-         * calls this method when a child is attached to list row.   However if
-         * application writes its own custom RowPresenter, it should call this method
-         * when attaches a child to the row view.
-         */
-        public final void syncActivatedStatus(View view) {
-            if (mActivated == ACTIVATED) {
-                view.setActivated(true);
-            } else if (mActivated == NOT_ACTIVATED) {
-                view.setActivated(false);
-            }
-        }
-
-        /**
-         * Sets a key listener.
-         */
-        public void setOnKeyListener(View.OnKeyListener keyListener) {
-            mOnKeyListener = keyListener;
-        }
-
-        /**
-         * Returns the key listener.
-         */
-        public View.OnKeyListener getOnKeyListener() {
-            return mOnKeyListener;
-        }
-
-        /**
-         * Sets the listener for item or row selection.  RowPresenter fires row selection
-         * event with null item.  A subclass of RowPresenter e.g. {@link ListRowPresenter} may
-         * fire a selection event with selected item.
-         */
-        public final void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
-            mOnItemViewSelectedListener = listener;
-        }
-
-        /**
-         * Returns the listener for item or row selection.
-         */
-        public final BaseOnItemViewSelectedListener getOnItemViewSelectedListener() {
-            return mOnItemViewSelectedListener;
-        }
-
-        /**
-         * Sets the listener for item click event.  RowPresenter does nothing but subclass of
-         * RowPresenter may fire item click event if it has the concept of item.
-         * OnItemViewClickedListener will override {@link View.OnClickListener} that
-         * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-         */
-        public final void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
-            mOnItemViewClickedListener = listener;
-        }
-
-        /**
-         * Returns the listener for item click event.
-         */
-        public final BaseOnItemViewClickedListener getOnItemViewClickedListener() {
-            return mOnItemViewClickedListener;
-        }
-        /**
-         * Return {@link ViewHolder} of currently selected item inside a row ViewHolder.
-         * @return The selected item's ViewHolder.
-         */
-        public Presenter.ViewHolder getSelectedItemViewHolder() {
-            return null;
-        }
-
-        /**
-         * Return currently selected item inside a row ViewHolder.
-         * @return The selected item.
-         */
-        public Object getSelectedItem() {
-            return null;
-        }
-    }
-
-    private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter();
-
-    boolean mSelectEffectEnabled = true;
-    int mSyncActivatePolicy = SYNC_ACTIVATED_TO_EXPANDED;
-
-
-    /**
-     * Constructs a RowPresenter.
-     */
-    public RowPresenter() {
-        mHeaderPresenter.setNullItemVisibilityGone(true);
-    }
-
-    @Override
-    public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        ViewHolder vh = createRowViewHolder(parent);
-        vh.mInitialzed = false;
-        Presenter.ViewHolder result;
-        if (needsRowContainerView()) {
-            RowContainerView containerView = new RowContainerView(parent.getContext());
-            if (mHeaderPresenter != null) {
-                vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder)
-                        mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view);
-            }
-            result = new ContainerViewHolder(containerView, vh);
-        } else {
-            result = vh;
-        }
-        initializeRowViewHolder(vh);
-        if (!vh.mInitialzed) {
-            throw new RuntimeException("super.initializeRowViewHolder() must be called");
-        }
-        return result;
-    }
-
-    /**
-     * Called to create a ViewHolder object for a Row. Subclasses will override
-     * this method to return a different concrete ViewHolder object.
-     *
-     * @param parent The parent View for the Row's view holder.
-     * @return A ViewHolder for the Row's View.
-     */
-    protected abstract ViewHolder createRowViewHolder(ViewGroup parent);
-
-    /**
-     * Returns true if the Row view should clip its children.  The clipChildren
-     * flag is set on view in {@link #initializeRowViewHolder(ViewHolder)}.  Note that
-     * Slide transition or explode transition need turn off clipChildren.
-     * Default value is false.
-     */
-    protected boolean isClippingChildren() {
-        return false;
-    }
-
-    /**
-     * Called after a {@link RowPresenter.ViewHolder} is created for a Row.
-     * Subclasses may override this method and start by calling
-     * super.initializeRowViewHolder(ViewHolder).
-     *
-     * @param vh The ViewHolder to initialize for the Row.
-     */
-    protected void initializeRowViewHolder(ViewHolder vh) {
-        vh.mInitialzed = true;
-        if (!isClippingChildren()) {
-            // set clip children to false for slide transition
-            if (vh.view instanceof ViewGroup) {
-                ((ViewGroup) vh.view).setClipChildren(false);
-            }
-            if (vh.mContainerViewHolder != null) {
-                ((ViewGroup) vh.mContainerViewHolder.view).setClipChildren(false);
-            }
-        }
-    }
-
-    /**
-     * Sets the Presenter used for rendering the header. Can be null to disable
-     * header rendering. The method must be called before creating any Row Views.
-     */
-    public final void setHeaderPresenter(RowHeaderPresenter headerPresenter) {
-        mHeaderPresenter = headerPresenter;
-    }
-
-    /**
-     * Returns the Presenter used for rendering the header, or null if none has been
-     * set.
-     */
-    public final RowHeaderPresenter getHeaderPresenter() {
-        return mHeaderPresenter;
-    }
-
-    /**
-     * Returns the {@link RowPresenter.ViewHolder} from the given RowPresenter
-     * ViewHolder.
-     */
-    public final ViewHolder getRowViewHolder(Presenter.ViewHolder holder) {
-        if (holder instanceof ContainerViewHolder) {
-            return ((ContainerViewHolder) holder).mRowViewHolder;
-        } else {
-            return (ViewHolder) holder;
-        }
-    }
-
-    /**
-     * Sets the expanded state of a Row view.
-     *
-     * @param holder The Row ViewHolder to set expanded state on.
-     * @param expanded True if the Row is expanded, false otherwise.
-     */
-    public final void setRowViewExpanded(Presenter.ViewHolder holder, boolean expanded) {
-        ViewHolder rowViewHolder = getRowViewHolder(holder);
-        rowViewHolder.mExpanded = expanded;
-        onRowViewExpanded(rowViewHolder, expanded);
-    }
-
-    /**
-     * Sets the selected state of a Row view.
-     *
-     * @param holder The Row ViewHolder to set expanded state on.
-     * @param selected True if the Row is expanded, false otherwise.
-     */
-    public final void setRowViewSelected(Presenter.ViewHolder holder, boolean selected) {
-        ViewHolder rowViewHolder = getRowViewHolder(holder);
-        rowViewHolder.mSelected = selected;
-        onRowViewSelected(rowViewHolder, selected);
-    }
-
-    /**
-     * Called when the row view's expanded state changes.  A subclass may override this method to
-     * respond to expanded state changes of a Row.
-     * The default implementation will hide/show the header view. Subclasses may
-     * make visual changes to the Row View but must not create animation on the
-     * Row view.
-     */
-    protected void onRowViewExpanded(ViewHolder vh, boolean expanded) {
-        updateHeaderViewVisibility(vh);
-        updateActivateStatus(vh, vh.view);
-    }
-
-    /**
-     * Updates the view's activate status according to {@link #getSyncActivatePolicy()} and the
-     * selected status and expanded status of the RowPresenter ViewHolder.
-     */
-    private void updateActivateStatus(ViewHolder vh, View view) {
-        switch (mSyncActivatePolicy) {
-            case SYNC_ACTIVATED_TO_EXPANDED:
-                vh.setActivated(vh.isExpanded());
-                break;
-            case SYNC_ACTIVATED_TO_SELECTED:
-                vh.setActivated(vh.isSelected());
-                break;
-            case SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED:
-                vh.setActivated(vh.isExpanded() && vh.isSelected());
-                break;
-        }
-        vh.syncActivatedStatus(view);
-    }
-
-    /**
-     * Sets the policy of updating row view activated status.  Can be one of:
-     * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
-     * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
-     * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
-     * <li> {@link #SYNC_ACTIVATED_CUSTOM}
-     */
-    public final void setSyncActivatePolicy(int syncActivatePolicy) {
-        mSyncActivatePolicy = syncActivatePolicy;
-    }
-
-    /**
-     * Returns the policy of updating row view activated status.  Can be one of:
-     * <li> Default value {@link #SYNC_ACTIVATED_TO_EXPANDED}
-     * <li> {@link #SYNC_ACTIVATED_TO_SELECTED}
-     * <li> {@link #SYNC_ACTIVATED_TO_EXPANDED_AND_SELECTED}
-     * <li> {@link #SYNC_ACTIVATED_CUSTOM}
-     */
-    public final int getSyncActivatePolicy() {
-        return mSyncActivatePolicy;
-    }
-
-    /**
-     * This method is only called from
-     * {@link #onRowViewSelected(ViewHolder, boolean)} onRowViewSelected.
-     * The default behavior is to signal row selected events with a null item parameter.
-     * A Subclass of RowPresenter having child items should override this method and dispatch
-     * events with item information.
-     */
-    protected void dispatchItemSelectedListener(ViewHolder vh, boolean selected) {
-        if (selected) {
-            if (vh.mOnItemViewSelectedListener != null) {
-                vh.mOnItemViewSelectedListener.onItemSelected(null, null, vh, vh.getRowObject());
-            }
-        }
-    }
-
-    /**
-     * Called when the given row view changes selection state.  A subclass may override this to
-     * respond to selected state changes of a Row.  A subclass may make visual changes to Row view
-     * but must not create animation on the Row view.
-     */
-    protected void onRowViewSelected(ViewHolder vh, boolean selected) {
-        dispatchItemSelectedListener(vh, selected);
-        updateHeaderViewVisibility(vh);
-        updateActivateStatus(vh, vh.view);
-    }
-
-    private void updateHeaderViewVisibility(ViewHolder vh) {
-        if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) {
-            RowContainerView containerView = ((RowContainerView) vh.mContainerViewHolder.view);
-            containerView.showHeader(vh.isExpanded());
-        }
-    }
-
-    /**
-     * Sets the current select level to a value between 0 (unselected) and 1 (selected).
-     * Subclasses may override {@link #onSelectLevelChanged(ViewHolder)} to
-     * respond to changes in the selected level.
-     */
-    public final void setSelectLevel(Presenter.ViewHolder vh, float level) {
-        ViewHolder rowViewHolder = getRowViewHolder(vh);
-        rowViewHolder.mSelectLevel = level;
-        onSelectLevelChanged(rowViewHolder);
-    }
-
-    /**
-     * Returns the current select level. The value will be between 0 (unselected)
-     * and 1 (selected).
-     */
-    public final float getSelectLevel(Presenter.ViewHolder vh) {
-        return getRowViewHolder(vh).mSelectLevel;
-    }
-
-    /**
-     * Callback when the select level changes. The default implementation applies
-     * the select level to {@link RowHeaderPresenter#setSelectLevel(RowHeaderPresenter.ViewHolder, float)}
-     * when {@link #getSelectEffectEnabled()} is true. Subclasses may override
-     * this function and implement a different select effect. In this case,
-     * the method {@link #isUsingDefaultSelectEffect()} should also be overridden to disable
-     * the default dimming effect.
-     */
-    protected void onSelectLevelChanged(ViewHolder vh) {
-        if (getSelectEffectEnabled()) {
-            vh.mColorDimmer.setActiveLevel(vh.mSelectLevel);
-            if (vh.mHeaderViewHolder != null) {
-                mHeaderPresenter.setSelectLevel(vh.mHeaderViewHolder, vh.mSelectLevel);
-            }
-            if (isUsingDefaultSelectEffect()) {
-                ((RowContainerView) vh.mContainerViewHolder.view).setForegroundColor(
-                        vh.mColorDimmer.getPaint().getColor());
-            }
-        }
-    }
-
-    /**
-     * Enables or disables the row selection effect.
-     * This will not only affect the default dim effect, but subclasses must
-     * respect this flag as well.
-     */
-    public final void setSelectEffectEnabled(boolean applyDimOnSelect) {
-        mSelectEffectEnabled = applyDimOnSelect;
-    }
-
-    /**
-     * Returns true if the row selection effect is enabled.
-     * This value not only determines whether the default dim implementation is
-     * used, but subclasses must also respect this flag.
-     */
-    public final boolean getSelectEffectEnabled() {
-        return mSelectEffectEnabled;
-    }
-
-    /**
-     * Returns true if this RowPresenter is using the default dimming effect.
-     * A subclass may (most likely) return false and
-     * override {@link #onSelectLevelChanged(ViewHolder)}.
-     */
-    public boolean isUsingDefaultSelectEffect() {
-        return true;
-    }
-
-    final boolean needsDefaultSelectEffect() {
-        return isUsingDefaultSelectEffect() && getSelectEffectEnabled();
-    }
-
-    final boolean needsRowContainerView() {
-        return mHeaderPresenter != null || needsDefaultSelectEffect();
-    }
-
-    @Override
-    public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-        onBindRowViewHolder(getRowViewHolder(viewHolder), item);
-    }
-
-    /**
-     * Binds the given row object to the given ViewHolder.
-     * Derived classes of {@link RowPresenter} overriding
-     * {@link #onBindRowViewHolder(ViewHolder, Object)} must call through the super class's
-     * implementation of this method.
-     */
-    protected void onBindRowViewHolder(ViewHolder vh, Object item) {
-        vh.mRowObject = item;
-        vh.mRow = item instanceof Row ? (Row) item : null;
-        if (vh.mHeaderViewHolder != null && vh.getRow() != null) {
-            mHeaderPresenter.onBindViewHolder(vh.mHeaderViewHolder, item);
-        }
-    }
-
-    @Override
-    public final void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-        onUnbindRowViewHolder(getRowViewHolder(viewHolder));
-    }
-
-    /**
-     * Unbinds the given ViewHolder.
-     * Derived classes of {@link RowPresenter} overriding {@link #onUnbindRowViewHolder(ViewHolder)}
-     * must call through the super class's implementation of this method.
-     */
-    protected void onUnbindRowViewHolder(ViewHolder vh) {
-        if (vh.mHeaderViewHolder != null) {
-            mHeaderPresenter.onUnbindViewHolder(vh.mHeaderViewHolder);
-        }
-        vh.mRow = null;
-        vh.mRowObject = null;
-    }
-
-    @Override
-    public final void onViewAttachedToWindow(Presenter.ViewHolder holder) {
-        onRowViewAttachedToWindow(getRowViewHolder(holder));
-    }
-
-    /**
-     * Invoked when the row view is attached to the window.
-     */
-    protected void onRowViewAttachedToWindow(ViewHolder vh) {
-        if (vh.mHeaderViewHolder != null) {
-            mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder);
-        }
-    }
-
-    @Override
-    public final void onViewDetachedFromWindow(Presenter.ViewHolder holder) {
-        onRowViewDetachedFromWindow(getRowViewHolder(holder));
-    }
-
-    /**
-     * Invoked when the row view is detached from the window.
-     */
-    protected void onRowViewDetachedFromWindow(ViewHolder vh) {
-        if (vh.mHeaderViewHolder != null) {
-            mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder);
-        }
-        cancelAnimationsRecursive(vh.view);
-    }
-
-    /**
-     * Freezes/unfreezes the row, typically used when a transition starts/ends.
-     * This method is called by the fragment, it should not call it directly by the application.
-     */
-    public void freeze(ViewHolder holder, boolean freeze) {
-    }
-
-    /**
-     * Changes the visibility of views.  The entrance transition will be run against the views that
-     * change visibilities.  A subclass may override and begin with calling
-     * super.setEntranceTransitionState().  This method is called by the fragment,
-     * it should not be called directly by the application.
-     *
-     * @param holder         The ViewHolder of the row.
-     * @param afterEntrance  true if children of row participating in entrance transition
-     *                       should be set to visible, false otherwise.
-     */
-    public void setEntranceTransitionState(ViewHolder holder, boolean afterEntrance) {
-        if (holder.mHeaderViewHolder != null
-                && holder.mHeaderViewHolder.view.getVisibility() != View.GONE) {
-            holder.mHeaderViewHolder.view.setVisibility(afterEntrance
-                    ? View.VISIBLE : View.INVISIBLE);
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/ScaleFrameLayout.java b/android/support/v17/leanback/widget/ScaleFrameLayout.java
deleted file mode 100644
index c394272..0000000
--- a/android/support/v17/leanback/widget/ScaleFrameLayout.java
+++ /dev/null
@@ -1,209 +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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * Subclass of FrameLayout that support scale layout area size for children.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ScaleFrameLayout extends FrameLayout {
-
-    private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
-
-    private float mLayoutScaleX = 1f;
-    private float mLayoutScaleY = 1f;
-
-    private float mChildScale = 1f;
-
-    public ScaleFrameLayout(Context context) {
-        this(context ,null);
-    }
-
-    public ScaleFrameLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ScaleFrameLayout(Context context, AttributeSet attrs,
-            int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public void setLayoutScaleX(float scaleX) {
-        if (scaleX != mLayoutScaleX) {
-            mLayoutScaleX = scaleX;
-            requestLayout();
-        }
-    }
-
-    public void setLayoutScaleY(float scaleY) {
-        if (scaleY != mLayoutScaleY) {
-            mLayoutScaleY = scaleY;
-            requestLayout();
-        }
-    }
-
-    public void setChildScale(float scale) {
-        if (mChildScale != scale) {
-            mChildScale = scale;
-            for (int i = 0; i < getChildCount(); i++) {
-                getChildAt(i).setScaleX(scale);
-                getChildAt(i).setScaleY(scale);
-            }
-        }
-    }
-
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        super.addView(child, index, params);
-        child.setScaleX(mChildScale);
-        child.setScaleY(mChildScale);
-    }
-
-    @Override
-    protected boolean addViewInLayout (View child, int index, ViewGroup.LayoutParams params,
-            boolean preventRequestLayout) {
-        boolean ret = super.addViewInLayout(child, index, params, preventRequestLayout);
-        if (ret) {
-            child.setScaleX(mChildScale);
-            child.setScaleY(mChildScale);
-        }
-        return ret;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final int count = getChildCount();
-
-        final int parentLeft, parentRight;
-        final int layoutDirection = getLayoutDirection();
-        final float pivotX = (layoutDirection == View.LAYOUT_DIRECTION_RTL)
-                ? getWidth() - getPivotX()
-                : getPivotX();
-        if (mLayoutScaleX != 1f) {
-            parentLeft = getPaddingLeft() + (int)(pivotX - pivotX / mLayoutScaleX + 0.5f);
-            parentRight = (int)(pivotX + (right - left - pivotX) / mLayoutScaleX + 0.5f)
-                    - getPaddingRight();
-        } else {
-            parentLeft = getPaddingLeft();
-            parentRight = right - left - getPaddingRight();
-        }
-
-        final int parentTop, parentBottom;
-        final float pivotY = getPivotY();
-        if (mLayoutScaleY != 1f) {
-            parentTop = getPaddingTop() + (int)(pivotY - pivotY / mLayoutScaleY + 0.5f);
-            parentBottom = (int)(pivotY + (bottom - top - pivotY) / mLayoutScaleY + 0.5f)
-                    - getPaddingBottom();
-        } else {
-            parentTop = getPaddingTop();
-            parentBottom = bottom - top - getPaddingBottom();
-        }
-
-        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 absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
-                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-                    case Gravity.CENTER_HORIZONTAL:
-                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2
-                                + lp.leftMargin - lp.rightMargin;
-                        break;
-                    case Gravity.RIGHT:
-                        childLeft = parentRight - width - lp.rightMargin;
-                        break;
-                    case Gravity.LEFT:
-                    default:
-                        childLeft = parentLeft + lp.leftMargin;
-                }
-
-                switch (verticalGravity) {
-                    case Gravity.TOP:
-                        childTop = parentTop + lp.topMargin;
-                        break;
-                    case Gravity.CENTER_VERTICAL:
-                        childTop = parentTop + (parentBottom - parentTop - height) / 2
-                                + lp.topMargin - lp.bottomMargin;
-                        break;
-                    case Gravity.BOTTOM:
-                        childTop = parentBottom - height - lp.bottomMargin;
-                        break;
-                    default:
-                        childTop = parentTop + lp.topMargin;
-                }
-
-                child.layout(childLeft, childTop, childLeft + width, childTop + height);
-                // synchronize child pivot to be same as ScaleFrameLayout's pivot
-                child.setPivotX(pivotX - childLeft);
-                child.setPivotY(pivotY - childTop);
-            }
-        }
-    }
-
-    private static int getScaledMeasureSpec(int measureSpec, float scale) {
-        return scale == 1f ? measureSpec : MeasureSpec.makeMeasureSpec(
-                (int) (MeasureSpec.getSize(measureSpec) / scale + 0.5f),
-                MeasureSpec.getMode(measureSpec));
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mLayoutScaleX != 1f || mLayoutScaleY != 1f) {
-            final int scaledWidthMeasureSpec =
-                    getScaledMeasureSpec(widthMeasureSpec, mLayoutScaleX);
-            final int scaledHeightMeasureSpec =
-                    getScaledMeasureSpec(heightMeasureSpec, mLayoutScaleY);
-            super.onMeasure(scaledWidthMeasureSpec, scaledHeightMeasureSpec);
-            setMeasuredDimension((int)(getMeasuredWidth() * mLayoutScaleX + 0.5f),
-                    (int)(getMeasuredHeight() * mLayoutScaleY + 0.5f));
-        } else {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    /**
-     * setForeground() is not supported,  throws UnsupportedOperationException() when called.
-     */
-    @Override
-    public void setForeground(Drawable d) {
-        throw new UnsupportedOperationException();
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/SearchBar.java b/android/support/v17/leanback/widget/SearchBar.java
deleted file mode 100644
index 1094343..0000000
--- a/android/support/v17/leanback/widget/SearchBar.java
+++ /dev/null
@@ -1,828 +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.v17.leanback.widget;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.media.SoundPool;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.speech.RecognitionListener;
-import android.speech.RecognizerIntent;
-import android.speech.SpeechRecognizer;
-import android.support.v17.leanback.R;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A search widget containing a search orb and a text entry view.
- *
- * <p>
- * Note: When {@link SpeechRecognitionCallback} is not used, i.e. using {@link SpeechRecognizer},
- * your application will need to declare android.permission.RECORD_AUDIO in manifest file.
- * If your application target >= 23 and the device is running >= 23, it needs implement
- * {@link SearchBarPermissionListener} where requests runtime permission.
- * </p>
- */
-public class SearchBar extends RelativeLayout {
-    static final String TAG = SearchBar.class.getSimpleName();
-    static final boolean DEBUG = false;
-
-    static final float FULL_LEFT_VOLUME = 1.0f;
-    static final float FULL_RIGHT_VOLUME = 1.0f;
-    static final int DEFAULT_PRIORITY = 1;
-    static final int DO_NOT_LOOP = 0;
-    static final float DEFAULT_RATE = 1.0f;
-
-    /**
-     * Interface for receiving notification of search query changes.
-     */
-    public interface SearchBarListener {
-
-        /**
-         * Method invoked when the search bar detects a change in the query.
-         *
-         * @param query The current full query.
-         */
-        public void onSearchQueryChange(String query);
-
-        /**
-         * <p>Method invoked when the search query is submitted.</p>
-         *
-         * <p>This method can be called without a preceeding onSearchQueryChange,
-         * in particular in the case of a voice input.</p>
-         *
-         * @param query The query being submitted.
-         */
-        public void onSearchQuerySubmit(String query);
-
-        /**
-         * Method invoked when the IME is being dismissed.
-         *
-         * @param query The query set in the search bar at the time the IME is being dismissed.
-         */
-        public void onKeyboardDismiss(String query);
-
-    }
-
-    /**
-     * Interface that handles runtime permissions requests. App sets listener on SearchBar via
-     * {@link #setPermissionListener(SearchBarPermissionListener)}.
-     */
-    public interface SearchBarPermissionListener {
-
-        /**
-         * Method invoked when SearchBar asks for "android.permission.RECORD_AUDIO" runtime
-         * permission.
-         */
-        void requestAudioPermission();
-
-    }
-
-    SearchBarListener mSearchBarListener;
-    SearchEditText mSearchTextEditor;
-    SpeechOrbView mSpeechOrbView;
-    private ImageView mBadgeView;
-    String mSearchQuery;
-    private String mHint;
-    private String mTitle;
-    private Drawable mBadgeDrawable;
-    final Handler mHandler = new Handler();
-    private final InputMethodManager mInputMethodManager;
-    boolean mAutoStartRecognition = false;
-    private Drawable mBarBackground;
-
-    private final int mTextColor;
-    private final int mTextColorSpeechMode;
-    private final int mTextHintColor;
-    private final int mTextHintColorSpeechMode;
-    private int mBackgroundAlpha;
-    private int mBackgroundSpeechAlpha;
-    private int mBarHeight;
-    private SpeechRecognizer mSpeechRecognizer;
-    private SpeechRecognitionCallback mSpeechRecognitionCallback;
-    private boolean mListening;
-    SoundPool mSoundPool;
-    SparseIntArray mSoundMap = new SparseIntArray();
-    boolean mRecognizing = false;
-    private final Context mContext;
-    private AudioManager mAudioManager;
-    private SearchBarPermissionListener mPermissionListener;
-
-    public SearchBar(Context context) {
-        this(context, null);
-    }
-
-    public SearchBar(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SearchBar(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mContext = context;
-
-        Resources r = getResources();
-
-        LayoutInflater inflater = LayoutInflater.from(getContext());
-        inflater.inflate(R.layout.lb_search_bar, this, true);
-
-        mBarHeight = getResources().getDimensionPixelSize(R.dimen.lb_search_bar_height);
-        RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                mBarHeight);
-        params.addRule(ALIGN_PARENT_TOP, RelativeLayout.TRUE);
-        setLayoutParams(params);
-        setBackgroundColor(Color.TRANSPARENT);
-        setClipChildren(false);
-
-        mSearchQuery = "";
-        mInputMethodManager =
-                (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
-
-        mTextColorSpeechMode = r.getColor(R.color.lb_search_bar_text_speech_mode);
-        mTextColor = r.getColor(R.color.lb_search_bar_text);
-
-        mBackgroundSpeechAlpha = r.getInteger(R.integer.lb_search_bar_speech_mode_background_alpha);
-        mBackgroundAlpha = r.getInteger(R.integer.lb_search_bar_text_mode_background_alpha);
-
-        mTextHintColorSpeechMode = r.getColor(R.color.lb_search_bar_hint_speech_mode);
-        mTextHintColor = r.getColor(R.color.lb_search_bar_hint);
-
-        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        RelativeLayout items = (RelativeLayout)findViewById(R.id.lb_search_bar_items);
-        mBarBackground = items.getBackground();
-
-        mSearchTextEditor = (SearchEditText)findViewById(R.id.lb_search_text_editor);
-        mBadgeView = (ImageView)findViewById(R.id.lb_search_bar_badge);
-        if (null != mBadgeDrawable) {
-            mBadgeView.setImageDrawable(mBadgeDrawable);
-        }
-
-        mSearchTextEditor.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View view, boolean hasFocus) {
-                if (DEBUG) Log.v(TAG, "EditText.onFocusChange " + hasFocus);
-                if (hasFocus) {
-                    showNativeKeyboard();
-                } else {
-                    hideNativeKeyboard();
-                }
-                updateUi(hasFocus);
-            }
-        });
-        final Runnable mOnTextChangedRunnable = new Runnable() {
-            @Override
-            public void run() {
-                setSearchQueryInternal(mSearchTextEditor.getText().toString());
-            }
-        };
-        mSearchTextEditor.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
-            }
-
-            @Override
-            public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
-                // don't propagate event during speech recognition.
-                if (mRecognizing) {
-                    return;
-                }
-                // while IME opens,  text editor becomes "" then restores to current value
-                mHandler.removeCallbacks(mOnTextChangedRunnable);
-                mHandler.post(mOnTextChangedRunnable);
-            }
-
-            @Override
-            public void afterTextChanged(Editable editable) {
-
-            }
-        });
-        mSearchTextEditor.setOnKeyboardDismissListener(
-                new SearchEditText.OnKeyboardDismissListener() {
-                    @Override
-                    public void onKeyboardDismiss() {
-                        if (null != mSearchBarListener) {
-                            mSearchBarListener.onKeyboardDismiss(mSearchQuery);
-                        }
-                    }
-                });
-
-        mSearchTextEditor.setOnEditorActionListener(new TextView.OnEditorActionListener() {
-            @Override
-            public boolean onEditorAction(TextView textView, int action, KeyEvent keyEvent) {
-                if (DEBUG) Log.v(TAG, "onEditorAction: " + action + " event: " + keyEvent);
-                boolean handled = true;
-                if ((EditorInfo.IME_ACTION_SEARCH == action
-                        || EditorInfo.IME_NULL == action) && null != mSearchBarListener) {
-                    if (DEBUG) Log.v(TAG, "Action or enter pressed");
-                    hideNativeKeyboard();
-                    mHandler.postDelayed(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (DEBUG) Log.v(TAG, "Delayed action handling (search)");
-                            submitQuery();
-                        }
-                    }, 500);
-
-                } else if (EditorInfo.IME_ACTION_NONE == action && null != mSearchBarListener) {
-                    if (DEBUG) Log.v(TAG, "Escaped North");
-                    hideNativeKeyboard();
-                    mHandler.postDelayed(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (DEBUG) Log.v(TAG, "Delayed action handling (escape_north)");
-                            mSearchBarListener.onKeyboardDismiss(mSearchQuery);
-                        }
-                    }, 500);
-                } else if (EditorInfo.IME_ACTION_GO == action) {
-                    if (DEBUG) Log.v(TAG, "Voice Clicked");
-                        hideNativeKeyboard();
-                        mHandler.postDelayed(new Runnable() {
-                            @Override
-                            public void run() {
-                                if (DEBUG) Log.v(TAG, "Delayed action handling (voice_mode)");
-                                mAutoStartRecognition = true;
-                                mSpeechOrbView.requestFocus();
-                            }
-                        }, 500);
-                } else {
-                    handled = false;
-                }
-
-                return handled;
-            }
-        });
-
-        mSearchTextEditor.setPrivateImeOptions("EscapeNorth=1;VoiceDismiss=1;");
-
-        mSpeechOrbView = (SpeechOrbView)findViewById(R.id.lb_search_bar_speech_orb);
-        mSpeechOrbView.setOnOrbClickedListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                toggleRecognition();
-            }
-        });
-        mSpeechOrbView.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View view, boolean hasFocus) {
-                if (DEBUG) Log.v(TAG, "SpeechOrb.onFocusChange " + hasFocus);
-                if (hasFocus) {
-                    hideNativeKeyboard();
-                    if (mAutoStartRecognition) {
-                        startRecognition();
-                        mAutoStartRecognition = false;
-                    }
-                } else {
-                    stopRecognition();
-                }
-                updateUi(hasFocus);
-            }
-        });
-
-        updateUi(hasFocus());
-        updateHint();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (DEBUG) Log.v(TAG, "Loading soundPool");
-        mSoundPool = new SoundPool(2, AudioManager.STREAM_SYSTEM, 0);
-        loadSounds(mContext);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        stopRecognition();
-        if (DEBUG) Log.v(TAG, "Releasing SoundPool");
-        mSoundPool.release();
-        super.onDetachedFromWindow();
-    }
-
-    /**
-     * Sets a listener for when the term search changes
-     * @param listener
-     */
-    public void setSearchBarListener(SearchBarListener listener) {
-        mSearchBarListener = listener;
-    }
-
-    /**
-     * Sets the search query
-     * @param query the search query to use
-     */
-    public void setSearchQuery(String query) {
-        stopRecognition();
-        mSearchTextEditor.setText(query);
-        setSearchQueryInternal(query);
-    }
-
-    void setSearchQueryInternal(String query) {
-        if (DEBUG) Log.v(TAG, "setSearchQueryInternal " + query);
-        if (TextUtils.equals(mSearchQuery, query)) {
-            return;
-        }
-        mSearchQuery = query;
-
-        if (null != mSearchBarListener) {
-            mSearchBarListener.onSearchQueryChange(mSearchQuery);
-        }
-    }
-
-    /**
-     * Sets the title text used in the hint shown in the search bar.
-     * @param title The hint to use.
-     */
-    public void setTitle(String title) {
-        mTitle = title;
-        updateHint();
-    }
-
-    /**
-     * Sets background color of not-listening state search orb.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        if (mSpeechOrbView != null) {
-            mSpeechOrbView.setNotListeningOrbColors(colors);
-        }
-    }
-
-    /**
-     * Sets background color of listening state search orb.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setSearchAffordanceColorsInListening(SearchOrbView.Colors colors) {
-        if (mSpeechOrbView != null) {
-            mSpeechOrbView.setListeningOrbColors(colors);
-        }
-    }
-
-    /**
-     * Returns the current title
-     */
-    public String getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Returns the current search bar hint text.
-     */
-    public CharSequence getHint() {
-        return mHint;
-    }
-
-    /**
-     * Sets the badge drawable showing inside the search bar.
-     * @param drawable The drawable to be used in the search bar.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        mBadgeDrawable = drawable;
-        if (null != mBadgeView) {
-            mBadgeView.setImageDrawable(drawable);
-            if (null != drawable) {
-                mBadgeView.setVisibility(View.VISIBLE);
-            } else {
-                mBadgeView.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    /**
-     * Returns the badge drawable
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeDrawable;
-    }
-
-    /**
-     * Updates the completion list shown by the IME
-     *
-     * @param completions list of completions shown in the IME, can be null or empty to clear them
-     */
-    public void displayCompletions(List<String> completions) {
-        List<CompletionInfo> infos = new ArrayList<>();
-        if (null != completions) {
-            for (String completion : completions) {
-                infos.add(new CompletionInfo(infos.size(), infos.size(), completion));
-            }
-        }
-        CompletionInfo[] array = new CompletionInfo[infos.size()];
-        displayCompletions(infos.toArray(array));
-    }
-
-    /**
-     * Updates the completion list shown by the IME
-     *
-     * @param completions list of completions shown in the IME, can be null or empty to clear them
-     */
-    public void displayCompletions(CompletionInfo[] completions) {
-        mInputMethodManager.displayCompletions(mSearchTextEditor, completions);
-    }
-
-    /**
-     * Sets the speech recognizer to be used when doing voice search. The Activity/Fragment is in
-     * charge of creating and destroying the recognizer with its own lifecycle.
-     *
-     * @param recognizer a SpeechRecognizer
-     */
-    public void setSpeechRecognizer(SpeechRecognizer recognizer) {
-        stopRecognition();
-        if (null != mSpeechRecognizer) {
-            mSpeechRecognizer.setRecognitionListener(null);
-            if (mListening) {
-                mSpeechRecognizer.cancel();
-                mListening = false;
-            }
-        }
-        mSpeechRecognizer = recognizer;
-        if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
-            throw new IllegalStateException("Can't have speech recognizer and request");
-        }
-    }
-
-    /**
-     * Sets the speech recognition callback.
-     *
-     * @deprecated Launching voice recognition activity is no longer supported. App should declare
-     *             android.permission.RECORD_AUDIO in AndroidManifest file. See details in
-     *             {@link android.support.v17.leanback.app.SearchSupportFragment}.
-     */
-    @Deprecated
-    public void setSpeechRecognitionCallback(SpeechRecognitionCallback request) {
-        mSpeechRecognitionCallback = request;
-        if (mSpeechRecognitionCallback != null && mSpeechRecognizer != null) {
-            throw new IllegalStateException("Can't have speech recognizer and request");
-        }
-    }
-
-    void hideNativeKeyboard() {
-        mInputMethodManager.hideSoftInputFromWindow(mSearchTextEditor.getWindowToken(),
-                InputMethodManager.RESULT_UNCHANGED_SHOWN);
-    }
-
-    void showNativeKeyboard() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mSearchTextEditor.requestFocusFromTouch();
-                mSearchTextEditor.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
-                        SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN,
-                        mSearchTextEditor.getWidth(), mSearchTextEditor.getHeight(), 0));
-                mSearchTextEditor.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
-                        SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
-                        mSearchTextEditor.getWidth(), mSearchTextEditor.getHeight(), 0));
-            }
-        });
-    }
-
-    /**
-     * This will update the hint for the search bar properly depending on state and provided title
-     */
-    private void updateHint() {
-        String title = getResources().getString(R.string.lb_search_bar_hint);
-        if (!TextUtils.isEmpty(mTitle)) {
-            if (isVoiceMode()) {
-                title = getResources().getString(R.string.lb_search_bar_hint_with_title_speech, mTitle);
-            } else {
-                title = getResources().getString(R.string.lb_search_bar_hint_with_title, mTitle);
-            }
-        } else if (isVoiceMode()) {
-            title = getResources().getString(R.string.lb_search_bar_hint_speech);
-        }
-        mHint = title;
-        if (mSearchTextEditor != null) {
-            mSearchTextEditor.setHint(mHint);
-        }
-    }
-
-    void toggleRecognition() {
-        if (mRecognizing) {
-            stopRecognition();
-        } else {
-            startRecognition();
-        }
-    }
-
-    /**
-     * Returns true if is not running Recognizer, false otherwise.
-     * @return True if is not running Recognizer, false otherwise.
-     */
-    public boolean isRecognizing() {
-        return mRecognizing;
-    }
-
-    /**
-     * Stops the speech recognition, if already started.
-     */
-    public void stopRecognition() {
-        if (DEBUG) Log.v(TAG, String.format("stopRecognition (listening: %s, recognizing: %s)",
-                mListening, mRecognizing));
-
-        if (!mRecognizing) return;
-
-        // Edit text content was cleared when starting recognition; ensure the content is restored
-        // in error cases
-        mSearchTextEditor.setText(mSearchQuery);
-        mSearchTextEditor.setHint(mHint);
-
-        mRecognizing = false;
-
-        if (mSpeechRecognitionCallback != null || null == mSpeechRecognizer) return;
-
-        mSpeechOrbView.showNotListening();
-
-        if (mListening) {
-            mSpeechRecognizer.cancel();
-            mListening = false;
-        }
-
-        mSpeechRecognizer.setRecognitionListener(null);
-    }
-
-    /**
-     * Sets listener that handles runtime permission requests.
-     * @param listener Listener that handles runtime permission requests.
-     */
-    public void setPermissionListener(SearchBarPermissionListener listener) {
-        mPermissionListener = listener;
-    }
-
-    public void startRecognition() {
-        if (DEBUG) Log.v(TAG, String.format("startRecognition (listening: %s, recognizing: %s)",
-                mListening, mRecognizing));
-
-        if (mRecognizing) return;
-        if (!hasFocus()) {
-            requestFocus();
-        }
-        if (mSpeechRecognitionCallback != null) {
-            mSearchTextEditor.setText("");
-            mSearchTextEditor.setHint("");
-            mSpeechRecognitionCallback.recognizeSpeech();
-            mRecognizing = true;
-            return;
-        }
-        if (null == mSpeechRecognizer) return;
-        int res = getContext().checkCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO);
-        if (PackageManager.PERMISSION_GRANTED != res) {
-            if (Build.VERSION.SDK_INT >= 23 && mPermissionListener != null) {
-                mPermissionListener.requestAudioPermission();
-                return;
-            } else {
-                throw new IllegalStateException(Manifest.permission.RECORD_AUDIO
-                        + " required for search");
-            }
-        }
-
-        mRecognizing = true;
-
-        mSearchTextEditor.setText("");
-
-        Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
-
-        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
-                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
-        recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
-
-        mSpeechRecognizer.setRecognitionListener(new RecognitionListener() {
-            @Override
-            public void onReadyForSpeech(Bundle bundle) {
-                if (DEBUG) Log.v(TAG, "onReadyForSpeech");
-                mSpeechOrbView.showListening();
-                playSearchOpen();
-            }
-
-            @Override
-            public void onBeginningOfSpeech() {
-                if (DEBUG) Log.v(TAG, "onBeginningOfSpeech");
-            }
-
-            @Override
-            public void onRmsChanged(float rmsdB) {
-                if (DEBUG) Log.v(TAG, "onRmsChanged " + rmsdB);
-                int level = rmsdB < 0 ? 0 : (int)(10 * rmsdB);
-                mSpeechOrbView.setSoundLevel(level);
-            }
-
-            @Override
-            public void onBufferReceived(byte[] bytes) {
-                if (DEBUG) Log.v(TAG, "onBufferReceived " + bytes.length);
-            }
-
-            @Override
-            public void onEndOfSpeech() {
-                if (DEBUG) Log.v(TAG, "onEndOfSpeech");
-            }
-
-            @Override
-            public void onError(int error) {
-                if (DEBUG) Log.v(TAG, "onError " + error);
-                switch (error) {
-                    case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
-                        Log.w(TAG, "recognizer network timeout");
-                        break;
-                    case SpeechRecognizer.ERROR_NETWORK:
-                        Log.w(TAG, "recognizer network error");
-                        break;
-                    case SpeechRecognizer.ERROR_AUDIO:
-                        Log.w(TAG, "recognizer audio error");
-                        break;
-                    case SpeechRecognizer.ERROR_SERVER:
-                        Log.w(TAG, "recognizer server error");
-                        break;
-                    case SpeechRecognizer.ERROR_CLIENT:
-                        Log.w(TAG, "recognizer client error");
-                        break;
-                    case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
-                        Log.w(TAG, "recognizer speech timeout");
-                        break;
-                    case SpeechRecognizer.ERROR_NO_MATCH:
-                        Log.w(TAG, "recognizer no match");
-                        break;
-                    case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
-                        Log.w(TAG, "recognizer busy");
-                        break;
-                    case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
-                        Log.w(TAG, "recognizer insufficient permissions");
-                        break;
-                    default:
-                        Log.d(TAG, "recognizer other error");
-                        break;
-                }
-
-                stopRecognition();
-                playSearchFailure();
-            }
-
-            @Override
-            public void onResults(Bundle bundle) {
-                if (DEBUG) Log.v(TAG, "onResults");
-                final ArrayList<String> matches =
-                        bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
-                if (matches != null) {
-                    if (DEBUG) Log.v(TAG, "Got results" + matches);
-
-                    mSearchQuery = matches.get(0);
-                    mSearchTextEditor.setText(mSearchQuery);
-                    submitQuery();
-                }
-
-                stopRecognition();
-                playSearchSuccess();
-            }
-
-            @Override
-            public void onPartialResults(Bundle bundle) {
-                ArrayList<String> results = bundle.getStringArrayList(
-                        SpeechRecognizer.RESULTS_RECOGNITION);
-                if (DEBUG) {
-                    Log.v(TAG, "onPartialResults " + bundle + " results "
-                            + (results == null ? results : results.size()));
-                }
-                if (results == null || results.size() == 0) {
-                    return;
-                }
-
-                // stableText: high confidence text from PartialResults, if any.
-                // Otherwise, existing stable text.
-                final String stableText = results.get(0);
-                if (DEBUG) Log.v(TAG, "onPartialResults stableText " + stableText);
-
-                // pendingText: low confidence text from PartialResults, if any.
-                // Otherwise, empty string.
-                final String pendingText = results.size() > 1 ? results.get(1) : null;
-                if (DEBUG) Log.v(TAG, "onPartialResults pendingText " + pendingText);
-
-                mSearchTextEditor.updateRecognizedText(stableText, pendingText);
-            }
-
-            @Override
-            public void onEvent(int i, Bundle bundle) {
-
-            }
-        });
-
-        mListening = true;
-        mSpeechRecognizer.startListening(recognizerIntent);
-    }
-
-    void updateUi(boolean hasFocus) {
-        if (hasFocus) {
-            mBarBackground.setAlpha(mBackgroundSpeechAlpha);
-            if (isVoiceMode()) {
-                mSearchTextEditor.setTextColor(mTextHintColorSpeechMode);
-                mSearchTextEditor.setHintTextColor(mTextHintColorSpeechMode);
-            } else {
-                mSearchTextEditor.setTextColor(mTextColorSpeechMode);
-                mSearchTextEditor.setHintTextColor(mTextHintColorSpeechMode);
-            }
-        } else {
-            mBarBackground.setAlpha(mBackgroundAlpha);
-            mSearchTextEditor.setTextColor(mTextColor);
-            mSearchTextEditor.setHintTextColor(mTextHintColor);
-        }
-
-        updateHint();
-    }
-
-    private boolean isVoiceMode() {
-        return mSpeechOrbView.isFocused();
-    }
-
-    void submitQuery() {
-        if (!TextUtils.isEmpty(mSearchQuery) && null != mSearchBarListener) {
-            mSearchBarListener.onSearchQuerySubmit(mSearchQuery);
-        }
-    }
-
-    private void loadSounds(Context context) {
-        int[] sounds = {
-                R.raw.lb_voice_failure,
-                R.raw.lb_voice_open,
-                R.raw.lb_voice_no_input,
-                R.raw.lb_voice_success,
-        };
-        for (int sound : sounds) {
-            mSoundMap.put(sound, mSoundPool.load(context, sound, 1));
-        }
-    }
-
-    private void play(final int resId) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                int sound = mSoundMap.get(resId);
-                mSoundPool.play(sound, FULL_LEFT_VOLUME, FULL_RIGHT_VOLUME, DEFAULT_PRIORITY,
-                        DO_NOT_LOOP, DEFAULT_RATE);
-            }
-        });
-    }
-
-    void playSearchOpen() {
-        play(R.raw.lb_voice_open);
-    }
-
-    void playSearchFailure() {
-        play(R.raw.lb_voice_failure);
-    }
-
-    private void playSearchNoInput() {
-        play(R.raw.lb_voice_no_input);
-    }
-
-    void playSearchSuccess() {
-        play(R.raw.lb_voice_success);
-    }
-
-    @Override
-    public void setNextFocusDownId(int viewId) {
-        mSpeechOrbView.setNextFocusDownId(viewId);
-        mSearchTextEditor.setNextFocusDownId(viewId);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/SearchEditText.java b/android/support/v17/leanback/widget/SearchEditText.java
deleted file mode 100644
index 51047d3..0000000
--- a/android/support/v17/leanback/widget/SearchEditText.java
+++ /dev/null
@@ -1,73 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-
-/**
- * EditText widget that monitors keyboard changes.
- */
-public class SearchEditText extends StreamingTextView {
-    private static final String TAG = SearchEditText.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    /**
-     * Interface for receiving notification when the keyboard is dismissed.
-     */
-    public interface OnKeyboardDismissListener {
-        /**
-         * Method invoked when the keyboard is dismissed.
-         */
-        public void onKeyboardDismiss();
-    }
-
-    private OnKeyboardDismissListener mKeyboardDismissListener;
-
-    public SearchEditText(Context context) {
-        this(context, null);
-    }
-
-    public SearchEditText(Context context, AttributeSet attrs) {
-        this(context, attrs, R.style.TextAppearance_Leanback_SearchTextEdit);
-    }
-
-    public SearchEditText(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
-            if (DEBUG) Log.v(TAG, "Keyboard being dismissed");
-            if (mKeyboardDismissListener != null) {
-                mKeyboardDismissListener.onKeyboardDismiss();
-            }
-            return false;
-        }
-        return super.onKeyPreIme(keyCode, event);
-    }
-
-    /**
-     * Sets a keyboard dismissed listener.
-     *
-     * @param listener The listener.
-     */
-    public void setOnKeyboardDismissListener(OnKeyboardDismissListener listener) {
-        mKeyboardDismissListener = listener;
-    }
-}
diff --git a/android/support/v17/leanback/widget/SearchOrbView.java b/android/support/v17/leanback/widget/SearchOrbView.java
deleted file mode 100644
index 8269f60..0000000
--- a/android/support/v17/leanback/widget/SearchOrbView.java
+++ /dev/null
@@ -1,382 +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.v17.leanback.widget;
-
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-/**
- * <p>A widget that draws a search affordance, represented by a round background and an icon.</p>
- *
- * The background color and icon can be customized.
- */
-public class SearchOrbView extends FrameLayout implements View.OnClickListener {
-    private OnClickListener mListener;
-    private View mRootView;
-    private View mSearchOrbView;
-    private ImageView mIcon;
-    private Drawable mIconDrawable;
-    private Colors mColors;
-    private final float mFocusedZoom;
-    private final int mPulseDurationMs;
-    private final int mScaleDurationMs;
-    private final float mUnfocusedZ;
-    private final float mFocusedZ;
-    private ValueAnimator mColorAnimator;
-    private boolean mColorAnimationEnabled;
-    private boolean mAttachedToWindow;
-
-    /**
-     * A set of colors used to display the search orb.
-     */
-    public static class Colors {
-        private static final float sBrightnessAlpha = 0.15f;
-
-        /**
-         * Constructs a color set using the given color for the search orb.
-         * Other colors are provided by the framework.
-         *
-         * @param color The main search orb color.
-         */
-        public Colors(@ColorInt int color) {
-            this(color, color);
-        }
-
-        /**
-         * Constructs a color set using the given colors for the search orb.
-         * Other colors are provided by the framework.
-         *
-         * @param color The main search orb color.
-         * @param brightColor A brighter version of the search orb used for animation.
-         */
-        public Colors(@ColorInt int color, @ColorInt int brightColor) {
-            this(color, brightColor, Color.TRANSPARENT);
-        }
-
-        /**
-         * Constructs a color set using the given colors.
-         *
-         * @param color The main search orb color.
-         * @param brightColor A brighter version of the search orb used for animation.
-         * @param iconColor A color used to tint the search orb icon.
-         */
-        public Colors(@ColorInt int color, @ColorInt int brightColor, @ColorInt int iconColor) {
-            this.color = color;
-            this.brightColor = brightColor == color ? getBrightColor(color) : brightColor;
-            this.iconColor = iconColor;
-        }
-
-        /**
-         * The main color of the search orb.
-         */
-        @ColorInt
-        public int color;
-
-        /**
-         * A brighter version of the search orb used for animation.
-         */
-        @ColorInt
-        public int brightColor;
-
-        /**
-         * A color used to tint the search orb icon.
-         */
-        @ColorInt
-        public int iconColor;
-
-        /**
-         * Computes a default brighter version of the given color.
-         */
-        public static int getBrightColor(int color) {
-            final float brightnessValue = 0xff * sBrightnessAlpha;
-            int red = (int)(Color.red(color) * (1 - sBrightnessAlpha) + brightnessValue);
-            int green = (int)(Color.green(color) * (1 - sBrightnessAlpha) + brightnessValue);
-            int blue = (int)(Color.blue(color) * (1 - sBrightnessAlpha) + brightnessValue);
-            int alpha = (int)(Color.alpha(color) * (1 - sBrightnessAlpha) + brightnessValue);
-            return Color.argb(alpha, red, green, blue);
-        }
-    }
-
-    private final ArgbEvaluator mColorEvaluator = new ArgbEvaluator();
-
-    private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animator) {
-            Integer color = (Integer) animator.getAnimatedValue();
-            setOrbViewColor(color.intValue());
-        }
-    };
-
-    private ValueAnimator mShadowFocusAnimator;
-
-    private final ValueAnimator.AnimatorUpdateListener mFocusUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            setSearchOrbZ(animation.getAnimatedFraction());
-        }
-    };
-
-    void setSearchOrbZ(float fraction) {
-        ShadowHelper.getInstance().setZ(mSearchOrbView,
-                mUnfocusedZ + fraction * (mFocusedZ - mUnfocusedZ));
-    }
-
-    public SearchOrbView(Context context) {
-        this(context, null);
-    }
-
-    public SearchOrbView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.searchOrbViewStyle);
-    }
-
-    public SearchOrbView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        final Resources res = context.getResources();
-
-        LayoutInflater inflater = (LayoutInflater) context
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mRootView = inflater.inflate(getLayoutResourceId(), this, true);
-        mSearchOrbView = mRootView.findViewById(R.id.search_orb);
-        mIcon = (ImageView) mRootView.findViewById(R.id.icon);
-
-        mFocusedZoom = context.getResources().getFraction(
-                R.fraction.lb_search_orb_focused_zoom, 1, 1);
-        mPulseDurationMs = context.getResources().getInteger(
-                R.integer.lb_search_orb_pulse_duration_ms);
-        mScaleDurationMs = context.getResources().getInteger(
-                R.integer.lb_search_orb_scale_duration_ms);
-        mFocusedZ = context.getResources().getDimensionPixelSize(
-                R.dimen.lb_search_orb_focused_z);
-        mUnfocusedZ = context.getResources().getDimensionPixelSize(
-                R.dimen.lb_search_orb_unfocused_z);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSearchOrbView,
-                defStyleAttr, 0);
-
-        Drawable img = a.getDrawable(R.styleable.lbSearchOrbView_searchOrbIcon);
-        if (img == null) {
-            img = res.getDrawable(R.drawable.lb_ic_in_app_search);
-        }
-        setOrbIcon(img);
-
-        int defColor = res.getColor(R.color.lb_default_search_color);
-        int color = a.getColor(R.styleable.lbSearchOrbView_searchOrbColor, defColor);
-        int brightColor = a.getColor(
-                R.styleable.lbSearchOrbView_searchOrbBrightColor, color);
-        int iconColor = a.getColor(R.styleable.lbSearchOrbView_searchOrbIconColor, Color.TRANSPARENT);
-        setOrbColors(new Colors(color, brightColor, iconColor));
-        a.recycle();
-
-        setFocusable(true);
-        setClipChildren(false);
-        setOnClickListener(this);
-        setSoundEffectsEnabled(false);
-        setSearchOrbZ(0);
-
-        // Icon has no background, but must be on top of the search orb view
-        ShadowHelper.getInstance().setZ(mIcon, mFocusedZ);
-    }
-
-    int getLayoutResourceId() {
-        return R.layout.lb_search_orb;
-    }
-
-    void scaleOrbViewOnly(float scale) {
-        mSearchOrbView.setScaleX(scale);
-        mSearchOrbView.setScaleY(scale);
-    }
-
-    float getFocusedZoom() {
-        return mFocusedZoom;
-    }
-
-    @Override
-    public void onClick(View view) {
-        if (null != mListener) {
-            mListener.onClick(view);
-        }
-    }
-
-    private void startShadowFocusAnimation(boolean gainFocus, int duration) {
-        if (mShadowFocusAnimator == null) {
-            mShadowFocusAnimator = ValueAnimator.ofFloat(0f, 1f);
-            mShadowFocusAnimator.addUpdateListener(mFocusUpdateListener);
-        }
-        if (gainFocus) {
-            mShadowFocusAnimator.start();
-        } else {
-            mShadowFocusAnimator.reverse();
-        }
-        mShadowFocusAnimator.setDuration(duration);
-    }
-
-    @Override
-    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        animateOnFocus(gainFocus);
-    }
-
-    void animateOnFocus(boolean hasFocus) {
-        final float zoom = hasFocus ? mFocusedZoom : 1f;
-        mRootView.animate().scaleX(zoom).scaleY(zoom).setDuration(mScaleDurationMs).start();
-        startShadowFocusAnimation(hasFocus, mScaleDurationMs);
-        enableOrbColorAnimation(hasFocus);
-    }
-
-    /**
-     * Sets the orb icon.
-     * @param icon the drawable to be used as the icon
-     */
-    public void setOrbIcon(Drawable icon) {
-        mIconDrawable = icon;
-        mIcon.setImageDrawable(mIconDrawable);
-    }
-
-    /**
-     * Returns the orb icon
-     * @return the drawable used as the icon
-     */
-    public Drawable getOrbIcon() {
-        return mIconDrawable;
-    }
-
-    /**
-     * Sets the on click listener for the orb.
-     * @param listener The listener.
-     */
-    public void setOnOrbClickedListener(OnClickListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Sets the background color of the search orb.
-     * Other colors will be provided by the framework.
-     *
-     * @param color the RGBA color
-     */
-    public void setOrbColor(int color) {
-        setOrbColors(new Colors(color, color, Color.TRANSPARENT));
-    }
-
-    /**
-     * Sets the search orb colors.
-     * Other colors are provided by the framework.
-     * @deprecated Use {@link #setOrbColors(Colors)} instead.
-     */
-    @Deprecated
-    public void setOrbColor(@ColorInt int color, @ColorInt int brightColor) {
-        setOrbColors(new Colors(color, brightColor, Color.TRANSPARENT));
-    }
-
-    /**
-     * Returns the orb color
-     * @return the RGBA color
-     */
-    @ColorInt
-    public int getOrbColor() {
-        return mColors.color;
-    }
-
-    /**
-     * Sets the {@link Colors} used to display the search orb.
-     */
-    public void setOrbColors(Colors colors) {
-        mColors = colors;
-        mIcon.setColorFilter(mColors.iconColor);
-
-        if (mColorAnimator == null) {
-            setOrbViewColor(mColors.color);
-        } else {
-            enableOrbColorAnimation(true);
-        }
-    }
-
-    /**
-     * Returns the {@link Colors} used to display the search orb.
-     */
-    public Colors getOrbColors() {
-        return mColors;
-    }
-
-    /**
-     * Enables or disables the orb color animation.
-     *
-     * <p>
-     * Orb color animation is handled automatically when the orb is focused/unfocused,
-     * however, an app may choose to override the current animation state, for example
-     * when an activity is paused.
-     * </p>
-     */
-    public void enableOrbColorAnimation(boolean enable) {
-        mColorAnimationEnabled = enable;
-        updateColorAnimator();
-    }
-
-    private void updateColorAnimator() {
-        if (mColorAnimator != null) {
-            mColorAnimator.end();
-            mColorAnimator = null;
-        }
-        if (mColorAnimationEnabled && mAttachedToWindow) {
-            // TODO: set interpolator (material if available)
-            mColorAnimator = ValueAnimator.ofObject(mColorEvaluator,
-                    mColors.color, mColors.brightColor, mColors.color);
-            mColorAnimator.setRepeatCount(ValueAnimator.INFINITE);
-            mColorAnimator.setDuration(mPulseDurationMs * 2);
-            mColorAnimator.addUpdateListener(mUpdateListener);
-            mColorAnimator.start();
-        }
-    }
-
-    void setOrbViewColor(int color) {
-        if (mSearchOrbView.getBackground() instanceof GradientDrawable) {
-            ((GradientDrawable) mSearchOrbView.getBackground()).setColor(color);
-        }
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mAttachedToWindow = true;
-        updateColorAnimator();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mAttachedToWindow = false;
-        // Must stop infinite animation to prevent activity leak
-        updateColorAnimator();
-        super.onDetachedFromWindow();
-    }
-}
diff --git a/android/support/v17/leanback/widget/SectionRow.java b/android/support/v17/leanback/widget/SectionRow.java
deleted file mode 100644
index 379508d..0000000
--- a/android/support/v17/leanback/widget/SectionRow.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.v17.leanback.widget;
-
-/**
- * Used to represent section item in HeadersFragment.  Unlike a normal Row, it's not focusable.
- */
-public class SectionRow extends Row {
-
-    public SectionRow(HeaderItem headerItem) {
-        super(headerItem);
-    }
-
-    public SectionRow(long id, String name) {
-        super(new HeaderItem(id, name));
-    }
-
-    public SectionRow(String name) {
-        super(new HeaderItem(name));
-    }
-
-    @Override
-    final public boolean isRenderedAsRowView() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/SeekBar.java b/android/support/v17/leanback/widget/SeekBar.java
deleted file mode 100644
index b86b9be..0000000
--- a/android/support/v17/leanback/widget/SeekBar.java
+++ /dev/null
@@ -1,246 +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.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/android/support/v17/leanback/widget/ShadowHelper.java b/android/support/v17/leanback/widget/ShadowHelper.java
deleted file mode 100644
index cbdddbf..0000000
--- a/android/support/v17/leanback/widget/ShadowHelper.java
+++ /dev/null
@@ -1,127 +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.v17.leanback.widget;
-
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-
-/**
- * Helper for shadow.
- */
-final class ShadowHelper {
-
-    final static ShadowHelper sInstance = new ShadowHelper();
-    boolean mSupportsDynamicShadow;
-    ShadowHelperVersionImpl mImpl;
-
-    /**
-     * Interface implemented by classes that support Shadow.
-     */
-    static interface ShadowHelperVersionImpl {
-        public Object addDynamicShadow(
-                View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius);
-        public void setZ(View view, float z);
-        public void setShadowFocusLevel(Object impl, float level);
-    }
-
-    /**
-     * Interface used when we do not support Shadow animations.
-     */
-    private static final class ShadowHelperStubImpl implements ShadowHelperVersionImpl {
-        ShadowHelperStubImpl() {
-        }
-
-        @Override
-        public Object addDynamicShadow(
-                View shadowContainer, float focusedZ, float unfocusedZ, int roundedCornerRadius) {
-            // do nothing
-            return null;
-        }
-
-        @Override
-        public void setShadowFocusLevel(Object impl, float level) {
-            // do nothing
-        }
-
-        @Override
-        public void setZ(View view, float z) {
-            // do nothing
-        }
-    }
-
-    /**
-     * Implementation used on api 21 (and above).
-     */
-    @RequiresApi(21)
-    private static final class ShadowHelperApi21Impl implements ShadowHelperVersionImpl {
-        ShadowHelperApi21Impl() {
-        }
-
-        @Override
-        public Object addDynamicShadow(
-                View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius) {
-            return ShadowHelperApi21.addDynamicShadow(
-                    shadowContainer, unfocusedZ, focusedZ, roundedCornerRadius);
-        }
-
-        @Override
-        public void setShadowFocusLevel(Object impl, float level) {
-            ShadowHelperApi21.setShadowFocusLevel(impl, level);
-        }
-
-        @Override
-        public void setZ(View view, float z) {
-            ShadowHelperApi21.setZ(view, z);
-        }
-    }
-
-    /**
-     * Returns the ShadowHelper.
-     */
-    private ShadowHelper() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            mSupportsDynamicShadow = true;
-            mImpl = new ShadowHelperApi21Impl();
-        } else {
-            mImpl = new ShadowHelperStubImpl();
-        }
-    }
-
-    public static ShadowHelper getInstance() {
-        return sInstance;
-    }
-
-    public boolean supportsDynamicShadow() {
-        return mSupportsDynamicShadow;
-    }
-
-    public Object addDynamicShadow(
-            View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius) {
-        return mImpl.addDynamicShadow(shadowContainer, unfocusedZ, focusedZ, roundedCornerRadius);
-    }
-
-    public void setShadowFocusLevel(Object impl, float level) {
-        mImpl.setShadowFocusLevel(impl, level);
-    }
-
-    /**
-     * Set the view z coordinate.
-     */
-    public void setZ(View view, float z) {
-        mImpl.setZ(view, z);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ShadowHelperApi21.java b/android/support/v17/leanback/widget/ShadowHelperApi21.java
deleted file mode 100644
index 4e03d8a..0000000
--- a/android/support/v17/leanback/widget/ShadowHelperApi21.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.v17.leanback.widget;
-
-import android.graphics.Outline;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-@RequiresApi(21)
-class ShadowHelperApi21 {
-
-    static class ShadowImpl {
-        View mShadowContainer;
-        float mNormalZ;
-        float mFocusedZ;
-    }
-
-    static final ViewOutlineProvider sOutlineProvider = new ViewOutlineProvider() {
-        @Override
-        public void getOutline(View view, Outline outline) {
-            outline.setRect(0, 0, view.getWidth(), view.getHeight());
-            outline.setAlpha(1.0f);
-        }
-    };
-
-    /* add shadows and return a implementation detail object */
-    public static Object addDynamicShadow(
-            View shadowContainer, float unfocusedZ, float focusedZ, int roundedCornerRadius) {
-        if (roundedCornerRadius > 0) {
-            RoundedRectHelperApi21.setClipToRoundedOutline(shadowContainer, true,
-                    roundedCornerRadius);
-        } else {
-            shadowContainer.setOutlineProvider(sOutlineProvider);
-        }
-        ShadowImpl impl = new ShadowImpl();
-        impl.mShadowContainer = shadowContainer;
-        impl.mNormalZ = unfocusedZ;
-        impl.mFocusedZ = focusedZ;
-        shadowContainer.setZ(impl.mNormalZ);
-        return impl;
-    }
-
-    /* set shadow focus level 0 for unfocused 1 for fully focused */
-    public static void setShadowFocusLevel(Object object, float level) {
-        ShadowImpl impl = (ShadowImpl) object;
-        impl.mShadowContainer.setZ(impl.mNormalZ + level * (impl.mFocusedZ - impl.mNormalZ));
-    }
-
-    public static void setZ(View view, float z) {
-        view.setZ(z);
-    }
-}
diff --git a/android/support/v17/leanback/widget/ShadowHelperJbmr2.java b/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
deleted file mode 100644
index 8cd0054..0000000
--- a/android/support/v17/leanback/widget/ShadowHelperJbmr2.java
+++ /dev/null
@@ -1,52 +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.v17.leanback.widget;
-
-import android.support.annotation.RequiresApi;
-import android.support.v17.leanback.R;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-@RequiresApi(18)
-class ShadowHelperJbmr2 {
-
-    static class ShadowImpl {
-        View mNormalShadow;
-        View mFocusShadow;
-    }
-
-    /* prepare parent for allowing shadows of a child */
-    public static void prepareParent(ViewGroup parent) {
-        parent.setLayoutMode(ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS);
-    }
-
-    /* add shadows and return a implementation detail object */
-    public static Object addShadow(ViewGroup shadowContainer) {
-        shadowContainer.setLayoutMode(ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS);
-        LayoutInflater inflater = LayoutInflater.from(shadowContainer.getContext());
-        inflater.inflate(R.layout.lb_shadow, shadowContainer, true);
-        ShadowImpl impl = new ShadowImpl();
-        impl.mNormalShadow = shadowContainer.findViewById(R.id.lb_shadow_normal);
-        impl.mFocusShadow = shadowContainer.findViewById(R.id.lb_shadow_focused);
-        return impl;
-    }
-
-    /* set shadow focus level 0 for unfocused 1 for fully focused */
-    public static void setShadowFocusLevel(Object impl, float level) {
-        ShadowImpl shadowImpl = (ShadowImpl) impl;
-        shadowImpl.mNormalShadow.setAlpha(1 - level);
-        shadowImpl.mFocusShadow.setAlpha(level);
-    }
-}
diff --git a/android/support/v17/leanback/widget/ShadowOverlayContainer.java b/android/support/v17/leanback/widget/ShadowOverlayContainer.java
deleted file mode 100644
index 5b71a43..0000000
--- a/android/support/v17/leanback/widget/ShadowOverlayContainer.java
+++ /dev/null
@@ -1,328 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.support.annotation.ColorInt;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * Provides an SDK version-independent wrapper to support shadows, color overlays, and rounded
- * corners.  It's not always preferred to create a ShadowOverlayContainer, use
- * {@link ShadowOverlayHelper} instead.
- * <p>
- * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
- * before using shadow.  Depending on sdk version, optical bounds might be applied
- * to parent.
- * </p>
- * <p>
- * If shadows can appear outside the bounds of the parent view, setClipChildren(false) must
- * be called on the grandparent view.
- * </p>
- * <p>
- * {@link #initialize(boolean, boolean, boolean)} must be first called on the container.
- * Then call {@link #wrap(View)} to insert the wrapped view into the container.
- * </p>
- * <p>
- * Call {@link #setShadowFocusLevel(float)} to control the strength of the shadow (focused shadows
- * cast stronger shadows).
- * </p>
- * <p>
- * Call {@link #setOverlayColor(int)} to control overlay color.
- * </p>
- */
-public class ShadowOverlayContainer extends FrameLayout {
-
-    /**
-     * No shadow.
-     */
-    public static final int SHADOW_NONE = ShadowOverlayHelper.SHADOW_NONE;
-
-    /**
-     * Shadows are fixed.
-     */
-    public static final int SHADOW_STATIC = ShadowOverlayHelper.SHADOW_STATIC;
-
-    /**
-     * Shadows depend on the size, shape, and position of the view.
-     */
-    public static final int SHADOW_DYNAMIC = ShadowOverlayHelper.SHADOW_DYNAMIC;
-
-    private boolean mInitialized;
-    private Object mShadowImpl;
-    private View mWrappedView;
-    private boolean mRoundedCorners;
-    private int mShadowType = SHADOW_NONE;
-    private float mUnfocusedZ;
-    private float mFocusedZ;
-    private int mRoundedCornerRadius;
-    private static final Rect sTempRect = new Rect();
-    private Paint mOverlayPaint;
-    int mOverlayColor;
-
-    /**
-     * Create ShadowOverlayContainer and auto select shadow type.
-     */
-    public ShadowOverlayContainer(Context context) {
-        this(context, null, 0);
-    }
-
-    /**
-     * Create ShadowOverlayContainer and auto select shadow type.
-     */
-    public ShadowOverlayContainer(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * Create ShadowOverlayContainer and auto select shadow type.
-     */
-    public ShadowOverlayContainer(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        useStaticShadow();
-        useDynamicShadow();
-    }
-
-    /**
-     * Create ShadowOverlayContainer with specific shadowType.
-     */
-    ShadowOverlayContainer(Context context,
-            int shadowType, boolean hasColorDimOverlay,
-            float unfocusedZ, float focusedZ, int roundedCornerRadius) {
-        super(context);
-        mUnfocusedZ = unfocusedZ;
-        mFocusedZ = focusedZ;
-        initialize(shadowType, hasColorDimOverlay, roundedCornerRadius);
-    }
-
-    /**
-     * Return true if the platform sdk supports shadow.
-     */
-    public static boolean supportsShadow() {
-        return StaticShadowHelper.getInstance().supportsShadow();
-    }
-
-    /**
-     * Returns true if the platform sdk supports dynamic shadows.
-     */
-    public static boolean supportsDynamicShadow() {
-        return ShadowHelper.getInstance().supportsDynamicShadow();
-    }
-
-    /**
-     * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
-     * before using shadow.  Depending on sdk version, optical bounds might be applied
-     * to parent.
-     */
-    public static void prepareParentForShadow(ViewGroup parent) {
-        StaticShadowHelper.getInstance().prepareParent(parent);
-    }
-
-    /**
-     * Sets the shadow type to {@link #SHADOW_DYNAMIC} if supported.
-     */
-    public void useDynamicShadow() {
-        useDynamicShadow(getResources().getDimension(R.dimen.lb_material_shadow_normal_z),
-                getResources().getDimension(R.dimen.lb_material_shadow_focused_z));
-    }
-
-    /**
-     * Sets the shadow type to {@link #SHADOW_DYNAMIC} if supported and sets the elevation/Z
-     * values to the given parameters.
-     */
-    public void useDynamicShadow(float unfocusedZ, float focusedZ) {
-        if (mInitialized) {
-            throw new IllegalStateException("Already initialized");
-        }
-        if (supportsDynamicShadow()) {
-            mShadowType = SHADOW_DYNAMIC;
-            mUnfocusedZ = unfocusedZ;
-            mFocusedZ = focusedZ;
-        }
-    }
-
-    /**
-     * Sets the shadow type to {@link #SHADOW_STATIC} if supported.
-     */
-    public void useStaticShadow() {
-        if (mInitialized) {
-            throw new IllegalStateException("Already initialized");
-        }
-        if (supportsShadow()) {
-            mShadowType = SHADOW_STATIC;
-        }
-    }
-
-    /**
-     * Returns the shadow type, one of {@link #SHADOW_NONE}, {@link #SHADOW_STATIC}, or
-     * {@link #SHADOW_DYNAMIC}.
-     */
-    public int getShadowType() {
-        return mShadowType;
-    }
-
-    /**
-     * Initialize shadows, color overlay.
-     * @deprecated use {@link ShadowOverlayHelper#createShadowOverlayContainer(Context)} instead.
-     */
-    @Deprecated
-    public void initialize(boolean hasShadow, boolean hasColorDimOverlay) {
-        initialize(hasShadow, hasColorDimOverlay, true);
-    }
-
-    /**
-     * Initialize shadows, color overlay, and rounded corners.  All are optional.
-     * Shadow type are auto-selected based on {@link #useStaticShadow()} and
-     * {@link #useDynamicShadow()} call.
-     * @deprecated use {@link ShadowOverlayHelper#createShadowOverlayContainer(Context)} instead.
-     */
-    @Deprecated
-    public void initialize(boolean hasShadow, boolean hasColorDimOverlay, boolean roundedCorners) {
-        int shadowType;
-        if (!hasShadow) {
-            shadowType = SHADOW_NONE;
-        } else {
-            shadowType = mShadowType;
-        }
-        int roundedCornerRadius = roundedCorners ? getContext().getResources().getDimensionPixelSize(
-                R.dimen.lb_rounded_rect_corner_radius) : 0;
-        initialize(shadowType, hasColorDimOverlay, roundedCornerRadius);
-    }
-
-    /**
-     * Initialize shadows, color overlay, and rounded corners.  All are optional.
-     */
-    void initialize(int shadowType, boolean hasColorDimOverlay, int roundedCornerRadius) {
-        if (mInitialized) {
-            throw new IllegalStateException();
-        }
-        mInitialized = true;
-        mRoundedCornerRadius = roundedCornerRadius;
-        mRoundedCorners = roundedCornerRadius > 0;
-        mShadowType = shadowType;
-        switch (mShadowType) {
-            case SHADOW_DYNAMIC:
-                mShadowImpl = ShadowHelper.getInstance().addDynamicShadow(
-                        this, mUnfocusedZ, mFocusedZ, mRoundedCornerRadius);
-                break;
-            case SHADOW_STATIC:
-                mShadowImpl = StaticShadowHelper.getInstance().addStaticShadow(this);
-                break;
-        }
-        if (hasColorDimOverlay) {
-            setWillNotDraw(false);
-            mOverlayColor = Color.TRANSPARENT;
-            mOverlayPaint = new Paint();
-            mOverlayPaint.setColor(mOverlayColor);
-            mOverlayPaint.setStyle(Paint.Style.FILL);
-        } else {
-            setWillNotDraw(true);
-            mOverlayPaint = null;
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mOverlayPaint != null && mOverlayColor != Color.TRANSPARENT) {
-            canvas.drawRect(mWrappedView.getLeft(), mWrappedView.getTop(),
-                    mWrappedView.getRight(), mWrappedView.getBottom(),
-                    mOverlayPaint);
-        }
-    }
-
-    /**
-     * Set shadow focus level (0 to 1). 0 for unfocused, 1f for fully focused.
-     */
-    public void setShadowFocusLevel(float level) {
-        if (mShadowImpl != null) {
-            ShadowOverlayHelper.setShadowFocusLevel(mShadowImpl, mShadowType, level);
-        }
-    }
-
-    /**
-     * Set color (with alpha) of the overlay.
-     */
-    public void setOverlayColor(@ColorInt int overlayColor) {
-        if (mOverlayPaint != null) {
-            if (overlayColor != mOverlayColor) {
-                mOverlayColor = overlayColor;
-                mOverlayPaint.setColor(overlayColor);
-                invalidate();
-            }
-        }
-    }
-
-    /**
-     * Inserts view into the wrapper.
-     */
-    public void wrap(View view) {
-        if (!mInitialized || mWrappedView != null) {
-            throw new IllegalStateException();
-        }
-        ViewGroup.LayoutParams lp = view.getLayoutParams();
-        if (lp != null) {
-            // if wrapped view has layout params, inherit everything but width/height.
-            // Wrapped view is assigned a FrameLayout.LayoutParams with width and height only.
-            // Margins, etc are assigned to the wrapper and take effect in parent container.
-            ViewGroup.LayoutParams wrapped_lp = new FrameLayout.LayoutParams(lp.width, lp.height);
-            // Uses MATCH_PARENT for MATCH_PARENT, WRAP_CONTENT for WRAP_CONTENT and fixed size,
-            // App can still change wrapped view fixed width/height afterwards.
-            lp.width = lp.width == LayoutParams.MATCH_PARENT
-                    ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
-            lp.height = lp.height == LayoutParams.MATCH_PARENT
-                    ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
-            this.setLayoutParams(lp);
-            addView(view, wrapped_lp);
-        } else {
-            addView(view);
-        }
-        if (mRoundedCorners && mShadowType != SHADOW_DYNAMIC) {
-            RoundedRectHelper.getInstance().setClipToRoundedOutline(this, true);
-        }
-        mWrappedView = view;
-    }
-
-    /**
-     * Returns the wrapper view.
-     */
-    public View getWrappedView() {
-        return mWrappedView;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        if (changed && mWrappedView != null) {
-            sTempRect.left = (int) mWrappedView.getPivotX();
-            sTempRect.top = (int) mWrappedView.getPivotY();
-            offsetDescendantRectToMyCoords(mWrappedView, sTempRect);
-            setPivotX(sTempRect.left);
-            setPivotY(sTempRect.top);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/android/support/v17/leanback/widget/ShadowOverlayHelper.java b/android/support/v17/leanback/widget/ShadowOverlayHelper.java
deleted file mode 100644
index 6458cb4..0000000
--- a/android/support/v17/leanback/widget/ShadowOverlayHelper.java
+++ /dev/null
@@ -1,467 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.system.Settings;
-import android.view.View;
-import android.view.ViewGroup;
-
-
-/**
- * ShadowOverlayHelper is a helper class for shadow, overlay color and rounded corner.
- * There are many choices to implement Shadow, overlay color.
- * Initialize it with ShadowOverlayHelper.Builder and it decides the best strategy based
- * on options user choose and current platform version.
- *
- * <li> For shadow:  it may use 9-patch with opticalBounds or Z-value based shadow for
- *                   API >= 21.  When 9-patch is used, it requires a ShadowOverlayContainer
- *                   to include 9-patch views.
- * <li> For overlay: it may use ShadowOverlayContainer which overrides draw() or it may
- *                   use setForeground(new ColorDrawable()) for API>=23.  The foreground support
- *                   might be disabled if rounded corner is applied due to performance reason.
- * <li> For rounded-corner:  it uses a ViewOutlineProvider for API>=21.
- *
- * There are two different strategies: use Wrapper with a ShadowOverlayContainer;
- * or apply rounded corner, overlay and rounded-corner to the view itself.  Below is an example
- * of how helper is used.
- *
- * <code>
- * ShadowOverlayHelper mHelper = new ShadowOverlayHelper.Builder().
- *         .needsOverlay(true).needsRoundedCorner(true).needsShadow(true)
- *         .build();
- * mHelper.prepareParentForShadow(parentView); // apply optical-bounds for 9-patch shadow.
- * mHelper.setOverlayColor(view, Color.argb(0x80, 0x80, 0x80, 0x80));
- * mHelper.setShadowFocusLevel(view, 1.0f);
- * ...
- * View initializeView(View view) {
- *     if (mHelper.needsWrapper()) {
- *         ShadowOverlayContainer wrapper = mHelper.createShadowOverlayContainer(context);
- *         wrapper.wrap(view);
- *         return wrapper;
- *     } else {
- *         mHelper.onViewCreated(view);
- *         return view;
- *     }
- * }
- * ...
- *
- * </code>
- */
-public final class ShadowOverlayHelper {
-
-    /**
-     * Builder for creating ShadowOverlayHelper.
-     */
-    public static final class Builder {
-
-        private boolean needsOverlay;
-        private boolean needsRoundedCorner;
-        private boolean needsShadow;
-        private boolean preferZOrder = true;
-        private boolean keepForegroundDrawable;
-        private Options options = Options.DEFAULT;
-
-        /**
-         * Set if needs overlay color.
-         * @param needsOverlay   True if needs overlay.
-         * @return  The Builder object itself.
-         */
-        public Builder needsOverlay(boolean needsOverlay) {
-            this.needsOverlay = needsOverlay;
-            return this;
-        }
-
-        /**
-         * Set if needs shadow.
-         * @param needsShadow   True if needs shadow.
-         * @return  The Builder object itself.
-         */
-        public Builder needsShadow(boolean needsShadow) {
-            this.needsShadow = needsShadow;
-            return this;
-        }
-
-        /**
-         * Set if needs rounded corner.
-         * @param needsRoundedCorner   True if needs rounded corner.
-         * @return  The Builder object itself.
-         */
-        public Builder needsRoundedCorner(boolean needsRoundedCorner) {
-            this.needsRoundedCorner = needsRoundedCorner;
-            return this;
-        }
-
-        /**
-         * Set if prefer z-order shadow.  On old devices,  z-order shadow might be slow,
-         * set to false to fall back to static 9-patch shadow.  Recommend to read
-         * from system wide Setting value: see {@link Settings}.
-         *
-         * @param preferZOrder   True if prefer Z shadow.  Default is true.
-         * @return The Builder object itself.
-         */
-        public Builder preferZOrder(boolean preferZOrder) {
-            this.preferZOrder = preferZOrder;
-            return this;
-        }
-
-        /**
-         * Set if not using foreground drawable for overlay color.  For example if
-         * the view has already assigned a foreground drawable for other purposes.
-         * When it's true, helper will use a ShadowOverlayContainer for overlay color.
-         *
-         * @param keepForegroundDrawable   True to keep the original foreground drawable.
-         * @return The Builder object itself.
-         */
-        public Builder keepForegroundDrawable(boolean keepForegroundDrawable) {
-            this.keepForegroundDrawable = keepForegroundDrawable;
-            return this;
-        }
-
-        /**
-         * Set option values e.g. Shadow Z value, rounded corner radius.
-         *
-         * @param options   The Options object to create ShadowOverlayHelper.
-         */
-        public Builder options(Options options) {
-            this.options = options;
-            return this;
-        }
-
-        /**
-         * Create ShadowOverlayHelper object
-         * @param context    The context uses to read Resources settings.
-         * @return           The ShadowOverlayHelper object.
-         */
-        public ShadowOverlayHelper build(Context context) {
-            final ShadowOverlayHelper helper = new ShadowOverlayHelper();
-            helper.mNeedsOverlay = needsOverlay;
-            helper.mNeedsRoundedCorner = needsRoundedCorner && supportsRoundedCorner();
-            helper.mNeedsShadow = needsShadow && supportsShadow();
-
-            if (helper.mNeedsRoundedCorner) {
-                helper.setupRoundedCornerRadius(options, context);
-            }
-
-            // figure out shadow type and if we need use wrapper:
-            if (helper.mNeedsShadow) {
-                // if static shadow is preferred or dynamic shadow is not supported,
-                // use static shadow,  otherwise use dynamic shadow.
-                if (!preferZOrder || !supportsDynamicShadow()) {
-                    helper.mShadowType = SHADOW_STATIC;
-                    // static shadow requires ShadowOverlayContainer to support crossfading
-                    // of two shadow views.
-                    helper.mNeedsWrapper = true;
-                } else {
-                    helper.mShadowType = SHADOW_DYNAMIC;
-                    helper.setupDynamicShadowZ(options, context);
-                    helper.mNeedsWrapper = ((!supportsForeground() || keepForegroundDrawable)
-                            && helper.mNeedsOverlay);
-                }
-            } else {
-                helper.mShadowType = SHADOW_NONE;
-                helper.mNeedsWrapper = ((!supportsForeground() || keepForegroundDrawable)
-                        && helper.mNeedsOverlay);
-            }
-
-            return helper;
-        }
-
-    }
-
-    /**
-     * Option values for ShadowOverlayContainer.
-     */
-    public static final class Options {
-
-        /**
-         * Default Options for values.
-         */
-        public static final Options DEFAULT = new Options();
-
-        private int roundedCornerRadius = 0; // 0 for default value
-        private float dynamicShadowUnfocusedZ = -1; // < 0 for default value
-        private float dynamicShadowFocusedZ = -1;   // < 0 for default value
-        /**
-         * Set value of rounded corner radius.
-         *
-         * @param roundedCornerRadius   Number of pixels of rounded corner radius.
-         *                              Set to 0 to use default settings.
-         * @return  The Options object itself.
-         */
-        public Options roundedCornerRadius(int roundedCornerRadius){
-            this.roundedCornerRadius = roundedCornerRadius;
-            return this;
-        }
-
-        /**
-         * Set value of focused and unfocused Z value for shadow.
-         *
-         * @param unfocusedZ   Number of pixels for unfocused Z value.
-         * @param focusedZ     Number of pixels for focused Z value.
-         * @return  The Options object itself.
-         */
-        public Options dynamicShadowZ(float unfocusedZ, float focusedZ){
-            this.dynamicShadowUnfocusedZ = unfocusedZ;
-            this.dynamicShadowFocusedZ = focusedZ;
-            return this;
-        }
-
-        /**
-         * Get radius of rounded corner in pixels.
-         *
-         * @return Radius of rounded corner in pixels.
-         */
-        public final int getRoundedCornerRadius() {
-            return roundedCornerRadius;
-        }
-
-        /**
-         * Get z value of shadow when a view is not focused.
-         *
-         * @return Z value of shadow when a view is not focused.
-         */
-        public final float getDynamicShadowUnfocusedZ() {
-            return dynamicShadowUnfocusedZ;
-        }
-
-        /**
-         * Get z value of shadow when a view is focused.
-         *
-         * @return Z value of shadow when a view is focused.
-         */
-        public final float getDynamicShadowFocusedZ() {
-            return dynamicShadowFocusedZ;
-        }
-    }
-
-    /**
-     * No shadow.
-     */
-    public static final int SHADOW_NONE = 1;
-
-    /**
-     * Shadows are fixed.
-     */
-    public static final int SHADOW_STATIC = 2;
-
-    /**
-     * Shadows depend on the size, shape, and position of the view.
-     */
-    public static final int SHADOW_DYNAMIC = 3;
-
-    int mShadowType = SHADOW_NONE;
-    boolean mNeedsOverlay;
-    boolean mNeedsRoundedCorner;
-    boolean mNeedsShadow;
-    boolean mNeedsWrapper;
-
-    int mRoundedCornerRadius;
-    float mUnfocusedZ;
-    float mFocusedZ;
-
-    /**
-     * Return true if the platform sdk supports shadow.
-     */
-    public static boolean supportsShadow() {
-        return StaticShadowHelper.getInstance().supportsShadow();
-    }
-
-    /**
-     * Returns true if the platform sdk supports dynamic shadows.
-     */
-    public static boolean supportsDynamicShadow() {
-        return ShadowHelper.getInstance().supportsDynamicShadow();
-    }
-
-    /**
-     * Returns true if the platform sdk supports rounded corner through outline.
-     */
-    public static boolean supportsRoundedCorner() {
-        return RoundedRectHelper.supportsRoundedCorner();
-    }
-
-    /**
-     * Returns true if view.setForeground() is supported.
-     */
-    public static boolean supportsForeground() {
-        return ForegroundHelper.supportsForeground();
-    }
-
-    /*
-     * hide from external, should be only created by ShadowOverlayHelper.Options.
-     */
-    ShadowOverlayHelper() {
-    }
-
-    /**
-     * {@link #prepareParentForShadow(ViewGroup)} must be called on parent of container
-     * before using shadow.  Depending on Shadow type, optical bounds might be applied.
-     */
-    public void prepareParentForShadow(ViewGroup parent) {
-        if (mShadowType == SHADOW_STATIC) {
-            StaticShadowHelper.getInstance().prepareParent(parent);
-        }
-    }
-
-    public int getShadowType() {
-        return mShadowType;
-    }
-
-    public boolean needsOverlay() {
-        return mNeedsOverlay;
-    }
-
-    public boolean needsRoundedCorner() {
-        return mNeedsRoundedCorner;
-    }
-
-    /**
-     * Returns true if a "wrapper" ShadowOverlayContainer is needed.
-     * When needsWrapper() is true,  call {@link #createShadowOverlayContainer(Context)}
-     * to create the wrapper.
-     */
-    public boolean needsWrapper() {
-        return mNeedsWrapper;
-    }
-
-    /**
-     * Create ShadowOverlayContainer for this helper.
-     * @param context   Context to create view.
-     * @return          ShadowOverlayContainer.
-     */
-    public ShadowOverlayContainer createShadowOverlayContainer(Context context) {
-        if (!needsWrapper()) {
-            throw new IllegalArgumentException();
-        }
-        return new ShadowOverlayContainer(context, mShadowType, mNeedsOverlay,
-                mUnfocusedZ, mFocusedZ, mRoundedCornerRadius);
-    }
-
-    /**
-     * Set overlay color for view other than ShadowOverlayContainer.
-     * See also {@link ShadowOverlayContainer#setOverlayColor(int)}.
-     */
-    public static void setNoneWrapperOverlayColor(View view, int color) {
-        Drawable d = ForegroundHelper.getForeground(view);
-        if (d instanceof ColorDrawable) {
-            ((ColorDrawable) d).setColor(color);
-        } else {
-            ForegroundHelper.setForeground(view, new ColorDrawable(color));
-        }
-    }
-
-    /**
-     * Set overlay color for view, it can be a ShadowOverlayContainer if needsWrapper() is true,
-     * or other view type.
-     */
-    public void setOverlayColor(View view, int color) {
-        if (needsWrapper()) {
-            ((ShadowOverlayContainer) view).setOverlayColor(color);
-        } else {
-            setNoneWrapperOverlayColor(view, color);
-        }
-    }
-
-    /**
-     * Must be called when view is created for cases {@link #needsWrapper()} is false.
-     * @param view
-     */
-    public void onViewCreated(View view) {
-        if (!needsWrapper()) {
-            if (!mNeedsShadow) {
-                if (mNeedsRoundedCorner) {
-                    RoundedRectHelper.getInstance().setClipToRoundedOutline(view,
-                            true, mRoundedCornerRadius);
-                }
-            } else {
-                if (mShadowType == SHADOW_DYNAMIC) {
-                    Object tag = ShadowHelper.getInstance().addDynamicShadow(
-                            view, mUnfocusedZ, mFocusedZ, mRoundedCornerRadius);
-                    view.setTag(R.id.lb_shadow_impl, tag);
-                } else if (mNeedsRoundedCorner) {
-                    RoundedRectHelper.getInstance().setClipToRoundedOutline(view,
-                            true, mRoundedCornerRadius);
-                }
-            }
-        }
-    }
-
-    /**
-     * Set shadow focus level (0 to 1). 0 for unfocused, 1 for fully focused.
-     * This is for view other than ShadowOverlayContainer.
-     * See also {@link ShadowOverlayContainer#setShadowFocusLevel(float)}.
-     */
-    public static void setNoneWrapperShadowFocusLevel(View view, float level) {
-        setShadowFocusLevel(getNoneWrapperDynamicShadowImpl(view), SHADOW_DYNAMIC, level);
-    }
-
-    /**
-     * Set shadow focus level (0 to 1). 0 for unfocused, 1 for fully focused.
-     */
-    public void setShadowFocusLevel(View view, float level) {
-        if (needsWrapper()) {
-            ((ShadowOverlayContainer) view).setShadowFocusLevel(level);
-        } else {
-            setShadowFocusLevel(getNoneWrapperDynamicShadowImpl(view), SHADOW_DYNAMIC, level);
-        }
-    }
-
-    void setupDynamicShadowZ(Options options, Context context) {
-        if (options.getDynamicShadowUnfocusedZ() < 0f) {
-            Resources res = context.getResources();
-            mFocusedZ = res.getDimension(R.dimen.lb_material_shadow_focused_z);
-            mUnfocusedZ = res.getDimension(R.dimen.lb_material_shadow_normal_z);
-        } else {
-            mFocusedZ = options.getDynamicShadowFocusedZ();
-            mUnfocusedZ = options.getDynamicShadowUnfocusedZ();
-        }
-    }
-
-    void setupRoundedCornerRadius(Options options, Context context) {
-        if (options.getRoundedCornerRadius() == 0) {
-            Resources res = context.getResources();
-            mRoundedCornerRadius = res.getDimensionPixelSize(
-                        R.dimen.lb_rounded_rect_corner_radius);
-        } else {
-            mRoundedCornerRadius = options.getRoundedCornerRadius();
-        }
-    }
-
-    static Object getNoneWrapperDynamicShadowImpl(View view) {
-        return view.getTag(R.id.lb_shadow_impl);
-    }
-
-    static void setShadowFocusLevel(Object impl, int shadowType, float level) {
-        if (impl != null) {
-            if (level < 0f) {
-                level = 0f;
-            } else if (level > 1f) {
-                level = 1f;
-            }
-            switch (shadowType) {
-                case SHADOW_DYNAMIC:
-                    ShadowHelper.getInstance().setShadowFocusLevel(impl, level);
-                    break;
-                case SHADOW_STATIC:
-                    StaticShadowHelper.getInstance().setShadowFocusLevel(impl, level);
-                    break;
-            }
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/SinglePresenterSelector.java b/android/support/v17/leanback/widget/SinglePresenterSelector.java
deleted file mode 100644
index 7fe5c48..0000000
--- a/android/support/v17/leanback/widget/SinglePresenterSelector.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.v17.leanback.widget;
-
-/**
- * A {@link PresenterSelector} that always returns the same {@link Presenter}.
- * Useful for rows of items of the same type that are all rendered the same way.
- */
-public final class SinglePresenterSelector extends PresenterSelector {
-
-    private final Presenter mPresenter;
-
-    /**
-     * @param presenter The Presenter to return for every item.
-     */
-    public SinglePresenterSelector(Presenter presenter) {
-        mPresenter = presenter;
-    }
-
-    @Override
-    public Presenter getPresenter(Object item) {
-        return mPresenter;
-    }
-
-    @Override
-    public Presenter[] getPresenters() {
-        return new Presenter[]{mPresenter};
-    }
-}
diff --git a/android/support/v17/leanback/widget/SingleRow.java b/android/support/v17/leanback/widget/SingleRow.java
deleted file mode 100644
index 56a3188..0000000
--- a/android/support/v17/leanback/widget/SingleRow.java
+++ /dev/null
@@ -1,194 +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.v17.leanback.widget;
-
-import android.support.annotation.NonNull;
-import android.support.v4.util.CircularIntArray;
-import android.support.v7.widget.RecyclerView;
-
-import java.io.PrintWriter;
-
-/**
- * A Grid with restriction to single row.
- */
-class SingleRow extends Grid {
-
-    private final Location mTmpLocation = new Location(0);
-
-    SingleRow() {
-        setNumRows(1);
-    }
-
-    @Override
-    public final Location getLocation(int index) {
-        // all items are on row 0, share the same Location object.
-        return mTmpLocation;
-    }
-
-    @Override
-    public final void debugPrint(PrintWriter pw) {
-        pw.print("SingleRow<");
-        pw.print(mFirstVisibleIndex);
-        pw.print(",");
-        pw.print(mLastVisibleIndex);
-        pw.print(">");
-        pw.println();
-    }
-
-    int getStartIndexForAppend() {
-        if (mLastVisibleIndex >= 0) {
-            return mLastVisibleIndex + 1;
-        } else if (mStartIndex != START_DEFAULT) {
-            return Math.min(mStartIndex, mProvider.getCount() - 1);
-        } else {
-            return 0;
-        }
-    }
-
-    int getStartIndexForPrepend() {
-        if (mFirstVisibleIndex >= 0) {
-            return mFirstVisibleIndex - 1;
-        } else if (mStartIndex != START_DEFAULT) {
-            return Math.min(mStartIndex, mProvider.getCount() - 1);
-        } else {
-            return mProvider.getCount() - 1;
-        }
-    }
-
-    @Override
-    protected final boolean prependVisibleItems(int toLimit, boolean oneColumnMode) {
-        if (mProvider.getCount() == 0) {
-            return false;
-        }
-        if (!oneColumnMode && checkPrependOverLimit(toLimit)) {
-            return false;
-        }
-        boolean filledOne = false;
-        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;
-                mLastVisibleIndex = mFirstVisibleIndex = index;
-            } else {
-                if (mReversedFlow) {
-                    edge = mProvider.getEdge(index + 1) + mSpacing + size;
-                } else {
-                    edge = mProvider.getEdge(index + 1) - mSpacing - size;
-                }
-                mFirstVisibleIndex = index;
-            }
-            mProvider.addItem(mTmpItem[0], index, size, 0, edge);
-            filledOne = true;
-            if (oneColumnMode || checkPrependOverLimit(toLimit)) {
-                break;
-            }
-        }
-        return filledOne;
-    }
-
-    @Override
-    protected final boolean appendVisibleItems(int toLimit, boolean oneColumnMode) {
-        if (mProvider.getCount() == 0) {
-            return false;
-        }
-        if (!oneColumnMode && checkAppendOverLimit(toLimit)) {
-            // not in one column mode, return immediately if over limit
-            return false;
-        }
-        boolean filledOne = false;
-        for (int index = getStartIndexForAppend(); index < mProvider.getCount(); index++) {
-            int size = mProvider.createItem(index, true, mTmpItem, false);
-            int edge;
-            if (mFirstVisibleIndex < 0 || mLastVisibleIndex< 0) {
-                edge = mReversedFlow ? Integer.MAX_VALUE : Integer.MIN_VALUE;
-                mLastVisibleIndex = mFirstVisibleIndex = index;
-            } else {
-                if (mReversedFlow) {
-                    edge = mProvider.getEdge(index - 1) - mProvider.getSize(index - 1) - mSpacing;
-                } else {
-                    edge = mProvider.getEdge(index - 1) + mProvider.getSize(index - 1) + mSpacing;
-                }
-                mLastVisibleIndex = index;
-            }
-            mProvider.addItem(mTmpItem[0], index, size, 0, edge);
-            filledOne = true;
-            if (oneColumnMode || checkAppendOverLimit(toLimit)) {
-                break;
-            }
-        }
-        return filledOne;
-    }
-
-    @Override
-    public void collectAdjacentPrefetchPositions(int fromLimit, int da,
-        @NonNull RecyclerView.LayoutManager.LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        int indexToPrefetch;
-        int nearestEdge;
-        if (mReversedFlow ? da > 0 : da < 0) {
-            // prefetch next prepend, lower index number
-            if (getFirstVisibleIndex() == 0) {
-                return; // no remaining items to prefetch
-            }
-
-            indexToPrefetch = getStartIndexForPrepend();
-            nearestEdge = mProvider.getEdge(mFirstVisibleIndex)
-                    + (mReversedFlow ? mSpacing : -mSpacing);
-        } else {
-            // prefetch next append, higher index number
-            if (getLastVisibleIndex() == mProvider.getCount() - 1) {
-                return; // no remaining items to prefetch
-            }
-
-            indexToPrefetch = getStartIndexForAppend();
-            int itemSizeWithSpace = mProvider.getSize(mLastVisibleIndex) + mSpacing;
-            nearestEdge = mProvider.getEdge(mLastVisibleIndex)
-                    + (mReversedFlow ? -itemSizeWithSpace : itemSizeWithSpace);
-        }
-
-        int distance = Math.abs(nearestEdge - fromLimit);
-        layoutPrefetchRegistry.addPosition(indexToPrefetch, distance);
-    }
-
-    @Override
-    public final CircularIntArray[] getItemPositionsInRows(int startPos, int endPos) {
-        // all items are on the same row:
-        mTmpItemPositionsInRows[0].clear();
-        mTmpItemPositionsInRows[0].addLast(startPos);
-        mTmpItemPositionsInRows[0].addLast(endPos);
-        return mTmpItemPositionsInRows;
-    }
-
-    @Override
-    protected final int findRowMin(boolean findLarge, int indexLimit, int[] indices) {
-        if (indices != null) {
-            indices[0] = 0;
-            indices[1] = indexLimit;
-        }
-        return mReversedFlow ? mProvider.getEdge(indexLimit) - mProvider.getSize(indexLimit)
-                : mProvider.getEdge(indexLimit);
-    }
-
-    @Override
-    protected final int findRowMax(boolean findLarge, int indexLimit, int[] indices) {
-        if (indices != null) {
-            indices[0] = 0;
-            indices[1] = indexLimit;
-        }
-        return mReversedFlow ? mProvider.getEdge(indexLimit)
-                : mProvider.getEdge(indexLimit) + mProvider.getSize(indexLimit);
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/SparseArrayObjectAdapter.java b/android/support/v17/leanback/widget/SparseArrayObjectAdapter.java
deleted file mode 100644
index c52fd92..0000000
--- a/android/support/v17/leanback/widget/SparseArrayObjectAdapter.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.util.SparseArray;
-
-/**
- * An {@link ObjectAdapter} implemented with a {@link android.util.SparseArray}.
- * This class maintains an array of objects where each object is associated
- * with an integer key which determines its order relative to other objects.
- */
-public class SparseArrayObjectAdapter extends ObjectAdapter {
-    private SparseArray<Object> mItems = new SparseArray<Object>();
-
-    /**
-     * Constructs an adapter with the given {@link PresenterSelector}.
-     */
-    public SparseArrayObjectAdapter(PresenterSelector presenterSelector) {
-        super(presenterSelector);
-    }
-
-    /**
-     * Constructs an adapter with the given {@link Presenter}.
-     */
-    public SparseArrayObjectAdapter(Presenter presenter) {
-        super(presenter);
-    }
-
-    /**
-     * Constructs an adapter.
-     */
-    public SparseArrayObjectAdapter() {
-        super();
-    }
-
-    @Override
-    public int size() {
-        return mItems.size();
-    }
-
-    @Override
-    public Object get(int position) {
-        return mItems.valueAt(position);
-    }
-
-    /**
-     * Returns the index for the given item in the adapter.
-     *
-     * @param item  The item to find in the array.
-     * @return Index of the item, or a negative value if not found.
-     */
-    public int indexOf(Object item) {
-        return mItems.indexOfValue(item);
-    }
-
-    /**
-     * Returns the index for the given key in the adapter.
-     *
-     * @param key The key to find in the array.
-     * @return Index of the item, or a negative value if not found.
-     */
-    public int indexOf(int key) {
-        return mItems.indexOfKey(key);
-    }
-
-    /**
-     * Notify that the content of a range of items changed. Note that this is
-     * not same as items being added or removed.
-     *
-     * @param positionStart The position of first item that has changed.
-     * @param itemCount The count of how many items have changed.
-     */
-    public void notifyArrayItemRangeChanged(int positionStart, int itemCount) {
-        notifyItemRangeChanged(positionStart, itemCount);
-    }
-
-    /**
-     * Sets the item for the given key.
-     *
-     * @param key The key associated with the item.
-     * @param item The item associated with the key.
-     */
-    public void set(int key, Object item) {
-        int index = mItems.indexOfKey(key);
-        if (index >= 0) {
-            if (mItems.valueAt(index) != item) {
-                mItems.setValueAt(index, item);
-                notifyItemRangeChanged(index, 1);
-            }
-        } else {
-            mItems.append(key, item);
-            index = mItems.indexOfKey(key);
-            notifyItemRangeInserted(index, 1);
-        }
-    }
-
-    /**
-     * Clears the given key and associated item from the adapter.
-     *
-     * @param key The key to be cleared.
-     */
-    public void clear(int key) {
-        int index = mItems.indexOfKey(key);
-        if (index >= 0) {
-            mItems.removeAt(index);
-            notifyItemRangeRemoved(index, 1);
-        }
-    }
-
-    /**
-     * Removes all items from this adapter, leaving it empty.
-     */
-    public void clear() {
-        final int itemCount = mItems.size();
-        if (itemCount == 0) {
-            return;
-        }
-        mItems.clear();
-        notifyItemRangeRemoved(0, itemCount);
-    }
-
-    /**
-     * Returns the object for the given key, or null if no mapping for that key exists.
-     */
-    public Object lookup(int key) {
-        return mItems.get(key);
-    }
-
-    @Override
-    public boolean isImmediateNotifySupported() {
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/SpeechOrbView.java b/android/support/v17/leanback/widget/SpeechOrbView.java
deleted file mode 100644
index 90df896..0000000
--- a/android/support/v17/leanback/widget/SpeechOrbView.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package android.support.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-
-/**
- * A subclass of {@link SearchOrbView} that visualizes the state of an ongoing speech recognition.
- */
-public class SpeechOrbView extends SearchOrbView {
-    private final float mSoundLevelMaxZoom;
-    private Colors mListeningOrbColors;
-    private Colors mNotListeningOrbColors;
-
-    private int mCurrentLevel = 0;
-    private boolean mListening = false;
-
-    public SpeechOrbView(Context context) {
-        this(context, null);
-    }
-
-    public SpeechOrbView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SpeechOrbView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        Resources resources = context.getResources();
-        mSoundLevelMaxZoom =
-                resources.getFraction(R.fraction.lb_search_bar_speech_orb_max_level_zoom, 1, 1);
-
-        mNotListeningOrbColors = new Colors(resources.getColor(R.color.lb_speech_orb_not_recording),
-                resources.getColor(R.color.lb_speech_orb_not_recording_pulsed),
-                resources.getColor(R.color.lb_speech_orb_not_recording_icon));
-        mListeningOrbColors = new Colors(resources.getColor(R.color.lb_speech_orb_recording),
-                resources.getColor(R.color.lb_speech_orb_recording),
-                Color.TRANSPARENT);
-
-        showNotListening();
-    }
-
-    @Override
-    int getLayoutResourceId() {
-        return R.layout.lb_speech_orb;
-    }
-
-    /**
-     * Sets default listening state orb color.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setListeningOrbColors(Colors colors) {
-        mListeningOrbColors = colors;
-    }
-
-    /**
-     * Sets default not-listening state orb color.
-     *
-     * @param colors SearchOrbView.Colors.
-     */
-    public void setNotListeningOrbColors(Colors colors) {
-        mNotListeningOrbColors = colors;
-    }
-
-    /**
-     * Sets the view to display listening state.
-     */
-    public void showListening() {
-        setOrbColors(mListeningOrbColors);
-        setOrbIcon(getResources().getDrawable(R.drawable.lb_ic_search_mic));
-        // Assume focused
-        animateOnFocus(true);
-        enableOrbColorAnimation(false);
-        scaleOrbViewOnly(1f);
-        mCurrentLevel = 0;
-        mListening = true;
-    }
-
-    /**
-     * Sets the view to display the not-listening state.
-     */
-    public void showNotListening() {
-        setOrbColors(mNotListeningOrbColors);
-        setOrbIcon(getResources().getDrawable(R.drawable.lb_ic_search_mic_out));
-        animateOnFocus(hasFocus());
-        scaleOrbViewOnly(1f);
-        mListening = false;
-    }
-
-    /**
-     * Sets the sound level while listening to speech.
-     */
-    public void setSoundLevel(int level) {
-        if (!mListening) return;
-
-        // Either ease towards the target level, or decay away from it depending on whether
-        // its higher or lower than the current.
-        if (level > mCurrentLevel) {
-            mCurrentLevel = mCurrentLevel + ((level - mCurrentLevel) / 2);
-        } else {
-            mCurrentLevel = (int) (mCurrentLevel * 0.7f);
-        }
-
-        float zoom = 1f + (mSoundLevelMaxZoom - getFocusedZoom()) * mCurrentLevel / 100;
-
-        scaleOrbViewOnly(zoom);
-    }
-}
diff --git a/android/support/v17/leanback/widget/SpeechRecognitionCallback.java b/android/support/v17/leanback/widget/SpeechRecognitionCallback.java
deleted file mode 100644
index 173444d..0000000
--- a/android/support/v17/leanback/widget/SpeechRecognitionCallback.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.v17.leanback.widget;
-
-/**
- * Interface for receiving notification that speech recognition should be initiated.
- *
- * @deprecated Launching voice recognition activity is no longer supported. App should declare
- *             android.permission.RECORD_AUDIO in AndroidManifest file. See details in
- *             {@link android.support.v17.leanback.app.SearchSupportFragment}.
- */
-@Deprecated
-public interface SpeechRecognitionCallback {
-    /**
-     * Method invoked when speech recognition should be initiated.
-     */
-    void recognizeSpeech();
-}
diff --git a/android/support/v17/leanback/widget/StaggeredGrid.java b/android/support/v17/leanback/widget/StaggeredGrid.java
deleted file mode 100644
index fb47c5b..0000000
--- a/android/support/v17/leanback/widget/StaggeredGrid.java
+++ /dev/null
@@ -1,436 +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.v17.leanback.widget;
-
-import android.support.v4.util.CircularArray;
-import android.support.v4.util.CircularIntArray;
-
-import java.io.PrintWriter;
-
-/**
- * A dynamic data structure that caches staggered grid position information
- * for each individual child. The algorithm ensures that each row will be kept
- * as balanced as possible when prepending and appending a child.
- *
- * <p>
- * You may keep view {@link StaggeredGrid.Location} inside StaggeredGrid as much
- * as possible since prepending and appending views is not symmetric: layout
- * going from 0 to N will likely produce a different result than layout going
- * from N to 0 for the staggered cases. If a user scrolls from 0 to N then
- * scrolls back to 0 and we don't keep history location information, edges of
- * the very beginning of rows will not be aligned. It is recommended to keep a
- * list of tens of thousands of {@link StaggeredGrid.Location}s which will be
- * big enough to remember a typical user's scroll history.
- *
- * <p>
- * This class is abstract and can be replaced with different implementations.
- */
-abstract class StaggeredGrid extends Grid {
-
-    /**
-     * Cached representation of Staggered item.
-     */
-    public static class Location extends Grid.Location {
-        /**
-         * Offset to previous item location.
-         * min_edge(index) - min_edge(index - 1) for non reversed case
-         * max_edge(index) - max_edge(index - 1) for reversed case
-         */
-        public int offset;
-
-        /**
-         * size of the item.
-         */
-        public int size;
-
-        public Location(int row, int offset, int size) {
-            super(row);
-            this.offset = offset;
-            this.size = size;
-        }
-    }
-
-    protected CircularArray<Location> mLocations = new CircularArray<Location>(64);
-
-    // mFirstIndex <= mFirstVisibleIndex <= mLastVisibleIndex
-    //    <= mFirstIndex + mLocations.size() - 1
-    protected int mFirstIndex = -1;
-
-    protected Object mPendingItem;
-    protected int mPendingItemSize;
-
-    /**
-     * Returns index of first item (cached or visible) in the staggered grid.
-     * Returns negative value if no item.
-     */
-    public final int getFirstIndex() {
-        return mFirstIndex;
-    }
-
-    /**
-     * Returns index of last item (cached or visible) in the staggered grid.
-     * Returns negative value if no item.
-     */
-    public final int getLastIndex() {
-        return mFirstIndex + mLocations.size() - 1;
-    }
-
-    /**
-     * Returns the size of the saved {@link Location}s.
-     */
-    public final int getSize() {
-        return mLocations.size();
-    }
-
-    @Override
-    public final Location getLocation(int index) {
-        final int indexInArray = index - mFirstIndex;
-        if (indexInArray < 0 || indexInArray >= mLocations.size()) {
-            return null;
-        }
-        return mLocations.get(indexInArray);
-    }
-
-    @Override
-    public final void debugPrint(PrintWriter pw) {
-        for (int i = 0, size = mLocations.size(); i < size; i++) {
-            Location loc = mLocations.get(i);
-            pw.print("<" + (mFirstIndex + i) + "," + loc.row + ">");
-            pw.print(" ");
-            pw.println();
-        }
-    }
-
-    @Override
-    protected final boolean prependVisibleItems(int toLimit, boolean oneColumnMode) {
-        if (mProvider.getCount() == 0) {
-            return false;
-        }
-        if (!oneColumnMode && checkPrependOverLimit(toLimit)) {
-            return false;
-        }
-        try {
-            if (prependVisbleItemsWithCache(toLimit, oneColumnMode)) {
-                return true;
-            }
-            return prependVisibleItemsWithoutCache(toLimit, oneColumnMode);
-        } finally {
-            mTmpItem[0] = null;
-            mPendingItem = null;
-        }
-    }
-
-    /**
-     * Prepends items using cached locations,  returning true if toLimit is reached.
-     * This method should only be called by prependVisibleItems().
-     */
-    protected final boolean prependVisbleItemsWithCache(int toLimit, boolean oneColumnMode) {
-        if (mLocations.size() == 0) {
-            return false;
-        }
-        int itemIndex;
-        int edge;
-        int offset;
-        if (mFirstVisibleIndex >= 0) {
-            // prepend visible items from first visible index
-            edge = mProvider.getEdge(mFirstVisibleIndex);
-            offset = getLocation(mFirstVisibleIndex).offset;
-            itemIndex = mFirstVisibleIndex - 1;
-        } else {
-            // prepend first visible item
-            edge = Integer.MAX_VALUE;
-            offset = 0;
-            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
-            if (itemIndex > getLastIndex() || itemIndex < getFirstIndex() - 1) {
-                // if the item is not within or adjacent to cached items, clear cache.
-                mLocations.clear();
-                return false;
-            } else if (itemIndex < getFirstIndex()) {
-                // if the item is adjacent to first index, should prepend without cache.
-                return false;
-            }
-        }
-        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, false);
-            if (size != loc.size) {
-                mLocations.removeFromStart(itemIndex + 1 - mFirstIndex);
-                mFirstIndex = mFirstVisibleIndex;
-                // pending item will be added in prependVisibleItemsWithoutCache
-                mPendingItem = mTmpItem[0];
-                mPendingItemSize = size;
-                return false;
-            }
-            mFirstVisibleIndex = itemIndex;
-            if (mLastVisibleIndex < 0) {
-                mLastVisibleIndex = itemIndex;
-            }
-            mProvider.addItem(mTmpItem[0], itemIndex, size, rowIndex, edge - offset);
-            if (!oneColumnMode && checkPrependOverLimit(toLimit)) {
-                return true;
-            }
-            edge = mProvider.getEdge(itemIndex);
-            offset = loc.offset;
-            // Check limit after filled a full column
-            if (rowIndex == 0) {
-                if (oneColumnMode) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Calculate offset of item after last cached item.
-     */
-    private int calculateOffsetAfterLastItem(int row) {
-        // Find a cached item in same row, if not found, just use last item.
-        int cachedIndex = getLastIndex();
-        boolean foundCachedItemInSameRow = false;
-        while (cachedIndex >= mFirstIndex) {
-            Location loc = getLocation(cachedIndex);
-            if (loc.row == row) {
-                foundCachedItemInSameRow = true;
-                break;
-            }
-            cachedIndex--;
-        }
-        if (!foundCachedItemInSameRow) {
-            cachedIndex = getLastIndex();
-        }
-        // Assuming the cachedIndex is next to item on the same row, so the
-        // sum of offset of [cachedIndex + 1, itemIndex] should be size of the
-        // cached item plus spacing.
-        int offset = isReversedFlow() ?  -getLocation(cachedIndex).size - mSpacing:
-                getLocation(cachedIndex).size + mSpacing;
-        for (int i = cachedIndex + 1; i <= getLastIndex(); i++) {
-            offset -= getLocation(i).offset;
-        }
-        return offset;
-    }
-
-
-    /**
-     * This implements the algorithm of layout staggered grid, the method should only be called by
-     * prependVisibleItems().
-     */
-    protected abstract boolean prependVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode);
-
-    /**
-     * Prepends one visible item with new Location info.  Only called from
-     * prependVisibleItemsWithoutCache().
-     */
-    protected final int prependVisibleItemToRow(int itemIndex, int rowIndex, int edge) {
-        int offset;
-        if (mFirstVisibleIndex >= 0) {
-            if (mFirstVisibleIndex != getFirstIndex() || mFirstVisibleIndex != itemIndex + 1) {
-                // should never hit this when we prepend a new item with a new Location object.
-                throw new IllegalStateException();
-            }
-        }
-        Location oldFirstLoc = mFirstIndex >= 0 ? getLocation(mFirstIndex) : null;
-        int oldFirstEdge = mProvider.getEdge(mFirstIndex);
-        Location loc = new Location(rowIndex, 0, 0);
-        mLocations.addFirst(loc);
-        Object item;
-        if (mPendingItem != null) {
-            loc.size = mPendingItemSize;
-            item = mPendingItem;
-            mPendingItem = null;
-        } else {
-            loc.size = mProvider.createItem(itemIndex, false, mTmpItem, false);
-            item = mTmpItem[0];
-        }
-        mFirstIndex = mFirstVisibleIndex = itemIndex;
-        if (mLastVisibleIndex < 0) {
-            mLastVisibleIndex = itemIndex;
-        }
-        int thisEdge = !mReversedFlow ? edge - loc.size : edge + loc.size;
-        if (oldFirstLoc != null) {
-            oldFirstLoc.offset = oldFirstEdge - thisEdge;
-        }
-        mProvider.addItem(item, itemIndex, loc.size, rowIndex, thisEdge);
-        return loc.size;
-    }
-
-    @Override
-    protected final boolean appendVisibleItems(int toLimit, boolean oneColumnMode) {
-        if (mProvider.getCount() == 0) {
-            return false;
-        }
-        if (!oneColumnMode && checkAppendOverLimit(toLimit)) {
-            return false;
-        }
-        try {
-            if (appendVisbleItemsWithCache(toLimit, oneColumnMode)) {
-                return true;
-            }
-            return appendVisibleItemsWithoutCache(toLimit, oneColumnMode);
-        } finally {
-            mTmpItem[0] = null;
-            mPendingItem = null;
-        }
-    }
-
-    /**
-     * Appends items using cached locations,  returning true if at least one item is appended
-     * and (oneColumnMode is true or reach limit and aboveIndex).
-     * This method should only be called by appendVisibleItems()
-     */
-    protected final boolean appendVisbleItemsWithCache(int toLimit, boolean oneColumnMode) {
-        if (mLocations.size() == 0) {
-            return false;
-        }
-        final int count = mProvider.getCount();
-        int itemIndex;
-        int edge;
-        if (mLastVisibleIndex >= 0) {
-            // append visible items from last visible index
-            itemIndex = mLastVisibleIndex + 1;
-            edge = mProvider.getEdge(mLastVisibleIndex);
-        } else {
-            // append first visible item
-            edge = Integer.MAX_VALUE;
-            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
-            if (itemIndex > getLastIndex() + 1 || itemIndex < getFirstIndex()) {
-                // if the item is not within or adjacent to cached items, clear cache.
-                mLocations.clear();
-                return false;
-            } else if (itemIndex > getLastIndex()) {
-                // if the item is adjacent to first index, should prepend without cache.
-                return false;
-            }
-        }
-        int lastIndex = getLastIndex();
-        for (; itemIndex < count && itemIndex <= lastIndex; itemIndex++) {
-            Location loc = getLocation(itemIndex);
-            if (edge != Integer.MAX_VALUE) {
-                edge = edge + loc.offset;
-            }
-            int rowIndex = loc.row;
-            int size = mProvider.createItem(itemIndex, true, mTmpItem, false);
-            if (size != loc.size) {
-                loc.size = size;
-                mLocations.removeFromEnd(lastIndex - itemIndex);
-                lastIndex = itemIndex;
-            }
-            mLastVisibleIndex = itemIndex;
-            if (mFirstVisibleIndex < 0) {
-                mFirstVisibleIndex = itemIndex;
-            }
-            mProvider.addItem(mTmpItem[0], itemIndex, size, rowIndex, edge);
-            if (!oneColumnMode && checkAppendOverLimit(toLimit)) {
-                return true;
-            }
-            if (edge == Integer.MAX_VALUE) {
-                edge = mProvider.getEdge(itemIndex);
-            }
-            // Check limit after filled a full column
-            if (rowIndex == mNumRows - 1) {
-                if (oneColumnMode) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * algorithm of layout staggered grid, this method should only be called by
-     * appendVisibleItems().
-     */
-    protected abstract boolean appendVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode);
-
-    /**
-     * Appends one visible item with new Location info.  Only called from
-     * appendVisibleItemsWithoutCache().
-     */
-    protected final int appendVisibleItemToRow(int itemIndex, int rowIndex, int location) {
-        int offset;
-        if (mLastVisibleIndex >= 0) {
-            if (mLastVisibleIndex != getLastIndex() || mLastVisibleIndex != itemIndex - 1) {
-                // should never hit this when we append a new item with a new Location object.
-                throw new IllegalStateException();
-            }
-        }
-        if (mLastVisibleIndex < 0) {
-            // if we append first visible item after existing cached items,  we need update
-            // the offset later when prependVisbleItemsWithCache()
-            if (mLocations.size() > 0 && itemIndex == getLastIndex() + 1) {
-                offset = calculateOffsetAfterLastItem(rowIndex);
-            } else {
-                offset = 0;
-            }
-        } else {
-            offset = location - mProvider.getEdge(mLastVisibleIndex);
-        }
-        Location loc = new Location(rowIndex, offset, 0);
-        mLocations.addLast(loc);
-        Object item;
-        if (mPendingItem != null) {
-            loc.size = mPendingItemSize;
-            item = mPendingItem;
-            mPendingItem = null;
-        } else {
-            loc.size = mProvider.createItem(itemIndex, true, mTmpItem, false);
-            item = mTmpItem[0];
-        }
-        if (mLocations.size() == 1) {
-            mFirstIndex = mFirstVisibleIndex = mLastVisibleIndex = itemIndex;
-        } else {
-            if (mLastVisibleIndex < 0) {
-                mFirstVisibleIndex = mLastVisibleIndex = itemIndex;
-            } else {
-                mLastVisibleIndex++;
-            }
-        }
-        mProvider.addItem(item, itemIndex, loc.size, rowIndex, location);
-        return loc.size;
-    }
-
-    @Override
-    public final CircularIntArray[] getItemPositionsInRows(int startPos, int endPos) {
-        for (int i = 0; i < mNumRows; i++) {
-            mTmpItemPositionsInRows[i].clear();
-        }
-        if (startPos >= 0) {
-            for (int i = startPos; i <= endPos; i++) {
-                CircularIntArray row = mTmpItemPositionsInRows[getLocation(i).row];
-                if (row.size() > 0 && row.getLast() == i - 1) {
-                    // update continuous range
-                    row.popLast();
-                    row.addLast(i);
-                } else {
-                    // add single position
-                    row.addLast(i);
-                    row.addLast(i);
-                }
-            }
-        }
-        return mTmpItemPositionsInRows;
-    }
-
-    @Override
-    public void invalidateItemsAfter(int index) {
-        super.invalidateItemsAfter(index);
-        mLocations.removeFromEnd(getLastIndex() - index + 1);
-        if (mLocations.size() == 0) {
-            mFirstIndex = -1;
-        }
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/StaggeredGridDefault.java b/android/support/v17/leanback/widget/StaggeredGridDefault.java
deleted file mode 100644
index f4c0141..0000000
--- a/android/support/v17/leanback/widget/StaggeredGridDefault.java
+++ /dev/null
@@ -1,430 +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.v17.leanback.widget;
-
-/**
- * A default implementation of {@link StaggeredGrid}.
- *
- * This implementation tries to fill items in consecutive row order. The next
- * item is always in same row or in the next row.
- */
-final class StaggeredGridDefault extends StaggeredGrid {
-
-    /**
-     * Returns the max edge value of item (visible or cached) in a row.  This
-     * will be the place to append or prepend item not in cache.
-     */
-    int getRowMax(int rowIndex) {
-        if (mFirstVisibleIndex < 0) {
-            return Integer.MIN_VALUE;
-        }
-        if (mReversedFlow) {
-            int edge = mProvider.getEdge(mFirstVisibleIndex);
-            if (getLocation(mFirstVisibleIndex).row == rowIndex) {
-                return edge;
-            }
-            for (int i = mFirstVisibleIndex + 1; i <= getLastIndex(); i++) {
-                Location loc = getLocation(i);
-                edge += loc.offset;
-                if (loc.row == rowIndex) {
-                    return edge;
-                }
-            }
-        } else {
-            int edge = mProvider.getEdge(mLastVisibleIndex);
-            Location loc = getLocation(mLastVisibleIndex);
-            if (loc.row == rowIndex) {
-                return edge + loc.size;
-            }
-            for (int i = mLastVisibleIndex - 1; i >= getFirstIndex(); i--) {
-                edge -= loc.offset;
-                loc = getLocation(i);
-                if (loc.row == rowIndex) {
-                    return edge + loc.size;
-                }
-            }
-        }
-        return Integer.MIN_VALUE;
-    }
-
-    /**
-     * Returns the min edge value of item (visible or cached) in a row.  This
-     * will be the place to prepend or append item not in cache.
-     */
-    int getRowMin(int rowIndex) {
-        if (mFirstVisibleIndex < 0) {
-            return Integer.MAX_VALUE;
-        }
-        if (mReversedFlow) {
-            int edge = mProvider.getEdge(mLastVisibleIndex);
-            Location loc = getLocation(mLastVisibleIndex);
-            if (loc.row == rowIndex) {
-                return edge - loc.size;
-            }
-            for (int i = mLastVisibleIndex - 1; i >= getFirstIndex(); i--) {
-                edge -= loc.offset;
-                loc = getLocation(i);
-                if (loc.row == rowIndex) {
-                    return edge - loc.size;
-                }
-            }
-        } else {
-            int edge = mProvider.getEdge(mFirstVisibleIndex);
-            if (getLocation(mFirstVisibleIndex).row == rowIndex) {
-                return edge;
-            }
-            for (int i = mFirstVisibleIndex + 1; i <= getLastIndex() ; i++) {
-                Location loc = getLocation(i);
-                edge += loc.offset;
-                if (loc.row == rowIndex) {
-                    return edge;
-                }
-            }
-        }
-        return Integer.MAX_VALUE;
-    }
-
-    /**
-     * Note this method has assumption that item is filled either in the same row
-     * next row of last item.  Search until row index wrapped.
-     */
-    @Override
-    public int findRowMax(boolean findLarge, int indexLimit, int[] indices) {
-        int value;
-        int edge = mProvider.getEdge(indexLimit);
-        Location loc = getLocation(indexLimit);
-        int row = loc.row;
-        int index = indexLimit;
-        int visitedRows = 1;
-        int visitRow = row;
-        if (mReversedFlow) {
-            value = edge;
-            for (int i = indexLimit + 1; visitedRows < mNumRows && i <= mLastVisibleIndex; i++) {
-                loc = getLocation(i);
-                edge += loc.offset;
-                if (loc.row != visitRow) {
-                    visitRow = loc.row;
-                    visitedRows++;
-                    if (findLarge ? edge > value : edge < value) {
-                        row = visitRow;
-                        value = edge;
-                        index = i;
-                    }
-                }
-            }
-        } else {
-            value = edge + mProvider.getSize(indexLimit);
-            for (int i = indexLimit - 1; visitedRows < mNumRows && i >= mFirstVisibleIndex; i--) {
-                edge -= loc.offset;
-                loc = getLocation(i);
-                if (loc.row != visitRow) {
-                    visitRow = loc.row;
-                    visitedRows++;
-                    int newValue = edge + mProvider.getSize(i);
-                    if (findLarge ? newValue > value : newValue < value) {
-                        row = visitRow;
-                        value = newValue;
-                        index = i;
-                    }
-                }
-            }
-        }
-        if (indices != null) {
-            indices[0] = row;
-            indices[1] = index;
-        }
-        return value;
-    }
-
-    /**
-     * Note this method has assumption that item is filled either in the same row
-     * next row of last item.  Search until row index wrapped.
-     */
-    @Override
-    public int findRowMin(boolean findLarge, int indexLimit, int[] indices) {
-        int value;
-        int edge = mProvider.getEdge(indexLimit);
-        Location loc = getLocation(indexLimit);
-        int row = loc.row;
-        int index = indexLimit;
-        int visitedRows = 1;
-        int visitRow = row;
-        if (mReversedFlow) {
-            value = edge - mProvider.getSize(indexLimit);
-            for (int i = indexLimit - 1; visitedRows < mNumRows && i >= mFirstVisibleIndex; i--) {
-                edge -= loc.offset;
-                loc = getLocation(i);
-                if (loc.row != visitRow) {
-                    visitRow = loc.row;
-                    visitedRows++;
-                    int newValue = edge - mProvider.getSize(i);
-                    if (findLarge ? newValue > value : newValue < value) {
-                        value = newValue;
-                        row = visitRow;
-                        index = i;
-                    }
-                }
-            }
-        } else {
-            value = edge;
-            for (int i = indexLimit + 1; visitedRows < mNumRows && i <= mLastVisibleIndex; i++) {
-                loc = getLocation(i);
-                edge += loc.offset;
-                if (loc.row != visitRow) {
-                    visitRow = loc.row;
-                    visitedRows++;
-                    if (findLarge ? edge > value : edge < value) {
-                        value = edge;
-                        row = visitRow;
-                        index = i;
-                    }
-                }
-            }
-        }
-        if (indices != null) {
-            indices[0] = row;
-            indices[1] = index;
-        }
-        return value;
-    }
-
-    private int findRowEdgeLimitSearchIndex(boolean append) {
-        boolean wrapped = false;
-        if (append) {
-            for (int index = mLastVisibleIndex; index >= mFirstVisibleIndex; index--) {
-                int row = getLocation(index).row;
-                if (row == 0) {
-                    wrapped = true;
-                } else if (wrapped && row == mNumRows - 1) {
-                    return index;
-                }
-            }
-        } else {
-            for (int index = mFirstVisibleIndex; index <= mLastVisibleIndex; index++) {
-                int row = getLocation(index).row;
-                if (row == mNumRows - 1) {
-                    wrapped = true;
-                } else if (wrapped && row == 0) {
-                    return index;
-                }
-            }
-        }
-        return -1;
-    }
-
-    @Override
-    protected boolean appendVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode) {
-        final int count = mProvider.getCount();
-        int itemIndex;
-        int rowIndex;
-        int edgeLimit;
-        boolean edgeLimitIsValid;
-        if (mLastVisibleIndex >= 0) {
-            if (mLastVisibleIndex < getLastIndex()) {
-                // should fill using cache instead
-                return false;
-            }
-            itemIndex = mLastVisibleIndex + 1;
-            rowIndex = getLocation(mLastVisibleIndex).row;
-            // find start item index of "previous column"
-            int edgeLimitSearchIndex = findRowEdgeLimitSearchIndex(true);
-            if (edgeLimitSearchIndex < 0) {
-                // if "previous column" is not found, using edgeLimit of
-                // first row currently in grid
-                edgeLimit = Integer.MIN_VALUE;
-                for (int i = 0; i < mNumRows; i++) {
-                    edgeLimit = mReversedFlow ? getRowMin(i) : getRowMax(i);
-                    if (edgeLimit != Integer.MIN_VALUE) {
-                        break;
-                    }
-                }
-            } else {
-                edgeLimit = mReversedFlow ? findRowMin(false, edgeLimitSearchIndex, null) :
-                        findRowMax(true, edgeLimitSearchIndex, null);
-            }
-            if (mReversedFlow ? getRowMin(rowIndex) <= edgeLimit
-                    : getRowMax(rowIndex) >= edgeLimit) {
-                // if current row exceeds previous column, fill from next row
-                rowIndex = rowIndex + 1;
-                if (rowIndex == mNumRows) {
-                    // start a new column and using edge limit of current column
-                    rowIndex = 0;
-                    edgeLimit = mReversedFlow ? findRowMin(false, null) : findRowMax(true, null);
-                }
-            }
-            edgeLimitIsValid = true;
-        } else {
-            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
-            // if there are cached items,  put on next row of last cached item.
-            rowIndex = (mLocations.size() > 0 ? getLocation(getLastIndex()).row + 1 : itemIndex)
-                    % mNumRows;
-            edgeLimit = 0;
-            edgeLimitIsValid = false;
-        }
-
-        boolean filledOne = false;
-        while (true) {
-            // find end-most row edge (.high is biggest, or .low is smallest in reversed flow)
-            // fill from current row till last row so that each row will grow longer than
-            // the previous highest row.
-            for (; rowIndex < mNumRows; rowIndex++) {
-                // fill one item to a row
-                if (itemIndex == count || (!oneColumnMode && checkAppendOverLimit(toLimit))) {
-                    return filledOne;
-                }
-                int location = mReversedFlow ? getRowMin(rowIndex) : getRowMax(rowIndex);
-                if (location == Integer.MAX_VALUE || location == Integer.MIN_VALUE) {
-                    // nothing on the row
-                    if (rowIndex == 0) {
-                        location = mReversedFlow ? getRowMin(mNumRows - 1) : getRowMax(mNumRows - 1);
-                        if (location != Integer.MAX_VALUE && location != Integer.MIN_VALUE) {
-                            location = location + (mReversedFlow ? -mSpacing : mSpacing);
-                        }
-                    } else {
-                        location = mReversedFlow ? getRowMax(rowIndex - 1) : getRowMin(rowIndex - 1);
-                    }
-                } else {
-                    location = location + (mReversedFlow ? -mSpacing : mSpacing);
-                }
-                int size = appendVisibleItemToRow(itemIndex++, rowIndex, location);
-                filledOne = true;
-                // fill more item to the row to make sure this row is longer than
-                // the previous highest row.
-                if (edgeLimitIsValid) {
-                    while (mReversedFlow ? location - size > edgeLimit :
-                            location + size < edgeLimit) {
-                        if (itemIndex == count || (!oneColumnMode && checkAppendOverLimit(toLimit))) {
-                            return filledOne;
-                        }
-                        location = location + (mReversedFlow ? - size - mSpacing : size + mSpacing);
-                        size = appendVisibleItemToRow(itemIndex++, rowIndex, location);
-                    }
-                } else {
-                    edgeLimitIsValid = true;
-                    edgeLimit = mReversedFlow ? getRowMin(rowIndex) : getRowMax(rowIndex);
-                }
-            }
-            if (oneColumnMode) {
-                return filledOne;
-            }
-            edgeLimit = mReversedFlow ? findRowMin(false, null) : findRowMax(true, null);
-            // start fill from row 0 again
-            rowIndex = 0;
-        }
-    }
-
-    @Override
-    protected boolean prependVisibleItemsWithoutCache(int toLimit, boolean oneColumnMode) {
-        int itemIndex;
-        int rowIndex;
-        int edgeLimit;
-        boolean edgeLimitIsValid;
-        if (mFirstVisibleIndex >= 0) {
-            if (mFirstVisibleIndex > getFirstIndex()) {
-                // should fill using cache instead
-                return false;
-            }
-            itemIndex = mFirstVisibleIndex - 1;
-            rowIndex = getLocation(mFirstVisibleIndex).row;
-            // find start item index of "previous column"
-            int edgeLimitSearchIndex = findRowEdgeLimitSearchIndex(false);
-            if (edgeLimitSearchIndex < 0) {
-                // if "previous column" is not found, using edgeLimit of
-                // last row currently in grid and fill from upper row
-                rowIndex = rowIndex - 1;
-                edgeLimit = Integer.MAX_VALUE;
-                for (int i = mNumRows - 1; i >= 0; i--) {
-                    edgeLimit = mReversedFlow ? getRowMax(i) : getRowMin(i);
-                    if (edgeLimit != Integer.MAX_VALUE) {
-                        break;
-                    }
-                }
-            } else {
-                edgeLimit = mReversedFlow ? findRowMax(true, edgeLimitSearchIndex, null) :
-                        findRowMin(false, edgeLimitSearchIndex, null);
-            }
-            if (mReversedFlow ? getRowMax(rowIndex) >= edgeLimit
-                    : getRowMin(rowIndex) <= edgeLimit) {
-                // if current row exceeds previous column, fill from next row
-                rowIndex = rowIndex - 1;
-                if (rowIndex < 0) {
-                    // start a new column and using edge limit of current column
-                    rowIndex = mNumRows - 1;
-                    edgeLimit = mReversedFlow ? findRowMax(true, null) :
-                            findRowMin(false, null);
-                }
-            }
-            edgeLimitIsValid = true;
-        } else {
-            itemIndex = mStartIndex != START_DEFAULT ? mStartIndex : 0;
-            // if there are cached items,  put on previous row of first cached item.
-            rowIndex = (mLocations.size() >= 0 ? getLocation(getFirstIndex()).row + mNumRows - 1
-                    : itemIndex) % mNumRows;
-            edgeLimit = 0;
-            edgeLimitIsValid = false;
-        }
-        boolean filledOne = false;
-        while (true) {
-            // find start-most row edge (.low is smallest, or .high is largest in reversed flow)
-            // fill from current row till first row so that each row will grow longer than
-            // the previous lowest row.
-            for (; rowIndex >= 0; rowIndex--) {
-                // fill one item to a row
-                if (itemIndex < 0 || (!oneColumnMode && checkPrependOverLimit(toLimit))) {
-                    return filledOne;
-                }
-                int location = mReversedFlow ? getRowMax(rowIndex) : getRowMin(rowIndex);
-                if (location == Integer.MAX_VALUE || location == Integer.MIN_VALUE) {
-                    // nothing on the row
-                    if (rowIndex == mNumRows - 1) {
-                        location = mReversedFlow ? getRowMax(0) : getRowMin(0);
-                        if (location != Integer.MAX_VALUE && location != Integer.MIN_VALUE) {
-                            location = location + (mReversedFlow ? mSpacing : -mSpacing);
-                        }
-                    } else {
-                        location = mReversedFlow ? getRowMin(rowIndex + 1) : getRowMax(rowIndex + 1);
-                    }
-                } else {
-                    location = location + (mReversedFlow ? mSpacing : -mSpacing);
-                }
-                int size = prependVisibleItemToRow(itemIndex--, rowIndex, location);
-                filledOne = true;
-
-                // fill more item to the row to make sure this row is longer than
-                // the previous highest row.
-                if (edgeLimitIsValid) {
-                    while (mReversedFlow ? location + size < edgeLimit :
-                            location - size > edgeLimit) {
-                        if (itemIndex < 0 || (!oneColumnMode && checkPrependOverLimit(toLimit))) {
-                            return filledOne;
-                        }
-                        location = location + (mReversedFlow ? size + mSpacing : -size - mSpacing);
-                        size = prependVisibleItemToRow(itemIndex--, rowIndex, location);
-                    }
-                } else {
-                    edgeLimitIsValid = true;
-                    edgeLimit = mReversedFlow ? getRowMax(rowIndex) : getRowMin(rowIndex);
-                }
-            }
-            if (oneColumnMode) {
-                return filledOne;
-            }
-            edgeLimit = mReversedFlow ? findRowMax(true, null) : findRowMin(false, null);
-            // start fill from last row again
-            rowIndex = mNumRows - 1;
-        }
-    }
-
-
-}
diff --git a/android/support/v17/leanback/widget/StaticShadowHelper.java b/android/support/v17/leanback/widget/StaticShadowHelper.java
deleted file mode 100644
index 436668f..0000000
--- a/android/support/v17/leanback/widget/StaticShadowHelper.java
+++ /dev/null
@@ -1,121 +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.v17.leanback.widget;
-
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.view.ViewGroup;
-
-
-/**
- * Helper for static (nine patch) shadows.
- */
-final class StaticShadowHelper {
-
-    final static StaticShadowHelper sInstance = new StaticShadowHelper();
-    boolean mSupportsShadow;
-    ShadowHelperVersionImpl mImpl;
-
-    /**
-     * Interface implemented by classes that support Shadow.
-     */
-    static interface ShadowHelperVersionImpl {
-        public void prepareParent(ViewGroup parent);
-        public Object addStaticShadow(ViewGroup shadowContainer);
-        public void setShadowFocusLevel(Object impl, float level);
-    }
-
-    /**
-     * Interface used when we do not support Shadow animations.
-     */
-    private static final class ShadowHelperStubImpl implements ShadowHelperVersionImpl {
-        ShadowHelperStubImpl() {
-        }
-
-        @Override
-        public void prepareParent(ViewGroup parent) {
-            // do nothing
-        }
-
-        @Override
-        public Object addStaticShadow(ViewGroup shadowContainer) {
-            // do nothing
-            return null;
-        }
-
-        @Override
-        public void setShadowFocusLevel(Object impl, float level) {
-            // do nothing
-        }
-    }
-
-    /**
-     * Implementation used on JBMR2 (and above).
-     */
-    @RequiresApi(19)
-    private static final class ShadowHelperJbmr2Impl implements ShadowHelperVersionImpl {
-        ShadowHelperJbmr2Impl() {
-        }
-
-        @Override
-        public void prepareParent(ViewGroup parent) {
-            ShadowHelperJbmr2.prepareParent(parent);
-        }
-
-        @Override
-        public Object addStaticShadow(ViewGroup shadowContainer) {
-            return ShadowHelperJbmr2.addShadow(shadowContainer);
-        }
-
-        @Override
-        public void setShadowFocusLevel(Object impl, float level) {
-            ShadowHelperJbmr2.setShadowFocusLevel(impl, level);
-        }
-    }
-
-    /**
-     * Returns the StaticShadowHelper.
-     */
-    private StaticShadowHelper() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            mSupportsShadow = true;
-            mImpl = new ShadowHelperJbmr2Impl();
-        } else {
-            mSupportsShadow = false;
-            mImpl = new ShadowHelperStubImpl();
-        }
-    }
-
-    public static StaticShadowHelper getInstance() {
-        return sInstance;
-    }
-
-    public boolean supportsShadow() {
-        return mSupportsShadow;
-    }
-
-    public void prepareParent(ViewGroup parent) {
-        mImpl.prepareParent(parent);
-    }
-
-    public Object addStaticShadow(ViewGroup shadowContainer) {
-        return mImpl.addStaticShadow(shadowContainer);
-    }
-
-    public void setShadowFocusLevel(Object impl, float level) {
-        mImpl.setShadowFocusLevel(impl, level);
-    }
-}
diff --git a/android/support/v17/leanback/widget/StreamingTextView.java b/android/support/v17/leanback/widget/StreamingTextView.java
deleted file mode 100644
index 0b8781c..0000000
--- a/android/support/v17/leanback/widget/StreamingTextView.java
+++ /dev/null
@@ -1,293 +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.v17.leanback.widget;
-
-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;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.ReplacementSpan;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Property;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.EditText;
-
-import java.util.List;
-import java.util.Random;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Shows the recognized text as a continuous stream of words.
- */
-class StreamingTextView extends EditText {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "StreamingTextView";
-
-    private static final float TEXT_DOT_SCALE = 1.3F;
-    private static final boolean DOTS_FOR_STABLE = false;
-    private static final boolean DOTS_FOR_PENDING = true;
-    static final boolean ANIMATE_DOTS_FOR_PENDING = true;
-
-    private static final long STREAM_UPDATE_DELAY_MILLIS = 50;
-
-    private static final Pattern SPLIT_PATTERN = Pattern.compile("\\S+");
-
-    private static final Property<StreamingTextView,Integer> STREAM_POSITION_PROPERTY =
-            new Property<StreamingTextView,Integer>(Integer.class, "streamPosition") {
-
-        @Override
-        public Integer get(StreamingTextView view) {
-            return view.getStreamPosition();
-        }
-
-        @Override
-        public void set(StreamingTextView view, Integer value) {
-            view.setStreamPosition(value);
-        }
-    };
-
-    final Random mRandom = new Random();
-
-    Bitmap mOneDot;
-    Bitmap mTwoDot;
-
-    int mStreamPosition;
-    private ObjectAnimator mStreamingAnimation;
-
-    public StreamingTextView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public StreamingTextView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mOneDot = getScaledBitmap(R.drawable.lb_text_dot_one, TEXT_DOT_SCALE);
-        mTwoDot = getScaledBitmap(R.drawable.lb_text_dot_two, TEXT_DOT_SCALE);
-
-        reset();
-    }
-
-    private Bitmap getScaledBitmap(int resourceId, float scaled) {
-        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
-        return Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() * scaled),
-                (int) (bitmap.getHeight() * scaled), false);
-    }
-
-    /**
-     * Resets the text view.
-     */
-    public void reset() {
-        if (DEBUG) Log.d(TAG, "#reset");
-
-        mStreamPosition = -1;
-        cancelStreamAnimation();
-        setText("");
-    }
-
-    /**
-     * Updates the recognized text.
-     */
-    public void updateRecognizedText(String stableText, String pendingText) {
-        if (DEBUG) Log.d(TAG, "updateText(" + stableText + "," + pendingText + ")");
-
-        if (stableText == null) {
-            stableText = "";
-        }
-
-        SpannableStringBuilder displayText = new SpannableStringBuilder(stableText);
-
-        if (DOTS_FOR_STABLE) {
-            addDottySpans(displayText, stableText, 0);
-        }
-
-        if (pendingText != null) {
-            int pendingTextStart = displayText.length();
-            displayText.append(pendingText);
-            if (DOTS_FOR_PENDING) {
-                addDottySpans(displayText, pendingText, pendingTextStart);
-            } else {
-                int pendingColor = getResources().getColor(
-                        R.color.lb_search_plate_hint_text_color);
-                addColorSpan(displayText, pendingColor, pendingText, pendingTextStart);
-            }
-        }
-
-        // Start streaming in dots from beginning of partials, or current position,
-        // whichever is larger
-        mStreamPosition = Math.max(stableText.length(), mStreamPosition);
-
-        // Copy the text and spans to a SpannedString, since editable text
-        // doesn't redraw in invalidate() when hardware accelerated
-        // if the text or spans haven't changed. (probably a framework bug)
-        updateText(new SpannedString(displayText));
-
-        if (ANIMATE_DOTS_FOR_PENDING) {
-            startStreamAnimation();
-        }
-    }
-
-    int getStreamPosition() {
-        return mStreamPosition;
-    }
-
-    void setStreamPosition(int streamPosition) {
-        mStreamPosition = streamPosition;
-        invalidate();
-    }
-
-    private void startStreamAnimation() {
-        cancelStreamAnimation();
-        int pos = getStreamPosition();
-        int totalLen = length();
-        int animLen = totalLen - pos;
-        if (animLen > 0) {
-            if (mStreamingAnimation == null) {
-                mStreamingAnimation = new ObjectAnimator();
-                mStreamingAnimation.setTarget(this);
-                mStreamingAnimation.setProperty(STREAM_POSITION_PROPERTY);
-            }
-            mStreamingAnimation.setIntValues(pos, totalLen);
-            mStreamingAnimation.setDuration(STREAM_UPDATE_DELAY_MILLIS * animLen);
-            mStreamingAnimation.start();
-        }
-    }
-
-    private void cancelStreamAnimation() {
-        if (mStreamingAnimation != null) {
-            mStreamingAnimation.cancel();
-        }
-    }
-
-    private void addDottySpans(SpannableStringBuilder displayText, String text, int textStart) {
-        Matcher m = SPLIT_PATTERN.matcher(text);
-        while (m.find()) {
-            int wordStart = textStart + m.start();
-            int wordEnd = textStart + m.end();
-            DottySpan span = new DottySpan(text.charAt(m.start()), wordStart);
-            displayText.setSpan(span, wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-        }
-    }
-
-    private void addColorSpan(SpannableStringBuilder displayText, int color, String text,
-            int textStart) {
-        ForegroundColorSpan span = new ForegroundColorSpan(color);
-        int start = textStart;
-        int end = textStart + text.length();
-        displayText.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-    }
-
-    /**
-     * Sets the final, non changing, full text result. This should only happen at the very end of
-     * a recognition.
-     *
-     * @param finalText to the view to.
-     */
-    public void setFinalRecognizedText(CharSequence finalText) {
-        if (DEBUG) Log.d(TAG, "setFinalRecognizedText(" + finalText + ")");
-
-        updateText(finalText);
-    }
-
-    private void updateText(CharSequence displayText) {
-        setText(displayText);
-        bringPointIntoView(length());
-    }
-
-    /**
-     * This is required to make the View findable by uiautomator.
-     */
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(StreamingTextView.class.getCanonicalName());
-    }
-
-    private class DottySpan extends ReplacementSpan {
-
-        private final int mSeed;
-        private final int mPosition;
-
-        public DottySpan(int seed, int pos) {
-            mSeed = seed;
-            mPosition = pos;
-        }
-
-        @Override
-        public void draw(Canvas canvas, CharSequence text, int start, int end,
-                float x, int top, int y, int bottom, Paint paint) {
-
-            int width = (int) paint.measureText(text, start, end);
-
-            int dotWidth = mOneDot.getWidth();
-            int sliceWidth = 2 * dotWidth;
-            int sliceCount = width / sliceWidth;
-            int excess = width % sliceWidth;
-            int prop = excess / 2;
-            boolean rtl = isLayoutRtl(StreamingTextView.this);
-
-            mRandom.setSeed(mSeed);
-            int oldAlpha = paint.getAlpha();
-            for (int i = 0; i < sliceCount; i++) {
-                if (ANIMATE_DOTS_FOR_PENDING) {
-                    if (mPosition + i >= mStreamPosition) break;
-                }
-
-                float left = i * sliceWidth + prop + dotWidth / 2;
-                float dotLeft = rtl ? x + width - left - dotWidth : x + left;
-
-                // give the dots some visual variety
-                paint.setAlpha((mRandom.nextInt(4) + 1) * 63);
-
-                if (mRandom.nextBoolean()) {
-                    canvas.drawBitmap(mTwoDot, dotLeft, y - mTwoDot.getHeight(), paint);
-                } else {
-                    canvas.drawBitmap(mOneDot, dotLeft, y - mOneDot.getHeight(), paint);
-                }
-            }
-            paint.setAlpha(oldAlpha);
-        }
-
-        @Override
-        public int getSize(Paint paint, CharSequence text, int start, int end,
-                Paint.FontMetricsInt fontMetricsInt) {
-            return (int) paint.measureText(text, start, end);
-        }
-    }
-
-    public static boolean isLayoutRtl(View view) {
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
-        } else {
-            return false;
-        }
-    }
-
-    public void updateRecognizedText(String stableText, List<Float> rmsValues) {}
-}
diff --git a/android/support/v17/leanback/widget/ThumbsBar.java b/android/support/v17/leanback/widget/ThumbsBar.java
deleted file mode 100644
index ca6eaca..0000000
--- a/android/support/v17/leanback/widget/ThumbsBar.java
+++ /dev/null
@@ -1,297 +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.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/android/support/v17/leanback/widget/TitleHelper.java b/android/support/v17/leanback/widget/TitleHelper.java
deleted file mode 100644
index 3aba59d..0000000
--- a/android/support/v17/leanback/widget/TitleHelper.java
+++ /dev/null
@@ -1,119 +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.v17.leanback.widget;
-
-import android.support.v17.leanback.transition.LeanbackTransitionHelper;
-import android.support.v17.leanback.transition.TransitionHelper;
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Helper for managing {@link android.support.v17.leanback.widget.TitleView}, including
- * transitions and focus movement.
- * Assumes the TitleView is overlayed on the topmost portion of the scene root view.
- */
-public class TitleHelper {
-
-    ViewGroup mSceneRoot;
-    View mTitleView;
-    private Object mTitleUpTransition;
-    private Object mTitleDownTransition;
-    private Object mSceneWithTitle;
-    private Object mSceneWithoutTitle;
-
-    // When moving focus off the TitleView, this focus search listener assumes that the view that
-    // should take focus comes before the TitleView in a focus search starting at the scene root.
-    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
-            new BrowseFrameLayout.OnFocusSearchListener() {
-        @Override
-        public View onFocusSearch(View focused, int direction) {
-            if (focused != mTitleView && direction == View.FOCUS_UP) {
-                return mTitleView;
-            }
-            final boolean isRtl = ViewCompat.getLayoutDirection(focused)
-                    == 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;
-            }
-            return null;
-        }
-    };
-
-    public TitleHelper(ViewGroup sceneRoot, View titleView) {
-        if (sceneRoot == null || titleView == null) {
-            throw new IllegalArgumentException("Views may not be null");
-        }
-        mSceneRoot = sceneRoot;
-        mTitleView = titleView;
-        createTransitions();
-    }
-
-    private void createTransitions() {
-        mTitleUpTransition = LeanbackTransitionHelper.loadTitleOutTransition(
-                mSceneRoot.getContext());
-        mTitleDownTransition = LeanbackTransitionHelper.loadTitleInTransition(
-                mSceneRoot.getContext());
-        mSceneWithTitle = TransitionHelper.createScene(mSceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.VISIBLE);
-            }
-        });
-        mSceneWithoutTitle = TransitionHelper.createScene(mSceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                mTitleView.setVisibility(View.INVISIBLE);
-            }
-        });
-    }
-
-    /**
-     * Shows the title.
-     */
-    public void showTitle(boolean show) {
-        if (show) {
-            TransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
-        } else {
-            TransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
-        }
-    }
-
-    /**
-     * Returns the scene root ViewGroup.
-     */
-    public ViewGroup getSceneRoot() {
-        return mSceneRoot;
-    }
-
-    /**
-     * Returns the {@link TitleView}
-     */
-    public View getTitleView() {
-        return mTitleView;
-    }
-
-    /**
-     * Returns a
-     * {@link android.support.v17.leanback.widget.BrowseFrameLayout.OnFocusSearchListener} which
-     * may be used to manage focus switching between the title view and scene root.
-     */
-    public BrowseFrameLayout.OnFocusSearchListener getOnFocusSearchListener() {
-        return mOnFocusSearchListener;
-    }
-}
diff --git a/android/support/v17/leanback/widget/TitleView.java b/android/support/v17/leanback/widget/TitleView.java
deleted file mode 100644
index 574d1c7..0000000
--- a/android/support/v17/leanback/widget/TitleView.java
+++ /dev/null
@@ -1,225 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import static android.support.v17.leanback.widget.TitleViewAdapter.BRANDING_VIEW_VISIBLE;
-import static android.support.v17.leanback.widget.TitleViewAdapter.SEARCH_VIEW_VISIBLE;
-import static android.support.v17.leanback.widget.TitleViewAdapter.FULL_VIEW_VISIBLE;
-
-/**
- * Title view for a leanback fragment.
- */
-public class TitleView extends FrameLayout implements TitleViewAdapter.Provider {
-
-    private ImageView mBadgeView;
-    private TextView mTextView;
-    private SearchOrbView mSearchOrbView;
-    private int flags = FULL_VIEW_VISIBLE;
-    private boolean mHasSearchListener = false;
-
-    private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter() {
-        @Override
-        public View getSearchAffordanceView() {
-            return TitleView.this.getSearchAffordanceView();
-        }
-
-        @Override
-        public void setOnSearchClickedListener(View.OnClickListener listener) {
-            TitleView.this.setOnSearchClickedListener(listener);
-        }
-
-        @Override
-        public void setAnimationEnabled(boolean enable) {
-            TitleView.this.enableAnimation(enable);
-        }
-
-        @Override
-        public Drawable getBadgeDrawable() {
-            return TitleView.this.getBadgeDrawable();
-        }
-
-        @Override
-        public SearchOrbView.Colors getSearchAffordanceColors() {
-            return TitleView.this.getSearchAffordanceColors();
-        }
-
-        @Override
-        public CharSequence getTitle() {
-            return TitleView.this.getTitle();
-        }
-
-        @Override
-        public void setBadgeDrawable(Drawable drawable) {
-            TitleView.this.setBadgeDrawable(drawable);
-        }
-
-        @Override
-        public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-            TitleView.this.setSearchAffordanceColors(colors);
-        }
-
-        @Override
-        public void setTitle(CharSequence titleText) {
-            TitleView.this.setTitle(titleText);
-        }
-
-        @Override
-        public void updateComponentsVisibility(int flags) {
-            TitleView.this.updateComponentsVisibility(flags);
-        }
-    };
-
-    public TitleView(Context context) {
-        this(context, null);
-    }
-
-    public TitleView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.browseTitleViewStyle);
-    }
-
-    public TitleView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        View rootView = inflater.inflate(R.layout.lb_title_view, this);
-
-        mBadgeView = (ImageView) rootView.findViewById(R.id.title_badge);
-        mTextView = (TextView) rootView.findViewById(R.id.title_text);
-        mSearchOrbView = (SearchOrbView) rootView.findViewById(R.id.title_orb);
-
-        setClipToPadding(false);
-        setClipChildren(false);
-    }
-
-    /**
-     * Sets the title text.
-     */
-    public void setTitle(CharSequence titleText) {
-        mTextView.setText(titleText);
-        updateBadgeVisibility();
-    }
-
-    /**
-     * Returns the title text.
-     */
-    public CharSequence getTitle() {
-        return mTextView.getText();
-    }
-
-    /**
-     * Sets the badge drawable.
-     * If non-null, the drawable is displayed instead of the title text.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-        mBadgeView.setImageDrawable(drawable);
-        updateBadgeVisibility();
-    }
-
-    /**
-     * Returns the badge drawable.
-     */
-    public Drawable getBadgeDrawable() {
-        return mBadgeView.getDrawable();
-    }
-
-    /**
-     * Sets the listener to be called when the search affordance is clicked.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        mHasSearchListener = listener != null;
-        mSearchOrbView.setOnOrbClickedListener(listener);
-        updateSearchOrbViewVisiblity();
-    }
-
-    /**
-     *  Returns the view for the search affordance.
-     */
-    public View getSearchAffordanceView() {
-        return mSearchOrbView;
-    }
-
-    /**
-     * Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-        mSearchOrbView.setOrbColors(colors);
-    }
-
-    /**
-     * Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        return mSearchOrbView.getOrbColors();
-    }
-
-    /**
-     * Enables or disables any view animations.
-     */
-    public void enableAnimation(boolean enable) {
-        mSearchOrbView.enableOrbColorAnimation(enable && mSearchOrbView.hasFocus());
-    }
-
-    /**
-     * Based on the flag, it updates the visibility of the individual components -
-     * BadgeView, TextView and SearchView.
-     *
-     * @param flags integer representing the visibility of TitleView components.
-     * @see TitleViewAdapter#SEARCH_VIEW_VISIBLE
-     * @see TitleViewAdapter#BRANDING_VIEW_VISIBLE
-     * @see TitleViewAdapter#FULL_VIEW_VISIBLE
-     */
-    public void updateComponentsVisibility(int flags) {
-        this.flags = flags;
-
-        if ((flags & BRANDING_VIEW_VISIBLE) == BRANDING_VIEW_VISIBLE) {
-            updateBadgeVisibility();
-        } else {
-            mBadgeView.setVisibility(View.GONE);
-            mTextView.setVisibility(View.GONE);
-        }
-        updateSearchOrbViewVisiblity();
-    }
-
-    private void updateSearchOrbViewVisiblity() {
-        int visibility = mHasSearchListener && (flags & SEARCH_VIEW_VISIBLE) == SEARCH_VIEW_VISIBLE
-                ? View.VISIBLE : View.INVISIBLE;
-        mSearchOrbView.setVisibility(visibility);
-    }
-
-    private void updateBadgeVisibility() {
-        Drawable drawable = mBadgeView.getDrawable();
-        if (drawable != null) {
-            mBadgeView.setVisibility(View.VISIBLE);
-            mTextView.setVisibility(View.GONE);
-        } else {
-            mBadgeView.setVisibility(View.GONE);
-            mTextView.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public TitleViewAdapter getTitleViewAdapter() {
-        return mTitleViewAdapter;
-    }
-}
diff --git a/android/support/v17/leanback/widget/TitleViewAdapter.java b/android/support/v17/leanback/widget/TitleViewAdapter.java
deleted file mode 100644
index de9db4b..0000000
--- a/android/support/v17/leanback/widget/TitleViewAdapter.java
+++ /dev/null
@@ -1,139 +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.v17.leanback.widget;
-
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-/**
- * This class allows a customized widget class to implement {@link TitleViewAdapter.Provider}
- * and expose {@link TitleViewAdapter} methods to containing fragment (e.g. BrowseFragment or
- * DetailsFragment).
- * The title view must have a search orb view ({@link #getSearchAffordanceView()} aligned to start
- * and can typically have a branding Drawable and or title text aligned to end.  The branding part
- * is fully open to customization: not necessary to be a drawable or text.
- */
-public abstract class TitleViewAdapter {
-
-    /**
-     * Interface to be implemented by a customized widget class to implement
-     * {@link TitleViewAdapter}.
-     */
-    public interface Provider {
-        /**
-         * Returns {@link TitleViewAdapter} to be implemented by the customized widget class.
-         * @return {@link TitleViewAdapter} to be implemented by the customized widget class.
-         */
-        TitleViewAdapter getTitleViewAdapter();
-    }
-
-    public static final int BRANDING_VIEW_VISIBLE = 0x02;
-    public static final int SEARCH_VIEW_VISIBLE = 0x04;
-    public static final int FULL_VIEW_VISIBLE = BRANDING_VIEW_VISIBLE | SEARCH_VIEW_VISIBLE;
-
-    /**
-     * Sets the title text.
-     * @param titleText The text to set as title.
-     */
-    public void setTitle(CharSequence titleText) {
-    }
-
-    /**
-     * Returns the title text.
-     * @return The title text.
-     */
-    public CharSequence getTitle() {
-        return null;
-    }
-
-    /**
-     * Sets the badge drawable.
-     * If non-null, the drawable is displayed instead of the title text.
-     * @param drawable The badge drawable to set on title view.
-     */
-    public void setBadgeDrawable(Drawable drawable) {
-    }
-
-    /**
-     * Returns the badge drawable.
-     * @return The badge drawable.
-     */
-    public Drawable getBadgeDrawable() {
-        return null;
-    }
-
-    /**
-     *  Returns the view for the search affordance.
-     *  @return The view for search affordance.
-     */
-    public abstract View getSearchAffordanceView();
-
-    /**
-     * Sets a click listener for the search affordance view.
-     *
-     * <p>The presence of a listener will change the visibility of the search
-     * affordance in the fragment title. When set to non-null, the title will
-     * contain an element that a user may click to begin a search.
-     *
-     * <p>The listener's {@link View.OnClickListener#onClick onClick} method
-     * will be invoked when the user clicks on the search element.
-     *
-     * @param listener The listener to call when the search element is clicked.
-     */
-    public void setOnSearchClickedListener(View.OnClickListener listener) {
-        View view = getSearchAffordanceView();
-        if (view != null) {
-            view.setOnClickListener(listener);
-        }
-    }
-
-    /**
-     * Sets the {@link android.support.v17.leanback.widget.SearchOrbView.Colors} used to draw the
-     * search affordance.
-     *
-     * @param colors Colors used to draw search affordance.
-     */
-    public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
-    }
-
-    /**
-     * Returns the {@link android.support.v17.leanback.widget.SearchOrbView.Colors} used to draw the
-     * search affordance.
-     *
-     * @return Colors used to draw search affordance.
-     */
-    public SearchOrbView.Colors getSearchAffordanceColors() {
-        return null;
-    }
-
-    /**
-     * Enables or disables any view animations.  This method is called to save CPU cycle for example
-     * stop search view breathing animation when containing fragment is paused.
-     * @param enable True to enable animation, false otherwise.
-     */
-    public void setAnimationEnabled(boolean enable) {
-    }
-
-    /**
-     * Based on the flag, it updates the visibility of the individual components -
-     * Branding views (badge drawable and/or title) and search affordance view.
-     *
-     * @param flags integer representing the visibility of TitleView components.
-     * @see #BRANDING_VIEW_VISIBLE
-     * @see #SEARCH_VIEW_VISIBLE
-     * @see #FULL_VIEW_VISIBLE
-     */
-    public void updateComponentsVisibility(int flags) {
-    }
-}
diff --git a/android/support/v17/leanback/widget/Util.java b/android/support/v17/leanback/widget/Util.java
deleted file mode 100644
index 7e89ea6..0000000
--- a/android/support/v17/leanback/widget/Util.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.v17.leanback.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class Util {
-
-    /**
-     * Returns true if child == parent or is descendant of the parent.
-     */
-    public static boolean isDescendant(ViewGroup parent, View child) {
-        while (child != null) {
-            if (child == parent) {
-                return true;
-            }
-            ViewParent p = child.getParent();
-            if (!(p instanceof View)) {
-                return false;
-            }
-            child = (View) p;
-        }
-        return false;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/VerticalGridPresenter.java b/android/support/v17/leanback/widget/VerticalGridPresenter.java
deleted file mode 100644
index 56b6ed1..0000000
--- a/android/support/v17/leanback/widget/VerticalGridPresenter.java
+++ /dev/null
@@ -1,405 +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.v17.leanback.widget;
-
-import android.content.Context;
-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;
-
-/**
- * A presenter that renders objects in a {@link VerticalGridView}.
- */
-public class VerticalGridPresenter extends Presenter {
-    private static final String TAG = "GridPresenter";
-    private static final boolean DEBUG = false;
-
-    class VerticalGridItemBridgeAdapter extends ItemBridgeAdapter {
-        @Override
-        protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
-            if (viewHolder.itemView instanceof ViewGroup) {
-                TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView,
-                        true);
-            }
-            if (mShadowOverlayHelper != null) {
-                mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
-            }
-        }
-
-        @Override
-        public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) {
-            // Only when having an OnItemClickListener, we attach the OnClickListener.
-            if (getOnItemViewClickedListener() != null) {
-                final View itemView = itemViewHolder.mHolder.view;
-                itemView.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        if (getOnItemViewClickedListener() != null) {
-                            // Row is always null
-                            getOnItemViewClickedListener().onItemClicked(
-                                    itemViewHolder.mHolder, itemViewHolder.mItem, null, null);
-                        }
-                    }
-                });
-            }
-        }
-
-        @Override
-        public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) {
-            if (getOnItemViewClickedListener() != null) {
-                viewHolder.mHolder.view.setOnClickListener(null);
-            }
-        }
-
-        @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            viewHolder.itemView.setActivated(true);
-        }
-    }
-
-    /**
-     * ViewHolder for the VerticalGridPresenter.
-     */
-    public static class ViewHolder extends Presenter.ViewHolder {
-        ItemBridgeAdapter mItemBridgeAdapter;
-        final VerticalGridView mGridView;
-        boolean mInitialized;
-
-        public ViewHolder(VerticalGridView view) {
-            super(view);
-            mGridView = view;
-        }
-
-        public VerticalGridView getGridView() {
-            return mGridView;
-        }
-    }
-
-    private int mNumColumns = -1;
-    private int mFocusZoomFactor;
-    private boolean mUseFocusDimmer;
-    private boolean mShadowEnabled = true;
-    private boolean mKeepChildForeground = true;
-    private OnItemViewSelectedListener mOnItemViewSelectedListener;
-    private OnItemViewClickedListener mOnItemViewClickedListener;
-    private boolean mRoundedCornersEnabled = true;
-    ShadowOverlayHelper mShadowOverlayHelper;
-    private ItemBridgeAdapter.Wrapper mShadowOverlayWrapper;
-
-    /**
-     * Constructs a VerticalGridPresenter with defaults.
-     * Uses {@link FocusHighlight#ZOOM_FACTOR_MEDIUM} for focus zooming and
-     * enabled dimming on focus.
-     */
-    public VerticalGridPresenter() {
-        this(FocusHighlight.ZOOM_FACTOR_LARGE);
-    }
-
-    /**
-     * Constructs a VerticalGridPresenter with the given parameters.
-     *
-     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
-     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
-     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
-     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
-     * enabled dimming on focus.
-     */
-    public VerticalGridPresenter(int focusZoomFactor) {
-        this(focusZoomFactor, true);
-    }
-
-    /**
-     * Constructs a VerticalGridPresenter with the given parameters.
-     *
-     * @param focusZoomFactor Controls the zoom factor used when an item view is focused. One of
-     *         {@link FocusHighlight#ZOOM_FACTOR_NONE},
-     *         {@link FocusHighlight#ZOOM_FACTOR_SMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_XSMALL},
-     *         {@link FocusHighlight#ZOOM_FACTOR_MEDIUM},
-     *         {@link FocusHighlight#ZOOM_FACTOR_LARGE}
-     * @param useFocusDimmer determines if the FocusHighlighter will use the dimmer
-     */
-    public VerticalGridPresenter(int focusZoomFactor, boolean useFocusDimmer) {
-        mFocusZoomFactor = focusZoomFactor;
-        mUseFocusDimmer = useFocusDimmer;
-    }
-
-    /**
-     * Sets the number of columns in the vertical grid.
-     */
-    public void setNumberOfColumns(int numColumns) {
-        if (numColumns < 0) {
-            throw new IllegalArgumentException("Invalid number of columns");
-        }
-        if (mNumColumns != numColumns) {
-            mNumColumns = numColumns;
-        }
-    }
-
-    /**
-     * Returns the number of columns in the vertical grid.
-     */
-    public int getNumberOfColumns() {
-        return mNumColumns;
-    }
-
-    /**
-     * Enable or disable child shadow.
-     * This is not only for enable/disable default shadow implementation but also subclass must
-     * respect this flag.
-     */
-    public final void setShadowEnabled(boolean enabled) {
-        mShadowEnabled = enabled;
-    }
-
-    /**
-     * Returns true if child shadow is enabled.
-     * This is not only for enable/disable default shadow implementation but also subclass must
-     * respect this flag.
-     */
-    public final boolean getShadowEnabled() {
-        return mShadowEnabled;
-    }
-
-    /**
-     * Default implementation returns true if SDK version >= 21, shadow (either static or z-order
-     * based) will be applied to each individual child of {@link VerticalGridView}.
-     * Subclass may return false to disable default implementation of shadow and provide its own.
-     */
-    public boolean isUsingDefaultShadow() {
-        return ShadowOverlayHelper.supportsShadow();
-    }
-
-    /**
-     * Enables or disabled rounded corners on children of this row.
-     * Supported on Android SDK >= L.
-     */
-    public final void enableChildRoundedCorners(boolean enable) {
-        mRoundedCornersEnabled = enable;
-    }
-
-    /**
-     * Returns true if rounded corners are enabled for children of this row.
-     */
-    public final boolean areChildRoundedCornersEnabled() {
-        return mRoundedCornersEnabled;
-    }
-
-    /**
-     * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled
-     * on each child of vertical grid.   If subclass returns false in isUsingDefaultShadow()
-     * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false.
-     */
-    public boolean isUsingZOrder(Context context) {
-        return !Settings.getInstance(context).preferStaticShadows();
-    }
-
-    final boolean needsDefaultShadow() {
-        return isUsingDefaultShadow() && getShadowEnabled();
-    }
-
-    /**
-     * Returns the zoom factor used for focus highlighting.
-     */
-    public final int getFocusZoomFactor() {
-        return mFocusZoomFactor;
-    }
-
-    /**
-     * Returns true if the focus dimmer is used for focus highlighting; false otherwise.
-     */
-    public final boolean isFocusDimmerUsed() {
-        return mUseFocusDimmer;
-    }
-
-    @Override
-    public final ViewHolder onCreateViewHolder(ViewGroup parent) {
-        ViewHolder vh = createGridViewHolder(parent);
-        vh.mInitialized = false;
-        vh.mItemBridgeAdapter = new VerticalGridItemBridgeAdapter();
-        initializeGridViewHolder(vh);
-        if (!vh.mInitialized) {
-            throw new RuntimeException("super.initializeGridViewHolder() must be called");
-        }
-        return vh;
-    }
-
-    /**
-     * Subclass may override this to inflate a different layout.
-     */
-    protected ViewHolder createGridViewHolder(ViewGroup parent) {
-        View root = LayoutInflater.from(parent.getContext()).inflate(
-                R.layout.lb_vertical_grid, parent, false);
-        return new ViewHolder((VerticalGridView) root.findViewById(R.id.browse_grid));
-    }
-
-    /**
-     * Called after a {@link VerticalGridPresenter.ViewHolder} is created.
-     * Subclasses may override this method and start by calling
-     * super.initializeGridViewHolder(ViewHolder).
-     *
-     * @param vh The ViewHolder to initialize for the vertical grid.
-     */
-    protected void initializeGridViewHolder(ViewHolder vh) {
-        if (mNumColumns == -1) {
-            throw new IllegalStateException("Number of columns must be set");
-        }
-        if (DEBUG) Log.v(TAG, "mNumColumns " + mNumColumns);
-        vh.getGridView().setNumColumns(mNumColumns);
-        vh.mInitialized = true;
-
-        Context context = vh.mGridView.getContext();
-        if (mShadowOverlayHelper == null) {
-            mShadowOverlayHelper = new ShadowOverlayHelper.Builder()
-                    .needsOverlay(mUseFocusDimmer)
-                    .needsShadow(needsDefaultShadow())
-                    .needsRoundedCorner(areChildRoundedCornersEnabled())
-                    .preferZOrder(isUsingZOrder(context))
-                    .keepForegroundDrawable(mKeepChildForeground)
-                    .options(createShadowOverlayOptions())
-                    .build(context);
-            if (mShadowOverlayHelper.needsWrapper()) {
-                mShadowOverlayWrapper = new ItemBridgeAdapterShadowOverlayWrapper(
-                        mShadowOverlayHelper);
-            }
-        }
-        vh.mItemBridgeAdapter.setWrapper(mShadowOverlayWrapper);
-        mShadowOverlayHelper.prepareParentForShadow(vh.mGridView);
-        vh.getGridView().setFocusDrawingOrderEnabled(mShadowOverlayHelper.getShadowType()
-                != ShadowOverlayHelper.SHADOW_DYNAMIC);
-        FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter,
-                mFocusZoomFactor, mUseFocusDimmer);
-
-        final ViewHolder gridViewHolder = vh;
-        vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() {
-            @Override
-            public void onChildSelected(ViewGroup parent, View view, int position, long id) {
-                selectChildView(gridViewHolder, view);
-            }
-        });
-    }
-
-    /**
-     * Set if keeps foreground of child of this grid, the foreground will not
-     * be used for overlay color.  Default value is true.
-     *
-     * @param keep   True if keep foreground of child of this grid.
-     */
-    public final void setKeepChildForeground(boolean keep) {
-        mKeepChildForeground = keep;
-    }
-
-    /**
-     * Returns true if keeps foreground of child of this grid, the foreground will not
-     * be used for overlay color.  Default value is true.
-     *
-     * @return   True if keeps foreground of child of this grid.
-     */
-    public final boolean getKeepChildForeground() {
-        return mKeepChildForeground;
-    }
-
-    /**
-     * Create ShadowOverlayHelper Options.  Subclass may override.
-     * e.g.
-     * <code>
-     * return new ShadowOverlayHelper.Options().roundedCornerRadius(10);
-     * </code>
-     *
-     * @return   The options to be used for shadow, overlay and rounded corner.
-     */
-    protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
-        return ShadowOverlayHelper.Options.DEFAULT;
-    }
-
-    @Override
-    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
-        if (DEBUG) Log.v(TAG, "onBindViewHolder " + item);
-        ViewHolder vh = (ViewHolder) viewHolder;
-        vh.mItemBridgeAdapter.setAdapter((ObjectAdapter) item);
-        vh.getGridView().setAdapter(vh.mItemBridgeAdapter);
-    }
-
-    @Override
-    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
-        if (DEBUG) Log.v(TAG, "onUnbindViewHolder");
-        ViewHolder vh = (ViewHolder) viewHolder;
-        vh.mItemBridgeAdapter.setAdapter(null);
-        vh.getGridView().setAdapter(null);
-    }
-
-    /**
-     * Sets the item selected listener.
-     * Since this is a grid the row parameter is always null.
-     */
-    public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
-        mOnItemViewSelectedListener = listener;
-    }
-
-    /**
-     * Returns the item selected listener.
-     */
-    public final OnItemViewSelectedListener getOnItemViewSelectedListener() {
-        return mOnItemViewSelectedListener;
-    }
-
-    /**
-     * Sets the item clicked listener.
-     * OnItemViewClickedListener will override {@link View.OnClickListener} that
-     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
-     * So in general, developer should choose one of the listeners but not both.
-     */
-    public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mOnItemViewClickedListener = listener;
-    }
-
-    /**
-     * Returns the item clicked listener.
-     */
-    public final OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mOnItemViewClickedListener;
-    }
-
-    void selectChildView(ViewHolder vh, View view) {
-        if (getOnItemViewSelectedListener() != null) {
-            ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null :
-                    (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view);
-            if (ibh == null) {
-                getOnItemViewSelectedListener().onItemSelected(null, null, null, null);
-            } else {
-                getOnItemViewSelectedListener().onItemSelected(ibh.mHolder, ibh.mItem, null, null);
-            }
-        }
-    }
-
-    /**
-     * Changes the visibility of views.  The entrance transition will be run against the views that
-     * change visibilities.  This method is called by the fragment, it should not be called
-     * directly by the application.
-     *
-     * @param holder         The ViewHolder for the vertical grid.
-     * @param afterEntrance  true if children of vertical grid participating in entrance transition
-     *                       should be set to visible, false otherwise.
-     */
-    public void setEntranceTransitionState(VerticalGridPresenter.ViewHolder holder,
-            boolean afterEntrance) {
-        holder.mGridView.setChildrenVisibility(afterEntrance? View.VISIBLE : View.INVISIBLE);
-    }
-}
diff --git a/android/support/v17/leanback/widget/VerticalGridView.java b/android/support/v17/leanback/widget/VerticalGridView.java
deleted file mode 100644
index 5cb2ec8..0000000
--- a/android/support/v17/leanback/widget/VerticalGridView.java
+++ /dev/null
@@ -1,91 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.v17.leanback.R;
-import android.support.v7.widget.RecyclerView;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-
-/**
- * A {@link android.view.ViewGroup} that shows items in a vertically scrolling list. The items
- * come from the {@link RecyclerView.Adapter} associated with this view.
- * <p>
- * {@link RecyclerView.Adapter} can optionally implement {@link FacetProviderAdapter} which
- * provides {@link FacetProvider} for a given view type;  {@link RecyclerView.ViewHolder}
- * can also implement {@link FacetProvider}.  Facet from ViewHolder
- * has a higher priority than the one from FacetProviderAdapter associated with viewType.
- * Supported optional facets are:
- * <ol>
- * <li> {@link ItemAlignmentFacet}
- * When this facet is provided by ViewHolder or FacetProviderAdapter,  it will
- * override the item alignment settings set on VerticalGridView.  This facet also allows multiple
- * alignment positions within one ViewHolder.
- * </li>
- * </ol>
- */
-public class VerticalGridView extends BaseGridView {
-
-    public VerticalGridView(Context context) {
-        this(context, null);
-    }
-
-    public VerticalGridView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public VerticalGridView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mLayoutManager.setOrientation(RecyclerView.VERTICAL);
-        initAttributes(context, attrs);
-    }
-
-    protected void initAttributes(Context context, AttributeSet attrs) {
-        initBaseGridViewAttributes(context, attrs);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbVerticalGridView);
-        setColumnWidth(a);
-        setNumColumns(a.getInt(R.styleable.lbVerticalGridView_numberOfColumns, 1));
-        a.recycle();
-    }
-
-    void setColumnWidth(TypedArray array) {
-        TypedValue typedValue = array.peekValue(R.styleable.lbVerticalGridView_columnWidth);
-        if (typedValue != null) {
-            int size = array.getLayoutDimension(R.styleable.lbVerticalGridView_columnWidth, 0);
-            setColumnWidth(size);
-        }
-    }
-
-    /**
-     * Sets the number of columns.  Defaults to one.
-     */
-    public void setNumColumns(int numColumns) {
-        mLayoutManager.setNumRows(numColumns);
-        requestLayout();
-    }
-
-    /**
-     * Sets the column width.
-     *
-     * @param width May be {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, or a size
-     *              in pixels. If zero, column width will be fixed based on number of columns
-     *              and view width.
-     */
-    public void setColumnWidth(int width) {
-        mLayoutManager.setRowHeight(width);
-        requestLayout();
-    }
-}
diff --git a/android/support/v17/leanback/widget/VideoSurfaceView.java b/android/support/v17/leanback/widget/VideoSurfaceView.java
deleted file mode 100644
index d42a60d..0000000
--- a/android/support/v17/leanback/widget/VideoSurfaceView.java
+++ /dev/null
@@ -1,52 +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.v17.leanback.widget;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.util.AttributeSet;
-import android.view.SurfaceView;
-
-/**
- * Activity transition will change transitionVisibility multiple times even the view is not
- * running transition, which causes visual flickering during activity return transition.
- * This class disables setTransitionVisibility() to avoid the problem.
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class VideoSurfaceView extends SurfaceView {
-
-    public VideoSurfaceView(Context context) {
-        super(context);
-    }
-
-    public VideoSurfaceView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    /**
-     * Overrides hidden method View.setTransitionVisibility() to disable visibility changes
-     * in activity transition.
-     */
-    public void setTransitionVisibility(int visibility) {
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/ViewHolderTask.java b/android/support/v17/leanback/widget/ViewHolderTask.java
deleted file mode 100644
index 6103487..0000000
--- a/android/support/v17/leanback/widget/ViewHolderTask.java
+++ /dev/null
@@ -1,23 +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.v17.leanback.widget;
-
-import android.support.v7.widget.RecyclerView;
-
-/**
- * Interface for schedule task on a ViewHolder.
- */
-public interface ViewHolderTask {
-    public void run(RecyclerView.ViewHolder viewHolder);
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/ViewsStateBundle.java b/android/support/v17/leanback/widget/ViewsStateBundle.java
deleted file mode 100644
index f823151..0000000
--- a/android/support/v17/leanback/widget/ViewsStateBundle.java
+++ /dev/null
@@ -1,223 +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.v17.leanback.widget;
-
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.support.v4.util.LruCache;
-import android.util.SparseArray;
-import android.view.View;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import static android.support.v17.leanback.widget.BaseGridView.SAVE_NO_CHILD;
-import static android.support.v17.leanback.widget.BaseGridView.SAVE_ON_SCREEN_CHILD;
-import static android.support.v17.leanback.widget.BaseGridView.SAVE_LIMITED_CHILD;
-import static android.support.v17.leanback.widget.BaseGridView.SAVE_ALL_CHILD;
-
-/**
- * Maintains a bundle of states for a group of views. Each view must have a unique id to identify
- * it. There are four different strategies {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
- * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
- * <p>
- * This class serves purpose of nested "listview" e.g.  a vertical list of horizontal list.
- * Vertical list maintains id->bundle mapping of all its children (even the children is offscreen
- * and being pruned).
- * <p>
- * The class is currently used within {@link GridLayoutManager}, but it might be used by other
- * ViewGroup.
- */
-class ViewsStateBundle {
-
-    public static final int LIMIT_DEFAULT = 100;
-    public static final int UNLIMITED = Integer.MAX_VALUE;
-
-    private int mSavePolicy;
-    private int mLimitNumber;
-
-    private LruCache<String, SparseArray<Parcelable>> mChildStates;
-
-    public ViewsStateBundle() {
-        mSavePolicy = SAVE_NO_CHILD;
-        mLimitNumber = LIMIT_DEFAULT;
-    }
-
-    public void clear() {
-        if (mChildStates != null) {
-            mChildStates.evictAll();
-        }
-    }
-
-    public void remove(int id) {
-        if (mChildStates != null && mChildStates.size() != 0) {
-            mChildStates.remove(getSaveStatesKey(id));
-        }
-    }
-
-    /**
-     * @return the saved views states
-     */
-    public final Bundle saveAsBundle() {
-        if (mChildStates == null || mChildStates.size() == 0) {
-            return null;
-        }
-        Map<String, SparseArray<Parcelable>> snapshot = mChildStates.snapshot();
-        Bundle bundle = new Bundle();
-        for (Iterator<Entry<String, SparseArray<Parcelable>>> i =
-                snapshot.entrySet().iterator(); i.hasNext(); ) {
-            Entry<String, SparseArray<Parcelable>> e = i.next();
-            bundle.putSparseParcelableArray(e.getKey(), e.getValue());
-        }
-        return bundle;
-    }
-
-    public final void loadFromBundle(Bundle savedBundle) {
-        if (mChildStates != null && savedBundle != null) {
-            mChildStates.evictAll();
-            for (Iterator<String> i = savedBundle.keySet().iterator(); i.hasNext(); ) {
-                String key = i.next();
-                mChildStates.put(key, savedBundle.getSparseParcelableArray(key));
-            }
-        }
-    }
-
-    /**
-     * @return the savePolicy, see {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
-     *         {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}
-     */
-    public final int getSavePolicy() {
-        return mSavePolicy;
-    }
-
-    /**
-     * @return the limitNumber, only works when {@link #getSavePolicy()} is
-     *         {@link #SAVE_LIMITED_CHILD}
-     */
-    public final int getLimitNumber() {
-        return mLimitNumber;
-    }
-
-    /**
-     * @see ViewsStateBundle#getSavePolicy()
-     */
-    public final void setSavePolicy(int savePolicy) {
-        this.mSavePolicy = savePolicy;
-        applyPolicyChanges();
-    }
-
-    /**
-     * @see ViewsStateBundle#getLimitNumber()
-     */
-    public final void setLimitNumber(int limitNumber) {
-        this.mLimitNumber = limitNumber;
-        applyPolicyChanges();
-    }
-
-    protected void applyPolicyChanges() {
-        if (mSavePolicy == SAVE_LIMITED_CHILD) {
-            if (mLimitNumber <= 0) {
-                throw new IllegalArgumentException();
-            }
-            if (mChildStates == null || mChildStates.maxSize() != mLimitNumber) {
-                mChildStates = new LruCache<String, SparseArray<Parcelable>>(mLimitNumber);
-            }
-        } else if (mSavePolicy == SAVE_ALL_CHILD || mSavePolicy == SAVE_ON_SCREEN_CHILD) {
-            if (mChildStates == null || mChildStates.maxSize() != UNLIMITED) {
-                mChildStates = new LruCache<String, SparseArray<Parcelable>>(UNLIMITED);
-            }
-        } else {
-            mChildStates = null;
-        }
-    }
-
-    /**
-     * Load view from states, it's none operation if the there is no state associated with the id.
-     *
-     * @param view view where loads into
-     * @param id unique id for the view within this ViewsStateBundle
-     */
-    public final void loadView(View view, int id) {
-        if (mChildStates != null) {
-            String key = getSaveStatesKey(id);
-            // Once loaded the state, do not keep the state of child. The child state will
-            // be saved again either when child is offscreen or when the parent is saved.
-            SparseArray<Parcelable> container = mChildStates.remove(key);
-            if (container != null) {
-                view.restoreHierarchyState(container);
-            }
-        }
-    }
-
-    /**
-     * Save views regardless what's the current policy is.
-     *
-     * @param view view to save
-     * @param id unique id for the view within this ViewsStateBundle
-     */
-    protected final void saveViewUnchecked(View view, int id) {
-        if (mChildStates != null) {
-            String key = getSaveStatesKey(id);
-            SparseArray<Parcelable> container = new SparseArray<Parcelable>();
-            view.saveHierarchyState(container);
-            mChildStates.put(key, container);
-        }
-    }
-
-    /**
-     * The on screen view is saved when policy is not {@link #SAVE_NO_CHILD}.
-     *
-     * @param bundle   Bundle where we save the on screen view state.  If null,
-     *                 a new Bundle is created and returned.
-     * @param view     The view to save.
-     * @param id       Id of the view.
-     */
-    public final Bundle saveOnScreenView(Bundle bundle, View view, int id) {
-        if (mSavePolicy != SAVE_NO_CHILD) {
-            String key = getSaveStatesKey(id);
-            SparseArray<Parcelable> container = new SparseArray<Parcelable>();
-            view.saveHierarchyState(container);
-            if (bundle == null) {
-                bundle = new Bundle();
-            }
-            bundle.putSparseParcelableArray(key, container);
-        }
-        return bundle;
-    }
-
-    /**
-     * Save off screen views according to policy.
-     *
-     * @param view view to save
-     * @param id unique id for the view within this ViewsStateBundle
-     */
-    public final void saveOffscreenView(View view, int id) {
-        switch (mSavePolicy) {
-            case SAVE_LIMITED_CHILD:
-            case SAVE_ALL_CHILD:
-                saveViewUnchecked(view, id);
-                break;
-            case SAVE_ON_SCREEN_CHILD:
-                remove(id);
-                break;
-            default:
-                break;
-        }
-    }
-
-    static String getSaveStatesKey(int id) {
-        return Integer.toString(id);
-    }
-}
diff --git a/android/support/v17/leanback/widget/Visibility.java b/android/support/v17/leanback/widget/Visibility.java
deleted file mode 100644
index b16a2f9..0000000
--- a/android/support/v17/leanback/widget/Visibility.java
+++ /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.
- */
-
-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/android/support/v17/leanback/widget/WindowAlignment.java b/android/support/v17/leanback/widget/WindowAlignment.java
deleted file mode 100644
index c6589d4..0000000
--- a/android/support/v17/leanback/widget/WindowAlignment.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.v17.leanback.widget;
-
-import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_BOTH_EDGE;
-import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_HIGH_EDGE;
-import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_LOW_EDGE;
-import static android.support.v17.leanback.widget.BaseGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED;
-import static android.support.v7.widget.RecyclerView.HORIZONTAL;
-
-/**
- * Maintains Window Alignment information of two axis.
- */
-class WindowAlignment {
-
-    /**
-     * Maintains alignment information in one direction.
-     */
-    public static class Axis {
-        /**
-         * Right or bottom edge of last child.
-         */
-        private int mMaxEdge;
-        /**
-         * Left or top edge of first child
-         */
-        private int mMinEdge;
-        /**
-         * Scroll distance to align last child, it defines limit of scroll.
-         */
-        private int mMaxScroll;
-        /**
-         * 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;
-
-        private float mWindowAlignmentOffsetPercent = 50f;
-
-        private int mSize;
-
-        /**
-         * Padding at the min edge, it is the left or top padding.
-         */
-        private int mPaddingMin;
-
-        /**
-         * Padding at the max edge, it is the right or bottom padding.
-         */
-        private int mPaddingMax;
-
-        private boolean mReversedFlow;
-
-        private String mName; // for debugging
-
-        public Axis(String name) {
-            reset();
-            mName = name;
-        }
-
-        public final int getWindowAlignment() {
-            return mWindowAlignment;
-        }
-
-        public final void setWindowAlignment(int windowAlignment) {
-            mWindowAlignment = windowAlignment;
-        }
-
-        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;
-        }
-
-        public final void setWindowAlignmentOffset(int offset) {
-            mWindowAlignmentOffset = offset;
-        }
-
-        public final void setWindowAlignmentOffsetPercent(float percent) {
-            if ((percent < 0 || percent > 100)
-                    && percent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
-                throw new IllegalArgumentException();
-            }
-            mWindowAlignmentOffsetPercent = percent;
-        }
-
-        public final float getWindowAlignmentOffsetPercent() {
-            return mWindowAlignmentOffsetPercent;
-        }
-
-        /**
-         * Returns scroll distance to align min child.
-         */
-        public final int getMinScroll() {
-            return mMinScroll;
-        }
-
-        public final void invalidateScrollMin() {
-            mMinEdge = Integer.MIN_VALUE;
-            mMinScroll = Integer.MIN_VALUE;
-        }
-
-        /**
-         * Returns scroll distance to align max child.
-         */
-        public final int getMaxScroll() {
-            return mMaxScroll;
-        }
-
-        public final void invalidateScrollMax() {
-            mMaxEdge = Integer.MAX_VALUE;
-            mMaxScroll = Integer.MAX_VALUE;
-        }
-
-        void reset() {
-            mMinEdge = Integer.MIN_VALUE;
-            mMaxEdge = Integer.MAX_VALUE;
-        }
-
-        public final boolean isMinUnknown() {
-            return mMinEdge == Integer.MIN_VALUE;
-        }
-
-        public final boolean isMaxUnknown() {
-            return mMaxEdge == Integer.MAX_VALUE;
-        }
-
-        public final void setSize(int size) {
-            mSize = size;
-        }
-
-        public final int getSize() {
-            return mSize;
-        }
-
-        public final void setPadding(int paddingMin, int paddingMax) {
-            mPaddingMin = paddingMin;
-            mPaddingMax = paddingMax;
-        }
-
-        public final int getPaddingMin() {
-            return mPaddingMin;
-        }
-
-        public final int getPaddingMax() {
-            return mPaddingMax;
-        }
-
-        public final int getClientSize() {
-            return mSize - mPaddingMin - mPaddingMax;
-        }
-
-        final int calculateKeyline() {
-            int keyLine;
-            if (!mReversedFlow) {
-                if (mWindowAlignmentOffset >= 0) {
-                    keyLine = mWindowAlignmentOffset;
-                } else {
-                    keyLine = mSize + mWindowAlignmentOffset;
-                }
-                if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
-                    keyLine += (int) (mSize * mWindowAlignmentOffsetPercent / 100);
-                }
-            } else {
-                if (mWindowAlignmentOffset >= 0) {
-                    keyLine = mSize - mWindowAlignmentOffset;
-                } else {
-                    keyLine = -mWindowAlignmentOffset;
-                }
-                if (mWindowAlignmentOffsetPercent != WINDOW_ALIGN_OFFSET_PERCENT_DISABLED) {
-                    keyLine -= (int) (mSize * mWindowAlignmentOffsetPercent / 100);
-                }
-            }
-            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) {
-                    // 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) {
-                    // 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);
-                }
-            }
-            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));
-                        }
-                        // 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));
-                        }
-                        // 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));
-                        }
-                        // 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));
-                        }
-                        // don't over scroll max
-                        mMaxScroll = Math.max(mMinScroll, mMaxScroll);
-                    }
-                }
-            }
-        }
-
-        /**
-         * 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 " min:" + mMinEdge + " " + mMinScroll + " max:" + mMaxEdge + " " + mMaxScroll;
-        }
-
-    }
-
-    private int mOrientation = HORIZONTAL;
-
-    public final Axis vertical = new Axis("vertical");
-
-    public final Axis horizontal = new Axis("horizontal");
-
-    private Axis mMainAxis = horizontal;
-
-    private Axis mSecondAxis = vertical;
-
-    public final Axis mainAxis() {
-        return mMainAxis;
-    }
-
-    public final Axis secondAxis() {
-        return mSecondAxis;
-    }
-
-    public final void setOrientation(int orientation) {
-        mOrientation = orientation;
-        if (mOrientation == HORIZONTAL) {
-            mMainAxis = horizontal;
-            mSecondAxis = vertical;
-        } else {
-            mMainAxis = vertical;
-            mSecondAxis = horizontal;
-        }
-    }
-
-    public final int getOrientation() {
-        return mOrientation;
-    }
-
-    public final void reset() {
-        mainAxis().reset();
-    }
-
-    @Override
-    public String toString() {
-        return "horizontal=" + horizontal + "; vertical=" + vertical;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/package-info.java b/android/support/v17/leanback/widget/package-info.java
deleted file mode 100644
index ed7401e..0000000
--- a/android/support/v17/leanback/widget/package-info.java
+++ /dev/null
@@ -1,67 +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.
- */
-
-/**
- * <p>Support classes providing low level Leanback user interface building blocks:
- * widgets and helpers.</p>
- * <p>
- * The core interface to the developer’s model is the
- * {@link android.support.v17.leanback.widget.ObjectAdapter}. It is similar to Adapter and the
- * RecyclerView Adapter, but separates iterating items from presenting them as Views.
- * Concrete implementations include
- * {@link android.support.v17.leanback.widget.ArrayObjectAdapter} and
- * {@link android.support.v17.leanback.widget.CursorObjectAdapter}, but a developer is free to use a
- * subclass of an ObjectAdapter to iterate over any existing Object hierarchy.
- * </p>
- * <p>
- * A {@link android.support.v17.leanback.widget.Presenter} creates Views and binds data from an Object
- * to those Views. This is the
- * complementary piece to ObjectAdapter that corresponds to existing Android adapter classes.
- * The benefit to separating out a Presenter is that we can use it to generate Views outside of the
- * context of an adapter. For example, a UI may represent data from a single Object in several places
- * at once. Each View that needs to be generated can be produced by a different Presenter, while the
- * Object is retrieved from the ObjectAdapter once.
- * </p>
- * A {@link android.support.v17.leanback.widget.PresenterSelector} determines which Presenter to use
- * for a given Object from an
- * ObjectAdapter. Two common cases are when an ObjectAdapter uses the same View type for every element
- * ({@link android.support.v17.leanback.widget.SinglePresenterSelector}), and when the Presenter is
- * determined by the Java class of
- * the element ({@link android.support.v17.leanback.widget.ClassPresenterSelector}).  A developer is
- * able to implement any selection logic
- * as a PresenterSelector. For example, if all the elements of an ObjectAdapter have the same type,
- * but certain elements are to be rendered using a 'promotional content' view in the developer’s
- * application, the PresenterSelector may inspect the fields of each element before choosing the
- * appropriate Presenter.
- * </p>
- * <p>
- * The basic navigation model for Leanback is that of a vertical list of rows, each of which may
- * be a horizontal list of items. Therefore, Leanback uses ObjectAdapters both for defining the
- * horizontal data items as well as the list of rows themselves.
- * </p>
- * <p>Leanback defines a few basic data model classes for rows: the
- * {@link android.support.v17.leanback.widget.Row}, which defines the
- * abstract concept of a row with a header; and {@link android.support.v17.leanback.widget.ListRow},
- * a concrete Row implementation that uses an ObjectAdapter to present a horizontal list of items.
- * The corresponding presenter for the ListRow is the
- * {@link android.support.v17.leanback.widget.ListRowPresenter}.
- * </p>
- * <p>
- * Other types of Rows and corresponding RowPresenters are provided; however the application may
- * define a custom subclass of {@link android.support.v17.leanback.widget.Row} and
- * {@link android.support.v17.leanback.widget.RowPresenter}.
- * </p>
- */
-
-package android.support.v17.leanback.widget;
diff --git a/android/support/v17/leanback/widget/picker/DatePicker.java b/android/support/v17/leanback/widget/picker/DatePicker.java
deleted file mode 100644
index 2744dec..0000000
--- a/android/support/v17/leanback/widget/picker/DatePicker.java
+++ /dev/null
@@ -1,500 +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.v17.leanback.widget.picker;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-
-/**
- * {@link DatePicker} is a directly subclass of {@link Picker}.
- * This class is a widget for selecting a date. The date can be selected by a
- * year, month, and day Columns. The "minDate" and "maxDate" from which dates to be selected
- * can be customized.  The columns can be customized by attribute "datePickerFormat" or
- * {@link #setDatePickerFormat(String)}.
- *
- * @attr ref R.styleable#lbDatePicker_android_maxDate
- * @attr ref R.styleable#lbDatePicker_android_minDate
- * @attr ref R.styleable#lbDatePicker_datePickerFormat
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class DatePicker extends Picker {
-
-    static final String LOG_TAG = "DatePicker";
-
-    private String mDatePickerFormat;
-    PickerColumn mMonthColumn;
-    PickerColumn mDayColumn;
-    PickerColumn mYearColumn;
-    int mColMonthIndex;
-    int mColDayIndex;
-    int mColYearIndex;
-
-    final static String DATE_FORMAT = "MM/dd/yyyy";
-    final DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
-    PickerUtility.DateConstant mConstant;
-
-    Calendar mMinDate;
-    Calendar mMaxDate;
-    Calendar mCurrentDate;
-    Calendar mTempDate;
-
-    public DatePicker(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        updateCurrentLocale();
-
-        final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
-                R.styleable.lbDatePicker);
-        String minDate = attributesArray.getString(R.styleable.lbDatePicker_android_minDate);
-        String maxDate = attributesArray.getString(R.styleable.lbDatePicker_android_maxDate);
-        mTempDate.clear();
-        if (!TextUtils.isEmpty(minDate)) {
-            if (!parseDate(minDate, mTempDate)) {
-                mTempDate.set(1900, 0, 1);
-            }
-        } else {
-            mTempDate.set(1900, 0, 1);
-        }
-        mMinDate.setTimeInMillis(mTempDate.getTimeInMillis());
-
-        mTempDate.clear();
-        if (!TextUtils.isEmpty(maxDate)) {
-            if (!parseDate(maxDate, mTempDate)) {
-                mTempDate.set(2100, 0, 1);
-            }
-        } else {
-            mTempDate.set(2100, 0, 1);
-        }
-        mMaxDate.setTimeInMillis(mTempDate.getTimeInMillis());
-
-        String datePickerFormat = attributesArray
-                .getString(R.styleable.lbDatePicker_datePickerFormat);
-        if (TextUtils.isEmpty(datePickerFormat)) {
-            datePickerFormat = new String(
-                    android.text.format.DateFormat.getDateFormatOrder(context));
-        }
-        setDatePickerFormat(datePickerFormat);
-    }
-
-    private boolean parseDate(String date, Calendar outDate) {
-        try {
-            outDate.setTime(mDateFormat.parse(date));
-            return true;
-        } catch (ParseException e) {
-            Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
-            return false;
-        }
-    }
-
-    /**
-     * Returns the best localized representation of the date for the given date format and the
-     * current locale.
-     *
-     * @param datePickerFormat The date format skeleton (e.g. "dMy") used to gather the
-     *                         appropriate representation of the date in the current locale.
-     *
-     * @return The best localized representation of the date for the given date format
-     */
-    String getBestYearMonthDayPattern(String datePickerFormat) {
-        final String yearPattern;
-        if (PickerUtility.SUPPORTS_BEST_DATE_TIME_PATTERN) {
-            yearPattern = android.text.format.DateFormat.getBestDateTimePattern(mConstant.locale,
-                    datePickerFormat);
-        } else {
-            final java.text.DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(
-                    getContext());
-            if (dateFormat instanceof SimpleDateFormat) {
-                yearPattern = ((SimpleDateFormat) dateFormat).toLocalizedPattern();
-            } else {
-                yearPattern = DATE_FORMAT;
-            }
-        }
-        return TextUtils.isEmpty(yearPattern) ? DATE_FORMAT : yearPattern;
-    }
-
-    /**
-     * Extracts the separators used to separate date fields (including before the first and after
-     * the last date field). The separators can vary based on the individual locale date format,
-     * defined in the Unicode CLDR and cannot be supposed to be "/".
-     *
-     * See http://unicode.org/cldr/trac/browser/trunk/common/main
-     *
-     * For example, for Croatian in dMy format, the best localized representation is "d. M. y". This
-     * method returns {"", ".", ".", "."}, where the first separator indicates nothing needs to be
-     * displayed to the left of the day field, "." needs to be displayed tos the right of the day
-     * field, and so forth.
-     *
-     * @return The ArrayList of separators to populate between the actual date fields in the
-     * DatePicker.
-     */
-    List<CharSequence> extractSeparators() {
-        // Obtain the time format string per the current locale (e.g. h:mm a)
-        String hmaPattern = getBestYearMonthDayPattern(mDatePickerFormat);
-
-        List<CharSequence> separators = new ArrayList<>();
-        StringBuilder sb = new StringBuilder();
-        char lastChar = '\0';
-        // See http://www.unicode.org/reports/tr35/tr35-dates.html for date formats
-        final char[] dateFormats = {'Y', 'y', 'M', 'm', 'D', 'd'};
-        boolean processingQuote = false;
-        for (int i = 0; i < hmaPattern.length(); i++) {
-            char c = hmaPattern.charAt(i);
-            if (c == ' ') {
-                continue;
-            }
-            if (c == '\'') {
-                if (!processingQuote) {
-                    sb.setLength(0);
-                    processingQuote = true;
-                } else {
-                    processingQuote = false;
-                }
-                continue;
-            }
-            if (processingQuote) {
-                sb.append(c);
-            } else {
-                if (isAnyOf(c, dateFormats)) {
-                    if (c != lastChar) {
-                        separators.add(sb.toString());
-                        sb.setLength(0);
-                    }
-                } else {
-                    sb.append(c);
-                }
-            }
-            lastChar = c;
-        }
-        separators.add(sb.toString());
-        return separators;
-    }
-
-    private static boolean isAnyOf(char c, char[] any) {
-        for (int i = 0; i < any.length; i++) {
-            if (c == any[i]) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Changes format of showing dates.  For example "YMD".
-     * @param datePickerFormat Format of showing dates.
-     */
-    public void setDatePickerFormat(String datePickerFormat) {
-        if (TextUtils.isEmpty(datePickerFormat)) {
-            datePickerFormat = new String(
-                    android.text.format.DateFormat.getDateFormatOrder(getContext()));
-        }
-        if (TextUtils.equals(mDatePickerFormat, datePickerFormat)) {
-            return;
-        }
-        mDatePickerFormat = datePickerFormat;
-        List<CharSequence> separators = extractSeparators();
-        if (separators.size() != (datePickerFormat.length() + 1)) {
-            throw new IllegalStateException("Separators size: " + separators.size() + " must equal"
-                    + " the size of datePickerFormat: " + datePickerFormat.length() + " + 1");
-        }
-        setSeparators(separators);
-        mYearColumn = mMonthColumn = mDayColumn = null;
-        mColYearIndex = mColDayIndex = mColMonthIndex = -1;
-        String dateFieldsPattern = datePickerFormat.toUpperCase();
-        ArrayList<PickerColumn> columns = new ArrayList<PickerColumn>(3);
-        for (int i = 0; i < dateFieldsPattern.length(); i++) {
-            switch (dateFieldsPattern.charAt(i)) {
-            case 'Y':
-                if (mYearColumn != null) {
-                    throw new IllegalArgumentException("datePicker format error");
-                }
-                columns.add(mYearColumn = new PickerColumn());
-                mColYearIndex = i;
-                mYearColumn.setLabelFormat("%d");
-                break;
-            case 'M':
-                if (mMonthColumn != null) {
-                    throw new IllegalArgumentException("datePicker format error");
-                }
-                columns.add(mMonthColumn = new PickerColumn());
-                mMonthColumn.setStaticLabels(mConstant.months);
-                mColMonthIndex = i;
-                break;
-            case 'D':
-                if (mDayColumn != null) {
-                    throw new IllegalArgumentException("datePicker format error");
-                }
-                columns.add(mDayColumn = new PickerColumn());
-                mDayColumn.setLabelFormat("%02d");
-                mColDayIndex = i;
-                break;
-            default:
-                throw new IllegalArgumentException("datePicker format error");
-            }
-        }
-        setColumns(columns);
-        updateSpinners(false);
-    }
-
-    /**
-     * Get format of showing dates.  For example "YMD".  Default value is from
-     * {@link android.text.format.DateFormat#getDateFormatOrder(Context)}.
-     * @return Format of showing dates.
-     */
-    public String getDatePickerFormat() {
-        return mDatePickerFormat;
-    }
-
-    private void updateCurrentLocale() {
-        mConstant = PickerUtility.getDateConstantInstance(Locale.getDefault(),
-                getContext().getResources());
-        mTempDate = PickerUtility.getCalendarForLocale(mTempDate, mConstant.locale);
-        mMinDate = PickerUtility.getCalendarForLocale(mMinDate, mConstant.locale);
-        mMaxDate = PickerUtility.getCalendarForLocale(mMaxDate, mConstant.locale);
-        mCurrentDate = PickerUtility.getCalendarForLocale(mCurrentDate, mConstant.locale);
-
-        if (mMonthColumn != null) {
-            mMonthColumn.setStaticLabels(mConstant.months);
-            setColumnAt(mColMonthIndex, mMonthColumn);
-        }
-    }
-
-    @Override
-    public final void onColumnValueChanged(int column, int newVal) {
-        mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
-        // take care of wrapping of days and months to update greater fields
-        int oldVal = getColumnAt(column).getCurrentValue();
-        if (column == mColDayIndex) {
-            mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
-        } else if (column == mColMonthIndex) {
-            mTempDate.add(Calendar.MONTH, newVal - oldVal);
-        } else if (column == mColYearIndex) {
-            mTempDate.add(Calendar.YEAR, newVal - oldVal);
-        } else {
-            throw new IllegalArgumentException();
-        }
-        setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
-                mTempDate.get(Calendar.DAY_OF_MONTH));
-        updateSpinners(false);
-    }
-
-
-    /**
-     * Sets the minimal date supported by this {@link DatePicker} in
-     * milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     *
-     * @param minDate The minimal supported date.
-     */
-    public void setMinDate(long minDate) {
-        mTempDate.setTimeInMillis(minDate);
-        if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
-                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
-            return;
-        }
-        mMinDate.setTimeInMillis(minDate);
-        if (mCurrentDate.before(mMinDate)) {
-            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
-        }
-        updateSpinners(false);
-    }
-
-
-    /**
-     * Gets the minimal date supported by this {@link DatePicker} in
-     * milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     * <p>
-     * Note: The default minimal date is 01/01/1900.
-     * <p>
-     *
-     * @return The minimal supported date.
-     */
-    public long getMinDate() {
-        return mMinDate.getTimeInMillis();
-    }
-
-    /**
-     * Sets the maximal date supported by this {@link DatePicker} in
-     * milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     *
-     * @param maxDate The maximal supported date.
-     */
-    public void setMaxDate(long maxDate) {
-        mTempDate.setTimeInMillis(maxDate);
-        if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
-                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
-            return;
-        }
-        mMaxDate.setTimeInMillis(maxDate);
-        if (mCurrentDate.after(mMaxDate)) {
-            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
-        }
-        updateSpinners(false);
-    }
-
-    /**
-     * Gets the maximal date supported by this {@link DatePicker} in
-     * milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     * <p>
-     * Note: The default maximal date is 12/31/2100.
-     * <p>
-     *
-     * @return The maximal supported date.
-     */
-    public long getMaxDate() {
-        return mMaxDate.getTimeInMillis();
-    }
-
-    /**
-     * Gets current date value in milliseconds since January 1, 1970 00:00:00 in
-     * {@link TimeZone#getDefault()} time zone.
-     *
-     * @return Current date values.
-     */
-    public long getDate() {
-        return mCurrentDate.getTimeInMillis();
-    }
-
-    private void setDate(int year, int month, int dayOfMonth) {
-        mCurrentDate.set(year, month, dayOfMonth);
-        if (mCurrentDate.before(mMinDate)) {
-            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
-        } else if (mCurrentDate.after(mMaxDate)) {
-            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
-        }
-    }
-
-    /**
-     * Update the current date.
-     *
-     * @param year The year.
-     * @param month The month which is <strong>starting from zero</strong>.
-     * @param dayOfMonth The day of the month.
-     * @param animation True to run animation to scroll the column.
-     */
-    public void updateDate(int year, int month, int dayOfMonth, boolean animation) {
-        if (!isNewDate(year, month, dayOfMonth)) {
-            return;
-        }
-        setDate(year, month, dayOfMonth);
-        updateSpinners(animation);
-    }
-
-    private boolean isNewDate(int year, int month, int dayOfMonth) {
-        return (mCurrentDate.get(Calendar.YEAR) != year
-                || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
-                || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month);
-    }
-
-    private static boolean updateMin(PickerColumn column, int value) {
-        if (value != column.getMinValue()) {
-            column.setMinValue(value);
-            return true;
-        }
-        return false;
-    }
-
-    private static boolean updateMax(PickerColumn column, int value) {
-        if (value != column.getMaxValue()) {
-            column.setMaxValue(value);
-            return true;
-        }
-        return false;
-    }
-
-    private static int[] DATE_FIELDS = {Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR};
-
-    // Following implementation always keeps up-to-date date ranges (min & max values) no matter
-    // what the currently selected date is. This prevents the constant updating of date values while
-    // scrolling vertically and thus fixes the animation jumps that used to happen when we reached
-    // the endpoint date field values since the adapter values do not change while scrolling up
-    // & down across a single field.
-    void updateSpinnersImpl(boolean animation) {
-        // set the spinner ranges respecting the min and max dates
-        int dateFieldIndices[] = {mColDayIndex, mColMonthIndex, mColYearIndex};
-
-        boolean allLargerDateFieldsHaveBeenEqualToMinDate = true;
-        boolean allLargerDateFieldsHaveBeenEqualToMaxDate = true;
-        for(int i = DATE_FIELDS.length - 1; i >= 0; i--) {
-            boolean dateFieldChanged = false;
-            if (dateFieldIndices[i] < 0)
-                continue;
-
-            int currField = DATE_FIELDS[i];
-            PickerColumn currPickerColumn = getColumnAt(dateFieldIndices[i]);
-
-            if (allLargerDateFieldsHaveBeenEqualToMinDate) {
-                dateFieldChanged |= updateMin(currPickerColumn,
-                        mMinDate.get(currField));
-            } else {
-                dateFieldChanged |= updateMin(currPickerColumn,
-                        mCurrentDate.getActualMinimum(currField));
-            }
-
-            if (allLargerDateFieldsHaveBeenEqualToMaxDate) {
-                dateFieldChanged |= updateMax(currPickerColumn,
-                        mMaxDate.get(currField));
-            } else {
-                dateFieldChanged |= updateMax(currPickerColumn,
-                        mCurrentDate.getActualMaximum(currField));
-            }
-
-            allLargerDateFieldsHaveBeenEqualToMinDate &=
-                    (mCurrentDate.get(currField) == mMinDate.get(currField));
-            allLargerDateFieldsHaveBeenEqualToMaxDate &=
-                    (mCurrentDate.get(currField) == mMaxDate.get(currField));
-
-            if (dateFieldChanged) {
-                setColumnAt(dateFieldIndices[i], currPickerColumn);
-            }
-            setColumnValue(dateFieldIndices[i], mCurrentDate.get(currField), animation);
-        }
-    }
-
-    private void updateSpinners(final boolean animation) {
-        // update range in a post call.  The reason is that RV does not allow notifyDataSetChange()
-        // in scroll pass.  UpdateSpinner can be called in a scroll pass, UpdateSpinner() may
-        // notifyDataSetChange to update the range.
-        post(new Runnable() {
-            @Override
-            public void run() {
-                updateSpinnersImpl(animation);
-            }
-        });
-    }
-}
\ No newline at end of file
diff --git a/android/support/v17/leanback/widget/picker/Picker.java b/android/support/v17/leanback/widget/picker/Picker.java
deleted file mode 100644
index 486e877..0000000
--- a/android/support/v17/leanback/widget/picker/Picker.java
+++ /dev/null
@@ -1,702 +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.v17.leanback.widget.picker;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.widget.OnChildViewHolderSelectedListener;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Picker is a widget showing multiple customized {@link PickerColumn}s. The PickerColumns are
- * initialized in {@link #setColumns(List)}. Call {@link #setColumnAt(int, PickerColumn)} if the
- * column value range or labels change. Call {@link #setColumnValue(int, int, boolean)} to update
- * the current value of PickerColumn.
- * <p>
- * Picker has two states and will change height:
- * <li>{@link #isActivated()} is true: Picker shows typically three items vertically (see
- * {@link #getActivatedVisibleItemCount()}}. Columns other than {@link #getSelectedColumn()} still
- * shows one item if the Picker is focused. On a touch screen device, the Picker will not get focus
- * so it always show three items on all columns. On a non-touch device (a TV), the Picker will show
- * three items only on currently activated column. If the Picker has focus, it will intercept DPAD
- * directions and select activated column.
- * <li>{@link #isActivated()} is false: Picker shows one item vertically (see
- * {@link #getVisibleItemCount()}) on all columns. The size of Picker shrinks.
- */
-public class Picker extends FrameLayout {
-
-    public interface PickerValueListener {
-        public void onValueChanged(Picker picker, int column);
-    }
-
-    private ViewGroup mRootView;
-    private ViewGroup mPickerView;
-    final List<VerticalGridView> mColumnViews = new ArrayList<VerticalGridView>();
-    ArrayList<PickerColumn> mColumns;
-
-    private float mUnfocusedAlpha;
-    private float mFocusedAlpha;
-    private float mVisibleColumnAlpha;
-    private float mInvisibleColumnAlpha;
-    private int mAlphaAnimDuration;
-    private Interpolator mDecelerateInterpolator;
-    private Interpolator mAccelerateInterpolator;
-    private ArrayList<PickerValueListener> mListeners;
-    private float mVisibleItemsActivated = 3;
-    private float mVisibleItems = 1;
-    private int mSelectedColumn = 0;
-
-    private List<CharSequence> mSeparators = new ArrayList<>();
-    private int mPickerItemLayoutId = R.layout.lb_picker_item;
-    private int mPickerItemTextViewId = 0;
-
-    /**
-     * Gets separator string between columns.
-     *
-     * @return The separator that will be populated between all the Picker columns.
-     * @deprecated Use {@link #getSeparators()}
-     */
-    public final CharSequence getSeparator() {
-        return mSeparators.get(0);
-    }
-
-    /**
-     * Sets separator String between Picker columns.
-     *
-     * @param separator Separator String between Picker columns.
-     */
-    public final void setSeparator(CharSequence separator) {
-        setSeparators(Arrays.asList(separator));
-    }
-
-    /**
-     * Returns the list of separators that will be populated between the picker column fields.
-     *
-     * @return The list of separators populated between the picker column fields.
-     */
-    public final List<CharSequence> getSeparators() {
-        return mSeparators;
-    }
-
-    /**
-     * Sets the list of separators that will be populated between the Picker columns. The
-     * number of the separators should be either 1 indicating the same separator used between all
-     * the columns fields (and nothing will be placed before the first and after the last column),
-     * or must be one unit larger than the number of columns passed to {@link #setColumns(List)}.
-     * In the latter case, the list of separators corresponds to the positions before the first
-     * column all the way to the position after the last column.
-     * An empty string for a given position indicates no separators needs to be placed for that
-     * position, otherwise a TextView with the given String will be created and placed there.
-     *
-     * @param separators The list of separators to be populated between the Picker columns.
-     */
-    public final void setSeparators(List<CharSequence> separators) {
-        mSeparators.clear();
-        mSeparators.addAll(separators);
-    }
-
-    /**
-     * Classes extending {@link Picker} can choose to override this method to
-     * supply the {@link Picker}'s item's layout id
-     */
-    public final int getPickerItemLayoutId() {
-        return mPickerItemLayoutId;
-    }
-
-    /**
-     * Returns the {@link Picker}'s item's {@link TextView}'s id from within the
-     * layout provided by {@link Picker#getPickerItemLayoutId()} or 0 if the
-     * layout provided by {@link Picker#getPickerItemLayoutId()} is a {link
-     * TextView}.
-     */
-    public final int getPickerItemTextViewId() {
-        return mPickerItemTextViewId;
-    }
-
-    /**
-     * Sets the {@link Picker}'s item's {@link TextView}'s id from within the
-     * layout provided by {@link Picker#getPickerItemLayoutId()} or 0 if the
-     * layout provided by {@link Picker#getPickerItemLayoutId()} is a {link
-     * TextView}.
-     *
-     * @param textViewId View id of TextView inside a Picker item, or 0 if the Picker item is a
-     *                   TextView.
-     */
-    public final void setPickerItemTextViewId(int textViewId) {
-        mPickerItemTextViewId = textViewId;
-    }
-
-    /**
-     * Creates a Picker widget.
-     */
-    public Picker(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        // Make it enabled and clickable to receive Click event.
-        setEnabled(true);
-        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-
-        mFocusedAlpha = 1f; //getFloat(R.dimen.list_item_selected_title_text_alpha);
-        mUnfocusedAlpha = 1f; //getFloat(R.dimen.list_item_unselected_text_alpha);
-        mVisibleColumnAlpha = 0.5f; //getFloat(R.dimen.picker_item_visible_column_item_alpha);
-        mInvisibleColumnAlpha = 0f; //getFloat(R.dimen.picker_item_invisible_column_item_alpha);
-
-        mAlphaAnimDuration =
-                200; // mContext.getResources().getInteger(R.integer.dialog_animation_duration);
-
-        mDecelerateInterpolator = new DecelerateInterpolator(2.5F);
-        mAccelerateInterpolator = new AccelerateInterpolator(2.5F);
-
-        LayoutInflater inflater = LayoutInflater.from(getContext());
-        mRootView = (ViewGroup) inflater.inflate(R.layout.lb_picker, this, true);
-        mPickerView = (ViewGroup) mRootView.findViewById(R.id.picker);
-    }
-
-    /**
-     * Get nth PickerColumn.
-     *
-     * @param colIndex Index of PickerColumn.
-     * @return PickerColumn at colIndex or null if {@link #setColumns(List)} is not called yet.
-     */
-    public PickerColumn getColumnAt(int colIndex) {
-        if (mColumns == null) {
-            return null;
-        }
-        return mColumns.get(colIndex);
-    }
-
-    /**
-     * Get number of PickerColumns.
-     *
-     * @return Number of PickerColumns or 0 if {@link #setColumns(List)} is not called yet.
-     */
-    public int getColumnsCount() {
-        if (mColumns == null) {
-            return 0;
-        }
-        return mColumns.size();
-    }
-
-    /**
-     * Set columns and create Views.
-     *
-     * @param columns The actual focusable columns of a picker which are scrollable if the field
-     *                takes more than one value (e.g. for a DatePicker, day, month, and year fields
-     *                and for TimePicker, hour, minute, and am/pm fields form the columns).
-     */
-    public void setColumns(List<PickerColumn> columns) {
-        if (mSeparators.size() == 0) {
-            throw new IllegalStateException("Separators size is: " + mSeparators.size()
-                    + ". At least one separator must be provided");
-        } else if (mSeparators.size() == 1) {
-            CharSequence separator = mSeparators.get(0);
-            mSeparators.clear();
-            mSeparators.add("");
-            for (int i = 0; i < columns.size() - 1; i++) {
-                mSeparators.add(separator);
-            }
-            mSeparators.add("");
-        } else {
-            if (mSeparators.size() != (columns.size() + 1)) {
-                throw new IllegalStateException("Separators size: " + mSeparators.size() + " must"
-                        + "equal the size of columns: " + columns.size() + " + 1");
-            }
-        }
-
-        mColumnViews.clear();
-        mPickerView.removeAllViews();
-        mColumns = new ArrayList<PickerColumn>(columns);
-        if (mSelectedColumn > mColumns.size() - 1) {
-            mSelectedColumn = mColumns.size() - 1;
-        }
-        LayoutInflater inflater = LayoutInflater.from(getContext());
-        int totalCol = getColumnsCount();
-
-        if (!TextUtils.isEmpty(mSeparators.get(0))) {
-            TextView separator = (TextView) inflater.inflate(
-                    R.layout.lb_picker_separator, mPickerView, false);
-            separator.setText(mSeparators.get(0));
-            mPickerView.addView(separator);
-        }
-        for (int i = 0; i < totalCol; i++) {
-            final int colIndex = i;
-            final VerticalGridView columnView = (VerticalGridView) inflater.inflate(
-                    R.layout.lb_picker_column, mPickerView, false);
-            // we don't want VerticalGridView to receive focus.
-            updateColumnSize(columnView);
-            // always center aligned, not aligning selected item on top/bottom edge.
-            columnView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-            // Width is dynamic, so has fixed size is false.
-            columnView.setHasFixedSize(false);
-            columnView.setFocusable(isActivated());
-            // Setting cache size to zero in order to rebind item views when picker widget becomes
-            // activated. Rebinding is necessary to update the alphas when the columns are expanded
-            // as a result of the picker getting activated, otherwise the cached views with the
-            // wrong alphas could be laid out.
-            columnView.setItemViewCacheSize(0);
-
-            mColumnViews.add(columnView);
-            // add view to root
-            mPickerView.addView(columnView);
-
-            if (!TextUtils.isEmpty(mSeparators.get(i + 1))) {
-                // add a separator if not the last element
-                TextView separator = (TextView) inflater.inflate(
-                        R.layout.lb_picker_separator, mPickerView, false);
-                separator.setText(mSeparators.get(i + 1));
-                mPickerView.addView(separator);
-            }
-
-            columnView.setAdapter(new PickerScrollArrayAdapter(getContext(),
-                    getPickerItemLayoutId(), getPickerItemTextViewId(), colIndex));
-            columnView.setOnChildViewHolderSelectedListener(mColumnChangeListener);
-        }
-    }
-
-    /**
-     * When column labels change or column range changes, call this function to re-populate the
-     * selection list.  Note this function cannot be called from RecyclerView layout/scroll pass.
-     *
-     * @param columnIndex Index of column to update.
-     * @param column      New column to update.
-     */
-    public void setColumnAt(int columnIndex, PickerColumn column) {
-        mColumns.set(columnIndex, column);
-        VerticalGridView columnView = mColumnViews.get(columnIndex);
-        PickerScrollArrayAdapter adapter = (PickerScrollArrayAdapter) columnView.getAdapter();
-        if (adapter != null) {
-            adapter.notifyDataSetChanged();
-        }
-        columnView.setSelectedPosition(column.getCurrentValue() - column.getMinValue());
-    }
-
-    /**
-     * Manually set current value of a column.  The function will update UI and notify listeners.
-     *
-     * @param columnIndex  Index of column to update.
-     * @param value        New value of the column.
-     * @param runAnimation True to scroll to the value or false otherwise.
-     */
-    public void setColumnValue(int columnIndex, int value, boolean runAnimation) {
-        PickerColumn column = mColumns.get(columnIndex);
-        if (column.getCurrentValue() != value) {
-            column.setCurrentValue(value);
-            notifyValueChanged(columnIndex);
-            VerticalGridView columnView = mColumnViews.get(columnIndex);
-            if (columnView != null) {
-                int position = value - mColumns.get(columnIndex).getMinValue();
-                if (runAnimation) {
-                    columnView.setSelectedPositionSmooth(position);
-                } else {
-                    columnView.setSelectedPosition(position);
-                }
-            }
-        }
-    }
-
-    private void notifyValueChanged(int columnIndex) {
-        if (mListeners != null) {
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onValueChanged(this, columnIndex);
-            }
-        }
-    }
-
-    /**
-     * Register a callback to be invoked when the picker's value has changed.
-     *
-     * @param listener The callback to ad
-     */
-    public void addOnValueChangedListener(PickerValueListener listener) {
-        if (mListeners == null) {
-            mListeners = new ArrayList<Picker.PickerValueListener>();
-        }
-        mListeners.add(listener);
-    }
-
-    /**
-     * Remove a previously installed value changed callback
-     *
-     * @param listener The callback to remove.
-     */
-    public void removeOnValueChangedListener(PickerValueListener listener) {
-        if (mListeners != null) {
-            mListeners.remove(listener);
-        }
-    }
-
-    void updateColumnAlpha(int colIndex, boolean animate) {
-        VerticalGridView column = mColumnViews.get(colIndex);
-
-        int selected = column.getSelectedPosition();
-        View item;
-
-        for (int i = 0; i < column.getAdapter().getItemCount(); i++) {
-            item = column.getLayoutManager().findViewByPosition(i);
-            if (item != null) {
-                setOrAnimateAlpha(item, (selected == i), colIndex, animate);
-            }
-        }
-    }
-
-    void setOrAnimateAlpha(View view, boolean selected, int colIndex,
-            boolean animate) {
-        boolean columnShownAsActivated = colIndex == mSelectedColumn || !hasFocus();
-        if (selected) {
-            // set alpha for main item (selected) in the column
-            if (columnShownAsActivated) {
-                setOrAnimateAlpha(view, animate, mFocusedAlpha, -1, mDecelerateInterpolator);
-            } else {
-                setOrAnimateAlpha(view, animate, mUnfocusedAlpha, -1, mDecelerateInterpolator);
-            }
-        } else {
-            // set alpha for remaining items in the column
-            if (columnShownAsActivated) {
-                setOrAnimateAlpha(view, animate, mVisibleColumnAlpha, -1, mDecelerateInterpolator);
-            } else {
-                setOrAnimateAlpha(view, animate, mInvisibleColumnAlpha, -1,
-                        mDecelerateInterpolator);
-            }
-        }
-    }
-
-    private void setOrAnimateAlpha(View view, boolean animate, float destAlpha, float startAlpha,
-            Interpolator interpolator) {
-        view.animate().cancel();
-        if (!animate) {
-            view.setAlpha(destAlpha);
-        } else {
-            if (startAlpha >= 0.0f) {
-                // set a start alpha
-                view.setAlpha(startAlpha);
-            }
-            view.animate().alpha(destAlpha)
-                    .setDuration(mAlphaAnimDuration).setInterpolator(interpolator)
-                    .start();
-        }
-    }
-
-    /**
-     * Classes extending {@link Picker} can override this function to supply the
-     * behavior when a list has been scrolled.  Subclass may call {@link #setColumnValue(int, int,
-     * boolean)} and or {@link #setColumnAt(int, PickerColumn)}.  Subclass should not directly call
-     * {@link PickerColumn#setCurrentValue(int)} which does not update internal state or notify
-     * listeners.
-     *
-     * @param columnIndex index of which column was changed.
-     * @param newValue    A new value desired to be set on the column.
-     */
-    public void onColumnValueChanged(int columnIndex, int newValue) {
-        PickerColumn column = mColumns.get(columnIndex);
-        if (column.getCurrentValue() != newValue) {
-            column.setCurrentValue(newValue);
-            notifyValueChanged(columnIndex);
-        }
-    }
-
-    private float getFloat(int resourceId) {
-        TypedValue buffer = new TypedValue();
-        getContext().getResources().getValue(resourceId, buffer, true);
-        return buffer.getFloat();
-    }
-
-    static class ViewHolder extends RecyclerView.ViewHolder {
-        final TextView textView;
-
-        ViewHolder(View v, TextView textView) {
-            super(v);
-            this.textView = textView;
-        }
-    }
-
-    class PickerScrollArrayAdapter extends RecyclerView.Adapter<ViewHolder> {
-
-        private final int mResource;
-        private final int mColIndex;
-        private final int mTextViewResourceId;
-        private PickerColumn mData;
-
-        PickerScrollArrayAdapter(Context context, int resource, int textViewResourceId,
-                int colIndex) {
-            mResource = resource;
-            mColIndex = colIndex;
-            mTextViewResourceId = textViewResourceId;
-            mData = mColumns.get(mColIndex);
-        }
-
-        @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            View v = inflater.inflate(mResource, parent, false);
-            TextView textView;
-            if (mTextViewResourceId != 0) {
-                textView = (TextView) v.findViewById(mTextViewResourceId);
-            } else {
-                textView = (TextView) v;
-            }
-            ViewHolder vh = new ViewHolder(v, textView);
-            return vh;
-        }
-
-        @Override
-        public void onBindViewHolder(ViewHolder holder, int position) {
-            if (holder.textView != null && mData != null) {
-                holder.textView.setText(mData.getLabelFor(mData.getMinValue() + position));
-            }
-            setOrAnimateAlpha(holder.itemView,
-                    (mColumnViews.get(mColIndex).getSelectedPosition() == position),
-                    mColIndex, false);
-        }
-
-        @Override
-        public void onViewAttachedToWindow(ViewHolder holder) {
-            holder.itemView.setFocusable(isActivated());
-        }
-
-        @Override
-        public int getItemCount() {
-            return mData == null ? 0 : mData.getCount();
-        }
-    }
-
-    private final OnChildViewHolderSelectedListener mColumnChangeListener = new
-            OnChildViewHolderSelectedListener() {
-
-                @Override
-                public void onChildViewHolderSelected(RecyclerView parent,
-                        RecyclerView.ViewHolder child,
-                        int position, int subposition) {
-                    PickerScrollArrayAdapter pickerScrollArrayAdapter =
-                            (PickerScrollArrayAdapter) parent
-                                    .getAdapter();
-
-                    int colIndex = mColumnViews.indexOf(parent);
-                    updateColumnAlpha(colIndex, true);
-                    if (child != null) {
-                        int newValue = mColumns.get(colIndex).getMinValue() + position;
-                        onColumnValueChanged(colIndex, newValue);
-                    }
-                }
-
-            };
-
-    @Override
-    public boolean dispatchKeyEvent(android.view.KeyEvent event) {
-        if (isActivated()) {
-            final int keyCode = event.getKeyCode();
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_DPAD_CENTER:
-                case KeyEvent.KEYCODE_ENTER:
-                    if (event.getAction() == KeyEvent.ACTION_UP) {
-                        performClick();
-                    }
-                    break;
-                default:
-                    return super.dispatchKeyEvent(event);
-            }
-            return true;
-        }
-        return super.dispatchKeyEvent(event);
-    }
-
-    @Override
-    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-        int column = getSelectedColumn();
-        if (column < mColumnViews.size()) {
-            return mColumnViews.get(column).requestFocus(direction, previouslyFocusedRect);
-        }
-        return false;
-    }
-
-    /**
-     * Classes extending {@link Picker} can choose to override this method to
-     * supply the {@link Picker}'s column's single item height in pixels.
-     */
-    protected int getPickerItemHeightPixels() {
-        return getContext().getResources().getDimensionPixelSize(R.dimen.picker_item_height);
-    }
-
-    private void updateColumnSize() {
-        for (int i = 0; i < getColumnsCount(); i++) {
-            updateColumnSize(mColumnViews.get(i));
-        }
-    }
-
-    private void updateColumnSize(VerticalGridView columnView) {
-        ViewGroup.LayoutParams lp = columnView.getLayoutParams();
-        float itemCount = isActivated() ? getActivatedVisibleItemCount() : getVisibleItemCount();
-        lp.height = (int) (getPickerItemHeightPixels() * itemCount
-                + columnView.getVerticalSpacing() * (itemCount - 1));
-        columnView.setLayoutParams(lp);
-    }
-
-    private void updateItemFocusable() {
-        final boolean activated = isActivated();
-        for (int i = 0; i < getColumnsCount(); i++) {
-            VerticalGridView grid = mColumnViews.get(i);
-            for (int j = 0; j < grid.getChildCount(); j++) {
-                View view = grid.getChildAt(j);
-                view.setFocusable(activated);
-            }
-        }
-    }
-
-    /**
-     * Returns number of visible items showing in a column when it's activated.  The default value
-     * is 3.
-     *
-     * @return Number of visible items showing in a column when it's activated.
-     */
-    public float getActivatedVisibleItemCount() {
-        return mVisibleItemsActivated;
-    }
-
-    /**
-     * Changes number of visible items showing in a column when it's activated.  The default value
-     * is 3.
-     *
-     * @param visiblePickerItems Number of visible items showing in a column when it's activated.
-     */
-    public void setActivatedVisibleItemCount(float visiblePickerItems) {
-        if (visiblePickerItems <= 0) {
-            throw new IllegalArgumentException();
-        }
-        if (mVisibleItemsActivated != visiblePickerItems) {
-            mVisibleItemsActivated = visiblePickerItems;
-            if (isActivated()) {
-                updateColumnSize();
-            }
-        }
-    }
-
-    /**
-     * Returns number of visible items showing in a column when it's not activated.  The default
-     * value is 1.
-     *
-     * @return Number of visible items showing in a column when it's not activated.
-     */
-    public float getVisibleItemCount() {
-        return 1;
-    }
-
-    /**
-     * Changes number of visible items showing in a column when it's not activated.  The default
-     * value is 1.
-     *
-     * @param pickerItems Number of visible items showing in a column when it's not activated.
-     */
-    public void setVisibleItemCount(float pickerItems) {
-        if (pickerItems <= 0) {
-            throw new IllegalArgumentException();
-        }
-        if (mVisibleItems != pickerItems) {
-            mVisibleItems = pickerItems;
-            if (!isActivated()) {
-                updateColumnSize();
-            }
-        }
-    }
-
-    @Override
-    public void setActivated(boolean activated) {
-        if (activated == isActivated()) {
-            super.setActivated(activated);
-            return;
-        }
-        super.setActivated(activated);
-        boolean hadFocus = hasFocus();
-        int column = getSelectedColumn();
-        // To avoid temporary focus loss in both the following cases, we set Picker's flag to
-        // FOCUS_BEFORE_DESCENDANTS first, and then back to FOCUS_AFTER_DESCENDANTS once done with
-        // the focus logic.
-        // 1. When changing from activated to deactivated, the Picker should grab the focus
-        // back if it's focusable. However, calling requestFocus on it will transfer the focus down
-        // to its children if it's flag is FOCUS_AFTER_DESCENDANTS.
-        // 2. When changing from deactivated to activated, while setting focusable flags on each
-        // column VerticalGridView, that column will call requestFocus (regardless of which column
-        // is the selected column) since the currently focused view (Picker) has a flag of
-        // FOCUS_AFTER_DESCENDANTS.
-        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
-        if (!activated && hadFocus && isFocusable()) {
-            // When picker widget that originally had focus is deactivated and it is focusable, we
-            // should not pass the focus down to the children. The Picker itself will capture focus.
-            requestFocus();
-        }
-
-        for (int i = 0; i < getColumnsCount(); i++) {
-            mColumnViews.get(i).setFocusable(activated);
-        }
-
-        updateColumnSize();
-        updateItemFocusable();
-        if (activated && hadFocus && (column >= 0)) {
-            mColumnViews.get(column).requestFocus();
-        }
-        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        super.requestChildFocus(child, focused);
-        for (int i = 0; i < mColumnViews.size(); i++) {
-            if (mColumnViews.get(i).hasFocus()) {
-                setSelectedColumn(i);
-            }
-        }
-    }
-
-    /**
-     * Change current selected column.  Picker shows multiple items on selected column if Picker has
-     * focus.  Picker shows multiple items on all column if Picker has no focus (e.g. a Touchscreen
-     * screen).
-     *
-     * @param columnIndex Index of column to activate.
-     */
-    public void setSelectedColumn(int columnIndex) {
-        if (mSelectedColumn != columnIndex) {
-            mSelectedColumn = columnIndex;
-            for (int i = 0; i < mColumnViews.size(); i++) {
-                updateColumnAlpha(i, true);
-            }
-        }
-    }
-
-    /**
-     * Get current activated column index.
-     *
-     * @return Current activated column index.
-     */
-    public int getSelectedColumn() {
-        return mSelectedColumn;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/picker/PickerColumn.java b/android/support/v17/leanback/widget/picker/PickerColumn.java
deleted file mode 100644
index b307df8..0000000
--- a/android/support/v17/leanback/widget/picker/PickerColumn.java
+++ /dev/null
@@ -1,140 +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.v17.leanback.widget.picker;
-
-/**
- * Picker column class used by {@link Picker}, defines a contiguous value ranges and associated
- * labels.  A PickerColumn has a minValue and maxValue to choose between.  The Picker column has
- * a current value.
- * The labels can be dynamically generated from value by {@link #setLabelFormat(String)} or
- * a list of static labels set by {@link #setStaticLabels(CharSequence[])}.
- */
-public class PickerColumn {
-
-    private int mCurrentValue;
-    private int mMinValue;
-    private int mMaxValue;
-    private CharSequence[] mStaticLabels;
-    private String mLabelFormat;
-
-    public PickerColumn() {
-    }
-
-    /**
-     * Set string format (see {@link String#format}) to display label for an
-     * integer value.  {@link #setStaticLabels(CharSequence[])} overrides the format.
-     *
-     * @param labelFormat String format to display label for value between minValue and maxValue.
-     */
-    public void setLabelFormat(String labelFormat) {
-        mLabelFormat = labelFormat;
-    }
-
-    /**
-     * Return string format (see {@link String#format}) to display label for
-     * value.
-     * @return String format to display label for value.
-     */
-    public String getLabelFormat() {
-        return mLabelFormat;
-    }
-
-    /**
-     * Set static labels for each value, minValue maps to labels[0], maxValue maps to
-     * labels[labels.length - 1].
-     * @param labels Static labels for each value between minValue and maxValue.
-     */
-    public void setStaticLabels(CharSequence[] labels) {
-        mStaticLabels = labels;
-    }
-
-    /**
-     * Returns static labels for each value, minValue maps to labels[0], maxValue maps to
-     * labels[labels.length - 1].  When null, {@link #getLabelFormat()} will be used.
-     */
-    public CharSequence[] getStaticLabels() {
-        return mStaticLabels;
-    }
-
-    /**
-     * Get a label for value. The label can be static {@link #setStaticLabels(CharSequence[])}
-     * or dynamically generated {@link #setLabelFormat(String)} when static labels is null.
-     * 
-     * @param value Value between minValue and maxValue.
-     * @return Label for the value.
-     */
-    public CharSequence getLabelFor(int value) {
-        if (mStaticLabels == null) {
-            return String.format(mLabelFormat, value);
-        }
-        return mStaticLabels[value];
-    }
-
-    /**
-     * Returns current value of the Column.
-     * @return Current value of the Column.
-     */
-    public int getCurrentValue() {
-        return mCurrentValue;
-    }
-
-    /**
-     * Sets current value of the Column.
-     */
-    public void setCurrentValue(int value) {
-        mCurrentValue = value;
-    }
-
-    /**
-     * Get total items count between minValue and maxValue.
-     * @return Total items count between minValue and maxValue.
-     */
-    public int getCount() {
-        return mMaxValue - mMinValue + 1;
-    }
-
-    /**
-     * Returns minimal value of the Column.
-     * @return Minimal value of the Column.
-     */
-    public int getMinValue() {
-        return mMinValue;
-    }
-
-    /**
-     * Returns maximum value of the Column.
-     * @return Maximum value of the Column.
-     */
-    public int getMaxValue() {
-        return mMaxValue;
-    }
-
-    /**
-     * Sets minimal value of the Column.
-     * @param minValue New minimal value to set.
-     */
-    public void setMinValue(int minValue) {
-        mMinValue = minValue;
-    }
-
-    /**
-     * Sets maximum value of the Column.
-     * @param maxValue New maximum value to set.
-     */
-    public void setMaxValue(int maxValue) {
-        mMaxValue = maxValue;
-    }
-
-}
diff --git a/android/support/v17/leanback/widget/picker/PickerUtility.java b/android/support/v17/leanback/widget/picker/PickerUtility.java
deleted file mode 100644
index bff278a..0000000
--- a/android/support/v17/leanback/widget/picker/PickerUtility.java
+++ /dev/null
@@ -1,98 +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.v17.leanback.widget.picker;
-
-import android.content.res.Resources;
-import android.os.Build;
-
-import java.text.DateFormatSymbols;
-import java.util.Calendar;
-import java.util.Locale;
-
-/**
- * Utility class that provides Date/Time related constants as well as locale-specific calendar for
- * both {@link DatePicker} and {@link TimePicker}.
- */
-class PickerUtility {
-
-    // Whether the API version supports the use of {@link DateFormat#getBestDateTimePattern()}
-    static final boolean SUPPORTS_BEST_DATE_TIME_PATTERN =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
-    public static class DateConstant {
-        public final Locale locale;
-        public final String[] months;
-        public final String[] days;
-
-        private DateConstant(Locale locale, Resources resources) {
-            this.locale = locale;
-            DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
-            months = symbols.getShortMonths();
-            Calendar calendar = Calendar.getInstance(locale);
-            days = createStringIntArrays(calendar.getMinimum(Calendar.DAY_OF_MONTH),
-                    calendar.getMaximum(Calendar.DAY_OF_MONTH), "%02d");
-        }
-    }
-
-    public static class TimeConstant {
-        public final Locale locale;
-        public final String[] hours12;
-        public final String[] hours24;
-        public final String[] minutes;
-        public final String[] ampm;
-
-        private TimeConstant(Locale locale, Resources resources) {
-            this.locale = locale;
-            DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
-            hours12 = createStringIntArrays(1, 12, "%02d");
-            hours24 = createStringIntArrays(0, 23, "%02d");
-            minutes = createStringIntArrays(0, 59, "%02d");
-            ampm = symbols.getAmPmStrings();
-        }
-    }
-
-    public static DateConstant getDateConstantInstance(Locale locale, Resources resources) {
-        return new DateConstant(locale, resources);
-    }
-
-    public static TimeConstant getTimeConstantInstance(Locale locale, Resources resources) {
-        return new TimeConstant(locale, resources);
-    }
-
-
-    public static String[] createStringIntArrays(int firstNumber, int lastNumber, String format) {
-        String[] array = new String[lastNumber - firstNumber + 1];
-        for (int i = firstNumber; i <= lastNumber; i++) {
-            if (format != null) {
-                array[i - firstNumber] = String.format(format, i);
-            } else {
-                array[i - firstNumber] = String.valueOf(i);
-            }
-        }
-        return array;
-    }
-
-    public static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
-        if (oldCalendar == null) {
-            return Calendar.getInstance(locale);
-        } else {
-            final long currentTimeMillis = oldCalendar.getTimeInMillis();
-            Calendar newCalendar = Calendar.getInstance(locale);
-            newCalendar.setTimeInMillis(currentTimeMillis);
-            return newCalendar;
-        }
-    }
-}
diff --git a/android/support/v17/leanback/widget/picker/TimePicker.java b/android/support/v17/leanback/widget/picker/TimePicker.java
deleted file mode 100644
index 90a1785..0000000
--- a/android/support/v17/leanback/widget/picker/TimePicker.java
+++ /dev/null
@@ -1,453 +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.v17.leanback.widget.picker;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.IntRange;
-import android.support.v17.leanback.R;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.view.View;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * {@link TimePicker} is a direct subclass of {@link Picker}.
- * <p>
- * This class is a widget for selecting time and displays it according to the formatting for the
- * current system locale. The time can be selected by hour, minute, and AM/PM picker columns.
- * The AM/PM mode is determined by either explicitly setting the current mode through
- * {@link #setIs24Hour(boolean)} or the widget attribute {@code is24HourFormat} (true for 24-hour
- * mode, false for 12-hour mode). Otherwise, TimePicker retrieves the mode based on the current
- * context. In 24-hour mode, TimePicker displays only the hour and minute columns.
- * <p>
- * This widget can show the current time as the initial value if {@code useCurrentTime} is set to
- * true. Each individual time picker field can be set at any time by calling {@link #setHour(int)},
- * {@link #setMinute(int)} using 24-hour time format. The time format can also be changed at any
- * time by calling {@link #setIs24Hour(boolean)}, and the AM/PM picker column will be activated or
- * deactivated accordingly.
- *
- * @attr ref R.styleable#lbTimePicker_is24HourFormat
- * @attr ref R.styleable#lbTimePicker_useCurrentTime
- */
-public class TimePicker extends Picker {
-
-    static final String TAG = "TimePicker";
-
-    private static final int AM_INDEX = 0;
-    private static final int PM_INDEX = 1;
-
-    private static final int HOURS_IN_HALF_DAY = 12;
-    PickerColumn mHourColumn;
-    PickerColumn mMinuteColumn;
-    PickerColumn mAmPmColumn;
-    int mColHourIndex;
-    int mColMinuteIndex;
-    int mColAmPmIndex;
-
-    private final PickerUtility.TimeConstant mConstant;
-
-    private boolean mIs24hFormat;
-
-    private int mCurrentHour;
-    private int mCurrentMinute;
-    private int mCurrentAmPmIndex;
-
-    private String mTimePickerFormat;
-
-    /**
-     * Constructor called when inflating a TimePicker widget. 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.
-     *
-     * @param context the context this TimePicker widget is associated with through which we can
-     *                access the current theme attributes and resources
-     * @param attrs the attributes of the XML tag that is inflating the TimePicker widget
-     */
-    public TimePicker(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * Constructor called when inflating a TimePicker widget.
-     *
-     * @param context the context this TimePicker widget is associated with through which we can
-     *                access the current theme attributes and resources
-     * @param attrs the attributes of the XML tag that is inflating the TimePicker widget
-     * @param defStyleAttr An attribute in the current theme that contains a reference to a style
-     *                     resource that supplies default values for the widget. Can be 0 to not
-     *                     look for defaults.
-     */
-    public TimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mConstant = PickerUtility.getTimeConstantInstance(Locale.getDefault(),
-                context.getResources());
-
-        final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
-                R.styleable.lbTimePicker);
-        mIs24hFormat = attributesArray.getBoolean(R.styleable.lbTimePicker_is24HourFormat,
-                DateFormat.is24HourFormat(context));
-        boolean useCurrentTime = attributesArray.getBoolean(R.styleable.lbTimePicker_useCurrentTime,
-                true);
-
-        // The following 2 methods must be called after setting mIs24hFormat since this attribute is
-        // used to extract the time format string.
-        updateColumns();
-        updateColumnsRange();
-
-        if (useCurrentTime) {
-            Calendar currentDate = PickerUtility.getCalendarForLocale(null,
-                    mConstant.locale);
-            setHour(currentDate.get(Calendar.HOUR_OF_DAY));
-            setMinute(currentDate.get(Calendar.MINUTE));
-            setAmPmValue();
-        }
-    }
-
-    private static boolean updateMin(PickerColumn column, int value) {
-        if (value != column.getMinValue()) {
-            column.setMinValue(value);
-            return true;
-        }
-        return false;
-    }
-
-    private static boolean updateMax(PickerColumn column, int value) {
-        if (value != column.getMaxValue()) {
-            column.setMaxValue(value);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return The best localized representation of time for the current locale
-     */
-    String getBestHourMinutePattern() {
-        final String hourPattern;
-        if (PickerUtility.SUPPORTS_BEST_DATE_TIME_PATTERN) {
-            hourPattern = DateFormat.getBestDateTimePattern(mConstant.locale, mIs24hFormat ? "Hma"
-                    : "hma");
-        } else {
-            // Using short style to avoid picking extra fields e.g. time zone in the returned time
-            // format.
-            final java.text.DateFormat dateFormat =
-                    SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT, mConstant.locale);
-            if (dateFormat instanceof SimpleDateFormat) {
-                String defaultPattern = ((SimpleDateFormat) dateFormat).toPattern();
-                defaultPattern = defaultPattern.replace("s", "");
-                if (mIs24hFormat) {
-                    defaultPattern = defaultPattern.replace('h', 'H').replace("a", "");
-                }
-                hourPattern = defaultPattern;
-            } else {
-                hourPattern = mIs24hFormat ? "H:mma" : "h:mma";
-            }
-        }
-        return TextUtils.isEmpty(hourPattern) ? "h:mma" : hourPattern;
-    }
-
-    /**
-     * Extracts the separators used to separate time fields (including before the first and after
-     * the last time field). The separators can vary based on the individual locale and 12 or
-     * 24 hour time format, defined in the Unicode CLDR and cannot be supposed to be ":".
-     *
-     * See http://unicode.org/cldr/trac/browser/trunk/common/main
-     *
-     * For example, for english in 12 hour format
-     * (time pattern of "h:mm a"), this will return {"", ":", "", ""}, where the first separator
-     * indicates nothing needs to be displayed to the left of the hour field, ":" needs to be
-     * displayed to the right of hour field, and so forth.
-     *
-     * @return The ArrayList of separators to populate between the actual time fields in the
-     * TimePicker.
-     */
-    List<CharSequence> extractSeparators() {
-        // Obtain the time format string per the current locale (e.g. h:mm a)
-        String hmaPattern = getBestHourMinutePattern();
-
-        List<CharSequence> separators = new ArrayList<>();
-        StringBuilder sb = new StringBuilder();
-        char lastChar = '\0';
-        // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
-        final char[] timeFormats = {'H', 'h', 'K', 'k', 'm', 'M', 'a'};
-        boolean processingQuote = false;
-        for (int i = 0; i < hmaPattern.length(); i++) {
-            char c = hmaPattern.charAt(i);
-            if (c == ' ') {
-                continue;
-            }
-            if (c == '\'') {
-                if (!processingQuote) {
-                    sb.setLength(0);
-                    processingQuote = true;
-                } else {
-                    processingQuote = false;
-                }
-                continue;
-            }
-            if (processingQuote) {
-                sb.append(c);
-            } else {
-                if (isAnyOf(c, timeFormats)) {
-                    if (c != lastChar) {
-                        separators.add(sb.toString());
-                        sb.setLength(0);
-                    }
-                } else {
-                    sb.append(c);
-                }
-            }
-            lastChar = c;
-        }
-        separators.add(sb.toString());
-        return separators;
-    }
-
-    private static boolean isAnyOf(char c, char[] any) {
-        for (int i = 0; i < any.length; i++) {
-            if (c == any[i]) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     *
-     * @return the time picker format string based on the current system locale and the layout
-     *         direction
-     */
-    private String extractTimeFields() {
-        // Obtain the time format string per the current locale (e.g. h:mm a)
-        String hmaPattern = getBestHourMinutePattern();
-
-        boolean isRTL = TextUtils.getLayoutDirectionFromLocale(mConstant.locale) == View
-                .LAYOUT_DIRECTION_RTL;
-        boolean isAmPmAtEnd = (hmaPattern.indexOf('a') >= 0)
-                ? (hmaPattern.indexOf("a") > hmaPattern.indexOf("m")) : true;
-        // Hour will always appear to the left of minutes regardless of layout direction.
-        String timePickerFormat = isRTL ? "mh" : "hm";
-
-        if (is24Hour()) {
-            return timePickerFormat;
-        } else {
-            return isAmPmAtEnd ? (timePickerFormat + "a") : ("a" + timePickerFormat);
-        }
-    }
-
-    private void updateColumns() {
-        String timePickerFormat = getBestHourMinutePattern();
-        if (TextUtils.equals(timePickerFormat, mTimePickerFormat)) {
-            return;
-        }
-        mTimePickerFormat = timePickerFormat;
-
-        String timeFieldsPattern = extractTimeFields();
-        List<CharSequence> separators = extractSeparators();
-        if (separators.size() != (timeFieldsPattern.length() + 1)) {
-            throw new IllegalStateException("Separators size: " + separators.size() + " must equal"
-                    + " the size of timeFieldsPattern: " + timeFieldsPattern.length() + " + 1");
-        }
-        setSeparators(separators);
-        timeFieldsPattern = timeFieldsPattern.toUpperCase();
-
-        mHourColumn = mMinuteColumn = mAmPmColumn = null;
-        mColHourIndex = mColMinuteIndex = mColAmPmIndex = -1;
-
-        ArrayList<PickerColumn> columns = new ArrayList<>(3);
-        for (int i = 0; i < timeFieldsPattern.length(); i++) {
-            switch (timeFieldsPattern.charAt(i)) {
-                case 'H':
-                    columns.add(mHourColumn = new PickerColumn());
-                    mHourColumn.setStaticLabels(mConstant.hours24);
-                    mColHourIndex = i;
-                    break;
-                case 'M':
-                    columns.add(mMinuteColumn = new PickerColumn());
-                    mMinuteColumn.setStaticLabels(mConstant.minutes);
-                    mColMinuteIndex = i;
-                    break;
-                case 'A':
-                    columns.add(mAmPmColumn = new PickerColumn());
-                    mAmPmColumn.setStaticLabels(mConstant.ampm);
-                    mColAmPmIndex = i;
-                    updateMin(mAmPmColumn, 0);
-                    updateMax(mAmPmColumn, 1);
-                    break;
-                default:
-                    throw new IllegalArgumentException("Invalid time picker format.");
-            }
-        }
-        setColumns(columns);
-    }
-
-    private void updateColumnsRange() {
-        // updateHourColumn(false);
-        updateMin(mHourColumn, mIs24hFormat ? 0 : 1);
-        updateMax(mHourColumn, mIs24hFormat ? 23 : 12);
-
-        updateMin(mMinuteColumn, 0);
-        updateMax(mMinuteColumn, 59);
-
-        if (mAmPmColumn != null) {
-            updateMin(mAmPmColumn, 0);
-            updateMax(mAmPmColumn, 1);
-        }
-    }
-
-    /**
-     * Updates the value of AM/PM column for a 12 hour time format. The correct value should already
-     * be calculated before this method is called by calling setHour.
-     */
-    private void setAmPmValue() {
-        if (!is24Hour()) {
-            setColumnValue(mColAmPmIndex, mCurrentAmPmIndex, false);
-        }
-    }
-
-    /**
-     * Sets the currently selected hour using a 24-hour time.
-     *
-     * @param hour the hour to set, in the range (0-23)
-     * @see #getHour()
-     */
-    public void setHour(@IntRange(from = 0, to = 23) int hour) {
-        if (hour < 0 || hour > 23) {
-            throw new IllegalArgumentException("hour: " + hour + " is not in [0-23] range in");
-        }
-        mCurrentHour = hour;
-        if (!is24Hour()) {
-            if (mCurrentHour >= HOURS_IN_HALF_DAY) {
-                mCurrentAmPmIndex = PM_INDEX;
-                if (mCurrentHour > HOURS_IN_HALF_DAY) {
-                    mCurrentHour -= HOURS_IN_HALF_DAY;
-                }
-            } else {
-                mCurrentAmPmIndex = AM_INDEX;
-                if (mCurrentHour == 0) {
-                    mCurrentHour = HOURS_IN_HALF_DAY;
-                }
-            }
-            setAmPmValue();
-        }
-        setColumnValue(mColHourIndex, mCurrentHour, false);
-    }
-
-    /**
-     * Returns the currently selected hour using 24-hour time.
-     *
-     * @return the currently selected hour in the range (0-23)
-     * @see #setHour(int)
-     */
-    public int getHour() {
-        if (mIs24hFormat) {
-            return mCurrentHour;
-        }
-        if (mCurrentAmPmIndex == AM_INDEX) {
-            return mCurrentHour % HOURS_IN_HALF_DAY;
-        }
-        return (mCurrentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
-    }
-
-    /**
-     * Sets the currently selected minute.
-     *
-     * @param minute the minute to set, in the range (0-59)
-     * @see #getMinute()
-     */
-    public void setMinute(@IntRange(from = 0, to = 59) int minute) {
-        if (minute < 0 || minute > 59) {
-            throw new IllegalArgumentException("minute: " + minute + " is not in [0-59] range.");
-        }
-        mCurrentMinute = minute;
-        setColumnValue(mColMinuteIndex, mCurrentMinute, false);
-    }
-
-    /**
-     * Returns the currently selected minute.
-     *
-     * @return the currently selected minute, in the range (0-59)
-     * @see #setMinute(int)
-     */
-    public int getMinute() {
-        return mCurrentMinute;
-    }
-
-    /**
-     * Sets whether this widget displays a 24-hour mode or a 12-hour mode with an AM/PM picker.
-     *
-     * @param is24Hour {@code true} to display in 24-hour mode,
-     *                 {@code false} ti display in 12-hour mode with AM/PM.
-     * @see #is24Hour()
-     */
-    public void setIs24Hour(boolean is24Hour) {
-        if (mIs24hFormat == is24Hour) {
-            return;
-        }
-        // the ordering of these statements is important
-        int currentHour = getHour();
-        int currentMinute = getMinute();
-        mIs24hFormat = is24Hour;
-        updateColumns();
-        updateColumnsRange();
-
-        setHour(currentHour);
-        setMinute(currentMinute);
-        setAmPmValue();
-    }
-
-    /**
-     * @return {@code true} if this widget displays time in 24-hour mode,
-     *         {@code false} otherwise.
-     *
-     * @see #setIs24Hour(boolean)
-     */
-    public boolean is24Hour() {
-        return mIs24hFormat;
-    }
-
-    /**
-     * Only meaningful for a 12-hour time.
-     *
-     * @return {@code true} if the currently selected time is in PM,
-     *         {@code false} if the currently selected time in in AM.
-     */
-    public boolean isPm() {
-        return (mCurrentAmPmIndex == PM_INDEX);
-    }
-
-    @Override
-    public void onColumnValueChanged(int columnIndex, int newValue) {
-        if (columnIndex == mColHourIndex) {
-            mCurrentHour = newValue;
-        } else if (columnIndex == mColMinuteIndex) {
-            mCurrentMinute = newValue;
-        } else if (columnIndex == mColAmPmIndex) {
-            mCurrentAmPmIndex = newValue;
-        } else {
-            throw new IllegalArgumentException("Invalid column index.");
-        }
-    }
-}
diff --git a/android/support/v17/preference/BaseLeanbackPreferenceFragment.java b/android/support/v17/preference/BaseLeanbackPreferenceFragment.java
deleted file mode 100644
index 15db3d6..0000000
--- a/android/support/v17/preference/BaseLeanbackPreferenceFragment.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.v17.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v7.preference.PreferenceRecyclerViewAccessibilityDelegate;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-/**
- * This fragment provides a preference fragment with leanback-style behavior, suitable for
- * embedding into broader UI elements.
- */
-public abstract class BaseLeanbackPreferenceFragment extends PreferenceFragment {
-
-    @Override
-    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        VerticalGridView verticalGridView = (VerticalGridView) inflater
-                .inflate(R.layout.leanback_preferences_list, parent, false);
-        verticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
-        verticalGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
-        verticalGridView.setAccessibilityDelegateCompat(
-                new PreferenceRecyclerViewAccessibilityDelegate(verticalGridView));
-        return verticalGridView;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public Fragment getCallbackFragment() {
-        return getParentFragment();
-    }
-}
diff --git a/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java b/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java
deleted file mode 100644
index c9837de..0000000
--- a/android/support/v17/preference/LeanbackListPreferenceDialogFragment.java
+++ /dev/null
@@ -1,336 +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.v17.preference;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-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;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-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(1);
-        args.putString(ARG_KEY, key);
-
-        final LeanbackListPreferenceDialogFragment
-                fragment = new LeanbackListPreferenceDialogFragment();
-        fragment.setArguments(args);
-
-        return fragment;
-    }
-
-    public static LeanbackListPreferenceDialogFragment newInstanceMulti(String key) {
-        final Bundle args = new Bundle(1);
-        args.putString(ARG_KEY, key);
-
-        final LeanbackListPreferenceDialogFragment
-                fragment = new LeanbackListPreferenceDialogFragment();
-        fragment.setArguments(args);
-
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        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);
-        }
-    }
-
-    @Override
-    public @Nullable View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        final View view = inflater.inflate(R.layout.leanback_list_preference_fragment, container,
-                false);
-        final VerticalGridView verticalGridView =
-                (VerticalGridView) view.findViewById(android.R.id.list);
-
-        verticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
-        verticalGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
-        verticalGridView.setAdapter(onCreateAdapter());
-        verticalGridView.requestFocus();
-
-        final CharSequence title = mDialogTitle;
-        if (!TextUtils.isEmpty(title)) {
-            final TextView titleView = (TextView) view.findViewById(R.id.decor_title);
-            titleView.setText(title);
-        }
-
-        final CharSequence message = mDialogMessage;
-        if (!TextUtils.isEmpty(message)) {
-            final TextView messageView = (TextView) view.findViewById(android.R.id.message);
-            messageView.setVisibility(View.VISIBLE);
-            messageView.setText(message);
-        }
-
-        return view;
-    }
-
-    public RecyclerView.Adapter onCreateAdapter() {
-        //final DialogPreference preference = getPreference();
-        if (mMulti) {
-            return new AdapterMulti(mEntries, mEntryValues, mInitialSelections);
-        } else {
-            return new AdapterSingle(mEntries, mEntryValues, mInitialSelection);
-        }
-    }
-
-    public class AdapterSingle extends RecyclerView.Adapter<ViewHolder>
-            implements ViewHolder.OnItemClickListener {
-
-        private final CharSequence[] mEntries;
-        private final CharSequence[] mEntryValues;
-        private CharSequence mSelectedValue;
-
-        public AdapterSingle(CharSequence[] entries, CharSequence[] entryValues,
-                CharSequence selectedValue) {
-            mEntries = entries;
-            mEntryValues = entryValues;
-            mSelectedValue = selectedValue;
-        }
-
-        @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            final View view = inflater.inflate(R.layout.leanback_list_preference_item_single,
-                    parent, false);
-            return new ViewHolder(view, this);
-        }
-
-        @Override
-        public void onBindViewHolder(ViewHolder holder, int position) {
-            holder.getWidgetView().setChecked(mEntryValues[position].equals(mSelectedValue));
-            holder.getTitleView().setText(mEntries[position]);
-        }
-
-        @Override
-        public int getItemCount() {
-            return mEntries.length;
-        }
-
-        @Override
-        public void onItemClick(ViewHolder viewHolder) {
-            final int index = viewHolder.getAdapterPosition();
-            if (index == RecyclerView.NO_POSITION) {
-                return;
-            }
-            final CharSequence entry = mEntryValues[index];
-            final ListPreference preference = (ListPreference) getPreference();
-            if (index >= 0) {
-                String value = mEntryValues[index].toString();
-                if (preference.callChangeListener(value)) {
-                    preference.setValue(value);
-                    mSelectedValue = entry;
-                }
-            }
-
-            getFragmentManager().popBackStack();
-            notifyDataSetChanged();
-        }
-    }
-
-    public class AdapterMulti extends RecyclerView.Adapter<ViewHolder>
-            implements ViewHolder.OnItemClickListener {
-
-        private final CharSequence[] mEntries;
-        private final CharSequence[] mEntryValues;
-        private final Set<String> mSelections;
-
-        public AdapterMulti(CharSequence[] entries, CharSequence[] entryValues,
-                Set<String> initialSelections) {
-            mEntries = entries;
-            mEntryValues = entryValues;
-            mSelections = new HashSet<>(initialSelections);
-        }
-
-        @Override
-        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            final View view = inflater.inflate(R.layout.leanback_list_preference_item_multi, parent,
-                    false);
-            return new ViewHolder(view, this);
-        }
-
-        @Override
-        public void onBindViewHolder(ViewHolder holder, int position) {
-            holder.getWidgetView().setChecked(
-                    mSelections.contains(mEntryValues[position].toString()));
-            holder.getTitleView().setText(mEntries[position]);
-        }
-
-        @Override
-        public int getItemCount() {
-            return mEntries.length;
-        }
-
-        @Override
-        public void onItemClick(ViewHolder viewHolder) {
-            final int index = viewHolder.getAdapterPosition();
-            if (index == RecyclerView.NO_POSITION) {
-                return;
-            }
-            final String entry = mEntryValues[index].toString();
-            if (mSelections.contains(entry)) {
-                mSelections.remove(entry);
-            } else {
-                mSelections.add(entry);
-            }
-            final MultiSelectListPreference multiSelectListPreference
-                    = (MultiSelectListPreference) getPreference();
-            // 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)) {
-                    mSelections.remove(entry);
-                } else {
-                    mSelections.add(entry);
-                }
-            }
-
-            notifyDataSetChanged();
-        }
-    }
-
-    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
-
-        public interface OnItemClickListener {
-            void onItemClick(ViewHolder viewHolder);
-        }
-
-        private final Checkable mWidgetView;
-        private final TextView mTitleView;
-        private final ViewGroup mContainer;
-        private final OnItemClickListener mListener;
-
-        public ViewHolder(@NonNull View view, @NonNull OnItemClickListener listener) {
-            super(view);
-            mWidgetView = (Checkable) view.findViewById(R.id.button);
-            mContainer = (ViewGroup) view.findViewById(R.id.container);
-            mTitleView = (TextView) view.findViewById(android.R.id.title);
-            mContainer.setOnClickListener(this);
-            mListener = listener;
-        }
-
-        public Checkable getWidgetView() {
-            return mWidgetView;
-        }
-
-        public TextView getTitleView() {
-            return mTitleView;
-        }
-
-        public ViewGroup getContainer() {
-            return mContainer;
-        }
-
-        @Override
-        public void onClick(View v) {
-            mListener.onItemClick(this);
-        }
-    }
-}
diff --git a/android/support/v17/preference/LeanbackPreferenceDialogFragment.java b/android/support/v17/preference/LeanbackPreferenceDialogFragment.java
deleted file mode 100644
index 9c1335c..0000000
--- a/android/support/v17/preference/LeanbackPreferenceDialogFragment.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.v17.preference;
-
-import android.app.Fragment;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v7.preference.DialogPreference;
-
-public class LeanbackPreferenceDialogFragment extends Fragment {
-
-    public static final String ARG_KEY = "key";
-
-    private DialogPreference mPreference;
-
-    public LeanbackPreferenceDialogFragment() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            LeanbackPreferenceFragmentTransitionHelperApi21.addTransitions(this);
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final Fragment rawFragment = getTargetFragment();
-        if (!(rawFragment instanceof DialogPreference.TargetFragment)) {
-            throw new IllegalStateException("Target fragment " + rawFragment
-                    + " must implement TargetFragment interface");
-        }
-    }
-
-    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/android/support/v17/preference/LeanbackPreferenceFragment.java b/android/support/v17/preference/LeanbackPreferenceFragment.java
deleted file mode 100644
index 48d14b8..0000000
--- a/android/support/v17/preference/LeanbackPreferenceFragment.java
+++ /dev/null
@@ -1,79 +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.v17.preference;
-
-import android.os.Build;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-/**
- * This fragment provides a fully decorated leanback-style preference fragment, including a
- * list background and header.
- *
- * <p>The following sample code shows a simple leanback preference fragment that is
- * populated from a resource.  The resource it loads is:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences}
- *
- * <p>The fragment needs only to implement {@link #onCreatePreferences(Bundle, String)} to populate
- * the list of preference objects:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
- *      support_fragment_leanback}
- */
-public abstract class LeanbackPreferenceFragment extends BaseLeanbackPreferenceFragment {
-
-    public LeanbackPreferenceFragment() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            LeanbackPreferenceFragmentTransitionHelperApi21.addTransitions(this);
-        }
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        final View view = inflater.inflate(R.layout.leanback_preference_fragment, container, false);
-        final ViewGroup innerContainer = (ViewGroup) view.findViewById(R.id.main_frame);
-        final View innerView = super.onCreateView(inflater, innerContainer, savedInstanceState);
-        if (innerView != null) {
-            innerContainer.addView(innerView);
-        }
-        return view;
-    }
-
-    @Override
-    public void onViewCreated(View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        setTitle(getPreferenceScreen().getTitle());
-    }
-
-    /**
-     * Set the title to be shown above the preference list
-     * @param title Title text to be shown
-     */
-    public void setTitle(CharSequence title) {
-        final View view = getView();
-        final TextView decorTitle = view == null
-                ? null : (TextView) view.findViewById(R.id.decor_title);
-        if (decorTitle != null) {
-            decorTitle.setText(title);
-        }
-    }
-}
diff --git a/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
deleted file mode 100644
index 955ab9e..0000000
--- a/android/support/v17/preference/LeanbackPreferenceFragmentTransitionHelperApi21.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.v17.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Fragment;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v17.leanback.transition.FadeAndShortSlide;
-import android.transition.Transition;
-import android.view.Gravity;
-
-/**
- * @hide
- */
-@RequiresApi(21)
-@RestrictTo(LIBRARY_GROUP)
-public class LeanbackPreferenceFragmentTransitionHelperApi21 {
-
-    public static void addTransitions(Fragment f) {
-        final Transition transitionStartEdge = new FadeAndShortSlide(Gravity.START);
-        final Transition transitionEndEdge = new FadeAndShortSlide(Gravity.END);
-
-        f.setEnterTransition(transitionEndEdge);
-        f.setExitTransition(transitionStartEdge);
-        f.setReenterTransition(transitionStartEdge);
-        f.setReturnTransition(transitionEndEdge);
-    }
-
-
-}
diff --git a/android/support/v17/preference/LeanbackSettingsFragment.java b/android/support/v17/preference/LeanbackSettingsFragment.java
deleted file mode 100644
index d56a2a6..0000000
--- a/android/support/v17/preference/LeanbackSettingsFragment.java
+++ /dev/null
@@ -1,206 +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.v17.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v14.preference.MultiSelectListPreference;
-import android.support.v14.preference.PreferenceFragment;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Space;
-
-/**
- * This fragment provides a container for displaying a {@link LeanbackPreferenceFragment}
- *
- * <p>The following sample code shows a simple leanback preference fragment that is
- * populated from a resource.  The resource it loads is:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences}
- *
- * <p>The sample implements
- * {@link PreferenceFragment.OnPreferenceStartFragmentCallback#onPreferenceStartFragment(PreferenceFragment, Preference)},
- * {@link PreferenceFragment.OnPreferenceStartScreenCallback#onPreferenceStartScreen(PreferenceFragment, PreferenceScreen)},
- * and {@link #onPreferenceStartInitialScreen()}:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
- *      support_fragment_leanback}
- */
-public abstract class LeanbackSettingsFragment extends Fragment
-        implements PreferenceFragment.OnPreferenceStartFragmentCallback,
-        PreferenceFragment.OnPreferenceStartScreenCallback,
-        PreferenceFragment.OnPreferenceDisplayDialogCallback {
-
-    private static final String PREFERENCE_FRAGMENT_TAG =
-            "android.support.v17.preference.LeanbackSettingsFragment.PREFERENCE_FRAGMENT";
-
-    private final RootViewOnKeyListener mRootViewOnKeyListener = new RootViewOnKeyListener();
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        final View v = inflater.inflate(R.layout.leanback_settings_fragment, container, false);
-
-        return v;
-    }
-
-    @Override
-    public void onViewCreated(View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        if (savedInstanceState == null) {
-            onPreferenceStartInitialScreen();
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        // Trap back button presses
-        final LeanbackSettingsRootView rootView = (LeanbackSettingsRootView) getView();
-        if (rootView != null) {
-            rootView.setOnBackKeyListener(mRootViewOnKeyListener);
-        }
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        final LeanbackSettingsRootView rootView = (LeanbackSettingsRootView) getView();
-        if (rootView != null) {
-            rootView.setOnBackKeyListener(null);
-        }
-    }
-
-    @Override
-    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;
-            f = LeanbackListPreferenceDialogFragment.newInstanceSingle(listPreference.getKey());
-            f.setTargetFragment(caller, 0);
-            startPreferenceFragment(f);
-        } else if (pref instanceof MultiSelectListPreference) {
-            MultiSelectListPreference listPreference = (MultiSelectListPreference) pref;
-            f = LeanbackListPreferenceDialogFragment.newInstanceMulti(listPreference.getKey());
-            f.setTargetFragment(caller, 0);
-            startPreferenceFragment(f);
-        }
-        // TODO
-//        else if (pref instanceof EditTextPreference) {
-//
-//        }
-        else {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Called to instantiate the initial {@link android.support.v14.preference.PreferenceFragment}
-     * to be shown in this fragment. Implementations are expected to call
-     * {@link #startPreferenceFragment(android.app.Fragment)}.
-     */
-    public abstract void onPreferenceStartInitialScreen();
-
-    /**
-     * Displays a preference fragment to the user. This method can also be used to display
-     * list-style fragments on top of the stack of preference fragments.
-     *
-     * @param fragment Fragment instance to be added.
-     */
-    public void startPreferenceFragment(@NonNull Fragment fragment) {
-        final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
-        final Fragment prevFragment =
-                getChildFragmentManager().findFragmentByTag(PREFERENCE_FRAGMENT_TAG);
-        if (prevFragment != null) {
-            transaction
-                    .addToBackStack(null)
-                    .replace(R.id.settings_preference_fragment_container, fragment,
-                            PREFERENCE_FRAGMENT_TAG);
-        } else {
-            transaction
-                    .add(R.id.settings_preference_fragment_container, fragment,
-                            PREFERENCE_FRAGMENT_TAG);
-        }
-        transaction.commit();
-    }
-
-    /**
-     * Displays a fragment to the user, temporarily replacing the contents of this fragment.
-     *
-     * @param fragment Fragment instance to be added.
-     */
-    public void startImmersiveFragment(@NonNull Fragment fragment) {
-        final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
-        final Fragment preferenceFragment =
-                getChildFragmentManager().findFragmentByTag(PREFERENCE_FRAGMENT_TAG);
-        if (preferenceFragment != null && !preferenceFragment.isHidden()) {
-            if (Build.VERSION.SDK_INT < 23) {
-                // b/22631964
-                transaction.add(R.id.settings_preference_fragment_container, new DummyFragment());
-            }
-            transaction.remove(preferenceFragment);
-        }
-        transaction
-                .add(R.id.settings_dialog_container, fragment)
-                .addToBackStack(null)
-                .commit();
-    }
-
-    private class RootViewOnKeyListener implements View.OnKeyListener {
-
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                return getChildFragmentManager().popBackStackImmediate();
-            } else {
-                return false;
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class DummyFragment extends Fragment {
-
-        @Override
-        public @Nullable View onCreateView(LayoutInflater inflater, ViewGroup container,
-                Bundle savedInstanceState) {
-            final View v = new Space(inflater.getContext());
-            v.setVisibility(View.GONE);
-            return v;
-        }
-    }
-}
diff --git a/android/support/v17/preference/LeanbackSettingsRootView.java b/android/support/v17/preference/LeanbackSettingsRootView.java
deleted file mode 100644
index fa221e6..0000000
--- a/android/support/v17/preference/LeanbackSettingsRootView.java
+++ /dev/null
@@ -1,61 +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.v17.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.widget.FrameLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class LeanbackSettingsRootView extends FrameLayout {
-
-    private OnKeyListener mOnBackKeyListener;
-
-    public LeanbackSettingsRootView(Context context) {
-        super(context);
-    }
-
-    public LeanbackSettingsRootView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public LeanbackSettingsRootView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public void setOnBackKeyListener(OnKeyListener backKeyListener) {
-        mOnBackKeyListener = backKeyListener;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
-        boolean handled = false;
-        if (event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_BACK
-                && mOnBackKeyListener != null) {
-            handled = mOnBackKeyListener.onKey(this, event.getKeyCode(), event);
-        }
-        return handled || super.dispatchKeyEvent(event);
-    }
-}
diff --git a/android/support/v4/BaseInstrumentationTestCase.java b/android/support/v4/BaseInstrumentationTestCase.java
new file mode 100644
index 0000000..e480297
--- /dev/null
+++ b/android/support/v4/BaseInstrumentationTestCase.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.support.v4;
+
+import android.app.Activity;
+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);
+    }
+}
diff --git a/android/support/v4/BaseTestActivity.java b/android/support/v4/BaseTestActivity.java
new file mode 100644
index 0000000..e0682ce
--- /dev/null
+++ b/android/support/v4/BaseTestActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public abstract class BaseTestActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        final int contentView = getContentViewLayoutResId();
+        if (contentView > 0) {
+            setContentView(contentView);
+        }
+        onContentViewSet();
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+    }
+
+    protected abstract int getContentViewLayoutResId();
+
+    protected void onContentViewSet() {}
+}
diff --git a/android/support/v4/ThemedYellowActivity.java b/android/support/v4/ThemedYellowActivity.java
new file mode 100644
index 0000000..e256251
--- /dev/null
+++ b/android/support/v4/ThemedYellowActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+public class ThemedYellowActivity extends Activity {
+    FrameLayout mContainer;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mContainer = new FrameLayout(this);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        setContentView(mContainer);
+    }
+}
diff --git a/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java b/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
deleted file mode 100644
index 0dcd902..0000000
--- a/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
+++ /dev/null
@@ -1,319 +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.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View;
-
-/**
- * Helper for accessing features in {@link AccessibilityServiceInfo}.
- */
-public final class AccessibilityServiceInfoCompat {
-    /**
-     * Capability: This accessibility service can retrieve the active window content.
-     */
-    public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001;
-
-    /**
-     * Capability: This accessibility service can request touch exploration mode in which
-     * touched items are spoken aloud and the UI can be explored via gestures.
-     */
-    public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002;
-
-    /**
-     * Capability: This accessibility service can request enhanced web accessibility
-     * enhancements. For example, installing scripts to make app content more accessible.
-     */
-    public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004;
-
-    /**
-     * Capability: This accessibility service can filter the key event stream.
-     */
-    public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 0x00000008;
-
-    // Feedback types
-
-    /**
-     * Denotes braille feedback.
-     */
-    public static final int FEEDBACK_BRAILLE = 0x0000020;
-
-    /**
-     * Mask for all feedback types.
-     *
-     * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
-     * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
-     * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
-     * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
-     * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
-     * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
-     */
-    public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF;
-
-    // Flags
-
-    /**
-     * If this flag is set the system will regard views that are not important
-     * for accessibility in addition to the ones that are important for accessibility.
-     * That is, views that are marked as not important for accessibility via
-     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} or
-     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are
-     * marked as potentially important for accessibility via
-     * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined
-     * that are not important for accessibility, are both reported while querying the
-     * window content and also the accessibility service will receive accessibility events
-     * from them.
-     * <p>
-     * <strong>Note:</strong> For accessibility services targeting API version
-     * {@link Build.VERSION_CODES#JELLY_BEAN} or higher this flag has to be explicitly
-     * set for the system to regard views that are not important for accessibility. For
-     * accessibility services targeting API version lower than
-     * {@link Build.VERSION_CODES#JELLY_BEAN} this flag is ignored and all views are
-     * regarded for accessibility purposes.
-     * </p>
-     * <p>
-     * Usually views not important for accessibility are layout managers that do not
-     * react to user actions, do not draw any content, and do not have any special
-     * semantics in the context of the screen content. For example, a three by three
-     * grid can be implemented as three horizontal linear layouts and one vertical,
-     * or three vertical linear layouts and one horizontal, or one grid layout, etc.
-     * In this context the actual layout mangers used to achieve the grid configuration
-     * are not important, rather it is important that there are nine evenly distributed
-     * elements.
-     * </p>
-     */
-    public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002;
-
-    /**
-     * This flag requests that the system gets into touch exploration mode.
-     * In this mode a single finger moving on the screen behaves as a mouse
-     * pointer hovering over the user interface. The system will also detect
-     * certain gestures performed on the touch screen and notify this service.
-     * The system will enable touch exploration mode if there is at least one
-     * accessibility service that has this flag set. Hence, clearing this
-     * flag does not guarantee that the device will not be in touch exploration
-     * mode since there may be another enabled service that requested it.
-     * <p>
-     * For accessibility services targeting API version higher than
-     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} that want to set
-     * this flag have to declare this capability in their meta-data by setting
-     * the attribute canRequestTouchExplorationMode to true, otherwise this flag
-     * will be ignored. For how to declare the meta-data of a service refer to
-     * {@value AccessibilityService#SERVICE_META_DATA}.
-     * </p>
-     * <p>
-     * Services targeting API version equal to or lower than
-     * {@link Build.VERSION_CODES#JELLY_BEAN_MR1} will work normally, i.e.
-     * the first time they are run, if this flag is specified, a dialog is
-     * shown to the user to confirm enabling explore by touch.
-     * </p>
-     */
-    public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004;
-
-    /**
-     * This flag requests from the system to enable web accessibility enhancing
-     * extensions. Such extensions aim to provide improved accessibility support
-     * for content presented in a {@link android.webkit.WebView}. An example of such
-     * an extension is injecting JavaScript from a secure source. The system will enable
-     * enhanced web accessibility if there is at least one accessibility service
-     * that has this flag set. Hence, clearing this flag does not guarantee that the
-     * device will not have enhanced web accessibility enabled since there may be
-     * another enabled service that requested it.
-     * <p>
-     * Services that want to set this flag have to declare this capability
-     * in their meta-data by setting the attribute canRequestEnhancedWebAccessibility
-     * to true, otherwise this flag will be ignored. For how to declare the meta-data
-     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
-     * </p>
-     */
-    public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008;
-
-    /**
-     * This flag requests that the AccessibilityNodeInfos obtained
-     * by an {@link AccessibilityService} contain the id of the source view.
-     * The source view id will be a fully qualified resource name of the
-     * form "package:id/name", for example "foo.bar:id/my_list", and it is
-     * useful for UI test automation. This flag is not set by default.
-     */
-    public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
-
-    /**
-     * This flag requests from the system to filter key events. If this flag
-     * is set the accessibility service will receive the key events before
-     * applications allowing it implement global shortcuts. Setting this flag
-     * does not guarantee that this service will filter key events since only
-     * one service can do so at any given time. This avoids user confusion due
-     * to behavior change in case different key filtering services are enabled.
-     * If there is already another key filtering service enabled, this one will
-     * not receive key events.
-     * <p>
-     * Services that want to set this flag have to declare this capability
-     * in their meta-data by setting the attribute canRequestFilterKeyEvents
-     * to true, otherwise this flag will be ignored. For how to declare the meta
-     * -data of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
-     * </p>
-     */
-    public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
-
-    /*
-     * Hide constructor
-     */
-    private AccessibilityServiceInfoCompat() {}
-
-    /**
-     * The localized description of the accessibility service.
-     * <p>
-     *    <strong>Statically set from
-     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
-     * </p>
-     *
-     * @param info The service info of interest
-     * @param packageManager The current package manager
-     * @return The localized description.
-     */
-    @Nullable
-    public static String loadDescription(
-            @NonNull AccessibilityServiceInfo info, @NonNull PackageManager packageManager) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return info.loadDescription(packageManager);
-        } else {
-            //noinspection deprecation
-            return info.getDescription();
-        }
-    }
-
-    /**
-     * Returns the string representation of a feedback type. For example,
-     * {@link AccessibilityServiceInfo#FEEDBACK_SPOKEN} is represented by the
-     * string FEEDBACK_SPOKEN.
-     *
-     * @param feedbackType The feedback type.
-     * @return The string representation.
-     */
-    @NonNull
-    public static String feedbackTypeToString(int feedbackType) {
-        StringBuilder builder = new StringBuilder();
-        builder.append("[");
-        while (feedbackType > 0) {
-            final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
-            feedbackType &= ~feedbackTypeFlag;
-            if (builder.length() > 1) {
-                builder.append(", ");
-            }
-            switch (feedbackTypeFlag) {
-                case AccessibilityServiceInfo.FEEDBACK_AUDIBLE:
-                    builder.append("FEEDBACK_AUDIBLE");
-                    break;
-                case AccessibilityServiceInfo.FEEDBACK_HAPTIC:
-                    builder.append("FEEDBACK_HAPTIC");
-                    break;
-                case AccessibilityServiceInfo.FEEDBACK_GENERIC:
-                    builder.append("FEEDBACK_GENERIC");
-                    break;
-                case AccessibilityServiceInfo.FEEDBACK_SPOKEN:
-                    builder.append("FEEDBACK_SPOKEN");
-                    break;
-                case AccessibilityServiceInfo.FEEDBACK_VISUAL:
-                    builder.append("FEEDBACK_VISUAL");
-                    break;
-            }
-        }
-        builder.append("]");
-        return builder.toString();
-    }
-
-    /**
-     * Returns the string representation of a flag. For example,
-     * {@link AccessibilityServiceInfo#DEFAULT} is represented by the
-     * string DEFAULT.
-     *
-     * @param flag The flag.
-     * @return The string representation.
-     */
-    @Nullable
-    public static String flagToString(int flag) {
-        switch (flag) {
-            case AccessibilityServiceInfo.DEFAULT:
-                return "DEFAULT";
-            case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS:
-                return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS";
-            case FLAG_REQUEST_TOUCH_EXPLORATION_MODE:
-                return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE";
-            case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
-                return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
-            case FLAG_REPORT_VIEW_IDS:
-                return "FLAG_REPORT_VIEW_IDS";
-            case FLAG_REQUEST_FILTER_KEY_EVENTS:
-                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
-            default:
-                return null;
-        }
-    }
-
-    /**
-     * Returns the bit mask of capabilities this accessibility service has such as
-     * being able to retrieve the active window content, etc.
-     *
-     * @param info The service info whose capabilities to get.
-     * @return The capability bit mask.
-     *
-     * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
-     * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
-     * @see #CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
-     * @see #CAPABILITY_CAN_FILTER_KEY_EVENTS
-     */
-    public static int getCapabilities(@NonNull AccessibilityServiceInfo info) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return info.getCapabilities();
-        } else {
-            //noinspection deprecation
-            if (info.getCanRetrieveWindowContent()) {
-                return CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
-            }
-            return 0;
-        }
-    }
-
-    /**
-     * Returns the string representation of a capability. For example,
-     * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented
-     * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT.
-     *
-     * @param capability The capability.
-     * @return The string representation.
-     */
-    @NonNull
-    public static String capabilityToString(int capability) {
-        switch (capability) {
-            case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT:
-                return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT";
-            case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION:
-                return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION";
-            case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY:
-                return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY";
-            case CAPABILITY_CAN_FILTER_KEY_EVENTS:
-                return "CAPABILITY_CAN_FILTER_KEY_EVENTS";
-            default:
-                return "UNKNOWN";
-        }
-    }
-}
diff --git a/android/support/v4/app/ActionBarDrawerToggle.java b/android/support/v4/app/ActionBarDrawerToggle.java
deleted file mode 100644
index d95e42c..0000000
--- a/android/support/v4/app/ActionBarDrawerToggle.java
+++ /dev/null
@@ -1,601 +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.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.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
- * {@link DrawerLayout} and the framework <code>ActionBar</code> to implement the recommended
- * design for navigation drawers.
- *
- * <p>To use <code>ActionBarDrawerToggle</code>, create one in your Activity and call through
- * to the following methods corresponding to your Activity callbacks:</p>
- *
- * <ul>
- * <li>{@link Activity#onConfigurationChanged(android.content.res.Configuration) onConfigurationChanged}</li>
- * <li>{@link Activity#onOptionsItemSelected(android.view.MenuItem) onOptionsItemSelected}</li>
- * </ul>
- *
- * <p>Call {@link #syncState()} from your <code>Activity</code>'s
- * {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} to synchronize the indicator
- * with the state of the linked DrawerLayout after <code>onRestoreInstanceState</code>
- * has occurred.</p>
- *
- * <p><code>ActionBarDrawerToggle</code> can be used directly as a
- * {@link DrawerLayout.DrawerListener}, or if you are already providing your own listener,
- * call through to each of the listener methods from your own.</p>
- *
- */
-@Deprecated
-public class ActionBarDrawerToggle implements DrawerLayout.DrawerListener {
-
-    /**
-     * Allows an implementing Activity to return an {@link ActionBarDrawerToggle.Delegate} to use
-     * with ActionBarDrawerToggle.
-     *
-     * @deprecated Use ActionBarDrawerToggle.DelegateProvider in support-v7-appcompat.
-     */
-    @Deprecated
-    public interface DelegateProvider {
-
-        /**
-         * @return Delegate to use for ActionBarDrawableToggles, or null if the Activity
-         *         does not wish to override the default behavior.
-         */
-        @Nullable
-        Delegate getDrawerToggleDelegate();
-    }
-
-    /**
-     * @deprecated Use ActionBarDrawerToggle.DelegateProvider in support-v7-appcompat.
-     */
-    @Deprecated
-    public interface Delegate {
-        /**
-         * @return Up indicator drawable as defined in the Activity's theme, or null if one is not
-         *         defined.
-         */
-        @Nullable
-        Drawable getThemeUpIndicator();
-
-        /**
-         * Set the Action Bar's up indicator drawable and content description.
-         *
-         * @param upDrawable     - Drawable to set as up indicator
-         * @param contentDescRes - Content description to set
-         */
-        void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes);
-
-        /**
-         * Set the Action Bar's up indicator content description.
-         *
-         * @param contentDescRes - Content description to set
-         */
-        void setActionBarDescription(@StringRes int contentDescRes);
-    }
-
-    private static final String TAG = "ActionBarDrawerToggle";
-
-    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;
-
-    // android.R.id.home as defined by public API in v11
-    private static final int ID_HOME = 0x0102002c;
-
-    final Activity mActivity;
-    private final Delegate mActivityImpl;
-    private final DrawerLayout mDrawerLayout;
-    private boolean mDrawerIndicatorEnabled = true;
-    private boolean mHasCustomUpIndicator;
-
-    private Drawable mHomeAsUpIndicator;
-    private Drawable mDrawerImage;
-    private SlideDrawable mSlider;
-    private final int mDrawerImageResource;
-    private final int mOpenDrawerContentDescRes;
-    private final int mCloseDrawerContentDescRes;
-
-    private SetIndicatorInfo mSetIndicatorInfo;
-
-    /**
-     * Construct a new ActionBarDrawerToggle.
-     *
-     * <p>The given {@link Activity} will be linked to the specified {@link DrawerLayout}.
-     * The provided drawer indicator drawable will animate slightly off-screen as the drawer
-     * is opened, indicating that in the open state the drawer will move off-screen when pressed
-     * and in the closed state the drawer will move on-screen when pressed.</p>
-     *
-     * <p>String resources must be provided to describe the open/close drawer actions for
-     * accessibility services.</p>
-     *
-     * @param activity The Activity hosting the drawer
-     * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar
-     * @param drawerImageRes A Drawable resource to use as the drawer indicator
-     * @param openDrawerContentDescRes A String resource to describe the "open drawer" action
-     *                                 for accessibility
-     * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action
-     *                                  for accessibility
-     */
-    public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
-            @DrawableRes int drawerImageRes, @StringRes int openDrawerContentDescRes,
-            @StringRes int closeDrawerContentDescRes) {
-        this(activity, drawerLayout, !assumeMaterial(activity), drawerImageRes,
-                openDrawerContentDescRes, closeDrawerContentDescRes);
-    }
-
-    private static boolean assumeMaterial(Context context) {
-        return context.getApplicationInfo().targetSdkVersion >= 21
-                && (Build.VERSION.SDK_INT >= 21);
-    }
-
-    /**
-     * Construct a new ActionBarDrawerToggle.
-     *
-     * <p>The given {@link Activity} will be linked to the specified {@link DrawerLayout}.
-     * The provided drawer indicator drawable will animate slightly off-screen as the drawer
-     * is opened, indicating that in the open state the drawer will move off-screen when pressed
-     * and in the closed state the drawer will move on-screen when pressed.</p>
-     *
-     * <p>String resources must be provided to describe the open/close drawer actions for
-     * accessibility services.</p>
-     *
-     * @param activity The Activity hosting the drawer
-     * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar
-     * @param animate True to animate the drawer indicator along with the drawer's position.
-     *                Material apps should set this to false.
-     * @param drawerImageRes A Drawable resource to use as the drawer indicator
-     * @param openDrawerContentDescRes A String resource to describe the "open drawer" action
-     *                                 for accessibility
-     * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action
-     *                                  for accessibility
-     */
-    public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, boolean animate,
-            @DrawableRes int drawerImageRes, @StringRes int openDrawerContentDescRes,
-            @StringRes int closeDrawerContentDescRes) {
-        mActivity = activity;
-
-        // Allow the Activity to provide an impl
-        if (activity instanceof DelegateProvider) {
-            mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate();
-        } else {
-            mActivityImpl = null;
-        }
-
-        mDrawerLayout = drawerLayout;
-        mDrawerImageResource = drawerImageRes;
-        mOpenDrawerContentDescRes = openDrawerContentDescRes;
-        mCloseDrawerContentDescRes = closeDrawerContentDescRes;
-
-        mHomeAsUpIndicator = getThemeUpIndicator();
-        mDrawerImage = ContextCompat.getDrawable(activity, drawerImageRes);
-        mSlider = new SlideDrawable(mDrawerImage);
-        mSlider.setOffset(animate ? TOGGLE_DRAWABLE_OFFSET : 0);
-    }
-
-    /**
-     * Synchronize the state of the drawer indicator/affordance with the linked DrawerLayout.
-     *
-     * <p>This should be called from your <code>Activity</code>'s
-     * {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} method to synchronize after
-     * the DrawerLayout's instance state has been restored, and any other time when the state
-     * may have diverged in such a way that the ActionBarDrawerToggle was not notified.
-     * (For example, if you stop forwarding appropriate drawer events for a period of time.)</p>
-     */
-    public void syncState() {
-        if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
-            mSlider.setPosition(1);
-        } else {
-            mSlider.setPosition(0);
-        }
-
-        if (mDrawerIndicatorEnabled) {
-            setActionBarUpIndicator(mSlider, mDrawerLayout.isDrawerOpen(GravityCompat.START)
-                    ? mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
-        }
-    }
-
-    /**
-     * Set the up indicator to display when the drawer indicator is not
-     * enabled.
-     * <p>
-     * If you pass <code>null</code> to this method, the default drawable from
-     * the theme will be used.
-     *
-     * @param indicator A drawable to use for the up indicator, or null to use
-     *                  the theme's default
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public void setHomeAsUpIndicator(Drawable indicator) {
-        if (indicator == null) {
-            mHomeAsUpIndicator = getThemeUpIndicator();
-            mHasCustomUpIndicator = false;
-        } else {
-            mHomeAsUpIndicator = indicator;
-            mHasCustomUpIndicator = true;
-        }
-
-        if (!mDrawerIndicatorEnabled) {
-            setActionBarUpIndicator(mHomeAsUpIndicator, 0);
-        }
-    }
-
-    /**
-     * Set the up indicator to display when the drawer indicator is not
-     * enabled.
-     * <p>
-     * If you pass 0 to this method, the default drawable from the theme will
-     * be used.
-     *
-     * @param resId Resource ID of a drawable to use for the up indicator, or 0
-     *              to use the theme's default
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public void setHomeAsUpIndicator(int resId) {
-        Drawable indicator = null;
-        if (resId != 0) {
-            indicator = ContextCompat.getDrawable(mActivity, resId);
-        }
-
-        setHomeAsUpIndicator(indicator);
-    }
-
-    /**
-     * Enable or disable the drawer indicator. The indicator defaults to enabled.
-     *
-     * <p>When the indicator is disabled, the <code>ActionBar</code> will revert to displaying
-     * the home-as-up indicator provided by the <code>Activity</code>'s theme in the
-     * <code>android.R.attr.homeAsUpIndicator</code> attribute instead of the animated
-     * drawer glyph.</p>
-     *
-     * @param enable true to enable, false to disable
-     */
-    public void setDrawerIndicatorEnabled(boolean enable) {
-        if (enable != mDrawerIndicatorEnabled) {
-            if (enable) {
-                setActionBarUpIndicator(mSlider, mDrawerLayout.isDrawerOpen(GravityCompat.START)
-                        ? mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
-            } else {
-                setActionBarUpIndicator(mHomeAsUpIndicator, 0);
-            }
-            mDrawerIndicatorEnabled = enable;
-        }
-    }
-
-    /**
-     * @return true if the enhanced drawer indicator is enabled, false otherwise
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public boolean isDrawerIndicatorEnabled() {
-        return mDrawerIndicatorEnabled;
-    }
-
-    /**
-     * This method should always be called by your <code>Activity</code>'s
-     * {@link Activity#onConfigurationChanged(android.content.res.Configuration) onConfigurationChanged}
-     * method.
-     *
-     * @param newConfig The new configuration
-     */
-    public void onConfigurationChanged(Configuration newConfig) {
-        // Reload drawables that can change with configuration
-        if (!mHasCustomUpIndicator) {
-            mHomeAsUpIndicator = getThemeUpIndicator();
-        }
-        mDrawerImage = ContextCompat.getDrawable(mActivity, mDrawerImageResource);
-        syncState();
-    }
-
-    /**
-     * This method should be called by your <code>Activity</code>'s
-     * {@link Activity#onOptionsItemSelected(android.view.MenuItem) onOptionsItemSelected} method.
-     * If it returns true, your <code>onOptionsItemSelected</code> method should return true and
-     * skip further processing.
-     *
-     * @param item the MenuItem instance representing the selected menu item
-     * @return true if the event was handled and further processing should not occur
-     */
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item != null && item.getItemId() == ID_HOME && mDrawerIndicatorEnabled) {
-            if (mDrawerLayout.isDrawerVisible(GravityCompat.START)) {
-                mDrawerLayout.closeDrawer(GravityCompat.START);
-            } else {
-                mDrawerLayout.openDrawer(GravityCompat.START);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param drawerView The child view that was moved
-     * @param slideOffset The new offset of this drawer within its range, from 0-1
-     */
-    @Override
-    public void onDrawerSlide(View drawerView, float slideOffset) {
-        float glyphOffset = mSlider.getPosition();
-        if (slideOffset > 0.5f) {
-            glyphOffset = Math.max(glyphOffset, Math.max(0.f, slideOffset - 0.5f) * 2);
-        } else {
-            glyphOffset = Math.min(glyphOffset, slideOffset * 2);
-        }
-        mSlider.setPosition(glyphOffset);
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param drawerView Drawer view that is now open
-     */
-    @Override
-    public void onDrawerOpened(View drawerView) {
-        mSlider.setPosition(1);
-        if (mDrawerIndicatorEnabled) {
-            setActionBarDescription(mCloseDrawerContentDescRes);
-        }
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param drawerView Drawer view that is now closed
-     */
-    @Override
-    public void onDrawerClosed(View drawerView) {
-        mSlider.setPosition(0);
-        if (mDrawerIndicatorEnabled) {
-            setActionBarDescription(mOpenDrawerContentDescRes);
-        }
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param newState The new drawer motion state
-     */
-    @Override
-    public void onDrawerStateChanged(int newState) {
-    }
-
-    private Drawable getThemeUpIndicator() {
-        if (mActivityImpl != null) {
-            return mActivityImpl.getThemeUpIndicator();
-        }
-        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;
-        }
-    }
-
-    private void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
-        if (mActivityImpl != null) {
-            mActivityImpl.setActionBarUpIndicator(upDrawable, contentDescRes);
-            return;
-        }
-        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");
-            }
-        }
-    }
-
-    private void setActionBarDescription(int contentDescRes) {
-        if (mActivityImpl != null) {
-            mActivityImpl.setActionBarDescription(contentDescRes);
-            return;
-        }
-        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 {
-        private final boolean mHasMirroring = Build.VERSION.SDK_INT > 18;
-        private final Rect mTmpRect = new Rect();
-
-        private float mPosition;
-        private float mOffset;
-
-        SlideDrawable(Drawable wrapped) {
-            super(wrapped, 0);
-        }
-
-        /**
-         * Sets the current position along the offset.
-         *
-         * @param position a value between 0 and 1
-         */
-        public void setPosition(float position) {
-            mPosition = position;
-            invalidateSelf();
-        }
-
-        public float getPosition() {
-            return mPosition;
-        }
-
-        /**
-         * Specifies the maximum offset when the position is at 1.
-         *
-         * @param offset maximum offset as a fraction of the drawable width,
-         *            positive to shift left or negative to shift right.
-         * @see #setPosition(float)
-         */
-        public void setOffset(float offset) {
-            mOffset = offset;
-            invalidateSelf();
-        }
-
-        @Override
-        public void draw(@NonNull Canvas canvas) {
-            copyBounds(mTmpRect);
-            canvas.save();
-
-            // Layout direction must be obtained from the activity.
-            final boolean isLayoutRTL = ViewCompat.getLayoutDirection(
-                    mActivity.getWindow().getDecorView()) == ViewCompat.LAYOUT_DIRECTION_RTL;
-            final int flipRtl = isLayoutRTL ? -1 : 1;
-            final int width = mTmpRect.width();
-            canvas.translate(-mOffset * width * mPosition * flipRtl, 0);
-
-            // Force auto-mirroring if it's not supported by the platform.
-            if (isLayoutRTL && !mHasMirroring) {
-                canvas.translate(width, 0);
-                canvas.scale(-1, 1);
-            }
-
-            super.draw(canvas);
-            canvas.restore();
-        }
-    }
-}
diff --git a/android/support/v4/app/ActivityCompat.java b/android/support/v4/app/ActivityCompat.java
deleted file mode 100644
index 333871a..0000000
--- a/android/support/v4/app/ActivityCompat.java
+++ /dev/null
@@ -1,637 +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.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.PackageManager;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.support.annotation.IdRes;
-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.v13.view.DragAndDropPermissionsCompat;
-import android.support.v4.content.ContextCompat;
-import android.view.DragEvent;
-import android.view.View;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Helper for accessing features in {@link android.app.Activity}.
- */
-public class ActivityCompat extends ContextCompat {
-
-    /**
-     * This interface is the contract for receiving the results for permission requests.
-     */
-    public interface OnRequestPermissionsResultCallback {
-
-        /**
-         * Callback for the result from requesting permissions. This method
-         * is invoked for every call on {@link #requestPermissions(android.app.Activity,
-         * String[], int)}.
-         * <p>
-         * <strong>Note:</strong> It is possible that the permissions request interaction
-         * with the user is interrupted. In this case you will receive empty permissions
-         * and results arrays which should be treated as a cancellation.
-         * </p>
-         *
-         * @param requestCode The request code passed in {@link #requestPermissions(
-         * android.app.Activity, String[], int)}
-         * @param permissions The requested permissions. Never null.
-         * @param grantResults The grant results for the corresponding permissions
-         *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
-         *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
-         *
-         * @see #requestPermissions(android.app.Activity, String[], int)
-         */
-        void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
-                @NonNull int[] grantResults);
-    }
-
-    /**
-     * Customizable delegate that allows delegating permission compatibility methods to a custom
-     * implementation.
-     *
-     * <p>
-     *     To delegate permission compatibility methods to a custom class, implement this interface,
-     *     and call {@code ActivityCompat.setPermissionCompatDelegate(delegate);}. All future calls
-     *     to the permission compatibility methods in this class will first check whether the
-     *     delegate can handle the method call, and invoke the corresponding method if it can.
-     * </p>
-     */
-    public interface PermissionCompatDelegate {
-
-        /**
-         * Determines whether the delegate should handle
-         * {@link ActivityCompat#requestPermissions(Activity, String[], int)}, and request
-         * permissions if applicable. If this method returns true, it means that permission
-         * request is successfully handled by the delegate, and platform should not perform any
-         * further requests for permission.
-         *
-         * @param activity The target activity.
-         * @param permissions The requested permissions. Must me non-null and not empty.
-         * @param requestCode Application specific request code to match with a result reported to
-         *    {@link
-         *    OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
-         *    Should be >= 0.
-         *
-         * @return Whether the delegate has handled the permission request.
-         * @see ActivityCompat#requestPermissions(Activity, String[], int)
-         */
-        boolean requestPermissions(@NonNull Activity activity,
-                @NonNull String[] permissions, @IntRange(from = 0) int requestCode);
-
-        /**
-         * Determines whether the delegate should handle the permission request as part of
-         * {@code FragmentActivity#onActivityResult(int, int, Intent)}. If this method returns true,
-         * it means that activity result is successfully handled by the delegate, and no further
-         * action is needed on this activity result.
-         *
-         * @param activity    The target Activity.
-         * @param requestCode The integer request code originally supplied to
-         *                    {@code startActivityForResult()}, allowing you to identify who this
-         *                    result came from.
-         * @param resultCode  The integer result code returned by the child activity
-         *                    through its {@code }setResult()}.
-         * @param data        An Intent, which can return result data to the caller
-         *                    (various data can be attached to Intent "extras").
-         *
-         * @return Whether the delegate has handled the activity result.
-         * @see ActivityCompat#requestPermissions(Activity, String[], int)
-         */
-        boolean onActivityResult(@NonNull Activity activity,
-                @IntRange(from = 0) int requestCode, int resultCode, @Nullable Intent data);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public interface RequestPermissionsRequestCodeValidator {
-        void validateRequestPermissionsRequestCode(int requestCode);
-    }
-
-    private static PermissionCompatDelegate sDelegate;
-
-    /**
-     * This class should not be instantiated, but the constructor must be
-     * visible for the class to be extended (as in support-v13).
-     */
-    protected ActivityCompat() {
-        // Not publicly instantiable, but may be extended.
-    }
-
-    /**
-     * Sets the permission delegate for {@code ActivityCompat}. Replaces the previously set
-     * delegate.
-     *
-     * @param delegate The delegate to be set. {@code null} to clear the set delegate.
-     */
-    public static void setPermissionCompatDelegate(
-            @Nullable PermissionCompatDelegate delegate) {
-        sDelegate = delegate;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static PermissionCompatDelegate getPermissionCompatDelegate() {
-        return sDelegate;
-    }
-
-    /**
-     * Invalidate the activity's options menu, if able.
-     *
-     * <p>Before API level 11 (Android 3.0/Honeycomb) the lifecycle of the
-     * options menu was controlled primarily by the user's operation of
-     * the hardware menu key. When the user presses down on the menu key
-     * for the first time the menu was created and prepared by calls
-     * to {@link Activity#onCreateOptionsMenu(android.view.Menu)} and
-     * {@link Activity#onPrepareOptionsMenu(android.view.Menu)} respectively.
-     * Subsequent presses of the menu key kept the existing instance of the
-     * Menu itself and called {@link Activity#onPrepareOptionsMenu(android.view.Menu)}
-     * to give the activity an opportunity to contextually alter the menu
-     * before the menu panel was shown.</p>
-     *
-     * <p>In Android 3.0+ the Action Bar forces the options menu to be built early
-     * so that items chosen to show as actions may be displayed when the activity
-     * first becomes visible. The Activity method invalidateOptionsMenu forces
-     * the entire menu to be destroyed and recreated from
-     * {@link Activity#onCreateOptionsMenu(android.view.Menu)}, offering a similar
-     * though heavier-weight opportunity to change the menu's contents. Normally
-     * this functionality is used to support a changing configuration of Fragments.</p>
-     *
-     * <p>Applications may use this support helper to signal a significant change in
-     * activity state that should cause the options menu to be rebuilt. If the app
-     * is running on an older platform version that does not support menu invalidation
-     * the app will still receive {@link Activity#onPrepareOptionsMenu(android.view.Menu)}
-     * the next time the user presses the menu key and this method will return false.
-     * If this method returns true the options menu was successfully invalidated.</p>
-     *
-     * @param activity Invalidate the options menu of this activity
-     * @return true if this operation was supported and it completed; false if it was not available.
-     * @deprecated Use {@link Activity#invalidateOptionsMenu()} directly.
-     */
-    @Deprecated
-    public static boolean invalidateOptionsMenu(Activity activity) {
-        activity.invalidateOptionsMenu();
-        return true;
-    }
-
-    /**
-     * Start new activity with options, if able, for which you would like a
-     * result when it finished.
-     *
-     * <p>In Android 4.1+ additional options were introduced to allow for more
-     * control on activity launch animations. Applications can use this method
-     * along with {@link ActivityOptionsCompat} to use these animations when
-     * available. When run on versions of the platform where this feature does
-     * not exist the activity will be launched normally.</p>
-     *
-     * @param activity Origin activity to launch from.
-     * @param intent The description of the activity to start.
-     * @param requestCode If >= 0, this code will be returned in
-     *                   onActivityResult() when the activity exits.
-     * @param options Additional options for how the Activity should be started.
-     *                May be null if there are no options. See
-     *                {@link ActivityOptionsCompat} for how to build the Bundle
-     *                supplied here; there are no supported definitions for
-     *                building it manually.
-     */
-    public static void startActivityForResult(@NonNull Activity activity, @NonNull Intent intent,
-            int requestCode, @Nullable Bundle options) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            activity.startActivityForResult(intent, requestCode, options);
-        } else {
-            activity.startActivityForResult(intent, requestCode);
-        }
-    }
-
-    /**
-     * Start new IntentSender with options, if able, for which you would like a
-     * result when it finished.
-     *
-     * <p>In Android 4.1+ additional options were introduced to allow for more
-     * control on activity launch animations. Applications can use this method
-     * along with {@link ActivityOptionsCompat} to use these animations when
-     * available. When run on versions of the platform where this feature does
-     * not exist the activity will be launched normally.</p>
-     *
-     * @param activity Origin activity to launch from.
-     * @param intent The IntentSender to launch.
-     * @param requestCode If >= 0, this code will be returned in
-     *                   onActivityResult() when the activity exits.
-     * @param fillInIntent If non-null, this will be provided as the
-     *                     intent parameter to {@link IntentSender#sendIntent}.
-     * @param flagsMask Intent flags in the original IntentSender that you
-     *                  would like to change.
-     * @param flagsValues Desired values for any bits set in <var>flagsMask</var>
-     * @param extraFlags Always set to 0.
-     * @param options Additional options for how the Activity should be started.
-     *                May be null if there are no options. See
-     *                {@link ActivityOptionsCompat} for how to build the Bundle
-     *                supplied here; there are no supported definitions for
-     *                building it manually.
-     */
-    public static void startIntentSenderForResult(@NonNull Activity activity,
-            @NonNull IntentSender intent, int requestCode, @Nullable Intent fillInIntent,
-            int flagsMask, int flagsValues, int extraFlags, @Nullable Bundle options)
-            throws IntentSender.SendIntentException {
-        if (Build.VERSION.SDK_INT >= 16) {
-            activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
-                    flagsValues, extraFlags, options);
-        } else {
-            activity.startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask,
-                    flagsValues, extraFlags);
-        }
-    }
-
-    /**
-     * Finish this activity, and tries to finish all activities immediately below it
-     * in the current task that have the same affinity.
-     *
-     * <p>On Android 4.1+ calling this method will call through to the native version of this
-     * method. For other platforms {@link Activity#finish()} will be called instead.</p>
-     */
-    public static void finishAffinity(@NonNull Activity activity) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            activity.finishAffinity();
-        } else {
-            activity.finish();
-        }
-    }
-
-    /**
-     * Reverses the Activity Scene entry Transition and triggers the calling Activity
-     * to reverse its exit Transition. When the exit Transition completes,
-     * {@link Activity#finish()} is called. If no entry Transition was used, finish() is called
-     * immediately and the Activity exit Transition is run.
-     *
-     * <p>On Android 4.4 or lower, this method only finishes the Activity with no
-     * special exit transition.</p>
-     */
-    public static void finishAfterTransition(@NonNull Activity activity) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            activity.finishAfterTransition();
-        } else {
-            activity.finish();
-        }
-    }
-
-    /**
-     * Return information about who launched this activity.  If the launching Intent
-     * contains an {@link Intent#EXTRA_REFERRER Intent.EXTRA_REFERRER},
-     * that will be returned as-is; otherwise, if known, an
-     * {@link Intent#URI_ANDROID_APP_SCHEME android-app:} referrer URI containing the
-     * package name that started the Intent will be returned.  This may return null if no
-     * referrer can be identified -- it is neither explicitly specified, nor is it known which
-     * application package was involved.
-     *
-     * <p>If called while inside the handling of {@link Activity#onNewIntent}, this function will
-     * return the referrer that submitted that new intent to the activity.  Otherwise, it
-     * always returns the referrer of the original Intent.</p>
-     *
-     * <p>Note that this is <em>not</em> a security feature -- you can not trust the
-     * referrer information, applications can spoof it.</p>
-     */
-    @Nullable
-    public static Uri getReferrer(@NonNull Activity activity) {
-        if (Build.VERSION.SDK_INT >= 22) {
-            return activity.getReferrer();
-        }
-        Intent intent = activity.getIntent();
-        Uri referrer = intent.getParcelableExtra("android.intent.extra.REFERRER");
-        if (referrer != null) {
-            return referrer;
-        }
-        String referrerName = intent.getStringExtra("android.intent.extra.REFERRER_NAME");
-        if (referrerName != null) {
-            return Uri.parse(referrerName);
-        }
-        return null;
-    }
-
-    /**
-     * Finds a view that was identified by the {@code android:id} XML attribute that was processed
-     * in {@link Activity#onCreate}, or throws an IllegalArgumentException if the ID is invalid, or
-     * there is no matching view in the hierarchy.
-     * <p>
-     * <strong>Note:</strong> In most cases -- depending on compiler support --
-     * the resulting view is automatically cast to the target class type. If
-     * the target class type is unconstrained, an explicit cast may be
-     * necessary.
-     *
-     * @param id the ID to search for
-     * @return a view with given ID
-     * @see Activity#findViewById(int)
-     * @see android.support.v4.view.ViewCompat#requireViewById(View, int)
-     */
-    @NonNull
-    public static <T extends View> T requireViewById(@NonNull Activity activity, @IdRes int id) {
-        // TODO: use and link to Activity#requireViewById() directly, once available
-        T view = activity.findViewById(id);
-        if (view == null) {
-            throw new IllegalArgumentException("ID does not reference a View inside this Activity");
-        }
-        return view;
-    }
-
-    /**
-     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
-     * android.view.View, String)} was used to start an Activity, <var>callback</var>
-     * will be called to handle shared elements on the <i>launched</i> Activity. This requires
-     * {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS}.
-     *
-     * @param callback Used to manipulate shared element transitions on the launched Activity.
-     */
-    public static void setEnterSharedElementCallback(@NonNull Activity activity,
-            @Nullable SharedElementCallback callback) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            android.app.SharedElementCallback frameworkCallback = callback != null
-                    ? new SharedElementCallback23Impl(callback)
-                    : null;
-            activity.setEnterSharedElementCallback(frameworkCallback);
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            android.app.SharedElementCallback frameworkCallback = callback != null
-                    ? new SharedElementCallback21Impl(callback)
-                    : null;
-            activity.setEnterSharedElementCallback(frameworkCallback);
-        }
-    }
-
-    /**
-     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
-     * android.view.View, String)} was used to start an Activity, <var>callback</var>
-     * will be called to handle shared elements on the <i>launching</i> Activity. Most
-     * calls will only come when returning from the started Activity.
-     * This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS}.
-     *
-     * @param callback Used to manipulate shared element transitions on the launching Activity.
-     */
-    public static void setExitSharedElementCallback(@NonNull Activity activity,
-            @Nullable SharedElementCallback callback) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            android.app.SharedElementCallback frameworkCallback = callback != null
-                    ? new SharedElementCallback23Impl(callback)
-                    : null;
-            activity.setExitSharedElementCallback(frameworkCallback);
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            android.app.SharedElementCallback frameworkCallback = callback != null
-                    ? new SharedElementCallback21Impl(callback)
-                    : null;
-            activity.setExitSharedElementCallback(frameworkCallback);
-        }
-    }
-
-    public static void postponeEnterTransition(@NonNull Activity activity) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            activity.postponeEnterTransition();
-        }
-    }
-
-    public static void startPostponedEnterTransition(@NonNull Activity activity) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            activity.startPostponedEnterTransition();
-        }
-    }
-
-    /**
-     * Requests permissions to be granted to this application. These permissions
-     * must be requested in your manifest, they should not be granted to your app,
-     * and they should have protection level {@link android.content.pm.PermissionInfo
-     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
-     * the platform or a third-party app.
-     * <p>
-     * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
-     * are granted at install time if requested in the manifest. Signature permissions
-     * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
-     * install time if requested in the manifest and the signature of your app matches
-     * the signature of the app declaring the permissions.
-     * </p>
-     * <p>
-     * If your app does not have the requested permissions the user will be presented
-     * with UI for accepting them. After the user has accepted or rejected the
-     * requested permissions you will receive a callback reporting whether the
-     * permissions were granted or not. Your activity has to implement {@link
-     * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
-     * and the results of permission requests will be delivered to its {@link
-     * android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(
-     * int, String[], int[])} method.
-     * </p>
-     * <p>
-     * Note that requesting a permission does not guarantee it will be granted and
-     * your app should be able to run without having this permission.
-     * </p>
-     * <p>
-     * This method may start an activity allowing the user to choose which permissions
-     * to grant and which to reject. Hence, you should be prepared that your activity
-     * may be paused and resumed. Further, granting some permissions may require
-     * a restart of you application. In such a case, the system will recreate the
-     * activity stack before delivering the result to your
-     * {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
-     * </p>
-     * <p>
-     * When checking whether you have a permission you should use {@link
-     * #checkSelfPermission(android.content.Context, String)}.
-     * </p>
-     * <p>
-     * Calling this API for permissions already granted to your app would show UI
-     * to the user to decided whether the app can still hold these permissions. This
-     * can be useful if the way your app uses the data guarded by the permissions
-     * changes significantly.
-     * </p>
-     * <p>
-     * You cannot request a permission if your activity sets {@link
-     * android.R.attr#noHistory noHistory} to <code>true</code> in the manifest
-     * because in this case the activity would not receive result callbacks including
-     * {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
-     * </p>
-     * <p>
-     * The <a href="http://developer.android.com/samples/RuntimePermissions/index.html">
-     * RuntimePermissions</a> sample app demonstrates how to use this method to
-     * request permissions at run time.
-     * </p>
-     *
-     * @param activity The target activity.
-     * @param permissions The requested permissions. Must me non-null and not empty.
-     * @param requestCode Application specific request code to match with a result
-     *    reported to {@link OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}.
-     *    Should be >= 0.
-     *
-     * @see OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])
-     * @see #checkSelfPermission(android.content.Context, String)
-     * @see #shouldShowRequestPermissionRationale(android.app.Activity, String)
-     */
-    public static void requestPermissions(final @NonNull Activity activity,
-            final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
-        if (sDelegate != null
-                && sDelegate.requestPermissions(activity, permissions, requestCode)) {
-            // Delegate has handled the permission request.
-            return;
-        }
-
-        if (Build.VERSION.SDK_INT >= 23) {
-            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() {
-                @Override
-                public void run() {
-                    final int[] grantResults = new int[permissions.length];
-
-                    PackageManager packageManager = activity.getPackageManager();
-                    String packageName = activity.getPackageName();
-
-                    final int permissionCount = permissions.length;
-                    for (int i = 0; i < permissionCount; i++) {
-                        grantResults[i] = packageManager.checkPermission(
-                                permissions[i], packageName);
-                    }
-
-                    ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
-                            requestCode, permissions, grantResults);
-                }
-            });
-        }
-    }
-
-    /**
-     * Gets whether you should show UI with rationale for requesting a permission.
-     * You should do this only if you do not have the permission and the context in
-     * which the permission is requested does not clearly communicate to the user
-     * what would be the benefit from granting this permission.
-     * <p>
-     * For example, if you write a camera app, requesting the camera permission
-     * would be expected by the user and no rationale for why it is requested is
-     * needed. If however, the app needs location for tagging photos then a non-tech
-     * savvy user may wonder how location is related to taking photos. In this case
-     * you may choose to show UI with rationale of requesting this permission.
-     * </p>
-     *
-     * @param activity The target activity.
-     * @param permission A permission your app wants to request.
-     * @return Whether you can show permission rationale UI.
-     *
-     * @see #checkSelfPermission(android.content.Context, String)
-     * @see #requestPermissions(android.app.Activity, String[], int)
-     */
-    public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
-            @NonNull String permission) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return activity.shouldShowRequestPermissionRationale(permission);
-        }
-        return false;
-    }
-
-    /**
-     * Create {@link DragAndDropPermissionsCompat} object bound to this activity and controlling
-     * the access permissions for content URIs associated with the {@link android.view.DragEvent}.
-     * @param dragEvent Drag event to request permission for
-     * @return The {@link DragAndDropPermissionsCompat} object used to control access to the content
-     * URIs. {@code null} if no content URIs are associated with the event or if permissions could
-     * not be granted.
-     */
-    @Nullable
-    public static DragAndDropPermissionsCompat requestDragAndDropPermissions(Activity activity,
-            DragEvent dragEvent) {
-        return DragAndDropPermissionsCompat.request(activity, dragEvent);
-    }
-
-    @RequiresApi(21)
-    private static class SharedElementCallback21Impl extends android.app.SharedElementCallback {
-
-        protected SharedElementCallback mCallback;
-
-        public SharedElementCallback21Impl(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);
-        }
-    }
-
-    @RequiresApi(23)
-    private static class SharedElementCallback23Impl extends SharedElementCallback21Impl {
-        public SharedElementCallback23Impl(SharedElementCallback callback) {
-            super(callback);
-        }
-
-        @Override
-        public void onSharedElementsArrived(List<String> sharedElementNames,
-                List<View> sharedElements, final OnSharedElementsReadyListener listener) {
-            mCallback.onSharedElementsArrived(sharedElementNames, sharedElements,
-                    new SharedElementCallback.OnSharedElementsReadyListener() {
-                        @Override
-                        public void onSharedElementsReady() {
-                            listener.onSharedElementsReady();
-                        }
-                    });
-        }
-    }
-}
diff --git a/android/support/v4/app/ActivityManagerCompat.java b/android/support/v4/app/ActivityManagerCompat.java
deleted file mode 100644
index e8aaab6..0000000
--- a/android/support/v4/app/ActivityManagerCompat.java
+++ /dev/null
@@ -1,44 +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.os.Build;
-import android.support.annotation.NonNull;
-
-/**
- * Helper for accessing features in {@link android.app.ActivityManager} in a backwards compatible
- * fashion.
- */
-public final class ActivityManagerCompat {
-
-    private ActivityManagerCompat() {}
-
-    /**
-     * Returns true if this is a low-RAM device.  Exactly whether a device is low-RAM
-     * is ultimately up to the device configuration, but currently it generally means
-     * something in the class of a 512MB device with about a 800x480 or less screen.
-     * 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 activityManager) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return activityManager.isLowRamDevice();
-        }
-        return false;
-    }
-}
diff --git a/android/support/v4/app/ActivityOptionsCompat.java b/android/support/v4/app/ActivityOptionsCompat.java
deleted file mode 100644
index 6676805..0000000
--- a/android/support/v4/app/ActivityOptionsCompat.java
+++ /dev/null
@@ -1,389 +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.Activity;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.graphics.Bitmap;
-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.v4.util.Pair;
-import android.view.View;
-
-/**
- * Helper for accessing features in {@link android.app.ActivityOptions} in a backwards compatible
- * fashion.
- */
-public class ActivityOptionsCompat {
-    /**
-     * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
-     * the total time (in ms) the user spent in the app flow.
-     */
-    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
-
-    /**
-     * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
-     * detailed information about the time spent in each package associated with the app;
-     * each key is a package name, whose value is a long containing the time (in ms).
-     */
-    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
-
-    /**
-     * Create an ActivityOptions specifying a custom animation to run when the
-     * activity is displayed.
-     *
-     * @param context Who is defining this. This is the application that the
-     * animation resources will be loaded from.
-     * @param enterResId A resource ID of the animation resource to use for the
-     * incoming activity. Use 0 for no animation.
-     * @param exitResId A resource ID of the animation resource to use for the
-     * outgoing activity. Use 0 for no animation.
-     * @return Returns a new ActivityOptions object that you can use to supply
-     * these options as the options Bundle when starting an activity.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeCustomAnimation(@NonNull Context context,
-            int enterResId, int exitResId) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return createImpl(ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * Create an ActivityOptions specifying an animation where the new activity is
-     * scaled from a small originating area of the screen to its final full
-     * representation.
-     * <p/>
-     * If the Intent this is being used with has not set its
-     * {@link android.content.Intent#setSourceBounds(android.graphics.Rect)},
-     * those bounds will be filled in for you based on the initial bounds passed
-     * in here.
-     *
-     * @param source The View that the new activity is animating from. This
-     * defines the coordinate space for startX and startY.
-     * @param startX The x starting location of the new activity, relative to
-     * source.
-     * @param startY The y starting location of the activity, relative to source.
-     * @param startWidth The initial width of the new activity.
-     * @param startHeight The initial height of the new activity.
-     * @return Returns a new ActivityOptions object that you can use to supply
-     * these options as the options Bundle when starting an activity.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeScaleUpAnimation(@NonNull View source,
-            int startX, int startY, int startWidth, int startHeight) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return createImpl(ActivityOptions.makeScaleUpAnimation(
-                    source, startX, startY, startWidth, startHeight));
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * Create an ActivityOptions specifying an animation where the new
-     * activity is revealed from a small originating area of the screen to
-     * its final full representation.
-     *
-     * @param source The View that the new activity is animating from.  This
-     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
-     * @param startX The x starting location of the new activity, relative to <var>source</var>.
-     * @param startY The y starting location of the activity, relative to <var>source</var>.
-     * @param width The initial width of the new activity.
-     * @param height The initial height of the new activity.
-     * @return Returns a new ActivityOptions object that you can use to
-     * supply these options as the options Bundle when starting an activity.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeClipRevealAnimation(@NonNull View source,
-            int startX, int startY, int width, int height) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return createImpl(ActivityOptions.makeClipRevealAnimation(
-                    source, startX, startY, width, height));
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * Create an ActivityOptions specifying an animation where a thumbnail is
-     * scaled from a given position to the new activity window that is being
-     * started.
-     * <p/>
-     * If the Intent this is being used with has not set its
-     * {@link android.content.Intent#setSourceBounds(android.graphics.Rect)},
-     * those bounds will be filled in for you based on the initial thumbnail
-     * location and size provided here.
-     *
-     * @param source The View that this thumbnail is animating from. This
-     * defines the coordinate space for startX and startY.
-     * @param thumbnail The bitmap that will be shown as the initial thumbnail
-     * of the animation.
-     * @param startX The x starting location of the bitmap, relative to source.
-     * @param startY The y starting location of the bitmap, relative to source.
-     * @return Returns a new ActivityOptions object that you can use to supply
-     * these options as the options Bundle when starting an activity.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeThumbnailScaleUpAnimation(@NonNull View source,
-            @NonNull Bitmap thumbnail, int startX, int startY) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return createImpl(ActivityOptions.makeThumbnailScaleUpAnimation(
-                    source, thumbnail, startX, startY));
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * Create an ActivityOptions to transition between Activities using cross-Activity scene
-     * animations. This method carries the position of one shared element to the started Activity.
-     * The position of <code>sharedElement</code> will be used as the epicenter for the
-     * exit Transition. The position of the shared element in the launched Activity will be the
-     * epicenter of its entering Transition.
-     *
-     * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
-     * enabled on the calling Activity to cause an exit transition. The same must be in
-     * the called Activity to get an entering transition.</p>
-     * @param activity The Activity whose window contains the shared elements.
-     * @param sharedElement The View to transition to the started Activity. sharedElement must
-     *                      have a non-null sharedElementName.
-     * @param sharedElementName The shared element name as used in the target Activity. This may
-     *                          be null if it has the same name as sharedElement.
-     * @return Returns a new ActivityOptions object that you can use to
-     *         supply these options as the options Bundle when starting an activity.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeSceneTransitionAnimation(@NonNull Activity activity,
-            @NonNull View sharedElement, @NonNull String sharedElementName) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return createImpl(ActivityOptions.makeSceneTransitionAnimation(
-                    activity, sharedElement, sharedElementName));
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * Create an ActivityOptions to transition between Activities using cross-Activity scene
-     * animations. This method carries the position of multiple shared elements to the started
-     * Activity. The position of the first element in sharedElements
-     * will be used as the epicenter for the exit Transition. The position of the associated
-     * shared element in the launched Activity will be the epicenter of its entering Transition.
-     *
-     * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
-     * enabled on the calling Activity to cause an exit transition. The same must be in
-     * the called Activity to get an entering transition.</p>
-     * @param activity The Activity whose window contains the shared elements.
-     * @param sharedElements The names of the shared elements to transfer to the called
-     *                       Activity and their associated Views. The Views must each have
-     *                       a unique shared element name.
-     * @return Returns a new ActivityOptions object that you can use to
-     *         supply these options as the options Bundle when starting an activity.
-     */
-    @NonNull
-    @SuppressWarnings("unchecked")
-    public static ActivityOptionsCompat makeSceneTransitionAnimation(@NonNull Activity activity,
-            Pair<View, String>... sharedElements) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            android.util.Pair<View, String>[] pairs = null;
-            if (sharedElements != null) {
-                pairs = new android.util.Pair[sharedElements.length];
-                for (int i = 0; i < sharedElements.length; i++) {
-                    pairs[i] = android.util.Pair.create(
-                            sharedElements[i].first, sharedElements[i].second);
-                }
-            }
-            return createImpl(ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
-     * presented to the user but will instead be only available through the recents task list.
-     * In addition, the new task wil be affiliated with the launching activity's task.
-     * Affiliated tasks are grouped together in the recents task list.
-     *
-     * <p>This behavior is not supported for activities with
-     * {@link android.R.attr#launchMode launchMode} values of
-     * <code>singleInstance</code> or <code>singleTask</code>.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeTaskLaunchBehind() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return createImpl(ActivityOptions.makeTaskLaunchBehind());
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    /**
-     * Create a basic ActivityOptions that has no special animation associated with it.
-     * Other options can still be set.
-     */
-    @NonNull
-    public static ActivityOptionsCompat makeBasic() {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return createImpl(ActivityOptions.makeBasic());
-        }
-        return new ActivityOptionsCompat();
-    }
-
-    @RequiresApi(16)
-    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(16)
-    private static class ActivityOptionsCompatApi16Impl extends ActivityOptionsCompat {
-        protected final ActivityOptions mActivityOptions;
-
-        ActivityOptionsCompatApi16Impl(ActivityOptions activityOptions) {
-            mActivityOptions = activityOptions;
-        }
-
-        @Override
-        public Bundle toBundle() {
-            return mActivityOptions.toBundle();
-        }
-
-        @Override
-        public void update(ActivityOptionsCompat otherOptions) {
-            if (otherOptions instanceof ActivityOptionsCompatApi16Impl) {
-                ActivityOptionsCompatApi16Impl otherImpl =
-                        (ActivityOptionsCompatApi16Impl) otherOptions;
-                mActivityOptions.update(otherImpl.mActivityOptions);
-            }
-        }
-    }
-
-    @RequiresApi(23)
-    private static class ActivityOptionsCompatApi23Impl extends ActivityOptionsCompatApi16Impl {
-        ActivityOptionsCompatApi23Impl(ActivityOptions activityOptions) {
-            super(activityOptions);
-        }
-
-        @Override
-        public void requestUsageTimeReport(PendingIntent receiver) {
-            mActivityOptions.requestUsageTimeReport(receiver);
-        }
-    }
-
-    @RequiresApi(24)
-    private static class ActivityOptionsCompatApi24Impl extends ActivityOptionsCompatApi23Impl {
-        ActivityOptionsCompatApi24Impl(ActivityOptions activityOptions) {
-            super(activityOptions);
-        }
-
-        @Override
-        public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
-            return new ActivityOptionsCompatApi24Impl(
-                    mActivityOptions.setLaunchBounds(screenSpacePixelRect));
-        }
-
-        @Override
-        public Rect getLaunchBounds() {
-            return mActivityOptions.getLaunchBounds();
-        }
-    }
-
-    protected ActivityOptionsCompat() {
-    }
-
-    /**
-     * Sets the bounds (window size) that the activity should be launched in.
-     * Rect position should be provided in pixels and in screen coordinates.
-     * Set to null explicitly for fullscreen.
-     * <p>
-     * <strong>NOTE:<strong/> This value is ignored on devices that don't have
-     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
-     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
-     * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen.
-     */
-    @NonNull
-    public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
-        return this;
-    }
-
-    /**
-     * Returns the bounds that should be used to launch the activity.
-     * @see #setLaunchBounds(Rect)
-     * @return Bounds used to launch the activity.
-     */
-    @Nullable
-    public Rect getLaunchBounds() {
-        return null;
-    }
-
-    /**
-     * Returns the created options as a Bundle, which can be passed to
-     * {@link android.support.v4.content.ContextCompat#startActivity(Context, android.content.Intent, Bundle)}.
-     * Note that the returned Bundle is still owned by the ActivityOptions
-     * object; you must not modify it, but can supply it to the startActivity
-     * methods that take an options Bundle.
-     */
-    @Nullable
-    public Bundle toBundle() {
-        return null;
-    }
-
-    /**
-     * Update the current values in this ActivityOptions from those supplied in
-     * otherOptions. Any values defined in otherOptions replace those in the
-     * base options.
-     */
-    public void update(@NonNull ActivityOptionsCompat otherOptions) {
-        // Do nothing.
-    }
-
-    /**
-     * Ask the the system track that time the user spends in the app being launched, and
-     * report it back once done.  The report will be sent to the given receiver, with
-     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
-     * filled in.
-     *
-     * <p>The time interval tracked is from launching this activity until the user leaves
-     * that activity's flow.  They are considered to stay in the flow as long as
-     * new activities are being launched or returned to from the original flow,
-     * even if this crosses package or task boundaries.  For example, if the originator
-     * starts an activity to view an image, and while there the user selects to share,
-     * which launches their email app in a new task, and they complete the share, the
-     * time during that entire operation will be included until they finally hit back from
-     * the original image viewer activity.</p>
-     *
-     * <p>The user is considered to complete a flow once they switch to another
-     * activity that is not part of the tracked flow.  This may happen, for example, by
-     * using the notification shade, launcher, or recents to launch or switch to another
-     * app.  Simply going in to these navigation elements does not break the flow (although
-     * 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 will receive the report.
-     */
-    public void requestUsageTimeReport(@NonNull PendingIntent receiver) {
-        // Do nothing.
-    }
-}
diff --git a/android/support/v4/app/AlarmManagerCompat.java b/android/support/v4/app/AlarmManagerCompat.java
deleted file mode 100644
index a297cb5..0000000
--- a/android/support/v4/app/AlarmManagerCompat.java
+++ /dev/null
@@ -1,230 +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.AlarmManager;
-import android.app.PendingIntent;
-import android.os.Build;
-import android.support.annotation.NonNull;
-
-/**
- * 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(@NonNull AlarmManager alarmManager, long triggerTime,
-            @NonNull PendingIntent showIntent, @NonNull 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(@NonNull AlarmManager alarmManager, int type,
-            long triggerAtMillis, @NonNull 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(@NonNull AlarmManager alarmManager, int type, long triggerAtMillis,
-            @NonNull 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(@NonNull AlarmManager alarmManager, int type,
-            long triggerAtMillis, @NonNull 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/android/support/v4/app/AppLaunchChecker.java b/android/support/v4/app/AppLaunchChecker.java
deleted file mode 100644
index af9512a..0000000
--- a/android/support/v4/app/AppLaunchChecker.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.v4.app;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.content.IntentCompat;
-
-/**
- * This class provides APIs for determining how an app has been launched.
- * This can be useful if you want to confirm that a user has launched your
- * app through its front door activity from their launcher/home screen, rather
- * than just if the app has been opened in the past in order to view a link,
- * open a document or perform some other service for other apps on the device.
- */
-public class AppLaunchChecker {
-    private static final String SHARED_PREFS_NAME = "android.support.AppLaunchChecker";
-    private static final String KEY_STARTED_FROM_LAUNCHER = "startedFromLauncher";
-
-    /**
-     * Checks if this app has been launched by the user from their launcher or home screen
-     * since it was installed.
-     *
-     * <p>To track this state properly you must call {@link #onActivityCreate(Activity)}
-     * in your launcher activity's {@link Activity#onCreate(Bundle)} method.</p>
-     *
-     * @param context Context to check
-     * @return true if this app has been started by the user from the launcher at least once
-     */
-    public static boolean hasStartedFromLauncher(@NonNull Context context) {
-        return context.getSharedPreferences(SHARED_PREFS_NAME, 0)
-                .getBoolean(KEY_STARTED_FROM_LAUNCHER, false);
-    }
-
-    /**
-     * Records the parameters of an activity's launch for later use by the other
-     * methods available on this class.
-     *
-     * <p>Your app should call this method in your launcher activity's
-     * {@link Activity#onCreate(Bundle)} method to track launch state.
-     * If the app targets API 23 (Android 6.0 Marshmallow) or later, this state will be
-     * eligible for full data backup and may be restored to the user's device automatically.</p>     *
-     *
-     * @param activity the Activity currently running onCreate
-     */
-    public static void onActivityCreate(@NonNull Activity activity) {
-        final SharedPreferences sp = activity.getSharedPreferences(SHARED_PREFS_NAME, 0);
-        if (sp.getBoolean(KEY_STARTED_FROM_LAUNCHER, false)) {
-            return;
-        }
-
-        final Intent launchIntent = activity.getIntent();
-        if (launchIntent == null) {
-            return;
-        }
-
-        if (Intent.ACTION_MAIN.equals(launchIntent.getAction())
-                && (launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
-                || launchIntent.hasCategory(IntentCompat.CATEGORY_LEANBACK_LAUNCHER))) {
-            sp.edit().putBoolean(KEY_STARTED_FROM_LAUNCHER, true).apply();
-        }
-    }
-}
diff --git a/android/support/v4/app/AppOpsManagerCompat.java b/android/support/v4/app/AppOpsManagerCompat.java
deleted file mode 100644
index 7e97199..0000000
--- a/android/support/v4/app/AppOpsManagerCompat.java
+++ /dev/null
@@ -1,119 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-/**
- * Helper for accessing features in {@link android.app.AppOpsManager}.
- */
-public final class AppOpsManagerCompat {
-
-    /**
-     * Result from {@link #noteOp}: the given caller is allowed to
-     * perform the given operation.
-     */
-    public static final int MODE_ALLOWED = 0;
-
-    /**
-     * Result from {@link #noteOp}: the given caller is not allowed to perform
-     * the given operation, and this attempt should <em>silently fail</em> (it
-     * should not cause the app to crash).
-     */
-    public static final int MODE_IGNORED = 1;
-
-    /**
-     * Result from {@link #noteOp}: the given caller should use its default
-     * security check.  This mode is not normally used; it should only be used
-     * with appop permissions, and callers must explicitly check for it and
-     * deal with it.
-     */
-    public static final int MODE_DEFAULT = 3;
-
-    private AppOpsManagerCompat() {}
-
-    /**
-     * Gets the app op name associated with a given permission.
-     *
-     * @param permission The permission.
-     * @return The app op associated with the permission or null.
-     */
-    @Nullable
-    public static String permissionToOp(@NonNull String permission) {
-        if (SDK_INT >= 23) {
-            return AppOpsManager.permissionToOp(permission);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Make note of an application performing an operation.  Note that you must pass
-     * in both the uid and name of the application to be checked; this function will verify
-     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time.
-     * @param context Your context.
-     * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
-     */
-    public static int noteOp(@NonNull Context context, @NonNull String op, int uid,
-            @NonNull String packageName) {
-        if (SDK_INT >= 23) {
-            AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-            return appOpsManager.noteOp(op, uid, packageName);
-        } else {
-            return MODE_IGNORED;
-        }
-    }
-
-    /**
-     * Make note of an application performing an operation on behalf of another
-     * application when handling an IPC. Note that you must pass the package name
-     * of the application that is being proxied while its UID will be inferred from
-     * the IPC state; this function will verify that the calling uid and proxied
-     * package name match, and if not, return {@link #MODE_IGNORED}. If this call
-     * succeeds, the last execution time of the operation for the proxied app and
-     * your app will be updated to the current time.
-     * @param context Your context.
-     * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param proxiedPackageName The name of the application calling into the proxy application.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
-     */
-    public static int noteProxyOp(@NonNull Context context, @NonNull String op,
-            @NonNull String proxiedPackageName) {
-        if (SDK_INT >= 23) {
-            AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
-            return appOpsManager.noteProxyOp(op, proxiedPackageName);
-        } else {
-            return MODE_IGNORED;
-        }
-    }
-}
diff --git a/android/support/v4/app/BackStackRecord.java b/android/support/v4/app/BackStackRecord.java
deleted file mode 100644
index c2272c3..0000000
--- a/android/support/v4/app/BackStackRecord.java
+++ /dev/null
@@ -1,1026 +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.os.Parcel;
-import android.os.Parcelable;
-import android.support.v4.util.LogWriter;
-import android.support.v4.view.ViewCompat;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-
-final class BackStackState implements Parcelable {
-    final int[] mOps;
-    final int mTransition;
-    final int mTransitionStyle;
-    final String mName;
-    final int mIndex;
-    final int mBreadCrumbTitleRes;
-    final CharSequence mBreadCrumbTitleText;
-    final int mBreadCrumbShortTitleRes;
-    final CharSequence mBreadCrumbShortTitleText;
-    final ArrayList<String> mSharedElementSourceNames;
-    final ArrayList<String> mSharedElementTargetNames;
-    final boolean mReorderingAllowed;
-
-    public BackStackState(BackStackRecord bse) {
-        final int numOps = bse.mOps.size();
-        mOps = new int[numOps * 6];
-
-        if (!bse.mAddToBackStack) {
-            throw new IllegalStateException("Not on back stack");
-        }
-
-        int pos = 0;
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final BackStackRecord.Op op = bse.mOps.get(opNum);
-            mOps[pos++] = op.cmd;
-            mOps[pos++] = op.fragment != null ? op.fragment.mIndex : -1;
-            mOps[pos++] = op.enterAnim;
-            mOps[pos++] = op.exitAnim;
-            mOps[pos++] = op.popEnterAnim;
-            mOps[pos++] = op.popExitAnim;
-        }
-        mTransition = bse.mTransition;
-        mTransitionStyle = bse.mTransitionStyle;
-        mName = bse.mName;
-        mIndex = bse.mIndex;
-        mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
-        mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
-        mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
-        mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
-        mSharedElementSourceNames = bse.mSharedElementSourceNames;
-        mSharedElementTargetNames = bse.mSharedElementTargetNames;
-        mReorderingAllowed = bse.mReorderingAllowed;
-    }
-
-    public BackStackState(Parcel in) {
-        mOps = in.createIntArray();
-        mTransition = in.readInt();
-        mTransitionStyle = in.readInt();
-        mName = in.readString();
-        mIndex = in.readInt();
-        mBreadCrumbTitleRes = in.readInt();
-        mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        mBreadCrumbShortTitleRes = in.readInt();
-        mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        mSharedElementSourceNames = in.createStringArrayList();
-        mSharedElementTargetNames = in.createStringArrayList();
-        mReorderingAllowed = in.readInt() != 0;
-    }
-
-    public BackStackRecord instantiate(FragmentManagerImpl fm) {
-        BackStackRecord bse = new BackStackRecord(fm);
-        int pos = 0;
-        int num = 0;
-        while (pos < mOps.length) {
-            BackStackRecord.Op op = new BackStackRecord.Op();
-            op.cmd = mOps[pos++];
-            if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
-                    "Instantiate " + bse + " op #" + num + " base fragment #" + mOps[pos]);
-            int findex = mOps[pos++];
-            if (findex >= 0) {
-                Fragment f = fm.mActive.get(findex);
-                op.fragment = f;
-            } else {
-                op.fragment = null;
-            }
-            op.enterAnim = mOps[pos++];
-            op.exitAnim = mOps[pos++];
-            op.popEnterAnim = mOps[pos++];
-            op.popExitAnim = mOps[pos++];
-            bse.mEnterAnim = op.enterAnim;
-            bse.mExitAnim = op.exitAnim;
-            bse.mPopEnterAnim = op.popEnterAnim;
-            bse.mPopExitAnim = op.popExitAnim;
-            bse.addOp(op);
-            num++;
-        }
-        bse.mTransition = mTransition;
-        bse.mTransitionStyle = mTransitionStyle;
-        bse.mName = mName;
-        bse.mIndex = mIndex;
-        bse.mAddToBackStack = true;
-        bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
-        bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
-        bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
-        bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
-        bse.mSharedElementSourceNames = mSharedElementSourceNames;
-        bse.mSharedElementTargetNames = mSharedElementTargetNames;
-        bse.mReorderingAllowed = mReorderingAllowed;
-        bse.bumpBackStackNesting(1);
-        return bse;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeIntArray(mOps);
-        dest.writeInt(mTransition);
-        dest.writeInt(mTransitionStyle);
-        dest.writeString(mName);
-        dest.writeInt(mIndex);
-        dest.writeInt(mBreadCrumbTitleRes);
-        TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
-        dest.writeInt(mBreadCrumbShortTitleRes);
-        TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
-        dest.writeStringList(mSharedElementSourceNames);
-        dest.writeStringList(mSharedElementTargetNames);
-        dest.writeInt(mReorderingAllowed ? 1 : 0);
-    }
-
-    public static final Parcelable.Creator<BackStackState> CREATOR
-            = new Parcelable.Creator<BackStackState>() {
-        @Override
-        public BackStackState createFromParcel(Parcel in) {
-            return new BackStackState(in);
-        }
-
-        @Override
-        public BackStackState[] newArray(int size) {
-            return new BackStackState[size];
-        }
-    };
-}
-
-/**
- * Entry of an operation on the fragment back stack.
- */
-final class BackStackRecord extends FragmentTransaction implements
-        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
-    static final String TAG = FragmentManagerImpl.TAG;
-
-    final FragmentManagerImpl mManager;
-
-    static final int OP_NULL = 0;
-    static final int OP_ADD = 1;
-    static final int OP_REPLACE = 2;
-    static final int OP_REMOVE = 3;
-    static final int OP_HIDE = 4;
-    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;
-        Fragment fragment;
-        int enterAnim;
-        int exitAnim;
-        int popEnterAnim;
-        int popExitAnim;
-
-        Op() {
-        }
-
-        Op(int cmd, Fragment fragment) {
-            this.cmd = cmd;
-            this.fragment = fragment;
-        }
-    }
-
-    ArrayList<Op> mOps = new ArrayList<>();
-    int mEnterAnim;
-    int mExitAnim;
-    int mPopEnterAnim;
-    int mPopExitAnim;
-    int mTransition;
-    int mTransitionStyle;
-    boolean mAddToBackStack;
-    boolean mAllowAddToBackStack = true;
-    String mName;
-    boolean mCommitted;
-    int mIndex = -1;
-
-    int mBreadCrumbTitleRes;
-    CharSequence mBreadCrumbTitleText;
-    int mBreadCrumbShortTitleRes;
-    CharSequence mBreadCrumbShortTitleText;
-
-    ArrayList<String> mSharedElementSourceNames;
-    ArrayList<String> mSharedElementTargetNames;
-    boolean mReorderingAllowed = false;
-
-    ArrayList<Runnable> mCommitRunnables;
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("BackStackEntry{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        if (mIndex >= 0) {
-            sb.append(" #");
-            sb.append(mIndex);
-        }
-        if (mName != null) {
-            sb.append(" ");
-            sb.append(mName);
-        }
-        sb.append("}");
-        return sb.toString();
-    }
-
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        dump(prefix, writer, true);
-    }
-
-    public void dump(String prefix, PrintWriter writer, boolean full) {
-        if (full) {
-            writer.print(prefix); writer.print("mName="); writer.print(mName);
-                    writer.print(" mIndex="); writer.print(mIndex);
-                    writer.print(" mCommitted="); writer.println(mCommitted);
-            if (mTransition != FragmentTransaction.TRANSIT_NONE) {
-                writer.print(prefix); writer.print("mTransition=#");
-                        writer.print(Integer.toHexString(mTransition));
-                        writer.print(" mTransitionStyle=#");
-                        writer.println(Integer.toHexString(mTransitionStyle));
-            }
-            if (mEnterAnim != 0 || mExitAnim !=0) {
-                writer.print(prefix); writer.print("mEnterAnim=#");
-                        writer.print(Integer.toHexString(mEnterAnim));
-                        writer.print(" mExitAnim=#");
-                        writer.println(Integer.toHexString(mExitAnim));
-            }
-            if (mPopEnterAnim != 0 || mPopExitAnim !=0) {
-                writer.print(prefix); writer.print("mPopEnterAnim=#");
-                        writer.print(Integer.toHexString(mPopEnterAnim));
-                        writer.print(" mPopExitAnim=#");
-                        writer.println(Integer.toHexString(mPopExitAnim));
-            }
-            if (mBreadCrumbTitleRes != 0 || mBreadCrumbTitleText != null) {
-                writer.print(prefix); writer.print("mBreadCrumbTitleRes=#");
-                        writer.print(Integer.toHexString(mBreadCrumbTitleRes));
-                        writer.print(" mBreadCrumbTitleText=");
-                        writer.println(mBreadCrumbTitleText);
-            }
-            if (mBreadCrumbShortTitleRes != 0 || mBreadCrumbShortTitleText != null) {
-                writer.print(prefix); writer.print("mBreadCrumbShortTitleRes=#");
-                        writer.print(Integer.toHexString(mBreadCrumbShortTitleRes));
-                        writer.print(" mBreadCrumbShortTitleText=");
-                        writer.println(mBreadCrumbShortTitleText);
-            }
-        }
-
-        if (!mOps.isEmpty()) {
-            writer.print(prefix); writer.println("Operations:");
-            String innerPrefix = prefix + "    ";
-            final int numOps = mOps.size();
-            for (int opNum = 0; opNum < numOps; opNum++) {
-                final Op op = mOps.get(opNum);
-                String cmdStr;
-                switch (op.cmd) {
-                    case OP_NULL: cmdStr="NULL"; break;
-                    case OP_ADD: cmdStr="ADD"; break;
-                    case OP_REPLACE: cmdStr="REPLACE"; break;
-                    case OP_REMOVE: cmdStr="REMOVE"; break;
-                    case OP_HIDE: cmdStr="HIDE"; break;
-                    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);
-                        writer.print(": "); writer.print(cmdStr);
-                        writer.print(" "); writer.println(op.fragment);
-                if (full) {
-                    if (op.enterAnim != 0 || op.exitAnim != 0) {
-                        writer.print(prefix); writer.print("enterAnim=#");
-                                writer.print(Integer.toHexString(op.enterAnim));
-                                writer.print(" exitAnim=#");
-                                writer.println(Integer.toHexString(op.exitAnim));
-                    }
-                    if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
-                        writer.print(prefix); writer.print("popEnterAnim=#");
-                                writer.print(Integer.toHexString(op.popEnterAnim));
-                                writer.print(" popExitAnim=#");
-                                writer.println(Integer.toHexString(op.popExitAnim));
-                    }
-                }
-            }
-        }
-    }
-
-    public BackStackRecord(FragmentManagerImpl manager) {
-        mManager = manager;
-    }
-
-    @Override
-    public int getId() {
-        return mIndex;
-    }
-
-    @Override
-    public int getBreadCrumbTitleRes() {
-        return mBreadCrumbTitleRes;
-    }
-
-    @Override
-    public int getBreadCrumbShortTitleRes() {
-        return mBreadCrumbShortTitleRes;
-    }
-
-    @Override
-    public CharSequence getBreadCrumbTitle() {
-        if (mBreadCrumbTitleRes != 0) {
-            return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
-        }
-        return mBreadCrumbTitleText;
-    }
-
-    @Override
-    public CharSequence getBreadCrumbShortTitle() {
-        if (mBreadCrumbShortTitleRes != 0) {
-            return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
-        }
-        return mBreadCrumbShortTitleText;
-    }
-
-    void addOp(Op op) {
-        mOps.add(op);
-        op.enterAnim = mEnterAnim;
-        op.exitAnim = mExitAnim;
-        op.popEnterAnim = mPopEnterAnim;
-        op.popExitAnim = mPopExitAnim;
-    }
-
-    @Override
-    public FragmentTransaction add(Fragment fragment, String tag) {
-        doAddOp(0, fragment, tag, OP_ADD);
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction add(int containerViewId, Fragment fragment) {
-        doAddOp(containerViewId, fragment, null, OP_ADD);
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
-        doAddOp(containerViewId, fragment, tag, OP_ADD);
-        return this;
-    }
-
-    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
-        final Class fragmentClass = fragment.getClass();
-        final int modifiers = fragmentClass.getModifiers();
-        if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
-                || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
-            throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
-                    + " must be a public static class to be  properly recreated from"
-                    + " instance state.");
-        }
-
-        fragment.mFragmentManager = mManager;
-
-        if (tag != null) {
-            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
-                throw new IllegalStateException("Can't change tag of fragment "
-                        + fragment + ": was " + fragment.mTag
-                        + " now " + tag);
-            }
-            fragment.mTag = tag;
-        }
-
-        if (containerViewId != 0) {
-            if (containerViewId == View.NO_ID) {
-                throw new IllegalArgumentException("Can't add fragment "
-                        + fragment + " with tag " + tag + " to container view with no id");
-            }
-            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
-                throw new IllegalStateException("Can't change container ID of fragment "
-                        + fragment + ": was " + fragment.mFragmentId
-                        + " now " + containerViewId);
-            }
-            fragment.mContainerId = fragment.mFragmentId = containerViewId;
-        }
-
-        addOp(new Op(opcmd, fragment));
-    }
-
-    @Override
-    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
-        return replace(containerViewId, fragment, null);
-    }
-
-    @Override
-    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
-        if (containerViewId == 0) {
-            throw new IllegalArgumentException("Must use non-zero containerViewId");
-        }
-
-        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction remove(Fragment fragment) {
-        addOp(new Op(OP_REMOVE, fragment));
-
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction hide(Fragment fragment) {
-        addOp(new Op(OP_HIDE, fragment));
-
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction show(Fragment fragment) {
-        addOp(new Op(OP_SHOW, fragment));
-
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction detach(Fragment fragment) {
-        addOp(new Op(OP_DETACH, fragment));
-
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction attach(Fragment fragment) {
-        addOp(new Op(OP_ATTACH, fragment));
-
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setPrimaryNavigationFragment(Fragment fragment) {
-        addOp(new Op(OP_SET_PRIMARY_NAV, fragment));
-
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setCustomAnimations(int enter, int exit) {
-        return setCustomAnimations(enter, exit, 0, 0);
-    }
-
-    @Override
-    public FragmentTransaction setCustomAnimations(int enter, int exit,
-            int popEnter, int popExit) {
-        mEnterAnim = enter;
-        mExitAnim = exit;
-        mPopEnterAnim = popEnter;
-        mPopExitAnim = popExit;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setTransition(int transition) {
-        mTransition = transition;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction addSharedElement(View sharedElement, String name) {
-        if (FragmentTransition.supportsTransition()) {
-            String transitionName = ViewCompat.getTransitionName(sharedElement);
-            if (transitionName == null) {
-                throw new IllegalArgumentException("Unique transitionNames are required for all" +
-                        " sharedElements");
-            }
-            if (mSharedElementSourceNames == null) {
-                mSharedElementSourceNames = new ArrayList<String>();
-                mSharedElementTargetNames = new ArrayList<String>();
-            } else if (mSharedElementTargetNames.contains(name)) {
-                throw new IllegalArgumentException("A shared element with the target name '"
-                        + name + "' has already been added to the transaction.");
-            } else if (mSharedElementSourceNames.contains(transitionName)) {
-                throw new IllegalArgumentException("A shared element with the source name '"
-                        + transitionName + " has already been added to the transaction.");
-            }
-
-            mSharedElementSourceNames.add(transitionName);
-            mSharedElementTargetNames.add(name);
-        }
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setTransitionStyle(int styleRes) {
-        mTransitionStyle = styleRes;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction addToBackStack(String name) {
-        if (!mAllowAddToBackStack) {
-            throw new IllegalStateException(
-                    "This FragmentTransaction is not allowed to be added to the back stack.");
-        }
-        mAddToBackStack = true;
-        mName = name;
-        return this;
-    }
-
-    @Override
-    public boolean isAddToBackStackAllowed() {
-        return mAllowAddToBackStack;
-    }
-
-    @Override
-    public FragmentTransaction disallowAddToBackStack() {
-        if (mAddToBackStack) {
-            throw new IllegalStateException(
-                    "This transaction is already being added to the back stack");
-        }
-        mAllowAddToBackStack = false;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setBreadCrumbTitle(int res) {
-        mBreadCrumbTitleRes = res;
-        mBreadCrumbTitleText = null;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
-        mBreadCrumbTitleRes = 0;
-        mBreadCrumbTitleText = text;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setBreadCrumbShortTitle(int res) {
-        mBreadCrumbShortTitleRes = res;
-        mBreadCrumbShortTitleText = null;
-        return this;
-    }
-
-    @Override
-    public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
-        mBreadCrumbShortTitleRes = 0;
-        mBreadCrumbShortTitleText = text;
-        return this;
-    }
-
-    void bumpBackStackNesting(int amt) {
-        if (!mAddToBackStack) {
-            return;
-        }
-        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
-                + " by " + amt);
-        final int numOps = mOps.size();
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final Op op = mOps.get(opNum);
-            if (op.fragment != null) {
-                op.fragment.mBackStackNesting += amt;
-                if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
-                        + op.fragment + " to " + op.fragment.mBackStackNesting);
-            }
-        }
-    }
-
-    @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);
-    }
-
-    @Override
-    public int commitAllowingStateLoss() {
-        return commitInternal(true);
-    }
-
-    @Override
-    public void commitNow() {
-        disallowAddToBackStack();
-        mManager.execSingleAction(this, false);
-    }
-
-    @Override
-    public void commitNowAllowingStateLoss() {
-        disallowAddToBackStack();
-        mManager.execSingleAction(this, true);
-    }
-
-    @Override
-    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) {
-            Log.v(TAG, "Commit: " + this);
-            LogWriter logw = new LogWriter(TAG);
-            PrintWriter pw = new PrintWriter(logw);
-            dump("  ", null, pw, null);
-            pw.close();
-        }
-        mCommitted = true;
-        if (mAddToBackStack) {
-            mIndex = mManager.allocBackStackIndex(this);
-        } else {
-            mIndex = -1;
-        }
-        mManager.enqueueAction(this, allowStateLoss);
-        return mIndex;
-    }
-
-    /**
-     * Implementation of {@link FragmentManagerImpl.OpGenerator}.
-     * This operation is added to the list of pending actions during {@link #commit()}, and
-     * will be executed on the UI thread to run this FragmentTransaction.
-     *
-     * @param records Modified to add this BackStackRecord
-     * @param isRecordPop Modified to add a false (this isn't a pop)
-     * @return true always because the records and isRecordPop will always be changed
-     */
-    @Override
-    public boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
-        if (FragmentManagerImpl.DEBUG) {
-            Log.v(TAG, "Run: " + this);
-        }
-
-        records.add(this);
-        isRecordPop.add(false);
-        if (mAddToBackStack) {
-            mManager.addBackStackState(this);
-        }
-        return true;
-    }
-
-    boolean interactsWith(int containerId) {
-        final int numOps = mOps.size();
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final Op op = mOps.get(opNum);
-            final int fragContainer = op.fragment != null ? op.fragment.mContainerId : 0;
-            if (fragContainer != 0 && fragContainer == containerId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean interactsWith(ArrayList<BackStackRecord> records, int startIndex, int endIndex) {
-        if (endIndex == startIndex) {
-            return false;
-        }
-        final int numOps = mOps.size();
-        int lastContainer = -1;
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final Op op = mOps.get(opNum);
-            final int container = op.fragment != null ? op.fragment.mContainerId : 0;
-            if (container != 0 && container != lastContainer) {
-                lastContainer = container;
-                for (int i = startIndex; i < endIndex; i++) {
-                    BackStackRecord record = records.get(i);
-                    final int numThoseOps = record.mOps.size();
-                    for (int thoseOpIndex = 0; thoseOpIndex < numThoseOps; thoseOpIndex++) {
-                        final Op thatOp = record.mOps.get(thoseOpIndex);
-                        final int thatContainer = thatOp.fragment != null
-                                ? thatOp.fragment.mContainerId : 0;
-                        if (thatContainer == container) {
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Executes the operations contained within this transaction. The Fragment states will only
-     * be modified if optimizations are not allowed.
-     */
-    void executeOps() {
-        final int numOps = mOps.size();
-        for (int opNum = 0; opNum < numOps; opNum++) {
-            final Op op = mOps.get(opNum);
-            final Fragment f = op.fragment;
-            if (f != null) {
-                f.setNextTransition(mTransition, mTransitionStyle);
-            }
-            switch (op.cmd) {
-                case OP_ADD:
-                    f.setNextAnim(op.enterAnim);
-                    mManager.addFragment(f, false);
-                    break;
-                case OP_REMOVE:
-                    f.setNextAnim(op.exitAnim);
-                    mManager.removeFragment(f);
-                    break;
-                case OP_HIDE:
-                    f.setNextAnim(op.exitAnim);
-                    mManager.hideFragment(f);
-                    break;
-                case OP_SHOW:
-                    f.setNextAnim(op.enterAnim);
-                    mManager.showFragment(f);
-                    break;
-                case OP_DETACH:
-                    f.setNextAnim(op.exitAnim);
-                    mManager.detachFragment(f);
-                    break;
-                case OP_ATTACH:
-                    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 (!mReorderingAllowed && op.cmd != OP_ADD && f != null) {
-                mManager.moveFragmentToExpectedState(f);
-            }
-        }
-        if (!mReorderingAllowed) {
-            // Added fragments are added at the end to comply with prior behavior.
-            mManager.moveToState(mManager.mCurState, true);
-        }
-    }
-
-    /**
-     * Reverses the execution of the operations within this transaction. The Fragment states will
-     * only be modified if reordering is not allowed.
-     *
-     * @param moveToState {@code true} if added fragments should be moved to their final state
-     *                    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;
-            if (f != null) {
-                f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition),
-                        mTransitionStyle);
-            }
-            switch (op.cmd) {
-                case OP_ADD:
-                    f.setNextAnim(op.popExitAnim);
-                    mManager.removeFragment(f);
-                    break;
-                case OP_REMOVE:
-                    f.setNextAnim(op.popEnterAnim);
-                    mManager.addFragment(f, false);
-                    break;
-                case OP_HIDE:
-                    f.setNextAnim(op.popEnterAnim);
-                    mManager.showFragment(f);
-                    break;
-                case OP_SHOW:
-                    f.setNextAnim(op.popExitAnim);
-                    mManager.hideFragment(f);
-                    break;
-                case OP_DETACH:
-                    f.setNextAnim(op.popEnterAnim);
-                    mManager.attachFragment(f);
-                    break;
-                case OP_ATTACH:
-                    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 (!mReorderingAllowed && op.cmd != OP_REMOVE && f != null) {
-                mManager.moveFragmentToExpectedState(f);
-            }
-        }
-        if (!mReorderingAllowed && moveToState) {
-            mManager.moveToState(mManager.mCurState, true);
-        }
-    }
-
-    /**
-     * 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
-     */
-    @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) {
-                case OP_ADD:
-                case OP_ATTACH:
-                    added.add(op.fragment);
-                    break;
-                case OP_REMOVE:
-                case OP_DETACH: {
-                    added.remove(op.fragment);
-                    if (op.fragment == oldPrimaryNav) {
-                        mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
-                        opNum++;
-                        oldPrimaryNav = null;
-                    }
-                }
-                break;
-                case OP_REPLACE: {
-                    final Fragment f = op.fragment;
-                    final int containerId = f.mContainerId;
-                    boolean alreadyAdded = false;
-                    for (int i = added.size() - 1; i >= 0; i--) {
-                        final Fragment old = added.get(i);
-                        if (old.mContainerId == containerId) {
-                            if (old == f) {
-                                alreadyAdded = true;
-                            } else {
-                                // 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;
-                                removeOp.popExitAnim = op.popExitAnim;
-                                mOps.add(opNum, removeOp);
-                                added.remove(old);
-                                opNum++;
-                            }
-                        }
-                    }
-                    if (alreadyAdded) {
-                        mOps.remove(opNum);
-                        opNum--;
-                    } else {
-                        op.cmd = OP_ADD;
-                        added.add(f);
-                    }
-                }
-                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;
-    }
-
-    /**
-     * Removes fragments that are added or removed during a pop operation.
-     *
-     * @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
-     */
-    Fragment trackAddedFragmentsInPop(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
-        for (int opNum = 0; opNum < mOps.size(); opNum++) {
-            final Op op = mOps.get(opNum);
-            switch (op.cmd) {
-                case OP_ADD:
-                case OP_ATTACH:
-                    added.remove(op.fragment);
-                    break;
-                case OP_REMOVE:
-                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() {
-        for (int opNum = 0; opNum < mOps.size(); opNum++) {
-            final Op op = mOps.get(opNum);
-            if (isFragmentPostponed(op)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    void setOnStartPostponedListener(Fragment.OnStartEnterTransitionListener listener) {
-        for (int opNum = 0; opNum < mOps.size(); opNum++) {
-            final Op op = mOps.get(opNum);
-            if (isFragmentPostponed(op)) {
-                op.fragment.setOnStartEnterTransitionListener(listener);
-            }
-        }
-    }
-
-    private static boolean isFragmentPostponed(Op op) {
-        final Fragment fragment = op.fragment;
-        return fragment != null && fragment.mAdded && fragment.mView != null && !fragment.mDetached
-                && !fragment.mHidden && fragment.isPostponed();
-    }
-
-    @Override
-    public String getName() {
-        return mName;
-    }
-
-    public int getTransition() {
-        return mTransition;
-    }
-
-    public int getTransitionStyle() {
-        return mTransitionStyle;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return mOps.isEmpty();
-    }
-}
diff --git a/android/support/v4/app/BaseFragmentActivityApi14.java b/android/support/v4/app/BaseFragmentActivityApi14.java
deleted file mode 100644
index f60d6ce..0000000
--- a/android/support/v4/app/BaseFragmentActivityApi14.java
+++ /dev/null
@@ -1,82 +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.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/android/support/v4/app/BaseFragmentActivityApi16.java b/android/support/v4/app/BaseFragmentActivityApi16.java
deleted file mode 100644
index 0af2cec..0000000
--- a/android/support/v4/app/BaseFragmentActivityApi16.java
+++ /dev/null
@@ -1,72 +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 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/android/support/v4/app/BundleCompat.java b/android/support/v4/app/BundleCompat.java
deleted file mode 100644
index 21d730d..0000000
--- a/android/support/v4/app/BundleCompat.java
+++ /dev/null
@@ -1,123 +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.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * 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() {}
-
-    /**
-     * A convenience method to handle getting an {@link IBinder} inside a {@link Bundle} for all
-     * Android versions.
-     * @param bundle The bundle to get the {@link IBinder}.
-     * @param key    The key to use while getting the {@link IBinder}.
-     * @return       The {@link IBinder} that was obtained.
-     */
-    @Nullable
-    public static IBinder getBinder(@NonNull Bundle bundle, @Nullable String key) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return bundle.getBinder(key);
-        } else {
-            return BundleCompatBaseImpl.getBinder(bundle, key);
-        }
-    }
-
-    /**
-     * A convenience method to handle putting an {@link IBinder} inside a {@link Bundle} for all
-     * Android versions.
-     * @param bundle The bundle to insert the {@link IBinder}.
-     * @param key    The key to use while putting the {@link IBinder}.
-     * @param binder The {@link IBinder} to put.
-     */
-    public static void putBinder(@NonNull Bundle bundle, @Nullable String key,
-            @Nullable IBinder binder) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            bundle.putBinder(key, binder);
-        } else {
-            BundleCompatBaseImpl.putBinder(bundle, key, binder);
-        }
-    }
-}
diff --git a/android/support/v4/app/DialogFragment.java b/android/support/v4/app/DialogFragment.java
deleted file mode 100644
index dfd0416..0000000
--- a/android/support/v4/app/DialogFragment.java
+++ /dev/null
@@ -1,469 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-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.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Static library support version of the framework's {@link android.app.DialogFragment}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public class DialogFragment extends Fragment
-        implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface DialogStyle {}
-
-    /**
-     * Style for {@link #setStyle(int, int)}: a basic,
-     * normal dialog.
-     */
-    public static final int STYLE_NORMAL = 0;
-
-    /**
-     * Style for {@link #setStyle(int, int)}: don't include
-     * a title area.
-     */
-    public static final int STYLE_NO_TITLE = 1;
-
-    /**
-     * Style for {@link #setStyle(int, int)}: don't draw
-     * any frame at all; the view hierarchy returned by {@link #onCreateView}
-     * is entirely responsible for drawing the dialog.
-     */
-    public static final int STYLE_NO_FRAME = 2;
-
-    /**
-     * Style for {@link #setStyle(int, int)}: like
-     * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
-     * The user can not touch it, and its window will not receive input focus.
-     */
-    public static final int STYLE_NO_INPUT = 3;
-
-    private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
-    private static final String SAVED_STYLE = "android:style";
-    private static final String SAVED_THEME = "android:theme";
-    private static final String SAVED_CANCELABLE = "android:cancelable";
-    private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
-    private static final String SAVED_BACK_STACK_ID = "android:backStackId";
-
-    int mStyle = STYLE_NORMAL;
-    int mTheme = 0;
-    boolean mCancelable = true;
-    boolean mShowsDialog = true;
-    int mBackStackId = -1;
-
-    Dialog mDialog;
-    boolean mViewDestroyed;
-    boolean mDismissed;
-    boolean mShownByMe;
-
-    public DialogFragment() {
-    }
-
-    /**
-     * Call to customize the basic appearance and behavior of the
-     * fragment's dialog.  This can be used for some common dialog behaviors,
-     * taking care of selecting flags, theme, and other options for you.  The
-     * same effect can be achieve by manually setting Dialog and Window
-     * attributes yourself.  Calling this after the fragment's Dialog is
-     * created will have no effect.
-     *
-     * @param style Selects a standard style: may be {@link #STYLE_NORMAL},
-     * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
-     * {@link #STYLE_NO_INPUT}.
-     * @param theme Optional custom theme.  If 0, an appropriate theme (based
-     * on the style) will be selected for you.
-     */
-    public void setStyle(@DialogStyle int style, @StyleRes int theme) {
-        mStyle = style;
-        if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
-            mTheme = android.R.style.Theme_Panel;
-        }
-        if (theme != 0) {
-            mTheme = theme;
-        }
-    }
-
-    /**
-     * Display the dialog, adding the fragment to the given FragmentManager.  This
-     * is a convenience for explicitly creating a transaction, adding the
-     * fragment to it with the given tag, and committing it.  This does
-     * <em>not</em> add the transaction to the back stack.  When the fragment
-     * is dismissed, a new transaction will be executed to remove it from
-     * the activity.
-     * @param manager The FragmentManager this fragment will be added to.
-     * @param tag The tag for this fragment, as per
-     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
-     */
-    public void show(FragmentManager manager, String tag) {
-        mDismissed = false;
-        mShownByMe = true;
-        FragmentTransaction ft = manager.beginTransaction();
-        ft.add(this, tag);
-        ft.commit();
-    }
-
-    /**
-     * Display the dialog, adding the fragment using an existing transaction
-     * and then committing the transaction.
-     * @param transaction An existing transaction in which to add the fragment.
-     * @param tag The tag for this fragment, as per
-     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
-     * @return Returns the identifier of the committed transaction, as per
-     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
-     */
-    public int show(FragmentTransaction transaction, String tag) {
-        mDismissed = false;
-        mShownByMe = true;
-        transaction.add(this, tag);
-        mViewDestroyed = false;
-        mBackStackId = transaction.commit();
-        return mBackStackId;
-    }
-
-    /**
-     * Dismiss the fragment and its dialog.  If the fragment was added to the
-     * back stack, all back stack state up to and including this entry will
-     * be popped.  Otherwise, a new transaction will be committed to remove
-     * the fragment.
-     */
-    public void dismiss() {
-        dismissInternal(false);
-    }
-
-    /**
-     * Version of {@link #dismiss()} that uses
-     * {@link FragmentTransaction#commitAllowingStateLoss()
-     * FragmentTransaction.commitAllowingStateLoss()}. See linked
-     * documentation for further details.
-     */
-    public void dismissAllowingStateLoss() {
-        dismissInternal(true);
-    }
-
-    void dismissInternal(boolean allowStateLoss) {
-        if (mDismissed) {
-            return;
-        }
-        mDismissed = true;
-        mShownByMe = false;
-        if (mDialog != null) {
-            mDialog.dismiss();
-            mDialog = null;
-        }
-        mViewDestroyed = true;
-        if (mBackStackId >= 0) {
-            getFragmentManager().popBackStack(mBackStackId,
-                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
-            mBackStackId = -1;
-        } else {
-            FragmentTransaction ft = getFragmentManager().beginTransaction();
-            ft.remove(this);
-            if (allowStateLoss) {
-                ft.commitAllowingStateLoss();
-            } else {
-                ft.commit();
-            }
-        }
-    }
-
-    public Dialog getDialog() {
-        return mDialog;
-    }
-
-    @StyleRes
-    public int getTheme() {
-        return mTheme;
-    }
-
-    /**
-     * Control whether the shown Dialog is cancelable.  Use this instead of
-     * directly calling {@link Dialog#setCancelable(boolean)
-     * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
-     * its behavior based on this.
-     *
-     * @param cancelable If true, the dialog is cancelable.  The default
-     * is true.
-     */
-    public void setCancelable(boolean cancelable) {
-        mCancelable = cancelable;
-        if (mDialog != null) mDialog.setCancelable(cancelable);
-    }
-
-    /**
-     * Return the current value of {@link #setCancelable(boolean)}.
-     */
-    public boolean isCancelable() {
-        return mCancelable;
-    }
-
-    /**
-     * Controls whether this fragment should be shown in a dialog.  If not
-     * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
-     * and the fragment's view hierarchy will thus not be added to it.  This
-     * allows you to instead use it as a normal fragment (embedded inside of
-     * its activity).
-     *
-     * <p>This is normally set for you based on whether the fragment is
-     * associated with a container view ID passed to
-     * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
-     * If the fragment was added with a container, setShowsDialog will be
-     * initialized to false; otherwise, it will be true.
-     *
-     * @param showsDialog If true, the fragment will be displayed in a Dialog.
-     * If false, no Dialog will be created and the fragment's view hierarchy
-     * left undisturbed.
-     */
-    public void setShowsDialog(boolean showsDialog) {
-        mShowsDialog = showsDialog;
-    }
-
-    /**
-     * Return the current value of {@link #setShowsDialog(boolean)}.
-     */
-    public boolean getShowsDialog() {
-        return mShowsDialog;
-    }
-
-    @Override
-    public void onAttach(Context context) {
-        super.onAttach(context);
-        if (!mShownByMe) {
-            // If not explicitly shown through our API, take this as an
-            // indication that the dialog is no longer dismissed.
-            mDismissed = false;
-        }
-    }
-
-    @Override
-    public void onDetach() {
-        super.onDetach();
-        if (!mShownByMe && !mDismissed) {
-            // The fragment was not shown by a direct call here, it is not
-            // dismissed, and now it is being detached...  well, okay, thou
-            // art now dismissed.  Have fun.
-            mDismissed = true;
-        }
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mShowsDialog = mContainerId == 0;
-
-        if (savedInstanceState != null) {
-            mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
-            mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
-            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
-            mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
-            mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
-        }
-    }
-
-    @Override
-    public LayoutInflater onGetLayoutInflater(Bundle savedInstanceState) {
-        if (!mShowsDialog) {
-            return super.onGetLayoutInflater(savedInstanceState);
-        }
-
-        mDialog = onCreateDialog(savedInstanceState);
-
-        if (mDialog != null) {
-            setupDialog(mDialog, mStyle);
-
-            return (LayoutInflater) mDialog.getContext().getSystemService(
-                    Context.LAYOUT_INFLATER_SERVICE);
-        }
-        return (LayoutInflater) mHost.getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setupDialog(Dialog dialog, int style) {
-        switch (style) {
-            case STYLE_NO_INPUT:
-                dialog.getWindow().addFlags(
-                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
-                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
-                // fall through...
-            case STYLE_NO_FRAME:
-            case STYLE_NO_TITLE:
-                dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-        }
-    }
-
-    /**
-     * Override to build your own custom Dialog container.  This is typically
-     * used to show an AlertDialog instead of a generic Dialog; when doing so,
-     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} does not need
-     * to be implemented since the AlertDialog takes care of its own content.
-     *
-     * <p>This method will be called after {@link #onCreate(Bundle)} and
-     * before {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.  The
-     * default implementation simply instantiates and returns a {@link Dialog}
-     * class.
-     *
-     * <p><em>Note: DialogFragment own the {@link Dialog#setOnCancelListener
-     * Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener
-     * Dialog.setOnDismissListener} callbacks.  You must not set them yourself.</em>
-     * To find out about these events, override {@link #onCancel(DialogInterface)}
-     * and {@link #onDismiss(DialogInterface)}.</p>
-     *
-     * @param savedInstanceState The last saved instance state of the Fragment,
-     * or null if this is a freshly created Fragment.
-     *
-     * @return Return a new Dialog instance to be displayed by the Fragment.
-     */
-    @NonNull
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        return new Dialog(getActivity(), getTheme());
-    }
-
-    @Override
-    public void onCancel(DialogInterface dialog) {
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        if (!mViewDestroyed) {
-            // Note: we need to use allowStateLoss, because the dialog
-            // dispatches this asynchronously so we can receive the call
-            // after the activity is paused.  Worst case, when the user comes
-            // back to the activity they see the dialog again.
-            dismissInternal(true);
-        }
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        if (!mShowsDialog) {
-            return;
-        }
-
-        View view = getView();
-        if (view != null) {
-            if (view.getParent() != null) {
-                throw new IllegalStateException(
-                        "DialogFragment can not be attached to a container view");
-            }
-            mDialog.setContentView(view);
-        }
-        final Activity activity = getActivity();
-        if (activity != null) {
-            mDialog.setOwnerActivity(activity);
-        }
-        mDialog.setCancelable(mCancelable);
-        mDialog.setOnCancelListener(this);
-        mDialog.setOnDismissListener(this);
-        if (savedInstanceState != null) {
-            Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
-            if (dialogState != null) {
-                mDialog.onRestoreInstanceState(dialogState);
-            }
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        if (mDialog != null) {
-            mViewDestroyed = false;
-            mDialog.show();
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        if (mDialog != null) {
-            Bundle dialogState = mDialog.onSaveInstanceState();
-            if (dialogState != null) {
-                outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
-            }
-        }
-        if (mStyle != STYLE_NORMAL) {
-            outState.putInt(SAVED_STYLE, mStyle);
-        }
-        if (mTheme != 0) {
-            outState.putInt(SAVED_THEME, mTheme);
-        }
-        if (!mCancelable) {
-            outState.putBoolean(SAVED_CANCELABLE, mCancelable);
-        }
-        if (!mShowsDialog) {
-            outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
-        }
-        if (mBackStackId != -1) {
-            outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
-        }
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mDialog != null) {
-            mDialog.hide();
-        }
-    }
-
-    /**
-     * Remove dialog.
-     */
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        if (mDialog != null) {
-            // Set removed here because this dismissal is just to hide
-            // the dialog -- we don't want this to cause the fragment to
-            // actually be removed.
-            mViewDestroyed = true;
-            mDialog.dismiss();
-            mDialog = null;
-        }
-    }
-}
diff --git a/android/support/v4/app/Fragment.java b/android/support/v4/app/Fragment.java
deleted file mode 100644
index f3c73ae..0000000
--- a/android/support/v4/app/Fragment.java
+++ /dev/null
@@ -1,2743 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.Animator;
-import android.app.Activity;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.arch.lifecycle.LifecycleRegistry;
-import android.content.ComponentCallbacks;
-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.Bundle;
-import android.os.Looper;
-import android.os.Parcel;
-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.annotation.StringRes;
-import android.support.v4.util.DebugUtils;
-import android.support.v4.util.SimpleArrayMap;
-import android.support.v4.view.LayoutInflaterCompat;
-import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.ViewGroup;
-import android.view.animation.Animation;
-import android.widget.AdapterView;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * Static library support version of the framework's {@link android.app.Fragment}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation. See the framework {@link android.app.Fragment}
- * documentation for a class overview.
- *
- * <p>The main differences when using this support version instead of the framework version are:
- * <ul>
- *  <li>Your activity must extend {@link FragmentActivity}
- *  <li>You must call {@link FragmentActivity#getSupportFragmentManager} to get the
- *  {@link FragmentManager}
- * </ul>
- *
- */
-public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner {
-    private static final SimpleArrayMap<String, Class<?>> sClassMap =
-            new SimpleArrayMap<String, Class<?>>();
-
-    static final Object USE_DEFAULT_TRANSITION = new Object();
-
-    static final int INITIALIZING = 0;     // Not yet created.
-    static final int CREATED = 1;          // Created.
-    static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
-    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;
-
-    // Target fragment.
-    Fragment mTarget;
-
-    // For use when retaining a fragment: this is the index of the last mTarget.
-    int mTargetIndex = -1;
-
-    // Target request code.
-    int mTargetRequestCode;
-
-    // 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;
-
-    // The fragment manager we are associated with.  Set as soon as the
-    // fragment is used in a transaction; cleared after it has been removed
-    // from all transactions.
-    FragmentManagerImpl mFragmentManager;
-
-    // Host this fragment is attached to.
-    FragmentHostCallback mHost;
-
-    // Private fragment manager for child fragments inside of this one.
-    FragmentManagerImpl mChildFragmentManager;
-
-    // For use when restoring fragment state and descendant fragments are retained.
-    // This state is set by FragmentState.instantiate and cleared in onCreate.
-    FragmentManagerNonConfig mChildNonConfig;
-
-    // If this Fragment is contained in another Fragment, this is that container.
-    Fragment mParentFragment;
-
-    // The optional identifier for this fragment -- either the container ID if it
-    // 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;
-
-    // If set this fragment would like its instance retained across
-    // configuration changes.
-    boolean mRetainInstance;
-
-    // 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;
-
-    // Set to true to allow the fragment's menu to be shown.
-    boolean mMenuVisible = true;
-
-    // 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;
-
-    LoaderManagerImpl mLoaderManager;
-    boolean mLoadersStarted;
-    boolean mCheckedForLoaderManager;
-
-    // The animation and transition information for the fragment. This will be null
-    // unless the elements are explicitly accessed and should remain null for Fragments
-    // without Views.
-    AnimationInfo mAnimationInfo;
-
-    // True if the View was added, and its animation has yet to be run. This could
-    // also indicate that the fragment view hasn't been made visible, even if there is no
-    // animation for this fragment.
-    boolean mIsNewlyAdded;
-
-    // True if mHidden has been changed and the animation should be scheduled.
-    boolean mHiddenChanged;
-
-    // The alpha of the view when the view was added and then postponed. If the value is less
-    // than zero, this means that the view's add was canceled and should not participate in
-    // removal animations.
-    float mPostponedAlpha;
-
-    // The cached value from onGetLayoutInflater(Bundle) that will be returned from
-    // 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;
-
-    LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
-
-    @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycleRegistry;
-    }
-
-    /**
-     * State information that has been retrieved from a fragment instance
-     * through {@link FragmentManager#saveFragmentInstanceState(Fragment)
-     * FragmentManager.saveFragmentInstanceState}.
-     */
-    public static class SavedState implements Parcelable {
-        final Bundle mState;
-
-        SavedState(Bundle state) {
-            mState = state;
-        }
-
-        SavedState(Parcel in, ClassLoader loader) {
-            mState = in.readBundle();
-            if (loader != null && mState != null) {
-                mState.setClassLoader(loader);
-            }
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeBundle(mState);
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
-            @Override
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in, null);
-            }
-
-            @Override
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-
-    /**
-     * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
-     * there is an instantiation failure.
-     */
-    static public class InstantiationException extends RuntimeException {
-        public InstantiationException(String msg, Exception cause) {
-            super(msg, cause);
-        }
-    }
-
-    /**
-     * Default constructor.  <strong>Every</strong> fragment must have an
-     * empty constructor, so it can be instantiated when restoring its
-     * activity's state.  It is strongly recommended that subclasses do not
-     * have other constructors with parameters, since these constructors
-     * 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
-     * its context. Some applications may also want to implement {@link #onInflate} to retrieve
-     * attributes from a layout resource, although note this happens when the fragment is attached.
-     */
-    public Fragment() {
-    }
-
-    /**
-     * Like {@link #instantiate(Context, String, Bundle)} but with a null
-     * argument Bundle.
-     */
-    public static Fragment instantiate(Context context, String fname) {
-        return instantiate(context, fname, null);
-    }
-
-    /**
-     * Create a new instance of a Fragment with the given class name.  This is
-     * the same as calling its empty constructor.
-     *
-     * @param context The calling context being used to instantiate the fragment.
-     * This is currently just used to get its ClassLoader.
-     * @param fname The class name of the fragment to instantiate.
-     * @param args Bundle of arguments to supply to the fragment, which it
-     * can retrieve with {@link #getArguments()}.  May be null.
-     * @return Returns a new fragment instance.
-     * @throws InstantiationException If there is a failure in instantiating
-     * the given fragment class.  This is a runtime exception; it is not
-     * normally expected to happen.
-     */
-    public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
-        try {
-            Class<?> clazz = sClassMap.get(fname);
-            if (clazz == null) {
-                // Class not found in the cache, see if it's real, and try to add it
-                clazz = context.getClassLoader().loadClass(fname);
-                sClassMap.put(fname, clazz);
-            }
-            Fragment f = (Fragment) clazz.getConstructor().newInstance();
-            if (args != null) {
-                args.setClassLoader(f.getClass().getClassLoader());
-                f.setArguments(args);
-            }
-            return f;
-        } catch (ClassNotFoundException e) {
-            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 (java.lang.InstantiationException e) {
-            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 (IllegalAccessException e) {
-            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);
-        }
-    }
-
-    /**
-     * Determine if the given fragment name is a support library fragment class.
-     *
-     * @param context Context used to determine the correct ClassLoader to use
-     * @param fname Class name of the fragment to test
-     * @return true if <code>fname</code> is <code>android.support.v4.app.Fragment</code>
-     *         or a subclass, false otherwise.
-     */
-    static boolean isSupportFragmentClass(Context context, String fname) {
-        try {
-            Class<?> clazz = sClassMap.get(fname);
-            if (clazz == null) {
-                // Class not found in the cache, see if it's real, and try to add it
-                clazz = context.getClassLoader().loadClass(fname);
-                sClassMap.put(fname, clazz);
-            }
-            return Fragment.class.isAssignableFrom(clazz);
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-    }
-
-    final void restoreViewState(Bundle savedInstanceState) {
-        if (mSavedViewState != null) {
-            mInnerView.restoreHierarchyState(mSavedViewState);
-            mSavedViewState = null;
-        }
-        mCalled = false;
-        onViewStateRestored(savedInstanceState);
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onViewStateRestored()");
-        }
-    }
-
-    final void setIndex(int index, Fragment parent) {
-        mIndex = index;
-        if (parent != null) {
-            mWho = parent.mWho + ":" + mIndex;
-        } else {
-            mWho = "android:fragment:" + mIndex;
-        }
-    }
-
-    final boolean isInBackStack() {
-        return mBackStackNesting > 0;
-    }
-
-    /**
-     * Subclasses can not override equals().
-     */
-    @Override final public boolean equals(Object o) {
-        return super.equals(o);
-    }
-
-    /**
-     * Subclasses can not override hashCode().
-     */
-    @Override final public int hashCode() {
-        return super.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        DebugUtils.buildShortClassTag(this, sb);
-        if (mIndex >= 0) {
-            sb.append(" #");
-            sb.append(mIndex);
-        }
-        if (mFragmentId != 0) {
-            sb.append(" id=0x");
-            sb.append(Integer.toHexString(mFragmentId));
-        }
-        if (mTag != null) {
-            sb.append(" ");
-            sb.append(mTag);
-        }
-        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
-     * supplied when adding the fragment.
-     */
-    final public int getId() {
-        return mFragmentId;
-    }
-
-    /**
-     * Get the tag name of the fragment, if specified.
-     */
-    @Nullable
-    final public String getTag() {
-        return mTag;
-    }
-
-    /**
-     * 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(@Nullable Bundle args) {
-        if (mIndex >= 0 && isStateSaved()) {
-            throw new IllegalStateException("Fragment already active and state has been saved");
-        }
-        mArguments = args;
-    }
-
-    /**
-     * Return the arguments supplied when the fragment was instantiated,
-     * if any.
-     */
-    @Nullable
-    final public Bundle getArguments() {
-        return mArguments;
-    }
-
-    /**
-     * 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)
-     * FragmentManager.saveFragmentInstanceState}.
-     *
-     * @param state The state the fragment should be restored from.
-     */
-    public void setInitialSavedState(@Nullable SavedState state) {
-        if (mIndex >= 0) {
-            throw new IllegalStateException("Fragment already active");
-        }
-        mSavedFragmentState = state != null && state.mState != null
-                ? state.mState : null;
-    }
-
-    /**
-     * Optional target for this fragment.  This may be used, for example,
-     * if this fragment is being started by another, and when done wants to
-     * give a result back to the first.  The target set here is retained
-     * across instances via {@link FragmentManager#putFragment
-     * FragmentManager.putFragment()}.
-     *
-     * @param fragment The fragment that is the target of this one.
-     * @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(@Nullable 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;
-    }
-
-    /**
-     * Return the target fragment set by {@link #setTargetFragment}.
-     */
-    @Nullable
-    final public Fragment getTargetFragment() {
-        return mTarget;
-    }
-
-    /**
-     * Return the target request code set by {@link #setTargetFragment}.
-     */
-    final public int getTargetRequestCode() {
-        return mTargetRequestCode;
-    }
-
-    /**
-     * Return the {@link Context} this fragment is currently associated with.
-     */
-    public Context getContext() {
-        return mHost == null ? null : mHost.getContext();
-    }
-
-    /**
-     * Return the {@link FragmentActivity} this fragment is currently associated with.
-     * May return {@code null} if the fragment is associated with a {@link Context}
-     * instead.
-     */
-    final public FragmentActivity getActivity() {
-        return mHost == null ? null : (FragmentActivity) mHost.getActivity();
-    }
-
-    /**
-     * Return the host object of this fragment. May return {@code null} if the fragment
-     * isn't currently being hosted.
-     */
-    final public Object getHost() {
-        return mHost == null ? null : mHost.onGetHost();
-    }
-
-    /**
-     * Return <code>getActivity().getResources()</code>.
-     */
-    @NonNull
-    final public Resources getResources() {
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
-        }
-        return mHost.getContext().getResources();
-    }
-
-    /**
-     * Return a localized, styled CharSequence from the application's package's
-     * default string table.
-     *
-     * @param resId Resource id for the CharSequence text
-     */
-    @NonNull
-    public final CharSequence getText(@StringRes int resId) {
-        return getResources().getText(resId);
-    }
-
-    /**
-     * Return a localized string from the application's package's
-     * default string table.
-     *
-     * @param resId Resource id for the string
-     */
-    @NonNull
-    public final String getString(@StringRes int resId) {
-        return getResources().getString(resId);
-    }
-
-    /**
-     * Return a localized formatted string from the application's package's
-     * default string table, substituting the format arguments as defined in
-     * {@link java.util.Formatter} and {@link java.lang.String#format}.
-     *
-     * @param resId Resource id for the format string
-     * @param formatArgs The format arguments that will be used for substitution.
-     */
-    @NonNull
-    public final String getString(@StringRes int resId, Object... formatArgs) {
-        return getResources().getString(resId, formatArgs);
-    }
-
-    /**
-     * Return the FragmentManager for interacting with fragments associated
-     * with this fragment's activity.  Note that this will be non-null slightly
-     * before {@link #getActivity()}, during the time from when the fragment is
-     * placed in a {@link FragmentTransaction} until it is committed and
-     * attached to its activity.
-     *
-     * <p>If this Fragment is a child of another Fragment, the FragmentManager
-     * returned here will be the parent's {@link #getChildFragmentManager()}.
-     */
-    final public FragmentManager getFragmentManager() {
-        return mFragmentManager;
-    }
-
-    /**
-     * Return a private FragmentManager for placing and managing Fragments
-     * inside of this Fragment.
-     */
-    @NonNull
-    final public FragmentManager getChildFragmentManager() {
-        if (mChildFragmentManager == null) {
-            instantiateChildFragmentManager();
-            if (mState >= RESUMED) {
-                mChildFragmentManager.dispatchResume();
-            } else if (mState >= STARTED) {
-                mChildFragmentManager.dispatchStart();
-            } else if (mState >= ACTIVITY_CREATED) {
-                mChildFragmentManager.dispatchActivityCreated();
-            } else if (mState >= CREATED) {
-                mChildFragmentManager.dispatchCreate();
-            }
-        }
-        return mChildFragmentManager;
-    }
-
-    /**
-     * Return this fragment's child FragmentManager one has been previously created,
-     * otherwise null.
-     */
-    @Nullable
-    FragmentManager peekChildFragmentManager() {
-        return mChildFragmentManager;
-    }
-
-    /**
-     * Returns the parent Fragment containing this Fragment.  If this Fragment
-     * is attached directly to an Activity, returns null.
-     */
-    @Nullable
-    final public Fragment getParentFragment() {
-        return mParentFragment;
-    }
-
-    /**
-     * Return true if the fragment is currently added to its activity.
-     */
-    final public boolean isAdded() {
-        return mHost != null && mAdded;
-    }
-
-    /**
-     * Return true if the fragment has been explicitly detached from the UI.
-     * That is, {@link FragmentTransaction#detach(Fragment)
-     * FragmentTransaction.detach(Fragment)} has been used on it.
-     */
-    final public boolean isDetached() {
-        return mDetached;
-    }
-
-    /**
-     * Return true if this fragment is currently being removed from its
-     * activity.  This is  <em>not</em> whether its activity is finishing, but
-     * rather whether it is in the process of being removed from its activity.
-     */
-    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
-     * fragments are created through the &lt;fragment&gt; tag, <em>except</em>
-     * in the case where an old fragment is restored from a previous state and
-     * it does not appear in the layout of the current state.
-     */
-    final public boolean isInLayout() {
-        return mInLayout;
-    }
-
-    /**
-     * Return true if the fragment is in the resumed state.  This is true
-     * for the duration of {@link #onResume()} and {@link #onPause()} as well.
-     */
-    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
-     * (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
-     * {@link #onHiddenChanged}.  Note that the hidden state is orthogonal
-     * to other states -- that is, to be visible to the user, a fragment
-     * must be both started and not hidden.
-     */
-    final public boolean isHidden() {
-        return mHidden;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    final public boolean hasOptionsMenu() {
-        return mHasMenu;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    final public boolean isMenuVisible() {
-        return mMenuVisible;
-    }
-
-    /**
-     * Called when the hidden state (as returned by {@link #isHidden()} of
-     * the fragment has changed.  Fragments start out not hidden; this will
-     * be called whenever the fragment changes state from that.
-     * @param hidden True if the fragment is now hidden, false otherwise.
-     */
-    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
-     * be used with fragments not in the back stack.  If set, the fragment
-     * lifecycle will be slightly different when an activity is recreated:
-     * <ul>
-     * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
-     * will be, because the fragment is being detached from its current activity).
-     * <li> {@link #onCreate(Bundle)} will not be called since the fragment
-     * is not being re-created.
-     * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
-     * still be called.
-     * </ul>
-     */
-    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}
-     * and related methods.
-     *
-     * @param hasMenu If true, the fragment has menu items to contribute.
-     */
-    public void setHasOptionsMenu(boolean hasMenu) {
-        if (mHasMenu != hasMenu) {
-            mHasMenu = hasMenu;
-            if (isAdded() && !isHidden()) {
-                mHost.onSupportInvalidateOptionsMenu();
-            }
-        }
-    }
-
-    /**
-     * Set a hint for whether this fragment's menu should be visible.  This
-     * is useful if you know that a fragment has been placed in your view
-     * hierarchy so that the user can not currently seen it, so any menu items
-     * it has should also not be shown.
-     *
-     * @param menuVisible The default is true, meaning the fragment's menu will
-     * be shown as usual.  If false, the user will not see the menu.
-     */
-    public void setMenuVisibility(boolean menuVisible) {
-        if (mMenuVisible != menuVisible) {
-            mMenuVisible = menuVisible;
-            if (mHasMenu && isAdded() && !isHidden()) {
-                mHost.onSupportInvalidateOptionsMenu();
-            }
-        }
-    }
-
-    /**
-     * Set a hint to the system about whether this fragment's UI is currently visible
-     * to the user. This hint defaults to true and is persistent across fragment instance
-     * state save and restore.
-     *
-     * <p>An app may set this to false to indicate that the fragment's UI is
-     * scrolled out of visibility or is otherwise not directly visible to the user.
-     * This may be used by the system to prioritize operations such as fragment lifecycle updates
-     * or loader ordering behavior.</p>
-     *
-     * <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle.
-     * and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p>
-     *
-     * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
-     *                        false if it is not.
-     */
-    public void setUserVisibleHint(boolean isVisibleToUser) {
-        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
-                && mFragmentManager != null && isAdded()) {
-            mFragmentManager.performPendingDeferredStart(this);
-        }
-        mUserVisibleHint = isVisibleToUser;
-        mDeferStart = mState < STARTED && !isVisibleToUser;
-        if (mSavedFragmentState != null) {
-            // Ensure that if the user visible hint is set before the Fragment has
-            // restored its state that we don't lose the new value
-            mSavedFragmentState.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG,
-                    mUserVisibleHint);
-        }
-    }
-
-    /**
-     * @return The current value of the user-visible hint on this fragment.
-     * @see #setUserVisibleHint(boolean)
-     */
-    public boolean getUserVisibleHint() {
-        return mUserVisibleHint;
-    }
-
-    /**
-     * Return the LoaderManager for this fragment, creating it if needed.
-     */
-    public LoaderManager getLoaderManager() {
-        if (mLoaderManager != null) {
-            return mLoaderManager;
-        }
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
-        }
-        mCheckedForLoaderManager = true;
-        mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true);
-        return mLoaderManager;
-    }
-
-    /**
-     * Call {@link Activity#startActivity(Intent)} from the fragment's
-     * containing Activity.
-     */
-    public void startActivity(Intent intent) {
-        startActivity(intent, null);
-    }
-
-    /**
-     * Call {@link Activity#startActivity(Intent, Bundle)} from the fragment's
-     * containing Activity.
-     */
-    public void startActivity(Intent intent, @Nullable Bundle options) {
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
-        }
-        mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);
-    }
-
-    /**
-     * Call {@link Activity#startActivityForResult(Intent, int)} from the fragment's
-     * containing Activity.
-     */
-    public void startActivityForResult(Intent intent, int requestCode) {
-        startActivityForResult(intent, requestCode, null);
-    }
-
-    /**
-     * Call {@link Activity#startActivityForResult(Intent, int, Bundle)} from the fragment's
-     * containing Activity.
-     */
-    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
-        }
-        mHost.onStartActivityFromFragment(this /*fragment*/, intent, requestCode, options);
-    }
-
-    /**
-     * Call {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int,
-     * Bundle)} from the fragment's containing Activity.
-     */
-    public void startIntentSenderForResult(IntentSender intent, int requestCode,
-            @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
-            Bundle options) throws IntentSender.SendIntentException {
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
-        }
-        mHost.onStartIntentSenderFromFragment(this, intent, requestCode, fillInIntent, flagsMask,
-                flagsValues, extraFlags, options);
-    }
-
-    /**
-     * Receive the result from a previous call to
-     * {@link #startActivityForResult(Intent, int)}.  This follows the
-     * related Activity API as described there in
-     * {@link Activity#onActivityResult(int, int, Intent)}.
-     *
-     * @param requestCode The integer request code originally supplied to
-     *                    startActivityForResult(), allowing you to identify who this
-     *                    result came from.
-     * @param resultCode The integer result code returned by the child activity
-     *                   through its setResult().
-     * @param data An Intent, which can return result data to the caller
-     *               (various data can be attached to Intent "extras").
-     */
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-    }
-
-    /**
-     * Requests permissions to be granted to this application. These permissions
-     * must be requested in your manifest, they should not be granted to your app,
-     * and they should have protection level {@link android.content.pm.PermissionInfo
-     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
-     * the platform or a third-party app.
-     * <p>
-     * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
-     * are granted at install time if requested in the manifest. Signature permissions
-     * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
-     * install time if requested in the manifest and the signature of your app matches
-     * the signature of the app declaring the permissions.
-     * </p>
-     * <p>
-     * If your app does not have the requested permissions the user will be presented
-     * with UI for accepting them. After the user has accepted or rejected the
-     * requested permissions you will receive a callback on {@link
-     * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
-     * permissions were granted or not.
-     * </p>
-     * <p>
-     * Note that requesting a permission does not guarantee it will be granted and
-     * your app should be able to run without having this permission.
-     * </p>
-     * <p>
-     * This method may start an activity allowing the user to choose which permissions
-     * to grant and which to reject. Hence, you should be prepared that your activity
-     * may be paused and resumed. Further, granting some permissions may require
-     * a restart of you application. In such a case, the system will recreate the
-     * activity stack before delivering the result to {@link
-     * #onRequestPermissionsResult(int, String[], int[])}.
-     * </p>
-     * <p>
-     * When checking whether you have a permission you should use {@link
-     * android.content.Context#checkSelfPermission(String)}.
-     * </p>
-     * <p>
-     * Calling this API for permissions already granted to your app would show UI
-     * to the user to decided whether the app can still hold these permissions. This
-     * can be useful if the way your app uses the data guarded by the permissions
-     * changes significantly.
-     * </p>
-     * <p>
-     * A sample permissions request looks like this:
-     * </p>
-     * <code><pre><p>
-     * private void showContacts() {
-     *     if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
-     *             != PackageManager.PERMISSION_GRANTED) {
-     *         requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
-     *                 PERMISSIONS_REQUEST_READ_CONTACTS);
-     *     } else {
-     *         doShowContacts();
-     *     }
-     * }
-     *
-     * {@literal @}Override
-     * public void onRequestPermissionsResult(int requestCode, String[] permissions,
-     *         int[] grantResults) {
-     *     if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
-     *             && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-     *         doShowContacts();
-     *     }
-     * }
-     * </code></pre></p>
-     *
-     * @param permissions The requested permissions.
-     * @param requestCode Application specific request code to match with a result
-     *    reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
-     *
-     * @see #onRequestPermissionsResult(int, String[], int[])
-     * @see android.content.Context#checkSelfPermission(String)
-     */
-    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
-        }
-        mHost.onRequestPermissionsFromFragment(this, permissions, requestCode);
-    }
-
-    /**
-     * Callback for the result from requesting permissions. This method
-     * is invoked for every call on {@link #requestPermissions(String[], int)}.
-     * <p>
-     * <strong>Note:</strong> It is possible that the permissions request interaction
-     * with the user is interrupted. In this case you will receive empty permissions
-     * and results arrays which should be treated as a cancellation.
-     * </p>
-     *
-     * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
-     * @param permissions The requested permissions. Never null.
-     * @param grantResults The grant results for the corresponding permissions
-     *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
-     *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
-     *
-     * @see #requestPermissions(String[], int)
-     */
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
-            @NonNull int[] grantResults) {
-        /* callback - do nothing */
-    }
-
-    /**
-     * Gets whether you should show UI with rationale for requesting a permission.
-     * You should do this only if you do not have the permission and the context in
-     * which the permission is requested does not clearly communicate to the user
-     * what would be the benefit from granting this permission.
-     * <p>
-     * For example, if you write a camera app, requesting the camera permission
-     * would be expected by the user and no rationale for why it is requested is
-     * needed. If however, the app needs location for tagging photos then a non-tech
-     * savvy user may wonder how location is related to taking photos. In this case
-     * you may choose to show UI with rationale of requesting this permission.
-     * </p>
-     *
-     * @param permission A permission your app wants to request.
-     * @return Whether you can show permission rationale UI.
-     *
-     * @see Context#checkSelfPermission(String)
-     * @see #requestPermissions(String[], int)
-     * @see #onRequestPermissionsResult(int, String[], int[])
-     */
-    public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
-        if (mHost != null) {
-            return mHost.onShouldShowRequestPermissionRationale(permission);
-        }
-        return false;
-    }
-
-    /**
-     * Returns the LayoutInflater used to inflate Views of this Fragment. The default
-     * implementation will throw an exception if the Fragment is not attached.
-     *
-     * @param savedInstanceState If the fragment is being re-created from
-     * a previous saved state, this is the state.
-     * @return The LayoutInflater used to inflate Views of this Fragment.
-     */
-    @NonNull
-    public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
-        // TODO: move the implementation in getLayoutInflater to here
-        return getLayoutInflater(savedInstanceState);
-    }
-
-    /**
-     * Returns the cached LayoutInflater used to inflate Views of this Fragment. If
-     * {@link #onGetLayoutInflater(Bundle)} has not been called {@link #onGetLayoutInflater(Bundle)}
-     * will be called with a {@code null} argument and that value will be cached.
-     * <p>
-     * The cached LayoutInflater will be replaced immediately prior to
-     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} and cleared immediately after
-     * {@link #onDetach()}.
-     *
-     * @return The LayoutInflater used to inflate Views of this Fragment.
-     */
-    public final LayoutInflater getLayoutInflater() {
-        if (mLayoutInflater == null) {
-            return performGetLayoutInflater(null);
-        }
-        return mLayoutInflater;
-    }
-
-    /**
-     * Calls {@link #onGetLayoutInflater(Bundle)} and caches the result for use by
-     * {@link #getLayoutInflater()}.
-     *
-     * @param savedInstanceState If the fragment is being re-created from
-     * a previous saved state, this is the state.
-     * @return The LayoutInflater used to inflate Views of this Fragment.
-     */
-    @NonNull
-    LayoutInflater performGetLayoutInflater(@Nullable Bundle savedInstanceState) {
-        LayoutInflater layoutInflater = onGetLayoutInflater(savedInstanceState);
-        mLayoutInflater = layoutInflater;
-        return mLayoutInflater;
-    }
-
-    /**
-     * Override {@link #onGetLayoutInflater(Bundle)} when you need to change the
-     * LayoutInflater or call {@link #getLayoutInflater()} when you want to
-     * retrieve the current LayoutInflater.
-     *
-     * @hide
-     * @deprecated Override {@link #onGetLayoutInflater(Bundle)} or call
-     * {@link #getLayoutInflater()} instead of this method.
-     */
-    @Deprecated
-    @NonNull
-    @RestrictTo(LIBRARY_GROUP)
-    public LayoutInflater getLayoutInflater(@Nullable Bundle savedFragmentState) {
-        if (mHost == null) {
-            throw new IllegalStateException("onGetLayoutInflater() cannot be executed until the "
-                    + "Fragment is attached to the FragmentManager.");
-        }
-        LayoutInflater result = mHost.onGetLayoutInflater();
-        getChildFragmentManager(); // Init if needed; use raw implementation below.
-        LayoutInflaterCompat.setFactory2(result, mChildFragmentManager.getLayoutInflaterFactory());
-        return result;
-    }
-
-    /**
-     * Called when a fragment is being created as part of a view layout
-     * inflation, typically from setting the content view of an activity.  This
-     * may be called immediately after the fragment is created from a <fragment>
-     * 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
-     * different configurations.</p>
-     *
-     * <p>Here is a typical implementation of a fragment that can take parameters
-     * both through attributes supplied here as well from {@link #getArguments()}:</p>
-     *
-     * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentArgumentsSupport.java
-     *      fragment}
-     *
-     * <p>Note that parsing the XML attributes uses a "styleable" resource.  The
-     * declaration for the styleable used here is:</p>
-     *
-     * {@sample frameworks/support/samples/Support4Demos/src/main/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>
-     *
-     * {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_arguments_support.xml from_attributes}
-     *
-     * <p>This fragment can also be created dynamically from arguments given
-     * at runtime in the arguments Bundle; here is an example of doing so at
-     * creation of the containing activity:</p>
-     *
-     * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentArgumentsSupport.java
-     *      create}
-     *
-     * @param context The Activity that is inflating this fragment.
-     * @param attrs The attributes at the tag where the fragment is
-     * being created.
-     * @param savedInstanceState If the fragment is being re-created from
-     * a previous saved state, this is the state.
-     */
-    @CallSuper
-    public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
-        mCalled = true;
-        final Activity hostActivity = mHost == null ? null : mHost.getActivity();
-        if (hostActivity != null) {
-            mCalled = false;
-            onInflate(hostActivity, attrs, savedInstanceState);
-        }
-    }
-
-    /**
-     * Called when a fragment is being created as part of a view layout
-     * inflation, typically from setting the content view of an activity.
-     *
-     * @deprecated See {@link #onInflate(Context, AttributeSet, Bundle)}.
-     */
-    @Deprecated
-    @CallSuper
-    public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
-        mCalled = true;
-    }
-
-    /**
-     * Called when a fragment is attached as a child of this fragment.
-     *
-     * <p>This is called after the attached fragment's <code>onAttach</code> and before
-     * the attached fragment's <code>onCreate</code> if the fragment has not yet had a previous
-     * call to <code>onCreate</code>.</p>
-     *
-     * @param childFragment child fragment being attached
-     */
-    public void onAttachFragment(Fragment childFragment) {
-    }
-
-    /**
-     * Called when a fragment is first attached to its context.
-     * {@link #onCreate(Bundle)} will be called after this.
-     */
-    @CallSuper
-    public void onAttach(Context context) {
-        mCalled = true;
-        final Activity hostActivity = mHost == null ? null : mHost.getActivity();
-        if (hostActivity != null) {
-            mCalled = false;
-            onAttach(hostActivity);
-        }
-    }
-
-    /**
-     * Called when a fragment is first attached to its activity.
-     * {@link #onCreate(Bundle)} will be called after this.
-     *
-     * @deprecated See {@link #onAttach(Context)}.
-     */
-    @Deprecated
-    @CallSuper
-    public void onAttach(Activity activity) {
-        mCalled = true;
-    }
-
-    /**
-     * 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
-     * at this point.  If you want to do work once the activity itself is
-     * created, see {@link #onActivityCreated(Bundle)}.
-     *
-     * <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.
-     */
-    @CallSuper
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        mCalled = true;
-        restoreChildFragmentState(savedInstanceState);
-        if (mChildFragmentManager != null
-                && !mChildFragmentManager.isStateAtLeast(Fragment.CREATED)) {
-            mChildFragmentManager.dispatchCreate();
-        }
-    }
-
-    /**
-     * Restore the state of the child FragmentManager. Called by either
-     * {@link #onCreate(Bundle)} for non-retained instance fragments or by
-     * {@link FragmentManagerImpl#moveToState(Fragment, int, int, int, boolean)}
-     * for retained instance fragments.
-     *
-     * <p><strong>Postcondition:</strong> if there were child fragments to restore,
-     * the child FragmentManager will be instantiated and brought to the {@link #CREATED} state.
-     * </p>
-     *
-     * @param savedInstanceState the savedInstanceState potentially containing fragment info
-     */
-    void restoreChildFragmentState(@Nullable Bundle savedInstanceState) {
-        if (savedInstanceState != null) {
-            Parcelable p = savedInstanceState.getParcelable(
-                    FragmentActivity.FRAGMENTS_TAG);
-            if (p != null) {
-                if (mChildFragmentManager == null) {
-                    instantiateChildFragmentManager();
-                }
-                mChildFragmentManager.restoreAllState(p, mChildNonConfig);
-                mChildNonConfig = null;
-                mChildFragmentManager.dispatchCreate();
-            }
-        }
-    }
-
-    /**
-     * Called to have the fragment instantiate its user interface view.
-     * 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
-     * UI should be attached to.  The fragment should not add the view itself,
-     * 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
-    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
-        return null;
-    }
-
-    /**
-     * Called immediately after {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
-     * has returned, but before any saved state has been restored in to the view.
-     * This gives subclasses a chance to initialize themselves once
-     * they know their view hierarchy has been completely created.  The fragment's
-     * view hierarchy is not however attached to its parent at this point.
-     * @param view The View returned by {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
-     * @param savedInstanceState If non-null, this fragment is being re-constructed
-     * from a previous saved state as given here.
-     */
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-    }
-
-    /**
-     * 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
-     * initialization once these pieces are in place, such as retrieving
-     * views or restoring state.  It is also useful for fragments that use
-     * {@link #setRetainInstance(boolean)} to retain their instance,
-     * as this callback tells the fragment when it is fully associated with
-     * the new activity instance.  This is called after {@link #onCreateView}
-     * and before {@link #onViewStateRestored(Bundle)}.
-     *
-     * @param savedInstanceState If the fragment is being re-created from
-     * a previous saved state, this is the state.
-     */
-    @CallSuper
-    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
-        mCalled = true;
-    }
-
-    /**
-     * Called when all saved state has been restored into the view hierarchy
-     * of the fragment.  This can be used to do initialization based on saved
-     * state that you are letting the view hierarchy track itself, such as
-     * whether check box widgets are currently checked.  This is called
-     * after {@link #onActivityCreated(Bundle)} and before
-     * {@link #onStart()}.
-     *
-     * @param savedInstanceState If the fragment is being re-created from
-     * a previous saved state, this is the state.
-     */
-    @CallSuper
-    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
-        mCalled = true;
-    }
-
-    /**
-     * Called when the Fragment is visible to the user.  This is generally
-     * tied to {@link Activity#onStart() Activity.onStart} of the containing
-     * Activity's lifecycle.
-     */
-    @CallSuper
-    public void onStart() {
-        mCalled = true;
-
-        if (!mLoadersStarted) {
-            mLoadersStarted = true;
-            if (!mCheckedForLoaderManager) {
-                mCheckedForLoaderManager = true;
-                mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
-            } else if (mLoaderManager != null) {
-                mLoaderManager.doStart();
-            }
-        }
-    }
-
-    /**
-     * Called when the fragment is visible to the user and actively running.
-     * This is generally
-     * tied to {@link Activity#onResume() Activity.onResume} of the containing
-     * Activity's lifecycle.
-     */
-    @CallSuper
-    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
-     * restarted.  If a new instance of the fragment later needs to be
-     * created, the data you place in the Bundle here will be available
-     * in the Bundle given to {@link #onCreate(Bundle)},
-     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, and
-     * {@link #onActivityCreated(Bundle)}.
-     *
-     * <p>This corresponds to {@link Activity#onSaveInstanceState(Bundle)
-     * Activity.onSaveInstanceState(Bundle)} and most of the discussion there
-     * applies here as well.  Note however: <em>this method may be called
-     * at any time before {@link #onDestroy()}</em>.  There are many situations
-     * where a fragment may be mostly torn down (such as when placed on the
-     * back stack with no UI showing), but its state will not be saved until
-     * its owning activity actually needs to save its state.
-     *
-     * @param outState Bundle in which to place your saved state.
-     */
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-    }
-
-    /**
-     * Called when the Fragment's activity changes from fullscreen mode to multi-window mode and
-     * visa-versa. This is generally tied to {@link Activity#onMultiWindowModeChanged} of the
-     * containing Activity.
-     *
-     * @param isInMultiWindowMode True if the activity is in multi-window mode.
-     */
-    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
-    }
-
-    /**
-     * Called by the system when the activity changes to and from picture-in-picture mode. This is
-     * generally tied to {@link Activity#onPictureInPictureModeChanged} of the containing Activity.
-     *
-     * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode.
-     */
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-    }
-
-    @Override
-    @CallSuper
-    public void onConfigurationChanged(Configuration newConfig) {
-        mCalled = true;
-    }
-
-    /**
-     * Called when the Fragment is no longer resumed.  This is generally
-     * tied to {@link Activity#onPause() Activity.onPause} of the containing
-     * Activity's lifecycle.
-     */
-    @CallSuper
-    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
-     * Activity's lifecycle.
-     */
-    @CallSuper
-    public void onStop() {
-        mCalled = true;
-    }
-
-    @Override
-    @CallSuper
-    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
-     * to be displayed, a new view will be created.  This is called
-     * after {@link #onStop()} and before {@link #onDestroy()}.  It is called
-     * <em>regardless</em> of whether {@link #onCreateView} returned a
-     * non-null view.  Internally it is called after the view's state has
-     * been saved but before it has been removed from its parent.
-     */
-    @CallSuper
-    public void onDestroyView() {
-        mCalled = true;
-    }
-
-    /**
-     * Called when the fragment is no longer in use.  This is called
-     * after {@link #onStop()} and before {@link #onDetach()}.
-     */
-    @CallSuper
-    public void onDestroy() {
-        mCalled = true;
-        //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager
-        //        + " mLoaderManager=" + mLoaderManager);
-        if (!mCheckedForLoaderManager) {
-            mCheckedForLoaderManager = true;
-            mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
-        }
-        if (mLoaderManager != null) {
-            mLoaderManager.doDestroy();
-        }
-    }
-
-    /**
-     * Called by the fragment manager once this fragment has been removed,
-     * so that we don't have any left-over state if the application decides
-     * to re-use the instance.  This only clears state that the framework
-     * internally manages, not things the application sets.
-     */
-    void initState() {
-        mIndex = -1;
-        mWho = null;
-        mAdded = false;
-        mRemoving = false;
-        mFromLayout = false;
-        mInLayout = false;
-        mRestored = false;
-        mBackStackNesting = 0;
-        mFragmentManager = null;
-        mChildFragmentManager = null;
-        mHost = null;
-        mFragmentId = 0;
-        mContainerId = 0;
-        mTag = null;
-        mHidden = false;
-        mDetached = false;
-        mRetaining = false;
-        mLoaderManager = null;
-        mLoadersStarted = false;
-        mCheckedForLoaderManager = false;
-    }
-
-    /**
-     * Called when the fragment is no longer attached to its activity.  This
-     * is called after {@link #onDestroy()}.
-     */
-    @CallSuper
-    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
-     */
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-    }
-
-    /**
-     * Prepare the Fragment host's standard options menu to be displayed.  This is
-     * called right before the menu is shown, every time it is shown.  You can
-     * use this method to efficiently enable/disable items or otherwise
-     * 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
-     */
-    public void onPrepareOptionsMenu(Menu menu) {
-    }
-
-    /**
-     * Called when this fragment's option menu items are no longer being
-     * included in the overall options menu.  Receiving this call means that
-     * the menu needed to be rebuilt, but this fragment's items were not
-     * included in the newly built menu (its {@link #onCreateOptionsMenu(Menu, MenuInflater)}
-     * was not called).
-     */
-    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
-     * processing happen (calling the item's Runnable or sending a message to
-     * 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) {
-        return false;
-    }
-
-    /**
-     * 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
-     * time the context menu is about to be shown and should be populated for
-     * the view (or item inside the view for {@link AdapterView} subclasses,
-     * this can be found in the {@code menuInfo})).
-     * <p>
-     * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an
-     * item has been selected.
-     * <p>
-     * The default implementation calls up to
-     * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though
-     * you can not call this implementation if you don't want that behavior.
-     * <p>
-     * It is not safe to hold onto the context menu after this method returns.
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-        getActivity().onCreateContextMenu(menu, v, menuInfo);
-    }
-
-    /**
-     * Registers a context menu to be shown for the given view (multiple views
-     * can show the context menu). This method will set the
-     * {@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
-     * happen (calling the item's Runnable or sending a message to 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>
-     * Use {@link MenuItem#getMenuInfo()} to get extra information set by the
-     * View that added this menu item.
-     * <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.
-     */
-    public boolean onContextItemSelected(MenuItem item) {
-        return false;
-    }
-
-    /**
-     * When custom transitions are used with Fragments, the enter transition callback
-     * is called when this Fragment is attached or detached when not popping the back stack.
-     *
-     * @param callback Used to manipulate the shared element transitions on this Fragment
-     *                 when added not as a pop from the back stack.
-     */
-    public void setEnterSharedElementCallback(SharedElementCallback callback) {
-        ensureAnimationInfo().mEnterTransitionCallback = callback;
-    }
-
-    /**
-     * When custom transitions are used with Fragments, the exit transition callback
-     * is called when this Fragment is attached or detached when popping the back stack.
-     *
-     * @param callback Used to manipulate the shared element transitions on this Fragment
-     *                 when added as a pop from the back stack.
-     */
-    public void setExitSharedElementCallback(SharedElementCallback callback) {
-        ensureAnimationInfo().mExitTransitionCallback = callback;
-    }
-
-    /**
-     * Sets the Transition that will be used to move Views into the initial scene. The entering
-     * Views will be those that are regular Views or ViewGroups that have
-     * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
-     * {@link android.transition.Visibility} as entering is governed by changing visibility from
-     * {@link View#INVISIBLE} to {@link View#VISIBLE}. If <code>transition</code> is null,
-     * entering Views will remain unaffected.
-     *
-     * @param transition The Transition to use to move Views into the initial Scene.
-     */
-    public void setEnterTransition(@Nullable Object transition) {
-        ensureAnimationInfo().mEnterTransition = transition;
-    }
-
-    /**
-     * Returns the Transition that will be used to move Views into the initial scene. The entering
-     * Views will be those that are regular Views or ViewGroups that have
-     * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
-     * {@link android.transition.Visibility} as entering is governed by changing visibility from
-     * {@link View#INVISIBLE} to {@link View#VISIBLE}.
-     *
-     * @return the Transition to use to move Views into the initial Scene.
-     */
-    @Nullable
-    public Object getEnterTransition() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mEnterTransition;
-    }
-
-    /**
-     * Sets the Transition that will be used to move Views out of the scene when the Fragment is
-     * preparing to be removed, hidden, or detached because of popping the back stack. The exiting
-     * Views will be those that are regular Views or ViewGroups that have
-     * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
-     * {@link android.transition.Visibility} as entering is governed by changing visibility from
-     * {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null,
-     * entering Views will remain unaffected. If nothing is set, the default will be to
-     * use the same value as set in {@link #setEnterTransition(Object)}.
-     *
-     * @param transition The Transition to use to move Views out of the Scene when the Fragment
-     *         is preparing to close. <code>transition</code> must be an
-     *         {@link android.transition.Transition android.transition.Transition} or
-     *         {@link android.support.transition.Transition android.support.transition.Transition}.
-     */
-    public void setReturnTransition(@Nullable Object transition) {
-        ensureAnimationInfo().mReturnTransition = transition;
-    }
-
-    /**
-     * Returns the Transition that will be used to move Views out of the scene when the Fragment is
-     * preparing to be removed, hidden, or detached because of popping the back stack. The exiting
-     * Views will be those that are regular Views or ViewGroups that have
-     * {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
-     * {@link android.transition.Visibility} as entering is governed by changing visibility from
-     * {@link View#VISIBLE} to {@link View#INVISIBLE}. If <code>transition</code> is null,
-     * entering Views will remain unaffected.
-     *
-     * @return the Transition to use to move Views out of the Scene when the Fragment
-     *         is preparing to close.
-     */
-    @Nullable
-    public Object getReturnTransition() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mReturnTransition == USE_DEFAULT_TRANSITION ? getEnterTransition()
-                : mAnimationInfo.mReturnTransition;
-    }
-
-    /**
-     * Sets the Transition that will be used to move Views out of the scene when the
-     * fragment is removed, hidden, or detached when not popping the back stack.
-     * The exiting Views will be those that are regular Views or ViewGroups that
-     * have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
-     * {@link android.transition.Visibility} as exiting is governed by changing visibility
-     * from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will
-     * remain unaffected.
-     *
-     * @param transition The Transition to use to move Views out of the Scene when the Fragment
-     *          is being closed not due to popping the back stack. <code>transition</code>
-     *          must be an
-     *          {@link android.transition.Transition android.transition.Transition} or
-     *          {@link android.support.transition.Transition android.support.transition.Transition}.
-     */
-    public void setExitTransition(@Nullable Object transition) {
-        ensureAnimationInfo().mExitTransition = transition;
-    }
-
-    /**
-     * Returns the Transition that will be used to move Views out of the scene when the
-     * fragment is removed, hidden, or detached when not popping the back stack.
-     * The exiting Views will be those that are regular Views or ViewGroups that
-     * have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions will extend
-     * {@link android.transition.Visibility} as exiting is governed by changing visibility
-     * from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null, the views will
-     * remain unaffected.
-     *
-     * @return the Transition to use to move Views out of the Scene when the Fragment
-     *         is being closed not due to popping the back stack.
-     */
-    @Nullable
-    public Object getExitTransition() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mExitTransition;
-    }
-
-    /**
-     * Sets the Transition that will be used to move Views in to the scene when returning due
-     * to popping a back stack. The entering Views will be those that are regular Views
-     * or ViewGroups that have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions
-     * will extend {@link android.transition.Visibility} as exiting is governed by changing
-     * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null,
-     * the views will remain unaffected. If nothing is set, the default will be to use the same
-     * transition as {@link #setExitTransition(Object)}.
-     *
-     * @param transition The Transition to use to move Views into the scene when reentering from a
-     *          previously-started Activity. <code>transition</code>
-     *          must be an
-     *          {@link android.transition.Transition android.transition.Transition} or
-     *          {@link android.support.transition.Transition android.support.transition.Transition}.
-     */
-    public void setReenterTransition(@Nullable Object transition) {
-        ensureAnimationInfo().mReenterTransition = transition;
-    }
-
-    /**
-     * Returns the Transition that will be used to move Views in to the scene when returning due
-     * to popping a back stack. The entering Views will be those that are regular Views
-     * or ViewGroups that have {@link ViewGroup#isTransitionGroup} return true. Typical Transitions
-     * will extend {@link android.transition.Visibility} as exiting is governed by changing
-     * visibility from {@link View#VISIBLE} to {@link View#INVISIBLE}. If transition is null,
-     * the views will remain unaffected. If nothing is set, the default will be to use the same
-     * transition as {@link #setExitTransition(Object)}.
-     *
-     * @return the Transition to use to move Views into the scene when reentering from a
-     *                   previously-started Activity.
-     */
-    public Object getReenterTransition() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mReenterTransition == USE_DEFAULT_TRANSITION ? getExitTransition()
-                : mAnimationInfo.mReenterTransition;
-    }
-
-    /**
-     * Sets the Transition that will be used for shared elements transferred into the content
-     * Scene. Typical Transitions will affect size and location, such as
-     * {@link android.transition.ChangeBounds}. A null
-     * value will cause transferred shared elements to blink to the final position.
-     *
-     * @param transition The Transition to use for shared elements transferred into the content
-     *          Scene.  <code>transition</code> must be an
-     *          {@link android.transition.Transition android.transition.Transition} or
-     *          {@link android.support.transition.Transition android.support.transition.Transition}.
-     */
-    public void setSharedElementEnterTransition(@Nullable Object transition) {
-        ensureAnimationInfo().mSharedElementEnterTransition = transition;
-    }
-
-    /**
-     * Returns the Transition that will be used for shared elements transferred into the content
-     * Scene. Typical Transitions will affect size and location, such as
-     * {@link android.transition.ChangeBounds}. A null
-     * value will cause transferred shared elements to blink to the final position.
-     *
-     * @return The Transition to use for shared elements transferred into the content
-     *                   Scene.
-     */
-    @Nullable
-    public Object getSharedElementEnterTransition() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mSharedElementEnterTransition;
-    }
-
-    /**
-     * Sets the Transition that will be used for shared elements transferred back during a
-     * pop of the back stack. This Transition acts in the leaving Fragment.
-     * Typical Transitions will affect size and location, such as
-     * {@link android.transition.ChangeBounds}. A null
-     * value will cause transferred shared elements to blink to the final position.
-     * If no value is set, the default will be to use the same value as
-     * {@link #setSharedElementEnterTransition(Object)}.
-     *
-     * @param transition The Transition to use for shared elements transferred out of the content
-     *          Scene. <code>transition</code> must be an
-     *          {@link android.transition.Transition android.transition.Transition} or
-     *          {@link android.support.transition.Transition android.support.transition.Transition}.
-     */
-    public void setSharedElementReturnTransition(@Nullable Object transition) {
-        ensureAnimationInfo().mSharedElementReturnTransition = transition;
-    }
-
-    /**
-     * Return the Transition that will be used for shared elements transferred back during a
-     * pop of the back stack. This Transition acts in the leaving Fragment.
-     * Typical Transitions will affect size and location, such as
-     * {@link android.transition.ChangeBounds}. A null
-     * value will cause transferred shared elements to blink to the final position.
-     * If no value is set, the default will be to use the same value as
-     * {@link #setSharedElementEnterTransition(Object)}.
-     *
-     * @return The Transition to use for shared elements transferred out of the content
-     *                   Scene.
-     */
-    @Nullable
-    public Object getSharedElementReturnTransition() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mSharedElementReturnTransition == USE_DEFAULT_TRANSITION
-                ? getSharedElementEnterTransition()
-                : mAnimationInfo.mSharedElementReturnTransition;
-    }
-
-    /**
-     * Sets whether the the exit transition and enter transition overlap or not.
-     * When true, the enter transition will start as soon as possible. When false, the
-     * enter transition will wait until the exit transition completes before starting.
-     *
-     * @param allow true to start the enter transition when possible or false to
-     *              wait until the exiting transition completes.
-     */
-    public void setAllowEnterTransitionOverlap(boolean allow) {
-        ensureAnimationInfo().mAllowEnterTransitionOverlap = allow;
-    }
-
-    /**
-     * Returns whether the the exit transition and enter transition overlap or not.
-     * When true, the enter transition will start as soon as possible. When false, the
-     * enter transition will wait until the exit transition completes before starting.
-     *
-     * @return true when the enter transition should start as soon as possible or false to
-     * when it should wait until the exiting transition completes.
-     */
-    public boolean getAllowEnterTransitionOverlap() {
-        return (mAnimationInfo == null || mAnimationInfo.mAllowEnterTransitionOverlap == null)
-                ? true : mAnimationInfo.mAllowEnterTransitionOverlap;
-    }
-
-    /**
-     * Sets whether the the return transition and reenter transition overlap or not.
-     * When true, the reenter transition will start as soon as possible. When false, the
-     * reenter transition will wait until the return transition completes before starting.
-     *
-     * @param allow true to start the reenter transition when possible or false to wait until the
-     *              return transition completes.
-     */
-    public void setAllowReturnTransitionOverlap(boolean allow) {
-        ensureAnimationInfo().mAllowReturnTransitionOverlap = allow;
-    }
-
-    /**
-     * Returns whether the the return transition and reenter transition overlap or not.
-     * When true, the reenter transition will start as soon as possible. When false, the
-     * reenter transition will wait until the return transition completes before starting.
-     *
-     * @return true to start the reenter transition when possible or false to wait until the
-     *         return transition completes.
-     */
-    public boolean getAllowReturnTransitionOverlap() {
-        return (mAnimationInfo == null || mAnimationInfo.mAllowReturnTransitionOverlap == null)
-                ? true : mAnimationInfo.mAllowReturnTransitionOverlap;
-    }
-
-    /**
-     * Postpone the entering Fragment transition until {@link #startPostponedEnterTransition()}
-     * or {@link FragmentManager#executePendingTransactions()} has been called.
-     * <p>
-     * This method gives the Fragment the ability to delay Fragment animations
-     * until all data is loaded. Until then, the added, shown, and
-     * attached Fragments will be INVISIBLE and removed, hidden, and detached Fragments won't
-     * be have their Views removed. The transaction runs when all postponed added Fragments in the
-     * transaction have called {@link #startPostponedEnterTransition()}.
-     * <p>
-     * This method should be called before being added to the FragmentTransaction or
-     * in {@link #onCreate(Bundle), {@link #onAttach(Context)}, or
-     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}}.
-     * {@link #startPostponedEnterTransition()} must be called to allow the Fragment to
-     * start the transitions.
-     * <p>
-     * When a FragmentTransaction is started that may affect a postponed FragmentTransaction,
-     * based on which containers are in their operations, the postponed FragmentTransaction
-     * will have its start triggered. The early triggering may result in faulty or nonexistent
-     * animations in the postponed transaction. FragmentTransactions that operate only on
-     * 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
-     * {@link FragmentTransaction#setReorderingAllowed(boolean) FragmentTransaction reordering} is
-     * enabled.
-     *
-     * @see Activity#postponeEnterTransition()
-     * @see FragmentTransaction#setReorderingAllowed(boolean)
-     */
-    public void postponeEnterTransition() {
-        ensureAnimationInfo().mEnterTransitionPostponed = true;
-    }
-
-    /**
-     * Begin postponed transitions after {@link #postponeEnterTransition()} was called.
-     * If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
-     * or {@link FragmentManager#executePendingTransactions()} to complete the FragmentTransaction.
-     * If postponement was interrupted with {@link FragmentManager#executePendingTransactions()},
-     * before {@code startPostponedEnterTransition()}, animations may not run or may execute
-     * improperly.
-     *
-     * @see Activity#startPostponedEnterTransition()
-     */
-    public void startPostponedEnterTransition() {
-        if (mFragmentManager == null || mFragmentManager.mHost == null) {
-            ensureAnimationInfo().mEnterTransitionPostponed = false;
-        } else if (Looper.myLooper() != mFragmentManager.mHost.getHandler().getLooper()) {
-            mFragmentManager.mHost.getHandler().postAtFrontOfQueue(new Runnable() {
-                @Override
-                public void run() {
-                    callStartTransitionListener();
-                }
-            });
-        } else {
-            callStartTransitionListener();
-        }
-    }
-
-    /**
-     * Calls the start transition listener. This must be called on the UI thread.
-     */
-    private void callStartTransitionListener() {
-        final OnStartEnterTransitionListener listener;
-        if (mAnimationInfo == null) {
-            listener = null;
-        } else {
-            mAnimationInfo.mEnterTransitionPostponed = false;
-            listener = mAnimationInfo.mStartEnterTransitionListener;
-            mAnimationInfo.mStartEnterTransitionListener = null;
-        }
-        if (listener != null) {
-            listener.onStartEnterTransition();
-        }
-    }
-
-    /**
-     * Print the Fragments's state into the given stream.
-     *
-     * @param prefix Text to print at the front of each line.
-     * @param fd The raw file descriptor that the dump is being sent to.
-     * @param writer The PrintWriter to which you should dump your state.  This will be
-     * closed for you after you return.
-     * @param args additional arguments to the dump request.
-     */
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        writer.print(prefix); writer.print("mFragmentId=#");
-                writer.print(Integer.toHexString(mFragmentId));
-                writer.print(" mContainerId=#");
-                writer.print(Integer.toHexString(mContainerId));
-                writer.print(" mTag="); writer.println(mTag);
-        writer.print(prefix); writer.print("mState="); writer.print(mState);
-                writer.print(" mIndex="); writer.print(mIndex);
-                writer.print(" mWho="); writer.print(mWho);
-                writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
-        writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
-                writer.print(" mRemoving="); writer.print(mRemoving);
-                writer.print(" mFromLayout="); writer.print(mFromLayout);
-                writer.print(" mInLayout="); writer.println(mInLayout);
-        writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
-                writer.print(" mDetached="); writer.print(mDetached);
-                writer.print(" mMenuVisible="); writer.print(mMenuVisible);
-                writer.print(" mHasMenu="); writer.println(mHasMenu);
-        writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance);
-                writer.print(" mRetaining="); writer.print(mRetaining);
-                writer.print(" mUserVisibleHint="); writer.println(mUserVisibleHint);
-        if (mFragmentManager != null) {
-            writer.print(prefix); writer.print("mFragmentManager=");
-                    writer.println(mFragmentManager);
-        }
-        if (mHost != null) {
-            writer.print(prefix); writer.print("mHost=");
-                    writer.println(mHost);
-        }
-        if (mParentFragment != null) {
-            writer.print(prefix); writer.print("mParentFragment=");
-                    writer.println(mParentFragment);
-        }
-        if (mArguments != null) {
-            writer.print(prefix); writer.print("mArguments="); writer.println(mArguments);
-        }
-        if (mSavedFragmentState != null) {
-            writer.print(prefix); writer.print("mSavedFragmentState=");
-                    writer.println(mSavedFragmentState);
-        }
-        if (mSavedViewState != null) {
-            writer.print(prefix); writer.print("mSavedViewState=");
-                    writer.println(mSavedViewState);
-        }
-        if (mTarget != null) {
-            writer.print(prefix); writer.print("mTarget="); writer.print(mTarget);
-                    writer.print(" mTargetRequestCode=");
-                    writer.println(mTargetRequestCode);
-        }
-        if (getNextAnim() != 0) {
-            writer.print(prefix); writer.print("mNextAnim="); writer.println(getNextAnim());
-        }
-        if (mContainer != null) {
-            writer.print(prefix); writer.print("mContainer="); writer.println(mContainer);
-        }
-        if (mView != null) {
-            writer.print(prefix); writer.print("mView="); writer.println(mView);
-        }
-        if (mInnerView != null) {
-            writer.print(prefix); writer.print("mInnerView="); writer.println(mView);
-        }
-        if (getAnimatingAway() != null) {
-            writer.print(prefix);
-            writer.print("mAnimatingAway=");
-            writer.println(getAnimatingAway());
-            writer.print(prefix);
-            writer.print("mStateAfterAnimating=");
-            writer.println(getStateAfterAnimating());
-        }
-        if (mLoaderManager != null) {
-            writer.print(prefix); writer.println("Loader Manager:");
-            mLoaderManager.dump(prefix + "  ", fd, writer, args);
-        }
-        if (mChildFragmentManager != null) {
-            writer.print(prefix); writer.println("Child " + mChildFragmentManager + ":");
-            mChildFragmentManager.dump(prefix + "  ", fd, writer, args);
-        }
-    }
-
-    Fragment findFragmentByWho(String who) {
-        if (who.equals(mWho)) {
-            return this;
-        }
-        if (mChildFragmentManager != null) {
-            return mChildFragmentManager.findFragmentByWho(who);
-        }
-        return null;
-    }
-
-    void instantiateChildFragmentManager() {
-        if (mHost == null) {
-            throw new IllegalStateException("Fragment has not been attached yet.");
-        }
-        mChildFragmentManager = new FragmentManagerImpl();
-        mChildFragmentManager.attachController(mHost, new FragmentContainer() {
-            @Override
-            @Nullable
-            public View onFindViewById(int id) {
-                if (mView == null) {
-                    throw new IllegalStateException("Fragment does not have a view");
-                }
-                return mView.findViewById(id);
-            }
-
-            @Override
-            public boolean onHasView() {
-                return (mView != null);
-            }
-
-            @Override
-            public Fragment instantiate(Context context, String className, Bundle arguments) {
-                return mHost.instantiate(context, className, arguments);
-            }
-        }, this);
-    }
-
-    void performCreate(Bundle savedInstanceState) {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.noteStateNotSaved();
-        }
-        mState = CREATED;
-        mCalled = false;
-        onCreate(savedInstanceState);
-        mIsCreated = true;
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onCreate()");
-        }
-        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
-    }
-
-    View performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
-            @Nullable Bundle savedInstanceState) {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.noteStateNotSaved();
-        }
-        mPerformedCreateView = true;
-        return onCreateView(inflater, container, savedInstanceState);
-    }
-
-    void performActivityCreated(Bundle savedInstanceState) {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.noteStateNotSaved();
-        }
-        mState = ACTIVITY_CREATED;
-        mCalled = false;
-        onActivityCreated(savedInstanceState);
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onActivityCreated()");
-        }
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchActivityCreated();
-        }
-    }
-
-    void performStart() {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.noteStateNotSaved();
-            mChildFragmentManager.execPendingActions();
-        }
-        mState = STARTED;
-        mCalled = false;
-        onStart();
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onStart()");
-        }
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchStart();
-        }
-        if (mLoaderManager != null) {
-            mLoaderManager.doReportStart();
-        }
-        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
-    }
-
-    void performResume() {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.noteStateNotSaved();
-            mChildFragmentManager.execPendingActions();
-        }
-        mState = RESUMED;
-        mCalled = false;
-        onResume();
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onResume()");
-        }
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchResume();
-            mChildFragmentManager.execPendingActions();
-        }
-        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
-    }
-
-    void noteStateNotSaved() {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.noteStateNotSaved();
-        }
-    }
-
-    void performMultiWindowModeChanged(boolean isInMultiWindowMode) {
-        onMultiWindowModeChanged(isInMultiWindowMode);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode);
-        }
-    }
-
-    void performPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        onPictureInPictureModeChanged(isInPictureInPictureMode);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
-        }
-    }
-
-    void performConfigurationChanged(Configuration newConfig) {
-        onConfigurationChanged(newConfig);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchConfigurationChanged(newConfig);
-        }
-    }
-
-    void performLowMemory() {
-        onLowMemory();
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchLowMemory();
-        }
-    }
-
-    /*
-    void performTrimMemory(int level) {
-        onTrimMemory(level);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchTrimMemory(level);
-        }
-    }
-    */
-
-    boolean performCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        boolean show = false;
-        if (!mHidden) {
-            if (mHasMenu && mMenuVisible) {
-                show = true;
-                onCreateOptionsMenu(menu, inflater);
-            }
-            if (mChildFragmentManager != null) {
-                show |= mChildFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
-            }
-        }
-        return show;
-    }
-
-    boolean performPrepareOptionsMenu(Menu menu) {
-        boolean show = false;
-        if (!mHidden) {
-            if (mHasMenu && mMenuVisible) {
-                show = true;
-                onPrepareOptionsMenu(menu);
-            }
-            if (mChildFragmentManager != null) {
-                show |= mChildFragmentManager.dispatchPrepareOptionsMenu(menu);
-            }
-        }
-        return show;
-    }
-
-    boolean performOptionsItemSelected(MenuItem item) {
-        if (!mHidden) {
-            if (mHasMenu && mMenuVisible) {
-                if (onOptionsItemSelected(item)) {
-                    return true;
-                }
-            }
-            if (mChildFragmentManager != null) {
-                if (mChildFragmentManager.dispatchOptionsItemSelected(item)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean performContextItemSelected(MenuItem item) {
-        if (!mHidden) {
-            if (onContextItemSelected(item)) {
-                return true;
-            }
-            if (mChildFragmentManager != null) {
-                if (mChildFragmentManager.dispatchContextItemSelected(item)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    void performOptionsMenuClosed(Menu menu) {
-        if (!mHidden) {
-            if (mHasMenu && mMenuVisible) {
-                onOptionsMenuClosed(menu);
-            }
-            if (mChildFragmentManager != null) {
-                mChildFragmentManager.dispatchOptionsMenuClosed(menu);
-            }
-        }
-    }
-
-    void performSaveInstanceState(Bundle outState) {
-        onSaveInstanceState(outState);
-        if (mChildFragmentManager != null) {
-            Parcelable p = mChildFragmentManager.saveAllState();
-            if (p != null) {
-                outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
-            }
-        }
-    }
-
-    void performPause() {
-        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchPause();
-        }
-        mState = STARTED;
-        mCalled = false;
-        onPause();
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onPause()");
-        }
-    }
-
-    void performStop() {
-        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchStop();
-        }
-        mState = STOPPED;
-        mCalled = false;
-        onStop();
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onStop()");
-        }
-    }
-
-    void performReallyStop() {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchReallyStop();
-        }
-        mState = ACTIVITY_CREATED;
-        if (mLoadersStarted) {
-            mLoadersStarted = false;
-            if (!mCheckedForLoaderManager) {
-                mCheckedForLoaderManager = true;
-                mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
-            }
-            if (mLoaderManager != null) {
-                if (mHost.getRetainLoaders()) {
-                    mLoaderManager.doRetain();
-                } else {
-                    mLoaderManager.doStop();
-                }
-            }
-        }
-    }
-
-    void performDestroyView() {
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchDestroyView();
-        }
-        mState = CREATED;
-        mCalled = false;
-        onDestroyView();
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onDestroyView()");
-        }
-        if (mLoaderManager != null) {
-            mLoaderManager.doReportNextStart();
-        }
-        mPerformedCreateView = false;
-    }
-
-    void performDestroy() {
-        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
-        if (mChildFragmentManager != null) {
-            mChildFragmentManager.dispatchDestroy();
-        }
-        mState = INITIALIZING;
-        mCalled = false;
-        mIsCreated = false;
-        onDestroy();
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onDestroy()");
-        }
-        mChildFragmentManager = null;
-    }
-
-    void performDetach() {
-        mCalled = false;
-        onDetach();
-        mLayoutInflater = null;
-        if (!mCalled) {
-            throw new SuperNotCalledException("Fragment " + this
-                    + " did not call through to super.onDetach()");
-        }
-
-        // Destroy the child FragmentManager if we still have it here.
-        // We won't unless we're retaining our instance and if we do,
-        // our child FragmentManager instance state will have already been saved.
-        if (mChildFragmentManager != null) {
-            if (!mRetaining) {
-                throw new IllegalStateException("Child FragmentManager of " + this + " was not "
-                        + " destroyed and this fragment is not retaining instance");
-            }
-            mChildFragmentManager.dispatchDestroy();
-            mChildFragmentManager = null;
-        }
-    }
-
-    void setOnStartEnterTransitionListener(OnStartEnterTransitionListener listener) {
-        ensureAnimationInfo();
-        if (listener == mAnimationInfo.mStartEnterTransitionListener) {
-            return;
-        }
-        if (listener != null && mAnimationInfo.mStartEnterTransitionListener != null) {
-            throw new IllegalStateException("Trying to set a replacement "
-                    + "startPostponedEnterTransition on " + this);
-        }
-        if (mAnimationInfo.mEnterTransitionPostponed) {
-            mAnimationInfo.mStartEnterTransitionListener = listener;
-        }
-        if (listener != null) {
-            listener.startListening();
-        }
-    }
-
-    private AnimationInfo ensureAnimationInfo() {
-        if (mAnimationInfo == null) {
-            mAnimationInfo = new AnimationInfo();
-        }
-        return mAnimationInfo;
-    }
-
-    int getNextAnim() {
-        if (mAnimationInfo == null) {
-            return 0;
-        }
-        return mAnimationInfo.mNextAnim;
-    }
-
-    void setNextAnim(int animResourceId) {
-        if (mAnimationInfo == null && animResourceId == 0) {
-            return; // no change!
-        }
-        ensureAnimationInfo().mNextAnim = animResourceId;
-    }
-
-    int getNextTransition() {
-        if (mAnimationInfo == null) {
-            return 0;
-        }
-        return mAnimationInfo.mNextTransition;
-    }
-
-    void setNextTransition(int nextTransition, int nextTransitionStyle) {
-        if (mAnimationInfo == null && nextTransition == 0 && nextTransitionStyle == 0) {
-            return; // no change!
-        }
-        ensureAnimationInfo();
-        mAnimationInfo.mNextTransition = nextTransition;
-        mAnimationInfo.mNextTransitionStyle = nextTransitionStyle;
-    }
-
-    int getNextTransitionStyle() {
-        if (mAnimationInfo == null) {
-            return 0;
-        }
-        return mAnimationInfo.mNextTransitionStyle;
-    }
-
-    SharedElementCallback getEnterTransitionCallback() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mEnterTransitionCallback;
-    }
-
-    SharedElementCallback getExitTransitionCallback() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mExitTransitionCallback;
-    }
-
-    View getAnimatingAway() {
-        if (mAnimationInfo == null) {
-            return null;
-        }
-        return mAnimationInfo.mAnimatingAway;
-    }
-
-    void setAnimatingAway(View view) {
-        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;
-        }
-        return mAnimationInfo.mStateAfterAnimating;
-    }
-
-    void setStateAfterAnimating(int state) {
-        ensureAnimationInfo().mStateAfterAnimating = state;
-    }
-
-    boolean isPostponed() {
-        if (mAnimationInfo == null) {
-            return false;
-        }
-        return mAnimationInfo.mEnterTransitionPostponed;
-    }
-
-    boolean isHideReplaced() {
-        if (mAnimationInfo == null) {
-            return false;
-        }
-        return mAnimationInfo.mIsHideReplaced;
-    }
-
-    void setHideReplaced(boolean replaced) {
-        ensureAnimationInfo().mIsHideReplaced = replaced;
-    }
-
-    /**
-     * Used internally to be notified when {@link #startPostponedEnterTransition()} has
-     * been called. This listener will only be called once and then be removed from the
-     * listeners.
-     */
-    interface OnStartEnterTransitionListener {
-        void onStartEnterTransition();
-        void startListening();
-    }
-
-    /**
-     * Contains all the animation and transition information for a fragment. This will only
-     * be instantiated for Fragments that have Views.
-     */
-    static class AnimationInfo {
-        // Non-null if the fragment's view hierarchy is currently animating away,
-        // meaning we need to wait a bit on completely destroying it.  This is the
-        // 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;
-
-        // If app has requested a specific animation, this is the one to use.
-        int mNextAnim;
-
-        // If app has requested a specific transition, this is the one to use.
-        int mNextTransition;
-
-        // If app has requested a specific transition style, this is the one to use.
-        int mNextTransitionStyle;
-
-        private Object mEnterTransition = null;
-        private Object mReturnTransition = USE_DEFAULT_TRANSITION;
-        private Object mExitTransition = null;
-        private Object mReenterTransition = USE_DEFAULT_TRANSITION;
-        private Object mSharedElementEnterTransition = null;
-        private Object mSharedElementReturnTransition = USE_DEFAULT_TRANSITION;
-        private Boolean mAllowReturnTransitionOverlap;
-        private Boolean mAllowEnterTransitionOverlap;
-
-        SharedElementCallback mEnterTransitionCallback = null;
-        SharedElementCallback mExitTransitionCallback = null;
-
-        // True when postponeEnterTransition has been called and startPostponeEnterTransition
-        // hasn't been called yet.
-        boolean mEnterTransitionPostponed;
-
-        // Listener to wait for startPostponeEnterTransition. After being called, it will
-        // be set to null
-        OnStartEnterTransitionListener mStartEnterTransitionListener;
-
-        // True if the View was hidden, but the transition is handling the hide
-        boolean mIsHideReplaced;
-    }
-}
diff --git a/android/support/v4/app/FragmentActivity.java b/android/support/v4/app/FragmentActivity.java
deleted file mode 100644
index e3f5684..0000000
--- a/android/support/v4/app/FragmentActivity.java
+++ /dev/null
@@ -1,1000 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.arch.lifecycle.Lifecycle;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-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.util.SimpleArrayMap;
-import android.support.v4.util.SparseArrayCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Collection;
-
-/**
- * Base class for activities that want to use the support-based
- * {@link android.support.v4.app.Fragment} and
- * {@link android.support.v4.content.Loader} APIs.
- *
- * <p>When using this class as opposed to new platform's built-in fragment
- * and loader support, you must use the {@link #getSupportFragmentManager()}
- * and {@link #getSupportLoaderManager()} methods respectively to access
- * those features.
- *
- * <p>Known limitations:</p>
- * <ul>
- * <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>
- * </ul>
- */
-public class FragmentActivity extends BaseFragmentActivityApi16 implements
-        ActivityCompat.OnRequestPermissionsResultCallback,
-        ActivityCompat.RequestPermissionsRequestCodeValidator {
-    private static final String TAG = "FragmentActivity";
-
-    static final String FRAGMENTS_TAG = "android:support:fragments";
-    static final String NEXT_CANDIDATE_REQUEST_INDEX_TAG = "android:support:next_request_index";
-    static final String ALLOCATED_REQUEST_INDICIES_TAG = "android:support:request_indicies";
-    static final String REQUEST_FRAGMENT_WHO_TAG = "android:support:request_fragment_who";
-    static final int MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS = 0xffff - 1;
-
-    static final int MSG_REALLY_STOPPED = 1;
-    static final int MSG_RESUME_PENDING = 2;
-
-    final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_REALLY_STOPPED:
-                    if (mStopped) {
-                        doReallyStop(false);
-                    }
-                    break;
-                case MSG_RESUME_PENDING:
-                    onResumeFragments();
-                    mFragments.execPendingActions();
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-
-    };
-    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
-
-    boolean mCreated;
-    boolean mResumed;
-    boolean mStopped = true;
-    boolean mReallyStopped = true;
-    boolean mRetaining;
-
-    boolean mRequestedPermissionsFromFragment;
-
-    // A hint for the next candidate request index. Request indicies are ints between 0 and 2^16-1
-    // which are encoded into the upper 16 bits of the requestCode for
-    // Fragment.startActivityForResult(...) calls. This allows us to dispatch onActivityResult(...)
-    // to the appropriate Fragment. Request indicies are allocated by allocateRequestIndex(...).
-    int mNextCandidateRequestIndex;
-    // A map from request index to Fragment "who" (i.e. a Fragment's unique identifier). Used to
-    // keep track of the originating Fragment for Fragment.startActivityForResult(...) calls, so we
-    // can dispatch the onActivityResult(...) to the appropriate Fragment. Will only contain entries
-    // for startActivityForResult calls where a result has not yet been delivered.
-    SparseArrayCompat<String> mPendingFragmentActivityResults;
-
-    static final class NonConfigurationInstances {
-        Object custom;
-        FragmentManagerNonConfig fragments;
-        SimpleArrayMap<String, LoaderManager> loaders;
-    }
-
-    // ------------------------------------------------------------------------
-    // HOOKS INTO ACTIVITY
-    // ------------------------------------------------------------------------
-
-    /**
-     * Dispatch incoming result to the correct fragment.
-     */
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        mFragments.noteStateNotSaved();
-        int requestIndex = requestCode>>16;
-        if (requestIndex != 0) {
-            requestIndex--;
-
-            String who = mPendingFragmentActivityResults.get(requestIndex);
-            mPendingFragmentActivityResults.remove(requestIndex);
-            if (who == null) {
-                Log.w(TAG, "Activity result delivered for unknown Fragment.");
-                return;
-            }
-            Fragment targetFragment = mFragments.findFragmentByWho(who);
-            if (targetFragment == null) {
-                Log.w(TAG, "Activity result no fragment exists for who: " + who);
-            } else {
-                targetFragment.onActivityResult(requestCode & 0xffff, resultCode, data);
-            }
-            return;
-        }
-
-        ActivityCompat.PermissionCompatDelegate delegate =
-                ActivityCompat.getPermissionCompatDelegate();
-        if (delegate != null && delegate.onActivityResult(this, requestCode, resultCode, data)) {
-            // Delegate has handled the activity result
-            return;
-        }
-
-        super.onActivityResult(requestCode, resultCode, data);
-    }
-
-    /**
-     * Take care of popping the fragment back stack or finishing the activity
-     * as appropriate.
-     */
-    @Override
-    public void onBackPressed() {
-        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();
-        }
-    }
-
-    /**
-     * 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
-     * immediately and the Activity exit Transition is run.
-     *
-     * <p>On Android 4.4 or lower, this method only finishes the Activity with no
-     * special exit transition.</p>
-     */
-    public void supportFinishAfterTransition() {
-        ActivityCompat.finishAfterTransition(this);
-    }
-
-    /**
-     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
-     * android.view.View, String)} was used to start an Activity, <var>callback</var>
-     * will be called to handle shared elements on the <i>launched</i> Activity. This requires
-     * {@link Window#FEATURE_CONTENT_TRANSITIONS}.
-     *
-     * @param callback Used to manipulate shared element transitions on the launched Activity.
-     */
-    public void setEnterSharedElementCallback(SharedElementCallback callback) {
-        ActivityCompat.setEnterSharedElementCallback(this, callback);
-    }
-
-    /**
-     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
-     * android.view.View, String)} was used to start an Activity, <var>listener</var>
-     * will be called to handle shared elements on the <i>launching</i> Activity. Most
-     * calls will only come when returning from the started Activity.
-     * This requires {@link Window#FEATURE_CONTENT_TRANSITIONS}.
-     *
-     * @param listener Used to manipulate shared element transitions on the launching Activity.
-     */
-    public void setExitSharedElementCallback(SharedElementCallback listener) {
-        ActivityCompat.setExitSharedElementCallback(this, listener);
-    }
-
-    /**
-     * Support library version of {@link android.app.Activity#postponeEnterTransition()} that works
-     * only on API 21 and later.
-     */
-    public void supportPostponeEnterTransition() {
-        ActivityCompat.postponeEnterTransition(this);
-    }
-
-    /**
-     * Support library version of {@link android.app.Activity#startPostponedEnterTransition()}
-     * that only works with API 21 and later.
-     */
-    public void supportStartPostponedEnterTransition() {
-        ActivityCompat.startPostponedEnterTransition(this);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p><strong>Note:</strong> If you override this method you must call
-     * <code>super.onMultiWindowModeChanged</code> to correctly dispatch the event
-     * to support fragments attached to this activity.</p>
-     *
-     * @param isInMultiWindowMode True if the activity is in multi-window mode.
-     */
-    @Override
-    @CallSuper
-    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
-        mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p><strong>Note:</strong> If you override this method you must call
-     * <code>super.onPictureInPictureModeChanged</code> to correctly dispatch the event
-     * to support fragments attached to this activity.</p>
-     *
-     * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode.
-     */
-    @Override
-    @CallSuper
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
-    }
-
-    /**
-     * Dispatch configuration change to all fragments.
-     */
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mFragments.noteStateNotSaved();
-        mFragments.dispatchConfigurationChanged(newConfig);
-    }
-
-    /**
-     * Returns the Lifecycle of the provider.
-     *
-     * @return The lifecycle of the provider.
-     */
-    @Override
-    public Lifecycle getLifecycle() {
-        return super.getLifecycle();
-    }
-
-    /**
-     * Perform initialization of all fragments and loaders.
-     */
-    @SuppressWarnings("deprecation")
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        mFragments.attachHost(null /*parent*/);
-
-        super.onCreate(savedInstanceState);
-
-        NonConfigurationInstances nc =
-                (NonConfigurationInstances) getLastNonConfigurationInstance();
-        if (nc != null) {
-            mFragments.restoreLoaderNonConfig(nc.loaders);
-        }
-        if (savedInstanceState != null) {
-            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
-            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
-
-            // Check if there are any pending onActivityResult calls to descendent Fragments.
-            if (savedInstanceState.containsKey(NEXT_CANDIDATE_REQUEST_INDEX_TAG)) {
-                mNextCandidateRequestIndex =
-                        savedInstanceState.getInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG);
-                int[] requestCodes = savedInstanceState.getIntArray(ALLOCATED_REQUEST_INDICIES_TAG);
-                String[] fragmentWhos = savedInstanceState.getStringArray(REQUEST_FRAGMENT_WHO_TAG);
-                if (requestCodes == null || fragmentWhos == null ||
-                            requestCodes.length != fragmentWhos.length) {
-                    Log.w(TAG, "Invalid requestCode mapping in savedInstanceState.");
-                } else {
-                    mPendingFragmentActivityResults = new SparseArrayCompat<>(requestCodes.length);
-                    for (int i = 0; i < requestCodes.length; i++) {
-                        mPendingFragmentActivityResults.put(requestCodes[i], fragmentWhos[i]);
-                    }
-                }
-            }
-        }
-
-        if (mPendingFragmentActivityResults == null) {
-            mPendingFragmentActivityResults = new SparseArrayCompat<>();
-            mNextCandidateRequestIndex = 0;
-        }
-
-        mFragments.dispatchCreate();
-    }
-
-    /**
-     * Dispatch to Fragment.onCreateOptionsMenu().
-     */
-    @Override
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
-        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
-            boolean show = super.onCreatePanelMenu(featureId, menu);
-            show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
-            return show;
-        }
-        return super.onCreatePanelMenu(featureId, menu);
-    }
-
-    @Override
-    final View dispatchFragmentsOnCreateView(View parent, String name, Context context,
-            AttributeSet attrs) {
-        return mFragments.onCreateView(parent, name, context, attrs);
-    }
-
-    /**
-     * Destroy all fragments and loaders.
-     */
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        doReallyStop(false);
-
-        mFragments.dispatchDestroy();
-        mFragments.doLoaderDestroy();
-    }
-
-    /**
-     * Dispatch onLowMemory() to all fragments.
-     */
-    @Override
-    public void onLowMemory() {
-        super.onLowMemory();
-        mFragments.dispatchLowMemory();
-    }
-
-    /**
-     * Dispatch context and options menu to fragments.
-     */
-    @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {
-        if (super.onMenuItemSelected(featureId, item)) {
-            return true;
-        }
-
-        switch (featureId) {
-            case Window.FEATURE_OPTIONS_PANEL:
-                return mFragments.dispatchOptionsItemSelected(item);
-
-            case Window.FEATURE_CONTEXT_MENU:
-                return mFragments.dispatchContextItemSelected(item);
-
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Call onOptionsMenuClosed() on fragments.
-     */
-    @Override
-    public void onPanelClosed(int featureId, Menu menu) {
-        switch (featureId) {
-            case Window.FEATURE_OPTIONS_PANEL:
-                mFragments.dispatchOptionsMenuClosed(menu);
-                break;
-        }
-        super.onPanelClosed(featureId, menu);
-    }
-
-    /**
-     * Dispatch onPause() to fragments.
-     */
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mResumed = false;
-        if (mHandler.hasMessages(MSG_RESUME_PENDING)) {
-            mHandler.removeMessages(MSG_RESUME_PENDING);
-            onResumeFragments();
-        }
-        mFragments.dispatchPause();
-    }
-
-    /**
-     * Handle onNewIntent() to inform the fragment manager that the
-     * state is not saved.  If you are handling new intents and may be
-     * making changes to the fragment state, you want to be sure to call
-     * through to the super-class here first.  Otherwise, if your state
-     * is saved but the activity is not stopped, you could get an
-     * onNewIntent() call which happens before onResume() and trying to
-     * perform fragment operations at that point will throw IllegalStateException
-     * because the fragment manager thinks the state is still saved.
-     */
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        mFragments.noteStateNotSaved();
-    }
-
-    /**
-     * Hook in to note that fragment state is no longer saved.
-     */
-    @Override
-    public void onStateNotSaved() {
-        mFragments.noteStateNotSaved();
-    }
-
-    /**
-     * Dispatch onResume() to fragments.  Note that for better inter-operation
-     * with older versions of the platform, at the point of this call the
-     * fragments attached to the activity are <em>not</em> resumed.  This means
-     * that in some cases the previous state may still be saved, not allowing
-     * fragment transactions that modify the state.  To correctly interact
-     * with fragments in their proper state, you should instead override
-     * {@link #onResumeFragments()}.
-     */
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mHandler.sendEmptyMessage(MSG_RESUME_PENDING);
-        mResumed = true;
-        mFragments.execPendingActions();
-    }
-
-    /**
-     * Dispatch onResume() to fragments.
-     */
-    @Override
-    protected void onPostResume() {
-        super.onPostResume();
-        mHandler.removeMessages(MSG_RESUME_PENDING);
-        onResumeFragments();
-        mFragments.execPendingActions();
-    }
-
-    /**
-     * This is the fragment-orientated version of {@link #onResume()} that you
-     * can override to perform operations in the Activity at the same point
-     * where its fragments are resumed.  Be sure to always call through to
-     * the super-class.
-     */
-    protected void onResumeFragments() {
-        mFragments.dispatchResume();
-    }
-
-    /**
-     * Dispatch onPrepareOptionsMenu() to fragments.
-     */
-    @Override
-    public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
-            boolean goforit = onPrepareOptionsPanel(view, menu);
-            goforit |= mFragments.dispatchPrepareOptionsMenu(menu);
-            return goforit;
-        }
-        return super.onPreparePanel(featureId, view, menu);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean onPrepareOptionsPanel(View view, Menu menu) {
-        return super.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, view, menu);
-    }
-
-    /**
-     * Retain all appropriate fragment and loader state.  You can NOT
-     * override this yourself!  Use {@link #onRetainCustomNonConfigurationInstance()}
-     * if you want to retain your own state.
-     */
-    @Override
-    public final Object onRetainNonConfigurationInstance() {
-        if (mStopped) {
-            doReallyStop(true);
-        }
-
-        Object custom = onRetainCustomNonConfigurationInstance();
-
-        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
-        SimpleArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
-
-        if (fragments == null && loaders == null && custom == null) {
-            return null;
-        }
-
-        NonConfigurationInstances nci = new NonConfigurationInstances();
-        nci.custom = custom;
-        nci.fragments = fragments;
-        nci.loaders = loaders;
-        return nci;
-    }
-
-    /**
-     * Save all appropriate fragment state.
-     */
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        markFragmentsCreated();
-        Parcelable p = mFragments.saveAllState();
-        if (p != null) {
-            outState.putParcelable(FRAGMENTS_TAG, p);
-        }
-        if (mPendingFragmentActivityResults.size() > 0) {
-            outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG, mNextCandidateRequestIndex);
-
-            int[] requestCodes = new int[mPendingFragmentActivityResults.size()];
-            String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()];
-            for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) {
-                requestCodes[i] = mPendingFragmentActivityResults.keyAt(i);
-                fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i);
-            }
-            outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes);
-            outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos);
-        }
-    }
-
-    /**
-     * Dispatch onStart() to all fragments.  Ensure any created loaders are
-     * now started.
-     */
-    @Override
-    protected void onStart() {
-        super.onStart();
-
-        mStopped = false;
-        mReallyStopped = false;
-        mHandler.removeMessages(MSG_REALLY_STOPPED);
-
-        if (!mCreated) {
-            mCreated = true;
-            mFragments.dispatchActivityCreated();
-        }
-
-        mFragments.noteStateNotSaved();
-        mFragments.execPendingActions();
-
-        mFragments.doLoaderStart();
-
-        // NOTE: HC onStart goes here.
-
-        mFragments.dispatchStart();
-        mFragments.reportLoaderStart();
-    }
-
-    /**
-     * Dispatch onStop() to all fragments.  Ensure all loaders are stopped.
-     */
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        mStopped = true;
-        markFragmentsCreated();
-        mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
-
-        mFragments.dispatchStop();
-    }
-
-    // ------------------------------------------------------------------------
-    // NEW METHODS
-    // ------------------------------------------------------------------------
-
-    /**
-     * Use this instead of {@link #onRetainNonConfigurationInstance()}.
-     * Retrieve later with {@link #getLastCustomNonConfigurationInstance()}.
-     */
-    public Object onRetainCustomNonConfigurationInstance() {
-        return null;
-    }
-
-    /**
-     * Return the value previously returned from
-     * {@link #onRetainCustomNonConfigurationInstance()}.
-     */
-    @SuppressWarnings("deprecation")
-    public Object getLastCustomNonConfigurationInstance() {
-        NonConfigurationInstances nc = (NonConfigurationInstances)
-                getLastNonConfigurationInstance();
-        return nc != null ? nc.custom : null;
-    }
-
-    /**
-     * Support library version of {@link Activity#invalidateOptionsMenu}.
-     *
-     * <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() {
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Print the Activity's state into the given stream.  This gets invoked if
-     * you run "adb shell dumpsys activity <activity_component_name>".
-     *
-     * @param prefix Desired prefix to prepend at each line of output.
-     * @param fd The raw file descriptor that the dump is being sent to.
-     * @param writer The PrintWriter to which you should dump your state.  This will be
-     * 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) {
-        super.dump(prefix, fd, writer, args);
-        writer.print(prefix); writer.print("Local FragmentActivity ");
-                writer.print(Integer.toHexString(System.identityHashCode(this)));
-                writer.println(" State:");
-        String innerPrefix = prefix + "  ";
-        writer.print(innerPrefix); writer.print("mCreated=");
-                writer.print(mCreated); writer.print("mResumed=");
-                writer.print(mResumed); writer.print(" mStopped=");
-                writer.print(mStopped); writer.print(" mReallyStopped=");
-                writer.println(mReallyStopped);
-        mFragments.dumpLoaders(innerPrefix, fd, writer, args);
-        mFragments.getSupportFragmentManager().dump(prefix, fd, writer, args);
-    }
-
-    void doReallyStop(boolean retaining) {
-        if (!mReallyStopped) {
-            mReallyStopped = true;
-            mRetaining = retaining;
-            mHandler.removeMessages(MSG_REALLY_STOPPED);
-            onReallyStop();
-        } else if (retaining) {
-            // We're already really stopped, but we've been asked to retain.
-            // Our fragments are taken care of but we need to mark the loaders for retention.
-            // In order to do this correctly we need to restart the loaders first before
-            // handing them off to the next activity.
-            mFragments.doLoaderStart();
-            mFragments.doLoaderStop(true);
-        }
-    }
-
-    /**
-     * Pre-HC, we didn't have a way to determine whether an activity was
-     * being stopped for a config change or not until we saw
-     * onRetainNonConfigurationInstance() called after onStop().  However
-     * we need to know this, to know whether to retain fragments.  This will
-     * tell us what we need to know.
-     */
-    void onReallyStop() {
-        mFragments.doLoaderStop(mRetaining);
-
-        mFragments.dispatchReallyStop();
-    }
-
-    // ------------------------------------------------------------------------
-    // FRAGMENT SUPPORT
-    // ------------------------------------------------------------------------
-
-    /**
-     * Called when a fragment is attached to the activity.
-     *
-     * <p>This is called after the attached fragment's <code>onAttach</code> and before
-     * the attached fragment's <code>onCreate</code> if the fragment has not yet had a previous
-     * call to <code>onCreate</code>.</p>
-     */
-    @SuppressWarnings("unused")
-    public void onAttachFragment(Fragment fragment) {
-    }
-
-    /**
-     * Return the FragmentManager for interacting with fragments associated
-     * with this activity.
-     */
-    public FragmentManager getSupportFragmentManager() {
-        return mFragments.getSupportFragmentManager();
-    }
-
-    public LoaderManager getSupportLoaderManager() {
-        return mFragments.getSupportLoaderManager();
-    }
-
-    /**
-     * Modifies the standard behavior to allow results to be delivered to fragments.
-     * This imposes a restriction that requestCode be <= 0xffff.
-     */
-    @Override
-    public void startActivityForResult(Intent intent, int requestCode) {
-        // 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);
-    }
-
-    @Override
-    public final void validateRequestPermissionsRequestCode(int requestCode) {
-        // We use 16 bits of the request code to encode the fragment id when
-        // requesting permissions from a fragment. Hence, requestPermissions()
-        // should validate the code against that but we cannot override it as
-        // we can not then call super and also the ActivityCompat would call
-        // back to this override. To handle this we use dependency inversion
-        // where we are the validator of request codes when requesting
-        // permissions in ActivityCompat.
-        if (!mRequestedPermissionsFromFragment
-                && requestCode != -1) {
-            checkForValidRequestCode(requestCode);
-        }
-    }
-
-    /**
-     * Callback for the result from requesting permissions. This method
-     * is invoked for every call on {@link #requestPermissions(String[], int)}.
-     * <p>
-     * <strong>Note:</strong> It is possible that the permissions request interaction
-     * with the user is interrupted. In this case you will receive empty permissions
-     * and results arrays which should be treated as a cancellation.
-     * </p>
-     *
-     * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
-     * @param permissions The requested permissions. Never null.
-     * @param grantResults The grant results for the corresponding permissions
-     *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
-     *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
-     *
-     * @see #requestPermissions(String[], int)
-     */
-    @Override
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
-            @NonNull int[] grantResults) {
-        mFragments.noteStateNotSaved();
-        int index = (requestCode >> 16) & 0xffff;
-        if (index != 0) {
-            index--;
-
-            String who = mPendingFragmentActivityResults.get(index);
-            mPendingFragmentActivityResults.remove(index);
-            if (who == null) {
-                Log.w(TAG, "Activity result delivered for unknown Fragment.");
-                return;
-            }
-            Fragment frag = mFragments.findFragmentByWho(who);
-            if (frag == null) {
-                Log.w(TAG, "Activity result no fragment exists for who: " + who);
-            } else {
-                frag.onRequestPermissionsResult(requestCode & 0xffff, permissions, grantResults);
-            }
-        }
-    }
-
-    /**
-     * Called by Fragment.startActivityForResult() to implement its behavior.
-     */
-    public void startActivityFromFragment(Fragment fragment, Intent intent,
-            int requestCode) {
-        startActivityFromFragment(fragment, intent, requestCode, null);
-    }
-
-    /**
-     * Called by Fragment.startActivityForResult() to implement its behavior.
-     */
-    public void startActivityFromFragment(Fragment fragment, Intent intent,
-            int requestCode, @Nullable Bundle options) {
-        mStartedActivityFromFragment = true;
-        try {
-            if (requestCode == -1) {
-                ActivityCompat.startActivityForResult(this, intent, -1, options);
-                return;
-            }
-            checkForValidRequestCode(requestCode);
-            int requestIndex = allocateRequestIndex(fragment);
-            ActivityCompat.startActivityForResult(
-                    this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
-        } finally {
-            mStartedActivityFromFragment = false;
-        }
-    }
-
-    /**
-     * Called by Fragment.startIntentSenderForResult() to implement its behavior.
-     */
-    public void startIntentSenderFromFragment(Fragment fragment, IntentSender intent,
-            int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
-            int extraFlags, Bundle options) throws IntentSender.SendIntentException {
-        mStartedIntentSenderFromFragment = true;
-        try {
-            if (requestCode == -1) {
-                ActivityCompat.startIntentSenderForResult(this, intent, requestCode, fillInIntent,
-                        flagsMask, flagsValues, extraFlags, options);
-                return;
-            }
-            checkForValidRequestCode(requestCode);
-            int requestIndex = allocateRequestIndex(fragment);
-            ActivityCompat.startIntentSenderForResult(this, intent,
-                    ((requestIndex + 1) << 16) + (requestCode & 0xffff), fillInIntent,
-                    flagsMask, flagsValues, extraFlags, options);
-        } finally {
-            mStartedIntentSenderFromFragment = false;
-        }
-    }
-
-    // Allocates the next available startActivityForResult request index.
-    private int allocateRequestIndex(Fragment fragment) {
-        // Sanity check that we havn't exhaused the request index space.
-        if (mPendingFragmentActivityResults.size() >= MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS) {
-            throw new IllegalStateException("Too many pending Fragment activity results.");
-        }
-
-        // Find an unallocated request index in the mPendingFragmentActivityResults map.
-        while (mPendingFragmentActivityResults.indexOfKey(mNextCandidateRequestIndex) >= 0) {
-            mNextCandidateRequestIndex =
-                    (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;
-        }
-
-        int requestIndex = mNextCandidateRequestIndex;
-        mPendingFragmentActivityResults.put(requestIndex, fragment.mWho);
-        mNextCandidateRequestIndex =
-                (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;
-
-        return requestIndex;
-    }
-
-    /**
-     * Called by Fragment.requestPermissions() to implement its behavior.
-     */
-    void requestPermissionsFromFragment(Fragment fragment, String[] permissions,
-            int requestCode) {
-        if (requestCode == -1) {
-            ActivityCompat.requestPermissions(this, permissions, requestCode);
-            return;
-        }
-        checkForValidRequestCode(requestCode);
-        try {
-            mRequestedPermissionsFromFragment = true;
-            int requestIndex = allocateRequestIndex(fragment);
-            ActivityCompat.requestPermissions(this, permissions,
-                    ((requestIndex + 1) << 16) + (requestCode & 0xffff));
-        } finally {
-            mRequestedPermissionsFromFragment = false;
-        }
-    }
-
-    class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
-        public HostCallbacks() {
-            super(FragmentActivity.this /*fragmentActivity*/);
-        }
-
-        @Override
-        public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-            FragmentActivity.this.dump(prefix, fd, writer, args);
-        }
-
-        @Override
-        public boolean onShouldSaveFragmentState(Fragment fragment) {
-            return !isFinishing();
-        }
-
-        @Override
-        public LayoutInflater onGetLayoutInflater() {
-            return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.this);
-        }
-
-        @Override
-        public FragmentActivity onGetHost() {
-            return FragmentActivity.this;
-        }
-
-        @Override
-        public void onSupportInvalidateOptionsMenu() {
-            FragmentActivity.this.supportInvalidateOptionsMenu();
-        }
-
-        @Override
-        public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
-            FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
-        }
-
-        @Override
-        public void onStartActivityFromFragment(
-                Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
-            FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
-        }
-
-        @Override
-        public void onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent,
-                int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
-                int extraFlags, Bundle options) throws IntentSender.SendIntentException {
-            FragmentActivity.this.startIntentSenderFromFragment(fragment, intent, requestCode,
-                    fillInIntent, flagsMask, flagsValues, extraFlags, options);
-        }
-
-        @Override
-        public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
-                @NonNull String[] permissions, int requestCode) {
-            FragmentActivity.this.requestPermissionsFromFragment(fragment, permissions,
-                    requestCode);
-        }
-
-        @Override
-        public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
-            return ActivityCompat.shouldShowRequestPermissionRationale(
-                    FragmentActivity.this, permission);
-        }
-
-        @Override
-        public boolean onHasWindowAnimations() {
-            return getWindow() != null;
-        }
-
-        @Override
-        public int onGetWindowAnimations() {
-            final Window w = getWindow();
-            return (w == null) ? 0 : w.getAttributes().windowAnimations;
-        }
-
-        @Override
-        public void onAttachFragment(Fragment fragment) {
-            FragmentActivity.this.onAttachFragment(fragment);
-        }
-
-        @Nullable
-        @Override
-        public View onFindViewById(int id) {
-            return FragmentActivity.this.findViewById(id);
-        }
-
-        @Override
-        public boolean onHasView() {
-            final Window w = getWindow();
-            return (w != null && w.peekDecorView() != null);
-        }
-    }
-
-    private void markFragmentsCreated() {
-        boolean reiterate;
-        do {
-            reiterate = markState(getSupportFragmentManager(), Lifecycle.State.CREATED);
-        } while (reiterate);
-    }
-
-    private static boolean markState(FragmentManager manager, Lifecycle.State state) {
-        boolean hadNotMarked = false;
-        Collection<Fragment> fragments = manager.getFragments();
-        for (Fragment fragment : fragments) {
-            if (fragment == null) {
-                continue;
-            }
-            if (fragment.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
-                fragment.mLifecycleRegistry.markState(state);
-                hadNotMarked = true;
-            }
-
-            FragmentManager childFragmentManager = fragment.peekChildFragmentManager();
-            if (childFragmentManager != null) {
-                hadNotMarked |= markState(childFragmentManager, state);
-            }
-        }
-        return hadNotMarked;
-    }
-}
diff --git a/android/support/v4/app/FragmentContainer.java b/android/support/v4/app/FragmentContainer.java
deleted file mode 100644
index d15953b..0000000
--- a/android/support/v4/app/FragmentContainer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-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;
-
-
-/**
- * Callbacks to a {@link Fragment}'s container.
- */
-public abstract class FragmentContainer {
-    /**
-     * Return the view with the given resource ID. May return {@code null} if the
-     * view is not a child of this container.
-     */
-    @Nullable
-    public abstract View onFindViewById(@IdRes int id);
-
-    /**
-     * 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/android/support/v4/app/FragmentController.java b/android/support/v4/app/FragmentController.java
deleted file mode 100644
index 48b3602..0000000
--- a/android/support/v4/app/FragmentController.java
+++ /dev/null
@@ -1,445 +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.content.res.Configuration;
-import android.os.Parcelable;
-import android.support.annotation.Nullable;
-import android.support.v4.util.SimpleArrayMap;
-import android.util.AttributeSet;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-/**
- * Provides integration points with a {@link FragmentManager} for a fragment host.
- * <p>
- * It is the responsibility of the host to take care of the Fragment's lifecycle.
- * The methods provided by {@link FragmentController} are for that purpose.
- */
-public class FragmentController {
-    private final FragmentHostCallback<?> mHost;
-
-    /**
-     * Returns a {@link FragmentController}.
-     */
-    public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
-        return new FragmentController(callbacks);
-    }
-
-    private FragmentController(FragmentHostCallback<?> callbacks) {
-        mHost = callbacks;
-    }
-
-    /**
-     * Returns a {@link FragmentManager} for this controller.
-     */
-    public FragmentManager getSupportFragmentManager() {
-        return mHost.getFragmentManagerImpl();
-    }
-
-    /**
-     * Returns a {@link LoaderManager}.
-     */
-    public LoaderManager getSupportLoaderManager() {
-        return mHost.getLoaderManagerImpl();
-    }
-
-    /**
-     * Returns a fragment with the given identifier.
-     */
-    @Nullable
-    public Fragment findFragmentByWho(String who) {
-        return mHost.mFragmentManager.findFragmentByWho(who);
-    }
-
-    /**
-     * Returns the number of active fragments.
-     */
-    public int getActiveFragmentsCount() {
-        return mHost.mFragmentManager.getActiveFragmentCount();
-    }
-
-    /**
-     * Returns the list of active fragments.
-     */
-    public List<Fragment> getActiveFragments(List<Fragment> actives) {
-        return mHost.mFragmentManager.getActiveFragments();
-    }
-
-    /**
-     * Attaches the host to the FragmentManager for this controller. The host must be
-     * attached before the FragmentManager can be used to manage Fragments.
-     */
-    public void attachHost(Fragment parent) {
-        mHost.mFragmentManager.attachController(
-                mHost, mHost /*container*/, parent);
-    }
-
-    /**
-     * Instantiates a Fragment's view.
-     *
-     * @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 the newly created view
-     */
-    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        return mHost.mFragmentManager.onCreateView(parent, name, context, attrs);
-    }
-
-    /**
-     * Marks the fragment state as unsaved. This allows for "state loss" detection.
-     */
-    public void noteStateNotSaved() {
-        mHost.mFragmentManager.noteStateNotSaved();
-    }
-
-    /**
-     * Saves the state for all Fragments.
-     */
-    public Parcelable saveAllState() {
-        return mHost.mFragmentManager.saveAllState();
-    }
-
-    /**
-     * Restores the saved state for all Fragments. The given Fragment list are Fragment
-     * instances retained across configuration changes.
-     *
-     * @see #retainNonConfig()
-     *
-     * @deprecated use {@link #restoreAllState(Parcelable, FragmentManagerNonConfig)}
-     */
-    @Deprecated
-    public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
-        mHost.mFragmentManager.restoreAllState(state,
-                new FragmentManagerNonConfig(nonConfigList, null));
-    }
-
-    /**
-     * Restores the saved state for all Fragments. The given FragmentManagerNonConfig are Fragment
-     * instances retained across configuration changes, including nested fragments
-     *
-     * @see #retainNestedNonConfig()
-     */
-    public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
-        mHost.mFragmentManager.restoreAllState(state, nonConfig);
-    }
-
-    /**
-     * Returns a list of Fragments that have opted to retain their instance across
-     * configuration changes.
-     *
-     * @deprecated use {@link #retainNestedNonConfig()} to also track retained
-     *             nested child fragments
-     */
-    @Deprecated
-    public List<Fragment> retainNonConfig() {
-        FragmentManagerNonConfig nonconf = mHost.mFragmentManager.retainNonConfig();
-        return nonconf != null ? nonconf.getFragments() : null;
-    }
-
-    /**
-     * Returns a nested tree of Fragments that have opted to retain their instance across
-     * configuration changes.
-     */
-    public FragmentManagerNonConfig retainNestedNonConfig() {
-        return mHost.mFragmentManager.retainNonConfig();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the create state.
-     * <p>Call when Fragments should be created.
-     *
-     * @see Fragment#onCreate(Bundle)
-     */
-    public void dispatchCreate() {
-        mHost.mFragmentManager.dispatchCreate();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the activity created state.
-     * <p>Call when Fragments should be informed their host has been created.
-     *
-     * @see Fragment#onActivityCreated(Bundle)
-     */
-    public void dispatchActivityCreated() {
-        mHost.mFragmentManager.dispatchActivityCreated();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the start state.
-     * <p>Call when Fragments should be started.
-     *
-     * @see Fragment#onStart()
-     */
-    public void dispatchStart() {
-        mHost.mFragmentManager.dispatchStart();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the resume state.
-     * <p>Call when Fragments should be resumed.
-     *
-     * @see Fragment#onResume()
-     */
-    public void dispatchResume() {
-        mHost.mFragmentManager.dispatchResume();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the pause state.
-     * <p>Call when Fragments should be paused.
-     *
-     * @see Fragment#onPause()
-     */
-    public void dispatchPause() {
-        mHost.mFragmentManager.dispatchPause();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the stop state.
-     * <p>Call when Fragments should be stopped.
-     *
-     * @see Fragment#onStop()
-     */
-    public void dispatchStop() {
-        mHost.mFragmentManager.dispatchStop();
-    }
-
-    public void dispatchReallyStop() {
-        mHost.mFragmentManager.dispatchReallyStop();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the destroy view state.
-     * <p>Call when the Fragment's views should be destroyed.
-     *
-     * @see Fragment#onDestroyView()
-     */
-    public void dispatchDestroyView() {
-        mHost.mFragmentManager.dispatchDestroyView();
-    }
-
-    /**
-     * Moves all Fragments managed by the controller's FragmentManager
-     * into the destroy state.
-     * <p>Call when Fragments should be destroyed.
-     *
-     * @see Fragment#onDestroy()
-     */
-    public void dispatchDestroy() {
-        mHost.mFragmentManager.dispatchDestroy();
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager know the multi-window mode of
-     * the activity changed.
-     * <p>Call when the multi-window mode of the activity changed.
-     *
-     * @see Fragment#onMultiWindowModeChanged
-     */
-    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
-        mHost.mFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode);
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager know the picture-in-picture
-     * mode of the activity changed.
-     * <p>Call when the picture-in-picture mode of the activity changed.
-     *
-     * @see Fragment#onPictureInPictureModeChanged
-     */
-    public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        mHost.mFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager
-     * know a configuration change occurred.
-     * <p>Call when there is a configuration change.
-     *
-     * @see Fragment#onConfigurationChanged(Configuration)
-     */
-    public void dispatchConfigurationChanged(Configuration newConfig) {
-        mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager
-     * know the device is in a low memory condition.
-     * <p>Call when the device is low on memory and Fragment's should trim
-     * their memory usage.
-     *
-     * @see Fragment#onLowMemory()
-     */
-    public void dispatchLowMemory() {
-        mHost.mFragmentManager.dispatchLowMemory();
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager
-     * know they should create an options menu.
-     * <p>Call when the Fragment should create an options menu.
-     *
-     * @return {@code true} if the options menu contains items to display
-     * @see Fragment#onCreateOptionsMenu(Menu, MenuInflater)
-     */
-    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        return mHost.mFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager
-     * know they should prepare their options menu for display.
-     * <p>Call immediately before displaying the Fragment's options menu.
-     *
-     * @return {@code true} if the options menu contains items to display
-     * @see Fragment#onPrepareOptionsMenu(Menu)
-     */
-    public boolean dispatchPrepareOptionsMenu(Menu menu) {
-        return mHost.mFragmentManager.dispatchPrepareOptionsMenu(menu);
-    }
-
-    /**
-     * Sends an option item selection event to the Fragments managed by the
-     * controller's FragmentManager. Once the event has been consumed,
-     * no additional handling will be performed.
-     * <p>Call immediately after an options menu item has been selected
-     *
-     * @return {@code true} if the options menu selection event was consumed
-     * @see Fragment#onOptionsItemSelected(MenuItem)
-     */
-    public boolean dispatchOptionsItemSelected(MenuItem item) {
-        return mHost.mFragmentManager.dispatchOptionsItemSelected(item);
-    }
-
-    /**
-     * Sends a context item selection event to the Fragments managed by the
-     * controller's FragmentManager. Once the event has been consumed,
-     * no additional handling will be performed.
-     * <p>Call immediately after an options menu item has been selected
-     *
-     * @return {@code true} if the context menu selection event was consumed
-     * @see Fragment#onContextItemSelected(MenuItem)
-     */
-    public boolean dispatchContextItemSelected(MenuItem item) {
-        return mHost.mFragmentManager.dispatchContextItemSelected(item);
-    }
-
-    /**
-     * Lets all Fragments managed by the controller's FragmentManager
-     * know their options menu has closed.
-     * <p>Call immediately after closing the Fragment's options menu.
-     *
-     * @see Fragment#onOptionsMenuClosed(Menu)
-     */
-    public void dispatchOptionsMenuClosed(Menu menu) {
-        mHost.mFragmentManager.dispatchOptionsMenuClosed(menu);
-    }
-
-    /**
-     * Execute any pending actions for the Fragments managed by the
-     * controller's FragmentManager.
-     * <p>Call when queued actions can be performed [eg when the
-     * Fragment moves into a start or resume state].
-     * @return {@code true} if queued actions were performed
-     */
-    public boolean execPendingActions() {
-        return mHost.mFragmentManager.execPendingActions();
-    }
-
-    /**
-     * Starts the loaders.
-     */
-    public void doLoaderStart() {
-        mHost.doLoaderStart();
-    }
-
-    /**
-     * Stops the loaders, optionally retaining their state. This is useful for keeping the
-     * loader state across configuration changes.
-     *
-     * @param retain When {@code true}, the loaders aren't stopped, but, their instances
-     * are retained in a started state
-     */
-    public void doLoaderStop(boolean retain) {
-        mHost.doLoaderStop(retain);
-    }
-
-    /**
-     * Retains the state of each of the loaders.
-     */
-    public void doLoaderRetain() {
-        mHost.doLoaderRetain();
-    }
-
-    /**
-     * Destroys the loaders and, if their state is not being retained, removes them.
-     */
-    public void doLoaderDestroy() {
-        mHost.doLoaderDestroy();
-    }
-
-    /**
-     * Lets the loaders know the host is ready to receive notifications.
-     */
-    public void reportLoaderStart() {
-        mHost.reportLoaderStart();
-    }
-
-    /**
-     * Returns a list of LoaderManagers that have opted to retain their instance across
-     * configuration changes.
-     */
-    public SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
-        return mHost.retainLoaderNonConfig();
-    }
-
-    /**
-     * Restores the saved state for all LoaderManagers. The given LoaderManager list are
-     * LoaderManager instances retained across configuration changes.
-     *
-     * @see #retainLoaderNonConfig()
-     */
-    public void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
-        mHost.restoreLoaderNonConfig(loaderManagers);
-    }
-
-    /**
-     * Dumps the current state of the loaders.
-     */
-    public void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        mHost.dumpLoaders(prefix, fd, writer, args);
-    }
-}
diff --git a/android/support/v4/app/FragmentHostCallback.java b/android/support/v4/app/FragmentHostCallback.java
deleted file mode 100644
index eeae62a..0000000
--- a/android/support/v4/app/FragmentHostCallback.java
+++ /dev/null
@@ -1,371 +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.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.util.SimpleArrayMap;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Integration points with the Fragment host.
- * <p>
- * Fragments may be hosted by any object; such as an {@link Activity}. In order to
- * host fragments, implement {@link FragmentHostCallback}, overriding the methods
- * applicable to the host.
- */
-public abstract class FragmentHostCallback<E> extends FragmentContainer {
-    private final Activity mActivity;
-    final Context mContext;
-    private final Handler mHandler;
-    final int mWindowAnimations;
-    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
-    /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */
-    private SimpleArrayMap<String, LoaderManager> mAllLoaderManagers;
-    /** Whether or not fragment loaders should retain their state */
-    private boolean mRetainLoaders;
-    /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */
-    private LoaderManagerImpl mLoaderManager;
-    private boolean mCheckedForLoaderManager;
-    /** Whether or not the fragment host loader manager was started */
-    private boolean mLoadersStarted;
-
-    public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
-        this(context instanceof Activity ? (Activity) context : null, context, handler,
-                windowAnimations);
-    }
-
-    FragmentHostCallback(FragmentActivity activity) {
-        this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
-    }
-
-    FragmentHostCallback(Activity activity, Context context, Handler handler,
-            int windowAnimations) {
-        mActivity = activity;
-        mContext = context;
-        mHandler = handler;
-        mWindowAnimations = windowAnimations;
-    }
-
-    /**
-     * Print internal state into the given stream.
-     *
-     * @param prefix Desired prefix to prepend at each line of output.
-     * @param fd The raw file descriptor that the dump is being sent to.
-     * @param writer The PrintWriter to which you should dump your state. This will be closed
-     *                  for you after you return.
-     * @param args additional arguments to the dump request.
-     */
-    public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-    }
-
-    /**
-     * Return {@code true} if the fragment's state needs to be saved.
-     */
-    public boolean onShouldSaveFragmentState(Fragment fragment) {
-        return true;
-    }
-
-    /**
-     * Return a {@link LayoutInflater}.
-     * See {@link Activity#getLayoutInflater()}.
-     */
-    @NonNull
-    public LayoutInflater onGetLayoutInflater() {
-        return LayoutInflater.from(mContext);
-    }
-
-    /**
-     * Return the object that's currently hosting the fragment. If a {@link Fragment}
-     * is hosted by a {@link FragmentActivity}, the object returned here should be
-     * the same object returned from {@link Fragment#getActivity()}.
-     */
-    @Nullable
-    public abstract E onGetHost();
-
-    /**
-     * Invalidates the activity's options menu.
-     * See {@link FragmentActivity#supportInvalidateOptionsMenu()}
-     */
-    public void onSupportInvalidateOptionsMenu() {
-    }
-
-    /**
-     * Starts a new {@link Activity} from the given fragment.
-     * See {@link FragmentActivity#startActivityForResult(Intent, int)}.
-     */
-    public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
-        onStartActivityFromFragment(fragment, intent, requestCode, null);
-    }
-
-    /**
-     * Starts a new {@link Activity} from the given fragment.
-     * See {@link FragmentActivity#startActivityForResult(Intent, int, Bundle)}.
-     */
-    public void onStartActivityFromFragment(
-            Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
-        if (requestCode != -1) {
-            throw new IllegalStateException(
-                    "Starting activity with a requestCode requires a FragmentActivity host");
-        }
-        mContext.startActivity(intent);
-    }
-
-    /**
-     * Starts a new {@link IntentSender} from the given fragment.
-     * See {@link Activity#startIntentSender(IntentSender, Intent, int, int, int, Bundle)}.
-     */
-    public void onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent,
-            int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
-            int extraFlags, Bundle options) throws IntentSender.SendIntentException {
-        if (requestCode != -1) {
-            throw new IllegalStateException(
-                    "Starting intent sender with a requestCode requires a FragmentActivity host");
-        }
-        ActivityCompat.startIntentSenderForResult(mActivity, intent, requestCode, fillInIntent,
-                flagsMask, flagsValues, extraFlags, options);
-    }
-
-    /**
-     * Requests permissions from the given fragment.
-     * See {@link FragmentActivity#requestPermissions(String[], int)}
-     */
-    public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
-            @NonNull String[] permissions, int requestCode) {
-    }
-
-    /**
-     * Checks whether to show permission rationale UI from a fragment.
-     * See {@link FragmentActivity#shouldShowRequestPermissionRationale(String)}
-     */
-    public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
-        return false;
-    }
-
-    /**
-     * Return {@code true} if there are window animations.
-     */
-    public boolean onHasWindowAnimations() {
-        return true;
-    }
-
-    /**
-     * Return the window animations.
-     */
-    public int onGetWindowAnimations() {
-        return mWindowAnimations;
-    }
-
-    @Nullable
-    @Override
-    public View onFindViewById(int id) {
-        return null;
-    }
-
-    @Override
-    public boolean onHasView() {
-        return true;
-    }
-
-    Activity getActivity() {
-        return mActivity;
-    }
-
-    Context getContext() {
-        return mContext;
-    }
-
-    Handler getHandler() {
-        return mHandler;
-    }
-
-    FragmentManagerImpl getFragmentManagerImpl() {
-        return mFragmentManager;
-    }
-
-    LoaderManagerImpl getLoaderManagerImpl() {
-        if (mLoaderManager != null) {
-            return mLoaderManager;
-        }
-        mCheckedForLoaderManager = true;
-        mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
-        return mLoaderManager;
-    }
-
-    void inactivateFragment(String who) {
-        //Log.v(TAG, "invalidateSupportFragment: who=" + who);
-        if (mAllLoaderManagers != null) {
-            LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
-            if (lm != null && !lm.mRetaining) {
-                lm.doDestroy();
-                mAllLoaderManagers.remove(who);
-            }
-        }
-    }
-
-    void onAttachFragment(Fragment fragment) {
-    }
-
-    boolean getRetainLoaders() {
-        return mRetainLoaders;
-    }
-
-    void doLoaderStart() {
-        if (mLoadersStarted) {
-            return;
-        }
-        mLoadersStarted = true;
-
-        if (mLoaderManager != null) {
-            mLoaderManager.doStart();
-        } else if (!mCheckedForLoaderManager) {
-            mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
-            // the returned loader manager may be a new one, so we have to start it
-            if ((mLoaderManager != null) && (!mLoaderManager.mStarted)) {
-                mLoaderManager.doStart();
-            }
-        }
-        mCheckedForLoaderManager = true;
-    }
-
-    // retain -- whether to stop the loader or retain it
-    void doLoaderStop(boolean retain) {
-        mRetainLoaders = retain;
-
-        if (mLoaderManager == null) {
-            return;
-        }
-
-        if (!mLoadersStarted) {
-            return;
-        }
-        mLoadersStarted = false;
-
-        if (retain) {
-            mLoaderManager.doRetain();
-        } else {
-            mLoaderManager.doStop();
-        }
-    }
-
-    void doLoaderRetain() {
-        if (mLoaderManager == null) {
-            return;
-        }
-        mLoaderManager.doRetain();
-    }
-
-    void doLoaderDestroy() {
-        if (mLoaderManager == null) {
-            return;
-        }
-        mLoaderManager.doDestroy();
-    }
-
-    void reportLoaderStart() {
-        if (mAllLoaderManagers != null) {
-            final int N = mAllLoaderManagers.size();
-            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
-            for (int i=N-1; i>=0; i--) {
-                loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
-            }
-            for (int i=0; i<N; i++) {
-                LoaderManagerImpl lm = loaders[i];
-                lm.finishRetain();
-                lm.doReportStart();
-            }
-        }
-    }
-
-    LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
-        if (mAllLoaderManagers == null) {
-            mAllLoaderManagers = new SimpleArrayMap<String, LoaderManager>();
-        }
-        LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
-        if (lm == null && create) {
-            lm = new LoaderManagerImpl(who, this, started);
-            mAllLoaderManagers.put(who, lm);
-        } else if (started && lm != null && !lm.mStarted) {
-            lm.doStart();
-        }
-        return lm;
-    }
-
-    SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
-        boolean retainLoaders = false;
-        if (mAllLoaderManagers != null) {
-            // Restart any loader managers that were already stopped so that they
-            // will be ready to retain
-            final int N = mAllLoaderManagers.size();
-            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
-            for (int i=N-1; i>=0; i--) {
-                loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
-            }
-            final boolean doRetainLoaders = getRetainLoaders();
-            for (int i=0; i<N; i++) {
-                LoaderManagerImpl lm = loaders[i];
-                if (!lm.mRetaining && doRetainLoaders) {
-                    if (!lm.mStarted) {
-                        lm.doStart();
-                    }
-                    lm.doRetain();
-                }
-                if (lm.mRetaining) {
-                    retainLoaders = true;
-                } else {
-                    lm.doDestroy();
-                    mAllLoaderManagers.remove(lm.mWho);
-                }
-            }
-        }
-
-        if (retainLoaders) {
-            return mAllLoaderManagers;
-        }
-        return null;
-    }
-
-    void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
-        if (loaderManagers != null) {
-            for (int i = 0, N = loaderManagers.size(); i < N; i++) {
-                ((LoaderManagerImpl) loaderManagers.valueAt(i)).updateHostController(this);
-            }
-        }
-        mAllLoaderManagers = loaderManagers;
-    }
-
-    void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        writer.print(prefix); writer.print("mLoadersStarted=");
-        writer.println(mLoadersStarted);
-        if (mLoaderManager != null) {
-            writer.print(prefix); writer.print("Loader Manager ");
-            writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
-            writer.println(":");
-            mLoaderManager.dump(prefix + "  ", fd, writer, args);
-        }
-    }
-}
diff --git a/android/support/v4/app/FragmentManager.java b/android/support/v4/app/FragmentManager.java
deleted file mode 100644
index 16103f8..0000000
--- a/android/support/v4/app/FragmentManager.java
+++ /dev/null
@@ -1,4006 +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 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;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcel;
-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.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.ViewCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.Animation.AnimationListener;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.ScaleAnimation;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Static library support version of the framework's {@link android.app.FragmentManager}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework {@link FragmentManager}
- * documentation for a class overview.
- *
- * <p>Your activity must derive from {@link FragmentActivity} to use this. From such an activity,
- * you can acquire the {@link FragmentManager} by calling
- * {@link FragmentActivity#getSupportFragmentManager}.
- */
-public abstract class FragmentManager {
-    /**
-     * Representation of an entry on the fragment back stack, as created
-     * with {@link FragmentTransaction#addToBackStack(String)
-     * FragmentTransaction.addToBackStack()}.  Entries can later be
-     * retrieved with {@link FragmentManager#getBackStackEntryAt(int)
-     * FragmentManager.getBackStackEntryAt()}.
-     *
-     * <p>Note that you should never hold on to a BackStackEntry object;
-     * the identifier as returned by {@link #getId} is the only thing that
-     * will be persisted across activity instances.
-     */
-    public interface BackStackEntry {
-        /**
-         * Return the unique identifier for the entry.  This is the only
-         * representation of the entry that will persist across activity
-         * instances.
-         */
-        public int getId();
-
-        /**
-         * Get the name that was supplied to
-         * {@link FragmentTransaction#addToBackStack(String)
-         * FragmentTransaction.addToBackStack(String)} when creating this entry.
-         */
-        public String getName();
-
-        /**
-         * Return the full bread crumb title resource identifier for the entry,
-         * or 0 if it does not have one.
-         */
-        @StringRes
-        public int getBreadCrumbTitleRes();
-
-        /**
-         * Return the short bread crumb title resource identifier for the entry,
-         * or 0 if it does not have one.
-         */
-        @StringRes
-        public int getBreadCrumbShortTitleRes();
-
-        /**
-         * Return the full bread crumb title for the entry, or null if it
-         * does not have one.
-         */
-        public CharSequence getBreadCrumbTitle();
-
-        /**
-         * Return the short bread crumb title for the entry, or null if it
-         * does not have one.
-         */
-        public CharSequence getBreadCrumbShortTitle();
-    }
-
-    /**
-     * Interface to watch for changes to the back stack.
-     */
-    public interface OnBackStackChangedListener {
-        /**
-         * Called whenever the contents of the back stack change.
-         */
-        public void onBackStackChanged();
-    }
-
-    /**
-     * Start a series of edit operations on the Fragments associated with
-     * this FragmentManager.
-     *
-     * <p>Note: A fragment transaction can only be created/committed prior
-     * to an activity saving its state.  If you try to commit a transaction
-     * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()}
-     * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart}
-     * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error.
-     * This is because the framework takes care of saving your current fragments
-     * in the state, and if changes are made after the state is saved then they
-     * will be lost.</p>
-     */
-    public abstract FragmentTransaction beginTransaction();
-
-    /**
-     * @hide -- remove once prebuilts are in.
-     * @deprecated
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Deprecated
-    public FragmentTransaction openTransaction() {
-        return beginTransaction();
-    }
-
-    /**
-     * After a {@link FragmentTransaction} is committed with
-     * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it
-     * is scheduled to be executed asynchronously on the process's main thread.
-     * If you want to immediately executing any such pending operations, you
-     * can call this function (only from the main thread) to do so.  Note that
-     * all callbacks and other related behavior will be done from within this
-     * call, so be careful about where this is called from.
-     *
-     * <p>If you are committing a single transaction that does not modify the
-     * fragment back stack, strongly consider using
-     * {@link FragmentTransaction#commitNow()} instead. This can help avoid
-     * unwanted side effects when other code in your app has pending committed
-     * transactions that expect different timing.</p>
-     * <p>
-     * This also forces the start of any postponed Transactions where
-     * {@link Fragment#postponeEnterTransition()} has been called.
-     *
-     * @return Returns true if there were any pending transactions to be
-     * executed.
-     */
-    public abstract boolean executePendingTransactions();
-
-    /**
-     * Finds a fragment that was identified by the given id either when inflated
-     * from XML or as the container ID when added in a transaction.  This first
-     * searches through fragments that are currently added to the manager's
-     * activity; if no such fragment is found, then all fragments currently
-     * on the back stack associated with this ID are searched.
-     * @return The fragment if found or null otherwise.
-     */
-    public abstract Fragment findFragmentById(@IdRes int id);
-
-    /**
-     * Finds a fragment that was identified by the given tag either when inflated
-     * from XML or as supplied when added in a transaction.  This first
-     * searches through fragments that are currently added to the manager's
-     * activity; if no such fragment is found, then all fragments currently
-     * on the back stack are searched.
-     * @return The fragment if found or null otherwise.
-     */
-    public abstract Fragment findFragmentByTag(String tag);
-
-    /**
-     * Flag for {@link #popBackStack(String, int)}
-     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
-     * a back stack entry has been supplied, then all matching entries will
-     * be consumed until one that doesn't match is found or the bottom of
-     * the stack is reached.  Otherwise, all entries up to but not including that entry
-     * will be removed.
-     */
-    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
-
-    /**
-     * Pop the top state off the back stack.  Returns true if there was one
-     * to pop, else false.  This function is asynchronous -- it enqueues the
-     * request to pop, but the action will not be performed until the application
-     * returns to its event loop.
-     */
-    public abstract void popBackStack();
-
-    /**
-     * Like {@link #popBackStack()}, but performs the operation immediately
-     * inside of the call.  This is like calling {@link #executePendingTransactions()}
-     * afterwards without forcing the start of postponed Transactions.
-     * @return Returns true if there was something popped, else false.
-     */
-    public abstract boolean popBackStackImmediate();
-
-    /**
-     * Pop the last fragment transition from the manager's fragment
-     * back stack.  If there is nothing to pop, false is returned.
-     * This function is asynchronous -- it enqueues the
-     * request to pop, but the action will not be performed until the application
-     * returns to its event loop.
-     *
-     * @param name If non-null, this is the name of a previous back state
-     * to look for; if found, all states up to that state will be popped.  The
-     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
-     * the named state itself is popped. If null, only the top state is popped.
-     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
-     */
-    public abstract void popBackStack(String name, int flags);
-
-    /**
-     * Like {@link #popBackStack(String, int)}, but performs the operation immediately
-     * inside of the call.  This is like calling {@link #executePendingTransactions()}
-     * afterwards without forcing the start of postponed Transactions.
-     * @return Returns true if there was something popped, else false.
-     */
-    public abstract boolean popBackStackImmediate(String name, int flags);
-
-    /**
-     * Pop all back stack states up to the one with the given identifier.
-     * This function is asynchronous -- it enqueues the
-     * request to pop, but the action will not be performed until the application
-     * returns to its event loop.
-     *
-     * @param id Identifier of the stated to be popped. If no identifier exists,
-     * false is returned.
-     * The identifier is the number returned by
-     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
-     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
-     * the named state itself is popped.
-     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
-     */
-    public abstract void popBackStack(int id, int flags);
-
-    /**
-     * Like {@link #popBackStack(int, int)}, but performs the operation immediately
-     * inside of the call.  This is like calling {@link #executePendingTransactions()}
-     * afterwards without forcing the start of postponed Transactions.
-     * @return Returns true if there was something popped, else false.
-     */
-    public abstract boolean popBackStackImmediate(int id, int flags);
-
-    /**
-     * Return the number of entries currently in the back stack.
-     */
-    public abstract int getBackStackEntryCount();
-
-    /**
-     * Return the BackStackEntry at index <var>index</var> in the back stack;
-     * entries start index 0 being the bottom of the stack.
-     */
-    public abstract BackStackEntry getBackStackEntryAt(int index);
-
-    /**
-     * Add a new listener for changes to the fragment back stack.
-     */
-    public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
-
-    /**
-     * Remove a listener that was previously added with
-     * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
-     */
-    public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
-
-    /**
-     * Put a reference to a fragment in a Bundle.  This Bundle can be
-     * persisted as saved state, and when later restoring
-     * {@link #getFragment(Bundle, String)} will return the current
-     * instance of the same fragment.
-     *
-     * @param bundle The bundle in which to put the fragment reference.
-     * @param key The name of the entry in the bundle.
-     * @param fragment The Fragment whose reference is to be stored.
-     */
-    public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
-
-    /**
-     * Retrieve the current Fragment instance for a reference previously
-     * placed with {@link #putFragment(Bundle, String, Fragment)}.
-     *
-     * @param bundle The bundle from which to retrieve the fragment reference.
-     * @param key The name of the entry in the bundle.
-     * @return Returns the current Fragment instance that is associated with
-     * the given reference.
-     */
-    public abstract Fragment getFragment(Bundle bundle, String key);
-
-    /**
-     * Get a list of all fragments that are currently added to the FragmentManager.
-     * This may include those that are hidden as well as those that are shown.
-     * This will not include any fragments only in the back stack, or fragments that
-     * are detached or removed.
-     * <p>
-     * The order of the fragments in the list is the order in which they were
-     * added or attached.
-     *
-     * @return A list of all fragments that are added to the FragmentManager.
-     */
-    public abstract List<Fragment> getFragments();
-
-    /**
-     * Save the current instance state of the given Fragment.  This can be
-     * used later when creating a new instance of the Fragment and adding
-     * it to the fragment manager, to have it create itself to match the
-     * current state returned here.  Note that there are limits on how
-     * this can be used:
-     *
-     * <ul>
-     * <li>The Fragment must currently be attached to the FragmentManager.
-     * <li>A new Fragment created using this saved state must be the same class
-     * type as the Fragment it was created from.
-     * <li>The saved state can not contain dependencies on other fragments --
-     * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to
-     * store a fragment reference because that reference may not be valid when
-     * this saved state is later used.  Likewise the Fragment's target and
-     * result code are not included in this state.
-     * </ul>
-     *
-     * @param f The Fragment whose state is to be saved.
-     * @return The generated state.  This will be null if there was no
-     * interesting state created by the fragment.
-     */
-    public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
-
-    /**
-     * Returns true if the final {@link android.app.Activity#onDestroy() Activity.onDestroy()}
-     * call has been made on the FragmentManager's Activity, so this instance is now dead.
-     */
-    public abstract boolean isDestroyed();
-
-    /**
-     * Registers a {@link FragmentLifecycleCallbacks} to listen to fragment lifecycle events
-     * happening in this FragmentManager. All registered callbacks will be automatically
-     * unregistered when this FragmentManager is destroyed.
-     *
-     * @param cb Callbacks to register
-     * @param recursive true to automatically register this callback for all child FragmentManagers
-     */
-    public abstract void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb,
-            boolean recursive);
-
-    /**
-     * Unregisters a previously registered {@link FragmentLifecycleCallbacks}. If the callback
-     * was not previously registered this call has no effect. All registered callbacks will be
-     * automatically unregistered when this FragmentManager is destroyed.
-     *
-     * @param cb Callbacks to unregister
-     */
-    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.
-     * @param fd The raw file descriptor that the dump is being sent to.
-     * @param writer A PrintWriter to which the dump is to be set.
-     * @param args Additional arguments to the dump request.
-     */
-    public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
-
-    /**
-     * Control whether the framework's internal fragment manager debugging
-     * logs are turned on.  If enabled, you will see output in logcat as
-     * the framework performs fragment operations.
-     */
-    public static void enableDebugLogging(boolean enabled) {
-        FragmentManagerImpl.DEBUG = enabled;
-    }
-
-    /**
-     * 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 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
-         * @param context Context that the Fragment is being attached to
-         */
-        public void onFragmentPreAttached(FragmentManager fm, Fragment f, Context context) {}
-
-        /**
-         * Called after the fragment has been attached to its host. Its host will have had
-         * <code>onAttachFragment</code> called before this call happens.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         * @param context Context that the Fragment was attached to
-         */
-        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.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         * @param savedInstanceState Saved instance bundle from a previous instance
-         */
-        public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given
-         * fragment instance, though the fragment may be attached and detached multiple times.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         * @param savedInstanceState Saved instance bundle from a previous instance
-         */
-        public void onFragmentActivityCreated(FragmentManager fm, Fragment f,
-                Bundle savedInstanceState) {}
-
-        /**
-         * Called after the fragment has returned a non-null view from the FragmentManager's
-         * request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment that created and owns the view
-         * @param v View returned by the fragment
-         * @param savedInstanceState Saved instance bundle from a previous instance
-         */
-        public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,
-                Bundle savedInstanceState) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onStart()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentStarted(FragmentManager fm, Fragment f) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onResume()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentResumed(FragmentManager fm, Fragment f) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onPause()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentPaused(FragmentManager fm, Fragment f) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onStop()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentStopped(FragmentManager fm, Fragment f) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onSaveInstanceState(Bundle)}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         * @param outState Saved state bundle for the fragment
-         */
-        public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onDestroyView()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onDestroy()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}
-
-        /**
-         * Called after the fragment has returned from the FragmentManager's call to
-         * {@link Fragment#onDetach()}.
-         *
-         * @param fm Host FragmentManager
-         * @param f Fragment changing state
-         */
-        public void onFragmentDetached(FragmentManager fm, Fragment f) {}
-    }
-}
-
-final class FragmentManagerState implements Parcelable {
-    FragmentState[] mActive;
-    int[] mAdded;
-    BackStackState[] mBackStack;
-    int mPrimaryNavActiveIndex = -1;
-    int mNextFragmentIndex;
-
-    public FragmentManagerState() {
-    }
-
-    public FragmentManagerState(Parcel in) {
-        mActive = in.createTypedArray(FragmentState.CREATOR);
-        mAdded = in.createIntArray();
-        mBackStack = in.createTypedArray(BackStackState.CREATOR);
-        mPrimaryNavActiveIndex = in.readInt();
-        mNextFragmentIndex = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeTypedArray(mActive, flags);
-        dest.writeIntArray(mAdded);
-        dest.writeTypedArray(mBackStack, flags);
-        dest.writeInt(mPrimaryNavActiveIndex);
-        dest.writeInt(mNextFragmentIndex);
-    }
-
-    public static final Parcelable.Creator<FragmentManagerState> CREATOR
-            = new Parcelable.Creator<FragmentManagerState>() {
-        @Override
-        public FragmentManagerState createFromParcel(Parcel in) {
-            return new FragmentManagerState(in);
-        }
-
-        @Override
-        public FragmentManagerState[] newArray(int size) {
-            return new FragmentManagerState[size];
-        }
-    };
-}
-
-/**
- * Container for fragments associated with an activity.
- */
-final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
-    static boolean DEBUG = false;
-    static final String TAG = "FragmentManager";
-
-    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";
-
-    ArrayList<OpGenerator> mPendingActions;
-    boolean mExecutingActions;
-
-    int mNextFragmentIndex = 0;
-
-    final ArrayList<Fragment> mAdded = new ArrayList<>();
-    SparseArray<Fragment> mActive;
-    ArrayList<BackStackRecord> mBackStack;
-    ArrayList<Fragment> mCreatedMenus;
-
-    // Must be accessed while locked.
-    ArrayList<BackStackRecord> mBackStackIndices;
-    ArrayList<Integer> mAvailBackStackIndices;
-
-    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
-    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;
-
-    boolean mNeedMenuInvalidate;
-    boolean mStateSaved;
-    boolean mDestroyed;
-    String mNoTransactionsBecause;
-    boolean mHavePendingDeferredStart;
-
-    // Temporary vars for removing redundant operations in BackStackRecords:
-    ArrayList<BackStackRecord> mTmpRecords;
-    ArrayList<Boolean> mTmpIsPop;
-    ArrayList<Fragment> mTmpAddedFragments;
-
-    // Temporary vars for state save and restore.
-    Bundle mStateBundle = null;
-    SparseArray<Parcelable> mStateArray = null;
-
-    // Postponed transactions.
-    ArrayList<StartEnterTransitionListener> mPostponedTransactions;
-
-    // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved()
-    FragmentManagerNonConfig mSavedNonConfig;
-
-    Runnable mExecCommit = new Runnable() {
-        @Override
-        public void run() {
-            execPendingActions();
-        }
-    };
-
-    static boolean modifiesAlpha(AnimationOrAnimator anim) {
-        if (anim.animation instanceof AlphaAnimation) {
-            return true;
-        } 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, AnimationOrAnimator anim) {
-        if (v == null || anim == null) {
-            return false;
-        }
-        return Build.VERSION.SDK_INT >= 19
-                && v.getLayerType() == View.LAYER_TYPE_NONE
-                && ViewCompat.hasOverlappingRendering(v)
-                && modifiesAlpha(anim);
-    }
-
-    private void throwException(RuntimeException ex) {
-        Log.e(TAG, ex.getMessage());
-        Log.e(TAG, "Activity state:");
-        LogWriter logw = new LogWriter(TAG);
-        PrintWriter pw = new PrintWriter(logw);
-        if (mHost != null) {
-            try {
-                mHost.onDump("  ", null, pw, new String[] { });
-            } catch (Exception e) {
-                Log.e(TAG, "Failed dumping state", e);
-            }
-        } else {
-            try {
-                dump("  ", null, pw, new String[] { });
-            } catch (Exception e) {
-                Log.e(TAG, "Failed dumping state", e);
-            }
-        }
-        throw ex;
-    }
-
-    @Override
-    public FragmentTransaction beginTransaction() {
-        return new BackStackRecord(this);
-    }
-
-    @Override
-    public boolean executePendingTransactions() {
-        boolean updates = execPendingActions();
-        forcePostponedTransactions();
-        return updates;
-    }
-
-    @Override
-    public void popBackStack() {
-        enqueueAction(new PopBackStackState(null, -1, 0), false);
-    }
-
-    @Override
-    public boolean popBackStackImmediate() {
-        checkStateLoss();
-        return popBackStackImmediate(null, -1, 0);
-    }
-
-    @Override
-    public void popBackStack(final String name, final int flags) {
-        enqueueAction(new PopBackStackState(name, -1, flags), false);
-    }
-
-    @Override
-    public boolean popBackStackImmediate(String name, int flags) {
-        checkStateLoss();
-        return popBackStackImmediate(name, -1, flags);
-    }
-
-    @Override
-    public void popBackStack(final int id, final int flags) {
-        if (id < 0) {
-            throw new IllegalArgumentException("Bad id: " + id);
-        }
-        enqueueAction(new PopBackStackState(null, id, flags), false);
-    }
-
-    @Override
-    public boolean popBackStackImmediate(int id, int flags) {
-        checkStateLoss();
-        execPendingActions();
-        if (id < 0) {
-            throw new IllegalArgumentException("Bad id: " + id);
-        }
-        return popBackStackImmediate(null, id, flags);
-    }
-
-    /**
-     * Used by all public popBackStackImmediate methods, this executes pending transactions and
-     * returns true if the pop action did anything, regardless of what other pending
-     * transactions did.
-     *
-     * @return true if the pop operation did anything or false otherwise.
-     */
-    private boolean popBackStackImmediate(String name, int id, int flags) {
-        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 {
-                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
-            } finally {
-                cleanupExec();
-            }
-        }
-
-        doPendingDeferredStart();
-        burpActive();
-        return executePop;
-    }
-
-    @Override
-    public int getBackStackEntryCount() {
-        return mBackStack != null ? mBackStack.size() : 0;
-    }
-
-    @Override
-    public BackStackEntry getBackStackEntryAt(int index) {
-        return mBackStack.get(index);
-    }
-
-    @Override
-    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
-        if (mBackStackChangeListeners == null) {
-            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
-        }
-        mBackStackChangeListeners.add(listener);
-    }
-
-    @Override
-    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
-        if (mBackStackChangeListeners != null) {
-            mBackStackChangeListeners.remove(listener);
-        }
-    }
-
-    @Override
-    public void putFragment(Bundle bundle, String key, Fragment fragment) {
-        if (fragment.mIndex < 0) {
-            throwException(new IllegalStateException("Fragment " + fragment
-                    + " is not currently in the FragmentManager"));
-        }
-        bundle.putInt(key, fragment.mIndex);
-    }
-
-    @Override
-    public Fragment getFragment(Bundle bundle, String key) {
-        int index = bundle.getInt(key, -1);
-        if (index == -1) {
-            return null;
-        }
-        Fragment f = mActive.get(index);
-        if (f == null) {
-            throwException(new IllegalStateException("Fragment no longer exists for key "
-                    + key + ": index " + index));
-        }
-        return f;
-    }
-
-    @Override
-    public List<Fragment> getFragments() {
-        if (mAdded.isEmpty()) {
-            return Collections.EMPTY_LIST;
-        }
-        synchronized (mAdded) {
-            return (List<Fragment>) mAdded.clone();
-        }
-    }
-
-    /**
-     * This is used by FragmentController to get the Active fragments.
-     *
-     * @return A list of active fragments in the fragment manager, including those that are in the
-     * back stack.
-     */
-    List<Fragment> getActiveFragments() {
-        if (mActive == null) {
-            return null;
-        }
-        final int count = mActive.size();
-        ArrayList<Fragment> fragments = new ArrayList<>(count);
-        for (int i = 0; i < count; i++) {
-            fragments.add(mActive.valueAt(i));
-        }
-        return fragments;
-    }
-
-    /**
-     * Used by FragmentController to get the number of Active Fragments.
-     *
-     * @return The number of active fragments.
-     */
-    int getActiveFragmentCount() {
-        if (mActive == null) {
-            return 0;
-        }
-        return mActive.size();
-    }
-
-    @Override
-    public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
-        if (fragment.mIndex < 0) {
-            throwException( new IllegalStateException("Fragment " + fragment
-                    + " is not currently in the FragmentManager"));
-        }
-        if (fragment.mState > Fragment.INITIALIZING) {
-            Bundle result = saveFragmentBasicState(fragment);
-            return result != null ? new Fragment.SavedState(result) : null;
-        }
-        return null;
-    }
-
-    @Override
-    public boolean isDestroyed() {
-        return mDestroyed;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("FragmentManager{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(" in ");
-        if (mParent != null) {
-            DebugUtils.buildShortClassTag(mParent, sb);
-        } else {
-            DebugUtils.buildShortClassTag(mHost, sb);
-        }
-        sb.append("}}");
-        return sb.toString();
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        String innerPrefix = prefix + "    ";
-
-        int N;
-        if (mActive != null) {
-            N = mActive.size();
-            if (N > 0) {
-                writer.print(prefix); writer.print("Active Fragments in ");
-                        writer.print(Integer.toHexString(System.identityHashCode(this)));
-                        writer.println(":");
-                for (int i=0; i<N; i++) {
-                    Fragment f = mActive.valueAt(i);
-                    writer.print(prefix); writer.print("  #"); writer.print(i);
-                            writer.print(": "); writer.println(f);
-                    if (f != null) {
-                        f.dump(innerPrefix, fd, writer, args);
-                    }
-                }
-            }
-        }
-
-        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());
-            }
-        }
-
-        if (mCreatedMenus != null) {
-            N = mCreatedMenus.size();
-            if (N > 0) {
-                writer.print(prefix); writer.println("Fragments Created Menus:");
-                for (int i=0; i<N; i++) {
-                    Fragment f = mCreatedMenus.get(i);
-                    writer.print(prefix); writer.print("  #"); writer.print(i);
-                            writer.print(": "); writer.println(f.toString());
-                }
-            }
-        }
-
-        if (mBackStack != null) {
-            N = mBackStack.size();
-            if (N > 0) {
-                writer.print(prefix); writer.println("Back Stack:");
-                for (int i=0; i<N; i++) {
-                    BackStackRecord bs = mBackStack.get(i);
-                    writer.print(prefix); writer.print("  #"); writer.print(i);
-                            writer.print(": "); writer.println(bs.toString());
-                    bs.dump(innerPrefix, fd, writer, args);
-                }
-            }
-        }
-
-        synchronized (this) {
-            if (mBackStackIndices != null) {
-                N = mBackStackIndices.size();
-                if (N > 0) {
-                    writer.print(prefix); writer.println("Back Stack Indices:");
-                    for (int i=0; i<N; i++) {
-                        BackStackRecord bs = mBackStackIndices.get(i);
-                        writer.print(prefix); writer.print("  #"); writer.print(i);
-                                writer.print(": "); writer.println(bs);
-                    }
-                }
-            }
-
-            if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) {
-                writer.print(prefix); writer.print("mAvailBackStackIndices: ");
-                        writer.println(Arrays.toString(mAvailBackStackIndices.toArray()));
-            }
-        }
-
-        if (mPendingActions != null) {
-            N = mPendingActions.size();
-            if (N > 0) {
-                writer.print(prefix); writer.println("Pending Actions:");
-                for (int i=0; i<N; i++) {
-                    OpGenerator r = mPendingActions.get(i);
-                    writer.print(prefix); writer.print("  #"); writer.print(i);
-                            writer.print(": "); writer.println(r);
-                }
-            }
-        }
-
-        writer.print(prefix); writer.println("FragmentManager misc state:");
-        writer.print(prefix); writer.print("  mHost="); writer.println(mHost);
-        writer.print(prefix); writer.print("  mContainer="); writer.println(mContainer);
-        if (mParent != null) {
-            writer.print(prefix); writer.print("  mParent="); writer.println(mParent);
-        }
-        writer.print(prefix); writer.print("  mCurState="); writer.print(mCurState);
-                writer.print(" mStateSaved="); writer.print(mStateSaved);
-                writer.print(" mDestroyed="); writer.println(mDestroyed);
-        if (mNeedMenuInvalidate) {
-            writer.print(prefix); writer.print("  mNeedMenuInvalidate=");
-                    writer.println(mNeedMenuInvalidate);
-        }
-        if (mNoTransactionsBecause != null) {
-            writer.print(prefix); writer.print("  mNoTransactionsBecause=");
-                    writer.println(mNoTransactionsBecause);
-        }
-    }
-
-    static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
-    static final Interpolator DECELERATE_CUBIC = new DecelerateInterpolator(1.5f);
-    static final Interpolator ACCELERATE_QUINT = new AccelerateInterpolator(2.5f);
-    static final Interpolator ACCELERATE_CUBIC = new AccelerateInterpolator(1.5f);
-
-    static final int ANIM_DUR = 220;
-
-    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,
-                Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f);
-        scale.setInterpolator(DECELERATE_QUINT);
-        scale.setDuration(ANIM_DUR);
-        set.addAnimation(scale);
-        AlphaAnimation alpha = new AlphaAnimation(startAlpha, endAlpha);
-        alpha.setInterpolator(DECELERATE_CUBIC);
-        alpha.setDuration(ANIM_DUR);
-        set.addAnimation(alpha);
-        return new AnimationOrAnimator(set);
-    }
-
-    static AnimationOrAnimator makeFadeAnimation(Context context, float start, float end) {
-        AlphaAnimation anim = new AlphaAnimation(start, end);
-        anim.setInterpolator(DECELERATE_CUBIC);
-        anim.setDuration(ANIM_DUR);
-        return new AnimationOrAnimator(anim);
-    }
-
-    AnimationOrAnimator loadAnimation(Fragment fragment, int transit, boolean enter,
-            int transitionStyle) {
-        int nextAnim = fragment.getNextAnim();
-        Animation animation = fragment.onCreateAnimation(transit, enter, nextAnim);
-        if (animation != null) {
-            return new AnimationOrAnimator(animation);
-        }
-
-        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);
-                    }
-                }
-            }
-        }
-
-        if (transit == 0) {
-            return null;
-        }
-
-        int styleIndex = transitToStyleIndex(transit, enter);
-        if (styleIndex < 0) {
-            return null;
-        }
-
-        switch (styleIndex) {
-            case ANIM_STYLE_OPEN_ENTER:
-                return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1);
-            case ANIM_STYLE_OPEN_EXIT:
-                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0);
-            case ANIM_STYLE_CLOSE_ENTER:
-                return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1);
-            case ANIM_STYLE_CLOSE_EXIT:
-                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0);
-            case ANIM_STYLE_FADE_ENTER:
-                return makeFadeAnimation(mHost.getContext(), 0, 1);
-            case ANIM_STYLE_FADE_EXIT:
-                return makeFadeAnimation(mHost.getContext(), 1, 0);
-        }
-
-        // TODO: remove or fix transitionStyle -- it apparently never worked.
-        if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
-            transitionStyle = mHost.onGetWindowAnimations();
-        }
-        if (transitionStyle == 0) {
-            return null;
-        }
-
-        //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
-        //        com.android.internal.R.styleable.FragmentAnimation);
-        //int anim = attrs.getResourceId(styleIndex, 0);
-        //attrs.recycle();
-
-        //if (anim == 0) {
-        //    return null;
-        //}
-
-        //return AnimatorInflater.loadAnimator(mActivity, anim);
-        return null;
-    }
-
-    public void performPendingDeferredStart(Fragment f) {
-        if (f.mDeferStart) {
-            if (mExecutingActions) {
-                // Wait until we're done executing our pending transactions
-                mHavePendingDeferredStart = true;
-                return;
-            }
-            f.mDeferStart = false;
-            moveToState(f, mCurState, 0, 0, false);
-        }
-    }
-
-    /**
-     * Sets the to be animated view on hardware layer during the animation. Note
-     * that calling this will replace any existing animation listener on the animation
-     * with a new one, as animations do not support more than one listeners. Therefore,
-     * animations that already have listeners should do the layer change operations
-     * in their existing listeners, rather than calling this function.
-     */
-    private static void setHWLayerAnimListenerIfAlpha(final View v, AnimationOrAnimator anim) {
-        if (v == null || anim == null) {
-            return;
-        }
-        if (shouldRunOnHWLayer(v, anim)) {
-            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));
-            }
-        }
-    }
-
-    /**
-     * 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.
-        if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {
-            newState = Fragment.CREATED;
-        }
-        if (f.mRemoving && newState > f.mState) {
-            if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) {
-                // Allow the fragment to be created so that it can be saved later.
-                newState = Fragment.CREATED;
-            } else {
-                // While removing a fragment, we can't change it to a higher state.
-                newState = f.mState;
-            }
-        }
-        // Defer start if requested; don't allow it to move to STARTED or higher
-        // if it's not already started.
-        if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) {
-            newState = Fragment.STOPPED;
-        }
-        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 || 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 (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();
-
-                        // 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.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) {
-                            ViewGroup container = null;
-                            if (f.mContainerId != 0) {
-                                if (f.mContainerId == View.NO_ID) {
-                                    throwException(new IllegalArgumentException(
-                                            "Cannot create fragment "
-                                                    + f
-                                                    + " for a container view with no id"));
-                                }
-                                container = (ViewGroup) mContainer.onFindViewById(f.mContainerId);
-                                if (container == null && !f.mRestored) {
-                                    String resName;
-                                    try {
-                                        resName = f.getResources().getResourceName(f.mContainerId);
-                                    } catch (NotFoundException e) {
-                                        resName = "unknown";
-                                    }
-                                    throwException(new IllegalArgumentException(
-                                            "No view found for id 0x"
-                                            + Integer.toHexString(f.mContainerId) + " ("
-                                            + resName
-                                            + ") for fragment " + f));
-                                }
-                            }
-                            f.mContainer = container;
-                            f.mView = f.performCreateView(f.performGetLayoutInflater(
-                                    f.mSavedFragmentState), container, f.mSavedFragmentState);
-                            if (f.mView != null) {
-                                f.mInnerView = f.mView;
-                                f.mView.setSaveFromParentEnabled(false);
-                                if (container != null) {
-                                    container.addView(f.mView);
-                                }
-                                if (f.mHidden) {
-                                    f.mView.setVisibility(View.GONE);
-                                }
-                                f.onViewCreated(f.mView, f.mSavedFragmentState);
-                                dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
-                                        false);
-                                // Only animate the view if it is visible. This is done after
-                                // dispatchOnFragmentViewCreated in case visibility is changed
-                                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
-                                        && f.mContainer != null;
-                            } else {
-                                f.mInnerView = null;
-                            }
-                        }
-
-                        f.performActivityCreated(f.mSavedFragmentState);
-                        dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
-                        if (f.mView != null) {
-                            f.restoreViewState(f.mSavedFragmentState);
-                        }
-                        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);
-                        f.performResume();
-                        dispatchOnFragmentResumed(f, false);
-                        f.mSavedFragmentState = null;
-                        f.mSavedViewState = null;
-                    }
-            }
-        } else if (f.mState > newState) {
-            switch (f.mState) {
-                case Fragment.RESUMED:
-                    if (newState < Fragment.RESUMED) {
-                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
-                        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);
-                        if (f.mView != null) {
-                            // Need to save the current view state if not
-                            // done already.
-                            if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
-                                saveFragmentViewState(f);
-                            }
-                        }
-                        f.performDestroyView();
-                        dispatchOnFragmentViewDestroyed(f, false);
-                        if (f.mView != null && f.mContainer != 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) {
-                                anim = loadAnimation(f, transit, false,
-                                        transitionStyle);
-                            }
-                            f.mPostponedAlpha = 0;
-                            if (anim != null) {
-                                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) {
-                                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 || 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
-                            // is done.
-                            f.setStateAfterAnimating(newState);
-                            newState = Fragment.CREATED;
-                        } else {
-                            if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
-                            if (!f.mRetaining) {
-                                f.performDestroy();
-                                dispatchOnFragmentDestroyed(f, false);
-                            } else {
-                                f.mState = Fragment.INITIALIZING;
-                            }
-
-                            f.performDetach();
-                            dispatchOnFragmentDetached(f, false);
-                            if (!keepActive) {
-                                if (!f.mRetaining) {
-                                    makeInactive(f);
-                                } else {
-                                    f.mHost = null;
-                                    f.mParentFragment = null;
-                                    f.mFragmentManager = null;
-                                }
-                            }
-                        }
-                    }
-            }
-        }
-
-        if (f.mState != newState) {
-            Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
-                    + "expected state " + newState + " found " + f.mState);
-            f.mState = newState;
-        }
-    }
-
-    /**
-     * 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;
-        final ViewGroup container = fragment.mContainer;
-        container.startViewTransition(viewToAnimate);
-        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);
-                    // onAnimationEnd() comes during draw(), so there can still be some
-                    // draw events happening after this call. We don't want to detach
-                    // the view until after the onAnimationEnd()
-                    container.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            container.endViewTransition(viewToAnimate);
-
-                            if (fragment.getAnimatingAway() != null) {
-                                fragment.setAnimatingAway(null);
-                                moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0,
-                                        false);
-                            }
-                        }
-                    });
-                }
-            });
-            setHWLayerAnimListenerIfAlpha(viewToAnimate, anim);
-            fragment.mView.startAnimation(animation);
-        } else {
-            Animator animator = anim.animator;
-            fragment.setAnimator(anim.animator);
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator anim) {
-                    container.endViewTransition(viewToAnimate);
-                    // If an animator ends immediately, we can just pretend there is no animation.
-                    // When that happens the the fragment's view won't have been removed yet.
-                    Animator animator = fragment.getAnimator();
-                    fragment.setAnimator(null);
-                    if (animator != null && container.indexOfChild(viewToAnimate) < 0) {
-                        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)}
-     * calls. After fragments are brought to their final state in
-     * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or
-     * hidden must have their visibility changed and their animations started here.
-     *
-     * @param fragment The fragment with mHiddenChanged = true that should change its View's
-     *                 visibility and start the show or hide animation.
-     */
-    void completeShowHideFragment(final Fragment fragment) {
-        if (fragment.mView != null) {
-            AnimationOrAnimator anim = loadAnimation(fragment, fragment.getNextTransition(),
-                    !fragment.mHidden, fragment.getNextTransitionStyle());
-            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);
-                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) {
-            mNeedMenuInvalidate = true;
-        }
-        fragment.mHiddenChanged = false;
-        fragment.onHiddenChanged(fragment.mHidden);
-    }
-
-    /**
-     * Moves a fragment to its expected final state or the fragment manager's state, depending
-     * on whether the fragment manager's state is raised properly.
-     *
-     * @param f The fragment to change.
-     */
-    void moveFragmentToExpectedState(Fragment f) {
-        if (f == null) {
-            return;
-        }
-        int nextState = mCurState;
-        if (f.mRemoving) {
-            if (f.isInBackStack()) {
-                nextState = Math.min(nextState, Fragment.CREATED);
-            } else {
-                nextState = Math.min(nextState, Fragment.INITIALIZING);
-            }
-        }
-        moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);
-
-        if (f.mView != null) {
-            // Move the view if it is out of order
-            Fragment underFragment = findFragmentUnder(f);
-            if (underFragment != null) {
-                final View underView = underFragment.mView;
-                // make sure this fragment is in the right order.
-                final ViewGroup container = f.mContainer;
-                int underIndex = container.indexOfChild(underView);
-                int viewIndex = container.indexOfChild(f.mView);
-                if (viewIndex < underIndex) {
-                    container.removeViewAt(viewIndex);
-                    container.addView(f.mView, underIndex);
-                }
-            }
-            if (f.mIsNewlyAdded && f.mContainer != null) {
-                // Make it visible and run the animations
-                if (f.mPostponedAlpha > 0f) {
-                    f.mView.setAlpha(f.mPostponedAlpha);
-                }
-                f.mPostponedAlpha = 0f;
-                f.mIsNewlyAdded = false;
-                // run animations:
-                AnimationOrAnimator anim = loadAnimation(f, f.getNextTransition(), true,
-                        f.getNextTransitionStyle());
-                if (anim != null) {
-                    setHWLayerAnimListenerIfAlpha(f.mView, anim);
-                    if (anim.animation != null) {
-                        f.mView.startAnimation(anim.animation);
-                    } else {
-                        anim.animator.setTarget(f.mView);
-                        anim.animator.start();
-                    }
-                }
-            }
-        }
-        if (f.mHiddenChanged) {
-            completeShowHideFragment(f);
-        }
-    }
-
-    /**
-     * Changes the state of the fragment manager to {@code newState}. If the fragment manager
-     * changes state or {@code always} is {@code true}, any fragments within it have their
-     * states updated as well.
-     *
-     * @param newState The new state for the fragment manager
-     * @param always If {@code true}, all fragments update their state, even
-     *               if {@code newState} matches the current fragment manager's state.
-     */
-    void moveToState(int newState, boolean always) {
-        if (mHost == null && newState != Fragment.INITIALIZING) {
-            throw new IllegalStateException("No activity");
-        }
-
-        if (!always && newState == mCurState) {
-            return;
-        }
-
-        mCurState = newState;
-
-        if (mActive != null) {
-            boolean loadersRunning = false;
-
-            // Must add them in the proper order. mActive fragments may be out of order
-            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();
-                }
-            }
-
-            // Now iterate through all active fragments. These will include those that are removed
-            // and detached.
-            final int numActive = mActive.size();
-            for (int i = 0; i < numActive; i++) {
-                Fragment f = mActive.valueAt(i);
-                if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {
-                    moveFragmentToExpectedState(f);
-                    if (f.mLoaderManager != null) {
-                        loadersRunning |= f.mLoaderManager.hasRunningLoaders();
-                    }
-                }
-            }
-
-            if (!loadersRunning) {
-                startPendingDeferredFragments();
-            }
-
-            if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
-                mHost.onSupportInvalidateOptionsMenu();
-                mNeedMenuInvalidate = false;
-            }
-        }
-    }
-
-    void startPendingDeferredFragments() {
-        if (mActive == null) return;
-
-        for (int i=0; i<mActive.size(); i++) {
-            Fragment f = mActive.valueAt(i);
-            if (f != null) {
-                performPendingDeferredStart(f);
-            }
-        }
-    }
-
-    void makeActive(Fragment f) {
-        if (f.mIndex >= 0) {
-            return;
-        }
-
-        f.setIndex(mNextFragmentIndex++, mParent);
-        if (mActive == null) {
-            mActive = new SparseArray<>();
-        }
-        mActive.put(f.mIndex, f);
-        if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
-    }
-
-    void makeInactive(Fragment f) {
-        if (f.mIndex < 0) {
-            return;
-        }
-
-        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f);
-        // Don't remove yet. That happens in burpActive(). This prevents
-        // concurrent modification while iterating over mActive
-        mActive.put(f.mIndex, null);
-
-        mHost.inactivateFragment(f.mWho);
-        f.initState();
-    }
-
-    public void addFragment(Fragment fragment, boolean moveToStateNow) {
-        if (DEBUG) Log.v(TAG, "add: " + fragment);
-        makeActive(fragment);
-        if (!fragment.mDetached) {
-            if (mAdded.contains(fragment)) {
-                throw new IllegalStateException("Fragment already added: " + fragment);
-            }
-            synchronized (mAdded) {
-                mAdded.add(fragment);
-            }
-            fragment.mAdded = true;
-            fragment.mRemoving = false;
-            if (fragment.mView == null) {
-                fragment.mHiddenChanged = false;
-            }
-            if (fragment.mHasMenu && fragment.mMenuVisible) {
-                mNeedMenuInvalidate = true;
-            }
-            if (moveToStateNow) {
-                moveToState(fragment);
-            }
-        }
-    }
-
-    public void removeFragment(Fragment fragment) {
-        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
-        final boolean inactive = !fragment.isInBackStack();
-        if (!fragment.mDetached || inactive) {
-            synchronized (mAdded) {
-                mAdded.remove(fragment);
-            }
-            if (fragment.mHasMenu && fragment.mMenuVisible) {
-                mNeedMenuInvalidate = true;
-            }
-            fragment.mAdded = false;
-            fragment.mRemoving = true;
-        }
-    }
-
-    /**
-     * Marks a fragment as hidden to be later animated in with
-     * {@link #completeShowHideFragment(Fragment)}.
-     *
-     * @param fragment The fragment to be shown.
-     */
-    public void hideFragment(Fragment fragment) {
-        if (DEBUG) Log.v(TAG, "hide: " + fragment);
-        if (!fragment.mHidden) {
-            fragment.mHidden = true;
-            // Toggle hidden changed so that if a fragment goes through show/hide/show
-            // it doesn't go through the animation.
-            fragment.mHiddenChanged = !fragment.mHiddenChanged;
-        }
-    }
-
-    /**
-     * Marks a fragment as shown to be later animated in with
-     * {@link #completeShowHideFragment(Fragment)}.
-     *
-     * @param fragment The fragment to be shown.
-     */
-    public void showFragment(Fragment fragment) {
-        if (DEBUG) Log.v(TAG, "show: " + fragment);
-        if (fragment.mHidden) {
-            fragment.mHidden = false;
-            // Toggle hidden changed so that if a fragment goes through show/hide/show
-            // it doesn't go through the animation.
-            fragment.mHiddenChanged = !fragment.mHiddenChanged;
-        }
-    }
-
-    public void detachFragment(Fragment fragment) {
-        if (DEBUG) Log.v(TAG, "detach: " + fragment);
-        if (!fragment.mDetached) {
-            fragment.mDetached = true;
-            if (fragment.mAdded) {
-                // We are not already in back stack, so need to remove the fragment.
-                if (DEBUG) Log.v(TAG, "remove from detach: " + fragment);
-                synchronized (mAdded) {
-                    mAdded.remove(fragment);
-                }
-                if (fragment.mHasMenu && fragment.mMenuVisible) {
-                    mNeedMenuInvalidate = true;
-                }
-                fragment.mAdded = false;
-            }
-        }
-    }
-
-    public void attachFragment(Fragment fragment) {
-        if (DEBUG) Log.v(TAG, "attach: " + fragment);
-        if (fragment.mDetached) {
-            fragment.mDetached = false;
-            if (!fragment.mAdded) {
-                if (mAdded.contains(fragment)) {
-                    throw new IllegalStateException("Fragment already added: " + fragment);
-                }
-                if (DEBUG) Log.v(TAG, "add from attach: " + fragment);
-                synchronized (mAdded) {
-                    mAdded.add(fragment);
-                }
-                fragment.mAdded = true;
-                if (fragment.mHasMenu && fragment.mMenuVisible) {
-                    mNeedMenuInvalidate = true;
-                }
-            }
-        }
-    }
-
-    @Override
-    public Fragment findFragmentById(int id) {
-        // 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) {
-            // Now for any known fragment.
-            for (int i=mActive.size()-1; i>=0; i--) {
-                Fragment f = mActive.valueAt(i);
-                if (f != null && f.mFragmentId == id) {
-                    return f;
-                }
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public Fragment findFragmentByTag(String tag) {
-        if (tag != null) {
-            // First look through added fragments.
-            for (int i=mAdded.size()-1; i>=0; i--) {
-                Fragment f = mAdded.get(i);
-                if (f != null && tag.equals(f.mTag)) {
-                    return f;
-                }
-            }
-        }
-        if (mActive != null && tag != null) {
-            // Now for any known fragment.
-            for (int i=mActive.size()-1; i>=0; i--) {
-                Fragment f = mActive.valueAt(i);
-                if (f != null && tag.equals(f.mTag)) {
-                    return f;
-                }
-            }
-        }
-        return null;
-    }
-
-    public Fragment findFragmentByWho(String who) {
-        if (mActive != null && who != null) {
-            for (int i=mActive.size()-1; i>=0; i--) {
-                Fragment f = mActive.valueAt(i);
-                if (f != null && (f=f.findFragmentByWho(who)) != null) {
-                    return f;
-                }
-            }
-        }
-        return null;
-    }
-
-    private void checkStateLoss() {
-        if (mStateSaved) {
-            throw new IllegalStateException(
-                    "Can not perform this action after onSaveInstanceState");
-        }
-        if (mNoTransactionsBecause != null) {
-            throw new IllegalStateException(
-                    "Can not perform this action inside of " + mNoTransactionsBecause);
-        }
-    }
-
-    @Override
-    public boolean isStateSaved() {
-        return mStateSaved;
-    }
-
-    /**
-     * Adds an action to the queue of pending actions.
-     *
-     * @param action the action to add
-     * @param allowStateLoss whether to allow loss of state information
-     * @throws IllegalStateException if the activity has been destroyed
-     */
-    public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
-        if (!allowStateLoss) {
-            checkStateLoss();
-        }
-        synchronized (this) {
-            if (mDestroyed || mHost == null) {
-                if (allowStateLoss) {
-                    // This FragmentManager isn't attached, so drop the entire transaction.
-                    return;
-                }
-                throw new IllegalStateException("Activity has been destroyed");
-            }
-            if (mPendingActions == null) {
-                mPendingActions = new ArrayList<>();
-            }
-            mPendingActions.add(action);
-            scheduleCommit();
-        }
-    }
-
-    /**
-     * Schedules the execution when one hasn't been scheduled already. This should happen
-     * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
-     * a postponed transaction has been started with
-     * {@link Fragment#startPostponedEnterTransition()}
-     */
-    private void scheduleCommit() {
-        synchronized (this) {
-            boolean postponeReady =
-                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
-            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
-            if (postponeReady || pendingReady) {
-                mHost.getHandler().removeCallbacks(mExecCommit);
-                mHost.getHandler().post(mExecCommit);
-            }
-        }
-    }
-
-    public int allocBackStackIndex(BackStackRecord bse) {
-        synchronized (this) {
-            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
-                if (mBackStackIndices == null) {
-                    mBackStackIndices = new ArrayList<BackStackRecord>();
-                }
-                int index = mBackStackIndices.size();
-                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
-                mBackStackIndices.add(bse);
-                return index;
-
-            } else {
-                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
-                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
-                mBackStackIndices.set(index, bse);
-                return index;
-            }
-        }
-    }
-
-    public void setBackStackIndex(int index, BackStackRecord bse) {
-        synchronized (this) {
-            if (mBackStackIndices == null) {
-                mBackStackIndices = new ArrayList<BackStackRecord>();
-            }
-            int N = mBackStackIndices.size();
-            if (index < N) {
-                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
-                mBackStackIndices.set(index, bse);
-            } else {
-                while (N < index) {
-                    mBackStackIndices.add(null);
-                    if (mAvailBackStackIndices == null) {
-                        mAvailBackStackIndices = new ArrayList<Integer>();
-                    }
-                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
-                    mAvailBackStackIndices.add(N);
-                    N++;
-                }
-                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
-                mBackStackIndices.add(bse);
-            }
-        }
-    }
-
-    public void freeBackStackIndex(int index) {
-        synchronized (this) {
-            mBackStackIndices.set(index, null);
-            if (mAvailBackStackIndices == null) {
-                mAvailBackStackIndices = new ArrayList<Integer>();
-            }
-            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
-            mAvailBackStackIndices.add(index);
-        }
-    }
-
-    /**
-     * Broken out from exec*, this prepares for gathering and executing operations.
-     *
-     * @param allowStateLoss true if state loss should be ignored or false if it should be
-     *                       checked.
-     */
-    private void ensureExecReady(boolean allowStateLoss) {
-        if (mExecutingActions) {
-            throw new IllegalStateException("FragmentManager is already executing transactions");
-        }
-
-        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
-            throw new IllegalStateException("Must be called from main thread of fragment host");
-        }
-
-        if (!allowStateLoss) {
-            checkStateLoss();
-        }
-
-        if (mTmpRecords == null) {
-            mTmpRecords = new ArrayList<>();
-            mTmpIsPop = new ArrayList<>();
-        }
-        mExecutingActions = true;
-        try {
-            executePostponedTransaction(null, null);
-        } finally {
-            mExecutingActions = false;
-        }
-    }
-
-    public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
-        if (allowStateLoss && (mHost == null || mDestroyed)) {
-            // This FragmentManager isn't attached, so drop the entire transaction.
-            return;
-        }
-        ensureExecReady(allowStateLoss);
-        if (action.generateOps(mTmpRecords, mTmpIsPop)) {
-            mExecutingActions = true;
-            try {
-                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
-            } finally {
-                cleanupExec();
-            }
-        }
-
-        doPendingDeferredStart();
-        burpActive();
-    }
-
-    /**
-     * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures
-     * used in executing operations.
-     */
-    private void cleanupExec() {
-        mExecutingActions = false;
-        mTmpIsPop.clear();
-        mTmpRecords.clear();
-    }
-
-    /**
-     * Only call from main thread!
-     */
-    public boolean execPendingActions() {
-        ensureExecReady(true);
-
-        boolean didSomething = false;
-        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
-            mExecutingActions = true;
-            try {
-                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
-            } finally {
-                cleanupExec();
-            }
-            didSomething = true;
-        }
-
-        doPendingDeferredStart();
-        burpActive();
-
-        return didSomething;
-    }
-
-    /**
-     * Complete the execution of transactions that have previously been postponed, but are
-     * now ready.
-     */
-    private void executePostponedTransaction(ArrayList<BackStackRecord> records,
-            ArrayList<Boolean> isRecordPop) {
-        int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size();
-        for (int i = 0; i < numPostponed; i++) {
-            StartEnterTransitionListener listener = mPostponedTransactions.get(i);
-            if (records != null && !listener.mIsBack) {
-                int index = records.indexOf(listener.mRecord);
-                if (index != -1 && isRecordPop.get(index)) {
-                    listener.cancelTransaction();
-                    continue;
-                }
-            }
-            if (listener.isReady() || (records != null
-                    && listener.mRecord.interactsWith(records, 0, records.size()))) {
-                mPostponedTransactions.remove(i);
-                i--;
-                numPostponed--;
-                int index;
-                if (records != null && !listener.mIsBack
-                        && (index = records.indexOf(listener.mRecord)) != -1
-                        && isRecordPop.get(index)) {
-                    // This is popping a postponed transaction
-                    listener.cancelTransaction();
-                } else {
-                    listener.completeTransaction();
-                }
-            }
-        }
-    }
-
-    /**
-     * 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 to remove the unnecessary operation.
-     * <p>
-     * Likewise, two transactions committed that are executed at the same time will be optimized
-     * 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 removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
-            ArrayList<Boolean> isRecordPop) {
-        if (records == null || records.isEmpty()) {
-            return;
-        }
-
-        if (isRecordPop == null || records.size() != isRecordPop.size()) {
-            throw new IllegalStateException("Internal error with the back stack records");
-        }
-
-        // Force start of any postponed transactions that interact with scheduled transactions:
-        executePostponedTransaction(records, isRecordPop);
-
-        final int numRecords = records.size();
-        int startIndex = 0;
-        for (int recordNum = 0; recordNum < numRecords; recordNum++) {
-            final boolean canReorder = records.get(recordNum).mReorderingAllowed;
-            if (!canReorder) {
-                // execute all previous transactions
-                if (startIndex != recordNum) {
-                    executeOpsTogether(records, isRecordPop, startIndex, recordNum);
-                }
-                // execute all pop operations that don't allow reordering together or
-                // one add operation
-                int reorderingEnd = recordNum + 1;
-                if (isRecordPop.get(recordNum)) {
-                    while (reorderingEnd < numRecords
-                            && isRecordPop.get(reorderingEnd)
-                            && !records.get(reorderingEnd).mReorderingAllowed) {
-                        reorderingEnd++;
-                    }
-                }
-                executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
-                startIndex = reorderingEnd;
-                recordNum = reorderingEnd - 1;
-            }
-        }
-        if (startIndex != numRecords) {
-            executeOpsTogether(records, isRecordPop, startIndex, numRecords);
-        }
-    }
-
-    /**
-     * 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 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 allowReordering = records.get(startIndex).mReorderingAllowed;
-        boolean addToBackStack = false;
-        if (mTmpAddedFragments == null) {
-            mTmpAddedFragments = new ArrayList<>();
-        } else {
-            mTmpAddedFragments.clear();
-        }
-        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) {
-                oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
-            } else {
-                oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
-            }
-            addToBackStack = addToBackStack || record.mAddToBackStack;
-        }
-        mTmpAddedFragments.clear();
-
-        if (!allowReordering) {
-            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
-                    false);
-        }
-        executeOps(records, isRecordPop, startIndex, endIndex);
-
-        int postponeIndex = endIndex;
-        if (allowReordering) {
-            ArraySet<Fragment> addedFragments = new ArraySet<>();
-            addAddedFragments(addedFragments);
-            postponeIndex = postponePostponableTransactions(records, isRecordPop,
-                    startIndex, endIndex, addedFragments);
-            makeRemovedFragmentsInvisible(addedFragments);
-        }
-
-        if (postponeIndex != startIndex && allowReordering) {
-            // need to run something now
-            FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
-                    postponeIndex, true);
-            moveToState(mCurState, true);
-        }
-
-        for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
-            final BackStackRecord record = records.get(recordNum);
-            final boolean isPop = isRecordPop.get(recordNum);
-            if (isPop && record.mIndex >= 0) {
-                freeBackStackIndex(record.mIndex);
-                record.mIndex = -1;
-            }
-            record.runOnCommitRunnables();
-        }
-        if (addToBackStack) {
-            reportBackStackChanged();
-        }
-    }
-
-    /**
-     * Any fragments that were removed because they have been postponed should have their views
-     * 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.
-     */
-    private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) {
-        final int numAdded = fragments.size();
-        for (int i = 0; i < numAdded; i++) {
-            final Fragment fragment = fragments.valueAt(i);
-            if (!fragment.mAdded) {
-                final View view = fragment.getView();
-                fragment.mPostponedAlpha = view.getAlpha();
-                view.setAlpha(0f);
-            }
-        }
-    }
-
-    /**
-     * Examine all transactions and determine which ones are marked as postponed. Those will
-     * have their operations rolled back and moved to the end of the record list (up to endIndex).
-     * It will also add the postponed transaction to the queue.
-     *
-     * @param records A list of BackStackRecords that should be checked.
-     * @param isRecordPop The direction that these records are being run.
-     * @param startIndex The index of the first record in <code>records</code> to be checked
-     * @param endIndex One more than the final record index in <code>records</code> to be checked.
-     * @return The index of the first postponed transaction or endIndex if no transaction was
-     * postponed.
-     */
-    private int postponePostponableTransactions(ArrayList<BackStackRecord> records,
-            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex,
-            ArraySet<Fragment> added) {
-        int postponeIndex = endIndex;
-        for (int i = endIndex - 1; i >= startIndex; i--) {
-            final BackStackRecord record = records.get(i);
-            final boolean isPop = isRecordPop.get(i);
-            boolean isPostponed = record.isPostponed()
-                    && !record.interactsWith(records, i + 1, endIndex);
-            if (isPostponed) {
-                if (mPostponedTransactions == null) {
-                    mPostponedTransactions = new ArrayList<>();
-                }
-                StartEnterTransitionListener listener =
-                        new StartEnterTransitionListener(record, isPop);
-                mPostponedTransactions.add(listener);
-                record.setOnStartPostponedListener(listener);
-
-                // roll back the transaction
-                if (isPop) {
-                    record.executeOps();
-                } else {
-                    record.executePopOps(false);
-                }
-
-                // move to the end
-                postponeIndex--;
-                if (i != postponeIndex) {
-                    records.remove(i);
-                    records.add(postponeIndex, record);
-                }
-
-                // different views may be visible now
-                addAddedFragments(added);
-            }
-        }
-        return postponeIndex;
-    }
-
-    /**
-     * When a postponed transaction is ready to be started, this completes the transaction,
-     * removing, hiding, or showing views as well as starting the animations and transitions.
-     * <p>
-     * {@code runtransitions} is set to false when the transaction postponement was interrupted
-     * abnormally -- normally by a new transaction being started that affects the postponed
-     * transaction.
-     *
-     * @param record The transaction to run
-     * @param isPop true if record is popping or false if it is adding
-     * @param runTransitions true if the fragment transition should be run or false otherwise.
-     * @param moveToState true if the state should be changed after executing the operations.
-     *                    This is false when the transaction is canceled when a postponed
-     *                    transaction is popped.
-     */
-    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);
-        if (runTransitions) {
-            FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
-        }
-        if (moveToState) {
-            moveToState(mCurState, true);
-        }
-
-        if (mActive != null) {
-            final int numActive = mActive.size();
-            for (int i = 0; i < numActive; i++) {
-                // Allow added fragments to be removed during the pop since we aren't going
-                // to move them to the final state with moveToState(mCurState).
-                Fragment fragment = mActive.valueAt(i);
-                if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded
-                        && record.interactsWith(fragment.mContainerId)) {
-                    if (fragment.mPostponedAlpha > 0) {
-                        fragment.mView.setAlpha(fragment.mPostponedAlpha);
-                    }
-                    if (moveToState) {
-                        fragment.mPostponedAlpha = 0;
-                    } else {
-                        fragment.mPostponedAlpha = -1;
-                        fragment.mIsNewlyAdded = false;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Find a fragment within the fragment's container whose View should be below the passed
-     * fragment. {@code null} is returned when the fragment has no View or if there should be
-     * no fragment with a View below the given fragment.
-     *
-     * As an example, if mAdded has two Fragments with Views sharing the same container:
-     * FragmentA
-     * FragmentB
-     *
-     * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA
-     * had no View, null would be returned.
-     *
-     * @param f The fragment that may be on top of another fragment.
-     * @return The fragment with a View under f, if one exists or null if f has no View or
-     * there are no fragments with Views in the same container.
-     */
-    private Fragment findFragmentUnder(Fragment f) {
-        final ViewGroup container = f.mContainer;
-        final View view = f.mView;
-
-        if (container == null || view == null) {
-            return null;
-        }
-
-        final int fragmentIndex = mAdded.indexOf(f);
-        for (int i = fragmentIndex - 1; i >= 0; i--) {
-            Fragment underFragment = mAdded.get(i);
-            if (underFragment.mContainer == container && underFragment.mView != null) {
-                // Found the fragment under this one
-                return underFragment;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Run the operations in the BackStackRecords, either to push or pop.
-     *
-     * @param records The list of records whose operations should be run.
-     * @param isRecordPop The direction that these records are being run.
-     * @param startIndex The index of the first entry in records to run.
-     * @param endIndex One past the index of the final entry in records to run.
-     */
-    private static void executeOps(ArrayList<BackStackRecord> records,
-            ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
-        for (int i = startIndex; i < endIndex; i++) {
-            final BackStackRecord record = records.get(i);
-            final boolean isPop = isRecordPop.get(i);
-            if (isPop) {
-                record.bumpBackStackNesting(-1);
-                // Only execute the add operations at the end of
-                // all transactions.
-                boolean moveToState = i == (endIndex - 1);
-                record.executePopOps(moveToState);
-            } else {
-                record.bumpBackStackNesting(1);
-                record.executeOps();
-            }
-        }
-    }
-
-    /**
-     * Ensure that fragments that are added are moved to at least the CREATED state.
-     * Any newly-added Views are inserted into {@code added} so that the Transaction can be
-     * postponed with {@link Fragment#postponeEnterTransition()}. They will later be made
-     * invisible (by setting their alpha to 0) if they have been removed when postponed.
-     */
-    private void addAddedFragments(ArraySet<Fragment> added) {
-        if (mCurState < Fragment.CREATED) {
-            return;
-        }
-        // We want to leave the fragment in the started state
-        final int state = Math.min(mCurState, Fragment.STARTED);
-        final int numAdded = mAdded.size();
-        for (int i = 0; i < numAdded; i++) {
-            Fragment fragment = mAdded.get(i);
-            if (fragment.mState < state) {
-                moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(),
-                        false);
-                if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) {
-                    added.add(fragment);
-                }
-            }
-        }
-    }
-
-    /**
-     * Starts all postponed transactions regardless of whether they are ready or not.
-     */
-    private void forcePostponedTransactions() {
-        if (mPostponedTransactions != null) {
-            while (!mPostponedTransactions.isEmpty()) {
-                mPostponedTransactions.remove(0).completeTransaction();
-            }
-        }
-    }
-
-    /**
-     * Ends the animations of fragments so that they immediately reach the end state.
-     * This is used prior to saving the state so that the correct state is saved.
-     */
-    private void endAnimatingAwayFragments() {
-        final int numFragments = mActive == null ? 0 : mActive.size();
-        for (int i = 0; i < numFragments; i++) {
-            Fragment fragment = mActive.valueAt(i);
-            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();
-                    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();
-                    }
-                    fragment.setAnimatingAway(null);
-                    moveToState(fragment, stateAfterAnimating, 0, 0, false);
-                } else if (fragment.getAnimator() != null) {
-                    fragment.getAnimator().end();
-                }
-            }
-        }
-    }
-
-    /**
-     * Adds all records in the pending actions to records and whether they are add or pop
-     * operations to isPop. After executing, the pending actions will be empty.
-     *
-     * @param records All pending actions will generate BackStackRecords added to this.
-     *                This contains the transactions, in order, to execute.
-     * @param isPop All pending actions will generate booleans to add to this. This contains
-     *              an entry for each entry in records to indicate whether or not it is a
-     *              pop action.
-     */
-    private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records,
-            ArrayList<Boolean> isPop) {
-        boolean didSomething = false;
-        synchronized (this) {
-            if (mPendingActions == null || mPendingActions.size() == 0) {
-                return false;
-            }
-
-            final int numActions = mPendingActions.size();
-            for (int i = 0; i < numActions; i++) {
-                didSomething |= mPendingActions.get(i).generateOps(records, isPop);
-            }
-            mPendingActions.clear();
-            mHost.getHandler().removeCallbacks(mExecCommit);
-        }
-        return didSomething;
-    }
-
-    void doPendingDeferredStart() {
-        if (mHavePendingDeferredStart) {
-            boolean loadersRunning = false;
-            for (int i = 0; i < mActive.size(); i++) {
-                Fragment f = mActive.valueAt(i);
-                if (f != null && f.mLoaderManager != null) {
-                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
-                }
-            }
-            if (!loadersRunning) {
-                mHavePendingDeferredStart = false;
-                startPendingDeferredFragments();
-            }
-        }
-    }
-
-    void reportBackStackChanged() {
-        if (mBackStackChangeListeners != null) {
-            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
-                mBackStackChangeListeners.get(i).onBackStackChanged();
-            }
-        }
-    }
-
-    void addBackStackState(BackStackRecord state) {
-        if (mBackStack == null) {
-            mBackStack = new ArrayList<BackStackRecord>();
-        }
-        mBackStack.add(state);
-    }
-
-    @SuppressWarnings("unused")
-    boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
-            String name, int id, int flags) {
-        if (mBackStack == null) {
-            return false;
-        }
-        if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
-            int last = mBackStack.size() - 1;
-            if (last < 0) {
-                return false;
-            }
-            records.add(mBackStack.remove(last));
-            isRecordPop.add(true);
-        } else {
-            int index = -1;
-            if (name != null || id >= 0) {
-                // If a name or ID is specified, look for that place in
-                // the stack.
-                index = mBackStack.size()-1;
-                while (index >= 0) {
-                    BackStackRecord bss = mBackStack.get(index);
-                    if (name != null && name.equals(bss.getName())) {
-                        break;
-                    }
-                    if (id >= 0 && id == bss.mIndex) {
-                        break;
-                    }
-                    index--;
-                }
-                if (index < 0) {
-                    return false;
-                }
-                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
-                    index--;
-                    // Consume all following entries that match.
-                    while (index >= 0) {
-                        BackStackRecord bss = mBackStack.get(index);
-                        if ((name != null && name.equals(bss.getName()))
-                                || (id >= 0 && id == bss.mIndex)) {
-                            index--;
-                            continue;
-                        }
-                        break;
-                    }
-                }
-            }
-            if (index == mBackStack.size()-1) {
-                return false;
-            }
-            for (int i = mBackStack.size() - 1; i > index; i--) {
-                records.add(mBackStack.remove(i));
-                isRecordPop.add(true);
-            }
-        }
-        return true;
-    }
-
-    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) {
-            for (int i=0; i<mActive.size(); i++) {
-                Fragment f = mActive.valueAt(i);
-                if (f != null) {
-                    if (f.mRetainInstance) {
-                        if (fragments == null) {
-                            fragments = new ArrayList<Fragment>();
-                        }
-                        fragments.add(f);
-                        f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
-                        if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
-                    }
-                    FragmentManagerNonConfig child;
-                    if (f.mChildFragmentManager != null) {
-                        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) {
-                        childFragments.add(child);
-                    }
-                }
-            }
-        }
-        if (fragments == null && childFragments == null) {
-            mSavedNonConfig = null;
-        } else {
-            mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments);
-        }
-    }
-
-    void saveFragmentViewState(Fragment f) {
-        if (f.mInnerView == null) {
-            return;
-        }
-        if (mStateArray == null) {
-            mStateArray = new SparseArray<Parcelable>();
-        } else {
-            mStateArray.clear();
-        }
-        f.mInnerView.saveHierarchyState(mStateArray);
-        if (mStateArray.size() > 0) {
-            f.mSavedViewState = mStateArray;
-            mStateArray = null;
-        }
-    }
-
-    Bundle saveFragmentBasicState(Fragment f) {
-        Bundle result = null;
-
-        if (mStateBundle == null) {
-            mStateBundle = new Bundle();
-        }
-        f.performSaveInstanceState(mStateBundle);
-        dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
-        if (!mStateBundle.isEmpty()) {
-            result = mStateBundle;
-            mStateBundle = null;
-        }
-
-        if (f.mView != null) {
-            saveFragmentViewState(f);
-        }
-        if (f.mSavedViewState != null) {
-            if (result == null) {
-                result = new Bundle();
-            }
-            result.putSparseParcelableArray(
-                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
-        }
-        if (!f.mUserVisibleHint) {
-            if (result == null) {
-                result = new Bundle();
-            }
-            // Only add this if it's not the default value
-            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
-        }
-
-        return result;
-    }
-
-    Parcelable saveAllState() {
-        // Make sure all pending operations have now been executed to get
-        // our state update-to-date.
-        forcePostponedTransactions();
-        endAnimatingAwayFragments();
-        execPendingActions();
-
-        mStateSaved = true;
-        mSavedNonConfig = null;
-
-        if (mActive == null || mActive.size() <= 0) {
-            return null;
-        }
-
-        // First collect all active fragments.
-        int N = mActive.size();
-        FragmentState[] active = new FragmentState[N];
-        boolean haveFragments = false;
-        for (int i=0; i<N; i++) {
-            Fragment f = mActive.valueAt(i);
-            if (f != null) {
-                if (f.mIndex < 0) {
-                    throwException(new IllegalStateException(
-                            "Failure saving state: active " + f
-                            + " has cleared index: " + f.mIndex));
-                }
-
-                haveFragments = true;
-
-                FragmentState fs = new FragmentState(f);
-                active[i] = fs;
-
-                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
-                    fs.mSavedFragmentState = saveFragmentBasicState(f);
-
-                    if (f.mTarget != null) {
-                        if (f.mTarget.mIndex < 0) {
-                            throwException(new IllegalStateException(
-                                    "Failure saving state: " + f
-                                    + " has target not in fragment manager: " + f.mTarget));
-                        }
-                        if (fs.mSavedFragmentState == null) {
-                            fs.mSavedFragmentState = new Bundle();
-                        }
-                        putFragment(fs.mSavedFragmentState,
-                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
-                        if (f.mTargetRequestCode != 0) {
-                            fs.mSavedFragmentState.putInt(
-                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
-                                    f.mTargetRequestCode);
-                        }
-                    }
-
-                } else {
-                    fs.mSavedFragmentState = f.mSavedFragmentState;
-                }
-
-                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
-                        + fs.mSavedFragmentState);
-            }
-        }
-
-        if (!haveFragments) {
-            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
-            return null;
-        }
-
-        int[] added = null;
-        BackStackState[] backStack = null;
-
-        // Build list of currently added fragments.
-        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));
-                }
-            }
-        }
-
-        // Now save back stack.
-        if (mBackStack != null) {
-            N = mBackStack.size();
-            if (N > 0) {
-                backStack = new BackStackState[N];
-                for (int i=0; i<N; i++) {
-                    backStack[i] = new BackStackState(mBackStack.get(i));
-                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
-                            + ": " + mBackStack.get(i));
-                }
-            }
-        }
-
-        FragmentManagerState fms = new FragmentManagerState();
-        fms.mActive = active;
-        fms.mAdded = added;
-        fms.mBackStack = backStack;
-        if (mPrimaryNav != null) {
-            fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex;
-        }
-        fms.mNextFragmentIndex = mNextFragmentIndex;
-        saveNonConfig();
-        return fms;
-    }
-
-    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
-        // If there is no saved state at all, then there can not be
-        // any nonConfig fragments either, so that is that.
-        if (state == null) return;
-        FragmentManagerState fms = (FragmentManagerState)state;
-        if (fms.mActive == null) return;
-
-        List<FragmentManagerNonConfig> childNonConfigs = null;
-
-        // First re-attach any non-config instances we are retaining back
-        // to their saved state, so we don't try to instantiate them again.
-        if (nonConfig != null) {
-            List<Fragment> nonConfigFragments = nonConfig.getFragments();
-            childNonConfigs = nonConfig.getChildNonConfigs();
-            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
-            for (int i = 0; i < count; i++) {
-                Fragment f = nonConfigFragments.get(i);
-                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
-                int index = 0; // index into fms.mActive
-                while (index < fms.mActive.length && fms.mActive[index].mIndex != f.mIndex) {
-                    index++;
-                }
-                if (index == fms.mActive.length) {
-                    throwException(new IllegalStateException("Could not find active fragment "
-                            + "with index " + f.mIndex));
-                }
-                FragmentState fs = fms.mActive[index];
-                fs.mInstance = f;
-                f.mSavedViewState = null;
-                f.mBackStackNesting = 0;
-                f.mInLayout = false;
-                f.mAdded = false;
-                f.mTarget = null;
-                if (fs.mSavedFragmentState != null) {
-                    fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
-                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
-                            FragmentManagerImpl.VIEW_STATE_TAG);
-                    f.mSavedFragmentState = fs.mSavedFragmentState;
-                }
-            }
-        }
-
-        // Build the full list of active fragments, instantiating them from
-        // their saved state.
-        mActive = new SparseArray<>(fms.mActive.length);
-        for (int i=0; i<fms.mActive.length; i++) {
-            FragmentState fs = fms.mActive[i];
-            if (fs != null) {
-                FragmentManagerNonConfig childNonConfig = null;
-                if (childNonConfigs != null && i < childNonConfigs.size()) {
-                    childNonConfig = childNonConfigs.get(i);
-                }
-                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
-                // retained above), clear mInstance in case we end up re-restoring
-                // from this FragmentState again.
-                fs.mInstance = null;
-            }
-        }
-
-        // Update the target of all retained fragments.
-        if (nonConfig != null) {
-            List<Fragment> nonConfigFragments = nonConfig.getFragments();
-            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
-            for (int i = 0; i < count; i++) {
-                Fragment f = nonConfigFragments.get(i);
-                if (f.mTargetIndex >= 0) {
-                    f.mTarget = mActive.get(f.mTargetIndex);
-                    if (f.mTarget == null) {
-                        Log.w(TAG, "Re-attaching retained fragment " + f
-                                + " target no longer exists: " + f.mTargetIndex);
-                    }
-                }
-            }
-        }
-
-        // Build the list of currently added fragments.
-        mAdded.clear();
-        if (fms.mAdded != null) {
-            for (int i=0; i<fms.mAdded.length; i++) {
-                Fragment f = mActive.get(fms.mAdded[i]);
-                if (f == null) {
-                    throwException(new IllegalStateException(
-                            "No instantiated fragment for index #" + fms.mAdded[i]));
-                }
-                f.mAdded = true;
-                if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f);
-                if (mAdded.contains(f)) {
-                    throw new IllegalStateException("Already added!");
-                }
-                synchronized (mAdded) {
-                    mAdded.add(f);
-                }
-            }
-        }
-
-        // Build the back stack.
-        if (fms.mBackStack != null) {
-            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
-            for (int i=0; i<fms.mBackStack.length; i++) {
-                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
-                if (DEBUG) {
-                    Log.v(TAG, "restoreAllState: back stack #" + i
-                        + " (index " + bse.mIndex + "): " + bse);
-                    LogWriter logw = new LogWriter(TAG);
-                    PrintWriter pw = new PrintWriter(logw);
-                    bse.dump("  ", pw, false);
-                    pw.close();
-                }
-                mBackStack.add(bse);
-                if (bse.mIndex >= 0) {
-                    setBackStackIndex(bse.mIndex, bse);
-                }
-            }
-        } else {
-            mBackStack = null;
-        }
-
-        if (fms.mPrimaryNavActiveIndex >= 0) {
-            mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex);
-        }
-        this.mNextFragmentIndex = fms.mNextFragmentIndex;
-    }
-
-    /**
-     * To prevent list modification errors, mActive sets values to null instead of
-     * removing them when the Fragment becomes inactive. This cleans up the list at the
-     * end of executing the transactions.
-     */
-    private void burpActive() {
-        if (mActive != null) {
-            for (int i = mActive.size() - 1; i >= 0; i--) {
-                if (mActive.valueAt(i) == null) {
-                    mActive.delete(mActive.keyAt(i));
-                }
-            }
-        }
-    }
-
-    public void attachController(FragmentHostCallback host,
-            FragmentContainer container, Fragment parent) {
-        if (mHost != null) throw new IllegalStateException("Already attached");
-        mHost = host;
-        mContainer = container;
-        mParent = parent;
-    }
-
-    public void noteStateNotSaved() {
-        mSavedNonConfig = null;
-        mStateSaved = false;
-        final int addedCount = mAdded.size();
-        for (int i = 0; i < addedCount; i++) {
-            Fragment fragment = mAdded.get(i);
-            if (fragment != null) {
-                fragment.noteStateNotSaved();
-            }
-        }
-    }
-
-    public void dispatchCreate() {
-        mStateSaved = false;
-        dispatchStateChange(Fragment.CREATED);
-    }
-
-    public void dispatchActivityCreated() {
-        mStateSaved = false;
-        dispatchStateChange(Fragment.ACTIVITY_CREATED);
-    }
-
-    public void dispatchStart() {
-        mStateSaved = false;
-        dispatchStateChange(Fragment.STARTED);
-    }
-
-    public void dispatchResume() {
-        mStateSaved = false;
-        dispatchStateChange(Fragment.RESUMED);
-    }
-
-    public void dispatchPause() {
-        dispatchStateChange(Fragment.STARTED);
-    }
-
-    public void dispatchStop() {
-        // See saveAllState() for the explanation of this.  We do this for
-        // all platform versions, to keep our behavior more consistent between
-        // them.
-        mStateSaved = true;
-
-        dispatchStateChange(Fragment.STOPPED);
-    }
-
-    public void dispatchReallyStop() {
-        dispatchStateChange(Fragment.ACTIVITY_CREATED);
-    }
-
-    public void dispatchDestroyView() {
-        dispatchStateChange(Fragment.CREATED);
-    }
-
-    public void dispatchDestroy() {
-        mDestroyed = true;
-        execPendingActions();
-        dispatchStateChange(Fragment.INITIALIZING);
-        mHost = null;
-        mContainer = null;
-        mParent = null;
-    }
-
-    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) {
-                f.performMultiWindowModeChanged(isInMultiWindowMode);
-            }
-        }
-    }
-
-    public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        for (int i = mAdded.size() - 1; i >= 0; --i) {
-            final android.support.v4.app.Fragment f = mAdded.get(i);
-            if (f != null) {
-                f.performPictureInPictureModeChanged(isInPictureInPictureMode);
-            }
-        }
-    }
-
-    public void dispatchConfigurationChanged(Configuration newConfig) {
-        for (int i = 0; i < mAdded.size(); i++) {
-            Fragment f = mAdded.get(i);
-            if (f != null) {
-                f.performConfigurationChanged(newConfig);
-            }
-        }
-    }
-
-    public void dispatchLowMemory() {
-        for (int i = 0; i < mAdded.size(); i++) {
-            Fragment f = mAdded.get(i);
-            if (f != null) {
-                f.performLowMemory();
-            }
-        }
-    }
-
-    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        if (mCurState < Fragment.CREATED) {
-            return false;
-        }
-        boolean show = false;
-        ArrayList<Fragment> newMenus = 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);
-                }
-            }
-        }
-
-        if (mCreatedMenus != null) {
-            for (int i=0; i<mCreatedMenus.size(); i++) {
-                Fragment f = mCreatedMenus.get(i);
-                if (newMenus == null || !newMenus.contains(f)) {
-                    f.onDestroyOptionsMenu();
-                }
-            }
-        }
-
-        mCreatedMenus = newMenus;
-
-        return show;
-    }
-
-    public boolean dispatchPrepareOptionsMenu(Menu menu) {
-        if (mCurState < Fragment.CREATED) {
-            return false;
-        }
-        boolean show = false;
-        for (int i = 0; i < mAdded.size(); i++) {
-            Fragment f = mAdded.get(i);
-            if (f != null) {
-                if (f.performPrepareOptionsMenu(menu)) {
-                    show = true;
-                }
-            }
-        }
-        return show;
-    }
-
-    public boolean dispatchOptionsItemSelected(MenuItem item) {
-        if (mCurState < Fragment.CREATED) {
-            return false;
-        }
-        for (int i = 0; i < mAdded.size(); i++) {
-            Fragment f = mAdded.get(i);
-            if (f != null) {
-                if (f.performOptionsItemSelected(item)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public boolean dispatchContextItemSelected(MenuItem item) {
-        if (mCurState < Fragment.CREATED) {
-            return false;
-        }
-        for (int i = 0; i < mAdded.size(); i++) {
-            Fragment f = mAdded.get(i);
-            if (f != null) {
-                if (f.performContextItemSelected(item)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public void dispatchOptionsMenuClosed(Menu menu) {
-        if (mCurState < Fragment.CREATED) {
-            return;
-        }
-        for (int i = 0; i < mAdded.size(); i++) {
-            Fragment f = mAdded.get(i);
-            if (f != null) {
-                f.performOptionsMenuClosed(menu);
-            }
-        }
-    }
-
-    @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);
-        }
-        mPrimaryNav = f;
-    }
-
-    @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) {
-                    mLifecycleCallbacks.remove(i);
-                    break;
-                }
-            }
-        }
-    }
-
-    void dispatchOnFragmentPreAttached(Fragment f, Context context, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentPreAttached(f, context, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentPreAttached(this, f, context);
-            }
-        }
-    }
-
-    void dispatchOnFragmentAttached(Fragment f, Context context, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentAttached(f, context, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentAttached(this, f, context);
-            }
-        }
-    }
-
-    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();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentCreated(f, savedInstanceState, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentCreated(this, f, savedInstanceState);
-            }
-        }
-    }
-
-    void dispatchOnFragmentActivityCreated(Fragment f, Bundle savedInstanceState,
-            boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentActivityCreated(f, savedInstanceState, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentActivityCreated(this, f, savedInstanceState);
-            }
-        }
-    }
-
-    void dispatchOnFragmentViewCreated(Fragment f, View v, Bundle savedInstanceState,
-            boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentViewCreated(this, f, v, savedInstanceState);
-            }
-        }
-    }
-
-    void dispatchOnFragmentStarted(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentStarted(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentStarted(this, f);
-            }
-        }
-    }
-
-    void dispatchOnFragmentResumed(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentResumed(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentResumed(this, f);
-            }
-        }
-    }
-
-    void dispatchOnFragmentPaused(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentPaused(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentPaused(this, f);
-            }
-        }
-    }
-
-    void dispatchOnFragmentStopped(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentStopped(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentStopped(this, f);
-            }
-        }
-    }
-
-    void dispatchOnFragmentSaveInstanceState(Fragment f, Bundle outState, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentSaveInstanceState(f, outState, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentSaveInstanceState(this, f, outState);
-            }
-        }
-    }
-
-    void dispatchOnFragmentViewDestroyed(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentViewDestroyed(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentViewDestroyed(this, f);
-            }
-        }
-    }
-
-    void dispatchOnFragmentDestroyed(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentDestroyed(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentDestroyed(this, f);
-            }
-        }
-    }
-
-    void dispatchOnFragmentDetached(Fragment f, boolean onlyRecursive) {
-        if (mParent != null) {
-            FragmentManager parentManager = mParent.getFragmentManager();
-            if (parentManager instanceof FragmentManagerImpl) {
-                ((FragmentManagerImpl) parentManager)
-                        .dispatchOnFragmentDetached(f, true);
-            }
-        }
-        for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) {
-            if (!onlyRecursive || p.second) {
-                p.first.onFragmentDetached(this, f);
-            }
-        }
-    }
-
-    public static int reverseTransit(int transit) {
-        int rev = 0;
-        switch (transit) {
-            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
-                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
-                break;
-            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
-                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
-                break;
-            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
-                rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE;
-                break;
-        }
-        return rev;
-
-    }
-
-    public static final int ANIM_STYLE_OPEN_ENTER = 1;
-    public static final int ANIM_STYLE_OPEN_EXIT = 2;
-    public static final int ANIM_STYLE_CLOSE_ENTER = 3;
-    public static final int ANIM_STYLE_CLOSE_EXIT = 4;
-    public static final int ANIM_STYLE_FADE_ENTER = 5;
-    public static final int ANIM_STYLE_FADE_EXIT = 6;
-
-    public static int transitToStyleIndex(int transit, boolean enter) {
-        int animAttr = -1;
-        switch (transit) {
-            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
-                animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT;
-                break;
-            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
-                animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT;
-                break;
-            case FragmentTransaction.TRANSIT_FRAGMENT_FADE:
-                animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT;
-                break;
-        }
-        return animAttr;
-    }
-
-    @Override
-    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        if (!"fragment".equals(name)) {
-            return null;
-        }
-
-        String fname = attrs.getAttributeValue(null, "class");
-        TypedArray a =  context.obtainStyledAttributes(attrs, FragmentTag.Fragment);
-        if (fname == null) {
-            fname = a.getString(FragmentTag.Fragment_name);
-        }
-        int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID);
-        String tag = a.getString(FragmentTag.Fragment_tag);
-        a.recycle();
-
-        if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {
-            // Invalid support lib fragment; let the device's framework handle it.
-            // This will allow android.app.Fragments to do the right thing.
-            return null;
-        }
-
-        int containerId = parent != null ? parent.getId() : 0;
-        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
-            throw new IllegalArgumentException(attrs.getPositionDescription()
-                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
-        }
-
-        // If we restored from a previous state, we may already have
-        // instantiated this fragment from the state and should use
-        // that instance instead of making a new one.
-        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
-        if (fragment == null && tag != null) {
-            fragment = findFragmentByTag(tag);
-        }
-        if (fragment == null && containerId != View.NO_ID) {
-            fragment = findFragmentById(containerId);
-        }
-
-        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
-                + Integer.toHexString(id) + " fname=" + fname
-                + " existing=" + fragment);
-        if (fragment == null) {
-            fragment = mContainer.instantiate(context, fname, null);
-            fragment.mFromLayout = true;
-            fragment.mFragmentId = id != 0 ? id : containerId;
-            fragment.mContainerId = containerId;
-            fragment.mTag = tag;
-            fragment.mInLayout = true;
-            fragment.mFragmentManager = this;
-            fragment.mHost = mHost;
-            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
-            addFragment(fragment, true);
-
-        } else if (fragment.mInLayout) {
-            // A fragment already exists and it is not one we restored from
-            // previous state.
-            throw new IllegalArgumentException(attrs.getPositionDescription()
-                    + ": Duplicate id 0x" + Integer.toHexString(id)
-                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
-                    + " with another fragment for " + fname);
-        } else {
-            // This fragment was retained from a previous instance; get it
-            // going now.
-            fragment.mInLayout = true;
-            fragment.mHost = mHost;
-            // If this fragment is newly instantiated (either right now, or
-            // from last saved state), then give it the attributes to
-            // initialize itself.
-            if (!fragment.mRetaining) {
-                fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
-            }
-        }
-
-        // If we haven't finished entering the CREATED state ourselves yet,
-        // 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 {
-            moveToState(fragment);
-        }
-
-        if (fragment.mView == null) {
-            throw new IllegalStateException("Fragment " + fname
-                    + " did not create a view.");
-        }
-        if (id != 0) {
-            fragment.mView.setId(id);
-        }
-        if (fragment.mView.getTag() == null) {
-            fragment.mView.setTag(tag);
-        }
-        return fragment.mView;
-    }
-
-    @Override
-    public View onCreateView(String name, Context context, AttributeSet attrs) {
-        return onCreateView(null, name, context, attrs);
-    }
-
-    LayoutInflater.Factory2 getLayoutInflaterFactory() {
-        return this;
-    }
-
-    static class FragmentTag {
-        public static final int[] Fragment = {
-                0x01010003, 0x010100d0, 0x010100d1
-        };
-        public static final int Fragment_id = 1;
-        public static final int Fragment_name = 0;
-        public static final int Fragment_tag = 2;
-    }
-
-    /**
-     * An add or pop transaction to be scheduled for the UI thread.
-     */
-    interface OpGenerator {
-        /**
-         * Generate transactions to add to {@code records} and whether or not the transaction is
-         * an add or pop to {@code isRecordPop}.
-         *
-         * records and isRecordPop must be added equally so that each transaction in records
-         * matches the boolean for whether or not it is a pop in isRecordPop.
-         *
-         * @param records A list to add transactions to.
-         * @param isRecordPop A list to add whether or not the transactions added to records is
-         *                    a pop transaction.
-         * @return true if something was added or false otherwise.
-         */
-        boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
-    }
-
-    /**
-     * A pop operation OpGenerator. This will be run on the UI thread and will generate the
-     * transactions that will be popped if anything can be popped.
-     */
-    private class PopBackStackState implements OpGenerator {
-        final String mName;
-        final int mId;
-        final int mFlags;
-
-        PopBackStackState(String name, int id, int flags) {
-            mName = name;
-            mId = id;
-            mFlags = flags;
-        }
-
-        @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);
-        }
-    }
-
-    /**
-     * A listener for a postponed transaction. This waits until
-     * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started
-     * that interacts with this one, based on interactions with the fragment container.
-     */
-    static class StartEnterTransitionListener
-            implements Fragment.OnStartEnterTransitionListener {
-        private final boolean mIsBack;
-        private final BackStackRecord mRecord;
-        private int mNumPostponed;
-
-        StartEnterTransitionListener(BackStackRecord record, boolean isBack) {
-            mIsBack = isBack;
-            mRecord = record;
-        }
-
-        /**
-         * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the
-         * number of Fragments that are postponed. This may cause the transaction to schedule
-         * to finish running and run transitions and animations.
-         */
-        @Override
-        public void onStartEnterTransition() {
-            mNumPostponed--;
-            if (mNumPostponed != 0) {
-                return;
-            }
-            mRecord.mManager.scheduleCommit();
-        }
-
-        /**
-         * Called from {@link Fragment#
-         * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this
-         * increases the number of fragments that are postponed as part of this transaction.
-         */
-        @Override
-        public void startListening() {
-            mNumPostponed++;
-        }
-
-        /**
-         * @return true if there are no more postponed fragments as part of the transaction.
-         */
-        public boolean isReady() {
-            return mNumPostponed == 0;
-        }
-
-        /**
-         * Completes the transaction and start the animations and transitions. This may skip
-         * the transitions if this is called before all fragments have called
-         * {@link Fragment#startPostponedEnterTransition()}.
-         */
-        public void completeTransaction() {
-            final boolean canceled;
-            canceled = mNumPostponed > 0;
-            FragmentManagerImpl manager = mRecord.mManager;
-            final int numAdded = manager.mAdded.size();
-            for (int i = 0; i < numAdded; i++) {
-                final Fragment fragment = manager.mAdded.get(i);
-                fragment.setOnStartEnterTransitionListener(null);
-                if (canceled && fragment.isPostponed()) {
-                    fragment.startPostponedEnterTransition();
-                }
-            }
-            mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true);
-        }
-
-        /**
-         * Cancels this transaction instead of completing it. That means that the state isn't
-         * changed, so the pop results in no change to the state.
-         */
-        public void cancelTransaction() {
-            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/android/support/v4/app/FragmentManagerNonConfig.java b/android/support/v4/app/FragmentManagerNonConfig.java
deleted file mode 100644
index 832426a..0000000
--- a/android/support/v4/app/FragmentManagerNonConfig.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.v4.app;
-
-import android.os.Parcelable;
-
-import java.util.List;
-
-/**
- * FragmentManagerNonConfig stores the retained instance fragments across
- * activity recreation events.
- *
- * <p>Apps should treat objects of this type as opaque, returned by
- * and passed to the state save and restore process for fragments in
- * {@link FragmentController#retainNonConfig()} and
- * {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
- */
-public class FragmentManagerNonConfig {
-    private final List<Fragment> mFragments;
-    private final List<FragmentManagerNonConfig> mChildNonConfigs;
-
-    FragmentManagerNonConfig(List<Fragment> fragments,
-            List<FragmentManagerNonConfig> childNonConfigs) {
-        mFragments = fragments;
-        mChildNonConfigs = childNonConfigs;
-    }
-
-    /**
-     * @return the retained instance fragments returned by a FragmentManager
-     */
-    List<Fragment> getFragments() {
-        return mFragments;
-    }
-
-    /**
-     * @return the FragmentManagerNonConfigs from any applicable fragment's child FragmentManager
-     */
-    List<FragmentManagerNonConfig> getChildNonConfigs() {
-        return mChildNonConfigs;
-    }
-}
diff --git a/android/support/v4/app/FragmentPagerAdapter.java b/android/support/v4/app/FragmentPagerAdapter.java
deleted file mode 100644
index 6b25d2f..0000000
--- a/android/support/v4/app/FragmentPagerAdapter.java
+++ /dev/null
@@ -1,180 +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.os.Parcelable;
-import android.support.v4.view.PagerAdapter;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Implementation of {@link PagerAdapter} that
- * represents each page as a {@link Fragment} that is persistently
- * kept in the fragment manager as long as the user can return to the page.
- *
- * <p>This version of the pager is best for use when there are a handful of
- * typically more static fragments to be paged through, such as a set of tabs.
- * The fragment of each page the user visits will be kept in memory, though its
- * view hierarchy may be destroyed when not visible.  This can result in using
- * a significant amount of memory since fragment instances can hold on to an
- * arbitrary amount of state.  For larger sets of pages, consider
- * {@link FragmentStatePagerAdapter}.
- *
- * <p>When using FragmentPagerAdapter the host ViewPager must have a
- * valid ID set.</p>
- *
- * <p>Subclasses only need to implement {@link #getItem(int)}
- * and {@link #getCount()} to have a working adapter.
- *
- * <p>Here is an example implementation of a pager containing fragments of
- * lists:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentPagerSupport.java
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager.xml
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager_list</code> resource containing each
- * individual fragment's layout is:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/res/layout/fragment_pager_list.xml
- *      complete}
- */
-public abstract class FragmentPagerAdapter extends PagerAdapter {
-    private static final String TAG = "FragmentPagerAdapter";
-    private static final boolean DEBUG = false;
-
-    private final FragmentManager mFragmentManager;
-    private FragmentTransaction mCurTransaction = null;
-    private Fragment mCurrentPrimaryItem = null;
-
-    public FragmentPagerAdapter(FragmentManager fm) {
-        mFragmentManager = fm;
-    }
-
-    /**
-     * Return the Fragment associated with a specified position.
-     */
-    public abstract Fragment getItem(int position);
-
-    @Override
-    public void startUpdate(ViewGroup container) {
-        if (container.getId() == View.NO_ID) {
-            throw new IllegalStateException("ViewPager with adapter " + this
-                    + " requires a view id");
-        }
-    }
-
-    @SuppressWarnings("ReferenceEquality")
-    @Override
-    public Object instantiateItem(ViewGroup container, int position) {
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-
-        final long itemId = getItemId(position);
-
-        // Do we already have this fragment?
-        String name = makeFragmentName(container.getId(), itemId);
-        Fragment fragment = mFragmentManager.findFragmentByTag(name);
-        if (fragment != null) {
-            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
-            mCurTransaction.attach(fragment);
-        } else {
-            fragment = getItem(position);
-            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
-            mCurTransaction.add(container.getId(), fragment,
-                    makeFragmentName(container.getId(), itemId));
-        }
-        if (fragment != mCurrentPrimaryItem) {
-            fragment.setMenuVisibility(false);
-            fragment.setUserVisibleHint(false);
-        }
-
-        return fragment;
-    }
-
-    @Override
-    public void destroyItem(ViewGroup container, int position, Object object) {
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
-                + " v=" + ((Fragment)object).getView());
-        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) {
-                mCurrentPrimaryItem.setMenuVisibility(false);
-                mCurrentPrimaryItem.setUserVisibleHint(false);
-            }
-            if (fragment != null) {
-                fragment.setMenuVisibility(true);
-                fragment.setUserVisibleHint(true);
-            }
-            mCurrentPrimaryItem = fragment;
-        }
-    }
-
-    @Override
-    public void finishUpdate(ViewGroup container) {
-        if (mCurTransaction != null) {
-            mCurTransaction.commitNowAllowingStateLoss();
-            mCurTransaction = null;
-        }
-    }
-
-    @Override
-    public boolean isViewFromObject(View view, Object object) {
-        return ((Fragment)object).getView() == view;
-    }
-
-    @Override
-    public Parcelable saveState() {
-        return null;
-    }
-
-    @Override
-    public void restoreState(Parcelable state, ClassLoader loader) {
-    }
-
-    /**
-     * Return a unique identifier for the item at the given position.
-     *
-     * <p>The default implementation returns the given position.
-     * Subclasses should override this method if the positions of items can change.</p>
-     *
-     * @param position Position within this adapter
-     * @return Unique identifier for the item at position
-     */
-    public long getItemId(int position) {
-        return position;
-    }
-
-    private static String makeFragmentName(int viewId, long id) {
-        return "android:switcher:" + viewId + ":" + id;
-    }
-}
diff --git a/android/support/v4/app/FragmentState.java b/android/support/v4/app/FragmentState.java
deleted file mode 100644
index dbe6327..0000000
--- a/android/support/v4/app/FragmentState.java
+++ /dev/null
@@ -1,137 +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.app;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-final class FragmentState implements Parcelable {
-    final String mClassName;
-    final int mIndex;
-    final boolean mFromLayout;
-    final int mFragmentId;
-    final int mContainerId;
-    final String mTag;
-    final boolean mRetainInstance;
-    final boolean mDetached;
-    final Bundle mArguments;
-    final boolean mHidden;
-
-    Bundle mSavedFragmentState;
-
-    Fragment mInstance;
-
-    FragmentState(Fragment frag) {
-        mClassName = frag.getClass().getName();
-        mIndex = frag.mIndex;
-        mFromLayout = frag.mFromLayout;
-        mFragmentId = frag.mFragmentId;
-        mContainerId = frag.mContainerId;
-        mTag = frag.mTag;
-        mRetainInstance = frag.mRetainInstance;
-        mDetached = frag.mDetached;
-        mArguments = frag.mArguments;
-        mHidden = frag.mHidden;
-    }
-
-    FragmentState(Parcel in) {
-        mClassName = in.readString();
-        mIndex = in.readInt();
-        mFromLayout = in.readInt() != 0;
-        mFragmentId = in.readInt();
-        mContainerId = in.readInt();
-        mTag = in.readString();
-        mRetainInstance = in.readInt() != 0;
-        mDetached = in.readInt() != 0;
-        mArguments = in.readBundle();
-        mHidden = in.readInt() != 0;
-        mSavedFragmentState = in.readBundle();
-    }
-
-    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());
-            }
-
-            if (container != null) {
-                mInstance = container.instantiate(context, mClassName, mArguments);
-            } else {
-                mInstance = Fragment.instantiate(context, mClassName, mArguments);
-            }
-
-            if (mSavedFragmentState != null) {
-                mSavedFragmentState.setClassLoader(context.getClassLoader());
-                mInstance.mSavedFragmentState = mSavedFragmentState;
-            }
-            mInstance.setIndex(mIndex, parent);
-            mInstance.mFromLayout = mFromLayout;
-            mInstance.mRestored = true;
-            mInstance.mFragmentId = mFragmentId;
-            mInstance.mContainerId = mContainerId;
-            mInstance.mTag = mTag;
-            mInstance.mRetainInstance = mRetainInstance;
-            mInstance.mDetached = mDetached;
-            mInstance.mHidden = mHidden;
-            mInstance.mFragmentManager = host.mFragmentManager;
-
-            if (FragmentManagerImpl.DEBUG) {
-                Log.v(FragmentManagerImpl.TAG, "Instantiated fragment " + mInstance);
-            }
-        }
-        mInstance.mChildNonConfig = childNonConfig;
-        return mInstance;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mClassName);
-        dest.writeInt(mIndex);
-        dest.writeInt(mFromLayout ? 1 : 0);
-        dest.writeInt(mFragmentId);
-        dest.writeInt(mContainerId);
-        dest.writeString(mTag);
-        dest.writeInt(mRetainInstance ? 1 : 0);
-        dest.writeInt(mDetached ? 1 : 0);
-        dest.writeBundle(mArguments);
-        dest.writeInt(mHidden ? 1 : 0);
-        dest.writeBundle(mSavedFragmentState);
-    }
-
-    public static final Parcelable.Creator<FragmentState> CREATOR =
-            new Parcelable.Creator<FragmentState>() {
-                @Override
-                public FragmentState createFromParcel(Parcel in) {
-                    return new FragmentState(in);
-                }
-
-                @Override
-                public FragmentState[] newArray(int size) {
-                    return new FragmentState[size];
-                }
-            };
-}
diff --git a/android/support/v4/app/FragmentStatePagerAdapter.java b/android/support/v4/app/FragmentStatePagerAdapter.java
deleted file mode 100644
index 040f2db..0000000
--- a/android/support/v4/app/FragmentStatePagerAdapter.java
+++ /dev/null
@@ -1,230 +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.os.Bundle;
-import android.os.Parcelable;
-import android.support.v4.view.PagerAdapter;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * Implementation of {@link PagerAdapter} that
- * uses a {@link Fragment} to manage each page. This class also handles
- * saving and restoring of fragment's state.
- *
- * <p>This version of the pager is more useful when there are a large number
- * of pages, working more like a list view.  When pages are not visible to
- * the user, their entire fragment may be destroyed, only keeping the saved
- * state of that fragment.  This allows the pager to hold on to much less
- * memory associated with each visited page as compared to
- * {@link FragmentPagerAdapter} at the cost of potentially more overhead when
- * switching between pages.
- *
- * <p>When using FragmentPagerAdapter the host ViewPager must have a
- * valid ID set.</p>
- *
- * <p>Subclasses only need to implement {@link #getItem(int)}
- * and {@link #getCount()} to have a working adapter.
- *
- * <p>Here is an example implementation of a pager containing fragments of
- * lists:
- *
- * {@sample frameworks/support/samples/Support13Demos/src/main/java/com/example/android/supportv13/app/FragmentStatePagerSupport.java
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:
- *
- * {@sample frameworks/support/samples/Support13Demos/src/main/res/layout/fragment_pager.xml
- *      complete}
- *
- * <p>The <code>R.layout.fragment_pager_list</code> resource containing each
- * individual fragment's layout is:
- *
- * {@sample frameworks/support/samples/Support13Demos/src/main/res/layout/fragment_pager_list.xml
- *      complete}
- */
-public abstract class FragmentStatePagerAdapter extends PagerAdapter {
-    private static final String TAG = "FragmentStatePagerAdapt";
-    private static final boolean DEBUG = false;
-
-    private final FragmentManager mFragmentManager;
-    private FragmentTransaction mCurTransaction = null;
-
-    private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
-    private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
-    private Fragment mCurrentPrimaryItem = null;
-
-    public FragmentStatePagerAdapter(FragmentManager fm) {
-        mFragmentManager = fm;
-    }
-
-    /**
-     * Return the Fragment associated with a specified position.
-     */
-    public abstract Fragment getItem(int position);
-
-    @Override
-    public void startUpdate(ViewGroup container) {
-        if (container.getId() == View.NO_ID) {
-            throw new IllegalStateException("ViewPager with adapter " + this
-                    + " requires a view id");
-        }
-    }
-
-    @Override
-    public Object instantiateItem(ViewGroup container, int position) {
-        // If we already have this item instantiated, there is nothing
-        // to do.  This can happen when we are restoring the entire pager
-        // from its saved state, where the fragment manager has already
-        // taken care of restoring the fragments we previously had instantiated.
-        if (mFragments.size() > position) {
-            Fragment f = mFragments.get(position);
-            if (f != null) {
-                return f;
-            }
-        }
-
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-
-        Fragment fragment = getItem(position);
-        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
-        if (mSavedState.size() > position) {
-            Fragment.SavedState fss = mSavedState.get(position);
-            if (fss != null) {
-                fragment.setInitialSavedState(fss);
-            }
-        }
-        while (mFragments.size() <= position) {
-            mFragments.add(null);
-        }
-        fragment.setMenuVisibility(false);
-        fragment.setUserVisibleHint(false);
-        mFragments.set(position, fragment);
-        mCurTransaction.add(container.getId(), fragment);
-
-        return fragment;
-    }
-
-    @Override
-    public void destroyItem(ViewGroup container, int position, Object object) {
-        Fragment fragment = (Fragment) object;
-
-        if (mCurTransaction == null) {
-            mCurTransaction = mFragmentManager.beginTransaction();
-        }
-        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
-                + " v=" + ((Fragment)object).getView());
-        while (mSavedState.size() <= position) {
-            mSavedState.add(null);
-        }
-        mSavedState.set(position, fragment.isAdded()
-                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
-        mFragments.set(position, null);
-
-        mCurTransaction.remove(fragment);
-    }
-
-    @Override
-    @SuppressWarnings("ReferenceEquality")
-    public void setPrimaryItem(ViewGroup container, int position, Object object) {
-        Fragment fragment = (Fragment)object;
-        if (fragment != mCurrentPrimaryItem) {
-            if (mCurrentPrimaryItem != null) {
-                mCurrentPrimaryItem.setMenuVisibility(false);
-                mCurrentPrimaryItem.setUserVisibleHint(false);
-            }
-            if (fragment != null) {
-                fragment.setMenuVisibility(true);
-                fragment.setUserVisibleHint(true);
-            }
-            mCurrentPrimaryItem = fragment;
-        }
-    }
-
-    @Override
-    public void finishUpdate(ViewGroup container) {
-        if (mCurTransaction != null) {
-            mCurTransaction.commitNowAllowingStateLoss();
-            mCurTransaction = null;
-        }
-    }
-
-    @Override
-    public boolean isViewFromObject(View view, Object object) {
-        return ((Fragment)object).getView() == view;
-    }
-
-    @Override
-    public Parcelable saveState() {
-        Bundle state = null;
-        if (mSavedState.size() > 0) {
-            state = new Bundle();
-            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
-            mSavedState.toArray(fss);
-            state.putParcelableArray("states", fss);
-        }
-        for (int i=0; i<mFragments.size(); i++) {
-            Fragment f = mFragments.get(i);
-            if (f != null && f.isAdded()) {
-                if (state == null) {
-                    state = new Bundle();
-                }
-                String key = "f" + i;
-                mFragmentManager.putFragment(state, key, f);
-            }
-        }
-        return state;
-    }
-
-    @Override
-    public void restoreState(Parcelable state, ClassLoader loader) {
-        if (state != null) {
-            Bundle bundle = (Bundle)state;
-            bundle.setClassLoader(loader);
-            Parcelable[] fss = bundle.getParcelableArray("states");
-            mSavedState.clear();
-            mFragments.clear();
-            if (fss != null) {
-                for (int i=0; i<fss.length; i++) {
-                    mSavedState.add((Fragment.SavedState)fss[i]);
-                }
-            }
-            Iterable<String> keys = bundle.keySet();
-            for (String key: keys) {
-                if (key.startsWith("f")) {
-                    int index = Integer.parseInt(key.substring(1));
-                    Fragment f = mFragmentManager.getFragment(bundle, key);
-                    if (f != null) {
-                        while (mFragments.size() <= index) {
-                            mFragments.add(null);
-                        }
-                        f.setMenuVisibility(false);
-                        mFragments.set(index, f);
-                    } else {
-                        Log.w(TAG, "Bad fragment at key " + key);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v4/app/FragmentTabHost.java b/android/support/v4/app/FragmentTabHost.java
deleted file mode 100644
index 6b914fe..0000000
--- a/android/support/v4/app/FragmentTabHost.java
+++ /dev/null
@@ -1,372 +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.content.Context;
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.TabHost;
-import android.widget.TabWidget;
-
-import java.util.ArrayList;
-
-/**
- * Special TabHost that allows the use of {@link Fragment} objects for
- * its tab content.  When placing this in a view hierarchy, after inflating
- * the hierarchy you must call {@link #setup(Context, FragmentManager, int)}
- * to complete the initialization of the tab host.
- *
- * <p>Here is a simple example of using a FragmentTabHost in an Activity:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentTabs.java
- *      complete}
- *
- * <p>This can also be used inside of a fragment through fragment nesting:
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java
- *      complete}
- */
-public class FragmentTabHost extends TabHost
-        implements TabHost.OnTabChangeListener {
-    private final ArrayList<TabInfo> mTabs = new ArrayList<>();
-
-    private FrameLayout mRealTabContent;
-    private Context mContext;
-    private FragmentManager mFragmentManager;
-    private int mContainerId;
-    private TabHost.OnTabChangeListener mOnTabChangeListener;
-    private TabInfo mLastTab;
-    private boolean mAttached;
-
-    static final class TabInfo {
-        final @NonNull String tag;
-        final @NonNull Class<?> clss;
-        final @Nullable Bundle args;
-        Fragment fragment;
-
-        TabInfo(@NonNull String _tag, @NonNull Class<?> _class, @Nullable Bundle _args) {
-            tag = _tag;
-            clss = _class;
-            args = _args;
-        }
-    }
-
-    static class DummyTabFactory implements TabHost.TabContentFactory {
-        private final Context mContext;
-
-        public DummyTabFactory(Context context) {
-            mContext = context;
-        }
-
-        @Override
-        public View createTabContent(String tag) {
-            View v = new View(mContext);
-            v.setMinimumWidth(0);
-            v.setMinimumHeight(0);
-            return v;
-        }
-    }
-
-    static class SavedState extends BaseSavedState {
-        String curTab;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        SavedState(Parcel in) {
-            super(in);
-            curTab = in.readString();
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeString(curTab);
-        }
-
-        @Override
-        public String toString() {
-            return "FragmentTabHost.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " curTab=" + curTab + "}";
-        }
-
-        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];
-            }
-        };
-    }
-
-    public FragmentTabHost(Context context) {
-        // Note that we call through to the version that takes an AttributeSet,
-        // because the simple Context construct can result in a broken object!
-        super(context, null);
-        initFragmentTabHost(context, null);
-    }
-
-    public FragmentTabHost(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        initFragmentTabHost(context, attrs);
-    }
-
-    private void initFragmentTabHost(Context context, AttributeSet attrs) {
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                new int[] { android.R.attr.inflatedId }, 0, 0);
-        mContainerId = a.getResourceId(0, 0);
-        a.recycle();
-
-        super.setOnTabChangedListener(this);
-    }
-
-    private void ensureHierarchy(Context context) {
-        // If owner hasn't made its own view hierarchy, then as a convenience
-        // we will construct a standard one here.
-        if (findViewById(android.R.id.tabs) == null) {
-            LinearLayout ll = new LinearLayout(context);
-            ll.setOrientation(LinearLayout.VERTICAL);
-            addView(ll, new FrameLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.MATCH_PARENT));
-
-            TabWidget tw = new TabWidget(context);
-            tw.setId(android.R.id.tabs);
-            tw.setOrientation(TabWidget.HORIZONTAL);
-            ll.addView(tw, new LinearLayout.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, 0));
-
-            FrameLayout fl = new FrameLayout(context);
-            fl.setId(android.R.id.tabcontent);
-            ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));
-
-            mRealTabContent = fl = new FrameLayout(context);
-            mRealTabContent.setId(mContainerId);
-            ll.addView(fl, new LinearLayout.LayoutParams(
-                    LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
-        }
-    }
-
-    /**
-     * @deprecated Don't call the original TabHost setup, you must instead
-     * call {@link #setup(Context, FragmentManager)} or
-     * {@link #setup(Context, FragmentManager, int)}.
-     */
-    @Override @Deprecated
-    public void setup() {
-        throw new IllegalStateException(
-                "Must call setup() that takes a Context and FragmentManager");
-    }
-
-    public void setup(Context context, FragmentManager manager) {
-        ensureHierarchy(context);  // Ensure views required by super.setup()
-        super.setup();
-        mContext = context;
-        mFragmentManager = manager;
-        ensureContent();
-    }
-
-    public void setup(Context context, FragmentManager manager, int containerId) {
-        ensureHierarchy(context);  // Ensure views required by super.setup()
-        super.setup();
-        mContext = context;
-        mFragmentManager = manager;
-        mContainerId = containerId;
-        ensureContent();
-        mRealTabContent.setId(containerId);
-
-        // We must have an ID to be able to save/restore our state.  If
-        // the owner hasn't set one at this point, we will set it ourselves.
-        if (getId() == View.NO_ID) {
-            setId(android.R.id.tabhost);
-        }
-    }
-
-    private void ensureContent() {
-        if (mRealTabContent == null) {
-            mRealTabContent = (FrameLayout)findViewById(mContainerId);
-            if (mRealTabContent == null) {
-                throw new IllegalStateException(
-                        "No tab content FrameLayout found for id " + mContainerId);
-            }
-        }
-    }
-
-    @Override
-    public void setOnTabChangedListener(OnTabChangeListener l) {
-        mOnTabChangeListener = l;
-    }
-
-    public void addTab(@NonNull TabHost.TabSpec tabSpec, @NonNull Class<?> clss,
-            @Nullable Bundle args) {
-        tabSpec.setContent(new DummyTabFactory(mContext));
-
-        final String tag = tabSpec.getTag();
-        final TabInfo info = new TabInfo(tag, clss, args);
-
-        if (mAttached) {
-            // If we are already attached to the window, then check to make
-            // sure this tab's fragment is inactive if it exists.  This shouldn't
-            // normally happen.
-            info.fragment = mFragmentManager.findFragmentByTag(tag);
-            if (info.fragment != null && !info.fragment.isDetached()) {
-                final FragmentTransaction ft = mFragmentManager.beginTransaction();
-                ft.detach(info.fragment);
-                ft.commit();
-            }
-        }
-
-        mTabs.add(info);
-        addTab(tabSpec);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        final String currentTag = getCurrentTabTag();
-
-        // Go through all tabs and make sure their fragments match
-        // the correct state.
-        FragmentTransaction ft = null;
-        for (int i = 0, count = mTabs.size(); i < count; i++) {
-            final TabInfo tab = mTabs.get(i);
-            tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
-            if (tab.fragment != null && !tab.fragment.isDetached()) {
-                if (tab.tag.equals(currentTag)) {
-                    // The fragment for this tab is already there and
-                    // active, and it is what we really want to have
-                    // as the current tab.  Nothing to do.
-                    mLastTab = tab;
-                } else {
-                    // This fragment was restored in the active state,
-                    // but is not the current tab.  Deactivate it.
-                    if (ft == null) {
-                        ft = mFragmentManager.beginTransaction();
-                    }
-                    ft.detach(tab.fragment);
-                }
-            }
-        }
-
-        // We are now ready to go.  Make sure we are switched to the
-        // correct tab.
-        mAttached = true;
-        ft = doTabChanged(currentTag, ft);
-        if (ft != null) {
-            ft.commit();
-            mFragmentManager.executePendingTransactions();
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mAttached = false;
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        SavedState ss = new SavedState(superState);
-        ss.curTab = getCurrentTabTag();
-        return ss;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-        SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-        setCurrentTabByTag(ss.curTab);
-    }
-
-    @Override
-    public void onTabChanged(String tabId) {
-        if (mAttached) {
-            final FragmentTransaction ft = doTabChanged(tabId, null);
-            if (ft != null) {
-                ft.commit();
-            }
-        }
-        if (mOnTabChangeListener != null) {
-            mOnTabChangeListener.onTabChanged(tabId);
-        }
-    }
-
-    @Nullable
-    private FragmentTransaction doTabChanged(@Nullable String tag,
-            @Nullable FragmentTransaction ft) {
-        final TabInfo newTab = getTabInfoForTag(tag);
-        if (mLastTab != newTab) {
-            if (ft == null) {
-                ft = mFragmentManager.beginTransaction();
-            }
-
-            if (mLastTab != null) {
-                if (mLastTab.fragment != null) {
-                    ft.detach(mLastTab.fragment);
-                }
-            }
-
-            if (newTab != null) {
-                if (newTab.fragment == null) {
-                    newTab.fragment = Fragment.instantiate(mContext,
-                            newTab.clss.getName(), newTab.args);
-                    ft.add(mContainerId, newTab.fragment, newTab.tag);
-                } else {
-                    ft.attach(newTab.fragment);
-                }
-            }
-
-            mLastTab = newTab;
-        }
-
-        return ft;
-    }
-
-    @Nullable
-    private TabInfo getTabInfoForTag(String tabId) {
-        for (int i = 0, count = mTabs.size(); i < count; i++) {
-            final TabInfo tab = mTabs.get(i);
-            if (tab.tag.equals(tabId)) {
-                return tab;
-            }
-        }
-        return null;
-    }
-}
diff --git a/android/support/v4/app/FragmentTransaction.java b/android/support/v4/app/FragmentTransaction.java
deleted file mode 100644
index 75c6f88..0000000
--- a/android/support/v4/app/FragmentTransaction.java
+++ /dev/null
@@ -1,446 +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 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;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.annotation.StyleRes;
-import android.view.View;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Static library support version of the framework's {@link android.app.FragmentTransaction}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public abstract class FragmentTransaction {
-    /**
-     * Calls {@link #add(int, Fragment, String)} with a 0 containerViewId.
-     */
-    public abstract FragmentTransaction add(Fragment fragment, String tag);
-    
-    /**
-     * Calls {@link #add(int, Fragment, String)} with a null tag.
-     */
-    public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);
-    
-    /**
-     * Add a fragment to the activity state.  This fragment may optionally
-     * also have its view (if {@link Fragment#onCreateView Fragment.onCreateView}
-     * returns non-null) into a container view of the activity.
-     * 
-     * @param containerViewId Optional identifier of the container this fragment is
-     * to be placed in.  If 0, it will not be placed in a container.
-     * @param fragment The fragment to be added.  This fragment must not already
-     * be added to the activity.
-     * @param tag Optional tag name for the fragment, to later retrieve the
-     * fragment with {@link FragmentManager#findFragmentByTag(String)
-     * FragmentManager.findFragmentByTag(String)}.
-     * 
-     * @return Returns the same FragmentTransaction instance.
-     */
-    public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment,
-            @Nullable String tag);
-    
-    /**
-     * Calls {@link #replace(int, Fragment, String)} with a null tag.
-     */
-    public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);
-    
-    /**
-     * Replace an existing fragment that was added to a container.  This is
-     * essentially the same as calling {@link #remove(Fragment)} for all
-     * currently added fragments that were added with the same containerViewId
-     * and then {@link #add(int, Fragment, String)} with the same arguments
-     * given here.
-     * 
-     * @param containerViewId Identifier of the container whose fragment(s) are
-     * to be replaced.
-     * @param fragment The new fragment to place in the container.
-     * @param tag Optional tag name for the fragment, to later retrieve the
-     * fragment with {@link FragmentManager#findFragmentByTag(String)
-     * FragmentManager.findFragmentByTag(String)}.
-     * 
-     * @return Returns the same FragmentTransaction instance.
-     */
-    public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment,
-            @Nullable String tag);
-    
-    /**
-     * Remove an existing fragment.  If it was added to a container, its view
-     * is also removed from that container.
-     * 
-     * @param fragment The fragment to be removed.
-     * 
-     * @return Returns the same FragmentTransaction instance.
-     */
-    public abstract FragmentTransaction remove(Fragment fragment);
-    
-    /**
-     * Hides an existing fragment.  This is only relevant for fragments whose
-     * views have been added to a container, as this will cause the view to
-     * be hidden.
-     * 
-     * @param fragment The fragment to be hidden.
-     * 
-     * @return Returns the same FragmentTransaction instance.
-     */
-    public abstract FragmentTransaction hide(Fragment fragment);
-    
-    /**
-     * Shows a previously hidden fragment.  This is only relevant for fragments whose
-     * views have been added to a container, as this will cause the view to
-     * be shown.
-     * 
-     * @param fragment The fragment to be shown.
-     * 
-     * @return Returns the same FragmentTransaction instance.
-     */
-    public abstract FragmentTransaction show(Fragment fragment);
-
-    /**
-     * Detach the given fragment from the UI.  This is the same state as
-     * when it is put on the back stack: the fragment is removed from
-     * the UI, however its state is still being actively managed by the
-     * fragment manager.  When going into this state its view hierarchy
-     * is destroyed.
-     *
-     * @param fragment The fragment to be detached.
-     *
-     * @return Returns the same FragmentTransaction instance.
-     */
-    public abstract FragmentTransaction detach(Fragment fragment);
-
-    /**
-     * Re-attach a fragment after it had previously been detached from
-     * the UI with {@link #detach(Fragment)}.  This
-     * causes its view hierarchy to be re-created, attached to the UI,
-     * and displayed.
-     *
-     * @param fragment The fragment to be attached.
-     *
-     * @return Returns the same FragmentTransaction instance.
-     */
-    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.
-     */
-    public abstract boolean isEmpty();
-    
-    /**
-     * Bit mask that is set for all enter transitions.
-     */
-    public static final int TRANSIT_ENTER_MASK = 0x1000;
-    
-    /**
-     * Bit mask that is set for all exit transitions.
-     */
-    public static final int TRANSIT_EXIT_MASK = 0x2000;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface Transit {}
-
-    /** Not set up for a transition. */
-    public static final int TRANSIT_UNSET = -1;
-    /** No animation for transition. */
-    public static final int TRANSIT_NONE = 0;
-    /** Fragment is being added onto the stack */
-    public static final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK;
-    /** Fragment is being removed from the stack */
-    public static final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK;
-    /** Fragment should simply fade in or out; that is, no strong navigation associated
-     * with it except that it is appearing or disappearing for some reason. */
-    public static final int TRANSIT_FRAGMENT_FADE = 3 | TRANSIT_ENTER_MASK;
-
-    /**
-     * 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(@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(@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
-     * Fragment to a View from a shown or added Fragment.
-     * <var>sharedElement</var> must have a unique transitionName in the View hierarchy.
-     *
-     * @param sharedElement A View in a disappearing Fragment to match with a View in an
-     *                      appearing Fragment.
-     * @param name The transitionName for a View in an appearing Fragment to match to the shared
-     *             element.
-     * @see Fragment#setSharedElementReturnTransition(Object)
-     * @see Fragment#setSharedElementEnterTransition(Object)
-     */
-    public abstract FragmentTransaction addSharedElement(View sharedElement, String name);
-
-    /**
-     * Select a standard transition animation for this transaction.  May be
-     * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
-     * {@link #TRANSIT_FRAGMENT_CLOSE}, or {@link #TRANSIT_FRAGMENT_FADE}.
-     */
-    public abstract FragmentTransaction setTransition(@Transit int transit);
-
-    /**
-     * Set a custom style resource that will be used for resolving transit
-     * animations.
-     */
-    public abstract FragmentTransaction setTransitionStyle(@StyleRes int styleRes);
-    
-    /**
-     * Add this transaction to the back stack.  This means that the transaction
-     * will be remembered after it is committed, and will reverse its operation
-     * when later popped off the stack.
-     *
-     * @param name An optional name for this back stack state, or null.
-     */
-    public abstract FragmentTransaction addToBackStack(@Nullable String name);
-
-    /**
-     * Returns true if this FragmentTransaction is allowed to be added to the back
-     * stack. If this method would return false, {@link #addToBackStack(String)}
-     * will throw {@link IllegalStateException}.
-     *
-     * @return True if {@link #addToBackStack(String)} is permitted on this transaction.
-     */
-    public abstract boolean isAddToBackStackAllowed();
-
-    /**
-     * Disallow calls to {@link #addToBackStack(String)}. Any future calls to
-     * addToBackStack will throw {@link IllegalStateException}. If addToBackStack
-     * has already been called, this method will throw IllegalStateException.
-     */
-    public abstract FragmentTransaction disallowAddToBackStack();
-
-    /**
-     * Set the full title to show as a bread crumb when this transaction
-     * is on the back stack.
-     *
-     * @param res A string resource containing the title.
-     */
-    public abstract FragmentTransaction setBreadCrumbTitle(@StringRes int res);
-
-    /**
-     * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this
-     * method is <em>not</em> recommended, as the string can not be changed
-     * later if the locale changes.
-     */
-    public abstract FragmentTransaction setBreadCrumbTitle(CharSequence text);
-
-    /**
-     * Set the short title to show as a bread crumb when this transaction
-     * is on the back stack.
-     *
-     * @param res A string resource containing the title.
-     */
-    public abstract FragmentTransaction setBreadCrumbShortTitle(@StringRes int res);
-
-    /**
-     * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this
-     * method is <em>not</em> recommended, as the string can not be changed
-     * later if the locale changes.
-     */
-    public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
-
-    /**
-     * Sets whether or not to allow optimizing operations within and across
-     * 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 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 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 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 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.
-     *
-     * <p class="note">A transaction can only be committed with this method
-     * prior to its containing activity saving its state.  If the commit is
-     * attempted after that point, an exception will be thrown.  This is
-     * because the state after the commit can be lost if the activity needs to
-     * be restored from its state.  See {@link #commitAllowingStateLoss()} for
-     * situations where it may be okay to lose the commit.</p>
-     * 
-     * @return Returns the identifier of this transaction's back stack entry,
-     * if {@link #addToBackStack(String)} had been called.  Otherwise, returns
-     * a negative number.
-     */
-    public abstract int commit();
-
-    /**
-     * Like {@link #commit} but allows the commit to be executed after an
-     * activity's state is saved.  This is dangerous because the commit can
-     * be lost if the activity needs to later be restored from its state, so
-     * this should only be used for cases where it is okay for the UI state
-     * to change unexpectedly on the user.
-     */
-    public abstract int commitAllowingStateLoss();
-
-    /**
-     * Commits this transaction synchronously. Any added fragments will be
-     * initialized and brought completely to the lifecycle state of their host
-     * and any removed fragments will be torn down accordingly before this
-     * call returns. Committing a transaction in this way allows fragments
-     * to be added as dedicated, encapsulated components that monitor the
-     * lifecycle state of their host while providing firmer ordering guarantees
-     * around when those fragments are fully initialized and ready. Fragments
-     * that manage views will have those views created and attached.
-     *
-     * <p>Calling <code>commitNow</code> is preferable to calling
-     * {@link #commit()} followed by {@link FragmentManager#executePendingTransactions()}
-     * as the latter will have the side effect of attempting to commit <em>all</em>
-     * currently pending transactions whether that is the desired behavior
-     * or not.</p>
-     *
-     * <p>Transactions committed in this way may not be added to the
-     * FragmentManager's back stack, as doing so would break other expected
-     * ordering guarantees for other asynchronously committed transactions.
-     * This method will throw {@link IllegalStateException} if the transaction
-     * previously requested to be added to the back stack with
-     * {@link #addToBackStack(String)}.</p>
-     *
-     * <p class="note">A transaction can only be committed with this method
-     * prior to its containing activity saving its state.  If the commit is
-     * attempted after that point, an exception will be thrown.  This is
-     * because the state after the commit can be lost if the activity needs to
-     * be restored from its state.  See {@link #commitAllowingStateLoss()} for
-     * situations where it may be okay to lose the commit.</p>
-     */
-    public abstract void commitNow();
-
-    /**
-     * Like {@link #commitNow} but allows the commit to be executed after an
-     * activity's state is saved.  This is dangerous because the commit can
-     * be lost if the activity needs to later be restored from its state, so
-     * this should only be used for cases where it is okay for the UI state
-     * to change unexpectedly on the user.
-     */
-    public abstract void commitNowAllowingStateLoss();
-}
diff --git a/android/support/v4/app/FragmentTransition.java b/android/support/v4/app/FragmentTransition.java
deleted file mode 100644
index fae3b52..0000000
--- a/android/support/v4/app/FragmentTransition.java
+++ /dev/null
@@ -1,1254 +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.graphics.Rect;
-import android.os.Build;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.view.ViewCompat;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 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 {
-    /**
-     * The inverse of all BackStackRecord operation commands. This assumes that
-     * 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_UNSET_PRIMARY_NAV, // inverse of OP_SET_PRIMARY_NAV
-            BackStackRecord.OP_SET_PRIMARY_NAV,   // inverse of OP_UNSET_PRIMARY_NAV
-    };
-
-    private static final FragmentTransitionImpl PLATFORM_IMPL = Build.VERSION.SDK_INT >= 21
-            ? new FragmentTransitionCompat21()
-            : null;
-
-    private static final FragmentTransitionImpl SUPPORT_IMPL = resolveSupportImpl();
-
-    private static FragmentTransitionImpl resolveSupportImpl() {
-        try {
-            @SuppressWarnings("unchecked")
-            Class<FragmentTransitionImpl> impl = (Class<FragmentTransitionImpl>) Class.forName(
-                    "android.support.transition.FragmentTransitionSupport");
-            return impl.getDeclaredConstructor().newInstance();
-        } catch (Exception ignored) {
-            // support-transition is not loaded; ignore
-        }
-        return null;
-    }
-
-    /**
-     * The main entry point for Fragment Transitions, this starts the transitions
-     * set on the leaving Fragment's {@link Fragment#getExitTransition()}, the
-     * entering Fragment's {@link Fragment#getEnterTransition()} and
-     * {@link Fragment#getSharedElementEnterTransition()}. When popping,
-     * the leaving Fragment's {@link Fragment#getReturnTransition()} and
-     * {@link Fragment#getSharedElementReturnTransition()} and the entering
-     * {@link Fragment#getReenterTransition()} will be run.
-     * <p>
-     * 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 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.
-     *
-     * @param fragmentManager The executing FragmentManagerImpl
-     * @param records The list of transactions being executed.
-     * @param isRecordPop For each transaction, whether it is a pop transaction or not.
-     * @param startIndex The first index into records and isRecordPop to execute as
-     *                   part of this transition.
-     * @param endIndex One past the last index into records and isRecordPop to execute
-     *                 as part of this transition.
-     * @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 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, isReordered);
-            } else {
-                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);
-                }
-            }
-        }
-    }
-
-    /**
-     * Iterates through the transactions that affect a given fragment container
-     * and tracks the shared element names across transactions. This is most useful
-     * in pop transactions where the names of shared elements are known.
-     *
-     * @param containerId The container ID that is executing the transition.
-     * @param records The list of transactions being executed.
-     * @param isRecordPop For each transaction, whether it is a pop transaction or not.
-     * @param startIndex The first index into records and isRecordPop to execute as
-     *                   part of this transition.
-     * @param endIndex One past the last index into records and isRecordPop to execute
-     *                 as part of this transition.
-     * @return A map from the initial shared element name to the final shared element name
-     * before any onMapSharedElements is run.
-     */
-    private static ArrayMap<String, String> calculateNameOverrides(int containerId,
-            ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
-            int startIndex, int endIndex) {
-        ArrayMap<String, String> nameOverrides = new ArrayMap<>();
-        for (int recordNum = endIndex - 1; recordNum >= startIndex; recordNum--) {
-            final BackStackRecord record = records.get(recordNum);
-            if (!record.interactsWith(containerId)) {
-                continue;
-            }
-            final boolean isPop = isRecordPop.get(recordNum);
-            if (record.mSharedElementSourceNames != null) {
-                final int numSharedElements = record.mSharedElementSourceNames.size();
-                final ArrayList<String> sources;
-                final ArrayList<String> targets;
-                if (isPop) {
-                    targets = record.mSharedElementSourceNames;
-                    sources = record.mSharedElementTargetNames;
-                } else {
-                    sources = record.mSharedElementSourceNames;
-                    targets = record.mSharedElementTargetNames;
-                }
-                for (int i = 0; i < numSharedElements; i++) {
-                    String sourceName = sources.get(i);
-                    String targetName = targets.get(i);
-                    String previousTarget = nameOverrides.remove(targetName);
-                    if (previousTarget != null) {
-                        nameOverrides.put(sourceName, previousTarget);
-                    } else {
-                        nameOverrides.put(sourceName, targetName);
-                    }
-                }
-            }
-        }
-        return nameOverrides;
-    }
-
-    /**
-     * Configures a transition for a single fragment container for which the transaction was
-     * reordered. That means that all Fragment Views have been added and incoming fragment
-     * Views are marked invisible.
-     *
-     * @param fragmentManager The executing FragmentManagerImpl
-     * @param containerId The container ID that is executing the transition.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     */
-    private static void configureTransitionsReordered(FragmentManagerImpl fragmentManager,
-            int containerId, FragmentContainerTransition fragments,
-            View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = null;
-        if (fragmentManager.mContainer.onHasView()) {
-            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
-        }
-        if (sceneRoot == null) {
-            return;
-        }
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-        final FragmentTransitionImpl impl = chooseImpl(outFragment, inFragment);
-        if (impl == null) {
-            return;
-        }
-        final boolean inIsPop = fragments.lastInIsPop;
-        final boolean outIsPop = fragments.firstOutIsPop;
-
-        ArrayList<View> sharedElementsIn = new ArrayList<>();
-        ArrayList<View> sharedElementsOut = new ArrayList<>();
-        Object enterTransition = getEnterTransition(impl, inFragment, inIsPop);
-        Object exitTransition = getExitTransition(impl, outFragment, outIsPop);
-
-        Object sharedElementTransition = configureSharedElementsReordered(impl, sceneRoot,
-                nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
-                enterTransition, exitTransition);
-
-        if (enterTransition == null && sharedElementTransition == null
-                && exitTransition == null) {
-            return; // no transitions!
-        }
-
-        ArrayList<View> exitingViews = configureEnteringExitingViews(impl, exitTransition,
-                outFragment, sharedElementsOut, nonExistentView);
-
-        ArrayList<View> enteringViews = configureEnteringExitingViews(impl, enterTransition,
-                inFragment, sharedElementsIn, nonExistentView);
-
-        setViewVisibility(enteringViews, View.INVISIBLE);
-
-        Object transition = mergeTransitions(impl, enterTransition, exitTransition,
-                sharedElementTransition, inFragment, inIsPop);
-
-        if (transition != null) {
-            replaceHide(impl, exitTransition, outFragment, exitingViews);
-            ArrayList<String> inNames =
-                    impl.prepareSetNameOverridesReordered(sharedElementsIn);
-            impl.scheduleRemoveTargets(transition,
-                    enterTransition, enteringViews, exitTransition, exitingViews,
-                    sharedElementTransition, sharedElementsIn);
-            impl.beginDelayedTransition(sceneRoot, transition);
-            impl.setNameOverridesReordered(sceneRoot, sharedElementsOut,
-                    sharedElementsIn, inNames, nameOverrides);
-            setViewVisibility(enteringViews, View.VISIBLE);
-            impl.swapSharedElementTargets(sharedElementTransition,
-                    sharedElementsOut, sharedElementsIn);
-        }
-    }
-
-    /**
-     * Replace hide operations with visibility changes on the exiting views. Instead of making
-     * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
-     * transition, make the fragment's view GONE.
-     */
-    private static void replaceHide(FragmentTransitionImpl impl,
-            Object exitTransition, Fragment exitingFragment,
-            final ArrayList<View> exitingViews) {
-        if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
-                && exitingFragment.mHidden && exitingFragment.mHiddenChanged) {
-            exitingFragment.setHideReplaced(true);
-            impl.scheduleHideFragmentView(exitTransition,
-                    exitingFragment.getView(), exitingViews);
-            final ViewGroup container = exitingFragment.mContainer;
-            OneShotPreDrawListener.add(container, new Runnable() {
-                @Override
-                public void run() {
-                    setViewVisibility(exitingViews, View.INVISIBLE);
-                }
-            });
-        }
-    }
-
-    /**
-     * Configures a transition for a single fragment container for which the transaction was
-     * ordered. That means that the transaction has not been executed yet, so incoming
-     * Views are not yet known.
-     *
-     * @param fragmentManager The executing FragmentManagerImpl
-     * @param containerId The container ID that is executing the transition.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     */
-    private static void configureTransitionsOrdered(FragmentManagerImpl fragmentManager,
-            int containerId, FragmentContainerTransition fragments,
-            View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = null;
-        if (fragmentManager.mContainer.onHasView()) {
-            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
-        }
-        if (sceneRoot == null) {
-            return;
-        }
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-        final FragmentTransitionImpl impl = chooseImpl(outFragment, inFragment);
-        if (impl == null) {
-            return;
-        }
-        final boolean inIsPop = fragments.lastInIsPop;
-        final boolean outIsPop = fragments.firstOutIsPop;
-
-        Object enterTransition = getEnterTransition(impl, inFragment, inIsPop);
-        Object exitTransition = getExitTransition(impl, outFragment, outIsPop);
-
-        ArrayList<View> sharedElementsOut = new ArrayList<>();
-        ArrayList<View> sharedElementsIn = new ArrayList<>();
-
-        Object sharedElementTransition = configureSharedElementsOrdered(impl, sceneRoot,
-                nonExistentView, nameOverrides, fragments, sharedElementsOut, sharedElementsIn,
-                enterTransition, exitTransition);
-
-        if (enterTransition == null && sharedElementTransition == null
-                && exitTransition == null) {
-            return; // no transitions!
-        }
-
-        ArrayList<View> exitingViews = configureEnteringExitingViews(impl, exitTransition,
-                outFragment, sharedElementsOut, nonExistentView);
-
-        if (exitingViews == null || exitingViews.isEmpty()) {
-            exitTransition = null;
-        }
-
-        // Ensure the entering transition doesn't target anything until the views are made
-        // visible
-        impl.addTarget(enterTransition, nonExistentView);
-
-        Object transition = mergeTransitions(impl, enterTransition, exitTransition,
-                sharedElementTransition, inFragment, fragments.lastInIsPop);
-
-        if (transition != null) {
-            final ArrayList<View> enteringViews = new ArrayList<>();
-            impl.scheduleRemoveTargets(transition,
-                    enterTransition, enteringViews, exitTransition, exitingViews,
-                    sharedElementTransition, sharedElementsIn);
-            scheduleTargetChange(impl, sceneRoot, inFragment, nonExistentView, sharedElementsIn,
-                    enterTransition, enteringViews, exitTransition, exitingViews);
-            impl.setNameOverridesOrdered(sceneRoot, sharedElementsIn, nameOverrides);
-
-            impl.beginDelayedTransition(sceneRoot, transition);
-            impl.scheduleNameReset(sceneRoot, sharedElementsIn, nameOverrides);
-        }
-    }
-
-    /**
-     * 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
-     * targeting the Views of the incoming Fragment.
-     *
-     * @param sceneRoot The fragment container View
-     * @param inFragment The last fragment that is entering
-     * @param nonExistentView A view that does not exist in the hierarchy that is used as a
-     *                        transition target to ensure no View is targeted.
-     * @param sharedElementsIn The shared element Views of the incoming fragment
-     * @param enterTransition The enter transition of the incoming fragment
-     * @param enteringViews The entering Views of the incoming fragment
-     * @param exitTransition The exit transition of the outgoing fragment
-     * @param exitingViews The exiting views of the outgoing fragment
-     */
-    private static void scheduleTargetChange(final FragmentTransitionImpl impl,
-            final ViewGroup sceneRoot,
-            final Fragment inFragment, final View nonExistentView,
-            final ArrayList<View> sharedElementsIn,
-            final Object enterTransition, final ArrayList<View> enteringViews,
-            final Object exitTransition, final ArrayList<View> exitingViews) {
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                if (enterTransition != null) {
-                    impl.removeTarget(enterTransition,
-                            nonExistentView);
-                    ArrayList<View> views = configureEnteringExitingViews(impl,
-                            enterTransition, inFragment, sharedElementsIn, nonExistentView);
-                    enteringViews.addAll(views);
-                }
-
-                if (exitingViews != null) {
-                    if (exitTransition != null) {
-                        ArrayList<View> tempExiting = new ArrayList<>();
-                        tempExiting.add(nonExistentView);
-                        impl.replaceTargets(exitTransition, exitingViews,
-                                tempExiting);
-                    }
-                    exitingViews.clear();
-                    exitingViews.add(nonExistentView);
-                }
-            }
-        });
-    }
-
-    /**
-     * Chooses the appropriate implementation depending on the Transition instances hold by the
-     * Fragments.
-     */
-    private static FragmentTransitionImpl chooseImpl(Fragment outFragment, Fragment inFragment) {
-        // Collect all transition instances
-        final ArrayList<Object> transitions = new ArrayList<>();
-        if (outFragment != null) {
-            final Object exitTransition = outFragment.getExitTransition();
-            if (exitTransition != null) {
-                transitions.add(exitTransition);
-            }
-            final Object returnTransition = outFragment.getReturnTransition();
-            if (returnTransition != null) {
-                transitions.add(returnTransition);
-            }
-            final Object sharedReturnTransition = outFragment.getSharedElementReturnTransition();
-            if (sharedReturnTransition != null) {
-                transitions.add(sharedReturnTransition);
-            }
-        }
-        if (inFragment != null) {
-            final Object enterTransition = inFragment.getEnterTransition();
-            if (enterTransition != null) {
-                transitions.add(enterTransition);
-            }
-            final Object reenterTransition = inFragment.getReenterTransition();
-            if (reenterTransition != null) {
-                transitions.add(reenterTransition);
-            }
-            final Object sharedEnterTransition = inFragment.getSharedElementEnterTransition();
-            if (sharedEnterTransition != null) {
-                transitions.add(sharedEnterTransition);
-            }
-        }
-        if (transitions.isEmpty()) {
-            return null; // No transition to run
-        }
-        // Pick the implementation that can handle all the transitions
-        if (PLATFORM_IMPL != null && canHandleAll(PLATFORM_IMPL, transitions)) {
-            return PLATFORM_IMPL;
-        }
-        if (SUPPORT_IMPL != null && canHandleAll(SUPPORT_IMPL, transitions)) {
-            return SUPPORT_IMPL;
-        }
-        if (PLATFORM_IMPL != null || SUPPORT_IMPL != null) {
-            throw new IllegalArgumentException("Invalid Transition types");
-        }
-        return null;
-    }
-
-    private static boolean canHandleAll(FragmentTransitionImpl impl, List<Object> transitions) {
-        for (int i = 0, size = transitions.size(); i < size; i++) {
-            if (!impl.canHandle(transitions.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns a TransitionSet containing the shared element transition. The wrapping TransitionSet
-     * targets all shared elements to ensure that no other Views are targeted. The shared element
-     * transition can then target any or all shared elements without worrying about accidentally
-     * targeting entering or exiting Views.
-     *
-     * @param inFragment The incoming fragment
-     * @param outFragment the outgoing fragment
-     * @param isPop True if this is a pop transaction or false if it is a normal (add) transaction.
-     * @return A TransitionSet wrapping the shared element transition or null if no such transition
-     * exists.
-     */
-    private static Object getSharedElementTransition(FragmentTransitionImpl impl,
-            Fragment inFragment, Fragment outFragment, boolean isPop) {
-        if (inFragment == null || outFragment == null) {
-            return null;
-        }
-        Object transition = impl.cloneTransition(isPop
-                ? outFragment.getSharedElementReturnTransition()
-                : inFragment.getSharedElementEnterTransition());
-        return impl.wrapTransitionInSet(transition);
-    }
-
-    /**
-     * Returns a clone of the enter transition or null if no such transition exists.
-     */
-    private static Object getEnterTransition(FragmentTransitionImpl impl,
-            Fragment inFragment, boolean isPop) {
-        if (inFragment == null) {
-            return null;
-        }
-        return impl.cloneTransition(isPop
-                ? inFragment.getReenterTransition()
-                : inFragment.getEnterTransition());
-    }
-
-    /**
-     * Returns a clone of the exit transition or null if no such transition exists.
-     */
-    private static Object getExitTransition(FragmentTransitionImpl impl,
-            Fragment outFragment, boolean isPop) {
-        if (outFragment == null) {
-            return null;
-        }
-        return impl.cloneTransition(isPop
-                ? outFragment.getReturnTransition()
-                : outFragment.getExitTransition());
-    }
-
-    /**
-     * 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>
-     * The epicenter of exit and shared element transitions is the first shared element
-     * in the outgoing fragment. The epicenter of the entering transition is the first shared
-     * element in the incoming fragment.
-     *
-     * @param sceneRoot The fragment container View
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
-     *                          fragment
-     * @param sharedElementsIn A list modified to contain the shared elements in the incoming
-     *                         fragment
-     * @param enterTransition The transition used for entering Views, modified by applying the
-     *                        epicenter
-     * @param exitTransition The transition used for exiting Views, modified by applying the
-     *                       epicenter
-     * @return The shared element transition or null if no shared elements exist
-     */
-    private static Object configureSharedElementsReordered(final FragmentTransitionImpl impl,
-            final ViewGroup sceneRoot,
-            final View nonExistentView, final ArrayMap<String, String> nameOverrides,
-            final FragmentContainerTransition fragments,
-            final ArrayList<View> sharedElementsOut,
-            final ArrayList<View> sharedElementsIn,
-            final Object enterTransition, final Object exitTransition) {
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-        if (inFragment != null) {
-            inFragment.getView().setVisibility(View.VISIBLE);
-        }
-        if (inFragment == null || outFragment == null) {
-            return null; // no shared element without a fragment
-        }
-
-        final boolean inIsPop = fragments.lastInIsPop;
-        Object sharedElementTransition = nameOverrides.isEmpty() ? null
-                : getSharedElementTransition(impl, inFragment, outFragment, inIsPop);
-
-        final ArrayMap<String, View> outSharedElements = captureOutSharedElements(impl,
-                nameOverrides, sharedElementTransition, fragments);
-
-        final ArrayMap<String, View> inSharedElements = captureInSharedElements(impl,
-                nameOverrides, sharedElementTransition, fragments);
-
-        if (nameOverrides.isEmpty()) {
-            sharedElementTransition = null;
-            if (outSharedElements != null) {
-                outSharedElements.clear();
-            }
-            if (inSharedElements != null) {
-                inSharedElements.clear();
-            }
-        } else {
-            addSharedElementsWithMatchingNames(sharedElementsOut, outSharedElements,
-                    nameOverrides.keySet());
-            addSharedElementsWithMatchingNames(sharedElementsIn, inSharedElements,
-                    nameOverrides.values());
-        }
-
-        if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
-            // don't call onSharedElementStart/End since there is no transition
-            return null;
-        }
-
-        callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
-
-        final Rect epicenter;
-        final View epicenterView;
-        if (sharedElementTransition != null) {
-            sharedElementsIn.add(nonExistentView);
-            impl.setSharedElementTargets(sharedElementTransition,
-                    nonExistentView, sharedElementsOut);
-            final boolean outIsPop = fragments.firstOutIsPop;
-            final BackStackRecord outTransaction = fragments.firstOutTransaction;
-            setOutEpicenter(impl, sharedElementTransition, exitTransition, outSharedElements,
-                    outIsPop, outTransaction);
-            epicenter = new Rect();
-            epicenterView = getInEpicenterView(inSharedElements, fragments,
-                    enterTransition, inIsPop);
-            if (epicenterView != null) {
-                impl.setEpicenter(enterTransition, epicenter);
-            }
-        } else {
-            epicenter = null;
-            epicenterView = null;
-        }
-
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                callSharedElementStartEnd(inFragment, outFragment, inIsPop,
-                        inSharedElements, false);
-                if (epicenterView != null) {
-                    impl.getBoundsOnScreen(epicenterView, epicenter);
-                }
-            }
-        });
-        return sharedElementTransition;
-    }
-
-    /**
-     * Add Views from sharedElements into views that have the transitionName in the
-     * nameOverridesSet.
-     *
-     * @param views               Views list to add shared elements to
-     * @param sharedElements      List of shared elements
-     * @param nameOverridesSet    The transition names for all views to be copied from
-     *                            sharedElements to views.
-     */
-    private static void addSharedElementsWithMatchingNames(ArrayList<View> views,
-            ArrayMap<String, View> sharedElements, Collection<String> nameOverridesSet) {
-        for (int i = sharedElements.size() - 1; i >= 0; i--) {
-            View view = sharedElements.valueAt(i);
-            if (nameOverridesSet.contains(ViewCompat.getTransitionName(view))) {
-                views.add(view);
-            }
-        }
-    }
-
-    /**
-     * 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.
-     * <p>
-     * The epicenter of exit and shared element transitions is the first shared element
-     * in the outgoing fragment. The epicenter of the entering transition is the first shared
-     * element in the incoming fragment.
-     *
-     * @param sceneRoot The fragment container View
-     * @param nonExistentView A View that does not exist in the hierarchy. This is used to
-     *                        prevent transitions from acting on other Views when there is no
-     *                        other target.
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param sharedElementsOut A list modified to contain the shared elements in the outgoing
-     *                          fragment
-     * @param sharedElementsIn A list modified to contain the shared elements in the incoming
-     *                         fragment
-     * @param enterTransition The transition used for entering Views, modified by applying the
-     *                        epicenter
-     * @param exitTransition The transition used for exiting Views, modified by applying the
-     *                       epicenter
-     * @return The shared element transition or null if no shared elements exist
-     */
-    private static Object configureSharedElementsOrdered(final FragmentTransitionImpl impl,
-            final ViewGroup sceneRoot,
-            final View nonExistentView, final ArrayMap<String, String> nameOverrides,
-            final FragmentContainerTransition fragments,
-            final ArrayList<View> sharedElementsOut,
-            final ArrayList<View> sharedElementsIn,
-            final Object enterTransition, final Object exitTransition) {
-        final Fragment inFragment = fragments.lastIn;
-        final Fragment outFragment = fragments.firstOut;
-
-        if (inFragment == null || outFragment == null) {
-            return null; // no transition
-        }
-
-        final boolean inIsPop = fragments.lastInIsPop;
-        Object sharedElementTransition = nameOverrides.isEmpty() ? null
-                : getSharedElementTransition(impl, inFragment, outFragment, inIsPop);
-
-        ArrayMap<String, View> outSharedElements = captureOutSharedElements(impl, nameOverrides,
-                sharedElementTransition, fragments);
-
-        if (nameOverrides.isEmpty()) {
-            sharedElementTransition = null;
-        } else {
-            sharedElementsOut.addAll(outSharedElements.values());
-        }
-
-        if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
-            // don't call onSharedElementStart/End since there is no transition
-            return null;
-        }
-
-        callSharedElementStartEnd(inFragment, outFragment, inIsPop, outSharedElements, true);
-
-        final Rect inEpicenter;
-        if (sharedElementTransition != null) {
-            inEpicenter = new Rect();
-            impl.setSharedElementTargets(sharedElementTransition,
-                    nonExistentView, sharedElementsOut);
-            final boolean outIsPop = fragments.firstOutIsPop;
-            final BackStackRecord outTransaction = fragments.firstOutTransaction;
-            setOutEpicenter(impl, sharedElementTransition, exitTransition, outSharedElements,
-                    outIsPop, outTransaction);
-            if (enterTransition != null) {
-                impl.setEpicenter(enterTransition, inEpicenter);
-            }
-        } else {
-            inEpicenter = null;
-        }
-
-
-        final Object finalSharedElementTransition = sharedElementTransition;
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                ArrayMap<String, View> inSharedElements = captureInSharedElements(impl,
-                        nameOverrides, finalSharedElementTransition, fragments);
-
-                if (inSharedElements != null) {
-                    sharedElementsIn.addAll(inSharedElements.values());
-                    sharedElementsIn.add(nonExistentView);
-                }
-
-                callSharedElementStartEnd(inFragment, outFragment, inIsPop,
-                        inSharedElements, false);
-                if (finalSharedElementTransition != null) {
-                    impl.swapSharedElementTargets(
-                            finalSharedElementTransition, sharedElementsOut,
-                            sharedElementsIn);
-
-                    final View inEpicenterView = getInEpicenterView(inSharedElements,
-                            fragments, enterTransition, inIsPop);
-                    if (inEpicenterView != null) {
-                        impl.getBoundsOnScreen(inEpicenterView,
-                                inEpicenter);
-                    }
-                }
-            }
-        });
-
-        return sharedElementTransition;
-    }
-
-    /**
-     * Finds the shared elements in the outgoing fragment. It also calls
-     * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
-     * of the shared element mapping. {@code nameOverrides} is updated to match the
-     * actual transition name of the mapped shared elements.
-     *
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param sharedElementTransition The shared element transition
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @return The mapping of shared element names to the Views in the hierarchy or null
-     * if there is no shared element transition.
-     */
-    private static ArrayMap<String, View> captureOutSharedElements(FragmentTransitionImpl impl,
-            ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
-            FragmentContainerTransition fragments) {
-        if (nameOverrides.isEmpty() || sharedElementTransition == null) {
-            nameOverrides.clear();
-            return null;
-        }
-        final Fragment outFragment = fragments.firstOut;
-        final ArrayMap<String, View> outSharedElements = new ArrayMap<>();
-        impl.findNamedViews(outSharedElements, outFragment.getView());
-
-        final SharedElementCallback sharedElementCallback;
-        final ArrayList<String> names;
-        final BackStackRecord outTransaction = fragments.firstOutTransaction;
-        if (fragments.firstOutIsPop) {
-            sharedElementCallback = outFragment.getEnterTransitionCallback();
-            names = outTransaction.mSharedElementTargetNames;
-        } else {
-            sharedElementCallback = outFragment.getExitTransitionCallback();
-            names = outTransaction.mSharedElementSourceNames;
-        }
-
-        outSharedElements.retainAll(names);
-        if (sharedElementCallback != null) {
-            sharedElementCallback.onMapSharedElements(names, outSharedElements);
-            for (int i = names.size() - 1; i >= 0; i--) {
-                String name = names.get(i);
-                View view = outSharedElements.get(name);
-                if (view == null) {
-                    nameOverrides.remove(name);
-                } else if (!name.equals(ViewCompat.getTransitionName(view))) {
-                    String targetValue = nameOverrides.remove(name);
-                    nameOverrides.put(ViewCompat.getTransitionName(view), targetValue);
-                }
-            }
-        } else {
-            nameOverrides.retainAll(outSharedElements.keySet());
-        }
-        return outSharedElements;
-    }
-
-    /**
-     * Finds the shared elements in the incoming fragment. It also calls
-     * {@link SharedElementCallback#onMapSharedElements(List, Map)} to allow more control
-     * of the shared element mapping. {@code nameOverrides} is updated to match the
-     * actual transition name of the mapped shared elements.
-     *
-     * @param nameOverrides A map of the shared element names from the starting fragment to
-     *                      the final fragment's Views as given in
-     *                      {@link FragmentTransaction#addSharedElement(View, String)}.
-     * @param sharedElementTransition The shared element transition
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @return The mapping of shared element names to the Views in the hierarchy or null
-     * if there is no shared element transition.
-     */
-    private static ArrayMap<String, View> captureInSharedElements(FragmentTransitionImpl impl,
-            ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
-            FragmentContainerTransition fragments) {
-        Fragment inFragment = fragments.lastIn;
-        final View fragmentView = inFragment.getView();
-        if (nameOverrides.isEmpty() || sharedElementTransition == null || fragmentView == null) {
-            nameOverrides.clear();
-            return null;
-        }
-        final ArrayMap<String, View> inSharedElements = new ArrayMap<>();
-        impl.findNamedViews(inSharedElements, fragmentView);
-
-        final SharedElementCallback sharedElementCallback;
-        final ArrayList<String> names;
-        final BackStackRecord inTransaction = fragments.lastInTransaction;
-        if (fragments.lastInIsPop) {
-            sharedElementCallback = inFragment.getExitTransitionCallback();
-            names = inTransaction.mSharedElementSourceNames;
-        } else {
-            sharedElementCallback = inFragment.getEnterTransitionCallback();
-            names = inTransaction.mSharedElementTargetNames;
-        }
-
-        if (names != null) {
-            inSharedElements.retainAll(names);
-        }
-        if (sharedElementCallback != null) {
-            sharedElementCallback.onMapSharedElements(names, inSharedElements);
-            for (int i = names.size() - 1; i >= 0; i--) {
-                String name = names.get(i);
-                View view = inSharedElements.get(name);
-                if (view == null) {
-                    String key = findKeyForValue(nameOverrides, name);
-                    if (key != null) {
-                        nameOverrides.remove(key);
-                    }
-                } else if (!name.equals(ViewCompat.getTransitionName(view))) {
-                    String key = findKeyForValue(nameOverrides, name);
-                    if (key != null) {
-                        nameOverrides.put(key, ViewCompat.getTransitionName(view));
-                    }
-                }
-            }
-        } else {
-            retainValues(nameOverrides, inSharedElements);
-        }
-        return inSharedElements;
-    }
-
-    /**
-     * Utility to find the String key in {@code map} that maps to {@code value}.
-     */
-    private static String findKeyForValue(ArrayMap<String, String> map, String value) {
-        final int numElements = map.size();
-        for (int i = 0; i < numElements; i++) {
-            if (value.equals(map.valueAt(i))) {
-                return map.keyAt(i);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the View in the incoming Fragment that should be used as the epicenter.
-     *
-     * @param inSharedElements The mapping of shared element names to Views in the
-     *                         incoming fragment.
-     * @param fragments A structure holding the transitioning fragments in this container.
-     * @param enterTransition The transition used for the incoming Fragment's views
-     * @param inIsPop Is the incoming fragment being added as a pop transaction?
-     */
-    private static View getInEpicenterView(ArrayMap<String, View> inSharedElements,
-            FragmentContainerTransition fragments,
-            Object enterTransition, boolean inIsPop) {
-        BackStackRecord inTransaction = fragments.lastInTransaction;
-        if (enterTransition != null && inSharedElements != null
-                && inTransaction.mSharedElementSourceNames != null
-                && !inTransaction.mSharedElementSourceNames.isEmpty()) {
-            final String targetName = inIsPop
-                    ? inTransaction.mSharedElementSourceNames.get(0)
-                    : inTransaction.mSharedElementTargetNames.get(0);
-            return inSharedElements.get(targetName);
-        }
-        return null;
-    }
-
-    /**
-     * Sets the epicenter for the exit transition.
-     *
-     * @param sharedElementTransition The shared element transition
-     * @param exitTransition The transition for the outgoing fragment's views
-     * @param outSharedElements Shared elements in the outgoing fragment
-     * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
-     * @param outTransaction The transaction that caused the fragment to be removed.
-     */
-    private static void setOutEpicenter(FragmentTransitionImpl impl, Object sharedElementTransition,
-            Object exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
-            BackStackRecord outTransaction) {
-        if (outTransaction.mSharedElementSourceNames != null
-                && !outTransaction.mSharedElementSourceNames.isEmpty()) {
-            final String sourceName = outIsPop
-                    ? outTransaction.mSharedElementTargetNames.get(0)
-                    : outTransaction.mSharedElementSourceNames.get(0);
-            final View outEpicenterView = outSharedElements.get(sourceName);
-            impl.setEpicenter(sharedElementTransition, outEpicenterView);
-
-            if (exitTransition != null) {
-                impl.setEpicenter(exitTransition, outEpicenterView);
-            }
-        }
-    }
-
-    /**
-     * A utility to retain only the mappings in {@code nameOverrides} that have a value
-     * that has a key in {@code namedViews}. This is a useful equivalent to
-     * {@link ArrayMap#retainAll(Collection)} for values.
-     */
-    private static void retainValues(ArrayMap<String, String> nameOverrides,
-            ArrayMap<String, View> namedViews) {
-        for (int i = nameOverrides.size() - 1; i >= 0; i--) {
-            final String targetName = nameOverrides.valueAt(i);
-            if (!namedViews.containsKey(targetName)) {
-                nameOverrides.removeAt(i);
-            }
-        }
-    }
-
-    /**
-     * Calls the {@link SharedElementCallback#onSharedElementStart(List, List, List)} or
-     * {@link SharedElementCallback#onSharedElementEnd(List, List, List)} on the appropriate
-     * incoming or outgoing fragment.
-     *
-     * @param inFragment The incoming fragment
-     * @param outFragment The outgoing fragment
-     * @param isPop Is the incoming fragment part of a pop transaction?
-     * @param sharedElements The shared element Views
-     * @param isStart Call the start or end call on the SharedElementCallback
-     */
-    private static void callSharedElementStartEnd(Fragment inFragment, Fragment outFragment,
-            boolean isPop, ArrayMap<String, View> sharedElements, boolean isStart) {
-        SharedElementCallback sharedElementCallback = isPop
-                ? outFragment.getEnterTransitionCallback()
-                : inFragment.getEnterTransitionCallback();
-        if (sharedElementCallback != null) {
-            ArrayList<View> views = new ArrayList<>();
-            ArrayList<String> names = new ArrayList<>();
-            final int count = sharedElements == null ? 0 : sharedElements.size();
-            for (int i = 0; i < count; i++) {
-                names.add(sharedElements.keyAt(i));
-                views.add(sharedElements.valueAt(i));
-            }
-            if (isStart) {
-                sharedElementCallback.onSharedElementStart(names, views, null);
-            } else {
-                sharedElementCallback.onSharedElementEnd(names, views, null);
-            }
-        }
-    }
-
-    private static ArrayList<View> configureEnteringExitingViews(FragmentTransitionImpl impl,
-            Object transition,
-            Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
-        ArrayList<View> viewList = null;
-        if (transition != null) {
-            viewList = new ArrayList<>();
-            View root = fragment.getView();
-            if (root != null) {
-                impl.captureTransitioningViews(viewList, root);
-            }
-            if (sharedElements != null) {
-                viewList.removeAll(sharedElements);
-            }
-            if (!viewList.isEmpty()) {
-                viewList.add(nonExistentView);
-                impl.addTargets(transition, viewList);
-            }
-        }
-        return viewList;
-    }
-
-    /**
-     * Sets the visibility of all Views in {@code views} to {@code visibility}.
-     */
-    private static void setViewVisibility(ArrayList<View> views, int visibility) {
-        if (views == null) {
-            return;
-        }
-        for (int i = views.size() - 1; i >= 0; i--) {
-            final View view = views.get(i);
-            view.setVisibility(visibility);
-        }
-    }
-
-    /**
-     * Merges exit, shared element, and enter transitions so that they act together or
-     * sequentially as defined in the fragments.
-     */
-    private static Object mergeTransitions(FragmentTransitionImpl impl, Object enterTransition,
-            Object exitTransition, Object sharedElementTransition, Fragment inFragment,
-            boolean isPop) {
-        boolean overlap = true;
-        if (enterTransition != null && exitTransition != null && inFragment != null) {
-            overlap = isPop ? inFragment.getAllowReturnTransitionOverlap() :
-                    inFragment.getAllowEnterTransitionOverlap();
-        }
-
-        // Wrap the transitions. Explicit targets like in enter and exit will cause the
-        // views to be targeted regardless of excluded views. If that happens, then the
-        // excluded fragments views (hidden fragments) will still be in the transition.
-
-        Object transition;
-        if (overlap) {
-            // Regular transition -- do it all together
-            transition = impl.mergeTransitionsTogether(exitTransition,
-                    enterTransition, sharedElementTransition);
-        } else {
-            // First do exit, then enter, but allow shared element transition to happen
-            // during both.
-            transition = impl.mergeTransitionsInSequence(exitTransition,
-                    enterTransition, sharedElementTransition);
-        }
-        return transition;
-    }
-
-    /**
-     * Finds the first removed fragment and last added fragments when going forward.
-     * If none of the fragments have transitions, then both lists will be empty.
-     *
-     * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
-     *                               and last fragments to be added. This will be modified by
-     *                               this method.
-     */
-    public static void calculateFragments(BackStackRecord transaction,
-            SparseArray<FragmentContainerTransition> transitioningFragments,
-            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, isReordered);
-        }
-    }
-
-    /**
-     * Finds the first removed fragment and last added fragments when popping the back stack.
-     * If none of the fragments have transitions, then both lists will be empty.
-     *
-     * @param transitioningFragments Keyed on the container ID, the first fragments to be removed,
-     *                               and last fragments to be added. This will be modified by
-     *                               this method.
-     */
-    public static void calculatePopFragments(BackStackRecord transaction,
-            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, isReordered);
-        }
-    }
-
-    static boolean supportsTransition() {
-        return PLATFORM_IMPL != null || SUPPORT_IMPL != null;
-    }
-
-    /**
-     * Examines the {@code command} and may set the first out or last in fragment for the fragment's
-     * container.
-     *
-     * @param transaction The executing transaction
-     * @param op The operation being run.
-     * @param transitioningFragments A structure holding the first in and last out fragments
-     *                               for each fragment container.
-     * @param isPop Is the operation a pop?
-     * @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 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
-        }
-        final int command = isPop ? INVERSE_OPS[op.cmd] : op.cmd;
-        boolean setLastIn = false;
-        boolean wasRemoved = false;
-        boolean setFirstOut = false;
-        boolean wasAdded = false;
-        switch (command) {
-            case BackStackRecord.OP_SHOW:
-                if (isReorderedTransaction) {
-                    setLastIn = fragment.mHiddenChanged && !fragment.mHidden && fragment.mAdded;
-                } else {
-                    setLastIn = fragment.mHidden;
-                }
-                wasAdded = true;
-                break;
-            case BackStackRecord.OP_ADD:
-            case BackStackRecord.OP_ATTACH:
-                if (isReorderedTransaction) {
-                    setLastIn = fragment.mIsNewlyAdded;
-                } else {
-                    setLastIn = !fragment.mAdded && !fragment.mHidden;
-                }
-                wasAdded = true;
-                break;
-            case BackStackRecord.OP_HIDE:
-                if (isReorderedTransaction) {
-                    setFirstOut = fragment.mHiddenChanged && fragment.mAdded && fragment.mHidden;
-                } else {
-                    setFirstOut = fragment.mAdded && !fragment.mHidden;
-                }
-                wasRemoved = true;
-                break;
-            case BackStackRecord.OP_REMOVE:
-            case BackStackRecord.OP_DETACH:
-                if (isReorderedTransaction) {
-                    setFirstOut = !fragment.mAdded && fragment.mView != null
-                            && fragment.mView.getVisibility() == View.VISIBLE
-                            && fragment.mPostponedAlpha >= 0;
-                } else {
-                    setFirstOut = fragment.mAdded && !fragment.mHidden;
-                }
-                wasRemoved = true;
-                break;
-        }
-        FragmentContainerTransition containerTransition = transitioningFragments.get(containerId);
-        if (setLastIn) {
-            containerTransition =
-                    ensureContainer(containerTransition, transitioningFragments, containerId);
-            containerTransition.lastIn = fragment;
-            containerTransition.lastInIsPop = isPop;
-            containerTransition.lastInTransaction = transaction;
-        }
-        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.mReorderingAllowed) {
-                manager.makeActive(fragment);
-                manager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
-            }
-        }
-        if (setFirstOut && (containerTransition == null || containerTransition.firstOut == null)) {
-            containerTransition =
-                    ensureContainer(containerTransition, transitioningFragments, containerId);
-            containerTransition.firstOut = fragment;
-            containerTransition.firstOutIsPop = isPop;
-            containerTransition.firstOutTransaction = transaction;
-        }
-
-        if (!isReorderedTransaction && wasRemoved
-                && (containerTransition != null && containerTransition.lastIn == fragment)) {
-            containerTransition.lastIn = null;
-        }
-    }
-
-    /**
-     * Ensures that a FragmentContainerTransition has been added to the SparseArray. If so,
-     * it returns the existing one. If not, one is created and added to the SparseArray and
-     * returned.
-     */
-    private static FragmentContainerTransition ensureContainer(
-            FragmentContainerTransition containerTransition,
-            SparseArray<FragmentContainerTransition> transitioningFragments, int containerId) {
-        if (containerTransition == null) {
-            containerTransition = new FragmentContainerTransition();
-            transitioningFragments.put(containerId, containerTransition);
-        }
-        return containerTransition;
-    }
-
-    /**
-     * Tracks the last fragment added and first fragment removed for fragment transitions.
-     * This also tracks which fragments are changed by push or pop transactions.
-     */
-    static class FragmentContainerTransition {
-        /**
-         * The last fragment added/attached/shown in its container
-         */
-        public Fragment lastIn;
-
-        /**
-         * true when lastIn was added during a pop transaction or false if added with a push
-         */
-        public boolean lastInIsPop;
-
-        /**
-         * The transaction that included the last in fragment
-         */
-        public BackStackRecord lastInTransaction;
-
-        /**
-         * The first fragment with a View that was removed/detached/hidden in its container.
-         */
-        public Fragment firstOut;
-
-        /**
-         * true when firstOut was removed during a pop transaction or false otherwise
-         */
-        public boolean firstOutIsPop;
-
-        /**
-         * The transaction that included the first out fragment
-         */
-        public BackStackRecord firstOutTransaction;
-    }
-}
diff --git a/android/support/v4/app/FragmentTransitionCompat21.java b/android/support/v4/app/FragmentTransitionCompat21.java
deleted file mode 100644
index 7021912..0000000
--- a/android/support/v4/app/FragmentTransitionCompat21.java
+++ /dev/null
@@ -1,316 +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.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;
-
-@RequiresApi(21)
-class FragmentTransitionCompat21 extends FragmentTransitionImpl {
-
-    @Override
-    public boolean canHandle(Object transition) {
-        return transition instanceof Transition;
-    }
-
-    @Override
-    public Object cloneTransition(Object transition) {
-        Transition copy = null;
-        if (transition != null) {
-            copy = ((Transition) transition).clone();
-        }
-        return copy;
-    }
-
-    @Override
-    public Object wrapTransitionInSet(Object transition) {
-        if (transition == null) {
-            return null;
-        }
-        TransitionSet transitionSet = new TransitionSet();
-        transitionSet.addTransition((Transition) transition);
-        return transitionSet;
-    }
-
-    @Override
-    public 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);
-    }
-
-    @Override
-    public 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;
-                }
-            });
-        }
-    }
-
-    @Override
-    public 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());
-    }
-
-    @Override
-    public 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;
-    }
-
-    @Override
-    public 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) {
-            }
-        });
-    }
-
-    @Override
-    public 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;
-        }
-    }
-
-    @Override
-    public void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
-        TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
-    }
-
-    @Override
-    public 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) {
-            }
-        });
-    }
-
-    @Override
-    public 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);
-        }
-    }
-
-    @Override
-    public 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));
-                }
-            }
-        }
-    }
-
-    @Override
-    public void addTarget(Object transitionObj, View view) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.addTarget(view);
-        }
-    }
-
-    @Override
-    public void removeTarget(Object transitionObj, View view) {
-        if (transitionObj != null) {
-            Transition transition = (Transition) transitionObj;
-            transition.removeTarget(view);
-        }
-    }
-
-    @Override
-    public 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;
-                }
-            });
-        }
-    }
-
-}
diff --git a/android/support/v4/app/FragmentTransitionImpl.java b/android/support/v4/app/FragmentTransitionImpl.java
deleted file mode 100644
index b90e7fe..0000000
--- a/android/support/v4/app/FragmentTransitionImpl.java
+++ /dev/null
@@ -1,363 +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.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.Rect;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewGroupCompat;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public abstract class FragmentTransitionImpl {
-
-    /**
-     * Returns {@code true} if this implementation can handle the specified {@link transition}.
-     */
-    public abstract boolean canHandle(Object transition);
-
-    /**
-     * Returns a clone of a transition or null if it is null
-     */
-    public abstract Object cloneTransition(Object transition);
-
-    /**
-     * Wraps a transition in a TransitionSet and returns the set. If transition is null, null is
-     * returned.
-     */
-    public abstract Object wrapTransitionInSet(Object transition);
-
-    /**
-     * 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 abstract void setSharedElementTargets(Object transitionObj,
-            View nonExistentView, ArrayList<View> sharedViews);
-
-    /**
-     * Sets a transition epicenter to the rectangle of a given View.
-     */
-    public abstract void setEpicenter(Object transitionObj, View view);
-
-    /**
-     * 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.
-     */
-    protected 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 abstract void addTargets(Object transitionObj, ArrayList<View> views);
-
-    /**
-     * 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 abstract Object mergeTransitionsTogether(Object transition1, Object transition2,
-            Object transition3);
-
-    /**
-     * After the transition completes, the fragment's view is set to GONE and the exiting
-     * views are set to VISIBLE.
-     */
-    public abstract void scheduleHideFragmentView(Object exitTransitionObj, View fragmentView,
-            ArrayList<View> exitingViews);
-
-    /**
-     * 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 abstract Object mergeTransitionsInSequence(Object exitTransitionObj,
-            Object enterTransitionObj, Object sharedElementTransitionObj);
-
-    /**
-     * Calls {@code TransitionManager#beginDelayedTransition(ViewGroup, Transition)}.
-     */
-    public abstract void beginDelayedTransition(ViewGroup sceneRoot, Object 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)}.
-     */
-    ArrayList<String> prepareSetNameOverridesReordered(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(ViewCompat.getTransitionName(view));
-            ViewCompat.setTransitionName(view, null);
-        }
-        return names;
-    }
-
-    /**
-     * Changes the shared element names for the incoming shared elements 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)}.
-     */
-    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 = ViewCompat.getTransitionName(view);
-            outNames.add(name);
-            if (name == null) {
-                continue;
-            }
-            ViewCompat.setTransitionName(view, null);
-            final String inName = nameOverrides.get(name);
-            for (int j = 0; j < numSharedElements; j++) {
-                if (inName.equals(inNames.get(j))) {
-                    ViewCompat.setTransitionName(sharedElementsIn.get(j), name);
-                    break;
-                }
-            }
-        }
-
-        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
-            @Override
-            public void run() {
-                for (int i = 0; i < numSharedElements; i++) {
-                    ViewCompat.setTransitionName(sharedElementsIn.get(i), inNames.get(i));
-                    ViewCompat.setTransitionName(sharedElementsOut.get(i), 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.
-     */
-    void captureTransitioningViews(ArrayList<View> transitioningViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            if (view instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) view;
-                if (ViewGroupCompat.isTransitionGroup(viewGroup)) {
-                    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.
-     */
-    void findNamedViews(Map<String, View> namedViews, View view) {
-        if (view.getVisibility() == View.VISIBLE) {
-            String transitionName = ViewCompat.getTransitionName(view);
-            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);
-                }
-            }
-        }
-    }
-
-    /**
-     *Applies the prepared {@code nameOverrides} to the view hierarchy.
-     */
-    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 = ViewCompat.getTransitionName(view);
-                    if (name != null) {
-                        String inName = findKeyForValue(nameOverrides, name);
-                        ViewCompat.setTransitionName(view, inName);
-                    }
-                }
-            }
-        });
-    }
-
-    /**
-     * 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 abstract void scheduleRemoveTargets(Object overallTransitionObj,
-            Object enterTransition, ArrayList<View> enteringViews,
-            Object exitTransition, ArrayList<View> exitingViews,
-            Object sharedElementTransition, ArrayList<View> sharedElementsIn);
-
-    /**
-     * Swap the targets for the shared element transition from those Views in sharedElementsOut
-     * to those in sharedElementsIn
-     */
-    public abstract void swapSharedElementTargets(Object sharedElementTransitionObj,
-            ArrayList<View> sharedElementsOut, ArrayList<View> 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 abstract void replaceTargets(Object transitionObj, ArrayList<View> oldTargets,
-            ArrayList<View> newTargets);
-
-    /**
-     * Adds a View target to a transition. If transitionObj is null, nothing is done.
-     */
-    public abstract void addTarget(Object transitionObj, View view);
-
-    /**
-     * Remove a View target to a transition. If transitionObj is null, nothing is done.
-     */
-    public abstract void removeTarget(Object transitionObj, View view);
-
-    /**
-     * Sets the epicenter of a transition to a rect object. The object can be modified until
-     * the transition runs.
-     */
-    public abstract void setEpicenter(Object transitionObj, Rect epicenter);
-
-    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 = ViewCompat.getTransitionName(view);
-                    final String inName = nameOverrides.get(name);
-                    ViewCompat.setTransitionName(view, inName);
-                }
-            }
-        });
-    }
-
-    /**
-     * 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.
-     */
-    protected 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;
-    }
-
-    /**
-     * Simple utility to detect if a list is null or has no elements.
-     */
-    protected static boolean isNullOrEmpty(List list) {
-        return list == null || list.isEmpty();
-    }
-
-    /**
-     * Utility to find the String key in {@code map} that maps to {@code value}.
-     */
-    @SuppressWarnings("WeakerAccess")
-    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;
-    }
-
-}
diff --git a/android/support/v4/app/FrameMetricsAggregator.java b/android/support/v4/app/FrameMetricsAggregator.java
deleted file mode 100644
index 48beb2f..0000000
--- a/android/support/v4/app/FrameMetricsAggregator.java
+++ /dev/null
@@ -1,464 +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.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/android/support/v4/app/JobIntentService.java b/android/support/v4/app/JobIntentService.java
deleted file mode 100644
index 87b7441..0000000
--- a/android/support/v4/app/JobIntentService.java
+++ /dev/null
@@ -1,650 +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.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/main/java/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;
-    boolean mDestroyed = 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 serviceStartReceived() {
-        }
-
-        public void serviceProcessingStarted() {
-        }
-
-        public void serviceProcessingFinished() {
-        }
-    }
-
-    /**
-     * 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 mServiceProcessing;
-
-        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 (!mServiceProcessing) {
-                            // 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 it to not get
-                            // the work.
-                            mLaunchWakeLock.acquire(60 * 1000);
-                        }
-                    }
-                }
-            }
-        }
-
-        @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 serviceProcessingStarted() {
-            synchronized (this) {
-                // We hold the wake lock as long as the service is processing commands.
-                if (!mServiceProcessing) {
-                    mServiceProcessing = true;
-                    // Keep the device awake, but only for at most 10 minutes at a time
-                    // (Similar to JobScheduler.)
-                    mRunWakeLock.acquire(10 * 60 * 1000L);
-                    mLaunchWakeLock.release();
-                }
-            }
-        }
-
-        @Override
-        public void serviceProcessingFinished() {
-            synchronized (this) {
-                if (mServiceProcessing) {
-                    // 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);
-                    }
-                    mServiceProcessing = 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(false);
-            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);
-        }
-    }
-
-    /**
-     * 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(true);
-            }
-            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 (mCompatQueue != null) {
-            synchronized (mCompatQueue) {
-                mDestroyed = true;
-                mCompatWorkEnqueuer.serviceProcessingFinished();
-            }
-        }
-    }
-
-    /**
-     * 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(boolean reportStarted) {
-        if (mCurProcessor == null) {
-            mCurProcessor = new CommandProcessor();
-            if (mCompatWorkEnqueuer != null && reportStarted) {
-                mCompatWorkEnqueuer.serviceProcessingStarted();
-            }
-            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(false);
-                } else if (!mDestroyed) {
-                    mCompatWorkEnqueuer.serviceProcessingFinished();
-                }
-            }
-        }
-    }
-
-    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/android/support/v4/app/ListFragment.java b/android/support/v4/app/ListFragment.java
deleted file mode 100644
index 496bd8e..0000000
--- a/android/support/v4/app/ListFragment.java
+++ /dev/null
@@ -1,379 +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.Bundle;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.widget.AdapterView;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-/**
- * Static library support version of the framework's {@link android.app.ListFragment}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public class ListFragment extends Fragment {
-    static final int INTERNAL_EMPTY_ID = 0x00ff0001;
-    static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002;
-    static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003;
-    
-    final private Handler mHandler = new Handler();
-
-    final private Runnable mRequestFocus = new Runnable() {
-        @Override
-        public void run() {
-            mList.focusableViewAvailable(mList);
-        }
-    };
-    
-    final private AdapterView.OnItemClickListener mOnClickListener
-            = new AdapterView.OnItemClickListener() {
-        @Override
-        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
-            onListItemClick((ListView)parent, v, position, id);
-        }
-    };
-
-    ListAdapter mAdapter;
-    ListView mList;
-    View mEmptyView;
-    TextView mStandardEmptyView;
-    View mProgressContainer;
-    View mListContainer;
-    CharSequence mEmptyText;
-    boolean mListShown;
-
-    public ListFragment() {
-    }
-
-    /**
-     * Provide default implementation to return a simple list view.  Subclasses
-     * can override to replace with their own layout.  If doing so, the
-     * returned view hierarchy <em>must</em> have a ListView whose id
-     * is {@link android.R.id#list android.R.id.list} and can optionally
-     * have a sibling view id {@link android.R.id#empty android.R.id.empty}
-     * that is to be shown when the list is empty.
-     * 
-     * <p>If you are overriding this method with your own custom content,
-     * consider including the standard layout {@link android.R.layout#list_content}
-     * in your layout file, so that you continue to retain all of the standard
-     * behavior of ListFragment.  In particular, this is currently the only
-     * way to have the built-in indeterminant progress state be shown.
-     */
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        final Context context = getContext();
-
-        FrameLayout root = new FrameLayout(context);
-
-        // ------------------------------------------------------------------
-
-        LinearLayout pframe = new LinearLayout(context);
-        pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID);
-        pframe.setOrientation(LinearLayout.VERTICAL);
-        pframe.setVisibility(View.GONE);
-        pframe.setGravity(Gravity.CENTER);
-
-        ProgressBar progress = new ProgressBar(context, null,
-                android.R.attr.progressBarStyleLarge);
-        pframe.addView(progress, new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
-
-        root.addView(pframe, new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        // ------------------------------------------------------------------
-
-        FrameLayout lframe = new FrameLayout(context);
-        lframe.setId(INTERNAL_LIST_CONTAINER_ID);
-
-        TextView tv = new TextView(context);
-        tv.setId(INTERNAL_EMPTY_ID);
-        tv.setGravity(Gravity.CENTER);
-        lframe.addView(tv, new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        ListView lv = new ListView(context);
-        lv.setId(android.R.id.list);
-        lv.setDrawSelectorOnTop(false);
-        lframe.addView(lv, new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        root.addView(lframe, new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        // ------------------------------------------------------------------
-
-        root.setLayoutParams(new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
-        return root;
-    }
-
-    /**
-     * Attach to list view once the view hierarchy has been created.
-     */
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        ensureList();
-    }
-
-    /**
-     * Detach from list view.
-     */
-    @Override
-    public void onDestroyView() {
-        mHandler.removeCallbacks(mRequestFocus);
-        mList = null;
-        mListShown = false;
-        mEmptyView = mProgressContainer = mListContainer = null;
-        mStandardEmptyView = null;
-        super.onDestroyView();
-    }
-
-    /**
-     * This method will be called when an item in the list is selected.
-     * Subclasses should override. Subclasses can call
-     * getListView().getItemAtPosition(position) if they need to access the
-     * data associated with the selected item.
-     *
-     * @param l The ListView where the click happened
-     * @param v The view that was clicked within the ListView
-     * @param position The position of the view in the list
-     * @param id The row id of the item that was clicked
-     */
-    public void onListItemClick(ListView l, View v, int position, long id) {
-    }
-
-    /**
-     * Provide the cursor for the list view.
-     */
-    public void setListAdapter(ListAdapter adapter) {
-        boolean hadAdapter = mAdapter != null;
-        mAdapter = adapter;
-        if (mList != null) {
-            mList.setAdapter(adapter);
-            if (!mListShown && !hadAdapter) {
-                // The list was hidden, and previously didn't have an
-                // adapter.  It is now time to show it.
-                setListShown(true, getView().getWindowToken() != null);
-            }
-        }
-    }
-
-    /**
-     * Set the currently selected list item to the specified
-     * position with the adapter's data
-     *
-     * @param position
-     */
-    public void setSelection(int position) {
-        ensureList();
-        mList.setSelection(position);
-    }
-
-    /**
-     * Get the position of the currently selected list item.
-     */
-    public int getSelectedItemPosition() {
-        ensureList();
-        return mList.getSelectedItemPosition();
-    }
-
-    /**
-     * Get the cursor row ID of the currently selected list item.
-     */
-    public long getSelectedItemId() {
-        ensureList();
-        return mList.getSelectedItemId();
-    }
-
-    /**
-     * Get the fragment's list view widget.
-     */
-    public ListView getListView() {
-        ensureList();
-        return mList;
-    }
-
-    /**
-     * The default content for a ListFragment has a TextView that can
-     * be shown when the list is empty.  If you would like to have it
-     * shown, call this method to supply the text it should use.
-     */
-    public void setEmptyText(CharSequence text) {
-        ensureList();
-        if (mStandardEmptyView == null) {
-            throw new IllegalStateException("Can't be used with a custom content view");
-        }
-        mStandardEmptyView.setText(text);
-        if (mEmptyText == null) {
-            mList.setEmptyView(mStandardEmptyView);
-        }
-        mEmptyText = text;
-    }
-    
-    /**
-     * Control whether the list is being displayed.  You can make it not
-     * displayed if you are waiting for the initial data to show in it.  During
-     * this time an indeterminant progress indicator will be shown instead.
-     * 
-     * <p>Applications do not normally need to use this themselves.  The default
-     * behavior of ListFragment is to start with the list not being shown, only
-     * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}.
-     * If the list at that point had not been shown, when it does get shown
-     * it will be do without the user ever seeing the hidden state.
-     * 
-     * @param shown If true, the list view is shown; if false, the progress
-     * indicator.  The initial value is true.
-     */
-    public void setListShown(boolean shown) {
-        setListShown(shown, true);
-    }
-    
-    /**
-     * Like {@link #setListShown(boolean)}, but no animation is used when
-     * transitioning from the previous state.
-     */
-    public void setListShownNoAnimation(boolean shown) {
-        setListShown(shown, false);
-    }
-    
-    /**
-     * Control whether the list is being displayed.  You can make it not
-     * displayed if you are waiting for the initial data to show in it.  During
-     * this time an indeterminant progress indicator will be shown instead.
-     * 
-     * @param shown If true, the list view is shown; if false, the progress
-     * indicator.  The initial value is true.
-     * @param animate If true, an animation will be used to transition to the
-     * new state.
-     */
-    private void setListShown(boolean shown, boolean animate) {
-        ensureList();
-        if (mProgressContainer == null) {
-            throw new IllegalStateException("Can't be used with a custom content view");
-        }
-        if (mListShown == shown) {
-            return;
-        }
-        mListShown = shown;
-        if (shown) {
-            if (animate) {
-                mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
-                        getContext(), android.R.anim.fade_out));
-                mListContainer.startAnimation(AnimationUtils.loadAnimation(
-                        getContext(), android.R.anim.fade_in));
-            } else {
-                mProgressContainer.clearAnimation();
-                mListContainer.clearAnimation();
-            }
-            mProgressContainer.setVisibility(View.GONE);
-            mListContainer.setVisibility(View.VISIBLE);
-        } else {
-            if (animate) {
-                mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
-                        getContext(), android.R.anim.fade_in));
-                mListContainer.startAnimation(AnimationUtils.loadAnimation(
-                        getContext(), android.R.anim.fade_out));
-            } else {
-                mProgressContainer.clearAnimation();
-                mListContainer.clearAnimation();
-            }
-            mProgressContainer.setVisibility(View.VISIBLE);
-            mListContainer.setVisibility(View.GONE);
-        }
-    }
-    
-    /**
-     * Get the ListAdapter associated with this fragment's ListView.
-     */
-    public ListAdapter getListAdapter() {
-        return mAdapter;
-    }
-
-    private void ensureList() {
-        if (mList != null) {
-            return;
-        }
-        View root = getView();
-        if (root == null) {
-            throw new IllegalStateException("Content view not yet created");
-        }
-        if (root instanceof ListView) {
-            mList = (ListView)root;
-        } else {
-            mStandardEmptyView = (TextView)root.findViewById(INTERNAL_EMPTY_ID);
-            if (mStandardEmptyView == null) {
-                mEmptyView = root.findViewById(android.R.id.empty);
-            } else {
-                mStandardEmptyView.setVisibility(View.GONE);
-            }
-            mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID);
-            mListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID);
-            View rawListView = root.findViewById(android.R.id.list);
-            if (!(rawListView instanceof ListView)) {
-                if (rawListView == null) {
-                    throw new RuntimeException(
-                            "Your content must have a ListView whose id attribute is " +
-                            "'android.R.id.list'");
-                }
-                throw new RuntimeException(
-                        "Content has view with id attribute 'android.R.id.list' "
-                        + "that is not a ListView class");
-            }
-            mList = (ListView)rawListView;
-            if (mEmptyView != null) {
-                mList.setEmptyView(mEmptyView);
-            } else if (mEmptyText != null) {
-                mStandardEmptyView.setText(mEmptyText);
-                mList.setEmptyView(mStandardEmptyView);
-            }
-        }
-        mListShown = true;
-        mList.setOnItemClickListener(mOnClickListener);
-        if (mAdapter != null) {
-            ListAdapter adapter = mAdapter;
-            mAdapter = null;
-            setListAdapter(adapter);
-        } else {
-            // We are starting without an adapter, so assume we won't
-            // have our data right away and start with the progress indicator.
-            if (mProgressContainer != null) {
-                setListShown(false, false);
-            }
-        }
-        mHandler.post(mRequestFocus);
-    }
-}
diff --git a/android/support/v4/app/LoaderManager.java b/android/support/v4/app/LoaderManager.java
deleted file mode 100644
index 32e211a..0000000
--- a/android/support/v4/app/LoaderManager.java
+++ /dev/null
@@ -1,874 +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.os.Bundle;
-import android.os.Looper;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.Loader;
-import android.support.v4.util.DebugUtils;
-import android.support.v4.util.SparseArrayCompat;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.reflect.Modifier;
-
-/**
- * Static library support version of the framework's {@link android.app.LoaderManager}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- *
- * <p>Your activity must derive from {@link FragmentActivity} to use this.
- */
-public abstract class LoaderManager {
-    /**
-     * Callback interface for a client to interact with the manager.
-     */
-    public interface LoaderCallbacks<D> {
-        /**
-         * Instantiate and return a new Loader for the given ID.
-         *
-         * <p>This will always be called from the process's main thread.
-         *
-         * @param id The ID whose loader is to be created.
-         * @param args Any arguments supplied by the caller.
-         * @return Return a new Loader instance that is ready to start loading.
-         */
-        @MainThread
-        @NonNull
-        Loader<D> onCreateLoader(int id, @Nullable Bundle args);
-
-        /**
-         * Called when a previously created loader has finished its load.  Note
-         * that normally an application is <em>not</em> allowed to commit fragment
-         * transactions while in this call, since it can happen after an
-         * activity's state is saved.  See {@link FragmentManager#beginTransaction()
-         * FragmentManager.openTransaction()} for further discussion on this.
-         *
-         * <p>This function is guaranteed to be called prior to the release of
-         * the last data that was supplied for this Loader.  At this point
-         * you should remove all use of the old data (since it will be released
-         * soon), but should not do your own release of the data since its Loader
-         * owns it and will take care of that.  The Loader will take care of
-         * management of its data so you don't have to.  In particular:
-         *
-         * <ul>
-         * <li> <p>The Loader will monitor for changes to the data, and report
-         * them to you through new calls here.  You should not monitor the
-         * data yourself.  For example, if the data is a {@link android.database.Cursor}
-         * and you place it in a {@link android.widget.CursorAdapter}, use
-         * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
-         * android.database.Cursor, int)} constructor <em>without</em> passing
-         * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
-         * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
-         * (that is, use 0 for the flags argument).  This prevents the CursorAdapter
-         * from doing its own observing of the Cursor, which is not needed since
-         * when a change happens you will get a new Cursor throw another call
-         * here.
-         * <li> The Loader will release the data once it knows the application
-         * is no longer using it.  For example, if the data is
-         * a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
-         * you should not call close() on it yourself.  If the Cursor is being placed in a
-         * {@link android.widget.CursorAdapter}, you should use the
-         * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
-         * method so that the old Cursor is not closed.
-         * </ul>
-         *
-         * <p>This will always be called from the process's main thread.
-         *
-         * @param loader The Loader that has finished.
-         * @param data The data generated by the Loader.
-         */
-        @MainThread
-        void onLoadFinished(@NonNull Loader<D> loader, D data);
-
-        /**
-         * Called when a previously created loader is being reset, and thus
-         * making its data unavailable.  The application should at this point
-         * remove any references it has to the Loader's data.
-         *
-         * <p>This will always be called from the process's main thread.
-         *
-         * @param loader The Loader that is being reset.
-         */
-        @MainThread
-        void onLoaderReset(@NonNull Loader<D> loader);
-    }
-
-    /**
-     * Ensures a loader is initialized and active.  If the loader doesn't
-     * already exist, one is created and (if the activity/fragment is currently
-     * started) starts the loader.  Otherwise the last created
-     * loader is re-used.
-     *
-     * <p>In either case, the given callback is associated with the loader, and
-     * will be called as the loader state changes.  If at the point of call
-     * the caller is in its started state, and the requested loader
-     * already exists and has generated its data, then
-     * callback {@link LoaderCallbacks#onLoadFinished} will
-     * be called immediately (inside of this function), so you must be prepared
-     * for this to happen.
-     *
-     * <p>Must be called from the process's main thread.
-     *
-     * @param id A unique identifier for this loader.  Can be whatever you want.
-     * Identifiers are scoped to a particular LoaderManager instance.
-     * @param args Optional arguments to supply to the loader at construction.
-     * If a loader already exists (a new one does not need to be created), this
-     * parameter will be ignored and the last arguments continue to be used.
-     * @param callback Interface the LoaderManager will call to report about
-     * changes in the state of the loader.  Required.
-     */
-    @MainThread
-    @NonNull
-    public abstract <D> Loader<D> initLoader(int id, @Nullable Bundle args,
-            @NonNull LoaderManager.LoaderCallbacks<D> callback);
-
-    /**
-     * Starts a new or restarts an existing {@link android.content.Loader} in
-     * this manager, registers the callbacks to it,
-     * and (if the activity/fragment is currently started) starts loading it.
-     * If a loader with the same id has previously been
-     * started it will automatically be destroyed when the new loader completes
-     * its work. The callback will be delivered before the old loader
-     * is destroyed.
-     *
-     * <p>Must be called from the process's main thread.
-     *
-     * @param id A unique identifier for this loader.  Can be whatever you want.
-     * Identifiers are scoped to a particular LoaderManager instance.
-     * @param args Optional arguments to supply to the loader at construction.
-     * @param callback Interface the LoaderManager will call to report about
-     * changes in the state of the loader.  Required.
-     */
-    @MainThread
-    @NonNull
-    public abstract <D> Loader<D> restartLoader(int id, @Nullable Bundle args,
-            @NonNull LoaderManager.LoaderCallbacks<D> callback);
-
-    /**
-     * Stops and removes the loader with the given ID.  If this loader
-     * had previously reported data to the client through
-     * {@link LoaderCallbacks#onLoadFinished(Loader, Object)}, a call
-     * will be made to {@link LoaderCallbacks#onLoaderReset(Loader)}.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public abstract void destroyLoader(int id);
-
-    /**
-     * Return the Loader with the given id or null if no matching Loader
-     * is found.
-     */
-    @Nullable
-    public abstract <D> Loader<D> getLoader(int id);
-
-    /**
-     * Print the LoaderManager's state into the given stream.
-     *
-     * @param prefix Text to print at the front of each line.
-     * @param fd The raw file descriptor that the dump is being sent to.
-     * @param writer A PrintWriter to which the dump is to be set.
-     * @param args Additional arguments to the dump request.
-     */
-    public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
-
-    /**
-     * Control whether the framework's internal loader manager debugging
-     * logs are turned on.  If enabled, you will see output in logcat as
-     * the framework performs loader operations.
-     */
-    public static void enableDebugLogging(boolean enabled) {
-        LoaderManagerImpl.DEBUG = enabled;
-    }
-
-    /**
-     * Returns true if any loaders managed are currently running and have not
-     * returned data to the application yet.
-     */
-    public boolean hasRunningLoaders() { return false; }
-}
-
-class LoaderManagerImpl extends LoaderManager {
-    static final String TAG = "LoaderManager";
-    static boolean DEBUG = false;
-
-    // These are the currently active loaders.  A loader is here
-    // from the time its load is started until it has been explicitly
-    // stopped or restarted by the application.
-    final SparseArrayCompat<LoaderInfo> mLoaders = new SparseArrayCompat<LoaderInfo>();
-
-    // These are previously run loaders.  This list is maintained internally
-    // to avoid destroying a loader while an application is still using it.
-    // It allows an application to restart a loader, but continue using its
-    // previously run loader until the new loader's data is available.
-    final SparseArrayCompat<LoaderInfo> mInactiveLoaders = new SparseArrayCompat<LoaderInfo>();
-
-    final String mWho;
-
-    boolean mStarted;
-    boolean mRetaining;
-    boolean mRetainingStarted;
-
-    boolean mCreatingLoader;
-    FragmentHostCallback mHost;
-
-    final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
-            Loader.OnLoadCanceledListener<Object> {
-        final int mId;
-        final Bundle mArgs;
-        LoaderManager.LoaderCallbacks<Object> mCallbacks;
-        Loader<Object> mLoader;
-        boolean mHaveData;
-        boolean mDeliveredData;
-        Object mData;
-        @SuppressWarnings("hiding")
-        boolean mStarted;
-        @SuppressWarnings("hiding")
-        boolean mRetaining;
-        @SuppressWarnings("hiding")
-        boolean mRetainingStarted;
-        boolean mReportNextStart;
-        boolean mDestroyed;
-        boolean mListenerRegistered;
-
-        LoaderInfo mPendingLoader;
-
-        public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
-            mId = id;
-            mArgs = args;
-            mCallbacks = callbacks;
-        }
-
-        void start() {
-            if (mRetaining && mRetainingStarted) {
-                // Our owner is started, but we were being retained from a
-                // previous instance in the started state...  so there is really
-                // nothing to do here, since the loaders are still started.
-                mStarted = true;
-                return;
-            }
-
-            if (mStarted) {
-                // If loader already started, don't restart.
-                return;
-            }
-
-            mStarted = true;
-
-            if (DEBUG) Log.v(TAG, "  Starting: " + this);
-            if (mLoader == null && mCallbacks != null) {
-               mLoader = mCallbacks.onCreateLoader(mId, mArgs);
-            }
-            if (mLoader != null) {
-                if (mLoader.getClass().isMemberClass()
-                        && !Modifier.isStatic(mLoader.getClass().getModifiers())) {
-                    throw new IllegalArgumentException(
-                            "Object returned from onCreateLoader must not be a non-static inner member class: "
-                            + mLoader);
-                }
-                if (!mListenerRegistered) {
-                    mLoader.registerListener(mId, this);
-                    mLoader.registerOnLoadCanceledListener(this);
-                    mListenerRegistered = true;
-                }
-                mLoader.startLoading();
-            }
-        }
-
-        void retain() {
-            if (DEBUG) Log.v(TAG, "  Retaining: " + this);
-            mRetaining = true;
-            mRetainingStarted = mStarted;
-            mStarted = false;
-            mCallbacks = null;
-        }
-
-        void finishRetain() {
-            if (mRetaining) {
-                if (DEBUG) Log.v(TAG, "  Finished Retaining: " + this);
-                mRetaining = false;
-                if (mStarted != mRetainingStarted) {
-                    if (!mStarted) {
-                        // This loader was retained in a started state, but
-                        // at the end of retaining everything our owner is
-                        // no longer started...  so make it stop.
-                        stop();
-                    }
-                }
-            }
-
-            if (mStarted && mHaveData && !mReportNextStart) {
-                // This loader has retained its data, either completely across
-                // a configuration change or just whatever the last data set
-                // was after being restarted from a stop, and now at the point of
-                // finishing the retain we find we remain started, have
-                // our data, and the owner has a new callback...  so
-                // let's deliver the data now.
-                callOnLoadFinished(mLoader, mData);
-            }
-        }
-
-        void reportStart() {
-            if (mStarted) {
-                if (mReportNextStart) {
-                    mReportNextStart = false;
-                    if (mHaveData && !mRetaining) {
-                        callOnLoadFinished(mLoader, mData);
-                    }
-                }
-            }
-        }
-
-        void stop() {
-            if (DEBUG) Log.v(TAG, "  Stopping: " + this);
-            mStarted = false;
-            if (!mRetaining) {
-                if (mLoader != null && mListenerRegistered) {
-                    // Let the loader know we're done with it
-                    mListenerRegistered = false;
-                    mLoader.unregisterListener(this);
-                    mLoader.unregisterOnLoadCanceledListener(this);
-                    mLoader.stopLoading();
-                }
-            }
-        }
-
-        boolean cancel() {
-            if (DEBUG) Log.v(TAG, "  Canceling: " + this);
-            if (mStarted && mLoader != null && mListenerRegistered) {
-                final boolean cancelLoadResult = mLoader.cancelLoad();
-                if (!cancelLoadResult) {
-                    onLoadCanceled(mLoader);
-                }
-                return cancelLoadResult;
-            }
-            return false;
-        }
-
-        void destroy() {
-            if (DEBUG) Log.v(TAG, "  Destroying: " + this);
-            mDestroyed = true;
-            boolean needReset = mDeliveredData;
-            mDeliveredData = false;
-            if (mCallbacks != null && mLoader != null && mHaveData && needReset) {
-                if (DEBUG) Log.v(TAG, "  Resetting: " + this);
-                String lastBecause = null;
-                if (mHost != null) {
-                    lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
-                    mHost.mFragmentManager.mNoTransactionsBecause = "onLoaderReset";
-                }
-                try {
-                    mCallbacks.onLoaderReset(mLoader);
-                } finally {
-                    if (mHost != null) {
-                        mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
-                    }
-                }
-            }
-            mCallbacks = null;
-            mData = null;
-            mHaveData = false;
-            if (mLoader != null) {
-                if (mListenerRegistered) {
-                    mListenerRegistered = false;
-                    mLoader.unregisterListener(this);
-                    mLoader.unregisterOnLoadCanceledListener(this);
-                }
-                mLoader.reset();
-            }
-            if (mPendingLoader != null) {
-                mPendingLoader.destroy();
-            }
-        }
-
-        @Override
-        public void onLoadCanceled(@NonNull Loader<Object> loader) {
-            if (DEBUG) Log.v(TAG, "onLoadCanceled: " + this);
-
-            if (mDestroyed) {
-                if (DEBUG) Log.v(TAG, "  Ignoring load canceled -- destroyed");
-                return;
-            }
-
-            if (mLoaders.get(mId) != this) {
-                // This cancellation message is not coming from the current active loader.
-                // We don't care about it.
-                if (DEBUG) Log.v(TAG, "  Ignoring load canceled -- not active");
-                return;
-            }
-
-            LoaderInfo pending = mPendingLoader;
-            if (pending != null) {
-                // There is a new request pending and we were just
-                // waiting for the old one to cancel or complete before starting
-                // it.  So now it is time, switch over to the new loader.
-                if (DEBUG) Log.v(TAG, "  Switching to pending loader: " + pending);
-                mPendingLoader = null;
-                mLoaders.put(mId, null);
-                destroy();
-                installLoader(pending);
-            }
-        }
-
-        @Override
-        public void onLoadComplete(@NonNull Loader<Object> loader, Object data) {
-            if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
-
-            if (mDestroyed) {
-                if (DEBUG) Log.v(TAG, "  Ignoring load complete -- destroyed");
-                return;
-            }
-
-            if (mLoaders.get(mId) != this) {
-                // This data is not coming from the current active loader.
-                // We don't care about it.
-                if (DEBUG) Log.v(TAG, "  Ignoring load complete -- not active");
-                return;
-            }
-
-            LoaderInfo pending = mPendingLoader;
-            if (pending != null) {
-                // There is a new request pending and we were just
-                // waiting for the old one to complete before starting
-                // it.  So now it is time, switch over to the new loader.
-                if (DEBUG) Log.v(TAG, "  Switching to pending loader: " + pending);
-                mPendingLoader = null;
-                mLoaders.put(mId, null);
-                destroy();
-                installLoader(pending);
-                return;
-            }
-
-            // Notify of the new data so the app can switch out the old data before
-            // we try to destroy it.
-            if (mData != data || !mHaveData) {
-                mData = data;
-                mHaveData = true;
-                if (mStarted) {
-                    callOnLoadFinished(loader, data);
-                }
-            }
-
-            //if (DEBUG) Log.v(TAG, "  onLoadFinished returned: " + this);
-
-            // We have now given the application the new loader with its
-            // loaded data, so it should have stopped using the previous
-            // loader.  If there is a previous loader on the inactive list,
-            // clean it up.
-            LoaderInfo info = mInactiveLoaders.get(mId);
-            if (info != null && info != this) {
-                info.mDeliveredData = false;
-                info.destroy();
-                mInactiveLoaders.remove(mId);
-            }
-
-            if (mHost != null && !hasRunningLoaders()) {
-                mHost.mFragmentManager.startPendingDeferredFragments();
-            }
-        }
-
-        void callOnLoadFinished(Loader<Object> loader, Object data) {
-            if (mCallbacks != null) {
-                String lastBecause = null;
-                if (mHost != null) {
-                    lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
-                    mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
-                }
-                try {
-                    if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "
-                            + loader.dataToString(data));
-                    mCallbacks.onLoadFinished(loader, data);
-                } finally {
-                    if (mHost != null) {
-                        mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
-                    }
-                }
-                mDeliveredData = true;
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(64);
-            sb.append("LoaderInfo{");
-            sb.append(Integer.toHexString(System.identityHashCode(this)));
-            sb.append(" #");
-            sb.append(mId);
-            sb.append(" : ");
-            DebugUtils.buildShortClassTag(mLoader, sb);
-            sb.append("}}");
-            return sb.toString();
-        }
-
-        public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-            writer.print(prefix); writer.print("mId="); writer.print(mId);
-                    writer.print(" mArgs="); writer.println(mArgs);
-            writer.print(prefix); writer.print("mCallbacks="); writer.println(mCallbacks);
-            writer.print(prefix); writer.print("mLoader="); writer.println(mLoader);
-            if (mLoader != null) {
-                mLoader.dump(prefix + "  ", fd, writer, args);
-            }
-            if (mHaveData || mDeliveredData) {
-                writer.print(prefix); writer.print("mHaveData="); writer.print(mHaveData);
-                        writer.print("  mDeliveredData="); writer.println(mDeliveredData);
-                writer.print(prefix); writer.print("mData="); writer.println(mData);
-            }
-            writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
-                    writer.print(" mReportNextStart="); writer.print(mReportNextStart);
-                    writer.print(" mDestroyed="); writer.println(mDestroyed);
-            writer.print(prefix); writer.print("mRetaining="); writer.print(mRetaining);
-                    writer.print(" mRetainingStarted="); writer.print(mRetainingStarted);
-                    writer.print(" mListenerRegistered="); writer.println(mListenerRegistered);
-            if (mPendingLoader != null) {
-                writer.print(prefix); writer.println("Pending Loader ");
-                        writer.print(mPendingLoader); writer.println(":");
-                mPendingLoader.dump(prefix + "  ", fd, writer, args);
-            }
-        }
-    }
-
-    LoaderManagerImpl(String who, FragmentHostCallback host, boolean started) {
-        mWho = who;
-        mHost = host;
-        mStarted = started;
-    }
-
-    void updateHostController(FragmentHostCallback host) {
-        mHost = host;
-    }
-
-    private LoaderInfo createLoader(int id, Bundle args,
-            LoaderManager.LoaderCallbacks<Object> callback) {
-        LoaderInfo info = new LoaderInfo(id, args,  callback);
-        Loader<Object> loader = callback.onCreateLoader(id, args);
-        info.mLoader = loader;
-        return info;
-    }
-
-    private LoaderInfo createAndInstallLoader(int id, Bundle args,
-            LoaderManager.LoaderCallbacks<Object> callback) {
-        try {
-            mCreatingLoader = true;
-            LoaderInfo info = createLoader(id, args, callback);
-            installLoader(info);
-            return info;
-        } finally {
-            mCreatingLoader = false;
-        }
-    }
-
-    void installLoader(LoaderInfo info) {
-        mLoaders.put(info.mId, info);
-        if (mStarted) {
-            // The activity will start all existing loaders in it's onStart(),
-            // so only start them here if we're past that point of the activity's
-            // life cycle
-            info.start();
-        }
-    }
-
-    @MainThread
-    @NonNull
-    @Override
-    @SuppressWarnings("unchecked")
-    public <D> Loader<D> initLoader(int id, @Nullable Bundle args,
-            @NonNull LoaderManager.LoaderCallbacks<D> callback) {
-        if (mCreatingLoader) {
-            throw new IllegalStateException("Called while creating a loader");
-        }
-        if (Looper.getMainLooper() != Looper.myLooper()) {
-            throw new IllegalStateException("initLoader must be called on the main thread");
-        }
-
-        LoaderInfo info = mLoaders.get(id);
-
-        if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
-
-        if (info == null) {
-            // Loader doesn't already exist; create.
-            info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
-            if (DEBUG) Log.v(TAG, "  Created new loader " + info);
-        } else {
-            if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);
-            info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
-        }
-
-        if (info.mHaveData && mStarted) {
-            // If the loader has already generated its data, report it now.
-            info.callOnLoadFinished(info.mLoader, info.mData);
-        }
-
-        return (Loader<D>)info.mLoader;
-    }
-
-    @MainThread
-    @NonNull
-    @Override
-    @SuppressWarnings("unchecked")
-    public <D> Loader<D> restartLoader(int id, @Nullable Bundle args,
-            @NonNull LoaderManager.LoaderCallbacks<D> callback) {
-        if (mCreatingLoader) {
-            throw new IllegalStateException("Called while creating a loader");
-        }
-        if (Looper.getMainLooper() != Looper.myLooper()) {
-            throw new IllegalStateException("restartLoader must be called on the main thread");
-        }
-
-        LoaderInfo info = mLoaders.get(id);
-        if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args);
-        if (info != null) {
-            LoaderInfo inactive = mInactiveLoaders.get(id);
-            if (inactive != null) {
-                if (info.mHaveData) {
-                    // This loader now has data...  we are probably being
-                    // called from within onLoadComplete, where we haven't
-                    // yet destroyed the last inactive loader.  So just do
-                    // that now.
-                    if (DEBUG) Log.v(TAG, "  Removing last inactive loader: " + info);
-                    inactive.mDeliveredData = false;
-                    inactive.destroy();
-                    info.mLoader.abandon();
-                    mInactiveLoaders.put(id, info);
-                } else {
-                    // We already have an inactive loader for this ID that we are
-                    // waiting for! Try to cancel; if this returns true then the task is still
-                    // running and we have more work to do.
-                    if (!info.cancel()) {
-                        // The current Loader has not been started or was successfully canceled,
-                        // we thus have no reason to keep it around. Remove it and a new
-                        // LoaderInfo will be created below.
-                        if (DEBUG) Log.v(TAG, "  Current loader is stopped; replacing");
-                        mLoaders.put(id, null);
-                        info.destroy();
-                    } else {
-                        // Now we have three active loaders... we'll queue
-                        // up this request to be processed once one of the other loaders
-                        // finishes.
-                        if (DEBUG) Log.v(TAG,
-                                "  Current loader is running; configuring pending loader");
-                        if (info.mPendingLoader != null) {
-                            if (DEBUG) Log.v(TAG, "  Removing pending loader: " + info.mPendingLoader);
-                            info.mPendingLoader.destroy();
-                            info.mPendingLoader = null;
-                        }
-                        if (DEBUG) Log.v(TAG, "  Enqueuing as new pending loader");
-                        info.mPendingLoader = createLoader(id, args,
-                                (LoaderManager.LoaderCallbacks<Object>)callback);
-                        return (Loader<D>)info.mPendingLoader.mLoader;
-                    }
-                }
-            } else {
-                // Keep track of the previous instance of this loader so we can destroy
-                // it when the new one completes.
-                if (DEBUG) Log.v(TAG, "  Making last loader inactive: " + info);
-                info.mLoader.abandon();
-                mInactiveLoaders.put(id, info);
-            }
-        }
-
-        info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
-        return (Loader<D>)info.mLoader;
-    }
-
-    @MainThread
-    @Override
-    public void destroyLoader(int id) {
-        if (mCreatingLoader) {
-            throw new IllegalStateException("Called while creating a loader");
-        }
-        if (Looper.getMainLooper() != Looper.myLooper()) {
-            throw new IllegalStateException("destroyLoader must be called on the main thread");
-        }
-
-        if (DEBUG) Log.v(TAG, "destroyLoader in " + this + " of " + id);
-        int idx = mLoaders.indexOfKey(id);
-        if (idx >= 0) {
-            LoaderInfo info = mLoaders.valueAt(idx);
-            mLoaders.removeAt(idx);
-            info.destroy();
-        }
-        idx = mInactiveLoaders.indexOfKey(id);
-        if (idx >= 0) {
-            LoaderInfo info = mInactiveLoaders.valueAt(idx);
-            mInactiveLoaders.removeAt(idx);
-            info.destroy();
-        }
-        if (mHost != null && !hasRunningLoaders()) {
-            mHost.mFragmentManager.startPendingDeferredFragments();
-        }
-    }
-
-    @Nullable
-    @Override
-    @SuppressWarnings("unchecked")
-    public <D> Loader<D> getLoader(int id) {
-        if (mCreatingLoader) {
-            throw new IllegalStateException("Called while creating a loader");
-        }
-
-        LoaderInfo loaderInfo = mLoaders.get(id);
-        if (loaderInfo != null) {
-            if (loaderInfo.mPendingLoader != null) {
-                return (Loader<D>)loaderInfo.mPendingLoader.mLoader;
-            }
-            return (Loader<D>)loaderInfo.mLoader;
-        }
-        return null;
-    }
-
-    void doStart() {
-        if (DEBUG) Log.v(TAG, "Starting in " + this);
-        if (mStarted) {
-            RuntimeException e = new RuntimeException("here");
-            e.fillInStackTrace();
-            Log.w(TAG, "Called doStart when already started: " + this, e);
-            return;
-        }
-
-        mStarted = true;
-
-        // Call out to sub classes so they can start their loaders
-        // Let the existing loaders know that we want to be notified when a load is complete
-        for (int i = mLoaders.size()-1; i >= 0; i--) {
-            mLoaders.valueAt(i).start();
-        }
-    }
-
-    void doStop() {
-        if (DEBUG) Log.v(TAG, "Stopping in " + this);
-        if (!mStarted) {
-            RuntimeException e = new RuntimeException("here");
-            e.fillInStackTrace();
-            Log.w(TAG, "Called doStop when not started: " + this, e);
-            return;
-        }
-
-        for (int i = mLoaders.size()-1; i >= 0; i--) {
-            mLoaders.valueAt(i).stop();
-        }
-        mStarted = false;
-    }
-
-    void doRetain() {
-        if (DEBUG) Log.v(TAG, "Retaining in " + this);
-        if (!mStarted) {
-            RuntimeException e = new RuntimeException("here");
-            e.fillInStackTrace();
-            Log.w(TAG, "Called doRetain when not started: " + this, e);
-            return;
-        }
-
-        mRetaining = true;
-        mStarted = false;
-        for (int i = mLoaders.size()-1; i >= 0; i--) {
-            mLoaders.valueAt(i).retain();
-        }
-    }
-
-    void finishRetain() {
-        if (mRetaining) {
-            if (DEBUG) Log.v(TAG, "Finished Retaining in " + this);
-
-            mRetaining = false;
-            for (int i = mLoaders.size()-1; i >= 0; i--) {
-                mLoaders.valueAt(i).finishRetain();
-            }
-        }
-    }
-
-    void doReportNextStart() {
-        for (int i = mLoaders.size()-1; i >= 0; i--) {
-            mLoaders.valueAt(i).mReportNextStart = true;
-        }
-    }
-
-    void doReportStart() {
-        for (int i = mLoaders.size()-1; i >= 0; i--) {
-            mLoaders.valueAt(i).reportStart();
-        }
-    }
-
-    void doDestroy() {
-        if (!mRetaining) {
-            if (DEBUG) Log.v(TAG, "Destroying Active in " + this);
-            for (int i = mLoaders.size()-1; i >= 0; i--) {
-                mLoaders.valueAt(i).destroy();
-            }
-            mLoaders.clear();
-        }
-
-        if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this);
-        for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {
-            mInactiveLoaders.valueAt(i).destroy();
-        }
-        mInactiveLoaders.clear();
-        mHost = null;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("LoaderManager{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
-        sb.append(" in ");
-        DebugUtils.buildShortClassTag(mHost, sb);
-        sb.append("}}");
-        return sb.toString();
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (mLoaders.size() > 0) {
-            writer.print(prefix); writer.println("Active Loaders:");
-            String innerPrefix = prefix + "    ";
-            for (int i=0; i < mLoaders.size(); i++) {
-                LoaderInfo li = mLoaders.valueAt(i);
-                writer.print(prefix); writer.print("  #"); writer.print(mLoaders.keyAt(i));
-                        writer.print(": "); writer.println(li.toString());
-                li.dump(innerPrefix, fd, writer, args);
-            }
-        }
-        if (mInactiveLoaders.size() > 0) {
-            writer.print(prefix); writer.println("Inactive Loaders:");
-            String innerPrefix = prefix + "    ";
-            for (int i=0; i < mInactiveLoaders.size(); i++) {
-                LoaderInfo li = mInactiveLoaders.valueAt(i);
-                writer.print(prefix); writer.print("  #"); writer.print(mInactiveLoaders.keyAt(i));
-                        writer.print(": "); writer.println(li.toString());
-                li.dump(innerPrefix, fd, writer, args);
-            }
-        }
-    }
-
-    @Override
-    public boolean hasRunningLoaders() {
-        boolean loadersRunning = false;
-        final int count = mLoaders.size();
-        for (int i = 0; i < count; i++) {
-            final LoaderInfo li = mLoaders.valueAt(i);
-            loadersRunning |= li.mStarted && !li.mDeliveredData;
-        }
-        return loadersRunning;
-    }
-}
diff --git a/android/support/v4/app/NavUtils.java b/android/support/v4/app/NavUtils.java
deleted file mode 100644
index d259417..0000000
--- a/android/support/v4/app/NavUtils.java
+++ /dev/null
@@ -1,263 +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.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-/**
- * NavUtils provides helper functionality for applications implementing
- * recommended Android UI navigation patterns. For information about recommended
- * navigation patterns see
- * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>
- * from the developer guide and <a href="{@docRoot}design/patterns/navigation.html">Navigation</a>
- * from the design guide.
- */
-public final class NavUtils {
-    private static final String TAG = "NavUtils";
-    public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
-
-    /**
-     * Returns true if sourceActivity should recreate the task when navigating 'up'
-     * by using targetIntent.
-     *
-     * <p>If this method returns false the app can trivially call
-     * {@link #navigateUpTo(Activity, Intent)} using the same parameters to correctly perform
-     * up navigation. If this method returns true, the app should synthesize a new task stack
-     * by using {@link TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
-     *
-     * @param sourceActivity The current activity from which the user is attempting to navigate up
-     * @param targetIntent An intent representing the target destination for up navigation
-     * @return true if navigating up should recreate a new task stack, false if the same task
-     *         should be used for the destination
-     */
-    public static boolean shouldUpRecreateTask(@NonNull Activity sourceActivity,
-            @NonNull Intent 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);
-        }
-    }
-
-    /**
-     * Convenience method that is equivalent to calling
-     * <code>{@link #navigateUpTo(Activity, Intent) navigateUpTo}(sourceActivity,
-     * {@link #getParentActivityIntent(Activity) getParentActivityIntent} (sourceActivity))</code>.
-     * sourceActivity will be finished by this call.
-     *
-     * <p><em>Note:</em> This method should only be used when sourceActivity and the corresponding
-     * parent are within the same task. If up navigation should cross tasks in some cases, see
-     * {@link #shouldUpRecreateTask(Activity, Intent)}.</p>
-     *
-     * @param sourceActivity The current activity from which the user is attempting to navigate up
-     */
-    public static void navigateUpFromSameTask(@NonNull Activity sourceActivity) {
-        Intent upIntent = getParentActivityIntent(sourceActivity);
-
-        if (upIntent == null) {
-            throw new IllegalArgumentException("Activity " +
-                    sourceActivity.getClass().getSimpleName() +
-                    " does not have a parent activity name specified." +
-                    " (Did you forget to add the android.support.PARENT_ACTIVITY <meta-data> " +
-                    " element in your manifest?)");
-        }
-
-        navigateUpTo(sourceActivity, upIntent);
-    }
-
-    /**
-     * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
-     * in the process. upIntent will have the flag {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} set
-     * by this method, along with any others required for proper up navigation as outlined
-     * in the Android Design Guide.
-     *
-     * <p>This method should be used when performing up navigation from within the same task
-     * as the destination. If up navigation should cross tasks in some cases, see
-     * {@link #shouldUpRecreateTask(Activity, Intent)}.</p>
-     *
-     * @param sourceActivity The current activity from which the user is attempting to navigate up
-     * @param upIntent An intent representing the target destination for up navigation
-     */
-    public static void navigateUpTo(@NonNull Activity sourceActivity, @NonNull Intent upIntent) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            sourceActivity.navigateUpTo(upIntent);
-        } else {
-            upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-            sourceActivity.startActivity(upIntent);
-            sourceActivity.finish();
-        }
-    }
-
-    /**
-     * Obtain an {@link Intent} that will launch an explicit target activity
-     * specified by sourceActivity's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
-     * element in the application's manifest. If the device is running
-     * Jellybean or newer, the android:parentActivityName attribute will be preferred
-     * if it is present.
-     *
-     * @param sourceActivity Activity to fetch a parent intent for
-     * @return a new Intent targeting the defined parent activity of sourceActivity
-     */
-    @Nullable
-    public static Intent getParentActivityIntent(@NonNull Activity 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;
-        }
-    }
-
-    /**
-     * Obtain an {@link Intent} that will launch an explicit target activity
-     * specified by sourceActivityClass's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
-     * element in the application's manifest.
-     *
-     * @param context Context for looking up the activity component for sourceActivityClass
-     * @param sourceActivityClass {@link java.lang.Class} object for an Activity class
-     * @return a new Intent targeting the defined parent activity of sourceActivity
-     * @throws NameNotFoundException if the ComponentName for sourceActivityClass is invalid
-     */
-    @Nullable
-    public static Intent getParentActivityIntent(@NonNull Context context,
-            @NonNull Class<?> sourceActivityClass)
-            throws NameNotFoundException {
-        String parentActivity = getParentActivityName(context,
-                new ComponentName(context, sourceActivityClass));
-        if (parentActivity == null) return null;
-
-        // If the parent itself has no parent, generate a main activity intent.
-        final ComponentName target = new ComponentName(context, parentActivity);
-        final String grandparent = getParentActivityName(context, target);
-        final Intent parentIntent = grandparent == null
-                ? Intent.makeMainActivity(target)
-                : new Intent().setComponent(target);
-        return parentIntent;
-    }
-
-    /**
-     * Obtain an {@link Intent} that will launch an explicit target activity
-     * specified by sourceActivityClass's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
-     * element in the application's manifest.
-     *
-     * @param context Context for looking up the activity component for the source activity
-     * @param componentName ComponentName for the source Activity
-     * @return a new Intent targeting the defined parent activity of sourceActivity
-     * @throws NameNotFoundException if the ComponentName for sourceActivityClass is invalid
-     */
-    @Nullable
-    public static Intent getParentActivityIntent(@NonNull Context context,
-            @NonNull ComponentName componentName)
-            throws NameNotFoundException {
-        String parentActivity = getParentActivityName(context, componentName);
-        if (parentActivity == null) return null;
-
-        // If the parent itself has no parent, generate a main activity intent.
-        final ComponentName target = new ComponentName(
-                componentName.getPackageName(), parentActivity);
-        final String grandparent = getParentActivityName(context, target);
-        final Intent parentIntent = grandparent == null
-                ? Intent.makeMainActivity(target)
-                : new Intent().setComponent(target);
-        return parentIntent;
-    }
-
-    /**
-     * Return the fully qualified class name of sourceActivity's parent activity as specified by
-     * a {@link #PARENT_ACTIVITY} &lt;meta-data&gt; element within the activity element in
-     * the application's manifest.
-     *
-     * @param sourceActivity Activity to fetch a parent class name for
-     * @return The fully qualified class name of sourceActivity's parent activity or null if
-     *         it was not specified
-     */
-    @Nullable
-    public static String getParentActivityName(@NonNull Activity sourceActivity) {
-        try {
-            return getParentActivityName(sourceActivity, sourceActivity.getComponentName());
-        } catch (NameNotFoundException e) {
-            // Component name of supplied activity does not exist...?
-            throw new IllegalArgumentException(e);
-        }
-    }
-    /**
-     * Return the fully qualified class name of a source activity's parent activity as specified by
-     * a {@link #PARENT_ACTIVITY} &lt;meta-data&gt; element within the activity element in
-     * the application's manifest. The source activity is provided by componentName.
-     *
-     * @param context Context for looking up the activity component for the source activity
-     * @param componentName ComponentName for the source Activity
-     * @return The fully qualified class name of sourceActivity's parent activity or null if
-     *         it was not specified
-     */
-    @Nullable
-    public static String getParentActivityName(@NonNull Context context,
-            @NonNull ComponentName componentName)
-            throws NameNotFoundException {
-        PackageManager pm = context.getPackageManager();
-        ActivityInfo info = pm.getActivityInfo(componentName, PackageManager.GET_META_DATA);
-        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;
-    }
-
-    /** No instances! */
-    private NavUtils() {
-    }
-}
diff --git a/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java b/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
deleted file mode 100644
index 4421579..0000000
--- a/android/support/v4/app/NotificationBuilderWithBuilderAccessor.java
+++ /dev/null
@@ -1,34 +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.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();
-}
diff --git a/android/support/v4/app/NotificationCompat.java b/android/support/v4/app/NotificationCompat.java
deleted file mode 100644
index 9d71ad1..0000000
--- a/android/support/v4/app/NotificationCompat.java
+++ /dev/null
@@ -1,4991 +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 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.AudioAttributes;
-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.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.SparseArray;
-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}.
- */
-public class NotificationCompat {
-
-    /**
-     * Use all default values (where applicable).
-     */
-    public static final int DEFAULT_ALL = ~0;
-
-    /**
-     * Use the default notification sound. This will ignore any sound set using
-     * {@link Builder#setSound}
-     *
-     * <p>
-     * A notification that is noisy is more likely to be presented as a heads-up notification,
-     * on some platforms.
-     * </p>
-     *
-     * @see Builder#setDefaults
-     */
-    public static final int DEFAULT_SOUND = 1;
-
-    /**
-     * Use the default notification vibrate. This will ignore any vibrate set using
-     * {@link Builder#setVibrate}. Using phone vibration requires the
-     * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
-     *
-     * <p>
-     * A notification that vibrates is more likely to be presented as a heads-up notification,
-     * on some platforms.
-     * </p>
-     *
-     * @see Builder#setDefaults
-     */
-    public static final int DEFAULT_VIBRATE = 2;
-
-    /**
-     * Use the default notification lights. This will ignore the
-     * {@link #FLAG_SHOW_LIGHTS} bit, and values set with {@link Builder#setLights}.
-     *
-     * @see Builder#setDefaults
-     */
-    public static final int DEFAULT_LIGHTS = 4;
-
-    /**
-     * Use this constant as the value for audioStreamType to request that
-     * the default stream type for notifications be used.  Currently the
-     * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
-     */
-    public static final int STREAM_DEFAULT = -1;
-    /**
-     * Bit set in the Notification flags field when LEDs should be turned on
-     * for this notification.
-     */
-    public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
-
-    /**
-     * Bit set in the Notification flags field if this notification is in
-     * reference to something that is ongoing, like a phone call.  It should
-     * not be set if this notification is in reference to something that
-     * happened at a particular point in time, like a missed phone call.
-     */
-    public static final int FLAG_ONGOING_EVENT      = 0x00000002;
-
-    /**
-     * Bit set in the Notification flags field if
-     * the audio will be repeated until the notification is
-     * cancelled or the notification window is opened.
-     */
-    public static final int FLAG_INSISTENT          = 0x00000004;
-
-    /**
-     * Bit set in the Notification flags field if the notification's sound,
-     * vibrate and ticker should only be played if the notification is not already showing.
-     */
-    public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
-
-    /**
-     * Bit set in the Notification flags field if the notification should be canceled when
-     * it is clicked by the user.
-     */
-    public static final int FLAG_AUTO_CANCEL        = 0x00000010;
-
-    /**
-     * Bit set in the Notification flags field if the notification should not be canceled
-     * when the user clicks the Clear all button.
-     */
-    public static final int FLAG_NO_CLEAR           = 0x00000020;
-
-    /**
-     * Bit set in the Notification flags field if this notification represents a currently
-     * running service.  This will normally be set for you by
-     * {@link android.app.Service#startForeground}.
-     */
-    public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
-
-    /**
-     * Obsolete flag indicating high-priority notifications; use the priority field instead.
-     *
-     * @deprecated Use {@link NotificationCompat.Builder#setPriority(int)} with a positive value.
-     */
-    @Deprecated
-    public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
-
-    /**
-     * Bit set in the Notification flags field if this notification is relevant to the current
-     * device only and it is not recommended that it bridge to other devices.
-     */
-    public static final int FLAG_LOCAL_ONLY         = 0x00000100;
-
-    /**
-     * Bit set in the Notification flags field if this notification is the group summary for a
-     * group of notifications. Grouped notifications may display in a cluster or stack on devices
-     * which support such rendering. Requires a group key also be set using
-     * {@link Builder#setGroup}.
-     */
-    public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
-
-    /**
-     * Default notification priority for {@link NotificationCompat.Builder#setPriority(int)}.
-     * If your application does not prioritize its own notifications,
-     * use this value for all notifications.
-     */
-    public static final int PRIORITY_DEFAULT = 0;
-
-    /**
-     * Lower notification priority for {@link NotificationCompat.Builder#setPriority(int)},
-     * for items that are less important. The UI may choose to show
-     * these items smaller, or at a different position in the list,
-     * compared with your app's {@link #PRIORITY_DEFAULT} items.
-     */
-    public static final int PRIORITY_LOW = -1;
-
-    /**
-     * Lowest notification priority for {@link NotificationCompat.Builder#setPriority(int)};
-     * these items might not be shown to the user except under
-     * special circumstances, such as detailed notification logs.
-     */
-    public static final int PRIORITY_MIN = -2;
-
-    /**
-     * Higher notification priority for {@link NotificationCompat.Builder#setPriority(int)},
-     * for more important notifications or alerts. The UI may choose
-     * to show these items larger, or at a different position in
-     * notification lists, compared with your app's {@link #PRIORITY_DEFAULT} items.
-     */
-    public static final int PRIORITY_HIGH = 1;
-
-    /**
-     * Highest notification priority for {@link NotificationCompat.Builder#setPriority(int)},
-     * for your application's most important items that require the user's
-     * prompt attention or input.
-     */
-    public static final int PRIORITY_MAX = 2;
-
-    /**
-     * Notification extras key: this is the title of the notification,
-     * as supplied to {@link Builder#setContentTitle(CharSequence)}.
-     */
-    public static final String EXTRA_TITLE = "android.title";
-
-    /**
-     * Notification extras key: this is the title of the notification when shown in expanded form,
-     * e.g. as supplied to {@link BigTextStyle#setBigContentTitle(CharSequence)}.
-     */
-    public static final String EXTRA_TITLE_BIG = EXTRA_TITLE + ".big";
-
-    /**
-     * Notification extras key: this is the main text payload, as supplied to
-     * {@link Builder#setContentText(CharSequence)}.
-     */
-    public static final String EXTRA_TEXT = "android.text";
-
-    /**
-     * Notification extras key: this is a third line of text, as supplied to
-     * {@link Builder#setSubText(CharSequence)}.
-     */
-    public static final String EXTRA_SUB_TEXT = "android.subText";
-
-    /**
-     * Notification extras key: this is the remote input history, as supplied to
-     * {@link Builder#setRemoteInputHistory(CharSequence[])}.
-     *
-     * Apps can fill this through {@link Builder#setRemoteInputHistory(CharSequence[])}
-     * with the most recent inputs that have been sent through a {@link RemoteInput} of this
-     * Notification and are expected to clear it once the it is no longer relevant (e.g. for chat
-     * notifications once the other party has responded).
-     *
-     * The extra with this key is of type CharSequence[] and contains the most recent entry at
-     * the 0 index, the second most recent at the 1 index, etc.
-     *
-     * @see Builder#setRemoteInputHistory(CharSequence[])
-     */
-    public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
-
-    /**
-     * Notification extras key: this is a small piece of additional text as supplied to
-     * {@link Builder#setContentInfo(CharSequence)}.
-     */
-    public static final String EXTRA_INFO_TEXT = "android.infoText";
-
-    /**
-     * Notification extras key: this is a line of summary information intended to be shown
-     * alongside expanded notifications, as supplied to (e.g.)
-     * {@link BigTextStyle#setSummaryText(CharSequence)}.
-     */
-    public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
-
-    /**
-     * Notification extras key: this is the longer text shown in the big form of a
-     * {@link BigTextStyle} notification, as supplied to
-     * {@link BigTextStyle#bigText(CharSequence)}.
-     */
-    public static final String EXTRA_BIG_TEXT = "android.bigText";
-
-    /**
-     * Notification extras key: this is the resource ID of the notification's main small icon, as
-     * supplied to {@link Builder#setSmallIcon(int)}.
-     */
-    public static final String EXTRA_SMALL_ICON = "android.icon";
-
-    /**
-     * Notification extras key: this is a bitmap to be used instead of the small icon when showing the
-     * notification payload, as
-     * supplied to {@link Builder#setLargeIcon(android.graphics.Bitmap)}.
-     */
-    public static final String EXTRA_LARGE_ICON = "android.largeIcon";
-
-    /**
-     * Notification extras key: this is a bitmap to be used instead of the one from
-     * {@link Builder#setLargeIcon(android.graphics.Bitmap)} when the notification is
-     * shown in its expanded form, as supplied to
-     * {@link BigPictureStyle#bigLargeIcon(android.graphics.Bitmap)}.
-     */
-    public static final String EXTRA_LARGE_ICON_BIG = EXTRA_LARGE_ICON + ".big";
-
-    /**
-     * Notification extras key: this is the progress value supplied to
-     * {@link Builder#setProgress(int, int, boolean)}.
-     */
-    public static final String EXTRA_PROGRESS = "android.progress";
-
-    /**
-     * Notification extras key: this is the maximum value supplied to
-     * {@link Builder#setProgress(int, int, boolean)}.
-     */
-    public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
-
-    /**
-     * Notification extras key: whether the progress bar is indeterminate, supplied to
-     * {@link Builder#setProgress(int, int, boolean)}.
-     */
-    public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
-
-    /**
-     * Notification extras key: whether the when field set using {@link Builder#setWhen} should
-     * be shown as a count-up timer (specifically a {@link android.widget.Chronometer}) instead
-     * of a timestamp, as supplied to {@link Builder#setUsesChronometer(boolean)}.
-     */
-    public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
-
-    /**
-     * Notification extras key: whether the when field set using {@link Builder#setWhen} should
-     * be shown, as supplied to {@link Builder#setShowWhen(boolean)}.
-     */
-    public static final String EXTRA_SHOW_WHEN = "android.showWhen";
-
-    /**
-     * Notification extras key: this is a bitmap to be shown in {@link BigPictureStyle} expanded
-     * notifications, supplied to {@link BigPictureStyle#bigPicture(android.graphics.Bitmap)}.
-     */
-    public static final String EXTRA_PICTURE = "android.picture";
-
-    /**
-     * Notification extras key: An array of CharSequences to show in {@link InboxStyle} expanded
-     * notifications, each of which was supplied to {@link InboxStyle#addLine(CharSequence)}.
-     */
-    public static final String EXTRA_TEXT_LINES = "android.textLines";
-
-    /**
-     * Notification extras key: A string representing the name of the specific
-     * {@link android.app.Notification.Style} used to create this notification.
-     */
-    public static final String EXTRA_TEMPLATE = "android.template";
-
-    /**
-     * Notification extras key: A String array containing the people that this
-     * notification relates to, each of which was supplied to
-     * {@link Builder#addPerson(String)}.
-     */
-    public static final String EXTRA_PEOPLE = "android.people";
-
-    /**
-     * Notification extras key: A
-     * {@link android.content.ContentUris content URI} pointing to an image that can be displayed
-     * in the background when the notification is selected. The URI must point to an image stream
-     * suitable for passing into
-     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
-     * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
-     * URI used for this purpose must require no permissions to read the image data.
-     */
-    public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
-
-    /**
-     * Notification key: A
-     * {@link android.media.session.MediaSession.Token} associated with a
-     * {@link android.app.Notification.MediaStyle} notification.
-     */
-    public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
-
-    /**
-     * Notification extras key: the indices of actions to be shown in the compact view,
-     * as supplied to (e.g.) {@link Notification.MediaStyle#setShowActionsInCompactView(int...)}.
-     */
-    public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
-
-    /**
-     * Notification key: the username to be displayed for all messages sent by the user
-     * including
-     * direct replies
-     * {@link MessagingStyle} notification.
-     */
-    public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
-
-    /**
-     * Notification key: a {@link String} to be displayed as the title to a conversation
-     * represented by a {@link MessagingStyle}
-     */
-    public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
-
-    /**
-     * Notification key: an array of {@link Bundle} objects representing
-     * {@link MessagingStyle.Message} objects for a {@link MessagingStyle} notification.
-     */
-    public static final String EXTRA_MESSAGES = "android.messages";
-
-    /**
-     * Notification key: whether the {@link NotificationCompat.MessagingStyle} notification
-     * represents a group conversation.
-     */
-    public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
-
-    /**
-     * 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
-     * default colors when presenting this notification.
-     */
-    @ColorInt
-    public static final int COLOR_DEFAULT = Color.TRANSPARENT;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({AudioManager.STREAM_VOICE_CALL, AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING,
-            AudioManager.STREAM_MUSIC, AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
-            AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StreamType {}
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(SOURCE)
-    @IntDef({VISIBILITY_PUBLIC, VISIBILITY_PRIVATE, VISIBILITY_SECRET})
-    public @interface NotificationVisibility {}
-    /**
-     * Notification visibility: Show this notification in its entirety on all lockscreens.
-     *
-     * {@see android.app.Notification#visibility}
-     */
-    public static final int VISIBILITY_PUBLIC = Notification.VISIBILITY_PUBLIC;
-
-    /**
-     * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
-     * private information on secure lockscreens.
-     *
-     * {@see android.app.Notification#visibility}
-     */
-    public static final int VISIBILITY_PRIVATE = Notification.VISIBILITY_PRIVATE;
-
-    /**
-     * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
-     *
-     * {@see android.app.Notification#visibility}
-     */
-    public static final int VISIBILITY_SECRET = Notification.VISIBILITY_SECRET;
-
-    /**
-     * Notification category: incoming call (voice or video) or similar synchronous communication request.
-     */
-    public static final String CATEGORY_CALL = Notification.CATEGORY_CALL;
-
-    /**
-     * Notification category: incoming direct message (SMS, instant message, etc.).
-     */
-    public static final String CATEGORY_MESSAGE = Notification.CATEGORY_MESSAGE;
-
-    /**
-     * Notification category: asynchronous bulk message (email).
-     */
-    public static final String CATEGORY_EMAIL = Notification.CATEGORY_EMAIL;
-
-    /**
-     * Notification category: calendar event.
-     */
-    public static final String CATEGORY_EVENT = Notification.CATEGORY_EVENT;
-
-    /**
-     * Notification category: promotion or advertisement.
-     */
-    public static final String CATEGORY_PROMO = Notification.CATEGORY_PROMO;
-
-    /**
-     * Notification category: alarm or timer.
-     */
-    public static final String CATEGORY_ALARM = Notification.CATEGORY_ALARM;
-
-    /**
-     * Notification category: progress of a long-running background operation.
-     */
-    public static final String CATEGORY_PROGRESS = Notification.CATEGORY_PROGRESS;
-
-    /**
-     * Notification category: social network or sharing update.
-     */
-    public static final String CATEGORY_SOCIAL = Notification.CATEGORY_SOCIAL;
-
-    /**
-     * Notification category: error in background operation or authentication status.
-     */
-    public static final String CATEGORY_ERROR = Notification.CATEGORY_ERROR;
-
-    /**
-     * Notification category: media transport control for playback.
-     */
-    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 = Notification.CATEGORY_SYSTEM;
-
-    /**
-     * Notification category: indication of running background service.
-     */
-    public static final String CATEGORY_SERVICE = Notification.CATEGORY_SERVICE;
-
-    /**
-     * Notification category: user-scheduled reminder.
-     */
-    public static final String CATEGORY_REMINDER = Notification.CATEGORY_REMINDER;
-
-    /**
-     * Notification category: a specific, timely recommendation for a single thing.
-     * For example, a news app might want to recommend a news story it believes the user will
-     * want to read next.
-     */
-    public static final String CATEGORY_RECOMMENDATION =
-            Notification.CATEGORY_RECOMMENDATION;
-
-    /**
-     * Notification category: ongoing information about device or contextual 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;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({GROUP_ALERT_ALL, GROUP_ALERT_SUMMARY, GROUP_ALERT_CHILDREN})
-    public @interface GroupAlertBehavior {}
-
-    /**
-     * 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 = Notification.GROUP_ALERT_ALL;
-
-    /**
-     * 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. This must be applied to all children notifications you want
-     * to mute.
-     *
-     * <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 = Notification.GROUP_ALERT_SUMMARY;
-
-    /**
-     * 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
-     * rather than to alert the user that new information is available.
-     */
-    public static final int GROUP_ALERT_CHILDREN = Notification.GROUP_ALERT_CHILDREN;
-
-    /**
-     * Builder class for {@link NotificationCompat} objects.  Allows easier control over
-     * all the flags, as well as help constructing the typical notification layouts.
-     * <p>
-     * On platform versions that don't offer expanded notifications, methods that depend on
-     * expanded notifications have no effect.
-     * </p>
-     * <p>
-     * For example, action buttons won't appear on platforms prior to Android 4.1. Action
-     * buttons depend on expanded notifications, which are only available in Android 4.1
-     * and later.
-     * <p>
-     * For this reason, you should always ensure that UI controls in a notification are also
-     * available in an {@link android.app.Activity} in your app, and you should always start that
-     * {@link android.app.Activity} when users click the notification. To do this, use the
-     * {@link NotificationCompat.Builder#setContentIntent setContentIntent()}
-     * method.
-     * </p>
-     *
-     */
-    public static class Builder {
-        /**
-         * Maximum length of CharSequences accepted by Builder and friends.
-         *
-         * <p>
-         * Avoids spamming the system with overly large strings such as full e-mails.
-         */
-        private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
-
-        // All these variables are declared public/hidden so they can be accessed by a builder
-        // extender.
-
-        /** @hide */
-        @RestrictTo(LIBRARY_GROUP)
-        public Context mContext;
-
-        /** @hide */
-        @RestrictTo(LIBRARY_GROUP)
-        public ArrayList<Action> mActions = new ArrayList<>();
-
-        CharSequence mContentTitle;
-        CharSequence mContentText;
-        PendingIntent mContentIntent;
-        PendingIntent mFullScreenIntent;
-        RemoteViews mTickerView;
-        Bitmap mLargeIcon;
-        CharSequence mContentInfo;
-        int mNumber;
-        int mPriority;
-        boolean mShowWhen = true;
-        boolean mUseChronometer;
-        Style mStyle;
-        CharSequence mSubText;
-        CharSequence[] mRemoteInputHistory;
-        int mProgressMax;
-        int mProgress;
-        boolean mProgressIndeterminate;
-        String mGroupKey;
-        boolean mGroupSummary;
-        String mSortKey;
-        boolean mLocalOnly = false;
-        boolean mColorized;
-        boolean mColorizedSet;
-        String mCategory;
-        Bundle mExtras;
-        int mColor = COLOR_DEFAULT;
-        @NotificationVisibility int mVisibility = VISIBILITY_PRIVATE;
-        Notification mPublicVersion;
-        RemoteViews mContentView;
-        RemoteViews mBigContentView;
-        RemoteViews mHeadsUpContentView;
-        String mChannelId;
-        int mBadgeIcon = BADGE_ICON_NONE;
-        String mShortcutId;
-        long mTimeout;
-        @GroupAlertBehavior int mGroupAlertBehavior = GROUP_ALERT_ALL;
-        Notification mNotification = new Notification();
-
-        /**
-         * @deprecated This field was not meant to be public.
-         */
-        @Deprecated
-        public ArrayList<String> mPeople;
-
-        /**
-         * Constructor.
-         *
-         * Automatically sets the when field to {@link System#currentTimeMillis()
-         * System.currentTimeMillis()} and the audio stream to the
-         * {@link Notification#STREAM_DEFAULT}.
-         *
-         * @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(@NonNull Context context, @NonNull String channelId) {
-            mContext = context;
-            mChannelId = channelId;
-
-            // Set defaults to match the defaults of a Notification
-            mNotification.when = System.currentTimeMillis();
-            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
-            mPriority = PRIORITY_DEFAULT;
-            mPeople = new ArrayList<String>();
-        }
-
-        /**
-         * @deprecated use {@link #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.
-         */
-        public Builder setWhen(long when) {
-            mNotification.when = when;
-            return this;
-        }
-
-        /**
-         * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
-         * in the content view.
-         */
-        public Builder setShowWhen(boolean show) {
-            mShowWhen = show;
-            return this;
-        }
-
-        /**
-         * Show the {@link Notification#when} field as a stopwatch.
-         *
-         * Instead of presenting <code>when</code> as a timestamp, the notification will show an
-         * automatically updating display of the minutes and seconds since <code>when</code>.
-         *
-         * Useful when showing an elapsed time (like an ongoing phone call).
-         *
-         * @see android.widget.Chronometer
-         * @see Notification#when
-         */
-        public Builder setUsesChronometer(boolean b) {
-            mUseChronometer = b;
-            return this;
-        }
-
-        /**
-         * Set the small icon to use in the notification layouts.  Different classes of devices
-         * 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 drawable to use.
-         */
-        public Builder setSmallIcon(int icon) {
-            mNotification.icon = icon;
-            return this;
-        }
-
-        /**
-         * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
-         * 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 drawable to use.
-         * @param level The level to use for the icon.
-         *
-         * @see android.graphics.drawable.LevelListDrawable
-         */
-        public Builder setSmallIcon(int icon, int level) {
-            mNotification.icon = icon;
-            mNotification.iconLevel = level;
-            return this;
-        }
-
-        /**
-         * Set the title (first row) of the notification, in a standard notification.
-         */
-        public Builder setContentTitle(CharSequence title) {
-            mContentTitle = limitCharSequenceLength(title);
-            return this;
-        }
-
-        /**
-         * Set the text (second row) of the notification, in a standard notification.
-         */
-        public Builder setContentText(CharSequence text) {
-            mContentText = limitCharSequenceLength(text);
-            return this;
-        }
-
-        /**
-         * Set the third line of text in the platform notification template.
-         * Don't use if you're also using {@link #setProgress(int, int, boolean)};
-         * they occupy the same location in the standard template.
-         * <br>
-         * If the platform does not provide large-format notifications, this method has no effect.
-         * The third line of text only appears in expanded view.
-         * <br>
-         */
-        public Builder setSubText(CharSequence text) {
-            mSubText = limitCharSequenceLength(text);
-            return this;
-        }
-
-        /**
-         * Set the remote input history.
-         *
-         * This should be set to the most recent inputs that have been sent
-         * through a {@link RemoteInput} of this Notification and cleared once the it is no
-         * longer relevant (e.g. for chat notifications once the other party has responded).
-         *
-         * The most recent input must be stored at the 0 index, the second most recent at the
-         * 1 index, etc. Note that the system will limit both how far back the inputs will be shown
-         * and how much of each individual input is shown.
-         *
-         * <p>Note: The reply text will only be shown on notifications that have least one action
-         * with a {@code RemoteInput}.</p>
-         */
-        public Builder setRemoteInputHistory(CharSequence[] text) {
-            mRemoteInputHistory = text;
-            return this;
-        }
-
-        /**
-         * Set the large number at the right-hand side of the notification.  This is
-         * equivalent to setContentInfo, although it might show the number in a different
-         * font size for readability.
-         */
-        public Builder setNumber(int number) {
-            mNumber = number;
-            return this;
-        }
-
-        /**
-         * Set the large text at the right-hand side of the notification.
-         */
-        public Builder setContentInfo(CharSequence info) {
-            mContentInfo = limitCharSequenceLength(info);
-            return this;
-        }
-
-        /**
-         * Set the progress this notification represents, which may be
-         * represented as a {@link android.widget.ProgressBar}.
-         */
-        public Builder setProgress(int max, int progress, boolean indeterminate) {
-            mProgressMax = max;
-            mProgress = progress;
-            mProgressIndeterminate = indeterminate;
-            return this;
-        }
-
-        /**
-         * Supply a custom RemoteViews to use instead of the standard one.
-         */
-        public Builder setContent(RemoteViews views) {
-            mNotification.contentView = views;
-            return this;
-        }
-
-        /**
-         * Supply a {@link PendingIntent} to send when the notification is clicked.
-         * If you do not supply an intent, you can now add PendingIntents to individual
-         * views to be launched when clicked by calling {@link RemoteViews#setOnClickPendingIntent
-         * RemoteViews.setOnClickPendingIntent(int,PendingIntent)}.  Be sure to
-         * read {@link Notification#contentIntent Notification.contentIntent} for
-         * how to correctly use this.
-         */
-        public Builder setContentIntent(PendingIntent intent) {
-            mContentIntent = intent;
-            return this;
-        }
-
-        /**
-         * Supply a {@link PendingIntent} to send when the notification is cleared by the user
-         * directly from the notification panel.  For example, this intent is sent when the user
-         * clicks the "Clear all" button, or the individual "X" buttons on notifications.  This
-         * intent is not sent when the application calls
-         * {@link android.app.NotificationManager#cancel NotificationManager.cancel(int)}.
-         */
-        public Builder setDeleteIntent(PendingIntent intent) {
-            mNotification.deleteIntent = intent;
-            return this;
-        }
-
-        /**
-         * An intent to launch instead of posting the notification to the status bar.
-         * Only for use with extremely high-priority notifications demanding the user's
-         * <strong>immediate</strong> attention, such as an incoming phone call or
-         * alarm clock that the user has explicitly set to a particular time.
-         * If this facility is used for something else, please give the user an option
-         * to turn it off and use a normal notification, as this can be extremely
-         * disruptive.
-         *
-         * <p>
-         * On some platforms, the system UI may choose to display a heads-up notification,
-         * instead of launching this intent, while the user is using the device.
-         * </p>
-         *
-         * @param intent The pending intent to launch.
-         * @param highPriority Passing true will cause this notification to be sent
-         *          even if other notifications are suppressed.
-         */
-        public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
-            mFullScreenIntent = intent;
-            setFlag(FLAG_HIGH_PRIORITY, highPriority);
-            return this;
-        }
-
-        /**
-         * Sets the "ticker" text which is sent to accessibility services. Prior to
-         * {@link Build.VERSION_CODES#LOLLIPOP}, sets the text that is displayed in the status bar
-         * when the notification first arrives.
-         */
-        public Builder setTicker(CharSequence tickerText) {
-            mNotification.tickerText = limitCharSequenceLength(tickerText);
-            return this;
-        }
-
-        /**
-         * Sets the "ticker" text which is sent to accessibility services. Prior to
-         * {@link Build.VERSION_CODES#LOLLIPOP}, sets the text that is displayed in the status bar
-         * when the notification first arrives, and also a RemoteViews object that may be displayed
-         * instead on some devices.
-         */
-        public Builder setTicker(CharSequence tickerText, RemoteViews views) {
-            mNotification.tickerText = limitCharSequenceLength(tickerText);
-            mTickerView = views;
-            return this;
-        }
-
-        /**
-         * Set the large icon that is shown in the ticker and notification.
-         */
-        public Builder setLargeIcon(Bitmap icon) {
-            mLargeIcon = icon;
-            return this;
-        }
-
-        /**
-         * Set the sound to play.  It will play on the default stream.
-         *
-         * <p>
-         * On some platforms, a notification that is noisy is more likely to be presented
-         * as a heads-up notification.
-         * </p>
-         */
-        public Builder setSound(Uri sound) {
-            mNotification.sound = sound;
-            mNotification.audioStreamType = Notification.STREAM_DEFAULT;
-            if (Build.VERSION.SDK_INT >= 21) {
-                mNotification.audioAttributes = new AudioAttributes.Builder()
-                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-                        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
-                        .build();
-            }
-            return this;
-        }
-
-        /**
-         * Set the sound to play.  It will play on the stream you supply.
-         *
-         * <p>
-         * On some platforms, a notification that is noisy is more likely to be presented
-         * as a heads-up notification.
-         * </p>
-         *
-         * @see Notification#STREAM_DEFAULT
-         * @see AudioManager for the <code>STREAM_</code> constants.
-         */
-        public Builder setSound(Uri sound, @StreamType int streamType) {
-            mNotification.sound = sound;
-            mNotification.audioStreamType = streamType;
-            if (Build.VERSION.SDK_INT >= 21) {
-                mNotification.audioAttributes = new AudioAttributes.Builder()
-                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-                        .setLegacyStreamType(streamType)
-                        .build();
-            }
-            return this;
-        }
-
-        /**
-         * Set the vibration pattern to use.
-         *
-         * <p>
-         * On some platforms, a notification that vibrates is more likely to be presented
-         * as a heads-up notification.
-         * </p>
-         *
-         * @see android.os.Vibrator for a discussion of the <code>pattern</code>
-         * parameter.
-         */
-        public Builder setVibrate(long[] pattern) {
-            mNotification.vibrate = pattern;
-            return this;
-        }
-
-        /**
-         * Set the argb value that you would like the LED on the device to blink, as well as the
-         * rate.  The rate is specified in terms of the number of milliseconds to be on
-         * and then the number of milliseconds to be off.
-         */
-        public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
-            mNotification.ledARGB = argb;
-            mNotification.ledOnMS = onMs;
-            mNotification.ledOffMS = offMs;
-            boolean showLights = mNotification.ledOnMS != 0 && mNotification.ledOffMS != 0;
-            mNotification.flags = (mNotification.flags & ~Notification.FLAG_SHOW_LIGHTS) |
-                    (showLights ? Notification.FLAG_SHOW_LIGHTS : 0);
-            return this;
-        }
-
-        /**
-         * Set whether this is an ongoing notification.
-         *
-         * <p>Ongoing notifications differ from regular notifications in the following ways:
-         * <ul>
-         *   <li>Ongoing notifications are sorted above the regular notifications in the
-         *   notification panel.</li>
-         *   <li>Ongoing notifications do not have an 'X' close button, and are not affected
-         *   by the "Clear all" button.
-         * </ul>
-         */
-        public Builder setOngoing(boolean ongoing) {
-            setFlag(Notification.FLAG_ONGOING_EVENT, ongoing);
-            return this;
-        }
-
-        /**
-         * 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.
-         */
-        public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
-            setFlag(Notification.FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
-            return this;
-        }
-
-        /**
-         * Setting this flag will make it so the notification is automatically
-         * canceled when the user clicks it in the panel.  The PendingIntent
-         * set with {@link #setDeleteIntent} will be broadcast when the notification
-         * is canceled.
-         */
-        public Builder setAutoCancel(boolean autoCancel) {
-            setFlag(Notification.FLAG_AUTO_CANCEL, autoCancel);
-            return this;
-        }
-
-        /**
-         * Set whether or not this notification is only relevant to the current device.
-         *
-         * <p>Some notifications can be bridged to other devices for remote display.
-         * This hint can be set to recommend this notification not be bridged.
-         */
-        public Builder setLocalOnly(boolean b) {
-            mLocalOnly = b;
-            return this;
-        }
-
-        /**
-         * Set the notification category.
-         *
-         * <p>Must be one of the predefined notification categories (see the <code>CATEGORY_*</code>
-         * constants in {@link Notification}) that best describes this notification.
-         * May be used by the system for ranking and filtering.
-         */
-        public Builder setCategory(String category) {
-            mCategory = category;
-            return this;
-        }
-
-        /**
-         * Set the default notification options that will be used.
-         * <p>
-         * The value should be one or more of the following fields combined with
-         * bitwise-or:
-         * {@link Notification#DEFAULT_SOUND}, {@link Notification#DEFAULT_VIBRATE},
-         * {@link Notification#DEFAULT_LIGHTS}.
-         * <p>
-         * For all default values, use {@link Notification#DEFAULT_ALL}.
-         */
-        public Builder setDefaults(int defaults) {
-            mNotification.defaults = defaults;
-            if ((defaults & Notification.DEFAULT_LIGHTS) != 0) {
-                mNotification.flags |= Notification.FLAG_SHOW_LIGHTS;
-            }
-            return this;
-        }
-
-        private void setFlag(int mask, boolean value) {
-            if (value) {
-                mNotification.flags |= mask;
-            } else {
-                mNotification.flags &= ~mask;
-            }
-        }
-
-        /**
-         * Set the relative priority for this notification.
-         *
-         * Priority is an indication of how much of the user's
-         * valuable attention should be consumed by this
-         * notification. Low-priority notifications may be hidden from
-         * the user in certain situations, while the user might be
-         * interrupted for a higher-priority notification.
-         * The system sets a notification's priority based on various factors including the
-         * setPriority value. The effect may differ slightly on different platforms.
-         *
-         * @param pri Relative priority for this notification. Must be one of
-         *     the priority constants defined by {@link NotificationCompat}.
-         *     Acceptable values range from {@link
-         *     NotificationCompat#PRIORITY_MIN} (-2) to {@link
-         *     NotificationCompat#PRIORITY_MAX} (2).
-         */
-        public Builder setPriority(int pri) {
-            mPriority = pri;
-            return this;
-        }
-
-        /**
-         * Add a person that is relevant to this notification.
-         *
-         * <P>
-         * Depending on user preferences, this annotation may allow the notification to pass
-         * through interruption filters, and to appear more prominently in the user interface.
-         * </P>
-         *
-         * <P>
-         * The person should be specified by the {@code String} representation of a
-         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
-         * </P>
-         *
-         * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
-         * URIs.  The path part of these URIs must exist in the contacts database, in the
-         * appropriate column, or the reference will be discarded as invalid. Telephone schema
-         * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
-         * </P>
-         *
-         * @param uri A URI for the person.
-         * @see Notification#EXTRA_PEOPLE
-         */
-        public Builder addPerson(String uri) {
-            mPeople.add(uri);
-            return this;
-        }
-
-        /**
-         * Set this notification to be part of a group of notifications sharing the same key.
-         * Grouped notifications may display in a cluster or stack on devices which
-         * support such rendering.
-         *
-         * <p>To make this notification the summary for its group, also call
-         * {@link #setGroupSummary}. A sort order can be specified for group members by using
-         * {@link #setSortKey}.
-         * @param groupKey The group key of the group.
-         * @return this object for method chaining
-         */
-        public Builder setGroup(String groupKey) {
-            mGroupKey = groupKey;
-            return this;
-        }
-
-        /**
-         * Set this notification to be the group summary for a group of notifications.
-         * Grouped notifications may display in a cluster or stack on devices which
-         * support such rendering. Requires a group key also be set using {@link #setGroup}.
-         * @param isGroupSummary Whether this notification should be a group summary.
-         * @return this object for method chaining
-         */
-        public Builder setGroupSummary(boolean isGroupSummary) {
-            mGroupSummary = isGroupSummary;
-            return this;
-        }
-
-        /**
-         * Set a sort key that orders this notification among other notifications from the
-         * same package. This can be useful if an external sort was already applied and an app
-         * would like to preserve this. Notifications will be sorted lexicographically using this
-         * value, although providing different priorities in addition to providing sort key may
-         * cause this value to be ignored.
-         *
-         * <p>This sort key can also be used to order members of a notification group. See
-         * {@link Builder#setGroup}.
-         *
-         * @see String#compareTo(String)
-         */
-        public Builder setSortKey(String sortKey) {
-            mSortKey = sortKey;
-            return this;
-        }
-
-        /**
-         * Merge additional metadata into this notification.
-         *
-         * <p>Values within the Bundle will replace existing extras values in this Builder.
-         *
-         * @see Notification#extras
-         */
-        public Builder addExtras(Bundle extras) {
-            if (extras != null) {
-                if (mExtras == null) {
-                    mExtras = new Bundle(extras);
-                } else {
-                    mExtras.putAll(extras);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Set metadata for this notification.
-         *
-         * <p>A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
-         * current contents are copied into the Notification each time {@link #build()} is
-         * called.
-         *
-         * <p>Replaces any existing extras values with those from the provided Bundle.
-         * Use {@link #addExtras} to merge in metadata instead.
-         *
-         * @see Notification#extras
-         */
-        public Builder setExtras(Bundle extras) {
-            mExtras = extras;
-            return this;
-        }
-
-        /**
-         * Get the current metadata Bundle used by this notification Builder.
-         *
-         * <p>The returned Bundle is shared with this Builder.
-         *
-         * <p>The current contents of this Bundle are copied into the Notification each time
-         * {@link #build()} is called.
-         *
-         * @see Notification#extras
-         */
-        public Bundle getExtras() {
-            if (mExtras == null) {
-                mExtras = new Bundle();
-            }
-            return mExtras;
-        }
-
-        /**
-         * Add an action to this notification. Actions are typically displayed by
-         * the system as a button adjacent to the notification content.
-         * <br>
-         * Action buttons won't appear on platforms prior to Android 4.1. Action
-         * buttons depend on expanded notifications, which are only available in Android 4.1
-         * and later. To ensure that an action button's functionality is always available, first
-         * implement the functionality in the {@link android.app.Activity} that starts when a user
-         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
-         * enhance the notification by implementing the same functionality with
-         * {@link #addAction addAction()}.
-         *
-         * @param icon Resource ID of a drawable that represents the action.
-         * @param title Text describing the action.
-         * @param intent {@link android.app.PendingIntent} to be fired when the action is invoked.
-         */
-        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
-            mActions.add(new Action(icon, title, intent));
-            return this;
-        }
-
-        /**
-         * Add an action to this notification. Actions are typically displayed by
-         * the system as a button adjacent to the notification content.
-         * <br>
-         * Action buttons won't appear on platforms prior to Android 4.1. Action
-         * buttons depend on expanded notifications, which are only available in Android 4.1
-         * and later. To ensure that an action button's functionality is always available, first
-         * implement the functionality in the {@link android.app.Activity} that starts when a user
-         * clicks the  notification (see {@link #setContentIntent setContentIntent()}), and then
-         * enhance the notification by implementing the same functionality with
-         * {@link #addAction addAction()}.
-         *
-         * @param action The action to add.
-         */
-        public Builder addAction(Action action) {
-            mActions.add(action);
-            return this;
-        }
-
-        /**
-         * Add a rich notification style to be applied at build time.
-         * <br>
-         * If the platform does not provide rich notification styles, this method has no effect. The
-         * user will always see the normal notification style.
-         *
-         * @param style Object responsible for modifying the notification style.
-         */
-        public Builder setStyle(Style style) {
-            if (mStyle != style) {
-                mStyle = style;
-                if (mStyle != null) {
-                    mStyle.setBuilder(this);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Sets {@link Notification#color}.
-         *
-         * @param argb The accent color to use
-         *
-         * @return The same Builder.
-         */
-        public Builder setColor(@ColorInt int argb) {
-            mColor = argb;
-            return this;
-        }
-
-        /**
-         * Sets {@link Notification#visibility}.
-         *
-         * @param visibility One of {@link Notification#VISIBILITY_PRIVATE} (the default),
-         *                   {@link Notification#VISIBILITY_PUBLIC}, or
-         *                   {@link Notification#VISIBILITY_SECRET}.
-         */
-        public Builder setVisibility(@NotificationVisibility int visibility) {
-            mVisibility = visibility;
-            return this;
-        }
-
-        /**
-         * Supply a replacement Notification whose contents should be shown in insecure contexts
-         * (i.e. atop the secure lockscreen). See {@link Notification#visibility} and
-         * {@link #VISIBILITY_PUBLIC}.
-         *
-         * @param n A replacement notification, presumably with some or all info redacted.
-         * @return The same Builder.
-         */
-        public Builder setPublicVersion(Notification n) {
-            mPublicVersion = n;
-            return this;
-        }
-
-        /**
-         * Supply custom RemoteViews to use instead of the platform template.
-         *
-         * This will override the layout that would otherwise be constructed by this Builder
-         * object.
-         */
-        public Builder setCustomContentView(RemoteViews contentView) {
-            mContentView = contentView;
-            return this;
-        }
-
-        /**
-         * Supply custom RemoteViews to use instead of the platform template in the expanded form.
-         *
-         * This will override the expanded layout that would otherwise be constructed by this
-         * Builder object.
-         *
-         * No-op on versions prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.
-         */
-        public Builder setCustomBigContentView(RemoteViews contentView) {
-            mBigContentView = contentView;
-            return this;
-        }
-
-        /**
-         * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
-         *
-         * This will override the heads-up layout that would otherwise be constructed by this
-         * Builder object.
-         *
-         * No-op on versions prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
-         */
-        public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
-            mHeadsUpContentView = contentView;
-            return this;
-        }
-
-        /**
-         * 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}. This must be called on all notifications you want to
-         * mute. For example, if you want only the summary of your group to make noise, all
-         * children in the group should have the group alert behavior {@link #GROUP_ALERT_SUMMARY}.
-         *
-         * <p> The default value is {@link #GROUP_ALERT_ALL}.</p>
-         */
-        public Builder setGroupAlertBehavior(@GroupAlertBehavior 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.
-         */
-        public Builder extend(Extender extender) {
-            extender.extend(this);
-            return this;
-        }
-
-        /**
-         * @deprecated Use {@link #build()} instead.
-         */
-        @Deprecated
-        public Notification getNotification() {
-            return build();
-        }
-
-        /**
-         * Combine all of the options that have been set and return a new {@link Notification}
-         * object.
-         */
-        public Notification build() {
-            return new NotificationCompatBuilder(this).build();
-        }
-
-        protected static CharSequence limitCharSequenceLength(CharSequence cs) {
-            if (cs == null) return cs;
-            if (cs.length() > MAX_CHARSEQUENCE_LENGTH) {
-                cs = cs.subSequence(0, MAX_CHARSEQUENCE_LENGTH);
-            }
-            return cs;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public RemoteViews getContentView() {
-            return mContentView;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public RemoteViews getBigContentView() {
-            return mBigContentView;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public RemoteViews getHeadsUpContentView() {
-            return mHeadsUpContentView;
-        }
-
-        /**
-         * return when if it is showing or 0 otherwise
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public long getWhenIfShowing() {
-            return mShowWhen ? mNotification.when : 0;
-        }
-
-        /**
-         * @return the priority set on the notification
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public int getPriority() {
-            return mPriority;
-        }
-
-        /**
-         * @return the color of the notification
-         *
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public int getColor() {
-            return mColor;
-        }
-    }
-
-    /**
-     * An object that can apply a rich notification style to a {@link Notification.Builder}
-     * object.
-     * <br>
-     * If the platform does not provide rich notification styles, methods in this class have no
-     * effect.
-     */
-    public static abstract class Style {
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        protected Builder mBuilder;
-        CharSequence mBigContentTitle;
-        CharSequence mSummaryText;
-        boolean mSummaryTextSet = false;
-
-        public void setBuilder(Builder builder) {
-            if (mBuilder != builder) {
-                mBuilder = builder;
-                if (mBuilder != null) {
-                    mBuilder.setStyle(this);
-                }
-            }
-        }
-
-        public Notification build() {
-            Notification notification = null;
-            if (mBuilder != null) {
-                notification = mBuilder.build();
-            }
-            return notification;
-        }
-
-        /**
-         * @hide
-         */
-        @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) {
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        // 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);
-        }
-    }
-
-    /**
-     * Helper class for generating large-format notifications that include a large image attachment.
-     * <br>
-     * If the platform does not provide large-format notifications, this method has no effect. The
-     * user will always see the normal notification view.
-     * <br>
-     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
-     * <pre class="prettyprint">
-     * Notification notification = new Notification.Builder(mContext)
-     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
-     *     .setContentText(subject)
-     *     .setSmallIcon(R.drawable.new_post)
-     *     .setLargeIcon(aBitmap)
-     *     .setStyle(new Notification.BigPictureStyle()
-     *         .bigPicture(aBigBitmap))
-     *     .build();
-     * </pre>
-     *
-     * @see Notification#bigContentView
-     */
-    public static class BigPictureStyle extends Style {
-        private Bitmap mPicture;
-        private Bitmap mBigLargeIcon;
-        private boolean mBigLargeIconSet;
-
-        public BigPictureStyle() {
-        }
-
-        public BigPictureStyle(Builder builder) {
-            setBuilder(builder);
-        }
-
-        /**
-         * Overrides ContentTitle in the big form of the template.
-         * This defaults to the value passed to setContentTitle().
-         */
-        public BigPictureStyle setBigContentTitle(CharSequence title) {
-            mBigContentTitle = Builder.limitCharSequenceLength(title);
-            return this;
-        }
-
-        /**
-         * Set the first line of text after the detail section in the big form of the template.
-         */
-        public BigPictureStyle setSummaryText(CharSequence cs) {
-            mSummaryText = Builder.limitCharSequenceLength(cs);
-            mSummaryTextSet = true;
-            return this;
-        }
-
-        /**
-         * Provide the bitmap to be used as the payload for the BigPicture notification.
-         */
-        public BigPictureStyle bigPicture(Bitmap b) {
-            mPicture = b;
-            return this;
-        }
-
-        /**
-         * Override the large icon when the big notification is shown.
-         */
-        public BigPictureStyle bigLargeIcon(Bitmap b) {
-            mBigLargeIcon = b;
-            mBigLargeIconSet = true;
-            return this;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        public void apply(NotificationBuilderWithBuilderAccessor builder) {
-            if (Build.VERSION.SDK_INT >= 16) {
-                Notification.BigPictureStyle style =
-                        new Notification.BigPictureStyle(builder.getBuilder())
-                                .setBigContentTitle(mBigContentTitle)
-                                .bigPicture(mPicture);
-                if (mBigLargeIconSet) {
-                    style.bigLargeIcon(mBigLargeIcon);
-                }
-                if (mSummaryTextSet) {
-                    style.setSummaryText(mSummaryText);
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper class for generating large-format notifications that include a lot of text.
-     *
-     * <br>
-     * If the platform does not provide large-format notifications, this method has no effect. The
-     * user will always see the normal notification view.
-     * <br>
-     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
-     * <pre class="prettyprint">
-     * Notification notification = new Notification.Builder(mContext)
-     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
-     *     .setContentText(subject)
-     *     .setSmallIcon(R.drawable.new_mail)
-     *     .setLargeIcon(aBitmap)
-     *     .setStyle(new Notification.BigTextStyle()
-     *         .bigText(aVeryLongString))
-     *     .build();
-     * </pre>
-     *
-     * @see Notification#bigContentView
-     */
-    public static class BigTextStyle extends Style {
-        private CharSequence mBigText;
-
-        public BigTextStyle() {
-        }
-
-        public BigTextStyle(Builder builder) {
-            setBuilder(builder);
-        }
-
-        /**
-         * Overrides ContentTitle in the big form of the template.
-         * This defaults to the value passed to setContentTitle().
-         */
-        public BigTextStyle setBigContentTitle(CharSequence title) {
-            mBigContentTitle = Builder.limitCharSequenceLength(title);
-            return this;
-        }
-
-        /**
-         * Set the first line of text after the detail section in the big form of the template.
-         */
-        public BigTextStyle setSummaryText(CharSequence cs) {
-            mSummaryText = Builder.limitCharSequenceLength(cs);
-            mSummaryTextSet = true;
-            return this;
-        }
-
-        /**
-         * Provide the longer text to be displayed in the big form of the
-         * template in place of the content text.
-         */
-        public BigTextStyle bigText(CharSequence cs) {
-            mBigText = Builder.limitCharSequenceLength(cs);
-            return this;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        public void apply(NotificationBuilderWithBuilderAccessor builder) {
-            if (Build.VERSION.SDK_INT >= 16) {
-                Notification.BigTextStyle style =
-                        new Notification.BigTextStyle(builder.getBuilder())
-                                .setBigContentTitle(mBigContentTitle)
-                                .bigText(mBigText);
-                if (mSummaryTextSet) {
-                    style.setSummaryText(mSummaryText);
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper class for generating large-format notifications that include multiple back-and-forth
-     * messages of varying types between any number of people.
-     *
-     * <br>
-     * In order to get a backwards compatible behavior, the app needs to use the v7 version of the
-     * notification builder together with this style, otherwise the user will see the normal
-     * notification view.
-     *
-     * <br>
-     * Use {@link MessagingStyle#setConversationTitle(CharSequence)} to set a conversation title for
-     * group chats with more than two people. This could be the user-created name of the group or,
-     * if it doesn't have a specific name, a list of the participants in the conversation. Do not
-     * set a conversation title for one-on-one chats, since platforms use the existence of this
-     * field as a hint that the conversation is a group.
-     *
-     * <br>
-     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
-     * so:
-     * <pre class="prettyprint">
-     *
-     * Notification notification = new Notification.Builder()
-     *     .setContentTitle(&quot;2 new messages with &quot; + sender.toString())
-     *     .setContentText(subject)
-     *     .setSmallIcon(R.drawable.new_message)
-     *     .setLargeIcon(aBitmap)
-     *     .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
-     *         .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
-     *         .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
-     *     .build();
-     * </pre>
-     */
-    public static class MessagingStyle extends Style {
-
-        /**
-         * The maximum number of messages that will be retained in the Notification itself (the
-         * number displayed is up to the platform).
-         */
-        public static final int MAXIMUM_RETAINED_MESSAGES = 25;
-
-        CharSequence mUserDisplayName;
-        @Nullable CharSequence mConversationTitle;
-        List<Message> mMessages = new ArrayList<>();
-        boolean mIsGroupConversation;
-
-        MessagingStyle() {
-        }
-
-        /**
-         * @param userDisplayName Required - the name to be displayed for any replies sent by the
-         * user before the posting app reposts the notification with those messages after they've
-         * been actually sent and in previous messages sent by the user added in
-         * {@link #addMessage(Message)}
-         */
-        public MessagingStyle(@NonNull CharSequence userDisplayName) {
-            mUserDisplayName = userDisplayName;
-        }
-
-        /**
-         * Returns the name to be displayed for any replies sent by the user
-         */
-        public CharSequence getUserDisplayName() {
-            return mUserDisplayName;
-        }
-
-        /**
-         * Sets the title to be displayed on this conversation. May be set to {@code null}.
-         *
-         * <p>This API's behavior was changed in SDK version {@link Build.VERSION_CODES#P}. If your
-         * application's target version is less than {@link Build.VERSION_CODES#P}, setting a
-         * conversation title to a non-null value will make {@link #isGroupConversation()} return
-         * {@code true} and passing {@code null} will make it return {@code false}. In
-         * {@link Build.VERSION_CODES#P} and beyond, use {@link #setGroupConversation(boolean)}
-         * to set group conversation status.
-         *
-         * @param conversationTitle Title displayed for this conversation
-         * @return this object for method chaining
-         */
-        public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
-            mConversationTitle = conversationTitle;
-            return this;
-        }
-
-        /**
-         * Return the title to be displayed on this conversation. Can be {@code null}.
-         */
-        @Nullable
-        public CharSequence getConversationTitle() {
-            return mConversationTitle;
-        }
-
-        /**
-         * 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 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()}.
-         * Should be unique amongst all individuals in the conversation, and should be
-         * consistent during re-posts of the notification.
-         *
-         * @see Message#Message(CharSequence, long, CharSequence)
-         *
-         * @return this object for method chaining
-         */
-        public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
-            mMessages.add(new Message(text, timestamp, sender));
-            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
-                mMessages.remove(0);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a {@link Message} for display in this notification.
-         * @param message The {@link Message} to be displayed
-         * @return this object for method chaining
-         */
-        public MessagingStyle addMessage(Message message) {
-            mMessages.add(message);
-            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
-                mMessages.remove(0);
-            }
-            return this;
-        }
-
-        /**
-         * Gets the list of {@code Message} objects that represent the notification
-         */
-        public List<Message> getMessages() {
-            return mMessages;
-        }
-
-        /**
-         * Sets whether this conversation notification represents a group.
-         * @param isGroupConversation {@code true} if the conversation represents a group,
-         * {@code false} otherwise.
-         * @return this object for method chaining
-         */
-        public MessagingStyle setGroupConversation(boolean isGroupConversation) {
-            mIsGroupConversation = isGroupConversation;
-            return this;
-        }
-
-        /**
-         * Returns {@code true} if this notification represents a group conversation, otherwise
-         * {@code false}.
-         *
-         * <p> If the application that generated this {@link MessagingStyle} targets an SDK version
-         * less than {@link Build.VERSION_CODES#P}, this method becomes dependent on whether or
-         * not the conversation title is set; returning {@code true} if the conversation title is
-         * a non-null value, or {@code false} otherwise. From {@link Build.VERSION_CODES#P} forward,
-         * this method returns what's set by {@link #setGroupConversation(boolean)} allowing for
-         * named, non-group conversations.
-         *
-         * @see #setConversationTitle(CharSequence)
-         */
-        public boolean isGroupConversation() {
-            // When target SDK version is < P, a non-null conversation title dictates if this is
-            // as group conversation.
-            if (mBuilder != null
-                    && mBuilder.mContext.getApplicationInfo().targetSdkVersion
-                    < Build.VERSION_CODES.P) {
-                return mConversationTitle != null;
-            }
-
-            return mIsGroupConversation;
-        }
-
-        /**
-         * Retrieves a {@link MessagingStyle} from a {@link Notification}, enabling an application
-         * that has set a {@link MessagingStyle} using {@link NotificationCompat} or
-         * {@link android.app.Notification.Builder} to send messaging information to another
-         * 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 notification) {
-            MessagingStyle style;
-            Bundle extras = NotificationCompat.getExtras(notification);
-            if (extras != null && !extras.containsKey(EXTRA_SELF_DISPLAY_NAME)) {
-                style = null;
-            } else {
-                try {
-                    style = new MessagingStyle();
-                    style.restoreFromCompatExtras(extras);
-                } catch (ClassCastException e) {
-                    style = null;
-                }
-            }
-            return style;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        public void apply(NotificationBuilderWithBuilderAccessor builder) {
-            if (Build.VERSION.SDK_INT >= 24) {
-                Notification.MessagingStyle style =
-                        new Notification.MessagingStyle(mUserDisplayName)
-                                .setConversationTitle(mConversationTitle);
-                for (MessagingStyle.Message message : mMessages) {
-                    Notification.MessagingStyle.Message frameworkMessage =
-                            new Notification.MessagingStyle.Message(
-                                    message.getText(),
-                                    message.getTimestamp(),
-                                    message.getSender());
-                    if (message.getDataMimeType() != null) {
-                        frameworkMessage.setData(message.getDataMimeType(), message.getDataUri());
-                    }
-                    style.addMessage(frameworkMessage);
-                }
-                style.setBuilder(builder.getBuilder());
-            } 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);
-                    }
-                    new Notification.BigTextStyle(builder.getBuilder())
-                            .setBigContentTitle(null)
-                            .bigText(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);
-            if (mUserDisplayName != null) {
-                extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
-            }
-            if (mConversationTitle != null) {
-                extras.putCharSequence(EXTRA_CONVERSATION_TITLE, mConversationTitle);
-            }
-            if (!mMessages.isEmpty()) { extras.putParcelableArray(EXTRA_MESSAGES,
-                    Message.getBundleArrayForMessages(mMessages));
-            }
-            extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation);
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        protected void restoreFromCompatExtras(Bundle extras) {
-            mMessages.clear();
-            mUserDisplayName = extras.getString(EXTRA_SELF_DISPLAY_NAME);
-            mConversationTitle = extras.getString(EXTRA_CONVERSATION_TITLE);
-            Parcelable[] parcelables = extras.getParcelableArray(EXTRA_MESSAGES);
-            if (parcelables != null) {
-                mMessages = Message.getMessagesFromBundleArray(parcelables);
-            }
-            mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
-        }
-
-        public static final class Message {
-
-            static final String KEY_TEXT = "text";
-            static final String KEY_TIMESTAMP = "time";
-            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 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()}.
-             * Should be unique amongst all individuals in the conversation, and should be
-             * consistent during re-posts of the notification.
-             */
-            public Message(CharSequence text, long timestamp, CharSequence sender){
-                mText = text;
-                mTimestamp = timestamp;
-                mSender = sender;
-            }
-
-            /**
-             * Sets a binary blob of data and an associated MIME type for a message. In the case
-             * where the platform doesn't support the MIME type, the original text provided in the
-             * constructor will be used.
-             * @param dataMimeType The MIME type of the content. See
-             * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
-             * types on Android and Android Wear.
-             * @param dataUri The uri containing the content whose type is given by the MIME type.
-             * <p class="note">
-             * <ol>
-             *   <li>Notification Listeners including the System UI need permission to access the
-             *       data the Uri points to. The recommended ways to do this are:</li>
-             *   <li>Store the data in your own ContentProvider, making sure that other apps have
-             *       the correct permission to access your provider. The preferred mechanism for
-             *       providing access is to use per-URI permissions which are temporary and only
-             *       grant access to the receiving application. An easy way to create a
-             *       ContentProvider like this is to use the FileProvider helper class.</li>
-             *   <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
-             *       and image MIME types, however beginning with Android 3.0 (API level 11) it can
-             *       also store non-media types (see MediaStore.Files for more info). Files can be
-             *       inserted into the MediaStore using scanFile() after which a content:// style
-             *       Uri suitable for sharing is passed to the provided onScanCompleted() callback.
-             *       Note that once added to the system MediaStore the content is accessible to any
-             *       app on the device.</li>
-             * </ol>
-             * @return this object for method chaining
-             */
-            public Message setData(String dataMimeType, Uri dataUri) {
-                mDataMimeType = dataMimeType;
-                mDataUri = dataUri;
-                return this;
-            }
-
-            /**
-             * Get the text to be used for this message, or the fallback text if a type and content
-             * Uri have been set
-             */
-            public CharSequence getText() {
-                return mText;
-            }
-
-            /**
-             * 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() {
-                return mSender;
-            }
-
-            /**
-             * Get the MIME type of the data pointed to by the Uri
-             */
-            public String getDataMimeType() {
-                return mDataMimeType;
-            }
-
-            /**
-             * Get the the Uri pointing to the content of the message. Can be null, in which case
-             * {@see #getText()} is used.
-             */
-            public Uri getDataUri() {
-                return mDataUri;
-            }
-
-            private Bundle toBundle() {
-                Bundle bundle = new Bundle();
-                if (mText != null) {
-                    bundle.putCharSequence(KEY_TEXT, mText);
-                }
-                bundle.putLong(KEY_TIMESTAMP, mTimestamp);
-                if (mSender != null) {
-                    bundle.putCharSequence(KEY_SENDER, mSender);
-                }
-                if (mDataMimeType != null) {
-                    bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
-                }
-                if (mDataUri != null) {
-                    bundle.putParcelable(KEY_DATA_URI, mDataUri);
-                }
-                if (mExtras != null) {
-                    bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
-                }
-                return bundle;
-            }
-
-            static Bundle[] getBundleArrayForMessages(List<Message> messages) {
-                Bundle[] bundles = new Bundle[messages.size()];
-                final int N = messages.size();
-                for (int i = 0; i < N; i++) {
-                    bundles[i] = messages.get(i).toBundle();
-                }
-                return bundles;
-            }
-
-            static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
-                List<Message> messages = new ArrayList<>(bundles.length);
-                for (int i = 0; i < bundles.length; i++) {
-                    if (bundles[i] instanceof Bundle) {
-                        Message message = getMessageFromBundle((Bundle)bundles[i]);
-                        if (message != null) {
-                            messages.add(message);
-                        }
-                    }
-                }
-                return messages;
-            }
-
-            static Message getMessageFromBundle(Bundle bundle) {
-                try {
-                    if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
-                        return null;
-                    } else {
-                        Message message = new Message(bundle.getCharSequence(KEY_TEXT),
-                                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) {
-                    return null;
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
-     *
-     * <br>
-     * If the platform does not provide large-format notifications, this method has no effect. The
-     * user will always see the normal notification view.
-     * <br>
-     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
-     * <pre class="prettyprint">
-     * Notification notification = new Notification.Builder()
-     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
-     *     .setContentText(subject)
-     *     .setSmallIcon(R.drawable.new_mail)
-     *     .setLargeIcon(aBitmap)
-     *     .setStyle(new Notification.InboxStyle()
-     *         .addLine(str1)
-     *         .addLine(str2)
-     *         .setContentTitle(&quot;&quot;)
-     *         .setSummaryText(&quot;+3 more&quot;))
-     *     .build();
-     * </pre>
-     *
-     * @see Notification#bigContentView
-     */
-    public static class InboxStyle extends Style {
-        private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
-
-        public InboxStyle() {
-        }
-
-        public InboxStyle(Builder builder) {
-            setBuilder(builder);
-        }
-
-        /**
-         * Overrides ContentTitle in the big form of the template.
-         * This defaults to the value passed to setContentTitle().
-         */
-        public InboxStyle setBigContentTitle(CharSequence title) {
-            mBigContentTitle = Builder.limitCharSequenceLength(title);
-            return this;
-        }
-
-        /**
-         * Set the first line of text after the detail section in the big form of the template.
-         */
-        public InboxStyle setSummaryText(CharSequence cs) {
-            mSummaryText = Builder.limitCharSequenceLength(cs);
-            mSummaryTextSet = true;
-            return this;
-        }
-
-        /**
-         * Append a line to the digest section of the Inbox notification.
-         */
-        public InboxStyle addLine(CharSequence cs) {
-            mTexts.add(Builder.limitCharSequenceLength(cs));
-            return this;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Override
-        public void apply(NotificationBuilderWithBuilderAccessor builder) {
-            if (Build.VERSION.SDK_INT >= 16) {
-                Notification.InboxStyle style =
-                        new Notification.InboxStyle(builder.getBuilder())
-                                .setBigContentTitle(mBigContentTitle);
-                if (mSummaryTextSet) {
-                    style.setSummaryText(mSummaryText);
-                }
-                for (CharSequence text: mTexts) {
-                    style.addLine(text);
-                }
-            }
-        }
-    }
-
-    /**
-     * 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;
-        }
-    }
-
-    /**
-     * Structure to encapsulate a named action that can be shown as part of this notification.
-     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
-     * selected by the user. Action buttons won't appear on platforms prior to Android 4.1.
-     * <p>
-     * Apps should use {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent)}
-     * or {@link NotificationCompat.Builder#addAction(NotificationCompat.Action)}
-     * to attach actions.
-     */
-    public static class Action {
-        /**
-         * {@link SemanticAction}: No semantic action defined.
-         */
-        public static final int SEMANTIC_ACTION_NONE = 0;
-
-        /**
-         * {@link SemanticAction}: Reply to a conversation, chat, group, or wherever replies
-         * may be appropriate.
-         */
-        public static final int SEMANTIC_ACTION_REPLY = 1;
-
-        /**
-         * {@link SemanticAction}: Mark content as read.
-         */
-        public static final int SEMANTIC_ACTION_MARK_AS_READ = 2;
-
-        /**
-         * {@link SemanticAction}: Mark content as unread.
-         */
-        public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
-
-        /**
-         * {@link SemanticAction}: Delete the content associated with the notification. This
-         * could mean deleting an email, message, etc.
-         */
-        public static final int SEMANTIC_ACTION_DELETE = 4;
-
-        /**
-         * {@link SemanticAction}: Archive the content associated with the notification. This
-         * could mean archiving an email, message, etc.
-         */
-        public static final int SEMANTIC_ACTION_ARCHIVE = 5;
-
-        /**
-         * {@link SemanticAction}: Mute the content associated with the notification. This could
-         * mean silencing a conversation or currently playing media.
-         */
-        public static final int SEMANTIC_ACTION_MUTE = 6;
-
-        /**
-         * {@link SemanticAction}: Unmute the content associated with the notification. This could
-         * mean un-silencing a conversation or currently playing media.
-         */
-        public static final int SEMANTIC_ACTION_UNMUTE = 7;
-
-        /**
-         * {@link SemanticAction}: Mark content with a thumbs up.
-         */
-        public static final int SEMANTIC_ACTION_THUMBS_UP = 8;
-
-        /**
-         * {@link SemanticAction}: Mark content with a thumbs down.
-         */
-        public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9;
-
-        static final String EXTRA_SHOWS_USER_INTERFACE =
-                "android.support.action.showsUserInterface";
-
-        static final String EXTRA_SEMANTIC_ACTION = "android.support.action.semanticAction";
-
-        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;
-        private boolean mShowsUserInterface = true;
-
-        private final @SemanticAction int mSemanticAction;
-
-        /**
-         * Small icon representing the action.
-         */
-        public int icon;
-        /**
-         * Title of the action.
-         */
-        public CharSequence title;
-        /**
-         * Intent to send when the user invokes this action. May be null, in which case the action
-         * may be rendered in a disabled presentation.
-         */
-        public PendingIntent actionIntent;
-
-        public Action(int icon, CharSequence title, PendingIntent intent) {
-            this(icon, title, intent, new Bundle(), null, null, true, SEMANTIC_ACTION_NONE, true);
-        }
-
-        Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
-                RemoteInput[] remoteInputs, RemoteInput[] dataOnlyRemoteInputs,
-                boolean allowGeneratedReplies, @SemanticAction int semanticAction,
-                boolean showsUserInterface) {
-            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;
-            this.mSemanticAction = semanticAction;
-            this.mShowsUserInterface = showsUserInterface;
-        }
-
-        public int getIcon() {
-            return icon;
-        }
-
-        public CharSequence getTitle() {
-            return title;
-        }
-
-        public PendingIntent getActionIntent() {
-            return actionIntent;
-        }
-
-        /**
-         * Get additional metadata carried around with this Action.
-         */
-        public Bundle getExtras() {
-            return mExtras;
-        }
-
-        /**
-         * Return whether the platform should automatically generate possible replies for this
-         * {@link Action}
-         */
-        public boolean getAllowGeneratedReplies() {
-            return mAllowGeneratedReplies;
-        }
-
-        /**
-         * 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. Only returns inputs which accept
-         * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
-         */
-        public RemoteInput[] getRemoteInputs() {
-            return mRemoteInputs;
-        }
-
-        /**
-         * Returns the {@link SemanticAction} associated with this {@link Action}. A
-         * {@link SemanticAction} denotes what an {@link Action}'s {@link PendingIntent} will do
-         * (eg. reply, mark as read, delete, etc).
-         *
-         * @see SemanticAction
-         */
-        public @SemanticAction int getSemanticAction() {
-            return mSemanticAction;
-        }
-
-        /**
-         * 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.
-         */
-        public RemoteInput[] getDataOnlyRemoteInputs() {
-            return mDataOnlyRemoteInputs;
-        }
-
-        /**
-         * Return whether or not triggering this {@link Action}'s {@link PendingIntent} will open a
-         * user interface.
-         */
-        public boolean getShowsUserInterface() {
-            return mShowsUserInterface;
-        }
-
-        /**
-         * Builder class for {@link Action} objects.
-         */
-        public static final class Builder {
-            private final int mIcon;
-            private final CharSequence mTitle;
-            private final PendingIntent mIntent;
-            private boolean mAllowGeneratedReplies = true;
-            private final Bundle mExtras;
-            private ArrayList<RemoteInput> mRemoteInputs;
-            private @SemanticAction int mSemanticAction;
-            private boolean mShowsUserInterface = true;
-
-            /**
-             * Construct a new builder for {@link Action} object.
-             * @param icon icon to show for this action
-             * @param title the title of the action
-             * @param intent the {@link PendingIntent} to fire when users trigger this action
-             */
-            public Builder(int icon, CharSequence title, PendingIntent intent) {
-                this(icon, title, intent, new Bundle(), null, true, SEMANTIC_ACTION_NONE, true);
-            }
-
-            /**
-             * Construct a new builder for {@link Action} object using the fields from an
-             * {@link Action}.
-             * @param action the action to read fields from.
-             */
-            public Builder(Action action) {
-                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
-                        action.getRemoteInputs(), action.getAllowGeneratedReplies(),
-                        action.getSemanticAction(), action.mShowsUserInterface);
-            }
-
-            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
-                    RemoteInput[] remoteInputs, boolean allowGeneratedReplies,
-                    @SemanticAction int semanticAction, boolean showsUserInterface) {
-                mIcon = icon;
-                mTitle = NotificationCompat.Builder.limitCharSequenceLength(title);
-                mIntent = intent;
-                mExtras = extras;
-                mRemoteInputs = remoteInputs == null ? null : new ArrayList<>(
-                        Arrays.asList(remoteInputs));
-                mAllowGeneratedReplies = allowGeneratedReplies;
-                mSemanticAction = semanticAction;
-                mShowsUserInterface = showsUserInterface;
-            }
-
-            /**
-             * Merge additional metadata into this builder.
-             *
-             * <p>Values within the Bundle will replace existing extras values in this Builder.
-             *
-             * @see NotificationCompat.Action#getExtras
-             */
-            public Builder addExtras(Bundle extras) {
-                if (extras != null) {
-                    mExtras.putAll(extras);
-                }
-                return this;
-            }
-
-            /**
-             * Get the metadata Bundle used by this Builder.
-             *
-             * <p>The returned Bundle is shared with this Builder.
-             */
-            public Bundle getExtras() {
-                return mExtras;
-            }
-
-            /**
-             * Add an input to be collected from the user when this action is sent.
-             * Response values can be retrieved from the fired intent by using the
-             * {@link RemoteInput#getResultsFromIntent} function.
-             * @param remoteInput a {@link RemoteInput} to add to the action
-             * @return this object for method chaining
-             */
-            public Builder addRemoteInput(RemoteInput remoteInput) {
-                if (mRemoteInputs == null) {
-                    mRemoteInputs = new ArrayList<RemoteInput>();
-                }
-                mRemoteInputs.add(remoteInput);
-                return this;
-            }
-
-            /**
-             * Set whether the platform should automatically generate possible replies to add to
-             * {@link RemoteInput#getChoices()}. If the {@link Action} doesn't have a
-             * {@link RemoteInput}, this has no effect.
-             * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
-             * otherwise
-             * @return this object for method chaining
-             * The default value is {@code true}
-             */
-            public Builder setAllowGeneratedReplies(boolean allowGeneratedReplies) {
-                mAllowGeneratedReplies = allowGeneratedReplies;
-                return this;
-            }
-
-            /**
-             * Sets the {@link SemanticAction} for this {@link Action}. A {@link SemanticAction}
-             * denotes what an {@link Action}'s {@link PendingIntent} will do (eg. reply, mark
-             * as read, delete, etc).
-             * @param semanticAction a {@link SemanticAction} defined within {@link Action} with
-             * {@code SEMANTIC_ACTION_} prefixes
-             * @return this object for method chaining
-             */
-            public Builder setSemanticAction(@SemanticAction int semanticAction) {
-                mSemanticAction = semanticAction;
-                return this;
-            }
-
-            /**
-             * Set whether or not this {@link Action}'s {@link PendingIntent} will open a user
-             * interface.
-             * @param showsUserInterface {@code true} if this {@link Action}'s {@link PendingIntent}
-             * will open a user interface, otherwise {@code false}
-             * @return this object for method chaining
-             * The default value is {@code true}
-             */
-            public Builder setShowsUserInterface(boolean showsUserInterface) {
-                mShowsUserInterface = showsUserInterface;
-                return this;
-            }
-
-            /**
-             * Apply an extender to this action builder. Extenders may be used to add
-             * metadata or change options on this builder.
-             */
-            public Builder extend(Extender extender) {
-                extender.extend(this);
-                return this;
-            }
-
-            /**
-             * Combine all of the options that have been set and return a new {@link Action}
-             * object.
-             * @return the built action
-             */
-            public Action build() {
-                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, mSemanticAction,
-                        mShowsUserInterface);
-            }
-        }
-
-
-        /**
-         * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
-         * metadata or change options on an action builder.
-         */
-        public interface Extender {
-            /**
-             * Apply this extender to a notification action builder.
-             * @param builder the builder to be modified.
-             * @return the build object for chaining.
-             */
-            Builder extend(Builder builder);
-        }
-
-        /**
-         * Wearable extender for notification actions. To add extensions to an action,
-         * create a new {@link NotificationCompat.Action.WearableExtender} object using
-         * the {@code WearableExtender()} constructor and apply it to a
-         * {@link NotificationCompat.Action.Builder} using
-         * {@link NotificationCompat.Action.Builder#extend}.
-         *
-         * <pre class="prettyprint">
-         * NotificationCompat.Action action = new NotificationCompat.Action.Builder(
-         *         R.drawable.archive_all, "Archive all", actionIntent)
-         *         .extend(new NotificationCompat.Action.WearableExtender()
-         *                 .setAvailableOffline(false))
-         *         .build();</pre>
-         */
-        public static final class WearableExtender implements Extender {
-            /** Notification action extra which contains wearable extensions */
-            private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
-            // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
-            private static final String KEY_FLAGS = "flags";
-            private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel";
-            private static final String KEY_CONFIRM_LABEL = "confirmLabel";
-            private static final String KEY_CANCEL_LABEL = "cancelLabel";
-
-            // Flags bitwise-ored to mFlags
-            private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
-            private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
-            private static final int FLAG_HINT_DISPLAY_INLINE = 1 << 2;
-
-            // Default value for flags integer
-            private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
-
-            private int mFlags = DEFAULT_FLAGS;
-
-            private CharSequence mInProgressLabel;
-            private CharSequence mConfirmLabel;
-            private CharSequence mCancelLabel;
-
-            /**
-             * Create a {@link NotificationCompat.Action.WearableExtender} with default
-             * options.
-             */
-            public WearableExtender() {
-            }
-
-            /**
-             * Create a {@link NotificationCompat.Action.WearableExtender} by reading
-             * wearable options present in an existing notification action.
-             * @param action the notification action to inspect.
-             */
-            public WearableExtender(Action action) {
-                Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS);
-                if (wearableBundle != null) {
-                    mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
-                    mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL);
-                    mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL);
-                    mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL);
-                }
-            }
-
-            /**
-             * Apply wearable extensions to a notification action that is being built. This is
-             * typically called by the {@link NotificationCompat.Action.Builder#extend}
-             * method of {@link NotificationCompat.Action.Builder}.
-             */
-            @Override
-            public Action.Builder extend(Action.Builder builder) {
-                Bundle wearableBundle = new Bundle();
-
-                if (mFlags != DEFAULT_FLAGS) {
-                    wearableBundle.putInt(KEY_FLAGS, mFlags);
-                }
-                if (mInProgressLabel != null) {
-                    wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel);
-                }
-                if (mConfirmLabel != null) {
-                    wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel);
-                }
-                if (mCancelLabel != null) {
-                    wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel);
-                }
-
-                builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
-                return builder;
-            }
-
-            @Override
-            public WearableExtender clone() {
-                WearableExtender that = new WearableExtender();
-                that.mFlags = this.mFlags;
-                that.mInProgressLabel = this.mInProgressLabel;
-                that.mConfirmLabel = this.mConfirmLabel;
-                that.mCancelLabel = this.mCancelLabel;
-                return that;
-            }
-
-            /**
-             * Set whether this action is available when the wearable device is not connected to
-             * a companion device. The user can still trigger this action when the wearable device
-             * is offline, but a visual hint will indicate that the action may not be available.
-             * Defaults to true.
-             */
-            public WearableExtender setAvailableOffline(boolean availableOffline) {
-                setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
-                return this;
-            }
-
-            /**
-             * Get whether this action is available when the wearable device is not connected to
-             * a companion device. The user can still trigger this action when the wearable device
-             * is offline, but a visual hint will indicate that the action may not be available.
-             * Defaults to true.
-             */
-            public boolean isAvailableOffline() {
-                return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
-            }
-
-            private void setFlag(int mask, boolean value) {
-                if (value) {
-                    mFlags |= mask;
-                } else {
-                    mFlags &= ~mask;
-                }
-            }
-
-            /**
-             * Set a label to display while the wearable is preparing to automatically execute the
-             * action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
-             *
-             * @param label the label to display while the action is being prepared to execute
-             * @return this object for method chaining
-             */
-            public WearableExtender setInProgressLabel(CharSequence label) {
-                mInProgressLabel = label;
-                return this;
-            }
-
-            /**
-             * Get the label to display while the wearable is preparing to automatically execute
-             * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..."
-             *
-             * @return the label to display while the action is being prepared to execute
-             */
-            public CharSequence getInProgressLabel() {
-                return mInProgressLabel;
-            }
-
-            /**
-             * Set a label to display to confirm that the action should be executed.
-             * This is usually an imperative verb like "Send".
-             *
-             * @param label the label to confirm the action should be executed
-             * @return this object for method chaining
-             */
-            public WearableExtender setConfirmLabel(CharSequence label) {
-                mConfirmLabel = label;
-                return this;
-            }
-
-            /**
-             * Get the label to display to confirm that the action should be executed.
-             * This is usually an imperative verb like "Send".
-             *
-             * @return the label to confirm the action should be executed
-             */
-            public CharSequence getConfirmLabel() {
-                return mConfirmLabel;
-            }
-
-            /**
-             * Set a label to display to cancel the action.
-             * This is usually an imperative verb, like "Cancel".
-             *
-             * @param label the label to display to cancel the action
-             * @return this object for method chaining
-             */
-            public WearableExtender setCancelLabel(CharSequence label) {
-                mCancelLabel = label;
-                return this;
-            }
-
-            /**
-             * Get the label to display to cancel the action.
-             * This is usually an imperative verb like "Cancel".
-             *
-             * @return the label to display to cancel the action
-             */
-            public CharSequence getCancelLabel() {
-                return mCancelLabel;
-            }
-
-            /**
-             * Set a hint that this Action will launch an {@link Activity} directly, telling the
-             * platform that it can generate the appropriate transitions.
-             * @param hintLaunchesActivity {@code true} if the content intent will launch
-             * an activity and transitions should be generated, false otherwise.
-             * @return this object for method chaining
-             */
-            public WearableExtender setHintLaunchesActivity(
-                    boolean hintLaunchesActivity) {
-                setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
-                return this;
-            }
-
-            /**
-             * Get a hint that this Action will launch an {@link Activity} directly, telling the
-             * platform that it can generate the appropriate transitions
-             * @return {@code true} if the content intent will launch an activity and transitions
-             * should be generated, false otherwise. The default value is {@code false} if this was
-             * never set.
-             */
-            public boolean getHintLaunchesActivity() {
-                return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
-            }
-
-            /**
-             * Set a hint that this Action should be displayed inline - i.e. it will have a visual
-             * representation directly on the notification surface in addition to the expanded
-             * Notification
-             *
-             * @param hintDisplayInline {@code true} if action should be displayed inline, false
-             *        otherwise
-             * @return this object for method chaining
-             */
-            public WearableExtender setHintDisplayActionInline(
-                    boolean hintDisplayInline) {
-                setFlag(FLAG_HINT_DISPLAY_INLINE, hintDisplayInline);
-                return this;
-            }
-
-            /**
-             * Get a hint that this Action should be displayed inline - i.e. it should have a
-             * visual representation directly on the notification surface in addition to the
-             * expanded Notification
-             *
-             * @return {@code true} if the Action should be displayed inline, {@code false}
-             *         otherwise. The default value is {@code false} if this was never set.
-             */
-            public boolean getHintDisplayActionInline() {
-                return (mFlags & FLAG_HINT_DISPLAY_INLINE) != 0;
-            }
-        }
-
-        /**
-         * Provides meaning to an {@link Action} that hints at what the associated
-         * {@link PendingIntent} will do. For example, an {@link Action} with a
-         * {@link PendingIntent} that replies to a text message notification may have the
-         * {@link #SEMANTIC_ACTION_REPLY} {@link SemanticAction} set within it.
-         */
-        @IntDef({
-                SEMANTIC_ACTION_NONE,
-                SEMANTIC_ACTION_REPLY,
-                SEMANTIC_ACTION_MARK_AS_READ,
-                SEMANTIC_ACTION_MARK_AS_UNREAD,
-                SEMANTIC_ACTION_DELETE,
-                SEMANTIC_ACTION_ARCHIVE,
-                SEMANTIC_ACTION_MUTE,
-                SEMANTIC_ACTION_UNMUTE,
-                SEMANTIC_ACTION_THUMBS_UP,
-                SEMANTIC_ACTION_THUMBS_DOWN
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface SemanticAction {}
-    }
-
-
-    /**
-     * Extender interface for use with {@link Builder#extend}. Extenders may be used to add
-     * metadata or change options on a notification builder.
-     */
-    public interface Extender {
-        /**
-         * Apply this extender to a notification builder.
-         * @param builder the builder to be modified.
-         * @return the build object for chaining.
-         */
-        Builder extend(Builder builder);
-    }
-
-    /**
-     * Helper class to add wearable extensions to notifications.
-     * <p class="note"> See
-     * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
-     * for Android Wear</a> for more information on how to use this class.
-     * <p>
-     * To create a notification with wearable extensions:
-     * <ol>
-     *   <li>Create a {@link NotificationCompat.Builder}, setting any desired
-     *   properties.
-     *   <li>Create a {@link NotificationCompat.WearableExtender}.
-     *   <li>Set wearable-specific properties using the
-     *   {@code add} and {@code set} methods of {@link NotificationCompat.WearableExtender}.
-     *   <li>Call {@link NotificationCompat.Builder#extend} to apply the extensions to a
-     *   notification.
-     *   <li>Post the notification to the notification
-     *   system with the {@code NotificationManagerCompat.notify(...)} methods
-     *   and not the {@code NotificationManager.notify(...)} methods.
-     * </ol>
-     *
-     * <pre class="prettyprint">
-     * 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, notification);</pre>
-     *
-     * <p>Wearable extensions can be accessed on an existing notification by using the
-     * {@code WearableExtender(Notification)} constructor,
-     * and then using the {@code get} methods to access values.
-     *
-     * <pre class="prettyprint">
-     * NotificationCompat.WearableExtender wearableExtender =
-     *         new NotificationCompat.WearableExtender(notification);
-     * List&lt;Notification&gt; pages = wearableExtender.getPages();</pre>
-     */
-    public static final class WearableExtender implements Extender {
-        /**
-         * Sentinel value for an action index that is unset.
-         */
-        public static final int UNSET_ACTION_INDEX = -1;
-
-        /**
-         * Size value for use with {@link #setCustomSizePreset} to show this notification with
-         * default sizing.
-         * <p>For custom display notifications created using {@link #setDisplayIntent},
-         * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
-         * on their content.
-         */
-        public static final int SIZE_DEFAULT = 0;
-
-        /**
-         * Size value for use with {@link #setCustomSizePreset} to show this notification
-         * with an extra small size.
-         * <p>This value is only applicable for custom display notifications created using
-         * {@link #setDisplayIntent}.
-         */
-        public static final int SIZE_XSMALL = 1;
-
-        /**
-         * Size value for use with {@link #setCustomSizePreset} to show this notification
-         * with a small size.
-         * <p>This value is only applicable for custom display notifications created using
-         * {@link #setDisplayIntent}.
-         */
-        public static final int SIZE_SMALL = 2;
-
-        /**
-         * Size value for use with {@link #setCustomSizePreset} to show this notification
-         * with a medium size.
-         * <p>This value is only applicable for custom display notifications created using
-         * {@link #setDisplayIntent}.
-         */
-        public static final int SIZE_MEDIUM = 3;
-
-        /**
-         * Size value for use with {@link #setCustomSizePreset} to show this notification
-         * with a large size.
-         * <p>This value is only applicable for custom display notifications created using
-         * {@link #setDisplayIntent}.
-         */
-        public static final int SIZE_LARGE = 4;
-
-        /**
-         * Size value for use with {@link #setCustomSizePreset} to show this notification
-         * full screen.
-         * <p>This value is only applicable for custom display notifications created using
-         * {@link #setDisplayIntent}.
-         */
-        public static final int SIZE_FULL_SCREEN = 5;
-
-        /**
-         * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
-         * short amount of time when this notification is displayed on the screen. This
-         * is the default value.
-         */
-        public static final int SCREEN_TIMEOUT_SHORT = 0;
-
-        /**
-         * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
-         * for a longer amount of time when this notification is displayed on the screen.
-         */
-        public static final int SCREEN_TIMEOUT_LONG = -1;
-
-        /** Notification extra which contains wearable extensions */
-        private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
-
-        // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options.
-        private static final String KEY_ACTIONS = "actions";
-        private static final String KEY_FLAGS = "flags";
-        private static final String KEY_DISPLAY_INTENT = "displayIntent";
-        private static final String KEY_PAGES = "pages";
-        private static final String KEY_BACKGROUND = "background";
-        private static final String KEY_CONTENT_ICON = "contentIcon";
-        private static final String KEY_CONTENT_ICON_GRAVITY = "contentIconGravity";
-        private static final String KEY_CONTENT_ACTION_INDEX = "contentActionIndex";
-        private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
-        private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
-        private static final String KEY_GRAVITY = "gravity";
-        private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
-        private static final String KEY_DISMISSAL_ID = "dismissalId";
-        private static final String KEY_BRIDGE_TAG = "bridgeTag";
-
-        // Flags bitwise-ored to mFlags
-        private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
-        private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
-        private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
-        private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
-        private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
-        private static final int FLAG_BIG_PICTURE_AMBIENT = 1 << 5;
-        private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
-
-        // Default value for flags integer
-        private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
-
-        private static final int DEFAULT_CONTENT_ICON_GRAVITY = GravityCompat.END;
-        private static final int DEFAULT_GRAVITY = Gravity.BOTTOM;
-
-        private ArrayList<Action> mActions = new ArrayList<Action>();
-        private int mFlags = DEFAULT_FLAGS;
-        private PendingIntent mDisplayIntent;
-        private ArrayList<Notification> mPages = new ArrayList<Notification>();
-        private Bitmap mBackground;
-        private int mContentIcon;
-        private int mContentIconGravity = DEFAULT_CONTENT_ICON_GRAVITY;
-        private int mContentActionIndex = UNSET_ACTION_INDEX;
-        private int mCustomSizePreset = SIZE_DEFAULT;
-        private int mCustomContentHeight;
-        private int mGravity = DEFAULT_GRAVITY;
-        private int mHintScreenTimeout;
-        private String mDismissalId;
-        private String mBridgeTag;
-
-        /**
-         * Create a {@link NotificationCompat.WearableExtender} with default
-         * options.
-         */
-        public WearableExtender() {
-        }
-
-        public WearableExtender(Notification notification) {
-            Bundle extras = getExtras(notification);
-            Bundle wearableBundle = extras != null ? extras.getBundle(EXTRA_WEARABLE_EXTENSIONS)
-                    : null;
-            if (wearableBundle != null) {
-                final ArrayList<Parcelable> parcelables =
-                        wearableBundle.getParcelableArrayList(KEY_ACTIONS);
-                if (Build.VERSION.SDK_INT >= 16 && parcelables != null) {
-                    Action[] actions = new Action[parcelables.size()];
-                    for (int i = 0; i < actions.length; i++) {
-                        if (Build.VERSION.SDK_INT >= 20) {
-                            actions[i] = NotificationCompat.getActionCompatFromAction(
-                                    (Notification.Action) parcelables.get(i));
-                        } else if (Build.VERSION.SDK_INT >= 16) {
-                            actions[i] = NotificationCompatJellybean.getActionFromBundle(
-                                    (Bundle) parcelables.get(i));
-                        }
-                    }
-                    Collections.addAll(mActions, (Action[]) actions);
-                }
-
-                mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
-                mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
-
-                Notification[] pages = getNotificationArrayFromBundle(
-                        wearableBundle, KEY_PAGES);
-                if (pages != null) {
-                    Collections.addAll(mPages, pages);
-                }
-
-                mBackground = wearableBundle.getParcelable(KEY_BACKGROUND);
-                mContentIcon = wearableBundle.getInt(KEY_CONTENT_ICON);
-                mContentIconGravity = wearableBundle.getInt(KEY_CONTENT_ICON_GRAVITY,
-                        DEFAULT_CONTENT_ICON_GRAVITY);
-                mContentActionIndex = wearableBundle.getInt(KEY_CONTENT_ACTION_INDEX,
-                        UNSET_ACTION_INDEX);
-                mCustomSizePreset = wearableBundle.getInt(KEY_CUSTOM_SIZE_PRESET,
-                        SIZE_DEFAULT);
-                mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
-                mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
-                mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
-                mDismissalId = wearableBundle.getString(KEY_DISMISSAL_ID);
-                mBridgeTag = wearableBundle.getString(KEY_BRIDGE_TAG);
-            }
-        }
-
-        /**
-         * Apply wearable extensions to a notification that is being built. This is typically
-         * called by the {@link NotificationCompat.Builder#extend} method of
-         * {@link NotificationCompat.Builder}.
-         */
-        @Override
-        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
-            Bundle wearableBundle = new Bundle();
-
-            if (!mActions.isEmpty()) {
-                if (Build.VERSION.SDK_INT >= 16) {
-                    ArrayList<Parcelable> parcelables = new ArrayList<>(mActions.size());
-                    for (Action action : mActions) {
-                        if (Build.VERSION.SDK_INT >= 20) {
-                            parcelables.add(
-                                    WearableExtender.getActionFromActionCompat(action));
-                        } else if (Build.VERSION.SDK_INT >= 16) {
-                            parcelables.add(NotificationCompatJellybean.getBundleForAction(action));
-                        }
-                    }
-                    wearableBundle.putParcelableArrayList(KEY_ACTIONS, parcelables);
-                } else {
-                    wearableBundle.putParcelableArrayList(KEY_ACTIONS, null);
-                }
-            }
-            if (mFlags != DEFAULT_FLAGS) {
-                wearableBundle.putInt(KEY_FLAGS, mFlags);
-            }
-            if (mDisplayIntent != null) {
-                wearableBundle.putParcelable(KEY_DISPLAY_INTENT, mDisplayIntent);
-            }
-            if (!mPages.isEmpty()) {
-                wearableBundle.putParcelableArray(KEY_PAGES, mPages.toArray(
-                        new Notification[mPages.size()]));
-            }
-            if (mBackground != null) {
-                wearableBundle.putParcelable(KEY_BACKGROUND, mBackground);
-            }
-            if (mContentIcon != 0) {
-                wearableBundle.putInt(KEY_CONTENT_ICON, mContentIcon);
-            }
-            if (mContentIconGravity != DEFAULT_CONTENT_ICON_GRAVITY) {
-                wearableBundle.putInt(KEY_CONTENT_ICON_GRAVITY, mContentIconGravity);
-            }
-            if (mContentActionIndex != UNSET_ACTION_INDEX) {
-                wearableBundle.putInt(KEY_CONTENT_ACTION_INDEX,
-                        mContentActionIndex);
-            }
-            if (mCustomSizePreset != SIZE_DEFAULT) {
-                wearableBundle.putInt(KEY_CUSTOM_SIZE_PRESET, mCustomSizePreset);
-            }
-            if (mCustomContentHeight != 0) {
-                wearableBundle.putInt(KEY_CUSTOM_CONTENT_HEIGHT, mCustomContentHeight);
-            }
-            if (mGravity != DEFAULT_GRAVITY) {
-                wearableBundle.putInt(KEY_GRAVITY, mGravity);
-            }
-            if (mHintScreenTimeout != 0) {
-                wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
-            }
-            if (mDismissalId != null) {
-                wearableBundle.putString(KEY_DISMISSAL_ID, mDismissalId);
-            }
-            if (mBridgeTag != null) {
-                wearableBundle.putString(KEY_BRIDGE_TAG, mBridgeTag);
-            }
-
-            builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
-            return builder;
-        }
-
-        @RequiresApi(20)
-        private static Notification.Action getActionFromActionCompat(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());
-            if (Build.VERSION.SDK_INT >= 24) {
-                actionBuilder.setAllowGeneratedReplies(actionCompat.getAllowGeneratedReplies());
-            }
-            actionBuilder.addExtras(actionExtras);
-            RemoteInput[] remoteInputCompats = actionCompat.getRemoteInputs();
-            if (remoteInputCompats != null) {
-                android.app.RemoteInput[] remoteInputs = RemoteInput.fromCompat(remoteInputCompats);
-                for (android.app.RemoteInput remoteInput : remoteInputs) {
-                    actionBuilder.addRemoteInput(remoteInput);
-                }
-            }
-            return actionBuilder.build();
-        }
-
-        @Override
-        public WearableExtender clone() {
-            WearableExtender that = new WearableExtender();
-            that.mActions = new ArrayList<>(this.mActions);
-            that.mFlags = this.mFlags;
-            that.mDisplayIntent = this.mDisplayIntent;
-            that.mPages = new ArrayList<>(this.mPages);
-            that.mBackground = this.mBackground;
-            that.mContentIcon = this.mContentIcon;
-            that.mContentIconGravity = this.mContentIconGravity;
-            that.mContentActionIndex = this.mContentActionIndex;
-            that.mCustomSizePreset = this.mCustomSizePreset;
-            that.mCustomContentHeight = this.mCustomContentHeight;
-            that.mGravity = this.mGravity;
-            that.mHintScreenTimeout = this.mHintScreenTimeout;
-            that.mDismissalId = this.mDismissalId;
-            that.mBridgeTag = this.mBridgeTag;
-            return that;
-        }
-
-        /**
-         * Add a wearable action to this notification.
-         *
-         * <p>When wearable actions are added using this method, the set of actions that
-         * show on a wearable device splits from devices that only show actions added
-         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
-         * of which actions display on different devices.
-         *
-         * @param action the action to add to this notification
-         * @return this object for method chaining
-         * @see NotificationCompat.Action
-         */
-        public WearableExtender addAction(Action action) {
-            mActions.add(action);
-            return this;
-        }
-
-        /**
-         * Adds wearable actions to this notification.
-         *
-         * <p>When wearable actions are added using this method, the set of actions that
-         * show on a wearable device splits from devices that only show actions added
-         * using {@link NotificationCompat.Builder#addAction}. This allows for customization
-         * of which actions display on different devices.
-         *
-         * @param actions the actions to add to this notification
-         * @return this object for method chaining
-         * @see NotificationCompat.Action
-         */
-        public WearableExtender addActions(List<Action> actions) {
-            mActions.addAll(actions);
-            return this;
-        }
-
-        /**
-         * Clear all wearable actions present on this builder.
-         * @return this object for method chaining.
-         * @see #addAction
-         */
-        public WearableExtender clearActions() {
-            mActions.clear();
-            return this;
-        }
-
-        /**
-         * Get the wearable actions present on this notification.
-         */
-        public List<Action> getActions() {
-            return mActions;
-        }
-
-        /**
-         * Set an intent to launch inside of an activity view when displaying
-         * this notification. The {@link PendingIntent} provided should be for an activity.
-         *
-         * <pre class="prettyprint">
-         * Intent displayIntent = new Intent(context, MyDisplayActivity.class);
-         * PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
-         *         0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-         * Notification notification = new NotificationCompat.Builder(context)
-         *         .extend(new NotificationCompat.WearableExtender()
-         *                 .setDisplayIntent(displayPendingIntent)
-         *                 .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_MEDIUM))
-         *         .build();</pre>
-         *
-         * <p>The activity to launch needs to allow embedding, must be exported, and
-         * should have an empty task affinity. It is also recommended to use the device
-         * default light theme.
-         *
-         * <p>Example AndroidManifest.xml entry:
-         * <pre class="prettyprint">
-         * &lt;activity android:name=&quot;com.example.MyDisplayActivity&quot;
-         *     android:exported=&quot;true&quot;
-         *     android:allowEmbedded=&quot;true&quot;
-         *     android:taskAffinity=&quot;&quot;
-         *     android:theme=&quot;@android:style/Theme.DeviceDefault.Light&quot; /&gt;</pre>
-         *
-         * @param intent the {@link PendingIntent} for an activity
-         * @return this object for method chaining
-         * @see NotificationCompat.WearableExtender#getDisplayIntent
-         */
-        public WearableExtender setDisplayIntent(PendingIntent intent) {
-            mDisplayIntent = intent;
-            return this;
-        }
-
-        /**
-         * Get the intent to launch inside of an activity view when displaying this
-         * notification. This {@code PendingIntent} should be for an activity.
-         */
-        public PendingIntent getDisplayIntent() {
-            return mDisplayIntent;
-        }
-
-        /**
-         * Add an additional page of content to display with this notification. The current
-         * notification forms the first page, and pages added using this function form
-         * subsequent pages. This field can be used to separate a notification into multiple
-         * sections.
-         *
-         * @param page the notification to add as another page
-         * @return this object for method chaining
-         * @see NotificationCompat.WearableExtender#getPages
-         */
-        public WearableExtender addPage(Notification page) {
-            mPages.add(page);
-            return this;
-        }
-
-        /**
-         * Add additional pages of content to display with this notification. The current
-         * notification forms the first page, and pages added using this function form
-         * subsequent pages. This field can be used to separate a notification into multiple
-         * sections.
-         *
-         * @param pages a list of notifications
-         * @return this object for method chaining
-         * @see NotificationCompat.WearableExtender#getPages
-         */
-        public WearableExtender addPages(List<Notification> pages) {
-            mPages.addAll(pages);
-            return this;
-        }
-
-        /**
-         * Clear all additional pages present on this builder.
-         * @return this object for method chaining.
-         * @see #addPage
-         */
-        public WearableExtender clearPages() {
-            mPages.clear();
-            return this;
-        }
-
-        /**
-         * Get the array of additional pages of content for displaying this notification. The
-         * current notification forms the first page, and elements within this array form
-         * subsequent pages. This field can be used to separate a notification into multiple
-         * sections.
-         * @return the pages for this notification
-         */
-        public List<Notification> getPages() {
-            return mPages;
-        }
-
-        /**
-         * Set a background image to be displayed behind the notification content.
-         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
-         * will work with any notification style.
-         *
-         * @param background the background bitmap
-         * @return this object for method chaining
-         * @see NotificationCompat.WearableExtender#getBackground
-         */
-        public WearableExtender setBackground(Bitmap background) {
-            mBackground = background;
-            return this;
-        }
-
-        /**
-         * Get a background image to be displayed behind the notification content.
-         * Contrary to the {@link NotificationCompat.BigPictureStyle}, this background
-         * will work with any notification style.
-         *
-         * @return the background image
-         * @see NotificationCompat.WearableExtender#setBackground
-         */
-        public Bitmap getBackground() {
-            return mBackground;
-        }
-
-        /**
-         * Set an icon that goes with the content of this notification.
-         */
-        public WearableExtender setContentIcon(int icon) {
-            mContentIcon = icon;
-            return this;
-        }
-
-        /**
-         * Get an icon that goes with the content of this notification.
-         */
-        public int getContentIcon() {
-            return mContentIcon;
-        }
-
-        /**
-         * Set the gravity that the content icon should have within the notification display.
-         * Supported values include {@link android.view.Gravity#START} and
-         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
-         * @see #setContentIcon
-         */
-        public WearableExtender setContentIconGravity(int contentIconGravity) {
-            mContentIconGravity = contentIconGravity;
-            return this;
-        }
-
-        /**
-         * Get the gravity that the content icon should have within the notification display.
-         * Supported values include {@link android.view.Gravity#START} and
-         * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
-         * @see #getContentIcon
-         */
-        public int getContentIconGravity() {
-            return mContentIconGravity;
-        }
-
-        /**
-         * Set an action from this notification's actions to be clickable with the content of
-         * this notification. This action will no longer display separately from the
-         * notification's content.
-         *
-         * <p>For notifications with multiple pages, child pages can also have content actions
-         * set, although the list of available actions comes from the main notification and not
-         * from the child page's notification.
-         *
-         * @param actionIndex The index of the action to hoist onto the current notification page.
-         *                    If wearable actions were added to the main notification, this index
-         *                    will apply to that list, otherwise it will apply to the regular
-         *                    actions list.
-         */
-        public WearableExtender setContentAction(int actionIndex) {
-            mContentActionIndex = actionIndex;
-            return this;
-        }
-
-        /**
-         * Get the index of the notification action, if any, that was specified as being clickable
-         * with the content of this notification. This action will no longer display separately
-         * from the notification's content.
-         *
-         * <p>For notifications with multiple pages, child pages can also have content actions
-         * set, although the list of available actions comes from the main notification and not
-         * from the child page's notification.
-         *
-         * <p>If wearable specific actions were added to the main notification, this index will
-         * apply to that list, otherwise it will apply to the regular actions list.
-         *
-         * @return the action index or {@link #UNSET_ACTION_INDEX} if no action was selected.
-         */
-        public int getContentAction() {
-            return mContentActionIndex;
-        }
-
-        /**
-         * Set the gravity that this notification should have within the available viewport space.
-         * Supported values include {@link android.view.Gravity#TOP},
-         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
-         * The default value is {@link android.view.Gravity#BOTTOM}.
-         */
-        public WearableExtender setGravity(int gravity) {
-            mGravity = gravity;
-            return this;
-        }
-
-        /**
-         * Get the gravity that this notification should have within the available viewport space.
-         * Supported values include {@link android.view.Gravity#TOP},
-         * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
-         * The default value is {@link android.view.Gravity#BOTTOM}.
-         */
-        public int getGravity() {
-            return mGravity;
-        }
-
-        /**
-         * Set the custom size preset for the display of this notification out of the available
-         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
-         * {@link #SIZE_LARGE}.
-         * <p>Some custom size presets are only applicable for custom display notifications created
-         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. Check the
-         * documentation for the preset in question. See also
-         * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
-         */
-        public WearableExtender setCustomSizePreset(int sizePreset) {
-            mCustomSizePreset = sizePreset;
-            return this;
-        }
-
-        /**
-         * Get the custom size preset for the display of this notification out of the available
-         * presets found in {@link NotificationCompat.WearableExtender}, e.g.
-         * {@link #SIZE_LARGE}.
-         * <p>Some custom size presets are only applicable for custom display notifications created
-         * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
-         * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
-         */
-        public int getCustomSizePreset() {
-            return mCustomSizePreset;
-        }
-
-        /**
-         * Set the custom height in pixels for the display of this notification's content.
-         * <p>This option is only available for custom display notifications created
-         * using {@link NotificationCompat.WearableExtender#setDisplayIntent}. See also
-         * {@link NotificationCompat.WearableExtender#setCustomSizePreset} and
-         * {@link #getCustomContentHeight}.
-         */
-        public WearableExtender setCustomContentHeight(int height) {
-            mCustomContentHeight = height;
-            return this;
-        }
-
-        /**
-         * Get the custom height in pixels for the display of this notification's content.
-         * <p>This option is only available for custom display notifications created
-         * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
-         * {@link #setCustomContentHeight}.
-         */
-        public int getCustomContentHeight() {
-            return mCustomContentHeight;
-        }
-
-        /**
-         * Set whether the scrolling position for the contents of this notification should start
-         * at the bottom of the contents instead of the top when the contents are too long to
-         * display within the screen.  Default is false (start scroll at the top).
-         */
-        public WearableExtender setStartScrollBottom(boolean startScrollBottom) {
-            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
-            return this;
-        }
-
-        /**
-         * Get whether the scrolling position for the contents of this notification should start
-         * at the bottom of the contents instead of the top when the contents are too long to
-         * display within the screen. Default is false (start scroll at the top).
-         */
-        public boolean getStartScrollBottom() {
-            return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
-        }
-
-        /**
-         * Set whether the content intent is available when the wearable device is not connected
-         * to a companion device.  The user can still trigger this intent when the wearable device
-         * is offline, but a visual hint will indicate that the content intent may not be available.
-         * Defaults to true.
-         */
-        public WearableExtender setContentIntentAvailableOffline(
-                boolean contentIntentAvailableOffline) {
-            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
-            return this;
-        }
-
-        /**
-         * Get whether the content intent is available when the wearable device is not connected
-         * to a companion device.  The user can still trigger this intent when the wearable device
-         * is offline, but a visual hint will indicate that the content intent may not be available.
-         * Defaults to true.
-         */
-        public boolean getContentIntentAvailableOffline() {
-            return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
-        }
-
-        /**
-         * Set a hint that this notification's icon should not be displayed.
-         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
-         * @return this object for method chaining
-         */
-        public WearableExtender setHintHideIcon(boolean hintHideIcon) {
-            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
-            return this;
-        }
-
-        /**
-         * Get a hint that this notification's icon should not be displayed.
-         * @return {@code true} if this icon should not be displayed, false otherwise.
-         * The default value is {@code false} if this was never set.
-         */
-        public boolean getHintHideIcon() {
-            return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
-        }
-
-        /**
-         * Set a visual hint that only the background image of this notification should be
-         * displayed, and other semantic content should be hidden. This hint is only applicable
-         * to sub-pages added using {@link #addPage}.
-         */
-        public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
-            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
-            return this;
-        }
-
-        /**
-         * Get a visual hint that only the background image of this notification should be
-         * displayed, and other semantic content should be hidden. This hint is only applicable
-         * to sub-pages added using {@link NotificationCompat.WearableExtender#addPage}.
-         */
-        public boolean getHintShowBackgroundOnly() {
-            return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
-        }
-
-        /**
-         * Set a hint that this notification's background should not be clipped if possible,
-         * and should instead be resized to fully display on the screen, retaining the aspect
-         * ratio of the image. This can be useful for images like barcodes or qr codes.
-         * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
-         * @return this object for method chaining
-         */
-        public WearableExtender setHintAvoidBackgroundClipping(
-                boolean hintAvoidBackgroundClipping) {
-            setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
-            return this;
-        }
-
-        /**
-         * Get a hint that this notification's background should not be clipped if possible,
-         * and should instead be resized to fully display on the screen, retaining the aspect
-         * ratio of the image. This can be useful for images like barcodes or qr codes.
-         * @return {@code true} if it's ok if the background is clipped on the screen, false
-         * otherwise. The default value is {@code false} if this was never set.
-         */
-        public boolean getHintAvoidBackgroundClipping() {
-            return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
-        }
-
-        /**
-         * Set a hint that the screen should remain on for at least this duration when
-         * this notification is displayed on the screen.
-         * @param timeout The requested screen timeout in milliseconds. Can also be either
-         *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
-         * @return this object for method chaining
-         */
-        public WearableExtender setHintScreenTimeout(int timeout) {
-            mHintScreenTimeout = timeout;
-            return this;
-        }
-
-        /**
-         * Get the duration, in milliseconds, that the screen should remain on for
-         * when this notification is displayed.
-         * @return the duration in milliseconds if > 0, or either one of the sentinel values
-         *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
-         */
-        public int getHintScreenTimeout() {
-            return mHintScreenTimeout;
-        }
-
-        /**
-         * Set a hint that this notification's {@link BigPictureStyle} (if present) should be
-         * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
-         * qr codes, as well as other simple black-and-white tickets.
-         * @param hintAmbientBigPicture {@code true} to enable converstion and ambient.
-         * @return this object for method chaining
-         */
-        public WearableExtender setHintAmbientBigPicture(boolean hintAmbientBigPicture) {
-            setFlag(FLAG_BIG_PICTURE_AMBIENT, hintAmbientBigPicture);
-            return this;
-        }
-
-        /**
-         * Get a hint that this notification's {@link BigPictureStyle} (if present) should be
-         * converted to low-bit and displayed in ambient mode, especially useful for barcodes and
-         * qr codes, as well as other simple black-and-white tickets.
-         * @return {@code true} if it should be displayed in ambient, false otherwise
-         * otherwise. The default value is {@code false} if this was never set.
-         */
-        public boolean getHintAmbientBigPicture() {
-            return (mFlags & FLAG_BIG_PICTURE_AMBIENT) != 0;
-        }
-
-        /**
-         * Set a hint that this notification's content intent will launch an {@link Activity}
-         * directly, telling the platform that it can generate the appropriate transitions.
-         * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
-         * an activity and transitions should be generated, false otherwise.
-         * @return this object for method chaining
-         */
-        public WearableExtender setHintContentIntentLaunchesActivity(
-                boolean hintContentIntentLaunchesActivity) {
-            setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
-            return this;
-        }
-
-        /**
-         * Get a hint that this notification's content intent will launch an {@link Activity}
-         * directly, telling the platform that it can generate the appropriate transitions
-         * @return {@code true} if the content intent will launch an activity and transitions should
-         * be generated, false otherwise. The default value is {@code false} if this was never set.
-         */
-        public boolean getHintContentIntentLaunchesActivity() {
-            return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
-        }
-
-        /**
-         * Sets the dismissal id for this notification. If a notification is posted with a
-         * dismissal id, then when that notification is canceled, notifications on other wearables
-         * and the paired Android phone having that same dismissal id will also be canceled. See
-         * <a href="{@docRoot}wear/notifications/index.html">Adding Wearable Features to
-         * Notifications</a> for more information.
-         * @param dismissalId the dismissal id of the notification.
-         * @return this object for method chaining
-         */
-        public WearableExtender setDismissalId(String dismissalId) {
-            mDismissalId = dismissalId;
-            return this;
-        }
-
-        /**
-         * Returns the dismissal id of the notification.
-         * @return the dismissal id of the notification or null if it has not been set.
-         */
-        public String getDismissalId() {
-            return mDismissalId;
-        }
-
-        /**
-         * Sets a bridge tag for this notification. A bridge tag can be set for notifications
-         * posted from a phone to provide finer-grained control on what notifications are bridged
-         * to wearables. See <a href="{@docRoot}wear/notifications/index.html">Adding Wearable
-         * Features to Notifications</a> for more information.
-         * @param bridgeTag the bridge tag of the notification.
-         * @return this object for method chaining
-         */
-        public WearableExtender setBridgeTag(String bridgeTag) {
-            mBridgeTag = bridgeTag;
-            return this;
-        }
-
-        /**
-         * Returns the bridge tag of the notification.
-         * @return the bridge tag or null if not present.
-         */
-        public String getBridgeTag() {
-            return mBridgeTag;
-        }
-
-        private void setFlag(int mask, boolean value) {
-            if (value) {
-                mFlags |= mask;
-            } else {
-                mFlags &= ~mask;
-            }
-        }
-    }
-
-    /**
-     * <p>Helper class to add Android Auto extensions to notifications. To create a notification
-     * with car extensions:
-     *
-     * <ol>
-     *  <li>Create an {@link NotificationCompat.Builder}, setting any desired
-     *  properties.
-     *  <li>Create a {@link CarExtender}.
-     *  <li>Set car-specific properties using the {@code add} and {@code set} methods of
-     *  {@link CarExtender}.
-     *  <li>Call {@link android.support.v4.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)}
-     *  to apply the extensions to a notification.
-     *  <li>Post the notification to the notification system with the
-     *  {@code NotificationManagerCompat.notify(...)} methods and not the
-     *  {@code NotificationManager.notify(...)} methods.
-     * </ol>
-     *
-     * <pre class="prettyprint">
-     * Notification notification = new NotificationCompat.Builder(context)
-     *         ...
-     *         .extend(new CarExtender()
-     *                 .set*(...))
-     *         .build();
-     * </pre>
-     *
-     * <p>Car extensions can be accessed on an existing notification by using the
-     * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
-     * to access values.
-     */
-    public static final class CarExtender implements Extender {
-        private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
-        private static final String EXTRA_LARGE_ICON = "large_icon";
-        private static final String EXTRA_CONVERSATION = "car_conversation";
-        private static final String EXTRA_COLOR = "app_color";
-
-        private static final String KEY_AUTHOR = "author";
-        private static final String KEY_TEXT = "text";
-        private static final String KEY_MESSAGES = "messages";
-        private static final String KEY_REMOTE_INPUT = "remote_input";
-        private static final String KEY_ON_REPLY = "on_reply";
-        private static final String KEY_ON_READ = "on_read";
-        private static final String KEY_PARTICIPANTS = "participants";
-        private static final String KEY_TIMESTAMP = "timestamp";
-
-        private Bitmap mLargeIcon;
-        private UnreadConversation mUnreadConversation;
-        private int mColor = NotificationCompat.COLOR_DEFAULT;
-
-        /**
-         * Create a {@link CarExtender} with default options.
-         */
-        public CarExtender() {
-        }
-
-        /**
-         * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
-         *
-         * @param notification The notification from which to copy options.
-         */
-        public CarExtender(Notification notification) {
-            if (Build.VERSION.SDK_INT < 21) {
-                return;
-            }
-
-            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);
-
-                Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
-                mUnreadConversation = getUnreadConversationFromBundle(b);
-            }
-        }
-
-        @RequiresApi(21)
-        private static UnreadConversation getUnreadConversationFromBundle(@Nullable Bundle b) {
-            if (b == null) {
-                return null;
-            }
-            Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
-            String[] messages = null;
-            if (parcelableMessages != null) {
-                String[] tmp = new String[parcelableMessages.length];
-                boolean success = true;
-                for (int i = 0; i < tmp.length; i++) {
-                    if (!(parcelableMessages[i] instanceof Bundle)) {
-                        success = false;
-                        break;
-                    }
-                    tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
-                    if (tmp[i] == null) {
-                        success = false;
-                        break;
-                    }
-                }
-                if (success) {
-                    messages = tmp;
-                } else {
-                    return null;
-                }
-            }
-
-            PendingIntent onRead = b.getParcelable(KEY_ON_READ);
-            PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
-
-            android.app.RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
-
-            String[] participants = b.getStringArray(KEY_PARTICIPANTS);
-            if (participants == null || participants.length != 1) {
-                return null;
-            }
-
-            RemoteInput remoteInputCompat = remoteInput != null
-                    ? new RemoteInput(remoteInput.getResultKey(),
-                    remoteInput.getLabel(),
-                    remoteInput.getChoices(),
-                    remoteInput.getAllowFreeFormInput(),
-                    remoteInput.getExtras(),
-                    null /* allowedDataTypes */)
-                    : null;
-
-            return new UnreadConversation(messages, remoteInputCompat, onReply,
-                    onRead, participants, b.getLong(KEY_TIMESTAMP));
-        }
-
-        @RequiresApi(21)
-        private static Bundle getBundleForUnreadConversation(@NonNull UnreadConversation uc) {
-            Bundle b = new Bundle();
-            String author = null;
-            if (uc.getParticipants() != null && uc.getParticipants().length > 1) {
-                author = uc.getParticipants()[0];
-            }
-            Parcelable[] messages = new Parcelable[uc.getMessages().length];
-            for (int i = 0; i < messages.length; i++) {
-                Bundle m = new Bundle();
-                m.putString(KEY_TEXT, uc.getMessages()[i]);
-                m.putString(KEY_AUTHOR, author);
-                messages[i] = m;
-            }
-            b.putParcelableArray(KEY_MESSAGES, messages);
-            RemoteInput remoteInputCompat = uc.getRemoteInput();
-            if (remoteInputCompat != null) {
-                android.app.RemoteInput remoteInput =
-                        new android.app.RemoteInput.Builder(remoteInputCompat.getResultKey())
-                                .setLabel(remoteInputCompat.getLabel())
-                                .setChoices(remoteInputCompat.getChoices())
-                                .setAllowFreeFormInput(remoteInputCompat.getAllowFreeFormInput())
-                                .addExtras(remoteInputCompat.getExtras())
-                                .build();
-                b.putParcelable(KEY_REMOTE_INPUT, remoteInput);
-            }
-            b.putParcelable(KEY_ON_REPLY, uc.getReplyPendingIntent());
-            b.putParcelable(KEY_ON_READ, uc.getReadPendingIntent());
-            b.putStringArray(KEY_PARTICIPANTS, uc.getParticipants());
-            b.putLong(KEY_TIMESTAMP, uc.getLatestTimestamp());
-            return b;
-        }
-
-        /**
-         * Apply car extensions to a notification that is being built. This is typically called by
-         * the {@link android.support.v4.app.NotificationCompat.Builder#extend(NotificationCompat.Extender)}
-         * method of {@link NotificationCompat.Builder}.
-         */
-        @Override
-        public NotificationCompat.Builder extend(NotificationCompat.Builder builder) {
-            if (Build.VERSION.SDK_INT < 21) {
-                return builder;
-            }
-
-            Bundle carExtensions = new Bundle();
-
-            if (mLargeIcon != null) {
-                carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
-            }
-            if (mColor != NotificationCompat.COLOR_DEFAULT) {
-                carExtensions.putInt(EXTRA_COLOR, mColor);
-            }
-
-            if (mUnreadConversation != null) {
-                Bundle b = getBundleForUnreadConversation(mUnreadConversation);
-                carExtensions.putBundle(EXTRA_CONVERSATION, b);
-            }
-
-            builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
-            return builder;
-        }
-
-        /**
-         * Sets the accent color to use when Android Auto presents the notification.
-         *
-         * Android Auto uses the color set with {@link android.support.v4.app.NotificationCompat.Builder#setColor(int)}
-         * to accent the displayed notification. However, not all colors are acceptable in an
-         * automotive setting. This method can be used to override the color provided in the
-         * notification in such a situation.
-         */
-        public CarExtender setColor(@ColorInt int color) {
-            mColor = color;
-            return this;
-        }
-
-        /**
-         * Gets the accent color.
-         *
-         * @see #setColor
-         */
-        @ColorInt
-        public int getColor() {
-            return mColor;
-        }
-
-        /**
-         * Sets the large icon of the car notification.
-         *
-         * If no large icon is set in the extender, Android Auto will display the icon
-         * specified by {@link android.support.v4.app.NotificationCompat.Builder#setLargeIcon(android.graphics.Bitmap)}
-         *
-         * @param largeIcon The large icon to use in the car notification.
-         * @return This object for method chaining.
-         */
-        public CarExtender setLargeIcon(Bitmap largeIcon) {
-            mLargeIcon = largeIcon;
-            return this;
-        }
-
-        /**
-         * Gets the large icon used in this car notification, or null if no icon has been set.
-         *
-         * @return The large icon for the car notification.
-         * @see CarExtender#setLargeIcon
-         */
-        public Bitmap getLargeIcon() {
-            return mLargeIcon;
-        }
-
-        /**
-         * Sets the unread conversation in a message notification.
-         *
-         * @param unreadConversation The unread part of the conversation this notification conveys.
-         * @return This object for method chaining.
-         */
-        public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
-            mUnreadConversation = unreadConversation;
-            return this;
-        }
-
-        /**
-         * Returns the unread conversation conveyed by this notification.
-         * @see #setUnreadConversation(UnreadConversation)
-         */
-        public UnreadConversation getUnreadConversation() {
-            return mUnreadConversation;
-        }
-
-        /**
-         * A class which holds the unread messages from a conversation.
-         */
-        public static class UnreadConversation {
-            private final String[] mMessages;
-            private final RemoteInput mRemoteInput;
-            private final PendingIntent mReplyPendingIntent;
-            private final PendingIntent mReadPendingIntent;
-            private final String[] mParticipants;
-            private final long mLatestTimestamp;
-
-            UnreadConversation(String[] messages, RemoteInput remoteInput,
-                    PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
-                    String[] participants, long latestTimestamp) {
-                mMessages = messages;
-                mRemoteInput = remoteInput;
-                mReadPendingIntent = readPendingIntent;
-                mReplyPendingIntent = replyPendingIntent;
-                mParticipants = participants;
-                mLatestTimestamp = latestTimestamp;
-            }
-
-            /**
-             * Gets the list of messages conveyed by this notification.
-             */
-            public String[] getMessages() {
-                return mMessages;
-            }
-
-            /**
-             * Gets the remote input that will be used to convey the response to a message list, or
-             * null if no such remote input exists.
-             */
-            public RemoteInput getRemoteInput() {
-                return mRemoteInput;
-            }
-
-            /**
-             * Gets the pending intent that will be triggered when the user replies to this
-             * notification.
-             */
-            public PendingIntent getReplyPendingIntent() {
-                return mReplyPendingIntent;
-            }
-
-            /**
-             * Gets the pending intent that Android Auto will send after it reads aloud all messages
-             * in this object's message list.
-             */
-            public PendingIntent getReadPendingIntent() {
-                return mReadPendingIntent;
-            }
-
-            /**
-             * Gets the participants in the conversation.
-             */
-            public String[] getParticipants() {
-                return mParticipants;
-            }
-
-            /**
-             * Gets the firs participant in the conversation.
-             */
-            public String getParticipant() {
-                return mParticipants.length > 0 ? mParticipants[0] : null;
-            }
-
-            /**
-             * Gets the timestamp of the conversation.
-             */
-            public long getLatestTimestamp() {
-                return mLatestTimestamp;
-            }
-
-            /**
-             * Builder class for {@link CarExtender.UnreadConversation} objects.
-             */
-            public static class Builder {
-                private final List<String> mMessages = new ArrayList<String>();
-                private final String mParticipant;
-                private RemoteInput mRemoteInput;
-                private PendingIntent mReadPendingIntent;
-                private PendingIntent mReplyPendingIntent;
-                private long mLatestTimestamp;
-
-                /**
-                 * Constructs a new builder for {@link CarExtender.UnreadConversation}.
-                 *
-                 * @param name The name of the other participant in the conversation.
-                 */
-                public Builder(String name) {
-                    mParticipant = name;
-                }
-
-                /**
-                 * Appends a new unread message to the list of messages for this conversation.
-                 *
-                 * The messages should be added from oldest to newest.
-                 *
-                 * @param message The text of the new unread message.
-                 * @return This object for method chaining.
-                 */
-                public Builder addMessage(String message) {
-                    mMessages.add(message);
-                    return this;
-                }
-
-                /**
-                 * Sets the pending intent and remote input which will convey the reply to this
-                 * notification.
-                 *
-                 * @param pendingIntent The pending intent which will be triggered on a reply.
-                 * @param remoteInput The remote input parcelable which will carry the reply.
-                 * @return This object for method chaining.
-                 *
-                 * @see CarExtender.UnreadConversation#getRemoteInput
-                 * @see CarExtender.UnreadConversation#getReplyPendingIntent
-                 */
-                public Builder setReplyAction(
-                        PendingIntent pendingIntent, RemoteInput remoteInput) {
-                    mRemoteInput = remoteInput;
-                    mReplyPendingIntent = pendingIntent;
-
-                    return this;
-                }
-
-                /**
-                 * Sets the pending intent that will be sent once the messages in this notification
-                 * are read.
-                 *
-                 * @param pendingIntent The pending intent to use.
-                 * @return This object for method chaining.
-                 */
-                public Builder setReadPendingIntent(PendingIntent pendingIntent) {
-                    mReadPendingIntent = pendingIntent;
-                    return this;
-                }
-
-                /**
-                 * Sets the timestamp of the most recent message in an unread conversation.
-                 *
-                 * If a messaging notification has been posted by your application and has not
-                 * yet been cancelled, posting a later notification with the same id and tag
-                 * but without a newer timestamp may result in Android Auto not displaying a
-                 * heads up notification for the later notification.
-                 *
-                 * @param timestamp The timestamp of the most recent message in the conversation.
-                 * @return This object for method chaining.
-                 */
-                public Builder setLatestTimestamp(long timestamp) {
-                    mLatestTimestamp = timestamp;
-                    return this;
-                }
-
-                /**
-                 * Builds a new unread conversation object.
-                 *
-                 * @return The new unread conversation object.
-                 */
-                public UnreadConversation build() {
-                    String[] messages = mMessages.toArray(new String[mMessages.size()]);
-                    String[] participants = { mParticipant };
-                    return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
-                            mReadPendingIntent, participants, mLatestTimestamp);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Get an array of Notification objects from a parcelable array bundle field.
-     * Update the bundle to have a typed array so fetches in the future don't need
-     * to do an array copy.
-     */
-    static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
-        Parcelable[] array = bundle.getParcelableArray(key);
-        if (array instanceof Notification[] || array == null) {
-            return (Notification[]) array;
-        }
-        Notification[] typedArray = new Notification[array.length];
-        for (int i = 0; i < array.length; i++) {
-            typedArray[i] = (Notification) array[i];
-        }
-        bundle.putParcelableArray(key, typedArray);
-        return typedArray;
-    }
-
-    /**
-     * Gets the {@link Notification#extras} field from a notification in a backwards
-     * 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 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 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 notification The notification to inspect.
-     * @param actionIndex The index of the action to retrieve.
-     */
-    public static Action getAction(Notification notification, int actionIndex) {
-        if (Build.VERSION.SDK_INT >= 20) {
-            return getActionCompatFromAction(notification.actions[actionIndex]);
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            Notification.Action action = notification.actions[actionIndex];
-            Bundle actionExtras = null;
-            SparseArray<Bundle> actionExtrasMap = notification.extras.getSparseParcelableArray(
-                    NotificationCompatExtras.EXTRA_ACTION_EXTRAS);
-            if (actionExtrasMap != null) {
-                actionExtras = actionExtrasMap.get(actionIndex);
-            }
-            return NotificationCompatJellybean.readAction(action.icon, action.title,
-                    action.actionIntent, actionExtras);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return NotificationCompatJellybean.getAction(notification, actionIndex);
-        } else {
-            return null;
-        }
-    }
-
-    @RequiresApi(20)
-    static Action getActionCompatFromAction(Notification.Action action) {
-        final RemoteInput[] remoteInputs;
-        final android.app.RemoteInput[] srcArray = action.getRemoteInputs();
-        if (srcArray == null) {
-            remoteInputs = null;
-        } else {
-            remoteInputs = new RemoteInput[srcArray.length];
-            for (int i = 0; i < srcArray.length; i++) {
-                android.app.RemoteInput src = srcArray[i];
-                remoteInputs[i] = new RemoteInput(src.getResultKey(), src.getLabel(),
-                        src.getChoices(), src.getAllowFreeFormInput(), src.getExtras(), null);
-            }
-        }
-
-        final boolean allowGeneratedReplies;
-        if (Build.VERSION.SDK_INT >= 24) {
-            allowGeneratedReplies = action.getExtras().getBoolean(
-                    NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES)
-                    || action.getAllowGeneratedReplies();
-        } else {
-            allowGeneratedReplies = action.getExtras().getBoolean(
-                    NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES);
-        }
-
-        final boolean showsUserInterface =
-                action.getExtras().getBoolean(Action.EXTRA_SHOWS_USER_INTERFACE, true);
-
-        final @Action.SemanticAction int semanticAction;
-        if (Build.VERSION.SDK_INT >= 28) {
-            semanticAction = action.getSemanticAction();
-        } else {
-            semanticAction = action.getExtras().getInt(
-                    Action.EXTRA_SEMANTIC_ACTION, Action.SEMANTIC_ACTION_NONE);
-        }
-
-        return new Action(action.icon, action.title, action.actionIntent,
-                action.getExtras(), remoteInputs, null, allowGeneratedReplies,
-                semanticAction, showsUserInterface);
-    }
-
-    /**
-     * Get the category of this notification in a backwards compatible
-     * manner.
-     * @param notification The notification to inspect.
-     */
-    public static String getCategory(Notification notification) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return notification.category;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Get whether or not this notification is only relevant to the current device.
-     *
-     * <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 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 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;
-        }
-    }
-
-    /**
-     * Get whether this notification to be the group summary for a group of notifications.
-     * Grouped notifications may display in a cluster or stack on devices which
-     * 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 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;
-        }
-    }
-
-    /**
-     * Get a sort key that orders this notification among other notifications from the
-     * same package. This can be useful if an external sort was already applied and an app
-     * would like to preserve this. Notifications will be sorted lexicographically using this
-     * value, although providing different priorities in addition to providing sort key may
-     * cause this value to be ignored.
-     *
-     * <p>This sort key can also be used to order members of a notification group. See
-     * {@link Builder#setGroup}.
-     *
-     * @see String#compareTo(String)
-     */
-    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}.
-     */
-    @GroupAlertBehavior
-    public static int getGroupAlertBehavior(Notification notification) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            return notification.getGroupAlertBehavior();
-        } else {
-            return GROUP_ALERT_ALL;
-        }
-    }
-}
diff --git a/android/support/v4/app/NotificationCompatBuilder.java b/android/support/v4/app/NotificationCompatBuilder.java
deleted file mode 100644
index e5fb4f9..0000000
--- a/android/support/v4/app/NotificationCompatBuilder.java
+++ /dev/null
@@ -1,399 +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.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-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.os.Build;
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-import android.text.TextUtils;
-import android.util.SparseArray;
-import android.widget.RemoteViews;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Wrapper around {@link Notification.Builder} that works in a backwards compatible way.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-class NotificationCompatBuilder implements NotificationBuilderWithBuilderAccessor {
-    private final Notification.Builder mBuilder;
-    private final NotificationCompat.Builder mBuilderCompat;
-
-    // @RequiresApi(16) - uncomment when lint bug is fixed.
-    private RemoteViews mContentView;
-    // @RequiresApi(16) - uncomment when lint bug is fixed.
-    private RemoteViews mBigContentView;
-    // @RequiresApi(16) - uncomment when lint bug is fixed.
-    private final List<Bundle> mActionExtrasList = new ArrayList<>();
-    // @RequiresApi(16) - uncomment when lint bug is fixed.
-    private final Bundle mExtras = new Bundle();
-    // @RequiresApi(20) - uncomment when lint bug is fixed.
-    private int mGroupAlertBehavior;
-    // @RequiresApi(21) - uncomment when lint bug is fixed.
-    private RemoteViews mHeadsUpContentView;
-
-    NotificationCompatBuilder(NotificationCompat.Builder b) {
-        mBuilderCompat = b;
-        if (Build.VERSION.SDK_INT >= 26) {
-            mBuilder = new Notification.Builder(b.mContext, b.mChannelId);
-        } else {
-            mBuilder = new Notification.Builder(b.mContext);
-        }
-        Notification n = b.mNotification;
-        mBuilder.setWhen(n.when)
-                .setSmallIcon(n.icon, n.iconLevel)
-                .setContent(n.contentView)
-                .setTicker(n.tickerText, b.mTickerView)
-                .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(b.mContentTitle)
-                .setContentText(b.mContentText)
-                .setContentInfo(b.mContentInfo)
-                .setContentIntent(b.mContentIntent)
-                .setDeleteIntent(n.deleteIntent)
-                .setFullScreenIntent(b.mFullScreenIntent,
-                        (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
-                .setLargeIcon(b.mLargeIcon)
-                .setNumber(b.mNumber)
-                .setProgress(b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
-        if (Build.VERSION.SDK_INT < 21) {
-            mBuilder.setSound(n.sound, n.audioStreamType);
-        }
-        if (Build.VERSION.SDK_INT >= 16) {
-            mBuilder.setSubText(b.mSubText)
-                    .setUsesChronometer(b.mUseChronometer)
-                    .setPriority(b.mPriority);
-
-            for (NotificationCompat.Action action : b.mActions) {
-                addAction(action);
-            }
-
-            if (b.mExtras != null) {
-                mExtras.putAll(b.mExtras);
-            }
-            if (Build.VERSION.SDK_INT < 20) {
-                if (b.mLocalOnly) {
-                    mExtras.putBoolean(NotificationCompatExtras.EXTRA_LOCAL_ONLY, true);
-                }
-                if (b.mGroupKey != null) {
-                    mExtras.putString(NotificationCompatExtras.EXTRA_GROUP_KEY, b.mGroupKey);
-                    if (b.mGroupSummary) {
-                        mExtras.putBoolean(NotificationCompatExtras.EXTRA_GROUP_SUMMARY, true);
-                    } else {
-                        mExtras.putBoolean(
-                                NotificationManagerCompat.EXTRA_USE_SIDE_CHANNEL, true);
-                    }
-                }
-                if (b.mSortKey != null) {
-                    mExtras.putString(NotificationCompatExtras.EXTRA_SORT_KEY, b.mSortKey);
-                }
-            }
-
-            mContentView = b.mContentView;
-            mBigContentView = b.mBigContentView;
-        }
-        if (Build.VERSION.SDK_INT >= 19) {
-            mBuilder.setShowWhen(b.mShowWhen);
-
-            if (Build.VERSION.SDK_INT < 21) {
-                if (b.mPeople != null && !b.mPeople.isEmpty()) {
-                    mExtras.putStringArray(Notification.EXTRA_PEOPLE,
-                            b.mPeople.toArray(new String[b.mPeople.size()]));
-                }
-            }
-        }
-        if (Build.VERSION.SDK_INT >= 20) {
-            mBuilder.setLocalOnly(b.mLocalOnly)
-                    .setGroup(b.mGroupKey)
-                    .setGroupSummary(b.mGroupSummary)
-                    .setSortKey(b.mSortKey);
-
-            mGroupAlertBehavior = b.mGroupAlertBehavior;
-        }
-        if (Build.VERSION.SDK_INT >= 21) {
-            mBuilder.setCategory(b.mCategory)
-                    .setColor(b.mColor)
-                    .setVisibility(b.mVisibility)
-                    .setPublicVersion(b.mPublicVersion)
-                    .setSound(n.sound, n.audioAttributes);
-
-            for (String person: b.mPeople) {
-                mBuilder.addPerson(person);
-            }
-            mHeadsUpContentView = b.mHeadsUpContentView;
-        }
-        if (Build.VERSION.SDK_INT >= 24) {
-            mBuilder.setExtras(b.mExtras)
-                    .setRemoteInputHistory(b.mRemoteInputHistory);
-            if (b.mContentView != null) {
-                mBuilder.setCustomContentView(b.mContentView);
-            }
-            if (b.mBigContentView != null) {
-                mBuilder.setCustomBigContentView(b.mBigContentView);
-            }
-            if (b.mHeadsUpContentView != null) {
-                mBuilder.setCustomHeadsUpContentView(b.mHeadsUpContentView);
-            }
-        }
-        if (Build.VERSION.SDK_INT >= 26) {
-            mBuilder.setBadgeIconType(b.mBadgeIcon)
-                    .setShortcutId(b.mShortcutId)
-                    .setTimeoutAfter(b.mTimeout)
-                    .setGroupAlertBehavior(b.mGroupAlertBehavior);
-            if (b.mColorizedSet) {
-                mBuilder.setColorized(b.mColorized);
-            }
-
-            if (!TextUtils.isEmpty(b.mChannelId)) {
-                mBuilder.setSound(null)
-                        .setDefaults(0)
-                        .setLights(0, 0, 0)
-                        .setVibrate(null);
-            }
-        }
-    }
-
-    @Override
-    public Notification.Builder getBuilder() {
-        return mBuilder;
-    }
-
-    public Notification build() {
-        final NotificationCompat.Style style = mBuilderCompat.mStyle;
-        if (style != null) {
-            style.apply(this);
-        }
-
-        RemoteViews styleContentView = style != null
-                ? style.makeContentView(this)
-                : null;
-        Notification n = buildInternal();
-        if (styleContentView != null) {
-            n.contentView = styleContentView;
-        } else if (mBuilderCompat.mContentView != null) {
-            n.contentView = mBuilderCompat.mContentView;
-        }
-        if (Build.VERSION.SDK_INT >= 16 && style != null) {
-            RemoteViews styleBigContentView = style.makeBigContentView(this);
-            if (styleBigContentView != null) {
-                n.bigContentView = styleBigContentView;
-            }
-        }
-        if (Build.VERSION.SDK_INT >= 21 && style != null) {
-            RemoteViews styleHeadsUpContentView =
-                    mBuilderCompat.mStyle.makeHeadsUpContentView(this);
-            if (styleHeadsUpContentView != null) {
-                n.headsUpContentView = styleHeadsUpContentView;
-            }
-        }
-
-        if (Build.VERSION.SDK_INT >= 16 && style != null) {
-            Bundle extras = NotificationCompat.getExtras(n);
-            if (extras != null) {
-                style.addCompatExtras(extras);
-            }
-        }
-
-        return n;
-    }
-
-    private void addAction(NotificationCompat.Action action) {
-        if (Build.VERSION.SDK_INT >= 20) {
-            Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
-                    action.getIcon(), action.getTitle(), action.getActionIntent());
-            if (action.getRemoteInputs() != null) {
-                for (android.app.RemoteInput remoteInput : RemoteInput.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());
-            if (Build.VERSION.SDK_INT >= 24) {
-                actionBuilder.setAllowGeneratedReplies(action.getAllowGeneratedReplies());
-            }
-
-            actionExtras.putInt(NotificationCompat.Action.EXTRA_SEMANTIC_ACTION,
-                    action.getSemanticAction());
-            if (Build.VERSION.SDK_INT >= 28) {
-                actionBuilder.setSemanticAction(action.getSemanticAction());
-            }
-
-            actionExtras.putBoolean(NotificationCompat.Action.EXTRA_SHOWS_USER_INTERFACE,
-                    action.getShowsUserInterface());
-            actionBuilder.addExtras(actionExtras);
-            mBuilder.addAction(actionBuilder.build());
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            mActionExtrasList.add(
-                    NotificationCompatJellybean.writeActionAndGetExtras(mBuilder, action));
-        }
-    }
-
-    protected Notification buildInternal() {
-        if (Build.VERSION.SDK_INT >= 26) {
-            return mBuilder.build();
-        } else if (Build.VERSION.SDK_INT >= 24) {
-            Notification notification =  mBuilder.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;
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            mBuilder.setExtras(mExtras);
-            Notification notification = mBuilder.build();
-            if (mContentView != null) {
-                notification.contentView = mContentView;
-            }
-            if (mBigContentView != null) {
-                notification.bigContentView = mBigContentView;
-            }
-            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;
-        } else if (Build.VERSION.SDK_INT >= 20) {
-            mBuilder.setExtras(mExtras);
-            Notification notification = mBuilder.build();
-            if (mContentView != null) {
-                notification.contentView = mContentView;
-            }
-            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;
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            SparseArray<Bundle> actionExtrasMap =
-                    NotificationCompatJellybean.buildActionExtrasMap(mActionExtrasList);
-            if (actionExtrasMap != null) {
-                // Add the action extras sparse array if any action was added with extras.
-                mExtras.putSparseParcelableArray(
-                        NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
-            }
-            mBuilder.setExtras(mExtras);
-            Notification notification = mBuilder.build();
-            if (mContentView != null) {
-                notification.contentView = mContentView;
-            }
-            if (mBigContentView != null) {
-                notification.bigContentView = mBigContentView;
-            }
-            return notification;
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            Notification notification = mBuilder.build();
-            // Merge in developer provided extras, but let the values already set
-            // for keys take precedence.
-            Bundle extras = NotificationCompat.getExtras(notification);
-            Bundle mergeBundle = new Bundle(mExtras);
-            for (String key : mExtras.keySet()) {
-                if (extras.containsKey(key)) {
-                    mergeBundle.remove(key);
-                }
-            }
-            extras.putAll(mergeBundle);
-            SparseArray<Bundle> actionExtrasMap =
-                    NotificationCompatJellybean.buildActionExtrasMap(mActionExtrasList);
-            if (actionExtrasMap != null) {
-                // Add the action extras sparse array if any action was added with extras.
-                NotificationCompat.getExtras(notification).putSparseParcelableArray(
-                        NotificationCompatExtras.EXTRA_ACTION_EXTRAS, actionExtrasMap);
-            }
-            if (mContentView != null) {
-                notification.contentView = mContentView;
-            }
-            if (mBigContentView != null) {
-                notification.bigContentView = mBigContentView;
-            }
-            return notification;
-        } else {
-            //noinspection deprecation
-            return mBuilder.getNotification();
-        }
-    }
-
-    private void removeSoundAndVibration(Notification notification) {
-        notification.sound = null;
-        notification.vibrate = null;
-        notification.defaults &= ~DEFAULT_SOUND;
-        notification.defaults &= ~DEFAULT_VIBRATE;
-    }
-}
diff --git a/android/support/v4/app/NotificationCompatExtras.java b/android/support/v4/app/NotificationCompatExtras.java
deleted file mode 100644
index 33bdd06..0000000
--- a/android/support/v4/app/NotificationCompatExtras.java
+++ /dev/null
@@ -1,67 +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;
-
-/**
- * Well-known extras used by {@link NotificationCompat} for backwards compatibility.
- */
-public final class NotificationCompatExtras {
-    /**
-     * Extras key used internally by {@link NotificationCompat} to store the value of
-     * 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 = "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 = "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 = "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 = "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 = "android.support.actionExtras";
-
-    /**
-     * Extras key used internally by {@link NotificationCompat} to store the value of
-     * the {@link android.app.Notification.Action#getRemoteInputs} before the field
-     * was available.
-     * If possible, use {@link NotificationCompat.Action#getRemoteInputs} to access this field.
-     */
-    public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
-
-    private NotificationCompatExtras() {}
-}
diff --git a/android/support/v4/app/NotificationCompatJellybean.java b/android/support/v4/app/NotificationCompatJellybean.java
deleted file mode 100644
index 82f8941..0000000
--- a/android/support/v4/app/NotificationCompatJellybean.java
+++ /dev/null
@@ -1,336 +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.os.Bundle;
-import android.os.Parcelable;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@RequiresApi(16)
-class NotificationCompatJellybean {
-    public static final String TAG = "NotificationCompat";
-
-    // Extras keys used for Jellybean SDK and above.
-    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
-    private static final String KEY_ICON = "icon";
-    private static final String KEY_TITLE = "title";
-    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_DATA_ONLY_REMOTE_INPUTS = "dataOnlyRemoteInputs";
-    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_ALLOWED_DATA_TYPES = "allowedDataTypes";
-
-    private static final Object sExtrasLock = new Object();
-    private static Field sExtrasField;
-    private static boolean sExtrasFieldAccessFailed;
-
-    private static final Object sActionsLock = new Object();
-    private static Class<?> sActionClass;
-    private static Field sActionsField;
-    private static Field sActionIconField;
-    private static Field sActionTitleField;
-    private static Field sActionIntentField;
-    private static boolean sActionsAccessFailed;
-
-    /** Return an SparseArray for action extras or null if none was needed. */
-    public static SparseArray<Bundle> buildActionExtrasMap(List<Bundle> actionExtrasList) {
-        SparseArray<Bundle> actionExtrasMap = null;
-        for (int i = 0, count = actionExtrasList.size(); i < count; i++) {
-            Bundle actionExtras = actionExtrasList.get(i);
-            if (actionExtras != null) {
-                if (actionExtrasMap == null) {
-                    actionExtrasMap = new SparseArray<Bundle>();
-                }
-                actionExtrasMap.put(i, actionExtras);
-            }
-        }
-        return actionExtrasMap;
-    }
-
-    /**
-     * Get the extras Bundle from a notification using reflection. Extras were present in
-     * Jellybean notifications, but the field was private until KitKat.
-     */
-    public static Bundle getExtras(Notification notif) {
-        synchronized (sExtrasLock) {
-            if (sExtrasFieldAccessFailed) {
-                return null;
-            }
-            try {
-                if (sExtrasField == null) {
-                    Field extrasField = Notification.class.getDeclaredField("extras");
-                    if (!Bundle.class.isAssignableFrom(extrasField.getType())) {
-                        Log.e(TAG, "Notification.extras field is not of type Bundle");
-                        sExtrasFieldAccessFailed = true;
-                        return null;
-                    }
-                    extrasField.setAccessible(true);
-                    sExtrasField = extrasField;
-                }
-                Bundle extras = (Bundle) sExtrasField.get(notif);
-                if (extras == null) {
-                    extras = new Bundle();
-                    sExtrasField.set(notif, extras);
-                }
-                return extras;
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Unable to access notification extras", e);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "Unable to access notification extras", e);
-            }
-            sExtrasFieldAccessFailed = true;
-            return null;
-        }
-    }
-
-    public static NotificationCompat.Action readAction(int icon, CharSequence title,
-            PendingIntent actionIntent, Bundle extras) {
-        RemoteInput[] remoteInputs = null;
-        RemoteInput[] dataOnlyRemoteInputs = null;
-        boolean allowGeneratedReplies = false;
-        if (extras != null) {
-            remoteInputs = fromBundleArray(
-                    getBundleArrayFromBundle(extras,
-                            NotificationCompatExtras.EXTRA_REMOTE_INPUTS));
-            dataOnlyRemoteInputs = fromBundleArray(
-                    getBundleArrayFromBundle(extras, EXTRA_DATA_ONLY_REMOTE_INPUTS));
-            allowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES);
-        }
-        return new NotificationCompat.Action(icon, title, actionIntent, extras, remoteInputs,
-                dataOnlyRemoteInputs, allowGeneratedReplies,
-                NotificationCompat.Action.SEMANTIC_ACTION_NONE, true);
-    }
-
-    public static Bundle writeActionAndGetExtras(
-            Notification.Builder builder, NotificationCompat.Action action) {
-        builder.addAction(action.getIcon(), action.getTitle(), action.getActionIntent());
-        Bundle actionExtras = new Bundle(action.getExtras());
-        if (action.getRemoteInputs() != null) {
-            actionExtras.putParcelableArray(NotificationCompatExtras.EXTRA_REMOTE_INPUTS,
-                    toBundleArray(action.getRemoteInputs()));
-        }
-        if (action.getDataOnlyRemoteInputs() != null) {
-            actionExtras.putParcelableArray(EXTRA_DATA_ONLY_REMOTE_INPUTS,
-                    toBundleArray(action.getDataOnlyRemoteInputs()));
-        }
-        actionExtras.putBoolean(EXTRA_ALLOW_GENERATED_REPLIES,
-                action.getAllowGeneratedReplies());
-        return actionExtras;
-    }
-
-    public static int getActionCount(Notification notif) {
-        synchronized (sActionsLock) {
-            Object[] actionObjects = getActionObjectsLocked(notif);
-            return actionObjects != null ? actionObjects.length : 0;
-        }
-    }
-
-    public static NotificationCompat.Action getAction(Notification notif, int actionIndex) {
-        synchronized (sActionsLock) {
-            try {
-                Object[] actionObjects = getActionObjectsLocked(notif);
-                if (actionObjects != null) {
-                    Object actionObject = actionObjects[actionIndex];
-                    Bundle actionExtras = null;
-                    Bundle extras = getExtras(notif);
-                    if (extras != null) {
-                        SparseArray<Bundle> actionExtrasMap = extras.getSparseParcelableArray(
-                                NotificationCompatExtras.EXTRA_ACTION_EXTRAS);
-                        if (actionExtrasMap != null) {
-                            actionExtras = actionExtrasMap.get(actionIndex);
-                        }
-                    }
-                    return readAction(sActionIconField.getInt(actionObject),
-                            (CharSequence) sActionTitleField.get(actionObject),
-                            (PendingIntent) sActionIntentField.get(actionObject),
-                            actionExtras);
-                }
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Unable to access notification actions", e);
-                sActionsAccessFailed = true;
-            }
-        }
-        return null;
-    }
-
-    private static Object[] getActionObjectsLocked(Notification notif) {
-        synchronized (sActionsLock) {
-            if (!ensureActionReflectionReadyLocked()) {
-                return null;
-            }
-            try {
-                return (Object[]) sActionsField.get(notif);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Unable to access notification actions", e);
-                sActionsAccessFailed = true;
-                return null;
-            }
-        }
-    }
-
-    @SuppressWarnings("LiteralClassName")
-    private static boolean ensureActionReflectionReadyLocked() {
-        if (sActionsAccessFailed) {
-            return false;
-        }
-        try {
-            if (sActionsField == null) {
-                sActionClass = Class.forName("android.app.Notification$Action");
-                sActionIconField = sActionClass.getDeclaredField("icon");
-                sActionTitleField = sActionClass.getDeclaredField("title");
-                sActionIntentField = sActionClass.getDeclaredField("actionIntent");
-                sActionsField = Notification.class.getDeclaredField("actions");
-                sActionsField.setAccessible(true);
-            }
-        } catch (ClassNotFoundException e) {
-            Log.e(TAG, "Unable to access notification actions", e);
-            sActionsAccessFailed = true;
-        } catch (NoSuchFieldException e) {
-            Log.e(TAG, "Unable to access notification actions", e);
-            sActionsAccessFailed = true;
-        }
-        return !sActionsAccessFailed;
-    }
-
-    static NotificationCompat.Action getActionFromBundle(Bundle bundle) {
-        Bundle extras = bundle.getBundle(KEY_EXTRAS);
-        boolean allowGeneratedReplies = false;
-        if (extras != null) {
-            allowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES, false);
-        }
-        return new NotificationCompat.Action(
-                bundle.getInt(KEY_ICON),
-                bundle.getCharSequence(KEY_TITLE),
-                bundle.<PendingIntent>getParcelable(KEY_ACTION_INTENT),
-                bundle.getBundle(KEY_EXTRAS),
-                fromBundleArray(getBundleArrayFromBundle(bundle, KEY_REMOTE_INPUTS)),
-                fromBundleArray(getBundleArrayFromBundle(bundle, KEY_DATA_ONLY_REMOTE_INPUTS)),
-                allowGeneratedReplies,
-                NotificationCompat.Action.SEMANTIC_ACTION_NONE,
-                true);
-    }
-
-    static Bundle getBundleForAction(NotificationCompat.Action action) {
-        Bundle bundle = new Bundle();
-        bundle.putInt(KEY_ICON, action.getIcon());
-        bundle.putCharSequence(KEY_TITLE, action.getTitle());
-        bundle.putParcelable(KEY_ACTION_INTENT, action.getActionIntent());
-        Bundle actionExtras;
-        if (action.getExtras() != null) {
-            actionExtras = new Bundle(action.getExtras());
-        } else {
-            actionExtras = new Bundle();
-        }
-        actionExtras.putBoolean(NotificationCompatJellybean.EXTRA_ALLOW_GENERATED_REPLIES,
-                action.getAllowGeneratedReplies());
-        bundle.putBundle(KEY_EXTRAS, actionExtras);
-        bundle.putParcelableArray(KEY_REMOTE_INPUTS, toBundleArray(action.getRemoteInputs()));
-        return bundle;
-    }
-
-
-    private static RemoteInput fromBundle(Bundle data) {
-        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 new RemoteInput(data.getString(KEY_RESULT_KEY),
-                data.getCharSequence(KEY_LABEL),
-                data.getCharSequenceArray(KEY_CHOICES),
-                data.getBoolean(KEY_ALLOW_FREE_FORM_INPUT),
-                data.getBundle(KEY_EXTRAS),
-                allowedDataTypes);
-    }
-
-    private static Bundle toBundle(RemoteInput remoteInput) {
-        Bundle data = new Bundle();
-        data.putString(KEY_RESULT_KEY, remoteInput.getResultKey());
-        data.putCharSequence(KEY_LABEL, remoteInput.getLabel());
-        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;
-    }
-
-    private static RemoteInput[] fromBundleArray(Bundle[] bundles) {
-        if (bundles == null) {
-            return null;
-        }
-        RemoteInput[] remoteInputs = new RemoteInput[bundles.length];
-        for (int i = 0; i < bundles.length; i++) {
-            remoteInputs[i] = fromBundle(bundles[i]);
-        }
-        return remoteInputs;
-    }
-
-    private static Bundle[] toBundleArray(RemoteInput[] remoteInputs) {
-        if (remoteInputs == null) {
-            return null;
-        }
-        Bundle[] bundles = new Bundle[remoteInputs.length];
-        for (int i = 0; i < remoteInputs.length; i++) {
-            bundles[i] = toBundle(remoteInputs[i]);
-        }
-        return bundles;
-    }
-
-    /**
-     * Get an array of Bundle objects from a parcelable array field in a bundle.
-     * Update the bundle to have a typed array so fetches in the future don't need
-     * to do an array copy.
-     */
-    private static Bundle[] getBundleArrayFromBundle(Bundle bundle, String key) {
-        Parcelable[] array = bundle.getParcelableArray(key);
-        if (array instanceof Bundle[] || array == null) {
-            return (Bundle[]) array;
-        }
-        Bundle[] typedArray = Arrays.copyOf(array, array.length,
-                Bundle[].class);
-        bundle.putParcelableArray(key, typedArray);
-        return typedArray;
-    }
-}
diff --git a/android/support/v4/app/NotificationCompatSideChannelService.java b/android/support/v4/app/NotificationCompatSideChannelService.java
deleted file mode 100644
index f633076..0000000
--- a/android/support/v4/app/NotificationCompatSideChannelService.java
+++ /dev/null
@@ -1,121 +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.Notification;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-/**
- * Abstract service to receive side channel notifications sent from
- * {@link android.support.v4.app.NotificationManagerCompat}.
- *
- * <p>To receive side channel notifications, extend this service and register it in your
- * android manifest with an intent filter for the BIND_NOTIFICATION_SIDE_CHANNEL action.
- * Note: you must also have an enabled
- * {@link android.service.notification.NotificationListenerService} within your package.
- *
- * <p>Example AndroidManifest.xml addition:
- * <pre>
- * &lt;service android:name="com.example.NotificationSideChannelService"&gt;
- *     &lt;intent-filter&gt;
- *         &lt;action android:name="android.support.BIND_NOTIFICATION_SIDE_CHANNEL" /&gt;
- *     &lt;/intent-filter&gt;
- * &lt;/service&gt;</pre>
- *
- */
-public abstract class NotificationCompatSideChannelService extends Service {
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (intent.getAction().equals(NotificationManagerCompat.ACTION_BIND_SIDE_CHANNEL)) {
-            // Block side channel service connections if the current sdk has no need for
-            // side channeling.
-            if (Build.VERSION.SDK_INT > NotificationManagerCompat.MAX_SIDE_CHANNEL_SDK_VERSION) {
-                return null;
-            }
-            return new NotificationSideChannelStub();
-        }
-        return null;
-    }
-
-    /**
-     * Handle a side-channeled notification being posted.
-     */
-    public abstract void notify(String packageName, int id, String tag, Notification notification);
-
-    /**
-     * Handle a side-channelled notification being cancelled.
-     */
-    public abstract void cancel(String packageName, int id, String tag);
-
-    /**
-     * Handle the side-channelled cancelling of all notifications for a package.
-     */
-    public abstract void cancelAll(String packageName);
-
-    private class NotificationSideChannelStub extends INotificationSideChannel.Stub {
-        NotificationSideChannelStub() {
-        }
-
-        @Override
-        public void notify(String packageName, int id, String tag, Notification notification)
-                throws RemoteException {
-            checkPermission(getCallingUid(), packageName);
-            long idToken = clearCallingIdentity();
-            try {
-                NotificationCompatSideChannelService.this.notify(packageName, id, tag, notification);
-            } finally {
-                restoreCallingIdentity(idToken);
-            }
-        }
-
-        @Override
-        public void cancel(String packageName, int id, String tag) throws RemoteException {
-            checkPermission(getCallingUid(), packageName);
-            long idToken = clearCallingIdentity();
-            try {
-                NotificationCompatSideChannelService.this.cancel(packageName, id, tag);
-            } finally {
-                restoreCallingIdentity(idToken);
-            }
-        }
-
-        @Override
-        public void cancelAll(String packageName) {
-            checkPermission(getCallingUid(), packageName);
-            long idToken = clearCallingIdentity();
-            try {
-                NotificationCompatSideChannelService.this.cancelAll(packageName);
-            } finally {
-                restoreCallingIdentity(idToken);
-            }
-        }
-    }
-
-    void checkPermission(int callingUid, String packageName) {
-        for (String validPackage : getPackageManager().getPackagesForUid(callingUid)) {
-            if (validPackage.equals(packageName)) {
-                return;
-            }
-        }
-        throw new SecurityException("NotificationSideChannelService: Uid " + callingUid
-                + " is not authorized for package " + packageName);
-    }
-}
diff --git a/android/support/v4/app/NotificationManagerCompat.java b/android/support/v4/app/NotificationManagerCompat.java
deleted file mode 100644
index 7099cb9..0000000
--- a/android/support/v4/app/NotificationManagerCompat.java
+++ /dev/null
@@ -1,657 +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.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.support.annotation.GuardedBy;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayDeque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Compatibility library for NotificationManager with fallbacks for older platforms.
- *
- * <p>To use this class, call the static function {@link #from} to get a
- * {@link NotificationManagerCompat} object, and then call one of its
- * methods to post or cancel notifications.
- */
-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 = "android.support.useSideChannel";
-
-    /**
-     * Intent action to register for on a service to receive side channel
-     * notifications. The listening service must be in the same package as an enabled
-     * {@link android.service.notification.NotificationListenerService}.
-     */
-    public static final String ACTION_BIND_SIDE_CHANNEL =
-            "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
-
-    /**
-     * Maximum sdk build version which needs support for side channeled notifications.
-     * Currently the only needed use is for side channeling group children before KITKAT_WATCH.
-     */
-    static final int MAX_SIDE_CHANNEL_SDK_VERSION = 19;
-
-    /** Base time delay for a side channel listener queue retry. */
-    private static final int SIDE_CHANNEL_RETRY_BASE_INTERVAL_MS = 1000;
-    /** Maximum retries for a side channel listener before dropping tasks. */
-    private static final int SIDE_CHANNEL_RETRY_MAX_COUNT = 6;
-    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
-    private static final String SETTING_ENABLED_NOTIFICATION_LISTENERS =
-            "enabled_notification_listeners";
-
-    /** Cache of enabled notification listener components */
-    private static final Object sEnabledNotificationListenersLock = new Object();
-    @GuardedBy("sEnabledNotificationListenersLock")
-    private static String sEnabledNotificationListeners;
-    @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();
-    @GuardedBy("sLock")
-    private static SideChannelManager sSideChannelManager;
-
-    /**
-     * Value signifying that the user has not expressed an importance.
-     *
-     * This value is for persisting preferences, and should never be associated with
-     * an actual notification.
-     */
-    public static final int IMPORTANCE_UNSPECIFIED = -1000;
-
-    /**
-     * A notification with no importance: shows nowhere, is blocked.
-     */
-    public static final int IMPORTANCE_NONE = 0;
-
-    /**
-     * Min notification importance: only shows in the shade, below the fold.
-     */
-    public static final int IMPORTANCE_MIN = 1;
-
-    /**
-     * Low notification importance: shows everywhere, but is not intrusive.
-     */
-    public static final int IMPORTANCE_LOW = 2;
-
-    /**
-     * Default notification importance: shows everywhere, allowed to makes noise,
-     * but does not visually intrude.
-     */
-    public static final int IMPORTANCE_DEFAULT = 3;
-
-    /**
-     * Higher notification importance: shows everywhere, allowed to makes noise and peek.
-     */
-    public static final int IMPORTANCE_HIGH = 4;
-
-    /**
-     * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
-     * use full screen intents.
-     */
-    public static final int IMPORTANCE_MAX = 5;
-
-    /** Get a {@link NotificationManagerCompat} instance for a provided context. */
-    @NonNull
-    public static NotificationManagerCompat from(@NonNull Context context) {
-        return new NotificationManagerCompat(context);
-    }
-
-    private NotificationManagerCompat(Context context) {
-        mContext = context;
-        mNotificationManager = (NotificationManager) mContext.getSystemService(
-                Context.NOTIFICATION_SERVICE);
-    }
-
-    /**
-     * Cancel a previously shown notification.
-     * @param id the ID of the notification
-     */
-    public void cancel(int id) {
-        cancel(null, id);
-    }
-
-    /**
-     * Cancel a previously shown notification.
-     * @param tag the string identifier of the notification.
-     * @param id the ID of the notification
-     */
-    public void cancel(@Nullable String tag, int id) {
-        mNotificationManager.cancel(tag, id);
-        if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {
-            pushSideChannelQueue(new CancelTask(mContext.getPackageName(), id, tag));
-        }
-    }
-
-    /** Cancel all previously shown notifications. */
-    public void cancelAll() {
-        mNotificationManager.cancelAll();
-        if (Build.VERSION.SDK_INT <= MAX_SIDE_CHANNEL_SDK_VERSION) {
-            pushSideChannelQueue(new CancelTask(mContext.getPackageName()));
-        }
-    }
-
-    /**
-     * Post a notification to be shown in the status bar, stream, etc.
-     * @param id the ID of the notification
-     * @param notification the notification to post to the system
-     */
-    public void notify(int id, @NonNull Notification notification) {
-        notify(null, id, notification);
-    }
-
-    /**
-     * Post a notification to be shown in the status bar, stream, etc.
-     * @param tag the string identifier for a notification. Can be {@code null}.
-     * @param id the ID of the notification. The pair (tag, id) must be unique within your app.
-     * @param notification the notification to post to the system
-    */
-    public void notify(@Nullable String tag, int id, @NonNull Notification notification) {
-        if (useSideChannelForNotification(notification)) {
-            pushSideChannelQueue(new NotifyTask(mContext.getPackageName(), id, tag, notification));
-            // Cancel this notification in notification manager if it just transitioned to being
-            // side channelled.
-            mNotificationManager.cancel(tag, id);
-        } else {
-            mNotificationManager.notify(tag, id, notification);
-        }
-    }
-
-    /**
-     * Returns whether notifications from the calling package are not blocked.
-     */
-    public boolean areNotificationsEnabled() {
-        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;
-        }
-    }
-
-    /**
-     * Returns the user specified importance for notifications from the calling package.
-     *
-     * @return An importance level, such as {@link #IMPORTANCE_DEFAULT}.
-     */
-    public int getImportance() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return mNotificationManager.getImportance();
-        } else {
-            return IMPORTANCE_UNSPECIFIED;
-        }
-    }
-
-    /**
-     * Get the set of packages that have an enabled notification listener component within them.
-     */
-    @NonNull
-    public static Set<String> getEnabledListenerPackages(@NonNull Context context) {
-        final String enabledNotificationListeners = Settings.Secure.getString(
-                context.getContentResolver(),
-                SETTING_ENABLED_NOTIFICATION_LISTENERS);
-        synchronized (sEnabledNotificationListenersLock) {
-            // Parse the string again if it is different from the last time this method was called.
-            if (enabledNotificationListeners != null
-                    && !enabledNotificationListeners.equals(sEnabledNotificationListeners)) {
-                final String[] components = enabledNotificationListeners.split(":");
-                Set<String> packageNames = new HashSet<String>(components.length);
-                for (String component : components) {
-                    ComponentName componentName = ComponentName.unflattenFromString(component);
-                    if (componentName != null) {
-                        packageNames.add(componentName.getPackageName());
-                    }
-                }
-                sEnabledNotificationListenerPackages = packageNames;
-                sEnabledNotificationListeners = enabledNotificationListeners;
-            }
-            return sEnabledNotificationListenerPackages;
-        }
-    }
-
-    /**
-     * Returns true if this notification should use the side channel for delivery.
-     */
-    private static boolean useSideChannelForNotification(Notification notification) {
-        Bundle extras = NotificationCompat.getExtras(notification);
-        return extras != null && extras.getBoolean(EXTRA_USE_SIDE_CHANNEL);
-    }
-
-    /**
-     * Push a notification task for distribution to notification side channels.
-     */
-    private void pushSideChannelQueue(Task task) {
-        synchronized (sLock) {
-            if (sSideChannelManager == null) {
-                sSideChannelManager = new SideChannelManager(mContext.getApplicationContext());
-            }
-            sSideChannelManager.queueTask(task);
-        }
-    }
-
-    /**
-     * Helper class to manage a queue of pending tasks to send to notification side channel
-     * listeners.
-     */
-    private static class SideChannelManager implements Handler.Callback, ServiceConnection {
-        private static final int MSG_QUEUE_TASK = 0;
-        private static final int MSG_SERVICE_CONNECTED = 1;
-        private static final int MSG_SERVICE_DISCONNECTED = 2;
-        private static final int MSG_RETRY_LISTENER_QUEUE = 3;
-
-        private final Context mContext;
-        private final HandlerThread mHandlerThread;
-        private final Handler mHandler;
-        private final Map<ComponentName, ListenerRecord> mRecordMap =
-                new HashMap<ComponentName, ListenerRecord>();
-        private Set<String> mCachedEnabledPackages = new HashSet<String>();
-
-        public SideChannelManager(Context context) {
-            mContext = context;
-            mHandlerThread = new HandlerThread("NotificationManagerCompat");
-            mHandlerThread.start();
-            mHandler = new Handler(mHandlerThread.getLooper(), this);
-        }
-
-        /**
-         * Queue a new task to be sent to all listeners. This function can be called
-         * from any thread.
-         */
-        public void queueTask(Task task) {
-            mHandler.obtainMessage(MSG_QUEUE_TASK, task).sendToTarget();
-        }
-
-        @Override
-        public boolean handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_QUEUE_TASK:
-                    handleQueueTask((Task) msg.obj);
-                    return true;
-                case MSG_SERVICE_CONNECTED:
-                    ServiceConnectedEvent event = (ServiceConnectedEvent) msg.obj;
-                    handleServiceConnected(event.componentName, event.iBinder);
-                    return true;
-                case MSG_SERVICE_DISCONNECTED:
-                    handleServiceDisconnected((ComponentName) msg.obj);
-                    return true;
-                case MSG_RETRY_LISTENER_QUEUE:
-                    handleRetryListenerQueue((ComponentName) msg.obj);
-                    return true;
-            }
-            return false;
-        }
-
-        private void handleQueueTask(Task task) {
-            updateListenerMap();
-            for (ListenerRecord record : mRecordMap.values()) {
-                record.taskQueue.add(task);
-                processListenerQueue(record);
-            }
-        }
-
-        private void handleServiceConnected(ComponentName componentName, IBinder iBinder) {
-            ListenerRecord record = mRecordMap.get(componentName);
-            if (record != null) {
-                record.service = INotificationSideChannel.Stub.asInterface(iBinder);
-                record.retryCount = 0;
-                processListenerQueue(record);
-            }
-        }
-
-        private void handleServiceDisconnected(ComponentName componentName) {
-            ListenerRecord record = mRecordMap.get(componentName);
-            if (record != null) {
-                ensureServiceUnbound(record);
-            }
-        }
-
-        private void handleRetryListenerQueue(ComponentName componentName) {
-            ListenerRecord record = mRecordMap.get(componentName);
-            if (record != null) {
-                processListenerQueue(record);
-            }
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Connected to service " + componentName);
-            }
-            mHandler.obtainMessage(MSG_SERVICE_CONNECTED,
-                    new ServiceConnectedEvent(componentName, iBinder))
-                    .sendToTarget();
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName componentName) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Disconnected from service " + componentName);
-            }
-            mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, componentName).sendToTarget();
-        }
-
-        /**
-         * Check the current list of enabled listener packages and update the records map
-         * accordingly.
-         */
-        private void updateListenerMap() {
-            Set<String> enabledPackages = getEnabledListenerPackages(mContext);
-            if (enabledPackages.equals(mCachedEnabledPackages)) {
-                // Short-circuit when the list of enabled packages has not changed.
-                return;
-            }
-            mCachedEnabledPackages = enabledPackages;
-            List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServices(
-                    new Intent().setAction(ACTION_BIND_SIDE_CHANNEL), 0);
-            Set<ComponentName> enabledComponents = new HashSet<ComponentName>();
-            for (ResolveInfo resolveInfo : resolveInfos) {
-                if (!enabledPackages.contains(resolveInfo.serviceInfo.packageName)) {
-                    continue;
-                }
-                ComponentName componentName = new ComponentName(
-                        resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
-                if (resolveInfo.serviceInfo.permission != null) {
-                    Log.w(TAG, "Permission present on component " + componentName
-                            + ", not adding listener record.");
-                    continue;
-                }
-                enabledComponents.add(componentName);
-            }
-            // Ensure all enabled components have a record in the listener map.
-            for (ComponentName componentName : enabledComponents) {
-                if (!mRecordMap.containsKey(componentName)) {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Adding listener record for " + componentName);
-                    }
-                    mRecordMap.put(componentName, new ListenerRecord(componentName));
-                }
-            }
-            // Remove listener records that are no longer for enabled components.
-            Iterator<Map.Entry<ComponentName, ListenerRecord>> it =
-                    mRecordMap.entrySet().iterator();
-            while (it.hasNext()) {
-                Map.Entry<ComponentName, ListenerRecord> entry = it.next();
-                if (!enabledComponents.contains(entry.getKey())) {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Removing listener record for " + entry.getKey());
-                    }
-                    ensureServiceUnbound(entry.getValue());
-                    it.remove();
-                }
-            }
-        }
-
-        /**
-         * Ensure we are already attempting to bind to a service, or start a new binding if not.
-         * @return Whether the service bind attempt was successful.
-         */
-        private boolean ensureServiceBound(ListenerRecord record) {
-            if (record.bound) {
-                return true;
-            }
-            Intent intent = new Intent(ACTION_BIND_SIDE_CHANNEL).setComponent(record.componentName);
-            record.bound = mContext.bindService(intent, this, Service.BIND_AUTO_CREATE
-                    | Service.BIND_WAIVE_PRIORITY);
-            if (record.bound) {
-                record.retryCount = 0;
-            } else {
-                Log.w(TAG, "Unable to bind to listener " + record.componentName);
-                mContext.unbindService(this);
-            }
-            return record.bound;
-        }
-
-        /**
-         * Ensure we have unbound from a service.
-         */
-        private void ensureServiceUnbound(ListenerRecord record) {
-            if (record.bound) {
-                mContext.unbindService(this);
-                record.bound = false;
-            }
-            record.service = null;
-        }
-
-        /**
-         * Schedule a delayed retry to communicate with a listener service.
-         * After a maximum number of attempts (with exponential back-off), start
-         * dropping pending tasks for this listener.
-         */
-        private void scheduleListenerRetry(ListenerRecord record) {
-            if (mHandler.hasMessages(MSG_RETRY_LISTENER_QUEUE, record.componentName)) {
-                return;
-            }
-            record.retryCount++;
-            if (record.retryCount > SIDE_CHANNEL_RETRY_MAX_COUNT) {
-                Log.w(TAG, "Giving up on delivering " + record.taskQueue.size() + " tasks to "
-                        + record.componentName + " after " + record.retryCount + " retries");
-                record.taskQueue.clear();
-                return;
-            }
-            int delayMs = SIDE_CHANNEL_RETRY_BASE_INTERVAL_MS * (1 << (record.retryCount - 1));
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Scheduling retry for " + delayMs + " ms");
-            }
-            Message msg = mHandler.obtainMessage(MSG_RETRY_LISTENER_QUEUE, record.componentName);
-            mHandler.sendMessageDelayed(msg, delayMs);
-        }
-
-        /**
-         * Perform a processing step for a listener. First check the bind state, then attempt
-         * to flush the task queue, and if an error is encountered, schedule a retry.
-         */
-        private void processListenerQueue(ListenerRecord record) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Processing component " + record.componentName + ", "
-                        + record.taskQueue.size() + " queued tasks");
-            }
-            if (record.taskQueue.isEmpty()) {
-                return;
-            }
-            if (!ensureServiceBound(record) || record.service == null) {
-                // Ensure bind has started and that a service interface is ready to use.
-                scheduleListenerRetry(record);
-                return;
-            }
-            // Attempt to flush all items in the task queue.
-            while (true) {
-                Task task = record.taskQueue.peek();
-                if (task == null) {
-                    break;
-                }
-                try {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Sending task " + task);
-                    }
-                    task.send(record.service);
-                    record.taskQueue.remove();
-                } catch (DeadObjectException e) {
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "Remote service has died: " + record.componentName);
-                    }
-                    break;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "RemoteException communicating with " + record.componentName, e);
-                    break;
-                }
-            }
-            if (!record.taskQueue.isEmpty()) {
-                // Some tasks were not sent, meaning an error was encountered, schedule a retry.
-                scheduleListenerRetry(record);
-            }
-        }
-
-        /** A per-side-channel-service listener state record */
-        private static class ListenerRecord {
-            public final ComponentName componentName;
-            /** Whether the service is currently bound to. */
-            public boolean bound = false;
-            /** The service stub provided by onServiceConnected */
-            public INotificationSideChannel service;
-            /** Queue of pending tasks to send to this listener service */
-            public ArrayDeque<Task> taskQueue = new ArrayDeque<>();
-            /** Number of retries attempted while connecting to this listener service */
-            public int retryCount = 0;
-
-            public ListenerRecord(ComponentName componentName) {
-                this.componentName = componentName;
-            }
-        }
-    }
-
-    private static class ServiceConnectedEvent {
-        final ComponentName componentName;
-        final IBinder iBinder;
-
-        ServiceConnectedEvent(ComponentName componentName,
-                final IBinder iBinder) {
-            this.componentName = componentName;
-            this.iBinder = iBinder;
-        }
-    }
-
-    private interface Task {
-        void send(INotificationSideChannel service) throws RemoteException;
-    }
-
-    private static class NotifyTask implements Task {
-        final String packageName;
-        final int id;
-        final String tag;
-        final Notification notif;
-
-        NotifyTask(String packageName, int id, String tag, Notification notif) {
-            this.packageName = packageName;
-            this.id = id;
-            this.tag = tag;
-            this.notif = notif;
-        }
-
-        @Override
-        public void send(INotificationSideChannel service) throws RemoteException {
-            service.notify(packageName, id, tag, notif);
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder("NotifyTask[");
-            sb.append("packageName:").append(packageName);
-            sb.append(", id:").append(id);
-            sb.append(", tag:").append(tag);
-            sb.append("]");
-            return sb.toString();
-        }
-    }
-
-    private static class CancelTask implements Task {
-        final String packageName;
-        final int id;
-        final String tag;
-        final boolean all;
-
-        CancelTask(String packageName) {
-            this.packageName = packageName;
-            this.id = 0;
-            this.tag = null;
-            this.all = true;
-        }
-
-        CancelTask(String packageName, int id, String tag) {
-            this.packageName = packageName;
-            this.id = id;
-            this.tag = tag;
-            this.all = false;
-        }
-
-        @Override
-        public void send(INotificationSideChannel service) throws RemoteException {
-            if (all) {
-                service.cancelAll(packageName);
-            } else {
-                service.cancel(packageName, id, tag);
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder("CancelTask[");
-            sb.append("packageName:").append(packageName);
-            sb.append(", id:").append(id);
-            sb.append(", tag:").append(tag);
-            sb.append(", all:").append(all);
-            sb.append("]");
-            return sb.toString();
-        }
-    }
-}
diff --git a/android/support/v4/app/OneShotPreDrawListener.java b/android/support/v4/app/OneShotPreDrawListener.java
deleted file mode 100644
index 502af1f..0000000
--- a/android/support/v4/app/OneShotPreDrawListener.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.v4.app;
-
-
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-/**
- * An OnPreDrawListener that will remove itself after one OnPreDraw call. Typical
- * usage is:
- * <pre><code>
- *     OneShotPreDrawListener.add(view, () -> { view.doSomething(); })
- * </code></pre>
- * <p>
- * The onPreDraw always returns true.
- * <p>
- * The listener will also remove itself from the ViewTreeObserver when the view
- * is detached from the view hierarchy. In that case, the Runnable will never be
- * executed.
- */
-class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListener,
-        View.OnAttachStateChangeListener {
-    private final View mView;
-    private ViewTreeObserver mViewTreeObserver;
-    private final Runnable mRunnable;
-
-    private OneShotPreDrawListener(View view, Runnable runnable) {
-        mView = view;
-        mViewTreeObserver = view.getViewTreeObserver();
-        mRunnable = runnable;
-    }
-
-    /**
-     * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
-     * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
-     * @param runnable The Runnable to execute in the OnPreDraw (once)
-     * @return The added OneShotPreDrawListener. It can be removed prior to
-     * the onPreDraw by calling {@link #removeListener()}.
-     */
-    public static OneShotPreDrawListener add(View view, Runnable runnable) {
-        OneShotPreDrawListener listener = new OneShotPreDrawListener(view, runnable);
-        view.getViewTreeObserver().addOnPreDrawListener(listener);
-        view.addOnAttachStateChangeListener(listener);
-        return listener;
-    }
-
-    @Override
-    public boolean onPreDraw() {
-        removeListener();
-        mRunnable.run();
-        return true;
-    }
-
-    /**
-     * Removes the listener from the ViewTreeObserver. This is useful to call if the
-     * callback should be removed prior to {@link #onPreDraw()}.
-     */
-    public void removeListener() {
-        if (mViewTreeObserver.isAlive()) {
-            mViewTreeObserver.removeOnPreDrawListener(this);
-        } else {
-            mView.getViewTreeObserver().removeOnPreDrawListener(this);
-        }
-        mView.removeOnAttachStateChangeListener(this);
-    }
-
-    @Override
-    public void onViewAttachedToWindow(View v) {
-        mViewTreeObserver = v.getViewTreeObserver();
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View v) {
-        removeListener();
-    }
-}
diff --git a/android/support/v4/app/RemoteInput.java b/android/support/v4/app/RemoteInput.java
deleted file mode 100644
index 0ae3e81..0000000
--- a/android/support/v4/app/RemoteInput.java
+++ /dev/null
@@ -1,454 +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.content.ClipData;
-import android.content.ClipDescription;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * 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 = "android.remoteinput.results";
-
-    /** 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 mAllowFreeFormTextInput;
-    private final Bundle mExtras;
-    private final Set<String> mAllowedDataTypes;
-
-    RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
-            boolean allowFreeFormTextInput, Bundle extras, Set<String> allowedDataTypes) {
-        this.mResultKey = resultKey;
-        this.mLabel = label;
-        this.mChoices = choices;
-        this.mAllowFreeFormTextInput = allowFreeFormTextInput;
-        this.mExtras = extras;
-        this.mAllowedDataTypes = allowedDataTypes;
-    }
-
-    /**
-     * Get the key that the result of this input will be set in from the Bundle returned by
-     * {@link #getResultsFromIntent} when the {@link android.app.PendingIntent} is sent.
-     */
-    @Override
-    public String getResultKey() {
-        return mResultKey;
-    }
-
-    /**
-     * Get the label to display to users when collecting this input.
-     */
-    @Override
-    public CharSequence getLabel() {
-        return mLabel;
-    }
-
-    /**
-     * Get possible input choices. This can be {@code null} if there are no choices to present.
-     */
-    @Override
-    public CharSequence[] getChoices() {
-        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
-     * choices in {@link #getChoices}. An {@link IllegalArgumentException} is thrown
-     * if you set this to false and {@link #getChoices} returns {@code null} or empty.
-     */
-    @Override
-    public boolean getAllowFreeFormInput() {
-        return mAllowFreeFormTextInput;
-    }
-
-    /**
-     * Get additional metadata carried around with this remote input.
-     */
-    @Override
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    /**
-     * Builder class for {@link android.support.v4.app.RemoteInput} objects.
-     */
-    public static final class Builder {
-        private final String mResultKey;
-        private CharSequence mLabel;
-        private CharSequence[] mChoices;
-        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.
-         * @param resultKey the Bundle key that refers to this input when collected from the user
-         */
-        public Builder(String resultKey) {
-            if (resultKey == null) {
-                throw new IllegalArgumentException("Result key can't be null");
-            }
-            mResultKey = resultKey;
-        }
-
-        /**
-         * Set a label to be displayed to the user when collecting this input.
-         * @param label The label to show to users when they input a response.
-         * @return this object for method chaining
-         */
-        public Builder setLabel(CharSequence label) {
-            mLabel = label;
-            return this;
-        }
-
-        /**
-         * Specifies choices available to the user to satisfy this input.
-         * @param choices an array of pre-defined choices for users input.
-         *        You must provide a non-null and non-empty array if
-         *        you disabled free form input using {@link #setAllowFreeFormInput}.
-         * @return this object for method chaining
-         */
-        public Builder setChoices(CharSequence[] choices) {
-            mChoices = choices;
-            return this;
-        }
-
-        /**
-         * Specifies whether the user can provide arbitrary values.
-         *
-         * @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 allowFreeFormTextInput) {
-            mAllowFreeFormTextInput = allowFreeFormTextInput;
-            return this;
-        }
-
-        /**
-         * Merge additional metadata into this builder.
-         *
-         * <p>Values within the Bundle will replace existing extras values in this Builder.
-         *
-         * @see RemoteInput#getExtras
-         */
-        public Builder addExtras(Bundle extras) {
-            if (extras != null) {
-                mExtras.putAll(extras);
-            }
-            return this;
-        }
-
-        /**
-         * Get the metadata Bundle used by this Builder.
-         *
-         * <p>The returned Bundle is shared with this Builder.
-         */
-        public Bundle getExtras() {
-            return mExtras;
-        }
-
-        /**
-         * Combine all of the options that have been set and return a new
-         * {@link android.support.v4.app.RemoteInput} object.
-         */
-        public RemoteInput build() {
-            return new RemoteInput(
-                    mResultKey,
-                    mLabel,
-                    mChoices,
-                    mAllowFreeFormTextInput,
-                    mExtras,
-                    mAllowedDataTypes);
-        }
-    }
-
-    /**
-     * 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) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            return android.app.RemoteInput.getDataResultsFromIntent(intent, remoteInputResultKey);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            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.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;
-        } else {
-            Log.w(TAG, "RemoteInput is only supported from API Level 16");
-            return null;
-        }
-    }
-
-    /**
-     * 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. 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.
-     */
-    public static Bundle getResultsFromIntent(Intent intent) {
-        if (Build.VERSION.SDK_INT >= 20) {
-            return android.app.RemoteInput.getResultsFromIntent(intent);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            Intent clipDataIntent = getClipDataIntentFromIntent(intent);
-            if (clipDataIntent == null) {
-                return null;
-            }
-            return clipDataIntent.getExtras().getParcelable(RemoteInput.EXTRA_RESULTS_DATA);
-        } else {
-            Log.w(TAG, "RemoteInput is only supported from API Level 16");
-            return null;
-        }
-    }
-
-    /**
-     * Populate an intent object with the results gathered from remote input. This method
-     * should only be called by remote input collection services when sending results to a
-     * pending intent.
-     * @param remoteInputs The remote inputs for which results are being provided
-     * @param intent The intent to add remote inputs to. The {@link android.content.ClipData}
-     *               field of the intent will be modified to contain the results.
-     * @param results A bundle holding the remote input results. This bundle should
-     *                be populated with keys matching the result keys specified in
-     *                {@code remoteInputs} with values being the result per key.
-     */
-    public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
-            Bundle results) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            android.app.RemoteInput.addResultsToIntent(fromCompat(remoteInputs), intent, results);
-        } else if (Build.VERSION.SDK_INT >= 20) {
-            // 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 =
-                    android.support.v4.app.RemoteInput.getResultsFromIntent(intent);
-            if (existingTextResults == null) {
-                existingTextResults = results;
-            } else {
-                existingTextResults.putAll(results);
-            }
-            for (RemoteInput input : remoteInputs) {
-                // Data results are also wiped out. So grab them and add them back in.
-                Map<String, Uri> existingDataResults =
-                        android.support.v4.app.RemoteInput.getDataResultsFromIntent(
-                                intent, input.getResultKey());
-                RemoteInput[] arr = new RemoteInput[1];
-                arr[0] = input;
-                android.app.RemoteInput.addResultsToIntent(
-                        fromCompat(arr), intent, existingTextResults);
-                if (existingDataResults != null) {
-                    RemoteInput.addDataResultToIntent(input, intent, existingDataResults);
-                }
-            }
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            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 (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));
-        } else {
-            Log.w(TAG, "RemoteInput is only supported from API Level 16");
-        }
-    }
-
-    /**
-     * 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) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            android.app.RemoteInput.addDataResultToIntent(fromCompat(remoteInput), intent, results);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            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));
-        } else {
-            Log.w(TAG, "RemoteInput is only supported from API Level 16");
-        }
-    }
-
-    private static String getExtraResultsKeyForData(String mimeType) {
-        return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
-    }
-
-    @RequiresApi(20)
-    static android.app.RemoteInput[] fromCompat(RemoteInput[] srcArray) {
-        if (srcArray == null) {
-            return null;
-        }
-        android.app.RemoteInput[] result = new android.app.RemoteInput[srcArray.length];
-        for (int i = 0; i < srcArray.length; i++) {
-            result[i] = fromCompat(srcArray[i]);
-        }
-        return result;
-    }
-
-    @RequiresApi(20)
-    static android.app.RemoteInput fromCompat(RemoteInput src) {
-        return new android.app.RemoteInput.Builder(src.getResultKey())
-                .setLabel(src.getLabel())
-                .setChoices(src.getChoices())
-                .setAllowFreeFormInput(src.getAllowFreeFormInput())
-                .addExtras(src.getExtras())
-                .build();
-    }
-
-    @RequiresApi(16)
-    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/android/support/v4/app/RemoteInputCompatBase.java b/android/support/v4/app/RemoteInputCompatBase.java
deleted file mode 100644
index fa7f7b6..0000000
--- a/android/support/v4/app/RemoteInputCompatBase.java
+++ /dev/null
@@ -1,87 +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.app;
-
-import android.os.Bundle;
-
-import java.util.Set;
-
-/**
- * @deprecated This class was not meant to be made public.
- */
-@Deprecated
-class RemoteInputCompatBase {
-
-    /**
-     * @deprecated This class was not meant to be made public.
-     */
-    @Deprecated
-    public abstract static class RemoteInput {
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        public RemoteInput() {}
-
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        protected abstract String getResultKey();
-
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        protected abstract CharSequence getLabel();
-
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        protected abstract CharSequence[] getChoices();
-
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        protected abstract boolean getAllowFreeFormInput();
-
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        protected abstract Bundle getExtras();
-
-        /**
-         * @deprecated This method was not meant to be made public.
-         */
-        @Deprecated
-        protected abstract Set<String> getAllowedDataTypes();
-
-        /**
-         * @deprecated This class was not meant to be made public.
-         */
-        @Deprecated
-        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/android/support/v4/app/ServiceCompat.java b/android/support/v4/app/ServiceCompat.java
deleted file mode 100644
index 2e63b23..0000000
--- a/android/support/v4/app/ServiceCompat.java
+++ /dev/null
@@ -1,103 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Notification;
-import android.app.Service;
-import android.os.Build;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Helper for accessing features in {@link android.app.Service}.
- */
-public final class ServiceCompat {
-
-    private ServiceCompat() {
-        /* Hide constructor */
-    }
-
-    /**
-     * Constant to return from {@link android.app.Service#onStartCommand}: if this
-     * service's process is killed while it is started (after returning from
-     * {@link android.app.Service#onStartCommand}), then leave it in the started
-     * state but don't retain this delivered intent.  Later the system will try to
-     * re-create the service.  Because it is in the started state, it will
-     * guarantee to call {@link android.app.Service#onStartCommand} after creating
-     * the new service instance; if there are not any pending start commands to be
-     * delivered to the service, it will be called with a null intent
-     * object, so you must take care to check for this.
-     *
-     * <p>This mode makes sense for things that will be explicitly started
-     * and stopped to run for arbitrary periods of time, such as a service
-     * performing background music playback.
-     */
-    public static final int START_STICKY = 1;
-
-    /**
-     * Flag for {@link #stopForeground(Service, int)}: if set, the notification previously provided
-     * to {@link Service#startForeground(int, Notification)} will be removed.  Otherwise it
-     * will remain until a later call (to {@link Service#startForeground(int, Notification)} or
-     * {@link #stopForeground(Service, int)} removes it, or the service is destroyed.
-     */
-    public static final int STOP_FOREGROUND_REMOVE = 1<<0;
-
-    /**
-     * Flag for {@link #stopForeground(Service, int)}: if set, the notification previously provided
-     * to {@link Service#startForeground(int, Notification)} will be detached from the service.
-     * Only makes sense when {@link #STOP_FOREGROUND_REMOVE} is <b>not</b> set -- in this case, the
-     * notification will remain shown, but be completely detached from the service and so no longer
-     * changed except through direct calls to the
-     * notification manager.
-     * <p>
-     * This flag will only work on
-     * {@link android.os.Build.VERSION_CODES#N} and later. It doesn't have any effect on earlier
-     * platform versions.
-     */
-    public static final int STOP_FOREGROUND_DETACH = 1<<1;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef(flag = true,
-            value = {
-                    STOP_FOREGROUND_REMOVE,
-                    STOP_FOREGROUND_DETACH
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StopForegroundFlags {}
-
-    /**
-     * Remove the passed service from foreground state, allowing it to be killed if
-     * more memory is needed.
-     * @param flags Additional behavior options: {@link #STOP_FOREGROUND_REMOVE},
-     * {@link #STOP_FOREGROUND_DETACH}.
-     * @see Service#startForeground(int, Notification)
-     */
-    public static void stopForeground(@NonNull 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/android/support/v4/app/ShareCompat.java b/android/support/v4/app/ShareCompat.java
deleted file mode 100644
index b0c6e62..0000000
--- a/android/support/v4/app/ShareCompat.java
+++ /dev/null
@@ -1,923 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.support.annotation.StringRes;
-import android.support.v4.content.IntentCompat;
-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;
-
-/**
- * Extra helper functionality for sharing data between activities.
- *
- * ShareCompat provides functionality to extend the {@link Intent#ACTION_SEND}/
- * {@link Intent#ACTION_SEND_MULTIPLE} protocol and support retrieving more info
- * about the activity that invoked a social sharing action.
- *
- * {@link IntentBuilder} provides helper functions for constructing a sharing
- * intent that always includes data about the calling activity and app.
- * This lets the called activity provide attribution for the app that shared
- * content. Constructing an intent this way can be done in a method-chaining style.
- * To obtain an IntentBuilder with info about your calling activity, use the static
- * method {@link IntentBuilder#from(Activity)}.
- *
- * {@link IntentReader} provides helper functions for parsing the defined extras
- * within an {@link Intent#ACTION_SEND} or {@link Intent#ACTION_SEND_MULTIPLE} intent
- * used to launch an activity. You can also obtain a Drawable for the caller's
- * application icon and the application's localized label (the app's human-readable name).
- * Social apps that enable sharing content are encouraged to use this information
- * to call out the app that the content was shared from.
- */
-public final class ShareCompat {
-    /**
-     * Intent extra that stores the name of the calling package for an ACTION_SEND intent.
-     * When an activity is started using startActivityForResult this is redundant info.
-     * (It is also provided by {@link Activity#getCallingPackage()}.)
-     *
-     * Instead of using this constant directly, consider using {@link #getCallingPackage(Activity)}
-     * or {@link IntentReader#getCallingPackage()}.
-     */
-    public static final String EXTRA_CALLING_PACKAGE =
-            "android.support.v4.app.EXTRA_CALLING_PACKAGE";
-
-    /**
-     * Intent extra that stores the {@link ComponentName} of the calling activity for
-     * an ACTION_SEND intent.
-     */
-    public static final String EXTRA_CALLING_ACTIVITY =
-            "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
-
-    private static final String HISTORY_FILENAME_PREFIX = ".sharecompat_";
-
-    private ShareCompat() {}
-
-    /**
-     * Retrieve the name of the package that launched calledActivity from a share intent.
-     * Apps that provide social sharing functionality can use this to provide attribution
-     * for the app that shared the content.
-     *
-     * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-     * application. As such it should not be trusted for accuracy in the context of
-     * security or verification.</p>
-     *
-     * @param calledActivity Current activity that was launched to share content
-     * @return Name of the calling package
-     */
-    public static String getCallingPackage(Activity calledActivity) {
-        String result = calledActivity.getCallingPackage();
-        if (result == null) {
-            result = calledActivity.getIntent().getStringExtra(EXTRA_CALLING_PACKAGE);
-        }
-        return result;
-    }
-
-    /**
-     * Retrieve the ComponentName of the activity that launched calledActivity from a share intent.
-     * Apps that provide social sharing functionality can use this to provide attribution
-     * for the app that shared the content.
-     *
-     * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-     * application. As such it should not be trusted for accuracy in the context of
-     * security or verification.</p>
-     *
-     * @param calledActivity Current activity that was launched to share content
-     * @return ComponentName of the calling activity
-     */
-    public static ComponentName getCallingActivity(Activity calledActivity) {
-        ComponentName result = calledActivity.getCallingActivity();
-        if (result == null) {
-            result = calledActivity.getIntent().getParcelableExtra(EXTRA_CALLING_ACTIVITY);
-        }
-        return result;
-    }
-
-    /**
-     * Configure a {@link MenuItem} to act as a sharing action.
-     *
-     * <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 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 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
-     * the share menu item.</li>
-     * <li>Keep a reference to the MenuItem object for the share item once it has been created
-     * and call configureMenuItem to update the associated sharing intent as needed.</li>
-     * </ul>
-     *
-     * @param item MenuItem to configure for sharing
-     * @param shareIntent IntentBuilder with data about the content to share
-     */
-    public static void configureMenuItem(MenuItem item, IntentBuilder 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());
-            }
-        }
-    }
-
-    /**
-     * Configure a menu item to act as a sharing action.
-     *
-     * @param menu Menu containing the item to use for sharing
-     * @param menuItemId ID of the share item within menu
-     * @param shareIntent IntentBuilder with data about the content to share
-     * @see #configureMenuItem(MenuItem, IntentBuilder)
-     */
-    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");
-        }
-        configureMenuItem(item, shareIntent);
-    }
-
-    /**
-     * IntentBuilder is a helper for constructing {@link Intent#ACTION_SEND} and
-     * {@link Intent#ACTION_SEND_MULTIPLE} sharing intents and starting activities
-     * to share content. The ComponentName and package name of the calling activity
-     * will be included.
-     */
-    public static class IntentBuilder {
-        private Activity mActivity;
-        private Intent mIntent;
-        private CharSequence mChooserTitle;
-        private ArrayList<String> mToAddresses;
-        private ArrayList<String> mCcAddresses;
-        private ArrayList<String> mBccAddresses;
-
-        private ArrayList<Uri> mStreams;
-
-        /**
-         * Create a new IntentBuilder for launching a sharing action from launchingActivity.
-         *
-         * @param launchingActivity Activity that the share will be launched from
-         * @return a new IntentBuilder instance
-         */
-        public static IntentBuilder from(Activity launchingActivity) {
-            return new IntentBuilder(launchingActivity);
-        }
-
-        private IntentBuilder(Activity launchingActivity) {
-            mActivity = launchingActivity;
-            mIntent = new Intent().setAction(Intent.ACTION_SEND);
-            mIntent.putExtra(EXTRA_CALLING_PACKAGE, launchingActivity.getPackageName());
-            mIntent.putExtra(EXTRA_CALLING_ACTIVITY, launchingActivity.getComponentName());
-            mIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-        }
-
-        /**
-         * Retrieve the Intent as configured so far by the IntentBuilder. This Intent
-         * is suitable for use in a ShareActionProvider or chooser dialog.
-         *
-         * <p>To create an intent that will launch the activity chooser so that the user
-         * may select a target for the share, see {@link #createChooserIntent()}.
-         *
-         * @return The current Intent being configured by this builder
-         */
-        public Intent getIntent() {
-            if (mToAddresses != null) {
-                combineArrayExtra(Intent.EXTRA_EMAIL, mToAddresses);
-                mToAddresses = null;
-            }
-            if (mCcAddresses != null) {
-                combineArrayExtra(Intent.EXTRA_CC, mCcAddresses);
-                mCcAddresses = null;
-            }
-            if (mBccAddresses != null) {
-                combineArrayExtra(Intent.EXTRA_BCC, mBccAddresses);
-                mBccAddresses = null;
-            }
-
-            // Check if we need to change the action.
-            boolean needsSendMultiple = mStreams != null && mStreams.size() > 1;
-            boolean isSendMultiple = mIntent.getAction().equals(Intent.ACTION_SEND_MULTIPLE);
-
-            if (!needsSendMultiple && isSendMultiple) {
-                // Change back to a single send action; place the first stream into the
-                // intent for single sharing.
-                mIntent.setAction(Intent.ACTION_SEND);
-                if (mStreams != null && !mStreams.isEmpty()) {
-                    mIntent.putExtra(Intent.EXTRA_STREAM, mStreams.get(0));
-                } else {
-                    mIntent.removeExtra(Intent.EXTRA_STREAM);
-                }
-                mStreams = null;
-            }
-
-            if (needsSendMultiple && !isSendMultiple) {
-                // Change to a multiple send action; place the relevant ArrayList into the
-                // intent for multiple sharing.
-                mIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
-                if (mStreams != null && !mStreams.isEmpty()) {
-                    mIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mStreams);
-                } else {
-                    mIntent.removeExtra(Intent.EXTRA_STREAM);
-                }
-            }
-
-            return mIntent;
-        }
-
-        Activity getActivity() {
-            return mActivity;
-        }
-
-        private void combineArrayExtra(String extra, ArrayList<String> add) {
-            String[] currentAddresses = mIntent.getStringArrayExtra(extra);
-            int currentLength = currentAddresses != null ? currentAddresses.length : 0;
-            String[] finalAddresses = new String[currentLength + add.size()];
-            add.toArray(finalAddresses);
-            if (currentAddresses != null) {
-                System.arraycopy(currentAddresses, 0, finalAddresses, add.size(), currentLength);
-            }
-            mIntent.putExtra(extra, finalAddresses);
-        }
-
-        private void combineArrayExtra(String extra, String[] add) {
-            // Add any items still pending
-            Intent intent = getIntent();
-            String[] old = intent.getStringArrayExtra(extra);
-            int oldLength = old != null ? old.length : 0;
-            String[] result = new String[oldLength + add.length];
-            if (old != null) System.arraycopy(old, 0, result, 0, oldLength);
-            System.arraycopy(add, 0, result, oldLength, add.length);
-            intent.putExtra(extra, result);
-        }
-
-        /**
-         * Create an Intent that will launch the standard Android activity chooser,
-         * allowing the user to pick what activity/app on the system should handle
-         * the share.
-         *
-         * @return A chooser Intent for the currently configured sharing action
-         */
-        public Intent createChooserIntent() {
-            return Intent.createChooser(getIntent(), mChooserTitle);
-        }
-
-        /**
-         * Start a chooser activity for the current share intent.
-         *
-         * <p>Note that under most circumstances you should use
-         * {@link ShareCompat#configureMenuItem(MenuItem, IntentBuilder)
-         *  ShareCompat.configureMenuItem()} to add a Share item to the menu while
-         * presenting a detail view of the content to be shared instead
-         * of invoking this directly.</p>
-         */
-        public void startChooser() {
-            mActivity.startActivity(createChooserIntent());
-        }
-
-        /**
-         * Set the title that will be used for the activity chooser for this share.
-         *
-         * @param title Title string
-         * @return This IntentBuilder for method chaining
-         */
-        public IntentBuilder setChooserTitle(CharSequence title) {
-            mChooserTitle = title;
-            return this;
-        }
-
-        /**
-         * Set the title that will be used for the activity chooser for this share.
-         *
-         * @param resId Resource ID of the title string to use
-         * @return This IntentBuilder for method chaining
-         */
-        public IntentBuilder setChooserTitle(@StringRes int resId) {
-            return setChooserTitle(mActivity.getText(resId));
-        }
-
-        /**
-         * Set the type of data being shared
-         *
-         * @param mimeType mimetype of the shared data
-         * @return This IntentBuilder for method chaining
-         * @see Intent#setType(String)
-         */
-        public IntentBuilder setType(String mimeType) {
-            mIntent.setType(mimeType);
-            return this;
-        }
-
-        /**
-         * Set the literal text data to be sent as part of the share.
-         * This may be a styled CharSequence.
-         *
-         * @param text Text to share
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_TEXT
-         */
-        public IntentBuilder setText(CharSequence text) {
-            mIntent.putExtra(Intent.EXTRA_TEXT, text);
-            return this;
-        }
-
-        /**
-         * Set an HTML string to be sent as part of the share.
-         * If {@link Intent#EXTRA_TEXT EXTRA_TEXT} has not already been supplied,
-         * a styled version of the supplied HTML text will be added as EXTRA_TEXT as
-         * parsed by {@link android.text.Html#fromHtml(String) Html.fromHtml}.
-         *
-         * @param htmlText A string containing HTML markup as a richer version of the text
-         *                 provided by EXTRA_TEXT.
-         * @return This IntentBuilder for method chaining
-         * @see #setText(CharSequence)
-         */
-        public IntentBuilder setHtmlText(String htmlText) {
-            mIntent.putExtra(IntentCompat.EXTRA_HTML_TEXT, htmlText);
-            if (!mIntent.hasExtra(Intent.EXTRA_TEXT)) {
-                // Supply a default if EXTRA_TEXT isn't set
-                setText(Html.fromHtml(htmlText));
-            }
-            return this;
-        }
-
-        /**
-         * Set a stream URI to the data that should be shared.
-         *
-         * <p>This replaces all currently set stream URIs and will produce a single-stream
-         * ACTION_SEND intent.</p>
-         *
-         * @param streamUri URI of the stream to share
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_STREAM
-         */
-        public IntentBuilder setStream(Uri streamUri) {
-            if (!mIntent.getAction().equals(Intent.ACTION_SEND)) {
-                mIntent.setAction(Intent.ACTION_SEND);
-            }
-            mStreams = null;
-            mIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
-            return this;
-        }
-
-        /**
-         * Add a stream URI to the data that should be shared. If this is not the first
-         * stream URI added the final intent constructed will become an ACTION_SEND_MULTIPLE
-         * intent. Not all apps will handle both ACTION_SEND and ACTION_SEND_MULTIPLE.
-         *
-         * @param streamUri URI of the stream to share
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_STREAM
-         * @see Intent#ACTION_SEND
-         * @see Intent#ACTION_SEND_MULTIPLE
-         */
-        public IntentBuilder addStream(Uri streamUri) {
-            Uri currentStream = mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
-            if (mStreams == null && currentStream == null) {
-                return setStream(streamUri);
-            }
-            if (mStreams == null) {
-                mStreams = new ArrayList<Uri>();
-            }
-            if (currentStream != null) {
-                mIntent.removeExtra(Intent.EXTRA_STREAM);
-                mStreams.add(currentStream);
-            }
-            mStreams.add(streamUri);
-            return this;
-        }
-
-        /**
-         * Set an array of email addresses as recipients of this share.
-         * This replaces all current "to" recipients that have been set so far.
-         *
-         * @param addresses Email addresses to send to
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_EMAIL
-         */
-        public IntentBuilder setEmailTo(String[] addresses) {
-            if (mToAddresses != null) {
-                mToAddresses = null;
-            }
-            mIntent.putExtra(Intent.EXTRA_EMAIL, addresses);
-            return this;
-        }
-
-        /**
-         * Add an email address to be used in the "to" field of the final Intent.
-         *
-         * @param address Email address to send to
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_EMAIL
-         */
-        public IntentBuilder addEmailTo(String address) {
-            if (mToAddresses == null) {
-                mToAddresses = new ArrayList<String>();
-            }
-            mToAddresses.add(address);
-            return this;
-        }
-
-        /**
-         * Add an array of email addresses to be used in the "to" field of the final Intent.
-         *
-         * @param addresses Email addresses to send to
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_EMAIL
-         */
-        public IntentBuilder addEmailTo(String[] addresses) {
-            combineArrayExtra(Intent.EXTRA_EMAIL, addresses);
-            return this;
-        }
-
-        /**
-         * Set an array of email addresses to CC on this share.
-         * This replaces all current "CC" recipients that have been set so far.
-         *
-         * @param addresses Email addresses to CC on the share
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_CC
-         */
-        public IntentBuilder setEmailCc(String[] addresses) {
-            mIntent.putExtra(Intent.EXTRA_CC, addresses);
-            return this;
-        }
-
-        /**
-         * Add an email address to be used in the "cc" field of the final Intent.
-         *
-         * @param address Email address to CC
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_CC
-         */
-        public IntentBuilder addEmailCc(String address) {
-            if (mCcAddresses == null) {
-                mCcAddresses = new ArrayList<String>();
-            }
-            mCcAddresses.add(address);
-            return this;
-        }
-
-        /**
-         * Add an array of email addresses to be used in the "cc" field of the final Intent.
-         *
-         * @param addresses Email addresses to CC
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_CC
-         */
-        public IntentBuilder addEmailCc(String[] addresses) {
-            combineArrayExtra(Intent.EXTRA_CC, addresses);
-            return this;
-        }
-
-        /**
-         * Set an array of email addresses to BCC on this share.
-         * This replaces all current "BCC" recipients that have been set so far.
-         *
-         * @param addresses Email addresses to BCC on the share
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_BCC
-         */
-        public IntentBuilder setEmailBcc(String[] addresses) {
-            mIntent.putExtra(Intent.EXTRA_BCC, addresses);
-            return this;
-        }
-
-        /**
-         * Add an email address to be used in the "bcc" field of the final Intent.
-         *
-         * @param address Email address to BCC
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_BCC
-         */
-        public IntentBuilder addEmailBcc(String address) {
-            if (mBccAddresses == null) {
-                mBccAddresses = new ArrayList<String>();
-            }
-            mBccAddresses.add(address);
-            return this;
-        }
-
-        /**
-         * Add an array of email addresses to be used in the "bcc" field of the final Intent.
-         *
-         * @param addresses Email addresses to BCC
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_BCC
-         */
-        public IntentBuilder addEmailBcc(String[] addresses) {
-            combineArrayExtra(Intent.EXTRA_BCC, addresses);
-            return this;
-        }
-
-        /**
-         * Set a subject heading for this share; useful for sharing via email.
-         *
-         * @param subject Subject heading for this share
-         * @return This IntentBuilder for method chaining
-         * @see Intent#EXTRA_SUBJECT
-         */
-        public IntentBuilder setSubject(String subject) {
-            mIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
-            return this;
-        }
-    }
-
-    /**
-     * IntentReader is a helper for reading the data contained within a sharing (ACTION_SEND)
-     * Intent. It provides methods to parse standard elements included with a share
-     * in addition to extra metadata about the app that shared the content.
-     *
-     * <p>Social sharing apps are encouraged to provide attribution for the app that shared
-     * the content. IntentReader offers access to the application label, calling activity info,
-     * and application icon of the app that shared the content. This data may have been provided
-     * voluntarily by the calling app and should always be displayed to the user before submission
-     * for manual verification. The user should be offered the option to omit this information
-     * from shared posts if desired.</p>
-     *
-     * <p>Activities that intend to receive sharing intents should configure an intent-filter
-     * to accept {@link Intent#ACTION_SEND} intents ("android.intent.action.SEND") and optionally
-     * accept {@link Intent#ACTION_SEND_MULTIPLE} ("android.intent.action.SEND_MULTIPLE") if
-     * the activity is equipped to handle multiple data streams.</p>
-     */
-    public static class IntentReader {
-        private static final String TAG = "IntentReader";
-
-        private Activity mActivity;
-        private Intent mIntent;
-        private String mCallingPackage;
-        private ComponentName mCallingActivity;
-
-        private ArrayList<Uri> mStreams;
-
-        /**
-         * Get an IntentReader for parsing and interpreting the sharing intent
-         * used to start the given activity.
-         *
-         * @param activity Activity that was started to share content
-         * @return IntentReader for parsing sharing data
-         */
-        public static IntentReader from(Activity activity) {
-            return new IntentReader(activity);
-        }
-
-        private IntentReader(Activity activity) {
-            mActivity = activity;
-            mIntent = activity.getIntent();
-            mCallingPackage = ShareCompat.getCallingPackage(activity);
-            mCallingActivity = ShareCompat.getCallingActivity(activity);
-        }
-
-        /**
-         * Returns true if the activity this reader was obtained for was
-         * started with an {@link Intent#ACTION_SEND} or {@link Intent#ACTION_SEND_MULTIPLE}
-         * sharing Intent.
-         *
-         * @return true if the activity was started with an ACTION_SEND
-         *         or ACTION_SEND_MULTIPLE Intent
-         */
-        public boolean isShareIntent() {
-            final String action = mIntent.getAction();
-            return Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action);
-        }
-
-        /**
-         * Returns true if the activity this reader was obtained for was started with an
-         * {@link Intent#ACTION_SEND} intent and contains a single shared item.
-         * The shared content should be obtained using either the {@link #getText()}
-         * or {@link #getStream()} methods depending on the type of content shared.
-         *
-         * @return true if the activity was started with an ACTION_SEND intent
-         */
-        public boolean isSingleShare() {
-            return Intent.ACTION_SEND.equals(mIntent.getAction());
-        }
-
-        /**
-         * Returns true if the activity this reader was obtained for was started with an
-         * {@link Intent#ACTION_SEND_MULTIPLE} intent. The Intent may contain more than
-         * one stream item.
-         *
-         * @return true if the activity was started with an ACTION_SEND_MULTIPLE intent
-         */
-        public boolean isMultipleShare() {
-            return Intent.ACTION_SEND_MULTIPLE.equals(mIntent.getAction());
-        }
-
-        /**
-         * Get the mimetype of the data shared to this activity.
-         *
-         * @return mimetype of the shared data
-         * @see Intent#getType()
-         */
-        public String getType() {
-            return mIntent.getType();
-        }
-
-        /**
-         * Get the literal text shared with the target activity.
-         *
-         * @return Literal shared text or null if none was supplied
-         * @see Intent#EXTRA_TEXT
-         */
-        public CharSequence getText() {
-            return mIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
-        }
-
-        /**
-         * Get the styled HTML text shared with the target activity.
-         * If no HTML text was supplied but {@link Intent#EXTRA_TEXT} contained
-         * styled text, it will be converted to HTML if possible and returned.
-         * If the text provided by {@link Intent#EXTRA_TEXT} was not styled text,
-         * it will be escaped by {@link android.text.Html#escapeHtml(CharSequence)}
-         * and returned. If no text was provided at all, this method will return null.
-         *
-         * @return Styled text provided by the sender as HTML.
-         */
-        public String getHtmlText() {
-            String result = mIntent.getStringExtra(IntentCompat.EXTRA_HTML_TEXT);
-            if (result == null) {
-                CharSequence text = getText();
-                if (text instanceof Spanned) {
-                    result = Html.toHtml((Spanned) text);
-                } else if (text != null) {
-                    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.
-         *
-         * <p>This call will fail if the share intent contains multiple stream items.
-         * If {@link #isMultipleShare()} returns true the application should use
-         * {@link #getStream(int)} and {@link #getStreamCount()} to retrieve the
-         * included stream items.</p>
-         *
-         * @return A URI referring to a data stream to be shared or null if one was not supplied
-         * @see Intent#EXTRA_STREAM
-         */
-        public Uri getStream() {
-            return mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
-        }
-
-        /**
-         * Get the URI of a stream item shared with the target activity.
-         * Index should be in the range [0-getStreamCount()).
-         *
-         * @param index Index of text item to retrieve
-         * @return Requested stream item URI
-         * @see Intent#EXTRA_STREAM
-         * @see Intent#ACTION_SEND_MULTIPLE
-         */
-        public Uri getStream(int index) {
-            if (mStreams == null && isMultipleShare()) {
-                mStreams = mIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
-            }
-            if (mStreams != null) {
-                return mStreams.get(index);
-            }
-            if (index == 0) {
-                return mIntent.getParcelableExtra(Intent.EXTRA_STREAM);
-            }
-            throw new IndexOutOfBoundsException("Stream items available: " + getStreamCount()
-                    + " index requested: " + index);
-        }
-
-        /**
-         * Return the number of stream items shared. The return value will be 0 or 1 if
-         * this was an {@link Intent#ACTION_SEND} intent, or 0 or more if it was an
-         * {@link Intent#ACTION_SEND_MULTIPLE} intent.
-         *
-         * @return Count of text items contained within the Intent
-         */
-        public int getStreamCount() {
-            if (mStreams == null && isMultipleShare()) {
-                mStreams = mIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
-            }
-            if (mStreams != null) {
-                return mStreams.size();
-            }
-            return mIntent.hasExtra(Intent.EXTRA_STREAM) ? 1 : 0;
-        }
-
-        /**
-         * Get an array of Strings, each an email address to share to.
-         *
-         * @return An array of email addresses or null if none were supplied.
-         * @see Intent#EXTRA_EMAIL
-         */
-        public String[] getEmailTo() {
-            return mIntent.getStringArrayExtra(Intent.EXTRA_EMAIL);
-        }
-
-        /**
-         * Get an array of Strings, each an email address to CC on this share.
-         *
-         * @return An array of email addresses or null if none were supplied.
-         * @see Intent#EXTRA_CC
-         */
-        public String[] getEmailCc() {
-            return mIntent.getStringArrayExtra(Intent.EXTRA_CC);
-        }
-
-        /**
-         * Get an array of Strings, each an email address to BCC on this share.
-         *
-         * @return An array of email addresses or null if none were supplied.
-         * @see Intent#EXTRA_BCC
-         */
-        public String[] getEmailBcc() {
-            return mIntent.getStringArrayExtra(Intent.EXTRA_BCC);
-        }
-
-        /**
-         * Get a subject heading for this share; useful when sharing via email.
-         *
-         * @return The subject heading for this share or null if one was not supplied.
-         * @see Intent#EXTRA_SUBJECT
-         */
-        public String getSubject() {
-            return mIntent.getStringExtra(Intent.EXTRA_SUBJECT);
-        }
-
-        /**
-         * Get the name of the package that invoked this sharing intent. If the activity
-         * was not started for a result, IntentBuilder will read this from extra metadata placed
-         * in the Intent by ShareBuilder.
-         *
-         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-         * application. As such it should not be trusted for accuracy in the context of
-         * security or verification.</p>
-         *
-         * @return Name of the package that started this activity or null if unknown
-         * @see Activity#getCallingPackage()
-         * @see ShareCompat#EXTRA_CALLING_PACKAGE
-         */
-        public String getCallingPackage() {
-            return mCallingPackage;
-        }
-
-        /**
-         * Get the {@link ComponentName} of the Activity that invoked this sharing intent.
-         * If the target sharing activity was not started for a result, IntentBuilder will read
-         * this from extra metadata placed in the intent by ShareBuilder.
-         *
-         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-         * application. As such it should not be trusted for accuracy in the context of
-         * security or verification.</p>
-         *
-         * @return ComponentName of the calling Activity or null if unknown
-         * @see Activity#getCallingActivity()
-         * @see ShareCompat#EXTRA_CALLING_ACTIVITY
-         */
-        public ComponentName getCallingActivity() {
-            return mCallingActivity;
-        }
-
-        /**
-         * Get the icon of the calling activity as a Drawable if data about
-         * the calling activity is available.
-         *
-         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-         * application. As such it should not be trusted for accuracy in the context of
-         * security or verification.</p>
-         *
-         * @return The calling Activity's icon or null if unknown
-         */
-        public Drawable getCallingActivityIcon() {
-            if (mCallingActivity == null) return null;
-
-            PackageManager pm = mActivity.getPackageManager();
-            try {
-                return pm.getActivityIcon(mCallingActivity);
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Could not retrieve icon for calling activity", e);
-            }
-            return null;
-        }
-
-        /**
-         * Get the icon of the calling application as a Drawable if data
-         * about the calling package is available.
-         *
-         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-         * application. As such it should not be trusted for accuracy in the context of
-         * security or verification.</p>
-         *
-         * @return The calling application's icon or null if unknown
-         */
-        public Drawable getCallingApplicationIcon() {
-            if (mCallingPackage == null) return null;
-
-            PackageManager pm = mActivity.getPackageManager();
-            try {
-                return pm.getApplicationIcon(mCallingPackage);
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Could not retrieve icon for calling application", e);
-            }
-            return null;
-        }
-
-        /**
-         * Get the human-readable label (title) of the calling application if
-         * data about the calling package is available.
-         *
-         * <p><em>Note:</em> This data may have been provided voluntarily by the calling
-         * application. As such it should not be trusted for accuracy in the context of
-         * security or verification.</p>
-         *
-         * @return The calling application's label or null if unknown
-         */
-        public CharSequence getCallingApplicationLabel() {
-            if (mCallingPackage == null) return null;
-
-            PackageManager pm = mActivity.getPackageManager();
-            try {
-                return pm.getApplicationLabel(pm.getApplicationInfo(mCallingPackage, 0));
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Could not retrieve label for calling application", e);
-            }
-            return null;
-        }
-    }
-}
diff --git a/android/support/v4/app/SharedElementCallback.java b/android/support/v4/app/SharedElementCallback.java
deleted file mode 100644
index c218d86..0000000
--- a/android/support/v4/app/SharedElementCallback.java
+++ /dev/null
@@ -1,320 +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.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.ImageView.ScaleType;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Listener provided in
- * {@link FragmentActivity#setEnterSharedElementCallback(SharedElementCallback)} and
- * {@link FragmentActivity#setExitSharedElementCallback(SharedElementCallback)}
- * to monitor the Activity transitions. The events can be used to customize Activity
- * Transition behavior.
- */
-public abstract class SharedElementCallback {
-    private Matrix mTempMatrix;
-    private static int MAX_IMAGE_SIZE = (1024 * 1024);
-    private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap";
-    private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType";
-    private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
-
-    /**
-     * In Activity Transitions, onSharedElementStart is called immediately before
-     * capturing the start of the shared element state on enter and reenter transitions and
-     * immediately before capturing the end of the shared element state for exit and return
-     * transitions.
-     * <p>
-     * In Fragment Transitions, onSharedElementStart is called immediately before capturing the
-     * start state of all shared element transitions.
-     * <p>
-     * This call can be used to adjust the transition start state by modifying the shared
-     * element Views. Note that no layout step will be executed between onSharedElementStart
-     * and the transition state capture.
-     * <p>
-     * For Activity Transitions, any changes made in {@link #onSharedElementEnd(List, List, List)}
-     * that are not updated during layout should be corrected in onSharedElementStart for exit and
-     * return transitions. For example, rotation or scale will not be affected by layout and
-     * if changed in {@link #onSharedElementEnd(List, List, List)}, it will also have to be reset
-     * in onSharedElementStart again to correct the end state.
-     *
-     * @param sharedElementNames The names of the shared elements that were accepted into
-     *                           the View hierarchy.
-     * @param sharedElements The shared elements that are part of the View hierarchy.
-     * @param sharedElementSnapshots The Views containing snap shots of the shared element
-     *                               from the launching Window. These elements will not
-     *                               be part of the scene, but will be positioned relative
-     *                               to the Window decor View. This list is null for Fragment
-     *                               Transitions.
-     */
-    public void onSharedElementStart(List<String> sharedElementNames,
-            List<View> sharedElements, List<View> sharedElementSnapshots) {}
-
-    /**
-     * In Activity Transitions, onSharedElementEnd is called immediately before
-     * capturing the end of the shared element state on enter and reenter transitions and
-     * immediately before capturing the start of the shared element state for exit and return
-     * transitions.
-     * <p>
-     * In Fragment Transitions, onSharedElementEnd is called immediately before capturing the
-     * end state of all shared element transitions.
-     * <p>
-     * This call can be used to adjust the transition end state by modifying the shared
-     * element Views. Note that no layout step will be executed between onSharedElementEnd
-     * and the transition state capture.
-     * <p>
-     * Any changes made in {@link #onSharedElementStart(List, List, List)} that are not updated
-     * during layout should be corrected in onSharedElementEnd. For example, rotation or scale
-     * will not be affected by layout and if changed in
-     * {@link #onSharedElementStart(List, List, List)}, it will also have to be reset in
-     * onSharedElementEnd again to correct the end state.
-     *
-     * @param sharedElementNames The names of the shared elements that were accepted into
-     *                           the View hierarchy.
-     * @param sharedElements The shared elements that are part of the View hierarchy.
-     * @param sharedElementSnapshots The Views containing snap shots of the shared element
-     *                               from the launching Window. These elements will not
-     *                               be part of the scene, but will be positioned relative
-     *                               to the Window decor View. This list will be null for
-     *                               Fragment Transitions.
-     */
-    public void onSharedElementEnd(List<String> sharedElementNames,
-            List<View> sharedElements, List<View> sharedElementSnapshots) {}
-
-    /**
-     * Called after {@link #onMapSharedElements(java.util.List, java.util.Map)} when
-     * transferring shared elements in. Any shared elements that have no mapping will be in
-     * <var>rejectedSharedElements</var>. The elements remaining in
-     * <var>rejectedSharedElements</var> will be transitioned out of the Scene. If a
-     * View is removed from <var>rejectedSharedElements</var>, it must be handled by the
-     * <code>SharedElementListener</code>.
-     * <p>
-     * Views in rejectedSharedElements will have their position and size set to the
-     * position of the calling shared element, relative to the Window decor View and contain
-     * snapshots of the View from the calling Activity or Fragment. This
-     * view may be safely added to the decor View's overlay to remain in position.
-     * </p>
-     * <p>This method is not called for Fragment Transitions. All rejected shared elements
-     * will be handled by the exit transition.</p>
-     *
-     * @param rejectedSharedElements Views containing visual information of shared elements
-     *                               that are not part of the entering scene. These Views
-     *                               are positioned relative to the Window decor View. A
-     *                               View removed from this list will not be transitioned
-     *                               automatically.
-     */
-    public void onRejectSharedElements(List<View> rejectedSharedElements) {}
-
-    /**
-     * Lets the SharedElementCallback adjust the mapping of shared element names to
-     * Views.
-     *
-     * @param names The names of all shared elements transferred from the calling Activity
-     *              or Fragment in the order they were provided.
-     * @param sharedElements The mapping of shared element names to Views. The best guess
-     *                       will be filled into sharedElements based on the transitionNames.
-     */
-    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}
-
-
-    /**
-     * Creates a snapshot of a shared element to be used by the remote Activity and reconstituted
-     * with {@link #onCreateSnapshotView(android.content.Context, android.os.Parcelable)}. A
-     * null return value will mean that the remote Activity will have a null snapshot View in
-     * {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} and
-     * {@link #onSharedElementEnd(java.util.List, java.util.List, java.util.List)}.
-     *
-     * <p>This is not called for Fragment Transitions.</p>
-     *
-     * @param sharedElement The shared element View to create a snapshot for.
-     * @param viewToGlobalMatrix A matrix containing a transform from the view to the screen
-     *                           coordinates.
-     * @param screenBounds The bounds of shared element in screen coordinate space. This is
-     *                     the bounds of the view with the viewToGlobalMatrix applied.
-     * @return A snapshot to send to the remote Activity to be reconstituted with
-     * {@link #onCreateSnapshotView(android.content.Context, android.os.Parcelable)} and passed
-     * into {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} and
-     * {@link #onSharedElementEnd(java.util.List, java.util.List, java.util.List)}.
-     */
-    public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,
-            RectF screenBounds) {
-        if (sharedElement instanceof ImageView) {
-            ImageView imageView = ((ImageView) sharedElement);
-            Drawable d = imageView.getDrawable();
-            Drawable bg = imageView.getBackground();
-            if (d != null && bg == null) {
-                Bitmap bitmap = createDrawableBitmap(d);
-                if (bitmap != null) {
-                    Bundle bundle = new Bundle();
-                    bundle.putParcelable(BUNDLE_SNAPSHOT_BITMAP, bitmap);
-                    bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
-                            imageView.getScaleType().toString());
-                    if (imageView.getScaleType() == ScaleType.MATRIX) {
-                        Matrix matrix = imageView.getImageMatrix();
-                        float[] values = new float[9];
-                        matrix.getValues(values);
-                        bundle.putFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX, values);
-                    }
-                    return bundle;
-                }
-            }
-        }
-        int bitmapWidth = Math.round(screenBounds.width());
-        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 = (int) (bitmapWidth * scale);
-            bitmapHeight = (int) (bitmapHeight * scale);
-            if (mTempMatrix == null) {
-                mTempMatrix = new Matrix();
-            }
-            mTempMatrix.set(viewToGlobalMatrix);
-            mTempMatrix.postTranslate(-screenBounds.left, -screenBounds.top);
-            mTempMatrix.postScale(scale, scale);
-            bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(bitmap);
-            canvas.concat(mTempMatrix);
-            sharedElement.draw(canvas);
-        }
-        return bitmap;
-    }
-
-    /**
-     * Get a copy of bitmap of given drawable.
-     */
-    private static Bitmap createDrawableBitmap(Drawable drawable) {
-        int width = drawable.getIntrinsicWidth();
-        int height = drawable.getIntrinsicHeight();
-        if (width <= 0 || height <= 0) {
-            return null;
-        }
-        float scale = Math.min(1f, ((float)MAX_IMAGE_SIZE) / (width * height));
-        if (drawable instanceof BitmapDrawable && scale == 1f) {
-            // return same bitmap if scale down not needed
-            return ((BitmapDrawable) drawable).getBitmap();
-        }
-        int bitmapWidth = (int) (width * scale);
-        int bitmapHeight = (int) (height * scale);
-        Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        Rect existingBounds = drawable.getBounds();
-        int left = existingBounds.left;
-        int top = existingBounds.top;
-        int right = existingBounds.right;
-        int bottom = existingBounds.bottom;
-        drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
-        drawable.draw(canvas);
-        drawable.setBounds(left, top, right, bottom);
-        return bitmap;
-    }
-
-    /**
-     * Reconstitutes a snapshot View from a Parcelable returned in
-     * {@link #onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix,
-     * android.graphics.RectF)} to be used in {@link #onSharedElementStart(java.util.List,
-     * java.util.List, java.util.List)} and {@link #onSharedElementEnd(java.util.List,
-     * java.util.List, java.util.List)}. The returned View will be sized and positioned after
-     * this call so that it is ready to be added to the decor View's overlay.
-     *
-     * <p>This is not called for Fragment Transitions.</p>
-     *
-     * @param context The Context used to create the snapshot View.
-     * @param snapshot The Parcelable returned by {@link #onCaptureSharedElementSnapshot(
-     * android.view.View, android.graphics.Matrix, android.graphics.RectF)}.
-     * @return A View to be sent in {@link #onSharedElementStart(java.util.List, java.util.List,
-     * java.util.List)} and {@link #onSharedElementEnd(java.util.List, java.util.List,
-     * java.util.List)}. A null value will produce a null snapshot value for those two methods.
-     */
-    public View onCreateSnapshotView(Context context, Parcelable snapshot) {
-        ImageView view = null;
-        if (snapshot instanceof Bundle) {
-            Bundle bundle = (Bundle) snapshot;
-            Bitmap bitmap = (Bitmap) bundle.getParcelable(BUNDLE_SNAPSHOT_BITMAP);
-            if (bitmap == null) {
-                return null;
-            }
-            ImageView imageView = new ImageView(context);
-            view = imageView;
-            imageView.setImageBitmap(bitmap);
-            imageView.setScaleType(
-                    ScaleType.valueOf(bundle.getString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE)));
-            if (imageView.getScaleType() == ScaleType.MATRIX) {
-                float[] values = bundle.getFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX);
-                Matrix matrix = new Matrix();
-                matrix.setValues(values);
-                imageView.setImageMatrix(matrix);
-            }
-        } else if (snapshot instanceof Bitmap) {
-            Bitmap bitmap = (Bitmap) snapshot;
-            view = new ImageView(context);
-            view.setImageBitmap(bitmap);
-        }
-        return view;
-    }
-
-    /**
-     * Called during an Activity Transition when the shared elements have arrived at the
-     * final location and are ready to be transferred. This method is called for both the
-     * source and destination Activities.
-     * <p>
-     * When the shared elements are ready to be transferred,
-     * {@link OnSharedElementsReadyListener#onSharedElementsReady()}
-     * must be called to trigger the transfer.
-     * <p>
-     * The default behavior is to trigger the transfer immediately.
-     *
-     * @param sharedElementNames The names of the shared elements that are being transferred..
-     * @param sharedElements The shared elements that are part of the View hierarchy.
-     * @param listener The listener to call when the shared elements are ready to be hidden
-     *                 in the source Activity or shown in the destination Activity.
-     */
-    public void onSharedElementsArrived(List<String> sharedElementNames,
-            List<View> sharedElements, OnSharedElementsReadyListener listener) {
-        listener.onSharedElementsReady();
-    }
-
-    /**
-     * Listener to be called after {@link
-     * SharedElementCallback#onSharedElementsArrived(List, List, OnSharedElementsReadyListener)}
-     * when the shared elements are ready to be hidden in the source Activity and shown in the
-     * destination Activity.
-     */
-    public interface OnSharedElementsReadyListener {
-
-        /**
-         * Call this method during or after the OnSharedElementsReadyListener has been received
-         * in {@link SharedElementCallback#onSharedElementsArrived(List, List,
-         * OnSharedElementsReadyListener)} to indicate that the shared elements are ready to be
-         * hidden in the source and shown in the destination Activity.
-         */
-        void onSharedElementsReady();
-    }
-}
diff --git a/android/support/v4/app/SuperNotCalledException.java b/android/support/v4/app/SuperNotCalledException.java
deleted file mode 100644
index 0313eb9..0000000
--- a/android/support/v4/app/SuperNotCalledException.java
+++ /dev/null
@@ -1,25 +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.util.AndroidRuntimeException;
-
-final class SuperNotCalledException extends AndroidRuntimeException {
-    public SuperNotCalledException(String msg) {
-        super(msg);
-    }
-}
diff --git a/android/support/v4/app/SupportActivity.java b/android/support/v4/app/SupportActivity.java
deleted file mode 100644
index 51fcbf5..0000000
--- a/android/support/v4/app/SupportActivity.java
+++ /dev/null
@@ -1,99 +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 android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.arch.lifecycle.Lifecycle;
-import android.arch.lifecycle.LifecycleOwner;
-import android.arch.lifecycle.LifecycleRegistry;
-import android.arch.lifecycle.ReportFragment;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.util.SimpleArrayMap;
-
-/**
- * Base class for composing together compatibility functionality
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class SupportActivity extends Activity implements LifecycleOwner {
-    /**
-     * Storage for {@link ExtraData} instances.
-     *
-     * <p>Note that these objects are not retained across configuration changes</p>
-     */
-    private SimpleArrayMap<Class<? extends ExtraData>, ExtraData> mExtraDataMap =
-            new SimpleArrayMap<>();
-
-    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
-
-    /**
-     * Store an instance of {@link ExtraData} for later retrieval by class name
-     * via {@link #getExtraData}.
-     *
-     * <p>Note that these objects are not retained across configuration changes</p>
-     *
-     * @see #getExtraData
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void putExtraData(ExtraData extraData) {
-        mExtraDataMap.put(extraData.getClass(), extraData);
-    }
-
-    @Override
-    @SuppressWarnings("RestrictedApi")
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        ReportFragment.injectIfNeededIn(this);
-    }
-
-    @CallSuper
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
-        super.onSaveInstanceState(outState);
-    }
-
-    /**
-     * Retrieves a previously set {@link ExtraData} by class name.
-     *
-     * @see #putExtraData
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public <T extends ExtraData> T getExtraData(Class<T> extraDataClass) {
-        return (T) mExtraDataMap.get(extraDataClass);
-    }
-
-    @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycleRegistry;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class ExtraData {
-    }
-}
diff --git a/android/support/v4/app/TaskStackBuilder.java b/android/support/v4/app/TaskStackBuilder.java
deleted file mode 100644
index 14aadce..0000000
--- a/android/support/v4/app/TaskStackBuilder.java
+++ /dev/null
@@ -1,392 +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.Activity;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
-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.content.ContextCompat;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-/**
- * Utility class for constructing synthetic back stacks for cross-task navigation
- * on Android 3.0 and newer.
- *
- * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
- * app navigation using the back key changed. The back key's behavior is local
- * to the current task and does not capture navigation across different tasks.
- * Navigating across tasks and easily reaching the previous task is accomplished
- * through the "recents" UI, accessible through the software-provided Recents key
- * on the navigation or system bar. On devices with the older hardware button configuration
- * the recents UI can be accessed with a long press on the Home key.</p>
- *
- * <p>When crossing from one task stack to another post-Android 3.0,
- * the application should synthesize a back stack/history for the new task so that
- * the user may navigate out of the new task and back to the Launcher by repeated
- * presses of the back key. Back key presses should not navigate across task stacks.</p>
- *
- * <p>TaskStackBuilder provides a backward-compatible way to obey the correct conventions
- * around cross-task navigation on the device's version of the platform. On devices running
- * Android 3.0 or newer, calls to the {@link #startActivities()} method or sending the
- * {@link PendingIntent} generated by {@link #getPendingIntent(int, int)} will construct
- * the synthetic back stack as prescribed. On devices running older versions of the platform,
- * these same calls will invoke the topmost activity in the supplied stack, ignoring
- * the rest of the synthetic stack and allowing the back key to navigate back to the previous
- * task.</p>
- *
- * <div class="special reference">
- * <h3>About Navigation</h3>
- * For more detailed information about tasks, the back stack, and navigation design guidelines,
- * please read
- * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>
- * from the developer guide and <a href="{@docRoot}design/patterns/navigation.html">Navigation</a>
- * from the design guide.
- * </div>
- */
-public final class TaskStackBuilder implements Iterable<Intent> {
-    private static final String TAG = "TaskStackBuilder";
-
-    public interface SupportParentable {
-        @Nullable
-        Intent getSupportParentActivityIntent();
-    }
-
-    static class TaskStackBuilderBaseImpl {
-        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
-                    | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-            return PendingIntent.getActivities(context, requestCode, intents, flags);
-        }
-    }
-
-    @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
-                    | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-            return PendingIntent.getActivities(context, requestCode, intents, flags, options);
-        }
-    }
-
-    private static final TaskStackBuilderBaseImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 16) {
-            IMPL = new TaskStackBuilderApi16Impl();
-        } else {
-            IMPL = new TaskStackBuilderBaseImpl();
-        }
-    }
-
-    private final ArrayList<Intent> mIntents = new ArrayList<Intent>();
-    private final Context mSourceContext;
-
-    private TaskStackBuilder(Context a) {
-        mSourceContext = a;
-    }
-
-    /**
-     * Return a new TaskStackBuilder for launching a fresh task stack consisting
-     * of a series of activities.
-     *
-     * @param context The context that will launch the new task stack or generate a PendingIntent
-     * @return A new TaskStackBuilder
-     */
-    @NonNull
-    public static TaskStackBuilder create(@NonNull Context context) {
-        return new TaskStackBuilder(context);
-    }
-
-    /**
-     * Return a new TaskStackBuilder for launching a fresh task stack consisting
-     * of a series of activities.
-     *
-     * @param context The context that will launch the new task stack or generate a PendingIntent
-     * @return A new TaskStackBuilder
-     *
-     * @deprecated use {@link #create(Context)} instead
-     */
-    @Deprecated
-    public static TaskStackBuilder from(Context context) {
-        return create(context);
-    }
-
-    /**
-     * Add a new Intent to the task stack. The most recently added Intent will invoke
-     * the Activity at the top of the final task stack.
-     *
-     * @param nextIntent Intent for the next Activity in the synthesized task stack
-     * @return This TaskStackBuilder for method chaining
-     */
-    @NonNull
-    public TaskStackBuilder addNextIntent(@NonNull Intent nextIntent) {
-        mIntents.add(nextIntent);
-        return this;
-    }
-
-    /**
-     * Add a new Intent with the resolved chain of parents for the target activity to
-     * the task stack.
-     *
-     * <p>This is equivalent to calling {@link #addParentStack(ComponentName) addParentStack}
-     * with the resolved ComponentName of nextIntent (if it can be resolved), followed by
-     * {@link #addNextIntent(Intent) addNextIntent} with nextIntent.</p>
-     *
-     * @param nextIntent Intent for the topmost Activity in the synthesized task stack.
-     *                   Its chain of parents as specified in the manifest will be added.
-     * @return This TaskStackBuilder for method chaining.
-     */
-    @NonNull
-    public TaskStackBuilder addNextIntentWithParentStack(@NonNull Intent nextIntent) {
-        ComponentName target = nextIntent.getComponent();
-        if (target == null) {
-            target = nextIntent.resolveActivity(mSourceContext.getPackageManager());
-        }
-        if (target != null) {
-            addParentStack(target);
-        }
-        addNextIntent(nextIntent);
-        return this;
-    }
-
-    /**
-     * Add the activity parent chain as specified by manifest &lt;meta-data&gt; elements
-     * to the task stack builder.
-     *
-     * @param sourceActivity All parents of this activity will be added
-     * @return This TaskStackBuilder for method chaining
-     */
-    @NonNull
-    public TaskStackBuilder addParentStack(@NonNull Activity sourceActivity) {
-        Intent parent = null;
-        if (sourceActivity instanceof SupportParentable) {
-            parent = ((SupportParentable) sourceActivity).getSupportParentActivityIntent();
-        }
-        if (parent == null) {
-            parent = NavUtils.getParentActivityIntent(sourceActivity);
-        }
-
-        if (parent != null) {
-            // We have the actual parent intent, build the rest from static metadata
-            // then add the direct parent intent to the end.
-            ComponentName target = parent.getComponent();
-            if (target == null) {
-                target = parent.resolveActivity(mSourceContext.getPackageManager());
-            }
-            addParentStack(target);
-            addNextIntent(parent);
-        }
-        return this;
-    }
-
-    /**
-     * Add the activity parent chain as specified by manifest &lt;meta-data&gt; elements
-     * to the task stack builder.
-     *
-     * @param sourceActivityClass All parents of this activity will be added
-     * @return This TaskStackBuilder for method chaining
-     */
-    @NonNull
-    public TaskStackBuilder addParentStack(@NonNull Class<?> sourceActivityClass) {
-        return addParentStack(new ComponentName(mSourceContext, sourceActivityClass));
-    }
-
-    /**
-     * Add the activity parent chain as specified by manifest &lt;meta-data&gt; elements
-     * to the task stack builder.
-     *
-     * @param sourceActivityName Must specify an Activity component. All parents of
-     *                           this activity will be added
-     * @return This TaskStackBuilder for method chaining
-     */
-    public TaskStackBuilder addParentStack(ComponentName sourceActivityName) {
-        final int insertAt = mIntents.size();
-        try {
-            Intent parent = NavUtils.getParentActivityIntent(mSourceContext, sourceActivityName);
-            while (parent != null) {
-                mIntents.add(insertAt, parent);
-                parent = NavUtils.getParentActivityIntent(mSourceContext, parent.getComponent());
-            }
-        } catch (NameNotFoundException e) {
-            Log.e(TAG, "Bad ComponentName while traversing activity parent metadata");
-            throw new IllegalArgumentException(e);
-        }
-        return this;
-    }
-
-    /**
-     * @return the number of intents added so far.
-     */
-    public int getIntentCount() {
-        return mIntents.size();
-    }
-
-    /**
-     * Get the intent at the specified index.
-     * Useful if you need to modify the flags or extras of an intent that was previously added,
-     * for example with {@link #addParentStack(Activity)}.
-     *
-     * @param index Index from 0-getIntentCount()
-     * @return the intent at position index
-     *
-     * @deprecated Renamed to editIntentAt to better reflect intended usage
-     */
-    @Deprecated
-    public Intent getIntent(int index) {
-        return editIntentAt(index);
-    }
-
-    /**
-     * Return the intent at the specified index for modification.
-     * Useful if you need to modify the flags or extras of an intent that was previously added,
-     * for example with {@link #addParentStack(Activity)}.
-     *
-     * @param index Index from 0-getIntentCount()
-     * @return the intent at position index
-     */
-    @Nullable
-    public Intent editIntentAt(int index) {
-        return mIntents.get(index);
-    }
-
-    /**
-     * @deprecated Use editIntentAt instead
-     */
-    @Override
-    @Deprecated
-    public Iterator<Intent> iterator() {
-        return mIntents.iterator();
-    }
-
-    /**
-     * Start the task stack constructed by this builder. The Context used to obtain
-     * this builder must be an Activity.
-     *
-     * <p>On devices that do not support API level 11 or higher the topmost activity
-     * will be started as a new task. On devices that do support API level 11 or higher
-     * the new task stack will be created in its entirety.</p>
-     */
-    public void startActivities() {
-        startActivities(null);
-    }
-
-    /**
-     * Start the task stack constructed by this builder. The Context used to obtain
-     * this builder must be an Activity.
-     *
-     * <p>On devices that do not support API level 11 or higher the topmost activity
-     * will be started as a new task. On devices that do support API level 11 or higher
-     * the new task stack will be created in its entirety.</p>
-     *
-     * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)}
-     */
-    public void startActivities(@Nullable Bundle options) {
-        if (mIntents.isEmpty()) {
-            throw new IllegalStateException(
-                    "No intents added to TaskStackBuilder; cannot startActivities");
-        }
-
-        Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
-        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);
-            mSourceContext.startActivity(topIntent);
-        }
-    }
-
-    /**
-     * Obtain a {@link PendingIntent} for launching the task constructed by this builder so far.
-     *
-     * @param requestCode Private request code for the sender
-     * @param flags May be {@link PendingIntent#FLAG_ONE_SHOT},
-     *              {@link PendingIntent#FLAG_NO_CREATE}, {@link PendingIntent#FLAG_CANCEL_CURRENT},
-     *              {@link PendingIntent#FLAG_UPDATE_CURRENT}, or any of the flags supported by
-     *              {@link Intent#fillIn(Intent, int)} to control which unspecified parts of the
-     *              intent that can be supplied when the actual send happens.
-     * @return The obtained PendingIntent.  May return null only if
-     * {@link PendingIntent#FLAG_NO_CREATE} has been supplied.
-     */
-    @Nullable
-    public PendingIntent getPendingIntent(int requestCode, int flags) {
-        return getPendingIntent(requestCode, flags, null);
-    }
-
-    /**
-     * Obtain a {@link PendingIntent} for launching the task constructed by this builder so far.
-     *
-     * @param requestCode Private request code for the sender
-     * @param flags May be {@link PendingIntent#FLAG_ONE_SHOT},
-     *              {@link PendingIntent#FLAG_NO_CREATE}, {@link PendingIntent#FLAG_CANCEL_CURRENT},
-     *              {@link PendingIntent#FLAG_UPDATE_CURRENT}, or any of the flags supported by
-     *              {@link Intent#fillIn(Intent, int)} to control which unspecified parts of the
-     *              intent that can be supplied when the actual send happens.
-     * @param options Additional options for how the Activity should be started.
-     * See {@link android.content.Context#startActivity(Intent, Bundle)}
-     * @return The obtained PendingIntent.  May return null only if
-     * {@link PendingIntent#FLAG_NO_CREATE} has been supplied.
-     */
-    @Nullable
-    public PendingIntent getPendingIntent(int requestCode, int flags, @Nullable Bundle options) {
-        if (mIntents.isEmpty()) {
-            throw new IllegalStateException(
-                    "No intents added to TaskStackBuilder; cannot getPendingIntent");
-        }
-
-        Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
-        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);
-    }
-
-    /**
-     * Return an array containing the intents added to this builder. The intent at the
-     * root of the task stack will appear as the first item in the array and the
-     * intent at the top of the stack will appear as the last item.
-     *
-     * @return An array containing the intents added to this builder.
-     */
-    @NonNull
-    public Intent[] getIntents() {
-        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
-                | 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));
-        }
-        return intents;
-    }
-}
diff --git a/android/support/v4/content/AsyncTaskLoader.java b/android/support/v4/content/AsyncTaskLoader.java
deleted file mode 100644
index 5882f69..0000000
--- a/android/support/v4/content/AsyncTaskLoader.java
+++ /dev/null
@@ -1,372 +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.content;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.os.OperationCanceledException;
-import android.support.v4.util.TimeUtils;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-
-/**
- * Static library support version of the framework's {@link android.content.AsyncTaskLoader}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public abstract class AsyncTaskLoader<D> extends Loader<D> {
-    static final String TAG = "AsyncTaskLoader";
-    static final boolean DEBUG = false;
-
-    final class LoadTask extends ModernAsyncTask<Void, Void, D> implements Runnable {
-        private final CountDownLatch mDone = new CountDownLatch(1);
-
-        // Set to true to indicate that the task has been posted to a handler for
-        // execution at a later time.  Used to throttle updates.
-        boolean waiting;
-
-        /* Runs on a worker thread */
-        @Override
-        protected D doInBackground(Void... params) {
-            if (DEBUG) Log.v(TAG, this + " >>> doInBackground");
-            try {
-                D data = AsyncTaskLoader.this.onLoadInBackground();
-                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");
-                return data;
-            } catch (OperationCanceledException ex) {
-                if (!isCancelled()) {
-                    // onLoadInBackground threw a canceled exception spuriously.
-                    // This is problematic because it means that the LoaderManager did not
-                    // cancel the Loader itself and still expects to receive a result.
-                    // Additionally, the Loader's own state will not have been updated to
-                    // reflect the fact that the task was being canceled.
-                    // So we treat this case as an unhandled exception.
-                    throw ex;
-                }
-                if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);
-                return null;
-            }
-        }
-
-        /* Runs on the UI thread */
-        @Override
-        protected void onPostExecute(D data) {
-            if (DEBUG) Log.v(TAG, this + " onPostExecute");
-            try {
-                AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);
-            } finally {
-                mDone.countDown();
-            }
-        }
-
-        /* Runs on the UI thread */
-        @Override
-        protected void onCancelled(D data) {
-            if (DEBUG) Log.v(TAG, this + " onCancelled");
-            try {
-                AsyncTaskLoader.this.dispatchOnCancelled(this, data);
-            } finally {
-                mDone.countDown();
-            }
-        }
-
-        /* Runs on the UI thread, when the waiting task is posted to a handler.
-         * This method is only executed when task execution was deferred (waiting was true). */
-        @Override
-        public void run() {
-            waiting = false;
-            AsyncTaskLoader.this.executePendingTask();
-        }
-
-        /* Used for testing purposes to wait for the task to complete. */
-        public void waitForLoader() {
-            try {
-                mDone.await();
-            } catch (InterruptedException e) {
-                // Ignore
-            }
-        }
-    }
-
-    private final Executor mExecutor;
-
-    volatile LoadTask mTask;
-    volatile LoadTask mCancellingTask;
-
-    long mUpdateThrottle;
-    long mLastLoadCompleteTime = -10000;
-    Handler mHandler;
-
-    public AsyncTaskLoader(@NonNull Context context) {
-        this(context, ModernAsyncTask.THREAD_POOL_EXECUTOR);
-    }
-
-    private AsyncTaskLoader(@NonNull Context context, @NonNull Executor executor) {
-        super(context);
-        mExecutor = executor;
-    }
-
-    /**
-     * Set amount to throttle updates by.  This is the minimum time from
-     * when the last {@link #loadInBackground()} call has completed until
-     * a new load is scheduled.
-     *
-     * @param delayMS Amount of delay, in milliseconds.
-     */
-    public void setUpdateThrottle(long delayMS) {
-        mUpdateThrottle = delayMS;
-        if (delayMS != 0) {
-            mHandler = new Handler();
-        }
-    }
-
-    @Override
-    protected void onForceLoad() {
-        super.onForceLoad();
-        cancelLoad();
-        mTask = new LoadTask();
-        if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);
-        executePendingTask();
-    }
-
-    @Override
-    protected boolean onCancelLoad() {
-        if (DEBUG) Log.v(TAG, "onCancelLoad: mTask=" + mTask);
-        if (mTask != null) {
-            if (!mStarted) {
-                mContentChanged = true;
-            }
-            if (mCancellingTask != null) {
-                // There was a pending task already waiting for a previous
-                // one being canceled; just drop it.
-                if (DEBUG) Log.v(TAG,
-                        "cancelLoad: still waiting for cancelled task; dropping next");
-                if (mTask.waiting) {
-                    mTask.waiting = false;
-                    mHandler.removeCallbacks(mTask);
-                }
-                mTask = null;
-                return false;
-            } else if (mTask.waiting) {
-                // There is a task, but it is waiting for the time it should
-                // execute.  We can just toss it.
-                if (DEBUG) Log.v(TAG, "cancelLoad: task is waiting, dropping it");
-                mTask.waiting = false;
-                mHandler.removeCallbacks(mTask);
-                mTask = null;
-                return false;
-            } else {
-                boolean cancelled = mTask.cancel(false);
-                if (DEBUG) Log.v(TAG, "cancelLoad: cancelled=" + cancelled);
-                if (cancelled) {
-                    mCancellingTask = mTask;
-                    cancelLoadInBackground();
-                }
-                mTask = null;
-                return cancelled;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called if the task was canceled before it was completed.  Gives the class a chance
-     * to clean up post-cancellation and to properly dispose of the result.
-     *
-     * @param data The value that was returned by {@link #loadInBackground}, or null
-     * if the task threw {@link OperationCanceledException}.
-     */
-    public void onCanceled(@Nullable D data) {
-    }
-
-    void executePendingTask() {
-        if (mCancellingTask == null && mTask != null) {
-            if (mTask.waiting) {
-                mTask.waiting = false;
-                mHandler.removeCallbacks(mTask);
-            }
-            if (mUpdateThrottle > 0) {
-                long now = SystemClock.uptimeMillis();
-                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
-                    // Not yet time to do another load.
-                    if (DEBUG) Log.v(TAG, "Waiting until "
-                            + (mLastLoadCompleteTime+mUpdateThrottle)
-                            + " to execute: " + mTask);
-                    mTask.waiting = true;
-                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
-                    return;
-                }
-            }
-            if (DEBUG) Log.v(TAG, "Executing: " + mTask);
-            mTask.executeOnExecutor(mExecutor, (Void[]) null);
-        }
-    }
-
-    void dispatchOnCancelled(LoadTask task, D data) {
-        onCanceled(data);
-        if (mCancellingTask == task) {
-            if (DEBUG) Log.v(TAG, "Cancelled task is now canceled!");
-            rollbackContentChanged();
-            mLastLoadCompleteTime = SystemClock.uptimeMillis();
-            mCancellingTask = null;
-            if (DEBUG) Log.v(TAG, "Delivering cancellation");
-            deliverCancellation();
-            executePendingTask();
-        }
-    }
-
-    void dispatchOnLoadComplete(LoadTask task, D data) {
-        if (mTask != task) {
-            if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");
-            dispatchOnCancelled(task, data);
-        } else {
-            if (isAbandoned()) {
-                // This cursor has been abandoned; just cancel the new data.
-                onCanceled(data);
-            } else {
-                commitContentChanged();
-                mLastLoadCompleteTime = SystemClock.uptimeMillis();
-                mTask = null;
-                if (DEBUG) Log.v(TAG, "Delivering result");
-                deliverResult(data);
-            }
-        }
-    }
-
-    /**
-     * Called on a worker thread to perform the actual load and to return
-     * the result of the load operation.
-     *
-     * Implementations should not deliver the result directly, but should return them
-     * from this method, which will eventually end up calling {@link #deliverResult} on
-     * the UI thread.  If implementations need to process the results on the UI thread
-     * they may override {@link #deliverResult} and do so there.
-     *
-     * To support cancellation, this method should periodically check the value of
-     * {@link #isLoadInBackgroundCanceled} and terminate when it returns true.
-     * Subclasses may also override {@link #cancelLoadInBackground} to interrupt the load
-     * directly instead of polling {@link #isLoadInBackgroundCanceled}.
-     *
-     * When the load is canceled, this method may either return normally or throw
-     * {@link OperationCanceledException}.  In either case, the {@link Loader} will
-     * call {@link #onCanceled} to perform post-cancellation cleanup and to dispose of the
-     * result object, if any.
-     *
-     * @return The result of the load operation.
-     *
-     * @throws OperationCanceledException if the load is canceled during execution.
-     *
-     * @see #isLoadInBackgroundCanceled
-     * @see #cancelLoadInBackground
-     * @see #onCanceled
-     */
-    @Nullable
-    public abstract D loadInBackground();
-
-    /**
-     * Calls {@link #loadInBackground()}.
-     *
-     * This method is reserved for use by the loader framework.
-     * Subclasses should override {@link #loadInBackground} instead of this method.
-     *
-     * @return The result of the load operation.
-     *
-     * @throws OperationCanceledException if the load is canceled during execution.
-     *
-     * @see #loadInBackground
-     */
-    @Nullable
-    protected D onLoadInBackground() {
-        return loadInBackground();
-    }
-
-    /**
-     * Called on the main thread to abort a load in progress.
-     *
-     * Override this method to abort the current invocation of {@link #loadInBackground}
-     * that is running in the background on a worker thread.
-     *
-     * This method should do nothing if {@link #loadInBackground} has not started
-     * running or if it has already finished.
-     *
-     * @see #loadInBackground
-     */
-    public void cancelLoadInBackground() {
-    }
-
-    /**
-     * Returns true if the current invocation of {@link #loadInBackground} is being canceled.
-     *
-     * @return True if the current invocation of {@link #loadInBackground} is being canceled.
-     *
-     * @see #loadInBackground
-     */
-    public boolean isLoadInBackgroundCanceled() {
-        return mCancellingTask != null;
-    }
-
-    /**
-     * Locks the current thread until the loader completes the current load
-     * operation. Returns immediately if there is no load operation running.
-     * Should not be called from the UI thread: calling it from the UI
-     * thread would cause a deadlock.
-     * <p>
-     * Use for testing only.  <b>Never</b> call this from a UI thread.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void waitForLoader() {
-        LoadTask task = mTask;
-        if (task != null) {
-            task.waitForLoader();
-        }
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        if (mTask != null) {
-            writer.print(prefix); writer.print("mTask="); writer.print(mTask);
-                    writer.print(" waiting="); writer.println(mTask.waiting);
-        }
-        if (mCancellingTask != null) {
-            writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
-                    writer.print(" waiting="); writer.println(mCancellingTask.waiting);
-        }
-        if (mUpdateThrottle != 0) {
-            writer.print(prefix); writer.print("mUpdateThrottle=");
-                    TimeUtils.formatDuration(mUpdateThrottle, writer);
-                    writer.print(" mLastLoadCompleteTime=");
-                    TimeUtils.formatDuration(mLastLoadCompleteTime,
-                            SystemClock.uptimeMillis(), writer);
-                    writer.println();
-        }
-    }
-}
diff --git a/android/support/v4/content/ContentResolverCompat.java b/android/support/v4/content/ContentResolverCompat.java
deleted file mode 100644
index 1076738..0000000
--- a/android/support/v4/content/ContentResolverCompat.java
+++ /dev/null
@@ -1,101 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.content.ContentResolver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.support.v4.os.CancellationSignal;
-import android.support.v4.os.OperationCanceledException;
-
-/**
- * Helper for accessing features in {@link android.content.ContentResolver} in a backwards
- * compatible fashion.
- */
-public final class ContentResolverCompat {
-    private ContentResolverCompat() {
-        /* Hide constructor */
-    }
-
-    /**
-     * Query the given URI, returning a {@link Cursor} over the result set
-     * with optional support for cancellation.
-     * <p>
-     * For best performance, the caller should follow these guidelines:
-     * <ul>
-     * <li>Provide an explicit projection, to prevent
-     * reading data from storage that aren't going to be used.</li>
-     * <li>Use question mark parameter markers such as 'phone=?' instead of
-     * explicit values in the {@code selection} parameter, so that queries
-     * that differ only by those values will be recognized as the same
-     * for caching purposes.</li>
-     * </ul>
-     * </p>
-     *
-     * @param uri The URI, using the content:// scheme, for the content to
-     *         retrieve.
-     * @param projection A list of which columns to return. Passing null will
-     *         return all columns, which is inefficient.
-     * @param selection A filter declaring which rows to return, formatted as an
-     *         SQL WHERE clause (excluding the WHERE itself). Passing null will
-     *         return all rows for the given URI.
-     * @param selectionArgs You may include ?s in selection, which will be
-     *         replaced by the values from selectionArgs, in the order that they
-     *         appear in the selection. The values will be bound as Strings.
-     * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
-     *         clause (excluding the ORDER BY itself). Passing null will use the
-     *         default sort order, which may be unordered.
-     * @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 Cursor object, which is positioned before the first entry, or null
-     * @see Cursor
-     */
-    public static Cursor query(ContentResolver resolver,
-            Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder, CancellationSignal 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/android/support/v4/content/ContextCompat.java b/android/support/v4/content/ContextCompat.java
deleted file mode 100644
index 8a4fd7a..0000000
--- a/android/support/v4/content/ContextCompat.java
+++ /dev/null
@@ -1,576 +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.content.pm.ApplicationInfo;
-import android.content.res.ColorStateList;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.support.annotation.ColorInt;
-import android.support.annotation.ColorRes;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.ActivityOptionsCompat;
-import android.support.v4.os.EnvironmentCompat;
-import android.util.Log;
-import android.util.TypedValue;
-
-import java.io.File;
-
-/**
- * Helper for accessing features in {@link android.content.Context}.
- */
-public class ContextCompat {
-    private static final String TAG = "ContextCompat";
-
-    private static final Object sLock = new Object();
-
-    private static TypedValue sTempValue;
-
-    /**
-     * This class should not be instantiated, but the constructor must be
-     * visible for the class to be extended (ex. in ActivityCompat).
-     */
-    protected ContextCompat() {
-        // Not publicly instantiable, but may be extended.
-    }
-
-    /**
-     * Start a set of activities as a synthesized task stack, if able.
-     *
-     * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
-     * app navigation using the back key changed. The back key's behavior is local
-     * to the current task and does not capture navigation across different tasks.
-     * Navigating across tasks and easily reaching the previous task is accomplished
-     * through the "recents" UI, accessible through the software-provided Recents key
-     * on the navigation or system bar. On devices with the older hardware button configuration
-     * the recents UI can be accessed with a long press on the Home key.</p>
-     *
-     * <p>When crossing from one task stack to another post-Android 3.0,
-     * the application should synthesize a back stack/history for the new task so that
-     * the user may navigate out of the new task and back to the Launcher by repeated
-     * presses of the back key. Back key presses should not navigate across task stacks.</p>
-     *
-     * <p>startActivities provides a mechanism for constructing a synthetic task stack of
-     * multiple activities. If the underlying API is not available on the system this method
-     * will return false.</p>
-     *
-     * @param context Start activities using this activity as the starting context
-     * @param intents Array of intents defining the activities that will be started. The element
-     *                length-1 will correspond to the top activity on the resulting task stack.
-     * @return true if the underlying API was available and the call was successful, false otherwise
-     */
-    public static boolean startActivities(@NonNull Context context, @NonNull Intent[] intents) {
-        return startActivities(context, intents, null);
-    }
-
-    /**
-     * Start a set of activities as a synthesized task stack, if able.
-     *
-     * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
-     * app navigation using the back key changed. The back key's behavior is local
-     * to the current task and does not capture navigation across different tasks.
-     * Navigating across tasks and easily reaching the previous task is accomplished
-     * through the "recents" UI, accessible through the software-provided Recents key
-     * on the navigation or system bar. On devices with the older hardware button configuration
-     * the recents UI can be accessed with a long press on the Home key.</p>
-     *
-     * <p>When crossing from one task stack to another post-Android 3.0,
-     * the application should synthesize a back stack/history for the new task so that
-     * the user may navigate out of the new task and back to the Launcher by repeated
-     * presses of the back key. Back key presses should not navigate across task stacks.</p>
-     *
-     * <p>startActivities provides a mechanism for constructing a synthetic task stack of
-     * multiple activities. If the underlying API is not available on the system this method
-     * will return false.</p>
-     *
-     * @param context Start activities using this activity as the starting context
-     * @param intents Array of intents defining the activities that will be started. The element
-     *                length-1 will correspond to the top activity on the resulting task stack.
-     * @param options Additional options for how the Activity should be started.
-     * 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(@NonNull Context context, @NonNull Intent[] intents,
-            @Nullable Bundle options) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            context.startActivities(intents, options);
-        } else {
-            context.startActivities(intents);
-        }
-        return true;
-    }
-
-    /**
-     * Start an activity with additional launch information, if able.
-     *
-     * <p>In Android 4.1+ additional options were introduced to allow for more
-     * control on activity launch animations. Applications can use this method
-     * along with {@link ActivityOptionsCompat} to use these animations when
-     * available. When run on versions of the platform where this feature does
-     * not exist the activity will be launched normally.</p>
-     *
-     * @param context Context to launch activity from.
-     * @param intent The description of the activity to start.
-     * @param options Additional options for how the Activity should be started.
-     *                May be null if there are no options. See
-     *                {@link ActivityOptionsCompat} for how to build the Bundle
-     *                supplied here; there are no supported definitions for
-     *                building it manually.
-     */
-    public static void startActivity(@NonNull Context context, @NonNull Intent intent,
-            @Nullable Bundle options) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            context.startActivity(intent, options);
-        } else {
-            context.startActivity(intent);
-        }
-    }
-
-    /**
-     * Returns the absolute path to the directory on the filesystem where all
-     * private files belonging to this app are stored. Apps should not use this
-     * path directly; they should instead use {@link Context#getFilesDir()},
-     * {@link Context#getCacheDir()}, {@link Context#getDir(String, int)}, or
-     * other storage APIs on {@link Context}.
-     * <p>
-     * The returned path may change over time if the calling app is moved to an
-     * adopted storage device, so only relative paths should be persisted.
-     * <p>
-     * No additional permissions are required for the calling app to read or
-     * write files under the returned path.
-     *
-     * @see ApplicationInfo#dataDir
-     */
-    @Nullable
-    public static File getDataDir(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return context.getDataDir();
-        } else {
-            final String dataDir = context.getApplicationInfo().dataDir;
-            return dataDir != null ? new File(dataDir) : null;
-        }
-    }
-
-    /**
-     * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application's OBB files (if there are
-     * any) can be found. Note if the application does not have any OBB files,
-     * these directories may not exist.
-     * <p>
-     * This is like {@link Context#getFilesDir()} in that these files will be
-     * deleted when the application is uninstalled, however there are some
-     * important differences:
-     * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
-     * </ul>
-     * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
-     * <p>
-     * An application may store data on any or all of the returned devices. For
-     * example, an app may choose to store large files on the device with the
-     * most available space, as measured by {@link android.os.StatFs}.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
-     * are required to write to the returned paths; they're always accessible to
-     * the calling app. Before then,
-     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
-     * write. Write access outside of these paths on secondary external storage
-     * devices is not available. To request external storage access in a
-     * backwards compatible way, consider using {@code android:maxSdkVersion}
-     * like this:
-     *
-     * <pre class="prettyprint">&lt;uses-permission
-     *     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-     *     android:maxSdkVersion="18" /&gt;</pre>
-     * <p>
-     * The first path returned is the same as {@link Context#getObbDir()}.
-     * Returned paths may be {@code null} if a storage device is unavailable.
-     *
-     * @see Context#getObbDir()
-     * @see EnvironmentCompat#getStorageState(File)
-     */
-    @NonNull
-    public static File[] getObbDirs(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return context.getObbDirs();
-        } else {
-            return new File[] { context.getObbDir() };
-        }
-    }
-
-    /**
-     * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application can place persistent files
-     * it owns. These files are internal to the application, and not typically
-     * visible to the user as media.
-     * <p>
-     * This is like {@link Context#getFilesDir()} in that these files will be
-     * deleted when the application is uninstalled, however there are some
-     * important differences:
-     * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
-     * </ul>
-     * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
-     * <p>
-     * An application may store data on any or all of the returned devices. For
-     * example, an app may choose to store large files on the device with the
-     * most available space, as measured by {@link android.os.StatFs}.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
-     * are required to write to the returned paths; they're always accessible to
-     * the calling app. Before then,
-     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
-     * write. Write access outside of these paths on secondary external storage
-     * devices is not available. To request external storage access in a
-     * backwards compatible way, consider using {@code android:maxSdkVersion}
-     * like this:
-     *
-     * <pre class="prettyprint">&lt;uses-permission
-     *     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-     *     android:maxSdkVersion="18" /&gt;</pre>
-     * <p>
-     * The first path returned is the same as
-     * {@link Context#getExternalFilesDir(String)}. Returned paths may be
-     * {@code null} if a storage device is unavailable.
-     *
-     * @see Context#getExternalFilesDir(String)
-     * @see EnvironmentCompat#getStorageState(File)
-     */
-    @NonNull
-    public static File[] getExternalFilesDirs(@NonNull Context context, @Nullable String type) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return context.getExternalFilesDirs(type);
-        } else {
-            return new File[] { context.getExternalFilesDir(type) };
-        }
-    }
-
-    /**
-     * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application can place cache files it
-     * owns. These files are internal to the application, and not typically
-     * visible to the user as media.
-     * <p>
-     * This is like {@link Context#getCacheDir()} in that these files will be
-     * deleted when the application is uninstalled, however there are some
-     * important differences:
-     * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
-     * </ul>
-     * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
-     * <p>
-     * An application may store data on any or all of the returned devices. For
-     * example, an app may choose to store large files on the device with the
-     * most available space, as measured by {@link android.os.StatFs}.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
-     * are required to write to the returned paths; they're always accessible to
-     * the calling app. Before then,
-     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
-     * write. Write access outside of these paths on secondary external storage
-     * devices is not available. To request external storage access in a
-     * backwards compatible way, consider using {@code android:maxSdkVersion}
-     * like this:
-     *
-     * <pre class="prettyprint">&lt;uses-permission
-     *     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-     *     android:maxSdkVersion="18" /&gt;</pre>
-     * <p>
-     * The first path returned is the same as
-     * {@link Context#getExternalCacheDir()}. Returned paths may be {@code null}
-     * if a storage device is unavailable.
-     *
-     * @see Context#getExternalCacheDir()
-     * @see EnvironmentCompat#getStorageState(File)
-     */
-    @NonNull
-    public static File[] getExternalCacheDirs(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return context.getExternalCacheDirs();
-        } else {
-            return new File[] { context.getExternalCacheDir() };
-        }
-    }
-
-    private static File buildPath(File base, String... segments) {
-        File cur = base;
-        for (String segment : segments) {
-            if (cur == null) {
-                cur = new File(segment);
-            } else if (segment != null) {
-                cur = new File(cur, segment);
-            }
-        }
-        return cur;
-    }
-
-    /**
-     * Returns a drawable object associated with a particular resource ID.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the
-     * returned drawable will be styled for the specified Context's theme.
-     *
-     * @param id The desired resource identifier, as generated by the aapt tool.
-     *           This integer encodes the package, type, and resource entry.
-     *           The value 0 is an invalid identifier.
-     * @return Drawable An object that can be used to draw this resource.
-     */
-    @Nullable
-    public static final Drawable getDrawable(@NonNull Context context, @DrawableRes int id) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return context.getDrawable(id);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return context.getResources().getDrawable(id);
-        } else {
-            // Prior to JELLY_BEAN, Resources.getDrawable() would not correctly
-            // retrieve the final configuration density when the resource ID
-            // is a reference another Drawable resource. As a workaround, try
-            // to resolve the drawable reference manually.
-            final int resolvedId;
-            synchronized (sLock) {
-                if (sTempValue == null) {
-                    sTempValue = new TypedValue();
-                }
-                context.getResources().getValue(id, sTempValue, true);
-                resolvedId = sTempValue.resourceId;
-            }
-            return context.getResources().getDrawable(resolvedId);
-        }
-    }
-
-    /**
-     * Returns a color state list associated with a particular resource ID.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
-     * color state list will be styled for the specified Context's theme.
-     *
-     * @param id The desired resource identifier, as generated by the aapt
-     *           tool. This integer encodes the package, type, and resource
-     *           entry. The value 0 is an invalid identifier.
-     * @return A color state list, or {@code null} if the resource could not be
-     *         resolved.
-     * @throws android.content.res.Resources.NotFoundException if the given ID
-     *         does not exist.
-     */
-    @Nullable
-    public static final ColorStateList getColorStateList(@NonNull Context context,
-            @ColorRes int id) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return context.getColorStateList(id);
-        } else {
-            return context.getResources().getColorStateList(id);
-        }
-    }
-
-    /**
-     * Returns a color associated with a particular resource ID
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned
-     * color will be styled for the specified Context's theme.
-     *
-     * @param id The desired resource identifier, as generated by the aapt
-     *           tool. This integer encodes the package, type, and resource
-     *           entry. The value 0 is an invalid identifier.
-     * @return A single color value in the form 0xAARRGGBB.
-     * @throws android.content.res.Resources.NotFoundException if the given ID
-     *         does not exist.
-     */
-    @ColorInt
-    public static final int getColor(@NonNull Context context, @ColorRes int id) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return context.getColor(id);
-        } else {
-            return context.getResources().getColor(id);
-        }
-    }
-
-    /**
-     * Determine whether <em>you</em> have been granted a particular permission.
-     *
-     * @param permission The name of the permission being checked.
-     *
-     * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
-     * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
-     *
-     * @see android.content.pm.PackageManager#checkPermission(String, String)
-     */
-    public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
-        if (permission == null) {
-            throw new IllegalArgumentException("permission is null");
-        }
-
-        return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
-    }
-
-    /**
-     * Returns the absolute path to the directory on the filesystem similar to
-     * {@link Context#getFilesDir()}.  The difference is that files placed under this
-     * directory will be excluded from automatic backup to remote storage on
-     * devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later.
-     *
-     * <p>No permissions are required to read or write to the returned path, since this
-     * path is internal storage.
-     *
-     * @return The path of the directory holding application files that will not be
-     *         automatically backed up to remote storage.
-     *
-     * @see android.content.Context#getFilesDir()
-     */
-    @Nullable
-    public static final File getNoBackupFilesDir(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return context.getNoBackupFilesDir();
-        } else {
-            ApplicationInfo appInfo = context.getApplicationInfo();
-            return createFilesDir(new File(appInfo.dataDir, "no_backup"));
-        }
-    }
-
-    /**
-     * Returns the absolute path to the application specific cache directory on
-     * the filesystem designed for storing cached code. On devices running
-     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, the system will delete
-     * any files stored in this location both when your specific application is
-     * upgraded, and when the entire platform is upgraded.
-     * <p>
-     * This location is optimal for storing compiled or optimized code generated
-     * by your application at runtime.
-     * <p>
-     * Apps require no extra permissions to read or write to the returned path,
-     * since this path lives in their private storage.
-     *
-     * @return The path of the directory holding application code cache files.
-     */
-    public static File getCodeCacheDir(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return context.getCodeCacheDir();
-        } else {
-            ApplicationInfo appInfo = context.getApplicationInfo();
-            return createFilesDir(new File(appInfo.dataDir, "code_cache"));
-        }
-    }
-
-    private synchronized static File createFilesDir(File file) {
-        if (!file.exists()) {
-            if (!file.mkdirs()) {
-                if (file.exists()) {
-                    // spurious failure; probably racing with another process for this app
-                    return file;
-                }
-                Log.w(TAG, "Unable to create files subdir " + file.getPath());
-                return null;
-            }
-        }
-        return file;
-    }
-
-    /**
-     * Return a new Context object for the current Context but whose storage
-     * APIs are backed by device-protected storage.
-     * <p>
-     * On devices with direct boot, data stored in this location is encrypted
-     * with a key tied to the physical device, and it can be accessed
-     * immediately after the device has booted successfully, both
-     * <em>before and after</em> the user has authenticated with their
-     * credentials (such as a lock pattern or PIN).
-     * <p>
-     * Because device-protected data is available without user authentication,
-     * you should carefully limit the data you store using this Context. For
-     * example, storing sensitive authentication tokens or passwords in the
-     * device-protected area is strongly discouraged.
-     * <p>
-     * If the underlying device does not have the ability to store
-     * device-protected and credential-protected data using different keys, then
-     * both storage areas will become available at the same time. They remain as
-     * two distinct storage locations on disk, and only the window of
-     * availability changes.
-     * <p>
-     * Each call to this method returns a new instance of a Context object;
-     * Context objects are not shared, however common state (ClassLoader, other
-     * Resources for the same configuration) may be so the Context itself can be
-     * fairly lightweight.
-     * <p>
-     * Prior to API 24 this method returns
-     * {@code null}, since device-protected storage is not available.
-     *
-     * @see ContextCompat#isDeviceProtectedStorage(Context)
-     */
-    @Nullable
-    public static Context createDeviceProtectedStorageContext(@NonNull Context context) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return context.createDeviceProtectedStorageContext();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Indicates if the storage APIs of this Context are backed by
-     * device-encrypted storage.
-     *
-     * @see ContextCompat#createDeviceProtectedStorageContext(Context)
-     */
-    public static boolean isDeviceProtectedStorage(@NonNull Context 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(@NonNull Context context, @NonNull Intent intent) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            context.startForegroundService(intent);
-        } else {
-            // Pre-O behavior.
-            context.startService(intent);
-        }
-    }
-}
diff --git a/android/support/v4/content/CursorLoader.java b/android/support/v4/content/CursorLoader.java
deleted file mode 100644
index 5c6925d..0000000
--- a/android/support/v4/content/CursorLoader.java
+++ /dev/null
@@ -1,247 +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.content;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.os.CancellationSignal;
-import android.support.v4.os.OperationCanceledException;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Arrays;
-
-/**
- * Static library support version of the framework's {@link android.content.CursorLoader}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public class CursorLoader extends AsyncTaskLoader<Cursor> {
-    final ForceLoadContentObserver mObserver;
-
-    Uri mUri;
-    String[] mProjection;
-    String mSelection;
-    String[] mSelectionArgs;
-    String mSortOrder;
-
-    Cursor mCursor;
-    CancellationSignal mCancellationSignal;
-
-    /* Runs on a worker thread */
-    @Override
-    public Cursor loadInBackground() {
-        synchronized (this) {
-            if (isLoadInBackgroundCanceled()) {
-                throw new OperationCanceledException();
-            }
-            mCancellationSignal = new CancellationSignal();
-        }
-        try {
-            Cursor cursor = ContentResolverCompat.query(getContext().getContentResolver(),
-                    mUri, mProjection, mSelection, mSelectionArgs, mSortOrder,
-                    mCancellationSignal);
-            if (cursor != null) {
-                try {
-                    // Ensure the cursor window is filled.
-                    cursor.getCount();
-                    cursor.registerContentObserver(mObserver);
-                } catch (RuntimeException ex) {
-                    cursor.close();
-                    throw ex;
-                }
-            }
-            return cursor;
-        } finally {
-            synchronized (this) {
-                mCancellationSignal = null;
-            }
-        }
-    }
-
-    @Override
-    public void cancelLoadInBackground() {
-        super.cancelLoadInBackground();
-
-        synchronized (this) {
-            if (mCancellationSignal != null) {
-                mCancellationSignal.cancel();
-            }
-        }
-    }
-
-    /* Runs on the UI thread */
-    @Override
-    public void deliverResult(Cursor cursor) {
-        if (isReset()) {
-            // An async query came in while the loader is stopped
-            if (cursor != null) {
-                cursor.close();
-            }
-            return;
-        }
-        Cursor oldCursor = mCursor;
-        mCursor = cursor;
-
-        if (isStarted()) {
-            super.deliverResult(cursor);
-        }
-
-        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
-            oldCursor.close();
-        }
-    }
-
-    /**
-     * Creates an empty unspecified CursorLoader.  You must follow this with
-     * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc
-     * to specify the query to perform.
-     */
-    public CursorLoader(@NonNull Context context) {
-        super(context);
-        mObserver = new ForceLoadContentObserver();
-    }
-
-    /**
-     * Creates a fully-specified CursorLoader.  See
-     * {@link ContentResolver#query(Uri, String[], String, String[], String)
-     * ContentResolver.query()} for documentation on the meaning of the
-     * parameters.  These will be passed as-is to that call.
-     */
-    public CursorLoader(@NonNull Context context, @NonNull Uri uri, @Nullable String[] projection,
-            @Nullable String selection, @Nullable String[] selectionArgs,
-            @Nullable String sortOrder) {
-        super(context);
-        mObserver = new ForceLoadContentObserver();
-        mUri = uri;
-        mProjection = projection;
-        mSelection = selection;
-        mSelectionArgs = selectionArgs;
-        mSortOrder = sortOrder;
-    }
-
-    /**
-     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
-     * will be called on the UI thread. If a previous load has been completed and is still valid
-     * the result may be passed to the callbacks immediately.
-     *
-     * Must be called from the UI thread
-     */
-    @Override
-    protected void onStartLoading() {
-        if (mCursor != null) {
-            deliverResult(mCursor);
-        }
-        if (takeContentChanged() || mCursor == null) {
-            forceLoad();
-        }
-    }
-
-    /**
-     * Must be called from the UI thread
-     */
-    @Override
-    protected void onStopLoading() {
-        // Attempt to cancel the current load task if possible.
-        cancelLoad();
-    }
-
-    @Override
-    public void onCanceled(Cursor cursor) {
-        if (cursor != null && !cursor.isClosed()) {
-            cursor.close();
-        }
-    }
-
-    @Override
-    protected void onReset() {
-        super.onReset();
-        
-        // Ensure the loader is stopped
-        onStopLoading();
-
-        if (mCursor != null && !mCursor.isClosed()) {
-            mCursor.close();
-        }
-        mCursor = null;
-    }
-
-    @NonNull
-    public Uri getUri() {
-        return mUri;
-    }
-
-    public void setUri(@NonNull Uri uri) {
-        mUri = uri;
-    }
-
-    @Nullable
-    public String[] getProjection() {
-        return mProjection;
-    }
-
-    public void setProjection(@Nullable String[] projection) {
-        mProjection = projection;
-    }
-
-    @Nullable
-    public String getSelection() {
-        return mSelection;
-    }
-
-    public void setSelection(@Nullable String selection) {
-        mSelection = selection;
-    }
-
-    @Nullable
-    public String[] getSelectionArgs() {
-        return mSelectionArgs;
-    }
-
-    public void setSelectionArgs(@Nullable String[] selectionArgs) {
-        mSelectionArgs = selectionArgs;
-    }
-
-    @Nullable
-    public String getSortOrder() {
-        return mSortOrder;
-    }
-
-    public void setSortOrder(@Nullable String sortOrder) {
-        mSortOrder = sortOrder;
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        writer.print(prefix); writer.print("mUri="); writer.println(mUri);
-        writer.print(prefix); writer.print("mProjection=");
-                writer.println(Arrays.toString(mProjection));
-        writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
-        writer.print(prefix); writer.print("mSelectionArgs=");
-                writer.println(Arrays.toString(mSelectionArgs));
-        writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
-        writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
-        writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
-    }
-}
diff --git a/android/support/v4/content/FileProvider.java b/android/support/v4/content/FileProvider.java
deleted file mode 100644
index 16164be..0000000
--- a/android/support/v4/content/FileProvider.java
+++ /dev/null
@@ -1,833 +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 static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.res.XmlResourceParser;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.provider.OpenableColumns;
-import android.support.annotation.GuardedBy;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-import android.webkit.MimeTypeMap;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * FileProvider is a special subclass of {@link ContentProvider} that facilitates secure sharing
- * of files associated with an app by creating a <code>content://</code> {@link Uri} for a file
- * instead of a <code>file:///</code> {@link Uri}.
- * <p>
- * A content URI allows you to grant read and write access using
- * temporary access permissions. When you create an {@link Intent} containing
- * a content URI, in order to send the content URI
- * to a client app, you can also call {@link Intent#setFlags(int) Intent.setFlags()} to add
- * permissions. These permissions are available to the client app for as long as the stack for
- * a receiving {@link android.app.Activity} is active. For an {@link Intent} going to a
- * {@link android.app.Service}, the permissions are available as long as the
- * {@link android.app.Service} is running.
- * <p>
- * In comparison, to control access to a <code>file:///</code> {@link Uri} you have to modify the
- * file system permissions of the underlying file. The permissions you provide become available to
- * <em>any</em> app, and remain in effect until you change them. This level of access is
- * fundamentally insecure.
- * <p>
- * The increased level of file access security offered by a content URI
- * makes FileProvider a key part of Android's security infrastructure.
- * <p>
- * This overview of FileProvider includes the following topics:
- * </p>
- * <ol>
- *     <li><a href="#ProviderDefinition">Defining a FileProvider</a></li>
- *     <li><a href="#SpecifyFiles">Specifying Available Files</a></li>
- *     <li><a href="#GetUri">Retrieving the Content URI for a File</li>
- *     <li><a href="#Permissions">Granting Temporary Permissions to a URI</a></li>
- *     <li><a href="#ServeUri">Serving a Content URI to Another App</a></li>
- * </ol>
- * <h3 id="ProviderDefinition">Defining a FileProvider</h3>
- * <p>
- * Since the default functionality of FileProvider includes content URI generation for files, you
- * don't need to define a subclass in code. Instead, you can include a FileProvider in your app
- * by specifying it entirely in XML. To specify the FileProvider component itself, add a
- * <code><a href="{@docRoot}guide/topics/manifest/provider-element.html">&lt;provider&gt;</a></code>
- * element to your app manifest. Set the <code>android:name</code> attribute to
- * <code>android.support.v4.content.FileProvider</code>. Set the <code>android:authorities</code>
- * attribute to a URI authority based on a domain you control; for example, if you control the
- * domain <code>mydomain.com</code> you should use the authority
- * <code>com.mydomain.fileprovider</code>. Set the <code>android:exported</code> attribute to
- * <code>false</code>; the FileProvider does not need to be public. Set the
- * <a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"
- * >android:grantUriPermissions</a> attribute to <code>true</code>, to allow you
- * to grant temporary access to files. For example:
- * <pre class="prettyprint">
- *&lt;manifest&gt;
- *    ...
- *    &lt;application&gt;
- *        ...
- *        &lt;provider
- *            android:name="android.support.v4.content.FileProvider"
- *            android:authorities="com.mydomain.fileprovider"
- *            android:exported="false"
- *            android:grantUriPermissions="true"&gt;
- *            ...
- *        &lt;/provider&gt;
- *        ...
- *    &lt;/application&gt;
- *&lt;/manifest&gt;</pre>
- * <p>
- * If you want to override any of the default behavior of FileProvider methods, extend
- * the FileProvider class and use the fully-qualified class name in the <code>android:name</code>
- * attribute of the <code>&lt;provider&gt;</code> element.
- * <h3 id="SpecifyFiles">Specifying Available Files</h3>
- * A FileProvider can only generate a content URI for files in directories that you specify
- * beforehand. To specify a directory, specify the its storage area and path in XML, using child
- * elements of the <code>&lt;paths&gt;</code> element.
- * For example, the following <code>paths</code> element tells FileProvider that you intend to
- * request content URIs for the <code>images/</code> subdirectory of your private file area.
- * <pre class="prettyprint">
- *&lt;paths xmlns:android="http://schemas.android.com/apk/res/android"&gt;
- *    &lt;files-path name="my_images" path="images/"/&gt;
- *    ...
- *&lt;/paths&gt;
- *</pre>
- * <p>
- * The <code>&lt;paths&gt;</code> element must contain one or more of the following child elements:
- * </p>
- * <dl>
- *     <dt>
- * <pre class="prettyprint">
- *&lt;files-path name="<i>name</i>" path="<i>path</i>" /&gt;
- *</pre>
- *     </dt>
- *     <dd>
- *     Represents files in the <code>files/</code> subdirectory of your app's internal storage
- *     area. This subdirectory is the same as the value returned by {@link Context#getFilesDir()
- *     Context.getFilesDir()}.
- *     </dd>
- *     <dt>
- * <pre>
- *&lt;cache-path name="<i>name</i>" path="<i>path</i>" /&gt;
- *</pre>
- *     <dt>
- *     <dd>
- *     Represents files in the cache subdirectory of your app's internal storage area. The root path
- *     of this subdirectory is the same as the value returned by {@link Context#getCacheDir()
- *     getCacheDir()}.
- *     </dd>
- *     <dt>
- * <pre class="prettyprint">
- *&lt;external-path name="<i>name</i>" path="<i>path</i>" /&gt;
- *</pre>
- *     </dt>
- *     <dd>
- *     Represents files in the root of the external storage area. The root path of this subdirectory
- *     is the same as the value returned by
- *     {@link Environment#getExternalStorageDirectory() Environment.getExternalStorageDirectory()}.
- *     </dd>
- *     <dt>
- * <pre class="prettyprint">
- *&lt;external-files-path name="<i>name</i>" path="<i>path</i>" /&gt;
- *</pre>
- *     </dt>
- *     <dd>
- *     Represents files in the root of your app's external storage area. The root path of this
- *     subdirectory is the same as the value returned by
- *     {@code Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)}.
- *     </dd>
- *     <dt>
- * <pre class="prettyprint">
- *&lt;external-cache-path name="<i>name</i>" path="<i>path</i>" /&gt;
- *</pre>
- *     </dt>
- *     <dd>
- *     Represents files in the root of your app's external cache area. The root path of this
- *     subdirectory is the same as the value returned by
- *     {@link Context#getExternalCacheDir() Context.getExternalCacheDir()}.
- *     </dd>
- *     <dt>
- * <pre class="prettyprint">
- *&lt;external-media-path name="<i>name</i>" path="<i>path</i>" /&gt;
- *</pre>
- *     </dt>
- *     <dd>
- *     Represents files in the root of your app's external media area. The root path of this
- *     subdirectory is the same as the value returned by the first result of
- *     {@link Context#getExternalMediaDirs() Context.getExternalMediaDirs()}.
- *     <p><strong>Note:</strong> this directory is only available on API 21+ devices.</p>
- *     </dd>
- * </dl>
- * <p>
- *     These child elements all use the same attributes:
- * </p>
- * <dl>
- *     <dt>
- *         <code>name="<i>name</i>"</code>
- *     </dt>
- *     <dd>
- *         A URI path segment. To enforce security, this value hides the name of the subdirectory
- *         you're sharing. The subdirectory name for this value is contained in the
- *         <code>path</code> attribute.
- *     </dd>
- *     <dt>
- *         <code>path="<i>path</i>"</code>
- *     </dt>
- *     <dd>
- *         The subdirectory you're sharing. While the <code>name</code> attribute is a URI path
- *         segment, the <code>path</code> value is an actual subdirectory name. Notice that the
- *         value refers to a <b>subdirectory</b>, not an individual file or files. You can't
- *         share a single file by its file name, nor can you specify a subset of files using
- *         wildcards.
- *     </dd>
- * </dl>
- * <p>
- * You must specify a child element of <code>&lt;paths&gt;</code> for each directory that contains
- * files for which you want content URIs. For example, these XML elements specify two directories:
- * <pre class="prettyprint">
- *&lt;paths xmlns:android="http://schemas.android.com/apk/res/android"&gt;
- *    &lt;files-path name="my_images" path="images/"/&gt;
- *    &lt;files-path name="my_docs" path="docs/"/&gt;
- *&lt;/paths&gt;
- *</pre>
- * <p>
- * Put the <code>&lt;paths&gt;</code> element and its children in an XML file in your project.
- * For example, you can add them to a new file called <code>res/xml/file_paths.xml</code>.
- * To link this file to the FileProvider, add a
- * <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">&lt;meta-data&gt;</a> element
- * as a child of the <code>&lt;provider&gt;</code> element that defines the FileProvider. Set the
- * <code>&lt;meta-data&gt;</code> element's "android:name" attribute to
- * <code>android.support.FILE_PROVIDER_PATHS</code>. Set the element's "android:resource" attribute
- * to <code>&#64;xml/file_paths</code> (notice that you don't specify the <code>.xml</code>
- * extension). For example:
- * <pre class="prettyprint">
- *&lt;provider
- *    android:name="android.support.v4.content.FileProvider"
- *    android:authorities="com.mydomain.fileprovider"
- *    android:exported="false"
- *    android:grantUriPermissions="true"&gt;
- *    &lt;meta-data
- *        android:name="android.support.FILE_PROVIDER_PATHS"
- *        android:resource="&#64;xml/file_paths" /&gt;
- *&lt;/provider&gt;
- *</pre>
- * <h3 id="GetUri">Generating the Content URI for a File</h3>
- * <p>
- * To share a file with another app using a content URI, your app has to generate the content URI.
- * To generate the content URI, create a new {@link File} for the file, then pass the {@link File}
- * to {@link #getUriForFile(Context, String, File) getUriForFile()}. You can send the content URI
- * returned by {@link #getUriForFile(Context, String, File) getUriForFile()} to another app in an
- * {@link android.content.Intent}. The client app that receives the content URI can open the file
- * and access its contents by calling
- * {@link android.content.ContentResolver#openFileDescriptor(Uri, String)
- * ContentResolver.openFileDescriptor} to get a {@link ParcelFileDescriptor}.
- * <p>
- * For example, suppose your app is offering files to other apps with a FileProvider that has the
- * authority <code>com.mydomain.fileprovider</code>. To get a content URI for the file
- * <code>default_image.jpg</code> in the <code>images/</code> subdirectory of your internal storage
- * add the following code:
- * <pre class="prettyprint">
- *File imagePath = new File(Context.getFilesDir(), "images");
- *File newFile = new File(imagePath, "default_image.jpg");
- *Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
- *</pre>
- * As a result of the previous snippet,
- * {@link #getUriForFile(Context, String, File) getUriForFile()} returns the content URI
- * <code>content://com.mydomain.fileprovider/my_images/default_image.jpg</code>.
- * <h3 id="Permissions">Granting Temporary Permissions to a URI</h3>
- * To grant an access permission to a content URI returned from
- * {@link #getUriForFile(Context, String, File) getUriForFile()}, do one of the following:
- * <ul>
- * <li>
- *     Call the method
- *     {@link Context#grantUriPermission(String, Uri, int)
- *     Context.grantUriPermission(package, Uri, mode_flags)} for the <code>content://</code>
- *     {@link Uri}, using the desired mode flags. This grants temporary access permission for the
- *     content URI to the specified package, according to the value of the
- *     the <code>mode_flags</code> parameter, which you can set to
- *     {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}
- *     or both. The permission remains in effect until you revoke it by calling
- *     {@link Context#revokeUriPermission(Uri, int) revokeUriPermission()} or until the device
- *     reboots.
- * </li>
- * <li>
- *     Put the content URI in an {@link Intent} by calling {@link Intent#setData(Uri) setData()}.
- * </li>
- * <li>
- *     Next, call the method {@link Intent#setFlags(int) Intent.setFlags()} with either
- *     {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} or
- *     {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION} or both.
- * </li>
- * <li>
- *     Finally, send the {@link Intent} to
- *     another app. Most often, you do this by calling
- *     {@link android.app.Activity#setResult(int, android.content.Intent) setResult()}.
- *     <p>
- *     Permissions granted in an {@link Intent} remain in effect while the stack of the receiving
- *     {@link android.app.Activity} is active. When the stack finishes, the permissions are
- *     automatically removed. Permissions granted to one {@link android.app.Activity} in a client
- *     app are automatically extended to other components of that app.
- *     </p>
- * </li>
- * </ul>
- * <h3 id="ServeUri">Serving a Content URI to Another App</h3>
- * <p>
- * There are a variety of ways to serve the content URI for a file to a client app. One common way
- * is for the client app to start your app by calling
- * {@link android.app.Activity#startActivityForResult(Intent, int, Bundle) startActivityResult()},
- * which sends an {@link Intent} to your app to start an {@link android.app.Activity} in your app.
- * In response, your app can immediately return a content URI to the client app or present a user
- * interface that allows the user to pick a file. In the latter case, once the user picks the file
- * your app can return its content URI. In both cases, your app returns the content URI in an
- * {@link Intent} sent via {@link android.app.Activity#setResult(int, Intent) setResult()}.
- * </p>
- * <p>
- *  You can also put the content URI in a {@link android.content.ClipData} object and then add the
- *  object to an {@link Intent} you send to a client app. To do this, call
- *  {@link Intent#setClipData(ClipData) Intent.setClipData()}. When you use this approach, you can
- *  add multiple {@link android.content.ClipData} objects to the {@link Intent}, each with its own
- *  content URI. When you call {@link Intent#setFlags(int) Intent.setFlags()} on the {@link Intent}
- *  to set temporary access permissions, the same permissions are applied to all of the content
- *  URIs.
- * </p>
- * <p class="note">
- *  <strong>Note:</strong> The {@link Intent#setClipData(ClipData) Intent.setClipData()} method is
- *  only available in platform version 16 (Android 4.1) and later. If you want to maintain
- *  compatibility with previous versions, you should send one content URI at a time in the
- *  {@link Intent}. Set the action to {@link Intent#ACTION_SEND} and put the URI in data by calling
- *  {@link Intent#setData setData()}.
- * </p>
- * <h3 id="">More Information</h3>
- * <p>
- *    To learn more about FileProvider, see the Android training class
- *    <a href="{@docRoot}training/secure-file-sharing/index.html">Sharing Files Securely with URIs</a>.
- * </p>
- */
-public class FileProvider extends ContentProvider {
-    private static final String[] COLUMNS = {
-            OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
-
-    private static final String
-            META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS";
-
-    private static final String TAG_ROOT_PATH = "root-path";
-    private static final String TAG_FILES_PATH = "files-path";
-    private static final String TAG_CACHE_PATH = "cache-path";
-    private static final String TAG_EXTERNAL = "external-path";
-    private static final String TAG_EXTERNAL_FILES = "external-files-path";
-    private static final String TAG_EXTERNAL_CACHE = "external-cache-path";
-    private static final String TAG_EXTERNAL_MEDIA = "external-media-path";
-
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_PATH = "path";
-
-    private static final File DEVICE_ROOT = new File("/");
-
-    @GuardedBy("sCache")
-    private static HashMap<String, PathStrategy> sCache = new HashMap<String, PathStrategy>();
-
-    private PathStrategy mStrategy;
-
-    /**
-     * The default FileProvider implementation does not need to be initialized. If you want to
-     * override this method, you must provide your own subclass of FileProvider.
-     */
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    /**
-     * After the FileProvider is instantiated, this method is called to provide the system with
-     * information about the provider.
-     *
-     * @param context A {@link Context} for the current component.
-     * @param info A {@link ProviderInfo} for the new provider.
-     */
-    @Override
-    public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) {
-        super.attachInfo(context, info);
-
-        // Sanity check our security
-        if (info.exported) {
-            throw new SecurityException("Provider must not be exported");
-        }
-        if (!info.grantUriPermissions) {
-            throw new SecurityException("Provider must grant uri permissions");
-        }
-
-        mStrategy = getPathStrategy(context, info.authority);
-    }
-
-    /**
-     * Return a content URI for a given {@link File}. Specific temporary
-     * permissions for the content URI can be set with
-     * {@link Context#grantUriPermission(String, Uri, int)}, or added
-     * to an {@link Intent} by calling {@link Intent#setData(Uri) setData()} and then
-     * {@link Intent#setFlags(int) setFlags()}; in both cases, the applicable flags are
-     * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and
-     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. A FileProvider can only return a
-     * <code>content</code> {@link Uri} for file paths defined in their <code>&lt;paths&gt;</code>
-     * meta-data element. See the Class Overview for more information.
-     *
-     * @param context A {@link Context} for the current component.
-     * @param authority The authority of a {@link FileProvider} defined in a
-     *            {@code <provider>} element in your app's manifest.
-     * @param file A {@link File} pointing to the filename for which you want a
-     * <code>content</code> {@link Uri}.
-     * @return A content URI for the file.
-     * @throws IllegalArgumentException When the given {@link File} is outside
-     * the paths supported by the provider.
-     */
-    public static Uri getUriForFile(@NonNull Context context, @NonNull String authority,
-            @NonNull File file) {
-        final PathStrategy strategy = getPathStrategy(context, authority);
-        return strategy.getUriForFile(file);
-    }
-
-    /**
-     * Use a content URI returned by
-     * {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file
-     * managed by the FileProvider.
-     * FileProvider reports the column names defined in {@link android.provider.OpenableColumns}:
-     * <ul>
-     * <li>{@link android.provider.OpenableColumns#DISPLAY_NAME}</li>
-     * <li>{@link android.provider.OpenableColumns#SIZE}</li>
-     * </ul>
-     * For more information, see
-     * {@link ContentProvider#query(Uri, String[], String, String[], String)
-     * ContentProvider.query()}.
-     *
-     * @param uri A content URI returned by {@link #getUriForFile}.
-     * @param projection The list of columns to put into the {@link Cursor}. If null all columns are
-     * included.
-     * @param selection Selection criteria to apply. If null then all data that matches the content
-     * URI is returned.
-     * @param selectionArgs An array of {@link java.lang.String}, containing arguments to bind to
-     * the <i>selection</i> parameter. The <i>query</i> method scans <i>selection</i> from left to
-     * right and iterates through <i>selectionArgs</i>, replacing the current "?" character in
-     * <i>selection</i> with the value at the current position in <i>selectionArgs</i>. The
-     * values are bound to <i>selection</i> as {@link java.lang.String} values.
-     * @param sortOrder A {@link java.lang.String} containing the column name(s) on which to sort
-     * the resulting {@link Cursor}.
-     * @return A {@link Cursor} containing the results of the query.
-     *
-     */
-    @Override
-    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
-            @Nullable String[] selectionArgs,
-            @Nullable String sortOrder) {
-        // ContentProvider has already checked granted permissions
-        final File file = mStrategy.getFileForUri(uri);
-
-        if (projection == null) {
-            projection = COLUMNS;
-        }
-
-        String[] cols = new String[projection.length];
-        Object[] values = new Object[projection.length];
-        int i = 0;
-        for (String col : projection) {
-            if (OpenableColumns.DISPLAY_NAME.equals(col)) {
-                cols[i] = OpenableColumns.DISPLAY_NAME;
-                values[i++] = file.getName();
-            } else if (OpenableColumns.SIZE.equals(col)) {
-                cols[i] = OpenableColumns.SIZE;
-                values[i++] = file.length();
-            }
-        }
-
-        cols = copyOf(cols, i);
-        values = copyOf(values, i);
-
-        final MatrixCursor cursor = new MatrixCursor(cols, 1);
-        cursor.addRow(values);
-        return cursor;
-    }
-
-    /**
-     * Returns the MIME type of a content URI returned by
-     * {@link #getUriForFile(Context, String, File) getUriForFile()}.
-     *
-     * @param uri A content URI returned by
-     * {@link #getUriForFile(Context, String, File) getUriForFile()}.
-     * @return If the associated file has an extension, the MIME type associated with that
-     * extension; otherwise <code>application/octet-stream</code>.
-     */
-    @Override
-    public String getType(@NonNull Uri uri) {
-        // ContentProvider has already checked granted permissions
-        final File file = mStrategy.getFileForUri(uri);
-
-        final int lastDot = file.getName().lastIndexOf('.');
-        if (lastDot >= 0) {
-            final String extension = file.getName().substring(lastDot + 1);
-            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
-            if (mime != null) {
-                return mime;
-            }
-        }
-
-        return "application/octet-stream";
-    }
-
-    /**
-     * By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must
-     * subclass FileProvider if you want to provide different functionality.
-     */
-    @Override
-    public Uri insert(@NonNull Uri uri, ContentValues values) {
-        throw new UnsupportedOperationException("No external inserts");
-    }
-
-    /**
-     * By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must
-     * subclass FileProvider if you want to provide different functionality.
-     */
-    @Override
-    public int update(@NonNull Uri uri, ContentValues values, @Nullable String selection,
-            @Nullable String[] selectionArgs) {
-        throw new UnsupportedOperationException("No external updates");
-    }
-
-    /**
-     * Deletes the file associated with the specified content URI, as
-     * returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this
-     * method does <b>not</b> throw an {@link java.io.IOException}; you must check its return value.
-     *
-     * @param uri A content URI for a file, as returned by
-     * {@link #getUriForFile(Context, String, File) getUriForFile()}.
-     * @param selection Ignored. Set to {@code null}.
-     * @param selectionArgs Ignored. Set to {@code null}.
-     * @return 1 if the delete succeeds; otherwise, 0.
-     */
-    @Override
-    public int delete(@NonNull Uri uri, @Nullable String selection,
-            @Nullable String[] selectionArgs) {
-        // ContentProvider has already checked granted permissions
-        final File file = mStrategy.getFileForUri(uri);
-        return file.delete() ? 1 : 0;
-    }
-
-    /**
-     * By default, FileProvider automatically returns the
-     * {@link ParcelFileDescriptor} for a file associated with a <code>content://</code>
-     * {@link Uri}. To get the {@link ParcelFileDescriptor}, call
-     * {@link android.content.ContentResolver#openFileDescriptor(Uri, String)
-     * ContentResolver.openFileDescriptor}.
-     *
-     * To override this method, you must provide your own subclass of FileProvider.
-     *
-     * @param uri A content URI associated with a file, as returned by
-     * {@link #getUriForFile(Context, String, File) getUriForFile()}.
-     * @param mode Access mode for the file. May be "r" for read-only access, "rw" for read and
-     * write access, or "rwt" for read and write access that truncates any existing file.
-     * @return A new {@link ParcelFileDescriptor} with which you can access the file.
-     */
-    @Override
-    public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
-            throws FileNotFoundException {
-        // ContentProvider has already checked granted permissions
-        final File file = mStrategy.getFileForUri(uri);
-        final int fileMode = modeToMode(mode);
-        return ParcelFileDescriptor.open(file, fileMode);
-    }
-
-    /**
-     * Return {@link PathStrategy} for given authority, either by parsing or
-     * returning from cache.
-     */
-    private static PathStrategy getPathStrategy(Context context, String authority) {
-        PathStrategy strat;
-        synchronized (sCache) {
-            strat = sCache.get(authority);
-            if (strat == null) {
-                try {
-                    strat = parsePathStrategy(context, authority);
-                } catch (IOException e) {
-                    throw new IllegalArgumentException(
-                            "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
-                } catch (XmlPullParserException e) {
-                    throw new IllegalArgumentException(
-                            "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
-                }
-                sCache.put(authority, strat);
-            }
-        }
-        return strat;
-    }
-
-    /**
-     * Parse and return {@link PathStrategy} for given authority as defined in
-     * {@link #META_DATA_FILE_PROVIDER_PATHS} {@code <meta-data>}.
-     *
-     * @see #getPathStrategy(Context, String)
-     */
-    private static PathStrategy parsePathStrategy(Context context, String authority)
-            throws IOException, XmlPullParserException {
-        final SimplePathStrategy strat = new SimplePathStrategy(authority);
-
-        final ProviderInfo info = context.getPackageManager()
-                .resolveContentProvider(authority, PackageManager.GET_META_DATA);
-        final XmlResourceParser in = info.loadXmlMetaData(
-                context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
-        if (in == null) {
-            throw new IllegalArgumentException(
-                    "Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
-        }
-
-        int type;
-        while ((type = in.next()) != END_DOCUMENT) {
-            if (type == START_TAG) {
-                final String tag = in.getName();
-
-                final String name = in.getAttributeValue(null, ATTR_NAME);
-                String path = in.getAttributeValue(null, ATTR_PATH);
-
-                File target = null;
-                if (TAG_ROOT_PATH.equals(tag)) {
-                    target = DEVICE_ROOT;
-                } else if (TAG_FILES_PATH.equals(tag)) {
-                    target = context.getFilesDir();
-                } else if (TAG_CACHE_PATH.equals(tag)) {
-                    target = context.getCacheDir();
-                } else if (TAG_EXTERNAL.equals(tag)) {
-                    target = Environment.getExternalStorageDirectory();
-                } else if (TAG_EXTERNAL_FILES.equals(tag)) {
-                    File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
-                    if (externalFilesDirs.length > 0) {
-                        target = externalFilesDirs[0];
-                    }
-                } else if (TAG_EXTERNAL_CACHE.equals(tag)) {
-                    File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);
-                    if (externalCacheDirs.length > 0) {
-                        target = externalCacheDirs[0];
-                    }
-                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
-                        && TAG_EXTERNAL_MEDIA.equals(tag)) {
-                    File[] externalMediaDirs = context.getExternalMediaDirs();
-                    if (externalMediaDirs.length > 0) {
-                        target = externalMediaDirs[0];
-                    }
-                }
-
-                if (target != null) {
-                    strat.addRoot(name, buildPath(target, path));
-                }
-            }
-        }
-
-        return strat;
-    }
-
-    /**
-     * Strategy for mapping between {@link File} and {@link Uri}.
-     * <p>
-     * Strategies must be symmetric so that mapping a {@link File} to a
-     * {@link Uri} and then back to a {@link File} points at the original
-     * target.
-     * <p>
-     * Strategies must remain consistent across app launches, and not rely on
-     * dynamic state. This ensures that any generated {@link Uri} can still be
-     * resolved if your process is killed and later restarted.
-     *
-     * @see SimplePathStrategy
-     */
-    interface PathStrategy {
-        /**
-         * Return a {@link Uri} that represents the given {@link File}.
-         */
-        public Uri getUriForFile(File file);
-
-        /**
-         * Return a {@link File} that represents the given {@link Uri}.
-         */
-        public File getFileForUri(Uri uri);
-    }
-
-    /**
-     * Strategy that provides access to files living under a narrow whitelist of
-     * filesystem roots. It will throw {@link SecurityException} if callers try
-     * accessing files outside the configured roots.
-     * <p>
-     * For example, if configured with
-     * {@code addRoot("myfiles", context.getFilesDir())}, then
-     * {@code context.getFileStreamPath("foo.txt")} would map to
-     * {@code content://myauthority/myfiles/foo.txt}.
-     */
-    static class SimplePathStrategy implements PathStrategy {
-        private final String mAuthority;
-        private final HashMap<String, File> mRoots = new HashMap<String, File>();
-
-        public SimplePathStrategy(String authority) {
-            mAuthority = authority;
-        }
-
-        /**
-         * Add a mapping from a name to a filesystem root. The provider only offers
-         * access to files that live under configured roots.
-         */
-        public void addRoot(String name, File root) {
-            if (TextUtils.isEmpty(name)) {
-                throw new IllegalArgumentException("Name must not be empty");
-            }
-
-            try {
-                // Resolve to canonical path to keep path checking fast
-                root = root.getCanonicalFile();
-            } catch (IOException e) {
-                throw new IllegalArgumentException(
-                        "Failed to resolve canonical path for " + root, e);
-            }
-
-            mRoots.put(name, root);
-        }
-
-        @Override
-        public Uri getUriForFile(File file) {
-            String path;
-            try {
-                path = file.getCanonicalPath();
-            } catch (IOException e) {
-                throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
-            }
-
-            // Find the most-specific root path
-            Map.Entry<String, File> mostSpecific = null;
-            for (Map.Entry<String, File> root : mRoots.entrySet()) {
-                final String rootPath = root.getValue().getPath();
-                if (path.startsWith(rootPath) && (mostSpecific == null
-                        || rootPath.length() > mostSpecific.getValue().getPath().length())) {
-                    mostSpecific = root;
-                }
-            }
-
-            if (mostSpecific == null) {
-                throw new IllegalArgumentException(
-                        "Failed to find configured root that contains " + path);
-            }
-
-            // Start at first char of path under root
-            final String rootPath = mostSpecific.getValue().getPath();
-            if (rootPath.endsWith("/")) {
-                path = path.substring(rootPath.length());
-            } else {
-                path = path.substring(rootPath.length() + 1);
-            }
-
-            // Encode the tag and path separately
-            path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/");
-            return new Uri.Builder().scheme("content")
-                    .authority(mAuthority).encodedPath(path).build();
-        }
-
-        @Override
-        public File getFileForUri(Uri uri) {
-            String path = uri.getEncodedPath();
-
-            final int splitIndex = path.indexOf('/', 1);
-            final String tag = Uri.decode(path.substring(1, splitIndex));
-            path = Uri.decode(path.substring(splitIndex + 1));
-
-            final File root = mRoots.get(tag);
-            if (root == null) {
-                throw new IllegalArgumentException("Unable to find configured root for " + uri);
-            }
-
-            File file = new File(root, path);
-            try {
-                file = file.getCanonicalFile();
-            } catch (IOException e) {
-                throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
-            }
-
-            if (!file.getPath().startsWith(root.getPath())) {
-                throw new SecurityException("Resolved path jumped beyond configured root");
-            }
-
-            return file;
-        }
-    }
-
-    /**
-     * Copied from ContentResolver.java
-     */
-    private static int modeToMode(String mode) {
-        int modeBits;
-        if ("r".equals(mode)) {
-            modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
-        } else if ("w".equals(mode) || "wt".equals(mode)) {
-            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
-                    | ParcelFileDescriptor.MODE_CREATE
-                    | ParcelFileDescriptor.MODE_TRUNCATE;
-        } else if ("wa".equals(mode)) {
-            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
-                    | ParcelFileDescriptor.MODE_CREATE
-                    | ParcelFileDescriptor.MODE_APPEND;
-        } else if ("rw".equals(mode)) {
-            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
-                    | ParcelFileDescriptor.MODE_CREATE;
-        } else if ("rwt".equals(mode)) {
-            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
-                    | ParcelFileDescriptor.MODE_CREATE
-                    | ParcelFileDescriptor.MODE_TRUNCATE;
-        } else {
-            throw new IllegalArgumentException("Invalid mode: " + mode);
-        }
-        return modeBits;
-    }
-
-    private static File buildPath(File base, String... segments) {
-        File cur = base;
-        for (String segment : segments) {
-            if (segment != null) {
-                cur = new File(cur, segment);
-            }
-        }
-        return cur;
-    }
-
-    private static String[] copyOf(String[] original, int newLength) {
-        final String[] result = new String[newLength];
-        System.arraycopy(original, 0, result, 0, newLength);
-        return result;
-    }
-
-    private static Object[] copyOf(Object[] original, int newLength) {
-        final Object[] result = new Object[newLength];
-        System.arraycopy(original, 0, result, 0, newLength);
-        return result;
-    }
-}
diff --git a/android/support/v4/content/IntentCompat.java b/android/support/v4/content/IntentCompat.java
deleted file mode 100644
index f5b3a0b..0000000
--- a/android/support/v4/content/IntentCompat.java
+++ /dev/null
@@ -1,87 +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.content;
-
-import android.content.Intent;
-import android.os.Build;
-import android.support.annotation.NonNull;
-
-/**
- * Helper for accessing features in {@link android.content.Intent}.
- */
-public final class IntentCompat {
-    private IntentCompat() {
-        /* Hide constructor */
-    }
-
-    /**
-     * A constant String that is associated with the Intent, used with
-     * {@link android.content.Intent#ACTION_SEND} to supply an alternative to
-     * {@link android.content.Intent#EXTRA_TEXT}
-     * as HTML formatted text.  Note that you <em>must</em> also supply
-     * {@link android.content.Intent#EXTRA_TEXT}.
-     */
-    public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
-
-    /**
-     * Used as a boolean extra field in {@link android.content.Intent#ACTION_VIEW} intents to
-     * indicate that content should immediately be played without any intermediate screens that
-     * require additional user input, e.g. a profile selection screen or a details page.
-     */
-    public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
-
-    /**
-     * Indicates an activity optimized for Leanback mode, and that should
-     * be displayed in the Leanback launcher.
-     */
-    public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
-
-    /**
-     * Make an Intent for the main activity of an application, without
-     * specifying a specific activity to run but giving a selector to find
-     * the activity.  This results in a final Intent that is structured
-     * the same as when the application is launched from
-     * Home.  For anything else that wants to launch an application in the
-     * same way, it is important that they use an Intent structured the same
-     * way, and can use this function to ensure this is the case.
-     *
-     * <p>The returned Intent has {@link Intent#ACTION_MAIN} as its action, and includes the
-     * category {@link Intent#CATEGORY_LAUNCHER}.  This does <em>not</em> have
-     * {@link Intent#FLAG_ACTIVITY_NEW_TASK} set, though typically you will want
-     * to do that through {@link Intent#addFlags(int)} on the returned Intent.
-     *
-     * @param selectorAction The action name of the Intent's selector.
-     * @param selectorCategory The name of a category to add to the Intent's
-     * selector.
-     * @return Returns a newly created Intent that can be used to launch the
-     * activity as a main application entry.
-     */
-    @NonNull
-    public static Intent makeMainSelectorActivity(@NonNull String selectorAction,
-            @NonNull String selectorCategory) {
-        if (Build.VERSION.SDK_INT >= 15) {
-            return Intent.makeMainSelectorActivity(selectorAction, selectorCategory);
-        } else {
-            // Before api 15 you couldn't set a selector intent.
-            // Fall back and just return an intent with the requested action/category,
-            // even though it won't be a proper "main" intent.
-            Intent intent = new Intent(selectorAction);
-            intent.addCategory(selectorCategory);
-            return intent;
-        }
-    }
-}
diff --git a/android/support/v4/content/Loader.java b/android/support/v4/content/Loader.java
deleted file mode 100644
index 431964d..0000000
--- a/android/support/v4/content/Loader.java
+++ /dev/null
@@ -1,562 +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.content;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.util.DebugUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Static library support version of the framework's {@link android.content.Loader}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public class Loader<D> {
-    int mId;
-    OnLoadCompleteListener<D> mListener;
-    OnLoadCanceledListener<D> mOnLoadCanceledListener;
-    Context mContext;
-    boolean mStarted = false;
-    boolean mAbandoned = false;
-    boolean mReset = true;
-    boolean mContentChanged = false;
-    boolean mProcessingChange = false;
-
-    /**
-     * An implementation of a ContentObserver that takes care of connecting
-     * it to the Loader to have the loader re-load its data when the observer
-     * is told it has changed.  You do not normally need to use this yourself;
-     * it is used for you by {@link CursorLoader} to take care of executing
-     * an update when the cursor's backing data changes.
-     */
-    public final class ForceLoadContentObserver extends ContentObserver {
-        public ForceLoadContentObserver() {
-            super(new Handler());
-        }
-
-        @Override
-        public boolean deliverSelfNotifications() {
-            return true;
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onContentChanged();
-        }
-    }
-
-    /**
-     * Interface that is implemented to discover when a Loader has finished
-     * loading its data.  You do not normally need to implement this yourself;
-     * it is used in the implementation of {@link android.support.v4.app.LoaderManager}
-     * to find out when a Loader it is managing has completed so that this can
-     * be reported to its client.  This interface should only be used if a
-     * Loader is not being used in conjunction with LoaderManager.
-     */
-    public interface OnLoadCompleteListener<D> {
-        /**
-         * Called on the thread that created the Loader when the load is complete.
-         *
-         * @param loader the loader that completed the load
-         * @param data the result of the load
-         */
-        void onLoadComplete(@NonNull Loader<D> loader, @Nullable D data);
-    }
-
-    /**
-     * Interface that is implemented to discover when a Loader has been canceled
-     * before it finished loading its data.  You do not normally need to implement
-     * this yourself; it is used in the implementation of {@link android.support.v4.app.LoaderManager}
-     * to find out when a Loader it is managing has been canceled so that it
-     * can schedule the next Loader.  This interface should only be used if a
-     * Loader is not being used in conjunction with LoaderManager.
-     */
-    public interface OnLoadCanceledListener<D> {
-        /**
-         * Called on the thread that created the Loader when the load is canceled.
-         *
-         * @param loader the loader that canceled the load
-         */
-        void onLoadCanceled(@NonNull Loader<D> loader);
-    }
-
-    /**
-     * Stores away the application context associated with context.
-     * Since Loaders can be used across multiple activities it's dangerous to
-     * store the context directly; always use {@link #getContext()} to retrieve
-     * the Loader's Context, don't use the constructor argument directly.
-     * The Context returned by {@link #getContext} is safe to use across
-     * Activity instances.
-     *
-     * @param context used to retrieve the application context.
-     */
-    public Loader(@NonNull Context context) {
-        mContext = context.getApplicationContext();
-    }
-
-    /**
-     * Sends the result of the load to the registered listener. Should only be called by subclasses.
-     *
-     * Must be called from the process's main thread.
-     *
-     * @param data the result of the load
-     */
-    @MainThread
-    public void deliverResult(@Nullable D data) {
-        if (mListener != null) {
-            mListener.onLoadComplete(this, data);
-        }
-    }
-
-    /**
-     * Informs the registered {@link OnLoadCanceledListener} that the load has been canceled.
-     * Should only be called by subclasses.
-     *
-     * Must be called from the process's main thread.
-     */
-    @MainThread
-    public void deliverCancellation() {
-        if (mOnLoadCanceledListener != null) {
-            mOnLoadCanceledListener.onLoadCanceled(this);
-        }
-    }
-
-    /**
-     * @return an application context retrieved from the Context passed to the constructor.
-     */
-    @NonNull
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * @return the ID of this loader
-     */
-    public int getId() {
-        return mId;
-    }
-
-    /**
-     * Registers a class that will receive callbacks when a load is complete.
-     * The callback will be called on the process's main thread so it's safe to
-     * pass the results to widgets.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public void registerListener(int id, @NonNull OnLoadCompleteListener<D> listener) {
-        if (mListener != null) {
-            throw new IllegalStateException("There is already a listener registered");
-        }
-        mListener = listener;
-        mId = id;
-    }
-
-    /**
-     * Remove a listener that was previously added with {@link #registerListener}.
-     *
-     * Must be called from the process's main thread.
-     */
-    @MainThread
-    public void unregisterListener(@NonNull OnLoadCompleteListener<D> listener) {
-        if (mListener == null) {
-            throw new IllegalStateException("No listener register");
-        }
-        if (mListener != listener) {
-            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
-        }
-        mListener = null;
-    }
-
-    /**
-     * Registers a listener that will receive callbacks when a load is canceled.
-     * The callback will be called on the process's main thread so it's safe to
-     * pass the results to widgets.
-     *
-     * Must be called from the process's main thread.
-     *
-     * @param listener The listener to register.
-     */
-    @MainThread
-    public void registerOnLoadCanceledListener(@NonNull OnLoadCanceledListener<D> listener) {
-        if (mOnLoadCanceledListener != null) {
-            throw new IllegalStateException("There is already a listener registered");
-        }
-        mOnLoadCanceledListener = listener;
-    }
-
-    /**
-     * Unregisters a listener that was previously added with
-     * {@link #registerOnLoadCanceledListener}.
-     *
-     * Must be called from the process's main thread.
-     *
-     * @param listener The listener to unregister.
-     */
-    @MainThread
-    public void unregisterOnLoadCanceledListener(@NonNull OnLoadCanceledListener<D> listener) {
-        if (mOnLoadCanceledListener == null) {
-            throw new IllegalStateException("No listener register");
-        }
-        if (mOnLoadCanceledListener != listener) {
-            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
-        }
-        mOnLoadCanceledListener = null;
-    }
-
-    /**
-     * Return whether this load has been started.  That is, its {@link #startLoading()}
-     * has been called and no calls to {@link #stopLoading()} or
-     * {@link #reset()} have yet been made.
-     */
-    public boolean isStarted() {
-        return mStarted;
-    }
-
-    /**
-     * Return whether this loader has been abandoned.  In this state, the
-     * loader <em>must not</em> report any new data, and <em>must</em> keep
-     * its last reported data valid until it is finally reset.
-     */
-    public boolean isAbandoned() {
-        return mAbandoned;
-    }
-
-    /**
-     * Return whether this load has been reset.  That is, either the loader
-     * has not yet been started for the first time, or its {@link #reset()}
-     * has been called.
-     */
-    public boolean isReset() {
-        return mReset;
-    }
-
-    /**
-     * This function will normally be called for you automatically by
-     * {@link android.support.v4.app.LoaderManager} when the associated fragment/activity
-     * is being started.  When using a Loader with {@link android.support.v4.app.LoaderManager},
-     * you <em>must not</em> call this method yourself, or you will conflict
-     * with its management of the Loader.
-     *
-     * Starts an asynchronous load of the Loader's data. When the result
-     * is ready the callbacks will be called on the process's main thread.
-     * If a previous load has been completed and is still valid
-     * the result may be passed to the callbacks immediately.
-     * The loader will monitor the source of
-     * the data set and may deliver future callbacks if the source changes.
-     * Calling {@link #stopLoading} will stop the delivery of callbacks.
-     *
-     * <p>This updates the Loader's internal state so that
-     * {@link #isStarted()} and {@link #isReset()} will return the correct
-     * values, and then calls the implementation's {@link #onStartLoading()}.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public final void startLoading() {
-        mStarted = true;
-        mReset = false;
-        mAbandoned = false;
-        onStartLoading();
-    }
-
-    /**
-     * Subclasses must implement this to take care of loading their data,
-     * as per {@link #startLoading()}.  This is not called by clients directly,
-     * but as a result of a call to {@link #startLoading()}.
-     * This will always be called from the process's main thread.
-     */
-    @MainThread
-    protected void onStartLoading() {
-    }
-
-    /**
-     * Attempt to cancel the current load task.
-     * Must be called on the main thread of the process.
-     *
-     * <p>Cancellation is not an immediate operation, since the load is performed
-     * in a background thread.  If there is currently a load in progress, this
-     * method requests that the load be canceled, and notes this is the case;
-     * once the background thread has completed its work its remaining state
-     * will be cleared.  If another load request comes in during this time,
-     * it will be held until the canceled load is complete.
-     *
-     * @return Returns <tt>false</tt> if the task could not be canceled,
-     * typically because it has already completed normally, or
-     * because {@link #startLoading()} hasn't been called; returns
-     * <tt>true</tt> otherwise.  When <tt>true</tt> is returned, the task
-     * is still running and the {@link OnLoadCanceledListener} will be called
-     * when the task completes.
-     */
-    @MainThread
-    public boolean cancelLoad() {
-        return onCancelLoad();
-    }
-
-    /**
-     * Subclasses must implement this to take care of requests to {@link #cancelLoad()}.
-     * This will always be called from the process's main thread.
-     *
-     * @return Returns <tt>false</tt> if the task could not be canceled,
-     * typically because it has already completed normally, or
-     * because {@link #startLoading()} hasn't been called; returns
-     * <tt>true</tt> otherwise.  When <tt>true</tt> is returned, the task
-     * is still running and the {@link OnLoadCanceledListener} will be called
-     * when the task completes.
-     */
-    @MainThread
-    protected boolean onCancelLoad() {
-        return false;
-    }
-
-    /**
-     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
-     * loaded data set and load a new one.  This simply calls through to the
-     * implementation's {@link #onForceLoad()}.  You generally should only call this
-     * when the loader is started -- that is, {@link #isStarted()} returns true.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public void forceLoad() {
-        onForceLoad();
-    }
-
-    /**
-     * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
-     * This will always be called from the process's main thread.
-     */
-    @MainThread
-    protected void onForceLoad() {
-    }
-
-    /**
-     * This function will normally be called for you automatically by
-     * {@link android.support.v4.app.LoaderManager} when the associated fragment/activity
-     * is being stopped.  When using a Loader with {@link android.support.v4.app.LoaderManager},
-     * you <em>must not</em> call this method yourself, or you will conflict
-     * with its management of the Loader.
-     *
-     * <p>Stops delivery of updates until the next time {@link #startLoading()} is called.
-     * Implementations should <em>not</em> invalidate their data at this point --
-     * clients are still free to use the last data the loader reported.  They will,
-     * however, typically stop reporting new data if the data changes; they can
-     * still monitor for changes, but must not report them to the client until and
-     * if {@link #startLoading()} is later called.
-     *
-     * <p>This updates the Loader's internal state so that
-     * {@link #isStarted()} will return the correct
-     * value, and then calls the implementation's {@link #onStopLoading()}.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public void stopLoading() {
-        mStarted = false;
-        onStopLoading();
-    }
-
-    /**
-     * Subclasses must implement this to take care of stopping their loader,
-     * as per {@link #stopLoading()}.  This is not called by clients directly,
-     * but as a result of a call to {@link #stopLoading()}.
-     * This will always be called from the process's main thread.
-     */
-    @MainThread
-    protected void onStopLoading() {
-    }
-
-    /**
-     * This function will normally be called for you automatically by
-     * {@link android.support.v4.app.LoaderManager} when restarting a Loader.  When using
-     * a Loader with {@link android.support.v4.app.LoaderManager},
-     * you <em>must not</em> call this method yourself, or you will conflict
-     * with its management of the Loader.
-     *
-     * Tell the Loader that it is being abandoned.  This is called prior
-     * to {@link #reset} to have it retain its current data but not report
-     * any new data.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public void abandon() {
-        mAbandoned = true;
-        onAbandon();
-    }
-
-    /**
-     * Subclasses implement this to take care of being abandoned.  This is
-     * an optional intermediate state prior to {@link #onReset()} -- it means that
-     * the client is no longer interested in any new data from the loader,
-     * so the loader must not report any further updates.  However, the
-     * loader <em>must</em> keep its last reported data valid until the final
-     * {@link #onReset()} happens.  You can retrieve the current abandoned
-     * state with {@link #isAbandoned}.
-     * This will always be called from the process's main thread.
-     */
-    @MainThread
-    protected void onAbandon() {
-    }
-
-    /**
-     * This function will normally be called for you automatically by
-     * {@link android.support.v4.app.LoaderManager} when destroying a Loader.  When using
-     * a Loader with {@link android.support.v4.app.LoaderManager},
-     * you <em>must not</em> call this method yourself, or you will conflict
-     * with its management of the Loader.
-     *
-     * Resets the state of the Loader.  The Loader should at this point free
-     * all of its resources, since it may never be called again; however, its
-     * {@link #startLoading()} may later be called at which point it must be
-     * able to start running again.
-     *
-     * <p>This updates the Loader's internal state so that
-     * {@link #isStarted()} and {@link #isReset()} will return the correct
-     * values, and then calls the implementation's {@link #onReset()}.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public void reset() {
-        onReset();
-        mReset = true;
-        mStarted = false;
-        mAbandoned = false;
-        mContentChanged = false;
-        mProcessingChange = false;
-    }
-
-    /**
-     * Subclasses must implement this to take care of resetting their loader,
-     * as per {@link #reset()}.  This is not called by clients directly,
-     * but as a result of a call to {@link #reset()}.
-     * This will always be called from the process's main thread.
-     */
-    @MainThread
-    protected void onReset() {
-    }
-
-    /**
-     * Take the current flag indicating whether the loader's content had
-     * changed while it was stopped.  If it had, true is returned and the
-     * flag is cleared.
-     */
-    public boolean takeContentChanged() {
-        boolean res = mContentChanged;
-        mContentChanged = false;
-        mProcessingChange |= res;
-        return res;
-    }
-
-    /**
-     * Commit that you have actually fully processed a content change that
-     * was returned by {@link #takeContentChanged}.  This is for use with
-     * {@link #rollbackContentChanged()} to handle situations where a load
-     * is cancelled.  Call this when you have completely processed a load
-     * without it being cancelled.
-     */
-    public void commitContentChanged() {
-        mProcessingChange = false;
-    }
-
-    /**
-     * Report that you have abandoned the processing of a content change that
-     * was returned by {@link #takeContentChanged()} and would like to rollback
-     * to the state where there is again a pending content change.  This is
-     * to handle the case where a data load due to a content change has been
-     * canceled before its data was delivered back to the loader.
-     */
-    public void rollbackContentChanged() {
-        if (mProcessingChange) {
-            onContentChanged();
-        }
-    }
-
-    /**
-     * Called when {@link ForceLoadContentObserver} detects a change.  The
-     * default implementation checks to see if the loader is currently started;
-     * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag
-     * so that {@link #takeContentChanged()} returns true.
-     *
-     * <p>Must be called from the process's main thread.
-     */
-    @MainThread
-    public void onContentChanged() {
-        if (mStarted) {
-            forceLoad();
-        } else {
-            // This loader has been stopped, so we don't want to load
-            // new data right now...  but keep track of it changing to
-            // refresh later if we start again.
-            mContentChanged = true;
-        }
-    }
-
-    /**
-     * For debugging, converts an instance of the Loader's data class to
-     * a string that can be printed.  Must handle a null data.
-     */
-    @NonNull
-    public String dataToString(@Nullable D data) {
-        StringBuilder sb = new StringBuilder(64);
-        DebugUtils.buildShortClassTag(data, sb);
-        sb.append("}");
-        return sb.toString();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder(64);
-        DebugUtils.buildShortClassTag(this, sb);
-        sb.append(" id=");
-        sb.append(mId);
-        sb.append("}");
-        return sb.toString();
-    }
-
-    /**
-     * Print the Loader's state into the given stream.
-     *
-     * @param prefix Text to print at the front of each line.
-     * @param fd The raw file descriptor that the dump is being sent to.
-     * @param writer A PrintWriter to which the dump is to be set.
-     * @param args Additional arguments to the dump request.
-     */
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        writer.print(prefix); writer.print("mId="); writer.print(mId);
-                writer.print(" mListener="); writer.println(mListener);
-        if (mStarted || mContentChanged || mProcessingChange) {
-            writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
-                    writer.print(" mContentChanged="); writer.print(mContentChanged);
-                    writer.print(" mProcessingChange="); writer.println(mProcessingChange);
-        }
-        if (mAbandoned || mReset) {
-            writer.print(prefix); writer.print("mAbandoned="); writer.print(mAbandoned);
-                    writer.print(" mReset="); writer.println(mReset);
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v4/content/LocalBroadcastManager.java b/android/support/v4/content/LocalBroadcastManager.java
deleted file mode 100644
index aaaf8be..0000000
--- a/android/support/v4/content/LocalBroadcastManager.java
+++ /dev/null
@@ -1,317 +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.content;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
-/**
- * Helper to register for and send broadcasts of Intents to local objects
- * within your process.  This has a number of advantages over sending
- * global broadcasts with {@link android.content.Context#sendBroadcast}:
- * <ul>
- * <li> You know that the data you are broadcasting won't leave your app, so
- * don't need to worry about leaking private data.
- * <li> It is not possible for other applications to send these broadcasts to
- * your app, so you don't need to worry about having security holes they can
- * exploit.
- * <li> It is more efficient than sending a global broadcast through the
- * system.
- * </ul>
- */
-public final class LocalBroadcastManager {
-    private static final class ReceiverRecord {
-        final IntentFilter filter;
-        final BroadcastReceiver receiver;
-        boolean broadcasting;
-        boolean dead;
-
-        ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
-            filter = _filter;
-            receiver = _receiver;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder(128);
-            builder.append("Receiver{");
-            builder.append(receiver);
-            builder.append(" filter=");
-            builder.append(filter);
-            if (dead) {
-                builder.append(" DEAD");
-            }
-            builder.append("}");
-            return builder.toString();
-        }
-    }
-
-    private static final class BroadcastRecord {
-        final Intent intent;
-        final ArrayList<ReceiverRecord> receivers;
-
-        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
-            intent = _intent;
-            receivers = _receivers;
-        }
-    }
-
-    private static final String TAG = "LocalBroadcastManager";
-    private static final boolean DEBUG = false;
-
-    private final Context mAppContext;
-
-    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<>();
-
-    static final int MSG_EXEC_PENDING_BROADCASTS = 1;
-
-    private final Handler mHandler;
-
-    private static final Object mLock = new Object();
-    private static LocalBroadcastManager mInstance;
-
-    @NonNull
-    public static LocalBroadcastManager getInstance(@NonNull Context context) {
-        synchronized (mLock) {
-            if (mInstance == null) {
-                mInstance = new LocalBroadcastManager(context.getApplicationContext());
-            }
-            return mInstance;
-        }
-    }
-
-    private LocalBroadcastManager(Context context) {
-        mAppContext = context;
-        mHandler = new Handler(context.getMainLooper()) {
-
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case MSG_EXEC_PENDING_BROADCASTS:
-                        executePendingBroadcasts();
-                        break;
-                    default:
-                        super.handleMessage(msg);
-                }
-            }
-        };
-    }
-
-    /**
-     * Register a receive for any local broadcasts that match the given IntentFilter.
-     *
-     * @param receiver The BroadcastReceiver to handle the broadcast.
-     * @param filter Selects the Intent broadcasts to be received.
-     *
-     * @see #unregisterReceiver
-     */
-    public void registerReceiver(@NonNull BroadcastReceiver receiver,
-            @NonNull IntentFilter filter) {
-        synchronized (mReceivers) {
-            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
-            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
-            if (filters == null) {
-                filters = new ArrayList<>(1);
-                mReceivers.put(receiver, filters);
-            }
-            filters.add(entry);
-            for (int i=0; i<filter.countActions(); i++) {
-                String action = filter.getAction(i);
-                ArrayList<ReceiverRecord> entries = mActions.get(action);
-                if (entries == null) {
-                    entries = new ArrayList<ReceiverRecord>(1);
-                    mActions.put(action, entries);
-                }
-                entries.add(entry);
-            }
-        }
-    }
-
-    /**
-     * Unregister a previously registered BroadcastReceiver.  <em>All</em>
-     * filters that have been registered for this BroadcastReceiver will be
-     * removed.
-     *
-     * @param receiver The BroadcastReceiver to unregister.
-     *
-     * @see #registerReceiver
-     */
-    public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
-        synchronized (mReceivers) {
-            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
-            if (filters == null) {
-                return;
-            }
-            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=receivers.size()-1; k>=0; k--) {
-                            final ReceiverRecord rec = receivers.get(k);
-                            if (rec.receiver == receiver) {
-                                rec.dead = true;
-                                receivers.remove(k);
-                            }
-                        }
-                        if (receivers.size() <= 0) {
-                            mActions.remove(action);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Broadcast the given intent to all interested BroadcastReceivers.  This
-     * call is asynchronous; it returns immediately, and you will continue
-     * executing while the receivers are run.
-     *
-     * @param intent The Intent to broadcast; all receivers matching this
-     *     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(@NonNull Intent intent) {
-        synchronized (mReceivers) {
-            final String action = intent.getAction();
-            final String type = intent.resolveTypeIfNeeded(
-                    mAppContext.getContentResolver());
-            final Uri data = intent.getData();
-            final String scheme = intent.getScheme();
-            final Set<String> categories = intent.getCategories();
-
-            final boolean debug = DEBUG ||
-                    ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
-            if (debug) Log.v(
-                    TAG, "Resolving type " + type + " scheme " + scheme
-                    + " of intent " + intent);
-
-            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
-            if (entries != null) {
-                if (debug) Log.v(TAG, "Action list: " + entries);
-
-                ArrayList<ReceiverRecord> receivers = null;
-                for (int i=0; i<entries.size(); i++) {
-                    ReceiverRecord receiver = entries.get(i);
-                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
-
-                    if (receiver.broadcasting) {
-                        if (debug) {
-                            Log.v(TAG, "  Filter's target already added");
-                        }
-                        continue;
-                    }
-
-                    int match = receiver.filter.match(action, type, scheme, data,
-                            categories, "LocalBroadcastManager");
-                    if (match >= 0) {
-                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
-                                Integer.toHexString(match));
-                        if (receivers == null) {
-                            receivers = new ArrayList<ReceiverRecord>();
-                        }
-                        receivers.add(receiver);
-                        receiver.broadcasting = true;
-                    } else {
-                        if (debug) {
-                            String reason;
-                            switch (match) {
-                                case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
-                                case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
-                                case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
-                                case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
-                                default: reason = "unknown reason"; break;
-                            }
-                            Log.v(TAG, "  Filter did not match: " + reason);
-                        }
-                    }
-                }
-
-                if (receivers != null) {
-                    for (int i=0; i<receivers.size(); i++) {
-                        receivers.get(i).broadcasting = false;
-                    }
-                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
-                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
-                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
-     * the Intent this function will block and immediately dispatch them before
-     * returning.
-     */
-    public void sendBroadcastSync(@NonNull Intent intent) {
-        if (sendBroadcast(intent)) {
-            executePendingBroadcasts();
-        }
-    }
-
-    private void executePendingBroadcasts() {
-        while (true) {
-            final BroadcastRecord[] brs;
-            synchronized (mReceivers) {
-                final int N = mPendingBroadcasts.size();
-                if (N <= 0) {
-                    return;
-                }
-                brs = new BroadcastRecord[N];
-                mPendingBroadcasts.toArray(brs);
-                mPendingBroadcasts.clear();
-            }
-            for (int i=0; i<brs.length; i++) {
-                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/android/support/v4/content/MimeTypeFilter.java b/android/support/v4/content/MimeTypeFilter.java
deleted file mode 100644
index 8a90c62..0000000
--- a/android/support/v4/content/MimeTypeFilter.java
+++ /dev/null
@@ -1,152 +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;
-
-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.
-     */
-    @Nullable
-    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.
-     */
-    @Nullable
-    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.
-     */
-    @NonNull
-    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/android/support/v4/content/ModernAsyncTask.java b/android/support/v4/content/ModernAsyncTask.java
deleted file mode 100644
index 06404ce..0000000
--- a/android/support/v4/content/ModernAsyncTask.java
+++ /dev/null
@@ -1,530 +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.content;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.support.annotation.RestrictTo;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Copy of the required parts of {@link android.os.AsyncTask} from Android 3.0 that is
- * needed to support AsyncTaskLoader.  We use this rather than the one from the platform
- * because we rely on some subtle behavior of AsyncTask that is not reliable on
- * older platforms.
- *
- * <p>Note that for now this is not publicly available because it is not a
- * complete implementation, only sufficient for the needs of
- * {@link android.content.AsyncTaskLoader}.
- */
-abstract class ModernAsyncTask<Params, Progress, Result> {
-    private static final String LOG_TAG = "AsyncTask";
-
-    private static final int CORE_POOL_SIZE = 5;
-    private static final int MAXIMUM_POOL_SIZE = 128;
-    private static final int KEEP_ALIVE = 1;
-
-    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
-        private final AtomicInteger mCount = new AtomicInteger(1);
-
-        @Override
-        public Thread newThread(Runnable r) {
-            return new Thread(r, "ModernAsyncTask #" + mCount.getAndIncrement());
-        }
-    };
-
-    private static final BlockingQueue<Runnable> sPoolWorkQueue =
-            new LinkedBlockingQueue<Runnable>(10);
-
-    /**
-     * An {@link Executor} that can be used to execute tasks in parallel.
-     */
-    public static final Executor THREAD_POOL_EXECUTOR =
-            new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
-                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
-
-    private static final int MESSAGE_POST_RESULT = 0x1;
-    private static final int MESSAGE_POST_PROGRESS = 0x2;
-
-    private static InternalHandler sHandler;
-
-    private static volatile Executor sDefaultExecutor = THREAD_POOL_EXECUTOR;
-    private final WorkerRunnable<Params, Result> mWorker;
-    private final FutureTask<Result> mFuture;
-
-    private volatile Status mStatus = Status.PENDING;
-
-    private final AtomicBoolean mCancelled = new AtomicBoolean();
-    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
-
-    /**
-     * Indicates the current status of the task. Each status will be set only once
-     * during the lifetime of a task.
-     */
-    public enum Status {
-        /**
-         * Indicates that the task has not been executed yet.
-         */
-        PENDING,
-        /**
-         * Indicates that the task is running.
-         */
-        RUNNING,
-        /**
-         * Indicates that {@link android.os.AsyncTask#onPostExecute(Object)} has finished.
-         */
-        FINISHED,
-    }
-
-    private static Handler getHandler() {
-        synchronized (ModernAsyncTask.class) {
-            if (sHandler == null) {
-                sHandler = new InternalHandler();
-            }
-            return sHandler;
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public static void setDefaultExecutor(Executor exec) {
-        sDefaultExecutor = exec;
-    }
-
-    /**
-     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
-     */
-    ModernAsyncTask() {
-        mWorker = new WorkerRunnable<Params, Result>() {
-            @Override
-            public Result call() throws Exception {
-                mTaskInvoked.set(true);
-                Result result = null;
-                try {
-                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                    //noinspection unchecked
-                    result = doInBackground(mParams);
-                    Binder.flushPendingCommands();
-                } catch (Throwable tr) {
-                    mCancelled.set(true);
-                    throw tr;
-                } finally {
-                    postResult(result);
-                }
-                return result;
-            }
-        };
-
-        mFuture = new FutureTask<Result>(mWorker) {
-            @Override
-            protected void done() {
-                try {
-                    final Result result = get();
-
-                    postResultIfNotInvoked(result);
-                } catch (InterruptedException e) {
-                    android.util.Log.w(LOG_TAG, e);
-                } catch (ExecutionException e) {
-                    throw new RuntimeException(
-                            "An error occurred while executing doInBackground()", e.getCause());
-                } catch (CancellationException e) {
-                    postResultIfNotInvoked(null);
-                } catch (Throwable t) {
-                    throw new RuntimeException(
-                            "An error occurred while executing doInBackground()", t);
-                }
-            }
-        };
-    }
-
-    void postResultIfNotInvoked(Result result) {
-        final boolean wasTaskInvoked = mTaskInvoked.get();
-        if (!wasTaskInvoked) {
-            postResult(result);
-        }
-    }
-
-    Result postResult(Result result) {
-        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
-                new AsyncTaskResult<>(this, result));
-        message.sendToTarget();
-        return result;
-    }
-
-    /**
-     * Returns the current status of this task.
-     *
-     * @return The current status.
-     */
-    public final Status getStatus() {
-        return mStatus;
-    }
-
-    /**
-     * Override this method to perform a computation on a background thread. The
-     * specified parameters are the parameters passed to {@link #execute}
-     * by the caller of this task.
-     *
-     * This method can call {@link #publishProgress} to publish updates
-     * on the UI thread.
-     *
-     * @param params The parameters of the task.
-     *
-     * @return A result, defined by the subclass of this task.
-     *
-     * @see #onPreExecute()
-     * @see #onPostExecute
-     * @see #publishProgress
-     */
-    protected abstract Result doInBackground(Params... params);
-
-    /**
-     * Runs on the UI thread before {@link #doInBackground}.
-     *
-     * @see #onPostExecute
-     * @see #doInBackground
-     */
-    protected void onPreExecute() {
-    }
-
-    /**
-     * <p>Runs on the UI thread after {@link #doInBackground}. The
-     * specified result is the value returned by {@link #doInBackground}.</p>
-     *
-     * <p>This method won't be invoked if the task was cancelled.</p>
-     *
-     * @param result The result of the operation computed by {@link #doInBackground}.
-     *
-     * @see #onPreExecute
-     * @see #doInBackground
-     * @see #onCancelled(Object)
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    protected void onPostExecute(Result result) {
-    }
-
-    /**
-     * Runs on the UI thread after {@link #publishProgress} is invoked.
-     * The specified values are the values passed to {@link #publishProgress}.
-     *
-     * @param values The values indicating progress.
-     *
-     * @see #publishProgress
-     * @see #doInBackground
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    protected void onProgressUpdate(Progress... values) {
-    }
-
-    /**
-     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
-     * {@link #doInBackground(Object[])} has finished.</p>
-     *
-     * <p>The default implementation simply invokes {@link #onCancelled()} and
-     * ignores the result. If you write your own implementation, do not call
-     * <code>super.onCancelled(result)</code>.</p>
-     *
-     * @param result The result, if any, computed in
-     *               {@link #doInBackground(Object[])}, can be null
-     *
-     * @see #cancel(boolean)
-     * @see #isCancelled()
-     */
-    @SuppressWarnings({"UnusedParameters"})
-    protected void onCancelled(Result result) {
-        onCancelled();
-    }
-
-    /**
-     * <p>Applications should preferably override {@link #onCancelled(Object)}.
-     * This method is invoked by the default implementation of
-     * {@link #onCancelled(Object)}.</p>
-     *
-     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
-     * {@link #doInBackground(Object[])} has finished.</p>
-     *
-     * @see #onCancelled(Object)
-     * @see #cancel(boolean)
-     * @see #isCancelled()
-     */
-    protected void onCancelled() {
-    }
-
-    /**
-     * Returns <tt>true</tt> if this task was cancelled before it completed
-     * normally. If you are calling {@link #cancel(boolean)} on the task,
-     * the value returned by this method should be checked periodically from
-     * {@link #doInBackground(Object[])} to end the task as soon as possible.
-     *
-     * @return <tt>true</tt> if task was cancelled before it completed
-     *
-     * @see #cancel(boolean)
-     */
-    public final boolean isCancelled() {
-        return mCancelled.get();
-    }
-
-    /**
-     * <p>Attempts to cancel execution of this task.  This attempt will
-     * fail if the task has already completed, already been cancelled,
-     * or could not be cancelled for some other reason. If successful,
-     * and this task has not started when <tt>cancel</tt> is called,
-     * this task should never run. If the task has already started,
-     * then the <tt>mayInterruptIfRunning</tt> parameter determines
-     * whether the thread executing this task should be interrupted in
-     * an attempt to stop the task.</p>
-     *
-     * <p>Calling this method will result in {@link #onCancelled(Object)} being
-     * invoked on the UI thread after {@link #doInBackground(Object[])}
-     * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
-     * is never invoked. After invoking this method, you should check the
-     * value returned by {@link #isCancelled()} periodically from
-     * {@link #doInBackground(Object[])} to finish the task as early as
-     * possible.</p>
-     *
-     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
-     *        task should be interrupted; otherwise, in-progress tasks are allowed
-     *        to complete.
-     *
-     * @return <tt>false</tt> if the task could not be cancelled,
-     *         typically because it has already completed normally;
-     *         <tt>true</tt> otherwise
-     *
-     * @see #isCancelled()
-     * @see #onCancelled(Object)
-     */
-    public final boolean cancel(boolean mayInterruptIfRunning) {
-        mCancelled.set(true);
-        return mFuture.cancel(mayInterruptIfRunning);
-    }
-
-    /**
-     * Waits if necessary for the computation to complete, and then
-     * retrieves its result.
-     *
-     * @return The computed result.
-     *
-     * @throws CancellationException If the computation was cancelled.
-     * @throws ExecutionException If the computation threw an exception.
-     * @throws InterruptedException If the current thread was interrupted
-     *         while waiting.
-     */
-    public final Result get() throws InterruptedException, ExecutionException {
-        return mFuture.get();
-    }
-
-    /**
-     * Waits if necessary for at most the given time for the computation
-     * to complete, and then retrieves its result.
-     *
-     * @param timeout Time to wait before cancelling the operation.
-     * @param unit The time unit for the timeout.
-     *
-     * @return The computed result.
-     *
-     * @throws CancellationException If the computation was cancelled.
-     * @throws ExecutionException If the computation threw an exception.
-     * @throws InterruptedException If the current thread was interrupted
-     *         while waiting.
-     * @throws TimeoutException If the wait timed out.
-     */
-    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
-            ExecutionException, TimeoutException {
-        return mFuture.get(timeout, unit);
-    }
-
-    /**
-     * Executes the task with the specified parameters. The task returns
-     * itself (this) so that the caller can keep a reference to it.
-     *
-     * <p>Note: this function schedules the task on a queue for a single background
-     * thread or pool of threads depending on the platform version.  When first
-     * introduced, AsyncTasks were executed serially on a single background thread.
-     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
-     * to a pool of threads allowing multiple tasks to operate in parallel.  After
-     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, it is planned to change this
-     * back to a single thread to avoid common application errors caused
-     * by parallel execution.  If you truly want parallel execution, you can use
-     * the {@link #executeOnExecutor} version of this method
-     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings on
-     * its use.
-     *
-     * <p>This method must be invoked on the UI thread.
-     *
-     * @param params The parameters of the task.
-     *
-     * @return This instance of AsyncTask.
-     *
-     * @throws IllegalStateException If {@link #getStatus()} returns either
-     *         {@link android.os.AsyncTask.Status#RUNNING} or
-     *          {@link android.os.AsyncTask.Status#FINISHED}.
-     */
-    public final ModernAsyncTask<Params, Progress, Result> execute(Params... params) {
-        return executeOnExecutor(sDefaultExecutor, params);
-    }
-
-    /**
-     * Executes the task with the specified parameters. The task returns
-     * itself (this) so that the caller can keep a reference to it.
-     *
-     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
-     * allow multiple tasks to run in parallel on a pool of threads managed by
-     * AsyncTask, however you can also use your own {@link Executor} for custom
-     * behavior.
-     *
-     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
-     * a thread pool is generally <em>not</em> what one wants, because the order
-     * of their operation is not defined.  For example, if these tasks are used
-     * to modify any state in common (such as writing a file due to a button click),
-     * there are no guarantees on the order of the modifications.
-     * Without careful work it is possible in rare cases for the newer version
-     * of the data to be over-written by an older one, leading to obscure data
-     * loss and stability issues.
-     *
-     * <p>This method must be invoked on the UI thread.
-     *
-     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
-     *              convenient process-wide thread pool for tasks that are loosely coupled.
-     * @param params The parameters of the task.
-     *
-     * @return This instance of AsyncTask.
-     *
-     * @throws IllegalStateException If {@link #getStatus()} returns either
-     *         {@link android.os.AsyncTask.Status#RUNNING}
-     *          or {@link android.os.AsyncTask.Status#FINISHED}.
-     */
-    public final ModernAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
-            Params... params) {
-        if (mStatus != Status.PENDING) {
-            switch (mStatus) {
-                case RUNNING:
-                    throw new IllegalStateException("Cannot execute task:"
-                            + " the task is already running.");
-                case FINISHED:
-                    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");
-            }
-        }
-
-        mStatus = Status.RUNNING;
-
-        onPreExecute();
-
-        mWorker.mParams = params;
-        exec.execute(mFuture);
-
-        return this;
-    }
-
-    /**
-     * Convenience version of {@link #execute(Object...)} for use with
-     * a simple Runnable object.
-     */
-    public static void execute(Runnable runnable) {
-        sDefaultExecutor.execute(runnable);
-    }
-
-    /**
-     * This method can be invoked from {@link #doInBackground} to
-     * publish updates on the UI thread while the background computation is
-     * still running. Each call to this method will trigger the execution of
-     * {@link #onProgressUpdate} on the UI thread.
-     *
-     * {@link #onProgressUpdate} will note be called if the task has been
-     * canceled.
-     *
-     * @param values The progress values to update the UI with.
-     *
-     * @see #onProgressUpdate
-     * @see #doInBackground
-     */
-    protected final void publishProgress(Progress... values) {
-        if (!isCancelled()) {
-            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
-                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
-        }
-    }
-
-    void finish(Result result) {
-        if (isCancelled()) {
-            onCancelled(result);
-        } else {
-            onPostExecute(result);
-        }
-        mStatus = Status.FINISHED;
-    }
-
-    private static class InternalHandler extends Handler {
-        InternalHandler() {
-            super(Looper.getMainLooper());
-        }
-
-        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
-            switch (msg.what) {
-                case MESSAGE_POST_RESULT:
-                    // There is only one result
-                    result.mTask.finish(result.mData[0]);
-                    break;
-                case MESSAGE_POST_PROGRESS:
-                    result.mTask.onProgressUpdate(result.mData);
-                    break;
-            }
-        }
-    }
-
-    private abstract static class WorkerRunnable<Params, Result> implements Callable<Result> {
-        Params[] mParams;
-
-        WorkerRunnable() {
-        }
-    }
-
-    @SuppressWarnings({"RawUseOfParameterizedType"})
-    private static class AsyncTaskResult<Data> {
-        final ModernAsyncTask mTask;
-        final Data[] mData;
-
-        AsyncTaskResult(ModernAsyncTask task, Data... data) {
-            mTask = task;
-            mData = data;
-        }
-    }
-}
diff --git a/android/support/v4/content/PermissionChecker.java b/android/support/v4/content/PermissionChecker.java
deleted file mode 100644
index c9f18a9..0000000
--- a/android/support/v4/content/PermissionChecker.java
+++ /dev/null
@@ -1,175 +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 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.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.AppOpsManagerCompat;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This class provides permission check APIs that verify both the
- * permission and the associated app op for this permission if
- * such is defined.
- * <p>
- * In the new permission model permissions with protection level
- * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
- * and above the user may not grant such permissions or revoke
- * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
- * these permissions are always granted as such apps do not expect
- * permission revocations and would crash. Therefore, when the
- * user disables a permission for a legacy app in the UI the
- * platform disables the APIs guarded by this permission making
- * them a no-op which is doing nothing or returning an empty
- * result or default error.
- * </p>
- * <p>
- * It is important that when you perform an operation on behalf of
- * another app you use these APIs to check for permissions as the
- * app may be a legacy app that does not participate in the new
- * permission model for which the user had disabled the "permission"
- * which is achieved by disallowing the corresponding app op.
- * </p>
- */
-public final class PermissionChecker {
-    /** Permission result: The permission is granted. */
-    public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;
-
-    /** Permission result: The permission is denied. */
-    public static final int PERMISSION_DENIED =  PackageManager.PERMISSION_DENIED;
-
-    /** 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})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface PermissionResult {}
-
-    private PermissionChecker() {
-        /* do nothing */
-    }
-
-    /**
-     * Checks whether a given package in a UID and PID has a given permission
-     * and whether the app op that corresponds to this permission is allowed.
-     *
-     * @param context Context for accessing resources.
-     * @param permission The permission to check.
-     * @param pid The process id for which to check.
-     * @param uid The uid for which to check.
-     * @param packageName The package name for which to check. If null the
-     *     the first package for the calling UID will be used.
-     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
-     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
-     */
-    @PermissionResult
-    public static int checkPermission(@NonNull Context context, @NonNull String permission,
-            int pid, int uid, @Nullable String packageName) {
-        if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
-            return PERMISSION_DENIED;
-        }
-
-        String op = AppOpsManagerCompat.permissionToOp(permission);
-        if (op == null) {
-            return PERMISSION_GRANTED;
-        }
-
-        if (packageName == null) {
-            String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
-            if (packageNames == null || packageNames.length <= 0) {
-                return PERMISSION_DENIED;
-            }
-            packageName = packageNames[0];
-        }
-
-        if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)
-                != AppOpsManagerCompat.MODE_ALLOWED) {
-            return PERMISSION_DENIED_APP_OP;
-        }
-
-        return PERMISSION_GRANTED;
-    }
-
-    /**
-     * Checks whether your app has a given permission and whether the app op
-     * that corresponds to this permission is allowed.
-     *
-     * @param context Context for accessing resources.
-     * @param permission The permission to check.
-     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
-     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
-     */
-    @PermissionResult
-    public static int checkSelfPermission(@NonNull Context context,
-            @NonNull String permission) {
-        return checkPermission(context, permission, android.os.Process.myPid(),
-                android.os.Process.myUid(), context.getPackageName());
-    }
-
-    /**
-     * Checks whether the IPC you are handling has a given permission and whether
-     * the app op that corresponds to this permission is allowed.
-     *
-     * @param context Context for accessing resources.
-     * @param permission The permission to check.
-     * @param packageName The package name making the IPC. If null the
-     *     the first package for the calling UID will be used.
-     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
-     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
-     */
-    @PermissionResult
-    public static int checkCallingPermission(@NonNull Context context,
-            @NonNull String permission, @Nullable String packageName) {
-        if (Binder.getCallingPid() == Process.myPid()) {
-            return PERMISSION_DENIED;
-        }
-        return checkPermission(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName);
-    }
-
-    /**
-     * Checks whether the IPC you are handling or your app has a given permission
-     * and whether the app op that corresponds to this permission is allowed.
-     *
-     * @param context Context for accessing resources.
-     * @param permission The permission to check.
-     * @return The permission check result which is either {@link #PERMISSION_GRANTED}
-     *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
-     */
-    @PermissionResult
-    public static int checkCallingOrSelfPermission(@NonNull Context context,
-            @NonNull String permission) {
-        String packageName = (Binder.getCallingPid() == Process.myPid())
-                ? context.getPackageName() : null;
-        return checkPermission(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName);
-    }
-}
diff --git a/android/support/v4/content/SharedPreferencesCompat.java b/android/support/v4/content/SharedPreferencesCompat.java
deleted file mode 100644
index 1d43c2e..0000000
--- a/android/support/v4/content/SharedPreferencesCompat.java
+++ /dev/null
@@ -1,85 +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.SharedPreferences;
-import android.support.annotation.NonNull;
-
-/**
- * @deprecated This compatibility class is no longer required. Use {@link SharedPreferences}
- * directly.
- */
-@Deprecated
-public final class SharedPreferencesCompat {
-
-    /**
-     * @deprecated This compatibility class is no longer required. Use
-     * {@link SharedPreferences.Editor} directly.
-     */
-    @Deprecated
-    public final static class EditorCompat {
-
-        private static EditorCompat sInstance;
-
-        private static class Helper {
-            Helper() {
-            }
-
-            public void apply(@NonNull SharedPreferences.Editor editor) {
-                try {
-                    editor.apply();
-                } catch (AbstractMethodError unused) {
-                    // The app injected its own pre-Gingerbread
-                    // SharedPreferences.Editor implementation without
-                    // an apply method.
-                    editor.commit();
-                }
-            }
-        }
-
-        private final Helper mHelper;
-
-        private EditorCompat() {
-            mHelper = new Helper();
-        }
-        /**
-         * @deprecated This compatibility class is no longer required. Use
-         * {@link SharedPreferences.Editor} directly.
-         */
-        @Deprecated
-        public static EditorCompat getInstance() {
-            if (sInstance == null) {
-                sInstance = new EditorCompat();
-            }
-            return sInstance;
-        }
-        /**
-         * @deprecated This compatibility method is no longer required. Use
-         * {@link SharedPreferences.Editor#apply()} directly.
-         */
-        @Deprecated
-        public void apply(@NonNull SharedPreferences.Editor editor) {
-            // Note that this redirection is needed to not break the public API chain
-            // of getInstance().apply() calls. Otherwise this method could (and should)
-            // be static.
-            mHelper.apply(editor);
-        }
-    }
-
-    private SharedPreferencesCompat() {}
-
-}
diff --git a/android/support/v4/content/WakefulBroadcastReceiver.java b/android/support/v4/content/WakefulBroadcastReceiver.java
deleted file mode 100644
index 78555aa..0000000
--- a/android/support/v4/content/WakefulBroadcastReceiver.java
+++ /dev/null
@@ -1,147 +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.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.PowerManager;
-import android.util.Log;
-import android.util.SparseArray;
-
-/**
- * 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.
- *
- * <p>This class takes care of creating and managing a partial wake lock
- * for you; you must request the {@link android.Manifest.permission#WAKE_LOCK}
- * permission to use it.</p>
- *
- * <p>Wakelocks held by this class are reported to tools as
- * {@code "androidx.core:wake:<component-name>"}.</p>
- *
- * <h3>Example</h3>
- *
- * <p>A {@link WakefulBroadcastReceiver} uses the method
- * {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()}
- * to start the service that does the work. This method is comparable to
- * {@link android.content.Context#startService startService()}, except that
- * the {@link WakefulBroadcastReceiver} is holding a wake lock when the service
- * starts. The intent that is passed with
- * {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()}
- * holds an extra identifying the wake lock.</p>
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/java/com/example/android/supportv4/content/SimpleWakefulReceiver.java
- *      complete}
- *
- * <p>The service (in this example, an {@link android.app.IntentService}) does
- * some work. When it is finished, it releases the wake lock by calling
- * {@link WakefulBroadcastReceiver#completeWakefulIntent
- * completeWakefulIntent(intent)}. The intent it passes as a parameter
- * is the same intent that the {@link WakefulBroadcastReceiver} originally
- * passed in.</p>
- *
- * {@sample frameworks/support/samples/Support4Demos/src/main/java/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> sActiveWakeLocks = new SparseArray<>();
-    private static int mNextId = 1;
-
-    /**
-     * Do a {@link android.content.Context#startService(android.content.Intent)
-     * Context.startService}, but holding a wake lock while the service starts.
-     * This will modify the Intent to hold an extra identifying the wake lock;
-     * when the service receives it in {@link android.app.Service#onStartCommand
-     * Service.onStartCommand}, it should pass back the Intent it receives there to
-     * {@link #completeWakefulIntent(android.content.Intent)} in order to release
-     * the wake lock.
-     *
-     * @param context The Context in which it operate.
-     * @param intent The Intent with which to start the service, as per
-     * {@link android.content.Context#startService(android.content.Intent)
-     * Context.startService}.
-     */
-    public static ComponentName startWakefulService(Context context, Intent intent) {
-        synchronized (sActiveWakeLocks) {
-            int id = mNextId;
-            mNextId++;
-            if (mNextId <= 0) {
-                mNextId = 1;
-            }
-
-            intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
-            ComponentName comp = context.startService(intent);
-            if (comp == null) {
-                return null;
-            }
-
-            PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    "androidx.core:wake:" + comp.flattenToShortString());
-            wl.setReferenceCounted(false);
-            wl.acquire(60 * 1000);
-            sActiveWakeLocks.put(id, wl);
-            return comp;
-        }
-    }
-
-    /**
-     * Finish the execution from a previous {@link #startWakefulService}.  Any wake lock
-     * that was being held will now be released.
-     *
-     * @param intent The Intent as originally generated by {@link #startWakefulService}.
-     * @return Returns true if the intent is associated with a wake lock that is
-     * now released; returns false if there was no wake lock specified for it.
-     */
-    public static boolean completeWakefulIntent(Intent intent) {
-        final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
-        if (id == 0) {
-            return false;
-        }
-        synchronized (sActiveWakeLocks) {
-            PowerManager.WakeLock wl = sActiveWakeLocks.get(id);
-            if (wl != null) {
-                wl.release();
-                sActiveWakeLocks.remove(id);
-                return true;
-            }
-            // We return true whether or not we actually found the wake lock
-            // the return code is defined to indicate whether the Intent contained
-            // an identifier for a wake lock that it was supposed to match.
-            // 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("WakefulBroadcastReceiv.", "No active wake lock id #" + id);
-            return true;
-        }
-    }
-}
diff --git a/android/support/v4/content/pm/ActivityInfoCompat.java b/android/support/v4/content/pm/ActivityInfoCompat.java
deleted file mode 100644
index eb40a5e..0000000
--- a/android/support/v4/content/pm/ActivityInfoCompat.java
+++ /dev/null
@@ -1,40 +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.content.pm;
-
-/**
- * Helper for accessing features in {@link android.content.pm.ActivityInfo}.
- *
- * @deprecated Use {@link android.content.pm.ActivityInfo} directly.
- */
-@Deprecated
-public final class ActivityInfoCompat {
-
-    private ActivityInfoCompat() {
-        /* Hide constructor */
-    }
-
-    /**
-     * Bit in ActivityInfo#configChanges that indicates that the
-     * activity can itself handle the ui mode. Set from the
-     * {@link android.R.attr#configChanges} attribute.
-     *
-     * @deprecated Use {@link android.content.pm.ActivityInfo#CONFIG_UI_MODE} directly.
-     */
-    @Deprecated
-    public static final int CONFIG_UI_MODE = 0x0200;
-}
diff --git a/android/support/v4/content/pm/ShortcutInfoCompat.java b/android/support/v4/content/pm/ShortcutInfoCompat.java
deleted file mode 100644
index 63585e1..0000000
--- a/android/support/v4/content/pm/ShortcutInfoCompat.java
+++ /dev/null
@@ -1,312 +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.content.pm;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.graphics.drawable.IconCompat;
-import android.text.TextUtils;
-
-import java.util.Arrays;
-
-/**
- * Helper for accessing features in {@link 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 boolean mIsAlwaysBadged;
-
-    private ShortcutInfoCompat() { }
-
-    /**
-     * @return {@link ShortcutInfo} object from this compat object.
-     */
-    @RequiresApi(25)
-    public 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();
-    }
-
-    @VisibleForTesting
-    Intent addToIntent(Intent outIntent) {
-        outIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, mIntents[mIntents.length - 1])
-                .putExtra(Intent.EXTRA_SHORTCUT_NAME, mLabel.toString());
-        if (mIcon != null) {
-            Drawable badge = null;
-            if (mIsAlwaysBadged) {
-                PackageManager pm = mContext.getPackageManager();
-                if (mActivity != null) {
-                    try {
-                        badge = pm.getActivityIcon(mActivity);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        // Ignore
-                    }
-                }
-                if (badge == null) {
-                    badge = mContext.getApplicationInfo().loadIcon(pm);
-                }
-            }
-            mIcon.addToShortcutIntent(outIntent, badge);
-        }
-        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.
-         */
-        @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 ShortcutInfo.Builder#setActivity(ComponentName)
-         */
-        @NonNull
-        public Builder setActivity(@NonNull ComponentName activity) {
-            mInfo.mActivity = activity;
-            return this;
-        }
-
-        /**
-         * Badges the icon before passing it over to the Launcher.
-         * <p>
-         * Launcher automatically badges {@link ShortcutInfo}, so only the legacy shortcut icon,
-         * {@link Intent.ShortcutIconResource} is badged. This field is ignored when using
-         * {@link ShortcutInfo} on API 25 and above.
-         * <p>
-         * If the shortcut is associated with an activity, the activity icon is used as the badge,
-         * otherwise application icon is used.
-         *
-         * @see #setActivity(ComponentName)
-         */
-        public Builder setAlwaysBadged() {
-            mInfo.mIsAlwaysBadged = true;
-            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/android/support/v4/content/pm/ShortcutManagerCompat.java b/android/support/v4/content/pm/ShortcutManagerCompat.java
deleted file mode 100644
index 189e51d..0000000
--- a/android/support/v4/content/pm/ShortcutManagerCompat.java
+++ /dev/null
@@ -1,143 +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.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/android/support/v4/content/res/ConfigurationHelper.java b/android/support/v4/content/res/ConfigurationHelper.java
deleted file mode 100644
index 39b0e4a..0000000
--- a/android/support/v4/content/res/ConfigurationHelper.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.content.res;
-
-import static android.os.Build.VERSION.SDK_INT;
-
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.support.annotation.NonNull;
-
-/**
- * Helper class which allows access to properties of {@link Configuration} in
- * a backward compatible fashion.
- */
-public final class ConfigurationHelper {
-    private ConfigurationHelper() {
-    }
-
-    /**
-     * Returns the target screen density being rendered to.
-     *
-     * <p>Uses {@code Configuration.densityDpi} when available, otherwise an approximation
-     * is computed and returned.</p>
-     */
-    public static int getDensityDpi(@NonNull Resources resources) {
-        if (SDK_INT >= 17) {
-            return resources.getConfiguration().densityDpi;
-        } else {
-            return resources.getDisplayMetrics().densityDpi;
-        }
-    }
-}
diff --git a/android/support/v4/content/res/FontResourcesParserCompat.java b/android/support/v4/content/res/FontResourcesParserCompat.java
deleted file mode 100644
index f597e68..0000000
--- a/android/support/v4/content/res/FontResourcesParserCompat.java
+++ /dev/null
@@ -1,310 +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.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 String mVariationSettings;
-        private int mTtcIndex;
-        private int mResourceId;
-
-        public FontFileResourceEntry(@NonNull String fileName, int weight, boolean italic,
-                @Nullable String variationSettings, int ttcIndex, int resourceId) {
-            mFileName = fileName;
-            mWeight = weight;
-            mItalic = italic;
-            mVariationSettings = variationSettings;
-            mTtcIndex = ttcIndex;
-            mResourceId = resourceId;
-        }
-
-        public @NonNull String getFileName() {
-            return mFileName;
-        }
-
-        public int getWeight() {
-            return mWeight;
-        }
-
-        public boolean isItalic() {
-            return mItalic;
-        }
-
-        public @Nullable String getVariationSettings() {
-            return mVariationSettings;
-        }
-
-        public int getTtcIndex() {
-            return mTtcIndex;
-        }
-
-        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);
-        final int weightAttr = array.hasValue(R.styleable.FontFamilyFont_fontWeight)
-                ? R.styleable.FontFamilyFont_fontWeight
-                : R.styleable.FontFamilyFont_android_fontWeight;
-        int weight = array.getInt(weightAttr, NORMAL_WEIGHT);
-        final int styleAttr = array.hasValue(R.styleable.FontFamilyFont_fontStyle)
-                ? R.styleable.FontFamilyFont_fontStyle
-                : R.styleable.FontFamilyFont_android_fontStyle;
-        boolean isItalic = ITALIC == array.getInt(styleAttr, 0);
-        final int ttcIndexAttr = array.hasValue(R.styleable.FontFamilyFont_ttcIndex)
-                ? R.styleable.FontFamilyFont_ttcIndex
-                : R.styleable.FontFamilyFont_android_ttcIndex;
-        final int variationSettingsAttr =
-                array.hasValue(R.styleable.FontFamilyFont_fontVariationSettings)
-                        ? R.styleable.FontFamilyFont_fontVariationSettings
-                        : R.styleable.FontFamilyFont_android_fontVariationSettings;
-        String variationSettings = array.getString(variationSettingsAttr);
-        int ttcIndex = array.getInt(ttcIndexAttr, 0);
-        final int resourceAttr = array.hasValue(R.styleable.FontFamilyFont_font)
-                ? R.styleable.FontFamilyFont_font
-                : R.styleable.FontFamilyFont_android_font;
-        int resourceId = array.getResourceId(resourceAttr, 0);
-        String filename = array.getString(resourceAttr);
-        array.recycle();
-        while (parser.next() != XmlPullParser.END_TAG) {
-            skip(parser);
-        }
-        return new FontFileResourceEntry(filename, weight, isItalic, variationSettings, ttcIndex,
-                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/android/support/v4/content/res/ResourcesCompat.java b/android/support/v4/content/res/ResourcesCompat.java
deleted file mode 100644
index 15b8ce9..0000000
--- a/android/support/v4/content/res/ResourcesCompat.java
+++ /dev/null
@@ -1,417 +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 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.os.Handler;
-import android.os.Looper;
-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.support.v4.provider.FontsContractCompat.FontRequestCallback;
-import android.support.v4.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason;
-import android.support.v4.util.Preconditions;
-import android.util.Log;
-import android.util.TypedValue;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-/**
- * 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
-     * returned depending on the underlying resource -- for example, a solid
-     * color, PNG image, scalable image, etc.
-     * <p>
-     * Prior to API level 21, the theme will not be applied and this method
-     * simply calls through to {@link Resources#getDrawable(int)}.
-     *
-     * @param id The desired resource identifier, as generated by the aapt
-     *           tool. This integer encodes the package, type, and resource
-     *           entry. The value 0 is an invalid identifier.
-     * @param theme The theme used to style the drawable attributes, may be
-     *              {@code null}.
-     * @return Drawable An object that can be used to draw this resource.
-     * @throws NotFoundException Throws NotFoundException if the given ID does
-     *         not exist.
-     */
-    @Nullable
-    @SuppressWarnings("deprecation")
-    public static Drawable getDrawable(@NonNull Resources res, @DrawableRes int id,
-            @Nullable Theme theme) throws NotFoundException {
-        if (SDK_INT >= 21) {
-            return res.getDrawable(id, theme);
-        } else {
-            return res.getDrawable(id);
-        }
-    }
-
-
-    /**
-     * Return a drawable object associated with a particular resource ID for
-     * the given screen density in DPI and styled for the specified theme.
-     * <p>
-     * Prior to API level 15, the theme and density will not be applied and
-     * this method simply calls through to {@link Resources#getDrawable(int)}.
-     * <p>
-     * Prior to API level 21, the theme will not be applied and this method
-     * calls through to Resources#getDrawableForDensity(int, int).
-     *
-     * @param id The desired resource identifier, as generated by the aapt
-     *           tool. This integer encodes the package, type, and resource
-     *           entry. The value 0 is an invalid identifier.
-     * @param density The desired screen density indicated by the resource as
-     *                found in {@link android.util.DisplayMetrics}.
-     * @param theme The theme used to style the drawable attributes, may be
-     *              {@code null}.
-     * @return Drawable An object that can be used to draw this resource.
-     * @throws NotFoundException Throws NotFoundException if the given ID does
-     *         not exist.
-     */
-    @Nullable
-    @SuppressWarnings("deprecation")
-    public static Drawable getDrawableForDensity(@NonNull Resources res, @DrawableRes int id,
-            int density, @Nullable Theme theme) throws NotFoundException {
-        if (SDK_INT >= 21) {
-            return res.getDrawableForDensity(id, density, theme);
-        } else if (SDK_INT >= 15) {
-            return res.getDrawableForDensity(id, density);
-        } else {
-            return res.getDrawable(id);
-        }
-    }
-
-    /**
-     * Returns a themed color integer associated with a particular resource ID.
-     * If the resource holds a complex {@link ColorStateList}, then the default
-     * color from the set is returned.
-     * <p>
-     * Prior to API level 23, the theme will not be applied and this method
-     * calls through to {@link Resources#getColor(int)}.
-     *
-     * @param id The desired resource identifier, as generated by the aapt
-     *           tool. This integer encodes the package, type, and resource
-     *           entry. The value 0 is an invalid identifier.
-     * @param theme The theme used to style the color attributes, may be
-     *              {@code null}.
-     * @return A single color value in the form {@code 0xAARRGGBB}.
-     * @throws NotFoundException Throws NotFoundException if the given ID does
-     *         not exist.
-     */
-    @ColorInt
-    @SuppressWarnings("deprecation")
-    public static int getColor(@NonNull Resources res, @ColorRes int id, @Nullable Theme theme)
-            throws NotFoundException {
-        if (SDK_INT >= 23) {
-            return res.getColor(id, theme);
-        } else {
-            return res.getColor(id);
-        }
-    }
-
-    /**
-     * Returns a themed color state list associated with a particular resource
-     * ID. The resource may contain either a single raw color value or a
-     * complex {@link ColorStateList} holding multiple possible colors.
-     * <p>
-     * Prior to API level 23, the theme will not be applied and this method
-     * calls through to {@link Resources#getColorStateList(int)}.
-     *
-     * @param id The desired resource identifier of a {@link ColorStateList},
-     *           as generated by the aapt tool. This integer encodes the
-     *           package, type, and resource entry. The value 0 is an invalid
-     *           identifier.
-     * @param theme The theme used to style the color attributes, may be
-     *              {@code null}.
-     * @return A themed ColorStateList object containing either a single solid
-     *         color or multiple colors that can be selected based on a state.
-     * @throws NotFoundException Throws NotFoundException if the given ID does
-     *         not exist.
-     */
-    @Nullable
-    @SuppressWarnings("deprecation")
-    public static ColorStateList getColorStateList(@NonNull Resources res, @ColorRes int id,
-            @Nullable Theme theme) throws NotFoundException {
-        if (SDK_INT >= 23) {
-            return res.getColorStateList(id, theme);
-        } else {
-            return res.getColorStateList(id);
-        }
-    }
-
-    /**
-     * Returns a font Typeface associated with a particular resource ID.
-     * <p>
-     * This method will block the calling thread to retrieve the requested font, including if it
-     * is from a font provider. If you wish to not have this behavior, use
-     * {@link #getFont(Context, int, FontCallback, Handler)} instead.
-     * <p>
-     * Prior to API level 23, font resources with more than one font in a family will only load the
-     * font closest to a regular weight typeface.
-     *
-     * @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.
-     *
-     * @see #getFont(Context, int, FontCallback, Handler)
-     */
-    @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 /* callback */,
-                null /* handler */, false /* isXmlRequest */);
-    }
-
-    /**
-     * Interface used to receive asynchronous font fetching events.
-     */
-    public abstract static class FontCallback {
-
-        /**
-         * Called when an asynchronous font was finished loading.
-         *
-         * @param typeface The font that was loaded.
-         */
-        public abstract void onFontRetrieved(@NonNull Typeface typeface);
-
-        /**
-         * Called when an asynchronous font failed to load.
-         *
-         * @param reason The reason the font failed to load. One of
-         *      {@link FontRequestFailReason#FAIL_REASON_PROVIDER_NOT_FOUND},
-         *      {@link FontRequestFailReason#FAIL_REASON_WRONG_CERTIFICATES},
-         *      {@link FontRequestFailReason#FAIL_REASON_FONT_LOAD_ERROR},
-         *      {@link FontRequestFailReason#FAIL_REASON_SECURITY_VIOLATION},
-         *      {@link FontRequestFailReason#FAIL_REASON_FONT_NOT_FOUND},
-         *      {@link FontRequestFailReason#FAIL_REASON_FONT_UNAVAILABLE} or
-         *      {@link FontRequestFailReason#FAIL_REASON_MALFORMED_QUERY}.
-         */
-        public abstract void onFontRetrievalFailed(@FontRequestFailReason int reason);
-
-        /**
-         * Call {@link #onFontRetrieved(Typeface)} on the handler given, or the Ui Thread if it is
-         * null.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public final void callbackSuccessAsync(final Typeface typeface, @Nullable Handler handler) {
-            if (handler == null) {
-                handler = new Handler(Looper.getMainLooper());
-            }
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onFontRetrieved(typeface);
-                }
-            });
-        }
-
-        /**
-         * Call {@link #onFontRetrievalFailed(int)} on the handler given, or the Ui Thread if it is
-         * null.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public final void callbackFailAsync(
-                @FontRequestFailReason final int reason, @Nullable Handler handler) {
-            if (handler == null) {
-                handler = new Handler(Looper.getMainLooper());
-            }
-            handler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onFontRetrievalFailed(reason);
-                }
-            });
-        }
-    }
-
-    /**
-     * Returns a font Typeface associated with a particular resource ID asynchronously.
-     * <p>
-     * Prior to API level 23, font resources with more than one font in a family will only load the
-     * font closest to a regular weight typeface.
-     * </p>
-     *
-     * @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.
-     * @param fontCallback A callback to receive async fetching of this font. The callback will be
-     *           triggered on the UI thread.
-     * @param handler A handler for the thread the callback should be called on. If null, the
-     *           callback will be called on the UI thread.
-     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
-     */
-    public static void getFont(@NonNull Context context, @FontRes int id,
-            @NonNull FontCallback fontCallback, @Nullable Handler handler)
-            throws NotFoundException {
-        Preconditions.checkNotNull(fontCallback);
-        if (context.isRestricted()) {
-            fontCallback.callbackFailAsync(
-                    FontRequestCallback.FAIL_REASON_SECURITY_VIOLATION, handler);
-            return;
-        }
-        loadFont(context, id, new TypedValue(), Typeface.NORMAL, fontCallback, handler,
-                false /* isXmlRequest */);
-    }
-
-    /**
-     * Used by TintTypedArray.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,
-            int style, @Nullable FontCallback fontCallback) throws NotFoundException {
-        if (context.isRestricted()) {
-            return null;
-        }
-        return loadFont(context, id, value, style, fontCallback, null /* handler */,
-                true /* isXmlRequest */);
-    }
-
-    /**
-     *
-     * @param context The Context to get Resources from
-     * @param id The Resource id to load
-     * @param value A TypedValue to use in the fetching
-     * @param style The font style to load
-     * @param fontCallback A callback to trigger when the font is fetched or an error occurs
-     * @param handler A handler to the thread the callback should be called on
-     * @param isRequestFromLayoutInflator Whether this request originated from XML. This is used to
-     *                     determine if we use or ignore the fontProviderFetchStrategy attribute in
-     *                     font provider XML fonts.
-     * @return
-     */
-    private static Typeface loadFont(@NonNull Context context, int id, TypedValue value,
-            int style, @Nullable FontCallback fontCallback, @Nullable Handler handler,
-            boolean isRequestFromLayoutInflator) {
-        final Resources resources = context.getResources();
-        resources.getValue(id, value, true);
-        Typeface typeface = loadFont(context, resources, value, id, style, fontCallback, handler,
-                isRequestFromLayoutInflator);
-        if (typeface == null && fontCallback == null) {
-            throw new NotFoundException("Font resource ID #0x"
-                    + Integer.toHexString(id) + " could not be retrieved.");
-        }
-        return typeface;
-    }
-
-    /**
-     * Load the given font. This method will always return null for asynchronous requests, which
-     * provide a fontCallback, as there is no immediate result. When the callback is not provided,
-     * the request is treated as synchronous and fails if async loading is required.
-     */
-    private static Typeface loadFont(
-            @NonNull Context context, Resources wrapper, TypedValue value, int id, int style,
-            @Nullable FontCallback fontCallback, @Nullable Handler handler,
-            boolean isRequestFromLayoutInflator) {
-        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 be a resource path.
-            if (fontCallback != null) {
-                fontCallback.callbackFailAsync(
-                        FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, handler);
-            }
-            return null;
-        }
-        Typeface typeface = TypefaceCompat.findFromCache(wrapper, id, style);
-
-        if (typeface != null) {
-            if (fontCallback != null) {
-                fontCallback.callbackSuccessAsync(typeface, handler);
-            }
-            return typeface;
-        }
-
-        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");
-                    if (fontCallback != null) {
-                        fontCallback.callbackFailAsync(
-                                FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, handler);
-                    }
-                    return null;
-                }
-                return TypefaceCompat.createFromResourcesFamilyXml(context, familyEntry, wrapper,
-                        id, style, fontCallback, handler, isRequestFromLayoutInflator);
-            }
-            typeface = TypefaceCompat.createFromResourcesFontFile(
-                    context, wrapper, id, file, style);
-            if (fontCallback != null) {
-                if (typeface != null) {
-                    fontCallback.callbackSuccessAsync(typeface, handler);
-                } else {
-                    fontCallback.callbackFailAsync(
-                            FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, handler);
-                }
-            }
-            return typeface;
-        } 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);
-        }
-        if (fontCallback != null) {
-            fontCallback.callbackFailAsync(
-                    FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, handler);
-        }
-        return null;
-    }
-
-    private ResourcesCompat() {}
-}
diff --git a/android/support/v4/content/res/TypedArrayUtils.java b/android/support/v4/content/res/TypedArrayUtils.java
deleted file mode 100644
index 64cb981..0000000
--- a/android/support/v4/content/res/TypedArrayUtils.java
+++ /dev/null
@@ -1,296 +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.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.Nullable;
-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,
-            @NonNull 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,
-            @NonNull 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,
-            @NonNull 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,
-            @NonNull 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.
-     */
-    @Nullable
-    public static String getNamedString(@NonNull TypedArray a, @NonNull XmlPullParser parser,
-            @NonNull 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}.
-     */
-    @Nullable
-    public static TypedValue peekNamedValue(@NonNull TypedArray a, @NonNull XmlPullParser parser,
-            @NonNull 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.
-     */
-    @NonNull
-    public static TypedArray obtainAttributes(@NonNull Resources res,
-            @Nullable Resources.Theme theme, @NonNull AttributeSet set, @NonNull 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(@NonNull 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}.
-     */
-    @Nullable
-    public static Drawable getDrawable(@NonNull 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(@NonNull 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(@NonNull 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}.
-     */
-    @Nullable
-    public static String getString(@NonNull 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}.
-     */
-    @Nullable
-    public static CharSequence getText(@NonNull 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}.
-     */
-    @Nullable
-    public static CharSequence[] getTextArray(@NonNull 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(@NonNull 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/android/support/v4/database/DatabaseUtilsCompat.java b/android/support/v4/database/DatabaseUtilsCompat.java
deleted file mode 100644
index 419ae09..0000000
--- a/android/support/v4/database/DatabaseUtilsCompat.java
+++ /dev/null
@@ -1,68 +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.database;
-
-import android.text.TextUtils;
-
-/**
- * Helper for accessing features in {@link android.database.DatabaseUtils}.
- *
- * @deprecated Use {@link android.database.DatabaseUtils} directly.
- */
-@Deprecated
-public final class DatabaseUtilsCompat {
-
-    private DatabaseUtilsCompat() {
-        /* Hide constructor */
-    }
-
-    /**
-     * Concatenates two SQL WHERE clauses, handling empty or null values.
-     *
-     * @deprecated Use {@link android.database.DatabaseUtils#concatenateWhere(String, String)}
-     * directly.
-     */
-    @Deprecated
-    public static String concatenateWhere(String a, String b) {
-        if (TextUtils.isEmpty(a)) {
-            return b;
-        }
-        if (TextUtils.isEmpty(b)) {
-            return a;
-        }
-
-        return "(" + a + ") AND (" + b + ")";
-    }
-
-    /**
-     * Appends one set of selection args to another. This is useful when adding a selection
-     * argument to a user provided set.
-     *
-     * @deprecated Use
-     * {@link android.database.DatabaseUtils#appendSelectionArgs(String[], String[])} directly.
-     */
-    @Deprecated
-    public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
-        if (originalValues == null || originalValues.length == 0) {
-            return newValues;
-        }
-        String[] result = new String[originalValues.length + newValues.length ];
-        System.arraycopy(originalValues, 0, result, 0, originalValues.length);
-        System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
-        return result;
-    }
-}
diff --git a/android/support/v4/graphics/BitmapCompat.java b/android/support/v4/graphics/BitmapCompat.java
deleted file mode 100644
index acc37b1..0000000
--- a/android/support/v4/graphics/BitmapCompat.java
+++ /dev/null
@@ -1,95 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-
-/**
- * Helper for accessing features in {@link android.graphics.Bitmap}.
- */
-public final class BitmapCompat {
-    static class BitmapCompatBaseImpl {
-        public boolean hasMipMap(Bitmap bitmap) {
-            return false;
-        }
-
-        public void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
-        }
-
-        public int getAllocationByteCount(Bitmap bitmap) {
-            return bitmap.getByteCount();
-        }
-    }
-
-    @RequiresApi(18)
-    static class BitmapCompatApi18Impl extends BitmapCompatBaseImpl {
-        @Override
-        public boolean hasMipMap(Bitmap bitmap){
-            return bitmap.hasMipMap();
-        }
-
-        @Override
-        public void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
-            bitmap.setHasMipMap(hasMipMap);
-        }
-    }
-
-    @RequiresApi(19)
-    static class BitmapCompatApi19Impl extends BitmapCompatApi18Impl {
-        @Override
-        public int getAllocationByteCount(Bitmap bitmap) {
-            return bitmap.getAllocationByteCount();
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final BitmapCompatBaseImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 19) {
-            IMPL = new BitmapCompatApi19Impl();
-        } else if (Build.VERSION.SDK_INT >= 18) {
-            IMPL = new BitmapCompatApi18Impl();
-        } else {
-            IMPL = new BitmapCompatBaseImpl();
-        }
-    }
-
-    public static boolean hasMipMap(@NonNull Bitmap bitmap) {
-        return IMPL.hasMipMap(bitmap);
-    }
-
-    public static void setHasMipMap(@NonNull Bitmap bitmap, boolean hasMipMap) {
-        IMPL.setHasMipMap(bitmap, hasMipMap);
-    }
-
-    /**
-     * Returns the size of the allocated memory used to store this bitmap's pixels in a backwards
-     * compatible way.
-     *
-     * @param bitmap the bitmap in which to return its allocation size
-     * @return the allocation size in bytes
-     */
-    public static int getAllocationByteCount(@NonNull Bitmap bitmap) {
-        return IMPL.getAllocationByteCount(bitmap);
-    }
-
-    private BitmapCompat() {}
-}
\ No newline at end of file
diff --git a/android/support/v4/graphics/ColorUtils.java b/android/support/v4/graphics/ColorUtils.java
deleted file mode 100644
index c58d2ba..0000000
--- a/android/support/v4/graphics/ColorUtils.java
+++ /dev/null
@@ -1,618 +0,0 @@
-/*
- * Copyright 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;
-
-import android.graphics.Color;
-import android.support.annotation.ColorInt;
-import android.support.annotation.FloatRange;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.VisibleForTesting;
-
-/**
- * A set of color-related utility methods, building upon those available in {@code Color}.
- */
-public final class ColorUtils {
-
-    private static final double XYZ_WHITE_REFERENCE_X = 95.047;
-    private static final double XYZ_WHITE_REFERENCE_Y = 100;
-    private static final double XYZ_WHITE_REFERENCE_Z = 108.883;
-    private static final double XYZ_EPSILON = 0.008856;
-    private static final double XYZ_KAPPA = 903.3;
-
-    private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
-    private static final int MIN_ALPHA_SEARCH_PRECISION = 1;
-
-    private static final ThreadLocal<double[]> TEMP_ARRAY = new ThreadLocal<>();
-
-    private ColorUtils() {}
-
-    /**
-     * Composite two potentially translucent colors over each other and returns the result.
-     */
-    public static int compositeColors(@ColorInt int foreground, @ColorInt int background) {
-        int bgAlpha = Color.alpha(background);
-        int fgAlpha = Color.alpha(foreground);
-        int a = compositeAlpha(fgAlpha, bgAlpha);
-
-        int r = compositeComponent(Color.red(foreground), fgAlpha,
-                Color.red(background), bgAlpha, a);
-        int g = compositeComponent(Color.green(foreground), fgAlpha,
-                Color.green(background), bgAlpha, a);
-        int b = compositeComponent(Color.blue(foreground), fgAlpha,
-                Color.blue(background), bgAlpha, a);
-
-        return Color.argb(a, r, g, b);
-    }
-
-    private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
-        return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
-    }
-
-    private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) {
-        if (a == 0) return 0;
-        return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF);
-    }
-
-    /**
-     * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}.
-     * <p>Defined as the Y component in the XYZ representation of {@code color}.</p>
-     */
-    @FloatRange(from = 0.0, to = 1.0)
-    public static double calculateLuminance(@ColorInt int color) {
-        final double[] result = getTempDouble3Array();
-        colorToXYZ(color, result);
-        // Luminance is the Y component
-        return result[1] / 100;
-    }
-
-    /**
-     * Returns the contrast ratio between {@code foreground} and {@code background}.
-     * {@code background} must be opaque.
-     * <p>
-     * Formula defined
-     * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
-     */
-    public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) {
-        if (Color.alpha(background) != 255) {
-            throw new IllegalArgumentException("background can not be translucent: #"
-                    + Integer.toHexString(background));
-        }
-        if (Color.alpha(foreground) < 255) {
-            // If the foreground is translucent, composite the foreground over the background
-            foreground = compositeColors(foreground, background);
-        }
-
-        final double luminance1 = calculateLuminance(foreground) + 0.05;
-        final double luminance2 = calculateLuminance(background) + 0.05;
-
-        // Now return the lighter luminance divided by the darker luminance
-        return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2);
-    }
-
-    /**
-     * Calculates the minimum alpha value which can be applied to {@code foreground} so that would
-     * have a contrast value of at least {@code minContrastRatio} when compared to
-     * {@code background}.
-     *
-     * @param foreground       the foreground color
-     * @param background       the opaque background color
-     * @param minContrastRatio the minimum contrast ratio
-     * @return the alpha value in the range 0-255, or -1 if no value could be calculated
-     */
-    public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background,
-            float minContrastRatio) {
-        if (Color.alpha(background) != 255) {
-            throw new IllegalArgumentException("background can not be translucent: #"
-                    + Integer.toHexString(background));
-        }
-
-        // First lets check that a fully opaque foreground has sufficient contrast
-        int testForeground = setAlphaComponent(foreground, 255);
-        double testRatio = calculateContrast(testForeground, background);
-        if (testRatio < minContrastRatio) {
-            // Fully opaque foreground does not have sufficient contrast, return error
-            return -1;
-        }
-
-        // Binary search to find a value with the minimum value which provides sufficient contrast
-        int numIterations = 0;
-        int minAlpha = 0;
-        int maxAlpha = 255;
-
-        while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&
-                (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {
-            final int testAlpha = (minAlpha + maxAlpha) / 2;
-
-            testForeground = setAlphaComponent(foreground, testAlpha);
-            testRatio = calculateContrast(testForeground, background);
-
-            if (testRatio < minContrastRatio) {
-                minAlpha = testAlpha;
-            } else {
-                maxAlpha = testAlpha;
-            }
-
-            numIterations++;
-        }
-
-        // Conservatively return the max of the range of possible alphas, which is known to pass.
-        return maxAlpha;
-    }
-
-    /**
-     * Convert RGB components to HSL (hue-saturation-lightness).
-     * <ul>
-     * <li>outHsl[0] is Hue [0 .. 360)</li>
-     * <li>outHsl[1] is Saturation [0...1]</li>
-     * <li>outHsl[2] is Lightness [0...1]</li>
-     * </ul>
-     *
-     * @param r      red component value [0..255]
-     * @param g      green component value [0..255]
-     * @param b      blue component value [0..255]
-     * @param outHsl 3-element array which holds the resulting HSL components
-     */
-    public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
-            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
-            @NonNull float[] outHsl) {
-        final float rf = r / 255f;
-        final float gf = g / 255f;
-        final float bf = b / 255f;
-
-        final float max = Math.max(rf, Math.max(gf, bf));
-        final float min = Math.min(rf, Math.min(gf, bf));
-        final float deltaMaxMin = max - min;
-
-        float h, s;
-        float l = (max + min) / 2f;
-
-        if (max == min) {
-            // Monochromatic
-            h = s = 0f;
-        } else {
-            if (max == rf) {
-                h = ((gf - bf) / deltaMaxMin) % 6f;
-            } else if (max == gf) {
-                h = ((bf - rf) / deltaMaxMin) + 2f;
-            } else {
-                h = ((rf - gf) / deltaMaxMin) + 4f;
-            }
-
-            s = deltaMaxMin / (1f - Math.abs(2f * l - 1f));
-        }
-
-        h = (h * 60f) % 360f;
-        if (h < 0) {
-            h += 360f;
-        }
-
-        outHsl[0] = constrain(h, 0f, 360f);
-        outHsl[1] = constrain(s, 0f, 1f);
-        outHsl[2] = constrain(l, 0f, 1f);
-    }
-
-    /**
-     * Convert the ARGB color to its HSL (hue-saturation-lightness) components.
-     * <ul>
-     * <li>outHsl[0] is Hue [0 .. 360)</li>
-     * <li>outHsl[1] is Saturation [0...1]</li>
-     * <li>outHsl[2] is Lightness [0...1]</li>
-     * </ul>
-     *
-     * @param color  the ARGB color to convert. The alpha component is ignored
-     * @param outHsl 3-element array which holds the resulting HSL components
-     */
-    public static void colorToHSL(@ColorInt int color, @NonNull float[] outHsl) {
-        RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), outHsl);
-    }
-
-    /**
-     * Convert HSL (hue-saturation-lightness) components to a RGB color.
-     * <ul>
-     * <li>hsl[0] is Hue [0 .. 360)</li>
-     * <li>hsl[1] is Saturation [0...1]</li>
-     * <li>hsl[2] is Lightness [0...1]</li>
-     * </ul>
-     * If hsv values are out of range, they are pinned.
-     *
-     * @param hsl 3-element array which holds the input HSL components
-     * @return the resulting RGB color
-     */
-    @ColorInt
-    public static int HSLToColor(@NonNull float[] hsl) {
-        final float h = hsl[0];
-        final float s = hsl[1];
-        final float l = hsl[2];
-
-        final float c = (1f - Math.abs(2 * l - 1f)) * s;
-        final float m = l - 0.5f * c;
-        final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
-
-        final int hueSegment = (int) h / 60;
-
-        int r = 0, g = 0, b = 0;
-
-        switch (hueSegment) {
-            case 0:
-                r = Math.round(255 * (c + m));
-                g = Math.round(255 * (x + m));
-                b = Math.round(255 * m);
-                break;
-            case 1:
-                r = Math.round(255 * (x + m));
-                g = Math.round(255 * (c + m));
-                b = Math.round(255 * m);
-                break;
-            case 2:
-                r = Math.round(255 * m);
-                g = Math.round(255 * (c + m));
-                b = Math.round(255 * (x + m));
-                break;
-            case 3:
-                r = Math.round(255 * m);
-                g = Math.round(255 * (x + m));
-                b = Math.round(255 * (c + m));
-                break;
-            case 4:
-                r = Math.round(255 * (x + m));
-                g = Math.round(255 * m);
-                b = Math.round(255 * (c + m));
-                break;
-            case 5:
-            case 6:
-                r = Math.round(255 * (c + m));
-                g = Math.round(255 * m);
-                b = Math.round(255 * (x + m));
-                break;
-        }
-
-        r = constrain(r, 0, 255);
-        g = constrain(g, 0, 255);
-        b = constrain(b, 0, 255);
-
-        return Color.rgb(r, g, b);
-    }
-
-    /**
-     * Set the alpha component of {@code color} to be {@code alpha}.
-     */
-    @ColorInt
-    public static int setAlphaComponent(@ColorInt int color,
-            @IntRange(from = 0x0, to = 0xFF) int alpha) {
-        if (alpha < 0 || alpha > 255) {
-            throw new IllegalArgumentException("alpha must be between 0 and 255.");
-        }
-        return (color & 0x00ffffff) | (alpha << 24);
-    }
-
-    /**
-     * Convert the ARGB color to its CIE Lab representative components.
-     *
-     * @param color  the ARGB color to convert. The alpha component is ignored
-     * @param outLab 3-element array which holds the resulting LAB components
-     */
-    public static void colorToLAB(@ColorInt int color, @NonNull double[] outLab) {
-        RGBToLAB(Color.red(color), Color.green(color), Color.blue(color), outLab);
-    }
-
-    /**
-     * Convert RGB components to its CIE Lab representative components.
-     *
-     * <ul>
-     * <li>outLab[0] is L [0 ...1)</li>
-     * <li>outLab[1] is a [-128...127)</li>
-     * <li>outLab[2] is b [-128...127)</li>
-     * </ul>
-     *
-     * @param r      red component value [0..255]
-     * @param g      green component value [0..255]
-     * @param b      blue component value [0..255]
-     * @param outLab 3-element array which holds the resulting LAB components
-     */
-    public static void RGBToLAB(@IntRange(from = 0x0, to = 0xFF) int r,
-            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
-            @NonNull double[] outLab) {
-        // First we convert RGB to XYZ
-        RGBToXYZ(r, g, b, outLab);
-        // outLab now contains XYZ
-        XYZToLAB(outLab[0], outLab[1], outLab[2], outLab);
-        // outLab now contains LAB representation
-    }
-
-    /**
-     * Convert the ARGB color to its CIE XYZ representative components.
-     *
-     * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
-     * 2° Standard Observer (1931).</p>
-     *
-     * <ul>
-     * <li>outXyz[0] is X [0 ...95.047)</li>
-     * <li>outXyz[1] is Y [0...100)</li>
-     * <li>outXyz[2] is Z [0...108.883)</li>
-     * </ul>
-     *
-     * @param color  the ARGB color to convert. The alpha component is ignored
-     * @param outXyz 3-element array which holds the resulting LAB components
-     */
-    public static void colorToXYZ(@ColorInt int color, @NonNull double[] outXyz) {
-        RGBToXYZ(Color.red(color), Color.green(color), Color.blue(color), outXyz);
-    }
-
-    /**
-     * Convert RGB components to its CIE XYZ representative components.
-     *
-     * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
-     * 2° Standard Observer (1931).</p>
-     *
-     * <ul>
-     * <li>outXyz[0] is X [0 ...95.047)</li>
-     * <li>outXyz[1] is Y [0...100)</li>
-     * <li>outXyz[2] is Z [0...108.883)</li>
-     * </ul>
-     *
-     * @param r      red component value [0..255]
-     * @param g      green component value [0..255]
-     * @param b      blue component value [0..255]
-     * @param outXyz 3-element array which holds the resulting XYZ components
-     */
-    public static void RGBToXYZ(@IntRange(from = 0x0, to = 0xFF) int r,
-            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
-            @NonNull double[] outXyz) {
-        if (outXyz.length != 3) {
-            throw new IllegalArgumentException("outXyz must have a length of 3.");
-        }
-
-        double sr = r / 255.0;
-        sr = sr < 0.04045 ? sr / 12.92 : Math.pow((sr + 0.055) / 1.055, 2.4);
-        double sg = g / 255.0;
-        sg = sg < 0.04045 ? sg / 12.92 : Math.pow((sg + 0.055) / 1.055, 2.4);
-        double sb = b / 255.0;
-        sb = sb < 0.04045 ? sb / 12.92 : Math.pow((sb + 0.055) / 1.055, 2.4);
-
-        outXyz[0] = 100 * (sr * 0.4124 + sg * 0.3576 + sb * 0.1805);
-        outXyz[1] = 100 * (sr * 0.2126 + sg * 0.7152 + sb * 0.0722);
-        outXyz[2] = 100 * (sr * 0.0193 + sg * 0.1192 + sb * 0.9505);
-    }
-
-    /**
-     * Converts a color from CIE XYZ to CIE Lab representation.
-     *
-     * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
-     * 2° Standard Observer (1931).</p>
-     *
-     * <ul>
-     * <li>outLab[0] is L [0 ...1)</li>
-     * <li>outLab[1] is a [-128...127)</li>
-     * <li>outLab[2] is b [-128...127)</li>
-     * </ul>
-     *
-     * @param x      X component value [0...95.047)
-     * @param y      Y component value [0...100)
-     * @param z      Z component value [0...108.883)
-     * @param outLab 3-element array which holds the resulting Lab components
-     */
-    public static void XYZToLAB(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
-            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
-            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z,
-            @NonNull double[] outLab) {
-        if (outLab.length != 3) {
-            throw new IllegalArgumentException("outLab must have a length of 3.");
-        }
-        x = pivotXyzComponent(x / XYZ_WHITE_REFERENCE_X);
-        y = pivotXyzComponent(y / XYZ_WHITE_REFERENCE_Y);
-        z = pivotXyzComponent(z / XYZ_WHITE_REFERENCE_Z);
-        outLab[0] = Math.max(0, 116 * y - 16);
-        outLab[1] = 500 * (x - y);
-        outLab[2] = 200 * (y - z);
-    }
-
-    /**
-     * Converts a color from CIE Lab to CIE XYZ representation.
-     *
-     * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
-     * 2° Standard Observer (1931).</p>
-     *
-     * <ul>
-     * <li>outXyz[0] is X [0 ...95.047)</li>
-     * <li>outXyz[1] is Y [0...100)</li>
-     * <li>outXyz[2] is Z [0...108.883)</li>
-     * </ul>
-     *
-     * @param l      L component value [0...100)
-     * @param a      A component value [-128...127)
-     * @param b      B component value [-128...127)
-     * @param outXyz 3-element array which holds the resulting XYZ components
-     */
-    public static void LABToXYZ(@FloatRange(from = 0f, to = 100) final double l,
-            @FloatRange(from = -128, to = 127) final double a,
-            @FloatRange(from = -128, to = 127) final double b,
-            @NonNull double[] outXyz) {
-        final double fy = (l + 16) / 116;
-        final double fx = a / 500 + fy;
-        final double fz = fy - b / 200;
-
-        double tmp = Math.pow(fx, 3);
-        final double xr = tmp > XYZ_EPSILON ? tmp : (116 * fx - 16) / XYZ_KAPPA;
-        final double yr = l > XYZ_KAPPA * XYZ_EPSILON ? Math.pow(fy, 3) : l / XYZ_KAPPA;
-
-        tmp = Math.pow(fz, 3);
-        final double zr = tmp > XYZ_EPSILON ? tmp : (116 * fz - 16) / XYZ_KAPPA;
-
-        outXyz[0] = xr * XYZ_WHITE_REFERENCE_X;
-        outXyz[1] = yr * XYZ_WHITE_REFERENCE_Y;
-        outXyz[2] = zr * XYZ_WHITE_REFERENCE_Z;
-    }
-
-    /**
-     * Converts a color from CIE XYZ to its RGB representation.
-     *
-     * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
-     * 2° Standard Observer (1931).</p>
-     *
-     * @param x X component value [0...95.047)
-     * @param y Y component value [0...100)
-     * @param z Z component value [0...108.883)
-     * @return int containing the RGB representation
-     */
-    @ColorInt
-    public static int XYZToColor(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
-            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
-            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z) {
-        double r = (x * 3.2406 + y * -1.5372 + z * -0.4986) / 100;
-        double g = (x * -0.9689 + y * 1.8758 + z * 0.0415) / 100;
-        double b = (x * 0.0557 + y * -0.2040 + z * 1.0570) / 100;
-
-        r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r;
-        g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g;
-        b = b > 0.0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - 0.055 : 12.92 * b;
-
-        return Color.rgb(
-                constrain((int) Math.round(r * 255), 0, 255),
-                constrain((int) Math.round(g * 255), 0, 255),
-                constrain((int) Math.round(b * 255), 0, 255));
-    }
-
-    /**
-     * Converts a color from CIE Lab to its RGB representation.
-     *
-     * @param l L component value [0...100]
-     * @param a A component value [-128...127]
-     * @param b B component value [-128...127]
-     * @return int containing the RGB representation
-     */
-    @ColorInt
-    public static int LABToColor(@FloatRange(from = 0f, to = 100) final double l,
-            @FloatRange(from = -128, to = 127) final double a,
-            @FloatRange(from = -128, to = 127) final double b) {
-        final double[] result = getTempDouble3Array();
-        LABToXYZ(l, a, b, result);
-        return XYZToColor(result[0], result[1], result[2]);
-    }
-
-    /**
-     * Returns the euclidean distance between two LAB colors.
-     */
-    public static double distanceEuclidean(@NonNull double[] labX, @NonNull double[] labY) {
-        return Math.sqrt(Math.pow(labX[0] - labY[0], 2)
-                + Math.pow(labX[1] - labY[1], 2)
-                + Math.pow(labX[2] - labY[2], 2));
-    }
-
-    private static float constrain(float amount, float low, float high) {
-        return amount < low ? low : (amount > high ? high : amount);
-    }
-
-    private static int constrain(int amount, int low, int high) {
-        return amount < low ? low : (amount > high ? high : amount);
-    }
-
-    private static double pivotXyzComponent(double component) {
-        return component > XYZ_EPSILON
-                ? Math.pow(component, 1 / 3.0)
-                : (XYZ_KAPPA * component + 16) / 116;
-    }
-
-    /**
-     * Blend between two ARGB colors using the given ratio.
-     *
-     * <p>A blend ratio of 0.0 will result in {@code color1}, 0.5 will give an even blend,
-     * 1.0 will result in {@code color2}.</p>
-     *
-     * @param color1 the first ARGB color
-     * @param color2 the second ARGB color
-     * @param ratio  the blend ratio of {@code color1} to {@code color2}
-     */
-    @ColorInt
-    public static int blendARGB(@ColorInt int color1, @ColorInt int color2,
-            @FloatRange(from = 0.0, to = 1.0) float ratio) {
-        final float inverseRatio = 1 - ratio;
-        float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio;
-        float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio;
-        float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio;
-        float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio;
-        return Color.argb((int) a, (int) r, (int) g, (int) b);
-    }
-
-    /**
-     * Blend between {@code hsl1} and {@code hsl2} using the given ratio. This will interpolate
-     * the hue using the shortest angle.
-     *
-     * <p>A blend ratio of 0.0 will result in {@code hsl1}, 0.5 will give an even blend,
-     * 1.0 will result in {@code hsl2}.</p>
-     *
-     * @param hsl1      3-element array which holds the first HSL color
-     * @param hsl2      3-element array which holds the second HSL color
-     * @param ratio     the blend ratio of {@code hsl1} to {@code hsl2}
-     * @param outResult 3-element array which holds the resulting HSL components
-     */
-    public static void blendHSL(@NonNull float[] hsl1, @NonNull float[] hsl2,
-            @FloatRange(from = 0.0, to = 1.0) float ratio, @NonNull float[] outResult) {
-        if (outResult.length != 3) {
-            throw new IllegalArgumentException("result must have a length of 3.");
-        }
-        final float inverseRatio = 1 - ratio;
-        // Since hue is circular we will need to interpolate carefully
-        outResult[0] = circularInterpolate(hsl1[0], hsl2[0], ratio);
-        outResult[1] = hsl1[1] * inverseRatio + hsl2[1] * ratio;
-        outResult[2] = hsl1[2] * inverseRatio + hsl2[2] * ratio;
-    }
-
-    /**
-     * Blend between two CIE-LAB colors using the given ratio.
-     *
-     * <p>A blend ratio of 0.0 will result in {@code lab1}, 0.5 will give an even blend,
-     * 1.0 will result in {@code lab2}.</p>
-     *
-     * @param lab1      3-element array which holds the first LAB color
-     * @param lab2      3-element array which holds the second LAB color
-     * @param ratio     the blend ratio of {@code lab1} to {@code lab2}
-     * @param outResult 3-element array which holds the resulting LAB components
-     */
-    public static void blendLAB(@NonNull double[] lab1, @NonNull double[] lab2,
-            @FloatRange(from = 0.0, to = 1.0) double ratio, @NonNull double[] outResult) {
-        if (outResult.length != 3) {
-            throw new IllegalArgumentException("outResult must have a length of 3.");
-        }
-        final double inverseRatio = 1 - ratio;
-        outResult[0] = lab1[0] * inverseRatio + lab2[0] * ratio;
-        outResult[1] = lab1[1] * inverseRatio + lab2[1] * ratio;
-        outResult[2] = lab1[2] * inverseRatio + lab2[2] * ratio;
-    }
-
-    @VisibleForTesting
-    static float circularInterpolate(float a, float b, float f) {
-        if (Math.abs(b - a) > 180) {
-            if (b > a) {
-                a += 360;
-            } else {
-                b += 360;
-            }
-        }
-        return (a + ((b - a) * f)) % 360;
-    }
-
-    private static double[] getTempDouble3Array() {
-        double[] result = TEMP_ARRAY.get();
-        if (result == null) {
-            result = new double[3];
-            TEMP_ARRAY.set(result);
-        }
-        return result;
-    }
-
-}
diff --git a/android/support/v4/graphics/PaintCompat.java b/android/support/v4/graphics/PaintCompat.java
deleted file mode 100644
index b651fe6..0000000
--- a/android/support/v4/graphics/PaintCompat.java
+++ /dev/null
@@ -1,119 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.v4.util.Pair;
-
-/**
- * Helper for accessing features in {@link Paint}.
- */
-public final class PaintCompat {
-    // 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<>();
-
-    /**
-     * Determine whether the typeface set on the paint has a glyph supporting the
-     * string in a backwards compatible way.
-     *
-     * @param paint the paint instance to check
-     * @param string the string to test whether there is glyph support
-     * @return true if the typeface set on the given paint has a glyph for the string
-     */
-    public static boolean hasGlyph(@NonNull Paint paint, @NonNull String string) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return paint.hasGlyph(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;
-    }
-
-    private PaintCompat() {}
-}
diff --git a/android/support/v4/graphics/PathParser.java b/android/support/v4/graphics/PathParser.java
deleted file mode 100644
index 3ee85cc..0000000
--- a/android/support/v4/graphics/PathParser.java
+++ /dev/null
@@ -1,760 +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 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/android/support/v4/graphics/TypefaceCompat.java b/android/support/v4/graphics/TypefaceCompat.java
deleted file mode 100644
index b763101..0000000
--- a/android/support/v4/graphics/TypefaceCompat.java
+++ /dev/null
@@ -1,171 +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 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.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.FontResourcesParserCompat;
-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.content.res.ResourcesCompat;
-import android.support.v4.os.BuildCompat;
-import android.support.v4.provider.FontsContractCompat;
-import android.support.v4.provider.FontsContractCompat.FontInfo;
-import android.support.v4.util.LruCache;
-/**
- * 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 (BuildCompat.isAtLeastP()) {
-            sTypefaceCompatImpl = new TypefaceCompatApi28Impl();
-        } else 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.
-     */
-    @Nullable
-    public static Typeface findFromCache(@NonNull 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.
-     */
-    @Nullable
-    public static Typeface createFromResourcesFamilyXml(
-            @NonNull Context context, @NonNull FamilyResourceEntry entry,
-            @NonNull Resources resources, int id, int style,
-            @Nullable ResourcesCompat.FontCallback fontCallback, @Nullable Handler handler,
-            boolean isRequestFromLayoutInflator) {
-        Typeface typeface;
-        if (entry instanceof ProviderResourceEntry) {
-            ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
-            final boolean isBlocking = isRequestFromLayoutInflator
-                    ? providerEntry.getFetchStrategy()
-                    == FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING
-                    : fontCallback == null;
-            final int timeout = isRequestFromLayoutInflator ? providerEntry.getTimeout()
-                    : FontResourcesParserCompat.INFINITE_TIMEOUT_VALUE;
-            typeface = FontsContractCompat.getFontSync(context, providerEntry.getRequest(),
-                    fontCallback, handler, isBlocking, timeout, style);
-        } else {
-            typeface = sTypefaceCompatImpl.createFromFontFamilyFilesResourceEntry(
-                    context, (FontFamilyFilesResourceEntry) entry, resources, style);
-            if (fontCallback != null) {
-                if (typeface != null) {
-                    fontCallback.callbackSuccessAsync(typeface, handler);
-                } else {
-                    fontCallback.callbackFailAsync(
-                            FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR,
-                            handler);
-                }
-            }
-        }
-        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(
-            @NonNull Context context, @NonNull Resources resources, int id, String path,
-            int style) {
-        Typeface typeface = sTypefaceCompatImpl.createFromResourcesFontFile(
-                context, resources, id, path, style);
-        if (typeface != null) {
-            final String resourceUid = createResourceUid(resources, id, style);
-            sTypefaceCache.put(resourceUid, typeface);
-        }
-        return typeface;
-    }
-
-    /**
-     * Create a Typeface from a given FontInfo list and a map that matches them to ByteBuffers.
-     */
-    @Nullable
-    public static Typeface createFromFontInfo(@NonNull Context context,
-            @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
-        return sTypefaceCompatImpl.createFromFontInfo(context, cancellationSignal, fonts, style);
-    }
-}
diff --git a/android/support/v4/graphics/TypefaceCompatApi21Impl.java b/android/support/v4/graphics/TypefaceCompatApi21Impl.java
deleted file mode 100644
index a742004..0000000
--- a/android/support/v4/graphics/TypefaceCompatApi21Impl.java
+++ /dev/null
@@ -1,85 +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 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/android/support/v4/graphics/TypefaceCompatApi24Impl.java b/android/support/v4/graphics/TypefaceCompatApi24Impl.java
deleted file mode 100644
index a8c1988..0000000
--- a/android/support/v4/graphics/TypefaceCompatApi24Impl.java
+++ /dev/null
@@ -1,168 +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 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;
-            }
-        }
-        final Typeface typeface = createFromFamiliesWithDefault(family);
-        return Typeface.create(typeface, style);
-    }
-
-    @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());
-            if (buffer == null) {
-                return null;
-            }
-            if (!addFontWeightStyle(family, buffer, e.getTtcIndex(), e.getWeight(), e.isItalic())) {
-                return null;
-            }
-        }
-        return createFromFamiliesWithDefault(family);
-    }
-}
diff --git a/android/support/v4/graphics/TypefaceCompatApi26Impl.java b/android/support/v4/graphics/TypefaceCompatApi26Impl.java
deleted file mode 100644
index 955284e..0000000
--- a/android/support/v4/graphics/TypefaceCompatApi26Impl.java
+++ /dev/null
@@ -1,341 +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 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 int RESOLVE_BY_FONT_TABLE = -1;
-    private static final String DEFAULT_FAMILY = "sans-serif";
-
-    protected final Class mFontFamily;
-    protected final Constructor mFontFamilyCtor;
-    protected final Method mAddFontFromAssetManager;
-    protected final Method mAddFontFromBuffer;
-    protected final Method mFreeze;
-    protected final Method mAbortCreation;
-    protected final Method mCreateFromFamiliesWithDefault;
-
-    public TypefaceCompatApi26Impl() {
-        Class fontFamily;
-        Constructor fontFamilyCtor;
-        Method addFontFromAssetManager;
-        Method addFontFromBuffer;
-        Method freeze;
-        Method abortCreation;
-        Method createFromFamiliesWithDefault;
-        try {
-            fontFamily = obtainFontFamily();
-            fontFamilyCtor = obtainFontFamilyCtor(fontFamily);
-            addFontFromAssetManager = obtainAddFontFromAssetManagerMethod(fontFamily);
-            addFontFromBuffer = obtainAddFontFromBufferMethod(fontFamily);
-            freeze = obtainFreezeMethod(fontFamily);
-            abortCreation = obtainAbortCreationMethod(fontFamily);
-            createFromFamiliesWithDefault = obtainCreateFromFamiliesWithDefaultMethod(fontFamily);
-        } catch (ClassNotFoundException | NoSuchMethodException e) {
-            Log.e(TAG, "Unable to collect necessary methods for class " + e.getClass().getName(),
-                    e);
-            fontFamily = null;
-            fontFamilyCtor = null;
-            addFontFromAssetManager = null;
-            addFontFromBuffer = null;
-            freeze = null;
-            abortCreation = null;
-            createFromFamiliesWithDefault = null;
-        }
-        mFontFamily = fontFamily;
-        mFontFamilyCtor = fontFamilyCtor;
-        mAddFontFromAssetManager = addFontFromAssetManager;
-        mAddFontFromBuffer = addFontFromBuffer;
-        mFreeze = freeze;
-        mAbortCreation = abortCreation;
-        mCreateFromFamiliesWithDefault = createFromFamiliesWithDefault;
-    }
-
-    /**
-     * Returns true if all the necessary methods were found.
-     */
-    private boolean isFontFamilyPrivateAPIAvailable() {
-        if (mAddFontFromAssetManager == null) {
-            Log.w(TAG, "Unable to collect necessary private methods. "
-                    + "Fallback to legacy implementation.");
-        }
-        return mAddFontFromAssetManager != null;
-    }
-
-    /**
-     * Create a new FontFamily instance
-     */
-    private Object newFamily() {
-        try {
-            return mFontFamilyCtor.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 boolean addFontFromAssetManager(Context context, Object family, String fileName,
-            int ttcIndex, int weight, int style, @Nullable FontVariationAxis[] axes) {
-        try {
-            final Boolean result = (Boolean) mAddFontFromAssetManager.invoke(family,
-                    context.getAssets(), fileName, 0 /* cookie */, false /* isAsset */, ttcIndex,
-                    weight, style, 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 boolean addFontFromBuffer(Object family, ByteBuffer buffer,
-            int ttcIndex, int weight, int style) {
-        try {
-            final Boolean result = (Boolean) mAddFontFromBuffer.invoke(family,
-                    buffer, ttcIndex, null /* axes */, weight, style);
-            return result.booleanValue();
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Call method Typeface#createFromFamiliesWithDefault(
-     *      FontFamily[] families, int weight, int italic)
-     */
-    protected Typeface createFromFamiliesWithDefault(Object family) {
-        try {
-            Object familyArray = Array.newInstance(mFontFamily, 1);
-            Array.set(familyArray, 0, family);
-            return (Typeface) mCreateFromFamiliesWithDefault.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 boolean freeze(Object family) {
-        try {
-            Boolean result = (Boolean) mFreeze.invoke(family);
-            return result.booleanValue();
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Call FontFamily#abortCreation()
-     */
-    private void abortCreation(Object family) {
-        try {
-            mAbortCreation.invoke(family);
-        } 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()) {
-            if (!addFontFromAssetManager(context, fontFamily, fontFile.getFileName(),
-                    fontFile.getTtcIndex(), fontFile.getWeight(), fontFile.isItalic() ? 1 : 0,
-                    FontVariationAxis.fromFontVariationSettings(fontFile.getVariationSettings()))) {
-                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)) {
-                if (pfd == null) {
-                    return null;
-                }
-                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;
-        }
-        final Typeface typeface = createFromFamiliesWithDefault(fontFamily);
-        return Typeface.create(typeface, 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) {
-        if (!isFontFamilyPrivateAPIAvailable()) {
-            return 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 */, null /* axes */)) {
-            abortCreation(fontFamily);
-            return null;
-        }
-        if (!freeze(fontFamily)) {
-            return null;
-        }
-        return createFromFamiliesWithDefault(fontFamily);
-    }
-
-    // The following getters retrieve by reflection the Typeface methods, belonging to the
-    // framework code, which will be invoked. Since the definitions of these methods can change
-    // across different API versions, inheriting classes should override these getters in order to
-    // reflect the method definitions in the API versions they represent.
-    //===========================================================================================
-    protected Class obtainFontFamily() throws ClassNotFoundException {
-        return Class.forName(FONT_FAMILY_CLASS);
-    }
-
-    protected Constructor obtainFontFamilyCtor(Class fontFamily) throws NoSuchMethodException {
-        return fontFamily.getConstructor();
-    }
-
-    protected Method obtainAddFontFromAssetManagerMethod(Class fontFamily)
-            throws NoSuchMethodException {
-        return fontFamily.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
-                AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
-                Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
-    }
-
-    protected Method obtainAddFontFromBufferMethod(Class fontFamily) throws NoSuchMethodException {
-        return fontFamily.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
-                ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
-                Integer.TYPE);
-    }
-
-    protected Method obtainFreezeMethod(Class fontFamily) throws NoSuchMethodException {
-        return fontFamily.getMethod(FREEZE_METHOD);
-    }
-
-    protected Method obtainAbortCreationMethod(Class fontFamily) throws NoSuchMethodException {
-        return fontFamily.getMethod(ABORT_CREATION_METHOD);
-    }
-
-    protected Method obtainCreateFromFamiliesWithDefaultMethod(Class fontFamily)
-            throws NoSuchMethodException {
-        Object familyArray = Array.newInstance(fontFamily, 1);
-        Method m =  Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
-                familyArray.getClass(), Integer.TYPE, Integer.TYPE);
-        m.setAccessible(true);
-        return m;
-    }
-}
diff --git a/android/support/v4/graphics/TypefaceCompatApi28Impl.java b/android/support/v4/graphics/TypefaceCompatApi28Impl.java
deleted file mode 100644
index baa2ce6..0000000
--- a/android/support/v4/graphics/TypefaceCompatApi28Impl.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.graphics;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.Typeface;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Implementation of the Typeface compat methods for API 28 and above.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-@RequiresApi(28)
-public class TypefaceCompatApi28Impl extends TypefaceCompatApi26Impl {
-    private static final String TAG = "TypefaceCompatApi28Impl";
-
-    private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
-            "createFromFamiliesWithDefault";
-    private static final int RESOLVE_BY_FONT_TABLE = -1;
-    private static final String DEFAULT_FAMILY = "sans-serif";
-
-    /**
-     * Call method Typeface#createFromFamiliesWithDefault(
-     *      FontFamily[] families, String fallbackName, int weight, int italic)
-     */
-    @Override
-    protected Typeface createFromFamiliesWithDefault(Object family) {
-        try {
-            Object familyArray = Array.newInstance(mFontFamily, 1);
-            Array.set(familyArray, 0, family);
-            return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,
-                    familyArray, DEFAULT_FAMILY, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-        } catch (IllegalAccessException | InvocationTargetException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    protected Method obtainCreateFromFamiliesWithDefaultMethod(Class fontFamily)
-            throws NoSuchMethodException {
-        Object familyArray = Array.newInstance(fontFamily, 1);
-        Method m =  Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
-                familyArray.getClass(), String.class, Integer.TYPE, Integer.TYPE);
-        m.setAccessible(true);
-        return m;
-    }
-}
diff --git a/android/support/v4/graphics/TypefaceCompatBaseImpl.java b/android/support/v4/graphics/TypefaceCompatBaseImpl.java
deleted file mode 100644
index 8cfbd5d..0000000
--- a/android/support/v4/graphics/TypefaceCompatBaseImpl.java
+++ /dev/null
@@ -1,176 +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 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/android/support/v4/graphics/TypefaceCompatUtil.java b/android/support/v4/graphics/TypefaceCompatUtil.java
deleted file mode 100644
index c524f82..0000000
--- a/android/support/v4/graphics/TypefaceCompatUtil.java
+++ /dev/null
@@ -1,173 +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 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.Nullable;
-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.
-     */
-    @Nullable
-    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.
-     */
-    @Nullable
-    @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.
-     */
-    @Nullable
-    @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)) {
-            if (pfd == null) {
-                return null;
-            }
-            try (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.
-     */
-    @Nullable
-    @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/android/support/v4/graphics/drawable/DrawableCompat.java b/android/support/v4/graphics/drawable/DrawableCompat.java
deleted file mode 100644
index f15354e..0000000
--- a/android/support/v4/graphics/drawable/DrawableCompat.java
+++ /dev/null
@@ -1,424 +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.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.os.Build;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-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}.
- */
-public final class DrawableCompat {
-    private static final String TAG = "DrawableCompat";
-
-    private static Method sSetLayoutDirectionMethod;
-    private static boolean sSetLayoutDirectionMethodFetched;
-
-    private static Method sGetLayoutDirectionMethod;
-    private static boolean sGetLayoutDirectionMethodFetched;
-
-    /**
-     * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     *
-     * @deprecated Use {@link Drawable#jumpToCurrentState()} directly.
-     */
-    @Deprecated
-    public static void jumpToCurrentState(@NonNull Drawable drawable) {
-        drawable.jumpToCurrentState();
-    }
-
-    /**
-     * Set whether this Drawable is automatically mirrored when its layout
-     * direction is RTL (right-to left). See
-     * {@link android.util.LayoutDirection}.
-     * <p>
-     * If running on a pre-{@link android.os.Build.VERSION_CODES#KITKAT} device
-     * this method does nothing.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     * @param mirrored Set to true if the Drawable should be mirrored, false if
-     *            not.
-     */
-    public static void setAutoMirrored(@NonNull Drawable drawable, boolean mirrored) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            drawable.setAutoMirrored(mirrored);
-        }
-    }
-
-    /**
-     * Tells if this Drawable will be automatically mirrored when its layout
-     * direction is RTL right-to-left. See {@link android.util.LayoutDirection}.
-     * <p>
-     * If running on a pre-{@link android.os.Build.VERSION_CODES#KITKAT} device
-     * this method returns false.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     * @return boolean Returns true if this Drawable will be automatically
-     *         mirrored.
-     */
-    public static boolean isAutoMirrored(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return drawable.isAutoMirrored();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Specifies the hotspot's location within the drawable.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     * @param x The X coordinate of the center of the hotspot
-     * @param y The Y coordinate of the center of the hotspot
-     */
-    public static void setHotspot(@NonNull Drawable drawable, float x, float y) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.setHotspot(x, y);
-        }
-    }
-
-    /**
-     * Sets the bounds to which the hotspot is constrained, if they should be
-     * different from the drawable bounds.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     */
-    public static void setHotspotBounds(@NonNull Drawable drawable, int left, int top,
-            int right, int bottom) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.setHotspotBounds(left, top, right, bottom);
-        }
-    }
-
-    /**
-     * Specifies a tint for {@code drawable}.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     * @param tint     Color to use for tinting this drawable
-     */
-    public static void setTint(@NonNull Drawable drawable, @ColorInt int tint) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.setTint(tint);
-        } else if (drawable instanceof TintAwareDrawable) {
-            ((TintAwareDrawable) drawable).setTint(tint);
-        }
-    }
-
-    /**
-     * Specifies a tint for {@code drawable} as a color state list.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     * @param tint     Color state list to use for tinting this drawable, or null to clear the tint
-     */
-    public static void setTintList(@NonNull Drawable drawable, @Nullable ColorStateList tint) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.setTintList(tint);
-        } else if (drawable instanceof TintAwareDrawable) {
-            ((TintAwareDrawable) drawable).setTintList(tint);
-        }
-    }
-
-    /**
-     * Specifies a tint blending mode for {@code drawable}.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     * @param tintMode A Porter-Duff blending mode
-     */
-    public static void setTintMode(@NonNull Drawable drawable, @NonNull PorterDuff.Mode tintMode) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.setTintMode(tintMode);
-        } else if (drawable instanceof TintAwareDrawable) {
-            ((TintAwareDrawable) drawable).setTintMode(tintMode);
-        }
-    }
-
-    /**
-     * Get the alpha value of the {@code drawable}.
-     * 0 means fully transparent, 255 means fully opaque.
-     *
-     * @param drawable The Drawable against which to invoke the method.
-     */
-    public static int getAlpha(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return drawable.getAlpha();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Applies the specified theme to this Drawable and its children.
-     */
-    public static void applyTheme(@NonNull Drawable drawable, @NonNull Resources.Theme theme) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.applyTheme(theme);
-        }
-    }
-
-    /**
-     * Whether a theme can be applied to this Drawable and its children.
-     */
-    public static boolean canApplyTheme(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return drawable.canApplyTheme();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the current color filter, or {@code null} if none set.
-     *
-     * @return the current color filter, or {@code null} if none set
-     */
-    public static ColorFilter getColorFilter(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return drawable.getColorFilter();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Removes the color filter from the given drawable.
-     */
-    public static void clearColorFilter(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            // We can use clearColorFilter() safely on M+
-            drawable.clearColorFilter();
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            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 WrappedDrawable) {
-                clearColorFilter(((WrappedDrawable) 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);
-                        }
-                    }
-                }
-            }
-        } else {
-            drawable.clearColorFilter();
-        }
-    }
-
-    /**
-     * Inflate this Drawable from an XML resource optionally styled by a theme.
-     *
-     * @param res Resources used to resolve attribute values
-     * @param parser XML parser from which to inflate this Drawable
-     * @param attrs Base set of attribute values
-     * @param theme Theme to apply, may be null
-     * @throws XmlPullParserException
-     * @throws IOException
-     */
-    public static void inflate(@NonNull Drawable drawable, @NonNull Resources res,
-            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
-            @Nullable Resources.Theme theme)
-            throws XmlPullParserException, IOException {
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawable.inflate(res, parser, attrs, theme);
-        } else {
-            drawable.inflate(res, parser, attrs);
-        }
-    }
-
-    /**
-     * Potentially wrap {@code drawable} so that it may be used for tinting across the
-     * different API levels, via the tinting methods in this class.
-     *
-     * <p>If the given drawable is wrapped, we will copy over certain state over to the wrapped
-     * drawable, such as its bounds, level, visibility and state.</p>
-     *
-     * <p>You must use the result of this call. If the given drawable is being used by a view
-     * (as its background for instance), you must replace the original drawable with
-     * the result of this call:</p>
-     *
-     * <pre>
-     * Drawable bg = DrawableCompat.wrap(view.getBackground());
-     * // Need to set the background with the wrapped drawable
-     * view.setBackground(bg);
-     *
-     * // You can now tint the drawable
-     * DrawableCompat.setTint(bg, ...);
-     * </pre>
-     *
-     * <p>If you need to get hold of the original {@link android.graphics.drawable.Drawable} again,
-     * you can use the value returned from {@link #unwrap(Drawable)}.</p>
-     *
-     * @param drawable The Drawable to process
-     * @return A drawable capable of being tinted across all API levels.
-     *
-     * @see #setTint(Drawable, int)
-     * @see #setTintList(Drawable, ColorStateList)
-     * @see #setTintMode(Drawable, PorterDuff.Mode)
-     * @see #unwrap(Drawable)
-     */
-    public static Drawable wrap(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return drawable;
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            if (!(drawable instanceof TintAwareDrawable)) {
-                return new WrappedDrawableApi21(drawable);
-            }
-            return drawable;
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            if (!(drawable instanceof TintAwareDrawable)) {
-                return new WrappedDrawableApi19(drawable);
-            }
-            return drawable;
-        } else {
-            if (!(drawable instanceof TintAwareDrawable)) {
-                return new WrappedDrawableApi14(drawable);
-            }
-            return drawable;
-        }
-    }
-
-    /**
-     * Unwrap {@code drawable} if it is the result of a call to {@link #wrap(Drawable)}. If
-     * the {@code drawable} is not the result of a call to {@link #wrap(Drawable)} then
-     * {@code drawable} is returned as-is.
-     *
-     * @param drawable The drawable to unwrap
-     * @return the unwrapped {@link Drawable} or {@code drawable} if it hasn't been wrapped.
-     *
-     * @see #wrap(Drawable)
-     */
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    public static <T extends Drawable> T unwrap(@NonNull Drawable drawable) {
-        if (drawable instanceof WrappedDrawable) {
-            return (T) ((WrappedDrawable) drawable).getWrappedDrawable();
-        }
-        return (T) drawable;
-    }
-
-    /**
-     * Set the layout direction for this drawable. Should be a resolved
-     * layout direction, as the Drawable has no capacity to do the resolution on
-     * its own.
-     *
-     * @param layoutDirection the resolved layout direction for the drawable,
-     *                        either {@link ViewCompat#LAYOUT_DIRECTION_LTR}
-     *                        or {@link ViewCompat#LAYOUT_DIRECTION_RTL}
-     * @return {@code true} if the layout direction change has caused the
-     *         appearance of the drawable to change such that it needs to be
-     *         re-drawn, {@code false} otherwise
-     * @see #getLayoutDirection(Drawable)
-     */
-    public static boolean setLayoutDirection(@NonNull Drawable drawable, int layoutDirection) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return drawable.setLayoutDirection(layoutDirection);
-        } else if (Build.VERSION.SDK_INT >= 17) {
-            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;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the resolved layout direction for this Drawable.
-     *
-     * @return One of {@link ViewCompat#LAYOUT_DIRECTION_LTR},
-     *         {@link ViewCompat#LAYOUT_DIRECTION_RTL}
-     * @see #setLayoutDirection(Drawable, int)
-     */
-    public static int getLayoutDirection(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return drawable.getLayoutDirection();
-        } else if (Build.VERSION.SDK_INT >= 17) {
-            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;
-        } else {
-            return ViewCompat.LAYOUT_DIRECTION_LTR;
-        }
-    }
-
-    private DrawableCompat() {}
-}
diff --git a/android/support/v4/graphics/drawable/IconCompat.java b/android/support/v4/graphics/drawable/IconCompat.java
deleted file mode 100644
index dc226c1..0000000
--- a/android/support/v4/graphics/drawable/IconCompat.java
+++ /dev/null
@@ -1,316 +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.drawable;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.ActivityManager;
-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.Drawable;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-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.annotation.VisibleForTesting;
-import android.support.v4.content.ContextCompat;
-
-/**
- * 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;
-    }
-
-    /**
-     * Convert this compat object to {@link Icon} object.
-     *
-     * @return {@link Icon} object
-     */
-    @RequiresApi(23)
-    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, false));
-                }
-            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");
-        }
-    }
-
-    /**
-     * Use {@link #addToShortcutIntent(Intent, Drawable)} instead
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Deprecated
-    public void addToShortcutIntent(@NonNull Intent outIntent) {
-        addToShortcutIntent(outIntent, null);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @SuppressWarnings("deprecation")
-    public void addToShortcutIntent(@NonNull Intent outIntent, @Nullable Drawable badge) {
-        Bitmap icon;
-        switch (mType) {
-            case TYPE_BITMAP:
-                icon = (Bitmap) mObj1;
-                if (badge != null) {
-                    // Do not modify the original icon when applying a badge
-                    icon = icon.copy(icon.getConfig(), true);
-                }
-                break;
-            case TYPE_ADAPTIVE_BITMAP:
-                icon = createLegacyIconFromAdaptiveIcon((Bitmap) mObj1, true);
-                break;
-            case TYPE_RESOURCE:
-                if (badge == null) {
-                    outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
-                            Intent.ShortcutIconResource.fromContext((Context) mObj1, mInt1));
-                    return;
-                } else {
-                    Context context = (Context) mObj1;
-                    Drawable dr = ContextCompat.getDrawable(context, mInt1);
-                    if (dr.getIntrinsicWidth() <= 0 || dr.getIntrinsicHeight() <= 0) {
-                        int size = ((ActivityManager) context.getSystemService(
-                                Context.ACTIVITY_SERVICE)).getLauncherLargeIconSize();
-                        icon = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
-                    } else {
-                        icon = Bitmap.createBitmap(dr.getIntrinsicWidth(), dr.getIntrinsicHeight(),
-                                Bitmap.Config.ARGB_8888);
-                    }
-                    dr.setBounds(0, 0, icon.getWidth(), icon.getHeight());
-                    dr.draw(new Canvas(icon));
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("Icon type not supported for intent shortcuts");
-        }
-        if (badge != null) {
-            // Badge the icon
-            int w = icon.getWidth();
-            int h = icon.getHeight();
-            badge.setBounds(w / 2, h / 2, w, h);
-            badge.draw(new Canvas(icon));
-        }
-        outIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
-    }
-
-    /**
-     * 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.
-     *
-     * @param addShadow set to {@code true} only for legacy shortcuts and {@code false} otherwise
-     */
-    @VisibleForTesting
-    static Bitmap createLegacyIconFromAdaptiveIcon(Bitmap adaptiveIconBitmap, boolean addShadow) {
-        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;
-
-        if (addShadow) {
-            // 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/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java b/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
deleted file mode 100644
index 795126d..0000000
--- a/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
+++ /dev/null
@@ -1,401 +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.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Matrix;
-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.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.util.DisplayMetrics;
-import android.view.Gravity;
-
-/**
- * A Drawable that wraps a bitmap and can be drawn with rounded corners. You can create a
- * RoundedBitmapDrawable from a file path, an input stream, or from a
- * {@link android.graphics.Bitmap} object.
- * <p>
- * Also see the {@link android.graphics.Bitmap} class, which handles the management and
- * transformation of raw bitmap graphics, and should be used when drawing to a
- * {@link android.graphics.Canvas}.
- * </p>
- */
-@RequiresApi(9)
-public abstract class RoundedBitmapDrawable extends Drawable {
-    private static final int DEFAULT_PAINT_FLAGS =
-            Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
-    final Bitmap mBitmap;
-    private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
-    private int mGravity = Gravity.FILL;
-    private final Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
-    private final BitmapShader mBitmapShader;
-    private final Matrix mShaderMatrix = new Matrix();
-    private float mCornerRadius;
-
-    final Rect mDstRect = new Rect();   // Gravity.apply() sets this
-    private final RectF mDstRectF = new RectF();
-
-    private boolean mApplyGravity = true;
-    private boolean mIsCircular;
-
-    // These are scaled to match the target density.
-    private int mBitmapWidth;
-    private int mBitmapHeight;
-
-    /**
-     * Returns the paint used to render this drawable.
-     */
-    @NonNull
-    public final Paint getPaint() {
-        return mPaint;
-    }
-
-    /**
-     * Returns the bitmap used by this drawable to render. May be null.
-     */
-    @Nullable
-    public final Bitmap getBitmap() {
-        return mBitmap;
-    }
-
-    private void computeBitmapSize() {
-        mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity);
-        mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity);
-    }
-
-    /**
-     * Set the density scale at which this drawable will be rendered. This
-     * method assumes the drawable will be rendered at the same density as the
-     * specified canvas.
-     *
-     * @param canvas The Canvas from which the density scale must be obtained.
-     *
-     * @see android.graphics.Bitmap#setDensity(int)
-     * @see android.graphics.Bitmap#getDensity()
-     */
-    public void setTargetDensity(@NonNull Canvas canvas) {
-        setTargetDensity(canvas.getDensity());
-    }
-
-    /**
-     * Set the density scale at which this drawable will be rendered.
-     *
-     * @param metrics The DisplayMetrics indicating the density scale for this drawable.
-     *
-     * @see android.graphics.Bitmap#setDensity(int)
-     * @see android.graphics.Bitmap#getDensity()
-     */
-    public void setTargetDensity(@NonNull DisplayMetrics metrics) {
-        setTargetDensity(metrics.densityDpi);
-    }
-
-    /**
-     * Set the density at which this drawable will be rendered.
-     *
-     * @param density The density scale for this drawable.
-     *
-     * @see android.graphics.Bitmap#setDensity(int)
-     * @see android.graphics.Bitmap#getDensity()
-     */
-    public void setTargetDensity(int density) {
-        if (mTargetDensity != density) {
-            mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
-            if (mBitmap != null) {
-                computeBitmapSize();
-            }
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Get the gravity used to position/stretch the bitmap within its bounds.
-     *
-     * @return the gravity applied to the bitmap
-     *
-     * @see android.view.Gravity
-     */
-    public int getGravity() {
-        return mGravity;
-    }
-
-    /**
-     * Set the gravity used to position/stretch the bitmap within its bounds.
-     *
-     * @param gravity the gravity
-     *
-     * @see android.view.Gravity
-     */
-    public void setGravity(int gravity) {
-        if (mGravity != gravity) {
-            mGravity = gravity;
-            mApplyGravity = true;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Enables or disables the mipmap hint for this drawable's bitmap.
-     * See {@link Bitmap#setHasMipMap(boolean)} for more information.
-     *
-     * If the bitmap is null, or the current API version does not support setting a mipmap hint,
-     * calling this method has no effect.
-     *
-     * @param mipMap True if the bitmap should use mipmaps, false otherwise.
-     *
-     * @see #hasMipMap()
-     */
-    public void setMipMap(boolean mipMap) {
-        throw new UnsupportedOperationException(); // must be overridden in subclasses
-    }
-
-    /**
-     * Indicates whether the mipmap hint is enabled on this drawable's bitmap.
-     *
-     * @return True if the mipmap hint is set, false otherwise. If the bitmap
-     *         is null, this method always returns false.
-     *
-     * @see #setMipMap(boolean)
-     */
-    public boolean hasMipMap() {
-        throw new UnsupportedOperationException(); // must be overridden in subclasses
-    }
-
-    /**
-     * Enables or disables anti-aliasing for this drawable. Anti-aliasing affects
-     * the edges of the bitmap only so it applies only when the drawable is rotated.
-     *
-     * @param aa True if the bitmap should be anti-aliased, false otherwise.
-     *
-     * @see #hasAntiAlias()
-     */
-    public void setAntiAlias(boolean aa) {
-        mPaint.setAntiAlias(aa);
-        invalidateSelf();
-    }
-
-    /**
-     * Indicates whether anti-aliasing is enabled for this drawable.
-     *
-     * @return True if anti-aliasing is enabled, false otherwise.
-     *
-     * @see #setAntiAlias(boolean)
-     */
-    public boolean hasAntiAlias() {
-        return mPaint.isAntiAlias();
-    }
-
-    @Override
-    public void setFilterBitmap(boolean filter) {
-        mPaint.setFilterBitmap(filter);
-        invalidateSelf();
-    }
-
-    @Override
-    public void setDither(boolean dither) {
-        mPaint.setDither(dither);
-        invalidateSelf();
-    }
-
-    void gravityCompatApply(int gravity, int bitmapWidth, int bitmapHeight,
-            Rect bounds, Rect outRect) {
-        throw new UnsupportedOperationException();
-    }
-
-    void updateDstRect() {
-        if (mApplyGravity) {
-            if (mIsCircular) {
-                final int minDimen = Math.min(mBitmapWidth, mBitmapHeight);
-                gravityCompatApply(mGravity, minDimen, minDimen, getBounds(), mDstRect);
-
-                // inset the drawing rectangle to the largest contained square,
-                // so that a circle will be drawn
-                final int minDrawDimen = Math.min(mDstRect.width(), mDstRect.height());
-                final int insetX = Math.max(0, (mDstRect.width() - minDrawDimen) / 2);
-                final int insetY = Math.max(0, (mDstRect.height() - minDrawDimen) / 2);
-                mDstRect.inset(insetX, insetY);
-                mCornerRadius = 0.5f * minDrawDimen;
-            } else {
-                gravityCompatApply(mGravity, mBitmapWidth, mBitmapHeight, getBounds(), mDstRect);
-            }
-            mDstRectF.set(mDstRect);
-
-            if (mBitmapShader != null) {
-                // setup shader matrix
-                mShaderMatrix.setTranslate(mDstRectF.left,mDstRectF.top);
-                mShaderMatrix.preScale(
-                        mDstRectF.width() / mBitmap.getWidth(),
-                        mDstRectF.height() / mBitmap.getHeight());
-                mBitmapShader.setLocalMatrix(mShaderMatrix);
-                mPaint.setShader(mBitmapShader);
-            }
-
-            mApplyGravity = false;
-        }
-    }
-
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        final Bitmap bitmap = mBitmap;
-        if (bitmap == null) {
-            return;
-        }
-
-        updateDstRect();
-        if (mPaint.getShader() == null) {
-            canvas.drawBitmap(bitmap, null, mDstRect, mPaint);
-        } else {
-            canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, mPaint);
-        }
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        final int oldAlpha = mPaint.getAlpha();
-        if (alpha != oldAlpha) {
-            mPaint.setAlpha(alpha);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public int getAlpha() {
-        return mPaint.getAlpha();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        mPaint.setColorFilter(cf);
-        invalidateSelf();
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        return mPaint.getColorFilter();
-    }
-
-    /**
-     * Sets the image shape to circular.
-     * <p>This overwrites any calls made to {@link #setCornerRadius(float)} so far.</p>
-     */
-    public void setCircular(boolean circular) {
-        mIsCircular = circular;
-        mApplyGravity = true;
-        if (circular) {
-            updateCircularCornerRadius();
-            mPaint.setShader(mBitmapShader);
-            invalidateSelf();
-        } else {
-            setCornerRadius(0);
-        }
-    }
-
-    private void updateCircularCornerRadius() {
-        final int minCircularSize = Math.min(mBitmapHeight, mBitmapWidth);
-        mCornerRadius = minCircularSize / 2;
-    }
-
-    /**
-     * @return <code>true</code> if the image is circular, else <code>false</code>.
-     */
-    public boolean isCircular() {
-        return mIsCircular;
-    }
-
-    /**
-     * Sets the corner radius to be applied when drawing the bitmap.
-     */
-    public void setCornerRadius(float cornerRadius) {
-        if (mCornerRadius == cornerRadius) return;
-
-        mIsCircular = false;
-        if (isGreaterThanZero(cornerRadius)) {
-            mPaint.setShader(mBitmapShader);
-        } else {
-            mPaint.setShader(null);
-        }
-
-        mCornerRadius = cornerRadius;
-        invalidateSelf();
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.onBoundsChange(bounds);
-        if (mIsCircular) {
-            updateCircularCornerRadius();
-        }
-        mApplyGravity = true;
-    }
-
-    /**
-     * @return The corner radius applied when drawing the bitmap.
-     */
-    public float getCornerRadius() {
-        return mCornerRadius;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mBitmapWidth;
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mBitmapHeight;
-    }
-
-    @Override
-    public int getOpacity() {
-        if (mGravity != Gravity.FILL || mIsCircular) {
-            return PixelFormat.TRANSLUCENT;
-        }
-        Bitmap bm = mBitmap;
-        return (bm == null
-                || bm.hasAlpha()
-                || mPaint.getAlpha() < 255
-                || isGreaterThanZero(mCornerRadius))
-                ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
-    }
-
-    RoundedBitmapDrawable(Resources res, Bitmap bitmap) {
-        if (res != null) {
-            mTargetDensity = res.getDisplayMetrics().densityDpi;
-        }
-
-        mBitmap = bitmap;
-        if (mBitmap != null) {
-            computeBitmapSize();
-            mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
-        } else {
-            mBitmapWidth = mBitmapHeight = -1;
-            mBitmapShader = null;
-        }
-    }
-
-    private static boolean isGreaterThanZero(float toCompare) {
-        return toCompare > 0.05f;
-    }
-}
diff --git a/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java b/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.java
deleted file mode 100644
index 2aa4345..0000000
--- a/android/support/v4/graphics/drawable/RoundedBitmapDrawable21.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.graphics.drawable;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.support.annotation.RequiresApi;
-import android.view.Gravity;
-import android.view.View;
-
-@RequiresApi(21)
-class RoundedBitmapDrawable21 extends RoundedBitmapDrawable {
-    protected RoundedBitmapDrawable21(Resources res, Bitmap bitmap) {
-        super(res, bitmap);
-    }
-
-    @Override
-    public void getOutline(Outline outline) {
-        updateDstRect();
-        outline.setRoundRect(mDstRect, getCornerRadius());
-    }
-
-    @Override
-    public void setMipMap(boolean mipMap) {
-        if (mBitmap != null) {
-            mBitmap.setHasMipMap(mipMap);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public boolean hasMipMap() {
-        return mBitmap != null && mBitmap.hasMipMap();
-    }
-
-    @Override
-    void gravityCompatApply(int gravity, int bitmapWidth, int bitmapHeight,
-            Rect bounds, Rect outRect) {
-        Gravity.apply(gravity, bitmapWidth, bitmapHeight,
-                bounds, outRect, View.LAYOUT_DIRECTION_LTR);
-    }
-}
diff --git a/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java b/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java
deleted file mode 100644
index 7790055..0000000
--- a/android/support/v4/graphics/drawable/RoundedBitmapDrawableFactory.java
+++ /dev/null
@@ -1,105 +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.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.BitmapCompat;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.util.Log;
-
-import java.io.InputStream;
-
-/**
- * Constructs {@link RoundedBitmapDrawable RoundedBitmapDrawable} objects,
- * either from Bitmaps directly, or from streams and files.
- */
-public final class RoundedBitmapDrawableFactory {
-    private static final String TAG = "RoundedBitmapDrawableFa";
-
-    private static class DefaultRoundedBitmapDrawable extends RoundedBitmapDrawable {
-        DefaultRoundedBitmapDrawable(Resources res, Bitmap bitmap) {
-            super(res, bitmap);
-        }
-
-        @Override
-        public void setMipMap(boolean mipMap) {
-            if (mBitmap != null) {
-                BitmapCompat.setHasMipMap(mBitmap, mipMap);
-                invalidateSelf();
-            }
-        }
-
-        @Override
-        public boolean hasMipMap() {
-            return mBitmap != null && BitmapCompat.hasMipMap(mBitmap);
-        }
-
-        @Override
-        void gravityCompatApply(int gravity, int bitmapWidth, int bitmapHeight,
-                Rect bounds, Rect outRect) {
-            GravityCompat.apply(gravity, bitmapWidth, bitmapHeight,
-                    bounds, outRect, ViewCompat.LAYOUT_DIRECTION_LTR);
-        }
-    }
-
-    /**
-     * Returns a new drawable by creating it from a bitmap, setting initial target density based on
-     * the display metrics of the resources.
-     */
-    @NonNull
-    public static RoundedBitmapDrawable create(@NonNull Resources res, @Nullable Bitmap bitmap) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return new RoundedBitmapDrawable21(res, bitmap);
-        }
-        return new DefaultRoundedBitmapDrawable(res, bitmap);
-    }
-
-    /**
-     * Returns a new drawable, creating it by opening a given file path and decoding the bitmap.
-     */
-    @NonNull
-    public static RoundedBitmapDrawable create(@NonNull Resources res, @NonNull String filepath) {
-        final RoundedBitmapDrawable drawable = create(res, BitmapFactory.decodeFile(filepath));
-        if (drawable.getBitmap() == null) {
-            Log.w(TAG, "RoundedBitmapDrawable cannot decode " + filepath);
-        }
-        return drawable;
-    }
-
-
-    /**
-     * Returns a new drawable, creating it by decoding a bitmap from the given input stream.
-     */
-    @NonNull
-    public static RoundedBitmapDrawable create(@NonNull Resources res, @NonNull InputStream is) {
-        final RoundedBitmapDrawable drawable = create(res, BitmapFactory.decodeStream(is));
-        if (drawable.getBitmap() == null) {
-            Log.w(TAG, "RoundedBitmapDrawable cannot decode " + is);
-        }
-        return drawable;
-    }
-
-    private RoundedBitmapDrawableFactory() {}
-
-}
diff --git a/android/support/v4/graphics/drawable/TintAwareDrawable.java b/android/support/v4/graphics/drawable/TintAwareDrawable.java
deleted file mode 100644
index 81bed7b..0000000
--- a/android/support/v4/graphics/drawable/TintAwareDrawable.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.graphics.drawable;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.support.annotation.ColorInt;
-import android.support.annotation.RestrictTo;
-
-/**
- * Interface which allows a {@link android.graphics.drawable.Drawable} to receive tinting calls
- * from {@code DrawableCompat}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface TintAwareDrawable {
-    void setTint(@ColorInt int tint);
-    void setTintList(ColorStateList tint);
-    void setTintMode(PorterDuff.Mode tintMode);
-}
diff --git a/android/support/v4/graphics/drawable/WrappedDrawable.java b/android/support/v4/graphics/drawable/WrappedDrawable.java
deleted file mode 100644
index 3bd1d68..0000000
--- a/android/support/v4/graphics/drawable/WrappedDrawable.java
+++ /dev/null
@@ -1,34 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-
-/**
- * Interface which allows a {@link android.graphics.drawable.Drawable} to get/set wrapped
- * drawables from {@code DrawableCompat}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface WrappedDrawable {
-    Drawable getWrappedDrawable();
-    void setWrappedDrawable(Drawable drawable);
-}
diff --git a/android/support/v4/graphics/drawable/WrappedDrawableApi14.java b/android/support/v4/graphics/drawable/WrappedDrawableApi14.java
deleted file mode 100644
index d1218bc..0000000
--- a/android/support/v4/graphics/drawable/WrappedDrawableApi14.java
+++ /dev/null
@@ -1,391 +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;
-
-/**
- * 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}.
- */
-class WrappedDrawableApi14 extends Drawable
-        implements Drawable.Callback, WrappedDrawable, 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;
-
-    WrappedDrawableApi14(@NonNull DrawableWrapperState state, @Nullable Resources res) {
-        mState = state;
-        updateLocalState(res);
-    }
-
-    /**
-     * Creates a new wrapper around the specified drawable.
-     *
-     * @param dr the drawable to wrap
-     */
-    WrappedDrawableApi14(@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) {
-            setWrappedDrawable(mState.mDrawableState.newDrawable(res));
-        }
-    }
-
-    @Override
-    public void jumpToCurrentState() {
-        mDrawable.jumpToCurrentState();
-    }
-
-    @Override
-    public void draw(@NonNull 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(@NonNull int[] stateSet) {
-        boolean handled = mDrawable.setState(stateSet);
-        handled = updateTint(stateSet) || handled;
-        return handled;
-    }
-
-    @NonNull
-    @Override
-    public int[] getState() {
-        return mDrawable.getState();
-    }
-
-    @NonNull
-    @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(@NonNull Rect padding) {
-        return mDrawable.getPadding(padding);
-    }
-
-    @Override
-    @Nullable
-    public ConstantState getConstantState() {
-        if (mState != null && mState.canConstantState()) {
-            mState.mChangingConfigurations = getChangingConfigurations();
-            return mState;
-        }
-        return null;
-    }
-
-    @NonNull
-    @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(@NonNull Drawable who) {
-        invalidateSelf();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
-        scheduleSelf(what, when);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void unscheduleDrawable(@NonNull Drawable who, @NonNull 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(@NonNull 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;
-            }
-        }
-
-        @NonNull
-        @Override
-        public Drawable newDrawable() {
-            return newDrawable(null);
-        }
-
-        @NonNull
-        @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);
-        }
-
-        @NonNull
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new WrappedDrawableApi14(this, res);
-        }
-    }
-}
diff --git a/android/support/v4/graphics/drawable/WrappedDrawableApi19.java b/android/support/v4/graphics/drawable/WrappedDrawableApi19.java
deleted file mode 100644
index 7d6b8d8..0000000
--- a/android/support/v4/graphics/drawable/WrappedDrawableApi19.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.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 WrappedDrawableApi19 extends WrappedDrawableApi14 {
-
-    WrappedDrawableApi19(Drawable drawable) {
-        super(drawable);
-    }
-
-    WrappedDrawableApi19(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);
-        }
-
-        @NonNull
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new WrappedDrawableApi19(this, res);
-        }
-    }
-}
diff --git a/android/support/v4/graphics/drawable/WrappedDrawableApi21.java b/android/support/v4/graphics/drawable/WrappedDrawableApi21.java
deleted file mode 100644
index b550742..0000000
--- a/android/support/v4/graphics/drawable/WrappedDrawableApi21.java
+++ /dev/null
@@ -1,167 +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.graphics.drawable.RippleDrawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-
-@RequiresApi(21)
-class WrappedDrawableApi21 extends WrappedDrawableApi19 {
-    private static final String TAG = "WrappedDrawableApi21";
-    private static Method sIsProjectedDrawableMethod;
-
-    WrappedDrawableApi21(Drawable drawable) {
-        super(drawable);
-        findAndCacheIsProjectedDrawableMethod();
-    }
-
-    WrappedDrawableApi21(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(@NonNull Outline outline) {
-        mDrawable.getOutline(outline);
-    }
-
-    @NonNull
-    @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(@NonNull 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;
-    }
-
-    /**
-     * This method is overriding hidden framework method in {@link Drawable}. It is used by the
-     * system and thus it should not be removed.
-     */
-    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);
-        }
-
-        @NonNull
-        @Override
-        public Drawable newDrawable(@Nullable Resources res) {
-            return new WrappedDrawableApi21(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/android/support/v4/hardware/display/DisplayManagerCompat.java b/android/support/v4/hardware/display/DisplayManagerCompat.java
deleted file mode 100644
index 22d39ac..0000000
--- a/android/support/v4/hardware/display/DisplayManagerCompat.java
+++ /dev/null
@@ -1,162 +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.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-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}.
- */
-public abstract class DisplayManagerCompat {
-    private static final WeakHashMap<Context, DisplayManagerCompat> sInstances =
-            new WeakHashMap<Context, DisplayManagerCompat>();
-
-    /**
-     * Display category: Presentation displays.
-     * <p>
-     * This category can be used to identify secondary displays that are suitable for
-     * use as presentation displays.
-     * </p>
-     *
-     * @see android.app.Presentation for information about presenting content
-     * on secondary displays.
-     * @see #getDisplays(String)
-     */
-    public static final String DISPLAY_CATEGORY_PRESENTATION =
-            "android.hardware.display.category.PRESENTATION";
-
-    DisplayManagerCompat() {
-    }
-
-    /**
-     * Gets an instance of the display manager given the context.
-     */
-    @NonNull
-    public static DisplayManagerCompat getInstance(@NonNull Context context) {
-        synchronized (sInstances) {
-            DisplayManagerCompat instance = sInstances.get(context);
-            if (instance == null) {
-                if (Build.VERSION.SDK_INT >= 17) {
-                    instance = new DisplayManagerCompatApi17Impl(context);
-                } else {
-                    instance = new DisplayManagerCompatApi14Impl(context);
-                }
-                sInstances.put(context, instance);
-            }
-            return instance;
-        }
-    }
-
-    /**
-     * Gets information about a logical display.
-     *
-     * The display metrics may be adjusted to provide compatibility
-     * for legacy applications.
-     *
-     * @param displayId The logical display id.
-     * @return The display object, or null if there is no valid display with the given id.
-     */
-    @Nullable
-    public abstract Display getDisplay(int displayId);
-
-    /**
-     * Gets all currently valid logical displays.
-     *
-     * @return An array containing all displays.
-     */
-    @NonNull
-    public abstract Display[] getDisplays();
-
-    /**
-     * Gets all currently valid logical displays of the specified category.
-     * <p>
-     * When there are multiple displays in a category the returned displays are sorted
-     * of preference.  For example, if the requested category is
-     * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays
-     * then the displays are sorted so that the first display in the returned array
-     * is the most preferred presentation display.  The application may simply
-     * use the first display or allow the user to choose.
-     * </p>
-     *
-     * @param category The requested display category or null to return all displays.
-     * @return An array containing all displays sorted by order of preference.
-     *
-     * @see #DISPLAY_CATEGORY_PRESENTATION
-     */
-    @NonNull
-    public abstract Display[] getDisplays(String category);
-
-    private static class DisplayManagerCompatApi14Impl extends DisplayManagerCompat {
-        private final WindowManager mWindowManager;
-
-        DisplayManagerCompatApi14Impl(Context context) {
-            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        }
-
-        @Override
-        public Display getDisplay(int displayId) {
-            Display display = mWindowManager.getDefaultDisplay();
-            if (display.getDisplayId() == displayId) {
-                return display;
-            }
-            return null;
-        }
-
-        @Override
-        public Display[] getDisplays() {
-            return new Display[] { mWindowManager.getDefaultDisplay() };
-        }
-
-        @Override
-        public Display[] getDisplays(String category) {
-            return category == null ? getDisplays() : new Display[0];
-        }
-    }
-
-    @RequiresApi(17)
-    private static class DisplayManagerCompatApi17Impl extends DisplayManagerCompat {
-        private final DisplayManager mDisplayManager;
-
-        DisplayManagerCompatApi17Impl(Context context) {
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
-        }
-
-        @Override
-        public Display getDisplay(int displayId) {
-            return mDisplayManager.getDisplay(displayId);
-        }
-
-        @Override
-        public Display[] getDisplays() {
-            return mDisplayManager.getDisplays();
-        }
-
-        @Override
-        public Display[] getDisplays(String category) {
-            return mDisplayManager.getDisplays(category);
-        }
-    }
-}
diff --git a/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java b/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
deleted file mode 100644
index 6747d11..0000000
--- a/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
+++ /dev/null
@@ -1,292 +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.hardware.fingerprint;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.os.Build;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RequiresPermission;
-import android.support.v4.os.CancellationSignal;
-
-import java.security.Signature;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-
-/**
- * A class that coordinates access to the fingerprint hardware.
- * <p>
- * On platforms before {@link android.os.Build.VERSION_CODES#M}, this class behaves as there would
- * be no fingerprint hardware available.
- */
-public final class FingerprintManagerCompat {
-
-    private final Context mContext;
-
-    /** Get a {@link FingerprintManagerCompat} instance for a provided context. */
-    @NonNull
-    public static FingerprintManagerCompat from(@NonNull Context context) {
-        return new FingerprintManagerCompat(context);
-    }
-
-    private FingerprintManagerCompat(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Determine if there is at least one fingerprint enrolled.
-     *
-     * @return true if at least one fingerprint is enrolled, false otherwise
-     */
-    @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
-    public boolean hasEnrolledFingerprints() {
-        if (Build.VERSION.SDK_INT >= 23) {
-            final FingerprintManager fp = getFingerprintManagerOrNull(mContext);
-            return (fp != null) && fp.hasEnrolledFingerprints();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Determine if fingerprint hardware is present and functional.
-     *
-     * @return true if hardware is present and functional, false otherwise.
-     */
-    @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
-    public boolean isHardwareDetected() {
-        if (Build.VERSION.SDK_INT >= 23) {
-            final FingerprintManager fp = getFingerprintManagerOrNull(mContext);
-            return (fp != null) && fp.isHardwareDetected();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Request authentication of a crypto object. This call warms up the fingerprint hardware
-     * and starts scanning for a fingerprint. It terminates when
-     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
-     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
-     * which point the object is no longer valid. The operation can be canceled by using the
-     * provided cancel object.
-     *
-     * @param crypto object associated with the call or null if none required.
-     * @param flags optional flags; should be 0
-     * @param cancel an object that can be used to cancel authentication
-     * @param callback an object to receive authentication events
-     * @param handler an optional handler for events
-     */
-    @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT)
-    public void authenticate(@Nullable CryptoObject crypto, int flags,
-            @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback,
-            @Nullable Handler handler) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            final FingerprintManager fp = getFingerprintManagerOrNull(mContext);
-            if (fp != null) {
-                android.os.CancellationSignal cancellationSignal = cancel != null
-                        ? (android.os.CancellationSignal) cancel.getCancellationSignalObject()
-                        : null;
-                fp.authenticate(
-                        wrapCryptoObject(crypto),
-                        cancellationSignal,
-                        flags,
-                        wrapCallback(callback),
-                        handler);
-            }
-        }
-    }
-
-    @Nullable
-    @RequiresApi(23)
-    private static FingerprintManager getFingerprintManagerOrNull(@NonNull Context context) {
-        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            return context.getSystemService(FingerprintManager.class);
-        } else {
-            return null;
-        }
-    }
-
-    @RequiresApi(23)
-    private static FingerprintManager.CryptoObject wrapCryptoObject(CryptoObject cryptoObject) {
-        if (cryptoObject == null) {
-            return null;
-        } else if (cryptoObject.getCipher() != null) {
-            return new FingerprintManager.CryptoObject(cryptoObject.getCipher());
-        } else if (cryptoObject.getSignature() != null) {
-            return new FingerprintManager.CryptoObject(cryptoObject.getSignature());
-        } else if (cryptoObject.getMac() != null) {
-            return new FingerprintManager.CryptoObject(cryptoObject.getMac());
-        } else {
-            return null;
-        }
-    }
-
-    @RequiresApi(23)
-    private static CryptoObject unwrapCryptoObject(FingerprintManager.CryptoObject cryptoObject) {
-        if (cryptoObject == null) {
-            return null;
-        } else if (cryptoObject.getCipher() != null) {
-            return new CryptoObject(cryptoObject.getCipher());
-        } else if (cryptoObject.getSignature() != null) {
-            return new CryptoObject(cryptoObject.getSignature());
-        } else if (cryptoObject.getMac() != null) {
-            return new CryptoObject(cryptoObject.getMac());
-        } else {
-            return null;
-        }
-    }
-
-    @RequiresApi(23)
-    private static FingerprintManager.AuthenticationCallback wrapCallback(
-            final AuthenticationCallback callback) {
-        return new FingerprintManager.AuthenticationCallback() {
-            @Override
-            public void onAuthenticationError(int errMsgId, CharSequence errString) {
-                callback.onAuthenticationError(errMsgId, errString);
-            }
-
-            @Override
-            public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
-                callback.onAuthenticationHelp(helpMsgId, helpString);
-            }
-
-            @Override
-            public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
-                callback.onAuthenticationSucceeded(new AuthenticationResult(
-                        unwrapCryptoObject(result.getCryptoObject())));
-            }
-
-            @Override
-            public void onAuthenticationFailed() {
-                callback.onAuthenticationFailed();
-            }
-        };
-    }
-
-    /**
-     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
-     * framework supports {@link Signature} and {@link Cipher} objects.
-     */
-    public static class CryptoObject {
-
-        private final Signature mSignature;
-        private final Cipher mCipher;
-        private final Mac mMac;
-
-        public CryptoObject(@NonNull Signature signature) {
-            mSignature = signature;
-            mCipher = null;
-            mMac = null;
-
-        }
-
-        public CryptoObject(@NonNull Cipher cipher) {
-            mCipher = cipher;
-            mSignature = null;
-            mMac = null;
-        }
-
-        public CryptoObject(@NonNull Mac mac) {
-            mMac = mac;
-            mCipher = null;
-            mSignature = null;
-        }
-
-        /**
-         * Get {@link Signature} object.
-         * @return {@link Signature} object or null if this doesn't contain one.
-         */
-        @Nullable
-        public Signature getSignature() { return mSignature; }
-
-        /**
-         * Get {@link Cipher} object.
-         * @return {@link Cipher} object or null if this doesn't contain one.
-         */
-        @Nullable
-        public Cipher getCipher() { return mCipher; }
-
-        /**
-         * Get {@link Mac} object.
-         * @return {@link Mac} object or null if this doesn't contain one.
-         */
-        @Nullable
-        public Mac getMac() { return mMac; }
-    }
-
-    /**
-     * Container for callback data from {@link FingerprintManagerCompat#authenticate(CryptoObject,
-     *     int, CancellationSignal, AuthenticationCallback, Handler)}.
-     */
-    public static final class AuthenticationResult {
-        private final CryptoObject mCryptoObject;
-
-        public AuthenticationResult(CryptoObject crypto) {
-            mCryptoObject = crypto;
-        }
-
-        /**
-         * Obtain the crypto object associated with this transaction
-         * @return crypto object provided to {@link FingerprintManagerCompat#authenticate(
-         *         CryptoObject, int, CancellationSignal, AuthenticationCallback, Handler)}.
-         */
-        public CryptoObject getCryptoObject() { return mCryptoObject; }
-    }
-
-    /**
-     * Callback structure provided to {@link FingerprintManagerCompat#authenticate(CryptoObject,
-     * int, CancellationSignal, AuthenticationCallback, Handler)}. Users of {@link
-     * FingerprintManagerCompat#authenticate(CryptoObject, int, CancellationSignal,
-     * AuthenticationCallback, Handler) } must provide an implementation of this for listening to
-     * fingerprint events.
-     */
-    public static abstract class AuthenticationCallback {
-        /**
-         * Called when an unrecoverable error has been encountered and the operation is complete.
-         * No further callbacks will be made on this object.
-         * @param errMsgId An integer identifying the error message
-         * @param errString A human-readable error string that can be shown in UI
-         */
-        public void onAuthenticationError(int errMsgId, CharSequence errString) { }
-
-        /**
-         * Called when a recoverable error has been encountered during authentication. The help
-         * string is provided to give the user guidance for what went wrong, such as
-         * "Sensor dirty, please clean it."
-         * @param helpMsgId An integer identifying the error message
-         * @param helpString A human-readable string that can be shown in UI
-         */
-        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }
-
-        /**
-         * Called when a fingerprint is recognized.
-         * @param result An object containing authentication-related data
-         */
-        public void onAuthenticationSucceeded(AuthenticationResult result) { }
-
-        /**
-         * Called when a fingerprint is valid but not recognized.
-         */
-        public void onAuthenticationFailed() { }
-    }
-}
diff --git a/android/support/v4/internal/package-info.java b/android/support/v4/internal/package-info.java
deleted file mode 100644
index d2d2d2f..0000000
--- a/android/support/v4/internal/package-info.java
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-package android.support.v4.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
\ No newline at end of file
diff --git a/android/support/v4/internal/view/SupportMenu.java b/android/support/v4/internal/view/SupportMenu.java
deleted file mode 100644
index c072151..0000000
--- a/android/support/v4/internal/view/SupportMenu.java
+++ /dev/null
@@ -1,68 +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.internal.view;
-
-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.
- *
- * This version extends the one available in the framework to ensures that any necessary
- * elements added in later versions of the framework, are available for all platforms.
- *
- * @see android.view.Menu
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface SupportMenu extends android.view.Menu {
-
-    /**
-     * This is the part of an order integer that the user can provide.
-     */
-    int USER_MASK = 0x0000ffff;
-
-    /**
-     * Bit shift of the user portion of the order integer.
-     */
-    int USER_SHIFT = 0;
-
-    /**
-     * This is the part of an order integer that supplies the category of the item.
-     */
-    int CATEGORY_MASK = 0xffff0000;
-
-    /**
-     * Bit shift of the category portion of the order integer.
-     */
-    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/android/support/v4/internal/view/SupportMenuItem.java b/android/support/v4/internal/view/SupportMenuItem.java
deleted file mode 100644
index e6cdd3d..0000000
--- a/android/support/v4/internal/view/SupportMenuItem.java
+++ /dev/null
@@ -1,378 +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.internal.view;
-
-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.view.MenuItem;
-import android.view.View;
-
-/**
- * Interface for direct access to a previously created menu item.
- *
- * This version extends the one available in the framework to ensures that any necessary
- * elements added in later versions of the framework, are available for all platforms.
- *
- * @see android.view.MenuItem
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface SupportMenuItem extends android.view.MenuItem {
-    /*
-    * These should be kept in sync with attrs.xml enum constants for showAsAction
-    */
-    /**
-     * Never show this item as a button in an Action Bar.
-     */
-    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.
-     */
-    int SHOW_AS_ACTION_IF_ROOM = 1;
-    /**
-     * Always show this item as a button in an Action Bar.
-     * Use sparingly! If too many items are set to always show in the Action Bar it can
-     * 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.
-     */
-    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.
-     */
-    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.
-     */
-    int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8;
-
-    /**
-     * Sets how this item should display in the presence of an Action Bar. The parameter actionEnum
-     * is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or
-     * {@link #SHOW_AS_ACTION_NEVER} should be used, and you may optionally OR the value with {@link
-     * #SHOW_AS_ACTION_WITH_TEXT}. SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as
-     * an action, it should be shown with a text label.
-     *
-     * @param actionEnum How the item should display. One of {@link #SHOW_AS_ACTION_ALWAYS}, {@link
-     *                   #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER}.
-     *                   SHOW_AS_ACTION_NEVER is the default.
-     * @see android.app.ActionBar
-     * @see #setActionView(View)
-     */
-    @Override
-    void setShowAsAction(int actionEnum);
-
-    /**
-     * Sets how this item should display in the presence of an Action Bar.
-     * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS},
-     * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should
-     * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}.
-     * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action,
-     * it should be shown with a text label.
-     *
-     * <p>Note: This method differs from {@link #setShowAsAction(int)} only in that it
-     * returns the current MenuItem instance for call chaining.
-     *
-     * @param actionEnum How the item should display. One of {@link #SHOW_AS_ACTION_ALWAYS}, {@link
-     *                   #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER}.
-     *                   SHOW_AS_ACTION_NEVER is the default.
-     * @return This MenuItem instance for call chaining.
-     * @see android.app.ActionBar
-     * @see #setActionView(View)
-     */
-    @Override
-    MenuItem setShowAsActionFlags(int actionEnum);
-
-    /**
-     * Set an action view for this menu item. An action view will be displayed in place
-     * of an automatically generated menu item element in the UI when this item is shown
-     * as an action within a parent.
-     *
-     * <p><strong>Note:</strong> Setting an action view overrides the action provider
-     * provider set via {@link #setSupportActionProvider(android.support.v4.view.ActionProvider)}. </p>
-     *
-     * @param view View to use for presenting this item to the user.
-     * @return This Item so additional setters can be called.
-     * @see #setShowAsAction(int)
-     */
-    @Override
-    MenuItem setActionView(View view);
-
-    /**
-     * Set an action view for this menu item. An action view will be displayed in place
-     * of an automatically generated menu item element in the UI when this item is shown
-     * as an action within a parent.
-     *
-     * <p><strong>Note:</strong> Setting an action view overrides the action provider
-     * provider set via {@link #setSupportActionProvider(android.support.v4.view.ActionProvider)}. </p>
-     *
-     * @param resId Layout resource to use for presenting this item to the user.
-     * @return This Item so additional setters can be called.
-     * @see #setShowAsAction(int)
-     */
-    @Override
-    MenuItem setActionView(int resId);
-
-    /**
-     * Returns the currently set action view for this menu item.
-     *
-     * @return This item's action view
-     * @see #setActionView(View)
-     * @see #setShowAsAction(int)
-     */
-    @Override
-    View getActionView();
-
-    /**
-     * Sets the {@link android.support.v4.view.ActionProvider} responsible for creating an action view if
-     * the item is placed on the action bar. The provider also provides a default
-     * action invoked if the item is placed in the overflow menu.
-     *
-     * <p><strong>Note:</strong> Setting an action provider overrides the action view
-     * set via {@link #setActionView(int)} or {@link #setActionView(View)}.
-     * </p>
-     *
-     * @param actionProvider The action provider.
-     * @return This Item so additional setters can be called.
-     * @see android.support.v4.view.ActionProvider
-     */
-    SupportMenuItem setSupportActionProvider(ActionProvider actionProvider);
-
-    /**
-     * Gets the {@link ActionProvider}.
-     *
-     * @return The action provider.
-     * @see ActionProvider
-     * @see #setSupportActionProvider(ActionProvider)
-     */
-    ActionProvider getSupportActionProvider();
-
-    /**
-     * 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(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(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();
-
-    /**
-     * Returns true if this menu item's action view has been expanded.
-     *
-     * @return true if the item's action view is expanded, false otherwise.
-     * @see #expandActionView()
-     * @see #collapseActionView()
-     * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
-     * @see MenuItem.OnActionExpandListener
-     */
-    @Override
-    boolean isActionViewExpanded();
-
-    /**
-     * Change the content description associated with this menu item.
-     *
-     * @param contentDescription The new content description.
-     * @return This menu item instance for call chaining.
-     */
-    @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/android/support/v4/internal/view/SupportSubMenu.java b/android/support/v4/internal/view/SupportSubMenu.java
deleted file mode 100644
index 08b3a33..0000000
--- a/android/support/v4/internal/view/SupportSubMenu.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.internal.view;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Subclass of {@link SupportMenu} for sub menus.
- *
- * This version extends the one available in the framework to ensures that any necessary
- * elements added in later versions of the framework, are available for all platforms.
- *
- * @see android.view.SubMenu
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface SupportSubMenu extends SupportMenu, android.view.SubMenu {
-}
diff --git a/android/support/v4/math/MathUtils.java b/android/support/v4/math/MathUtils.java
deleted file mode 100644
index 25f8644..0000000
--- a/android/support/v4/math/MathUtils.java
+++ /dev/null
@@ -1,88 +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.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/android/support/v4/media/AudioAttributesCompat.java b/android/support/v4/media/AudioAttributesCompat.java
deleted file mode 100644
index 1bd09de..0000000
--- a/android/support/v4/media/AudioAttributesCompat.java
+++ /dev/null
@@ -1,813 +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.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/android/support/v4/media/AudioAttributesCompatApi21.java b/android/support/v4/media/AudioAttributesCompatApi21.java
deleted file mode 100644
index 6d53b80..0000000
--- a/android/support/v4/media/AudioAttributesCompatApi21.java
+++ /dev/null
@@ -1,67 +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.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/android/support/v4/media/MediaBrowserCompat.java b/android/support/v4/media/MediaBrowserCompat.java
index 1b27925..a0e839b 100644
--- a/android/support/v4/media/MediaBrowserCompat.java
+++ b/android/support/v4/media/MediaBrowserCompat.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,38 +15,37 @@
  */
 package android.support.v4.media;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_ADD_SUBSCRIPTION;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_CONNECT;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_DISCONNECT;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_GET_MEDIA_ITEM;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REGISTER_CALLBACK_MESSENGER;
-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_VERSION_CURRENT;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_CALLBACK_TOKEN;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION_EXTRAS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_ID;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_LIST;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_SESSION_TOKEN;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_OPTIONS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_PACKAGE_NAME;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_RESULT_RECEIVER;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_ROOT_HINTS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_SEARCH_EXTRAS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_SEARCH_QUERY;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_CLIENT_VERSION;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_MESSENGER_BINDER;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SERVICE_VERSION;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SESSION_BINDER;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT_FAILED;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_LOAD_CHILDREN;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_VERSION_2;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_ADD_SUBSCRIPTION;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_CONNECT;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_DISCONNECT;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_GET_MEDIA_ITEM;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_REGISTER_CALLBACK_MESSENGER;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_REMOVE_SUBSCRIPTION;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_SEARCH;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_SEND_CUSTOM_ACTION;
+import static androidx.media.MediaBrowserProtocol.CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
+import static androidx.media.MediaBrowserProtocol.CLIENT_VERSION_CURRENT;
+import static androidx.media.MediaBrowserProtocol.DATA_CALLBACK_TOKEN;
+import static androidx.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION;
+import static androidx.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION_EXTRAS;
+import static androidx.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_ID;
+import static androidx.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_LIST;
+import static androidx.media.MediaBrowserProtocol.DATA_MEDIA_SESSION_TOKEN;
+import static androidx.media.MediaBrowserProtocol.DATA_OPTIONS;
+import static androidx.media.MediaBrowserProtocol.DATA_PACKAGE_NAME;
+import static androidx.media.MediaBrowserProtocol.DATA_RESULT_RECEIVER;
+import static androidx.media.MediaBrowserProtocol.DATA_ROOT_HINTS;
+import static androidx.media.MediaBrowserProtocol.DATA_SEARCH_EXTRAS;
+import static androidx.media.MediaBrowserProtocol.DATA_SEARCH_QUERY;
+import static androidx.media.MediaBrowserProtocol.EXTRA_CLIENT_VERSION;
+import static androidx.media.MediaBrowserProtocol.EXTRA_MESSENGER_BINDER;
+import static androidx.media.MediaBrowserProtocol.EXTRA_SERVICE_VERSION;
+import static androidx.media.MediaBrowserProtocol.EXTRA_SESSION_BINDER;
+import static androidx.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT;
+import static androidx.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT_FAILED;
+import static androidx.media.MediaBrowserProtocol.SERVICE_MSG_ON_LOAD_CHILDREN;
+import static androidx.media.MediaBrowserProtocol.SERVICE_VERSION_2;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -63,20 +62,23 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-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.ResultReceiver;
-import android.support.v4.util.ArrayMap;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.collection.ArrayMap;
+import androidx.core.app.BundleCompat;
+import androidx.media.MediaBrowserCompatUtils;
+import androidx.media.MediaBrowserServiceCompat;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
diff --git a/android/support/v4/media/MediaBrowserCompatApi21.java b/android/support/v4/media/MediaBrowserCompatApi21.java
index 0634309..acd8bf8 100644
--- a/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,8 +20,9 @@
 import android.content.Context;
 import android.media.browse.MediaBrowser;
 import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 import java.util.List;
 
@@ -147,5 +148,11 @@
         public static Object getDescription(Object itemObj) {
             return ((MediaBrowser.MediaItem) itemObj).getDescription();
         }
+
+        private MediaItem() {
+        }
+    }
+
+    private MediaBrowserCompatApi21() {
     }
 }
diff --git a/android/support/v4/media/MediaBrowserCompatApi23.java b/android/support/v4/media/MediaBrowserCompatApi23.java
index 0a12597..514b1e9 100644
--- a/android/support/v4/media/MediaBrowserCompatApi23.java
+++ b/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,8 +18,9 @@
 
 import android.media.browse.MediaBrowser;
 import android.os.Parcel;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(23)
 class MediaBrowserCompatApi23 {
@@ -60,4 +61,7 @@
             mItemCallback.onError(itemId);
         }
     }
+
+    private MediaBrowserCompatApi23() {
+    }
 }
diff --git a/android/support/v4/media/MediaBrowserCompatApi26.java b/android/support/v4/media/MediaBrowserCompatApi26.java
index 8dcbe88..e156c06 100644
--- a/android/support/v4/media/MediaBrowserCompatApi26.java
+++ b/android/support/v4/media/MediaBrowserCompatApi26.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,8 +18,9 @@
 
 import android.media.browse.MediaBrowser;
 import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 import java.util.List;
 
@@ -63,4 +64,7 @@
             mSubscriptionCallback.onError(parentId, options);
         }
     }
+
+    private MediaBrowserCompatApi26() {
+    }
 }
diff --git a/android/support/v4/media/MediaBrowserCompatUtils.java b/android/support/v4/media/MediaBrowserCompatUtils.java
deleted file mode 100644
index 296b4c6..0000000
--- a/android/support/v4/media/MediaBrowserCompatUtils.java
+++ /dev/null
@@ -1,78 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class MediaBrowserCompatUtils {
-    public static boolean areSameOptions(Bundle options1, Bundle options2) {
-        if (options1 == options2) {
-            return true;
-        } else if (options1 == null) {
-            return options2.getInt(MediaBrowserCompat.EXTRA_PAGE, -1) == -1
-                    && options2.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1) == -1;
-        } else if (options2 == null) {
-            return options1.getInt(MediaBrowserCompat.EXTRA_PAGE, -1) == -1
-                    && options1.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1) == -1;
-        } else {
-            return options1.getInt(MediaBrowserCompat.EXTRA_PAGE, -1)
-                    == options2.getInt(MediaBrowserCompat.EXTRA_PAGE, -1)
-                    && options1.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1)
-                    == options2.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
-        }
-    }
-
-    public static boolean hasDuplicatedItems(Bundle options1, Bundle options2) {
-        int page1 = options1 == null ? -1 : options1.getInt(MediaBrowserCompat.EXTRA_PAGE, -1);
-        int page2 = options2 == null ? -1 :options2.getInt(MediaBrowserCompat.EXTRA_PAGE, -1);
-        int pageSize1 = options1 == null
-                ? -1 :options1.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
-        int pageSize2 = options2 == null
-                ? -1 :options2.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
-
-        int startIndex1, startIndex2, endIndex1, endIndex2;
-        if (page1 == -1 || pageSize1 == -1) {
-            startIndex1 = 0;
-            endIndex1 = Integer.MAX_VALUE;
-        } else {
-            startIndex1 = pageSize1 * page1;
-            endIndex1 = startIndex1 + pageSize1 - 1;
-        }
-
-        if (page2 == -1 || pageSize2 == -1) {
-            startIndex2 = 0;
-            endIndex2 = Integer.MAX_VALUE;
-        } else {
-            startIndex2 = pageSize2 * page2;
-            endIndex2 = startIndex2 + pageSize2 - 1;
-        }
-
-        if (startIndex1 <= startIndex2 && startIndex2 <= endIndex1) {
-            return true;
-        } else if (startIndex1 <= endIndex2 && endIndex2 <= endIndex1) {
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/android/support/v4/media/MediaBrowserProtocol.java b/android/support/v4/media/MediaBrowserProtocol.java
deleted file mode 100644
index 8ed152d..0000000
--- a/android/support/v4/media/MediaBrowserProtocol.java
+++ /dev/null
@@ -1,196 +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.media;
-
-/**
- * Defines the communication protocol for media browsers and media browser services.
- */
-class MediaBrowserProtocol {
-
-    public static final String DATA_CALLBACK_TOKEN = "data_callback_token";
-    public static final String DATA_CALLING_UID = "data_calling_uid";
-    public static final String DATA_MEDIA_ITEM_ID = "data_media_item_id";
-    public static final String DATA_MEDIA_ITEM_LIST = "data_media_item_list";
-    public static final String DATA_MEDIA_SESSION_TOKEN = "data_media_session_token";
-    public static final String DATA_OPTIONS = "data_options";
-    public static final String DATA_PACKAGE_NAME = "data_package_name";
-    public static final String DATA_RESULT_RECEIVER = "data_result_receiver";
-    public static final String DATA_ROOT_HINTS = "data_root_hints";
-    public static final String DATA_SEARCH_EXTRAS = "data_search_extras";
-    public static final String DATA_SEARCH_QUERY = "data_search_query";
-    public static final String DATA_CUSTOM_ACTION = "data_custom_action";
-    public static final String DATA_CUSTOM_ACTION_EXTRAS = "data_custom_action_extras";
-
-    public static final String EXTRA_CLIENT_VERSION = "extra_client_version";
-    public static final String EXTRA_SERVICE_VERSION = "extra_service_version";
-    public static final String EXTRA_MESSENGER_BINDER = "extra_messenger";
-    public static final String EXTRA_SESSION_BINDER = "extra_session_binder";
-
-    /**
-     * MediaBrowserCompat will check the version of the connected MediaBrowserServiceCompat,
-     * and it will not send messages if they are introduced in the higher version of the
-     * MediaBrowserServiceCompat.
-     */
-    public static final int SERVICE_VERSION_1 = 1;
-
-    /**
-     * To prevent ClassNotFoundException from Parcelables, MediaBrowser(Service)Compat tries to
-     * avoid using framework code as much as possible (b/62648808). For backward compatibility,
-     * service v2 is introduced so that the browser can distinguish whether the service supports
-     * subscribing through compat code.
-     */
-    public static final int SERVICE_VERSION_2 = 2;
-    public static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_2;
-
-    /*
-     * Messages sent from the media browser service compat to the media browser compat.
-     * (Compat implementation for IMediaBrowserServiceCallbacks)
-     * DO NOT RENUMBER THESE!
-     */
-
-    /** (service v1)
-     * Sent after {@link MediaBrowserCompat#connect()} when the request has successfully
-     * completed.
-     * - arg1 : The service version
-     * - data
-     *     DATA_MEDIA_ITEM_ID : A string for the root media item id
-     *     DATA_MEDIA_SESSION_TOKEN : Media session token
-     *     DATA_ROOT_HINTS : An optional root hints bundle of service-specific arguments
-     */
-    public static final int SERVICE_MSG_ON_CONNECT = 1;
-
-    /** (service v1)
-     * Sent after {@link MediaBrowserCompat#connect()} when the connection to the media browser
-     * failed.
-     * - arg1 : service version
-     */
-    public static final int SERVICE_MSG_ON_CONNECT_FAILED = 2;
-
-    /** (service v1)
-     * Sent when the list of children is loaded or updated.
-     * - arg1 : The service version
-     * - data
-     *     DATA_MEDIA_ITEM_ID : A string for the parent media item id
-     *     DATA_MEDIA_ITEM_LIST : An array list for the media item children
-     *     DATA_OPTIONS : A bundle of service-specific arguments sent from the media browse to
-     *                    the media browser service
-     */
-    public static final int SERVICE_MSG_ON_LOAD_CHILDREN = 3;
-
-    /**
-     * MediaBrowserServiceCompat will check the version of the MediaBrowserCompat, and it will not
-     * send messages if they are introduced in the higher version of the MediaBrowserCompat.
-     */
-    public static final int CLIENT_VERSION_1 = 1;
-    public static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_1;
-
-    /*
-     * Messages sent from the media browser compat to the media browser service compat.
-     * (Compat implementation for IMediaBrowserService)
-     * DO NOT RENUMBER THESE!
-     */
-
-    /** (client v1)
-     * Sent to connect to the media browse service compat.
-     * - arg1 : The client version
-     * - data
-     *     DATA_PACKAGE_NAME : A string for the package name of MediaBrowserCompat
-     *     DATA_ROOT_HINTS : An optional root hints bundle of service-specific arguments
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_CONNECT = 1;
-
-    /** (client v1)
-     * Sent to disconnect from the media browse service compat.
-     * - arg1 : The client version
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_DISCONNECT = 2;
-
-    /** (client v1)
-     * Sent to subscribe for changes to the children of the specified media id.
-     * - arg1 : The client version
-     * - data
-     *     DATA_MEDIA_ITEM_ID : A string for a media item id
-     *     DATA_OPTIONS : A bundle of service-specific arguments sent from the media browser to
-     *                    the media browser service
-     *     DATA_CALLBACK_TOKEN : An IBinder of service-specific arguments sent from the media
-     *                           browser to the media browser service
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_ADD_SUBSCRIPTION = 3;
-
-    /** (client v1)
-     * Sent to unsubscribe for changes to the children of the specified media id.
-     * - arg1 : The client version
-     * - data
-     *     DATA_MEDIA_ITEM_ID : A string for a media item id
-     *     DATA_CALLBACK_TOKEN : An IBinder of service-specific arguments sent from the media
-     *                           browser to the media browser service
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_REMOVE_SUBSCRIPTION = 4;
-
-    /** (client v1)
-     * Sent to retrieve a specific media item from the connected service.
-     * - arg1 : The client version
-     * - data
-     *     DATA_MEDIA_ITEM_ID : A string for a media item id
-     *     DATA_RESULT_RECEIVER : Result receiver to get the result
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_GET_MEDIA_ITEM = 5;
-
-    /** (client v1)
-     * Sent to register the client messenger
-     * - arg1 : The client version
-     * - data
-     *     DATA_ROOT_HINTS : An optional root hints bundle of service-specific arguments
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_REGISTER_CALLBACK_MESSENGER = 6;
-
-    /** (client v1)
-     * Sent to unregister the client messenger
-     * - arg1 : The client version
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER = 7;
-
-    /** (client v1)
-     * Sent to retrieve a specific media item from the connected service.
-     * - arg1 : The client version
-     * - data
-     *     DATA_SEARCH_QUERY : A string for search query that contains keywords separated by space.
-     *     DATA_SEARCH_EXTRAS : A bundle of service-specific arguments to send to the media browser
-     *                          service.
-     *     DATA_RESULT_RECEIVER : Result receiver to get the result.
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_SEARCH = 8;
-
-    /** (client v1)
-     * Sent to request a custom action from the media browser.
-     * - arg1 : The client version
-     * - data
-     *     DATA_CUSTOM_ACTION : A string for the custom action.
-     *     DATA_CUSTOM_ACTION_EXTRAS : A bundle of service-specific arguments to send to the media
-     *                                 browser service.
-     *     DATA_RESULT_RECEIVER : Result receiver to get the result.
-     * - replyTo : Callback messenger
-     */
-    public static final int CLIENT_MSG_SEND_CUSTOM_ACTION = 9;
-}
diff --git a/android/support/v4/media/MediaBrowserServiceCompat.java b/android/support/v4/media/MediaBrowserServiceCompat.java
deleted file mode 100644
index 27bf0e3..0000000
--- a/android/support/v4/media/MediaBrowserServiceCompat.java
+++ /dev/null
@@ -1,1588 +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.media;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_ADD_SUBSCRIPTION;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_CONNECT;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_DISCONNECT;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_GET_MEDIA_ITEM;
-import static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REGISTER_CALLBACK_MESSENGER;
-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.DATA_CALLBACK_TOKEN;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_CALLING_UID;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_CUSTOM_ACTION_EXTRAS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_ID;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_LIST;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_SESSION_TOKEN;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_OPTIONS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_PACKAGE_NAME;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_RESULT_RECEIVER;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_ROOT_HINTS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_SEARCH_EXTRAS;
-import static android.support.v4.media.MediaBrowserProtocol.DATA_SEARCH_QUERY;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_CLIENT_VERSION;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_MESSENGER_BINDER;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SERVICE_VERSION;
-import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SESSION_BINDER;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT_FAILED;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_LOAD_CHILDREN;
-import static android.support.v4.media.MediaBrowserProtocol.SERVICE_VERSION_CURRENT;
-
-import android.app.Service;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.Parcel;
-import android.os.RemoteException;
-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.MediaSessionCompat;
-import android.support.v4.os.ResultReceiver;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.util.Pair;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Base class for media browse services.
- * <p>
- * Media browse services enable applications to browse media content provided by an application
- * and ask the application to start playing it. They may also be used to control content that
- * is already playing by way of a {@link MediaSessionCompat}.
- * </p>
- *
- * To extend this class, you must declare the service in your manifest file with
- * an intent filter with the {@link #SERVICE_INTERFACE} action.
- *
- * For example:
- * </p><pre>
- * &lt;service android:name=".MyMediaBrowserServiceCompat"
- *          android:label="&#64;string/service_name" >
- *     &lt;intent-filter>
- *         &lt;action android:name="android.media.browse.MediaBrowserService" />
- *     &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;
-
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     */
-    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
-
-    /**
-     * A key for passing the MediaItem to the ResultReceiver in getItem.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final String KEY_MEDIA_ITEM = "media_item";
-
-    /**
-     * A key for passing the list of MediaItems to the ResultReceiver in search.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final String KEY_SEARCH_RESULTS = "search_results";
-
-    static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
-    static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
-    static final int RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED = 1 << 2;
-
-    static final int RESULT_ERROR = -1;
-    static final int RESULT_OK = 0;
-    static final int RESULT_PROGRESS_UPDATE = 1;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
-            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED })
-    private @interface ResultFlags { }
-
-    final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
-    ConnectionRecord mCurConnection;
-    final ServiceHandler mHandler = new ServiceHandler();
-    MediaSessionCompat.Token mSession;
-
-    interface MediaBrowserServiceImpl {
-        void onCreate();
-        IBinder onBind(Intent intent);
-        void setSessionToken(MediaSessionCompat.Token token);
-        void notifyChildrenChanged(final String parentId, final Bundle options);
-        Bundle getBrowserRootHints();
-    }
-
-    class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
-        private Messenger mMessenger;
-
-        @Override
-        public void onCreate() {
-            mMessenger = new Messenger(mHandler);
-        }
-
-        @Override
-        public IBinder onBind(Intent intent) {
-            if (SERVICE_INTERFACE.equals(intent.getAction())) {
-                return mMessenger.getBinder();
-            }
-            return null;
-        }
-
-        @Override
-        public void setSessionToken(final MediaSessionCompat.Token token) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    Iterator<ConnectionRecord> iter = mConnections.values().iterator();
-                    while (iter.hasNext()){
-                        ConnectionRecord connection = iter.next();
-                        try {
-                            connection.callbacks.onConnect(connection.root.getRootId(), token,
-                                    connection.root.getExtras());
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
-                            iter.remove();
-                        }
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void notifyChildrenChanged(@NonNull final String parentId, final Bundle options) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    for (IBinder binder : mConnections.keySet()) {
-                        ConnectionRecord connection = mConnections.get(binder);
-                        List<Pair<IBinder, Bundle>> callbackList =
-                                connection.subscriptions.get(parentId);
-                        if (callbackList != null) {
-                            for (Pair<IBinder, Bundle> callback : callbackList) {
-                                if (MediaBrowserCompatUtils.hasDuplicatedItems(
-                                        options, callback.second)) {
-                                    performLoadChildren(parentId, connection, callback.second);
-                                }
-                            }
-                        }
-                    }
-                }
-            });
-        }
-
-        @Override
-        public Bundle getBrowserRootHints() {
-            if (mCurConnection == null) {
-                throw new IllegalStateException("This should be called inside of onLoadChildren,"
-                        + " onLoadItem or onSearch methods");
-            }
-            return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
-        }
-    }
-
-    @RequiresApi(21)
-    class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl,
-            MediaBrowserServiceCompatApi21.ServiceCompatProxy {
-        final List<Bundle> mRootExtrasList = new ArrayList<>();
-        Object mServiceObj;
-        Messenger mMessenger;
-
-        @Override
-        public void onCreate() {
-            mServiceObj = MediaBrowserServiceCompatApi21.createService(
-                    MediaBrowserServiceCompat.this, this);
-            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
-        }
-
-        @Override
-        public IBinder onBind(Intent intent) {
-            return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent);
-        }
-
-        @Override
-        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
-        public void notifyChildrenChanged(final String parentId, final Bundle options) {
-            notifyChildrenChangedForFramework(parentId, options);
-            notifyChildrenChangedForCompat(parentId, options);
-        }
-
-        @Override
-        public Bundle getBrowserRootHints() {
-            if (mMessenger == null) {
-                // TODO: Handle getBrowserRootHints when connected with framework MediaBrowser.
-                return null;
-            }
-            if (mCurConnection == null) {
-                throw new IllegalStateException("This should be called inside of onLoadChildren,"
-                        + " onLoadItem or onSearch methods");
-            }
-            return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
-        }
-
-        @Override
-        public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot(
-                String clientPackageName, int clientUid, Bundle rootHints) {
-            Bundle rootExtras = null;
-            if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) {
-                rootHints.remove(EXTRA_CLIENT_VERSION);
-                mMessenger = new Messenger(mHandler);
-                rootExtras = new Bundle();
-                rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
-                BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
-                if (mSession != null) {
-                    IMediaSession extraBinder = mSession.getExtraBinder();
-                    BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
-                            extraBinder == null ? null : extraBinder.asBinder());
-                } else {
-                    mRootExtrasList.add(rootExtras);
-                }
-            }
-            BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
-                    clientPackageName, clientUid, rootHints);
-            if (root == null) {
-                return null;
-            }
-            if (rootExtras == null) {
-                rootExtras = root.getExtras();
-            } else if (root.getExtras() != null) {
-                rootExtras.putAll(root.getExtras());
-            }
-            return new MediaBrowserServiceCompatApi21.BrowserRoot(
-                    root.getRootId(), rootExtras);
-        }
-
-        @Override
-        public void onLoadChildren(String parentId,
-                final MediaBrowserServiceCompatApi21.ResultWrapper<List<Parcel>> resultWrapper) {
-            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);
-                }
-
-                @Override
-                public void detach() {
-                    resultWrapper.detach();
-                }
-            };
-            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result);
-        }
-
-        void notifyChildrenChangedForFramework(final String parentId, final Bundle options) {
-            MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
-        }
-
-        void notifyChildrenChangedForCompat(final String parentId, final Bundle options) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    for (IBinder binder : mConnections.keySet()) {
-                        ConnectionRecord connection = mConnections.get(binder);
-                        List<Pair<IBinder, Bundle>> callbackList =
-                                connection.subscriptions.get(parentId);
-                        if (callbackList != null) {
-                            for (Pair<IBinder, Bundle> callback : callbackList) {
-                                if (MediaBrowserCompatUtils.hasDuplicatedItems(
-                                        options, callback.second)) {
-                                    performLoadChildren(parentId, connection, callback.second);
-                                }
-                            }
-                        }
-                    }
-                }
-            });
-        }
-    }
-
-    @RequiresApi(23)
-    class MediaBrowserServiceImplApi23 extends MediaBrowserServiceImplApi21 implements
-            MediaBrowserServiceCompatApi23.ServiceCompatProxy {
-        @Override
-        public void onCreate() {
-            mServiceObj = MediaBrowserServiceCompatApi23.createService(
-                    MediaBrowserServiceCompat.this, this);
-            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
-        }
-
-        @Override
-        public void onLoadItem(String itemId,
-                final MediaBrowserServiceCompatApi21.ResultWrapper<Parcel> resultWrapper) {
-            final Result<MediaBrowserCompat.MediaItem> result
-                    = new Result<MediaBrowserCompat.MediaItem>(itemId) {
-                @Override
-                void onResultSent(MediaBrowserCompat.MediaItem item) {
-                    if (item == null) {
-                        resultWrapper.sendResult(null);
-                    } else {
-                        Parcel parcelItem = Parcel.obtain();
-                        item.writeToParcel(parcelItem, 0);
-                        resultWrapper.sendResult(parcelItem);
-                    }
-                }
-
-                @Override
-                public void detach() {
-                    resultWrapper.detach();
-                }
-            };
-            MediaBrowserServiceCompat.this.onLoadItem(itemId, result);
-        }
-    }
-
-    @RequiresApi(26)
-    class MediaBrowserServiceImplApi26 extends MediaBrowserServiceImplApi23 implements
-            MediaBrowserServiceCompatApi26.ServiceCompatProxy {
-        @Override
-        public void onCreate() {
-            mServiceObj = MediaBrowserServiceCompatApi26.createService(
-                    MediaBrowserServiceCompat.this, this);
-            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
-        }
-
-        @Override
-        public void onLoadChildren(String parentId,
-                final MediaBrowserServiceCompatApi26.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 MediaBrowserServiceCompatApi26.getBrowserRootHints(mServiceObj);
-        }
-
-        @Override
-        void notifyChildrenChangedForFramework(final String parentId, final Bundle options) {
-            if (options != null) {
-                MediaBrowserServiceCompatApi26.notifyChildrenChanged(mServiceObj, parentId,
-                        options);
-            } else {
-                super.notifyChildrenChangedForFramework(parentId, options);
-            }
-        }
-    }
-
-    private final class ServiceHandler extends Handler {
-        private final ServiceBinderImpl mServiceBinderImpl = new ServiceBinderImpl();
-
-        ServiceHandler() {
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            Bundle data = msg.getData();
-            switch (msg.what) {
-                case CLIENT_MSG_CONNECT:
-                    mServiceBinderImpl.connect(data.getString(DATA_PACKAGE_NAME),
-                            data.getInt(DATA_CALLING_UID), data.getBundle(DATA_ROOT_HINTS),
-                            new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_DISCONNECT:
-                    mServiceBinderImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_ADD_SUBSCRIPTION:
-                    mServiceBinderImpl.addSubscription(data.getString(DATA_MEDIA_ITEM_ID),
-                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
-                            data.getBundle(DATA_OPTIONS),
-                            new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_REMOVE_SUBSCRIPTION:
-                    mServiceBinderImpl.removeSubscription(data.getString(DATA_MEDIA_ITEM_ID),
-                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
-                            new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_GET_MEDIA_ITEM:
-                    mServiceBinderImpl.getMediaItem(data.getString(DATA_MEDIA_ITEM_ID),
-                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER),
-                            new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_REGISTER_CALLBACK_MESSENGER:
-                    mServiceBinderImpl.registerCallbacks(new ServiceCallbacksCompat(msg.replyTo),
-                            data.getBundle(DATA_ROOT_HINTS));
-                    break;
-                case CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER:
-                    mServiceBinderImpl.unregisterCallbacks(new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_SEARCH:
-                    mServiceBinderImpl.search(data.getString(DATA_SEARCH_QUERY),
-                            data.getBundle(DATA_SEARCH_EXTRAS),
-                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER),
-                            new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                case CLIENT_MSG_SEND_CUSTOM_ACTION:
-                    mServiceBinderImpl.sendCustomAction(data.getString(DATA_CUSTOM_ACTION),
-                            data.getBundle(DATA_CUSTOM_ACTION_EXTRAS),
-                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER),
-                            new ServiceCallbacksCompat(msg.replyTo));
-                    break;
-                default:
-                    Log.w(TAG, "Unhandled message: " + msg
-                            + "\n  Service version: " + SERVICE_VERSION_CURRENT
-                            + "\n  Client version: " + msg.arg1);
-            }
-        }
-
-        @Override
-        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
-            // Binder.getCallingUid() in handleMessage will return the uid of this process.
-            // In order to get the right calling uid, Binder.getCallingUid() should be called here.
-            Bundle data = msg.getData();
-            data.setClassLoader(MediaBrowserCompat.class.getClassLoader());
-            data.putInt(DATA_CALLING_UID, Binder.getCallingUid());
-            return super.sendMessageAtTime(msg, uptimeMillis);
-        }
-
-        public void postOrRun(Runnable r) {
-            if (Thread.currentThread() == getLooper().getThread()) {
-                r.run();
-            } else {
-                post(r);
-            }
-        }
-    }
-
-    /**
-     * All the info about a connection.
-     */
-    private class ConnectionRecord implements IBinder.DeathRecipient {
-        String pkg;
-        Bundle rootHints;
-        ServiceCallbacks callbacks;
-        BrowserRoot root;
-        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
-
-        ConnectionRecord() {
-        }
-
-        @Override
-        public void binderDied() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mConnections.remove(callbacks.asBinder());
-                }
-            });
-        }
-    }
-
-    /**
-     * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}.
-     * <p>
-     * Each of the methods that takes one of these to send the result must call either
-     * {@link #sendResult} or {@link #sendError} to respond to the caller with the given results or
-     * errors. If those functions return without calling {@link #sendResult} or {@link #sendError},
-     * they must instead call {@link #detach} before returning, and then may call
-     * {@link #sendResult} or {@link #sendError} when they are done. If {@link #sendResult},
-     * {@link #sendError}, or {@link #detach} is called twice, an exception will be thrown.
-     * </p><p>
-     * Those functions might also want to call {@link #sendProgressUpdate} to send interim updates
-     * to the caller. If it is called after calling {@link #sendResult} or {@link #sendError}, an
-     * exception will be thrown.
-     * </p>
-     *
-     * @see MediaBrowserServiceCompat#onLoadChildren
-     * @see MediaBrowserServiceCompat#onLoadItem
-     * @see MediaBrowserServiceCompat#onSearch
-     * @see MediaBrowserServiceCompat#onCustomAction
-     */
-    public static class Result<T> {
-        private final Object mDebug;
-        private boolean mDetachCalled;
-        private boolean mSendResultCalled;
-        private boolean mSendProgressUpdateCalled;
-        private boolean mSendErrorCalled;
-        private int mFlags;
-
-        Result(Object debug) {
-            mDebug = debug;
-        }
-
-        /**
-         * Send the result back to the caller.
-         */
-        public void sendResult(T result) {
-            if (mSendResultCalled || mSendErrorCalled) {
-                throw new IllegalStateException("sendResult() called when either sendResult() or "
-                        + "sendError() had already been called for: " + mDebug);
-            }
-            mSendResultCalled = true;
-            onResultSent(result);
-        }
-
-        /**
-         * Send an interim update to the caller. This method is supported only when it is used in
-         * {@link #onCustomAction}.
-         *
-         * @param extras A bundle that contains extra data.
-         */
-        public void sendProgressUpdate(Bundle extras) {
-            if (mSendResultCalled || mSendErrorCalled) {
-                throw new IllegalStateException("sendProgressUpdate() called when either "
-                        + "sendResult() or sendError() had already been called for: " + mDebug);
-            }
-            checkExtraFields(extras);
-            mSendProgressUpdateCalled = true;
-            onProgressUpdateSent(extras);
-        }
-
-        /**
-         * Notify the caller of a failure. This is supported only when it is used in
-         * {@link #onCustomAction}.
-         *
-         * @param extras A bundle that contains extra data.
-         */
-        public void sendError(Bundle extras) {
-            if (mSendResultCalled || mSendErrorCalled) {
-                throw new IllegalStateException("sendError() called when either sendResult() or "
-                        + "sendError() had already been called for: " + mDebug);
-            }
-            mSendErrorCalled = true;
-            onErrorSent(extras);
-        }
-
-        /**
-         * Detach this message from the current thread and allow the {@link #sendResult}
-         * call to happen later.
-         */
-        public void detach() {
-            if (mDetachCalled) {
-                throw new IllegalStateException("detach() called when detach() had already"
-                        + " been called for: " + mDebug);
-            }
-            if (mSendResultCalled) {
-                throw new IllegalStateException("detach() called when sendResult() had already"
-                        + " been called for: " + mDebug);
-            }
-            if (mSendErrorCalled) {
-                throw new IllegalStateException("detach() called when sendError() had already"
-                        + " been called for: " + mDebug);
-            }
-            mDetachCalled = true;
-        }
-
-        boolean isDone() {
-            return mDetachCalled || mSendResultCalled || mSendErrorCalled;
-        }
-
-        void setFlags(@ResultFlags int flags) {
-            mFlags = flags;
-        }
-
-        int getFlags() {
-            return mFlags;
-        }
-
-        /**
-         * Called when the result is sent, after assertions about not being called twice have
-         * happened.
-         */
-        void onResultSent(T result) {
-        }
-
-        /**
-         * Called when an interim update is sent.
-         */
-        void onProgressUpdateSent(Bundle extras) {
-            throw new UnsupportedOperationException("It is not supported to send an interim update "
-                    + "for " + mDebug);
-        }
-
-        /**
-         * Called when an error is sent, after assertions about not being called twice have
-         * happened.
-         */
-        void onErrorSent(Bundle extras) {
-            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 {
-        ServiceBinderImpl() {
-        }
-
-        public void connect(final String pkg, final int uid, final Bundle rootHints,
-                final ServiceCallbacks callbacks) {
-
-            if (!isValidPackage(pkg, uid)) {
-                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
-                        + " package=" + pkg);
-            }
-
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    // Clear out the old subscriptions. We are getting new ones.
-                    mConnections.remove(b);
-
-                    final ConnectionRecord connection = new ConnectionRecord();
-                    connection.pkg = pkg;
-                    connection.rootHints = rootHints;
-                    connection.callbacks = callbacks;
-
-                    connection.root =
-                            MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints);
-
-                    // If they didn't return something, don't allow this client.
-                    if (connection.root == null) {
-                        Log.i(TAG, "No root for client " + pkg + " from service "
-                                + getClass().getName());
-                        try {
-                            callbacks.onConnectFailed();
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
-                                    + "pkg=" + pkg);
-                        }
-                    } else {
-                        try {
-                            mConnections.put(b, connection);
-                            b.linkToDeath(connection, 0);
-                            if (mSession != null) {
-                                callbacks.onConnect(connection.root.getRootId(),
-                                        mSession, connection.root.getExtras());
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "Calling onConnect() failed. Dropping client. "
-                                    + "pkg=" + pkg);
-                            mConnections.remove(b);
-                        }
-                    }
-                }
-            });
-        }
-
-        public void disconnect(final ServiceCallbacks callbacks) {
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    // Clear out the old subscriptions. We are getting new ones.
-                    final ConnectionRecord old = mConnections.remove(b);
-                    if (old != null) {
-                        // TODO
-                        old.callbacks.asBinder().unlinkToDeath(old, 0);
-                    }
-                }
-            });
-        }
-
-        public void addSubscription(final String id, final IBinder token, final Bundle options,
-                final ServiceCallbacks callbacks) {
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    // Get the record for the connection
-                    final ConnectionRecord connection = mConnections.get(b);
-                    if (connection == null) {
-                        Log.w(TAG, "addSubscription for callback that isn't registered id="
-                                + id);
-                        return;
-                    }
-
-                    MediaBrowserServiceCompat.this.addSubscription(id, connection, token, options);
-                }
-            });
-        }
-
-        public void removeSubscription(final String id, final IBinder token,
-                final ServiceCallbacks callbacks) {
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    ConnectionRecord connection = mConnections.get(b);
-                    if (connection == null) {
-                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
-                                + id);
-                        return;
-                    }
-                    if (!MediaBrowserServiceCompat.this.removeSubscription(
-                            id, connection, token)) {
-                        Log.w(TAG, "removeSubscription called for " + id
-                                + " which is not subscribed");
-                    }
-                }
-            });
-        }
-
-        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
-                final ServiceCallbacks callbacks) {
-            if (TextUtils.isEmpty(mediaId) || receiver == null) {
-                return;
-            }
-
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    ConnectionRecord connection = mConnections.get(b);
-                    if (connection == null) {
-                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
-                        return;
-                    }
-                    performLoadItem(mediaId, connection, receiver);
-                }
-            });
-        }
-
-        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
-        public void registerCallbacks(final ServiceCallbacks callbacks, final Bundle rootHints) {
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-                    // Clear out the old subscriptions. We are getting new ones.
-                    mConnections.remove(b);
-
-                    final ConnectionRecord connection = new ConnectionRecord();
-                    connection.callbacks = callbacks;
-                    connection.rootHints = rootHints;
-                    mConnections.put(b, connection);
-                    try {
-                        b.linkToDeath(connection, 0);
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "IBinder is already dead.");
-                    }
-                }
-            });
-        }
-
-        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
-        public void unregisterCallbacks(final ServiceCallbacks callbacks) {
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-                    ConnectionRecord old = mConnections.remove(b);
-                    if (old != null) {
-                        b.unlinkToDeath(old, 0);
-                    }
-                }
-            });
-        }
-
-        public void search(final String query, final Bundle extras, final ResultReceiver receiver,
-                final ServiceCallbacks callbacks) {
-            if (TextUtils.isEmpty(query) || receiver == null) {
-                return;
-            }
-
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    ConnectionRecord connection = mConnections.get(b);
-                    if (connection == null) {
-                        Log.w(TAG, "search for callback that isn't registered query=" + query);
-                        return;
-                    }
-                    performSearch(query, extras, connection, receiver);
-                }
-            });
-        }
-
-        public void sendCustomAction(final String action, final Bundle extras,
-                final ResultReceiver receiver, final ServiceCallbacks callbacks) {
-            if (TextUtils.isEmpty(action) || receiver == null) {
-                return;
-            }
-
-            mHandler.postOrRun(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-
-                    ConnectionRecord connection = mConnections.get(b);
-                    if (connection == null) {
-                        Log.w(TAG, "sendCustomAction for callback that isn't registered action="
-                                + action + ", extras=" + extras);
-                        return;
-                    }
-                    performCustomAction(action, extras, connection, receiver);
-                }
-            });
-        }
-    }
-
-    private interface ServiceCallbacks {
-        IBinder asBinder();
-        void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
-                throws RemoteException;
-        void onConnectFailed() throws RemoteException;
-        void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list, Bundle options)
-                throws RemoteException;
-    }
-
-    private static class ServiceCallbacksCompat implements ServiceCallbacks {
-        final Messenger mCallbacks;
-
-        ServiceCallbacksCompat(Messenger callbacks) {
-            mCallbacks = callbacks;
-        }
-
-        @Override
-        public IBinder asBinder() {
-            return mCallbacks.getBinder();
-        }
-
-        @Override
-        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
-                throws RemoteException {
-            if (extras == null) {
-                extras = new Bundle();
-            }
-            extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
-            Bundle data = new Bundle();
-            data.putString(DATA_MEDIA_ITEM_ID, root);
-            data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session);
-            data.putBundle(DATA_ROOT_HINTS, extras);
-            sendRequest(SERVICE_MSG_ON_CONNECT, data);
-        }
-
-        @Override
-        public void onConnectFailed() throws RemoteException {
-            sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null);
-        }
-
-        @Override
-        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list,
-                Bundle options) throws RemoteException {
-            Bundle data = new Bundle();
-            data.putString(DATA_MEDIA_ITEM_ID, mediaId);
-            data.putBundle(DATA_OPTIONS, options);
-            if (list != null) {
-                data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST,
-                        list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
-            }
-            sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, data);
-        }
-
-        private void sendRequest(int what, Bundle data) throws RemoteException {
-            Message msg = Message.obtain();
-            msg.what = what;
-            msg.arg1 = SERVICE_VERSION_CURRENT;
-            msg.setData(data);
-            mCallbacks.send(msg);
-        }
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        if (Build.VERSION.SDK_INT >= 26) {
-            mImpl = new MediaBrowserServiceImplApi26();
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            mImpl = new MediaBrowserServiceImplApi23();
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            mImpl = new MediaBrowserServiceImplApi21();
-        } else {
-            mImpl = new MediaBrowserServiceImplBase();
-        }
-        mImpl.onCreate();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mImpl.onBind(intent);
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-    }
-
-    /**
-     * Called to get the root information for browsing by a particular client.
-     * <p>
-     * The implementation should verify that the client package has permission
-     * to access browse media information before returning the root id; it
-     * should return null if the client is not allowed to access this
-     * information.
-     * </p>
-     *
-     * @param clientPackageName The package name of the application which is
-     *            requesting access to browse media.
-     * @param clientUid The uid of the application which is requesting access to
-     *            browse media.
-     * @param rootHints An optional bundle of service-specific arguments to send
-     *            to the media browse service when connecting and retrieving the
-     *            root id for browsing, or null if none. The contents of this
-     *            bundle may affect the information returned when browsing.
-     * @return The {@link BrowserRoot} for accessing this app's content or null.
-     * @see BrowserRoot#EXTRA_RECENT
-     * @see BrowserRoot#EXTRA_OFFLINE
-     * @see BrowserRoot#EXTRA_SUGGESTED
-     */
-    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
-            int clientUid, @Nullable Bundle rootHints);
-
-    /**
-     * Called to get information about the children of a media item.
-     * <p>
-     * Implementations must call {@link Result#sendResult result.sendResult}
-     * with the list of children. If loading the children will be an expensive
-     * operation that should be performed on another thread,
-     * {@link Result#detach result.detach} may be called before returning from
-     * this function, and then {@link Result#sendResult result.sendResult}
-     * called when the loading is complete.
-     * </p><p>
-     * In case the media item does not have any children, call {@link Result#sendResult}
-     * with an empty list. When the given {@code parentId} is invalid, implementations must
-     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
-     * {@link MediaBrowserCompat.SubscriptionCallback#onError}.
-     * </p>
-     *
-     * @param parentId The id of the parent media item whose children are to be
-     *            queried.
-     * @param result The Result to send the list of children to.
-     */
-    public abstract void onLoadChildren(@NonNull String parentId,
-            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
-
-    /**
-     * Called to get information about the children of a media item.
-     * <p>
-     * Implementations must call {@link Result#sendResult result.sendResult}
-     * with the list of children. If loading the children will be an expensive
-     * operation that should be performed on another thread,
-     * {@link Result#detach result.detach} may be called before returning from
-     * this function, and then {@link Result#sendResult result.sendResult}
-     * called when the loading is complete.
-     * </p><p>
-     * In case the media item does not have any children, call {@link Result#sendResult}
-     * with an empty list. When the given {@code parentId} is invalid, implementations must
-     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
-     * {@link MediaBrowserCompat.SubscriptionCallback#onError}.
-     * </p>
-     *
-     * @param parentId The id of the parent media item whose children are to be
-     *            queried.
-     * @param result The Result to send the list of children to.
-     * @param options A bundle of service-specific arguments sent from the media
-     *            browse. The information returned through the result should be
-     *            affected by the contents of this bundle.
-     */
-    public void onLoadChildren(@NonNull String parentId,
-            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result, @NonNull Bundle options) {
-        // To support backward compatibility, when the implementation of MediaBrowserService doesn't
-        // override onLoadChildren() with options, onLoadChildren() without options will be used
-        // instead, and the options will be applied in the implementation of result.onResultSent().
-        result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
-        onLoadChildren(parentId, result);
-    }
-
-    /**
-     * Called to get information about a specific media item.
-     * <p>
-     * Implementations must call {@link Result#sendResult result.sendResult}. If
-     * loading the item will be an expensive operation {@link Result#detach
-     * result.detach} may be called before returning from this function, and
-     * then {@link Result#sendResult result.sendResult} called when the item has
-     * been loaded.
-     * </p><p>
-     * When the given {@code itemId} is invalid, implementations must call
-     * {@link Result#sendResult result.sendResult} with {@code null}.
-     * </p><p>
-     * The default implementation will invoke {@link MediaBrowserCompat.ItemCallback#onError}.
-     *
-     * @param itemId The id for the specific {@link MediaBrowserCompat.MediaItem}.
-     * @param result The Result to send the item to, or null if the id is
-     *            invalid.
-     */
-    public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result) {
-        result.setFlags(RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED);
-        result.sendResult(null);
-    }
-
-    /**
-     * Called to get the search result.
-     * <p>
-     * Implementations must call {@link Result#sendResult result.sendResult}. If the search will be
-     * an expensive operation {@link Result#detach result.detach} may be called before returning
-     * from this function, and then {@link Result#sendResult result.sendResult} called when the
-     * search has been completed.
-     * </p><p>
-     * In case there are no search results, call {@link Result#sendResult result.sendResult} with an
-     * empty list. In case there are some errors happened, call {@link Result#sendResult
-     * result.sendResult} with {@code null}, which will invoke {@link
-     * MediaBrowserCompat.SearchCallback#onError}.
-     * </p><p>
-     * The default implementation will invoke {@link MediaBrowserCompat.SearchCallback#onError}.
-     * </p>
-     *
-     * @param query The search query sent from the media browser. It contains keywords separated
-     *            by space.
-     * @param extras The bundle of service-specific arguments sent from the media browser.
-     * @param result The {@link Result} to send the search result.
-     */
-    public void onSearch(@NonNull String query, Bundle extras,
-            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
-        result.setFlags(RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED);
-        result.sendResult(null);
-    }
-
-    /**
-     * Called to request a custom action to this service.
-     * <p>
-     * Implementations must call either {@link Result#sendResult} or {@link Result#sendError}. If
-     * the requested custom action will be an expensive operation {@link Result#detach} may be
-     * called before returning from this function, and then the service can send the result later
-     * when the custom action is completed. Implementation can also call
-     * {@link Result#sendProgressUpdate} to send an interim update to the requester.
-     * </p><p>
-     * If the requested custom action is not supported by this service, call
-     * {@link Result#sendError}. The default implementation will invoke {@link Result#sendError}.
-     * </p>
-     *
-     * @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) {
-        result.sendError(null);
-    }
-
-    /**
-     * Call to set the media session.
-     * <p>
-     * This should be called as soon as possible during the service's startup.
-     * It may only be called once.
-     *
-     * @param token The token for the service's {@link MediaSessionCompat}.
-     */
-    public void setSessionToken(MediaSessionCompat.Token token) {
-        if (token == null) {
-            throw new IllegalArgumentException("Session token may not be null.");
-        }
-        if (mSession != null) {
-            throw new IllegalStateException("The session token has already been set.");
-        }
-        mSession = token;
-        mImpl.setSessionToken(token);
-    }
-
-    /**
-     * Gets the session token, or null if it has not yet been created
-     * or if it has been destroyed.
-     */
-    public @Nullable MediaSessionCompat.Token getSessionToken() {
-        return mSession;
-    }
-
-    /**
-     * Gets the root hints sent from the currently connected {@link MediaBrowserCompat}.
-     * The root hints are service-specific arguments included in an optional bundle sent to the
-     * media browser service when connecting and retrieving the root id for browsing, or null if
-     * none. The contents of this bundle may affect the information returned when browsing.
-     * <p>
-     * Note that this will return null when connected to {@link android.media.browse.MediaBrowser}
-     * and running on API 23 or lower.
-     *
-     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren},
-     *             {@link #onLoadItem} or {@link #onSearch}.
-     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_RECENT
-     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_OFFLINE
-     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_SUGGESTED
-     */
-    public final Bundle getBrowserRootHints() {
-        return mImpl.getBrowserRootHints();
-    }
-
-    /**
-     * Notifies all connected media browsers that the children of
-     * the specified parent id have changed in some way.
-     * This will cause browsers to fetch subscribed content again.
-     *
-     * @param parentId The id of the parent media item whose
-     * children changed.
-     */
-    public void notifyChildrenChanged(@NonNull String parentId) {
-        if (parentId == null) {
-            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
-        }
-        mImpl.notifyChildrenChanged(parentId, null);
-    }
-
-    /**
-     * Notifies all connected media browsers that the children of
-     * the specified parent id have changed in some way.
-     * This will cause browsers to fetch subscribed content again.
-     *
-     * @param parentId The id of the parent media item whose
-     *            children changed.
-     * @param options A bundle of service-specific arguments to send
-     *            to the media browse. The contents of this bundle may
-     *            contain the information about the change.
-     */
-    public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
-        if (parentId == null) {
-            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
-        }
-        if (options == null) {
-            throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
-        }
-        mImpl.notifyChildrenChanged(parentId, options);
-    }
-
-    /**
-     * Return whether the given package is one of the ones that is owned by the uid.
-     */
-    boolean isValidPackage(String pkg, int uid) {
-        if (pkg == null) {
-            return false;
-        }
-        final PackageManager pm = getPackageManager();
-        final String[] packages = pm.getPackagesForUid(uid);
-        final int N = packages.length;
-        for (int i=0; i<N; i++) {
-            if (packages[i].equals(pkg)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Save the subscription and if it is a new subscription send the results.
-     */
-    void addSubscription(String id, ConnectionRecord connection, IBinder token,
-            Bundle options) {
-        // Save the subscription
-        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
-        if (callbackList == null) {
-            callbackList = new ArrayList<>();
-        }
-        for (Pair<IBinder, Bundle> callback : callbackList) {
-            if (token == callback.first
-                    && MediaBrowserCompatUtils.areSameOptions(options, callback.second)) {
-                return;
-            }
-        }
-        callbackList.add(new Pair<>(token, options));
-        connection.subscriptions.put(id, callbackList);
-        // send the results
-        performLoadChildren(id, connection, options);
-    }
-
-    /**
-     * Remove the subscription.
-     */
-    boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
-        if (token == null) {
-            return connection.subscriptions.remove(id) != null;
-        }
-        boolean removed = false;
-        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
-        if (callbackList != null) {
-            Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
-            while (iter.hasNext()){
-                if (token == iter.next().first) {
-                    removed = true;
-                    iter.remove();
-                }
-            }
-            if (callbackList.size() == 0) {
-                connection.subscriptions.remove(id);
-            }
-        }
-        return removed;
-    }
-
-    /**
-     * Call onLoadChildren and then send the results back to the connection.
-     * <p>
-     * Callers must make sure that this connection is still connected.
-     */
-    void performLoadChildren(final String parentId, final ConnectionRecord connection,
-            final Bundle options) {
-        final Result<List<MediaBrowserCompat.MediaItem>> result
-                = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
-            @Override
-            void onResultSent(List<MediaBrowserCompat.MediaItem> list) {
-                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
-                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
-                    }
-                    return;
-                }
-
-                List<MediaBrowserCompat.MediaItem> filteredList =
-                        (getFlags() & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
-                                ? applyOptions(list, options) : list;
-                try {
-                    connection.callbacks.onLoadChildren(parentId, filteredList, options);
-                } catch (RemoteException ex) {
-                    // The other side is in the process of crashing.
-                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
-                            + " package=" + connection.pkg);
-                }
-            }
-        };
-
-        mCurConnection = connection;
-        if (options == null) {
-            onLoadChildren(parentId, result);
-        } else {
-            onLoadChildren(parentId, result, options);
-        }
-        mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
-                    + " before returning for package=" + connection.pkg + " id=" + parentId);
-        }
-    }
-
-    List<MediaBrowserCompat.MediaItem> applyOptions(List<MediaBrowserCompat.MediaItem> list,
-            final Bundle options) {
-        if (list == null) {
-            return null;
-        }
-        int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1);
-        int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
-        if (page == -1 && pageSize == -1) {
-            return list;
-        }
-        int fromIndex = pageSize * page;
-        int toIndex = fromIndex + pageSize;
-        if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
-            return Collections.EMPTY_LIST;
-        }
-        if (toIndex > list.size()) {
-            toIndex = list.size();
-        }
-        return list.subList(fromIndex, toIndex);
-    }
-
-    void performLoadItem(String itemId, ConnectionRecord connection,
-            final ResultReceiver receiver) {
-        final Result<MediaBrowserCompat.MediaItem> result =
-                new Result<MediaBrowserCompat.MediaItem>(itemId) {
-                    @Override
-                    void onResultSent(MediaBrowserCompat.MediaItem item) {
-                        if ((getFlags() & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
-                            receiver.send(RESULT_ERROR, null);
-                            return;
-                        }
-                        Bundle bundle = new Bundle();
-                        bundle.putParcelable(KEY_MEDIA_ITEM, item);
-                        receiver.send(RESULT_OK, bundle);
-                    }
-                };
-
-        mCurConnection = connection;
-        onLoadItem(itemId, result);
-        mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
-                    + " before returning for id=" + itemId);
-        }
-    }
-
-    void performSearch(final String query, Bundle extras, ConnectionRecord connection,
-            final ResultReceiver receiver) {
-        final Result<List<MediaBrowserCompat.MediaItem>> result =
-                new Result<List<MediaBrowserCompat.MediaItem>>(query) {
-            @Override
-            void onResultSent(List<MediaBrowserCompat.MediaItem> items) {
-                if ((getFlags() & RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED) != 0
-                        || items == null) {
-                    receiver.send(RESULT_ERROR, null);
-                    return;
-                }
-                Bundle bundle = new Bundle();
-                bundle.putParcelableArray(KEY_SEARCH_RESULTS,
-                        items.toArray(new MediaBrowserCompat.MediaItem[0]));
-                receiver.send(RESULT_OK, bundle);
-            }
-        };
-
-        mCurConnection = connection;
-        onSearch(query, extras, result);
-        mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onSearch must call detach() or sendResult()"
-                    + " before returning for query=" + query);
-        }
-    }
-
-    void performCustomAction(final String action, Bundle extras, ConnectionRecord connection,
-            final ResultReceiver receiver) {
-        final Result<Bundle> result = new Result<Bundle>(action) {
-                @Override
-                void onResultSent(Bundle result) {
-                    receiver.send(RESULT_OK, result);
-                }
-
-                @Override
-                void onProgressUpdateSent(Bundle data) {
-                    receiver.send(RESULT_PROGRESS_UPDATE, data);
-                }
-
-                @Override
-                void onErrorSent(Bundle data) {
-                    receiver.send(RESULT_ERROR, data);
-                }
-            };
-
-        mCurConnection = connection;
-        onCustomAction(action, extras, result);
-        mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onCustomAction must call detach() or sendResult() or "
-                    + "sendError() before returning for action=" + action + " extras="
-                    + extras);
-        }
-    }
-
-    /**
-     * Contains information that the browser service needs to send to the client
-     * when first connected.
-     */
-    public static final class BrowserRoot {
-        /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for recently played media items.
-         *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint for retrieving media items that are recently played.
-         * If the media browser service can provide such media items, the implementation must return
-         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
-         *
-         * <p>The root hint may contain multiple keys.
-         *
-         * @see #EXTRA_OFFLINE
-         * @see #EXTRA_SUGGESTED
-         */
-        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
-
-        /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for offline media items.
-         *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint for retrieving media items that are can be played without an
-         * internet connection.
-         * If the media browser service can provide such media items, the implementation must return
-         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
-         *
-         * <p>The root hint may contain multiple keys.
-         *
-         * @see #EXTRA_RECENT
-         * @see #EXTRA_SUGGESTED
-         */
-        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
-
-        /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for suggested media items.
-         *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint for retrieving the media items suggested by the media browser
-         * service. The list of media items passed in {@link android.support.v4.media.MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded(String, List)}
-         * is considered ordered by relevance, first being the top suggestion.
-         * If the media browser service can provide such media items, the implementation must return
-         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
-         *
-         * <p>The root hint may contain multiple keys.
-         *
-         * @see #EXTRA_RECENT
-         * @see #EXTRA_OFFLINE
-         */
-        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-
-        /**
-         * The lookup key for a string that indicates specific keywords which will be considered
-         * when the browser service suggests media items.
-         *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint together with {@link #EXTRA_SUGGESTED} for retrieving suggested
-         * media items related with the keywords. The list of media items passed in
-         * {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
-         * is considered ordered by relevance, first being the top suggestion.
-         * If the media browser service can provide such media items, the implementation must return
-         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
-         *
-         * <p>The root hint may contain multiple keys.
-         *
-         * @see #EXTRA_RECENT
-         * @see #EXTRA_OFFLINE
-         * @see #EXTRA_SUGGESTED
-         * @deprecated The search functionality is now supported by the methods
-         *             {@link MediaBrowserCompat#search} and {@link #onSearch}. Use those methods
-         *             instead.
-         */
-        @Deprecated
-        public static final String EXTRA_SUGGESTION_KEYWORDS
-                = "android.service.media.extra.SUGGESTION_KEYWORDS";
-
-        final private String mRootId;
-        final private Bundle mExtras;
-
-        /**
-         * Constructs a browser root.
-         * @param rootId The root id for browsing.
-         * @param extras Any extras about the browser service.
-         */
-        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
-            if (rootId == null) {
-                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
-                        "Use null for BrowserRoot instead.");
-            }
-            mRootId = rootId;
-            mExtras = extras;
-        }
-
-        /**
-         * Gets the root id for browsing.
-         */
-        public String getRootId() {
-            return mRootId;
-        }
-
-        /**
-         * Gets any extras about the browser service.
-         */
-        public Bundle getExtras() {
-            return mExtras;
-        }
-    }
-}
diff --git a/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/android/support/v4/media/MediaBrowserServiceCompatApi21.java
deleted file mode 100644
index cff846d..0000000
--- a/android/support/v4/media/MediaBrowserServiceCompatApi21.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.media;
-
-import android.content.Context;
-import android.content.Intent;
-import android.media.browse.MediaBrowser;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.service.media.MediaBrowserService;
-import android.support.annotation.RequiresApi;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RequiresApi(21)
-class MediaBrowserServiceCompatApi21 {
-
-    public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
-        return new MediaBrowserServiceAdaptor(context, serviceProxy);
-    }
-
-    public static void onCreate(Object serviceObj) {
-        ((MediaBrowserService) serviceObj).onCreate();
-    }
-
-    public static IBinder onBind(Object serviceObj, Intent intent) {
-        return ((MediaBrowserService) serviceObj).onBind(intent);
-    }
-
-    public static void setSessionToken(Object serviceObj, Object token) {
-        ((MediaBrowserService) serviceObj).setSessionToken((MediaSession.Token) token);
-    }
-
-    public static void notifyChildrenChanged(Object serviceObj, String parentId) {
-        ((MediaBrowserService) serviceObj).notifyChildrenChanged(parentId);
-    }
-
-    public interface ServiceCompatProxy {
-        BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints);
-        void onLoadChildren(String parentId, ResultWrapper<List<Parcel>> result);
-    }
-
-    static class ResultWrapper<T> {
-        MediaBrowserService.Result mResultObj;
-
-        ResultWrapper(MediaBrowserService.Result result) {
-            mResultObj = result;
-        }
-
-        public void sendResult(T result) {
-            if (result instanceof List) {
-                mResultObj.sendResult(parcelListToItemList((List<Parcel>)result));
-            } else if (result instanceof Parcel) {
-                Parcel parcel = (Parcel) result;
-                parcel.setDataPosition(0);
-                mResultObj.sendResult(MediaBrowser.MediaItem.CREATOR.createFromParcel(parcel));
-                parcel.recycle();
-            } else {
-                // The result is null or an invalid instance.
-                mResultObj.sendResult(null);
-            }
-        }
-
-        public void detach() {
-            mResultObj.detach();
-        }
-
-        List<MediaBrowser.MediaItem> parcelListToItemList(List<Parcel> parcelList) {
-            if (parcelList == null) {
-                return null;
-            }
-            List<MediaBrowser.MediaItem> items = new ArrayList<>();
-            for (Parcel parcel : parcelList) {
-                parcel.setDataPosition(0);
-                items.add(MediaBrowser.MediaItem.CREATOR.createFromParcel(parcel));
-                parcel.recycle();
-            }
-            return items;
-        }
-    }
-
-    static class BrowserRoot {
-        final String mRootId;
-        final Bundle mExtras;
-
-        BrowserRoot(String rootId, Bundle extras) {
-            mRootId = rootId;
-            mExtras = extras;
-        }
-    }
-
-    static class MediaBrowserServiceAdaptor extends MediaBrowserService {
-        final ServiceCompatProxy mServiceProxy;
-
-        MediaBrowserServiceAdaptor(Context context, ServiceCompatProxy serviceWrapper) {
-            attachBaseContext(context);
-            mServiceProxy = serviceWrapper;
-        }
-
-        @Override
-        public MediaBrowserService.BrowserRoot onGetRoot(String clientPackageName, int clientUid,
-                Bundle rootHints) {
-            MediaBrowserServiceCompatApi21.BrowserRoot browserRoot = mServiceProxy.onGetRoot(
-                    clientPackageName, clientUid, rootHints == null ? null : new Bundle(rootHints));
-            return browserRoot == null ? null : new MediaBrowserService.BrowserRoot(
-                    browserRoot.mRootId, browserRoot.mExtras);
-        }
-
-        @Override
-        public void onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result) {
-            mServiceProxy.onLoadChildren(parentId, new ResultWrapper<List<Parcel>>(result));
-        }
-    }
-}
diff --git a/android/support/v4/media/MediaBrowserServiceCompatApi23.java b/android/support/v4/media/MediaBrowserServiceCompatApi23.java
deleted file mode 100644
index 7b30ad5..0000000
--- a/android/support/v4/media/MediaBrowserServiceCompatApi23.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.v4.media;
-
-import android.content.Context;
-import android.media.browse.MediaBrowser;
-import android.os.Parcel;
-import android.support.annotation.RequiresApi;
-import android.support.v4.media.MediaBrowserServiceCompatApi21.ResultWrapper;
-
-@RequiresApi(23)
-class MediaBrowserServiceCompatApi23 {
-
-    public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
-        return new MediaBrowserServiceAdaptor(context, serviceProxy);
-    }
-
-    public interface ServiceCompatProxy extends MediaBrowserServiceCompatApi21.ServiceCompatProxy {
-        void onLoadItem(String itemId, ResultWrapper<Parcel> result);
-    }
-
-    static class MediaBrowserServiceAdaptor extends
-            MediaBrowserServiceCompatApi21.MediaBrowserServiceAdaptor {
-        MediaBrowserServiceAdaptor(Context context, ServiceCompatProxy serviceWrapper) {
-            super(context, serviceWrapper);
-        }
-
-        @Override
-        public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
-            ((ServiceCompatProxy) mServiceProxy).onLoadItem(itemId,
-                    new ResultWrapper<Parcel>(result));
-        }
-    }
-}
diff --git a/android/support/v4/media/MediaBrowserServiceCompatApi26.java b/android/support/v4/media/MediaBrowserServiceCompatApi26.java
deleted file mode 100644
index 6f4559d..0000000
--- a/android/support/v4/media/MediaBrowserServiceCompatApi26.java
+++ /dev/null
@@ -1,108 +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 android.content.Context;
-import android.media.browse.MediaBrowser;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.service.media.MediaBrowserService;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
-@RequiresApi(26)
-class MediaBrowserServiceCompatApi26 {
-    private static final String TAG = "MBSCompatApi26";
-
-    private static Field sResultFlags;
-    static {
-        try {
-            sResultFlags = MediaBrowserService.Result.class.getDeclaredField("mFlags");
-            sResultFlags.setAccessible(true);
-        } catch (NoSuchFieldException e) {
-            Log.w(TAG, e);
-        }
-    }
-
-    public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
-        return new MediaBrowserServiceAdaptor(context, serviceProxy);
-    }
-
-    public static void notifyChildrenChanged(Object serviceObj, String parentId, Bundle options) {
-        ((MediaBrowserService) serviceObj).notifyChildrenChanged(parentId, options);
-    }
-
-    public static Bundle getBrowserRootHints(Object serviceObj) {
-        return ((MediaBrowserService) serviceObj).getBrowserRootHints();
-    }
-
-    public interface ServiceCompatProxy extends MediaBrowserServiceCompatApi23.ServiceCompatProxy {
-        void onLoadChildren(String parentId, ResultWrapper result, Bundle options);
-    }
-
-    static class ResultWrapper {
-        MediaBrowserService.Result mResultObj;
-
-        ResultWrapper(MediaBrowserService.Result result) {
-            mResultObj = result;
-        }
-
-        public void sendResult(List<Parcel> result, int flags) {
-            try {
-                sResultFlags.setInt(mResultObj, flags);
-            } catch (IllegalAccessException e) {
-                Log.w(TAG, e);
-            }
-            mResultObj.sendResult(parcelListToItemList(result));
-        }
-
-        public void detach() {
-            mResultObj.detach();
-        }
-
-        List<MediaBrowser.MediaItem> parcelListToItemList(List<Parcel> parcelList) {
-            if (parcelList == null) {
-                return null;
-            }
-            List<MediaBrowser.MediaItem> items = new ArrayList<>();
-            for (Parcel parcel : parcelList) {
-                parcel.setDataPosition(0);
-                items.add(MediaBrowser.MediaItem.CREATOR.createFromParcel(parcel));
-                parcel.recycle();
-            }
-            return items;
-        }
-    }
-
-    static class MediaBrowserServiceAdaptor extends
-            MediaBrowserServiceCompatApi23.MediaBrowserServiceAdaptor {
-        MediaBrowserServiceAdaptor(Context context, ServiceCompatProxy serviceWrapper) {
-            super(context, serviceWrapper);
-        }
-
-        @Override
-        public void onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result,
-                Bundle options) {
-            ((ServiceCompatProxy) mServiceProxy).onLoadChildren(
-                    parentId, new ResultWrapper(result), options);
-        }
-    }
-}
diff --git a/android/support/v4/media/MediaDescriptionCompat.java b/android/support/v4/media/MediaDescriptionCompat.java
index 97068f1..723cb3b 100644
--- a/android/support/v4/media/MediaDescriptionCompat.java
+++ b/android/support/v4/media/MediaDescriptionCompat.java
@@ -15,7 +15,7 @@
  */
 package android.support.v4.media;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -23,10 +23,11 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
 import android.text.TextUtils;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
 /**
  * A simple set of metadata for a media item suitable for display. This can be
  * created using the Builder or retrieved from existing metadata using
diff --git a/android/support/v4/media/MediaDescriptionCompatApi21.java b/android/support/v4/media/MediaDescriptionCompatApi21.java
index 8361711..bed7f01 100644
--- a/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -20,7 +20,8 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(21)
 class MediaDescriptionCompatApi21 {
@@ -98,5 +99,11 @@
         public static Object build(Object builderObj) {
             return ((MediaDescription.Builder) builderObj).build();
         }
+
+        private Builder() {
+        }
+    }
+
+    private MediaDescriptionCompatApi21() {
     }
 }
diff --git a/android/support/v4/media/MediaDescriptionCompatApi23.java b/android/support/v4/media/MediaDescriptionCompatApi23.java
index 957cc54..ee1b1a1 100644
--- a/android/support/v4/media/MediaDescriptionCompatApi23.java
+++ b/android/support/v4/media/MediaDescriptionCompatApi23.java
@@ -17,17 +17,24 @@
 
 import android.media.MediaDescription;
 import android.net.Uri;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(23)
-class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
+class MediaDescriptionCompatApi23 {
     public static Uri getMediaUri(Object descriptionObj) {
         return ((MediaDescription) descriptionObj).getMediaUri();
     }
 
-    static class Builder extends MediaDescriptionCompatApi21.Builder {
+    static class Builder {
         public static void setMediaUri(Object builderObj, Uri mediaUri) {
             ((MediaDescription.Builder)builderObj).setMediaUri(mediaUri);
         }
+
+        private Builder() {
+        }
+    }
+
+    private MediaDescriptionCompatApi23() {
     }
 }
diff --git a/android/support/v4/media/MediaMetadataCompat.java b/android/support/v4/media/MediaMetadataCompat.java
index 00f16cb..435d78e 100644
--- a/android/support/v4/media/MediaMetadataCompat.java
+++ b/android/support/v4/media/MediaMetadataCompat.java
@@ -15,7 +15,7 @@
  */
 package android.support.v4.media;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -23,13 +23,14 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringDef;
 import android.support.v4.media.session.MediaControllerCompat.TransportControls;
-import android.support.v4.util.ArrayMap;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.RestrictTo;
+import androidx.annotation.StringDef;
+import androidx.collection.ArrayMap;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Set;
diff --git a/android/support/v4/media/MediaMetadataCompatApi21.java b/android/support/v4/media/MediaMetadataCompatApi21.java
index 6020a08..5ce873a 100644
--- a/android/support/v4/media/MediaMetadataCompatApi21.java
+++ b/android/support/v4/media/MediaMetadataCompatApi21.java
@@ -20,7 +20,8 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.os.Parcel;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 import java.util.Set;
 
@@ -82,5 +83,11 @@
         public static Object build(Object builderObj) {
             return ((MediaMetadata.Builder)builderObj).build();
         }
+
+        private Builder() {
+        }
+    }
+
+    private MediaMetadataCompatApi21() {
     }
 }
diff --git a/android/support/v4/media/ParceledListSliceAdapterApi21.java b/android/support/v4/media/ParceledListSliceAdapterApi21.java
index 6af9903..30d0225 100644
--- a/android/support/v4/media/ParceledListSliceAdapterApi21.java
+++ b/android/support/v4/media/ParceledListSliceAdapterApi21.java
@@ -17,7 +17,8 @@
 package android.support.v4.media;
 
 import android.media.browse.MediaBrowser;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -47,4 +48,7 @@
         }
         return result;
     }
+
+    private ParceledListSliceAdapterApi21() {
+    }
 }
diff --git a/android/support/v4/media/RatingCompat.java b/android/support/v4/media/RatingCompat.java
index e70243f..1a9a033 100644
--- a/android/support/v4/media/RatingCompat.java
+++ b/android/support/v4/media/RatingCompat.java
@@ -16,16 +16,17 @@
 
 package android.support.v4.media;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.media.Rating;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.RestrictTo;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
diff --git a/android/support/v4/media/VolumeProviderCompat.java b/android/support/v4/media/VolumeProviderCompat.java
deleted file mode 100644
index 3085969..0000000
--- a/android/support/v4/media/VolumeProviderCompat.java
+++ /dev/null
@@ -1,192 +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;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Build;
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
-import android.support.v4.media.session.MediaSessionCompat;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Handles requests to adjust or set the volume on a session. This is also used
- * to push volume updates back to the session after a request has been handled.
- * You can set a volume provider on a session by calling
- * {@link MediaSessionCompat#setPlaybackToRemote}.
- */
-public abstract class VolumeProviderCompat {
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({VOLUME_CONTROL_FIXED, VOLUME_CONTROL_RELATIVE, VOLUME_CONTROL_ABSOLUTE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ControlType {}
-
-    /**
-     * The volume is fixed and can not be modified. Requests to change volume
-     * should be ignored.
-     */
-    public static final int VOLUME_CONTROL_FIXED = 0;
-
-    /**
-     * The volume control uses relative adjustment via
-     * {@link #onAdjustVolume(int)}. Attempts to set the volume to a specific
-     * value should be ignored.
-     */
-    public static final int VOLUME_CONTROL_RELATIVE = 1;
-
-    /**
-     * The volume control uses an absolute value. It may be adjusted using
-     * {@link #onAdjustVolume(int)} or set directly using
-     * {@link #onSetVolumeTo(int)}.
-     */
-    public static final int VOLUME_CONTROL_ABSOLUTE = 2;
-
-    private final int mControlType;
-    private final int mMaxVolume;
-    private int mCurrentVolume;
-    private Callback mCallback;
-
-    private Object mVolumeProviderObj;
-
-    /**
-     * Create a new volume provider for handling volume events. You must specify
-     * the type of volume control and the maximum volume that can be used.
-     *
-     * @param volumeControl The method for controlling volume that is used by
-     *            this provider.
-     * @param maxVolume The maximum allowed volume.
-     * @param currentVolume The current volume.
-     */
-    public VolumeProviderCompat(@ControlType int volumeControl, int maxVolume, int currentVolume) {
-        mControlType = volumeControl;
-        mMaxVolume = maxVolume;
-        mCurrentVolume = currentVolume;
-    }
-
-    /**
-     * Get the current volume of the provider.
-     *
-     * @return The current volume.
-     */
-    public final int getCurrentVolume() {
-        return mCurrentVolume;
-    }
-
-    /**
-     * Get the volume control type that this volume provider uses.
-     *
-     * @return The volume control type for this volume provider
-     */
-    @ControlType
-    public final int getVolumeControl() {
-        return mControlType;
-    }
-
-    /**
-     * Get the maximum volume this provider allows.
-     *
-     * @return The max allowed volume.
-     */
-    public final int getMaxVolume() {
-        return mMaxVolume;
-    }
-
-    /**
-     * Set the current volume and notify the system that the volume has been
-     * changed.
-     *
-     * @param currentVolume The current volume of the output.
-     */
-    public final void setCurrentVolume(int currentVolume) {
-        mCurrentVolume = currentVolume;
-        Object volumeProviderObj = getVolumeProvider();
-        if (volumeProviderObj != null && Build.VERSION.SDK_INT >= 21) {
-            VolumeProviderCompatApi21.setCurrentVolume(volumeProviderObj, currentVolume);
-        }
-        if (mCallback != null) {
-            mCallback.onVolumeChanged(this);
-        }
-    }
-
-    /**
-     * Override to handle requests to set the volume of the current output.
-     *
-     * @param volume The volume to set the output to.
-     */
-    public void onSetVolumeTo(int volume) {
-    }
-
-    /**
-     * Override to handle requests to adjust the volume of the current output.
-     *
-     * @param direction The direction to adjust the volume in.
-     */
-    public void onAdjustVolume(int direction) {
-    }
-
-    /**
-     * Sets a callback to receive volume changes.
-     * <p>
-     * Used internally by the support library.
-     * <p>
-     */
-    public void setCallback(Callback callback) {
-        mCallback = callback;
-    }
-
-    /**
-     * Gets the underlying framework {@link android.media.VolumeProvider} object.
-     * <p>
-     * This method is only supported on API 21+.
-     * </p>
-     *
-     * @return An equivalent {@link android.media.VolumeProvider} object, or null if none.
-     */
-    public Object getVolumeProvider() {
-        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);
-                        }
-                    });
-        }
-        return mVolumeProviderObj;
-    }
-
-    /**
-     * Listens for changes to the volume.
-     */
-    public static abstract class Callback {
-        public abstract void onVolumeChanged(VolumeProviderCompat volumeProvider);
-    }
-}
diff --git a/android/support/v4/media/VolumeProviderCompatApi21.java b/android/support/v4/media/VolumeProviderCompatApi21.java
deleted file mode 100644
index cefbf59..0000000
--- a/android/support/v4/media/VolumeProviderCompatApi21.java
+++ /dev/null
@@ -1,47 +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;
-
-import android.media.VolumeProvider;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(21)
-class VolumeProviderCompatApi21 {
-    public static Object createVolumeProvider(int volumeControl, int maxVolume, int currentVolume,
-            final Delegate delegate) {
-        return new VolumeProvider(volumeControl, maxVolume, currentVolume) {
-            @Override
-            public void onSetVolumeTo(int volume) {
-                delegate.onSetVolumeTo(volume);
-            }
-
-            @Override
-            public void onAdjustVolume(int direction) {
-                delegate.onAdjustVolume(direction);
-            }
-        };
-    }
-
-    public static void setCurrentVolume(Object volumeProviderObj, int currentVolume) {
-        ((VolumeProvider) volumeProviderObj).setCurrentVolume(currentVolume);
-    }
-
-    public interface Delegate {
-        void onSetVolumeTo(int volume);
-        void onAdjustVolume(int delta);
-    }
-}
diff --git a/android/support/v4/media/app/NotificationCompat.java b/android/support/v4/media/app/NotificationCompat.java
deleted file mode 100644
index be1d423..0000000
--- a/android/support/v4/media/app/NotificationCompat.java
+++ /dev/null
@@ -1,502 +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.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/android/support/v4/media/session/MediaButtonReceiver.java b/android/support/v4/media/session/MediaButtonReceiver.java
deleted file mode 100644
index d411131..0000000
--- a/android/support/v4/media/session/MediaButtonReceiver.java
+++ /dev/null
@@ -1,310 +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.media.session;
-
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-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;
-import android.view.KeyEvent;
-
-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.
- * <p />
- * You can add this MediaButtonReceiver to your app by adding it directly to your
- * AndroidManifest.xml:
- * <pre>
- * &lt;receiver android:name="android.support.v4.media.session.MediaButtonReceiver" &gt;
- *   &lt;intent-filter&gt;
- *     &lt;action android:name="android.intent.action.MEDIA_BUTTON" /&gt;
- *   &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}. 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 />
- *
- * <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;
- *     &lt;action android:name="android.intent.action.MEDIA_BUTTON" /&gt;
- *   &lt;/intent-filter&gt;
- * &lt;/service&gt;
- * </pre>
- *
- * 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}:
- * <pre>
- * private MediaSessionCompat mMediaSessionCompat = ...;
- *
- * public int onStartCommand(Intent intent, int flags, int startId) {
- *   MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent);
- *   return super.onStartCommand(intent, flags, startId);
- * }
- * </pre>
- *
- * 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) {
-        if (intent == null
-                || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
-                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
-            Log.d(TAG, "Ignore unsupported intent: " + intent);
-            return;
-        }
-        ComponentName mediaButtonServiceComponentName =
-                getServiceComponentByAction(context, Intent.ACTION_MEDIA_BUTTON);
-        if (mediaButtonServiceComponentName != null) {
-            intent.setComponent(mediaButtonServiceComponentName);
-            startForegroundService(context, intent);
-            return;
-        }
-        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)}.
-     * @param mediaSessionCompat A {@link MediaSessionCompat} that has a
-     *            {@link MediaSessionCompat.Callback} set.
-     * @param intent The intent to parse.
-     * @return The extracted {@link KeyEvent} if found, or null.
-     */
-    public static KeyEvent handleIntent(MediaSessionCompat mediaSessionCompat, Intent intent) {
-        if (mediaSessionCompat == null || intent == null
-                || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
-                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
-            return null;
-        }
-        KeyEvent ke = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
-        MediaControllerCompat mediaController = mediaSessionCompat.getController();
-        mediaController.dispatchMediaButtonEvent(ke);
-        return ke;
-    }
-
-    /**
-     * Creates a broadcast pending intent that will send a media button event. The {@code action}
-     * will be translated to the appropriate {@link KeyEvent}, and it will be sent to the
-     * registered media button receiver in the given context. The {@code action} should be one of
-     * the following:
-     * <ul>
-     * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_STOP}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li>
-     * </ul>
-     *
-     * @param context The context of the application.
-     * @param action The action to be sent via the pending intent.
-     * @return Created pending intent, or null if cannot find a unique registered media button
-     *         receiver or if the {@code action} is unsupported/invalid.
-     */
-    public static PendingIntent buildMediaButtonPendingIntent(Context context,
-            @MediaKeyAction long action) {
-        ComponentName mbrComponent = getMediaButtonReceiverComponent(context);
-        if (mbrComponent == null) {
-            Log.w(TAG, "A unique media button receiver could not be found in the given context, so "
-                    + "couldn't build a pending intent.");
-            return null;
-        }
-        return buildMediaButtonPendingIntent(context, mbrComponent, action);
-    }
-
-    /**
-     * Creates a broadcast pending intent that will send a media button event. The {@code action}
-     * will be translated to the appropriate {@link KeyEvent}, and sent to the provided media
-     * button receiver via the pending intent. The {@code action} should be one of the following:
-     * <ul>
-     * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_STOP}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li>
-     * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li>
-     * </ul>
-     *
-     * @param context The context of the application.
-     * @param mbrComponent The full component name of a media button receiver where you want to send
-     *            this intent.
-     * @param action The action to be sent via the pending intent.
-     * @return Created pending intent, or null if the given component name is null or the
-     *         {@code action} is unsupported/invalid.
-     */
-    public static PendingIntent buildMediaButtonPendingIntent(Context context,
-            ComponentName mbrComponent, @MediaKeyAction long action) {
-        if (mbrComponent == null) {
-            Log.w(TAG, "The component name of media button receiver should be provided.");
-            return null;
-        }
-        int keyCode = PlaybackStateCompat.toKeyCode(action);
-        if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
-            Log.w(TAG,
-                    "Cannot build a media button pending intent with the given action: " + action);
-            return null;
-        }
-        Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-        intent.setComponent(mbrComponent);
-        intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
-        return PendingIntent.getBroadcast(context, keyCode, intent, 0);
-    }
-
-    static ComponentName getMediaButtonReceiverComponent(Context context) {
-        Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-        queryIntent.setPackage(context.getPackageName());
-        PackageManager pm = context.getPackageManager();
-        List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(queryIntent, 0);
-        if (resolveInfos.size() == 1) {
-            ResolveInfo resolveInfo = resolveInfos.get(0);
-            return new ComponentName(resolveInfo.activityInfo.packageName,
-                    resolveInfo.activityInfo.name);
-        } else if (resolveInfos.size() > 1) {
-            Log.w(TAG, "More than one BroadcastReceiver that handles "
-                    + Intent.ACTION_MEDIA_BUTTON + " was found, returning null.");
-        }
-        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/android/support/v4/media/session/MediaControllerCompat.java b/android/support/v4/media/session/MediaControllerCompat.java
index f24da1e..5e6f4ea 100644
--- a/android/support/v4/media/session/MediaControllerCompat.java
+++ b/android/support/v4/media/session/MediaControllerCompat.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
 
 package android.support.v4.media.session;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -29,20 +31,23 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-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.MediaBrowserCompat;
 import android.support.v4.media.MediaDescriptionCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
-import android.support.v4.media.VolumeProviderCompat;
 import android.support.v4.media.session.MediaSessionCompat.QueueItem;
 import android.support.v4.media.session.PlaybackStateCompat.CustomAction;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.app.BundleCompat;
+import androidx.core.app.SupportActivity;
+import androidx.media.VolumeProviderCompat;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -65,7 +70,7 @@
  * If MediaControllerCompat is created with a {@link MediaSessionCompat.Token session token}
  * from another process, following methods will not work directly after the creation if the
  * {@link MediaSessionCompat.Token session token} is not passed through a
- * {@link android.support.v4.media.MediaBrowserCompat}:
+ * {@link MediaBrowserCompat}:
  * <ul>
  * <li>{@link #getPlaybackState()}.{@link PlaybackStateCompat#getExtras() getExtras()}</li>
  * <li>{@link #isCaptioningEnabled()}</li>
@@ -82,20 +87,48 @@
 public final class MediaControllerCompat {
     static final String TAG = "MediaControllerCompat";
 
-    static final String COMMAND_GET_EXTRA_BINDER =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_GET_EXTRA_BINDER =
             "android.support.v4.media.session.command.GET_EXTRA_BINDER";
-    static final String COMMAND_ADD_QUEUE_ITEM =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_ADD_QUEUE_ITEM =
             "android.support.v4.media.session.command.ADD_QUEUE_ITEM";
-    static final String COMMAND_ADD_QUEUE_ITEM_AT =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_ADD_QUEUE_ITEM_AT =
             "android.support.v4.media.session.command.ADD_QUEUE_ITEM_AT";
-    static final String COMMAND_REMOVE_QUEUE_ITEM =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_REMOVE_QUEUE_ITEM =
             "android.support.v4.media.session.command.REMOVE_QUEUE_ITEM";
-    static final String COMMAND_REMOVE_QUEUE_ITEM_AT =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_REMOVE_QUEUE_ITEM_AT =
             "android.support.v4.media.session.command.REMOVE_QUEUE_ITEM_AT";
 
-    static final String COMMAND_ARGUMENT_MEDIA_DESCRIPTION =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_ARGUMENT_MEDIA_DESCRIPTION =
             "android.support.v4.media.session.command.ARGUMENT_MEDIA_DESCRIPTION";
-    static final String COMMAND_ARGUMENT_INDEX =
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String COMMAND_ARGUMENT_INDEX =
             "android.support.v4.media.session.command.ARGUMENT_INDEX";
 
     private static class MediaControllerExtraData extends SupportActivity.ExtraData {
@@ -115,7 +148,7 @@
      * {@link #getMediaController(Activity)}.
      *
      * <p>This is compatible with {@link Activity#setMediaController(MediaController)}.
-     * If {@code activity} inherits {@link android.support.v4.app.FragmentActivity}, the
+     * If {@code activity} inherits {@link androidx.fragment.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>
diff --git a/android/support/v4/media/session/MediaControllerCompatApi21.java b/android/support/v4/media/session/MediaControllerCompatApi21.java
index c13d901..0cd1cd9 100644
--- a/android/support/v4/media/session/MediaControllerCompatApi21.java
+++ b/android/support/v4/media/session/MediaControllerCompatApi21.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,9 +29,10 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.ResultReceiver;
-import android.support.annotation.RequiresApi;
 import android.view.KeyEvent;
 
+import androidx.annotation.RequiresApi;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -185,6 +186,9 @@
         public static void sendCustomAction(Object controlsObj, String action, Bundle args) {
             ((MediaController.TransportControls) controlsObj).sendCustomAction(action, args);
         }
+
+        private TransportControls() {
+        }
     }
 
     public static class PlaybackInfo {
@@ -257,6 +261,9 @@
                     return AudioManager.STREAM_MUSIC;
             }
         }
+
+        private PlaybackInfo() {
+        }
     }
 
     public static interface Callback {
@@ -319,4 +326,7 @@
                     info.getMaxVolume(), info.getCurrentVolume());
         }
     }
+
+    private MediaControllerCompatApi21() {
+    }
 }
diff --git a/android/support/v4/media/session/MediaControllerCompatApi23.java b/android/support/v4/media/session/MediaControllerCompatApi23.java
index 12ce345..c5137b5 100644
--- a/android/support/v4/media/session/MediaControllerCompatApi23.java
+++ b/android/support/v4/media/session/MediaControllerCompatApi23.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,14 +19,21 @@
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(23)
 class MediaControllerCompatApi23 {
 
-    public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
+    public static class TransportControls {
         public static void playFromUri(Object controlsObj, Uri uri, Bundle extras) {
             ((MediaController.TransportControls) controlsObj).playFromUri(uri, extras);
         }
+
+        private TransportControls() {
+        }
+    }
+
+    private MediaControllerCompatApi23() {
     }
 }
diff --git a/android/support/v4/media/session/MediaControllerCompatApi24.java b/android/support/v4/media/session/MediaControllerCompatApi24.java
index c336d70..f130ed0 100644
--- a/android/support/v4/media/session/MediaControllerCompatApi24.java
+++ b/android/support/v4/media/session/MediaControllerCompatApi24.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,12 +19,13 @@
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(24)
 class MediaControllerCompatApi24 {
 
-    public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
+    public static class TransportControls {
         public static void prepare(Object controlsObj) {
             ((MediaController.TransportControls) controlsObj).prepare();
         }
@@ -40,5 +41,11 @@
         public static void prepareFromUri(Object controlsObj, Uri uri, Bundle extras) {
             ((MediaController.TransportControls) controlsObj).prepareFromUri(uri, extras);
         }
+
+        private TransportControls() {
+        }
+    }
+
+    private MediaControllerCompatApi24() {
     }
 }
diff --git a/android/support/v4/media/session/MediaSessionCompat.java b/android/support/v4/media/session/MediaSessionCompat.java
index 8b91413..1d78fc8 100644
--- a/android/support/v4/media/session/MediaSessionCompat.java
+++ b/android/support/v4/media/session/MediaSessionCompat.java
@@ -16,7 +16,8 @@
 
 package android.support.v4.media.session;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.app.Activity;
 import android.app.PendingIntent;
@@ -44,20 +45,22 @@
 import android.os.RemoteException;
 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;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
-import android.support.v4.media.VolumeProviderCompat;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.ViewConfiguration;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.app.BundleCompat;
+import androidx.media.VolumeProviderCompat;
+import androidx.media.session.MediaButtonReceiver;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -219,108 +222,163 @@
 
     /**
      * Custom action to invoke playFromUri() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_PLAY_FROM_URI =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_PLAY_FROM_URI =
             "android.support.v4.media.session.action.PLAY_FROM_URI";
 
     /**
      * Custom action to invoke prepare() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_PREPARE = "android.support.v4.media.session.action.PREPARE";
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_PREPARE = "android.support.v4.media.session.action.PREPARE";
 
     /**
      * Custom action to invoke prepareFromMediaId() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_PREPARE_FROM_MEDIA_ID =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_PREPARE_FROM_MEDIA_ID =
             "android.support.v4.media.session.action.PREPARE_FROM_MEDIA_ID";
 
     /**
      * Custom action to invoke prepareFromSearch() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_PREPARE_FROM_SEARCH =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_PREPARE_FROM_SEARCH =
             "android.support.v4.media.session.action.PREPARE_FROM_SEARCH";
 
     /**
      * Custom action to invoke prepareFromUri() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_PREPARE_FROM_URI =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_PREPARE_FROM_URI =
             "android.support.v4.media.session.action.PREPARE_FROM_URI";
 
     /**
      * Custom action to invoke setCaptioningEnabled() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_SET_CAPTIONING_ENABLED =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_SET_CAPTIONING_ENABLED =
             "android.support.v4.media.session.action.SET_CAPTIONING_ENABLED";
 
     /**
      * Custom action to invoke setRepeatMode() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_SET_REPEAT_MODE =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_SET_REPEAT_MODE =
             "android.support.v4.media.session.action.SET_REPEAT_MODE";
 
     /**
      * Custom action to invoke setShuffleMode() for the forward compatibility.
+     *
+     * @hide
      */
-    static final String ACTION_SET_SHUFFLE_MODE =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_SET_SHUFFLE_MODE =
             "android.support.v4.media.session.action.SET_SHUFFLE_MODE";
 
     /**
      * Custom action to invoke setRating() with extra fields.
+     *
+     * @hide
      */
-    static final String ACTION_SET_RATING =
+    @RestrictTo(LIBRARY)
+    public 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.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_MEDIA_ID =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_MEDIA_ID =
             "android.support.v4.media.session.action.ARGUMENT_MEDIA_ID";
 
     /**
      * Argument for use with {@link #ACTION_PREPARE_FROM_SEARCH} indicating search query.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_QUERY =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_QUERY =
             "android.support.v4.media.session.action.ARGUMENT_QUERY";
 
     /**
      * Argument for use with {@link #ACTION_PREPARE_FROM_URI} and {@link #ACTION_PLAY_FROM_URI}
      * indicating URI to play.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_URI =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_URI =
             "android.support.v4.media.session.action.ARGUMENT_URI";
 
     /**
      * Argument for use with {@link #ACTION_SET_RATING} indicating the rate to be set.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_RATING =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_RATING =
             "android.support.v4.media.session.action.ARGUMENT_RATING";
 
     /**
      * Argument for use with various actions indicating extra bundle.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_EXTRAS =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_EXTRAS =
             "android.support.v4.media.session.action.ARGUMENT_EXTRAS";
 
     /**
      * Argument for use with {@link #ACTION_SET_CAPTIONING_ENABLED} indicating whether captioning is
      * enabled.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_CAPTIONING_ENABLED =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_CAPTIONING_ENABLED =
             "android.support.v4.media.session.action.ARGUMENT_CAPTIONING_ENABLED";
 
     /**
      * Argument for use with {@link #ACTION_SET_REPEAT_MODE} indicating repeat mode.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_REPEAT_MODE =
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_REPEAT_MODE =
             "android.support.v4.media.session.action.ARGUMENT_REPEAT_MODE";
 
     /**
      * Argument for use with {@link #ACTION_SET_SHUFFLE_MODE} indicating shuffle mode.
+     *
+     * @hide
      */
-    static final String ACTION_ARGUMENT_SHUFFLE_MODE =
+    @RestrictTo(LIBRARY)
+    public 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";
+    /**
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String EXTRA_BINDER = "android.support.v4.media.session.EXTRA_BINDER";
 
     // Maximum size of the bitmap in dp.
     private static final int MAX_BITMAP_SIZE_IN_DP = 320;
@@ -1690,8 +1748,11 @@
      * This is a wrapper for {@link ResultReceiver} for sending over aidl
      * interfaces. The framework version was not exposed to aidls until
      * {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
+     *
+     * @hide
      */
-    static final class ResultReceiverWrapper implements Parcelable {
+    @RestrictTo(LIBRARY)
+    public static final class ResultReceiverWrapper implements Parcelable {
         private ResultReceiver mResultReceiver;
 
         public ResultReceiverWrapper(ResultReceiver resultReceiver) {
diff --git a/android/support/v4/media/session/MediaSessionCompatApi21.java b/android/support/v4/media/session/MediaSessionCompatApi21.java
index 698e37d..a359704 100644
--- a/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -30,9 +30,10 @@
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.ResultReceiver;
-import android.support.annotation.RequiresApi;
 import android.util.Log;
 
+import androidx.annotation.RequiresApi;
+
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
@@ -268,5 +269,11 @@
         public static long getQueueId(Object queueItem) {
             return ((MediaSession.QueueItem) queueItem).getQueueId();
         }
+
+        private QueueItem() {
+        }
+    }
+
+    private MediaSessionCompatApi21() {
     }
 }
diff --git a/android/support/v4/media/session/MediaSessionCompatApi22.java b/android/support/v4/media/session/MediaSessionCompatApi22.java
index b7ddc99..9a44d8c 100644
--- a/android/support/v4/media/session/MediaSessionCompatApi22.java
+++ b/android/support/v4/media/session/MediaSessionCompatApi22.java
@@ -16,7 +16,8 @@
 package android.support.v4.media.session;
 
 import android.media.session.MediaSession;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(22)
 class MediaSessionCompatApi22 {
@@ -24,4 +25,7 @@
     public static void setRatingType(Object sessionObj, int type) {
         ((MediaSession) sessionObj).setRatingType(type);
     }
+
+    private MediaSessionCompatApi22() {
+    }
 }
diff --git a/android/support/v4/media/session/MediaSessionCompatApi23.java b/android/support/v4/media/session/MediaSessionCompatApi23.java
index 2818f02..586ffda 100644
--- a/android/support/v4/media/session/MediaSessionCompatApi23.java
+++ b/android/support/v4/media/session/MediaSessionCompatApi23.java
@@ -18,7 +18,8 @@
 
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 @RequiresApi(23)
 class MediaSessionCompatApi23 {
@@ -41,4 +42,7 @@
             mCallback.onPlayFromUri(uri, extras);
         }
     }
+
+    private MediaSessionCompatApi23() {
+    }
 }
diff --git a/android/support/v4/media/session/MediaSessionCompatApi24.java b/android/support/v4/media/session/MediaSessionCompatApi24.java
index cd358e5..818277f 100644
--- a/android/support/v4/media/session/MediaSessionCompatApi24.java
+++ b/android/support/v4/media/session/MediaSessionCompatApi24.java
@@ -19,9 +19,10 @@
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
 import android.util.Log;
 
+import androidx.annotation.RequiresApi;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
@@ -77,4 +78,7 @@
             mCallback.onPrepareFromUri(uri, extras);
         }
     }
+
+    private MediaSessionCompatApi24() {
+    }
 }
diff --git a/android/support/v4/media/session/PlaybackStateCompat.java b/android/support/v4/media/session/PlaybackStateCompat.java
index 3b06125..e6420ea 100644
--- a/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/android/support/v4/media/session/PlaybackStateCompat.java
@@ -16,20 +16,21 @@
 package android.support.v4.media.session;
 
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.support.annotation.IntDef;
-import android.support.annotation.LongDef;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
 import android.text.TextUtils;
 import android.view.KeyEvent;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.LongDef;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
diff --git a/android/support/v4/media/session/PlaybackStateCompatApi21.java b/android/support/v4/media/session/PlaybackStateCompatApi21.java
index 577f35d..f4aa9fc 100644
--- a/android/support/v4/media/session/PlaybackStateCompatApi21.java
+++ b/android/support/v4/media/session/PlaybackStateCompatApi21.java
@@ -18,7 +18,8 @@
 
 import android.media.session.PlaybackState;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 import java.util.List;
 
@@ -99,5 +100,11 @@
             customActionObj.setExtras(extras);
             return customActionObj.build();
         }
+
+        private CustomAction() {
+        }
+    }
+
+    private PlaybackStateCompatApi21() {
     }
 }
diff --git a/android/support/v4/media/session/PlaybackStateCompatApi22.java b/android/support/v4/media/session/PlaybackStateCompatApi22.java
index 6be3f4b..1be825f 100644
--- a/android/support/v4/media/session/PlaybackStateCompatApi22.java
+++ b/android/support/v4/media/session/PlaybackStateCompatApi22.java
@@ -18,7 +18,8 @@
 
 import android.media.session.PlaybackState;
 import android.os.Bundle;
-import android.support.annotation.RequiresApi;
+
+import androidx.annotation.RequiresApi;
 
 import java.util.List;
 
@@ -44,4 +45,7 @@
         stateObj.setExtras(extras);
         return stateObj.build();
     }
+
+    private PlaybackStateCompatApi22() {
+    }
 }
diff --git a/android/support/v4/net/ConnectivityManagerCompat.java b/android/support/v4/net/ConnectivityManagerCompat.java
deleted file mode 100644
index c08cac8..0000000
--- a/android/support/v4/net/ConnectivityManagerCompat.java
+++ /dev/null
@@ -1,163 +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 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;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Build;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-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}.
- */
-public final class ConnectivityManagerCompat {
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {
-            RESTRICT_BACKGROUND_STATUS_DISABLED,
-            RESTRICT_BACKGROUND_STATUS_WHITELISTED,
-            RESTRICT_BACKGROUND_STATUS_ENABLED,
-    })
-    public @interface RestrictBackgroundStatus {
-    }
-
-    /**
-     * Device is not restricting metered network activity while application is running on
-     * background.
-     */
-    public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1;
-
-    /**
-     * Device is restricting metered network activity while application is running on background,
-     * but application is allowed to bypass it.
-     * <p>
-     * In this state, application should take action to mitigate metered network access.
-     * For example, a music streaming application should switch to a low-bandwidth bitrate.
-     */
-    public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2;
-
-    /**
-     * Device is restricting metered network activity while application is running on background.
-     * <p>
-     * In this state, application should not try to use the network while running on background,
-     * because it would be denied.
-     */
-    public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3;
-
-    /**
-     * Returns if the currently active data network is metered. A network is
-     * classified as metered when the user is sensitive to heavy data usage on
-     * that connection due to monetary costs, data limitations or
-     * battery/performance issues. You should check this before doing large
-     * data transfers, and warn the user or delay the operation until another
-     * network is available.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
-     *
-     * @return {@code true} if large transfers should be avoided, otherwise
-     *        {@code false}.
-     */
-    @SuppressWarnings("deprecation")
-    @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public static boolean isActiveNetworkMetered(@NonNull ConnectivityManager cm) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return cm.isActiveNetworkMetered();
-        } else {
-            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;
-            }
-        }
-    }
-
-    /**
-     * Return the {@link NetworkInfo} that caused the given
-     * {@link ConnectivityManager#CONNECTIVITY_ACTION} broadcast. This obtains
-     * the current state from {@link ConnectivityManager} instead of using the
-     * potentially-stale value from
-     * {@link ConnectivityManager#EXTRA_NETWORK_INFO}. May be {@code null}.
-     */
-    @Nullable
-    @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
-    public static NetworkInfo getNetworkInfoFromBroadcast(@NonNull ConnectivityManager cm,
-            @NonNull Intent intent) {
-        final NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
-        if (info != null) {
-            return cm.getNetworkInfo(info.getType());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Determines if the calling application is subject to metered network restrictions while
-     * running on background.
-     *
-     * @return {@link #RESTRICT_BACKGROUND_STATUS_DISABLED},
-     *         {@link #RESTRICT_BACKGROUND_STATUS_ENABLED},
-     *         or {@link #RESTRICT_BACKGROUND_STATUS_WHITELISTED}
-     */
-    @RestrictBackgroundStatus
-    public static int getRestrictBackgroundStatus(@NonNull ConnectivityManager cm) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return cm.getRestrictBackgroundStatus();
-        } else {
-            return RESTRICT_BACKGROUND_STATUS_ENABLED;
-        }
-    }
-
-    private ConnectivityManagerCompat() {}
-}
diff --git a/android/support/v4/net/DatagramSocketWrapper.java b/android/support/v4/net/DatagramSocketWrapper.java
deleted file mode 100644
index 57d3caf..0000000
--- a/android/support/v4/net/DatagramSocketWrapper.java
+++ /dev/null
@@ -1,115 +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 java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.SocketImpl;
-
-class DatagramSocketWrapper extends Socket {
-    DatagramSocketWrapper(DatagramSocket socket, FileDescriptor fd) throws SocketException {
-        super(new DatagramSocketImplWrapper(socket, fd));
-    }
-
-    /**
-     * Empty implementation which wires in the given {@link FileDescriptor}.
-     */
-    private static class DatagramSocketImplWrapper extends SocketImpl {
-        DatagramSocketImplWrapper(DatagramSocket socket, FileDescriptor fd) {
-            super();
-            this.localport = socket.getLocalPort();
-            this.fd = fd;
-        }
-
-        @Override
-        protected void accept(SocketImpl newSocket) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected int available() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void bind(InetAddress address, int port) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void close() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void connect(String host, int port) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void connect(InetAddress address, int port) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void create(boolean isStreaming) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected InputStream getInputStream() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected OutputStream getOutputStream() throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void listen(int backlog) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void connect(SocketAddress remoteAddr, int timeout) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected void sendUrgentData(int value) throws IOException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public Object getOption(int optID) throws SocketException {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void setOption(int optID, Object val) throws SocketException {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/android/support/v4/net/TrafficStatsCompat.java b/android/support/v4/net/TrafficStatsCompat.java
deleted file mode 100644
index b74b8d0..0000000
--- a/android/support/v4/net/TrafficStatsCompat.java
+++ /dev/null
@@ -1,167 +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.Build;
-import android.os.ParcelFileDescriptor;
-import android.support.annotation.NonNull;
-
-import java.net.DatagramSocket;
-import java.net.Socket;
-import java.net.SocketException;
-
-/**
- * Helper for accessing features in {@link TrafficStats}.
- */
-public final class TrafficStatsCompat {
-    /**
-     * 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() {
-        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 TrafficStats.getThreadStatsTag();
-    }
-
-    /**
-     * Increment count of network operations performed under the accounting tag
-     * currently active on the calling thread. This can be used to derive
-     * 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) {
-        TrafficStats.incrementOperationCount(operationCount);
-    }
-
-    /**
-     * Increment count of network operations performed under the given
-     * accounting tag. This can be used to derive bytes-per-operation.
-     *
-     * @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) {
-        TrafficStats.incrementOperationCount(tag, operationCount);
-    }
-
-    /**
-     * Set active tag to use when accounting {@link Socket} traffic originating
-     * from the current thread. Only one active tag per thread is supported.
-     * <p>
-     * Changes only take effect during subsequent calls to
-     * {@link #tagSocket(Socket)}.
-     * <p>
-     * 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) {
-        TrafficStats.setThreadStatsTag(tag);
-    }
-
-    /**
-     * Tag the given {@link Socket} with any statistics parameters active for
-     * the current thread. Subsequent calls always replace any existing
-     * parameters. When finished, call {@link #untagSocket(Socket)} to remove
-     * statistics parameters.
-     *
-     * @see #setThreadStatsTag(int)
-     *
-     * @deprecated Use {@link TrafficStats#tagSocket(Socket)} directly.
-     */
-    @Deprecated
-    public static void tagSocket(Socket socket) throws SocketException {
-        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 {
-        TrafficStats.untagSocket(socket);
-    }
-
-    /**
-     * Tag the given {@link DatagramSocket} with any statistics parameters
-     * active for the current thread. Subsequent calls always replace any
-     * existing parameters. When finished, call
-     * {@link #untagDatagramSocket(DatagramSocket)} to remove statistics
-     * parameters.
-     *
-     * @see #setThreadStatsTag(int)
-     */
-    public static void tagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
-        if (Build.VERSION.SDK_INT >= 24) {
-            TrafficStats.tagDatagramSocket(socket);
-        } else {
-            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();
-        }
-    }
-
-    /**
-     * Remove any statistics parameters from the given {@link DatagramSocket}.
-     */
-    public static void untagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
-        if (Build.VERSION.SDK_INT >= 24) {
-            TrafficStats.untagDatagramSocket(socket);
-        } else {
-            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();
-        }
-    }
-
-    private TrafficStatsCompat() {}
-}
diff --git a/android/support/v4/os/BuildCompat.java b/android/support/v4/os/BuildCompat.java
deleted file mode 100644
index 586557d..0000000
--- a/android/support/v4/os/BuildCompat.java
+++ /dev/null
@@ -1,93 +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;
-
-/**
- * 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}.
-     */
-    @Deprecated
-    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>
-     * @return {@code true} if O MR1 APIs are available for use, {@code false} otherwise
-     * @deprecated Android O 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#O_MR1}.
-     */
-    @Deprecated
-    public static boolean isAtLeastOMR1() {
-        return VERSION.SDK_INT >= 27;
-    }
-
-    /**
-     * 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/android/support/v4/os/CancellationSignal.java b/android/support/v4/os/CancellationSignal.java
deleted file mode 100644
index b6c0dc9..0000000
--- a/android/support/v4/os/CancellationSignal.java
+++ /dev/null
@@ -1,167 +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.os;
-
-import android.os.Build;
-
-/**
- * Static library support version of the framework's {@link android.os.CancellationSignal}.
- * Used to write apps that run on platforms prior to Android 4.1.  See the framework SDK
- * documentation for a class overview.
- */
-public final class CancellationSignal {
-    private boolean mIsCanceled;
-    private OnCancelListener mOnCancelListener;
-    private Object mCancellationSignalObj;
-    private boolean mCancelInProgress;
-
-    /**
-     * Creates a cancellation signal, initially not canceled.
-     */
-    public CancellationSignal() {
-    }
-
-    /**
-     * Returns true if the operation has been canceled.
-     *
-     * @return True if the operation has been canceled.
-     */
-    public boolean isCanceled() {
-        synchronized (this) {
-            return mIsCanceled;
-        }
-    }
-
-    /**
-     * Throws {@link OperationCanceledException} if the operation has been canceled.
-     *
-     * @throws OperationCanceledException if the operation has been canceled.
-     */
-    public void throwIfCanceled() {
-        if (isCanceled()) {
-            throw new OperationCanceledException();
-        }
-    }
-
-    /**
-     * Cancels the operation and signals the cancellation listener.
-     * If the operation has not yet started, then it will be canceled as soon as it does.
-     */
-    public void cancel() {
-        final OnCancelListener listener;
-        final Object obj;
-        synchronized (this) {
-            if (mIsCanceled) {
-                return;
-            }
-            mIsCanceled = true;
-            mCancelInProgress = true;
-            listener = mOnCancelListener;
-            obj = mCancellationSignalObj;
-        }
-
-        try {
-            if (listener != null) {
-                listener.onCancel();
-            }
-            if (obj != null && Build.VERSION.SDK_INT >= 16) {
-                ((android.os.CancellationSignal) obj).cancel();
-            }
-        } finally {
-            synchronized (this) {
-                mCancelInProgress = false;
-                notifyAll();
-            }
-        }
-    }
-
-    /**
-     * Sets the cancellation listener to be called when canceled.
-     *
-     * This method is intended to be used by the recipient of a cancellation signal
-     * such as a database or a content provider to handle cancellation requests
-     * while performing a long-running operation.  This method is not intended to be
-     * used by applications themselves.
-     *
-     * If {@link CancellationSignal#cancel} has already been called, then the provided
-     * listener is invoked immediately.
-     *
-     * This method is guaranteed that the listener will not be called after it
-     * has been removed.
-     *
-     * @param listener The cancellation listener, or null to remove the current listener.
-     */
-    public void setOnCancelListener(OnCancelListener listener) {
-        synchronized (this) {
-            waitForCancelFinishedLocked();
-
-            if (mOnCancelListener == listener) {
-                return;
-            }
-            mOnCancelListener = listener;
-            if (!mIsCanceled || listener == null) {
-                return;
-            }
-        }
-        listener.onCancel();
-    }
-
-    /**
-     * Gets the framework {@link android.os.CancellationSignal} associated with this object.
-     * <p>
-     * Framework support for cancellation signals was added in
-     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} so this method will always
-     * return null on older versions of the platform.
-     * </p>
-     *
-     * @return A framework cancellation signal object, or null on platform versions
-     * prior to Jellybean.
-     */
-    public Object getCancellationSignalObject() {
-        if (Build.VERSION.SDK_INT < 16) {
-            return null;
-        }
-        synchronized (this) {
-            if (mCancellationSignalObj == null) {
-                mCancellationSignalObj = new android.os.CancellationSignal();
-                if (mIsCanceled) {
-                    ((android.os.CancellationSignal) mCancellationSignalObj).cancel();
-                }
-            }
-            return mCancellationSignalObj;
-        }
-    }
-
-    private void waitForCancelFinishedLocked() {
-        while (mCancelInProgress) {
-            try {
-                wait();
-            } catch (InterruptedException ex) {
-            }
-        }
-    }
-
-     /**
-     * Listens for cancellation.
-     */
-    public interface OnCancelListener {
-        /**
-         * Called when {@link CancellationSignal#cancel} is invoked.
-         */
-        void onCancel();
-    }
-}
diff --git a/android/support/v4/os/ConfigurationCompat.java b/android/support/v4/os/ConfigurationCompat.java
deleted file mode 100644
index 95de506..0000000
--- a/android/support/v4/os/ConfigurationCompat.java
+++ /dev/null
@@ -1,43 +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.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/android/support/v4/os/EnvironmentCompat.java b/android/support/v4/os/EnvironmentCompat.java
deleted file mode 100644
index 3e9e7f9..0000000
--- a/android/support/v4/os/EnvironmentCompat.java
+++ /dev/null
@@ -1,75 +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.Build;
-import android.os.Environment;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Helper for accessing features in {@link Environment}.
- */
-public final class EnvironmentCompat {
-    private static final String TAG = "EnvironmentCompat";
-
-    /**
-     * Unknown storage state, such as when a path isn't backed by known storage
-     * media.
-     *
-     * @see #getStorageState(File)
-     */
-    public static final String MEDIA_UNKNOWN = "unknown";
-
-    /**
-     * Returns the current state of the storage device that provides the given
-     * path.
-     *
-     * @return one of {@link #MEDIA_UNKNOWN}, {@link Environment#MEDIA_REMOVED},
-     *         {@link Environment#MEDIA_UNMOUNTED},
-     *         {@link Environment#MEDIA_CHECKING},
-     *         {@link Environment#MEDIA_NOFS},
-     *         {@link Environment#MEDIA_MOUNTED},
-     *         {@link Environment#MEDIA_MOUNTED_READ_ONLY},
-     *         {@link Environment#MEDIA_SHARED},
-     *         {@link Environment#MEDIA_BAD_REMOVAL}, or
-     *         {@link Environment#MEDIA_UNMOUNTABLE}.
-     */
-    public static String getStorageState(File path) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return Environment.getStorageState(path);
-        }
-
-        try {
-            final String canonicalPath = path.getCanonicalPath();
-            final String canonicalExternal = Environment.getExternalStorageDirectory()
-                    .getCanonicalPath();
-
-            if (canonicalPath.startsWith(canonicalExternal)) {
-                return Environment.getExternalStorageState();
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to resolve canonical path: " + e);
-        }
-
-        return MEDIA_UNKNOWN;
-    }
-
-    private EnvironmentCompat() {}
-}
diff --git a/android/support/v4/os/LocaleHelper.java b/android/support/v4/os/LocaleHelper.java
deleted file mode 100644
index 8b933b9..0000000
--- a/android/support/v4/os/LocaleHelper.java
+++ /dev/null
@@ -1,72 +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.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/android/support/v4/os/LocaleListCompat.java b/android/support/v4/os/LocaleListCompat.java
deleted file mode 100644
index 9ccc35b..0000000
--- a/android/support/v4/os/LocaleListCompat.java
+++ /dev/null
@@ -1,367 +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.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/android/support/v4/os/LocaleListHelper.java b/android/support/v4/os/LocaleListHelper.java
deleted file mode 100644
index cfb24fb..0000000
--- a/android/support/v4/os/LocaleListHelper.java
+++ /dev/null
@@ -1,584 +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.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/android/support/v4/os/LocaleListInterface.java b/android/support/v4/os/LocaleListInterface.java
deleted file mode 100644
index ce310e6..0000000
--- a/android/support/v4/os/LocaleListInterface.java
+++ /dev/null
@@ -1,62 +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.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/android/support/v4/os/OperationCanceledException.java b/android/support/v4/os/OperationCanceledException.java
deleted file mode 100644
index 9b28030..0000000
--- a/android/support/v4/os/OperationCanceledException.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;
-
-
-/**
- * An exception type that is thrown when an operation in progress is canceled.
- */
-public class OperationCanceledException extends RuntimeException {
-    public OperationCanceledException() {
-        this(null);
-    }
-
-    public OperationCanceledException(String message) {
-        super(message != null ? message : "The operation has been canceled.");
-    }
-}
diff --git a/android/support/v4/os/ParcelableCompat.java b/android/support/v4/os/ParcelableCompat.java
deleted file mode 100644
index ee83122..0000000
--- a/android/support/v4/os/ParcelableCompat.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.os;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Helper for accessing features in {@link android.os.Parcelable}.
- *
- * @deprecated Use {@link android.os.Parcelable.ClassLoaderCreator} directly.
- */
-@Deprecated
-public final class ParcelableCompat {
-
-    /**
-     * Factory method for {@link Parcelable.Creator}.
-     *
-     * @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) {
-        return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);
-    }
-
-    static class ParcelableCompatCreatorHoneycombMR2<T>
-            implements Parcelable.ClassLoaderCreator<T> {
-        private final ParcelableCompatCreatorCallbacks<T> mCallbacks;
-
-        ParcelableCompatCreatorHoneycombMR2(ParcelableCompatCreatorCallbacks<T> callbacks) {
-            mCallbacks = callbacks;
-        }
-
-        @Override
-        public T createFromParcel(Parcel in) {
-            return mCallbacks.createFromParcel(in, null);
-        }
-
-        @Override
-        public T createFromParcel(Parcel in, ClassLoader loader) {
-            return mCallbacks.createFromParcel(in, loader);
-        }
-
-        @Override
-        public T[] newArray(int size) {
-            return mCallbacks.newArray(size);
-        }
-    }
-
-    private ParcelableCompat() {}
-}
diff --git a/android/support/v4/os/ParcelableCompatCreatorCallbacks.java b/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
deleted file mode 100644
index e5e8a3a..0000000
--- a/android/support/v4/os/ParcelableCompatCreatorCallbacks.java
+++ /dev/null
@@ -1,50 +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.
- *
- * @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/android/support/v4/os/ResultReceiver.java b/android/support/v4/os/ResultReceiver.java
index 7c0230a..4c08e1e 100644
--- a/android/support/v4/os/ResultReceiver.java
+++ b/android/support/v4/os/ResultReceiver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,14 +16,15 @@
 
 package android.support.v4.os;
 
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.support.annotation.RestrictTo;
+
+import androidx.annotation.RestrictTo;
 
 /**
  * Generic interface for receiving a callback result from someone.  Use this
diff --git a/android/support/v4/os/TraceCompat.java b/android/support/v4/os/TraceCompat.java
deleted file mode 100644
index 7badc62..0000000
--- a/android/support/v4/os/TraceCompat.java
+++ /dev/null
@@ -1,63 +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.Build;
-import android.os.Trace;
-
-/**
- * Writes trace events to the system trace buffer.  These trace events can be
- * collected and visualized using the Systrace tool.
- *
- * <p>This tracing mechanism is independent of the method tracing mechanism
- * offered by {@link android.os.Debug#startMethodTracing}.  In particular, it enables
- * tracing of events that occur across multiple processes.
- * <p>For information about using the Systrace tool, read <a
- * href="{@docRoot}tools/debugging/systrace.html">Analyzing Display and Performance
- * with Systrace</a>.
- */
-public final class TraceCompat {
-    /**
-     * Writes a trace message to indicate that a given section of code has begun. This call must
-     * be followed by a corresponding call to {@link #endSection()} on the same thread.
-     *
-     * <p class="note"> At this time the vertical bar character '|', newline character '\n', and
-     * null character '\0' are used internally by the tracing mechanism.  If sectionName contains
-     * these characters they will be replaced with a space character in the trace.
-     *
-     * @param sectionName The name of the code section to appear in the trace.  This may be at
-     * most 127 Unicode code units long.
-     */
-
-    public static void beginSection(String sectionName) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            Trace.beginSection(sectionName);
-        }
-    }
-
-    /**
-     * Writes a trace message to indicate that a given section of code has ended. This call must
-     * be preceeded by a corresponding call to {@link #beginSection(String)}. Calling this method
-     * will mark the end of the most recently begun section of code, so care must be taken to
-     * ensure that beginSection / endSection pairs are properly nested and called from the same
-     * thread.
-     */
-    public static void endSection() {
-        if (Build.VERSION.SDK_INT >= 18) {
-            Trace.endSection();
-        }
-    }
-
-    private TraceCompat() {}
-}
diff --git a/android/support/v4/os/UserManagerCompat.java b/android/support/v4/os/UserManagerCompat.java
deleted file mode 100644
index 0a82a5d..0000000
--- a/android/support/v4/os/UserManagerCompat.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.v4.os;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.UserManager;
-
-/**
- * Helper for accessing features in {@link android.os.UserManager} in a backwards compatible
- * fashion.
- */
-public class UserManagerCompat {
-
-    private UserManagerCompat() {
-    }
-
-    /**
-     * Return whether the calling user is running in an "unlocked" state. A user
-     * is unlocked only after they've entered their credentials (such as a lock
-     * pattern or PIN), and credential-encrypted private app data storage is
-     * available.
-     */
-    public static boolean isUserUnlocked(Context context) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return context.getSystemService(UserManager.class).isUserUnlocked();
-        } else {
-            return true;
-        }
-    }
-
-}
diff --git a/android/support/v4/print/PrintHelper.java b/android/support/v4/print/PrintHelper.java
deleted file mode 100644
index ce342e3..0000000
--- a/android/support/v4/print/PrintHelper.java
+++ /dev/null
@@ -1,1020 +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.content.Context;
-import android.graphics.Bitmap;
-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.
- */
-public final class PrintHelper {
-    /**
-     * 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;
-
-    /**
-     * this is a black and white image
-     */
-    public static final int COLOR_MODE_MONOCHROME = PrintAttributes.COLOR_MODE_MONOCHROME;
-
-    /**
-     * this is a color image (default)
-     */
-    public static final int COLOR_MODE_COLOR = PrintAttributes.COLOR_MODE_COLOR;
-
-    /**
-     * Print the image in landscape orientation (default).
-     */
-    public static final int ORIENTATION_LANDSCAPE = 1;
-
-    /**
-     * Print the image in  portrait orientation.
-     */
-    public static final int ORIENTATION_PORTRAIT = 2;
-
-    /**
-     * Callback for observing when a print operation is completed.
-     * When print is finished either the system acquired the
-     * document to print or printing was cancelled.
-     */
-    public interface OnPrintFinishCallback {
-        /**
-         * Called when a print operation is finished.
-         */
-        void onFinish();
-    }
-
-    @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.
-     *
-     * @return True if printing is supported.
-     */
-    public static boolean systemSupportsPrint() {
-        // Supported on Android 4.4 or later.
-        return Build.VERSION.SDK_INT >= 19;
-    }
-
-    /**
-     * Interface implemented by classes that support printing
-     */
-    interface PrintHelperVersionImpl {
-
-        void setScaleMode(int scaleMode);
-
-        int getScaleMode();
-
-        void setColorMode(int colorMode);
-
-        int getColorMode();
-
-        void setOrientation(int orientation);
-
-        int getOrientation();
-
-        void printBitmap(String jobName, Bitmap bitmap, OnPrintFinishCallback callback);
-
-        void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback)
-                throws FileNotFoundException;
-    }
-
-    /**
-     * Implementation used when we do not support printing
-     */
-    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(@ScaleMode int scaleMode) {
-            mScaleMode = scaleMode;
-        }
-
-        @ScaleMode
-        @Override
-        public int getScaleMode() {
-            return mScaleMode;
-        }
-
-        @ColorMode
-        @Override
-        public int getColorMode() {
-            return mColorMode;
-        }
-
-        @Override
-        public void setColorMode(@ColorMode int colorMode) {
-            mColorMode = colorMode;
-        }
-
-        @Override
-        public void setOrientation(@Orientation int orientation) {
-            mOrientation = orientation;
-        }
-
-        @Orientation
-        @Override
-        public int getOrientation() {
-            return mOrientation;
-        }
-
-        @Override
-        public void printBitmap(String jobName, Bitmap bitmap, OnPrintFinishCallback callback) {
-        }
-
-        @Override
-        public void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback) {
-        }
-    }
-
-    /**
-     * Kitkat specific PrintManager API implementation.
-     */
-    @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();
-
-        /**
-         * 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(@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 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(@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 void setOrientation(@Orientation 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}
-         */
-        @Orientation
-        @Override
-        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}.
-         */
-        @ColorMode
-        @Override
-        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) {
-            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,
-                @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(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);
-            }
-            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;
-        }
-    }
-
-    /**
-     * Api20 specific PrintManager API implementation.
-     */
-    @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;
-        }
-    }
-
-    /**
-     * Api23 specific PrintManager API implementation.
-     */
-    @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;
-        }
-    }
-
-    /**
-     * Api24 specific PrintManager API implementation.
-     */
-    @RequiresApi(24)
-    private static class PrintHelperApi24 extends PrintHelperApi23 {
-        PrintHelperApi24(Context context) {
-            super(context);
-
-            mIsMinMarginsHandlingCorrect = true;
-            mPrintActivityRespectsOrientation = true;
-        }
-    }
-
-    /**
-     * Constructs the PrintHelper that can be used to print images.
-     *
-     * @param context A context for accessing system resources.
-     */
-    public PrintHelper(Context 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 {
-            // System does not support printing.
-            mImpl = new PrintHelperStub();
-        }
-    }
-
-    /**
-     * Selects whether the image will fill the paper and be cropped
-     * {@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(@ScaleMode int scaleMode) {
-        mImpl.setScaleMode(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
-    public int getScaleMode() {
-        return mImpl.getScaleMode();
-    }
-
-    /**
-     * 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(@ColorMode int colorMode) {
-        mImpl.setColorMode(colorMode);
-    }
-
-    /**
-     * 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
-    public int getColorMode() {
-        return mImpl.getColorMode();
-    }
-
-    /**
-     * Sets whether the image will be printed in landscape {@link #ORIENTATION_LANDSCAPE} (default)
-     * 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) {
-        mImpl.setOrientation(orientation);
-    }
-
-    /**
-     * Gets whether the image will be printed in landscape or portrait.
-     *
-     * @return The page orientation which is one of
-     * {@link #ORIENTATION_LANDSCAPE} or {@link #ORIENTATION_PORTRAIT}.
-     */
-    public int getOrientation() {
-        return mImpl.getOrientation();
-    }
-
-
-    /**
-     * Prints a bitmap.
-     *
-     * @param jobName The print job name.
-     * @param bitmap  The bitmap to print.
-     */
-    public void printBitmap(String jobName, Bitmap bitmap) {
-        mImpl.printBitmap(jobName, bitmap, null);
-    }
-
-    /**
-     * 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(String jobName, Bitmap bitmap, OnPrintFinishCallback callback) {
-        mImpl.printBitmap(jobName, bitmap, callback);
-    }
-
-    /**
-     * Prints an image located at the Uri. Image types supported are those of
-     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
-     * android.graphics.BitmapFactory.decodeStream(java.io.InputStream)}
-     *
-     * @param jobName   The print job name.
-     * @param imageFile The <code>Uri</code> pointing to an image to print.
-     * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
-     */
-    public void printBitmap(String jobName, Uri imageFile) throws FileNotFoundException {
-        mImpl.printBitmap(jobName, imageFile, null);
-    }
-
-    /**
-     * Prints an image located at the Uri. Image types supported are those of
-     * {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
-     * android.graphics.BitmapFactory.decodeStream(java.io.InputStream)}
-     *
-     * @param jobName   The print job name.
-     * @param imageFile The <code>Uri</code> pointing to an image to print.
-     * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
-     * @param callback Optional callback to observe when printing is finished.
-     */
-    public void printBitmap(String jobName, Uri imageFile, OnPrintFinishCallback callback)
-            throws FileNotFoundException {
-        mImpl.printBitmap(jobName, imageFile, callback);
-    }
-}
diff --git a/android/support/v4/provider/DocumentFile.java b/android/support/v4/provider/DocumentFile.java
deleted file mode 100644
index 7e08d57..0000000
--- a/android/support/v4/provider/DocumentFile.java
+++ /dev/null
@@ -1,352 +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.provider;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.provider.DocumentsContract;
-
-import java.io.File;
-
-/**
- * Representation of a document backed by either a
- * {@link android.provider.DocumentsProvider} or a raw file on disk. This is a
- * utility class designed to emulate the traditional {@link File} interface. It
- * offers a simplified view of a tree of documents, but it has substantial
- * overhead. For optimal performance and a richer feature set, use the
- * {@link android.provider.DocumentsContract} methods and constants directly.
- * <p>
- * There are several differences between documents and traditional files:
- * <ul>
- * <li>Documents express their display name and MIME type as separate fields,
- * instead of relying on file extensions. Some documents providers may still
- * choose to append extensions to their display names, but that's an
- * implementation detail.
- * <li>A single document may appear as the child of multiple directories, so it
- * doesn't inherently know who its parent is. That is, documents don't have a
- * strong notion of path. You can easily traverse a tree of documents from
- * parent to child, but not from child to parent.
- * <li>Each document has a unique identifier within that provider. This
- * identifier is an <em>opaque</em> implementation detail of the provider, and
- * as such it must not be parsed.
- * </ul>
- * <p>
- * Before using this class, first consider if you really need access to an
- * entire subtree of documents. The principle of least privilege dictates that
- * you should only ask for access to documents you really need. If you only need
- * the user to pick a single file, use {@link Intent#ACTION_OPEN_DOCUMENT} or
- * {@link Intent#ACTION_GET_CONTENT}. If you want to let the user pick multiple
- * files, add {@link Intent#EXTRA_ALLOW_MULTIPLE}. If you only need the user to
- * save a single file, use {@link Intent#ACTION_CREATE_DOCUMENT}. If you use
- * these APIs, you can pass the resulting {@link Intent#getData()} into
- * {@link #fromSingleUri(Context, Uri)} to work with that document.
- * <p>
- * If you really do need full access to an entire subtree of documents, start by
- * launching {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to let the user pick a
- * directory. Then pass the resulting {@link Intent#getData()} into
- * {@link #fromTreeUri(Context, Uri)} to start working with the user selected
- * tree.
- * <p>
- * As you navigate the tree of DocumentFile instances, you can always use
- * {@link #getUri()} to obtain the Uri representing the underlying document for
- * that object, for use with {@link ContentResolver#openInputStream(Uri)}, etc.
- * <p>
- * To simplify your code on devices running
- * {@link android.os.Build.VERSION_CODES#KITKAT} or earlier, you can use
- * {@link #fromFile(File)} which emulates the behavior of a
- * {@link android.provider.DocumentsProvider}.
- *
- * @see android.provider.DocumentsProvider
- * @see android.provider.DocumentsContract
- */
-public abstract class DocumentFile {
-    static final String TAG = "DocumentFile";
-
-    private final DocumentFile mParent;
-
-    DocumentFile(DocumentFile parent) {
-        mParent = parent;
-    }
-
-    /**
-     * Create a {@link DocumentFile} representing the filesystem tree rooted at
-     * the given {@link File}. This doesn't give you any additional access to the
-     * underlying files beyond what your app already has.
-     * <p>
-     * {@link #getUri()} will return {@code file://} Uris for files explored
-     * through this tree.
-     */
-    public static DocumentFile fromFile(File file) {
-        return new RawDocumentFile(null, file);
-    }
-
-    /**
-     * Create a {@link DocumentFile} representing the single document at the
-     * given {@link Uri}. This is only useful on devices running
-     * {@link android.os.Build.VERSION_CODES#KITKAT} or later, and will return
-     * {@code null} when called on earlier platform versions.
-     *
-     * @param singleUri the {@link Intent#getData()} from a successful
-     *            {@link Intent#ACTION_OPEN_DOCUMENT} or
-     *            {@link Intent#ACTION_CREATE_DOCUMENT} request.
-     */
-    public static DocumentFile fromSingleUri(Context context, Uri singleUri) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return new SingleDocumentFile(null, context, singleUri);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Create a {@link DocumentFile} representing the document tree rooted at
-     * the given {@link Uri}. This is only useful on devices running
-     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, and will return
-     * {@code null} when called on earlier platform versions.
-     *
-     * @param treeUri the {@link Intent#getData()} from a successful
-     *            {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
-     */
-    public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return new TreeDocumentFile(null, context,
-                    DocumentsContract.buildDocumentUriUsingTree(treeUri,
-                            DocumentsContract.getTreeDocumentId(treeUri)));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Test if given Uri is backed by a
-     * {@link android.provider.DocumentsProvider}.
-     */
-    public static boolean isDocumentUri(Context context, Uri uri) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return DocumentsContractApi19.isDocumentUri(context, uri);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Create a new document as a direct child of this directory.
-     *
-     * @param mimeType MIME type of new document, such as {@code image/png} or
-     *            {@code audio/flac}
-     * @param displayName name of new document, without any file extension
-     *            appended; the underlying provider may choose to append the
-     *            extension
-     * @return file representing newly created document, or null if failed
-     * @throws UnsupportedOperationException when working with a single document
-     *             created from {@link #fromSingleUri(Context, Uri)}.
-     * @see android.provider.DocumentsContract#createDocument(ContentResolver,
-     *      Uri, String, String)
-     */
-    public abstract DocumentFile createFile(String mimeType, String displayName);
-
-    /**
-     * Create a new directory as a direct child of this directory.
-     *
-     * @param displayName name of new directory
-     * @return file representing newly created directory, or null if failed
-     * @throws UnsupportedOperationException when working with a single document
-     *             created from {@link #fromSingleUri(Context, Uri)}.
-     * @see android.provider.DocumentsContract#createDocument(ContentResolver,
-     *      Uri, String, String)
-     */
-    public abstract DocumentFile createDirectory(String displayName);
-
-    /**
-     * Return a Uri for the underlying document represented by this file. This
-     * can be used with other platform APIs to manipulate or share the
-     * underlying content. You can use {@link #isDocumentUri(Context, Uri)} to
-     * test if the returned Uri is backed by a
-     * {@link android.provider.DocumentsProvider}.
-     *
-     * @see Intent#setData(Uri)
-     * @see Intent#setClipData(android.content.ClipData)
-     * @see ContentResolver#openInputStream(Uri)
-     * @see ContentResolver#openOutputStream(Uri)
-     * @see ContentResolver#openFileDescriptor(Uri, String)
-     */
-    public abstract Uri getUri();
-
-    /**
-     * Return the display name of this document.
-     *
-     * @see android.provider.DocumentsContract.Document#COLUMN_DISPLAY_NAME
-     */
-    public abstract String getName();
-
-    /**
-     * Return the MIME type of this document.
-     *
-     * @see android.provider.DocumentsContract.Document#COLUMN_MIME_TYPE
-     */
-    public abstract String getType();
-
-    /**
-     * Return the parent file of this document. Only defined inside of the
-     * user-selected tree; you can never escape above the top of the tree.
-     * <p>
-     * The underlying {@link android.provider.DocumentsProvider} only defines a
-     * forward mapping from parent to child, so the reverse mapping of child to
-     * parent offered here is purely a convenience method, and it may be
-     * incorrect if the underlying tree structure changes.
-     */
-    public DocumentFile getParentFile() {
-        return mParent;
-    }
-
-    /**
-     * Indicates if this file represents a <em>directory</em>.
-     *
-     * @return {@code true} if this file is a directory, {@code false}
-     *         otherwise.
-     * @see android.provider.DocumentsContract.Document#MIME_TYPE_DIR
-     */
-    public abstract boolean isDirectory();
-
-    /**
-     * Indicates if this file represents a <em>file</em>.
-     *
-     * @return {@code true} if this file is a file, {@code false} otherwise.
-     * @see android.provider.DocumentsContract.Document#COLUMN_MIME_TYPE
-     */
-    public abstract boolean isFile();
-
-    /**
-     * Indicates if this file represents a <em>virtual</em> document.
-     *
-     * @return {@code true} if this file is a virtual document.
-     * @see android.provider.DocumentsContract.Document#FLAG_VIRTUAL_DOCUMENT
-     */
-    public abstract boolean isVirtual();
-
-    /**
-     * Returns the time when this file was last modified, measured in
-     * milliseconds since January 1st, 1970, midnight. Returns 0 if the file
-     * does not exist, or if the modified time is unknown.
-     *
-     * @return the time when this file was last modified.
-     * @see android.provider.DocumentsContract.Document#COLUMN_LAST_MODIFIED
-     */
-    public abstract long lastModified();
-
-    /**
-     * Returns the length of this file in bytes. Returns 0 if the file does not
-     * exist, or if the length is unknown. The result for a directory is not
-     * defined.
-     *
-     * @return the number of bytes in this file.
-     * @see android.provider.DocumentsContract.Document#COLUMN_SIZE
-     */
-    public abstract long length();
-
-    /**
-     * Indicates whether the current context is allowed to read from this file.
-     *
-     * @return {@code true} if this file can be read, {@code false} otherwise.
-     */
-    public abstract boolean canRead();
-
-    /**
-     * Indicates whether the current context is allowed to write to this file.
-     *
-     * @return {@code true} if this file can be written, {@code false}
-     *         otherwise.
-     * @see android.provider.DocumentsContract.Document#COLUMN_FLAGS
-     * @see android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE
-     * @see android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE
-     * @see android.provider.DocumentsContract.Document#FLAG_DIR_SUPPORTS_CREATE
-     */
-    public abstract boolean canWrite();
-
-    /**
-     * Deletes this file.
-     * <p>
-     * Note that this method does <i>not</i> throw {@code IOException} on
-     * failure. Callers must check the return value.
-     *
-     * @return {@code true} if this file was deleted, {@code false} otherwise.
-     * @see android.provider.DocumentsContract#deleteDocument(ContentResolver,
-     *      Uri)
-     */
-    public abstract boolean delete();
-
-    /**
-     * Returns a boolean indicating whether this file can be found.
-     *
-     * @return {@code true} if this file exists, {@code false} otherwise.
-     */
-    public abstract boolean exists();
-
-    /**
-     * Returns an array of files contained in the directory represented by this
-     * file.
-     *
-     * @return an array of files or {@code null}.
-     * @throws UnsupportedOperationException when working with a single document
-     *             created from {@link #fromSingleUri(Context, Uri)}.
-     * @see android.provider.DocumentsContract#buildChildDocumentsUriUsingTree(Uri,
-     *      String)
-     */
-    public abstract DocumentFile[] listFiles();
-
-    /**
-     * Search through {@link #listFiles()} for the first document matching the
-     * given display name. Returns {@code null} when no matching document is
-     * found.
-     *
-     * @throws UnsupportedOperationException when working with a single document
-     *             created from {@link #fromSingleUri(Context, Uri)}.
-     */
-    public DocumentFile findFile(String displayName) {
-        for (DocumentFile doc : listFiles()) {
-            if (displayName.equals(doc.getName())) {
-                return doc;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Renames this file to {@code displayName}.
-     * <p>
-     * Note that this method does <i>not</i> throw {@code IOException} on
-     * failure. Callers must check the return value.
-     * <p>
-     * Some providers may need to create a new document to reflect the rename,
-     * potentially with a different MIME type, so {@link #getUri()} and
-     * {@link #getType()} may change to reflect the rename.
-     * <p>
-     * When renaming a directory, children previously enumerated through
-     * {@link #listFiles()} may no longer be valid.
-     *
-     * @param displayName the new display name.
-     * @return true on success.
-     * @throws UnsupportedOperationException when working with a single document
-     *             created from {@link #fromSingleUri(Context, Uri)}.
-     * @see android.provider.DocumentsContract#renameDocument(ContentResolver,
-     *      Uri, String)
-     */
-    public abstract boolean renameTo(String displayName);
-}
diff --git a/android/support/v4/provider/DocumentsContractApi19.java b/android/support/v4/provider/DocumentsContractApi19.java
deleted file mode 100644
index b920dc7..0000000
--- a/android/support/v4/provider/DocumentsContractApi19.java
+++ /dev/null
@@ -1,210 +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.provider;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.DocumentsContract;
-import android.support.annotation.RequiresApi;
-import android.text.TextUtils;
-import android.util.Log;
-
-@RequiresApi(19)
-class DocumentsContractApi19 {
-    private static final String TAG = "DocumentFile";
-
-    // DocumentsContract API level 24.
-    private static final int FLAG_VIRTUAL_DOCUMENT = 1 << 9;
-
-    public static boolean isDocumentUri(Context context, Uri self) {
-        return DocumentsContract.isDocumentUri(context, self);
-    }
-
-    public static boolean isVirtual(Context context, Uri self) {
-        if (!isDocumentUri(context, self)) {
-            return false;
-        }
-
-        return (getFlags(context, self) & FLAG_VIRTUAL_DOCUMENT) != 0;
-    }
-
-    public static String getName(Context context, Uri self) {
-        return queryForString(context, self, DocumentsContract.Document.COLUMN_DISPLAY_NAME, null);
-    }
-
-    private static String getRawType(Context context, Uri self) {
-        return queryForString(context, self, DocumentsContract.Document.COLUMN_MIME_TYPE, null);
-    }
-
-    public static String getType(Context context, Uri self) {
-        final String rawType = getRawType(context, self);
-        if (DocumentsContract.Document.MIME_TYPE_DIR.equals(rawType)) {
-            return null;
-        } else {
-            return rawType;
-        }
-    }
-
-    public static long getFlags(Context context, Uri self) {
-        return queryForLong(context, self, DocumentsContract.Document.COLUMN_FLAGS, 0);
-    }
-
-    public static boolean isDirectory(Context context, Uri self) {
-        return DocumentsContract.Document.MIME_TYPE_DIR.equals(getRawType(context, self));
-    }
-
-    public static boolean isFile(Context context, Uri self) {
-        final String type = getRawType(context, self);
-        if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type) || TextUtils.isEmpty(type)) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    public static long lastModified(Context context, Uri self) {
-        return queryForLong(context, self, DocumentsContract.Document.COLUMN_LAST_MODIFIED, 0);
-    }
-
-    public static long length(Context context, Uri self) {
-        return queryForLong(context, self, DocumentsContract.Document.COLUMN_SIZE, 0);
-    }
-
-    public static boolean canRead(Context context, Uri self) {
-        // Ignore if grant doesn't allow read
-        if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                != PackageManager.PERMISSION_GRANTED) {
-            return false;
-        }
-
-        // Ignore documents without MIME
-        if (TextUtils.isEmpty(getRawType(context, self))) {
-            return false;
-        }
-
-        return true;
-    }
-
-    public static boolean canWrite(Context context, Uri self) {
-        // Ignore if grant doesn't allow write
-        if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-                != PackageManager.PERMISSION_GRANTED) {
-            return false;
-        }
-
-        final String type = getRawType(context, self);
-        final int flags = queryForInt(context, self, DocumentsContract.Document.COLUMN_FLAGS, 0);
-
-        // Ignore documents without MIME
-        if (TextUtils.isEmpty(type)) {
-            return false;
-        }
-
-        // Deletable documents considered writable
-        if ((flags & DocumentsContract.Document.FLAG_SUPPORTS_DELETE) != 0) {
-            return true;
-        }
-
-        if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type)
-                && (flags & DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE) != 0) {
-            // Directories that allow create considered writable
-            return true;
-        } else if (!TextUtils.isEmpty(type)
-                && (flags & DocumentsContract.Document.FLAG_SUPPORTS_WRITE) != 0) {
-            // Writable normal files considered writable
-            return true;
-        }
-
-        return false;
-    }
-
-    public static boolean exists(Context context, Uri self) {
-        final ContentResolver resolver = context.getContentResolver();
-
-        Cursor c = null;
-        try {
-            c = resolver.query(self, new String[] {
-                    DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null);
-            return c.getCount() > 0;
-        } catch (Exception e) {
-            Log.w(TAG, "Failed query: " + e);
-            return false;
-        } finally {
-            closeQuietly(c);
-        }
-    }
-
-    private static String queryForString(Context context, Uri self, String column,
-            String defaultValue) {
-        final ContentResolver resolver = context.getContentResolver();
-
-        Cursor c = null;
-        try {
-            c = resolver.query(self, new String[] { column }, null, null, null);
-            if (c.moveToFirst() && !c.isNull(0)) {
-                return c.getString(0);
-            } else {
-                return defaultValue;
-            }
-        } catch (Exception e) {
-            Log.w(TAG, "Failed query: " + e);
-            return defaultValue;
-        } finally {
-            closeQuietly(c);
-        }
-    }
-
-    private static int queryForInt(Context context, Uri self, String column,
-            int defaultValue) {
-        return (int) queryForLong(context, self, column, defaultValue);
-    }
-
-    private static long queryForLong(Context context, Uri self, String column,
-            long defaultValue) {
-        final ContentResolver resolver = context.getContentResolver();
-
-        Cursor c = null;
-        try {
-            c = resolver.query(self, new String[] { column }, null, null, null);
-            if (c.moveToFirst() && !c.isNull(0)) {
-                return c.getLong(0);
-            } else {
-                return defaultValue;
-            }
-        } catch (Exception e) {
-            Log.w(TAG, "Failed query: " + e);
-            return defaultValue;
-        } finally {
-            closeQuietly(c);
-        }
-    }
-
-    private static void closeQuietly(AutoCloseable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException rethrown) {
-                throw rethrown;
-            } catch (Exception ignored) {
-            }
-        }
-    }
-}
diff --git a/android/support/v4/provider/FontRequest.java b/android/support/v4/provider/FontRequest.java
deleted file mode 100644
index b14f85e..0000000
--- a/android/support/v4/provider/FontRequest.java
+++ /dev/null
@@ -1,168 +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.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.
-     */
-    @NonNull
-    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.
-     */
-    @NonNull
-    public String getProviderPackage() {
-        return mProviderPackage;
-    }
-
-    /**
-     * Returns the query string. Refer to your font provider's documentation on the format of this
-     * string.
-     */
-    @NonNull
-    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/android/support/v4/provider/FontsContractCompat.java b/android/support/v4/provider/FontsContractCompat.java
deleted file mode 100644
index 39acf68..0000000
--- a/android/support/v4/provider/FontsContractCompat.java
+++ /dev/null
@@ -1,874 +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.provider;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.content.res.ResourcesCompat;
-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 java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-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)
-    /* package */ static final int RESULT_CODE_PROVIDER_NOT_FOUND = -1;
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    /* package */ static final int RESULT_CODE_WRONG_CERTIFICATES = -2;
-    // Note -3 is used by FontRequestCallback 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);
-
-    @NonNull
-    private static TypefaceResult getFontInternal(final Context context, final FontRequest request,
-            int style) {
-        FontFamilyResult result;
-        try {
-            result = fetchFonts(context, null /* CancellationSignal */, request);
-        } catch (PackageManager.NameNotFoundException e) {
-            return new TypefaceResult(null, FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND);
-        }
-        if (result.getStatusCode() == FontFamilyResult.STATUS_OK) {
-            final Typeface typeface = TypefaceCompat.createFromFontInfo(
-                    context, null /* CancellationSignal */, result.getFonts(), style);
-            return new TypefaceResult(typeface, typeface != null
-                    ? FontRequestCallback.RESULT_OK
-                    : FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR);
-        }
-        int resultCode = result.getStatusCode() == FontFamilyResult.STATUS_WRONG_CERTIFICATES
-                ? FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES
-                : FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR;
-        return new TypefaceResult(null, resultCode);
-    }
-
-    private static final Object sLock = new Object();
-    @GuardedBy("sLock")
-    private static final SimpleArrayMap<String, ArrayList<ReplyCallback<TypefaceResult>>>
-            sPendingReplies = new SimpleArrayMap<>();
-
-    private static final class TypefaceResult {
-        final Typeface mTypeface;
-        @FontRequestCallback.FontRequestFailReason final int mResult;
-
-        TypefaceResult(@Nullable Typeface typeface,
-                @FontRequestCallback.FontRequestFailReason int result) {
-            mTypeface = typeface;
-            mResult = result;
-        }
-    }
-
-    /**
-     * Used for tests, should not be used otherwise.
-     * @hide
-     **/
-    @RestrictTo(LIBRARY_GROUP)
-    public static final void resetCache() {
-        sTypefaceCache.evictAll();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public static Typeface getFontSync(final Context context, final FontRequest request,
-            final @Nullable ResourcesCompat.FontCallback fontCallback,
-            final @Nullable Handler handler, boolean isBlockingFetch, int timeout,
-            final int style) {
-        final String id = request.getIdentifier() + "-" + style;
-        Typeface cached = sTypefaceCache.get(id);
-        if (cached != null) {
-            if (fontCallback != null) {
-                fontCallback.onFontRetrieved(cached);
-            }
-            return cached;
-        }
-
-        if (isBlockingFetch && timeout == FontResourcesParserCompat.INFINITE_TIMEOUT_VALUE) {
-            // Wait forever. No need to post to the thread.
-            TypefaceResult typefaceResult = getFontInternal(context, request, style);
-            if (fontCallback != null) {
-                if (typefaceResult.mResult == FontFamilyResult.STATUS_OK) {
-                    fontCallback.callbackSuccessAsync(typefaceResult.mTypeface, handler);
-                } else {
-                    fontCallback.callbackFailAsync(typefaceResult.mResult, handler);
-                }
-            }
-            return typefaceResult.mTypeface;
-        }
-
-        final Callable<TypefaceResult> fetcher = new Callable<TypefaceResult>() {
-            @Override
-            public TypefaceResult call() throws Exception {
-                TypefaceResult typeface = getFontInternal(context, request, style);
-                if (typeface.mTypeface != null) {
-                    sTypefaceCache.put(id, typeface.mTypeface);
-                }
-                return typeface;
-            }
-        };
-
-        if (isBlockingFetch) {
-            try {
-                return sBackgroundThread.postAndWait(fetcher, timeout).mTypeface;
-            } catch (InterruptedException e) {
-                return null;
-            }
-        } else {
-            final ReplyCallback<TypefaceResult> reply = fontCallback == null ? null
-                    : new ReplyCallback<TypefaceResult>() {
-                        @Override
-                        public void onReply(final TypefaceResult typeface) {
-                            if (typeface == null) {
-                                fontCallback.callbackFailAsync(
-                                        FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND, handler);
-                            } else if (typeface.mResult == FontFamilyResult.STATUS_OK) {
-                                fontCallback.callbackSuccessAsync(typeface.mTypeface, handler);
-                            } else {
-                                fontCallback.callbackFailAsync(typeface.mResult, handler);
-                            }
-                        }
-                    };
-
-            synchronized (sLock) {
-                if (sPendingReplies.containsKey(id)) {
-                    // Already requested. Do not request the same provider again and insert the
-                    // reply to the queue instead.
-                    if (reply != null) {
-                        sPendingReplies.get(id).add(reply);
-                    }
-                    return null;
-                }
-                if (reply != null) {
-                    ArrayList<ReplyCallback<TypefaceResult>> pendingReplies = new ArrayList<>();
-                    pendingReplies.add(reply);
-                    sPendingReplies.put(id, pendingReplies);
-                }
-            }
-            sBackgroundThread.postAndReply(fetcher, new ReplyCallback<TypefaceResult>() {
-                @Override
-                public void onReply(final TypefaceResult typeface) {
-                    final ArrayList<ReplyCallback<TypefaceResult>> replies;
-                    synchronized (sLock) {
-                        replies = sPendingReplies.get(id);
-                        if (replies == null) {
-                            return;  // Nobody requested replies. Do nothing.
-                        }
-                        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 {
-        /** @hide */
-        @RestrictTo(LIBRARY_GROUP)
-        public static final int RESULT_OK = Columns.RESULT_CODE_OK;
-        /**
-         * 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 that signals that the font was not loaded due to security issues. This usually
-         * means the font was attempted to load on a restricted context.
-         */
-        public static final int FAIL_REASON_SECURITY_VIOLATION = -4;
-        /**
-         * 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,
-                FAIL_REASON_SECURITY_VIOLATION, RESULT_OK })
-        @Retention(RetentionPolicy.SOURCE)
-        public @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.
-     */
-    @Nullable
-    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/android/support/v4/provider/RawDocumentFile.java b/android/support/v4/provider/RawDocumentFile.java
deleted file mode 100644
index f6ca912..0000000
--- a/android/support/v4/provider/RawDocumentFile.java
+++ /dev/null
@@ -1,179 +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.provider;
-
-import android.net.Uri;
-import android.util.Log;
-import android.webkit.MimeTypeMap;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-
-class RawDocumentFile extends DocumentFile {
-    private File mFile;
-
-    RawDocumentFile(DocumentFile parent, File file) {
-        super(parent);
-        mFile = file;
-    }
-
-    @Override
-    public DocumentFile createFile(String mimeType, String displayName) {
-        // Tack on extension when valid MIME type provided
-        final String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
-        if (extension != null) {
-            displayName += "." + extension;
-        }
-        final File target = new File(mFile, displayName);
-        try {
-            target.createNewFile();
-            return new RawDocumentFile(this, target);
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to createFile: " + e);
-            return null;
-        }
-    }
-
-    @Override
-    public DocumentFile createDirectory(String displayName) {
-        final File target = new File(mFile, displayName);
-        if (target.isDirectory() || target.mkdir()) {
-            return new RawDocumentFile(this, target);
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public Uri getUri() {
-        return Uri.fromFile(mFile);
-    }
-
-    @Override
-    public String getName() {
-        return mFile.getName();
-    }
-
-    @Override
-    public String getType() {
-        if (mFile.isDirectory()) {
-            return null;
-        } else {
-            return getTypeForName(mFile.getName());
-        }
-    }
-
-    @Override
-    public boolean isDirectory() {
-        return mFile.isDirectory();
-    }
-
-    @Override
-    public boolean isFile() {
-        return mFile.isFile();
-    }
-
-    @Override
-    public boolean isVirtual() {
-        return false;
-    }
-
-    @Override
-    public long lastModified() {
-        return mFile.lastModified();
-    }
-
-    @Override
-    public long length() {
-        return mFile.length();
-    }
-
-    @Override
-    public boolean canRead() {
-        return mFile.canRead();
-    }
-
-    @Override
-    public boolean canWrite() {
-        return mFile.canWrite();
-    }
-
-    @Override
-    public boolean delete() {
-        deleteContents(mFile);
-        return mFile.delete();
-    }
-
-    @Override
-    public boolean exists() {
-        return mFile.exists();
-    }
-
-    @Override
-    public DocumentFile[] listFiles() {
-        final ArrayList<DocumentFile> results = new ArrayList<DocumentFile>();
-        final File[] files = mFile.listFiles();
-        if (files != null) {
-            for (File file : files) {
-                results.add(new RawDocumentFile(this, file));
-            }
-        }
-        return results.toArray(new DocumentFile[results.size()]);
-    }
-
-    @Override
-    public boolean renameTo(String displayName) {
-        final File target = new File(mFile.getParentFile(), displayName);
-        if (mFile.renameTo(target)) {
-            mFile = target;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private static String getTypeForName(String name) {
-        final int lastDot = name.lastIndexOf('.');
-        if (lastDot >= 0) {
-            final String extension = name.substring(lastDot + 1).toLowerCase();
-            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
-            if (mime != null) {
-                return mime;
-            }
-        }
-
-        return "application/octet-stream";
-    }
-
-    private static boolean deleteContents(File dir) {
-        File[] files = dir.listFiles();
-        boolean success = true;
-        if (files != null) {
-            for (File file : files) {
-                if (file.isDirectory()) {
-                    success &= deleteContents(file);
-                }
-                if (!file.delete()) {
-                    Log.w(TAG, "Failed to delete " + file);
-                    success = false;
-                }
-            }
-        }
-        return success;
-    }
-}
diff --git a/android/support/v4/provider/SelfDestructiveThread.java b/android/support/v4/provider/SelfDestructiveThread.java
deleted file mode 100644
index 7cfe1f8..0000000
--- a/android/support/v4/provider/SelfDestructiveThread.java
+++ /dev/null
@@ -1,233 +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.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 finishes.
-     */
-    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/android/support/v4/provider/SingleDocumentFile.java b/android/support/v4/provider/SingleDocumentFile.java
deleted file mode 100644
index 509e11d..0000000
--- a/android/support/v4/provider/SingleDocumentFile.java
+++ /dev/null
@@ -1,118 +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.provider;
-
-import android.content.Context;
-import android.net.Uri;
-import android.provider.DocumentsContract;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(19)
-class SingleDocumentFile extends DocumentFile {
-    private Context mContext;
-    private Uri mUri;
-
-    SingleDocumentFile(DocumentFile parent, Context context, Uri uri) {
-        super(parent);
-        mContext = context;
-        mUri = uri;
-    }
-
-    @Override
-    public DocumentFile createFile(String mimeType, String displayName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public DocumentFile createDirectory(String displayName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Uri getUri() {
-        return mUri;
-    }
-
-    @Override
-    public String getName() {
-        return DocumentsContractApi19.getName(mContext, mUri);
-    }
-
-    @Override
-    public String getType() {
-        return DocumentsContractApi19.getType(mContext, mUri);
-    }
-
-    @Override
-    public boolean isDirectory() {
-        return DocumentsContractApi19.isDirectory(mContext, mUri);
-    }
-
-    @Override
-    public boolean isFile() {
-        return DocumentsContractApi19.isFile(mContext, mUri);
-    }
-
-    @Override
-    public boolean isVirtual() {
-        return DocumentsContractApi19.isVirtual(mContext, mUri);
-    }
-
-    @Override
-    public long lastModified() {
-        return DocumentsContractApi19.lastModified(mContext, mUri);
-    }
-
-    @Override
-    public long length() {
-        return DocumentsContractApi19.length(mContext, mUri);
-    }
-
-    @Override
-    public boolean canRead() {
-        return DocumentsContractApi19.canRead(mContext, mUri);
-    }
-
-    @Override
-    public boolean canWrite() {
-        return DocumentsContractApi19.canWrite(mContext, mUri);
-    }
-
-    @Override
-    public boolean delete() {
-        try {
-            return DocumentsContract.deleteDocument(mContext.getContentResolver(), mUri);
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean exists() {
-        return DocumentsContractApi19.exists(mContext, mUri);
-    }
-
-    @Override
-    public DocumentFile[] listFiles() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean renameTo(String displayName) {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/android/support/v4/provider/TreeDocumentFile.java b/android/support/v4/provider/TreeDocumentFile.java
deleted file mode 100644
index 7a0c8d3..0000000
--- a/android/support/v4/provider/TreeDocumentFile.java
+++ /dev/null
@@ -1,184 +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.provider;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.DocumentsContract;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-@RequiresApi(21)
-class TreeDocumentFile extends DocumentFile {
-    private Context mContext;
-    private Uri mUri;
-
-    TreeDocumentFile(DocumentFile parent, Context context, Uri uri) {
-        super(parent);
-        mContext = context;
-        mUri = uri;
-    }
-
-    @Override
-    public DocumentFile createFile(String mimeType, String displayName) {
-        final Uri result = TreeDocumentFile.createFile(mContext, mUri, mimeType, displayName);
-        return (result != null) ? new TreeDocumentFile(this, mContext, result) : null;
-    }
-
-    private static Uri createFile(Context context, Uri self, String mimeType,
-            String displayName) {
-        try {
-            return DocumentsContract.createDocument(context.getContentResolver(), self, mimeType,
-                    displayName);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    @Override
-    public DocumentFile createDirectory(String displayName) {
-        final Uri result = TreeDocumentFile.createFile(
-                mContext, mUri, DocumentsContract.Document.MIME_TYPE_DIR, displayName);
-        return (result != null) ? new TreeDocumentFile(this, mContext, result) : null;
-    }
-
-    @Override
-    public Uri getUri() {
-        return mUri;
-    }
-
-    @Override
-    public String getName() {
-        return DocumentsContractApi19.getName(mContext, mUri);
-    }
-
-    @Override
-    public String getType() {
-        return DocumentsContractApi19.getType(mContext, mUri);
-    }
-
-    @Override
-    public boolean isDirectory() {
-        return DocumentsContractApi19.isDirectory(mContext, mUri);
-    }
-
-    @Override
-    public boolean isFile() {
-        return DocumentsContractApi19.isFile(mContext, mUri);
-    }
-
-    @Override
-    public boolean isVirtual() {
-        return DocumentsContractApi19.isVirtual(mContext, mUri);
-    }
-
-    @Override
-    public long lastModified() {
-        return DocumentsContractApi19.lastModified(mContext, mUri);
-    }
-
-    @Override
-    public long length() {
-        return DocumentsContractApi19.length(mContext, mUri);
-    }
-
-    @Override
-    public boolean canRead() {
-        return DocumentsContractApi19.canRead(mContext, mUri);
-    }
-
-    @Override
-    public boolean canWrite() {
-        return DocumentsContractApi19.canWrite(mContext, mUri);
-    }
-
-    @Override
-    public boolean delete() {
-        try {
-            return DocumentsContract.deleteDocument(mContext.getContentResolver(), mUri);
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean exists() {
-        return DocumentsContractApi19.exists(mContext, mUri);
-    }
-
-    @Override
-    public DocumentFile[] listFiles() {
-        final ContentResolver resolver = mContext.getContentResolver();
-        final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(mUri,
-                DocumentsContract.getDocumentId(mUri));
-        final ArrayList<Uri> results = new ArrayList<>();
-
-        Cursor c = null;
-        try {
-            c = resolver.query(childrenUri, new String[] {
-                    DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null);
-            while (c.moveToNext()) {
-                final String documentId = c.getString(0);
-                final Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(mUri,
-                        documentId);
-                results.add(documentUri);
-            }
-        } catch (Exception e) {
-            Log.w(TAG, "Failed query: " + e);
-        } finally {
-            closeQuietly(c);
-        }
-
-        final Uri[] result = results.toArray(new Uri[results.size()]);
-        final DocumentFile[] resultFiles = new DocumentFile[result.length];
-        for (int i = 0; i < result.length; i++) {
-            resultFiles[i] = new TreeDocumentFile(this, mContext, result[i]);
-        }
-        return resultFiles;
-    }
-
-    private static void closeQuietly(AutoCloseable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (RuntimeException rethrown) {
-                throw rethrown;
-            } catch (Exception ignored) {
-            }
-        }
-    }
-
-    @Override
-    public boolean renameTo(String displayName) {
-        try {
-            final Uri result = DocumentsContract.renameDocument(
-                    mContext.getContentResolver(), mUri, displayName);
-            if (result != null) {
-                mUri = result;
-                return true;
-            } else {
-                return false;
-            }
-        } catch (Exception e) {
-            return false;
-        }
-    }
-}
diff --git a/android/support/v4/testutils/LayoutDirectionActions.java b/android/support/v4/testutils/LayoutDirectionActions.java
new file mode 100644
index 0000000..e71d9ba
--- /dev/null
+++ b/android/support/v4/testutils/LayoutDirectionActions.java
@@ -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.support.v4.testutils;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.View;
+
+import androidx.core.view.ViewCompat;
+
+import org.hamcrest.Matcher;
+
+public class LayoutDirectionActions {
+    /**
+     * Sets layout direction on the view.
+     */
+    public static ViewAction setLayoutDirection(final int layoutDirection) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "set layout direction";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewCompat.setLayoutDirection(view, layoutDirection);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/android/support/v4/testutils/TestUtils.java b/android/support/v4/testutils/TestUtils.java
new file mode 100644
index 0000000..1b3a83a
--- /dev/null
+++ b/android/support/v4/testutils/TestUtils.java
@@ -0,0 +1,110 @@
+/*
+ * 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.testutils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+
+public class TestUtils {
+    /**
+     * Checks whether all the pixels in the specified drawable are of the same specified color.
+     * If the passed <code>Drawable</code> does not have positive intrinsic dimensions set, this
+     * method will throw an <code>IllegalArgumentException</code>. If there is a color mismatch,
+     * this method will call <code>Assert.fail</code> with detailed description of the mismatch.
+     */
+    public static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull Drawable drawable,
+            @ColorInt int color) {
+        int drawableWidth = drawable.getIntrinsicWidth();
+        int drawableHeight = drawable.getIntrinsicHeight();
+
+        if ((drawableWidth <= 0) || (drawableHeight <= 0)) {
+            throw new IllegalArgumentException("Drawable must be configured to have non-zero size");
+        }
+
+        assertAllPixelsOfColor(failMessagePrefix, drawable, drawableWidth, drawableHeight, color,
+                false);
+    }
+
+    /**
+     * Checks whether all the pixels in the specified drawable are of the same specified color.
+     *
+     * In case there is a color mismatch, the behavior of this method depends on the
+     * <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
+     * throw an <code>Exception</code> describing the mismatch. Otherwise this method will call
+     * <code>Assert.fail</code> with detailed description of the mismatch.
+     */
+    public static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull Drawable drawable,
+            int drawableWidth, int drawableHeight, @ColorInt int color,
+            boolean throwExceptionIfFails) {
+        // Create a bitmap
+        Bitmap bitmap = Bitmap.createBitmap(drawableWidth, drawableHeight, Bitmap.Config.ARGB_8888);
+        // Create a canvas that wraps the bitmap
+        Canvas canvas = new Canvas(bitmap);
+        // Configure the drawable to have bounds that match its intrinsic size
+        drawable.setBounds(0, 0, drawableWidth, drawableHeight);
+        // And ask the drawable to draw itself to the canvas / bitmap
+        drawable.draw(canvas);
+
+        try {
+            int[] rowPixels = new int[drawableWidth];
+            for (int row = 0; row < drawableHeight; row++) {
+                bitmap.getPixels(rowPixels, 0, drawableWidth, 0, row, drawableWidth, 1);
+                for (int column = 0; column < drawableWidth; column++) {
+                    if (rowPixels[column] != color) {
+                        String mismatchDescription = failMessagePrefix
+                                + ": expected all drawable colors to be ["
+                                + Color.red(color) + "," + Color.green(color) + ","
+                                + Color.blue(color)
+                                + "] but at position (" + row + "," + column + ") found ["
+                                + Color.red(rowPixels[column]) + ","
+                                + Color.green(rowPixels[column]) + ","
+                                + Color.blue(rowPixels[column]) + "]";
+                        if (throwExceptionIfFails) {
+                            throw new RuntimeException(mismatchDescription);
+                        } else {
+                            fail(mismatchDescription);
+                        }
+                    }
+                }
+            }
+        } finally {
+            bitmap.recycle();
+        }
+    }
+
+    /**
+     * Checks whether the specified rectangle matches the specified left / top / right /
+     * bottom bounds.
+     */
+    public static void assertRectangleBounds(String failMessagePrefix, @NonNull Rect rectangle,
+            int left, int top, int right, int bottom) {
+        assertEquals(failMessagePrefix + " left", rectangle.left, left);
+        assertEquals(failMessagePrefix + " top", rectangle.top, top);
+        assertEquals(failMessagePrefix + " right", rectangle.right, right);
+        assertEquals(failMessagePrefix + " bottom", rectangle.bottom, bottom);
+    }
+}
\ No newline at end of file
diff --git a/android/support/v4/testutils/TestUtilsAssertions.java b/android/support/v4/testutils/TestUtilsAssertions.java
new file mode 100644
index 0000000..b578d30
--- /dev/null
+++ b/android/support/v4/testutils/TestUtilsAssertions.java
@@ -0,0 +1,61 @@
+/*
+ * 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.testutils;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class TestUtilsAssertions {
+
+    /**
+     * Returns an assertion that asserts that there is specified number of fully displayed
+     * children.
+     */
+    public static ViewAssertion hasDisplayedChildren(final int expectedCount) {
+        return new ViewAssertion() {
+            @Override
+            public void check(final View foundView, NoMatchingViewException noViewException) {
+                if (noViewException != null) {
+                    throw noViewException;
+                } else {
+                    if (!(foundView instanceof ViewGroup)) {
+                        throw new AssertionError("View "
+                                + foundView.getClass().getSimpleName() + " is not a ViewGroup");
+                    }
+                    final ViewGroup foundGroup = (ViewGroup) foundView;
+
+                    final int childrenCount = foundGroup.getChildCount();
+                    int childrenDisplayedCount = 0;
+                    for (int i = 0; i < childrenCount; i++) {
+                        if (isDisplayed().matches(foundGroup.getChildAt(i))) {
+                            childrenDisplayedCount++;
+                        }
+                    }
+
+                    if (childrenDisplayedCount != expectedCount) {
+                        throw new AssertionError("Expected " + expectedCount
+                                + " displayed children, but found " + childrenDisplayedCount);
+                    }
+                }
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/android/support/v4/testutils/TestUtilsMatchers.java b/android/support/v4/testutils/TestUtilsMatchers.java
new file mode 100644
index 0000000..0cdbf73
--- /dev/null
+++ b/android/support/v4/testutils/TestUtilsMatchers.java
@@ -0,0 +1,270 @@
+/*
+ * 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.testutils;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.drawable.Drawable;
+import android.support.test.espresso.matcher.BoundedMatcher;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+import androidx.annotation.ColorInt;
+import androidx.core.view.ViewCompat;
+
+import org.hamcrest.Description;
+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.
+     */
+    public static Matcher backgroundColor(@ColorInt final int backgroundColor) {
+        return new BoundedMatcher<View, View>(View.class) {
+            private String failedComparisonDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText("with background color: ");
+
+                description.appendText(failedComparisonDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final View view) {
+                Drawable actualBackgroundDrawable = view.getBackground();
+                if (actualBackgroundDrawable == null) {
+                    return false;
+                }
+
+                // One option is to check if we have a ColorDrawable and then call getColor
+                // but that API is v11+. Instead, we call our helper method that checks whether
+                // all pixels in a Drawable are of the same specified color. Here we pass
+                // hard-coded dimensions of 40x40 since we can't rely on the intrinsic dimensions
+                // being set on our drawable.
+                try {
+                    TestUtils.assertAllPixelsOfColor("", actualBackgroundDrawable,
+                            40, 40, backgroundColor, true);
+                    // If we are here, the color comparison has passed.
+                    failedComparisonDescription = null;
+                    return true;
+                } catch (Throwable t) {
+                    // If we are here, the color comparison has failed.
+                    failedComparisonDescription = t.getMessage();
+                    return false;
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches Views which are an instance of the provided class.
+     */
+    public static Matcher<View> isOfClass(final Class<? extends View> clazz) {
+        if (clazz == null) {
+            fail("Passed null Class instance");
+        }
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is identical to class: " + clazz);
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                return clazz.equals(view.getClass());
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches Views that are aligned to the left / start edge of
+     * their parent.
+     */
+    public static Matcher<View> startAlignedToParent() {
+        return new BoundedMatcher<View, View>(View.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final View view) {
+                final ViewParent parent = view.getParent();
+                if (!(parent instanceof ViewGroup)) {
+                    return false;
+                }
+                final ViewGroup parentGroup = (ViewGroup) parent;
+
+                final int parentLayoutDirection = ViewCompat.getLayoutDirection(parentGroup);
+                if (parentLayoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
+                    if (view.getLeft() == 0) {
+                        return true;
+                    } else {
+                        failedCheckDescription =
+                                "not aligned to start (left) edge of parent : left=" +
+                                        view.getLeft();
+                        return false;
+                    }
+                } else {
+                    if (view.getRight() == parentGroup.getWidth()) {
+                        return true;
+                    } else {
+                        failedCheckDescription =
+                                "not aligned to start (right) edge of parent : right=" +
+                                        view.getRight() + ", parent width=" +
+                                        parentGroup.getWidth();
+                        return false;
+                    }
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches Views that are aligned to the right / end edge of
+     * their parent.
+     */
+    public static Matcher<View> endAlignedToParent() {
+        return new BoundedMatcher<View, View>(View.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final View view) {
+                final ViewParent parent = view.getParent();
+                if (!(parent instanceof ViewGroup)) {
+                    return false;
+                }
+                final ViewGroup parentGroup = (ViewGroup) parent;
+
+                final int parentLayoutDirection = ViewCompat.getLayoutDirection(parentGroup);
+                if (parentLayoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
+                    if (view.getRight() == parentGroup.getWidth()) {
+                        return true;
+                    } else {
+                        failedCheckDescription =
+                                "not aligned to end (right) edge of parent : right=" +
+                                        view.getRight() + ", parent width=" +
+                                        parentGroup.getWidth();
+                        return false;
+                    }
+                } else {
+                    if (view.getLeft() == 0) {
+                        return true;
+                    } else {
+                        failedCheckDescription =
+                                "not aligned to end (left) edge of parent : left=" +
+                                        view.getLeft();
+                        return false;
+                    }
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches Views that are centered horizontally in their parent.
+     */
+    public static Matcher<View> centerAlignedInParent() {
+        return new BoundedMatcher<View, View>(View.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final View view) {
+                final ViewParent parent = view.getParent();
+                if (!(parent instanceof ViewGroup)) {
+                    return false;
+                }
+                final ViewGroup parentGroup = (ViewGroup) parent;
+
+                final int viewLeft = view.getLeft();
+                final int viewRight = view.getRight();
+                final int parentWidth = parentGroup.getWidth();
+
+                final int viewMiddle = (viewLeft + viewRight) / 2;
+                final int parentMiddle = parentWidth / 2;
+
+                // Check that the view is centered in its parent, accounting for off-by-one
+                // pixel difference in case one is even and the other is odd.
+                if (Math.abs(viewMiddle - parentMiddle) > 1) {
+                    failedCheckDescription =
+                            "not aligned to center of parent : own span=[" +
+                                    viewLeft + "-" + viewRight + "], parent width=" + parentWidth;
+                    return false;
+                }
+
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches lists of integer values that match the specified sequence
+     * of values.
+     */
+    public static Matcher<List<Integer>> matches(final int ... expectedValues) {
+        return new TypeSafeMatcher<List<Integer>>() {
+            private String mFailedDescription;
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText(mFailedDescription);
+            }
+
+            @Override
+            protected boolean matchesSafely(List<Integer> item) {
+                int actualCount = item.size();
+                int expectedCount = expectedValues.length;
+
+                if (actualCount != expectedCount) {
+                    mFailedDescription = "Expected " + expectedCount + " values, but got " +
+                            actualCount;
+                    return false;
+                }
+
+                for (int i = 0; i < expectedCount; i++) {
+                    int curr = item.get(i);
+
+                    if (curr != expectedValues[i]) {
+                        mFailedDescription = "At #" + i + " got " + curr + " but should be " +
+                                expectedValues[i];
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        };
+    }
+
+}
diff --git a/android/support/v4/testutils/TextViewActions.java b/android/support/v4/testutils/TextViewActions.java
new file mode 100644
index 0000000..feec51f
--- /dev/null
+++ b/android/support/v4/testutils/TextViewActions.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+
+import android.graphics.drawable.Drawable;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.StyleRes;
+import androidx.core.widget.TextViewCompat;
+
+import org.hamcrest.Matcher;
+
+public class TextViewActions {
+    /**
+     * Sets max lines count on <code>TextView</code>.
+     */
+    public static ViewAction setMaxLines(final int maxLines) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set max lines";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                textView.setMaxLines(maxLines);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets min lines count on <code>TextView</code>.
+     */
+    public static ViewAction setMinLines(final int minLines) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set min lines";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                textView.setMinLines(minLines);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets text content on <code>TextView</code>.
+     */
+    public static ViewAction setText(final @StringRes int stringResId) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set text";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                textView.setText(stringResId);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets text appearance on <code>TextView</code>.
+     */
+    public static ViewAction setTextAppearance(final @StyleRes int styleResId) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set text appearance";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setTextAppearance(textView, styleResId);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets compound drawables on <code>TextView</code>.
+     */
+    public static ViewAction setCompoundDrawablesRelative(final @Nullable Drawable start,
+            final @Nullable Drawable top, final @Nullable Drawable end,
+            final @Nullable Drawable bottom) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set compound drawables";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets compound drawables on <code>TextView</code>.
+     */
+    public static ViewAction setCompoundDrawablesRelativeWithIntrinsicBounds(
+            final @Nullable Drawable start, final @Nullable Drawable top,
+            final @Nullable Drawable end, final @Nullable Drawable bottom) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set compound drawables";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                        textView, start, top, end, bottom);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets compound drawables on <code>TextView</code>.
+     */
+    public static ViewAction setCompoundDrawablesRelativeWithIntrinsicBounds(
+            final @DrawableRes int start, final @DrawableRes int top, final @DrawableRes int end,
+            final @DrawableRes int bottom) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set compound drawables";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                        textView, start, top, end, bottom);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/android/support/v4/text/BidiFormatter.java b/android/support/v4/text/BidiFormatter.java
deleted file mode 100644
index b3b8b1c..0000000
--- a/android/support/v4/text/BidiFormatter.java
+++ /dev/null
@@ -1,958 +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.text;
-
-import android.support.v4.view.ViewCompat;
-import android.text.SpannableStringBuilder;
-
-import java.util.Locale;
-
-import static android.support.v4.text.TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR;
-
-/**
- * Utility class for formatting text for display in a potentially opposite-directionality context
- * without garbling. The directionality of the context is set at formatter creation and the
- * directionality of the text can be either estimated or passed in when known. Provides the
- * following functionality:
- * <p>
- * 1. Bidi Wrapping
- * When text in one language is mixed into a document in another, opposite-directionality language,
- * e.g. when an English business name is embedded in a Hebrew web page, both the inserted string
- * and the text surrounding it may be displayed incorrectly unless the inserted string is explicitly
- * separated from the surrounding text in a "wrapper" that:
- * <p>
- * - Declares its directionality so that the string is displayed correctly. This can be done in
- *   Unicode bidi formatting codes by {@link #unicodeWrap} and similar methods.
- * <p>
- * - Isolates the string's directionality, so it does not unduly affect the surrounding content.
- *   Currently, this can only be done using invisible Unicode characters of the same direction as
- *   the context (LRM or RLM) in addition to the directionality declaration above, thus "resetting"
- *   the directionality to that of the context. The "reset" may need to be done at both ends of the
- *   string. Without "reset" after the string, the string will "stick" to a number or logically
- *   separate opposite-direction text that happens to follow it in-line (even if separated by
- *   neutral content like spaces and punctuation). Without "reset" before the string, the same can
- *   happen there, but only with more opposite-direction text, not a number. One approach is to
- *   "reset" the direction only after each string, on the theory that if the preceding opposite-
- *   direction text is itself bidi-wrapped, the "reset" after it will prevent the sticking. (Doing
- *   the "reset" only before each string definitely does not work because we do not want to require
- *   bidi-wrapping numbers, and a bidi-wrapped opposite-direction string could be followed by a
- *   number.) Still, the safest policy is to do the "reset" on both ends of each string, since RTL
- *   message translations often contain untranslated Latin-script brand names and technical terms,
- *   and one of these can be followed by a bidi-wrapped inserted value. On the other hand, when one
- *   has such a message, it is best to do the "reset" manually in the message translation itself,
- *   since the message's opposite-direction text could be followed by an inserted number, which we
- *   would not bidi-wrap anyway. Thus, "reset" only after the string is the current default. In an
- *   alternative to "reset", recent additions to the HTML, CSS, and Unicode standards allow the
- *   isolation to be part of the directionality declaration. This form of isolation is better than
- *   "reset" because it takes less space, does not require knowing the context directionality, has a
- *   gentler effect than "reset", and protects both ends of the string. However, we do not yet allow
- *   using it because required platforms do not yet support it.
- * <p>
- * Providing these wrapping services is the basic purpose of the bidi formatter.
- * <p>
- * 2. Directionality estimation
- * How does one know whether a string about to be inserted into surrounding text has the same
- * directionality? Well, in many cases, one knows that this must be the case when writing the code
- * doing the insertion, e.g. when a localized message is inserted into a localized page. In such
- * cases there is no need to involve the bidi formatter at all. In some other cases, it need not be
- * the same as the context, but is either constant (e.g. urls are always LTR) or otherwise known.
- * In the remaining cases, e.g. when the string is user-entered or comes from a database, the
- * language of the string (and thus its directionality) is not known a priori, and must be
- * estimated at run-time. The bidi formatter can do this automatically using the default
- * first-strong estimation algorithm. It can also be configured to use a custom directionality
- * estimation object.
- */
-public final class BidiFormatter {
-
-    /**
-     * The default text direction heuristic.
-     */
-    private static TextDirectionHeuristicCompat DEFAULT_TEXT_DIRECTION_HEURISTIC = FIRSTSTRONG_LTR;
-
-    /**
-     * Unicode "Left-To-Right Embedding" (LRE) character.
-     */
-    private static final char LRE = '\u202A';
-
-    /**
-     * Unicode "Right-To-Left Embedding" (RLE) character.
-     */
-    private static final char RLE = '\u202B';
-
-    /**
-     * Unicode "Pop Directional Formatting" (PDF) character.
-     */
-    private static final char PDF = '\u202C';
-
-    /**
-     *  Unicode "Left-To-Right Mark" (LRM) character.
-     */
-    private static final char LRM = '\u200E';
-
-    /*
-     * Unicode "Right-To-Left Mark" (RLM) character.
-     */
-    private static final char RLM = '\u200F';
-
-    /*
-     * String representation of LRM
-     */
-    private static final String LRM_STRING = Character.toString(LRM);
-
-    /*
-     * String representation of RLM
-     */
-    private static final String RLM_STRING = Character.toString(RLM);
-
-    /**
-     * Empty string constant.
-     */
-    private static final String EMPTY_STRING = "";
-
-    /**
-     * A class for building a BidiFormatter with non-default options.
-     */
-    public static final class Builder {
-        private boolean mIsRtlContext;
-        private int mFlags;
-        private TextDirectionHeuristicCompat mTextDirectionHeuristicCompat;
-
-        /**
-         * Constructor.
-         *
-         */
-        public Builder() {
-            initialize(isRtlLocale(Locale.getDefault()));
-        }
-
-        /**
-         * Constructor.
-         *
-         * @param rtlContext Whether the context directionality is RTL.
-         */
-        public Builder(boolean rtlContext) {
-            initialize(rtlContext);
-        }
-
-        /**
-         * Constructor.
-         *
-         * @param locale The context locale.
-         */
-        public Builder(Locale locale) {
-            initialize(isRtlLocale(locale));
-        }
-
-        /**
-         * Initializes the builder with the given context directionality and default options.
-         *
-         * @param isRtlContext Whether the context is RTL or not.
-         */
-        private void initialize(boolean isRtlContext) {
-            mIsRtlContext = isRtlContext;
-            mTextDirectionHeuristicCompat = DEFAULT_TEXT_DIRECTION_HEURISTIC;
-            mFlags = DEFAULT_FLAGS;
-        }
-
-        /**
-         * Specifies whether the BidiFormatter to be built should also "reset" directionality before
-         * a string being bidi-wrapped, not just after it. The default is true.
-         */
-        public Builder stereoReset(boolean stereoReset) {
-            if (stereoReset) {
-                mFlags |= FLAG_STEREO_RESET;
-            } else {
-                mFlags &= ~FLAG_STEREO_RESET;
-            }
-            return this;
-        }
-
-        /**
-         * Specifies the default directionality estimation algorithm to be used by the BidiFormatter.
-         * By default, uses the first-strong heuristic.
-         *
-         * @param heuristic the {@code TextDirectionHeuristic} to use.
-         * @return the builder itself.
-         */
-        public Builder setTextDirectionHeuristic(TextDirectionHeuristicCompat heuristic) {
-            mTextDirectionHeuristicCompat = heuristic;
-            return this;
-        }
-
-        private static BidiFormatter getDefaultInstanceFromContext(boolean isRtlContext) {
-            return isRtlContext ? DEFAULT_RTL_INSTANCE : DEFAULT_LTR_INSTANCE;
-        }
-
-        /**
-         * @return A BidiFormatter with the specified options.
-         */
-        public BidiFormatter build() {
-            if (mFlags == DEFAULT_FLAGS &&
-                    mTextDirectionHeuristicCompat == DEFAULT_TEXT_DIRECTION_HEURISTIC) {
-                return getDefaultInstanceFromContext(mIsRtlContext);
-            }
-            return new BidiFormatter(mIsRtlContext, mFlags, mTextDirectionHeuristicCompat);
-        }
-    }
-
-    //
-    private static final int FLAG_STEREO_RESET = 2;
-    private static final int DEFAULT_FLAGS = FLAG_STEREO_RESET;
-
-    private static final BidiFormatter DEFAULT_LTR_INSTANCE = new BidiFormatter(
-            false /* LTR context */,
-            DEFAULT_FLAGS,
-            DEFAULT_TEXT_DIRECTION_HEURISTIC);
-
-    private static final BidiFormatter DEFAULT_RTL_INSTANCE = new BidiFormatter(
-            true /* RTL context */,
-            DEFAULT_FLAGS,
-            DEFAULT_TEXT_DIRECTION_HEURISTIC);
-
-    private final boolean mIsRtlContext;
-    private final int mFlags;
-    private final TextDirectionHeuristicCompat mDefaultTextDirectionHeuristicCompat;
-
-    /**
-     * Factory for creating an instance of BidiFormatter for the default locale directionality.
-     *
-     */
-    public static BidiFormatter getInstance() {
-        return new Builder().build();
-    }
-
-    /**
-     * Factory for creating an instance of BidiFormatter given the context directionality.
-     *
-     * @param rtlContext Whether the context directionality is RTL.
-     */
-    public static BidiFormatter getInstance(boolean rtlContext) {
-        return new Builder(rtlContext).build();
-    }
-
-    /**
-     * Factory for creating an instance of BidiFormatter given the context locale.
-     *
-     * @param locale The context locale.
-     */
-    public static BidiFormatter getInstance(Locale locale) {
-        return new Builder(locale).build();
-    }
-
-    /**
-     * @param isRtlContext Whether the context directionality is RTL or not.
-     * @param flags The option flags.
-     * @param heuristic The default text direction heuristic.
-     */
-    private BidiFormatter(boolean isRtlContext, int flags, TextDirectionHeuristicCompat heuristic) {
-        mIsRtlContext = isRtlContext;
-        mFlags = flags;
-        mDefaultTextDirectionHeuristicCompat = heuristic;
-    }
-
-    /**
-     * @return Whether the context directionality is RTL
-     */
-    public boolean isRtlContext() {
-        return mIsRtlContext;
-    }
-
-    /**
-     * @return Whether directionality "reset" should also be done before a string being
-     * bidi-wrapped, not just after it.
-     */
-    public boolean getStereoReset() {
-        return (mFlags & FLAG_STEREO_RESET) != 0;
-    }
-
-    /**
-     * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) if either the
-     * overall or the exit directionality of a given CharSequence is opposite to the context
-     * directionality. Putting this after the CharSequence (including its directionality
-     * declaration wrapping) prevents it from "sticking" to other opposite-directionality text or a
-     * number appearing after it inline with only neutral content in between. Otherwise returns
-     * the empty string. While the exit directionality is determined by scanning the end of the
-     * CharSequence, the overall directionality is given explicitly by a heuristic to estimate the
-     * {@code str}'s directionality.
-     *
-     * @param str CharSequence after which the mark may need to appear.
-     * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
-     *                  directionality.
-     * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
-     *     else, the empty .
-     */
-    private String markAfter(CharSequence str, TextDirectionHeuristicCompat heuristic) {
-        final boolean isRtl = heuristic.isRtl(str, 0, str.length());
-        // getExitDir() is called only if needed (short-circuit).
-        if (!mIsRtlContext && (isRtl || getExitDir(str) == DIR_RTL)) {
-            return LRM_STRING;
-        }
-        if (mIsRtlContext && (!isRtl || getExitDir(str) == DIR_LTR)) {
-            return RLM_STRING;
-        }
-        return EMPTY_STRING;
-    }
-
-    /**
-     * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) if either the
-     * overall or the entry directionality of a given CharSequence is opposite to the context
-     * directionality. Putting this before the CharSequence (including its directionality
-     * declaration wrapping) prevents it from "sticking" to other opposite-directionality text
-     * appearing before it inline with only neutral content in between. Otherwise returns the
-     * empty string. While the entry directionality is determined by scanning the beginning of the
-     * CharSequence, the overall directionality is given explicitly by a heuristic to estimate the
-     * {@code str}'s directionality.
-     *
-     * @param str CharSequence before which the mark may need to appear.
-     * @param heuristic The text direction heuristic that will be used to estimate the {@code str}'s
-     *                  directionality.
-     * @return LRM for RTL text in LTR context; RLM for LTR text in RTL context;
-     *     else, the empty string.
-     */
-    private String markBefore(CharSequence str, TextDirectionHeuristicCompat heuristic) {
-        final boolean isRtl = heuristic.isRtl(str, 0, str.length());
-        // getEntryDir() is called only if needed (short-circuit).
-        if (!mIsRtlContext && (isRtl || getEntryDir(str) == DIR_RTL)) {
-            return LRM_STRING;
-        }
-        if (mIsRtlContext && (!isRtl || getEntryDir(str) == DIR_LTR)) {
-            return RLM_STRING;
-        }
-        return EMPTY_STRING;
-    }
-
-    /**
-     * Estimates the directionality of a string using the default text direction heuristic.
-     *
-     * @param str String whose directionality is to be estimated.
-     * @return true if {@code str}'s estimated overall directionality is RTL. Otherwise returns
-     *          false.
-     */
-    public boolean isRtl(String str) {
-        return isRtl((CharSequence) str);
-    }
-
-    /**
-     * Operates like {@link #isRtl(String)}, but takes a CharSequence instead of a string.
-     *
-     * @param str CharSequence whose directionality is to be estimated.
-     * @return true if {@code str}'s estimated overall directionality is RTL. Otherwise returns
-     *          false.
-     */
-    public boolean isRtl(CharSequence str) {
-        return mDefaultTextDirectionHeuristicCompat.isRtl(str, 0, str.length());
-    }
-
-    /**
-     * Formats a string of given directionality for use in plain-text output of the context
-     * directionality, so an opposite-directionality string is neither garbled nor garbles its
-     * surroundings. This makes use of Unicode bidi formatting characters.
-     * <p>
-     * The algorithm: In case the given directionality doesn't match the context directionality, wraps
-     * the string with Unicode bidi formatting characters: RLE+{@code str}+PDF for RTL text, or
-     * LRE+{@code str}+PDF for LTR text.
-     * <p>
-     * If {@code isolate}, directionally isolates the string so that it does not garble its
-     * surroundings. Currently, this is done by "resetting" the directionality after the string by
-     * appending a trailing Unicode bidi mark matching the context directionality (LRM or RLM) when
-     * either the overall directionality or the exit directionality of the string is opposite to
-     * that of the context. Unless the formatter was built using
-     * {@link Builder#stereoReset(boolean)} with a {@code false} argument, also prepends a Unicode
-     * bidi mark matching the context directionality when either the overall directionality or the
-     * entry directionality of the string is opposite to that of the context. Note that as opposed
-     * to the overall directionality, the entry and exit directionalities are determined from the
-     * string itself.
-     * <p>
-     * Does *not* do HTML-escaping.
-     *
-     * @param str The input string.
-     * @param heuristic The algorithm to be used to estimate the string's overall direction.
-     * @param isolate Whether to directionally isolate the string to prevent it from garbling the
-     *     content around it
-     * @return Input string after applying the above processing. {@code null} if {@code str} is
-     *     {@code null}.
-     */
-    public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic, boolean isolate) {
-        if (str == null) return null;
-        return unicodeWrap((CharSequence) str, heuristic, isolate).toString();
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(String,
-     * android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but takes a CharSequence
-     * instead of a string
-     *
-     * @param str The input CharSequence.
-     * @param heuristic The algorithm to be used to estimate the CharSequence's overall direction.
-     *        See {@link android.support.v4.text.TextDirectionHeuristicsCompat} for pre-defined
-     *        heuristics.
-     * @param isolate Whether to directionally isolate the CharSequence to prevent it from garbling
-     *     the content around it
-     * @return Input CharSequence after applying the above processing. {@code null} if {@code str}
-     *     is {@code null}.
-     */
-    public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic,
-            boolean isolate) {
-        if (str == null) return null;
-        final boolean isRtl = heuristic.isRtl(str, 0, str.length());
-        SpannableStringBuilder result = new SpannableStringBuilder();
-        if (getStereoReset() && isolate) {
-            result.append(markBefore(str,
-                    isRtl ? TextDirectionHeuristicsCompat.RTL : TextDirectionHeuristicsCompat.LTR));
-        }
-        if (isRtl != mIsRtlContext) {
-            result.append(isRtl ? RLE : LRE);
-            result.append(str);
-            result.append(PDF);
-        } else {
-            result.append(str);
-        }
-        if (isolate) {
-            result.append(markAfter(str,
-                    isRtl ? TextDirectionHeuristicsCompat.RTL : TextDirectionHeuristicsCompat.LTR));
-        }
-        return result;
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(String, android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but assumes
-     * {@code isolate} is true.
-     *
-     * @param str The input string.
-     * @param heuristic The algorithm to be used to estimate the string's overall direction.
-     * @return Input string after applying the above processing.
-     */
-    public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic) {
-        return unicodeWrap(str, heuristic, true /* isolate */);
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(CharSequence,
-     * android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but assumes {@code isolate}
-     * is true.
-     *
-     * @param str The input CharSequence.
-     * @param heuristic The algorithm to be used to estimate the CharSequence's overall direction.
-     *        See {@link android.support.v4.text.TextDirectionHeuristicsCompat} for pre-defined
-     *        heuristics.
-     * @return Input CharSequence after applying the above processing.
-     */
-    public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic) {
-        return unicodeWrap(str, heuristic, true /* isolate */);
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(String, android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but uses the
-     * formatter's default direction estimation algorithm.
-     *
-     * @param str The input string.
-     * @param isolate Whether to directionally isolate the string to prevent it from garbling the
-     *     content around it
-     * @return Input string after applying the above processing.
-     */
-    public String unicodeWrap(String str, boolean isolate) {
-        return unicodeWrap(str, mDefaultTextDirectionHeuristicCompat, isolate);
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(CharSequence,
-     * android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but uses the formatter's
-     * default direction estimation algorithm.
-     *
-     * @param str The input CharSequence.
-     * @param isolate Whether to directionally isolate the CharSequence to prevent it from garbling
-     *     the content around it
-     * @return Input CharSequence after applying the above processing.
-     */
-    public CharSequence unicodeWrap(CharSequence str, boolean isolate) {
-        return unicodeWrap(str, mDefaultTextDirectionHeuristicCompat, isolate);
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(String, android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but uses the
-     * formatter's default direction estimation algorithm and assumes {@code isolate} is true.
-     *
-     * @param str The input string.
-     * @return Input string after applying the above processing.
-     */
-    public String unicodeWrap(String str) {
-        return unicodeWrap(str, mDefaultTextDirectionHeuristicCompat, true /* isolate */);
-    }
-
-    /**
-     * Operates like {@link #unicodeWrap(CharSequence,
-     * android.support.v4.text.TextDirectionHeuristicCompat, boolean)}, but uses the formatter's
-     * default direction estimation algorithm and assumes {@code isolate} is true.
-     *
-     * @param str The input CharSequence.
-     * @return Input CharSequence after applying the above processing.
-     */
-    public CharSequence unicodeWrap(CharSequence str) {
-        return unicodeWrap(str, mDefaultTextDirectionHeuristicCompat, true /* isolate */);
-    }
-
-    /**
-     * Helper method to return true if the Locale directionality is RTL.
-     *
-     * @param locale The Locale whose directionality will be checked to be RTL or LTR
-     * @return true if the {@code locale} directionality is RTL. False otherwise.
-     */
-    private static boolean isRtlLocale(Locale locale) {
-        return (TextUtilsCompat.getLayoutDirectionFromLocale(locale) == ViewCompat.LAYOUT_DIRECTION_RTL);
-    }
-
-    /**
-     * Enum for directionality type.
-     */
-    private static final int DIR_LTR = -1;
-    private static final int DIR_UNKNOWN = 0;
-    private static final int DIR_RTL = +1;
-
-    /**
-     * Returns the directionality of the last character with strong directionality in the string, or
-     * DIR_UNKNOWN if none was encountered. For efficiency, actually scans backwards from the end of
-     * the string. Treats a non-BN character between an LRE/RLE/LRO/RLO and its matching PDF as a
-     * strong character, LTR after LRE/LRO, and RTL after RLE/RLO. The results are undefined for a
-     * string containing unbalanced LRE/RLE/LRO/RLO/PDF characters. The intended use is to check
-     * whether a logically separate item that starts with a number or a character of the string's
-     * exit directionality and follows this string inline (not counting any neutral characters in
-     * between) would "stick" to it in an opposite-directionality context, thus being displayed in
-     * an incorrect position. An LRM or RLM character (the one of the context's directionality)
-     * between the two will prevent such sticking.
-     *
-     * @param str the string to check.
-     */
-    private static int getExitDir(CharSequence str) {
-        return new DirectionalityEstimator(str, false /* isHtml */).getExitDir();
-    }
-
-    /**
-     * Returns the directionality of the first character with strong directionality in the string,
-     * or DIR_UNKNOWN if none was encountered. Treats a non-BN character between an
-     * LRE/RLE/LRO/RLO and its matching PDF as a strong character, LTR after LRE/LRO, and RTL after
-     * RLE/RLO. The results are undefined for a string containing unbalanced LRE/RLE/LRO/RLO/PDF
-     * characters. The intended use is to check whether a logically separate item that ends with a
-     * character of the string's entry directionality and precedes the string inline (not counting
-     * any neutral characters in between) would "stick" to it in an opposite-directionality context,
-     * thus being displayed in an incorrect position. An LRM or RLM character (the one of the
-     * context's directionality) between the two will prevent such sticking.
-     *
-     * @param str the string to check.
-     */
-    private static int getEntryDir(CharSequence str) {
-        return new DirectionalityEstimator(str, false /* isHtml */).getEntryDir();
-    }
-
-    /**
-     * An object that estimates the directionality of a given string by various methods.
-     *
-     */
-    private static class DirectionalityEstimator {
-
-        // Internal static variables and constants.
-
-        /**
-         * Size of the bidi character class cache. The results of the Character.getDirectionality()
-         * calls on the lowest DIR_TYPE_CACHE_SIZE codepoints are kept in an array for speed.
-         * The 0x700 value is designed to leave all the European and Near Eastern languages in the
-         * cache. It can be reduced to 0x180, restricting the cache to the Western European
-         * languages.
-         */
-        private static final int DIR_TYPE_CACHE_SIZE = 0x700;
-
-        /**
-         * The bidi character class cache.
-         */
-        private static final byte DIR_TYPE_CACHE[];
-
-        static {
-            DIR_TYPE_CACHE = new byte[DIR_TYPE_CACHE_SIZE];
-            for (int i = 0; i < DIR_TYPE_CACHE_SIZE; i++) {
-                DIR_TYPE_CACHE[i] = Character.getDirectionality(i);
-            }
-        }
-
-        // Internal instance variables.
-
-        /**
-         * The text to be scanned.
-         */
-        private final CharSequence text;
-
-        /**
-         * Whether the text to be scanned is to be treated as HTML, i.e. skipping over tags and
-         * entities when looking for the next / preceding dir type.
-         */
-        private final boolean isHtml;
-
-        /**
-         * The length of the text in chars.
-         */
-        private final int length;
-
-        /**
-         * The current position in the text.
-         */
-        private int charIndex;
-
-        /**
-         * The char encountered by the last dirTypeForward or dirTypeBackward call. If it
-         * encountered a supplementary codepoint, this contains a char that is not a valid
-         * codepoint. This is ok, because this member is only used to detect some well-known ASCII
-         * syntax, e.g. "http://" and the beginning of an HTML tag or entity.
-         */
-        private char lastChar;
-
-        /**
-         * Constructor.
-         *
-         * @param text The string to scan.
-         * @param isHtml Whether the text to be scanned is to be treated as HTML, i.e. skipping over
-         *     tags and entities.
-         */
-        DirectionalityEstimator(CharSequence text, boolean isHtml) {
-            this.text = text;
-            this.isHtml = isHtml;
-            length = text.length();
-        }
-
-        /**
-         * Returns the directionality of the first character with strong directionality in the
-         * string, or DIR_UNKNOWN if none was encountered. Treats a non-BN character between an
-         * LRE/RLE/LRO/RLO and its matching PDF as a strong character, LTR after LRE/LRO, and RTL
-         * after RLE/RLO. The results are undefined for a string containing unbalanced
-         * LRE/RLE/LRO/RLO/PDF characters.
-         */
-        int getEntryDir() {
-            // The reason for this method name, as opposed to getFirstStrongDir(), is that
-            // "first strong" is a commonly used description of Unicode's estimation algorithm,
-            // but the two must treat formatting characters quite differently. Thus, we are staying
-            // away from both "first" and "last" in these method names to avoid confusion.
-            charIndex = 0;
-            int embeddingLevel = 0;
-            int embeddingLevelDir = DIR_UNKNOWN;
-            int firstNonEmptyEmbeddingLevel = 0;
-            while (charIndex < length && firstNonEmptyEmbeddingLevel == 0) {
-                switch (dirTypeForward()) {
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
-                        ++embeddingLevel;
-                        embeddingLevelDir = DIR_LTR;
-                        break;
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
-                        ++embeddingLevel;
-                        embeddingLevelDir = DIR_RTL;
-                        break;
-                    case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
-                        --embeddingLevel;
-                        // To restore embeddingLevelDir to its previous value, we would need a
-                        // stack, which we want to avoid. Thus, at this point we do not know the
-                        // current embedding's directionality.
-                        embeddingLevelDir = DIR_UNKNOWN;
-                        break;
-                    case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
-                        break;
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-                        if (embeddingLevel == 0) {
-                            return DIR_LTR;
-                        }
-                        firstNonEmptyEmbeddingLevel = embeddingLevel;
-                        break;
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                        if (embeddingLevel == 0) {
-                            return DIR_RTL;
-                        }
-                        firstNonEmptyEmbeddingLevel = embeddingLevel;
-                        break;
-                    default:
-                        firstNonEmptyEmbeddingLevel = embeddingLevel;
-                        break;
-                }
-            }
-
-            // We have either found a non-empty embedding or scanned the entire string finding
-            // neither a non-empty embedding nor a strong character outside of an embedding.
-            if (firstNonEmptyEmbeddingLevel == 0) {
-                // We have not found a non-empty embedding. Thus, the string contains neither a
-                // non-empty embedding nor a strong character outside of an embedding.
-                return DIR_UNKNOWN;
-            }
-
-            // We have found a non-empty embedding.
-            if (embeddingLevelDir != DIR_UNKNOWN) {
-                // We know the directionality of the non-empty embedding.
-                return embeddingLevelDir;
-            }
-
-            // We do not remember the directionality of the non-empty embedding we found. So, we go
-            // backwards to find the start of the non-empty embedding and get its directionality.
-            while (charIndex > 0) {
-                switch (dirTypeBackward()) {
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
-                        if (firstNonEmptyEmbeddingLevel == embeddingLevel) {
-                            return DIR_LTR;
-                        }
-                        --embeddingLevel;
-                        break;
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
-                        if (firstNonEmptyEmbeddingLevel == embeddingLevel) {
-                            return DIR_RTL;
-                        }
-                        --embeddingLevel;
-                        break;
-                    case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
-                        ++embeddingLevel;
-                        break;
-                }
-            }
-            // We should never get here.
-            return DIR_UNKNOWN;
-        }
-
-        /**
-         * Returns the directionality of the last character with strong directionality in the
-         * string, or DIR_UNKNOWN if none was encountered. For efficiency, actually scans backwards
-         * from the end of the string. Treats a non-BN character between an LRE/RLE/LRO/RLO and its
-         * matching PDF as a strong character, LTR after LRE/LRO, and RTL after RLE/RLO. The results
-         * are undefined for a string containing unbalanced LRE/RLE/LRO/RLO/PDF characters.
-         */
-        int getExitDir() {
-            // The reason for this method name, as opposed to getLastStrongDir(), is that "last
-            // strong" sounds like the exact opposite of "first strong", which is a commonly used
-            // description of Unicode's estimation algorithm (getUnicodeDir() above), but the two
-            // must treat formatting characters quite differently. Thus, we are staying away from
-            // both "first" and "last" in these method names to avoid confusion.
-            charIndex = length;
-            int embeddingLevel = 0;
-            int lastNonEmptyEmbeddingLevel = 0;
-            while (charIndex > 0) {
-                switch (dirTypeBackward()) {
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-                        if (embeddingLevel == 0) {
-                            return DIR_LTR;
-                        }
-                        if (lastNonEmptyEmbeddingLevel == 0) {
-                            lastNonEmptyEmbeddingLevel = embeddingLevel;
-                        }
-                        break;
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
-                    case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
-                        if (lastNonEmptyEmbeddingLevel == embeddingLevel) {
-                            return DIR_LTR;
-                        }
-                        --embeddingLevel;
-                        break;
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                        if (embeddingLevel == 0) {
-                            return DIR_RTL;
-                        }
-                        if (lastNonEmptyEmbeddingLevel == 0) {
-                            lastNonEmptyEmbeddingLevel = embeddingLevel;
-                        }
-                        break;
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
-                    case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
-                        if (lastNonEmptyEmbeddingLevel == embeddingLevel) {
-                            return DIR_RTL;
-                        }
-                        --embeddingLevel;
-                        break;
-                    case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
-                        ++embeddingLevel;
-                        break;
-                    case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
-                        break;
-                    default:
-                        if (lastNonEmptyEmbeddingLevel == 0) {
-                            lastNonEmptyEmbeddingLevel = embeddingLevel;
-                        }
-                        break;
-                }
-            }
-            return DIR_UNKNOWN;
-        }
-
-        // Internal methods
-
-        /**
-         * Gets the bidi character class, i.e. Character.getDirectionality(), of a given char, using
-         * a cache for speed. Not designed for supplementary codepoints, whose results we do not
-         * cache.
-         */
-        private static byte getCachedDirectionality(char c) {
-            return c < DIR_TYPE_CACHE_SIZE ? DIR_TYPE_CACHE[c] : Character.getDirectionality(c);
-        }
-
-        /**
-         * Returns the Character.DIRECTIONALITY_... value of the next codepoint and advances
-         * charIndex. If isHtml, and the codepoint is '<' or '&', advances through the tag/entity,
-         * and returns Character.DIRECTIONALITY_WHITESPACE. For an entity, it would be best to
-         * figure out the actual character, and return its dirtype, but treating it as whitespace is
-         * good enough for our purposes.
-         *
-         * @throws java.lang.IndexOutOfBoundsException if called when charIndex >= length or < 0.
-         */
-        byte dirTypeForward() {
-            lastChar = text.charAt(charIndex);
-            if (Character.isHighSurrogate(lastChar)) {
-                int codePoint = Character.codePointAt(text, charIndex);
-                charIndex += Character.charCount(codePoint);
-                return Character.getDirectionality(codePoint);
-            }
-            charIndex++;
-            byte dirType = getCachedDirectionality(lastChar);
-            if (isHtml) {
-                // Process tags and entities.
-                if (lastChar == '<') {
-                    dirType = skipTagForward();
-                } else if (lastChar == '&') {
-                    dirType = skipEntityForward();
-                }
-            }
-            return dirType;
-        }
-
-        /**
-         * Returns the Character.DIRECTIONALITY_... value of the preceding codepoint and advances
-         * charIndex backwards. If isHtml, and the codepoint is the end of a complete HTML tag or
-         * entity, advances over the whole tag/entity and returns
-         * Character.DIRECTIONALITY_WHITESPACE. For an entity, it would be best to figure out the
-         * actual character, and return its dirtype, but treating it as whitespace is good enough
-         * for our purposes.
-         *
-         * @throws java.lang.IndexOutOfBoundsException if called when charIndex > length or <= 0.
-         */
-        byte dirTypeBackward() {
-            lastChar = text.charAt(charIndex - 1);
-            if (Character.isLowSurrogate(lastChar)) {
-                int codePoint = Character.codePointBefore(text, charIndex);
-                charIndex -= Character.charCount(codePoint);
-                return Character.getDirectionality(codePoint);
-            }
-            charIndex--;
-            byte dirType = getCachedDirectionality(lastChar);
-            if (isHtml) {
-                // Process tags and entities.
-                if (lastChar == '>') {
-                    dirType = skipTagBackward();
-                } else if (lastChar == ';') {
-                    dirType = skipEntityBackward();
-                }
-            }
-            return dirType;
-        }
-
-        /**
-         * Advances charIndex forward through an HTML tag (after the opening &lt; has already been
-         * read) and returns Character.DIRECTIONALITY_WHITESPACE. If there is no matching &gt;,
-         * does not change charIndex and returns Character.DIRECTIONALITY_OTHER_NEUTRALS (for the
-         * &lt; that hadn't been part of a tag after all).
-         */
-        private byte skipTagForward() {
-            int initialCharIndex = charIndex;
-            while (charIndex < length) {
-                lastChar = text.charAt(charIndex++);
-                if (lastChar == '>') {
-                    // The end of the tag.
-                    return Character.DIRECTIONALITY_WHITESPACE;
-                }
-                if (lastChar == '"' || lastChar == '\'') {
-                    // Skip over a quoted attribute value inside the tag.
-                    char quote = lastChar;
-                    while (charIndex < length && (lastChar = text.charAt(charIndex++)) != quote) {}
-                }
-            }
-            // The original '<' wasn't the start of a tag after all.
-            charIndex = initialCharIndex;
-            lastChar = '<';
-            return Character.DIRECTIONALITY_OTHER_NEUTRALS;
-        }
-
-        /**
-         * Advances charIndex backward through an HTML tag (after the closing &gt; has already been
-         * read) and returns Character.DIRECTIONALITY_WHITESPACE. If there is no matching &lt;, does
-         * not change charIndex and returns Character.DIRECTIONALITY_OTHER_NEUTRALS (for the &gt;
-         * that hadn't been part of a tag after all). Nevertheless, the running time for calling
-         * skipTagBackward() in a loop remains linear in the size of the text, even for a text like
-         * "&gt;&gt;&gt;&gt;", because skipTagBackward() also stops looking for a matching &lt;
-         * when it encounters another &gt;.
-         */
-        private byte skipTagBackward() {
-            int initialCharIndex = charIndex;
-            while (charIndex > 0) {
-                lastChar = text.charAt(--charIndex);
-                if (lastChar == '<') {
-                    // The start of the tag.
-                    return Character.DIRECTIONALITY_WHITESPACE;
-                }
-                if (lastChar == '>') {
-                    break;
-                }
-                if (lastChar == '"' || lastChar == '\'') {
-                    // Skip over a quoted attribute value inside the tag.
-                    char quote = lastChar;
-                    while (charIndex > 0 && (lastChar = text.charAt(--charIndex)) != quote) {}
-                }
-            }
-            // The original '>' wasn't the end of a tag after all.
-            charIndex = initialCharIndex;
-            lastChar = '>';
-            return Character.DIRECTIONALITY_OTHER_NEUTRALS;
-        }
-
-        /**
-         * Advances charIndex forward through an HTML character entity tag (after the opening
-         * &amp; has already been read) and returns Character.DIRECTIONALITY_WHITESPACE. It would be
-         * best to figure out the actual character and return its dirtype, but this is good enough.
-         */
-        private byte skipEntityForward() {
-            while (charIndex < length && (lastChar = text.charAt(charIndex++)) != ';') {}
-            return Character.DIRECTIONALITY_WHITESPACE;
-        }
-
-        /**
-         * Advances charIndex backward through an HTML character entity tag (after the closing ;
-         * has already been read) and returns Character.DIRECTIONALITY_WHITESPACE. It would be best
-         * to figure out the actual character and return its dirtype, but this is good enough.
-         * If there is no matching &amp;, does not change charIndex and returns
-         * Character.DIRECTIONALITY_OTHER_NEUTRALS (for the ';' that did not start an entity after
-         * all). Nevertheless, the running time for calling skipEntityBackward() in a loop remains
-         * linear in the size of the text, even for a text like ";;;;;;;", because skipTagBackward()
-         * also stops looking for a matching &amp; when it encounters another ;.
-         */
-        private byte skipEntityBackward() {
-            int initialCharIndex = charIndex;
-            while (charIndex > 0) {
-                lastChar = text.charAt(--charIndex);
-                if (lastChar == '&') {
-                    return Character.DIRECTIONALITY_WHITESPACE;
-                }
-                if (lastChar == ';') {
-                    break;
-                }
-            }
-            charIndex = initialCharIndex;
-            lastChar = ';';
-            return Character.DIRECTIONALITY_OTHER_NEUTRALS;
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v4/text/ICUCompat.java b/android/support/v4/text/ICUCompat.java
deleted file mode 100644
index 7234156..0000000
--- a/android/support/v4/text/ICUCompat.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.text;
-
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Locale;
-
-public final class ICUCompat {
-    private static final String TAG = "ICUCompat";
-
-    private static Method sGetScriptMethod;
-    private static Method sAddLikelySubtagsMethod;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            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);
-            }
-        } else {
-            try {
-                final Class<?> clazz = Class.forName("libcore.icu.ICU");
-                if (clazz != null) {
-                    sGetScriptMethod = clazz.getMethod("getScript",
-                            new Class[]{ String.class });
-                    sAddLikelySubtagsMethod = clazz.getMethod("addLikelySubtags",
-                            new Class[]{ String.class });
-                }
-            } catch (Exception e) {
-                sGetScriptMethod = null;
-                sAddLikelySubtagsMethod = null;
-
-                // Nothing we can do here, we just log the exception
-                Log.w(TAG, e);
-            }
-        }
-    }
-
-    /**
-     * Returns the script for a given Locale.
-     *
-     * If the locale isn't already in its maximal form, likely subtags for the provided locale
-     * ID are added before we determine the script. For further details, see the following CLDR
-     * technical report :
-     *
-     * http://www.unicode.org/reports/tr35/#Likely_Subtags
-     *
-     * If locale is already in the maximal form, or there is no data available for maximization,
-     * it will be just returned. For example, "und-Zzzz" cannot be maximized, since there is no
-     * reasonable maximization.
-     *
-     * Examples:
-     *
-     * "en" maximizes to "en_Latn_US"
-     * "de" maximizes to "de_Latn_US"
-     * "sr" maximizes to "sr_Cyrl_RS"
-     * "sh" maximizes to "sr_Latn_RS" (Note this will not reverse.)
-     * "zh_Hani" maximizes to "zh_Hans_CN" (Note this will not reverse.)
-     *
-     * @return The script for a given Locale if ICU library is available, otherwise null.
-     */
-    @Nullable
-    public static String maximizeAndGetScript(Locale locale) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            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();
-        } else {
-            final String localeWithSubtags = addLikelySubtags(locale);
-            if (localeWithSubtags != null) {
-                return getScript(localeWithSubtags);
-            }
-
-            return null;
-        }
-    }
-
-    private static String getScript(String localeStr) {
-        try {
-            if (sGetScriptMethod != null) {
-                final Object[] args = new Object[] { localeStr };
-                return (String) sGetScriptMethod.invoke(null, args);
-            }
-        } catch (IllegalAccessException e) {
-            // Nothing we can do here, we just log the exception
-            Log.w(TAG, e);
-        } catch (InvocationTargetException e) {
-            // Nothing we can do here, we just log the exception
-            Log.w(TAG, e);
-        }
-        return null;
-    }
-
-    private static String addLikelySubtags(Locale locale) {
-        final String localeStr = locale.toString();
-        try {
-            if (sAddLikelySubtagsMethod != null) {
-                final Object[] args = new Object[] { localeStr };
-                return (String) sAddLikelySubtagsMethod.invoke(null, args);
-            }
-        } catch (IllegalAccessException e) {
-            // Nothing we can do here, we just log the exception
-            Log.w(TAG, e);
-        } catch (InvocationTargetException e) {
-            // Nothing we can do here, we just log the exception
-            Log.w(TAG, e);
-        }
-
-        return localeStr;
-    }
-
-    private ICUCompat() {}
-}
diff --git a/android/support/v4/text/TextDirectionHeuristicCompat.java b/android/support/v4/text/TextDirectionHeuristicCompat.java
deleted file mode 100644
index ab9b2f7..0000000
--- a/android/support/v4/text/TextDirectionHeuristicCompat.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.text;
-
-/**
- * Interface for objects that use a heuristic for guessing at the paragraph direction by examining text.
- */
-public interface TextDirectionHeuristicCompat {
-    /**
-     * Guess if a chars array is in the RTL direction or not.
-     *
-     * @param array the char array.
-     * @param start start index, inclusive.
-     * @param count the length to check, must not be negative and not greater than
-     *          {@code array.length - start}.
-     * @return true if all chars in the range are to be considered in a RTL direction,
-     *          false otherwise.
-     */
-    boolean isRtl(char[] array, int start, int count);
-
-    /**
-     * Guess if a {@code CharSequence} is in the RTL direction or not.
-     *
-     * @param cs the CharSequence.
-     * @param start start index, inclusive.
-     * @param count the length to check, must not be negative and not greater than
-     *            {@code CharSequence.length() - start}.
-     * @return true if all chars in the range are to be considered in a RTL direction,
-     *          false otherwise.
-     */
-    boolean isRtl(CharSequence cs, int start, int count);
-}
diff --git a/android/support/v4/text/TextDirectionHeuristicsCompat.java b/android/support/v4/text/TextDirectionHeuristicsCompat.java
deleted file mode 100644
index d55ae8a..0000000
--- a/android/support/v4/text/TextDirectionHeuristicsCompat.java
+++ /dev/null
@@ -1,259 +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.text;
-
-
-import android.support.v4.view.ViewCompat;
-
-import java.nio.CharBuffer;
-
-/**
- * Some objects that implement TextDirectionHeuristic.
- *
- */
-public final class TextDirectionHeuristicsCompat {
-
-    /**
-     * Always decides that the direction is left to right.
-     */
-    public static final android.support.v4.text.TextDirectionHeuristicCompat LTR =
-            new TextDirectionHeuristicInternal(null /* no algorithm */, false);
-
-    /**
-     * Always decides that the direction is right to left.
-     */
-    public static final android.support.v4.text.TextDirectionHeuristicCompat RTL =
-            new TextDirectionHeuristicInternal(null /* no algorithm */, true);
-
-    /**
-     * Determines the direction based on the first strong directional character, including bidi
-     * format chars, falling back to left to right if it finds none. This is the default behavior
-     * of the Unicode Bidirectional Algorithm.
-     */
-    public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_LTR =
-            new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
-
-    /**
-     * Determines the direction based on the first strong directional character, including bidi
-     * format chars, falling back to right to left if it finds none. This is similar to the default
-     * behavior of the Unicode Bidirectional Algorithm, just with different fallback behavior.
-     */
-    public static final android.support.v4.text.TextDirectionHeuristicCompat FIRSTSTRONG_RTL =
-            new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
-
-    /**
-     * If the text contains any strong right to left non-format character, determines that the
-     * direction is right to left, falling back to left to right if it finds none.
-     */
-    public static final android.support.v4.text.TextDirectionHeuristicCompat ANYRTL_LTR =
-            new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
-
-    /**
-     * Force the paragraph direction to the Locale direction. Falls back to left to right.
-     */
-    public static final android.support.v4.text.TextDirectionHeuristicCompat LOCALE =
-            TextDirectionHeuristicLocale.INSTANCE;
-
-    /**
-     * State constants for taking care about true / false / unknown
-     */
-    private static final int STATE_TRUE = 0;
-    private static final int STATE_FALSE = 1;
-    private static final int STATE_UNKNOWN = 2;
-
-    static int isRtlText(int directionality) {
-        switch (directionality) {
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-                return STATE_FALSE;
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                return STATE_TRUE;
-            default:
-                return STATE_UNKNOWN;
-        }
-    }
-
-    static int isRtlTextOrFormat(int directionality) {
-        switch (directionality) {
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
-            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
-                return STATE_FALSE;
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
-            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
-                return STATE_TRUE;
-            default:
-                return STATE_UNKNOWN;
-        }
-    }
-
-    /**
-     * Computes the text direction based on an algorithm.  Subclasses implement
-     * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
-     * direction from the text alone.
-     */
-    private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristicCompat {
-        private final TextDirectionAlgorithm mAlgorithm;
-
-        public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
-            mAlgorithm = algorithm;
-        }
-
-        /**
-         * Return true if the default text direction is rtl.
-         */
-        abstract protected boolean defaultIsRtl();
-
-        @Override
-        public boolean isRtl(char[] array, int start, int count) {
-            return isRtl(CharBuffer.wrap(array), start, count);
-        }
-
-        @Override
-        public boolean isRtl(CharSequence cs, int start, int count) {
-            if (cs == null || start < 0 || count < 0 || cs.length() - count < start) {
-                throw new IllegalArgumentException();
-            }
-            if (mAlgorithm == null) {
-                return defaultIsRtl();
-            }
-            return doCheck(cs, start, count);
-        }
-
-        private boolean doCheck(CharSequence cs, int start, int count) {
-            switch(mAlgorithm.checkRtl(cs, start, count)) {
-                case STATE_TRUE:
-                    return true;
-                case STATE_FALSE:
-                    return false;
-                default:
-                    return defaultIsRtl();
-            }
-        }
-    }
-
-    private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
-        private final boolean mDefaultIsRtl;
-
-        TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
-                boolean defaultIsRtl) {
-            super(algorithm);
-            mDefaultIsRtl = defaultIsRtl;
-        }
-
-        @Override
-        protected boolean defaultIsRtl() {
-            return mDefaultIsRtl;
-        }
-    }
-
-    /**
-     * Interface for an algorithm to guess the direction of a paragraph of text.
-     */
-    private static interface TextDirectionAlgorithm {
-        /**
-         * Returns whether the range of text is RTL according to the algorithm.
-         */
-        int checkRtl(CharSequence cs, int start, int count);
-    }
-
-    /**
-     * Algorithm that uses the first strong directional character to determine the paragraph
-     * direction. This is the standard Unicode Bidirectional algorithm.
-     */
-    private static class FirstStrong implements TextDirectionAlgorithm {
-        @Override
-        public int checkRtl(CharSequence cs, int start, int count) {
-            int result = STATE_UNKNOWN;
-            for (int i = start, e = start + count; i < e && result == STATE_UNKNOWN; ++i) {
-                result = isRtlTextOrFormat(Character.getDirectionality(cs.charAt(i)));
-            }
-            return result;
-        }
-
-        private FirstStrong() {
-        }
-
-        public static final FirstStrong INSTANCE = new FirstStrong();
-    }
-
-    /**
-     * Algorithm that uses the presence of any strong directional non-format
-     * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
-     * direction of text.
-     */
-    private static class AnyStrong implements TextDirectionAlgorithm {
-        private final boolean mLookForRtl;
-
-        @Override
-        public int checkRtl(CharSequence cs, int start, int count) {
-            boolean haveUnlookedFor = false;
-            for (int i = start, e = start + count; i < e; ++i) {
-                switch (isRtlText(Character.getDirectionality(cs.charAt(i)))) {
-                    case STATE_TRUE:
-                        if (mLookForRtl) {
-                            return STATE_TRUE;
-                        }
-                        haveUnlookedFor = true;
-                        break;
-                    case STATE_FALSE:
-                        if (!mLookForRtl) {
-                            return STATE_FALSE;
-                        }
-                        haveUnlookedFor = true;
-                        break;
-                    default:
-                        break;
-                }
-            }
-            if (haveUnlookedFor) {
-                return mLookForRtl ? STATE_FALSE : STATE_TRUE;
-            }
-            return STATE_UNKNOWN;
-        }
-
-        private AnyStrong(boolean lookForRtl) {
-            this.mLookForRtl = lookForRtl;
-        }
-
-        public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
-        public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
-    }
-
-    /**
-     * Algorithm that uses the Locale direction to force the direction of a paragraph.
-     */
-    private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
-
-        public TextDirectionHeuristicLocale() {
-            super(null);
-        }
-
-        @Override
-        protected boolean defaultIsRtl() {
-            final int dir = TextUtilsCompat.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
-            return (dir == ViewCompat.LAYOUT_DIRECTION_RTL);
-        }
-
-        public static final TextDirectionHeuristicLocale INSTANCE =
-                new TextDirectionHeuristicLocale();
-    }
-
-    private TextDirectionHeuristicsCompat() {}
-}
diff --git a/android/support/v4/text/TextUtilsCompat.java b/android/support/v4/text/TextUtilsCompat.java
deleted file mode 100644
index ee58ab0..0000000
--- a/android/support/v4/text/TextUtilsCompat.java
+++ /dev/null
@@ -1,130 +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.text;
-
-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 final Locale ROOT = new Locale("", "");
-    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++) {
-                c = s.charAt(i);
-                switch (c) {
-                    case '<':
-                        sb.append("&lt;"); //$NON-NLS-1$
-                        break;
-                    case '>':
-                        sb.append("&gt;"); //$NON-NLS-1$
-                        break;
-                    case '&':
-                        sb.append("&amp;"); //$NON-NLS-1$
-                        break;
-                    case '\'':
-                        //http://www.w3.org/TR/xhtml1
-                        // The named character reference &apos; (the apostrophe, U+0027) was
-                        // introduced in XML 1.0 but does not appear in HTML. Authors should
-                        // therefore use &#39; instead of &apos; to work as expected in HTML 4
-                        // user agents.
-                        sb.append("&#39;"); //$NON-NLS-1$
-                        break;
-                    case '"':
-                        sb.append("&quot;"); //$NON-NLS-1$
-                        break;
-                    default:
-                        sb.append(c);
-                }
-            }
-            return sb.toString();
-        }
-    }
-
-    /**
-     * 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)) {
-                    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 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}.
-     */
-    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 TextUtilsCompat() {}
-}
diff --git a/android/support/v4/text/util/LinkifyCompat.java b/android/support/v4/text/util/LinkifyCompat.java
deleted file mode 100644
index b7bf352..0000000
--- a/android/support/v4/text/util/LinkifyCompat.java
+++ /dev/null
@@ -1,540 +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.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;
-import android.text.Spanned;
-import android.text.method.LinkMovementMethod;
-import android.text.method.MovementMethod;
-import android.text.style.URLSpan;
-import android.text.util.Linkify;
-import android.text.util.Linkify.MatchFilter;
-import android.text.util.Linkify.TransformFilter;
-import android.webkit.WebView;
-import android.widget.TextView;
-
-import java.io.UnsupportedEncodingException;
-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;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * LinkifyCompat brings in {@code Linkify} improvements for URLs and email addresses to older API
- * levels.
- */
-public final class LinkifyCompat {
-    private static final String[] EMPTY_STRING = new String[0];
-
-    private static final Comparator<LinkSpec>  COMPARATOR = new Comparator<LinkSpec>() {
-        @Override
-        public final int compare(LinkSpec a, LinkSpec b) {
-            if (a.start < b.start) {
-                return -1;
-            }
-
-            if (a.start > b.start) {
-                return 1;
-            }
-
-            if (a.end < b.end) {
-                return 1;
-            }
-
-            if (a.end > b.end) {
-                return -1;
-            }
-
-            return 0;
-        }
-    };
-
-    /** @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)
-    public @interface LinkifyMask {}
-
-    /**
-     *  Scans the text of the provided Spannable and turns all occurrences
-     *  of the link types indicated in the mask into clickable links.
-     *  If the mask is nonzero, it also removes any existing URLSpans
-     *  attached to the Spannable, to avoid problems if you call it
-     *  repeatedly on the same text.
-     *
-     *  @param text Spannable whose text is to be marked-up with links
-     *  @param mask Mask to define which kinds of links will be searched.
-     *
-     *  @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 >= 27) {
-            return Linkify.addLinks(text, mask);
-        }
-        if (mask == 0) {
-            return false;
-        }
-
-        URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);
-
-        for (int i = old.length - 1; i >= 0; i--) {
-            text.removeSpan(old[i]);
-        }
-
-        // Use framework to linkify phone numbers.
-        boolean frameworkReturn = false;
-        if ((mask & Linkify.PHONE_NUMBERS) != 0) {
-            frameworkReturn = Linkify.addLinks(text, Linkify.PHONE_NUMBERS);
-        }
-
-        ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();
-
-        if ((mask & Linkify.WEB_URLS) != 0) {
-            gatherLinks(links, text, PatternsCompat.AUTOLINK_WEB_URL,
-                    new String[] { "http://", "https://", "rtsp://" },
-                    Linkify.sUrlMatchFilter, null);
-        }
-
-        if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
-            gatherLinks(links, text, PatternsCompat.AUTOLINK_EMAIL_ADDRESS,
-                    new String[] { "mailto:" },
-                    null, null);
-        }
-
-        if ((mask & Linkify.MAP_ADDRESSES) != 0) {
-            gatherMapLinks(links, text);
-        }
-
-        pruneOverlaps(links, text);
-
-        if (links.size() == 0) {
-            return false;
-        }
-
-        for (LinkSpec link: links) {
-            if (link.frameworkAddedSpan == null) {
-                applyLink(link.url, link.start, link.end, text);
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     *  Scans the text of the provided TextView and turns all occurrences of
-     *  the link types indicated in the mask into clickable links.  If matches
-     *  are found the movement method for the TextView is set to
-     *  LinkMovementMethod.
-     *
-     *  @param text TextView whose text is to be marked-up with links
-     *  @param mask Mask to define which kinds of links will be searched.
-     *
-     *  @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;
-        }
-
-        CharSequence t = text.getText();
-
-        if (t instanceof Spannable) {
-            if (addLinks((Spannable) t, mask)) {
-                addLinkMovementMethod(text);
-                return true;
-            }
-
-            return false;
-        } else {
-            SpannableString s = SpannableString.valueOf(t);
-
-            if (addLinks(s, mask)) {
-                addLinkMovementMethod(text);
-                text.setText(s);
-
-                return true;
-            }
-
-            return false;
-        }
-    }
-
-    /**
-     *  Applies a regex to the text of a TextView turning the matches into
-     *  links.  If links are found then UrlSpans are applied to the link
-     *  text match areas, and the movement method for the text is changed
-     *  to LinkMovementMethod.
-     *
-     *  @param text         TextView whose text is to be marked-up with links
-     *  @param pattern      Regex pattern to be used for finding links
-     *  @param scheme       URL scheme string (eg <code>http://</code>) to be
-     *                      prepended to the links that do not start with this scheme.
-     */
-    public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
-            @Nullable String scheme) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            Linkify.addLinks(text, pattern, scheme);
-            return;
-        }
-        addLinks(text, pattern, scheme, null, null, null);
-    }
-
-    /**
-     *  Applies a regex to the text of a TextView turning the matches into
-     *  links.  If links are found then UrlSpans are applied to the link
-     *  text match areas, and the movement method for the text is changed
-     *  to LinkMovementMethod.
-     *
-     *  @param text         TextView whose text is to be marked-up with links
-     *  @param pattern      Regex pattern to be used for finding links
-     *  @param scheme       URL scheme string (eg <code>http://</code>) to be
-     *                      prepended to the links that do not start with this scheme.
-     *  @param matchFilter  The filter that is used to allow the client code
-     *                      additional control over which pattern matches are
-     *                      to be converted into links.
-     */
-    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);
-    }
-
-    /**
-     *  Applies a regex to the text of a TextView turning the matches into
-     *  links.  If links are found then UrlSpans are applied to the link
-     *  text match areas, and the movement method for the text is changed
-     *  to LinkMovementMethod.
-     *
-     *  @param text TextView whose text is to be marked-up with links.
-     *  @param pattern Regex pattern to be used for finding links.
-     *  @param defaultScheme The default scheme to be prepended to links if the link does not
-     *                       start with one of the <code>schemes</code> given.
-     *  @param schemes Array of schemes (eg <code>http://</code>) to check if the link found
-     *                 contains a scheme. Passing a null or empty value means prepend defaultScheme
-     *                 to all links.
-     *  @param matchFilter  The filter that is used to allow the client code additional control
-     *                      over which pattern matches are to be converted into links.
-     *  @param transformFilter Filter to allow the client code to update the link found.
-     */
-    public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
-            @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,
-                transformFilter);
-        if (linksAdded) {
-            text.setText(spannable);
-            addLinkMovementMethod(text);
-        }
-    }
-
-    /**
-     *  Applies a regex to a Spannable turning the matches into
-     *  links.
-     *
-     *  @param text         Spannable whose text is to be marked-up with links
-     *  @param pattern      Regex pattern to be used for finding links
-     *  @param scheme       URL scheme string (eg <code>http://</code>) to be
-     *                      prepended to the links that do not start with this scheme.
-     */
-    public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
-            @Nullable String scheme) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            return Linkify.addLinks(text, pattern, scheme);
-        }
-        return addLinks(text, pattern, scheme, null, null, null);
-    }
-
-    /**
-     * Applies a regex to a Spannable turning the matches into
-     * links.
-     *
-     * @param spannable    Spannable whose text is to be marked-up with links
-     * @param pattern      Regex pattern to be used for finding links
-     * @param scheme       URL scheme string (eg <code>http://</code>) to be
-     *                     prepended to the links that do not start with this scheme.
-     * @param matchFilter  The filter that is used to allow the client code
-     *                     additional control over which pattern matches are
-     *                     to be converted into links.
-     * @param transformFilter Filter to allow the client code to update the link found.
-     *
-     * @return True if at least one link is found and applied.
-     */
-    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);
-    }
-
-    /**
-     * Applies a regex to a Spannable turning the matches into links.
-     *
-     * @param spannable Spannable whose text is to be marked-up with links.
-     * @param pattern Regex pattern to be used for finding links.
-     * @param defaultScheme The default scheme to be prepended to links if the link does not
-     *                      start with one of the <code>schemes</code> given.
-     * @param schemes Array of schemes (eg <code>http://</code>) to check if the link found
-     *                contains a scheme. Passing a null or empty value means prepend defaultScheme
-     *                to all links.
-     * @param matchFilter  The filter that is used to allow the client code additional control
-     *                     over which pattern matches are to be converted into links.
-     * @param transformFilter Filter to allow the client code to update the link found.
-     *
-     * @return True if at least one link is found and applied.
-     */
-    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) {
-            schemes = EMPTY_STRING;
-        }
-
-        schemesCopy = new String[schemes.length + 1];
-        schemesCopy[0] = defaultScheme.toLowerCase(Locale.ROOT);
-        for (int index = 0; index < schemes.length; index++) {
-            String scheme = schemes[index];
-            schemesCopy[index + 1] = (scheme == null) ? "" : scheme.toLowerCase(Locale.ROOT);
-        }
-
-        boolean hasMatches = false;
-        Matcher m = pattern.matcher(spannable);
-
-        while (m.find()) {
-            int start = m.start();
-            int end = m.end();
-            boolean allowed = true;
-
-            if (matchFilter != null) {
-                allowed = matchFilter.acceptMatch(spannable, start, end);
-            }
-
-            if (allowed) {
-                String url = makeUrl(m.group(0), schemesCopy, m, transformFilter);
-
-                applyLink(url, start, end, spannable);
-                hasMatches = true;
-            }
-        }
-
-        return hasMatches;
-    }
-
-    private static void addLinkMovementMethod(@NonNull TextView t) {
-        MovementMethod m = t.getMovementMethod();
-
-        if ((m == null) || !(m instanceof LinkMovementMethod)) {
-            if (t.getLinksClickable()) {
-                t.setMovementMethod(LinkMovementMethod.getInstance());
-            }
-        }
-    }
-
-    private static String makeUrl(@NonNull String url, @NonNull String[] prefixes,
-            Matcher matcher, @Nullable Linkify.TransformFilter filter) {
-        if (filter != null) {
-            url = filter.transformUrl(matcher, url);
-        }
-
-        boolean hasPrefix = false;
-
-        for (int i = 0; i < prefixes.length; i++) {
-            if (url.regionMatches(true, 0, prefixes[i], 0, prefixes[i].length())) {
-                hasPrefix = true;
-
-                // Fix capitalization if necessary
-                if (!url.regionMatches(false, 0, prefixes[i], 0, prefixes[i].length())) {
-                    url = prefixes[i] + url.substring(prefixes[i].length());
-                }
-
-                break;
-            }
-        }
-
-        if (!hasPrefix && prefixes.length > 0) {
-            url = prefixes[0] + url;
-        }
-
-        return url;
-    }
-
-    private static void gatherLinks(ArrayList<LinkSpec> links,
-            Spannable s, Pattern pattern, String[] schemes,
-            Linkify.MatchFilter matchFilter, Linkify.TransformFilter transformFilter) {
-        Matcher m = pattern.matcher(s);
-
-        while (m.find()) {
-            int start = m.start();
-            int end = m.end();
-
-            if (matchFilter == null || matchFilter.acceptMatch(s, start, end)) {
-                LinkSpec spec = new LinkSpec();
-                String url = makeUrl(m.group(0), schemes, m, transformFilter);
-
-                spec.url = url;
-                spec.start = start;
-                spec.end = end;
-
-                links.add(spec);
-            }
-        }
-    }
-
-    private static void applyLink(String url, int start, int end, Spannable text) {
-        URLSpan span = new URLSpan(url);
-
-        text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-    }
-
-    private static final void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {
-        String string = s.toString();
-        String address;
-        int base = 0;
-
-        try {
-            while ((address = WebView.findAddress(string)) != null) {
-                int start = string.indexOf(address);
-
-                if (start < 0) {
-                    break;
-                }
-
-                LinkSpec spec = new LinkSpec();
-                int length = address.length();
-                int end = start + length;
-
-                spec.start = base + start;
-                spec.end = base + end;
-                string = string.substring(end);
-                base += end;
-
-                String encodedAddress = null;
-
-                try {
-                    encodedAddress = URLEncoder.encode(address,"UTF-8");
-                } catch (UnsupportedEncodingException e) {
-                    continue;
-                }
-
-                spec.url = "geo:0,0?q=" + encodedAddress;
-                links.add(spec);
-            }
-        } catch (UnsupportedOperationException e) {
-            // findAddress may fail with an unsupported exception on platforms without a WebView.
-            // In this case, we will not append anything to the links variable: it would have died
-            // in WebView.findAddress.
-            return;
-        }
-    }
-
-    private static final void pruneOverlaps(ArrayList<LinkSpec> links, Spannable text) {
-        // Append spans added by framework
-        URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class);
-        for (int i = 0; i < urlSpans.length; i++) {
-            LinkSpec spec = new LinkSpec();
-            spec.frameworkAddedSpan = urlSpans[i];
-            spec.start = text.getSpanStart(urlSpans[i]);
-            spec.end = text.getSpanEnd(urlSpans[i]);
-            links.add(spec);
-        }
-
-        Collections.sort(links, COMPARATOR);
-
-        int len = links.size();
-        int i = 0;
-
-        while (i < len - 1) {
-            LinkSpec a = links.get(i);
-            LinkSpec b = links.get(i + 1);
-            int remove = -1;
-
-            if ((a.start <= b.start) && (a.end > b.start)) {
-                if (b.end <= a.end) {
-                    remove = i + 1;
-                } else if ((a.end - a.start) > (b.end - b.start)) {
-                    remove = i + 1;
-                } else if ((a.end - a.start) < (b.end - b.start)) {
-                    remove = i;
-                }
-
-                if (remove != -1) {
-                    URLSpan span = links.get(remove).frameworkAddedSpan;
-                    if (span != null) {
-                        text.removeSpan(span);
-                    }
-                    links.remove(remove);
-                    len--;
-                    continue;
-                }
-
-            }
-
-            i++;
-        }
-    }
-
-    /**
-     * Do not create this static utility class.
-     */
-    private LinkifyCompat() {}
-
-    private static class LinkSpec {
-        URLSpan frameworkAddedSpan;
-        String url;
-        int start;
-        int end;
-
-        LinkSpec() {
-        }
-    }
-}
diff --git a/android/support/v4/util/ArrayMap.java b/android/support/v4/util/ArrayMap.java
deleted file mode 100644
index 0a8a0ab..0000000
--- a/android/support/v4/util/ArrayMap.java
+++ /dev/null
@@ -1,205 +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.util;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * ArrayMap is a generic key->value mapping data structure that is
- * designed to be more memory efficient than a traditional {@link java.util.HashMap},
- * this implementation is a version of the platform's
- * {@link android.util.ArrayMap} that can be used on older versions of the platform.
- * It keeps its mappings in an array data structure -- an integer array of hash
- * codes for each item, and an Object array of the key/value pairs.  This allows it to
- * avoid having to create an extra object for every entry put in to the map, and it
- * also tries to control the growth of the size of these arrays more aggressively
- * (since growing them only requires copying the entries in the array, not rebuilding
- * a hash map).
- *
- * <p>If you don't need the standard Java container APIs provided here (iterators etc),
- * consider using {@link SimpleArrayMap} instead.</p>
- *
- * <p>Note that this implementation is not intended to be appropriate for data structures
- * that may contain large numbers of items.  It is generally slower than a traditional
- * HashMap, since lookups require a binary search and adds and removes require inserting
- * and deleting entries in the array.  For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.</p>
- *
- * <p>Because this container is intended to better balance memory use, unlike most other
- * standard Java containers it will shrink its array as items are removed from it.  Currently
- * you have no control over this shrinking -- if you set a capacity and then remove an
- * item, it may reduce the capacity to better match the current size.  In the future an
- * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
- */
-public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> {
-    MapCollections<K, V> mCollections;
-
-    public ArrayMap() {
-        super();
-    }
-
-    /**
-     * Create a new ArrayMap with a given initial capacity.
-     */
-    public ArrayMap(int capacity) {
-        super(capacity);
-    }
-
-    /**
-     * Create a new ArrayMap with the mappings from the given ArrayMap.
-     */
-    public ArrayMap(SimpleArrayMap map) {
-        super(map);
-    }
-
-    private MapCollections<K, V> getCollection() {
-        if (mCollections == null) {
-            mCollections = new MapCollections<K, V>() {
-                @Override
-                protected int colGetSize() {
-                    return mSize;
-                }
-
-                @Override
-                protected Object colGetEntry(int index, int offset) {
-                    return mArray[(index<<1) + offset];
-                }
-
-                @Override
-                protected int colIndexOfKey(Object key) {
-                    return indexOfKey(key);
-                }
-
-                @Override
-                protected int colIndexOfValue(Object value) {
-                    return indexOfValue(value);
-                }
-
-                @Override
-                protected Map<K, V> colGetMap() {
-                    return ArrayMap.this;
-                }
-
-                @Override
-                protected void colPut(K key, V value) {
-                    put(key, value);
-                }
-
-                @Override
-                protected V colSetValue(int index, V value) {
-                    return setValueAt(index, value);
-                }
-
-                @Override
-                protected void colRemoveAt(int index) {
-                    removeAt(index);
-                }
-
-                @Override
-                protected void colClear() {
-                    clear();
-                }
-            };
-        }
-        return mCollections;
-    }
-
-    /**
-     * Determine if the array map contains all of the keys in the given collection.
-     * @param collection The collection whose contents are to be checked against.
-     * @return Returns true if this array map contains a key for every entry
-     * in <var>collection</var>, else returns false.
-     */
-    public boolean containsAll(Collection<?> collection) {
-        return MapCollections.containsAllHelper(this, collection);
-    }
-
-    /**
-     * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>map</var>
-     * @param map The map whose contents are to be retrieved.
-     */
-    @Override
-    public void putAll(Map<? extends K, ? extends V> map) {
-        ensureCapacity(mSize + map.size());
-        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
-            put(entry.getKey(), entry.getValue());
-        }
-    }
-
-    /**
-     * Remove all keys in the array map that exist in the given collection.
-     * @param collection The collection whose contents are to be used to remove keys.
-     * @return Returns true if any keys were removed from the array map, else false.
-     */
-    public boolean removeAll(Collection<?> collection) {
-        return MapCollections.removeAllHelper(this, collection);
-    }
-
-    /**
-     * Remove all keys in the array map that do <b>not</b> exist in the given collection.
-     * @param collection The collection whose contents are to be used to determine which
-     * keys to keep.
-     * @return Returns true if any keys were removed from the array map, else false.
-     */
-    public boolean retainAll(Collection<?> collection) {
-        return MapCollections.retainAllHelper(this, collection);
-    }
-
-    /**
-     * Return a {@link java.util.Set} for iterating over and interacting with all mappings
-     * in the array map.
-     *
-     * <p><b>Note:</b> this is a very inefficient way to access the array contents, it
-     * requires generating a number of temporary objects.</p>
-     *
-     * <p><b>Note:</b></p> the semantics of this
-     * Set are subtly different than that of a {@link java.util.HashMap}: most important,
-     * the {@link java.util.Map.Entry Map.Entry} object returned by its iterator is a single
-     * object that exists for the entire iterator, so you can <b>not</b> hold on to it
-     * after calling {@link java.util.Iterator#next() Iterator.next}.</p>
-     */
-    @Override
-    public Set<Entry<K, V>> entrySet() {
-        return getCollection().getEntrySet();
-    }
-
-    /**
-     * Return a {@link java.util.Set} for iterating over and interacting with all keys
-     * in the array map.
-     *
-     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
-     * requires generating a number of temporary objects.</p>
-     */
-    @Override
-    public Set<K> keySet() {
-        return getCollection().getKeySet();
-    }
-
-    /**
-     * Return a {@link java.util.Collection} for iterating over and interacting with all values
-     * in the array map.
-     *
-     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
-     * requires generating a number of temporary objects.</p>
-     */
-    @Override
-    public Collection<V> values() {
-        return getCollection().getValues();
-    }
-}
diff --git a/android/support/v4/util/ArraySet.java b/android/support/v4/util/ArraySet.java
deleted file mode 100644
index 8444d2c..0000000
--- a/android/support/v4/util/ArraySet.java
+++ /dev/null
@@ -1,788 +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.util;
-
-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.util.Log;
-
-import java.lang.reflect.Array;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * ArraySet is a generic set data structure that is designed to be more memory efficient than a
- * traditional {@link java.util.HashSet}.  The design is very similar to
- * {@link ArrayMap}, with all of the caveats described there.  This implementation is
- * separate from ArrayMap, however, so the Object array contains only one item for each
- * entry in the set (instead of a pair for a mapping).
- *
- * <p>Note that this implementation is not intended to be appropriate for data structures
- * that may contain large numbers of items.  It is generally slower than a traditional
- * HashSet, since lookups require a binary search and adds and removes require inserting
- * and deleting entries in the array.  For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.</p>
- *
- * <p>Because this container is intended to better balance memory use, unlike most other
- * standard Java containers it will shrink its array as items are removed from it.  Currently
- * you have no control over this shrinking -- if you set a capacity and then remove an
- * item, it may reduce the capacity to better match the current size.  In the future an
- * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
- */
-public final class ArraySet<E> implements Collection<E>, Set<E> {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "ArraySet";
-    private static final int[] INT = new int[0];
-    private static final Object[] OBJECT = new Object[0];
-
-    /**
-     * The minimum amount by which the capacity of a ArraySet will increase.
-     * This is tuned to be relatively space-efficient.
-     */
-    private static final int BASE_SIZE = 4;
-
-    /**
-     * Maximum number of entries to have in array caches.
-     */
-    private static final int CACHE_SIZE = 10;
-
-    /**
-     * Caches of small array objects to avoid spamming garbage.  The cache
-     * Object[] variable is a pointer to a linked list of array objects.
-     * The first entry in the array is a pointer to the next array in the
-     * list; the second entry is a pointer to the int[] hash code array for it.
-     */
-    private static Object[] sBaseCache;
-    private static int sBaseCacheSize;
-    private static Object[] sTwiceBaseCache;
-    private static int sTwiceBaseCacheSize;
-
-    private int[] mHashes;
-    private Object[] mArray;
-    private int mSize;
-    private MapCollections<E, E> mCollections;
-
-    private int indexOf(Object key, int hash) {
-        final int N = mSize;
-
-        // Important fast case: if nothing is in here, nothing to look for.
-        if (N == 0) {
-            return ~0;
-        }
-
-        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
-
-        // If the hash code wasn't found, then we have no entry for this key.
-        if (index < 0) {
-            return index;
-        }
-
-        // If the key at the returned index matches, that's what we want.
-        if (key.equals(mArray[index])) {
-            return index;
-        }
-
-        // Search for a matching key after the index.
-        int end;
-        for (end = index + 1; end < N && mHashes[end] == hash; end++) {
-            if (key.equals(mArray[end])) return end;
-        }
-
-        // Search for a matching key before the index.
-        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
-            if (key.equals(mArray[i])) return i;
-        }
-
-        // Key not found -- return negative value indicating where a
-        // new entry for this key should go.  We use the end of the
-        // hash chain to reduce the number of array entries that will
-        // need to be copied when inserting.
-        return ~end;
-    }
-
-    private int indexOfNull() {
-        final int N = mSize;
-
-        // Important fast case: if nothing is in here, nothing to look for.
-        if (N == 0) {
-            return ~0;
-        }
-
-        int index = ContainerHelpers.binarySearch(mHashes, N, 0);
-
-        // If the hash code wasn't found, then we have no entry for this key.
-        if (index < 0) {
-            return index;
-        }
-
-        // If the key at the returned index matches, that's what we want.
-        if (null == mArray[index]) {
-            return index;
-        }
-
-        // Search for a matching key after the index.
-        int end;
-        for (end = index + 1; end < N && mHashes[end] == 0; end++) {
-            if (null == mArray[end]) return end;
-        }
-
-        // Search for a matching key before the index.
-        for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {
-            if (null == mArray[i]) return i;
-        }
-
-        // Key not found -- return negative value indicating where a
-        // new entry for this key should go.  We use the end of the
-        // hash chain to reduce the number of array entries that will
-        // need to be copied when inserting.
-        return ~end;
-    }
-
-    @SuppressWarnings("ArrayToString")
-    private void allocArrays(final int size) {
-        if (size == (BASE_SIZE * 2)) {
-            synchronized (ArraySet.class) {
-                if (sTwiceBaseCache != null) {
-                    final Object[] array = sTwiceBaseCache;
-                    mArray = array;
-                    sTwiceBaseCache = (Object[]) array[0];
-                    mHashes = (int[]) array[1];
-                    array[0] = array[1] = null;
-                    sTwiceBaseCacheSize--;
-                    if (DEBUG) {
-                        Log.d(TAG, "Retrieving 2x cache " + mHashes + " now have "
-                                + sTwiceBaseCacheSize + " entries");
-                    }
-                    return;
-                }
-            }
-        } else if (size == BASE_SIZE) {
-            synchronized (ArraySet.class) {
-                if (sBaseCache != null) {
-                    final Object[] array = sBaseCache;
-                    mArray = array;
-                    sBaseCache = (Object[]) array[0];
-                    mHashes = (int[]) array[1];
-                    array[0] = array[1] = null;
-                    sBaseCacheSize--;
-                    if (DEBUG) {
-                        Log.d(TAG, "Retrieving 1x cache " + mHashes + " now have " + sBaseCacheSize
-                                + " entries");
-                    }
-                    return;
-                }
-            }
-        }
-
-        mHashes = new int[size];
-        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) {
-                if (sTwiceBaseCacheSize < CACHE_SIZE) {
-                    array[0] = sTwiceBaseCache;
-                    array[1] = hashes;
-                    for (int i = size - 1; i >= 2; i--) {
-                        array[i] = null;
-                    }
-                    sTwiceBaseCache = array;
-                    sTwiceBaseCacheSize++;
-                    if (DEBUG) {
-                        Log.d(TAG, "Storing 2x cache " + array + " now have " + sTwiceBaseCacheSize
-                                + " entries");
-                    }
-                }
-            }
-        } else if (hashes.length == BASE_SIZE) {
-            synchronized (ArraySet.class) {
-                if (sBaseCacheSize < CACHE_SIZE) {
-                    array[0] = sBaseCache;
-                    array[1] = hashes;
-                    for (int i = size - 1; i >= 2; i--) {
-                        array[i] = null;
-                    }
-                    sBaseCache = array;
-                    sBaseCacheSize++;
-                    if (DEBUG) {
-                        Log.d(TAG, "Storing 1x cache " + array + " now have "
-                                + sBaseCacheSize + " entries");
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Create a new empty ArraySet.  The default capacity of an array map is 0, and
-     * will grow once items are added to it.
-     */
-    public ArraySet() {
-        this(0);
-    }
-
-    /**
-     * Create a new ArraySet with a given initial capacity.
-     */
-    public ArraySet(int capacity) {
-        if (capacity == 0) {
-            mHashes = INT;
-            mArray = OBJECT;
-        } else {
-            allocArrays(capacity);
-        }
-        mSize = 0;
-    }
-
-    /**
-     * Create a new ArraySet with the mappings from the given ArraySet.
-     */
-    public ArraySet(@Nullable ArraySet<E> set) {
-        this();
-        if (set != null) {
-            addAll(set);
-        }
-    }
-
-    /**
-     * Create a new ArraySet with the mappings from the given {@link Collection}.
-     */
-    public ArraySet(@Nullable Collection<E> set) {
-        this();
-        if (set != null) {
-            addAll(set);
-        }
-    }
-
-    /**
-     * Make the array map empty.  All storage is released.
-     */
-    @Override
-    public void clear() {
-        if (mSize != 0) {
-            freeArrays(mHashes, mArray, mSize);
-            mHashes = INT;
-            mArray = OBJECT;
-            mSize = 0;
-        }
-    }
-
-    /**
-     * Ensure the array map can hold at least <var>minimumCapacity</var>
-     * items.
-     */
-    public void ensureCapacity(int minimumCapacity) {
-        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);
-            }
-            freeArrays(ohashes, oarray, mSize);
-        }
-    }
-
-    /**
-     * Check whether a value exists in the set.
-     *
-     * @param key The value to search for.
-     * @return Returns true if the value exists, else false.
-     */
-    @Override
-    public boolean contains(Object key) {
-        return indexOf(key) >= 0;
-    }
-
-    /**
-     * Returns the index of a value in the set.
-     *
-     * @param key The value to search for.
-     * @return Returns the index of the value if it exists, else a negative integer.
-     */
-    public int indexOf(Object key) {
-        return key == null ? indexOfNull() : indexOf(key, key.hashCode());
-    }
-
-    /**
-     * Return the value at the given index in the array.
-     * @param index The desired index, must be between 0 and {@link #size()}-1.
-     * @return Returns the value stored at the given index.
-     */
-    @Nullable
-    public E valueAt(int index) {
-        return (E) mArray[index];
-    }
-
-    /**
-     * Return true if the array map contains no items.
-     */
-    @Override
-    public boolean isEmpty() {
-        return mSize <= 0;
-    }
-
-    /**
-     * Adds the specified object to this set. The set is not modified if it
-     * already contains the object.
-     *
-     * @param value the object to add.
-     * @return {@code true} if this set is modified, {@code false} otherwise.
-     * @throws ClassCastException
-     *             when the class of the object is inappropriate for this set.
-     */
-    @Override
-    public boolean add(@Nullable E value) {
-        final int hash;
-        int index;
-        if (value == null) {
-            hash = 0;
-            index = indexOfNull();
-        } else {
-            hash = value.hashCode();
-            index = indexOf(value, hash);
-        }
-        if (index >= 0) {
-            return false;
-        }
-
-        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 (DEBUG) Log.d(TAG, "add: grow from " + mHashes.length + " to " + n);
-
-            final int[] ohashes = mHashes;
-            final Object[] oarray = mArray;
-            allocArrays(n);
-
-            if (mHashes.length > 0) {
-                if (DEBUG) Log.d(TAG, "add: copy 0-" + mSize + " to 0");
-                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
-                System.arraycopy(oarray, 0, mArray, 0, oarray.length);
-            }
-
-            freeArrays(ohashes, oarray, mSize);
-        }
-
-        if (index < mSize) {
-            if (DEBUG) {
-                Log.d(TAG, "add: move " + index + "-" + (mSize - index) + " to " + (index + 1));
-            }
-            System.arraycopy(mHashes, index, mHashes, index + 1, mSize - index);
-            System.arraycopy(mArray, index, mArray, index + 1, mSize - index);
-        }
-
-        mHashes[index] = hash;
-        mArray[index] = value;
-        mSize++;
-        return true;
-    }
-
-    /**
-     * Special fast path for appending items to the end of the array without validation.
-     * 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 : value.hashCode();
-        if (index >= mHashes.length) {
-            throw new IllegalStateException("Array is full");
-        }
-        if (index > 0 && mHashes[index - 1] > hash) {
-            // Cannot optimize since it would break the sorted order - fallback to add()
-            if (DEBUG) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Log.w(TAG, "New hash " + hash
-                        + " is before end of array hash " + mHashes[index - 1]
-                        + " at index " + index, e);
-            }
-            add(value);
-            return;
-        }
-        mSize = index + 1;
-        mHashes[index] = hash;
-        mArray[index] = value;
-    }
-
-    /**
-     * Perform a {@link #add(Object)} of all values in <var>array</var>
-     * @param array The array whose contents are to be retrieved.
-     */
-    public void addAll(@NonNull ArraySet<? extends E> array) {
-        final int N = array.mSize;
-        ensureCapacity(mSize + N);
-        if (mSize == 0) {
-            if (N > 0) {
-                System.arraycopy(array.mHashes, 0, mHashes, 0, N);
-                System.arraycopy(array.mArray, 0, mArray, 0, N);
-                mSize = N;
-            }
-        } else {
-            for (int i = 0; i < N; i++) {
-                add(array.valueAt(i));
-            }
-        }
-    }
-
-    /**
-     * Removes the specified object from this set.
-     *
-     * @param object the object to remove.
-     * @return {@code true} if this set was modified, {@code false} otherwise.
-     */
-    @Override
-    public boolean remove(Object object) {
-        final int index = indexOf(object);
-        if (index >= 0) {
-            removeAt(index);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Remove the key/value mapping at the given index.
-     * @param index The desired index, must be between 0 and {@link #size()}-1.
-     * @return Returns the value that was stored at this index.
-     */
-    public E removeAt(int index) {
-        final Object old = mArray[index];
-        if (mSize <= 1) {
-            // Now empty.
-            if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
-            freeArrays(mHashes, mArray, mSize);
-            mHashes = INT;
-            mArray = OBJECT;
-            mSize = 0;
-        } else {
-            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);
-
-                if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
-
-                final int[] ohashes = mHashes;
-                final Object[] oarray = mArray;
-                allocArrays(n);
-
-                mSize--;
-                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);
-                }
-                if (index < mSize) {
-                    if (DEBUG) {
-                        Log.d(TAG, "remove: copy from " + (index + 1) + "-" + mSize
-                                + " to " + index);
-                    }
-                    System.arraycopy(ohashes, index + 1, mHashes, index, mSize - index);
-                    System.arraycopy(oarray, index + 1, mArray, index, mSize - index);
-                }
-            } else {
-                mSize--;
-                if (index < mSize) {
-                    if (DEBUG) {
-                        Log.d(TAG, "remove: move " + (index + 1) + "-" + mSize + " to " + index);
-                    }
-                    System.arraycopy(mHashes, index + 1, mHashes, index, mSize - index);
-                    System.arraycopy(mArray, index + 1, mArray, index, mSize - index);
-                }
-                mArray[mSize] = null;
-            }
-        }
-        return (E) old;
-    }
-
-    /**
-     * Perform a {@link #remove(Object)} of all values in <var>array</var>
-     * @param array The array whose contents are to be removed.
-     */
-    public boolean removeAll(ArraySet<? extends E> array) {
-        // TODO: If array is sufficiently large, a marking approach might be beneficial. In a first
-        //       pass, use the property that the sets are sorted by hash to make this linear passes
-        //       (except for hash collisions, which means worst case still n*m), then do one
-        //       collection pass into a new array. This avoids binary searches and excessive memcpy.
-        final int N = array.mSize;
-
-        // Note: ArraySet does not make thread-safety guarantees. So instead of OR-ing together all
-        //       the single results, compare size before and after.
-        final int originalSize = mSize;
-        for (int i = 0; i < N; i++) {
-            remove(array.valueAt(i));
-        }
-        return originalSize != mSize;
-    }
-
-    /**
-     * Return the number of items in this array map.
-     */
-    @Override
-    public int size() {
-        return mSize;
-    }
-
-    @NonNull
-    @Override
-    public Object[] toArray() {
-        Object[] result = new Object[mSize];
-        System.arraycopy(mArray, 0, result, 0, mSize);
-        return result;
-    }
-
-    @NonNull
-    @Override
-    public <T> T[] toArray(@NonNull T[] array) {
-        if (array.length < mSize) {
-            @SuppressWarnings("unchecked") T[] newArray =
-                    (T[]) Array.newInstance(array.getClass().getComponentType(), mSize);
-            array = newArray;
-        }
-        System.arraycopy(mArray, 0, array, 0, mSize);
-        if (array.length > mSize) {
-            array[mSize] = null;
-        }
-        return array;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation returns false if the object is not a set, or
-     * if the sets have different sizes.  Otherwise, for each value in this
-     * set, it checks to make sure the value also exists in the other set.
-     * If any value doesn't exist, the method returns false; otherwise, it
-     * returns true.
-     */
-    @Override
-    public boolean equals(Object object) {
-        if (this == object) {
-            return true;
-        }
-        if (object instanceof Set) {
-            Set<?> set = (Set<?>) object;
-            if (size() != set.size()) {
-                return false;
-            }
-
-            try {
-                for (int i = 0; i < mSize; i++) {
-                    E mine = valueAt(i);
-                    if (!set.contains(mine)) {
-                        return false;
-                    }
-                }
-            } catch (NullPointerException ignored) {
-                return false;
-            } catch (ClassCastException ignored) {
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        final int[] hashes = mHashes;
-        int result = 0;
-        for (int i = 0, s = mSize; i < s; i++) {
-            result += hashes[i];
-        }
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation composes a string by iterating over its values. If
-     * this set contains itself as a value, the string "(this Set)"
-     * will appear in its place.
-     */
-    @Override
-    public String toString() {
-        if (isEmpty()) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 14);
-        buffer.append('{');
-        for (int i = 0; i < mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            Object value = valueAt(i);
-            if (value != this) {
-                buffer.append(value);
-            } else {
-                buffer.append("(this Set)");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-    // ------------------------------------------------------------------------
-    // Interop with traditional Java containers.  Not as efficient as using
-    // specialized collection APIs.
-    // ------------------------------------------------------------------------
-
-    private MapCollections<E, E> getCollection() {
-        if (mCollections == null) {
-            mCollections = new MapCollections<E, E>() {
-                @Override
-                protected int colGetSize() {
-                    return mSize;
-                }
-
-                @Override
-                protected Object colGetEntry(int index, int offset) {
-                    return mArray[index];
-                }
-
-                @Override
-                protected int colIndexOfKey(Object key) {
-                    return indexOf(key);
-                }
-
-                @Override
-                protected int colIndexOfValue(Object value) {
-                    return indexOf(value);
-                }
-
-                @Override
-                protected Map<E, E> colGetMap() {
-                    throw new UnsupportedOperationException("not a map");
-                }
-
-                @Override
-                protected void colPut(E key, E value) {
-                    add(key);
-                }
-
-                @Override
-                protected E colSetValue(int index, E value) {
-                    throw new UnsupportedOperationException("not a map");
-                }
-
-                @Override
-                protected void colRemoveAt(int index) {
-                    removeAt(index);
-                }
-
-                @Override
-                protected void colClear() {
-                    clear();
-                }
-            };
-        }
-        return mCollections;
-    }
-
-    /**
-     * Return an {@link java.util.Iterator} over all values in the set.
-     *
-     * <p><b>Note:</b> this is a fairly inefficient way to access the array contents, it
-     * requires generating a number of temporary objects and allocates additional state
-     * information associated with the container that will remain for the life of the container.</p>
-     */
-    @Override
-    public Iterator<E> iterator() {
-        return getCollection().getKeySet().iterator();
-    }
-
-    /**
-     * Determine if the array set contains all of the values in the given collection.
-     * @param collection The collection whose contents are to be checked against.
-     * @return Returns true if this array set contains a value for every entry
-     * in <var>collection</var>, else returns false.
-     */
-    @Override
-    public boolean containsAll(@NonNull Collection<?> collection) {
-        Iterator<?> it = collection.iterator();
-        while (it.hasNext()) {
-            if (!contains(it.next())) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Perform an {@link #add(Object)} of all values in <var>collection</var>
-     * @param collection The collection whose contents are to be retrieved.
-     */
-    @Override
-    public boolean addAll(@NonNull Collection<? extends E> collection) {
-        ensureCapacity(mSize + collection.size());
-        boolean added = false;
-        for (E value : collection) {
-            added |= add(value);
-        }
-        return added;
-    }
-
-    /**
-     * Remove all values in the array set that exist in the given collection.
-     * @param collection The collection whose contents are to be used to remove values.
-     * @return Returns true if any values were removed from the array set, else false.
-     */
-    @Override
-    public boolean removeAll(@NonNull Collection<?> collection) {
-        boolean removed = false;
-        for (Object value : collection) {
-            removed |= remove(value);
-        }
-        return removed;
-    }
-
-    /**
-     * Remove all values in the array set that do <b>not</b> exist in the given collection.
-     * @param collection The collection whose contents are to be used to determine which
-     * values to keep.
-     * @return Returns true if any values were removed from the array set, else false.
-     */
-    @Override
-    public boolean retainAll(@NonNull Collection<?> collection) {
-        boolean removed = false;
-        for (int i = mSize - 1; i >= 0; i--) {
-            if (!collection.contains(mArray[i])) {
-                removeAt(i);
-                removed = true;
-            }
-        }
-        return removed;
-    }
-}
diff --git a/android/support/v4/util/AtomicFile.java b/android/support/v4/util/AtomicFile.java
deleted file mode 100644
index aefe705..0000000
--- a/android/support/v4/util/AtomicFile.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Static library support version of the framework's {@link android.util.AtomicFile},
- * a helper class for performing atomic operations on a file by creating a
- * backup file until a write has successfully completed.
- * <p>
- * Atomic file guarantees file integrity by ensuring that a file has
- * been completely written and sync'd to disk before removing its backup.
- * As long as the backup file exists, the original file is considered
- * to be invalid (left over from a previous attempt to write the file).
- * </p><p>
- * Atomic file does not confer any file locking semantics.
- * Do not use this class when the file may be accessed or modified concurrently
- * by multiple threads or processes.  The caller is responsible for ensuring
- * appropriate mutual exclusion invariants whenever it accesses the file.
- * </p>
- */
-public class AtomicFile {
-    private final File mBaseName;
-    private final File mBackupName;
-
-    /**
-     * Create a new AtomicFile for a file located at the given File path.
-     * The secondary backup file will be the same file path with ".bak" appended.
-     */
-    public AtomicFile(@NonNull File baseName) {
-        mBaseName = baseName;
-        mBackupName = new File(baseName.getPath() + ".bak");
-    }
-
-    /**
-     * Return the path to the base file.  You should not generally use this,
-     * as the data at that path may not be valid.
-     */
-    @NonNull
-    public File getBaseFile() {
-        return mBaseName;
-    }
-
-    /**
-     * Delete the atomic file.  This deletes both the base and backup files.
-     */
-    public void delete() {
-        mBaseName.delete();
-        mBackupName.delete();
-    }
-
-    /**
-     * Start a new write operation on the file.  This returns a FileOutputStream
-     * to which you can write the new file data.  The existing file is replaced
-     * with the new data.  You <em>must not</em> directly close the given
-     * FileOutputStream; instead call either {@link #finishWrite(FileOutputStream)}
-     * or {@link #failWrite(FileOutputStream)}.
-     *
-     * <p>Note that if another thread is currently performing
-     * a write, this will simply replace whatever that thread is writing
-     * with the new file being written by this thread, and when the other
-     * thread finishes the write the new write operation will no longer be
-     * safe (or will be lost).  You must do your own threading protection for
-     * access to AtomicFile.
-     */
-    @NonNull
-    public FileOutputStream startWrite() throws IOException {
-        // Rename the current file so it may be used as a backup during the next read
-        if (mBaseName.exists()) {
-            if (!mBackupName.exists()) {
-                if (!mBaseName.renameTo(mBackupName)) {
-                    Log.w("AtomicFile", "Couldn't rename file " + mBaseName
-                            + " to backup file " + mBackupName);
-                }
-            } else {
-                mBaseName.delete();
-            }
-        }
-        FileOutputStream str;
-        try {
-            str = new FileOutputStream(mBaseName);
-        } catch (FileNotFoundException e) {
-            File parent = mBaseName.getParentFile();
-            if (!parent.mkdirs()) {
-                throw new IOException("Couldn't create directory " + mBaseName);
-            }
-            try {
-                str = new FileOutputStream(mBaseName);
-            } catch (FileNotFoundException e2) {
-                throw new IOException("Couldn't create " + mBaseName);
-            }
-        }
-        return str;
-    }
-
-    /**
-     * Call when you have successfully finished writing to the stream
-     * returned by {@link #startWrite()}.  This will close, sync, and
-     * commit the new data.  The next attempt to read the atomic file
-     * will return the new file stream.
-     */
-    public void finishWrite(@Nullable FileOutputStream str) {
-        if (str != null) {
-            sync(str);
-            try {
-                str.close();
-                mBackupName.delete();
-            } catch (IOException e) {
-                Log.w("AtomicFile", "finishWrite: Got exception:", e);
-            }
-        }
-    }
-
-    /**
-     * Call when you have failed for some reason at writing to the stream
-     * returned by {@link #startWrite()}.  This will close the current
-     * write stream, and roll back to the previous state of the file.
-     */
-    public void failWrite(@Nullable FileOutputStream str) {
-        if (str != null) {
-            sync(str);
-            try {
-                str.close();
-                mBaseName.delete();
-                mBackupName.renameTo(mBaseName);
-            } catch (IOException e) {
-                Log.w("AtomicFile", "failWrite: Got exception:", e);
-            }
-        }
-    }
-
-    /**
-     * Open the atomic file for reading.  If there previously was an
-     * incomplete write, this will roll back to the last good data before
-     * opening for read.  You should call close() on the FileInputStream when
-     * you are done reading from it.
-     *
-     * <p>Note that if another thread is currently performing
-     * a write, this will incorrectly consider it to be in the state of a bad
-     * write and roll back, causing the new data currently being written to
-     * be dropped.  You must do your own threading protection for access to
-     * AtomicFile.
-     */
-    @NonNull
-    public FileInputStream openRead() throws FileNotFoundException {
-        if (mBackupName.exists()) {
-            mBaseName.delete();
-            mBackupName.renameTo(mBaseName);
-        }
-        return new FileInputStream(mBaseName);
-    }
-
-    /**
-     * A convenience for {@link #openRead()} that also reads all of the
-     * file contents into a byte array which is returned.
-     */
-    @NonNull
-    public byte[] readFully() throws IOException {
-        FileInputStream stream = openRead();
-        try {
-            int pos = 0;
-            int avail = stream.available();
-            byte[] data = new byte[avail];
-            while (true) {
-                int amt = stream.read(data, pos, data.length-pos);
-                //Log.i("foo", "Read " + amt + " bytes at " + pos
-                //        + " of avail " + data.length);
-                if (amt <= 0) {
-                    //Log.i("foo", "**** FINISHED READING: pos=" + pos
-                    //        + " len=" + data.length);
-                    return data;
-                }
-                pos += amt;
-                avail = stream.available();
-                if (avail > data.length-pos) {
-                    byte[] newData = new byte[pos+avail];
-                    System.arraycopy(data, 0, newData, 0, pos);
-                    data = newData;
-                }
-            }
-        } finally {
-            stream.close();
-        }
-    }
-
-    private static boolean sync(@NonNull FileOutputStream stream) {
-        try {
-            stream.getFD().sync();
-            return true;
-        } catch (IOException e) {
-        }
-        return false;
-    }
-}
diff --git a/android/support/v4/util/CircularArray.java b/android/support/v4/util/CircularArray.java
deleted file mode 100644
index 9533136..0000000
--- a/android/support/v4/util/CircularArray.java
+++ /dev/null
@@ -1,260 +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.util;
-
-/**
- * CircularArray is a generic circular array data structure that provides O(1) random read, O(1)
- * prepend and O(1) append. The CircularArray automatically grows its capacity when number of added
- * items is over its capacity.
- */
-public final class CircularArray<E> {
-    private E[] mElements;
-    private int mHead;
-    private int mTail;
-    private int mCapacityBitmask;
-
-    private void doubleCapacity() {
-        int n = mElements.length;
-        int r = n - mHead;
-        int newCapacity = n << 1;
-        if (newCapacity < 0) {
-            throw new RuntimeException("Max array capacity exceeded");
-        }
-        Object[] a = new Object[newCapacity];
-        System.arraycopy(mElements, mHead, a, 0, r);
-        System.arraycopy(mElements, 0, a, r, mHead);
-        mElements = (E[]) a;
-        mHead = 0;
-        mTail = n;
-        mCapacityBitmask = newCapacity - 1;
-    }
-
-    /**
-     * Creates a circular array with default capacity.
-     */
-    public CircularArray() {
-        this(8);
-    }
-
-    /**
-     * Creates a circular array with capacity for at least {@code minCapacity}
-     * elements.
-     *
-     * @param minCapacity the minimum capacity, between 1 and 2^30 inclusive
-     */
-    public CircularArray(int minCapacity) {
-        if (minCapacity < 1) {
-            throw new IllegalArgumentException("capacity must be >= 1");
-        }
-        if (minCapacity > (2 << 29)) {
-            throw new IllegalArgumentException("capacity must be <= 2^30");
-        }
-
-        // If minCapacity isn't a power of 2, round up to the next highest
-        // power of 2.
-        final int arrayCapacity;
-        if (Integer.bitCount(minCapacity) != 1) {
-            arrayCapacity = Integer.highestOneBit(minCapacity - 1) << 1;
-        } else {
-            arrayCapacity = minCapacity;
-        }
-
-        mCapacityBitmask = arrayCapacity - 1;
-        mElements = (E[]) new Object[arrayCapacity];
-    }
-
-    /**
-     * Add an element in front of the CircularArray.
-     * @param e  Element to add.
-     */
-    public void addFirst(E e) {
-        mHead = (mHead - 1) & mCapacityBitmask;
-        mElements[mHead] = e;
-        if (mHead == mTail) {
-            doubleCapacity();
-        }
-    }
-
-    /**
-     * Add an element at end of the CircularArray.
-     * @param e  Element to add.
-     */
-    public void addLast(E e) {
-        mElements[mTail] = e;
-        mTail = (mTail + 1) & mCapacityBitmask;
-        if (mTail == mHead) {
-            doubleCapacity();
-        }
-    }
-
-    /**
-     * Remove first element from front of the CircularArray and return it.
-     * @return  The element removed.
-     * @throws ArrayIndexOutOfBoundsException if CircularArray is empty.
-     */
-    public E popFirst() {
-        if (mHead == mTail) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        E result = mElements[mHead];
-        mElements[mHead] = null;
-        mHead = (mHead + 1) & mCapacityBitmask;
-        return result;
-    }
-
-    /**
-     * Remove last element from end of the CircularArray and return it.
-     * @return  The element removed.
-     * @throws ArrayIndexOutOfBoundsException if CircularArray is empty.
-     */
-    public E popLast() {
-        if (mHead == mTail) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        int t = (mTail - 1) & mCapacityBitmask;
-        E result = mElements[t];
-        mElements[t] = null;
-        mTail = t;
-        return result;
-    }
-
-    /**
-     * Remove all elements from the CircularArray.
-     */
-    public void clear() {
-        removeFromStart(size());
-    }
-
-    /**
-     * Remove multiple elements from front of the CircularArray, ignore when numOfElements
-     * is less than or equals to 0.
-     * @param numOfElements  Number of elements to remove.
-     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
-     *         {@link #size()}
-     */
-    public void removeFromStart(int numOfElements) {
-        if (numOfElements <= 0) {
-            return;
-        }
-        if (numOfElements > size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        int end = mElements.length;
-        if (numOfElements < end - mHead) {
-            end = mHead + numOfElements;
-        }
-        for (int i = mHead; i < end; i++) {
-            mElements[i] = null;
-        }
-        int removed = (end - mHead);
-        numOfElements -= removed;
-        mHead = (mHead + removed) & mCapacityBitmask;
-        if (numOfElements > 0) {
-            // mHead wrapped to 0
-            for (int i = 0; i < numOfElements; i++) {
-                mElements[i] = null;
-            }
-            mHead = numOfElements;
-        }
-    }
-
-    /**
-     * Remove multiple elements from end of the CircularArray, ignore when numOfElements
-     * is less than or equals to 0.
-     * @param numOfElements  Number of elements to remove.
-     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
-     *         {@link #size()}
-     */
-    public void removeFromEnd(int numOfElements) {
-        if (numOfElements <= 0) {
-            return;
-        }
-        if (numOfElements > size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        int start = 0;
-        if (numOfElements < mTail) {
-            start = mTail - numOfElements;
-        }
-        for (int i = start; i < mTail; i++) {
-            mElements[i] = null;
-        }
-        int removed = (mTail - start);
-        numOfElements -= removed;
-        mTail = mTail - removed;
-        if (numOfElements > 0) {
-            // mTail wrapped to mElements.length
-            mTail = mElements.length;
-            int newTail = mTail - numOfElements;
-            for (int i = newTail; i < mTail; i++) {
-                mElements[i] = null;
-            }
-            mTail = newTail;
-        }
-    }
-
-    /**
-     * Get first element of the CircularArray.
-     * @return The first element.
-     * @throws {@link ArrayIndexOutOfBoundsException} if CircularArray is empty.
-     */
-    public E getFirst() {
-        if (mHead == mTail) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        return mElements[mHead];
-    }
-
-    /**
-     * Get last element of the CircularArray.
-     * @return The last element.
-     * @throws {@link ArrayIndexOutOfBoundsException} if CircularArray is empty.
-     */
-    public E getLast() {
-        if (mHead == mTail) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        return mElements[(mTail - 1) & mCapacityBitmask];
-    }
-
-    /**
-     * Get nth (0 <= n <= size()-1) element of the CircularArray.
-     * @param n  The zero based element index in the CircularArray.
-     * @return The nth element.
-     * @throws {@link ArrayIndexOutOfBoundsException} if n < 0 or n >= size().
-     */
-    public E get(int n) {
-        if (n < 0 || n >= size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        return mElements[(mHead + n) & mCapacityBitmask];
-    }
-
-    /**
-     * Get number of elements in the CircularArray.
-     * @return Number of elements in the CircularArray.
-     */
-    public int size() {
-        return (mTail - mHead) & mCapacityBitmask;
-    }
-
-    /**
-     * Return true if size() is 0.
-     * @return true if size() is 0.
-     */
-    public boolean isEmpty() {
-        return mHead == mTail;
-    }
-
-}
diff --git a/android/support/v4/util/CircularIntArray.java b/android/support/v4/util/CircularIntArray.java
deleted file mode 100644
index b82107f..0000000
--- a/android/support/v4/util/CircularIntArray.java
+++ /dev/null
@@ -1,215 +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.util;
-
-/**
- * CircularIntArray is a circular integer array data structure that provides O(1) random read, O(1)
- * prepend and O(1) append. The CircularIntArray automatically grows its capacity when number of
- * added integers is over its capacity.
- */
-public final class CircularIntArray
-{
-    private int[] mElements;
-    private int mHead;
-    private int mTail;
-    private int mCapacityBitmask;
-
-    private void doubleCapacity() {
-        int n = mElements.length;
-        int r = n - mHead;
-        int newCapacity = n << 1;
-        if (newCapacity < 0) {
-            throw new RuntimeException("Max array capacity exceeded");
-        }
-        int[] a = new int[newCapacity];
-        System.arraycopy(mElements, mHead, a, 0, r);
-        System.arraycopy(mElements, 0, a, r, mHead);
-        mElements = a;
-        mHead = 0;
-        mTail = n;
-        mCapacityBitmask = newCapacity - 1;
-    }
-
-    /**
-     * Creates a circular array with default capacity.
-     */
-    public CircularIntArray() {
-        this(8);
-    }
-
-    /**
-     * Creates a circular array with capacity for at least {@code minCapacity}
-     * elements.
-     *
-     * @param minCapacity the minimum capacity, between 1 and 2^30 inclusive
-     */
-    public CircularIntArray(int minCapacity) {
-        if (minCapacity < 1) {
-            throw new IllegalArgumentException("capacity must be >= 1");
-        }
-        if (minCapacity > (2 << 29)) {
-            throw new IllegalArgumentException("capacity must be <= 2^30");
-        }
-
-        // If minCapacity isn't a power of 2, round up to the next highest
-        // power of 2.
-        final int arrayCapacity;
-        if (Integer.bitCount(minCapacity) != 1) {
-            arrayCapacity = Integer.highestOneBit(minCapacity - 1) << 1;
-        } else {
-            arrayCapacity = minCapacity;
-        }
-
-        mCapacityBitmask = arrayCapacity - 1;
-        mElements = new int[arrayCapacity];
-    }
-
-    /**
-     * Add an integer in front of the CircularIntArray.
-     * @param e  Integer to add.
-     */
-    public void addFirst(int e) {
-        mHead = (mHead - 1) & mCapacityBitmask;
-        mElements[mHead] = e;
-        if (mHead == mTail) {
-            doubleCapacity();
-        }
-    }
-
-    /**
-     * Add an integer at end of the CircularIntArray.
-     * @param e  Integer to add.
-     */
-    public void addLast(int e) {
-        mElements[mTail] = e;
-        mTail = (mTail + 1) & mCapacityBitmask;
-        if (mTail == mHead) {
-            doubleCapacity();
-        }
-    }
-
-    /**
-     * Remove first integer from front of the CircularIntArray and return it.
-     * @return  The integer removed.
-     * @throws ArrayIndexOutOfBoundsException if CircularIntArray is empty.
-     */
-    public int popFirst() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
-        int result = mElements[mHead];
-        mHead = (mHead + 1) & mCapacityBitmask;
-        return result;
-    }
-
-    /**
-     * Remove last integer from end of the CircularIntArray and return it.
-     * @return  The integer removed.
-     * @throws ArrayIndexOutOfBoundsException if CircularIntArray is empty.
-     */
-    public int popLast() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
-        int t = (mTail - 1) & mCapacityBitmask;
-        int result = mElements[t];
-        mTail = t;
-        return result;
-    }
-
-    /**
-     * Remove all integers from the CircularIntArray.
-     */
-    public void clear() {
-        mTail = mHead;
-    }
-
-    /**
-     * Remove multiple integers from front of the CircularIntArray, ignore when numOfElements
-     * is less than or equals to 0.
-     * @param numOfElements  Number of integers to remove.
-     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
-     *         {@link #size()}
-     */
-    public void removeFromStart(int numOfElements) {
-        if (numOfElements <= 0) {
-            return;
-        }
-        if (numOfElements > size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mHead = (mHead + numOfElements) & mCapacityBitmask;
-    }
-
-    /**
-     * Remove multiple elements from end of the CircularIntArray, ignore when numOfElements
-     * is less than or equals to 0.
-     * @param numOfElements  Number of integers to remove.
-     * @throws ArrayIndexOutOfBoundsException if numOfElements is larger than
-     *         {@link #size()}
-     */
-    public void removeFromEnd(int numOfElements) {
-        if (numOfElements <= 0) {
-            return;
-        }
-        if (numOfElements > size()) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mTail = (mTail - numOfElements) & mCapacityBitmask;
-    }
-
-    /**
-     * Get first integer of the CircularIntArray.
-     * @return The first integer.
-     * @throws {@link ArrayIndexOutOfBoundsException} if CircularIntArray is empty.
-     */
-    public int getFirst() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
-        return mElements[mHead];
-    }
-
-    /**
-     * Get last integer of the CircularIntArray.
-     * @return The last integer.
-     * @throws {@link ArrayIndexOutOfBoundsException} if CircularIntArray is empty.
-     */
-    public int getLast() {
-        if (mHead == mTail) throw new ArrayIndexOutOfBoundsException();
-        return mElements[(mTail - 1) & mCapacityBitmask];
-    }
-
-    /**
-     * Get nth (0 <= n <= size()-1) integer of the CircularIntArray.
-     * @param n  The zero based element index in the CircularIntArray.
-     * @return The nth integer.
-     * @throws {@link ArrayIndexOutOfBoundsException} if n < 0 or n >= size().
-     */
-    public int get(int n) {
-        if (n < 0 || n >= size()) throw new ArrayIndexOutOfBoundsException();
-        return mElements[(mHead + n) & mCapacityBitmask];
-    }
-
-    /**
-     * Get number of integers in the CircularIntArray.
-     * @return Number of integers in the CircularIntArray.
-     */
-    public int size() {
-        return (mTail - mHead) & mCapacityBitmask;
-    }
-
-    /**
-     * Return true if size() is 0.
-     * @return true if size() is 0.
-     */
-    public boolean isEmpty() {
-        return mHead == mTail;
-    }
-
-}
diff --git a/android/support/v4/util/ContainerHelpers.java b/android/support/v4/util/ContainerHelpers.java
deleted file mode 100644
index 74f39d3..0000000
--- a/android/support/v4/util/ContainerHelpers.java
+++ /dev/null
@@ -1,82 +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.util;
-
-class ContainerHelpers {
-    static final int[] EMPTY_INTS = new int[0];
-    static final long[] EMPTY_LONGS = new long[0];
-    static final Object[] EMPTY_OBJECTS = new Object[0];
-
-    public static int idealIntArraySize(int need) {
-        return idealByteArraySize(need * 4) / 4;
-    }
-
-    public static int idealLongArraySize(int need) {
-        return idealByteArraySize(need * 8) / 8;
-    }
-
-    public static int idealByteArraySize(int need) {
-        for (int i = 4; i < 32; i++)
-            if (need <= (1 << i) - 12)
-                return (1 << i) - 12;
-
-        return need;
-    }
-
-    public static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
-    // This is Arrays.binarySearch(), but doesn't do any argument validation.
-    static int binarySearch(int[] array, int size, int value) {
-        int lo = 0;
-        int hi = size - 1;
-
-        while (lo <= hi) {
-            int mid = (lo + hi) >>> 1;
-            int midVal = array[mid];
-
-            if (midVal < value) {
-                lo = mid + 1;
-            } else if (midVal > value) {
-                hi = mid - 1;
-            } else {
-                return mid;  // value found
-            }
-        }
-        return ~lo;  // value not present
-    }
-
-    static int binarySearch(long[] array, int size, long value) {
-        int lo = 0;
-        int hi = size - 1;
-
-        while (lo <= hi) {
-            final int mid = (lo + hi) >>> 1;
-            final long midVal = array[mid];
-
-            if (midVal < value) {
-                lo = mid + 1;
-            } else if (midVal > value) {
-                hi = mid - 1;
-            } else {
-                return mid;  // value found
-            }
-        }
-        return ~lo;  // value not present
-    }
-}
diff --git a/android/support/v4/util/DebugUtils.java b/android/support/v4/util/DebugUtils.java
deleted file mode 100644
index 7e35afa..0000000
--- a/android/support/v4/util/DebugUtils.java
+++ /dev/null
@@ -1,48 +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.util;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Helper for accessing features in {@link android.util.DebugUtils}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class DebugUtils {
-
-    public static void buildShortClassTag(Object cls, StringBuilder out) {
-        if (cls == null) {
-            out.append("null");
-        } else {
-            String simpleName = cls.getClass().getSimpleName();
-            if (simpleName == null || simpleName.length() <= 0) {
-                simpleName = cls.getClass().getName();
-                int end = simpleName.lastIndexOf('.');
-                if (end > 0) {
-                    simpleName = simpleName.substring(end+1);
-                }
-            }
-            out.append(simpleName);
-            out.append('{');
-            out.append(Integer.toHexString(System.identityHashCode(cls)));
-        }
-    }
-}
diff --git a/android/support/v4/util/LogWriter.java b/android/support/v4/util/LogWriter.java
deleted file mode 100644
index 0ab3298..0000000
--- a/android/support/v4/util/LogWriter.java
+++ /dev/null
@@ -1,72 +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.util;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-
-import java.io.Writer;
-
-/**
- * Helper for accessing features in {@link android.util.LogWriter}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class LogWriter extends Writer {
-    private final String mTag;
-    private StringBuilder mBuilder = new StringBuilder(128);
-
-    /**
-     * Create a new Writer that sends to the log with the given priority
-     * and tag.
-     *
-     * @param tag A string tag to associate with each printed log statement.
-     */
-    public LogWriter(String tag) {
-        mTag = tag;
-    }
-
-    @Override public void close() {
-        flushBuilder();
-    }
-
-    @Override public void flush() {
-        flushBuilder();
-    }
-
-    @Override public void write(char[] buf, int offset, int count) {
-        for(int i = 0; i < count; i++) {
-            char c = buf[offset + i];
-            if ( c == '\n') {
-                flushBuilder();
-            }
-            else {
-                mBuilder.append(c);
-            }
-        }
-    }
-
-    private void flushBuilder() {
-        if (mBuilder.length() > 0) {
-            Log.d(mTag, mBuilder.toString());
-            mBuilder.delete(0, mBuilder.length());
-        }
-    }
-}
diff --git a/android/support/v4/util/LongSparseArray.java b/android/support/v4/util/LongSparseArray.java
deleted file mode 100644
index febb5d5..0000000
--- a/android/support/v4/util/LongSparseArray.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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;
-
-/**
- * SparseArray mapping longs to Objects, a version of the platform's
- * {@link android.util.LongSparseArray} that can be used on older versions of the
- * platform.  Unlike a normal array of Objects,
- * there can be gaps in the indices.  It is intended to be more memory efficient
- * than using a HashMap to map Longs to Objects, both because it avoids
-  * auto-boxing keys and its data structure doesn't rely on an extra entry object
-  * for each mapping.
- *
- * <p>Note that this container keeps its mappings in an array data structure,
- * using a binary search to find keys.  The implementation is not intended to be appropriate for
- * data structures
- * that may contain large numbers of items.  It is generally slower than a traditional
- * HashMap, since lookups require a binary search and adds and removes require inserting
- * and deleting entries in the array.  For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.</p>
- *
- * <p>To help with performance, the container includes an optimization when removing
- * keys: instead of compacting its array immediately, it leaves the removed entry marked
- * as deleted.  The entry can then be re-used for the same key, or compacted later in
- * a single garbage collection step of all removed entries.  This garbage collection will
- * need to be performed at any time the array needs to be grown or the the map size or
- * entry values are retrieved.</p>
- */
-public class LongSparseArray<E> implements Cloneable {
-    private static final Object DELETED = new Object();
-    private boolean mGarbage = false;
-
-    private long[] mKeys;
-    private Object[] mValues;
-    private int mSize;
-
-    /**
-     * Creates a new LongSparseArray containing no mappings.
-     */
-    public LongSparseArray() {
-        this(10);
-    }
-
-    /**
-     * Creates a new LongSparseArray containing no mappings that will not
-     * require any additional memory allocation to store the specified
-     * number of mappings.  If you supply an initial capacity of 0, the
-     * sparse array will be initialized with a light-weight representation
-     * not requiring any additional array allocations.
-     */
-    public LongSparseArray(int initialCapacity) {
-        if (initialCapacity == 0) {
-            mKeys = ContainerHelpers.EMPTY_LONGS;
-            mValues = ContainerHelpers.EMPTY_OBJECTS;
-        } else {
-            initialCapacity = ContainerHelpers.idealLongArraySize(initialCapacity);
-            mKeys = new long[initialCapacity];
-            mValues = new Object[initialCapacity];
-        }
-        mSize = 0;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public LongSparseArray<E> clone() {
-        LongSparseArray<E> clone = null;
-        try {
-            clone = (LongSparseArray<E>) super.clone();
-            clone.mKeys = mKeys.clone();
-            clone.mValues = mValues.clone();
-        } catch (CloneNotSupportedException cnse) {
-            /* ignore */
-        }
-        return clone;
-    }
-
-    /**
-     * Gets the Object mapped from the specified key, or <code>null</code>
-     * if no such mapping has been made.
-     */
-    public E get(long key) {
-        return get(key, null);
-    }
-
-    /**
-     * Gets the Object mapped from the specified key, or the specified Object
-     * if no such mapping has been made.
-     */
-    @SuppressWarnings("unchecked")
-    public E get(long key, E valueIfKeyNotFound) {
-        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i < 0 || mValues[i] == DELETED) {
-            return valueIfKeyNotFound;
-        } else {
-            return (E) mValues[i];
-        }
-    }
-
-    /**
-     * Removes the mapping from the specified key, if there was any.
-     */
-    public void delete(long key) {
-        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i >= 0) {
-            if (mValues[i] != DELETED) {
-                mValues[i] = DELETED;
-                mGarbage = true;
-            }
-        }
-    }
-
-    /**
-     * Alias for {@link #delete(long)}.
-     */
-    public void remove(long key) {
-        delete(key);
-    }
-
-    /**
-     * Removes the mapping at the specified index.
-     */
-    public void removeAt(int index) {
-        if (mValues[index] != DELETED) {
-            mValues[index] = DELETED;
-            mGarbage = true;
-        }
-    }
-
-    private void gc() {
-        // Log.e("SparseArray", "gc start with " + mSize);
-
-        int n = mSize;
-        int o = 0;
-        long[] keys = mKeys;
-        Object[] values = mValues;
-
-        for (int i = 0; i < n; i++) {
-            Object val = values[i];
-
-            if (val != DELETED) {
-                if (i != o) {
-                    keys[o] = keys[i];
-                    values[o] = val;
-                    values[i] = null;
-                }
-
-                o++;
-            }
-        }
-
-        mGarbage = false;
-        mSize = o;
-
-        // Log.e("SparseArray", "gc end with " + mSize);
-    }
-
-    /**
-     * Adds a mapping from the specified key to the specified value,
-     * replacing the previous mapping from the specified key if there
-     * was one.
-     */
-    public void put(long key, E value) {
-        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i >= 0) {
-            mValues[i] = value;
-        } else {
-            i = ~i;
-
-            if (i < mSize && mValues[i] == DELETED) {
-                mKeys[i] = key;
-                mValues[i] = value;
-                return;
-            }
-
-            if (mGarbage && mSize >= mKeys.length) {
-                gc();
-
-                // Search again because indices may have changed.
-                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
-            }
-
-            if (mSize >= mKeys.length) {
-                int n = ContainerHelpers.idealLongArraySize(mSize + 1);
-
-                long[] nkeys = new long[n];
-                Object[] nvalues = new Object[n];
-
-                // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
-                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
-                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
-                mKeys = nkeys;
-                mValues = nvalues;
-            }
-
-            if (mSize - i != 0) {
-                // Log.e("SparseArray", "move " + (mSize - i));
-                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
-                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
-            }
-
-            mKeys[i] = key;
-            mValues[i] = value;
-            mSize++;
-        }
-    }
-
-    /**
-     * Returns the number of key-value mappings that this LongSparseArray
-     * currently stores.
-     */
-    public int size() {
-        if (mGarbage) {
-            gc();
-        }
-
-        return mSize;
-    }
-
-    /**
-     * Return true if size() is 0.
-     * @return true if size() is 0.
-     */
-    public boolean isEmpty() {
-        return size() == 0;
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, returns
-     * the key from the <code>index</code>th key-value mapping that this
-     * LongSparseArray stores.
-     */
-    public long keyAt(int index) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return mKeys[index];
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, returns
-     * the value from the <code>index</code>th key-value mapping that this
-     * LongSparseArray stores.
-     */
-    @SuppressWarnings("unchecked")
-    public E valueAt(int index) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return (E) mValues[index];
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, sets a new
-     * value for the <code>index</code>th key-value mapping that this
-     * LongSparseArray stores.
-     */
-    public void setValueAt(int index, E value) {
-        if (mGarbage) {
-            gc();
-        }
-
-        mValues[index] = value;
-    }
-
-    /**
-     * Returns the index for which {@link #keyAt} would return the
-     * specified key, or a negative number if the specified
-     * key is not mapped.
-     */
-    public int indexOfKey(long key) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return ContainerHelpers.binarySearch(mKeys, mSize, key);
-    }
-
-    /**
-     * Returns an index for which {@link #valueAt} would return the
-     * specified key, or a negative number if no keys map to the
-     * specified value.
-     * Beware that this is a linear search, unlike lookups by key,
-     * and that multiple keys can map to the same value and this will
-     * find only one of them.
-     */
-    public int indexOfValue(E value) {
-        if (mGarbage) {
-            gc();
-        }
-
-        for (int i = 0; i < mSize; i++)
-            if (mValues[i] == value)
-                return i;
-
-        return -1;
-    }
-
-    /**
-     * Removes all key-value mappings from this LongSparseArray.
-     */
-    public void clear() {
-        int n = mSize;
-        Object[] values = mValues;
-
-        for (int i = 0; i < n; i++) {
-            values[i] = null;
-        }
-
-        mSize = 0;
-        mGarbage = false;
-    }
-
-    /**
-     * Puts a key/value pair into the array, optimizing for the case where
-     * the key is greater than all existing keys in the array.
-     */
-    public void append(long key, E value) {
-        if (mSize != 0 && key <= mKeys[mSize - 1]) {
-            put(key, value);
-            return;
-        }
-
-        if (mGarbage && mSize >= mKeys.length) {
-            gc();
-        }
-
-        int pos = mSize;
-        if (pos >= mKeys.length) {
-            int n = ContainerHelpers.idealLongArraySize(pos + 1);
-
-            long[] nkeys = new long[n];
-            Object[] nvalues = new Object[n];
-
-            // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
-            System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
-            System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
-            mKeys = nkeys;
-            mValues = nvalues;
-        }
-
-        mKeys[pos] = key;
-        mValues[pos] = value;
-        mSize = pos + 1;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation composes a string by iterating over its mappings. If
-     * this map contains itself as a value, the string "(this Map)"
-     * will appear in its place.
-     */
-    @Override
-    public String toString() {
-        if (size() <= 0) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 28);
-        buffer.append('{');
-        for (int i=0; i<mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            long key = keyAt(i);
-            buffer.append(key);
-            buffer.append('=');
-            Object value = valueAt(i);
-            if (value != this) {
-                buffer.append(value);
-            } else {
-                buffer.append("(this Map)");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-}
diff --git a/android/support/v4/util/LruCache.java b/android/support/v4/util/LruCache.java
deleted file mode 100644
index 944b354..0000000
--- a/android/support/v4/util/LruCache.java
+++ /dev/null
@@ -1,344 +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.util;
-
-import java.util.LinkedHashMap;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * Static library version of {@link android.util.LruCache}. Used to write apps
- * that run on API levels prior to 12. When running on API level 12 or above,
- * this implementation is still used; it does not try to switch to the
- * framework's implementation. See the framework SDK documentation for a class
- * overview.
- */
-public class LruCache<K, V> {
-    private final LinkedHashMap<K, V> map;
-
-    /** Size of this cache in units. Not necessarily the number of elements. */
-    private int size;
-    private int maxSize;
-
-    private int putCount;
-    private int createCount;
-    private int evictionCount;
-    private int hitCount;
-    private int missCount;
-
-    /**
-     * @param maxSize for caches that do not override {@link #sizeOf}, this is
-     *     the maximum number of entries in the cache. For all other caches,
-     *     this is the maximum sum of the sizes of the entries in this cache.
-     */
-    public LruCache(int maxSize) {
-        if (maxSize <= 0) {
-            throw new IllegalArgumentException("maxSize <= 0");
-        }
-        this.maxSize = maxSize;
-        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
-    }
-
-    /**
-     * Sets the size of the cache.
-     *
-     * @param maxSize The new maximum size.
-     */
-    public void resize(int maxSize) {
-        if (maxSize <= 0) {
-            throw new IllegalArgumentException("maxSize <= 0");
-        }
-
-        synchronized (this) {
-            this.maxSize = maxSize;
-        }
-        trimToSize(maxSize);
-    }
-
-    /**
-     * Returns the value for {@code key} if it exists in the cache or can be
-     * created by {@code #create}. If a value was returned, it is moved to the
-     * head of the queue. This returns null if a value is not cached and cannot
-     * be created.
-     */
-    public final V get(K key) {
-        if (key == null) {
-            throw new NullPointerException("key == null");
-        }
-
-        V mapValue;
-        synchronized (this) {
-            mapValue = map.get(key);
-            if (mapValue != null) {
-                hitCount++;
-                return mapValue;
-            }
-            missCount++;
-        }
-
-        /*
-         * Attempt to create a value. This may take a long time, and the map
-         * may be different when create() returns. If a conflicting value was
-         * added to the map while create() was working, we leave that value in
-         * the map and release the created value.
-         */
-
-        V createdValue = create(key);
-        if (createdValue == null) {
-            return null;
-        }
-
-        synchronized (this) {
-            createCount++;
-            mapValue = map.put(key, createdValue);
-
-            if (mapValue != null) {
-                // There was a conflict so undo that last put
-                map.put(key, mapValue);
-            } else {
-                size += safeSizeOf(key, createdValue);
-            }
-        }
-
-        if (mapValue != null) {
-            entryRemoved(false, key, createdValue, mapValue);
-            return mapValue;
-        } else {
-            trimToSize(maxSize);
-            return createdValue;
-        }
-    }
-
-    /**
-     * Caches {@code value} for {@code key}. The value is moved to the head of
-     * the queue.
-     *
-     * @return the previous value mapped by {@code key}.
-     */
-    public final V put(K key, V value) {
-        if (key == null || value == null) {
-            throw new NullPointerException("key == null || value == null");
-        }
-
-        V previous;
-        synchronized (this) {
-            putCount++;
-            size += safeSizeOf(key, value);
-            previous = map.put(key, value);
-            if (previous != null) {
-                size -= safeSizeOf(key, previous);
-            }
-        }
-
-        if (previous != null) {
-            entryRemoved(false, key, previous, value);
-        }
-
-        trimToSize(maxSize);
-        return previous;
-    }
-
-    /**
-     * Remove the eldest entries until the total of remaining entries is at or
-     * below the requested size.
-     *
-     * @param maxSize the maximum size of the cache before returning. May be -1
-     *            to evict even 0-sized elements.
-     */
-    public void trimToSize(int maxSize) {
-        while (true) {
-            K key;
-            V value;
-            synchronized (this) {
-                if (size < 0 || (map.isEmpty() && size != 0)) {
-                    throw new IllegalStateException(getClass().getName()
-                            + ".sizeOf() is reporting inconsistent results!");
-                }
-
-                if (size <= maxSize || map.isEmpty()) {
-                    break;
-                }
-
-                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
-                key = toEvict.getKey();
-                value = toEvict.getValue();
-                map.remove(key);
-                size -= safeSizeOf(key, value);
-                evictionCount++;
-            }
-
-            entryRemoved(true, key, value, null);
-        }
-    }
-
-    /**
-     * Removes the entry for {@code key} if it exists.
-     *
-     * @return the previous value mapped by {@code key}.
-     */
-    public final V remove(K key) {
-        if (key == null) {
-            throw new NullPointerException("key == null");
-        }
-
-        V previous;
-        synchronized (this) {
-            previous = map.remove(key);
-            if (previous != null) {
-                size -= safeSizeOf(key, previous);
-            }
-        }
-
-        if (previous != null) {
-            entryRemoved(false, key, previous, null);
-        }
-
-        return previous;
-    }
-
-    /**
-     * Called for entries that have been evicted or removed. This method is
-     * invoked when a value is evicted to make space, removed by a call to
-     * {@link #remove}, or replaced by a call to {@link #put}. The default
-     * implementation does nothing.
-     *
-     * <p>The method is called without synchronization: other threads may
-     * access the cache while this method is executing.
-     *
-     * @param evicted true if the entry is being removed to make space, false
-     *     if the removal was caused by a {@link #put} or {@link #remove}.
-     * @param newValue the new value for {@code key}, if it exists. If non-null,
-     *     this removal was caused by a {@link #put}. Otherwise it was caused by
-     *     an eviction or a {@link #remove}.
-     */
-    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
-
-    /**
-     * Called after a cache miss to compute a value for the corresponding key.
-     * Returns the computed value or null if no value can be computed. The
-     * default implementation returns null.
-     *
-     * <p>The method is called without synchronization: other threads may
-     * access the cache while this method is executing.
-     *
-     * <p>If a value for {@code key} exists in the cache when this method
-     * returns, the created value will be released with {@link #entryRemoved}
-     * and discarded. This can occur when multiple threads request the same key
-     * at the same time (causing multiple values to be created), or when one
-     * thread calls {@link #put} while another is creating a value for the same
-     * key.
-     */
-    protected V create(K key) {
-        return null;
-    }
-
-    private int safeSizeOf(K key, V value) {
-        int result = sizeOf(key, value);
-        if (result < 0) {
-            throw new IllegalStateException("Negative size: " + key + "=" + value);
-        }
-        return result;
-    }
-
-    /**
-     * Returns the size of the entry for {@code key} and {@code value} in
-     * user-defined units.  The default implementation returns 1 so that size
-     * is the number of entries and max size is the maximum number of entries.
-     *
-     * <p>An entry's size must not change while it is in the cache.
-     */
-    protected int sizeOf(K key, V value) {
-        return 1;
-    }
-
-    /**
-     * Clear the cache, calling {@link #entryRemoved} on each removed entry.
-     */
-    public final void evictAll() {
-        trimToSize(-1); // -1 will evict 0-sized elements
-    }
-
-    /**
-     * For caches that do not override {@link #sizeOf}, this returns the number
-     * of entries in the cache. For all other caches, this returns the sum of
-     * the sizes of the entries in this cache.
-     */
-    public synchronized final int size() {
-        return size;
-    }
-
-    /**
-     * For caches that do not override {@link #sizeOf}, this returns the maximum
-     * number of entries in the cache. For all other caches, this returns the
-     * maximum sum of the sizes of the entries in this cache.
-     */
-    public synchronized final int maxSize() {
-        return maxSize;
-    }
-
-    /**
-     * Returns the number of times {@link #get} returned a value that was
-     * already present in the cache.
-     */
-    public synchronized final int hitCount() {
-        return hitCount;
-    }
-
-    /**
-     * Returns the number of times {@link #get} returned null or required a new
-     * value to be created.
-     */
-    public synchronized final int missCount() {
-        return missCount;
-    }
-
-    /**
-     * Returns the number of times {@link #create(Object)} returned a value.
-     */
-    public synchronized final int createCount() {
-        return createCount;
-    }
-
-    /**
-     * Returns the number of times {@link #put} was called.
-     */
-    public synchronized final int putCount() {
-        return putCount;
-    }
-
-    /**
-     * Returns the number of values that have been evicted.
-     */
-    public synchronized final int evictionCount() {
-        return evictionCount;
-    }
-
-    /**
-     * Returns a copy of the current contents of the cache, ordered from least
-     * recently accessed to most recently accessed.
-     */
-    public synchronized final Map<K, V> snapshot() {
-        return new LinkedHashMap<K, V>(map);
-    }
-
-    @Override public synchronized final String toString() {
-        int accesses = hitCount + missCount;
-        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
-        return String.format(Locale.US, "LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
-                maxSize, hitCount, missCount, hitPercent);
-    }
-}
diff --git a/android/support/v4/util/MapCollections.java b/android/support/v4/util/MapCollections.java
deleted file mode 100644
index 1a0ab6b..0000000
--- a/android/support/v4/util/MapCollections.java
+++ /dev/null
@@ -1,560 +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.util;
-
-import java.lang.reflect.Array;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-/**
- * Helper for writing standard Java collection interfaces to a data
- * structure like {@link ArrayMap}.
- */
-abstract class MapCollections<K, V> {
-    EntrySet mEntrySet;
-    KeySet mKeySet;
-    ValuesCollection mValues;
-
-    final class ArrayIterator<T> implements Iterator<T> {
-        final int mOffset;
-        int mSize;
-        int mIndex;
-        boolean mCanRemove = false;
-
-        ArrayIterator(int offset) {
-            mOffset = offset;
-            mSize = colGetSize();
-        }
-
-        @Override
-        public boolean hasNext() {
-            return mIndex < mSize;
-        }
-
-        @Override
-        public T next() {
-            if (!hasNext()) throw new NoSuchElementException();
-            Object res = colGetEntry(mIndex, mOffset);
-            mIndex++;
-            mCanRemove = true;
-            return (T)res;
-        }
-
-        @Override
-        public void remove() {
-            if (!mCanRemove) {
-                throw new IllegalStateException();
-            }
-            mIndex--;
-            mSize--;
-            mCanRemove = false;
-            colRemoveAt(mIndex);
-        }
-    }
-
-    final class MapIterator implements Iterator<Map.Entry<K, V>>, Map.Entry<K, V> {
-        int mEnd;
-        int mIndex;
-        boolean mEntryValid = false;
-
-        MapIterator() {
-            mEnd = colGetSize() - 1;
-            mIndex = -1;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return mIndex < mEnd;
-        }
-
-        @Override
-        public Map.Entry<K, V> next() {
-            if (!hasNext()) throw new NoSuchElementException();
-            mIndex++;
-            mEntryValid = true;
-            return this;
-        }
-
-        @Override
-        public void remove() {
-            if (!mEntryValid) {
-                throw new IllegalStateException();
-            }
-            colRemoveAt(mIndex);
-            mIndex--;
-            mEnd--;
-            mEntryValid = false;
-        }
-
-        @Override
-        public K getKey() {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            return (K)colGetEntry(mIndex, 0);
-        }
-
-        @Override
-        public V getValue() {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            return (V)colGetEntry(mIndex, 1);
-        }
-
-        @Override
-        public V setValue(V object) {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            return colSetValue(mIndex, object);
-        }
-
-        @Override
-        public final boolean equals(Object o) {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            if (!(o instanceof Map.Entry)) {
-                return false;
-            }
-            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
-            return ContainerHelpers.equal(e.getKey(), colGetEntry(mIndex, 0))
-                    && ContainerHelpers.equal(e.getValue(), colGetEntry(mIndex, 1));
-        }
-
-        @Override
-        public final int hashCode() {
-            if (!mEntryValid) {
-                throw new IllegalStateException(
-                        "This container does not support retaining Map.Entry objects");
-            }
-            final Object key = colGetEntry(mIndex, 0);
-            final Object value = colGetEntry(mIndex, 1);
-            return (key == null ? 0 : key.hashCode()) ^
-                    (value == null ? 0 : value.hashCode());
-        }
-
-        @Override
-        public final String toString() {
-            return getKey() + "=" + getValue();
-        }
-    }
-
-    final class EntrySet implements Set<Map.Entry<K, V>> {
-        @Override
-        public boolean add(Map.Entry<K, V> object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean addAll(Collection<? extends Map.Entry<K, V>> collection) {
-            int oldSize = colGetSize();
-            for (Map.Entry<K, V> entry : collection) {
-                colPut(entry.getKey(), entry.getValue());
-            }
-            return oldSize != colGetSize();
-        }
-
-        @Override
-        public void clear() {
-            colClear();
-        }
-
-        @Override
-        public boolean contains(Object o) {
-            if (!(o instanceof Map.Entry))
-                return false;
-            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
-            int index = colIndexOfKey(e.getKey());
-            if (index < 0) {
-                return false;
-            }
-            Object foundVal = colGetEntry(index, 1);
-            return ContainerHelpers.equal(foundVal, e.getValue());
-        }
-
-        @Override
-        public boolean containsAll(Collection<?> collection) {
-            Iterator<?> it = collection.iterator();
-            while (it.hasNext()) {
-                if (!contains(it.next())) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return colGetSize() == 0;
-        }
-
-        @Override
-        public Iterator<Map.Entry<K, V>> iterator() {
-            return new MapIterator();
-        }
-
-        @Override
-        public boolean remove(Object object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean removeAll(Collection<?> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean retainAll(Collection<?> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public int size() {
-            return colGetSize();
-        }
-
-        @Override
-        public Object[] toArray() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public <T> T[] toArray(T[] array) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            return equalsSetHelper(this, object);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = 0;
-            for (int i=colGetSize()-1; i>=0; i--) {
-                final Object key = colGetEntry(i, 0);
-                final Object value = colGetEntry(i, 1);
-                result += ( (key == null ? 0 : key.hashCode()) ^
-                        (value == null ? 0 : value.hashCode()) );
-            }
-            return result;
-        }
-    };
-
-    final class KeySet implements Set<K> {
-
-        @Override
-        public boolean add(K object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean addAll(Collection<? extends K> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void clear() {
-            colClear();
-        }
-
-        @Override
-        public boolean contains(Object object) {
-            return colIndexOfKey(object) >= 0;
-        }
-
-        @Override
-        public boolean containsAll(Collection<?> collection) {
-            return containsAllHelper(colGetMap(), collection);
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return colGetSize() == 0;
-        }
-
-        @Override
-        public Iterator<K> iterator() {
-            return new ArrayIterator<K>(0);
-        }
-
-        @Override
-        public boolean remove(Object object) {
-            int index = colIndexOfKey(object);
-            if (index >= 0) {
-                colRemoveAt(index);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean removeAll(Collection<?> collection) {
-            return removeAllHelper(colGetMap(), collection);
-        }
-
-        @Override
-        public boolean retainAll(Collection<?> collection) {
-            return retainAllHelper(colGetMap(), collection);
-        }
-
-        @Override
-        public int size() {
-            return colGetSize();
-        }
-
-        @Override
-        public Object[] toArray() {
-            return toArrayHelper(0);
-        }
-
-        @Override
-        public <T> T[] toArray(T[] array) {
-            return toArrayHelper(array, 0);
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            return equalsSetHelper(this, object);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = 0;
-            for (int i=colGetSize()-1; i>=0; i--) {
-                Object obj = colGetEntry(i, 0);
-                result += obj == null ? 0 : obj.hashCode();
-            }
-            return result;
-        }
-    };
-
-    final class ValuesCollection implements Collection<V> {
-
-        @Override
-        public boolean add(V object) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean addAll(Collection<? extends V> collection) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public void clear() {
-            colClear();
-        }
-
-        @Override
-        public boolean contains(Object object) {
-            return colIndexOfValue(object) >= 0;
-        }
-
-        @Override
-        public boolean containsAll(Collection<?> collection) {
-            Iterator<?> it = collection.iterator();
-            while (it.hasNext()) {
-                if (!contains(it.next())) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return colGetSize() == 0;
-        }
-
-        @Override
-        public Iterator<V> iterator() {
-            return new ArrayIterator<V>(1);
-        }
-
-        @Override
-        public boolean remove(Object object) {
-            int index = colIndexOfValue(object);
-            if (index >= 0) {
-                colRemoveAt(index);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean removeAll(Collection<?> collection) {
-            int N = colGetSize();
-            boolean changed = false;
-            for (int i=0; i<N; i++) {
-                Object cur = colGetEntry(i, 1);
-                if (collection.contains(cur)) {
-                    colRemoveAt(i);
-                    i--;
-                    N--;
-                    changed = true;
-                }
-            }
-            return changed;
-        }
-
-        @Override
-        public boolean retainAll(Collection<?> collection) {
-            int N = colGetSize();
-            boolean changed = false;
-            for (int i=0; i<N; i++) {
-                Object cur = colGetEntry(i, 1);
-                if (!collection.contains(cur)) {
-                    colRemoveAt(i);
-                    i--;
-                    N--;
-                    changed = true;
-                }
-            }
-            return changed;
-        }
-
-        @Override
-        public int size() {
-            return colGetSize();
-        }
-
-        @Override
-        public Object[] toArray() {
-            return toArrayHelper(1);
-        }
-
-        @Override
-        public <T> T[] toArray(T[] array) {
-            return toArrayHelper(array, 1);
-        }
-    };
-
-    public static <K, V> boolean containsAllHelper(Map<K, V> map, Collection<?> collection) {
-        Iterator<?> it = collection.iterator();
-        while (it.hasNext()) {
-            if (!map.containsKey(it.next())) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static <K, V> boolean removeAllHelper(Map<K, V> map, Collection<?> collection) {
-        int oldSize = map.size();
-        Iterator<?> it = collection.iterator();
-        while (it.hasNext()) {
-            map.remove(it.next());
-        }
-        return oldSize != map.size();
-    }
-
-    public static <K, V> boolean retainAllHelper(Map<K, V> map, Collection<?> collection) {
-        int oldSize = map.size();
-        Iterator<K> it = map.keySet().iterator();
-        while (it.hasNext()) {
-            if (!collection.contains(it.next())) {
-                it.remove();
-            }
-        }
-        return oldSize != map.size();
-    }
-
-
-    public Object[] toArrayHelper(int offset) {
-        final int N = colGetSize();
-        Object[] result = new Object[N];
-        for (int i=0; i<N; i++) {
-            result[i] = colGetEntry(i, offset);
-        }
-        return result;
-    }
-
-    public <T> T[] toArrayHelper(T[] array, int offset) {
-        final int N  = colGetSize();
-        if (array.length < N) {
-            @SuppressWarnings("unchecked") T[] newArray
-                = (T[]) Array.newInstance(array.getClass().getComponentType(), N);
-            array = newArray;
-        }
-        for (int i=0; i<N; i++) {
-            array[i] = (T)colGetEntry(i, offset);
-        }
-        if (array.length > N) {
-            array[N] = null;
-        }
-        return array;
-    }
-
-    public static <T> boolean equalsSetHelper(Set<T> set, Object object) {
-        if (set == object) {
-            return true;
-        }
-        if (object instanceof Set) {
-            Set<?> s = (Set<?>) object;
-
-            try {
-                return set.size() == s.size() && set.containsAll(s);
-            } catch (NullPointerException ignored) {
-                return false;
-            } catch (ClassCastException ignored) {
-                return false;
-            }
-        }
-        return false;
-    }
-
-    public Set<Map.Entry<K, V>> getEntrySet() {
-        if (mEntrySet == null) {
-            mEntrySet = new EntrySet();
-        }
-        return mEntrySet;
-    }
-
-    public Set<K> getKeySet() {
-        if (mKeySet == null) {
-            mKeySet = new KeySet();
-        }
-        return mKeySet;
-    }
-
-    public Collection<V> getValues() {
-        if (mValues == null) {
-            mValues = new ValuesCollection();
-        }
-        return mValues;
-    }
-
-    protected abstract int colGetSize();
-    protected abstract Object colGetEntry(int index, int offset);
-    protected abstract int colIndexOfKey(Object key);
-    protected abstract int colIndexOfValue(Object key);
-    protected abstract Map<K, V> colGetMap();
-    protected abstract void colPut(K key, V value);
-    protected abstract V colSetValue(int index, V value);
-    protected abstract void colRemoveAt(int index);
-    protected abstract void colClear();
-}
diff --git a/android/support/v4/util/ObjectsCompat.java b/android/support/v4/util/ObjectsCompat.java
deleted file mode 100644
index 747cfb4..0000000
--- a/android/support/v4/util/ObjectsCompat.java
+++ /dev/null
@@ -1,97 +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.util;
-
-import android.os.Build;
-import android.support.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * This class consists of static utility methods for operating on objects.
- */
-public class ObjectsCompat {
-    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(@Nullable Object a, @Nullable Object b) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return Objects.equals(a, b);
-        } else {
-            return (a == b) || (a != null && a.equals(b));
-        }
-    }
-
-    /**
-     * Returns the hash code of a non-{@code null} argument and 0 for a {@code null} argument.
-     *
-     * @param o an object
-     * @return the hash code of a non-{@code null} argument and 0 for a {@code null} argument
-     * @see Object#hashCode
-     */
-    public static int hashCode(@Nullable Object o) {
-        return o != null ? o.hashCode() : 0;
-    }
-
-    /**
-     * Generates a hash code for a sequence of input values. The hash code is generated as if all
-     * the input values were placed into an array, and that array were hashed by calling
-     * {@link Arrays#hashCode(Object[])}.
-     *
-     * <p>This method is useful for implementing {@link Object#hashCode()} on objects containing
-     * multiple fields. For example, if an object that has three fields, {@code x}, {@code y}, and
-     * {@code z}, one could write:
-     *
-     * <blockquote><pre>
-     * &#064;Override public int hashCode() {
-     *     return ObjectsCompat.hash(x, y, z);
-     * }
-     * </pre></blockquote>
-     *
-     * <b>Warning: When a single object reference is supplied, the returned value does not equal the
-     * hash code of that object reference.</b> This value can be computed by calling
-     * {@link #hashCode(Object)}.
-     *
-     * @param values the values to be hashed
-     * @return a hash value of the sequence of input values
-     * @see Arrays#hashCode(Object[])
-     */
-    public static int hash(@Nullable Object... values) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return Objects.hash(values);
-        } else {
-            return Arrays.hashCode(values);
-        }
-    }
-}
diff --git a/android/support/v4/util/Pair.java b/android/support/v4/util/Pair.java
deleted file mode 100644
index 9047aec..0000000
--- a/android/support/v4/util/Pair.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.NonNull;
-import android.support.annotation.Nullable;
-
-/**
- * Container to ease passing around a tuple of two objects. This object provides a sensible
- * implementation of equals(), returning true if equals() is true on each of the contained
- * objects.
- */
-public class Pair<F, S> {
-    public final @Nullable F first;
-    public final @Nullable S second;
-
-    /**
-     * Constructor for a Pair.
-     *
-     * @param first the first object in the Pair
-     * @param second the second object in the pair
-     */
-    public Pair(@Nullable F first, @Nullable S second) {
-        this.first = first;
-        this.second = second;
-    }
-
-    /**
-     * Checks the two objects for equality by delegating to their respective
-     * {@link Object#equals(Object)} methods.
-     *
-     * @param o the {@link Pair} to which this one is to be checked for equality
-     * @return true if the underlying objects of the Pair are both considered
-     *         equal
-     */
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof Pair)) {
-            return false;
-        }
-        Pair<?, ?> p = (Pair<?, ?>) o;
-        return objectsEqual(p.first, first) && objectsEqual(p.second, second);
-    }
-
-    private static boolean objectsEqual(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
-    /**
-     * Compute a hash code using the hash codes of the underlying objects
-     *
-     * @return a hashcode of the Pair
-     */
-    @Override
-    public int hashCode() {
-        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
-    }
-
-    @Override
-    public String toString() {
-        return "Pair{" + String.valueOf(first) + " " + String.valueOf(second) + "}";
-    }
-
-    /**
-     * Convenience method for creating an appropriately typed pair.
-     * @param a the first object in the Pair
-     * @param b the second object in the pair
-     * @return a Pair that is templatized with the types of a and b
-     */
-    @NonNull
-    public static <A, B> Pair <A, B> create(@Nullable A a, @Nullable B b) {
-        return new Pair<A, B>(a, b);
-    }
-}
diff --git a/android/support/v4/util/PatternsCompat.java b/android/support/v4/util/PatternsCompat.java
deleted file mode 100644
index 3d26a3f..0000000
--- a/android/support/v4/util/PatternsCompat.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.v4.util;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-import java.util.regex.Pattern;
-
-/**
- * Commonly used regular expression patterns.
- */
-public final class PatternsCompat {
-    /**
-     *  Regular expression to match all IANA top-level domains.
-     *
-     *  List accurate as of 2015/11/24.  List taken from:
-     *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
-     */
-    static final String IANA_TOP_LEVEL_DOMAINS =
-        "(?:"
-        + "(?:aaa|aarp|abb|abbott|abogado|academy|accenture|accountant|accountants|aco|active"
-        + "|actor|ads|adult|aeg|aero|afl|agency|aig|airforce|airtel|allfinanz|alsace|amica|amsterdam"
-        + "|android|apartments|app|apple|aquarelle|aramco|archi|army|arpa|arte|asia|associates"
-        + "|attorney|auction|audio|auto|autos|axa|azure|a[cdefgilmoqrstuwxz])"
-        + "|(?:band|bank|bar|barcelona|barclaycard|barclays|bargains|bauhaus|bayern|bbc|bbva"
-        + "|bcn|beats|beer|bentley|berlin|best|bet|bharti|bible|bid|bike|bing|bingo|bio|biz|black"
-        + "|blackfriday|bloomberg|blue|bms|bmw|bnl|bnpparibas|boats|bom|bond|boo|boots|boutique"
-        + "|bradesco|bridgestone|broadway|broker|brother|brussels|budapest|build|builders|business"
-        + "|buzz|bzh|b[abdefghijmnorstvwyz])"
-        + "|(?:cab|cafe|cal|camera|camp|cancerresearch|canon|capetown|capital|car|caravan|cards"
-        + "|care|career|careers|cars|cartier|casa|cash|casino|cat|catering|cba|cbn|ceb|center|ceo"
-        + "|cern|cfa|cfd|chanel|channel|chat|cheap|chloe|christmas|chrome|church|cipriani|cisco"
-        + "|citic|city|cityeats|claims|cleaning|click|clinic|clothing|cloud|club|clubmed|coach"
-        + "|codes|coffee|college|cologne|com|commbank|community|company|computer|comsec|condos"
-        + "|construction|consulting|contractors|cooking|cool|coop|corsica|country|coupons|courses"
-        + "|credit|creditcard|creditunion|cricket|crown|crs|cruises|csc|cuisinella|cymru|cyou|c[acdfghiklmnoruvwxyz])"
-        + "|(?:dabur|dad|dance|date|dating|datsun|day|dclk|deals|degree|delivery|dell|delta"
-        + "|democrat|dental|dentist|desi|design|dev|diamonds|diet|digital|direct|directory|discount"
-        + "|dnp|docs|dog|doha|domains|doosan|download|drive|durban|dvag|d[ejkmoz])"
-        + "|(?:earth|eat|edu|education|email|emerck|energy|engineer|engineering|enterprises"
-        + "|epson|equipment|erni|esq|estate|eurovision|eus|events|everbank|exchange|expert|exposed"
-        + "|express|e[cegrstu])"
-        + "|(?:fage|fail|fairwinds|faith|family|fan|fans|farm|fashion|feedback|ferrero|film"
-        + "|final|finance|financial|firmdale|fish|fishing|fit|fitness|flights|florist|flowers|flsmidth"
-        + "|fly|foo|football|forex|forsale|forum|foundation|frl|frogans|fund|furniture|futbol|fyi"
-        + "|f[ijkmor])"
-        + "|(?:gal|gallery|game|garden|gbiz|gdn|gea|gent|genting|ggee|gift|gifts|gives|giving"
-        + "|glass|gle|global|globo|gmail|gmo|gmx|gold|goldpoint|golf|goo|goog|google|gop|gov|grainger"
-        + "|graphics|gratis|green|gripe|group|gucci|guge|guide|guitars|guru|g[abdefghilmnpqrstuwy])"
-        + "|(?:hamburg|hangout|haus|healthcare|help|here|hermes|hiphop|hitachi|hiv|hockey|holdings"
-        + "|holiday|homedepot|homes|honda|horse|host|hosting|hoteles|hotmail|house|how|hsbc|hyundai"
-        + "|h[kmnrtu])"
-        + "|(?:ibm|icbc|ice|icu|ifm|iinet|immo|immobilien|industries|infiniti|info|ing|ink|institute"
-        + "|insure|int|international|investments|ipiranga|irish|ist|istanbul|itau|iwc|i[delmnoqrst])"
-        + "|(?:jaguar|java|jcb|jetzt|jewelry|jlc|jll|jobs|joburg|jprs|juegos|j[emop])"
-        + "|(?:kaufen|kddi|kia|kim|kinder|kitchen|kiwi|koeln|komatsu|krd|kred|kyoto|k[eghimnprwyz])"
-        + "|(?:lacaixa|lancaster|land|landrover|lasalle|lat|latrobe|law|lawyer|lds|lease|leclerc"
-        + "|legal|lexus|lgbt|liaison|lidl|life|lifestyle|lighting|limited|limo|linde|link|live"
-        + "|lixil|loan|loans|lol|london|lotte|lotto|love|ltd|ltda|lupin|luxe|luxury|l[abcikrstuvy])"
-        + "|(?:madrid|maif|maison|man|management|mango|market|marketing|markets|marriott|mba"
-        + "|media|meet|melbourne|meme|memorial|men|menu|meo|miami|microsoft|mil|mini|mma|mobi|moda"
-        + "|moe|moi|mom|monash|money|montblanc|mormon|mortgage|moscow|motorcycles|mov|movie|movistar"
-        + "|mtn|mtpc|mtr|museum|mutuelle|m[acdeghklmnopqrstuvwxyz])"
-        + "|(?:nadex|nagoya|name|navy|nec|net|netbank|network|neustar|new|news|nexus|ngo|nhk"
-        + "|nico|ninja|nissan|nokia|nra|nrw|ntt|nyc|n[acefgilopruz])"
-        + "|(?:obi|office|okinawa|omega|one|ong|onl|online|ooo|oracle|orange|org|organic|osaka"
-        + "|otsuka|ovh|om)"
-        + "|(?:page|panerai|paris|partners|parts|party|pet|pharmacy|philips|photo|photography"
-        + "|photos|physio|piaget|pics|pictet|pictures|ping|pink|pizza|place|play|playstation|plumbing"
-        + "|plus|pohl|poker|porn|post|praxi|press|pro|prod|productions|prof|properties|property"
-        + "|protection|pub|p[aefghklmnrstwy])"
-        + "|(?:qpon|quebec|qa)"
-        + "|(?:racing|realtor|realty|recipes|red|redstone|rehab|reise|reisen|reit|ren|rent|rentals"
-        + "|repair|report|republican|rest|restaurant|review|reviews|rich|ricoh|rio|rip|rocher|rocks"
-        + "|rodeo|rsvp|ruhr|run|rwe|ryukyu|r[eosuw])"
-        + "|(?:saarland|sakura|sale|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|saxo"
-        + "|sbs|sca|scb|schmidt|scholarships|school|schule|schwarz|science|scor|scot|seat|security"
-        + "|seek|sener|services|seven|sew|sex|sexy|shiksha|shoes|show|shriram|singles|site|ski"
-        + "|sky|skype|sncf|soccer|social|software|sohu|solar|solutions|sony|soy|space|spiegel|spreadbetting"
-        + "|srl|stada|starhub|statoil|stc|stcgroup|stockholm|studio|study|style|sucks|supplies"
-        + "|supply|support|surf|surgery|suzuki|swatch|swiss|sydney|systems|s[abcdeghijklmnortuvxyz])"
-        + "|(?:tab|taipei|tatamotors|tatar|tattoo|tax|taxi|team|tech|technology|tel|telefonica"
-        + "|temasek|tennis|thd|theater|theatre|tickets|tienda|tips|tires|tirol|today|tokyo|tools"
-        + "|top|toray|toshiba|tours|town|toyota|toys|trade|trading|training|travel|trust|tui|t[cdfghjklmnortvwz])"
-        + "|(?:ubs|university|uno|uol|u[agksyz])"
-        + "|(?:vacations|vana|vegas|ventures|versicherung|vet|viajes|video|villas|vin|virgin"
-        + "|vision|vista|vistaprint|viva|vlaanderen|vodka|vote|voting|voto|voyage|v[aceginu])"
-        + "|(?:wales|walter|wang|watch|webcam|website|wed|wedding|weir|whoswho|wien|wiki|williamhill"
-        + "|win|windows|wine|wme|work|works|world|wtc|wtf|w[fs])"
-        + "|(?:\u03b5\u03bb|\u0431\u0435\u043b|\u0434\u0435\u0442\u0438|\u043a\u043e\u043c|\u043c\u043a\u0434"
-        + "|\u043c\u043e\u043d|\u043c\u043e\u0441\u043a\u0432\u0430|\u043e\u043d\u043b\u0430\u0439\u043d"
-        + "|\u043e\u0440\u0433|\u0440\u0443\u0441|\u0440\u0444|\u0441\u0430\u0439\u0442|\u0441\u0440\u0431"
-        + "|\u0443\u043a\u0440|\u049b\u0430\u0437|\u0570\u0561\u0575|\u05e7\u05d5\u05dd|\u0627\u0631\u0627\u0645\u0643\u0648"
-        + "|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629"
-        + "|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0627\u06cc\u0631\u0627\u0646"
-        + "|\u0628\u0627\u0632\u0627\u0631|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633"
-        + "|\u0633\u0648\u062f\u0627\u0646|\u0633\u0648\u0631\u064a\u0629|\u0634\u0628\u0643\u0629"
-        + "|\u0639\u0631\u0627\u0642|\u0639\u0645\u0627\u0646|\u0641\u0644\u0633\u0637\u064a\u0646"
-        + "|\u0642\u0637\u0631|\u0643\u0648\u0645|\u0645\u0635\u0631|\u0645\u0644\u064a\u0633\u064a\u0627"
-        + "|\u0645\u0648\u0642\u0639|\u0915\u0949\u092e|\u0928\u0947\u091f|\u092d\u093e\u0930\u0924"
-        + "|\u0938\u0902\u0917\u0920\u0928|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4"
-        + "|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd"
-        + "|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e04\u0e2d\u0e21|\u0e44\u0e17\u0e22"
-        + "|\u10d2\u10d4|\u307f\u3093\u306a|\u30b0\u30fc\u30b0\u30eb|\u30b3\u30e0|\u4e16\u754c"
-        + "|\u4e2d\u4fe1|\u4e2d\u56fd|\u4e2d\u570b|\u4e2d\u6587\u7f51|\u4f01\u4e1a|\u4f5b\u5c71"
-        + "|\u4fe1\u606f|\u5065\u5eb7|\u516b\u5366|\u516c\u53f8|\u516c\u76ca|\u53f0\u6e7e|\u53f0\u7063"
-        + "|\u5546\u57ce|\u5546\u5e97|\u5546\u6807|\u5728\u7ebf|\u5927\u62ff|\u5a31\u4e50|\u5de5\u884c"
-        + "|\u5e7f\u4e1c|\u6148\u5584|\u6211\u7231\u4f60|\u624b\u673a|\u653f\u52a1|\u653f\u5e9c"
-        + "|\u65b0\u52a0\u5761|\u65b0\u95fb|\u65f6\u5c1a|\u673a\u6784|\u6de1\u9a6c\u9521|\u6e38\u620f"
-        + "|\u70b9\u770b|\u79fb\u52a8|\u7ec4\u7ec7\u673a\u6784|\u7f51\u5740|\u7f51\u5e97|\u7f51\u7edc"
-        + "|\u8c37\u6b4c|\u96c6\u56e2|\u98de\u5229\u6d66|\u9910\u5385|\u9999\u6e2f|\ub2f7\ub137"
-        + "|\ub2f7\ucef4|\uc0bc\uc131|\ud55c\uad6d|xbox"
-        + "|xerox|xin|xn\\-\\-11b4c3d|xn\\-\\-1qqw23a|xn\\-\\-30rr7y|xn\\-\\-3bst00m|xn\\-\\-3ds443g"
-        + "|xn\\-\\-3e0b707e|xn\\-\\-3pxu8k|xn\\-\\-42c2d9a|xn\\-\\-45brj9c|xn\\-\\-45q11c|xn\\-\\-4gbrim"
-        + "|xn\\-\\-55qw42g|xn\\-\\-55qx5d|xn\\-\\-6frz82g|xn\\-\\-6qq986b3xl|xn\\-\\-80adxhks"
-        + "|xn\\-\\-80ao21a|xn\\-\\-80asehdb|xn\\-\\-80aswg|xn\\-\\-90a3ac|xn\\-\\-90ais|xn\\-\\-9dbq2a"
-        + "|xn\\-\\-9et52u|xn\\-\\-b4w605ferd|xn\\-\\-c1avg|xn\\-\\-c2br7g|xn\\-\\-cg4bki|xn\\-\\-clchc0ea0b2g2a9gcd"
-        + "|xn\\-\\-czr694b|xn\\-\\-czrs0t|xn\\-\\-czru2d|xn\\-\\-d1acj3b|xn\\-\\-d1alf|xn\\-\\-efvy88h"
-        + "|xn\\-\\-estv75g|xn\\-\\-fhbei|xn\\-\\-fiq228c5hs|xn\\-\\-fiq64b|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s"
-        + "|xn\\-\\-fjq720a|xn\\-\\-flw351e|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-gecrj9c"
-        + "|xn\\-\\-h2brj9c|xn\\-\\-hxt814e|xn\\-\\-i1b6b1a6a2e|xn\\-\\-imr513n|xn\\-\\-io0a7i"
-        + "|xn\\-\\-j1aef|xn\\-\\-j1amh|xn\\-\\-j6w193g|xn\\-\\-kcrx77d1x4a|xn\\-\\-kprw13d|xn\\-\\-kpry57d"
-        + "|xn\\-\\-kput3i|xn\\-\\-l1acc|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgb9awbf|xn\\-\\-mgba3a3ejt"
-        + "|xn\\-\\-mgba3a4f16a|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbab2bd|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e"
-        + "|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-mgbpl2fh|xn\\-\\-mgbtx2b|xn\\-\\-mgbx4cd0ab"
-        + "|xn\\-\\-mk1bu44c|xn\\-\\-mxtq1m|xn\\-\\-ngbc5azd|xn\\-\\-node|xn\\-\\-nqv7f|xn\\-\\-nqv7fs00ema"
-        + "|xn\\-\\-nyqy26a|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1acf|xn\\-\\-p1ai|xn\\-\\-pgbs0dh"
-        + "|xn\\-\\-pssy2u|xn\\-\\-q9jyb4c|xn\\-\\-qcka1pmc|xn\\-\\-qxam|xn\\-\\-rhqv96g|xn\\-\\-s9brj9c"
-        + "|xn\\-\\-ses554g|xn\\-\\-t60b56a|xn\\-\\-tckwe|xn\\-\\-unup4y|xn\\-\\-vermgensberater\\-ctb"
-        + "|xn\\-\\-vermgensberatung\\-pwb|xn\\-\\-vhquv|xn\\-\\-vuq861b|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a"
-        + "|xn\\-\\-xhq521b|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-y9a3aq|xn\\-\\-yfro4i67o"
-        + "|xn\\-\\-ygbi2ammx|xn\\-\\-zfr164b|xperia|xxx|xyz)"
-        + "|(?:yachts|yamaxun|yandex|yodobashi|yoga|yokohama|youtube|y[et])"
-        + "|(?:zara|zip|zone|zuerich|z[amw]))";
-
-    public static final Pattern IP_ADDRESS
-            = Pattern.compile(
-            "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
-                    + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
-                    + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
-                    + "|[1-9][0-9]|[0-9]))");
-
-    /**
-     * Valid UCS characters defined in RFC 3987. Excludes space characters.
-     */
-    private static final String UCS_CHAR = "[" +
-            "\u00A0-\uD7FF" +
-            "\uF900-\uFDCF" +
-            "\uFDF0-\uFFEF" +
-            "\uD800\uDC00-\uD83F\uDFFD" +
-            "\uD840\uDC00-\uD87F\uDFFD" +
-            "\uD880\uDC00-\uD8BF\uDFFD" +
-            "\uD8C0\uDC00-\uD8FF\uDFFD" +
-            "\uD900\uDC00-\uD93F\uDFFD" +
-            "\uD940\uDC00-\uD97F\uDFFD" +
-            "\uD980\uDC00-\uD9BF\uDFFD" +
-            "\uD9C0\uDC00-\uD9FF\uDFFD" +
-            "\uDA00\uDC00-\uDA3F\uDFFD" +
-            "\uDA40\uDC00-\uDA7F\uDFFD" +
-            "\uDA80\uDC00-\uDABF\uDFFD" +
-            "\uDAC0\uDC00-\uDAFF\uDFFD" +
-            "\uDB00\uDC00-\uDB3F\uDFFD" +
-            "\uDB44\uDC00-\uDB7F\uDFFD" +
-            "&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]";
-
-    /**
-     * Valid characters for IRI label defined in RFC 3987.
-     */
-    private static final String LABEL_CHAR = "a-zA-Z0-9" + UCS_CHAR;
-
-    /**
-     * Valid characters for IRI TLD defined in RFC 3987.
-     */
-    private static final String TLD_CHAR = "a-zA-Z" + UCS_CHAR;
-
-    /**
-     * RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets.
-     */
-    private static final String IRI_LABEL =
-            "[" + LABEL_CHAR + "](?:[" + LABEL_CHAR + "_\\-]{0,61}[" + LABEL_CHAR + "]){0,1}";
-
-    /**
-     * RFC 3492 references RFC 1034 and limits Punycode algorithm output to 63 characters.
-     */
-    private static final String PUNYCODE_TLD = "xn\\-\\-[\\w\\-]{0,58}\\w";
-
-    private static final String TLD = "(" + PUNYCODE_TLD + "|" + "[" + TLD_CHAR + "]{2,63}" +")";
-
-    private static final String HOST_NAME = "(" + IRI_LABEL + "\\.)+" + TLD;
-
-    public static final Pattern DOMAIN_NAME
-            = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
-
-    private static final String PROTOCOL = "(?i:http|https|rtsp)://";
-
-    /* A word boundary or end of input.  This is to stop foo.sure from matching as foo.su */
-    private static final String WORD_BOUNDARY = "(?:\\b|$|^)";
-
-    private static final String USER_INFO = "(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
-            + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
-            + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@";
-
-    private static final String PORT_NUMBER = "\\:\\d{1,5}";
-
-    private static final String PATH_AND_QUERY = "[/\\?](?:(?:[" + LABEL_CHAR
-            + ";/\\?:@&=#~"  // plus optional query params
-            + "\\-\\.\\+!\\*'\\(\\),_\\$])|(?:%[a-fA-F0-9]{2}))*";
-
-    /**
-     *  Regular expression pattern to match most part of RFC 3987
-     *  Internationalized URLs, aka IRIs.
-     */
-    public static final Pattern WEB_URL = Pattern.compile("("
-            + "("
-            + "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?"
-            + "(?:" + DOMAIN_NAME + ")"
-            + "(?:" + PORT_NUMBER + ")?"
-            + ")"
-            + "(" + PATH_AND_QUERY + ")?"
-            + WORD_BOUNDARY
-            + ")");
-
-    /**
-     * Regular expression that matches known TLDs and punycode TLDs
-     */
-    private static final String STRICT_TLD = "(?:" +
-            IANA_TOP_LEVEL_DOMAINS + "|" + PUNYCODE_TLD + ")";
-
-    /**
-     * Regular expression that matches host names using {@link #STRICT_TLD}
-     */
-    private static final String STRICT_HOST_NAME = "(?:(?:" + IRI_LABEL + "\\.)+"
-            + STRICT_TLD + ")";
-
-    /**
-     * Regular expression that matches domain names using either {@link #STRICT_HOST_NAME} or
-     * {@link #IP_ADDRESS}
-     */
-    private static final Pattern STRICT_DOMAIN_NAME
-            = Pattern.compile("(?:" + STRICT_HOST_NAME + "|" + IP_ADDRESS + ")");
-
-    /**
-     * Regular expression that matches domain names without a TLD
-     */
-    private static final String RELAXED_DOMAIN_NAME =
-            "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS + ")";
-
-    /**
-     * Regular expression to match strings that do not start with a supported protocol. The TLDs
-     * are expected to be one of the known TLDs.
-     */
-    private static final String WEB_URL_WITHOUT_PROTOCOL = "("
-            + WORD_BOUNDARY
-            + "(?<!:\\/\\/)"
-            + "("
-            + "(?:" + STRICT_DOMAIN_NAME + ")"
-            + "(?:" + PORT_NUMBER + ")?"
-            + ")"
-            + "(?:" + PATH_AND_QUERY + ")?"
-            + WORD_BOUNDARY
-            + ")";
-
-    /**
-     * Regular expression to match strings that start with a supported protocol. Rules for domain
-     * names and TLDs are more relaxed. TLDs are optional.
-     */
-    private static final String WEB_URL_WITH_PROTOCOL = "("
-            + WORD_BOUNDARY
-            + "(?:"
-            + "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")"
-            + "(?:" + RELAXED_DOMAIN_NAME + ")?"
-            + "(?:" + PORT_NUMBER + ")?"
-            + ")"
-            + "(?:" + PATH_AND_QUERY + ")?"
-            + WORD_BOUNDARY
-            + ")";
-
-    /**
-     * Regular expression pattern to match IRIs. If a string starts with http(s):// the expression
-     * tries to match the URL structure with a relaxed rule for TLDs. If the string does not start
-     * with http(s):// the TLDs are expected to be one of the known TLDs.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final Pattern AUTOLINK_WEB_URL = Pattern.compile(
-            "(" + WEB_URL_WITH_PROTOCOL + "|" + WEB_URL_WITHOUT_PROTOCOL + ")");
-
-    /**
-     * Regular expression for valid email characters. Does not include some of the valid characters
-     * defined in RFC5321: #&~!^`{}/=$*?|
-     */
-    private static final String EMAIL_CHAR = LABEL_CHAR + "\\+\\-_%'";
-
-    /**
-     * Regular expression for local part of an email address. RFC5321 section 4.5.3.1.1 limits
-     * the local part to be at most 64 octets.
-     */
-    private static final String EMAIL_ADDRESS_LOCAL_PART =
-            "[" + EMAIL_CHAR + "]" + "(?:[" + EMAIL_CHAR + "\\.]{0,62}[" + EMAIL_CHAR + "])?";
-
-    /**
-     * Regular expression for the domain part of an email address. RFC5321 section 4.5.3.1.2 limits
-     * the domain to be at most 255 octets.
-     */
-    private static final String EMAIL_ADDRESS_DOMAIN =
-            "(?=.{1,255}(?:\\s|$|^))" + HOST_NAME;
-
-    /**
-     * Regular expression pattern to match email addresses. It excludes double quoted local parts
-     * and the special characters #&~!^`{}/=$*?| that are included in RFC5321.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static final Pattern AUTOLINK_EMAIL_ADDRESS = Pattern.compile("(" + WORD_BOUNDARY +
-            "(?:" + EMAIL_ADDRESS_LOCAL_PART + "@" + EMAIL_ADDRESS_DOMAIN + ")" +
-            WORD_BOUNDARY + ")"
-    );
-
-    public static final Pattern EMAIL_ADDRESS
-            = Pattern.compile(
-            "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" +
-                    "\\@" +
-                    "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
-                    "(" +
-                    "\\." +
-                    "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" +
-                    ")+"
-    );
-
-    /**
-     * Do not create this static utility class.
-     */
-    private PatternsCompat() {}
-}
diff --git a/android/support/v4/util/Pools.java b/android/support/v4/util/Pools.java
deleted file mode 100644
index 99fd888..0000000
--- a/android/support/v4/util/Pools.java
+++ /dev/null
@@ -1,170 +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.util;
-
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-/**
- * Helper class for creating pools of objects. An example use looks like this:
- * <pre>
- * public class MyPooledClass {
- *
- *     private static final SynchronizedPool<MyPooledClass> sPool =
- *             new SynchronizedPool<MyPooledClass>(10);
- *
- *     public static MyPooledClass obtain() {
- *         MyPooledClass instance = sPool.acquire();
- *         return (instance != null) ? instance : new MyPooledClass();
- *     }
- *
- *     public void recycle() {
- *          // Clear state if needed.
- *          sPool.release(this);
- *     }
- *
- *     . . .
- * }
- * </pre>
- *
- */
-public final class Pools {
-
-    /**
-     * Interface for managing a pool of objects.
-     *
-     * @param <T> The pooled type.
-     */
-    public static interface Pool<T> {
-
-        /**
-         * @return An instance from the pool if such, null otherwise.
-         */
-        @Nullable
-        public T acquire();
-
-        /**
-         * Release an instance to the pool.
-         *
-         * @param instance The instance to release.
-         * @return Whether the instance was put in the pool.
-         *
-         * @throws IllegalStateException If the instance is already in the pool.
-         */
-        public boolean release(@NonNull T instance);
-    }
-
-    private Pools() {
-        /* do nothing - hiding constructor */
-    }
-
-    /**
-     * Simple (non-synchronized) pool of objects.
-     *
-     * @param <T> The pooled type.
-     */
-    public static class SimplePool<T> implements Pool<T> {
-        private final Object[] mPool;
-
-        private int mPoolSize;
-
-        /**
-         * Creates a new instance.
-         *
-         * @param maxPoolSize The max pool size.
-         *
-         * @throws IllegalArgumentException If the max pool size is less than zero.
-         */
-        public SimplePool(int maxPoolSize) {
-            if (maxPoolSize <= 0) {
-                throw new IllegalArgumentException("The max pool size must be > 0");
-            }
-            mPool = new Object[maxPoolSize];
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public T acquire() {
-            if (mPoolSize > 0) {
-                final int lastPooledIndex = mPoolSize - 1;
-                T instance = (T) mPool[lastPooledIndex];
-                mPool[lastPooledIndex] = null;
-                mPoolSize--;
-                return instance;
-            }
-            return null;
-        }
-
-        @Override
-        public boolean release(@NonNull T instance) {
-            if (isInPool(instance)) {
-                throw new IllegalStateException("Already in the pool!");
-            }
-            if (mPoolSize < mPool.length) {
-                mPool[mPoolSize] = instance;
-                mPoolSize++;
-                return true;
-            }
-            return false;
-        }
-
-        private boolean isInPool(@NonNull T instance) {
-            for (int i = 0; i < mPoolSize; i++) {
-                if (mPool[i] == instance) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Synchronized) pool of objects.
-     *
-     * @param <T> The pooled type.
-     */
-    public static class SynchronizedPool<T> extends SimplePool<T> {
-        private final Object mLock = new Object();
-
-        /**
-         * Creates a new instance.
-         *
-         * @param maxPoolSize The max pool size.
-         *
-         * @throws IllegalArgumentException If the max pool size is less than zero.
-         */
-        public SynchronizedPool(int maxPoolSize) {
-            super(maxPoolSize);
-        }
-
-        @Override
-        public T acquire() {
-            synchronized (mLock) {
-                return super.acquire();
-            }
-        }
-
-        @Override
-        public boolean release(@NonNull T element) {
-            synchronized (mLock) {
-                return super.release(element);
-            }
-        }
-    }
-}
diff --git a/android/support/v4/util/Preconditions.java b/android/support/v4/util/Preconditions.java
deleted file mode 100644
index 711566a..0000000
--- a/android/support/v4/util/Preconditions.java
+++ /dev/null
@@ -1,464 +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.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/android/support/v4/util/SimpleArrayMap.java b/android/support/v4/util/SimpleArrayMap.java
deleted file mode 100644
index 06e68f0..0000000
--- a/android/support/v4/util/SimpleArrayMap.java
+++ /dev/null
@@ -1,697 +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.util;
-
-import android.util.Log;
-
-import java.util.ConcurrentModificationException;
-import java.util.Map;
-
-/**
- * Base implementation of {@link ArrayMap} that doesn't include any standard Java
- * container API interoperability.  These features are generally heavier-weight ways
- * to interact with the container, so discouraged, but they can be useful to make it
- * easier to use as a drop-in replacement for HashMap.  If you don't need them, this
- * class can be preferrable since it doesn't bring in any of the implementation of those
- * APIs, allowing that code to be stripped by ProGuard.
- */
-public class SimpleArrayMap<K, V> {
-    private static final boolean DEBUG = false;
-    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.
-     */
-    private static final int BASE_SIZE = 4;
-
-    /**
-     * Maximum number of entries to have in array caches.
-     */
-    private static final int CACHE_SIZE = 10;
-
-    /**
-     * Caches of small array objects to avoid spamming garbage.  The cache
-     * Object[] variable is a pointer to a linked list of array objects.
-     * The first entry in the array is a pointer to the next array in the
-     * list; the second entry is a pointer to the int[] hash code array for it.
-     */
-    static Object[] mBaseCache;
-    static int mBaseCacheSize;
-    static Object[] mTwiceBaseCache;
-    static int mTwiceBaseCacheSize;
-
-    int[] mHashes;
-    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;
-
-        // Important fast case: if nothing is in here, nothing to look for.
-        if (N == 0) {
-            return ~0;
-        }
-
-        int index = binarySearchHashes(mHashes, N, hash);
-
-        // If the hash code wasn't found, then we have no entry for this key.
-        if (index < 0) {
-            return index;
-        }
-
-        // If the key at the returned index matches, that's what we want.
-        if (key.equals(mArray[index<<1])) {
-            return index;
-        }
-
-        // Search for a matching key after the index.
-        int end;
-        for (end = index + 1; end < N && mHashes[end] == hash; end++) {
-            if (key.equals(mArray[end << 1])) return end;
-        }
-
-        // Search for a matching key before the index.
-        for (int i = index - 1; i >= 0 && mHashes[i] == hash; i--) {
-            if (key.equals(mArray[i << 1])) return i;
-        }
-
-        // Key not found -- return negative value indicating where a
-        // new entry for this key should go.  We use the end of the
-        // hash chain to reduce the number of array entries that will
-        // need to be copied when inserting.
-        return ~end;
-    }
-
-    int indexOfNull() {
-        final int N = mSize;
-
-        // Important fast case: if nothing is in here, nothing to look for.
-        if (N == 0) {
-            return ~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) {
-            return index;
-        }
-
-        // If the key at the returned index matches, that's what we want.
-        if (null == mArray[index<<1]) {
-            return index;
-        }
-
-        // Search for a matching key after the index.
-        int end;
-        for (end = index + 1; end < N && mHashes[end] == 0; end++) {
-            if (null == mArray[end << 1]) return end;
-        }
-
-        // Search for a matching key before the index.
-        for (int i = index - 1; i >= 0 && mHashes[i] == 0; i--) {
-            if (null == mArray[i << 1]) return i;
-        }
-
-        // Key not found -- return negative value indicating where a
-        // new entry for this key should go.  We use the end of the
-        // hash chain to reduce the number of array entries that will
-        // need to be copied when inserting.
-        return ~end;
-    }
-
-    @SuppressWarnings("ArrayToString")
-    private void allocArrays(final int size) {
-        if (size == (BASE_SIZE*2)) {
-            synchronized (ArrayMap.class) {
-                if (mTwiceBaseCache != null) {
-                    final Object[] array = mTwiceBaseCache;
-                    mArray = array;
-                    mTwiceBaseCache = (Object[])array[0];
-                    mHashes = (int[])array[1];
-                    array[0] = array[1] = null;
-                    mTwiceBaseCacheSize--;
-                    if (DEBUG) Log.d(TAG, "Retrieving 2x cache " + mHashes
-                            + " now have " + mTwiceBaseCacheSize + " entries");
-                    return;
-                }
-            }
-        } else if (size == BASE_SIZE) {
-            synchronized (ArrayMap.class) {
-                if (mBaseCache != null) {
-                    final Object[] array = mBaseCache;
-                    mArray = array;
-                    mBaseCache = (Object[])array[0];
-                    mHashes = (int[])array[1];
-                    array[0] = array[1] = null;
-                    mBaseCacheSize--;
-                    if (DEBUG) Log.d(TAG, "Retrieving 1x cache " + mHashes
-                            + " now have " + mBaseCacheSize + " entries");
-                    return;
-                }
-            }
-        }
-
-        mHashes = new int[size];
-        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) {
-                if (mTwiceBaseCacheSize < CACHE_SIZE) {
-                    array[0] = mTwiceBaseCache;
-                    array[1] = hashes;
-                    for (int i=(size<<1)-1; i>=2; i--) {
-                        array[i] = null;
-                    }
-                    mTwiceBaseCache = array;
-                    mTwiceBaseCacheSize++;
-                    if (DEBUG) Log.d(TAG, "Storing 2x cache " + array
-                            + " now have " + mTwiceBaseCacheSize + " entries");
-                }
-            }
-        } else if (hashes.length == BASE_SIZE) {
-            synchronized (ArrayMap.class) {
-                if (mBaseCacheSize < CACHE_SIZE) {
-                    array[0] = mBaseCache;
-                    array[1] = hashes;
-                    for (int i=(size<<1)-1; i>=2; i--) {
-                        array[i] = null;
-                    }
-                    mBaseCache = array;
-                    mBaseCacheSize++;
-                    if (DEBUG) Log.d(TAG, "Storing 1x cache " + array
-                            + " now have " + mBaseCacheSize + " entries");
-                }
-            }
-        }
-    }
-
-    /**
-     * Create a new empty ArrayMap.  The default capacity of an array map is 0, and
-     * will grow once items are added to it.
-     */
-    public SimpleArrayMap() {
-        mHashes = ContainerHelpers.EMPTY_INTS;
-        mArray = ContainerHelpers.EMPTY_OBJECTS;
-        mSize = 0;
-    }
-
-    /**
-     * Create a new ArrayMap with a given initial capacity.
-     */
-    public SimpleArrayMap(int capacity) {
-        if (capacity == 0) {
-            mHashes = ContainerHelpers.EMPTY_INTS;
-            mArray = ContainerHelpers.EMPTY_OBJECTS;
-        } else {
-            allocArrays(capacity);
-        }
-        mSize = 0;
-    }
-
-    /**
-     * Create a new ArrayMap with the mappings from the given ArrayMap.
-     */
-    public SimpleArrayMap(SimpleArrayMap<K, V> map) {
-        this();
-        if (map != null) {
-            putAll(map);
-        }
-    }
-
-    /**
-     * Make the array map empty.  All storage is released.
-     */
-    public void clear() {
-        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();
-        }
-    }
-
-    /**
-     * Ensure the array map can hold at least <var>minimumCapacity</var>
-     * 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, osize);
-                System.arraycopy(oarray, 0, mArray, 0, osize<<1);
-            }
-            freeArrays(ohashes, oarray, osize);
-        }
-        if (CONCURRENT_MODIFICATION_EXCEPTIONS && mSize != osize) {
-            throw new ConcurrentModificationException();
-        }
-    }
-
-    /**
-     * Check whether a key exists in the array.
-     *
-     * @param key The key to search for.
-     * @return Returns true if the key exists, else false.
-     */
-    public boolean containsKey(Object key) {
-        return indexOfKey(key) >= 0;
-    }
-
-    /**
-     * Returns the index of a key in the set.
-     *
-     * @param key The key to search for.
-     * @return Returns the index of the key if it exists, else a negative integer.
-     */
-    public int indexOfKey(Object key) {
-        return key == null ? indexOfNull() : indexOf(key, key.hashCode());
-    }
-
-    int indexOfValue(Object value) {
-        final int N = mSize*2;
-        final Object[] array = mArray;
-        if (value == null) {
-            for (int i=1; i<N; i+=2) {
-                if (array[i] == null) {
-                    return i>>1;
-                }
-            }
-        } else {
-            for (int i=1; i<N; i+=2) {
-                if (value.equals(array[i])) {
-                    return i>>1;
-                }
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Check whether a value exists in the array.  This requires a linear search
-     * through the entire array.
-     *
-     * @param value The value to search for.
-     * @return Returns true if the value exists, else false.
-     */
-    public boolean containsValue(Object value) {
-        return indexOfValue(value) >= 0;
-    }
-
-    /**
-     * Retrieve a value from the array.
-     * @param key The key of the value to retrieve.
-     * @return Returns the value associated with the given key,
-     * or null if there is no such key.
-     */
-    public V get(Object key) {
-        final int index = indexOfKey(key);
-        return index >= 0 ? (V)mArray[(index<<1)+1] : null;
-    }
-
-    /**
-     * Return the key at the given index in the array.
-     * @param index The desired index, must be between 0 and {@link #size()}-1.
-     * @return Returns the key stored at the given index.
-     */
-    public K keyAt(int index) {
-        return (K)mArray[index << 1];
-    }
-
-    /**
-     * Return the value at the given index in the array.
-     * @param index The desired index, must be between 0 and {@link #size()}-1.
-     * @return Returns the value stored at the given index.
-     */
-    public V valueAt(int index) {
-        return (V)mArray[(index << 1) + 1];
-    }
-
-    /**
-     * Set the value at a given index in the array.
-     * @param index The desired index, must be between 0 and {@link #size()}-1.
-     * @param value The new value to store at this index.
-     * @return Returns the previous value at the given index.
-     */
-    public V setValueAt(int index, V value) {
-        index = (index << 1) + 1;
-        V old = (V)mArray[index];
-        mArray[index] = value;
-        return old;
-    }
-
-    /**
-     * Return true if the array map contains no items.
-     */
-    public boolean isEmpty() {
-        return mSize <= 0;
-    }
-
-    /**
-     * Add a new value to the array map.
-     * @param key The key under which to store the value.  <b>Must not be null.</b>  If
-     * this key already exists in the array, its value will be replaced.
-     * @param value The value to store for the given key.
-     * @return Returns the old value that was stored for the given key, or null if there
-     * was no such key.
-     */
-    public V put(K key, V value) {
-        final int osize = mSize;
-        final int hash;
-        int index;
-        if (key == null) {
-            hash = 0;
-            index = indexOfNull();
-        } else {
-            hash = key.hashCode();
-            index = indexOf(key, hash);
-        }
-        if (index >= 0) {
-            index = (index<<1) + 1;
-            final V old = (V)mArray[index];
-            mArray[index] = value;
-            return old;
-        }
-
-        index = ~index;
-        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);
-
-            final int[] ohashes = mHashes;
-            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-" + osize + " to 0");
-                System.arraycopy(ohashes, 0, mHashes, 0, ohashes.length);
-                System.arraycopy(oarray, 0, mArray, 0, oarray.length);
-            }
-
-            freeArrays(ohashes, oarray, osize);
-        }
-
-        if (index < osize) {
-            if (DEBUG) Log.d(TAG, "put: move " + index + "-" + (osize-index)
-                    + " to " + (index+1));
-            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;
-        mSize++;
-        return null;
-    }
-
-    /**
-     * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>array</var>
-     * @param array The array whose contents are to be retrieved.
-     */
-    public void putAll(SimpleArrayMap<? extends K, ? extends V> array) {
-        final int N = array.mSize;
-        ensureCapacity(mSize + N);
-        if (mSize == 0) {
-            if (N > 0) {
-                System.arraycopy(array.mHashes, 0, mHashes, 0, N);
-                System.arraycopy(array.mArray, 0, mArray, 0, N<<1);
-                mSize = N;
-            }
-        } else {
-            for (int i=0; i<N; i++) {
-                put(array.keyAt(i), array.valueAt(i));
-            }
-        }
-    }
-
-    /**
-     * Remove an existing key from the array map.
-     * @param key The key of the mapping to remove.
-     * @return Returns the value that was stored under the key, or null if there
-     * was no such key.
-     */
-    public V remove(Object key) {
-        final int index = indexOfKey(key);
-        if (index >= 0) {
-            return removeAt(index);
-        }
-
-        return null;
-    }
-
-    /**
-     * Remove the key/value mapping at the given index.
-     * @param index The desired index, must be between 0 and {@link #size()}-1.
-     * @return Returns the value that was stored at this index.
-     */
-    public V removeAt(int index) {
-        final Object old = mArray[(index << 1) + 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, osize);
-            mHashes = ContainerHelpers.EMPTY_INTS;
-            mArray = ContainerHelpers.EMPTY_OBJECTS;
-            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 = osize > (BASE_SIZE*2) ? (osize + (osize>>1)) : (BASE_SIZE*2);
-
-                if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to " + n);
-
-                final int[] ohashes = mHashes;
-                final Object[] oarray = mArray;
-                allocArrays(n);
-
-                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 < nsize) {
-                    if (DEBUG) Log.d(TAG, "remove: copy from " + (index+1) + "-" + nsize
-                            + " to " + index);
-                    System.arraycopy(ohashes, index + 1, mHashes, index, nsize - index);
-                    System.arraycopy(oarray, (index + 1) << 1, mArray, index << 1,
-                            (nsize - index) << 1);
-                }
-            } else {
-                if (index < nsize) {
-                    if (DEBUG) Log.d(TAG, "remove: move " + (index+1) + "-" + nsize
-                            + " to " + index);
-                    System.arraycopy(mHashes, index + 1, mHashes, index, nsize - index);
-                    System.arraycopy(mArray, (index + 1) << 1, mArray, index << 1,
-                            (nsize - index) << 1);
-                }
-                mArray[nsize << 1] = null;
-                mArray[(nsize << 1) + 1] = null;
-            }
-        }
-        if (CONCURRENT_MODIFICATION_EXCEPTIONS && osize != mSize) {
-            throw new ConcurrentModificationException();
-        }
-        mSize = nsize;
-        return (V)old;
-    }
-
-    /**
-     * Return the number of items in this array map.
-     */
-    public int size() {
-        return mSize;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation returns false if the object is not a Map or
-     * SimpleArrayMap, or if the maps have different sizes. Otherwise, for each
-     * key in this map, values of both maps are compared. If the values for any
-     * key are not equal, the method returns false, otherwise it returns true.
-     */
-    @Override
-    public boolean equals(Object object) {
-        if (this == object) {
-            return true;
-        }
-        if (object instanceof SimpleArrayMap) {
-            SimpleArrayMap<?, ?> map = (SimpleArrayMap<?, ?>) object;
-            if (size() != map.size()) {
-                return false;
-            }
-
-            try {
-                for (int i=0; i<mSize; i++) {
-                    K key = keyAt(i);
-                    V mine = valueAt(i);
-                    Object theirs = map.get(key);
-                    if (mine == null) {
-                        if (theirs != null || !map.containsKey(key)) {
-                            return false;
-                        }
-                    } else if (!mine.equals(theirs)) {
-                        return false;
-                    }
-                }
-            } catch (NullPointerException ignored) {
-                return false;
-            } catch (ClassCastException ignored) {
-                return false;
-            }
-            return true;
-        } else if (object instanceof Map) {
-            Map<?, ?> map = (Map<?, ?>) object;
-            if (size() != map.size()) {
-                return false;
-            }
-
-            try {
-                for (int i=0; i<mSize; i++) {
-                    K key = keyAt(i);
-                    V mine = valueAt(i);
-                    Object theirs = map.get(key);
-                    if (mine == null) {
-                        if (theirs != null || !map.containsKey(key)) {
-                            return false;
-                        }
-                    } else if (!mine.equals(theirs)) {
-                        return false;
-                    }
-                }
-            } catch (NullPointerException ignored) {
-                return false;
-            } catch (ClassCastException ignored) {
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int hashCode() {
-        final int[] hashes = mHashes;
-        final Object[] array = mArray;
-        int result = 0;
-        for (int i = 0, v = 1, s = mSize; i < s; i++, v+=2) {
-            Object value = array[v];
-            result += hashes[i] ^ (value == null ? 0 : value.hashCode());
-        }
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation composes a string by iterating over its mappings. If
-     * this map contains itself as a key or a value, the string "(this Map)"
-     * will appear in its place.
-     */
-    @Override
-    public String toString() {
-        if (isEmpty()) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 28);
-        buffer.append('{');
-        for (int i=0; i<mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            Object key = keyAt(i);
-            if (key != this) {
-                buffer.append(key);
-            } else {
-                buffer.append("(this Map)");
-            }
-            buffer.append('=');
-            Object value = valueAt(i);
-            if (value != this) {
-                buffer.append(value);
-            } else {
-                buffer.append("(this Map)");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-}
diff --git a/android/support/v4/util/SparseArrayCompat.java b/android/support/v4/util/SparseArrayCompat.java
deleted file mode 100644
index 5238cf0..0000000
--- a/android/support/v4/util/SparseArrayCompat.java
+++ /dev/null
@@ -1,394 +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.util;
-
-/**
- * A copy of the current platform (currently {@link android.os.Build.VERSION_CODES#KITKAT}
- * version of {@link android.util.SparseArray}; provides a removeAt() method and other things.
- */
-public class SparseArrayCompat<E> implements Cloneable {
-    private static final Object DELETED = new Object();
-    private boolean mGarbage = false;
-
-    private int[] mKeys;
-    private Object[] mValues;
-    private int mSize;
-
-    /**
-     * Creates a new SparseArray containing no mappings.
-     */
-    public SparseArrayCompat() {
-        this(10);
-    }
-
-    /**
-     * Creates a new SparseArray containing no mappings that will not
-     * require any additional memory allocation to store the specified
-     * number of mappings.  If you supply an initial capacity of 0, the
-     * sparse array will be initialized with a light-weight representation
-     * not requiring any additional array allocations.
-     */
-    public SparseArrayCompat(int initialCapacity) {
-        if (initialCapacity == 0) {
-            mKeys =  ContainerHelpers.EMPTY_INTS;
-            mValues =  ContainerHelpers.EMPTY_OBJECTS;
-        } else {
-            initialCapacity =  ContainerHelpers.idealIntArraySize(initialCapacity);
-            mKeys = new int[initialCapacity];
-            mValues = new Object[initialCapacity];
-        }
-        mSize = 0;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public SparseArrayCompat<E> clone() {
-        SparseArrayCompat<E> clone = null;
-        try {
-            clone = (SparseArrayCompat<E>) super.clone();
-            clone.mKeys = mKeys.clone();
-            clone.mValues = mValues.clone();
-        } catch (CloneNotSupportedException cnse) {
-            /* ignore */
-        }
-        return clone;
-    }
-
-    /**
-     * Gets the Object mapped from the specified key, or <code>null</code>
-     * if no such mapping has been made.
-     */
-    public E get(int key) {
-        return get(key, null);
-    }
-
-    /**
-     * Gets the Object mapped from the specified key, or the specified Object
-     * if no such mapping has been made.
-     */
-    @SuppressWarnings("unchecked")
-    public E get(int key, E valueIfKeyNotFound) {
-        int i =  ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i < 0 || mValues[i] == DELETED) {
-            return valueIfKeyNotFound;
-        } else {
-            return (E) mValues[i];
-        }
-    }
-
-    /**
-     * Removes the mapping from the specified key, if there was any.
-     */
-    public void delete(int key) {
-        int i =  ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i >= 0) {
-            if (mValues[i] != DELETED) {
-                mValues[i] = DELETED;
-                mGarbage = true;
-            }
-        }
-    }
-
-    /**
-     * Alias for {@link #delete(int)}.
-     */
-    public void remove(int key) {
-        delete(key);
-    }
-
-    /**
-     * Removes the mapping at the specified index.
-     */
-    public void removeAt(int index) {
-        if (mValues[index] != DELETED) {
-            mValues[index] = DELETED;
-            mGarbage = true;
-        }
-    }
-
-    /**
-     * Remove a range of mappings as a batch.
-     *
-     * @param index Index to begin at
-     * @param size Number of mappings to remove
-     */
-    public void removeAtRange(int index, int size) {
-        final int end = Math.min(mSize, index + size);
-        for (int i = index; i < end; i++) {
-            removeAt(i);
-        }
-    }
-
-    private void gc() {
-        // Log.e("SparseArray", "gc start with " + mSize);
-
-        int n = mSize;
-        int o = 0;
-        int[] keys = mKeys;
-        Object[] values = mValues;
-
-        for (int i = 0; i < n; i++) {
-            Object val = values[i];
-
-            if (val != DELETED) {
-                if (i != o) {
-                    keys[o] = keys[i];
-                    values[o] = val;
-                    values[i] = null;
-                }
-
-                o++;
-            }
-        }
-
-        mGarbage = false;
-        mSize = o;
-
-        // Log.e("SparseArray", "gc end with " + mSize);
-    }
-
-    /**
-     * Adds a mapping from the specified key to the specified value,
-     * replacing the previous mapping from the specified key if there
-     * was one.
-     */
-    public void put(int key, E value) {
-        int i =  ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i >= 0) {
-            mValues[i] = value;
-        } else {
-            i = ~i;
-
-            if (i < mSize && mValues[i] == DELETED) {
-                mKeys[i] = key;
-                mValues[i] = value;
-                return;
-            }
-
-            if (mGarbage && mSize >= mKeys.length) {
-                gc();
-
-                // Search again because indices may have changed.
-                i = ~ ContainerHelpers.binarySearch(mKeys, mSize, key);
-            }
-
-            if (mSize >= mKeys.length) {
-                int n =  ContainerHelpers.idealIntArraySize(mSize + 1);
-
-                int[] nkeys = new int[n];
-                Object[] nvalues = new Object[n];
-
-                // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
-                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
-                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
-                mKeys = nkeys;
-                mValues = nvalues;
-            }
-
-            if (mSize - i != 0) {
-                // Log.e("SparseArray", "move " + (mSize - i));
-                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
-                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
-            }
-
-            mKeys[i] = key;
-            mValues[i] = value;
-            mSize++;
-        }
-    }
-
-    /**
-     * Returns the number of key-value mappings that this SparseArray
-     * currently stores.
-     */
-    public int size() {
-        if (mGarbage) {
-            gc();
-        }
-
-        return mSize;
-    }
-
-    /**
-     * Return true if size() is 0.
-     * @return true if size() is 0.
-     */
-    public boolean isEmpty() {
-        return size() == 0;
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, returns
-     * the key from the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
-     */
-    public int keyAt(int index) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return mKeys[index];
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, returns
-     * the value from the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
-     */
-    @SuppressWarnings("unchecked")
-    public E valueAt(int index) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return (E) mValues[index];
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, sets a new
-     * value for the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
-     */
-    public void setValueAt(int index, E value) {
-        if (mGarbage) {
-            gc();
-        }
-
-        mValues[index] = value;
-    }
-
-    /**
-     * Returns the index for which {@link #keyAt} would return the
-     * specified key, or a negative number if the specified
-     * key is not mapped.
-     */
-    public int indexOfKey(int key) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return  ContainerHelpers.binarySearch(mKeys, mSize, key);
-    }
-
-    /**
-     * Returns an index for which {@link #valueAt} would return the
-     * specified key, or a negative number if no keys map to the
-     * specified value.
-     * <p>Beware that this is a linear search, unlike lookups by key,
-     * and that multiple keys can map to the same value and this will
-     * find only one of them.
-     * <p>Note also that unlike most collections' {@code indexOf} methods,
-     * this method compares values using {@code ==} rather than {@code equals}.
-     */
-    public int indexOfValue(E value) {
-        if (mGarbage) {
-            gc();
-        }
-
-        for (int i = 0; i < mSize; i++)
-            if (mValues[i] == value)
-                return i;
-
-        return -1;
-    }
-
-    /**
-     * Removes all key-value mappings from this SparseArray.
-     */
-    public void clear() {
-        int n = mSize;
-        Object[] values = mValues;
-
-        for (int i = 0; i < n; i++) {
-            values[i] = null;
-        }
-
-        mSize = 0;
-        mGarbage = false;
-    }
-
-    /**
-     * Puts a key/value pair into the array, optimizing for the case where
-     * the key is greater than all existing keys in the array.
-     */
-    public void append(int key, E value) {
-        if (mSize != 0 && key <= mKeys[mSize - 1]) {
-            put(key, value);
-            return;
-        }
-
-        if (mGarbage && mSize >= mKeys.length) {
-            gc();
-        }
-
-        int pos = mSize;
-        if (pos >= mKeys.length) {
-            int n =  ContainerHelpers.idealIntArraySize(pos + 1);
-
-            int[] nkeys = new int[n];
-            Object[] nvalues = new Object[n];
-
-            // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
-            System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
-            System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
-            mKeys = nkeys;
-            mValues = nvalues;
-        }
-
-        mKeys[pos] = key;
-        mValues[pos] = value;
-        mSize = pos + 1;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation composes a string by iterating over its mappings. If
-     * this map contains itself as a value, the string "(this Map)"
-     * will appear in its place.
-     */
-    @Override
-    public String toString() {
-        if (size() <= 0) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 28);
-        buffer.append('{');
-        for (int i=0; i<mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            int key = keyAt(i);
-            buffer.append(key);
-            buffer.append('=');
-            Object value = valueAt(i);
-            if (value != this) {
-                buffer.append(value);
-            } else {
-                buffer.append("(this Map)");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-}
diff --git a/android/support/v4/util/TimeUtils.java b/android/support/v4/util/TimeUtils.java
deleted file mode 100644
index 2d1ce53..0000000
--- a/android/support/v4/util/TimeUtils.java
+++ /dev/null
@@ -1,186 +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.util;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-import java.io.PrintWriter;
-
-/**
- * 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;
-    private static final int SECONDS_PER_HOUR = 60 * 60;
-    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
-
-    private static final Object sFormatSync = new Object();
-    private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
-
-    static private int accumField(int amt, int suffix, boolean always, int zeropad) {
-        if (amt > 99 || (always && zeropad >= 3)) {
-            return 3+suffix;
-        }
-        if (amt > 9 || (always && zeropad >= 2)) {
-            return 2+suffix;
-        }
-        if (always || amt > 0) {
-            return 1+suffix;
-        }
-        return 0;
-    }
-
-    static private int printField(char[] formatStr, int amt, char suffix, int pos,
-            boolean always, int zeropad) {
-        if (always || amt > 0) {
-            final int startPos = pos;
-            if ((always && zeropad >= 3) || amt > 99) {
-                int dig = amt/100;
-                formatStr[pos] = (char)(dig + '0');
-                pos++;
-                amt -= (dig*100);
-            }
-            if ((always && zeropad >= 2) || amt > 9 || startPos != pos) {
-                int dig = amt/10;
-                formatStr[pos] = (char)(dig + '0');
-                pos++;
-                amt -= (dig*10);
-            }
-            formatStr[pos] = (char)(amt + '0');
-            pos++;
-            formatStr[pos] = suffix;
-            pos++;
-        }
-        return pos;
-    }
-
-    private static int formatDurationLocked(long duration, int fieldLen) {
-        if (sFormatStr.length < fieldLen) {
-            sFormatStr = new char[fieldLen];
-        }
-
-        char[] formatStr = sFormatStr;
-
-        if (duration == 0) {
-            int pos = 0;
-            fieldLen -= 1;
-            while (pos < fieldLen) {
-                formatStr[pos] = ' ';
-            }
-            formatStr[pos] = '0';
-            return pos+1;
-        }
-
-        char prefix;
-        if (duration > 0) {
-            prefix = '+';
-        } else {
-            prefix = '-';
-            duration = -duration;
-        }
-
-        int millis = (int)(duration%1000);
-        int seconds = (int) Math.floor(duration / 1000);
-        int days = 0, hours = 0, minutes = 0;
-
-        if (seconds > SECONDS_PER_DAY) {
-            days = seconds / SECONDS_PER_DAY;
-            seconds -= days * SECONDS_PER_DAY;
-        }
-        if (seconds > SECONDS_PER_HOUR) {
-            hours = seconds / SECONDS_PER_HOUR;
-            seconds -= hours * SECONDS_PER_HOUR;
-        }
-        if (seconds > SECONDS_PER_MINUTE) {
-            minutes = seconds / SECONDS_PER_MINUTE;
-            seconds -= minutes * SECONDS_PER_MINUTE;
-        }
-
-        int pos = 0;
-
-        if (fieldLen != 0) {
-            int myLen = accumField(days, 1, false, 0);
-            myLen += accumField(hours, 1, myLen > 0, 2);
-            myLen += accumField(minutes, 1, myLen > 0, 2);
-            myLen += accumField(seconds, 1, myLen > 0, 2);
-            myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
-            while (myLen < fieldLen) {
-                formatStr[pos] = ' ';
-                pos++;
-                myLen++;
-            }
-        }
-
-        formatStr[pos] = prefix;
-        pos++;
-
-        int start = pos;
-        boolean zeropad = fieldLen != 0;
-        pos = printField(formatStr, days, 'd', pos, false, 0);
-        pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
-        pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
-        pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
-        pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
-        formatStr[pos] = 's';
-        return pos + 1;
-    }
-
-    /** @hide Just for debugging; not internationalized. */
-    @RestrictTo(LIBRARY_GROUP)
-    public static void formatDuration(long duration, StringBuilder builder) {
-        synchronized (sFormatSync) {
-            int len = formatDurationLocked(duration, 0);
-            builder.append(sFormatStr, 0, len);
-        }
-    }
-
-    /** @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);
-            pw.print(new String(sFormatStr, 0, len));
-        }
-    }
-
-    /** @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("--");
-            return;
-        }
-        formatDuration(time-now, pw, 0);
-    }
-
-    private TimeUtils() {}
-}
diff --git a/android/support/v4/view/AbsSavedState.java b/android/support/v4/view/AbsSavedState.java
deleted file mode 100644
index 86f491f..0000000
--- a/android/support/v4/view/AbsSavedState.java
+++ /dev/null
@@ -1,107 +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.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-/**
- * A {@link Parcelable} implementation that should be used by inheritance
- * hierarchies to ensure the state of all classes along the chain is saved.
- */
-public abstract class AbsSavedState implements Parcelable {
-    public static final AbsSavedState EMPTY_STATE = new AbsSavedState() {};
-
-    private final Parcelable mSuperState;
-
-    /**
-     * Constructor used to make the EMPTY_STATE singleton
-     */
-    private AbsSavedState() {
-        mSuperState = null;
-    }
-
-    /**
-     * Constructor called by derived classes when creating their SavedState objects
-     *
-     * @param superState The state of the superclass of this view
-     */
-    protected AbsSavedState(@NonNull Parcelable superState) {
-        if (superState == null) {
-            throw new IllegalArgumentException("superState must not be null");
-        }
-        mSuperState = superState != EMPTY_STATE ? superState : null;
-    }
-
-    /**
-     * Constructor used when reading from a parcel. Reads the state of the superclass.
-     *
-     * @param source parcel to read from
-     */
-    protected AbsSavedState(@NonNull Parcel source) {
-        this(source, null);
-    }
-
-    /**
-     * Constructor used when reading from a parcel. Reads the state of the superclass.
-     *
-     * @param source parcel to read from
-     * @param loader ClassLoader to use for reading
-     */
-    protected AbsSavedState(@NonNull Parcel source, @Nullable ClassLoader loader) {
-        Parcelable superState = source.readParcelable(loader);
-        mSuperState = superState != null ? superState : EMPTY_STATE;
-    }
-
-    @Nullable
-    public final Parcelable getSuperState() {
-        return mSuperState;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mSuperState, flags);
-    }
-
-    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 createFromParcel(Parcel in) {
-            return createFromParcel(in, null);
-        }
-
-        @Override
-        public AbsSavedState[] newArray(int size) {
-            return new AbsSavedState[size];
-        }
-    };
-}
diff --git a/android/support/v4/view/AccessibilityDelegateCompat.java b/android/support/v4/view/AccessibilityDelegateCompat.java
deleted file mode 100644
index 66b2cef..0000000
--- a/android/support/v4/view/AccessibilityDelegateCompat.java
+++ /dev/null
@@ -1,393 +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.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 AccessibilityDelegate}.
- * <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>
- * host methods. This prevents certain properties such as class name from
- * being modified by overriding
- * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
- * as any changes will be overwritten by the host class.
- * <p>
- * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
- * methods are called <i>after</i> host methods, which all properties to be
- * modified without being overwritten by the host class.
- */
-public class AccessibilityDelegateCompat {
-
-    static class AccessibilityDelegateBaseImpl {
-        public AccessibilityDelegate newAccessibilityDelegateBridge(
-                final AccessibilityDelegateCompat compat) {
-            return new AccessibilityDelegate() {
-                @Override
-                public boolean dispatchPopulateAccessibilityEvent(View host,
-                        AccessibilityEvent event) {
-                    return compat.dispatchPopulateAccessibilityEvent(host, event);
-                }
-
-                @Override
-                public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-                    compat.onInitializeAccessibilityEvent(host, event);
-                }
-
-                @Override
-                public void onInitializeAccessibilityNodeInfo(
-                        View host, AccessibilityNodeInfo info) {
-                    compat.onInitializeAccessibilityNodeInfo(host,
-                            AccessibilityNodeInfoCompat.wrap(info));
-                }
-
-                @Override
-                public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-                    compat.onPopulateAccessibilityEvent(host, event);
-                }
-
-                @Override
-                public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-                        AccessibilityEvent event) {
-                    return compat.onRequestSendAccessibilityEvent(host, child, event);
-                }
-
-                @Override
-                public void sendAccessibilityEvent(View host, int eventType) {
-                    compat.sendAccessibilityEvent(host, eventType);
-                }
-
-                @Override
-                public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-                    compat.sendAccessibilityEventUnchecked(host, event);
-                }
-            };
-        }
-
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(
-                AccessibilityDelegate delegate, View host) {
-            // Do nothing. Added in API 16.
-            return null;
-        }
-
-        public boolean performAccessibilityAction(AccessibilityDelegate delegate, View host,
-                int action, Bundle args) {
-            // Do nothing. Added in API 16.
-            return false;
-        }
-    }
-
-    @RequiresApi(16)
-    static class AccessibilityDelegateApi16Impl extends AccessibilityDelegateBaseImpl {
-        @Override
-        public AccessibilityDelegate newAccessibilityDelegateBridge(
-                final AccessibilityDelegateCompat compat) {
-            return new AccessibilityDelegate()  {
-                @Override
-                public boolean dispatchPopulateAccessibilityEvent(View host,
-                        AccessibilityEvent event) {
-                    return compat.dispatchPopulateAccessibilityEvent(host, event);
-                }
-
-                @Override
-                public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-                    compat.onInitializeAccessibilityEvent(host, event);
-                }
-
-                @Override
-                public void onInitializeAccessibilityNodeInfo(
-                        View host, AccessibilityNodeInfo info) {
-                    compat.onInitializeAccessibilityNodeInfo(host,
-                            AccessibilityNodeInfoCompat.wrap(info));
-                }
-
-                @Override
-                public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-                    compat.onPopulateAccessibilityEvent(host, event);
-                }
-
-                @Override
-                public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-                        AccessibilityEvent event) {
-                    return compat.onRequestSendAccessibilityEvent(host, child, event);
-                }
-
-                @Override
-                public void sendAccessibilityEvent(View host, int eventType) {
-                    compat.sendAccessibilityEvent(host, eventType);
-                }
-
-                @Override
-                public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-                    compat.sendAccessibilityEventUnchecked(host, event);
-                }
-
-                @Override
-                public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
-                    AccessibilityNodeProviderCompat provider =
-                        compat.getAccessibilityNodeProvider(host);
-                    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(
-                AccessibilityDelegate delegate, View host) {
-            Object provider = delegate.getAccessibilityNodeProvider(host);
-            if (provider != null) {
-                return new AccessibilityNodeProviderCompat(provider);
-            }
-            return null;
-        }
-
-        @Override
-        public boolean performAccessibilityAction(AccessibilityDelegate delegate, View host,
-                int action, Bundle args) {
-            return delegate.performAccessibilityAction(host, action, args);
-        }
-    }
-
-    private static final AccessibilityDelegateBaseImpl IMPL;
-    private static final AccessibilityDelegate DEFAULT_DELEGATE;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 16) { // JellyBean
-            IMPL = new AccessibilityDelegateApi16Impl();
-        } else {
-            IMPL = new AccessibilityDelegateBaseImpl();
-        }
-        DEFAULT_DELEGATE = new AccessibilityDelegate();
-    }
-
-    final AccessibilityDelegate mBridge;
-
-    /**
-     * Creates a new instance.
-     */
-    public AccessibilityDelegateCompat() {
-        mBridge = IMPL.newAccessibilityDelegateBridge(this);
-    }
-
-    /**
-     * @return The wrapped bridge implementation.
-     */
-    AccessibilityDelegate getBridge() {
-        return mBridge;
-    }
-
-    /**
-     * Sends an accessibility event of the given type. If accessibility is not
-     * enabled this method has no effect.
-     * <p>
-     * The default implementation behaves as {@link View#sendAccessibilityEvent(int)
-     * View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
-     * been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param eventType The type of the event to send.
-     *
-     * @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
-     */
-    public void sendAccessibilityEvent(View host, int eventType) {
-        DEFAULT_DELEGATE.sendAccessibilityEvent(host, eventType);
-    }
-
-    /**
-     * Sends an accessibility event. This method behaves exactly as
-     * {@link #sendAccessibilityEvent(View, int)} but takes as an argument an
-     * empty {@link AccessibilityEvent} and does not perform a check whether
-     * accessibility is enabled.
-     * <p>
-     * The default implementation behaves as
-     * {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)
-     * View#sendAccessibilityEventUnchecked(AccessibilityEvent)} for
-     * the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param event The event to send.
-     *
-     * @see View#sendAccessibilityEventUnchecked(AccessibilityEvent)
-     *      View#sendAccessibilityEventUnchecked(AccessibilityEvent)
-     */
-    public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-        DEFAULT_DELEGATE.sendAccessibilityEventUnchecked(host, event);
-    }
-
-    /**
-     * Dispatches an {@link AccessibilityEvent} to the host {@link View} first and then
-     * to its children for adding their text content to the event.
-     * <p>
-     * The default implementation behaves as
-     * {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
-     * View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} for
-     * the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param event The event.
-     * @return True if the event population was completed.
-     *
-     * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
-     *      View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
-     */
-    public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-        return DEFAULT_DELEGATE.dispatchPopulateAccessibilityEvent(host, event);
-    }
-
-    /**
-     * Gives a chance to the host View to populate the accessibility event with its
-     * text content.
-     * <p>
-     * The default implementation behaves as
-     * {@link ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
-     * ViewCompat#onPopulateAccessibilityEvent(AccessibilityEvent)} for
-     * the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param event The accessibility event which to populate.
-     *
-     * @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent)
-     *      ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
-     */
-    public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-        DEFAULT_DELEGATE.onPopulateAccessibilityEvent(host, event);
-    }
-
-    /**
-     * Initializes an {@link AccessibilityEvent} with information about the
-     * the host View which is the event source.
-     * <p>
-     * The default implementation behaves as
-     * {@link ViewCompat#onInitializeAccessibilityEvent(View v, AccessibilityEvent event)
-     * ViewCompat#onInitalizeAccessibilityEvent(View v, AccessibilityEvent event)} for
-     * the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param event The event to initialize.
-     *
-     * @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
-     *      ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
-     */
-    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-        DEFAULT_DELEGATE.onInitializeAccessibilityEvent(host, event);
-    }
-
-    /**
-     * Initializes an {@link AccessibilityNodeInfoCompat} with information about the host view.
-     * <p>
-     * The default implementation behaves as
-     * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
-     * ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)} for
-     * the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param info The instance to initialize.
-     *
-     * @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
-     *      ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
-     */
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-        DEFAULT_DELEGATE.onInitializeAccessibilityNodeInfo(
-                host, info.unwrap());
-    }
-
-    /**
-     * Called when a child of the host View has requested sending an
-     * {@link AccessibilityEvent} and gives an opportunity to the parent (the host)
-     * to augment the event.
-     * <p>
-     * The default implementation behaves as
-     * {@link ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
-     * ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)} for
-     * the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @param host The View hosting the delegate.
-     * @param child The child which requests sending the event.
-     * @param event The event to be sent.
-     * @return True if the event should be sent
-     *
-     * @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
-     *      ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
-     */
-    public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-            AccessibilityEvent event) {
-        return DEFAULT_DELEGATE.onRequestSendAccessibilityEvent(host, child, event);
-    }
-
-    /**
-     * Gets the provider for managing a virtual view hierarchy rooted at this View
-     * and reported to {@link android.accessibilityservice.AccessibilityService}s
-     * that explore the window content.
-     * <p>
-     * The default implementation behaves as
-     * {@link ViewCompat#getAccessibilityNodeProvider(View) ViewCompat#getAccessibilityNodeProvider(View)}
-     * for the case of no accessibility delegate been set.
-     * </p>
-     *
-     * @return The provider.
-     *
-     * @see AccessibilityNodeProviderCompat
-     */
-    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
-        return IMPL.getAccessibilityNodeProvider(DEFAULT_DELEGATE, host);
-    }
-
-    /**
-     * Performs the specified accessibility action on the view. For
-     * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
-     * <p>
-     * The default implementation behaves as
-     * {@link View#performAccessibilityAction(int, Bundle)
-     *  View#performAccessibilityAction(int, Bundle)} for the case of
-     *  no accessibility delegate been set.
-     * </p>
-     *
-     * @param action The action to perform.
-     * @return Whether the action was performed.
-     *
-     * @see View#performAccessibilityAction(int, Bundle)
-     *      View#performAccessibilityAction(int, Bundle)
-     */
-    public boolean performAccessibilityAction(View host, int action, Bundle args) {
-        return IMPL.performAccessibilityAction(DEFAULT_DELEGATE, host, action, args);
-    }
-}
diff --git a/android/support/v4/view/ActionProvider.java b/android/support/v4/view/ActionProvider.java
deleted file mode 100644
index e6ffd98..0000000
--- a/android/support/v4/view/ActionProvider.java
+++ /dev/null
@@ -1,333 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-
-/**
- * This class is a mediator for accomplishing a given task, for example sharing a file. It is
- * responsible for creating a view that performs an action that accomplishes the task. This class
- * also implements other functions such a performing a default action.
- *
- * <p class="note"><strong>Note:</strong> This class is included in the <a
- * href="{@docRoot}tools/extras/support-library.html">support library</a> for compatibility
- * with API level 4 and higher. If you're developing your app for API level 14 and higher
- * <em>only</em>, you should instead use the framework {@link android.view.ActionProvider}
- * class.</p>
- *
- * <p>An ActionProvider can be
- * optionally specified for a {@link android.view.MenuItem} and in such a case it will be
- * responsible for
- * creating the action view that appears in the {@link android.app.ActionBar} as a substitute for
- * the menu item when the item is displayed as an action item. Also the provider is responsible for
- * performing a default action if a menu item placed on the overflow menu of the ActionBar is
- * selected and none of the menu item callbacks has handled the selection. For this case the
- * provider can also optionally provide a sub-menu for accomplishing the task at hand.
- *
- * <p>There are two ways for using an action provider for creating and handling of action views:
- *
- * <ul><li> Setting the action provider on a {@link android.view.MenuItem} directly by
- * calling {@link
- * android.support.v4.view.MenuItemCompat#setActionProvider(android.view.MenuItem, ActionProvider)}.
- * </li>
- *
- * <li>Declaring the action provider in the menu XML resource. For example:
- *
- * <pre><code>
- *   &lt;item android:id="@+id/my_menu_item"
- *     android:title="@string/my_menu_item_title"
- *     android:icon="@drawable/my_menu_item_icon"
- *     android:showAsAction="ifRoom"
- *     android:actionProviderClass="foo.bar.SomeActionProvider" /&gt;
- * </code></pre>
- * </li></ul></p>
- *
- * <h3>Creating a custom action provider</h3>
- *
- * <p>To create a custom action provider, extend ActionProvider and implement
- * its callback methods as necessary. In particular, implement the following
- * methods:</p>
- *
- * <dl>
- * <dt>{@link #ActionProvider ActionProvider()} constructor</dt>
- * <dd>This constructor is passed the application context. You should
- * save the context in a member field to use in the other callback methods.</dd>
- *
- * <dt>{@link #onCreateActionView onCreateActionView(MenuItem)}</dt>
- * <dd>The system calls this method when the action provider is created.
- * You define the action provider's layout through the implementation of this
- * method. Use the context acquired
- * from the constructor to instantiate a {@link android.view.LayoutInflater} and
- * inflate your action provider's layout from an XML resource, then hook up
- * event listeners for the view's components. For example:
- *
- *<pre>
- * public View onCreateActionView(MenuItem forItem) {
- *     // Inflate the action provider to be shown on the action bar.
- *     LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- *     View providerView =
- *         layoutInflater.inflate(R.layout.my_action_provider, null);
- *     ImageButton button =
- *         (ImageButton) providerView.findViewById(R.id.button);
- *     button.setOnClickListener(new View.OnClickListener() {
- *         &#64;Override
- *         public void onClick(View v) {
- *             // Do something...
- *         }
- *     });
- *     return providerView;
- * }</pre>
- * </dd>
- *
- * <dt>{@link #onPerformDefaultAction onPerformDefaultAction()}</dt>
- * <dd><p>The system calls this method when the user selects a menu item from the action
- * overflow. The action provider should perform a default action for the
- * menu item. The system does not call this method if the menu item opens a submenu.</p>
- *
- * <p>If your action provider presents a submenu through the
- * {@link #onPrepareSubMenu onPrepareSubMenu()} callback, the submenu
- * appears even if the action provider is in the overflow menu.
- * Thus, the system never calls {@link #onPerformDefaultAction
- * onPerformDefaultAction()} if there is a submenu.</p>
- *
- * <p class="note"> <strong>Note:</strong> An activity or a fragment that
- * implements <code>onOptionsItemSelected()</code> can override the action
- * provider's default behavior (unless it uses a submenu) by handling the
- * item-selected event and returning <code>true</code>. In this case, the
- * system does not call
- * {@link #onPerformDefaultAction onPerformDefaultAction()}.</p></dd>
- * </dl>
- *
- *
- * @see android.support.v4.view.MenuItemCompat#setActionProvider(android.view.MenuItem, ActionProvider)
- * @see android.support.v4.view.MenuItemCompat#getActionProvider(android.view.MenuItem)
- */
-public abstract class ActionProvider {
-    private static final String TAG = "ActionProvider(support)";
-    private final Context mContext;
-
-    private SubUiVisibilityListener mSubUiVisibilityListener;
-    private VisibilityListener mVisibilityListener;
-
-    /**
-     * Creates a new instance.
-     *
-     * @param context Context for accessing resources.
-     */
-    public ActionProvider(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Gets the context associated with this action provider.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Factory method for creating new action views.
-     *
-     * @return A new action view.
-     */
-    public abstract View onCreateActionView();
-
-    /**
-     * Factory method called by the Android framework to create new action views.
-     * This method returns a new action view for the given MenuItem.
-     *
-     * <p>If your ActionProvider implementation overrides the deprecated no-argument overload
-     * {@link #onCreateActionView()}, overriding this method for devices running API 16 or later
-     * is recommended but optional. The default implementation calls {@link #onCreateActionView()}
-     * for compatibility with applications written for older platform versions.</p>
-     *
-     * @param forItem MenuItem to create the action view for
-     * @return the new action view
-     */
-    public View onCreateActionView(MenuItem forItem) {
-        return onCreateActionView();
-    }
-
-    /**
-     * The result of this method determines whether or not {@link #isVisible()} will be used
-     * by the {@link MenuItem} this ActionProvider is bound to help determine its visibility.
-     *
-     * @return true if this ActionProvider overrides the visibility of the MenuItem
-     *         it is bound to, false otherwise. The default implementation returns false.
-     * @see #isVisible()
-     */
-    public boolean overridesItemVisibility() {
-        return false;
-    }
-
-    /**
-     * If {@link #overridesItemVisibility()} returns true, the return value of this method
-     * will help determine the visibility of the {@link MenuItem} this ActionProvider is bound to.
-     *
-     * <p>If the MenuItem's visibility is explicitly set to false by the application,
-     * the MenuItem will not be shown, even if this method returns true.</p>
-     *
-     * @return true if the MenuItem this ActionProvider is bound to is visible, false if
-     *         it is invisible. The default implementation returns true.
-     */
-    public boolean isVisible() {
-        return true;
-    }
-
-    /**
-     * If this ActionProvider is associated with an item in a menu,
-     * refresh the visibility of the item based on {@link #overridesItemVisibility()} and
-     * {@link #isVisible()}. If {@link #overridesItemVisibility()} returns false, this call
-     * will have no effect.
-     */
-    public void refreshVisibility() {
-        if (mVisibilityListener != null && overridesItemVisibility()) {
-            mVisibilityListener.onActionProviderVisibilityChanged(isVisible());
-        }
-    }
-
-    /**
-     * Performs an optional default action.
-     *
-     * <p>For the case of an action provider placed in a menu
-     * item not shown as an action this method is invoked if previous callbacks for processing menu
-     * selection has handled the event.
-     *
-     * <p> A menu item selection is processed in the following order:
-     *
-     * <ul><li>Receiving a call to
-     * {@link android.view.MenuItem.OnMenuItemClickListener#onMenuItemClick
-     * MenuItem.OnMenuItemClickListener.onMenuItemClick}.</li>
-     *
-     * <li>Receiving a call to
-     * {@link android.app.Activity#onOptionsItemSelected(android.view.MenuItem)}
-     * FragmentActivity.onOptionsItemSelected(MenuItem)}
-     * </li>
-     *
-     * <li>Receiving a call to
-     * {@link android.support.v4.app.Fragment#onOptionsItemSelected(android.view.MenuItem)}
-     * Fragment.onOptionsItemSelected(MenuItem)}</li>
-     *
-     * <li>Launching the {@link android.content.Intent} set via
-     * {@link android.view.MenuItem#setIntent(android.content.Intent)
-     * MenuItem.setIntent(android.content.Intent)}
-     * </li>
-     *
-     * <li>Invoking this method.</li></ul>
-     *
-     * <p>The default implementation does not perform any action and returns false.
-     */
-    public boolean onPerformDefaultAction() {
-        return false;
-    }
-
-    /**
-     * Determines if this ActionProvider has a submenu associated with it.
-     *
-     * <p>Associated submenus will be shown when an action view is not. This provider instance will
-     * receive a call to {@link #onPrepareSubMenu(SubMenu)} after the call to {@link
-     * #onPerformDefaultAction()} and before a submenu is displayed to the user.
-     *
-     * @return true if the item backed by this provider should have an associated submenu
-     */
-    public boolean hasSubMenu() {
-        return false;
-    }
-
-    /**
-     * Called to prepare an associated submenu for the menu item backed by this ActionProvider.
-     *
-     * <p>if {@link #hasSubMenu()} returns true, this method will be called when the menu item is
-     * selected to prepare the submenu for presentation to the user. Apps may use this to create or
-     * alter submenu content right before display.
-     *
-     * @param subMenu Submenu that will be displayed
-     */
-    public void onPrepareSubMenu(SubMenu subMenu) {
-    }
-
-    /**
-     * Notify the system that the visibility of an action view's sub-UI such as an anchored popup
-     * has changed. This will affect how other system visibility notifications occur.
-     *
-     * @hide Pending future API approval
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void subUiVisibilityChanged(boolean isVisible) {
-        if (mSubUiVisibilityListener != null) {
-            mSubUiVisibilityListener.onSubUiVisibilityChanged(isVisible);
-        }
-    }
-
-    /**
-     * @hide Internal use only
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
-        mSubUiVisibilityListener = listener;
-    }
-
-    /**
-     * Set a listener to be notified when this ActionProvider's overridden visibility changes.
-     * This should only be used by MenuItem implementations.
-     *
-     * @param listener listener to set
-     */
-    public void setVisibilityListener(VisibilityListener listener) {
-        if (mVisibilityListener != null && listener != null) {
-            Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
-                    "when one is already set. Are you reusing this " + getClass().getSimpleName() +
-                    " instance while it is still in use somewhere else?");
-        }
-        mVisibilityListener = listener;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void reset() {
-        mVisibilityListener = null;
-        mSubUiVisibilityListener = null;
-    }
-
-    /**
-     * @hide Internal use only
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public interface SubUiVisibilityListener {
-
-        public void onSubUiVisibilityChanged(boolean isVisible);
-    }
-
-    /**
-     * Listens to changes in visibility as reported by {@link ActionProvider#refreshVisibility()}.
-     *
-     * @see ActionProvider#overridesItemVisibility()
-     * @see ActionProvider#isVisible()
-     */
-    public interface VisibilityListener {
-        public void onActionProviderVisibilityChanged(boolean isVisible);
-    }
-}
diff --git a/android/support/v4/view/AsyncLayoutInflater.java b/android/support/v4/view/AsyncLayoutInflater.java
deleted file mode 100644
index d26e5b8..0000000
--- a/android/support/v4/view/AsyncLayoutInflater.java
+++ /dev/null
@@ -1,231 +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.content.Context;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.UiThread;
-import android.support.v4.util.Pools.SynchronizedPool;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.concurrent.ArrayBlockingQueue;
-
-/**
- * <p>Helper class for inflating layouts asynchronously. To use, construct
- * an instance of {@link AsyncLayoutInflater} on the UI thread and call
- * {@link #inflate(int, ViewGroup, OnInflateFinishedListener)}. The
- * {@link OnInflateFinishedListener} will be invoked on the UI thread
- * when the inflate request has completed.
- *
- * <p>This is intended for parts of the UI that are created lazily or in
- * response to user interactions. This allows the UI thread to continue
- * to be responsive & animate while the relatively heavy inflate
- * is being performed.
- *
- * <p>For a layout to be inflated asynchronously it needs to have a parent
- * whose {@link ViewGroup#generateLayoutParams(AttributeSet)} is thread-safe
- * and all the Views being constructed as part of inflation must not create
- * any {@link Handler}s or otherwise call {@link Looper#myLooper()}. If the
- * layout that is trying to be inflated cannot be constructed
- * asynchronously for whatever reason, {@link AsyncLayoutInflater} will
- * automatically fall back to inflating on the UI thread.
- *
- * <p>NOTE that the inflated View hierarchy is NOT added to the parent. It is
- * equivalent to calling {@link LayoutInflater#inflate(int, ViewGroup, boolean)}
- * with attachToRoot set to false. Callers will likely want to call
- * {@link ViewGroup#addView(View)} in the {@link OnInflateFinishedListener}
- * callback at a minimum.
- *
- * <p>This inflater does not support setting a {@link LayoutInflater.Factory}
- * nor {@link LayoutInflater.Factory2}. Similarly it does not support inflating
- * layouts that contain fragments.
- */
-public final class AsyncLayoutInflater {
-    private static final String TAG = "AsyncLayoutInflater";
-
-    LayoutInflater mInflater;
-    Handler mHandler;
-    InflateThread mInflateThread;
-
-    public AsyncLayoutInflater(@NonNull Context context) {
-        mInflater = new BasicInflater(context);
-        mHandler = new Handler(mHandlerCallback);
-        mInflateThread = InflateThread.getInstance();
-    }
-
-    @UiThread
-    public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
-            @NonNull OnInflateFinishedListener callback) {
-        if (callback == null) {
-            throw new NullPointerException("callback argument may not be null!");
-        }
-        InflateRequest request = mInflateThread.obtainRequest();
-        request.inflater = this;
-        request.resid = resid;
-        request.parent = parent;
-        request.callback = callback;
-        mInflateThread.enqueue(request);
-    }
-
-    private Callback mHandlerCallback = new Callback() {
-        @Override
-        public boolean handleMessage(Message msg) {
-            InflateRequest request = (InflateRequest) msg.obj;
-            if (request.view == null) {
-                request.view = mInflater.inflate(
-                        request.resid, request.parent, false);
-            }
-            request.callback.onInflateFinished(
-                    request.view, request.resid, request.parent);
-            mInflateThread.releaseRequest(request);
-            return true;
-        }
-    };
-
-    public interface OnInflateFinishedListener {
-        void onInflateFinished(@NonNull View view, @LayoutRes int resid,
-                @Nullable ViewGroup parent);
-    }
-
-    private static class InflateRequest {
-        AsyncLayoutInflater inflater;
-        ViewGroup parent;
-        int resid;
-        View view;
-        OnInflateFinishedListener callback;
-
-        InflateRequest() {
-        }
-    }
-
-    private static class BasicInflater extends LayoutInflater {
-        private static final String[] sClassPrefixList = {
-            "android.widget.",
-            "android.webkit.",
-            "android.app."
-        };
-
-        BasicInflater(Context context) {
-            super(context);
-        }
-
-        @Override
-        public LayoutInflater cloneInContext(Context newContext) {
-            return new BasicInflater(newContext);
-        }
-
-        @Override
-        protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
-            for (String prefix : sClassPrefixList) {
-                try {
-                    View view = createView(name, prefix, attrs);
-                    if (view != null) {
-                        return view;
-                    }
-                } catch (ClassNotFoundException e) {
-                    // In this case we want to let the base class take a crack
-                    // at it.
-                }
-            }
-
-            return super.onCreateView(name, attrs);
-        }
-    }
-
-    private static class InflateThread extends Thread {
-        private static final InflateThread sInstance;
-        static {
-            sInstance = new InflateThread();
-            sInstance.start();
-        }
-
-        public static InflateThread getInstance() {
-            return sInstance;
-        }
-
-        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) {
-                runInner();
-            }
-        }
-
-        public InflateRequest obtainRequest() {
-            InflateRequest obj = mRequestPool.acquire();
-            if (obj == null) {
-                obj = new InflateRequest();
-            }
-            return obj;
-        }
-
-        public void releaseRequest(InflateRequest obj) {
-            obj.callback = null;
-            obj.inflater = null;
-            obj.parent = null;
-            obj.resid = 0;
-            obj.view = null;
-            mRequestPool.release(obj);
-        }
-
-        public void enqueue(InflateRequest request) {
-            try {
-                mQueue.put(request);
-            } catch (InterruptedException e) {
-                throw new RuntimeException(
-                        "Failed to enqueue async inflate request", e);
-            }
-        }
-    }
-}
diff --git a/android/support/v4/view/GestureDetectorCompat.java b/android/support/v4/view/GestureDetectorCompat.java
deleted file mode 100644
index fbecce9..0000000
--- a/android/support/v4/view/GestureDetectorCompat.java
+++ /dev/null
@@ -1,569 +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.content.Context;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Message;
-import android.view.GestureDetector;
-import android.view.GestureDetector.OnDoubleTapListener;
-import android.view.GestureDetector.OnGestureListener;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-/**
- * Detects various gestures and events using the supplied {@link MotionEvent}s.
- * The {@link OnGestureListener} callback will notify users when a particular
- * motion event has occurred. This class should only be used with {@link MotionEvent}s
- * reported via touch (don't use for trackball events).
- *
- * <p>This compatibility implementation of the framework's GestureDetector guarantees
- * the newer focal point scrolling behavior from Jellybean MR1 on all platform versions.</p>
- *
- * To use this class:
- * <ul>
- *  <li>Create an instance of the {@code GestureDetectorCompat} for your {@link View}
- *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your callback
- *          will be executed when the events occur.
- * </ul>
- */
-public final class GestureDetectorCompat {
-    interface GestureDetectorCompatImpl {
-        boolean isLongpressEnabled();
-        boolean onTouchEvent(MotionEvent ev);
-        void setIsLongpressEnabled(boolean enabled);
-        void setOnDoubleTapListener(OnDoubleTapListener listener);
-    }
-
-    static class GestureDetectorCompatImplBase implements GestureDetectorCompatImpl {
-        private int mTouchSlopSquare;
-        private int mDoubleTapSlopSquare;
-        private int mMinimumFlingVelocity;
-        private int mMaximumFlingVelocity;
-
-        private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
-        private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
-        private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
-
-        // constants for Message.what used by GestureHandler below
-        private static final int SHOW_PRESS = 1;
-        private static final int LONG_PRESS = 2;
-        private static final int TAP = 3;
-
-        private final Handler mHandler;
-        final OnGestureListener mListener;
-        OnDoubleTapListener mDoubleTapListener;
-
-        boolean mStillDown;
-        boolean mDeferConfirmSingleTap;
-        private boolean mInLongPress;
-        private boolean mAlwaysInTapRegion;
-        private boolean mAlwaysInBiggerTapRegion;
-
-        MotionEvent mCurrentDownEvent;
-        private MotionEvent mPreviousUpEvent;
-
-        /**
-         * True when the user is still touching for the second tap (down, move, and
-         * up events). Can only be true if there is a double tap listener attached.
-         */
-        private boolean mIsDoubleTapping;
-
-        private float mLastFocusX;
-        private float mLastFocusY;
-        private float mDownFocusX;
-        private float mDownFocusY;
-
-        private boolean mIsLongpressEnabled;
-
-        /**
-         * Determines speed during touch scrolling
-         */
-        private VelocityTracker mVelocityTracker;
-
-        private class GestureHandler extends Handler {
-            GestureHandler() {
-                super();
-            }
-
-            GestureHandler(Handler handler) {
-                super(handler.getLooper());
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                case SHOW_PRESS:
-                    mListener.onShowPress(mCurrentDownEvent);
-                    break;
-
-                case LONG_PRESS:
-                    dispatchLongPress();
-                    break;
-
-                case TAP:
-                    // If the user's finger is still down, do not count it as a tap
-                    if (mDoubleTapListener != null) {
-                        if (!mStillDown) {
-                            mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
-                        } else {
-                            mDeferConfirmSingleTap = true;
-                        }
-                    }
-                    break;
-
-                default:
-                    throw new RuntimeException("Unknown message " + msg); //never
-                }
-            }
-        }
-
-        /**
-         * Creates a GestureDetector with the supplied listener.
-         * You may only use this constructor from a UI thread (this is the usual situation).
-         * @see android.os.Handler#Handler()
-         *
-         * @param context the application's context
-         * @param listener the listener invoked for all the callbacks, this must
-         * not be null.
-         * @param handler the handler to use
-         *
-         * @throws NullPointerException if {@code listener} is null.
-         */
-        public GestureDetectorCompatImplBase(Context context, OnGestureListener listener,
-                Handler handler) {
-            if (handler != null) {
-                mHandler = new GestureHandler(handler);
-            } else {
-                mHandler = new GestureHandler();
-            }
-            mListener = listener;
-            if (listener instanceof OnDoubleTapListener) {
-                setOnDoubleTapListener((OnDoubleTapListener) listener);
-            }
-            init(context);
-        }
-
-        private void init(Context context) {
-            if (context == null) {
-                throw new IllegalArgumentException("Context must not be null");
-            }
-            if (mListener == null) {
-                throw new IllegalArgumentException("OnGestureListener must not be null");
-            }
-            mIsLongpressEnabled = true;
-
-            final ViewConfiguration configuration = ViewConfiguration.get(context);
-            final int touchSlop = configuration.getScaledTouchSlop();
-            final int doubleTapSlop = configuration.getScaledDoubleTapSlop();
-            mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
-            mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
-
-            mTouchSlopSquare = touchSlop * touchSlop;
-            mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
-        }
-
-        /**
-         * Sets the listener which will be called for double-tap and related
-         * gestures.
-         *
-         * @param onDoubleTapListener the listener invoked for all the callbacks, or
-         *        null to stop listening for double-tap gestures.
-         */
-        @Override
-        public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
-            mDoubleTapListener = onDoubleTapListener;
-        }
-
-        /**
-         * Set whether longpress is enabled, if this is enabled when a user
-         * presses and holds down you get a longpress event and nothing further.
-         * If it's disabled the user can press and hold down and then later
-         * moved their finger and you will get scroll events. By default
-         * longpress is enabled.
-         *
-         * @param isLongpressEnabled whether longpress should be enabled.
-         */
-        @Override
-        public void setIsLongpressEnabled(boolean isLongpressEnabled) {
-            mIsLongpressEnabled = isLongpressEnabled;
-        }
-
-        /**
-         * @return true if longpress is enabled, else false.
-         */
-        @Override
-        public boolean isLongpressEnabled() {
-            return mIsLongpressEnabled;
-        }
-
-        /**
-         * Analyzes the given motion event and if applicable triggers the
-         * appropriate callbacks on the {@link OnGestureListener} supplied.
-         *
-         * @param ev The current motion event.
-         * @return true if the {@link OnGestureListener} consumed the event,
-         *              else false.
-         */
-        @Override
-        public boolean onTouchEvent(MotionEvent ev) {
-            final int action = ev.getAction();
-
-            if (mVelocityTracker == null) {
-                mVelocityTracker = VelocityTracker.obtain();
-            }
-            mVelocityTracker.addMovement(ev);
-
-            final boolean pointerUp =
-                    (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
-            final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
-
-            // Determine focal point
-            float sumX = 0, sumY = 0;
-            final int count = ev.getPointerCount();
-            for (int i = 0; i < count; i++) {
-                if (skipIndex == i) continue;
-                sumX += ev.getX(i);
-                sumY += ev.getY(i);
-            }
-            final int div = pointerUp ? count - 1 : count;
-            final float focusX = sumX / div;
-            final float focusY = sumY / div;
-
-            boolean handled = false;
-
-            switch (action & MotionEvent.ACTION_MASK) {
-                case MotionEvent.ACTION_POINTER_DOWN:
-                    mDownFocusX = mLastFocusX = focusX;
-                    mDownFocusY = mLastFocusY = focusY;
-                    // Cancel long press and taps
-                    cancelTaps();
-                    break;
-
-                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 = 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 * mVelocityTracker.getXVelocity(id2);
-                        final float y = y1 * mVelocityTracker.getYVelocity(id2);
-
-                        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;
-                    }
-                    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) {
-                            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;
-                    }
-                    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);
-                        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);
-
-                        if ((Math.abs(velocityY) > mMinimumFlingVelocity)
-                                || (Math.abs(velocityX) > mMinimumFlingVelocity)) {
-                            handled = mListener.onFling(
-                                    mCurrentDownEvent, ev, velocityX, velocityY);
-                        }
-                    }
-                    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;
-
-                case MotionEvent.ACTION_CANCEL:
-                    cancel();
-                    break;
-            }
-
-            return handled;
-        }
-
-        private void cancel() {
-            mHandler.removeMessages(SHOW_PRESS);
-            mHandler.removeMessages(LONG_PRESS);
-            mHandler.removeMessages(TAP);
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-            mIsDoubleTapping = false;
-            mStillDown = false;
-            mAlwaysInTapRegion = false;
-            mAlwaysInBiggerTapRegion = false;
-            mDeferConfirmSingleTap = false;
-            if (mInLongPress) {
-                mInLongPress = false;
-            }
-        }
-
-        private void cancelTaps() {
-            mHandler.removeMessages(SHOW_PRESS);
-            mHandler.removeMessages(LONG_PRESS);
-            mHandler.removeMessages(TAP);
-            mIsDoubleTapping = false;
-            mAlwaysInTapRegion = false;
-            mAlwaysInBiggerTapRegion = false;
-            mDeferConfirmSingleTap = false;
-            if (mInLongPress) {
-                mInLongPress = false;
-            }
-        }
-
-        private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
-                MotionEvent secondDown) {
-            if (!mAlwaysInBiggerTapRegion) {
-                return false;
-            }
-
-            if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
-                return false;
-            }
-
-            int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
-            int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
-            return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
-        }
-
-        void dispatchLongPress() {
-            mHandler.removeMessages(TAP);
-            mDeferConfirmSingleTap = false;
-            mInLongPress = true;
-            mListener.onLongPress(mCurrentDownEvent);
-        }
-    }
-
-    static class GestureDetectorCompatImplJellybeanMr2 implements GestureDetectorCompatImpl {
-        private final GestureDetector mDetector;
-
-        public GestureDetectorCompatImplJellybeanMr2(Context context, OnGestureListener listener,
-                Handler handler) {
-            mDetector = new GestureDetector(context, listener, handler);
-        }
-
-        @Override
-        public boolean isLongpressEnabled() {
-            return mDetector.isLongpressEnabled();
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent ev) {
-            return mDetector.onTouchEvent(ev);
-        }
-
-        @Override
-        public void setIsLongpressEnabled(boolean enabled) {
-            mDetector.setIsLongpressEnabled(enabled);
-        }
-
-        @Override
-        public void setOnDoubleTapListener(OnDoubleTapListener listener) {
-            mDetector.setOnDoubleTapListener(listener);
-        }
-    }
-
-    private final GestureDetectorCompatImpl mImpl;
-
-    /**
-     * Creates a GestureDetectorCompat with the supplied listener.
-     * As usual, you may only use this constructor from a UI thread.
-     * @see android.os.Handler#Handler()
-     *
-     * @param context the application's context
-     * @param listener the listener invoked for all the callbacks, this must
-     * not be null.
-     */
-    public GestureDetectorCompat(Context context, OnGestureListener listener) {
-        this(context, listener, null);
-    }
-
-    /**
-     * Creates a GestureDetectorCompat with the supplied listener.
-     * As usual, you may only use this constructor from a UI thread.
-     * @see android.os.Handler#Handler()
-     *
-     * @param context the application's context
-     * @param listener the listener invoked for all the callbacks, this must
-     * not be null.
-     * @param handler the handler that will be used for posting deferred messages
-     */
-    public GestureDetectorCompat(Context context, OnGestureListener listener, Handler handler) {
-        if (Build.VERSION.SDK_INT > 17) {
-            mImpl = new GestureDetectorCompatImplJellybeanMr2(context, listener, handler);
-        } else {
-            mImpl = new GestureDetectorCompatImplBase(context, listener, handler);
-        }
-    }
-
-    /**
-     * @return true if longpress is enabled, else false.
-     */
-    public boolean isLongpressEnabled() {
-        return mImpl.isLongpressEnabled();
-    }
-
-    /**
-     * Analyzes the given motion event and if applicable triggers the
-     * appropriate callbacks on the {@link OnGestureListener} supplied.
-     *
-     * @param event The current motion event.
-     * @return true if the {@link OnGestureListener} consumed the event,
-     *              else false.
-     */
-    public boolean onTouchEvent(MotionEvent event) {
-        return mImpl.onTouchEvent(event);
-    }
-
-    /**
-     * Set whether longpress is enabled, if this is enabled when a user
-     * presses and holds down you get a longpress event and nothing further.
-     * If it's disabled the user can press and hold down and then later
-     * moved their finger and you will get scroll events. By default
-     * longpress is enabled.
-     *
-     * @param enabled whether longpress should be enabled.
-     */
-    public void setIsLongpressEnabled(boolean enabled) {
-        mImpl.setIsLongpressEnabled(enabled);
-    }
-
-    /**
-     * Sets the listener which will be called for double-tap and related
-     * gestures.
-     *
-     * @param listener the listener invoked for all the callbacks, or
-     *        null to stop listening for double-tap gestures.
-     */
-    public void setOnDoubleTapListener(OnDoubleTapListener listener) {
-        mImpl.setOnDoubleTapListener(listener);
-    }
-}
diff --git a/android/support/v4/view/GravityCompat.java b/android/support/v4/view/GravityCompat.java
deleted file mode 100644
index c729a61..0000000
--- a/android/support/v4/view/GravityCompat.java
+++ /dev/null
@@ -1,154 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.graphics.Rect;
-import android.view.Gravity;
-
-/**
- * Compatibility shim for accessing newer functionality from {@link android.view.Gravity}.
- */
-public final class GravityCompat {
-    /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
-     * absolute LEFT/RIGHT).
-     */
-    public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
-
-    /** Push object to x-axis position at the start of its container, not changing its size. */
-    public static final int START = RELATIVE_LAYOUT_DIRECTION | Gravity.LEFT;
-
-    /** Push object to x-axis position at the end of its container, not changing its size. */
-    public static final int END = RELATIVE_LAYOUT_DIRECTION | Gravity.RIGHT;
-
-    /**
-     * Binary mask for the horizontal gravity and script specific direction bit.
-     */
-    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
-
-    /**
-     * Apply a gravity constant to an object and take care if layout direction is RTL or not.
-     *
-     * @param gravity The desired placement of the object, as defined by the
-     *                constants in this class.
-     * @param w The horizontal size of the object.
-     * @param h The vertical size of the object.
-     * @param container The frame of the containing space, in which the object
-     *                  will be placed.  Should be large enough to contain the
-     *                  width and height of the object.
-     * @param outRect Receives the computed frame of the object in its
-     *                container.
-     * @param layoutDirection The layout direction.
-     *
-     * @see ViewCompat#LAYOUT_DIRECTION_LTR
-     * @see ViewCompat#LAYOUT_DIRECTION_RTL
-     */
-    public static void apply(int gravity, int w, int h, Rect container,
-            Rect outRect, int layoutDirection) {
-        if (SDK_INT >= 17) {
-            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
-        } else {
-            Gravity.apply(gravity, w, h, container, outRect);
-        }
-    }
-
-    /**
-     * Apply a gravity constant to an object.
-     *
-     * @param gravity The desired placement of the object, as defined by the
-     *                constants in this class.
-     * @param w The horizontal size of the object.
-     * @param h The vertical size of the object.
-     * @param container The frame of the containing space, in which the object
-     *                  will be placed.  Should be large enough to contain the
-     *                  width and height of the object.
-     * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
-     *             pushes it to the right; if gravity is RIGHT it pushes it to
-     *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
-     *             right or left; otherwise it is ignored.
-     * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
-     *             it down; if gravity is BOTTOM it pushes it up; if gravity is
-     *             CENTER_VERTICAL it pushes it down or up; otherwise it is
-     *             ignored.
-     * @param outRect Receives the computed frame of the object in its
-     *                container.
-     * @param layoutDirection The layout direction.
-     *
-     * @see ViewCompat#LAYOUT_DIRECTION_LTR
-     * @see ViewCompat#LAYOUT_DIRECTION_RTL
-     */
-    public static void apply(int gravity, int w, int h, Rect container,
-            int xAdj, int yAdj, Rect outRect, int 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);
-        }
-    }
-
-    /**
-     * Apply additional gravity behavior based on the overall "display" that an
-     * object exists in.  This can be used after
-     * {@link android.view.Gravity#apply(int, int, int, Rect, int, int, Rect)} to place the object
-     * within a visible display.  By default this moves or clips the object
-     * to be visible in the display; the gravity flags
-     * {@link android.view.Gravity#DISPLAY_CLIP_HORIZONTAL} and
-     * {@link android.view.Gravity#DISPLAY_CLIP_VERTICAL} can be used to change this behavior.
-     *
-     * @param gravity Gravity constants to modify the placement within the
-     * display.
-     * @param display The rectangle of the display in which the object is
-     * being placed.
-     * @param inoutObj Supplies the current object position; returns with it
-     * modified if needed to fit in the display.
-     * @param layoutDirection The layout direction.
-     *
-     * @see ViewCompat#LAYOUT_DIRECTION_LTR
-     * @see ViewCompat#LAYOUT_DIRECTION_RTL
-     */
-    public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
-        if (SDK_INT >= 17) {
-            Gravity.applyDisplay(gravity, display, inoutObj, layoutDirection);
-        } else {
-            Gravity.applyDisplay(gravity, display, inoutObj);
-        }
-    }
-
-    /**
-     * <p>Convert script specific gravity to absolute horizontal value.</p>
-     *
-     * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
-     * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
-     *
-     *
-     * @param gravity The gravity to convert to absolute (horizontal) values.
-     * @param layoutDirection The layout direction.
-     * @return gravity converted to absolute (horizontal) values.
-     */
-    public static int getAbsoluteGravity(int gravity, int 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/android/support/v4/view/InputDeviceCompat.java b/android/support/v4/view/InputDeviceCompat.java
deleted file mode 100644
index 58c9c04..0000000
--- a/android/support/v4/view/InputDeviceCompat.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.v4.view;
-
-/**
- * Helper class for accessing values in {@link android.view.InputDevice}.
- */
-public final class InputDeviceCompat {
-
-    /**
-     * A mask for input source classes.
-     *
-     * Each distinct input source constant has one or more input source class bits set to
-     * specify the desired interpretation for its input events.
-     */
-    public static final int SOURCE_CLASS_MASK = 0x000000ff;
-
-    /**
-     * The input source has no class.
-     *
-     * It is up to the application to determine how to handle the device based on the device type.
-     */
-    public static final int SOURCE_CLASS_NONE = 0x00000000;
-
-    /**
-     * The input source has buttons or keys.
-     * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}.
-     *
-     * A {@link android.view.KeyEvent} should be interpreted as a button or key press.
-     */
-    public static final int SOURCE_CLASS_BUTTON = 0x00000001;
-
-    /**
-     * The input source is a pointing device associated with a display.
-     * Examples: {@link #SOURCE_TOUCHSCREEN}, {@link #SOURCE_MOUSE}.
-     *
-     * A {@link android.view.MotionEvent} should be interpreted as absolute coordinates in
-     * display units according to the {@link android.view.View} hierarchy.  Pointer down/up
-     * indicated when
-     * the finger touches the display or when the selection button is pressed/released.
-     *
-     * Use {@link android.view.InputDevice#getMotionRange} to query the range of the pointing
-     * device.  Some devices permit
-     * touches outside the display area so the effective range may be somewhat smaller or larger
-     * than the actual display size.
-     */
-    public static final int SOURCE_CLASS_POINTER = 0x00000002;
-
-    /**
-     * The input source is a trackball navigation device.
-     * Examples: {@link #SOURCE_TRACKBALL}.
-     *
-     * A {@link android.view.MotionEvent} should be interpreted as relative movements in
-     * device-specific
-     * units used for navigation purposes.  Pointer down/up indicates when the selection button
-     * is pressed/released.
-     *
-     * Use {@link android.view.InputDevice#getMotionRange} to query the range of motion.
-     */
-    public static final int SOURCE_CLASS_TRACKBALL = 0x00000004;
-
-    /**
-     * The input source is an absolute positioning device not associated with a display
-     * (unlike {@link #SOURCE_CLASS_POINTER}).
-     *
-     * A {@link android.view.MotionEvent} should be interpreted as absolute coordinates in
-     * device-specific surface units.
-     *
-     * Use {@link android.view.InputDevice#getMotionRange} to query the range of positions.
-     */
-    public static final int SOURCE_CLASS_POSITION = 0x00000008;
-
-    /**
-     * The input source is a joystick.
-     *
-     * A {@link android.view.MotionEvent} should be interpreted as absolute joystick movements.
-     *
-     * Use {@link android.view.InputDevice#getMotionRange} to query the range of positions.
-     */
-    public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
-
-    /**
-     * The input source is unknown.
-     */
-    public static final int SOURCE_UNKNOWN = 0x00000000;
-
-    /**
-     * The input source is a keyboard.
-     *
-     * This source indicates pretty much anything that has buttons.  Use
-     * {@link android.view.InputDevice#getKeyboardType()} to determine whether the keyboard has
-     * alphabetic keys
-     * and can be used to enter text.
-     *
-     * @see #SOURCE_CLASS_BUTTON
-     */
-    public static final int SOURCE_KEYBOARD = 0x00000100 | SOURCE_CLASS_BUTTON;
-
-    /**
-     * The input source is a DPad.
-     *
-     * @see #SOURCE_CLASS_BUTTON
-     */
-    public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON;
-
-    /**
-     * The input source is a game pad.
-     * (It may also be a {@link #SOURCE_JOYSTICK}).
-     *
-     * @see #SOURCE_CLASS_BUTTON
-     */
-    public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON;
-
-    /**
-     * The input source is a touch screen pointing device.
-     *
-     * @see #SOURCE_CLASS_POINTER
-     */
-    public static final int SOURCE_TOUCHSCREEN = 0x00001000 | SOURCE_CLASS_POINTER;
-
-    /**
-     * The input source is a mouse pointing device.
-     * This code is also used for other mouse-like pointing devices such as trackpads
-     * and trackpoints.
-     *
-     * @see #SOURCE_CLASS_POINTER
-     */
-    public static final int SOURCE_MOUSE = 0x00002000 | SOURCE_CLASS_POINTER;
-
-    /**
-     * The input source is a stylus pointing device.
-     * <p>
-     * Note that this bit merely indicates that an input device is capable of obtaining
-     * input from a stylus.  To determine whether a given touch event was produced
-     * by a stylus, examine the tool type returned by {@link android.view.MotionEvent#getToolType(int)}
-     * for each individual pointer.
-     * </p><p>
-     * A single touch event may multiple pointers with different tool types,
-     * such as an event that has one pointer with tool type
-     * {@link android.view.MotionEvent#TOOL_TYPE_FINGER} and another pointer with tool type
-     * {@link android.view.MotionEvent#TOOL_TYPE_STYLUS}.  So it is important to examine
-     * the tool type of each pointer, regardless of the source reported
-     * by {@link android.view.MotionEvent#getSource()}.
-     * </p>
-     *
-     * @see #SOURCE_CLASS_POINTER
-     */
-    public static final int SOURCE_STYLUS = 0x00004000 | SOURCE_CLASS_POINTER;
-
-    /**
-     * The input source is a trackball.
-     *
-     * @see #SOURCE_CLASS_TRACKBALL
-     */
-    public static final int SOURCE_TRACKBALL = 0x00010000 | SOURCE_CLASS_TRACKBALL;
-
-    /**
-     * The input source is a touch pad or digitizer tablet that is not
-     * associated with a display (unlike {@link #SOURCE_TOUCHSCREEN}).
-     *
-     * @see #SOURCE_CLASS_POSITION
-     */
-    public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
-
-    /**
-     * The input source is a touch device whose motions should be interpreted as navigation events.
-     *
-     * For example, an upward swipe should be as an upward focus traversal in the same manner as
-     * pressing up on a D-Pad would be. Swipes to the left, right and down should be treated in a
-     * similar manner.
-     *
-     * @see #SOURCE_CLASS_NONE
-     */
-    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}).
-     *
-     * @see #SOURCE_CLASS_JOYSTICK
-     */
-    public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK;
-
-    /**
-     * The input source is a device connected through HDMI-based bus.
-     *
-     * The key comes in through HDMI-CEC or MHL signal line, and is treated as if it were
-     * generated by a locally connected DPAD or keyboard.
-     */
-    public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON;
-
-    /**
-     * A special input source constant that is used when filtering input devices
-     * to match devices that provide any type of input source.
-     */
-    public static final int SOURCE_ANY = 0xffffff00;
-
-    private InputDeviceCompat() {}
-}
diff --git a/android/support/v4/view/LayoutInflaterCompat.java b/android/support/v4/view/LayoutInflaterCompat.java
deleted file mode 100644
index 0cb2969..0000000
--- a/android/support/v4/view/LayoutInflaterCompat.java
+++ /dev/null
@@ -1,198 +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.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 LayoutInflater}.
- */
-public final class LayoutInflaterCompat {
-    private static final String TAG = "LayoutInflaterCompatHC";
-
-    private static Field sLayoutInflaterFactory2Field;
-    private static boolean sCheckedField;
-
-    @SuppressWarnings("deprecation")
-    static class Factory2Wrapper implements LayoutInflater.Factory2 {
-        final LayoutInflaterFactory mDelegateFactory;
-
-        Factory2Wrapper(LayoutInflaterFactory delegateFactory) {
-            mDelegateFactory = delegateFactory;
-        }
-
-        @Override
-        public View onCreateView(String name, Context context, AttributeSet attrs) {
-            return mDelegateFactory.onCreateView(null, name, context, attrs);
-        }
-
-        @Override
-        public View onCreateView(View parent, String name, Context context,
-                AttributeSet attributeSet) {
-            return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
-        }
-
-        @Override
-        public String toString() {
-            return getClass().getName() + "{" + mDelegateFactory + "}";
-        }
-    }
-
-    /**
-     * 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 {
-        if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new LayoutInflaterCompatApi21Impl();
-        } else {
-            IMPL = new LayoutInflaterCompatBaseImpl();
-        }
-    }
-
-    /*
-     * Hide the constructor.
-     */
-    private LayoutInflaterCompat() {
-    }
-
-    /**
-     * Attach a custom Factory interface for creating views while using
-     * this LayoutInflater. This must not be null, and can only be set once;
-     * 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.
-     */
-    @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).
-     *
-     * @return The {@link LayoutInflaterFactory} associated with the
-     * {@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/android/support/v4/view/LayoutInflaterFactory.java b/android/support/v4/view/LayoutInflaterFactory.java
deleted file mode 100644
index 2ee4704..0000000
--- a/android/support/v4/view/LayoutInflaterFactory.java
+++ /dev/null
@@ -1,48 +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
- * {@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/android/support/v4/view/MarginLayoutParamsCompat.java b/android/support/v4/view/MarginLayoutParamsCompat.java
deleted file mode 100644
index 8c76854..0000000
--- a/android/support/v4/view/MarginLayoutParamsCompat.java
+++ /dev/null
@@ -1,167 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.view.ViewGroup;
-
-/**
- * Helper for accessing API features in
- * {@link android.view.ViewGroup.MarginLayoutParams MarginLayoutParams} in a backwards compatible
- * way.
- */
-public final class MarginLayoutParamsCompat {
-    /**
-     * Get the relative starting margin that was set.
-     *
-     * <p>On platform versions supporting bidirectional text and layouts
-     * this value will be resolved into the LayoutParams object's left or right
-     * margin as appropriate when the associated View is attached to a window
-     * or when the layout direction of that view changes.</p>
-     *
-     * @param lp LayoutParams to query
-     * @return the margin along the starting edge in pixels
-     */
-    public static int getMarginStart(ViewGroup.MarginLayoutParams lp) {
-        if (SDK_INT >= 17) {
-            return lp.getMarginStart();
-        } else {
-            return lp.leftMargin;
-        }
-    }
-
-    /**
-     * Get the relative ending margin that was set.
-     *
-     * <p>On platform versions supporting bidirectional text and layouts
-     * this value will be resolved into the LayoutParams object's left or right
-     * margin as appropriate when the associated View is attached to a window
-     * or when the layout direction of that view changes.</p>
-     *
-     * @param lp LayoutParams to query
-     * @return the margin along the ending edge in pixels
-     */
-    public static int getMarginEnd(ViewGroup.MarginLayoutParams lp) {
-        if (SDK_INT >= 17) {
-            return lp.getMarginEnd();
-        } else {
-            return lp.rightMargin;
-        }
-    }
-
-    /**
-     * Set the relative start margin.
-     *
-     * <p>On platform versions supporting bidirectional text and layouts
-     * this value will be resolved into the LayoutParams object's left or right
-     * margin as appropriate when the associated View is attached to a window
-     * or when the layout direction of that view changes.</p>
-     *
-     * @param lp LayoutParams to query
-     * @param marginStart the desired start margin in pixels
-     */
-    public static void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart) {
-        if (SDK_INT >= 17) {
-            lp.setMarginStart(marginStart);
-        } else {
-            lp.leftMargin = marginStart;
-        }
-    }
-
-    /**
-     * Set the relative end margin.
-     *
-     * <p>On platform versions supporting bidirectional text and layouts
-     * this value will be resolved into the LayoutParams object's left or right
-     * margin as appropriate when the associated View is attached to a window
-     * or when the layout direction of that view changes.</p>
-     *
-     * @param lp LayoutParams to query
-     * @param marginEnd the desired end margin in pixels
-     */
-    public static void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd) {
-        if (SDK_INT >= 17) {
-            lp.setMarginEnd(marginEnd);
-        } else {
-            lp.rightMargin = marginEnd;
-        }
-    }
-
-    /**
-     * Check if margins are relative.
-     *
-     * @return true if either marginStart or marginEnd has been set.
-     */
-    public static boolean isMarginRelative(ViewGroup.MarginLayoutParams lp) {
-        if (SDK_INT >= 17) {
-            return lp.isMarginRelative();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns the layout direction. Can be either {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
-     * {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
-     *
-     * @return the layout direction.
-     */
-    public static int getLayoutDirection(ViewGroup.MarginLayoutParams 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
-            // is -1
-            result = ViewCompat.LAYOUT_DIRECTION_LTR;
-        }
-        return result;
-    }
-
-    /**
-     * Set the layout direction.
-     *
-     * @param layoutDirection the layout direction.
-     *        Should be either {@link ViewCompat#LAYOUT_DIRECTION_LTR}
-     *                     or {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
-     */
-    public static void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {
-        if (SDK_INT >= 17) {
-            lp.setLayoutDirection(layoutDirection);
-        }
-    }
-
-    /**
-     * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
-     * may be overridden depending on layout direction.
-     */
-    public static void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp,
-            int layoutDirection) {
-        if (SDK_INT >= 17) {
-            lp.resolveLayoutDirection(layoutDirection);
-        }
-    }
-
-    private MarginLayoutParamsCompat() {}
-}
diff --git a/android/support/v4/view/MenuCompat.java b/android/support/v4/view/MenuCompat.java
deleted file mode 100644
index bbac379..0000000
--- a/android/support/v4/view/MenuCompat.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.view.MenuItem;
-
-/**
- * Helper for accessing features in {@link android.view.Menu}.
- */
-public final class MenuCompat {
-    /**
-     * Call {@link MenuItem#setShowAsAction(int) MenuItem.setShowAsAction()}.
-     *
-     * @deprecated Use {@link MenuItem#setShowAsAction(int)} directly.
-     */
-    @Deprecated
-    public static void setShowAsAction(MenuItem item, int actionEnum) {
-        item.setShowAsAction(actionEnum);
-    }
-
-    private MenuCompat() {}
-}
diff --git a/android/support/v4/view/MenuItemCompat.java b/android/support/v4/view/MenuItemCompat.java
deleted file mode 100644
index bbbc2e5..0000000
--- a/android/support/v4/view/MenuItemCompat.java
+++ /dev/null
@@ -1,702 +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.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}.
- * <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
- * argument.</p>
- */
-public final class MenuItemCompat {
-    private static final String TAG = "MenuItemCompat";
-
-    /**
-     * 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;
-
-    /**
-     * Always show this item as a button in an Action Bar. Use sparingly!
-     * If too many items are set to always show in the Action Bar it can
-     * 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 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);
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when a menu item marked with {@link
-     * #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} is expanded or collapsed.
-     *
-     * @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 {
-
-        /**
-         * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
-         * is expanded.
-         *
-         * @param item Item that was expanded
-         * @return true if the item should expand, false if expansion should be suppressed.
-         */
-        boolean onMenuItemActionExpand(MenuItem item);
-
-        /**
-         * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
-         * is collapsed.
-         *
-         * @param item Item that was collapsed
-         * @return true if the item should collapse, false if collapsing should be suppressed.
-         */
-        boolean onMenuItemActionCollapse(MenuItem item);
-    }
-
-    static class MenuItemCompatBaseImpl implements MenuVersionImpl {
-        @Override
-        public void setContentDescription(MenuItem item, CharSequence contentDescription) {
-        }
-
-        @Override
-        public CharSequence getContentDescription(MenuItem item) {
-            return null;
-        }
-
-        @Override
-        public void setTooltipText(MenuItem item, CharSequence tooltipText) {
-        }
-
-        @Override
-        public CharSequence getTooltipText(MenuItem item) {
-            return null;
-        }
-
-        @Override
-        public void setShortcut(MenuItem item, char numericChar, char alphaChar,
-                int numericModifiers, int alphaModifiers) {
-        }
-
-        @Override
-        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;
-        }
-    }
-
-    @RequiresApi(26)
-    static class MenuItemCompatApi26Impl extends MenuItemCompatBaseImpl {
-        @Override
-        public void setContentDescription(MenuItem item, CharSequence contentDescription) {
-            item.setContentDescription(contentDescription);
-        }
-
-        @Override
-        public CharSequence getContentDescription(MenuItem item) {
-            return item.getContentDescription();
-        }
-
-        @Override
-        public void setTooltipText(MenuItem item, CharSequence tooltipText) {
-            item.setTooltipText(tooltipText);
-        }
-
-        @Override
-        public CharSequence getTooltipText(MenuItem item) {
-            return item.getTooltipText();
-        }
-
-        @Override
-        public void setShortcut(MenuItem item, char numericChar, char alphaChar,
-                int numericModifiers, int alphaModifiers) {
-            item.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers);
-        }
-
-        @Override
-        public void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
-            item.setAlphabeticShortcut(alphaChar, alphaModifiers);
-        }
-
-        @Override
-        public int getAlphabeticModifiers(MenuItem item) {
-            return item.getAlphabeticModifiers();
-        }
-
-        @Override
-        public void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
-            item.setNumericShortcut(numericChar, numericModifiers);
-        }
-
-        @Override
-        public int getNumericModifiers(MenuItem item) {
-            return item.getNumericModifiers();
-        }
-
-        @Override
-        public void setIconTintList(MenuItem item, ColorStateList tint) {
-            item.setIconTintList(tint);
-        }
-
-        @Override
-        public ColorStateList getIconTintList(MenuItem item) {
-            return item.getIconTintList();
-        }
-
-        @Override
-        public void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {
-            item.setIconTintMode(tintMode);
-        }
-
-        @Override
-        public PorterDuff.Mode getIconTintMode(MenuItem item) {
-            return item.getIconTintMode();
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final MenuVersionImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 26) {
-            IMPL = new MenuItemCompatApi26Impl();
-        } else {
-            IMPL = new MenuItemCompatBaseImpl();
-        }
-    }
-
-    // -------------------------------------------------------------------
-
-    /**
-     * Sets how this item should display in the presence of a compatible Action Bar. If the given
-     * item is compatible, this will call the item's supported implementation of
-     * {@link MenuItem#setShowAsAction(int)}.
-     *
-     * @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) {
-        item.setShowAsAction(actionEnum);
-    }
-
-    /**
-     * Set an action view for this menu item. An action view will be displayed in place
-     * of an automatically generated menu item element in the UI when this item is shown
-     * as an action within a parent.
-     *
-     * @param item the item to change
-     * @param view View to use for presenting this item to the user.
-     * @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) {
-        return item.setActionView(view);
-    }
-
-    /**
-     * Set an action view for this menu item. An action view will be displayed in place
-     * of an automatically generated menu item element in the UI when this item is shown
-     * as an action within a parent.
-     * <p>
-     *   <strong>Note:</strong> Setting an action view overrides the action provider
-     *           set via {@link #setActionProvider(MenuItem, ActionProvider)}.
-     * </p>
-     *
-     * @param item the item to change
-     * @param resId Layout resource to use for presenting this item to the user.
-     * @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) {
-        return item.setActionView(resId);
-    }
-
-    /**
-     * Returns the currently set action view for this menu item.
-     *
-     * @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) {
-        return item.getActionView();
-    }
-
-    /**
-     * Sets the {@link ActionProvider} responsible for creating an action view if
-     * the item is placed on the action bar. The provider also provides a default
-     * action invoked if the item is placed in the overflow menu.
-     * <p>
-     *   <strong>Note:</strong> Setting an action provider overrides the action view
-     *           set via {@link #setActionView(MenuItem, View)}.
-     * </p>
-     *
-     * @param item item to change
-     * @param provider The action provider.
-     * @return This Item so additional setters can be called.
-     *
-     * @see ActionProvider
-     */
-    public static MenuItem setActionProvider(MenuItem item, ActionProvider provider) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).setSupportActionProvider(provider);
-        }
-        // TODO Wrap the support ActionProvider and assign it
-        Log.w(TAG, "setActionProvider: item does not implement SupportMenuItem; ignoring");
-        return item;
-    }
-
-    /**
-     * Gets the {@link ActionProvider}.
-     *
-     * @return The action provider.
-     *
-     * @see ActionProvider
-     * @see #setActionProvider(MenuItem, ActionProvider)
-     */
-    public static ActionProvider getActionProvider(MenuItem item) {
-        if (item instanceof SupportMenuItem) {
-            return ((SupportMenuItem) item).getSupportActionProvider();
-        }
-
-        // TODO Wrap the framework ActionProvider and return it
-        Log.w(TAG, "getActionProvider: item does not implement SupportMenuItem; returning null");
-        return null;
-    }
-
-    /**
-     * 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 #setOnActionExpandListener(MenuItem, OnActionExpandListener)}
-     * it will have its {@link 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.
-     *
-     * @deprecated Use {@link MenuItem#expandActionView()} directly.
-     */
-    @Deprecated
-    public static boolean expandActionView(MenuItem item) {
-        return item.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 #setOnActionExpandListener(MenuItem,
-     * 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.
-     *
-     * @return true if the action view was collapsed, false otherwise.
-     *
-     * @deprecated Use {@link MenuItem#collapseActionView()} directly.
-     */
-    @Deprecated
-    public static boolean collapseActionView(MenuItem item) {
-        return item.collapseActionView();
-    }
-
-    /**
-     * Returns true if this menu item's action view has been expanded.
-     *
-     * @return true if the item's action view is expanded, false otherwise.
-     * @see #expandActionView(MenuItem)
-     * @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) {
-        return item.isActionViewExpanded();
-    }
-
-    /**
-     * Set an {@link 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}.
-     *
-     * @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,
-            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) {
-            ((SupportMenuItem) item).setContentDescription(contentDescription);
-        } else {
-            IMPL.setContentDescription(item, contentDescription);
-        }
-    }
-
-    /**
-     * 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/android/support/v4/view/MotionEventCompat.java b/android/support/v4/view/MotionEventCompat.java
deleted file mode 100644
index 367eda2..0000000
--- a/android/support/v4/view/MotionEventCompat.java
+++ /dev/null
@@ -1,603 +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.view.MotionEvent;
-
-/**
- * Helper for accessing features in {@link MotionEvent}.
- */
-public final class MotionEventCompat {
-    /**
-     * 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;
-
-    /**
-     * Synonym for {@link MotionEvent#AXIS_RELATIVE_Y}.
-     */
-    public static final int AXIS_RELATIVE_Y = 28;
-
-    /**
-     * 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.getActionMasked();
-    }
-
-    /**
-     * Call {@link MotionEvent#getAction}, returning only the pointer index
-     * 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.getActionIndex();
-    }
-
-    /**
-     * Call {@link MotionEvent#findPointerIndex(int)}.
-     *
-     * @deprecated Call {@link MotionEvent#findPointerIndex(int)} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static int findPointerIndex(MotionEvent event, int pointerId) {
-        return event.findPointerIndex(pointerId);
-    }
-
-    /**
-     * Call {@link MotionEvent#getPointerId(int)}.
-     *
-     * @deprecated Call {@link MotionEvent#getPointerId(int)} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static int getPointerId(MotionEvent event, int pointerIndex) {
-        return event.getPointerId(pointerIndex);
-    }
-
-    /**
-     * Call {@link MotionEvent#getX(int)}.
-     *
-     * @deprecated Call {@link MotionEvent#getX()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static float getX(MotionEvent event, int pointerIndex) {
-        return event.getX(pointerIndex);
-    }
-
-    /**
-     * Call {@link MotionEvent#getY(int)}.
-     *
-     * @deprecated Call {@link MotionEvent#getY()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static float getY(MotionEvent event, int pointerIndex) {
-        return event.getY(pointerIndex);
-    }
-
-    /**
-     * The number of pointers of data contained in this event.  Always
-     *
-     * @deprecated Call {@link MotionEvent#getPointerCount()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static int getPointerCount(MotionEvent event) {
-        return event.getPointerCount();
-    }
-
-    /**
-     * Gets the source of the event.
-     *
-     * @return The event source or {@link InputDeviceCompat#SOURCE_UNKNOWN} if unknown.
-     * @deprecated Call {@link MotionEvent#getSource()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static int getSource(MotionEvent event) {
-        return event.getSource();
-    }
-
-    /**
-     * Determines whether the event is from the given source.
-     * @param source The input source to check against.
-     * @return Whether the event is from the given source.
-     */
-    public static boolean isFromSource(MotionEvent event, int source) {
-        return (event.getSource() & source) == source;
-    }
-
-    /**
-     * Get axis value for the first pointer index (may be an
-     * arbitrary pointer identifier).
-     *
-     * @param axis The axis identifier for the axis value to retrieve.
-     *
-     * @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 event.getAxisValue(axis);
-    }
-
-    /**
-     * Returns the value of the requested axis for the given pointer <em>index</em>
-     * (use {@link #getPointerId(MotionEvent, int)} to find the pointer identifier for this index).
-     *
-     * @param axis The axis identifier for the axis value to retrieve.
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount(MotionEvent)}-1.
-     * @return The value of the axis, or 0 if the axis is not available.
-     *
-     * @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 event.getAxisValue(axis, pointerIndex);
-    }
-
-    /**
-     * @deprecated Call {@link MotionEvent#getButtonState()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static int getButtonState(MotionEvent event) {
-        return event.getButtonState();
-    }
-
-    private MotionEventCompat() {}
-}
diff --git a/android/support/v4/view/NestedScrollingChild.java b/android/support/v4/view/NestedScrollingChild.java
deleted file mode 100644
index f57dd9f..0000000
--- a/android/support/v4/view/NestedScrollingChild.java
+++ /dev/null
@@ -1,230 +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.Nullable;
-import android.support.v4.view.ViewCompat.ScrollAxis;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewParent;
-
-/**
- * This interface should be implemented by {@link android.view.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 Android
- * 5.0 Lollipop and newer.</p>
- */
-public interface NestedScrollingChild {
-    /**
-     * Enable or disable nested scrolling for this view.
-     *
-     * <p>If this property is set to true the view will be permitted to initiate nested
-     * scrolling operations with a compatible parent view in the current hierarchy. If this
-     * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
-     * while a nested scroll is in progress has the effect of {@link #stopNestedScroll() stopping}
-     * the nested scroll.</p>
-     *
-     * @param enabled true to enable nested scrolling, false to disable
-     *
-     * @see #isNestedScrollingEnabled()
-     */
-    void setNestedScrollingEnabled(boolean enabled);
-
-    /**
-     * Returns true if nested scrolling is enabled for this view.
-     *
-     * <p>If nested scrolling is enabled and this View class implementation supports it,
-     * this view will act as a nested scrolling child view when applicable, forwarding data
-     * about the scroll operation in progress to a compatible and cooperating nested scrolling
-     * parent.</p>
-     *
-     * @return true if nested scrolling is enabled
-     *
-     * @see #setNestedScrollingEnabled(boolean)
-     */
-    boolean isNestedScrollingEnabled();
-
-    /**
-     * 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
-     * of a touch scroll 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()} 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[]) 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[]) dispatchNestedScroll}, passing
-     * both the delta consumed and the delta unconsumed. A nested scrolling parent may treat
-     * these values differently. See
-     * {@link NestedScrollingParent#onNestedScroll(View, 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}.
-     * @return true if a cooperative parent was found and nested scrolling has been enabled for
-     *         the current gesture.
-     *
-     * @see #stopNestedScroll()
-     * @see #dispatchNestedPreScroll(int, int, int[], int[])
-     * @see #dispatchNestedScroll(int, int, int, int, int[])
-     */
-    boolean startNestedScroll(@ScrollAxis int axes);
-
-    /**
-     * Stop a nested scroll in progress.
-     *
-     * <p>Calling this method when a nested scroll is not currently in progress is harmless.</p>
-     *
-     * @see #startNestedScroll(int)
-     */
-    void stopNestedScroll();
-
-    /**
-     * Returns true if this view has a nested scrolling parent.
-     *
-     * <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>
-     *
-     * @return whether this view has a nested scrolling parent
-     */
-    boolean hasNestedScrollingParent();
-
-    /**
-     * 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[]) 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.
-     * @return true if the event was dispatched, false if it could not be dispatched.
-     * @see #dispatchNestedPreScroll(int, int, int[], int[])
-     */
-    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.
-     *
-     * <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.
-     * @return true if the parent consumed some or all of the scroll delta
-     * @see #dispatchNestedScroll(int, int, int, int, int[])
-     */
-    boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
-            @Nullable int[] offsetInWindow);
-
-    /**
-     * Dispatch a fling to a nested scrolling parent.
-     *
-     * <p>This method should be used to indicate that a nested scrolling child has detected
-     * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
-     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
-     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
-     * along a scrollable axis.</p>
-     *
-     * <p>If a nested scrolling child view would normally fling but it is at the edge of
-     * its own content, it can use this method to delegate the fling to its nested scrolling
-     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
-     *
-     * @param velocityX Horizontal fling velocity in pixels per second
-     * @param velocityY Vertical fling velocity in pixels per second
-     * @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
-     */
-    boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
-
-    /**
-     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
-     *
-     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
-     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
-     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
-     * before the child view consumes it. If this method returns <code>true</code>, a nested
-     * parent view consumed the fling and this view should not scroll as a result.</p>
-     *
-     * <p>For a better user experience, only one view in a nested scrolling chain should consume
-     * the fling at a time. If a parent view consumed the fling this method will return false.
-     * Custom view implementations should account for this in two ways:</p>
-     *
-     * <ul>
-     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
-     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
-     *     position regardless.</li>
-     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
-     *     even to settle back to a valid idle position.</li>
-     * </ul>
-     *
-     * <p>Views should also not offer fling velocities to nested parent views along an axis
-     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
-     * should not offer a horizontal fling velocity to its parents since scrolling along that
-     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
-     *
-     * @param velocityX Horizontal fling velocity in pixels per second
-     * @param velocityY Vertical fling velocity in pixels per second
-     * @return true if a nested scrolling parent consumed the fling
-     */
-    boolean dispatchNestedPreFling(float velocityX, float velocityY);
-}
diff --git a/android/support/v4/view/NestedScrollingChild2.java b/android/support/v4/view/NestedScrollingChild2.java
deleted file mode 100644
index ec31c65..0000000
--- a/android/support/v4/view/NestedScrollingChild2.java
+++ /dev/null
@@ -1,156 +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.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/android/support/v4/view/NestedScrollingChildHelper.java b/android/support/v4/view/NestedScrollingChildHelper.java
deleted file mode 100644
index 69a8bfd..0000000
--- a/android/support/v4/view/NestedScrollingChildHelper.java
+++ /dev/null
@@ -1,401 +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 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;
-
-/**
- * Helper class for implementing nested scrolling child views compatible with Android platform
- * versions earlier than Android 5.0 Lollipop (API 21).
- *
- * <p>{@link android.view.View View} subclasses should instantiate a final instance of this
- * class as a field at construction. For each <code>View</code> method that has a matching
- * method signature in this class, delegate the operation to the helper instance in an overridden
- * method implementation. This implements the standard framework policy for nested scrolling.</p>
- *
- * <p>Views invoking nested scrolling functionality should always do so from the relevant
- * {@link android.support.v4.view.ViewCompat}, {@link android.support.v4.view.ViewGroupCompat} or
- * {@link android.support.v4.view.ViewParentCompat} compatibility
- * shim static methods. This ensures interoperability with nested scrolling views on Android
- * 5.0 Lollipop and newer.</p>
- */
-public class NestedScrollingChildHelper {
-    private ViewParent mNestedScrollingParentTouch;
-    private ViewParent mNestedScrollingParentNonTouch;
-    private final View mView;
-    private boolean mIsNestedScrollingEnabled;
-    private int[] mTempNestedScrollConsumed;
-
-    /**
-     * Construct a new helper for a given view.
-     */
-    public NestedScrollingChildHelper(@NonNull View view) {
-        mView = view;
-    }
-
-    /**
-     * Enable nested scrolling.
-     *
-     * <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>
-     *
-     * @param enabled true to enable nested scrolling dispatch from this view, false otherwise
-     */
-    public void setNestedScrollingEnabled(boolean enabled) {
-        if (mIsNestedScrollingEnabled) {
-            ViewCompat.stopNestedScroll(mView);
-        }
-        mIsNestedScrollingEnabled = enabled;
-    }
-
-    /**
-     * Check if nested scrolling is enabled 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.NestedScrollingChild} interface method with the same
-     * signature to implement the standard policy.</p>
-     *
-     * @return true if nested scrolling is enabled for this view
-     */
-    public boolean isNestedScrollingEnabled() {
-        return mIsNestedScrollingEnabled;
-    }
-
-    /**
-     * Check if this view has a nested scrolling parent view currently receiving events for
-     * 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
-     * signature to implement the standard policy.</p>
-     *
-     * @return true if this view has a nested scrolling parent, false otherwise
-     */
-    public boolean hasNestedScrollingParent() {
-        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;
-    }
-
-    /**
-     * 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.NestedScrollingChild} interface method with the same
-     * signature to implement the standard policy.</p>
-     *
-     * @param axes Supported nested scroll axes.
-     *             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(@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;
-        }
-        if (isNestedScrollingEnabled()) {
-            ViewParent p = mView.getParent();
-            View child = mView;
-            while (p != null) {
-                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
-                    setNestedScrollingParentForType(type, p);
-                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
-                    return true;
-                }
-                if (p instanceof View) {
-                    child = (View) p;
-                }
-                p = p.getParent();
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 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.NestedScrollingChild} interface method with the same
-     * signature to implement the standard policy.</p>
-     */
-    public void stopNestedScroll() {
-        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);
-        }
-    }
-
-    /**
-     * 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.NestedScrollingChild} 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) {
-        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;
-                if (offsetInWindow != null) {
-                    mView.getLocationInWindow(offsetInWindow);
-                    startX = offsetInWindow[0];
-                    startY = offsetInWindow[1];
-                }
-
-                ViewParentCompat.onNestedScroll(parent, mView, dxConsumed,
-                        dyConsumed, dxUnconsumed, dyUnconsumed, type);
-
-                if (offsetInWindow != null) {
-                    mView.getLocationInWindow(offsetInWindow);
-                    offsetInWindow[0] -= startX;
-                    offsetInWindow[1] -= startY;
-                }
-                return true;
-            } else if (offsetInWindow != null) {
-                // No motion, no dispatch. Keep offsetInWindow up to date.
-                offsetInWindow[0] = 0;
-                offsetInWindow[1] = 0;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 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.NestedScrollingChild} 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) {
-        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;
-                if (offsetInWindow != null) {
-                    mView.getLocationInWindow(offsetInWindow);
-                    startX = offsetInWindow[0];
-                    startY = offsetInWindow[1];
-                }
-
-                if (consumed == null) {
-                    if (mTempNestedScrollConsumed == null) {
-                        mTempNestedScrollConsumed = new int[2];
-                    }
-                    consumed = mTempNestedScrollConsumed;
-                }
-                consumed[0] = 0;
-                consumed[1] = 0;
-                ViewParentCompat.onNestedPreScroll(parent, mView, dx, dy, consumed, type);
-
-                if (offsetInWindow != null) {
-                    mView.getLocationInWindow(offsetInWindow);
-                    offsetInWindow[0] -= startX;
-                    offsetInWindow[1] -= startY;
-                }
-                return consumed[0] != 0 || consumed[1] != 0;
-            } else if (offsetInWindow != null) {
-                offsetInWindow[0] = 0;
-                offsetInWindow[1] = 0;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Dispatch a nested fling 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.NestedScrollingChild} interface method with the same
-     * signature to implement the standard policy.</p>
-     *
-     * @return true if the parent consumed the nested fling
-     */
-    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
-        if (isNestedScrollingEnabled()) {
-            ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);
-            if (parent != null) {
-                return ViewParentCompat.onNestedFling(parent, mView, velocityX,
-                        velocityY, consumed);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Dispatch a nested pre-fling 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.NestedScrollingChild} interface method with the same
-     * signature to implement the standard policy.</p>
-     *
-     * @return true if the parent consumed the nested fling
-     */
-    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
-        if (isNestedScrollingEnabled()) {
-            ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);
-            if (parent != null) {
-                return ViewParentCompat.onNestedPreFling(parent, mView, velocityX,
-                        velocityY);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * View subclasses should always call this method on their
-     * <code>NestedScrollingChildHelper</code> when detached from a window.
-     *
-     * <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>
-     */
-    public void onDetachedFromWindow() {
-        ViewCompat.stopNestedScroll(mView);
-    }
-
-    /**
-     * Called when a nested scrolling child stops its current nested scroll operation.
-     *
-     * <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>
-     *
-     * @param child Child view stopping its nested scroll. This may not be a direct child view.
-     */
-    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/android/support/v4/view/NestedScrollingParent.java b/android/support/v4/view/NestedScrollingParent.java
deleted file mode 100644
index 4620f01..0000000
--- a/android/support/v4/view/NestedScrollingParent.java
+++ /dev/null
@@ -1,199 +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.NonNull;
-import android.support.v4.view.ViewCompat.ScrollAxis;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-/**
- * 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 Android
- * 5.0 Lollipop and newer.</p>
- */
-public interface 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)}.
-     * </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
-     * @return true if this ViewParent accepts the nested scroll operation
-     */
-    boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes);
-
-    /**
-     * React to the successful claiming of a nested scroll operation.
-     *
-     * <p>This method will be called after
-     * {@link #onStartNestedScroll(View, View, 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
-     * @see #onStartNestedScroll(View, View, int)
-     * @see #onStopNestedScroll(View)
-     */
-    void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes);
-
-    /**
-     * 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
-     */
-    void onStopNestedScroll(@NonNull View target);
-
-    /**
-     * 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)}.</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
-     */
-    void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
-            int dxUnconsumed, int dyUnconsumed);
-
-    /**
-     * 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
-     */
-    void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed);
-
-    /**
-     * Request a fling from a nested scroll.
-     *
-     * <p>This method signifies that a nested scrolling child has detected suitable conditions
-     * for a fling. Generally this means that a touch scroll has ended with a
-     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
-     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
-     * along a scrollable axis.</p>
-     *
-     * <p>If a nested scrolling child view would normally fling but it is at the edge of
-     * its own content, it can use this method to delegate the fling to its nested scrolling
-     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
-     *
-     * @param target View that initiated the nested scroll
-     * @param velocityX Horizontal velocity in pixels per second
-     * @param velocityY Vertical velocity in pixels per second
-     * @param consumed true if the child consumed the fling, false otherwise
-     * @return true if this parent consumed or otherwise reacted to the fling
-     */
-    boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed);
-
-    /**
-     * React to a nested fling before the target view consumes it.
-     *
-     * <p>This method siginfies that a nested scrolling child has detected a fling with the given
-     * velocity along each axis. Generally this means that a touch scroll has ended with a
-     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
-     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
-     * along a scrollable axis.</p>
-     *
-     * <p>If a nested scrolling parent is consuming motion as part of a
-     * {@link #onNestedPreScroll(View, int, int, int[]) pre-scroll}, it may be appropriate for
-     * it to also consume the pre-fling to complete that same motion. By returning
-     * <code>true</code> from this method, the parent indicates that the child should not
-     * fling its own internal content as well.</p>
-     *
-     * @param target View that initiated the nested scroll
-     * @param velocityX Horizontal velocity in pixels per second
-     * @param velocityY Vertical velocity in pixels per second
-     * @return true if this parent consumed the fling ahead of the target view
-     */
-    boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY);
-
-    /**
-     * Return the current axes of nested scrolling for this NestedScrollingParent.
-     *
-     * <p>A NestedScrollingParent returning something other than {@link ViewCompat#SCROLL_AXIS_NONE}
-     * is currently acting as a nested scrolling parent for one or more descendant views in
-     * the hierarchy.</p>
-     *
-     * @return Flags indicating the current axes of nested scrolling
-     * @see ViewCompat#SCROLL_AXIS_HORIZONTAL
-     * @see ViewCompat#SCROLL_AXIS_VERTICAL
-     * @see ViewCompat#SCROLL_AXIS_NONE
-     */
-    @ScrollAxis
-    int getNestedScrollAxes();
-}
diff --git a/android/support/v4/view/NestedScrollingParent2.java b/android/support/v4/view/NestedScrollingParent2.java
deleted file mode 100644
index 2ab463e..0000000
--- a/android/support/v4/view/NestedScrollingParent2.java
+++ /dev/null
@@ -1,149 +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.view;
-
-import android.support.annotation.NonNull;
-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, @NonNull int[] consumed,
-            @NestedScrollType int type);
-
-}
diff --git a/android/support/v4/view/NestedScrollingParentHelper.java b/android/support/v4/view/NestedScrollingParentHelper.java
deleted file mode 100644
index ad06097..0000000
--- a/android/support/v4/view/NestedScrollingParentHelper.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.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;
-
-/**
- * Helper class for implementing nested scrolling parent views compatible with Android platform
- * versions earlier than Android 5.0 Lollipop (API 21).
- *
- * <p>{@link android.view.ViewGroup ViewGroup} subclasses should instantiate a final instance
- * of this class as a field at construction. For each <code>ViewGroup</code> method that has
- * a matching method signature in this class, delegate the operation to the helper instance
- * in an overridden method implementation. This implements the standard framework policy
- * for nested scrolling.</p>
- *
- * <p>Views invoking nested scrolling functionality should always do so from the relevant
- * {@link android.support.v4.view.ViewCompat}, {@link android.support.v4.view.ViewGroupCompat} or
- * {@link android.support.v4.view.ViewParentCompat} compatibility
- * shim static methods. This ensures interoperability with nested scrolling views on Android
- * 5.0 Lollipop and newer.</p>
- */
-public class NestedScrollingParentHelper {
-    private final ViewGroup mViewGroup;
-    private int mNestedScrollAxes;
-
-    /**
-     * Construct a new helper for a given ViewGroup
-     */
-    public NestedScrollingParentHelper(@NonNull ViewGroup viewGroup) {
-        mViewGroup = viewGroup;
-    }
-
-    /**
-     * 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.NestedScrollingParent} interface method with
-     * the same signature to implement the standard policy.</p>
-     */
-    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;
-    }
-
-    /**
-     * Return the current axes of nested scrolling for this ViewGroup.
-     *
-     * <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>
-     */
-    @ScrollAxis
-    public int getNestedScrollAxes() {
-        return mNestedScrollAxes;
-    }
-
-    /**
-     * 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.NestedScrollingParent} interface method with
-     * the same signature to implement the standard policy.</p>
-     */
-    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/android/support/v4/view/OnApplyWindowInsetsListener.java b/android/support/v4/view/OnApplyWindowInsetsListener.java
deleted file mode 100644
index ac9d28f..0000000
--- a/android/support/v4/view/OnApplyWindowInsetsListener.java
+++ /dev/null
@@ -1,43 +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.view.View;
-
-/**
- * Listener for applying window insets on a view in a custom way.
- *
- * <p>Apps may choose to implement this interface if they want to apply custom policy
- * to the way that window insets are treated for a view. If an OnApplyWindowInsetsListener
- * is set, its
- * {@link #onApplyWindowInsets(android.view.View, WindowInsetsCompat) onApplyWindowInsets}
- * method will be called instead of the View's own {@code onApplyWindowInsets} method.
- * The listener may optionally call the parameter View's <code>onApplyWindowInsets</code>
- * method to apply the View's normal behavior as part of its own.</p>
- */
-public interface OnApplyWindowInsetsListener {
-    /**
-     * When {@link ViewCompat#setOnApplyWindowInsetsListener(View, OnApplyWindowInsetsListener) set}
-     * on a View, this listener method will be called instead of the view's own
-     * {@code onApplyWindowInsets} method.
-     *
-     * @param v The view applying window insets
-     * @param insets The insets to apply
-     * @return The insets supplied, minus any insets that were consumed
-     */
-    WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets);
-}
\ No newline at end of file
diff --git a/android/support/v4/view/PagerAdapter.java b/android/support/v4/view/PagerAdapter.java
deleted file mode 100644
index af8e076..0000000
--- a/android/support/v4/view/PagerAdapter.java
+++ /dev/null
@@ -1,344 +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.database.DataSetObservable;
-import android.database.DataSetObserver;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Base class providing the adapter to populate pages inside of
- * a {@link ViewPager}.  You will most likely want to use a more
- * specific implementation of this, such as
- * {@link android.support.v4.app.FragmentPagerAdapter} or
- * {@link android.support.v4.app.FragmentStatePagerAdapter}.
- *
- * <p>When you implement a PagerAdapter, you must override the following methods
- * at minimum:</p>
- * <ul>
- * <li>{@link #instantiateItem(ViewGroup, int)}</li>
- * <li>{@link #destroyItem(ViewGroup, int, Object)}</li>
- * <li>{@link #getCount()}</li>
- * <li>{@link #isViewFromObject(View, Object)}</li>
- * </ul>
- *
- * <p>PagerAdapter is more general than the adapters used for
- * {@link android.widget.AdapterView AdapterViews}. Instead of providing a
- * View recycling mechanism directly ViewPager uses callbacks to indicate the
- * steps taken during an update. A PagerAdapter may implement a form of View
- * recycling if desired or use a more sophisticated method of managing page
- * Views such as Fragment transactions where each page is represented by its
- * own Fragment.</p>
- *
- * <p>ViewPager associates each page with a key Object instead of working with
- * Views directly. This key is used to track and uniquely identify a given page
- * independent of its position in the adapter. A call to the PagerAdapter method
- * {@link #startUpdate(ViewGroup)} indicates that the contents of the ViewPager
- * are about to change. One or more calls to {@link #instantiateItem(ViewGroup, int)}
- * and/or {@link #destroyItem(ViewGroup, int, Object)} will follow, and the end
- * of an update will be signaled by a call to {@link #finishUpdate(ViewGroup)}.
- * By the time {@link #finishUpdate(ViewGroup) finishUpdate} returns the views
- * associated with the key objects returned by
- * {@link #instantiateItem(ViewGroup, int) instantiateItem} should be added to
- * the parent ViewGroup passed to these methods and the views associated with
- * the keys passed to {@link #destroyItem(ViewGroup, int, Object) destroyItem}
- * should be removed. The method {@link #isViewFromObject(View, Object)} identifies
- * whether a page View is associated with a given key object.</p>
- *
- * <p>A very simple PagerAdapter may choose to use the page Views themselves
- * as key objects, returning them from {@link #instantiateItem(ViewGroup, int)}
- * after creation and adding them to the parent ViewGroup. A matching
- * {@link #destroyItem(ViewGroup, int, Object)} implementation would remove the
- * View from the parent ViewGroup and {@link #isViewFromObject(View, Object)}
- * could be implemented as <code>return view == object;</code>.</p>
- *
- * <p>PagerAdapter supports data set changes. Data set changes must occur on the
- * main thread and must end with a call to {@link #notifyDataSetChanged()} similar
- * to AdapterView adapters derived from {@link android.widget.BaseAdapter}. A data
- * set change may involve pages being added, removed, or changing position. The
- * ViewPager will keep the current page active provided the adapter implements
- * the method {@link #getItemPosition(Object)}.</p>
- */
-public abstract class PagerAdapter {
-    private final DataSetObservable mObservable = new DataSetObservable();
-    private DataSetObserver mViewPagerObserver;
-
-    public static final int POSITION_UNCHANGED = -1;
-    public static final int POSITION_NONE = -2;
-
-    /**
-     * Return the number of views available.
-     */
-    public abstract int getCount();
-
-    /**
-     * Called when a change in the shown pages is going to start being made.
-     * @param container The containing View which is displaying this adapter's
-     * page views.
-     */
-    public void startUpdate(@NonNull ViewGroup container) {
-        startUpdate((View) container);
-    }
-
-    /**
-     * Create the page for the given position.  The adapter is responsible
-     * for adding the view to the container given here, although it only
-     * must ensure this is done by the time it returns from
-     * {@link #finishUpdate(ViewGroup)}.
-     *
-     * @param container The containing View in which the page will be shown.
-     * @param position The page position to be instantiated.
-     * @return Returns an Object representing the new page.  This does not
-     * need to be a View, but can be some other container of the page.
-     */
-    @NonNull
-    public Object instantiateItem(@NonNull ViewGroup container, int position) {
-        return instantiateItem((View) container, position);
-    }
-
-    /**
-     * Remove a page for the given position.  The adapter is responsible
-     * for removing the view from its container, although it only must ensure
-     * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
-     *
-     * @param container The containing View from which the page will be removed.
-     * @param position The page position to be removed.
-     * @param object The same object that was returned by
-     * {@link #instantiateItem(View, int)}.
-     */
-    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
-        destroyItem((View) container, position, object);
-    }
-
-    /**
-     * Called to inform the adapter of which item is currently considered to
-     * be the "primary", that is the one show to the user as the current page.
-     * This method will not be invoked when the adapter contains no items.
-     *
-     * @param container The containing View from which the page will be removed.
-     * @param position The page position that is now the primary.
-     * @param object The same object that was returned by
-     * {@link #instantiateItem(View, int)}.
-     */
-    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
-        setPrimaryItem((View) container, position, object);
-    }
-
-    /**
-     * Called when the a change in the shown pages has been completed.  At this
-     * point you must ensure that all of the pages have actually been added or
-     * removed from the container as appropriate.
-     * @param container The containing View which is displaying this adapter's
-     * page views.
-     */
-    public void finishUpdate(@NonNull ViewGroup container) {
-        finishUpdate((View) container);
-    }
-
-    /**
-     * Called when a change in the shown pages is going to start being made.
-     * @param container The containing View which is displaying this adapter's
-     * page views.
-     *
-     * @deprecated Use {@link #startUpdate(ViewGroup)}
-     */
-    @Deprecated
-    public void startUpdate(@NonNull View container) {
-    }
-
-    /**
-     * Create the page for the given position.  The adapter is responsible
-     * for adding the view to the container given here, although it only
-     * must ensure this is done by the time it returns from
-     * {@link #finishUpdate(ViewGroup)}.
-     *
-     * @param container The containing View in which the page will be shown.
-     * @param position The page position to be instantiated.
-     * @return Returns an Object representing the new page.  This does not
-     * need to be a View, but can be some other container of the page.
-     *
-     * @deprecated Use {@link #instantiateItem(ViewGroup, int)}
-     */
-    @Deprecated
-    @NonNull
-    public Object instantiateItem(@NonNull View container, int position) {
-        throw new UnsupportedOperationException(
-                "Required method instantiateItem was not overridden");
-    }
-
-    /**
-     * Remove a page for the given position.  The adapter is responsible
-     * for removing the view from its container, although it only must ensure
-     * this is done by the time it returns from {@link #finishUpdate(View)}.
-     *
-     * @param container The containing View from which the page will be removed.
-     * @param position The page position to be removed.
-     * @param object The same object that was returned by
-     * {@link #instantiateItem(View, int)}.
-     *
-     * @deprecated Use {@link #destroyItem(ViewGroup, int, Object)}
-     */
-    @Deprecated
-    public void destroyItem(@NonNull View container, int position, @NonNull Object object) {
-        throw new UnsupportedOperationException("Required method destroyItem was not overridden");
-    }
-
-    /**
-     * Called to inform the adapter of which item is currently considered to
-     * be the "primary", that is the one show to the user as the current page.
-     *
-     * @param container The containing View from which the page will be removed.
-     * @param position The page position that is now the primary.
-     * @param object The same object that was returned by
-     * {@link #instantiateItem(View, int)}.
-     *
-     * @deprecated Use {@link #setPrimaryItem(ViewGroup, int, Object)}
-     */
-    @Deprecated
-    public void setPrimaryItem(@NonNull View container, int position, @NonNull Object object) {
-    }
-
-    /**
-     * Called when the a change in the shown pages has been completed.  At this
-     * point you must ensure that all of the pages have actually been added or
-     * removed from the container as appropriate.
-     * @param container The containing View which is displaying this adapter's
-     * page views.
-     *
-     * @deprecated Use {@link #finishUpdate(ViewGroup)}
-     */
-    @Deprecated
-    public void finishUpdate(@NonNull View container) {
-    }
-
-    /**
-     * Determines whether a page View is associated with a specific key object
-     * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is
-     * required for a PagerAdapter to function properly.
-     *
-     * @param view Page View to check for association with <code>object</code>
-     * @param object Object to check for association with <code>view</code>
-     * @return true if <code>view</code> is associated with the key object <code>object</code>
-     */
-    public abstract boolean isViewFromObject(@NonNull View view, @NonNull Object object);
-
-    /**
-     * Save any instance state associated with this adapter and its pages that should be
-     * restored if the current UI state needs to be reconstructed.
-     *
-     * @return Saved state for this adapter
-     */
-    @Nullable
-    public Parcelable saveState() {
-        return null;
-    }
-
-    /**
-     * Restore any instance state associated with this adapter and its pages
-     * that was previously saved by {@link #saveState()}.
-     *
-     * @param state State previously saved by a call to {@link #saveState()}
-     * @param loader A ClassLoader that should be used to instantiate any restored objects
-     */
-    public void restoreState(@Nullable Parcelable state, @Nullable ClassLoader loader) {
-    }
-
-    /**
-     * Called when the host view is attempting to determine if an item's position
-     * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given
-     * item has not changed or {@link #POSITION_NONE} if the item is no longer present
-     * in the adapter.
-     *
-     * <p>The default implementation assumes that items will never
-     * change position and always returns {@link #POSITION_UNCHANGED}.
-     *
-     * @param object Object representing an item, previously returned by a call to
-     *               {@link #instantiateItem(View, int)}.
-     * @return object's new position index from [0, {@link #getCount()}),
-     *         {@link #POSITION_UNCHANGED} if the object's position has not changed,
-     *         or {@link #POSITION_NONE} if the item is no longer present.
-     */
-    public int getItemPosition(@NonNull Object object) {
-        return POSITION_UNCHANGED;
-    }
-
-    /**
-     * This method should be called by the application if the data backing this adapter has changed
-     * and associated views should update.
-     */
-    public void notifyDataSetChanged() {
-        synchronized (this) {
-            if (mViewPagerObserver != null) {
-                mViewPagerObserver.onChanged();
-            }
-        }
-        mObservable.notifyChanged();
-    }
-
-    /**
-     * Register an observer to receive callbacks related to the adapter's data changing.
-     *
-     * @param observer The {@link android.database.DataSetObserver} which will receive callbacks.
-     */
-    public void registerDataSetObserver(@NonNull DataSetObserver observer) {
-        mObservable.registerObserver(observer);
-    }
-
-    /**
-     * Unregister an observer from callbacks related to the adapter's data changing.
-     *
-     * @param observer The {@link android.database.DataSetObserver} which will be unregistered.
-     */
-    public void unregisterDataSetObserver(@NonNull DataSetObserver observer) {
-        mObservable.unregisterObserver(observer);
-    }
-
-    void setViewPagerObserver(DataSetObserver observer) {
-        synchronized (this) {
-            mViewPagerObserver = observer;
-        }
-    }
-
-    /**
-     * This method may be called by the ViewPager to obtain a title string
-     * to describe the specified page. This method may return null
-     * indicating no title for this page. The default implementation returns
-     * null.
-     *
-     * @param position The position of the title requested
-     * @return A title for the requested page
-     */
-    @Nullable
-    public CharSequence getPageTitle(int position) {
-        return null;
-    }
-
-    /**
-     * Returns the proportional width of a given page as a percentage of the
-     * ViewPager's measured width from (0.f-1.f]
-     *
-     * @param position The position of the page requested
-     * @return Proportional width for the given page position
-     */
-    public float getPageWidth(int position) {
-        return 1.f;
-    }
-}
diff --git a/android/support/v4/view/PagerTabStrip.java b/android/support/v4/view/PagerTabStrip.java
deleted file mode 100644
index 6c88572..0000000
--- a/android/support/v4/view/PagerTabStrip.java
+++ /dev/null
@@ -1,301 +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.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.ColorRes;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-/**
- * PagerTabStrip is an interactive indicator of the current, next,
- * and previous pages of a {@link ViewPager}. It is intended to be used as a
- * child view of a ViewPager widget in your XML layout.
- * Add it as a child of a ViewPager in your layout file and set its
- * android:layout_gravity to TOP or BOTTOM to pin it to the top or bottom
- * of the ViewPager. The title from each page is supplied by the method
- * {@link PagerAdapter#getPageTitle(int)} in the adapter supplied to
- * the ViewPager.
- *
- * <p>For a non-interactive indicator, see {@link PagerTitleStrip}.</p>
- */
-public class PagerTabStrip extends PagerTitleStrip {
-    private static final String TAG = "PagerTabStrip";
-
-    private static final int INDICATOR_HEIGHT = 3; // dp
-    private static final int MIN_PADDING_BOTTOM = INDICATOR_HEIGHT + 3; // dp
-    private static final int TAB_PADDING = 16; // dp
-    private static final int TAB_SPACING = 32; // dp
-    private static final int MIN_TEXT_SPACING = TAB_SPACING + TAB_PADDING * 2; // dp
-    private static final int FULL_UNDERLINE_HEIGHT = 1; // dp
-    private static final int MIN_STRIP_HEIGHT = 32; // dp
-
-    private int mIndicatorColor;
-    private int mIndicatorHeight;
-
-    private int mMinPaddingBottom;
-    private int mMinTextSpacing;
-    private int mMinStripHeight;
-
-    private int mTabPadding;
-
-    private final Paint mTabPaint = new Paint();
-    private final Rect mTempRect = new Rect();
-
-    private int mTabAlpha = 0xFF;
-
-    private boolean mDrawFullUnderline = false;
-    private boolean mDrawFullUnderlineSet = false;
-    private int mFullUnderlineHeight;
-
-    private boolean mIgnoreTap;
-    private float mInitialMotionX;
-    private float mInitialMotionY;
-    private int mTouchSlop;
-
-    public PagerTabStrip(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public PagerTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-
-        mIndicatorColor = mTextColor;
-        mTabPaint.setColor(mIndicatorColor);
-
-        // Note: this follows the rules for Resources#getDimensionPixelOffset/Size:
-        //       sizes round up, offsets round down.
-        final float density = context.getResources().getDisplayMetrics().density;
-        mIndicatorHeight = (int) (INDICATOR_HEIGHT * density + 0.5f);
-        mMinPaddingBottom = (int) (MIN_PADDING_BOTTOM * density + 0.5f);
-        mMinTextSpacing = (int) (MIN_TEXT_SPACING * density);
-        mTabPadding = (int) (TAB_PADDING * density + 0.5f);
-        mFullUnderlineHeight = (int) (FULL_UNDERLINE_HEIGHT * density + 0.5f);
-        mMinStripHeight = (int) (MIN_STRIP_HEIGHT * density + 0.5f);
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-
-        // Enforce restrictions
-        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
-        setTextSpacing(getTextSpacing());
-
-        setWillNotDraw(false);
-
-        mPrevText.setFocusable(true);
-        mPrevText.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mPager.setCurrentItem(mPager.getCurrentItem() - 1);
-            }
-        });
-
-        mNextText.setFocusable(true);
-        mNextText.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mPager.setCurrentItem(mPager.getCurrentItem() + 1);
-            }
-        });
-
-        if (getBackground() == null) {
-            mDrawFullUnderline = true;
-        }
-    }
-
-    /**
-     * Set the color of the tab indicator bar.
-     *
-     * @param color Color to set as an 0xRRGGBB value. The high byte (alpha) is ignored.
-     */
-    public void setTabIndicatorColor(@ColorInt int color) {
-        mIndicatorColor = color;
-        mTabPaint.setColor(mIndicatorColor);
-        invalidate();
-    }
-
-    /**
-     * Set the color of the tab indicator bar from a color resource.
-     *
-     * @param resId Resource ID of a color resource to load
-     */
-    public void setTabIndicatorColorResource(@ColorRes int resId) {
-        setTabIndicatorColor(ContextCompat.getColor(getContext(), resId));
-    }
-
-    /**
-     * @return The current tab indicator color as an 0xRRGGBB value.
-     */
-    @ColorInt
-    public int getTabIndicatorColor() {
-        return mIndicatorColor;
-    }
-
-    @Override
-    public void setPadding(int left, int top, int right, int bottom) {
-        if (bottom < mMinPaddingBottom) {
-            bottom = mMinPaddingBottom;
-        }
-        super.setPadding(left, top, right, bottom);
-    }
-
-    @Override
-    public void setTextSpacing(int textSpacing) {
-        if (textSpacing < mMinTextSpacing) {
-            textSpacing = mMinTextSpacing;
-        }
-        super.setTextSpacing(textSpacing);
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable d) {
-        super.setBackgroundDrawable(d);
-        if (!mDrawFullUnderlineSet) {
-            mDrawFullUnderline = d == null;
-        }
-    }
-
-    @Override
-    public void setBackgroundColor(@ColorInt int color) {
-        super.setBackgroundColor(color);
-        if (!mDrawFullUnderlineSet) {
-            mDrawFullUnderline = (color & 0xFF000000) == 0;
-        }
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (!mDrawFullUnderlineSet) {
-            mDrawFullUnderline = resId == 0;
-        }
-    }
-
-    /**
-     * Set whether this tab strip should draw a full-width underline in the
-     * current tab indicator color.
-     *
-     * @param drawFull true to draw a full-width underline, false otherwise
-     */
-    public void setDrawFullUnderline(boolean drawFull) {
-        mDrawFullUnderline = drawFull;
-        mDrawFullUnderlineSet = true;
-        invalidate();
-    }
-
-    /**
-     * Return whether or not this tab strip will draw a full-width underline.
-     * This defaults to true if no background is set.
-     *
-     * @return true if this tab strip will draw a full-width underline in the
-     * current tab indicator color.
-     */
-    public boolean getDrawFullUnderline() {
-        return mDrawFullUnderline;
-    }
-
-    @Override
-    int getMinHeight() {
-        return Math.max(super.getMinHeight(), mMinStripHeight);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        final int action = ev.getAction();
-        if (action != MotionEvent.ACTION_DOWN && mIgnoreTap) {
-            return false;
-        }
-
-        // Any tap within touch slop to either side of the current item
-        // will scroll to prev/next.
-        final float x = ev.getX();
-        final float y = ev.getY();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mInitialMotionX = x;
-                mInitialMotionY = y;
-                mIgnoreTap = false;
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (Math.abs(x - mInitialMotionX) > mTouchSlop
-                        || Math.abs(y - mInitialMotionY) > mTouchSlop) {
-                    mIgnoreTap = true;
-                }
-                break;
-
-            case MotionEvent.ACTION_UP:
-                if (x < mCurrText.getLeft() - mTabPadding) {
-                    mPager.setCurrentItem(mPager.getCurrentItem() - 1);
-                } else if (x > mCurrText.getRight() + mTabPadding) {
-                    mPager.setCurrentItem(mPager.getCurrentItem() + 1);
-                }
-                break;
-        }
-
-        return true;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        final int height = getHeight();
-        final int bottom = height;
-        final int left = mCurrText.getLeft() - mTabPadding;
-        final int right = mCurrText.getRight() + mTabPadding;
-        final int top = bottom - mIndicatorHeight;
-
-        mTabPaint.setColor(mTabAlpha << 24 | (mIndicatorColor & 0xFFFFFF));
-        canvas.drawRect(left, top, right, bottom, mTabPaint);
-
-        if (mDrawFullUnderline) {
-            mTabPaint.setColor(0xFF << 24 | (mIndicatorColor & 0xFFFFFF));
-            canvas.drawRect(getPaddingLeft(), height - mFullUnderlineHeight,
-                    getWidth() - getPaddingRight(), height, mTabPaint);
-        }
-    }
-
-    @Override
-    void updateTextPositions(int position, float positionOffset, boolean force) {
-        final Rect r = mTempRect;
-        int bottom = getHeight();
-        int left = mCurrText.getLeft() - mTabPadding;
-        int right = mCurrText.getRight() + mTabPadding;
-        int top = bottom - mIndicatorHeight;
-
-        r.set(left, top, right, bottom);
-
-        super.updateTextPositions(position, positionOffset, force);
-        mTabAlpha = (int) (Math.abs(positionOffset - 0.5f) * 2 * 0xFF);
-
-        left = mCurrText.getLeft() - mTabPadding;
-        right = mCurrText.getRight() + mTabPadding;
-        r.union(left, top, right, bottom);
-
-        invalidate(r);
-    }
-}
diff --git a/android/support/v4/view/PagerTitleStrip.java b/android/support/v4/view/PagerTitleStrip.java
deleted file mode 100644
index 79a6240..0000000
--- a/android/support/v4/view/PagerTitleStrip.java
+++ /dev/null
@@ -1,516 +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.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.FloatRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-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,
- * and previous pages of a {@link ViewPager}. It is intended to be used as a
- * child view of a ViewPager widget in your XML layout.
- * Add it as a child of a ViewPager in your layout file and set its
- * android:layout_gravity to TOP or BOTTOM to pin it to the top or bottom
- * of the ViewPager. The title from each page is supplied by the method
- * {@link PagerAdapter#getPageTitle(int)} in the adapter supplied to
- * the ViewPager.
- *
- * <p>For an interactive indicator, see {@link PagerTabStrip}.</p>
- */
[email protected]
-public class PagerTitleStrip extends ViewGroup {
-    ViewPager mPager;
-    TextView mPrevText;
-    TextView mCurrText;
-    TextView mNextText;
-
-    private int mLastKnownCurrentPage = -1;
-    float mLastKnownPositionOffset = -1;
-    private int mScaledTextSpacing;
-    private int mGravity;
-
-    private boolean mUpdatingText;
-    private boolean mUpdatingPositions;
-
-    private final PageListener mPageListener = new PageListener();
-
-    private WeakReference<PagerAdapter> mWatchingAdapter;
-
-    private static final int[] ATTRS = new int[] {
-        android.R.attr.textAppearance,
-        android.R.attr.textSize,
-        android.R.attr.textColor,
-        android.R.attr.gravity
-    };
-
-    private static final int[] TEXT_ATTRS = new int[] {
-        0x0101038c // android.R.attr.textAllCaps
-    };
-
-    private static final float SIDE_ALPHA = 0.6f;
-    private static final int TEXT_SPACING = 16; // dip
-
-    private int mNonPrimaryAlpha;
-    int mTextColor;
-
-    private static class SingleLineAllCapsTransform extends SingleLineTransformationMethod {
-        private Locale mLocale;
-
-        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;
-        }
-    }
-
-    private static void setSingleLineAllCaps(TextView text) {
-        text.setTransformationMethod(new SingleLineAllCapsTransform(text.getContext()));
-    }
-
-    public PagerTitleStrip(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public PagerTitleStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-
-        addView(mPrevText = new TextView(context));
-        addView(mCurrText = new TextView(context));
-        addView(mNextText = new TextView(context));
-
-        final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
-        final int textAppearance = a.getResourceId(0, 0);
-        if (textAppearance != 0) {
-            TextViewCompat.setTextAppearance(mPrevText, textAppearance);
-            TextViewCompat.setTextAppearance(mCurrText, textAppearance);
-            TextViewCompat.setTextAppearance(mNextText, textAppearance);
-        }
-        final int textSize = a.getDimensionPixelSize(1, 0);
-        if (textSize != 0) {
-            setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-        }
-        if (a.hasValue(2)) {
-            final int textColor = a.getColor(2, 0);
-            mPrevText.setTextColor(textColor);
-            mCurrText.setTextColor(textColor);
-            mNextText.setTextColor(textColor);
-        }
-        mGravity = a.getInteger(3, Gravity.BOTTOM);
-        a.recycle();
-
-        mTextColor = mCurrText.getTextColors().getDefaultColor();
-        setNonPrimaryAlpha(SIDE_ALPHA);
-
-        mPrevText.setEllipsize(TruncateAt.END);
-        mCurrText.setEllipsize(TruncateAt.END);
-        mNextText.setEllipsize(TruncateAt.END);
-
-        boolean allCaps = false;
-        if (textAppearance != 0) {
-            final TypedArray ta = context.obtainStyledAttributes(textAppearance, TEXT_ATTRS);
-            allCaps = ta.getBoolean(0, false);
-            ta.recycle();
-        }
-
-        if (allCaps) {
-            setSingleLineAllCaps(mPrevText);
-            setSingleLineAllCaps(mCurrText);
-            setSingleLineAllCaps(mNextText);
-        } else {
-            mPrevText.setSingleLine();
-            mCurrText.setSingleLine();
-            mNextText.setSingleLine();
-        }
-
-        final float density = context.getResources().getDisplayMetrics().density;
-        mScaledTextSpacing = (int) (TEXT_SPACING * density);
-    }
-
-    /**
-     * Set the required spacing between title segments.
-     *
-     * @param spacingPixels Spacing between each title displayed in pixels
-     */
-    public void setTextSpacing(int spacingPixels) {
-        mScaledTextSpacing = spacingPixels;
-        requestLayout();
-    }
-
-    /**
-     * @return The required spacing between title segments in pixels
-     */
-    public int getTextSpacing() {
-        return mScaledTextSpacing;
-    }
-
-    /**
-     * Set the alpha value used for non-primary page titles.
-     *
-     * @param alpha Opacity value in the range 0-1f
-     */
-    public void setNonPrimaryAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
-        mNonPrimaryAlpha = (int) (alpha * 255) & 0xFF;
-        final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
-        mPrevText.setTextColor(transparentColor);
-        mNextText.setTextColor(transparentColor);
-    }
-
-    /**
-     * Set the color value used as the base color for all displayed page titles.
-     * Alpha will be ignored for non-primary page titles. See {@link #setNonPrimaryAlpha(float)}.
-     *
-     * @param color Color hex code in 0xAARRGGBB format
-     */
-    public void setTextColor(@ColorInt int color) {
-        mTextColor = color;
-        mCurrText.setTextColor(color);
-        final int transparentColor = (mNonPrimaryAlpha << 24) | (mTextColor & 0xFFFFFF);
-        mPrevText.setTextColor(transparentColor);
-        mNextText.setTextColor(transparentColor);
-    }
-
-    /**
-     * Set the default text size to a given unit and value.
-     * See {@link TypedValue} for the possible dimension units.
-     *
-     * <p>Example: to set the text size to 14px, use
-     * setTextSize(TypedValue.COMPLEX_UNIT_PX, 14);</p>
-     *
-     * @param unit The desired dimension unit
-     * @param size The desired size in the given units
-     */
-    public void setTextSize(int unit, float size) {
-        mPrevText.setTextSize(unit, size);
-        mCurrText.setTextSize(unit, size);
-        mNextText.setTextSize(unit, size);
-    }
-
-    /**
-     * Set the {@link Gravity} used to position text within the title strip.
-     * Only the vertical gravity component is used.
-     *
-     * @param gravity {@link Gravity} constant for positioning title text
-     */
-    public void setGravity(int gravity) {
-        mGravity = gravity;
-        requestLayout();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        final ViewParent parent = getParent();
-        if (!(parent instanceof ViewPager)) {
-            throw new IllegalStateException(
-                    "PagerTitleStrip must be a direct child of a ViewPager.");
-        }
-
-        final ViewPager pager = (ViewPager) parent;
-        final PagerAdapter adapter = pager.getAdapter();
-
-        pager.setInternalPageChangeListener(mPageListener);
-        pager.addOnAdapterChangeListener(mPageListener);
-        mPager = pager;
-        updateAdapter(mWatchingAdapter != null ? mWatchingAdapter.get() : null, adapter);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mPager != null) {
-            updateAdapter(mPager.getAdapter(), null);
-            mPager.setInternalPageChangeListener(null);
-            mPager.removeOnAdapterChangeListener(mPageListener);
-            mPager = null;
-        }
-    }
-
-    void updateText(int currentItem, PagerAdapter adapter) {
-        final int itemCount = adapter != null ? adapter.getCount() : 0;
-        mUpdatingText = true;
-
-        CharSequence text = null;
-        if (currentItem >= 1 && adapter != null) {
-            text = adapter.getPageTitle(currentItem - 1);
-        }
-        mPrevText.setText(text);
-
-        mCurrText.setText(adapter != null && currentItem < itemCount
-                ? adapter.getPageTitle(currentItem) : null);
-
-        text = null;
-        if (currentItem + 1 < itemCount && adapter != null) {
-            text = adapter.getPageTitle(currentItem + 1);
-        }
-        mNextText.setText(text);
-
-        // Measure everything
-        final int width = getWidth() - getPaddingLeft() - getPaddingRight();
-        final int maxWidth = Math.max(0, (int) (width * 0.8f));
-        final int childWidthSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
-        final int childHeight = getHeight() - getPaddingTop() - getPaddingBottom();
-        final int maxHeight = Math.max(0, childHeight);
-        final int childHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
-        mPrevText.measure(childWidthSpec, childHeightSpec);
-        mCurrText.measure(childWidthSpec, childHeightSpec);
-        mNextText.measure(childWidthSpec, childHeightSpec);
-
-        mLastKnownCurrentPage = currentItem;
-
-        if (!mUpdatingPositions) {
-            updateTextPositions(currentItem, mLastKnownPositionOffset, false);
-        }
-
-        mUpdatingText = false;
-    }
-
-    @Override
-    public void requestLayout() {
-        if (!mUpdatingText) {
-            super.requestLayout();
-        }
-    }
-
-    void updateAdapter(PagerAdapter oldAdapter, PagerAdapter newAdapter) {
-        if (oldAdapter != null) {
-            oldAdapter.unregisterDataSetObserver(mPageListener);
-            mWatchingAdapter = null;
-        }
-        if (newAdapter != null) {
-            newAdapter.registerDataSetObserver(mPageListener);
-            mWatchingAdapter = new WeakReference<PagerAdapter>(newAdapter);
-        }
-        if (mPager != null) {
-            mLastKnownCurrentPage = -1;
-            mLastKnownPositionOffset = -1;
-            updateText(mPager.getCurrentItem(), newAdapter);
-            requestLayout();
-        }
-    }
-
-    void updateTextPositions(int position, float positionOffset, boolean force) {
-        if (position != mLastKnownCurrentPage) {
-            updateText(position, mPager.getAdapter());
-        } else if (!force && positionOffset == mLastKnownPositionOffset) {
-            return;
-        }
-
-        mUpdatingPositions = true;
-
-        final int prevWidth = mPrevText.getMeasuredWidth();
-        final int currWidth = mCurrText.getMeasuredWidth();
-        final int nextWidth = mNextText.getMeasuredWidth();
-        final int halfCurrWidth = currWidth / 2;
-
-        final int stripWidth = getWidth();
-        final int stripHeight = getHeight();
-        final int paddingLeft = getPaddingLeft();
-        final int paddingRight = getPaddingRight();
-        final int paddingTop = getPaddingTop();
-        final int paddingBottom = getPaddingBottom();
-        final int textPaddedLeft = paddingLeft + halfCurrWidth;
-        final int textPaddedRight = paddingRight + halfCurrWidth;
-        final int contentWidth = stripWidth - textPaddedLeft - textPaddedRight;
-
-        float currOffset = positionOffset + 0.5f;
-        if (currOffset > 1.f) {
-            currOffset -= 1.f;
-        }
-        final int currCenter = stripWidth - textPaddedRight - (int) (contentWidth * currOffset);
-        final int currLeft = currCenter - currWidth / 2;
-        final int currRight = currLeft + currWidth;
-
-        final int prevBaseline = mPrevText.getBaseline();
-        final int currBaseline = mCurrText.getBaseline();
-        final int nextBaseline = mNextText.getBaseline();
-        final int maxBaseline = Math.max(Math.max(prevBaseline, currBaseline), nextBaseline);
-        final int prevTopOffset = maxBaseline - prevBaseline;
-        final int currTopOffset = maxBaseline - currBaseline;
-        final int nextTopOffset = maxBaseline - nextBaseline;
-        final int alignedPrevHeight = prevTopOffset + mPrevText.getMeasuredHeight();
-        final int alignedCurrHeight = currTopOffset + mCurrText.getMeasuredHeight();
-        final int alignedNextHeight = nextTopOffset + mNextText.getMeasuredHeight();
-        final int maxTextHeight = Math.max(Math.max(alignedPrevHeight, alignedCurrHeight),
-                alignedNextHeight);
-
-        final int vgrav = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-        int prevTop;
-        int currTop;
-        int nextTop;
-        switch (vgrav) {
-            default:
-            case Gravity.TOP:
-                prevTop = paddingTop + prevTopOffset;
-                currTop = paddingTop + currTopOffset;
-                nextTop = paddingTop + nextTopOffset;
-                break;
-            case Gravity.CENTER_VERTICAL:
-                final int paddedHeight = stripHeight - paddingTop - paddingBottom;
-                final int centeredTop = (paddedHeight - maxTextHeight) / 2;
-                prevTop = centeredTop + prevTopOffset;
-                currTop = centeredTop + currTopOffset;
-                nextTop = centeredTop + nextTopOffset;
-                break;
-            case Gravity.BOTTOM:
-                final int bottomGravTop = stripHeight - paddingBottom - maxTextHeight;
-                prevTop = bottomGravTop + prevTopOffset;
-                currTop = bottomGravTop + currTopOffset;
-                nextTop = bottomGravTop + nextTopOffset;
-                break;
-        }
-
-        mCurrText.layout(currLeft, currTop, currRight,
-                currTop + mCurrText.getMeasuredHeight());
-
-        final int prevLeft = Math.min(paddingLeft, currLeft - mScaledTextSpacing - prevWidth);
-        mPrevText.layout(prevLeft, prevTop, prevLeft + prevWidth,
-                prevTop + mPrevText.getMeasuredHeight());
-
-        final int nextLeft = Math.max(stripWidth - paddingRight - nextWidth,
-                currRight + mScaledTextSpacing);
-        mNextText.layout(nextLeft, nextTop, nextLeft + nextWidth,
-                nextTop + mNextText.getMeasuredHeight());
-
-        mLastKnownPositionOffset = positionOffset;
-        mUpdatingPositions = false;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        if (widthMode != MeasureSpec.EXACTLY) {
-            throw new IllegalStateException("Must measure with an exact width");
-        }
-
-        final int heightPadding = getPaddingTop() + getPaddingBottom();
-        final int childHeightSpec = getChildMeasureSpec(heightMeasureSpec,
-                heightPadding, LayoutParams.WRAP_CONTENT);
-
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int widthPadding = (int) (widthSize * 0.2f);
-        final int childWidthSpec = getChildMeasureSpec(widthMeasureSpec,
-                widthPadding, LayoutParams.WRAP_CONTENT);
-
-        mPrevText.measure(childWidthSpec, childHeightSpec);
-        mCurrText.measure(childWidthSpec, childHeightSpec);
-        mNextText.measure(childWidthSpec, childHeightSpec);
-
-        final int height;
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode == MeasureSpec.EXACTLY) {
-            height = MeasureSpec.getSize(heightMeasureSpec);
-        } else {
-            final int textHeight = mCurrText.getMeasuredHeight();
-            final int minHeight = getMinHeight();
-            height = Math.max(minHeight, textHeight + heightPadding);
-        }
-
-        final int childState = mCurrText.getMeasuredState();
-        final int measuredHeight = View.resolveSizeAndState(height, heightMeasureSpec,
-                childState << View.MEASURED_HEIGHT_STATE_SHIFT);
-        setMeasuredDimension(widthSize, measuredHeight);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        if (mPager != null) {
-            final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
-            updateTextPositions(mLastKnownCurrentPage, offset, true);
-        }
-    }
-
-    int getMinHeight() {
-        int minHeight = 0;
-        final Drawable bg = getBackground();
-        if (bg != null) {
-            minHeight = bg.getIntrinsicHeight();
-        }
-        return minHeight;
-    }
-
-    private class PageListener extends DataSetObserver implements ViewPager.OnPageChangeListener,
-            ViewPager.OnAdapterChangeListener {
-        private int mScrollState;
-
-        PageListener() {
-        }
-
-        @Override
-        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-            if (positionOffset > 0.5f) {
-                // Consider ourselves to be on the next page when we're 50% of the way there.
-                position++;
-            }
-            updateTextPositions(position, positionOffset, false);
-        }
-
-        @Override
-        public void onPageSelected(int position) {
-            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
-                // Only update the text here if we're not dragging or settling.
-                updateText(mPager.getCurrentItem(), mPager.getAdapter());
-
-                final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
-                updateTextPositions(mPager.getCurrentItem(), offset, true);
-            }
-        }
-
-        @Override
-        public void onPageScrollStateChanged(int state) {
-            mScrollState = state;
-        }
-
-        @Override
-        public void onAdapterChanged(ViewPager viewPager, PagerAdapter oldAdapter,
-                PagerAdapter newAdapter) {
-            updateAdapter(oldAdapter, newAdapter);
-        }
-
-        @Override
-        public void onChanged() {
-            updateText(mPager.getCurrentItem(), mPager.getAdapter());
-
-            final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
-            updateTextPositions(mPager.getCurrentItem(), offset, true);
-        }
-    }
-}
diff --git a/android/support/v4/view/PointerIconCompat.java b/android/support/v4/view/PointerIconCompat.java
deleted file mode 100644
index d9e65e1..0000000
--- a/android/support/v4/view/PointerIconCompat.java
+++ /dev/null
@@ -1,184 +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 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.view.PointerIcon;
-
-/**
- * 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} */
-    public static final int TYPE_NULL = 0;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_ARROW} */
-    public static final int TYPE_ARROW = 1000;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_CONTEXT_MENU} */
-    public static final int TYPE_CONTEXT_MENU = 1001;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_HAND} */
-    public static final int TYPE_HAND = 1002;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_HELP} */
-    public static final int TYPE_HELP = 1003;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_WAIT} */
-    public static final int TYPE_WAIT = 1004;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_CELL} */
-    public static final int TYPE_CELL = 1006;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_CROSSHAIR} */
-    public static final int TYPE_CROSSHAIR = 1007;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_TEXT} */
-    public static final int TYPE_TEXT = 1008;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_VERTICAL_TEXT} */
-    public static final int TYPE_VERTICAL_TEXT = 1009;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_ALIAS} */
-    public static final int TYPE_ALIAS = 1010;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_COPY} */
-    public static final int TYPE_COPY = 1011;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_NO_DROP} */
-    public static final int TYPE_NO_DROP = 1012;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_ALL_SCROLL} */
-    public static final int TYPE_ALL_SCROLL = 1013;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_HORIZONTAL_DOUBLE_ARROW} */
-    public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_VERTICAL_DOUBLE_ARROW} */
-    public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW} */
-    public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW} */
-    public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_ZOOM_IN} */
-    public static final int TYPE_ZOOM_IN = 1018;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_ZOOM_OUT} */
-    public static final int TYPE_ZOOM_OUT = 1019;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_GRAB} */
-    public static final int TYPE_GRAB = 1020;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_GRABBING} */
-    public static final int TYPE_GRABBING = 1021;
-
-    /** Synonym for {@link android.view.PointerIcon#TYPE_DEFAULT} */
-    public static final int TYPE_DEFAULT = TYPE_ARROW;
-
-
-    private Object mPointerIcon;
-
-    private PointerIconCompat(Object pointerIcon) {
-        mPointerIcon = pointerIcon;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public Object getPointerIcon() {
-        return mPointerIcon;
-    }
-
-    /**
-     * Gets a system pointer icon for the given style.
-     * If style is not recognized, returns the default pointer icon.
-     *
-     * @param context The context.
-     * @param style The pointer icon style.
-     * @return The pointer icon.
-     *
-     * @throws IllegalArgumentException if context is null.
-     */
-    public static PointerIconCompat getSystemIcon(Context context, int style) {
-        if (SDK_INT >= 24) {
-            return new PointerIconCompat(PointerIcon.getSystemIcon(context, style));
-        } else {
-            return new PointerIconCompat(null);
-        }
-    }
-
-    /**
-     * Creates a custom pointer from the given bitmap and hotspot information.
-     *
-     * @param bitmap The bitmap for the icon.
-     * @param hotSpotX The X offset of the pointer icon hotspot in the bitmap.
-     *        Must be within the [0, bitmap.getWidth()) range.
-     * @param hotSpotY The Y offset of the pointer icon hotspot in the bitmap.
-     *        Must be within the [0, bitmap.getHeight()) range.
-     * @return A pointer icon for this bitmap.
-     *
-     * @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
-     *         parameters are invalid.
-     */
-    public static PointerIconCompat create(Bitmap bitmap, float hotSpotX, float hotSpotY) {
-        if (SDK_INT >= 24) {
-            return new PointerIconCompat(PointerIcon.create(bitmap, hotSpotX, hotSpotY));
-        } else {
-            return new PointerIconCompat(null);
-        }
-    }
-
-    /**
-     * Loads a custom pointer icon from an XML resource.
-     * <p>
-     * The XML resource should have the following form:
-     * <code>
-     * &lt;?xml version="1.0" encoding="utf-8"?&gt;
-     * &lt;pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
-     *   android:bitmap="@drawable/my_pointer_bitmap"
-     *   android:hotSpotX="24"
-     *   android:hotSpotY="24" /&gt;
-     * </code>
-     * </p>
-     *
-     * @param resources The resources object.
-     * @param resourceId The resource id.
-     * @return The pointer icon.
-     *
-     * @throws IllegalArgumentException if resources is null.
-     * @throws Resources.NotFoundException if the resource was not found or the drawable
-     * linked in the resource was not found.
-     */
-    public static PointerIconCompat load(Resources resources, int resourceId) {
-        if (SDK_INT >= 24) {
-            return new PointerIconCompat(PointerIcon.load(resources, resourceId));
-        } else {
-            return new PointerIconCompat(null);
-        }
-    }
-}
diff --git a/android/support/v4/view/ScaleGestureDetectorCompat.java b/android/support/v4/view/ScaleGestureDetectorCompat.java
deleted file mode 100644
index 6233dff..0000000
--- a/android/support/v4/view/ScaleGestureDetectorCompat.java
+++ /dev/null
@@ -1,84 +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.os.Build;
-import android.view.ScaleGestureDetector;
-
-/**
- * Helper for accessing features in {@link ScaleGestureDetector}.
- */
-public final class ScaleGestureDetectorCompat {
-    private ScaleGestureDetectorCompat() {}
-
-    /**
-     * 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) {
-        ScaleGestureDetectorCompat.setQuickScaleEnabled(
-                (ScaleGestureDetector) scaleGestureDetector, enabled);
-    }
-
-    /**
-     * 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 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/android/support/v4/view/ScrollingView.java b/android/support/v4/view/ScrollingView.java
deleted file mode 100644
index 4fe0507..0000000
--- a/android/support/v4/view/ScrollingView.java
+++ /dev/null
@@ -1,134 +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;
-
-/**
- * An interface that can be implemented by Views to provide scroll related APIs.
- */
-public interface ScrollingView {
-    /**
-     * <p>Compute the horizontal range that the horizontal scrollbar
-     * represents.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeHorizontalScrollExtent()} and
-     * {@link #computeHorizontalScrollOffset()}.</p>
-     *
-     * <p>The default range is the drawing width of this view.</p>
-     *
-     * @return the total horizontal range represented by the horizontal
-     *         scrollbar
-     *
-     * @see #computeHorizontalScrollExtent()
-     * @see #computeHorizontalScrollOffset()
-     * @see android.widget.ScrollBarDrawable
-     */
-    int computeHorizontalScrollRange();
-
-    /**
-     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb
-     * within the horizontal range. This value is used to compute the position
-     * of the thumb within the scrollbar's track.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeHorizontalScrollRange()} and
-     * {@link #computeHorizontalScrollExtent()}.</p>
-     *
-     * <p>The default offset is the scroll offset of this view.</p>
-     *
-     * @return the horizontal offset of the scrollbar's thumb
-     *
-     * @see #computeHorizontalScrollRange()
-     * @see #computeHorizontalScrollExtent()
-     * @see android.widget.ScrollBarDrawable
-     */
-    int computeHorizontalScrollOffset();
-
-    /**
-     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb
-     * within the horizontal range. This value is used to compute the length
-     * of the thumb within the scrollbar's track.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeHorizontalScrollRange()} and
-     * {@link #computeHorizontalScrollOffset()}.</p>
-     *
-     * <p>The default extent is the drawing width of this view.</p>
-     *
-     * @return the horizontal extent of the scrollbar's thumb
-     *
-     * @see #computeHorizontalScrollRange()
-     * @see #computeHorizontalScrollOffset()
-     * @see android.widget.ScrollBarDrawable
-     */
-    int computeHorizontalScrollExtent();
-
-    /**
-     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeVerticalScrollExtent()} and
-     * {@link #computeVerticalScrollOffset()}.</p>
-     *
-     * @return the total vertical range represented by the vertical scrollbar
-     *
-     * <p>The default range is the drawing height of this view.</p>
-     *
-     * @see #computeVerticalScrollExtent()
-     * @see #computeVerticalScrollOffset()
-     * @see android.widget.ScrollBarDrawable
-     */
-    int computeVerticalScrollRange();
-
-    /**
-     * <p>Compute the vertical offset of the vertical scrollbar's thumb
-     * within the horizontal range. This value is used to compute the position
-     * of the thumb within the scrollbar's track.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeVerticalScrollRange()} and
-     * {@link #computeVerticalScrollExtent()}.</p>
-     *
-     * <p>The default offset is the scroll offset of this view.</p>
-     *
-     * @return the vertical offset of the scrollbar's thumb
-     *
-     * @see #computeVerticalScrollRange()
-     * @see #computeVerticalScrollExtent()
-     * @see android.widget.ScrollBarDrawable
-     */
-    int computeVerticalScrollOffset();
-
-    /**
-     * <p>Compute the vertical extent of the vertical scrollbar's thumb
-     * within the vertical range. This value is used to compute the length
-     * of the thumb within the scrollbar's track.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the
-     * units used by {@link #computeVerticalScrollRange()} and
-     * {@link #computeVerticalScrollOffset()}.</p>
-     *
-     * <p>The default extent is the drawing height of this view.</p>
-     *
-     * @return the vertical extent of the scrollbar's thumb
-     *
-     * @see #computeVerticalScrollRange()
-     * @see #computeVerticalScrollOffset()
-     * @see android.widget.ScrollBarDrawable
-     */
-    int computeVerticalScrollExtent();
-}
diff --git a/android/support/v4/view/TintableBackgroundView.java b/android/support/v4/view/TintableBackgroundView.java
deleted file mode 100644
index 9a22d7e..0000000
--- a/android/support/v4/view/TintableBackgroundView.java
+++ /dev/null
@@ -1,70 +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.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.support.annotation.Nullable;
-
-/**
- * Interface which allows a {@link android.view.View} to receive background tinting calls from
- * {@link ViewCompat} when running on API v20 devices or lower.
- */
-public interface TintableBackgroundView {
-
-    /**
-     * Applies a tint to the background drawable. Does not modify the current tint
-     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
-     * <p>
-     * Subsequent calls to {@code View.setBackground(Drawable)} will 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
-     *
-     * @see #getSupportBackgroundTintList()
-     */
-    void setSupportBackgroundTintList(@Nullable ColorStateList tint);
-
-    /**
-     * Return the tint applied to the background drawable, if specified.
-     *
-     * @return the tint applied to the background drawable
-     */
-    @Nullable
-    ColorStateList getSupportBackgroundTintList();
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setSupportBackgroundTintList(ColorStateList)}} to the background
-     * 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 #getSupportBackgroundTintMode()
-     */
-    void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode);
-
-    /**
-     * Return the blending mode used to apply the tint to the background
-     * drawable, if specified.
-     *
-     * @return the blending mode used to apply the tint to the background
-     *         drawable
-     */
-    @Nullable
-    PorterDuff.Mode getSupportBackgroundTintMode();
-}
diff --git a/android/support/v4/view/VelocityTrackerCompat.java b/android/support/v4/view/VelocityTrackerCompat.java
deleted file mode 100644
index d536085..0000000
--- a/android/support/v4/view/VelocityTrackerCompat.java
+++ /dev/null
@@ -1,53 +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.view.VelocityTracker;
-
-/**
- * Helper for accessing features in {@link VelocityTracker}.
- *
- * @deprecated Use {@link VelocityTracker} directly.
- */
-@Deprecated
-public final class VelocityTrackerCompat {
-    /**
-     * 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 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 tracker.getYVelocity(pointerId);
-    }
-
-    private VelocityTrackerCompat() {}
-}
diff --git a/android/support/v4/view/ViewCompat.java b/android/support/v4/view/ViewCompat.java
deleted file mode 100644
index abdaa1a..0000000
--- a/android/support/v4/view/ViewCompat.java
+++ /dev/null
@@ -1,3991 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-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.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;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Helper for accessing features in {@link View}.
- */
-public class ViewCompat {
-    private static final String TAG = "ViewCompat";
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN,
-            View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FocusDirection {}
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({View.FOCUS_LEFT, View.FOCUS_UP, View.FOCUS_RIGHT, View.FOCUS_DOWN})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FocusRealDirection {}
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({View.FOCUS_FORWARD, View.FOCUS_BACKWARD})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FocusRelativeDirection {}
-
-    @IntDef({OVER_SCROLL_ALWAYS, OVER_SCROLL_IF_CONTENT_SCROLLS, OVER_SCROLL_NEVER})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface OverScroll {}
-
-    /**
-     * Always allow a user to over-scroll this view, provided it is a
-     * view that can scroll.
-     * @deprecated Use {@link View#OVER_SCROLL_ALWAYS} directly. This constant will be removed in
-     * a future release.
-     */
-    @Deprecated
-    public static final int OVER_SCROLL_ALWAYS = 0;
-
-    /**
-     * Allow a user to over-scroll this view only if the content is large
-     * enough to meaningfully scroll, provided it is a view that can scroll.
-     * @deprecated Use {@link View#OVER_SCROLL_IF_CONTENT_SCROLLS} directly. This constant will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
-
-    /**
-     * Never allow a user to over-scroll this view.
-     * @deprecated Use {@link View#OVER_SCROLL_NEVER} directly. This constant will be removed in
-     * a future release.
-     */
-    @Deprecated
-    public static final int OVER_SCROLL_NEVER = 2;
-
-    @TargetApi(Build.VERSION_CODES.O)
-    @IntDef({
-            View.IMPORTANT_FOR_AUTOFILL_AUTO,
-            View.IMPORTANT_FOR_AUTOFILL_YES,
-            View.IMPORTANT_FOR_AUTOFILL_NO,
-            View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
-            View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface AutofillImportance {}
-
-    @IntDef({
-            IMPORTANT_FOR_ACCESSIBILITY_AUTO,
-            IMPORTANT_FOR_ACCESSIBILITY_YES,
-            IMPORTANT_FOR_ACCESSIBILITY_NO,
-            IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ImportantForAccessibility {}
-
-    /**
-     * Automatically determine whether a view is important for accessibility.
-     */
-    public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0x00000000;
-
-    /**
-     * The view is important for accessibility.
-     */
-    public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 0x00000001;
-
-    /**
-     * The view is not important for accessibility.
-     */
-    public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002;
-
-    /**
-     * The view is not important for accessibility, nor are any of its
-     * descendant views.
-     */
-    public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 0x00000004;
-
-    @IntDef({
-            ACCESSIBILITY_LIVE_REGION_NONE,
-            ACCESSIBILITY_LIVE_REGION_POLITE,
-            ACCESSIBILITY_LIVE_REGION_ASSERTIVE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface AccessibilityLiveRegion {}
-
-    /**
-     * Live region mode specifying that accessibility services should not
-     * automatically announce changes to this view. This is the default live
-     * region mode for most views.
-     * <p>
-     * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
-     */
-    public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0x00000000;
-
-    /**
-     * Live region mode specifying that accessibility services should announce
-     * changes to this view.
-     * <p>
-     * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
-     */
-    public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001;
-
-    /**
-     * Live region mode specifying that accessibility services should interrupt
-     * ongoing speech to immediately announce changes to this view.
-     * <p>
-     * Use with {@link ViewCompat#setAccessibilityLiveRegion(View, int)}.
-     */
-    public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 0x00000002;
-
-    @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;
-
-    /**
-     * <p>Indicates that the view has a software layer. A software layer is backed
-     * by a bitmap and causes the view to be rendered using Android's software
-     * rendering pipeline, even if hardware acceleration is enabled.</p>
-     *
-     * <p>Software layers have various usages:</p>
-     * <p>When the application is not using hardware acceleration, a software layer
-     * is useful to apply a specific color filter and/or blending mode and/or
-     * translucency to a view and all its children.</p>
-     * <p>When the application is using hardware acceleration, a software layer
-     * is useful to render drawing primitives not supported by the hardware
-     * accelerated pipeline. It can also be used to cache a complex view tree
-     * into a texture and reduce the complexity of drawing operations. For instance,
-     * when animating a complex view tree with a translation, a software layer can
-     * be used to render the view tree only once.</p>
-     * <p>Software layers should be avoided when the affected view tree updates
-     * often. Every update will require to re-render the software layer, which can
-     * 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;
-
-    /**
-     * <p>Indicates that the view has a hardware layer. A hardware layer is backed
-     * by a hardware specific texture (generally Frame Buffer Objects or FBO on
-     * 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 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>
-     * <p>A hardware layer can be used to cache a complex view tree into a
-     * texture and reduce the complexity of drawing operations. For instance,
-     * when animating a complex view tree with a translation, a hardware layer can
-     * be used to render the view tree only once.</p>
-     * <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({
-            LAYOUT_DIRECTION_LTR,
-            LAYOUT_DIRECTION_RTL,
-            LAYOUT_DIRECTION_INHERIT,
-            LAYOUT_DIRECTION_LOCALE})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface LayoutDirectionMode {}
-
-    @IntDef({
-            LAYOUT_DIRECTION_LTR,
-            LAYOUT_DIRECTION_RTL
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ResolvedLayoutDirectionMode {}
-
-    /**
-     * Horizontal layout direction of this view is from Left to Right.
-     */
-    public static final int LAYOUT_DIRECTION_LTR = 0;
-
-    /**
-     * Horizontal layout direction of this view is from Right to Left.
-     */
-    public static final int LAYOUT_DIRECTION_RTL = 1;
-
-    /**
-     * Horizontal layout direction of this view is inherited from its parent.
-     * Use with {@link #setLayoutDirection}.
-     */
-    public static final int LAYOUT_DIRECTION_INHERIT = 2;
-
-    /**
-     * Horizontal layout direction of this view is from deduced from the default language
-     * script for the locale. Use with {@link #setLayoutDirection}.
-     */
-    public static final int LAYOUT_DIRECTION_LOCALE = 3;
-
-    /**
-     * 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;
-
-    /**
-     * Bit shift of {@link #MEASURED_STATE_MASK} to get to the height bits
-     * 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;
-
-    /**
-     * Indicates scrolling along the horizontal axis.
-     */
-    public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
-
-    /**
-     * Indicates scrolling along the vertical axis.
-     */
-    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 = {
-                    SCROLL_INDICATOR_TOP,
-                    SCROLL_INDICATOR_BOTTOM,
-                    SCROLL_INDICATOR_LEFT,
-                    SCROLL_INDICATOR_RIGHT,
-                    SCROLL_INDICATOR_START,
-                    SCROLL_INDICATOR_END,
-            })
-    public @interface ScrollIndicators {}
-
-    /**
-     * Scroll indicator direction for the top edge of the view.
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static final int SCROLL_INDICATOR_TOP = 0x1;
-
-    /**
-     * Scroll indicator direction for the bottom edge of the view.
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static final int SCROLL_INDICATOR_BOTTOM = 0x2;
-
-    /**
-     * Scroll indicator direction for the left edge of the view.
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static final int SCROLL_INDICATOR_LEFT = 0x4;
-
-    /**
-     * Scroll indicator direction for the right edge of the view.
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static final int SCROLL_INDICATOR_RIGHT = 0x8;
-
-    /**
-     * Scroll indicator direction for the starting edge of the view.
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static final int SCROLL_INDICATOR_START = 0x10;
-
-    /**
-     * Scroll indicator direction for the ending edge of the view.
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static final int SCROLL_INDICATOR_END = 0x20;
-
-    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 static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
-        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;
-
-        public void setAutofillHints(@NonNull View v, @Nullable String... autofillHints) {
-            // no-op
-        }
-
-        public void setAccessibilityDelegate(View v,
-                @Nullable AccessibilityDelegateCompat delegate) {
-            v.setAccessibilityDelegate(delegate == null ? null : delegate.getBridge());
-        }
-
-        public boolean hasAccessibilityDelegate(View v) {
-            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;
-            }
-        }
-
-        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
-            v.onInitializeAccessibilityNodeInfo(info.unwrap());
-        }
-
-        @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;
-        }
-
-        public void setHasTransientState(View view, boolean hasTransientState) {
-            // Do nothing; API doesn't exist
-        }
-
-        public void postInvalidateOnAnimation(View view) {
-            view.postInvalidate();
-        }
-
-        public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) {
-            view.postInvalidate(left, top, right, bottom);
-        }
-
-        public void postOnAnimation(View view, Runnable action) {
-            view.postDelayed(action, getFrameTime());
-        }
-
-        public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
-            view.postDelayed(action, getFrameTime() + delayMillis);
-        }
-
-        long getFrameTime() {
-            return ValueAnimator.getFrameDelay();
-        }
-
-        public int getImportantForAccessibility(View view) {
-            return 0;
-        }
-
-        public void setImportantForAccessibility(View view, int mode) {
-        }
-
-        public boolean isImportantForAccessibility(View view) {
-            return true;
-        }
-
-        public boolean performAccessibilityAction(View view, int action, Bundle arguments) {
-            return false;
-        }
-
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
-            return null;
-        }
-
-        public int getLabelFor(View view) {
-            return 0;
-        }
-
-        public void setLabelFor(View view, int id) {
-        }
-
-        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;
-        }
-
-        public void setLayoutDirection(View view, int layoutDirection) {
-            // No-op
-        }
-
-        public ViewParent getParentForAccessibility(View view) {
-            return view.getParent();
-        }
-
-        public int getAccessibilityLiveRegion(View view) {
-            return ACCESSIBILITY_LIVE_REGION_NONE;
-        }
-
-        public void setAccessibilityLiveRegion(View view, int mode) {
-            // No-op
-        }
-
-        public int getPaddingStart(View view) {
-            return view.getPaddingLeft();
-        }
-
-        public int getPaddingEnd(View view) {
-            return view.getPaddingRight();
-        }
-
-        public void setPaddingRelative(View view, int start, int top, int end, int bottom) {
-            view.setPadding(start, top, end, bottom);
-        }
-
-        public void dispatchStartTemporaryDetach(View view) {
-            if (!mTempDetachBound) {
-                bindTempDetach();
-            }
-            if (mDispatchStartTemporaryDetach != null) {
-                try {
-                    mDispatchStartTemporaryDetach.invoke(view);
-                } catch (Exception e) {
-                    Log.d(TAG, "Error calling dispatchStartTemporaryDetach", e);
-                }
-            } else {
-                // Try this instead
-                view.onStartTemporaryDetach();
-            }
-        }
-
-        public void dispatchFinishTemporaryDetach(View view) {
-            if (!mTempDetachBound) {
-                bindTempDetach();
-            }
-            if (mDispatchFinishTemporaryDetach != null) {
-                try {
-                    mDispatchFinishTemporaryDetach.invoke(view);
-                } catch (Exception e) {
-                    Log.d(TAG, "Error calling dispatchFinishTemporaryDetach", e);
-                }
-            } else {
-                // Try this instead
-                view.onFinishTemporaryDetach();
-            }
-        }
-
-        public boolean hasOverlappingRendering(View view) {
-            return true;
-        }
-
-        private void bindTempDetach() {
-            try {
-                mDispatchStartTemporaryDetach = View.class.getDeclaredMethod(
-                        "dispatchStartTemporaryDetach");
-                mDispatchFinishTemporaryDetach = View.class.getDeclaredMethod(
-                        "dispatchFinishTemporaryDetach");
-            } catch (NoSuchMethodException e) {
-                Log.e(TAG, "Couldn't find method", e);
-            }
-            mTempDetachBound = true;
-        }
-
-        public 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;
-        }
-
-        public 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;
-        }
-
-        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;
-        }
-
-        public void setTransitionName(View view, String transitionName) {
-            if (sTransitionNameMap == null) {
-                sTransitionNameMap = new WeakHashMap<>();
-            }
-            sTransitionNameMap.put(view, transitionName);
-        }
-
-        public String getTransitionName(View view) {
-            if (sTransitionNameMap == null) {
-                return null;
-            }
-            return sTransitionNameMap.get(view);
-        }
-
-        public int getWindowSystemUiVisibility(View view) {
-            return 0;
-        }
-
-        public void requestApplyInsets(View view) {
-        }
-
-        public void setElevation(View view, float elevation) {
-        }
-
-        public float getElevation(View view) {
-            return 0f;
-        }
-
-        public void setTranslationZ(View view, float translationZ) {
-        }
-
-        public float getTranslationZ(View view) {
-            return 0f;
-        }
-
-        public void setClipBounds(View view, Rect clipBounds) {
-        }
-
-        public Rect getClipBounds(View view) {
-            return null;
-        }
-
-        public void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) {
-            if (sChildrenDrawingOrderMethod == null) {
-                try {
-                    sChildrenDrawingOrderMethod = ViewGroup.class
-                            .getDeclaredMethod("setChildrenDrawingOrderEnabled", boolean.class);
-                } catch (NoSuchMethodException e) {
-                    Log.e(TAG, "Unable to find childrenDrawingOrderEnabled", e);
-                }
-                sChildrenDrawingOrderMethod.setAccessible(true);
-            }
-            try {
-                sChildrenDrawingOrderMethod.invoke(viewGroup, enabled);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e);
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e);
-            } catch (InvocationTargetException e) {
-                Log.e(TAG, "Unable to invoke childrenDrawingOrderEnabled", e);
-            }
-        }
-
-        public boolean getFitsSystemWindows(View view) {
-            return false;
-        }
-
-        public void setOnApplyWindowInsetsListener(View view,
-                OnApplyWindowInsetsListener listener) {
-            // noop
-        }
-
-        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
-            return insets;
-        }
-
-        public WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
-            return insets;
-        }
-
-        public boolean isPaddingRelative(View view) {
-            return false;
-        }
-
-        public void setNestedScrollingEnabled(View view, boolean enabled) {
-            if (view instanceof NestedScrollingChild) {
-                ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled);
-            }
-        }
-
-        public boolean isNestedScrollingEnabled(View view) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).isNestedScrollingEnabled();
-            }
-            return false;
-        }
-
-        public void setBackground(View view, Drawable background) {
-            view.setBackgroundDrawable(background);
-        }
-
-        public ColorStateList getBackgroundTintList(View view) {
-            return (view instanceof TintableBackgroundView)
-                    ? ((TintableBackgroundView) view).getSupportBackgroundTintList()
-                    : null;
-        }
-
-        public void setBackgroundTintList(View view, ColorStateList tintList) {
-            if (view instanceof TintableBackgroundView) {
-                ((TintableBackgroundView) view).setSupportBackgroundTintList(tintList);
-            }
-        }
-
-        public void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
-            if (view instanceof TintableBackgroundView) {
-                ((TintableBackgroundView) view).setSupportBackgroundTintMode(mode);
-            }
-        }
-
-        public PorterDuff.Mode getBackgroundTintMode(View view) {
-            return (view instanceof TintableBackgroundView)
-                    ? ((TintableBackgroundView) view).getSupportBackgroundTintMode()
-                    : null;
-        }
-
-        public boolean startNestedScroll(View view, int axes) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).startNestedScroll(axes);
-            }
-            return false;
-        }
-
-        public void stopNestedScroll(View view) {
-            if (view instanceof NestedScrollingChild) {
-                ((NestedScrollingChild) view).stopNestedScroll();
-            }
-        }
-
-        public boolean hasNestedScrollingParent(View view) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).hasNestedScrollingParent();
-            }
-            return false;
-        }
-
-        public boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
-                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).dispatchNestedScroll(dxConsumed, dyConsumed,
-                        dxUnconsumed, dyUnconsumed, offsetInWindow);
-            }
-            return false;
-        }
-
-        public boolean dispatchNestedPreScroll(View view, int dx, int dy,
-                int[] consumed, int[] offsetInWindow) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).dispatchNestedPreScroll(dx, dy, consumed,
-                        offsetInWindow);
-            }
-            return false;
-        }
-
-        public boolean dispatchNestedFling(View view, float velocityX, float velocityY,
-                boolean consumed) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).dispatchNestedFling(velocityX, velocityY,
-                        consumed);
-            }
-            return false;
-        }
-
-        public boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
-            if (view instanceof NestedScrollingChild) {
-                return ((NestedScrollingChild) view).dispatchNestedPreFling(velocityX, velocityY);
-            }
-            return false;
-        }
-
-        public boolean isInLayout(View view) {
-            return false;
-        }
-
-        public boolean isLaidOut(View view) {
-            return view.getWidth() > 0 && view.getHeight() > 0;
-        }
-
-        public boolean isLayoutDirectionResolved(View view) {
-            return false;
-        }
-
-        public float getZ(View view) {
-            return getTranslationZ(view) + getElevation(view);
-        }
-
-        public void setZ(View view, float z) {
-            // no-op
-        }
-
-        public boolean isAttachedToWindow(View view) {
-            return view.getWindowToken() != null;
-        }
-
-        public boolean hasOnClickListeners(View view) {
-            return false;
-        }
-
-        public int getScrollIndicators(View view) {
-            return 0;
-        }
-
-        public void setScrollIndicators(View view, int indicators) {
-            // no-op
-        }
-
-        public void setScrollIndicators(View view, int indicators, int mask) {
-            // no-op
-        }
-
-        public 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);
-                }
-            }
-        }
-
-        public 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);
-                }
-            }
-        }
-
-        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
-        }
-
-        public Display getDisplay(View 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();
-        }
-
-        @TargetApi(Build.VERSION_CODES.O)
-        public @AutofillImportance int getImportantForAutofill(@NonNull View v) {
-            return View.IMPORTANT_FOR_AUTOFILL_AUTO;
-        }
-
-        public void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) {
-            // no-op
-        }
-
-        public boolean isImportantForAutofill(@NonNull View v) {
-            return true;
-        }
-
-        /**
-         * {@link ViewCompat#generateViewId()}
-         */
-        public int generateViewId() {
-            for (;;) {
-                final int result = sNextGeneratedId.get();
-                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
-                int newValue = result + 1;
-                if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
-                if (sNextGeneratedId.compareAndSet(result, newValue)) {
-                    return result;
-                }
-            }
-        }
-    }
-
-    @RequiresApi(15)
-    static class ViewCompatApi15Impl extends ViewCompatBaseImpl {
-        @Override
-        public boolean hasOnClickListeners(View view) {
-            return view.hasOnClickListeners();
-        }
-    }
-
-    @RequiresApi(16)
-    static class ViewCompatApi16Impl extends ViewCompatApi15Impl {
-        @Override
-        public boolean hasTransientState(View view) {
-            return view.hasTransientState();
-        }
-        @Override
-        public void setHasTransientState(View view, boolean hasTransientState) {
-            view.setHasTransientState(hasTransientState);
-        }
-        @Override
-        public void postInvalidateOnAnimation(View view) {
-            view.postInvalidateOnAnimation();
-        }
-        @Override
-        public void postInvalidateOnAnimation(View view, int left, int top, int right, int bottom) {
-            view.postInvalidateOnAnimation(left, top, right, bottom);
-        }
-        @Override
-        public void postOnAnimation(View view, Runnable action) {
-            view.postOnAnimation(action);
-        }
-        @Override
-        public void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
-            view.postOnAnimationDelayed(action, delayMillis);
-        }
-        @Override
-        public int getImportantForAccessibility(View view) {
-            return view.getImportantForAccessibility();
-        }
-        @Override
-        public void setImportantForAccessibility(View view, int mode) {
-            // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS is not available
-            // on this platform so replace with IMPORTANT_FOR_ACCESSIBILITY_NO
-            // which is closer semantically.
-            if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
-                mode = IMPORTANT_FOR_ACCESSIBILITY_NO;
-            }
-            //noinspection WrongConstant
-            view.setImportantForAccessibility(mode);
-        }
-        @Override
-        public boolean performAccessibilityAction(View view, int action, Bundle arguments) {
-            return view.performAccessibilityAction(action, arguments);
-        }
-        @Override
-        public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
-            AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
-            if (provider != null) {
-                return new AccessibilityNodeProviderCompat(provider);
-            }
-            return null;
-        }
-
-        @Override
-        public ViewParent getParentForAccessibility(View view) {
-            return view.getParentForAccessibility();
-        }
-
-        @Override
-        public int getMinimumWidth(View view) {
-            return view.getMinimumWidth();
-        }
-
-        @Override
-        public int getMinimumHeight(View view) {
-            return view.getMinimumHeight();
-        }
-
-        @SuppressWarnings("deprecation")
-        @Override
-        public void requestApplyInsets(View view) {
-            view.requestFitSystemWindows();
-        }
-
-        @Override
-        public boolean getFitsSystemWindows(View view) {
-            return view.getFitsSystemWindows();
-        }
-
-        @Override
-        public boolean hasOverlappingRendering(View view) {
-            return view.hasOverlappingRendering();
-        }
-
-        @Override
-        public void setBackground(View view, Drawable background) {
-            view.setBackground(background);
-        }
-    }
-
-    @RequiresApi(17)
-    static class ViewCompatApi17Impl extends ViewCompatApi16Impl {
-
-        @Override
-        public int getLabelFor(View view) {
-            return view.getLabelFor();
-        }
-
-        @Override
-        public void setLabelFor(View view, int id) {
-            view.setLabelFor(id);
-        }
-
-        @Override
-        public void setLayerPaint(View view, Paint paint) {
-            view.setLayerPaint(paint);
-        }
-
-        @Override
-        public int getLayoutDirection(View view) {
-            return view.getLayoutDirection();
-        }
-
-        @Override
-        public void setLayoutDirection(View view, int layoutDirection) {
-            view.setLayoutDirection(layoutDirection);
-        }
-
-        @Override
-        public int getPaddingStart(View view) {
-            return view.getPaddingStart();
-        }
-
-        @Override
-        public int getPaddingEnd(View view) {
-            return view.getPaddingEnd();
-        }
-
-        @Override
-        public void setPaddingRelative(View view, int start, int top, int end, int bottom) {
-            view.setPaddingRelative(start, top, end, bottom);
-        }
-
-        @Override
-        public int getWindowSystemUiVisibility(View view) {
-            return view.getWindowSystemUiVisibility();
-        }
-
-        @Override
-        public boolean isPaddingRelative(View view) {
-            return view.isPaddingRelative();
-        }
-
-        @Override
-        public Display getDisplay(View view) {
-            return view.getDisplay();
-        }
-
-        @Override
-        public int generateViewId() {
-            return View.generateViewId();
-        }
-    }
-
-    @RequiresApi(18)
-    static class ViewCompatApi18Impl extends ViewCompatApi17Impl {
-        @Override
-        public void setClipBounds(View view, Rect clipBounds) {
-            view.setClipBounds(clipBounds);
-        }
-
-        @Override
-        public Rect getClipBounds(View view) {
-            return view.getClipBounds();
-        }
-
-        @Override
-        public boolean isInLayout(View view) {
-            return view.isInLayout();
-        }
-    }
-
-    @RequiresApi(19)
-    static class ViewCompatApi19Impl extends ViewCompatApi18Impl {
-        @Override
-        public int getAccessibilityLiveRegion(View view) {
-            return view.getAccessibilityLiveRegion();
-        }
-
-        @Override
-        public void setAccessibilityLiveRegion(View view, int mode) {
-            view.setAccessibilityLiveRegion(mode);
-        }
-
-        @Override
-        public void setImportantForAccessibility(View view, int mode) {
-            view.setImportantForAccessibility(mode);
-        }
-
-        @Override
-        public boolean isLaidOut(View view) {
-            return view.isLaidOut();
-        }
-
-        @Override
-        public boolean isLayoutDirectionResolved(View view) {
-            return view.isLayoutDirectionResolved();
-        }
-
-        @Override
-        public boolean isAttachedToWindow(View view) {
-            return view.isAttachedToWindow();
-        }
-    }
-
-    @RequiresApi(21)
-    static class ViewCompatApi21Impl extends ViewCompatApi19Impl {
-        private static ThreadLocal<Rect> sThreadLocalRect;
-
-        @Override
-        public void setTransitionName(View view, String transitionName) {
-            view.setTransitionName(transitionName);
-        }
-
-        @Override
-        public String getTransitionName(View view) {
-            return view.getTransitionName();
-        }
-
-        @Override
-        public void requestApplyInsets(View view) {
-            view.requestApplyInsets();
-        }
-
-        @Override
-        public void setElevation(View view, float elevation) {
-            view.setElevation(elevation);
-        }
-
-        @Override
-        public float getElevation(View view) {
-            return view.getElevation();
-        }
-
-        @Override
-        public void setTranslationZ(View view, float translationZ) {
-            view.setTranslationZ(translationZ);
-        }
-
-        @Override
-        public float getTranslationZ(View view) {
-            return view.getTranslationZ();
-        }
-
-        @Override
-        public void setOnApplyWindowInsetsListener(View view,
-                final OnApplyWindowInsetsListener listener) {
-            if (listener == null) {
-                view.setOnApplyWindowInsetsListener(null);
-                return;
-            }
-
-            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) {
-            view.setNestedScrollingEnabled(enabled);
-        }
-
-        @Override
-        public boolean isNestedScrollingEnabled(View view) {
-            return view.isNestedScrollingEnabled();
-        }
-
-        @Override
-        public boolean startNestedScroll(View view, int axes) {
-            return view.startNestedScroll(axes);
-        }
-
-        @Override
-        public void stopNestedScroll(View view) {
-            view.stopNestedScroll();
-        }
-
-        @Override
-        public boolean hasNestedScrollingParent(View view) {
-            return view.hasNestedScrollingParent();
-        }
-
-        @Override
-        public boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
-                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
-            return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
-                    offsetInWindow);
-        }
-
-        @Override
-        public boolean dispatchNestedPreScroll(View view, int dx, int dy,
-                int[] consumed, int[] offsetInWindow) {
-            return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
-        }
-
-        @Override
-        public boolean dispatchNestedFling(View view, float velocityX, float velocityY,
-                boolean consumed) {
-            return view.dispatchNestedFling(velocityX, velocityY, consumed);
-        }
-
-        @Override
-        public boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
-            return view.dispatchNestedPreFling(velocityX, velocityY);
-        }
-
-        @Override
-        public boolean isImportantForAccessibility(View view) {
-            return view.isImportantForAccessibility();
-        }
-
-        @Override
-        public ColorStateList getBackgroundTintList(View view) {
-            return view.getBackgroundTintList();
-        }
-
-        @Override
-        public 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);
-                }
-            }
-        }
-
-        @Override
-        public 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);
-                }
-            }
-        }
-
-        @Override
-        public PorterDuff.Mode getBackgroundTintMode(View view) {
-            return view.getBackgroundTintMode();
-        }
-
-        @Override
-        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat 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) {
-            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 view.getZ();
-        }
-
-        @Override
-        public void setZ(View view, float z) {
-            view.setZ(z);
-        }
-
-        @Override
-        public void offsetLeftAndRight(View view, 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)
-            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) {
-            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;
-        }
-    }
-
-    @RequiresApi(23)
-    static class ViewCompatApi23Impl extends ViewCompatApi21Impl {
-        @Override
-        public void setScrollIndicators(View view, int indicators) {
-            view.setScrollIndicators(indicators);
-        }
-
-        @Override
-        public void setScrollIndicators(View view, int indicators, int mask) {
-            view.setScrollIndicators(indicators, mask);
-        }
-
-        @Override
-        public int getScrollIndicators(View view) {
-            return view.getScrollIndicators();
-        }
-
-
-        @Override
-        public void offsetLeftAndRight(View view, int offset) {
-            view.offsetLeftAndRight(offset);
-        }
-
-        @Override
-        public void offsetTopAndBottom(View view, int offset) {
-            view.offsetTopAndBottom(offset);
-        }
-    }
-
-    @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) {
-            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);
-        }
-    }
-
-    @RequiresApi(26)
-    static class ViewCompatApi26Impl extends ViewCompatApi24Impl {
-
-        @Override
-        public void setAutofillHints(@NonNull View v, @Nullable String... autofillHints) {
-            v.setAutofillHints(autofillHints);
-        }
-
-        @Override
-        public @AutofillImportance int getImportantForAutofill(@NonNull View v) {
-            return v.getImportantForAutofill();
-        }
-
-        @Override
-        public void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) {
-            v.setImportantForAutofill(mode);
-        }
-
-        @Override
-        public boolean isImportantForAutofill(@NonNull View v) {
-            return v.isImportantForAutofill();
-        }
-
-        @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 {
-        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 ViewCompatBaseImpl();
-        }
-    }
-
-    /**
-     * Check if this view can be scrolled horizontally in a certain direction.
-     *
-     * @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.
-     */
-    @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 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.
-     */
-    @Deprecated
-    public static boolean canScrollVertically(View view, int direction) {
-        return view.canScrollVertically(direction);
-    }
-
-    /**
-     * Returns the over-scroll mode for this view. The result will be
-     * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
-     * (allow over-scrolling only if the view content is larger than the container),
-     * or {@link #OVER_SCROLL_NEVER}.
-     *
-     * @param v The View against which to invoke the method.
-     * @return This view's over-scroll mode.
-     * @deprecated Call {@link View#getOverScrollMode()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    @OverScroll
-    public static int getOverScrollMode(View v) {
-        //noinspection ResourceType
-        return v.getOverScrollMode();
-    }
-
-    /**
-     * Set the over-scroll mode for this view. Valid over-scroll modes are
-     * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
-     * (allow over-scrolling only if the view content is larger than the container),
-     * or {@link #OVER_SCROLL_NEVER}.
-     *
-     * Setting the over-scroll mode of a view will have an effect only if the
-     * view is capable of scrolling.
-     *
-     * @param v The View against which to invoke the method.
-     * @param overScrollMode The new over-scroll mode for this view.
-     * @deprecated Call {@link View#setOverScrollMode(int)} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static void setOverScrollMode(View v, @OverScroll int overScrollMode) {
-        v.setOverScrollMode(overScrollMode);
-    }
-
-    /**
-     * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
-     * giving a chance to this View to populate the accessibility event with its
-     * text content. While this method is free to modify event
-     * attributes other than text content, doing so should normally be performed in
-     * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}.
-     * <p>
-     * Example: Adding formatted date string to an accessibility event in addition
-     *          to the text added by the super implementation:
-     * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
-     *     super.onPopulateAccessibilityEvent(event);
-     *     final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
-     *     String selectedDateUtterance = DateUtils.formatDateTime(mContext,
-     *         mCurrentDate.getTimeInMillis(), flags);
-     *     event.getText().add(selectedDateUtterance);
-     * }</pre>
-     * <p>
-     * If an {@link AccessibilityDelegateCompat} has been specified via calling
-     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
-     * {@link AccessibilityDelegateCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)}
-     * is responsible for handling this call.
-     * </p>
-     * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
-     * information to the event, in case the default implementation has basic information to add.
-     * </p>
-     *
-     * @param v The View against which to invoke the method.
-     * @param event The accessibility event which to populate.
-     *
-     * @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) {
-        v.onPopulateAccessibilityEvent(event);
-    }
-
-    /**
-     * Initializes an {@link AccessibilityEvent} with information about
-     * this View which is the event source. In other words, the source of
-     * an accessibility event is the view whose state change triggered firing
-     * the event.
-     * <p>
-     * Example: Setting the password property of an event in addition
-     *          to properties set by the super implementation:
-     * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-     *     super.onInitializeAccessibilityEvent(event);
-     *     event.setPassword(true);
-     * }</pre>
-     * <p>
-     * If an {@link AccessibilityDelegateCompat} has been specified via calling
-     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
-     * {@link AccessibilityDelegateCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)}
-     * is responsible for handling this call.
-     *
-     * @param v The View against which to invoke the method.
-     * @param event The event to initialize.
-     *
-     * @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) {
-        v.onInitializeAccessibilityEvent(event);
-    }
-
-    /**
-     * Initializes an {@link AccessibilityNodeInfoCompat} with information
-     * about this view. The base implementation sets:
-     * <ul>
-     * <li>{@link AccessibilityNodeInfoCompat#setParent(View)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setPackageName(CharSequence)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setClassName(CharSequence)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setEnabled(boolean)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setClickable(boolean)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setFocusable(boolean)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setFocused(boolean)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setLongClickable(boolean)},</li>
-     * <li>{@link AccessibilityNodeInfoCompat#setSelected(boolean)},</li>
-     * </ul>
-     * <p>
-     * If an {@link AccessibilityDelegateCompat} has been specified via calling
-     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)}, its
-     * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
-     * method is responsible for handling this call.
-     *
-     * @param v The View against which to invoke the method.
-     * @param info The instance to initialize.
-     */
-    public static void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
-        IMPL.onInitializeAccessibilityNodeInfo(v, info);
-    }
-
-    /**
-     * Sets a delegate for implementing accessibility support via composition
-     * (as opposed to inheritance). For more details, see
-     * {@link AccessibilityDelegateCompat}.
-     * <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>
-     * host methods. This prevents certain properties such as class name from
-     * being modified by overriding
-     * {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
-     * as any changes will be overwritten by the host class.
-     * <p>
-     * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
-     * methods are called <i>after</i> host methods, which all properties to be
-     * modified without being overwritten by the host class.
-     *
-     * @param delegate the object to which accessibility method calls should be
-     *                 delegated
-     * @see AccessibilityDelegateCompat
-     */
-    public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
-        IMPL.setAccessibilityDelegate(v, delegate);
-    }
-
-    /**
-     * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how
-     * to autofill the view with the user's data.
-     *
-     * <p>Typically, there is only one way to autofill a view, but there could be more than one.
-     * For example, if the application accepts either an username or email address to identify
-     * an user.
-     *
-     * <p>These hints are not validated by the Android System, but passed "as is" to the service.
-     * Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_}
-     * constants such as:
-     * {@link View#AUTOFILL_HINT_USERNAME}, {@link View#AUTOFILL_HINT_PASSWORD},
-     * {@link View#AUTOFILL_HINT_EMAIL_ADDRESS},
-     * {@link View#AUTOFILL_HINT_NAME},
-     * {@link View#AUTOFILL_HINT_PHONE},
-     * {@link View#AUTOFILL_HINT_POSTAL_ADDRESS}, {@link View#AUTOFILL_HINT_POSTAL_CODE},
-     * {@link View#AUTOFILL_HINT_CREDIT_CARD_NUMBER},
-     * {@link View#AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
-     * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
-     * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
-     * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or
-     * {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}.
-     *
-     * <p>This method is only supported on API >= 26.
-     * On API 25 and below, it is a no-op</p>
-     *
-     * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set.
-     * @attr ref android.R.styleable#View_autofillHints
-     */
-    public static void setAutofillHints(@NonNull View v, @Nullable String... autofillHints) {
-        IMPL.setAutofillHints(v, autofillHints);
-    }
-
-    /**
-     * Gets the mode for determining whether this view is important for autofill.
-     *
-     * <p>See {@link #setImportantForAutofill(View, int)} and {@link #isImportantForAutofill(View)}
-     * for more info about this mode.
-     *
-     * <p>This method is only supported on API >= 26.
-     * On API 25 and below, it will always return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO}.</p>
-     *
-     * @return {@link View#IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to
-     * {@link #setImportantForAutofill(View, int)}.
-     *
-     * @attr ref android.R.styleable#View_importantForAutofill
-     */
-    public static @AutofillImportance int getImportantForAutofill(@NonNull View v) {
-        return IMPL.getImportantForAutofill(v);
-    }
-
-    /**
-     * Sets the mode for determining whether this view is considered important for autofill.
-     *
-     * <p>The platform determines the importance for autofill automatically but you
-     * can use this method to customize the behavior. For example:
-     *
-     * <ol>
-     *   <li>When the view contents is irrelevant for autofill (for example, a text field used in a
-     *       "Captcha" challenge), it should be {@link View#IMPORTANT_FOR_AUTOFILL_NO}.
-     *   <li>When both the view and its children are irrelevant for autofill (for example, the root
-     *       view of an activity containing a spreadhseet editor), it should be
-     *       {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}.
-     *   <li>When the view content is relevant for autofill but its children aren't (for example,
-     *       a credit card expiration date represented by a custom view that overrides the proper
-     *       autofill methods and has 2 children representing the month and year), it should
-     *       be {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}.
-     * </ol>
-     *
-     * <p><b>NOTE:</strong> setting the mode as does {@link View#IMPORTANT_FOR_AUTOFILL_NO} or
-     * {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and
-     * its children) will be always be considered not important; for example, when the user
-     * explicitly makes an autofill request, all views are considered important. See
-     * {@link #isImportantForAutofill(View)} for more details about how the View's importance for
-     * autofill is used.
-     *
-     * <p>This method is only supported on API >= 26.
-     * On API 25 and below, it is a no-op</p>
-     *
-     *
-     * @param mode {@link View#IMPORTANT_FOR_AUTOFILL_AUTO},
-     * {@link View#IMPORTANT_FOR_AUTOFILL_YES},
-     * {@link View#IMPORTANT_FOR_AUTOFILL_NO},
-     * {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS},
-     * or {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS}.
-     *
-     * @attr ref android.R.styleable#View_importantForAutofill
-     */
-    public static void setImportantForAutofill(@NonNull View v, @AutofillImportance int mode) {
-        IMPL.setImportantForAutofill(v, mode);
-    }
-
-    /**
-     * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode}
-     * associated with this view is considered important for autofill purposes.
-     *
-     * <p>Generally speaking, a view is important for autofill if:
-     * <ol>
-     * <li>The view can be autofilled by an {@link android.service.autofill.AutofillService}.
-     * <li>The view contents can help an {@link android.service.autofill.AutofillService}
-     *     determine how other views can be autofilled.
-     * <ol>
-     *
-     * <p>For example, view containers should typically return {@code false} for performance reasons
-     * (since the important info is provided by their children), but if its properties have relevant
-     * information (for example, a resource id called {@code credentials}, it should return
-     * {@code true}. On the other hand, views representing labels or editable fields should
-     * typically return {@code true}, but in some cases they could return {@code false}
-     * (for example, if they're part of a "Captcha" mechanism).
-     *
-     * <p>The value returned by this method depends on the value returned by
-     * {@link #getImportantForAutofill(View)}:
-     *
-     * <ol>
-     *   <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_YES} or
-     *       {@link View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS},
-     *       then it returns {@code true}
-     *   <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_NO} or
-     *       {@link View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS},
-     *       then it returns {@code false}
-     *   <li>if it returns {@link View#IMPORTANT_FOR_AUTOFILL_AUTO},
-     *   then it uses some simple heuristics that can return {@code true}
-     *   in some cases (like a container with a resource id), but {@code false} in most.
-     *   <li>otherwise, it returns {@code false}.
-     * </ol>
-     *
-     * <p>When a view is considered important for autofill:
-     * <ul>
-     *   <li>The view might automatically trigger an autofill request when focused on.
-     *   <li>The contents of the view are included in the {@link android.view.ViewStructure}
-     *   used in an autofill request.
-     * </ul>
-     *
-     * <p>On the other hand, when a view is considered not important for autofill:
-     * <ul>
-     *   <li>The view never automatically triggers autofill requests, but it can trigger a manual
-     *       request through {@link android.view.autofill.AutofillManager#requestAutofill(View)}.
-     *   <li>The contents of the view are not included in the {@link android.view.ViewStructure}
-     *   used in an autofill request, unless the request has the
-     *       {@link View#AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS} flag.
-     * </ul>
-     *
-     * <p>This method is only supported on API >= 26.
-     * On API 25 and below, it will always return {@code true}.</p>
-     *
-     * @return whether the view is considered important for autofill.
-     *
-     * @see #setImportantForAutofill(View, int)
-     * @see View#IMPORTANT_FOR_AUTOFILL_AUTO
-     * @see View#IMPORTANT_FOR_AUTOFILL_YES
-     * @see View#IMPORTANT_FOR_AUTOFILL_NO
-     * @see View#IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS
-     * @see View#IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
-     * @see android.view.autofill.AutofillManager#requestAutofill(View)
-     */
-    public static boolean isImportantForAutofill(@NonNull View v) {
-        return IMPL.isImportantForAutofill(v);
-    }
-
-    /**
-     * Checks whether provided View has an accessibility delegate attached to it.
-     *
-     * @param v The View instance to check
-     * @return True if the View has an accessibility delegate
-     */
-    public static boolean hasAccessibilityDelegate(View v) {
-        return IMPL.hasAccessibilityDelegate(v);
-    }
-
-    /**
-     * Indicates whether the view is currently tracking transient state that the
-     * app should not need to concern itself with saving and restoring, but that
-     * the framework should take special note to preserve when possible.
-     *
-     * @param view View to check for transient state
-     * @return true if the view has transient state
-     */
-    public static boolean hasTransientState(View view) {
-        return IMPL.hasTransientState(view);
-    }
-
-    /**
-     * Set whether this view is currently tracking transient state that the
-     * framework should attempt to preserve when possible.
-     *
-     * @param view View tracking transient state
-     * @param hasTransientState true if this view has transient state
-     */
-    public static void setHasTransientState(View view, boolean hasTransientState) {
-        IMPL.setHasTransientState(view, hasTransientState);
-    }
-
-    /**
-     * <p>Cause an invalidate to happen on the next animation time step, typically the
-     * next display frame.</p>
-     *
-     * <p>This method can be invoked from outside of the UI thread
-     * only when this View is attached to a window.</p>
-     *
-     * @param view View to invalidate
-     */
-    public static void postInvalidateOnAnimation(View view) {
-        IMPL.postInvalidateOnAnimation(view);
-    }
-
-    /**
-     * <p>Cause an invalidate of the specified area to happen on the next animation
-     * time step, typically the next display frame.</p>
-     *
-     * <p>This method can be invoked from outside of the UI thread
-     * only when this View is attached to a window.</p>
-     *
-     * @param view View to invalidate
-     * @param left The left coordinate of the rectangle to invalidate.
-     * @param top The top coordinate of the rectangle to invalidate.
-     * @param right The right coordinate of the rectangle to invalidate.
-     * @param bottom The bottom coordinate of the rectangle to invalidate.
-     */
-    public static void postInvalidateOnAnimation(View view, int left, int top,
-            int right, int bottom) {
-        IMPL.postInvalidateOnAnimation(view, left, top, right, bottom);
-    }
-
-    /**
-     * <p>Causes the Runnable to execute on the next animation time step.
-     * The runnable will be run on the user interface thread.</p>
-     *
-     * <p>This method can be invoked from outside of the UI thread
-     * only when this View is attached to a window.</p>
-     *
-     * @param view View to post this Runnable to
-     * @param action The Runnable that will be executed.
-     */
-    public static void postOnAnimation(View view, Runnable action) {
-        IMPL.postOnAnimation(view, action);
-    }
-
-    /**
-     * <p>Causes the Runnable to execute on the next animation time step,
-     * after the specified amount of time elapses.
-     * The runnable will be run on the user interface thread.</p>
-     *
-     * <p>This method can be invoked from outside of the UI thread
-     * only when this View is attached to a window.</p>
-     *
-     * @param view The view to post this Runnable to
-     * @param action The Runnable that will be executed.
-     * @param delayMillis The delay (in milliseconds) until the Runnable
-     *        will be executed.
-     */
-    public static void postOnAnimationDelayed(View view, Runnable action, long delayMillis) {
-        IMPL.postOnAnimationDelayed(view, action, delayMillis);
-    }
-
-    /**
-     * Gets the mode for determining whether this View is important for accessibility
-     * which is if it fires accessibility events and if it is reported to
-     * accessibility services that query the screen.
-     *
-     * @param view The view whose property to get.
-     * @return The mode for determining whether a View is important for accessibility.
-     *
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
-     */
-    @ImportantForAccessibility
-    public static int getImportantForAccessibility(View view) {
-        //noinspection ResourceType
-        return IMPL.getImportantForAccessibility(view);
-    }
-
-    /**
-     * Sets how to determine whether this view is important for accessibility
-     * which is if it fires accessibility events and if it is reported to
-     * accessibility services that query the screen.
-     * <p>
-     * <em>Note:</em> If the current platform version does not support the
-     *  {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} mode, then
-     *  {@link #IMPORTANT_FOR_ACCESSIBILITY_NO} will be used as it is the
-     *  closest terms of semantics.
-     * </p>
-     *
-     * @param view The view whose property to set.
-     * @param mode How to determine whether this view is important for accessibility.
-     *
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_YES
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_NO
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-     * @see #IMPORTANT_FOR_ACCESSIBILITY_AUTO
-     */
-    public static void setImportantForAccessibility(View view,
-            @ImportantForAccessibility int mode) {
-        IMPL.setImportantForAccessibility(view, mode);
-    }
-
-    /**
-     * Computes whether this view should be exposed for accessibility. In
-     * general, views that are interactive or provide information are exposed
-     * while views that serve only as containers are hidden.
-     * <p>
-     * If an ancestor of this view has importance
-     * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, this method
-     * returns <code>false</code>.
-     * <p>
-     * Otherwise, the value is computed according to the view's
-     * {@link #getImportantForAccessibility(View)} value:
-     * <ol>
-     * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_NO} or
-     * {@link #IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS}, return <code>false
-     * </code>
-     * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_YES}, return <code>true</code>
-     * <li>{@link #IMPORTANT_FOR_ACCESSIBILITY_AUTO}, return <code>true</code> if
-     * view satisfies any of the following:
-     * <ul>
-     * <li>Is actionable, e.g. {@link View#isClickable()},
-     * {@link View#isLongClickable()}, or {@link View#isFocusable()}
-     * <li>Has an {@link AccessibilityDelegateCompat}
-     * <li>Has an interaction listener, e.g. {@link View.OnTouchListener},
-     * {@link View.OnKeyListener}, etc.
-     * <li>Is an accessibility live region, e.g.
-     * {@link #getAccessibilityLiveRegion(View)} is not
-     * {@link #ACCESSIBILITY_LIVE_REGION_NONE}.
-     * </ul>
-     * </ol>
-     * <p>
-     * <em>Note:</em> Prior to API 21, this method will always return {@code true}.
-     *
-     * @return Whether the view is exposed for accessibility.
-     * @see #setImportantForAccessibility(View, int)
-     * @see #getImportantForAccessibility(View)
-     */
-    public static boolean isImportantForAccessibility(View view) {
-        return IMPL.isImportantForAccessibility(view);
-    }
-
-    /**
-     * Performs the specified accessibility action on the view. For
-     * possible accessibility actions look at {@link AccessibilityNodeInfoCompat}.
-     * <p>
-     * If an {@link AccessibilityDelegateCompat} has been specified via calling
-     * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
-     * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)}
-     * is responsible for handling this call.
-     * </p>
-     *
-     * @param action The action to perform.
-     * @param arguments Optional action arguments.
-     * @return Whether the action was performed.
-     */
-    public static boolean performAccessibilityAction(View view, int action, Bundle arguments) {
-        return IMPL.performAccessibilityAction(view, action, arguments);
-    }
-
-    /**
-     * Gets the provider for managing a virtual view hierarchy rooted at this View
-     * and reported to {@link android.accessibilityservice.AccessibilityService}s
-     * that explore the window content.
-     * <p>
-     * If this method returns an instance, this instance is responsible for managing
-     * {@link AccessibilityNodeInfoCompat}s describing the virtual sub-tree rooted at
-     * this View including the one representing the View itself. Similarly the returned
-     * instance is responsible for performing accessibility actions on any virtual
-     * view or the root view itself.
-     * </p>
-     * <p>
-     * If an {@link AccessibilityDelegateCompat} has been specified via calling
-     * {@link #setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
-     * {@link AccessibilityDelegateCompat#getAccessibilityNodeProvider(View)}
-     * is responsible for handling this call.
-     * </p>
-     *
-     * @param view The view whose property to get.
-     * @return The provider.
-     *
-     * @see AccessibilityNodeProviderCompat
-     */
-    public static AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View view) {
-        return IMPL.getAccessibilityNodeProvider(view);
-    }
-
-    /**
-     * 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.
-     * @return The opacity of the view.
-     *
-     * @deprecated Use {@link View#getAlpha()} directly.
-     */
-    @Deprecated
-    public static float getAlpha(View view) {
-        return view.getAlpha();
-    }
-
-    /**
-     * <p>Specifies the type of layer backing this view. The layer can be
-     * {@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
-     * properties of the paint are taken into account when composing the layer:</p>
-     * <ul>
-     * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
-     * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
-     * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
-     * </ul>
-     *
-     * <p>If this view has an alpha value set to < 1.0 by calling
-     * setAlpha(float), the alpha value of the layer's paint is replaced by
-     * this view's alpha value. Calling setAlpha(float) is therefore
-     * 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 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 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 View#LAYER_TYPE_NONE}
-     *
-     * @deprecated Use {@link View#setLayerType(int, Paint)} directly.
-     */
-    @Deprecated
-    public static void setLayerType(View view, @LayerType int layerType, Paint 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 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 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 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 view.getLayerType();
-    }
-
-    /**
-     * Gets the id of a view for which a given view serves as a label for
-     * accessibility purposes.
-     *
-     * @param view The view on which to invoke the corresponding method.
-     * @return The labeled view id.
-     */
-    public static int getLabelFor(View view) {
-        return IMPL.getLabelFor(view);
-    }
-
-    /**
-     * Sets the id of a view for which a given view serves as a label for
-     * accessibility purposes.
-     *
-     * @param view The view on which to invoke the corresponding method.
-     * @param labeledId The labeled view id.
-     */
-    public static void setLabelFor(View view, @IdRes int labeledId) {
-        IMPL.setLabelFor(view, labeledId);
-    }
-
-    /**
-     * Updates the {@link Paint} object used with the current layer (used only if the current
-     * 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)}
-     * must be called to ensure that the view gets redrawn immediately.
-     *
-     * <p>A layer is associated with an optional {@link android.graphics.Paint}
-     * instance that controls how the layer is composed on screen. The following
-     * properties of the paint are taken into account when composing the layer:</p>
-     * <ul>
-     * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
-     * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
-     * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
-     * </ul>
-     *
-     * <p>If this view has an alpha value set to < 1.0 by calling
-     * View#setAlpha(float), the alpha value of the layer's paint is replaced by
-     * this view's alpha value. Calling View#setAlpha(float) is therefore
-     * equivalent to setting a hardware layer on this view and providing a paint with
-     * the desired alpha value.</p>
-     *
-     * @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 View#LAYER_TYPE_NONE}
-     *
-     * @see #setLayerType(View, int, android.graphics.Paint)
-     */
-    public static void setLayerPaint(View view, Paint paint) {
-        IMPL.setLayerPaint(view, paint);
-    }
-
-    /**
-     * Returns the resolved layout direction for this view.
-     *
-     * @param view View to get layout direction for
-     * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
-     * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
-     *
-     * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version
-     * is lower than Jellybean MR1 (API 17)
-     */
-    @ResolvedLayoutDirectionMode
-    public static int getLayoutDirection(View view) {
-        //noinspection ResourceType
-        return IMPL.getLayoutDirection(view);
-    }
-
-    /**
-     * Set the layout direction for this view. This will propagate a reset of layout direction
-     * resolution to the view's children and resolve layout direction for this view.
-     *
-     * @param view View to set layout direction for
-     * @param layoutDirection the layout direction to set. Should be one of:
-     *
-     * {@link #LAYOUT_DIRECTION_LTR},
-     * {@link #LAYOUT_DIRECTION_RTL},
-     * {@link #LAYOUT_DIRECTION_INHERIT},
-     * {@link #LAYOUT_DIRECTION_LOCALE}.
-     *
-     * Resolution will be done if the value is set to LAYOUT_DIRECTION_INHERIT. The resolution
-     * proceeds up the parent chain of the view to get the value. If there is no parent, then it
-     * will return the default {@link #LAYOUT_DIRECTION_LTR}.
-     */
-    public static void setLayoutDirection(View view, @LayoutDirectionMode int layoutDirection) {
-        IMPL.setLayoutDirection(view, layoutDirection);
-    }
-
-    /**
-     * Gets the parent for accessibility purposes. Note that the parent for
-     * accessibility is not necessary the immediate parent. It is the first
-     * predecessor that is important for accessibility.
-     *
-     * @param view View to retrieve parent for
-     * @return The parent for use in accessibility inspection
-     */
-    public static ViewParent getParentForAccessibility(View view) {
-        return IMPL.getParentForAccessibility(view);
-    }
-
-    /**
-     * Finds the first descendant view with the given ID, the view itself if the ID matches
-     * {@link View#getId()}, or throws an IllegalArgumentException if the ID is invalid or there
-     * is no matching view in the hierarchy.
-     * <p>
-     * <strong>Note:</strong> In most cases -- depending on compiler support --
-     * the resulting view is automatically cast to the target class type. If
-     * the target class type is unconstrained, an explicit cast may be
-     * necessary.
-     *
-     * @param id the ID to search for
-     * @return a view with given ID
-     * @see View#findViewById(int)
-     */
-    @NonNull
-    public static <T extends View> T requireViewById(@NonNull View view, @IdRes int id) {
-        // TODO: use and link to View#requireViewById() directly, once available
-        T targetView = view.findViewById(id);
-        if (targetView == null) {
-            throw new IllegalArgumentException("ID does not reference a View inside this View");
-        }
-        return targetView;
-    }
-
-    /**
-     * Indicates whether this View is opaque. An opaque View guarantees that it will
-     * draw all the pixels overlapping its bounds using a fully opaque color.
-     *
-     * @return True if this View is guaranteed to be fully opaque, false otherwise.
-     * @deprecated Use {@link View#isOpaque()} directly. This method will be
-     * removed in a future release.
-     */
-    @Deprecated
-    public static boolean isOpaque(View view) {
-        return view.isOpaque();
-    }
-
-    /**
-     * Utility to reconcile a desired size and state, with constraints imposed
-     * by a MeasureSpec.  Will take the desired size, unless a different size
-     * is imposed by the constraints.  The returned value is a compound integer,
-     * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
-     * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the resulting
-     * size is smaller than the size the view wants to be.
-     *
-     * @param size How big the view wants to be
-     * @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 View.resolveSizeAndState(size, measureSpec, childMeasuredState);
-    }
-
-    /**
-     * Return the full width measurement information for this view as computed
-     * by the most recent call to {@link android.view.View#measure(int, int)}.
-     * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and
-     * {@link #MEASURED_STATE_TOO_SMALL}.
-     * This should be used during measurement and layout calculations only. Use
-     * {@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 view.getMeasuredWidthAndState();
-    }
-
-    /**
-     * Return the full height measurement information for this view as computed
-     * by the most recent call to {@link android.view.View#measure(int, int)}.
-     * This result is a bit mask as defined by {@link #MEASURED_SIZE_MASK} and
-     * {@link #MEASURED_STATE_TOO_SMALL}.
-     * This should be used during measurement and layout calculations only. Use
-     * {@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 view.getMeasuredHeightAndState();
-    }
-
-    /**
-     * Return only the state bits of {@link #getMeasuredWidthAndState}
-     * and {@link #getMeasuredHeightAndState}, combined into one integer.
-     * 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 view.getMeasuredState();
-    }
-
-    /**
-     * Merge two states as returned by {@link #getMeasuredState(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.
-     *
-     * @deprecated Use {@link View#combineMeasuredStates(int, int)} directly.
-     */
-    @Deprecated
-    public static int combineMeasuredStates(int curState, int newState) {
-        return View.combineMeasuredStates(curState, newState);
-    }
-
-    /**
-     * Gets the live region mode for the specified View.
-     *
-     * @param view The view from which to obtain the live region mode
-     * @return The live region mode for the view.
-     *
-     * @see ViewCompat#setAccessibilityLiveRegion(View, int)
-     */
-    @AccessibilityLiveRegion
-    public static int getAccessibilityLiveRegion(View view) {
-        //noinspection ResourceType
-        return IMPL.getAccessibilityLiveRegion(view);
-    }
-
-    /**
-     * Sets the live region mode for the specified view. This indicates to
-     * accessibility services whether they should automatically notify the user
-     * about changes to the view's content description or text, or to the
-     * content descriptions or text of the view's children (where applicable).
-     * <p>
-     * For example, in a login screen with a TextView that displays an "incorrect
-     * password" notification, that view should be marked as a live region with
-     * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
-     * <p>
-     * To disable change notifications for this view, use
-     * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
-     * mode for most views.
-     * <p>
-     * To indicate that the user should be notified of changes, use
-     * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
-     * <p>
-     * If the view's changes should interrupt ongoing speech and notify the user
-     * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
-     *
-     * @param view The view on which to set the live region mode
-     * @param mode The live region mode for this view, one of:
-     *        <ul>
-     *        <li>{@link #ACCESSIBILITY_LIVE_REGION_NONE}
-     *        <li>{@link #ACCESSIBILITY_LIVE_REGION_POLITE}
-     *        <li>{@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}
-     *        </ul>
-     */
-    public static void setAccessibilityLiveRegion(View view, @AccessibilityLiveRegion int mode) {
-        IMPL.setAccessibilityLiveRegion(view, mode);
-    }
-
-    /**
-     * Returns the start padding of the specified view depending on its resolved layout direction.
-     * If there are inset and enabled scrollbars, this value may include the space
-     * required to display the scrollbars as well.
-     *
-     * @param view The view to get padding for
-     * @return the start padding in pixels
-     */
-    public static int getPaddingStart(View view) {
-        return IMPL.getPaddingStart(view);
-    }
-
-    /**
-     * Returns the end padding of the specified view depending on its resolved layout direction.
-     * If there are inset and enabled scrollbars, this value may include the space
-     * required to display the scrollbars as well.
-     *
-     * @param view The view to get padding for
-     * @return the end padding in pixels
-     */
-    public static int getPaddingEnd(View view) {
-        return IMPL.getPaddingEnd(view);
-    }
-
-    /**
-     * Sets the relative padding. The view may add on the space required to display
-     * the scrollbars, depending on the style and visibility of the scrollbars.
-     * So the values returned from {@link #getPaddingStart}, {@link View#getPaddingTop},
-     * {@link #getPaddingEnd} and {@link View#getPaddingBottom} may be different
-     * from the values set in this call.
-     *
-     * @param view The view on which to set relative padding
-     * @param start the start padding in pixels
-     * @param top the top padding in pixels
-     * @param end the end padding in pixels
-     * @param bottom the bottom padding in pixels
-     */
-    public static void setPaddingRelative(View view, int start, int top, int end, int bottom) {
-        IMPL.setPaddingRelative(view, start, top, end, bottom);
-    }
-
-    /**
-     * Notify a view that it is being temporarily detached.
-     */
-    public static void dispatchStartTemporaryDetach(View view) {
-        IMPL.dispatchStartTemporaryDetach(view);
-    }
-
-    /**
-     * Notify a view that its temporary detach has ended; the view is now reattached.
-     */
-    public static void dispatchFinishTemporaryDetach(View view) {
-        IMPL.dispatchFinishTemporaryDetach(view);
-    }
-
-    /**
-     * The horizontal location of this view relative to its {@link View#getLeft() left} position.
-     * This position is post-layout, in addition to wherever the object's
-     * layout placed it.
-     *
-     * @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 view.getTranslationX();
-    }
-
-    /**
-     * The vertical location of this view relative to its {@link View#getTop() top} position.
-     * This position is post-layout, in addition to wherever the object's
-     * layout placed it.
-     *
-     * @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 view.getTranslationY();
-    }
-
-    /**
-     * The transform matrix of this view, which is calculated based on the current
-     * rotation, scale, and pivot properties.
-     * <p>
-     *
-     * @param view The view whose Matrix will be returned
-     * @return The current transform matrix for the view
-     *
-     * @see #getRotation(View)
-     * @see #getScaleX(View)
-     * @see #getScaleY(View)
-     * @see #getPivotX(View)
-     * @see #getPivotY(View)
-     *
-     * @deprecated Use {@link View#getMatrix()} directly.
-     */
-    @Deprecated
-    @Nullable
-    public static Matrix getMatrix(View view) {
-        return view.getMatrix();
-    }
-
-    /**
-     * Returns the minimum width of the view.
-     *
-     * <p>Prior to API 16, this method may return 0 on some platforms.</p>
-     *
-     * @return the minimum width the view will try to be.
-     */
-    public static int getMinimumWidth(View view) {
-        return IMPL.getMinimumWidth(view);
-    }
-
-    /**
-     * Returns the minimum height of the view.
-     *
-     * <p>Prior to API 16, this method may return 0 on some platforms.</p>
-     *
-     * @return the minimum height the view will try to be.
-     */
-    public static int getMinimumHeight(View view) {
-        return IMPL.getMinimumHeight(view);
-    }
-
-    /**
-     * This method returns a ViewPropertyAnimator object, which can be used to animate
-     * specific properties on this View.
-     *
-     * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
-     */
-    public static ViewPropertyAnimatorCompat animate(View view) {
-        return IMPL.animate(view);
-    }
-
-    /**
-     * Sets the horizontal location of this view relative to its left position.
-     * This effectively positions the object post-layout, in addition to wherever the object's
-     * layout placed it.
-     *
-     * @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) {
-        view.setTranslationX(value);
-    }
-
-    /**
-     * Sets the vertical location of this view relative to its top position.
-     * This effectively positions the object post-layout, in addition to wherever the object's
-     * layout placed it.
-     *
-     * @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) {
-        view.setTranslationY(value);
-    }
-
-    /**
-     * <p>Sets 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>
-     *
-     * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant
-     * 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>
-     *
-     * @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) {
-        view.setAlpha(value);
-    }
-
-    /**
-     * Sets the visual x position of this view, in pixels. This is equivalent to setting the
-     * {@link #setTranslationX(View, float) translationX} property to be the difference between
-     * the x value passed in and the current left property of the view as determined
-     * by the layout bounds.
-     *
-     * @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) {
-        view.setX(value);
-    }
-
-    /**
-     * Sets the visual y position of this view, in pixels. This is equivalent to setting the
-     * {@link #setTranslationY(View, float) translationY} property to be the difference between
-     * the y value passed in and the current top property of the view as determined by the
-     * layout bounds.
-     *
-     * @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) {
-        view.setY(value);
-    }
-
-    /**
-     * Sets the degrees that the view is rotated around the pivot point. Increasing values
-     * result in clockwise rotation.
-     *
-     * @param value The degrees of rotation.
-     *
-     * @deprecated Use {@link View#setRotation(float)} directly.
-     */
-    @Deprecated
-    public static void setRotation(View view, float value) {
-        view.setRotation(value);
-    }
-
-    /**
-     * Sets the degrees that the view is rotated around the horizontal axis through the pivot point.
-     * Increasing values result in clockwise rotation from the viewpoint of looking down the
-     * x axis.
-     *
-     * @param value The degrees of X rotation.
-     *
-     * @deprecated Use {@link View#setRotationX(float)} directly.
-     */
-    @Deprecated
-    public static void setRotationX(View view, float value) {
-        view.setRotationX(value);
-    }
-
-    /**
-     * Sets the degrees that the view is rotated around the vertical axis through the pivot point.
-     * Increasing values result in counter-clockwise rotation from the viewpoint of looking
-     * down the y axis.
-     *
-     * @param value The degrees of Y rotation.
-     *
-     * @deprecated Use {@link View#setRotationY(float)} directly.
-     */
-    @Deprecated
-    public static void setRotationY(View view, float 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.
-     *
-     * @param value The scaling factor.
-     *
-     * @deprecated Use {@link View#setScaleX(float)} directly.
-     */
-    @Deprecated
-    public static void setScaleX(View view, float 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.
-     *
-     * @param value The scaling factor.
-     *
-     * @deprecated Use {@link View#setScaleY(float)} directly.
-     */
-    @Deprecated
-    public static void setScaleY(View view, float 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}.
-     *
-     * @deprecated Use {@link View#getPivotX()} directly.
-     */
-    @Deprecated
-    public static float getPivotX(View view) {
-        return view.getPivotX();
-    }
-
-    /**
-     * Sets the x location of the point around which the view is
-     * {@link #setRotation(View, float) rotated} and {@link #setScaleX(View, float) scaled}.
-     * By default, the pivot point is centered on the object.
-     * Setting this property disables this behavior and causes the view to use only the
-     * explicitly set pivotX and pivotY values.
-     *
-     * @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) {
-        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}.
-     *
-     * @return The y location of the pivot point.
-     *
-     * @deprecated Use {@link View#getPivotY()} directly.
-     */
-    @Deprecated
-    public static float getPivotY(View view) {
-        return view.getPivotY();
-    }
-
-    /**
-     * Sets the y location of the point around which the view is
-     * {@link #setRotation(View, float) rotated} and {@link #setScaleY(View, float) scaled}.
-     * By default, the pivot point is centered on the object.
-     * Setting this property disables this behavior and causes the view to use only the
-     * explicitly set pivotX and pivotY values.
-     *
-     * @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) {
-        view.setPivotY(value);
-    }
-
-    /**
-     * @deprecated Use {@link View#getRotation()} directly.
-     */
-    @Deprecated
-    public static float getRotation(View view) {
-        return view.getRotation();
-    }
-
-    /**
-     * @deprecated Use {@link View#getRotationX()} directly.
-     */
-    @Deprecated
-    public static float getRotationX(View view) {
-        return view.getRotationX();
-    }
-
-    /**
-     * @deprecated Use {@link View#getRotationY()} directly.
-     */
-    @Deprecated
-    public static float getRotationY(View view) {
-        return view.getRotationY();
-    }
-
-    /**
-     * @deprecated Use {@link View#getScaleX()} directly.
-     */
-    @Deprecated
-    public static float getScaleX(View view) {
-        return view.getScaleX();
-    }
-
-    /**
-     * @deprecated Use {@link View#getScaleY()} directly.
-     */
-    @Deprecated
-    public static float getScaleY(View view) {
-        return view.getScaleY();
-    }
-
-    /**
-     * @deprecated Use {@link View#getX()} directly.
-     */
-    @Deprecated
-    public static float getX(View view) {
-        return view.getX();
-    }
-
-    /**
-     * @deprecated Use {@link View#getY()} directly.
-     */
-    @Deprecated
-    public static float getY(View view) {
-        return view.getY();
-    }
-
-    /**
-     * Sets the base elevation of this view, in pixels.
-     */
-    public static void setElevation(View view, float elevation) {
-        IMPL.setElevation(view, elevation);
-    }
-
-    /**
-     * The base elevation of this view relative to its parent, in pixels.
-     *
-     * @return The base depth position of the view, in pixels.
-     */
-    public static float getElevation(View view) {
-        return IMPL.getElevation(view);
-    }
-
-    /**
-     * Sets the depth location of this view relative to its {@link #getElevation(View) elevation}.
-     */
-    public static void setTranslationZ(View view, float translationZ) {
-        IMPL.setTranslationZ(view, translationZ);
-    }
-
-    /**
-     * The depth location of this view relative to its {@link #getElevation(View) elevation}.
-     *
-     * @return The depth of this view relative to its elevation.
-     */
-    public static float getTranslationZ(View view) {
-        return IMPL.getTranslationZ(view);
-    }
-
-    /**
-     * Sets the name of the View to be used to identify Views in Transitions.
-     * Names should be unique in the View hierarchy.
-     *
-     * @param view The View against which to invoke the method.
-     * @param transitionName The name of the View to uniquely identify it for Transitions.
-     */
-    public static void setTransitionName(View view, String transitionName) {
-        IMPL.setTransitionName(view, transitionName);
-    }
-
-    /**
-     * Returns the name of the View to be used to identify Views in Transitions.
-     * Names should be unique in the View hierarchy.
-     *
-     * <p>This returns null if the View has not been given a name.</p>
-     *
-     * @param view The View against which to invoke the method.
-     * @return The name used of the View to be used to identify Views in Transitions or null
-     * if no name has been given.
-     */
-    public static String getTransitionName(View view) {
-        return IMPL.getTransitionName(view);
-    }
-
-    /**
-     * Returns the current system UI visibility that is currently set for the entire window.
-     */
-    public static int getWindowSystemUiVisibility(View view) {
-        return IMPL.getWindowSystemUiVisibility(view);
-    }
-
-    /**
-     * Ask that a new dispatch of {@code View.onApplyWindowInsets(WindowInsets)} be performed. This
-     * falls back to {@code View.requestFitSystemWindows()} where available.
-     */
-    public static void requestApplyInsets(View view) {
-        IMPL.requestApplyInsets(view);
-    }
-
-    /**
-     * Tells the ViewGroup whether to draw its children in the order defined by the method
-     * {@code ViewGroup.getChildDrawingOrder(int, int)}.
-     *
-     * @param enabled true if the order of the children when drawing is determined by
-     *        {@link ViewGroup#getChildDrawingOrder(int, int)}, false otherwise
-     *
-     * <p>Prior to API 7 this will have no effect.</p>
-     *
-     * @deprecated Use {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)} directly.
-     */
-    @Deprecated
-    public static void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) {
-       IMPL.setChildrenDrawingOrderEnabled(viewGroup, enabled);
-    }
-
-    /**
-     * Returns true if this view should adapt to fit system window insets. This method will always
-     * return false before API 16 (Jellybean).
-     */
-    public static boolean getFitsSystemWindows(View v) {
-        return IMPL.getFitsSystemWindows(v);
-    }
-
-    /**
-     * Sets whether or not this view should account for system screen decorations
-     * 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) {
-        view.setFitsSystemWindows(fitSystemWindows);
-    }
-
-    /**
-     * On API 11 devices and above, call <code>Drawable.jumpToCurrentState()</code>
-     * on all Drawable objects associated with this view.
-     * <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) {
-        v.jumpDrawablesToCurrentState();
-    }
-
-    /**
-     * Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying
-     * window insets to this view. This will only take effect on devices with API 21 or above.
-     */
-    public static void setOnApplyWindowInsetsListener(View v,
-            OnApplyWindowInsetsListener listener) {
-        IMPL.setOnApplyWindowInsetsListener(v, listener);
-    }
-
-    /**
-     * Called when the view should apply {@link WindowInsetsCompat} according to its internal policy.
-     *
-     * <p>Clients may supply an {@link OnApplyWindowInsetsListener} to a view. If one is set
-     * it will be called during dispatch instead of this method. The listener may optionally
-     * call this method from its own implementation if it wishes to apply the view's default
-     * insets policy in addition to its own.</p>
-     *
-     * @param view The View against which to invoke the method.
-     * @param insets Insets to apply
-     * @return The supplied insets with any applied insets consumed
-     */
-    public static WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {
-        return IMPL.onApplyWindowInsets(view, insets);
-    }
-
-    /**
-     * Request to apply the given window insets to this view or another view in its subtree.
-     *
-     * <p>This method should be called by clients wishing to apply insets corresponding to areas
-     * obscured by window decorations or overlays. This can include the status and navigation bars,
-     * action bars, input methods and more. New inset categories may be added in the future.
-     * The method returns the insets provided minus any that were applied by this view or its
-     * children.</p>
-     *
-     * @param insets Insets to apply
-     * @return The provided insets minus the insets that were consumed
-     */
-    public static WindowInsetsCompat dispatchApplyWindowInsets(View view,
-            WindowInsetsCompat insets) {
-        return IMPL.dispatchApplyWindowInsets(view, insets);
-    }
-
-    /**
-     * Controls whether the entire hierarchy under this view will save its
-     * state when a state saving traversal occurs from its parent.
-     *
-     * @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) {
-        v.setSaveFromParentEnabled(enabled);
-    }
-
-    /**
-     * Changes the activated state of this view. A view can be activated or not.
-     * Note that activation is not the same as selection.  Selection is
-     * a transient property, representing the view (hierarchy) the user is
-     * currently interacting with.  Activation is a longer-term state that the
-     * 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) {
-        view.setActivated(activated);
-    }
-
-    /**
-     * Returns whether this View has content which overlaps.
-     *
-     * <p>This function, intended to be overridden by specific View types, is an optimization when
-     * alpha is set on a view. If rendering overlaps in a view with alpha < 1, that view is drawn to
-     * an offscreen buffer and then composited into place, which can be expensive. If the view has
-     * no overlapping rendering, the view can draw each primitive with the appropriate alpha value
-     * directly. An example of overlapping rendering is a TextView with a background image, such as
-     * a Button. An example of non-overlapping rendering is a TextView with no background, or an
-     * ImageView with only the foreground image. The default implementation returns true; subclasses
-     * should override if they have cases which can be optimized.</p>
-     *
-     * @return true if the content in this view might overlap, false otherwise.
-     */
-    public static boolean hasOverlappingRendering(View view) {
-        return IMPL.hasOverlappingRendering(view);
-    }
-
-    /**
-     * Return if the padding as been set through relative values
-     * {@code View.setPaddingRelative(int, int, int, int)} or thru
-     *
-     * @return true if the padding is relative or false if it is not.
-     */
-    public static boolean isPaddingRelative(View view) {
-        return IMPL.isPaddingRelative(view);
-    }
-
-    /**
-     * Set the background of the {@code view} to a given Drawable, or remove the background. If the
-     * background has padding, {@code view}'s padding is set to the background's padding. However,
-     * when a background is removed, this View's padding isn't touched. If setting the padding is
-     * desired, please use{@code setPadding(int, int, int, int)}.
-     */
-    public static void setBackground(View view, Drawable background) {
-        IMPL.setBackground(view, background);
-    }
-
-    /**
-     * Return the tint applied to the background drawable, if specified.
-     * <p>
-     * Only returns meaningful info when running on API v21 or newer, or if {@code view}
-     * implements the {@code TintableBackgroundView} interface.
-     */
-    public static ColorStateList getBackgroundTintList(View view) {
-        return IMPL.getBackgroundTintList(view);
-    }
-
-    /**
-     * Applies a tint to the background drawable.
-     * <p>
-     * This will always take effect when running on API v21 or newer. When running on platforms
-     * previous to API v21, it will only take effect if {@code view} implements the
-     * {@code TintableBackgroundView} interface.
-     */
-    public static void setBackgroundTintList(View view, ColorStateList tintList) {
-        IMPL.setBackgroundTintList(view, tintList);
-    }
-
-    /**
-     * Return the blending mode used to apply the tint to the background
-     * drawable, if specified.
-     * <p>
-     * Only returns meaningful info when running on API v21 or newer, or if {@code view}
-     * implements the {@code TintableBackgroundView} interface.
-     */
-    public static PorterDuff.Mode getBackgroundTintMode(View view) {
-        return IMPL.getBackgroundTintMode(view);
-    }
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setBackgroundTintList(android.view.View, android.content.res.ColorStateList)} to
-     * the background drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
-     * <p>
-     * This will always take effect when running on API v21 or newer. When running on platforms
-     * previous to API v21, it will only take effect if {@code view} implement the
-     * {@code TintableBackgroundView} interface.
-     */
-    public static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
-        IMPL.setBackgroundTintMode(view, mode);
-    }
-
-    // TODO: getters for various view properties (rotation, etc)
-
-    /**
-     * Enable or disable nested scrolling for this view.
-     *
-     * <p>If this property is set to true the view will be permitted to initiate nested
-     * scrolling operations with a compatible parent view in the current hierarchy. If this
-     * view does not implement nested scrolling this will have no effect. Disabling nested scrolling
-     * while a nested scroll is in progress has the effect of
-     * {@link #stopNestedScroll(View) stopping} the nested scroll.</p>
-     *
-     * @param enabled true to enable nested scrolling, false to disable
-     *
-     * @see #isNestedScrollingEnabled(View)
-     */
-    public static void setNestedScrollingEnabled(@NonNull View view, boolean enabled) {
-        IMPL.setNestedScrollingEnabled(view, enabled);
-    }
-
-    /**
-     * Returns true if nested scrolling is enabled for this view.
-     *
-     * <p>If nested scrolling is enabled and this View class implementation supports it,
-     * this view will act as a nested scrolling child view when applicable, forwarding data
-     * about the scroll operation in progress to a compatible and cooperating nested scrolling
-     * parent.</p>
-     *
-     * @return true if nested scrolling is enabled
-     *
-     * @see #setNestedScrollingEnabled(View, boolean)
-     */
-    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
-     * of a touch scroll 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(View)} 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(View, 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(View, 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 NestedScrollingParent#onNestedScroll(View, 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(View)
-     * @see #dispatchNestedPreScroll(View, int, int, int[], int[])
-     * @see #dispatchNestedScroll(View, int, int, int, int, int[])
-     */
-    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;
-    }
-
-    /**
-     * Stop a nested scroll in progress.
-     *
-     * <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(@NonNull View view, @NestedScrollType int type) {
-        if (view instanceof NestedScrollingChild2) {
-            ((NestedScrollingChild2) view).stopNestedScroll(type);
-        } else if (type == ViewCompat.TYPE_TOUCH) {
-            IMPL.stopNestedScroll(view);
-        }
-    }
-
-    /**
-     * Returns true if this view has a nested scrolling parent.
-     *
-     * <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(@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;
-    }
-
-    /**
-     * 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(View) enabled} for this view this method does nothing.</p>
-     *
-     * <p>Compatible View implementations should also call
-     * {@link #dispatchNestedPreScroll(View, 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(View, int, int, int[], int[])
-     */
-    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;
-    }
-
-    /**
-     * 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(View, int, int, int, int, int[])
-     */
-    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;
-    }
-
-    /**
-     * Dispatch a fling to a nested scrolling parent.
-     *
-     * <p>This method should be used to indicate that a nested scrolling child has detected
-     * suitable conditions for a fling. Generally this means that a touch scroll has ended with a
-     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
-     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
-     * along a scrollable axis.</p>
-     *
-     * <p>If a nested scrolling child view would normally fling but it is at the edge of
-     * its own content, it can use this method to delegate the fling to its nested scrolling
-     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
-     *
-     * @param velocityX Horizontal fling velocity in pixels per second
-     * @param velocityY Vertical fling velocity in pixels per second
-     * @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(@NonNull View view, float velocityX, float velocityY,
-            boolean consumed) {
-        return IMPL.dispatchNestedFling(view, velocityX, velocityY, consumed);
-    }
-
-    /**
-     * Dispatch a fling to a nested scrolling parent before it is processed by this view.
-     *
-     * <p>Nested pre-fling events are to nested fling events what touch intercept is to touch
-     * and what nested pre-scroll is to nested scroll. <code>dispatchNestedPreFling</code>
-     * offsets an opportunity for the parent view in a nested fling to fully consume the fling
-     * before the child view consumes it. If this method returns <code>true</code>, a nested
-     * parent view consumed the fling and this view should not scroll as a result.</p>
-     *
-     * <p>For a better user experience, only one view in a nested scrolling chain should consume
-     * the fling at a time. If a parent view consumed the fling this method will return false.
-     * Custom view implementations should account for this in two ways:</p>
-     *
-     * <ul>
-     *     <li>If a custom view is paged and needs to settle to a fixed page-point, do not
-     *     call <code>dispatchNestedPreFling</code>; consume the fling and settle to a valid
-     *     position regardless.</li>
-     *     <li>If a nested parent does consume the fling, this view should not scroll at all,
-     *     even to settle back to a valid idle position.</li>
-     * </ul>
-     *
-     * <p>Views should also not offer fling velocities to nested parent views along an axis
-     * where scrolling is not currently supported; a {@link android.widget.ScrollView ScrollView}
-     * should not offer a horizontal fling velocity to its parents since scrolling along that
-     * axis is not permitted and carrying velocity along that motion does not make sense.</p>
-     *
-     * @param velocityX Horizontal fling velocity in pixels per second
-     * @param velocityY Vertical fling velocity in pixels per second
-     * @return true if a nested scrolling parent consumed the fling
-     */
-    public static boolean dispatchNestedPreFling(@NonNull View view, float velocityX,
-            float velocityY) {
-        return IMPL.dispatchNestedPreFling(view, velocityX, velocityY);
-    }
-
-    /**
-     * Returns whether the view hierarchy is currently undergoing a layout pass. This
-     * information is useful to avoid situations such as calling {@link View#requestLayout()}
-     * during a layout pass.
-     * <p>
-     * Compatibility:
-     * <ul>
-     *     <li>API &lt; 18: Always returns {@code false}</li>
-     * </ul>
-     *
-     * @return whether the view hierarchy is currently undergoing a layout pass
-     */
-    public static boolean isInLayout(View view) {
-        return IMPL.isInLayout(view);
-    }
-
-    /**
-     * Returns true if {@code view} has been through at least one layout since it
-     * was last attached to or detached from a window.
-     */
-    public static boolean isLaidOut(View view) {
-        return IMPL.isLaidOut(view);
-    }
-
-    /**
-     * Returns whether layout direction has been resolved.
-     * <p>
-     * Compatibility:
-     * <ul>
-     *     <li>API &lt; 19: Always returns {@code false}</li>
-     * </ul>
-     *
-     * @return true if layout direction has been resolved.
-     */
-    public static boolean isLayoutDirectionResolved(View view) {
-        return IMPL.isLayoutDirectionResolved(view);
-    }
-
-    /**
-     * The visual z position of this view, in pixels. This is equivalent to the
-     * {@link #setTranslationZ(View, float) translationZ} property plus the current
-     * {@link #getElevation(View) elevation} property.
-     *
-     * @return The visual z position of this view, in pixels.
-     */
-    public static float getZ(View view) {
-        return IMPL.getZ(view);
-    }
-
-    /**
-     * Sets the visual z position of this view, in pixels. This is equivalent to setting the
-     * {@link #setTranslationZ(View, float) translationZ} property to be the difference between
-     * the x value passed in and the current {@link #getElevation(View) elevation} property.
-     * <p>
-     * Compatibility:
-     * <ul>
-     *     <li>API &lt; 21: No-op
-     * </ul>
-     *
-     * @param z The visual z position of this view, in pixels.
-     */
-    public static void setZ(View view, float z) {
-        IMPL.setZ(view, z);
-    }
-
-    /**
-     * Offset this view's vertical location by the specified number of pixels.
-     *
-     * @param offset the number of pixels to offset the view by
-     */
-    public static void offsetTopAndBottom(View view, int offset) {
-        IMPL.offsetTopAndBottom(view, offset);
-    }
-
-    /**
-     * Offset this view's horizontal location by the specified amount of pixels.
-     *
-     * @param offset the number of pixels to offset the view by
-     */
-    public static void offsetLeftAndRight(View view, int offset) {
-        IMPL.offsetLeftAndRight(view, offset);
-    }
-
-    /**
-     * Sets a rectangular area on this view to which the view will be clipped
-     * when it is drawn. Setting the value to null will remove the clip bounds
-     * and the view will draw normally, using its full bounds.
-     *
-     * <p>Prior to API 18 this does nothing.</p>
-     *
-     * @param view       The view to set clipBounds.
-     * @param clipBounds The rectangular area, in the local coordinates of
-     * this view, to which future drawing operations will be clipped.
-     */
-    public static void setClipBounds(View view, Rect clipBounds) {
-        IMPL.setClipBounds(view, clipBounds);
-    }
-
-    /**
-     * Returns a copy of the current {@link #setClipBounds(View, Rect)}.
-     *
-     * <p>Prior to API 18 this will return null.</p>
-     *
-     * @return A copy of the current clip bounds if clip bounds are set,
-     * otherwise null.
-     */
-    public static Rect getClipBounds(View view) {
-        return IMPL.getClipBounds(view);
-    }
-
-    /**
-     * Returns true if the provided view is currently attached to a window.
-     */
-    public static boolean isAttachedToWindow(View view) {
-        return IMPL.isAttachedToWindow(view);
-    }
-
-    /**
-     * Returns whether the provided view has an attached {@link View.OnClickListener}.
-     *
-     * @return true if there is a listener, false if there is none.
-     */
-    public static boolean hasOnClickListeners(View view) {
-        return IMPL.hasOnClickListeners(view);
-    }
-
-    /**
-     * Sets the state of all scroll indicators.
-     * <p>
-     * See {@link #setScrollIndicators(View, int, int)} for usage information.
-     *
-     * @param indicators a bitmask of indicators that should be enabled, or
-     *                   {@code 0} to disable all indicators
-     *
-     * @see #setScrollIndicators(View, int, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) {
-        IMPL.setScrollIndicators(view, indicators);
-    }
-
-    /**
-     * Sets the state of the scroll indicators specified by the mask. To change
-     * all scroll indicators at once, see {@link #setScrollIndicators(View, int)}.
-     * <p>
-     * When a scroll indicator is enabled, it will be displayed if the view
-     * can scroll in the direction of the indicator.
-     * <p>
-     * Multiple indicator types may be enabled or disabled by passing the
-     * logical OR of the desired types. If multiple types are specified, they
-     * will all be set to the same enabled state.
-     * <p>
-     * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators}
-     *
-     * @param indicators the indicator direction, or the logical OR of multiple
-     *             indicator directions. One or more of:
-     *             <ul>
-     *               <li>{@link #SCROLL_INDICATOR_TOP}</li>
-     *               <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
-     *               <li>{@link #SCROLL_INDICATOR_LEFT}</li>
-     *               <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
-     *               <li>{@link #SCROLL_INDICATOR_START}</li>
-     *               <li>{@link #SCROLL_INDICATOR_END}</li>
-     *             </ul>
-     *
-     * @see #setScrollIndicators(View, int)
-     * @see #getScrollIndicators(View)
-     */
-    public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators,
-            @ScrollIndicators int mask) {
-        IMPL.setScrollIndicators(view, indicators, mask);
-    }
-
-    /**
-     * Returns a bitmask representing the enabled scroll indicators.
-     * <p>
-     * For example, if the top and left scroll indicators are enabled and all
-     * other indicators are disabled, the return value will be
-     * {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}.
-     * <p>
-     * To check whether the bottom scroll indicator is enabled, use the value
-     * of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}.
-     *
-     * @return a bitmask representing the enabled scroll indicators
-     */
-    public static int getScrollIndicators(@NonNull View view) {
-        return IMPL.getScrollIndicators(view);
-    }
-
-    /**
-     * Set the pointer icon for the current view.
-     * @param pointerIcon A PointerIconCompat instance which will be shown when the mouse hovers.
-     */
-    public static void setPointerIcon(@NonNull View view, PointerIconCompat pointerIcon) {
-        IMPL.setPointerIcon(view, pointerIcon);
-    }
-
-    /**
-     * Gets the logical display to which the view's window has been attached.
-     * <p>
-     * Compatibility:
-     * <ul>
-     * <li>API &lt; 17: Returns the default display when the view is attached. Otherwise, null.
-     * </ul>
-     *
-     * @return The logical display, or null if the view is not currently attached to a window.
-     */
-    public static Display getDisplay(@NonNull View view) {
-        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);
-    }
-
-    /**
-     * Generate a value suitable for use in {@link View#setId(int)}.
-     * This value will not collide with ID values generated at build time by aapt for R.id.
-     *
-     * @return a generated ID value
-     */
-    public static int generateViewId() {
-        return IMPL.generateViewId();
-    }
-
-    protected ViewCompat() {}
-}
diff --git a/android/support/v4/view/ViewConfigurationCompat.java b/android/support/v4/view/ViewConfigurationCompat.java
deleted file mode 100644
index a12387b..0000000
--- a/android/support/v4/view/ViewConfigurationCompat.java
+++ /dev/null
@@ -1,133 +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.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}.
- */
-public final class ViewConfigurationCompat {
-    private static final String TAG = "ViewConfigCompat";
-
-    private static Method sGetScaledScrollFactorMethod;
-
-    static {
-        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()}.
-     *
-     * @deprecated Call {@link ViewConfiguration#getScaledPagingTouchSlop()} directly.
-     * This method will be removed in a future release.
-     */
-    @Deprecated
-    public static int getScaledPagingTouchSlop(ViewConfiguration config) {
-        return config.getScaledPagingTouchSlop();
-    }
-
-    /**
-     * 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 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;
-    }
-
-    /**
-     * @param config Used to get the hover slop directly from the {@link ViewConfiguration}.
-     *
-     * @return The hover slop value.
-     */
-    public static int getScaledHoverSlop(ViewConfiguration config) {
-        if (android.os.Build.VERSION.SDK_INT >= 28) {
-            return config.getScaledHoverSlop();
-        }
-        return config.getScaledTouchSlop() / 2;
-    }
-
-    private ViewConfigurationCompat() {}
-}
diff --git a/android/support/v4/view/ViewGroupCompat.java b/android/support/v4/view/ViewGroupCompat.java
deleted file mode 100644
index 445a587..0000000
--- a/android/support/v4/view/ViewGroupCompat.java
+++ /dev/null
@@ -1,237 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.compat.R;
-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}.
- */
-public final class ViewGroupCompat {
-
-    /**
-     * This constant is a {@link #setLayoutMode(ViewGroup, int) layoutMode}.
-     * Clip bounds are the raw values of {@link android.view.View#getLeft() left},
-     * {@link android.view.View#getTop() top},
-     * {@link android.view.View#getRight() right} and {@link android.view.View#getBottom() bottom}.
-     */
-    public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
-
-    /**
-     * This constant is a {@link #setLayoutMode(ViewGroup, int) layoutMode}.
-     * Optical bounds describe where a widget appears to be. They sit inside the clip
-     * bounds which need to cover a larger area to allow other effects,
-     * such as shadows and glows, to be drawn.
-     */
-    public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
-
-    static class ViewGroupCompatBaseImpl {
-        public int getLayoutMode(ViewGroup group) {
-            return LAYOUT_MODE_CLIP_BOUNDS;
-        }
-
-        public void setLayoutMode(ViewGroup group, int mode) {
-            // no-op, didn't exist. Views only support clip bounds.
-        }
-
-        public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-            group.setTag(R.id.tag_transition_group, isTransitionGroup);
-        }
-
-        public boolean isTransitionGroup(ViewGroup group) {
-            Boolean explicit = (Boolean) group.getTag(R.id.tag_transition_group);
-            return (explicit != null && explicit)
-                    || group.getBackground() != null
-                    || ViewCompat.getTransitionName(group) != null;
-        }
-
-        public int getNestedScrollAxes(ViewGroup group) {
-            if (group instanceof NestedScrollingParent) {
-                return ((NestedScrollingParent) group).getNestedScrollAxes();
-            }
-            return ViewCompat.SCROLL_AXIS_NONE;
-        }
-    }
-
-    @RequiresApi(18)
-    static class ViewGroupCompatApi18Impl extends ViewGroupCompatBaseImpl {
-        @Override
-        public int getLayoutMode(ViewGroup group) {
-            return group.getLayoutMode();
-        }
-
-        @Override
-        public void setLayoutMode(ViewGroup group, int mode) {
-            group.setLayoutMode(mode);
-        }
-    }
-
-    @RequiresApi(21)
-    static class ViewGroupCompatApi21Impl extends ViewGroupCompatApi18Impl {
-        @Override
-        public void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-            group.setTransitionGroup(isTransitionGroup);
-        }
-
-        @Override
-        public boolean isTransitionGroup(ViewGroup group) {
-            return group.isTransitionGroup();
-        }
-
-        @Override
-        public int getNestedScrollAxes(ViewGroup group) {
-            return group.getNestedScrollAxes();
-        }
-    }
-
-    static final ViewGroupCompatBaseImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new ViewGroupCompatApi21Impl();
-        } else if (Build.VERSION.SDK_INT >= 18) {
-            IMPL = new ViewGroupCompatApi18Impl();
-        } else {
-            IMPL = new ViewGroupCompatBaseImpl();
-        }
-    }
-
-    /*
-     * Hide the constructor.
-     */
-    private ViewGroupCompat() {}
-
-    /**
-     * Called when a child has requested sending an {@link AccessibilityEvent} and
-     * gives an opportunity to its parent to augment the event.
-     * <p>
-     * If an {@link AccessibilityDelegateCompat} has been specified via calling
-     * {@link ViewCompat#setAccessibilityDelegate(View, AccessibilityDelegateCompat)} its
-     * {@link AccessibilityDelegateCompat#onRequestSendAccessibilityEvent(ViewGroup, View,
-     * AccessibilityEvent)} is responsible for handling this call.
-     * </p>
-     *
-     * @param group The group whose method to invoke.
-     * @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 group.onRequestSendAccessibilityEvent(child, event);
-    }
-
-    /**
-     * Enable or disable the splitting of MotionEvents to multiple children during touch event
-     * dispatch. This behavior is enabled by default for applications that target an
-     * SDK version of 11 (Honeycomb) or newer. On earlier platform versions this feature
-     * was not supported and this method is a no-op.
-     *
-     * <p>When this option is enabled MotionEvents may be split and dispatched to different child
-     * views depending on where each pointer initially went down. This allows for user interactions
-     * such as scrolling two panes of content independently, chording of buttons, and performing
-     * independent gestures on different pieces of content.
-     *
-     * @param group ViewGroup to modify
-     * @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) {
-        group.setMotionEventSplittingEnabled(split);
-    }
-
-    /**
-     * Returns the basis of alignment during layout operations on this ViewGroup:
-     * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
-     * <p>
-     * If no layoutMode was explicitly set, either programmatically or in an XML resource,
-     * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
-     * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
-     *
-     * @return the layout mode to use during layout operations
-     *
-     * @see #setLayoutMode(ViewGroup, int)
-     */
-    public static int getLayoutMode(ViewGroup group) {
-        return IMPL.getLayoutMode(group);
-    }
-
-    /**
-     * Sets the basis of alignment during the layout of this ViewGroup.
-     * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
-     * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
-     *
-     * @param mode the layout mode to use during layout operations
-     *
-     * @see #getLayoutMode(ViewGroup)
-     */
-    public static void setLayoutMode(ViewGroup group, int mode) {
-        IMPL.setLayoutMode(group, mode);
-    }
-
-    /**
-     * Changes whether or not this ViewGroup should be treated as a single entity during
-     * Activity Transitions.
-     * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
-     *                          in Activity transitions. If false, the ViewGroup won't transition,
-     *                          only its children. If true, the entire ViewGroup will transition
-     *                          together.
-     */
-    public static void setTransitionGroup(ViewGroup group, boolean isTransitionGroup) {
-        IMPL.setTransitionGroup(group, isTransitionGroup);
-    }
-
-    /**
-     * Returns true if this ViewGroup should be considered as a single entity for removal
-     * when executing an Activity transition. If this is false, child elements will move
-     * individually during the transition.
-     */
-    public static boolean isTransitionGroup(ViewGroup group) {
-        return IMPL.isTransitionGroup(group);
-    }
-
-    /**
-     * Return the current axes of nested scrolling for this ViewGroup.
-     *
-     * <p>A ViewGroup returning something other than {@link ViewCompat#SCROLL_AXIS_NONE} is
-     * currently acting as a nested scrolling parent for one or more descendant views in
-     * the hierarchy.</p>
-     *
-     * @return Flags indicating the current axes of nested scrolling
-     * @see ViewCompat#SCROLL_AXIS_HORIZONTAL
-     * @see ViewCompat#SCROLL_AXIS_VERTICAL
-     * @see ViewCompat#SCROLL_AXIS_NONE
-     */
-    @ScrollAxis
-    public static int getNestedScrollAxes(@NonNull ViewGroup group) {
-        return IMPL.getNestedScrollAxes(group);
-    }
-}
diff --git a/android/support/v4/view/ViewPager.java b/android/support/v4/view/ViewPager.java
deleted file mode 100644
index 350fe95..0000000
--- a/android/support/v4/view/ViewPager.java
+++ /dev/null
@@ -1,3162 +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.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.annotation.CallSuper;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.FocusFinder;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-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;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Layout manager that allows the user to flip left and right
- * through pages of data.  You supply an implementation of a
- * {@link PagerAdapter} to generate the pages that the view shows.
- *
- * <p>ViewPager is most often used in conjunction with {@link android.app.Fragment},
- * which is a convenient way to supply and manage the lifecycle of each page.
- * There are standard adapters implemented for using fragments with the ViewPager,
- * which cover the most common use cases.  These are
- * {@link android.support.v4.app.FragmentPagerAdapter} and
- * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these
- * classes have simple code showing how to build a full user interface
- * with them.
- *
- * <p>Views which are annotated with the {@link DecorView} annotation are treated as
- * part of the view pagers 'decor'. Each decor view's position can be controlled via
- * its {@code android:layout_gravity} attribute. For example:
- *
- * <pre>
- * &lt;android.support.v4.view.ViewPager
- *     android:layout_width=&quot;match_parent&quot;
- *     android:layout_height=&quot;match_parent&quot;&gt;
- *
- *     &lt;android.support.v4.view.PagerTitleStrip
- *         android:layout_width=&quot;match_parent&quot;
- *         android:layout_height=&quot;wrap_content&quot;
- *         android:layout_gravity=&quot;top&quot; /&gt;
- *
- * &lt;/android.support.v4.view.ViewPager&gt;
- * </pre>
- *
- * <p>For more information about how to use ViewPager, read <a
- * href="{@docRoot}training/implementing-navigation/lateral.html">Creating Swipe Views with
- * Tabs</a>.</p>
- *
- * <p>You can find examples of using ViewPager in the API 4+ Support Demos and API 13+ Support Demos
- * sample code.
- */
-public class ViewPager extends ViewGroup {
-    private static final String TAG = "ViewPager";
-    private static final boolean DEBUG = false;
-
-    private static final boolean USE_CACHE = false;
-
-    private static final int DEFAULT_OFFSCREEN_PAGES = 1;
-    private static final int MAX_SETTLE_DURATION = 600; // ms
-    private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
-
-    private static final int DEFAULT_GUTTER_SIZE = 16; // dips
-
-    private static final int MIN_FLING_VELOCITY = 400; // dips
-
-    static final int[] LAYOUT_ATTRS = new int[] {
-        android.R.attr.layout_gravity
-    };
-
-    /**
-     * Used to track what the expected number of items in the adapter should be.
-     * If the app changes this when we don't expect it, we'll throw a big obnoxious exception.
-     */
-    private int mExpectedAdapterCount;
-
-    static class ItemInfo {
-        Object object;
-        int position;
-        boolean scrolling;
-        float widthFactor;
-        float offset;
-    }
-
-    private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>(){
-        @Override
-        public int compare(ItemInfo lhs, ItemInfo rhs) {
-            return lhs.position - rhs.position;
-        }
-    };
-
-    private static final Interpolator sInterpolator = new Interpolator() {
-        @Override
-        public float getInterpolation(float t) {
-            t -= 1.0f;
-            return t * t * t * t * t + 1.0f;
-        }
-    };
-
-    private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
-    private final ItemInfo mTempItem = new ItemInfo();
-
-    private final Rect mTempRect = new Rect();
-
-    PagerAdapter mAdapter;
-    int mCurItem;   // Index of currently displayed page.
-    private int mRestoredCurItem = -1;
-    private Parcelable mRestoredAdapterState = null;
-    private ClassLoader mRestoredClassLoader = null;
-
-    private Scroller mScroller;
-    private boolean mIsScrollStarted;
-
-    private PagerObserver mObserver;
-
-    private int mPageMargin;
-    private Drawable mMarginDrawable;
-    private int mTopPageBounds;
-    private int mBottomPageBounds;
-
-    // Offsets of the first and last items, if known.
-    // Set during population, used to determine if we are at the beginning
-    // or end of the pager data set during touch scrolling.
-    private float mFirstOffset = -Float.MAX_VALUE;
-    private float mLastOffset = Float.MAX_VALUE;
-
-    private int mChildWidthMeasureSpec;
-    private int mChildHeightMeasureSpec;
-    private boolean mInLayout;
-
-    private boolean mScrollingCacheEnabled;
-
-    private boolean mPopulatePending;
-    private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;
-
-    private boolean mIsBeingDragged;
-    private boolean mIsUnableToDrag;
-    private int mDefaultGutterSize;
-    private int mGutterSize;
-    private int mTouchSlop;
-    /**
-     * Position of the last motion event.
-     */
-    private float mLastMotionX;
-    private float mLastMotionY;
-    private float mInitialMotionX;
-    private float mInitialMotionY;
-    /**
-     * ID of the active pointer. This is used to retain consistency during
-     * drags/flings if multiple pointers are used.
-     */
-    private int mActivePointerId = INVALID_POINTER;
-    /**
-     * Sentinel value for no current active pointer.
-     * Used by {@link #mActivePointerId}.
-     */
-    private static final int INVALID_POINTER = -1;
-
-    /**
-     * Determines speed during touch scrolling
-     */
-    private VelocityTracker mVelocityTracker;
-    private int mMinimumVelocity;
-    private int mMaximumVelocity;
-    private int mFlingDistance;
-    private int mCloseEnough;
-
-    // If the pager is at least this close to its final position, complete the scroll
-    // on touch down and let the user interact with the content inside instead of
-    // "catching" the flinging pager.
-    private static final int CLOSE_ENOUGH = 2; // dp
-
-    private boolean mFakeDragging;
-    private long mFakeDragBeginTime;
-
-    private EdgeEffect mLeftEdge;
-    private EdgeEffect mRightEdge;
-
-    private boolean mFirstLayout = true;
-    private boolean mNeedCalculatePageOffsets = false;
-    private boolean mCalledSuper;
-    private int mDecorChildCount;
-
-    private List<OnPageChangeListener> mOnPageChangeListeners;
-    private OnPageChangeListener mOnPageChangeListener;
-    private OnPageChangeListener mInternalPageChangeListener;
-    private List<OnAdapterChangeListener> mAdapterChangeListeners;
-    private PageTransformer mPageTransformer;
-    private int mPageTransformerLayerType;
-
-    private static final int DRAW_ORDER_DEFAULT = 0;
-    private static final int DRAW_ORDER_FORWARD = 1;
-    private static final int DRAW_ORDER_REVERSE = 2;
-    private int mDrawingOrder;
-    private ArrayList<View> mDrawingOrderedChildren;
-    private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();
-
-    /**
-     * Indicates that the pager is in an idle, settled state. The current page
-     * is fully in view and no animation is in progress.
-     */
-    public static final int SCROLL_STATE_IDLE = 0;
-
-    /**
-     * Indicates that the pager is currently being dragged by the user.
-     */
-    public static final int SCROLL_STATE_DRAGGING = 1;
-
-    /**
-     * Indicates that the pager is in the process of settling to a final position.
-     */
-    public static final int SCROLL_STATE_SETTLING = 2;
-
-    private final Runnable mEndScrollRunnable = new Runnable() {
-        @Override
-        public void run() {
-            setScrollState(SCROLL_STATE_IDLE);
-            populate();
-        }
-    };
-
-    private int mScrollState = SCROLL_STATE_IDLE;
-
-    /**
-     * Callback interface for responding to changing state of the selected page.
-     */
-    public interface OnPageChangeListener {
-
-        /**
-         * This method will be invoked when the current page is scrolled, either as part
-         * of a programmatically initiated smooth scroll or a user initiated touch scroll.
-         *
-         * @param position Position index of the first page currently being displayed.
-         *                 Page position+1 will be visible if positionOffset is nonzero.
-         * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
-         * @param positionOffsetPixels Value in pixels indicating the offset from position.
-         */
-        void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
-
-        /**
-         * This method will be invoked when a new page becomes selected. Animation is not
-         * necessarily complete.
-         *
-         * @param position Position index of the new selected page.
-         */
-        void onPageSelected(int position);
-
-        /**
-         * Called when the scroll state changes. Useful for discovering when the user
-         * begins dragging, when the pager is automatically settling to the current page,
-         * or when it is fully stopped/idle.
-         *
-         * @param state The new scroll state.
-         * @see ViewPager#SCROLL_STATE_IDLE
-         * @see ViewPager#SCROLL_STATE_DRAGGING
-         * @see ViewPager#SCROLL_STATE_SETTLING
-         */
-        void onPageScrollStateChanged(int state);
-    }
-
-    /**
-     * Simple implementation of the {@link OnPageChangeListener} interface with stub
-     * implementations of each method. Extend this if you do not intend to override
-     * every method of {@link OnPageChangeListener}.
-     */
-    public static class SimpleOnPageChangeListener implements OnPageChangeListener {
-        @Override
-        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-            // This space for rent
-        }
-
-        @Override
-        public void onPageSelected(int position) {
-            // This space for rent
-        }
-
-        @Override
-        public void onPageScrollStateChanged(int state) {
-            // This space for rent
-        }
-    }
-
-    /**
-     * A PageTransformer is invoked whenever a visible/attached page is scrolled.
-     * This offers an opportunity for the application to apply a custom transformation
-     * to the page views using animation properties.
-     *
-     * <p>As property animation is only supported as of Android 3.0 and forward,
-     * setting a PageTransformer on a ViewPager on earlier platform versions will
-     * be ignored.</p>
-     */
-    public interface PageTransformer {
-        /**
-         * Apply a property transformation to the given page.
-         *
-         * @param page Apply the transformation to this page
-         * @param position Position of page relative to the current front-and-center
-         *                 position of the pager. 0 is front and center. 1 is one full
-         *                 page position to the right, and -1 is one page position to the left.
-         */
-        void transformPage(@NonNull View page, float position);
-    }
-
-    /**
-     * Callback interface for responding to adapter changes.
-     */
-    public interface OnAdapterChangeListener {
-        /**
-         * Called when the adapter for the given view pager has changed.
-         *
-         * @param viewPager  ViewPager where the adapter change has happened
-         * @param oldAdapter the previously set adapter
-         * @param newAdapter the newly set adapter
-         */
-        void onAdapterChanged(@NonNull ViewPager viewPager,
-                @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter);
-    }
-
-    /**
-     * Annotation which allows marking of views to be decoration views when added to a view
-     * pager.
-     *
-     * <p>Views marked with this annotation can be added to the view pager with a layout resource.
-     * An example being {@link PagerTitleStrip}.</p>
-     *
-     * <p>You can also control whether a view is a decor view but setting
-     * {@link LayoutParams#isDecor} on the child's layout params.</p>
-     */
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target(ElementType.TYPE)
-    @Inherited
-    public @interface DecorView {
-    }
-
-    public ViewPager(@NonNull Context context) {
-        super(context);
-        initViewPager();
-    }
-
-    public ViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        initViewPager();
-    }
-
-    void initViewPager() {
-        setWillNotDraw(false);
-        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
-        setFocusable(true);
-        final Context context = getContext();
-        mScroller = new Scroller(context, sInterpolator);
-        final ViewConfiguration configuration = ViewConfiguration.get(context);
-        final float density = context.getResources().getDisplayMetrics().density;
-
-        mTouchSlop = configuration.getScaledPagingTouchSlop();
-        mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mLeftEdge = new EdgeEffect(context);
-        mRightEdge = new EdgeEffect(context);
-
-        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
-        mCloseEnough = (int) (CLOSE_ENOUGH * density);
-        mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
-
-        ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());
-
-        if (ViewCompat.getImportantForAccessibility(this)
-                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-            ViewCompat.setImportantForAccessibility(this,
-                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-        }
-
-        ViewCompat.setOnApplyWindowInsetsListener(this,
-                new android.support.v4.view.OnApplyWindowInsetsListener() {
-                    private final Rect mTempRect = new Rect();
-
-                    @Override
-                    public WindowInsetsCompat onApplyWindowInsets(final View v,
-                            final WindowInsetsCompat originalInsets) {
-                        // First let the ViewPager itself try and consume them...
-                        final WindowInsetsCompat applied =
-                                ViewCompat.onApplyWindowInsets(v, originalInsets);
-                        if (applied.isConsumed()) {
-                            // If the ViewPager consumed all insets, return now
-                            return applied;
-                        }
-
-                        // Now we'll manually dispatch the insets to our children. Since ViewPager
-                        // children are always full-height, we do not want to use the standard
-                        // ViewGroup dispatchApplyWindowInsets since if child 0 consumes them,
-                        // the rest of the children will not receive any insets. To workaround this
-                        // we manually dispatch the applied insets, not allowing children to
-                        // consume them from each other. We do however keep track of any insets
-                        // which are consumed, returning the union of our children's consumption
-                        final Rect res = mTempRect;
-                        res.left = applied.getSystemWindowInsetLeft();
-                        res.top = applied.getSystemWindowInsetTop();
-                        res.right = applied.getSystemWindowInsetRight();
-                        res.bottom = applied.getSystemWindowInsetBottom();
-
-                        for (int i = 0, count = getChildCount(); i < count; i++) {
-                            final WindowInsetsCompat childInsets = ViewCompat
-                                    .dispatchApplyWindowInsets(getChildAt(i), applied);
-                            // Now keep track of any consumed by tracking each dimension's min
-                            // value
-                            res.left = Math.min(childInsets.getSystemWindowInsetLeft(),
-                                    res.left);
-                            res.top = Math.min(childInsets.getSystemWindowInsetTop(),
-                                    res.top);
-                            res.right = Math.min(childInsets.getSystemWindowInsetRight(),
-                                    res.right);
-                            res.bottom = Math.min(childInsets.getSystemWindowInsetBottom(),
-                                    res.bottom);
-                        }
-
-                        // Now return a new WindowInsets, using the consumed window insets
-                        return applied.replaceSystemWindowInsets(
-                                res.left, res.top, res.right, res.bottom);
-                    }
-                });
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        removeCallbacks(mEndScrollRunnable);
-        // To be on the safe side, abort the scroller
-        if ((mScroller != null) && !mScroller.isFinished()) {
-            mScroller.abortAnimation();
-        }
-        super.onDetachedFromWindow();
-    }
-
-    void setScrollState(int newState) {
-        if (mScrollState == newState) {
-            return;
-        }
-
-        mScrollState = newState;
-        if (mPageTransformer != null) {
-            // PageTransformers can do complex things that benefit from hardware layers.
-            enableLayers(newState != SCROLL_STATE_IDLE);
-        }
-        dispatchOnScrollStateChanged(newState);
-    }
-
-    /**
-     * Set a PagerAdapter that will supply views for this pager as needed.
-     *
-     * @param adapter Adapter to use
-     */
-    public void setAdapter(@Nullable PagerAdapter adapter) {
-        if (mAdapter != null) {
-            mAdapter.setViewPagerObserver(null);
-            mAdapter.startUpdate(this);
-            for (int i = 0; i < mItems.size(); i++) {
-                final ItemInfo ii = mItems.get(i);
-                mAdapter.destroyItem(this, ii.position, ii.object);
-            }
-            mAdapter.finishUpdate(this);
-            mItems.clear();
-            removeNonDecorViews();
-            mCurItem = 0;
-            scrollTo(0, 0);
-        }
-
-        final PagerAdapter oldAdapter = mAdapter;
-        mAdapter = adapter;
-        mExpectedAdapterCount = 0;
-
-        if (mAdapter != null) {
-            if (mObserver == null) {
-                mObserver = new PagerObserver();
-            }
-            mAdapter.setViewPagerObserver(mObserver);
-            mPopulatePending = false;
-            final boolean wasFirstLayout = mFirstLayout;
-            mFirstLayout = true;
-            mExpectedAdapterCount = mAdapter.getCount();
-            if (mRestoredCurItem >= 0) {
-                mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
-                setCurrentItemInternal(mRestoredCurItem, false, true);
-                mRestoredCurItem = -1;
-                mRestoredAdapterState = null;
-                mRestoredClassLoader = null;
-            } else if (!wasFirstLayout) {
-                populate();
-            } else {
-                requestLayout();
-            }
-        }
-
-        // Dispatch the change to any listeners
-        if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
-            for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
-                mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
-            }
-        }
-    }
-
-    private void removeNonDecorViews() {
-        for (int i = 0; i < getChildCount(); i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (!lp.isDecor) {
-                removeViewAt(i);
-                i--;
-            }
-        }
-    }
-
-    /**
-     * Retrieve the current adapter supplying pages.
-     *
-     * @return The currently registered PagerAdapter
-     */
-    @Nullable
-    public PagerAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Add a listener that will be invoked whenever the adapter for this ViewPager changes.
-     *
-     * @param listener listener to add
-     */
-    public void addOnAdapterChangeListener(@NonNull OnAdapterChangeListener listener) {
-        if (mAdapterChangeListeners == null) {
-            mAdapterChangeListeners = new ArrayList<>();
-        }
-        mAdapterChangeListeners.add(listener);
-    }
-
-    /**
-     * Remove a listener that was previously added via
-     * {@link #addOnAdapterChangeListener(OnAdapterChangeListener)}.
-     *
-     * @param listener listener to remove
-     */
-    public void removeOnAdapterChangeListener(@NonNull OnAdapterChangeListener listener) {
-        if (mAdapterChangeListeners != null) {
-            mAdapterChangeListeners.remove(listener);
-        }
-    }
-
-    private int getClientWidth() {
-        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
-    }
-
-    /**
-     * Set the currently selected page. If the ViewPager has already been through its first
-     * layout with its current adapter there will be a smooth animated transition between
-     * the current item and the specified item.
-     *
-     * @param item Item index to select
-     */
-    public void setCurrentItem(int item) {
-        mPopulatePending = false;
-        setCurrentItemInternal(item, !mFirstLayout, false);
-    }
-
-    /**
-     * Set the currently selected page.
-     *
-     * @param item Item index to select
-     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
-     */
-    public void setCurrentItem(int item, boolean smoothScroll) {
-        mPopulatePending = false;
-        setCurrentItemInternal(item, smoothScroll, false);
-    }
-
-    public int getCurrentItem() {
-        return mCurItem;
-    }
-
-    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
-        setCurrentItemInternal(item, smoothScroll, always, 0);
-    }
-
-    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
-        if (mAdapter == null || mAdapter.getCount() <= 0) {
-            setScrollingCacheEnabled(false);
-            return;
-        }
-        if (!always && mCurItem == item && mItems.size() != 0) {
-            setScrollingCacheEnabled(false);
-            return;
-        }
-
-        if (item < 0) {
-            item = 0;
-        } else if (item >= mAdapter.getCount()) {
-            item = mAdapter.getCount() - 1;
-        }
-        final int pageLimit = mOffscreenPageLimit;
-        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
-            // We are doing a jump by more than one page.  To avoid
-            // glitches, we want to keep all current pages in the view
-            // until the scroll ends.
-            for (int i = 0; i < mItems.size(); i++) {
-                mItems.get(i).scrolling = true;
-            }
-        }
-        final boolean dispatchSelected = mCurItem != item;
-
-        if (mFirstLayout) {
-            // We don't have any idea how big we are yet and shouldn't have any pages either.
-            // Just set things up and let the pending layout handle things.
-            mCurItem = item;
-            if (dispatchSelected) {
-                dispatchOnPageSelected(item);
-            }
-            requestLayout();
-        } else {
-            populate(item);
-            scrollToItem(item, smoothScroll, velocity, dispatchSelected);
-        }
-    }
-
-    private void scrollToItem(int item, boolean smoothScroll, int velocity,
-            boolean dispatchSelected) {
-        final ItemInfo curInfo = infoForPosition(item);
-        int destX = 0;
-        if (curInfo != null) {
-            final int width = getClientWidth();
-            destX = (int) (width * Math.max(mFirstOffset,
-                    Math.min(curInfo.offset, mLastOffset)));
-        }
-        if (smoothScroll) {
-            smoothScrollTo(destX, 0, velocity);
-            if (dispatchSelected) {
-                dispatchOnPageSelected(item);
-            }
-        } else {
-            if (dispatchSelected) {
-                dispatchOnPageSelected(item);
-            }
-            completeScroll(false);
-            scrollTo(destX, 0);
-            pageScrolled(destX);
-        }
-    }
-
-    /**
-     * Set a listener that will be invoked whenever the page changes or is incrementally
-     * scrolled. See {@link OnPageChangeListener}.
-     *
-     * @param listener Listener to set
-     *
-     * @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)}
-     * and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead.
-     */
-    @Deprecated
-    public void setOnPageChangeListener(OnPageChangeListener listener) {
-        mOnPageChangeListener = listener;
-    }
-
-    /**
-     * Add a listener that will be invoked whenever the page changes or is incrementally
-     * scrolled. See {@link OnPageChangeListener}.
-     *
-     * <p>Components that add a listener should take care to remove it when finished.
-     * Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()}
-     * to remove all attached listeners.</p>
-     *
-     * @param listener listener to add
-     */
-    public void addOnPageChangeListener(@NonNull OnPageChangeListener listener) {
-        if (mOnPageChangeListeners == null) {
-            mOnPageChangeListeners = new ArrayList<>();
-        }
-        mOnPageChangeListeners.add(listener);
-    }
-
-    /**
-     * Remove a listener that was previously added via
-     * {@link #addOnPageChangeListener(OnPageChangeListener)}.
-     *
-     * @param listener listener to remove
-     */
-    public void removeOnPageChangeListener(@NonNull OnPageChangeListener listener) {
-        if (mOnPageChangeListeners != null) {
-            mOnPageChangeListeners.remove(listener);
-        }
-    }
-
-    /**
-     * Remove all listeners that are notified of any changes in scroll state or position.
-     */
-    public void clearOnPageChangeListeners() {
-        if (mOnPageChangeListeners != null) {
-            mOnPageChangeListeners.clear();
-        }
-    }
-
-    /**
-     * Sets a {@link PageTransformer} that will be called for each attached page whenever
-     * 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> 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 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,
-            @Nullable PageTransformer transformer) {
-        setPageTransformer(reverseDrawingOrder, transformer, View.LAYER_TYPE_HARDWARE);
-    }
-
-    /**
-     * Sets a {@link PageTransformer} that will be called for each attached page whenever
-     * the scroll position is changed. This allows the application to apply custom property
-     * transformations to each page, overriding the default sliding behavior.
-     *
-     * @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 View#LAYER_TYPE_HARDWARE},
-     *                      {@link View#LAYER_TYPE_SOFTWARE}, or
-     *                      {@link View#LAYER_TYPE_NONE}.
-     */
-    public void setPageTransformer(boolean reverseDrawingOrder,
-            @Nullable PageTransformer transformer, int pageLayerType) {
-        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;
-        }
-        if (needsPopulate) populate();
-    }
-
-    @Override
-    protected int getChildDrawingOrder(int childCount, int i) {
-        final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;
-        final int result =
-                ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;
-        return result;
-    }
-
-    /**
-     * Set a separate OnPageChangeListener for internal use by the support library.
-     *
-     * @param listener Listener to set
-     * @return The old listener that was set, if any.
-     */
-    OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {
-        OnPageChangeListener oldListener = mInternalPageChangeListener;
-        mInternalPageChangeListener = listener;
-        return oldListener;
-    }
-
-    /**
-     * Returns the number of pages that will be retained to either side of the
-     * current page in the view hierarchy in an idle state. Defaults to 1.
-     *
-     * @return How many pages will be kept offscreen on either side
-     * @see #setOffscreenPageLimit(int)
-     */
-    public int getOffscreenPageLimit() {
-        return mOffscreenPageLimit;
-    }
-
-    /**
-     * Set the number of pages that should be retained to either side of the
-     * current page in the view hierarchy in an idle state. Pages beyond this
-     * limit will be recreated from the adapter when needed.
-     *
-     * <p>This is offered as an optimization. If you know in advance the number
-     * of pages you will need to support or have lazy-loading mechanisms in place
-     * on your pages, tweaking this setting can have benefits in perceived smoothness
-     * of paging animations and interaction. If you have a small number of pages (3-4)
-     * that you can keep active all at once, less time will be spent in layout for
-     * newly created view subtrees as the user pages back and forth.</p>
-     *
-     * <p>You should keep this limit low, especially if your pages have complex layouts.
-     * This setting defaults to 1.</p>
-     *
-     * @param limit How many pages will be kept offscreen in an idle state.
-     */
-    public void setOffscreenPageLimit(int limit) {
-        if (limit < DEFAULT_OFFSCREEN_PAGES) {
-            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
-                    + DEFAULT_OFFSCREEN_PAGES);
-            limit = DEFAULT_OFFSCREEN_PAGES;
-        }
-        if (limit != mOffscreenPageLimit) {
-            mOffscreenPageLimit = limit;
-            populate();
-        }
-    }
-
-    /**
-     * Set the margin between pages.
-     *
-     * @param marginPixels Distance between adjacent pages in pixels
-     * @see #getPageMargin()
-     * @see #setPageMarginDrawable(Drawable)
-     * @see #setPageMarginDrawable(int)
-     */
-    public void setPageMargin(int marginPixels) {
-        final int oldMargin = mPageMargin;
-        mPageMargin = marginPixels;
-
-        final int width = getWidth();
-        recomputeScrollPosition(width, width, marginPixels, oldMargin);
-
-        requestLayout();
-    }
-
-    /**
-     * Return the margin between pages.
-     *
-     * @return The size of the margin in pixels
-     */
-    public int getPageMargin() {
-        return mPageMargin;
-    }
-
-    /**
-     * Set a drawable that will be used to fill the margin between pages.
-     *
-     * @param d Drawable to display between pages
-     */
-    public void setPageMarginDrawable(@Nullable Drawable d) {
-        mMarginDrawable = d;
-        if (d != null) refreshDrawableState();
-        setWillNotDraw(d == null);
-        invalidate();
-    }
-
-    /**
-     * Set a drawable that will be used to fill the margin between pages.
-     *
-     * @param resId Resource ID of a drawable to display between pages
-     */
-    public void setPageMarginDrawable(@DrawableRes int resId) {
-        setPageMarginDrawable(ContextCompat.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mMarginDrawable;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        final Drawable d = mMarginDrawable;
-        if (d != null && d.isStateful()) {
-            d.setState(getDrawableState());
-        }
-    }
-
-    // We want the duration of the page snap animation to be influenced by the distance that
-    // the screen has to travel, however, we don't want this duration to be effected in a
-    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
-    // of travel has on the overall snap duration.
-    float distanceInfluenceForSnapDuration(float f) {
-        f -= 0.5f; // center the values about 0.
-        f *= 0.3f * (float) Math.PI / 2.0f;
-        return (float) Math.sin(f);
-    }
-
-    /**
-     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
-     *
-     * @param x the number of pixels to scroll by on the X axis
-     * @param y the number of pixels to scroll by on the Y axis
-     */
-    void smoothScrollTo(int x, int y) {
-        smoothScrollTo(x, y, 0);
-    }
-
-    /**
-     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
-     *
-     * @param x the number of pixels to scroll by on the X axis
-     * @param y the number of pixels to scroll by on the Y axis
-     * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)
-     */
-    void smoothScrollTo(int x, int y, int velocity) {
-        if (getChildCount() == 0) {
-            // Nothing to do.
-            setScrollingCacheEnabled(false);
-            return;
-        }
-
-        int sx;
-        boolean wasScrolling = (mScroller != null) && !mScroller.isFinished();
-        if (wasScrolling) {
-            // We're in the middle of a previously initiated scrolling. Check to see
-            // whether that scrolling has actually started (if we always call getStartX
-            // we can get a stale value from the scroller if it hadn't yet had its first
-            // computeScrollOffset call) to decide what is the current scrolling position.
-            sx = mIsScrollStarted ? mScroller.getCurrX() : mScroller.getStartX();
-            // And abort the current scrolling.
-            mScroller.abortAnimation();
-            setScrollingCacheEnabled(false);
-        } else {
-            sx = getScrollX();
-        }
-        int sy = getScrollY();
-        int dx = x - sx;
-        int dy = y - sy;
-        if (dx == 0 && dy == 0) {
-            completeScroll(false);
-            populate();
-            setScrollState(SCROLL_STATE_IDLE);
-            return;
-        }
-
-        setScrollingCacheEnabled(true);
-        setScrollState(SCROLL_STATE_SETTLING);
-
-        final int width = getClientWidth();
-        final int halfWidth = width / 2;
-        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
-        final float distance = halfWidth + halfWidth
-                * distanceInfluenceForSnapDuration(distanceRatio);
-
-        int duration;
-        velocity = Math.abs(velocity);
-        if (velocity > 0) {
-            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
-        } else {
-            final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
-            final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMargin);
-            duration = (int) ((pageDelta + 1) * 100);
-        }
-        duration = Math.min(duration, MAX_SETTLE_DURATION);
-
-        // Reset the "scroll started" flag. It will be flipped to true in all places
-        // where we call computeScrollOffset().
-        mIsScrollStarted = false;
-        mScroller.startScroll(sx, sy, dx, dy, duration);
-        ViewCompat.postInvalidateOnAnimation(this);
-    }
-
-    ItemInfo addNewItem(int position, int index) {
-        ItemInfo ii = new ItemInfo();
-        ii.position = position;
-        ii.object = mAdapter.instantiateItem(this, position);
-        ii.widthFactor = mAdapter.getPageWidth(position);
-        if (index < 0 || index >= mItems.size()) {
-            mItems.add(ii);
-        } else {
-            mItems.add(index, ii);
-        }
-        return ii;
-    }
-
-    void dataSetChanged() {
-        // This method only gets called if our observer is attached, so mAdapter is non-null.
-
-        final int adapterCount = mAdapter.getCount();
-        mExpectedAdapterCount = adapterCount;
-        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
-                && mItems.size() < adapterCount;
-        int newCurrItem = mCurItem;
-
-        boolean isUpdating = false;
-        for (int i = 0; i < mItems.size(); i++) {
-            final ItemInfo ii = mItems.get(i);
-            final int newPos = mAdapter.getItemPosition(ii.object);
-
-            if (newPos == PagerAdapter.POSITION_UNCHANGED) {
-                continue;
-            }
-
-            if (newPos == PagerAdapter.POSITION_NONE) {
-                mItems.remove(i);
-                i--;
-
-                if (!isUpdating) {
-                    mAdapter.startUpdate(this);
-                    isUpdating = true;
-                }
-
-                mAdapter.destroyItem(this, ii.position, ii.object);
-                needPopulate = true;
-
-                if (mCurItem == ii.position) {
-                    // Keep the current item in the valid range
-                    newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
-                    needPopulate = true;
-                }
-                continue;
-            }
-
-            if (ii.position != newPos) {
-                if (ii.position == mCurItem) {
-                    // Our current item changed position. Follow it.
-                    newCurrItem = newPos;
-                }
-
-                ii.position = newPos;
-                needPopulate = true;
-            }
-        }
-
-        if (isUpdating) {
-            mAdapter.finishUpdate(this);
-        }
-
-        Collections.sort(mItems, COMPARATOR);
-
-        if (needPopulate) {
-            // Reset our known page widths; populate will recompute them.
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (!lp.isDecor) {
-                    lp.widthFactor = 0.f;
-                }
-            }
-
-            setCurrentItemInternal(newCurrItem, false, true);
-            requestLayout();
-        }
-    }
-
-    void populate() {
-        populate(mCurItem);
-    }
-
-    void populate(int newCurrentItem) {
-        ItemInfo oldCurInfo = null;
-        if (mCurItem != newCurrentItem) {
-            oldCurInfo = infoForPosition(mCurItem);
-            mCurItem = newCurrentItem;
-        }
-
-        if (mAdapter == null) {
-            sortChildDrawingOrder();
-            return;
-        }
-
-        // Bail now if we are waiting to populate.  This is to hold off
-        // on creating views from the time the user releases their finger to
-        // fling to a new position until we have finished the scroll to
-        // that position, avoiding glitches from happening at that point.
-        if (mPopulatePending) {
-            if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");
-            sortChildDrawingOrder();
-            return;
-        }
-
-        // Also, don't populate until we are attached to a window.  This is to
-        // avoid trying to populate before we have restored our view hierarchy
-        // state and conflicting with what is restored.
-        if (getWindowToken() == null) {
-            return;
-        }
-
-        mAdapter.startUpdate(this);
-
-        final int pageLimit = mOffscreenPageLimit;
-        final int startPos = Math.max(0, mCurItem - pageLimit);
-        final int N = mAdapter.getCount();
-        final int endPos = Math.min(N - 1, mCurItem + pageLimit);
-
-        if (N != mExpectedAdapterCount) {
-            String resName;
-            try {
-                resName = getResources().getResourceName(getId());
-            } catch (Resources.NotFoundException e) {
-                resName = Integer.toHexString(getId());
-            }
-            throw new IllegalStateException("The application's PagerAdapter changed the adapter's"
-                    + " contents without calling PagerAdapter#notifyDataSetChanged!"
-                    + " Expected adapter item count: " + mExpectedAdapterCount + ", found: " + N
-                    + " Pager id: " + resName
-                    + " Pager class: " + getClass()
-                    + " Problematic adapter: " + mAdapter.getClass());
-        }
-
-        // Locate the currently focused item or add it if needed.
-        int curIndex = -1;
-        ItemInfo curItem = null;
-        for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
-            final ItemInfo ii = mItems.get(curIndex);
-            if (ii.position >= mCurItem) {
-                if (ii.position == mCurItem) curItem = ii;
-                break;
-            }
-        }
-
-        if (curItem == null && N > 0) {
-            curItem = addNewItem(mCurItem, curIndex);
-        }
-
-        // Fill 3x the available width or up to the number of offscreen
-        // pages requested to either side, whichever is larger.
-        // If we have no current item we have no work to do.
-        if (curItem != null) {
-            float extraWidthLeft = 0.f;
-            int itemIndex = curIndex - 1;
-            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
-            final int clientWidth = getClientWidth();
-            final float leftWidthNeeded = clientWidth <= 0 ? 0 :
-                    2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
-            for (int pos = mCurItem - 1; pos >= 0; pos--) {
-                if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
-                    if (ii == null) {
-                        break;
-                    }
-                    if (pos == ii.position && !ii.scrolling) {
-                        mItems.remove(itemIndex);
-                        mAdapter.destroyItem(this, pos, ii.object);
-                        if (DEBUG) {
-                            Log.i(TAG, "populate() - destroyItem() with pos: " + pos
-                                    + " view: " + ((View) ii.object));
-                        }
-                        itemIndex--;
-                        curIndex--;
-                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
-                    }
-                } else if (ii != null && pos == ii.position) {
-                    extraWidthLeft += ii.widthFactor;
-                    itemIndex--;
-                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
-                } else {
-                    ii = addNewItem(pos, itemIndex + 1);
-                    extraWidthLeft += ii.widthFactor;
-                    curIndex++;
-                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
-                }
-            }
-
-            float extraWidthRight = curItem.widthFactor;
-            itemIndex = curIndex + 1;
-            if (extraWidthRight < 2.f) {
-                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
-                final float rightWidthNeeded = clientWidth <= 0 ? 0 :
-                        (float) getPaddingRight() / (float) clientWidth + 2.f;
-                for (int pos = mCurItem + 1; pos < N; pos++) {
-                    if (extraWidthRight >= rightWidthNeeded && pos > endPos) {
-                        if (ii == null) {
-                            break;
-                        }
-                        if (pos == ii.position && !ii.scrolling) {
-                            mItems.remove(itemIndex);
-                            mAdapter.destroyItem(this, pos, ii.object);
-                            if (DEBUG) {
-                                Log.i(TAG, "populate() - destroyItem() with pos: " + pos
-                                        + " view: " + ((View) ii.object));
-                            }
-                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
-                        }
-                    } else if (ii != null && pos == ii.position) {
-                        extraWidthRight += ii.widthFactor;
-                        itemIndex++;
-                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
-                    } else {
-                        ii = addNewItem(pos, itemIndex);
-                        itemIndex++;
-                        extraWidthRight += ii.widthFactor;
-                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
-                    }
-                }
-            }
-
-            calculatePageOffsets(curItem, curIndex, oldCurInfo);
-
-            mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
-        }
-
-        if (DEBUG) {
-            Log.i(TAG, "Current page list:");
-            for (int i = 0; i < mItems.size(); i++) {
-                Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
-            }
-        }
-
-        mAdapter.finishUpdate(this);
-
-        // Check width measurement of current pages and drawing sort order.
-        // Update LayoutParams as needed.
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.childIndex = i;
-            if (!lp.isDecor && lp.widthFactor == 0.f) {
-                // 0 means requery the adapter for this, it doesn't have a valid width.
-                final ItemInfo ii = infoForChild(child);
-                if (ii != null) {
-                    lp.widthFactor = ii.widthFactor;
-                    lp.position = ii.position;
-                }
-            }
-        }
-        sortChildDrawingOrder();
-
-        if (hasFocus()) {
-            View currentFocused = findFocus();
-            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
-            if (ii == null || ii.position != mCurItem) {
-                for (int i = 0; i < getChildCount(); i++) {
-                    View child = getChildAt(i);
-                    ii = infoForChild(child);
-                    if (ii != null && ii.position == mCurItem) {
-                        if (child.requestFocus(View.FOCUS_FORWARD)) {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private void sortChildDrawingOrder() {
-        if (mDrawingOrder != DRAW_ORDER_DEFAULT) {
-            if (mDrawingOrderedChildren == null) {
-                mDrawingOrderedChildren = new ArrayList<View>();
-            } else {
-                mDrawingOrderedChildren.clear();
-            }
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                mDrawingOrderedChildren.add(child);
-            }
-            Collections.sort(mDrawingOrderedChildren, sPositionComparator);
-        }
-    }
-
-    private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
-        final int N = mAdapter.getCount();
-        final int width = getClientWidth();
-        final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
-        // Fix up offsets for later layout.
-        if (oldCurInfo != null) {
-            final int oldCurPosition = oldCurInfo.position;
-            // Base offsets off of oldCurInfo.
-            if (oldCurPosition < curItem.position) {
-                int itemIndex = 0;
-                ItemInfo ii = null;
-                float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
-                for (int pos = oldCurPosition + 1;
-                        pos <= curItem.position && itemIndex < mItems.size(); pos++) {
-                    ii = mItems.get(itemIndex);
-                    while (pos > ii.position && itemIndex < mItems.size() - 1) {
-                        itemIndex++;
-                        ii = mItems.get(itemIndex);
-                    }
-                    while (pos < ii.position) {
-                        // We don't have an item populated for this,
-                        // ask the adapter for an offset.
-                        offset += mAdapter.getPageWidth(pos) + marginOffset;
-                        pos++;
-                    }
-                    ii.offset = offset;
-                    offset += ii.widthFactor + marginOffset;
-                }
-            } else if (oldCurPosition > curItem.position) {
-                int itemIndex = mItems.size() - 1;
-                ItemInfo ii = null;
-                float offset = oldCurInfo.offset;
-                for (int pos = oldCurPosition - 1;
-                        pos >= curItem.position && itemIndex >= 0; pos--) {
-                    ii = mItems.get(itemIndex);
-                    while (pos < ii.position && itemIndex > 0) {
-                        itemIndex--;
-                        ii = mItems.get(itemIndex);
-                    }
-                    while (pos > ii.position) {
-                        // We don't have an item populated for this,
-                        // ask the adapter for an offset.
-                        offset -= mAdapter.getPageWidth(pos) + marginOffset;
-                        pos--;
-                    }
-                    offset -= ii.widthFactor + marginOffset;
-                    ii.offset = offset;
-                }
-            }
-        }
-
-        // Base all offsets off of curItem.
-        final int itemCount = mItems.size();
-        float offset = curItem.offset;
-        int pos = curItem.position - 1;
-        mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
-        mLastOffset = curItem.position == N - 1
-                ? curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
-        // Previous pages
-        for (int i = curIndex - 1; i >= 0; i--, pos--) {
-            final ItemInfo ii = mItems.get(i);
-            while (pos > ii.position) {
-                offset -= mAdapter.getPageWidth(pos--) + marginOffset;
-            }
-            offset -= ii.widthFactor + marginOffset;
-            ii.offset = offset;
-            if (ii.position == 0) mFirstOffset = offset;
-        }
-        offset = curItem.offset + curItem.widthFactor + marginOffset;
-        pos = curItem.position + 1;
-        // Next pages
-        for (int i = curIndex + 1; i < itemCount; i++, pos++) {
-            final ItemInfo ii = mItems.get(i);
-            while (pos < ii.position) {
-                offset += mAdapter.getPageWidth(pos++) + marginOffset;
-            }
-            if (ii.position == N - 1) {
-                mLastOffset = offset + ii.widthFactor - 1;
-            }
-            ii.offset = offset;
-            offset += ii.widthFactor + marginOffset;
-        }
-
-        mNeedCalculatePageOffsets = false;
-    }
-
-    /**
-     * This is the persistent state that is saved by ViewPager.  Only needed
-     * if you are creating a sublass of ViewPager that must save its own
-     * state, in which case it should implement a subclass of this which
-     * contains that state.
-     */
-    public static class SavedState extends AbsSavedState {
-        int position;
-        Parcelable adapterState;
-        ClassLoader loader;
-
-        public SavedState(@NonNull Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(position);
-            out.writeParcelable(adapterState, flags);
-        }
-
-        @Override
-        public String toString() {
-            return "FragmentPager.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " position=" + position + "}";
-        }
-
-        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);
-            if (loader == null) {
-                loader = getClass().getClassLoader();
-            }
-            position = in.readInt();
-            adapterState = in.readParcelable(loader);
-            this.loader = loader;
-        }
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        SavedState ss = new SavedState(superState);
-        ss.position = mCurItem;
-        if (mAdapter != null) {
-            ss.adapterState = mAdapter.saveState();
-        }
-        return ss;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-
-        if (mAdapter != null) {
-            mAdapter.restoreState(ss.adapterState, ss.loader);
-            setCurrentItemInternal(ss.position, false, true);
-        } else {
-            mRestoredCurItem = ss.position;
-            mRestoredAdapterState = ss.adapterState;
-            mRestoredClassLoader = ss.loader;
-        }
-    }
-
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        if (!checkLayoutParams(params)) {
-            params = generateLayoutParams(params);
-        }
-        final LayoutParams lp = (LayoutParams) params;
-        // Any views added via inflation should be classed as part of the decor
-        lp.isDecor |= isDecorView(child);
-        if (mInLayout) {
-            if (lp != null && lp.isDecor) {
-                throw new IllegalStateException("Cannot add pager decor view during layout");
-            }
-            lp.needsMeasure = true;
-            addViewInLayout(child, index, params);
-        } else {
-            super.addView(child, index, params);
-        }
-
-        if (USE_CACHE) {
-            if (child.getVisibility() != GONE) {
-                child.setDrawingCacheEnabled(mScrollingCacheEnabled);
-            } else {
-                child.setDrawingCacheEnabled(false);
-            }
-        }
-    }
-
-    private static boolean isDecorView(@NonNull View view) {
-        Class<?> clazz = view.getClass();
-        return clazz.getAnnotation(DecorView.class) != null;
-    }
-
-    @Override
-    public void removeView(View view) {
-        if (mInLayout) {
-            removeViewInLayout(view);
-        } else {
-            super.removeView(view);
-        }
-    }
-
-    ItemInfo infoForChild(View child) {
-        for (int i = 0; i < mItems.size(); i++) {
-            ItemInfo ii = mItems.get(i);
-            if (mAdapter.isViewFromObject(child, ii.object)) {
-                return ii;
-            }
-        }
-        return null;
-    }
-
-    ItemInfo infoForAnyChild(View child) {
-        ViewParent parent;
-        while ((parent = child.getParent()) != this) {
-            if (parent == null || !(parent instanceof View)) {
-                return null;
-            }
-            child = (View) parent;
-        }
-        return infoForChild(child);
-    }
-
-    ItemInfo infoForPosition(int position) {
-        for (int i = 0; i < mItems.size(); i++) {
-            ItemInfo ii = mItems.get(i);
-            if (ii.position == position) {
-                return ii;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mFirstLayout = true;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // For simple implementation, our internal size is always 0.
-        // We depend on the container to specify the layout size of
-        // our view.  We can't really know what it is since we will be
-        // adding and removing different arbitrary views and do not
-        // want the layout to change as this happens.
-        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
-                getDefaultSize(0, heightMeasureSpec));
-
-        final int measuredWidth = getMeasuredWidth();
-        final int maxGutterSize = measuredWidth / 10;
-        mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);
-
-        // Children are just made to fill our space.
-        int childWidthSize = measuredWidth - getPaddingLeft() - getPaddingRight();
-        int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
-
-        /*
-         * Make sure all children have been properly measured. Decor views first.
-         * Right now we cheat and make this less complicated by assuming decor
-         * views won't intersect. We will pin to edges based on gravity.
-         */
-        int size = getChildCount();
-        for (int i = 0; i < size; ++i) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp != null && lp.isDecor) {
-                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
-                    int widthMode = MeasureSpec.AT_MOST;
-                    int heightMode = MeasureSpec.AT_MOST;
-                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
-                    boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;
-
-                    if (consumeVertical) {
-                        widthMode = MeasureSpec.EXACTLY;
-                    } else if (consumeHorizontal) {
-                        heightMode = MeasureSpec.EXACTLY;
-                    }
-
-                    int widthSize = childWidthSize;
-                    int heightSize = childHeightSize;
-                    if (lp.width != LayoutParams.WRAP_CONTENT) {
-                        widthMode = MeasureSpec.EXACTLY;
-                        if (lp.width != LayoutParams.MATCH_PARENT) {
-                            widthSize = lp.width;
-                        }
-                    }
-                    if (lp.height != LayoutParams.WRAP_CONTENT) {
-                        heightMode = MeasureSpec.EXACTLY;
-                        if (lp.height != LayoutParams.MATCH_PARENT) {
-                            heightSize = lp.height;
-                        }
-                    }
-                    final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
-                    final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
-                    child.measure(widthSpec, heightSpec);
-
-                    if (consumeVertical) {
-                        childHeightSize -= child.getMeasuredHeight();
-                    } else if (consumeHorizontal) {
-                        childWidthSize -= child.getMeasuredWidth();
-                    }
-                }
-            }
-        }
-
-        mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
-        mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);
-
-        // Make sure we have created all fragments that we need to have shown.
-        mInLayout = true;
-        populate();
-        mInLayout = false;
-
-        // Page views next.
-        size = getChildCount();
-        for (int i = 0; i < size; ++i) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                if (DEBUG) {
-                    Log.v(TAG, "Measuring #" + i + " " + child + ": " + mChildWidthMeasureSpec);
-                }
-
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp == null || !lp.isDecor) {
-                    final int widthSpec = MeasureSpec.makeMeasureSpec(
-                            (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);
-                    child.measure(widthSpec, mChildHeightMeasureSpec);
-                }
-            }
-        }
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-
-        // Make sure scroll position is set correctly.
-        if (w != oldw) {
-            recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);
-        }
-    }
-
-    private void recomputeScrollPosition(int width, int oldWidth, int margin, int oldMargin) {
-        if (oldWidth > 0 && !mItems.isEmpty()) {
-            if (!mScroller.isFinished()) {
-                mScroller.setFinalX(getCurrentItem() * getClientWidth());
-            } else {
-                final int widthWithMargin = width - getPaddingLeft() - getPaddingRight() + margin;
-                final int oldWidthWithMargin = oldWidth - getPaddingLeft() - getPaddingRight()
-                        + oldMargin;
-                final int xpos = getScrollX();
-                final float pageOffset = (float) xpos / oldWidthWithMargin;
-                final int newOffsetPixels = (int) (pageOffset * widthWithMargin);
-
-                scrollTo(newOffsetPixels, getScrollY());
-            }
-        } else {
-            final ItemInfo ii = infoForPosition(mCurItem);
-            final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;
-            final int scrollPos =
-                    (int) (scrollOffset * (width - getPaddingLeft() - getPaddingRight()));
-            if (scrollPos != getScrollX()) {
-                completeScroll(false);
-                scrollTo(scrollPos, getScrollY());
-            }
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        final int count = getChildCount();
-        int width = r - l;
-        int height = b - t;
-        int paddingLeft = getPaddingLeft();
-        int paddingTop = getPaddingTop();
-        int paddingRight = getPaddingRight();
-        int paddingBottom = getPaddingBottom();
-        final int scrollX = getScrollX();
-
-        int decorCount = 0;
-
-        // First pass - decor views. We need to do this in two passes so that
-        // we have the proper offsets for non-decor views later.
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                int childLeft = 0;
-                int childTop = 0;
-                if (lp.isDecor) {
-                    final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-                    final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
-                    switch (hgrav) {
-                        default:
-                            childLeft = paddingLeft;
-                            break;
-                        case Gravity.LEFT:
-                            childLeft = paddingLeft;
-                            paddingLeft += child.getMeasuredWidth();
-                            break;
-                        case Gravity.CENTER_HORIZONTAL:
-                            childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
-                                    paddingLeft);
-                            break;
-                        case Gravity.RIGHT:
-                            childLeft = width - paddingRight - child.getMeasuredWidth();
-                            paddingRight += child.getMeasuredWidth();
-                            break;
-                    }
-                    switch (vgrav) {
-                        default:
-                            childTop = paddingTop;
-                            break;
-                        case Gravity.TOP:
-                            childTop = paddingTop;
-                            paddingTop += child.getMeasuredHeight();
-                            break;
-                        case Gravity.CENTER_VERTICAL:
-                            childTop = Math.max((height - child.getMeasuredHeight()) / 2,
-                                    paddingTop);
-                            break;
-                        case Gravity.BOTTOM:
-                            childTop = height - paddingBottom - child.getMeasuredHeight();
-                            paddingBottom += child.getMeasuredHeight();
-                            break;
-                    }
-                    childLeft += scrollX;
-                    child.layout(childLeft, childTop,
-                            childLeft + child.getMeasuredWidth(),
-                            childTop + child.getMeasuredHeight());
-                    decorCount++;
-                }
-            }
-        }
-
-        final int childWidth = width - paddingLeft - paddingRight;
-        // Page views. Do this once we have the right padding offsets from above.
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                ItemInfo ii;
-                if (!lp.isDecor && (ii = infoForChild(child)) != null) {
-                    int loff = (int) (childWidth * ii.offset);
-                    int childLeft = paddingLeft + loff;
-                    int childTop = paddingTop;
-                    if (lp.needsMeasure) {
-                        // This was added during layout and needs measurement.
-                        // Do it now that we know what we're working with.
-                        lp.needsMeasure = false;
-                        final int widthSpec = MeasureSpec.makeMeasureSpec(
-                                (int) (childWidth * lp.widthFactor),
-                                MeasureSpec.EXACTLY);
-                        final int heightSpec = MeasureSpec.makeMeasureSpec(
-                                (int) (height - paddingTop - paddingBottom),
-                                MeasureSpec.EXACTLY);
-                        child.measure(widthSpec, heightSpec);
-                    }
-                    if (DEBUG) {
-                        Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
-                                + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
-                                + "x" + child.getMeasuredHeight());
-                    }
-                    child.layout(childLeft, childTop,
-                            childLeft + child.getMeasuredWidth(),
-                            childTop + child.getMeasuredHeight());
-                }
-            }
-        }
-        mTopPageBounds = paddingTop;
-        mBottomPageBounds = height - paddingBottom;
-        mDecorChildCount = decorCount;
-
-        if (mFirstLayout) {
-            scrollToItem(mCurItem, false, 0, false);
-        }
-        mFirstLayout = false;
-    }
-
-    @Override
-    public void computeScroll() {
-        mIsScrollStarted = true;
-        if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
-            int oldX = getScrollX();
-            int oldY = getScrollY();
-            int x = mScroller.getCurrX();
-            int y = mScroller.getCurrY();
-
-            if (oldX != x || oldY != y) {
-                scrollTo(x, y);
-                if (!pageScrolled(x)) {
-                    mScroller.abortAnimation();
-                    scrollTo(0, y);
-                }
-            }
-
-            // Keep on drawing until the animation has finished.
-            ViewCompat.postInvalidateOnAnimation(this);
-            return;
-        }
-
-        // Done with scroll, clean up state.
-        completeScroll(true);
-    }
-
-    private boolean pageScrolled(int xpos) {
-        if (mItems.size() == 0) {
-            if (mFirstLayout) {
-                // If we haven't been laid out yet, we probably just haven't been populated yet.
-                // Let's skip this call since it doesn't make sense in this state
-                return false;
-            }
-            mCalledSuper = false;
-            onPageScrolled(0, 0, 0);
-            if (!mCalledSuper) {
-                throw new IllegalStateException(
-                        "onPageScrolled did not call superclass implementation");
-            }
-            return false;
-        }
-        final ItemInfo ii = infoForCurrentScrollPosition();
-        final int width = getClientWidth();
-        final int widthWithMargin = width + mPageMargin;
-        final float marginOffset = (float) mPageMargin / width;
-        final int currentPage = ii.position;
-        final float pageOffset = (((float) xpos / width) - ii.offset)
-                / (ii.widthFactor + marginOffset);
-        final int offsetPixels = (int) (pageOffset * widthWithMargin);
-
-        mCalledSuper = false;
-        onPageScrolled(currentPage, pageOffset, offsetPixels);
-        if (!mCalledSuper) {
-            throw new IllegalStateException(
-                    "onPageScrolled did not call superclass implementation");
-        }
-        return true;
-    }
-
-    /**
-     * This method will be invoked when the current page is scrolled, either as part
-     * of a programmatically initiated smooth scroll or a user initiated touch scroll.
-     * If you override this method you must call through to the superclass implementation
-     * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
-     * returns.
-     *
-     * @param position Position index of the first page currently being displayed.
-     *                 Page position+1 will be visible if positionOffset is nonzero.
-     * @param offset Value from [0, 1) indicating the offset from the page at position.
-     * @param offsetPixels Value in pixels indicating the offset from position.
-     */
-    @CallSuper
-    protected void onPageScrolled(int position, float offset, int offsetPixels) {
-        // Offset any decor views if needed - keep them on-screen at all times.
-        if (mDecorChildCount > 0) {
-            final int scrollX = getScrollX();
-            int paddingLeft = getPaddingLeft();
-            int paddingRight = getPaddingRight();
-            final int width = getWidth();
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (!lp.isDecor) continue;
-
-                final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-                int childLeft = 0;
-                switch (hgrav) {
-                    default:
-                        childLeft = paddingLeft;
-                        break;
-                    case Gravity.LEFT:
-                        childLeft = paddingLeft;
-                        paddingLeft += child.getWidth();
-                        break;
-                    case Gravity.CENTER_HORIZONTAL:
-                        childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
-                                paddingLeft);
-                        break;
-                    case Gravity.RIGHT:
-                        childLeft = width - paddingRight - child.getMeasuredWidth();
-                        paddingRight += child.getMeasuredWidth();
-                        break;
-                }
-                childLeft += scrollX;
-
-                final int childOffset = childLeft - child.getLeft();
-                if (childOffset != 0) {
-                    child.offsetLeftAndRight(childOffset);
-                }
-            }
-        }
-
-        dispatchOnPageScrolled(position, offset, offsetPixels);
-
-        if (mPageTransformer != null) {
-            final int scrollX = getScrollX();
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-                if (lp.isDecor) continue;
-                final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
-                mPageTransformer.transformPage(child, transformPos);
-            }
-        }
-
-        mCalledSuper = true;
-    }
-
-    private void dispatchOnPageScrolled(int position, float offset, int offsetPixels) {
-        if (mOnPageChangeListener != null) {
-            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
-        }
-        if (mOnPageChangeListeners != null) {
-            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
-                OnPageChangeListener listener = mOnPageChangeListeners.get(i);
-                if (listener != null) {
-                    listener.onPageScrolled(position, offset, offsetPixels);
-                }
-            }
-        }
-        if (mInternalPageChangeListener != null) {
-            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
-        }
-    }
-
-    private void dispatchOnPageSelected(int position) {
-        if (mOnPageChangeListener != null) {
-            mOnPageChangeListener.onPageSelected(position);
-        }
-        if (mOnPageChangeListeners != null) {
-            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
-                OnPageChangeListener listener = mOnPageChangeListeners.get(i);
-                if (listener != null) {
-                    listener.onPageSelected(position);
-                }
-            }
-        }
-        if (mInternalPageChangeListener != null) {
-            mInternalPageChangeListener.onPageSelected(position);
-        }
-    }
-
-    private void dispatchOnScrollStateChanged(int state) {
-        if (mOnPageChangeListener != null) {
-            mOnPageChangeListener.onPageScrollStateChanged(state);
-        }
-        if (mOnPageChangeListeners != null) {
-            for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
-                OnPageChangeListener listener = mOnPageChangeListeners.get(i);
-                if (listener != null) {
-                    listener.onPageScrollStateChanged(state);
-                }
-            }
-        }
-        if (mInternalPageChangeListener != null) {
-            mInternalPageChangeListener.onPageScrollStateChanged(state);
-        }
-    }
-
-    private void completeScroll(boolean postEvents) {
-        boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
-        if (needPopulate) {
-            // Done with scroll, no longer want to cache view drawing.
-            setScrollingCacheEnabled(false);
-            boolean wasScrolling = !mScroller.isFinished();
-            if (wasScrolling) {
-                mScroller.abortAnimation();
-                int oldX = getScrollX();
-                int oldY = getScrollY();
-                int x = mScroller.getCurrX();
-                int y = mScroller.getCurrY();
-                if (oldX != x || oldY != y) {
-                    scrollTo(x, y);
-                    if (x != oldX) {
-                        pageScrolled(x);
-                    }
-                }
-            }
-        }
-        mPopulatePending = false;
-        for (int i = 0; i < mItems.size(); i++) {
-            ItemInfo ii = mItems.get(i);
-            if (ii.scrolling) {
-                needPopulate = true;
-                ii.scrolling = false;
-            }
-        }
-        if (needPopulate) {
-            if (postEvents) {
-                ViewCompat.postOnAnimation(this, mEndScrollRunnable);
-            } else {
-                mEndScrollRunnable.run();
-            }
-        }
-    }
-
-    private boolean isGutterDrag(float x, float dx) {
-        return (x < mGutterSize && dx > 0) || (x > getWidth() - mGutterSize && dx < 0);
-    }
-
-    private void enableLayers(boolean enable) {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final int layerType = enable
-                    ? mPageTransformerLayerType : View.LAYER_TYPE_NONE;
-            getChildAt(i).setLayerType(layerType, null);
-        }
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        /*
-         * This method JUST determines whether we want to intercept the motion.
-         * If we return true, onMotionEvent will be called and we do the actual
-         * scrolling there.
-         */
-
-        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) {
-            // Release the drag.
-            if (DEBUG) Log.v(TAG, "Intercept done!");
-            resetTouch();
-            return false;
-        }
-
-        // Nothing more to do here if we have decided whether or not we
-        // are dragging.
-        if (action != MotionEvent.ACTION_DOWN) {
-            if (mIsBeingDragged) {
-                if (DEBUG) Log.v(TAG, "Intercept returning true!");
-                return true;
-            }
-            if (mIsUnableToDrag) {
-                if (DEBUG) Log.v(TAG, "Intercept returning false!");
-                return false;
-            }
-        }
-
-        switch (action) {
-            case MotionEvent.ACTION_MOVE: {
-                /*
-                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
-                 * whether the user has moved far enough from his original down touch.
-                 */
-
-                /*
-                * Locally do absolute value. mLastMotionY is set to the y value
-                * of the down event.
-                */
-                final int activePointerId = mActivePointerId;
-                if (activePointerId == INVALID_POINTER) {
-                    // If we don't have a valid id, the touch down wasn't on content.
-                    break;
-                }
-
-                final int pointerIndex = ev.findPointerIndex(activePointerId);
-                final float x = ev.getX(pointerIndex);
-                final float dx = x - mLastMotionX;
-                final float xDiff = Math.abs(dx);
-                final float y = ev.getY(pointerIndex);
-                final float yDiff = Math.abs(y - mInitialMotionY);
-                if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
-
-                if (dx != 0 && !isGutterDrag(mLastMotionX, dx)
-                        && canScroll(this, false, (int) dx, (int) x, (int) y)) {
-                    // Nested view has scrollable area under this point. Let it be handled there.
-                    mLastMotionX = x;
-                    mLastMotionY = y;
-                    mIsUnableToDrag = true;
-                    return false;
-                }
-                if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
-                    if (DEBUG) Log.v(TAG, "Starting drag!");
-                    mIsBeingDragged = true;
-                    requestParentDisallowInterceptTouchEvent(true);
-                    setScrollState(SCROLL_STATE_DRAGGING);
-                    mLastMotionX = dx > 0
-                            ? mInitialMotionX + mTouchSlop : mInitialMotionX - mTouchSlop;
-                    mLastMotionY = y;
-                    setScrollingCacheEnabled(true);
-                } else if (yDiff > mTouchSlop) {
-                    // The finger has moved enough in the vertical
-                    // direction to be counted as a drag...  abort
-                    // any attempt to drag horizontally, to work correctly
-                    // with children that have scrolling containers.
-                    if (DEBUG) Log.v(TAG, "Starting unable to drag!");
-                    mIsUnableToDrag = true;
-                }
-                if (mIsBeingDragged) {
-                    // Scroll to follow the motion event
-                    if (performDrag(x)) {
-                        ViewCompat.postInvalidateOnAnimation(this);
-                    }
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_DOWN: {
-                /*
-                 * Remember location of down touch.
-                 * ACTION_DOWN always refers to pointer index 0.
-                 */
-                mLastMotionX = mInitialMotionX = ev.getX();
-                mLastMotionY = mInitialMotionY = ev.getY();
-                mActivePointerId = ev.getPointerId(0);
-                mIsUnableToDrag = false;
-
-                mIsScrollStarted = true;
-                mScroller.computeScrollOffset();
-                if (mScrollState == SCROLL_STATE_SETTLING
-                        && Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
-                    // Let the user 'catch' the pager as it animates.
-                    mScroller.abortAnimation();
-                    mPopulatePending = false;
-                    populate();
-                    mIsBeingDragged = true;
-                    requestParentDisallowInterceptTouchEvent(true);
-                    setScrollState(SCROLL_STATE_DRAGGING);
-                } else {
-                    completeScroll(false);
-                    mIsBeingDragged = false;
-                }
-
-                if (DEBUG) {
-                    Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
-                            + " mIsBeingDragged=" + mIsBeingDragged
-                            + "mIsUnableToDrag=" + mIsUnableToDrag);
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                break;
-        }
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        /*
-         * The only time we want to intercept motion events is if we are in the
-         * drag mode.
-         */
-        return mIsBeingDragged;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mFakeDragging) {
-            // A fake drag is in progress already, ignore this real one
-            // but still eat the touch events.
-            // (It is likely that the user is multi-touching the screen.)
-            return true;
-        }
-
-        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
-            // Don't handle edge touches immediately -- they may actually belong to one of our
-            // descendants.
-            return false;
-        }
-
-        if (mAdapter == null || mAdapter.getCount() == 0) {
-            // Nothing to present or scroll; nothing to touch.
-            return false;
-        }
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        final int action = ev.getAction();
-        boolean needsInvalidate = false;
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                mScroller.abortAnimation();
-                mPopulatePending = false;
-                populate();
-
-                // Remember where the motion event started
-                mLastMotionX = mInitialMotionX = ev.getX();
-                mLastMotionY = mInitialMotionY = ev.getY();
-                mActivePointerId = ev.getPointerId(0);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE:
-                if (!mIsBeingDragged) {
-                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                    if (pointerIndex == -1) {
-                        // A child has consumed some touch events and put us into an inconsistent
-                        // state.
-                        needsInvalidate = resetTouch();
-                        break;
-                    }
-                    final float x = ev.getX(pointerIndex);
-                    final float xDiff = Math.abs(x - mLastMotionX);
-                    final float y = ev.getY(pointerIndex);
-                    final float yDiff = Math.abs(y - mLastMotionY);
-                    if (DEBUG) {
-                        Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
-                    }
-                    if (xDiff > mTouchSlop && xDiff > yDiff) {
-                        if (DEBUG) Log.v(TAG, "Starting drag!");
-                        mIsBeingDragged = true;
-                        requestParentDisallowInterceptTouchEvent(true);
-                        mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
-                                mInitialMotionX - mTouchSlop;
-                        mLastMotionY = y;
-                        setScrollState(SCROLL_STATE_DRAGGING);
-                        setScrollingCacheEnabled(true);
-
-                        // Disallow Parent Intercept, just in case
-                        ViewParent parent = getParent();
-                        if (parent != null) {
-                            parent.requestDisallowInterceptTouchEvent(true);
-                        }
-                    }
-                }
-                // Not else! Note that mIsBeingDragged can be set above.
-                if (mIsBeingDragged) {
-                    // Scroll to follow the motion event
-                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                    final float x = ev.getX(activePointerIndex);
-                    needsInvalidate |= performDrag(x);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mIsBeingDragged) {
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
-                    mPopulatePending = true;
-                    final int width = getClientWidth();
-                    final int scrollX = getScrollX();
-                    final ItemInfo ii = infoForCurrentScrollPosition();
-                    final float marginOffset = (float) mPageMargin / width;
-                    final int currentPage = ii.position;
-                    final float pageOffset = (((float) scrollX / width) - ii.offset)
-                            / (ii.widthFactor + marginOffset);
-                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                    final float x = ev.getX(activePointerIndex);
-                    final int totalDelta = (int) (x - mInitialMotionX);
-                    int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
-                            totalDelta);
-                    setCurrentItemInternal(nextPage, true, true, initialVelocity);
-
-                    needsInvalidate = resetTouch();
-                }
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                if (mIsBeingDragged) {
-                    scrollToItem(mCurItem, true, 0, false);
-                    needsInvalidate = resetTouch();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                final float x = ev.getX(index);
-                mLastMotionX = x;
-                mActivePointerId = ev.getPointerId(index);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
-                break;
-        }
-        if (needsInvalidate) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-        return true;
-    }
-
-    private boolean resetTouch() {
-        boolean needsInvalidate;
-        mActivePointerId = INVALID_POINTER;
-        endDrag();
-        mLeftEdge.onRelease();
-        mRightEdge.onRelease();
-        needsInvalidate = mLeftEdge.isFinished() || mRightEdge.isFinished();
-        return needsInvalidate;
-    }
-
-    private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        final ViewParent parent = getParent();
-        if (parent != null) {
-            parent.requestDisallowInterceptTouchEvent(disallowIntercept);
-        }
-    }
-
-    private boolean performDrag(float x) {
-        boolean needsInvalidate = false;
-
-        final float deltaX = mLastMotionX - x;
-        mLastMotionX = x;
-
-        float oldScrollX = getScrollX();
-        float scrollX = oldScrollX + deltaX;
-        final int width = getClientWidth();
-
-        float leftBound = width * mFirstOffset;
-        float rightBound = width * mLastOffset;
-        boolean leftAbsolute = true;
-        boolean rightAbsolute = true;
-
-        final ItemInfo firstItem = mItems.get(0);
-        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-        if (firstItem.position != 0) {
-            leftAbsolute = false;
-            leftBound = firstItem.offset * width;
-        }
-        if (lastItem.position != mAdapter.getCount() - 1) {
-            rightAbsolute = false;
-            rightBound = lastItem.offset * width;
-        }
-
-        if (scrollX < leftBound) {
-            if (leftAbsolute) {
-                float over = leftBound - scrollX;
-                mLeftEdge.onPull(Math.abs(over) / width);
-                needsInvalidate = true;
-            }
-            scrollX = leftBound;
-        } else if (scrollX > rightBound) {
-            if (rightAbsolute) {
-                float over = scrollX - rightBound;
-                mRightEdge.onPull(Math.abs(over) / width);
-                needsInvalidate = true;
-            }
-            scrollX = rightBound;
-        }
-        // Don't lose the rounded component
-        mLastMotionX += scrollX - (int) scrollX;
-        scrollTo((int) scrollX, getScrollY());
-        pageScrolled((int) scrollX);
-
-        return needsInvalidate;
-    }
-
-    /**
-     * @return Info about the page at the current scroll position.
-     *         This can be synthetic for a missing middle page; the 'object' field can be null.
-     */
-    private ItemInfo infoForCurrentScrollPosition() {
-        final int width = getClientWidth();
-        final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
-        final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
-        int lastPos = -1;
-        float lastOffset = 0.f;
-        float lastWidth = 0.f;
-        boolean first = true;
-
-        ItemInfo lastItem = null;
-        for (int i = 0; i < mItems.size(); i++) {
-            ItemInfo ii = mItems.get(i);
-            float offset;
-            if (!first && ii.position != lastPos + 1) {
-                // Create a synthetic item for a missing page.
-                ii = mTempItem;
-                ii.offset = lastOffset + lastWidth + marginOffset;
-                ii.position = lastPos + 1;
-                ii.widthFactor = mAdapter.getPageWidth(ii.position);
-                i--;
-            }
-            offset = ii.offset;
-
-            final float leftBound = offset;
-            final float rightBound = offset + ii.widthFactor + marginOffset;
-            if (first || scrollOffset >= leftBound) {
-                if (scrollOffset < rightBound || i == mItems.size() - 1) {
-                    return ii;
-                }
-            } else {
-                return lastItem;
-            }
-            first = false;
-            lastPos = ii.position;
-            lastOffset = offset;
-            lastWidth = ii.widthFactor;
-            lastItem = ii;
-        }
-
-        return lastItem;
-    }
-
-    private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
-        int targetPage;
-        if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
-            targetPage = velocity > 0 ? currentPage : currentPage + 1;
-        } else {
-            final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
-            targetPage = currentPage + (int) (pageOffset + truncator);
-        }
-
-        if (mItems.size() > 0) {
-            final ItemInfo firstItem = mItems.get(0);
-            final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-
-            // Only let the user target pages we have items for
-            targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
-        }
-
-        return targetPage;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        boolean needsInvalidate = false;
-
-        final int overScrollMode = getOverScrollMode();
-        if (overScrollMode == View.OVER_SCROLL_ALWAYS
-                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS
-                        && mAdapter != null && mAdapter.getCount() > 1)) {
-            if (!mLeftEdge.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int height = getHeight() - getPaddingTop() - getPaddingBottom();
-                final int width = getWidth();
-
-                canvas.rotate(270);
-                canvas.translate(-height + getPaddingTop(), mFirstOffset * width);
-                mLeftEdge.setSize(height, width);
-                needsInvalidate |= mLeftEdge.draw(canvas);
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mRightEdge.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int width = getWidth();
-                final int height = getHeight() - getPaddingTop() - getPaddingBottom();
-
-                canvas.rotate(90);
-                canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width);
-                mRightEdge.setSize(height, width);
-                needsInvalidate |= mRightEdge.draw(canvas);
-                canvas.restoreToCount(restoreCount);
-            }
-        } else {
-            mLeftEdge.finish();
-            mRightEdge.finish();
-        }
-
-        if (needsInvalidate) {
-            // Keep animating
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        // Draw the margin drawable between pages if needed.
-        if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {
-            final int scrollX = getScrollX();
-            final int width = getWidth();
-
-            final float marginOffset = (float) mPageMargin / width;
-            int itemIndex = 0;
-            ItemInfo ii = mItems.get(0);
-            float offset = ii.offset;
-            final int itemCount = mItems.size();
-            final int firstPos = ii.position;
-            final int lastPos = mItems.get(itemCount - 1).position;
-            for (int pos = firstPos; pos < lastPos; pos++) {
-                while (pos > ii.position && itemIndex < itemCount) {
-                    ii = mItems.get(++itemIndex);
-                }
-
-                float drawAt;
-                if (pos == ii.position) {
-                    drawAt = (ii.offset + ii.widthFactor) * width;
-                    offset = ii.offset + ii.widthFactor + marginOffset;
-                } else {
-                    float widthFactor = mAdapter.getPageWidth(pos);
-                    drawAt = (offset + widthFactor) * width;
-                    offset += widthFactor + marginOffset;
-                }
-
-                if (drawAt + mPageMargin > scrollX) {
-                    mMarginDrawable.setBounds(Math.round(drawAt), mTopPageBounds,
-                            Math.round(drawAt + mPageMargin), mBottomPageBounds);
-                    mMarginDrawable.draw(canvas);
-                }
-
-                if (drawAt > scrollX + width) {
-                    break; // No more visible, no sense in continuing
-                }
-            }
-        }
-    }
-
-    /**
-     * Start a fake drag of the pager.
-     *
-     * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
-     * with the touch scrolling of another view, while still letting the ViewPager
-     * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
-     * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
-     * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
-     *
-     * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
-     * is already in progress, this method will return false.
-     *
-     * @return true if the fake drag began successfully, false if it could not be started.
-     *
-     * @see #fakeDragBy(float)
-     * @see #endFakeDrag()
-     */
-    public boolean beginFakeDrag() {
-        if (mIsBeingDragged) {
-            return false;
-        }
-        mFakeDragging = true;
-        setScrollState(SCROLL_STATE_DRAGGING);
-        mInitialMotionX = mLastMotionX = 0;
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        } else {
-            mVelocityTracker.clear();
-        }
-        final long time = SystemClock.uptimeMillis();
-        final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
-        mVelocityTracker.addMovement(ev);
-        ev.recycle();
-        mFakeDragBeginTime = time;
-        return true;
-    }
-
-    /**
-     * End a fake drag of the pager.
-     *
-     * @see #beginFakeDrag()
-     * @see #fakeDragBy(float)
-     */
-    public void endFakeDrag() {
-        if (!mFakeDragging) {
-            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
-        }
-
-        if (mAdapter != null) {
-            final VelocityTracker velocityTracker = mVelocityTracker;
-            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-            int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
-            mPopulatePending = true;
-            final int width = getClientWidth();
-            final int scrollX = getScrollX();
-            final ItemInfo ii = infoForCurrentScrollPosition();
-            final int currentPage = ii.position;
-            final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
-            final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
-            int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
-                    totalDelta);
-            setCurrentItemInternal(nextPage, true, true, initialVelocity);
-        }
-        endDrag();
-
-        mFakeDragging = false;
-    }
-
-    /**
-     * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
-     *
-     * @param xOffset Offset in pixels to drag by.
-     * @see #beginFakeDrag()
-     * @see #endFakeDrag()
-     */
-    public void fakeDragBy(float xOffset) {
-        if (!mFakeDragging) {
-            throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
-        }
-
-        if (mAdapter == null) {
-            return;
-        }
-
-        mLastMotionX += xOffset;
-
-        float oldScrollX = getScrollX();
-        float scrollX = oldScrollX - xOffset;
-        final int width = getClientWidth();
-
-        float leftBound = width * mFirstOffset;
-        float rightBound = width * mLastOffset;
-
-        final ItemInfo firstItem = mItems.get(0);
-        final ItemInfo lastItem = mItems.get(mItems.size() - 1);
-        if (firstItem.position != 0) {
-            leftBound = firstItem.offset * width;
-        }
-        if (lastItem.position != mAdapter.getCount() - 1) {
-            rightBound = lastItem.offset * width;
-        }
-
-        if (scrollX < leftBound) {
-            scrollX = leftBound;
-        } else if (scrollX > rightBound) {
-            scrollX = rightBound;
-        }
-        // Don't lose the rounded component
-        mLastMotionX += scrollX - (int) scrollX;
-        scrollTo((int) scrollX, getScrollY());
-        pageScrolled((int) scrollX);
-
-        // Synthesize an event for the VelocityTracker.
-        final long time = SystemClock.uptimeMillis();
-        final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
-                mLastMotionX, 0, 0);
-        mVelocityTracker.addMovement(ev);
-        ev.recycle();
-    }
-
-    /**
-     * Returns true if a fake drag is in progress.
-     *
-     * @return true if currently in a fake drag, false otherwise.
-     *
-     * @see #beginFakeDrag()
-     * @see #fakeDragBy(float)
-     * @see #endFakeDrag()
-     */
-    public boolean isFakeDragging() {
-        return mFakeDragging;
-    }
-
-    private void onSecondaryPointerUp(MotionEvent 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
-            // active pointer and adjust accordingly.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionX = ev.getX(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
-    private void endDrag() {
-        mIsBeingDragged = false;
-        mIsUnableToDrag = false;
-
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    private void setScrollingCacheEnabled(boolean enabled) {
-        if (mScrollingCacheEnabled != enabled) {
-            mScrollingCacheEnabled = enabled;
-            if (USE_CACHE) {
-                final int size = getChildCount();
-                for (int i = 0; i < size; ++i) {
-                    final View child = getChildAt(i);
-                    if (child.getVisibility() != GONE) {
-                        child.setDrawingCacheEnabled(enabled);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Check if this ViewPager can be scrolled horizontally in a certain direction.
-     *
-     * @param direction Negative to check scrolling left, positive to check scrolling right.
-     * @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;
-        }
-
-        final int width = getClientWidth();
-        final int scrollX = getScrollX();
-        if (direction < 0) {
-            return (scrollX > (int) (width * mFirstOffset));
-        } else if (direction > 0) {
-            return (scrollX < (int) (width * mLastOffset));
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Tests scrollability within child views of v given a delta of dx.
-     *
-     * @param v View to test for horizontal scrollability
-     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
-     *               or just its children (false).
-     * @param dx Delta scrolled in pixels
-     * @param x X coordinate of the active touch point
-     * @param y Y coordinate of the active touch point
-     * @return true if child views of v can be scrolled by delta of dx.
-     */
-    protected boolean canScroll(View v, boolean checkV, int dx, int x, int 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();
-            // Count backwards - let topmost views consume scroll distance first.
-            for (int i = count - 1; i >= 0; i--) {
-                // TODO: Add versioned support here for transformed views.
-                // This will not work for transformed views in Honeycomb+
-                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(-dx);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        // Let the focused view and/or our descendants get the key first
-        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
-    }
-
-    /**
-     * You can call this function yourself to have the scroll view perform
-     * scrolling from a key event, just as if the event had been dispatched to
-     * it by the view hierarchy.
-     *
-     * @param event The key event to execute.
-     * @return Return true if the event was handled, else false.
-     */
-    public boolean executeKeyEvent(@NonNull KeyEvent event) {
-        boolean handled = false;
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            switch (event.getKeyCode()) {
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                    if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                        handled = pageLeft();
-                    } else {
-                        handled = arrowScroll(FOCUS_LEFT);
-                    }
-                    break;
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
-                        handled = pageRight();
-                    } else {
-                        handled = arrowScroll(FOCUS_RIGHT);
-                    }
-                    break;
-                case KeyEvent.KEYCODE_TAB:
-                    if (event.hasNoModifiers()) {
-                        handled = arrowScroll(FOCUS_FORWARD);
-                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
-                        handled = arrowScroll(FOCUS_BACKWARD);
-                    }
-                    break;
-            }
-        }
-        return handled;
-    }
-
-    /**
-     * Handle scrolling in response to a left or right arrow click.
-     *
-     * @param direction The direction corresponding to the arrow key that was pressed. It should be
-     *                  either {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT}.
-     * @return Whether the scrolling was handled successfully.
-     */
-    public boolean arrowScroll(int direction) {
-        View currentFocused = findFocus();
-        if (currentFocused == this) {
-            currentFocused = null;
-        } else if (currentFocused != null) {
-            boolean isChild = false;
-            for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
-                    parent = parent.getParent()) {
-                if (parent == this) {
-                    isChild = true;
-                    break;
-                }
-            }
-            if (!isChild) {
-                // This would cause the focus search down below to fail in fun ways.
-                final StringBuilder sb = new StringBuilder();
-                sb.append(currentFocused.getClass().getSimpleName());
-                for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;
-                        parent = parent.getParent()) {
-                    sb.append(" => ").append(parent.getClass().getSimpleName());
-                }
-                Log.e(TAG, "arrowScroll tried to find focus based on non-child "
-                        + "current focused view " + sb.toString());
-                currentFocused = null;
-            }
-        }
-
-        boolean handled = false;
-
-        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
-                direction);
-        if (nextFocused != null && nextFocused != currentFocused) {
-            if (direction == View.FOCUS_LEFT) {
-                // If there is nothing to the left, or this is causing us to
-                // jump to the right, then what we really want to do is page left.
-                final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;
-                final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;
-                if (currentFocused != null && nextLeft >= currLeft) {
-                    handled = pageLeft();
-                } else {
-                    handled = nextFocused.requestFocus();
-                }
-            } else if (direction == View.FOCUS_RIGHT) {
-                // If there is nothing to the right, or this is causing us to
-                // jump to the left, then what we really want to do is page right.
-                final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;
-                final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;
-                if (currentFocused != null && nextLeft <= currLeft) {
-                    handled = pageRight();
-                } else {
-                    handled = nextFocused.requestFocus();
-                }
-            }
-        } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {
-            // Trying to move left and nothing there; try to page.
-            handled = pageLeft();
-        } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {
-            // Trying to move right and nothing there; try to page.
-            handled = pageRight();
-        }
-        if (handled) {
-            playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
-        }
-        return handled;
-    }
-
-    private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {
-        if (outRect == null) {
-            outRect = new Rect();
-        }
-        if (child == null) {
-            outRect.set(0, 0, 0, 0);
-            return outRect;
-        }
-        outRect.left = child.getLeft();
-        outRect.right = child.getRight();
-        outRect.top = child.getTop();
-        outRect.bottom = child.getBottom();
-
-        ViewParent parent = child.getParent();
-        while (parent instanceof ViewGroup && parent != this) {
-            final ViewGroup group = (ViewGroup) parent;
-            outRect.left += group.getLeft();
-            outRect.right += group.getRight();
-            outRect.top += group.getTop();
-            outRect.bottom += group.getBottom();
-
-            parent = group.getParent();
-        }
-        return outRect;
-    }
-
-    boolean pageLeft() {
-        if (mCurItem > 0) {
-            setCurrentItem(mCurItem - 1, true);
-            return true;
-        }
-        return false;
-    }
-
-    boolean pageRight() {
-        if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {
-            setCurrentItem(mCurItem + 1, true);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * We only want the current page that is being shown to be focusable.
-     */
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        final int focusableCount = views.size();
-
-        final int descendantFocusability = getDescendantFocusability();
-
-        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
-            for (int i = 0; i < getChildCount(); i++) {
-                final View child = getChildAt(i);
-                if (child.getVisibility() == VISIBLE) {
-                    ItemInfo ii = infoForChild(child);
-                    if (ii != null && ii.position == mCurItem) {
-                        child.addFocusables(views, direction, focusableMode);
-                    }
-                }
-            }
-        }
-
-        // we add ourselves (if focusable) in all cases except for when we are
-        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
-        // to avoid the focus search finding layouts when a more precise search
-        // among the focusable children would be more interesting.
-        if (descendantFocusability != FOCUS_AFTER_DESCENDANTS
-                || (focusableCount == views.size())) { // No focusable descendants
-            // Note that we can't call the superclass here, because it will
-            // add all views in.  So we need to do the same thing View does.
-            if (!isFocusable()) {
-                return;
-            }
-            if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
-                    && isInTouchMode() && !isFocusableInTouchMode()) {
-                return;
-            }
-            if (views != null) {
-                views.add(this);
-            }
-        }
-    }
-
-    /**
-     * We only want the current page that is being shown to be touchable.
-     */
-    @Override
-    public void addTouchables(ArrayList<View> views) {
-        // Note that we don't call super.addTouchables(), which means that
-        // we don't call View.addTouchables().  This is okay because a ViewPager
-        // is itself not touchable.
-        for (int i = 0; i < getChildCount(); i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == VISIBLE) {
-                ItemInfo ii = infoForChild(child);
-                if (ii != null && ii.position == mCurItem) {
-                    child.addTouchables(views);
-                }
-            }
-        }
-    }
-
-    /**
-     * We only want the current page that is being shown to be focusable.
-     */
-    @Override
-    protected boolean onRequestFocusInDescendants(int direction,
-            Rect previouslyFocusedRect) {
-        int index;
-        int increment;
-        int end;
-        int count = getChildCount();
-        if ((direction & FOCUS_FORWARD) != 0) {
-            index = 0;
-            increment = 1;
-            end = count;
-        } else {
-            index = count - 1;
-            increment = -1;
-            end = -1;
-        }
-        for (int i = index; i != end; i += increment) {
-            View child = getChildAt(i);
-            if (child.getVisibility() == VISIBLE) {
-                ItemInfo ii = infoForChild(child);
-                if (ii != null && ii.position == mCurItem) {
-                    if (child.requestFocus(direction, previouslyFocusedRect)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        // Dispatch scroll events from this ViewPager.
-        if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {
-            return super.dispatchPopulateAccessibilityEvent(event);
-        }
-
-        // Dispatch all other accessibility events from the current page.
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == VISIBLE) {
-                final ItemInfo ii = infoForChild(child);
-                if (ii != null && ii.position == mCurItem
-                        && child.dispatchPopulateAccessibilityEvent(event)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams();
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        return generateDefaultLayoutParams();
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams && super.checkLayoutParams(p);
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    class MyAccessibilityDelegate extends AccessibilityDelegateCompat {
-
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-            event.setClassName(ViewPager.class.getName());
-            event.setScrollable(canScroll());
-            if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
-                event.setItemCount(mAdapter.getCount());
-                event.setFromIndex(mCurItem);
-                event.setToIndex(mCurItem);
-            }
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-            super.onInitializeAccessibilityNodeInfo(host, info);
-            info.setClassName(ViewPager.class.getName());
-            info.setScrollable(canScroll());
-            if (canScrollHorizontally(1)) {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
-            }
-            if (canScrollHorizontally(-1)) {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
-            }
-        }
-
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle args) {
-            if (super.performAccessibilityAction(host, action, args)) {
-                return true;
-            }
-            switch (action) {
-                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
-                    if (canScrollHorizontally(1)) {
-                        setCurrentItem(mCurItem + 1);
-                        return true;
-                    }
-                } return false;
-                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
-                    if (canScrollHorizontally(-1)) {
-                        setCurrentItem(mCurItem - 1);
-                        return true;
-                    }
-                } return false;
-            }
-            return false;
-        }
-
-        private boolean canScroll() {
-            return (mAdapter != null) && (mAdapter.getCount() > 1);
-        }
-    }
-
-    private class PagerObserver extends DataSetObserver {
-        PagerObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            dataSetChanged();
-        }
-        @Override
-        public void onInvalidated() {
-            dataSetChanged();
-        }
-    }
-
-    /**
-     * Layout parameters that should be supplied for views added to a
-     * ViewPager.
-     */
-    public static class LayoutParams extends ViewGroup.LayoutParams {
-        /**
-         * true if this view is a decoration on the pager itself and not
-         * a view supplied by the adapter.
-         */
-        public boolean isDecor;
-
-        /**
-         * Gravity setting for use on decor views only:
-         * Where to position the view page within the overall ViewPager
-         * container; constants are defined in {@link android.view.Gravity}.
-         */
-        public int gravity;
-
-        /**
-         * Width as a 0-1 multiplier of the measured pager width
-         */
-        float widthFactor = 0.f;
-
-        /**
-         * true if this view was added during layout and needs to be measured
-         * before being positioned.
-         */
-        boolean needsMeasure;
-
-        /**
-         * Adapter position this view is for if !isDecor
-         */
-        int position;
-
-        /**
-         * Current child index within the ViewPager that this view occupies
-         */
-        int childIndex;
-
-        public LayoutParams() {
-            super(MATCH_PARENT, MATCH_PARENT);
-        }
-
-        public LayoutParams(Context context, AttributeSet attrs) {
-            super(context, attrs);
-
-            final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
-            gravity = a.getInteger(0, Gravity.TOP);
-            a.recycle();
-        }
-    }
-
-    static class ViewPositionComparator implements Comparator<View> {
-        @Override
-        public int compare(View lhs, View rhs) {
-            final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();
-            final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();
-            if (llp.isDecor != rlp.isDecor) {
-                return llp.isDecor ? 1 : -1;
-            }
-            return llp.position - rlp.position;
-        }
-    }
-}
diff --git a/android/support/v4/view/ViewParentCompat.java b/android/support/v4/view/ViewParentCompat.java
deleted file mode 100644
index bb1587e..0000000
--- a/android/support/v4/view/ViewParentCompat.java
+++ /dev/null
@@ -1,537 +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.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;
-
-/**
- * Helper for accessing features in {@link ViewParent}.
- */
-public final class ViewParentCompat {
-
-    private static final String TAG = "ViewParentCompat";
-
-    static class ViewParentCompatBaseImpl {
-        public boolean onStartNestedScroll(ViewParent parent, View child, View target,
-                int nestedScrollAxes) {
-            if (parent instanceof NestedScrollingParent) {
-                return ((NestedScrollingParent) parent).onStartNestedScroll(child, target,
-                        nestedScrollAxes);
-            }
-            return false;
-        }
-
-        public void onNestedScrollAccepted(ViewParent parent, View child, View target,
-                int nestedScrollAxes) {
-            if (parent instanceof NestedScrollingParent) {
-                ((NestedScrollingParent) parent).onNestedScrollAccepted(child, target,
-                        nestedScrollAxes);
-            }
-        }
-
-        public void onStopNestedScroll(ViewParent parent, View target) {
-            if (parent instanceof NestedScrollingParent) {
-                ((NestedScrollingParent) parent).onStopNestedScroll(target);
-            }
-        }
-
-        public void onNestedScroll(ViewParent parent, View target, int dxConsumed, int dyConsumed,
-                int dxUnconsumed, int dyUnconsumed) {
-            if (parent instanceof NestedScrollingParent) {
-                ((NestedScrollingParent) parent).onNestedScroll(target, dxConsumed, dyConsumed,
-                        dxUnconsumed, dyUnconsumed);
-            }
-        }
-
-        public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
-                int[] consumed) {
-            if (parent instanceof NestedScrollingParent) {
-                ((NestedScrollingParent) parent).onNestedPreScroll(target, dx, dy, consumed);
-            }
-        }
-
-        public boolean onNestedFling(ViewParent parent, View target, float velocityX,
-                float velocityY, boolean consumed) {
-            if (parent instanceof NestedScrollingParent) {
-                return ((NestedScrollingParent) parent).onNestedFling(target, velocityX, velocityY,
-                        consumed);
-            }
-            return false;
-        }
-
-        public boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
-                float velocityY) {
-            if (parent instanceof NestedScrollingParent) {
-                return ((NestedScrollingParent) parent).onNestedPreFling(target, velocityX,
-                        velocityY);
-            }
-            return false;
-        }
-
-        public void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-                View source, int changeType) {
-        }
-    }
-
-    @RequiresApi(19)
-    static class ViewParentCompatApi19Impl extends ViewParentCompatBaseImpl {
-
-        @Override
-        public void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-                View source, int changeType) {
-            parent.notifySubtreeAccessibilityStateChanged(child, source, changeType);
-        }
-    }
-
-    @RequiresApi(21)
-    static class ViewParentCompatApi21Impl extends ViewParentCompatApi19Impl {
-        @Override
-        public 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;
-            }
-        }
-
-        @Override
-        public 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);
-            }
-        }
-
-        @Override
-        public 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);
-            }
-        }
-
-        @Override
-        public 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);
-            }
-        }
-
-        @Override
-        public 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);
-            }
-        }
-
-        @Override
-        public 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;
-            }
-        }
-
-        @Override
-        public 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;
-            }
-        }
-    }
-
-    static final ViewParentCompatBaseImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new ViewParentCompatApi21Impl();
-        } else if (Build.VERSION.SDK_INT >= 19) {
-            IMPL = new ViewParentCompatApi19Impl();
-        } else {
-            IMPL = new ViewParentCompatBaseImpl();
-        }
-    }
-
-    /*
-     * Hide the constructor.
-     */
-    private ViewParentCompat() {}
-
-    /**
-     * Called by a child to request from its parent to send an {@link AccessibilityEvent}.
-     * The child has already populated a record for itself in the event and is delegating
-     * to its parent to send the event. The parent can optionally add a record for itself.
-     * <p>
-     * Note: An accessibility event is fired by an individual view which populates the
-     *       event with a record for its state and requests from its parent to perform
-     *       the sending. The parent can optionally add a record for itself before
-     *       dispatching the request to its parent. A parent can also choose not to
-     *       respect the request for sending the event. The accessibility event is sent
-     *       by the topmost view in the view tree.</p>
-     *
-     * @param parent The parent whose method to invoke.
-     * @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 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);
-    }
-
-    /**
-     * 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(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, 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;
-    }
-
-    /**
-     * React to the successful claiming of a nested scroll operation.
-     *
-     * <p>This method will be called after
-     * {@link #onStartNestedScroll(ViewParent, View, View, 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 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, int)
-     */
-    public static void onNestedScrollAccepted(ViewParent parent, View child, View target,
-            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);
-        }
-    }
-
-    /**
-     * 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
-     */
-    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);
-        }
-    }
-
-    /**
-     * 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(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
-     * 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
-     */
-    public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
-            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);
-        }
-    }
-
-    /**
-     * 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 ViewCompat#dispatchNestedPreScroll(View, 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
-     */
-    public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
-            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);
-        }
-    }
-
-    /**
-     * Request a fling from a nested scroll.
-     *
-     * <p>This method signifies that a nested scrolling child has detected suitable conditions
-     * for a fling. Generally this means that a touch scroll has ended with a
-     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
-     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
-     * along a scrollable axis.</p>
-     *
-     * <p>If a nested scrolling child view would normally fling but it is at the edge of
-     * its own content, it can use this method to delegate the fling to its nested scrolling
-     * parent instead. The parent may optionally consume the fling or observe a child fling.</p>
-     *
-     * @param target View that initiated the nested scroll
-     * @param velocityX Horizontal velocity in pixels per second
-     * @param velocityY Vertical velocity in pixels per second
-     * @param consumed true if the child consumed the fling, false otherwise
-     * @return true if this parent consumed or otherwise reacted to the fling
-     */
-    public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
-            float velocityY, boolean consumed) {
-        return IMPL.onNestedFling(parent, target, velocityX, velocityY, consumed);
-    }
-
-    /**
-     * React to a nested fling before the target view consumes it.
-     *
-     * <p>This method siginfies that a nested scrolling child has detected a fling with the given
-     * velocity along each axis. Generally this means that a touch scroll has ended with a
-     * {@link VelocityTracker velocity} in the direction of scrolling that meets or exceeds
-     * the {@link ViewConfiguration#getScaledMinimumFlingVelocity() minimum fling velocity}
-     * along a scrollable axis.</p>
-     *
-     * <p>If a nested scrolling parent is consuming motion as part of a
-     * {@link #onNestedPreScroll(ViewParent, View, int, int, int[]) pre-scroll}, it may be
-     * appropriate for it to also consume the pre-fling to complete that same motion. By returning
-     * <code>true</code> from this method, the parent indicates that the child should not
-     * fling its own internal content as well.</p>
-     *
-     * @param target View that initiated the nested scroll
-     * @param velocityX Horizontal velocity in pixels per second
-     * @param velocityY Vertical velocity in pixels per second
-     * @return true if this parent consumed the fling ahead of the target view
-     */
-    public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,
-            float velocityY) {
-        return IMPL.onNestedPreFling(parent, target, velocityX, velocityY);
-    }
-
-    /**
-     * Notifies a view parent that the accessibility state of one of its
-     * descendants has changed and that the structure of the subtree is
-     * different.
-     * @param child The direct child whose subtree has changed.
-     * @param source The descendant view that changed.
-     * @param changeType A bit mask of the types of changes that occurred. One
-     *            or more of:
-     *            <ul>
-     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
-     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
-     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
-     *            <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
-     *            </ul>
-     */
-    public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,
-            View source, int changeType) {
-        IMPL.notifySubtreeAccessibilityStateChanged(parent, child, source, changeType);
-    }
-}
diff --git a/android/support/v4/view/ViewPropertyAnimatorCompat.java b/android/support/v4/view/ViewPropertyAnimatorCompat.java
deleted file mode 100644
index 44b1a11..0000000
--- a/android/support/v4/view/ViewPropertyAnimatorCompat.java
+++ /dev/null
@@ -1,790 +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.animation.ValueAnimator;
-import android.os.Build;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import java.lang.ref.WeakReference;
-
-public final class ViewPropertyAnimatorCompat {
-    private static final String TAG = "ViewAnimatorCompat";
-    private WeakReference<View> mView;
-    Runnable mStartAction = null;
-    Runnable mEndAction = null;
-    int mOldLayerType = -1;
-    // HACK ALERT! Choosing this id knowing that the framework does not use it anywhere
-    // internally and apps should use ids higher than it
-    static final int LISTENER_TAG_ID = 0x7e000000;
-
-    ViewPropertyAnimatorCompat(View view) {
-        mView = new WeakReference<View>(view);
-    }
-
-    static class ViewPropertyAnimatorListenerApi14 implements ViewPropertyAnimatorListener {
-        ViewPropertyAnimatorCompat mVpa;
-        boolean mAnimEndCalled;
-
-        ViewPropertyAnimatorListenerApi14(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 > -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;
-            }
-            if (listener != null) {
-                listener.onAnimationStart(view);
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(View view) {
-            if (mVpa.mOldLayerType > -1) {
-                view.setLayerType(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;
-            }
-            if (listener != null) {
-                listener.onAnimationCancel(view);
-            }
-        }
-    }
-
-    /**
-     * Sets the duration for the underlying animator that animates the requested properties.
-     * By default, the animator uses the default value for ValueAnimator. Calling this method
-     * will cause the declared value to be used instead.
-     *
-     * @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.
-     */
-    public ViewPropertyAnimatorCompat setDuration(long value) {
-        View view;
-        if ((view = mView.get()) != null) {
-            view.animate().setDuration(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().alpha(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().alphaBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().translationX(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().translationY(value);
-        }
-        return this;
-    }
-
-    /**
-     * Specifies an action to take place when the next animation ends. The action is only
-     * run if the animation ends normally; if the ViewPropertyAnimator is canceled during
-     * that animation, the runnable will not run.
-     * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate
-     * choreographing ViewPropertyAnimator animations with other animations or actions
-     * in the application.
-     *
-     * <p>For example, the following code animates a view to x=200 and then back to 0:</p>
-     * <pre>
-     *     Runnable endAction = new Runnable() {
-     *         public void run() {
-     *             view.animate().x(0);
-     *         }
-     *     };
-     *     view.animate().x(200).withEndAction(endAction);
-     * </pre>
-     *
-     * <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>
-     *
-     * @param runnable The action to run when the next animation ends.
-     * @return This object, allowing calls to methods in this class to be chained.
-     */
-    public ViewPropertyAnimatorCompat withEndAction(Runnable runnable) {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 16) {
-                view.animate().withEndAction(runnable);
-            } else {
-                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
-                mEndAction = runnable;
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Returns the current duration of property animations. If the duration was set on this
-     * object, that value is returned. Otherwise, the default value of the underlying Animator
-     * is returned.
-     *
-     * @see #setDuration(long)
-     * @return The duration of animations, in milliseconds.
-     */
-    public long getDuration() {
-        View view;
-        if ((view = mView.get()) != null) {
-            return view.animate().getDuration();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Sets the interpolator for the underlying animator that animates the requested properties.
-     * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
-     * will cause the declared object to be used instead.
-     *
-     * @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) {
-            view.animate().setInterpolator(value);
-        }
-        return this;
-    }
-
-    /**
-     * Returns the timing interpolator that this animation uses.
-     *
-     * @return The timing interpolator for this animation.
-     */
-    public Interpolator getInterpolator() {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 18) {
-                return (Interpolator) view.animate().getInterpolator();
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Sets the startDelay for the underlying animator that animates the requested properties.
-     * By default, the animator uses the default value for ValueAnimator. Calling this method
-     * will cause the declared value to be used instead.
-     *
-     * @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.
-     */
-    public ViewPropertyAnimatorCompat setStartDelay(long value) {
-        View view;
-        if ((view = mView.get()) != null) {
-            view.animate().setStartDelay(value);
-        }
-        return this;
-    }
-
-    /**
-     * Returns the current startDelay of property animations. If the startDelay was set on this
-     * object, that value is returned. Otherwise, the default value of the underlying Animator
-     * is returned.
-     *
-     * @see #setStartDelay(long)
-     * @return The startDelay of animations, in milliseconds.
-     */
-    public long getStartDelay() {
-        View view;
-        if ((view = mView.get()) != null) {
-            return view.animate().getStartDelay();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().rotation(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().rotationBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().rotationX(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().rotationXBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().rotationY(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().rotationYBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().scaleX(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().scaleXBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().scaleY(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().scaleYBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * Cancels all property animations that are currently running or pending.
-     */
-    public void cancel() {
-        View view;
-        if ((view = mView.get()) != null) {
-            view.animate().cancel();
-        }
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().x(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().xBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().y(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().yBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().translationXBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * 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.
-     *
-     * @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) {
-            view.animate().translationYBy(value);
-        }
-        return this;
-    }
-
-    /**
-     * This method will cause the View's <code>translationZ</code> property to be animated by the
-     * specified value. Animations already running on the property will be canceled.
-     *
-     * <p>Prior to API 21, 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 translationZBy(float value) {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 21) {
-                view.animate().translationZBy(value);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * This method will cause the View's <code>translationZ</code> property to be animated to the
-     * specified value. Animations already running on the property will be canceled.
-     *
-     * <p>Prior to API 21, 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 translationZ(float value) {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 21) {
-                view.animate().translationZ(value);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * This method will cause the View's <code>z</code> property to be animated to the
-     * specified value. Animations already running on the property will be canceled.
-     *
-     * <p>Prior to API 21, 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 z(float value) {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 21) {
-                view.animate().z(value);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * This method will cause the View's <code>z</code> property to be animated by the
-     * specified value. Animations already running on the property will be canceled.
-     *
-     * <p>Prior to API 21, 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 zBy(float value) {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 21) {
-                view.animate().zBy(value);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Starts the currently pending property animations immediately. Calling <code>start()</code>
-     * is optional because all animations start automatically at the next opportunity. However,
-     * 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.
-     */
-    public void start() {
-        View view;
-        if ((view = mView.get()) != null) {
-            view.animate().start();
-        }
-    }
-
-    /**
-     * The View associated with this ViewPropertyAnimator will have its
-     * {@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 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 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>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,
-     * and then restoring the orignal layer type in the listener's
-     * {@link ViewPropertyAnimatorListener#onAnimationEnd(View)} method.</p>
-     *
-     * @see View#setLayerType(int, android.graphics.Paint)
-     * @return This object, allowing calls to methods in this class to be chained.
-     */
-    public ViewPropertyAnimatorCompat withLayer() {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 16) {
-                view.animate().withLayer();
-            } else {
-                mOldLayerType = view.getLayerType();
-                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Specifies an action to take place when the next animation runs. If there is a
-     * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the
-     * action will run after that startDelay expires, when the actual animation begins.
-     * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate
-     * choreographing ViewPropertyAnimator animations with other animations or actions
-     * in the application.
-     *
-     * <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>
-     *
-     * @param runnable The action to run when the next animation starts.
-     * @return This object, allowing calls to methods in this class to be chained.
-     */
-    public ViewPropertyAnimatorCompat withStartAction(Runnable runnable) {
-        View view;
-        if ((view = mView.get()) != null) {
-            if (Build.VERSION.SDK_INT >= 16) {
-                view.animate().withStartAction(runnable);
-            } else {
-                setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this));
-                mStartAction = runnable;
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Sets a listener for events in the underlying Animators that run the property
-     * animations.
-     *
-     * @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(final ViewPropertyAnimatorListener listener) {
-        final View view;
-        if ((view = mView.get()) != null) {
-            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.
-     *
-     * <p>Prior to API 19, this method will do nothing.</p>
-     *
-     * @param listener The listener to be called with update 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 setUpdateListener(
-            final ViewPropertyAnimatorUpdateListener listener) {
-        final View view;
-        if ((view = mView.get()) != null) {
-            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/android/support/v4/view/ViewPropertyAnimatorListener.java b/android/support/v4/view/ViewPropertyAnimatorListener.java
deleted file mode 100644
index 5c8e862..0000000
--- a/android/support/v4/view/ViewPropertyAnimatorListener.java
+++ /dev/null
@@ -1,49 +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.view.View;
-
-/**
- * <p>An animation listener receives notifications from an animation.
- * Notifications indicate animation related events, such as the end or the
- * start of the animation.</p>
- */
-public interface ViewPropertyAnimatorListener {
-    /**
-     * <p>Notifies the start of the animation.</p>
-     *
-     * @param view The view associated with the ViewPropertyAnimator
-     */
-    void onAnimationStart(View view);
-
-    /**
-     * <p>Notifies the end of the animation. This callback is not invoked
-     * for animations with repeat count set to INFINITE.</p>
-     *
-     * @param view The view associated with the ViewPropertyAnimator
-     */
-    void onAnimationEnd(View view);
-
-    /**
-     * <p>Notifies the cancellation of the animation. This callback is not invoked
-     * for animations with repeat count set to INFINITE.</p>
-     *
-     * @param view The view associated with the ViewPropertyAnimator
-     */
-    void onAnimationCancel(View view);
-}
diff --git a/android/support/v4/view/ViewPropertyAnimatorListenerAdapter.java b/android/support/v4/view/ViewPropertyAnimatorListenerAdapter.java
deleted file mode 100644
index 4f58912..0000000
--- a/android/support/v4/view/ViewPropertyAnimatorListenerAdapter.java
+++ /dev/null
@@ -1,49 +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.view.View;
-
-/**
- * This adapter class provides empty implementations of the methods from
- * {@link ViewPropertyAnimatorListener}. 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 ViewPropertyAnimatorListenerAdapter implements ViewPropertyAnimatorListener {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAnimationStart(View view) {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAnimationEnd(View view) {
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onAnimationCancel(View view) {
-    }
-}
diff --git a/android/support/v4/view/ViewPropertyAnimatorUpdateListener.java b/android/support/v4/view/ViewPropertyAnimatorUpdateListener.java
deleted file mode 100644
index b189d8a..0000000
--- a/android/support/v4/view/ViewPropertyAnimatorUpdateListener.java
+++ /dev/null
@@ -1,36 +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.view.View;
-
-/**
- * Implementors of this interface can add themselves as update listeners
- * to an <code>ViewPropertyAnimatorCompat</code> instance to receive callbacks on every animation
- * frame, after the current frame's values have been calculated for that
- * <code>ViewPropertyAnimatorCompat</code>.
- */
-public interface ViewPropertyAnimatorUpdateListener {
-
-    /**
-     * <p>Notifies the occurrence of another frame of the animation.</p>
-     *
-     * @param view The view associated with the ViewPropertyAnimatorCompat
-     */
-    void onAnimationUpdate(View view);
-
-}
diff --git a/android/support/v4/view/WindowCompat.java b/android/support/v4/view/WindowCompat.java
deleted file mode 100644
index dd0a736..0000000
--- a/android/support/v4/view/WindowCompat.java
+++ /dev/null
@@ -1,89 +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.IdRes;
-import android.support.annotation.NonNull;
-import android.view.View;
-import android.view.Window;
-
-/**
- * Helper for accessing features in {@link Window}.
- */
-public final class WindowCompat {
-    /**
-     * Flag for enabling the Action Bar.
-     * This is enabled by default for some devices. The Action Bar
-     * replaces the title bar and provides an alternate location
-     * for an on-screen menu button on some devices.
-     */
-    public static final int FEATURE_ACTION_BAR = 8;
-
-    /**
-     * Flag for requesting an Action Bar that overlays window content.
-     * Normally an Action Bar will sit in the space above window content, but if this
-     * feature is requested along with {@link #FEATURE_ACTION_BAR} it will be layered over
-     * the window content itself. This is useful if you would like your app to have more control
-     * over how the Action Bar is displayed, such as letting application content scroll beneath
-     * an Action Bar with a transparent background or otherwise displaying a transparent/translucent
-     * Action Bar over application content.
-     *
-     * <p>This mode is especially useful with {@link View#SYSTEM_UI_FLAG_FULLSCREEN
-     * View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows you to seamlessly hide the
-     * action bar in conjunction with other screen decorations.
-     *
-     * <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, when an
-     * ActionBar is in this mode it will adjust the insets provided to
-     * {@link View#fitSystemWindows(android.graphics.Rect) View.fitSystemWindows(Rect)}
-     * to include the content covered by the action bar, so you can do layout within
-     * that space.
-     */
-    public static final int FEATURE_ACTION_BAR_OVERLAY = 9;
-
-    /**
-     * Flag for specifying the behavior of action modes when an Action Bar is not present.
-     * If overlay is enabled, the action mode UI will be allowed to cover existing window content.
-     */
-    public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
-
-    private WindowCompat() {}
-
-    /**
-     * Finds a view that was identified by the {@code android:id} XML attribute
-     * that was processed in {@link android.app.Activity#onCreate}, or throws an
-     * IllegalArgumentException if the ID is invalid, or there is no matching view in the hierarchy.
-     * <p>
-     * <strong>Note:</strong> In most cases -- depending on compiler support --
-     * the resulting view is automatically cast to the target class type. If
-     * the target class type is unconstrained, an explicit cast may be
-     * necessary.
-     *
-     * @param id the ID to search for
-     * @return a view with given ID
-     * @see ViewCompat#requireViewById(View, int)
-     * @see Window#findViewById(int)
-     */
-    @NonNull
-    public static <T extends View> T requireViewById(@NonNull Window window, @IdRes int id) {
-        // TODO: use and link to Window#requireViewById() directly, once available
-        T view = window.findViewById(id);
-        if (view == null) {
-            throw new IllegalArgumentException("ID does not reference a View inside this Window");
-        }
-        return view;
-    }
-}
diff --git a/android/support/v4/view/WindowInsetsCompat.java b/android/support/v4/view/WindowInsetsCompat.java
deleted file mode 100644
index 1e23ff9..0000000
--- a/android/support/v4/view/WindowInsetsCompat.java
+++ /dev/null
@@ -1,370 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.graphics.Rect;
-import android.view.WindowInsets;
-
-/**
- * Describes a set of insets for window content.
- *
- * <p>WindowInsetsCompats are immutable and may be expanded to include more inset types in the
- * future. To adjust insets, use one of the supplied clone methods to obtain a new
- * WindowInsetsCompat instance with the adjusted properties.</p>
- */
-public class WindowInsetsCompat {
-    private final Object mInsets;
-
-    private WindowInsetsCompat(Object insets) {
-        mInsets = insets;
-    }
-
-    /**
-     * Constructs a new WindowInsetsCompat, copying all values from a source WindowInsetsCompat.
-     *
-     * @param src source from which values are copied
-     */
-    public WindowInsetsCompat(WindowInsetsCompat src) {
-        if (SDK_INT >= 20) {
-            mInsets = src == null ? null : new WindowInsets((WindowInsets) src.mInsets);
-        } else {
-            mInsets = null;
-        }
-    }
-
-    /**
-     * Returns the left system window inset in pixels.
-     *
-     * <p>The system window inset represents the area of a full-screen window that is
-     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
-     * </p>
-     *
-     * @return The left system window inset
-     */
-    public int getSystemWindowInsetLeft() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).getSystemWindowInsetLeft();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns the top system window inset in pixels.
-     *
-     * <p>The system window inset represents the area of a full-screen window that is
-     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
-     * </p>
-     *
-     * @return The top system window inset
-     */
-    public int getSystemWindowInsetTop() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).getSystemWindowInsetTop();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns the right system window inset in pixels.
-     *
-     * <p>The system window inset represents the area of a full-screen window that is
-     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
-     * </p>
-     *
-     * @return The right system window inset
-     */
-    public int getSystemWindowInsetRight() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).getSystemWindowInsetRight();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns the bottom system window inset in pixels.
-     *
-     * <p>The system window inset represents the area of a full-screen window that is
-     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
-     * </p>
-     *
-     * @return The bottom system window inset
-     */
-    public int getSystemWindowInsetBottom() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).getSystemWindowInsetBottom();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns true if this WindowInsets has nonzero system window insets.
-     *
-     * <p>The system window inset represents the area of a full-screen window that is
-     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
-     * </p>
-     *
-     * @return true if any of the system window inset values are nonzero
-     */
-    public boolean hasSystemWindowInsets() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).hasSystemWindowInsets();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns true if this WindowInsets has any nonzero insets.
-     *
-     * @return true if any inset values are nonzero
-     */
-    public boolean hasInsets() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).hasInsets();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Check if these insets have been fully consumed.
-     *
-     * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods
-     * have been called such that all insets have been set to zero. This affects propagation of
-     * insets through the view hierarchy; insets that have not been fully consumed will continue
-     * to propagate down to child views.</p>
-     *
-     * <p>The result of this method is equivalent to the return value of
-     * {@link android.view.View#fitSystemWindows(android.graphics.Rect)}.</p>
-     *
-     * @return true if the insets have been fully consumed.
-     */
-    public boolean isConsumed() {
-        if (SDK_INT >= 21) {
-            return ((WindowInsets) mInsets).isConsumed();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns true if the associated window has a round shape.
-     *
-     * <p>A round window's left, top, right and bottom edges reach all the way to the
-     * associated edges of the window but the corners may not be visible. Views responding
-     * to round insets should take care to not lay out critical elements within the corners
-     * where they may not be accessible.</p>
-     *
-     * @return True if the window is round
-     */
-    public boolean isRound() {
-        if (SDK_INT >= 20) {
-            return ((WindowInsets) mInsets).isRound();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns a copy of this WindowInsets with the system window insets fully consumed.
-     *
-     * @return A modified copy of this WindowInsets
-     */
-    public WindowInsetsCompat consumeSystemWindowInsets() {
-        if (SDK_INT >= 20) {
-            return new WindowInsetsCompat(((WindowInsets) mInsets).consumeSystemWindowInsets());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns a copy of this WindowInsets with selected system window insets replaced
-     * with new values.
-     *
-     * @param left New left inset in pixels
-     * @param top New top inset in pixels
-     * @param right New right inset in pixels
-     * @param bottom New bottom inset in pixels
-     * @return A modified copy of this WindowInsets
-     */
-    public WindowInsetsCompat replaceSystemWindowInsets(int left, int top, int right, int bottom) {
-        if (SDK_INT >= 20) {
-            return new WindowInsetsCompat(
-                    ((WindowInsets) mInsets).replaceSystemWindowInsets(left, top, right, bottom));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns a copy of this WindowInsets with selected system window insets replaced
-     * with new values.
-     *
-     * @param systemWindowInsets New system window insets. Each field is the inset in pixels
-     *                           for that edge
-     * @return A modified copy of this WindowInsets
-     */
-    public WindowInsetsCompat replaceSystemWindowInsets(Rect systemWindowInsets) {
-        if (SDK_INT >= 21) {
-            return new WindowInsetsCompat(
-                    ((WindowInsets) mInsets).replaceSystemWindowInsets(systemWindowInsets));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns the top stable inset in pixels.
-     *
-     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
-     * partially or fully obscured by the system UI elements.  This value does not change
-     * based on the visibility state of those elements; for example, if the status bar is
-     * normally shown, but temporarily hidden, the stable inset will still provide the inset
-     * associated with the status bar being shown.</p>
-     *
-     * @return The top stable inset
-     */
-    public int getStableInsetTop() {
-        if (SDK_INT >= 21) {
-            return ((WindowInsets) mInsets).getStableInsetTop();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns the left stable inset in pixels.
-     *
-     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
-     * partially or fully obscured by the system UI elements.  This value does not change
-     * based on the visibility state of those elements; for example, if the status bar is
-     * normally shown, but temporarily hidden, the stable inset will still provide the inset
-     * associated with the status bar being shown.</p>
-     *
-     * @return The left stable inset
-     */
-    public int getStableInsetLeft() {
-        if (SDK_INT >= 21) {
-            return ((WindowInsets) mInsets).getStableInsetLeft();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns the right stable inset in pixels.
-     *
-     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
-     * partially or fully obscured by the system UI elements.  This value does not change
-     * based on the visibility state of those elements; for example, if the status bar is
-     * normally shown, but temporarily hidden, the stable inset will still provide the inset
-     * associated with the status bar being shown.</p>
-     *
-     * @return The right stable inset
-     */
-    public int getStableInsetRight() {
-        if (SDK_INT >= 21) {
-            return ((WindowInsets) mInsets).getStableInsetRight();
-        } else {
-            return 0;
-        }
-    }
-
-
-    /**
-     * Returns the bottom stable inset in pixels.
-     *
-     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
-     * partially or fully obscured by the system UI elements.  This value does not change
-     * based on the visibility state of those elements; for example, if the status bar is
-     * normally shown, but temporarily hidden, the stable inset will still provide the inset
-     * associated with the status bar being shown.</p>
-     *
-     * @return The bottom stable inset
-     */
-    public int getStableInsetBottom() {
-        if (SDK_INT >= 21) {
-            return ((WindowInsets) mInsets).getStableInsetBottom();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Returns true if this WindowInsets has nonzero stable insets.
-     *
-     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
-     * partially or fully obscured by the system UI elements.  This value does not change
-     * based on the visibility state of those elements; for example, if the status bar is
-     * normally shown, but temporarily hidden, the stable inset will still provide the inset
-     * associated with the status bar being shown.</p>
-     *
-     * @return true if any of the stable inset values are nonzero
-     */
-    public boolean hasStableInsets() {
-        if (SDK_INT >= 21) {
-            return ((WindowInsets) mInsets).hasStableInsets();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Returns a copy of this WindowInsets with the stable insets fully consumed.
-     *
-     * @return A modified copy of this WindowInsetsCompat
-     */
-    public WindowInsetsCompat consumeStableInsets() {
-        if (SDK_INT >= 21) {
-            return new WindowInsetsCompat(((WindowInsets) mInsets).consumeStableInsets());
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        WindowInsetsCompat other = (WindowInsetsCompat) o;
-        return mInsets == null ? other.mInsets == null : mInsets.equals(other.mInsets);
-    }
-
-    @Override
-    public int hashCode() {
-        return mInsets == null ? 0 : mInsets.hashCode();
-    }
-
-    static WindowInsetsCompat wrap(Object insets) {
-        return insets == null ? null : new WindowInsetsCompat(insets);
-    }
-
-    static Object unwrap(WindowInsetsCompat insets) {
-        return insets == null ? null : insets.mInsets;
-    }
-}
diff --git a/android/support/v4/view/accessibility/AccessibilityEventCompat.java b/android/support/v4/view/accessibility/AccessibilityEventCompat.java
deleted file mode 100644
index 462ad11..0000000
--- a/android/support/v4/view/accessibility/AccessibilityEventCompat.java
+++ /dev/null
@@ -1,351 +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.Build;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityRecord;
-
-/**
- * Helper for accessing features in {@link AccessibilityEvent}.
- */
-public final class AccessibilityEventCompat {
-    /**
-     * Represents the event of a hover enter over a {@link android.view.View}.
-     * @deprecated Use {@link  AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} directly.
-     */
-    @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.
-     */
-    @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.
-     */
-    @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.
-     */
-    @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.
-     */
-    @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.
-     */
-    @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.
-     */
-    @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.
-     */
-    public static final int TYPE_ANNOUNCEMENT = 0x00004000;
-
-    /**
-     * Represents the event of gaining accessibility focus.
-     */
-    public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
-
-    /**
-     * Represents the event of clearing accessibility focus.
-     */
-    public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
-
-    /**
-     * Represents the event of traversing the text of a view at a given movement granularity.
-     */
-    public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000;
-
-    /**
-     * Represents the event of beginning gesture detection.
-     */
-    public static final int TYPE_GESTURE_DETECTION_START = 0x00040000;
-
-    /**
-     * Represents the event of ending gesture detection.
-     */
-    public static final int TYPE_GESTURE_DETECTION_END = 0x00080000;
-
-    /**
-     * Represents the event of the user starting to touch the screen.
-     */
-    public static final int TYPE_TOUCH_INTERACTION_START = 0x00100000;
-
-    /**
-     * Represents the event of the user ending to touch the screen.
-     */
-    public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
-
-    /**
-     * Represents the event change in the windows shown on the screen.
-     */
-    public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
-
-    /**
-     * Represents the event of a context click on a {@link android.view.View}.
-     */
-    public static final int TYPE_VIEW_CONTEXT_CLICKED = 0x00800000;
-
-    /**
-     * Represents the event of the assistant currently reading the users screen context.
-     */
-    public static final int TYPE_ASSIST_READING_CONTEXT = 0x01000000;
-
-    /**
-     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
-     * The type of change is not defined.
-     */
-    public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000;
-
-    /**
-     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
-     * A node in the subtree rooted at the source node was added or removed.
-     */
-    public static final int CONTENT_CHANGE_TYPE_SUBTREE = 0x00000001;
-
-    /**
-     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
-     * The node's text changed.
-     */
-    public static final int CONTENT_CHANGE_TYPE_TEXT = 0x00000002;
-
-    /**
-     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
-     * The node's content description changed.
-     */
-    public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
-
-    /**
-     * Mask for {@link AccessibilityEvent} all types.
-     *
-     * @see AccessibilityEvent#TYPE_VIEW_CLICKED
-     * @see AccessibilityEvent#TYPE_VIEW_LONG_CLICKED
-     * @see AccessibilityEvent#TYPE_VIEW_SELECTED
-     * @see AccessibilityEvent#TYPE_VIEW_FOCUSED
-     * @see AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
-     * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
-     * @see AccessibilityEvent#TYPE_NOTIFICATION_STATE_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;
-
-    /*
-     * Hide constructor from clients.
-     */
-    private AccessibilityEventCompat() {
-
-    }
-
-    /**
-     * 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 event.getRecordCount();
-    }
-
-    /**
-     * Appends an {@link android.view.accessibility.AccessibilityRecord} to the end of
-     * event records.
-     *
-     * @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) {
-        event.appendRecord((AccessibilityRecord) record.getImpl());
-    }
-
-    /**
-     * Gets the record at a given index.
-     *
-     * @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(event.getRecord(index));
-    }
-
-    /**
-     * Creates an {@link AccessibilityRecordCompat} from an {@link AccessibilityEvent}
-     * that can be used to manipulate the event properties defined in
-     * {@link android.view.accessibility.AccessibilityRecord}.
-     * <p>
-     * <strong>Note:</strong> Do not call {@link AccessibilityRecordCompat#recycle()} on the
-     * returned {@link AccessibilityRecordCompat}. Call {@link AccessibilityEvent#recycle()}
-     * in case you want to recycle the event.
-     * </p>
-     *
-     * @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);
-    }
-
-    /**
-     * Sets the bit mask of node tree changes signaled by an
-     * {@link #TYPE_WINDOW_CONTENT_CHANGED} event.
-     *
-     * @param changeTypes The bit mask of change types.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     * @see #getContentChangeTypes(AccessibilityEvent)
-     */
-    public static void setContentChangeTypes(AccessibilityEvent event, int changeTypes) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            event.setContentChangeTypes(changeTypes);
-        }
-    }
-
-    /**
-     * Gets the bit mask of change types signaled by an
-     * {@link #TYPE_WINDOW_CONTENT_CHANGED} event. A single event may represent
-     * multiple change types.
-     *
-     * @return The bit mask of change types. One or more of:
-     *         <ul>
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
-     *         </ul>
-     */
-    public static int getContentChangeTypes(AccessibilityEvent event) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return event.getContentChangeTypes();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Sets the movement granularity that was traversed.
-     *
-     * @param granularity The granularity.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public static void setMovementGranularity(AccessibilityEvent event, int granularity) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            event.setMovementGranularity(granularity);
-        }
-    }
-
-    /**
-     * Gets the movement granularity that was traversed.
-     *
-     * @return The granularity.
-     */
-    public static int getMovementGranularity(AccessibilityEvent event) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return event.getMovementGranularity();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Sets the performed action that triggered this event.
-     * <p>
-     * Valid actions are defined in {@link AccessibilityNodeInfoCompat}:
-     * <ul>
-     * <li>{@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}
-     * <li>{@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
-     * <li>{@link AccessibilityNodeInfoCompat#ACTION_CLEAR_FOCUS}
-     * <li>{@link AccessibilityNodeInfoCompat#ACTION_CLEAR_SELECTION}
-     * <li>{@link AccessibilityNodeInfoCompat#ACTION_CLICK}
-     * <li>etc.
-     * </ul>
-     *
-     * @param action The action.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     * @see AccessibilityNodeInfoCompat#performAction(int)
-     */
-    public static void setAction(AccessibilityEvent event, int action) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            event.setAction(action);
-        }
-    }
-
-    /**
-     * Gets the performed action that triggered this event.
-     *
-     * @return The action.
-     */
-    public static int getAction(AccessibilityEvent event) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return event.getAction();
-        } else {
-            return 0;
-        }
-    }
-}
diff --git a/android/support/v4/view/accessibility/AccessibilityManagerCompat.java b/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
deleted file mode 100644
index 5767f74..0000000
--- a/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
+++ /dev/null
@@ -1,274 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.view.accessibility.AccessibilityManager;
-
-import java.util.List;
-
-/**
- * Helper for accessing features in {@link AccessibilityManager}.
- */
-public final class AccessibilityManagerCompat {
-    /**
-     * Registers an {@link AccessibilityManager.AccessibilityStateChangeListener} for changes in
-     * the global accessibility state of the system.
-     *
-     * @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) {
-        if (listener == null) {
-            return false;
-        }
-        return manager.addAccessibilityStateChangeListener(
-                new AccessibilityStateChangeListenerWrapper(listener));
-    }
-
-    /**
-     * Unregisters an {@link AccessibilityManager.AccessibilityStateChangeListener}.
-     *
-     * @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) {
-        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);
-        }
-    }
-
-    /**
-     * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
-     *
-     * @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 manager.getInstalledAccessibilityServiceList();
-    }
-
-    /**
-     * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
-     * for a given feedback type.
-     *
-     * @param manager The accessibility manager.
-     * @param feedbackTypeFlags The feedback type flags.
-     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
-     *
-     * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
-     * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
-     * @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 manager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
-    }
-
-    /**
-     * Returns if the touch exploration in the system is enabled.
-     *
-     * @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 manager.isTouchExplorationEnabled();
-    }
-
-    /**
-     * Registers a {@link TouchExplorationStateChangeListener} for changes in
-     * the global touch exploration state of the system.
-     *
-     * @param listener The listener.
-     * @return True if successfully registered.
-     */
-    public static boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,
-            TouchExplorationStateChangeListener listener) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            if (listener == null) {
-                return false;
-            }
-            return manager.addTouchExplorationStateChangeListener(
-                    new TouchExplorationStateChangeListenerWrapper(listener));
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Unregisters a {@link TouchExplorationStateChangeListener}.
-     *
-     * @param listener The listener.
-     * @return True if successfully unregistered.
-     */
-    public static boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,
-            TouchExplorationStateChangeListener 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 AccessibilityManager.AccessibilityStateChangeListener} directly
-     * instead of this listener.
-     */
-    @Deprecated
-    public static abstract class AccessibilityStateChangeListenerCompat
-            implements AccessibilityStateChangeListener {
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Listener for the system touch exploration state. To listen for changes to
-     * the touch exploration state on the device, implement this interface and
-     * register it with the system by calling
-     * {@link #addTouchExplorationStateChangeListener}.
-     */
-    public interface TouchExplorationStateChangeListener {
-        /**
-         * Called when the touch exploration enabled state changes.
-         *
-         * @param enabled Whether touch exploration is enabled.
-         */
-        void onTouchExplorationStateChanged(boolean enabled);
-    }
-
-    private AccessibilityManagerCompat() {}
-}
diff --git a/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
deleted file mode 100644
index 327be01..0000000
--- a/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
+++ /dev/null
@@ -1,3230 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.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} in a backwards
- * compatible fashion.
- */
-public class AccessibilityNodeInfoCompat {
-
-    public static class AccessibilityActionCompat {
-
-        /**
-         * Action that gives input focus to the node.
-         */
-        public static final AccessibilityActionCompat ACTION_FOCUS =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_FOCUS, null);
-
-        /**
-         * Action that clears input focus of the node.
-         */
-        public static final AccessibilityActionCompat ACTION_CLEAR_FOCUS =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS, null);
-
-        /**
-         *  Action that selects the node.
-         */
-        public static final AccessibilityActionCompat ACTION_SELECT =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_SELECT, null);
-
-        /**
-         * Action that deselects the node.
-         */
-        public static final AccessibilityActionCompat ACTION_CLEAR_SELECTION =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION, null);
-
-        /**
-         * Action that clicks on the node info.
-         */
-        public static final AccessibilityActionCompat ACTION_CLICK =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_CLICK, null);
-
-        /**
-         * Action that long clicks on the node.
-         */
-        public static final AccessibilityActionCompat ACTION_LONG_CLICK =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null);
-
-        /**
-         * Action that gives accessibility focus to the node.
-         */
-        public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);
-
-        /**
-         * Action that clears accessibility focus of the node.
-         */
-        public static final AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
-
-        /**
-         * Action that requests to go to the next entity in this node's text
-         * at a given movement granularity. For example, move to the next character,
-         * word, etc.
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
-         * <strong>Example:</strong> Move to the previous character and do not extend selection.
-         * <code><pre><p>
-         *   Bundle arguments = new Bundle();
-         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
-         *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
-         *   arguments.putBoolean(
-         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
-         *   info.performAction(
-         *           AccessibilityActionCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
-         *           arguments);
-         * </code></pre></p>
-         * </p>
-         *
-         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         *
-         * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         * @see AccessibilityNodeInfoCompat#getMovementGranularities()
-         *  AccessibilityNodeInfoCompat.getMovementGranularities()
-         *
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
-         */
-        public static final AccessibilityActionCompat ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null);
-
-        /**
-         * Action that requests to go to the previous entity in this node's text
-         * at a given movement granularity. For example, move to the next character,
-         * word, etc.
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
-         * <strong>Example:</strong> Move to the next character and do not extend selection.
-         * <code><pre><p>
-         *   Bundle arguments = new Bundle();
-         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
-         *           AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER);
-         *   arguments.putBoolean(
-         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
-         *   info.performAction(
-         *           AccessibilityActionCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
-         *           arguments);
-         * </code></pre></p>
-         * </p>
-         *
-         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-         *
-         * @see AccessibilityNodeInfoCompat#setMovementGranularities(int)
-         *   AccessibilityNodeInfoCompat.setMovementGranularities(int)
-         * @see AccessibilityNodeInfoCompat#getMovementGranularities()
-         *  AccessibilityNodeInfoCompat.getMovementGranularities()
-         *
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_CHARACTER
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_WORD
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_WORD
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_LINE
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_LINE
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PARAGRAPH
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PARAGRAPH
-         * @see AccessibilityNodeInfoCompat#MOVEMENT_GRANULARITY_PAGE
-         *  AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE
-         */
-        public static final AccessibilityActionCompat ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null);
-
-        /**
-         * Action to move to the next HTML element of a given type. For example, move
-         * to the BUTTON, INPUT, TABLE, etc.
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
-         * <strong>Example:</strong>
-         * <code><pre><p>
-         *   Bundle arguments = new Bundle();
-         *   arguments.putString(
-         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
-         *   info.performAction(
-         *           AccessibilityActionCompat.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
-         * </code></pre></p>
-         * </p>
-         */
-        public static final AccessibilityActionCompat ACTION_NEXT_HTML_ELEMENT =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_NEXT_HTML_ELEMENT, null);
-
-        /**
-         * Action to move to the previous HTML element of a given type. For example, move
-         * to the BUTTON, INPUT, TABLE, etc.
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
-         * <strong>Example:</strong>
-         * <code><pre><p>
-         *   Bundle arguments = new Bundle();
-         *   arguments.putString(
-         *           AccessibilityNodeInfoCompat.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
-         *   info.performAction(
-         *           AccessibilityActionCompat.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
-         * </code></pre></p>
-         * </p>
-         */
-        public static final AccessibilityActionCompat ACTION_PREVIOUS_HTML_ELEMENT =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_PREVIOUS_HTML_ELEMENT, null);
-
-        /**
-         * Action to scroll the node content forward.
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_FORWARD =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD, null);
-
-        /**
-         * Action to scroll the node content backward.
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_BACKWARD =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD, null);
-
-        /**
-         * Action to copy the current selection to the clipboard.
-         */
-        public static final AccessibilityActionCompat ACTION_COPY =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_COPY, null);
-
-        /**
-         * Action to paste the current clipboard content.
-         */
-        public static final AccessibilityActionCompat ACTION_PASTE =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_PASTE, null);
-
-        /**
-         * Action to cut the current selection and place it to the clipboard.
-         */
-        public static final AccessibilityActionCompat ACTION_CUT =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_CUT, null);
-
-        /**
-         * Action to set the selection. Performing this action with no arguments
-         * clears the selection.
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT},
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT}<br>
-         * <strong>Example:</strong>
-         * <code><pre><p>
-         *   Bundle arguments = new Bundle();
-         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 1);
-         *   arguments.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, 2);
-         *   info.performAction(AccessibilityActionCompat.ACTION_SET_SELECTION.getId(), arguments);
-         * </code></pre></p>
-         * </p>
-         *
-         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT
-         * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT
-         */
-        public static final AccessibilityActionCompat ACTION_SET_SELECTION =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, null);
-
-        /**
-         * Action to expand an expandable node.
-         */
-        public static final AccessibilityActionCompat ACTION_EXPAND =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_EXPAND, null);
-
-        /**
-         * Action to collapse an expandable node.
-         */
-        public static final AccessibilityActionCompat ACTION_COLLAPSE =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_COLLAPSE, null);
-
-        /**
-         * Action to dismiss a dismissable node.
-         */
-        public static final AccessibilityActionCompat ACTION_DISMISS =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_DISMISS, null);
-
-        /**
-         * Action that sets the text of the node. Performing the action without argument,
-         * using <code> null</code> or empty {@link CharSequence} will clear the text. This
-         * action will also put the cursor at the end of text.
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
-         *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
-         * <strong>Example:</strong>
-         * <code><pre><p>
-         *   Bundle arguments = new Bundle();
-         *   arguments.putCharSequence(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
-         *       "android");
-         *   info.performAction(AccessibilityActionCompat.ACTION_SET_TEXT.getId(), arguments);
-         * </code></pre></p>
-         */
-        public static final AccessibilityActionCompat ACTION_SET_TEXT =
-                new AccessibilityActionCompat(
-                        AccessibilityNodeInfoCompat.ACTION_SET_TEXT, null);
-
-        /**
-         * Action that requests the node make its bounding rectangle visible
-         * on the screen, scrolling if necessary just enough.
-         *
-         * @see View#requestRectangleOnScreen(Rect)
-         */
-        public static final AccessibilityActionCompat ACTION_SHOW_ON_SCREEN =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SHOW_ON_SCREEN : null);
-
-        /**
-         * Action that scrolls the node to make the specified collection
-         * position visible on screen.
-         * <p>
-         * <strong>Arguments:</strong>
-         * <ul>
-         *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_ROW_INT}</li>
-         *     <li>{@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_COLUMN_INT}</li>
-         * <ul>
-         *
-         * @see AccessibilityNodeInfoCompat#getCollectionInfo()
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_TO_POSITION =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_TO_POSITION
-                        : null);
-
-        /**
-         * Action to scroll the node content up.
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_UP =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP : null);
-
-        /**
-         * Action to scroll the node content left.
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_LEFT =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT : null);
-
-        /**
-         * Action to scroll the node content down.
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_DOWN =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN : null);
-
-        /**
-         * Action to scroll the node content right.
-         */
-        public static final AccessibilityActionCompat ACTION_SCROLL_RIGHT =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT : null);
-
-        /**
-         * Action that context clicks the node.
-         */
-        public static final AccessibilityActionCompat ACTION_CONTEXT_CLICK =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_CONTEXT_CLICK : null);
-
-        /**
-         * Action that sets progress between {@link  RangeInfoCompat#getMin() RangeInfo.getMin()} and
-         * {@link  RangeInfoCompat#getMax() RangeInfo.getMax()}. It should use the same value type as
-         * {@link RangeInfoCompat#getType() RangeInfo.getType()}
-         * <p>
-         * <strong>Arguments:</strong>
-         * {@link AccessibilityNodeInfoCompat#ACTION_ARGUMENT_PROGRESS_VALUE}
-         *
-         * @see RangeInfoCompat
-         */
-        public static final AccessibilityActionCompat ACTION_SET_PROGRESS =
-                new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 24
-                        ? AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS : null);
-
-        final Object mAction;
-
-        /**
-         * Creates a new instance.
-         *
-         * @param actionId The action id.
-         * @param label The action label.
-         */
-        public AccessibilityActionCompat(int actionId, CharSequence label) {
-            this(Build.VERSION.SDK_INT >= 21
-                    ? new AccessibilityNodeInfo.AccessibilityAction(actionId, label) : null);
-        }
-
-        AccessibilityActionCompat(Object action) {
-            mAction = action;
-        }
-
-        /**
-         * Gets the id for this action.
-         *
-         * @return The action id.
-         */
-        public int getId() {
-            if (Build.VERSION.SDK_INT >= 21) {
-                return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getId();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the label for this action. Its purpose is to describe the
-         * action to user.
-         *
-         * @return The label.
-         */
-        public CharSequence getLabel() {
-            if (Build.VERSION.SDK_INT >= 21) {
-                return ((AccessibilityNodeInfo.AccessibilityAction) mAction).getLabel();
-            } else {
-                return null;
-            }
-        }
-    }
-
-    /**
-     * Class with information if a node is a collection.
-     * <p>
-     * A collection of items has rows and columns and may be hierarchical.
-     * For example, a horizontal list is a collection with one column, as
-     * many rows as the list items, and is not hierarchical; A table is a
-     * collection with several rows, several columns, and is not hierarchical;
-     * A vertical tree is a hierarchical collection with one column and
-     * as many rows as the first level children.
-     * </p>
-     */
-    public static class CollectionInfoCompat {
-        /** Selection mode where items are not selectable. */
-        public static final int SELECTION_MODE_NONE = 0;
-
-        /** Selection mode where a single item may be selected. */
-        public static final int SELECTION_MODE_SINGLE = 1;
-
-        /** Selection mode where multiple items may be selected. */
-        public static final int SELECTION_MODE_MULTIPLE = 2;
-
-        final Object mInfo;
-
-        /**
-         * Returns a cached instance if such is available otherwise a new one.
-         *
-         * @param rowCount The number of rows.
-         * @param columnCount The number of columns.
-         * @param hierarchical Whether the collection is hierarchical.
-         * @param selectionMode The collection's selection mode, one of:
-         *            <ul>
-         *            <li>{@link #SELECTION_MODE_NONE}
-         *            <li>{@link #SELECTION_MODE_SINGLE}
-         *            <li>{@link #SELECTION_MODE_MULTIPLE}
-         *            </ul>
-         *
-         * @return An instance.
-         */
-        public static CollectionInfoCompat obtain(int rowCount, int columnCount,
-                boolean hierarchical, int selectionMode) {
-            if (Build.VERSION.SDK_INT >= 21) {
-                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
-                        rowCount, columnCount, hierarchical, selectionMode));
-            } else if (Build.VERSION.SDK_INT >= 19) {
-                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
-                        rowCount, columnCount, hierarchical));
-            } else {
-                return new CollectionInfoCompat(null);
-            }
-        }
-
-        /**
-         * Returns a cached instance if such is available otherwise a new one.
-         *
-         * @param rowCount The number of rows.
-         * @param columnCount The number of columns.
-         * @param hierarchical Whether the collection is hierarchical.
-         *
-         * @return An instance.
-         */
-        public static CollectionInfoCompat obtain(int rowCount, int columnCount,
-                boolean hierarchical) {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return new CollectionInfoCompat(AccessibilityNodeInfo.CollectionInfo.obtain(
-                        rowCount, columnCount, hierarchical));
-            } else {
-                return new CollectionInfoCompat(null);
-            }
-        }
-
-        CollectionInfoCompat(Object info) {
-            mInfo = info;
-        }
-
-        /**
-         * Gets the number of columns.
-         *
-         * @return The column count.
-         */
-        public int getColumnCount() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getColumnCount();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the number of rows.
-         *
-         * @return The row count.
-         */
-        public int getRowCount() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getRowCount();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets if the collection is a hierarchically ordered.
-         *
-         * @return Whether the collection is hierarchical.
-         */
-        public boolean isHierarchical() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).isHierarchical();
-            } else {
-                return false;
-            }
-        }
-
-        /**
-         * Gets the collection's selection mode.
-         *
-         * @return The collection's selection mode, one of:
-         *         <ul>
-         *         <li>{@link #SELECTION_MODE_NONE}
-         *         <li>{@link #SELECTION_MODE_SINGLE}
-         *         <li>{@link #SELECTION_MODE_MULTIPLE}
-         *         </ul>
-         */
-        public int getSelectionMode() {
-            if (Build.VERSION.SDK_INT >= 21) {
-                return ((AccessibilityNodeInfo.CollectionInfo) mInfo).getSelectionMode();
-            } else {
-                return 0;
-            }
-        }
-    }
-
-    /**
-     * Class with information if a node is a collection item.
-     * <p>
-     * A collection item is contained in a collection, it starts at
-     * a given row and column in the collection, and spans one or
-     * more rows and columns. For example, a header of two related
-     * table columns starts at the first row and the first column,
-     * spans one row and two columns.
-     * </p>
-     */
-    public static class CollectionItemInfoCompat {
-
-        final Object mInfo;
-
-        /**
-         * Returns a cached instance if such is available otherwise a new one.
-         *
-         * @param rowIndex The row index at which the item is located.
-         * @param rowSpan The number of rows the item spans.
-         * @param columnIndex The column index at which the item is located.
-         * @param columnSpan The number of columns the item spans.
-         * @param heading Whether the item is a heading.
-         * @param selected Whether the item is selected.
-         * @return An instance.
-         */
-        public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
-                int columnIndex, int columnSpan, boolean heading, boolean selected) {
-            if (Build.VERSION.SDK_INT >= 21) {
-                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
-                        rowIndex, rowSpan, columnIndex, columnSpan, heading, selected));
-            } else if (Build.VERSION.SDK_INT >= 19) {
-                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
-                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
-            } else {
-                return new CollectionItemInfoCompat(null);
-            }
-        }
-
-        /**
-         * Returns a cached instance if such is available otherwise a new one.
-         *
-         * @param rowIndex The row index at which the item is located.
-         * @param rowSpan The number of rows the item spans.
-         * @param columnIndex The column index at which the item is located.
-         * @param columnSpan The number of columns the item spans.
-         * @param heading Whether the item is a heading.
-         * @return An instance.
-         */
-        public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,
-                int columnIndex, int columnSpan, boolean heading) {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return new CollectionItemInfoCompat(AccessibilityNodeInfo.CollectionItemInfo.obtain(
-                        rowIndex, rowSpan, columnIndex, columnSpan, heading));
-            } else {
-                return new CollectionItemInfoCompat(null);
-            }
-        }
-
-        CollectionItemInfoCompat(Object info) {
-            mInfo = info;
-        }
-
-        /**
-         * Gets the column index at which the item is located.
-         *
-         * @return The column index.
-         */
-        public int getColumnIndex() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnIndex();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the number of columns the item spans.
-         *
-         * @return The column span.
-         */
-        public int getColumnSpan() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getColumnSpan();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the row index at which the item is located.
-         *
-         * @return The row index.
-         */
-        public int getRowIndex() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowIndex();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the number of rows the item spans.
-         *
-         * @return The row span.
-         */
-        public int getRowSpan() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).getRowSpan();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets if the collection item is a heading. For example, section
-         * heading, table header, etc.
-         *
-         * @return If the item is a heading.
-         */
-        public boolean isHeading() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isHeading();
-            } else {
-                return false;
-            }
-        }
-
-        /**
-         * Gets if the collection item is selected.
-         *
-         * @return If the item is selected.
-         */
-        public boolean isSelected() {
-            if (Build.VERSION.SDK_INT >= 21) {
-                return ((AccessibilityNodeInfo.CollectionItemInfo) mInfo).isSelected();
-            } else {
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Class with information if a node is a range.
-     */
-    public static class RangeInfoCompat {
-        /** Range type: integer. */
-        public static final int RANGE_TYPE_INT = 0;
-        /** Range type: float. */
-        public static final int RANGE_TYPE_FLOAT = 1;
-        /** Range type: percent with values from zero to one.*/
-        public static final int RANGE_TYPE_PERCENT = 2;
-
-        /**
-         * Obtains a cached instance if such is available otherwise a new one.
-         *
-         * @param type The type of the range.
-         * @param min The min value.
-         * @param max The max value.
-         * @param current The current value.
-         * @return The instance
-         */
-        public static RangeInfoCompat obtain(int type, float min, float max, float current) {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return new RangeInfoCompat(
-                        AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current));
-            } else {
-                return new RangeInfoCompat(null);
-            }
-        }
-
-        final Object mInfo;
-
-        RangeInfoCompat(Object info) {
-            mInfo = info;
-        }
-
-        /**
-         * Gets the current value.
-         *
-         * @return The current value.
-         */
-        public float getCurrent() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getCurrent();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the max value.
-         *
-         * @return The max value.
-         */
-        public float getMax() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMax();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the min value.
-         *
-         * @return The min value.
-         */
-        public float getMin() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getMin();
-            } else {
-                return 0;
-            }
-        }
-
-        /**
-         * Gets the range type.
-         *
-         * @return The range type.
-         *
-         * @see #RANGE_TYPE_INT
-         * @see #RANGE_TYPE_FLOAT
-         * @see #RANGE_TYPE_PERCENT
-         */
-        public int getType() {
-            if (Build.VERSION.SDK_INT >= 19) {
-                return ((AccessibilityNodeInfo.RangeInfo) mInfo).getType();
-            } else {
-                return RANGE_TYPE_INT;
-            }
-        }
-    }
-
-    private static final String ROLE_DESCRIPTION_KEY =
-            "AccessibilityNodeInfo.roleDescription";
-
-    private final AccessibilityNodeInfo mInfo;
-
-    /**
-     *  android.support.v4.widget.ExploreByTouchHelper.HOST_ID = -1;
-     *
-     *  @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int mParentVirtualDescendantId = -1;
-
-    // Actions introduced in IceCreamSandwich
-
-    /**
-     * Action that focuses the node.
-     */
-    public static final int ACTION_FOCUS = 0x00000001;
-
-    /**
-     * Action that unfocuses the node.
-     */
-    public static final int ACTION_CLEAR_FOCUS = 0x00000002;
-
-    /**
-     * Action that selects the node.
-     */
-    public static final int ACTION_SELECT = 0x00000004;
-
-    /**
-     * Action that unselects the node.
-     */
-    public static final int ACTION_CLEAR_SELECTION = 0x00000008;
-
-    /**
-     * Action that clicks on the node info.
-     */
-    public static final int ACTION_CLICK = 0x00000010;
-
-    /**
-     * Action that long clicks on the node.
-     */
-    public static final int ACTION_LONG_CLICK = 0x00000020;
-
-    // Actions introduced in JellyBean
-
-    /**
-     * Action that gives accessibility focus to the node.
-     */
-    public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
-
-    /**
-     * Action that clears accessibility focus of the node.
-     */
-    public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
-
-    /**
-     * Action that requests to go to the next entity in this node's text
-     * at a given movement granularity. For example, move to the next character,
-     * word, etc.
-     * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
-     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
-     * <strong>Example:</strong> Move to the previous character and do not extend selection.
-     * <code><pre><p>
-     *   Bundle arguments = new Bundle();
-     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
-     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
-     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
-     *           false);
-     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
-     * </code></pre></p>
-     * </p>
-     *
-     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-     *
-     * @see #setMovementGranularities(int)
-     * @see #getMovementGranularities()
-     *
-     * @see #MOVEMENT_GRANULARITY_CHARACTER
-     * @see #MOVEMENT_GRANULARITY_WORD
-     * @see #MOVEMENT_GRANULARITY_LINE
-     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
-     * @see #MOVEMENT_GRANULARITY_PAGE
-     */
-    public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
-
-    /**
-     * Action that requests to go to the previous entity in this node's text
-     * at a given movement granularity. For example, move to the next character,
-     * word, etc.
-     * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
-     * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
-     * <strong>Example:</strong> Move to the next character and do not extend selection.
-     * <code><pre><p>
-     *   Bundle arguments = new Bundle();
-     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
-     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
-     *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
-     *           false);
-     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
-     *           arguments);
-     * </code></pre></p>
-     * </p>
-     *
-     * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
-     * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
-     *
-     * @see #setMovementGranularities(int)
-     * @see #getMovementGranularities()
-     *
-     * @see #MOVEMENT_GRANULARITY_CHARACTER
-     * @see #MOVEMENT_GRANULARITY_WORD
-     * @see #MOVEMENT_GRANULARITY_LINE
-     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
-     * @see #MOVEMENT_GRANULARITY_PAGE
-     */
-    public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
-
-    /**
-     * Action to move to the next HTML element of a given type. For example, move
-     * to the BUTTON, INPUT, TABLE, etc.
-     * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
-     * <strong>Example:</strong>
-     * <code><pre><p>
-     *   Bundle arguments = new Bundle();
-     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
-     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
-     * </code></pre></p>
-     * </p>
-     */
-    public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
-
-    /**
-     * Action to move to the previous HTML element of a given type. For example, move
-     * to the BUTTON, INPUT, TABLE, etc.
-     * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
-     * <strong>Example:</strong>
-     * <code><pre><p>
-     *   Bundle arguments = new Bundle();
-     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
-     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
-     * </code></pre></p>
-     * </p>
-     */
-    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
-
-    /**
-     * Action to scroll the node content forward.
-     */
-    public static final int ACTION_SCROLL_FORWARD = 0x00001000;
-
-    /**
-     * Action to scroll the node content backward.
-     */
-    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
-
-    // Actions introduced in JellyBeanMr2
-
-    /**
-     * Action to copy the current selection to the clipboard.
-     */
-    public static final int ACTION_COPY = 0x00004000;
-
-    /**
-     * Action to paste the current clipboard content.
-     */
-    public static final int ACTION_PASTE = 0x00008000;
-
-    /**
-     * Action to cut the current selection and place it to the clipboard.
-     */
-    public static final int ACTION_CUT = 0x00010000;
-
-    /**
-     * Action to set the selection. Performing this action with no arguments
-     * clears the selection.
-     * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
-     * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
-     * <strong>Example:</strong>
-     * <code><pre><p>
-     *   Bundle arguments = new Bundle();
-     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
-     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
-     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
-     * </code></pre></p>
-     * </p>
-     *
-     * @see #ACTION_ARGUMENT_SELECTION_START_INT
-     * @see #ACTION_ARGUMENT_SELECTION_END_INT
-     */
-    public static final int ACTION_SET_SELECTION = 0x00020000;
-
-    /**
-     * Action to expand an expandable node.
-     */
-    public static final int ACTION_EXPAND = 0x00040000;
-
-    /**
-     * Action to collapse an expandable node.
-     */
-    public static final int ACTION_COLLAPSE = 0x00080000;
-
-    /**
-     * Action to dismiss a dismissable node.
-     */
-    public static final int ACTION_DISMISS = 0x00100000;
-
-    /**
-     * Action that sets the text of the node. Performing the action without argument, using <code>
-     * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
-     * cursor at the end of text.
-     * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
-     * <strong>Example:</strong>
-     * <code><pre><p>
-     *   Bundle arguments = new Bundle();
-     *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
-     *       "android");
-     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
-     * </code></pre></p>
-     */
-    public static final int ACTION_SET_TEXT = 0x00200000;
-
-    // Action arguments
-
-    /**
-     * Argument for which movement granularity to be used when traversing the node text.
-     * <p>
-     * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
-     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
-     * </p>
-     */
-    public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
-        "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
-
-    /**
-     * Argument for which HTML element to get moving to the next/previous HTML element.
-     * <p>
-     * <strong>Type:</strong> String<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
-     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
-     * </p>
-     */
-    public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
-        "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
-
-    /**
-     * Argument for whether when moving at granularity to extend the selection
-     * or to move it otherwise.
-     * <p>
-     * <strong>Type:</strong> boolean<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
-     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
-     * </p>
-     *
-     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
-     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
-     */
-    public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
-            "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
-
-    /**
-     * Argument for specifying the selection start.
-     * <p>
-     * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
-     * </p>
-     *
-     * @see #ACTION_SET_SELECTION
-     */
-    public static final String ACTION_ARGUMENT_SELECTION_START_INT =
-            "ACTION_ARGUMENT_SELECTION_START_INT";
-
-    /**
-     * Argument for specifying the selection end.
-     * <p>
-     * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
-     * </p>
-     *
-     * @see #ACTION_SET_SELECTION
-     */
-    public static final String ACTION_ARGUMENT_SELECTION_END_INT =
-            "ACTION_ARGUMENT_SELECTION_END_INT";
-
-    /**
-     * Argument for specifying the text content to set
-     * <p>
-     * <strong>Type:</strong> CharSequence<br>
-     * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
-     * </p>
-     *
-     * @see #ACTION_SET_TEXT
-     */
-    public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
-            "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
-
-    /**
-     * Argument for specifying the collection row to make visible on screen.
-     * <p>
-     * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong>
-     * <ul>
-     *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
-     * </ul>
-     *
-     * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
-     */
-    public static final String ACTION_ARGUMENT_ROW_INT =
-            "android.view.accessibility.action.ARGUMENT_ROW_INT";
-
-    /**
-     * Argument for specifying the collection column to make visible on screen.
-     * <p>
-     * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong>
-     * <ul>
-     *     <li>{@link AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION}</li>
-     * </ul>
-     *
-     * @see AccessibilityActionCompat#ACTION_SCROLL_TO_POSITION
-     */
-    public static final String ACTION_ARGUMENT_COLUMN_INT =
-            "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
-
-    /**
-     * Argument for specifying the progress value to set.
-     * <p>
-     * <strong>Type:</strong> float<br>
-     * <strong>Actions:</strong>
-     * <ul>
-     *     <li>{@link AccessibilityActionCompat#ACTION_SET_PROGRESS}</li>
-     * </ul>
-     *
-     * @see AccessibilityActionCompat#ACTION_SET_PROGRESS
-     */
-    public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
-            "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
-
-    // Focus types
-
-    /**
-     * The input focus.
-     */
-    public static final int FOCUS_INPUT = 1;
-
-    /**
-     * The accessibility focus.
-     */
-    public static final int FOCUS_ACCESSIBILITY = 2;
-
-    // Movement granularities
-
-    /**
-     * Movement granularity bit for traversing the text of a node by character.
-     */
-    public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
-
-    /**
-     * Movement granularity bit for traversing the text of a node by word.
-     */
-    public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
-
-    /**
-     * Movement granularity bit for traversing the text of a node by line.
-     */
-    public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
-
-    /**
-     * Movement granularity bit for traversing the text of a node by paragraph.
-     */
-    public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
-
-    /**
-     * Movement granularity bit for traversing the text of a node by page.
-     */
-    public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
-
-    /**
-     * Creates a wrapper for info implementation.
-     *
-     * @param object The info to wrap.
-     * @return A wrapper for if the object is not null, null otherwise.
-     */
-    static AccessibilityNodeInfoCompat wrapNonNullInstance(Object object) {
-        if (object != null) {
-            return new AccessibilityNodeInfoCompat(object);
-        }
-        return null;
-    }
-
-    /**
-     * Creates a new instance wrapping an
-     * {@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;
-    }
-
-    /**
-     * 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;
-    }
-
-    /**
-     * Returns a cached instance if such is available otherwise a new one and
-     * sets the source.
-     *
-     * @return An instance.
-     * @see #setSource(View)
-     */
-    public static AccessibilityNodeInfoCompat obtain(View source) {
-        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(source));
-    }
-
-    /**
-     * Returns a cached instance if such is available otherwise a new one
-     * and sets the source.
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual descendant.
-     * @return An instance.
-     *
-     * @see #setSource(View, int)
-     */
-    public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
-                    AccessibilityNodeInfo.obtain(root, virtualDescendantId));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns a cached instance if such is available otherwise a new one.
-     *
-     * @return An instance.
-     */
-    public static AccessibilityNodeInfoCompat obtain() {
-        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain());
-    }
-
-    /**
-     * Returns a cached instance if such is available or a new one is create.
-     * The returned instance is initialized from the given <code>info</code>.
-     *
-     * @param info The other info.
-     * @return An instance.
-     */
-    public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {
-        return AccessibilityNodeInfoCompat.wrap(AccessibilityNodeInfo.obtain(info.mInfo));
-    }
-
-    /**
-     * Sets the source.
-     *
-     * @param source The info source.
-     */
-    public void setSource(View source) {
-        mInfo.setSource(source);
-    }
-
-    /**
-     * Sets the source to be a virtual descendant of the given <code>root</code>.
-     * If <code>virtualDescendantId</code> is {@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 themselves as a tree of virtual views, thus conveying their
-     * logical structure.
-     * </p>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    public void setSource(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            mInfo.setSource(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Find the view that has the specified focus type. The search starts from
-     * the view represented by this node info.
-     *
-     * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
-     *         {@link #FOCUS_ACCESSIBILITY}.
-     * @return The node info of the focused view or null.
-     *
-     * @see #FOCUS_INPUT
-     * @see #FOCUS_ACCESSIBILITY
-     */
-    public AccessibilityNodeInfoCompat findFocus(int focus) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.findFocus(focus));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Searches for the nearest view in the specified direction that can take
-     * input focus.
-     *
-     * @param direction The direction. Can be one of:
-     *     {@link View#FOCUS_DOWN},
-     *     {@link View#FOCUS_UP},
-     *     {@link View#FOCUS_LEFT},
-     *     {@link View#FOCUS_RIGHT},
-     *     {@link View#FOCUS_FORWARD},
-     *     {@link View#FOCUS_BACKWARD}.
-     *
-     * @return The node info for the view that can take accessibility focus.
-     */
-    public AccessibilityNodeInfoCompat focusSearch(int direction) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.focusSearch(direction));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the id of the window from which the info comes from.
-     *
-     * @return The window id.
-     */
-    public int getWindowId() {
-        return mInfo.getWindowId();
-    }
-
-    /**
-     * Gets the number of children.
-     *
-     * @return The child count.
-     */
-    public int getChildCount() {
-        return mInfo.getChildCount();
-    }
-
-    /**
-     * Get the child at given index.
-     * <p>
-     * <strong>Note:</strong> It is a client responsibility to recycle the
-     * received info by calling {@link AccessibilityNodeInfoCompat#recycle()} to
-     * avoid creating of multiple instances.
-     * </p>
-     *
-     * @param index The child index.
-     * @return The child node.
-     * @throws IllegalStateException If called outside of an
-     *             AccessibilityService.
-     */
-    public AccessibilityNodeInfoCompat getChild(int index) {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getChild(index));
-    }
-
-    /**
-     * Adds a child.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param child The child.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void addChild(View child) {
-        mInfo.addChild(child);
-    }
-
-    /**
-     * Adds a virtual child which is a descendant of the given <code>root</code>.
-     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
-     * is added as a child.
-     * <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 root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual child.
-     */
-    public void addChild(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            mInfo.addChild(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Removes a child. If the child was not previously added to the node,
-     * calling this method has no effect.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}.
-     * This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param child The child.
-     * @return true if the child was present
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public boolean removeChild(View child) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return mInfo.removeChild(child);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Removes a virtual child which is a descendant of the given
-     * <code>root</code>. If the child was not previously added to the node,
-     * calling this method has no effect.
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual child.
-     * @return true if the child was present
-     * @see #addChild(View, int)
-     */
-    public boolean removeChild(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return mInfo.removeChild(root, virtualDescendantId);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Gets the actions that can be performed on the node.
-     *
-     * @return The bit mask of with actions.
-     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_FOCUS
-     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
-     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_SELECT
-     * @see android.view.accessibility.AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
-     */
-    public int getActions() {
-        return mInfo.getActions();
-    }
-
-    /**
-     * Adds an action that can be performed on the node.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param action The action.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void addAction(int action) {
-        mInfo.addAction(action);
-    }
-
-    /**
-     * Adds an action that can be performed on the node.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param action The action.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void addAction(AccessibilityActionCompat action) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            mInfo.addAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
-        }
-    }
-
-    /**
-     * Removes an action that can be performed on the node. If the action was
-     * not already added to the node, calling this method has no effect.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param action The action to be removed.
-     * @return The action removed from the list of actions.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public boolean removeAction(AccessibilityActionCompat action) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return mInfo.removeAction((AccessibilityNodeInfo.AccessibilityAction) action.mAction);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Performs an action on the node.
-     * <p>
-     * <strong>Note:</strong> An action can be performed only if the request is
-     * made from an {@link android.accessibilityservice.AccessibilityService}.
-     * </p>
-     *
-     * @param action The action to perform.
-     * @return True if the action was performed.
-     * @throws IllegalStateException If called outside of an
-     *             AccessibilityService.
-     */
-    public boolean performAction(int action) {
-        return mInfo.performAction(action);
-    }
-
-    /**
-     * Performs an action on the node.
-     * <p>
-     *   <strong>Note:</strong> An action can be performed only if the request is made
-     *   from an {@link android.accessibilityservice.AccessibilityService}.
-     * </p>
-     *
-     * @param action The action to perform.
-     * @param arguments A bundle with additional arguments.
-     * @return True if the action was performed.
-     *
-     * @throws IllegalStateException If called outside of an AccessibilityService.
-     */
-    public boolean performAction(int action, Bundle arguments) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return mInfo.performAction(action, arguments);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets the movement granularities for traversing the text of this node.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param granularities The bit mask with granularities.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setMovementGranularities(int granularities) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            mInfo.setMovementGranularities(granularities);
-        }
-    }
-
-    /**
-     * Gets the movement granularities for traversing the text of this node.
-     *
-     * @return The bit mask with granularities.
-     */
-    public int getMovementGranularities() {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return mInfo.getMovementGranularities();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by text. The match
-     * is case insensitive containment. The search is relative to this info i.e. this
-     * info is the root of the traversed tree.
-     * <p>
-     * <strong>Note:</strong> It is a client responsibility to recycle the
-     * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
-     * to avoid creating of multiple instances.
-     * </p>
-     *
-     * @param text The searched text.
-     * @return A list of node info.
-     */
-    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {
-        List<AccessibilityNodeInfoCompat> result = new ArrayList<AccessibilityNodeInfoCompat>();
-        List<AccessibilityNodeInfo> infos = mInfo.findAccessibilityNodeInfosByText(text);
-        final int infoCount = infos.size();
-        for (int i = 0; i < infoCount; i++) {
-            AccessibilityNodeInfo info = infos.get(i);
-            result.add(AccessibilityNodeInfoCompat.wrap(info));
-        }
-        return result;
-    }
-
-    /**
-     * Gets the parent.
-     * <p>
-     * <strong>Note:</strong> It is a client responsibility to recycle the
-     * received info by calling {@link android.view.accessibility.AccessibilityNodeInfo#recycle()}
-     * to avoid creating of multiple instances.
-     * </p>
-     *
-     * @return The parent.
-     */
-    public AccessibilityNodeInfoCompat getParent() {
-        return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getParent());
-    }
-
-    /**
-     * Sets the parent.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param parent The parent.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setParent(View parent) {
-        mInfo.setParent(parent);
-    }
-
-    /**
-     * Sets the parent 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 parent.
-     * <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>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    public void setParent(View root, int virtualDescendantId) {
-        mParentVirtualDescendantId = virtualDescendantId;
-        if (Build.VERSION.SDK_INT >= 16) {
-            mInfo.setParent(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Gets the node bounds in parent coordinates.
-     *
-     * @param outBounds The output node bounds.
-     */
-    public void getBoundsInParent(Rect outBounds) {
-        mInfo.getBoundsInParent(outBounds);
-    }
-
-    /**
-     * Sets the node bounds in parent coordinates.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param bounds The node bounds.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setBoundsInParent(Rect bounds) {
-        mInfo.setBoundsInParent(bounds);
-    }
-
-    /**
-     * Gets the node bounds in screen coordinates.
-     *
-     * @param outBounds The output node bounds.
-     */
-    public void getBoundsInScreen(Rect outBounds) {
-        mInfo.getBoundsInScreen(outBounds);
-    }
-
-    /**
-     * Sets the node bounds in screen coordinates.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param bounds The node bounds.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setBoundsInScreen(Rect bounds) {
-        mInfo.setBoundsInScreen(bounds);
-    }
-
-    /**
-     * Gets whether this node is checkable.
-     *
-     * @return True if the node is checkable.
-     */
-    public boolean isCheckable() {
-        return mInfo.isCheckable();
-    }
-
-    /**
-     * Sets whether this node is checkable.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param checkable True if the node is checkable.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setCheckable(boolean checkable) {
-        mInfo.setCheckable(checkable);
-    }
-
-    /**
-     * Gets whether this node is checked.
-     *
-     * @return True if the node is checked.
-     */
-    public boolean isChecked() {
-        return mInfo.isChecked();
-    }
-
-    /**
-     * Sets whether this node is checked.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param checked True if the node is checked.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setChecked(boolean checked) {
-        mInfo.setChecked(checked);
-    }
-
-    /**
-     * Gets whether this node is focusable.
-     *
-     * @return True if the node is focusable.
-     */
-    public boolean isFocusable() {
-        return mInfo.isFocusable();
-    }
-
-    /**
-     * Sets whether this node is focusable.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param focusable True if the node is focusable.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setFocusable(boolean focusable) {
-        mInfo.setFocusable(focusable);
-    }
-
-    /**
-     * Gets whether this node is focused.
-     *
-     * @return True if the node is focused.
-     */
-    public boolean isFocused() {
-        return mInfo.isFocused();
-    }
-
-    /**
-     * Sets whether this node is focused.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param focused True if the node is focused.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setFocused(boolean focused) {
-        mInfo.setFocused(focused);
-    }
-
-    /**
-     * Gets whether this node is visible to the user.
-     *
-     * @return Whether the node is visible to the user.
-     */
-    public boolean isVisibleToUser() {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return mInfo.isVisibleToUser();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets whether this node is visible to the user.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param visibleToUser Whether the node is visible to the user.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setVisibleToUser(boolean visibleToUser) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            mInfo.setVisibleToUser(visibleToUser);
-        }
-    }
-
-    /**
-     * Gets whether this node is accessibility focused.
-     *
-     * @return True if the node is accessibility focused.
-     */
-    public boolean isAccessibilityFocused() {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return mInfo.isAccessibilityFocused();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets whether this node is accessibility focused.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param focused True if the node is accessibility focused.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setAccessibilityFocused(boolean focused) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            mInfo.setAccessibilityFocused(focused);
-        }
-    }
-
-    /**
-     * Gets whether this node is selected.
-     *
-     * @return True if the node is selected.
-     */
-    public boolean isSelected() {
-        return mInfo.isSelected();
-    }
-
-    /**
-     * Sets whether this node is selected.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param selected True if the node is selected.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setSelected(boolean selected) {
-        mInfo.setSelected(selected);
-    }
-
-    /**
-     * Gets whether this node is clickable.
-     *
-     * @return True if the node is clickable.
-     */
-    public boolean isClickable() {
-        return mInfo.isClickable();
-    }
-
-    /**
-     * Sets whether this node is clickable.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param clickable True if the node is clickable.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setClickable(boolean clickable) {
-        mInfo.setClickable(clickable);
-    }
-
-    /**
-     * Gets whether this node is long clickable.
-     *
-     * @return True if the node is long clickable.
-     */
-    public boolean isLongClickable() {
-        return mInfo.isLongClickable();
-    }
-
-    /**
-     * Sets whether this node is long clickable.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param longClickable True if the node is long clickable.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setLongClickable(boolean longClickable) {
-        mInfo.setLongClickable(longClickable);
-    }
-
-    /**
-     * Gets whether this node is enabled.
-     *
-     * @return True if the node is enabled.
-     */
-    public boolean isEnabled() {
-        return mInfo.isEnabled();
-    }
-
-    /**
-     * Sets whether this node is enabled.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param enabled True if the node is enabled.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setEnabled(boolean enabled) {
-        mInfo.setEnabled(enabled);
-    }
-
-    /**
-     * Gets whether this node is a password.
-     *
-     * @return True if the node is a password.
-     */
-    public boolean isPassword() {
-        return mInfo.isPassword();
-    }
-
-    /**
-     * Sets whether this node is a password.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param password True if the node is a password.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setPassword(boolean password) {
-        mInfo.setPassword(password);
-    }
-
-    /**
-     * Gets if the node is scrollable.
-     *
-     * @return True if the node is scrollable, false otherwise.
-     */
-    public boolean isScrollable() {
-        return mInfo.isScrollable();
-    }
-
-    /**
-     * Sets if the node is scrollable.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param scrollable True if the node is scrollable, false otherwise.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setScrollable(boolean scrollable) {
-        mInfo.setScrollable(scrollable);
-    }
-
-    /**
-     * Returns whether the node originates from a view considered important for accessibility.
-     *
-     * @return {@code true} if the node originates from a view considered important for
-     *         accessibility, {@code false} otherwise
-     *
-     * @see View#isImportantForAccessibility()
-     */
-    public boolean isImportantForAccessibility() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return mInfo.isImportantForAccessibility();
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Sets whether the node is considered important for accessibility.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param important {@code true} if the node is considered important for accessibility,
-     *                  {@code false} otherwise
-     */
-    public void setImportantForAccessibility(boolean important) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            mInfo.setImportantForAccessibility(important);
-        }
-    }
-
-    /**
-     * Gets the package this node comes from.
-     *
-     * @return The package name.
-     */
-    public CharSequence getPackageName() {
-        return mInfo.getPackageName();
-    }
-
-    /**
-     * Sets the package this node comes from.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param packageName The package name.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setPackageName(CharSequence packageName) {
-        mInfo.setPackageName(packageName);
-    }
-
-    /**
-     * Gets the class this node comes from.
-     *
-     * @return The class name.
-     */
-    public CharSequence getClassName() {
-        return mInfo.getClassName();
-    }
-
-    /**
-     * Sets the class this node comes from.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param className The class name.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setClassName(CharSequence className) {
-        mInfo.setClassName(className);
-    }
-
-    /**
-     * Gets the text of this node.
-     *
-     * @return The text.
-     */
-    public CharSequence getText() {
-        return mInfo.getText();
-    }
-
-    /**
-     * Sets the text of this node.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param text The text.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setText(CharSequence text) {
-        mInfo.setText(text);
-    }
-
-    /**
-     * Gets the content description of this node.
-     *
-     * @return The content description.
-     */
-    public CharSequence getContentDescription() {
-        return mInfo.getContentDescription();
-    }
-
-    /**
-     * Sets the content description of this node.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param contentDescription The content description.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setContentDescription(CharSequence contentDescription) {
-        mInfo.setContentDescription(contentDescription);
-    }
-
-    /**
-     * Return an instance back to be reused.
-     * <p>
-     * <strong>Note:</strong> You must not touch the object after calling this function.
-     *
-     * @throws IllegalStateException If the info is already recycled.
-     */
-    public void recycle() {
-        mInfo.recycle();
-    }
-
-    /**
-     * Sets the fully qualified resource name of the source view's id.
-     *
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param viewId The id resource name.
-     */
-    public void setViewIdResourceName(String viewId) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            mInfo.setViewIdResourceName(viewId);
-        }
-    }
-
-    /**
-     * Gets the fully qualified resource name of the source view's id.
-     *
-     * <p>
-     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
-     *   and in order to report the source view id of an {@link AccessibilityNodeInfoCompat}
-     *   the client has to set the {@link AccessibilityServiceInfoCompat#FLAG_REPORT_VIEW_IDS}
-     *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
-     * </p>
-     *
-     * @return The id resource name.
-     */
-    public String getViewIdResourceName() {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return mInfo.getViewIdResourceName();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the node's live region mode.
-     * <p>
-     * A live region is a node that contains information that is important for
-     * the user and when it changes the user should be notified. For example,
-     * in a login screen with a TextView that displays an "incorrect password"
-     * notification, that view should be marked as a live region with mode
-     * {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_POLITE}.
-     * <p>
-     * It is the responsibility of the accessibility service to monitor
-     * {@link AccessibilityEventCompat#TYPE_WINDOW_CONTENT_CHANGED} events
-     * indicating changes to live region nodes and their children.
-     *
-     * @return The live region mode, or
-     *         {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
-     *         not a live region.
-     * @see ViewCompat#getAccessibilityLiveRegion(View)
-     */
-    public int getLiveRegion() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.getLiveRegion();
-        } else {
-            return ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE;
-        }
-    }
-
-    /**
-     * Sets the node's live region mode.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is
-     * made immutable before being delivered to an AccessibilityService.
-     *
-     * @param mode The live region mode, or
-     *        {@link ViewCompat#ACCESSIBILITY_LIVE_REGION_NONE} if the view is
-     *        not a live region.
-     * @see ViewCompat#setAccessibilityLiveRegion(View, int)
-     */
-    public void setLiveRegion(int mode) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setLiveRegion(mode);
-        }
-    }
-
-    /**
-     * Get the drawing order of the view corresponding it this node.
-     * <p>
-     * Drawing order is determined only within the node's parent, so this index is only relative
-     * to its siblings.
-     * <p>
-     * In some cases, the drawing order is essentially simultaneous, so it is possible for two
-     * siblings to return the same value. It is also possible that values will be skipped.
-     *
-     * @return The drawing position of the view corresponding to this node relative to its siblings.
-     */
-    public int getDrawingOrder() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return mInfo.getDrawingOrder();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Set the drawing order of the view corresponding it this node.
-     *
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     * @param drawingOrderInParent
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setDrawingOrder(int drawingOrderInParent) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            mInfo.setDrawingOrder(drawingOrderInParent);
-        }
-    }
-
-    /**
-     * Gets the collection info if the node is a collection. A collection
-     * child is always a collection item.
-     *
-     * @return The collection info.
-     */
-    public CollectionInfoCompat getCollectionInfo() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            AccessibilityNodeInfo.CollectionInfo info = mInfo.getCollectionInfo();
-            if (info != null) {
-                return new CollectionInfoCompat(info);
-            }
-        }
-        return null;
-    }
-
-    public void setCollectionInfo(Object collectionInfo) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setCollectionInfo((AccessibilityNodeInfo.CollectionInfo)
-                    ((CollectionInfoCompat) collectionInfo).mInfo);
-        }
-
-    }
-
-    public void setCollectionItemInfo(Object collectionItemInfo) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setCollectionItemInfo((AccessibilityNodeInfo.CollectionItemInfo)
-                    ((CollectionItemInfoCompat) collectionItemInfo).mInfo);
-        }
-    }
-
-    /**
-     * Gets the collection item info if the node is a collection item. A collection
-     * item is always a child of a collection.
-     *
-     * @return The collection item info.
-     */
-    public CollectionItemInfoCompat getCollectionItemInfo() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            AccessibilityNodeInfo.CollectionItemInfo info = mInfo.getCollectionItemInfo();
-            if (info != null) {
-                return new CollectionItemInfoCompat(info);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Gets the range info if this node is a range.
-     *
-     * @return The range.
-     */
-    public RangeInfoCompat getRangeInfo() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            AccessibilityNodeInfo.RangeInfo info = mInfo.getRangeInfo();
-            if (info != null) {
-                return new RangeInfoCompat(info);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Sets the range info if this node is a range.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param rangeInfo The range info.
-     */
-    public void setRangeInfo(RangeInfoCompat rangeInfo) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setRangeInfo((AccessibilityNodeInfo.RangeInfo) rangeInfo.mInfo);
-        }
-    }
-
-    /**
-     * Gets the actions that can be performed on the node.
-     *
-     * @return A list of AccessibilityActions.
-     */
-    @SuppressWarnings("unchecked")
-    public List<AccessibilityActionCompat> getActionList() {
-        List<Object> actions = null;
-        if (Build.VERSION.SDK_INT >= 21) {
-            actions = (List<Object>) (List<?>) mInfo.getActionList();
-        }
-        if (actions != null) {
-            List<AccessibilityActionCompat> result = new ArrayList<AccessibilityActionCompat>();
-            final int actionCount = actions.size();
-            for (int i = 0; i < actionCount; i++) {
-                Object action = actions.get(i);
-                result.add(new AccessibilityActionCompat(action));
-            }
-            return result;
-        } else {
-            return Collections.<AccessibilityActionCompat>emptyList();
-        }
-    }
-
-    /**
-     * Sets if the content of this node is invalid. For example,
-     * a date is not well-formed.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param contentInvalid If the node content is invalid.
-     */
-    public void setContentInvalid(boolean contentInvalid) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setContentInvalid(contentInvalid);
-        }
-    }
-
-    /**
-     * Gets if the content of this node is invalid. For example,
-     * a date is not well-formed.
-     *
-     * @return If the node content is invalid.
-     */
-    public boolean isContentInvalid() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.isContentInvalid();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Gets whether this node is context clickable.
-     *
-     * @return True if the node is context clickable.
-     */
-    public boolean isContextClickable() {
-        if (Build.VERSION.SDK_INT >= 23) {
-            return mInfo.isContextClickable();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets whether this node is context clickable.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
-     * before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param contextClickable True if the node is context clickable.
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setContextClickable(boolean contextClickable) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            mInfo.setContextClickable(contextClickable);
-        }
-    }
-
-    /**
-     * Sets the error text of this node.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param error The error text.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setError(CharSequence error) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            mInfo.setError(error);
-        }
-    }
-
-    /**
-     * Gets the error text of this node.
-     *
-     * @return The error text.
-     */
-    public CharSequence getError() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return mInfo.getError();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Sets the view for which the view represented by this info serves as a
-     * label for accessibility purposes.
-     *
-     * @param labeled The view for which this info serves as a label.
-     */
-    public void setLabelFor(View labeled) {
-        if (Build.VERSION.SDK_INT >= 17) {
-            mInfo.setLabelFor(labeled);
-        }
-    }
-
-    /**
-     * Sets the view for which the view represented by this info serves as a
-     * label for accessibility purposes. If <code>virtualDescendantId</code>
-     * is {@link View#NO_ID} the root is set as the labeled.
-     * <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 themselves as a tree of virtual views, thus conveying their
-     * logical structure.
-     * </p>
-     *
-     * @param root The root whose virtual descendant serves as a label.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    public void setLabelFor(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 17) {
-            mInfo.setLabelFor(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Gets the node info for which the view represented by this info serves as
-     * a label for accessibility purposes.
-     * <p>
-     *   <strong>Note:</strong> It is a client responsibility to recycle the
-     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
-     *     to avoid creating of multiple instances.
-     * </p>
-     *
-     * @return The labeled info.
-     */
-    public AccessibilityNodeInfoCompat getLabelFor() {
-        if (Build.VERSION.SDK_INT >= 17) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabelFor());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Sets the view which serves as the label of the view represented by
-     * this info for accessibility purposes.
-     *
-     * @param label The view that labels this node's source.
-     */
-    public void setLabeledBy(View label) {
-        if (Build.VERSION.SDK_INT >= 17) {
-            mInfo.setLabeledBy(label);
-        }
-    }
-
-    /**
-     * Sets the view which serves as the label of the view represented by
-     * this info for accessibility purposes. If <code>virtualDescendantId</code>
-     * is {@link View#NO_ID} the root is set as the label.
-     * <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 themselves as a tree of virtual views, thus conveying their
-     * logical structure.
-     * </p>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param root The root whose virtual descendant labels this node's source.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    public void setLabeledBy(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 17) {
-            mInfo.setLabeledBy(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Gets the node info which serves as the label of the view represented by
-     * this info for accessibility purposes.
-     * <p>
-     *   <strong>Note:</strong> It is a client responsibility to recycle the
-     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
-     *     to avoid creating of multiple instances.
-     * </p>
-     *
-     * @return The label.
-     */
-    public AccessibilityNodeInfoCompat getLabeledBy() {
-        if (Build.VERSION.SDK_INT >= 17) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getLabeledBy());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets if this node opens a popup or a dialog.
-     *
-     * @return If the the node opens a popup.
-     */
-    public boolean canOpenPopup() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.canOpenPopup();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets if this node opens a popup or a dialog.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param opensPopup If the the node opens a popup.
-     */
-    public void setCanOpenPopup(boolean opensPopup) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setCanOpenPopup(opensPopup);
-        }
-    }
-
-    /**
-     * Finds {@link AccessibilityNodeInfoCompat}s by the fully qualified view id's resource
-     * name where a fully qualified id is of the from "package:id/id_resource_name".
-     * For example, if the target application's package is "foo.bar" and the id
-     * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
-     *
-     * <p>
-     *   <strong>Note:</strong> It is a client responsibility to recycle the
-     *     received info by calling {@link AccessibilityNodeInfoCompat#recycle()}
-     *     to avoid creating of multiple instances.
-     * </p>
-     * <p>
-     *   <strong>Note:</strong> The primary usage of this API is for UI test automation
-     *   and in order to report the fully qualified view id if an
-     *   {@link AccessibilityNodeInfoCompat} the client has to set the
-     *   {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
-     *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
-     * </p>
-     *
-     * @param viewId The fully qualified resource name of the view id to find.
-     * @return A list of node info.
-     */
-    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            List<AccessibilityNodeInfo> nodes = mInfo.findAccessibilityNodeInfosByViewId(viewId);
-            List<AccessibilityNodeInfoCompat> result = new ArrayList<>();
-            for (AccessibilityNodeInfo node : nodes) {
-                result.add(AccessibilityNodeInfoCompat.wrap(node));
-            }
-            return result;
-        } else {
-            return Collections.emptyList();
-        }
-    }
-
-    /**
-     * Gets an optional bundle with extra data. The bundle
-     * is lazily created and never <code>null</code>.
-     * <p>
-     * <strong>Note:</strong> It is recommended to use the package
-     * name of your application as a prefix for the keys to avoid
-     * collisions which may confuse an accessibility service if the
-     * same key has different meaning when emitted from different
-     * applications.
-     * </p>
-     *
-     * @return The bundle.
-     */
-    public Bundle getExtras() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.getExtras();
-        } else {
-            return new Bundle();
-        }
-    }
-
-    /**
-     * Gets the input type of the source as defined by {@link InputType}.
-     *
-     * @return The input type.
-     */
-    public int getInputType() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.getInputType();
-        } else {
-            return InputType.TYPE_NULL;
-        }
-    }
-
-    /**
-     * Sets the input type of the source as defined by {@link InputType}.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an
-     *   AccessibilityService.
-     * </p>
-     *
-     * @param inputType The input type.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setInputType(int inputType) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setInputType(inputType);
-        }
-    }
-
-    /**
-     * Sets the maximum text length, or -1 for no limit.
-     * <p>
-     * Typically used to indicate that an editable text field has a limit on
-     * the number of characters entered.
-     * <p>
-     * <strong>Note:</strong> Cannot be called from an
-     * {@link android.accessibilityservice.AccessibilityService}.
-     * This class is made immutable before being delivered to an AccessibilityService.
-     *
-     * @param max The maximum text length.
-     * @see #getMaxTextLength()
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setMaxTextLength(int max) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            mInfo.setMaxTextLength(max);
-        }
-    }
-
-    /**
-     * Returns the maximum text length for this node.
-     *
-     * @return The maximum text length, or -1 for no limit.
-     * @see #setMaxTextLength(int)
-     */
-    public int getMaxTextLength() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return mInfo.getMaxTextLength();
-        } else {
-            return -1;
-        }
-    }
-
-    /**
-     * Sets the text selection start and end.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param start The text selection start.
-     * @param end The text selection end.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setTextSelection(int start, int end) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            mInfo.setTextSelection(start, end);
-        }
-    }
-
-    /**
-     * Gets the text selection start.
-     *
-     * @return The text selection start if there is selection or -1.
-     */
-    public int getTextSelectionStart() {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return mInfo.getTextSelectionStart();
-        } else {
-            return -1;
-        }
-    }
-
-    /**
-     * Gets the text selection end.
-     *
-     * @return The text selection end if there is selection or -1.
-     */
-    public int getTextSelectionEnd() {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return mInfo.getTextSelectionEnd();
-        } else {
-            return -1;
-        }
-    }
-
-    /**
-     * Gets the node before which this one is visited during traversal. A screen-reader
-     * must visit the content of this node before the content of the one it precedes.
-     *
-     * @return The succeeding node if such or <code>null</code>.
-     *
-     * @see #setTraversalBefore(android.view.View)
-     * @see #setTraversalBefore(android.view.View, int)
-     */
-    public AccessibilityNodeInfoCompat getTraversalBefore() {
-        if (Build.VERSION.SDK_INT >= 22) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalBefore());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Sets the view before whose node this one should be visited during traversal. A
-     * screen-reader must visit the content of this node before the content of the one
-     * it precedes.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param view The view providing the preceding node.
-     *
-     * @see #getTraversalBefore()
-     */
-    public void setTraversalBefore(View view) {
-        if (Build.VERSION.SDK_INT >= 22) {
-            mInfo.setTraversalBefore(view);
-        }
-    }
-
-    /**
-     * Sets the node before which this one is visited during traversal. A screen-reader
-     * must visit the content of this node before the content of the one it precedes.
-     * The successor is 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 successor.
-     * <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>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    public void setTraversalBefore(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 22) {
-            mInfo.setTraversalBefore(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Gets the node after which this one is visited in accessibility traversal.
-     * A screen-reader must visit the content of the other node before the content
-     * of this one.
-     *
-     * @return The succeeding node if such or <code>null</code>.
-     *
-     * @see #setTraversalAfter(android.view.View)
-     * @see #setTraversalAfter(android.view.View, int)
-     */
-    public AccessibilityNodeInfoCompat getTraversalAfter() {
-        if (Build.VERSION.SDK_INT >= 22) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(mInfo.getTraversalAfter());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Sets the view whose node is visited after this one in accessibility traversal.
-     * A screen-reader must visit the content of the other node before the content
-     * of this one.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param view The previous view.
-     *
-     * @see #getTraversalAfter()
-     */
-    public void setTraversalAfter(View view) {
-        if (Build.VERSION.SDK_INT >= 22) {
-            mInfo.setTraversalAfter(view);
-        }
-    }
-
-    /**
-     * Sets the node after which this one is visited in accessibility traversal.
-     * A screen-reader must visit the content of the other node before the content
-     * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
-     * the root is set as the predecessor.
-     * <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>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param root The root of the virtual subtree.
-     * @param virtualDescendantId The id of the virtual descendant.
-     */
-    public void setTraversalAfter(View root, int virtualDescendantId) {
-        if (Build.VERSION.SDK_INT >= 22) {
-            mInfo.setTraversalAfter(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Gets the window to which this node belongs.
-     *
-     * @return The window.
-     *
-     * @see android.accessibilityservice.AccessibilityService#getWindows()
-     */
-    public AccessibilityWindowInfoCompat getWindow() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return AccessibilityWindowInfoCompat.wrapNonNullInstance(mInfo.getWindow());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets if the node can be dismissed.
-     *
-     * @return If the node can be dismissed.
-     */
-    public boolean isDismissable() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.isDismissable();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets if the node can be dismissed.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param dismissable If the node can be dismissed.
-     */
-    public void setDismissable(boolean dismissable) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setDismissable(dismissable);
-        }
-    }
-
-    /**
-     * Gets if the node is editable.
-     *
-     * @return True if the node is editable, false otherwise.
-     */
-    public boolean isEditable() {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return mInfo.isEditable();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets whether this node is editable.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param editable True if the node is editable.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setEditable(boolean editable) {
-        if (Build.VERSION.SDK_INT >= 18) {
-            mInfo.setEditable(editable);
-        }
-    }
-
-    /**
-     * Gets if the node is a multi line editable text.
-     *
-     * @return True if the node is multi line.
-     */
-    public boolean isMultiLine() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.isMultiLine();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets if the node is a multi line editable text.
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param multiLine True if the node is multi line.
-     */
-    public void setMultiLine(boolean multiLine) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.setMultiLine(multiLine);
-        }
-    }
-
-    /**
-     * Refreshes this info with the latest state of the view it represents.
-     * <p>
-     * <strong>Note:</strong> If this method returns false this info is obsolete
-     * since it represents a view that is no longer in the view tree and should
-     * be recycled.
-     * </p>
-     * @return Whether the refresh succeeded.
-     */
-    public boolean refresh() {
-        if (Build.VERSION.SDK_INT >= 18) {
-            return mInfo.refresh();
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Gets the custom role description.
-     * @return The role description.
-     */
-    public @Nullable CharSequence getRoleDescription() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return mInfo.getExtras().getCharSequence(ROLE_DESCRIPTION_KEY);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Sets the custom role description.
-     *
-     * <p>
-     *   The role description allows you to customize the name for the view's semantic
-     *   role. For example, if you create a custom subclass of {@link android.view.View}
-     *   to display a menu bar, you could assign it the role description of "menu bar".
-     * </p>
-     * <p>
-     *   <strong>Warning:</strong> For consistency with other applications, you should
-     *   not use the role description to force accessibility services to describe
-     *   standard views (such as buttons or checkboxes) using specific wording. For
-     *   example, you should not set a role description of "check box" or "tick box" for
-     *   a standard {@link android.widget.CheckBox}. Instead let accessibility services
-     *   decide what feedback to provide.
-     * </p>
-     * <p>
-     *   <strong>Note:</strong> Cannot be called from an
-     *   {@link android.accessibilityservice.AccessibilityService}.
-     *   This class is made immutable before being delivered to an AccessibilityService.
-     * </p>
-     *
-     * @param roleDescription The role description.
-     */
-    public void setRoleDescription(@Nullable CharSequence roleDescription) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mInfo.getExtras().putCharSequence(ROLE_DESCRIPTION_KEY, roleDescription);
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        return (mInfo == null) ? 0 : mInfo.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        AccessibilityNodeInfoCompat other = (AccessibilityNodeInfoCompat) obj;
-        if (mInfo == null) {
-            if (other.mInfo != null) {
-                return false;
-            }
-        } else if (!mInfo.equals(other.mInfo)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append(super.toString());
-
-        Rect bounds = new Rect();
-
-        getBoundsInParent(bounds);
-        builder.append("; boundsInParent: " + bounds);
-
-        getBoundsInScreen(bounds);
-        builder.append("; boundsInScreen: " + bounds);
-
-        builder.append("; packageName: ").append(getPackageName());
-        builder.append("; className: ").append(getClassName());
-        builder.append("; text: ").append(getText());
-        builder.append("; contentDescription: ").append(getContentDescription());
-        builder.append("; viewId: ").append(getViewIdResourceName());
-
-        builder.append("; checkable: ").append(isCheckable());
-        builder.append("; checked: ").append(isChecked());
-        builder.append("; focusable: ").append(isFocusable());
-        builder.append("; focused: ").append(isFocused());
-        builder.append("; selected: ").append(isSelected());
-        builder.append("; clickable: ").append(isClickable());
-        builder.append("; longClickable: ").append(isLongClickable());
-        builder.append("; enabled: ").append(isEnabled());
-        builder.append("; password: ").append(isPassword());
-        builder.append("; scrollable: " + isScrollable());
-
-        builder.append("; [");
-        for (int actionBits = getActions(); actionBits != 0;) {
-            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
-            actionBits &= ~action;
-            builder.append(getActionSymbolicName(action));
-            if (actionBits != 0) {
-                builder.append(", ");
-            }
-        }
-        builder.append("]");
-
-        return builder.toString();
-    }
-
-    private static String getActionSymbolicName(int action) {
-        switch (action) {
-            case ACTION_FOCUS:
-                return "ACTION_FOCUS";
-            case ACTION_CLEAR_FOCUS:
-                return "ACTION_CLEAR_FOCUS";
-            case ACTION_SELECT:
-                return "ACTION_SELECT";
-            case ACTION_CLEAR_SELECTION:
-                return "ACTION_CLEAR_SELECTION";
-            case ACTION_CLICK:
-                return "ACTION_CLICK";
-            case ACTION_LONG_CLICK:
-                return "ACTION_LONG_CLICK";
-            case ACTION_ACCESSIBILITY_FOCUS:
-                return "ACTION_ACCESSIBILITY_FOCUS";
-            case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-                return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
-            case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-                return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
-            case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
-                return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
-            case ACTION_NEXT_HTML_ELEMENT:
-                return "ACTION_NEXT_HTML_ELEMENT";
-            case ACTION_PREVIOUS_HTML_ELEMENT:
-                return "ACTION_PREVIOUS_HTML_ELEMENT";
-            case ACTION_SCROLL_FORWARD:
-                return "ACTION_SCROLL_FORWARD";
-            case ACTION_SCROLL_BACKWARD:
-                return "ACTION_SCROLL_BACKWARD";
-            case ACTION_CUT:
-                return "ACTION_CUT";
-            case ACTION_COPY:
-                return "ACTION_COPY";
-            case ACTION_PASTE:
-                return "ACTION_PASTE";
-            case ACTION_SET_SELECTION:
-                return "ACTION_SET_SELECTION";
-            default:
-                return"ACTION_UNKNOWN";
-        }
-    }
-}
diff --git a/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java b/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
deleted file mode 100644
index 627a73d..0000000
--- a/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
+++ /dev/null
@@ -1,209 +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.Build;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}.
- */
-public class AccessibilityNodeProviderCompat {
-    @RequiresApi(16)
-    static class AccessibilityNodeProviderApi16 extends AccessibilityNodeProvider {
-        final AccessibilityNodeProviderCompat mCompat;
-
-        AccessibilityNodeProviderApi16(AccessibilityNodeProviderCompat compat) {
-            mCompat = compat;
-        }
-
-        @Override
-        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
-            final AccessibilityNodeInfoCompat compatInfo =
-                    mCompat.createAccessibilityNodeInfo(virtualViewId);
-            if (compatInfo == null) {
-                return null;
-            } else {
-                return compatInfo.unwrap();
-            }
-        }
-
-        @Override
-        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(
-                String text, int virtualViewId) {
-            final List<AccessibilityNodeInfoCompat> compatInfos =
-                    mCompat.findAccessibilityNodeInfosByText(text, virtualViewId);
-            if (compatInfos == null) {
-                return null;
-            } else {
-                final List<AccessibilityNodeInfo> infoList = new ArrayList<>();
-                final int infoCount = compatInfos.size();
-                for (int i = 0; i < infoCount; i++) {
-                    AccessibilityNodeInfoCompat infoCompat = compatInfos.get(i);
-                    infoList.add(infoCompat.unwrap());
-                }
-                return infoList;
-            }
-        }
-
-        @Override
-        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
-            return mCompat.performAction(virtualViewId, action, arguments);
-        }
-    }
-
-    @RequiresApi(19)
-    static class AccessibilityNodeProviderApi19 extends AccessibilityNodeProviderApi16 {
-        AccessibilityNodeProviderApi19(AccessibilityNodeProviderCompat compat) {
-            super(compat);
-        }
-
-        @Override
-        public AccessibilityNodeInfo findFocus(int focus) {
-            final AccessibilityNodeInfoCompat compatInfo = mCompat.findFocus(focus);
-            if (compatInfo == null) {
-                return null;
-            } else {
-                return compatInfo.unwrap();
-            }
-        }
-    }
-
-    /**
-     * The virtual id for the hosting View.
-     */
-    public static final int HOST_VIEW_ID = -1;
-
-    private final Object mProvider;
-
-    /**
-     * Creates a new instance.
-     */
-    public AccessibilityNodeProviderCompat() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            mProvider = new AccessibilityNodeProviderApi19(this);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            mProvider = new AccessibilityNodeProviderApi16(this);
-        } else {
-            mProvider = null;
-        }
-    }
-
-    /**
-     * Creates a new instance wrapping an
-     * {@link android.view.accessibility.AccessibilityNodeProvider}.
-     *
-     * @param provider The provider.
-     */
-    public AccessibilityNodeProviderCompat(Object provider) {
-        mProvider = provider;
-    }
-
-    /**
-     * @return The wrapped {@link android.view.accessibility.AccessibilityNodeProvider}.
-     */
-    public Object getProvider() {
-        return mProvider;
-    }
-
-    /**
-     * Returns an {@link AccessibilityNodeInfoCompat} representing a virtual view,
-     * i.e. a descendant of the host View, with the given <code>virtualViewId</code>
-     * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
-     * <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>
-     * <p>
-     * The implementer is responsible for obtaining an accessibility node info from the
-     * pool of reusable instances and setting the desired properties of the node info
-     * before returning it.
-     * </p>
-     *
-     * @param virtualViewId A client defined virtual view id.
-     * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant
-     *     or the host View.
-     *
-     * @see AccessibilityNodeInfoCompat
-     */
-    @Nullable
-    public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
-        return null;
-    }
-
-    /**
-     * Performs an accessibility action on a virtual view, i.e. a descendant of the
-     * host View, with the given <code>virtualViewId</code> or the host View itself
-     * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
-     *
-     * @param virtualViewId A client defined virtual view id.
-     * @param action The action to perform.
-     * @param arguments Optional arguments.
-     * @return True if the action was performed.
-     *
-     * @see #createAccessibilityNodeInfo(int)
-     * @see AccessibilityNodeInfoCompat
-     */
-    public boolean performAction(int virtualViewId, int action, Bundle arguments) {
-        return false;
-    }
-
-    /**
-     * Finds {@link AccessibilityNodeInfoCompat}s by text. The match is case insensitive
-     * containment. The search is relative to the virtual view, i.e. a descendant of the
-     * host View, with the given <code>virtualViewId</code> or the host View itself
-     * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
-     *
-     * @param virtualViewId A client defined virtual view id which defined
-     *     the root of the tree in which to perform the search.
-     * @param text The searched text.
-     * @return A list of node info.
-     *
-     * @see #createAccessibilityNodeInfo(int)
-     * @see AccessibilityNodeInfoCompat
-     */
-    @Nullable
-    public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text,
-            int virtualViewId) {
-        return null;
-    }
-
-    /**
-     * Find the virtual view, i.e. a descendant of the host View, that has the
-     * specified focus type.
-     *
-     * @param focus The focus to find. One of
-     *            {@link AccessibilityNodeInfoCompat#FOCUS_INPUT} or
-     *            {@link AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY}.
-     * @return The node info of the focused view or null.
-     * @see AccessibilityNodeInfoCompat#FOCUS_INPUT
-     * @see AccessibilityNodeInfoCompat#FOCUS_ACCESSIBILITY
-     */
-    @Nullable
-    public AccessibilityNodeInfoCompat findFocus(int focus) {
-        return null;
-    }
-}
diff --git a/android/support/v4/view/accessibility/AccessibilityRecordCompat.java b/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
deleted file mode 100644
index ce5d024..0000000
--- a/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
+++ /dev/null
@@ -1,779 +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.Build;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityRecord;
-
-import java.util.List;
-
-/**
- * Helper for accessing {@link AccessibilityRecord}.
- */
-public class AccessibilityRecordCompat {
-    private final AccessibilityRecord mRecord;
-
-    /**
-     * @deprecated This is not type safe. If you want to modify an
-     * {@link AccessibilityEvent}'s properties defined in
-     * {@link android.view.accessibility.AccessibilityRecord} use
-     * {@link AccessibilityEventCompat#asRecord(AccessibilityEvent)}. This method will be removed
-     * in a subsequent release of the support library.
-     */
-    @Deprecated
-    public AccessibilityRecordCompat(Object record) {
-        mRecord = (AccessibilityRecord) record;
-    }
-
-    /**
-     * @return The wrapped implementation.
-     *
-     * @deprecated This method will be removed in a subsequent release of
-     * the support library.
-     */
-    @Deprecated
-    public Object getImpl() {
-        return mRecord;
-    }
-
-    /**
-     * Returns a cached instance if such is available or a new one is
-     * instantiated. The instance is initialized with data from the
-     * given record.
-     *
-     * @return An instance.
-     *
-     * @deprecated Use {@link AccessibilityRecord#obtain(AccessibilityRecord)} directly.
-     */
-    @Deprecated
-    public static AccessibilityRecordCompat obtain(AccessibilityRecordCompat record) {
-        return new AccessibilityRecordCompat(AccessibilityRecord.obtain(record.mRecord));
-    }
-
-    /**
-     * Returns a cached instance if such is available or a new one is
-     * instantiated.
-     *
-     * @return An instance.
-     *
-     * @deprecated Use {@link AccessibilityRecord#obtain()} directly.
-     */
-    @Deprecated
-    public static AccessibilityRecordCompat obtain() {
-        return new AccessibilityRecordCompat(AccessibilityRecord.obtain());
-    }
-
-    /**
-     * Sets the event source.
-     *
-     * @param source The source.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     *
-     * @deprecated Use {@link AccessibilityRecord#setSource(View)} directly.
-     */
-    @Deprecated
-    public void setSource(View source) {
-        mRecord.setSource(source);
-    }
-
-    /**
-     * 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 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) {
-        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) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            record.setSource(root, virtualDescendantId);
-        }
-    }
-
-    /**
-     * Gets the {@link android.view.accessibility.AccessibilityNodeInfo} of
-     * the event source.
-     * <p>
-     * <strong>Note:</strong> It is a client responsibility to recycle the
-     * received info by calling
-     * {@link android.view.accessibility.AccessibilityNodeInfo#recycle()
-     * AccessibilityNodeInfo#recycle()} to avoid creating of multiple instances.
-     *</p>
-     *
-     * @return The info of the source.
-     *
-     * @deprecated Use {@link AccessibilityRecord#getSource()} directly.
-     */
-    @Deprecated
-    public AccessibilityNodeInfoCompat getSource() {
-        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 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 mRecord.isChecked();
-    }
-
-    /**
-     * Sets if the source is checked.
-     *
-     * @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) {
-        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 mRecord.isEnabled();
-    }
-
-    /**
-     * Sets if the source is enabled.
-     *
-     * @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) {
-        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 mRecord.isPassword();
-    }
-
-    /**
-     * Sets if the source is a password field.
-     *
-     * @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) {
-        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 mRecord.isFullScreen();
-    }
-
-    /**
-     * Sets if the source is taking the entire screen.
-     *
-     * @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) {
-        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 mRecord.isScrollable();
-    }
-
-    /**
-     * Sets if the source is scrollable.
-     *
-     * @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) {
-        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 mRecord.getItemCount();
-    }
-
-    /**
-     * Sets the number of items that can be visited.
-     *
-     * @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) {
-        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 mRecord.getCurrentItemIndex();
-    }
-
-    /**
-     * Sets the index of the source in the list of items that can be visited.
-     *
-     * @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) {
-        mRecord.setCurrentItemIndex(currentItemIndex);
-    }
-
-    /**
-     * Gets the index of the first character of the changed sequence,
-     * or the beginning of a text selection or the index of the first
-     * visible item when scrolling.
-     *
-     * @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 mRecord.getFromIndex();
-    }
-
-    /**
-     * Sets the index of the first character of the changed sequence
-     * or the beginning of a text selection or the index of the first
-     * visible item when scrolling.
-     *
-     * @param fromIndex The index of the first character or selection
-     *        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) {
-        mRecord.setFromIndex(fromIndex);
-    }
-
-    /**
-     * Gets the index of text selection end or the index of the last
-     * 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 mRecord.getToIndex();
-    }
-
-    /**
-     * Sets the index of text selection end or the index of the last
-     * 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) {
-        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 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) {
-        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 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) {
-        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 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) {
-        if (Build.VERSION.SDK_INT >= 15) {
-            return record.getMaxScrollX();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * 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) {
-        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) {
-        if (Build.VERSION.SDK_INT >= 15) {
-            record.setMaxScrollX(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 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) {
-        if (Build.VERSION.SDK_INT >= 15) {
-            return record.getMaxScrollY();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * 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) {
-        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) {
-        if (Build.VERSION.SDK_INT >= 15) {
-            record.setMaxScrollY(maxScrollY);
-        }
-    }
-
-    /**
-     * Gets the number of added characters.
-     *
-     * @return The number of added characters.
-     *
-     * @deprecated Use {@link AccessibilityRecord#getAddedCount()} directly.
-     */
-    @Deprecated
-    public int getAddedCount() {
-        return mRecord.getAddedCount();
-    }
-
-    /**
-     * Sets the number of added characters.
-     *
-     * @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) {
-        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 mRecord.getRemovedCount();
-    }
-
-    /**
-     * Sets the number of removed characters.
-     *
-     * @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) {
-        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 mRecord.getClassName();
-    }
-
-    /**
-     * Sets the class name of the source.
-     *
-     * @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) {
-        mRecord.setClassName(className);
-    }
-
-    /**
-     * Gets the text of the event. The index in the list represents the priority
-     * 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 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 mRecord.getBeforeText();
-    }
-
-    /**
-     * Sets the text before a change.
-     *
-     * @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) {
-        mRecord.setBeforeText(beforeText);
-    }
-
-    /**
-     * Gets the description of the source.
-     *
-     * @return The description.
-     *
-     * @deprecated Use {@link AccessibilityRecord#getContentDescription()} directly.
-     */
-    @Deprecated
-    public CharSequence getContentDescription() {
-        return mRecord.getContentDescription();
-    }
-
-    /**
-     * Sets the description of the source.
-     *
-     * @param contentDescription The description.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     *
-     * @deprecated Use {@link AccessibilityRecord#setContentDescription(CharSequence)} directly.
-     */
-    @Deprecated
-    public void setContentDescription(CharSequence contentDescription) {
-        mRecord.setContentDescription(contentDescription);
-    }
-
-    /**
-     * Gets the {@link Parcelable} data.
-     *
-     * @return The parcelable data.
-     *
-     * @deprecated Use {@link AccessibilityRecord#getParcelableData()} directly.
-     */
-    @Deprecated
-    public Parcelable getParcelableData() {
-        return mRecord.getParcelableData();
-    }
-
-    /**
-     * Sets the {@link Parcelable} data of the event.
-     *
-     * @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) {
-        mRecord.setParcelableData(parcelableData);
-    }
-
-    /**
-     * Return an instance back to be reused.
-     * <p>
-     * <strong>Note:</strong> You must not touch the object after calling this
-     * function.
-     * </p>
-     *
-     * @throws IllegalStateException If the record is already recycled.
-     *
-     * @deprecated Use {@link AccessibilityRecord#recycle()} directly.
-     */
-    @Deprecated
-    public void recycle() {
-        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) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        AccessibilityRecordCompat other = (AccessibilityRecordCompat) obj;
-        if (mRecord == null) {
-            if (other.mRecord != null) {
-                return false;
-            }
-        } else if (!mRecord.equals(other.mRecord)) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java b/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
deleted file mode 100644
index be5d29f..0000000
--- a/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
+++ /dev/null
@@ -1,375 +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 static android.os.Build.VERSION.SDK_INT;
-
-import android.graphics.Rect;
-import android.view.accessibility.AccessibilityWindowInfo;
-
-/**
- * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo}.
- */
-public class AccessibilityWindowInfoCompat {
-    private Object mInfo;
-
-    private static final int UNDEFINED = -1;
-
-    /**
-     * Window type: This is an application window. Such a window shows UI for
-     * interacting with an application.
-     */
-    public static final int TYPE_APPLICATION = 1;
-
-    /**
-     * Window type: This is an input method window. Such a window shows UI for
-     * inputting text such as keyboard, suggestions, etc.
-     */
-    public static final int TYPE_INPUT_METHOD = 2;
-
-    /**
-     * Window type: This is an system window. Such a window shows UI for
-     * interacting with the system.
-     */
-    public static final int TYPE_SYSTEM = 3;
-
-    /**
-     * Window type: Windows that are overlaid <em>only</em> by an {@link
-     * android.accessibilityservice.AccessibilityService} for interception of
-     * user interactions without changing the windows an accessibility service
-     * can introspect. In particular, an accessibility service can introspect
-     * only windows that a sighted user can interact with which they can touch
-     * these windows or can type into these windows. For example, if there
-     * is a full screen accessibility overlay that is touchable, the windows
-     * below it will be introspectable by an accessibility service regardless
-     * they are covered by a touchable window.
-     */
-    public static final int TYPE_ACCESSIBILITY_OVERLAY = 4;
-
-    /**
-     * Window type: A system window used to divide the screen in split-screen mode.
-     * This type of window is present only in split-screen mode.
-     */
-    public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
-
-    /**
-     * Creates a wrapper for info implementation.
-     *
-     * @param object The info to wrap.
-     * @return A wrapper for if the object is not null, null otherwise.
-     */
-    static AccessibilityWindowInfoCompat wrapNonNullInstance(Object object) {
-        if (object != null) {
-            return new AccessibilityWindowInfoCompat(object);
-        }
-        return null;
-    }
-
-    private AccessibilityWindowInfoCompat(Object info) {
-        mInfo = info;
-    }
-
-    /**
-     * Gets the type of the window.
-     *
-     * @return The type.
-     *
-     * @see #TYPE_APPLICATION
-     * @see #TYPE_INPUT_METHOD
-     * @see #TYPE_SYSTEM
-     * @see #TYPE_ACCESSIBILITY_OVERLAY
-     */
-    public int getType() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).getType();
-        } else {
-            return UNDEFINED;
-        }
-    }
-
-    /**
-     * Gets the layer which determines the Z-order of the window. Windows
-     * with greater layer appear on top of windows with lesser layer.
-     *
-     * @return The window layer.
-     */
-    public int getLayer() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).getLayer();
-        } else {
-            return UNDEFINED;
-        }
-    }
-
-    /**
-     * Gets the root node in the window's hierarchy.
-     *
-     * @return The root node.
-     */
-    public AccessibilityNodeInfoCompat getRoot() {
-        if (SDK_INT >= 21) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
-                    ((AccessibilityWindowInfo) mInfo).getRoot());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the parent window if such.
-     *
-     * @return The parent window.
-     */
-    public AccessibilityWindowInfoCompat getParent() {
-        if (SDK_INT >= 21) {
-            return wrapNonNullInstance(((AccessibilityWindowInfo) mInfo).getParent());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the unique window id.
-     *
-     * @return windowId The window id.
-     */
-    public int getId() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).getId();
-        } else {
-            return UNDEFINED;
-        }
-    }
-
-    /**
-     * Gets the bounds of this window in the screen.
-     *
-     * @param outBounds The out window bounds.
-     */
-    public void getBoundsInScreen(Rect outBounds) {
-        if (SDK_INT >= 21) {
-            ((AccessibilityWindowInfo) mInfo).getBoundsInScreen(outBounds);
-        }
-    }
-
-    /**
-     * Gets if this window is active. An active window is the one
-     * the user is currently touching or the window has input focus
-     * and the user is not touching any window.
-     *
-     * @return Whether this is the active window.
-     */
-    public boolean isActive() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).isActive();
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Gets if this window has input focus.
-     *
-     * @return Whether has input focus.
-     */
-    public boolean isFocused() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).isFocused();
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Gets if this window has accessibility focus.
-     *
-     * @return Whether has accessibility focus.
-     */
-    public boolean isAccessibilityFocused() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).isAccessibilityFocused();
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Gets the number of child windows.
-     *
-     * @return The child count.
-     */
-    public int getChildCount() {
-        if (SDK_INT >= 21) {
-            return ((AccessibilityWindowInfo) mInfo).getChildCount();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * Gets the child window at a given index.
-     *
-     * @param index The index.
-     * @return The child.
-     */
-    public AccessibilityWindowInfoCompat getChild(int index) {
-        if (SDK_INT >= 21) {
-            return wrapNonNullInstance(((AccessibilityWindowInfo) mInfo).getChild(index));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the title of the window.
-     *
-     * @return The title of the window, or the application label for the window if no title was
-     * explicitly set, or {@code null} if neither is available.
-     */
-    public CharSequence getTitle() {
-        if (SDK_INT >= 24) {
-            return ((AccessibilityWindowInfo) mInfo).getTitle();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the node that anchors this window to another.
-     *
-     * @return The anchor node, or {@code null} if none exists.
-     */
-    public AccessibilityNodeInfoCompat getAnchor() {
-        if (SDK_INT >= 24) {
-            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
-                    ((AccessibilityWindowInfo) mInfo).getAnchor());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns a cached instance if such is available or a new one is
-     * created.
-     *
-     * @return An instance.
-     */
-    public static AccessibilityWindowInfoCompat obtain() {
-        if (SDK_INT >= 21) {
-            return wrapNonNullInstance(AccessibilityWindowInfo.obtain());
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Returns a cached instance if such is available or a new one is
-     * created. The returned instance is initialized from the given
-     * <code>info</code>.
-     *
-     * @param info The other info.
-     * @return An instance.
-     */
-    public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) {
-        if (SDK_INT >= 21) {
-            return info == null
-                    ? null
-                    : wrapNonNullInstance(
-                            AccessibilityWindowInfo.obtain((AccessibilityWindowInfo) info.mInfo));
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Return an instance back to be reused.
-     * <p>
-     * <strong>Note:</strong> You must not touch the object after calling this function.
-     * </p>
-     *
-     * @throws IllegalStateException If the info is already recycled.
-     */
-    public void recycle() {
-        if (SDK_INT >= 21) {
-            ((AccessibilityWindowInfo) mInfo).recycle();
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        return (mInfo == null) ? 0 : mInfo.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        AccessibilityWindowInfoCompat other = (AccessibilityWindowInfoCompat) obj;
-        if (mInfo == null) {
-            if (other.mInfo != null) {
-                return false;
-            }
-        } else if (!mInfo.equals(other.mInfo)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        Rect bounds = new Rect();
-        getBoundsInScreen(bounds);
-        builder.append("AccessibilityWindowInfo[");
-        builder.append("id=").append(getId());
-        builder.append(", type=").append(typeToString(getType()));
-        builder.append(", layer=").append(getLayer());
-        builder.append(", bounds=").append(bounds);
-        builder.append(", focused=").append(isFocused());
-        builder.append(", active=").append(isActive());
-        builder.append(", hasParent=").append(getParent() != null);
-        builder.append(", hasChildren=").append(getChildCount() > 0);
-        builder.append(']');
-        return builder.toString();
-    }
-
-    private static String typeToString(int type) {
-        switch (type) {
-            case TYPE_APPLICATION: {
-                return "TYPE_APPLICATION";
-            }
-            case TYPE_INPUT_METHOD: {
-                return "TYPE_INPUT_METHOD";
-            }
-            case TYPE_SYSTEM: {
-                return "TYPE_SYSTEM";
-            }
-            case TYPE_ACCESSIBILITY_OVERLAY: {
-                return "TYPE_ACCESSIBILITY_OVERLAY";
-            }
-            default:
-                return "<UNKNOWN>";
-        }
-    }
-}
diff --git a/android/support/v4/view/animation/FastOutLinearInInterpolator.java b/android/support/v4/view/animation/FastOutLinearInInterpolator.java
deleted file mode 100644
index 1f39aa8..0000000
--- a/android/support/v4/view/animation/FastOutLinearInInterpolator.java
+++ /dev/null
@@ -1,69 +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;
-
-/**
- * Interpolator corresponding to {@link android.R.interpolator#fast_out_linear_in}.
- *
- * Uses a lookup table for the Bezier curve from (0,0) to (1,1) with control points:
- * P0 (0, 0)
- * P1 (0.4, 0)
- * P2 (1.0, 1.0)
- * P3 (1.0, 1.0)
- */
-public class FastOutLinearInInterpolator extends LookupTableInterpolator {
-
-    /**
-     * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of
-     * 201 points.
-     */
-    private static final float[] VALUES = new float[] {
-            0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0008f, 0.0013f, 0.0018f,
-            0.0024f, 0.0032f, 0.0040f, 0.0049f, 0.0059f, 0.0069f, 0.0081f,
-            0.0093f, 0.0106f, 0.0120f, 0.0135f, 0.0151f, 0.0167f, 0.0184f,
-            0.0201f, 0.0220f, 0.0239f, 0.0259f, 0.0279f, 0.0300f, 0.0322f,
-            0.0345f, 0.0368f, 0.0391f, 0.0416f, 0.0441f, 0.0466f, 0.0492f,
-            0.0519f, 0.0547f, 0.0574f, 0.0603f, 0.0632f, 0.0662f, 0.0692f,
-            0.0722f, 0.0754f, 0.0785f, 0.0817f, 0.0850f, 0.0884f, 0.0917f,
-            0.0952f, 0.0986f, 0.1021f, 0.1057f, 0.1093f, 0.1130f, 0.1167f,
-            0.1205f, 0.1243f, 0.1281f, 0.1320f, 0.1359f, 0.1399f, 0.1439f,
-            0.1480f, 0.1521f, 0.1562f, 0.1604f, 0.1647f, 0.1689f, 0.1732f,
-            0.1776f, 0.1820f, 0.1864f, 0.1909f, 0.1954f, 0.1999f, 0.2045f,
-            0.2091f, 0.2138f, 0.2184f, 0.2232f, 0.2279f, 0.2327f, 0.2376f,
-            0.2424f, 0.2473f, 0.2523f, 0.2572f, 0.2622f, 0.2673f, 0.2723f,
-            0.2774f, 0.2826f, 0.2877f, 0.2929f, 0.2982f, 0.3034f, 0.3087f,
-            0.3141f, 0.3194f, 0.3248f, 0.3302f, 0.3357f, 0.3412f, 0.3467f,
-            0.3522f, 0.3578f, 0.3634f, 0.3690f, 0.3747f, 0.3804f, 0.3861f,
-            0.3918f, 0.3976f, 0.4034f, 0.4092f, 0.4151f, 0.4210f, 0.4269f,
-            0.4329f, 0.4388f, 0.4448f, 0.4508f, 0.4569f, 0.4630f, 0.4691f,
-            0.4752f, 0.4814f, 0.4876f, 0.4938f, 0.5000f, 0.5063f, 0.5126f,
-            0.5189f, 0.5252f, 0.5316f, 0.5380f, 0.5444f, 0.5508f, 0.5573f,
-            0.5638f, 0.5703f, 0.5768f, 0.5834f, 0.5900f, 0.5966f, 0.6033f,
-            0.6099f, 0.6166f, 0.6233f, 0.6301f, 0.6369f, 0.6436f, 0.6505f,
-            0.6573f, 0.6642f, 0.6710f, 0.6780f, 0.6849f, 0.6919f, 0.6988f,
-            0.7059f, 0.7129f, 0.7199f, 0.7270f, 0.7341f, 0.7413f, 0.7484f,
-            0.7556f, 0.7628f, 0.7700f, 0.7773f, 0.7846f, 0.7919f, 0.7992f,
-            0.8066f, 0.8140f, 0.8214f, 0.8288f, 0.8363f, 0.8437f, 0.8513f,
-            0.8588f, 0.8664f, 0.8740f, 0.8816f, 0.8892f, 0.8969f, 0.9046f,
-            0.9124f, 0.9201f, 0.9280f, 0.9358f, 0.9437f, 0.9516f, 0.9595f,
-            0.9675f, 0.9755f, 0.9836f, 0.9918f, 1.0000f
-    };
-
-    public FastOutLinearInInterpolator() {
-        super(VALUES);
-    }
-}
diff --git a/android/support/v4/view/animation/FastOutSlowInInterpolator.java b/android/support/v4/view/animation/FastOutSlowInInterpolator.java
deleted file mode 100644
index a21d131..0000000
--- a/android/support/v4/view/animation/FastOutSlowInInterpolator.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.view.animation;
-
-/**
- * Interpolator corresponding to {@link android.R.interpolator#fast_out_slow_in}.
- *
- * Uses a lookup table for the 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)
- */
-public class FastOutSlowInInterpolator extends LookupTableInterpolator {
-
-    /**
-     * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of
-     * 201 points.
-     */
-    private static final float[] VALUES = new float[] {
-            0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0009f, 0.0014f, 0.0020f,
-            0.0027f, 0.0036f, 0.0046f, 0.0058f, 0.0071f, 0.0085f, 0.0101f,
-            0.0118f, 0.0137f, 0.0158f, 0.0180f, 0.0205f, 0.0231f, 0.0259f,
-            0.0289f, 0.0321f, 0.0355f, 0.0391f, 0.0430f, 0.0471f, 0.0514f,
-            0.0560f, 0.0608f, 0.0660f, 0.0714f, 0.0771f, 0.0830f, 0.0893f,
-            0.0959f, 0.1029f, 0.1101f, 0.1177f, 0.1257f, 0.1339f, 0.1426f,
-            0.1516f, 0.1610f, 0.1707f, 0.1808f, 0.1913f, 0.2021f, 0.2133f,
-            0.2248f, 0.2366f, 0.2487f, 0.2611f, 0.2738f, 0.2867f, 0.2998f,
-            0.3131f, 0.3265f, 0.3400f, 0.3536f, 0.3673f, 0.3810f, 0.3946f,
-            0.4082f, 0.4217f, 0.4352f, 0.4485f, 0.4616f, 0.4746f, 0.4874f,
-            0.5000f, 0.5124f, 0.5246f, 0.5365f, 0.5482f, 0.5597f, 0.5710f,
-            0.5820f, 0.5928f, 0.6033f, 0.6136f, 0.6237f, 0.6335f, 0.6431f,
-            0.6525f, 0.6616f, 0.6706f, 0.6793f, 0.6878f, 0.6961f, 0.7043f,
-            0.7122f, 0.7199f, 0.7275f, 0.7349f, 0.7421f, 0.7491f, 0.7559f,
-            0.7626f, 0.7692f, 0.7756f, 0.7818f, 0.7879f, 0.7938f, 0.7996f,
-            0.8053f, 0.8108f, 0.8162f, 0.8215f, 0.8266f, 0.8317f, 0.8366f,
-            0.8414f, 0.8461f, 0.8507f, 0.8551f, 0.8595f, 0.8638f, 0.8679f,
-            0.8720f, 0.8760f, 0.8798f, 0.8836f, 0.8873f, 0.8909f, 0.8945f,
-            0.8979f, 0.9013f, 0.9046f, 0.9078f, 0.9109f, 0.9139f, 0.9169f,
-            0.9198f, 0.9227f, 0.9254f, 0.9281f, 0.9307f, 0.9333f, 0.9358f,
-            0.9382f, 0.9406f, 0.9429f, 0.9452f, 0.9474f, 0.9495f, 0.9516f,
-            0.9536f, 0.9556f, 0.9575f, 0.9594f, 0.9612f, 0.9629f, 0.9646f,
-            0.9663f, 0.9679f, 0.9695f, 0.9710f, 0.9725f, 0.9739f, 0.9753f,
-            0.9766f, 0.9779f, 0.9791f, 0.9803f, 0.9815f, 0.9826f, 0.9837f,
-            0.9848f, 0.9858f, 0.9867f, 0.9877f, 0.9885f, 0.9894f, 0.9902f,
-            0.9910f, 0.9917f, 0.9924f, 0.9931f, 0.9937f, 0.9944f, 0.9949f,
-            0.9955f, 0.9960f, 0.9964f, 0.9969f, 0.9973f, 0.9977f, 0.9980f,
-            0.9984f, 0.9986f, 0.9989f, 0.9991f, 0.9993f, 0.9995f, 0.9997f,
-            0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
-    };
-
-    public FastOutSlowInInterpolator() {
-        super(VALUES);
-    }
-
-}
diff --git a/android/support/v4/view/animation/LinearOutSlowInInterpolator.java b/android/support/v4/view/animation/LinearOutSlowInInterpolator.java
deleted file mode 100644
index 41f4cd6..0000000
--- a/android/support/v4/view/animation/LinearOutSlowInInterpolator.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.view.animation;
-
-/**
- * Interpolator corresponding to {@link android.R.interpolator#linear_out_slow_in}.
- *
- * Uses a lookup table for the Bezier curve from (0,0) to (1,1) with control points:
- * P0 (0, 0)
- * P1 (0, 0)
- * P2 (0.2, 1.0)
- * P3 (1.0, 1.0)
- */
-public class LinearOutSlowInInterpolator extends LookupTableInterpolator {
-
-    /**
-     * Lookup table values sampled with x at regular intervals between 0 and 1 for a total of
-     * 201 points.
-     */
-    private static final float[] VALUES = new float[] {
-            0.0000f, 0.0222f, 0.0424f, 0.0613f, 0.0793f, 0.0966f, 0.1132f,
-            0.1293f, 0.1449f, 0.1600f, 0.1747f, 0.1890f, 0.2029f, 0.2165f,
-            0.2298f, 0.2428f, 0.2555f, 0.2680f, 0.2802f, 0.2921f, 0.3038f,
-            0.3153f, 0.3266f, 0.3377f, 0.3486f, 0.3592f, 0.3697f, 0.3801f,
-            0.3902f, 0.4002f, 0.4100f, 0.4196f, 0.4291f, 0.4385f, 0.4477f,
-            0.4567f, 0.4656f, 0.4744f, 0.4831f, 0.4916f, 0.5000f, 0.5083f,
-            0.5164f, 0.5245f, 0.5324f, 0.5402f, 0.5479f, 0.5555f, 0.5629f,
-            0.5703f, 0.5776f, 0.5847f, 0.5918f, 0.5988f, 0.6057f, 0.6124f,
-            0.6191f, 0.6257f, 0.6322f, 0.6387f, 0.6450f, 0.6512f, 0.6574f,
-            0.6635f, 0.6695f, 0.6754f, 0.6812f, 0.6870f, 0.6927f, 0.6983f,
-            0.7038f, 0.7093f, 0.7147f, 0.7200f, 0.7252f, 0.7304f, 0.7355f,
-            0.7406f, 0.7455f, 0.7504f, 0.7553f, 0.7600f, 0.7647f, 0.7694f,
-            0.7740f, 0.7785f, 0.7829f, 0.7873f, 0.7917f, 0.7959f, 0.8002f,
-            0.8043f, 0.8084f, 0.8125f, 0.8165f, 0.8204f, 0.8243f, 0.8281f,
-            0.8319f, 0.8356f, 0.8392f, 0.8429f, 0.8464f, 0.8499f, 0.8534f,
-            0.8568f, 0.8601f, 0.8634f, 0.8667f, 0.8699f, 0.8731f, 0.8762f,
-            0.8792f, 0.8823f, 0.8852f, 0.8882f, 0.8910f, 0.8939f, 0.8967f,
-            0.8994f, 0.9021f, 0.9048f, 0.9074f, 0.9100f, 0.9125f, 0.9150f,
-            0.9174f, 0.9198f, 0.9222f, 0.9245f, 0.9268f, 0.9290f, 0.9312f,
-            0.9334f, 0.9355f, 0.9376f, 0.9396f, 0.9416f, 0.9436f, 0.9455f,
-            0.9474f, 0.9492f, 0.9510f, 0.9528f, 0.9545f, 0.9562f, 0.9579f,
-            0.9595f, 0.9611f, 0.9627f, 0.9642f, 0.9657f, 0.9672f, 0.9686f,
-            0.9700f, 0.9713f, 0.9726f, 0.9739f, 0.9752f, 0.9764f, 0.9776f,
-            0.9787f, 0.9798f, 0.9809f, 0.9820f, 0.9830f, 0.9840f, 0.9849f,
-            0.9859f, 0.9868f, 0.9876f, 0.9885f, 0.9893f, 0.9900f, 0.9908f,
-            0.9915f, 0.9922f, 0.9928f, 0.9934f, 0.9940f, 0.9946f, 0.9951f,
-            0.9956f, 0.9961f, 0.9966f, 0.9970f, 0.9974f, 0.9977f, 0.9981f,
-            0.9984f, 0.9987f, 0.9989f, 0.9992f, 0.9994f, 0.9995f, 0.9997f,
-            0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
-    };
-
-    public LinearOutSlowInInterpolator() {
-        super(VALUES);
-    }
-
-}
diff --git a/android/support/v4/view/animation/LookupTableInterpolator.java b/android/support/v4/view/animation/LookupTableInterpolator.java
deleted file mode 100644
index c234177..0000000
--- a/android/support/v4/view/animation/LookupTableInterpolator.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.v4.view.animation;
-
-import android.view.animation.Interpolator;
-
-/**
- * An {@link Interpolator} that uses a lookup table to compute an interpolation based on a
- * given input.
- */
-abstract class LookupTableInterpolator implements Interpolator {
-
-    private final float[] mValues;
-    private final float mStepSize;
-
-    public LookupTableInterpolator(float[] values) {
-        mValues = values;
-        mStepSize = 1f / (mValues.length - 1);
-    }
-
-    @Override
-    public float getInterpolation(float input) {
-        if (input >= 1.0f) {
-            return 1.0f;
-        }
-        if (input <= 0f) {
-            return 0f;
-        }
-
-        // Calculate index - We use min with length - 2 to avoid IndexOutOfBoundsException when
-        // we lerp (linearly interpolate) in the return statement
-        int position = Math.min((int) (input * (mValues.length - 1)), mValues.length - 2);
-
-        // Calculate values to account for small offsets as the lookup table has discrete values
-        float quantized = position * mStepSize;
-        float diff = input - quantized;
-        float weight = diff / mStepSize;
-
-        // Linearly interpolate between the table values
-        return mValues[position] + weight * (mValues[position + 1] - mValues[position]);
-    }
-
-}
diff --git a/android/support/v4/view/animation/PathInterpolatorApi14.java b/android/support/v4/view/animation/PathInterpolatorApi14.java
deleted file mode 100644
index 4416a79..0000000
--- a/android/support/v4/view/animation/PathInterpolatorApi14.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.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/android/support/v4/view/animation/PathInterpolatorCompat.java b/android/support/v4/view/animation/PathInterpolatorCompat.java
deleted file mode 100644
index 767b977..0000000
--- a/android/support/v4/view/animation/PathInterpolatorCompat.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.view.animation;
-
-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
- * platform implementation will be used and on older platforms a compatible alternative
- * implementation will be used.
- */
-public final class PathInterpolatorCompat {
-
-    private PathInterpolatorCompat() {
-        // prevent instantiation
-    }
-
-    /**
-     * Create an {@link Interpolator} for an arbitrary {@link Path}. The {@link Path}
-     * must begin at {@code (0, 0)} and end at {@code (1, 1)}. The x-coordinate along the
-     * {@link Path} is the input value and the output is the y coordinate of the line at that
-     * point. This means that the Path must conform to a function {@code y = f(x)}.
-     * <p/>
-     * The {@link Path} must not have gaps in the x direction and must not
-     * loop back on itself such that there can be two points sharing the same x coordinate.
-     *
-     * @param path the {@link Path} to use to make the line representing the {@link Interpolator}
-     * @return the {@link Interpolator} representing the {@link Path}
-     */
-    public static Interpolator create(Path path) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return new PathInterpolator(path);
-        }
-        return new PathInterpolatorApi14(path);
-    }
-
-    /**
-     * Create an {@link Interpolator} for a quadratic Bezier curve. The end points
-     * {@code (0, 0)} and {@code (1, 1)} are assumed.
-     *
-     * @param controlX the x coordinate of the quadratic Bezier control point
-     * @param controlY the y coordinate of the quadratic Bezier control point
-     * @return the {@link Interpolator} representing the quadratic Bezier curve
-     */
-    public static Interpolator create(float controlX, float controlY) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return new PathInterpolator(controlX, controlY);
-        }
-        return new PathInterpolatorApi14(controlX, controlY);
-    }
-
-    /**
-     * Create an {@link Interpolator} for a cubic Bezier curve.  The end points
-     * {@code (0, 0)} and {@code (1, 1)} are assumed.
-     *
-     * @param controlX1 the x coordinate of the first control point of the cubic Bezier
-     * @param controlY1 the y coordinate of the first control point of the cubic Bezier
-     * @param controlX2 the x coordinate of the second control point of the cubic Bezier
-     * @param controlY2 the y coordinate of the second control point of the cubic Bezier
-     * @return the {@link Interpolator} representing the cubic Bezier curve
-     */
-    public static Interpolator create(float controlX1, float controlY1,
-            float controlX2, float controlY2) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
-        }
-        return new PathInterpolatorApi14(controlX1, controlY1, controlX2, controlY2);
-    }
-}
diff --git a/android/support/v4/widget/AutoScrollHelper.java b/android/support/v4/widget/AutoScrollHelper.java
deleted file mode 100644
index 60d208d..0000000
--- a/android/support/v4/widget/AutoScrollHelper.java
+++ /dev/null
@@ -1,879 +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.content.res.Resources;
-import android.os.SystemClock;
-import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-/**
- * AutoScrollHelper is a utility class for adding automatic edge-triggered
- * scrolling to Views.
- * <p>
- * <b>Note:</b> Implementing classes are responsible for overriding the
- * {@link #scrollTargetBy}, {@link #canTargetScrollHorizontally}, and
- * {@link #canTargetScrollVertically} methods. See
- * {@link ListViewAutoScrollHelper} for a {@link android.widget.ListView}
- * -specific implementation.
- * <p>
- * <h1>Activation</h1> Automatic scrolling starts when the user touches within
- * an activation area. By default, activation areas are defined as the top,
- * left, right, and bottom 20% of the host view's total area. Touching within
- * the top activation area scrolls up, left scrolls to the left, and so on.
- * <p>
- * As the user touches closer to the extreme edge of the activation area,
- * scrolling accelerates up to a maximum velocity. When using the default edge
- * type, {@link #EDGE_TYPE_INSIDE_EXTEND}, moving outside of the view bounds
- * will scroll at the maximum velocity.
- * <p>
- * The following activation properties may be configured:
- * <ul>
- * <li>Delay after entering activation area before auto-scrolling begins, see
- * {@link #setActivationDelay}. Default value is
- * {@link ViewConfiguration#getTapTimeout()} to avoid conflicting with taps.
- * <li>Location of activation areas, see {@link #setEdgeType}. Default value is
- * {@link #EDGE_TYPE_INSIDE_EXTEND}.
- * <li>Size of activation areas relative to view size, see
- * {@link #setRelativeEdges}. Default value is 20% for both vertical and
- * horizontal edges.
- * <li>Maximum size used to constrain relative size, see
- * {@link #setMaximumEdges}. Default value is {@link #NO_MAX}.
- * </ul>
- * <h1>Scrolling</h1> When automatic scrolling is active, the helper will
- * repeatedly call {@link #scrollTargetBy} to apply new scrolling offsets.
- * <p>
- * The following scrolling properties may be configured:
- * <ul>
- * <li>Acceleration ramp-up duration, see {@link #setRampUpDuration}. Default
- * value is 500 milliseconds.
- * <li>Acceleration ramp-down duration, see {@link #setRampDownDuration}.
- * Default value is 500 milliseconds.
- * <li>Target velocity relative to view size, see {@link #setRelativeVelocity}.
- * Default value is 100% per second for both vertical and horizontal.
- * <li>Minimum velocity used to constrain relative velocity, see
- * {@link #setMinimumVelocity}. When set, scrolling will accelerate to the
- * larger of either this value or the relative target value. Default value is
- * approximately 5 centimeters or 315 dips per second.
- * <li>Maximum velocity used to constrain relative velocity, see
- * {@link #setMaximumVelocity}. Default value is approximately 25 centimeters or
- * 1575 dips per second.
- * </ul>
- */
-public abstract class AutoScrollHelper implements View.OnTouchListener {
-    /**
-     * Constant passed to {@link #setRelativeEdges} or
-     * {@link #setRelativeVelocity}. Using this value ensures that the computed
-     * relative value is ignored and the absolute maximum value is always used.
-     */
-    public static final float RELATIVE_UNSPECIFIED = 0;
-
-    /**
-     * Constant passed to {@link #setMaximumEdges}, {@link #setMaximumVelocity},
-     * or {@link #setMinimumVelocity}. Using this value ensures that the
-     * computed relative value is always used without constraining to a
-     * particular minimum or maximum value.
-     */
-    public static final float NO_MAX = Float.MAX_VALUE;
-
-    /**
-     * Constant passed to {@link #setMaximumEdges}, or
-     * {@link #setMaximumVelocity}, or {@link #setMinimumVelocity}. Using this
-     * value ensures that the computed relative value is always used without
-     * constraining to a particular minimum or maximum value.
-     */
-    public static final float NO_MIN = 0;
-
-    /**
-     * Edge type that specifies an activation area starting at the view bounds
-     * and extending inward. Moving outside the view bounds will stop scrolling.
-     *
-     * @see #setEdgeType
-     */
-    public static final int EDGE_TYPE_INSIDE = 0;
-
-    /**
-     * Edge type that specifies an activation area starting at the view bounds
-     * and extending inward. After activation begins, moving outside the view
-     * bounds will continue scrolling.
-     *
-     * @see #setEdgeType
-     */
-    public static final int EDGE_TYPE_INSIDE_EXTEND = 1;
-
-    /**
-     * Edge type that specifies an activation area starting at the view bounds
-     * and extending outward. Moving inside the view bounds will stop scrolling.
-     *
-     * @see #setEdgeType
-     */
-    public static final int EDGE_TYPE_OUTSIDE = 2;
-
-    private static final int HORIZONTAL = 0;
-    private static final int VERTICAL = 1;
-
-    /** Scroller used to control acceleration toward maximum velocity. */
-    final ClampedScroller mScroller = new ClampedScroller();
-
-    /** Interpolator used to scale velocity with touch position. */
-    private final Interpolator mEdgeInterpolator = new AccelerateInterpolator();
-
-    /** The view to auto-scroll. Might not be the source of touch events. */
-    final View mTarget;
-
-    /** Runnable used to animate scrolling. */
-    private Runnable mRunnable;
-
-    /** Edge insets used to activate auto-scrolling. */
-    private float[] mRelativeEdges = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
-
-    /** Clamping values for edge insets used to activate auto-scrolling. */
-    private float[] mMaximumEdges = new float[] { NO_MAX, NO_MAX };
-
-    /** The type of edge being used. */
-    private int mEdgeType;
-
-    /** Delay after entering an activation edge before auto-scrolling begins. */
-    private int mActivationDelay;
-
-    /** Relative scrolling velocity at maximum edge distance. */
-    private float[] mRelativeVelocity = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
-
-    /** Clamping values used for scrolling velocity. */
-    private float[] mMinimumVelocity = new float[] { NO_MIN, NO_MIN };
-
-    /** Clamping values used for scrolling velocity. */
-    private float[] mMaximumVelocity = new float[] { NO_MAX, NO_MAX };
-
-    /** Whether to start activation immediately. */
-    private boolean mAlreadyDelayed;
-
-    /** Whether to reset the scroller start time on the next animation. */
-    boolean mNeedsReset;
-
-    /** Whether to send a cancel motion event to the target view. */
-    boolean mNeedsCancel;
-
-    /** Whether the auto-scroller is actively scrolling. */
-    boolean mAnimating;
-
-    /** Whether the auto-scroller is enabled. */
-    private boolean mEnabled;
-
-    /** Whether the auto-scroller consumes events when scrolling. */
-    private boolean mExclusive;
-
-    // Default values.
-    private static final int DEFAULT_EDGE_TYPE = EDGE_TYPE_INSIDE_EXTEND;
-    private static final int DEFAULT_MINIMUM_VELOCITY_DIPS = 315;
-    private static final int DEFAULT_MAXIMUM_VELOCITY_DIPS = 1575;
-    private static final float DEFAULT_MAXIMUM_EDGE = NO_MAX;
-    private static final float DEFAULT_RELATIVE_EDGE = 0.2f;
-    private static final float DEFAULT_RELATIVE_VELOCITY = 1f;
-    private static final int DEFAULT_ACTIVATION_DELAY = ViewConfiguration.getTapTimeout();
-    private static final int DEFAULT_RAMP_UP_DURATION = 500;
-    private static final int DEFAULT_RAMP_DOWN_DURATION = 500;
-
-    /**
-     * Creates a new helper for scrolling the specified target view.
-     * <p>
-     * The resulting helper may be configured by chaining setter calls and
-     * should be set as a touch listener on the target view.
-     * <p>
-     * By default, the helper is disabled and will not respond to touch events
-     * until it is enabled using {@link #setEnabled}.
-     *
-     * @param target The view to automatically scroll.
-     */
-    public AutoScrollHelper(@NonNull View target) {
-        mTarget = target;
-
-        final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
-        final int maxVelocity = (int) (DEFAULT_MAXIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
-        final int minVelocity = (int) (DEFAULT_MINIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
-        setMaximumVelocity(maxVelocity, maxVelocity);
-        setMinimumVelocity(minVelocity, minVelocity);
-
-        setEdgeType(DEFAULT_EDGE_TYPE);
-        setMaximumEdges(DEFAULT_MAXIMUM_EDGE, DEFAULT_MAXIMUM_EDGE);
-        setRelativeEdges(DEFAULT_RELATIVE_EDGE, DEFAULT_RELATIVE_EDGE);
-        setRelativeVelocity(DEFAULT_RELATIVE_VELOCITY, DEFAULT_RELATIVE_VELOCITY);
-        setActivationDelay(DEFAULT_ACTIVATION_DELAY);
-        setRampUpDuration(DEFAULT_RAMP_UP_DURATION);
-        setRampDownDuration(DEFAULT_RAMP_DOWN_DURATION);
-    }
-
-    /**
-     * Sets whether the scroll helper is enabled and should respond to touch
-     * events.
-     *
-     * @param enabled Whether the scroll helper is enabled.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setEnabled(boolean enabled) {
-        if (mEnabled && !enabled) {
-            requestStop();
-        }
-
-        mEnabled = enabled;
-        return this;
-    }
-
-    /**
-     * @return True if this helper is enabled and responding to touch events.
-     */
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    /**
-     * Enables or disables exclusive handling of touch events during scrolling.
-     * By default, exclusive handling is disabled and the target view receives
-     * all touch events.
-     * <p>
-     * When enabled, {@link #onTouch} will return true if the helper is
-     * currently scrolling and false otherwise.
-     *
-     * @param exclusive True to exclusively handle touch events during scrolling,
-     *            false to allow the target view to receive all touch events.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setExclusive(boolean exclusive) {
-        mExclusive = exclusive;
-        return this;
-    }
-
-    /**
-     * Indicates whether the scroll helper handles touch events exclusively
-     * during scrolling.
-     *
-     * @return True if exclusive handling of touch events during scrolling is
-     *         enabled, false otherwise.
-     * @see #setExclusive(boolean)
-     */
-    public boolean isExclusive() {
-        return mExclusive;
-    }
-
-    /**
-     * Sets the absolute maximum scrolling velocity.
-     * <p>
-     * If relative velocity is not specified, scrolling will always reach the
-     * same maximum velocity. If both relative and maximum velocities are
-     * specified, the maximum velocity will be used to clamp the calculated
-     * relative velocity.
-     *
-     * @param horizontalMax The maximum horizontal scrolling velocity, or
-     *            {@link #NO_MAX} to leave the relative value unconstrained.
-     * @param verticalMax The maximum vertical scrolling velocity, or
-     *            {@link #NO_MAX} to leave the relative value unconstrained.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setMaximumVelocity(float horizontalMax, float verticalMax) {
-        mMaximumVelocity[HORIZONTAL] = horizontalMax / 1000f;
-        mMaximumVelocity[VERTICAL] = verticalMax / 1000f;
-        return this;
-    }
-
-    /**
-     * Sets the absolute minimum scrolling velocity.
-     * <p>
-     * If both relative and minimum velocities are specified, the minimum
-     * velocity will be used to clamp the calculated relative velocity.
-     *
-     * @param horizontalMin The minimum horizontal scrolling velocity, or
-     *            {@link #NO_MIN} to leave the relative value unconstrained.
-     * @param verticalMin The minimum vertical scrolling velocity, or
-     *            {@link #NO_MIN} to leave the relative value unconstrained.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setMinimumVelocity(float horizontalMin, float verticalMin) {
-        mMinimumVelocity[HORIZONTAL] = horizontalMin / 1000f;
-        mMinimumVelocity[VERTICAL] = verticalMin / 1000f;
-        return this;
-    }
-
-    /**
-     * Sets the target scrolling velocity relative to the host view's
-     * dimensions.
-     * <p>
-     * If both relative and maximum velocities are specified, the maximum
-     * velocity will be used to clamp the calculated relative velocity.
-     *
-     * @param horizontal The target horizontal velocity as a fraction of the
-     *            host view width per second, or {@link #RELATIVE_UNSPECIFIED}
-     *            to ignore.
-     * @param vertical The target vertical velocity as a fraction of the host
-     *            view height per second, or {@link #RELATIVE_UNSPECIFIED} to
-     *            ignore.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setRelativeVelocity(float horizontal, float vertical) {
-        mRelativeVelocity[HORIZONTAL] = horizontal / 1000f;
-        mRelativeVelocity[VERTICAL] = vertical / 1000f;
-        return this;
-    }
-
-    /**
-     * Sets the activation edge type, one of:
-     * <ul>
-     * <li>{@link #EDGE_TYPE_INSIDE} for edges that respond to touches inside
-     * the bounds of the host view. If touch moves outside the bounds, scrolling
-     * will stop.
-     * <li>{@link #EDGE_TYPE_INSIDE_EXTEND} for inside edges that continued to
-     * scroll when touch moves outside the bounds of the host view.
-     * <li>{@link #EDGE_TYPE_OUTSIDE} for edges that only respond to touches
-     * that move outside the bounds of the host view.
-     * </ul>
-     *
-     * @param type The type of edge to use.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setEdgeType(int type) {
-        mEdgeType = type;
-        return this;
-    }
-
-    /**
-     * Sets the activation edge size relative to the host view's dimensions.
-     * <p>
-     * If both relative and maximum edges are specified, the maximum edge will
-     * be used to constrain the calculated relative edge size.
-     *
-     * @param horizontal The horizontal edge size as a fraction of the host view
-     *            width, or {@link #RELATIVE_UNSPECIFIED} to always use the
-     *            maximum value.
-     * @param vertical The vertical edge size as a fraction of the host view
-     *            height, or {@link #RELATIVE_UNSPECIFIED} to always use the
-     *            maximum value.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setRelativeEdges(float horizontal, float vertical) {
-        mRelativeEdges[HORIZONTAL] = horizontal;
-        mRelativeEdges[VERTICAL] = vertical;
-        return this;
-    }
-
-    /**
-     * Sets the absolute maximum edge size.
-     * <p>
-     * If relative edge size is not specified, activation edges will always be
-     * the maximum edge size. If both relative and maximum edges are specified,
-     * the maximum edge will be used to constrain the calculated relative edge
-     * size.
-     *
-     * @param horizontalMax The maximum horizontal edge size in pixels, or
-     *            {@link #NO_MAX} to use the unconstrained calculated relative
-     *            value.
-     * @param verticalMax The maximum vertical edge size in pixels, or
-     *            {@link #NO_MAX} to use the unconstrained calculated relative
-     *            value.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setMaximumEdges(float horizontalMax, float verticalMax) {
-        mMaximumEdges[HORIZONTAL] = horizontalMax;
-        mMaximumEdges[VERTICAL] = verticalMax;
-        return this;
-    }
-
-    /**
-     * Sets the delay after entering an activation edge before activation of
-     * auto-scrolling. By default, the activation delay is set to
-     * {@link ViewConfiguration#getTapTimeout()}.
-     * <p>
-     * Specifying a delay of zero will start auto-scrolling immediately after
-     * the touch position enters an activation edge.
-     *
-     * @param delayMillis The activation delay in milliseconds.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setActivationDelay(int delayMillis) {
-        mActivationDelay = delayMillis;
-        return this;
-    }
-
-    /**
-     * Sets the amount of time after activation of auto-scrolling that is takes
-     * to reach target velocity for the current touch position.
-     * <p>
-     * Specifying a duration greater than zero prevents sudden jumps in
-     * velocity.
-     *
-     * @param durationMillis The ramp-up duration in milliseconds.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setRampUpDuration(int durationMillis) {
-        mScroller.setRampUpDuration(durationMillis);
-        return this;
-    }
-
-    /**
-     * Sets the amount of time after de-activation of auto-scrolling that is
-     * takes to slow to a stop.
-     * <p>
-     * Specifying a duration greater than zero prevents sudden jumps in
-     * velocity.
-     *
-     * @param durationMillis The ramp-down duration in milliseconds.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    @NonNull
-    public AutoScrollHelper setRampDownDuration(int durationMillis) {
-        mScroller.setRampDownDuration(durationMillis);
-        return this;
-    }
-
-    /**
-     * Handles touch events by activating automatic scrolling, adjusting scroll
-     * velocity, or stopping.
-     * <p>
-     * If {@link #isExclusive()} is false, always returns false so that
-     * the host view may handle touch events. Otherwise, returns true when
-     * automatic scrolling is active and false otherwise.
-     */
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        if (!mEnabled) {
-            return false;
-        }
-
-        final int action = event.getActionMasked();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mNeedsCancel = true;
-                mAlreadyDelayed = false;
-                // $FALL-THROUGH$
-            case MotionEvent.ACTION_MOVE:
-                final float xTargetVelocity = computeTargetVelocity(
-                        HORIZONTAL, event.getX(), v.getWidth(), mTarget.getWidth());
-                final float yTargetVelocity = computeTargetVelocity(
-                        VERTICAL, event.getY(), v.getHeight(), mTarget.getHeight());
-                mScroller.setTargetVelocity(xTargetVelocity, yTargetVelocity);
-
-                // If the auto scroller was not previously active, but it should
-                // be, then update the state and start animations.
-                if (!mAnimating && shouldAnimate()) {
-                    startAnimating();
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                requestStop();
-                break;
-        }
-
-        return mExclusive && mAnimating;
-    }
-
-    /**
-     * @return whether the target is able to scroll in the requested direction
-     */
-    boolean shouldAnimate() {
-        final ClampedScroller scroller = mScroller;
-        final int verticalDirection = scroller.getVerticalDirection();
-        final int horizontalDirection = scroller.getHorizontalDirection();
-
-        return (verticalDirection != 0 && canTargetScrollVertically(verticalDirection))
-                || (horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection));
-    }
-
-    /**
-     * Starts the scroll animation.
-     */
-    private void startAnimating() {
-        if (mRunnable == null) {
-            mRunnable = new ScrollAnimationRunnable();
-        }
-
-        mAnimating = true;
-        mNeedsReset = true;
-
-        if (!mAlreadyDelayed && mActivationDelay > 0) {
-            ViewCompat.postOnAnimationDelayed(mTarget, mRunnable, mActivationDelay);
-        } else {
-            mRunnable.run();
-        }
-
-        // If we start animating again before the user lifts their finger, we
-        // already know it's not a tap and don't need an activation delay.
-        mAlreadyDelayed = true;
-    }
-
-    /**
-     * Requests that the scroll animation slow to a stop. If there is an
-     * activation delay, this may occur between posting the animation and
-     * actually running it.
-     */
-    private void requestStop() {
-        if (mNeedsReset) {
-            // The animation has been posted, but hasn't run yet. Manually
-            // stopping animation will prevent it from running.
-            mAnimating = false;
-        } else {
-            mScroller.requestStop();
-        }
-    }
-
-    private float computeTargetVelocity(
-            int direction, float coordinate, float srcSize, float dstSize) {
-        final float relativeEdge = mRelativeEdges[direction];
-        final float maximumEdge = mMaximumEdges[direction];
-        final float value = getEdgeValue(relativeEdge, srcSize, maximumEdge, coordinate);
-        if (value == 0) {
-            // The edge in this direction is not activated.
-            return 0;
-        }
-
-        final float relativeVelocity = mRelativeVelocity[direction];
-        final float minimumVelocity = mMinimumVelocity[direction];
-        final float maximumVelocity = mMaximumVelocity[direction];
-        final float targetVelocity = relativeVelocity * dstSize;
-
-        // Target velocity is adjusted for interpolated edge position, then
-        // clamped to the minimum and maximum values. Later, this value will be
-        // adjusted for time-based acceleration.
-        if (value > 0) {
-            return constrain(value * targetVelocity, minimumVelocity, maximumVelocity);
-        } else {
-            return -constrain(-value * targetVelocity, minimumVelocity, maximumVelocity);
-        }
-    }
-
-    /**
-     * Override this method to scroll the target view by the specified number of
-     * pixels.
-     *
-     * @param deltaX The number of pixels to scroll by horizontally.
-     * @param deltaY The number of pixels to scroll by vertically.
-     */
-    public abstract void scrollTargetBy(int deltaX, int deltaY);
-
-    /**
-     * Override this method to return whether the target view can be scrolled
-     * horizontally in a certain direction.
-     *
-     * @param direction Negative to check scrolling left, positive to check
-     *            scrolling right.
-     * @return true if the target view is able to horizontally scroll in the
-     *         specified direction.
-     */
-    public abstract boolean canTargetScrollHorizontally(int direction);
-
-    /**
-     * Override this method to return whether the target view can be scrolled
-     * vertically in a certain direction.
-     *
-     * @param direction Negative to check scrolling up, positive to check
-     *            scrolling down.
-     * @return true if the target view is able to vertically scroll in the
-     *         specified direction.
-     */
-    public abstract boolean canTargetScrollVertically(int direction);
-
-    /**
-     * Returns the interpolated position of a touch point relative to an edge
-     * defined by its relative inset, its maximum absolute inset, and the edge
-     * interpolator.
-     *
-     * @param relativeValue The size of the inset relative to the total size.
-     * @param size Total size.
-     * @param maxValue The maximum size of the inset, used to clamp (relative *
-     *            total).
-     * @param current Touch position within within the total size.
-     * @return Interpolated value of the touch position within the edge.
-     */
-    private float getEdgeValue(float relativeValue, float size, float maxValue, float current) {
-        // For now, leading and trailing edges are always the same size.
-        final float edgeSize = constrain(relativeValue * size, NO_MIN, maxValue);
-        final float valueLeading = constrainEdgeValue(current, edgeSize);
-        final float valueTrailing = constrainEdgeValue(size - current, edgeSize);
-        final float value = (valueTrailing - valueLeading);
-        final float interpolated;
-        if (value < 0) {
-            interpolated = -mEdgeInterpolator.getInterpolation(-value);
-        } else if (value > 0) {
-            interpolated = mEdgeInterpolator.getInterpolation(value);
-        } else {
-            return 0;
-        }
-
-        return constrain(interpolated, -1, 1);
-    }
-
-    private float constrainEdgeValue(float current, float leading) {
-        if (leading == 0) {
-            return 0;
-        }
-
-        switch (mEdgeType) {
-            case EDGE_TYPE_INSIDE:
-            case EDGE_TYPE_INSIDE_EXTEND:
-                if (current < leading) {
-                    if (current >= 0) {
-                        // Movement up to the edge is scaled.
-                        return 1f - current / leading;
-                    } else if (mAnimating && (mEdgeType == EDGE_TYPE_INSIDE_EXTEND)) {
-                        // Movement beyond the edge is always maximum.
-                        return 1f;
-                    }
-                }
-                break;
-            case EDGE_TYPE_OUTSIDE:
-                if (current < 0) {
-                    // Movement beyond the edge is scaled.
-                    return current / -leading;
-                }
-                break;
-        }
-
-        return 0;
-    }
-
-    static int constrain(int value, int min, int max) {
-        if (value > max) {
-            return max;
-        } else if (value < min) {
-            return min;
-        } else {
-            return value;
-        }
-    }
-
-    static float constrain(float value, float min, float max) {
-        if (value > max) {
-            return max;
-        } else if (value < min) {
-            return min;
-        } else {
-            return value;
-        }
-    }
-
-    /**
-     * Sends a {@link MotionEvent#ACTION_CANCEL} event to the target view,
-     * canceling any ongoing touch events.
-     */
-    void cancelTargetTouch() {
-        final long eventTime = SystemClock.uptimeMillis();
-        final MotionEvent cancel = MotionEvent.obtain(
-                eventTime, eventTime, MotionEvent.ACTION_CANCEL, 0, 0, 0);
-        mTarget.onTouchEvent(cancel);
-        cancel.recycle();
-    }
-
-    private class ScrollAnimationRunnable implements Runnable {
-        ScrollAnimationRunnable() {
-        }
-
-        @Override
-        public void run() {
-            if (!mAnimating) {
-                return;
-            }
-
-            if (mNeedsReset) {
-                mNeedsReset = false;
-                mScroller.start();
-            }
-
-            final ClampedScroller scroller = mScroller;
-            if (scroller.isFinished() || !shouldAnimate()) {
-                mAnimating = false;
-                return;
-            }
-
-            if (mNeedsCancel) {
-                mNeedsCancel = false;
-                cancelTargetTouch();
-            }
-
-            scroller.computeScrollDelta();
-
-            final int deltaX = scroller.getDeltaX();
-            final int deltaY = scroller.getDeltaY();
-            scrollTargetBy(deltaX,  deltaY);
-
-            // Keep going until the scroller has permanently stopped.
-            ViewCompat.postOnAnimation(mTarget, this);
-        }
-    }
-
-    /**
-     * Scroller whose velocity follows the curve of an {@link Interpolator} and
-     * is clamped to the interpolated 0f value before starting and the
-     * interpolated 1f value after a specified duration.
-     */
-    private static class ClampedScroller {
-        private int mRampUpDuration;
-        private int mRampDownDuration;
-        private float mTargetVelocityX;
-        private float mTargetVelocityY;
-
-        private long mStartTime;
-
-        private long mDeltaTime;
-        private int mDeltaX;
-        private int mDeltaY;
-
-        private long mStopTime;
-        private float mStopValue;
-        private int mEffectiveRampDown;
-
-        /**
-         * Creates a new ramp-up scroller that reaches full velocity after a
-         * specified duration.
-         */
-        ClampedScroller() {
-            mStartTime = Long.MIN_VALUE;
-            mStopTime = -1;
-            mDeltaTime = 0;
-            mDeltaX = 0;
-            mDeltaY = 0;
-        }
-
-        public void setRampUpDuration(int durationMillis) {
-            mRampUpDuration = durationMillis;
-        }
-
-        public void setRampDownDuration(int durationMillis) {
-            mRampDownDuration = durationMillis;
-        }
-
-        /**
-         * Starts the scroller at the current animation time.
-         */
-        public void start() {
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mStopTime = -1;
-            mDeltaTime = mStartTime;
-            mStopValue = 0.5f;
-            mDeltaX = 0;
-            mDeltaY = 0;
-        }
-
-        /**
-         * Stops the scroller at the current animation time.
-         */
-        public void requestStop() {
-            final long currentTime = AnimationUtils.currentAnimationTimeMillis();
-            mEffectiveRampDown = constrain((int) (currentTime - mStartTime), 0, mRampDownDuration);
-            mStopValue = getValueAt(currentTime);
-            mStopTime = currentTime;
-        }
-
-        public boolean isFinished() {
-            return mStopTime > 0
-                    && AnimationUtils.currentAnimationTimeMillis() > mStopTime + mEffectiveRampDown;
-        }
-
-        private float getValueAt(long currentTime) {
-            if (currentTime < mStartTime) {
-                return 0f;
-            } else if (mStopTime < 0 || currentTime < mStopTime) {
-                final long elapsedSinceStart = currentTime - mStartTime;
-                return 0.5f * constrain(elapsedSinceStart / (float) mRampUpDuration, 0, 1);
-            } else {
-                final long elapsedSinceEnd = currentTime - mStopTime;
-                return (1 - mStopValue) + mStopValue
-                        * constrain(elapsedSinceEnd / (float) mEffectiveRampDown, 0, 1);
-            }
-        }
-
-        /**
-         * Interpolates the value along a parabolic curve corresponding to the equation
-         * <code>y = -4x * (x-1)</code>.
-         *
-         * @param value The value to interpolate, between 0 and 1.
-         * @return the interpolated value, between 0 and 1.
-         */
-        private float interpolateValue(float value) {
-            return -4 * value * value + 4 * value;
-        }
-
-        /**
-         * Computes the current scroll deltas. This usually only be called after
-         * starting the scroller with {@link #start()}.
-         *
-         * @see #getDeltaX()
-         * @see #getDeltaY()
-         */
-        public void computeScrollDelta() {
-            if (mDeltaTime == 0) {
-                throw new RuntimeException("Cannot compute scroll delta before calling start()");
-            }
-
-            final long currentTime = AnimationUtils.currentAnimationTimeMillis();
-            final float value = getValueAt(currentTime);
-            final float scale = interpolateValue(value);
-            final long elapsedSinceDelta = currentTime - mDeltaTime;
-
-            mDeltaTime = currentTime;
-            mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX);
-            mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY);
-        }
-
-        /**
-         * Sets the target velocity for this scroller.
-         *
-         * @param x The target X velocity in pixels per millisecond.
-         * @param y The target Y velocity in pixels per millisecond.
-         */
-        public void setTargetVelocity(float x, float y) {
-            mTargetVelocityX = x;
-            mTargetVelocityY = y;
-        }
-
-        public int getHorizontalDirection() {
-            return (int) (mTargetVelocityX / Math.abs(mTargetVelocityX));
-        }
-
-        public int getVerticalDirection() {
-            return (int) (mTargetVelocityY / Math.abs(mTargetVelocityY));
-        }
-
-        /**
-         * The distance traveled in the X-coordinate computed by the last call
-         * to {@link #computeScrollDelta()}.
-         */
-        public int getDeltaX() {
-            return mDeltaX;
-        }
-
-        /**
-         * The distance traveled in the Y-coordinate computed by the last call
-         * to {@link #computeScrollDelta()}.
-         */
-        public int getDeltaY() {
-            return mDeltaY;
-        }
-    }
-}
diff --git a/android/support/v4/widget/AutoSizeableTextView.java b/android/support/v4/widget/AutoSizeableTextView.java
deleted file mode 100644
index a0430c6..0000000
--- a/android/support/v4/widget/AutoSizeableTextView.java
+++ /dev/null
@@ -1,146 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
-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 {
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    boolean PLATFORM_SUPPORTS_AUTOSIZE = BuildCompat.isAtLeastOMR1();
-
-    /**
-     * 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/android/support/v4/widget/CircleImageView.java b/android/support/v4/widget/CircleImageView.java
deleted file mode 100644
index 24a175d..0000000
--- a/android/support/v4/widget/CircleImageView.java
+++ /dev/null
@@ -1,158 +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.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.RadialGradient;
-import android.graphics.Shader;
-import android.graphics.drawable.ShapeDrawable;
-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;
-
-/**
- * Private class created to work around issues with AnimationListeners being
- * called before the animation is actually complete and support shadows on older
- * platforms.
- */
-class CircleImageView extends ImageView {
-
-    private static final int KEY_SHADOW_COLOR = 0x1E000000;
-    private static final int FILL_SHADOW_COLOR = 0x3D000000;
-    // PX
-    private static final float X_OFFSET = 0f;
-    private static final float Y_OFFSET = 1.75f;
-    private static final float SHADOW_RADIUS = 3.5f;
-    private static final int SHADOW_ELEVATION = 4;
-
-    private Animation.AnimationListener mListener;
-    int mShadowRadius;
-
-    CircleImageView(Context context, int color) {
-        super(context);
-        final float density = getContext().getResources().getDisplayMetrics().density;
-        final int shadowYOffset = (int) (density * Y_OFFSET);
-        final int shadowXOffset = (int) (density * X_OFFSET);
-
-        mShadowRadius = (int) (density * SHADOW_RADIUS);
-
-        ShapeDrawable circle;
-        if (elevationSupported()) {
-            circle = new ShapeDrawable(new OvalShape());
-            ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
-        } else {
-            OvalShape oval = new OvalShadow(mShadowRadius);
-            circle = new ShapeDrawable(oval);
-            setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint());
-            circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
-                    KEY_SHADOW_COLOR);
-            final int padding = mShadowRadius;
-            // set padding so the inner image sits correctly within the shadow.
-            setPadding(padding, padding, padding, padding);
-        }
-        circle.getPaint().setColor(color);
-        ViewCompat.setBackground(this, circle);
-    }
-
-    private boolean elevationSupported() {
-        return android.os.Build.VERSION.SDK_INT >= 21;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (!elevationSupported()) {
-            setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight()
-                    + mShadowRadius * 2);
-        }
-    }
-
-    public void setAnimationListener(Animation.AnimationListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public void onAnimationStart() {
-        super.onAnimationStart();
-        if (mListener != null) {
-            mListener.onAnimationStart(getAnimation());
-        }
-    }
-
-    @Override
-    public void onAnimationEnd() {
-        super.onAnimationEnd();
-        if (mListener != null) {
-            mListener.onAnimationEnd(getAnimation());
-        }
-    }
-
-    /**
-     * Update the background color of the circle image view.
-     *
-     * @param colorRes Id of a color resource.
-     */
-    public void setBackgroundColorRes(int colorRes) {
-        setBackgroundColor(ContextCompat.getColor(getContext(), colorRes));
-    }
-
-    @Override
-    public void setBackgroundColor(int color) {
-        if (getBackground() instanceof ShapeDrawable) {
-            ((ShapeDrawable) getBackground()).getPaint().setColor(color);
-        }
-    }
-
-    private class OvalShadow extends OvalShape {
-        private RadialGradient mRadialGradient;
-        private Paint mShadowPaint;
-
-        OvalShadow(int shadowRadius) {
-            super();
-            mShadowPaint = new Paint();
-            mShadowRadius = shadowRadius;
-            updateRadialGradient((int) rect().width());
-        }
-
-        @Override
-        protected void onResize(float width, float height) {
-            super.onResize(width, height);
-            updateRadialGradient((int) width);
-        }
-
-        @Override
-        public void draw(Canvas canvas, Paint paint) {
-            final int viewWidth = CircleImageView.this.getWidth();
-            final int viewHeight = CircleImageView.this.getHeight();
-            canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2, mShadowPaint);
-            canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2 - mShadowRadius, paint);
-        }
-
-        private void updateRadialGradient(int diameter) {
-            mRadialGradient = new RadialGradient(diameter / 2, diameter / 2,
-                    mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT },
-                    null, Shader.TileMode.CLAMP);
-            mShadowPaint.setShader(mRadialGradient);
-        }
-    }
-}
diff --git a/android/support/v4/widget/CircularProgressDrawable.java b/android/support/v4/widget/CircularProgressDrawable.java
deleted file mode 100644
index 2055669..0000000
--- a/android/support/v4/widget/CircularProgressDrawable.java
+++ /dev/null
@@ -1,947 +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 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(@NonNull 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(@NonNull Paint.Cap strokeCap) {
-        mRing.setStrokeCap(strokeCap);
-        invalidateSelf();
-    }
-
-    /**
-     * Returns the stroke cap of the progress spinner.
-     *
-     * @return stroke cap
-     */
-    @NonNull
-    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
-     */
-    @NonNull
-    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(@NonNull 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/android/support/v4/widget/CompoundButtonCompat.java b/android/support/v4/widget/CompoundButtonCompat.java
deleted file mode 100644
index a858b09..0000000
--- a/android/support/v4/widget/CompoundButtonCompat.java
+++ /dev/null
@@ -1,198 +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.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}.
- */
-public final class CompoundButtonCompat {
-
-    private static final CompoundButtonCompatBaseImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 23) {
-            IMPL = new CompoundButtonCompatApi23Impl();
-        } else if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new CompoundButtonCompatApi21Impl();
-        } else {
-            IMPL = new CompoundButtonCompatBaseImpl();
-        }
-    }
-
-    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;
-        }
-    }
-
-    @RequiresApi(21)
-    static class CompoundButtonCompatApi21Impl extends CompoundButtonCompatBaseImpl {
-        @Override
-        public void setButtonTintList(CompoundButton button, ColorStateList tint) {
-            button.setButtonTintList(tint);
-        }
-
-        @Override
-        public ColorStateList getButtonTintList(CompoundButton button) {
-            return button.getButtonTintList();
-        }
-
-        @Override
-        public void setButtonTintMode(CompoundButton button, PorterDuff.Mode tintMode) {
-            button.setButtonTintMode(tintMode);
-        }
-
-        @Override
-        public PorterDuff.Mode getButtonTintMode(CompoundButton button) {
-            return button.getButtonTintMode();
-        }
-    }
-
-    @RequiresApi(23)
-    static class CompoundButtonCompatApi23Impl extends CompoundButtonCompatApi21Impl {
-        @Override
-        public Drawable getButtonDrawable(CompoundButton button) {
-            return button.getButtonDrawable();
-        }
-    }
-
-    private CompoundButtonCompat() {}
-
-    /**
-     * 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 CompoundButton#setButtonDrawable(Drawable)} should
-     * automatically mutate the drawable and apply the specified tint and tint
-     * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     *
-     * @see #setButtonTintList(CompoundButton, ColorStateList)
-     */
-    public static void setButtonTintList(@NonNull CompoundButton button, @Nullable ColorStateList tint) {
-        IMPL.setButtonTintList(button, tint);
-    }
-
-    /**
-     * Returns the tint applied to the button drawable
-     *
-     * @see #setButtonTintList(CompoundButton, ColorStateList)
-     */
-    @Nullable
-    public static ColorStateList getButtonTintList(@NonNull CompoundButton button) {
-        return IMPL.getButtonTintList(button);
-    }
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setButtonTintList(CompoundButton, 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 #getButtonTintMode(CompoundButton)
-     * @see DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)
-     */
-    public static void setButtonTintMode(@NonNull CompoundButton button,
-            @Nullable PorterDuff.Mode tintMode) {
-        IMPL.setButtonTintMode(button, tintMode);
-    }
-
-    /**
-     * @return the blending mode used to apply the tint to the button drawable
-     * @attr name android:buttonTintMode
-     * @see #setButtonTintMode(CompoundButton, PorterDuff.Mode)
-     */
-    @Nullable
-    public static PorterDuff.Mode getButtonTintMode(@NonNull CompoundButton button) {
-        return IMPL.getButtonTintMode(button);
-    }
-
-    /**
-     * Returns the drawable used as the compound button image
-     *
-     * @see CompoundButton#setButtonDrawable(Drawable)
-     */
-    @Nullable
-    public static Drawable getButtonDrawable(@NonNull CompoundButton button) {
-        return IMPL.getButtonDrawable(button);
-    }
-}
diff --git a/android/support/v4/widget/ContentLoadingProgressBar.java b/android/support/v4/widget/ContentLoadingProgressBar.java
deleted file mode 100644
index 631bec5..0000000
--- a/android/support/v4/widget/ContentLoadingProgressBar.java
+++ /dev/null
@@ -1,132 +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.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ProgressBar;
-
-/**
- * ContentLoadingProgressBar implements a ProgressBar that waits a minimum time to be
- * dismissed before showing. Once visible, the progress bar will be visible for
- * a minimum amount of time to avoid "flashes" in the UI when an event could take
- * a largely variable time to complete (from none, to a user perceivable amount)
- */
-public class ContentLoadingProgressBar extends ProgressBar {
-    private static final int MIN_SHOW_TIME = 500; // ms
-    private static final int MIN_DELAY = 500; // ms
-
-    long mStartTime = -1;
-
-    boolean mPostedHide = false;
-
-    boolean mPostedShow = false;
-
-    boolean mDismissed = false;
-
-    private final Runnable mDelayedHide = new Runnable() {
-
-        @Override
-        public void run() {
-            mPostedHide = false;
-            mStartTime = -1;
-            setVisibility(View.GONE);
-        }
-    };
-
-    private final Runnable mDelayedShow = new Runnable() {
-
-        @Override
-        public void run() {
-            mPostedShow = false;
-            if (!mDismissed) {
-                mStartTime = System.currentTimeMillis();
-                setVisibility(View.VISIBLE);
-            }
-        }
-    };
-
-    public ContentLoadingProgressBar(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public ContentLoadingProgressBar(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        removeCallbacks();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        removeCallbacks();
-    }
-
-    private void removeCallbacks() {
-        removeCallbacks(mDelayedHide);
-        removeCallbacks(mDelayedShow);
-    }
-
-    /**
-     * Hide the progress view if it is visible. The progress view will not be
-     * hidden until it has been shown for at least a minimum show time. If the
-     * progress view was not yet visible, cancels showing the progress view.
-     */
-    public synchronized void hide() {
-        mDismissed = true;
-        removeCallbacks(mDelayedShow);
-        mPostedShow = false;
-        long diff = System.currentTimeMillis() - mStartTime;
-        if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
-            // The progress spinner has been shown long enough
-            // OR was not shown yet. If it wasn't shown yet,
-            // it will just never be shown.
-            setVisibility(View.GONE);
-        } else {
-            // The progress spinner is shown, but not long enough,
-            // so put a delayed message in to hide it when its been
-            // shown long enough.
-            if (!mPostedHide) {
-                postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
-                mPostedHide = true;
-            }
-        }
-    }
-
-    /**
-     * Show the progress view after waiting for a minimum delay. If
-     * during that time, hide() is called, the view is never made visible.
-     */
-    public synchronized void show() {
-        // Reset the start time.
-        mStartTime = -1;
-        mDismissed = false;
-        removeCallbacks(mDelayedHide);
-        mPostedHide = false;
-        if (!mPostedShow) {
-            postDelayed(mDelayedShow, MIN_DELAY);
-            mPostedShow = true;
-        }
-    }
-}
diff --git a/android/support/v4/widget/CursorAdapter.java b/android/support/v4/widget/CursorAdapter.java
deleted file mode 100644
index 3ea6fc8..0000000
--- a/android/support/v4/widget/CursorAdapter.java
+++ /dev/null
@@ -1,507 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.support.annotation.RestrictTo;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.Filter;
-import android.widget.FilterQueryProvider;
-import android.widget.Filterable;
-
-/**
- * Static library support version of the framework's {@link android.widget.CursorAdapter}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public abstract class CursorAdapter extends BaseAdapter implements Filterable,
-        CursorFilter.CursorFilterClient {
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean mDataValid;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean mAutoRequery;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected Cursor mCursor;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected Context mContext;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected int mRowIDColumn;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected ChangeObserver mChangeObserver;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected DataSetObserver mDataSetObserver;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected CursorFilter mCursorFilter;
-    /**
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected FilterQueryProvider mFilterQueryProvider;
-
-    /**
-     * If set the adapter will call requery() on the cursor whenever a content change
-     * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
-     *
-     * @deprecated This option is discouraged, as it results in Cursor queries
-     * being performed on the application's UI thread and thus can cause poor
-     * responsiveness or even Application Not Responding errors.  As an alternative,
-     * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
-     */
-    @Deprecated
-    public static final int FLAG_AUTO_REQUERY = 0x01;
-
-    /**
-     * If set the adapter will register a content observer on the cursor and will call
-     * {@link #onContentChanged()} when a notification comes in.  Be careful when
-     * using this flag: you will need to unset the current Cursor from the adapter
-     * to avoid leaks due to its registered observers.  This flag is not needed
-     * when using a CursorAdapter with a
-     * {@link android.content.CursorLoader}.
-     */
-    public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
-
-    /**
-     * Constructor that always enables auto-requery.
-     *
-     * @deprecated This option is discouraged, as it results in Cursor queries
-     * being performed on the application's UI thread and thus can cause poor
-     * responsiveness or even Application Not Responding errors.  As an alternative,
-     * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
-     *
-     * @param c The cursor from which to get the data.
-     * @param context The context
-     */
-    @Deprecated
-    public CursorAdapter(Context context, Cursor c) {
-        init(context, c, FLAG_AUTO_REQUERY);
-    }
-
-    /**
-     * Constructor that allows control over auto-requery.  It is recommended
-     * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
-     * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
-     * will always be set.
-     *
-     * @param c The cursor from which to get the data.
-     * @param context The context
-     * @param autoRequery If true the adapter will call requery() on the
-     *                    cursor whenever it changes so the most recent
-     *                    data is always displayed.  Using true here is discouraged.
-     */
-    public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
-        init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
-    }
-
-    /**
-     * Recommended constructor.
-     *
-     * @param c The cursor from which to get the data.
-     * @param context The context
-     * @param flags Flags used to determine the behavior of the adapter; may
-     * be any combination of {@link #FLAG_AUTO_REQUERY} and
-     * {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
-     */
-    public CursorAdapter(Context context, Cursor c, int flags) {
-        init(context, c, flags);
-    }
-
-    /**
-     * @deprecated Don't use this, use the normal constructor.  This will
-     * be removed in the future.
-     */
-    @Deprecated
-    protected void init(Context context, Cursor c, boolean autoRequery) {
-        init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
-    }
-
-    void init(Context context, Cursor c, int flags) {
-        if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
-            flags |= FLAG_REGISTER_CONTENT_OBSERVER;
-            mAutoRequery = true;
-        } else {
-            mAutoRequery = false;
-        }
-        boolean cursorPresent = c != null;
-        mCursor = c;
-        mDataValid = cursorPresent;
-        mContext = context;
-        mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
-        if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
-            mChangeObserver = new ChangeObserver();
-            mDataSetObserver = new MyDataSetObserver();
-        } else {
-            mChangeObserver = null;
-            mDataSetObserver = null;
-        }
-
-        if (cursorPresent) {
-            if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
-            if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
-        }
-    }
-
-    /**
-     * Returns the cursor.
-     * @return the cursor.
-     */
-    @Override
-    public Cursor getCursor() {
-        return mCursor;
-    }
-
-    /**
-     * @see android.widget.ListAdapter#getCount()
-     */
-    @Override
-    public int getCount() {
-        if (mDataValid && mCursor != null) {
-            return mCursor.getCount();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * @see android.widget.ListAdapter#getItem(int)
-     */
-    @Override
-    public Object getItem(int position) {
-        if (mDataValid && mCursor != null) {
-            mCursor.moveToPosition(position);
-            return mCursor;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @see android.widget.ListAdapter#getItemId(int)
-     */
-    @Override
-    public long getItemId(int position) {
-        if (mDataValid && mCursor != null) {
-            if (mCursor.moveToPosition(position)) {
-                return mCursor.getLong(mRowIDColumn);
-            } else {
-                return 0;
-            }
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return true;
-    }
-
-    /**
-     * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
-     */
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        if (!mDataValid) {
-            throw new IllegalStateException("this should only be called when the cursor is valid");
-        }
-        if (!mCursor.moveToPosition(position)) {
-            throw new IllegalStateException("couldn't move cursor to position " + position);
-        }
-        View v;
-        if (convertView == null) {
-            v = newView(mContext, mCursor, parent);
-        } else {
-            v = convertView;
-        }
-        bindView(v, mContext, mCursor);
-        return v;
-    }
-
-    @Override
-    public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        if (mDataValid) {
-            mCursor.moveToPosition(position);
-            View v;
-            if (convertView == null) {
-                v = newDropDownView(mContext, mCursor, parent);
-            } else {
-                v = convertView;
-            }
-            bindView(v, mContext, mCursor);
-            return v;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Makes a new view to hold the data pointed to by cursor.
-     * @param context Interface to application's global information
-     * @param cursor The cursor from which to get the data. The cursor is already
-     * moved to the correct position.
-     * @param parent The parent to which the new view is attached to
-     * @return the newly created view.
-     */
-    public abstract View newView(Context context, Cursor cursor, ViewGroup parent);
-
-    /**
-     * Makes a new drop down view to hold the data pointed to by cursor.
-     * @param context Interface to application's global information
-     * @param cursor The cursor from which to get the data. The cursor is already
-     * moved to the correct position.
-     * @param parent The parent to which the new view is attached to
-     * @return the newly created view.
-     */
-    public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
-        return newView(context, cursor, parent);
-    }
-
-    /**
-     * Bind an existing view to the data pointed to by cursor
-     * @param view Existing view, returned earlier by newView
-     * @param context Interface to application's global information
-     * @param cursor The cursor from which to get the data. The cursor is already
-     * moved to the correct position.
-     */
-    public abstract void bindView(View view, Context context, Cursor cursor);
-
-    /**
-     * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
-     * closed.
-     *
-     * @param cursor The new cursor to be used
-     */
-    @Override
-    public void changeCursor(Cursor cursor) {
-        Cursor old = swapCursor(cursor);
-        if (old != null) {
-            old.close();
-        }
-    }
-
-    /**
-     * Swap in a new Cursor, returning the old Cursor.  Unlike
-     * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
-     * closed.
-     *
-     * @param newCursor The new cursor to be used.
-     * @return Returns the previously set Cursor, or null if there was not one.
-     * If the given new Cursor is the same instance is the previously set
-     * Cursor, null is also returned.
-     */
-    public Cursor swapCursor(Cursor newCursor) {
-        if (newCursor == mCursor) {
-            return null;
-        }
-        Cursor oldCursor = mCursor;
-        if (oldCursor != null) {
-            if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
-            if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
-        }
-        mCursor = newCursor;
-        if (newCursor != null) {
-            if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
-            if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
-            mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
-            mDataValid = true;
-            // notify the observers about the new cursor
-            notifyDataSetChanged();
-        } else {
-            mRowIDColumn = -1;
-            mDataValid = false;
-            // notify the observers about the lack of a data set
-            notifyDataSetInvalidated();
-        }
-        return oldCursor;
-    }
-
-    /**
-     * <p>Converts the cursor into a CharSequence. Subclasses should override this
-     * method to convert their results. The default implementation returns an
-     * empty String for null values or the default String representation of
-     * the value.</p>
-     *
-     * @param cursor the cursor to convert to a CharSequence
-     * @return a CharSequence representing the value
-     */
-    @Override
-    public CharSequence convertToString(Cursor cursor) {
-        return cursor == null ? "" : cursor.toString();
-    }
-
-    /**
-     * Runs a query with the specified constraint. This query is requested
-     * by the filter attached to this adapter.
-     *
-     * The query is provided by a
-     * {@link android.widget.FilterQueryProvider}.
-     * If no provider is specified, the current cursor is not filtered and returned.
-     *
-     * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
-     * and the previous cursor is closed.
-     *
-     * This method is always executed on a background thread, not on the
-     * application's main thread (or UI thread.)
-     *
-     * Contract: when constraint is null or empty, the original results,
-     * prior to any filtering, must be returned.
-     *
-     * @param constraint the constraint with which the query must be filtered
-     *
-     * @return a Cursor representing the results of the new query
-     *
-     * @see #getFilter()
-     * @see #getFilterQueryProvider()
-     * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
-     */
-    @Override
-    public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
-        if (mFilterQueryProvider != null) {
-            return mFilterQueryProvider.runQuery(constraint);
-        }
-
-        return mCursor;
-    }
-
-    @Override
-    public Filter getFilter() {
-        if (mCursorFilter == null) {
-            mCursorFilter = new CursorFilter(this);
-        }
-        return mCursorFilter;
-    }
-
-    /**
-     * Returns the query filter provider used for filtering. When the
-     * provider is null, no filtering occurs.
-     *
-     * @return the current filter query provider or null if it does not exist
-     *
-     * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
-     * @see #runQueryOnBackgroundThread(CharSequence)
-     */
-    public FilterQueryProvider getFilterQueryProvider() {
-        return mFilterQueryProvider;
-    }
-
-    /**
-     * Sets the query filter provider used to filter the current Cursor.
-     * The provider's
-     * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)}
-     * method is invoked when filtering is requested by a client of
-     * this adapter.
-     *
-     * @param filterQueryProvider the filter query provider or null to remove it
-     *
-     * @see #getFilterQueryProvider()
-     * @see #runQueryOnBackgroundThread(CharSequence)
-     */
-    public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
-        mFilterQueryProvider = filterQueryProvider;
-    }
-
-    /**
-     * Called when the {@link ContentObserver} on the cursor receives a change notification.
-     * The default implementation provides the auto-requery logic, but may be overridden by
-     * sub classes.
-     *
-     * @see ContentObserver#onChange(boolean)
-     */
-    protected void onContentChanged() {
-        if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
-            if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
-            mDataValid = mCursor.requery();
-        }
-    }
-
-    private class ChangeObserver extends ContentObserver {
-        ChangeObserver() {
-            super(new Handler());
-        }
-
-        @Override
-        public boolean deliverSelfNotifications() {
-            return true;
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onContentChanged();
-        }
-    }
-
-    private class MyDataSetObserver extends DataSetObserver {
-        MyDataSetObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            mDataValid = true;
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public void onInvalidated() {
-            mDataValid = false;
-            notifyDataSetInvalidated();
-        }
-    }
-
-}
diff --git a/android/support/v4/widget/CursorFilter.java b/android/support/v4/widget/CursorFilter.java
deleted file mode 100644
index bb05879..0000000
--- a/android/support/v4/widget/CursorFilter.java
+++ /dev/null
@@ -1,71 +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.database.Cursor;
-import android.widget.Filter;
-
-/**
- * The CursorFilter delegates most of the work to the
- * {@link android.widget.CursorAdapter}. Subclasses should override these
- * delegate methods to run the queries and convert the results into String
- * that can be used by auto-completion widgets.
- */
-class CursorFilter extends Filter {
-
-    CursorFilterClient mClient;
-
-    interface CursorFilterClient {
-        CharSequence convertToString(Cursor cursor);
-        Cursor runQueryOnBackgroundThread(CharSequence constraint);
-        Cursor getCursor();
-        void changeCursor(Cursor cursor);
-    }
-
-    CursorFilter(CursorFilterClient client) {
-        mClient = client;
-    }
-
-    @Override
-    public CharSequence convertResultToString(Object resultValue) {
-        return mClient.convertToString((Cursor) resultValue);
-    }
-
-    @Override
-    protected FilterResults performFiltering(CharSequence constraint) {
-        Cursor cursor = mClient.runQueryOnBackgroundThread(constraint);
-
-        FilterResults results = new FilterResults();
-        if (cursor != null) {
-            results.count = cursor.getCount();
-            results.values = cursor;
-        } else {
-            results.count = 0;
-            results.values = null;
-        }
-        return results;
-    }
-
-    @Override
-    protected void publishResults(CharSequence constraint, FilterResults results) {
-        Cursor oldCursor = mClient.getCursor();
-
-        if (results.values != null && results.values != oldCursor) {
-            mClient.changeCursor((Cursor) results.values);
-        }
-    }
-}
diff --git a/android/support/v4/widget/DirectedAcyclicGraph.java b/android/support/v4/widget/DirectedAcyclicGraph.java
deleted file mode 100644
index 83c62c0..0000000
--- a/android/support/v4/widget/DirectedAcyclicGraph.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.util.Pools;
-import android.support.v4.util.SimpleArrayMap;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * A class which represents a simple directed acyclic graph.
- *
- * @param <T> Class for the data objects of this graph.
- *
- * @hide
- */
-@RestrictTo(LIBRARY)
-public final class DirectedAcyclicGraph<T> {
-    private final Pools.Pool<ArrayList<T>> mListPool = new Pools.SimplePool<>(10);
-    private final SimpleArrayMap<T, ArrayList<T>> mGraph = new SimpleArrayMap<>();
-
-    private final ArrayList<T> mSortResult = new ArrayList<>();
-    private final HashSet<T> mSortTmpMarked = new HashSet<>();
-
-    /**
-     * Add a node to the graph.
-     *
-     * <p>If the node already exists in the graph then this method is a no-op.</p>
-     *
-     * @param node the node to add
-     */
-    public void addNode(@NonNull T node) {
-        if (!mGraph.containsKey(node)) {
-            mGraph.put(node, null);
-        }
-    }
-
-    /**
-     * Returns true if the node is already present in the graph, false otherwise.
-     */
-    public boolean contains(@NonNull T node) {
-        return mGraph.containsKey(node);
-    }
-
-    /**
-     * Add an edge to the graph.
-     *
-     * <p>Both the given nodes should already have been added to the graph through
-     * {@link #addNode(Object)}.</p>
-     *
-     * @param node the parent node
-     * @param incomingEdge the node which has is an incoming edge to {@code node}
-     */
-    public void addEdge(@NonNull T node, @NonNull T incomingEdge) {
-        if (!mGraph.containsKey(node) || !mGraph.containsKey(incomingEdge)) {
-            throw new IllegalArgumentException("All nodes must be present in the graph before"
-                    + " being added as an edge");
-        }
-
-        ArrayList<T> edges = mGraph.get(node);
-        if (edges == null) {
-            // If edges is null, we should try and get one from the pool and add it to the graph
-            edges = getEmptyList();
-            mGraph.put(node, edges);
-        }
-        // Finally add the edge to the list
-        edges.add(incomingEdge);
-    }
-
-    /**
-     * Get any incoming edges from the given node.
-     *
-     * @return a list containing any incoming edges, or null if there are none.
-     */
-    @Nullable
-    public List getIncomingEdges(@NonNull T node) {
-        return mGraph.get(node);
-    }
-
-    /**
-     * Get any outgoing edges for the given node (i.e. nodes which have an incoming edge
-     * from the given node).
-     *
-     * @return a list containing any outgoing edges, or null if there are none.
-     */
-    @Nullable
-    public 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);
-            if (edges != null && edges.contains(node)) {
-                if (result == null) {
-                    result = new ArrayList<>();
-                }
-                result.add(mGraph.keyAt(i));
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Checks whether we have any outgoing edges for the given node (i.e. nodes which have
-     * an incoming edge from the given node).
-     *
-     * @return <code>true</code> if the node has any outgoing edges, <code>false</code>
-     * otherwise.
-     */
-    public boolean hasOutgoingEdges(@NonNull T node) {
-        for (int i = 0, size = mGraph.size(); i < size; i++) {
-            ArrayList<T> edges = mGraph.valueAt(i);
-            if (edges != null && edges.contains(node)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Clears the internal graph, and releases resources to pools.
-     */
-    public void clear() {
-        for (int i = 0, size = mGraph.size(); i < size; i++) {
-            ArrayList<T> edges = mGraph.valueAt(i);
-            if (edges != null) {
-                poolList(edges);
-            }
-        }
-        mGraph.clear();
-    }
-
-    /**
-     * Returns a topologically sorted list of the nodes in this graph. This uses the DFS algorithm
-     * as described by Cormen et al. (2001). If this graph contains cyclic dependencies then this
-     * method will throw a {@link RuntimeException}.
-     *
-     * <p>The resulting list will be ordered such that index 0 will contain the node at the bottom
-     * of the graph. The node at the end of the list will have no dependencies on other nodes.</p>
-     */
-    @NonNull
-    public ArrayList<T> getSortedList() {
-        mSortResult.clear();
-        mSortTmpMarked.clear();
-
-        // Start a DFS from each node in the graph
-        for (int i = 0, size = mGraph.size(); i < size; i++) {
-            dfs(mGraph.keyAt(i), mSortResult, mSortTmpMarked);
-        }
-
-        return mSortResult;
-    }
-
-    private void dfs(final T node, final ArrayList<T> result, final HashSet<T> tmpMarked) {
-        if (result.contains(node)) {
-            // We've already seen and added the node to the result list, skip...
-            return;
-        }
-        if (tmpMarked.contains(node)) {
-            throw new RuntimeException("This graph contains cyclic dependencies");
-        }
-        // Temporarily mark the node
-        tmpMarked.add(node);
-        // Recursively dfs all of the node's edges
-        final ArrayList<T> edges = mGraph.get(node);
-        if (edges != null) {
-            for (int i = 0, size = edges.size(); i < size; i++) {
-                dfs(edges.get(i), result, tmpMarked);
-            }
-        }
-        // Unmark the node from the temporary list
-        tmpMarked.remove(node);
-        // Finally add it to the result list
-        result.add(node);
-    }
-
-    /**
-     * Returns the size of the graph
-     */
-    int size() {
-        return mGraph.size();
-    }
-
-    @NonNull
-    private ArrayList<T> getEmptyList() {
-        ArrayList<T> list = mListPool.acquire();
-        if (list == null) {
-            list = new ArrayList<>();
-        }
-        return list;
-    }
-
-    private void poolList(@NonNull ArrayList<T> list) {
-        list.clear();
-        mListPool.release(list);
-    }
-}
diff --git a/android/support/v4/widget/DrawerLayout.java b/android/support/v4/widget/DrawerLayout.java
deleted file mode 100644
index aa2077d..0000000
--- a/android/support/v4/widget/DrawerLayout.java
+++ /dev/null
@@ -1,2384 +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 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;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-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.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.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-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;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * DrawerLayout acts as a top-level container for window content that allows for
- * interactive "drawer" views to be pulled out from one or both vertical edges of the window.
- *
- * <p>Drawer positioning and layout is controlled using the <code>android:layout_gravity</code>
- * attribute on child views corresponding to which side of the view you want the drawer
- * to emerge from: left or right (or start/end on platform versions that support layout direction.)
- * Note that you can only have one drawer view for each vertical edge of the window. If your
- * layout configures more than one drawer view per vertical edge of the window, an exception will
- * be thrown at runtime.
- * </p>
- *
- * <p>To use a DrawerLayout, position your primary content view as the first child with
- * width and height of <code>match_parent</code> and no <code>layout_gravity></code>.
- * Add drawers as child views after the main content view and set the <code>layout_gravity</code>
- * appropriately. Drawers commonly use <code>match_parent</code> for height with a fixed width.</p>
- *
- * <p>{@link DrawerListener} can be used to monitor the state and motion of drawer views.
- * Avoid performing expensive operations such as layout during animation as it can cause
- * stuttering; try to perform expensive operations during the {@link #STATE_IDLE} state.
- * {@link SimpleDrawerListener} offers default/no-op implementations of each callback method.</p>
- *
- * <p>As per the <a href="{@docRoot}design/patterns/navigation-drawer.html">Android Design
- * guide</a>, any drawers positioned to the left/start should
- * always contain content for navigating around the application, whereas any drawers
- * positioned to the right/end should always contain actions to take on the current content.
- * This preserves the same navigation left, actions right structure present in the Action Bar
- * and elsewhere.</p>
- *
- * <p>For more information about how to use DrawerLayout, read <a
- * href="{@docRoot}training/implementing-navigation/nav-drawer.html">Creating a Navigation
- * Drawer</a>.</p>
- */
-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 {}
-
-    /**
-     * Indicates that any drawers are in an idle, settled state. No animation is in progress.
-     */
-    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;
-
-    /**
-     * Indicates that a drawer is currently being dragged by the user.
-     */
-    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;
-
-    /**
-     * Indicates that a drawer is in the process of settling to a final position.
-     */
-    public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;
-
-    @IntDef({LOCK_MODE_UNLOCKED, LOCK_MODE_LOCKED_CLOSED, LOCK_MODE_LOCKED_OPEN,
-            LOCK_MODE_UNDEFINED})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface LockMode {}
-
-    /**
-     * The drawer is unlocked.
-     */
-    public static final int LOCK_MODE_UNLOCKED = 0;
-
-    /**
-     * The drawer is locked closed. The user may not open it, though
-     * the app may open it programmatically.
-     */
-    public static final int LOCK_MODE_LOCKED_CLOSED = 1;
-
-    /**
-     * The drawer is locked open. The user may not close it, though the app
-     * may close it programmatically.
-     */
-    public static final int LOCK_MODE_LOCKED_OPEN = 2;
-
-    /**
-     * The drawer's lock state is reset to default.
-     */
-    public static final int LOCK_MODE_UNDEFINED = 3;
-
-    @IntDef(value = {Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END},
-            flag = true)
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface EdgeGravity {}
-
-
-    private static final int MIN_DRAWER_MARGIN = 64; // dp
-    private static final int DRAWER_ELEVATION = 10; //dp
-
-    private static final int DEFAULT_SCRIM_COLOR = 0x99000000;
-
-    /**
-     * Length of time to delay before peeking the drawer.
-     */
-    private static final int PEEK_DELAY = 160; // ms
-
-    /**
-     * Minimum velocity that will be detected as a fling
-     */
-    private static final int MIN_FLING_VELOCITY = 400; // dips per second
-
-    /**
-     * Experimental feature.
-     */
-    private static final boolean ALLOW_EDGE_LOCK = false;
-
-    private static final boolean CHILDREN_DISALLOW_INTERCEPT = true;
-
-    private static final float TOUCH_SLOP_SENSITIVITY = 1.f;
-
-    static final int[] LAYOUT_ATTRS = new int[] {
-            android.R.attr.layout_gravity
-    };
-
-    /** Whether we can use NO_HIDE_DESCENDANTS accessibility importance. */
-    static final boolean CAN_HIDE_DESCENDANTS = Build.VERSION.SDK_INT >= 19;
-
-    /** Whether the drawer shadow comes from setting elevation on the drawer. */
-    private static final boolean SET_DRAWER_SHADOW_FROM_ELEVATION =
-            Build.VERSION.SDK_INT >= 21;
-
-    private final ChildAccessibilityDelegate mChildAccessibilityDelegate =
-            new ChildAccessibilityDelegate();
-    private float mDrawerElevation;
-
-    private int mMinDrawerMargin;
-
-    private int mScrimColor = DEFAULT_SCRIM_COLOR;
-    private float mScrimOpacity;
-    private Paint mScrimPaint = new Paint();
-
-    private final ViewDragHelper mLeftDragger;
-    private final ViewDragHelper mRightDragger;
-    private final ViewDragCallback mLeftCallback;
-    private final ViewDragCallback mRightCallback;
-    private int mDrawerState;
-    private boolean mInLayout;
-    private boolean mFirstLayout = true;
-
-    private @LockMode int mLockModeLeft = LOCK_MODE_UNDEFINED;
-    private @LockMode int mLockModeRight = LOCK_MODE_UNDEFINED;
-    private @LockMode int mLockModeStart = LOCK_MODE_UNDEFINED;
-    private @LockMode int mLockModeEnd = LOCK_MODE_UNDEFINED;
-
-    private boolean mDisallowInterceptRequested;
-    private boolean mChildrenCanceledTouch;
-
-    private @Nullable DrawerListener mListener;
-    private List<DrawerListener> mListeners;
-
-    private float mInitialMotionX;
-    private float mInitialMotionY;
-
-    private Drawable mStatusBarBackground;
-    private Drawable mShadowLeftResolved;
-    private Drawable mShadowRightResolved;
-
-    private CharSequence mTitleLeft;
-    private CharSequence mTitleRight;
-
-    private Object mLastInsets;
-    private boolean mDrawStatusBarBackground;
-
-    /** Shadow drawables for different gravity */
-    private Drawable mShadowStart = null;
-    private Drawable mShadowEnd = null;
-    private Drawable mShadowLeft = null;
-    private Drawable mShadowRight = null;
-
-    private final ArrayList<View> mNonDrawerViews;
-
-    /**
-     * Listener for monitoring events about drawers.
-     */
-    public interface DrawerListener {
-        /**
-         * Called when a drawer's position changes.
-         * @param drawerView The child view that was moved
-         * @param slideOffset The new offset of this drawer within its range, from 0-1
-         */
-        void onDrawerSlide(@NonNull View drawerView, float slideOffset);
-
-        /**
-         * Called when a drawer has settled in a completely open state.
-         * The drawer is interactive at this point.
-         *
-         * @param drawerView Drawer view that is now open
-         */
-        void onDrawerOpened(@NonNull View drawerView);
-
-        /**
-         * Called when a drawer has settled in a completely closed state.
-         *
-         * @param drawerView Drawer view that is now closed
-         */
-        void onDrawerClosed(@NonNull View drawerView);
-
-        /**
-         * Called when the drawer motion state changes. The new state will
-         * be one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
-         *
-         * @param newState The new drawer motion state
-         */
-        void onDrawerStateChanged(@State int newState);
-    }
-
-    /**
-     * Stub/no-op implementations of all methods of {@link DrawerListener}.
-     * Override this if you only care about a few of the available callback methods.
-     */
-    public abstract static class SimpleDrawerListener implements DrawerListener {
-        @Override
-        public void onDrawerSlide(View drawerView, float slideOffset) {
-        }
-
-        @Override
-        public void onDrawerOpened(View drawerView) {
-        }
-
-        @Override
-        public void onDrawerClosed(View drawerView) {
-        }
-
-        @Override
-        public void onDrawerStateChanged(int newState) {
-        }
-    }
-
-    public DrawerLayout(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public DrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public DrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-        final float density = getResources().getDisplayMetrics().density;
-        mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);
-        final float minVel = MIN_FLING_VELOCITY * density;
-
-        mLeftCallback = new ViewDragCallback(Gravity.LEFT);
-        mRightCallback = new ViewDragCallback(Gravity.RIGHT);
-
-        mLeftDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mLeftCallback);
-        mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
-        mLeftDragger.setMinVelocity(minVel);
-        mLeftCallback.setDragger(mLeftDragger);
-
-        mRightDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mRightCallback);
-        mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);
-        mRightDragger.setMinVelocity(minVel);
-        mRightCallback.setDragger(mRightDragger);
-
-        // So that we can catch the back button
-        setFocusableInTouchMode(true);
-
-        ViewCompat.setImportantForAccessibility(this,
-                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-
-        ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
-        setMotionEventSplittingEnabled(false);
-        if (ViewCompat.getFitsSystemWindows(this)) {
-            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;
-
-        mNonDrawerViews = new ArrayList<View>();
-    }
-
-    /**
-     * Sets the base elevation of the drawer(s) relative to the parent, in pixels. Note that the
-     * elevation change is only supported in API 21 and above.
-     *
-     * @param elevation The base depth position of the view, in pixels.
-     */
-    public void setDrawerElevation(float elevation) {
-        mDrawerElevation = elevation;
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (isDrawerView(child)) {
-                ViewCompat.setElevation(child, mDrawerElevation);
-            }
-        }
-    }
-
-    /**
-     * The base elevation of the drawer(s) relative to the parent, in pixels. Note that the
-     * elevation change is only supported in API 21 and above. For unsupported API levels, 0 will
-     * be returned as the elevation.
-     *
-     * @return The base depth position of the view, in pixels.
-     */
-    public float getDrawerElevation() {
-        if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
-            return mDrawerElevation;
-        }
-        return 0f;
-    }
-
-    /**
-     * @hide Internal use only; called to apply window insets when configured
-     * with fitsSystemWindows="true"
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setChildInsets(Object insets, boolean draw) {
-        mLastInsets = insets;
-        mDrawStatusBarBackground = draw;
-        setWillNotDraw(!draw && getBackground() == null);
-        requestLayout();
-    }
-
-    /**
-     * Set a simple drawable used for the left or right shadow. The drawable provided must have a
-     * nonzero intrinsic width. For API 21 and above, an elevation will be set on the drawer
-     * instead of using the provided shadow drawable.
-     *
-     * <p>Note that for better support for both left-to-right and right-to-left layout
-     * directions, a drawable for RTL layout (in additional to the one in LTR layout) can be
-     * defined with a resource qualifier "ldrtl" for API 17 and above with the gravity
-     * {@link GravityCompat#START}. Alternatively, for API 23 and above, the drawable can
-     * auto-mirrored such that the drawable will be mirrored in RTL layout.</p>
-     *
-     * @param shadowDrawable Shadow drawable to use at the edge of a drawer
-     * @param gravity Which drawer the shadow should apply to
-     */
-    public void setDrawerShadow(Drawable shadowDrawable, @EdgeGravity int gravity) {
-        /*
-         * TODO Someone someday might want to set more complex drawables here.
-         * They're probably nuts, but we might want to consider registering callbacks,
-         * setting states, etc. properly.
-         */
-        if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
-            // No op. Drawer shadow will come from setting an elevation on the drawer.
-            return;
-        }
-        if ((gravity & GravityCompat.START) == GravityCompat.START) {
-            mShadowStart = shadowDrawable;
-        } else if ((gravity & GravityCompat.END) == GravityCompat.END) {
-            mShadowEnd = shadowDrawable;
-        } else if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
-            mShadowLeft = shadowDrawable;
-        } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
-            mShadowRight = shadowDrawable;
-        } else {
-            return;
-        }
-        resolveShadowDrawables();
-        invalidate();
-    }
-
-    /**
-     * Set a simple drawable used for the left or right shadow. The drawable provided must have a
-     * nonzero intrinsic width. For API 21 and above, an elevation will be set on the drawer
-     * instead of using the provided shadow drawable.
-     *
-     * <p>Note that for better support for both left-to-right and right-to-left layout
-     * directions, a drawable for RTL layout (in additional to the one in LTR layout) can be
-     * defined with a resource qualifier "ldrtl" for API 17 and above with the gravity
-     * {@link GravityCompat#START}. Alternatively, for API 23 and above, the drawable can
-     * auto-mirrored such that the drawable will be mirrored in RTL layout.</p>
-     *
-     * @param resId Resource id of a shadow drawable to use at the edge of a drawer
-     * @param gravity Which drawer the shadow should apply to
-     */
-    public void setDrawerShadow(@DrawableRes int resId, @EdgeGravity int gravity) {
-        setDrawerShadow(ContextCompat.getDrawable(getContext(), resId), gravity);
-    }
-
-    /**
-     * Set a color to use for the scrim that obscures primary content while a drawer is open.
-     *
-     * @param color Color to use in 0xAARRGGBB format.
-     */
-    public void setScrimColor(@ColorInt int color) {
-        mScrimColor = color;
-        invalidate();
-    }
-
-    /**
-     * Set a listener to be notified of drawer events. Note that this method is deprecated
-     * and you should use {@link #addDrawerListener(DrawerListener)} to add a listener and
-     * {@link #removeDrawerListener(DrawerListener)} to remove a registered listener.
-     *
-     * @param listener Listener to notify when drawer events occur
-     * @deprecated Use {@link #addDrawerListener(DrawerListener)}
-     * @see DrawerListener
-     * @see #addDrawerListener(DrawerListener)
-     * @see #removeDrawerListener(DrawerListener)
-     */
-    @Deprecated
-    public void setDrawerListener(DrawerListener listener) {
-        // The logic in this method emulates what we had before support for multiple
-        // registered listeners.
-        if (mListener != null) {
-            removeDrawerListener(mListener);
-        }
-        if (listener != null) {
-            addDrawerListener(listener);
-        }
-        // Update the deprecated field so that we can remove the passed listener the next
-        // time we're called
-        mListener = listener;
-    }
-
-    /**
-     * Adds the specified listener to the list of listeners that will be notified of drawer events.
-     *
-     * @param listener Listener to notify when drawer events occur.
-     * @see #removeDrawerListener(DrawerListener)
-     */
-    public void addDrawerListener(@NonNull DrawerListener listener) {
-        if (listener == null) {
-            return;
-        }
-        if (mListeners == null) {
-            mListeners = new ArrayList<DrawerListener>();
-        }
-        mListeners.add(listener);
-    }
-
-    /**
-     * Removes the specified listener from the list of listeners that will be notified of drawer
-     * events.
-     *
-     * @param listener Listener to remove from being notified of drawer events
-     * @see #addDrawerListener(DrawerListener)
-     */
-    public void removeDrawerListener(@NonNull DrawerListener listener) {
-        if (listener == null) {
-            return;
-        }
-        if (mListeners == null) {
-            // This can happen if this method is called before the first call to addDrawerListener
-            return;
-        }
-        mListeners.remove(listener);
-    }
-
-    /**
-     * Enable or disable interaction with all drawers.
-     *
-     * <p>This allows the application to restrict the user's ability to open or close
-     * any drawer within this layout. DrawerLayout will still respond to calls to
-     * {@link #openDrawer(int)}, {@link #closeDrawer(int)} and friends if a drawer is locked.</p>
-     *
-     * <p>Locking drawers open or closed will implicitly open or close
-     * any drawers as appropriate.</p>
-     *
-     * @param lockMode The new lock mode for the given drawer. One of {@link #LOCK_MODE_UNLOCKED},
-     *                 {@link #LOCK_MODE_LOCKED_CLOSED} or {@link #LOCK_MODE_LOCKED_OPEN}.
-     */
-    public void setDrawerLockMode(@LockMode int lockMode) {
-        setDrawerLockMode(lockMode, Gravity.LEFT);
-        setDrawerLockMode(lockMode, Gravity.RIGHT);
-    }
-
-    /**
-     * Enable or disable interaction with the given drawer.
-     *
-     * <p>This allows the application to restrict the user's ability to open or close
-     * the given drawer. DrawerLayout will still respond to calls to {@link #openDrawer(int)},
-     * {@link #closeDrawer(int)} and friends if a drawer is locked.</p>
-     *
-     * <p>Locking a drawer open or closed will implicitly open or close
-     * that drawer as appropriate.</p>
-     *
-     * @param lockMode The new lock mode for the given drawer. One of {@link #LOCK_MODE_UNLOCKED},
-     *                 {@link #LOCK_MODE_LOCKED_CLOSED} or {@link #LOCK_MODE_LOCKED_OPEN}.
-     * @param edgeGravity Gravity.LEFT, RIGHT, START or END.
-     *                    Expresses which drawer to change the mode for.
-     *
-     * @see #LOCK_MODE_UNLOCKED
-     * @see #LOCK_MODE_LOCKED_CLOSED
-     * @see #LOCK_MODE_LOCKED_OPEN
-     */
-    public void setDrawerLockMode(@LockMode int lockMode, @EdgeGravity int edgeGravity) {
-        final int absGravity = GravityCompat.getAbsoluteGravity(edgeGravity,
-                ViewCompat.getLayoutDirection(this));
-
-        switch (edgeGravity) {
-            case Gravity.LEFT:
-                mLockModeLeft = lockMode;
-                break;
-            case Gravity.RIGHT:
-                mLockModeRight = lockMode;
-                break;
-            case GravityCompat.START:
-                mLockModeStart = lockMode;
-                break;
-            case GravityCompat.END:
-                mLockModeEnd = lockMode;
-                break;
-        }
-
-        if (lockMode != LOCK_MODE_UNLOCKED) {
-            // Cancel interaction in progress
-            final ViewDragHelper helper = absGravity == Gravity.LEFT ? mLeftDragger : mRightDragger;
-            helper.cancel();
-        }
-        switch (lockMode) {
-            case LOCK_MODE_LOCKED_OPEN:
-                final View toOpen = findDrawerWithGravity(absGravity);
-                if (toOpen != null) {
-                    openDrawer(toOpen);
-                }
-                break;
-            case LOCK_MODE_LOCKED_CLOSED:
-                final View toClose = findDrawerWithGravity(absGravity);
-                if (toClose != null) {
-                    closeDrawer(toClose);
-                }
-                break;
-            // default: do nothing
-        }
-    }
-
-    /**
-     * Enable or disable interaction with the given drawer.
-     *
-     * <p>This allows the application to restrict the user's ability to open or close
-     * the given drawer. DrawerLayout will still respond to calls to {@link #openDrawer(int)},
-     * {@link #closeDrawer(int)} and friends if a drawer is locked.</p>
-     *
-     * <p>Locking a drawer open or closed will implicitly open or close
-     * that drawer as appropriate.</p>
-     *
-     * @param lockMode The new lock mode for the given drawer. One of {@link #LOCK_MODE_UNLOCKED},
-     *                 {@link #LOCK_MODE_LOCKED_CLOSED} or {@link #LOCK_MODE_LOCKED_OPEN}.
-     * @param drawerView The drawer view to change the lock mode for
-     *
-     * @see #LOCK_MODE_UNLOCKED
-     * @see #LOCK_MODE_LOCKED_CLOSED
-     * @see #LOCK_MODE_LOCKED_OPEN
-     */
-    public void setDrawerLockMode(@LockMode int lockMode, @NonNull View drawerView) {
-        if (!isDrawerView(drawerView)) {
-            throw new IllegalArgumentException("View " + drawerView + " is not a "
-                    + "drawer with appropriate layout_gravity");
-        }
-        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
-        setDrawerLockMode(lockMode, gravity);
-    }
-
-    /**
-     * Check the lock mode of the drawer with the given gravity.
-     *
-     * @param edgeGravity Gravity of the drawer to check
-     * @return one of {@link #LOCK_MODE_UNLOCKED}, {@link #LOCK_MODE_LOCKED_CLOSED} or
-     *         {@link #LOCK_MODE_LOCKED_OPEN}.
-     */
-    @LockMode
-    public int getDrawerLockMode(@EdgeGravity int edgeGravity) {
-        int layoutDirection = ViewCompat.getLayoutDirection(this);
-
-        switch (edgeGravity) {
-            case Gravity.LEFT:
-                if (mLockModeLeft != LOCK_MODE_UNDEFINED) {
-                    return mLockModeLeft;
-                }
-                int leftLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR)
-                        ? mLockModeStart : mLockModeEnd;
-                if (leftLockMode != LOCK_MODE_UNDEFINED) {
-                    return leftLockMode;
-                }
-                break;
-            case Gravity.RIGHT:
-                if (mLockModeRight != LOCK_MODE_UNDEFINED) {
-                    return mLockModeRight;
-                }
-                int rightLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR)
-                        ? mLockModeEnd : mLockModeStart;
-                if (rightLockMode != LOCK_MODE_UNDEFINED) {
-                    return rightLockMode;
-                }
-                break;
-            case GravityCompat.START:
-                if (mLockModeStart != LOCK_MODE_UNDEFINED) {
-                    return mLockModeStart;
-                }
-                int startLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR)
-                        ? mLockModeLeft : mLockModeRight;
-                if (startLockMode != LOCK_MODE_UNDEFINED) {
-                    return startLockMode;
-                }
-                break;
-            case GravityCompat.END:
-                if (mLockModeEnd != LOCK_MODE_UNDEFINED) {
-                    return mLockModeEnd;
-                }
-                int endLockMode = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR)
-                        ? mLockModeRight : mLockModeLeft;
-                if (endLockMode != LOCK_MODE_UNDEFINED) {
-                    return endLockMode;
-                }
-                break;
-        }
-
-        return LOCK_MODE_UNLOCKED;
-    }
-
-    /**
-     * Check the lock mode of the given drawer view.
-     *
-     * @param drawerView Drawer view to check lock mode
-     * @return one of {@link #LOCK_MODE_UNLOCKED}, {@link #LOCK_MODE_LOCKED_CLOSED} or
-     *         {@link #LOCK_MODE_LOCKED_OPEN}.
-     */
-    @LockMode
-    public int getDrawerLockMode(@NonNull View drawerView) {
-        if (!isDrawerView(drawerView)) {
-            throw new IllegalArgumentException("View " + drawerView + " is not a drawer");
-        }
-        final int drawerGravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
-        return getDrawerLockMode(drawerGravity);
-    }
-
-    /**
-     * Sets the title of the drawer with the given gravity.
-     * <p>
-     * When accessibility is turned on, this is the title that will be used to
-     * identify the drawer to the active accessibility service.
-     *
-     * @param edgeGravity Gravity.LEFT, RIGHT, START or END. Expresses which
-     *            drawer to set the title for.
-     * @param title The title for the drawer.
-     */
-    public void setDrawerTitle(@EdgeGravity int edgeGravity, @Nullable CharSequence title) {
-        final int absGravity = GravityCompat.getAbsoluteGravity(
-                edgeGravity, ViewCompat.getLayoutDirection(this));
-        if (absGravity == Gravity.LEFT) {
-            mTitleLeft = title;
-        } else if (absGravity == Gravity.RIGHT) {
-            mTitleRight = title;
-        }
-    }
-
-    /**
-     * Returns the title of the drawer with the given gravity.
-     *
-     * @param edgeGravity Gravity.LEFT, RIGHT, START or END. Expresses which
-     *            drawer to return the title for.
-     * @return The title of the drawer, or null if none set.
-     * @see #setDrawerTitle(int, CharSequence)
-     */
-    @Nullable
-    public CharSequence getDrawerTitle(@EdgeGravity int edgeGravity) {
-        final int absGravity = GravityCompat.getAbsoluteGravity(
-                edgeGravity, ViewCompat.getLayoutDirection(this));
-        if (absGravity == Gravity.LEFT) {
-            return mTitleLeft;
-        } else if (absGravity == Gravity.RIGHT) {
-            return mTitleRight;
-        }
-        return null;
-    }
-
-    /**
-     * Resolve the shared state of all drawers from the component ViewDragHelpers.
-     * Should be called whenever a ViewDragHelper's state changes.
-     */
-    void updateDrawerState(int forGravity, @State int activeState, View activeDrawer) {
-        final int leftState = mLeftDragger.getViewDragState();
-        final int rightState = mRightDragger.getViewDragState();
-
-        final int state;
-        if (leftState == STATE_DRAGGING || rightState == STATE_DRAGGING) {
-            state = STATE_DRAGGING;
-        } else if (leftState == STATE_SETTLING || rightState == STATE_SETTLING) {
-            state = STATE_SETTLING;
-        } else {
-            state = STATE_IDLE;
-        }
-
-        if (activeDrawer != null && activeState == STATE_IDLE) {
-            final LayoutParams lp = (LayoutParams) activeDrawer.getLayoutParams();
-            if (lp.onScreen == 0) {
-                dispatchOnDrawerClosed(activeDrawer);
-            } else if (lp.onScreen == 1) {
-                dispatchOnDrawerOpened(activeDrawer);
-            }
-        }
-
-        if (state != mDrawerState) {
-            mDrawerState = state;
-
-            if (mListeners != null) {
-                // Notify the listeners. Do that from the end of the list so that if a listener
-                // removes itself as the result of being called, it won't mess up with our iteration
-                int listenerCount = mListeners.size();
-                for (int i = listenerCount - 1; i >= 0; i--) {
-                    mListeners.get(i).onDrawerStateChanged(state);
-                }
-            }
-        }
-    }
-
-    void dispatchOnDrawerClosed(View drawerView) {
-        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
-        if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 1) {
-            lp.openState = 0;
-
-            if (mListeners != null) {
-                // Notify the listeners. Do that from the end of the list so that if a listener
-                // removes itself as the result of being called, it won't mess up with our iteration
-                int listenerCount = mListeners.size();
-                for (int i = listenerCount - 1; i >= 0; i--) {
-                    mListeners.get(i).onDrawerClosed(drawerView);
-                }
-            }
-
-            updateChildrenImportantForAccessibility(drawerView, false);
-
-            // Only send WINDOW_STATE_CHANGE if the host has window focus. This
-            // may change if support for multiple foreground windows (e.g. IME)
-            // improves.
-            if (hasWindowFocus()) {
-                final View rootView = getRootView();
-                if (rootView != null) {
-                    rootView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                }
-            }
-        }
-    }
-
-    void dispatchOnDrawerOpened(View drawerView) {
-        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
-        if ((lp.openState & LayoutParams.FLAG_IS_OPENED) == 0) {
-            lp.openState = LayoutParams.FLAG_IS_OPENED;
-            if (mListeners != null) {
-                // Notify the listeners. Do that from the end of the list so that if a listener
-                // removes itself as the result of being called, it won't mess up with our iteration
-                int listenerCount = mListeners.size();
-                for (int i = listenerCount - 1; i >= 0; i--) {
-                    mListeners.get(i).onDrawerOpened(drawerView);
-                }
-            }
-
-            updateChildrenImportantForAccessibility(drawerView, true);
-
-            // Only send WINDOW_STATE_CHANGE if the host has window focus.
-            if (hasWindowFocus()) {
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-            }
-        }
-    }
-
-    private void updateChildrenImportantForAccessibility(View drawerView, boolean isDrawerOpen) {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            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,
-                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-            } else {
-                ViewCompat.setImportantForAccessibility(child,
-                        ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-            }
-        }
-    }
-
-    void dispatchOnDrawerSlide(View drawerView, float slideOffset) {
-        if (mListeners != null) {
-            // Notify the listeners. Do that from the end of the list so that if a listener
-            // removes itself as the result of being called, it won't mess up with our iteration
-            int listenerCount = mListeners.size();
-            for (int i = listenerCount - 1; i >= 0; i--) {
-                mListeners.get(i).onDrawerSlide(drawerView, slideOffset);
-            }
-        }
-    }
-
-    void setDrawerViewOffset(View drawerView, float slideOffset) {
-        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
-        if (slideOffset == lp.onScreen) {
-            return;
-        }
-
-        lp.onScreen = slideOffset;
-        dispatchOnDrawerSlide(drawerView, slideOffset);
-    }
-
-    float getDrawerViewOffset(View drawerView) {
-        return ((LayoutParams) drawerView.getLayoutParams()).onScreen;
-    }
-
-    /**
-     * @return the absolute gravity of the child drawerView, resolved according
-     *         to the current layout direction
-     */
-    int getDrawerViewAbsoluteGravity(View drawerView) {
-        final int gravity = ((LayoutParams) drawerView.getLayoutParams()).gravity;
-        return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this));
-    }
-
-    boolean checkDrawerViewAbsoluteGravity(View drawerView, int checkFor) {
-        final int absGravity = getDrawerViewAbsoluteGravity(drawerView);
-        return (absGravity & checkFor) == checkFor;
-    }
-
-    View findOpenDrawer() {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams childLp = (LayoutParams) child.getLayoutParams();
-            if ((childLp.openState & LayoutParams.FLAG_IS_OPENED) == 1) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    void moveDrawerToOffset(View drawerView, float slideOffset) {
-        final float oldOffset = getDrawerViewOffset(drawerView);
-        final int width = drawerView.getWidth();
-        final int oldPos = (int) (width * oldOffset);
-        final int newPos = (int) (width * slideOffset);
-        final int dx = newPos - oldPos;
-
-        drawerView.offsetLeftAndRight(
-                checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT) ? dx : -dx);
-        setDrawerViewOffset(drawerView, slideOffset);
-    }
-
-    /**
-     * @param gravity the gravity of the child to return. If specified as a
-     *            relative value, it will be resolved according to the current
-     *            layout direction.
-     * @return the drawer with the specified gravity
-     */
-    View findDrawerWithGravity(int gravity) {
-        final int absHorizGravity = GravityCompat.getAbsoluteGravity(
-                gravity, ViewCompat.getLayoutDirection(this)) & Gravity.HORIZONTAL_GRAVITY_MASK;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final int childAbsGravity = getDrawerViewAbsoluteGravity(child);
-            if ((childAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == absHorizGravity) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Simple gravity to string - only supports LEFT and RIGHT for debugging output.
-     *
-     * @param gravity Absolute gravity value
-     * @return LEFT or RIGHT as appropriate, or a hex string
-     */
-    static String gravityToString(@EdgeGravity int gravity) {
-        if ((gravity & Gravity.LEFT) == Gravity.LEFT) {
-            return "LEFT";
-        }
-        if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) {
-            return "RIGHT";
-        }
-        return Integer.toHexString(gravity);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mFirstLayout = true;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mFirstLayout = true;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
-            if (isInEditMode()) {
-                // Don't crash the layout editor. Consume all of the space if specified
-                // or pick a magic number from thin air otherwise.
-                // TODO Better communication with tools of this bogus state.
-                // It will crash on a real device.
-                if (widthMode == MeasureSpec.AT_MOST) {
-                    widthMode = MeasureSpec.EXACTLY;
-                } else if (widthMode == MeasureSpec.UNSPECIFIED) {
-                    widthMode = MeasureSpec.EXACTLY;
-                    widthSize = 300;
-                }
-                if (heightMode == MeasureSpec.AT_MOST) {
-                    heightMode = MeasureSpec.EXACTLY;
-                } else if (heightMode == MeasureSpec.UNSPECIFIED) {
-                    heightMode = MeasureSpec.EXACTLY;
-                    heightSize = 300;
-                }
-            } else {
-                throw new IllegalArgumentException(
-                        "DrawerLayout must be measured with MeasureSpec.EXACTLY.");
-            }
-        }
-
-        setMeasuredDimension(widthSize, heightSize);
-
-        final boolean applyInsets = mLastInsets != null && ViewCompat.getFitsSystemWindows(this);
-        final int layoutDirection = ViewCompat.getLayoutDirection(this);
-
-        // Only one drawer is permitted along each vertical edge (left / right). These two booleans
-        // are tracking the presence of the edge drawers.
-        boolean hasDrawerOnLeftEdge = false;
-        boolean hasDrawerOnRightEdge = false;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            if (applyInsets) {
-                final int cgrav = GravityCompat.getAbsoluteGravity(lp.gravity, layoutDirection);
-                if (ViewCompat.getFitsSystemWindows(child)) {
-                    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 {
-                    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();
-                    }
-                }
-            }
-
-            if (isContentView(child)) {
-                // Content views get measured at exactly the layout's size.
-                final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
-                        widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
-                final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
-                        heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
-                child.measure(contentWidthSpec, contentHeightSpec);
-            } else if (isDrawerView(child)) {
-                if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
-                    if (ViewCompat.getElevation(child) != mDrawerElevation) {
-                        ViewCompat.setElevation(child, mDrawerElevation);
-                    }
-                }
-                final @EdgeGravity int childGravity =
-                        getDrawerViewAbsoluteGravity(child) & Gravity.HORIZONTAL_GRAVITY_MASK;
-                // Note that the isDrawerView check guarantees that childGravity here is either
-                // LEFT or RIGHT
-                boolean isLeftEdgeDrawer = (childGravity == Gravity.LEFT);
-                if ((isLeftEdgeDrawer && hasDrawerOnLeftEdge)
-                        || (!isLeftEdgeDrawer && hasDrawerOnRightEdge)) {
-                    throw new IllegalStateException("Child drawer has absolute gravity "
-                            + gravityToString(childGravity) + " but this " + TAG + " already has a "
-                            + "drawer view along that edge");
-                }
-                if (isLeftEdgeDrawer) {
-                    hasDrawerOnLeftEdge = true;
-                } else {
-                    hasDrawerOnRightEdge = true;
-                }
-                final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
-                        mMinDrawerMargin + lp.leftMargin + lp.rightMargin,
-                        lp.width);
-                final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
-                        lp.topMargin + lp.bottomMargin,
-                        lp.height);
-                child.measure(drawerWidthSpec, drawerHeightSpec);
-            } else {
-                throw new IllegalStateException("Child " + child + " at index " + i
-                        + " does not have a valid layout_gravity - must be Gravity.LEFT, "
-                        + "Gravity.RIGHT or Gravity.NO_GRAVITY");
-            }
-        }
-    }
-
-    private void resolveShadowDrawables() {
-        if (SET_DRAWER_SHADOW_FROM_ELEVATION) {
-            return;
-        }
-        mShadowLeftResolved = resolveLeftShadow();
-        mShadowRightResolved = resolveRightShadow();
-    }
-
-    private Drawable resolveLeftShadow() {
-        int layoutDirection = ViewCompat.getLayoutDirection(this);
-        // Prefer shadows defined with start/end gravity over left and right.
-        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
-            if (mShadowStart != null) {
-                // Correct drawable layout direction, if needed.
-                mirror(mShadowStart, layoutDirection);
-                return mShadowStart;
-            }
-        } else {
-            if (mShadowEnd != null) {
-                // Correct drawable layout direction, if needed.
-                mirror(mShadowEnd, layoutDirection);
-                return mShadowEnd;
-            }
-        }
-        return mShadowLeft;
-    }
-
-    private Drawable resolveRightShadow() {
-        int layoutDirection = ViewCompat.getLayoutDirection(this);
-        if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
-            if (mShadowEnd != null) {
-                // Correct drawable layout direction, if needed.
-                mirror(mShadowEnd, layoutDirection);
-                return mShadowEnd;
-            }
-        } else {
-            if (mShadowStart != null) {
-                // Correct drawable layout direction, if needed.
-                mirror(mShadowStart, layoutDirection);
-                return mShadowStart;
-            }
-        }
-        return mShadowRight;
-    }
-
-    /**
-     * Change the layout direction of the given drawable.
-     * Return true if auto-mirror is supported and drawable's layout direction can be changed.
-     * Otherwise, return false.
-     */
-    private boolean mirror(Drawable drawable, int layoutDirection) {
-        if (drawable == null || !DrawableCompat.isAutoMirrored(drawable)) {
-            return false;
-        }
-
-        DrawableCompat.setLayoutDirection(drawable, layoutDirection);
-        return true;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        mInLayout = true;
-        final int width = r - l;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            if (isContentView(child)) {
-                child.layout(lp.leftMargin, lp.topMargin,
-                        lp.leftMargin + child.getMeasuredWidth(),
-                        lp.topMargin + child.getMeasuredHeight());
-            } else { // Drawer, if it wasn't onMeasure would have thrown an exception.
-                final int childWidth = child.getMeasuredWidth();
-                final int childHeight = child.getMeasuredHeight();
-                int childLeft;
-
-                final float newOffset;
-                if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
-                    childLeft = -childWidth + (int) (childWidth * lp.onScreen);
-                    newOffset = (float) (childWidth + childLeft) / childWidth;
-                } else { // Right; onMeasure checked for us.
-                    childLeft = width - (int) (childWidth * lp.onScreen);
-                    newOffset = (float) (width - childLeft) / childWidth;
-                }
-
-                final boolean changeOffset = newOffset != lp.onScreen;
-
-                final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-                switch (vgrav) {
-                    default:
-                    case Gravity.TOP: {
-                        child.layout(childLeft, lp.topMargin, childLeft + childWidth,
-                                lp.topMargin + childHeight);
-                        break;
-                    }
-
-                    case Gravity.BOTTOM: {
-                        final int height = b - t;
-                        child.layout(childLeft,
-                                height - lp.bottomMargin - child.getMeasuredHeight(),
-                                childLeft + childWidth,
-                                height - lp.bottomMargin);
-                        break;
-                    }
-
-                    case Gravity.CENTER_VERTICAL: {
-                        final int height = b - t;
-                        int childTop = (height - childHeight) / 2;
-
-                        // Offset for margins. If things don't fit right because of
-                        // bad measurement before, oh well.
-                        if (childTop < lp.topMargin) {
-                            childTop = lp.topMargin;
-                        } else if (childTop + childHeight > height - lp.bottomMargin) {
-                            childTop = height - lp.bottomMargin - childHeight;
-                        }
-                        child.layout(childLeft, childTop, childLeft + childWidth,
-                                childTop + childHeight);
-                        break;
-                    }
-                }
-
-                if (changeOffset) {
-                    setDrawerViewOffset(child, newOffset);
-                }
-
-                final int newVisibility = lp.onScreen > 0 ? VISIBLE : INVISIBLE;
-                if (child.getVisibility() != newVisibility) {
-                    child.setVisibility(newVisibility);
-                }
-            }
-        }
-        mInLayout = false;
-        mFirstLayout = false;
-    }
-
-    @Override
-    public void requestLayout() {
-        if (!mInLayout) {
-            super.requestLayout();
-        }
-    }
-
-    @Override
-    public void computeScroll() {
-        final int childCount = getChildCount();
-        float scrimOpacity = 0;
-        for (int i = 0; i < childCount; i++) {
-            final float onscreen = ((LayoutParams) getChildAt(i).getLayoutParams()).onScreen;
-            scrimOpacity = Math.max(scrimOpacity, onscreen);
-        }
-        mScrimOpacity = scrimOpacity;
-
-        boolean leftDraggerSettling = mLeftDragger.continueSettling(true);
-        boolean rightDraggerSettling = mRightDragger.continueSettling(true);
-        if (leftDraggerSettling || rightDraggerSettling) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    private static boolean hasOpaqueBackground(View v) {
-        final Drawable bg = v.getBackground();
-        if (bg != null) {
-            return bg.getOpacity() == PixelFormat.OPAQUE;
-        }
-        return false;
-    }
-
-    /**
-     * Set a drawable to draw in the insets area for the status bar.
-     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
-     *
-     * @param bg Background drawable to draw behind the status bar
-     */
-    public void setStatusBarBackground(@Nullable Drawable bg) {
-        mStatusBarBackground = bg;
-        invalidate();
-    }
-
-    /**
-     * Gets the drawable used to draw in the insets area for the status bar.
-     *
-     * @return The status bar background drawable, or null if none set
-     */
-    @Nullable
-    public Drawable getStatusBarBackgroundDrawable() {
-        return mStatusBarBackground;
-    }
-
-    /**
-     * Set a drawable to draw in the insets area for the status bar.
-     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
-     *
-     * @param resId Resource id of a background drawable to draw behind the status bar
-     */
-    public void setStatusBarBackground(int resId) {
-        mStatusBarBackground = resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null;
-        invalidate();
-    }
-
-    /**
-     * Set a drawable to draw in the insets area for the status bar.
-     * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
-     *
-     * @param color Color to use as a background drawable to draw behind the status bar
-     *              in 0xAARRGGBB format.
-     */
-    public void setStatusBarBackgroundColor(@ColorInt int color) {
-        mStatusBarBackground = new ColorDrawable(color);
-        invalidate();
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        resolveShadowDrawables();
-    }
-
-    @Override
-    public void onDraw(Canvas c) {
-        super.onDraw(c);
-        if (mDrawStatusBarBackground && mStatusBarBackground != null) {
-            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);
-            }
-        }
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        final int height = getHeight();
-        final boolean drawingContent = isContentView(child);
-        int clipLeft = 0, clipRight = getWidth();
-
-        final int restoreCount = canvas.save();
-        if (drawingContent) {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View v = getChildAt(i);
-                if (v == child || v.getVisibility() != VISIBLE
-                        || !hasOpaqueBackground(v) || !isDrawerView(v)
-                        || v.getHeight() < height) {
-                    continue;
-                }
-
-                if (checkDrawerViewAbsoluteGravity(v, Gravity.LEFT)) {
-                    final int vright = v.getRight();
-                    if (vright > clipLeft) clipLeft = vright;
-                } else {
-                    final int vleft = v.getLeft();
-                    if (vleft < clipRight) clipRight = vleft;
-                }
-            }
-            canvas.clipRect(clipLeft, 0, clipRight, getHeight());
-        }
-        final boolean result = super.drawChild(canvas, child, drawingTime);
-        canvas.restoreToCount(restoreCount);
-
-        if (mScrimOpacity > 0 && drawingContent) {
-            final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;
-            final int imag = (int) (baseAlpha * mScrimOpacity);
-            final int color = imag << 24 | (mScrimColor & 0xffffff);
-            mScrimPaint.setColor(color);
-
-            canvas.drawRect(clipLeft, 0, clipRight, getHeight(), mScrimPaint);
-        } else if (mShadowLeftResolved != null
-                &&  checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
-            final int shadowWidth = mShadowLeftResolved.getIntrinsicWidth();
-            final int childRight = child.getRight();
-            final int drawerPeekDistance = mLeftDragger.getEdgeSize();
-            final float alpha =
-                    Math.max(0, Math.min((float) childRight / drawerPeekDistance, 1.f));
-            mShadowLeftResolved.setBounds(childRight, child.getTop(),
-                    childRight + shadowWidth, child.getBottom());
-            mShadowLeftResolved.setAlpha((int) (0xff * alpha));
-            mShadowLeftResolved.draw(canvas);
-        } else if (mShadowRightResolved != null
-                &&  checkDrawerViewAbsoluteGravity(child, Gravity.RIGHT)) {
-            final int shadowWidth = mShadowRightResolved.getIntrinsicWidth();
-            final int childLeft = child.getLeft();
-            final int showing = getWidth() - childLeft;
-            final int drawerPeekDistance = mRightDragger.getEdgeSize();
-            final float alpha =
-                    Math.max(0, Math.min((float) showing / drawerPeekDistance, 1.f));
-            mShadowRightResolved.setBounds(childLeft - shadowWidth, child.getTop(),
-                    childLeft, child.getBottom());
-            mShadowRightResolved.setAlpha((int) (0xff * alpha));
-            mShadowRightResolved.draw(canvas);
-        }
-        return result;
-    }
-
-    boolean isContentView(View child) {
-        return ((LayoutParams) child.getLayoutParams()).gravity == Gravity.NO_GRAVITY;
-    }
-
-    boolean isDrawerView(View child) {
-        final int gravity = ((LayoutParams) child.getLayoutParams()).gravity;
-        final int absGravity = GravityCompat.getAbsoluteGravity(gravity,
-                ViewCompat.getLayoutDirection(child));
-        if ((absGravity & Gravity.LEFT) != 0) {
-            // This child is a left-edge drawer
-            return true;
-        }
-        if ((absGravity & Gravity.RIGHT) != 0) {
-            // This child is a right-edge drawer
-            return true;
-        }
-        return false;
-    }
-
-    @SuppressWarnings("ShortCircuitBoolean")
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        final int action = ev.getActionMasked();
-
-        // "|" used deliberately here; both methods should be invoked.
-        final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev)
-                | mRightDragger.shouldInterceptTouchEvent(ev);
-
-        boolean interceptForTap = false;
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                mInitialMotionX = x;
-                mInitialMotionY = y;
-                if (mScrimOpacity > 0) {
-                    final View child = mLeftDragger.findTopChildUnder((int) x, (int) y);
-                    if (child != null && isContentView(child)) {
-                        interceptForTap = true;
-                    }
-                }
-                mDisallowInterceptRequested = false;
-                mChildrenCanceledTouch = false;
-                break;
-            }
-
-            case MotionEvent.ACTION_MOVE: {
-                // If we cross the touch slop, don't perform the delayed peek for an edge touch.
-                if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {
-                    mLeftCallback.removeCallbacks();
-                    mRightCallback.removeCallbacks();
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP: {
-                closeDrawers(true);
-                mDisallowInterceptRequested = false;
-                mChildrenCanceledTouch = false;
-            }
-        }
-
-        return interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mLeftDragger.processTouchEvent(ev);
-        mRightDragger.processTouchEvent(ev);
-
-        final int action = ev.getAction();
-        boolean wantTouchEvents = true;
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                mInitialMotionX = x;
-                mInitialMotionY = y;
-                mDisallowInterceptRequested = false;
-                mChildrenCanceledTouch = false;
-                break;
-            }
-
-            case MotionEvent.ACTION_UP: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                boolean peekingOnly = true;
-                final View touchedView = mLeftDragger.findTopChildUnder((int) x, (int) y);
-                if (touchedView != null && isContentView(touchedView)) {
-                    final float dx = x - mInitialMotionX;
-                    final float dy = y - mInitialMotionY;
-                    final int slop = mLeftDragger.getTouchSlop();
-                    if (dx * dx + dy * dy < slop * slop) {
-                        // Taps close a dimmed open drawer but only if it isn't locked open.
-                        final View openDrawer = findOpenDrawer();
-                        if (openDrawer != null) {
-                            peekingOnly = getDrawerLockMode(openDrawer) == LOCK_MODE_LOCKED_OPEN;
-                        }
-                    }
-                }
-                closeDrawers(peekingOnly);
-                mDisallowInterceptRequested = false;
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL: {
-                closeDrawers(true);
-                mDisallowInterceptRequested = false;
-                mChildrenCanceledTouch = false;
-                break;
-            }
-        }
-
-        return wantTouchEvents;
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        if (CHILDREN_DISALLOW_INTERCEPT
-                || (!mLeftDragger.isEdgeTouched(ViewDragHelper.EDGE_LEFT)
-                        && !mRightDragger.isEdgeTouched(ViewDragHelper.EDGE_RIGHT))) {
-            // If we have an edge touch we want to skip this and track it for later instead.
-            super.requestDisallowInterceptTouchEvent(disallowIntercept);
-        }
-        mDisallowInterceptRequested = disallowIntercept;
-        if (disallowIntercept) {
-            closeDrawers(true);
-        }
-    }
-
-    /**
-     * Close all currently open drawer views by animating them out of view.
-     */
-    public void closeDrawers() {
-        closeDrawers(false);
-    }
-
-    void closeDrawers(boolean peekingOnly) {
-        boolean needsInvalidate = false;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            if (!isDrawerView(child) || (peekingOnly && !lp.isPeeking)) {
-                continue;
-            }
-
-            final int childWidth = child.getWidth();
-
-            if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
-                needsInvalidate |= mLeftDragger.smoothSlideViewTo(child,
-                        -childWidth, child.getTop());
-            } else {
-                needsInvalidate |= mRightDragger.smoothSlideViewTo(child,
-                        getWidth(), child.getTop());
-            }
-
-            lp.isPeeking = false;
-        }
-
-        mLeftCallback.removeCallbacks();
-        mRightCallback.removeCallbacks();
-
-        if (needsInvalidate) {
-            invalidate();
-        }
-    }
-
-    /**
-     * Open the specified drawer view by animating it into view.
-     *
-     * @param drawerView Drawer view to open
-     */
-    public void openDrawer(@NonNull View drawerView) {
-        openDrawer(drawerView, true);
-    }
-
-    /**
-     * Open the specified drawer view.
-     *
-     * @param drawerView Drawer view to open
-     * @param animate Whether opening of the drawer should be animated.
-     */
-    public void openDrawer(@NonNull View drawerView, boolean animate) {
-        if (!isDrawerView(drawerView)) {
-            throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
-        }
-
-        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
-        if (mFirstLayout) {
-            lp.onScreen = 1.f;
-            lp.openState = LayoutParams.FLAG_IS_OPENED;
-
-            updateChildrenImportantForAccessibility(drawerView, true);
-        } else if (animate) {
-            lp.openState |= LayoutParams.FLAG_IS_OPENING;
-
-            if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
-                mLeftDragger.smoothSlideViewTo(drawerView, 0, drawerView.getTop());
-            } else {
-                mRightDragger.smoothSlideViewTo(drawerView, getWidth() - drawerView.getWidth(),
-                        drawerView.getTop());
-            }
-        } else {
-            moveDrawerToOffset(drawerView, 1.f);
-            updateDrawerState(lp.gravity, STATE_IDLE, drawerView);
-            drawerView.setVisibility(VISIBLE);
-        }
-        invalidate();
-    }
-
-    /**
-     * Open the specified drawer by animating it out of view.
-     *
-     * @param gravity Gravity.LEFT to move the left drawer or Gravity.RIGHT for the right.
-     *                GravityCompat.START or GravityCompat.END may also be used.
-     */
-    public void openDrawer(@EdgeGravity int gravity) {
-        openDrawer(gravity, true);
-    }
-
-    /**
-     * Open the specified drawer.
-     *
-     * @param gravity Gravity.LEFT to move the left drawer or Gravity.RIGHT for the right.
-     *                GravityCompat.START or GravityCompat.END may also be used.
-     * @param animate Whether opening of the drawer should be animated.
-     */
-    public void openDrawer(@EdgeGravity int gravity, boolean animate) {
-        final View drawerView = findDrawerWithGravity(gravity);
-        if (drawerView == null) {
-            throw new IllegalArgumentException("No drawer view found with gravity "
-                    + gravityToString(gravity));
-        }
-        openDrawer(drawerView, animate);
-    }
-
-    /**
-     * Close the specified drawer view by animating it into view.
-     *
-     * @param drawerView Drawer view to close
-     */
-    public void closeDrawer(@NonNull View drawerView) {
-        closeDrawer(drawerView, true);
-    }
-
-    /**
-     * Close the specified drawer view.
-     *
-     * @param drawerView Drawer view to close
-     * @param animate Whether closing of the drawer should be animated.
-     */
-    public void closeDrawer(@NonNull View drawerView, boolean animate) {
-        if (!isDrawerView(drawerView)) {
-            throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
-        }
-
-        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
-        if (mFirstLayout) {
-            lp.onScreen = 0.f;
-            lp.openState = 0;
-        } else if (animate) {
-            lp.openState |= LayoutParams.FLAG_IS_CLOSING;
-
-            if (checkDrawerViewAbsoluteGravity(drawerView, Gravity.LEFT)) {
-                mLeftDragger.smoothSlideViewTo(drawerView, -drawerView.getWidth(),
-                        drawerView.getTop());
-            } else {
-                mRightDragger.smoothSlideViewTo(drawerView, getWidth(), drawerView.getTop());
-            }
-        } else {
-            moveDrawerToOffset(drawerView, 0.f);
-            updateDrawerState(lp.gravity, STATE_IDLE, drawerView);
-            drawerView.setVisibility(INVISIBLE);
-        }
-        invalidate();
-    }
-
-    /**
-     * Close the specified drawer by animating it out of view.
-     *
-     * @param gravity Gravity.LEFT to move the left drawer or Gravity.RIGHT for the right.
-     *                GravityCompat.START or GravityCompat.END may also be used.
-     */
-    public void closeDrawer(@EdgeGravity int gravity) {
-        closeDrawer(gravity, true);
-    }
-
-    /**
-     * Close the specified drawer.
-     *
-     * @param gravity Gravity.LEFT to move the left drawer or Gravity.RIGHT for the right.
-     *                GravityCompat.START or GravityCompat.END may also be used.
-     * @param animate Whether closing of the drawer should be animated.
-     */
-    public void closeDrawer(@EdgeGravity int gravity, boolean animate) {
-        final View drawerView = findDrawerWithGravity(gravity);
-        if (drawerView == null) {
-            throw new IllegalArgumentException("No drawer view found with gravity "
-                    + gravityToString(gravity));
-        }
-        closeDrawer(drawerView, animate);
-    }
-
-    /**
-     * Check if the given drawer view is currently in an open state.
-     * To be considered "open" the drawer must have settled into its fully
-     * visible state. To check for partial visibility use
-     * {@link #isDrawerVisible(android.view.View)}.
-     *
-     * @param drawer Drawer view to check
-     * @return true if the given drawer view is in an open state
-     * @see #isDrawerVisible(android.view.View)
-     */
-    public boolean isDrawerOpen(@NonNull View drawer) {
-        if (!isDrawerView(drawer)) {
-            throw new IllegalArgumentException("View " + drawer + " is not a drawer");
-        }
-        LayoutParams drawerLp = (LayoutParams) drawer.getLayoutParams();
-        return (drawerLp.openState & LayoutParams.FLAG_IS_OPENED) == 1;
-    }
-
-    /**
-     * Check if the given drawer view is currently in an open state.
-     * To be considered "open" the drawer must have settled into its fully
-     * visible state. If there is no drawer with the given gravity this method
-     * will return false.
-     *
-     * @param drawerGravity Gravity of the drawer to check
-     * @return true if the given drawer view is in an open state
-     */
-    public boolean isDrawerOpen(@EdgeGravity int drawerGravity) {
-        final View drawerView = findDrawerWithGravity(drawerGravity);
-        if (drawerView != null) {
-            return isDrawerOpen(drawerView);
-        }
-        return false;
-    }
-
-    /**
-     * Check if a given drawer view is currently visible on-screen. The drawer
-     * may be only peeking onto the screen, fully extended, or anywhere inbetween.
-     *
-     * @param drawer Drawer view to check
-     * @return true if the given drawer is visible on-screen
-     * @see #isDrawerOpen(android.view.View)
-     */
-    public boolean isDrawerVisible(@NonNull View drawer) {
-        if (!isDrawerView(drawer)) {
-            throw new IllegalArgumentException("View " + drawer + " is not a drawer");
-        }
-        return ((LayoutParams) drawer.getLayoutParams()).onScreen > 0;
-    }
-
-    /**
-     * Check if a given drawer view is currently visible on-screen. The drawer
-     * may be only peeking onto the screen, fully extended, or anywhere in between.
-     * If there is no drawer with the given gravity this method will return false.
-     *
-     * @param drawerGravity Gravity of the drawer to check
-     * @return true if the given drawer is visible on-screen
-     */
-    public boolean isDrawerVisible(@EdgeGravity int drawerGravity) {
-        final View drawerView = findDrawerWithGravity(drawerGravity);
-        if (drawerView != null) {
-            return isDrawerVisible(drawerView);
-        }
-        return false;
-    }
-
-    private boolean hasPeekingDrawer() {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
-            if (lp.isPeeking) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams
-                ? new LayoutParams((LayoutParams) p)
-                : p instanceof ViewGroup.MarginLayoutParams
-                ? new LayoutParams((MarginLayoutParams) p)
-                : new LayoutParams(p);
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams && super.checkLayoutParams(p);
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
-            return;
-        }
-
-        // Only the views in the open drawers are focusables. Add normal child views when
-        // no drawers are opened.
-        final int childCount = getChildCount();
-        boolean isDrawerOpen = false;
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (isDrawerView(child)) {
-                if (isDrawerOpen(child)) {
-                    isDrawerOpen = true;
-                    child.addFocusables(views, direction, focusableMode);
-                }
-            } else {
-                mNonDrawerViews.add(child);
-            }
-        }
-
-        if (!isDrawerOpen) {
-            final int nonDrawerViewsCount = mNonDrawerViews.size();
-            for (int i = 0; i < nonDrawerViewsCount; ++i) {
-                final View child = mNonDrawerViews.get(i);
-                if (child.getVisibility() == View.VISIBLE) {
-                    child.addFocusables(views, direction, focusableMode);
-                }
-            }
-        }
-
-        mNonDrawerViews.clear();
-    }
-
-    private boolean hasVisibleDrawer() {
-        return findVisibleDrawer() != null;
-    }
-
-    View findVisibleDrawer() {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (isDrawerView(child) && isDrawerVisible(child)) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    void cancelChildViewTouch() {
-        // Cancel child touches
-        if (!mChildrenCanceledTouch) {
-            final long now = SystemClock.uptimeMillis();
-            final MotionEvent cancelEvent = MotionEvent.obtain(now, now,
-                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                getChildAt(i).dispatchTouchEvent(cancelEvent);
-            }
-            cancelEvent.recycle();
-            mChildrenCanceledTouch = true;
-        }
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && hasVisibleDrawer()) {
-            event.startTracking();
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            final View visibleDrawer = findVisibleDrawer();
-            if (visibleDrawer != null && getDrawerLockMode(visibleDrawer) == LOCK_MODE_UNLOCKED) {
-                closeDrawers();
-            }
-            return visibleDrawer != null;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        final SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-
-        if (ss.openDrawerGravity != Gravity.NO_GRAVITY) {
-            final View toOpen = findDrawerWithGravity(ss.openDrawerGravity);
-            if (toOpen != null) {
-                openDrawer(toOpen);
-            }
-        }
-
-        if (ss.lockModeLeft != LOCK_MODE_UNDEFINED) {
-            setDrawerLockMode(ss.lockModeLeft, Gravity.LEFT);
-        }
-        if (ss.lockModeRight != LOCK_MODE_UNDEFINED) {
-            setDrawerLockMode(ss.lockModeRight, Gravity.RIGHT);
-        }
-        if (ss.lockModeStart != LOCK_MODE_UNDEFINED) {
-            setDrawerLockMode(ss.lockModeStart, GravityCompat.START);
-        }
-        if (ss.lockModeEnd != LOCK_MODE_UNDEFINED) {
-            setDrawerLockMode(ss.lockModeEnd, GravityCompat.END);
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        final SavedState ss = new SavedState(superState);
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            // Is the current child fully opened (that is, not closing)?
-            boolean isOpenedAndNotClosing = (lp.openState == LayoutParams.FLAG_IS_OPENED);
-            // Is the current child opening?
-            boolean isClosedAndOpening = (lp.openState == LayoutParams.FLAG_IS_OPENING);
-            if (isOpenedAndNotClosing || isClosedAndOpening) {
-                // If one of the conditions above holds, save the child's gravity
-                // so that we open that child during state restore.
-                ss.openDrawerGravity = lp.gravity;
-                break;
-            }
-        }
-
-        ss.lockModeLeft = mLockModeLeft;
-        ss.lockModeRight = mLockModeRight;
-        ss.lockModeStart = mLockModeStart;
-        ss.lockModeEnd = mLockModeEnd;
-
-        return ss;
-    }
-
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        super.addView(child, index, params);
-
-        final View openDrawer = findOpenDrawer();
-        if (openDrawer != null || isDrawerView(child)) {
-            // A drawer is already open or the new view is a drawer, so the
-            // new view should start out hidden.
-            ViewCompat.setImportantForAccessibility(child,
-                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-        } else {
-            // Otherwise this is a content view and no drawer is open, so the
-            // new view should start out visible.
-            ViewCompat.setImportantForAccessibility(child,
-                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-        }
-
-        // We only need a delegate here if the framework doesn't understand
-        // NO_HIDE_DESCENDANTS importance.
-        if (!CAN_HIDE_DESCENDANTS) {
-            ViewCompat.setAccessibilityDelegate(child, mChildAccessibilityDelegate);
-        }
-    }
-
-    static boolean includeChildForAccessibility(View child) {
-        // If the child is not important for accessibility we make
-        // sure this hides the entire subtree rooted at it as the
-        // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDATS is not
-        // supported on older platforms but we want to hide the entire
-        // content and not opened drawers if a drawer is opened.
-        return ViewCompat.getImportantForAccessibility(child)
-                != ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                    && ViewCompat.getImportantForAccessibility(child)
-                != ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO;
-    }
-
-    /**
-     * State persisted across instances
-     */
-    protected static class SavedState extends AbsSavedState {
-        int openDrawerGravity = Gravity.NO_GRAVITY;
-        @LockMode int lockModeLeft;
-        @LockMode int lockModeRight;
-        @LockMode int lockModeStart;
-        @LockMode int lockModeEnd;
-
-        public SavedState(@NonNull Parcel in, @Nullable ClassLoader loader) {
-            super(in, loader);
-            openDrawerGravity = in.readInt();
-            lockModeLeft = in.readInt();
-            lockModeRight = in.readInt();
-            lockModeStart = in.readInt();
-            lockModeEnd = in.readInt();
-        }
-
-        public SavedState(@NonNull Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(openDrawerGravity);
-            dest.writeInt(lockModeLeft);
-            dest.writeInt(lockModeRight);
-            dest.writeInt(lockModeStart);
-            dest.writeInt(lockModeEnd);
-        }
-
-        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];
-            }
-        };
-    }
-
-    private class ViewDragCallback extends ViewDragHelper.Callback {
-        private final int mAbsGravity;
-        private ViewDragHelper mDragger;
-
-        private final Runnable mPeekRunnable = new Runnable() {
-            @Override public void run() {
-                peekDrawer();
-            }
-        };
-
-        ViewDragCallback(int gravity) {
-            mAbsGravity = gravity;
-        }
-
-        public void setDragger(ViewDragHelper dragger) {
-            mDragger = dragger;
-        }
-
-        public void removeCallbacks() {
-            DrawerLayout.this.removeCallbacks(mPeekRunnable);
-        }
-
-        @Override
-        public boolean tryCaptureView(View child, int pointerId) {
-            // Only capture views where the gravity matches what we're looking for.
-            // This lets us use two ViewDragHelpers, one for each side drawer.
-            return isDrawerView(child) && checkDrawerViewAbsoluteGravity(child, mAbsGravity)
-                    && getDrawerLockMode(child) == LOCK_MODE_UNLOCKED;
-        }
-
-        @Override
-        public void onViewDragStateChanged(int state) {
-            updateDrawerState(mAbsGravity, state, mDragger.getCapturedView());
-        }
-
-        @Override
-        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
-            float offset;
-            final int childWidth = changedView.getWidth();
-
-            // This reverses the positioning shown in onLayout.
-            if (checkDrawerViewAbsoluteGravity(changedView, Gravity.LEFT)) {
-                offset = (float) (childWidth + left) / childWidth;
-            } else {
-                final int width = getWidth();
-                offset = (float) (width - left) / childWidth;
-            }
-            setDrawerViewOffset(changedView, offset);
-            changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);
-            invalidate();
-        }
-
-        @Override
-        public void onViewCaptured(View capturedChild, int activePointerId) {
-            final LayoutParams lp = (LayoutParams) capturedChild.getLayoutParams();
-            lp.isPeeking = false;
-
-            closeOtherDrawer();
-        }
-
-        private void closeOtherDrawer() {
-            final int otherGrav = mAbsGravity == Gravity.LEFT ? Gravity.RIGHT : Gravity.LEFT;
-            final View toClose = findDrawerWithGravity(otherGrav);
-            if (toClose != null) {
-                closeDrawer(toClose);
-            }
-        }
-
-        @Override
-        public void onViewReleased(View releasedChild, float xvel, float yvel) {
-            // Offset is how open the drawer is, therefore left/right values
-            // are reversed from one another.
-            final float offset = getDrawerViewOffset(releasedChild);
-            final int childWidth = releasedChild.getWidth();
-
-            int left;
-            if (checkDrawerViewAbsoluteGravity(releasedChild, Gravity.LEFT)) {
-                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;
-            }
-
-            mDragger.settleCapturedViewAt(left, releasedChild.getTop());
-            invalidate();
-        }
-
-        @Override
-        public void onEdgeTouched(int edgeFlags, int pointerId) {
-            postDelayed(mPeekRunnable, PEEK_DELAY);
-        }
-
-        void peekDrawer() {
-            final View toCapture;
-            final int childLeft;
-            final int peekDistance = mDragger.getEdgeSize();
-            final boolean leftEdge = mAbsGravity == Gravity.LEFT;
-            if (leftEdge) {
-                toCapture = findDrawerWithGravity(Gravity.LEFT);
-                childLeft = (toCapture != null ? -toCapture.getWidth() : 0) + peekDistance;
-            } else {
-                toCapture = findDrawerWithGravity(Gravity.RIGHT);
-                childLeft = getWidth() - peekDistance;
-            }
-            // Only peek if it would mean making the drawer more visible and the drawer isn't locked
-            if (toCapture != null && ((leftEdge && toCapture.getLeft() < childLeft)
-                    || (!leftEdge && toCapture.getLeft() > childLeft))
-                    && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
-                final LayoutParams lp = (LayoutParams) toCapture.getLayoutParams();
-                mDragger.smoothSlideViewTo(toCapture, childLeft, toCapture.getTop());
-                lp.isPeeking = true;
-                invalidate();
-
-                closeOtherDrawer();
-
-                cancelChildViewTouch();
-            }
-        }
-
-        @Override
-        public boolean onEdgeLock(int edgeFlags) {
-            if (ALLOW_EDGE_LOCK) {
-                final View drawer = findDrawerWithGravity(mAbsGravity);
-                if (drawer != null && !isDrawerOpen(drawer)) {
-                    closeDrawer(drawer);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
-            final View toCapture;
-            if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT) {
-                toCapture = findDrawerWithGravity(Gravity.LEFT);
-            } else {
-                toCapture = findDrawerWithGravity(Gravity.RIGHT);
-            }
-
-            if (toCapture != null && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
-                mDragger.captureChildView(toCapture, pointerId);
-            }
-        }
-
-        @Override
-        public int getViewHorizontalDragRange(View child) {
-            return isDrawerView(child) ? child.getWidth() : 0;
-        }
-
-        @Override
-        public int clampViewPositionHorizontal(View child, int left, int dx) {
-            if (checkDrawerViewAbsoluteGravity(child, Gravity.LEFT)) {
-                return Math.max(-child.getWidth(), Math.min(left, 0));
-            } else {
-                final int width = getWidth();
-                return Math.max(width - child.getWidth(), Math.min(left, width));
-            }
-        }
-
-        @Override
-        public int clampViewPositionVertical(View child, int top, int dy) {
-            return child.getTop();
-        }
-    }
-
-    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-        private static final int FLAG_IS_OPENED = 0x1;
-        private static final int FLAG_IS_OPENING = 0x2;
-        private static final int FLAG_IS_CLOSING = 0x4;
-
-        public int gravity = Gravity.NO_GRAVITY;
-        float onScreen;
-        boolean isPeeking;
-        int openState;
-
-        public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
-            super(c, attrs);
-
-            final TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
-            this.gravity = a.getInt(0, Gravity.NO_GRAVITY);
-            a.recycle();
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(int width, int height, int gravity) {
-            this(width, height);
-            this.gravity = gravity;
-        }
-
-        public LayoutParams(@NonNull LayoutParams source) {
-            super(source);
-            this.gravity = source.gravity;
-        }
-
-        public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
-            super(source);
-        }
-    }
-
-    class AccessibilityDelegate extends AccessibilityDelegateCompat {
-        private final Rect mTmpRect = new Rect();
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-            if (CAN_HIDE_DESCENDANTS) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-            } else {
-                // Obtain a node for the host, then manually generate the list
-                // of children to only include non-obscured views.
-                final AccessibilityNodeInfoCompat superNode =
-                        AccessibilityNodeInfoCompat.obtain(info);
-                super.onInitializeAccessibilityNodeInfo(host, superNode);
-
-                info.setSource(host);
-                final ViewParent parent = ViewCompat.getParentForAccessibility(host);
-                if (parent instanceof View) {
-                    info.setParent((View) parent);
-                }
-                copyNodeInfoNoChildren(info, superNode);
-                superNode.recycle();
-
-                addChildrenForAccessibility(info, (ViewGroup) host);
-            }
-
-            info.setClassName(DrawerLayout.class.getName());
-
-            // This view reports itself as focusable so that it can intercept
-            // the back button, but we should prevent this view from reporting
-            // itself as focusable to accessibility services.
-            info.setFocusable(false);
-            info.setFocused(false);
-            info.removeAction(AccessibilityActionCompat.ACTION_FOCUS);
-            info.removeAction(AccessibilityActionCompat.ACTION_CLEAR_FOCUS);
-        }
-
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-
-            event.setClassName(DrawerLayout.class.getName());
-        }
-
-        @Override
-        public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-            // Special case to handle window state change events. As far as
-            // accessibility services are concerned, state changes from
-            // DrawerLayout invalidate the entire contents of the screen (like
-            // an Activity or Dialog) and they should announce the title of the
-            // new content.
-            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-                final List<CharSequence> eventText = event.getText();
-                final View visibleDrawer = findVisibleDrawer();
-                if (visibleDrawer != null) {
-                    final int edgeGravity = getDrawerViewAbsoluteGravity(visibleDrawer);
-                    final CharSequence title = getDrawerTitle(edgeGravity);
-                    if (title != null) {
-                        eventText.add(title);
-                    }
-                }
-
-                return true;
-            }
-
-            return super.dispatchPopulateAccessibilityEvent(host, event);
-        }
-
-        @Override
-        public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-                AccessibilityEvent event) {
-            if (CAN_HIDE_DESCENDANTS || includeChildForAccessibility(child)) {
-                return super.onRequestSendAccessibilityEvent(host, child, event);
-            }
-            return false;
-        }
-
-        private void addChildrenForAccessibility(AccessibilityNodeInfoCompat info, ViewGroup v) {
-            final int childCount = v.getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = v.getChildAt(i);
-                if (includeChildForAccessibility(child)) {
-                    info.addChild(child);
-                }
-            }
-        }
-
-        /**
-         * This should really be in AccessibilityNodeInfoCompat, but there unfortunately
-         * seem to be a few elements that are not easily cloneable using the underlying API.
-         * Leave it private here as it's not general-purpose useful.
-         */
-        private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
-                AccessibilityNodeInfoCompat src) {
-            final Rect rect = mTmpRect;
-
-            src.getBoundsInParent(rect);
-            dest.setBoundsInParent(rect);
-
-            src.getBoundsInScreen(rect);
-            dest.setBoundsInScreen(rect);
-
-            dest.setVisibleToUser(src.isVisibleToUser());
-            dest.setPackageName(src.getPackageName());
-            dest.setClassName(src.getClassName());
-            dest.setContentDescription(src.getContentDescription());
-
-            dest.setEnabled(src.isEnabled());
-            dest.setClickable(src.isClickable());
-            dest.setFocusable(src.isFocusable());
-            dest.setFocused(src.isFocused());
-            dest.setAccessibilityFocused(src.isAccessibilityFocused());
-            dest.setSelected(src.isSelected());
-            dest.setLongClickable(src.isLongClickable());
-
-            dest.addAction(src.getActions());
-        }
-    }
-
-    static final class ChildAccessibilityDelegate extends AccessibilityDelegateCompat {
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View child,
-                AccessibilityNodeInfoCompat info) {
-            super.onInitializeAccessibilityNodeInfo(child, info);
-
-            if (!includeChildForAccessibility(child)) {
-                // If we are ignoring the sub-tree rooted at the child,
-                // break the connection to the rest of the node tree.
-                // For details refer to includeChildForAccessibility.
-                info.setParent(null);
-            }
-        }
-    }
-}
diff --git a/android/support/v4/widget/EdgeEffectCompat.java b/android/support/v4/widget/EdgeEffectCompat.java
deleted file mode 100644
index 0d370a8..0000000
--- a/android/support/v4/widget/EdgeEffectCompat.java
+++ /dev/null
@@ -1,230 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.widget.EdgeEffect;
-
-/**
- * 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
- * of scrolling containers.
- */
-public final class EdgeEffectCompat {
-    private EdgeEffect mEdgeEffect;
-
-    private static final EdgeEffectBaseImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new EdgeEffectApi21Impl();
-        } else {
-            IMPL = new EdgeEffectBaseImpl();
-        }
-    }
-
-    static class EdgeEffectBaseImpl {
-        public void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
-            edgeEffect.onPull(deltaDistance);
-        }
-    }
-
-    @RequiresApi(21)
-    static class EdgeEffectApi21Impl extends EdgeEffectBaseImpl {
-        @Override
-        public void onPull(EdgeEffect edgeEffect, float deltaDistance, float displacement) {
-            edgeEffect.onPull(deltaDistance, displacement);
-        }
-    }
-
-    /**
-     * Construct a new EdgeEffect themed using the given context.
-     *
-     * <p>Note: On platform versions that do not support EdgeEffect, all operations
-     * 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 = new EdgeEffect(context);
-    }
-
-    /**
-     * Set the size of this edge effect in pixels.
-     *
-     * @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) {
-        mEdgeEffect.setSize(width, height);
-    }
-
-    /**
-     * Reports if this EdgeEffectCompat's animation is finished. If this method returns false
-     * after a call to {@link #draw(Canvas)} the host widget should schedule another
-     * 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 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() {
-        mEdgeEffect.finish();
-    }
-
-    /**
-     * 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()} if this method
-     * returns true and draw the results accordingly.
-     *
-     * @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.
-     * @return true if the host view should call invalidate, false if it should not.
-     *
-     * @deprecated Use {@link #onPull(EdgeEffect, float, float)}.
-     */
-    @Deprecated
-    public boolean onPull(float deltaDistance) {
-        mEdgeEffect.onPull(deltaDistance);
-        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()} 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.
-     * @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.
-     * @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) {
-        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(@NonNull EdgeEffect edgeEffect, float deltaDistance,
-            float displacement) {
-        IMPL.onPull(edgeEffect, deltaDistance, displacement);
-    }
-
-    /**
-     * Call when the object is released after being pulled.
-     * This will begin the "decay" phase of the effect. After calling this method
-     * the host view should {@link android.view.View#invalidate()} if this method
-     * 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() {
-        mEdgeEffect.onRelease();
-        return mEdgeEffect.isFinished();
-    }
-
-    /**
-     * Call when the effect absorbs an impact at the given velocity.
-     * Used when a fling reaches the scroll boundary.
-     *
-     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
-     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
-     * to use here.</p>
-     *
-     * @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) {
-        mEdgeEffect.onAbsorb(velocity);
-        return true;
-    }
-
-    /**
-     * Draw into the provided canvas. Assumes that the canvas has been rotated
-     * accordingly and the size has been set. The effect will be drawn the full
-     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
-     * 1.f of height.
-     *
-     * @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 mEdgeEffect.draw(canvas);
-    }
-}
diff --git a/android/support/v4/widget/ExploreByTouchHelper.java b/android/support/v4/widget/ExploreByTouchHelper.java
deleted file mode 100644
index 2b5ed0a..0000000
--- a/android/support/v4/widget/ExploreByTouchHelper.java
+++ /dev/null
@@ -1,1262 +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.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.util.SparseArrayCompat;
-import android.support.v4.view.AccessibilityDelegateCompat;
-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.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-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;
-
-/**
- * ExploreByTouchHelper is a utility class for implementing accessibility
- * support in custom {@link View}s that represent a collection of View-like
- * logical items. It extends {@link AccessibilityNodeProviderCompat} and
- * simplifies many aspects of providing information to accessibility services
- * and managing accessibility focus.
- * <p>
- * Clients should override abstract methods on this class and attach it to the
- * host view using {@link ViewCompat#setAccessibilityDelegate}:
- * <p>
- * <pre>
- * class MyCustomView extends View {
- *     private MyVirtualViewHelper mVirtualViewHelper;
- *
- *     public MyCustomView(Context context, ...) {
- *         ...
- *         mVirtualViewHelper = new MyVirtualViewHelper(this);
- *         ViewCompat.setAccessibilityDelegate(this, mVirtualViewHelper);
- *     }
- *
- *     &#64;Override
- *     public boolean dispatchHoverEvent(MotionEvent event) {
- *       return mHelper.dispatchHoverEvent(this, event)
- *           || super.dispatchHoverEvent(event);
- *     }
- *
- *     &#64;Override
- *     public boolean dispatchKeyEvent(KeyEvent event) {
- *       return mHelper.dispatchKeyEvent(event)
- *           || super.dispatchKeyEvent(event);
- *     }
- *
- *     &#64;Override
- *     public boolean onFocusChanged(boolean gainFocus, int direction,
- *         Rect previouslyFocusedRect) {
- *       super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- *       mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- *     }
- * }
- * mAccessHelper = new MyExploreByTouchHelper(someView);
- * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
- * </pre>
- */
-public abstract class ExploreByTouchHelper extends AccessibilityDelegateCompat {
-    /** Virtual node identifier value for invalid nodes. */
-    public static final int INVALID_ID = Integer.MIN_VALUE;
-
-    /** Virtual node identifier value for the host view's node. */
-    public static final int HOST_ID = View.NO_ID;
-
-    /** Default class name used for virtual views. */
-    private static final String DEFAULT_CLASS_NAME = "android.view.View";
-
-    /** Default bounds used to determine if the client didn't set any. */
-    private static final Rect INVALID_PARENT_BOUNDS = new Rect(
-            Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
-
-    // Temporary, reusable data structures.
-    private final Rect mTempScreenRect = new Rect();
-    private final Rect mTempParentRect = new Rect();
-    private final Rect mTempVisibleRect = new Rect();
-    private final int[] mTempGlobalRect = new int[2];
-
-    /** System accessibility manager, used to check state and send events. */
-    private final AccessibilityManager mManager;
-
-    /** View whose internal structure is exposed through this helper. */
-    private final View mHost;
-
-    /** Virtual node provider used to expose logical structure to services. */
-    private MyNodeProvider mNodeProvider;
-
-    /** Identifier for the virtual view that holds accessibility focus. */
-    private int mAccessibilityFocusedVirtualViewId = INVALID_ID;
-
-    /** Identifier for the virtual view that holds keyboard focus. */
-    private int mKeyboardFocusedVirtualViewId = INVALID_ID;
-
-    /** Identifier for the virtual view that is currently hovered. */
-    private int mHoveredVirtualViewId = INVALID_ID;
-
-    /**
-     * Constructs a new helper that can expose a virtual view hierarchy for the
-     * specified host view.
-     *
-     * @param host view whose virtual view hierarchy is exposed by this helper
-     */
-    public ExploreByTouchHelper(@NonNull View host) {
-        if (host == null) {
-            throw new IllegalArgumentException("View may not be null");
-        }
-
-        mHost = host;
-
-        final Context context = host.getContext();
-        mManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
-
-        // Host view must be focusable so that we can delegate to virtual
-        // views.
-        host.setFocusable(true);
-        if (ViewCompat.getImportantForAccessibility(host)
-                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-            ViewCompat.setImportantForAccessibility(
-                    host, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-        }
-    }
-
-    @Override
-    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
-        if (mNodeProvider == null) {
-            mNodeProvider = new MyNodeProvider();
-        }
-        return mNodeProvider;
-    }
-
-    /**
-     * Delegates hover events from the host view.
-     * <p>
-     * Dispatches hover {@link MotionEvent}s to the virtual view hierarchy when
-     * the Explore by Touch feature is enabled.
-     * <p>
-     * This method should be called by overriding the host view's
-     * {@link View#dispatchHoverEvent(MotionEvent)} method:
-     * <pre>&#64;Override
-     * public boolean dispatchHoverEvent(MotionEvent event) {
-     *   return mHelper.dispatchHoverEvent(this, event)
-     *       || super.dispatchHoverEvent(event);
-     * }
-     * </pre>
-     *
-     * @param event The hover event to dispatch to the virtual view hierarchy.
-     * @return Whether the hover event was handled.
-     */
-    public final boolean dispatchHoverEvent(@NonNull MotionEvent event) {
-        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
-            return false;
-        }
-
-        switch (event.getAction()) {
-            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 MotionEvent.ACTION_HOVER_EXIT:
-                if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
-                    updateHoveredVirtualView(INVALID_ID);
-                    return true;
-                }
-                return false;
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * Delegates key events from the host view.
-     * <p>
-     * This method should be called by overriding the host view's
-     * {@link View#dispatchKeyEvent(KeyEvent)} method:
-     * <pre>&#64;Override
-     * public boolean dispatchKeyEvent(KeyEvent event) {
-     *   return mHelper.dispatchKeyEvent(event)
-     *       || super.dispatchKeyEvent(event);
-     * }
-     * </pre>
-     */
-    public final boolean dispatchKeyEvent(@NonNull KeyEvent event) {
-        boolean handled = false;
-
-        final int action = event.getAction();
-        if (action != KeyEvent.ACTION_UP) {
-            final int keyCode = event.getKeyCode();
-            switch (keyCode) {
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                case KeyEvent.KEYCODE_DPAD_UP:
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                    if (event.hasNoModifiers()) {
-                        final int direction = keyToDirection(keyCode);
-                        final int count = 1 + event.getRepeatCount();
-                        for (int i = 0; i < count; i++) {
-                            if (moveFocus(direction, null)) {
-                                handled = true;
-                            } else {
-                                break;
-                            }
-                        }
-                    }
-                    break;
-                case KeyEvent.KEYCODE_DPAD_CENTER:
-                case KeyEvent.KEYCODE_ENTER:
-                    if (event.hasNoModifiers()) {
-                        if (event.getRepeatCount() == 0) {
-                            clickKeyboardFocusedVirtualView();
-                            handled = true;
-                        }
-                    }
-                    break;
-                case KeyEvent.KEYCODE_TAB:
-                    if (event.hasNoModifiers()) {
-                        handled = moveFocus(View.FOCUS_FORWARD, null);
-                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
-                        handled = moveFocus(View.FOCUS_BACKWARD, null);
-                    }
-                    break;
-            }
-        }
-
-        return handled;
-    }
-
-    /**
-     * Delegates focus changes from the host view.
-     * <p>
-     * This method should be called by overriding the host view's
-     * {@link View#onFocusChanged(boolean, int, Rect)} method:
-     * <pre>&#64;Override
-     * public boolean onFocusChanged(boolean gainFocus, int direction,
-     *     Rect previouslyFocusedRect) {
-     *   super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-     *   mHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-     * }
-     * </pre>
-     */
-    public final void onFocusChanged(boolean gainFocus, int direction,
-            @Nullable Rect previouslyFocusedRect) {
-        if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
-            clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId);
-        }
-
-        if (gainFocus) {
-            moveFocus(direction, previouslyFocusedRect);
-        }
-    }
-
-    /**
-     * @return the identifier of the virtual view that has accessibility focus
-     *         or {@link #INVALID_ID} if no virtual view has accessibility
-     *         focus
-     */
-    public final int getAccessibilityFocusedVirtualViewId() {
-        return mAccessibilityFocusedVirtualViewId;
-    }
-
-    /**
-     * @return the identifier of the virtual view that has keyboard focus
-     *         or {@link #INVALID_ID} if no virtual view has keyboard focus
-     */
-    public final int getKeyboardFocusedVirtualViewId() {
-        return mKeyboardFocusedVirtualViewId;
-    }
-
-    /**
-     * Maps key event codes to focus directions.
-     *
-     * @param keyCode the key event code
-     * @return the corresponding focus direction
-     */
-    @FocusRealDirection
-    private static int keyToDirection(int keyCode) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                return View.FOCUS_LEFT;
-            case KeyEvent.KEYCODE_DPAD_UP:
-                return View.FOCUS_UP;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                return View.FOCUS_RIGHT;
-            default:
-                return View.FOCUS_DOWN;
-        }
-    }
-
-    /**
-     * Obtains the bounds for the specified virtual view.
-     *
-     * @param virtualViewId the identifier of the virtual view
-     * @param outBounds the rect to populate with virtual view bounds
-     */
-    private void getBoundsInParent(int virtualViewId, Rect outBounds) {
-        final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
-        node.getBoundsInParent(outBounds);
-    }
-
-    /**
-     * Adapts AccessibilityNodeInfoCompat for obtaining bounds.
-     */
-    private static final FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat> NODE_ADAPTER =
-            new FocusStrategy.BoundsAdapter<AccessibilityNodeInfoCompat>() {
-                @Override
-                public void obtainBounds(AccessibilityNodeInfoCompat node, Rect outBounds) {
-                    node.getBoundsInParent(outBounds);
-                }
-            };
-
-    /**
-     * Adapts SparseArrayCompat for iterating through values.
-     */
-    private static final FocusStrategy.CollectionAdapter<SparseArrayCompat<
-            AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat> SPARSE_VALUES_ADAPTER =
-            new FocusStrategy.CollectionAdapter<SparseArrayCompat<
-                    AccessibilityNodeInfoCompat>, AccessibilityNodeInfoCompat>() {
-                @Override
-                public AccessibilityNodeInfoCompat get(
-                        SparseArrayCompat<AccessibilityNodeInfoCompat> collection, int index) {
-                    return collection.valueAt(index);
-                }
-
-                @Override
-                public int size(SparseArrayCompat<AccessibilityNodeInfoCompat> collection) {
-                    return collection.size();
-                }
-            };
-
-    /**
-     * Attempts to move keyboard focus in the specified direction.
-     *
-     * @param direction the direction in which to move keyboard focus
-     * @param previouslyFocusedRect the bounds of the previously focused item,
-     *                              or {@code null} if not available
-     * @return {@code true} if keyboard focus moved to a virtual view managed
-     *         by this helper, or {@code false} otherwise
-     */
-    private boolean moveFocus(@FocusDirection int direction, @Nullable Rect previouslyFocusedRect) {
-        final SparseArrayCompat<AccessibilityNodeInfoCompat> allNodes = getAllNodes();
-
-        final int focusedNodeId = mKeyboardFocusedVirtualViewId;
-        final AccessibilityNodeInfoCompat focusedNode =
-                focusedNodeId == INVALID_ID ? null : allNodes.get(focusedNodeId);
-
-        final AccessibilityNodeInfoCompat nextFocusedNode;
-        switch (direction) {
-            case View.FOCUS_FORWARD:
-            case View.FOCUS_BACKWARD:
-                final boolean isLayoutRtl =
-                        ViewCompat.getLayoutDirection(mHost) == ViewCompat.LAYOUT_DIRECTION_RTL;
-                nextFocusedNode = FocusStrategy.findNextFocusInRelativeDirection(allNodes,
-                        SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, direction, isLayoutRtl,
-                        false);
-                break;
-            case View.FOCUS_LEFT:
-            case View.FOCUS_UP:
-            case View.FOCUS_RIGHT:
-            case View.FOCUS_DOWN:
-                final Rect selectedRect = new Rect();
-                if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
-                    // Focus is moving from a virtual view within the host.
-                    getBoundsInParent(mKeyboardFocusedVirtualViewId, selectedRect);
-                } else if (previouslyFocusedRect != null) {
-                    // Focus is moving from a real view outside the host.
-                    selectedRect.set(previouslyFocusedRect);
-                } else {
-                    // Focus is moving from... somewhere? Make a guess.
-                    // Usually this happens when another view was too lazy
-                    // to pass the previously focused rect (ex. ScrollView
-                    // when moving UP or DOWN).
-                    guessPreviouslyFocusedRect(mHost, direction, selectedRect);
-                }
-                nextFocusedNode = FocusStrategy.findNextFocusInAbsoluteDirection(allNodes,
-                        SPARSE_VALUES_ADAPTER, NODE_ADAPTER, focusedNode, selectedRect, direction);
-                break;
-            default:
-                throw new IllegalArgumentException("direction must be one of "
-                        + "{FOCUS_FORWARD, FOCUS_BACKWARD, FOCUS_UP, FOCUS_DOWN, "
-                        + "FOCUS_LEFT, FOCUS_RIGHT}.");
-        }
-
-        final int nextFocusedNodeId;
-        if (nextFocusedNode == null) {
-            nextFocusedNodeId = INVALID_ID;
-        } else {
-            final int index = allNodes.indexOfValue(nextFocusedNode);
-            nextFocusedNodeId = allNodes.keyAt(index);
-        }
-
-        return requestKeyboardFocusForVirtualView(nextFocusedNodeId);
-    }
-
-    private SparseArrayCompat<AccessibilityNodeInfoCompat> getAllNodes() {
-        final List<Integer> virtualViewIds = new ArrayList<>();
-        getVisibleVirtualViews(virtualViewIds);
-
-        final SparseArrayCompat<AccessibilityNodeInfoCompat> allNodes = new SparseArrayCompat<>();
-        for (int virtualViewId = 0; virtualViewId < virtualViewIds.size(); virtualViewId++) {
-            final AccessibilityNodeInfoCompat virtualView = createNodeForChild(virtualViewId);
-            allNodes.put(virtualViewId, virtualView);
-        }
-
-        return allNodes;
-    }
-
-    /**
-     * Obtains a best guess for the previously focused rect for keyboard focus
-     * moving in the specified direction.
-     *
-     * @param host the view into which focus is moving
-     * @param direction the absolute direction in which focus is moving
-     * @param outBounds the rect to populate with the best-guess bounds for the
-     *                  previous focus rect
-     */
-    private static Rect guessPreviouslyFocusedRect(@NonNull View host,
-            @FocusRealDirection int direction, @NonNull Rect outBounds) {
-        final int w = host.getWidth();
-        final int h = host.getHeight();
-
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                outBounds.set(w, 0, w, h);
-                break;
-            case View.FOCUS_UP:
-                outBounds.set(0, h, w, h);
-                break;
-            case View.FOCUS_RIGHT:
-                outBounds.set(-1, 0, -1, h);
-                break;
-            case View.FOCUS_DOWN:
-                outBounds.set(0, -1, w, -1);
-                break;
-            default:
-                throw new IllegalArgumentException("direction must be one of "
-                        + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-        }
-
-        return outBounds;
-    }
-
-    /**
-     * Performs a click action on the keyboard focused virtual view, if any.
-     *
-     * @return {@code true} if the click action was performed successfully or
-     *         {@code false} otherwise
-     */
-    private boolean clickKeyboardFocusedVirtualView() {
-        return mKeyboardFocusedVirtualViewId != INVALID_ID && onPerformActionForVirtualView(
-                mKeyboardFocusedVirtualViewId, AccessibilityNodeInfoCompat.ACTION_CLICK, null);
-    }
-
-    /**
-     * Populates an event of the specified type with information about an item
-     * and attempts to send it up through the view hierarchy.
-     * <p>
-     * You should call this method after performing a user action that normally
-     * fires an accessibility event, such as clicking on an item.
-     * <p>
-     * <pre>public void performItemClick(T item) {
-     *   ...
-     *   sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
-     * }
-     * </pre>
-     *
-     * @param virtualViewId the identifier of the virtual view for which to
-     *                      send an event
-     * @param eventType the type of event to send
-     * @return {@code true} if the event was sent successfully, {@code false}
-     *         otherwise
-     */
-    public final boolean sendEventForVirtualView(int virtualViewId, int eventType) {
-        if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
-            return false;
-        }
-
-        final ViewParent parent = mHost.getParent();
-        if (parent == null) {
-            return false;
-        }
-
-        final AccessibilityEvent event = createEvent(virtualViewId, eventType);
-        return ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event);
-    }
-
-    /**
-     * Notifies the accessibility framework that the properties of the parent
-     * view have changed.
-     * <p>
-     * You <strong>must</strong> call this method after adding or removing
-     * items from the parent view.
-     */
-    public final void invalidateRoot() {
-        invalidateVirtualView(HOST_ID, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE);
-    }
-
-    /**
-     * Notifies the accessibility framework that the properties of a particular
-     * item have changed.
-     * <p>
-     * You <strong>must</strong> call this method after changing any of the
-     * properties set in
-     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
-     *
-     * @param virtualViewId the virtual view id to invalidate, or
-     *                      {@link #HOST_ID} to invalidate the root view
-     * @see #invalidateVirtualView(int, int)
-     */
-    public final void invalidateVirtualView(int virtualViewId) {
-        invalidateVirtualView(virtualViewId,
-                AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED);
-    }
-
-    /**
-     * Notifies the accessibility framework that the properties of a particular
-     * item have changed.
-     * <p>
-     * You <strong>must</strong> call this method after changing any of the
-     * properties set in
-     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
-     *
-     * @param virtualViewId the virtual view id to invalidate, or
-     *                      {@link #HOST_ID} to invalidate the root view
-     * @param changeTypes the bit mask of change types. May be {@code 0} for the
-     *                    default (undefined) change type or one or more of:
-     *         <ul>
-     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
-     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_SUBTREE}
-     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_TEXT}
-     *         <li>{@link AccessibilityEventCompat#CONTENT_CHANGE_TYPE_UNDEFINED}
-     *         </ul>
-     */
-    public final void invalidateVirtualView(int virtualViewId, int changeTypes) {
-        if (virtualViewId != INVALID_ID && mManager.isEnabled()) {
-            final ViewParent parent = mHost.getParent();
-            if (parent != null) {
-                // Send events up the hierarchy so they can be coalesced.
-                final AccessibilityEvent event = createEvent(virtualViewId,
-                        AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
-                AccessibilityEventCompat.setContentChangeTypes(event, changeTypes);
-                ViewParentCompat.requestSendAccessibilityEvent(parent, mHost, event);
-            }
-        }
-    }
-
-    /**
-     * Returns the virtual view ID for the currently accessibility focused
-     * item.
-     *
-     * @return the identifier of the virtual view that has accessibility focus
-     *         or {@link #INVALID_ID} if no virtual view has accessibility
-     *         focus
-     * @deprecated Use {@link #getAccessibilityFocusedVirtualViewId()}.
-     */
-    @Deprecated
-    public int getFocusedVirtualView() {
-        return getAccessibilityFocusedVirtualViewId();
-    }
-
-    /**
-     * Called when the focus state of a virtual view changes.
-     *
-     * @param virtualViewId the virtual view identifier
-     * @param hasFocus      {@code true} if the view has focus, {@code false}
-     *                      otherwise
-     */
-    protected void onVirtualViewKeyboardFocusChanged(int virtualViewId, boolean hasFocus) {
-        // Stub method.
-    }
-
-    /**
-     * Sets the currently hovered item, sending hover accessibility events as
-     * necessary to maintain the correct state.
-     *
-     * @param virtualViewId the virtual view id for the item currently being
-     *                      hovered, or {@link #INVALID_ID} if no item is
-     *                      hovered within the parent view
-     */
-    private void updateHoveredVirtualView(int virtualViewId) {
-        if (mHoveredVirtualViewId == virtualViewId) {
-            return;
-        }
-
-        final int previousVirtualViewId = mHoveredVirtualViewId;
-        mHoveredVirtualViewId = virtualViewId;
-
-        // Stay consistent with framework behavior by sending ENTER/EXIT pairs
-        // in reverse order. This is accurate as of API 18.
-        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
-        sendEventForVirtualView(
-                previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-    }
-
-    /**
-     * Constructs and returns an {@link AccessibilityEvent} for the specified
-     * virtual view id, which includes the host view ({@link #HOST_ID}).
-     *
-     * @param virtualViewId the virtual view id for the item for which to
-     *                      construct an event
-     * @param eventType the type of event to construct
-     * @return an {@link AccessibilityEvent} populated with information about
-     *         the specified item
-     */
-    private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
-        switch (virtualViewId) {
-            case HOST_ID:
-                return createEventForHost(eventType);
-            default:
-                return createEventForChild(virtualViewId, eventType);
-        }
-    }
-
-    /**
-     * Constructs and returns an {@link AccessibilityEvent} for the host node.
-     *
-     * @param eventType the type of event to construct
-     * @return an {@link AccessibilityEvent} populated with information about
-     *         the specified item
-     */
-    private AccessibilityEvent createEventForHost(int eventType) {
-        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        mHost.onInitializeAccessibilityEvent(event);
-        return event;
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(host, event);
-
-        // Allow the client to populate the event.
-        onPopulateEventForHost(event);
-    }
-
-    /**
-     * Constructs and returns an {@link AccessibilityEvent} populated with
-     * information about the specified item.
-     *
-     * @param virtualViewId the virtual view id for the item for which to
-     *                      construct an event
-     * @param eventType the type of event to construct
-     * @return an {@link AccessibilityEvent} populated with information about
-     *         the specified item
-     */
-    private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
-        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
-        final AccessibilityNodeInfoCompat node = obtainAccessibilityNodeInfo(virtualViewId);
-
-        // Allow the client to override these properties,
-        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);
-
-        // Make sure the developer is following the rules.
-        if (event.getText().isEmpty() && (event.getContentDescription() == null)) {
-            throw new RuntimeException("Callbacks must add text or a content description in "
-                    + "populateEventForVirtualViewId()");
-        }
-
-        // Don't allow the client to override these properties.
-        event.setClassName(node.getClassName());
-        AccessibilityRecordCompat.setSource(event, mHost, virtualViewId);
-        event.setPackageName(mHost.getContext().getPackageName());
-
-        return event;
-    }
-
-    /**
-     * Obtains a populated {@link AccessibilityNodeInfoCompat} for the
-     * virtual view with the specified identifier.
-     * <p>
-     * This method may be called with identifier {@link #HOST_ID} to obtain a
-     * node for the host view.
-     *
-     * @param virtualViewId the identifier of the virtual view for which to
-     *                      construct a node
-     * @return an {@link AccessibilityNodeInfoCompat} populated with information
-     *         about the specified item
-     */
-    @NonNull
-    AccessibilityNodeInfoCompat obtainAccessibilityNodeInfo(int virtualViewId) {
-        if (virtualViewId == HOST_ID) {
-            return createNodeForHost();
-        }
-
-        return createNodeForChild(virtualViewId);
-    }
-
-    /**
-     * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
-     * host view populated with its virtual descendants.
-     *
-     * @return an {@link AccessibilityNodeInfoCompat} for the parent node
-     */
-    @NonNull
-    private AccessibilityNodeInfoCompat createNodeForHost() {
-        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(mHost);
-        ViewCompat.onInitializeAccessibilityNodeInfo(mHost, info);
-
-        // Add the virtual descendants.
-        final ArrayList<Integer> virtualViewIds = new ArrayList<>();
-        getVisibleVirtualViews(virtualViewIds);
-
-        final int realNodeCount = info.getChildCount();
-        if (realNodeCount > 0 && virtualViewIds.size() > 0) {
-            throw new RuntimeException("Views cannot have both real and virtual children");
-        }
-
-        for (int i = 0, count = virtualViewIds.size(); i < count; i++) {
-            info.addChild(mHost, virtualViewIds.get(i));
-        }
-
-        return info;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-        super.onInitializeAccessibilityNodeInfo(host, info);
-
-        // Allow the client to populate the host node.
-        onPopulateNodeForHost(info);
-    }
-
-    /**
-     * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
-     * specified item. Automatically manages accessibility focus actions.
-     * <p>
-     * Allows the implementing class to specify most node properties, but
-     * overrides the following:
-     * <ul>
-     * <li>{@link AccessibilityNodeInfoCompat#setPackageName}
-     * <li>{@link AccessibilityNodeInfoCompat#setClassName}
-     * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
-     * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
-     * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
-     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
-     * </ul>
-     * <p>
-     * Uses the bounds of the parent view and the parent-relative bounding
-     * rectangle specified by
-     * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
-     * update the following properties:
-     * <ul>
-     * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
-     * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
-     * </ul>
-     *
-     * @param virtualViewId the virtual view id for item for which to construct
-     *                      a node
-     * @return an {@link AccessibilityNodeInfoCompat} for the specified item
-     */
-    @NonNull
-    private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
-        final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
-
-        // Ensure the client has good defaults.
-        node.setEnabled(true);
-        node.setFocusable(true);
-        node.setClassName(DEFAULT_CLASS_NAME);
-        node.setBoundsInParent(INVALID_PARENT_BOUNDS);
-        node.setBoundsInScreen(INVALID_PARENT_BOUNDS);
-        node.setParent(mHost);
-
-        // Allow the client to populate the node.
-        onPopulateNodeForVirtualView(virtualViewId, node);
-
-        // Make sure the developer is following the rules.
-        if ((node.getText() == null) && (node.getContentDescription() == null)) {
-            throw new RuntimeException("Callbacks must add text or a content description in "
-                    + "populateNodeForVirtualViewId()");
-        }
-
-        node.getBoundsInParent(mTempParentRect);
-        if (mTempParentRect.equals(INVALID_PARENT_BOUNDS)) {
-            throw new RuntimeException("Callbacks must set parent bounds in "
-                    + "populateNodeForVirtualViewId()");
-        }
-
-        final int actions = node.getActions();
-        if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
-            throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
-                    + "populateNodeForVirtualViewId()");
-        }
-        if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
-            throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
-                    + "populateNodeForVirtualViewId()");
-        }
-
-        // Don't allow the client to override these properties.
-        node.setPackageName(mHost.getContext().getPackageName());
-        node.setSource(mHost, virtualViewId);
-
-        // Manage internal accessibility focus state.
-        if (mAccessibilityFocusedVirtualViewId == virtualViewId) {
-            node.setAccessibilityFocused(true);
-            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
-        } else {
-            node.setAccessibilityFocused(false);
-            node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
-        }
-
-        // Manage internal keyboard focus state.
-        final boolean isFocused = mKeyboardFocusedVirtualViewId == virtualViewId;
-        if (isFocused) {
-            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS);
-        } else if (node.isFocusable()) {
-            node.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS);
-        }
-        node.setFocused(isFocused);
-
-        mHost.getLocationOnScreen(mTempGlobalRect);
-
-        // If not explicitly specified, calculate screen-relative bounds and
-        // offset for scroll position based on bounds in parent.
-        node.getBoundsInScreen(mTempScreenRect);
-        if (mTempScreenRect.equals(INVALID_PARENT_BOUNDS)) {
-            node.getBoundsInParent(mTempScreenRect);
-
-            // If there is a parent node, adjust bounds based on the parent node.
-            if (node.mParentVirtualDescendantId != HOST_ID) {
-                AccessibilityNodeInfoCompat parentNode = AccessibilityNodeInfoCompat.obtain();
-                // Walk up the node tree to adjust the screen rect.
-                for (int virtualDescendantId = node.mParentVirtualDescendantId;
-                        virtualDescendantId != HOST_ID;
-                        virtualDescendantId = parentNode.mParentVirtualDescendantId) {
-                    // Reset the values in the parent node we'll be using.
-                    parentNode.setParent(mHost, HOST_ID);
-                    parentNode.setBoundsInParent(INVALID_PARENT_BOUNDS);
-                    // Adjust the bounds for the parent node.
-                    onPopulateNodeForVirtualView(virtualDescendantId, parentNode);
-                    parentNode.getBoundsInParent(mTempParentRect);
-                    mTempScreenRect.offset(mTempParentRect.left, mTempParentRect.top);
-                }
-                parentNode.recycle();
-            }
-            // Adjust the rect for the host view's location.
-            mTempScreenRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
-                    mTempGlobalRect[1] - mHost.getScrollY());
-        }
-
-        if (mHost.getLocalVisibleRect(mTempVisibleRect)) {
-            mTempVisibleRect.offset(mTempGlobalRect[0] - mHost.getScrollX(),
-                    mTempGlobalRect[1] - mHost.getScrollY());
-            final boolean intersects = mTempScreenRect.intersect(mTempVisibleRect);
-            if (intersects) {
-                node.setBoundsInScreen(mTempScreenRect);
-
-                if (isVisibleToUser(mTempScreenRect)) {
-                    node.setVisibleToUser(true);
-                }
-            }
-        }
-
-        return node;
-    }
-
-    boolean performAction(int virtualViewId, int action, Bundle arguments) {
-        switch (virtualViewId) {
-            case HOST_ID:
-                return performActionForHost(action, arguments);
-            default:
-                return performActionForChild(virtualViewId, action, arguments);
-        }
-    }
-
-    private boolean performActionForHost(int action, Bundle arguments) {
-        return ViewCompat.performAccessibilityAction(mHost, action, arguments);
-    }
-
-    private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
-        switch (action) {
-            case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
-                return requestAccessibilityFocus(virtualViewId);
-            case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
-                return clearAccessibilityFocus(virtualViewId);
-            case AccessibilityNodeInfoCompat.ACTION_FOCUS:
-                return requestKeyboardFocusForVirtualView(virtualViewId);
-            case AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS:
-                return clearKeyboardFocusForVirtualView(virtualViewId);
-            default:
-                return onPerformActionForVirtualView(virtualViewId, action, arguments);
-        }
-    }
-
-    /**
-     * Computes whether the specified {@link Rect} intersects with the visible
-     * portion of its parent {@link View}. Modifies {@code localRect} to contain
-     * only the visible portion.
-     *
-     * @param localRect a rectangle in local (parent) coordinates
-     * @return whether the specified {@link Rect} is visible on the screen
-     */
-    private boolean isVisibleToUser(Rect localRect) {
-        // Missing or empty bounds mean this view is not visible.
-        if ((localRect == null) || localRect.isEmpty()) {
-            return false;
-        }
-
-        // Attached to invisible window means this view is not visible.
-        if (mHost.getWindowVisibility() != View.VISIBLE) {
-            return false;
-        }
-
-        // An invisible predecessor means that this view is not visible.
-        ViewParent viewParent = mHost.getParent();
-        while (viewParent instanceof View) {
-            final View view = (View) viewParent;
-            if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
-                return false;
-            }
-            viewParent = view.getParent();
-        }
-
-        // A null parent implies the view is not visible.
-        return viewParent != null;
-    }
-
-    /**
-     * Attempts to give accessibility focus to a virtual view.
-     * <p>
-     * A virtual view will not actually take focus if
-     * {@link AccessibilityManager#isEnabled()} returns false,
-     * {@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
-     * or the view already has accessibility focus.
-     *
-     * @param virtualViewId the identifier of the virtual view on which to
-     *                      place accessibility focus
-     * @return whether this virtual view actually took accessibility focus
-     */
-    private boolean requestAccessibilityFocus(int virtualViewId) {
-        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
-            return false;
-        }
-        // TODO: Check virtual view visibility.
-        if (mAccessibilityFocusedVirtualViewId != virtualViewId) {
-            // Clear focus from the previously focused view, if applicable.
-            if (mAccessibilityFocusedVirtualViewId != INVALID_ID) {
-                clearAccessibilityFocus(mAccessibilityFocusedVirtualViewId);
-            }
-
-            // Set focus on the new view.
-            mAccessibilityFocusedVirtualViewId = virtualViewId;
-
-            // TODO: Only invalidate virtual view bounds.
-            mHost.invalidate();
-            sendEventForVirtualView(virtualViewId,
-                    AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Attempts to clear accessibility focus from a virtual view.
-     *
-     * @param virtualViewId the identifier of the virtual view from which to
-     *                      clear accessibility focus
-     * @return whether this virtual view actually cleared accessibility focus
-     */
-    private boolean clearAccessibilityFocus(int virtualViewId) {
-        if (mAccessibilityFocusedVirtualViewId == virtualViewId) {
-            mAccessibilityFocusedVirtualViewId = INVALID_ID;
-            mHost.invalidate();
-            sendEventForVirtualView(virtualViewId,
-                    AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Attempts to give keyboard focus to a virtual view.
-     *
-     * @param virtualViewId the identifier of the virtual view on which to
-     *                      place keyboard focus
-     * @return whether this virtual view actually took keyboard focus
-     */
-    public final boolean requestKeyboardFocusForVirtualView(int virtualViewId) {
-        if (!mHost.isFocused() && !mHost.requestFocus()) {
-            // Host must have real keyboard focus.
-            return false;
-        }
-
-        if (mKeyboardFocusedVirtualViewId == virtualViewId) {
-            // The virtual view already has focus.
-            return false;
-        }
-
-        if (mKeyboardFocusedVirtualViewId != INVALID_ID) {
-            clearKeyboardFocusForVirtualView(mKeyboardFocusedVirtualViewId);
-        }
-
-        mKeyboardFocusedVirtualViewId = virtualViewId;
-
-        onVirtualViewKeyboardFocusChanged(virtualViewId, true);
-        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED);
-
-        return true;
-    }
-
-    /**
-     * Attempts to clear keyboard focus from a virtual view.
-     *
-     * @param virtualViewId the identifier of the virtual view from which to
-     *                      clear keyboard focus
-     * @return whether this virtual view actually cleared keyboard focus
-     */
-    public final boolean clearKeyboardFocusForVirtualView(int virtualViewId) {
-        if (mKeyboardFocusedVirtualViewId != virtualViewId) {
-            // The virtual view is not focused.
-            return false;
-        }
-
-        mKeyboardFocusedVirtualViewId = INVALID_ID;
-
-        onVirtualViewKeyboardFocusChanged(virtualViewId, false);
-        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_FOCUSED);
-
-        return true;
-    }
-
-    /**
-     * Provides a mapping between view-relative coordinates and logical
-     * items.
-     *
-     * @param x The view-relative x coordinate
-     * @param y The view-relative y coordinate
-     * @return virtual view identifier for the logical item under
-     *         coordinates (x,y) or {@link #HOST_ID} if there is no item at
-     *         the given coordinates
-     */
-    protected abstract int getVirtualViewAt(float x, float y);
-
-    /**
-     * Populates a list with the view's visible items. The ordering of items
-     * within {@code virtualViewIds} specifies order of accessibility focus
-     * traversal.
-     *
-     * @param virtualViewIds The list to populate with visible items
-     */
-    protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds);
-
-    /**
-     * Populates an {@link AccessibilityEvent} with information about the
-     * specified item.
-     * <p>
-     * The helper class automatically populates the following fields based on
-     * the values set by
-     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)},
-     * but implementations may optionally override them:
-     * <ul>
-     * <li>event text, see {@link AccessibilityEvent#getText()}
-     * <li>content description, see
-     * {@link AccessibilityEvent#setContentDescription(CharSequence)}
-     * <li>scrollability, see {@link AccessibilityEvent#setScrollable(boolean)}
-     * <li>password state, see {@link AccessibilityEvent#setPassword(boolean)}
-     * <li>enabled state, see {@link AccessibilityEvent#setEnabled(boolean)}
-     * <li>checked state, see {@link AccessibilityEvent#setChecked(boolean)}
-     * </ul>
-     * <p>
-     * The following required fields are automatically populated by the
-     * helper class and may not be overridden:
-     * <ul>
-     * <li>item class name, set to the value used in
-     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}
-     * <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(AccessibilityRecord, View, int)}
-     * </ul>
-     *
-     * @param virtualViewId The virtual view id for the item for which to
-     *            populate the event
-     * @param event The event to populate
-     */
-    protected void onPopulateEventForVirtualView(int virtualViewId,
-            @NonNull AccessibilityEvent event) {
-        // Default implementation is no-op.
-    }
-
-    /**
-     * Populates an {@link AccessibilityEvent} with information about the host
-     * view.
-     * <p>
-     * The default implementation is a no-op.
-     *
-     * @param event the event to populate with information about the host view
-     */
-    protected void onPopulateEventForHost(@NonNull AccessibilityEvent event) {
-        // Default implementation is no-op.
-    }
-
-    /**
-     * Populates an {@link AccessibilityNodeInfoCompat} with information
-     * about the specified item.
-     * <p>
-     * Implementations <strong>must</strong> populate the following required
-     * fields:
-     * <ul>
-     * <li>event text, see
-     * {@link AccessibilityNodeInfoCompat#setText(CharSequence)} or
-     * {@link AccessibilityNodeInfoCompat#setContentDescription(CharSequence)}
-     * <li>bounds in parent coordinates, see
-     * {@link AccessibilityNodeInfoCompat#setBoundsInParent(Rect)}
-     * </ul>
-     * <p>
-     * The helper class automatically populates the following fields with
-     * default values, but implementations may optionally override them:
-     * <ul>
-     * <li>enabled state, set to {@code true}, see
-     * {@link AccessibilityNodeInfoCompat#setEnabled(boolean)}
-     * <li>keyboard focusability, set to {@code true}, see
-     * {@link AccessibilityNodeInfoCompat#setFocusable(boolean)}
-     * <li>item class name, set to {@code android.view.View}, see
-     * {@link AccessibilityNodeInfoCompat#setClassName(CharSequence)}
-     * </ul>
-     * <p>
-     * The following required fields are automatically populated by the
-     * helper class and may not be overridden:
-     * <ul>
-     * <li>package name, identical to the package name set by
-     * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see
-     * {@link AccessibilityNodeInfoCompat#setPackageName}
-     * <li>node source, identical to the event source set in
-     * {@link #onPopulateEventForVirtualView(int, AccessibilityEvent)}, see
-     * {@link AccessibilityNodeInfoCompat#setSource(View, int)}
-     * <li>parent view, set to the host view, see
-     * {@link AccessibilityNodeInfoCompat#setParent(View)}
-     * <li>visibility, computed based on parent-relative bounds, see
-     * {@link AccessibilityNodeInfoCompat#setVisibleToUser(boolean)}
-     * <li>accessibility focus, computed based on internal helper state, see
-     * {@link AccessibilityNodeInfoCompat#setAccessibilityFocused(boolean)}
-     * <li>keyboard focus, computed based on internal helper state, see
-     * {@link AccessibilityNodeInfoCompat#setFocused(boolean)}
-     * <li>bounds in screen coordinates, computed based on host view bounds,
-     * see {@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
-     * </ul>
-     * <p>
-     * Additionally, the helper class automatically handles keyboard focus and
-     * accessibility focus management by adding the appropriate
-     * {@link AccessibilityNodeInfoCompat#ACTION_FOCUS},
-     * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_FOCUS},
-     * {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}, or
-     * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
-     * actions. Implementations must <strong>never</strong> manually add these
-     * actions.
-     * <p>
-     * The helper class also automatically modifies parent- and
-     * screen-relative bounds to reflect the portion of the item visible
-     * within its parent.
-     *
-     * @param virtualViewId The virtual view identifier of the item for
-     *            which to populate the node
-     * @param node The node to populate
-     */
-    protected abstract void onPopulateNodeForVirtualView(
-            int virtualViewId, @NonNull AccessibilityNodeInfoCompat node);
-
-    /**
-     * Populates an {@link AccessibilityNodeInfoCompat} with information
-     * about the host view.
-     * <p>
-     * The default implementation is a no-op.
-     *
-     * @param node the node to populate with information about the host view
-     */
-    protected void onPopulateNodeForHost(@NonNull AccessibilityNodeInfoCompat node) {
-        // Default implementation is no-op.
-    }
-
-    /**
-     * Performs the specified accessibility action on the item associated
-     * with the virtual view identifier. See
-     * {@link AccessibilityNodeInfoCompat#performAction(int, Bundle)} for
-     * more information.
-     * <p>
-     * Implementations <strong>must</strong> handle any actions added manually
-     * in
-     * {@link #onPopulateNodeForVirtualView(int, AccessibilityNodeInfoCompat)}.
-     * <p>
-     * The helper class automatically handles focus management resulting
-     * from {@link AccessibilityNodeInfoCompat#ACTION_ACCESSIBILITY_FOCUS}
-     * and
-     * {@link AccessibilityNodeInfoCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
-     * actions.
-     *
-     * @param virtualViewId The virtual view identifier of the item on which
-     *            to perform the action
-     * @param action The accessibility action to perform
-     * @param arguments (Optional) A bundle with additional arguments, or
-     *            null
-     * @return true if the action was performed
-     */
-    protected abstract boolean onPerformActionForVirtualView(
-            int virtualViewId, int action, @Nullable Bundle arguments);
-
-    /**
-     * Exposes a virtual view hierarchy to the accessibility framework.
-     */
-    private class MyNodeProvider extends AccessibilityNodeProviderCompat {
-        MyNodeProvider() {
-        }
-
-        @Override
-        public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
-            // The caller takes ownership of the node and is expected to
-            // recycle it when done, so always return a copy.
-            final AccessibilityNodeInfoCompat node =
-                    ExploreByTouchHelper.this.obtainAccessibilityNodeInfo(virtualViewId);
-            return AccessibilityNodeInfoCompat.obtain(node);
-        }
-
-        @Override
-        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
-            return ExploreByTouchHelper.this.performAction(virtualViewId, action, arguments);
-        }
-
-        @Override
-        public AccessibilityNodeInfoCompat findFocus(int focusType) {
-            int focusedId = (focusType == AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY) ?
-                    mAccessibilityFocusedVirtualViewId : mKeyboardFocusedVirtualViewId;
-            if (focusedId == INVALID_ID) {
-                return null;
-            }
-            return createAccessibilityNodeInfo(focusedId);
-        }
-    }
-}
diff --git a/android/support/v4/widget/FocusStrategy.java b/android/support/v4/widget/FocusStrategy.java
deleted file mode 100644
index 77353c5..0000000
--- a/android/support/v4/widget/FocusStrategy.java
+++ /dev/null
@@ -1,454 +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.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View;
-
-import android.support.v4.view.ViewCompat.FocusRealDirection;
-import android.support.v4.view.ViewCompat.FocusRelativeDirection;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-/**
- * Implements absolute and relative focus movement strategies. Adapted from
- * android.view.FocusFinder to work with generic collections of bounded items.
- */
-class FocusStrategy {
-    public static <L, T> T findNextFocusInRelativeDirection(@NonNull L focusables,
-            @NonNull CollectionAdapter<L, T> collectionAdapter, @NonNull BoundsAdapter<T> adapter,
-            @Nullable T focused, @FocusRelativeDirection int direction, boolean isLayoutRtl,
-            boolean wrap) {
-        final int count = collectionAdapter.size(focusables);
-        final ArrayList<T> sortedFocusables = new ArrayList<>(count);
-        for (int i = 0; i < count; i++) {
-            sortedFocusables.add(collectionAdapter.get(focusables, i));
-        }
-
-        final SequentialComparator<T> comparator = new SequentialComparator<>(isLayoutRtl, adapter);
-        Collections.sort(sortedFocusables, comparator);
-
-        switch (direction) {
-            case View.FOCUS_FORWARD:
-                return getNextFocusable(focused, sortedFocusables, wrap);
-            case View.FOCUS_BACKWARD:
-                return getPreviousFocusable(focused, sortedFocusables, wrap);
-            default:
-                throw new IllegalArgumentException("direction must be one of "
-                        + "{FOCUS_FORWARD, FOCUS_BACKWARD}.");
-        }
-    }
-
-    private static <T> T getNextFocusable(T focused, ArrayList<T> focusables, boolean wrap) {
-        final int count = focusables.size();
-
-        // The position of the next focusable item, which is the first item if
-        // no item is currently focused.
-        final int position = (focused == null ? -1 : focusables.lastIndexOf(focused)) + 1;
-        if (position < count) {
-            return focusables.get(position);
-        } else if (wrap && count > 0) {
-            return focusables.get(0);
-        } else {
-            return null;
-        }
-    }
-
-    private static <T> T getPreviousFocusable(T focused, ArrayList<T> focusables, boolean wrap) {
-        final int count = focusables.size();
-
-        // The position of the previous focusable item, which is the last item
-        // if no item is currently focused.
-        final int position = (focused == null ? count : focusables.indexOf(focused)) - 1;
-        if (position >= 0) {
-            return focusables.get(position);
-        } else if (wrap && count > 0) {
-            return focusables.get(count - 1);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Sorts views according to their visual layout and geometry for default tab order.
-     * This is used for sequential focus traversal.
-     */
-    private static class SequentialComparator<T> implements Comparator<T> {
-        private final Rect mTemp1 = new Rect();
-        private final Rect mTemp2 = new Rect();
-
-        private final boolean mIsLayoutRtl;
-        private final BoundsAdapter<T> mAdapter;
-
-        SequentialComparator(boolean isLayoutRtl, BoundsAdapter<T> adapter) {
-            mIsLayoutRtl = isLayoutRtl;
-            mAdapter = adapter;
-        }
-
-        @Override
-        public int compare(T first, T second) {
-            final Rect firstRect = mTemp1;
-            final Rect secondRect = mTemp2;
-
-            mAdapter.obtainBounds(first, firstRect);
-            mAdapter.obtainBounds(second, secondRect);
-
-            if (firstRect.top < secondRect.top) {
-                return -1;
-            } else if (firstRect.top > secondRect.top) {
-                return 1;
-            } else if (firstRect.left < secondRect.left) {
-                return mIsLayoutRtl ? 1 : -1;
-            } else if (firstRect.left > secondRect.left) {
-                return mIsLayoutRtl ? -1 : 1;
-            } else if (firstRect.bottom < secondRect.bottom) {
-                return -1;
-            } else if (firstRect.bottom > secondRect.bottom) {
-                return 1;
-            } else if (firstRect.right < secondRect.right) {
-                return mIsLayoutRtl ? 1 : -1;
-            } else if (firstRect.right > secondRect.right) {
-                return mIsLayoutRtl ? -1 : 1;
-            } else {
-                // The view are distinct but completely coincident so we
-                // consider them equal for our purposes. Since the sort is
-                // stable, this means that the views will retain their
-                // layout order relative to one another.
-                return 0;
-            }
-        }
-    }
-
-    public static <L, T> T findNextFocusInAbsoluteDirection(@NonNull L focusables,
-            @NonNull CollectionAdapter<L, T> collectionAdapter, @NonNull BoundsAdapter<T> adapter,
-            @Nullable T focused, @NonNull Rect focusedRect, int direction) {
-        // Initialize the best candidate to something impossible so that
-        // the first plausible view will become the best choice.
-        final Rect bestCandidateRect = new Rect(focusedRect);
-
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                bestCandidateRect.offset(focusedRect.width() + 1, 0);
-                break;
-            case View.FOCUS_RIGHT:
-                bestCandidateRect.offset(-(focusedRect.width() + 1), 0);
-                break;
-            case View.FOCUS_UP:
-                bestCandidateRect.offset(0, focusedRect.height() + 1);
-                break;
-            case View.FOCUS_DOWN:
-                bestCandidateRect.offset(0, -(focusedRect.height() + 1));
-                break;
-            default:
-                throw new IllegalArgumentException("direction must be one of "
-                        + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-        }
-
-        T closest = null;
-
-        final int count = collectionAdapter.size(focusables);
-        final Rect focusableRect = new Rect();
-        for (int i = 0; i < count; i++) {
-            final T focusable = collectionAdapter.get(focusables, i);
-            if (focusable == focused) {
-                continue;
-            }
-
-            // get focus bounds of other view
-            adapter.obtainBounds(focusable, focusableRect);
-            if (isBetterCandidate(direction, focusedRect, focusableRect, bestCandidateRect)) {
-                bestCandidateRect.set(focusableRect);
-                closest = focusable;
-            }
-        }
-
-        return closest;
-    }
-
-    /**
-     * Is candidate a better candidate than currentBest for a focus search
-     * in a particular direction from a source rect? This is the core
-     * routine that determines the order of focus searching.
-     *
-     * @param direction   the direction (up, down, left, right)
-     * @param source      the source from which we are searching
-     * @param candidate   the candidate rectangle
-     * @param currentBest the current best rectangle
-     * @return {@code true} if the candidate rectangle is a better than the
-     * current best rectangle, {@code false} otherwise
-     */
-    private static boolean isBetterCandidate(
-            @FocusRealDirection int direction, @NonNull Rect source,
-            @NonNull Rect candidate, @NonNull Rect currentBest) {
-        // To be a better candidate, need to at least be a candidate in the
-        // first place. :)
-        if (!isCandidate(source, candidate, direction)) {
-            return false;
-        }
-
-        // We know that candidateRect is a candidate. If currentBest is not
-        // a candidate, candidateRect is better.
-        if (!isCandidate(source, currentBest, direction)) {
-            return true;
-        }
-
-        // If candidateRect is better by beam, it wins.
-        if (beamBeats(direction, source, candidate, currentBest)) {
-            return true;
-        }
-
-        // If currentBest is better, then candidateRect cant' be. :)
-        if (beamBeats(direction, source, currentBest, candidate)) {
-            return false;
-        }
-
-        // Otherwise, do fudge-tastic comparison of the major and minor
-        // axis.
-        final int candidateDist = getWeightedDistanceFor(
-                majorAxisDistance(direction, source, candidate),
-                minorAxisDistance(direction, source, candidate));
-        final int currentBestDist = getWeightedDistanceFor(
-                majorAxisDistance(direction, source, currentBest),
-                minorAxisDistance(direction, source, currentBest));
-        return candidateDist < currentBestDist;
-    }
-
-    /**
-     * One rectangle may be another candidate than another by virtue of
-     * being exclusively in the beam of the source rect.
-     *
-     * @return whether rect1 is a better candidate than rect2 by virtue of
-     * it being in source's beam
-     */
-    private static boolean beamBeats(@FocusRealDirection int direction,
-            @NonNull Rect source, @NonNull Rect rect1, @NonNull Rect rect2) {
-        final boolean rect1InSrcBeam = beamsOverlap(direction, source, rect1);
-        final boolean rect2InSrcBeam = beamsOverlap(direction, source, rect2);
-
-        // If rect1 isn't exclusively in the src beam, it doesn't win.
-        if (rect2InSrcBeam || !rect1InSrcBeam) {
-            return false;
-        }
-
-        // We know rect1 is in the beam, and rect2 is not.
-
-        // If rect1 is to the direction of, and rect2 is not, rect1 wins.
-        // For example, for direction left, if rect1 is to the left of the
-        // source and rect2 is below, then we always prefer the in beam
-        // rect1, since rect2 could be reached by going down.
-        if (!isToDirectionOf(direction, source, rect2)) {
-            return true;
-        }
-
-        // For horizontal directions, being exclusively in beam always
-        // wins.
-        if (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT) {
-            return true;
-        }
-
-        // For vertical directions, beams only beat up to a point: now, as
-        // long as rect2 isn't completely closer, rect1 wins, e.g. for
-        // direction down, completely closer means for rect2's top edge to
-        // be closer to the source's top edge than rect1's bottom edge.
-        return majorAxisDistance(direction, source, rect1)
-                < majorAxisDistanceToFarEdge(direction, source, rect2);
-    }
-
-    /**
-     * Fudge-factor opportunity: how to calculate distance given major and
-     * minor axis distances.
-     * <p/>
-     * Warning: this fudge factor is finely tuned, be sure to run all focus
-     * tests if you dare tweak it.
-     */
-    private static int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) {
-        return 13 * majorAxisDistance * majorAxisDistance
-                + minorAxisDistance * minorAxisDistance;
-    }
-
-    /**
-     * Is destRect a candidate for the next focus given the direction? This
-     * checks whether the dest is at least partially to the direction of
-     * (e.g. left of) from source.
-     * <p/>
-     * Includes an edge case for an empty rect,which is used in some cases
-     * when searching from a point on the screen.
-     */
-    private static boolean isCandidate(@NonNull Rect srcRect, @NonNull Rect destRect,
-            @FocusRealDirection int direction) {
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                return (srcRect.right > destRect.right || srcRect.left >= destRect.right)
-                        && srcRect.left > destRect.left;
-            case View.FOCUS_RIGHT:
-                return (srcRect.left < destRect.left || srcRect.right <= destRect.left)
-                        && srcRect.right < destRect.right;
-            case View.FOCUS_UP:
-                return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom)
-                        && srcRect.top > destRect.top;
-            case View.FOCUS_DOWN:
-                return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top)
-                        && srcRect.bottom < destRect.bottom;
-        }
-        throw new IllegalArgumentException("direction must be one of "
-                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-    }
-
-
-    /**
-     * Do the "beams" w.r.t the given direction's axis of rect1 and rect2 overlap?
-     *
-     * @param direction the direction (up, down, left, right)
-     * @param rect1     the first rectangle
-     * @param rect2     the second rectangle
-     * @return whether the beams overlap
-     */
-    private static boolean beamsOverlap(@FocusRealDirection int direction,
-            @NonNull Rect rect1, @NonNull Rect rect2) {
-        switch (direction) {
-            case View.FOCUS_LEFT:
-            case View.FOCUS_RIGHT:
-                return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom);
-            case View.FOCUS_UP:
-            case View.FOCUS_DOWN:
-                return (rect2.right >= rect1.left) && (rect2.left <= rect1.right);
-        }
-        throw new IllegalArgumentException("direction must be one of "
-                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-    }
-
-    /**
-     * e.g for left, is 'to left of'
-     */
-    private static boolean isToDirectionOf(@FocusRealDirection int direction,
-            @NonNull Rect src, @NonNull Rect dest) {
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                return src.left >= dest.right;
-            case View.FOCUS_RIGHT:
-                return src.right <= dest.left;
-            case View.FOCUS_UP:
-                return src.top >= dest.bottom;
-            case View.FOCUS_DOWN:
-                return src.bottom <= dest.top;
-        }
-        throw new IllegalArgumentException("direction must be one of "
-                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-    }
-
-    /**
-     * @return the distance from the edge furthest in the given direction
-     * of source to the edge nearest in the given direction of
-     * dest. If the dest is not in the direction from source,
-     * returns 0.
-     */
-    private static int majorAxisDistance(@FocusRealDirection int direction,
-            @NonNull Rect source, @NonNull Rect dest) {
-        return Math.max(0, majorAxisDistanceRaw(direction, source, dest));
-    }
-
-    private static int majorAxisDistanceRaw(@FocusRealDirection int direction,
-            @NonNull Rect source, @NonNull Rect dest) {
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                return source.left - dest.right;
-            case View.FOCUS_RIGHT:
-                return dest.left - source.right;
-            case View.FOCUS_UP:
-                return source.top - dest.bottom;
-            case View.FOCUS_DOWN:
-                return dest.top - source.bottom;
-        }
-        throw new IllegalArgumentException("direction must be one of "
-                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-    }
-
-    /**
-     * @return the distance along the major axis w.r.t the direction from
-     * the edge of source to the far edge of dest. If the dest is
-     * not in the direction from source, returns 1 to break ties
-     * with {@link #majorAxisDistance}.
-     */
-    private static int majorAxisDistanceToFarEdge(@FocusRealDirection int direction,
-            @NonNull Rect source, @NonNull Rect dest) {
-        return Math.max(1, majorAxisDistanceToFarEdgeRaw(direction, source, dest));
-    }
-
-    private static int majorAxisDistanceToFarEdgeRaw(
-            @FocusRealDirection int direction, @NonNull Rect source,
-            @NonNull Rect dest) {
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                return source.left - dest.left;
-            case View.FOCUS_RIGHT:
-                return dest.right - source.right;
-            case View.FOCUS_UP:
-                return source.top - dest.top;
-            case View.FOCUS_DOWN:
-                return dest.bottom - source.bottom;
-        }
-        throw new IllegalArgumentException("direction must be one of "
-                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-    }
-
-    /**
-     * Finds the distance on the minor axis w.r.t the direction to the
-     * nearest edge of the destination rectangle.
-     *
-     * @param direction the direction (up, down, left, right)
-     * @param source the source rect
-     * @param dest the destination rect
-     * @return the distance
-     */
-    private static int minorAxisDistance(@FocusRealDirection int direction, @NonNull Rect source,
-            @NonNull Rect dest) {
-        switch (direction) {
-            case View.FOCUS_LEFT:
-            case View.FOCUS_RIGHT:
-                // the distance between the center verticals
-                return Math.abs(
-                        ((source.top + source.height() / 2) - ((dest.top + dest.height() / 2))));
-            case View.FOCUS_UP:
-            case View.FOCUS_DOWN:
-                // the distance between the center horizontals
-                return Math.abs(
-                        ((source.left + source.width() / 2) -
-                                ((dest.left + dest.width() / 2))));
-        }
-        throw new IllegalArgumentException("direction must be one of "
-                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
-    }
-
-    /**
-     * Adapter used to obtain bounds from a generic data type.
-     */
-    public interface BoundsAdapter<T> {
-        void obtainBounds(T data, Rect outBounds);
-    }
-
-    /**
-     * Adapter used to obtain items from a generic collection type.
-     */
-    public interface CollectionAdapter<T, V> {
-        V get(T collection, int index);
-        int size(T collection);
-    }
-}
diff --git a/android/support/v4/widget/ImageViewCompat.java b/android/support/v4/widget/ImageViewCompat.java
deleted file mode 100644
index b517de5..0000000
--- a/android/support/v4/widget/ImageViewCompat.java
+++ /dev/null
@@ -1,166 +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.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.widget.ImageView;
-
-/**
- * Helper for accessing features in {@link ImageView}.
- */
-public class ImageViewCompat {
-    interface ImageViewCompatImpl {
-        ColorStateList getImageTintList(ImageView view);
-
-        void setImageTintList(ImageView view, ColorStateList tintList);
-
-        PorterDuff.Mode getImageTintMode(ImageView view);
-
-        void setImageTintMode(ImageView view, PorterDuff.Mode mode);
-    }
-
-    static class BaseViewCompatImpl implements ImageViewCompatImpl {
-        @Override
-        public ColorStateList getImageTintList(ImageView view) {
-            return (view instanceof TintableImageSourceView)
-                    ? ((TintableImageSourceView) view).getSupportImageTintList()
-                    : null;
-        }
-
-        @Override
-        public void setImageTintList(ImageView view, ColorStateList tintList) {
-            if (view instanceof TintableImageSourceView) {
-                ((TintableImageSourceView) view).setSupportImageTintList(tintList);
-            }
-        }
-
-        @Override
-        public void setImageTintMode(ImageView view, PorterDuff.Mode mode) {
-            if (view instanceof TintableImageSourceView) {
-                ((TintableImageSourceView) view).setSupportImageTintMode(mode);
-            }
-        }
-
-        @Override
-        public PorterDuff.Mode getImageTintMode(ImageView view) {
-            return (view instanceof TintableImageSourceView)
-                    ? ((TintableImageSourceView) view).getSupportImageTintMode()
-                    : null;
-        }
-    }
-
-    @RequiresApi(21)
-    static class LollipopViewCompatImpl extends BaseViewCompatImpl {
-        @Override
-        public ColorStateList getImageTintList(ImageView view) {
-            return view.getImageTintList();
-        }
-
-        @Override
-        public 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);
-                }
-            }
-        }
-
-        @Override
-        public 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);
-                }
-            }
-        }
-
-        @Override
-        public PorterDuff.Mode getImageTintMode(ImageView view) {
-            return view.getImageTintMode();
-        }
-    }
-
-    static final ImageViewCompatImpl IMPL;
-    static {
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            IMPL = new LollipopViewCompatImpl();
-        } else {
-            IMPL = new BaseViewCompatImpl();
-        }
-    }
-
-    /**
-     * Return the tint applied to the image drawable, if specified.
-     */
-    @Nullable
-    public static ColorStateList getImageTintList(@NonNull ImageView view) {
-        return IMPL.getImageTintList(view);
-    }
-
-    /**
-     * Applies a tint to the image drawable.
-     */
-    public static void setImageTintList(@NonNull ImageView view,
-            @Nullable ColorStateList tintList) {
-        IMPL.setImageTintList(view, tintList);
-    }
-
-    /**
-     * Return the blending mode used to apply the tint to the image drawable, if specified.
-     */
-    @Nullable
-    public static PorterDuff.Mode getImageTintMode(@NonNull ImageView view) {
-        return IMPL.getImageTintMode(view);
-    }
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setImageTintList(android.widget.ImageView, android.content.res.ColorStateList)}
-     * to the image drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
-     */
-    public static void setImageTintMode(@NonNull ImageView view, @Nullable PorterDuff.Mode mode) {
-        IMPL.setImageTintMode(view, mode);
-    }
-
-    private ImageViewCompat() {}
-}
diff --git a/android/support/v4/widget/ListPopupWindowCompat.java b/android/support/v4/widget/ListPopupWindowCompat.java
deleted file mode 100644
index 4532733..0000000
--- a/android/support/v4/widget/ListPopupWindowCompat.java
+++ /dev/null
@@ -1,102 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.widget.ListPopupWindow;
-
-/**
- * Helper for accessing features in {@link ListPopupWindow}.
- */
-public final class ListPopupWindowCompat {
-    private ListPopupWindowCompat() {
-        // This class is not publicly instantiable.
-    }
-
-    /**
-     * 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
-     *
-     * @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
-     */
-    @Nullable
-    public static OnTouchListener createDragToOpenListener(
-            @NonNull ListPopupWindow listPopupWindow, @NonNull View src) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return listPopupWindow.createDragToOpenListener(src);
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/android/support/v4/widget/ListViewAutoScrollHelper.java b/android/support/v4/widget/ListViewAutoScrollHelper.java
deleted file mode 100644
index c373f27..0000000
--- a/android/support/v4/widget/ListViewAutoScrollHelper.java
+++ /dev/null
@@ -1,83 +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.NonNull;
-import android.view.View;
-import android.widget.ListView;
-
-/**
- * An implementation of {@link AutoScrollHelper} that knows how to scroll
- * through a {@link ListView}.
- */
-public class ListViewAutoScrollHelper extends AutoScrollHelper {
-    private final ListView mTarget;
-
-    public ListViewAutoScrollHelper(@NonNull ListView target) {
-        super(target);
-
-        mTarget = target;
-    }
-
-    @Override
-    public void scrollTargetBy(int deltaX, int deltaY) {
-        ListViewCompat.scrollListBy(mTarget, deltaY);
-    }
-
-    @Override
-    public boolean canTargetScrollHorizontally(int direction) {
-        // List do not scroll horizontally.
-        return false;
-    }
-
-    @Override
-    public boolean canTargetScrollVertically(int direction) {
-        final ListView target = mTarget;
-        final int itemCount = target.getCount();
-        if (itemCount == 0) {
-            return false;
-        }
-
-        final int childCount = target.getChildCount();
-        final int firstPosition = target.getFirstVisiblePosition();
-        final int lastPosition = firstPosition + childCount;
-
-        if (direction > 0) {
-            // Are we already showing the entire last item?
-            if (lastPosition >= itemCount) {
-                final View lastView = target.getChildAt(childCount - 1);
-                if (lastView.getBottom() <= target.getHeight()) {
-                    return false;
-                }
-            }
-        } else if (direction < 0) {
-            // Are we already showing the entire first item?
-            if (firstPosition <= 0) {
-                final View firstView = target.getChildAt(0);
-                if (firstView.getTop() >= 0) {
-                    return false;
-                }
-            }
-        } else {
-            // The behavior for direction 0 is undefined and we can return
-            // whatever we want.
-            return false;
-        }
-
-        return true;
-    }
-}
diff --git a/android/support/v4/widget/ListViewCompat.java b/android/support/v4/widget/ListViewCompat.java
deleted file mode 100644
index 59ee741..0000000
--- a/android/support/v4/widget/ListViewCompat.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.v4.widget;
-
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.view.View;
-import android.widget.ListView;
-
-/**
- * Helper for accessing features in {@link ListView}
- */
-public final class ListViewCompat {
-
-    /**
-     * Scrolls the list items within the view by a specified number of pixels.
-     *
-     * @param listView the list to scroll
-     * @param y the amount of pixels to scroll by vertically
-     */
-    public static void scrollListBy(@NonNull ListView listView, int y) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            // Call the framework version directly
-            listView.scrollListBy(y);
-        } else {
-            // 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();
-            }
-        }
-    }
-
-    private ListViewCompat() {}
-}
diff --git a/android/support/v4/widget/NestedScrollView.java b/android/support/v4/widget/NestedScrollView.java
deleted file mode 100644
index 6fe1928..0000000
--- a/android/support/v4/widget/NestedScrollView.java
+++ /dev/null
@@ -1,2026 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.InputDeviceCompat;
-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.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.FocusFinder;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-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;
-
-/**
- * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting
- * as both a nested scrolling parent and child on both new and old versions of Android.
- * Nested scrolling is enabled by default.
- */
-public class NestedScrollView extends FrameLayout implements NestedScrollingParent,
-        NestedScrollingChild2, ScrollingView {
-    static final int ANIMATED_SCROLL_GAP = 250;
-
-    static final float MAX_SCROLL_FACTOR = 0.5f;
-
-    private static final String TAG = "NestedScrollView";
-
-    /**
-     * Interface definition for a callback to be invoked when the scroll
-     * X or Y positions of a view change.
-     *
-     * <p>This version of the interface works on all versions of Android, back to API v4.</p>
-     *
-     * @see #setOnScrollChangeListener(OnScrollChangeListener)
-     */
-    public interface OnScrollChangeListener {
-        /**
-         * Called when the scroll position of a view changes.
-         *
-         * @param v The view whose scroll position has changed.
-         * @param scrollX Current horizontal scroll origin.
-         * @param scrollY Current vertical scroll origin.
-         * @param oldScrollX Previous horizontal scroll origin.
-         * @param oldScrollY Previous vertical scroll origin.
-         */
-        void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
-                int oldScrollX, int oldScrollY);
-    }
-
-    private long mLastScroll;
-
-    private final Rect mTempRect = new Rect();
-    private OverScroller mScroller;
-    private EdgeEffect mEdgeGlowTop;
-    private EdgeEffect mEdgeGlowBottom;
-
-    /**
-     * Position of the last motion event.
-     */
-    private int mLastMotionY;
-
-    /**
-     * True when the layout has changed but the traversal has not come through yet.
-     * Ideally the view hierarchy would keep track of this for us.
-     */
-    private boolean mIsLayoutDirty = true;
-    private boolean mIsLaidOut = false;
-
-    /**
-     * The child to give focus to in the event that a child has requested focus while the
-     * layout is dirty. This prevents the scroll from being wrong if the child has not been
-     * laid out before requesting focus.
-     */
-    private View mChildToScrollTo = null;
-
-    /**
-     * True if the user is currently dragging this ScrollView around. This is
-     * not the same as 'is being flinged', which can be checked by
-     * mScroller.isFinished() (flinging begins when the user lifts his finger).
-     */
-    private boolean mIsBeingDragged = false;
-
-    /**
-     * Determines speed during touch scrolling
-     */
-    private VelocityTracker mVelocityTracker;
-
-    /**
-     * When set to true, the scroll view measure its child to make it fill the currently
-     * visible area.
-     */
-    private boolean mFillViewport;
-
-    /**
-     * Whether arrow scrolling is animated.
-     */
-    private boolean mSmoothScrollingEnabled = true;
-
-    private int mTouchSlop;
-    private int mMinimumVelocity;
-    private int mMaximumVelocity;
-
-    /**
-     * ID of the active pointer. This is used to retain consistency during
-     * drags/flings if multiple pointers are used.
-     */
-    private int mActivePointerId = INVALID_POINTER;
-
-    /**
-     * Used during scrolling to retrieve the new offset within the window.
-     */
-    private final int[] mScrollOffset = new int[2];
-    private final int[] mScrollConsumed = new int[2];
-    private int mNestedYOffset;
-
-    private int mLastScrollerY;
-
-    /**
-     * Sentinel value for no current active pointer.
-     * Used by {@link #mActivePointerId}.
-     */
-    private static final int INVALID_POINTER = -1;
-
-    private SavedState mSavedState;
-
-    private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate();
-
-    private static final int[] SCROLLVIEW_STYLEABLE = new int[] {
-            android.R.attr.fillViewport
-    };
-
-    private final NestedScrollingParentHelper mParentHelper;
-    private final NestedScrollingChildHelper mChildHelper;
-
-    private float mVerticalScrollFactor;
-
-    private OnScrollChangeListener mOnScrollChangeListener;
-
-    public NestedScrollView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        initScrollView();
-
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
-
-        setFillViewport(a.getBoolean(0, false));
-
-        a.recycle();
-
-        mParentHelper = new NestedScrollingParentHelper(this);
-        mChildHelper = new NestedScrollingChildHelper(this);
-
-        // ...because why else would you be using this widget?
-        setNestedScrollingEnabled(true);
-
-        ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
-    }
-
-    // NestedScrollingChild
-
-    @Override
-    public void setNestedScrollingEnabled(boolean enabled) {
-        mChildHelper.setNestedScrollingEnabled(enabled);
-    }
-
-    @Override
-    public boolean isNestedScrollingEnabled() {
-        return mChildHelper.isNestedScrollingEnabled();
-    }
-
-    @Override
-    public boolean startNestedScroll(int axes) {
-        return mChildHelper.startNestedScroll(axes);
-    }
-
-    @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,
-                offsetInWindow);
-    }
-
-    @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);
-    }
-
-    @Override
-    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
-        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
-    }
-
-    // NestedScrollingParent
-
-    @Override
-    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
-        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
-    }
-
-    @Override
-    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
-        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
-        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
-    }
-
-    @Override
-    public void onStopNestedScroll(View target) {
-        mParentHelper.onStopNestedScroll(target);
-        stopNestedScroll();
-    }
-
-    @Override
-    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
-            int dyUnconsumed) {
-        final int oldScrollY = getScrollY();
-        scrollBy(0, dyUnconsumed);
-        final int myConsumed = getScrollY() - oldScrollY;
-        final int myUnconsumed = dyUnconsumed - myConsumed;
-        dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
-    }
-
-    @Override
-    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
-        dispatchNestedPreScroll(dx, dy, consumed, null);
-    }
-
-    @Override
-    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
-        if (!consumed) {
-            flingWithNestedDispatch((int) velocityY);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
-        return dispatchNestedPreFling(velocityX, velocityY);
-    }
-
-    @Override
-    public int getNestedScrollAxes() {
-        return mParentHelper.getNestedScrollAxes();
-    }
-
-    // ScrollView import
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return true;
-    }
-
-    @Override
-    protected float getTopFadingEdgeStrength() {
-        if (getChildCount() == 0) {
-            return 0.0f;
-        }
-
-        final int length = getVerticalFadingEdgeLength();
-        final int scrollY = getScrollY();
-        if (scrollY < length) {
-            return scrollY / (float) length;
-        }
-
-        return 1.0f;
-    }
-
-    @Override
-    protected float getBottomFadingEdgeStrength() {
-        if (getChildCount() == 0) {
-            return 0.0f;
-        }
-
-        final int length = getVerticalFadingEdgeLength();
-        final int bottomEdge = getHeight() - getPaddingBottom();
-        final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge;
-        if (span < length) {
-            return span / (float) length;
-        }
-
-        return 1.0f;
-    }
-
-    /**
-     * @return The maximum amount this scroll view will scroll in response to
-     *   an arrow event.
-     */
-    public int getMaxScrollAmount() {
-        return (int) (MAX_SCROLL_FACTOR * getHeight());
-    }
-
-    private void initScrollView() {
-        mScroller = new OverScroller(getContext());
-        setFocusable(true);
-        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
-        setWillNotDraw(false);
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        mTouchSlop = configuration.getScaledTouchSlop();
-        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-    }
-
-    @Override
-    public void addView(View child) {
-        if (getChildCount() > 0) {
-            throw new IllegalStateException("ScrollView can host only one direct child");
-        }
-
-        super.addView(child);
-    }
-
-    @Override
-    public void addView(View child, int index) {
-        if (getChildCount() > 0) {
-            throw new IllegalStateException("ScrollView can host only one direct child");
-        }
-
-        super.addView(child, index);
-    }
-
-    @Override
-    public void addView(View child, ViewGroup.LayoutParams params) {
-        if (getChildCount() > 0) {
-            throw new IllegalStateException("ScrollView can host only one direct child");
-        }
-
-        super.addView(child, params);
-    }
-
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        if (getChildCount() > 0) {
-            throw new IllegalStateException("ScrollView can host only one direct child");
-        }
-
-        super.addView(child, index, params);
-    }
-
-    /**
-     * Register a callback to be invoked when the scroll X or Y positions of
-     * this view change.
-     * <p>This version of the method works on all versions of Android, back to API v4.</p>
-     *
-     * @param l The listener to notify when the scroll X or Y position changes.
-     * @see android.view.View#getScrollX()
-     * @see android.view.View#getScrollY()
-     */
-    public void setOnScrollChangeListener(@Nullable OnScrollChangeListener l) {
-        mOnScrollChangeListener = l;
-    }
-
-    /**
-     * @return Returns true this ScrollView can be scrolled
-     */
-    private boolean canScroll() {
-        View child = getChildAt(0);
-        if (child != null) {
-            int childHeight = child.getHeight();
-            return getHeight() < childHeight + getPaddingTop() + getPaddingBottom();
-        }
-        return false;
-    }
-
-    /**
-     * Indicates whether this ScrollView's content is stretched to fill the viewport.
-     *
-     * @return True if the content fills the viewport, false otherwise.
-     *
-     * @attr name android:fillViewport
-     */
-    public boolean isFillViewport() {
-        return mFillViewport;
-    }
-
-    /**
-     * Set whether this ScrollView should stretch its content height to fill the viewport or not.
-     *
-     * @param fillViewport True to stretch the content's height to the viewport's
-     *        boundaries, false otherwise.
-     *
-     * @attr name android:fillViewport
-     */
-    public void setFillViewport(boolean fillViewport) {
-        if (fillViewport != mFillViewport) {
-            mFillViewport = fillViewport;
-            requestLayout();
-        }
-    }
-
-    /**
-     * @return Whether arrow scrolling will animate its transition.
-     */
-    public boolean isSmoothScrollingEnabled() {
-        return mSmoothScrollingEnabled;
-    }
-
-    /**
-     * Set whether arrow scrolling will animate its transition.
-     * @param smoothScrollingEnabled whether arrow scrolling will animate its transition
-     */
-    public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
-        mSmoothScrollingEnabled = smoothScrollingEnabled;
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-
-        if (mOnScrollChangeListener != null) {
-            mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        if (!mFillViewport) {
-            return;
-        }
-
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode == MeasureSpec.UNSPECIFIED) {
-            return;
-        }
-
-        if (getChildCount() > 0) {
-            final View child = getChildAt(0);
-            int height = getMeasuredHeight();
-            if (child.getMeasuredHeight() < height) {
-                final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
-                        getPaddingLeft() + getPaddingRight(), lp.width);
-                height -= getPaddingTop();
-                height -= getPaddingBottom();
-                int childHeightMeasureSpec =
-                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-
-                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-            }
-        }
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        // Let the focused view and/or our descendants get the key first
-        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
-    }
-
-    /**
-     * You can call this function yourself to have the scroll view perform
-     * scrolling from a key event, just as if the event had been dispatched to
-     * it by the view hierarchy.
-     *
-     * @param event The key event to execute.
-     * @return Return true if the event was handled, else false.
-     */
-    public boolean executeKeyEvent(@NonNull KeyEvent event) {
-        mTempRect.setEmpty();
-
-        if (!canScroll()) {
-            if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
-                View currentFocused = findFocus();
-                if (currentFocused == this) currentFocused = null;
-                View nextFocused = FocusFinder.getInstance().findNextFocus(this,
-                        currentFocused, View.FOCUS_DOWN);
-                return nextFocused != null
-                        && nextFocused != this
-                        && nextFocused.requestFocus(View.FOCUS_DOWN);
-            }
-            return false;
-        }
-
-        boolean handled = false;
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            switch (event.getKeyCode()) {
-                case KeyEvent.KEYCODE_DPAD_UP:
-                    if (!event.isAltPressed()) {
-                        handled = arrowScroll(View.FOCUS_UP);
-                    } else {
-                        handled = fullScroll(View.FOCUS_UP);
-                    }
-                    break;
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                    if (!event.isAltPressed()) {
-                        handled = arrowScroll(View.FOCUS_DOWN);
-                    } else {
-                        handled = fullScroll(View.FOCUS_DOWN);
-                    }
-                    break;
-                case KeyEvent.KEYCODE_SPACE:
-                    pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN);
-                    break;
-            }
-        }
-
-        return handled;
-    }
-
-    private boolean inChild(int x, int y) {
-        if (getChildCount() > 0) {
-            final int scrollY = getScrollY();
-            final View child = getChildAt(0);
-            return !(y < child.getTop() - scrollY
-                    || y >= child.getBottom() - scrollY
-                    || x < child.getLeft()
-                    || x >= child.getRight());
-        }
-        return false;
-    }
-
-    private void initOrResetVelocityTracker() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        } else {
-            mVelocityTracker.clear();
-        }
-    }
-
-    private void initVelocityTrackerIfNotExists() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-    }
-
-    private void recycleVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        if (disallowIntercept) {
-            recycleVelocityTracker();
-        }
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        /*
-         * This method JUST determines whether we want to intercept the motion.
-         * If we return true, onMotionEvent will be called and we do the actual
-         * scrolling there.
-         */
-
-        /*
-        * Shortcut the most recurring case: the user is in the dragging
-        * state and he is moving his finger.  We want to intercept this
-        * motion.
-        */
-        final int action = ev.getAction();
-        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
-            return true;
-        }
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_MOVE: {
-                /*
-                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
-                 * whether the user has moved far enough from his original down touch.
-                 */
-
-                /*
-                * Locally do absolute value. mLastMotionY is set to the y value
-                * of the down event.
-                */
-                final int activePointerId = mActivePointerId;
-                if (activePointerId == INVALID_POINTER) {
-                    // If we don't have a valid id, the touch down wasn't on content.
-                    break;
-                }
-
-                final int pointerIndex = ev.findPointerIndex(activePointerId);
-                if (pointerIndex == -1) {
-                    Log.e(TAG, "Invalid pointerId=" + activePointerId
-                            + " in onInterceptTouchEvent");
-                    break;
-                }
-
-                final int y = (int) ev.getY(pointerIndex);
-                final int yDiff = Math.abs(y - mLastMotionY);
-                if (yDiff > mTouchSlop
-                        && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
-                    mIsBeingDragged = true;
-                    mLastMotionY = y;
-                    initVelocityTrackerIfNotExists();
-                    mVelocityTracker.addMovement(ev);
-                    mNestedYOffset = 0;
-                    final ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_DOWN: {
-                final int y = (int) ev.getY();
-                if (!inChild((int) ev.getX(), y)) {
-                    mIsBeingDragged = false;
-                    recycleVelocityTracker();
-                    break;
-                }
-
-                /*
-                 * Remember location of down touch.
-                 * ACTION_DOWN always refers to pointer index 0.
-                 */
-                mLastMotionY = y;
-                mActivePointerId = ev.getPointerId(0);
-
-                initOrResetVelocityTracker();
-                mVelocityTracker.addMovement(ev);
-                /*
-                 * If being flinged and user touches the screen, initiate drag;
-                 * otherwise don't. mScroller.isFinished should be false when
-                 * being flinged. We need to call computeScrollOffset() first so that
-                 * isFinished() is correct.
-                */
-                mScroller.computeScrollOffset();
-                mIsBeingDragged = !mScroller.isFinished();
-                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                /* Release the drag */
-                mIsBeingDragged = false;
-                mActivePointerId = INVALID_POINTER;
-                recycleVelocityTracker();
-                if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
-                    ViewCompat.postInvalidateOnAnimation(this);
-                }
-                stopNestedScroll(ViewCompat.TYPE_TOUCH);
-                break;
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                break;
-        }
-
-        /*
-        * The only time we want to intercept motion events is if we are in the
-        * drag mode.
-        */
-        return mIsBeingDragged;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        initVelocityTrackerIfNotExists();
-
-        MotionEvent vtev = MotionEvent.obtain(ev);
-
-        final int actionMasked = ev.getActionMasked();
-
-        if (actionMasked == MotionEvent.ACTION_DOWN) {
-            mNestedYOffset = 0;
-        }
-        vtev.offsetLocation(0, mNestedYOffset);
-
-        switch (actionMasked) {
-            case MotionEvent.ACTION_DOWN: {
-                if (getChildCount() == 0) {
-                    return false;
-                }
-                if ((mIsBeingDragged = !mScroller.isFinished())) {
-                    final ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                }
-
-                /*
-                 * If being flinged and user touches, stop the fling. isFinished
-                 * will be false if being flinged.
-                 */
-                if (!mScroller.isFinished()) {
-                    mScroller.abortAnimation();
-                }
-
-                // Remember where the motion event started
-                mLastMotionY = (int) ev.getY();
-                mActivePointerId = ev.getPointerId(0);
-                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE:
-                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (activePointerIndex == -1) {
-                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
-                    break;
-                }
-
-                final int y = (int) ev.getY(activePointerIndex);
-                int deltaY = mLastMotionY - y;
-                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset,
-                        ViewCompat.TYPE_TOUCH)) {
-                    deltaY -= mScrollConsumed[1];
-                    vtev.offsetLocation(0, mScrollOffset[1]);
-                    mNestedYOffset += mScrollOffset[1];
-                }
-                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
-                    final ViewParent parent = getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                    mIsBeingDragged = true;
-                    if (deltaY > 0) {
-                        deltaY -= mTouchSlop;
-                    } else {
-                        deltaY += mTouchSlop;
-                    }
-                }
-                if (mIsBeingDragged) {
-                    // Scroll to follow the motion event
-                    mLastMotionY = y - mScrollOffset[1];
-
-                    final int oldY = getScrollY();
-                    final int range = getScrollRange();
-                    final int overscrollMode = getOverScrollMode();
-                    boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS
-                            || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
-
-                    // Calling overScrollByCompat will call onOverScrolled, which
-                    // calls onScrollChanged if applicable.
-                    if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0,
-                            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,
-                            ViewCompat.TYPE_TOUCH)) {
-                        mLastMotionY -= mScrollOffset[1];
-                        vtev.offsetLocation(0, mScrollOffset[1]);
-                        mNestedYOffset += mScrollOffset[1];
-                    } else if (canOverscroll) {
-                        ensureGlows();
-                        final int pulledToY = oldY + deltaY;
-                        if (pulledToY < 0) {
-                            EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(),
-                                    ev.getX(activePointerIndex) / getWidth());
-                            if (!mEdgeGlowBottom.isFinished()) {
-                                mEdgeGlowBottom.onRelease();
-                            }
-                        } else if (pulledToY > range) {
-                            EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(),
-                                    1.f - ev.getX(activePointerIndex)
-                                            / getWidth());
-                            if (!mEdgeGlowTop.isFinished()) {
-                                mEdgeGlowTop.onRelease();
-                            }
-                        }
-                        if (mEdgeGlowTop != null
-                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
-                            ViewCompat.postInvalidateOnAnimation(this);
-                        }
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                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();
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
-                            getScrollRange())) {
-                        ViewCompat.postInvalidateOnAnimation(this);
-                    }
-                }
-                mActivePointerId = INVALID_POINTER;
-                endDrag();
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                mLastMotionY = (int) ev.getY(index);
-                mActivePointerId = ev.getPointerId(index);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
-                break;
-        }
-
-        if (mVelocityTracker != null) {
-            mVelocityTracker.addMovement(vtev);
-        }
-        vtev.recycle();
-        return true;
-    }
-
-    private void onSecondaryPointerUp(MotionEvent 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
-            // active pointer and adjust accordingly.
-            // TODO: Make this decision more intelligent.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionY = (int) ev.getY(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_SCROLL: {
-                    if (!mIsBeingDragged) {
-                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                        if (vscroll != 0) {
-                            final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
-                            final int range = getScrollRange();
-                            int oldScrollY = getScrollY();
-                            int newScrollY = oldScrollY - delta;
-                            if (newScrollY < 0) {
-                                newScrollY = 0;
-                            } else if (newScrollY > range) {
-                                newScrollY = range;
-                            }
-                            if (newScrollY != oldScrollY) {
-                                super.scrollTo(getScrollX(), newScrollY);
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    private float getVerticalScrollFactorCompat() {
-        if (mVerticalScrollFactor == 0) {
-            TypedValue outValue = new TypedValue();
-            final Context context = getContext();
-            if (!context.getTheme().resolveAttribute(
-                    android.R.attr.listPreferredItemHeight, outValue, true)) {
-                throw new IllegalStateException(
-                        "Expected theme to define listPreferredItemHeight.");
-            }
-            mVerticalScrollFactor = outValue.getDimension(
-                    context.getResources().getDisplayMetrics());
-        }
-        return mVerticalScrollFactor;
-    }
-
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY,
-            boolean clampedX, boolean clampedY) {
-        super.scrollTo(scrollX, scrollY);
-    }
-
-    boolean overScrollByCompat(int deltaX, int deltaY,
-            int scrollX, int scrollY,
-            int scrollRangeX, int scrollRangeY,
-            int maxOverScrollX, int maxOverScrollY,
-            boolean isTouchEvent) {
-        final int overScrollMode = getOverScrollMode();
-        final boolean canScrollHorizontal =
-                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
-        final boolean canScrollVertical =
-                computeVerticalScrollRange() > computeVerticalScrollExtent();
-        final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
-                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
-        final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
-                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
-
-        int newScrollX = scrollX + deltaX;
-        if (!overScrollHorizontal) {
-            maxOverScrollX = 0;
-        }
-
-        int newScrollY = scrollY + deltaY;
-        if (!overScrollVertical) {
-            maxOverScrollY = 0;
-        }
-
-        // Clamp values if at the limits and record
-        final int left = -maxOverScrollX;
-        final int right = maxOverScrollX + scrollRangeX;
-        final int top = -maxOverScrollY;
-        final int bottom = maxOverScrollY + scrollRangeY;
-
-        boolean clampedX = false;
-        if (newScrollX > right) {
-            newScrollX = right;
-            clampedX = true;
-        } else if (newScrollX < left) {
-            newScrollX = left;
-            clampedX = true;
-        }
-
-        boolean clampedY = false;
-        if (newScrollY > bottom) {
-            newScrollY = bottom;
-            clampedY = true;
-        } else if (newScrollY < top) {
-            newScrollY = top;
-            clampedY = true;
-        }
-
-        if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
-            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
-        }
-
-        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
-
-        return clampedX || clampedY;
-    }
-
-    int getScrollRange() {
-        int scrollRange = 0;
-        if (getChildCount() > 0) {
-            View child = getChildAt(0);
-            scrollRange = Math.max(0,
-                    child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
-        }
-        return scrollRange;
-    }
-
-    /**
-     * <p>
-     * Finds the next focusable component that fits in the specified bounds.
-     * </p>
-     *
-     * @param topFocus look for a candidate is the one at the top of the bounds
-     *                 if topFocus is true, or at the bottom of the bounds if topFocus is
-     *                 false
-     * @param top      the top offset of the bounds in which a focusable must be
-     *                 found
-     * @param bottom   the bottom offset of the bounds in which a focusable must
-     *                 be found
-     * @return the next focusable component in the bounds or null if none can
-     *         be found
-     */
-    private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
-
-        List<View> focusables = getFocusables(View.FOCUS_FORWARD);
-        View focusCandidate = null;
-
-        /*
-         * A fully contained focusable is one where its top is below the bound's
-         * top, and its bottom is above the bound's bottom. A partially
-         * contained focusable is one where some part of it is within the
-         * bounds, but it also has some part that is not within bounds.  A fully contained
-         * focusable is preferred to a partially contained focusable.
-         */
-        boolean foundFullyContainedFocusable = false;
-
-        int count = focusables.size();
-        for (int i = 0; i < count; i++) {
-            View view = focusables.get(i);
-            int viewTop = view.getTop();
-            int viewBottom = view.getBottom();
-
-            if (top < viewBottom && viewTop < bottom) {
-                /*
-                 * the focusable is in the target area, it is a candidate for
-                 * focusing
-                 */
-
-                final boolean viewIsFullyContained = (top < viewTop) && (viewBottom < bottom);
-
-                if (focusCandidate == null) {
-                    /* No candidate, take this one */
-                    focusCandidate = view;
-                    foundFullyContainedFocusable = viewIsFullyContained;
-                } else {
-                    final boolean viewIsCloserToBoundary =
-                            (topFocus && viewTop < focusCandidate.getTop())
-                                    || (!topFocus && viewBottom > focusCandidate.getBottom());
-
-                    if (foundFullyContainedFocusable) {
-                        if (viewIsFullyContained && viewIsCloserToBoundary) {
-                            /*
-                             * We're dealing with only fully contained views, so
-                             * it has to be closer to the boundary to beat our
-                             * candidate
-                             */
-                            focusCandidate = view;
-                        }
-                    } else {
-                        if (viewIsFullyContained) {
-                            /* Any fully contained view beats a partially contained view */
-                            focusCandidate = view;
-                            foundFullyContainedFocusable = true;
-                        } else if (viewIsCloserToBoundary) {
-                            /*
-                             * Partially contained view beats another partially
-                             * contained view if it's closer
-                             */
-                            focusCandidate = view;
-                        }
-                    }
-                }
-            }
-        }
-
-        return focusCandidate;
-    }
-
-    /**
-     * <p>Handles scrolling in response to a "page up/down" shortcut press. This
-     * method will scroll the view by one page up or down and give the focus
-     * to the topmost/bottommost component in the new visible area. If no
-     * component is a good candidate for focus, this scrollview reclaims the
-     * focus.</p>
-     *
-     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
-     *                  to go one page up or
-     *                  {@link android.view.View#FOCUS_DOWN} to go one page down
-     * @return true if the key event is consumed by this method, false otherwise
-     */
-    public boolean pageScroll(int direction) {
-        boolean down = direction == View.FOCUS_DOWN;
-        int height = getHeight();
-
-        if (down) {
-            mTempRect.top = getScrollY() + height;
-            int count = getChildCount();
-            if (count > 0) {
-                View view = getChildAt(count - 1);
-                if (mTempRect.top + height > view.getBottom()) {
-                    mTempRect.top = view.getBottom() - height;
-                }
-            }
-        } else {
-            mTempRect.top = getScrollY() - height;
-            if (mTempRect.top < 0) {
-                mTempRect.top = 0;
-            }
-        }
-        mTempRect.bottom = mTempRect.top + height;
-
-        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
-    }
-
-    /**
-     * <p>Handles scrolling in response to a "home/end" shortcut press. This
-     * method will scroll the view to the top or bottom and give the focus
-     * to the topmost/bottommost component in the new visible area. If no
-     * component is a good candidate for focus, this scrollview reclaims the
-     * focus.</p>
-     *
-     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
-     *                  to go the top of the view or
-     *                  {@link android.view.View#FOCUS_DOWN} to go the bottom
-     * @return true if the key event is consumed by this method, false otherwise
-     */
-    public boolean fullScroll(int direction) {
-        boolean down = direction == View.FOCUS_DOWN;
-        int height = getHeight();
-
-        mTempRect.top = 0;
-        mTempRect.bottom = height;
-
-        if (down) {
-            int count = getChildCount();
-            if (count > 0) {
-                View view = getChildAt(count - 1);
-                mTempRect.bottom = view.getBottom() + getPaddingBottom();
-                mTempRect.top = mTempRect.bottom - height;
-            }
-        }
-
-        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
-    }
-
-    /**
-     * <p>Scrolls the view to make the area defined by <code>top</code> and
-     * <code>bottom</code> visible. This method attempts to give the focus
-     * to a component visible in this area. If no component can be focused in
-     * the new visible area, the focus is reclaimed by this ScrollView.</p>
-     *
-     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
-     *                  to go upward, {@link android.view.View#FOCUS_DOWN} to downward
-     * @param top       the top offset of the new area to be made visible
-     * @param bottom    the bottom offset of the new area to be made visible
-     * @return true if the key event is consumed by this method, false otherwise
-     */
-    private boolean scrollAndFocus(int direction, int top, int bottom) {
-        boolean handled = true;
-
-        int height = getHeight();
-        int containerTop = getScrollY();
-        int containerBottom = containerTop + height;
-        boolean up = direction == View.FOCUS_UP;
-
-        View newFocused = findFocusableViewInBounds(up, top, bottom);
-        if (newFocused == null) {
-            newFocused = this;
-        }
-
-        if (top >= containerTop && bottom <= containerBottom) {
-            handled = false;
-        } else {
-            int delta = up ? (top - containerTop) : (bottom - containerBottom);
-            doScrollY(delta);
-        }
-
-        if (newFocused != findFocus()) newFocused.requestFocus(direction);
-
-        return handled;
-    }
-
-    /**
-     * Handle scrolling in response to an up or down arrow click.
-     *
-     * @param direction The direction corresponding to the arrow key that was
-     *                  pressed
-     * @return True if we consumed the event, false otherwise
-     */
-    public boolean arrowScroll(int direction) {
-
-        View currentFocused = findFocus();
-        if (currentFocused == this) currentFocused = null;
-
-        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
-
-        final int maxJump = getMaxScrollAmount();
-
-        if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) {
-            nextFocused.getDrawingRect(mTempRect);
-            offsetDescendantRectToMyCoords(nextFocused, mTempRect);
-            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
-            doScrollY(scrollDelta);
-            nextFocused.requestFocus(direction);
-        } else {
-            // no new focus
-            int scrollDelta = maxJump;
-
-            if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) {
-                scrollDelta = getScrollY();
-            } else if (direction == View.FOCUS_DOWN) {
-                if (getChildCount() > 0) {
-                    int daBottom = getChildAt(0).getBottom();
-                    int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
-                    if (daBottom - screenBottom < maxJump) {
-                        scrollDelta = daBottom - screenBottom;
-                    }
-                }
-            }
-            if (scrollDelta == 0) {
-                return false;
-            }
-            doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
-        }
-
-        if (currentFocused != null && currentFocused.isFocused()
-                && isOffScreen(currentFocused)) {
-            // previously focused item still has focus and is off screen, give
-            // it up (take it back to ourselves)
-            // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are
-            // sure to
-            // get it)
-            final int descendantFocusability = getDescendantFocusability();  // save
-            setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-            requestFocus();
-            setDescendantFocusability(descendantFocusability);  // restore
-        }
-        return true;
-    }
-
-    /**
-     * @return whether the descendant of this scroll view is scrolled off
-     *  screen.
-     */
-    private boolean isOffScreen(View descendant) {
-        return !isWithinDeltaOfScreen(descendant, 0, getHeight());
-    }
-
-    /**
-     * @return whether the descendant of this scroll view is within delta
-     *  pixels of being on the screen.
-     */
-    private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
-        descendant.getDrawingRect(mTempRect);
-        offsetDescendantRectToMyCoords(descendant, mTempRect);
-
-        return (mTempRect.bottom + delta) >= getScrollY()
-                && (mTempRect.top - delta) <= (getScrollY() + height);
-    }
-
-    /**
-     * Smooth scroll by a Y delta
-     *
-     * @param delta the number of pixels to scroll by on the Y axis
-     */
-    private void doScrollY(int delta) {
-        if (delta != 0) {
-            if (mSmoothScrollingEnabled) {
-                smoothScrollBy(0, delta);
-            } else {
-                scrollBy(0, delta);
-            }
-        }
-    }
-
-    /**
-     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
-     *
-     * @param dx the number of pixels to scroll by on the X axis
-     * @param dy the number of pixels to scroll by on the Y axis
-     */
-    public final void smoothScrollBy(int dx, int dy) {
-        if (getChildCount() == 0) {
-            // Nothing to do.
-            return;
-        }
-        long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
-        if (duration > ANIMATED_SCROLL_GAP) {
-            final int height = getHeight() - getPaddingBottom() - getPaddingTop();
-            final int bottom = getChildAt(0).getHeight();
-            final int maxY = Math.max(0, bottom - height);
-            final int scrollY = getScrollY();
-            dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
-
-            mScroller.startScroll(getScrollX(), scrollY, 0, dy);
-            ViewCompat.postInvalidateOnAnimation(this);
-        } else {
-            if (!mScroller.isFinished()) {
-                mScroller.abortAnimation();
-            }
-            scrollBy(dx, dy);
-        }
-        mLastScroll = AnimationUtils.currentAnimationTimeMillis();
-    }
-
-    /**
-     * Like {@link #scrollTo}, but scroll smoothly instead of immediately.
-     *
-     * @param x the position where to scroll on the X axis
-     * @param y the position where to scroll on the Y axis
-     */
-    public final void smoothScrollTo(int x, int y) {
-        smoothScrollBy(x - getScrollX(), y - getScrollY());
-    }
-
-    /**
-     * <p>The scroll range of a scroll view is the overall height of all of its
-     * children.</p>
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int computeVerticalScrollRange() {
-        final int count = getChildCount();
-        final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop();
-        if (count == 0) {
-            return contentHeight;
-        }
-
-        int scrollRange = getChildAt(0).getBottom();
-        final int scrollY = getScrollY();
-        final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
-        if (scrollY < 0) {
-            scrollRange -= scrollY;
-        } else if (scrollY > overscrollBottom) {
-            scrollRange += scrollY - overscrollBottom;
-        }
-
-        return scrollRange;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int computeVerticalScrollOffset() {
-        return Math.max(0, super.computeVerticalScrollOffset());
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int computeVerticalScrollExtent() {
-        return super.computeVerticalScrollExtent();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int computeHorizontalScrollRange() {
-        return super.computeHorizontalScrollRange();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int computeHorizontalScrollOffset() {
-        return super.computeHorizontalScrollOffset();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int computeHorizontalScrollExtent() {
-        return super.computeHorizontalScrollExtent();
-    }
-
-    @Override
-    protected void measureChild(View child, int parentWidthMeasureSpec,
-            int parentHeightMeasureSpec) {
-        ViewGroup.LayoutParams lp = child.getLayoutParams();
-
-        int childWidthMeasureSpec;
-        int childHeightMeasureSpec;
-
-        childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
-                + getPaddingRight(), lp.width);
-
-        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-    }
-
-    @Override
-    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
-            int parentHeightMeasureSpec, int heightUsed) {
-        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-
-        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
-                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
-                        + widthUsed, lp.width);
-        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
-
-        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mScroller.computeScrollOffset()) {
-            final int x = mScroller.getCurrX();
-            final int y = mScroller.getCurrY();
-
-            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 oldScrollY = getScrollY();
-
-                overScrollByCompat(0, dy, getScrollX(), oldScrollY, 0, range, 0, 0, false);
-
-                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;
-        }
-    }
-
-    /**
-     * Scrolls the view to the given child.
-     *
-     * @param child the View to scroll to
-     */
-    private void scrollToChild(View child) {
-        child.getDrawingRect(mTempRect);
-
-        /* Offset from child's local coordinates to ScrollView coordinates */
-        offsetDescendantRectToMyCoords(child, mTempRect);
-
-        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
-
-        if (scrollDelta != 0) {
-            scrollBy(0, scrollDelta);
-        }
-    }
-
-    /**
-     * If rect is off screen, scroll just enough to get it (or at least the
-     * first screen size chunk of it) on screen.
-     *
-     * @param rect      The rectangle.
-     * @param immediate True to scroll immediately without animation
-     * @return true if scrolling was performed
-     */
-    private boolean scrollToChildRect(Rect rect, boolean immediate) {
-        final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
-        final boolean scroll = delta != 0;
-        if (scroll) {
-            if (immediate) {
-                scrollBy(0, delta);
-            } else {
-                smoothScrollBy(0, delta);
-            }
-        }
-        return scroll;
-    }
-
-    /**
-     * Compute the amount to scroll in the Y direction in order to get
-     * a rectangle completely on the screen (or, if taller than the screen,
-     * at least the first screen size chunk of it).
-     *
-     * @param rect The rect.
-     * @return The scroll delta.
-     */
-    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
-        if (getChildCount() == 0) return 0;
-
-        int height = getHeight();
-        int screenTop = getScrollY();
-        int screenBottom = screenTop + height;
-
-        int fadingEdge = getVerticalFadingEdgeLength();
-
-        // leave room for top fading edge as long as rect isn't at very top
-        if (rect.top > 0) {
-            screenTop += fadingEdge;
-        }
-
-        // leave room for bottom fading edge as long as rect isn't at very bottom
-        if (rect.bottom < getChildAt(0).getHeight()) {
-            screenBottom -= fadingEdge;
-        }
-
-        int scrollYDelta = 0;
-
-        if (rect.bottom > screenBottom && rect.top > screenTop) {
-            // need to move down to get it in view: move down just enough so
-            // that the entire rectangle is in view (or at least the first
-            // screen size chunk).
-
-            if (rect.height() > height) {
-                // just enough to get screen size chunk on
-                scrollYDelta += (rect.top - screenTop);
-            } else {
-                // get entire rect at bottom of screen
-                scrollYDelta += (rect.bottom - screenBottom);
-            }
-
-            // make sure we aren't scrolling beyond the end of our content
-            int bottom = getChildAt(0).getBottom();
-            int distanceToBottom = bottom - screenBottom;
-            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
-
-        } else if (rect.top < screenTop && rect.bottom < screenBottom) {
-            // need to move up to get it in view: move up just enough so that
-            // entire rectangle is in view (or at least the first screen
-            // size chunk of it).
-
-            if (rect.height() > height) {
-                // screen size chunk
-                scrollYDelta -= (screenBottom - rect.bottom);
-            } else {
-                // entire rect at top
-                scrollYDelta -= (screenTop - rect.top);
-            }
-
-            // make sure we aren't scrolling any further than the top our content
-            scrollYDelta = Math.max(scrollYDelta, -getScrollY());
-        }
-        return scrollYDelta;
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        if (!mIsLayoutDirty) {
-            scrollToChild(focused);
-        } else {
-            // The child may not be laid out yet, we can't compute the scroll yet
-            mChildToScrollTo = focused;
-        }
-        super.requestChildFocus(child, focused);
-    }
-
-
-    /**
-     * When looking for focus in children of a scroll view, need to be a little
-     * more careful not to give focus to something that is scrolled off screen.
-     *
-     * This is more expensive than the default {@link android.view.ViewGroup}
-     * implementation, otherwise this behavior might have been made the default.
-     */
-    @Override
-    protected boolean onRequestFocusInDescendants(int direction,
-            Rect previouslyFocusedRect) {
-
-        // convert from forward / backward notation to up / down / left / right
-        // (ugh).
-        if (direction == View.FOCUS_FORWARD) {
-            direction = View.FOCUS_DOWN;
-        } else if (direction == View.FOCUS_BACKWARD) {
-            direction = View.FOCUS_UP;
-        }
-
-        final View nextFocus = previouslyFocusedRect == null
-                ? FocusFinder.getInstance().findNextFocus(this, null, direction)
-                : FocusFinder.getInstance().findNextFocusFromRect(
-                        this, previouslyFocusedRect, direction);
-
-        if (nextFocus == null) {
-            return false;
-        }
-
-        if (isOffScreen(nextFocus)) {
-            return false;
-        }
-
-        return nextFocus.requestFocus(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
-            boolean immediate) {
-        // offset into coordinate space of this scroll view
-        rectangle.offset(child.getLeft() - child.getScrollX(),
-                child.getTop() - child.getScrollY());
-
-        return scrollToChildRect(rectangle, immediate);
-    }
-
-    @Override
-    public void requestLayout() {
-        mIsLayoutDirty = true;
-        super.requestLayout();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        mIsLayoutDirty = false;
-        // Give a child focus if it needs it
-        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
-            scrollToChild(mChildToScrollTo);
-        }
-        mChildToScrollTo = null;
-
-        if (!mIsLaidOut) {
-            if (mSavedState != null) {
-                scrollTo(getScrollX(), mSavedState.scrollPosition);
-                mSavedState = null;
-            } // mScrollY default value is "0"
-
-            final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0;
-            final int scrollRange = Math.max(0,
-                    childHeight - (b - t - getPaddingBottom() - getPaddingTop()));
-
-            // Don't forget to clamp
-            if (getScrollY() > scrollRange) {
-                scrollTo(getScrollX(), scrollRange);
-            } else if (getScrollY() < 0) {
-                scrollTo(getScrollX(), 0);
-            }
-        }
-
-        // Calling this with the present values causes it to re-claim them
-        scrollTo(getScrollX(), getScrollY());
-        mIsLaidOut = true;
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mIsLaidOut = false;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-
-        View currentFocused = findFocus();
-        if (null == currentFocused || this == currentFocused) {
-            return;
-        }
-
-        // If the currently-focused view was visible on the screen when the
-        // screen was at the old height, then scroll the screen to make that
-        // view visible with the new screen height.
-        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
-            currentFocused.getDrawingRect(mTempRect);
-            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
-            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
-            doScrollY(scrollDelta);
-        }
-    }
-
-    /**
-     * Return true if child is a descendant of parent, (or equal to the parent).
-     */
-    private static boolean isViewDescendantOf(View child, View parent) {
-        if (child == parent) {
-            return true;
-        }
-
-        final ViewParent theParent = child.getParent();
-        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
-    }
-
-    /**
-     * Fling the scroll view
-     *
-     * @param velocityY The initial velocity in the Y direction. Positive
-     *                  numbers mean that the finger/cursor is moving down the screen,
-     *                  which means we want to scroll towards the top.
-     */
-    public void fling(int velocityY) {
-        if (getChildCount() > 0) {
-            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);
-        }
-    }
-
-    private void flingWithNestedDispatch(int velocityY) {
-        final int scrollY = getScrollY();
-        final boolean canFling = (scrollY > 0 || velocityY > 0)
-                && (scrollY < getScrollRange() || velocityY < 0);
-        if (!dispatchNestedPreFling(0, velocityY)) {
-            dispatchNestedFling(0, velocityY, canFling);
-            fling(velocityY);
-        }
-    }
-
-    private void endDrag() {
-        mIsBeingDragged = false;
-
-        recycleVelocityTracker();
-        stopNestedScroll(ViewCompat.TYPE_TOUCH);
-
-        if (mEdgeGlowTop != null) {
-            mEdgeGlowTop.onRelease();
-            mEdgeGlowBottom.onRelease();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This version also clamps the scrolling to the bounds of our child.
-     */
-    @Override
-    public void scrollTo(int x, int y) {
-        // we rely on the fact the View.scrollBy calls scrollTo.
-        if (getChildCount() > 0) {
-            View child = getChildAt(0);
-            x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth());
-            y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight());
-            if (x != getScrollX() || y != getScrollY()) {
-                super.scrollTo(x, y);
-            }
-        }
-    }
-
-    private void ensureGlows() {
-        if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
-            if (mEdgeGlowTop == null) {
-                Context context = getContext();
-                mEdgeGlowTop = new EdgeEffect(context);
-                mEdgeGlowBottom = new EdgeEffect(context);
-            }
-        } else {
-            mEdgeGlowTop = null;
-            mEdgeGlowBottom = null;
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mEdgeGlowTop != null) {
-            final int scrollY = getScrollY();
-            if (!mEdgeGlowTop.isFinished()) {
-                final int restoreCount = canvas.save();
-                int width = getWidth();
-                int height = getHeight();
-                int xTranslation = 0;
-                int yTranslation = Math.min(0, scrollY);
-                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) {
-                    width -= getPaddingLeft() + getPaddingRight();
-                    xTranslation += getPaddingLeft();
-                }
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) {
-                    height -= getPaddingTop() + getPaddingBottom();
-                    yTranslation += getPaddingTop();
-                }
-                canvas.translate(xTranslation, yTranslation);
-                mEdgeGlowTop.setSize(width, height);
-                if (mEdgeGlowTop.draw(canvas)) {
-                    ViewCompat.postInvalidateOnAnimation(this);
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mEdgeGlowBottom.isFinished()) {
-                final int restoreCount = canvas.save();
-                int width = getWidth();
-                int height = getHeight();
-                int xTranslation = 0;
-                int yTranslation = Math.max(getScrollRange(), scrollY) + height;
-                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) {
-                    width -= getPaddingLeft() + getPaddingRight();
-                    xTranslation += getPaddingLeft();
-                }
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) {
-                    height -= getPaddingTop() + getPaddingBottom();
-                    yTranslation -= getPaddingBottom();
-                }
-                canvas.translate(xTranslation - width, yTranslation);
-                canvas.rotate(180, width, 0);
-                mEdgeGlowBottom.setSize(width, height);
-                if (mEdgeGlowBottom.draw(canvas)) {
-                    ViewCompat.postInvalidateOnAnimation(this);
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-        }
-    }
-
-    private static int clamp(int n, int my, int child) {
-        if (my >= child || n < 0) {
-            /* my >= child is this case:
-             *                    |--------------- me ---------------|
-             *     |------ child ------|
-             * or
-             *     |--------------- me ---------------|
-             *            |------ child ------|
-             * or
-             *     |--------------- me ---------------|
-             *                                  |------ child ------|
-             *
-             * n < 0 is this case:
-             *     |------ me ------|
-             *                    |-------- child --------|
-             *     |-- mScrollX --|
-             */
-            return 0;
-        }
-        if ((my + n) > child) {
-            /* this case:
-             *                    |------ me ------|
-             *     |------ child ------|
-             *     |-- mScrollX --|
-             */
-            return child - my;
-        }
-        return n;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-        mSavedState = ss;
-        requestLayout();
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        SavedState ss = new SavedState(superState);
-        ss.scrollPosition = getScrollY();
-        return ss;
-    }
-
-    static class SavedState extends BaseSavedState {
-        public int scrollPosition;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        SavedState(Parcel source) {
-            super(source);
-            scrollPosition = source.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(scrollPosition);
-        }
-
-        @Override
-        public String toString() {
-            return "HorizontalScrollView.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " scrollPosition=" + scrollPosition + "}";
-        }
-
-        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];
-            }
-        };
-    }
-
-    static class AccessibilityDelegate extends AccessibilityDelegateCompat {
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
-            if (super.performAccessibilityAction(host, action, arguments)) {
-                return true;
-            }
-            final NestedScrollView nsvHost = (NestedScrollView) host;
-            if (!nsvHost.isEnabled()) {
-                return false;
-            }
-            switch (action) {
-                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
-                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
-                            - nsvHost.getPaddingTop();
-                    final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
-                            nsvHost.getScrollRange());
-                    if (targetScrollY != nsvHost.getScrollY()) {
-                        nsvHost.smoothScrollTo(0, targetScrollY);
-                        return true;
-                    }
-                }
-                return false;
-                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
-                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
-                            - nsvHost.getPaddingTop();
-                    final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
-                    if (targetScrollY != nsvHost.getScrollY()) {
-                        nsvHost.smoothScrollTo(0, targetScrollY);
-                        return true;
-                    }
-                }
-                return false;
-            }
-            return false;
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-            super.onInitializeAccessibilityNodeInfo(host, info);
-            final NestedScrollView nsvHost = (NestedScrollView) host;
-            info.setClassName(ScrollView.class.getName());
-            if (nsvHost.isEnabled()) {
-                final int scrollRange = nsvHost.getScrollRange();
-                if (scrollRange > 0) {
-                    info.setScrollable(true);
-                    if (nsvHost.getScrollY() > 0) {
-                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
-                    }
-                    if (nsvHost.getScrollY() < scrollRange) {
-                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-            final NestedScrollView nsvHost = (NestedScrollView) host;
-            event.setClassName(ScrollView.class.getName());
-            final boolean scrollable = nsvHost.getScrollRange() > 0;
-            event.setScrollable(scrollable);
-            event.setScrollX(nsvHost.getScrollX());
-            event.setScrollY(nsvHost.getScrollY());
-            AccessibilityRecordCompat.setMaxScrollX(event, nsvHost.getScrollX());
-            AccessibilityRecordCompat.setMaxScrollY(event, nsvHost.getScrollRange());
-        }
-    }
-}
diff --git a/android/support/v4/widget/PopupMenuCompat.java b/android/support/v4/widget/PopupMenuCompat.java
deleted file mode 100644
index 10c5ff3..0000000
--- a/android/support/v4/widget/PopupMenuCompat.java
+++ /dev/null
@@ -1,60 +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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View.OnTouchListener;
-import android.widget.PopupMenu;
-
-/**
- * Helper for accessing features in {@link PopupMenu}.
- */
-public final class PopupMenuCompat {
-    private PopupMenuCompat() {
-        // This class is not publicly instantiable.
-    }
-
-    /**
-     * On API {@link android.os.Build.VERSION_CODES#KITKAT} and higher, returns
-     * an {@link OnTouchListener} that can be added to the anchor view to
-     * implement drag-to-open behavior.
-     * <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>
-     * PopupMenu myPopup = new PopupMenu(context, myAnchor);
-     * myAnchor.setOnTouchListener(PopupMenuCompat.getDragToOpenListener(myPopup));
-     * </pre>
-     *
-     * @param popupMenu the PopupMenu against which to invoke the method
-     * @return a touch listener that controls drag-to-open behavior, or {@code null} on
-     *         unsupported APIs
-     */
-    @Nullable
-    public static OnTouchListener getDragToOpenListener(@NonNull Object popupMenu) {
-        if (Build.VERSION.SDK_INT >= 19) {
-            return ((PopupMenu) popupMenu).getDragToOpenListener();
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/android/support/v4/widget/PopupWindowCompat.java b/android/support/v4/widget/PopupWindowCompat.java
deleted file mode 100644
index d9de3db..0000000
--- a/android/support/v4/widget/PopupWindowCompat.java
+++ /dev/null
@@ -1,263 +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.os.Build;
-import android.support.annotation.NonNull;
-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.widget.PopupWindow;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-/**
- * Helper for accessing features in {@link PopupWindow}.
- */
-public final class PopupWindowCompat {
-
-    static class PopupWindowCompatBaseImpl {
-        private static Method sSetWindowLayoutTypeMethod;
-        private static boolean sSetWindowLayoutTypeMethodAttempted;
-        private static Method sGetWindowLayoutTypeMethod;
-        private static boolean sGetWindowLayoutTypeMethodAttempted;
-
-        public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
-                int gravity) {
-            final int hgrav = GravityCompat.getAbsoluteGravity(gravity,
-                    ViewCompat.getLayoutDirection(anchor)) & Gravity.HORIZONTAL_GRAVITY_MASK;
-            if (hgrav == Gravity.RIGHT) {
-                // Flip the location to align the right sides of the popup and
-                // anchor instead of left.
-                xoff -= (popup.getWidth() - anchor.getWidth());
-            }
-            popup.showAsDropDown(anchor, xoff, yoff);
-        }
-
-        public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-            // noop
-        }
-
-        public boolean getOverlapAnchor(PopupWindow popupWindow) {
-            return false;
-        }
-
-        public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
-            if (!sSetWindowLayoutTypeMethodAttempted) {
-                try {
-                    sSetWindowLayoutTypeMethod = PopupWindow.class.getDeclaredMethod(
-                            "setWindowLayoutType", int.class);
-                    sSetWindowLayoutTypeMethod.setAccessible(true);
-                } catch (Exception e) {
-                    // Reflection method fetch failed. Oh well.
-                }
-                sSetWindowLayoutTypeMethodAttempted = true;
-            }
-
-            if (sSetWindowLayoutTypeMethod != null) {
-                try {
-                    sSetWindowLayoutTypeMethod.invoke(popupWindow, layoutType);
-                } catch (Exception e) {
-                    // Reflection call failed. Oh well.
-                }
-            }
-        }
-
-        public int getWindowLayoutType(PopupWindow popupWindow) {
-            if (!sGetWindowLayoutTypeMethodAttempted) {
-                try {
-                    sGetWindowLayoutTypeMethod = PopupWindow.class.getDeclaredMethod(
-                            "getWindowLayoutType");
-                    sGetWindowLayoutTypeMethod.setAccessible(true);
-                } catch (Exception e) {
-                    // Reflection method fetch failed. Oh well.
-                }
-                sGetWindowLayoutTypeMethodAttempted = true;
-            }
-
-            if (sGetWindowLayoutTypeMethod != null) {
-                try {
-                    return (Integer) sGetWindowLayoutTypeMethod.invoke(popupWindow);
-                } catch (Exception e) {
-                    // Reflection call failed. Oh well.
-                }
-            }
-            return 0;
-        }
-    }
-
-    /**
-     * Interface implementation for devices with at least KitKat APIs.
-     */
-    @RequiresApi(19)
-    static class PopupWindowCompatApi19Impl extends PopupWindowCompatBaseImpl {
-        @Override
-        public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
-                int gravity) {
-            popup.showAsDropDown(anchor, xoff, yoff, gravity);
-        }
-    }
-
-    @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) {
-            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) {
-            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;
-        }
-    }
-
-    @RequiresApi(23)
-    static class PopupWindowCompatApi23Impl extends PopupWindowCompatApi21Impl {
-        @Override
-        public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-            popupWindow.setOverlapAnchor(overlapAnchor);
-        }
-
-        @Override
-        public boolean getOverlapAnchor(PopupWindow popupWindow) {
-            return popupWindow.getOverlapAnchor();
-        }
-
-        @Override
-        public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
-            popupWindow.setWindowLayoutType(layoutType);
-        }
-
-        @Override
-        public int getWindowLayoutType(PopupWindow popupWindow) {
-            return popupWindow.getWindowLayoutType();
-        }
-    }
-
-    /**
-     * Select the correct implementation to use for the current platform.
-     */
-    static final PopupWindowCompatBaseImpl IMPL;
-    static {
-        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 PopupWindowCompatBaseImpl();
-        }
-    }
-
-    private PopupWindowCompat() {
-        // This class is not publicly instantiable.
-    }
-
-    /**
-     * <p>Display the content view in a popup window anchored to the bottom-left
-     * corner of the anchor view offset by the specified x and y coordinates.
-     * If there is not enough room on screen to show
-     * the popup in its entirety, this method tries to find a parent scroll
-     * view to scroll. If no parent scroll view can be scrolled, the bottom-left
-     * corner of the popup is pinned at the top left corner of the anchor view.</p>
-     * <p>If the view later scrolls to move <code>anchor</code> to a different
-     * location, the popup will be moved correspondingly.</p>
-     *
-     * @param popup the PopupWindow to show
-     * @param anchor the view on which to pin the popup window
-     * @param xoff A horizontal offset from the anchor in pixels
-     * @param yoff A vertical offset from the anchor in pixels
-     * @param gravity Alignment of the popup relative to the anchor
-     */
-    public static void showAsDropDown(@NonNull PopupWindow popup, @NonNull View anchor,
-            int xoff, int yoff, int gravity) {
-        IMPL.showAsDropDown(popup, anchor, xoff, yoff, gravity);
-    }
-
-    /**
-     * Sets whether the popup window should overlap its anchor view when
-     * displayed as a drop-down.
-     *
-     * @param overlapAnchor Whether the popup should overlap its anchor.
-     */
-    public static void setOverlapAnchor(@NonNull PopupWindow popupWindow, boolean overlapAnchor) {
-        IMPL.setOverlapAnchor(popupWindow, overlapAnchor);
-    }
-
-    /**
-     * Returns whether the popup window should overlap its anchor view when
-     * displayed as a drop-down.
-     *
-     * @return Whether the popup should overlap its anchor.
-     */
-    public static boolean getOverlapAnchor(@NonNull PopupWindow popupWindow) {
-        return IMPL.getOverlapAnchor(popupWindow);
-    }
-
-    /**
-     * Set the layout type for this window. This value will be passed through to
-     * {@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 android.view.WindowManager.LayoutParams#type
-     */
-    public static void setWindowLayoutType(@NonNull PopupWindow popupWindow, int layoutType) {
-        IMPL.setWindowLayoutType(popupWindow, layoutType);
-    }
-
-    /**
-     * Returns the layout type for this window.
-     *
-     * @see #setWindowLayoutType(PopupWindow popupWindow, int)
-     */
-    public static int getWindowLayoutType(@NonNull PopupWindow popupWindow) {
-        return IMPL.getWindowLayoutType(popupWindow);
-    }
-}
diff --git a/android/support/v4/widget/ResourceCursorAdapter.java b/android/support/v4/widget/ResourceCursorAdapter.java
deleted file mode 100644
index 8a0136a..0000000
--- a/android/support/v4/widget/ResourceCursorAdapter.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.content.Context;
-import android.database.Cursor;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.LayoutInflater;
-
-/**
- * Static library support version of the framework's {@link android.widget.ResourceCursorAdapter}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public abstract class ResourceCursorAdapter extends CursorAdapter {
-    private int mLayout;
-
-    private int mDropDownLayout;
-
-    private LayoutInflater mInflater;
-
-    /**
-     * Constructor the enables auto-requery.
-     *
-     * @deprecated This option is discouraged, as it results in Cursor queries
-     * being performed on the application's UI thread and thus can cause poor
-     * responsiveness or even Application Not Responding errors.  As an alternative,
-     * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
-     *
-     * @param context The context where the ListView associated with this adapter is running
-     * @param layout resource identifier of a layout file that defines the views
-     *            for this list item.  Unless you override them later, this will
-     *            define both the item views and the drop down views.
-     */
-    @Deprecated
-    public ResourceCursorAdapter(Context context, int layout, Cursor c) {
-        super(context, c);
-        mLayout = mDropDownLayout = layout;
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-    }
-
-    /**
-     * Constructor with default behavior as per
-     * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
-     * you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
-     * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
-     * will always be set.
-     *
-     * @deprecated This option is discouraged, as it results in Cursor queries
-     * being performed on the application's UI thread and thus can cause poor
-     * responsiveness or even Application Not Responding errors.  As an alternative,
-     * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
-     *
-     * @param context The context where the ListView associated with this adapter is running
-     * @param layout resource identifier of a layout file that defines the views
-     *            for this list item.  Unless you override them later, this will
-     *            define both the item views and the drop down views.
-     * @param c The cursor from which to get the data.
-     * @param autoRequery If true the adapter will call requery() on the
-     *                    cursor whenever it changes so the most recent
-     *                    data is always displayed.  Using true here is discouraged.
-     */
-    @Deprecated
-    public ResourceCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {
-        super(context, c, autoRequery);
-        mLayout = mDropDownLayout = layout;
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-    }
-
-    /**
-     * Standard constructor.
-     *
-     * @param context The context where the ListView associated with this adapter is running
-     * @param layout Resource identifier of a layout file that defines the views
-     *            for this list item.  Unless you override them later, this will
-     *            define both the item views and the drop down views.
-     * @param c The cursor from which to get the data.
-     * @param flags Flags used to determine the behavior of the adapter,
-     * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
-     */
-    public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
-        super(context, c, flags);
-        mLayout = mDropDownLayout = layout;
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-    }
-
-    /**
-     * Inflates view(s) from the specified XML file.
-     *
-     * @see android.widget.CursorAdapter#newView(android.content.Context,
-     *      android.database.Cursor, ViewGroup)
-     */
-    @Override
-    public View newView(Context context, Cursor cursor, ViewGroup parent) {
-        return mInflater.inflate(mLayout, parent, false);
-    }
-
-    @Override
-    public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
-        return mInflater.inflate(mDropDownLayout, parent, false);
-    }
-
-    /**
-     * <p>Sets the layout resource of the item views.</p>
-     *
-     * @param layout the layout resources used to create item views
-     */
-    public void setViewResource(int layout) {
-        mLayout = layout;
-    }
-
-    /**
-     * <p>Sets the layout resource of the drop down views.</p>
-     *
-     * @param dropDownLayout the layout resources used to create drop down views
-     */
-    public void setDropDownViewResource(int dropDownLayout) {
-        mDropDownLayout = dropDownLayout;
-    }
-}
diff --git a/android/support/v4/widget/ScrollerCompat.java b/android/support/v4/widget/ScrollerCompat.java
deleted file mode 100644
index 8e8645c..0000000
--- a/android/support/v4/widget/ScrollerCompat.java
+++ /dev/null
@@ -1,336 +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.view.animation.Interpolator;
-import android.widget.OverScroller;
-
-/**
- * Provides access to new {@link android.widget.Scroller Scroller} APIs when available.
- *
- * <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;
-
-    /**
-     * @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(context, interpolator);
-    }
-
-    /**
-     * Package protected constructor that allows to specify if API version is newer than ICS.
-     * It is useful for unit testing.
-     */
-    ScrollerCompat(Context context, Interpolator interpolator) {
-        mScroller = interpolator != null ?
-                new OverScroller(context, interpolator) : new OverScroller(context);
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * 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();
-    }
-
-    /**
-     * Returns the current velocity on platform versions that support it.
-     *
-     * <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 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();
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     * The scroll will use the default value of 250 milliseconds for the
-     * duration.
-     *
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        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);
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     *
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @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);
-    }
-
-    /**
-     * Start scrolling based on a fling gesture. The distance travelled will
-     * depend on the initial velocity of the fling.
-     *
-     * @param startX Starting point of the scroll (X)
-     * @param startY Starting point of the scroll (Y)
-     * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *        second.
-     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *        second
-     * @param minX Minimum X value. The scroller will not scroll past this
-     *        point.
-     * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point.
-     * @param minY Minimum Y value. The scroller will not scroll past this
-     *        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);
-    }
-
-    /**
-     * Start scrolling based on a fling gesture. The distance travelled will
-     * depend on the initial velocity of the fling.
-     *
-     * @param startX Starting point of the scroll (X)
-     * @param startY Starting point of the scroll (Y)
-     * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *        second.
-     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *        second
-     * @param minX Minimum X value. The scroller will not scroll past this
-     *        point.
-     * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point.
-     * @param minY Minimum Y value. The scroller will not scroll past this
-     *        point.
-     * @param maxY Maximum Y value. The scroller will not scroll past this
-     *        point.
-     * @param overX Overfling range. If > 0, horizontal overfling in either
-     *            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,
-                minX, maxX, minY, maxY, overX, overY);
-    }
-
-    /**
-     * Call this when you want to 'spring back' into a valid coordinate range.
-     *
-     * @param startX Starting X coordinate
-     * @param startY Starting Y coordinate
-     * @param minX Minimum valid X value
-     * @param maxX Maximum valid X value
-     * @param minY Minimum valid Y value
-     * @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);
-    }
-
-    /**
-     * 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();
-    }
-
-
-    /**
-     * Notify the scroller that we've reached a horizontal boundary.
-     * Normally the information to handle this will already be known
-     * when the animation is started, such as in a call to one of the
-     * fling functions. However there are cases where this cannot be known
-     * in advance. This function will transition the current motion and
-     * animate from startX to finalX as appropriate.
-     *
-     * @param startX Starting/current X position
-     * @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);
-    }
-
-    /**
-     * Notify the scroller that we've reached a vertical boundary.
-     * Normally the information to handle this will already be known
-     * when the animation is started, such as in a call to one of the
-     * fling functions. However there are cases where this cannot be known
-     * in advance. This function will animate a parabolic motion from
-     * startY to finalY.
-     *
-     * @param startY Starting/current Y position
-     * @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);
-    }
-
-    /**
-     * Returns whether the current Scroller is currently returning to a valid position.
-     * Valid bounds were provided by the
-     * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
-     *
-     * One should check this value before calling
-     * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
-     * to restore a valid position will then be stopped. The caller has to take into account
-     * the fact that the started scroll will start from an overscrolled position.
-     *
-     * @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/android/support/v4/widget/SimpleCursorAdapter.java b/android/support/v4/widget/SimpleCursorAdapter.java
deleted file mode 100644
index ba3ee50..0000000
--- a/android/support/v4/widget/SimpleCursorAdapter.java
+++ /dev/null
@@ -1,407 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.support.annotation.RestrictTo;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * Static library support version of the framework's {@link android.widget.SimpleCursorAdapter}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation.  See the framework SDK
- * documentation for a class overview.
- */
-public class SimpleCursorAdapter extends ResourceCursorAdapter {
-    /**
-     * A list of columns containing the data to bind to the UI.
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected int[] mFrom;
-    /**
-     * A list of View ids representing the views to which the data must be bound.
-     * This field should be made private, so it is hidden from the SDK.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected int[] mTo;
-
-    private int mStringConversionColumn = -1;
-    private CursorToStringConverter mCursorToStringConverter;
-    private ViewBinder mViewBinder;
-
-    String[] mOriginalFrom;
-
-    /**
-     * Constructor the enables auto-requery.
-     *
-     * @deprecated This option is discouraged, as it results in Cursor queries
-     * being performed on the application's UI thread and thus can cause poor
-     * responsiveness or even Application Not Responding errors.  As an alternative,
-     * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
-     */
-    @Deprecated
-    public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
-        super(context, layout, c);
-        mTo = to;
-        mOriginalFrom = from;
-        findColumns(c, from);
-    }
-
-    /**
-     * Standard constructor.
-     *
-     * @param context The context where the ListView associated with this
-     *            SimpleListItemFactory is running
-     * @param layout resource identifier of a layout file that defines the views
-     *            for this list item. The layout file should include at least
-     *            those named views defined in "to"
-     * @param c The database cursor.  Can be null if the cursor is not available yet.
-     * @param from A list of column names representing the data to bind to the UI.  Can be null
-     *            if the cursor is not available yet.
-     * @param to The views that should display column in the "from" parameter.
-     *            These should all be TextViews. The first N views in this list
-     *            are given the values of the first N columns in the from
-     *            parameter.  Can be null if the cursor is not available yet.
-     * @param flags Flags used to determine the behavior of the adapter,
-     * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
-     */
-    public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from,
-            int[] to, int flags) {
-        super(context, layout, c, flags);
-        mTo = to;
-        mOriginalFrom = from;
-        findColumns(c, from);
-    }
-
-    /**
-     * Binds all of the field names passed into the "to" parameter of the
-     * constructor with their corresponding cursor columns as specified in the
-     * "from" parameter.
-     *
-     * Binding occurs in two phases. First, if a
-     * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
-     * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
-     * is invoked. If the returned value is true, binding has occured. If the
-     * returned value is false and the view to bind is a TextView,
-     * {@link #setViewText(TextView, String)} is invoked. If the returned value is
-     * false and the view to bind is an ImageView,
-     * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
-     * binding can be found, an {@link IllegalStateException} is thrown.
-     *
-     * @throws IllegalStateException if binding cannot occur
-     *
-     * @see android.widget.CursorAdapter#bindView(View, Context, Cursor)
-     * @see #getViewBinder()
-     * @see #setViewBinder(ViewBinder)
-     * @see #setViewImage(ImageView, String)
-     * @see #setViewText(TextView, String)
-     */
-    @Override
-    public void bindView(View view, Context context, Cursor cursor) {
-        final ViewBinder binder = mViewBinder;
-        final int count = mTo.length;
-        final int[] from = mFrom;
-        final int[] to = mTo;
-
-        for (int i = 0; i < count; i++) {
-            final View v = view.findViewById(to[i]);
-            if (v != null) {
-                boolean bound = false;
-                if (binder != null) {
-                    bound = binder.setViewValue(v, cursor, from[i]);
-                }
-
-                if (!bound) {
-                    String text = cursor.getString(from[i]);
-                    if (text == null) {
-                        text = "";
-                    }
-
-                    if (v instanceof TextView) {
-                        setViewText((TextView) v, text);
-                    } else if (v instanceof ImageView) {
-                        setViewImage((ImageView) v, text);
-                    } else {
-                        throw new IllegalStateException(v.getClass().getName() + " is not a "
-                                + " view that can be bounds by this SimpleCursorAdapter");
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns the {@link ViewBinder} used to bind data to views.
-     *
-     * @return a ViewBinder or null if the binder does not exist
-     *
-     * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
-     * @see #setViewBinder(ViewBinder)
-     */
-    public ViewBinder getViewBinder() {
-        return mViewBinder;
-    }
-
-    /**
-     * Sets the binder used to bind data to views.
-     *
-     * @param viewBinder the binder used to bind data to views, can be null to
-     *        remove the existing binder
-     *
-     * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
-     * @see #getViewBinder()
-     */
-    public void setViewBinder(ViewBinder viewBinder) {
-        mViewBinder = viewBinder;
-    }
-
-    /**
-     * Called by bindView() to set the image for an ImageView but only if
-     * there is no existing ViewBinder or if the existing ViewBinder cannot
-     * handle binding to an ImageView.
-     *
-     * By default, the value will be treated as an image resource. If the
-     * value cannot be used as an image resource, the value is used as an
-     * image Uri.
-     *
-     * Intended to be overridden by Adapters that need to filter strings
-     * retrieved from the database.
-     *
-     * @param v ImageView to receive an image
-     * @param value the value retrieved from the cursor
-     */
-    public void setViewImage(ImageView v, String value) {
-        try {
-            v.setImageResource(Integer.parseInt(value));
-        } catch (NumberFormatException nfe) {
-            v.setImageURI(Uri.parse(value));
-        }
-    }
-
-    /**
-     * Called by bindView() to set the text for a TextView but only if
-     * there is no existing ViewBinder or if the existing ViewBinder cannot
-     * handle binding to a TextView.
-     *
-     * Intended to be overridden by Adapters that need to filter strings
-     * retrieved from the database.
-     *
-     * @param v TextView to receive text
-     * @param text the text to be set for the TextView
-     */
-    public void setViewText(TextView v, String text) {
-        v.setText(text);
-    }
-
-    /**
-     * Return the index of the column used to get a String representation
-     * of the Cursor.
-     *
-     * @return a valid index in the current Cursor or -1
-     *
-     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
-     * @see #setStringConversionColumn(int)
-     * @see #setCursorToStringConverter(CursorToStringConverter)
-     * @see #getCursorToStringConverter()
-     */
-    public int getStringConversionColumn() {
-        return mStringConversionColumn;
-    }
-
-    /**
-     * Defines the index of the column in the Cursor used to get a String
-     * representation of that Cursor. The column is used to convert the
-     * Cursor to a String only when the current CursorToStringConverter
-     * is null.
-     *
-     * @param stringConversionColumn a valid index in the current Cursor or -1 to use the default
-     *        conversion mechanism
-     *
-     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
-     * @see #getStringConversionColumn()
-     * @see #setCursorToStringConverter(CursorToStringConverter)
-     * @see #getCursorToStringConverter()
-     */
-    public void setStringConversionColumn(int stringConversionColumn) {
-        mStringConversionColumn = stringConversionColumn;
-    }
-
-    /**
-     * Returns the converter used to convert the filtering Cursor
-     * into a String.
-     *
-     * @return null if the converter does not exist or an instance of
-     *         {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}
-     *
-     * @see #setCursorToStringConverter(CursorToStringConverter)
-     * @see #getStringConversionColumn()
-     * @see #setStringConversionColumn(int)
-     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
-     */
-    public CursorToStringConverter getCursorToStringConverter() {
-        return mCursorToStringConverter;
-    }
-
-    /**
-     * Sets the converter  used to convert the filtering Cursor
-     * into a String.
-     *
-     * @param cursorToStringConverter the Cursor to String converter, or
-     *        null to remove the converter
-     *
-     * @see #setCursorToStringConverter(CursorToStringConverter)
-     * @see #getStringConversionColumn()
-     * @see #setStringConversionColumn(int)
-     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
-     */
-    public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {
-        mCursorToStringConverter = cursorToStringConverter;
-    }
-
-    /**
-     * Returns a CharSequence representation of the specified Cursor as defined
-     * by the current CursorToStringConverter. If no CursorToStringConverter
-     * has been set, the String conversion column is used instead. If the
-     * conversion column is -1, the returned String is empty if the cursor
-     * is null or Cursor.toString().
-     *
-     * @param cursor the Cursor to convert to a CharSequence
-     *
-     * @return a non-null CharSequence representing the cursor
-     */
-    @Override
-    public CharSequence convertToString(Cursor cursor) {
-        if (mCursorToStringConverter != null) {
-            return mCursorToStringConverter.convertToString(cursor);
-        } else if (mStringConversionColumn > -1) {
-            return cursor.getString(mStringConversionColumn);
-        }
-
-        return super.convertToString(cursor);
-    }
-
-    /**
-     * Create a map from an array of strings to an array of column-id integers in cursor c.
-     * If c is null, the array will be discarded.
-     *
-     * @param c the cursor to find the columns from
-     * @param from the Strings naming the columns of interest
-     */
-    private void findColumns(Cursor c, String[] from) {
-        if (c != null) {
-            int i;
-            int count = from.length;
-            if (mFrom == null || mFrom.length != count) {
-                mFrom = new int[count];
-            }
-            for (i = 0; i < count; i++) {
-                mFrom[i] = c.getColumnIndexOrThrow(from[i]);
-            }
-        } else {
-            mFrom = null;
-        }
-    }
-
-    @Override
-    public Cursor swapCursor(Cursor c) {
-        // super.swapCursor() will notify observers before we have
-        // a valid mapping, make sure we have a mapping before this
-        // happens
-        findColumns(c, mOriginalFrom);
-        return super.swapCursor(c);
-    }
-
-    /**
-     * Change the cursor and change the column-to-view mappings at the same time.
-     *
-     * @param c The database cursor.  Can be null if the cursor is not available yet.
-     * @param from A list of column names representing the data to bind to the UI.  Can be null
-     *            if the cursor is not available yet.
-     * @param to The views that should display column in the "from" parameter.
-     *            These should all be TextViews. The first N views in this list
-     *            are given the values of the first N columns in the from
-     *            parameter.  Can be null if the cursor is not available yet.
-     */
-    public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
-        mOriginalFrom = from;
-        mTo = to;
-        // super.changeCursor() will notify observers before we have
-        // a valid mapping, make sure we have a mapping before this
-        // happens
-        findColumns(c, mOriginalFrom);
-        super.changeCursor(c);
-    }
-
-    /**
-     * This class can be used by external clients of SimpleCursorAdapter
-     * to bind values fom the Cursor to views.
-     *
-     * You should use this class to bind values from the Cursor to views
-     * that are not directly supported by SimpleCursorAdapter or to
-     * change the way binding occurs for views supported by
-     * SimpleCursorAdapter.
-     *
-     * @see SimpleCursorAdapter#bindView(View, Context, Cursor)
-     * @see SimpleCursorAdapter#setViewImage(ImageView, String)
-     * @see SimpleCursorAdapter#setViewText(TextView, String)
-     */
-    public interface ViewBinder {
-        /**
-         * Binds the Cursor column defined by the specified index to the specified view.
-         *
-         * When binding is handled by this ViewBinder, this method must return true.
-         * If this method returns false, SimpleCursorAdapter will attempts to handle
-         * the binding on its own.
-         *
-         * @param view the view to bind the data to
-         * @param cursor the cursor to get the data from
-         * @param columnIndex the column at which the data can be found in the cursor
-         *
-         * @return true if the data was bound to the view, false otherwise
-         */
-        boolean setViewValue(View view, Cursor cursor, int columnIndex);
-    }
-
-    /**
-     * This class can be used by external clients of SimpleCursorAdapter
-     * to define how the Cursor should be converted to a String.
-     *
-     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
-     */
-    public interface CursorToStringConverter {
-        /**
-         * Returns a CharSequence representing the specified Cursor.
-         *
-         * @param cursor the cursor for which a CharSequence representation
-         *        is requested
-         *
-         * @return a non-null CharSequence representing the cursor
-         */
-        CharSequence convertToString(Cursor cursor);
-    }
-
-}
diff --git a/android/support/v4/widget/SlidingPaneLayout.java b/android/support/v4/widget/SlidingPaneLayout.java
deleted file mode 100644
index 5676ccf..0000000
--- a/android/support/v4/widget/SlidingPaneLayout.java
+++ /dev/null
@@ -1,1645 +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.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.ColorInt;
-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.ContextCompat;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.AccessibilityDelegateCompat;
-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.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-
-/**
- * SlidingPaneLayout provides a horizontal, multi-pane layout for use at the top level
- * of a UI. A left (or first) pane is treated as a content list or browser, subordinate to a
- * primary detail view for displaying content.
- *
- * <p>Child views may overlap if their combined width exceeds the available width
- * in the SlidingPaneLayout. When this occurs the user may slide the topmost view out of the way
- * by dragging it, or by navigating in the direction of the overlapped view using a keyboard.
- * If the content of the dragged child view is itself horizontally scrollable, the user may
- * grab it by the very edge.</p>
- *
- * <p>Thanks to this sliding behavior, SlidingPaneLayout may be suitable for creating layouts
- * that can smoothly adapt across many different screen sizes, expanding out fully on larger
- * screens and collapsing on smaller screens.</p>
- *
- * <p>SlidingPaneLayout is distinct from a navigation drawer as described in the design
- * guide and should not be used in the same scenarios. SlidingPaneLayout should be thought
- * of only as a way to allow a two-pane layout normally used on larger screens to adapt to smaller
- * screens in a natural way. The interaction patterns expressed by SlidingPaneLayout imply
- * a physicality and direct information hierarchy between panes that does not necessarily exist
- * in a scenario where a navigation drawer should be used instead.</p>
- *
- * <p>Appropriate uses of SlidingPaneLayout include pairings of panes such as a contact list and
- * subordinate interactions with those contacts, or an email thread list with the content pane
- * displaying the contents of the selected thread. Inappropriate uses of SlidingPaneLayout include
- * switching between disparate functions of your app, such as jumping from a social stream view
- * to a view of your personal profile - cases such as this should use the navigation drawer
- * pattern instead. ({@link DrawerLayout DrawerLayout} implements this pattern.)</p>
- *
- * <p>Like {@link android.widget.LinearLayout LinearLayout}, SlidingPaneLayout supports
- * the use of the layout parameter <code>layout_weight</code> on child views to determine
- * how to divide leftover space after measurement is complete. It is only relevant for width.
- * When views do not overlap weight behaves as it does in a LinearLayout.</p>
- *
- * <p>When views do overlap, weight on a slideable pane indicates that the pane should be
- * sized to fill all available space in the closed state. Weight on a pane that becomes covered
- * indicates that the pane should be sized to fill all available space except a small minimum strip
- * that the user may use to grab the slideable view and pull it back over into a closed state.</p>
- */
-public class SlidingPaneLayout extends ViewGroup {
-    private static final String TAG = "SlidingPaneLayout";
-
-    /**
-     * Default size of the overhang for a pane in the open state.
-     * At least this much of a sliding pane will remain visible.
-     * This indicates that there is more content available and provides
-     * a "physical" edge to grab to pull it closed.
-     */
-    private static final int DEFAULT_OVERHANG_SIZE = 32; // dp;
-
-    /**
-     * If no fade color is given by default it will fade to 80% gray.
-     */
-    private static final int DEFAULT_FADE_COLOR = 0xcccccccc;
-
-    /**
-     * The fade color used for the sliding panel. 0 = no fading.
-     */
-    private int mSliderFadeColor = DEFAULT_FADE_COLOR;
-
-    /**
-     * Minimum velocity that will be detected as a fling
-     */
-    private static final int MIN_FLING_VELOCITY = 400; // dips per second
-
-    /**
-     * The fade color used for the panel covered by the slider. 0 = no fading.
-     */
-    private int mCoveredFadeColor;
-
-    /**
-     * Drawable used to draw the shadow between panes by default.
-     */
-    private Drawable mShadowDrawableLeft;
-
-    /**
-     * Drawable used to draw the shadow between panes to support RTL (right to left language).
-     */
-    private Drawable mShadowDrawableRight;
-
-    /**
-     * The size of the overhang in pixels.
-     * This is the minimum section of the sliding panel that will
-     * be visible in the open state to allow for a closing drag.
-     */
-    private final int mOverhangSize;
-
-    /**
-     * True if a panel can slide with the current measurements
-     */
-    private boolean mCanSlide;
-
-    /**
-     * The child view that can slide, if any.
-     */
-    View mSlideableView;
-
-    /**
-     * How far the panel is offset from its closed position.
-     * range [0, 1] where 0 = closed, 1 = open.
-     */
-    float mSlideOffset;
-
-    /**
-     * How far the non-sliding panel is parallaxed from its usual position when open.
-     * range [0, 1]
-     */
-    private float mParallaxOffset;
-
-    /**
-     * How far in pixels the slideable panel may move.
-     */
-    int mSlideRange;
-
-    /**
-     * A panel view is locked into internal scrolling or another condition that
-     * is preventing a drag.
-     */
-    boolean mIsUnableToDrag;
-
-    /**
-     * Distance in pixels to parallax the fixed pane by when fully closed
-     */
-    private int mParallaxBy;
-
-    private float mInitialMotionX;
-    private float mInitialMotionY;
-
-    private PanelSlideListener mPanelSlideListener;
-
-    final ViewDragHelper mDragHelper;
-
-    /**
-     * Stores whether or not the pane was open the last time it was slideable.
-     * If open/close operations are invoked this state is modified. Used by
-     * instance state save/restore.
-     */
-    boolean mPreservedOpenState;
-    private boolean mFirstLayout = true;
-
-    private final Rect mTmpRect = new Rect();
-
-    final ArrayList<DisableLayerRunnable> mPostedRunnables = new ArrayList<>();
-
-    static final SlidingPanelLayoutImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 17) {
-            IMPL = new SlidingPanelLayoutImplJBMR1();
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            IMPL = new SlidingPanelLayoutImplJB();
-        } else {
-            IMPL = new SlidingPanelLayoutImplBase();
-        }
-    }
-
-    /**
-     * Listener for monitoring events about sliding panes.
-     */
-    public interface PanelSlideListener {
-        /**
-         * Called when a sliding pane's position changes.
-         * @param panel The child view that was moved
-         * @param slideOffset The new offset of this sliding pane within its range, from 0-1
-         */
-        void onPanelSlide(@NonNull View panel, float slideOffset);
-        /**
-         * Called when a sliding pane becomes slid completely open. The pane may or may not
-         * be interactive at this point depending on how much of the pane is visible.
-         * @param panel The child view that was slid to an open position, revealing other panes
-         */
-        void onPanelOpened(@NonNull View panel);
-
-        /**
-         * Called when a sliding pane becomes slid completely closed. The pane is now guaranteed
-         * to be interactive. It may now obscure other views in the layout.
-         * @param panel The child view that was slid to a closed position
-         */
-        void onPanelClosed(@NonNull View panel);
-    }
-
-    /**
-     * No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset
-     * of the listener methods you can extend this instead of implement the full interface.
-     */
-    public static class SimplePanelSlideListener implements PanelSlideListener {
-        @Override
-        public void onPanelSlide(View panel, float slideOffset) {
-        }
-        @Override
-        public void onPanelOpened(View panel) {
-        }
-        @Override
-        public void onPanelClosed(View panel) {
-        }
-    }
-
-    public SlidingPaneLayout(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public SlidingPaneLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SlidingPaneLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        final float density = context.getResources().getDisplayMetrics().density;
-        mOverhangSize = (int) (DEFAULT_OVERHANG_SIZE * density + 0.5f);
-
-        setWillNotDraw(false);
-
-        ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());
-        ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-
-        mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
-        mDragHelper.setMinVelocity(MIN_FLING_VELOCITY * density);
-    }
-
-    /**
-     * Set a distance to parallax the lower pane by when the upper pane is in its
-     * fully closed state. The lower pane will scroll between this position and
-     * its fully open state.
-     *
-     * @param parallaxBy Distance to parallax by in pixels
-     */
-    public void setParallaxDistance(int parallaxBy) {
-        mParallaxBy = parallaxBy;
-        requestLayout();
-    }
-
-    /**
-     * @return The distance the lower pane will parallax by when the upper pane is fully closed.
-     *
-     * @see #setParallaxDistance(int)
-     */
-    public int getParallaxDistance() {
-        return mParallaxBy;
-    }
-
-    /**
-     * Set the color used to fade the sliding pane out when it is slid most of the way offscreen.
-     *
-     * @param color An ARGB-packed color value
-     */
-    public void setSliderFadeColor(@ColorInt int color) {
-        mSliderFadeColor = color;
-    }
-
-    /**
-     * @return The ARGB-packed color value used to fade the sliding pane
-     */
-    @ColorInt
-    public int getSliderFadeColor() {
-        return mSliderFadeColor;
-    }
-
-    /**
-     * Set the color used to fade the pane covered by the sliding pane out when the pane
-     * will become fully covered in the closed state.
-     *
-     * @param color An ARGB-packed color value
-     */
-    public void setCoveredFadeColor(@ColorInt int color) {
-        mCoveredFadeColor = color;
-    }
-
-    /**
-     * @return The ARGB-packed color value used to fade the fixed pane
-     */
-    @ColorInt
-    public int getCoveredFadeColor() {
-        return mCoveredFadeColor;
-    }
-
-    public void setPanelSlideListener(@Nullable PanelSlideListener listener) {
-        mPanelSlideListener = listener;
-    }
-
-    void dispatchOnPanelSlide(View panel) {
-        if (mPanelSlideListener != null) {
-            mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
-        }
-    }
-
-    void dispatchOnPanelOpened(View panel) {
-        if (mPanelSlideListener != null) {
-            mPanelSlideListener.onPanelOpened(panel);
-        }
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-    }
-
-    void dispatchOnPanelClosed(View panel) {
-        if (mPanelSlideListener != null) {
-            mPanelSlideListener.onPanelClosed(panel);
-        }
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-    }
-
-    void updateObscuredViewsVisibility(View panel) {
-        final boolean isLayoutRtl = isLayoutRtlSupport();
-        final int startBound = isLayoutRtl ? (getWidth() - getPaddingRight()) : getPaddingLeft();
-        final int endBound = isLayoutRtl ? getPaddingLeft() : (getWidth() - getPaddingRight());
-        final int topBound = getPaddingTop();
-        final int bottomBound = getHeight() - getPaddingBottom();
-        final int left;
-        final int right;
-        final int top;
-        final int bottom;
-        if (panel != null && viewIsOpaque(panel)) {
-            left = panel.getLeft();
-            right = panel.getRight();
-            top = panel.getTop();
-            bottom = panel.getBottom();
-        } else {
-            left = right = top = bottom = 0;
-        }
-
-        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
-            final View child = getChildAt(i);
-
-            if (child == panel) {
-                // There are still more children above the panel but they won't be affected.
-                break;
-            } else if (child.getVisibility() == GONE) {
-                continue;
-            }
-
-            final int clampedChildLeft = Math.max(
-                    (isLayoutRtl ? endBound : startBound), child.getLeft());
-            final int clampedChildTop = Math.max(topBound, child.getTop());
-            final int clampedChildRight = Math.min(
-                    (isLayoutRtl ? startBound : endBound), child.getRight());
-            final int clampedChildBottom = Math.min(bottomBound, child.getBottom());
-            final int vis;
-            if (clampedChildLeft >= left && clampedChildTop >= top
-                    && clampedChildRight <= right && clampedChildBottom <= bottom) {
-                vis = INVISIBLE;
-            } else {
-                vis = VISIBLE;
-            }
-            child.setVisibility(vis);
-        }
-    }
-
-    void setAllChildrenVisible() {
-        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == INVISIBLE) {
-                child.setVisibility(VISIBLE);
-            }
-        }
-    }
-
-    private static boolean viewIsOpaque(View v) {
-        if (v.isOpaque()) {
-            return true;
-        }
-
-        // View#isOpaque didn't take all valid opaque scrollbar modes into account
-        // before API 18 (JB-MR2). On newer devices rely solely on isOpaque above and return false
-        // here. On older devices, check the view's background drawable directly as a fallback.
-        if (Build.VERSION.SDK_INT >= 18) {
-            return false;
-        }
-
-        final Drawable bg = v.getBackground();
-        if (bg != null) {
-            return bg.getOpacity() == PixelFormat.OPAQUE;
-        }
-        return false;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mFirstLayout = true;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mFirstLayout = true;
-
-        for (int i = 0, count = mPostedRunnables.size(); i < count; i++) {
-            final DisableLayerRunnable dlr = mPostedRunnables.get(i);
-            dlr.run();
-        }
-        mPostedRunnables.clear();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        if (widthMode != MeasureSpec.EXACTLY) {
-            if (isInEditMode()) {
-                // Don't crash the layout editor. Consume all of the space if specified
-                // or pick a magic number from thin air otherwise.
-                // TODO Better communication with tools of this bogus state.
-                // It will crash on a real device.
-                if (widthMode == MeasureSpec.AT_MOST) {
-                    widthMode = MeasureSpec.EXACTLY;
-                } else if (widthMode == MeasureSpec.UNSPECIFIED) {
-                    widthMode = MeasureSpec.EXACTLY;
-                    widthSize = 300;
-                }
-            } else {
-                throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
-            }
-        } else if (heightMode == MeasureSpec.UNSPECIFIED) {
-            if (isInEditMode()) {
-                // Don't crash the layout editor. Pick a magic number from thin air instead.
-                // TODO Better communication with tools of this bogus state.
-                // It will crash on a real device.
-                if (heightMode == MeasureSpec.UNSPECIFIED) {
-                    heightMode = MeasureSpec.AT_MOST;
-                    heightSize = 300;
-                }
-            } else {
-                throw new IllegalStateException("Height must not be UNSPECIFIED");
-            }
-        }
-
-        int layoutHeight = 0;
-        int maxLayoutHeight = 0;
-        switch (heightMode) {
-            case MeasureSpec.EXACTLY:
-                layoutHeight = maxLayoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
-                break;
-            case MeasureSpec.AT_MOST:
-                maxLayoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
-                break;
-        }
-
-        float weightSum = 0;
-        boolean canSlide = false;
-        final int widthAvailable = widthSize - getPaddingLeft() - getPaddingRight();
-        int widthRemaining = widthAvailable;
-        final int childCount = getChildCount();
-
-        if (childCount > 2) {
-            Log.e(TAG, "onMeasure: More than two child views are not supported.");
-        }
-
-        // We'll find the current one below.
-        mSlideableView = null;
-
-        // First pass. Measure based on child LayoutParams width/height.
-        // Weight will incur a second pass.
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            if (child.getVisibility() == GONE) {
-                lp.dimWhenOffset = false;
-                continue;
-            }
-
-            if (lp.weight > 0) {
-                weightSum += lp.weight;
-
-                // If we have no width, weight is the only contributor to the final size.
-                // Measure this view on the weight pass only.
-                if (lp.width == 0) continue;
-            }
-
-            int childWidthSpec;
-            final int horizontalMargin = lp.leftMargin + lp.rightMargin;
-            if (lp.width == LayoutParams.WRAP_CONTENT) {
-                childWidthSpec = MeasureSpec.makeMeasureSpec(widthAvailable - horizontalMargin,
-                        MeasureSpec.AT_MOST);
-            } else if (lp.width == LayoutParams.MATCH_PARENT) {
-                childWidthSpec = MeasureSpec.makeMeasureSpec(widthAvailable - horizontalMargin,
-                        MeasureSpec.EXACTLY);
-            } else {
-                childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
-            }
-
-            int childHeightSpec;
-            if (lp.height == LayoutParams.WRAP_CONTENT) {
-                childHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight, MeasureSpec.AT_MOST);
-            } else if (lp.height == LayoutParams.MATCH_PARENT) {
-                childHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight, MeasureSpec.EXACTLY);
-            } else {
-                childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
-            }
-
-            child.measure(childWidthSpec, childHeightSpec);
-            final int childWidth = child.getMeasuredWidth();
-            final int childHeight = child.getMeasuredHeight();
-
-            if (heightMode == MeasureSpec.AT_MOST && childHeight > layoutHeight) {
-                layoutHeight = Math.min(childHeight, maxLayoutHeight);
-            }
-
-            widthRemaining -= childWidth;
-            canSlide |= lp.slideable = widthRemaining < 0;
-            if (lp.slideable) {
-                mSlideableView = child;
-            }
-        }
-
-        // Resolve weight and make sure non-sliding panels are smaller than the full screen.
-        if (canSlide || weightSum > 0) {
-            final int fixedPanelWidthLimit = widthAvailable - mOverhangSize;
-
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-
-                if (child.getVisibility() == GONE) {
-                    continue;
-                }
-
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-                if (child.getVisibility() == GONE) {
-                    continue;
-                }
-
-                final boolean skippedFirstPass = lp.width == 0 && lp.weight > 0;
-                final int measuredWidth = skippedFirstPass ? 0 : child.getMeasuredWidth();
-                if (canSlide && child != mSlideableView) {
-                    if (lp.width < 0 && (measuredWidth > fixedPanelWidthLimit || lp.weight > 0)) {
-                        // Fixed panels in a sliding configuration should
-                        // be clamped to the fixed panel limit.
-                        final int childHeightSpec;
-                        if (skippedFirstPass) {
-                            // Do initial height measurement if we skipped measuring this view
-                            // the first time around.
-                            if (lp.height == LayoutParams.WRAP_CONTENT) {
-                                childHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight,
-                                        MeasureSpec.AT_MOST);
-                            } else if (lp.height == LayoutParams.MATCH_PARENT) {
-                                childHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight,
-                                        MeasureSpec.EXACTLY);
-                            } else {
-                                childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height,
-                                        MeasureSpec.EXACTLY);
-                            }
-                        } else {
-                            childHeightSpec = MeasureSpec.makeMeasureSpec(
-                                    child.getMeasuredHeight(), MeasureSpec.EXACTLY);
-                        }
-                        final int childWidthSpec = MeasureSpec.makeMeasureSpec(
-                                fixedPanelWidthLimit, MeasureSpec.EXACTLY);
-                        child.measure(childWidthSpec, childHeightSpec);
-                    }
-                } else if (lp.weight > 0) {
-                    int childHeightSpec;
-                    if (lp.width == 0) {
-                        // This was skipped the first time; figure out a real height spec.
-                        if (lp.height == LayoutParams.WRAP_CONTENT) {
-                            childHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight,
-                                    MeasureSpec.AT_MOST);
-                        } else if (lp.height == LayoutParams.MATCH_PARENT) {
-                            childHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight,
-                                    MeasureSpec.EXACTLY);
-                        } else {
-                            childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height,
-                                    MeasureSpec.EXACTLY);
-                        }
-                    } else {
-                        childHeightSpec = MeasureSpec.makeMeasureSpec(
-                                child.getMeasuredHeight(), MeasureSpec.EXACTLY);
-                    }
-
-                    if (canSlide) {
-                        // Consume available space
-                        final int horizontalMargin = lp.leftMargin + lp.rightMargin;
-                        final int newWidth = widthAvailable - horizontalMargin;
-                        final int childWidthSpec = MeasureSpec.makeMeasureSpec(
-                                newWidth, MeasureSpec.EXACTLY);
-                        if (measuredWidth != newWidth) {
-                            child.measure(childWidthSpec, childHeightSpec);
-                        }
-                    } else {
-                        // Distribute the extra width proportionally similar to LinearLayout
-                        final int widthToDistribute = Math.max(0, widthRemaining);
-                        final int addedWidth = (int) (lp.weight * widthToDistribute / weightSum);
-                        final int childWidthSpec = MeasureSpec.makeMeasureSpec(
-                                measuredWidth + addedWidth, MeasureSpec.EXACTLY);
-                        child.measure(childWidthSpec, childHeightSpec);
-                    }
-                }
-            }
-        }
-
-        final int measuredWidth = widthSize;
-        final int measuredHeight = layoutHeight + getPaddingTop() + getPaddingBottom();
-
-        setMeasuredDimension(measuredWidth, measuredHeight);
-        mCanSlide = canSlide;
-
-        if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE && !canSlide) {
-            // Cancel scrolling in progress, it's no longer relevant.
-            mDragHelper.abort();
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        final boolean isLayoutRtl = isLayoutRtlSupport();
-        if (isLayoutRtl) {
-            mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);
-        } else {
-            mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
-        }
-        final int width = r - l;
-        final int paddingStart = isLayoutRtl ? getPaddingRight() : getPaddingLeft();
-        final int paddingEnd = isLayoutRtl ? getPaddingLeft() : getPaddingRight();
-        final int paddingTop = getPaddingTop();
-
-        final int childCount = getChildCount();
-        int xStart = paddingStart;
-        int nextXStart = xStart;
-
-        if (mFirstLayout) {
-            mSlideOffset = mCanSlide && mPreservedOpenState ? 1.f : 0.f;
-        }
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-
-            if (child.getVisibility() == GONE) {
-                continue;
-            }
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            final int childWidth = child.getMeasuredWidth();
-            int offset = 0;
-
-            if (lp.slideable) {
-                final int margin = lp.leftMargin + lp.rightMargin;
-                final int range = Math.min(nextXStart,
-                        width - paddingEnd - mOverhangSize) - xStart - margin;
-                mSlideRange = range;
-                final int lpMargin = isLayoutRtl ? lp.rightMargin : lp.leftMargin;
-                lp.dimWhenOffset = xStart + lpMargin + range + childWidth / 2 > width - paddingEnd;
-                final int pos = (int) (range * mSlideOffset);
-                xStart += pos + lpMargin;
-                mSlideOffset = (float) pos / mSlideRange;
-            } else if (mCanSlide && mParallaxBy != 0) {
-                offset = (int) ((1 - mSlideOffset) * mParallaxBy);
-                xStart = nextXStart;
-            } else {
-                xStart = nextXStart;
-            }
-
-            final int childRight;
-            final int childLeft;
-            if (isLayoutRtl) {
-                childRight = width - xStart + offset;
-                childLeft = childRight - childWidth;
-            } else {
-                childLeft = xStart - offset;
-                childRight = childLeft + childWidth;
-            }
-
-            final int childTop = paddingTop;
-            final int childBottom = childTop + child.getMeasuredHeight();
-            child.layout(childLeft, paddingTop, childRight, childBottom);
-
-            nextXStart += child.getWidth();
-        }
-
-        if (mFirstLayout) {
-            if (mCanSlide) {
-                if (mParallaxBy != 0) {
-                    parallaxOtherViews(mSlideOffset);
-                }
-                if (((LayoutParams) mSlideableView.getLayoutParams()).dimWhenOffset) {
-                    dimChildView(mSlideableView, mSlideOffset, mSliderFadeColor);
-                }
-            } else {
-                // Reset the dim level of all children; it's irrelevant when nothing moves.
-                for (int i = 0; i < childCount; i++) {
-                    dimChildView(getChildAt(i), 0, mSliderFadeColor);
-                }
-            }
-            updateObscuredViewsVisibility(mSlideableView);
-        }
-
-        mFirstLayout = false;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        // Recalculate sliding panes and their details
-        if (w != oldw) {
-            mFirstLayout = true;
-        }
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        super.requestChildFocus(child, focused);
-        if (!isInTouchMode() && !mCanSlide) {
-            mPreservedOpenState = child == mSlideableView;
-        }
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent 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) {
-            // After the first things will be slideable.
-            final View secondChild = getChildAt(1);
-            if (secondChild != null) {
-                mPreservedOpenState = !mDragHelper.isViewUnder(secondChild,
-                        (int) ev.getX(), (int) ev.getY());
-            }
-        }
-
-        if (!mCanSlide || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
-            mDragHelper.cancel();
-            return super.onInterceptTouchEvent(ev);
-        }
-
-        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
-            mDragHelper.cancel();
-            return false;
-        }
-
-        boolean interceptTap = false;
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                mIsUnableToDrag = false;
-                final float x = ev.getX();
-                final float y = ev.getY();
-                mInitialMotionX = x;
-                mInitialMotionY = y;
-
-                if (mDragHelper.isViewUnder(mSlideableView, (int) x, (int) y)
-                        && isDimmed(mSlideableView)) {
-                    interceptTap = true;
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_MOVE: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                final float adx = Math.abs(x - mInitialMotionX);
-                final float ady = Math.abs(y - mInitialMotionY);
-                final int slop = mDragHelper.getTouchSlop();
-                if (adx > slop && ady > adx) {
-                    mDragHelper.cancel();
-                    mIsUnableToDrag = true;
-                    return false;
-                }
-            }
-        }
-
-        final boolean interceptForDrag = mDragHelper.shouldInterceptTouchEvent(ev);
-
-        return interceptForDrag || interceptTap;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (!mCanSlide) {
-            return super.onTouchEvent(ev);
-        }
-
-        mDragHelper.processTouchEvent(ev);
-
-        boolean wantTouchEvents = true;
-
-        switch (ev.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                mInitialMotionX = x;
-                mInitialMotionY = y;
-                break;
-            }
-
-            case MotionEvent.ACTION_UP: {
-                if (isDimmed(mSlideableView)) {
-                    final float x = ev.getX();
-                    final float y = ev.getY();
-                    final float dx = x - mInitialMotionX;
-                    final float dy = y - mInitialMotionY;
-                    final int slop = mDragHelper.getTouchSlop();
-                    if (dx * dx + dy * dy < slop * slop
-                            && mDragHelper.isViewUnder(mSlideableView, (int) x, (int) y)) {
-                        // Taps close a dimmed open pane.
-                        closePane(mSlideableView, 0);
-                        break;
-                    }
-                }
-                break;
-            }
-        }
-
-        return wantTouchEvents;
-    }
-
-    private boolean closePane(View pane, int initialVelocity) {
-        if (mFirstLayout || smoothSlideTo(0.f, initialVelocity)) {
-            mPreservedOpenState = false;
-            return true;
-        }
-        return false;
-    }
-
-    private boolean openPane(View pane, int initialVelocity) {
-        if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) {
-            mPreservedOpenState = true;
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @deprecated Renamed to {@link #openPane()} - this method is going away soon!
-     */
-    @Deprecated
-    public void smoothSlideOpen() {
-        openPane();
-    }
-
-    /**
-     * Open the sliding pane if it is currently slideable. If first layout
-     * has already completed this will animate.
-     *
-     * @return true if the pane was slideable and is now open/in the process of opening
-     */
-    public boolean openPane() {
-        return openPane(mSlideableView, 0);
-    }
-
-    /**
-     * @deprecated Renamed to {@link #closePane()} - this method is going away soon!
-     */
-    @Deprecated
-    public void smoothSlideClosed() {
-        closePane();
-    }
-
-    /**
-     * Close the sliding pane if it is currently slideable. If first layout
-     * has already completed this will animate.
-     *
-     * @return true if the pane was slideable and is now closed/in the process of closing
-     */
-    public boolean closePane() {
-        return closePane(mSlideableView, 0);
-    }
-
-    /**
-     * Check if the layout is completely open. It can be open either because the slider
-     * itself is open revealing the left pane, or if all content fits without sliding.
-     *
-     * @return true if sliding panels are completely open
-     */
-    public boolean isOpen() {
-        return !mCanSlide || mSlideOffset == 1;
-    }
-
-    /**
-     * @return true if content in this layout can be slid open and closed
-     * @deprecated Renamed to {@link #isSlideable()} - this method is going away soon!
-     */
-    @Deprecated
-    public boolean canSlide() {
-        return mCanSlide;
-    }
-
-    /**
-     * Check if the content in this layout cannot fully fit side by side and therefore
-     * the content pane can be slid back and forth.
-     *
-     * @return true if content in this layout can be slid open and closed
-     */
-    public boolean isSlideable() {
-        return mCanSlide;
-    }
-
-    void onPanelDragged(int newLeft) {
-        if (mSlideableView == null) {
-            // This can happen if we're aborting motion during layout because everything now fits.
-            mSlideOffset = 0;
-            return;
-        }
-        final boolean isLayoutRtl = isLayoutRtlSupport();
-        final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
-
-        int childWidth = mSlideableView.getWidth();
-        final int newStart = isLayoutRtl ? getWidth() - newLeft - childWidth : newLeft;
-
-        final int paddingStart = isLayoutRtl ? getPaddingRight() : getPaddingLeft();
-        final int lpMargin = isLayoutRtl ? lp.rightMargin : lp.leftMargin;
-        final int startBound = paddingStart + lpMargin;
-
-        mSlideOffset = (float) (newStart - startBound) / mSlideRange;
-
-        if (mParallaxBy != 0) {
-            parallaxOtherViews(mSlideOffset);
-        }
-
-        if (lp.dimWhenOffset) {
-            dimChildView(mSlideableView, mSlideOffset, mSliderFadeColor);
-        }
-        dispatchOnPanelSlide(mSlideableView);
-    }
-
-    private void dimChildView(View v, float mag, int fadeColor) {
-        final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-
-        if (mag > 0 && fadeColor != 0) {
-            final int baseAlpha = (fadeColor & 0xff000000) >>> 24;
-            int imag = (int) (baseAlpha * mag);
-            int color = imag << 24 | (fadeColor & 0xffffff);
-            if (lp.dimPaint == null) {
-                lp.dimPaint = new Paint();
-            }
-            lp.dimPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_OVER));
-            if (v.getLayerType() != View.LAYER_TYPE_HARDWARE) {
-                v.setLayerType(View.LAYER_TYPE_HARDWARE, lp.dimPaint);
-            }
-            invalidateChildRegion(v);
-        } else if (v.getLayerType() != View.LAYER_TYPE_NONE) {
-            if (lp.dimPaint != null) {
-                lp.dimPaint.setColorFilter(null);
-            }
-            final DisableLayerRunnable dlr = new DisableLayerRunnable(v);
-            mPostedRunnables.add(dlr);
-            ViewCompat.postOnAnimation(this, dlr);
-        }
-    }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        boolean result;
-        final int save = canvas.save();
-
-        if (mCanSlide && !lp.slideable && mSlideableView != null) {
-            // Clip against the slider; no sense drawing what will immediately be covered.
-            canvas.getClipBounds(mTmpRect);
-            if (isLayoutRtlSupport()) {
-                mTmpRect.left = Math.max(mTmpRect.left, mSlideableView.getRight());
-            } else {
-                mTmpRect.right = Math.min(mTmpRect.right, mSlideableView.getLeft());
-            }
-            canvas.clipRect(mTmpRect);
-        }
-
-        result = super.drawChild(canvas, child, drawingTime);
-
-        canvas.restoreToCount(save);
-
-        return result;
-    }
-
-    void invalidateChildRegion(View v) {
-        IMPL.invalidateChildRegion(this, v);
-    }
-
-    /**
-     * Smoothly animate mDraggingPane to the target X position within its range.
-     *
-     * @param slideOffset position to animate to
-     * @param velocity initial velocity in case of fling, or 0.
-     */
-    boolean smoothSlideTo(float slideOffset, int velocity) {
-        if (!mCanSlide) {
-            // Nothing to do.
-            return false;
-        }
-
-        final boolean isLayoutRtl = isLayoutRtlSupport();
-        final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
-
-        int x;
-        if (isLayoutRtl) {
-            int startBound = getPaddingRight() + lp.rightMargin;
-            int childWidth = mSlideableView.getWidth();
-            x = (int) (getWidth() - (startBound + slideOffset * mSlideRange + childWidth));
-        } else {
-            int startBound = getPaddingLeft() + lp.leftMargin;
-            x = (int) (startBound + slideOffset * mSlideRange);
-        }
-
-        if (mDragHelper.smoothSlideViewTo(mSlideableView, x, mSlideableView.getTop())) {
-            setAllChildrenVisible();
-            ViewCompat.postInvalidateOnAnimation(this);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mDragHelper.continueSettling(true)) {
-            if (!mCanSlide) {
-                mDragHelper.abort();
-                return;
-            }
-
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    /**
-     * @deprecated Renamed to {@link #setShadowDrawableLeft(Drawable d)} to support LTR (left to
-     * right language) and {@link #setShadowDrawableRight(Drawable d)} to support RTL (right to left
-     * language) during opening/closing.
-     *
-     * @param d drawable to use as a shadow
-     */
-    @Deprecated
-    public void setShadowDrawable(Drawable d) {
-        setShadowDrawableLeft(d);
-    }
-
-    /**
-     * Set a drawable to use as a shadow cast by the right pane onto the left pane
-     * during opening/closing.
-     *
-     * @param d drawable to use as a shadow
-     */
-    public void setShadowDrawableLeft(@Nullable Drawable d) {
-        mShadowDrawableLeft = d;
-    }
-
-    /**
-     * Set a drawable to use as a shadow cast by the left pane onto the right pane
-     * during opening/closing to support right to left language.
-     *
-     * @param d drawable to use as a shadow
-     */
-    public void setShadowDrawableRight(@Nullable Drawable d) {
-        mShadowDrawableRight = d;
-    }
-
-    /**
-     * Set a drawable to use as a shadow cast by the right pane onto the left pane
-     * during opening/closing.
-     *
-     * @param resId Resource ID of a drawable to use
-     * @deprecated Renamed to {@link #setShadowResourceLeft(int)} to support LTR (left to
-     * right language) and {@link #setShadowResourceRight(int)} to support RTL (right to left
-     * language) during opening/closing.
-     */
-    @Deprecated
-    public void setShadowResource(@DrawableRes int resId) {
-        setShadowDrawable(getResources().getDrawable(resId));
-    }
-
-    /**
-     * Set a drawable to use as a shadow cast by the right pane onto the left pane
-     * during opening/closing.
-     *
-     * @param resId Resource ID of a drawable to use
-     */
-    public void setShadowResourceLeft(int resId) {
-        setShadowDrawableLeft(ContextCompat.getDrawable(getContext(), resId));
-    }
-
-    /**
-     * Set a drawable to use as a shadow cast by the left pane onto the right pane
-     * during opening/closing to support right to left language.
-     *
-     * @param resId Resource ID of a drawable to use
-     */
-    public void setShadowResourceRight(int resId) {
-        setShadowDrawableRight(ContextCompat.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        super.draw(c);
-        final boolean isLayoutRtl = isLayoutRtlSupport();
-        Drawable shadowDrawable;
-        if (isLayoutRtl) {
-            shadowDrawable = mShadowDrawableRight;
-        } else {
-            shadowDrawable = mShadowDrawableLeft;
-        }
-
-        final View shadowView = getChildCount() > 1 ? getChildAt(1) : null;
-        if (shadowView == null || shadowDrawable == null) {
-            // No need to draw a shadow if we don't have one.
-            return;
-        }
-
-        final int top = shadowView.getTop();
-        final int bottom = shadowView.getBottom();
-
-        final int shadowWidth = shadowDrawable.getIntrinsicWidth();
-        final int left;
-        final int right;
-        if (isLayoutRtlSupport()) {
-            left = shadowView.getRight();
-            right = left + shadowWidth;
-        } else {
-            right = shadowView.getLeft();
-            left = right - shadowWidth;
-        }
-
-        shadowDrawable.setBounds(left, top, right, bottom);
-        shadowDrawable.draw(c);
-    }
-
-    private void parallaxOtherViews(float slideOffset) {
-        final boolean isLayoutRtl = isLayoutRtlSupport();
-        final LayoutParams slideLp = (LayoutParams) mSlideableView.getLayoutParams();
-        final boolean dimViews = slideLp.dimWhenOffset
-                && (isLayoutRtl ? slideLp.rightMargin : slideLp.leftMargin) <= 0;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View v = getChildAt(i);
-            if (v == mSlideableView) continue;
-
-            final int oldOffset = (int) ((1 - mParallaxOffset) * mParallaxBy);
-            mParallaxOffset = slideOffset;
-            final int newOffset = (int) ((1 - slideOffset) * mParallaxBy);
-            final int dx = oldOffset - newOffset;
-
-            v.offsetLeftAndRight(isLayoutRtl ? -dx : dx);
-
-            if (dimViews) {
-                dimChildView(v, isLayoutRtl ? mParallaxOffset - 1
-                        : 1 - mParallaxOffset, mCoveredFadeColor);
-            }
-        }
-    }
-
-    /**
-     * Tests scrollability within child views of v given a delta of dx.
-     *
-     * @param v View to test for horizontal scrollability
-     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
-     *               or just its children (false).
-     * @param dx Delta scrolled in pixels
-     * @param x X coordinate of the active touch point
-     * @param y Y coordinate of the active touch point
-     * @return true if child views of v can be scrolled by delta of dx.
-     */
-    protected boolean canScroll(View v, boolean checkV, int dx, int x, int 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();
-            // Count backwards - let topmost views consume scroll distance first.
-            for (int i = count - 1; i >= 0; i--) {
-                // TODO: Add versioned support here for transformed views.
-                // This will not work for transformed views in Honeycomb+
-                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((isLayoutRtlSupport() ? dx : -dx));
-    }
-
-    boolean isDimmed(View child) {
-        if (child == null) {
-            return false;
-        }
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        return mCanSlide && lp.dimWhenOffset && mSlideOffset > 0;
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams();
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof MarginLayoutParams
-                ? new LayoutParams((MarginLayoutParams) p)
-                : new LayoutParams(p);
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams && super.checkLayoutParams(p);
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-
-        SavedState ss = new SavedState(superState);
-        ss.isOpen = isSlideable() ? isOpen() : mPreservedOpenState;
-
-        return ss;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-
-        if (ss.isOpen) {
-            openPane();
-        } else {
-            closePane();
-        }
-        mPreservedOpenState = ss.isOpen;
-    }
-
-    private class DragHelperCallback extends ViewDragHelper.Callback {
-
-        DragHelperCallback() {
-        }
-
-        @Override
-        public boolean tryCaptureView(View child, int pointerId) {
-            if (mIsUnableToDrag) {
-                return false;
-            }
-
-            return ((LayoutParams) child.getLayoutParams()).slideable;
-        }
-
-        @Override
-        public void onViewDragStateChanged(int state) {
-            if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
-                if (mSlideOffset == 0) {
-                    updateObscuredViewsVisibility(mSlideableView);
-                    dispatchOnPanelClosed(mSlideableView);
-                    mPreservedOpenState = false;
-                } else {
-                    dispatchOnPanelOpened(mSlideableView);
-                    mPreservedOpenState = true;
-                }
-            }
-        }
-
-        @Override
-        public void onViewCaptured(View capturedChild, int activePointerId) {
-            // Make all child views visible in preparation for sliding things around
-            setAllChildrenVisible();
-        }
-
-        @Override
-        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
-            onPanelDragged(left);
-            invalidate();
-        }
-
-        @Override
-        public void onViewReleased(View releasedChild, float xvel, float yvel) {
-            final LayoutParams lp = (LayoutParams) releasedChild.getLayoutParams();
-
-            int left;
-            if (isLayoutRtlSupport()) {
-                int startToRight =  getPaddingRight() + lp.rightMargin;
-                if (xvel < 0 || (xvel == 0 && mSlideOffset > 0.5f)) {
-                    startToRight += mSlideRange;
-                }
-                int childWidth = mSlideableView.getWidth();
-                left = getWidth() - startToRight - childWidth;
-            } else {
-                left = getPaddingLeft() + lp.leftMargin;
-                if (xvel > 0 || (xvel == 0 && mSlideOffset > 0.5f)) {
-                    left += mSlideRange;
-                }
-            }
-            mDragHelper.settleCapturedViewAt(left, releasedChild.getTop());
-            invalidate();
-        }
-
-        @Override
-        public int getViewHorizontalDragRange(View child) {
-            return mSlideRange;
-        }
-
-        @Override
-        public int clampViewPositionHorizontal(View child, int left, int dx) {
-            final LayoutParams lp = (LayoutParams) mSlideableView.getLayoutParams();
-
-            final int newLeft;
-            if (isLayoutRtlSupport()) {
-                int startBound = getWidth()
-                        - (getPaddingRight() + lp.rightMargin + mSlideableView.getWidth());
-                int endBound =  startBound - mSlideRange;
-                newLeft = Math.max(Math.min(left, startBound), endBound);
-            } else {
-                int startBound = getPaddingLeft() + lp.leftMargin;
-                int endBound = startBound + mSlideRange;
-                newLeft = Math.min(Math.max(left, startBound), endBound);
-            }
-            return newLeft;
-        }
-
-        @Override
-        public int clampViewPositionVertical(View child, int top, int dy) {
-            // Make sure we never move views vertically.
-            // This could happen if the child has less height than its parent.
-            return child.getTop();
-        }
-
-        @Override
-        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
-            mDragHelper.captureChildView(mSlideableView, pointerId);
-        }
-    }
-
-    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-        private static final int[] ATTRS = new int[] {
-            android.R.attr.layout_weight
-        };
-
-        /**
-         * The weighted proportion of how much of the leftover space
-         * this child should consume after measurement.
-         */
-        public float weight = 0;
-
-        /**
-         * True if this pane is the slideable pane in the layout.
-         */
-        boolean slideable;
-
-        /**
-         * True if this view should be drawn dimmed
-         * when it's been offset from its default position.
-         */
-        boolean dimWhenOffset;
-
-        Paint dimPaint;
-
-        public LayoutParams() {
-            super(MATCH_PARENT, MATCH_PARENT);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(@NonNull android.view.ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(@NonNull MarginLayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(@NonNull LayoutParams source) {
-            super(source);
-            this.weight = source.weight;
-        }
-
-        public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
-            super(c, attrs);
-
-            final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
-            this.weight = a.getFloat(0, 0);
-            a.recycle();
-        }
-
-    }
-
-    static class SavedState extends AbsSavedState {
-        boolean isOpen;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        SavedState(Parcel in, ClassLoader loader) {
-            super(in, loader);
-            isOpen = in.readInt() != 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(isOpen ? 1 : 0);
-        }
-
-        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 createFromParcel(Parcel in) {
-                return new SavedState(in, null);
-            }
-
-            @Override
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-
-    interface SlidingPanelLayoutImpl {
-        void invalidateChildRegion(SlidingPaneLayout parent, View child);
-    }
-
-    static class SlidingPanelLayoutImplBase implements SlidingPanelLayoutImpl {
-        @Override
-        public void invalidateChildRegion(SlidingPaneLayout parent, View child) {
-            ViewCompat.postInvalidateOnAnimation(parent, child.getLeft(), child.getTop(),
-                    child.getRight(), child.getBottom());
-        }
-    }
-
-    @RequiresApi(16)
-    static class SlidingPanelLayoutImplJB extends SlidingPanelLayoutImplBase {
-        /*
-         * Private API hacks! Nasty! Bad!
-         *
-         * In Jellybean, some optimizations in the hardware UI renderer
-         * prevent a changed Paint on a View using a hardware layer from having
-         * the intended effect. This twiddles some internal bits on the view to force
-         * it to recreate the display list.
-         */
-        private Method mGetDisplayList;
-        private Field mRecreateDisplayList;
-
-        SlidingPanelLayoutImplJB() {
-            try {
-                mGetDisplayList = View.class.getDeclaredMethod("getDisplayList", (Class[]) null);
-            } catch (NoSuchMethodException e) {
-                Log.e(TAG, "Couldn't fetch getDisplayList method; dimming won't work right.", e);
-            }
-            try {
-                mRecreateDisplayList = View.class.getDeclaredField("mRecreateDisplayList");
-                mRecreateDisplayList.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "Couldn't fetch mRecreateDisplayList field; dimming will be slow.", e);
-            }
-        }
-
-        @Override
-        public void invalidateChildRegion(SlidingPaneLayout parent, View child) {
-            if (mGetDisplayList != null && mRecreateDisplayList != null) {
-                try {
-                    mRecreateDisplayList.setBoolean(child, true);
-                    mGetDisplayList.invoke(child, (Object[]) null);
-                } catch (Exception e) {
-                    Log.e(TAG, "Error refreshing display list state", e);
-                }
-            } else {
-                // Slow path. REALLY slow path. Let's hope we don't get here.
-                child.invalidate();
-                return;
-            }
-            super.invalidateChildRegion(parent, child);
-        }
-    }
-
-    @RequiresApi(17)
-    static class SlidingPanelLayoutImplJBMR1 extends SlidingPanelLayoutImplBase {
-        @Override
-        public void invalidateChildRegion(SlidingPaneLayout parent, View child) {
-            ViewCompat.setLayerPaint(child, ((LayoutParams) child.getLayoutParams()).dimPaint);
-        }
-    }
-
-    class AccessibilityDelegate extends AccessibilityDelegateCompat {
-        private final Rect mTmpRect = new Rect();
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-            final AccessibilityNodeInfoCompat superNode = AccessibilityNodeInfoCompat.obtain(info);
-            super.onInitializeAccessibilityNodeInfo(host, superNode);
-            copyNodeInfoNoChildren(info, superNode);
-            superNode.recycle();
-
-            info.setClassName(SlidingPaneLayout.class.getName());
-            info.setSource(host);
-
-            final ViewParent parent = ViewCompat.getParentForAccessibility(host);
-            if (parent instanceof View) {
-                info.setParent((View) parent);
-            }
-
-            // This is a best-approximation of addChildrenForAccessibility()
-            // that accounts for filtering.
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                if (!filter(child) && (child.getVisibility() == View.VISIBLE)) {
-                    // Force importance to "yes" since we can't read the value.
-                    ViewCompat.setImportantForAccessibility(
-                            child, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-                    info.addChild(child);
-                }
-            }
-        }
-
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-
-            event.setClassName(SlidingPaneLayout.class.getName());
-        }
-
-        @Override
-        public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-                AccessibilityEvent event) {
-            if (!filter(child)) {
-                return super.onRequestSendAccessibilityEvent(host, child, event);
-            }
-            return false;
-        }
-
-        public boolean filter(View child) {
-            return isDimmed(child);
-        }
-
-        /**
-         * This should really be in AccessibilityNodeInfoCompat, but there unfortunately
-         * seem to be a few elements that are not easily cloneable using the underlying API.
-         * Leave it private here as it's not general-purpose useful.
-         */
-        private void copyNodeInfoNoChildren(AccessibilityNodeInfoCompat dest,
-                AccessibilityNodeInfoCompat src) {
-            final Rect rect = mTmpRect;
-
-            src.getBoundsInParent(rect);
-            dest.setBoundsInParent(rect);
-
-            src.getBoundsInScreen(rect);
-            dest.setBoundsInScreen(rect);
-
-            dest.setVisibleToUser(src.isVisibleToUser());
-            dest.setPackageName(src.getPackageName());
-            dest.setClassName(src.getClassName());
-            dest.setContentDescription(src.getContentDescription());
-
-            dest.setEnabled(src.isEnabled());
-            dest.setClickable(src.isClickable());
-            dest.setFocusable(src.isFocusable());
-            dest.setFocused(src.isFocused());
-            dest.setAccessibilityFocused(src.isAccessibilityFocused());
-            dest.setSelected(src.isSelected());
-            dest.setLongClickable(src.isLongClickable());
-
-            dest.addAction(src.getActions());
-
-            dest.setMovementGranularities(src.getMovementGranularities());
-        }
-    }
-
-    private class DisableLayerRunnable implements Runnable {
-        final View mChildView;
-
-        DisableLayerRunnable(View childView) {
-            mChildView = childView;
-        }
-
-        @Override
-        public void run() {
-            if (mChildView.getParent() == SlidingPaneLayout.this) {
-                mChildView.setLayerType(View.LAYER_TYPE_NONE, null);
-                invalidateChildRegion(mChildView);
-            }
-            mPostedRunnables.remove(this);
-        }
-    }
-
-    boolean isLayoutRtlSupport() {
-        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
-    }
-}
diff --git a/android/support/v4/widget/Space.java b/android/support/v4/widget/Space.java
deleted file mode 100644
index 7d37a72..0000000
--- a/android/support/v4/widget/Space.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.widget;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Space is a lightweight {@link View} subclass that may be used to create gaps between components
- * in general purpose layouts.
- */
-public class Space extends View {
-
-    public Space(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        if (getVisibility() == VISIBLE) {
-            setVisibility(INVISIBLE);
-        }
-    }
-
-    public Space(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public Space(@NonNull Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Draw nothing.
-     *
-     * @param canvas an unused parameter.
-     */
-    @Override
-    @SuppressLint("MissingSuperCall")
-    public void draw(Canvas canvas) {
-    }
-
-    /**
-     * Compare to: {@link View#getDefaultSize(int, int)}
-     * If mode is AT_MOST, return the child size instead of the parent size
-     * (unless it is too big).
-     */
-    private static int getDefaultSize2(int size, int measureSpec) {
-        int result = size;
-        int specMode = MeasureSpec.getMode(measureSpec);
-        int specSize = MeasureSpec.getSize(measureSpec);
-
-        switch (specMode) {
-            case MeasureSpec.UNSPECIFIED:
-                result = size;
-                break;
-            case MeasureSpec.AT_MOST:
-                result = Math.min(size, specSize);
-                break;
-            case MeasureSpec.EXACTLY:
-                result = specSize;
-                break;
-        }
-        return result;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(
-                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
-                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
-    }
-}
\ No newline at end of file
diff --git a/android/support/v4/widget/SwipeProgressBar.java b/android/support/v4/widget/SwipeProgressBar.java
deleted file mode 100644
index 6861179..0000000
--- a/android/support/v4/widget/SwipeProgressBar.java
+++ /dev/null
@@ -1,276 +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.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-
-/**
- * Custom progress bar that shows a cycle of colors as widening circles that
- * overdraw each other. When finished, the bar is cleared from the inside out as
- * the main cycle continues. Before running, this can also indicate how close
- * the user is to triggering something (e.g. how far they need to pull down to
- * trigger a refresh).
- */
-final class SwipeProgressBar {
-
-    // Default progress animation colors are grays.
-    private static final int COLOR1 = 0xB3000000;
-    private static final int COLOR2 = 0x80000000;
-    private static final int COLOR3 = 0x4d000000;
-    private static final int COLOR4 = 0x1a000000;
-
-    // The duration of the animation cycle.
-    private static final int ANIMATION_DURATION_MS = 2000;
-
-    // The duration of the animation to clear the bar.
-    private static final int FINISH_ANIMATION_DURATION_MS = 1000;
-
-    // Interpolator for varying the speed of the animation.
-    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
-
-    private final Paint mPaint = new Paint();
-    private final RectF mClipRect = new RectF();
-    private float mTriggerPercentage;
-    private long mStartTime;
-    private long mFinishTime;
-    private boolean mRunning;
-
-    // Colors used when rendering the animation,
-    private int mColor1;
-    private int mColor2;
-    private int mColor3;
-    private int mColor4;
-    private View mParent;
-
-    private Rect mBounds = new Rect();
-
-    SwipeProgressBar(View parent) {
-        mParent = parent;
-        mColor1 = COLOR1;
-        mColor2 = COLOR2;
-        mColor3 = COLOR3;
-        mColor4 = COLOR4;
-    }
-
-    /**
-     * Set the four colors used in the progress animation. The first color will
-     * also be the color of the bar that grows in response to a user swipe
-     * gesture.
-     *
-     * @param color1 Integer representation of a color.
-     * @param color2 Integer representation of a color.
-     * @param color3 Integer representation of a color.
-     * @param color4 Integer representation of a color.
-     */
-    void setColorScheme(int color1, int color2, int color3, int color4) {
-        mColor1 = color1;
-        mColor2 = color2;
-        mColor3 = color3;
-        mColor4 = color4;
-    }
-
-    /**
-     * Update the progress the user has made toward triggering the swipe
-     * gesture. and use this value to update the percentage of the trigger that
-     * is shown.
-     */
-    void setTriggerPercentage(float triggerPercentage) {
-        mTriggerPercentage = triggerPercentage;
-        mStartTime = 0;
-        ViewCompat.postInvalidateOnAnimation(
-                mParent, mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
-    }
-
-    /**
-     * Start showing the progress animation.
-     */
-    void start() {
-        if (!mRunning) {
-            mTriggerPercentage = 0;
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mRunning = true;
-            mParent.postInvalidate();
-        }
-    }
-
-    /**
-     * Stop showing the progress animation.
-     */
-    void stop() {
-        if (mRunning) {
-            mTriggerPercentage = 0;
-            mFinishTime = AnimationUtils.currentAnimationTimeMillis();
-            mRunning = false;
-            mParent.postInvalidate();
-        }
-    }
-
-    /**
-     * @return Return whether the progress animation is currently running.
-     */
-    boolean isRunning() {
-        return mRunning || mFinishTime > 0;
-    }
-
-    void draw(Canvas canvas) {
-        final int width = mBounds.width();
-        final int height = mBounds.height();
-        final int cx = width / 2;
-        final int cy = height / 2;
-        boolean drawTriggerWhileFinishing = false;
-        int restoreCount = canvas.save();
-        canvas.clipRect(mBounds);
-
-        if (mRunning || (mFinishTime > 0)) {
-            long now = AnimationUtils.currentAnimationTimeMillis();
-            long elapsed = (now - mStartTime) % ANIMATION_DURATION_MS;
-            long iterations = (now - mStartTime) / ANIMATION_DURATION_MS;
-            float rawProgress = (elapsed / (ANIMATION_DURATION_MS / 100f));
-
-            // If we're not running anymore, that means we're running through
-            // the finish animation.
-            if (!mRunning) {
-                // If the finish animation is done, don't draw anything, and
-                // don't repost.
-                if ((now - mFinishTime) >= FINISH_ANIMATION_DURATION_MS) {
-                    mFinishTime = 0;
-                    return;
-                }
-
-                // Otherwise, use a 0 opacity alpha layer to clear the animation
-                // from the inside out. This layer will prevent the circles from
-                // drawing within its bounds.
-                long finishElapsed = (now - mFinishTime) % FINISH_ANIMATION_DURATION_MS;
-                float finishProgress = (finishElapsed / (FINISH_ANIMATION_DURATION_MS / 100f));
-                float pct = (finishProgress / 100f);
-                // Radius of the circle is half of the screen.
-                float clearRadius = width / 2 * INTERPOLATOR.getInterpolation(pct);
-                mClipRect.set(cx - clearRadius, 0, cx + clearRadius, height);
-                canvas.saveLayerAlpha(mClipRect, 0, 0);
-                // Only draw the trigger if there is a space in the center of
-                // this refreshing view that needs to be filled in by the
-                // trigger. If the progress view is just still animating, let it
-                // continue animating.
-                drawTriggerWhileFinishing = true;
-            }
-
-            // First fill in with the last color that would have finished drawing.
-            if (iterations == 0) {
-                canvas.drawColor(mColor1);
-            } else {
-                if (rawProgress >= 0 && rawProgress < 25) {
-                    canvas.drawColor(mColor4);
-                } else if (rawProgress >= 25 && rawProgress < 50) {
-                    canvas.drawColor(mColor1);
-                } else if (rawProgress >= 50 && rawProgress < 75) {
-                    canvas.drawColor(mColor2);
-                } else {
-                    canvas.drawColor(mColor3);
-                }
-            }
-
-            // Then draw up to 4 overlapping concentric circles of varying radii, based on how far
-            // along we are in the cycle.
-            // progress 0-50 draw mColor2
-            // progress 25-75 draw mColor3
-            // progress 50-100 draw mColor4
-            // progress 75 (wrap to 25) draw mColor1
-            if ((rawProgress >= 0 && rawProgress <= 25)) {
-                float pct = (((rawProgress + 25) * 2) / 100f);
-                drawCircle(canvas, cx, cy, mColor1, pct);
-            }
-            if (rawProgress >= 0 && rawProgress <= 50) {
-                float pct = ((rawProgress * 2) / 100f);
-                drawCircle(canvas, cx, cy, mColor2, pct);
-            }
-            if (rawProgress >= 25 && rawProgress <= 75) {
-                float pct = (((rawProgress - 25) * 2) / 100f);
-                drawCircle(canvas, cx, cy, mColor3, pct);
-            }
-            if (rawProgress >= 50 && rawProgress <= 100) {
-                float pct = (((rawProgress - 50) * 2) / 100f);
-                drawCircle(canvas, cx, cy, mColor4, pct);
-            }
-            if ((rawProgress >= 75 && rawProgress <= 100)) {
-                float pct = (((rawProgress - 75) * 2) / 100f);
-                drawCircle(canvas, cx, cy, mColor1, pct);
-            }
-            if (mTriggerPercentage > 0 && drawTriggerWhileFinishing) {
-                // There is some portion of trigger to draw. Restore the canvas,
-                // then draw the trigger. Otherwise, the trigger does not appear
-                // until after the bar has finished animating and appears to
-                // just jump in at a larger width than expected.
-                canvas.restoreToCount(restoreCount);
-                restoreCount = canvas.save();
-                canvas.clipRect(mBounds);
-                drawTrigger(canvas, cx, cy);
-            }
-            // Keep running until we finish out the last cycle.
-            ViewCompat.postInvalidateOnAnimation(
-                    mParent, mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
-        } else {
-            // Otherwise if we're in the middle of a trigger, draw that.
-            if (mTriggerPercentage > 0 && mTriggerPercentage <= 1.0) {
-                drawTrigger(canvas, cx, cy);
-            }
-        }
-        canvas.restoreToCount(restoreCount);
-    }
-
-    private void drawTrigger(Canvas canvas, int cx, int cy) {
-        mPaint.setColor(mColor1);
-        canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
-    }
-
-    /**
-     * Draws a circle centered in the view.
-     *
-     * @param canvas the canvas to draw on
-     * @param cx the center x coordinate
-     * @param cy the center y coordinate
-     * @param color the color to draw
-     * @param pct the percentage of the view that the circle should cover
-     */
-    private void drawCircle(Canvas canvas, float cx, float cy, int color, float pct) {
-        mPaint.setColor(color);
-        canvas.save();
-        canvas.translate(cx, cy);
-        float radiusScale = INTERPOLATOR.getInterpolation(pct);
-        canvas.scale(radiusScale, radiusScale);
-        canvas.drawCircle(0, 0, cx, mPaint);
-        canvas.restore();
-    }
-
-    /**
-     * Set the drawing bounds of this SwipeProgressBar.
-     */
-    void setBounds(int left, int top, int right, int bottom) {
-        mBounds.left = left;
-        mBounds.top = top;
-        mBounds.right = right;
-        mBounds.bottom = bottom;
-    }
-}
diff --git a/android/support/v4/widget/SwipeRefreshLayout.java b/android/support/v4/widget/SwipeRefreshLayout.java
deleted file mode 100644
index ca04e46..0000000
--- a/android/support/v4/widget/SwipeRefreshLayout.java
+++ /dev/null
@@ -1,1195 +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.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.ColorInt;
-import android.support.annotation.ColorRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.NestedScrollingChild;
-import android.support.v4.view.NestedScrollingChildHelper;
-import android.support.v4.view.NestedScrollingParent;
-import android.support.v4.view.NestedScrollingParentHelper;
-import android.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.animation.Animation;
-import android.view.animation.Animation.AnimationListener;
-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
- * contents of a view via a vertical swipe gesture. The activity that
- * instantiates this view should add an OnRefreshListener to be notified
- * whenever the swipe to refresh gesture is completed. The SwipeRefreshLayout
- * will notify the listener each and every time the gesture is completed again;
- * the listener is responsible for correctly determining when to actually
- * initiate a refresh of its content. If the listener determines there should
- * not be a refresh, it must call setRefreshing(false) to cancel any visual
- * indication of a refresh. If an activity wishes to show just the progress
- * animation, it should call setRefreshing(true). To disable the gesture and
- * progress animation, call setEnabled(false) on the view.
- * <p>
- * This layout should be made the parent of the view that will be refreshed as a
- * result of the gesture and can only support one direct child. This view will
- * also be made the target of the gesture and will be forced to match both the
- * width and the height supplied in this layout. The SwipeRefreshLayout does not
- * provide accessibility events; instead, a menu item must be provided to allow
- * refresh of the content wherever this gesture is used.
- * </p>
- */
-public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingParent,
-        NestedScrollingChild {
-    // Maps to ProgressBar.Large style
-    public static final int LARGE = CircularProgressDrawable.LARGE;
-    // Maps to ProgressBar default style
-    public static final int DEFAULT = CircularProgressDrawable.DEFAULT;
-
-    @VisibleForTesting
-    static final int CIRCLE_DIAMETER = 40;
-    @VisibleForTesting
-    static final int CIRCLE_DIAMETER_LARGE = 56;
-
-    private static final String LOG_TAG = SwipeRefreshLayout.class.getSimpleName();
-
-    private static final int MAX_ALPHA = 255;
-    private static final int STARTING_PROGRESS_ALPHA = (int) (.3f * MAX_ALPHA);
-
-    private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
-    private static final int INVALID_POINTER = -1;
-    private static final float DRAG_RATE = .5f;
-
-    // Max amount of circle that can be filled by progress during swipe gesture,
-    // where 1.0 is a full circle
-    private static final float MAX_PROGRESS_ANGLE = .8f;
-
-    private static final int SCALE_DOWN_DURATION = 150;
-
-    private static final int ALPHA_ANIMATION_DURATION = 300;
-
-    private static final int ANIMATE_TO_TRIGGER_DURATION = 200;
-
-    private static final int ANIMATE_TO_START_DURATION = 200;
-
-    // Default background for the progress spinner
-    private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA;
-    // Default offset in dips from the top of the view to where the progress spinner should stop
-    private static final int DEFAULT_CIRCLE_TARGET = 64;
-
-    private View mTarget; // the target of the gesture
-    OnRefreshListener mListener;
-    boolean mRefreshing = false;
-    private int mTouchSlop;
-    private float mTotalDragDistance = -1;
-
-    // If nested scrolling is enabled, the total amount that needed to be
-    // consumed by this as the nested scrolling parent is used in place of the
-    // overscroll determined by MOVE events in the onTouch handler
-    private float mTotalUnconsumed;
-    private final NestedScrollingParentHelper mNestedScrollingParentHelper;
-    private final NestedScrollingChildHelper mNestedScrollingChildHelper;
-    private final int[] mParentScrollConsumed = new int[2];
-    private final int[] mParentOffsetInWindow = new int[2];
-    private boolean mNestedScrollInProgress;
-
-    private int mMediumAnimationDuration;
-    int mCurrentTargetOffsetTop;
-
-    private float mInitialMotionY;
-    private float mInitialDownY;
-    private boolean mIsBeingDragged;
-    private int mActivePointerId = INVALID_POINTER;
-    // Whether this item is scaled up rather than clipped
-    boolean mScale;
-
-    // Target is returning to its start offset because it was cancelled or a
-    // refresh was triggered.
-    private boolean mReturningToStart;
-    private final DecelerateInterpolator mDecelerateInterpolator;
-    private static final int[] LAYOUT_ATTRS = new int[] {
-        android.R.attr.enabled
-    };
-
-    CircleImageView mCircleView;
-    private int mCircleViewIndex = -1;
-
-    protected int mFrom;
-
-    float mStartingScale;
-
-    protected int mOriginalOffsetTop;
-
-    int mSpinnerOffsetEnd;
-
-    CircularProgressDrawable mProgress;
-
-    private Animation mScaleAnimation;
-
-    private Animation mScaleDownAnimation;
-
-    private Animation mAlphaStartAnimation;
-
-    private Animation mAlphaMaxAnimation;
-
-    private Animation mScaleDownToStartAnimation;
-
-    boolean mNotify;
-
-    private int mCircleDiameter;
-
-    // Whether the client has set a custom starting position;
-    boolean mUsingCustomStart;
-
-    private OnChildScrollUpCallback mChildScrollUpCallback;
-
-    private Animation.AnimationListener mRefreshListener = new Animation.AnimationListener() {
-        @Override
-        public void onAnimationStart(Animation animation) {
-        }
-
-        @Override
-        public void onAnimationRepeat(Animation animation) {
-        }
-
-        @Override
-        public void onAnimationEnd(Animation animation) {
-            if (mRefreshing) {
-                // Make sure the progress view is fully visible
-                mProgress.setAlpha(MAX_ALPHA);
-                mProgress.start();
-                if (mNotify) {
-                    if (mListener != null) {
-                        mListener.onRefresh();
-                    }
-                }
-                mCurrentTargetOffsetTop = mCircleView.getTop();
-            } else {
-                reset();
-            }
-        }
-    };
-
-    void reset() {
-        mCircleView.clearAnimation();
-        mProgress.stop();
-        mCircleView.setVisibility(View.GONE);
-        setColorViewAlpha(MAX_ALPHA);
-        // Return the circle to its start position
-        if (mScale) {
-            setAnimationProgress(0 /* animation complete and view is hidden */);
-        } else {
-            setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop);
-        }
-        mCurrentTargetOffsetTop = mCircleView.getTop();
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled);
-        if (!enabled) {
-            reset();
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        reset();
-    }
-
-    private void setColorViewAlpha(int targetAlpha) {
-        mCircleView.getBackground().setAlpha(targetAlpha);
-        mProgress.setAlpha(targetAlpha);
-    }
-
-    /**
-     * The refresh indicator starting and resting position is always positioned
-     * near the top of the refreshing content. This position is a consistent
-     * location, but can be adjusted in either direction based on whether or not
-     * there is a toolbar or actionbar present.
-     * <p>
-     * <strong>Note:</strong> Calling this will reset the position of the refresh indicator to
-     * <code>start</code>.
-     * </p>
-     *
-     * @param scale Set to true if there is no view at a higher z-order than where the progress
-     *              spinner is set to appear. Setting it to true will cause indicator to be scaled
-     *              up rather than clipped.
-     * @param start The offset in pixels from the top of this view at which the
-     *              progress spinner should appear.
-     * @param end The offset in pixels from the top of this view at which the
-     *            progress spinner should come to rest after a successful swipe
-     *            gesture.
-     */
-    public void setProgressViewOffset(boolean scale, int start, int end) {
-        mScale = scale;
-        mOriginalOffsetTop = start;
-        mSpinnerOffsetEnd = end;
-        mUsingCustomStart = true;
-        reset();
-        mRefreshing = false;
-    }
-
-    /**
-     * @return The offset in pixels from the top of this view at which the progress spinner should
-     *         appear.
-     */
-    public int getProgressViewStartOffset() {
-        return mOriginalOffsetTop;
-    }
-
-    /**
-     * @return The offset in pixels from the top of this view at which the progress spinner should
-     *         come to rest after a successful swipe gesture.
-     */
-    public int getProgressViewEndOffset() {
-        return mSpinnerOffsetEnd;
-    }
-
-    /**
-     * The refresh indicator resting position is always positioned near the top
-     * of the refreshing content. This position is a consistent location, but
-     * can be adjusted in either direction based on whether or not there is a
-     * toolbar or actionbar present.
-     *
-     * @param scale Set to true if there is no view at a higher z-order than where the progress
-     *              spinner is set to appear. Setting it to true will cause indicator to be scaled
-     *              up rather than clipped.
-     * @param end The offset in pixels from the top of this view at which the
-     *            progress spinner should come to rest after a successful swipe
-     *            gesture.
-     */
-    public void setProgressViewEndTarget(boolean scale, int end) {
-        mSpinnerOffsetEnd = end;
-        mScale = scale;
-        mCircleView.invalidate();
-    }
-
-    /**
-     * One of DEFAULT, or LARGE.
-     */
-    public void setSize(int size) {
-        if (size != CircularProgressDrawable.LARGE && size != CircularProgressDrawable.DEFAULT) {
-            return;
-        }
-        final DisplayMetrics metrics = getResources().getDisplayMetrics();
-        if (size == CircularProgressDrawable.LARGE) {
-            mCircleDiameter = (int) (CIRCLE_DIAMETER_LARGE * metrics.density);
-        } else {
-            mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);
-        }
-        // force the bounds of the progress circle inside the circle view to
-        // update by setting it to null before updating its size and then
-        // re-setting it
-        mCircleView.setImageDrawable(null);
-        mProgress.setStyle(size);
-        mCircleView.setImageDrawable(mProgress);
-    }
-
-    /**
-     * Simple constructor to use when creating a SwipeRefreshLayout from code.
-     *
-     * @param context
-     */
-    public SwipeRefreshLayout(@NonNull Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Constructor that is called when inflating SwipeRefreshLayout from XML.
-     *
-     * @param context
-     * @param attrs
-     */
-    public SwipeRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-
-        mMediumAnimationDuration = getResources().getInteger(
-                android.R.integer.config_mediumAnimTime);
-
-        setWillNotDraw(false);
-        mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
-
-        final DisplayMetrics metrics = getResources().getDisplayMetrics();
-        mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);
-
-        createProgressView();
-        setChildrenDrawingOrderEnabled(true);
-        // the absolute offset has to take into account that the circle starts at an offset
-        mSpinnerOffsetEnd = (int) (DEFAULT_CIRCLE_TARGET * metrics.density);
-        mTotalDragDistance = mSpinnerOffsetEnd;
-        mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
-
-        mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
-        setNestedScrollingEnabled(true);
-
-        mOriginalOffsetTop = mCurrentTargetOffsetTop = -mCircleDiameter;
-        moveToStart(1.0f);
-
-        final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
-        setEnabled(a.getBoolean(0, true));
-        a.recycle();
-    }
-
-    @Override
-    protected int getChildDrawingOrder(int childCount, int i) {
-        if (mCircleViewIndex < 0) {
-            return i;
-        } else if (i == childCount - 1) {
-            // Draw the selected child last
-            return mCircleViewIndex;
-        } else if (i >= mCircleViewIndex) {
-            // Move the children after the selected child earlier one
-            return i + 1;
-        } else {
-            // Keep the children before the selected child the same
-            return i;
-        }
-    }
-
-    private void createProgressView() {
-        mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT);
-        mProgress = new CircularProgressDrawable(getContext());
-        mProgress.setStyle(CircularProgressDrawable.DEFAULT);
-        mCircleView.setImageDrawable(mProgress);
-        mCircleView.setVisibility(View.GONE);
-        addView(mCircleView);
-    }
-
-    /**
-     * Set the listener to be notified when a refresh is triggered via the swipe
-     * gesture.
-     */
-    public void setOnRefreshListener(@Nullable OnRefreshListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Notify the widget that refresh state has changed. Do not call this when
-     * refresh is triggered by a swipe gesture.
-     *
-     * @param refreshing Whether or not the view should show refresh progress.
-     */
-    public void setRefreshing(boolean refreshing) {
-        if (refreshing && mRefreshing != refreshing) {
-            // scale and show
-            mRefreshing = refreshing;
-            int endTarget = 0;
-            if (!mUsingCustomStart) {
-                endTarget = mSpinnerOffsetEnd + mOriginalOffsetTop;
-            } else {
-                endTarget = mSpinnerOffsetEnd;
-            }
-            setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop);
-            mNotify = false;
-            startScaleUpAnimation(mRefreshListener);
-        } else {
-            setRefreshing(refreshing, false /* notify */);
-        }
-    }
-
-    private void startScaleUpAnimation(AnimationListener listener) {
-        mCircleView.setVisibility(View.VISIBLE);
-        if (android.os.Build.VERSION.SDK_INT >= 11) {
-            // Pre API 11, alpha is used in place of scale up to show the
-            // progress circle appearing.
-            // Don't adjust the alpha during appearance otherwise.
-            mProgress.setAlpha(MAX_ALPHA);
-        }
-        mScaleAnimation = new Animation() {
-            @Override
-            public void applyTransformation(float interpolatedTime, Transformation t) {
-                setAnimationProgress(interpolatedTime);
-            }
-        };
-        mScaleAnimation.setDuration(mMediumAnimationDuration);
-        if (listener != null) {
-            mCircleView.setAnimationListener(listener);
-        }
-        mCircleView.clearAnimation();
-        mCircleView.startAnimation(mScaleAnimation);
-    }
-
-    /**
-     * Pre API 11, this does an alpha animation.
-     * @param progress
-     */
-    void setAnimationProgress(float progress) {
-        mCircleView.setScaleX(progress);
-        mCircleView.setScaleY(progress);
-    }
-
-    private void setRefreshing(boolean refreshing, final boolean notify) {
-        if (mRefreshing != refreshing) {
-            mNotify = notify;
-            ensureTarget();
-            mRefreshing = refreshing;
-            if (mRefreshing) {
-                animateOffsetToCorrectPosition(mCurrentTargetOffsetTop, mRefreshListener);
-            } else {
-                startScaleDownAnimation(mRefreshListener);
-            }
-        }
-    }
-
-    void startScaleDownAnimation(Animation.AnimationListener listener) {
-        mScaleDownAnimation = new Animation() {
-            @Override
-            public void applyTransformation(float interpolatedTime, Transformation t) {
-                setAnimationProgress(1 - interpolatedTime);
-            }
-        };
-        mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION);
-        mCircleView.setAnimationListener(listener);
-        mCircleView.clearAnimation();
-        mCircleView.startAnimation(mScaleDownAnimation);
-    }
-
-    private void startProgressAlphaStartAnimation() {
-        mAlphaStartAnimation = startAlphaAnimation(mProgress.getAlpha(), STARTING_PROGRESS_ALPHA);
-    }
-
-    private void startProgressAlphaMaxAnimation() {
-        mAlphaMaxAnimation = startAlphaAnimation(mProgress.getAlpha(), MAX_ALPHA);
-    }
-
-    private Animation startAlphaAnimation(final int startingAlpha, final int endingAlpha) {
-        Animation alpha = new Animation() {
-            @Override
-            public void applyTransformation(float interpolatedTime, Transformation t) {
-                mProgress.setAlpha(
-                        (int) (startingAlpha + ((endingAlpha - startingAlpha) * interpolatedTime)));
-            }
-        };
-        alpha.setDuration(ALPHA_ANIMATION_DURATION);
-        // Clear out the previous animation listeners.
-        mCircleView.setAnimationListener(null);
-        mCircleView.clearAnimation();
-        mCircleView.startAnimation(alpha);
-        return alpha;
-    }
-
-    /**
-     * @deprecated Use {@link #setProgressBackgroundColorSchemeResource(int)}
-     */
-    @Deprecated
-    public void setProgressBackgroundColor(int colorRes) {
-        setProgressBackgroundColorSchemeResource(colorRes);
-    }
-
-    /**
-     * Set the background color of the progress spinner disc.
-     *
-     * @param colorRes Resource id of the color.
-     */
-    public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) {
-        setProgressBackgroundColorSchemeColor(ContextCompat.getColor(getContext(), colorRes));
-    }
-
-    /**
-     * Set the background color of the progress spinner disc.
-     *
-     * @param color
-     */
-    public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
-        mCircleView.setBackgroundColor(color);
-    }
-
-    /**
-     * @deprecated Use {@link #setColorSchemeResources(int...)}
-     */
-    @Deprecated
-    public void setColorScheme(@ColorRes int... colors) {
-        setColorSchemeResources(colors);
-    }
-
-    /**
-     * Set the color resources 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 colorResIds
-     */
-    public void setColorSchemeResources(@ColorRes int... colorResIds) {
-        final Context context = getContext();
-        int[] colorRes = new int[colorResIds.length];
-        for (int i = 0; i < colorResIds.length; i++) {
-            colorRes[i] = ContextCompat.getColor(context, colorResIds[i]);
-        }
-        setColorSchemeColors(colorRes);
-    }
-
-    /**
-     * Set the colors used in the progress animation. 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(@ColorInt int... colors) {
-        ensureTarget();
-        mProgress.setColorSchemeColors(colors);
-    }
-
-    /**
-     * @return Whether the SwipeRefreshWidget is actively showing refresh
-     *         progress.
-     */
-    public boolean isRefreshing() {
-        return mRefreshing;
-    }
-
-    private void ensureTarget() {
-        // Don't bother getting the parent height if the parent hasn't been laid
-        // out yet.
-        if (mTarget == null) {
-            for (int i = 0; i < getChildCount(); i++) {
-                View child = getChildAt(i);
-                if (!child.equals(mCircleView)) {
-                    mTarget = child;
-                    break;
-                }
-            }
-        }
-    }
-
-    /**
-     * Set the distance to trigger a sync in dips
-     *
-     * @param distance
-     */
-    public void setDistanceToTriggerSync(int distance) {
-        mTotalDragDistance = distance;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final int width = getMeasuredWidth();
-        final int height = getMeasuredHeight();
-        if (getChildCount() == 0) {
-            return;
-        }
-        if (mTarget == null) {
-            ensureTarget();
-        }
-        if (mTarget == null) {
-            return;
-        }
-        final View child = mTarget;
-        final int childLeft = getPaddingLeft();
-        final int childTop = getPaddingTop();
-        final int childWidth = width - getPaddingLeft() - getPaddingRight();
-        final int childHeight = height - getPaddingTop() - getPaddingBottom();
-        child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
-        int circleWidth = mCircleView.getMeasuredWidth();
-        int circleHeight = mCircleView.getMeasuredHeight();
-        mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop,
-                (width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight);
-    }
-
-    @Override
-    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        if (mTarget == null) {
-            ensureTarget();
-        }
-        if (mTarget == null) {
-            return;
-        }
-        mTarget.measure(MeasureSpec.makeMeasureSpec(
-                getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
-                MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
-                getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
-        mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY));
-        mCircleViewIndex = -1;
-        // Get the index of the circleview.
-        for (int index = 0; index < getChildCount(); index++) {
-            if (getChildAt(index) == mCircleView) {
-                mCircleViewIndex = index;
-                break;
-            }
-        }
-    }
-
-    /**
-     * Get the diameter of the progress circle that is displayed as part of the
-     * swipe to refresh layout.
-     *
-     * @return Diameter in pixels of the progress circle view.
-     */
-    public int getProgressCircleDiameter() {
-        return mCircleDiameter;
-    }
-
-    /**
-     * @return Whether it is possible for the child view of this layout to
-     *         scroll up. Override this if the child view is a custom view.
-     */
-    public boolean canChildScrollUp() {
-        if (mChildScrollUpCallback != null) {
-            return mChildScrollUpCallback.canChildScrollUp(this, mTarget);
-        }
-        if (mTarget instanceof ListView) {
-            return ListViewCompat.canScrollList((ListView) mTarget, -1);
-        }
-        return mTarget.canScrollVertically(-1);
-    }
-
-    /**
-     * Set a callback to override {@link SwipeRefreshLayout#canChildScrollUp()} method. Non-null
-     * callback will return the value provided by the callback and ignore all internal logic.
-     * @param callback Callback that should be called when canChildScrollUp() is called.
-     */
-    public void setOnChildScrollUpCallback(@Nullable OnChildScrollUpCallback callback) {
-        mChildScrollUpCallback = callback;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        ensureTarget();
-
-        final int action = ev.getActionMasked();
-        int pointerIndex;
-
-        if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
-            mReturningToStart = false;
-        }
-
-        if (!isEnabled() || mReturningToStart || canChildScrollUp()
-                || mRefreshing || mNestedScrollInProgress) {
-            // Fail fast if we're not in a state where a swipe is possible
-            return false;
-        }
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop());
-                mActivePointerId = ev.getPointerId(0);
-                mIsBeingDragged = false;
-
-                pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex < 0) {
-                    return false;
-                }
-                mInitialDownY = ev.getY(pointerIndex);
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (mActivePointerId == INVALID_POINTER) {
-                    Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
-                    return false;
-                }
-
-                pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex < 0) {
-                    return false;
-                }
-                final float y = ev.getY(pointerIndex);
-                startDragging(y);
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mIsBeingDragged = false;
-                mActivePointerId = INVALID_POINTER;
-                break;
-        }
-
-        return mIsBeingDragged;
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean b) {
-        // if this is a List < L or another view that doesn't support nested
-        // scrolling, ignore this request so that the vertical scroll event
-        // isn't stolen
-        if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
-                || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
-            // Nope.
-        } else {
-            super.requestDisallowInterceptTouchEvent(b);
-        }
-    }
-
-    // NestedScrollingParent
-
-    @Override
-    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
-        return isEnabled() && !mReturningToStart && !mRefreshing
-                && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
-    }
-
-    @Override
-    public void onNestedScrollAccepted(View child, View target, int axes) {
-        // Reset the counter of how much leftover scroll needs to be consumed.
-        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
-        // Dispatch up to the nested parent
-        startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);
-        mTotalUnconsumed = 0;
-        mNestedScrollInProgress = true;
-    }
-
-    @Override
-    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
-        // If we are in the middle of consuming, a scroll, then we want to move the spinner back up
-        // before allowing the list to scroll
-        if (dy > 0 && mTotalUnconsumed > 0) {
-            if (dy > mTotalUnconsumed) {
-                consumed[1] = dy - (int) mTotalUnconsumed;
-                mTotalUnconsumed = 0;
-            } else {
-                mTotalUnconsumed -= dy;
-                consumed[1] = dy;
-            }
-            moveSpinner(mTotalUnconsumed);
-        }
-
-        // If a client layout is using a custom start position for the circle
-        // view, they mean to hide it again before scrolling the child view
-        // If we get back to mTotalUnconsumed == 0 and there is more to go, hide
-        // the circle so it isn't exposed if its blocking content is moved
-        if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0
-                && Math.abs(dy - consumed[1]) > 0) {
-            mCircleView.setVisibility(View.GONE);
-        }
-
-        // Now let our nested parent consume the leftovers
-        final int[] parentConsumed = mParentScrollConsumed;
-        if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
-            consumed[0] += parentConsumed[0];
-            consumed[1] += parentConsumed[1];
-        }
-    }
-
-    @Override
-    public int getNestedScrollAxes() {
-        return mNestedScrollingParentHelper.getNestedScrollAxes();
-    }
-
-    @Override
-    public void onStopNestedScroll(View target) {
-        mNestedScrollingParentHelper.onStopNestedScroll(target);
-        mNestedScrollInProgress = false;
-        // Finish the spinner for nested scrolling if we ever consumed any
-        // unconsumed nested scroll
-        if (mTotalUnconsumed > 0) {
-            finishSpinner(mTotalUnconsumed);
-            mTotalUnconsumed = 0;
-        }
-        // Dispatch up our nested parent
-        stopNestedScroll();
-    }
-
-    @Override
-    public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed,
-            final int dxUnconsumed, final int dyUnconsumed) {
-        // Dispatch up to the nested parent first
-        dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
-                mParentOffsetInWindow);
-
-        // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are
-        // sometimes between two nested scrolling views, we need a way to be able to know when any
-        // nested scrolling parent has stopped handling events. We do that by using the
-        // 'offset in window 'functionality to see if we have been moved from the event.
-        // This is a decent indication of whether we should take over the event stream or not.
-        final int dy = dyUnconsumed + mParentOffsetInWindow[1];
-        if (dy < 0 && !canChildScrollUp()) {
-            mTotalUnconsumed += Math.abs(dy);
-            moveSpinner(mTotalUnconsumed);
-        }
-    }
-
-    // NestedScrollingChild
-
-    @Override
-    public void setNestedScrollingEnabled(boolean enabled) {
-        mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
-    }
-
-    @Override
-    public boolean isNestedScrollingEnabled() {
-        return mNestedScrollingChildHelper.isNestedScrollingEnabled();
-    }
-
-    @Override
-    public boolean startNestedScroll(int axes) {
-        return mNestedScrollingChildHelper.startNestedScroll(axes);
-    }
-
-    @Override
-    public void stopNestedScroll() {
-        mNestedScrollingChildHelper.stopNestedScroll();
-    }
-
-    @Override
-    public boolean hasNestedScrollingParent() {
-        return mNestedScrollingChildHelper.hasNestedScrollingParent();
-    }
-
-    @Override
-    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
-            int dyUnconsumed, int[] offsetInWindow) {
-        return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
-                dxUnconsumed, dyUnconsumed, offsetInWindow);
-    }
-
-    @Override
-    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
-        return mNestedScrollingChildHelper.dispatchNestedPreScroll(
-                dx, dy, consumed, offsetInWindow);
-    }
-
-    @Override
-    public boolean onNestedPreFling(View target, float velocityX,
-            float velocityY) {
-        return dispatchNestedPreFling(velocityX, velocityY);
-    }
-
-    @Override
-    public boolean onNestedFling(View target, float velocityX, float velocityY,
-            boolean consumed) {
-        return dispatchNestedFling(velocityX, velocityY, consumed);
-    }
-
-    @Override
-    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
-        return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
-    }
-
-    @Override
-    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
-        return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
-    }
-
-    private boolean isAnimationRunning(Animation animation) {
-        return animation != null && animation.hasStarted() && !animation.hasEnded();
-    }
-
-    private void moveSpinner(float overscrollTop) {
-        mProgress.setArrowEnabled(true);
-        float originalDragPercent = overscrollTop / mTotalDragDistance;
-
-        float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
-        float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;
-        float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
-        float slingshotDist = mUsingCustomStart ? mSpinnerOffsetEnd - mOriginalOffsetTop
-                : mSpinnerOffsetEnd;
-        float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2)
-                / slingshotDist);
-        float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
-                (tensionSlingshotPercent / 4), 2)) * 2f;
-        float extraMove = (slingshotDist) * tensionPercent * 2;
-
-        int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);
-        // where 1.0f is a full circle
-        if (mCircleView.getVisibility() != View.VISIBLE) {
-            mCircleView.setVisibility(View.VISIBLE);
-        }
-        if (!mScale) {
-            mCircleView.setScaleX(1f);
-            mCircleView.setScaleY(1f);
-        }
-
-        if (mScale) {
-            setAnimationProgress(Math.min(1f, overscrollTop / mTotalDragDistance));
-        }
-        if (overscrollTop < mTotalDragDistance) {
-            if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA
-                    && !isAnimationRunning(mAlphaStartAnimation)) {
-                // Animate the alpha
-                startProgressAlphaStartAnimation();
-            }
-        } else {
-            if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) {
-                // Animate the alpha
-                startProgressAlphaMaxAnimation();
-            }
-        }
-        float strokeStart = adjustedPercent * .8f;
-        mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));
-        mProgress.setArrowScale(Math.min(1f, adjustedPercent));
-
-        float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f;
-        mProgress.setProgressRotation(rotation);
-        setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop);
-    }
-
-    private void finishSpinner(float overscrollTop) {
-        if (overscrollTop > mTotalDragDistance) {
-            setRefreshing(true, true /* notify */);
-        } else {
-            // cancel refresh
-            mRefreshing = false;
-            mProgress.setStartEndTrim(0f, 0f);
-            Animation.AnimationListener listener = null;
-            if (!mScale) {
-                listener = new Animation.AnimationListener() {
-
-                    @Override
-                    public void onAnimationStart(Animation animation) {
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animation animation) {
-                        if (!mScale) {
-                            startScaleDownAnimation(null);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationRepeat(Animation animation) {
-                    }
-
-                };
-            }
-            animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
-            mProgress.setArrowEnabled(false);
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        final int action = ev.getActionMasked();
-        int pointerIndex = -1;
-
-        if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
-            mReturningToStart = false;
-        }
-
-        if (!isEnabled() || mReturningToStart || canChildScrollUp()
-                || mRefreshing || mNestedScrollInProgress) {
-            // Fail fast if we're not in a state where a swipe is possible
-            return false;
-        }
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mActivePointerId = ev.getPointerId(0);
-                mIsBeingDragged = false;
-                break;
-
-            case MotionEvent.ACTION_MOVE: {
-                pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex < 0) {
-                    Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
-                    return false;
-                }
-
-                final float y = ev.getY(pointerIndex);
-                startDragging(y);
-
-                if (mIsBeingDragged) {
-                    final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
-                    if (overscrollTop > 0) {
-                        moveSpinner(overscrollTop);
-                    } else {
-                        return false;
-                    }
-                }
-                break;
-            }
-            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.");
-                    return false;
-                }
-                mActivePointerId = ev.getPointerId(pointerIndex);
-                break;
-            }
-
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                break;
-
-            case MotionEvent.ACTION_UP: {
-                pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex < 0) {
-                    Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
-                    return false;
-                }
-
-                if (mIsBeingDragged) {
-                    final float y = ev.getY(pointerIndex);
-                    final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
-                    mIsBeingDragged = false;
-                    finishSpinner(overscrollTop);
-                }
-                mActivePointerId = INVALID_POINTER;
-                return false;
-            }
-            case MotionEvent.ACTION_CANCEL:
-                return false;
-        }
-
-        return true;
-    }
-
-    private void startDragging(float y) {
-        final float yDiff = y - mInitialDownY;
-        if (yDiff > mTouchSlop && !mIsBeingDragged) {
-            mInitialMotionY = mInitialDownY + mTouchSlop;
-            mIsBeingDragged = true;
-            mProgress.setAlpha(STARTING_PROGRESS_ALPHA);
-        }
-    }
-
-    private void animateOffsetToCorrectPosition(int from, AnimationListener listener) {
-        mFrom = from;
-        mAnimateToCorrectPosition.reset();
-        mAnimateToCorrectPosition.setDuration(ANIMATE_TO_TRIGGER_DURATION);
-        mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator);
-        if (listener != null) {
-            mCircleView.setAnimationListener(listener);
-        }
-        mCircleView.clearAnimation();
-        mCircleView.startAnimation(mAnimateToCorrectPosition);
-    }
-
-    private void animateOffsetToStartPosition(int from, AnimationListener listener) {
-        if (mScale) {
-            // Scale the item back down
-            startScaleDownReturnToStartAnimation(from, listener);
-        } else {
-            mFrom = from;
-            mAnimateToStartPosition.reset();
-            mAnimateToStartPosition.setDuration(ANIMATE_TO_START_DURATION);
-            mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
-            if (listener != null) {
-                mCircleView.setAnimationListener(listener);
-            }
-            mCircleView.clearAnimation();
-            mCircleView.startAnimation(mAnimateToStartPosition);
-        }
-    }
-
-    private final Animation mAnimateToCorrectPosition = new Animation() {
-        @Override
-        public void applyTransformation(float interpolatedTime, Transformation t) {
-            int targetTop = 0;
-            int endTarget = 0;
-            if (!mUsingCustomStart) {
-                endTarget = mSpinnerOffsetEnd - Math.abs(mOriginalOffsetTop);
-            } else {
-                endTarget = mSpinnerOffsetEnd;
-            }
-            targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
-            int offset = targetTop - mCircleView.getTop();
-            setTargetOffsetTopAndBottom(offset);
-            mProgress.setArrowScale(1 - interpolatedTime);
-        }
-    };
-
-    void moveToStart(float interpolatedTime) {
-        int targetTop = 0;
-        targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime));
-        int offset = targetTop - mCircleView.getTop();
-        setTargetOffsetTopAndBottom(offset);
-    }
-
-    private final Animation mAnimateToStartPosition = new Animation() {
-        @Override
-        public void applyTransformation(float interpolatedTime, Transformation t) {
-            moveToStart(interpolatedTime);
-        }
-    };
-
-    private void startScaleDownReturnToStartAnimation(int from,
-            Animation.AnimationListener listener) {
-        mFrom = from;
-        mStartingScale = mCircleView.getScaleX();
-        mScaleDownToStartAnimation = new Animation() {
-            @Override
-            public void applyTransformation(float interpolatedTime, Transformation t) {
-                float targetScale = (mStartingScale + (-mStartingScale  * interpolatedTime));
-                setAnimationProgress(targetScale);
-                moveToStart(interpolatedTime);
-            }
-        };
-        mScaleDownToStartAnimation.setDuration(SCALE_DOWN_DURATION);
-        if (listener != null) {
-            mCircleView.setAnimationListener(listener);
-        }
-        mCircleView.clearAnimation();
-        mCircleView.startAnimation(mScaleDownToStartAnimation);
-    }
-
-    void setTargetOffsetTopAndBottom(int offset) {
-        mCircleView.bringToFront();
-        ViewCompat.offsetTopAndBottom(mCircleView, offset);
-        mCurrentTargetOffsetTop = mCircleView.getTop();
-    }
-
-    private void onSecondaryPointerUp(MotionEvent 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
-            // active pointer and adjust accordingly.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-        }
-    }
-
-    /**
-     * Classes that wish to be notified when the swipe gesture correctly
-     * triggers a refresh should implement this interface.
-     */
-    public interface OnRefreshListener {
-        /**
-         * Called when a swipe gesture triggers a refresh.
-         */
-        void onRefresh();
-    }
-
-    /**
-     * Classes that wish to override {@link SwipeRefreshLayout#canChildScrollUp()} method
-     * behavior should implement this interface.
-     */
-    public interface OnChildScrollUpCallback {
-        /**
-         * Callback that will be called when {@link SwipeRefreshLayout#canChildScrollUp()} method
-         * is called to allow the implementer to override its behavior.
-         *
-         * @param parent SwipeRefreshLayout that this callback is overriding.
-         * @param child The child view of SwipeRefreshLayout.
-         *
-         * @return Whether it is possible for the child view of parent layout to scroll up.
-         */
-        boolean canChildScrollUp(@NonNull SwipeRefreshLayout parent, @Nullable View child);
-    }
-}
diff --git a/android/support/v4/widget/TextViewCompat.java b/android/support/v4/widget/TextViewCompat.java
deleted file mode 100644
index 8789815..0000000
--- a/android/support/v4/widget/TextViewCompat.java
+++ /dev/null
@@ -1,802 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-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.support.v4.os.BuildCompat;
-import android.text.Editable;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.TextView;
-
-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.ArrayList;
-import java.util.List;
-
-/**
- * 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 = TextView.AUTO_SIZE_TEXT_TYPE_NONE;
-
-    /**
-     * The TextView scales text size both horizontally and vertically to fit within the
-     * container.
-     */
-    public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM;
-
-    /** @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() {}
-
-    static class TextViewCompatBaseImpl {
-        private static final String LOG_TAG = "TextViewCompatBase";
-        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;
-
-        public void setCompoundDrawablesRelative(@NonNull TextView textView,
-                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-                @Nullable Drawable bottom) {
-            textView.setCompoundDrawables(start, top, end, bottom);
-        }
-
-        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-                @Nullable Drawable bottom) {
-            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
-        }
-
-        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
-                @DrawableRes int bottom) {
-            textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
-        }
-
-        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) {
-            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;
-        }
-
-        public 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;
-        }
-
-        @SuppressWarnings("deprecation")
-        public void setTextAppearance(TextView textView, @StyleRes int resId) {
-            textView.setTextAppearance(textView.getContext(), resId);
-        }
-
-        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView 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];
-        }
-
-        public void setCustomSelectionActionModeCallback(TextView textView,
-                ActionMode.Callback callback) {
-            textView.setCustomSelectionActionModeCallback(callback);
-        }
-    }
-
-    @RequiresApi(16)
-    static class TextViewCompatApi16Impl extends TextViewCompatBaseImpl {
-        @Override
-        public int getMaxLines(TextView textView) {
-            return textView.getMaxLines();
-        }
-
-        @Override
-        public int getMinLines(TextView textView) {
-            return textView.getMinLines();
-        }
-    }
-
-    @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) {
-            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) {
-            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) {
-            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) {
-            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;
-        }
-    }
-
-    @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) {
-            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) {
-            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) {
-            textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
-        }
-
-        @Override
-        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
-            return textView.getCompoundDrawablesRelative();
-        }
-    }
-
-    @RequiresApi(23)
-    static class TextViewCompatApi23Impl extends TextViewCompatApi18Impl {
-        @Override
-        public void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
-            textView.setTextAppearance(resId);
-        }
-    }
-
-    @RequiresApi(26)
-    static class TextViewCompatApi26Impl extends TextViewCompatApi23Impl {
-        @Override
-        public void setCustomSelectionActionModeCallback(final TextView textView,
-                final ActionMode.Callback callback) {
-            if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O
-                    && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) {
-                super.setCustomSelectionActionModeCallback(textView, callback);
-                return;
-            }
-
-
-            // A bug in O and O_MR1 causes a number of options for handling the ACTION_PROCESS_TEXT
-            // intent after selection to not be displayed in the menu, although they should be.
-            // Here we fix this, by removing the menu items created by the framework code, and
-            // adding them (and the missing ones) back correctly.
-            textView.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
-                // This constant should be correlated with its definition in the
-                // android.widget.Editor class.
-                private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
-
-                // References to the MenuBuilder class and its removeItemAt(int) method.
-                // Since in most cases the menu instance processed by this callback is going
-                // to be a MenuBuilder, we keep these references to avoid querying for them
-                // frequently by reflection in recomputeProcessTextMenuItems.
-                private Class mMenuBuilderClass;
-                private Method mMenuBuilderRemoveItemAtMethod;
-                private boolean mCanUseMenuBuilderReferences;
-                private boolean mInitializedMenuBuilderReferences = false;
-
-                @Override
-                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                    return callback.onCreateActionMode(mode, menu);
-                }
-
-                @Override
-                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-                    recomputeProcessTextMenuItems(menu);
-                    return callback.onPrepareActionMode(mode, menu);
-                }
-
-                @Override
-                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-                    return callback.onActionItemClicked(mode, item);
-                }
-
-                @Override
-                public void onDestroyActionMode(ActionMode mode) {
-                    callback.onDestroyActionMode(mode);
-                }
-
-                private void recomputeProcessTextMenuItems(final Menu menu) {
-                    final Context context = textView.getContext();
-                    final PackageManager packageManager = context.getPackageManager();
-
-                    if (!mInitializedMenuBuilderReferences) {
-                        mInitializedMenuBuilderReferences = true;
-                        try {
-                            mMenuBuilderClass =
-                                    Class.forName("com.android.internal.view.menu.MenuBuilder");
-                            mMenuBuilderRemoveItemAtMethod = mMenuBuilderClass
-                                    .getDeclaredMethod("removeItemAt", Integer.TYPE);
-                            mCanUseMenuBuilderReferences = true;
-                        } catch (ClassNotFoundException | NoSuchMethodException e) {
-                            mMenuBuilderClass = null;
-                            mMenuBuilderRemoveItemAtMethod = null;
-                            mCanUseMenuBuilderReferences = false;
-                        }
-                    }
-                    // Remove the menu items created for ACTION_PROCESS_TEXT handlers.
-                    try {
-                        final Method removeItemAtMethod =
-                                (mCanUseMenuBuilderReferences && mMenuBuilderClass.isInstance(menu))
-                                        ? mMenuBuilderRemoveItemAtMethod
-                                        : menu.getClass()
-                                                .getDeclaredMethod("removeItemAt", Integer.TYPE);
-                        for (int i = menu.size() - 1; i >= 0; --i) {
-                            final MenuItem item = menu.getItem(i);
-                            if (item.getIntent() != null && Intent.ACTION_PROCESS_TEXT
-                                    .equals(item.getIntent().getAction())) {
-                                removeItemAtMethod.invoke(menu, i);
-                            }
-                        }
-                    } catch (NoSuchMethodException | IllegalAccessException
-                            | InvocationTargetException e) {
-                        // There is a menu custom implementation used which is not providing
-                        // a removeItemAt(int) menu. There is nothing we can do in this case.
-                        return;
-                    }
-
-                    // Populate the menu again with the ACTION_PROCESS_TEXT handlers.
-                    final List<ResolveInfo> supportedActivities =
-                            getSupportedActivities(context, packageManager);
-                    for (int i = 0; i < supportedActivities.size(); ++i) {
-                        final ResolveInfo info = supportedActivities.get(i);
-                        menu.add(Menu.NONE, Menu.NONE,
-                                MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
-                                info.loadLabel(packageManager))
-                                .setIntent(createProcessTextIntentForResolveInfo(info, textView))
-                                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-                    }
-                }
-
-                private List<ResolveInfo> getSupportedActivities(final Context context,
-                        final PackageManager packageManager) {
-                    final List<ResolveInfo> supportedActivities = new ArrayList<>();
-                    boolean canStartActivityForResult = context instanceof Activity;
-                    if (!canStartActivityForResult) {
-                        return supportedActivities;
-                    }
-                    final List<ResolveInfo> unfiltered =
-                            packageManager.queryIntentActivities(createProcessTextIntent(), 0);
-                    for (ResolveInfo info : unfiltered) {
-                        if (isSupportedActivity(info, context)) {
-                            supportedActivities.add(info);
-                        }
-                    }
-                    return supportedActivities;
-                }
-
-                private boolean isSupportedActivity(final ResolveInfo info, final Context context) {
-                    if (context.getPackageName().equals(info.activityInfo.packageName)) {
-                        return true;
-                    }
-                    if (!info.activityInfo.exported) {
-                        return false;
-                    }
-                    return info.activityInfo.permission == null
-                            || context.checkSelfPermission(info.activityInfo.permission)
-                                == PackageManager.PERMISSION_GRANTED;
-                }
-
-                private Intent createProcessTextIntentForResolveInfo(final ResolveInfo info,
-                        final TextView textView) {
-                    return createProcessTextIntent()
-                            .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !isEditable(textView))
-                            .setClassName(info.activityInfo.packageName, info.activityInfo.name);
-                }
-
-                private boolean isEditable(final TextView textView) {
-                    return textView instanceof Editable
-                            && textView.onCheckIsTextEditor()
-                            && textView.isEnabled();
-                }
-
-                private Intent createProcessTextIntent() {
-                    return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain");
-                }
-            });
-        }
-    }
-
-    @RequiresApi(27)
-    static class TextViewCompatApi27Impl extends TextViewCompatApi26Impl {
-        @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 {
-        if (BuildCompat.isAtLeastOMR1()) {
-            IMPL = new TextViewCompatApi27Impl();
-        } else 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 TextViewCompatBaseImpl();
-        }
-    }
-
-    /**
-     * Sets the Drawables (if any) to appear to the start of, above, to the end
-     * of, and below the text. Use {@code null} if you do not want a Drawable
-     * there. The Drawables must already have had {@link Drawable#setBounds}
-     * called.
-     * <p/>
-     * Calling this method will overwrite any Drawables previously set using
-     * {@link TextView#setCompoundDrawables} or related methods.
-     *
-     * @param textView The TextView against which to invoke the method.
-     * @attr name android:drawableStart
-     * @attr name android:drawableTop
-     * @attr name android:drawableEnd
-     * @attr name android:drawableBottom
-     */
-    public static void setCompoundDrawablesRelative(@NonNull TextView textView,
-            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-            @Nullable Drawable bottom) {
-        IMPL.setCompoundDrawablesRelative(textView, start, top, end, bottom);
-    }
-
-    /**
-     * Sets the Drawables (if any) to appear to the start of, above, to the end
-     * of, and below the text. Use {@code null} if you do not want a Drawable
-     * there. The Drawables' bounds will be set to their intrinsic bounds.
-     * <p/>
-     * Calling this method will overwrite any Drawables previously set using
-     * {@link TextView#setCompoundDrawables} or related methods.
-     *
-     * @param textView The TextView against which to invoke the method.
-     * @attr name android:drawableStart
-     * @attr name android:drawableTop
-     * @attr name android:drawableEnd
-     * @attr name android:drawableBottom
-     */
-    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
-            @Nullable Drawable bottom) {
-        IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom);
-    }
-
-    /**
-     * Sets the Drawables (if any) to appear to the start of, above, to the end
-     * of, and below the text. Use 0 if you do not want a Drawable there. The
-     * Drawables' bounds will be set to their intrinsic bounds.
-     * <p/>
-     * Calling this method will overwrite any Drawables previously set using
-     * {@link TextView#setCompoundDrawables} or related methods.
-     *
-     * @param textView The TextView against which to invoke the method.
-     * @param start    Resource identifier of the start Drawable.
-     * @param top      Resource identifier of the top Drawable.
-     * @param end      Resource identifier of the end Drawable.
-     * @param bottom   Resource identifier of the bottom Drawable.
-     * @attr name android:drawableStart
-     * @attr name android:drawableTop
-     * @attr name android:drawableEnd
-     * @attr name android:drawableBottom
-     */
-    public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
-            @DrawableRes int bottom) {
-        IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom);
-    }
-
-    /**
-     * Returns the maximum number of lines displayed in the given TextView, or -1 if the maximum
-     * height was set in pixels instead.
-     */
-    public static int getMaxLines(@NonNull TextView textView) {
-        return IMPL.getMaxLines(textView);
-    }
-
-    /**
-     * Returns the minimum number of lines displayed in the given TextView, or -1 if the minimum
-     * height was set in pixels instead.
-     */
-    public static int getMinLines(@NonNull TextView textView) {
-        return IMPL.getMinLines(textView);
-    }
-
-    /**
-     * Sets the text appearance from the specified style resource.
-     * <p>
-     * Use a framework-defined {@code TextAppearance} style like
-     * {@link android.R.style#TextAppearance_Material_Body1 @android:style/TextAppearance.Material.Body1}.
-     *
-     * @param textView The TextView against which to invoke the method.
-     * @param resId    The resource identifier of the style to apply.
-     */
-    public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
-        IMPL.setTextAppearance(textView, resId);
-    }
-
-    /**
-     * Returns drawables for the start, top, end, and bottom borders from the given text view.
-     */
-    @NonNull
-    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(@NonNull 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(
-            @NonNull 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(@NonNull 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(@NonNull TextView textView) {
-        return IMPL.getAutoSizeTextType(textView);
-    }
-
-    /**
-     * @return the current auto-size step granularity in pixels.
-     *
-     * @attr name android:autoSizeStepGranularity
-     */
-    public static int getAutoSizeStepGranularity(@NonNull 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(@NonNull 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(@NonNull TextView textView) {
-        return IMPL.getAutoSizeMaxTextSize(textView);
-    }
-
-    /**
-     * @return the current auto-size {@code int} sizes array (in pixels).
-     *
-     * @attr name android:autoSizePresetSizes
-     */
-    @NonNull
-    public static int[] getAutoSizeTextAvailableSizes(@NonNull TextView textView) {
-        return IMPL.getAutoSizeTextAvailableSizes(textView);
-    }
-
-    /**
-     * Sets a selection action mode callback on a TextView.
-     *
-     * Also this method can be used to fix a bug in framework SDK 26. On these affected devices,
-     * the bug causes the menu containing the options for handling ACTION_PROCESS_TEXT after text
-     * selection to miss a number of items. This method can be used to fix this wrong behaviour for
-     * a text view, by passing any custom callback implementation. If no custom callback is desired,
-     * a no-op implementation should be provided.
-     *
-     * Note that, by default, the bug will only be fixed when the default floating toolbar menu
-     * implementation is used. If a custom implementation of {@link Menu} is provided, this should
-     * provide the method Menu#removeItemAt(int) which removes a menu item by its position,
-     * as given by Menu#getItem(int). Also, the following post condition should hold: a call
-     * to removeItemAt(i), should not modify the results of getItem(j) for any j < i. Intuitively,
-     * removing an element from the menu should behave as removing an element from a list.
-     * Note that this method does not exist in the {@link Menu} interface. However, it is required,
-     * and going to be called by reflection, in order to display the correct process text items in
-     * the menu.
-     *
-     * @param textView The TextView to set the action selection mode callback on.
-     * @param callback The action selection mode callback to set on textView.
-     */
-    public static void setCustomSelectionActionModeCallback(@NonNull TextView textView,
-                @NonNull ActionMode.Callback callback) {
-        IMPL.setCustomSelectionActionModeCallback(textView, callback);
-    }
-}
diff --git a/android/support/v4/widget/TintableCompoundButton.java b/android/support/v4/widget/TintableCompoundButton.java
deleted file mode 100644
index f739fac..0000000
--- a/android/support/v4/widget/TintableCompoundButton.java
+++ /dev/null
@@ -1,71 +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
-     */
-    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/android/support/v4/widget/TintableImageSourceView.java b/android/support/v4/widget/TintableImageSourceView.java
deleted file mode 100644
index 0c3d436..0000000
--- a/android/support/v4/widget/TintableImageSourceView.java
+++ /dev/null
@@ -1,75 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-
-/**
- * Interface which allows an {@link android.widget.ImageView} to receive image tinting calls
- * from {@link ImageViewCompat} when running on API v20 devices or lower.
- *
- * @hide Internal use only
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface TintableImageSourceView {
-
-    /**
-     * Applies a tint to the image drawable. Does not modify the current tint
-     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
-     * <p>
-     * Subsequent calls to the source's image will 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
-     *
-     * @see #getSupportImageTintList()
-     */
-    void setSupportImageTintList(@Nullable ColorStateList tint);
-
-    /**
-     * Return the tint applied to the image drawable, if specified.
-     *
-     * @return the tint applied to the image drawable
-     */
-    @Nullable
-    ColorStateList getSupportImageTintList();
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setSupportImageTintList(ColorStateList)}} to the image
-     * 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 #getSupportImageTintMode()
-     */
-    void setSupportImageTintMode(@Nullable PorterDuff.Mode tintMode);
-
-    /**
-     * Return the blending mode used to apply the tint to the image
-     * drawable, if specified.
-     *
-     * @return the blending mode used to apply the tint to the image drawable
-     */
-    @Nullable
-    PorterDuff.Mode getSupportImageTintMode();
-}
diff --git a/android/support/v4/widget/ViewDragHelper.java b/android/support/v4/widget/ViewDragHelper.java
deleted file mode 100644
index 09c6f66..0000000
--- a/android/support/v4/widget/ViewDragHelper.java
+++ /dev/null
@@ -1,1514 +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.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ViewCompat;
-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.view.animation.Interpolator;
-import android.widget.OverScroller;
-
-import java.util.Arrays;
-
-/**
- * ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number
- * of useful operations and state tracking for allowing a user to drag and reposition
- * views within their parent ViewGroup.
- */
-public class ViewDragHelper {
-    private static final String TAG = "ViewDragHelper";
-
-    /**
-     * A null/invalid pointer ID.
-     */
-    public static final int INVALID_POINTER = -1;
-
-    /**
-     * A view is not currently being dragged or animating as a result of a fling/snap.
-     */
-    public static final int STATE_IDLE = 0;
-
-    /**
-     * A view is currently being dragged. The position is currently changing as a result
-     * of user input or simulated user input.
-     */
-    public static final int STATE_DRAGGING = 1;
-
-    /**
-     * A view is currently settling into place as a result of a fling or
-     * predefined non-interactive motion.
-     */
-    public static final int STATE_SETTLING = 2;
-
-    /**
-     * Edge flag indicating that the left edge should be affected.
-     */
-    public static final int EDGE_LEFT = 1 << 0;
-
-    /**
-     * Edge flag indicating that the right edge should be affected.
-     */
-    public static final int EDGE_RIGHT = 1 << 1;
-
-    /**
-     * Edge flag indicating that the top edge should be affected.
-     */
-    public static final int EDGE_TOP = 1 << 2;
-
-    /**
-     * Edge flag indicating that the bottom edge should be affected.
-     */
-    public static final int EDGE_BOTTOM = 1 << 3;
-
-    /**
-     * Edge flag set indicating all edges should be affected.
-     */
-    public static final int EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT | EDGE_BOTTOM;
-
-    /**
-     * Indicates that a check should occur along the horizontal axis
-     */
-    public static final int DIRECTION_HORIZONTAL = 1 << 0;
-
-    /**
-     * Indicates that a check should occur along the vertical axis
-     */
-    public static final int DIRECTION_VERTICAL = 1 << 1;
-
-    /**
-     * Indicates that a check should occur along all axes
-     */
-    public static final int DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
-
-    private static final int EDGE_SIZE = 20; // dp
-
-    private static final int BASE_SETTLE_DURATION = 256; // ms
-    private static final int MAX_SETTLE_DURATION = 600; // ms
-
-    // Current drag state; idle, dragging or settling
-    private int mDragState;
-
-    // Distance to travel before a drag may begin
-    private int mTouchSlop;
-
-    // Last known position/pointer tracking
-    private int mActivePointerId = INVALID_POINTER;
-    private float[] mInitialMotionX;
-    private float[] mInitialMotionY;
-    private float[] mLastMotionX;
-    private float[] mLastMotionY;
-    private int[] mInitialEdgesTouched;
-    private int[] mEdgeDragsInProgress;
-    private int[] mEdgeDragsLocked;
-    private int mPointersDown;
-
-    private VelocityTracker mVelocityTracker;
-    private float mMaxVelocity;
-    private float mMinVelocity;
-
-    private int mEdgeSize;
-    private int mTrackingEdges;
-
-    private OverScroller mScroller;
-
-    private final Callback mCallback;
-
-    private View mCapturedView;
-    private boolean mReleaseInProgress;
-
-    private final ViewGroup mParentView;
-
-    /**
-     * A Callback is used as a communication channel with the ViewDragHelper back to the
-     * parent view using it. <code>on*</code>methods are invoked on siginficant events and several
-     * accessor methods are expected to provide the ViewDragHelper with more information
-     * about the state of the parent view upon request. The callback also makes decisions
-     * governing the range and draggability of child views.
-     */
-    public abstract static class Callback {
-        /**
-         * Called when the drag state changes. See the <code>STATE_*</code> constants
-         * for more information.
-         *
-         * @param state The new drag state
-         *
-         * @see #STATE_IDLE
-         * @see #STATE_DRAGGING
-         * @see #STATE_SETTLING
-         */
-        public void onViewDragStateChanged(int state) {}
-
-        /**
-         * Called when the captured view's position changes as the result of a drag or settle.
-         *
-         * @param changedView View whose position changed
-         * @param left New X coordinate of the left edge of the view
-         * @param top New Y coordinate of the top edge of the view
-         * @param dx Change in X position from the last call
-         * @param dy Change in Y position from the last call
-         */
-        public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx,
-                int dy) {
-        }
-
-        /**
-         * Called when a child view is captured for dragging or settling. The ID of the pointer
-         * currently dragging the captured view is supplied. If activePointerId is
-         * identified as {@link #INVALID_POINTER} the capture is programmatic instead of
-         * pointer-initiated.
-         *
-         * @param capturedChild Child view that was captured
-         * @param activePointerId Pointer id tracking the child capture
-         */
-        public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {}
-
-        /**
-         * Called when the child view is no longer being actively dragged.
-         * The fling velocity is also supplied, if relevant. The velocity values may
-         * be clamped to system minimums or maximums.
-         *
-         * <p>Calling code may decide to fling or otherwise release the view to let it
-         * settle into place. It should do so using {@link #settleCapturedViewAt(int, int)}
-         * or {@link #flingCapturedView(int, int, int, int)}. If the Callback invokes
-         * one of these methods, the ViewDragHelper will enter {@link #STATE_SETTLING}
-         * and the view capture will not fully end until it comes to a complete stop.
-         * If neither of these methods is invoked before <code>onViewReleased</code> returns,
-         * the view will stop in place and the ViewDragHelper will return to
-         * {@link #STATE_IDLE}.</p>
-         *
-         * @param releasedChild The captured child view now being released
-         * @param xvel X velocity of the pointer as it left the screen in pixels per second.
-         * @param yvel Y velocity of the pointer as it left the screen in pixels per second.
-         */
-        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {}
-
-        /**
-         * Called when one of the subscribed edges in the parent view has been touched
-         * by the user while no child view is currently captured.
-         *
-         * @param edgeFlags A combination of edge flags describing the edge(s) currently touched
-         * @param pointerId ID of the pointer touching the described edge(s)
-         * @see #EDGE_LEFT
-         * @see #EDGE_TOP
-         * @see #EDGE_RIGHT
-         * @see #EDGE_BOTTOM
-         */
-        public void onEdgeTouched(int edgeFlags, int pointerId) {}
-
-        /**
-         * Called when the given edge may become locked. This can happen if an edge drag
-         * was preliminarily rejected before beginning, but after {@link #onEdgeTouched(int, int)}
-         * was called. This method should return true to lock this edge or false to leave it
-         * unlocked. The default behavior is to leave edges unlocked.
-         *
-         * @param edgeFlags A combination of edge flags describing the edge(s) locked
-         * @return true to lock the edge, false to leave it unlocked
-         */
-        public boolean onEdgeLock(int edgeFlags) {
-            return false;
-        }
-
-        /**
-         * Called when the user has started a deliberate drag away from one
-         * of the subscribed edges in the parent view while no child view is currently captured.
-         *
-         * @param edgeFlags A combination of edge flags describing the edge(s) dragged
-         * @param pointerId ID of the pointer touching the described edge(s)
-         * @see #EDGE_LEFT
-         * @see #EDGE_TOP
-         * @see #EDGE_RIGHT
-         * @see #EDGE_BOTTOM
-         */
-        public void onEdgeDragStarted(int edgeFlags, int pointerId) {}
-
-        /**
-         * Called to determine the Z-order of child views.
-         *
-         * @param index the ordered position to query for
-         * @return index of the view that should be ordered at position <code>index</code>
-         */
-        public int getOrderedChildIndex(int index) {
-            return index;
-        }
-
-        /**
-         * Return the magnitude of a draggable child view's horizontal range of motion in pixels.
-         * This method should return 0 for views that cannot move horizontally.
-         *
-         * @param child Child view to check
-         * @return range of horizontal motion in pixels
-         */
-        public int getViewHorizontalDragRange(@NonNull View child) {
-            return 0;
-        }
-
-        /**
-         * Return the magnitude of a draggable child view's vertical range of motion in pixels.
-         * This method should return 0 for views that cannot move vertically.
-         *
-         * @param child Child view to check
-         * @return range of vertical motion in pixels
-         */
-        public int getViewVerticalDragRange(@NonNull View child) {
-            return 0;
-        }
-
-        /**
-         * Called when the user's input indicates that they want to capture the given child view
-         * with the pointer indicated by pointerId. The callback should return true if the user
-         * is permitted to drag the given view with the indicated pointer.
-         *
-         * <p>ViewDragHelper may call this method multiple times for the same view even if
-         * the view is already captured; this indicates that a new pointer is trying to take
-         * control of the view.</p>
-         *
-         * <p>If this method returns true, a call to {@link #onViewCaptured(android.view.View, int)}
-         * will follow if the capture is successful.</p>
-         *
-         * @param child Child the user is attempting to capture
-         * @param pointerId ID of the pointer attempting the capture
-         * @return true if capture should be allowed, false otherwise
-         */
-        public abstract boolean tryCaptureView(@NonNull View child, int pointerId);
-
-        /**
-         * Restrict the motion of the dragged child view along the horizontal axis.
-         * The default implementation does not allow horizontal motion; the extending
-         * class must override this method and provide the desired clamping.
-         *
-         *
-         * @param child Child view being dragged
-         * @param left Attempted motion along the X axis
-         * @param dx Proposed change in position for left
-         * @return The new clamped position for left
-         */
-        public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
-            return 0;
-        }
-
-        /**
-         * Restrict the motion of the dragged child view along the vertical axis.
-         * The default implementation does not allow vertical motion; the extending
-         * class must override this method and provide the desired clamping.
-         *
-         *
-         * @param child Child view being dragged
-         * @param top Attempted motion along the Y axis
-         * @param dy Proposed change in position for top
-         * @return The new clamped position for top
-         */
-        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
-            return 0;
-        }
-    }
-
-    /**
-     * Interpolator defining the animation curve for mScroller
-     */
-    private static final Interpolator sInterpolator = new Interpolator() {
-        @Override
-        public float getInterpolation(float t) {
-            t -= 1.0f;
-            return t * t * t * t * t + 1.0f;
-        }
-    };
-
-    private final Runnable mSetIdleRunnable = new Runnable() {
-        @Override
-        public void run() {
-            setDragState(STATE_IDLE);
-        }
-    };
-
-    /**
-     * Factory method to create a new ViewDragHelper.
-     *
-     * @param forParent Parent view to monitor
-     * @param cb Callback to provide information and receive events
-     * @return a new ViewDragHelper instance
-     */
-    public static ViewDragHelper create(@NonNull ViewGroup forParent, @NonNull Callback cb) {
-        return new ViewDragHelper(forParent.getContext(), forParent, cb);
-    }
-
-    /**
-     * Factory method to create a new ViewDragHelper.
-     *
-     * @param forParent Parent view to monitor
-     * @param sensitivity Multiplier for how sensitive the helper should be about detecting
-     *                    the start of a drag. Larger values are more sensitive. 1.0f is normal.
-     * @param cb Callback to provide information and receive events
-     * @return a new ViewDragHelper instance
-     */
-    public static ViewDragHelper create(@NonNull ViewGroup forParent, float sensitivity,
-            @NonNull Callback cb) {
-        final ViewDragHelper helper = create(forParent, cb);
-        helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
-        return helper;
-    }
-
-    /**
-     * Apps should use ViewDragHelper.create() to get a new instance.
-     * This will allow VDH to use internal compatibility implementations for different
-     * platform versions.
-     *
-     * @param context Context to initialize config-dependent params from
-     * @param forParent Parent view to monitor
-     */
-    private ViewDragHelper(@NonNull Context context, @NonNull ViewGroup forParent,
-            @NonNull Callback cb) {
-        if (forParent == null) {
-            throw new IllegalArgumentException("Parent view may not be null");
-        }
-        if (cb == null) {
-            throw new IllegalArgumentException("Callback may not be null");
-        }
-
-        mParentView = forParent;
-        mCallback = cb;
-
-        final ViewConfiguration vc = ViewConfiguration.get(context);
-        final float density = context.getResources().getDisplayMetrics().density;
-        mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);
-
-        mTouchSlop = vc.getScaledTouchSlop();
-        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
-        mMinVelocity = vc.getScaledMinimumFlingVelocity();
-        mScroller = new OverScroller(context, sInterpolator);
-    }
-
-    /**
-     * Set the minimum velocity that will be detected as having a magnitude greater than zero
-     * in pixels per second. Callback methods accepting a velocity will be clamped appropriately.
-     *
-     * @param minVel Minimum velocity to detect
-     */
-    public void setMinVelocity(float minVel) {
-        mMinVelocity = minVel;
-    }
-
-    /**
-     * Return the currently configured minimum velocity. Any flings with a magnitude less
-     * than this value in pixels per second. Callback methods accepting a velocity will receive
-     * zero as a velocity value if the real detected velocity was below this threshold.
-     *
-     * @return the minimum velocity that will be detected
-     */
-    public float getMinVelocity() {
-        return mMinVelocity;
-    }
-
-    /**
-     * Retrieve the current drag state of this helper. This will return one of
-     * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
-     * @return The current drag state
-     */
-    public int getViewDragState() {
-        return mDragState;
-    }
-
-    /**
-     * Enable edge tracking for the selected edges of the parent view.
-     * The callback's {@link Callback#onEdgeTouched(int, int)} and
-     * {@link Callback#onEdgeDragStarted(int, int)} methods will only be invoked
-     * for edges for which edge tracking has been enabled.
-     *
-     * @param edgeFlags Combination of edge flags describing the edges to watch
-     * @see #EDGE_LEFT
-     * @see #EDGE_TOP
-     * @see #EDGE_RIGHT
-     * @see #EDGE_BOTTOM
-     */
-    public void setEdgeTrackingEnabled(int edgeFlags) {
-        mTrackingEdges = edgeFlags;
-    }
-
-    /**
-     * Return the size of an edge. This is the range in pixels along the edges of this view
-     * that will actively detect edge touches or drags if edge tracking is enabled.
-     *
-     * @return The size of an edge in pixels
-     * @see #setEdgeTrackingEnabled(int)
-     */
-    public int getEdgeSize() {
-        return mEdgeSize;
-    }
-
-    /**
-     * Capture a specific child view for dragging within the parent. The callback will be notified
-     * but {@link Callback#tryCaptureView(android.view.View, int)} will not be asked permission to
-     * capture this view.
-     *
-     * @param childView Child view to capture
-     * @param activePointerId ID of the pointer that is dragging the captured child view
-     */
-    public void captureChildView(@NonNull View childView, int activePointerId) {
-        if (childView.getParent() != mParentView) {
-            throw new IllegalArgumentException("captureChildView: parameter must be a descendant "
-                    + "of the ViewDragHelper's tracked parent view (" + mParentView + ")");
-        }
-
-        mCapturedView = childView;
-        mActivePointerId = activePointerId;
-        mCallback.onViewCaptured(childView, activePointerId);
-        setDragState(STATE_DRAGGING);
-    }
-
-    /**
-     * @return The currently captured view, or null if no view has been captured.
-     */
-    @Nullable
-    public View getCapturedView() {
-        return mCapturedView;
-    }
-
-    /**
-     * @return The ID of the pointer currently dragging the captured view,
-     *         or {@link #INVALID_POINTER}.
-     */
-    public int getActivePointerId() {
-        return mActivePointerId;
-    }
-
-    /**
-     * @return The minimum distance in pixels that the user must travel to initiate a drag
-     */
-    public int getTouchSlop() {
-        return mTouchSlop;
-    }
-
-    /**
-     * The result of a call to this method is equivalent to
-     * {@link #processTouchEvent(android.view.MotionEvent)} receiving an ACTION_CANCEL event.
-     */
-    public void cancel() {
-        mActivePointerId = INVALID_POINTER;
-        clearMotionHistory();
-
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    /**
-     * {@link #cancel()}, but also abort all motion in progress and snap to the end of any
-     * animation.
-     */
-    public void abort() {
-        cancel();
-        if (mDragState == STATE_SETTLING) {
-            final int oldX = mScroller.getCurrX();
-            final int oldY = mScroller.getCurrY();
-            mScroller.abortAnimation();
-            final int newX = mScroller.getCurrX();
-            final int newY = mScroller.getCurrY();
-            mCallback.onViewPositionChanged(mCapturedView, newX, newY, newX - oldX, newY - oldY);
-        }
-        setDragState(STATE_IDLE);
-    }
-
-    /**
-     * Animate the view <code>child</code> to the given (left, top) position.
-     * If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
-     * on each subsequent frame to continue the motion until it returns false. If this method
-     * returns false there is no further work to do to complete the movement.
-     *
-     * <p>This operation does not count as a capture event, though {@link #getCapturedView()}
-     * will still report the sliding view while the slide is in progress.</p>
-     *
-     * @param child Child view to capture and animate
-     * @param finalLeft Final left position of child
-     * @param finalTop Final top position of child
-     * @return true if animation should continue through {@link #continueSettling(boolean)} calls
-     */
-    public boolean smoothSlideViewTo(@NonNull View child, int finalLeft, int finalTop) {
-        mCapturedView = child;
-        mActivePointerId = INVALID_POINTER;
-
-        boolean continueSliding = forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0);
-        if (!continueSliding && mDragState == STATE_IDLE && mCapturedView != null) {
-            // If we're in an IDLE state to begin with and aren't moving anywhere, we
-            // end up having a non-null capturedView with an IDLE dragState
-            mCapturedView = null;
-        }
-
-        return continueSliding;
-    }
-
-    /**
-     * Settle the captured view at the given (left, top) position.
-     * The appropriate velocity from prior motion will be taken into account.
-     * If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
-     * on each subsequent frame to continue the motion until it returns false. If this method
-     * returns false there is no further work to do to complete the movement.
-     *
-     * @param finalLeft Settled left edge position for the captured view
-     * @param finalTop Settled top edge position for the captured view
-     * @return true if animation should continue through {@link #continueSettling(boolean)} calls
-     */
-    public boolean settleCapturedViewAt(int finalLeft, int finalTop) {
-        if (!mReleaseInProgress) {
-            throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to "
-                    + "Callback#onViewReleased");
-        }
-
-        return forceSettleCapturedViewAt(finalLeft, finalTop,
-                (int) mVelocityTracker.getXVelocity(mActivePointerId),
-                (int) mVelocityTracker.getYVelocity(mActivePointerId));
-    }
-
-    /**
-     * Settle the captured view at the given (left, top) position.
-     *
-     * @param finalLeft Target left position for the captured view
-     * @param finalTop Target top position for the captured view
-     * @param xvel Horizontal velocity
-     * @param yvel Vertical velocity
-     * @return true if animation should continue through {@link #continueSettling(boolean)} calls
-     */
-    private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {
-        final int startLeft = mCapturedView.getLeft();
-        final int startTop = mCapturedView.getTop();
-        final int dx = finalLeft - startLeft;
-        final int dy = finalTop - startTop;
-
-        if (dx == 0 && dy == 0) {
-            // Nothing to do. Send callbacks, be done.
-            mScroller.abortAnimation();
-            setDragState(STATE_IDLE);
-            return false;
-        }
-
-        final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel);
-        mScroller.startScroll(startLeft, startTop, dx, dy, duration);
-
-        setDragState(STATE_SETTLING);
-        return true;
-    }
-
-    private int computeSettleDuration(View child, int dx, int dy, int xvel, int yvel) {
-        xvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity);
-        yvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity);
-        final int absDx = Math.abs(dx);
-        final int absDy = Math.abs(dy);
-        final int absXVel = Math.abs(xvel);
-        final int absYVel = Math.abs(yvel);
-        final int addedVel = absXVel + absYVel;
-        final int addedDistance = absDx + absDy;
-
-        final float xweight = xvel != 0 ? (float) absXVel / addedVel :
-                (float) absDx / addedDistance;
-        final float yweight = yvel != 0 ? (float) absYVel / addedVel :
-                (float) absDy / addedDistance;
-
-        int xduration = computeAxisDuration(dx, xvel, mCallback.getViewHorizontalDragRange(child));
-        int yduration = computeAxisDuration(dy, yvel, mCallback.getViewVerticalDragRange(child));
-
-        return (int) (xduration * xweight + yduration * yweight);
-    }
-
-    private int computeAxisDuration(int delta, int velocity, int motionRange) {
-        if (delta == 0) {
-            return 0;
-        }
-
-        final int width = mParentView.getWidth();
-        final int halfWidth = width / 2;
-        final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width);
-        final float distance = halfWidth + halfWidth
-                * distanceInfluenceForSnapDuration(distanceRatio);
-
-        int duration;
-        velocity = Math.abs(velocity);
-        if (velocity > 0) {
-            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
-        } else {
-            final float range = (float) Math.abs(delta) / motionRange;
-            duration = (int) ((range + 1) * BASE_SETTLE_DURATION);
-        }
-        return Math.min(duration, MAX_SETTLE_DURATION);
-    }
-
-    /**
-     * Clamp the magnitude of value for absMin and absMax.
-     * If the value is below the minimum, it will be clamped to zero.
-     * If the value is above the maximum, it will be clamped to the maximum.
-     *
-     * @param value Value to clamp
-     * @param absMin Absolute value of the minimum significant value to return
-     * @param absMax Absolute value of the maximum value to return
-     * @return The clamped value with the same sign as <code>value</code>
-     */
-    private int clampMag(int value, int absMin, int absMax) {
-        final int absValue = Math.abs(value);
-        if (absValue < absMin) return 0;
-        if (absValue > absMax) return value > 0 ? absMax : -absMax;
-        return value;
-    }
-
-    /**
-     * Clamp the magnitude of value for absMin and absMax.
-     * If the value is below the minimum, it will be clamped to zero.
-     * If the value is above the maximum, it will be clamped to the maximum.
-     *
-     * @param value Value to clamp
-     * @param absMin Absolute value of the minimum significant value to return
-     * @param absMax Absolute value of the maximum value to return
-     * @return The clamped value with the same sign as <code>value</code>
-     */
-    private float clampMag(float value, float absMin, float absMax) {
-        final float absValue = Math.abs(value);
-        if (absValue < absMin) return 0;
-        if (absValue > absMax) return value > 0 ? absMax : -absMax;
-        return value;
-    }
-
-    private float distanceInfluenceForSnapDuration(float f) {
-        f -= 0.5f; // center the values about 0.
-        f *= 0.3f * (float) Math.PI / 2.0f;
-        return (float) Math.sin(f);
-    }
-
-    /**
-     * Settle the captured view based on standard free-moving fling behavior.
-     * The caller should invoke {@link #continueSettling(boolean)} on each subsequent frame
-     * to continue the motion until it returns false.
-     *
-     * @param minLeft Minimum X position for the view's left edge
-     * @param minTop Minimum Y position for the view's top edge
-     * @param maxLeft Maximum X position for the view's left edge
-     * @param maxTop Maximum Y position for the view's top edge
-     */
-    public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop) {
-        if (!mReleaseInProgress) {
-            throw new IllegalStateException("Cannot flingCapturedView outside of a call to "
-                    + "Callback#onViewReleased");
-        }
-
-        mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(),
-                (int) mVelocityTracker.getXVelocity(mActivePointerId),
-                (int) mVelocityTracker.getYVelocity(mActivePointerId),
-                minLeft, maxLeft, minTop, maxTop);
-
-        setDragState(STATE_SETTLING);
-    }
-
-    /**
-     * Move the captured settling view by the appropriate amount for the current time.
-     * If <code>continueSettling</code> returns true, the caller should call it again
-     * on the next frame to continue.
-     *
-     * @param deferCallbacks true if state callbacks should be deferred via posted message.
-     *                       Set this to true if you are calling this method from
-     *                       {@link android.view.View#computeScroll()} or similar methods
-     *                       invoked as part of layout or drawing.
-     * @return true if settle is still in progress
-     */
-    public boolean continueSettling(boolean deferCallbacks) {
-        if (mDragState == STATE_SETTLING) {
-            boolean keepGoing = mScroller.computeScrollOffset();
-            final int x = mScroller.getCurrX();
-            final int y = mScroller.getCurrY();
-            final int dx = x - mCapturedView.getLeft();
-            final int dy = y - mCapturedView.getTop();
-
-            if (dx != 0) {
-                ViewCompat.offsetLeftAndRight(mCapturedView, dx);
-            }
-            if (dy != 0) {
-                ViewCompat.offsetTopAndBottom(mCapturedView, dy);
-            }
-
-            if (dx != 0 || dy != 0) {
-                mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);
-            }
-
-            if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {
-                // Close enough. The interpolator/scroller might think we're still moving
-                // but the user sure doesn't.
-                mScroller.abortAnimation();
-                keepGoing = false;
-            }
-
-            if (!keepGoing) {
-                if (deferCallbacks) {
-                    mParentView.post(mSetIdleRunnable);
-                } else {
-                    setDragState(STATE_IDLE);
-                }
-            }
-        }
-
-        return mDragState == STATE_SETTLING;
-    }
-
-    /**
-     * Like all callback events this must happen on the UI thread, but release
-     * involves some extra semantics. During a release (mReleaseInProgress)
-     * is the only time it is valid to call {@link #settleCapturedViewAt(int, int)}
-     * or {@link #flingCapturedView(int, int, int, int)}.
-     */
-    private void dispatchViewReleased(float xvel, float yvel) {
-        mReleaseInProgress = true;
-        mCallback.onViewReleased(mCapturedView, xvel, yvel);
-        mReleaseInProgress = false;
-
-        if (mDragState == STATE_DRAGGING) {
-            // onViewReleased didn't call a method that would have changed this. Go idle.
-            setDragState(STATE_IDLE);
-        }
-    }
-
-    private void clearMotionHistory() {
-        if (mInitialMotionX == null) {
-            return;
-        }
-        Arrays.fill(mInitialMotionX, 0);
-        Arrays.fill(mInitialMotionY, 0);
-        Arrays.fill(mLastMotionX, 0);
-        Arrays.fill(mLastMotionY, 0);
-        Arrays.fill(mInitialEdgesTouched, 0);
-        Arrays.fill(mEdgeDragsInProgress, 0);
-        Arrays.fill(mEdgeDragsLocked, 0);
-        mPointersDown = 0;
-    }
-
-    private void clearMotionHistory(int pointerId) {
-        if (mInitialMotionX == null || !isPointerDown(pointerId)) {
-            return;
-        }
-        mInitialMotionX[pointerId] = 0;
-        mInitialMotionY[pointerId] = 0;
-        mLastMotionX[pointerId] = 0;
-        mLastMotionY[pointerId] = 0;
-        mInitialEdgesTouched[pointerId] = 0;
-        mEdgeDragsInProgress[pointerId] = 0;
-        mEdgeDragsLocked[pointerId] = 0;
-        mPointersDown &= ~(1 << pointerId);
-    }
-
-    private void ensureMotionHistorySizeForId(int pointerId) {
-        if (mInitialMotionX == null || mInitialMotionX.length <= pointerId) {
-            float[] imx = new float[pointerId + 1];
-            float[] imy = new float[pointerId + 1];
-            float[] lmx = new float[pointerId + 1];
-            float[] lmy = new float[pointerId + 1];
-            int[] iit = new int[pointerId + 1];
-            int[] edip = new int[pointerId + 1];
-            int[] edl = new int[pointerId + 1];
-
-            if (mInitialMotionX != null) {
-                System.arraycopy(mInitialMotionX, 0, imx, 0, mInitialMotionX.length);
-                System.arraycopy(mInitialMotionY, 0, imy, 0, mInitialMotionY.length);
-                System.arraycopy(mLastMotionX, 0, lmx, 0, mLastMotionX.length);
-                System.arraycopy(mLastMotionY, 0, lmy, 0, mLastMotionY.length);
-                System.arraycopy(mInitialEdgesTouched, 0, iit, 0, mInitialEdgesTouched.length);
-                System.arraycopy(mEdgeDragsInProgress, 0, edip, 0, mEdgeDragsInProgress.length);
-                System.arraycopy(mEdgeDragsLocked, 0, edl, 0, mEdgeDragsLocked.length);
-            }
-
-            mInitialMotionX = imx;
-            mInitialMotionY = imy;
-            mLastMotionX = lmx;
-            mLastMotionY = lmy;
-            mInitialEdgesTouched = iit;
-            mEdgeDragsInProgress = edip;
-            mEdgeDragsLocked = edl;
-        }
-    }
-
-    private void saveInitialMotion(float x, float y, int pointerId) {
-        ensureMotionHistorySizeForId(pointerId);
-        mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;
-        mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;
-        mInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);
-        mPointersDown |= 1 << pointerId;
-    }
-
-    private void saveLastMotion(MotionEvent ev) {
-        final int pointerCount = ev.getPointerCount();
-        for (int i = 0; i < pointerCount; i++) {
-            final int pointerId = ev.getPointerId(i);
-            // If pointer is invalid then skip saving on ACTION_MOVE.
-            if (!isValidPointerForActionMove(pointerId)) {
-                continue;
-            }
-            final float x = ev.getX(i);
-            final float y = ev.getY(i);
-            mLastMotionX[pointerId] = x;
-            mLastMotionY[pointerId] = y;
-        }
-    }
-
-    /**
-     * Check if the given pointer ID represents a pointer that is currently down (to the best
-     * of the ViewDragHelper's knowledge).
-     *
-     * <p>The state used to report this information is populated by the methods
-     * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or
-     * {@link #processTouchEvent(android.view.MotionEvent)}. If one of these methods has not
-     * been called for all relevant MotionEvents to track, the information reported
-     * by this method may be stale or incorrect.</p>
-     *
-     * @param pointerId pointer ID to check; corresponds to IDs provided by MotionEvent
-     * @return true if the pointer with the given ID is still down
-     */
-    public boolean isPointerDown(int pointerId) {
-        return (mPointersDown & 1 << pointerId) != 0;
-    }
-
-    void setDragState(int state) {
-        mParentView.removeCallbacks(mSetIdleRunnable);
-        if (mDragState != state) {
-            mDragState = state;
-            mCallback.onViewDragStateChanged(state);
-            if (mDragState == STATE_IDLE) {
-                mCapturedView = null;
-            }
-        }
-    }
-
-    /**
-     * Attempt to capture the view with the given pointer ID. The callback will be involved.
-     * This will put us into the "dragging" state. If we've already captured this view with
-     * this pointer this method will immediately return true without consulting the callback.
-     *
-     * @param toCapture View to capture
-     * @param pointerId Pointer to capture with
-     * @return true if capture was successful
-     */
-    boolean tryCaptureViewForDrag(View toCapture, int pointerId) {
-        if (toCapture == mCapturedView && mActivePointerId == pointerId) {
-            // Already done!
-            return true;
-        }
-        if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) {
-            mActivePointerId = pointerId;
-            captureChildView(toCapture, pointerId);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Tests scrollability within child views of v given a delta of dx.
-     *
-     * @param v View to test for horizontal scrollability
-     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
-     *               or just its children (false).
-     * @param dx Delta scrolled in pixels along the X axis
-     * @param dy Delta scrolled in pixels along the Y axis
-     * @param x X coordinate of the active touch point
-     * @param y Y coordinate of the active touch point
-     * @return true if child views of v can be scrolled by delta of dx.
-     */
-    protected boolean canScroll(@NonNull View v, boolean checkV, int dx, int dy, int x, int 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();
-            // Count backwards - let topmost views consume scroll distance first.
-            for (int i = count - 1; i >= 0; i--) {
-                // TODO: Add versioned support here for transformed views.
-                // This will not work for transformed views in Honeycomb+
-                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, dy, x + scrollX - child.getLeft(),
-                                y + scrollY - child.getTop())) {
-                    return true;
-                }
-            }
-        }
-
-        return checkV && (v.canScrollHorizontally(-dx) || v.canScrollVertically(-dy));
-    }
-
-    /**
-     * Check if this event as provided to the parent view's onInterceptTouchEvent should
-     * cause the parent to intercept the touch event stream.
-     *
-     * @param ev MotionEvent provided to onInterceptTouchEvent
-     * @return true if the parent view should return true from onInterceptTouchEvent
-     */
-    public boolean shouldInterceptTouchEvent(@NonNull MotionEvent 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
-            // the whole previous stream.
-            cancel();
-        }
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                final int pointerId = ev.getPointerId(0);
-                saveInitialMotion(x, y, pointerId);
-
-                final View toCapture = findTopChildUnder((int) x, (int) y);
-
-                // Catch a settling view if possible.
-                if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {
-                    tryCaptureViewForDrag(toCapture, pointerId);
-                }
-
-                final int edgesTouched = mInitialEdgesTouched[pointerId];
-                if ((edgesTouched & mTrackingEdges) != 0) {
-                    mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int pointerId = ev.getPointerId(actionIndex);
-                final float x = ev.getX(actionIndex);
-                final float y = ev.getY(actionIndex);
-
-                saveInitialMotion(x, y, pointerId);
-
-                // A ViewDragHelper can only manipulate one view at a time.
-                if (mDragState == STATE_IDLE) {
-                    final int edgesTouched = mInitialEdgesTouched[pointerId];
-                    if ((edgesTouched & mTrackingEdges) != 0) {
-                        mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-                    }
-                } else if (mDragState == STATE_SETTLING) {
-                    // Catch a settling view if possible.
-                    final View toCapture = findTopChildUnder((int) x, (int) y);
-                    if (toCapture == mCapturedView) {
-                        tryCaptureViewForDrag(toCapture, pointerId);
-                    }
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_MOVE: {
-                if (mInitialMotionX == null || mInitialMotionY == null) break;
-
-                // First to cross a touch slop over a draggable view wins. Also report edge drags.
-                final int pointerCount = ev.getPointerCount();
-                for (int i = 0; i < pointerCount; i++) {
-                    final int pointerId = ev.getPointerId(i);
-
-                    // If pointer is invalid then skip the ACTION_MOVE.
-                    if (!isValidPointerForActionMove(pointerId)) continue;
-
-                    final float x = ev.getX(i);
-                    final float y = ev.getY(i);
-                    final float dx = x - mInitialMotionX[pointerId];
-                    final float dy = y - mInitialMotionY[pointerId];
-
-                    final View toCapture = findTopChildUnder((int) x, (int) y);
-                    final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy);
-                    if (pastSlop) {
-                        // check the callback's
-                        // getView[Horizontal|Vertical]DragRange methods to know
-                        // if you can move at all along an axis, then see if it
-                        // would clamp to the same value. If you can't move at
-                        // all in every dimension with a nonzero range, bail.
-                        final int oldLeft = toCapture.getLeft();
-                        final int targetLeft = oldLeft + (int) dx;
-                        final int newLeft = mCallback.clampViewPositionHorizontal(toCapture,
-                                targetLeft, (int) dx);
-                        final int oldTop = toCapture.getTop();
-                        final int targetTop = oldTop + (int) dy;
-                        final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,
-                                (int) dy);
-                        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;
-                        }
-                    }
-                    reportNewEdgeDrags(dx, dy, pointerId);
-                    if (mDragState == STATE_DRAGGING) {
-                        // Callback might have started an edge drag
-                        break;
-                    }
-
-                    if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) {
-                        break;
-                    }
-                }
-                saveLastMotion(ev);
-                break;
-            }
-
-            case MotionEvent.ACTION_POINTER_UP: {
-                final int pointerId = ev.getPointerId(actionIndex);
-                clearMotionHistory(pointerId);
-                break;
-            }
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                cancel();
-                break;
-            }
-        }
-
-        return mDragState == STATE_DRAGGING;
-    }
-
-    /**
-     * Process a touch event received by the parent view. This method will dispatch callback events
-     * as needed before returning. The parent view's onTouchEvent implementation should call this.
-     *
-     * @param ev The touch event received by the parent view
-     */
-    public void processTouchEvent(@NonNull MotionEvent 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
-            // the whole previous stream.
-            cancel();
-        }
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(ev);
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                final int pointerId = ev.getPointerId(0);
-                final View toCapture = findTopChildUnder((int) x, (int) y);
-
-                saveInitialMotion(x, y, pointerId);
-
-                // Since the parent is already directly processing this touch event,
-                // there is no reason to delay for a slop before dragging.
-                // Start immediately if possible.
-                tryCaptureViewForDrag(toCapture, pointerId);
-
-                final int edgesTouched = mInitialEdgesTouched[pointerId];
-                if ((edgesTouched & mTrackingEdges) != 0) {
-                    mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int pointerId = ev.getPointerId(actionIndex);
-                final float x = ev.getX(actionIndex);
-                final float y = ev.getY(actionIndex);
-
-                saveInitialMotion(x, y, pointerId);
-
-                // A ViewDragHelper can only manipulate one view at a time.
-                if (mDragState == STATE_IDLE) {
-                    // If we're idle we can do anything! Treat it like a normal down event.
-
-                    final View toCapture = findTopChildUnder((int) x, (int) y);
-                    tryCaptureViewForDrag(toCapture, pointerId);
-
-                    final int edgesTouched = mInitialEdgesTouched[pointerId];
-                    if ((edgesTouched & mTrackingEdges) != 0) {
-                        mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
-                    }
-                } else if (isCapturedViewUnder((int) x, (int) y)) {
-                    // We're still tracking a captured view. If the same view is under this
-                    // point, we'll swap to controlling it with this pointer instead.
-                    // (This will still work if we're "catching" a settling view.)
-
-                    tryCaptureViewForDrag(mCapturedView, pointerId);
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_MOVE: {
-                if (mDragState == STATE_DRAGGING) {
-                    // If pointer is invalid then skip the ACTION_MOVE.
-                    if (!isValidPointerForActionMove(mActivePointerId)) break;
-
-                    final int index = ev.findPointerIndex(mActivePointerId);
-                    final float x = ev.getX(index);
-                    final float y = ev.getY(index);
-                    final int idx = (int) (x - mLastMotionX[mActivePointerId]);
-                    final int idy = (int) (y - mLastMotionY[mActivePointerId]);
-
-                    dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);
-
-                    saveLastMotion(ev);
-                } else {
-                    // Check to see if any pointer is now over a draggable view.
-                    final int pointerCount = ev.getPointerCount();
-                    for (int i = 0; i < pointerCount; i++) {
-                        final int pointerId = ev.getPointerId(i);
-
-                        // If pointer is invalid then skip the ACTION_MOVE.
-                        if (!isValidPointerForActionMove(pointerId)) continue;
-
-                        final float x = ev.getX(i);
-                        final float y = ev.getY(i);
-                        final float dx = x - mInitialMotionX[pointerId];
-                        final float dy = y - mInitialMotionY[pointerId];
-
-                        reportNewEdgeDrags(dx, dy, pointerId);
-                        if (mDragState == STATE_DRAGGING) {
-                            // Callback might have started an edge drag.
-                            break;
-                        }
-
-                        final View toCapture = findTopChildUnder((int) x, (int) y);
-                        if (checkTouchSlop(toCapture, dx, dy)
-                                && tryCaptureViewForDrag(toCapture, pointerId)) {
-                            break;
-                        }
-                    }
-                    saveLastMotion(ev);
-                }
-                break;
-            }
-
-            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.
-                    int newActivePointer = INVALID_POINTER;
-                    final int pointerCount = ev.getPointerCount();
-                    for (int i = 0; i < pointerCount; i++) {
-                        final int id = ev.getPointerId(i);
-                        if (id == mActivePointerId) {
-                            // This one's going away, skip.
-                            continue;
-                        }
-
-                        final float x = ev.getX(i);
-                        final float y = ev.getY(i);
-                        if (findTopChildUnder((int) x, (int) y) == mCapturedView
-                                && tryCaptureViewForDrag(mCapturedView, id)) {
-                            newActivePointer = mActivePointerId;
-                            break;
-                        }
-                    }
-
-                    if (newActivePointer == INVALID_POINTER) {
-                        // We didn't find another pointer still touching the view, release it.
-                        releaseViewForPointerUp();
-                    }
-                }
-                clearMotionHistory(pointerId);
-                break;
-            }
-
-            case MotionEvent.ACTION_UP: {
-                if (mDragState == STATE_DRAGGING) {
-                    releaseViewForPointerUp();
-                }
-                cancel();
-                break;
-            }
-
-            case MotionEvent.ACTION_CANCEL: {
-                if (mDragState == STATE_DRAGGING) {
-                    dispatchViewReleased(0, 0);
-                }
-                cancel();
-                break;
-            }
-        }
-    }
-
-    private void reportNewEdgeDrags(float dx, float dy, int pointerId) {
-        int dragsStarted = 0;
-        if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) {
-            dragsStarted |= EDGE_LEFT;
-        }
-        if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) {
-            dragsStarted |= EDGE_TOP;
-        }
-        if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) {
-            dragsStarted |= EDGE_RIGHT;
-        }
-        if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) {
-            dragsStarted |= EDGE_BOTTOM;
-        }
-
-        if (dragsStarted != 0) {
-            mEdgeDragsInProgress[pointerId] |= dragsStarted;
-            mCallback.onEdgeDragStarted(dragsStarted, pointerId);
-        }
-    }
-
-    private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) {
-        final float absDelta = Math.abs(delta);
-        final float absODelta = Math.abs(odelta);
-
-        if ((mInitialEdgesTouched[pointerId] & edge) != edge  || (mTrackingEdges & edge) == 0
-                || (mEdgeDragsLocked[pointerId] & edge) == edge
-                || (mEdgeDragsInProgress[pointerId] & edge) == edge
-                || (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) {
-            return false;
-        }
-        if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) {
-            mEdgeDragsLocked[pointerId] |= edge;
-            return false;
-        }
-        return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop;
-    }
-
-    /**
-     * Check if we've crossed a reasonable touch slop for the given child view.
-     * If the child cannot be dragged along the horizontal or vertical axis, motion
-     * along that axis will not count toward the slop check.
-     *
-     * @param child Child to check
-     * @param dx Motion since initial position along X axis
-     * @param dy Motion since initial position along Y axis
-     * @return true if the touch slop has been crossed
-     */
-    private boolean checkTouchSlop(View child, float dx, float dy) {
-        if (child == null) {
-            return false;
-        }
-        final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0;
-        final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0;
-
-        if (checkHorizontal && checkVertical) {
-            return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
-        } else if (checkHorizontal) {
-            return Math.abs(dx) > mTouchSlop;
-        } else if (checkVertical) {
-            return Math.abs(dy) > mTouchSlop;
-        }
-        return false;
-    }
-
-    /**
-     * Check if any pointer tracked in the current gesture has crossed
-     * the required slop threshold.
-     *
-     * <p>This depends on internal state populated by
-     * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or
-     * {@link #processTouchEvent(android.view.MotionEvent)}. You should only rely on
-     * the results of this method after all currently available touch data
-     * has been provided to one of these two methods.</p>
-     *
-     * @param directions Combination of direction flags, see {@link #DIRECTION_HORIZONTAL},
-     *                   {@link #DIRECTION_VERTICAL}, {@link #DIRECTION_ALL}
-     * @return true if the slop threshold has been crossed, false otherwise
-     */
-    public boolean checkTouchSlop(int directions) {
-        final int count = mInitialMotionX.length;
-        for (int i = 0; i < count; i++) {
-            if (checkTouchSlop(directions, i)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Check if the specified pointer tracked in the current gesture has crossed
-     * the required slop threshold.
-     *
-     * <p>This depends on internal state populated by
-     * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or
-     * {@link #processTouchEvent(android.view.MotionEvent)}. You should only rely on
-     * the results of this method after all currently available touch data
-     * has been provided to one of these two methods.</p>
-     *
-     * @param directions Combination of direction flags, see {@link #DIRECTION_HORIZONTAL},
-     *                   {@link #DIRECTION_VERTICAL}, {@link #DIRECTION_ALL}
-     * @param pointerId ID of the pointer to slop check as specified by MotionEvent
-     * @return true if the slop threshold has been crossed, false otherwise
-     */
-    public boolean checkTouchSlop(int directions, int pointerId) {
-        if (!isPointerDown(pointerId)) {
-            return false;
-        }
-
-        final boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL;
-        final boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL;
-
-        final float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId];
-        final float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId];
-
-        if (checkHorizontal && checkVertical) {
-            return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
-        } else if (checkHorizontal) {
-            return Math.abs(dx) > mTouchSlop;
-        } else if (checkVertical) {
-            return Math.abs(dy) > mTouchSlop;
-        }
-        return false;
-    }
-
-    /**
-     * Check if any of the edges specified were initially touched in the currently active gesture.
-     * If there is no currently active gesture this method will return false.
-     *
-     * @param edges Edges to check for an initial edge touch. See {@link #EDGE_LEFT},
-     *              {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, {@link #EDGE_BOTTOM} and
-     *              {@link #EDGE_ALL}
-     * @return true if any of the edges specified were initially touched in the current gesture
-     */
-    public boolean isEdgeTouched(int edges) {
-        final int count = mInitialEdgesTouched.length;
-        for (int i = 0; i < count; i++) {
-            if (isEdgeTouched(edges, i)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Check if any of the edges specified were initially touched by the pointer with
-     * the specified ID. If there is no currently active gesture or if there is no pointer with
-     * the given ID currently down this method will return false.
-     *
-     * @param edges Edges to check for an initial edge touch. See {@link #EDGE_LEFT},
-     *              {@link #EDGE_TOP}, {@link #EDGE_RIGHT}, {@link #EDGE_BOTTOM} and
-     *              {@link #EDGE_ALL}
-     * @return true if any of the edges specified were initially touched in the current gesture
-     */
-    public boolean isEdgeTouched(int edges, int pointerId) {
-        return isPointerDown(pointerId) && (mInitialEdgesTouched[pointerId] & edges) != 0;
-    }
-
-    private void releaseViewForPointerUp() {
-        mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
-        final float xvel = clampMag(
-                mVelocityTracker.getXVelocity(mActivePointerId),
-                mMinVelocity, mMaxVelocity);
-        final float yvel = clampMag(
-                mVelocityTracker.getYVelocity(mActivePointerId),
-                mMinVelocity, mMaxVelocity);
-        dispatchViewReleased(xvel, yvel);
-    }
-
-    private void dragTo(int left, int top, int dx, int dy) {
-        int clampedX = left;
-        int clampedY = top;
-        final int oldLeft = mCapturedView.getLeft();
-        final int oldTop = mCapturedView.getTop();
-        if (dx != 0) {
-            clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
-            ViewCompat.offsetLeftAndRight(mCapturedView, clampedX - oldLeft);
-        }
-        if (dy != 0) {
-            clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
-            ViewCompat.offsetTopAndBottom(mCapturedView, clampedY - oldTop);
-        }
-
-        if (dx != 0 || dy != 0) {
-            final int clampedDx = clampedX - oldLeft;
-            final int clampedDy = clampedY - oldTop;
-            mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,
-                    clampedDx, clampedDy);
-        }
-    }
-
-    /**
-     * Determine if the currently captured view is under the given point in the
-     * parent view's coordinate system. If there is no captured view this method
-     * will return false.
-     *
-     * @param x X position to test in the parent's coordinate system
-     * @param y Y position to test in the parent's coordinate system
-     * @return true if the captured view is under the given point, false otherwise
-     */
-    public boolean isCapturedViewUnder(int x, int y) {
-        return isViewUnder(mCapturedView, x, y);
-    }
-
-    /**
-     * Determine if the supplied view is under the given point in the
-     * parent view's coordinate system.
-     *
-     * @param view Child view of the parent to hit test
-     * @param x X position to test in the parent's coordinate system
-     * @param y Y position to test in the parent's coordinate system
-     * @return true if the supplied view is under the given point, false otherwise
-     */
-    public boolean isViewUnder(@Nullable View view, int x, int y) {
-        if (view == null) {
-            return false;
-        }
-        return x >= view.getLeft()
-                && x < view.getRight()
-                && y >= view.getTop()
-                && y < view.getBottom();
-    }
-
-    /**
-     * Find the topmost child under the given point within the parent view's coordinate system.
-     * The child order is determined using {@link Callback#getOrderedChildIndex(int)}.
-     *
-     * @param x X position to test in the parent's coordinate system
-     * @param y Y position to test in the parent's coordinate system
-     * @return The topmost child view under (x, y) or null if none found.
-     */
-    @Nullable
-    public View findTopChildUnder(int x, int y) {
-        final int childCount = mParentView.getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
-            if (x >= child.getLeft() && x < child.getRight()
-                    && y >= child.getTop() && y < child.getBottom()) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    private int getEdgesTouched(int x, int y) {
-        int result = 0;
-
-        if (x < mParentView.getLeft() + mEdgeSize) result |= EDGE_LEFT;
-        if (y < mParentView.getTop() + mEdgeSize) result |= EDGE_TOP;
-        if (x > mParentView.getRight() - mEdgeSize) result |= EDGE_RIGHT;
-        if (y > mParentView.getBottom() - mEdgeSize) result |= EDGE_BOTTOM;
-
-        return result;
-    }
-
-    private boolean isValidPointerForActionMove(int pointerId) {
-        if (!isPointerDown(pointerId)) {
-            Log.e(TAG, "Ignoring pointerId=" + pointerId + " because ACTION_DOWN was not received "
-                    + "for this pointer before ACTION_MOVE. It likely happened because "
-                    + " ViewDragHelper did not receive all the events in the event stream.");
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/android/support/v4/widget/ViewGroupUtils.java b/android/support/v4/widget/ViewGroupUtils.java
deleted file mode 100644
index 986b4c2..0000000
--- a/android/support/v4/widget/ViewGroupUtils.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.support.annotation.RestrictTo;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY)
-public class ViewGroupUtils {
-    private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
-    private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
-
-    /**
-     * This is a port of the common
-     * {@link ViewGroup#offsetDescendantRectToMyCoords(View, Rect)}
-     * from the framework, but adapted to take transformations into account. The result
-     * will be the bounding rect of the real transformed rect.
-     *
-     * @param descendant view defining the original coordinate system of rect
-     * @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) {
-        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));
-    }
-
-    /**
-     * Retrieve the transformed bounding rect of an arbitrary descendant view.
-     * This does not need to be a direct child.
-     *
-     * @param descendant descendant view to reference
-     * @param out rect to set to the bounds of the descendant view
-     */
-    public static void getDescendantRect(ViewGroup parent, View descendant, Rect out) {
-        out.set(0, 0, descendant.getWidth(), descendant.getHeight());
-        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/android/support/v7/app/ActionBar.java b/android/support/v7/app/ActionBar.java
deleted file mode 100644
index 5c40908..0000000
--- a/android/support/v7/app/ActionBar.java
+++ /dev/null
@@ -1,1407 +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;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.view.GravityCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ActionMode;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.SpinnerAdapter;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A primary toolbar within the activity that may display the activity title, application-level
- * navigation affordances, and other interactive items.
- *
- * <p>The action bar appears at the top of an activity's window when the activity uses the
- * AppCompat's {@link R.style#Theme_AppCompat AppCompat} theme (or one of its descendant themes).
- * You may otherwise add the action bar by calling {@link
- * AppCompatDelegate#requestWindowFeature(int)  requestFeature(FEATURE_SUPPORT_ACTION_BAR)} or by
- * declaring it in a custom theme with the
- * {@link R.styleable#AppCompatTheme_windowActionBar windowActionBar} property.</p>
- *
- * <p>The action bar may be represented by any Toolbar widget within the application layout.
- * The application may signal to the Activity which Toolbar should be treated as the Activity's
- * action bar. Activities that use this feature should use one of the supplied
- * <code>.NoActionBar</code> themes, set the
- * {@link R.styleable#AppCompatTheme_windowActionBar windowActionBar} attribute to
- * <code>false</code> or otherwise not request the window feature.</p>
- *
- * <p>If your activity has an options menu, you can make select items accessible directly from the
- * action bar as "action items". You can also  modify various characteristics of the action bar or
- * remove it completely.</p>
- *
- * <p>The navigation button (formerly "Home") takes over the space previously occupied by the
- * application icon. Apps wishing to express a stronger branding should use their brand colors
- * heavily in the action bar and other application chrome or use a {@link #setLogo(int) logo}
- * in place of their standard title text.</p>
- *
- * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link
- * AppCompatActivity#getSupportActionBar()}  getSupportActionBar()}.</p>
- *
- * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions,
- * using an {@link ActionMode}. For example, when the user selects one or more items in
- * your activity, you can enable an action mode that offers actions specific to the selected
- * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the
- * same space, the {@link ActionMode} APIs are distinct and independent from those for
- * {@link ActionBar}.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about how to use the action bar, including how to add action items, navigation
- * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
- * Bar</a> developer guide.</p>
- * </div>
- */
-public abstract class ActionBar {
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
-    public @interface NavigationMode {}
-
-    /**
-     * Standard navigation mode. Consists of either a logo or icon
-     * and title text with an optional subtitle. Clicking any of these elements
-     * will dispatch onOptionsItemSelected to the host Activity with
-     * a MenuItem with item ID android.R.id.home.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public static final int NAVIGATION_MODE_STANDARD = 0;
-
-    /**
-     * List navigation mode. Instead of static title text this mode
-     * presents a list menu for navigation within the activity.
-     * e.g. this might be presented to the user as a dropdown list.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public static final int NAVIGATION_MODE_LIST = 1;
-
-    /**
-     * Tab navigation mode. Instead of static title text this mode
-     * presents a series of tabs for navigation within the activity.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public static final int NAVIGATION_MODE_TABS = 2;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef(flag=true, value={
-            DISPLAY_USE_LOGO,
-            DISPLAY_SHOW_HOME,
-            DISPLAY_HOME_AS_UP,
-            DISPLAY_SHOW_TITLE,
-            DISPLAY_SHOW_CUSTOM
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DisplayOptions {}
-
-    /**
-     * Use logo instead of icon if available. This flag will cause appropriate
-     * navigation modes to use a wider logo in place of the standard icon.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public static final int DISPLAY_USE_LOGO = android.app.ActionBar.DISPLAY_USE_LOGO;
-
-    /**
-     * Show 'home' elements in this action bar, leaving more space for other
-     * navigation elements. This includes logo and icon.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public static final int DISPLAY_SHOW_HOME = android.app.ActionBar.DISPLAY_SHOW_HOME;
-
-    /**
-     * Display the 'home' element such that it appears as an 'up' affordance.
-     * e.g. show an arrow to the left indicating the action that will be taken.
-     *
-     * Set this flag if selecting the 'home' button in the action bar to return
-     * up by a single level in your UI rather than back to the top level or front page.
-     *
-     * <p>Setting this option will implicitly enable interaction with the home/up
-     * button. See {@link #setHomeButtonEnabled(boolean)}.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public static final int DISPLAY_HOME_AS_UP = android.app.ActionBar.DISPLAY_HOME_AS_UP;
-
-    /**
-     * Show the activity title and subtitle, if present.
-     *
-     * @see #setTitle(CharSequence)
-     * @see #setTitle(int)
-     * @see #setSubtitle(CharSequence)
-     * @see #setSubtitle(int)
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public static final int DISPLAY_SHOW_TITLE = android.app.ActionBar.DISPLAY_SHOW_TITLE;
-
-    /**
-     * Show the custom view if one has been set.
-     *
-     * @see #setCustomView(View)
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public static final int DISPLAY_SHOW_CUSTOM = android.app.ActionBar.DISPLAY_SHOW_CUSTOM;
-
-    /**
-     * Set the action bar into custom navigation mode, supplying a view
-     * for custom navigation.
-     *
-     * Custom navigation views appear between the application icon and
-     * any action buttons and may use any space available there. Common
-     * use cases for custom navigation views might include an auto-suggesting
-     * address bar for a browser or other navigation mechanisms that do not
-     * translate well to provided navigation modes.
-     *
-     * @param view Custom navigation view to place in the ActionBar.
-     */
-    public abstract void setCustomView(View view);
-
-    /**
-     * Set the action bar into custom navigation mode, supplying a view
-     * for custom navigation.
-     *
-     * <p>Custom navigation views appear between the application icon and
-     * any action buttons and may use any space available there. Common
-     * use cases for custom navigation views might include an auto-suggesting
-     * address bar for a browser or other navigation mechanisms that do not
-     * translate well to provided navigation modes.</p>
-     *
-     * <p>The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for
-     * the custom view to be displayed.</p>
-     *
-     * @param view Custom navigation view to place in the ActionBar.
-     * @param layoutParams How this custom view should layout in the bar.
-     *
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setCustomView(View view, LayoutParams layoutParams);
-
-    /**
-     * Set the action bar into custom navigation mode, supplying a view
-     * for custom navigation.
-     *
-     * <p>Custom navigation views appear between the application icon and
-     * any action buttons and may use any space available there. Common
-     * use cases for custom navigation views might include an auto-suggesting
-     * address bar for a browser or other navigation mechanisms that do not
-     * translate well to provided navigation modes.</p>
-     *
-     * <p>The display option {@link #DISPLAY_SHOW_CUSTOM} must be set for
-     * the custom view to be displayed.</p>
-     *
-     * @param resId Resource ID of a layout to inflate into the ActionBar.
-     *
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setCustomView(int resId);
-
-    /**
-     * Set the icon to display in the 'home' section of the action bar.
-     * The action bar will use an icon specified by its style or the
-     * activity icon by default.
-     *
-     * Whether the home section shows an icon or logo is controlled
-     * by the display option {@link #DISPLAY_USE_LOGO}.
-     *
-     * @param resId Resource ID of a drawable to show as an icon.
-     *
-     * @see #setDisplayUseLogoEnabled(boolean)
-     * @see #setDisplayShowHomeEnabled(boolean)
-     */
-    public abstract void setIcon(@DrawableRes int resId);
-
-    /**
-     * Set the icon to display in the 'home' section of the action bar.
-     * The action bar will use an icon specified by its style or the
-     * activity icon by default.
-     *
-     * Whether the home section shows an icon or logo is controlled
-     * by the display option {@link #DISPLAY_USE_LOGO}.
-     *
-     * @param icon Drawable to show as an icon.
-     *
-     * @see #setDisplayUseLogoEnabled(boolean)
-     * @see #setDisplayShowHomeEnabled(boolean)
-     */
-    public abstract void setIcon(Drawable icon);
-
-    /**
-     * Set the logo to display in the 'home' section of the action bar.
-     * The action bar will use a logo specified by its style or the
-     * activity logo by default.
-     *
-     * Whether the home section shows an icon or logo is controlled
-     * by the display option {@link #DISPLAY_USE_LOGO}.
-     *
-     * @param resId Resource ID of a drawable to show as a logo.
-     *
-     * @see #setDisplayUseLogoEnabled(boolean)
-     * @see #setDisplayShowHomeEnabled(boolean)
-     */
-    public abstract void setLogo(@DrawableRes int resId);
-
-    /**
-     * Set the logo to display in the 'home' section of the action bar.
-     * The action bar will use a logo specified by its style or the
-     * activity logo by default.
-     *
-     * Whether the home section shows an icon or logo is controlled
-     * by the display option {@link #DISPLAY_USE_LOGO}.
-     *
-     * @param logo Drawable to show as a logo.
-     *
-     * @see #setDisplayUseLogoEnabled(boolean)
-     * @see #setDisplayShowHomeEnabled(boolean)
-     */
-    public abstract void setLogo(Drawable logo);
-
-    /**
-     * Set the adapter and navigation callback for list navigation mode.
-     *
-     * The supplied adapter will provide views for the expanded list as well as
-     * the currently selected item. (These may be displayed differently.)
-     *
-     * The supplied OnNavigationListener will alert the application when the user
-     * changes the current list selection.
-     *
-     * @param adapter An adapter that will provide views both to display
-     *                the current navigation selection and populate views
-     *                within the dropdown navigation menu.
-     * @param callback An OnNavigationListener that will receive events when the user
-     *                 selects a navigation item.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
-            OnNavigationListener callback);
-
-    /**
-     * Set the selected navigation item in list or tabbed navigation modes.
-     *
-     * @param position Position of the item to select.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void setSelectedNavigationItem(int position);
-
-    /**
-     * Get the position of the selected navigation item in list or tabbed navigation modes.
-     *
-     * @return Position of the selected item.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract int getSelectedNavigationIndex();
-
-    /**
-     * Get the number of navigation items present in the current navigation mode.
-     *
-     * @return Number of navigation items.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract int getNavigationItemCount();
-
-    /**
-     * Set the action bar's title. This will only be displayed if
-     * {@link #DISPLAY_SHOW_TITLE} is set.
-     *
-     * @param title Title to set
-     *
-     * @see #setTitle(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setTitle(CharSequence title);
-
-    /**
-     * Set the action bar's title. This will only be displayed if
-     * {@link #DISPLAY_SHOW_TITLE} is set.
-     *
-     * @param resId Resource ID of title string to set
-     *
-     * @see #setTitle(CharSequence)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setTitle(@StringRes int resId);
-
-    /**
-     * Set the action bar's subtitle. This will only be displayed if
-     * {@link #DISPLAY_SHOW_TITLE} is set. Set to null to disable the
-     * subtitle entirely.
-     *
-     * @param subtitle Subtitle to set
-     *
-     * @see #setSubtitle(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setSubtitle(CharSequence subtitle);
-
-    /**
-     * Set the action bar's subtitle. This will only be displayed if
-     * {@link #DISPLAY_SHOW_TITLE} is set.
-     *
-     * @param resId Resource ID of subtitle string to set
-     *
-     * @see #setSubtitle(CharSequence)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setSubtitle(int resId);
-
-    /**
-     * Set display options. This changes all display option bits at once. To change
-     * a limited subset of display options, see {@link #setDisplayOptions(int, int)}.
-     *
-     * @param options A combination of the bits defined by the DISPLAY_ constants
-     *                defined in ActionBar.
-     */
-    public abstract void setDisplayOptions(@DisplayOptions int options);
-
-    /**
-     * Set selected display options. Only the options specified by mask will be changed.
-     * To change all display option bits at once, see {@link #setDisplayOptions(int)}.
-     *
-     * <p>Example: setDisplayOptions(0, DISPLAY_SHOW_HOME) will disable the
-     * {@link #DISPLAY_SHOW_HOME} option.
-     * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_SHOW_HOME | DISPLAY_USE_LOGO)
-     * will enable {@link #DISPLAY_SHOW_HOME} and disable {@link #DISPLAY_USE_LOGO}.
-     *
-     * @param options A combination of the bits defined by the DISPLAY_ constants
-     *                defined in ActionBar.
-     * @param mask A bit mask declaring which display options should be changed.
-     */
-    public abstract void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask);
-
-    /**
-     * Set whether to display the activity logo rather than the activity icon.
-     * A logo is often a wider, more detailed image.
-     *
-     * <p>To set several display options at once, see the setDisplayOptions methods.
-     *
-     * @param useLogo true to use the activity logo, false to use the activity icon.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setDisplayUseLogoEnabled(boolean useLogo);
-
-    /**
-     * Set whether to include the application home affordance in the action bar.
-     * Home is presented as either an activity icon or logo.
-     *
-     * <p>To set several display options at once, see the setDisplayOptions methods.
-     *
-     * @param showHome true to show home, false otherwise.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setDisplayShowHomeEnabled(boolean showHome);
-
-    /**
-     * Set whether home should be displayed as an "up" affordance.
-     * Set this to true if selecting "home" returns up by a single level in your UI
-     * rather than back to the top level or front page.
-     *
-     * <p>To set several display options at once, see the setDisplayOptions methods.
-     *
-     * @param showHomeAsUp true to show the user that selecting home will return one
-     *                     level up rather than to the top level of the app.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setDisplayHomeAsUpEnabled(boolean showHomeAsUp);
-
-    /**
-     * Set whether an activity title/subtitle should be displayed.
-     *
-     * <p>To set several display options at once, see the setDisplayOptions methods.
-     *
-     * @param showTitle true to display a title/subtitle if present.
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setDisplayShowTitleEnabled(boolean showTitle);
-
-    /**
-     * Set whether a custom view should be displayed, if set.
-     *
-     * <p>To set several display options at once, see the setDisplayOptions methods.
-     *
-     * @param showCustom true if the currently set custom view should be displayed, false otherwise.
-     *
-     * @see #setDisplayOptions(int)
-     * @see #setDisplayOptions(int, int)
-     */
-    public abstract void setDisplayShowCustomEnabled(boolean showCustom);
-
-    /**
-     * Set the ActionBar's background. This will be used for the primary
-     * action bar.
-     *
-     * @param d Background drawable
-     * @see #setStackedBackgroundDrawable(Drawable)
-     * @see #setSplitBackgroundDrawable(Drawable)
-     */
-    public abstract void setBackgroundDrawable(@Nullable Drawable d);
-
-    /**
-     * Set the ActionBar's stacked background. This will appear
-     * in the second row/stacked bar on some devices and configurations.
-     *
-     * @param d Background drawable for the stacked row
-     */
-    public void setStackedBackgroundDrawable(Drawable d) { }
-
-    /**
-     * Set the ActionBar's split background. This will appear in
-     * the split action bar containing menu-provided action buttons
-     * on some devices and configurations.
-     * <p>You can enable split action bar with {@link android.R.attr#uiOptions}
-     *
-     * @param d Background drawable for the split bar
-     */
-    public void setSplitBackgroundDrawable(Drawable d) { }
-
-    /**
-     * @return The current custom view.
-     */
-    public abstract View getCustomView();
-
-    /**
-     * Returns the current ActionBar title in standard mode.
-     * Returns null if {@link #getNavigationMode()} would not return
-     * {@link #NAVIGATION_MODE_STANDARD}.
-     *
-     * @return The current ActionBar title or null.
-     */
-    @Nullable
-    public abstract CharSequence getTitle();
-
-    /**
-     * Returns the current ActionBar subtitle in standard mode.
-     * Returns null if {@link #getNavigationMode()} would not return
-     * {@link #NAVIGATION_MODE_STANDARD}.
-     *
-     * @return The current ActionBar subtitle or null.
-     */
-    @Nullable
-    public abstract CharSequence getSubtitle();
-
-    /**
-     * Returns the current navigation mode. The result will be one of:
-     * <ul>
-     * <li>{@link #NAVIGATION_MODE_STANDARD}</li>
-     * <li>{@link #NAVIGATION_MODE_LIST}</li>
-     * <li>{@link #NAVIGATION_MODE_TABS}</li>
-     * </ul>
-     *
-     * @return The current navigation mode.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    @NavigationMode
-    public abstract int getNavigationMode();
-
-    /**
-     * Set the current navigation mode.
-     *
-     * @param mode The new mode to set.
-     * @see #NAVIGATION_MODE_STANDARD
-     * @see #NAVIGATION_MODE_LIST
-     * @see #NAVIGATION_MODE_TABS
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void setNavigationMode(@NavigationMode int mode);
-
-    /**
-     * @return The current set of display options.
-     */
-    @DisplayOptions
-    public abstract int getDisplayOptions();
-
-    /**
-     * Create and return a new {@link Tab}.
-     * This tab will not be included in the action bar until it is added.
-     *
-     * @return A new Tab
-     *
-     * @see #addTab(Tab)
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract Tab newTab();
-
-    /**
-     * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
-     * If this is the first tab to be added it will become the selected tab.
-     *
-     * @param tab Tab to add
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void addTab(Tab tab);
-
-    /**
-     * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
-     *
-     * @param tab Tab to add
-     * @param setSelected True if the added tab should become the selected tab.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void addTab(Tab tab, boolean setSelected);
-
-    /**
-     * Add a tab for use in tabbed navigation mode. The tab will be inserted at
-     * <code>position</code>. If this is the first tab to be added it will become
-     * the selected tab.
-     *
-     * @param tab The tab to add
-     * @param position The new position of the tab
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void addTab(Tab tab, int position);
-
-    /**
-     * Add a tab for use in tabbed navigation mode. The tab will be inserted at
-     * <code>position</code>.
-     *
-     * @param tab The tab to add
-     * @param position The new position of the tab
-     * @param setSelected True if the added tab should become the selected tab.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void addTab(Tab tab, int position, boolean setSelected);
-
-    /**
-     * Remove a tab from the action bar. If the removed tab was selected it will be deselected
-     * and another tab will be selected if present.
-     *
-     * @param tab The tab to remove
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void removeTab(Tab tab);
-
-    /**
-     * Remove a tab from the action bar. If the removed tab was selected it will be deselected
-     * and another tab will be selected if present.
-     *
-     * @param position Position of the tab to remove
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void removeTabAt(int position);
-
-    /**
-     * Remove all tabs from the action bar and deselect the current tab.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void removeAllTabs();
-
-    /**
-     * Select the specified tab. If it is not a child of this action bar it will be added.
-     *
-     * <p>Note: If you want to select by index, use {@link #setSelectedNavigationItem(int)}.</p>
-     *
-     * @param tab Tab to select
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract void selectTab(Tab tab);
-
-    /**
-     * Returns the currently selected tab if in tabbed navigation mode and there is at least
-     * one tab present.
-     *
-     * @return The currently selected tab or null
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    @Nullable
-    public abstract Tab getSelectedTab();
-
-    /**
-     * Returns the tab at the specified index.
-     *
-     * @param index Index value in the range 0-get
-     * @return
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract Tab getTabAt(int index);
-
-    /**
-     * Returns the number of tabs currently registered with the action bar.
-     *
-     * @return Tab count
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public abstract int getTabCount();
-
-    /**
-     * Retrieve the current height of the ActionBar.
-     *
-     * @return The ActionBar's height
-     */
-    public abstract int getHeight();
-
-    /**
-     * Show the ActionBar if it is not currently showing.
-     * If the window hosting the ActionBar does not have the feature
-     * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
-     * content to fit the new space available.
-     *
-     * <p>If you are hiding the ActionBar through
-     * {@link View#SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_FULLSCREEN},
-     * you should not call this function directly.
-     */
-    public abstract void show();
-
-    /**
-     * Hide the ActionBar if it is currently showing.
-     * If the window hosting the ActionBar does not have the feature
-     * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
-     * content to fit the new space available.
-     *
-     * <p>Instead of calling this function directly, you can also cause an
-     * ActionBar using the overlay feature to hide through
-     * {@link View#SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_FULLSCREEN}.
-     * Hiding the ActionBar through this system UI flag allows you to more
-     * seamlessly hide it in conjunction with other screen decorations.
-     */
-    public abstract void hide();
-
-    /**
-     * @return <code>true</code> if the ActionBar is showing, <code>false</code> otherwise.
-     */
-    public abstract boolean isShowing();
-
-    /**
-     * Add a listener that will respond to menu visibility change events.
-     *
-     * @param listener The new listener to add
-     */
-    public abstract void addOnMenuVisibilityListener(OnMenuVisibilityListener listener);
-
-    /**
-     * Remove a menu visibility listener. This listener will no longer receive menu
-     * visibility change events.
-     *
-     * @param listener A listener to remove that was previously added
-     */
-    public abstract void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener);
-
-    /**
-     * Enable or disable the "home" button in the corner of the action bar. (Note that this
-     * is the application home/up affordance on the action bar, not the system wide home
-     * button.)
-     *
-     * <p>This defaults to true for packages targeting &lt; API 14. For packages targeting
-     * API 14 or greater, the application should call this method to enable interaction
-     * with the home/up affordance.
-     *
-     * <p>Setting the {@link #DISPLAY_HOME_AS_UP} display option will automatically enable
-     * the home button.
-     *
-     * @param enabled true to enable the home button, false to disable the home button.
-     */
-    public void setHomeButtonEnabled(boolean enabled) { }
-
-    /**
-     * Returns a {@link Context} with an appropriate theme for creating views that
-     * will appear in the action bar. If you are inflating or instantiating custom views
-     * that will appear in an action bar, you should use the Context returned by this method.
-     * (This includes adapters used for list navigation mode.)
-     * This will ensure that views contrast properly against the action bar.
-     *
-     * @return A themed Context for creating views
-     */
-    public Context getThemedContext() {
-        return null;
-    }
-
-    /**
-     * Returns true if the Title field has been truncated during layout for lack
-     * of available space.
-     *
-     * @return true if the Title field has been truncated
-     * @hide pending API approval
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isTitleTruncated() { return false; }
-
-    /**
-     * Set an alternate drawable to display next to the icon/logo/title
-     * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using
-     * this mode to display an alternate selection for up navigation, such as a sliding drawer.
-     *
-     * <p>If you pass <code>null</code> to this method, the default drawable from the theme
-     * will be used.</p>
-     *
-     * <p>If you implement alternate or intermediate behavior around Up, you should also
-     * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
-     * to provide a correct description of the action for accessibility support.</p>
-     *
-     * @param indicator A drawable to use for the up indicator, or null to use the theme's default
-     *
-     * @see #setDisplayOptions(int, int)
-     * @see #setDisplayHomeAsUpEnabled(boolean)
-     * @see #setHomeActionContentDescription(int)
-     */
-    public void setHomeAsUpIndicator(@Nullable Drawable indicator) {}
-
-    /**
-     * Set an alternate drawable to display next to the icon/logo/title
-     * when {@link #DISPLAY_HOME_AS_UP} is enabled. This can be useful if you are using
-     * this mode to display an alternate selection for up navigation, such as a sliding drawer.
-     *
-     * <p>If you pass <code>0</code> to this method, the default drawable from the theme
-     * will be used.</p>
-     *
-     * <p>If you implement alternate or intermediate behavior around Up, you should also
-     * call {@link #setHomeActionContentDescription(int) setHomeActionContentDescription()}
-     * to provide a correct description of the action for accessibility support.</p>
-     *
-     * @param resId Resource ID of a drawable to use for the up indicator, or null
-     *              to use the theme's default
-     *
-     * @see #setDisplayOptions(int, int)
-     * @see #setDisplayHomeAsUpEnabled(boolean)
-     * @see #setHomeActionContentDescription(int)
-     */
-    public void setHomeAsUpIndicator(@DrawableRes int resId) {}
-
-    /**
-     * Set an alternate description for the Home/Up action, when enabled.
-     *
-     * <p>This description is commonly used for accessibility/screen readers when
-     * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.)
-     * Examples of this are, "Navigate Home" or "Navigate Up" depending on the
-     * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up
-     * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific
-     * functionality such as a sliding drawer, you should also set this to accurately
-     * describe the action.</p>
-     *
-     * <p>Setting this to <code>null</code> will use the system default description.</p>
-     *
-     * @param description New description for the Home action when enabled
-     * @see #setHomeAsUpIndicator(int)
-     * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable)
-     */
-    public void setHomeActionContentDescription(@Nullable CharSequence description) {}
-
-    /**
-     * Set an alternate description for the Home/Up action, when enabled.
-     *
-     * <p>This description is commonly used for accessibility/screen readers when
-     * the Home action is enabled. (See {@link #setDisplayHomeAsUpEnabled(boolean)}.)
-     * Examples of this are, "Navigate Home" or "Navigate Up" depending on the
-     * {@link #DISPLAY_HOME_AS_UP} display option. If you have changed the home-as-up
-     * indicator using {@link #setHomeAsUpIndicator(int)} to indicate more specific
-     * functionality such as a sliding drawer, you should also set this to accurately
-     * describe the action.</p>
-     *
-     * <p>Setting this to <code>0</code> will use the system default description.</p>
-     *
-     * @param resId Resource ID of a string to use as the new description
-     *              for the Home action when enabled
-     * @see #setHomeAsUpIndicator(int)
-     * @see #setHomeAsUpIndicator(android.graphics.drawable.Drawable)
-     */
-    public void setHomeActionContentDescription(@StringRes int resId) {}
-
-    /**
-     * Enable hiding the action bar on content scroll.
-     *
-     * <p>If enabled, the action bar will scroll out of sight along with a
-     * {@link View#setNestedScrollingEnabled(boolean) nested scrolling child} view's content.
-     * The action bar must be in {@link Window#FEATURE_ACTION_BAR_OVERLAY overlay mode}
-     * to enable hiding on content scroll.</p>
-     *
-     * <p>When partially scrolled off screen the action bar is considered
-     * {@link #hide() hidden}. A call to {@link #show() show} will cause it to return to full view.
-     * </p>
-     * @param hideOnContentScroll true to enable hiding on content scroll.
-     */
-    public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
-        if (hideOnContentScroll) {
-            throw new UnsupportedOperationException("Hide on content scroll is not supported in " +
-                    "this action bar configuration.");
-        }
-    }
-
-    /**
-     * Return whether the action bar is configured to scroll out of sight along with
-     * a {@link View#setNestedScrollingEnabled(boolean) nested scrolling child}.
-     *
-     * @return true if hide-on-content-scroll is enabled
-     * @see #setHideOnContentScrollEnabled(boolean)
-     */
-    public boolean isHideOnContentScrollEnabled() {
-        return false;
-    }
-
-    /**
-     * Return the current vertical offset of the action bar.
-     *
-     * <p>The action bar's current hide offset is the distance that the action bar is currently
-     * scrolled offscreen in pixels. The valid range is 0 (fully visible) to the action bar's
-     * current measured {@link #getHeight() height} (fully invisible).</p>
-     *
-     * @return The action bar's offset toward its fully hidden state in pixels
-     */
-    public int getHideOffset() {
-        return 0;
-    }
-
-    /**
-     * Set the current hide offset of the action bar.
-     *
-     * <p>The action bar's current hide offset is the distance that the action bar is currently
-     * scrolled offscreen in pixels. The valid range is 0 (fully visible) to the action bar's
-     * current measured {@link #getHeight() height} (fully invisible).</p>
-     *
-     * @param offset The action bar's offset toward its fully hidden state in pixels.
-     */
-    public void setHideOffset(int offset) {
-        if (offset != 0) {
-            throw new UnsupportedOperationException("Setting an explicit action bar hide offset " +
-                    "is not supported in this action bar configuration.");
-        }
-    }
-
-    /**
-     * Set the Z-axis elevation of the action bar in pixels.
-     *
-     * <p>The action bar's elevation is the distance it is placed from its parent surface. Higher
-     * values are closer to the user.</p>
-     *
-     * @param elevation Elevation value in pixels
-     */
-    public void setElevation(float elevation) {
-        if (elevation != 0) {
-            throw new UnsupportedOperationException("Setting a non-zero elevation is " +
-                    "not supported in this action bar configuration.");
-        }
-    }
-
-    /**
-     * Get the Z-axis elevation of the action bar in pixels.
-     *
-     * <p>The action bar's elevation is the distance it is placed from its parent surface. Higher
-     * values are closer to the user.</p>
-     *
-     * @return Elevation value in pixels
-     */
-    public float getElevation() {
-        return 0;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) {
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setShowHideAnimationEnabled(boolean enabled) {
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void onConfigurationChanged(Configuration config) {
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void dispatchMenuVisibilityChanged(boolean visible) {
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public ActionMode startActionMode(ActionMode.Callback callback) {
-        return null;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean openOptionsMenu() {
-        return false;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean closeOptionsMenu() {
-        return false;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean invalidateOptionsMenu() {
-        return false;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean onMenuKeyEvent(KeyEvent event) {
-        return false;
-    }
-
-    /** @hide **/
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean onKeyShortcut(int keyCode, KeyEvent ev) {
-        return false;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean collapseActionView() {
-        return false;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setWindowTitle(CharSequence title) {
-    }
-
-    /**
-     * Attempts to move focus to the ActionBar if it does not already contain the focus.
-     *
-     * @return {@code true} if focus changes or {@code false} if focus doesn't change.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    boolean requestFocus() {
-        return false;
-    }
-
-    /**
-     * Clean up any resources
-     */
-    void onDestroy() {
-    }
-
-    /**
-     * Listener interface for ActionBar navigation events.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public interface OnNavigationListener {
-        /**
-         * This method is called whenever a navigation item in your action bar
-         * is selected.
-         *
-         * @param itemPosition Position of the item clicked.
-         * @param itemId ID of the item clicked.
-         * @return True if the event was handled, false otherwise.
-         */
-        public boolean onNavigationItemSelected(int itemPosition, long itemId);
-    }
-
-    /**
-     * Listener for receiving events when action bar menus are shown or hidden.
-     */
-    public interface OnMenuVisibilityListener {
-
-        /**
-         * Called when an action bar menu is shown or hidden. Applications may want to use
-         * this to tune auto-hiding behavior for the action bar or pause/resume video playback,
-         * gameplay, or other activity within the main content area.
-         *
-         * @param isVisible True if an action bar menu is now visible, false if no action bar
-         *                  menus are visible.
-         */
-        public void onMenuVisibilityChanged(boolean isVisible);
-    }
-
-    /**
-     * A tab in the action bar.
-     *
-     * <p>Tabs manage the hiding and showing of {@link Fragment}s.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public static abstract class Tab {
-
-        /**
-         * An invalid position for a tab.
-         *
-         * @see #getPosition()
-         */
-        public static final int INVALID_POSITION = -1;
-
-        /**
-         * Return the current position of this tab in the action bar.
-         *
-         * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
-         *         the action bar.
-         */
-        public abstract int getPosition();
-
-        /**
-         * Return the icon associated with this tab.
-         *
-         * @return The tab's icon
-         */
-        public abstract Drawable getIcon();
-
-        /**
-         * Return the text of this tab.
-         *
-         * @return The tab's text
-         */
-        public abstract CharSequence getText();
-
-        /**
-         * Set the icon displayed on this tab.
-         *
-         * @param icon The drawable to use as an icon
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setIcon(Drawable icon);
-
-        /**
-         * Set the icon displayed on this tab.
-         *
-         * @param resId Resource ID referring to the drawable to use as an icon
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setIcon(@DrawableRes int resId);
-
-        /**
-         * Set the text displayed on this tab. Text may be truncated if there is not
-         * room to display the entire string.
-         *
-         * @param text The text to display
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setText(CharSequence text);
-
-        /**
-         * Set the text displayed on this tab. Text may be truncated if there is not
-         * room to display the entire string.
-         *
-         * @param resId A resource ID referring to the text that should be displayed
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setText(int resId);
-
-        /**
-         * Set a custom view to be used for this tab. This overrides values set by
-         * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
-         *
-         * @param view Custom view to be used as a tab.
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setCustomView(View view);
-
-        /**
-         * Set a custom view to be used for this tab. This overrides values set by
-         * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
-         *
-         * @param layoutResId A layout resource to inflate and use as a custom tab view
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setCustomView(int layoutResId);
-
-        /**
-         * Retrieve a previously set custom view for this tab.
-         *
-         * @return The custom view set by {@link #setCustomView(View)}.
-         */
-        public abstract View getCustomView();
-
-        /**
-         * Give this Tab an arbitrary object to hold for later use.
-         *
-         * @param obj Object to store
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setTag(Object obj);
-
-        /**
-         * @return This Tab's tag object.
-         */
-        public abstract Object getTag();
-
-        /**
-         * Set the {@link TabListener} that will handle switching to and from this tab.
-         * All tabs must have a TabListener set before being added to the ActionBar.
-         *
-         * @param listener Listener to handle tab selection events
-         * @return The current instance for call chaining
-         */
-        public abstract Tab setTabListener(TabListener listener);
-
-        /**
-         * Select this tab. Only valid if the tab has been added to the action bar.
-         */
-        public abstract void select();
-
-        /**
-         * Set a description of this tab's content for use in accessibility support.
-         * If no content description is provided the title will be used.
-         *
-         * @param resId A resource ID referring to the description text
-         * @return The current instance for call chaining
-         * @see #setContentDescription(CharSequence)
-         * @see #getContentDescription()
-         */
-        public abstract Tab setContentDescription(@StringRes int resId);
-
-        /**
-         * Set a description of this tab's content for use in accessibility support.
-         * If no content description is provided the title will be used.
-         *
-         * @param contentDesc Description of this tab's content
-         * @return The current instance for call chaining
-         * @see #setContentDescription(int)
-         * @see #getContentDescription()
-         */
-        public abstract Tab setContentDescription(CharSequence contentDesc);
-
-        /**
-         * Gets a brief description of this tab's content for use in accessibility support.
-         *
-         * @return Description of this tab's content
-         * @see #setContentDescription(CharSequence)
-         * @see #setContentDescription(int)
-         */
-        public abstract CharSequence getContentDescription();
-    }
-
-    /**
-     * Callback interface invoked when a tab is focused, unfocused, added, or removed.
-     *
-     * @deprecated Action bar navigation modes are deprecated and not supported by inline
-     * toolbar action bars. Consider using other
-     * <a href="http://developer.android.com/design/patterns/navigation.html">common
-     * navigation patterns</a> instead.
-     */
-    @Deprecated
-    public interface TabListener {
-
-        /**
-         * Called when a tab enters the selected state.
-         *
-         * @param tab The tab that was selected
-         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
-         *        during a tab switch. The previous tab's unselect and this tab's select will be
-         *        executed in a single transaction. This FragmentTransaction does not support
-         *        being added to the back stack.
-         */
-        public void onTabSelected(Tab tab, FragmentTransaction ft);
-
-        /**
-         * Called when a tab exits the selected state.
-         *
-         * @param tab The tab that was unselected
-         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
-         *        during a tab switch. This tab's unselect and the newly selected tab's select
-         *        will be executed in a single transaction. This FragmentTransaction does not
-         *        support being added to the back stack.
-         */
-        public void onTabUnselected(Tab tab, FragmentTransaction ft);
-
-        /**
-         * Called when a tab that is already selected is chosen again by the user.
-         * Some applications may use this action to return to the top level of a category.
-         *
-         * @param tab The tab that was reselected.
-         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
-         *        once this method returns. This FragmentTransaction does not support
-         *        being added to the back stack.
-         */
-        public void onTabReselected(Tab tab, FragmentTransaction ft);
-    }
-
-    /**
-     * Per-child layout information associated with action bar custom views.
-     */
-    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-        /**
-         * Gravity for the view associated with these LayoutParams.
-         *
-         * @see android.view.Gravity
-         */
-        public int gravity = Gravity.NO_GRAVITY;
-
-        public LayoutParams(@NonNull Context c, AttributeSet attrs) {
-            super(c, attrs);
-
-            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ActionBarLayout);
-            gravity = a.getInt(R.styleable.ActionBarLayout_android_layout_gravity, Gravity.NO_GRAVITY);
-            a.recycle();
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-            this.gravity = Gravity.CENTER_VERTICAL | GravityCompat.START;
-        }
-
-        public LayoutParams(int width, int height, int gravity) {
-            super(width, height);
-            this.gravity = gravity;
-        }
-
-        public LayoutParams(int gravity) {
-            this(WRAP_CONTENT, MATCH_PARENT, gravity);
-        }
-
-        public LayoutParams(LayoutParams source) {
-            super(source);
-
-            this.gravity = source.gravity;
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-    }
-}
diff --git a/android/support/v7/app/ActionBarDrawerToggle.java b/android/support/v7/app/ActionBarDrawerToggle.java
deleted file mode 100644
index 1e6f8dc..0000000
--- a/android/support/v7/app/ActionBarDrawerToggle.java
+++ /dev/null
@@ -1,674 +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.app;
-
-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.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.StringRes;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.graphics.drawable.DrawerArrowDrawable;
-import android.support.v7.widget.Toolbar;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-
-/**
- * This class provides a handy way to tie together the functionality of
- * {@link android.support.v4.widget.DrawerLayout} and the framework <code>ActionBar</code> to
- * implement the recommended design for navigation drawers.
- *
- * <p>To use <code>ActionBarDrawerToggle</code>, create one in your Activity and call through
- * to the following methods corresponding to your Activity callbacks:</p>
- *
- * <ul>
- * <li>{@link android.app.Activity#onConfigurationChanged(android.content.res.Configuration)
- * onConfigurationChanged}
- * <li>{@link android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
- * onOptionsItemSelected}</li>
- * </ul>
- *
- * <p>Call {@link #syncState()} from your <code>Activity</code>'s
- * {@link android.app.Activity#onPostCreate(android.os.Bundle) onPostCreate} to synchronize the
- * indicator with the state of the linked DrawerLayout after <code>onRestoreInstanceState</code>
- * has occurred.</p>
- *
- * <p><code>ActionBarDrawerToggle</code> can be used directly as a
- * {@link android.support.v4.widget.DrawerLayout.DrawerListener}, or if you are already providing
- * your own listener, call through to each of the listener methods from your own.</p>
- *
- * <p>
- * You can customize the the animated toggle by defining the
- * {@link android.support.v7.appcompat.R.styleable#DrawerArrowToggle drawerArrowStyle} in your
- * ActionBar theme.
- */
-public class ActionBarDrawerToggle implements DrawerLayout.DrawerListener {
-
-    /**
-     * Allows an implementing Activity to return an {@link ActionBarDrawerToggle.Delegate} to use
-     * with ActionBarDrawerToggle.
-     */
-    public interface DelegateProvider {
-
-        /**
-         * @return Delegate to use for ActionBarDrawableToggles, or null if the Activity
-         * does not wish to override the default behavior.
-         */
-        @Nullable
-        Delegate getDrawerToggleDelegate();
-    }
-
-    public interface Delegate {
-
-        /**
-         * Set the Action Bar's up indicator drawable and content description.
-         *
-         * @param upDrawable     - Drawable to set as up indicator
-         * @param contentDescRes - Content description to set
-         */
-        void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes);
-
-        /**
-         * Set the Action Bar's up indicator content description.
-         *
-         * @param contentDescRes - Content description to set
-         */
-        void setActionBarDescription(@StringRes int contentDescRes);
-
-        /**
-         * Returns the drawable to be set as up button when DrawerToggle is disabled
-         */
-        Drawable getThemeUpIndicator();
-
-        /**
-         * Returns the context of ActionBar
-         */
-        Context getActionBarThemedContext();
-
-        /**
-         * Returns whether navigation icon is visible or not.
-         * Used to print warning messages in case developer forgets to set displayHomeAsUp to true
-         */
-        boolean isNavigationVisible();
-    }
-
-    private final Delegate mActivityImpl;
-    private final DrawerLayout mDrawerLayout;
-
-    private DrawerArrowDrawable mSlider;
-    private boolean mDrawerSlideAnimationEnabled = true;
-    private Drawable mHomeAsUpIndicator;
-    boolean mDrawerIndicatorEnabled = true;
-    private boolean mHasCustomUpIndicator;
-    private final int mOpenDrawerContentDescRes;
-    private final int mCloseDrawerContentDescRes;
-    // used in toolbar mode when DrawerToggle is disabled
-    View.OnClickListener mToolbarNavigationClickListener;
-    // If developer does not set displayHomeAsUp, DrawerToggle won't show up.
-    // DrawerToggle logs a warning if this case is detected
-    private boolean mWarnedForDisplayHomeAsUp = false;
-
-    /**
-     * Construct a new ActionBarDrawerToggle.
-     *
-     * <p>The given {@link Activity} will be linked to the specified {@link DrawerLayout} and
-     * its Actionbar's Up button will be set to a custom drawable.
-     * <p>This drawable shows a Hamburger icon when drawer is closed and an arrow when drawer
-     * is open. It animates between these two states as the drawer opens.</p>
-     *
-     * <p>String resources must be provided to describe the open/close drawer actions for
-     * accessibility services.</p>
-     *
-     * @param activity                  The Activity hosting the drawer. Should have an ActionBar.
-     * @param drawerLayout              The DrawerLayout to link to the given Activity's ActionBar
-     * @param openDrawerContentDescRes  A String resource to describe the "open drawer" action
-     *                                  for accessibility
-     * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action
-     *                                  for accessibility
-     */
-    public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
-            @StringRes int openDrawerContentDescRes,
-            @StringRes int closeDrawerContentDescRes) {
-        this(activity, null, drawerLayout, null, openDrawerContentDescRes,
-                closeDrawerContentDescRes);
-    }
-
-    /**
-     * Construct a new ActionBarDrawerToggle with a Toolbar.
-     * <p>
-     * The given {@link Activity} will be linked to the specified {@link DrawerLayout} and
-     * the Toolbar's navigation icon will be set to a custom drawable. Using this constructor
-     * will set Toolbar's navigation click listener to toggle the drawer when it is clicked.
-     * <p>
-     * This drawable shows a Hamburger icon when drawer is closed and an arrow when drawer
-     * is open. It animates between these two states as the drawer opens.
-     * <p>
-     * String resources must be provided to describe the open/close drawer actions for
-     * accessibility services.
-     * <p>
-     * Please use {@link #ActionBarDrawerToggle(Activity, DrawerLayout, int, int)} if you are
-     * setting the Toolbar as the ActionBar of your activity.
-     *
-     * @param activity                  The Activity hosting the drawer.
-     * @param toolbar                   The toolbar to use if you have an independent Toolbar.
-     * @param drawerLayout              The DrawerLayout to link to the given Activity's ActionBar
-     * @param openDrawerContentDescRes  A String resource to describe the "open drawer" action
-     *                                  for accessibility
-     * @param closeDrawerContentDescRes A String resource to describe the "close drawer" action
-     *                                  for accessibility
-     */
-    public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout,
-            Toolbar toolbar, @StringRes int openDrawerContentDescRes,
-            @StringRes int closeDrawerContentDescRes) {
-        this(activity, toolbar, drawerLayout, null, openDrawerContentDescRes,
-                closeDrawerContentDescRes);
-    }
-
-    /**
-     * In the future, we can make this constructor public if we want to let developers customize
-     * the
-     * animation.
-     */
-    ActionBarDrawerToggle(Activity activity, Toolbar toolbar, DrawerLayout drawerLayout,
-            DrawerArrowDrawable slider, @StringRes int openDrawerContentDescRes,
-            @StringRes int closeDrawerContentDescRes) {
-        if (toolbar != null) {
-            mActivityImpl = new ToolbarCompatDelegate(toolbar);
-            toolbar.setNavigationOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mDrawerIndicatorEnabled) {
-                        toggle();
-                    } else if (mToolbarNavigationClickListener != null) {
-                        mToolbarNavigationClickListener.onClick(v);
-                    }
-                }
-            });
-        } else if (activity instanceof DelegateProvider) { // Allow the Activity to provide an impl
-            mActivityImpl = ((DelegateProvider) activity).getDrawerToggleDelegate();
-        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            mActivityImpl = new JellybeanMr2Delegate(activity);
-        } else {
-            mActivityImpl = new IcsDelegate(activity);
-        }
-
-        mDrawerLayout = drawerLayout;
-        mOpenDrawerContentDescRes = openDrawerContentDescRes;
-        mCloseDrawerContentDescRes = closeDrawerContentDescRes;
-        if (slider == null) {
-            mSlider = new DrawerArrowDrawable(mActivityImpl.getActionBarThemedContext());
-        } else {
-            mSlider = slider;
-        }
-
-        mHomeAsUpIndicator = getThemeUpIndicator();
-    }
-
-    /**
-     * Synchronize the state of the drawer indicator/affordance with the linked DrawerLayout.
-     *
-     * <p>This should be called from your <code>Activity</code>'s
-     * {@link Activity#onPostCreate(android.os.Bundle) onPostCreate} method to synchronize after
-     * the DrawerLayout's instance state has been restored, and any other time when the state
-     * may have diverged in such a way that the ActionBarDrawerToggle was not notified.
-     * (For example, if you stop forwarding appropriate drawer events for a period of time.)</p>
-     */
-    public void syncState() {
-        if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
-            setPosition(1);
-        } else {
-            setPosition(0);
-        }
-        if (mDrawerIndicatorEnabled) {
-            setActionBarUpIndicator(mSlider,
-                    mDrawerLayout.isDrawerOpen(GravityCompat.START) ?
-                            mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
-        }
-    }
-
-    /**
-     * This method should always be called by your <code>Activity</code>'s
-     * {@link Activity#onConfigurationChanged(android.content.res.Configuration)
-     * onConfigurationChanged}
-     * method.
-     *
-     * @param newConfig The new configuration
-     */
-    public void onConfigurationChanged(Configuration newConfig) {
-        // Reload drawables that can change with configuration
-        if (!mHasCustomUpIndicator) {
-            mHomeAsUpIndicator = getThemeUpIndicator();
-        }
-        syncState();
-    }
-
-    /**
-     * This method should be called by your <code>Activity</code>'s
-     * {@link Activity#onOptionsItemSelected(android.view.MenuItem) onOptionsItemSelected} method.
-     * If it returns true, your <code>onOptionsItemSelected</code> method should return true and
-     * skip further processing.
-     *
-     * @param item the MenuItem instance representing the selected menu item
-     * @return true if the event was handled and further processing should not occur
-     */
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item != null && item.getItemId() == android.R.id.home && mDrawerIndicatorEnabled) {
-            toggle();
-            return true;
-        }
-        return false;
-    }
-
-    void toggle() {
-        int drawerLockMode = mDrawerLayout.getDrawerLockMode(GravityCompat.START);
-        if (mDrawerLayout.isDrawerVisible(GravityCompat.START)
-                && (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {
-            mDrawerLayout.closeDrawer(GravityCompat.START);
-        } else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
-            mDrawerLayout.openDrawer(GravityCompat.START);
-        }
-    }
-
-    /**
-     * Set the up indicator to display when the drawer indicator is not
-     * enabled.
-     * <p>
-     * If you pass <code>null</code> to this method, the default drawable from
-     * the theme will be used.
-     *
-     * @param indicator A drawable to use for the up indicator, or null to use
-     *                  the theme's default
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public void setHomeAsUpIndicator(Drawable indicator) {
-        if (indicator == null) {
-            mHomeAsUpIndicator = getThemeUpIndicator();
-            mHasCustomUpIndicator = false;
-        } else {
-            mHomeAsUpIndicator = indicator;
-            mHasCustomUpIndicator = true;
-        }
-
-        if (!mDrawerIndicatorEnabled) {
-            setActionBarUpIndicator(mHomeAsUpIndicator, 0);
-        }
-    }
-
-    /**
-     * Set the up indicator to display when the drawer indicator is not
-     * enabled.
-     * <p>
-     * If you pass 0 to this method, the default drawable from the theme will
-     * be used.
-     *
-     * @param resId Resource ID of a drawable to use for the up indicator, or 0
-     *              to use the theme's default
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public void setHomeAsUpIndicator(int resId) {
-        Drawable indicator = null;
-        if (resId != 0) {
-            indicator = mDrawerLayout.getResources().getDrawable(resId);
-        }
-        setHomeAsUpIndicator(indicator);
-    }
-
-    /**
-     * @return true if the enhanced drawer indicator is enabled, false otherwise
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public boolean isDrawerIndicatorEnabled() {
-        return mDrawerIndicatorEnabled;
-    }
-
-    /**
-     * Enable or disable the drawer indicator. The indicator defaults to enabled.
-     *
-     * <p>When the indicator is disabled, the <code>ActionBar</code> will revert to displaying
-     * the home-as-up indicator provided by the <code>Activity</code>'s theme in the
-     * <code>android.R.attr.homeAsUpIndicator</code> attribute instead of the animated
-     * drawer glyph.</p>
-     *
-     * @param enable true to enable, false to disable
-     */
-    public void setDrawerIndicatorEnabled(boolean enable) {
-        if (enable != mDrawerIndicatorEnabled) {
-            if (enable) {
-                setActionBarUpIndicator(mSlider,
-                        mDrawerLayout.isDrawerOpen(GravityCompat.START) ?
-                                mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
-            } else {
-                setActionBarUpIndicator(mHomeAsUpIndicator, 0);
-            }
-            mDrawerIndicatorEnabled = enable;
-        }
-    }
-
-    /**
-     * @return DrawerArrowDrawable that is currently shown by the ActionBarDrawerToggle.
-     */
-    @NonNull
-    public DrawerArrowDrawable getDrawerArrowDrawable() {
-        return mSlider;
-    }
-
-    /**
-     * Sets the DrawerArrowDrawable that should be shown by this ActionBarDrawerToggle.
-     *
-     * @param drawable DrawerArrowDrawable that should be shown by this ActionBarDrawerToggle
-     */
-    public void setDrawerArrowDrawable(@NonNull DrawerArrowDrawable drawable) {
-        mSlider = drawable;
-        syncState();
-    }
-
-    /**
-     * Specifies whether the drawer arrow should animate when the drawer position changes.
-     *
-     * @param enabled if this is {@code true} then the animation will run, else it will be skipped
-     */
-    public void setDrawerSlideAnimationEnabled(boolean enabled) {
-        mDrawerSlideAnimationEnabled = enabled;
-        if (!enabled) {
-            setPosition(0);
-        }
-    }
-
-    /**
-     * @return whether the drawer slide animation is enabled
-     */
-    public boolean isDrawerSlideAnimationEnabled() {
-        return mDrawerSlideAnimationEnabled;
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param drawerView  The child view that was moved
-     * @param slideOffset The new offset of this drawer within its range, from 0-1
-     */
-    @Override
-    public void onDrawerSlide(View drawerView, float slideOffset) {
-        if (mDrawerSlideAnimationEnabled) {
-            setPosition(Math.min(1f, Math.max(0, slideOffset)));
-        } else {
-            setPosition(0); // disable animation.
-        }
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param drawerView Drawer view that is now open
-     */
-    @Override
-    public void onDrawerOpened(View drawerView) {
-        setPosition(1);
-        if (mDrawerIndicatorEnabled) {
-            setActionBarDescription(mCloseDrawerContentDescRes);
-        }
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param drawerView Drawer view that is now closed
-     */
-    @Override
-    public void onDrawerClosed(View drawerView) {
-        setPosition(0);
-        if (mDrawerIndicatorEnabled) {
-            setActionBarDescription(mOpenDrawerContentDescRes);
-        }
-    }
-
-    /**
-     * {@link DrawerLayout.DrawerListener} callback method. If you do not use your
-     * ActionBarDrawerToggle instance directly as your DrawerLayout's listener, you should call
-     * through to this method from your own listener object.
-     *
-     * @param newState The new drawer motion state
-     */
-    @Override
-    public void onDrawerStateChanged(int newState) {
-    }
-
-    /**
-     * Returns the fallback listener for Navigation icon click events.
-     *
-     * @return The click listener which receives Navigation click events from Toolbar when
-     * drawer indicator is disabled.
-     * @see #setToolbarNavigationClickListener(android.view.View.OnClickListener)
-     * @see #setDrawerIndicatorEnabled(boolean)
-     * @see #isDrawerIndicatorEnabled()
-     */
-    public View.OnClickListener getToolbarNavigationClickListener() {
-        return mToolbarNavigationClickListener;
-    }
-
-    /**
-     * When DrawerToggle is constructed with a Toolbar, it sets the click listener on
-     * the Navigation icon. If you want to listen for clicks on the Navigation icon when
-     * DrawerToggle is disabled ({@link #setDrawerIndicatorEnabled(boolean)}, you should call this
-     * method with your listener and DrawerToggle will forward click events to that listener
-     * when drawer indicator is disabled.
-     *
-     * @see #setDrawerIndicatorEnabled(boolean)
-     */
-    public void setToolbarNavigationClickListener(
-            View.OnClickListener onToolbarNavigationClickListener) {
-        mToolbarNavigationClickListener = onToolbarNavigationClickListener;
-    }
-
-    void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
-        if (!mWarnedForDisplayHomeAsUp && !mActivityImpl.isNavigationVisible()) {
-            Log.w("ActionBarDrawerToggle", "DrawerToggle may not show up because NavigationIcon"
-                    + " is not visible. You may need to call "
-                    + "actionbar.setDisplayHomeAsUpEnabled(true);");
-            mWarnedForDisplayHomeAsUp = true;
-        }
-        mActivityImpl.setActionBarUpIndicator(upDrawable, contentDescRes);
-    }
-
-    void setActionBarDescription(int contentDescRes) {
-        mActivityImpl.setActionBarDescription(contentDescRes);
-    }
-
-    Drawable getThemeUpIndicator() {
-        return mActivityImpl.getThemeUpIndicator();
-    }
-
-    private void setPosition(float position) {
-        if (position == 1f) {
-            mSlider.setVerticalMirror(true);
-        } else if (position == 0f) {
-            mSlider.setVerticalMirror(false);
-        }
-        mSlider.setProgress(position);
-    }
-
-    /**
-     * Delegate if SDK version is between ICS and JBMR2
-     */
-    private static class IcsDelegate implements Delegate {
-
-        final Activity mActivity;
-        ActionBarDrawerToggleHoneycomb.SetIndicatorInfo mSetIndicatorInfo;
-
-        IcsDelegate(Activity activity) {
-            mActivity = activity;
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator() {
-            return ActionBarDrawerToggleHoneycomb.getThemeUpIndicator(mActivity);
-        }
-
-        @Override
-        public Context getActionBarThemedContext() {
-            final ActionBar actionBar = mActivity.getActionBar();
-            final Context context;
-            if (actionBar != null) {
-                context = actionBar.getThemedContext();
-            } else {
-                context = mActivity;
-            }
-            return context;
-        }
-
-        @Override
-        public boolean isNavigationVisible() {
-            final ActionBar actionBar = mActivity.getActionBar();
-            return actionBar != null
-                    && (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
-        }
-
-        @Override
-        public void setActionBarUpIndicator(Drawable themeImage, int contentDescRes) {
-            final ActionBar actionBar = mActivity.getActionBar();
-            if (actionBar != null) {
-                actionBar.setDisplayShowHomeEnabled(true);
-                mSetIndicatorInfo = ActionBarDrawerToggleHoneycomb.setActionBarUpIndicator(
-                        mSetIndicatorInfo, mActivity, themeImage, contentDescRes);
-                actionBar.setDisplayShowHomeEnabled(false);
-            }
-        }
-
-        @Override
-        public void setActionBarDescription(int contentDescRes) {
-            mSetIndicatorInfo = ActionBarDrawerToggleHoneycomb.setActionBarDescription(
-                    mSetIndicatorInfo, mActivity, contentDescRes);
-        }
-    }
-
-    /**
-     * Delegate if SDK version is JB MR2 or newer
-     */
-    @RequiresApi(18)
-    private static class JellybeanMr2Delegate implements Delegate {
-
-        final Activity mActivity;
-
-        JellybeanMr2Delegate(Activity activity) {
-            mActivity = activity;
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator() {
-            final TypedArray a = getActionBarThemedContext().obtainStyledAttributes(null,
-                    new int[]{android.R.attr.homeAsUpIndicator}, android.R.attr.actionBarStyle, 0);
-            final Drawable result = a.getDrawable(0);
-            a.recycle();
-            return result;
-        }
-
-        @Override
-        public Context getActionBarThemedContext() {
-            final ActionBar actionBar = mActivity.getActionBar();
-            final Context context;
-            if (actionBar != null) {
-                context = actionBar.getThemedContext();
-            } else {
-                context = mActivity;
-            }
-            return context;
-        }
-
-        @Override
-        public boolean isNavigationVisible() {
-            final ActionBar actionBar = mActivity.getActionBar();
-            return actionBar != null &&
-                    (actionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
-        }
-
-        @Override
-        public void setActionBarUpIndicator(Drawable drawable, int contentDescRes) {
-            final ActionBar actionBar = mActivity.getActionBar();
-            if (actionBar != null) {
-                actionBar.setHomeAsUpIndicator(drawable);
-                actionBar.setHomeActionContentDescription(contentDescRes);
-            }
-        }
-
-        @Override
-        public void setActionBarDescription(int contentDescRes) {
-            final ActionBar actionBar = mActivity.getActionBar();
-            if (actionBar != null) {
-                actionBar.setHomeActionContentDescription(contentDescRes);
-            }
-        }
-    }
-
-    /**
-     * Used when DrawerToggle is initialized with a Toolbar
-     */
-    static class ToolbarCompatDelegate implements Delegate {
-
-        final Toolbar mToolbar;
-        final Drawable mDefaultUpIndicator;
-        final CharSequence mDefaultContentDescription;
-
-        ToolbarCompatDelegate(Toolbar toolbar) {
-            mToolbar = toolbar;
-            mDefaultUpIndicator = toolbar.getNavigationIcon();
-            mDefaultContentDescription = toolbar.getNavigationContentDescription();
-        }
-
-        @Override
-        public void setActionBarUpIndicator(Drawable upDrawable, @StringRes int contentDescRes) {
-            mToolbar.setNavigationIcon(upDrawable);
-            setActionBarDescription(contentDescRes);
-        }
-
-        @Override
-        public void setActionBarDescription(@StringRes int contentDescRes) {
-            if (contentDescRes == 0) {
-                mToolbar.setNavigationContentDescription(mDefaultContentDescription);
-            } else {
-                mToolbar.setNavigationContentDescription(contentDescRes);
-            }
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator() {
-            return mDefaultUpIndicator;
-        }
-
-        @Override
-        public Context getActionBarThemedContext() {
-            return mToolbar.getContext();
-        }
-
-        @Override
-        public boolean isNavigationVisible() {
-            return true;
-        }
-    }
-}
diff --git a/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java b/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
deleted file mode 100644
index b2fc9fb..0000000
--- a/android/support/v7/app/ActionBarDrawerToggleHoneycomb.java
+++ /dev/null
@@ -1,141 +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.app;
-
-import android.R;
-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.
- *
- * Moved from Support-v4
- */
-@RequiresApi(11)
-class ActionBarDrawerToggleHoneycomb {
-    private static final String TAG = "ActionBarDrawerToggleHC";
-
-    private static final int[] THEME_ATTRS = new int[] {
-            R.attr.homeAsUpIndicator
-    };
-
-    public static SetIndicatorInfo setActionBarUpIndicator(SetIndicatorInfo info, Activity activity,
-            Drawable drawable, int contentDescRes) {
-        if (true || info == null) {
-            info = new SetIndicatorInfo(activity);
-        }
-        if (info.setHomeAsUpIndicator != null) {
-            try {
-                final ActionBar actionBar = activity.getActionBar();
-                info.setHomeAsUpIndicator.invoke(actionBar, drawable);
-                info.setHomeActionContentDescription.invoke(actionBar, contentDescRes);
-            } catch (Exception e) {
-                Log.w(TAG, "Couldn't set home-as-up indicator via JB-MR2 API", e);
-            }
-        } else if (info.upIndicatorView != null) {
-            info.upIndicatorView.setImageDrawable(drawable);
-        } else {
-            Log.w(TAG, "Couldn't set home-as-up indicator");
-        }
-        return info;
-    }
-
-    public static SetIndicatorInfo setActionBarDescription(SetIndicatorInfo info, Activity activity,
-            int contentDescRes) {
-        if (info == null) {
-            info = new SetIndicatorInfo(activity);
-        }
-        if (info.setHomeAsUpIndicator != null) {
-            try {
-                final ActionBar actionBar = activity.getActionBar();
-                info.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;
-    }
-
-    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/android/support/v7/app/AlertController.java b/android/support/v7/app/AlertController.java
deleted file mode 100644
index 01bc449..0000000
--- a/android/support/v7/app/AlertController.java
+++ /dev/null
@@ -1,1116 +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 static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.TypedArray;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Message;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.NestedScrollView;
-import android.support.v7.appcompat.R;
-import android.support.v7.widget.LinearLayoutCompat;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewParent;
-import android.view.ViewStub;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckedTextView;
-import android.widget.CursorAdapter;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.SimpleCursorAdapter;
-import android.widget.TextView;
-
-import java.lang.ref.WeakReference;
-
-class AlertController {
-    private final Context mContext;
-    final AppCompatDialog mDialog;
-    private final Window mWindow;
-    private final int mButtonIconDimen;
-
-    private CharSequence mTitle;
-    private CharSequence mMessage;
-    ListView mListView;
-    private View mView;
-
-    private int mViewLayoutResId;
-
-    private int mViewSpacingLeft;
-    private int mViewSpacingTop;
-    private int mViewSpacingRight;
-    private int mViewSpacingBottom;
-    private boolean mViewSpacingSpecified = false;
-
-    Button mButtonPositive;
-    private CharSequence mButtonPositiveText;
-    Message mButtonPositiveMessage;
-    private Drawable mButtonPositiveIcon;
-
-    Button mButtonNegative;
-    private CharSequence mButtonNegativeText;
-    Message mButtonNegativeMessage;
-    private Drawable mButtonNegativeIcon;
-
-    Button mButtonNeutral;
-    private CharSequence mButtonNeutralText;
-    Message mButtonNeutralMessage;
-    private Drawable mButtonNeutralIcon;
-
-    NestedScrollView mScrollView;
-
-    private int mIconId = 0;
-    private Drawable mIcon;
-
-    private ImageView mIconView;
-    private TextView mTitleView;
-    private TextView mMessageView;
-    private View mCustomTitleView;
-
-    ListAdapter mAdapter;
-
-    int mCheckedItem = -1;
-
-    private int mAlertDialogLayout;
-    private int mButtonPanelSideLayout;
-    int mListLayout;
-    int mMultiChoiceItemLayout;
-    int mSingleChoiceItemLayout;
-    int mListItemLayout;
-
-    private boolean mShowTitle;
-
-    private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
-
-    Handler mHandler;
-
-    private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            final Message m;
-            if (v == mButtonPositive && mButtonPositiveMessage != null) {
-                m = Message.obtain(mButtonPositiveMessage);
-            } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
-                m = Message.obtain(mButtonNegativeMessage);
-            } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
-                m = Message.obtain(mButtonNeutralMessage);
-            } else {
-                m = null;
-            }
-
-            if (m != null) {
-                m.sendToTarget();
-            }
-
-            // Post a message so we dismiss after the above handlers are executed
-            mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialog)
-                    .sendToTarget();
-        }
-    };
-
-    private static final class ButtonHandler extends Handler {
-        // Button clicks have Message.what as the BUTTON{1,2,3} constant
-        private static final int MSG_DISMISS_DIALOG = 1;
-
-        private WeakReference<DialogInterface> mDialog;
-
-        public ButtonHandler(DialogInterface dialog) {
-            mDialog = new WeakReference<>(dialog);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-
-                case DialogInterface.BUTTON_POSITIVE:
-                case DialogInterface.BUTTON_NEGATIVE:
-                case DialogInterface.BUTTON_NEUTRAL:
-                    ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
-                    break;
-
-                case MSG_DISMISS_DIALOG:
-                    ((DialogInterface) msg.obj).dismiss();
-            }
-        }
-    }
-
-    private static boolean shouldCenterSingleButton(Context context) {
-        final TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
-        return outValue.data != 0;
-    }
-
-    public AlertController(Context context, AppCompatDialog di, Window window) {
-        mContext = context;
-        mDialog = di;
-        mWindow = window;
-        mHandler = new ButtonHandler(di);
-
-        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
-                R.attr.alertDialogStyle, 0);
-
-        mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
-        mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);
-
-        mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
-        mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
-        mSingleChoiceItemLayout = a
-                .getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
-        mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);
-        mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);
-        mButtonIconDimen = a.getDimensionPixelSize(R.styleable.AlertDialog_buttonIconDimen, 0);
-
-        a.recycle();
-
-        /* We use a custom title so never request a window title */
-        di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
-    }
-
-    static boolean canTextInput(View v) {
-        if (v.onCheckIsTextEditor()) {
-            return true;
-        }
-
-        if (!(v instanceof ViewGroup)) {
-            return false;
-        }
-
-        ViewGroup vg = (ViewGroup) v;
-        int i = vg.getChildCount();
-        while (i > 0) {
-            i--;
-            v = vg.getChildAt(i);
-            if (canTextInput(v)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    public void installContent() {
-        final int contentView = selectContentView();
-        mDialog.setContentView(contentView);
-        setupView();
-    }
-
-    private int selectContentView() {
-        if (mButtonPanelSideLayout == 0) {
-            return mAlertDialogLayout;
-        }
-        if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
-            return mButtonPanelSideLayout;
-        }
-        return mAlertDialogLayout;
-    }
-
-    public void setTitle(CharSequence title) {
-        mTitle = title;
-        if (mTitleView != null) {
-            mTitleView.setText(title);
-        }
-    }
-
-    /**
-     * @see AlertDialog.Builder#setCustomTitle(View)
-     */
-    public void setCustomTitle(View customTitleView) {
-        mCustomTitleView = customTitleView;
-    }
-
-    public void setMessage(CharSequence message) {
-        mMessage = message;
-        if (mMessageView != null) {
-            mMessageView.setText(message);
-        }
-    }
-
-    /**
-     * Set the view resource to display in the dialog.
-     */
-    public void setView(int layoutResId) {
-        mView = null;
-        mViewLayoutResId = layoutResId;
-        mViewSpacingSpecified = false;
-    }
-
-    /**
-     * Set the view to display in the dialog.
-     */
-    public void setView(View view) {
-        mView = view;
-        mViewLayoutResId = 0;
-        mViewSpacingSpecified = false;
-    }
-
-    /**
-     * Set the view to display in the dialog along with the spacing around that view
-     */
-    public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
-            int viewSpacingBottom) {
-        mView = view;
-        mViewLayoutResId = 0;
-        mViewSpacingSpecified = true;
-        mViewSpacingLeft = viewSpacingLeft;
-        mViewSpacingTop = viewSpacingTop;
-        mViewSpacingRight = viewSpacingRight;
-        mViewSpacingBottom = viewSpacingBottom;
-    }
-
-    /**
-     * Sets a hint for the best button panel layout.
-     */
-    public void setButtonPanelLayoutHint(int layoutHint) {
-        mButtonPanelLayoutHint = layoutHint;
-    }
-
-    /**
-     * Sets an icon, a click listener or a message to be sent when the button is clicked.
-     * You only need to pass one of {@code icon}, {@code listener} or {@code msg}.
-     *
-     * @param whichButton Which button, can be one of
-     *                    {@link DialogInterface#BUTTON_POSITIVE},
-     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
-     *                    {@link DialogInterface#BUTTON_NEUTRAL}
-     * @param text        The text to display in positive button.
-     * @param listener    The {@link DialogInterface.OnClickListener} to use.
-     * @param msg         The {@link Message} to be sent when clicked.
-     * @param icon        The (@link Drawable) to be used as an icon for the button.
-     *
-     */
-    public void setButton(int whichButton, CharSequence text,
-            DialogInterface.OnClickListener listener, Message msg, Drawable icon) {
-
-        if (msg == null && listener != null) {
-            msg = mHandler.obtainMessage(whichButton, listener);
-        }
-
-        switch (whichButton) {
-
-            case DialogInterface.BUTTON_POSITIVE:
-                mButtonPositiveText = text;
-                mButtonPositiveMessage = msg;
-                mButtonPositiveIcon = icon;
-                break;
-
-            case DialogInterface.BUTTON_NEGATIVE:
-                mButtonNegativeText = text;
-                mButtonNegativeMessage = msg;
-                mButtonNegativeIcon = icon;
-                break;
-
-            case DialogInterface.BUTTON_NEUTRAL:
-                mButtonNeutralText = text;
-                mButtonNeutralMessage = msg;
-                mButtonNeutralIcon = icon;
-                break;
-
-            default:
-                throw new IllegalArgumentException("Button does not exist");
-        }
-    }
-
-    /**
-     * Specifies the icon to display next to the alert title.
-     *
-     * @param resId the resource identifier of the drawable to use as the icon,
-     *              or 0 for no icon
-     */
-    public void setIcon(int resId) {
-        mIcon = null;
-        mIconId = resId;
-
-        if (mIconView != null) {
-            if (resId != 0) {
-                mIconView.setVisibility(View.VISIBLE);
-                mIconView.setImageResource(mIconId);
-            } else {
-                mIconView.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    /**
-     * Specifies the icon to display next to the alert title.
-     *
-     * @param icon the drawable to use as the icon or null for no icon
-     */
-    public void setIcon(Drawable icon) {
-        mIcon = icon;
-        mIconId = 0;
-
-        if (mIconView != null) {
-            if (icon != null) {
-                mIconView.setVisibility(View.VISIBLE);
-                mIconView.setImageDrawable(icon);
-            } else {
-                mIconView.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    /**
-     * @param attrId the attributeId of the theme-specific drawable
-     *               to resolve the resourceId for.
-     *
-     * @return resId the resourceId of the theme-specific drawable
-     */
-    public int getIconAttributeResId(int attrId) {
-        TypedValue out = new TypedValue();
-        mContext.getTheme().resolveAttribute(attrId, out, true);
-        return out.resourceId;
-    }
-
-    public ListView getListView() {
-        return mListView;
-    }
-
-    public Button getButton(int whichButton) {
-        switch (whichButton) {
-            case DialogInterface.BUTTON_POSITIVE:
-                return mButtonPositive;
-            case DialogInterface.BUTTON_NEGATIVE:
-                return mButtonNegative;
-            case DialogInterface.BUTTON_NEUTRAL:
-                return mButtonNeutral;
-            default:
-                return null;
-        }
-    }
-
-    @SuppressWarnings({"UnusedDeclaration"})
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        return mScrollView != null && mScrollView.executeKeyEvent(event);
-    }
-
-    @SuppressWarnings({"UnusedDeclaration"})
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        return mScrollView != null && mScrollView.executeKeyEvent(event);
-    }
-
-    /**
-     * Resolves whether a custom or default panel should be used. Removes the
-     * default panel if a custom panel should be used. If the resolved panel is
-     * a view stub, inflates before returning.
-     *
-     * @param customPanel the custom panel
-     * @param defaultPanel the default panel
-     * @return the panel to use
-     */
-    @Nullable
-    private ViewGroup resolvePanel(@Nullable View customPanel, @Nullable View defaultPanel) {
-        if (customPanel == null) {
-            // Inflate the default panel, if needed.
-            if (defaultPanel instanceof ViewStub) {
-                defaultPanel = ((ViewStub) defaultPanel).inflate();
-            }
-
-            return (ViewGroup) defaultPanel;
-        }
-
-        // Remove the default panel entirely.
-        if (defaultPanel != null) {
-            final ViewParent parent = defaultPanel.getParent();
-            if (parent instanceof ViewGroup) {
-                ((ViewGroup) parent).removeView(defaultPanel);
-            }
-        }
-
-        // Inflate the custom panel, if needed.
-        if (customPanel instanceof ViewStub) {
-            customPanel = ((ViewStub) customPanel).inflate();
-        }
-
-        return (ViewGroup) customPanel;
-    }
-
-    private void setupView() {
-        final View parentPanel = mWindow.findViewById(R.id.parentPanel);
-        final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
-        final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
-        final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
-
-        // Install custom content before setting up the title or buttons so
-        // that we can handle panel overrides.
-        final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
-        setupCustomContent(customPanel);
-
-        final View customTopPanel = customPanel.findViewById(R.id.topPanel);
-        final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
-        final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
-
-        // Resolve the correct panels and remove the defaults, if needed.
-        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
-        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
-        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
-
-        setupContent(contentPanel);
-        setupButtons(buttonPanel);
-        setupTitle(topPanel);
-
-        final boolean hasCustomPanel = customPanel != null
-                && customPanel.getVisibility() != View.GONE;
-        final boolean hasTopPanel = topPanel != null
-                && topPanel.getVisibility() != View.GONE;
-        final boolean hasButtonPanel = buttonPanel != null
-                && buttonPanel.getVisibility() != View.GONE;
-
-        // Only display the text spacer if we don't have buttons.
-        if (!hasButtonPanel) {
-            if (contentPanel != null) {
-                final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
-                if (spacer != null) {
-                    spacer.setVisibility(View.VISIBLE);
-                }
-            }
-        }
-
-        if (hasTopPanel) {
-            // Only clip scrolling content to padding if we have a title.
-            if (mScrollView != null) {
-                mScrollView.setClipToPadding(true);
-            }
-
-            // Only show the divider if we have a title.
-            View divider = null;
-            if (mMessage != null || mListView != null) {
-                divider = topPanel.findViewById(R.id.titleDividerNoCustom);
-            }
-
-            if (divider != null) {
-                divider.setVisibility(View.VISIBLE);
-            }
-        } else {
-            if (contentPanel != null) {
-                final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
-                if (spacer != null) {
-                    spacer.setVisibility(View.VISIBLE);
-                }
-            }
-        }
-
-        if (mListView instanceof RecycleListView) {
-            ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
-        }
-
-        // Update scroll indicators as needed.
-        if (!hasCustomPanel) {
-            final View content = mListView != null ? mListView : mScrollView;
-            if (content != null) {
-                final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
-                        | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
-                setScrollIndicators(contentPanel, content, indicators,
-                        ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
-            }
-        }
-
-        final ListView listView = mListView;
-        if (listView != null && mAdapter != null) {
-            listView.setAdapter(mAdapter);
-            final int checkedItem = mCheckedItem;
-            if (checkedItem > -1) {
-                listView.setItemChecked(checkedItem, true);
-                listView.setSelection(checkedItem);
-            }
-        }
-    }
-
-    private void setScrollIndicators(ViewGroup contentPanel, View content,
-            final int indicators, final int mask) {
-        // Set up scroll indicators (if present).
-        View indicatorUp = mWindow.findViewById(R.id.scrollIndicatorUp);
-        View indicatorDown = mWindow.findViewById(R.id.scrollIndicatorDown);
-
-        if (Build.VERSION.SDK_INT >= 23) {
-            // We're on Marshmallow so can rely on the View APIs
-            ViewCompat.setScrollIndicators(content, indicators, mask);
-            // We can also remove the compat indicator views
-            if (indicatorUp != null) {
-                contentPanel.removeView(indicatorUp);
-            }
-            if (indicatorDown != null) {
-                contentPanel.removeView(indicatorDown);
-            }
-        } else {
-            // First, remove the indicator views if we're not set to use them
-            if (indicatorUp != null && (indicators & ViewCompat.SCROLL_INDICATOR_TOP) == 0) {
-                contentPanel.removeView(indicatorUp);
-                indicatorUp = null;
-            }
-            if (indicatorDown != null && (indicators & ViewCompat.SCROLL_INDICATOR_BOTTOM) == 0) {
-                contentPanel.removeView(indicatorDown);
-                indicatorDown = null;
-            }
-
-            if (indicatorUp != null || indicatorDown != null) {
-                final View top = indicatorUp;
-                final View bottom = indicatorDown;
-
-                if (mMessage != null) {
-                    // We're just showing the ScrollView, set up listener.
-                    mScrollView.setOnScrollChangeListener(
-                            new NestedScrollView.OnScrollChangeListener() {
-                                @Override
-                                public void onScrollChange(NestedScrollView v, int scrollX,
-                                        int scrollY,
-                                        int oldScrollX, int oldScrollY) {
-                                    manageScrollIndicators(v, top, bottom);
-                                }
-                            });
-                    // Set up the indicators following layout.
-                    mScrollView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            manageScrollIndicators(mScrollView, top, bottom);
-                        }
-                    });
-                } else if (mListView != null) {
-                    // We're just showing the AbsListView, set up listener.
-                    mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
-                        @Override
-                        public void onScrollStateChanged(AbsListView view, int scrollState) {}
-
-                        @Override
-                        public void onScroll(AbsListView v, int firstVisibleItem,
-                                int visibleItemCount, int totalItemCount) {
-                            manageScrollIndicators(v, top, bottom);
-                        }
-                    });
-                    // Set up the indicators following layout.
-                    mListView.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            manageScrollIndicators(mListView, top, bottom);
-                        }
-                    });
-                } else {
-                    // We don't have any content to scroll, remove the indicators.
-                    if (top != null) {
-                        contentPanel.removeView(top);
-                    }
-                    if (bottom != null) {
-                        contentPanel.removeView(bottom);
-                    }
-                }
-            }
-        }
-    }
-
-    private void setupCustomContent(ViewGroup customPanel) {
-        final View customView;
-        if (mView != null) {
-            customView = mView;
-        } else if (mViewLayoutResId != 0) {
-            final LayoutInflater inflater = LayoutInflater.from(mContext);
-            customView = inflater.inflate(mViewLayoutResId, customPanel, false);
-        } else {
-            customView = null;
-        }
-
-        final boolean hasCustomView = customView != null;
-        if (!hasCustomView || !canTextInput(customView)) {
-            mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-        }
-
-        if (hasCustomView) {
-            final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
-            custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-            if (mViewSpacingSpecified) {
-                custom.setPadding(
-                        mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
-            }
-
-            if (mListView != null) {
-                ((LinearLayoutCompat.LayoutParams) customPanel.getLayoutParams()).weight = 0;
-            }
-        } else {
-            customPanel.setVisibility(View.GONE);
-        }
-    }
-
-    private void setupTitle(ViewGroup topPanel) {
-        if (mCustomTitleView != null) {
-            // Add the custom title view directly to the topPanel layout
-            LayoutParams lp = new LayoutParams(
-                    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-
-            topPanel.addView(mCustomTitleView, 0, lp);
-
-            // Hide the title template
-            View titleTemplate = mWindow.findViewById(R.id.title_template);
-            titleTemplate.setVisibility(View.GONE);
-        } else {
-            mIconView = (ImageView) mWindow.findViewById(android.R.id.icon);
-
-            final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
-            if (hasTextTitle && mShowTitle) {
-                // Display the title if a title is supplied, else hide it.
-                mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
-                mTitleView.setText(mTitle);
-
-                // Do this last so that if the user has supplied any icons we
-                // use them instead of the default ones. If the user has
-                // specified 0 then make it disappear.
-                if (mIconId != 0) {
-                    mIconView.setImageResource(mIconId);
-                } else if (mIcon != null) {
-                    mIconView.setImageDrawable(mIcon);
-                } else {
-                    // Apply the padding from the icon to ensure the title is
-                    // aligned correctly.
-                    mTitleView.setPadding(mIconView.getPaddingLeft(),
-                            mIconView.getPaddingTop(),
-                            mIconView.getPaddingRight(),
-                            mIconView.getPaddingBottom());
-                    mIconView.setVisibility(View.GONE);
-                }
-            } else {
-                // Hide the title template
-                final View titleTemplate = mWindow.findViewById(R.id.title_template);
-                titleTemplate.setVisibility(View.GONE);
-                mIconView.setVisibility(View.GONE);
-                topPanel.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    private void setupContent(ViewGroup contentPanel) {
-        mScrollView = (NestedScrollView) mWindow.findViewById(R.id.scrollView);
-        mScrollView.setFocusable(false);
-        mScrollView.setNestedScrollingEnabled(false);
-
-        // Special case for users that only want to display a String
-        mMessageView = (TextView) contentPanel.findViewById(android.R.id.message);
-        if (mMessageView == null) {
-            return;
-        }
-
-        if (mMessage != null) {
-            mMessageView.setText(mMessage);
-        } else {
-            mMessageView.setVisibility(View.GONE);
-            mScrollView.removeView(mMessageView);
-
-            if (mListView != null) {
-                final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
-                final int childIndex = scrollParent.indexOfChild(mScrollView);
-                scrollParent.removeViewAt(childIndex);
-                scrollParent.addView(mListView, childIndex,
-                        new LayoutParams(MATCH_PARENT, MATCH_PARENT));
-            } else {
-                contentPanel.setVisibility(View.GONE);
-            }
-        }
-    }
-
-    static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
-        if (upIndicator != null) {
-            upIndicator.setVisibility(
-                    v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE);
-        }
-        if (downIndicator != null) {
-            downIndicator.setVisibility(
-                    v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE);
-        }
-    }
-
-    private void setupButtons(ViewGroup buttonPanel) {
-        int BIT_BUTTON_POSITIVE = 1;
-        int BIT_BUTTON_NEGATIVE = 2;
-        int BIT_BUTTON_NEUTRAL = 4;
-        int whichButtons = 0;
-        mButtonPositive = (Button) buttonPanel.findViewById(android.R.id.button1);
-        mButtonPositive.setOnClickListener(mButtonHandler);
-
-        if (TextUtils.isEmpty(mButtonPositiveText) && mButtonPositiveIcon == null) {
-            mButtonPositive.setVisibility(View.GONE);
-        } else {
-            mButtonPositive.setText(mButtonPositiveText);
-            if (mButtonPositiveIcon != null) {
-                mButtonPositiveIcon.setBounds(0, 0, mButtonIconDimen, mButtonIconDimen);
-                mButtonPositive.setCompoundDrawables(mButtonPositiveIcon, null, null, null);
-            }
-            mButtonPositive.setVisibility(View.VISIBLE);
-            whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
-        }
-
-        mButtonNegative = buttonPanel.findViewById(android.R.id.button2);
-        mButtonNegative.setOnClickListener(mButtonHandler);
-
-        if (TextUtils.isEmpty(mButtonNegativeText) && mButtonNegativeIcon == null) {
-            mButtonNegative.setVisibility(View.GONE);
-        } else {
-            mButtonNegative.setText(mButtonNegativeText);
-            if (mButtonNegativeIcon != null) {
-                mButtonNegativeIcon.setBounds(0, 0, mButtonIconDimen, mButtonIconDimen);
-                mButtonNegative.setCompoundDrawables(mButtonNegativeIcon, null, null, null);
-            }
-            mButtonNegative.setVisibility(View.VISIBLE);
-            whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
-        }
-
-        mButtonNeutral = (Button) buttonPanel.findViewById(android.R.id.button3);
-        mButtonNeutral.setOnClickListener(mButtonHandler);
-
-        if (TextUtils.isEmpty(mButtonNeutralText) && mButtonNeutralIcon == null) {
-            mButtonNeutral.setVisibility(View.GONE);
-        } else {
-            mButtonNeutral.setText(mButtonNeutralText);
-            if (mButtonPositiveIcon != null) {
-                mButtonPositiveIcon.setBounds(0, 0, mButtonIconDimen, mButtonIconDimen);
-                mButtonPositive.setCompoundDrawables(mButtonPositiveIcon, null, null, null);
-            }
-            mButtonNeutral.setVisibility(View.VISIBLE);
-            whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
-        }
-
-        if (shouldCenterSingleButton(mContext)) {
-            /*
-             * If we only have 1 button it should be centered on the layout and
-             * expand to fill 50% of the available space.
-             */
-            if (whichButtons == BIT_BUTTON_POSITIVE) {
-                centerButton(mButtonPositive);
-            } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
-                centerButton(mButtonNegative);
-            } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
-                centerButton(mButtonNeutral);
-            }
-        }
-
-        final boolean hasButtons = whichButtons != 0;
-        if (!hasButtons) {
-            buttonPanel.setVisibility(View.GONE);
-        }
-    }
-
-    private void centerButton(Button button) {
-        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
-        params.gravity = Gravity.CENTER_HORIZONTAL;
-        params.weight = 0.5f;
-        button.setLayoutParams(params);
-    }
-
-    public static class RecycleListView extends ListView {
-        private final int mPaddingTopNoTitle;
-        private final int mPaddingBottomNoButtons;
-
-        public RecycleListView(Context context) {
-            this(context, null);
-        }
-
-        public RecycleListView(Context context, AttributeSet attrs) {
-            super(context, attrs);
-
-            final TypedArray ta = context.obtainStyledAttributes(
-                    attrs, R.styleable.RecycleListView);
-            mPaddingBottomNoButtons = ta.getDimensionPixelOffset(
-                    R.styleable.RecycleListView_paddingBottomNoButtons, -1);
-            mPaddingTopNoTitle = ta.getDimensionPixelOffset(
-                    R.styleable.RecycleListView_paddingTopNoTitle, -1);
-        }
-
-        public void setHasDecor(boolean hasTitle, boolean hasButtons) {
-            if (!hasButtons || !hasTitle) {
-                final int paddingLeft = getPaddingLeft();
-                final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle;
-                final int paddingRight = getPaddingRight();
-                final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons;
-                setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
-            }
-        }
-    }
-
-    public static class AlertParams {
-        public final Context mContext;
-        public final LayoutInflater mInflater;
-
-        public int mIconId = 0;
-        public Drawable mIcon;
-        public int mIconAttrId = 0;
-        public CharSequence mTitle;
-        public View mCustomTitleView;
-        public CharSequence mMessage;
-        public CharSequence mPositiveButtonText;
-        public Drawable mPositiveButtonIcon;
-        public DialogInterface.OnClickListener mPositiveButtonListener;
-        public CharSequence mNegativeButtonText;
-        public Drawable mNegativeButtonIcon;
-        public DialogInterface.OnClickListener mNegativeButtonListener;
-        public CharSequence mNeutralButtonText;
-        public Drawable mNeutralButtonIcon;
-        public DialogInterface.OnClickListener mNeutralButtonListener;
-        public boolean mCancelable;
-        public DialogInterface.OnCancelListener mOnCancelListener;
-        public DialogInterface.OnDismissListener mOnDismissListener;
-        public DialogInterface.OnKeyListener mOnKeyListener;
-        public CharSequence[] mItems;
-        public ListAdapter mAdapter;
-        public DialogInterface.OnClickListener mOnClickListener;
-        public int mViewLayoutResId;
-        public View mView;
-        public int mViewSpacingLeft;
-        public int mViewSpacingTop;
-        public int mViewSpacingRight;
-        public int mViewSpacingBottom;
-        public boolean mViewSpacingSpecified = false;
-        public boolean[] mCheckedItems;
-        public boolean mIsMultiChoice;
-        public boolean mIsSingleChoice;
-        public int mCheckedItem = -1;
-        public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
-        public Cursor mCursor;
-        public String mLabelColumn;
-        public String mIsCheckedColumn;
-        public boolean mForceInverseBackground;
-        public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
-        public OnPrepareListViewListener mOnPrepareListViewListener;
-        public boolean mRecycleOnMeasure = true;
-
-        /**
-         * Interface definition for a callback to be invoked before the ListView
-         * will be bound to an adapter.
-         */
-        public interface OnPrepareListViewListener {
-
-            /**
-             * Called before the ListView is bound to an adapter.
-             * @param listView The ListView that will be shown in the dialog.
-             */
-            void onPrepareListView(ListView listView);
-        }
-
-        public AlertParams(Context context) {
-            mContext = context;
-            mCancelable = true;
-            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        }
-
-        public void apply(AlertController dialog) {
-            if (mCustomTitleView != null) {
-                dialog.setCustomTitle(mCustomTitleView);
-            } else {
-                if (mTitle != null) {
-                    dialog.setTitle(mTitle);
-                }
-                if (mIcon != null) {
-                    dialog.setIcon(mIcon);
-                }
-                if (mIconId != 0) {
-                    dialog.setIcon(mIconId);
-                }
-                if (mIconAttrId != 0) {
-                    dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
-                }
-            }
-            if (mMessage != null) {
-                dialog.setMessage(mMessage);
-            }
-            if (mPositiveButtonText != null || mPositiveButtonIcon != null) {
-                dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
-                        mPositiveButtonListener, null, mPositiveButtonIcon);
-            }
-            if (mNegativeButtonText != null || mNegativeButtonIcon != null) {
-                dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
-                        mNegativeButtonListener, null, mNegativeButtonIcon);
-            }
-            if (mNeutralButtonText != null || mNeutralButtonIcon != null) {
-                dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
-                        mNeutralButtonListener, null, mNeutralButtonIcon);
-            }
-            // For a list, the client can either supply an array of items or an
-            // adapter or a cursor
-            if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
-                createListView(dialog);
-            }
-            if (mView != null) {
-                if (mViewSpacingSpecified) {
-                    dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
-                            mViewSpacingBottom);
-                } else {
-                    dialog.setView(mView);
-                }
-            } else if (mViewLayoutResId != 0) {
-                dialog.setView(mViewLayoutResId);
-            }
-
-            /*
-            dialog.setCancelable(mCancelable);
-            dialog.setOnCancelListener(mOnCancelListener);
-            if (mOnKeyListener != null) {
-                dialog.setOnKeyListener(mOnKeyListener);
-            }
-            */
-        }
-
-        private void createListView(final AlertController dialog) {
-            final RecycleListView listView =
-                    (RecycleListView) mInflater.inflate(dialog.mListLayout, null);
-            final ListAdapter adapter;
-
-            if (mIsMultiChoice) {
-                if (mCursor == null) {
-                    adapter = new ArrayAdapter<CharSequence>(
-                            mContext, dialog.mMultiChoiceItemLayout, android.R.id.text1, mItems) {
-                        @Override
-                        public View getView(int position, View convertView, ViewGroup parent) {
-                            View view = super.getView(position, convertView, parent);
-                            if (mCheckedItems != null) {
-                                boolean isItemChecked = mCheckedItems[position];
-                                if (isItemChecked) {
-                                    listView.setItemChecked(position, true);
-                                }
-                            }
-                            return view;
-                        }
-                    };
-                } else {
-                    adapter = new CursorAdapter(mContext, mCursor, false) {
-                        private final int mLabelIndex;
-                        private final int mIsCheckedIndex;
-
-                        {
-                            final Cursor cursor = getCursor();
-                            mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
-                            mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
-                        }
-
-                        @Override
-                        public void bindView(View view, Context context, Cursor cursor) {
-                            CheckedTextView text = (CheckedTextView) view.findViewById(
-                                    android.R.id.text1);
-                            text.setText(cursor.getString(mLabelIndex));
-                            listView.setItemChecked(cursor.getPosition(),
-                                    cursor.getInt(mIsCheckedIndex) == 1);
-                        }
-
-                        @Override
-                        public View newView(Context context, Cursor cursor, ViewGroup parent) {
-                            return mInflater.inflate(dialog.mMultiChoiceItemLayout,
-                                    parent, false);
-                        }
-
-                    };
-                }
-            } else {
-                final int layout;
-                if (mIsSingleChoice) {
-                    layout = dialog.mSingleChoiceItemLayout;
-                } else {
-                    layout = dialog.mListItemLayout;
-                }
-
-                if (mCursor != null) {
-                    adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
-                            new String[] { mLabelColumn }, new int[] { android.R.id.text1 });
-                } else if (mAdapter != null) {
-                    adapter = mAdapter;
-                } else {
-                    adapter = new CheckedItemAdapter(mContext, layout, android.R.id.text1, mItems);
-                }
-            }
-
-            if (mOnPrepareListViewListener != null) {
-                mOnPrepareListViewListener.onPrepareListView(listView);
-            }
-
-            /* Don't directly set the adapter on the ListView as we might
-             * want to add a footer to the ListView later.
-             */
-            dialog.mAdapter = adapter;
-            dialog.mCheckedItem = mCheckedItem;
-
-            if (mOnClickListener != null) {
-                listView.setOnItemClickListener(new OnItemClickListener() {
-                    @Override
-                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
-                        mOnClickListener.onClick(dialog.mDialog, position);
-                        if (!mIsSingleChoice) {
-                            dialog.mDialog.dismiss();
-                        }
-                    }
-                });
-            } else if (mOnCheckboxClickListener != null) {
-                listView.setOnItemClickListener(new OnItemClickListener() {
-                    @Override
-                    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
-                        if (mCheckedItems != null) {
-                            mCheckedItems[position] = listView.isItemChecked(position);
-                        }
-                        mOnCheckboxClickListener.onClick(
-                                dialog.mDialog, position, listView.isItemChecked(position));
-                    }
-                });
-            }
-
-            // Attach a given OnItemSelectedListener to the ListView
-            if (mOnItemSelectedListener != null) {
-                listView.setOnItemSelectedListener(mOnItemSelectedListener);
-            }
-
-            if (mIsSingleChoice) {
-                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-            } else if (mIsMultiChoice) {
-                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-            }
-            dialog.mListView = listView;
-        }
-    }
-
-    private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
-        public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
-                CharSequence[] objects) {
-            super(context, resource, textViewResourceId, objects);
-        }
-
-        @Override
-        public boolean hasStableIds() {
-            return true;
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-    }
-}
diff --git a/android/support/v7/app/AlertDialog.java b/android/support/v7/app/AlertDialog.java
deleted file mode 100644
index 1712f20..0000000
--- a/android/support/v7/app/AlertDialog.java
+++ /dev/null
@@ -1,1011 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Message;
-import android.support.annotation.ArrayRes;
-import android.support.annotation.AttrRes;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.annotation.StyleRes;
-import android.support.v7.appcompat.R;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.Button;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-
-/**
- * A subclass of Dialog that can display one, two or three buttons. If you only want to
- * display a String in this dialog box, use the setMessage() method.  If you
- * want to display a more complex view, look up the FrameLayout called "custom"
- * and add your view to it:
- *
- * <pre>
- * FrameLayout fl = findViewById(android.R.id.custom);
- * fl.addView(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
- * </pre>
- *
- * <p>The AlertDialog class takes care of automatically setting
- * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
- * android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} for you based on whether
- * any views in the dialog return true from {@link View#onCheckIsTextEditor()
- * View.onCheckIsTextEditor()}.  Generally you want this set for a Dialog
- * without text editors, so that it will be placed on top of the current
- * input method UI.  You can modify this behavior by forcing the flag to your
- * desired mode after calling {@link #onCreate}.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about creating dialogs, read the
- * <a href="{@docRoot}guide/topics/ui/dialogs.html">Dialogs</a> developer guide.</p>
- * </div>
- */
-public class AlertDialog extends AppCompatDialog implements DialogInterface {
-
-    final AlertController mAlert;
-
-    /**
-     * No layout hint.
-     */
-    static final int LAYOUT_HINT_NONE = 0;
-
-    /**
-     * Hint layout to the side.
-     */
-    static final int LAYOUT_HINT_SIDE = 1;
-
-    protected AlertDialog(@NonNull Context context) {
-        this(context, 0);
-    }
-
-    /**
-     * Construct an AlertDialog that uses an explicit theme.  The actual style
-     * that an AlertDialog uses is a private implementation, however you can
-     * here supply either the name of an attribute in the theme from which
-     * to get the dialog's style (such as {@link R.attr#alertDialogTheme}.
-     */
-    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
-        super(context, resolveDialogTheme(context, themeResId));
-        mAlert = new AlertController(getContext(), this, getWindow());
-    }
-
-    protected AlertDialog(@NonNull Context context, boolean cancelable,
-            @Nullable OnCancelListener cancelListener) {
-        this(context, 0);
-        setCancelable(cancelable);
-        setOnCancelListener(cancelListener);
-    }
-
-    static int resolveDialogTheme(@NonNull Context context, @StyleRes int resid) {
-        // 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();
-            context.getTheme().resolveAttribute(R.attr.alertDialogTheme, outValue, true);
-            return outValue.resourceId;
-        }
-    }
-
-    /**
-     * Gets one of the buttons used in the dialog. Returns null if the specified
-     * button does not exist or the dialog has not yet been fully created (for
-     * example, via {@link #show()} or {@link #create()}).
-     *
-     * @param whichButton The identifier of the button that should be returned.
-     *                    For example, this can be
-     *                    {@link DialogInterface#BUTTON_POSITIVE}.
-     * @return The button from the dialog, or null if a button does not exist.
-     */
-    public Button getButton(int whichButton) {
-        return mAlert.getButton(whichButton);
-    }
-
-    /**
-     * Gets the list view used in the dialog.
-     *
-     * @return The {@link ListView} from the dialog.
-     */
-    public ListView getListView() {
-        return mAlert.getListView();
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        super.setTitle(title);
-        mAlert.setTitle(title);
-    }
-
-    /**
-     * This method has no effect if called after {@link #show()}.
-     *
-     * @see Builder#setCustomTitle(View)
-     */
-    public void setCustomTitle(View customTitleView) {
-        mAlert.setCustomTitle(customTitleView);
-    }
-
-    /**
-     * Sets the message to display.
-     *
-     * @param message The message to display in the dialog.
-     */
-    public void setMessage(CharSequence message) {
-        mAlert.setMessage(message);
-    }
-
-    /**
-     * Set the view to display in the dialog. This method has no effect if called
-     * after {@link #show()}.
-     */
-    public void setView(View view) {
-        mAlert.setView(view);
-    }
-
-    /**
-     * Set the view to display in the dialog, specifying the spacing to appear around that
-     * view.  This method has no effect if called after {@link #show()}.
-     *
-     * @param view              The view to show in the content area of the dialog
-     * @param viewSpacingLeft   Extra space to appear to the left of {@code view}
-     * @param viewSpacingTop    Extra space to appear above {@code view}
-     * @param viewSpacingRight  Extra space to appear to the right of {@code view}
-     * @param viewSpacingBottom Extra space to appear below {@code view}
-     */
-    public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
-            int viewSpacingBottom) {
-        mAlert.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight, viewSpacingBottom);
-    }
-
-    /**
-     * Internal api to allow hinting for the best button panel layout.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    void setButtonPanelLayoutHint(int layoutHint) {
-        mAlert.setButtonPanelLayoutHint(layoutHint);
-    }
-
-    /**
-     * Sets a message to be sent when a button is pressed. This method has no effect if called
-     * after {@link #show()}.
-     *
-     * @param whichButton Which button to set the message for, can be one of
-     *                    {@link DialogInterface#BUTTON_POSITIVE},
-     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
-     *                    {@link DialogInterface#BUTTON_NEUTRAL}
-     * @param text        The text to display in positive button.
-     * @param msg         The {@link Message} to be sent when clicked.
-     */
-    public void setButton(int whichButton, CharSequence text, Message msg) {
-        mAlert.setButton(whichButton, text, null, msg, null);
-    }
-
-    /**
-     * Sets a listener to be invoked when the positive button of the dialog is pressed. This method
-     * has no effect if called after {@link #show()}.
-     *
-     * @param whichButton Which button to set the listener on, can be one of
-     *                    {@link DialogInterface#BUTTON_POSITIVE},
-     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
-     *                    {@link DialogInterface#BUTTON_NEUTRAL}
-     * @param text        The text to display in positive button.
-     * @param listener    The {@link DialogInterface.OnClickListener} to use.
-     */
-    public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
-        mAlert.setButton(whichButton, text, listener, null, null);
-    }
-
-    /**
-     * Sets an icon to be displayed along with the button text and a listener to be invoked when
-     * the positive button of the dialog is pressed. This method has no effect if called after
-     * {@link #show()}.
-     *
-     * @param whichButton Which button to set the listener on, can be one of
-     *                    {@link DialogInterface#BUTTON_POSITIVE},
-     *                    {@link DialogInterface#BUTTON_NEGATIVE}, or
-     *                    {@link DialogInterface#BUTTON_NEUTRAL}
-     * @param text        The text to display in positive button.
-     * @param listener    The {@link DialogInterface.OnClickListener} to use.
-     * @param icon        The {@link Drawable} to be set as an icon for the button.
-     */
-    public void setButton(int whichButton, CharSequence text, Drawable icon,
-            OnClickListener listener) {
-        mAlert.setButton(whichButton, text, listener, null,  icon);
-    }
-
-    /**
-     * Set resId to 0 if you don't want an icon.
-     * @param resId the resourceId of the drawable to use as the icon or 0
-     * if you don't want an icon.
-     */
-    public void setIcon(int resId) {
-        mAlert.setIcon(resId);
-    }
-
-    /**
-     * Set the {@link Drawable} to be used in the title.
-     *
-     * @param icon Drawable to use as the icon or null if you don't want an icon.
-     */
-    public void setIcon(Drawable icon) {
-        mAlert.setIcon(icon);
-    }
-
-    /**
-     * Sets an icon as supplied by a theme attribute. e.g. android.R.attr.alertDialogIcon
-     *
-     * @param attrId ID of a theme attribute that points to a drawable resource.
-     */
-    public void setIconAttribute(int attrId) {
-        TypedValue out = new TypedValue();
-        getContext().getTheme().resolveAttribute(attrId, out, true);
-        mAlert.setIcon(out.resourceId);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mAlert.installContent();
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (mAlert.onKeyDown(keyCode, event)) {
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (mAlert.onKeyUp(keyCode, event)) {
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    public static class Builder {
-        private final AlertController.AlertParams P;
-        private final int mTheme;
-
-        /**
-         * Creates a builder for an alert dialog that uses the default alert
-         * dialog theme.
-         * <p>
-         * The default alert dialog theme is defined by
-         * {@link android.R.attr#alertDialogTheme} within the parent
-         * {@code context}'s theme.
-         *
-         * @param context the parent context
-         */
-        public Builder(@NonNull Context context) {
-            this(context, resolveDialogTheme(context, 0));
-        }
-
-        /**
-         * Creates a builder for an alert dialog that uses an explicit theme
-         * resource.
-         * <p>
-         * The specified theme resource ({@code themeResId}) is applied on top
-         * of the parent {@code context}'s theme. It may be specified as a
-         * style resource containing a fully-populated theme, such as
-         * {@link R.style#Theme_AppCompat_Dialog}, to replace all
-         * attributes in the parent {@code context}'s theme including primary
-         * and accent colors.
-         * <p>
-         * To preserve attributes such as primary and accent colors, the
-         * {@code themeResId} may instead be specified as an overlay theme such
-         * as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
-         * override only the window attributes necessary to style the alert
-         * window as a dialog.
-         * <p>
-         * Alternatively, the {@code themeResId} may be specified as {@code 0}
-         * to use the parent {@code context}'s resolved value for
-         * {@link android.R.attr#alertDialogTheme}.
-         *
-         * @param context the parent context
-         * @param themeResId the resource ID of the theme against which to inflate
-         *                   this dialog, or {@code 0} to use the parent
-         *                   {@code context}'s default alert dialog theme
-         */
-        public Builder(@NonNull Context context, @StyleRes int themeResId) {
-            P = new AlertController.AlertParams(new ContextThemeWrapper(
-                    context, resolveDialogTheme(context, themeResId)));
-            mTheme = themeResId;
-        }
-
-        /**
-         * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
-         * Applications should use this Context for obtaining LayoutInflaters for inflating views
-         * that will be used in the resulting dialogs, as it will cause views to be inflated with
-         * the correct theme.
-         *
-         * @return A Context for built Dialogs.
-         */
-        @NonNull
-        public Context getContext() {
-            return P.mContext;
-        }
-
-        /**
-         * Set the title using the given resource id.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setTitle(@StringRes int titleId) {
-            P.mTitle = P.mContext.getText(titleId);
-            return this;
-        }
-
-        /**
-         * Set the title displayed in the {@link Dialog}.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setTitle(@Nullable CharSequence title) {
-            P.mTitle = title;
-            return this;
-        }
-
-        /**
-         * Set the title using the custom view {@code customTitleView}.
-         * <p>
-         * The methods {@link #setTitle(int)} and {@link #setIcon(int)} should
-         * be sufficient for most titles, but this is provided if the title
-         * needs more customization. Using this will replace the title and icon
-         * set via the other methods.
-         * <p>
-         * <strong>Note:</strong> To ensure consistent styling, the custom view
-         * should be inflated or constructed using the alert dialog's themed
-         * context obtained via {@link #getContext()}.
-         *
-         * @param customTitleView the custom view to use as the title
-         * @return this Builder object to allow for chaining of calls to set
-         *         methods
-         */
-        public Builder setCustomTitle(@Nullable View customTitleView) {
-            P.mCustomTitleView = customTitleView;
-            return this;
-        }
-
-        /**
-         * Set the message to display using the given resource id.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setMessage(@StringRes int messageId) {
-            P.mMessage = P.mContext.getText(messageId);
-            return this;
-        }
-
-        /**
-         * Set the message to display.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setMessage(@Nullable CharSequence message) {
-            P.mMessage = message;
-            return this;
-        }
-
-        /**
-         * Set the resource id of the {@link Drawable} to be used in the title.
-         * <p>
-         * Takes precedence over values set using {@link #setIcon(Drawable)}.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setIcon(@DrawableRes int iconId) {
-            P.mIconId = iconId;
-            return this;
-        }
-
-        /**
-         * Set the {@link Drawable} to be used in the title.
-         * <p>
-         * <strong>Note:</strong> To ensure consistent styling, the drawable
-         * should be inflated or constructed using the alert dialog's themed
-         * context obtained via {@link #getContext()}.
-         *
-         * @return this Builder object to allow for chaining of calls to set
-         *         methods
-         */
-        public Builder setIcon(@Nullable Drawable icon) {
-            P.mIcon = icon;
-            return this;
-        }
-
-        /**
-         * Set an icon as supplied by a theme attribute. e.g.
-         * {@link android.R.attr#alertDialogIcon}.
-         * <p>
-         * Takes precedence over values set using {@link #setIcon(int)} or
-         * {@link #setIcon(Drawable)}.
-         *
-         * @param attrId ID of a theme attribute that points to a drawable resource.
-         */
-        public Builder setIconAttribute(@AttrRes int attrId) {
-            TypedValue out = new TypedValue();
-            P.mContext.getTheme().resolveAttribute(attrId, out, true);
-            P.mIconId = out.resourceId;
-            return this;
-        }
-
-        /**
-         * Set a listener to be invoked when the positive button of the dialog is pressed.
-         * @param textId The resource id of the text to display in the positive button
-         * @param listener The {@link DialogInterface.OnClickListener} to use.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
-            P.mPositiveButtonText = P.mContext.getText(textId);
-            P.mPositiveButtonListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a listener to be invoked when the positive button of the dialog is pressed.
-         * @param text The text to display in the positive button
-         * @param listener The {@link DialogInterface.OnClickListener} to use.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
-            P.mPositiveButtonText = text;
-            P.mPositiveButtonListener = listener;
-            return this;
-        }
-
-        /**
-         * Set an icon to be displayed for the positive button.
-         * @param icon The icon to be displayed
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setPositiveButtonIcon(Drawable icon) {
-            P.mPositiveButtonIcon = icon;
-            return this;
-        }
-
-        /**
-         * Set a listener to be invoked when the negative button of the dialog is pressed.
-         * @param textId The resource id of the text to display in the negative button
-         * @param listener The {@link DialogInterface.OnClickListener} to use.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setNegativeButton(@StringRes int textId, final OnClickListener listener) {
-            P.mNegativeButtonText = P.mContext.getText(textId);
-            P.mNegativeButtonListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a listener to be invoked when the negative button of the dialog is pressed.
-         * @param text The text to display in the negative button
-         * @param listener The {@link DialogInterface.OnClickListener} to use.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
-            P.mNegativeButtonText = text;
-            P.mNegativeButtonListener = listener;
-            return this;
-        }
-
-        /**
-         * Set an icon to be displayed for the negative button.
-         * @param icon The icon to be displayed
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setNegativeButtonIcon(Drawable icon) {
-            P.mNegativeButtonIcon = icon;
-            return this;
-        }
-
-        /**
-         * Set a listener to be invoked when the neutral button of the dialog is pressed.
-         * @param textId The resource id of the text to display in the neutral button
-         * @param listener The {@link DialogInterface.OnClickListener} to use.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setNeutralButton(@StringRes int textId, final OnClickListener listener) {
-            P.mNeutralButtonText = P.mContext.getText(textId);
-            P.mNeutralButtonListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a listener to be invoked when the neutral button of the dialog is pressed.
-         * @param text The text to display in the neutral button
-         * @param listener The {@link DialogInterface.OnClickListener} to use.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setNeutralButton(CharSequence text, final OnClickListener listener) {
-            P.mNeutralButtonText = text;
-            P.mNeutralButtonListener = listener;
-            return this;
-        }
-
-        /**
-         * Set an icon to be displayed for the neutral button.
-         * @param icon The icon to be displayed
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setNeutralButtonIcon(Drawable icon) {
-            P.mNeutralButtonIcon = icon;
-            return this;
-        }
-
-        /**
-         * Sets whether the dialog is cancelable or not.  Default is true.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setCancelable(boolean cancelable) {
-            P.mCancelable = cancelable;
-            return this;
-        }
-
-        /**
-         * Sets the callback that will be called if the dialog is canceled.
-         *
-         * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than
-         * being canceled or one of the supplied choices being selected.
-         * If you are interested in listening for all cases where the dialog is dismissed
-         * and not just when it is canceled, see
-         * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
-         * setOnDismissListener}.</p>
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         * @see #setCancelable(boolean)
-         * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener)
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
-            P.mOnCancelListener = onCancelListener;
-            return this;
-        }
-
-        /**
-         * Sets the callback that will be called when the dialog is dismissed for any reason.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
-            P.mOnDismissListener = onDismissListener;
-            return this;
-        }
-
-        /**
-         * Sets the callback that will be called if a key is dispatched to the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setOnKeyListener(OnKeyListener onKeyListener) {
-            P.mOnKeyListener = onKeyListener;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of the
-         * selected item via the supplied listener. This should be an array type i.e. R.array.foo
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setItems(@ArrayRes int itemsId, final OnClickListener listener) {
-            P.mItems = P.mContext.getResources().getTextArray(itemsId);
-            P.mOnClickListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of the
-         * selected item via the supplied listener.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setItems(CharSequence[] items, final OnClickListener listener) {
-            P.mItems = items;
-            P.mOnClickListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a list of items, which are supplied by the given {@link ListAdapter}, to be
-         * displayed in the dialog as the content, you will be notified of the
-         * selected item via the supplied listener.
-         *
-         * @param adapter The {@link ListAdapter} to supply the list of items
-         * @param listener The listener that will be called when an item is clicked.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setAdapter(final ListAdapter adapter, final OnClickListener listener) {
-            P.mAdapter = adapter;
-            P.mOnClickListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a list of items, which are supplied by the given {@link Cursor}, to be
-         * displayed in the dialog as the content, you will be notified of the
-         * selected item via the supplied listener.
-         *
-         * @param cursor The {@link Cursor} to supply the list of items
-         * @param listener The listener that will be called when an item is clicked.
-         * @param labelColumn The column name on the cursor containing the string to display
-         *          in the label.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setCursor(final Cursor cursor, final OnClickListener listener,
-                String labelColumn) {
-            P.mCursor = cursor;
-            P.mLabelColumn = labelColumn;
-            P.mOnClickListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content,
-         * you will be notified of the selected item via the supplied listener.
-         * This should be an array type, e.g. R.array.foo. The list will have
-         * a check mark displayed to the right of the text for each checked
-         * item. Clicking on an item in the list will not dismiss the dialog.
-         * Clicking on a button will dismiss the dialog.
-         *
-         * @param itemsId the resource id of an array i.e. R.array.foo
-         * @param checkedItems specifies which items are checked. It should be null in which case no
-         *        items are checked. If non null it must be exactly the same length as the array of
-         *        items.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setMultiChoiceItems(@ArrayRes int itemsId, boolean[] checkedItems,
-                final OnMultiChoiceClickListener listener) {
-            P.mItems = P.mContext.getResources().getTextArray(itemsId);
-            P.mOnCheckboxClickListener = listener;
-            P.mCheckedItems = checkedItems;
-            P.mIsMultiChoice = true;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content,
-         * you will be notified of the selected item via the supplied listener.
-         * The list will have a check mark displayed to the right of the text
-         * for each checked item. Clicking on an item in the list will not
-         * dismiss the dialog. Clicking on a button will dismiss the dialog.
-         *
-         * @param items the text of the items to be displayed in the list.
-         * @param checkedItems specifies which items are checked. It should be null in which case no
-         *        items are checked. If non null it must be exactly the same length as the array of
-         *        items.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
-                final OnMultiChoiceClickListener listener) {
-            P.mItems = items;
-            P.mOnCheckboxClickListener = listener;
-            P.mCheckedItems = checkedItems;
-            P.mIsMultiChoice = true;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content,
-         * you will be notified of the selected item via the supplied listener.
-         * The list will have a check mark displayed to the right of the text
-         * for each checked item. Clicking on an item in the list will not
-         * dismiss the dialog. Clicking on a button will dismiss the dialog.
-         *
-         * @param cursor the cursor used to provide the items.
-         * @param isCheckedColumn specifies the column name on the cursor to use to determine
-         *        whether a checkbox is checked or not. It must return an integer value where 1
-         *        means checked and 0 means unchecked.
-         * @param labelColumn The column name on the cursor containing the string to display in the
-         *        label.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn,
-                final OnMultiChoiceClickListener listener) {
-            P.mCursor = cursor;
-            P.mOnCheckboxClickListener = listener;
-            P.mIsCheckedColumn = isCheckedColumn;
-            P.mLabelColumn = labelColumn;
-            P.mIsMultiChoice = true;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of
-         * the selected item via the supplied listener. This should be an array type i.e.
-         * R.array.foo The list will have a check mark displayed to the right of the text for the
-         * checked item. Clicking on an item in the list will not dismiss the dialog. Clicking on a
-         * button will dismiss the dialog.
-         *
-         * @param itemsId the resource id of an array i.e. R.array.foo
-         * @param checkedItem specifies which item is checked. If -1 no items are checked.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setSingleChoiceItems(@ArrayRes int itemsId, int checkedItem,
-                final OnClickListener listener) {
-            P.mItems = P.mContext.getResources().getTextArray(itemsId);
-            P.mOnClickListener = listener;
-            P.mCheckedItem = checkedItem;
-            P.mIsSingleChoice = true;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of
-         * the selected item via the supplied listener. The list will have a check mark displayed to
-         * the right of the text for the checked item. Clicking on an item in the list will not
-         * dismiss the dialog. Clicking on a button will dismiss the dialog.
-         *
-         * @param cursor the cursor to retrieve the items from.
-         * @param checkedItem specifies which item is checked. If -1 no items are checked.
-         * @param labelColumn The column name on the cursor containing the string to display in the
-         *        label.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn,
-                final OnClickListener listener) {
-            P.mCursor = cursor;
-            P.mOnClickListener = listener;
-            P.mCheckedItem = checkedItem;
-            P.mLabelColumn = labelColumn;
-            P.mIsSingleChoice = true;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of
-         * the selected item via the supplied listener. The list will have a check mark displayed to
-         * the right of the text for the checked item. Clicking on an item in the list will not
-         * dismiss the dialog. Clicking on a button will dismiss the dialog.
-         *
-         * @param items the items to be displayed.
-         * @param checkedItem specifies which item is checked. If -1 no items are checked.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener) {
-            P.mItems = items;
-            P.mOnClickListener = listener;
-            P.mCheckedItem = checkedItem;
-            P.mIsSingleChoice = true;
-            return this;
-        }
-
-        /**
-         * Set a list of items to be displayed in the dialog as the content, you will be notified of
-         * the selected item via the supplied listener. The list will have a check mark displayed to
-         * the right of the text for the checked item. Clicking on an item in the list will not
-         * dismiss the dialog. Clicking on a button will dismiss the dialog.
-         *
-         * @param adapter The {@link ListAdapter} to supply the list of items
-         * @param checkedItem specifies which item is checked. If -1 no items are checked.
-         * @param listener notified when an item on the list is clicked. The dialog will not be
-         *        dismissed when an item is clicked. It will only be dismissed if clicked on a
-         *        button, if no buttons are supplied it's up to the user to dismiss the dialog.
-         *
-         * @return This Builder object to allow for chaining of calls to set methods
-         */
-        public Builder setSingleChoiceItems(ListAdapter adapter, int checkedItem, final OnClickListener listener) {
-            P.mAdapter = adapter;
-            P.mOnClickListener = listener;
-            P.mCheckedItem = checkedItem;
-            P.mIsSingleChoice = true;
-            return this;
-        }
-
-        /**
-         * Sets a listener to be invoked when an item in the list is selected.
-         *
-         * @param listener the listener to be invoked
-         * @return this Builder object to allow for chaining of calls to set methods
-         * @see AdapterView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
-         */
-        public Builder setOnItemSelectedListener(final AdapterView.OnItemSelectedListener listener) {
-            P.mOnItemSelectedListener = listener;
-            return this;
-        }
-
-        /**
-         * Set a custom view resource to be the contents of the Dialog. The
-         * resource will be inflated, adding all top-level views to the screen.
-         *
-         * @param layoutResId Resource ID to be inflated.
-         * @return this Builder object to allow for chaining of calls to set
-         *         methods
-         */
-        public Builder setView(int layoutResId) {
-            P.mView = null;
-            P.mViewLayoutResId = layoutResId;
-            P.mViewSpacingSpecified = false;
-            return this;
-        }
-
-        /**
-         * Sets a custom view to be the contents of the alert dialog.
-         * <p>
-         * When using a pre-Holo theme, if the supplied view is an instance of
-         * a {@link ListView} then the light background will be used.
-         * <p>
-         * <strong>Note:</strong> To ensure consistent styling, the custom view
-         * should be inflated or constructed using the alert dialog's themed
-         * context obtained via {@link #getContext()}.
-         *
-         * @param view the view to use as the contents of the alert dialog
-         * @return this Builder object to allow for chaining of calls to set
-         *         methods
-         */
-        public Builder setView(View view) {
-            P.mView = view;
-            P.mViewLayoutResId = 0;
-            P.mViewSpacingSpecified = false;
-            return this;
-        }
-
-        /**
-         * Set a custom view to be the contents of the Dialog, specifying the
-         * spacing to appear around that view. If the supplied view is an
-         * instance of a {@link ListView} the light background will be used.
-         *
-         * @param view              The view to use as the contents of the Dialog.
-         * @param viewSpacingLeft   Spacing between the left edge of the view and
-         *                          the dialog frame
-         * @param viewSpacingTop    Spacing between the top edge of the view and
-         *                          the dialog frame
-         * @param viewSpacingRight  Spacing between the right edge of the view
-         *                          and the dialog frame
-         * @param viewSpacingBottom Spacing between the bottom edge of the view
-         *                          and the dialog frame
-         * @return This Builder object to allow for chaining of calls to set
-         * methods
-         *
-         *
-         * This is currently hidden because it seems like people should just
-         * be able to put padding around the view.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        @Deprecated
-        public Builder setView(View view, int viewSpacingLeft, int viewSpacingTop,
-                int viewSpacingRight, int viewSpacingBottom) {
-            P.mView = view;
-            P.mViewLayoutResId = 0;
-            P.mViewSpacingSpecified = true;
-            P.mViewSpacingLeft = viewSpacingLeft;
-            P.mViewSpacingTop = viewSpacingTop;
-            P.mViewSpacingRight = viewSpacingRight;
-            P.mViewSpacingBottom = viewSpacingBottom;
-            return this;
-        }
-
-        /**
-         * Sets the Dialog to use the inverse background, regardless of what the
-         * contents is.
-         *
-         * @param useInverseBackground Whether to use the inverse background
-         * @return This Builder object to allow for chaining of calls to set methods
-         * @deprecated This flag is only used for pre-Material themes. Instead,
-         *             specify the window background using on the alert dialog
-         *             theme.
-         */
-        @Deprecated
-        public Builder setInverseBackgroundForced(boolean useInverseBackground) {
-            P.mForceInverseBackground = useInverseBackground;
-            return this;
-        }
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public Builder setRecycleOnMeasureEnabled(boolean enabled) {
-            P.mRecycleOnMeasure = enabled;
-            return this;
-        }
-
-
-        /**
-         * Creates an {@link AlertDialog} with the arguments supplied to this
-         * builder.
-         * <p>
-         * Calling this method does not display the dialog. If no additional
-         * processing is needed, {@link #show()} may be called instead to both
-         * create and display the dialog.
-         */
-        public AlertDialog create() {
-            // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
-            // so we always have to re-set the theme
-            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
-            P.apply(dialog.mAlert);
-            dialog.setCancelable(P.mCancelable);
-            if (P.mCancelable) {
-                dialog.setCanceledOnTouchOutside(true);
-            }
-            dialog.setOnCancelListener(P.mOnCancelListener);
-            dialog.setOnDismissListener(P.mOnDismissListener);
-            if (P.mOnKeyListener != null) {
-                dialog.setOnKeyListener(P.mOnKeyListener);
-            }
-            return dialog;
-        }
-
-        /**
-         * Creates an {@link AlertDialog} with the arguments supplied to this
-         * builder and immediately displays the dialog.
-         * <p>
-         * Calling this method is functionally identical to:
-         * <pre>
-         *     AlertDialog dialog = builder.create();
-         *     dialog.show();
-         * </pre>
-         */
-        public AlertDialog show() {
-            final AlertDialog dialog = create();
-            dialog.show();
-            return dialog;
-        }
-    }
-
-}
diff --git a/android/support/v7/app/AppCompatActivity.java b/android/support/v7/app/AppCompatActivity.java
deleted file mode 100644
index c8d7b0a..0000000
--- a/android/support/v7/app/AppCompatActivity.java
+++ /dev/null
@@ -1,591 +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.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.annotation.IdRes;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StyleRes;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.NavUtils;
-import android.support.v4.app.TaskStackBuilder;
-import android.support.v7.view.ActionMode;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.VectorEnabledTintResources;
-import android.util.DisplayMetrics;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-
-/**
- * Base class for activities that use the
- * <a href="{@docRoot}tools/extras/support-library.html">support library</a> action bar features.
- *
- * <p>You can add an {@link android.support.v7.app.ActionBar} to your activity when running on API level 7 or higher
- * by extending this class for your activity and setting the activity theme to
- * {@link android.support.v7.appcompat.R.style#Theme_AppCompat Theme.AppCompat} or a similar theme.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- *
- * <p>For information about how to use the action bar, including how to add action items, navigation
- * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action
- * Bar</a> API guide.</p>
- * </div>
- */
-public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
-        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
-
-    private AppCompatDelegate mDelegate;
-    private int mThemeId = 0;
-    private Resources mResources;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        final AppCompatDelegate delegate = getDelegate();
-        delegate.installViewFactory();
-        delegate.onCreate(savedInstanceState);
-        if (delegate.applyDayNight() && mThemeId != 0) {
-            // If DayNight has been applied, we need to re-apply the theme for
-            // the changes to take effect. On API 23+, we should bypass
-            // setTheme(), which will no-op if the theme ID is identical to the
-            // current theme ID.
-            if (Build.VERSION.SDK_INT >= 23) {
-                onApplyThemeResource(getTheme(), mThemeId, false);
-            } else {
-                setTheme(mThemeId);
-            }
-        }
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public void setTheme(@StyleRes final int resid) {
-        super.setTheme(resid);
-        // Keep hold of the theme id so that we can re-set it later if needed
-        mThemeId = resid;
-    }
-
-    @Override
-    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-        getDelegate().onPostCreate(savedInstanceState);
-    }
-
-    /**
-     * Support library version of {@link android.app.Activity#getActionBar}.
-     *
-     * <p>Retrieve a reference to this activity's ActionBar.
-     *
-     * @return The Activity's ActionBar, or null if it does not have one.
-     */
-    @Nullable
-    public ActionBar getSupportActionBar() {
-        return getDelegate().getSupportActionBar();
-    }
-
-    /**
-     * Set a {@link android.widget.Toolbar Toolbar} to act as the
-     * {@link android.support.v7.app.ActionBar} for this Activity window.
-     *
-     * <p>When set to a non-null value the {@link #getActionBar()} method will return
-     * an {@link android.support.v7.app.ActionBar} object that can be used to control the given
-     * toolbar as if it were a traditional window decor action bar. The toolbar's menu will be
-     * populated with the Activity's options menu and the navigation button will be wired through
-     * the standard {@link android.R.id#home home} menu select action.</p>
-     *
-     * <p>In order to use a Toolbar within the Activity's window content the application
-     * must not request the window feature
-     * {@link android.view.Window#FEATURE_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
-     *
-     * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it
-     */
-    public void setSupportActionBar(@Nullable Toolbar toolbar) {
-        getDelegate().setSupportActionBar(toolbar);
-    }
-
-    @Override
-    public MenuInflater getMenuInflater() {
-        return getDelegate().getMenuInflater();
-    }
-
-    @Override
-    public void setContentView(@LayoutRes int layoutResID) {
-        getDelegate().setContentView(layoutResID);
-    }
-
-    @Override
-    public void setContentView(View view) {
-        getDelegate().setContentView(view);
-    }
-
-    @Override
-    public void setContentView(View view, ViewGroup.LayoutParams params) {
-        getDelegate().setContentView(view, params);
-    }
-
-    @Override
-    public void addContentView(View view, ViewGroup.LayoutParams params) {
-        getDelegate().addContentView(view, params);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        getDelegate().onConfigurationChanged(newConfig);
-        if (mResources != null) {
-            // The real (and thus managed) resources object was already updated
-            // by ResourcesManager, so pull the current metrics from there.
-            final DisplayMetrics newMetrics = super.getResources().getDisplayMetrics();
-            mResources.updateConfiguration(newConfig, newMetrics);
-        }
-    }
-
-    @Override
-    protected void onPostResume() {
-        super.onPostResume();
-        getDelegate().onPostResume();
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        getDelegate().onStart();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        getDelegate().onStop();
-    }
-
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    @Override
-    public <T extends View> T findViewById(@IdRes int id) {
-        return getDelegate().findViewById(id);
-    }
-
-    @Override
-    public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) {
-        if (super.onMenuItemSelected(featureId, item)) {
-            return true;
-        }
-
-        final ActionBar ab = getSupportActionBar();
-        if (item.getItemId() == android.R.id.home && ab != null &&
-                (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-            return onSupportNavigateUp();
-        }
-        return false;
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        getDelegate().onDestroy();
-    }
-
-    @Override
-    protected void onTitleChanged(CharSequence title, int color) {
-        super.onTitleChanged(title, color);
-        getDelegate().setTitle(title);
-    }
-
-    /**
-     * Enable extended support library window features.
-     * <p>
-     * This is a convenience for calling
-     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
-     * </p>
-     *
-     * @param featureId The desired feature as defined in
-     * {@link android.view.Window} or {@link android.support.v4.view.WindowCompat}.
-     * @return Returns true if the requested feature is supported and now enabled.
-     *
-     * @see android.app.Activity#requestWindowFeature
-     * @see android.view.Window#requestFeature
-     */
-    public boolean supportRequestWindowFeature(int featureId) {
-        return getDelegate().requestWindowFeature(featureId);
-    }
-
-    @Override
-    public void supportInvalidateOptionsMenu() {
-        getDelegate().invalidateOptionsMenu();
-    }
-
-    @Override
-    public void invalidateOptionsMenu() {
-        getDelegate().invalidateOptionsMenu();
-    }
-
-    /**
-     * Notifies the Activity that a support action mode has been started.
-     * Activity subclasses overriding this method should call the superclass implementation.
-     *
-     * @param mode The new action mode.
-     */
-    @Override
-    @CallSuper
-    public void onSupportActionModeStarted(@NonNull ActionMode mode) {
-    }
-
-    /**
-     * Notifies the activity that a support action mode has finished.
-     * Activity subclasses overriding this method should call the superclass implementation.
-     *
-     * @param mode The action mode that just finished.
-     */
-    @Override
-    @CallSuper
-    public void onSupportActionModeFinished(@NonNull ActionMode mode) {
-    }
-
-    /**
-     * Called when a support action mode is being started for this window. Gives the
-     * callback an opportunity to handle the action mode in its own unique and
-     * beautiful way. If this method returns null the system can choose a way
-     * to present the mode or choose not to start the mode at all.
-     *
-     * @param callback Callback to control the lifecycle of this action mode
-     * @return The ActionMode that was started, or null if the system should present it
-     */
-    @Nullable
-    @Override
-    public ActionMode onWindowStartingSupportActionMode(@NonNull ActionMode.Callback callback) {
-        return null;
-    }
-
-    /**
-     * Start an action mode.
-     *
-     * @param callback Callback that will manage lifecycle events for this context mode
-     * @return The ContextMode that was started, or null if it was canceled
-     */
-    @Nullable
-    public ActionMode startSupportActionMode(@NonNull ActionMode.Callback callback) {
-        return getDelegate().startSupportActionMode(callback);
-    }
-
-    /**
-     * @deprecated Progress bars are no longer provided in AppCompat.
-     */
-    @Deprecated
-    public void setSupportProgressBarVisibility(boolean visible) {
-    }
-
-    /**
-     * @deprecated Progress bars are no longer provided in AppCompat.
-     */
-    @Deprecated
-    public void setSupportProgressBarIndeterminateVisibility(boolean visible) {
-    }
-
-    /**
-     * @deprecated Progress bars are no longer provided in AppCompat.
-     */
-    @Deprecated
-    public void setSupportProgressBarIndeterminate(boolean indeterminate) {
-    }
-
-    /**
-     * @deprecated Progress bars are no longer provided in AppCompat.
-     */
-    @Deprecated
-    public void setSupportProgress(int progress) {
-    }
-
-    /**
-     * Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}.
-     * This method will be called on all platform versions.
-     *
-     * Define the synthetic task stack that will be generated during Up navigation from
-     * a different task.
-     *
-     * <p>The default implementation of this method adds the parent chain of this activity
-     * as specified in the manifest to the supplied {@link android.support.v4.app.TaskStackBuilder}. Applications
-     * may choose to override this method to construct the desired task stack in a different
-     * way.</p>
-     *
-     * <p>This method will be invoked by the default implementation of {@link #onNavigateUp()}
-     * if {@link #shouldUpRecreateTask(android.content.Intent)} returns true when supplied with the intent
-     * returned by {@link #getParentActivityIntent()}.</p>
-     *
-     * <p>Applications that wish to supply extra Intent parameters to the parent stack defined
-     * by the manifest should override
-     * {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.</p>
-     *
-     * @param builder An empty TaskStackBuilder - the application should add intents representing
-     *                the desired task stack
-     */
-    public void onCreateSupportNavigateUpTaskStack(@NonNull TaskStackBuilder builder) {
-        builder.addParentStack(this);
-    }
-
-    /**
-     * Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}.
-     * This method will be called on all platform versions.
-     *
-     * Prepare the synthetic task stack that will be generated during Up navigation
-     * from a different task.
-     *
-     * <p>This method receives the {@link android.support.v4.app.TaskStackBuilder} with the constructed series of
-     * Intents as generated by {@link #onCreateSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}.
-     * If any extra data should be added to these intents before launching the new task,
-     * the application should override this method and add that data here.</p>
-     *
-     * @param builder A TaskStackBuilder that has been populated with Intents by
-     *                onCreateNavigateUpTaskStack.
-     */
-    public void onPrepareSupportNavigateUpTaskStack(@NonNull TaskStackBuilder builder) {
-    }
-
-    /**
-     * This method is called whenever the user chooses to navigate Up within your application's
-     * activity hierarchy from the action bar.
-     *
-     * <p>If a parent was specified in the manifest for this activity or an activity-alias to it,
-     * default Up navigation will be handled automatically. See
-     * {@link #getSupportParentActivityIntent()} for how to specify the parent. If any activity
-     * along the parent chain requires extra Intent arguments, the Activity subclass
-     * should override the method {@link #onPrepareSupportNavigateUpTaskStack(android.support.v4.app.TaskStackBuilder)}
-     * to supply those arguments.</p>
-     *
-     * <p>See <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and
-     * Back Stack</a> from the developer guide and
-     * <a href="{@docRoot}design/patterns/navigation.html">Navigation</a> from the design guide
-     * for more information about navigating within your app.</p>
-     *
-     * <p>See the {@link android.support.v4.app.TaskStackBuilder} class and the Activity methods
-     * {@link #getSupportParentActivityIntent()}, {@link #supportShouldUpRecreateTask(android.content.Intent)}, and
-     * {@link #supportNavigateUpTo(android.content.Intent)} for help implementing custom Up navigation.</p>
-     *
-     * @return true if Up navigation completed successfully and this Activity was finished,
-     *         false otherwise.
-     */
-    public boolean onSupportNavigateUp() {
-        Intent upIntent = getSupportParentActivityIntent();
-
-        if (upIntent != null) {
-            if (supportShouldUpRecreateTask(upIntent)) {
-                TaskStackBuilder b = TaskStackBuilder.create(this);
-                onCreateSupportNavigateUpTaskStack(b);
-                onPrepareSupportNavigateUpTaskStack(b);
-                b.startActivities();
-
-                try {
-                    ActivityCompat.finishAffinity(this);
-                } catch (IllegalStateException e) {
-                    // This can only happen on 4.1+, when we don't have a parent or a result set.
-                    // In that case we should just finish().
-                    finish();
-                }
-            } else {
-                // This activity is part of the application's task, so simply
-                // navigate up to the hierarchical parent activity.
-                supportNavigateUpTo(upIntent);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Obtain an {@link android.content.Intent} that will launch an explicit target activity
-     * specified by sourceActivity's {@link android.support.v4.app.NavUtils#PARENT_ACTIVITY} &lt;meta-data&gt;
-     * element in the application's manifest. If the device is running
-     * Jellybean or newer, the android:parentActivityName attribute will be preferred
-     * if it is present.
-     *
-     * @return a new Intent targeting the defined parent activity of sourceActivity
-     */
-    @Nullable
-    @Override
-    public Intent getSupportParentActivityIntent() {
-        return NavUtils.getParentActivityIntent(this);
-    }
-
-    /**
-     * Returns true if sourceActivity should recreate the task when navigating 'up'
-     * by using targetIntent.
-     *
-     * <p>If this method returns false the app can trivially call
-     * {@link #supportNavigateUpTo(android.content.Intent)} using the same parameters to correctly perform
-     * up navigation. If this method returns false, the app should synthesize a new task stack
-     * by using {@link android.support.v4.app.TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
-     *
-     * @param targetIntent An intent representing the target destination for up navigation
-     * @return true if navigating up should recreate a new task stack, false if the same task
-     *         should be used for the destination
-     */
-    public boolean supportShouldUpRecreateTask(@NonNull Intent targetIntent) {
-        return NavUtils.shouldUpRecreateTask(this, targetIntent);
-    }
-
-    /**
-     * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
-     * in the process. upIntent will have the flag {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} set
-     * by this method, along with any others required for proper up navigation as outlined
-     * in the Android Design Guide.
-     *
-     * <p>This method should be used when performing up navigation from within the same task
-     * as the destination. If up navigation should cross tasks in some cases, see
-     * {@link #supportShouldUpRecreateTask(android.content.Intent)}.</p>
-     *
-     * @param upIntent An intent representing the target destination for up navigation
-     */
-    public void supportNavigateUpTo(@NonNull Intent upIntent) {
-        NavUtils.navigateUpTo(this, upIntent);
-    }
-
-    @Override
-    public void onContentChanged() {
-        // Call onSupportContentChanged() for legacy reasons
-        onSupportContentChanged();
-    }
-
-    /**
-     * @deprecated Use {@link #onContentChanged()} instead.
-     */
-    @Deprecated
-    public void onSupportContentChanged() {
-    }
-
-    @Nullable
-    @Override
-    public ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
-        return getDelegate().getDrawerToggleDelegate();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>Please note: AppCompat uses its own feature id for the action bar:
-     * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
-     */
-    @Override
-    public boolean onMenuOpened(int featureId, Menu menu) {
-        return super.onMenuOpened(featureId, menu);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>Please note: AppCompat uses its own feature id for the action bar:
-     * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
-     */
-    @Override
-    public void onPanelClosed(int featureId, Menu menu) {
-        super.onPanelClosed(featureId, menu);
-    }
-
-    @Override
-    protected void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        getDelegate().onSaveInstanceState(outState);
-    }
-
-    /**
-     * @return The {@link AppCompatDelegate} being used by this Activity.
-     */
-    @NonNull
-    public AppCompatDelegate getDelegate() {
-        if (mDelegate == null) {
-            mDelegate = AppCompatDelegate.create(this, this);
-        }
-        return mDelegate;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        // 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);
-    }
-
-    @Override
-    public Resources getResources() {
-        if (mResources == null && VectorEnabledTintResources.shouldBeUsed()) {
-            mResources = new VectorEnabledTintResources(this, super.getResources());
-        }
-        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/android/support/v7/app/AppCompatCallback.java b/android/support/v7/app/AppCompatCallback.java
deleted file mode 100644
index de7a416..0000000
--- a/android/support/v7/app/AppCompatCallback.java
+++ /dev/null
@@ -1,56 +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.app;
-
-import android.support.annotation.Nullable;
-import android.support.v7.view.ActionMode;
-
-/**
- * Implemented this in order for AppCompat to be able to callback in certain situations.
- * <p>
- * This should be provided to
- * {@link AppCompatDelegate#create(android.app.Activity, AppCompatCallback)}.
- */
-public interface AppCompatCallback {
-
-    /**
-     * Called when a support action mode has been started.
-     *
-     * @param mode The new action mode.
-     */
-    void onSupportActionModeStarted(ActionMode mode);
-
-    /**
-     * Called when a support action mode has finished.
-     *
-     * @param mode The action mode that just finished.
-     */
-    void onSupportActionModeFinished(ActionMode mode);
-
-    /**
-     * Called when a support action mode is being started for this window. Gives the
-     * callback an opportunity to handle the action mode in its own unique and
-     * beautiful way. If this method returns null the system can choose a way
-     * to present the mode or choose not to start the mode at all.
-     *
-     * @param callback Callback to control the lifecycle of this action mode
-     * @return The ActionMode that was started, or null if the system should present it
-     */
-    @Nullable
-    ActionMode onWindowStartingSupportActionMode(ActionMode.Callback callback);
-
-}
diff --git a/android/support/v7/app/AppCompatDelegate.java b/android/support/v7/app/AppCompatDelegate.java
deleted file mode 100644
index 5d0bf6b..0000000
--- a/android/support/v7/app/AppCompatDelegate.java
+++ /dev/null
@@ -1,540 +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.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.IdRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.view.WindowCompat;
-import android.support.v7.view.ActionMode;
-import android.support.v7.widget.Toolbar;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This class represents a delegate which you can use to extend AppCompat's support to any
- * {@link android.app.Activity}.
- *
- * <p>When using an {@link AppCompatDelegate}, you should call the following methods instead of the
- * {@link android.app.Activity} method of the same name:</p>
- * <ul>
- *     <li>{@link #addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li>
- *     <li>{@link #setContentView(int)}</li>
- *     <li>{@link #setContentView(android.view.View)}</li>
- *     <li>{@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}</li>
- *     <li>{@link #requestWindowFeature(int)}</li>
- *     <li>{@link #hasWindowFeature(int)}</li>
- *     <li>{@link #invalidateOptionsMenu()}</li>
- *     <li>{@link #startSupportActionMode(android.support.v7.view.ActionMode.Callback)}</li>
- *     <li>{@link #setSupportActionBar(android.support.v7.widget.Toolbar)}</li>
- *     <li>{@link #getSupportActionBar()}</li>
- *     <li>{@link #getMenuInflater()}</li>
- *     <li>{@link #findViewById(int)}</li>
- * </ul>
- *
- * <p>The following methods should be called from the {@link android.app.Activity} method of the
- * same name:</p>
- * <ul>
- *     <li>{@link #onCreate(android.os.Bundle)}</li>
- *     <li>{@link #onPostCreate(android.os.Bundle)}</li>
- *     <li>{@link #onConfigurationChanged(android.content.res.Configuration)}</li>
- *     <li>{@link #onStart()}</li>
- *     <li>{@link #onStop()}</li>
- *     <li>{@link #onPostResume()}</li>
- *     <li>{@link #onSaveInstanceState(Bundle)}</li>
- *     <li>{@link #setTitle(CharSequence)}</li>
- *     <li>{@link #onStop()}</li>
- *     <li>{@link #onDestroy()}</li>
- * </ul>
- *
- * <p>An {@link Activity} can only be linked with one {@link AppCompatDelegate} instance,
- * therefore the instance returned from {@link #create(Activity, AppCompatCallback)} should be
- * retained until the Activity is destroyed.</p>
- */
-public abstract class AppCompatDelegate {
-
-    static final String TAG = "AppCompatDelegate";
-
-    /**
-     * Mode which means to not use night mode, and therefore prefer {@code notnight} qualified
-     * resources where available, regardless of the time.
-     *
-     * @see #setLocalNightMode(int)
-     */
-    public static final int MODE_NIGHT_NO = 1;
-
-    /**
-     * Mode which means to always use night mode, and therefore prefer {@code night} qualified
-     * resources where available, regardless of the time.
-     *
-     * @see #setLocalNightMode(int)
-     */
-    public static final int MODE_NIGHT_YES = 2;
-
-    /**
-     * Mode which means to use night mode when it is determined that it is night or not.
-     *
-     * <p>The calculation used to determine whether it is night or not makes use of the location
-     * APIs (if this app has the necessary permissions). This allows us to generate accurate
-     * sunrise and sunset times. If this app does not have permission to access the location APIs
-     * then we use hardcoded times which will be less accurate.</p>
-     *
-     * @see #setLocalNightMode(int)
-     */
-    public static final int MODE_NIGHT_AUTO = 0;
-
-    /**
-     * Mode which uses the system's night mode setting to determine if it is night or not.
-     *
-     * @see #setLocalNightMode(int)
-     */
-    public static final int MODE_NIGHT_FOLLOW_SYSTEM = -1;
-
-    static final int MODE_NIGHT_UNSPECIFIED = -100;
-
-    @NightMode
-    private static int sDefaultNightMode = MODE_NIGHT_FOLLOW_SYSTEM;
-
-    private static boolean sCompatVectorFromResourcesEnabled = false;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_AUTO, MODE_NIGHT_FOLLOW_SYSTEM,
-            MODE_NIGHT_UNSPECIFIED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface NightMode {}
-
-    @IntDef({MODE_NIGHT_NO, MODE_NIGHT_YES, MODE_NIGHT_FOLLOW_SYSTEM})
-    @Retention(RetentionPolicy.SOURCE)
-    @interface ApplyableNightMode {}
-
-    /**
-     * Flag for enabling the support Action Bar.
-     *
-     * <p>This is enabled by default for some devices. The Action Bar replaces the title bar and
-     * provides an alternate location for an on-screen menu button on some devices.
-     */
-    public static final int FEATURE_SUPPORT_ACTION_BAR = 100 + WindowCompat.FEATURE_ACTION_BAR;
-
-    /**
-     * Flag for requesting an support Action Bar that overlays window content.
-     * Normally an Action Bar will sit in the space above window content, but if this
-     * feature is requested along with {@link #FEATURE_SUPPORT_ACTION_BAR} it will be layered over
-     * the window content itself. This is useful if you would like your app to have more control
-     * over how the Action Bar is displayed, such as letting application content scroll beneath
-     * an Action Bar with a transparent background or otherwise displaying a transparent/translucent
-     * Action Bar over application content.
-     *
-     * <p>This mode is especially useful with {@code View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows
-     * you to seamlessly hide the action bar in conjunction with other screen decorations.
-     * When an ActionBar is in this mode it will adjust the insets provided to
-     * {@link View#fitSystemWindows(android.graphics.Rect) View.fitSystemWindows(Rect)}
-     * to include the content covered by the action bar, so you can do layout within
-     * that space.
-     */
-    public static final int FEATURE_SUPPORT_ACTION_BAR_OVERLAY =
-            100 + WindowCompat.FEATURE_ACTION_BAR_OVERLAY;
-
-    /**
-     * Flag for specifying the behavior of action modes when an Action Bar is not present.
-     * If overlay is enabled, the action mode UI will be allowed to cover existing window content.
-     */
-    public static final int FEATURE_ACTION_MODE_OVERLAY = WindowCompat.FEATURE_ACTION_MODE_OVERLAY;
-
-    /**
-     * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code activity}.
-     *
-     * @param callback An optional callback for AppCompat specific events
-     */
-    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
-        return create(activity, activity.getWindow(), callback);
-    }
-
-    /**
-     * Create a {@link android.support.v7.app.AppCompatDelegate} to use with {@code dialog}.
-     *
-     * @param callback An optional callback for AppCompat specific events
-     */
-    public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
-        return create(dialog.getContext(), dialog.getWindow(), callback);
-    }
-
-    private static AppCompatDelegate create(Context context, Window window,
-            AppCompatCallback callback) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new AppCompatDelegateImplN(context, window, callback);
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return new AppCompatDelegateImplV23(context, window, callback);
-        } else if (Build.VERSION.SDK_INT >= 14) {
-            return new AppCompatDelegateImplV14(context, window, callback);
-        } else if (Build.VERSION.SDK_INT >= 11) {
-            return new AppCompatDelegateImplV11(context, window, callback);
-        } else {
-            return new AppCompatDelegateImplV9(context, window, callback);
-        }
-    }
-
-    /**
-     * Private constructor
-     */
-    AppCompatDelegate() {}
-
-    /**
-     * Support library version of {@link Activity#getActionBar}.
-     *
-     * @return AppCompat's action bar, or null if it does not have one.
-     */
-    @Nullable
-    public abstract ActionBar getSupportActionBar();
-
-    /**
-     * Set a {@link Toolbar} to act as the {@link ActionBar} for this delegate.
-     *
-     * <p>When set to a non-null value the {@link #getSupportActionBar()} ()} method will return
-     * an {@link ActionBar} object that can be used to control the given toolbar as if it were
-     * a traditional window decor action bar. The toolbar's menu will be populated with the
-     * Activity's options menu and the navigation button will be wired through the standard
-     * {@link android.R.id#home home} menu select action.</p>
-     *
-     * <p>In order to use a Toolbar within the Activity's window content the application
-     * must not request the window feature
-     * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
-     *
-     * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it
-     */
-    public abstract void setSupportActionBar(@Nullable Toolbar toolbar);
-
-    /**
-     * Return the value of this call from your {@link Activity#getMenuInflater()}
-     */
-    public abstract MenuInflater getMenuInflater();
-
-    /**
-     * Should be called from {@link Activity#onCreate Activity.onCreate()}.
-     *
-     * <p>This should be called before {@code super.onCreate()} as so:</p>
-     * <pre class="prettyprint">
-     * protected void onCreate(Bundle savedInstanceState) {
-     *     getDelegate().onCreate(savedInstanceState);
-     *     super.onCreate(savedInstanceState);
-     *     // ...
-     * }
-     * </pre>
-     */
-    public abstract void onCreate(Bundle savedInstanceState);
-
-    /**
-     * Should be called from {@link Activity#onPostCreate(android.os.Bundle)}
-     */
-    public abstract void onPostCreate(Bundle savedInstanceState);
-
-    /**
-     * Should be called from
-     * {@link Activity#onConfigurationChanged}
-     */
-    public abstract void onConfigurationChanged(Configuration newConfig);
-
-    /**
-     * Should be called from {@link Activity#onStart()} Activity.onStart()}
-     */
-    public abstract void onStart();
-
-    /**
-     * Should be called from {@link Activity#onStop Activity.onStop()}
-     */
-    public abstract void onStop();
-
-    /**
-     * Should be called from {@link Activity#onPostResume()}
-     */
-    public abstract void onPostResume();
-
-    /**
-     * Finds a view that was identified by the id attribute from the XML that
-     * was processed in {@link #onCreate}.
-     *
-     * @return The view if found or null otherwise.
-     */
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    @Nullable
-    public abstract <T extends View> T findViewById(@IdRes int id);
-
-    /**
-     * Should be called instead of {@link Activity#setContentView(android.view.View)}}
-     */
-    public abstract void setContentView(View v);
-
-    /**
-     * Should be called instead of {@link Activity#setContentView(int)}}
-     */
-    public abstract void setContentView(@LayoutRes int resId);
-
-    /**
-     * Should be called instead of
-     * {@link Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}}
-     */
-    public abstract void setContentView(View v, ViewGroup.LayoutParams lp);
-
-    /**
-     * Should be called instead of
-     * {@link Activity#addContentView(android.view.View, android.view.ViewGroup.LayoutParams)}}
-     */
-    public abstract void addContentView(View v, ViewGroup.LayoutParams lp);
-
-    /**
-     * Should be called from {@link Activity#onTitleChanged(CharSequence, int)}}
-     */
-    public abstract void setTitle(@Nullable CharSequence title);
-
-    /**
-     * Should be called from {@link Activity#invalidateOptionsMenu()}} or
-     * {@link FragmentActivity#supportInvalidateOptionsMenu()}.
-     */
-    public abstract void invalidateOptionsMenu();
-
-    /**
-     * Should be called from {@link Activity#onDestroy()}
-     */
-    public abstract void onDestroy();
-
-    /**
-     * Returns an {@link ActionBarDrawerToggle.Delegate} which can be returned from your Activity
-     * if it implements {@link ActionBarDrawerToggle.DelegateProvider}.
-     */
-    @Nullable
-    public abstract ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
-
-    /**
-     * Enable extended window features.  This should be called instead of
-     * {@link android.app.Activity#requestWindowFeature(int)} or
-     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
-     *
-     * @param featureId The desired feature as defined in {@link android.view.Window}.
-     * @return Returns true if the requested feature is supported and now
-     *         enabled.
-     */
-    public abstract boolean requestWindowFeature(int featureId);
-
-    /**
-     * Query for the availability of a certain feature.
-     *
-     * <p>This should be called instead of {@link android.view.Window#hasFeature(int)}.</p>
-     *
-     * @param featureId The feature ID to check
-     * @return true if the feature is enabled, false otherwise.
-     */
-    public abstract boolean hasWindowFeature(int featureId);
-
-    /**
-     * Start an action mode.
-     *
-     * @param callback Callback that will manage lifecycle events for this context mode
-     * @return The ContextMode that was started, or null if it was canceled
-     */
-    @Nullable
-    public abstract ActionMode startSupportActionMode(@NonNull ActionMode.Callback callback);
-
-    /**
-     * Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace
-     * the framework widgets with compatible tinted versions. This should be called before
-     * {@code super.onCreate()} as so:
-     * <pre class="prettyprint">
-     * protected void onCreate(Bundle savedInstanceState) {
-     *     getDelegate().installViewFactory();
-     *     getDelegate().onCreate(savedInstanceState);
-     *     super.onCreate(savedInstanceState);
-     *
-     *     // ...
-     * }
-     * </pre>
-     * If you are using your own {@link android.view.LayoutInflater.Factory Factory} or
-     * {@link android.view.LayoutInflater.Factory2 Factory2} then you can omit this call, and instead call
-     * {@link #createView(android.view.View, String, android.content.Context, android.util.AttributeSet)}
-     * from your factory to return any compatible widgets.
-     */
-    public abstract void installViewFactory();
-
-    /**
-     * This should be called from a
-     * {@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
-     * {@link android.view.LayoutInflater LayoutInflater} factory, and have therefore not
-     * installed the default factory via {@link #installViewFactory()}.
-     */
-    public abstract View createView(@Nullable View parent, String name, @NonNull Context context,
-            @NonNull AttributeSet attrs);
-
-    /**
-     * Whether AppCompat handles any native action modes itself.
-     * <p>This methods only takes effect on
-     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and above.
-     *
-     * @param enabled whether AppCompat should handle native action modes.
-     */
-    public abstract void setHandleNativeActionModesEnabled(boolean enabled);
-
-    /**
-     * Returns whether AppCompat handles any native action modes itself.
-     *
-     * @return true if AppCompat should handle native action modes.
-     */
-    public abstract boolean isHandleNativeActionModesEnabled();
-
-    /**
-     * Allows AppCompat to save instance state.
-     */
-    public abstract void onSaveInstanceState(Bundle outState);
-
-    /**
-     * Allow AppCompat to apply the {@code night} and {@code notnight} resource qualifiers.
-     *
-     * <p>Doing this enables the
-     * {@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>
-     *
-     * <p>You can override the night mode using {@link #setLocalNightMode(int)}.</p>
-     *
-     * <p>This only works on devices running
-     * {@link Build.VERSION_CODES#ICE_CREAM_SANDWICH ICE_CREAM_SANDWICH} and above.</p>
-     *
-     * <p>If this is called after the host component has been created, the component will either be
-     * automatically recreated or its {@link Configuration} updated. Which one depends on how
-     * the component is setup (via {@code android:configChanges} or similar).</p>
-     *
-     * @see #setDefaultNightMode(int)
-     * @see #setLocalNightMode(int)
-     *
-     * @return true if the night mode was applied, false if not
-     */
-    public abstract boolean applyDayNight();
-
-    /**
-     * Override the night mode used for this delegate's host component. This method only takes
-     * effect for those situations where {@link #applyDayNight()} works.
-     *
-     * <p>As this will call {@link #applyDayNight()}, the host component might be
-     * recreated automatically.</p>
-     */
-    public abstract void setLocalNightMode(@NightMode int mode);
-
-    /**
-     * Sets the default night mode. This is used across all activities/dialogs but can be overridden
-     * locally via {@link #setLocalNightMode(int)}.
-     *
-     * <p>This method only takes effect for those situations where {@link #applyDayNight()} works.
-     * Defaults to {@link #MODE_NIGHT_NO}.</p>
-     *
-     * <p>This only takes effect for components which are created after the call. Any components
-     * which are already open will not be updated.</p>
-     *
-     * @see #setLocalNightMode(int)
-     * @see #getDefaultNightMode()
-     */
-    public static void setDefaultNightMode(@NightMode int mode) {
-        switch (mode) {
-            case MODE_NIGHT_AUTO:
-            case MODE_NIGHT_NO:
-            case MODE_NIGHT_YES:
-            case MODE_NIGHT_FOLLOW_SYSTEM:
-                sDefaultNightMode = mode;
-                break;
-            default:
-                Log.d(TAG, "setDefaultNightMode() called with an unknown mode");
-                break;
-        }
-    }
-
-    /**
-     * Returns the default night mode.
-     *
-     * @see #setDefaultNightMode(int)
-     */
-    @NightMode
-    public static int getDefaultNightMode() {
-        return sDefaultNightMode;
-    }
-
-    /**
-     * Sets whether vector drawables on older platforms (< API 21) can be used within
-     * {@link android.graphics.drawable.DrawableContainer} resources.
-     *
-     * <p>When enabled, AppCompat can intercept some drawable inflation from the framework, which
-     * enables implicit inflation of vector drawables within
-     * {@link android.graphics.drawable.DrawableContainer} resources. You can then use those
-     * drawables in places such as {@code android:src} on {@link android.widget.ImageView},
-     * or {@code android:drawableLeft} on {@link android.widget.TextView}. Example usage:</p>
-     *
-     * <pre>
-     * &lt;selector xmlns:android=&quot;...&quot;&gt;
-     *     &lt;item android:state_checked=&quot;true&quot;
-     *           android:drawable=&quot;@drawable/vector_checked_icon&quot; /&gt;
-     *     &lt;item android:drawable=&quot;@drawable/vector_icon&quot; /&gt;
-     * &lt;/selector&gt;
-     *
-     * &lt;TextView
-     *         ...
-     *         android:drawableLeft=&quot;@drawable/vector_state_list_icon&quot; /&gt;
-     * </pre>
-     *
-     * <p>This feature defaults to disabled, since enabling it can cause issues with memory usage,
-     * and problems updating {@link Configuration} instances. If you update the configuration
-     * manually, then you probably do not want to enable this. You have been warned.</p>
-     *
-     * <p>Even with this disabled, you can still use vector resources through
-     * {@link android.support.v7.widget.AppCompatImageView#setImageResource(int)} and its
-     * {@code app:srcCompat} attribute. They can also be used in anything which AppCompat inflates
-     * for you, such as menu resources.</p>
-     *
-     * <p>Please note: this only takes effect in Activities created after this call.</p>
-     */
-    public static void setCompatVectorFromResourcesEnabled(boolean enabled) {
-        sCompatVectorFromResourcesEnabled = enabled;
-    }
-
-    /**
-     * Returns whether vector drawables on older platforms (< API 21) can be accessed from within
-     * resources.
-     *
-     * @see #setCompatVectorFromResourcesEnabled(boolean)
-     */
-    public static boolean isCompatVectorFromResourcesEnabled() {
-        return sCompatVectorFromResourcesEnabled;
-    }
-}
diff --git a/android/support/v7/app/AppCompatDelegateImplBase.java b/android/support/v7/app/AppCompatDelegateImplBase.java
deleted file mode 100644
index 5bb5ca6..0000000
--- a/android/support/v7/app/AppCompatDelegateImplBase.java
+++ /dev/null
@@ -1,381 +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.v7.app;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.RequiresApi;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ActionMode;
-import android.support.v7.view.SupportMenuInflater;
-import android.support.v7.view.WindowCallbackWrapper;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.widget.TintTypedArray;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.Window;
-
-@RequiresApi(14)
-abstract class AppCompatDelegateImplBase extends AppCompatDelegate {
-
-    static final boolean DEBUG = false;
-
-    private static boolean sInstalledExceptionHandler;
-    private static final boolean SHOULD_INSTALL_EXCEPTION_HANDLER = Build.VERSION.SDK_INT < 21;
-
-    static final String EXCEPTION_HANDLER_MESSAGE_SUFFIX= ". If the resource you are"
-            + " trying to use is a vector resource, you may be referencing it in an unsupported"
-            + " way. See AppCompatDelegate.setCompatVectorFromResourcesEnabled() for more info.";
-
-    static {
-        if (SHOULD_INSTALL_EXCEPTION_HANDLER && !sInstalledExceptionHandler) {
-            final Thread.UncaughtExceptionHandler defHandler
-                    = Thread.getDefaultUncaughtExceptionHandler();
-
-            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-                @Override
-                public void uncaughtException(Thread thread, final Throwable thowable) {
-                    if (shouldWrapException(thowable)) {
-                        // Now wrap the throwable, but append some extra information to the message
-                        final Throwable wrapped = new Resources.NotFoundException(
-                                thowable.getMessage() + EXCEPTION_HANDLER_MESSAGE_SUFFIX);
-                        wrapped.initCause(thowable.getCause());
-                        wrapped.setStackTrace(thowable.getStackTrace());
-                        defHandler.uncaughtException(thread, wrapped);
-                    } else {
-                        defHandler.uncaughtException(thread, thowable);
-                    }
-                }
-
-                private boolean shouldWrapException(Throwable throwable) {
-                    if (throwable instanceof Resources.NotFoundException) {
-                        final String message = throwable.getMessage();
-                        return message != null && (message.contains("drawable")
-                                || message.contains("Drawable"));
-                    }
-                    return false;
-                }
-            });
-
-            sInstalledExceptionHandler = true;
-        }
-    }
-
-    private static final int[] sWindowBackgroundStyleable = {android.R.attr.windowBackground};
-
-    final Context mContext;
-    final Window mWindow;
-    final Window.Callback mOriginalWindowCallback;
-    final Window.Callback mAppCompatWindowCallback;
-    final AppCompatCallback mAppCompatCallback;
-
-    ActionBar mActionBar;
-    MenuInflater mMenuInflater;
-
-    // true if this activity has an action bar.
-    boolean mHasActionBar;
-    // true if this activity's action bar overlays other activity content.
-    boolean mOverlayActionBar;
-    // true if this any action modes should overlay the activity content
-    boolean mOverlayActionMode;
-    // true if this activity is floating (e.g. Dialog)
-    boolean mIsFloating;
-    // true if this activity has no title
-    boolean mWindowNoTitle;
-
-    private CharSequence mTitle;
-
-    private boolean mIsStarted;
-    private boolean mIsDestroyed;
-    private boolean mEatKeyUpEvent;
-
-    AppCompatDelegateImplBase(Context context, Window window, AppCompatCallback callback) {
-        mContext = context;
-        mWindow = window;
-        mAppCompatCallback = callback;
-
-        mOriginalWindowCallback = mWindow.getCallback();
-        if (mOriginalWindowCallback instanceof AppCompatWindowCallbackBase) {
-            throw new IllegalStateException(
-                    "AppCompat has already installed itself into the Window");
-        }
-        mAppCompatWindowCallback = wrapWindowCallback(mOriginalWindowCallback);
-        // Now install the new callback
-        mWindow.setCallback(mAppCompatWindowCallback);
-
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
-                context, null, sWindowBackgroundStyleable);
-        final Drawable winBg = a.getDrawableIfKnown(0);
-        if (winBg != null) {
-            mWindow.setBackgroundDrawable(winBg);
-        }
-        a.recycle();
-    }
-
-    abstract void initWindowDecorActionBar();
-
-    Window.Callback wrapWindowCallback(Window.Callback callback) {
-        return new AppCompatWindowCallbackBase(callback);
-    }
-
-    @Override
-    public ActionBar getSupportActionBar() {
-        // The Action Bar should be lazily created as hasActionBar
-        // could change after onCreate
-        initWindowDecorActionBar();
-        return mActionBar;
-    }
-
-    final ActionBar peekSupportActionBar() {
-        return mActionBar;
-    }
-
-    @Override
-    public MenuInflater getMenuInflater() {
-        // Make sure that action views can get an appropriate theme.
-        if (mMenuInflater == null) {
-            initWindowDecorActionBar();
-            mMenuInflater = new SupportMenuInflater(
-                    mActionBar != null ? mActionBar.getThemedContext() : mContext);
-        }
-        return mMenuInflater;
-    }
-
-    // Methods used to create and respond to options menu
-    abstract void onPanelClosed(int featureId, Menu menu);
-
-    abstract boolean onMenuOpened(int featureId, Menu menu);
-
-    abstract boolean dispatchKeyEvent(KeyEvent event);
-
-    abstract boolean onKeyShortcut(int keyCode, KeyEvent event);
-
-    @Override
-    public void setLocalNightMode(@NightMode int mode) {
-        // no-op
-    }
-
-    @Override
-    public final ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
-        return new ActionBarDrawableToggleImpl();
-    }
-
-    final Context getActionBarThemedContext() {
-        Context context = null;
-
-        // If we have an action bar, let it return a themed context
-        ActionBar ab = getSupportActionBar();
-        if (ab != null) {
-            context = ab.getThemedContext();
-        }
-
-        if (context == null) {
-            context = mContext;
-        }
-        return context;
-    }
-
-    private class ActionBarDrawableToggleImpl implements ActionBarDrawerToggle.Delegate {
-        ActionBarDrawableToggleImpl() {
-        }
-
-        @Override
-        public Drawable getThemeUpIndicator() {
-            final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
-                    getActionBarThemedContext(), null, new int[]{ R.attr.homeAsUpIndicator });
-            final Drawable result = a.getDrawable(0);
-            a.recycle();
-            return result;
-        }
-
-        @Override
-        public Context getActionBarThemedContext() {
-            return AppCompatDelegateImplBase.this.getActionBarThemedContext();
-        }
-
-        @Override
-        public boolean isNavigationVisible() {
-            final ActionBar ab = getSupportActionBar();
-            return ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0;
-        }
-
-        @Override
-        public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.setHomeAsUpIndicator(upDrawable);
-                ab.setHomeActionContentDescription(contentDescRes);
-            }
-        }
-
-        @Override
-        public void setActionBarDescription(int contentDescRes) {
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.setHomeActionContentDescription(contentDescRes);
-            }
-        }
-    }
-
-    abstract ActionMode startSupportActionModeFromWindow(ActionMode.Callback callback);
-
-    @Override
-    public void onStart() {
-        mIsStarted = true;
-    }
-
-    @Override
-    public void onStop() {
-        mIsStarted = false;
-    }
-
-    @Override
-    public void onDestroy() {
-        mIsDestroyed = true;
-    }
-
-    @Override
-    public void setHandleNativeActionModesEnabled(boolean enabled) {
-        // no-op pre-v14
-    }
-
-    @Override
-    public boolean isHandleNativeActionModesEnabled() {
-        // Always false pre-v14
-        return false;
-    }
-
-    @Override
-    public boolean applyDayNight() {
-        // no-op on v7
-        return false;
-    }
-
-    final boolean isDestroyed() {
-        return mIsDestroyed;
-    }
-
-    final boolean isStarted() {
-        return mIsStarted;
-    }
-
-    final Window.Callback getWindowCallback() {
-        return mWindow.getCallback();
-    }
-
-    @Override
-    public final void setTitle(CharSequence title) {
-        mTitle = title;
-        onTitleChanged(title);
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        // no-op
-    }
-
-    abstract void onTitleChanged(CharSequence title);
-
-    final CharSequence getTitle() {
-        // If the original window callback is an Activity, we'll use its title
-        if (mOriginalWindowCallback instanceof Activity) {
-            return ((Activity) mOriginalWindowCallback).getTitle();
-        }
-        // Else, we'll return the title we have recorded ourselves
-        return mTitle;
-    }
-
-    class AppCompatWindowCallbackBase extends WindowCallbackWrapper {
-        AppCompatWindowCallbackBase(Window.Callback callback) {
-            super(callback);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            return AppCompatDelegateImplBase.this.dispatchKeyEvent(event)
-                    || super.dispatchKeyEvent(event);
-        }
-
-        @Override
-        public boolean dispatchKeyShortcutEvent(KeyEvent event) {
-            return super.dispatchKeyShortcutEvent(event)
-                    || AppCompatDelegateImplBase.this.onKeyShortcut(event.getKeyCode(), event);
-        }
-
-        @Override
-        public boolean onCreatePanelMenu(int featureId, Menu menu) {
-            if (featureId == Window.FEATURE_OPTIONS_PANEL && !(menu instanceof MenuBuilder)) {
-                // If this is an options menu but it's not an AppCompat menu, we eat the event
-                // and return false
-                return false;
-            }
-            return super.onCreatePanelMenu(featureId, menu);
-        }
-
-        @Override
-        public void onContentChanged() {
-            // We purposely do not propagate this call as this is called when we install
-            // our sub-decor rather than the user's content
-        }
-
-        @Override
-        public boolean onPreparePanel(int featureId, View view, Menu menu) {
-            final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
-
-            if (featureId == Window.FEATURE_OPTIONS_PANEL && mb == null) {
-                // If this is an options menu but it's not an AppCompat menu, we eat the event
-                // and return false
-                return false;
-            }
-
-            // On ICS and below devices, onPreparePanel calls menu.hasVisibleItems() to determine
-            // if a panel is prepared. This interferes with any initially invisible items, which
-            // are later made visible. We workaround it by making hasVisibleItems() always
-            // return true during the onPreparePanel call.
-            if (mb != null) {
-                mb.setOverrideVisibleItems(true);
-            }
-
-            final boolean handled = super.onPreparePanel(featureId, view, menu);
-
-            if (mb != null) {
-                mb.setOverrideVisibleItems(false);
-            }
-
-            return handled;
-        }
-
-        @Override
-        public boolean onMenuOpened(int featureId, Menu menu) {
-            super.onMenuOpened(featureId, menu);
-            AppCompatDelegateImplBase.this.onMenuOpened(featureId, menu);
-            return true;
-        }
-
-        @Override
-        public void onPanelClosed(int featureId, Menu menu) {
-            super.onPanelClosed(featureId, menu);
-            AppCompatDelegateImplBase.this.onPanelClosed(featureId, menu);
-        }
-    }
-}
diff --git a/android/support/v7/app/AppCompatDelegateImplN.java b/android/support/v7/app/AppCompatDelegateImplN.java
deleted file mode 100644
index e282324..0000000
--- a/android/support/v7/app/AppCompatDelegateImplN.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.v7.app;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.view.KeyboardShortcutGroup;
-import android.view.Menu;
-import android.view.Window;
-
-import java.util.List;
-
-@RequiresApi(24)
-class AppCompatDelegateImplN extends AppCompatDelegateImplV23 {
-
-    AppCompatDelegateImplN(Context context, Window window, AppCompatCallback callback) {
-        super(context, window, callback);
-    }
-
-    @Override
-    Window.Callback wrapWindowCallback(Window.Callback callback) {
-        return new AppCompatWindowCallbackN(callback);
-    }
-
-    class AppCompatWindowCallbackN extends AppCompatWindowCallbackV23 {
-        AppCompatWindowCallbackN(Window.Callback callback) {
-            super(callback);
-        }
-
-        @Override
-        public void onProvideKeyboardShortcuts(
-                List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
-            final PanelFeatureState panel = getPanelState(Window.FEATURE_OPTIONS_PANEL, true);
-            if (panel != null && panel.menu != null) {
-                // The menu provided is one created by PhoneWindow which we don't actually use.
-                // Instead we'll pass through our own...
-                super.onProvideKeyboardShortcuts(data, panel.menu, deviceId);
-            } else {
-                // If we don't have a menu, jump pass through the original instead
-                super.onProvideKeyboardShortcuts(data, menu, deviceId);
-            }
-        }
-    }
-}
diff --git a/android/support/v7/app/AppCompatDelegateImplV11.java b/android/support/v7/app/AppCompatDelegateImplV11.java
deleted file mode 100644
index 3aa7f3f..0000000
--- a/android/support/v7/app/AppCompatDelegateImplV11.java
+++ /dev/null
@@ -1,43 +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.v7.app;
-
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.Window;
-
-@RequiresApi(14)
-class AppCompatDelegateImplV11 extends AppCompatDelegateImplV9 {
-
-    AppCompatDelegateImplV11(Context context, Window window, AppCompatCallback callback) {
-        super(context, window, callback);
-    }
-
-    @Override
-    public boolean hasWindowFeature(int featureId) {
-        return super.hasWindowFeature(featureId) || mWindow.hasFeature(featureId);
-    }
-
-    @Override
-    View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        // On Honeycomb+, Activity's private inflater factory will handle calling its
-        // onCreateView(...)
-        return null;
-    }
-}
diff --git a/android/support/v7/app/AppCompatDelegateImplV14.java b/android/support/v7/app/AppCompatDelegateImplV14.java
deleted file mode 100644
index 2a4022c..0000000
--- a/android/support/v7/app/AppCompatDelegateImplV14.java
+++ /dev/null
@@ -1,362 +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.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-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;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.view.SupportActionModeWrapper;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.ActionMode;
-import android.view.Window;
-
-@RequiresApi(14)
-class AppCompatDelegateImplV14 extends AppCompatDelegateImplV11 {
-
-    private static final String KEY_LOCAL_NIGHT_MODE = "appcompat:local_night_mode";
-
-    @NightMode
-    private int mLocalNightMode = MODE_NIGHT_UNSPECIFIED;
-    private boolean mApplyDayNightCalled;
-
-    private boolean mHandleNativeActionModes = true; // defaults to true
-
-    private AutoNightModeManager mAutoNightModeManager;
-
-    AppCompatDelegateImplV14(Context context, Window window, AppCompatCallback callback) {
-        super(context, window, callback);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState != null && mLocalNightMode == MODE_NIGHT_UNSPECIFIED) {
-            // If we have a icicle and we haven't had a local night mode set yet, try and read
-            // it from the icicle
-            mLocalNightMode = savedInstanceState.getInt(KEY_LOCAL_NIGHT_MODE,
-                    MODE_NIGHT_UNSPECIFIED);
-        }
-    }
-
-    @Override
-    Window.Callback wrapWindowCallback(Window.Callback callback) {
-        // Override the window callback so that we can intercept onWindowStartingActionMode()
-        // calls
-        return new AppCompatWindowCallbackV14(callback);
-    }
-
-    @Override
-    public void setHandleNativeActionModesEnabled(boolean enabled) {
-        mHandleNativeActionModes = enabled;
-    }
-
-    @Override
-    public boolean isHandleNativeActionModesEnabled() {
-        return mHandleNativeActionModes;
-    }
-
-    @Override
-    public boolean applyDayNight() {
-        boolean applied = false;
-
-        @NightMode final int nightMode = getNightMode();
-        @ApplyableNightMode final int modeToApply = mapNightMode(nightMode);
-        if (modeToApply != MODE_NIGHT_FOLLOW_SYSTEM) {
-            applied = updateForNightMode(modeToApply);
-        }
-
-        if (nightMode == MODE_NIGHT_AUTO) {
-            // If we're already been started, we may need to setup auto mode again
-            ensureAutoNightModeManager();
-            mAutoNightModeManager.setup();
-        }
-
-        mApplyDayNightCalled = true;
-        return applied;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        // This will apply day/night if the time has changed, it will also call through to
-        // setupAutoNightModeIfNeeded()
-        applyDayNight();
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-
-        // Make sure we clean up any receivers setup for AUTO mode
-        if (mAutoNightModeManager != null) {
-            mAutoNightModeManager.cleanup();
-        }
-    }
-
-    @Override
-    public void setLocalNightMode(@NightMode final int mode) {
-        switch (mode) {
-            case MODE_NIGHT_AUTO:
-            case MODE_NIGHT_NO:
-            case MODE_NIGHT_YES:
-            case MODE_NIGHT_FOLLOW_SYSTEM:
-                if (mLocalNightMode != mode) {
-                    mLocalNightMode = mode;
-                    if (mApplyDayNightCalled) {
-                        // If we've already applied day night, re-apply since we won't be
-                        // called again
-                        applyDayNight();
-                    }
-                }
-                break;
-            default:
-                Log.i(TAG, "setLocalNightMode() called with an unknown mode");
-                break;
-        }
-    }
-
-    @ApplyableNightMode
-    int mapNightMode(@NightMode final int mode) {
-        switch (mode) {
-            case MODE_NIGHT_AUTO:
-                ensureAutoNightModeManager();
-                return mAutoNightModeManager.getApplyableNightMode();
-            case MODE_NIGHT_UNSPECIFIED:
-                // If we don't have a mode specified, just let the system handle it
-                return MODE_NIGHT_FOLLOW_SYSTEM;
-            default:
-                return mode;
-        }
-    }
-
-    @NightMode
-    private int getNightMode() {
-        return mLocalNightMode != MODE_NIGHT_UNSPECIFIED ? mLocalNightMode : getDefaultNightMode();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        if (mLocalNightMode != MODE_NIGHT_UNSPECIFIED) {
-            // If we have a local night mode set, save it
-            outState.putInt(KEY_LOCAL_NIGHT_MODE, mLocalNightMode);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-
-        // Make sure we clean up any receivers setup for AUTO mode
-        if (mAutoNightModeManager != null) {
-            mAutoNightModeManager.cleanup();
-        }
-    }
-
-    /**
-     * Updates the {@link Resources} configuration {@code uiMode} with the
-     * chosen {@code UI_MODE_NIGHT} value.
-     */
-    private boolean updateForNightMode(@ApplyableNightMode final int mode) {
-        final Resources res = mContext.getResources();
-        final Configuration conf = res.getConfiguration();
-        final int currentNightMode = conf.uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
-        final int newNightMode = (mode == MODE_NIGHT_YES)
-                ? Configuration.UI_MODE_NIGHT_YES
-                : Configuration.UI_MODE_NIGHT_NO;
-
-        if (currentNightMode != newNightMode) {
-            if (shouldRecreateOnNightModeChange()) {
-                if (DEBUG) {
-                    Log.d(TAG, "applyNightMode() | Night mode changed, recreating Activity");
-                }
-                // If we've already been created, we need to recreate the Activity for the
-                // mode to be applied
-                final Activity activity = (Activity) mContext;
-                activity.recreate();
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "applyNightMode() | Night mode changed, updating configuration");
-                }
-                final Configuration config = new Configuration(conf);
-                final DisplayMetrics metrics = res.getDisplayMetrics();
-
-                // Update the UI Mode to reflect the new night mode
-                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.
-                if (!(Build.VERSION.SDK_INT >= 26)) {
-                    ResourcesFlusher.flush(res);
-                }
-            }
-            return true;
-        } else {
-            if (DEBUG) {
-                Log.d(TAG, "applyNightMode() | Skipping. Night mode has not changed: " + mode);
-            }
-        }
-        return false;
-    }
-
-    private void ensureAutoNightModeManager() {
-        if (mAutoNightModeManager == null) {
-            mAutoNightModeManager = new AutoNightModeManager(TwilightManager.getInstance(mContext));
-        }
-    }
-
-    @VisibleForTesting
-    final AutoNightModeManager getAutoNightModeManager() {
-        ensureAutoNightModeManager();
-        return mAutoNightModeManager;
-    }
-
-    private boolean shouldRecreateOnNightModeChange() {
-        if (mApplyDayNightCalled && mContext instanceof Activity) {
-            // If we've already applyDayNight() (via setTheme), we need to check if the
-            // Activity has configChanges set to handle uiMode changes
-            final PackageManager pm = mContext.getPackageManager();
-            try {
-                final ActivityInfo info = pm.getActivityInfo(
-                        new ComponentName(mContext, mContext.getClass()), 0);
-                // We should return true (to recreate) if configChanges does not want to
-                // handle uiMode
-                return (info.configChanges & ActivityInfo.CONFIG_UI_MODE) == 0;
-            } catch (PackageManager.NameNotFoundException e) {
-                // This shouldn't happen but let's not crash because of it, we'll just log and
-                // return true (since most apps will do that anyway)
-                Log.d(TAG, "Exception while getting ActivityInfo", e);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    class AppCompatWindowCallbackV14 extends AppCompatWindowCallbackBase {
-        AppCompatWindowCallbackV14(Window.Callback callback) {
-            super(callback);
-        }
-
-        @Override
-        public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
-            // We wrap in a support action mode on v14+ if enabled
-            if (isHandleNativeActionModesEnabled()) {
-                return startAsSupportActionMode(callback);
-            }
-            // Else, let the call fall through to the wrapped callback
-            return super.onWindowStartingActionMode(callback);
-        }
-
-        /**
-         * Wrap the framework {@link ActionMode.Callback} in a support action mode and
-         * let AppCompat display it.
-         */
-        final ActionMode startAsSupportActionMode(ActionMode.Callback callback) {
-            // Wrap the callback as a v7 ActionMode.Callback
-            final SupportActionModeWrapper.CallbackWrapper callbackWrapper
-                    = new SupportActionModeWrapper.CallbackWrapper(mContext, callback);
-
-            // Try and start a support action mode using the wrapped callback
-            final android.support.v7.view.ActionMode supportActionMode
-                    = startSupportActionMode(callbackWrapper);
-
-            if (supportActionMode != null) {
-                // If we received a support action mode, wrap and return it
-                return callbackWrapper.getActionModeWrapper(supportActionMode);
-            }
-            return null;
-        }
-    }
-
-    @VisibleForTesting
-    final class AutoNightModeManager {
-        private TwilightManager mTwilightManager;
-        private boolean mIsNight;
-
-        private BroadcastReceiver mAutoTimeChangeReceiver;
-        private IntentFilter mAutoTimeChangeReceiverFilter;
-
-        AutoNightModeManager(@NonNull TwilightManager twilightManager) {
-            mTwilightManager = twilightManager;
-            mIsNight = twilightManager.isNight();
-        }
-
-        @ApplyableNightMode
-        final int getApplyableNightMode() {
-            mIsNight = mTwilightManager.isNight();
-            return mIsNight ? MODE_NIGHT_YES : MODE_NIGHT_NO;
-        }
-
-        final void dispatchTimeChanged() {
-            final boolean isNight = mTwilightManager.isNight();
-            if (isNight != mIsNight) {
-                mIsNight = isNight;
-                applyDayNight();
-            }
-        }
-
-        final void setup() {
-            cleanup();
-
-            // If we're set to AUTO, we register a receiver to be notified on time changes. The
-            // system only sends the tick out every minute, but that's enough fidelity for our use
-            // case
-            if (mAutoTimeChangeReceiver == null) {
-                mAutoTimeChangeReceiver = new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        if (DEBUG) {
-                            Log.d("AutoTimeChangeReceiver", "onReceive | Intent: " + intent);
-                        }
-                        dispatchTimeChanged();
-                    }
-                };
-            }
-            if (mAutoTimeChangeReceiverFilter == null) {
-                mAutoTimeChangeReceiverFilter = new IntentFilter();
-                mAutoTimeChangeReceiverFilter.addAction(Intent.ACTION_TIME_CHANGED);
-                mAutoTimeChangeReceiverFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
-                mAutoTimeChangeReceiverFilter.addAction(Intent.ACTION_TIME_TICK);
-            }
-            mContext.registerReceiver(mAutoTimeChangeReceiver, mAutoTimeChangeReceiverFilter);
-        }
-
-        final void cleanup() {
-            if (mAutoTimeChangeReceiver != null) {
-                mContext.unregisterReceiver(mAutoTimeChangeReceiver);
-                mAutoTimeChangeReceiver = null;
-            }
-        }
-    }
-}
diff --git a/android/support/v7/app/AppCompatDelegateImplV23.java b/android/support/v7/app/AppCompatDelegateImplV23.java
deleted file mode 100644
index 0095b55..0000000
--- a/android/support/v7/app/AppCompatDelegateImplV23.java
+++ /dev/null
@@ -1,79 +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.UiModeManager;
-import android.content.Context;
-import android.support.annotation.RequiresApi;
-import android.view.ActionMode;
-import android.view.Window;
-
-@RequiresApi(23)
-class AppCompatDelegateImplV23 extends AppCompatDelegateImplV14 {
-
-    private final UiModeManager mUiModeManager;
-
-    AppCompatDelegateImplV23(Context context, Window window, AppCompatCallback callback) {
-        super(context, window, callback);
-
-        mUiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
-    }
-
-    @Override
-    Window.Callback wrapWindowCallback(Window.Callback callback) {
-        // Override the window callback so that we can intercept onWindowStartingActionMode(type)
-        // calls
-        return new AppCompatWindowCallbackV23(callback);
-    }
-
-    @ApplyableNightMode
-    @Override
-    int mapNightMode(@NightMode final int mode) {
-        if (mode == MODE_NIGHT_AUTO
-                && mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO) {
-            // If we're set to AUTO and the system's auto night mode is already enabled,
-            // we'll just let the system handle it by returning FOLLOW_SYSTEM
-            return MODE_NIGHT_FOLLOW_SYSTEM;
-        }
-        return super.mapNightMode(mode);
-    }
-
-    class AppCompatWindowCallbackV23 extends AppCompatWindowCallbackV14 {
-        AppCompatWindowCallbackV23(Window.Callback callback) {
-            super(callback);
-        }
-
-        @Override
-        public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
-            if (isHandleNativeActionModesEnabled()) {
-                switch (type) {
-                    case ActionMode.TYPE_PRIMARY:
-                        // We only take over if the type is TYPE_PRIMARY
-                        return startAsSupportActionMode(callback);
-                }
-            }
-            // Else, let the call fall through to the wrapped callback
-            return super.onWindowStartingActionMode(callback, type);
-        }
-
-        @Override
-        public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
-            // No-op on API 23+
-            return null;
-        }
-    }
-}
diff --git a/android/support/v7/app/AppCompatDelegateImplV9.java b/android/support/v7/app/AppCompatDelegateImplV9.java
deleted file mode 100644
index 5b53401..0000000
--- a/android/support/v7/app/AppCompatDelegateImplV9.java
+++ /dev/null
@@ -1,2162 +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.v7.app;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.Window.FEATURE_OPTIONS_PANEL;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.media.AudioManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.IdRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v4.app.NavUtils;
-import android.support.v4.view.LayoutInflaterCompat;
-import android.support.v4.view.OnApplyWindowInsetsListener;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.support.v4.view.WindowCompat;
-import android.support.v4.view.WindowInsetsCompat;
-import android.support.v4.widget.PopupWindowCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.ActionMode;
-import android.support.v7.view.ContextThemeWrapper;
-import android.support.v7.view.StandaloneActionMode;
-import android.support.v7.view.menu.ListMenuPresenter;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPresenter;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.widget.ActionBarContextView;
-import android.support.v7.widget.AppCompatDrawableManager;
-import android.support.v7.widget.ContentFrameLayout;
-import android.support.v7.widget.DecorContentParent;
-import android.support.v7.widget.FitWindowsViewGroup;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.VectorEnabledTintResources;
-import android.support.v7.widget.ViewStubCompat;
-import android.support.v7.widget.ViewUtils;
-import android.text.TextUtils;
-import android.util.AndroidRuntimeException;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-import android.widget.PopupWindow;
-import android.widget.TextView;
-
-import org.xmlpull.v1.XmlPullParser;
-
-@RequiresApi(14)
-class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
-        implements MenuBuilder.Callback, LayoutInflater.Factory2 {
-
-    private static final boolean IS_PRE_LOLLIPOP = Build.VERSION.SDK_INT < 21;
-
-    private DecorContentParent mDecorContentParent;
-    private ActionMenuPresenterCallback mActionMenuPresenterCallback;
-    private PanelMenuPresenterCallback mPanelMenuPresenterCallback;
-
-    ActionMode mActionMode;
-    ActionBarContextView mActionModeView;
-    PopupWindow mActionModePopup;
-    Runnable mShowActionModePopup;
-    ViewPropertyAnimatorCompat mFadeAnim = null;
-
-    // true if we have installed a window sub-decor layout.
-    private boolean mSubDecorInstalled;
-    private ViewGroup mSubDecor;
-
-    private TextView mTitleView;
-    private View mStatusGuard;
-
-    // Used to keep track of Progress Bar Window features
-    private boolean mFeatureProgress, mFeatureIndeterminateProgress;
-
-    // Used for emulating PanelFeatureState
-    private boolean mClosingActionMenu;
-    private PanelFeatureState[] mPanels;
-    private PanelFeatureState mPreparedPanel;
-
-    private boolean mLongPressBackDown;
-
-    boolean mInvalidatePanelMenuPosted;
-    int mInvalidatePanelMenuFeatures;
-    private final Runnable mInvalidatePanelMenuRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_OPTIONS_PANEL) != 0) {
-                doInvalidatePanelMenu(FEATURE_OPTIONS_PANEL);
-            }
-            if ((mInvalidatePanelMenuFeatures & 1 << FEATURE_SUPPORT_ACTION_BAR) != 0) {
-                doInvalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
-            }
-            mInvalidatePanelMenuPosted = false;
-            mInvalidatePanelMenuFeatures = 0;
-        }
-    };
-
-    private boolean mEnableDefaultActionBarUp;
-
-    private Rect mTempRect1;
-    private Rect mTempRect2;
-
-    private AppCompatViewInflater mAppCompatViewInflater;
-
-    AppCompatDelegateImplV9(Context context, Window window, AppCompatCallback callback) {
-        super(context, window, callback);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        if (mOriginalWindowCallback instanceof Activity) {
-            if (NavUtils.getParentActivityName((Activity) mOriginalWindowCallback) != null) {
-                // Peek at the Action Bar and update it if it already exists
-                ActionBar ab = peekSupportActionBar();
-                if (ab == null) {
-                    mEnableDefaultActionBarUp = true;
-                } else {
-                    ab.setDefaultDisplayHomeAsUpEnabled(true);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onPostCreate(Bundle savedInstanceState) {
-        // Make sure that the sub decor is installed
-        ensureSubDecor();
-    }
-
-    @Override
-    public void initWindowDecorActionBar() {
-        ensureSubDecor();
-
-        if (!mHasActionBar || mActionBar != null) {
-            return;
-        }
-
-        if (mOriginalWindowCallback instanceof Activity) {
-            mActionBar = new WindowDecorActionBar((Activity) mOriginalWindowCallback,
-                    mOverlayActionBar);
-        } else if (mOriginalWindowCallback instanceof Dialog) {
-            mActionBar = new WindowDecorActionBar((Dialog) mOriginalWindowCallback);
-        }
-        if (mActionBar != null) {
-            mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
-        }
-    }
-
-    @Override
-    public void setSupportActionBar(Toolbar toolbar) {
-        if (!(mOriginalWindowCallback instanceof Activity)) {
-            // Only Activities support custom Action Bars
-            return;
-        }
-
-        final ActionBar ab = getSupportActionBar();
-        if (ab instanceof WindowDecorActionBar) {
-            throw new IllegalStateException("This Activity already has an action bar supplied " +
-                    "by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set " +
-                    "windowActionBar to false in your theme to use a Toolbar instead.");
-        }
-
-        // If we reach here then we're setting a new action bar
-        // First clear out the MenuInflater to make sure that it is valid for the new Action Bar
-        mMenuInflater = null;
-
-        // If we have an action bar currently, destroy it
-        if (ab != null) {
-            ab.onDestroy();
-        }
-
-        if (toolbar != null) {
-            final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
-                    ((Activity) mOriginalWindowCallback).getTitle(), mAppCompatWindowCallback);
-            mActionBar = tbab;
-            mWindow.setCallback(tbab.getWrappedWindowCallback());
-        } else {
-            mActionBar = null;
-            // Re-set the original window callback since we may have already set a Toolbar wrapper
-            mWindow.setCallback(mAppCompatWindowCallback);
-        }
-
-        invalidateOptionsMenu();
-    }
-
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    @Nullable
-    @Override
-    public <T extends View> T findViewById(@IdRes int id) {
-        ensureSubDecor();
-        return (T) mWindow.findViewById(id);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        // If this is called before sub-decor is installed, ActionBar will not
-        // be properly initialized.
-        if (mHasActionBar && mSubDecorInstalled) {
-            // Note: The action bar will need to access
-            // view changes from superclass.
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.onConfigurationChanged(newConfig);
-            }
-        }
-
-        // Make sure that the DrawableManager knows about the new config
-        AppCompatDrawableManager.get().onConfigurationChanged(mContext);
-
-        // Re-apply Day/Night to the new configuration
-        applyDayNight();
-    }
-
-    @Override
-    public void onStop() {
-        ActionBar ab = getSupportActionBar();
-        if (ab != null) {
-            ab.setShowHideAnimationEnabled(false);
-        }
-    }
-
-    @Override
-    public void onPostResume() {
-        ActionBar ab = getSupportActionBar();
-        if (ab != null) {
-            ab.setShowHideAnimationEnabled(true);
-        }
-    }
-
-    @Override
-    public void setContentView(View v) {
-        ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
-        contentParent.removeAllViews();
-        contentParent.addView(v);
-        mOriginalWindowCallback.onContentChanged();
-    }
-
-    @Override
-    public void setContentView(int resId) {
-        ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
-        contentParent.removeAllViews();
-        LayoutInflater.from(mContext).inflate(resId, contentParent);
-        mOriginalWindowCallback.onContentChanged();
-    }
-
-    @Override
-    public void setContentView(View v, ViewGroup.LayoutParams lp) {
-        ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
-        contentParent.removeAllViews();
-        contentParent.addView(v, lp);
-        mOriginalWindowCallback.onContentChanged();
-    }
-
-    @Override
-    public void addContentView(View v, ViewGroup.LayoutParams lp) {
-        ensureSubDecor();
-        ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
-        contentParent.addView(v, lp);
-        mOriginalWindowCallback.onContentChanged();
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mInvalidatePanelMenuPosted) {
-            mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
-        }
-
-        super.onDestroy();
-
-        if (mActionBar != null) {
-            mActionBar.onDestroy();
-        }
-    }
-
-    private void ensureSubDecor() {
-        if (!mSubDecorInstalled) {
-            mSubDecor = createSubDecor();
-
-            // If a title was set before we installed the decor, propagate it now
-            CharSequence title = getTitle();
-            if (!TextUtils.isEmpty(title)) {
-                onTitleChanged(title);
-            }
-
-            applyFixedSizeWindow();
-
-            onSubDecorInstalled(mSubDecor);
-
-            mSubDecorInstalled = true;
-
-            // Invalidate if the panel menu hasn't been created before this.
-            // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
-            // being called in the middle of onCreate or similar.
-            // A pending invalidation will typically be resolved before the posted message
-            // would run normally in order to satisfy instance state restoration.
-            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-            if (!isDestroyed() && (st == null || st.menu == null)) {
-                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
-            }
-        }
-    }
-
-    private ViewGroup createSubDecor() {
-        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
-
-        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
-            a.recycle();
-            throw new IllegalStateException(
-                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
-        }
-
-        if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
-            requestWindowFeature(Window.FEATURE_NO_TITLE);
-        } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
-            // Don't allow an action bar if there is no title.
-            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
-        }
-        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
-            requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
-        }
-        if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
-            requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
-        }
-        mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
-        a.recycle();
-
-        // Now let's make sure that the Window has installed its decor by retrieving it
-        mWindow.getDecorView();
-
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-        ViewGroup subDecor = null;
-
-
-        if (!mWindowNoTitle) {
-            if (mIsFloating) {
-                // If we're floating, inflate the dialog title decor
-                subDecor = (ViewGroup) inflater.inflate(
-                        R.layout.abc_dialog_title_material, null);
-
-                // Floating windows can never have an action bar, reset the flags
-                mHasActionBar = mOverlayActionBar = false;
-            } else if (mHasActionBar) {
-                /**
-                 * This needs some explanation. As we can not use the android:theme attribute
-                 * pre-L, we emulate it by manually creating a LayoutInflater using a
-                 * ContextThemeWrapper pointing to actionBarTheme.
-                 */
-                TypedValue outValue = new TypedValue();
-                mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
-
-                Context themedContext;
-                if (outValue.resourceId != 0) {
-                    themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
-                } else {
-                    themedContext = mContext;
-                }
-
-                // Now inflate the view using the themed context and set it as the content view
-                subDecor = (ViewGroup) LayoutInflater.from(themedContext)
-                        .inflate(R.layout.abc_screen_toolbar, null);
-
-                mDecorContentParent = (DecorContentParent) subDecor
-                        .findViewById(R.id.decor_content_parent);
-                mDecorContentParent.setWindowCallback(getWindowCallback());
-
-                /**
-                 * Propagate features to DecorContentParent
-                 */
-                if (mOverlayActionBar) {
-                    mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
-                }
-                if (mFeatureProgress) {
-                    mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
-                }
-                if (mFeatureIndeterminateProgress) {
-                    mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-                }
-            }
-        } else {
-            if (mOverlayActionMode) {
-                subDecor = (ViewGroup) inflater.inflate(
-                        R.layout.abc_screen_simple_overlay_action_mode, null);
-            } else {
-                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
-            }
-
-            if (Build.VERSION.SDK_INT >= 21) {
-                // If we're running on L or above, we can rely on ViewCompat's
-                // setOnApplyWindowInsetsListener
-                ViewCompat.setOnApplyWindowInsetsListener(subDecor,
-                        new OnApplyWindowInsetsListener() {
-                            @Override
-                            public WindowInsetsCompat onApplyWindowInsets(View v,
-                                    WindowInsetsCompat insets) {
-                                final int top = insets.getSystemWindowInsetTop();
-                                final int newTop = updateStatusGuard(top);
-
-                                if (top != newTop) {
-                                    insets = insets.replaceSystemWindowInsets(
-                                            insets.getSystemWindowInsetLeft(),
-                                            newTop,
-                                            insets.getSystemWindowInsetRight(),
-                                            insets.getSystemWindowInsetBottom());
-                                }
-
-                                // Now apply the insets on our view
-                                return ViewCompat.onApplyWindowInsets(v, insets);
-                            }
-                        });
-            } else {
-                // Else, we need to use our own FitWindowsViewGroup handling
-                ((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
-                        new FitWindowsViewGroup.OnFitSystemWindowsListener() {
-                            @Override
-                            public void onFitSystemWindows(Rect insets) {
-                                insets.top = updateStatusGuard(insets.top);
-                            }
-                        });
-            }
-        }
-
-        if (subDecor == null) {
-            throw new IllegalArgumentException(
-                    "AppCompat does not support the current theme features: { "
-                            + "windowActionBar: " + mHasActionBar
-                            + ", windowActionBarOverlay: "+ mOverlayActionBar
-                            + ", android:windowIsFloating: " + mIsFloating
-                            + ", windowActionModeOverlay: " + mOverlayActionMode
-                            + ", windowNoTitle: " + mWindowNoTitle
-                            + " }");
-        }
-
-        if (mDecorContentParent == null) {
-            mTitleView = (TextView) subDecor.findViewById(R.id.title);
-        }
-
-        // Make the decor optionally fit system windows, like the window's decor
-        ViewUtils.makeOptionalFitsSystemWindows(subDecor);
-
-        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
-                R.id.action_bar_activity_content);
-
-        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
-        if (windowContentView != null) {
-            // There might be Views already added to the Window's content view so we need to
-            // migrate them to our content view
-            while (windowContentView.getChildCount() > 0) {
-                final View child = windowContentView.getChildAt(0);
-                windowContentView.removeViewAt(0);
-                contentView.addView(child);
-            }
-
-            // Change our content FrameLayout to use the android.R.id.content id.
-            // Useful for fragments.
-            windowContentView.setId(View.NO_ID);
-            contentView.setId(android.R.id.content);
-
-            // The decorContent may have a foreground drawable set (windowContentOverlay).
-            // Remove this as we handle it ourselves
-            if (windowContentView instanceof FrameLayout) {
-                ((FrameLayout) windowContentView).setForeground(null);
-            }
-        }
-
-        // Now set the Window's content view with the decor
-        mWindow.setContentView(subDecor);
-
-        contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
-            @Override
-            public void onAttachedFromWindow() {}
-
-            @Override
-            public void onDetachedFromWindow() {
-                dismissPopups();
-            }
-        });
-
-        return subDecor;
-    }
-
-    void onSubDecorInstalled(ViewGroup subDecor) {}
-
-    private void applyFixedSizeWindow() {
-        ContentFrameLayout cfl = (ContentFrameLayout) mSubDecor.findViewById(android.R.id.content);
-
-        // This is a bit weird. In the framework, the window sizing attributes control
-        // the decor view's size, meaning that any padding is inset for the min/max widths below.
-        // We don't control measurement at that level, so we need to workaround it by making sure
-        // that the decor view's padding is taken into account.
-        final View windowDecor = mWindow.getDecorView();
-        cfl.setDecorPadding(windowDecor.getPaddingLeft(),
-                windowDecor.getPaddingTop(), windowDecor.getPaddingRight(),
-                windowDecor.getPaddingBottom());
-
-        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
-        a.getValue(R.styleable.AppCompatTheme_windowMinWidthMajor, cfl.getMinWidthMajor());
-        a.getValue(R.styleable.AppCompatTheme_windowMinWidthMinor, cfl.getMinWidthMinor());
-
-        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMajor)) {
-            a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMajor,
-                    cfl.getFixedWidthMajor());
-        }
-        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedWidthMinor)) {
-            a.getValue(R.styleable.AppCompatTheme_windowFixedWidthMinor,
-                    cfl.getFixedWidthMinor());
-        }
-        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMajor)) {
-            a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMajor,
-                    cfl.getFixedHeightMajor());
-        }
-        if (a.hasValue(R.styleable.AppCompatTheme_windowFixedHeightMinor)) {
-            a.getValue(R.styleable.AppCompatTheme_windowFixedHeightMinor,
-                    cfl.getFixedHeightMinor());
-        }
-        a.recycle();
-
-        cfl.requestLayout();
-    }
-
-    @Override
-    public boolean requestWindowFeature(int featureId) {
-        featureId = sanitizeWindowFeatureId(featureId);
-
-        if (mWindowNoTitle && featureId == FEATURE_SUPPORT_ACTION_BAR) {
-            return false; // Ignore. No title dominates.
-        }
-        if (mHasActionBar && featureId == Window.FEATURE_NO_TITLE) {
-            // Remove the action bar feature if we have no title. No title dominates.
-            mHasActionBar = false;
-        }
-
-        switch (featureId) {
-            case FEATURE_SUPPORT_ACTION_BAR:
-                throwFeatureRequestIfSubDecorInstalled();
-                mHasActionBar = true;
-                return true;
-            case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
-                throwFeatureRequestIfSubDecorInstalled();
-                mOverlayActionBar = true;
-                return true;
-            case FEATURE_ACTION_MODE_OVERLAY:
-                throwFeatureRequestIfSubDecorInstalled();
-                mOverlayActionMode = true;
-                return true;
-            case Window.FEATURE_PROGRESS:
-                throwFeatureRequestIfSubDecorInstalled();
-                mFeatureProgress = true;
-                return true;
-            case Window.FEATURE_INDETERMINATE_PROGRESS:
-                throwFeatureRequestIfSubDecorInstalled();
-                mFeatureIndeterminateProgress = true;
-                return true;
-            case Window.FEATURE_NO_TITLE:
-                throwFeatureRequestIfSubDecorInstalled();
-                mWindowNoTitle = true;
-                return true;
-        }
-
-        return mWindow.requestFeature(featureId);
-    }
-
-    @Override
-    public boolean hasWindowFeature(int featureId) {
-        featureId = sanitizeWindowFeatureId(featureId);
-        switch (featureId) {
-            case FEATURE_SUPPORT_ACTION_BAR:
-                return mHasActionBar;
-            case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
-                return mOverlayActionBar;
-            case FEATURE_ACTION_MODE_OVERLAY:
-                return mOverlayActionMode;
-            case Window.FEATURE_PROGRESS:
-                return mFeatureProgress;
-            case Window.FEATURE_INDETERMINATE_PROGRESS:
-                return mFeatureIndeterminateProgress;
-            case Window.FEATURE_NO_TITLE:
-                return mWindowNoTitle;
-        }
-        return false;
-    }
-
-    @Override
-    void onTitleChanged(CharSequence title) {
-        if (mDecorContentParent != null) {
-            mDecorContentParent.setWindowTitle(title);
-        } else if (peekSupportActionBar() != null) {
-            peekSupportActionBar().setWindowTitle(title);
-        } else if (mTitleView != null) {
-            mTitleView.setText(title);
-        }
-    }
-
-    @Override
-    void onPanelClosed(final int featureId, Menu menu) {
-        if (featureId == FEATURE_SUPPORT_ACTION_BAR) {
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.dispatchMenuVisibilityChanged(false);
-            }
-        } else if (featureId == FEATURE_OPTIONS_PANEL) {
-            // Make sure that the options panel is closed. This is mainly used when we're using a
-            // ToolbarActionBar
-            PanelFeatureState st = getPanelState(featureId, true);
-            if (st.isOpen) {
-                closePanel(st, false);
-            }
-        }
-    }
-
-    @Override
-    boolean onMenuOpened(final int featureId, Menu menu) {
-        if (featureId == FEATURE_SUPPORT_ACTION_BAR) {
-            ActionBar ab = getSupportActionBar();
-            if (ab != null) {
-                ab.dispatchMenuVisibilityChanged(true);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-        final Window.Callback cb = getWindowCallback();
-        if (cb != null && !isDestroyed()) {
-            final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
-            if (panel != null) {
-                return cb.onMenuItemSelected(panel.featureId, item);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void onMenuModeChange(MenuBuilder menu) {
-        reopenMenu(menu, true);
-    }
-
-    @Override
-    public ActionMode startSupportActionMode(@NonNull final ActionMode.Callback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("ActionMode callback can not be null.");
-        }
-
-        if (mActionMode != null) {
-            mActionMode.finish();
-        }
-
-        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapperV9(callback);
-
-        ActionBar ab = getSupportActionBar();
-        if (ab != null) {
-            mActionMode = ab.startActionMode(wrappedCallback);
-            if (mActionMode != null && mAppCompatCallback != null) {
-                mAppCompatCallback.onSupportActionModeStarted(mActionMode);
-            }
-        }
-
-        if (mActionMode == null) {
-            // If the action bar didn't provide an action mode, start the emulated window one
-            mActionMode = startSupportActionModeFromWindow(wrappedCallback);
-        }
-
-        return mActionMode;
-    }
-
-    @Override
-    public void invalidateOptionsMenu() {
-        final ActionBar ab = getSupportActionBar();
-        if (ab != null && ab.invalidateOptionsMenu()) return;
-
-        invalidatePanelMenu(FEATURE_OPTIONS_PANEL);
-    }
-
-    @Override
-    ActionMode startSupportActionModeFromWindow(@NonNull ActionMode.Callback callback) {
-        endOnGoingFadeAnimation();
-        if (mActionMode != null) {
-            mActionMode.finish();
-        }
-
-        if (!(callback instanceof ActionModeCallbackWrapperV9)) {
-            // If the callback hasn't been wrapped yet, wrap it
-            callback = new ActionModeCallbackWrapperV9(callback);
-        }
-
-        ActionMode mode = null;
-        if (mAppCompatCallback != null && !isDestroyed()) {
-            try {
-                mode = mAppCompatCallback.onWindowStartingSupportActionMode(callback);
-            } catch (AbstractMethodError ame) {
-                // Older apps might not implement this callback method.
-            }
-        }
-
-        if (mode != null) {
-            mActionMode = mode;
-        } else {
-            if (mActionModeView == null) {
-                if (mIsFloating) {
-                    // Use the action bar theme.
-                    final TypedValue outValue = new TypedValue();
-                    final Resources.Theme baseTheme = mContext.getTheme();
-                    baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
-
-                    final Context actionBarContext;
-                    if (outValue.resourceId != 0) {
-                        final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
-                        actionBarTheme.setTo(baseTheme);
-                        actionBarTheme.applyStyle(outValue.resourceId, true);
-
-                        actionBarContext = new ContextThemeWrapper(mContext, 0);
-                        actionBarContext.getTheme().setTo(actionBarTheme);
-                    } else {
-                        actionBarContext = mContext;
-                    }
-
-                    mActionModeView = new ActionBarContextView(actionBarContext);
-                    mActionModePopup = new PopupWindow(actionBarContext, null,
-                            R.attr.actionModePopupWindowStyle);
-                    PopupWindowCompat.setWindowLayoutType(mActionModePopup,
-                            WindowManager.LayoutParams.TYPE_APPLICATION);
-                    mActionModePopup.setContentView(mActionModeView);
-                    mActionModePopup.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
-
-                    actionBarContext.getTheme().resolveAttribute(
-                            R.attr.actionBarSize, outValue, true);
-                    final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
-                            actionBarContext.getResources().getDisplayMetrics());
-                    mActionModeView.setContentHeight(height);
-                    mActionModePopup.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
-                    mShowActionModePopup = new Runnable() {
-                        @Override
-                        public void run() {
-                            mActionModePopup.showAtLocation(
-                                    mActionModeView,
-                                    Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
-                            endOnGoingFadeAnimation();
-
-                            if (shouldAnimateActionModeView()) {
-                                mActionModeView.setAlpha(0f);
-                                mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
-                                mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
-                                    @Override
-                                    public void onAnimationStart(View view) {
-                                        mActionModeView.setVisibility(View.VISIBLE);
-                                    }
-
-                                    @Override
-                                    public void onAnimationEnd(View view) {
-                                        mActionModeView.setAlpha(1f);
-                                        mFadeAnim.setListener(null);
-                                        mFadeAnim = null;
-                                    }
-                                });
-                            } else {
-                                mActionModeView.setAlpha(1f);
-                                mActionModeView.setVisibility(View.VISIBLE);
-                            }
-                        }
-                    };
-                } else {
-                    ViewStubCompat stub = (ViewStubCompat) mSubDecor
-                            .findViewById(R.id.action_mode_bar_stub);
-                    if (stub != null) {
-                        // Set the layout inflater so that it is inflated with the action bar's context
-                        stub.setLayoutInflater(LayoutInflater.from(getActionBarThemedContext()));
-                        mActionModeView = (ActionBarContextView) stub.inflate();
-                    }
-                }
-            }
-
-            if (mActionModeView != null) {
-                endOnGoingFadeAnimation();
-                mActionModeView.killMode();
-                mode = new StandaloneActionMode(mActionModeView.getContext(), mActionModeView,
-                        callback, mActionModePopup == null);
-                if (callback.onCreateActionMode(mode, mode.getMenu())) {
-                    mode.invalidate();
-                    mActionModeView.initForMode(mode);
-                    mActionMode = mode;
-
-                    if (shouldAnimateActionModeView()) {
-                        mActionModeView.setAlpha(0f);
-                        mFadeAnim = ViewCompat.animate(mActionModeView).alpha(1f);
-                        mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationStart(View view) {
-                                mActionModeView.setVisibility(View.VISIBLE);
-                                mActionModeView.sendAccessibilityEvent(
-                                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                                if (mActionModeView.getParent() instanceof View) {
-                                    ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
-                                }
-                            }
-
-                            @Override
-                            public void onAnimationEnd(View view) {
-                                mActionModeView.setAlpha(1f);
-                                mFadeAnim.setListener(null);
-                                mFadeAnim = null;
-                            }
-                        });
-                    } else {
-                        mActionModeView.setAlpha(1f);
-                        mActionModeView.setVisibility(View.VISIBLE);
-                        mActionModeView.sendAccessibilityEvent(
-                                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-                        if (mActionModeView.getParent() instanceof View) {
-                            ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
-                        }
-                    }
-
-                    if (mActionModePopup != null) {
-                        mWindow.getDecorView().post(mShowActionModePopup);
-                    }
-                } else {
-                    mActionMode = null;
-                }
-            }
-        }
-        if (mActionMode != null && mAppCompatCallback != null) {
-            mAppCompatCallback.onSupportActionModeStarted(mActionMode);
-        }
-        return mActionMode;
-    }
-
-    final boolean shouldAnimateActionModeView() {
-        // We only to animate the action mode in if the sub decor has already been laid out.
-        // If it hasn't been laid out, it hasn't been drawn to screen yet.
-        return mSubDecorInstalled && mSubDecor != null && ViewCompat.isLaidOut(mSubDecor);
-    }
-
-    void endOnGoingFadeAnimation() {
-        if (mFadeAnim != null) {
-            mFadeAnim.cancel();
-        }
-    }
-
-    boolean onBackPressed() {
-        // Back cancels action modes first.
-        if (mActionMode != null) {
-            mActionMode.finish();
-            return true;
-        }
-
-        // Next collapse any expanded action views.
-        ActionBar ab = getSupportActionBar();
-        if (ab != null && ab.collapseActionView()) {
-            return true;
-        }
-
-        // Let the call through...
-        return false;
-    }
-
-    @Override
-    boolean onKeyShortcut(int keyCode, KeyEvent ev) {
-        // Let the Action Bar have a chance at handling the shortcut
-        ActionBar ab = getSupportActionBar();
-        if (ab != null && ab.onKeyShortcut(keyCode, ev)) {
-            return true;
-        }
-
-        // If the panel is already prepared, then perform the shortcut using it.
-        boolean handled;
-        if (mPreparedPanel != null) {
-            handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
-                    Menu.FLAG_PERFORM_NO_CLOSE);
-            if (handled) {
-                if (mPreparedPanel != null) {
-                    mPreparedPanel.isHandled = true;
-                }
-                return true;
-            }
-        }
-
-        // If the panel is not prepared, then we may be trying to handle a shortcut key
-        // combination such as Control+C.  Temporarily prepare the panel then mark it
-        // unprepared again when finished to ensure that the panel will again be prepared
-        // the next time it is shown for real.
-        if (mPreparedPanel == null) {
-            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
-            preparePanel(st, ev);
-            handled = performPanelShortcut(st, ev.getKeyCode(), ev, Menu.FLAG_PERFORM_NO_CLOSE);
-            st.isPrepared = false;
-            if (handled) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    boolean dispatchKeyEvent(KeyEvent event) {
-        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) {
-            // If this is a MENU event, let the Activity have a go.
-            if (mOriginalWindowCallback.dispatchKeyEvent(event)) {
-                return true;
-            }
-        }
-
-        final int keyCode = event.getKeyCode();
-        final int action = event.getAction();
-        final boolean isDown = action == KeyEvent.ACTION_DOWN;
-
-        return isDown ? onKeyDown(keyCode, event) : onKeyUp(keyCode, event);
-    }
-
-    boolean onKeyUp(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MENU:
-                onKeyUpPanel(Window.FEATURE_OPTIONS_PANEL, event);
-                return true;
-            case KeyEvent.KEYCODE_BACK:
-                final boolean wasLongPressBackDown = mLongPressBackDown;
-                mLongPressBackDown = false;
-
-                PanelFeatureState st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
-                if (st != null && st.isOpen) {
-                    if (!wasLongPressBackDown) {
-                        // Certain devices allow opening the options menu via a long press of the
-                        // back button. We should only close the open options menu if it wasn't
-                        // opened via a long press gesture.
-                        closePanel(st, true);
-                    }
-                    return true;
-                }
-                if (onBackPressed()) {
-                    return true;
-                }
-                break;
-        }
-        return false;
-    }
-
-    boolean onKeyDown(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MENU:
-                onKeyDownPanel(Window.FEATURE_OPTIONS_PANEL, event);
-                // We need to return true here and not let it bubble up to the Window.
-                // For empty menus, PhoneWindow's KEYCODE_BACK handling will steals all events,
-                // not allowing the Activity to call onBackPressed().
-                return true;
-            case KeyEvent.KEYCODE_BACK:
-                // Certain devices allow opening the options menu via a long press of the back
-                // button. We keep a record of whether the last event is from a long press.
-                mLongPressBackDown = (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
-                break;
-        }
-        return false;
-    }
-
-    @Override
-    public View createView(View parent, final String name, @NonNull Context context,
-            @NonNull AttributeSet attrs) {
-        if (mAppCompatViewInflater == null) {
-            TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
-            String viewInflaterClassName =
-                    a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
-            if ((viewInflaterClassName == null)
-                    || AppCompatViewInflater.class.getName().equals(viewInflaterClassName)) {
-                // Either default class name or set explicitly to null. In both cases
-                // create the base inflater (no reflection)
-                mAppCompatViewInflater = new AppCompatViewInflater();
-            } else {
-                try {
-                    Class viewInflaterClass = Class.forName(viewInflaterClassName);
-                    mAppCompatViewInflater =
-                            (AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
-                                    .newInstance();
-                } catch (Throwable t) {
-                    Log.i(TAG, "Failed to instantiate custom view inflater "
-                            + viewInflaterClassName + ". Falling back to default.", t);
-                    mAppCompatViewInflater = new AppCompatViewInflater();
-                }
-            }
-        }
-
-        boolean inheritContext = false;
-        if (IS_PRE_LOLLIPOP) {
-            inheritContext = (attrs instanceof XmlPullParser)
-                    // If we have a XmlPullParser, we can detect where we are in the layout
-                    ? ((XmlPullParser) attrs).getDepth() > 1
-                    // Otherwise we have to use the old heuristic
-                    : shouldInheritContext((ViewParent) parent);
-        }
-
-        return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
-                IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
-                true, /* Read read app:theme as a fallback at all times for legacy reasons */
-                VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
-        );
-    }
-
-    private boolean shouldInheritContext(ViewParent parent) {
-        if (parent == null) {
-            // The initial parent is null so just return false
-            return false;
-        }
-        final View windowDecor = mWindow.getDecorView();
-        while (true) {
-            if (parent == null) {
-                // Bingo. We've hit a view which has a null parent before being terminated from
-                // the loop. This is (most probably) because it's the root view in an inflation
-                // call, therefore we should inherit. This works as the inflated layout is only
-                // added to the hierarchy at the end of the inflate() call.
-                return true;
-            } else if (parent == windowDecor || !(parent instanceof View)
-                    || ViewCompat.isAttachedToWindow((View) parent)) {
-                // We have either hit the window's decor view, a parent which isn't a View
-                // (i.e. ViewRootImpl), or an attached view, so we know that the original parent
-                // is currently added to the view hierarchy. This means that it has not be
-                // inflated in the current inflate() call and we should not inherit the context.
-                return false;
-            }
-            parent = parent.getParent();
-        }
-    }
-
-    @Override
-    public void installViewFactory() {
-        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        if (layoutInflater.getFactory() == null) {
-            LayoutInflaterCompat.setFactory2(layoutInflater, this);
-        } else {
-            if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
-                Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
-                        + " so we can not install AppCompat's");
-            }
-        }
-    }
-
-    /**
-     * From {@link LayoutInflater.Factory2}.
-     */
-    @Override
-    public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-        // First let the Activity's Factory try and inflate the view
-        final View view = callActivityOnCreateView(parent, name, context, attrs);
-        if (view != null) {
-            return view;
-        }
-
-        // If the Factory didn't handle it, let our createView() method try
-        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) {
-            final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
-                    .onCreateView(name, context, attrs);
-            if (result != null) {
-                return result;
-            }
-        }
-        return null;
-    }
-
-    private void openPanel(final PanelFeatureState st, KeyEvent event) {
-        // Already open, return
-        if (st.isOpen || isDestroyed()) {
-            return;
-        }
-
-        // Don't open an options panel on xlarge devices.
-        // (The app should be using an action bar for menu items.)
-        if (st.featureId == FEATURE_OPTIONS_PANEL) {
-            Configuration config = mContext.getResources().getConfiguration();
-            boolean isXLarge = (config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
-                    == Configuration.SCREENLAYOUT_SIZE_XLARGE;
-            if (isXLarge) {
-                return;
-            }
-        }
-
-        Window.Callback cb = getWindowCallback();
-        if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
-            // Callback doesn't want the menu to open, reset any state
-            closePanel(st, true);
-            return;
-        }
-
-        final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        if (wm == null) {
-            return;
-        }
-
-        // Prepare panel (should have been done before, but just in case)
-        if (!preparePanel(st, event)) {
-            return;
-        }
-
-        int width = WRAP_CONTENT;
-        if (st.decorView == null || st.refreshDecorView) {
-            if (st.decorView == null) {
-                // Initialize the panel decor, this will populate st.decorView
-                if (!initializePanelDecor(st) || (st.decorView == null))
-                    return;
-            } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {
-                // Decor needs refreshing, so remove its views
-                st.decorView.removeAllViews();
-            }
-
-            // This will populate st.shownPanelView
-            if (!initializePanelContent(st) || !st.hasPanelItems()) {
-                return;
-            }
-
-            ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();
-            if (lp == null) {
-                lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
-            }
-
-            int backgroundResId = st.background;
-            st.decorView.setBackgroundResource(backgroundResId);
-
-            ViewParent shownPanelParent = st.shownPanelView.getParent();
-            if (shownPanelParent != null && shownPanelParent instanceof ViewGroup) {
-                ((ViewGroup) shownPanelParent).removeView(st.shownPanelView);
-            }
-            st.decorView.addView(st.shownPanelView, lp);
-
-            /*
-             * Give focus to the view, if it or one of its children does not
-             * already have it.
-             */
-            if (!st.shownPanelView.hasFocus()) {
-                st.shownPanelView.requestFocus();
-            }
-        } else if (st.createdPanelView != null) {
-            // If we already had a panel view, carry width=MATCH_PARENT through
-            // as we did above when it was created.
-            ViewGroup.LayoutParams lp = st.createdPanelView.getLayoutParams();
-            if (lp != null && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
-                width = MATCH_PARENT;
-            }
-        }
-
-        st.isHandled = false;
-
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                width, WRAP_CONTENT,
-                st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
-                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-
-        lp.gravity = st.gravity;
-        lp.windowAnimations = st.windowAnimations;
-
-        wm.addView(st.decorView, lp);
-        st.isOpen = true;
-    }
-
-    private boolean initializePanelDecor(PanelFeatureState st) {
-        st.setStyle(getActionBarThemedContext());
-        st.decorView = new ListMenuDecorView(st.listPresenterContext);
-        st.gravity = Gravity.CENTER | Gravity.BOTTOM;
-        return true;
-    }
-
-    private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
-        if (mDecorContentParent != null && mDecorContentParent.canShowOverflowMenu()
-                && (!ViewConfiguration.get(mContext).hasPermanentMenuKey()
-                        || mDecorContentParent.isOverflowMenuShowPending())) {
-
-            final Window.Callback cb = getWindowCallback();
-
-            if (!mDecorContentParent.isOverflowMenuShowing() || !toggleMenuMode) {
-                if (cb != null && !isDestroyed()) {
-                    // If we have a menu invalidation pending, do it now.
-                    if (mInvalidatePanelMenuPosted &&
-                            (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
-                        mWindow.getDecorView().removeCallbacks(mInvalidatePanelMenuRunnable);
-                        mInvalidatePanelMenuRunnable.run();
-                    }
-
-                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
-
-                    // If we don't have a menu or we're waiting for a full content refresh,
-                    // forget it. This is a lingering event that no longer matters.
-                    if (st.menu != null && !st.refreshMenuContent &&
-                            cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
-                        cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, st.menu);
-                        mDecorContentParent.showOverflowMenu();
-                    }
-                }
-            } else {
-                mDecorContentParent.hideOverflowMenu();
-                if (!isDestroyed()) {
-                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
-                    cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, st.menu);
-                }
-            }
-            return;
-        }
-
-        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
-
-        st.refreshDecorView = true;
-        closePanel(st, false);
-
-        openPanel(st, null);
-    }
-
-    private boolean initializePanelMenu(final PanelFeatureState st) {
-        Context context = mContext;
-
-        // If we have an action bar, initialize the menu with the right theme.
-        if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_SUPPORT_ACTION_BAR) &&
-                mDecorContentParent != null) {
-            final TypedValue outValue = new TypedValue();
-            final Resources.Theme baseTheme = context.getTheme();
-            baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
-
-            Resources.Theme widgetTheme = null;
-            if (outValue.resourceId != 0) {
-                widgetTheme = context.getResources().newTheme();
-                widgetTheme.setTo(baseTheme);
-                widgetTheme.applyStyle(outValue.resourceId, true);
-                widgetTheme.resolveAttribute(
-                        R.attr.actionBarWidgetTheme, outValue, true);
-            } else {
-                baseTheme.resolveAttribute(
-                        R.attr.actionBarWidgetTheme, outValue, true);
-            }
-
-            if (outValue.resourceId != 0) {
-                if (widgetTheme == null) {
-                    widgetTheme = context.getResources().newTheme();
-                    widgetTheme.setTo(baseTheme);
-                }
-                widgetTheme.applyStyle(outValue.resourceId, true);
-            }
-
-            if (widgetTheme != null) {
-                context = new ContextThemeWrapper(context, 0);
-                context.getTheme().setTo(widgetTheme);
-            }
-        }
-
-        final MenuBuilder menu = new MenuBuilder(context);
-        menu.setCallback(this);
-        st.setMenu(menu);
-
-        return true;
-    }
-
-    private boolean initializePanelContent(PanelFeatureState st) {
-        if (st.createdPanelView != null) {
-            st.shownPanelView = st.createdPanelView;
-            return true;
-        }
-
-        if (st.menu == null) {
-            return false;
-        }
-
-        if (mPanelMenuPresenterCallback == null) {
-            mPanelMenuPresenterCallback = new PanelMenuPresenterCallback();
-        }
-
-        MenuView menuView = st.getListMenuView(mPanelMenuPresenterCallback);
-
-        st.shownPanelView = (View) menuView;
-
-        return st.shownPanelView != null;
-    }
-
-    private boolean preparePanel(PanelFeatureState st, KeyEvent event) {
-        if (isDestroyed()) {
-            return false;
-        }
-
-        // Already prepared (isPrepared will be reset to false later)
-        if (st.isPrepared) {
-            return true;
-        }
-
-        if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
-            // Another Panel is prepared and possibly open, so close it
-            closePanel(mPreparedPanel, false);
-        }
-
-        final Window.Callback cb = getWindowCallback();
-
-        if (cb != null) {
-            st.createdPanelView = cb.onCreatePanelView(st.featureId);
-        }
-
-        final boolean isActionBarMenu =
-                (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_SUPPORT_ACTION_BAR);
-
-        if (isActionBarMenu && mDecorContentParent != null) {
-            // Enforce ordering guarantees around events so that the action bar never
-            // dispatches menu-related events before the panel is prepared.
-            mDecorContentParent.setMenuPrepared();
-        }
-
-        if (st.createdPanelView == null &&
-                (!isActionBarMenu || !(peekSupportActionBar() instanceof ToolbarActionBar))) {
-            // Since ToolbarActionBar handles the list options menu itself, we only want to
-            // init this menu panel if we're not using a TAB.
-            if (st.menu == null || st.refreshMenuContent) {
-                if (st.menu == null) {
-                    if (!initializePanelMenu(st) || (st.menu == null)) {
-                        return false;
-                    }
-                }
-
-                if (isActionBarMenu && mDecorContentParent != null) {
-                    if (mActionMenuPresenterCallback == null) {
-                        mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
-                    }
-                    mDecorContentParent.setMenu(st.menu, mActionMenuPresenterCallback);
-                }
-
-                // Creating the panel menu will involve a lot of manipulation;
-                // don't dispatch change events to presenters until we're done.
-                st.menu.stopDispatchingItemsChanged();
-                if (!cb.onCreatePanelMenu(st.featureId, st.menu)) {
-                    // Ditch the menu created above
-                    st.setMenu(null);
-
-                    if (isActionBarMenu && mDecorContentParent != null) {
-                        // Don't show it in the action bar either
-                        mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
-                    }
-
-                    return false;
-                }
-
-                st.refreshMenuContent = false;
-            }
-
-            // Preparing the panel menu can involve a lot of manipulation;
-            // don't dispatch change events to presenters until we're done.
-            st.menu.stopDispatchingItemsChanged();
-
-            // Restore action view state before we prepare. This gives apps
-            // an opportunity to override frozen/restored state in onPrepare.
-            if (st.frozenActionViewState != null) {
-                st.menu.restoreActionViewStates(st.frozenActionViewState);
-                st.frozenActionViewState = null;
-            }
-
-            // Callback and return if the callback does not want to show the menu
-            if (!cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
-                if (isActionBarMenu && mDecorContentParent != null) {
-                    // The app didn't want to show the menu for now but it still exists.
-                    // Clear it out of the action bar.
-                    mDecorContentParent.setMenu(null, mActionMenuPresenterCallback);
-                }
-                st.menu.startDispatchingItemsChanged();
-                return false;
-            }
-
-            // Set the proper keymap
-            KeyCharacterMap kmap = KeyCharacterMap.load(
-                    event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
-            st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
-            st.menu.setQwertyMode(st.qwertyMode);
-            st.menu.startDispatchingItemsChanged();
-        }
-
-        // Set other state
-        st.isPrepared = true;
-        st.isHandled = false;
-        mPreparedPanel = st;
-
-        return true;
-    }
-
-    void checkCloseActionMenu(MenuBuilder menu) {
-        if (mClosingActionMenu) {
-            return;
-        }
-
-        mClosingActionMenu = true;
-        mDecorContentParent.dismissPopups();
-        Window.Callback cb = getWindowCallback();
-        if (cb != null && !isDestroyed()) {
-            cb.onPanelClosed(FEATURE_SUPPORT_ACTION_BAR, menu);
-        }
-        mClosingActionMenu = false;
-    }
-
-    void closePanel(int featureId) {
-        closePanel(getPanelState(featureId, true), true);
-    }
-
-    void closePanel(PanelFeatureState st, boolean doCallback) {
-        if (doCallback && st.featureId == FEATURE_OPTIONS_PANEL &&
-                mDecorContentParent != null && mDecorContentParent.isOverflowMenuShowing()) {
-            checkCloseActionMenu(st.menu);
-            return;
-        }
-
-        final WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        if (wm != null && st.isOpen && st.decorView != null) {
-            wm.removeView(st.decorView);
-
-            if (doCallback) {
-                callOnPanelClosed(st.featureId, st, null);
-            }
-        }
-
-        st.isPrepared = false;
-        st.isHandled = false;
-        st.isOpen = false;
-
-        // This view is no longer shown, so null it out
-        st.shownPanelView = null;
-
-        // Next time the menu opens, it should not be in expanded mode, so
-        // force a refresh of the decor
-        st.refreshDecorView = true;
-
-        if (mPreparedPanel == st) {
-            mPreparedPanel = null;
-        }
-    }
-
-    private boolean onKeyDownPanel(int featureId, KeyEvent event) {
-        if (event.getRepeatCount() == 0) {
-            PanelFeatureState st = getPanelState(featureId, true);
-            if (!st.isOpen) {
-                return preparePanel(st, event);
-            }
-        }
-
-        return false;
-    }
-
-    private boolean onKeyUpPanel(int featureId, KeyEvent event) {
-        if (mActionMode != null) {
-            return false;
-        }
-
-        boolean handled = false;
-        final PanelFeatureState st = getPanelState(featureId, true);
-        if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
-                mDecorContentParent.canShowOverflowMenu() &&
-                !ViewConfiguration.get(mContext).hasPermanentMenuKey()) {
-            if (!mDecorContentParent.isOverflowMenuShowing()) {
-                if (!isDestroyed() && preparePanel(st, event)) {
-                    handled = mDecorContentParent.showOverflowMenu();
-                }
-            } else {
-                handled = mDecorContentParent.hideOverflowMenu();
-            }
-        } else {
-            if (st.isOpen || st.isHandled) {
-                // Play the sound effect if the user closed an open menu (and not if
-                // they just released a menu shortcut)
-                handled = st.isOpen;
-                // Close menu
-                closePanel(st, true);
-            } else if (st.isPrepared) {
-                boolean show = true;
-                if (st.refreshMenuContent) {
-                    // Something may have invalidated the menu since we prepared it.
-                    // Re-prepare it to refresh.
-                    st.isPrepared = false;
-                    show = preparePanel(st, event);
-                }
-
-                if (show) {
-                    // Show menu
-                    openPanel(st, event);
-                    handled = true;
-                }
-            }
-        }
-
-        if (handled) {
-            AudioManager audioManager = (AudioManager) mContext.getSystemService(
-                    Context.AUDIO_SERVICE);
-            if (audioManager != null) {
-                audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
-            } else {
-                Log.w(TAG, "Couldn't get audio manager");
-            }
-        }
-        return handled;
-    }
-
-    void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
-        // Try to get a menu
-        if (menu == null) {
-            // Need a panel to grab the menu, so try to get that
-            if (panel == null) {
-                if ((featureId >= 0) && (featureId < mPanels.length)) {
-                    panel = mPanels[featureId];
-                }
-            }
-
-            if (panel != null) {
-                // menu still may be null, which is okay--we tried our best
-                menu = panel.menu;
-            }
-        }
-
-        // If the panel is not open, do not callback
-        if ((panel != null) && (!panel.isOpen))
-            return;
-
-        if (!isDestroyed()) {
-            // We need to be careful which callback we dispatch the call to. We can not dispatch
-            // this to the Window's callback since that will call back into this method and cause a
-            // crash. Instead we need to dispatch down to the original Activity/Dialog/etc.
-            mOriginalWindowCallback.onPanelClosed(featureId, menu);
-        }
-    }
-
-    PanelFeatureState findMenuPanel(Menu menu) {
-        final PanelFeatureState[] panels = mPanels;
-        final int N = panels != null ? panels.length : 0;
-        for (int i = 0; i < N; i++) {
-            final PanelFeatureState panel = panels[i];
-            if (panel != null && panel.menu == menu) {
-                return panel;
-            }
-        }
-        return null;
-    }
-
-    protected PanelFeatureState getPanelState(int featureId, boolean required) {
-        PanelFeatureState[] ar;
-        if ((ar = mPanels) == null || ar.length <= featureId) {
-            PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
-            if (ar != null) {
-                System.arraycopy(ar, 0, nar, 0, ar.length);
-            }
-            mPanels = ar = nar;
-        }
-
-        PanelFeatureState st = ar[featureId];
-        if (st == null) {
-            ar[featureId] = st = new PanelFeatureState(featureId);
-        }
-        return st;
-    }
-
-    private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
-            int flags) {
-        if (event.isSystem()) {
-            return false;
-        }
-
-        boolean handled = false;
-
-        // Only try to perform menu shortcuts if preparePanel returned true (possible false
-        // return value from application not wanting to show the menu).
-        if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
-            // The menu is prepared now, perform the shortcut on it
-            handled = st.menu.performShortcut(keyCode, event, flags);
-        }
-
-        if (handled) {
-            // Only close down the menu if we don't have an action bar keeping it open.
-            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0 && mDecorContentParent == null) {
-                closePanel(st, true);
-            }
-        }
-
-        return handled;
-    }
-
-    private void invalidatePanelMenu(int featureId) {
-        mInvalidatePanelMenuFeatures |= 1 << featureId;
-
-        if (!mInvalidatePanelMenuPosted) {
-            ViewCompat.postOnAnimation(mWindow.getDecorView(), mInvalidatePanelMenuRunnable);
-            mInvalidatePanelMenuPosted = true;
-        }
-    }
-
-    void doInvalidatePanelMenu(int featureId) {
-        PanelFeatureState st = getPanelState(featureId, true);
-        Bundle savedActionViewStates = null;
-        if (st.menu != null) {
-            savedActionViewStates = new Bundle();
-            st.menu.saveActionViewStates(savedActionViewStates);
-            if (savedActionViewStates.size() > 0) {
-                st.frozenActionViewState = savedActionViewStates;
-            }
-            // This will be started again when the panel is prepared.
-            st.menu.stopDispatchingItemsChanged();
-            st.menu.clear();
-        }
-        st.refreshMenuContent = true;
-        st.refreshDecorView = true;
-
-        // Prepare the options panel if we have an action bar
-        if ((featureId == FEATURE_SUPPORT_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
-                && mDecorContentParent != null) {
-            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
-            if (st != null) {
-                st.isPrepared = false;
-                preparePanel(st, null);
-            }
-        }
-    }
-
-    /**
-     * Updates the status bar guard
-     *
-     * @param insetTop the current top system window inset
-     * @return the new top system window inset
-     */
-    int updateStatusGuard(int insetTop) {
-        boolean showStatusGuard = false;
-        // Show the status guard when the non-overlay contextual action bar is showing
-        if (mActionModeView != null) {
-            if (mActionModeView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
-                ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams)
-                        mActionModeView.getLayoutParams();
-                boolean mlpChanged = false;
-
-                if (mActionModeView.isShown()) {
-                    if (mTempRect1 == null) {
-                        mTempRect1 = new Rect();
-                        mTempRect2 = new Rect();
-                    }
-                    final Rect insets = mTempRect1;
-                    final Rect localInsets = mTempRect2;
-                    insets.set(0, insetTop, 0, 0);
-
-                    ViewUtils.computeFitSystemWindows(mSubDecor, insets, localInsets);
-                    final int newMargin = localInsets.top == 0 ? insetTop : 0;
-                    if (mlp.topMargin != newMargin) {
-                        mlpChanged = true;
-                        mlp.topMargin = insetTop;
-
-                        if (mStatusGuard == null) {
-                            mStatusGuard = new View(mContext);
-                            mStatusGuard.setBackgroundColor(mContext.getResources()
-                                    .getColor(R.color.abc_input_method_navigation_guard));
-                            mSubDecor.addView(mStatusGuard, -1,
-                                    new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                                            insetTop));
-                        } else {
-                            ViewGroup.LayoutParams lp = mStatusGuard.getLayoutParams();
-                            if (lp.height != insetTop) {
-                                lp.height = insetTop;
-                                mStatusGuard.setLayoutParams(lp);
-                            }
-                        }
-                    }
-
-                    // The action mode's theme may differ from the app, so
-                    // always show the status guard above it.
-                    showStatusGuard = mStatusGuard != null;
-
-                    // We only need to consume the insets if the action
-                    // mode is overlaid on the app content (e.g. it's
-                    // sitting in a FrameLayout, see
-                    // screen_simple_overlay_action_mode.xml).
-                    if (!mOverlayActionMode && showStatusGuard) {
-                        insetTop = 0;
-                    }
-                } else {
-                    // reset top margin
-                    if (mlp.topMargin != 0) {
-                        mlpChanged = true;
-                        mlp.topMargin = 0;
-                    }
-                }
-                if (mlpChanged) {
-                    mActionModeView.setLayoutParams(mlp);
-                }
-            }
-        }
-        if (mStatusGuard != null) {
-            mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
-        }
-
-        return insetTop;
-    }
-
-    private void throwFeatureRequestIfSubDecorInstalled() {
-        if (mSubDecorInstalled) {
-            throw new AndroidRuntimeException(
-                    "Window feature must be requested before adding content");
-        }
-    }
-
-    private int sanitizeWindowFeatureId(int featureId) {
-        if (featureId == WindowCompat.FEATURE_ACTION_BAR) {
-            Log.i(TAG, "You should now use the AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR"
-                    + " id when requesting this feature.");
-            return FEATURE_SUPPORT_ACTION_BAR;
-        } else if (featureId == WindowCompat.FEATURE_ACTION_BAR_OVERLAY) {
-            Log.i(TAG, "You should now use the AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR_OVERLAY"
-                    + " id when requesting this feature.");
-            return FEATURE_SUPPORT_ACTION_BAR_OVERLAY;
-        }
-        // Else we'll just return the original id
-        return featureId;
-    }
-
-    ViewGroup getSubDecor() {
-        return mSubDecor;
-    }
-
-    void dismissPopups() {
-        if (mDecorContentParent != null) {
-            mDecorContentParent.dismissPopups();
-        }
-
-        if (mActionModePopup != null) {
-            mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
-            if (mActionModePopup.isShowing()) {
-                try {
-                    mActionModePopup.dismiss();
-                } catch (IllegalArgumentException e) {
-                    // Pre-v18, there are times when the Window will remove the popup before us.
-                    // In these cases we need to swallow the resulting exception.
-                }
-            }
-            mActionModePopup = null;
-        }
-        endOnGoingFadeAnimation();
-
-        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-        if (st != null && st.menu != null) {
-            st.menu.close();
-        }
-    }
-
-    /**
-     * Clears out internal reference when the action mode is destroyed.
-     */
-    class ActionModeCallbackWrapperV9 implements ActionMode.Callback {
-        private ActionMode.Callback mWrapped;
-
-        public ActionModeCallbackWrapperV9(ActionMode.Callback wrapped) {
-            mWrapped = wrapped;
-        }
-
-        @Override
-        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-            return mWrapped.onCreateActionMode(mode, menu);
-        }
-
-        @Override
-        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-            return mWrapped.onPrepareActionMode(mode, menu);
-        }
-
-        @Override
-        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            return mWrapped.onActionItemClicked(mode, item);
-        }
-
-        @Override
-        public void onDestroyActionMode(ActionMode mode) {
-            mWrapped.onDestroyActionMode(mode);
-            if (mActionModePopup != null) {
-                mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
-            }
-
-            if (mActionModeView != null) {
-                endOnGoingFadeAnimation();
-                mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0f);
-                mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(View view) {
-                        mActionModeView.setVisibility(View.GONE);
-                        if (mActionModePopup != null) {
-                            mActionModePopup.dismiss();
-                        } else if (mActionModeView.getParent() instanceof View) {
-                            ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
-                        }
-                        mActionModeView.removeAllViews();
-                        mFadeAnim.setListener(null);
-                        mFadeAnim = null;
-                    }
-                });
-            }
-            if (mAppCompatCallback != null) {
-                mAppCompatCallback.onSupportActionModeFinished(mActionMode);
-            }
-            mActionMode = null;
-        }
-    }
-
-    private final class PanelMenuPresenterCallback implements MenuPresenter.Callback {
-        PanelMenuPresenterCallback() {
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-            final Menu parentMenu = menu.getRootMenu();
-            final boolean isSubMenu = parentMenu != menu;
-            final PanelFeatureState panel = findMenuPanel(isSubMenu ? parentMenu : menu);
-            if (panel != null) {
-                if (isSubMenu) {
-                    callOnPanelClosed(panel.featureId, panel, parentMenu);
-                    closePanel(panel, true);
-                } else {
-                    // Close the panel and only do the callback if the menu is being
-                    // closed completely, not if opening a sub menu
-                    closePanel(panel, allMenusAreClosing);
-                }
-            }
-        }
-
-        @Override
-        public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            if (subMenu == null && mHasActionBar) {
-                Window.Callback cb = getWindowCallback();
-                if (cb != null && !isDestroyed()) {
-                    cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
-                }
-            }
-            return true;
-        }
-    }
-
-    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
-        ActionMenuPresenterCallback() {
-        }
-
-        @Override
-        public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            Window.Callback cb = getWindowCallback();
-            if (cb != null) {
-                cb.onMenuOpened(FEATURE_SUPPORT_ACTION_BAR, subMenu);
-            }
-            return true;
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-            checkCloseActionMenu(menu);
-        }
-    }
-
-    protected static final class PanelFeatureState {
-
-        /** Feature ID for this panel. */
-        int featureId;
-
-        int background;
-
-        int gravity;
-
-        int x;
-
-        int y;
-
-        int windowAnimations;
-
-        /** Dynamic state of the panel. */
-        ViewGroup decorView;
-
-        /** The panel that we are actually showing. */
-        View shownPanelView;
-
-        /** The panel that was returned by onCreatePanelView(). */
-        View createdPanelView;
-
-        /** Use {@link #setMenu} to set this. */
-        MenuBuilder menu;
-
-        ListMenuPresenter listMenuPresenter;
-
-        Context listPresenterContext;
-
-        /**
-         * Whether the panel has been prepared (see
-         * {@link #preparePanel}).
-         */
-        boolean isPrepared;
-
-        /**
-         * Whether an item's action has been performed. This happens in obvious
-         * scenarios (user clicks on menu item), but can also happen with
-         * chording menu+(shortcut key).
-         */
-        boolean isHandled;
-
-        boolean isOpen;
-
-        public boolean qwertyMode;
-
-        boolean refreshDecorView;
-
-        boolean refreshMenuContent;
-
-        boolean wasLastOpen;
-
-        /**
-         * Contains the state of the menu when told to freeze.
-         */
-        Bundle frozenMenuState;
-
-        /**
-         * Contains the state of associated action views when told to freeze.
-         * These are saved across invalidations.
-         */
-        Bundle frozenActionViewState;
-
-        PanelFeatureState(int featureId) {
-            this.featureId = featureId;
-
-            refreshDecorView = false;
-        }
-
-        public boolean hasPanelItems() {
-            if (shownPanelView == null) return false;
-            if (createdPanelView != null) return true;
-
-            return listMenuPresenter.getAdapter().getCount() > 0;
-        }
-
-        /**
-         * Unregister and free attached MenuPresenters. They will be recreated as needed.
-         */
-        public void clearMenuPresenters() {
-            if (menu != null) {
-                menu.removeMenuPresenter(listMenuPresenter);
-            }
-            listMenuPresenter = null;
-        }
-
-        void setStyle(Context context) {
-            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);
-            }
-
-            // Now 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);
-
-            listPresenterContext = context;
-
-            TypedArray a = context.obtainStyledAttributes(R.styleable.AppCompatTheme);
-            background = a.getResourceId(
-                    R.styleable.AppCompatTheme_panelBackground, 0);
-            windowAnimations = a.getResourceId(
-                    R.styleable.AppCompatTheme_android_windowAnimationStyle, 0);
-            a.recycle();
-        }
-
-        void setMenu(MenuBuilder menu) {
-            if (menu == this.menu) return;
-
-            if (this.menu != null) {
-                this.menu.removeMenuPresenter(listMenuPresenter);
-            }
-            this.menu = menu;
-            if (menu != null) {
-                if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);
-            }
-        }
-
-        MenuView getListMenuView(MenuPresenter.Callback cb) {
-            if (menu == null) return null;
-
-            if (listMenuPresenter == null) {
-                listMenuPresenter = new ListMenuPresenter(listPresenterContext,
-                        R.layout.abc_list_menu_item_layout);
-                listMenuPresenter.setCallback(cb);
-                menu.addMenuPresenter(listMenuPresenter);
-            }
-
-            MenuView result = listMenuPresenter.getMenuView(decorView);
-
-            return result;
-        }
-
-        Parcelable onSaveInstanceState() {
-            SavedState savedState = new SavedState();
-            savedState.featureId = featureId;
-            savedState.isOpen = isOpen;
-
-            if (menu != null) {
-                savedState.menuState = new Bundle();
-                menu.savePresenterStates(savedState.menuState);
-            }
-
-            return savedState;
-        }
-
-        void onRestoreInstanceState(Parcelable state) {
-            SavedState savedState = (SavedState) state;
-            featureId = savedState.featureId;
-            wasLastOpen = savedState.isOpen;
-            frozenMenuState = savedState.menuState;
-
-            shownPanelView = null;
-            decorView = null;
-        }
-
-        void applyFrozenState() {
-            if (menu != null && frozenMenuState != null) {
-                menu.restorePresenterStates(frozenMenuState);
-                frozenMenuState = null;
-            }
-        }
-
-        private static class SavedState implements Parcelable {
-            int featureId;
-            boolean isOpen;
-            Bundle menuState;
-
-            SavedState() {
-            }
-
-            @Override
-            public int describeContents() {
-                return 0;
-            }
-
-            @Override
-            public void writeToParcel(Parcel dest, int flags) {
-                dest.writeInt(featureId);
-                dest.writeInt(isOpen ? 1 : 0);
-
-                if (isOpen) {
-                    dest.writeBundle(menuState);
-                }
-            }
-
-            static SavedState readFromParcel(Parcel source, ClassLoader loader) {
-                SavedState savedState = new SavedState();
-                savedState.featureId = source.readInt();
-                savedState.isOpen = source.readInt() == 1;
-
-                if (savedState.isOpen) {
-                    savedState.menuState = source.readBundle(loader);
-                }
-
-                return savedState;
-            }
-
-            public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
-                @Override
-                public SavedState createFromParcel(Parcel in, ClassLoader loader) {
-                    return readFromParcel(in, loader);
-                }
-
-                @Override
-                public SavedState createFromParcel(Parcel in) {
-                    return readFromParcel(in, null);
-                }
-
-                @Override
-                public SavedState[] newArray(int size) {
-                    return new SavedState[size];
-                }
-            };
-        }
-    }
-
-    private class ListMenuDecorView extends ContentFrameLayout {
-        public ListMenuDecorView(Context context) {
-            super(context);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            return AppCompatDelegateImplV9.this.dispatchKeyEvent(event)
-                    || super.dispatchKeyEvent(event);
-        }
-
-        @Override
-        public boolean onInterceptTouchEvent(MotionEvent event) {
-            int action = event.getAction();
-            if (action == MotionEvent.ACTION_DOWN) {
-                int x = (int) event.getX();
-                int y = (int) event.getY();
-                if (isOutOfBounds(x, y)) {
-                    closePanel(Window.FEATURE_OPTIONS_PANEL);
-                    return true;
-                }
-            }
-            return super.onInterceptTouchEvent(event);
-        }
-
-        @Override
-        public void setBackgroundResource(int resid) {
-            setBackgroundDrawable(AppCompatResources.getDrawable(getContext(), resid));
-        }
-
-        private boolean isOutOfBounds(int x, int y) {
-            return x < -5 || y < -5 || x > (getWidth() + 5) || y > (getHeight() + 5);
-        }
-    }
-}
diff --git a/android/support/v7/app/AppCompatDialog.java b/android/support/v7/app/AppCompatDialog.java
deleted file mode 100644
index 076b1ef..0000000
--- a/android/support/v7/app/AppCompatDialog.java
+++ /dev/null
@@ -1,186 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.IdRes;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ActionMode;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Base class for AppCompat themed {@link android.app.Dialog}s.
- */
-public class AppCompatDialog extends Dialog implements AppCompatCallback {
-
-    private AppCompatDelegate mDelegate;
-
-    public AppCompatDialog(Context context) {
-        this(context, 0);
-    }
-
-    public AppCompatDialog(Context context, int theme) {
-        super(context, getThemeResId(context, theme));
-
-        // This is a bit weird, but Dialog's are typically created and setup before being shown,
-        // which means that we can't rely on onCreate() being called before a content view is set.
-        // To workaround this, we call onCreate(null) in the ctor, and then again as usual in
-        // onCreate().
-        getDelegate().onCreate(null);
-
-        // Apply AppCompat's DayNight resources if needed
-        getDelegate().applyDayNight();
-    }
-
-    protected AppCompatDialog(Context context, boolean cancelable,
-            OnCancelListener cancelListener) {
-        super(context, cancelable, cancelListener);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        getDelegate().installViewFactory();
-        super.onCreate(savedInstanceState);
-        getDelegate().onCreate(savedInstanceState);
-    }
-
-    /**
-     * Support library version of {@link android.app.Dialog#getActionBar}.
-     *
-     * <p>Retrieve a reference to this dialog's ActionBar.
-     *
-     * @return The Dialog's ActionBar, or null if it does not have one.
-     */
-    public ActionBar getSupportActionBar() {
-        return getDelegate().getSupportActionBar();
-    }
-
-    @Override
-    public void setContentView(@LayoutRes int layoutResID) {
-        getDelegate().setContentView(layoutResID);
-    }
-
-    @Override
-    public void setContentView(View view) {
-        getDelegate().setContentView(view);
-    }
-
-    @Override
-    public void setContentView(View view, ViewGroup.LayoutParams params) {
-        getDelegate().setContentView(view, params);
-    }
-
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    @Nullable
-    @Override
-    public <T extends View> T findViewById(@IdRes int id) {
-        return getDelegate().findViewById(id);
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        super.setTitle(title);
-        getDelegate().setTitle(title);
-    }
-
-    @Override
-    public void setTitle(int titleId) {
-        super.setTitle(titleId);
-        getDelegate().setTitle(getContext().getString(titleId));
-    }
-
-    @Override
-    public void addContentView(View view, ViewGroup.LayoutParams params) {
-        getDelegate().addContentView(view, params);
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        getDelegate().onStop();
-    }
-
-    /**
-     * Enable extended support library window features.
-     * <p>
-     * This is a convenience for calling
-     * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
-     * </p>
-     *
-     * @param featureId The desired feature as defined in {@link android.view.Window} or
-     *                  {@link android.support.v4.view.WindowCompat}.
-     * @return Returns true if the requested feature is supported and now enabled.
-     *
-     * @see android.app.Dialog#requestWindowFeature
-     * @see android.view.Window#requestFeature
-     */
-    public boolean supportRequestWindowFeature(int featureId) {
-        return getDelegate().requestWindowFeature(featureId);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    @RestrictTo(LIBRARY_GROUP)
-    public void invalidateOptionsMenu() {
-        getDelegate().invalidateOptionsMenu();
-    }
-
-    /**
-     * @return The {@link AppCompatDelegate} being used by this Dialog.
-     */
-    public AppCompatDelegate getDelegate() {
-        if (mDelegate == null) {
-            mDelegate = AppCompatDelegate.create(this, this);
-        }
-        return mDelegate;
-    }
-
-    private static int getThemeResId(Context context, int themeId) {
-        if (themeId == 0) {
-            // If the provided theme is 0, then retrieve the dialogTheme from our theme
-            TypedValue outValue = new TypedValue();
-            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
-            themeId = outValue.resourceId;
-        }
-        return themeId;
-    }
-
-    @Override
-    public void onSupportActionModeStarted(ActionMode mode) {
-    }
-
-    @Override
-    public void onSupportActionModeFinished(ActionMode mode) {
-    }
-
-    @Nullable
-    @Override
-    public ActionMode onWindowStartingSupportActionMode(ActionMode.Callback callback) {
-        return null;
-    }
-}
diff --git a/android/support/v7/app/AppCompatDialogFragment.java b/android/support/v7/app/AppCompatDialogFragment.java
deleted file mode 100644
index bdf222a..0000000
--- a/android/support/v7/app/AppCompatDialogFragment.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.v7.app;
-
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.DialogFragment;
-import android.view.Window;
-import android.view.WindowManager;
-
-/**
- * A special version of {@link DialogFragment} which uses an {@link AppCompatDialog} in place of a
- * platform-styled dialog.
- *
- * @see DialogFragment
- */
-public class AppCompatDialogFragment extends DialogFragment {
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        return new AppCompatDialog(getContext(), getTheme());
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setupDialog(Dialog dialog, int style) {
-        if (dialog instanceof AppCompatDialog) {
-            // If the dialog is an AppCompatDialog, we'll handle it
-            AppCompatDialog acd = (AppCompatDialog) dialog;
-            switch (style) {
-                case STYLE_NO_INPUT:
-                    dialog.getWindow().addFlags(
-                            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
-                                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
-                    // fall through...
-                case STYLE_NO_FRAME:
-                case STYLE_NO_TITLE:
-                    acd.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
-            }
-        } else {
-            // Else, just let super handle it
-            super.setupDialog(dialog, style);
-        }
-    }
-
-}
diff --git a/android/support/v7/app/AppCompatViewInflater.java b/android/support/v7/app/AppCompatViewInflater.java
deleted file mode 100644
index 87a1a3c..0000000
--- a/android/support/v7/app/AppCompatViewInflater.java
+++ /dev/null
@@ -1,426 +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.app;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ContextThemeWrapper;
-import android.support.v7.widget.AppCompatAutoCompleteTextView;
-import android.support.v7.widget.AppCompatButton;
-import android.support.v7.widget.AppCompatCheckBox;
-import android.support.v7.widget.AppCompatCheckedTextView;
-import android.support.v7.widget.AppCompatEditText;
-import android.support.v7.widget.AppCompatImageButton;
-import android.support.v7.widget.AppCompatImageView;
-import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
-import android.support.v7.widget.AppCompatRadioButton;
-import android.support.v7.widget.AppCompatRatingBar;
-import android.support.v7.widget.AppCompatSeekBar;
-import android.support.v7.widget.AppCompatSpinner;
-import android.support.v7.widget.AppCompatTextView;
-import android.support.v7.widget.TintContextWrapper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.InflateException;
-import android.view.View;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Map;
-
-/**
- * This class is responsible for manually inflating our tinted widgets.
- * <p>This class two main responsibilities: the first is to 'inject' our tinted views in place of
- * the framework versions in layout inflation; the second is backport the {@code android:theme}
- * functionality for any inflated widgets. This include theme inheritance from its parent.
- */
-public class AppCompatViewInflater {
-
-    private static final Class<?>[] sConstructorSignature = new Class[]{
-            Context.class, AttributeSet.class};
-    private static final int[] sOnClickAttrs = new int[]{android.R.attr.onClick};
-
-    private static final String[] sClassPrefixList = {
-            "android.widget.",
-            "android.view.",
-            "android.webkit."
-    };
-
-    private static final String LOG_TAG = "AppCompatViewInflater";
-
-    private static final Map<String, Constructor<? extends View>> sConstructorMap
-            = new ArrayMap<>();
-
-    private final Object[] mConstructorArgs = new Object[2];
-
-    final View createView(View parent, final String name, @NonNull Context context,
-            @NonNull AttributeSet attrs, boolean inheritContext,
-            boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
-        final Context originalContext = context;
-
-        // We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
-        // by using the parent's context
-        if (inheritContext && parent != null) {
-            context = parent.getContext();
-        }
-        if (readAndroidTheme || readAppTheme) {
-            // We then apply the theme on the context, if specified
-            context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
-        }
-        if (wrapContext) {
-            context = TintContextWrapper.wrap(context);
-        }
-
-        View view = null;
-
-        // We need to 'inject' our tint aware Views in place of the standard framework versions
-        switch (name) {
-            case "TextView":
-                view = createTextView(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "ImageView":
-                view = createImageView(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "Button":
-                view = createButton(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "EditText":
-                view = createEditText(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "Spinner":
-                view = createSpinner(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "ImageButton":
-                view = createImageButton(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "CheckBox":
-                view = createCheckBox(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "RadioButton":
-                view = createRadioButton(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "CheckedTextView":
-                view = createCheckedTextView(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "AutoCompleteTextView":
-                view = createAutoCompleteTextView(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "MultiAutoCompleteTextView":
-                view = createMultiAutoCompleteTextView(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "RatingBar":
-                view = createRatingBar(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            case "SeekBar":
-                view = createSeekBar(context, attrs);
-                verifyNotNull(view, name);
-                break;
-            default:
-                // The fallback that allows extending class to take over view inflation
-                // for other tags. Note that we don't check that the result is not-null.
-                // That allows the custom inflater path to fall back on the default one
-                // later in this method.
-                view = createView(context, name, attrs);
-        }
-
-        if (view == null && originalContext != context) {
-            // If the original context does not equal our themed context, then we need to manually
-            // inflate it using the name so that android:theme takes effect.
-            view = createViewFromTag(context, name, attrs);
-        }
-
-        if (view != null) {
-            // If we have created a view, check its android:onClick
-            checkOnClickListener(view, attrs);
-        }
-
-        return view;
-    }
-
-    @NonNull
-    protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
-        return new AppCompatTextView(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatImageView createImageView(Context context, AttributeSet attrs) {
-        return new AppCompatImageView(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatButton createButton(Context context, AttributeSet attrs) {
-        return new AppCompatButton(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatEditText createEditText(Context context, AttributeSet attrs) {
-        return new AppCompatEditText(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatSpinner createSpinner(Context context, AttributeSet attrs) {
-        return new AppCompatSpinner(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatImageButton createImageButton(Context context, AttributeSet attrs) {
-        return new AppCompatImageButton(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatCheckBox createCheckBox(Context context, AttributeSet attrs) {
-        return new AppCompatCheckBox(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatRadioButton createRadioButton(Context context, AttributeSet attrs) {
-        return new AppCompatRadioButton(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatCheckedTextView createCheckedTextView(Context context, AttributeSet attrs) {
-        return new AppCompatCheckedTextView(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatAutoCompleteTextView createAutoCompleteTextView(Context context,
-            AttributeSet attrs) {
-        return new AppCompatAutoCompleteTextView(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(Context context,
-            AttributeSet attrs) {
-        return new AppCompatMultiAutoCompleteTextView(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatRatingBar createRatingBar(Context context, AttributeSet attrs) {
-        return new AppCompatRatingBar(context, attrs);
-    }
-
-    @NonNull
-    protected AppCompatSeekBar createSeekBar(Context context, AttributeSet attrs) {
-        return new AppCompatSeekBar(context, attrs);
-    }
-
-    private void verifyNotNull(View view, String name) {
-        if (view == null) {
-            throw new IllegalStateException(this.getClass().getName()
-                    + " asked to inflate view for <" + name + ">, but returned null");
-        }
-    }
-
-    @Nullable
-    protected View createView(Context context, String name, AttributeSet attrs) {
-        return null;
-    }
-
-    private View createViewFromTag(Context context, String name, AttributeSet attrs) {
-        if (name.equals("view")) {
-            name = attrs.getAttributeValue(null, "class");
-        }
-
-        try {
-            mConstructorArgs[0] = context;
-            mConstructorArgs[1] = attrs;
-
-            if (-1 == name.indexOf('.')) {
-                for (int i = 0; i < sClassPrefixList.length; i++) {
-                    final View view = createViewByPrefix(context, name, sClassPrefixList[i]);
-                    if (view != null) {
-                        return view;
-                    }
-                }
-                return null;
-            } else {
-                return createViewByPrefix(context, name, null);
-            }
-        } catch (Exception e) {
-            // We do not want to catch these, lets return null and let the actual LayoutInflater
-            // try
-            return null;
-        } finally {
-            // Don't retain references on context.
-            mConstructorArgs[0] = null;
-            mConstructorArgs[1] = null;
-        }
-    }
-
-    /**
-     * android:onClick doesn't handle views with a ContextWrapper context. This method
-     * backports new framework functionality to traverse the Context wrappers to find a
-     * suitable target.
-     */
-    private void checkOnClickListener(View view, AttributeSet attrs) {
-        final Context context = view.getContext();
-
-        if (!(context instanceof ContextWrapper) ||
-                (Build.VERSION.SDK_INT >= 15 && !ViewCompat.hasOnClickListeners(view))) {
-            // Skip our compat functionality if: the Context isn't a ContextWrapper, or
-            // the view doesn't have an OnClickListener (we can only rely on this on API 15+ so
-            // always use our compat code on older devices)
-            return;
-        }
-
-        final TypedArray a = context.obtainStyledAttributes(attrs, sOnClickAttrs);
-        final String handlerName = a.getString(0);
-        if (handlerName != null) {
-            view.setOnClickListener(new DeclaredOnClickListener(view, handlerName));
-        }
-        a.recycle();
-    }
-
-    private View createViewByPrefix(Context context, String name, String prefix)
-            throws ClassNotFoundException, InflateException {
-        Constructor<? extends View> constructor = sConstructorMap.get(name);
-
-        try {
-            if (constructor == null) {
-                // Class not found in the cache, see if it's real, and try to add it
-                Class<? extends View> clazz = context.getClassLoader().loadClass(
-                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
-
-                constructor = clazz.getConstructor(sConstructorSignature);
-                sConstructorMap.put(name, constructor);
-            }
-            constructor.setAccessible(true);
-            return constructor.newInstance(mConstructorArgs);
-        } catch (Exception e) {
-            // We do not want to catch these, lets return null and let the actual LayoutInflater
-            // try
-            return null;
-        }
-    }
-
-    /**
-     * Allows us to emulate the {@code android:theme} attribute for devices before L.
-     */
-    private static Context themifyContext(Context context, AttributeSet attrs,
-            boolean useAndroidTheme, boolean useAppTheme) {
-        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.View, 0, 0);
-        int themeId = 0;
-        if (useAndroidTheme) {
-            // First try reading android:theme if enabled
-            themeId = a.getResourceId(R.styleable.View_android_theme, 0);
-        }
-        if (useAppTheme && themeId == 0) {
-            // ...if that didn't work, try reading app:theme (for legacy reasons) if enabled
-            themeId = a.getResourceId(R.styleable.View_theme, 0);
-
-            if (themeId != 0) {
-                Log.i(LOG_TAG, "app:theme is now deprecated. "
-                        + "Please move to using android:theme instead.");
-            }
-        }
-        a.recycle();
-
-        if (themeId != 0 && (!(context instanceof ContextThemeWrapper)
-                || ((ContextThemeWrapper) context).getThemeResId() != themeId)) {
-            // If the context isn't a ContextThemeWrapper, or it is but does not have
-            // the same theme as we need, wrap it in a new wrapper
-            context = new ContextThemeWrapper(context, themeId);
-        }
-        return context;
-    }
-
-    /**
-     * An implementation of OnClickListener that attempts to lazily load a
-     * named click handling method from a parent or ancestor context.
-     */
-    private static class DeclaredOnClickListener implements View.OnClickListener {
-        private final View mHostView;
-        private final String mMethodName;
-
-        private Method mResolvedMethod;
-        private Context mResolvedContext;
-
-        public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
-            mHostView = hostView;
-            mMethodName = methodName;
-        }
-
-        @Override
-        public void onClick(@NonNull View v) {
-            if (mResolvedMethod == null) {
-                resolveMethod(mHostView.getContext(), mMethodName);
-            }
-
-            try {
-                mResolvedMethod.invoke(mResolvedContext, v);
-            } catch (IllegalAccessException e) {
-                throw new IllegalStateException(
-                        "Could not execute non-public method for android:onClick", e);
-            } catch (InvocationTargetException e) {
-                throw new IllegalStateException(
-                        "Could not execute method for android:onClick", e);
-            }
-        }
-
-        @NonNull
-        private void resolveMethod(@Nullable Context context, @NonNull String name) {
-            while (context != null) {
-                try {
-                    if (!context.isRestricted()) {
-                        final Method method = context.getClass().getMethod(mMethodName, View.class);
-                        if (method != null) {
-                            mResolvedMethod = method;
-                            mResolvedContext = context;
-                            return;
-                        }
-                    }
-                } catch (NoSuchMethodException e) {
-                    // Failed to find method, keep searching up the hierarchy.
-                }
-
-                if (context instanceof ContextWrapper) {
-                    context = ((ContextWrapper) context).getBaseContext();
-                } else {
-                    // Can't search up the hierarchy, null out and fail.
-                    context = null;
-                }
-            }
-
-            final int id = mHostView.getId();
-            final String idText = id == View.NO_ID ? "" : " with id '"
-                    + mHostView.getContext().getResources().getResourceEntryName(id) + "'";
-            throw new IllegalStateException("Could not find method " + mMethodName
-                    + "(View) in a parent or ancestor Context for android:onClick "
-                    + "attribute defined on view " + mHostView.getClass() + idText);
-        }
-    }
-}
diff --git a/android/support/v7/app/MediaRouteActionProvider.java b/android/support/v7/app/MediaRouteActionProvider.java
deleted file mode 100644
index 4eb98aa..0000000
--- a/android/support/v7/app/MediaRouteActionProvider.java
+++ /dev/null
@@ -1,332 +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.v7.app;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ActionProvider;
-import android.support.v7.media.MediaRouteSelector;
-import android.support.v7.media.MediaRouter;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.lang.ref.WeakReference;
-
-/**
- * The media route action provider displays a {@link MediaRouteButton media route button}
- * in the application's {@link ActionBar} to allow the user to select routes and
- * to control the currently selected route.
- * <p>
- * The application must specify the kinds of routes that the user should be allowed
- * to select by specifying a {@link MediaRouteSelector selector} with the
- * {@link #setRouteSelector} method.
- * </p><p>
- * Refer to {@link MediaRouteButton} for a description of the button that will
- * appear in the action bar menu.  Note that instead of disabling the button
- * when no routes are available, the action provider will instead make the
- * menu item invisible.  In this way, the button will only be visible when it
- * is possible for the user to discover and select a matching route.
- * </p>
- *
- * <h3>Prerequisites</h3>
- * <p>
- * To use the media route action provider, the activity must be a subclass of
- * {@link AppCompatActivity} from the <code>android.support.v7.appcompat</code>
- * support library.  Refer to support library documentation for details.
- * </p>
- *
- * <h3>Example</h3>
- * <p>
- * </p><p>
- * The application should define a menu resource to include the provider in the
- * action bar options menu.  Note that the support library action bar uses attributes
- * that are defined in the application's resource namespace rather than the framework's
- * resource namespace to configure each item.
- * </p><pre>
- * &lt;menu xmlns:android="http://schemas.android.com/apk/res/android"
- *         xmlns:app="http://schemas.android.com/apk/res-auto">
- *     &lt;item android:id="@+id/media_route_menu_item"
- *         android:title="@string/media_route_menu_title"
- *         app:showAsAction="always"
- *         app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/>
- * &lt;/menu>
- * </pre><p>
- * Then configure the menu and set the route selector for the chooser.
- * </p><pre>
- * public class MyActivity extends AppCompatActivity {
- *     private MediaRouter mRouter;
- *     private MediaRouter.Callback mCallback;
- *     private MediaRouteSelector mSelector;
- *
- *     protected void onCreate(Bundle savedInstanceState) {
- *         super.onCreate(savedInstanceState);
- *
- *         mRouter = Mediarouter.getInstance(this);
- *         mSelector = new MediaRouteSelector.Builder()
- *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
- *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
- *                 .build();
- *         mCallback = new MyCallback();
- *     }
- *
- *     // Add the callback on start to tell the media router what kinds of routes
- *     // the application is interested in so that it can try to discover suitable ones.
- *     public void onStart() {
- *         super.onStart();
- *
- *         mediaRouter.addCallback(mSelector, mCallback,
- *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
- *
- *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
- *         // do something with the route...
- *     }
- *
- *     // Remove the selector on stop to tell the media router that it no longer
- *     // needs to invest effort trying to discover routes of these kinds for now.
- *     public void onStop() {
- *         super.onStop();
- *
- *         mediaRouter.removeCallback(mCallback);
- *     }
- *
- *     public boolean onCreateOptionsMenu(Menu menu) {
- *         super.onCreateOptionsMenu(menu);
- *
- *         getMenuInflater().inflate(R.menu.sample_media_router_menu, menu);
- *
- *         MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
- *         MediaRouteActionProvider mediaRouteActionProvider =
- *                 (MediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem);
- *         mediaRouteActionProvider.setRouteSelector(mSelector);
- *         return true;
- *     }
- *
- *     private final class MyCallback extends MediaRouter.Callback {
- *         // Implement callback methods as needed.
- *     }
- * }
- * </pre>
- *
- * @see #setRouteSelector
- */
-public class MediaRouteActionProvider extends ActionProvider {
-    private static final String TAG = "MediaRouteActionProvider";
-
-    private final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-
-    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
-    private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
-    private MediaRouteButton mButton;
-
-    /**
-     * Creates the action provider.
-     *
-     * @param context The context.
-     */
-    public MediaRouteActionProvider(Context context) {
-        super(context);
-
-        mRouter = MediaRouter.getInstance(context);
-        mCallback = new MediaRouterCallback(this);
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @return The selector, never null.
-     */
-    @NonNull
-    public MediaRouteSelector getRouteSelector() {
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @param selector The selector, must not be null.
-     */
-    public void setRouteSelector(@NonNull MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        if (!mSelector.equals(selector)) {
-            // FIXME: We currently have no way of knowing whether the action provider
-            // is still needed by the UI.  Unfortunately this means the action provider
-            // may leak callbacks until garbage collection occurs.  This may result in
-            // media route providers doing more work than necessary in the short term
-            // while trying to discover routes that are no longer of interest to the
-            // application.  To solve this problem, the action provider will need some
-            // indication from the framework that it is being destroyed.
-            if (!mSelector.isEmpty()) {
-                mRouter.removeCallback(mCallback);
-            }
-            if (!selector.isEmpty()) {
-                mRouter.addCallback(selector, mCallback);
-            }
-            mSelector = selector;
-            refreshRoute();
-
-            if (mButton != null) {
-                mButton.setRouteSelector(selector);
-            }
-        }
-    }
-
-    /**
-     * Gets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @return The dialog factory, never null.
-     */
-    @NonNull
-    public MediaRouteDialogFactory getDialogFactory() {
-        return mDialogFactory;
-    }
-
-    /**
-     * Sets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @param factory The dialog factory, must not be null.
-     */
-    public void setDialogFactory(@NonNull MediaRouteDialogFactory factory) {
-        if (factory == null) {
-            throw new IllegalArgumentException("factory must not be null");
-        }
-
-        if (mDialogFactory != factory) {
-            mDialogFactory = factory;
-
-            if (mButton != null) {
-                mButton.setDialogFactory(factory);
-            }
-        }
-    }
-
-    /**
-     * Gets the associated media route button, or null if it has not yet been created.
-     */
-    @Nullable
-    public MediaRouteButton getMediaRouteButton() {
-        return mButton;
-    }
-
-    /**
-     * Called when the media route button is being created.
-     * <p>
-     * Subclasses may override this method to customize the button.
-     * </p>
-     */
-    public MediaRouteButton onCreateMediaRouteButton() {
-        return new MediaRouteButton(getContext());
-    }
-
-    @Override
-    @SuppressWarnings("deprecation")
-    public View onCreateActionView() {
-        if (mButton != null) {
-            Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
-                    "with a menu item. Don't reuse MediaRouteActionProvider instances! " +
-                    "Abandoning the old menu item...");
-        }
-
-        mButton = onCreateMediaRouteButton();
-        mButton.setCheatSheetEnabled(true);
-        mButton.setRouteSelector(mSelector);
-        mButton.setDialogFactory(mDialogFactory);
-        mButton.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-        return mButton;
-    }
-
-    @Override
-    public boolean onPerformDefaultAction() {
-        if (mButton != null) {
-            return mButton.showDialog();
-        }
-        return false;
-    }
-
-    @Override
-    public boolean overridesItemVisibility() {
-        return true;
-    }
-
-    @Override
-    public boolean isVisible() {
-        return mRouter.isRouteAvailable(mSelector,
-                MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
-    }
-
-    void refreshRoute() {
-        refreshVisibility();
-    }
-
-    private static final class MediaRouterCallback extends MediaRouter.Callback {
-        private final WeakReference<MediaRouteActionProvider> mProviderWeak;
-
-        public MediaRouterCallback(MediaRouteActionProvider provider) {
-            mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute(router);
-        }
-
-        @Override
-        public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute(router);
-        }
-
-        private void refreshRoute(MediaRouter router) {
-            MediaRouteActionProvider provider = mProviderWeak.get();
-            if (provider != null) {
-                provider.refreshRoute();
-            } else {
-                router.removeCallback(this);
-            }
-        }
-    }
-}
diff --git a/android/support/v7/app/MediaRouteButton.java b/android/support/v7/app/MediaRouteButton.java
deleted file mode 100644
index fdbcf9a..0000000
--- a/android/support/v7/app/MediaRouteButton.java
+++ /dev/null
@@ -1,613 +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.v7.app;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-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.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.util.SparseArray;
-import android.view.SoundEffectConstants;
-import android.view.View;
-
-/**
- * The media route button allows the user to select routes and to control the
- * currently selected route.
- * <p>
- * The application must specify the kinds of routes that the user should be allowed
- * to select by specifying a {@link MediaRouteSelector selector} with the
- * {@link #setRouteSelector} method.
- * </p><p>
- * When the default route is selected or when the currently selected route does not
- * match the {@link #getRouteSelector() selector}, the button will appear in
- * an inactive state indicating that the application is not connected to a
- * route of the kind that it wants to use.  Clicking on the button opens
- * a {@link MediaRouteChooserDialog} to allow the user to select a route.
- * If no non-default routes match the selector and it is not possible for an active
- * scan to discover any matching routes, then the button is disabled and cannot
- * be clicked.
- * </p><p>
- * When a non-default route is selected that matches the selector, the button will
- * appear in an active state indicating that the application is connected
- * to a route of the kind that it wants to use.  The button may also appear
- * in an intermediary connecting state if the route is in the process of connecting
- * to the destination but has not yet completed doing so.  In either case, clicking
- * on the button opens a {@link MediaRouteControllerDialog} to allow the user
- * to control or disconnect from the current route.
- * </p>
- *
- * <h3>Prerequisites</h3>
- * <p>
- * To use the media route button, the activity must be a subclass of
- * {@link FragmentActivity} from the <code>android.support.v4</code>
- * support library.  Refer to support library documentation for details.
- * </p>
- *
- * @see MediaRouteActionProvider
- * @see #setRouteSelector
- */
-public class MediaRouteButton extends View {
-    private static final String TAG = "MediaRouteButton";
-
-    private static final String CHOOSER_FRAGMENT_TAG =
-            "android.support.v7.mediarouter:MediaRouteChooserDialogFragment";
-    private static final String CONTROLLER_FRAGMENT_TAG =
-            "android.support.v7.mediarouter:MediaRouteControllerDialogFragment";
-
-    private final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-
-    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
-    private MediaRouteDialogFactory mDialogFactory = MediaRouteDialogFactory.getDefault();
-
-    private boolean mAttachedToWindow;
-
-    private static final SparseArray<Drawable.ConstantState> sRemoteIndicatorCache =
-            new SparseArray<>(2);
-    private RemoteIndicatorLoader mRemoteIndicatorLoader;
-    private Drawable mRemoteIndicator;
-    private boolean mRemoteActive;
-    private boolean mIsConnecting;
-
-    private ColorStateList mButtonTint;
-    private int mMinWidth;
-    private int mMinHeight;
-
-    // The checked state is used when connected to a remote route.
-    private static final int[] CHECKED_STATE_SET = {
-        android.R.attr.state_checked
-    };
-
-    // The checkable state is used while connecting to a remote route.
-    private static final int[] CHECKABLE_STATE_SET = {
-        android.R.attr.state_checkable
-    };
-
-    public MediaRouteButton(Context context) {
-        this(context, null);
-    }
-
-    public MediaRouteButton(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.mediaRouteButtonStyle);
-    }
-
-    public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(MediaRouterThemeHelper.createThemedButtonContext(context), attrs, defStyleAttr);
-        context = getContext();
-
-        mRouter = MediaRouter.getInstance(context);
-        mCallback = new MediaRouterCallback();
-
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.MediaRouteButton, defStyleAttr, 0);
-        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);
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @return The selector, never null.
-     */
-    @NonNull
-    public MediaRouteSelector getRouteSelector() {
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can
-     * select using the media route chooser dialog.
-     *
-     * @param selector The selector, must not be null.
-     */
-    public void setRouteSelector(MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        if (!mSelector.equals(selector)) {
-            if (mAttachedToWindow) {
-                if (!mSelector.isEmpty()) {
-                    mRouter.removeCallback(mCallback);
-                }
-                if (!selector.isEmpty()) {
-                    mRouter.addCallback(selector, mCallback);
-                }
-            }
-            mSelector = selector;
-            refreshRoute();
-        }
-    }
-
-    /**
-     * Gets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @return The dialog factory, never null.
-     */
-    @NonNull
-    public MediaRouteDialogFactory getDialogFactory() {
-        return mDialogFactory;
-    }
-
-    /**
-     * Sets the media route dialog factory to use when showing the route chooser
-     * or controller dialog.
-     *
-     * @param factory The dialog factory, must not be null.
-     */
-    public void setDialogFactory(@NonNull MediaRouteDialogFactory factory) {
-        if (factory == null) {
-            throw new IllegalArgumentException("factory must not be null");
-        }
-
-        mDialogFactory = factory;
-    }
-
-    /**
-     * Show the route chooser or controller dialog.
-     * <p>
-     * If the default route is selected or if the currently selected route does
-     * not match the {@link #getRouteSelector selector}, then shows the route chooser dialog.
-     * Otherwise, shows the route controller dialog to offer the user
-     * a choice to disconnect from the route or perform other control actions
-     * such as setting the route's volume.
-     * </p><p>
-     * The application can customize the dialogs by calling {@link #setDialogFactory}
-     * to provide a customized dialog factory.
-     * </p>
-     *
-     * @return True if the dialog was actually shown.
-     *
-     * @throws IllegalStateException if the activity is not a subclass of
-     * {@link FragmentActivity}.
-     */
-    public boolean showDialog() {
-        if (!mAttachedToWindow) {
-            return false;
-        }
-
-        final FragmentManager fm = getFragmentManager();
-        if (fm == null) {
-            throw new IllegalStateException("The activity must be a subclass of FragmentActivity");
-        }
-
-        MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
-        if (route.isDefaultOrBluetooth() || !route.matchesSelector(mSelector)) {
-            if (fm.findFragmentByTag(CHOOSER_FRAGMENT_TAG) != null) {
-                Log.w(TAG, "showDialog(): Route chooser dialog already showing!");
-                return false;
-            }
-            MediaRouteChooserDialogFragment f =
-                    mDialogFactory.onCreateChooserDialogFragment();
-            f.setRouteSelector(mSelector);
-            f.show(fm, CHOOSER_FRAGMENT_TAG);
-        } else {
-            if (fm.findFragmentByTag(CONTROLLER_FRAGMENT_TAG) != null) {
-                Log.w(TAG, "showDialog(): Route controller dialog already showing!");
-                return false;
-            }
-            MediaRouteControllerDialogFragment f =
-                    mDialogFactory.onCreateControllerDialogFragment();
-            f.show(fm, CONTROLLER_FRAGMENT_TAG);
-        }
-        return true;
-    }
-
-    private FragmentManager getFragmentManager() {
-        Activity activity = getActivity();
-        if (activity instanceof FragmentActivity) {
-            return ((FragmentActivity)activity).getSupportFragmentManager();
-        }
-        return null;
-    }
-
-    private Activity getActivity() {
-        // Gross way of unwrapping the Activity so we can get the FragmentManager
-        Context context = getContext();
-        while (context instanceof ContextWrapper) {
-            if (context instanceof Activity) {
-                return (Activity)context;
-            }
-            context = ((ContextWrapper)context).getBaseContext();
-        }
-        return null;
-    }
-
-    /**
-     * Sets whether to enable showing a toast with the content descriptor of the
-     * button when the button is long pressed.
-     */
-    void setCheatSheetEnabled(boolean enable) {
-        TooltipCompat.setTooltipText(this,
-                enable ? getContext().getString(R.string.mr_button_content_description) : null);
-    }
-
-    @Override
-    public boolean performClick() {
-        // Send the appropriate accessibility events and call listeners
-        boolean handled = super.performClick();
-        if (!handled) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-        }
-        return showDialog() || handled;
-    }
-
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
-
-        // Technically we should be handling this more completely, but these
-        // are implementation details here. Checkable is used to express the connecting
-        // drawable state and it's mutually exclusive with check for the purposes
-        // of state selection here.
-        if (mIsConnecting) {
-            mergeDrawableStates(drawableState, CHECKABLE_STATE_SET);
-        } else if (mRemoteActive) {
-            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
-        }
-        return drawableState;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        if (mRemoteIndicator != null) {
-            int[] myDrawableState = getDrawableState();
-            mRemoteIndicator.setState(myDrawableState);
-            invalidate();
-        }
-    }
-
-    /**
-     * 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 (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
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mRemoteIndicator;
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        // We can't call super to handle the background so we do it ourselves.
-        //super.jumpDrawablesToCurrentState();
-        if (getBackground() != null) {
-            DrawableCompat.jumpToCurrentState(getBackground());
-        }
-
-        // Handle our own remote indicator.
-        if (mRemoteIndicator != null) {
-            DrawableCompat.jumpToCurrentState(mRemoteIndicator);
-        }
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-
-        if (mRemoteIndicator != null) {
-            mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
-        }
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mAttachedToWindow = true;
-        if (!mSelector.isEmpty()) {
-            mRouter.addCallback(mSelector, mCallback);
-        }
-        refreshRoute();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mAttachedToWindow = false;
-        if (!mSelector.isEmpty()) {
-            mRouter.removeCallback(mCallback);
-        }
-
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-
-        final int width = Math.max(mMinWidth, mRemoteIndicator != null ?
-                mRemoteIndicator.getIntrinsicWidth() + getPaddingLeft() + getPaddingRight() : 0);
-        final int height = Math.max(mMinHeight, mRemoteIndicator != null ?
-                mRemoteIndicator.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom() : 0);
-
-        int measuredWidth;
-        switch (widthMode) {
-            case MeasureSpec.EXACTLY:
-                measuredWidth = widthSize;
-                break;
-            case MeasureSpec.AT_MOST:
-                measuredWidth = Math.min(widthSize, width);
-                break;
-            default:
-            case MeasureSpec.UNSPECIFIED:
-                measuredWidth = width;
-                break;
-        }
-
-        int measuredHeight;
-        switch (heightMode) {
-            case MeasureSpec.EXACTLY:
-                measuredHeight = heightSize;
-                break;
-            case MeasureSpec.AT_MOST:
-                measuredHeight = Math.min(heightSize, height);
-                break;
-            default:
-            case MeasureSpec.UNSPECIFIED:
-                measuredHeight = height;
-                break;
-        }
-
-        setMeasuredDimension(measuredWidth, measuredHeight);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (mRemoteIndicator != null) {
-            final int left = getPaddingLeft();
-            final int right = getWidth() - getPaddingRight();
-            final int top = getPaddingTop();
-            final int bottom = getHeight() - getPaddingBottom();
-
-            final int drawWidth = mRemoteIndicator.getIntrinsicWidth();
-            final int drawHeight = mRemoteIndicator.getIntrinsicHeight();
-            final int drawLeft = left + (right - left - drawWidth) / 2;
-            final int drawTop = top + (bottom - top - drawHeight) / 2;
-
-            mRemoteIndicator.setBounds(drawLeft, drawTop,
-                    drawLeft + drawWidth, drawTop + drawHeight);
-            mRemoteIndicator.draw(canvas);
-        }
-    }
-
-    void refreshRoute() {
-        final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
-        final boolean isRemote = !route.isDefaultOrBluetooth() && route.matchesSelector(mSelector);
-        final boolean isConnecting = isRemote && route.isConnecting();
-        boolean needsRefresh = false;
-        if (mRemoteActive != isRemote) {
-            mRemoteActive = isRemote;
-            needsRefresh = true;
-        }
-        if (mIsConnecting != isConnecting) {
-            mIsConnecting = isConnecting;
-            needsRefresh = true;
-        }
-
-        if (needsRefresh) {
-            updateContentDescription();
-            refreshDrawableState();
-        }
-        if (mAttachedToWindow) {
-            setEnabled(mRouter.isRouteAvailable(mSelector,
-                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
-        }
-        if (mRemoteIndicator != null
-                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
-            AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
-            if (mAttachedToWindow) {
-                if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) {
-                    curDrawable.start();
-                }
-            } else if (isRemote && !isConnecting) {
-                // When the route is already connected before the view is attached, show the last
-                // frame of the connected animation immediately.
-                if (curDrawable.isRunning()) {
-                    curDrawable.stop();
-                }
-                curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1);
-            }
-        }
-    }
-
-    private void updateContentDescription() {
-        int resId;
-        if (mIsConnecting) {
-            resId = R.string.mr_cast_button_connecting;
-        } else if (mRemoteActive) {
-            resId = R.string.mr_cast_button_connected;
-        } else {
-            resId = R.string.mr_cast_button_disconnected;
-        }
-        setContentDescription(getContext().getString(resId));
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        MediaRouterCallback() {
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            refreshRoute();
-        }
-
-        @Override
-        public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) {
-            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/android/support/v7/app/MediaRouteChooserDialog.java b/android/support/v7/app/MediaRouteChooserDialog.java
deleted file mode 100644
index 17364ef..0000000
--- a/android/support/v7/app/MediaRouteChooserDialog.java
+++ /dev/null
@@ -1,390 +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.v7.app;
-
-import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED;
-import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.support.annotation.NonNull;
-import android.support.v7.media.MediaRouteSelector;
-import android.support.v7.media.MediaRouter;
-import android.support.v7.mediarouter.R;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * This class implements the route chooser dialog for {@link MediaRouter}.
- * <p>
- * This dialog allows the user to choose a route that matches a given selector.
- * </p>
- *
- * @see MediaRouteButton
- * @see MediaRouteActionProvider
- */
-public class MediaRouteChooserDialog extends AppCompatDialog {
-    static final String TAG = "MediaRouteChooserDialog";
-
-    // Do not update the route list immediately to avoid unnatural dialog change.
-    private static final long UPDATE_ROUTES_DELAY_MS = 300L;
-    static final int MSG_UPDATE_ROUTES = 1;
-
-    private final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-
-    private TextView mTitleView;
-    private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
-    private ArrayList<MediaRouter.RouteInfo> mRoutes;
-    private RouteAdapter mAdapter;
-    private ListView mListView;
-    private boolean mAttachedToWindow;
-    private long mLastUpdateTime;
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_UPDATE_ROUTES:
-                    updateRoutes((List<MediaRouter.RouteInfo>) message.obj);
-                    break;
-            }
-        }
-    };
-
-    public MediaRouteChooserDialog(Context context) {
-        this(context, 0);
-    }
-
-    public MediaRouteChooserDialog(Context context, int theme) {
-        super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, false),
-                MediaRouterThemeHelper.createThemedDialogStyle(context));
-        context = getContext();
-
-        mRouter = MediaRouter.getInstance(context);
-        mCallback = new MediaRouterCallback();
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can select.
-     *
-     * @return The selector, never null.
-     */
-    @NonNull
-    public MediaRouteSelector getRouteSelector() {
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can select.
-     *
-     * @param selector The selector, must not be null.
-     */
-    public void setRouteSelector(@NonNull MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        if (!mSelector.equals(selector)) {
-            mSelector = selector;
-
-            if (mAttachedToWindow) {
-                mRouter.removeCallback(mCallback);
-                mRouter.addCallback(selector, mCallback,
-                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            }
-
-            refreshRoutes();
-        }
-    }
-
-    /**
-     * Called to filter the set of routes that should be included in the list.
-     * <p>
-     * The default implementation iterates over all routes in the provided list and
-     * removes those for which {@link #onFilterRoute} returns false.
-     * </p>
-     *
-     * @param routes The list of routes to filter in-place, never null.
-     */
-    public void onFilterRoutes(@NonNull List<MediaRouter.RouteInfo> routes) {
-        for (int i = routes.size(); i-- > 0; ) {
-            if (!onFilterRoute(routes.get(i))) {
-                routes.remove(i);
-            }
-        }
-    }
-
-    /**
-     * Returns true if the route should be included in the list.
-     * <p>
-     * The default implementation returns true for enabled non-default routes that
-     * match the selector.  Subclasses can override this method to filter routes
-     * differently.
-     * </p>
-     *
-     * @param route The route to consider, never null.
-     * @return True if the route should be included in the chooser dialog.
-     */
-    public boolean onFilterRoute(@NonNull MediaRouter.RouteInfo route) {
-        return !route.isDefaultOrBluetooth() && route.isEnabled()
-                && route.matchesSelector(mSelector);
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mTitleView.setText(title);
-    }
-
-    @Override
-    public void setTitle(int titleId) {
-        mTitleView.setText(titleId);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.mr_chooser_dialog);
-
-        mRoutes = new ArrayList<>();
-        mAdapter = new RouteAdapter(getContext(), mRoutes);
-        mListView = (ListView)findViewById(R.id.mr_chooser_list);
-        mListView.setAdapter(mAdapter);
-        mListView.setOnItemClickListener(mAdapter);
-        mListView.setEmptyView(findViewById(android.R.id.empty));
-        mTitleView = findViewById(R.id.mr_chooser_title);
-
-        updateLayout();
-    }
-
-    /**
-     * Sets the width of the dialog. Also called when configuration changes.
-     */
-    void updateLayout() {
-        getWindow().setLayout(MediaRouteDialogHelper.getDialogWidth(getContext()),
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mAttachedToWindow = true;
-        mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-        refreshRoutes();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mAttachedToWindow = false;
-        mRouter.removeCallback(mCallback);
-        mHandler.removeMessages(MSG_UPDATE_ROUTES);
-
-        super.onDetachedFromWindow();
-    }
-
-    /**
-     * Refreshes the list of routes that are shown in the chooser dialog.
-     */
-    public void refreshRoutes() {
-        if (mAttachedToWindow) {
-            ArrayList<MediaRouter.RouteInfo> routes = new ArrayList<>(mRouter.getRoutes());
-            onFilterRoutes(routes);
-            Collections.sort(routes, RouteComparator.sInstance);
-            if (SystemClock.uptimeMillis() - mLastUpdateTime >= UPDATE_ROUTES_DELAY_MS) {
-                updateRoutes(routes);
-            } else {
-                mHandler.removeMessages(MSG_UPDATE_ROUTES);
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ROUTES, routes),
-                        mLastUpdateTime + UPDATE_ROUTES_DELAY_MS);
-            }
-        }
-    }
-
-    void updateRoutes(List<MediaRouter.RouteInfo> routes) {
-        mLastUpdateTime = SystemClock.uptimeMillis();
-        mRoutes.clear();
-        mRoutes.addAll(routes);
-        mAdapter.notifyDataSetChanged();
-    }
-
-    private final class RouteAdapter extends ArrayAdapter<MediaRouter.RouteInfo>
-            implements ListView.OnItemClickListener {
-        private final LayoutInflater mInflater;
-        private final Drawable mDefaultIcon;
-        private final Drawable mTvIcon;
-        private final Drawable mSpeakerIcon;
-        private final Drawable mSpeakerGroupIcon;
-
-        public RouteAdapter(Context context, List<MediaRouter.RouteInfo> routes) {
-            super(context, 0, routes);
-            mInflater = LayoutInflater.from(context);
-            TypedArray styledAttributes = getContext().obtainStyledAttributes(new int[] {
-                    R.attr.mediaRouteDefaultIconDrawable,
-                    R.attr.mediaRouteTvIconDrawable,
-                    R.attr.mediaRouteSpeakerIconDrawable,
-                    R.attr.mediaRouteSpeakerGroupIconDrawable});
-            mDefaultIcon = styledAttributes.getDrawable(0);
-            mTvIcon = styledAttributes.getDrawable(1);
-            mSpeakerIcon = styledAttributes.getDrawable(2);
-            mSpeakerGroupIcon = styledAttributes.getDrawable(3);
-            styledAttributes.recycle();
-        }
-
-        @Override
-        public boolean areAllItemsEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return getItem(position).isEnabled();
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            View view = convertView;
-            if (view == null) {
-                view = mInflater.inflate(R.layout.mr_chooser_list_item, parent, false);
-            }
-
-            MediaRouter.RouteInfo route = getItem(position);
-            TextView text1 = (TextView) view.findViewById(R.id.mr_chooser_route_name);
-            TextView text2 = (TextView) view.findViewById(R.id.mr_chooser_route_desc);
-            text1.setText(route.getName());
-            String description = route.getDescription();
-            boolean isConnectedOrConnecting =
-                    route.getConnectionState() == CONNECTION_STATE_CONNECTED
-                            || route.getConnectionState() == CONNECTION_STATE_CONNECTING;
-            if (isConnectedOrConnecting && !TextUtils.isEmpty(description)) {
-                text1.setGravity(Gravity.BOTTOM);
-                text2.setVisibility(View.VISIBLE);
-                text2.setText(description);
-            } else {
-                text1.setGravity(Gravity.CENTER_VERTICAL);
-                text2.setVisibility(View.GONE);
-                text2.setText("");
-            }
-            view.setEnabled(route.isEnabled());
-
-            ImageView iconView = (ImageView) view.findViewById(R.id.mr_chooser_route_icon);
-            if (iconView != null) {
-                iconView.setImageDrawable(getIconDrawable(route));
-            }
-            return view;
-        }
-
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            MediaRouter.RouteInfo route = getItem(position);
-            if (route.isEnabled()) {
-                route.select();
-                dismiss();
-            }
-        }
-
-        private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
-            Uri iconUri = route.getIconUri();
-            if (iconUri != null) {
-                try {
-                    InputStream is = getContext().getContentResolver().openInputStream(iconUri);
-                    Drawable drawable = Drawable.createFromStream(is, null);
-                    if (drawable != null) {
-                        return drawable;
-                    }
-                } catch (IOException e) {
-                    Log.w(TAG, "Failed to load " + iconUri, e);
-                    // Falls back.
-                }
-            }
-            return getDefaultIconDrawable(route);
-        }
-
-        private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
-            // If the type of the receiver device is specified, use it.
-            switch (route.getDeviceType()) {
-                case  MediaRouter.RouteInfo.DEVICE_TYPE_TV:
-                    return mTvIcon;
-                case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
-                    return mSpeakerIcon;
-            }
-
-            // Otherwise, make the best guess based on other route information.
-            if (route instanceof MediaRouter.RouteGroup) {
-                // Only speakers can be grouped for now.
-                return mSpeakerGroupIcon;
-            }
-            return mDefaultIcon;
-        }
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        MediaRouterCallback() {
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoutes();
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoutes();
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            refreshRoutes();
-        }
-
-        @Override
-        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
-            dismiss();
-        }
-    }
-
-    static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
-        public static final RouteComparator sInstance = new RouteComparator();
-
-        @Override
-        public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
-            return lhs.getName().compareToIgnoreCase(rhs.getName());
-        }
-    }
-}
diff --git a/android/support/v7/app/MediaRouteChooserDialogFragment.java b/android/support/v7/app/MediaRouteChooserDialogFragment.java
deleted file mode 100644
index 0e0268b..0000000
--- a/android/support/v7/app/MediaRouteChooserDialogFragment.java
+++ /dev/null
@@ -1,125 +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.v7.app;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.support.v7.media.MediaRouteSelector;
-
-/**
- * Media route chooser dialog fragment.
- * <p>
- * Creates a {@link MediaRouteChooserDialog}.  The application may subclass
- * this dialog fragment to customize the media route chooser dialog.
- * </p>
- */
-public class MediaRouteChooserDialogFragment extends DialogFragment {
-    private final String ARGUMENT_SELECTOR = "selector";
-
-    private MediaRouteChooserDialog mDialog;
-    private MediaRouteSelector mSelector;
-
-    /**
-     * Creates a media route chooser dialog fragment.
-     * <p>
-     * All subclasses of this class must also possess a default constructor.
-     * </p>
-     */
-    public MediaRouteChooserDialogFragment() {
-        setCancelable(true);
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes that the user can select.
-     *
-     * @return The selector, never null.
-     */
-    public MediaRouteSelector getRouteSelector() {
-        ensureRouteSelector();
-        return mSelector;
-    }
-
-    private void ensureRouteSelector() {
-        if (mSelector == null) {
-            Bundle args = getArguments();
-            if (args != null) {
-                mSelector = MediaRouteSelector.fromBundle(args.getBundle(ARGUMENT_SELECTOR));
-            }
-            if (mSelector == null) {
-                mSelector = MediaRouteSelector.EMPTY;
-            }
-        }
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes that the user can select.
-     * This method must be called before the fragment is added.
-     *
-     * @param selector The selector to set.
-     */
-    public void setRouteSelector(MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        ensureRouteSelector();
-        if (!mSelector.equals(selector)) {
-            mSelector = selector;
-
-            Bundle args = getArguments();
-            if (args == null) {
-                args = new Bundle();
-            }
-            args.putBundle(ARGUMENT_SELECTOR, selector.asBundle());
-            setArguments(args);
-
-            MediaRouteChooserDialog dialog = (MediaRouteChooserDialog)getDialog();
-            if (dialog != null) {
-                dialog.setRouteSelector(selector);
-            }
-        }
-    }
-
-    /**
-     * Called when the chooser dialog is being created.
-     * <p>
-     * Subclasses may override this method to customize the dialog.
-     * </p>
-     */
-    public MediaRouteChooserDialog onCreateChooserDialog(
-            Context context, Bundle savedInstanceState) {
-        return new MediaRouteChooserDialog(context);
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        mDialog = onCreateChooserDialog(getContext(), savedInstanceState);
-        mDialog.setRouteSelector(getRouteSelector());
-        return mDialog;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mDialog != null) {
-            mDialog.updateLayout();
-        }
-    }
-}
diff --git a/android/support/v7/app/MediaRouteControllerDialog.java b/android/support/v7/app/MediaRouteControllerDialog.java
deleted file mode 100644
index d89bf21..0000000
--- a/android/support/v7/app/MediaRouteControllerDialog.java
+++ /dev/null
@@ -1,1478 +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.v7.app;
-
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE;
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY;
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE;
-import static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP;
-
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.support.v4.media.MediaDescriptionCompat;
-import android.support.v4.media.MediaMetadataCompat;
-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;
-import android.support.v7.media.MediaRouteSelector;
-import android.support.v7.media.MediaRouter;
-import android.support.v7.mediarouter.R;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.Transformation;
-import android.view.animation.TranslateAnimation;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class implements the route controller dialog for {@link MediaRouter}.
- * <p>
- * This dialog allows the user to control or disconnect from the currently selected route.
- * </p>
- *
- * @see MediaRouteButton
- * @see MediaRouteActionProvider
- */
-public class MediaRouteControllerDialog extends AlertDialog {
-    // Tags should be less than 24 characters long (see docs for android.util.Log.isLoggable())
-    static final String TAG = "MediaRouteCtrlDialog";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    // Time to wait before updating the volume when the user lets go of the seek bar
-    // to allow the route provider time to propagate the change and publish a new
-    // route descriptor.
-    static final int VOLUME_UPDATE_DELAY_MILLIS = 500;
-    static final int CONNECTION_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(30L);
-
-    private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
-    static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
-    static final int BUTTON_STOP_RES_ID = android.R.id.button1;
-
-    final MediaRouter mRouter;
-    private final MediaRouterCallback mCallback;
-    final MediaRouter.RouteInfo mRoute;
-
-    Context mContext;
-    private boolean mCreated;
-    private boolean mAttachedToWindow;
-
-    private int mDialogContentWidth;
-
-    private View mCustomControlView;
-
-    private Button mDisconnectButton;
-    private Button mStopCastingButton;
-    private ImageButton mPlaybackControlButton;
-    private ImageButton mCloseButton;
-    private MediaRouteExpandCollapseButton mGroupExpandCollapseButton;
-
-    private FrameLayout mExpandableAreaLayout;
-    private LinearLayout mDialogAreaLayout;
-    FrameLayout mDefaultControlLayout;
-    private FrameLayout mCustomControlLayout;
-    private ImageView mArtView;
-    private TextView mTitleView;
-    private TextView mSubtitleView;
-    private TextView mRouteNameTextView;
-
-    private boolean mVolumeControlEnabled = true;
-    // Layout for media controllers including play/pause button and the main volume slider.
-    private LinearLayout mMediaMainControlLayout;
-    private RelativeLayout mPlaybackControlLayout;
-    private LinearLayout mVolumeControlLayout;
-    private View mDividerView;
-
-    OverlayListView mVolumeGroupList;
-    VolumeGroupAdapter mVolumeGroupAdapter;
-    private List<MediaRouter.RouteInfo> mGroupMemberRoutes;
-    Set<MediaRouter.RouteInfo> mGroupMemberRoutesAdded;
-    private Set<MediaRouter.RouteInfo> mGroupMemberRoutesRemoved;
-    Set<MediaRouter.RouteInfo> mGroupMemberRoutesAnimatingWithBitmap;
-    SeekBar mVolumeSlider;
-    VolumeChangeListener mVolumeChangeListener;
-    MediaRouter.RouteInfo mRouteInVolumeSliderTouched;
-    private int mVolumeGroupListItemIconSize;
-    private int mVolumeGroupListItemHeight;
-    private int mVolumeGroupListMaxHeight;
-    private final int mVolumeGroupListPaddingTop;
-    Map<MediaRouter.RouteInfo, SeekBar> mVolumeSliderMap;
-
-    MediaControllerCompat mMediaController;
-    MediaControllerCallback mControllerCallback;
-    PlaybackStateCompat mState;
-    MediaDescriptionCompat mDescription;
-
-    FetchArtTask mFetchArtTask;
-    Bitmap mArtIconBitmap;
-    Uri mArtIconUri;
-    boolean mArtIconIsLoaded;
-    Bitmap mArtIconLoadedBitmap;
-    int mArtIconBackgroundColor;
-
-    boolean mHasPendingUpdate;
-    boolean mPendingUpdateAnimationNeeded;
-
-    boolean mIsGroupExpanded;
-    boolean mIsGroupListAnimating;
-    boolean mIsGroupListAnimationPending;
-    int mGroupListAnimationDurationMs;
-    private int mGroupListFadeInDurationMs;
-    private int mGroupListFadeOutDurationMs;
-
-    private Interpolator mInterpolator;
-    private Interpolator mLinearOutSlowInInterpolator;
-    private Interpolator mFastOutSlowInInterpolator;
-    private Interpolator mAccelerateDecelerateInterpolator;
-
-    final AccessibilityManager mAccessibilityManager;
-
-    Runnable mGroupListFadeInAnimation = new Runnable() {
-        @Override
-        public void run() {
-            startGroupListFadeInAnimation();
-        }
-    };
-
-    public MediaRouteControllerDialog(Context context) {
-        this(context, 0);
-    }
-
-    public MediaRouteControllerDialog(Context context, int theme) {
-        super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, true),
-                MediaRouterThemeHelper.createThemedDialogStyle(context));
-        mContext = getContext();
-
-        mControllerCallback = new MediaControllerCallback();
-        mRouter = MediaRouter.getInstance(mContext);
-        mCallback = new MediaRouterCallback();
-        mRoute = mRouter.getSelectedRoute();
-        setMediaSession(mRouter.getMediaSessionToken());
-        mVolumeGroupListPaddingTop = mContext.getResources().getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_padding_top);
-        mAccessibilityManager =
-                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                    R.interpolator.mr_linear_out_slow_in);
-            mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                    R.interpolator.mr_fast_out_slow_in);
-        }
-        mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
-    }
-
-    /**
-     * Gets the route that this dialog is controlling.
-     */
-    public MediaRouter.RouteInfo getRoute() {
-        return mRoute;
-    }
-
-    private MediaRouter.RouteGroup getGroup() {
-        if (mRoute instanceof MediaRouter.RouteGroup) {
-            return (MediaRouter.RouteGroup) mRoute;
-        }
-        return null;
-    }
-
-    /**
-     * Provides the subclass an opportunity to create a view that will replace the default media
-     * controls for the currently playing content.
-     *
-     * @param savedInstanceState The dialog's saved instance state.
-     * @return The media control view, or null if none.
-     */
-    public View onCreateMediaControlView(Bundle savedInstanceState) {
-        return null;
-    }
-
-    /**
-     * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
-     *
-     * @return The media control view, or null if none.
-     */
-    public View getMediaControlView() {
-        return mCustomControlView;
-    }
-
-    /**
-     * Sets whether to enable the volume slider and volume control using the volume keys
-     * when the route supports it.
-     * <p>
-     * The default value is true.
-     * </p>
-     */
-    public void setVolumeControlEnabled(boolean enable) {
-        if (mVolumeControlEnabled != enable) {
-            mVolumeControlEnabled = enable;
-            if (mCreated) {
-                update(false);
-            }
-        }
-    }
-
-    /**
-     * Returns whether to enable the volume slider and volume control using the volume keys
-     * when the route supports it.
-     */
-    public boolean isVolumeControlEnabled() {
-        return mVolumeControlEnabled;
-    }
-
-    /**
-     * Set the session to use for metadata and transport controls. The dialog
-     * will listen to changes on this session and update the UI automatically in
-     * response to changes.
-     *
-     * @param sessionToken The token for the session to use.
-     */
-    private void setMediaSession(MediaSessionCompat.Token sessionToken) {
-        if (mMediaController != null) {
-            mMediaController.unregisterCallback(mControllerCallback);
-            mMediaController = null;
-        }
-        if (sessionToken == null) {
-            return;
-        }
-        if (!mAttachedToWindow) {
-            return;
-        }
-        try {
-            mMediaController = new MediaControllerCompat(mContext, sessionToken);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error creating media controller in setMediaSession.", e);
-        }
-        if (mMediaController != null) {
-            mMediaController.registerCallback(mControllerCallback);
-        }
-        MediaMetadataCompat metadata = mMediaController == null ? null
-                : mMediaController.getMetadata();
-        mDescription = metadata == null ? null : metadata.getDescription();
-        mState = mMediaController == null ? null : mMediaController.getPlaybackState();
-        updateArtIconIfNeeded();
-        update(false);
-    }
-
-    /**
-     * Gets the session to use for metadata and transport controls.
-     *
-     * @return The token for the session to use or null if none.
-     */
-    public MediaSessionCompat.Token getMediaSession() {
-        return mMediaController == null ? null : mMediaController.getSessionToken();
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        getWindow().setBackgroundDrawableResource(android.R.color.transparent);
-        setContentView(R.layout.mr_controller_material_dialog_b);
-
-        // Remove the neutral button.
-        findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE);
-
-        ClickListener listener = new ClickListener();
-
-        mExpandableAreaLayout = findViewById(R.id.mr_expandable_area);
-        mExpandableAreaLayout.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                dismiss();
-            }
-        });
-        mDialogAreaLayout = findViewById(R.id.mr_dialog_area);
-        mDialogAreaLayout.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Eat unhandled touch events.
-            }
-        });
-        int color = MediaRouterThemeHelper.getButtonTextColor(mContext);
-        mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID);
-        mDisconnectButton.setText(R.string.mr_controller_disconnect);
-        mDisconnectButton.setTextColor(color);
-        mDisconnectButton.setOnClickListener(listener);
-
-        mStopCastingButton = findViewById(BUTTON_STOP_RES_ID);
-        mStopCastingButton.setText(R.string.mr_controller_stop_casting);
-        mStopCastingButton.setTextColor(color);
-        mStopCastingButton.setOnClickListener(listener);
-
-        mRouteNameTextView = findViewById(R.id.mr_name);
-        mCloseButton = findViewById(R.id.mr_close);
-        mCloseButton.setOnClickListener(listener);
-        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() {
-            @Override
-            public void onClick(View v) {
-                if (mMediaController != null) {
-                    PendingIntent pi = mMediaController.getSessionActivity();
-                    if (pi != null) {
-                        try {
-                            pi.send();
-                            dismiss();
-                        } catch (PendingIntent.CanceledException e) {
-                            Log.e(TAG, pi + " was not sent, it had been canceled.");
-                        }
-                    }
-                }
-            }
-        };
-        mArtView = findViewById(R.id.mr_art);
-        mArtView.setOnClickListener(onClickListener);
-        findViewById(R.id.mr_control_title_container).setOnClickListener(onClickListener);
-
-        mMediaMainControlLayout = findViewById(R.id.mr_media_main_control);
-        mDividerView = findViewById(R.id.mr_control_divider);
-
-        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 = findViewById(R.id.mr_volume_control);
-        mVolumeControlLayout.setVisibility(View.GONE);
-        mVolumeSlider = findViewById(R.id.mr_volume_slider);
-        mVolumeSlider.setTag(mRoute);
-        mVolumeChangeListener = new VolumeChangeListener();
-        mVolumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
-
-        mVolumeGroupList = findViewById(R.id.mr_volume_group_list);
-        mGroupMemberRoutes = new ArrayList<MediaRouter.RouteInfo>();
-        mVolumeGroupAdapter = new VolumeGroupAdapter(mVolumeGroupList.getContext(),
-                mGroupMemberRoutes);
-        mVolumeGroupList.setAdapter(mVolumeGroupAdapter);
-        mGroupMemberRoutesAnimatingWithBitmap = new HashSet<>();
-
-        MediaRouterThemeHelper.setMediaControlsBackgroundColor(mContext,
-                mMediaMainControlLayout, mVolumeGroupList, getGroup() != null);
-        MediaRouterThemeHelper.setVolumeSliderColor(mContext,
-                (MediaRouteVolumeSlider) mVolumeSlider, mMediaMainControlLayout);
-        mVolumeSliderMap = new HashMap<>();
-        mVolumeSliderMap.put(mRoute, mVolumeSlider);
-
-        mGroupExpandCollapseButton =
-                findViewById(R.id.mr_group_expand_collapse);
-        mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mIsGroupExpanded = !mIsGroupExpanded;
-                if (mIsGroupExpanded) {
-                    mVolumeGroupList.setVisibility(View.VISIBLE);
-                }
-                loadInterpolator();
-                updateLayoutHeight(true);
-            }
-        });
-        loadInterpolator();
-        mGroupListAnimationDurationMs = mContext.getResources().getInteger(
-                R.integer.mr_controller_volume_group_list_animation_duration_ms);
-        mGroupListFadeInDurationMs = mContext.getResources().getInteger(
-                R.integer.mr_controller_volume_group_list_fade_in_duration_ms);
-        mGroupListFadeOutDurationMs = mContext.getResources().getInteger(
-                R.integer.mr_controller_volume_group_list_fade_out_duration_ms);
-
-        mCustomControlView = onCreateMediaControlView(savedInstanceState);
-        if (mCustomControlView != null) {
-            mCustomControlLayout.addView(mCustomControlView);
-            mCustomControlLayout.setVisibility(View.VISIBLE);
-        }
-        mCreated = true;
-        updateLayout();
-    }
-
-    /**
-     * Sets the width of the dialog. Also called when configuration changes.
-     */
-    void updateLayout() {
-        int width = MediaRouteDialogHelper.getDialogWidth(mContext);
-        getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-        View decorView = getWindow().getDecorView();
-        mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight();
-
-        Resources res = mContext.getResources();
-        mVolumeGroupListItemIconSize = res.getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_item_icon_size);
-        mVolumeGroupListItemHeight = res.getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_item_height);
-        mVolumeGroupListMaxHeight = res.getDimensionPixelSize(
-                R.dimen.mr_controller_volume_group_list_max_height);
-
-        // Fetch art icons again for layout changes to resize it accordingly
-        mArtIconBitmap = null;
-        mArtIconUri = null;
-        updateArtIconIfNeeded();
-        update(false);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mAttachedToWindow = true;
-
-        mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback,
-                MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
-        setMediaSession(mRouter.getMediaSessionToken());
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mRouter.removeCallback(mCallback);
-        setMediaSession(null);
-        mAttachedToWindow = false;
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
-            mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    void update(boolean animate) {
-        // Defer dialog updates if a user is adjusting a volume in the list
-        if (mRouteInVolumeSliderTouched != null) {
-            mHasPendingUpdate = true;
-            mPendingUpdateAnimationNeeded |= animate;
-            return;
-        }
-        mHasPendingUpdate = false;
-        mPendingUpdateAnimationNeeded = false;
-        if (!mRoute.isSelected() || mRoute.isDefaultOrBluetooth()) {
-            dismiss();
-            return;
-        }
-        if (!mCreated) {
-            return;
-        }
-
-        mRouteNameTextView.setText(mRoute.getName());
-        mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE);
-        if (mCustomControlView == null && mArtIconIsLoaded) {
-            if (isBitmapRecycled(mArtIconLoadedBitmap)) {
-                Log.w(TAG, "Can't set artwork image with recycled bitmap: " + mArtIconLoadedBitmap);
-            } else {
-                mArtView.setImageBitmap(mArtIconLoadedBitmap);
-                mArtView.setBackgroundColor(mArtIconBackgroundColor);
-            }
-            clearLoadedBitmap();
-        }
-        updateVolumeControlLayout();
-        updatePlaybackControlLayout();
-        updateLayoutHeight(animate);
-    }
-
-    private boolean isBitmapRecycled(Bitmap bitmap) {
-        return bitmap != null && bitmap.isRecycled();
-    }
-
-    private boolean canShowPlaybackControlLayout() {
-        return mCustomControlView == null && (mDescription != null || mState != null);
-    }
-
-    /**
-     * Returns the height of main media controller which includes playback control and master
-     * volume control.
-     */
-    private int getMainControllerHeight(boolean showPlaybackControl) {
-        int height = 0;
-        if (showPlaybackControl || mVolumeControlLayout.getVisibility() == View.VISIBLE) {
-            height += mMediaMainControlLayout.getPaddingTop()
-                    + mMediaMainControlLayout.getPaddingBottom();
-            if (showPlaybackControl) {
-                height +=  mPlaybackControlLayout.getMeasuredHeight();
-            }
-            if (mVolumeControlLayout.getVisibility() == View.VISIBLE) {
-                height += mVolumeControlLayout.getMeasuredHeight();
-            }
-            if (showPlaybackControl && mVolumeControlLayout.getVisibility() == View.VISIBLE) {
-                height += mDividerView.getMeasuredHeight();
-            }
-        }
-        return height;
-    }
-
-    private void updateMediaControlVisibility(boolean canShowPlaybackControlLayout) {
-        // TODO: Update the top and bottom padding of the control layout according to the display
-        // height.
-        mDividerView.setVisibility((mVolumeControlLayout.getVisibility() == View.VISIBLE
-                && canShowPlaybackControlLayout) ? View.VISIBLE : View.GONE);
-        mMediaMainControlLayout.setVisibility((mVolumeControlLayout.getVisibility() == View.GONE
-                && !canShowPlaybackControlLayout) ? View.GONE : View.VISIBLE);
-    }
-
-    void updateLayoutHeight(final boolean animate) {
-        // We need to defer the update until the first layout has occurred, as we don't yet know the
-        // overall visible display size in which the window this view is attached to has been
-        // positioned in.
-        mDefaultControlLayout.requestLayout();
-        ViewTreeObserver observer = mDefaultControlLayout.getViewTreeObserver();
-        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                mDefaultControlLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-                if (mIsGroupListAnimating) {
-                    mIsGroupListAnimationPending = true;
-                } else {
-                    updateLayoutHeightInternal(animate);
-                }
-            }
-        });
-    }
-
-    /**
-     * Updates the height of views and hide artwork or metadata if space is limited.
-     */
-    void updateLayoutHeightInternal(boolean animate) {
-        // Measure the size of widgets and get the height of main components.
-        int oldHeight = getLayoutHeight(mMediaMainControlLayout);
-        setLayoutHeight(mMediaMainControlLayout, ViewGroup.LayoutParams.MATCH_PARENT);
-        updateMediaControlVisibility(canShowPlaybackControlLayout());
-        View decorView = getWindow().getDecorView();
-        decorView.measure(
-                MeasureSpec.makeMeasureSpec(getWindow().getAttributes().width, MeasureSpec.EXACTLY),
-                MeasureSpec.UNSPECIFIED);
-        setLayoutHeight(mMediaMainControlLayout, oldHeight);
-        int artViewHeight = 0;
-        if (mCustomControlView == null && mArtView.getDrawable() instanceof BitmapDrawable) {
-            Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap();
-            if (art != null) {
-                artViewHeight = getDesiredArtHeight(art.getWidth(), art.getHeight());
-                mArtView.setScaleType(art.getWidth() >= art.getHeight()
-                        ? ImageView.ScaleType.FIT_XY : ImageView.ScaleType.FIT_CENTER);
-            }
-        }
-        int mainControllerHeight = getMainControllerHeight(canShowPlaybackControlLayout());
-        int volumeGroupListCount = mGroupMemberRoutes.size();
-        // Scale down volume group list items in landscape mode.
-        int expandedGroupListHeight = getGroup() == null ? 0 :
-                mVolumeGroupListItemHeight * getGroup().getRoutes().size();
-        if (volumeGroupListCount > 0) {
-            expandedGroupListHeight += mVolumeGroupListPaddingTop;
-        }
-        expandedGroupListHeight = Math.min(expandedGroupListHeight, mVolumeGroupListMaxHeight);
-        int visibleGroupListHeight = mIsGroupExpanded ? expandedGroupListHeight : 0;
-
-        int desiredControlLayoutHeight =
-                Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight;
-        Rect visibleRect = new Rect();
-        decorView.getWindowVisibleDisplayFrame(visibleRect);
-        // Height of non-control views in decor view.
-        // This includes title bar, button bar, and dialog's vertical padding which should be
-        // always shown.
-        int nonControlViewHeight = mDialogAreaLayout.getMeasuredHeight()
-                - mDefaultControlLayout.getMeasuredHeight();
-        // Maximum allowed height for controls to fit screen.
-        int maximumControlViewHeight = visibleRect.height() - nonControlViewHeight;
-
-        // Show artwork if it fits the screen.
-        if (mCustomControlView == null && artViewHeight > 0
-                && desiredControlLayoutHeight <= maximumControlViewHeight) {
-            mArtView.setVisibility(View.VISIBLE);
-            setLayoutHeight(mArtView, artViewHeight);
-        } else {
-            if (getLayoutHeight(mVolumeGroupList) + mMediaMainControlLayout.getMeasuredHeight()
-                    >= mDefaultControlLayout.getMeasuredHeight()) {
-                mArtView.setVisibility(View.GONE);
-            }
-            artViewHeight = 0;
-            desiredControlLayoutHeight = visibleGroupListHeight + mainControllerHeight;
-        }
-        // Show the playback control if it fits the screen.
-        if (canShowPlaybackControlLayout()
-                && desiredControlLayoutHeight <= maximumControlViewHeight) {
-            mPlaybackControlLayout.setVisibility(View.VISIBLE);
-        } else {
-            mPlaybackControlLayout.setVisibility(View.GONE);
-        }
-        updateMediaControlVisibility(mPlaybackControlLayout.getVisibility() == View.VISIBLE);
-        mainControllerHeight = getMainControllerHeight(
-                mPlaybackControlLayout.getVisibility() == View.VISIBLE);
-        desiredControlLayoutHeight =
-                Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight;
-
-        // Limit the volume group list height to fit the screen.
-        if (desiredControlLayoutHeight > maximumControlViewHeight) {
-            visibleGroupListHeight -= (desiredControlLayoutHeight - maximumControlViewHeight);
-            desiredControlLayoutHeight = maximumControlViewHeight;
-        }
-        // Update the layouts with the computed heights.
-        mMediaMainControlLayout.clearAnimation();
-        mVolumeGroupList.clearAnimation();
-        mDefaultControlLayout.clearAnimation();
-        if (animate) {
-            animateLayoutHeight(mMediaMainControlLayout, mainControllerHeight);
-            animateLayoutHeight(mVolumeGroupList, visibleGroupListHeight);
-            animateLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
-        } else {
-            setLayoutHeight(mMediaMainControlLayout, mainControllerHeight);
-            setLayoutHeight(mVolumeGroupList, visibleGroupListHeight);
-            setLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
-        }
-        // Maximize the window size with a transparent layout in advance for smooth animation.
-        setLayoutHeight(mExpandableAreaLayout, visibleRect.height());
-        rebuildVolumeGroupList(animate);
-    }
-
-    void updateVolumeGroupItemHeight(View item) {
-        LinearLayout container = (LinearLayout) item.findViewById(R.id.volume_item_container);
-        setLayoutHeight(container, mVolumeGroupListItemHeight);
-        View icon = item.findViewById(R.id.mr_volume_item_icon);
-        ViewGroup.LayoutParams lp = icon.getLayoutParams();
-        lp.width = mVolumeGroupListItemIconSize;
-        lp.height = mVolumeGroupListItemIconSize;
-        icon.setLayoutParams(lp);
-    }
-
-    private void animateLayoutHeight(final View view, int targetHeight) {
-        final int startValue = getLayoutHeight(view);
-        final int endValue = targetHeight;
-        Animation anim = new Animation() {
-            @Override
-            protected void applyTransformation(float interpolatedTime, Transformation t) {
-                int height = startValue - (int) ((startValue - endValue) * interpolatedTime);
-                setLayoutHeight(view, height);
-            }
-        };
-        anim.setDuration(mGroupListAnimationDurationMs);
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            anim.setInterpolator(mInterpolator);
-        }
-        view.startAnimation(anim);
-    }
-
-    void loadInterpolator() {
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
-            mInterpolator = mIsGroupExpanded ? mLinearOutSlowInInterpolator
-                    : mFastOutSlowInInterpolator;
-        } else {
-            mInterpolator = mAccelerateDecelerateInterpolator;
-        }
-    }
-
-    private void updateVolumeControlLayout() {
-        if (isVolumeControlAvailable(mRoute)) {
-            if (mVolumeControlLayout.getVisibility() == View.GONE) {
-                mVolumeControlLayout.setVisibility(View.VISIBLE);
-                mVolumeSlider.setMax(mRoute.getVolumeMax());
-                mVolumeSlider.setProgress(mRoute.getVolume());
-                mGroupExpandCollapseButton.setVisibility(getGroup() == null ? View.GONE
-                        : View.VISIBLE);
-            }
-        } else {
-            mVolumeControlLayout.setVisibility(View.GONE);
-        }
-    }
-
-    private void rebuildVolumeGroupList(boolean animate) {
-        List<MediaRouter.RouteInfo> routes = getGroup() == null ? null : getGroup().getRoutes();
-        if (routes == null) {
-            mGroupMemberRoutes.clear();
-            mVolumeGroupAdapter.notifyDataSetChanged();
-        } else if (MediaRouteDialogHelper.listUnorderedEquals(mGroupMemberRoutes, routes)) {
-            mVolumeGroupAdapter.notifyDataSetChanged();
-        } else {
-            HashMap<MediaRouter.RouteInfo, Rect> previousRouteBoundMap = animate
-                    ? MediaRouteDialogHelper.getItemBoundMap(mVolumeGroupList, mVolumeGroupAdapter)
-                    : null;
-            HashMap<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap = animate
-                    ? MediaRouteDialogHelper.getItemBitmapMap(mContext, mVolumeGroupList,
-                            mVolumeGroupAdapter) : null;
-            mGroupMemberRoutesAdded =
-                    MediaRouteDialogHelper.getItemsAdded(mGroupMemberRoutes, routes);
-            mGroupMemberRoutesRemoved = MediaRouteDialogHelper.getItemsRemoved(mGroupMemberRoutes,
-                    routes);
-            mGroupMemberRoutes.addAll(0, mGroupMemberRoutesAdded);
-            mGroupMemberRoutes.removeAll(mGroupMemberRoutesRemoved);
-            mVolumeGroupAdapter.notifyDataSetChanged();
-            if (animate && mIsGroupExpanded
-                    && mGroupMemberRoutesAdded.size() + mGroupMemberRoutesRemoved.size() > 0) {
-                animateGroupListItems(previousRouteBoundMap, previousRouteBitmapMap);
-            } else {
-                mGroupMemberRoutesAdded = null;
-                mGroupMemberRoutesRemoved = null;
-            }
-        }
-    }
-
-    private void animateGroupListItems(final Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap,
-            final Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) {
-        mVolumeGroupList.setEnabled(false);
-        mVolumeGroupList.requestLayout();
-        mIsGroupListAnimating = true;
-        ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver();
-        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-                animateGroupListItemsInternal(previousRouteBoundMap, previousRouteBitmapMap);
-            }
-        });
-    }
-
-    void animateGroupListItemsInternal(
-            Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap,
-            Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) {
-        if (mGroupMemberRoutesAdded == null || mGroupMemberRoutesRemoved == null) {
-            return;
-        }
-        int groupSizeDelta = mGroupMemberRoutesAdded.size() - mGroupMemberRoutesRemoved.size();
-        boolean listenerRegistered = false;
-        Animation.AnimationListener listener = new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) {
-                mVolumeGroupList.startAnimationAll();
-                mVolumeGroupList.postDelayed(mGroupListFadeInAnimation,
-                        mGroupListAnimationDurationMs);
-            }
-
-            @Override
-            public void onAnimationEnd(Animation animation) { }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) { }
-        };
-
-        // Animate visible items from previous positions to current positions except routes added
-        // just before. Added routes will remain hidden until translate animation finishes.
-        int first = mVolumeGroupList.getFirstVisiblePosition();
-        for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) {
-            View view = mVolumeGroupList.getChildAt(i);
-            int position = first + i;
-            MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position);
-            Rect previousBounds = previousRouteBoundMap.get(route);
-            int currentTop = view.getTop();
-            int previousTop = previousBounds != null ? previousBounds.top
-                    : (currentTop + mVolumeGroupListItemHeight * groupSizeDelta);
-            AnimationSet animSet = new AnimationSet(true);
-            if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) {
-                previousTop = currentTop;
-                Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f);
-                alphaAnim.setDuration(mGroupListFadeInDurationMs);
-                animSet.addAnimation(alphaAnim);
-            }
-            Animation translationAnim = new TranslateAnimation(0, 0, previousTop - currentTop, 0);
-            translationAnim.setDuration(mGroupListAnimationDurationMs);
-            animSet.addAnimation(translationAnim);
-            animSet.setFillAfter(true);
-            animSet.setFillEnabled(true);
-            animSet.setInterpolator(mInterpolator);
-            if (!listenerRegistered) {
-                listenerRegistered = true;
-                animSet.setAnimationListener(listener);
-            }
-            view.clearAnimation();
-            view.startAnimation(animSet);
-            previousRouteBoundMap.remove(route);
-            previousRouteBitmapMap.remove(route);
-        }
-
-        // If a member route doesn't exist any longer, it can be either removed or moved out of the
-        // ListView layout boundary. In this case, use the previously captured bitmaps for
-        // animation.
-        for (Map.Entry<MediaRouter.RouteInfo, BitmapDrawable> item
-                : previousRouteBitmapMap.entrySet()) {
-            final MediaRouter.RouteInfo route = item.getKey();
-            final BitmapDrawable bitmap = item.getValue();
-            final Rect bounds = previousRouteBoundMap.get(route);
-            OverlayObject object = null;
-            if (mGroupMemberRoutesRemoved.contains(route)) {
-                object = new OverlayObject(bitmap, bounds).setAlphaAnimation(1.0f, 0.0f)
-                        .setDuration(mGroupListFadeOutDurationMs)
-                        .setInterpolator(mInterpolator);
-            } else {
-                int deltaY = groupSizeDelta * mVolumeGroupListItemHeight;
-                object = new OverlayObject(bitmap, bounds).setTranslateYAnimation(deltaY)
-                        .setDuration(mGroupListAnimationDurationMs)
-                        .setInterpolator(mInterpolator)
-                        .setAnimationEndListener(new OverlayObject.OnAnimationEndListener() {
-                            @Override
-                            public void onAnimationEnd() {
-                                mGroupMemberRoutesAnimatingWithBitmap.remove(route);
-                                mVolumeGroupAdapter.notifyDataSetChanged();
-                            }
-                        });
-                mGroupMemberRoutesAnimatingWithBitmap.add(route);
-            }
-            mVolumeGroupList.addOverlayObject(object);
-        }
-    }
-
-    void startGroupListFadeInAnimation() {
-        clearGroupListAnimation(true);
-        mVolumeGroupList.requestLayout();
-        ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver();
-        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
-            @Override
-            public void onGlobalLayout() {
-                mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
-                startGroupListFadeInAnimationInternal();
-            }
-        });
-    }
-
-    void startGroupListFadeInAnimationInternal() {
-        if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.size() != 0) {
-            fadeInAddedRoutes();
-        } else {
-            finishAnimation(true);
-        }
-    }
-
-    void finishAnimation(boolean animate) {
-        mGroupMemberRoutesAdded = null;
-        mGroupMemberRoutesRemoved = null;
-        mIsGroupListAnimating = false;
-        if (mIsGroupListAnimationPending) {
-            mIsGroupListAnimationPending = false;
-            updateLayoutHeight(animate);
-        }
-        mVolumeGroupList.setEnabled(true);
-    }
-
-    private void fadeInAddedRoutes() {
-        Animation.AnimationListener listener = new Animation.AnimationListener() {
-            @Override
-            public void onAnimationStart(Animation animation) { }
-
-            @Override
-            public void onAnimationEnd(Animation animation) {
-                finishAnimation(true);
-            }
-
-            @Override
-            public void onAnimationRepeat(Animation animation) { }
-        };
-        boolean listenerRegistered = false;
-        int first = mVolumeGroupList.getFirstVisiblePosition();
-        for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) {
-            View view = mVolumeGroupList.getChildAt(i);
-            int position = first + i;
-            MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position);
-            if (mGroupMemberRoutesAdded.contains(route)) {
-                Animation alphaAnim = new AlphaAnimation(0.0f, 1.0f);
-                alphaAnim.setDuration(mGroupListFadeInDurationMs);
-                alphaAnim.setFillEnabled(true);
-                alphaAnim.setFillAfter(true);
-                if (!listenerRegistered) {
-                    listenerRegistered = true;
-                    alphaAnim.setAnimationListener(listener);
-                }
-                view.clearAnimation();
-                view.startAnimation(alphaAnim);
-            }
-        }
-    }
-
-    void clearGroupListAnimation(boolean exceptAddedRoutes) {
-        int first = mVolumeGroupList.getFirstVisiblePosition();
-        for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) {
-            View view = mVolumeGroupList.getChildAt(i);
-            int position = first + i;
-            MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position);
-            if (exceptAddedRoutes && mGroupMemberRoutesAdded != null
-                    && mGroupMemberRoutesAdded.contains(route)) {
-                continue;
-            }
-            LinearLayout container = (LinearLayout) view.findViewById(R.id.volume_item_container);
-            container.setVisibility(View.VISIBLE);
-            AnimationSet animSet = new AnimationSet(true);
-            Animation alphaAnim = new AlphaAnimation(1.0f, 1.0f);
-            alphaAnim.setDuration(0);
-            animSet.addAnimation(alphaAnim);
-            Animation translationAnim = new TranslateAnimation(0, 0, 0, 0);
-            translationAnim.setDuration(0);
-            animSet.setFillAfter(true);
-            animSet.setFillEnabled(true);
-            view.clearAnimation();
-            view.startAnimation(animSet);
-        }
-        mVolumeGroupList.stopAnimationAll();
-        if (!exceptAddedRoutes) {
-            finishAnimation(false);
-        }
-    }
-
-    private void updatePlaybackControlLayout() {
-        if (canShowPlaybackControlLayout()) {
-            CharSequence title = mDescription == null ? null : mDescription.getTitle();
-            boolean hasTitle = !TextUtils.isEmpty(title);
-
-            CharSequence subtitle = mDescription == null ? null : mDescription.getSubtitle();
-            boolean hasSubtitle = !TextUtils.isEmpty(subtitle);
-
-            boolean showTitle = false;
-            boolean showSubtitle = false;
-            if (mRoute.getPresentationDisplayId()
-                    != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
-                // The user is currently casting screen.
-                mTitleView.setText(R.string.mr_controller_casting_screen);
-                showTitle = true;
-            } else if (mState == null || mState.getState() == PlaybackStateCompat.STATE_NONE) {
-                // Show "No media selected" as we don't yet know the playback state.
-                mTitleView.setText(R.string.mr_controller_no_media_selected);
-                showTitle = true;
-            } else if (!hasTitle && !hasSubtitle) {
-                mTitleView.setText(R.string.mr_controller_no_info_available);
-                showTitle = true;
-            } else {
-                if (hasTitle) {
-                    mTitleView.setText(title);
-                    showTitle = true;
-                }
-                if (hasSubtitle) {
-                    mSubtitleView.setText(subtitle);
-                    showSubtitle = true;
-                }
-            }
-            mTitleView.setVisibility(showTitle ? View.VISIBLE : View.GONE);
-            mSubtitleView.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
-
-            if (mState != null) {
-                boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING
-                        || mState.getState() == PlaybackStateCompat.STATE_PLAYING;
-                Context playbackControlButtonContext = mPlaybackControlButton.getContext();
-                boolean visible = true;
-                int iconDrawableAttr = 0;
-                int iconDescResId = 0;
-                if (isPlaying && isPauseActionSupported()) {
-                    iconDrawableAttr = R.attr.mediaRoutePauseDrawable;
-                    iconDescResId = R.string.mr_controller_pause;
-                } else if (isPlaying && isStopActionSupported()) {
-                    iconDrawableAttr = R.attr.mediaRouteStopDrawable;
-                    iconDescResId = R.string.mr_controller_stop;
-                } else if (!isPlaying && isPlayActionSupported()) {
-                    iconDrawableAttr = R.attr.mediaRoutePlayDrawable;
-                    iconDescResId = R.string.mr_controller_play;
-                } else {
-                    visible = false;
-                }
-                mPlaybackControlButton.setVisibility(visible ? View.VISIBLE : View.GONE);
-                if (visible) {
-                    mPlaybackControlButton.setImageResource(
-                            MediaRouterThemeHelper.getThemeResource(
-                                    playbackControlButtonContext, iconDrawableAttr));
-                    mPlaybackControlButton.setContentDescription(
-                            playbackControlButtonContext.getResources()
-                                    .getText(iconDescResId));
-                }
-            }
-        }
-    }
-
-    private boolean isPlayActionSupported() {
-        return (mState.getActions() & (ACTION_PLAY | ACTION_PLAY_PAUSE)) != 0;
-    }
-
-    private boolean isPauseActionSupported() {
-        return (mState.getActions() & (ACTION_PAUSE | ACTION_PLAY_PAUSE)) != 0;
-    }
-
-    private boolean isStopActionSupported() {
-        return (mState.getActions() & ACTION_STOP) != 0;
-    }
-
-    boolean isVolumeControlAvailable(MediaRouter.RouteInfo route) {
-        return mVolumeControlEnabled && route.getVolumeHandling()
-                == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
-    }
-
-    private static int getLayoutHeight(View view) {
-        return view.getLayoutParams().height;
-    }
-
-    static void setLayoutHeight(View view, int height) {
-        ViewGroup.LayoutParams lp = view.getLayoutParams();
-        lp.height = height;
-        view.setLayoutParams(lp);
-    }
-
-    private static boolean uriEquals(Uri uri1, Uri uri2) {
-        if (uri1 != null && uri1.equals(uri2)) {
-            return true;
-        } else if (uri1 == null && uri2 == null) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns desired art height to fit into controller dialog.
-     */
-    int getDesiredArtHeight(int originalWidth, int originalHeight) {
-        if (originalWidth >= originalHeight) {
-            // For landscape art, fit width to dialog width.
-            return (int) ((float) mDialogContentWidth * originalHeight / originalWidth + 0.5f);
-        }
-        // For portrait art, fit height to 16:9 ratio case's height.
-        return (int) ((float) mDialogContentWidth * 9 / 16 + 0.5f);
-    }
-
-    void updateArtIconIfNeeded() {
-        if (mCustomControlView != null || !isIconChanged()) {
-            return;
-        }
-        if (mFetchArtTask != null) {
-            mFetchArtTask.cancel(true);
-        }
-        mFetchArtTask = new FetchArtTask();
-        mFetchArtTask.execute();
-    }
-
-    /**
-     * Clear the bitmap loaded by FetchArtTask. Will be called after the loaded bitmaps are applied
-     * to artwork, or no longer valid.
-     */
-    void clearLoadedBitmap() {
-        mArtIconIsLoaded = false;
-        mArtIconLoadedBitmap = null;
-        mArtIconBackgroundColor = 0;
-    }
-
-    /**
-     * Returns whether a new art image is different from an original art image. Compares
-     * Bitmap objects first, and then compares URIs only if bitmap is unchanged with
-     * a null value.
-     */
-    private boolean isIconChanged() {
-        Bitmap newBitmap = mDescription == null ? null : mDescription.getIconBitmap();
-        Uri newUri = mDescription == null ? null : mDescription.getIconUri();
-        Bitmap oldBitmap = mFetchArtTask == null ? mArtIconBitmap : mFetchArtTask.getIconBitmap();
-        Uri oldUri = mFetchArtTask == null ? mArtIconUri : mFetchArtTask.getIconUri();
-        if (oldBitmap != newBitmap) {
-            return true;
-        } else if (oldBitmap == null && !uriEquals(oldUri, newUri)) {
-            return true;
-        }
-        return false;
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        MediaRouterCallback() {
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
-            update(false);
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
-            update(true);
-        }
-
-        @Override
-        public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
-            SeekBar volumeSlider = mVolumeSliderMap.get(route);
-            int volume = route.getVolume();
-            if (DEBUG) {
-                Log.d(TAG, "onRouteVolumeChanged(), route.getVolume:" + volume);
-            }
-            if (volumeSlider != null && mRouteInVolumeSliderTouched != route) {
-                volumeSlider.setProgress(volume);
-            }
-        }
-    }
-
-    private final class MediaControllerCallback extends MediaControllerCompat.Callback {
-        MediaControllerCallback() {
-        }
-
-        @Override
-        public void onSessionDestroyed() {
-            if (mMediaController != null) {
-                mMediaController.unregisterCallback(mControllerCallback);
-                mMediaController = null;
-            }
-        }
-
-        @Override
-        public void onPlaybackStateChanged(PlaybackStateCompat state) {
-            mState = state;
-            update(false);
-        }
-
-        @Override
-        public void onMetadataChanged(MediaMetadataCompat metadata) {
-            mDescription = metadata == null ? null : metadata.getDescription();
-            updateArtIconIfNeeded();
-            update(false);
-        }
-    }
-
-    private final class ClickListener implements View.OnClickListener {
-        ClickListener() {
-        }
-
-        @Override
-        public void onClick(View v) {
-            int id = v.getId();
-            if (id == BUTTON_STOP_RES_ID || id == BUTTON_DISCONNECT_RES_ID) {
-                if (mRoute.isSelected()) {
-                    mRouter.unselect(id == BUTTON_STOP_RES_ID ?
-                            MediaRouter.UNSELECT_REASON_STOPPED :
-                            MediaRouter.UNSELECT_REASON_DISCONNECTED);
-                }
-                dismiss();
-            } else if (id == R.id.mr_control_playback_ctrl) {
-                if (mMediaController != null && mState != null) {
-                    boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_PLAYING;
-                    int actionDescResId = 0;
-                    if (isPlaying && isPauseActionSupported()) {
-                        mMediaController.getTransportControls().pause();
-                        actionDescResId = R.string.mr_controller_pause;
-                    } else if (isPlaying && isStopActionSupported()) {
-                        mMediaController.getTransportControls().stop();
-                        actionDescResId = R.string.mr_controller_stop;
-                    } else if (!isPlaying && isPlayActionSupported()){
-                        mMediaController.getTransportControls().play();
-                        actionDescResId = R.string.mr_controller_play;
-                    }
-                    // Announce the action for accessibility.
-                    if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()
-                            && actionDescResId != 0) {
-                        AccessibilityEvent event = AccessibilityEvent.obtain(
-                                AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
-                        event.setPackageName(mContext.getPackageName());
-                        event.setClassName(getClass().getName());
-                        event.getText().add(mContext.getString(actionDescResId));
-                        mAccessibilityManager.sendAccessibilityEvent(event);
-                    }
-                }
-            } else if (id == R.id.mr_close) {
-                dismiss();
-            }
-        }
-    }
-
-    private class VolumeChangeListener implements SeekBar.OnSeekBarChangeListener {
-        private final Runnable mStopTrackingTouch = new Runnable() {
-            @Override
-            public void run() {
-                if (mRouteInVolumeSliderTouched != null) {
-                    mRouteInVolumeSliderTouched = null;
-                    if (mHasPendingUpdate) {
-                        update(mPendingUpdateAnimationNeeded);
-                    }
-                }
-            }
-        };
-
-        VolumeChangeListener() {
-        }
-
-        @Override
-        public void onStartTrackingTouch(SeekBar seekBar) {
-            if (mRouteInVolumeSliderTouched != null) {
-                mVolumeSlider.removeCallbacks(mStopTrackingTouch);
-            }
-            mRouteInVolumeSliderTouched = (MediaRouter.RouteInfo) seekBar.getTag();
-        }
-
-        @Override
-        public void onStopTrackingTouch(SeekBar seekBar) {
-            // Defer resetting mVolumeSliderTouched to allow the media route provider
-            // a little time to settle into its new state and publish the final
-            // volume update.
-            mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
-        }
-
-        @Override
-        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            if (fromUser) {
-                MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) seekBar.getTag();
-                if (DEBUG) {
-                    Log.d(TAG, "onProgressChanged(): calling "
-                            + "MediaRouter.RouteInfo.requestSetVolume(" + progress + ")");
-                }
-                route.requestSetVolume(progress);
-            }
-        }
-    }
-
-    private class VolumeGroupAdapter extends ArrayAdapter<MediaRouter.RouteInfo> {
-        final float mDisabledAlpha;
-
-        public VolumeGroupAdapter(Context context, List<MediaRouter.RouteInfo> objects) {
-            super(context, 0, objects);
-            mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
-        }
-
-        @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) {
-                v = LayoutInflater.from(parent.getContext()).inflate(
-                        R.layout.mr_controller_volume_item, parent, false);
-            } else {
-                updateVolumeGroupItemHeight(v);
-            }
-
-            MediaRouter.RouteInfo route = getItem(position);
-            if (route != null) {
-                boolean isEnabled = route.isEnabled();
-
-                TextView routeName = (TextView) v.findViewById(R.id.mr_name);
-                routeName.setEnabled(isEnabled);
-                routeName.setText(route.getName());
-
-                MediaRouteVolumeSlider volumeSlider =
-                        (MediaRouteVolumeSlider) v.findViewById(R.id.mr_volume_slider);
-                MediaRouterThemeHelper.setVolumeSliderColor(
-                        parent.getContext(), volumeSlider, mVolumeGroupList);
-                volumeSlider.setTag(route);
-                mVolumeSliderMap.put(route, volumeSlider);
-                volumeSlider.setHideThumb(!isEnabled);
-                volumeSlider.setEnabled(isEnabled);
-                if (isEnabled) {
-                    if (isVolumeControlAvailable(route)) {
-                        volumeSlider.setMax(route.getVolumeMax());
-                        volumeSlider.setProgress(route.getVolume());
-                        volumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener);
-                    } else {
-                        volumeSlider.setMax(100);
-                        volumeSlider.setProgress(100);
-                        volumeSlider.setEnabled(false);
-                    }
-                }
-
-                ImageView volumeItemIcon =
-                        (ImageView) v.findViewById(R.id.mr_volume_item_icon);
-                volumeItemIcon.setAlpha(isEnabled ? 0xFF : (int) (0xFF * mDisabledAlpha));
-
-                // If overlay bitmap exists, real view should remain hidden until
-                // the animation ends.
-                LinearLayout container = (LinearLayout) v.findViewById(R.id.volume_item_container);
-                container.setVisibility(mGroupMemberRoutesAnimatingWithBitmap.contains(route)
-                        ? View.INVISIBLE : View.VISIBLE);
-
-                // Routes which are being added will be invisible until animation ends.
-                if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) {
-                    Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f);
-                    alphaAnim.setDuration(0);
-                    alphaAnim.setFillEnabled(true);
-                    alphaAnim.setFillAfter(true);
-                    v.clearAnimation();
-                    v.startAnimation(alphaAnim);
-                }
-            }
-            return v;
-        }
-    }
-
-    private class FetchArtTask extends AsyncTask<Void, Void, Bitmap> {
-        // Show animation only when fetching takes a long time.
-        private static final long SHOW_ANIM_TIME_THRESHOLD_MILLIS = 120L;
-
-        private final Bitmap mIconBitmap;
-        private final Uri mIconUri;
-        private int mBackgroundColor;
-        private long mStartTimeMillis;
-
-        FetchArtTask() {
-            Bitmap bitmap = mDescription == null ? null : mDescription.getIconBitmap();
-            if (isBitmapRecycled(bitmap)) {
-                Log.w(TAG, "Can't fetch the given art bitmap because it's already recycled.");
-                bitmap = null;
-            }
-            mIconBitmap = bitmap;
-            mIconUri = mDescription == null ? null : mDescription.getIconUri();
-        }
-
-        public Bitmap getIconBitmap() {
-            return mIconBitmap;
-        }
-
-        public Uri getIconUri() {
-            return mIconUri;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            mStartTimeMillis = SystemClock.uptimeMillis();
-            clearLoadedBitmap();
-        }
-
-        @Override
-        protected Bitmap doInBackground(Void... arg) {
-            Bitmap art = null;
-            if (mIconBitmap != null) {
-                art = mIconBitmap;
-            } else if (mIconUri != null) {
-                InputStream stream = null;
-                try {
-                    if ((stream = openInputStreamByScheme(mIconUri)) == null) {
-                        Log.w(TAG, "Unable to open: " + mIconUri);
-                        return null;
-                    }
-                    // Query art size.
-                    BitmapFactory.Options options = new BitmapFactory.Options();
-                    options.inJustDecodeBounds = true;
-                    BitmapFactory.decodeStream(stream, null, options);
-                    if (options.outWidth == 0 || options.outHeight == 0) {
-                        return null;
-                    }
-                    // Rewind the stream in order to restart art decoding.
-                    try {
-                        stream.reset();
-                    } catch (IOException e) {
-                        // Failed to rewind the stream, try to reopen it.
-                        stream.close();
-                        if ((stream = openInputStreamByScheme(mIconUri)) == null) {
-                            Log.w(TAG, "Unable to open: " + mIconUri);
-                            return null;
-                        }
-                    }
-                    // Calculate required size to decode the art and possibly resize it.
-                    options.inJustDecodeBounds = false;
-                    int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight);
-                    int ratio = options.outHeight / reqHeight;
-                    options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio));
-                    if (isCancelled()) {
-                        return null;
-                    }
-                    art = BitmapFactory.decodeStream(stream, null, options);
-                } catch (IOException e){
-                    Log.w(TAG, "Unable to open: " + mIconUri, e);
-                } finally {
-                    if (stream != null) {
-                        try {
-                            stream.close();
-                        } catch (IOException e) {
-                        }
-                    }
-                }
-            }
-            if (isBitmapRecycled(art)) {
-                Log.w(TAG, "Can't use recycled bitmap: " + art);
-                return null;
-            }
-            if (art != null && art.getWidth() < art.getHeight()) {
-                // Portrait art requires dominant color as background color.
-                Palette palette = new Palette.Builder(art).maximumColorCount(1).generate();
-                mBackgroundColor = palette.getSwatches().isEmpty()
-                        ? 0 : palette.getSwatches().get(0).getRgb();
-            }
-            return art;
-        }
-
-        @Override
-        protected void onPostExecute(Bitmap art) {
-            mFetchArtTask = null;
-            if (!ObjectsCompat.equals(mArtIconBitmap, mIconBitmap)
-                    || !ObjectsCompat.equals(mArtIconUri, mIconUri)) {
-                mArtIconBitmap = mIconBitmap;
-                mArtIconLoadedBitmap = art;
-                mArtIconUri = mIconUri;
-                mArtIconBackgroundColor = mBackgroundColor;
-                mArtIconIsLoaded = true;
-                long elapsedTimeMillis = SystemClock.uptimeMillis() - mStartTimeMillis;
-                // Loaded bitmap will be applied on the next update
-                update(elapsedTimeMillis > SHOW_ANIM_TIME_THRESHOLD_MILLIS);
-            }
-        }
-
-        private InputStream openInputStreamByScheme(Uri uri) throws IOException {
-            String scheme = uri.getScheme().toLowerCase();
-            InputStream stream = null;
-            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
-                    || ContentResolver.SCHEME_CONTENT.equals(scheme)
-                    || ContentResolver.SCHEME_FILE.equals(scheme)) {
-                stream = mContext.getContentResolver().openInputStream(uri);
-            } else {
-                URL url = new URL(uri.toString());
-                URLConnection conn = url.openConnection();
-                conn.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS);
-                conn.setReadTimeout(CONNECTION_TIMEOUT_MILLIS);
-                stream = conn.getInputStream();
-            }
-            return (stream == null) ? null : new BufferedInputStream(stream);
-        }
-    }
-}
diff --git a/android/support/v7/app/MediaRouteControllerDialogFragment.java b/android/support/v7/app/MediaRouteControllerDialogFragment.java
deleted file mode 100644
index ba9ba12..0000000
--- a/android/support/v7/app/MediaRouteControllerDialogFragment.java
+++ /dev/null
@@ -1,76 +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.v7.app;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-
-/**
- * Media route controller dialog fragment.
- * <p>
- * Creates a {@link MediaRouteControllerDialog}.  The application may subclass
- * this dialog fragment to customize the media route controller dialog.
- * </p>
- */
-public class MediaRouteControllerDialogFragment extends DialogFragment {
-    private MediaRouteControllerDialog mDialog;
-    /**
-     * Creates a media route controller dialog fragment.
-     * <p>
-     * All subclasses of this class must also possess a default constructor.
-     * </p>
-     */
-    public MediaRouteControllerDialogFragment() {
-        setCancelable(true);
-    }
-
-    /**
-     * Called when the controller dialog is being created.
-     * <p>
-     * Subclasses may override this method to customize the dialog.
-     * </p>
-     */
-    public MediaRouteControllerDialog onCreateControllerDialog(
-            Context context, Bundle savedInstanceState) {
-        return new MediaRouteControllerDialog(context);
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        mDialog = onCreateControllerDialog(getContext(), savedInstanceState);
-        return mDialog;
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (mDialog != null) {
-            mDialog.clearGroupListAnimation(false);
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mDialog != null) {
-            mDialog.updateLayout();
-        }
-    }
-}
diff --git a/android/support/v7/app/MediaRouteDialogFactory.java b/android/support/v7/app/MediaRouteDialogFactory.java
deleted file mode 100644
index 1ae284f..0000000
--- a/android/support/v7/app/MediaRouteDialogFactory.java
+++ /dev/null
@@ -1,74 +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.v7.app;
-
-import android.support.annotation.NonNull;
-
-/**
- * The media route dialog factory is responsible for creating the media route
- * chooser and controller dialogs as needed.
- * <p>
- * The application can customize the dialogs by providing a subclass of the
- * dialog factory to the {@link MediaRouteButton} using the
- * {@link MediaRouteButton#setDialogFactory setDialogFactory} method.
- * </p>
- */
-public class MediaRouteDialogFactory {
-    private static final MediaRouteDialogFactory sDefault = new MediaRouteDialogFactory();
-
-    /**
-     * Creates a default media route dialog factory.
-     */
-    public MediaRouteDialogFactory() {
-    }
-
-    /**
-     * Gets the default factory instance.
-     *
-     * @return The default media route dialog factory, never null.
-     */
-    @NonNull
-    public static MediaRouteDialogFactory getDefault() {
-        return sDefault;
-    }
-
-    /**
-     * Called when the chooser dialog is being opened and it is time to create the fragment.
-     * <p>
-     * Subclasses may override this method to create a customized fragment.
-     * </p>
-     *
-     * @return The media route chooser dialog fragment, must not be null.
-     */
-    @NonNull
-    public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() {
-        return new MediaRouteChooserDialogFragment();
-    }
-
-    /**
-     * Called when the controller dialog is being opened and it is time to create the fragment.
-     * <p>
-     * Subclasses may override this method to create a customized fragment.
-     * </p>
-     *
-     * @return The media route controller dialog fragment, must not be null.
-     */
-    @NonNull
-    public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
-        return new MediaRouteControllerDialogFragment();
-    }
-}
diff --git a/android/support/v7/app/MediaRouteDialogHelper.java b/android/support/v7/app/MediaRouteDialogHelper.java
deleted file mode 100644
index 78f550b..0000000
--- a/android/support/v7/app/MediaRouteDialogHelper.java
+++ /dev/null
@@ -1,151 +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.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.support.v7.mediarouter.R;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-final class MediaRouteDialogHelper {
-    /**
-     * The framework should set the dialog width properly, but somehow it doesn't work, hence
-     * duplicating a similar logic here to determine the appropriate dialog width.
-     */
-    public static int getDialogWidth(Context context) {
-        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
-        boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
-
-        TypedValue value = new TypedValue();
-        context.getResources().getValue(isPortrait ? R.dimen.mr_dialog_fixed_width_minor
-                : R.dimen.mr_dialog_fixed_width_major, value, true);
-        if (value.type == TypedValue.TYPE_DIMENSION) {
-            return (int) value.getDimension(metrics);
-        } else if (value.type == TypedValue.TYPE_FRACTION) {
-            return (int) value.getFraction(metrics.widthPixels, metrics.widthPixels);
-        }
-        return ViewGroup.LayoutParams.WRAP_CONTENT;
-    }
-
-    /**
-     * Compares two lists regardless of order.
-     *
-     * @param list1 A list
-     * @param list2 A list to be compared with {@code list1}
-     * @return True if two lists have exactly same items regardless of order, false otherwise.
-     */
-    public static <E> boolean listUnorderedEquals(List<E> list1, List<E> list2) {
-        HashSet<E> set1 = new HashSet<>(list1);
-        HashSet<E> set2 = new HashSet<>(list2);
-        return set1.equals(set2);
-    }
-
-    /**
-     * Compares two lists and returns a set of items which exist
-     * after-list but before-list, which means newly added items.
-     *
-     * @param before A list
-     * @param after A list to be compared with {@code before}
-     * @return A set of items which contains newly added items while
-     * comparing {@code after} to {@code before}.
-     */
-    public static <E> Set<E> getItemsAdded(List<E> before, List<E> after) {
-        HashSet<E> set = new HashSet<>(after);
-        set.removeAll(before);
-        return set;
-    }
-
-    /**
-     * Compares two lists and returns a set of items which exist
-     * before-list but after-list, which means removed items.
-     *
-     * @param before A list
-     * @param after A list to be compared with {@code before}
-     * @return A set of items which contains removed items while
-     * comparing {@code after} to {@code before}.
-     */
-    public static <E> Set<E> getItemsRemoved(List<E> before, List<E> after) {
-        HashSet<E> set = new HashSet<>(before);
-        set.removeAll(after);
-        return set;
-    }
-
-    /**
-     * Generates an item-Rect map which indicates where member
-     * items are located in the given ListView.
-     *
-     * @param listView A list view
-     * @param adapter An array adapter which contains an array of items.
-     * @return A map of items and bounds of their views located in the given list view.
-     */
-    public static <E> HashMap<E, Rect> getItemBoundMap(ListView listView,
-            ArrayAdapter<E> adapter) {
-        HashMap<E, Rect> itemBoundMap = new HashMap<>();
-        int firstVisiblePosition = listView.getFirstVisiblePosition();
-        for (int i = 0; i < listView.getChildCount(); ++i) {
-            int position = firstVisiblePosition + i;
-            E item = adapter.getItem(position);
-            View view = listView.getChildAt(i);
-            itemBoundMap.put(item,
-                    new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
-        }
-        return itemBoundMap;
-    }
-
-    /**
-     * Generates an item-BitmapDrawable map which stores snapshots
-     * of member items in the given ListView.
-     *
-     * @param context A context
-     * @param listView A list view
-     * @param adapter An array adapter which contains an array of items.
-     * @return A map of items and snapshots of their views in the given list view.
-     */
-    public static <E> HashMap<E, BitmapDrawable> getItemBitmapMap(Context context,
-            ListView listView, ArrayAdapter<E> adapter) {
-        HashMap<E, BitmapDrawable> itemBitmapMap = new HashMap<>();
-        int firstVisiblePosition = listView.getFirstVisiblePosition();
-        for (int i = 0; i < listView.getChildCount(); ++i) {
-            int position = firstVisiblePosition + i;
-            E item = adapter.getItem(position);
-            View view = listView.getChildAt(i);
-            itemBitmapMap.put(item, getViewBitmap(context, view));
-        }
-        return itemBitmapMap;
-    }
-
-    private static BitmapDrawable getViewBitmap(Context context, View view) {
-        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
-        return new BitmapDrawable(context.getResources(), bitmap);
-    }
-}
diff --git a/android/support/v7/app/MediaRouteDiscoveryFragment.java b/android/support/v7/app/MediaRouteDiscoveryFragment.java
deleted file mode 100644
index 3d10b1e..0000000
--- a/android/support/v7/app/MediaRouteDiscoveryFragment.java
+++ /dev/null
@@ -1,163 +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.v7.app;
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v7.media.MediaRouter;
-import android.support.v7.media.MediaRouteSelector;
-
-/**
- * Media route discovery fragment.
- * <p>
- * This fragment takes care of registering a callback for media route discovery
- * during the {@link Fragment#onStart onStart()} phase
- * and removing it during the {@link Fragment#onStop onStop()} phase.
- * </p><p>
- * The application must supply a route selector to specify the kinds of routes
- * to discover.  The application may also override {@link #onCreateCallback} to
- * provide the {@link MediaRouter} callback to register.
- * </p><p>
- * Note that the discovery callback makes the application be connected with all the
- * {@link android.support.v7.media.MediaRouteProviderService media route provider services}
- * while it is registered.
- * </p>
- */
-public class MediaRouteDiscoveryFragment extends Fragment {
-    private final String ARGUMENT_SELECTOR = "selector";
-
-    private MediaRouter mRouter;
-    private MediaRouteSelector mSelector;
-    private MediaRouter.Callback mCallback;
-
-    public MediaRouteDiscoveryFragment() {
-    }
-
-    /**
-     * Gets the media router instance.
-     */
-    public MediaRouter getMediaRouter() {
-        ensureRouter();
-        return mRouter;
-    }
-
-    private void ensureRouter() {
-        if (mRouter == null) {
-            mRouter = MediaRouter.getInstance(getContext());
-        }
-    }
-
-    /**
-     * Gets the media route selector for filtering the routes to be discovered.
-     *
-     * @return The selector, never null.
-     */
-    public MediaRouteSelector getRouteSelector() {
-        ensureRouteSelector();
-        return mSelector;
-    }
-
-    /**
-     * Sets the media route selector for filtering the routes to be discovered.
-     * This method must be called before the fragment is added.
-     *
-     * @param selector The selector to set.
-     */
-    public void setRouteSelector(MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        ensureRouteSelector();
-        if (!mSelector.equals(selector)) {
-            mSelector = selector;
-
-            Bundle args = getArguments();
-            if (args == null) {
-                args = new Bundle();
-            }
-            args.putBundle(ARGUMENT_SELECTOR, selector.asBundle());
-            setArguments(args);
-
-            if (mCallback != null) {
-                mRouter.removeCallback(mCallback);
-                mRouter.addCallback(mSelector, mCallback, onPrepareCallbackFlags());
-            }
-        }
-    }
-
-    private void ensureRouteSelector() {
-        if (mSelector == null) {
-            Bundle args = getArguments();
-            if (args != null) {
-                mSelector = MediaRouteSelector.fromBundle(args.getBundle(ARGUMENT_SELECTOR));
-            }
-            if (mSelector == null) {
-                mSelector = MediaRouteSelector.EMPTY;
-            }
-        }
-    }
-
-    /**
-     * Called to create the {@link android.support.v7.media.MediaRouter.Callback callback}
-     * that will be registered.
-     * <p>
-     * The default callback does nothing.  The application may override this method to
-     * supply its own callback.
-     * </p>
-     *
-     * @return The new callback, or null if no callback should be registered.
-     */
-    public MediaRouter.Callback onCreateCallback() {
-        return new MediaRouter.Callback() { };
-    }
-
-    /**
-     * Called to prepare the callback flags that will be used when the
-     * {@link android.support.v7.media.MediaRouter.Callback callback} is registered.
-     * <p>
-     * The default implementation returns {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY}.
-     * </p>
-     *
-     * @return The desired callback flags.
-     */
-    public int onPrepareCallbackFlags() {
-        return MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        ensureRouteSelector();
-        ensureRouter();
-        mCallback = onCreateCallback();
-        if (mCallback != null) {
-            mRouter.addCallback(mSelector, mCallback, onPrepareCallbackFlags());
-        }
-    }
-
-    @Override
-    public void onStop() {
-        if (mCallback != null) {
-            mRouter.removeCallback(mCallback);
-            mCallback = null;
-        }
-
-        super.onStop();
-    }
-}
diff --git a/android/support/v7/app/MediaRouteExpandCollapseButton.java b/android/support/v7/app/MediaRouteExpandCollapseButton.java
deleted file mode 100644
index df0c944..0000000
--- a/android/support/v7/app/MediaRouteExpandCollapseButton.java
+++ /dev/null
@@ -1,92 +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.content.Context;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.AnimationDrawable;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.mediarouter.R;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageButton;
-
-/**
- * Chevron/Caret button to expand/collapse group volume list with animation.
- */
-class MediaRouteExpandCollapseButton extends ImageButton {
-    final AnimationDrawable mExpandAnimationDrawable;
-    final AnimationDrawable mCollapseAnimationDrawable;
-    final String mExpandGroupDescription;
-    final String mCollapseGroupDescription;
-    boolean mIsGroupExpanded;
-    OnClickListener mListener;
-
-    public MediaRouteExpandCollapseButton(Context context) {
-        this(context, null);
-    }
-
-    public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public MediaRouteExpandCollapseButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mExpandAnimationDrawable = (AnimationDrawable) ContextCompat.getDrawable(
-                context, R.drawable.mr_group_expand);
-        mCollapseAnimationDrawable = (AnimationDrawable) ContextCompat.getDrawable(
-                context, R.drawable.mr_group_collapse);
-
-        ColorFilter filter = new PorterDuffColorFilter(
-                MediaRouterThemeHelper.getControllerColor(context, defStyleAttr),
-                PorterDuff.Mode.SRC_IN);
-        mExpandAnimationDrawable.setColorFilter(filter);
-        mCollapseAnimationDrawable.setColorFilter(filter);
-
-        mExpandGroupDescription = context.getString(R.string.mr_controller_expand_group);
-        mCollapseGroupDescription = context.getString(R.string.mr_controller_collapse_group);
-
-        setImageDrawable(mExpandAnimationDrawable.getFrame(0));
-        setContentDescription(mExpandGroupDescription);
-
-        super.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIsGroupExpanded = !mIsGroupExpanded;
-                if (mIsGroupExpanded) {
-                    setImageDrawable(mExpandAnimationDrawable);
-                    mExpandAnimationDrawable.start();
-                    setContentDescription(mCollapseGroupDescription);
-                } else {
-                    setImageDrawable(mCollapseAnimationDrawable);
-                    mCollapseAnimationDrawable.start();
-                    setContentDescription(mExpandGroupDescription);
-                }
-                if (mListener != null) {
-                    mListener.onClick(view);
-                }
-            }
-        });
-    }
-
-    @Override
-    public void setOnClickListener(OnClickListener listener) {
-        mListener = listener;
-    }
-}
diff --git a/android/support/v7/app/MediaRouteVolumeSlider.java b/android/support/v7/app/MediaRouteVolumeSlider.java
deleted file mode 100644
index 4278028..0000000
--- a/android/support/v7/app/MediaRouteVolumeSlider.java
+++ /dev/null
@@ -1,99 +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.content.Context;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.v7.widget.AppCompatSeekBar;
-import android.util.AttributeSet;
-import android.util.Log;
-
-/**
- * Volume slider with showing, hiding, and applying alpha supports to the thumb.
- */
-class MediaRouteVolumeSlider extends AppCompatSeekBar {
-    private static final String TAG = "MediaRouteVolumeSlider";
-
-    private final float mDisabledAlpha;
-
-    private boolean mHideThumb;
-    private Drawable mThumb;
-    private int mColor;
-
-    public MediaRouteVolumeSlider(Context context) {
-        this(context, null);
-    }
-
-    public MediaRouteVolumeSlider(Context context, AttributeSet attrs) {
-        this(context, attrs, android.support.v7.appcompat.R.attr.seekBarStyle);
-    }
-
-    public MediaRouteVolumeSlider(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        int alpha = isEnabled() ? 0xFF : (int) (0xFF * mDisabledAlpha);
-
-        // The thumb drawable is a collection of drawables and its current drawables are changed per
-        // state. Apply the color filter and alpha on every state change.
-        mThumb.setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
-        mThumb.setAlpha(alpha);
-
-        getProgressDrawable().setColorFilter(mColor, PorterDuff.Mode.SRC_IN);
-        getProgressDrawable().setAlpha(alpha);
-    }
-
-    @Override
-    public void setThumb(Drawable thumb) {
-        mThumb = thumb;
-        super.setThumb(mHideThumb ? null : mThumb);
-    }
-
-    /**
-     * Sets whether to show or hide thumb.
-     */
-    public void setHideThumb(boolean hideThumb) {
-        if (mHideThumb == hideThumb) {
-            return;
-        }
-        mHideThumb = hideThumb;
-        super.setThumb(mHideThumb ? null : mThumb);
-    }
-
-    /**
-     * Sets the volume slider color. The change takes effect next time drawable state is changed.
-     * <p>
-     * The color cannot be translucent, otherwise the underlying progress bar will be seen through
-     * the thumb.
-     * </p>
-     */
-    public void setColor(int color) {
-        if (mColor == color) {
-            return;
-        }
-        if (Color.alpha(color) != 0xFF) {
-            Log.e(TAG, "Volume slider color cannot be translucent: #" + Integer.toHexString(color));
-        }
-        mColor = color;
-    }
-}
diff --git a/android/support/v7/app/MediaRouterThemeHelper.java b/android/support/v7/app/MediaRouterThemeHelper.java
deleted file mode 100644
index 69e40ac..0000000
--- a/android/support/v7/app/MediaRouterThemeHelper.java
+++ /dev/null
@@ -1,209 +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.v7.app;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.support.annotation.IntDef;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.mediarouter.R;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.View;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-final class MediaRouterThemeHelper {
-    private static final float MIN_CONTRAST = 3.0f;
-
-    @IntDef({COLOR_DARK_ON_LIGHT_BACKGROUND, COLOR_WHITE_ON_DARK_BACKGROUND})
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface ControllerColorType {}
-
-    static final int COLOR_DARK_ON_LIGHT_BACKGROUND = 0xDE000000; /* Opacity of 87% */
-    static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE;
-
-    private MediaRouterThemeHelper() {
-    }
-
-    static Context createThemedButtonContext(Context context) {
-        // Apply base Media Router theme.
-        context = new ContextThemeWrapper(context, getRouterThemeId(context));
-
-        // Apply custom Media Router theme.
-        int style = getThemeResource(context, R.attr.mediaRouteTheme);
-        if (style != 0) {
-            context = new ContextThemeWrapper(context, style);
-        }
-
-        return context;
-    }
-
-    /*
-     * The following two methods are to be used in conjunction. They should be used to prepare
-     * the context and theme for a super class constructor (the latter method relies on the
-     * former method to properly prepare the context):
-     *   super(context = createThemedDialogContext(context, theme),
-     *           createThemedDialogStyle(context));
-     *
-     * It will apply theme in the following order (style lookups will be done in reverse):
-     *   1) Current theme
-     *   2) Supplied theme
-     *   3) Base Media Router theme
-     *   4) Custom Media Router theme, if provided
-     */
-    static Context createThemedDialogContext(Context context, int theme, boolean alertDialog) {
-        // 1) Current theme is already applied to the context
-
-        // 2) If no theme is supplied, look it up from the context (dialogTheme/alertDialogTheme)
-        if (theme == 0) {
-            theme = getThemeResource(context, !alertDialog
-                    ? android.support.v7.appcompat.R.attr.dialogTheme
-                    : android.support.v7.appcompat.R.attr.alertDialogTheme);
-        }
-        //    Apply it
-        context = new ContextThemeWrapper(context, theme);
-
-        // 3) If a custom Media Router theme is provided then apply the base theme
-        if (getThemeResource(context, R.attr.mediaRouteTheme) != 0) {
-            context = new ContextThemeWrapper(context, getRouterThemeId(context));
-        }
-
-        return context;
-    }
-    // This method should be used in conjunction with the previous method.
-    static int createThemedDialogStyle(Context context) {
-        // 4) Apply the custom Media Router theme
-        int theme = getThemeResource(context, R.attr.mediaRouteTheme);
-        if (theme == 0) {
-            // 3) No custom MediaRouther theme was provided so apply the base theme instead
-            theme = getRouterThemeId(context);
-        }
-
-        return theme;
-    }
-    // END. Previous two methods should be used in conjunction.
-
-    static int getThemeResource(Context context, int attr) {
-        TypedValue value = new TypedValue();
-        return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
-    }
-
-    static float getDisabledAlpha(Context context) {
-        TypedValue value = new TypedValue();
-        return context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true)
-                ? value.getFloat() : 0.5f;
-    }
-
-    static @ControllerColorType int getControllerColor(Context context, int style) {
-        int primaryColor = getThemeColor(context, style,
-                android.support.v7.appcompat.R.attr.colorPrimary);
-        if (ColorUtils.calculateContrast(COLOR_WHITE_ON_DARK_BACKGROUND, primaryColor)
-                >= MIN_CONTRAST) {
-            return COLOR_WHITE_ON_DARK_BACKGROUND;
-        }
-        return COLOR_DARK_ON_LIGHT_BACKGROUND;
-    }
-
-    static int getButtonTextColor(Context context) {
-        int primaryColor = getThemeColor(context, 0,
-                android.support.v7.appcompat.R.attr.colorPrimary);
-        int backgroundColor = getThemeColor(context, 0, android.R.attr.colorBackground);
-
-        if (ColorUtils.calculateContrast(primaryColor, backgroundColor) < MIN_CONTRAST) {
-            // Default to colorAccent if the contrast ratio is low.
-            return getThemeColor(context, 0, android.support.v7.appcompat.R.attr.colorAccent);
-        }
-        return primaryColor;
-    }
-
-    static void setMediaControlsBackgroundColor(
-            Context context, View mainControls, View groupControls, boolean hasGroup) {
-        int primaryColor = getThemeColor(context, 0,
-                android.support.v7.appcompat.R.attr.colorPrimary);
-        int primaryDarkColor = getThemeColor(context, 0,
-                android.support.v7.appcompat.R.attr.colorPrimaryDark);
-        if (hasGroup && getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-            // Instead of showing dark controls in a possibly dark (i.e. the primary dark), model
-            // the white dialog and use the primary color for the group controls.
-            primaryDarkColor = primaryColor;
-            primaryColor = Color.WHITE;
-        }
-        mainControls.setBackgroundColor(primaryColor);
-        groupControls.setBackgroundColor(primaryDarkColor);
-        // Also store the background colors to the view tags. They are used in
-        // setVolumeSliderColor() below.
-        mainControls.setTag(primaryColor);
-        groupControls.setTag(primaryDarkColor);
-    }
-
-    static void setVolumeSliderColor(
-            Context context, MediaRouteVolumeSlider volumeSlider, View backgroundView) {
-        int controllerColor = getControllerColor(context, 0);
-        if (Color.alpha(controllerColor) != 0xFF) {
-            // Composite with the background in order not to show the underlying progress bar
-            // through the thumb.
-            int backgroundColor = (int) backgroundView.getTag();
-            controllerColor = ColorUtils.compositeColors(controllerColor, backgroundColor);
-        }
-        volumeSlider.setColor(controllerColor);
-    }
-
-    private static boolean isLightTheme(Context context) {
-        TypedValue value = new TypedValue();
-        return context.getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.isLightTheme,
-                value, true) && value.data != 0;
-    }
-
-    private static int getThemeColor(Context context, int style, int attr) {
-        if (style != 0) {
-            int[] attrs = { attr };
-            TypedArray ta = context.obtainStyledAttributes(style, attrs);
-            int color = ta.getColor(0, 0);
-            ta.recycle();
-            if (color != 0) {
-                return color;
-            }
-        }
-        TypedValue value = new TypedValue();
-        context.getTheme().resolveAttribute(attr, value, true);
-        if (value.resourceId != 0) {
-            return context.getResources().getColor(value.resourceId);
-        }
-        return value.data;
-    }
-
-    private static int getRouterThemeId(Context context) {
-        int themeId;
-        if (isLightTheme(context)) {
-            if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-                themeId = R.style.Theme_MediaRouter_Light;
-            } else {
-                themeId = R.style.Theme_MediaRouter_Light_DarkControlPanel;
-            }
-        } else {
-            if (getControllerColor(context, 0) == COLOR_DARK_ON_LIGHT_BACKGROUND) {
-                themeId = R.style.Theme_MediaRouter_LightControlPanel;
-            } else {
-                themeId = R.style.Theme_MediaRouter;
-            }
-        }
-        return themeId;
-    }
-}
diff --git a/android/support/v7/app/NavItemSelectedListener.java b/android/support/v7/app/NavItemSelectedListener.java
deleted file mode 100644
index 39b5bfe..0000000
--- a/android/support/v7/app/NavItemSelectedListener.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.v7.app;
-
-import android.view.View;
-import android.widget.AdapterView;
-
-/**
- * Wrapper to adapt the ActionBar.OnNavigationListener in an AdapterView.OnItemSelectedListener
- * for use in Spinner widgets. Used by action bar implementations.
- */
-class NavItemSelectedListener implements AdapterView.OnItemSelectedListener {
-    private final ActionBar.OnNavigationListener mListener;
-
-    public NavItemSelectedListener(ActionBar.OnNavigationListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-        if (mListener != null) {
-            mListener.onNavigationItemSelected(position, id);
-        }
-    }
-
-    @Override
-    public void onNothingSelected(AdapterView<?> parent) {
-        // Do nothing
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/app/OverlayListView.java b/android/support/v7/app/OverlayListView.java
deleted file mode 100644
index ef322fd..0000000
--- a/android/support/v7/app/OverlayListView.java
+++ /dev/null
@@ -1,265 +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.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.util.AttributeSet;
-import android.view.animation.Interpolator;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * A ListView which has an additional overlay layer. {@link BitmapDrawable}
- * can be added to the layer and can be animated.
- */
-final class OverlayListView extends ListView {
-    private final List<OverlayObject> mOverlayObjects = new ArrayList<>();
-
-    public OverlayListView(Context context) {
-        super(context);
-    }
-
-    public OverlayListView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public OverlayListView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    /**
-     * Adds an object to the overlay layer.
-     *
-     * @param object An object to be added.
-     */
-    public void addOverlayObject(OverlayObject object) {
-        mOverlayObjects.add(object);
-    }
-
-    /**
-     * Starts all animations of objects in the overlay layer.
-     */
-    public void startAnimationAll() {
-        for (OverlayObject object : mOverlayObjects) {
-            if (!object.isAnimationStarted()) {
-                object.startAnimation(getDrawingTime());
-            }
-        }
-    }
-
-    /**
-     * Stops all animations of objects in the overlay layer.
-     */
-    public void stopAnimationAll() {
-        for (OverlayObject object : mOverlayObjects) {
-            object.stopAnimation();
-        }
-    }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        if (mOverlayObjects.size() > 0) {
-            Iterator<OverlayObject> it = mOverlayObjects.iterator();
-            while (it.hasNext()) {
-                OverlayObject object = it.next();
-                BitmapDrawable bitmap = object.getBitmapDrawable();
-                if (bitmap != null) {
-                    bitmap.draw(canvas);
-                }
-                if (!object.update(getDrawingTime())) {
-                    it.remove();
-                }
-            }
-        }
-    }
-
-    /**
-     * A class that represents an object to be shown in the overlay layer.
-     */
-    public static class OverlayObject {
-        private BitmapDrawable mBitmap;
-        private float mCurrentAlpha = 1.0f;
-        private Rect mCurrentBounds;
-        private Interpolator mInterpolator;
-        private long mDuration;
-        private Rect mStartRect;
-        private int mDeltaY;
-        private float mStartAlpha = 1.0f;
-        private float mEndAlpha = 1.0f;
-        private long mStartTime;
-        private boolean mIsAnimationStarted;
-        private boolean mIsAnimationEnded;
-        private OnAnimationEndListener mListener;
-
-        public OverlayObject(BitmapDrawable bitmap, Rect startRect) {
-            mBitmap = bitmap;
-            mStartRect = startRect;
-            mCurrentBounds = new Rect(startRect);
-            if (mBitmap != null && mCurrentBounds != null) {
-                mBitmap.setAlpha((int) (mCurrentAlpha * 255));
-                mBitmap.setBounds(mCurrentBounds);
-            }
-        }
-
-        /**
-         * Returns the bitmap that this object represents.
-         *
-         * @return BitmapDrawable that this object has.
-         */
-        public BitmapDrawable getBitmapDrawable() {
-            return mBitmap;
-        }
-
-        /**
-         * Returns the started status of the animation.
-         *
-         * @return True if the animation has started, false otherwise.
-         */
-        public boolean isAnimationStarted() {
-            return mIsAnimationStarted;
-        }
-
-        /**
-         * Sets animation for varying alpha.
-         *
-         * @param startAlpha Starting alpha value for the animation, where 1.0 means
-         * fully opaque and 0.0 means fully transparent.
-         * @param endAlpha Ending alpha value for the animation.
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setAlphaAnimation(float startAlpha, float endAlpha) {
-            mStartAlpha = startAlpha;
-            mEndAlpha = endAlpha;
-            return this;
-        }
-
-        /**
-         * Sets animation for moving objects vertically.
-         *
-         * @param deltaY Distance to move in pixels.
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setTranslateYAnimation(int deltaY) {
-            mDeltaY = deltaY;
-            return this;
-        }
-
-        /**
-         * Sets how long the animation will last.
-         *
-         * @param duration Duration in milliseconds
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setDuration(long duration) {
-            mDuration = duration;
-            return this;
-        }
-
-        /**
-         * Sets the acceleration curve for this animation.
-         *
-         * @param interpolator The interpolator which defines the acceleration curve
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setInterpolator(Interpolator interpolator) {
-            mInterpolator = interpolator;
-            return this;
-        }
-
-        /**
-         * Binds an animation end listener to the animation.
-         *
-         * @param listener the animation end listener to be notified.
-         * @return This OverlayObject to allow for chaining of calls.
-         */
-        public OverlayObject setAnimationEndListener(OnAnimationEndListener listener) {
-            mListener = listener;
-            return this;
-        }
-
-        /**
-         * Starts the animation and sets the start time.
-         *
-         * @param startTime Start time to be set in Millis
-         */
-        public void startAnimation(long startTime) {
-            mStartTime = startTime;
-            mIsAnimationStarted = true;
-        }
-
-        /**
-         * Stops the animation.
-         */
-        public void stopAnimation() {
-            mIsAnimationStarted = true;
-            mIsAnimationEnded = true;
-            if (mListener != null) {
-                mListener.onAnimationEnd();
-            }
-        }
-
-        /**
-         * Calculates and updates current bounds and alpha value.
-         *
-         * @param currentTime Current time.in millis
-         */
-        public boolean update(long currentTime) {
-            if (mIsAnimationEnded) {
-                return false;
-            }
-            float normalizedTime = (currentTime - mStartTime) / (float) mDuration;
-            normalizedTime = Math.max(0.0f, Math.min(1.0f, normalizedTime));
-            if (!mIsAnimationStarted) {
-                normalizedTime = 0.0f;
-            }
-            float interpolatedTime = (mInterpolator == null) ? normalizedTime
-                    : mInterpolator.getInterpolation(normalizedTime);
-            int deltaY = (int) (mDeltaY * interpolatedTime);
-            mCurrentBounds.top = mStartRect.top + deltaY;
-            mCurrentBounds.bottom = mStartRect.bottom + deltaY;
-            mCurrentAlpha = mStartAlpha + (mEndAlpha - mStartAlpha) * interpolatedTime;
-            if (mBitmap != null && mCurrentBounds != null) {
-                mBitmap.setAlpha((int) (mCurrentAlpha * 255));
-                mBitmap.setBounds(mCurrentBounds);
-            }
-            if (mIsAnimationStarted && normalizedTime >= 1.0f) {
-                mIsAnimationEnded = true;
-                if (mListener != null) {
-                    mListener.onAnimationEnd();
-                }
-            }
-            return !mIsAnimationEnded;
-        }
-
-        /**
-         * An animation listener that receives notifications when the animation ends.
-         */
-        public interface OnAnimationEndListener {
-            /**
-             * Notifies the end of the animation.
-             */
-            public void onAnimationEnd();
-        }
-    }
-}
diff --git a/android/support/v7/app/ResourcesFlusher.java b/android/support/v7/app/ResourcesFlusher.java
deleted file mode 100644
index 8454078..0000000
--- a/android/support/v7/app/ResourcesFlusher.java
+++ /dev/null
@@ -1,207 +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.support.annotation.RequiresApi;
-import android.content.res.Resources;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.util.LongSparseArray;
-
-import java.lang.reflect.Field;
-import java.util.Map;
-
-class ResourcesFlusher {
-    private static final String TAG = "ResourcesFlusher";
-
-    private static Field sDrawableCacheField;
-    private static boolean sDrawableCacheFieldFetched;
-
-    private static Class sThemedResourceCacheClazz;
-    private static boolean sThemedResourceCacheClazzFetched;
-
-    private static Field sThemedResourceCache_mUnthemedEntriesField;
-    private static boolean sThemedResourceCache_mUnthemedEntriesFieldFetched;
-
-    private static Field sResourcesImplField;
-    private static boolean sResourcesImplFieldFetched;
-
-    static boolean flush(@NonNull final Resources resources) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return flushNougats(resources);
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return flushMarshmallows(resources);
-        } 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 {
-                sDrawableCacheField = Resources.class.getDeclaredField("mDrawableCache");
-                sDrawableCacheField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "Could not retrieve Resources#mDrawableCache field", e);
-            }
-            sDrawableCacheFieldFetched = true;
-        }
-        if (sDrawableCacheField != null) {
-            Map drawableCache = null;
-            try {
-                drawableCache = (Map) sDrawableCacheField.get(resources);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Could not retrieve value from Resources#mDrawableCache", e);
-            }
-            if (drawableCache != null) {
-                drawableCache.clear();
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @RequiresApi(23)
-    private static boolean flushMarshmallows(@NonNull final Resources resources) {
-        if (!sDrawableCacheFieldFetched) {
-            try {
-                sDrawableCacheField = Resources.class.getDeclaredField("mDrawableCache");
-                sDrawableCacheField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "Could not retrieve Resources#mDrawableCache field", e);
-            }
-            sDrawableCacheFieldFetched = true;
-        }
-
-        Object drawableCache = null;
-        if (sDrawableCacheField != null) {
-            try {
-                drawableCache = sDrawableCacheField.get(resources);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Could not retrieve value from Resources#mDrawableCache", e);
-            }
-        }
-
-        if (drawableCache == null) {
-            // If there is no drawable cache, there's nothing to flush...
-            return false;
-        }
-
-        return drawableCache != null && flushThemedResourcesCache(drawableCache);
-    }
-
-    @RequiresApi(24)
-    private static boolean flushNougats(@NonNull final Resources resources) {
-        if (!sResourcesImplFieldFetched) {
-            try {
-                sResourcesImplField = Resources.class.getDeclaredField("mResourcesImpl");
-                sResourcesImplField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "Could not retrieve Resources#mResourcesImpl field", e);
-            }
-            sResourcesImplFieldFetched = true;
-        }
-
-        if (sResourcesImplField == null) {
-            // If the mResourcesImpl field isn't available, bail out now
-            return false;
-        }
-
-        Object resourcesImpl = null;
-        try {
-            resourcesImpl = sResourcesImplField.get(resources);
-        } catch (IllegalAccessException e) {
-            Log.e(TAG, "Could not retrieve value from Resources#mResourcesImpl", e);
-        }
-
-        if (resourcesImpl == null) {
-            // If there is no impl instance, bail out now
-            return false;
-        }
-
-        if (!sDrawableCacheFieldFetched) {
-            try {
-                sDrawableCacheField = resourcesImpl.getClass().getDeclaredField("mDrawableCache");
-                sDrawableCacheField.setAccessible(true);
-            } catch (NoSuchFieldException e) {
-                Log.e(TAG, "Could not retrieve ResourcesImpl#mDrawableCache field", e);
-            }
-            sDrawableCacheFieldFetched = true;
-        }
-
-        Object drawableCache = null;
-        if (sDrawableCacheField != null) {
-            try {
-                drawableCache = sDrawableCacheField.get(resourcesImpl);
-            } catch (IllegalAccessException e) {
-                Log.e(TAG, "Could not retrieve value from ResourcesImpl#mDrawableCache", e);
-            }
-        }
-
-        return drawableCache != null && flushThemedResourcesCache(drawableCache);
-    }
-
-    @RequiresApi(16)
-    private static boolean flushThemedResourcesCache(@NonNull final Object cache) {
-        if (!sThemedResourceCacheClazzFetched) {
-            try {
-                sThemedResourceCacheClazz = Class.forName("android.content.res.ThemedResourceCache");
-            } catch (ClassNotFoundException e) {
-                Log.e(TAG, "Could not find ThemedResourceCache class", e);
-            }
-            sThemedResourceCacheClazzFetched = true;
-        }
-
-        if (sThemedResourceCacheClazz == null) {
-            // If the ThemedResourceCache class isn't available, bail out now
-            return false;
-        }
-
-        if (!sThemedResourceCache_mUnthemedEntriesFieldFetched) {
-            try {
-                sThemedResourceCache_mUnthemedEntriesField =
-                        sThemedResourceCacheClazz.getDeclaredField("mUnthemedEntries");
-                sThemedResourceCache_mUnthemedEntriesField.setAccessible(true);
-            } catch (NoSuchFieldException ee) {
-                Log.e(TAG, "Could not retrieve ThemedResourceCache#mUnthemedEntries field", ee);
-            }
-            sThemedResourceCache_mUnthemedEntriesFieldFetched = true;
-        }
-
-        if (sThemedResourceCache_mUnthemedEntriesField == null) {
-            // Didn't get mUnthemedEntries field, bail out...
-            return false;
-        }
-
-        LongSparseArray unthemedEntries = null;
-        try {
-            unthemedEntries = (LongSparseArray)
-                    sThemedResourceCache_mUnthemedEntriesField.get(cache);
-        } catch (IllegalAccessException e) {
-            Log.e(TAG, "Could not retrieve value from ThemedResourceCache#mUnthemedEntries", e);
-        }
-
-        if (unthemedEntries != null) {
-            unthemedEntries.clear();
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/android/support/v7/app/ToolbarActionBar.java b/android/support/v7/app/ToolbarActionBar.java
deleted file mode 100644
index 473f1b5..0000000
--- a/android/support/v7/app/ToolbarActionBar.java
+++ /dev/null
@@ -1,602 +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.app;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.view.WindowCallbackWrapper;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPresenter;
-import android.support.v7.widget.DecorToolbar;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.ToolbarWidgetWrapper;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.SpinnerAdapter;
-
-import java.util.ArrayList;
-
-class ToolbarActionBar extends ActionBar {
-    DecorToolbar mDecorToolbar;
-    boolean mToolbarMenuPrepared;
-    Window.Callback mWindowCallback;
-    private boolean mMenuCallbackSet;
-
-    private boolean mLastMenuVisibility;
-    private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners = new ArrayList<>();
-
-    private final Runnable mMenuInvalidator = new Runnable() {
-        @Override
-        public void run() {
-            populateOptionsMenu();
-        }
-    };
-
-    private final Toolbar.OnMenuItemClickListener mMenuClicker =
-            new Toolbar.OnMenuItemClickListener() {
-                @Override
-                public boolean onMenuItemClick(MenuItem item) {
-                    return mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
-                }
-            };
-
-    ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
-        mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
-        mWindowCallback = new ToolbarCallbackWrapper(windowCallback);
-        mDecorToolbar.setWindowCallback(mWindowCallback);
-        toolbar.setOnMenuItemClickListener(mMenuClicker);
-        mDecorToolbar.setWindowTitle(title);
-    }
-
-    public Window.Callback getWrappedWindowCallback() {
-        return mWindowCallback;
-    }
-
-    @Override
-    public void setCustomView(View view) {
-        setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
-    }
-
-    @Override
-    public void setCustomView(View view, LayoutParams layoutParams) {
-        if (view != null) {
-            view.setLayoutParams(layoutParams);
-        }
-        mDecorToolbar.setCustomView(view);
-    }
-
-    @Override
-    public void setCustomView(int resId) {
-        final LayoutInflater inflater = LayoutInflater.from(mDecorToolbar.getContext());
-        setCustomView(inflater.inflate(resId, mDecorToolbar.getViewGroup(), false));
-    }
-
-    @Override
-    public void setIcon(int resId) {
-        mDecorToolbar.setIcon(resId);
-    }
-
-    @Override
-    public void setIcon(Drawable icon) {
-        mDecorToolbar.setIcon(icon);
-    }
-
-    @Override
-    public void setLogo(int resId) {
-        mDecorToolbar.setLogo(resId);
-    }
-
-    @Override
-    public void setLogo(Drawable logo) {
-        mDecorToolbar.setLogo(logo);
-    }
-
-    @Override
-    public void setStackedBackgroundDrawable(Drawable d) {
-        // This space for rent (do nothing)
-    }
-
-    @Override
-    public void setSplitBackgroundDrawable(Drawable d) {
-        // This space for rent (do nothing)
-    }
-
-    @Override
-    public void setHomeButtonEnabled(boolean enabled) {
-        // If the nav button on a Toolbar is present, it's enabled. No-op.
-    }
-
-    @Override
-    public void setElevation(float elevation) {
-        ViewCompat.setElevation(mDecorToolbar.getViewGroup(), elevation);
-    }
-
-    @Override
-    public float getElevation() {
-        return ViewCompat.getElevation(mDecorToolbar.getViewGroup());
-    }
-
-    @Override
-    public Context getThemedContext() {
-        return mDecorToolbar.getContext();
-    }
-
-    @Override
-    public boolean isTitleTruncated() {
-        return super.isTitleTruncated();
-    }
-
-    @Override
-    public void setHomeAsUpIndicator(Drawable indicator) {
-        mDecorToolbar.setNavigationIcon(indicator);
-    }
-
-    @Override
-    public void setHomeAsUpIndicator(int resId) {
-        mDecorToolbar.setNavigationIcon(resId);
-    }
-
-    @Override
-    public void setHomeActionContentDescription(CharSequence description) {
-        mDecorToolbar.setNavigationContentDescription(description);
-    }
-
-    @Override
-    public void setDefaultDisplayHomeAsUpEnabled(boolean enabled) {
-        // Do nothing
-    }
-
-    @Override
-    public void setHomeActionContentDescription(int resId) {
-        mDecorToolbar.setNavigationContentDescription(resId);
-    }
-
-    @Override
-    public void setShowHideAnimationEnabled(boolean enabled) {
-        // This space for rent; no-op.
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration config) {
-        super.onConfigurationChanged(config);
-    }
-
-    @Override
-    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
-        mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
-    }
-
-    @Override
-    public void setSelectedNavigationItem(int position) {
-        switch (mDecorToolbar.getNavigationMode()) {
-            case NAVIGATION_MODE_LIST:
-                mDecorToolbar.setDropdownSelectedPosition(position);
-                break;
-            default:
-                throw new IllegalStateException(
-                        "setSelectedNavigationIndex not valid for current navigation mode");
-        }
-    }
-
-    @Override
-    public int getSelectedNavigationIndex() {
-        return -1;
-    }
-
-    @Override
-    public int getNavigationItemCount() {
-        return 0;
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mDecorToolbar.setTitle(title);
-    }
-
-    @Override
-    public void setTitle(int resId) {
-        mDecorToolbar.setTitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
-    }
-
-    @Override
-    public void setWindowTitle(CharSequence title) {
-        mDecorToolbar.setWindowTitle(title);
-    }
-
-    @Override
-    public boolean requestFocus() {
-        final ViewGroup viewGroup = mDecorToolbar.getViewGroup();
-        if (viewGroup != null && !viewGroup.hasFocus()) {
-            viewGroup.requestFocus();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void setSubtitle(CharSequence subtitle) {
-        mDecorToolbar.setSubtitle(subtitle);
-    }
-
-    @Override
-    public void setSubtitle(int resId) {
-        mDecorToolbar.setSubtitle(resId != 0 ? mDecorToolbar.getContext().getText(resId) : null);
-    }
-
-    @SuppressLint("WrongConstant")
-    @Override
-    public void setDisplayOptions(@DisplayOptions int options) {
-        setDisplayOptions(options, 0xffffffff);
-    }
-
-    @Override
-    public void setDisplayOptions(@DisplayOptions int options, @DisplayOptions int mask) {
-        final int currentOptions = mDecorToolbar.getDisplayOptions();
-        mDecorToolbar.setDisplayOptions((options & mask) | (currentOptions & ~mask));
-    }
-
-    @Override
-    public void setDisplayUseLogoEnabled(boolean useLogo) {
-        setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
-    }
-
-    @Override
-    public void setDisplayShowHomeEnabled(boolean showHome) {
-        setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
-    }
-
-    @Override
-    public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
-        setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
-    }
-
-    @Override
-    public void setDisplayShowTitleEnabled(boolean showTitle) {
-        setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
-    }
-
-    @Override
-    public void setDisplayShowCustomEnabled(boolean showCustom) {
-        setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
-    }
-
-    @Override
-    public void setBackgroundDrawable(@Nullable Drawable d) {
-        mDecorToolbar.setBackgroundDrawable(d);
-    }
-
-    @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 NAVIGATION_MODE_STANDARD;
-    }
-
-    @Override
-    public void setNavigationMode(@NavigationMode int mode) {
-        if (mode == ActionBar.NAVIGATION_MODE_TABS) {
-            throw new IllegalArgumentException("Tabs not supported in this configuration");
-        }
-        mDecorToolbar.setNavigationMode(mode);
-    }
-
-    @Override
-    public int getDisplayOptions() {
-        return mDecorToolbar.getDisplayOptions();
-    }
-
-    @Override
-    public Tab newTab() {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void addTab(Tab tab) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void addTab(Tab tab, boolean setSelected) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void addTab(Tab tab, int position) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void addTab(Tab tab, int position, boolean setSelected) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void removeTab(Tab tab) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void removeTabAt(int position) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void removeAllTabs() {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public void selectTab(Tab tab) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public Tab getSelectedTab() {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public Tab getTabAt(int index) {
-        throw new UnsupportedOperationException(
-                "Tabs are not supported in toolbar action bars");
-    }
-
-    @Override
-    public int getTabCount() {
-        return 0;
-    }
-
-    @Override
-    public int getHeight() {
-        return mDecorToolbar.getHeight();
-    }
-
-    @Override
-    public void show() {
-        // TODO: Consider a better transition for this.
-        // Right now use no automatic transition so that the app can supply one if desired.
-        mDecorToolbar.setVisibility(View.VISIBLE);
-    }
-
-    @Override
-    public void hide() {
-        // TODO: Consider a better transition for this.
-        // Right now use no automatic transition so that the app can supply one if desired.
-        mDecorToolbar.setVisibility(View.GONE);
-    }
-
-    @Override
-    public boolean isShowing() {
-        return mDecorToolbar.getVisibility() == View.VISIBLE;
-    }
-
-    @Override
-    public boolean openOptionsMenu() {
-        return mDecorToolbar.showOverflowMenu();
-    }
-
-    @Override
-    public boolean closeOptionsMenu() {
-        return mDecorToolbar.hideOverflowMenu();
-    }
-
-    @Override
-    public boolean invalidateOptionsMenu() {
-        mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator);
-        ViewCompat.postOnAnimation(mDecorToolbar.getViewGroup(), mMenuInvalidator);
-        return true;
-    }
-
-    @Override
-    public boolean collapseActionView() {
-        if (mDecorToolbar.hasExpandedActionView()) {
-            mDecorToolbar.collapseActionView();
-            return true;
-        }
-        return false;
-    }
-
-    void populateOptionsMenu() {
-        final Menu menu = getMenu();
-        final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
-        if (mb != null) {
-            mb.stopDispatchingItemsChanged();
-        }
-        try {
-            menu.clear();
-            if (!mWindowCallback.onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) ||
-                    !mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
-                menu.clear();
-            }
-        } finally {
-            if (mb != null) {
-                mb.startDispatchingItemsChanged();
-            }
-        }
-    }
-
-    @Override
-    public boolean onMenuKeyEvent(KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_UP) {
-            openOptionsMenu();
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onKeyShortcut(int keyCode, KeyEvent ev) {
-        Menu menu = getMenu();
-        if (menu != null) {
-            final KeyCharacterMap kmap = KeyCharacterMap.load(
-                    ev != null ? ev.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
-            menu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
-            return menu.performShortcut(keyCode, ev, 0);
-        }
-        return false;
-    }
-
-    @Override
-    void onDestroy() {
-        // Remove any invalidation callbacks
-        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;
-        }
-        mLastMenuVisibility = isVisible;
-
-        final int count = mMenuVisibilityListeners.size();
-        for (int i = 0; i < count; i++) {
-            mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
-        }
-    }
-
-    private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
-        public ToolbarCallbackWrapper(Window.Callback wrapped) {
-            super(wrapped);
-        }
-
-        @Override
-        public boolean onPreparePanel(int featureId, View view, Menu menu) {
-            final boolean result = super.onPreparePanel(featureId, view, menu);
-            if (result && !mToolbarMenuPrepared) {
-                mDecorToolbar.setMenuPrepared();
-                mToolbarMenuPrepared = true;
-            }
-            return result;
-        }
-
-        @Override
-        public View onCreatePanelView(int featureId) {
-            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);
-        }
-    }
-
-    private Menu getMenu() {
-        if (!mMenuCallbackSet) {
-            mDecorToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(),
-                    new MenuBuilderCallback());
-            mMenuCallbackSet = true;
-        }
-        return mDecorToolbar.getMenu();
-    }
-
-    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
-        private boolean mClosingActionMenu;
-
-        ActionMenuPresenterCallback() {
-        }
-
-        @Override
-        public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            if (mWindowCallback != null) {
-                mWindowCallback.onMenuOpened(AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR, subMenu);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-            if (mClosingActionMenu) {
-                return;
-            }
-
-            mClosingActionMenu = true;
-            mDecorToolbar.dismissPopupMenus();
-            if (mWindowCallback != null) {
-                mWindowCallback.onPanelClosed(AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR, menu);
-            }
-            mClosingActionMenu = false;
-        }
-    }
-
-    private final class MenuBuilderCallback implements MenuBuilder.Callback {
-
-        MenuBuilderCallback() {
-        }
-
-        @Override
-        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-            return false;
-        }
-
-        @Override
-        public void onMenuModeChange(MenuBuilder menu) {
-            if (mWindowCallback != null) {
-                if (mDecorToolbar.isOverflowMenuShowing()) {
-                    mWindowCallback.onPanelClosed(AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR, menu);
-                } else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL,
-                        null, menu)) {
-                    mWindowCallback.onMenuOpened(AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR, menu);
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v7/app/TwilightCalculator.java b/android/support/v7/app/TwilightCalculator.java
deleted file mode 100644
index 02fe5e8..0000000
--- a/android/support/v7/app/TwilightCalculator.java
+++ /dev/null
@@ -1,140 +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.text.format.DateUtils;
-
-/**
- * Imported from frameworks/base/services/core/java/com/android/server/TwilightCalculator.java
- *
- * <p>Calculates the sunrise and sunsets times for a given location.</p>
- */
-class TwilightCalculator {
-
-    private static TwilightCalculator sInstance;
-
-    static TwilightCalculator getInstance() {
-        if (sInstance == null) {
-            sInstance = new TwilightCalculator();
-        }
-        return sInstance;
-    }
-
-    /** Value of {@link #state} if it is currently day */
-    public static final int DAY = 0;
-
-    /** Value of {@link #state} if it is currently night */
-    public static final int NIGHT = 1;
-
-    private static final float DEGREES_TO_RADIANS = (float) (Math.PI / 180.0f);
-
-    // element for calculating solar transit.
-    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.
-    private static final float C1 = 0.0334196f;
-    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.
-    private static final long UTC_2000 = 946728000000L;
-
-    /**
-     * Time of sunset (civil twilight) in milliseconds or -1 in the case the day
-     * or night never ends.
-     */
-    public long sunset;
-
-    /**
-     * Time of sunrise (civil twilight) in milliseconds or -1 in the case the
-     * day or night never ends.
-     */
-    public long sunrise;
-
-    /**
-     * Current state
-     */
-    public int state;
-
-    /**
-     * calculates the civil twilight bases on time and geo-coordinates.
-     *
-     * @param time time in milliseconds.
-     * @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;
-
-        // mean anomaly
-        final float meanAnomaly = 6.240059968f + daysSince2000 * 0.01720197f;
-
-        // true anomaly
-        final double trueAnomaly = meanAnomaly + C1 * Math.sin(meanAnomaly) + C2
-                * Math.sin(2 * meanAnomaly) + C3 * Math.sin(3 * meanAnomaly);
-
-        // ecliptic longitude
-        final double solarLng = trueAnomaly + 1.796593063d + Math.PI;
-
-        // solar transit in days since 2000
-        final double arcLongitude = -longitude / 360;
-        float n = Math.round(daysSince2000 - J0 - arcLongitude);
-        double solarTransitJ2000 = n + J0 + arcLongitude + 0.0053d * Math.sin(meanAnomaly)
-                + -0.0069d * Math.sin(2 * solarLng);
-
-        // declination of sun
-        double solarDec = Math.asin(Math.sin(solarLng) * Math.sin(OBLIQUITY));
-
-        final double latRad = latitude * DEGREES_TO_RADIANS;
-
-        double cosHourAngle = (Math.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
-                * Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec));
-        // The day or night never ends for the given date and location, if this value is out of
-        // range.
-        if (cosHourAngle >= 1) {
-            state = NIGHT;
-            sunset = -1;
-            sunrise = -1;
-            return;
-        } else if (cosHourAngle <= -1) {
-            state = DAY;
-            sunset = -1;
-            sunrise = -1;
-            return;
-        }
-
-        float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI));
-
-        sunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
-        sunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
-
-        if (sunrise < time && sunset > time) {
-            state = DAY;
-        } else {
-            state = NIGHT;
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/android/support/v7/app/TwilightManager.java b/android/support/v7/app/TwilightManager.java
deleted file mode 100644
index e574e1a..0000000
--- a/android/support/v7/app/TwilightManager.java
+++ /dev/null
@@ -1,207 +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 static android.Manifest.permission.ACCESS_COARSE_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.location.Location;
-import android.location.LocationManager;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresPermission;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.content.PermissionChecker;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import java.util.Calendar;
-
-/**
- * Class which managing whether we are in the night or not.
- */
-class TwilightManager {
-
-    private static final String TAG = "TwilightManager";
-
-    private static final int SUNRISE = 6; // 6am
-    private static final int SUNSET = 22; // 10pm
-
-    private static TwilightManager sInstance;
-
-    static TwilightManager getInstance(@NonNull Context context) {
-        if (sInstance == null) {
-            context = context.getApplicationContext();
-            sInstance = new TwilightManager(context,
-                    (LocationManager) context.getSystemService(Context.LOCATION_SERVICE));
-        }
-        return sInstance;
-    }
-
-    @VisibleForTesting
-    static void setInstance(TwilightManager twilightManager) {
-        sInstance = twilightManager;
-    }
-
-    private final Context mContext;
-    private final LocationManager mLocationManager;
-
-    private final TwilightState mTwilightState = new TwilightState();
-
-    @VisibleForTesting
-    TwilightManager(@NonNull Context context, @NonNull LocationManager locationManager) {
-        mContext = context;
-        mLocationManager = locationManager;
-    }
-
-    /**
-     * Returns true we are currently in the 'night'.
-     *
-     * @return true if we are at night, false if the day.
-     */
-    boolean isNight() {
-        final TwilightState state = mTwilightState;
-
-        if (isStateValid()) {
-            // If the current twilight state is still valid, use it
-            return state.isNight;
-        }
-
-        // Else, we will try and grab the last known location
-        final Location location = getLastKnownLocation();
-        if (location != null) {
-            updateState(location);
-            return state.isNight;
-        }
-
-        Log.i(TAG, "Could not get last known location. This is probably because the app does not"
-                + " have any location permissions. Falling back to hardcoded"
-                + " sunrise/sunset values.");
-
-        // If we don't have a location, we'll use our hardcoded sunrise/sunset values.
-        // These aren't great, but it's better than nothing.
-        Calendar calendar = Calendar.getInstance();
-        final int hour = calendar.get(Calendar.HOUR_OF_DAY);
-        return hour < SUNRISE || hour >= SUNSET;
-    }
-
-    @SuppressLint("MissingPermission") // permissions are checked for the needed call.
-    private Location getLastKnownLocation() {
-        Location coarseLoc = null;
-        Location fineLoc = null;
-
-        int permission = PermissionChecker.checkSelfPermission(mContext,
-                Manifest.permission.ACCESS_COARSE_LOCATION);
-        if (permission == PermissionChecker.PERMISSION_GRANTED) {
-            coarseLoc = getLastKnownLocationForProvider(LocationManager.NETWORK_PROVIDER);
-        }
-
-        permission = PermissionChecker.checkSelfPermission(mContext,
-                Manifest.permission.ACCESS_FINE_LOCATION);
-        if (permission == PermissionChecker.PERMISSION_GRANTED) {
-            fineLoc = getLastKnownLocationForProvider(LocationManager.GPS_PROVIDER);
-        }
-
-        if (fineLoc != null && coarseLoc != null) {
-            // If we have both a fine and coarse location, use the latest
-            return fineLoc.getTime() > coarseLoc.getTime() ? fineLoc : coarseLoc;
-        } else {
-            // Else, return the non-null one (if there is one)
-            return fineLoc != null ? fineLoc : coarseLoc;
-        }
-    }
-
-    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
-    private Location getLastKnownLocationForProvider(String provider) {
-        try {
-            if (mLocationManager.isProviderEnabled(provider)) {
-                return mLocationManager.getLastKnownLocation(provider);
-            }
-        } catch (Exception e) {
-            Log.d(TAG, "Failed to get last known location", e);
-        }
-        return null;
-    }
-
-    private boolean isStateValid() {
-        return mTwilightState.nextUpdate > System.currentTimeMillis();
-    }
-
-    private void updateState(@NonNull Location location) {
-        final TwilightState state = mTwilightState;
-        final long now = System.currentTimeMillis();
-        final TwilightCalculator calculator = TwilightCalculator.getInstance();
-
-        // calculate yesterday's twilight
-        calculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS,
-                location.getLatitude(), location.getLongitude());
-        final long yesterdaySunset = calculator.sunset;
-
-        // calculate today's twilight
-        calculator.calculateTwilight(now, location.getLatitude(), location.getLongitude());
-        final boolean isNight = (calculator.state == TwilightCalculator.NIGHT);
-        final long todaySunrise = calculator.sunrise;
-        final long todaySunset = calculator.sunset;
-
-        // calculate tomorrow's twilight
-        calculator.calculateTwilight(now + DateUtils.DAY_IN_MILLIS,
-                location.getLatitude(), location.getLongitude());
-        final long tomorrowSunrise = calculator.sunrise;
-
-        // Set next update
-        long nextUpdate = 0;
-        if (todaySunrise == -1 || todaySunset == -1) {
-            // In the case the day or night never ends the update is scheduled 12 hours later.
-            nextUpdate = now + 12 * DateUtils.HOUR_IN_MILLIS;
-        } else {
-            if (now > todaySunset) {
-                nextUpdate += tomorrowSunrise;
-            } else if (now > todaySunrise) {
-                nextUpdate += todaySunset;
-            } else {
-                nextUpdate += todaySunrise;
-            }
-            // add some extra time to be on the safe side.
-            nextUpdate += DateUtils.MINUTE_IN_MILLIS;
-        }
-
-        // Update the twilight state
-        state.isNight = isNight;
-        state.yesterdaySunset = yesterdaySunset;
-        state.todaySunrise = todaySunrise;
-        state.todaySunset = todaySunset;
-        state.tomorrowSunrise = tomorrowSunrise;
-        state.nextUpdate = nextUpdate;
-    }
-
-    /**
-     * Describes whether it is day or night.
-     */
-    private static class TwilightState {
-        boolean isNight;
-        long yesterdaySunset;
-        long todaySunrise;
-        long todaySunset;
-        long tomorrowSunrise;
-        long nextUpdate;
-
-        TwilightState() {
-        }
-    }
-}
diff --git a/android/support/v7/app/WindowDecorActionBar.java b/android/support/v7/app/WindowDecorActionBar.java
deleted file mode 100644
index 1c17922..0000000
--- a/android/support/v7/app/WindowDecorActionBar.java
+++ /dev/null
@@ -1,1413 +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.app;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentTransaction;
-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.view.ViewPropertyAnimatorUpdateListener;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.ActionBarPolicy;
-import android.support.v7.view.ActionMode;
-import android.support.v7.view.SupportMenuInflater;
-import android.support.v7.view.ViewPropertyAnimatorCompatSet;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPopupHelper;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.support.v7.widget.ActionBarContainer;
-import android.support.v7.widget.ActionBarContextView;
-import android.support.v7.widget.ActionBarOverlayLayout;
-import android.support.v7.widget.DecorToolbar;
-import android.support.v7.widget.ScrollingTabContainerView;
-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;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.Window;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.widget.SpinnerAdapter;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * WindowDecorActionBar is the ActionBar implementation used
- * by devices of all screen sizes as part of the window decor layout.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class WindowDecorActionBar extends ActionBar implements
-        ActionBarOverlayLayout.ActionBarVisibilityCallback {
-    private static final String TAG = "WindowDecorActionBar";
-
-    private static final Interpolator sHideInterpolator = new AccelerateInterpolator();
-    private static final Interpolator sShowInterpolator = new DecelerateInterpolator();
-
-    Context mContext;
-    private Context mThemedContext;
-    private Activity mActivity;
-    private Dialog mDialog;
-
-    ActionBarOverlayLayout mOverlayLayout;
-    ActionBarContainer mContainerView;
-    DecorToolbar mDecorToolbar;
-    ActionBarContextView mContextView;
-    View mContentView;
-    ScrollingTabContainerView mTabScrollView;
-
-    private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
-
-    private TabImpl mSelectedTab;
-    private int mSavedTabPosition = INVALID_POSITION;
-
-    private boolean mDisplayHomeAsUpSet;
-
-    ActionModeImpl mActionMode;
-    ActionMode mDeferredDestroyActionMode;
-    ActionMode.Callback mDeferredModeDestroyCallback;
-
-    private boolean mLastMenuVisibility;
-    private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
-            new ArrayList<OnMenuVisibilityListener>();
-
-    private static final int INVALID_POSITION = -1;
-
-    // The fade duration for toolbar and action bar when entering/exiting action mode.
-    private static final long FADE_OUT_DURATION_MS = 100;
-    private static final long FADE_IN_DURATION_MS = 200;
-
-    private boolean mHasEmbeddedTabs;
-
-    private int mCurWindowVisibility = View.VISIBLE;
-
-    boolean mContentAnimations = true;
-    boolean mHiddenByApp;
-    boolean mHiddenBySystem;
-    private boolean mShowingForMode;
-
-    private boolean mNowShowing = true;
-
-    ViewPropertyAnimatorCompatSet mCurrentShowAnim;
-    private boolean mShowHideAnimationEnabled;
-    boolean mHideOnContentScroll;
-
-    final ViewPropertyAnimatorListener mHideListener = new ViewPropertyAnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(View view) {
-            if (mContentAnimations && mContentView != null) {
-                mContentView.setTranslationY(0f);
-                mContainerView.setTranslationY(0f);
-            }
-            mContainerView.setVisibility(View.GONE);
-            mContainerView.setTransitioning(false);
-            mCurrentShowAnim = null;
-            completeDeferredDestroyActionMode();
-            if (mOverlayLayout != null) {
-                ViewCompat.requestApplyInsets(mOverlayLayout);
-            }
-        }
-    };
-
-    final ViewPropertyAnimatorListener mShowListener = new ViewPropertyAnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(View view) {
-            mCurrentShowAnim = null;
-            mContainerView.requestLayout();
-        }
-    };
-
-    final ViewPropertyAnimatorUpdateListener mUpdateListener =
-            new ViewPropertyAnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(View view) {
-                    final ViewParent parent = mContainerView.getParent();
-                    ((View) parent).invalidate();
-                }
-            };
-
-    public WindowDecorActionBar(Activity activity, boolean overlayMode) {
-        mActivity = activity;
-        Window window = activity.getWindow();
-        View decor = window.getDecorView();
-        init(decor);
-        if (!overlayMode) {
-            mContentView = decor.findViewById(android.R.id.content);
-        }
-    }
-
-    public WindowDecorActionBar(Dialog dialog) {
-        mDialog = dialog;
-        init(dialog.getWindow().getDecorView());
-    }
-
-    /**
-     * Only for edit mode.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public WindowDecorActionBar(View layout) {
-        assert layout.isInEditMode();
-        init(layout);
-    }
-
-    private void init(View decor) {
-        mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById(R.id.decor_content_parent);
-        if (mOverlayLayout != null) {
-            mOverlayLayout.setActionBarVisibilityCallback(this);
-        }
-        mDecorToolbar = getDecorToolbar(decor.findViewById(R.id.action_bar));
-        mContextView = (ActionBarContextView) decor.findViewById(
-                R.id.action_context_bar);
-        mContainerView = (ActionBarContainer) decor.findViewById(
-                R.id.action_bar_container);
-
-        if (mDecorToolbar == null || mContextView == null || mContainerView == null) {
-            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
-                    "with a compatible window decor layout");
-        }
-
-        mContext = mDecorToolbar.getContext();
-
-        // This was initially read from the action bar style
-        final int current = mDecorToolbar.getDisplayOptions();
-        final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0;
-        if (homeAsUp) {
-            mDisplayHomeAsUpSet = true;
-        }
-
-        ActionBarPolicy abp = ActionBarPolicy.get(mContext);
-        setHomeButtonEnabled(abp.enableHomeButtonByDefault() || homeAsUp);
-        setHasEmbeddedTabs(abp.hasEmbeddedTabs());
-
-        final TypedArray a = mContext.obtainStyledAttributes(null,
-                R.styleable.ActionBar,
-                R.attr.actionBarStyle, 0);
-        if (a.getBoolean(R.styleable.ActionBar_hideOnContentScroll, false)) {
-            setHideOnContentScrollEnabled(true);
-        }
-        final int elevation = a.getDimensionPixelSize(R.styleable.ActionBar_elevation, 0);
-        if (elevation != 0) {
-            setElevation(elevation);
-        }
-        a.recycle();
-    }
-
-    private DecorToolbar getDecorToolbar(View view) {
-        if (view instanceof DecorToolbar) {
-            return (DecorToolbar) view;
-        } else if (view instanceof Toolbar) {
-            return ((Toolbar) view).getWrapper();
-        } else {
-            throw new IllegalStateException("Can't make a decor toolbar out of " +
-                    view != null ? view.getClass().getSimpleName() : "null");
-        }
-    }
-
-    @Override
-    public void setElevation(float elevation) {
-        ViewCompat.setElevation(mContainerView, elevation);
-    }
-
-    @Override
-    public float getElevation() {
-        return ViewCompat.getElevation(mContainerView);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs());
-    }
-
-    private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) {
-        mHasEmbeddedTabs = hasEmbeddedTabs;
-        // Switch tab layout configuration if needed
-        if (!mHasEmbeddedTabs) {
-            mDecorToolbar.setEmbeddedTabView(null);
-            mContainerView.setTabContainer(mTabScrollView);
-        } else {
-            mContainerView.setTabContainer(null);
-            mDecorToolbar.setEmbeddedTabView(mTabScrollView);
-        }
-        final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS;
-        if (mTabScrollView != null) {
-            if (isInTabMode) {
-                mTabScrollView.setVisibility(View.VISIBLE);
-                if (mOverlayLayout != null) {
-                    ViewCompat.requestApplyInsets(mOverlayLayout);
-                }
-            } else {
-                mTabScrollView.setVisibility(View.GONE);
-            }
-        }
-        mDecorToolbar.setCollapsible(!mHasEmbeddedTabs && isInTabMode);
-        mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode);
-    }
-
-    private void ensureTabsExist() {
-        if (mTabScrollView != null) {
-            return;
-        }
-
-        ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext);
-
-        if (mHasEmbeddedTabs) {
-            tabScroller.setVisibility(View.VISIBLE);
-            mDecorToolbar.setEmbeddedTabView(tabScroller);
-        } else {
-            if (getNavigationMode() == NAVIGATION_MODE_TABS) {
-                tabScroller.setVisibility(View.VISIBLE);
-                if (mOverlayLayout != null) {
-                    ViewCompat.requestApplyInsets(mOverlayLayout);
-                }
-            } else {
-                tabScroller.setVisibility(View.GONE);
-            }
-            mContainerView.setTabContainer(tabScroller);
-        }
-        mTabScrollView = tabScroller;
-    }
-
-    void completeDeferredDestroyActionMode() {
-        if (mDeferredModeDestroyCallback != null) {
-            mDeferredModeDestroyCallback.onDestroyActionMode(mDeferredDestroyActionMode);
-            mDeferredDestroyActionMode = null;
-            mDeferredModeDestroyCallback = null;
-        }
-    }
-
-    @Override
-    public void onWindowVisibilityChanged(int visibility) {
-        mCurWindowVisibility = visibility;
-    }
-
-    /**
-     * Enables or disables animation between show/hide states.
-     * If animation is disabled using this method, animations in progress
-     * will be finished.
-     *
-     * @param enabled true to animate, false to not animate.
-     */
-    @Override
-    public void setShowHideAnimationEnabled(boolean enabled) {
-        mShowHideAnimationEnabled = enabled;
-        if (!enabled && mCurrentShowAnim != null) {
-            mCurrentShowAnim.cancel();
-        }
-    }
-
-    @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;
-        }
-        mLastMenuVisibility = isVisible;
-
-        final int count = mMenuVisibilityListeners.size();
-        for (int i = 0; i < count; i++) {
-            mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
-        }
-    }
-
-    @Override
-    public void setCustomView(int resId) {
-        setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId,
-                mDecorToolbar.getViewGroup(), false));
-    }
-
-    @Override
-    public void setDisplayUseLogoEnabled(boolean useLogo) {
-        setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
-    }
-
-    @Override
-    public void setDisplayShowHomeEnabled(boolean showHome) {
-        setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
-    }
-
-    @Override
-    public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
-        setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
-    }
-
-    @Override
-    public void setDisplayShowTitleEnabled(boolean showTitle) {
-        setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
-    }
-
-    @Override
-    public void setDisplayShowCustomEnabled(boolean showCustom) {
-        setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
-    }
-
-    @Override
-    public void setHomeButtonEnabled(boolean enable) {
-        mDecorToolbar.setHomeButtonEnabled(enable);
-    }
-
-    @Override
-    public void setTitle(int resId) {
-        setTitle(mContext.getString(resId));
-    }
-
-    @Override
-    public void setSubtitle(int resId) {
-        setSubtitle(mContext.getString(resId));
-    }
-
-    @Override
-    public void setSelectedNavigationItem(int position) {
-        switch (mDecorToolbar.getNavigationMode()) {
-            case NAVIGATION_MODE_TABS:
-                selectTab(mTabs.get(position));
-                break;
-            case NAVIGATION_MODE_LIST:
-                mDecorToolbar.setDropdownSelectedPosition(position);
-                break;
-            default:
-                throw new IllegalStateException(
-                        "setSelectedNavigationIndex not valid for current navigation mode");
-        }
-    }
-
-    @Override
-    public void removeAllTabs() {
-        cleanupTabs();
-    }
-
-    private void cleanupTabs() {
-        if (mSelectedTab != null) {
-            selectTab(null);
-        }
-        mTabs.clear();
-        if (mTabScrollView != null) {
-            mTabScrollView.removeAllTabs();
-        }
-        mSavedTabPosition = INVALID_POSITION;
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mDecorToolbar.setTitle(title);
-    }
-
-    @Override
-    public void setWindowTitle(CharSequence title) {
-        mDecorToolbar.setWindowTitle(title);
-    }
-
-    @Override
-    public boolean requestFocus() {
-        final ViewGroup viewGroup = mDecorToolbar.getViewGroup();
-        if (viewGroup != null && !viewGroup.hasFocus()) {
-            viewGroup.requestFocus();
-            return true;
-        }
-        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;
-        }
-        mDecorToolbar.setDisplayOptions(options);
-    }
-
-    @Override
-    public void setDisplayOptions(int options, int mask) {
-        final int current = mDecorToolbar.getDisplayOptions();
-        if ((mask & DISPLAY_HOME_AS_UP) != 0) {
-            mDisplayHomeAsUpSet = true;
-        }
-        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();
-        }
-
-        mOverlayLayout.setHideOnContentScrollEnabled(false);
-        mContextView.killMode();
-        ActionModeImpl mode = new ActionModeImpl(mContextView.getContext(), callback);
-        if (mode.dispatchOnCreate()) {
-            // This needs to be set before invalidate() so that it calls
-            // onPrepareActionMode()
-            mActionMode = mode;
-            mode.invalidate();
-            mContextView.initForMode(mode);
-            animateToMode(true);
-            mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-            return mode;
-        }
-        return null;
-    }
-
-    private void configureTab(Tab tab, int position) {
-        final TabImpl tabi = (TabImpl) tab;
-        final ActionBar.TabListener callback = tabi.getCallback();
-
-        if (callback == null) {
-            throw new IllegalStateException("Action Bar Tab must have a Callback");
-        }
-
-        tabi.setPosition(position);
-        mTabs.add(position, tabi);
-
-        final int count = mTabs.size();
-        for (int i = position + 1; i < count; i++) {
-            mTabs.get(i).setPosition(i);
-        }
-    }
-
-    @Override
-    public void addTab(Tab tab) {
-        addTab(tab, mTabs.isEmpty());
-    }
-
-    @Override
-    public void addTab(Tab tab, int position) {
-        addTab(tab, position, mTabs.isEmpty());
-    }
-
-    @Override
-    public void addTab(Tab tab, boolean setSelected) {
-        ensureTabsExist();
-        mTabScrollView.addTab(tab, setSelected);
-        configureTab(tab, mTabs.size());
-        if (setSelected) {
-            selectTab(tab);
-        }
-    }
-
-    @Override
-    public void addTab(Tab tab, int position, boolean setSelected) {
-        ensureTabsExist();
-        mTabScrollView.addTab(tab, position, setSelected);
-        configureTab(tab, position);
-        if (setSelected) {
-            selectTab(tab);
-        }
-    }
-
-    @Override
-    public Tab newTab() {
-        return new TabImpl();
-    }
-
-    @Override
-    public void removeTab(Tab tab) {
-        removeTabAt(tab.getPosition());
-    }
-
-    @Override
-    public void removeTabAt(int position) {
-        if (mTabScrollView == null) {
-            // No tabs around to remove
-            return;
-        }
-
-        int selectedTabPosition = mSelectedTab != null
-                ? mSelectedTab.getPosition() : mSavedTabPosition;
-        mTabScrollView.removeTabAt(position);
-        TabImpl removedTab = mTabs.remove(position);
-        if (removedTab != null) {
-            removedTab.setPosition(-1);
-        }
-
-        final int newTabCount = mTabs.size();
-        for (int i = position; i < newTabCount; i++) {
-            mTabs.get(i).setPosition(i);
-        }
-
-        if (selectedTabPosition == position) {
-            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
-        }
-    }
-
-    @Override
-    public void selectTab(Tab tab) {
-        if (getNavigationMode() != NAVIGATION_MODE_TABS) {
-            mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
-            return;
-        }
-
-        final FragmentTransaction trans;
-        if (mActivity instanceof FragmentActivity && !mDecorToolbar.getViewGroup().isInEditMode()) {
-            // If we're not in edit mode and our Activity is a FragmentActivity, start a tx
-            trans = ((FragmentActivity) mActivity).getSupportFragmentManager()
-                    .beginTransaction().disallowAddToBackStack();
-        } else {
-            trans = null;
-        }
-
-        if (mSelectedTab == tab) {
-            if (mSelectedTab != null) {
-                mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
-                mTabScrollView.animateToTab(tab.getPosition());
-            }
-        } else {
-            mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
-            if (mSelectedTab != null) {
-                mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
-            }
-            mSelectedTab = (TabImpl) tab;
-            if (mSelectedTab != null) {
-                mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
-            }
-        }
-
-        if (trans != null && !trans.isEmpty()) {
-            trans.commit();
-        }
-    }
-
-    @Override
-    public Tab getSelectedTab() {
-        return mSelectedTab;
-    }
-
-    @Override
-    public int getHeight() {
-        return mContainerView.getHeight();
-    }
-
-    @Override
-    public void enableContentAnimations(boolean enabled) {
-        mContentAnimations = enabled;
-    }
-
-    @Override
-    public void show() {
-        if (mHiddenByApp) {
-            mHiddenByApp = false;
-            updateVisibility(false);
-        }
-    }
-
-    private void showForActionMode() {
-        if (!mShowingForMode) {
-            mShowingForMode = true;
-            if (mOverlayLayout != null) {
-                mOverlayLayout.setShowingForActionMode(true);
-            }
-            updateVisibility(false);
-        }
-    }
-
-    @Override
-    public void showForSystem() {
-        if (mHiddenBySystem) {
-            mHiddenBySystem = false;
-            updateVisibility(true);
-        }
-    }
-
-    @Override
-    public void hide() {
-        if (!mHiddenByApp) {
-            mHiddenByApp = true;
-            updateVisibility(false);
-        }
-    }
-
-    private void hideForActionMode() {
-        if (mShowingForMode) {
-            mShowingForMode = false;
-            if (mOverlayLayout != null) {
-                mOverlayLayout.setShowingForActionMode(false);
-            }
-            updateVisibility(false);
-        }
-    }
-
-    @Override
-    public void hideForSystem() {
-        if (!mHiddenBySystem) {
-            mHiddenBySystem = true;
-            updateVisibility(true);
-        }
-    }
-
-    @Override
-    public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
-        if (hideOnContentScroll && !mOverlayLayout.isInOverlayMode()) {
-            throw new IllegalStateException("Action bar must be in overlay mode " +
-                    "(Window.FEATURE_OVERLAY_ACTION_BAR) to enable hide on content scroll");
-        }
-        mHideOnContentScroll = hideOnContentScroll;
-        mOverlayLayout.setHideOnContentScrollEnabled(hideOnContentScroll);
-    }
-
-    @Override
-    public boolean isHideOnContentScrollEnabled() {
-        return mOverlayLayout.isHideOnContentScrollEnabled();
-    }
-
-    @Override
-    public int getHideOffset() {
-        return mOverlayLayout.getActionBarHideOffset();
-    }
-
-    @Override
-    public void setHideOffset(int offset) {
-        if (offset != 0 && !mOverlayLayout.isInOverlayMode()) {
-            throw new IllegalStateException("Action bar must be in overlay mode " +
-                    "(Window.FEATURE_OVERLAY_ACTION_BAR) to set a non-zero hide offset");
-        }
-        mOverlayLayout.setActionBarHideOffset(offset);
-    }
-
-    static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem,
-            boolean showingForMode) {
-        if (showingForMode) {
-            return true;
-        } else if (hiddenByApp || hiddenBySystem) {
-            return false;
-        } else {
-            return true;
-        }
-    }
-
-    private void updateVisibility(boolean fromSystem) {
-        // Based on the current state, should we be hidden or shown?
-        final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem,
-                mShowingForMode);
-
-        if (shown) {
-            if (!mNowShowing) {
-                mNowShowing = true;
-                doShow(fromSystem);
-            }
-        } else {
-            if (mNowShowing) {
-                mNowShowing = false;
-                doHide(fromSystem);
-            }
-        }
-    }
-
-    public void doShow(boolean fromSystem) {
-        if (mCurrentShowAnim != null) {
-            mCurrentShowAnim.cancel();
-        }
-        mContainerView.setVisibility(View.VISIBLE);
-
-        if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled || fromSystem)) {
-            // because we're about to ask its window loc
-            mContainerView.setTranslationY(0f);
-            float startingY = -mContainerView.getHeight();
-            if (fromSystem) {
-                int topLeft[] = {0, 0};
-                mContainerView.getLocationInWindow(topLeft);
-                startingY -= topLeft[1];
-            }
-            mContainerView.setTranslationY(startingY);
-            ViewPropertyAnimatorCompatSet anim = new ViewPropertyAnimatorCompatSet();
-            ViewPropertyAnimatorCompat a = ViewCompat.animate(mContainerView).translationY(0f);
-            a.setUpdateListener(mUpdateListener);
-            anim.play(a);
-            if (mContentAnimations && mContentView != null) {
-                mContentView.setTranslationY(startingY);
-                anim.play(ViewCompat.animate(mContentView).translationY(0f));
-            }
-            anim.setInterpolator(sShowInterpolator);
-            anim.setDuration(250);
-            // If this is being shown from the system, add a small delay.
-            // This is because we will also be animating in the status bar,
-            // and these two elements can't be done in lock-step.  So we give
-            // a little time for the status bar to start its animation before
-            // the action bar animates.  (This corresponds to the corresponding
-            // case when hiding, where the status bar has a small delay before
-            // starting.)
-            anim.setListener(mShowListener);
-            mCurrentShowAnim = anim;
-            anim.start();
-        } else {
-            mContainerView.setAlpha(1f);
-            mContainerView.setTranslationY(0);
-            if (mContentAnimations && mContentView != null) {
-                mContentView.setTranslationY(0);
-            }
-            mShowListener.onAnimationEnd(null);
-        }
-        if (mOverlayLayout != null) {
-            ViewCompat.requestApplyInsets(mOverlayLayout);
-        }
-    }
-
-    public void doHide(boolean fromSystem) {
-        if (mCurrentShowAnim != null) {
-            mCurrentShowAnim.cancel();
-        }
-
-        if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled || fromSystem)) {
-            mContainerView.setAlpha(1f);
-            mContainerView.setTransitioning(true);
-            ViewPropertyAnimatorCompatSet anim = new ViewPropertyAnimatorCompatSet();
-            float endingY = -mContainerView.getHeight();
-            if (fromSystem) {
-                int topLeft[] = {0, 0};
-                mContainerView.getLocationInWindow(topLeft);
-                endingY -= topLeft[1];
-            }
-            ViewPropertyAnimatorCompat a = ViewCompat.animate(mContainerView).translationY(endingY);
-            a.setUpdateListener(mUpdateListener);
-            anim.play(a);
-            if (mContentAnimations && mContentView != null) {
-                anim.play(ViewCompat.animate(mContentView).translationY(endingY));
-            }
-            anim.setInterpolator(sHideInterpolator);
-            anim.setDuration(250);
-            anim.setListener(mHideListener);
-            mCurrentShowAnim = anim;
-            anim.start();
-        } else {
-            mHideListener.onAnimationEnd(null);
-        }
-    }
-
-    @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.
-        return mNowShowing && (height == 0 || getHideOffset() < height);
-    }
-
-    public void animateToMode(boolean toActionMode) {
-        if (toActionMode) {
-            showForActionMode();
-        } else {
-            hideForActionMode();
-        }
-
-        if (shouldAnimateContextView()) {
-            ViewPropertyAnimatorCompat fadeIn, fadeOut;
-            if (toActionMode) {
-                // We use INVISIBLE for the Toolbar to make sure that the container has a non-zero
-                // height throughout. The context view is GONE initially, so will not have been laid
-                // out when the animation starts. This can lead to the container collapsing to 0px
-                // height for a short period.
-                fadeOut = mDecorToolbar.setupAnimatorToVisibility(View.INVISIBLE,
-                        FADE_OUT_DURATION_MS);
-                fadeIn = mContextView.setupAnimatorToVisibility(View.VISIBLE,
-                        FADE_IN_DURATION_MS);
-            } else {
-                fadeIn = mDecorToolbar.setupAnimatorToVisibility(View.VISIBLE,
-                        FADE_IN_DURATION_MS);
-                fadeOut = mContextView.setupAnimatorToVisibility(View.GONE,
-                        FADE_OUT_DURATION_MS);
-            }
-            ViewPropertyAnimatorCompatSet set = new ViewPropertyAnimatorCompatSet();
-            set.playSequentially(fadeOut, fadeIn);
-            set.start();
-        } else {
-            if (toActionMode) {
-                mDecorToolbar.setVisibility(View.INVISIBLE);
-                mContextView.setVisibility(View.VISIBLE);
-            } else {
-                mDecorToolbar.setVisibility(View.VISIBLE);
-                mContextView.setVisibility(View.GONE);
-            }
-        }
-        // mTabScrollView's visibility is not affected by action mode.
-    }
-
-    private boolean shouldAnimateContextView() {
-        // We only to animate the action mode in if the container view has already been laid out.
-        // If it hasn't been laid out, it hasn't been drawn to screen yet.
-        return ViewCompat.isLaidOut(mContainerView);
-    }
-
-    @Override
-    public Context getThemedContext() {
-        if (mThemedContext == null) {
-            TypedValue outValue = new TypedValue();
-            Resources.Theme currentTheme = mContext.getTheme();
-            currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme, outValue, true);
-            final int targetThemeRes = outValue.resourceId;
-
-            if (targetThemeRes != 0) {
-                mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes);
-            } else {
-                mThemedContext = mContext;
-            }
-        }
-        return mThemedContext;
-    }
-
-    @Override
-    public boolean isTitleTruncated() {
-        return mDecorToolbar != null && mDecorToolbar.isTitleTruncated();
-    }
-
-    @Override
-    public void setHomeAsUpIndicator(Drawable indicator) {
-        mDecorToolbar.setNavigationIcon(indicator);
-    }
-
-    @Override
-    public void setHomeAsUpIndicator(int resId) {
-        mDecorToolbar.setNavigationIcon(resId);
-    }
-
-    @Override
-    public void setHomeActionContentDescription(CharSequence description) {
-        mDecorToolbar.setNavigationContentDescription(description);
-    }
-
-    @Override
-    public void setHomeActionContentDescription(int resId) {
-        mDecorToolbar.setNavigationContentDescription(resId);
-    }
-
-    @Override
-    public void onContentScrollStarted() {
-        if (mCurrentShowAnim != null) {
-            mCurrentShowAnim.cancel();
-            mCurrentShowAnim = null;
-        }
-    }
-
-    @Override
-    public void onContentScrollStopped() {
-    }
-
-    @Override
-    public boolean collapseActionView() {
-        if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) {
-            mDecorToolbar.collapseActionView();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
-        private final Context mActionModeContext;
-        private final MenuBuilder mMenu;
-
-        private ActionMode.Callback mCallback;
-        private WeakReference<View> mCustomView;
-
-        public ActionModeImpl(Context context, ActionMode.Callback callback) {
-            mActionModeContext = context;
-            mCallback = callback;
-            mMenu = new MenuBuilder(context)
-                    .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            mMenu.setCallback(this);
-        }
-
-        @Override
-        public MenuInflater getMenuInflater() {
-            return new SupportMenuInflater(mActionModeContext);
-        }
-
-        @Override
-        public Menu getMenu() {
-            return mMenu;
-        }
-
-        @Override
-        public void finish() {
-            if (mActionMode != this) {
-                // Not the active action mode - no-op
-                return;
-            }
-
-            // If this change in state is going to cause the action bar
-            // to be hidden, defer the onDestroy callback until the animation
-            // is finished and associated relayout is about to happen. This lets
-            // apps better anticipate visibility and layout behavior.
-            if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) {
-                // With the current state but the action bar hidden, our
-                // overall showing state is going to be false.
-                mDeferredDestroyActionMode = this;
-                mDeferredModeDestroyCallback = mCallback;
-            } else {
-                mCallback.onDestroyActionMode(this);
-            }
-            mCallback = null;
-            animateToMode(false);
-
-            // Clear out the context mode views after the animation finishes
-            mContextView.closeMode();
-            mDecorToolbar.getViewGroup().sendAccessibilityEvent(
-                    AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-            mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll);
-
-            mActionMode = null;
-        }
-
-        @Override
-        public void invalidate() {
-            if (mActionMode != this) {
-                // Not the active action mode - no-op. It's possible we are
-                // currently deferring onDestroy, so the app doesn't yet know we
-                // are going away and is trying to use us. That's also a no-op.
-                return;
-            }
-
-            mMenu.stopDispatchingItemsChanged();
-            try {
-                mCallback.onPrepareActionMode(this, mMenu);
-            } finally {
-                mMenu.startDispatchingItemsChanged();
-            }
-        }
-
-        public boolean dispatchOnCreate() {
-            mMenu.stopDispatchingItemsChanged();
-            try {
-                return mCallback.onCreateActionMode(this, mMenu);
-            } finally {
-                mMenu.startDispatchingItemsChanged();
-            }
-        }
-
-        @Override
-        public void setCustomView(View view) {
-            mContextView.setCustomView(view);
-            mCustomView = new WeakReference<View>(view);
-        }
-
-        @Override
-        public void setSubtitle(CharSequence subtitle) {
-            mContextView.setSubtitle(subtitle);
-        }
-
-        @Override
-        public void setTitle(CharSequence title) {
-            mContextView.setTitle(title);
-        }
-
-        @Override
-        public void setTitle(int resId) {
-            setTitle(mContext.getResources().getString(resId));
-        }
-
-        @Override
-        public void setSubtitle(int resId) {
-            setSubtitle(mContext.getResources().getString(resId));
-        }
-
-        @Override
-        public CharSequence getTitle() {
-            return mContextView.getTitle();
-        }
-
-        @Override
-        public CharSequence getSubtitle() {
-            return mContextView.getSubtitle();
-        }
-
-        @Override
-        public void setTitleOptionalHint(boolean titleOptional) {
-            super.setTitleOptionalHint(titleOptional);
-            mContextView.setTitleOptional(titleOptional);
-        }
-
-        @Override
-        public boolean isTitleOptional() {
-            return mContextView.isTitleOptional();
-        }
-
-        @Override
-        public View getCustomView() {
-            return mCustomView != null ? mCustomView.get() : null;
-        }
-
-        @Override
-        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-            if (mCallback != null) {
-                return mCallback.onActionItemClicked(this, item);
-            } else {
-                return false;
-            }
-        }
-
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        }
-
-        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-            if (mCallback == null) {
-                return false;
-            }
-
-            if (!subMenu.hasVisibleItems()) {
-                return true;
-            }
-
-            new MenuPopupHelper(getThemedContext(), subMenu).show();
-            return true;
-        }
-
-        public void onCloseSubMenu(SubMenuBuilder menu) {
-        }
-
-        @Override
-        public void onMenuModeChange(MenuBuilder menu) {
-            if (mCallback == null) {
-                return;
-            }
-            invalidate();
-            mContextView.showOverflowMenu();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public class TabImpl extends ActionBar.Tab {
-        private ActionBar.TabListener mCallback;
-        private Object mTag;
-        private Drawable mIcon;
-        private CharSequence mText;
-        private CharSequence mContentDesc;
-        private int mPosition = -1;
-        private View mCustomView;
-
-        @Override
-        public Object getTag() {
-            return mTag;
-        }
-
-        @Override
-        public Tab setTag(Object tag) {
-            mTag = tag;
-            return this;
-        }
-
-        public ActionBar.TabListener getCallback() {
-            return mCallback;
-        }
-
-        @Override
-        public Tab setTabListener(ActionBar.TabListener callback) {
-            mCallback = callback;
-            return this;
-        }
-
-        @Override
-        public View getCustomView() {
-            return mCustomView;
-        }
-
-        @Override
-        public Tab setCustomView(View view) {
-            mCustomView = view;
-            if (mPosition >= 0) {
-                mTabScrollView.updateTab(mPosition);
-            }
-            return this;
-        }
-
-        @Override
-        public Tab setCustomView(int layoutResId) {
-            return setCustomView(LayoutInflater.from(getThemedContext())
-                    .inflate(layoutResId, null));
-        }
-
-        @Override
-        public Drawable getIcon() {
-            return mIcon;
-        }
-
-        @Override
-        public int getPosition() {
-            return mPosition;
-        }
-
-        public void setPosition(int position) {
-            mPosition = position;
-        }
-
-        @Override
-        public CharSequence getText() {
-            return mText;
-        }
-
-        @Override
-        public Tab setIcon(Drawable icon) {
-            mIcon = icon;
-            if (mPosition >= 0) {
-                mTabScrollView.updateTab(mPosition);
-            }
-            return this;
-        }
-
-        @Override
-        public Tab setIcon(int resId) {
-            return setIcon(AppCompatResources.getDrawable(mContext, resId));
-        }
-
-        @Override
-        public Tab setText(CharSequence text) {
-            mText = text;
-            if (mPosition >= 0) {
-                mTabScrollView.updateTab(mPosition);
-            }
-            return this;
-        }
-
-        @Override
-        public Tab setText(int resId) {
-            return setText(mContext.getResources().getText(resId));
-        }
-
-        @Override
-        public void select() {
-            selectTab(this);
-        }
-
-        @Override
-        public Tab setContentDescription(int resId) {
-            return setContentDescription(mContext.getResources().getText(resId));
-        }
-
-        @Override
-        public Tab setContentDescription(CharSequence contentDesc) {
-            mContentDesc = contentDesc;
-            if (mPosition >= 0) {
-                mTabScrollView.updateTab(mPosition);
-            }
-            return this;
-        }
-
-        @Override
-        public CharSequence getContentDescription() {
-            return mContentDesc;
-        }
-    }
-
-    @Override
-    public void setCustomView(View view) {
-        mDecorToolbar.setCustomView(view);
-    }
-
-    @Override
-    public void setCustomView(View view, LayoutParams layoutParams) {
-        view.setLayoutParams(layoutParams);
-        mDecorToolbar.setCustomView(view);
-    }
-
-    @Override
-    public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
-        mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
-    }
-
-    @Override
-    public int getSelectedNavigationIndex() {
-        switch (mDecorToolbar.getNavigationMode()) {
-            case NAVIGATION_MODE_TABS:
-                return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
-            case NAVIGATION_MODE_LIST:
-                return mDecorToolbar.getDropdownSelectedPosition();
-            default:
-                return -1;
-        }
-    }
-
-    @Override
-    public int getNavigationItemCount() {
-        switch (mDecorToolbar.getNavigationMode()) {
-            case NAVIGATION_MODE_TABS:
-                return mTabs.size();
-            case NAVIGATION_MODE_LIST:
-                return mDecorToolbar.getDropdownItemCount();
-            default:
-                return 0;
-        }
-    }
-
-    @Override
-    public int getTabCount() {
-        return mTabs.size();
-    }
-
-    @Override
-    public void setNavigationMode(int mode) {
-        final int oldMode = mDecorToolbar.getNavigationMode();
-        switch (oldMode) {
-            case NAVIGATION_MODE_TABS:
-                mSavedTabPosition = getSelectedNavigationIndex();
-                selectTab(null);
-                mTabScrollView.setVisibility(View.GONE);
-                break;
-        }
-        if (oldMode != mode && !mHasEmbeddedTabs) {
-            if (mOverlayLayout != null) {
-                ViewCompat.requestApplyInsets(mOverlayLayout);
-            }
-        }
-        mDecorToolbar.setNavigationMode(mode);
-        switch (mode) {
-            case NAVIGATION_MODE_TABS:
-                ensureTabsExist();
-                mTabScrollView.setVisibility(View.VISIBLE);
-                if (mSavedTabPosition != INVALID_POSITION) {
-                    setSelectedNavigationItem(mSavedTabPosition);
-                    mSavedTabPosition = INVALID_POSITION;
-                }
-                break;
-        }
-        mDecorToolbar.setCollapsible(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
-        mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs);
-    }
-
-    @Override
-    public Tab getTabAt(int index) {
-        return mTabs.get(index);
-    }
-
-
-    @Override
-    public void setIcon(int resId) {
-        mDecorToolbar.setIcon(resId);
-    }
-
-    @Override
-    public void setIcon(Drawable icon) {
-        mDecorToolbar.setIcon(icon);
-    }
-
-    public boolean hasIcon() {
-        return mDecorToolbar.hasIcon();
-    }
-
-    @Override
-    public void setLogo(int resId) {
-        mDecorToolbar.setLogo(resId);
-    }
-
-    @Override
-    public void setLogo(Drawable logo) {
-        mDecorToolbar.setLogo(logo);
-    }
-
-    public boolean hasLogo() {
-        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/android/support/v7/content/res/AppCompatColorStateListInflater.java b/android/support/v7/content/res/AppCompatColorStateListInflater.java
deleted file mode 100644
index ca7f704..0000000
--- a/android/support/v7/content/res/AppCompatColorStateListInflater.java
+++ /dev/null
@@ -1,171 +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.content.res;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.util.StateSet;
-import android.util.Xml;
-
-import java.io.IOException;
-
-final class AppCompatColorStateListInflater {
-
-    private static final int DEFAULT_COLOR = Color.RED;
-
-    private AppCompatColorStateListInflater() {}
-
-    /**
-     * Creates a ColorStateList from an XML document using given a set of
-     * {@link Resources} and a {@link Theme}.
-     *
-     * @param r Resources against which the ColorStateList should be inflated.
-     * @param parser Parser for the XML document defining the ColorStateList.
-     * @param theme Optional theme to apply to the color state list, may be
-     *              {@code null}.
-     * @return A new color state list.
-     */
-    @NonNull
-    public static ColorStateList createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser,
-            @Nullable Resources.Theme theme) throws XmlPullParserException, IOException {
-        final AttributeSet attrs = Xml.asAttributeSet(parser);
-
-        int type;
-        while ((type = parser.next()) != XmlPullParser.START_TAG
-                && type != XmlPullParser.END_DOCUMENT) {
-            // Seek parser to start tag.
-        }
-
-        if (type != XmlPullParser.START_TAG) {
-            throw new XmlPullParserException("No start tag found");
-        }
-
-        return createFromXmlInner(r, parser, attrs, theme);
-    }
-
-    /**
-     * Create from inside an XML document. Called on a parser positioned at a
-     * tag in an XML document, tries to create a ColorStateList from that tag.
-     *
-     * @throws XmlPullParserException if the current tag is not &lt;selector>
-     * @return A new color state list for the current tag.
-     */
-    @NonNull
-    private static ColorStateList createFromXmlInner(@NonNull Resources r,
-            @NonNull XmlPullParser parser, @NonNull AttributeSet attrs,
-            @Nullable Resources.Theme theme)
-            throws XmlPullParserException, IOException {
-        final String name = parser.getName();
-        if (!name.equals("selector")) {
-            throw new XmlPullParserException(
-                    parser.getPositionDescription() + ": invalid color state list tag " + name);
-        }
-
-        return inflate(r, parser, attrs, theme);
-    }
-
-    /**
-     * Fill in this object based on the contents of an XML "selector" element.
-     */
-    private static ColorStateList inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
-            @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
-            throws XmlPullParserException, IOException {
-        final int innerDepth = parser.getDepth() + 1;
-        int depth;
-        int type;
-        int defaultColor = DEFAULT_COLOR;
-
-        int[][] stateSpecList = new int[20][];
-        int[] colorList = new int[stateSpecList.length];
-        int listSize = 0;
-
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
-            if (type != XmlPullParser.START_TAG || depth > innerDepth
-                    || !parser.getName().equals("item")) {
-                continue;
-            }
-
-            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ColorStateListItem);
-            final int baseColor = a.getColor(R.styleable.ColorStateListItem_android_color,
-                    Color.MAGENTA);
-
-            float alphaMod = 1.0f;
-            if (a.hasValue(R.styleable.ColorStateListItem_android_alpha)) {
-                alphaMod = a.getFloat(R.styleable.ColorStateListItem_android_alpha, alphaMod);
-            } else if (a.hasValue(R.styleable.ColorStateListItem_alpha)) {
-                alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, alphaMod);
-            }
-
-            a.recycle();
-
-            // Parse all unrecognized attributes as state specifiers.
-            int j = 0;
-            final int numAttrs = attrs.getAttributeCount();
-            int[] stateSpec = new int[numAttrs];
-            for (int i = 0; i < numAttrs; i++) {
-                final int stateResId = attrs.getAttributeNameResource(i);
-                if (stateResId != android.R.attr.color && stateResId != android.R.attr.alpha
-                        && stateResId != R.attr.alpha) {
-                    // Unrecognized attribute, add to state set
-                    stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
-                            ? stateResId : -stateResId;
-                }
-            }
-            stateSpec = StateSet.trimStateSet(stateSpec, j);
-
-            // Apply alpha modulation. If we couldn't resolve the color or
-            // alpha yet, the default values leave us enough information to
-            // modulate again during applyTheme().
-            final int color = modulateColorAlpha(baseColor, alphaMod);
-            if (listSize == 0 || stateSpec.length == 0) {
-                defaultColor = color;
-            }
-
-            colorList = GrowingArrayUtils.append(colorList, listSize, color);
-            stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
-            listSize++;
-        }
-
-        int[] colors = new int[listSize];
-        int[][] stateSpecs = new int[listSize][];
-        System.arraycopy(colorList, 0, colors, 0, listSize);
-        System.arraycopy(stateSpecList, 0, stateSpecs, 0, listSize);
-
-        return new ColorStateList(stateSpecs, colors);
-    }
-
-    private static TypedArray obtainAttributes(Resources res, Resources.Theme theme,
-            AttributeSet set, int[] attrs) {
-        return theme == null ? res.obtainAttributes(set, attrs)
-                : theme.obtainStyledAttributes(set, attrs, 0, 0);
-    }
-
-    private static int modulateColorAlpha(int color, float alphaMod) {
-        return ColorUtils.setAlphaComponent(color, Math.round(Color.alpha(color) * alphaMod));
-    }
-}
diff --git a/android/support/v7/content/res/AppCompatResources.java b/android/support/v7/content/res/AppCompatResources.java
deleted file mode 100644
index 1b90232..0000000
--- a/android/support/v7/content/res/AppCompatResources.java
+++ /dev/null
@@ -1,188 +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.content.res;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.ColorRes;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.AppCompatDrawableManager;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import java.util.WeakHashMap;
-
-/**
- * Class for accessing an application's resources through AppCompat, and thus any backward
- * compatible functionality.
- */
-public final class AppCompatResources {
-
-    private static final String LOG_TAG = "AppCompatResources";
-    private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
-
-    private static final WeakHashMap<Context, SparseArray<ColorStateListCacheEntry>>
-            sColorStateCaches = new WeakHashMap<>(0);
-
-    private static final Object sColorStateCacheLock = new Object();
-
-    private AppCompatResources() {}
-
-    /**
-     * Returns the {@link ColorStateList} from the given resource. The resource can include
-     * themeable attributes, regardless of API level.
-     *
-     * @param context context to inflate against
-     * @param resId the resource identifier of the ColorStateList to retrieve
-     */
-    public static ColorStateList getColorStateList(@NonNull Context context, @ColorRes int resId) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            // On M+ we can use the framework
-            return context.getColorStateList(resId);
-        }
-
-        // Before that, we'll try handle it ourselves
-        ColorStateList csl = getCachedColorStateList(context, resId);
-        if (csl != null) {
-            return csl;
-        }
-        // Cache miss, so try and inflate it ourselves
-        csl = inflateColorStateList(context, resId);
-        if (csl != null) {
-            // If we inflated it, add it to the cache and return
-            addColorStateListToCache(context, resId, csl);
-            return csl;
-        }
-
-        // If we reach here then we couldn't inflate it, so let the framework handle it
-        return ContextCompat.getColorStateList(context, resId);
-    }
-
-    /**
-     * Return a drawable object associated with a particular resource ID.
-     *
-     * <p>This method supports inflation of {@code <vector>} and {@code <animated-vector>}
-     * resources on devices where platform support is not available.</p>
-     *
-     * @param context context to inflate against
-     * @param resId   The desired resource identifier, as generated by the aapt
-     *                tool. This integer encodes the package, type, and resource
-     *                entry. The value 0 is an invalid identifier.
-     * @return Drawable An object that can be used to draw this resource.
-     * @see ContextCompat#getDrawable(Context, int)
-     */
-    @Nullable
-    public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
-        return AppCompatDrawableManager.get().getDrawable(context, resId);
-    }
-
-    /**
-     * Inflates a {@link ColorStateList} from resources, honouring theme attributes.
-     */
-    @Nullable
-    private static ColorStateList inflateColorStateList(Context context, int resId) {
-        if (isColorInt(context, resId)) {
-            // The resource is a color int, we can't handle it so return null
-            return null;
-        }
-
-        final Resources r = context.getResources();
-        final XmlPullParser xml = r.getXml(resId);
-        try {
-            return AppCompatColorStateListInflater.createFromXml(r, xml, context.getTheme());
-        } catch (Exception e) {
-            Log.e(LOG_TAG, "Failed to inflate ColorStateList, leaving it to the framework", e);
-        }
-        return null;
-    }
-
-    @Nullable
-    private static ColorStateList getCachedColorStateList(@NonNull Context context,
-            @ColorRes int resId) {
-        synchronized (sColorStateCacheLock) {
-            final SparseArray<ColorStateListCacheEntry> entries = sColorStateCaches.get(context);
-            if (entries != null && entries.size() > 0) {
-                final ColorStateListCacheEntry entry = entries.get(resId);
-                if (entry != null) {
-                    if (entry.configuration.equals(context.getResources().getConfiguration())) {
-                        // If the current configuration matches the entry's, we can use it
-                        return entry.value;
-                    } else {
-                        // Otherwise we'll remove the entry
-                        entries.remove(resId);
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
-    private static void addColorStateListToCache(@NonNull Context context, @ColorRes int resId,
-            @NonNull ColorStateList value) {
-        synchronized (sColorStateCacheLock) {
-            SparseArray<ColorStateListCacheEntry> entries = sColorStateCaches.get(context);
-            if (entries == null) {
-                entries = new SparseArray<>();
-                sColorStateCaches.put(context, entries);
-            }
-            entries.append(resId, new ColorStateListCacheEntry(value,
-                    context.getResources().getConfiguration()));
-        }
-    }
-
-    private static boolean isColorInt(@NonNull Context context, @ColorRes int resId) {
-        final Resources r = context.getResources();
-
-        final TypedValue value = getTypedValue();
-        r.getValue(resId, value, true);
-
-        return value.type >= TypedValue.TYPE_FIRST_COLOR_INT
-                && value.type <= TypedValue.TYPE_LAST_COLOR_INT;
-    }
-
-    @NonNull
-    private static TypedValue getTypedValue() {
-        TypedValue tv = TL_TYPED_VALUE.get();
-        if (tv == null) {
-            tv = new TypedValue();
-            TL_TYPED_VALUE.set(tv);
-        }
-        return tv;
-    }
-
-    private static class ColorStateListCacheEntry {
-        final ColorStateList value;
-        final Configuration configuration;
-
-        ColorStateListCacheEntry(@NonNull ColorStateList value,
-                @NonNull Configuration configuration) {
-            this.value = value;
-            this.configuration = configuration;
-        }
-    }
-
-}
diff --git a/android/support/v7/content/res/GrowingArrayUtils.java b/android/support/v7/content/res/GrowingArrayUtils.java
deleted file mode 100644
index 3fe2eb8..0000000
--- a/android/support/v7/content/res/GrowingArrayUtils.java
+++ /dev/null
@@ -1,194 +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.content.res;
-
-import java.lang.reflect.Array;
-
-/**
- * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
- * arrays. Common array operations are implemented for efficient use in dynamic containers.
- *
- * All methods in this class assume that the length of an array is equivalent to its capacity and
- * NOT the number of elements in the array. The current size of the array is always passed in as a
- * parameter.
- */
-final class GrowingArrayUtils {
-
-    /**
-     * Appends an element to the end of the array, growing the array if there is no more room.
-     * @param array The array to which to append the element. This must NOT be null.
-     * @param currentSize The number of elements in the array. Must be less than or equal to
-     *                    array.length.
-     * @param element The element to append.
-     * @return the array to which the element was appended. This may be different than the given
-     *         array.
-     */
-    public static <T> T[] append(T[] array, int currentSize, T element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 > array.length) {
-            T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
-                    growSize(currentSize));
-            System.arraycopy(array, 0, newArray, 0, currentSize);
-            array = newArray;
-        }
-        array[currentSize] = element;
-        return array;
-    }
-
-    /**
-     * Primitive int version of {@link #append(Object[], int, Object)}.
-     */
-    public static int[] append(int[] array, int currentSize, int element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 > array.length) {
-            int[] newArray = new int[growSize(currentSize)];
-            System.arraycopy(array, 0, newArray, 0, currentSize);
-            array = newArray;
-        }
-        array[currentSize] = element;
-        return array;
-    }
-
-    /**
-     * Primitive long version of {@link #append(Object[], int, Object)}.
-     */
-    public static long[] append(long[] array, int currentSize, long element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 > array.length) {
-            long[] newArray = new long[growSize(currentSize)];
-            System.arraycopy(array, 0, newArray, 0, currentSize);
-            array = newArray;
-        }
-        array[currentSize] = element;
-        return array;
-    }
-
-    /**
-     * Primitive boolean version of {@link #append(Object[], int, Object)}.
-     */
-    public static boolean[] append(boolean[] array, int currentSize, boolean element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 > array.length) {
-            boolean[] newArray = new boolean[growSize(currentSize)];
-            System.arraycopy(array, 0, newArray, 0, currentSize);
-            array = newArray;
-        }
-        array[currentSize] = element;
-        return array;
-    }
-
-    /**
-     * Inserts an element into the array at the specified index, growing the array if there is no
-     * more room.
-     *
-     * @param array The array to which to append the element. Must NOT be null.
-     * @param currentSize The number of elements in the array. Must be less than or equal to
-     *                    array.length.
-     * @param element The element to insert.
-     * @return the array to which the element was appended. This may be different than the given
-     *         array.
-     */
-    public static <T> T[] insert(T[] array, int currentSize, int index, T element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 <= array.length) {
-            System.arraycopy(array, index, array, index + 1, currentSize - index);
-            array[index] = element;
-            return array;
-        }
-
-        T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(),
-                growSize(currentSize));
-        System.arraycopy(array, 0, newArray, 0, index);
-        newArray[index] = element;
-        System.arraycopy(array, index, newArray, index + 1, array.length - index);
-        return newArray;
-    }
-
-    /**
-     * Primitive int version of {@link #insert(Object[], int, int, Object)}.
-     */
-    public static int[] insert(int[] array, int currentSize, int index, int element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 <= array.length) {
-            System.arraycopy(array, index, array, index + 1, currentSize - index);
-            array[index] = element;
-            return array;
-        }
-
-        int[] newArray = new int[growSize(currentSize)];
-        System.arraycopy(array, 0, newArray, 0, index);
-        newArray[index] = element;
-        System.arraycopy(array, index, newArray, index + 1, array.length - index);
-        return newArray;
-    }
-
-    /**
-     * Primitive long version of {@link #insert(Object[], int, int, Object)}.
-     */
-    public static long[] insert(long[] array, int currentSize, int index, long element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 <= array.length) {
-            System.arraycopy(array, index, array, index + 1, currentSize - index);
-            array[index] = element;
-            return array;
-        }
-
-        long[] newArray = new long[growSize(currentSize)];
-        System.arraycopy(array, 0, newArray, 0, index);
-        newArray[index] = element;
-        System.arraycopy(array, index, newArray, index + 1, array.length - index);
-        return newArray;
-    }
-
-    /**
-     * Primitive boolean version of {@link #insert(Object[], int, int, Object)}.
-     */
-    public static boolean[] insert(boolean[] array, int currentSize, int index, boolean element) {
-        assert currentSize <= array.length;
-
-        if (currentSize + 1 <= array.length) {
-            System.arraycopy(array, index, array, index + 1, currentSize - index);
-            array[index] = element;
-            return array;
-        }
-
-        boolean[] newArray = new boolean[growSize(currentSize)];
-        System.arraycopy(array, 0, newArray, 0, index);
-        newArray[index] = element;
-        System.arraycopy(array, index, newArray, index + 1, array.length - index);
-        return newArray;
-    }
-
-    /**
-     * Given the current size of an array, returns an ideal size to which the array should grow.
-     * This is typically double the given size, but should not be relied upon to do so in the
-     * future.
-     */
-    public static int growSize(int currentSize) {
-        return currentSize <= 4 ? 8 : currentSize * 2;
-    }
-
-    // Uninstantiable
-    private GrowingArrayUtils() {}
-}
\ No newline at end of file
diff --git a/android/support/v7/graphics/BucketTests.java b/android/support/v7/graphics/BucketTests.java
deleted file mode 100644
index ca8e508..0000000
--- a/android/support/v7/graphics/BucketTests.java
+++ /dev/null
@@ -1,181 +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.graphics;
-
-import static android.support.v7.graphics.TestUtils.assertCloseColors;
-import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-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;
-
-@RunWith(AndroidJUnit4.class)
-public class BucketTests {
-
-    @Test
-    @SmallTest
-    public void testSourceBitmapNotRecycled() {
-        final Bitmap sample = loadSampleBitmap();
-
-        Palette.from(sample).generate();
-        assertFalse(sample.isRecycled());
-    }
-
-    @Test(expected = UnsupportedOperationException.class)
-    @SmallTest
-    public void testSwatchesUnmodifiable() {
-        Palette p = Palette.from(loadSampleBitmap()).generate();
-        p.getSwatches().remove(0);
-    }
-
-    @Test
-    @SmallTest
-    public void testSwatchesBuilder() {
-        ArrayList<Palette.Swatch> swatches = new ArrayList<>();
-        swatches.add(new Palette.Swatch(Color.BLACK, 40));
-        swatches.add(new Palette.Swatch(Color.GREEN, 60));
-        swatches.add(new Palette.Swatch(Color.BLUE, 10));
-
-        Palette p = Palette.from(swatches);
-
-        assertEquals(swatches, p.getSwatches());
-    }
-
-    @Test
-    @SmallTest
-    public void testRegionWhole() {
-        final Bitmap sample = loadSampleBitmap();
-
-        Palette.Builder b = new Palette.Builder(sample);
-        b.setRegion(0, 0, sample.getWidth(), sample.getHeight());
-        b.generate();
-    }
-
-    @Test
-    @SmallTest
-    public void testRegionUpperLeft() {
-        final Bitmap sample = loadSampleBitmap();
-
-        Palette.Builder b = new Palette.Builder(sample);
-        b.setRegion(0, 0, sample.getWidth() / 2, sample.getHeight() / 2);
-        b.generate();
-    }
-
-    @Test
-    @SmallTest
-    public void testRegionBottomRight() {
-        final Bitmap sample = loadSampleBitmap();
-
-        Palette.Builder b = new Palette.Builder(sample);
-        b.setRegion(sample.getWidth() / 2, sample.getHeight() / 2,
-                sample.getWidth(), sample.getHeight());
-        b.generate();
-    }
-
-    @Test
-    @SmallTest
-    public void testOnePixelTallBitmap() {
-        final Bitmap bitmap = Bitmap.createBitmap(1000, 1, Bitmap.Config.ARGB_8888);
-
-        Palette.Builder b = new Palette.Builder(bitmap);
-        b.generate();
-    }
-
-    @Test
-    @SmallTest
-    public void testOnePixelWideBitmap() {
-        final Bitmap bitmap = Bitmap.createBitmap(1, 1000, Bitmap.Config.ARGB_8888);
-
-        Palette.Builder b = new Palette.Builder(bitmap);
-        b.generate();
-    }
-
-    @Test
-    @SmallTest
-    public void testBlueBitmapReturnsBlueSwatch() {
-        final Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        canvas.drawColor(Color.BLUE);
-
-        final Palette palette = Palette.from(bitmap).generate();
-
-        assertEquals(1, palette.getSwatches().size());
-
-        final Palette.Swatch swatch = palette.getSwatches().get(0);
-        assertCloseColors(Color.BLUE, swatch.getRgb());
-    }
-
-    @Test
-    @SmallTest
-    public void testBlueBitmapWithRegionReturnsBlueSwatch() {
-        final Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        canvas.drawColor(Color.BLUE);
-
-        final Palette palette = Palette.from(bitmap)
-                .setRegion(0, bitmap.getHeight() / 2, bitmap.getWidth(), bitmap.getHeight())
-                .generate();
-
-        assertEquals(1, palette.getSwatches().size());
-
-        final Palette.Swatch swatch = palette.getSwatches().get(0);
-        assertCloseColors(Color.BLUE, swatch.getRgb());
-    }
-
-    @Test
-    @SmallTest
-    public void testDominantSwatch() {
-        final Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
-
-        // First fill the canvas with blue
-        Canvas canvas = new Canvas(bitmap);
-        canvas.drawColor(Color.BLUE);
-
-        final Paint paint = new Paint();
-        // Now we'll draw the top 10px tall rect with green
-        paint.setColor(Color.GREEN);
-        canvas.drawRect(0, 0, 100, 10, paint);
-
-        // Now we'll draw the next 20px tall rect with red
-        paint.setColor(Color.RED);
-        canvas.drawRect(0, 11, 100, 30, paint);
-
-        // Now generate a palette from the bitmap
-        final Palette palette = Palette.from(bitmap).generate();
-
-        // First assert that there are 3 swatches
-        assertEquals(3, palette.getSwatches().size());
-
-        // Now assert that the dominant swatch is blue
-        final Palette.Swatch swatch = palette.getDominantSwatch();
-        assertNotNull(swatch);
-        assertCloseColors(Color.BLUE, swatch.getRgb());
-    }
-
-}
diff --git a/android/support/v7/graphics/ColorCutQuantizer.java b/android/support/v7/graphics/ColorCutQuantizer.java
deleted file mode 100644
index 6526715..0000000
--- a/android/support/v7/graphics/ColorCutQuantizer.java
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Copyright 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.graphics;
-
-import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v7.graphics.Palette.Swatch;
-import android.util.TimingLogger;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.PriorityQueue;
-
-/**
- * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct
- * colors rather than representation colors.
- *
- * The color space is represented as a 3-dimensional cube with each dimension being an RGB
- * component. The cube is then repeatedly divided until we have reduced the color space to the
- * requested number of colors. An average color is then generated from each cube.
- *
- * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes
- * have roughly the same population, where this quantizer divides boxes based on their color volume.
- * This means that the color space is divided into distinct colors, rather than representative
- * colors.
- */
-final class ColorCutQuantizer {
-
-    private static final String LOG_TAG = "ColorCutQuantizer";
-    private static final boolean LOG_TIMINGS = false;
-
-    static final int COMPONENT_RED = -3;
-    static final int COMPONENT_GREEN = -2;
-    static final int COMPONENT_BLUE = -1;
-
-    private static final int QUANTIZE_WORD_WIDTH = 5;
-    private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1;
-
-    final int[] mColors;
-    final int[] mHistogram;
-    final List<Swatch> mQuantizedColors;
-    final TimingLogger mTimingLogger;
-    final Palette.Filter[] mFilters;
-
-    private final float[] mTempHsl = new float[3];
-
-    /**
-     * Constructor.
-     *
-     * @param pixels histogram representing an image's pixel data
-     * @param maxColors The maximum number of colors that should be in the result palette.
-     * @param filters Set of filters to use in the quantization stage
-     */
-    ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
-        mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
-        mFilters = filters;
-
-        final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
-        for (int i = 0; i < pixels.length; i++) {
-            final int quantizedColor = quantizeFromRgb888(pixels[i]);
-            // Now update the pixel value to the quantized value
-            pixels[i] = quantizedColor;
-            // And update the histogram
-            hist[quantizedColor]++;
-        }
-
-        if (LOG_TIMINGS) {
-            mTimingLogger.addSplit("Histogram created");
-        }
-
-        // Now let's count the number of distinct colors
-        int distinctColorCount = 0;
-        for (int color = 0; color < hist.length; color++) {
-            if (hist[color] > 0 && shouldIgnoreColor(color)) {
-                // If we should ignore the color, set the population to 0
-                hist[color] = 0;
-            }
-            if (hist[color] > 0) {
-                // If the color has population, increase the distinct color count
-                distinctColorCount++;
-            }
-        }
-
-        if (LOG_TIMINGS) {
-            mTimingLogger.addSplit("Filtered colors and distinct colors counted");
-        }
-
-        // Now lets go through create an array consisting of only distinct colors
-        final int[] colors = mColors = new int[distinctColorCount];
-        int distinctColorIndex = 0;
-        for (int color = 0; color < hist.length; color++) {
-            if (hist[color] > 0) {
-                colors[distinctColorIndex++] = color;
-            }
-        }
-
-        if (LOG_TIMINGS) {
-            mTimingLogger.addSplit("Distinct colors copied into array");
-        }
-
-        if (distinctColorCount <= maxColors) {
-            // The image has fewer colors than the maximum requested, so just return the colors
-            mQuantizedColors = new ArrayList<>();
-            for (int color : colors) {
-                mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color]));
-            }
-
-            if (LOG_TIMINGS) {
-                mTimingLogger.addSplit("Too few colors present. Copied to Swatches");
-                mTimingLogger.dumpToLog();
-            }
-        } else {
-            // We need use quantization to reduce the number of colors
-            mQuantizedColors = quantizePixels(maxColors);
-
-            if (LOG_TIMINGS) {
-                mTimingLogger.addSplit("Quantized colors computed");
-                mTimingLogger.dumpToLog();
-            }
-        }
-    }
-
-    /**
-     * @return the list of quantized colors
-     */
-    List<Swatch> getQuantizedColors() {
-        return mQuantizedColors;
-    }
-
-    private List<Swatch> quantizePixels(int maxColors) {
-        // Create the priority queue which is sorted by volume descending. This means we always
-        // split the largest box in the queue
-        final PriorityQueue<Vbox> pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME);
-
-        // To start, offer a box which contains all of the colors
-        pq.offer(new Vbox(0, mColors.length - 1));
-
-        // Now go through the boxes, splitting them until we have reached maxColors or there are no
-        // more boxes to split
-        splitBoxes(pq, maxColors);
-
-        // Finally, return the average colors of the color boxes
-        return generateAverageColors(pq);
-    }
-
-    /**
-     * Iterate through the {@link java.util.Queue}, popping
-     * {@link ColorCutQuantizer.Vbox} objects from the queue
-     * and splitting them. Once split, the new box and the remaining box are offered back to the
-     * queue.
-     *
-     * @param queue {@link java.util.PriorityQueue} to poll for boxes
-     * @param maxSize Maximum amount of boxes to split
-     */
-    private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
-        while (queue.size() < maxSize) {
-            final Vbox vbox = queue.poll();
-
-            if (vbox != null && vbox.canSplit()) {
-                // First split the box, and offer the result
-                queue.offer(vbox.splitBox());
-
-                if (LOG_TIMINGS) {
-                    mTimingLogger.addSplit("Box split");
-                }
-                // Then offer the box back
-                queue.offer(vbox);
-            } else {
-                if (LOG_TIMINGS) {
-                    mTimingLogger.addSplit("All boxes split");
-                }
-                // If we get here then there are no more boxes to split, so return
-                return;
-            }
-        }
-    }
-
-    private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) {
-        ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
-        for (Vbox vbox : vboxes) {
-            Swatch swatch = vbox.getAverageColor();
-            if (!shouldIgnoreColor(swatch)) {
-                // As we're averaging a color box, we can still get colors which we do not want, so
-                // we check again here
-                colors.add(swatch);
-            }
-        }
-        return colors;
-    }
-
-    /**
-     * Represents a tightly fitting box around a color space.
-     */
-    private class Vbox {
-        // lower and upper index are inclusive
-        private int mLowerIndex;
-        private int mUpperIndex;
-        // Population of colors within this box
-        private int mPopulation;
-
-        private int mMinRed, mMaxRed;
-        private int mMinGreen, mMaxGreen;
-        private int mMinBlue, mMaxBlue;
-
-        Vbox(int lowerIndex, int upperIndex) {
-            mLowerIndex = lowerIndex;
-            mUpperIndex = upperIndex;
-            fitBox();
-        }
-
-        final int getVolume() {
-            return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) *
-                    (mMaxBlue - mMinBlue + 1);
-        }
-
-        final boolean canSplit() {
-            return getColorCount() > 1;
-        }
-
-        final int getColorCount() {
-            return 1 + mUpperIndex - mLowerIndex;
-        }
-
-        /**
-         * Recomputes the boundaries of this box to tightly fit the colors within the box.
-         */
-        final void fitBox() {
-            final int[] colors = mColors;
-            final int[] hist = mHistogram;
-
-            // Reset the min and max to opposite values
-            int minRed, minGreen, minBlue;
-            minRed = minGreen = minBlue = Integer.MAX_VALUE;
-            int maxRed, maxGreen, maxBlue;
-            maxRed = maxGreen = maxBlue = Integer.MIN_VALUE;
-            int count = 0;
-
-            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
-                final int color = colors[i];
-                count += hist[color];
-
-                final int r = quantizedRed(color);
-                final int g = quantizedGreen(color);
-                final int b = quantizedBlue(color);
-                if (r > maxRed) {
-                    maxRed = r;
-                }
-                if (r < minRed) {
-                    minRed = r;
-                }
-                if (g > maxGreen) {
-                    maxGreen = g;
-                }
-                if (g < minGreen) {
-                    minGreen = g;
-                }
-                if (b > maxBlue) {
-                    maxBlue = b;
-                }
-                if (b < minBlue) {
-                    minBlue = b;
-                }
-            }
-
-            mMinRed = minRed;
-            mMaxRed = maxRed;
-            mMinGreen = minGreen;
-            mMaxGreen = maxGreen;
-            mMinBlue = minBlue;
-            mMaxBlue = maxBlue;
-            mPopulation = count;
-        }
-
-        /**
-         * Split this color box at the mid-point along its longest dimension
-         *
-         * @return the new ColorBox
-         */
-        final Vbox splitBox() {
-            if (!canSplit()) {
-                throw new IllegalStateException("Can not split a box with only 1 color");
-            }
-
-            // find median along the longest dimension
-            final int splitPoint = findSplitPoint();
-
-            Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex);
-
-            // Now change this box's upperIndex and recompute the color boundaries
-            mUpperIndex = splitPoint;
-            fitBox();
-
-            return newBox;
-        }
-
-        /**
-         * @return the dimension which this box is largest in
-         */
-        final int getLongestColorDimension() {
-            final int redLength = mMaxRed - mMinRed;
-            final int greenLength = mMaxGreen - mMinGreen;
-            final int blueLength = mMaxBlue - mMinBlue;
-
-            if (redLength >= greenLength && redLength >= blueLength) {
-                return COMPONENT_RED;
-            } else if (greenLength >= redLength && greenLength >= blueLength) {
-                return COMPONENT_GREEN;
-            } else {
-                return COMPONENT_BLUE;
-            }
-        }
-
-        /**
-         * Finds the point within this box's lowerIndex and upperIndex index of where to split.
-         *
-         * This is calculated by finding the longest color dimension, and then sorting the
-         * sub-array based on that dimension value in each color. The colors are then iterated over
-         * until a color is found with at least the midpoint of the whole box's dimension midpoint.
-         *
-         * @return the index of the colors array to split from
-         */
-        final int findSplitPoint() {
-            final int longestDimension = getLongestColorDimension();
-            final int[] colors = mColors;
-            final int[] hist = mHistogram;
-
-            // We need to sort the colors in this box based on the longest color dimension.
-            // As we can't use a Comparator to define the sort logic, we modify each color so that
-            // its most significant is the desired dimension
-            modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
-
-            // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1
-            Arrays.sort(colors, mLowerIndex, mUpperIndex + 1);
-
-            // Now revert all of the colors so that they are packed as RGB again
-            modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
-
-            final int midPoint = mPopulation / 2;
-            for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++)  {
-                count += hist[colors[i]];
-                if (count >= midPoint) {
-                    // we never want to split on the upperIndex, as this will result in the same
-                    // box
-                    return Math.min(mUpperIndex - 1, i);
-                }
-            }
-
-            return mLowerIndex;
-        }
-
-        /**
-         * @return the average color of this box.
-         */
-        final Swatch getAverageColor() {
-            final int[] colors = mColors;
-            final int[] hist = mHistogram;
-            int redSum = 0;
-            int greenSum = 0;
-            int blueSum = 0;
-            int totalPopulation = 0;
-
-            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
-                final int color = colors[i];
-                final int colorPopulation = hist[color];
-
-                totalPopulation += colorPopulation;
-                redSum += colorPopulation * quantizedRed(color);
-                greenSum += colorPopulation * quantizedGreen(color);
-                blueSum += colorPopulation * quantizedBlue(color);
-            }
-
-            final int redMean = Math.round(redSum / (float) totalPopulation);
-            final int greenMean = Math.round(greenSum / (float) totalPopulation);
-            final int blueMean = Math.round(blueSum / (float) totalPopulation);
-
-            return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation);
-        }
-    }
-
-    /**
-     * Modify the significant octet in a packed color int. Allows sorting based on the value of a
-     * single color component. This relies on all components being the same word size.
-     *
-     * @see Vbox#findSplitPoint()
-     */
-    static void modifySignificantOctet(final int[] a, final int dimension,
-            final int lower, final int upper) {
-        switch (dimension) {
-            case COMPONENT_RED:
-                // Already in RGB, no need to do anything
-                break;
-            case COMPONENT_GREEN:
-                // We need to do a RGB to GRB swap, or vice-versa
-                for (int i = lower; i <= upper; i++) {
-                    final int color = a[i];
-                    a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
-                            | quantizedRed(color) << QUANTIZE_WORD_WIDTH
-                            | quantizedBlue(color);
-                }
-                break;
-            case COMPONENT_BLUE:
-                // We need to do a RGB to BGR swap, or vice-versa
-                for (int i = lower; i <= upper; i++) {
-                    final int color = a[i];
-                    a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
-                            | quantizedGreen(color) << QUANTIZE_WORD_WIDTH
-                            | quantizedRed(color);
-                }
-                break;
-        }
-    }
-
-    private boolean shouldIgnoreColor(int color565) {
-        final int rgb = approximateToRgb888(color565);
-        ColorUtils.colorToHSL(rgb, mTempHsl);
-        return shouldIgnoreColor(rgb, mTempHsl);
-    }
-
-    private boolean shouldIgnoreColor(Swatch color) {
-        return shouldIgnoreColor(color.getRgb(), color.getHsl());
-    }
-
-    private boolean shouldIgnoreColor(int rgb, float[] hsl) {
-        if (mFilters != null && mFilters.length > 0) {
-            for (int i = 0, count = mFilters.length; i < count; i++) {
-                if (!mFilters[i].isAllowed(rgb, hsl)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
-     */
-    private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {
-        @Override
-        public int compare(Vbox lhs, Vbox rhs) {
-            return rhs.getVolume() - lhs.getVolume();
-        }
-    };
-
-    /**
-     * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
-     */
-    private static int quantizeFromRgb888(int color) {
-        int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH);
-        int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH);
-        int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH);
-        return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b;
-    }
-
-    /**
-     * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
-     */
-    static int approximateToRgb888(int r, int g, int b) {
-        return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8),
-                modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8),
-                modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8));
-    }
-
-    private static int approximateToRgb888(int color) {
-        return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color));
-    }
-
-    /**
-     * @return red component of the quantized color
-     */
-    static int quantizedRed(int color) {
-        return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK;
-    }
-
-    /**
-     * @return green component of a quantized color
-     */
-    static int quantizedGreen(int color) {
-        return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK;
-    }
-
-    /**
-     * @return blue component of a quantized color
-     */
-    static int quantizedBlue(int color) {
-        return color & QUANTIZE_WORD_MASK;
-    }
-
-    private static int modifyWordWidth(int value, int currentWidth, int targetWidth) {
-        final int newValue;
-        if (targetWidth > currentWidth) {
-            // If we're approximating up in word width, we'll shift up
-            newValue = value << (targetWidth - currentWidth);
-        } else {
-            // Else, we will just shift and keep the MSB
-            newValue = value >> (currentWidth - targetWidth);
-        }
-        return newValue & ((1 << targetWidth) - 1);
-    }
-
-}
diff --git a/android/support/v7/graphics/ConsistencyTest.java b/android/support/v7/graphics/ConsistencyTest.java
deleted file mode 100644
index d9ac12e..0000000
--- a/android/support/v7/graphics/ConsistencyTest.java
+++ /dev/null
@@ -1,58 +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.graphics;
-
-import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
-
-import static org.junit.Assert.assertEquals;
-
-import android.graphics.Bitmap;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class ConsistencyTest {
-
-    private static final int NUMBER_TRIALS = 10;
-
-    @Test
-    @MediumTest
-    public void testConsistency() {
-        Palette lastPalette = null;
-        final Bitmap bitmap = loadSampleBitmap();
-
-        for (int i = 0; i < NUMBER_TRIALS; i++) {
-            Palette newPalette = Palette.from(bitmap).generate();
-            if (lastPalette != null) {
-                assetPalettesEqual(lastPalette, newPalette);
-            }
-            lastPalette = newPalette;
-        }
-    }
-
-    private static void assetPalettesEqual(Palette p1, Palette p2) {
-        assertEquals(p1.getVibrantSwatch(), p2.getVibrantSwatch());
-        assertEquals(p1.getLightVibrantSwatch(), p2.getLightVibrantSwatch());
-        assertEquals(p1.getDarkVibrantSwatch(), p2.getDarkVibrantSwatch());
-        assertEquals(p1.getMutedSwatch(), p2.getMutedSwatch());
-        assertEquals(p1.getLightMutedSwatch(), p2.getLightMutedSwatch());
-        assertEquals(p1.getDarkMutedSwatch(), p2.getDarkMutedSwatch());
-    }
-}
diff --git a/android/support/v7/graphics/MaxColorsTest.java b/android/support/v7/graphics/MaxColorsTest.java
deleted file mode 100644
index fbcf6ae..0000000
--- a/android/support/v7/graphics/MaxColorsTest.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.v7.graphics;
-
-import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
-
-import static org.junit.Assert.assertTrue;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class MaxColorsTest {
-
-    @Test
-    @SmallTest
-    public void testMaxColorCount32() {
-        testMaxColorCount(32);
-    }
-
-    @Test
-    @SmallTest
-    public void testMaxColorCount1() {
-        testMaxColorCount(1);
-    }
-
-    @Test
-    @SmallTest
-    public void testMaxColorCount15() {
-        testMaxColorCount(15);
-    }
-
-    private void testMaxColorCount(int colorCount) {
-        Palette newPalette = Palette.from(loadSampleBitmap())
-                .maximumColorCount(colorCount)
-                .generate();
-        assertTrue(newPalette.getSwatches().size() <= colorCount);
-    }
-}
diff --git a/android/support/v7/graphics/Palette.java b/android/support/v7/graphics/Palette.java
deleted file mode 100644
index e716fb5..0000000
--- a/android/support/v7/graphics/Palette.java
+++ /dev/null
@@ -1,986 +0,0 @@
-/*
- * Copyright 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.graphics;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.AsyncTask;
-import android.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
-import android.support.v4.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.util.TimingLogger;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A helper class to extract prominent colors from an image.
- * <p>
- * A number of colors with different profiles are extracted from the image:
- * <ul>
- *     <li>Vibrant</li>
- *     <li>Vibrant Dark</li>
- *     <li>Vibrant Light</li>
- *     <li>Muted</li>
- *     <li>Muted Dark</li>
- *     <li>Muted Light</li>
- * </ul>
- * These can be retrieved from the appropriate getter method.
- *
- * <p>
- * Instances are created with a {@link Builder} which supports several options to tweak the
- * generated Palette. See that class' documentation for more information.
- * <p>
- * Generation should always be completed on a background thread, ideally the one in
- * which you load your image on. {@link Builder} supports both synchronous and asynchronous
- * generation:
- *
- * <pre>
- * // Synchronous
- * Palette p = Palette.from(bitmap).generate();
- *
- * // Asynchronous
- * Palette.from(bitmap).generate(new PaletteAsyncListener() {
- *     public void onGenerated(Palette p) {
- *         // Use generated instance
- *     }
- * });
- * </pre>
- */
-public final class Palette {
-
-    /**
-     * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or
-     * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}
-     */
-    public interface PaletteAsyncListener {
-
-        /**
-         * Called when the {@link Palette} has been generated.
-         */
-        void onGenerated(@NonNull Palette palette);
-    }
-
-    static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112;
-    static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
-
-    static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
-    static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
-
-    static final String LOG_TAG = "Palette";
-    static final boolean LOG_TIMINGS = false;
-
-    /**
-     * Start generating a {@link Palette} with the returned {@link Builder} instance.
-     */
-    @NonNull
-    public static Builder from(@NonNull Bitmap bitmap) {
-        return new Builder(bitmap);
-    }
-
-    /**
-     * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches.
-     * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a
-     * list of swatches. Will return null if the {@code swatches} is null.
-     */
-    @NonNull
-    public static Palette from(@NonNull List<Swatch> swatches) {
-        return new Builder(swatches).generate();
-    }
-
-    /**
-     * @deprecated Use {@link Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static Palette generate(Bitmap bitmap) {
-        return from(bitmap).generate();
-    }
-
-    /**
-     * @deprecated Use {@link Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static Palette generate(Bitmap bitmap, int numColors) {
-        return from(bitmap).maximumColorCount(numColors).generate();
-    }
-
-    /**
-     * @deprecated Use {@link Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
-            Bitmap bitmap, PaletteAsyncListener listener) {
-        return from(bitmap).generate(listener);
-    }
-
-    /**
-     * @deprecated Use {@link Builder} to generate the Palette.
-     */
-    @Deprecated
-    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
-            final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) {
-        return from(bitmap).maximumColorCount(numColors).generate(listener);
-    }
-
-    private final List<Swatch> mSwatches;
-    private final List<Target> mTargets;
-
-    private final Map<Target, Swatch> mSelectedSwatches;
-    private final SparseBooleanArray mUsedColors;
-
-    private final Swatch mDominantSwatch;
-
-    Palette(List<Swatch> swatches, List<Target> targets) {
-        mSwatches = swatches;
-        mTargets = targets;
-
-        mUsedColors = new SparseBooleanArray();
-        mSelectedSwatches = new ArrayMap<>();
-
-        mDominantSwatch = findDominantSwatch();
-    }
-
-    /**
-     * Returns all of the swatches which make up the palette.
-     */
-    @NonNull
-    public List<Swatch> getSwatches() {
-        return Collections.unmodifiableList(mSwatches);
-    }
-
-    /**
-     * Returns the targets used to generate this palette.
-     */
-    @NonNull
-    public List<Target> getTargets() {
-        return Collections.unmodifiableList(mTargets);
-    }
-
-    /**
-     * Returns the most vibrant swatch in the palette. Might be null.
-     *
-     * @see Target#VIBRANT
-     */
-    @Nullable
-    public Swatch getVibrantSwatch() {
-        return getSwatchForTarget(Target.VIBRANT);
-    }
-
-    /**
-     * Returns a light and vibrant swatch from the palette. Might be null.
-     *
-     * @see Target#LIGHT_VIBRANT
-     */
-    @Nullable
-    public Swatch getLightVibrantSwatch() {
-        return getSwatchForTarget(Target.LIGHT_VIBRANT);
-    }
-
-    /**
-     * Returns a dark and vibrant swatch from the palette. Might be null.
-     *
-     * @see Target#DARK_VIBRANT
-     */
-    @Nullable
-    public Swatch getDarkVibrantSwatch() {
-        return getSwatchForTarget(Target.DARK_VIBRANT);
-    }
-
-    /**
-     * Returns a muted swatch from the palette. Might be null.
-     *
-     * @see Target#MUTED
-     */
-    @Nullable
-    public Swatch getMutedSwatch() {
-        return getSwatchForTarget(Target.MUTED);
-    }
-
-    /**
-     * Returns a muted and light swatch from the palette. Might be null.
-     *
-     * @see Target#LIGHT_MUTED
-     */
-    @Nullable
-    public Swatch getLightMutedSwatch() {
-        return getSwatchForTarget(Target.LIGHT_MUTED);
-    }
-
-    /**
-     * Returns a muted and dark swatch from the palette. Might be null.
-     *
-     * @see Target#DARK_MUTED
-     */
-    @Nullable
-    public Swatch getDarkMutedSwatch() {
-        return getSwatchForTarget(Target.DARK_MUTED);
-    }
-
-    /**
-     * Returns the most vibrant color in the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getVibrantSwatch()
-     */
-    @ColorInt
-    public int getVibrantColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.VIBRANT, defaultColor);
-    }
-
-    /**
-     * Returns a light and vibrant color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getLightVibrantSwatch()
-     */
-    @ColorInt
-    public int getLightVibrantColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor);
-    }
-
-    /**
-     * Returns a dark and vibrant color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getDarkVibrantSwatch()
-     */
-    @ColorInt
-    public int getDarkVibrantColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.DARK_VIBRANT, defaultColor);
-    }
-
-    /**
-     * Returns a muted color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getMutedSwatch()
-     */
-    @ColorInt
-    public int getMutedColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.MUTED, defaultColor);
-    }
-
-    /**
-     * Returns a muted and light color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getLightMutedSwatch()
-     */
-    @ColorInt
-    public int getLightMutedColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.LIGHT_MUTED, defaultColor);
-    }
-
-    /**
-     * Returns a muted and dark color from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getDarkMutedSwatch()
-     */
-    @ColorInt
-    public int getDarkMutedColor(@ColorInt final int defaultColor) {
-        return getColorForTarget(Target.DARK_MUTED, defaultColor);
-    }
-
-    /**
-     * Returns the selected swatch for the given target from the palette, or {@code null} if one
-     * could not be found.
-     */
-    @Nullable
-    public Swatch getSwatchForTarget(@NonNull final Target target) {
-        return mSelectedSwatches.get(target);
-    }
-
-    /**
-     * Returns the selected color for the given target from the palette as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     */
-    @ColorInt
-    public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) {
-        Swatch swatch = getSwatchForTarget(target);
-        return swatch != null ? swatch.getRgb() : defaultColor;
-    }
-
-    /**
-     * Returns the dominant swatch from the palette.
-     *
-     * <p>The dominant swatch is defined as the swatch with the greatest population (frequency)
-     * within the palette.</p>
-     */
-    @Nullable
-    public Swatch getDominantSwatch() {
-        return mDominantSwatch;
-    }
-
-    /**
-     * Returns the color of the dominant swatch from the palette, as an RGB packed int.
-     *
-     * @param defaultColor value to return if the swatch isn't available
-     * @see #getDominantSwatch()
-     */
-    @ColorInt
-    public int getDominantColor(@ColorInt int defaultColor) {
-        return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor;
-    }
-
-    void generate() {
-        // We need to make sure that the scored targets are generated first. This is so that
-        // inherited targets have something to inherit from
-        for (int i = 0, count = mTargets.size(); i < count; i++) {
-            final Target target = mTargets.get(i);
-            target.normalizeWeights();
-            mSelectedSwatches.put(target, generateScoredTarget(target));
-        }
-        // We now clear out the used colors
-        mUsedColors.clear();
-    }
-
-    private Swatch generateScoredTarget(final Target target) {
-        final Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target);
-        if (maxScoreSwatch != null && target.isExclusive()) {
-            // If we have a swatch, and the target is exclusive, add the color to the used list
-            mUsedColors.append(maxScoreSwatch.getRgb(), true);
-        }
-        return maxScoreSwatch;
-    }
-
-    private Swatch getMaxScoredSwatchForTarget(final Target target) {
-        float maxScore = 0;
-        Swatch maxScoreSwatch = null;
-        for (int i = 0, count = mSwatches.size(); i < count; i++) {
-            final Swatch swatch = mSwatches.get(i);
-            if (shouldBeScoredForTarget(swatch, target)) {
-                final float score = generateScore(swatch, target);
-                if (maxScoreSwatch == null || score > maxScore) {
-                    maxScoreSwatch = swatch;
-                    maxScore = score;
-                }
-            }
-        }
-        return maxScoreSwatch;
-    }
-
-    private boolean shouldBeScoredForTarget(final Swatch swatch, final Target target) {
-        // Check whether the HSL values are within the correct ranges, and this color hasn't
-        // been used yet.
-        final float hsl[] = swatch.getHsl();
-        return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation()
-                && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness()
-                && !mUsedColors.get(swatch.getRgb());
-    }
-
-    private float generateScore(Swatch swatch, Target target) {
-        final float[] hsl = swatch.getHsl();
-
-        float saturationScore = 0;
-        float luminanceScore = 0;
-        float populationScore = 0;
-
-        final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1;
-
-        if (target.getSaturationWeight() > 0) {
-            saturationScore = target.getSaturationWeight()
-                    * (1f - Math.abs(hsl[1] - target.getTargetSaturation()));
-        }
-        if (target.getLightnessWeight() > 0) {
-            luminanceScore = target.getLightnessWeight()
-                    * (1f - Math.abs(hsl[2] - target.getTargetLightness()));
-        }
-        if (target.getPopulationWeight() > 0) {
-            populationScore = target.getPopulationWeight()
-                    * (swatch.getPopulation() / (float) maxPopulation);
-        }
-
-        return saturationScore + luminanceScore + populationScore;
-    }
-
-    private Swatch findDominantSwatch() {
-        int maxPop = Integer.MIN_VALUE;
-        Swatch maxSwatch = null;
-        for (int i = 0, count = mSwatches.size(); i < count; i++) {
-            Swatch swatch = mSwatches.get(i);
-            if (swatch.getPopulation() > maxPop) {
-                maxSwatch = swatch;
-                maxPop = swatch.getPopulation();
-            }
-        }
-        return maxSwatch;
-    }
-
-    private static float[] copyHslValues(Swatch color) {
-        final float[] newHsl = new float[3];
-        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
-        return newHsl;
-    }
-
-    /**
-     * Represents a color swatch generated from an image's palette. The RGB color can be retrieved
-     * by calling {@link #getRgb()}.
-     */
-    public static final class Swatch {
-        private final int mRed, mGreen, mBlue;
-        private final int mRgb;
-        private final int mPopulation;
-
-        private boolean mGeneratedTextColors;
-        private int mTitleTextColor;
-        private int mBodyTextColor;
-
-        private float[] mHsl;
-
-        public Swatch(@ColorInt int color, int population) {
-            mRed = Color.red(color);
-            mGreen = Color.green(color);
-            mBlue = Color.blue(color);
-            mRgb = color;
-            mPopulation = population;
-        }
-
-        Swatch(int red, int green, int blue, int population) {
-            mRed = red;
-            mGreen = green;
-            mBlue = blue;
-            mRgb = Color.rgb(red, green, blue);
-            mPopulation = population;
-        }
-
-        Swatch(float[] hsl, int population) {
-            this(ColorUtils.HSLToColor(hsl), population);
-            mHsl = hsl;
-        }
-
-        /**
-         * @return this swatch's RGB color value
-         */
-        @ColorInt
-        public int getRgb() {
-            return mRgb;
-        }
-
-        /**
-         * Return this swatch's HSL values.
-         *     hsv[0] is Hue [0 .. 360)
-         *     hsv[1] is Saturation [0...1]
-         *     hsv[2] is Lightness [0...1]
-         */
-        @NonNull
-        public float[] getHsl() {
-            if (mHsl == null) {
-                mHsl = new float[3];
-            }
-            ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl);
-            return mHsl;
-        }
-
-        /**
-         * @return the number of pixels represented by this swatch
-         */
-        public int getPopulation() {
-            return mPopulation;
-        }
-
-        /**
-         * Returns an appropriate color to use for any 'title' text which is displayed over this
-         * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
-         */
-        @ColorInt
-        public int getTitleTextColor() {
-            ensureTextColorsGenerated();
-            return mTitleTextColor;
-        }
-
-        /**
-         * Returns an appropriate color to use for any 'body' text which is displayed over this
-         * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
-         */
-        @ColorInt
-        public int getBodyTextColor() {
-            ensureTextColorsGenerated();
-            return mBodyTextColor;
-        }
-
-        private void ensureTextColorsGenerated() {
-            if (!mGeneratedTextColors) {
-                // First check white, as most colors will be dark
-                final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT);
-                final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT);
-
-                if (lightBodyAlpha != -1 && lightTitleAlpha != -1) {
-                    // If we found valid light values, use them and return
-                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha);
-                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha);
-                    mGeneratedTextColors = true;
-                    return;
-                }
-
-                final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT);
-                final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
-                        Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
-
-                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);
-                    mGeneratedTextColors = true;
-                    return;
-                }
-
-                // If we reach here then we can not find title and body values which use the same
-                // lightness, we need to use mismatched values
-                mBodyTextColor = lightBodyAlpha != -1
-                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha)
-                        : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
-                mTitleTextColor = lightTitleAlpha != -1
-                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha)
-                        : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
-                mGeneratedTextColors = true;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return new StringBuilder(getClass().getSimpleName())
-                    .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']')
-                    .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']')
-                    .append(" [Population: ").append(mPopulation).append(']')
-                    .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor()))
-                    .append(']')
-                    .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor()))
-                    .append(']').toString();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            Swatch swatch = (Swatch) o;
-            return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;
-        }
-
-        @Override
-        public int hashCode() {
-            return 31 * mRgb + mPopulation;
-        }
-    }
-
-    /**
-     * Builder class for generating {@link Palette} instances.
-     */
-    public static final class Builder {
-        private final List<Swatch> mSwatches;
-        private final Bitmap mBitmap;
-
-        private final List<Target> mTargets = new ArrayList<>();
-
-        private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS;
-        private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA;
-        private int mResizeMaxDimension = -1;
-
-        private final List<Filter> mFilters = new ArrayList<>();
-        private Rect mRegion;
-
-        /**
-         * Construct a new {@link Builder} using a source {@link Bitmap}
-         */
-        public Builder(@NonNull Bitmap bitmap) {
-            if (bitmap == null || bitmap.isRecycled()) {
-                throw new IllegalArgumentException("Bitmap is not valid");
-            }
-            mFilters.add(DEFAULT_FILTER);
-            mBitmap = bitmap;
-            mSwatches = null;
-
-            // Add the default targets
-            mTargets.add(Target.LIGHT_VIBRANT);
-            mTargets.add(Target.VIBRANT);
-            mTargets.add(Target.DARK_VIBRANT);
-            mTargets.add(Target.LIGHT_MUTED);
-            mTargets.add(Target.MUTED);
-            mTargets.add(Target.DARK_MUTED);
-        }
-
-        /**
-         * Construct a new {@link Builder} using a list of {@link Swatch} instances.
-         * Typically only used for testing.
-         */
-        public Builder(@NonNull List<Swatch> swatches) {
-            if (swatches == null || swatches.isEmpty()) {
-                throw new IllegalArgumentException("List of Swatches is not valid");
-            }
-            mFilters.add(DEFAULT_FILTER);
-            mSwatches = swatches;
-            mBitmap = null;
-        }
-
-        /**
-         * Set the maximum number of colors to use in the quantization step when using a
-         * {@link android.graphics.Bitmap} as the source.
-         * <p>
-         * Good values for depend on the source image type. For landscapes, good values are in
-         * the range 10-16. For images which are largely made up of people's faces then this
-         * value should be increased to ~24.
-         */
-        @NonNull
-        public Builder maximumColorCount(int colors) {
-            mMaxColors = colors;
-            return this;
-        }
-
-        /**
-         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
-         * If the bitmap's largest dimension is greater than the value specified, then the bitmap
-         * will be resized so that its largest dimension matches {@code maxDimension}. If the
-         * bitmap is smaller or equal, the original is used as-is.
-         *
-         * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle
-         * abnormal aspect ratios more gracefully.
-         *
-         * @param maxDimension the number of pixels that the max dimension should be scaled down to,
-         *                     or any value <= 0 to disable resizing.
-         */
-        @NonNull
-        @Deprecated
-        public Builder resizeBitmapSize(final int maxDimension) {
-            mResizeMaxDimension = maxDimension;
-            mResizeArea = -1;
-            return this;
-        }
-
-        /**
-         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
-         * If the bitmap's area is greater than the value specified, then the bitmap
-         * will be resized so that its area matches {@code area}. If the
-         * bitmap is smaller or equal, the original is used as-is.
-         * <p>
-         * This value has a large effect on the processing time. The larger the resized image is,
-         * the greater time it will take to generate the palette. The smaller the image is, the
-         * more detail is lost in the resulting image and thus less precision for color selection.
-         *
-         * @param area the number of pixels that the intermediary scaled down Bitmap should cover,
-         *             or any value <= 0 to disable resizing.
-         */
-        @NonNull
-        public Builder resizeBitmapArea(final int area) {
-            mResizeArea = area;
-            mResizeMaxDimension = -1;
-            return this;
-        }
-
-        /**
-         * Clear all added filters. This includes any default filters added automatically by
-         * {@link Palette}.
-         */
-        @NonNull
-        public Builder clearFilters() {
-            mFilters.clear();
-            return this;
-        }
-
-        /**
-         * Add a filter to be able to have fine grained control over which colors are
-         * allowed in the resulting palette.
-         *
-         * @param filter filter to add.
-         */
-        @NonNull
-        public Builder addFilter(Filter filter) {
-            if (filter != null) {
-                mFilters.add(filter);
-            }
-            return this;
-        }
-
-        /**
-         * Set a region of the bitmap to be used exclusively when calculating the palette.
-         * <p>This only works when the original input is a {@link Bitmap}.</p>
-         *
-         * @param left The left side of the rectangle used for the region.
-         * @param top The top of the rectangle used for the region.
-         * @param right The right side of the rectangle used for the region.
-         * @param bottom The bottom of the rectangle used for the region.
-         */
-        @NonNull
-        public Builder setRegion(int left, int top, int right, int bottom) {
-            if (mBitmap != null) {
-                if (mRegion == null) mRegion = new Rect();
-                // Set the Rect to be initially the whole Bitmap
-                mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
-                // Now just get the intersection with the region
-                if (!mRegion.intersect(left, top, right, bottom)) {
-                    throw new IllegalArgumentException("The given region must intersect with "
-                            + "the Bitmap's dimensions.");
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Clear any previously region set via {@link #setRegion(int, int, int, int)}.
-         */
-        @NonNull
-        public Builder clearRegion() {
-            mRegion = null;
-            return this;
-        }
-
-        /**
-         * Add a target profile to be generated in the palette.
-         *
-         * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p>
-         */
-        @NonNull
-        public Builder addTarget(@NonNull final Target target) {
-            if (!mTargets.contains(target)) {
-                mTargets.add(target);
-            }
-            return this;
-        }
-
-        /**
-         * Clear all added targets. This includes any default targets added automatically by
-         * {@link Palette}.
-         */
-        @NonNull
-        public Builder clearTargets() {
-            if (mTargets != null) {
-                mTargets.clear();
-            }
-            return this;
-        }
-
-        /**
-         * Generate and return the {@link Palette} synchronously.
-         */
-        @NonNull
-        public Palette generate() {
-            final TimingLogger logger = LOG_TIMINGS
-                    ? new TimingLogger(LOG_TAG, "Generation")
-                    : null;
-
-            List<Swatch> swatches;
-
-            if (mBitmap != null) {
-                // We have a Bitmap so we need to use quantization to reduce the number of colors
-
-                // First we'll scale down the bitmap if needed
-                final Bitmap bitmap = scaleBitmapDown(mBitmap);
-
-                if (logger != null) {
-                    logger.addSplit("Processed Bitmap");
-                }
-
-                final Rect region = mRegion;
-                if (bitmap != mBitmap && region != null) {
-                    // If we have a scaled bitmap and a selected region, we need to scale down the
-                    // region to match the new scale
-                    final double scale = bitmap.getWidth() / (double) mBitmap.getWidth();
-                    region.left = (int) Math.floor(region.left * scale);
-                    region.top = (int) Math.floor(region.top * scale);
-                    region.right = Math.min((int) Math.ceil(region.right * scale),
-                            bitmap.getWidth());
-                    region.bottom = Math.min((int) Math.ceil(region.bottom * scale),
-                            bitmap.getHeight());
-                }
-
-                // Now generate a quantizer from the Bitmap
-                final ColorCutQuantizer quantizer = new ColorCutQuantizer(
-                        getPixelsFromBitmap(bitmap),
-                        mMaxColors,
-                        mFilters.isEmpty() ? null : mFilters.toArray(new Filter[mFilters.size()]));
-
-                // If created a new bitmap, recycle it
-                if (bitmap != mBitmap) {
-                    bitmap.recycle();
-                }
-
-                swatches = quantizer.getQuantizedColors();
-
-                if (logger != null) {
-                    logger.addSplit("Color quantization completed");
-                }
-            } else {
-                // Else we're using the provided swatches
-                swatches = mSwatches;
-            }
-
-            // Now create a Palette instance
-            final Palette p = new Palette(swatches, mTargets);
-            // And make it generate itself
-            p.generate();
-
-            if (logger != null) {
-                logger.addSplit("Created Palette");
-                logger.dumpToLog();
-            }
-
-            return p;
-        }
-
-        /**
-         * Generate the {@link Palette} asynchronously. The provided listener's
-         * {@link PaletteAsyncListener#onGenerated} method will be called with the palette when
-         * generated.
-         */
-        @NonNull
-        public AsyncTask<Bitmap, Void, Palette> generate(
-                @NonNull final PaletteAsyncListener listener) {
-            if (listener == null) {
-                throw new IllegalArgumentException("listener can not be 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);
-                }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
-        }
-
-        private int[] getPixelsFromBitmap(Bitmap bitmap) {
-            final int bitmapWidth = bitmap.getWidth();
-            final int bitmapHeight = bitmap.getHeight();
-            final int[] pixels = new int[bitmapWidth * bitmapHeight];
-            bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight);
-
-            if (mRegion == null) {
-                // If we don't have a region, return all of the pixels
-                return pixels;
-            } else {
-                // If we do have a region, lets create a subset array containing only the region's
-                // pixels
-                final int regionWidth = mRegion.width();
-                final int regionHeight = mRegion.height();
-                // pixels contains all of the pixels, so we need to iterate through each row and
-                // copy the regions pixels into a new smaller array
-                final int[] subsetPixels = new int[regionWidth * regionHeight];
-                for (int row = 0; row < regionHeight; row++) {
-                    System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left,
-                            subsetPixels, row * regionWidth, regionWidth);
-                }
-                return subsetPixels;
-            }
-        }
-
-        /**
-         * Scale the bitmap down as needed.
-         */
-        private Bitmap scaleBitmapDown(final Bitmap bitmap) {
-            double scaleRatio = -1;
-
-            if (mResizeArea > 0) {
-                final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
-                if (bitmapArea > mResizeArea) {
-                    scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea);
-                }
-            } else if (mResizeMaxDimension > 0) {
-                final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
-                if (maxDimension > mResizeMaxDimension) {
-                    scaleRatio = mResizeMaxDimension / (double) maxDimension;
-                }
-            }
-
-            if (scaleRatio <= 0) {
-                // Scaling has been disabled or not needed so just return the Bitmap
-                return bitmap;
-            }
-
-            return Bitmap.createScaledBitmap(bitmap,
-                    (int) Math.ceil(bitmap.getWidth() * scaleRatio),
-                    (int) Math.ceil(bitmap.getHeight() * scaleRatio),
-                    false);
-        }
-    }
-
-    /**
-     * A Filter provides a mechanism for exercising fine-grained control over which colors
-     * are valid within a resulting {@link Palette}.
-     */
-    public interface Filter {
-        /**
-         * Hook to allow clients to be able filter colors from resulting palette.
-         *
-         * @param rgb the color in RGB888.
-         * @param hsl HSL representation of the color.
-         *
-         * @return true if the color is allowed, false if not.
-         *
-         * @see Builder#addFilter(Filter)
-         */
-        boolean isAllowed(@ColorInt int rgb, @NonNull float[] hsl);
-    }
-
-    /**
-     * The default filter.
-     */
-    static final Filter DEFAULT_FILTER = new Filter() {
-        private static final float BLACK_MAX_LIGHTNESS = 0.05f;
-        private static final float WHITE_MIN_LIGHTNESS = 0.95f;
-
-        @Override
-        public boolean isAllowed(int rgb, float[] hsl) {
-            return !isWhite(hsl) && !isBlack(hsl) && !isNearRedILine(hsl);
-        }
-
-        /**
-         * @return true if the color represents a color which is close to black.
-         */
-        private boolean isBlack(float[] hslColor) {
-            return hslColor[2] <= BLACK_MAX_LIGHTNESS;
-        }
-
-        /**
-         * @return true if the color represents a color which is close to white.
-         */
-        private boolean isWhite(float[] hslColor) {
-            return hslColor[2] >= WHITE_MIN_LIGHTNESS;
-        }
-
-        /**
-         * @return true if the color lies close to the red side of the I line.
-         */
-        private boolean isNearRedILine(float[] hslColor) {
-            return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;
-        }
-    };
-}
diff --git a/android/support/v7/graphics/SwatchTests.java b/android/support/v7/graphics/SwatchTests.java
deleted file mode 100644
index efbcda4..0000000
--- a/android/support/v7/graphics/SwatchTests.java
+++ /dev/null
@@ -1,110 +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.graphics;
-
-import static android.support.v4.graphics.ColorUtils.HSLToColor;
-import static android.support.v4.graphics.ColorUtils.calculateContrast;
-import static android.support.v7.graphics.TestUtils.loadSampleBitmap;
-
-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.graphics.Color;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class SwatchTests {
-
-    private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
-    private static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
-
-    @Test
-    @SmallTest
-    public void testTextColorContrasts() {
-        final Palette p = Palette.from(loadSampleBitmap()).generate();
-
-        for (Palette.Swatch swatch : p.getSwatches()) {
-            testSwatchTextColorContrasts(swatch);
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testHslNotNull() {
-        final Palette p = Palette.from(loadSampleBitmap()).generate();
-
-        for (Palette.Swatch swatch : p.getSwatches()) {
-            assertNotNull(swatch.getHsl());
-        }
-    }
-
-    @Test
-    @SmallTest
-    public void testHslIsRgb() {
-        final Palette p = Palette.from(loadSampleBitmap()).generate();
-
-        for (Palette.Swatch swatch : p.getSwatches()) {
-            assertEquals(HSLToColor(swatch.getHsl()), swatch.getRgb());
-        }
-    }
-
-    private void testSwatchTextColorContrasts(Palette.Swatch swatch) {
-        final int bodyTextColor = swatch.getBodyTextColor();
-        assertTrue(calculateContrast(bodyTextColor, swatch.getRgb()) >= MIN_CONTRAST_BODY_TEXT);
-
-        final int titleTextColor = swatch.getTitleTextColor();
-        assertTrue(calculateContrast(titleTextColor, swatch.getRgb()) >= MIN_CONTRAST_TITLE_TEXT);
-    }
-
-    @Test
-    @SmallTest
-    public void testEqualsWhenSame() {
-        Palette.Swatch swatch1 = new Palette.Swatch(Color.WHITE, 50);
-        Palette.Swatch swatch2 = new Palette.Swatch(Color.WHITE, 50);
-        assertEquals(swatch1, swatch2);
-    }
-
-    @Test
-    @SmallTest
-    public void testEqualsWhenColorDifferent() {
-        Palette.Swatch swatch1 = new Palette.Swatch(Color.BLACK, 50);
-        Palette.Swatch swatch2 = new Palette.Swatch(Color.WHITE, 50);
-        assertFalse(swatch1.equals(swatch2));
-    }
-
-    @Test
-    @SmallTest
-    public void testEqualsWhenPopulationDifferent() {
-        Palette.Swatch swatch1 = new Palette.Swatch(Color.BLACK, 50);
-        Palette.Swatch swatch2 = new Palette.Swatch(Color.BLACK, 100);
-        assertFalse(swatch1.equals(swatch2));
-    }
-
-    @Test
-    @SmallTest
-    public void testEqualsWhenDifferent() {
-        Palette.Swatch swatch1 = new Palette.Swatch(Color.BLUE, 50);
-        Palette.Swatch swatch2 = new Palette.Swatch(Color.BLACK, 100);
-        assertFalse(swatch1.equals(swatch2));
-    }
-}
diff --git a/android/support/v7/graphics/Target.java b/android/support/v7/graphics/Target.java
deleted file mode 100644
index 0eff90b..0000000
--- a/android/support/v7/graphics/Target.java
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * Copyright 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.graphics;
-
-import android.support.annotation.FloatRange;
-import android.support.annotation.NonNull;
-
-/**
- * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances
- * can be created via the {@link Builder} class.
- *
- * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a
- * Palette.</p>
- */
-public final class Target {
-
-    private static final float TARGET_DARK_LUMA = 0.26f;
-    private static final float MAX_DARK_LUMA = 0.45f;
-
-    private static final float MIN_LIGHT_LUMA = 0.55f;
-    private static final float TARGET_LIGHT_LUMA = 0.74f;
-
-    private static final float MIN_NORMAL_LUMA = 0.3f;
-    private static final float TARGET_NORMAL_LUMA = 0.5f;
-    private static final float MAX_NORMAL_LUMA = 0.7f;
-
-    private static final float TARGET_MUTED_SATURATION = 0.3f;
-    private static final float MAX_MUTED_SATURATION = 0.4f;
-
-    private static final float TARGET_VIBRANT_SATURATION = 1f;
-    private static final float MIN_VIBRANT_SATURATION = 0.35f;
-
-    private static final float WEIGHT_SATURATION = 0.24f;
-    private static final float WEIGHT_LUMA = 0.52f;
-    private static final float WEIGHT_POPULATION = 0.24f;
-
-    static final int INDEX_MIN = 0;
-    static final int INDEX_TARGET = 1;
-    static final int INDEX_MAX = 2;
-
-    static final int INDEX_WEIGHT_SAT = 0;
-    static final int INDEX_WEIGHT_LUMA = 1;
-    static final int INDEX_WEIGHT_POP = 2;
-
-    /**
-     * A target which has the characteristics of a vibrant color which is light in luminance.
-    */
-    public static final Target LIGHT_VIBRANT;
-
-    /**
-     * A target which has the characteristics of a vibrant color which is neither light or dark.
-     */
-    public static final Target VIBRANT;
-
-    /**
-     * A target which has the characteristics of a vibrant color which is dark in luminance.
-     */
-    public static final Target DARK_VIBRANT;
-
-    /**
-     * A target which has the characteristics of a muted color which is light in luminance.
-     */
-    public static final Target LIGHT_MUTED;
-
-    /**
-     * A target which has the characteristics of a muted color which is neither light or dark.
-     */
-    public static final Target MUTED;
-
-    /**
-     * A target which has the characteristics of a muted color which is dark in luminance.
-     */
-    public static final Target DARK_MUTED;
-
-    static {
-        LIGHT_VIBRANT = new Target();
-        setDefaultLightLightnessValues(LIGHT_VIBRANT);
-        setDefaultVibrantSaturationValues(LIGHT_VIBRANT);
-
-        VIBRANT = new Target();
-        setDefaultNormalLightnessValues(VIBRANT);
-        setDefaultVibrantSaturationValues(VIBRANT);
-
-        DARK_VIBRANT = new Target();
-        setDefaultDarkLightnessValues(DARK_VIBRANT);
-        setDefaultVibrantSaturationValues(DARK_VIBRANT);
-
-        LIGHT_MUTED = new Target();
-        setDefaultLightLightnessValues(LIGHT_MUTED);
-        setDefaultMutedSaturationValues(LIGHT_MUTED);
-
-        MUTED = new Target();
-        setDefaultNormalLightnessValues(MUTED);
-        setDefaultMutedSaturationValues(MUTED);
-
-        DARK_MUTED = new Target();
-        setDefaultDarkLightnessValues(DARK_MUTED);
-        setDefaultMutedSaturationValues(DARK_MUTED);
-    }
-
-    final float[] mSaturationTargets = new float[3];
-    final float[] mLightnessTargets = new float[3];
-    final float[] mWeights = new float[3];
-    boolean mIsExclusive = true; // default to true
-
-    Target() {
-        setTargetDefaultValues(mSaturationTargets);
-        setTargetDefaultValues(mLightnessTargets);
-        setDefaultWeights();
-    }
-
-    Target(@NonNull Target from) {
-        System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0,
-                mSaturationTargets.length);
-        System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0,
-                mLightnessTargets.length);
-        System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length);
-    }
-
-    /**
-     * The minimum saturation value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMinimumSaturation() {
-        return mSaturationTargets[INDEX_MIN];
-    }
-
-    /**
-     * The target saturation value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getTargetSaturation() {
-        return mSaturationTargets[INDEX_TARGET];
-    }
-
-    /**
-     * The maximum saturation value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMaximumSaturation() {
-        return mSaturationTargets[INDEX_MAX];
-    }
-
-    /**
-     * The minimum lightness value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMinimumLightness() {
-        return mLightnessTargets[INDEX_MIN];
-    }
-
-    /**
-     * The target lightness value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getTargetLightness() {
-        return mLightnessTargets[INDEX_TARGET];
-    }
-
-    /**
-     * The maximum lightness value for this target.
-     */
-    @FloatRange(from = 0, to = 1)
-    public float getMaximumLightness() {
-        return mLightnessTargets[INDEX_MAX];
-    }
-
-    /**
-     * Returns the weight of importance that this target places on a color's saturation within
-     * the image.
-     *
-     * <p>The larger the weight, relative to the other weights, the more important that a color
-     * being close to the target value has on selection.</p>
-     *
-     * @see #getTargetSaturation()
-     */
-    public float getSaturationWeight() {
-        return mWeights[INDEX_WEIGHT_SAT];
-    }
-
-    /**
-     * Returns the weight of importance that this target places on a color's lightness within
-     * the image.
-     *
-     * <p>The larger the weight, relative to the other weights, the more important that a color
-     * being close to the target value has on selection.</p>
-     *
-     * @see #getTargetLightness()
-     */
-    public float getLightnessWeight() {
-        return mWeights[INDEX_WEIGHT_LUMA];
-    }
-
-    /**
-     * Returns the weight of importance that this target places on a color's population within
-     * the image.
-     *
-     * <p>The larger the weight, relative to the other weights, the more important that a
-     * color's population being close to the most populous has on selection.</p>
-     */
-    public float getPopulationWeight() {
-        return mWeights[INDEX_WEIGHT_POP];
-    }
-
-    /**
-     * Returns whether any color selected for this target is exclusive for this target only.
-     *
-     * <p>If false, then the color can be selected for other targets.</p>
-     */
-    public boolean isExclusive() {
-        return mIsExclusive;
-    }
-
-    private static void setTargetDefaultValues(final float[] values) {
-        values[INDEX_MIN] = 0f;
-        values[INDEX_TARGET] = 0.5f;
-        values[INDEX_MAX] = 1f;
-    }
-
-    private void setDefaultWeights() {
-        mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION;
-        mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA;
-        mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION;
-    }
-
-    void normalizeWeights() {
-        float sum = 0;
-        for (int i = 0, z = mWeights.length; i < z; i++) {
-            float weight = mWeights[i];
-            if (weight > 0) {
-                sum += weight;
-            }
-        }
-        if (sum != 0) {
-            for (int i = 0, z = mWeights.length; i < z; i++) {
-                if (mWeights[i] > 0) {
-                    mWeights[i] /= sum;
-                }
-            }
-        }
-    }
-
-    private static void setDefaultDarkLightnessValues(Target target) {
-        target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA;
-        target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA;
-    }
-
-    private static void setDefaultNormalLightnessValues(Target target) {
-        target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA;
-        target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA;
-        target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA;
-    }
-
-    private static void setDefaultLightLightnessValues(Target target) {
-        target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA;
-        target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA;
-    }
-
-    private static void setDefaultVibrantSaturationValues(Target target) {
-        target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION;
-        target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION;
-    }
-
-    private static void setDefaultMutedSaturationValues(Target target) {
-        target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION;
-        target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION;
-    }
-
-    /**
-     * Builder class for generating custom {@link Target} instances.
-     */
-    public final static class Builder {
-        private final Target mTarget;
-
-        /**
-         * Create a new {@link Target} builder from scratch.
-         */
-        public Builder() {
-            mTarget = new Target();
-        }
-
-        /**
-         * Create a new builder based on an existing {@link Target}.
-         */
-        public Builder(@NonNull Target target) {
-            mTarget = new Target(target);
-        }
-
-        /**
-         * Set the minimum saturation value for this target.
-         */
-        @NonNull
-        public Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mSaturationTargets[INDEX_MIN] = value;
-            return this;
-        }
-
-        /**
-         * Set the target/ideal saturation value for this target.
-         */
-        @NonNull
-        public Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mSaturationTargets[INDEX_TARGET] = value;
-            return this;
-        }
-
-        /**
-         * Set the maximum saturation value for this target.
-         */
-        @NonNull
-        public Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mSaturationTargets[INDEX_MAX] = value;
-            return this;
-        }
-
-        /**
-         * Set the minimum lightness value for this target.
-         */
-        @NonNull
-        public Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mLightnessTargets[INDEX_MIN] = value;
-            return this;
-        }
-
-        /**
-         * Set the target/ideal lightness value for this target.
-         */
-        @NonNull
-        public Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mLightnessTargets[INDEX_TARGET] = value;
-            return this;
-        }
-
-        /**
-         * Set the maximum lightness value for this target.
-         */
-        @NonNull
-        public Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) {
-            mTarget.mLightnessTargets[INDEX_MAX] = value;
-            return this;
-        }
-
-        /**
-         * Set the weight of importance that this target will place on saturation values.
-         *
-         * <p>The larger the weight, relative to the other weights, the more important that a color
-         * being close to the target value has on selection.</p>
-         *
-         * <p>A weight of 0 means that it has no weight, and thus has no
-         * bearing on the selection.</p>
-         *
-         * @see #setTargetSaturation(float)
-         */
-        @NonNull
-        public Builder setSaturationWeight(@FloatRange(from = 0) float weight) {
-            mTarget.mWeights[INDEX_WEIGHT_SAT] = weight;
-            return this;
-        }
-
-        /**
-         * Set the weight of importance that this target will place on lightness values.
-         *
-         * <p>The larger the weight, relative to the other weights, the more important that a color
-         * being close to the target value has on selection.</p>
-         *
-         * <p>A weight of 0 means that it has no weight, and thus has no
-         * bearing on the selection.</p>
-         *
-         * @see #setTargetLightness(float)
-         */
-        @NonNull
-        public Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
-            mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight;
-            return this;
-        }
-
-        /**
-         * Set the weight of importance that this target will place on a color's population within
-         * the image.
-         *
-         * <p>The larger the weight, relative to the other weights, the more important that a
-         * color's population being close to the most populous has on selection.</p>
-         *
-         * <p>A weight of 0 means that it has no weight, and thus has no
-         * bearing on the selection.</p>
-         */
-        @NonNull
-        public Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
-            mTarget.mWeights[INDEX_WEIGHT_POP] = weight;
-            return this;
-        }
-
-        /**
-         * Set whether any color selected for this target is exclusive to this target only.
-         * Defaults to true.
-         *
-         * @param exclusive true if any the color is exclusive to this target, or false is the
-         *                  color can be selected for other targets.
-         */
-        @NonNull
-        public Builder setExclusive(boolean exclusive) {
-            mTarget.mIsExclusive = exclusive;
-            return this;
-        }
-
-        /**
-         * Builds and returns the resulting {@link Target}.
-         */
-        @NonNull
-        public Target build() {
-            return mTarget;
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/android/support/v7/graphics/TestUtils.java b/android/support/v7/graphics/TestUtils.java
deleted file mode 100644
index 8de70c5..0000000
--- a/android/support/v7/graphics/TestUtils.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.v7.graphics;
-
-import static org.junit.Assert.assertEquals;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.support.test.InstrumentationRegistry;
-import android.support.v7.palette.test.R;
-
-class TestUtils {
-
-    static Bitmap loadSampleBitmap() {
-        return BitmapFactory.decodeResource(
-                InstrumentationRegistry.getContext().getResources(),
-                R.drawable.photo);
-    }
-
-    static void assertCloseColors(int expected, int actual) {
-        assertEquals(Color.red(expected), Color.red(actual), 8);
-        assertEquals(Color.green(expected), Color.green(actual), 8);
-        assertEquals(Color.blue(expected), Color.blue(actual), 8);
-    }
-
-}
diff --git a/android/support/v7/graphics/drawable/DrawableWrapper.java b/android/support/v7/graphics/drawable/DrawableWrapper.java
deleted file mode 100644
index b2406ac..0000000
--- a/android/support/v7/graphics/drawable/DrawableWrapper.java
+++ /dev/null
@@ -1,233 +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.graphics.drawable;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.res.ColorStateList;
-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.RestrictTo;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.view.View;
-
-/**
- * Drawable which delegates all calls to its wrapped {@link Drawable}.
- * <p>
- * The wrapped {@link Drawable} <em>must</em> be fully released from any {@link View}
- * before wrapping, otherwise internal {@link Drawable.Callback} may be dropped.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class DrawableWrapper extends Drawable implements Drawable.Callback {
-
-    private Drawable mDrawable;
-
-    public DrawableWrapper(Drawable drawable) {
-        setWrappedDrawable(drawable);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        mDrawable.draw(canvas);
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        mDrawable.setBounds(bounds);
-    }
-
-    @Override
-    public void setChangingConfigurations(int configs) {
-        mDrawable.setChangingConfigurations(configs);
-    }
-
-    @Override
-    public int getChangingConfigurations() {
-        return 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() {
-        return mDrawable.isStateful();
-    }
-
-    @Override
-    public boolean setState(final int[] stateSet) {
-        return mDrawable.setState(stateSet);
-    }
-
-    @Override
-    public int[] getState() {
-        return mDrawable.getState();
-    }
-
-    @Override
-    public void jumpToCurrentState() {
-        DrawableCompat.jumpToCurrentState(mDrawable);
-    }
-
-    @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);
-    }
-
-    /**
-     * {@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 setAutoMirrored(boolean mirrored) {
-        DrawableCompat.setAutoMirrored(mDrawable, mirrored);
-    }
-
-    @Override
-    public boolean isAutoMirrored() {
-        return DrawableCompat.isAutoMirrored(mDrawable);
-    }
-
-    @Override
-    public void setTint(int tint) {
-        DrawableCompat.setTint(mDrawable, tint);
-    }
-
-    @Override
-    public void setTintList(ColorStateList tint) {
-        DrawableCompat.setTintList(mDrawable, tint);
-    }
-
-    @Override
-    public void setTintMode(PorterDuff.Mode tintMode) {
-        DrawableCompat.setTintMode(mDrawable, tintMode);
-    }
-
-    @Override
-    public void setHotspot(float x, float y) {
-        DrawableCompat.setHotspot(mDrawable, x, y);
-    }
-
-    @Override
-    public void setHotspotBounds(int left, int top, int right, int bottom) {
-        DrawableCompat.setHotspotBounds(mDrawable, left, top, right, bottom);
-    }
-
-    public Drawable getWrappedDrawable() {
-        return mDrawable;
-    }
-
-    public void setWrappedDrawable(Drawable drawable) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-        }
-
-        mDrawable = drawable;
-
-        if (drawable != null) {
-            drawable.setCallback(this);
-        }
-    }
-}
diff --git a/android/support/v7/graphics/drawable/DrawerArrowDrawable.java b/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
deleted file mode 100644
index 5142224..0000000
--- a/android/support/v7/graphics/drawable/DrawerArrowDrawable.java
+++ /dev/null
@@ -1,466 +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.graphics.drawable;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.FloatRange;
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A drawable that can draw a "Drawer hamburger" menu or an arrow and animate between them.
- * <p>
- * The progress between the two states is controlled via {@link #setProgress(float)}.
- * </p>
- */
-public class DrawerArrowDrawable extends Drawable {
-
-    /**
-     * Direction to make the arrow point towards the left.
-     *
-     * @see #setDirection(int)
-     * @see #getDirection()
-     */
-    public static final int ARROW_DIRECTION_LEFT = 0;
-
-    /**
-     * Direction to make the arrow point towards the right.
-     *
-     * @see #setDirection(int)
-     * @see #getDirection()
-     */
-    public static final int ARROW_DIRECTION_RIGHT = 1;
-
-    /**
-     * Direction to make the arrow point towards the start.
-     *
-     * <p>When used in a view with a {@link ViewCompat#LAYOUT_DIRECTION_RTL RTL} layout direction,
-     * this is the same as {@link #ARROW_DIRECTION_RIGHT}, otherwise it is the same as
-     * {@link #ARROW_DIRECTION_LEFT}.</p>
-     *
-     * @see #setDirection(int)
-     * @see #getDirection()
-     */
-    public static final int ARROW_DIRECTION_START = 2;
-
-    /**
-     * Direction to make the arrow point to the end.
-     *
-     * <p>When used in a view with a {@link ViewCompat#LAYOUT_DIRECTION_RTL RTL} layout direction,
-     * this is the same as {@link #ARROW_DIRECTION_LEFT}, otherwise it is the same as
-     * {@link #ARROW_DIRECTION_RIGHT}.</p>
-     *
-     * @see #setDirection(int)
-     * @see #getDirection()
-     */
-    public static final int ARROW_DIRECTION_END = 3;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({ARROW_DIRECTION_LEFT, ARROW_DIRECTION_RIGHT,
-            ARROW_DIRECTION_START, ARROW_DIRECTION_END})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ArrowDirection {}
-
-    private final Paint mPaint = new Paint();
-
-    // The angle in degrees that the arrow head is inclined at.
-    private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
-    // The length of top and bottom bars when they merge into an arrow
-    private float mArrowHeadLength;
-    // The length of middle bar
-    private float mBarLength;
-    // The length of the middle bar when arrow is shaped
-    private float mArrowShaftLength;
-    // The space between bars when they are parallel
-    private float mBarGap;
-    // Whether bars should spin or not during progress
-    private boolean mSpin;
-    // Use Path instead of canvas operations so that if color has transparency, overlapping sections
-    // wont look different
-    private final Path mPath = new Path();
-    // The reported intrinsic size of the drawable.
-    private final int mSize;
-    // Whether we should mirror animation when animation is reversed.
-    private boolean mVerticalMirror = false;
-    // The interpolated version of the original progress
-    private float mProgress;
-    // the amount that overlaps w/ bar size when rotation is max
-    private float mMaxCutForBarSize;
-    // The arrow direction
-    private int mDirection = ARROW_DIRECTION_START;
-
-    /**
-     * @param context used to get the configuration for the drawable from
-     */
-    public DrawerArrowDrawable(Context context) {
-        mPaint.setStyle(Paint.Style.STROKE);
-        mPaint.setStrokeJoin(Paint.Join.MITER);
-        mPaint.setStrokeCap(Paint.Cap.BUTT);
-        mPaint.setAntiAlias(true);
-
-        final TypedArray a = context.getTheme().obtainStyledAttributes(null,
-                R.styleable.DrawerArrowToggle, R.attr.drawerArrowStyle,
-                R.style.Base_Widget_AppCompat_DrawerArrowToggle);
-
-        setColor(a.getColor(R.styleable.DrawerArrowToggle_color, 0));
-        setBarThickness(a.getDimension(R.styleable.DrawerArrowToggle_thickness, 0));
-        setSpinEnabled(a.getBoolean(R.styleable.DrawerArrowToggle_spinBars, true));
-        // round this because having this floating may cause bad measurements
-        setGapSize(Math.round(a.getDimension(R.styleable.DrawerArrowToggle_gapBetweenBars, 0)));
-
-        mSize = a.getDimensionPixelSize(R.styleable.DrawerArrowToggle_drawableSize, 0);
-        // round this because having this floating may cause bad measurements
-        mBarLength = Math.round(a.getDimension(R.styleable.DrawerArrowToggle_barLength, 0));
-        // round this because having this floating may cause bad measurements
-        mArrowHeadLength = Math.round(a.getDimension(
-                R.styleable.DrawerArrowToggle_arrowHeadLength, 0));
-        mArrowShaftLength = a.getDimension(R.styleable.DrawerArrowToggle_arrowShaftLength, 0);
-        a.recycle();
-    }
-
-    /**
-     * Sets the length of the arrow head (from tip to edge, perpendicular to the shaft).
-     *
-     * @param length the length in pixels
-     */
-    public void setArrowHeadLength(float length) {
-        if (mArrowHeadLength != length) {
-            mArrowHeadLength = length;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the length of the arrow head (from tip to edge, perpendicular to the shaft),
-     * in pixels.
-     */
-    public float getArrowHeadLength() {
-        return mArrowHeadLength;
-    }
-
-    /**
-     * Sets the arrow shaft length.
-     *
-     * @param length the length in pixels
-     */
-    public void setArrowShaftLength(float length) {
-        if (mArrowShaftLength != length) {
-            mArrowShaftLength = length;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the arrow shaft length in pixels.
-     */
-    public float getArrowShaftLength() {
-        return mArrowShaftLength;
-    }
-
-    /**
-     * The length of the bars when they are parallel to each other.
-     */
-    public float getBarLength() {
-        return mBarLength;
-    }
-
-    /**
-     * Sets the length of the bars when they are parallel to each other.
-     *
-     * @param length the length in pixels
-     */
-    public void setBarLength(float length) {
-        if (mBarLength != length) {
-            mBarLength = length;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Sets the color of the drawable.
-     */
-    public void setColor(@ColorInt int color) {
-        if (color != mPaint.getColor()) {
-            mPaint.setColor(color);
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the color of the drawable.
-     */
-    @ColorInt
-    public int getColor() {
-        return mPaint.getColor();
-    }
-
-    /**
-     * Sets the thickness (stroke size) for the bars.
-     *
-     * @param width stroke width in pixels
-     */
-    public void setBarThickness(float width) {
-        if (mPaint.getStrokeWidth() != width) {
-            mPaint.setStrokeWidth(width);
-            mMaxCutForBarSize = (float) (width / 2 * Math.cos(ARROW_HEAD_ANGLE));
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the thickness (stroke width) of the bars.
-     */
-    public float getBarThickness() {
-        return mPaint.getStrokeWidth();
-    }
-
-    /**
-     * Returns the max gap between the bars when they are parallel to each other.
-     *
-     * @see #getGapSize()
-     */
-    public float getGapSize() {
-        return mBarGap;
-    }
-
-    /**
-     * Sets the max gap between the bars when they are parallel to each other.
-     *
-     * @param gap the gap in pixels
-     *
-     * @see #getGapSize()
-     */
-    public void setGapSize(float gap) {
-        if (gap != mBarGap) {
-            mBarGap = gap;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Set the arrow direction.
-     */
-    public void setDirection(@ArrowDirection int direction) {
-        if (direction != mDirection) {
-            mDirection = direction;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns whether the bars should rotate or not during the transition.
-     *
-     * @see #setSpinEnabled(boolean)
-     */
-    public boolean isSpinEnabled() {
-        return mSpin;
-    }
-
-    /**
-     * Returns whether the bars should rotate or not during the transition.
-     *
-     * @param enabled true if the bars should rotate.
-     *
-     * @see #isSpinEnabled()
-     */
-    public void setSpinEnabled(boolean enabled) {
-        if (mSpin != enabled) {
-            mSpin = enabled;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the arrow direction.
-     */
-    @ArrowDirection
-    public int getDirection() {
-        return mDirection;
-    }
-
-    /**
-     * If set, canvas is flipped when progress reached to end and going back to start.
-     */
-    public void setVerticalMirror(boolean verticalMirror) {
-        if (mVerticalMirror != verticalMirror) {
-            mVerticalMirror = verticalMirror;
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        Rect bounds = getBounds();
-
-        final boolean flipToPointRight;
-        switch (mDirection) {
-            case ARROW_DIRECTION_LEFT:
-                flipToPointRight = false;
-                break;
-            case ARROW_DIRECTION_RIGHT:
-                flipToPointRight = true;
-                break;
-            case ARROW_DIRECTION_END:
-                flipToPointRight = DrawableCompat.getLayoutDirection(this)
-                        == ViewCompat.LAYOUT_DIRECTION_LTR;
-                break;
-            case ARROW_DIRECTION_START:
-            default:
-                flipToPointRight = DrawableCompat.getLayoutDirection(this)
-                        == ViewCompat.LAYOUT_DIRECTION_RTL;
-                break;
-        }
-
-        // Interpolated widths of arrow bars
-
-        float arrowHeadBarLength = (float) Math.sqrt(mArrowHeadLength * mArrowHeadLength * 2);
-        arrowHeadBarLength = lerp(mBarLength, arrowHeadBarLength, mProgress);
-        final float arrowShaftLength = lerp(mBarLength, mArrowShaftLength, mProgress);
-        // Interpolated size of middle bar
-        final float arrowShaftCut = Math.round(lerp(0, mMaxCutForBarSize, mProgress));
-        // The rotation of the top and bottom bars (that make the arrow head)
-        final float rotation = lerp(0, ARROW_HEAD_ANGLE, mProgress);
-
-        // The whole canvas rotates as the transition happens
-        final float canvasRotate = lerp(flipToPointRight ? 0 : -180,
-                flipToPointRight ? 180 : 0, mProgress);
-
-        final float arrowWidth = Math.round(arrowHeadBarLength * Math.cos(rotation));
-        final float arrowHeight = Math.round(arrowHeadBarLength * Math.sin(rotation));
-
-        mPath.rewind();
-        final float topBottomBarOffset = lerp(mBarGap + mPaint.getStrokeWidth(), -mMaxCutForBarSize,
-                mProgress);
-
-        final float arrowEdge = -arrowShaftLength / 2;
-        // draw middle bar
-        mPath.moveTo(arrowEdge + arrowShaftCut, 0);
-        mPath.rLineTo(arrowShaftLength - arrowShaftCut * 2, 0);
-
-        // bottom bar
-        mPath.moveTo(arrowEdge, topBottomBarOffset);
-        mPath.rLineTo(arrowWidth, arrowHeight);
-
-        // top bar
-        mPath.moveTo(arrowEdge, -topBottomBarOffset);
-        mPath.rLineTo(arrowWidth, -arrowHeight);
-
-        mPath.close();
-
-        canvas.save();
-
-        // Rotate the whole canvas if spinning, if not, rotate it 180 to get
-        // the arrow pointing the other way for RTL.
-        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.5f + mBarGap;
-
-        canvas.translate(bounds.centerX(), yOffset);
-        if (mSpin) {
-            canvas.rotate(canvasRotate * ((mVerticalMirror ^ flipToPointRight) ? -1 : 1));
-        } else if (flipToPointRight) {
-            canvas.rotate(180);
-        }
-        canvas.drawPath(mPath, mPaint);
-
-        canvas.restore();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        if (alpha != mPaint.getAlpha()) {
-            mPaint.setAlpha(alpha);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mPaint.setColorFilter(colorFilter);
-        invalidateSelf();
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mSize;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mSize;
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    /**
-     * Returns the current progress of the arrow.
-     */
-    @FloatRange(from = 0.0, to = 1.0)
-    public float getProgress() {
-        return mProgress;
-    }
-
-    /**
-     * Set the progress of the arrow.
-     *
-     * <p>A value of {@code 0.0} indicates that the arrow should be drawn in its starting
-     * position. A value of {@code 1.0} indicates that the arrow should be drawn in its ending
-     * position.</p>
-     */
-    public void setProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
-        if (mProgress != progress) {
-            mProgress = progress;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Returns the paint instance used for all drawing.
-     */
-    public final Paint getPaint() {
-        return mPaint;
-    }
-
-    /**
-     * Linear interpolate between a and b with parameter t.
-     */
-    private static float lerp(float a, float b, float t) {
-        return a + (b - a) * t;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/internal/package-info.java b/android/support/v7/internal/package-info.java
deleted file mode 100644
index b5fabd6..0000000
--- a/android/support/v7/internal/package-info.java
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-package android.support.v7.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
\ No newline at end of file
diff --git a/android/support/v7/internal/widget/PreferenceImageView.java b/android/support/v7/internal/widget/PreferenceImageView.java
deleted file mode 100644
index e9a9dcf..0000000
--- a/android/support/v7/internal/widget/PreferenceImageView.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.v7.internal.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v7.preference.R;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * Extension of ImageView that correctly applies maxWidth and maxHeight.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class PreferenceImageView extends ImageView {
-
-    private int mMaxWidth = Integer.MAX_VALUE;
-    private int mMaxHeight = Integer.MAX_VALUE;
-
-    public PreferenceImageView(Context context) {
-        this(context, null);
-    }
-
-    public PreferenceImageView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PreferenceImageView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.PreferenceImageView, defStyleAttr, 0);
-
-        setMaxWidth(a.getDimensionPixelSize(
-                R.styleable.PreferenceImageView_maxWidth, Integer.MAX_VALUE));
-
-        setMaxHeight(a.getDimensionPixelSize(
-                R.styleable.PreferenceImageView_maxHeight, Integer.MAX_VALUE));
-
-        a.recycle();
-    }
-
-//    public PreferenceImageView(Context context, AttributeSet attrs, int defStyleAttr,
-//            int defStyleRes) {
-//        super(context, attrs, defStyleAttr, defStyleRes);
-//    }
-
-    @Override
-    public void setMaxWidth(int maxWidth) {
-        mMaxWidth = maxWidth;
-        super.setMaxWidth(maxWidth);
-    }
-
-    @Override
-    public int getMaxWidth() {
-        return mMaxWidth;
-    }
-
-    @Override
-    public void setMaxHeight(int maxHeight) {
-        mMaxHeight = maxHeight;
-        super.setMaxHeight(maxHeight);
-    }
-
-    @Override
-    public int getMaxHeight() {
-        return mMaxHeight;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
-            final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-            final int maxWidth = getMaxWidth();
-            if (maxWidth != Integer.MAX_VALUE
-                    && (maxWidth < widthSize || widthMode == MeasureSpec.UNSPECIFIED)) {
-                widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
-            }
-        }
-
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
-            final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-            final int maxHeight = getMaxHeight();
-            if (maxHeight != Integer.MAX_VALUE
-                    && (maxHeight < heightSize || heightMode == MeasureSpec.UNSPECIFIED)) {
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
-            }
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-}
diff --git a/android/support/v7/media/MediaControlIntent.java b/android/support/v7/media/MediaControlIntent.java
deleted file mode 100644
index f1de234..0000000
--- a/android/support/v7/media/MediaControlIntent.java
+++ /dev/null
@@ -1,1228 +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.v7.media;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.net.Uri;
-
-/**
- * Constants for media control intents.
- * <p>
- * This class declares a set of standard media control intent categories and actions that
- * applications can use to identify the capabilities of media routes and control them.
- * </p>
- *
- * <h3>Media control intent categories</h3>
- * <p>
- * Media control intent categories specify means by which applications can
- * send media to the destination of a media route.  Categories are sometimes referred
- * to as describing "types" or "kinds" of routes.
- * </p><p>
- * For example, if a route supports the {@link #CATEGORY_REMOTE_PLAYBACK remote playback category},
- * then an application can ask it to play media remotely by sending a
- * {@link #ACTION_PLAY play} or {@link #ACTION_ENQUEUE enqueue} intent with the Uri of the
- * media content to play.  Such a route may then be referred to as
- * a "remote playback route" because it supports remote playback requests.  It is common
- * for a route to support multiple categories of requests at the same time, such as
- * live audio and live video.
- * </p><p>
- * The following standard route categories are defined.
- * </p><ul>
- * <li>{@link #CATEGORY_LIVE_AUDIO Live audio}: The route supports streaming live audio
- * from the device to the destination.  Live audio routes include local speakers
- * and Bluetooth headsets.
- * <li>{@link #CATEGORY_LIVE_VIDEO Live video}: The route supports streaming live video
- * from the device to the destination.  Live video routes include local displays
- * and wireless displays that support mirroring and
- * {@link android.app.Presentation presentations}.  Live video routes typically also
- * support live audio capabilities.
- * <li>{@link #CATEGORY_REMOTE_PLAYBACK Remote playback}: The route supports sending
- * remote playback requests for media content to the destination.  The content to be
- * played is identified by a Uri and mime-type.
- * </ul><p>
- * Media route providers may define custom media control intent categories of their own in
- * addition to the standard ones.  Custom categories can be used to provide a variety
- * of features to applications that recognize and know how to use them.  For example,
- * a media route provider might define a custom category to indicate that its routes
- * support a special device-specific control interface in addition to other
- * standard features.
- * </p><p>
- * Applications can determine which categories a route supports by using the
- * {@link MediaRouter.RouteInfo#supportsControlCategory MediaRouter.RouteInfo.supportsControlCategory}
- * or {@link MediaRouter.RouteInfo#getControlFilters MediaRouter.RouteInfo.getControlFilters}
- * methods.  Applications can also specify the types of routes that they want to use by
- * creating {@link MediaRouteSelector media route selectors} that contain the desired
- * categories and are used to filter routes in several parts of the media router API.
- * </p>
- *
- * <h3>Media control intent actions</h3>
- * <p>
- * Media control intent actions specify particular functions that applications
- * can ask the destination of a media route to perform.  Media route control requests
- * take the form of intents in a similar manner to other intents used to start activities
- * or send broadcasts.  The difference is that media control intents are directed to
- * routes rather than activity or broadcast receiver components.
- * </p><p>
- * Each media route control intent specifies an action, a category and some number of parameters
- * that are supplied as extras.  Applications send media control requests to routes using the
- * {@link MediaRouter.RouteInfo#sendControlRequest MediaRouter.RouteInfo.sendControlRequest}
- * method and receive results via a callback.
- * </p><p>
- * All media control intent actions are associated with the media control intent categories
- * that support them.  Thus only remote playback routes may perform remote playback actions.
- * The documentation of each action specifies the category to which the action belongs,
- * the parameters it requires, and the results it returns.
- * </p>
- *
- * <h3>Live audio and live video routes</h3>
- * <p>
- * {@link #CATEGORY_LIVE_AUDIO Live audio} and {@link #CATEGORY_LIVE_VIDEO live video}
- * routes present media using standard system interfaces such as audio streams,
- * {@link android.app.Presentation presentations} or display mirroring.  These routes are
- * the easiest to use because applications simply render content locally on the device
- * and the system streams it to the route destination automatically.
- * </p><p>
- * In most cases, applications can stream content to live audio and live video routes in
- * the same way they would play the content locally without any modification.  However,
- * applications may also be able to take advantage of more sophisticated features such
- * as second-screen presentation APIs that are particular to these routes.
- * </p>
- *
- * <h3>Remote playback routes</h3>
- * <p>
- * {@link #CATEGORY_REMOTE_PLAYBACK Remote playback} routes present media remotely
- * by playing content from a Uri.
- * These routes destinations take responsibility for fetching and rendering content
- * on their own.  Applications do not render the content themselves; instead, applications
- * send control requests to initiate play, pause, resume, or stop media items and receive
- * status updates as they change state.
- * </p>
- *
- * <h4>Sessions</h4>
- * <p>
- * Each remote media playback action is conducted within the scope of a session.
- * Sessions are used to prevent applications from accidentally interfering with one
- * another because at most one session can be valid at a time.
- * </p><p>
- * A session can be created using the {@link #ACTION_START_SESSION start session action}
- * and terminated using the {@link #ACTION_END_SESSION end session action} when the
- * route provides explicit session management features.
- * </p><p>
- * Explicit session management was added in a later revision of the protocol so not
- * all routes support it.  If the route does not support explicit session management
- * then implicit session management may still be used.  Implicit session management
- * relies on the use of the {@link #ACTION_PLAY play} and {@link #ACTION_ENQUEUE enqueue}
- * actions which have the side-effect of creating a new session if none is provided
- * as argument.
- * </p><p>
- * When a new session is created, the previous session is invalidated and any ongoing
- * media playback is stopped before the requested action is performed.  Any attempt
- * to use an invalidated session will result in an error.  (Protocol implementations
- * are encouraged to aggressively discard information associated with invalidated sessions
- * since it is no longer of use.)
- * </p><p>
- * Each session is identified by a unique session id that may be used to control
- * the session using actions such as pause, resume, stop and end session.
- * </p>
- *
- * <h4>Media items</h4>
- * <p>
- * Each successful {@link #ACTION_PLAY play} or {@link #ACTION_ENQUEUE enqueue} action
- * returns a unique media item id that an application can use to monitor and control
- * playback.  The media item id may be passed to other actions such as
- * {@link #ACTION_SEEK seek} or {@link #ACTION_GET_STATUS get status}.  It will also appear
- * as a parameter in status update broadcasts to identify the associated playback request.
- * </p><p>
- * Each media item is scoped to the session in which it was created.  Therefore media item
- * ids are only ever used together with session ids.  Media item ids are meaningless
- * on their own.  When the session is invalidated, all of its media items are also
- * invalidated.
- * </p>
- *
- * <h4>The playback queue</h4>
- * <p>
- * Each session has its own playback queue that consists of the media items that
- * are pending, playing, buffering or paused.  Items are added to the queue when
- * a playback request is issued.  Items are removed from the queue when they are no
- * longer eligible for playback (enter terminal states).
- * </p><p>
- * As described in the {@link MediaItemStatus} class, media items initially
- * start in a pending state, transition to the playing (or buffering or paused) state
- * during playback, and end in a finished, canceled, invalidated or error state.
- * Once the current item enters a terminal state, playback proceeds on to the
- * next item.
- * </p><p>
- * The application should determine whether the route supports queuing by checking
- * whether the {@link #ACTION_ENQUEUE} action is declared in the route's control filter
- * using {@link MediaRouter.RouteInfo#supportsControlRequest RouteInfo.supportsControlRequest}.
- * </p><p>
- * If the {@link #ACTION_ENQUEUE} action is supported by the route, then the route promises
- * to allow at least two items (possibly more) to be enqueued at a time.  Enqueued items play
- * back to back one after the other as the previous item completes.  Ideally there should
- * be no audible pause between items for standard audio content types.
- * </p><p>
- * If the {@link #ACTION_ENQUEUE} action is not supported by the route, then the queue
- * effectively contains at most one item at a time.  Each play action has the effect of
- * clearing the queue and resetting its state before the next item is played.
- * </p>
- *
- * <h4>Impact of pause, resume, stop and play actions on the playback queue</h4>
- * <p>
- * The pause, resume and stop actions affect the session's whole queue.  Pause causes
- * the playback queue to be suspended no matter which item is currently playing.
- * Resume reverses the effects of pause.  Stop clears the queue and also resets
- * the pause flag just like resume.
- * </p><p>
- * As described earlier, the play action has the effect of clearing the queue
- * and completely resetting its state (like the stop action) then enqueuing a
- * new media item to be played immediately.  Play is therefore equivalent
- * to stop followed by an action to enqueue an item.
- * </p><p>
- * The play action is also special in that it can be used to create new sessions.
- * An application with simple needs may find that it only needs to use play
- * (and occasionally stop) to control playback.
- * </p>
- *
- * <h4>Resolving conflicts between applications</h4>
- * <p>
- * When an application has a valid session, it is essentially in control of remote playback
- * on the route.  No other application can view or modify the remote playback state
- * of that application's session without knowing its id.
- * </p><p>
- * However, other applications can perform actions that have the effect of stopping
- * playback and invalidating the current session.  When this occurs, the former application
- * will be informed that it has lost control by way of individual media item status
- * update broadcasts that indicate that its queued media items have become
- * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated}.  This broadcast
- * implies that playback was terminated abnormally by an external cause.
- * </p><p>
- * Applications should handle conflicts conservatively to allow other applications to
- * smoothly assume control over the route.  When a conflict occurs, the currently playing
- * application should release its session and allow the new application to use the
- * route until such time as the user intervenes to take over the route again and begin
- * a new playback session.
- * </p>
- *
- * <h4>Basic actions</h4>
- * <p>
- * The following basic actions must be supported (all or nothing) by all remote
- * playback routes.  These actions form the basis of the remote playback protocol
- * and are required in all implementations.
- * </p><ul>
- * <li>{@link #ACTION_PLAY Play}: Starts playing content specified by a given Uri
- * and returns a new media item id to describe the request.  Implicitly creates a new
- * session if no session id was specified as a parameter.
- * <li>{@link #ACTION_SEEK Seek}: Sets the content playback position of a specific media item.
- * <li>{@link #ACTION_GET_STATUS Get status}: Gets the status of a media item
- * including the item's current playback position and progress.
- * <li>{@link #ACTION_PAUSE Pause}: Pauses playback of the queue.
- * <li>{@link #ACTION_RESUME Resume}: Resumes playback of the queue.
- * <li>{@link #ACTION_STOP Stop}: Stops playback, clears the queue, and resets the
- * pause state.
- * </ul>
- *
- * <h4>Queue actions</h4>
- * <p>
- * The following queue actions must be supported (all or nothing) by remote
- * playback routes that offer optional queuing capabilities.
- * </p><ul>
- * <li>{@link #ACTION_ENQUEUE Enqueue}: Enqueues content specified by a given Uri
- * and returns a new media item id to describe the request.  Implicitly creates a new
- * session if no session id was specified as a parameter.
- * <li>{@link #ACTION_REMOVE Remove}: Removes a specified media item from the queue.
- * </ul>
- *
- * <h4>Session actions</h4>
- * <p>
- * The following session actions must be supported (all or nothing) by remote
- * playback routes that offer optional session management capabilities.
- * </p><ul>
- * <li>{@link #ACTION_START_SESSION Start session}: Starts a new session explicitly.
- * <li>{@link #ACTION_GET_SESSION_STATUS Get session status}: Gets the status of a session.
- * <li>{@link #ACTION_END_SESSION End session}: Ends a session explicitly.
- * </ul>
- *
- * <h4>Implementation note</h4>
- * <p>
- * Implementations of the remote playback protocol must implement <em>all</em> of the
- * documented actions, parameters and results.  Note that the documentation is written from
- * the perspective of a client of the protocol.  In particular, whenever a parameter
- * is described as being "optional", it is only from the perspective of the client.
- * Compliant media route provider implementations of this protocol must support all
- * of the features described herein.
- * </p>
- */
-public final class MediaControlIntent {
-    /* Route categories. */
-
-    /**
-     * Media control category: Live audio.
-     * <p>
-     * A route that supports live audio routing will allow the media audio stream
-     * to be sent to supported destinations.  This can include internal speakers or
-     * audio jacks on the device itself, A2DP devices, and more.
-     * </p><p>
-     * When a live audio route is selected, audio routing is transparent to the application.
-     * All audio played on the media stream will be routed to the selected destination.
-     * </p><p>
-     * Refer to the class documentation for details about live audio routes.
-     * </p>
-     */
-    public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
-
-    /**
-     * Media control category: Live video.
-     * <p>
-     * A route that supports live video routing will allow a mirrored version
-     * of the device's primary display or a customized
-     * {@link android.app.Presentation Presentation} to be sent to supported
-     * destinations.
-     * </p><p>
-     * When a live video route is selected, audio and video routing is transparent
-     * to the application.  By default, audio and video is routed to the selected
-     * destination.  For certain live video routes, the application may also use a
-     * {@link android.app.Presentation Presentation} to replace the mirrored view
-     * on the external display with different content.
-     * </p><p>
-     * Refer to the class documentation for details about live video routes.
-     * </p>
-     *
-     * @see MediaRouter.RouteInfo#getPresentationDisplay()
-     * @see android.app.Presentation
-     */
-    public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
-
-    /**
-     * Media control category: Remote playback.
-     * <p>
-     * A route that supports remote playback routing will allow an application to send
-     * requests to play content remotely to supported destinations.
-     * </p><p>
-     * Remote playback routes destinations operate independently of the local device.
-     * When a remote playback route is selected, the application can control the content
-     * playing on the destination by sending media control actions to the route.
-     * The application may also receive status updates from the route regarding
-     * remote playback.
-     * </p><p>
-     * Refer to the class documentation for details about remote playback routes.
-     * </p>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     */
-    public static final String CATEGORY_REMOTE_PLAYBACK =
-            "android.media.intent.category.REMOTE_PLAYBACK";
-
-    /* Remote playback actions that affect individual items. */
-
-    /**
-     * Remote playback media control action: Play media item.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to start playing content with
-     * the {@link Uri} specified in the {@link Intent}'s {@link Intent#getData() data uri}.
-     * The action returns a media session id and media item id which can be used
-     * to control playback using other remote playback actions.
-     * </p><p>
-     * Once initiated, playback of the specified content will be managed independently
-     * by the destination.  The application will receive status updates as the state
-     * of the media item changes.
-     * </p><p>
-     * If the data uri specifies an HTTP or HTTPS scheme, then the destination is
-     * responsible for following HTTP redirects to a reasonable depth of at least 3
-     * levels as might typically be handled by a web browser.  If an HTTP error
-     * occurs, then the destination should send a {@link MediaItemStatus status update}
-     * back to the client indicating the {@link MediaItemStatus#PLAYBACK_STATE_ERROR error}
-     * {@link MediaItemStatus#getPlaybackState() playback state}.
-     * </p>
-     *
-     * <h3>One item at a time</h3>
-     * <p>
-     * Each successful play action <em>replaces</em> the previous play action.
-     * If an item is already playing, then it is canceled, the session's playback queue
-     * is cleared and the new item begins playing immediately (regardless of
-     * whether the previously playing item had been paused).
-     * </p><p>
-     * Play is therefore equivalent to {@link #ACTION_STOP stop} followed by an action
-     * to enqueue a new media item to be played immediately.
-     * </p>
-     *
-     * <h3>Sessions</h3>
-     * <p>
-     * This request has the effect of implicitly creating a media session whenever the
-     * application does not specify the {@link #EXTRA_SESSION_ID session id} parameter.
-     * Because there can only be at most one valid session at a time, creating a new session
-     * has the side-effect of invalidating any existing sessions and their media items,
-     * then handling the playback request with a new session.
-     * </p><p>
-     * If the application specifies an invalid session id, then an error is returned.
-     * When this happens, the application should assume that its session
-     * is no longer valid.  To obtain a new session, the application may try again
-     * and omit the session id parameter.  However, the application should
-     * only retry requests due to an explicit action performed by the user,
-     * such as the user clicking on a "play" button in the UI, since another
-     * application may be trying to take control of the route and the former
-     * application should try to stay out of its way.
-     * </p><p>
-     * For more information on sessions, queues and media items, please refer to the
-     * class documentation.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(optional)</em>: Specifies the session id of the
-     * session to which the playback request belongs.  If omitted, a new session
-     * is created implicitly.
-     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <em>(optional)</em>: Specifies the initial
-     * content playback position as a long integer number of milliseconds from
-     * the beginning of the content.
-     * <li>{@link #EXTRA_ITEM_METADATA} <em>(optional)</em>: Specifies metadata associated
-     * with the content such as the title of a song.
-     * <li>{@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER} <em>(optional)</em>: Specifies a
-     * {@link PendingIntent} for a broadcast receiver that will receive status updates
-     * about the media item.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(always returned)</em>: Specifies the session id of the
-     * session that was affected by the request.  This will be a new session in
-     * the case where no session id was supplied as a parameter.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(always returned)</em>: Specifies an opaque string identifier
-     * to use to refer to the media item in subsequent requests such as
-     * {@link #ACTION_GET_STATUS}.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the initial status of
-     * the new media item.
-     * </ul>
-     *
-     * <h3>Status updates</h3>
-     * <p>
-     * If the client supplies an
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver}
-     * then the media route provider is responsible for sending status updates to the receiver
-     * when significant media item state changes occur such as when playback starts or
-     * stops.  The receiver will not be invoked for content playback position changes.
-     * The application may retrieve the current playback position when necessary
-     * using the {@link #ACTION_GET_STATUS} request.
-     * </p><p>
-     * Refer to {@link MediaItemStatus} for details.
-     * </p>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if a session id was provided but is unknown or
-     * no longer valid, if the item Uri or content type is not supported, or if
-     * any other arguments are invalid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * <h3>Example</h3>
-     * <pre>
-     * MediaRouter mediaRouter = MediaRouter.getInstance(context);
-     * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
-     * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
-     * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-     * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
-     * if (route.supportsControlRequest(intent)) {
-     *     MediaRouter.ControlRequestCallback callback = new MediaRouter.ControlRequestCallback() {
-     *         public void onResult(Bundle data) {
-     *             // The request succeeded.
-     *             // Playback may be controlled using the returned session and item id.
-     *             String sessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID);
-     *             String itemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID);
-     *             MediaItemStatus status = MediaItemStatus.fromBundle(data.getBundle(
-     *                     MediaControlIntent.EXTRA_ITEM_STATUS));
-     *             // ...
-     *         }
-     *
-     *         public void onError(String message, Bundle data) {
-     *             // An error occurred!
-     *         }
-     *     };
-     *     route.sendControlRequest(intent, callback);
-     * }</pre>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE
-     * @see #ACTION_RESUME
-     * @see #ACTION_STOP
-     */
-    public static final String ACTION_PLAY = "android.media.intent.action.PLAY";
-
-    /**
-     * Remote playback media control action: Enqueue media item.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action works just like {@link #ACTION_PLAY play} except that it does
-     * not clear the queue or reset the pause state when it enqueues the
-     * new media item into the session's playback queue.  This action only
-     * enqueues a media item with no other side-effects on the queue.
-     * </p><p>
-     * If the queue is currently empty and then the item will play immediately
-     * (assuming the queue is not paused).  Otherwise, the item will play
-     * after all earlier items in the queue have finished or been removed.
-     * </p><p>
-     * The enqueue action can be used to create new sessions just like play.
-     * Its parameters and results are also the same.  Only the queuing behavior
-     * is different.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     */
-    public static final String ACTION_ENQUEUE = "android.media.intent.action.ENQUEUE";
-
-    /**
-     * Remote playback media control action: Seek media item to a new playback position.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to modify the current playback position
-     * of the specified media item.
-     * </p><p>
-     * This action only affects the playback position of the media item; not its playback state.
-     * If the playback queue is paused, then seeking sets the position but the item
-     * remains paused.  Likewise if the item is playing, then seeking will cause playback
-     * to jump to the new position and continue playing from that point.  If the item has
-     * not yet started playing, then the new playback position is remembered by the
-     * queue and used as the item's initial content position when playback eventually begins.
-     * </p><p>
-     * If successful, the media item's playback position is changed.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
-     * the media item to seek.
-     * <li>{@link #EXTRA_ITEM_CONTENT_POSITION} <em>(required)</em>: Specifies the new
-     * content position for playback as a long integer number of milliseconds from
-     * the beginning of the content.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the new status of
-     * the media item.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id or media item id are unknown
-     * or no longer valid, if the content position is invalid, or if the media item
-     * is in a terminal state.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_SEEK = "android.media.intent.action.SEEK";
-
-    /**
-     * Remote playback media control action: Get media item playback status
-     * and progress information.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action asks a remote playback route to provide updated playback status and progress
-     * information about the specified media item.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
-     * the media item to query.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the current status of
-     * the media item.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id or media item id are unknown
-     * or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #EXTRA_ITEM_STATUS_UPDATE_RECEIVER
-     */
-    public static final String ACTION_GET_STATUS = "android.media.intent.action.GET_STATUS";
-
-    /**
-     * Remote playback media control action: Remove media item from session's queue.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action asks a remote playback route to remove the specified media item
-     * from the session's playback queue.  If the current item is removed, then
-     * playback will proceed to the next media item (assuming the queue has not been
-     * paused).
-     * </p><p>
-     * This action does not affect the pause state of the queue.  If the queue was paused
-     * then it remains paused (even if it is now empty) until a resume, stop or play
-     * action is issued that causes the pause state to be cleared.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which the media item belongs.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of
-     * the media item to remove.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(always returned)</em>: Specifies the new status of
-     * the media item.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id or media item id are unknown
-     * or no longer valid, or if the media item is in a terminal state (and therefore
-     * no longer in the queue).
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_REMOVE = "android.media.intent.action.REMOVE";
-
-    /* Remote playback actions that affect the whole playback queue. */
-
-    /**
-     * Remote playback media control action: Pause media playback.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes the playback queue of the specified session to be paused.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * whose playback queue is to be paused.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_RESUME
-     */
-    public static final String ACTION_PAUSE = "android.media.intent.action.PAUSE";
-
-    /**
-     * Remote playback media control action: Resume media playback (unpause).
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes the playback queue of the specified session to be resumed.
-     * Reverses the effects of {@link #ACTION_PAUSE}.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * whose playback queue is to be resumed.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #ACTION_PAUSE
-     */
-    public static final String ACTION_RESUME = "android.media.intent.action.RESUME";
-
-    /**
-     * Remote playback media control action: Stop media playback (clear queue and unpause).
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to stop playback, cancel and remove
-     * all media items from the session's media item queue and, reset the queue's
-     * pause state.
-     * </p><p>
-     * If successful, the status of all media items in the queue is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent
-     * to the appropriate status update receivers indicating the new status of each item.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session whose playback queue is to be stopped (cleared and unpaused).
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_STOP = "android.media.intent.action.STOP";
-
-    /**
-     * Remote playback media control action: Start session.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to invalidate the current session
-     * and start a new session.  The new session initially has an empty queue.
-     * </p><p>
-     * If successful, the status of all media items in the previous session's queue is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_INVALIDATED invalidated} and a status update
-     * is sent to the appropriate status update receivers indicating the new status
-     * of each item.  The previous session becomes no longer valid and the new session
-     * takes control of the route.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER} <em>(optional)</em>: Specifies a
-     * {@link PendingIntent} for a broadcast receiver that will receive status updates
-     * about the media session.
-     * <li>{@link #EXTRA_MESSAGE_RECEIVER} <em>(optional)</em>: Specifies a
-     * {@link PendingIntent} for a broadcast receiver that will receive messages from
-     * the media session.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(always returned)</em>: Specifies the session id of the
-     * session that was started by the request.  This will always be a brand new session
-     * distinct from any other previously created sessions.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(always returned)</em>: Specifies the
-     * status of the media session.
-     * </ul>
-     *
-     * <h3>Status updates</h3>
-     * <p>
-     * If the client supplies a
-     * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER status update receiver}
-     * then the media route provider is responsible for sending status updates to the receiver
-     * when significant media session state changes occur such as when the session's
-     * queue is paused or resumed or when the session is terminated or invalidated.
-     * </p><p>
-     * Refer to {@link MediaSessionStatus} for details.
-     * </p>
-     *
-     * <h3>Custom messages</h3>
-     * <p>
-     * If the client supplies a {@link #EXTRA_MESSAGE_RECEIVER message receiver}
-     * then the media route provider is responsible for sending messages to the receiver
-     * when the session has any messages to send.
-     * </p><p>
-     * Refer to {@link #EXTRA_MESSAGE} for details.
-     * </p>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session could not be created.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_START_SESSION = "android.media.intent.action.START_SESSION";
-
-    /**
-     * Remote playback media control action: Get media session status information.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action asks a remote playback route to provide updated status information
-     * about the specified media session.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the
-     * session whose status is to be retrieved.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(always returned)</em>: Specifies the
-     * current status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     * @see #EXTRA_SESSION_STATUS_UPDATE_RECEIVER
-     */
-    public static final String ACTION_GET_SESSION_STATUS =
-            "android.media.intent.action.GET_SESSION_STATUS";
-
-    /**
-     * Remote playback media control action: End session.
-     * <p>
-     * Used with routes that support {@link #CATEGORY_REMOTE_PLAYBACK remote playback}
-     * media control.
-     * </p><p>
-     * This action causes a remote playback route to end the specified session.
-     * The session becomes no longer valid and the route ceases to be under control
-     * of the session.
-     * </p><p>
-     * If successful, the status of the session is set to
-     * {@link MediaSessionStatus#SESSION_STATE_ENDED} and a status update is sent to
-     * the session's status update receiver.
-     * </p><p>
-     * Additionally, the status of all media items in the queue is set to
-     * {@link MediaItemStatus#PLAYBACK_STATE_CANCELED canceled} and a status update is sent
-     * to the appropriate status update receivers indicating the new status of each item.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session to end.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(always returned)</em>: Specifies the
-     * status of the media session.
-     * </ul>
-     *
-     * <h3>Errors</h3>
-     * <p>
-     * This action returns an error if the session id is unknown or no longer valid.
-     * In other words, it is an error to attempt to end a session other than the
-     * current session.
-     * </p><ul>
-     * <li>{@link #EXTRA_ERROR_CODE} <em>(optional)</em>: Specifies the cause of the error.
-     * </ul>
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     * @see #CATEGORY_REMOTE_PLAYBACK
-     */
-    public static final String ACTION_END_SESSION = "android.media.intent.action.END_SESSION";
-
-    /**
-     * Custom media control action: Send {@link #EXTRA_MESSAGE}.
-     * <p>
-     * This action asks a route to handle a message described by EXTRA_MESSAGE.
-     * </p>
-     *
-     * <h3>Request parameters</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of the session
-     * to which will handle this message.
-     * <li>{@link #EXTRA_MESSAGE} <em>(required)</em>: Specifies the message to send.
-     * </ul>
-     *
-     * <h3>Result data</h3>
-     * Any messages defined by each media route provider.
-     *
-     * <h3>Errors</h3>
-     * Any error messages defined by each media route provider.
-     *
-     * @see MediaRouter.RouteInfo#sendControlRequest
-     */
-    public static final String ACTION_SEND_MESSAGE = "android.media.intent.action.SEND_MESSAGE";
-
-    /* Extras and related constants. */
-
-    /**
-     * Bundle extra: Media session id.
-     * <p>
-     * An opaque unique identifier that identifies the remote playback media session.
-     * </p><p>
-     * Used with various actions to specify the id of the media session to be controlled.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receivers} to identify
-     * the session to which the item in question belongs.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receivers} to identify
-     * the session.
-     * </p><p>
-     * The value is a unique string value generated by the media route provider
-     * to represent one particular media session.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE
-     * @see #ACTION_RESUME
-     * @see #ACTION_STOP
-     * @see #ACTION_START_SESSION
-     * @see #ACTION_GET_SESSION_STATUS
-     * @see #ACTION_END_SESSION
-     */
-    public static final String EXTRA_SESSION_ID =
-            "android.media.intent.extra.SESSION_ID";
-
-    /**
-     * Bundle extra: Media session status.
-     * <p>
-     * Returned as a result from media session actions such as {@link #ACTION_START_SESSION},
-     * {@link #ACTION_PAUSE}, and {@link #ACTION_GET_SESSION_STATUS}
-     * to describe the status of the specified media session.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receivers} to provide
-     * updated status information.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of data that can be converted into
-     * a {@link MediaSessionStatus} object using
-     * {@link MediaSessionStatus#fromBundle MediaSessionStatus.fromBundle}.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     * @see #ACTION_PAUSE
-     * @see #ACTION_RESUME
-     * @see #ACTION_STOP
-     * @see #ACTION_START_SESSION
-     * @see #ACTION_GET_SESSION_STATUS
-     * @see #ACTION_END_SESSION
-     */
-    public static final String EXTRA_SESSION_STATUS =
-            "android.media.intent.extra.SESSION_STATUS";
-
-    /**
-     * Bundle extra: Media session status update receiver.
-     * <p>
-     * Used with {@link #ACTION_START_SESSION} to specify a {@link PendingIntent} for a
-     * broadcast receiver that will receive status updates about the media session.
-     * </p><p>
-     * Whenever the status of the media session changes, the media route provider will
-     * send a broadcast to the pending intent with extras that identify the session
-     * id and its updated status.
-     * </p><p>
-     * The value is a {@link PendingIntent}.
-     * </p>
-     *
-     * <h3>Broadcast extras</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(required)</em>: Specifies the status of the
-     * session as a bundle that can be decoded into a {@link MediaSessionStatus} object.
-     * </ul>
-     *
-     * @see #ACTION_START_SESSION
-     */
-    public static final String EXTRA_SESSION_STATUS_UPDATE_RECEIVER =
-            "android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER";
-
-    /**
-     * Bundle extra: Media message receiver.
-     * <p>
-     * Used with {@link #ACTION_START_SESSION} to specify a {@link PendingIntent} for a
-     * broadcast receiver that will receive messages from the media session.
-     * </p><p>
-     * When the media session has a message to send, the media route provider will
-     * send a broadcast to the pending intent with extras that identify the session
-     * id and its message.
-     * </p><p>
-     * The value is a {@link PendingIntent}.
-     * </p>
-     *
-     * <h3>Broadcast extras</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session.
-     * <li>{@link #EXTRA_MESSAGE} <em>(required)</em>: Specifies the message from
-     * the session as a bundle object.
-     * </ul>
-     *
-     * @see #ACTION_START_SESSION
-     */
-    public static final String EXTRA_MESSAGE_RECEIVER =
-            "android.media.intent.extra.MESSAGE_RECEIVER";
-
-    /**
-     * Bundle extra: Media item id.
-     * <p>
-     * An opaque unique identifier returned as a result from {@link #ACTION_PLAY} or
-     * {@link #ACTION_ENQUEUE} that represents the media item that was created by the
-     * playback request.
-     * </p><p>
-     * Used with various actions to specify the id of the media item to be controlled.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receivers} to identify
-     * the item in question.
-     * </p><p>
-     * The value is a unique string value generated by the media route provider
-     * to represent one particular media item.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     */
-    public static final String EXTRA_ITEM_ID =
-            "android.media.intent.extra.ITEM_ID";
-
-    /**
-     * Bundle extra: Media item status.
-     * <p>
-     * Returned as a result from media item actions such as {@link #ACTION_PLAY},
-     * {@link #ACTION_ENQUEUE}, {@link #ACTION_SEEK}, and {@link #ACTION_GET_STATUS}
-     * to describe the status of the specified media item.
-     * </p><p>
-     * Included in broadcast intents sent to
-     * {@link #EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receivers} to provide
-     * updated status information.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of data that can be converted into
-     * a {@link MediaItemStatus} object using
-     * {@link MediaItemStatus#fromBundle MediaItemStatus.fromBundle}.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     * @see #ACTION_SEEK
-     * @see #ACTION_GET_STATUS
-     */
-    public static final String EXTRA_ITEM_STATUS =
-            "android.media.intent.extra.ITEM_STATUS";
-
-    /**
-     * Long extra: Media item content position.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify the
-     * starting playback position.
-     * </p><p>
-     * Used with {@link #ACTION_SEEK} to set a new playback position.
-     * </p><p>
-     * The value is a long integer number of milliseconds from the beginning of the content.
-     * <p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     * @see #ACTION_SEEK
-     */
-    public static final String EXTRA_ITEM_CONTENT_POSITION =
-            "android.media.intent.extra.ITEM_POSITION";
-
-    /**
-     * Bundle extra: Media item metadata.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify metadata
-     * associated with the content of a media item.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of metadata key-value pairs as defined
-     * in {@link MediaItemMetadata}.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     */
-    public static final String EXTRA_ITEM_METADATA =
-            "android.media.intent.extra.ITEM_METADATA";
-
-    /**
-     * Bundle extra: HTTP request headers.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify HTTP request
-     * headers to be included when fetching to the content indicated by the media
-     * item's data Uri.
-     * </p><p>
-     * This extra may be used to provide authentication tokens and other
-     * parameters to the server separately from the media item's data Uri.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of string based key-value pairs
-     * that describe the HTTP request headers.
-     * </p>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     */
-    public static final String EXTRA_ITEM_HTTP_HEADERS =
-            "android.media.intent.extra.HTTP_HEADERS";
-
-    /**
-     * Bundle extra: Media item status update receiver.
-     * <p>
-     * Used with {@link #ACTION_PLAY} or {@link #ACTION_ENQUEUE} to specify
-     * a {@link PendingIntent} for a
-     * broadcast receiver that will receive status updates about a particular
-     * media item.
-     * </p><p>
-     * Whenever the status of the media item changes, the media route provider will
-     * send a broadcast to the pending intent with extras that identify the session
-     * to which the item belongs, the session status, the item's id
-     * and the item's updated status.
-     * </p><p>
-     * The same pending intent and broadcast receiver may be shared by any number of
-     * media items since the broadcast intent includes the media session id
-     * and media item id.
-     * </p><p>
-     * The value is a {@link PendingIntent}.
-     * </p>
-     *
-     * <h3>Broadcast extras</h3>
-     * <ul>
-     * <li>{@link #EXTRA_SESSION_ID} <em>(required)</em>: Specifies the session id of
-     * the session to which the item in question belongs.
-     * <li>{@link #EXTRA_SESSION_STATUS} <em>(optional, old implementations may
-     * omit this key)</em>: Specifies the status of the media session.
-     * <li>{@link #EXTRA_ITEM_ID} <em>(required)</em>: Specifies the media item id of the
-     * media item in question.
-     * <li>{@link #EXTRA_ITEM_STATUS} <em>(required)</em>: Specifies the status of the
-     * item as a bundle that can be decoded into a {@link MediaItemStatus} object.
-     * </ul>
-     *
-     * @see #ACTION_PLAY
-     * @see #ACTION_ENQUEUE
-     */
-    public static final String EXTRA_ITEM_STATUS_UPDATE_RECEIVER =
-            "android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER";
-
-    /**
-     * Bundle extra: Message.
-     * <p>
-     * Used with {@link #ACTION_SEND_MESSAGE}, and included in broadcast intents sent to
-     * {@link #EXTRA_MESSAGE_RECEIVER message receivers} to describe a message between a
-     * session and a media route provider.
-     * </p><p>
-     * The value is a {@link android.os.Bundle}.
-     * </p>
-     */
-    public static final String EXTRA_MESSAGE = "android.media.intent.extra.MESSAGE";
-
-    /**
-     * Integer extra: Error code.
-     * <p>
-     * Used with all media control requests to describe the cause of an error.
-     * This extra may be omitted when the error is unknown.
-     * </p><p>
-     * The value is one of: {@link #ERROR_UNKNOWN}, {@link #ERROR_UNSUPPORTED_OPERATION},
-     * {@link #ERROR_INVALID_SESSION_ID}, {@link #ERROR_INVALID_ITEM_ID}.
-     * </p>
-     */
-    public static final String EXTRA_ERROR_CODE = "android.media.intent.extra.ERROR_CODE";
-
-    /**
-     * Error code: An unknown error occurred.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_UNKNOWN = 0;
-
-    /**
-     * Error code: The operation is not supported.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_UNSUPPORTED_OPERATION = 1;
-
-    /**
-     * Error code: The session id specified in the request was invalid.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_INVALID_SESSION_ID = 2;
-
-    /**
-     * Error code: The item id specified in the request was invalid.
-     *
-     * @see #EXTRA_ERROR_CODE
-     */
-    public static final int ERROR_INVALID_ITEM_ID = 3;
-
-    private MediaControlIntent() {
-    }
-}
diff --git a/android/support/v7/media/MediaItemMetadata.java b/android/support/v7/media/MediaItemMetadata.java
deleted file mode 100644
index a60653e..0000000
--- a/android/support/v7/media/MediaItemMetadata.java
+++ /dev/null
@@ -1,138 +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.v7.media;
-
-import android.os.Bundle;
-
-/**
- * Constants for specifying metadata about a media item as a {@link Bundle}.
- * <p>
- * This class is part of the remote playback protocol described by the
- * {@link MediaControlIntent MediaControlIntent} class.
- * </p><p>
- * Media item metadata is described as a bundle of key/value pairs as defined
- * in this class.  The documentation specifies the type of value associated
- * with each key.
- * </p><p>
- * An application may specify additional custom metadata keys but there is no guarantee
- * that they will be recognized by the destination.
- * </p>
- */
-public final class MediaItemMetadata {
-    /*
-     * Note: MediaMetadataRetriever also defines a collection of metadata keys that can be
-     * retrieved from a content stream although the representation is somewhat different here
-     * since we are sending the data to a remote endpoint.
-     */
-
-    private MediaItemMetadata() {
-    }
-
-    /**
-     * String key: Album artist name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
-
-    /**
-     * String key: Album title.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_ALBUM_TITLE = "android.media.metadata.ALBUM_TITLE";
-
-    /**
-     * String key: Artwork Uri.
-     * <p>
-     * The value is a string URI for an image file associated with the media item,
-     * such as album or cover art.
-     * </p>
-     */
-    public static final String KEY_ARTWORK_URI = "android.media.metadata.ARTWORK_URI";
-
-    /**
-     * String key: Artist name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_ARTIST = "android.media.metadata.ARTIST";
-
-    /**
-     * String key: Author name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_AUTHOR = "android.media.metadata.AUTHOR";
-
-    /**
-     * String key: Composer name.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_COMPOSER = "android.media.metadata.COMPOSER";
-
-    /**
-     * String key: Track title.
-     * <p>
-     * The value is a string suitable for display.
-     * </p>
-     */
-    public static final String KEY_TITLE = "android.media.metadata.TITLE";
-
-    /**
-     * Integer key: Year of publication.
-     * <p>
-     * The value is an integer year number.
-     * </p>
-     */
-    public static final String KEY_YEAR = "android.media.metadata.YEAR";
-
-    /**
-     * Integer key: Track number (such as a track on a CD).
-     * <p>
-     * The value is a one-based integer track number.
-     * </p>
-     */
-    public static final String KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
-
-    /**
-     * Integer key: Disc number within a collection.
-     * <p>
-     * The value is a one-based integer disc number.
-     * </p>
-     */
-    public static final String KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
-
-    /**
-     * Long key: Item playback duration in milliseconds.
-     * <p>
-     * The value is a <code>long</code> number of milliseconds.
-     * </p><p>
-     * The duration metadata is only a hint to enable a remote media player to
-     * guess the duration of the content before it actually opens the media stream.
-     * The remote media player should still determine the actual content duration from
-     * the media stream itself independent of the value that may be specified by this key.
-     * </p>
-     */
-    public static final String KEY_DURATION = "android.media.metadata.DURATION";
-}
diff --git a/android/support/v7/media/MediaItemStatus.java b/android/support/v7/media/MediaItemStatus.java
deleted file mode 100644
index c3d1c5e..0000000
--- a/android/support/v7/media/MediaItemStatus.java
+++ /dev/null
@@ -1,392 +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.v7.media;
-
-import android.app.PendingIntent;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.support.v4.util.TimeUtils;
-
-/**
- * Describes the playback status of a media item.
- * <p>
- * This class is part of the remote playback protocol described by the
- * {@link MediaControlIntent MediaControlIntent} class.
- * </p><p>
- * As a media item is played, it transitions through a sequence of states including:
- * {@link #PLAYBACK_STATE_PENDING pending}, {@link #PLAYBACK_STATE_BUFFERING buffering},
- * {@link #PLAYBACK_STATE_PLAYING playing}, {@link #PLAYBACK_STATE_PAUSED paused},
- * {@link #PLAYBACK_STATE_FINISHED finished}, {@link #PLAYBACK_STATE_CANCELED canceled},
- * {@link #PLAYBACK_STATE_INVALIDATED invalidated}, and
- * {@link #PLAYBACK_STATE_ERROR error}.  Refer to the documentation of each state
- * for an explanation of its meaning.
- * </p><p>
- * While the item is playing, the playback status may also include progress information
- * about the {@link #getContentPosition content position} and
- * {@link #getContentDuration content duration} although not all route destinations
- * will report it.
- * </p><p>
- * To monitor playback status, the application should supply a {@link PendingIntent} to use as the
- * {@link MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver}
- * for a given {@link MediaControlIntent#ACTION_PLAY playback request}.  Note that
- * the status update receiver will only be invoked for major status changes such as a
- * transition from playing to finished.
- * </p><p class="note">
- * The status update receiver will not be invoked for minor progress updates such as
- * changes to playback position or duration.  If the application wants to monitor
- * playback progress, then it must use the
- * {@link MediaControlIntent#ACTION_GET_STATUS get status request} to poll for changes
- * periodically and estimate the playback position while playing.  Note that there may
- * be a significant power impact to polling so the application is advised only
- * to poll when the screen is on and never more than about once every 5 seconds or so.
- * </p><p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaItemStatus {
-    static final String KEY_TIMESTAMP = "timestamp";
-    static final String KEY_PLAYBACK_STATE = "playbackState";
-    static final String KEY_CONTENT_POSITION = "contentPosition";
-    static final String KEY_CONTENT_DURATION = "contentDuration";
-    static final String KEY_EXTRAS = "extras";
-
-    final Bundle mBundle;
-
-    /**
-     * Playback state: Pending.
-     * <p>
-     * Indicates that the media item has not yet started playback but will be played eventually.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_PENDING = 0;
-
-    /**
-     * Playback state: Playing.
-     * <p>
-     * Indicates that the media item is currently playing.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_PLAYING = 1;
-
-    /**
-     * Playback state: Paused.
-     * <p>
-     * Indicates that playback of the media item has been paused.  Playback can be
-     * resumed using the {@link MediaControlIntent#ACTION_RESUME resume} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_PAUSED = 2;
-
-    /**
-     * Playback state: Buffering or seeking to a new position.
-     * <p>
-     * Indicates that the media item has been temporarily interrupted
-     * to fetch more content.  Playback will continue automatically
-     * when enough content has been buffered.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_BUFFERING = 3;
-
-    /**
-     * Playback state: Finished.
-     * <p>
-     * Indicates that the media item played to the end of the content and finished normally.
-     * </p><p>
-     * A finished media item cannot be resumed.  To play the content again, the application
-     * must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_FINISHED = 4;
-
-    /**
-     * Playback state: Canceled.
-     * <p>
-     * Indicates that the media item was explicitly removed from the queue by the
-     * application.  Items may be canceled and removed from the queue using
-     * the {@link MediaControlIntent#ACTION_REMOVE remove} or
-     * {@link MediaControlIntent#ACTION_STOP stop} action or by issuing
-     * another {@link MediaControlIntent#ACTION_PLAY play} action that has the
-     * side-effect of clearing the queue.
-     * </p><p>
-     * A canceled media item cannot be resumed.  To play the content again, the
-     * application must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_CANCELED = 5;
-
-    /**
-     * Playback state: Invalidated.
-     * <p>
-     * Indicates that the media item was invalidated permanently and involuntarily.
-     * This state is used to indicate that the media item was invalidated and removed
-     * from the queue because the session to which it belongs was invalidated
-     * (typically by another application taking control of the route).
-     * </p><p>
-     * When invalidation occurs, the application should generally wait for the user
-     * to perform an explicit action, such as clicking on a play button in the UI,
-     * before creating a new media session to avoid unnecessarily interrupting
-     * another application that may have just started using the route.
-     * </p><p>
-     * An invalidated media item cannot be resumed.  To play the content again, the application
-     * must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_INVALIDATED = 6;
-
-    /**
-     * Playback state: Playback halted or aborted due to an error.
-     * <p>
-     * Examples of errors are no network connectivity when attempting to retrieve content
-     * from a server, or expired user credentials when trying to play subscription-based
-     * content.
-     * </p><p>
-     * A media item in the error state cannot be resumed.  To play the content again,
-     * the application must send a new {@link MediaControlIntent#ACTION_PLAY play} or
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
-     * </p>
-     */
-    public static final int PLAYBACK_STATE_ERROR = 7;
-
-    /**
-     * Integer extra: HTTP status code.
-     * <p>
-     * Specifies the HTTP status code that was encountered when the content
-     * was requested after all redirects were followed.  This key only needs to
-     * specified when the content uri uses the HTTP or HTTPS scheme and an error
-     * occurred.  This key may be omitted if the content was able to be played
-     * successfully; there is no need to report a 200 (OK) status code.
-     * </p><p>
-     * The value is an integer HTTP status code, such as 401 (Unauthorized),
-     * 404 (Not Found), or 500 (Server Error), or 0 if none.
-     * </p>
-     */
-    public static final String EXTRA_HTTP_STATUS_CODE =
-            "android.media.status.extra.HTTP_STATUS_CODE";
-
-    /**
-     * Bundle extra: HTTP response headers.
-     * <p>
-     * Specifies the HTTP response headers that were returned when the content was
-     * requested from the network.  The headers may include additional information
-     * about the content or any errors conditions that were encountered while
-     * trying to fetch the content.
-     * </p><p>
-     * The value is a {@link android.os.Bundle} of string based key-value pairs
-     * that describe the HTTP response headers.
-     * </p>
-     */
-    public static final String EXTRA_HTTP_RESPONSE_HEADERS =
-            "android.media.status.extra.HTTP_RESPONSE_HEADERS";
-
-    MediaItemStatus(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    /**
-     * Gets the timestamp associated with the status information in
-     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-     *
-     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
-     */
-    public long getTimestamp() {
-        return mBundle.getLong(KEY_TIMESTAMP);
-    }
-
-    /**
-     * Gets the playback state of the media item.
-     *
-     * @return The playback state.  One of {@link #PLAYBACK_STATE_PENDING},
-     * {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
-     * {@link #PLAYBACK_STATE_BUFFERING}, {@link #PLAYBACK_STATE_FINISHED},
-     * {@link #PLAYBACK_STATE_CANCELED}, {@link #PLAYBACK_STATE_INVALIDATED},
-     * or {@link #PLAYBACK_STATE_ERROR}.
-     */
-    public int getPlaybackState() {
-        return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_ERROR);
-    }
-
-    /**
-     * Gets the content playback position as a long integer number of milliseconds
-     * from the beginning of the content.
-     *
-     * @return The content playback position in milliseconds, or -1 if unknown.
-     */
-    public long getContentPosition() {
-        return mBundle.getLong(KEY_CONTENT_POSITION, -1);
-    }
-
-    /**
-     * Gets the total duration of the content to be played as a long integer number of
-     * milliseconds.
-     *
-     * @return The content duration in milliseconds, or -1 if unknown.
-     */
-    public long getContentDuration() {
-        return mBundle.getLong(KEY_CONTENT_DURATION, -1);
-    }
-
-    /**
-     * Gets a bundle of extras for this status object.
-     * The extras will be ignored by the media router but they may be used
-     * by applications.
-     */
-    public Bundle getExtras() {
-        return mBundle.getBundle(KEY_EXTRAS);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaItemStatus{ ");
-        result.append("timestamp=");
-        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
-        result.append(" ms ago");
-        result.append(", playbackState=").append(playbackStateToString(getPlaybackState()));
-        result.append(", contentPosition=").append(getContentPosition());
-        result.append(", contentDuration=").append(getContentDuration());
-        result.append(", extras=").append(getExtras());
-        result.append(" }");
-        return result.toString();
-    }
-
-    private static String playbackStateToString(int playbackState) {
-        switch (playbackState) {
-            case PLAYBACK_STATE_PENDING:
-                return "pending";
-            case PLAYBACK_STATE_BUFFERING:
-                return "buffering";
-            case PLAYBACK_STATE_PLAYING:
-                return "playing";
-            case PLAYBACK_STATE_PAUSED:
-                return "paused";
-            case PLAYBACK_STATE_FINISHED:
-                return "finished";
-            case PLAYBACK_STATE_CANCELED:
-                return "canceled";
-            case PLAYBACK_STATE_INVALIDATED:
-                return "invalidated";
-            case PLAYBACK_STATE_ERROR:
-                return "error";
-        }
-        return Integer.toString(playbackState);
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaItemStatus fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaItemStatus(bundle) : null;
-    }
-
-    /**
-     * Builder for {@link MediaItemStatus media item status objects}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-
-        /**
-         * Creates a media item status builder using the current time as the
-         * reference timestamp.
-         *
-         * @param playbackState The item playback state.
-         */
-        public Builder(int playbackState) {
-            mBundle = new Bundle();
-            setTimestamp(SystemClock.elapsedRealtime());
-            setPlaybackState(playbackState);
-        }
-
-        /**
-         * Creates a media item status builder whose initial contents are
-         * copied from an existing status.
-         */
-        public Builder(MediaItemStatus status) {
-            if (status == null) {
-                throw new IllegalArgumentException("status must not be null");
-            }
-
-            mBundle = new Bundle(status.mBundle);
-        }
-
-        /**
-         * Sets the timestamp associated with the status information in
-         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-         */
-        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
-            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
-            return this;
-        }
-
-        /**
-         * Sets the playback state of the media item.
-         */
-        public Builder setPlaybackState(int playbackState) {
-            mBundle.putInt(KEY_PLAYBACK_STATE, playbackState);
-            return this;
-        }
-
-        /**
-         * Sets the content playback position as a long integer number of milliseconds
-         * from the beginning of the content.
-         */
-        public Builder setContentPosition(long positionMilliseconds) {
-            mBundle.putLong(KEY_CONTENT_POSITION, positionMilliseconds);
-            return this;
-        }
-
-        /**
-         * Sets the total duration of the content to be played as a long integer number
-         * of milliseconds.
-         */
-        public Builder setContentDuration(long durationMilliseconds) {
-            mBundle.putLong(KEY_CONTENT_DURATION, durationMilliseconds);
-            return this;
-        }
-
-        /**
-         * Sets a bundle of extras for this status object.
-         * The extras will be ignored by the media router but they may be used
-         * by applications.
-         */
-        public Builder setExtras(Bundle extras) {
-            mBundle.putBundle(KEY_EXTRAS, extras);
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaItemStatus media item status object}.
-         */
-        public MediaItemStatus build() {
-            return new MediaItemStatus(mBundle);
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouteDescriptor.java b/android/support/v7/media/MediaRouteDescriptor.java
deleted file mode 100644
index 0a4d0d4..0000000
--- a/android/support/v7/media/MediaRouteDescriptor.java
+++ /dev/null
@@ -1,696 +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.v7.media;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Describes the properties of a route.
- * <p>
- * Each route is uniquely identified by an opaque id string.  This token
- * may take any form as long as it is unique within the media route provider.
- * </p><p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaRouteDescriptor {
-    static final String KEY_ID = "id";
-    static final String KEY_GROUP_MEMBER_IDS = "groupMemberIds";
-    static final String KEY_NAME = "name";
-    static final String KEY_DESCRIPTION = "status";
-    static final String KEY_ICON_URI = "iconUri";
-    static final String KEY_ENABLED = "enabled";
-    static final String KEY_CONNECTING = "connecting";
-    static final String KEY_CONNECTION_STATE = "connectionState";
-    static final String KEY_CONTROL_FILTERS = "controlFilters";
-    static final String KEY_PLAYBACK_TYPE = "playbackType";
-    static final String KEY_PLAYBACK_STREAM = "playbackStream";
-    static final String KEY_DEVICE_TYPE = "deviceType";
-    static final String KEY_VOLUME = "volume";
-    static final String KEY_VOLUME_MAX = "volumeMax";
-    static final String KEY_VOLUME_HANDLING = "volumeHandling";
-    static final String KEY_PRESENTATION_DISPLAY_ID = "presentationDisplayId";
-    static final String KEY_EXTRAS = "extras";
-    static final String KEY_CAN_DISCONNECT = "canDisconnect";
-    static final String KEY_SETTINGS_INTENT = "settingsIntent";
-    static final String KEY_MIN_CLIENT_VERSION = "minClientVersion";
-    static final String KEY_MAX_CLIENT_VERSION = "maxClientVersion";
-
-    final Bundle mBundle;
-    List<IntentFilter> mControlFilters;
-
-    MediaRouteDescriptor(Bundle bundle, List<IntentFilter> controlFilters) {
-        mBundle = bundle;
-        mControlFilters = controlFilters;
-    }
-
-    /**
-     * Gets the unique id of the route.
-     * <p>
-     * The route id associated with a route descriptor functions as a stable
-     * identifier for the route and must be unique among all routes offered
-     * by the provider.
-     * </p>
-     */
-    public String getId() {
-        return mBundle.getString(KEY_ID);
-    }
-
-    /**
-     * Gets the group member ids of the route.
-     * <p>
-     * A route descriptor that has one or more group member route ids
-     * represents a route group. A member route may belong to another group.
-     * </p>
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public List<String> getGroupMemberIds() {
-        return mBundle.getStringArrayList(KEY_GROUP_MEMBER_IDS);
-    }
-
-    /**
-     * Gets the user-visible name of the route.
-     * <p>
-     * The route name identifies the destination represented by the route.
-     * It may be a user-supplied name, an alias, or device serial number.
-     * </p>
-     */
-    public String getName() {
-        return mBundle.getString(KEY_NAME);
-    }
-
-    /**
-     * Gets the user-visible description of the route.
-     * <p>
-     * The route description describes the kind of destination represented by the route.
-     * It may be a user-supplied string, a model number or brand of device.
-     * </p>
-     */
-    public String getDescription() {
-        return mBundle.getString(KEY_DESCRIPTION);
-    }
-
-    /**
-     * Gets the URI of the icon representing this route.
-     * <p>
-     * This icon will be used in picker UIs if available.
-     * </p>
-     */
-    public Uri getIconUri() {
-        String iconUri = mBundle.getString(KEY_ICON_URI);
-        return iconUri == null ? null : Uri.parse(iconUri);
-    }
-
-    /**
-     * Gets whether the route is enabled.
-     */
-    public boolean isEnabled() {
-        return mBundle.getBoolean(KEY_ENABLED, true);
-    }
-
-    /**
-     * Gets whether the route is connecting.
-     * @deprecated Use {@link #getConnectionState} instead
-     */
-    @Deprecated
-    public boolean isConnecting() {
-        return mBundle.getBoolean(KEY_CONNECTING, false);
-    }
-
-    /**
-     * Gets the connection state of the route.
-     *
-     * @return The connection state of this route:
-     * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED},
-     * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or
-     * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}.
-     */
-    public int getConnectionState() {
-        return mBundle.getInt(KEY_CONNECTION_STATE,
-                MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED);
-    }
-
-    /**
-     * Gets whether the route can be disconnected without stopping playback.
-     * <p>
-     * The route can normally be disconnected without stopping playback when
-     * the destination device on the route is connected to two or more source
-     * devices. The route provider should update the route immediately when the
-     * number of connected devices changes.
-     * </p><p>
-     * To specify that the route should disconnect without stopping use
-     * {@link MediaRouter#unselect(int)} with
-     * {@link MediaRouter#UNSELECT_REASON_DISCONNECTED}.
-     * </p>
-     */
-    public boolean canDisconnectAndKeepPlaying() {
-        return mBundle.getBoolean(KEY_CAN_DISCONNECT, false);
-    }
-
-    /**
-     * Gets an {@link IntentSender} for starting a settings activity for this
-     * route. The activity may have specific route settings or general settings
-     * for the connected device or route provider.
-     *
-     * @return An {@link IntentSender} to start a settings activity.
-     */
-    public IntentSender getSettingsActivity() {
-        return mBundle.getParcelable(KEY_SETTINGS_INTENT);
-    }
-
-    /**
-     * Gets the route's {@link MediaControlIntent media control intent} filters.
-     */
-    public List<IntentFilter> getControlFilters() {
-        ensureControlFilters();
-        return mControlFilters;
-    }
-
-    void ensureControlFilters() {
-        if (mControlFilters == null) {
-            mControlFilters = mBundle.<IntentFilter>getParcelableArrayList(KEY_CONTROL_FILTERS);
-            if (mControlFilters == null) {
-                mControlFilters = Collections.<IntentFilter>emptyList();
-            }
-        }
-    }
-
-    /**
-     * Gets the type of playback associated with this route.
-     *
-     * @return The type of playback associated with this route:
-     * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or
-     * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}.
-     */
-    public int getPlaybackType() {
-        return mBundle.getInt(KEY_PLAYBACK_TYPE, MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE);
-    }
-
-    /**
-     * Gets the route's playback stream.
-     */
-    public int getPlaybackStream() {
-        return mBundle.getInt(KEY_PLAYBACK_STREAM, -1);
-    }
-
-    /**
-     * Gets the type of the receiver device associated with this route.
-     *
-     * @return The type of the receiver device associated with this route:
-     * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or
-     * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}.
-     */
-    public int getDeviceType() {
-        return mBundle.getInt(KEY_DEVICE_TYPE);
-    }
-
-    /**
-     * Gets the route's current volume, or 0 if unknown.
-     */
-    public int getVolume() {
-        return mBundle.getInt(KEY_VOLUME);
-    }
-
-    /**
-     * Gets the route's maximum volume, or 0 if unknown.
-     */
-    public int getVolumeMax() {
-        return mBundle.getInt(KEY_VOLUME_MAX);
-    }
-
-    /**
-     * Gets information about how volume is handled on the route.
-     *
-     * @return How volume is handled on the route:
-     * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or
-     * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}.
-     */
-    public int getVolumeHandling() {
-        return mBundle.getInt(KEY_VOLUME_HANDLING,
-                MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED);
-    }
-
-    /**
-     * Gets the route's presentation display id, or -1 if none.
-     */
-    public int getPresentationDisplayId() {
-        return mBundle.getInt(
-                KEY_PRESENTATION_DISPLAY_ID, MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE);
-    }
-
-    /**
-     * Gets a bundle of extras for this route descriptor.
-     * The extras will be ignored by the media router but they may be used
-     * by applications.
-     */
-    public Bundle getExtras() {
-        return mBundle.getBundle(KEY_EXTRAS);
-    }
-
-    /**
-     * Gets the minimum client version required for this route.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int getMinClientVersion() {
-        return mBundle.getInt(KEY_MIN_CLIENT_VERSION,
-                MediaRouteProviderProtocol.CLIENT_VERSION_START);
-    }
-
-    /**
-     * Gets the maximum client version required for this route.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int getMaxClientVersion() {
-        return mBundle.getInt(KEY_MAX_CLIENT_VERSION, Integer.MAX_VALUE);
-    }
-
-    /**
-     * Returns true if the route descriptor has all of the required fields.
-     */
-    public boolean isValid() {
-        ensureControlFilters();
-        if (TextUtils.isEmpty(getId())
-                || TextUtils.isEmpty(getName())
-                || mControlFilters.contains(null)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaRouteDescriptor{ ");
-        result.append("id=").append(getId());
-        result.append(", groupMemberIds=").append(getGroupMemberIds());
-        result.append(", name=").append(getName());
-        result.append(", description=").append(getDescription());
-        result.append(", iconUri=").append(getIconUri());
-        result.append(", isEnabled=").append(isEnabled());
-        result.append(", isConnecting=").append(isConnecting());
-        result.append(", connectionState=").append(getConnectionState());
-        result.append(", controlFilters=").append(Arrays.toString(getControlFilters().toArray()));
-        result.append(", playbackType=").append(getPlaybackType());
-        result.append(", playbackStream=").append(getPlaybackStream());
-        result.append(", deviceType=").append(getDeviceType());
-        result.append(", volume=").append(getVolume());
-        result.append(", volumeMax=").append(getVolumeMax());
-        result.append(", volumeHandling=").append(getVolumeHandling());
-        result.append(", presentationDisplayId=").append(getPresentationDisplayId());
-        result.append(", extras=").append(getExtras());
-        result.append(", isValid=").append(isValid());
-        result.append(", minClientVersion=").append(getMinClientVersion());
-        result.append(", maxClientVersion=").append(getMaxClientVersion());
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteDescriptor fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaRouteDescriptor(bundle, null) : null;
-    }
-
-    /**
-     * Builder for {@link MediaRouteDescriptor media route descriptors}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-        private ArrayList<String> mGroupMemberIds;
-        private ArrayList<IntentFilter> mControlFilters;
-
-        /**
-         * Creates a media route descriptor builder.
-         *
-         * @param id The unique id of the route.
-         * @param name The user-visible name of the route.
-         */
-        public Builder(String id, String name) {
-            mBundle = new Bundle();
-            setId(id);
-            setName(name);
-        }
-
-        /**
-         * Creates a media route descriptor builder whose initial contents are
-         * copied from an existing descriptor.
-         */
-        public Builder(MediaRouteDescriptor descriptor) {
-            if (descriptor == null) {
-                throw new IllegalArgumentException("descriptor must not be null");
-            }
-
-            mBundle = new Bundle(descriptor.mBundle);
-
-            descriptor.ensureControlFilters();
-            if (!descriptor.mControlFilters.isEmpty()) {
-                mControlFilters = new ArrayList<IntentFilter>(descriptor.mControlFilters);
-            }
-        }
-
-        /**
-         * Sets the unique id of the route.
-         * <p>
-         * The route id associated with a route descriptor functions as a stable
-         * identifier for the route and must be unique among all routes offered
-         * by the provider.
-         * </p>
-         */
-        public Builder setId(String id) {
-            mBundle.putString(KEY_ID, id);
-            return this;
-        }
-
-        /**
-         * Adds a group member id of the route.
-         * <p>
-         * A route descriptor that has one or more group member route ids
-         * represents a route group. A member route may belong to another group.
-         * </p>
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public Builder addGroupMemberId(String groupMemberId) {
-            if (TextUtils.isEmpty(groupMemberId)) {
-                throw new IllegalArgumentException("groupMemberId must not be empty");
-            }
-
-            if (mGroupMemberIds == null) {
-                mGroupMemberIds = new ArrayList<>();
-            }
-            if (!mGroupMemberIds.contains(groupMemberId)) {
-                mGroupMemberIds.add(groupMemberId);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a list of group member ids of the route.
-         * <p>
-         * A route descriptor that has one or more group member route ids
-         * represents a route group. A member route may belong to another group.
-         * </p>
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public Builder addGroupMemberIds(Collection<String> groupMemberIds) {
-            if (groupMemberIds == null) {
-                throw new IllegalArgumentException("groupMemberIds must not be null");
-            }
-
-            if (!groupMemberIds.isEmpty()) {
-                for (String groupMemberId : groupMemberIds) {
-                    addGroupMemberId(groupMemberId);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Sets the user-visible name of the route.
-         * <p>
-         * The route name identifies the destination represented by the route.
-         * It may be a user-supplied name, an alias, or device serial number.
-         * </p>
-         */
-        public Builder setName(String name) {
-            mBundle.putString(KEY_NAME, name);
-            return this;
-        }
-
-        /**
-         * Sets the user-visible description of the route.
-         * <p>
-         * The route description describes the kind of destination represented by the route.
-         * It may be a user-supplied string, a model number or brand of device.
-         * </p>
-         */
-        public Builder setDescription(String description) {
-            mBundle.putString(KEY_DESCRIPTION, description);
-            return this;
-        }
-
-        /**
-         * Sets the URI of the icon representing this route.
-         * <p>
-         * This icon will be used in picker UIs if available.
-         * </p><p>
-         * The URI must be 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>
-         */
-        public Builder setIconUri(Uri iconUri) {
-            if (iconUri == null) {
-                throw new IllegalArgumentException("iconUri must not be null");
-            }
-            mBundle.putString(KEY_ICON_URI, iconUri.toString());
-            return this;
-        }
-
-        /**
-         * Sets whether the route is enabled.
-         * <p>
-         * Disabled routes represent routes that a route provider knows about, such as paired
-         * Wifi Display receivers, but that are not currently available for use.
-         * </p>
-         */
-        public Builder setEnabled(boolean enabled) {
-            mBundle.putBoolean(KEY_ENABLED, enabled);
-            return this;
-        }
-
-        /**
-         * Sets whether the route is in the process of connecting and is not yet
-         * ready for use.
-         * @deprecated Use {@link #setConnectionState} instead.
-         */
-        @Deprecated
-        public Builder setConnecting(boolean connecting) {
-            mBundle.putBoolean(KEY_CONNECTING, connecting);
-            return this;
-        }
-
-        /**
-         * Sets the route's connection state.
-         *
-         * @param connectionState The connection state of the route:
-         * {@link MediaRouter.RouteInfo#CONNECTION_STATE_DISCONNECTED},
-         * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTING}, or
-         * {@link MediaRouter.RouteInfo#CONNECTION_STATE_CONNECTED}.
-         */
-        public Builder setConnectionState(int connectionState) {
-            mBundle.putInt(KEY_CONNECTION_STATE, connectionState);
-            return this;
-        }
-
-        /**
-         * Sets whether the route can be disconnected without stopping playback.
-         */
-        public Builder setCanDisconnect(boolean canDisconnect) {
-            mBundle.putBoolean(KEY_CAN_DISCONNECT, canDisconnect);
-            return this;
-        }
-
-        /**
-         * Sets an intent sender for launching the settings activity for this
-         * route.
-         */
-        public Builder setSettingsActivity(IntentSender is) {
-            mBundle.putParcelable(KEY_SETTINGS_INTENT, is);
-            return this;
-        }
-
-        /**
-         * Adds a {@link MediaControlIntent media control intent} filter for the route.
-         */
-        public Builder addControlFilter(IntentFilter filter) {
-            if (filter == null) {
-                throw new IllegalArgumentException("filter must not be null");
-            }
-
-            if (mControlFilters == null) {
-                mControlFilters = new ArrayList<IntentFilter>();
-            }
-            if (!mControlFilters.contains(filter)) {
-                mControlFilters.add(filter);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a list of {@link MediaControlIntent media control intent} filters for the route.
-         */
-        public Builder addControlFilters(Collection<IntentFilter> filters) {
-            if (filters == null) {
-                throw new IllegalArgumentException("filters must not be null");
-            }
-
-            if (!filters.isEmpty()) {
-                for (IntentFilter filter : filters) {
-                    addControlFilter(filter);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Sets the route's playback type.
-         *
-         * @param playbackType The playback type of the route:
-         * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_LOCAL} or
-         * {@link MediaRouter.RouteInfo#PLAYBACK_TYPE_REMOTE}.
-         */
-        public Builder setPlaybackType(int playbackType) {
-            mBundle.putInt(KEY_PLAYBACK_TYPE, playbackType);
-            return this;
-        }
-
-        /**
-         * Sets the route's playback stream.
-         */
-        public Builder setPlaybackStream(int playbackStream) {
-            mBundle.putInt(KEY_PLAYBACK_STREAM, playbackStream);
-            return this;
-        }
-
-        /**
-         * Sets the route's receiver device type.
-         *
-         * @param deviceType The receive device type of the route:
-         * {@link MediaRouter.RouteInfo#DEVICE_TYPE_TV} or
-         * {@link MediaRouter.RouteInfo#DEVICE_TYPE_SPEAKER}.
-         */
-        public Builder setDeviceType(int deviceType) {
-            mBundle.putInt(KEY_DEVICE_TYPE, deviceType);
-            return this;
-        }
-
-        /**
-         * Sets the route's current volume, or 0 if unknown.
-         */
-        public Builder setVolume(int volume) {
-            mBundle.putInt(KEY_VOLUME, volume);
-            return this;
-        }
-
-        /**
-         * Sets the route's maximum volume, or 0 if unknown.
-         */
-        public Builder setVolumeMax(int volumeMax) {
-            mBundle.putInt(KEY_VOLUME_MAX, volumeMax);
-            return this;
-        }
-
-        /**
-         * Sets the route's volume handling.
-         *
-         * @param volumeHandling how volume is handled on the route:
-         * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_FIXED} or
-         * {@link MediaRouter.RouteInfo#PLAYBACK_VOLUME_VARIABLE}.
-         */
-        public Builder setVolumeHandling(int volumeHandling) {
-            mBundle.putInt(KEY_VOLUME_HANDLING, volumeHandling);
-            return this;
-        }
-
-        /**
-         * Sets the route's presentation display id, or -1 if none.
-         */
-        public Builder setPresentationDisplayId(int presentationDisplayId) {
-            mBundle.putInt(KEY_PRESENTATION_DISPLAY_ID, presentationDisplayId);
-            return this;
-        }
-
-        /**
-         * Sets a bundle of extras for this route descriptor.
-         * The extras will be ignored by the media router but they may be used
-         * by applications.
-         */
-        public Builder setExtras(Bundle extras) {
-            mBundle.putBundle(KEY_EXTRAS, extras);
-            return this;
-        }
-
-        /**
-         * Sets the route's minimum client version.
-         * A router whose version is lower than this will not be able to connect to this route.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public Builder setMinClientVersion(int minVersion) {
-            mBundle.putInt(KEY_MIN_CLIENT_VERSION, minVersion);
-            return this;
-        }
-
-        /**
-         * Sets the route's maximum client version.
-         * A router whose version is higher than this will not be able to connect to this route.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public Builder setMaxClientVersion(int maxVersion) {
-            mBundle.putInt(KEY_MAX_CLIENT_VERSION, maxVersion);
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaRouteDescriptor media route descriptor}.
-         */
-        public MediaRouteDescriptor build() {
-            if (mControlFilters != null) {
-                mBundle.putParcelableArrayList(KEY_CONTROL_FILTERS, mControlFilters);
-            }
-            if (mGroupMemberIds != null) {
-                mBundle.putStringArrayList(KEY_GROUP_MEMBER_IDS, mGroupMemberIds);
-            }
-            return new MediaRouteDescriptor(mBundle, mControlFilters);
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouteDiscoveryRequest.java b/android/support/v7/media/MediaRouteDiscoveryRequest.java
deleted file mode 100644
index c5fd3c3..0000000
--- a/android/support/v7/media/MediaRouteDiscoveryRequest.java
+++ /dev/null
@@ -1,132 +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.v7.media;
-
-import android.os.Bundle;
-
-/**
- * Describes the kinds of routes that the media router would like to discover
- * and whether to perform active scanning.
- * <p>
- * This object is immutable once created.
- * </p>
- */
-public final class MediaRouteDiscoveryRequest {
-    private static final String KEY_SELECTOR = "selector";
-    private static final String KEY_ACTIVE_SCAN = "activeScan";
-
-    private final Bundle mBundle;
-    private MediaRouteSelector mSelector;
-
-    /**
-     * Creates a media route discovery request.
-     *
-     * @param selector The route selector that specifies the kinds of routes to discover.
-     * @param activeScan True if active scanning should be performed.
-     */
-    public MediaRouteDiscoveryRequest(MediaRouteSelector selector, boolean activeScan) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-
-        mBundle = new Bundle();
-        mSelector = selector;
-        mBundle.putBundle(KEY_SELECTOR, selector.asBundle());
-        mBundle.putBoolean(KEY_ACTIVE_SCAN, activeScan);
-    }
-
-    private MediaRouteDiscoveryRequest(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    /**
-     * Gets the route selector that specifies the kinds of routes to discover.
-     */
-    public MediaRouteSelector getSelector() {
-        ensureSelector();
-        return mSelector;
-    }
-
-    private void ensureSelector() {
-        if (mSelector == null) {
-            mSelector = MediaRouteSelector.fromBundle(mBundle.getBundle(KEY_SELECTOR));
-            if (mSelector == null) {
-                mSelector = MediaRouteSelector.EMPTY;
-            }
-        }
-    }
-
-    /**
-     * Returns true if active scanning should be performed.
-     *
-     * @see MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN
-     */
-    public boolean isActiveScan() {
-        return mBundle.getBoolean(KEY_ACTIVE_SCAN);
-    }
-
-    /**
-     * Returns true if the discovery request has all of the required fields.
-     */
-    public boolean isValid() {
-        ensureSelector();
-        return mSelector.isValid();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof MediaRouteDiscoveryRequest) {
-            MediaRouteDiscoveryRequest other = (MediaRouteDiscoveryRequest)o;
-            return getSelector().equals(other.getSelector())
-                    && isActiveScan() == other.isActiveScan();
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return getSelector().hashCode() ^ (isActiveScan() ? 1 : 0);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("DiscoveryRequest{ selector=").append(getSelector());
-        result.append(", activeScan=").append(isActiveScan());
-        result.append(", isValid=").append(isValid());
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteDiscoveryRequest fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaRouteDiscoveryRequest(bundle) : null;
-    }
-}
diff --git a/android/support/v7/media/MediaRouteProvider.java b/android/support/v7/media/MediaRouteProvider.java
deleted file mode 100644
index 99bcf21..0000000
--- a/android/support/v7/media/MediaRouteProvider.java
+++ /dev/null
@@ -1,449 +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.v7.media;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-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.v4.util.ObjectsCompat;
-import android.support.v7.media.MediaRouter.ControlRequestCallback;
-
-/**
- * Media route providers are used to publish additional media routes for
- * use within an application.  Media route providers may also be declared
- * as a service to publish additional media routes to all applications
- * in the system.
- * <p>
- * The purpose of a media route provider is to discover media routes that satisfy
- * the criteria specified by the current {@link MediaRouteDiscoveryRequest} and publish a
- * {@link MediaRouteProviderDescriptor} with information about each route by calling
- * {@link #setDescriptor} to notify the currently registered {@link Callback}.
- * </p><p>
- * The provider should watch for changes to the discovery request by implementing
- * {@link #onDiscoveryRequestChanged} and updating the set of routes that it is
- * attempting to discover.  It should also handle route control requests such
- * as volume changes or {@link MediaControlIntent media control intents}
- * by implementing {@link #onCreateRouteController} to return a {@link RouteController}
- * for a particular route.
- * </p><p>
- * A media route provider may be used privately within the scope of a single
- * application process by calling {@link MediaRouter#addProvider MediaRouter.addProvider}
- * to add it to the local {@link MediaRouter}.  A media route provider may also be made
- * available globally to all applications by registering a {@link MediaRouteProviderService}
- * in the provider's manifest.  When the media route provider is registered
- * as a service, all applications that use the media router API will be able to
- * discover and used the provider's routes without having to install anything else.
- * </p><p>
- * This object must only be accessed on the main thread.
- * </p>
- */
-public abstract class MediaRouteProvider {
-    static final int MSG_DELIVER_DESCRIPTOR_CHANGED = 1;
-    static final int MSG_DELIVER_DISCOVERY_REQUEST_CHANGED = 2;
-
-    private final Context mContext;
-    private final ProviderMetadata mMetadata;
-    private final ProviderHandler mHandler = new ProviderHandler();
-
-    private Callback mCallback;
-
-    private MediaRouteDiscoveryRequest mDiscoveryRequest;
-    private boolean mPendingDiscoveryRequestChange;
-
-    private MediaRouteProviderDescriptor mDescriptor;
-    private boolean mPendingDescriptorChange;
-
-    /**
-     * Creates a media route provider.
-     *
-     * @param context The context.
-     */
-    public MediaRouteProvider(@NonNull Context context) {
-        this(context, null);
-    }
-
-    MediaRouteProvider(Context context, ProviderMetadata metadata) {
-        if (context == null) {
-            throw new IllegalArgumentException("context must not be null");
-        }
-
-        mContext = context;
-        if (metadata == null) {
-            mMetadata = new ProviderMetadata(new ComponentName(context, getClass()));
-        } else {
-            mMetadata = metadata;
-        }
-    }
-
-    /**
-     * Gets the context of the media route provider.
-     */
-    public final Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Gets the provider's handler which is associated with the main thread.
-     */
-    public final Handler getHandler() {
-        return mHandler;
-    }
-
-    /**
-     * Gets some metadata about the provider's implementation.
-     */
-    public final ProviderMetadata getMetadata() {
-        return mMetadata;
-    }
-
-    /**
-     * Sets a callback to invoke when the provider's descriptor changes.
-     *
-     * @param callback The callback to use, or null if none.
-     */
-    public final void setCallback(@Nullable Callback callback) {
-        MediaRouter.checkCallingThread();
-        mCallback = callback;
-    }
-
-    /**
-     * Gets the current discovery request which informs the provider about the
-     * kinds of routes to discover and whether to perform active scanning.
-     *
-     * @return The current discovery request, or null if no discovery is needed at this time.
-     *
-     * @see #onDiscoveryRequestChanged
-     */
-    @Nullable
-    public final MediaRouteDiscoveryRequest getDiscoveryRequest() {
-        return mDiscoveryRequest;
-    }
-
-    /**
-     * Sets a discovery request to inform the provider about the kinds of
-     * routes that its clients would like to discover and whether to perform active scanning.
-     *
-     * @param request The discovery request, or null if no discovery is needed at this time.
-     *
-     * @see #onDiscoveryRequestChanged
-     */
-    public final void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-        MediaRouter.checkCallingThread();
-
-        if (ObjectsCompat.equals(mDiscoveryRequest, request)) {
-            return;
-        }
-
-        mDiscoveryRequest = request;
-        if (!mPendingDiscoveryRequestChange) {
-            mPendingDiscoveryRequestChange = true;
-            mHandler.sendEmptyMessage(MSG_DELIVER_DISCOVERY_REQUEST_CHANGED);
-        }
-    }
-
-    void deliverDiscoveryRequestChanged() {
-        mPendingDiscoveryRequestChange = false;
-        onDiscoveryRequestChanged(mDiscoveryRequest);
-    }
-
-    /**
-     * Called by the media router when the {@link MediaRouteDiscoveryRequest discovery request}
-     * has changed.
-     * <p>
-     * Whenever an applications calls {@link MediaRouter#addCallback} to register
-     * a callback, it also provides a selector to specify the kinds of routes that
-     * it is interested in.  The media router combines all of these selectors together
-     * to generate a {@link MediaRouteDiscoveryRequest} and notifies each provider when a change
-     * occurs by calling {@link #setDiscoveryRequest} which posts a message to invoke
-     * this method asynchronously.
-     * </p><p>
-     * The provider should examine the {@link MediaControlIntent media control categories}
-     * in the discovery request's {@link MediaRouteSelector selector} to determine what
-     * kinds of routes it should try to discover and whether it should perform active
-     * or passive scans.  In many cases, the provider may be able to save power by
-     * determining that the selector does not contain any categories that it supports
-     * and it can therefore avoid performing any scans at all.
-     * </p>
-     *
-     * @param request The new discovery request, or null if no discovery is needed at this time.
-     *
-     * @see MediaRouter#addCallback
-     */
-    public void onDiscoveryRequestChanged(@Nullable MediaRouteDiscoveryRequest request) {
-    }
-
-    /**
-     * Gets the provider's descriptor.
-     * <p>
-     * The descriptor describes the state of the media route provider and
-     * the routes that it publishes.  Watch for changes to the descriptor
-     * by registering a {@link Callback callback} with {@link #setCallback}.
-     * </p>
-     *
-     * @return The media route provider descriptor, or null if none.
-     *
-     * @see Callback#onDescriptorChanged
-     */
-    @Nullable
-    public final MediaRouteProviderDescriptor getDescriptor() {
-        return mDescriptor;
-    }
-
-    /**
-     * Sets the provider's descriptor.
-     * <p>
-     * The provider must call this method to notify the currently registered
-     * {@link Callback callback} about the change to the provider's descriptor.
-     * </p>
-     *
-     * @param descriptor The updated route provider descriptor, or null if none.
-     *
-     * @see Callback#onDescriptorChanged
-     */
-    public final void setDescriptor(@Nullable MediaRouteProviderDescriptor descriptor) {
-        MediaRouter.checkCallingThread();
-
-        if (mDescriptor != descriptor) {
-            mDescriptor = descriptor;
-            if (!mPendingDescriptorChange) {
-                mPendingDescriptorChange = true;
-                mHandler.sendEmptyMessage(MSG_DELIVER_DESCRIPTOR_CHANGED);
-            }
-        }
-    }
-
-    void deliverDescriptorChanged() {
-        mPendingDescriptorChange = false;
-
-        if (mCallback != null) {
-            mCallback.onDescriptorChanged(this, mDescriptor);
-        }
-    }
-
-    /**
-     * Called by the media router to obtain a route controller for a particular route.
-     * <p>
-     * The media router will invoke the {@link RouteController#onRelease} method of the route
-     * controller when it is no longer needed to allow it to free its resources.
-     * </p>
-     *
-     * @param routeId The unique id of the route.
-     * @return The route controller.  Returns null if there is no such route or if the route
-     * cannot be controlled using the route controller interface.
-     */
-    @Nullable
-    public RouteController onCreateRouteController(@NonNull String routeId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        return null;
-    }
-
-    /**
-     * Called by the media router to obtain a route controller for a particular route which is a
-     * member of {@link MediaRouter.RouteGroup}.
-     * <p>
-     * The media router will invoke the {@link RouteController#onRelease} method of the route
-     * controller when it is no longer needed to allow it to free its resources.
-     * </p>
-     *
-     * @param routeId The unique id of the member route.
-     * @param routeGroupId The unique id of the route group.
-     * @return The route controller.  Returns null if there is no such route or if the route
-     * cannot be controlled using the route controller interface.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    public RouteController onCreateRouteController(@NonNull String routeId,
-            @NonNull String routeGroupId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        if (routeGroupId == null) {
-            throw new IllegalArgumentException("routeGroupId cannot be null");
-        }
-        return onCreateRouteController(routeId);
-    }
-
-    /**
-     * Describes properties of the route provider's implementation.
-     * <p>
-     * This object is immutable once created.
-     * </p>
-     */
-    public static final class ProviderMetadata {
-        private final ComponentName mComponentName;
-
-        ProviderMetadata(ComponentName componentName) {
-            if (componentName == null) {
-                throw new IllegalArgumentException("componentName must not be null");
-            }
-            mComponentName = componentName;
-        }
-
-        /**
-         * Gets the provider's package name.
-         */
-        public String getPackageName() {
-            return mComponentName.getPackageName();
-        }
-
-        /**
-         * Gets the provider's component name.
-         */
-        public ComponentName getComponentName() {
-            return mComponentName;
-        }
-
-        @Override
-        public String toString() {
-            return "ProviderMetadata{ componentName="
-                    + mComponentName.flattenToShortString() + " }";
-        }
-    }
-
-    /**
-     * Provides control over a particular route.
-     * <p>
-     * The media router obtains a route controller for a route whenever it needs
-     * to control a route.  When a route is selected, the media router invokes
-     * the {@link #onSelect} method of its route controller.  While selected,
-     * the media router may call other methods of the route controller to
-     * request that it perform certain actions to the route.  When a route is
-     * unselected, the media router invokes the {@link #onUnselect} method of its
-     * route controller.  When the media route no longer needs the route controller
-     * it will invoke the {@link #onRelease} method to allow the route controller
-     * to free its resources.
-     * </p><p>
-     * There may be multiple route controllers simultaneously active for the
-     * same route.  Each route controller will be released separately.
-     * </p><p>
-     * All operations on the route controller are asynchronous and
-     * results are communicated via callbacks.
-     * </p>
-     */
-    public static abstract class RouteController {
-        /**
-         * Releases the route controller, allowing it to free its resources.
-         */
-        public void onRelease() {
-        }
-
-        /**
-         * Selects the route.
-         */
-        public void onSelect() {
-        }
-
-        /**
-         * Unselects the route.
-         */
-        public void onUnselect() {
-        }
-
-        /**
-         * Unselects the route and provides a reason. The default implementation
-         * calls {@link #onUnselect()}.
-         * <p>
-         * The reason provided will be one of the following:
-         * <ul>
-         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
-         * </ul>
-         *
-         * @param reason The reason for unselecting the route.
-         */
-        public void onUnselect(int reason) {
-            onUnselect();
-        }
-
-        /**
-         * Requests to set the volume of the route.
-         *
-         * @param volume The new volume value between 0 and {@link MediaRouteDescriptor#getVolumeMax}.
-         */
-        public void onSetVolume(int volume) {
-        }
-
-        /**
-         * Requests an incremental volume update for the route.
-         *
-         * @param delta The delta to add to the current volume.
-         */
-        public void onUpdateVolume(int delta) {
-        }
-
-        /**
-         * Performs a {@link MediaControlIntent media control} request
-         * asynchronously on behalf of the route.
-         *
-         * @param intent A {@link MediaControlIntent media control intent}.
-         * @param callback A {@link ControlRequestCallback} to invoke with the result
-         * of the request, or null if no result is required.
-         * @return True if the controller intends to handle the request and will
-         * invoke the callback when finished.  False if the controller will not
-         * handle the request and will not invoke the callback.
-         *
-         * @see MediaControlIntent
-         */
-        public boolean onControlRequest(Intent intent, @Nullable ControlRequestCallback callback) {
-            return false;
-        }
-    }
-
-    /**
-     * Callback which is invoked when route information becomes available or changes.
-     */
-    public static abstract class Callback {
-        /**
-         * Called when information about a route provider and its routes changes.
-         *
-         * @param provider The media route provider that changed, never null.
-         * @param descriptor The new media route provider descriptor, or null if none.
-         */
-        public void onDescriptorChanged(@NonNull MediaRouteProvider provider,
-                @Nullable MediaRouteProviderDescriptor descriptor) {
-        }
-    }
-
-    private final class ProviderHandler extends Handler {
-        ProviderHandler() {
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_DELIVER_DESCRIPTOR_CHANGED:
-                    deliverDescriptorChanged();
-                    break;
-                case MSG_DELIVER_DISCOVERY_REQUEST_CHANGED:
-                    deliverDiscoveryRequestChanged();
-                    break;
-            }
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouteProviderDescriptor.java b/android/support/v7/media/MediaRouteProviderDescriptor.java
deleted file mode 100644
index ac3ae5a..0000000
--- a/android/support/v7/media/MediaRouteProviderDescriptor.java
+++ /dev/null
@@ -1,208 +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.v7.media;
-
-import android.os.Bundle;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Describes the state of a media route provider and the routes that it publishes.
- * <p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaRouteProviderDescriptor {
-    private static final String KEY_ROUTES = "routes";
-
-    private final Bundle mBundle;
-    private List<MediaRouteDescriptor> mRoutes;
-
-    private MediaRouteProviderDescriptor(Bundle bundle, List<MediaRouteDescriptor> routes) {
-        mBundle = bundle;
-        mRoutes = routes;
-    }
-
-    /**
-     * Gets the list of all routes that this provider has published.
-     */
-    public List<MediaRouteDescriptor> getRoutes() {
-        ensureRoutes();
-        return mRoutes;
-    }
-
-    private void ensureRoutes() {
-        if (mRoutes == null) {
-            ArrayList<Bundle> routeBundles = mBundle.<Bundle>getParcelableArrayList(KEY_ROUTES);
-            if (routeBundles == null || routeBundles.isEmpty()) {
-                mRoutes = Collections.<MediaRouteDescriptor>emptyList();
-            } else {
-                final int count = routeBundles.size();
-                mRoutes = new ArrayList<MediaRouteDescriptor>(count);
-                for (int i = 0; i < count; i++) {
-                    mRoutes.add(MediaRouteDescriptor.fromBundle(routeBundles.get(i)));
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns true if the route provider descriptor and all of the routes that
-     * it contains have all of the required fields.
-     * <p>
-     * This verification is deep.  If the provider descriptor is known to be
-     * valid then it is not necessary to call {@link #isValid} on each of its routes.
-     * </p>
-     */
-    public boolean isValid() {
-        ensureRoutes();
-        final int routeCount = mRoutes.size();
-        for (int i = 0; i < routeCount; i++) {
-            MediaRouteDescriptor route = mRoutes.get(i);
-            if (route == null || !route.isValid()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaRouteProviderDescriptor{ ");
-        result.append("routes=").append(
-                Arrays.toString(getRoutes().toArray()));
-        result.append(", isValid=").append(isValid());
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteProviderDescriptor fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaRouteProviderDescriptor(bundle, null) : null;
-    }
-
-    /**
-     * Builder for {@link MediaRouteProviderDescriptor media route provider descriptors}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-        private ArrayList<MediaRouteDescriptor> mRoutes;
-
-        /**
-         * Creates an empty media route provider descriptor builder.
-         */
-        public Builder() {
-            mBundle = new Bundle();
-        }
-
-        /**
-         * Creates a media route provider descriptor builder whose initial contents are
-         * copied from an existing descriptor.
-         */
-        public Builder(MediaRouteProviderDescriptor descriptor) {
-            if (descriptor == null) {
-                throw new IllegalArgumentException("descriptor must not be null");
-            }
-
-            mBundle = new Bundle(descriptor.mBundle);
-
-            descriptor.ensureRoutes();
-            if (!descriptor.mRoutes.isEmpty()) {
-                mRoutes = new ArrayList<MediaRouteDescriptor>(descriptor.mRoutes);
-            }
-        }
-
-        /**
-         * Adds a route.
-         */
-        public Builder addRoute(MediaRouteDescriptor route) {
-            if (route == null) {
-                throw new IllegalArgumentException("route must not be null");
-            }
-
-            if (mRoutes == null) {
-                mRoutes = new ArrayList<MediaRouteDescriptor>();
-            } else if (mRoutes.contains(route)) {
-                throw new IllegalArgumentException("route descriptor already added");
-            }
-            mRoutes.add(route);
-            return this;
-        }
-
-        /**
-         * Adds a list of routes.
-         */
-        public Builder addRoutes(Collection<MediaRouteDescriptor> routes) {
-            if (routes == null) {
-                throw new IllegalArgumentException("routes must not be null");
-            }
-
-            if (!routes.isEmpty()) {
-                for (MediaRouteDescriptor route : routes) {
-                    addRoute(route);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * 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() {
-            if (mRoutes != null) {
-                final int count = mRoutes.size();
-                ArrayList<Bundle> routeBundles = new ArrayList<Bundle>(count);
-                for (int i = 0; i < count; i++) {
-                    routeBundles.add(mRoutes.get(i).asBundle());
-                }
-                mBundle.putParcelableArrayList(KEY_ROUTES, routeBundles);
-            }
-            return new MediaRouteProviderDescriptor(mBundle, mRoutes);
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/media/MediaRouteProviderProtocol.java b/android/support/v7/media/MediaRouteProviderProtocol.java
deleted file mode 100644
index 3c44b6b..0000000
--- a/android/support/v7/media/MediaRouteProviderProtocol.java
+++ /dev/null
@@ -1,230 +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.v7.media;
-
-import android.content.Intent;
-import android.os.Messenger;
-
-/**
- * Defines the communication protocol for media route provider services.
- */
-abstract class MediaRouteProviderProtocol {
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     * Put this in your manifest.
-     */
-    public static final String SERVICE_INTERFACE =
-            "android.media.MediaRouteProviderService";
-
-    /*
-     * Messages sent from the client to the service.
-     * DO NOT RENUMBER THESE!
-     */
-
-    /** (client v1)
-     * Register client.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : client version
-     */
-    public static final int CLIENT_MSG_REGISTER = 1;
-
-    /** (client v1)
-     * Unregister client.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     */
-    public static final int CLIENT_MSG_UNREGISTER = 2;
-
-    /** (client v1)
-     * Create route controller.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - CLIENT_DATA_ROUTE_ID : route id string
-     */
-    public static final int CLIENT_MSG_CREATE_ROUTE_CONTROLLER = 3;
-
-    /** (client v1)
-     * Release route controller.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     */
-    public static final int CLIENT_MSG_RELEASE_ROUTE_CONTROLLER = 4;
-
-    /** (client v1)
-     * Select route.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     */
-    public static final int CLIENT_MSG_SELECT_ROUTE = 5;
-
-    /** (client v1)
-     * Unselect route.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     */
-    public static final int CLIENT_MSG_UNSELECT_ROUTE = 6;
-
-    /** (client v1)
-     * Set route volume.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - CLIENT_DATA_VOLUME : volume integer
-     */
-    public static final int CLIENT_MSG_SET_ROUTE_VOLUME = 7;
-
-    /** (client v1)
-     * Update route volume.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - CLIENT_DATA_VOLUME : volume delta integer
-     */
-    public static final int CLIENT_MSG_UPDATE_ROUTE_VOLUME = 8;
-
-    /** (client v1)
-     * Route control request.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - arg2    : route controller id
-     * - obj     : media control intent
-     */
-    public static final int CLIENT_MSG_ROUTE_CONTROL_REQUEST = 9;
-
-    /** (client v1)
-     * Sets the discovery request.
-     * - replyTo : client messenger
-     * - arg1    : request id
-     * - obj     : discovery request bundle, or null if none
-     */
-    public static final int CLIENT_MSG_SET_DISCOVERY_REQUEST = 10;
-
-    public static final String CLIENT_DATA_ROUTE_ID = "routeId";
-    public static final String CLIENT_DATA_ROUTE_LIBRARY_GROUP = "routeGroupId";
-    public static final String CLIENT_DATA_VOLUME = "volume";
-    public static final String CLIENT_DATA_UNSELECT_REASON = "unselectReason";
-
-    /*
-     * Messages sent from the service to the client.
-     * DO NOT RENUMBER THESE!
-     */
-
-    /** (service v1)
-     * Generic failure sent in response to any unrecognized or malformed request.
-     * - arg1    : request id
-     */
-    public static final int SERVICE_MSG_GENERIC_FAILURE = 0;
-
-    /** (service v1)
-     * Generic failure sent in response to a successful message.
-     * - arg1    : request id
-     */
-    public static final int SERVICE_MSG_GENERIC_SUCCESS = 1;
-
-    /** (service v1)
-     * Registration succeeded.
-     * - arg1    : request id
-     * - arg2    : server version
-     * - obj     : route provider descriptor bundle, or null
-     */
-    public static final int SERVICE_MSG_REGISTERED = 2;
-
-    /** (service v1)
-     * Route control request success result.
-     * - arg1    : request id
-     * - obj     : result data bundle, or null
-     */
-    public static final int SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED = 3;
-
-    /** (service v1)
-     * Route control request failure result.
-     * - arg1    : request id
-     * - obj     : result data bundle, or null
-     * - SERVICE_DATA_ERROR: error message
-     */
-    public static final int SERVICE_MSG_CONTROL_REQUEST_FAILED = 4;
-
-    /** (service v1)
-     * Route provider descriptor changed.  (unsolicited event)
-     * - arg1    : reserved (0)
-     * - obj     : route provider descriptor bundle, or null
-     */
-    public static final int SERVICE_MSG_DESCRIPTOR_CHANGED = 5;
-
-    public static final String SERVICE_DATA_ERROR = "error";
-
-    /*
-     * Recognized client version numbers.  (Reserved for future use.)
-     * DO NOT RENUMBER THESE!
-     */
-
-    /**
-     * The client version used from the beginning.
-     */
-    public static final int CLIENT_VERSION_1 = 1;
-
-    /**
-     * The client version used from support library v24.1.0.
-     */
-    public static final int CLIENT_VERSION_2 = 2;
-
-    /**
-     * The current client version.
-     */
-    public static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_2;
-
-    /*
-     * Recognized server version numbers.  (Reserved for future use.)
-     * DO NOT RENUMBER THESE!
-     */
-
-    /**
-     * The service version used from the beginning.
-     */
-    public static final int SERVICE_VERSION_1 = 1;
-
-    /**
-     * The current service version.
-     */
-    public static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_1;
-
-    static final int CLIENT_VERSION_START = CLIENT_VERSION_1;
-
-    /**
-     * Returns true if the messenger object is valid.
-     * <p>
-     * The messenger constructor and unparceling code does not check whether the
-     * provided IBinder is a valid IMessenger object.  As a result, it's possible
-     * for a peer to send an invalid IBinder that will result in crashes downstream.
-     * This method checks that the messenger is in a valid state.
-     * </p>
-     */
-    public static boolean isValidRemoteMessenger(Messenger messenger) {
-        try {
-            return messenger != null && messenger.getBinder() != null;
-        } catch (NullPointerException ex) {
-            // If the messenger was constructed with a binder interface other than
-            // IMessenger then the call to getBinder() will crash with an NPE.
-            return false;
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouteProviderService.java b/android/support/v7/media/MediaRouteProviderService.java
deleted file mode 100644
index faa211d..0000000
--- a/android/support/v7/media/MediaRouteProviderService.java
+++ /dev/null
@@ -1,749 +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.v7.media;
-
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_LIBRARY_GROUP;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_UNSELECT_REASON;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_CREATE_ROUTE_CONTROLLER;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_ROUTE_CONTROL_REQUEST;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_SET_DISCOVERY_REQUEST;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_SET_ROUTE_VOLUME;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_UNSELECT_ROUTE;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_UPDATE_ROUTE_VOLUME;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_VERSION_1;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_FAILED;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_DESCRIPTOR_CHANGED;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_GENERIC_FAILURE;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_GENERIC_SUCCESS;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_VERSION_CURRENT;
-import static android.support.v7.media.MediaRouteProviderProtocol.isValidRemoteMessenger;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-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;
-
-/**
- * Base class for media route provider services.
- * <p>
- * A media router will bind to media route provider services when a callback is added via
- * {@link MediaRouter#addCallback(MediaRouteSelector, MediaRouter.Callback, int)} with a discovery
- * flag: {@link MediaRouter#CALLBACK_FLAG_REQUEST_DISCOVERY},
- * {@link MediaRouter#CALLBACK_FLAG_FORCE_DISCOVERY}, or
- * {@link MediaRouter#CALLBACK_FLAG_PERFORM_ACTIVE_SCAN}, and will unbind when the callback
- * is removed via {@link MediaRouter#removeCallback(MediaRouter.Callback)}.
- * </p><p>
- * To implement your own media route provider service, extend this class and
- * override the {@link #onCreateMediaRouteProvider} method to return an
- * instance of your {@link MediaRouteProvider}.
- * </p><p>
- * Declare your media route provider service in your application manifest
- * like this:
- * </p>
- * <pre>
- *   &lt;service android:name=".MyMediaRouteProviderService"
- *           android:label="@string/my_media_route_provider_service">
- *       &lt;intent-filter>
- *           &lt;action android:name="android.media.MediaRouteProviderService" />
- *       &lt;/intent-filter>
- *   &lt;/service>
- * </pre>
- */
-public abstract class MediaRouteProviderService extends Service {
-    static final String TAG = "MediaRouteProviderSrv"; // max. 23 chars
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final ArrayList<ClientRecord> mClients = new ArrayList<ClientRecord>();
-    private final ReceiveHandler mReceiveHandler;
-    private final Messenger mReceiveMessenger;
-    final PrivateHandler mPrivateHandler;
-    private final ProviderCallback mProviderCallback;
-
-    MediaRouteProvider mProvider;
-    private MediaRouteDiscoveryRequest mCompositeDiscoveryRequest;
-
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     * Put this in your manifest.
-     */
-    public static final String SERVICE_INTERFACE = MediaRouteProviderProtocol.SERVICE_INTERFACE;
-
-    /*
-     * Private messages used internally.  (Yes, you can renumber these.)
-     */
-
-    static final int PRIVATE_MSG_CLIENT_DIED = 1;
-
-    /**
-     * Creates a media route provider service.
-     */
-    public MediaRouteProviderService() {
-        mReceiveHandler = new ReceiveHandler(this);
-        mReceiveMessenger = new Messenger(mReceiveHandler);
-        mPrivateHandler = new PrivateHandler();
-        mProviderCallback = new ProviderCallback();
-    }
-
-    /**
-     * Called by the system when it is time to create the media route provider.
-     *
-     * @return The media route provider offered by this service, or null if
-     * this service has decided not to offer a media route provider.
-     */
-    public abstract MediaRouteProvider onCreateMediaRouteProvider();
-
-    /**
-     * Gets the media route provider offered by this service.
-     *
-     * @return The media route provider offered by this service, or null if
-     * it has not yet been created.
-     *
-     * @see #onCreateMediaRouteProvider()
-     */
-    public MediaRouteProvider getMediaRouteProvider() {
-        return mProvider;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (intent.getAction().equals(SERVICE_INTERFACE)) {
-            if (mProvider == null) {
-                MediaRouteProvider provider = onCreateMediaRouteProvider();
-                if (provider != null) {
-                    String providerPackage = provider.getMetadata().getPackageName();
-                    if (!providerPackage.equals(getPackageName())) {
-                        throw new IllegalStateException("onCreateMediaRouteProvider() returned "
-                                + "a provider whose package name does not match the package "
-                                + "name of the service.  A media route provider service can "
-                                + "only export its own media route providers.  "
-                                + "Provider package name: " + providerPackage
-                                + ".  Service package name: " + getPackageName() + ".");
-                    }
-                    mProvider = provider;
-                    mProvider.setCallback(mProviderCallback);
-                }
-            }
-            if (mProvider != null) {
-                return mReceiveMessenger.getBinder();
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean onUnbind(Intent intent) {
-        if (mProvider != null) {
-            mProvider.setCallback(null);
-        }
-        return super.onUnbind(intent);
-    }
-
-    boolean onRegisterClient(Messenger messenger, int requestId, int version) {
-        if (version >= CLIENT_VERSION_1) {
-            int index = findClient(messenger);
-            if (index < 0) {
-                ClientRecord client = new ClientRecord(messenger, version);
-                if (client.register()) {
-                    mClients.add(client);
-                    if (DEBUG) {
-                        Log.d(TAG, client + ": Registered, version=" + version);
-                    }
-                    if (requestId != 0) {
-                        MediaRouteProviderDescriptor descriptor = mProvider.getDescriptor();
-                        sendReply(messenger, SERVICE_MSG_REGISTERED,
-                                requestId, SERVICE_VERSION_CURRENT,
-                                createDescriptorBundleForClientVersion(descriptor,
-                                        client.mVersion), null);
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean onUnregisterClient(Messenger messenger, int requestId) {
-        int index = findClient(messenger);
-        if (index >= 0) {
-            ClientRecord client = mClients.remove(index);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Unregistered");
-            }
-            client.dispose();
-            sendGenericSuccess(messenger, requestId);
-            return true;
-        }
-        return false;
-    }
-
-    void onBinderDied(Messenger messenger) {
-        int index = findClient(messenger);
-        if (index >= 0) {
-            ClientRecord client = mClients.remove(index);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Binder died");
-            }
-            client.dispose();
-        }
-    }
-
-    boolean onCreateRouteController(Messenger messenger, int requestId,
-            int controllerId, String routeId, String routeGroupId) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            if (client.createRouteController(routeId, routeGroupId, controllerId)) {
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route controller created, controllerId=" + controllerId
-                            + ", routeId=" + routeId + ", routeGroupId=" + routeGroupId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onReleaseRouteController(Messenger messenger, int requestId,
-            int controllerId) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            if (client.releaseRouteController(controllerId)) {
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route controller released"
-                            + ", controllerId=" + controllerId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onSelectRoute(Messenger messenger, int requestId,
-            int controllerId) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onSelect();
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route selected"
-                            + ", controllerId=" + controllerId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onUnselectRoute(Messenger messenger, int requestId,
-            int controllerId, int reason) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onUnselect(reason);
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route unselected"
-                            + ", controllerId=" + controllerId);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onSetRouteVolume(Messenger messenger, int requestId,
-            int controllerId, int volume) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onSetVolume(volume);
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route volume changed"
-                            + ", controllerId=" + controllerId + ", volume=" + volume);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onUpdateRouteVolume(Messenger messenger, int requestId,
-            int controllerId, int delta) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                controller.onUpdateVolume(delta);
-                if (DEBUG) {
-                    Log.d(TAG, client + ": Route volume updated"
-                            + ", controllerId=" + controllerId + ", delta=" + delta);
-                }
-                sendGenericSuccess(messenger, requestId);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean onRouteControlRequest(final Messenger messenger, final int requestId,
-            final int controllerId, final Intent intent) {
-        final ClientRecord client = getClient(messenger);
-        if (client != null) {
-            MediaRouteProvider.RouteController controller =
-                    client.getRouteController(controllerId);
-            if (controller != null) {
-                MediaRouter.ControlRequestCallback callback = null;
-                if (requestId != 0) {
-                    callback = new MediaRouter.ControlRequestCallback() {
-                        @Override
-                        public void onResult(Bundle data) {
-                            if (DEBUG) {
-                                Log.d(TAG, client + ": Route control request succeeded"
-                                        + ", controllerId=" + controllerId
-                                        + ", intent=" + intent
-                                        + ", data=" + data);
-                            }
-                            if (findClient(messenger) >= 0) {
-                                sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED,
-                                        requestId, 0, data, null);
-                            }
-                        }
-
-                        @Override
-                        public void onError(String error, Bundle data) {
-                            if (DEBUG) {
-                                Log.d(TAG, client + ": Route control request failed"
-                                        + ", controllerId=" + controllerId
-                                        + ", intent=" + intent
-                                        + ", error=" + error + ", data=" + data);
-                            }
-                            if (findClient(messenger) >= 0) {
-                                if (error != null) {
-                                    Bundle bundle = new Bundle();
-                                    bundle.putString(SERVICE_DATA_ERROR, error);
-                                    sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED,
-                                            requestId, 0, data, bundle);
-                                } else {
-                                    sendReply(messenger, SERVICE_MSG_CONTROL_REQUEST_FAILED,
-                                            requestId, 0, data, null);
-                                }
-                            }
-                        }
-                    };
-                }
-                if (controller.onControlRequest(intent, callback)) {
-                    if (DEBUG) {
-                        Log.d(TAG, client + ": Route control request delivered"
-                                + ", controllerId=" + controllerId + ", intent=" + intent);
-                    }
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    boolean onSetDiscoveryRequest(Messenger messenger, int requestId,
-            MediaRouteDiscoveryRequest request) {
-        ClientRecord client = getClient(messenger);
-        if (client != null) {
-            boolean actuallyChanged = client.setDiscoveryRequest(request);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Set discovery request, request=" + request
-                        + ", actuallyChanged=" + actuallyChanged
-                        + ", compositeDiscoveryRequest=" + mCompositeDiscoveryRequest);
-            }
-            sendGenericSuccess(messenger, requestId);
-            return true;
-        }
-        return false;
-    }
-
-    void sendDescriptorChanged(MediaRouteProviderDescriptor descriptor) {
-        final int count = mClients.size();
-        for (int i = 0; i < count; i++) {
-            ClientRecord client = mClients.get(i);
-            sendReply(client.mMessenger, SERVICE_MSG_DESCRIPTOR_CHANGED, 0, 0,
-                    createDescriptorBundleForClientVersion(descriptor, client.mVersion), null);
-            if (DEBUG) {
-                Log.d(TAG, client + ": Sent descriptor change event, descriptor=" + descriptor);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    static Bundle createDescriptorBundleForClientVersion(MediaRouteProviderDescriptor descriptor,
-            int clientVersion) {
-        if (descriptor == null) {
-            return null;
-        }
-        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);
-            }
-        }
-        return builder.build().asBundle();
-    }
-
-    boolean updateCompositeDiscoveryRequest() {
-        MediaRouteDiscoveryRequest composite = null;
-        MediaRouteSelector.Builder selectorBuilder = null;
-        boolean activeScan = false;
-        final int count = mClients.size();
-        for (int i = 0; i < count; i++) {
-            MediaRouteDiscoveryRequest request = mClients.get(i).mDiscoveryRequest;
-            if (request != null
-                    && (!request.getSelector().isEmpty() || request.isActiveScan())) {
-                activeScan |= request.isActiveScan();
-                if (composite == null) {
-                    composite = request;
-                } else {
-                    if (selectorBuilder == null) {
-                        selectorBuilder = new MediaRouteSelector.Builder(composite.getSelector());
-                    }
-                    selectorBuilder.addSelector(request.getSelector());
-                }
-            }
-        }
-        if (selectorBuilder != null) {
-            composite = new MediaRouteDiscoveryRequest(selectorBuilder.build(), activeScan);
-        }
-        if (!ObjectsCompat.equals(mCompositeDiscoveryRequest, composite)) {
-            mCompositeDiscoveryRequest = composite;
-            mProvider.setDiscoveryRequest(composite);
-            return true;
-        }
-        return false;
-    }
-
-    private ClientRecord getClient(Messenger messenger) {
-        int index = findClient(messenger);
-        return index >= 0 ? mClients.get(index) : null;
-    }
-
-    int findClient(Messenger messenger) {
-        final int count = mClients.size();
-        for (int i = 0; i < count; i++) {
-            ClientRecord client = mClients.get(i);
-            if (client.hasMessenger(messenger)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    static void sendGenericFailure(Messenger messenger, int requestId) {
-        if (requestId != 0) {
-            sendReply(messenger, SERVICE_MSG_GENERIC_FAILURE, requestId, 0, null, null);
-        }
-    }
-
-    private static void sendGenericSuccess(Messenger messenger, int requestId) {
-        if (requestId != 0) {
-            sendReply(messenger, SERVICE_MSG_GENERIC_SUCCESS, requestId, 0, null, null);
-        }
-    }
-
-    static void sendReply(Messenger messenger, int what,
-            int requestId, int arg, Object obj, Bundle data) {
-        Message msg = Message.obtain();
-        msg.what = what;
-        msg.arg1 = requestId;
-        msg.arg2 = arg;
-        msg.obj = obj;
-        msg.setData(data);
-        try {
-            messenger.send(msg);
-        } catch (DeadObjectException ex) {
-            // The client died.
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Could not send message to " + getClientId(messenger), ex);
-        }
-    }
-
-    static String getClientId(Messenger messenger) {
-        return "Client connection " + messenger.getBinder().toString();
-    }
-
-    private final class PrivateHandler extends Handler {
-        PrivateHandler() {
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case PRIVATE_MSG_CLIENT_DIED:
-                    onBinderDied((Messenger)msg.obj);
-                    break;
-            }
-        }
-    }
-
-    private final class ProviderCallback extends MediaRouteProvider.Callback {
-        ProviderCallback() {
-        }
-
-        @Override
-        public void onDescriptorChanged(MediaRouteProvider provider,
-                MediaRouteProviderDescriptor descriptor) {
-            sendDescriptorChanged(descriptor);
-        }
-    }
-
-    private final class ClientRecord implements DeathRecipient {
-        public final Messenger mMessenger;
-        public final int mVersion;
-        public MediaRouteDiscoveryRequest mDiscoveryRequest;
-
-        private final SparseArray<MediaRouteProvider.RouteController> mControllers =
-                new SparseArray<MediaRouteProvider.RouteController>();
-
-        public ClientRecord(Messenger messenger, int version) {
-            mMessenger = messenger;
-            mVersion = version;
-        }
-
-        public boolean register() {
-            try {
-                mMessenger.getBinder().linkToDeath(this, 0);
-                return true;
-            } catch (RemoteException ex) {
-                binderDied();
-            }
-            return false;
-        }
-
-        public void dispose() {
-            int count = mControllers.size();
-            for (int i = 0; i < count; i++) {
-                mControllers.valueAt(i).onRelease();
-            }
-            mControllers.clear();
-
-            mMessenger.getBinder().unlinkToDeath(this, 0);
-
-            setDiscoveryRequest(null);
-        }
-
-        public boolean hasMessenger(Messenger other) {
-            return mMessenger.getBinder() == other.getBinder();
-        }
-
-        public boolean createRouteController(String routeId, String routeGroupId,
-                int controllerId) {
-            if (mControllers.indexOfKey(controllerId) < 0) {
-                MediaRouteProvider.RouteController controller = routeGroupId == null
-                        ? mProvider.onCreateRouteController(routeId)
-                        : mProvider.onCreateRouteController(routeId, routeGroupId);
-                if (controller != null) {
-                    mControllers.put(controllerId, controller);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public boolean releaseRouteController(int controllerId) {
-            MediaRouteProvider.RouteController controller = mControllers.get(controllerId);
-            if (controller != null) {
-                mControllers.remove(controllerId);
-                controller.onRelease();
-                return true;
-            }
-            return false;
-        }
-
-        public MediaRouteProvider.RouteController getRouteController(int controllerId) {
-            return mControllers.get(controllerId);
-        }
-
-        public boolean setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-            if (!ObjectsCompat.equals(mDiscoveryRequest, request)) {
-                mDiscoveryRequest = request;
-                return updateCompositeDiscoveryRequest();
-            }
-            return false;
-        }
-
-        // Runs on a binder thread.
-        @Override
-        public void binderDied() {
-            mPrivateHandler.obtainMessage(PRIVATE_MSG_CLIENT_DIED, mMessenger).sendToTarget();
-        }
-
-        @Override
-        public String toString() {
-            return getClientId(mMessenger);
-        }
-    }
-
-    /**
-     * Handler that receives messages from clients.
-     * <p>
-     * This inner class is static and only retains a weak reference to the service
-     * to prevent the service from being leaked in case one of the clients is holding an
-     * active reference to the server's messenger.
-     * </p><p>
-     * This handler should not be used to handle any messages other than those
-     * that come from the client.
-     * </p>
-     */
-    private static final class ReceiveHandler extends Handler {
-        private final WeakReference<MediaRouteProviderService> mServiceRef;
-
-        public ReceiveHandler(MediaRouteProviderService service) {
-            mServiceRef = new WeakReference<MediaRouteProviderService>(service);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final Messenger messenger = msg.replyTo;
-            if (isValidRemoteMessenger(messenger)) {
-                final int what = msg.what;
-                final int requestId = msg.arg1;
-                final int arg = msg.arg2;
-                final Object obj = msg.obj;
-                final Bundle data = msg.peekData();
-                if (!processMessage(what, messenger, requestId, arg, obj, data)) {
-                    if (DEBUG) {
-                        Log.d(TAG, getClientId(messenger) + ": Message failed, what=" + what
-                                + ", requestId=" + requestId + ", arg=" + arg
-                                + ", obj=" + obj + ", data=" + data);
-                    }
-                    sendGenericFailure(messenger, requestId);
-                }
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "Ignoring message without valid reply messenger.");
-                }
-            }
-        }
-
-        private boolean processMessage(int what,
-                Messenger messenger, int requestId, int arg, Object obj, Bundle data) {
-            MediaRouteProviderService service = mServiceRef.get();
-            if (service != null) {
-                switch (what) {
-                    case CLIENT_MSG_REGISTER:
-                        return service.onRegisterClient(messenger, requestId, arg);
-
-                    case CLIENT_MSG_UNREGISTER:
-                        return service.onUnregisterClient(messenger, requestId);
-
-                    case CLIENT_MSG_CREATE_ROUTE_CONTROLLER: {
-                        String routeId = data.getString(CLIENT_DATA_ROUTE_ID);
-                        String routeGroupId = data.getString(CLIENT_DATA_ROUTE_LIBRARY_GROUP);
-                        if (routeId != null) {
-                            return service.onCreateRouteController(
-                                    messenger, requestId, arg, routeId, routeGroupId);
-                        }
-                        break;
-                    }
-
-                    case CLIENT_MSG_RELEASE_ROUTE_CONTROLLER:
-                        return service.onReleaseRouteController(messenger, requestId, arg);
-
-                    case CLIENT_MSG_SELECT_ROUTE:
-                        return service.onSelectRoute(messenger, requestId, arg);
-
-                    case CLIENT_MSG_UNSELECT_ROUTE:
-                        int reason = data == null ?
-                                MediaRouter.UNSELECT_REASON_UNKNOWN
-                                : data.getInt(CLIENT_DATA_UNSELECT_REASON,
-                                        MediaRouter.UNSELECT_REASON_UNKNOWN);
-                        return service.onUnselectRoute(messenger, requestId, arg, reason);
-
-                    case CLIENT_MSG_SET_ROUTE_VOLUME: {
-                        int volume = data.getInt(CLIENT_DATA_VOLUME, -1);
-                        if (volume >= 0) {
-                            return service.onSetRouteVolume(
-                                    messenger, requestId, arg, volume);
-                        }
-                        break;
-                    }
-
-                    case CLIENT_MSG_UPDATE_ROUTE_VOLUME: {
-                        int delta = data.getInt(CLIENT_DATA_VOLUME, 0);
-                        if (delta != 0) {
-                            return service.onUpdateRouteVolume(
-                                    messenger, requestId, arg, delta);
-                        }
-                        break;
-                    }
-
-                    case CLIENT_MSG_ROUTE_CONTROL_REQUEST:
-                        if (obj instanceof Intent) {
-                            return service.onRouteControlRequest(
-                                    messenger, requestId, arg, (Intent)obj);
-                        }
-                        break;
-
-                    case CLIENT_MSG_SET_DISCOVERY_REQUEST: {
-                        if (obj == null || obj instanceof Bundle) {
-                            MediaRouteDiscoveryRequest request =
-                                    MediaRouteDiscoveryRequest.fromBundle((Bundle)obj);
-                            return service.onSetDiscoveryRequest(
-                                    messenger, requestId,
-                                    request != null && request.isValid() ? request : null);
-                        }
-                    }
-                }
-            }
-            return false;
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouteSelector.java b/android/support/v7/media/MediaRouteSelector.java
deleted file mode 100644
index 65406da..0000000
--- a/android/support/v7/media/MediaRouteSelector.java
+++ /dev/null
@@ -1,308 +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.v7.media;
-
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Describes the capabilities of routes that applications would like to discover and use.
- * <p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- *
- * <h3>Example</h3>
- * <pre>
- * MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
- *         .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
- *         .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
- *         .build();
- *
- * MediaRouter router = MediaRouter.getInstance(context);
- * router.addCallback(selector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
- * </pre>
- */
-public final class MediaRouteSelector {
-    static final String KEY_CONTROL_CATEGORIES = "controlCategories";
-
-    private final Bundle mBundle;
-    List<String> mControlCategories;
-
-    /**
-     * An empty media route selector that will not match any routes.
-     */
-    public static final MediaRouteSelector EMPTY = new MediaRouteSelector(new Bundle(), null);
-
-    MediaRouteSelector(Bundle bundle, List<String> controlCategories) {
-        mBundle = bundle;
-        mControlCategories = controlCategories;
-    }
-
-    /**
-     * Gets the list of {@link MediaControlIntent media control categories} in the selector.
-     *
-     * @return The list of categories.
-     */
-    public List<String> getControlCategories() {
-        ensureControlCategories();
-        return mControlCategories;
-    }
-
-    void ensureControlCategories() {
-        if (mControlCategories == null) {
-            mControlCategories = mBundle.getStringArrayList(KEY_CONTROL_CATEGORIES);
-            if (mControlCategories == null || mControlCategories.isEmpty()) {
-                mControlCategories = Collections.<String>emptyList();
-            }
-        }
-    }
-
-    /**
-     * Returns true if the selector contains the specified category.
-     *
-     * @param category The category to check.
-     * @return True if the category is present.
-     */
-    public boolean hasControlCategory(String category) {
-        if (category != null) {
-            ensureControlCategories();
-            final int categoryCount = mControlCategories.size();
-            for (int i = 0; i < categoryCount; i++) {
-                if (mControlCategories.get(i).equals(category)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the selector matches at least one of the specified control filters.
-     *
-     * @param filters The list of control filters to consider.
-     * @return True if a match is found.
-     */
-    public boolean matchesControlFilters(List<IntentFilter> filters) {
-        if (filters != null) {
-            ensureControlCategories();
-            final int categoryCount = mControlCategories.size();
-            if (categoryCount != 0) {
-                final int filterCount = filters.size();
-                for (int i = 0; i < filterCount; i++) {
-                    final IntentFilter filter = filters.get(i);
-                    if (filter != null) {
-                        for (int j = 0; j < categoryCount; j++) {
-                            if (filter.hasCategory(mControlCategories.get(j))) {
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if this selector contains all of the capabilities described
-     * by the specified selector.
-     *
-     * @param selector The selector to be examined.
-     * @return True if this selector contains all of the capabilities described
-     * by the specified selector.
-     */
-    public boolean contains(MediaRouteSelector selector) {
-        if (selector != null) {
-            ensureControlCategories();
-            selector.ensureControlCategories();
-            return mControlCategories.containsAll(selector.mControlCategories);
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the selector does not specify any capabilities.
-     */
-    public boolean isEmpty() {
-        ensureControlCategories();
-        return mControlCategories.isEmpty();
-    }
-
-    /**
-     * Returns true if the selector has all of the required fields.
-     */
-    public boolean isValid() {
-        ensureControlCategories();
-        if (mControlCategories.contains(null)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof MediaRouteSelector) {
-            MediaRouteSelector other = (MediaRouteSelector)o;
-            ensureControlCategories();
-            other.ensureControlCategories();
-            return mControlCategories.equals(other.mControlCategories);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        ensureControlCategories();
-        return mControlCategories.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaRouteSelector{ ");
-        result.append("controlCategories=").append(
-                Arrays.toString(getControlCategories().toArray()));
-        result.append(" }");
-        return result.toString();
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaRouteSelector fromBundle(@Nullable Bundle bundle) {
-        return bundle != null ? new MediaRouteSelector(bundle, null) : null;
-    }
-
-    /**
-     * Builder for {@link MediaRouteSelector media route selectors}.
-     */
-    public static final class Builder {
-        private ArrayList<String> mControlCategories;
-
-        /**
-         * Creates an empty media route selector builder.
-         */
-        public Builder() {
-        }
-
-        /**
-         * Creates a media route selector descriptor builder whose initial contents are
-         * copied from an existing selector.
-         */
-        public Builder(@NonNull MediaRouteSelector selector) {
-            if (selector == null) {
-                throw new IllegalArgumentException("selector must not be null");
-            }
-
-            selector.ensureControlCategories();
-            if (!selector.mControlCategories.isEmpty()) {
-                mControlCategories = new ArrayList<String>(selector.mControlCategories);
-            }
-        }
-
-        /**
-         * Adds a {@link MediaControlIntent media control category} to the builder.
-         *
-         * @param category The category to add to the set of desired capabilities, such as
-         * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}.
-         * @return The builder instance for chaining.
-         */
-        @NonNull
-        public Builder addControlCategory(@NonNull String category) {
-            if (category == null) {
-                throw new IllegalArgumentException("category must not be null");
-            }
-
-            if (mControlCategories == null) {
-                mControlCategories = new ArrayList<String>();
-            }
-            if (!mControlCategories.contains(category)) {
-                mControlCategories.add(category);
-            }
-            return this;
-        }
-
-        /**
-         * Adds a list of {@link MediaControlIntent media control categories} to the builder.
-         *
-         * @param categories The list categories to add to the set of desired capabilities,
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}.
-         * @return The builder instance for chaining.
-         */
-        @NonNull
-        public Builder addControlCategories(@NonNull Collection<String> categories) {
-            if (categories == null) {
-                throw new IllegalArgumentException("categories must not be null");
-            }
-
-            if (!categories.isEmpty()) {
-                for (String category : categories) {
-                    addControlCategory(category);
-                }
-            }
-            return this;
-        }
-
-        /**
-         * Adds the contents of an existing media route selector to the builder.
-         *
-         * @param selector The media route selector whose contents are to be added.
-         * @return The builder instance for chaining.
-         */
-        @NonNull
-        public Builder addSelector(@NonNull MediaRouteSelector selector) {
-            if (selector == null) {
-                throw new IllegalArgumentException("selector must not be null");
-            }
-
-            addControlCategories(selector.getControlCategories());
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaRouteSelector media route selector}.
-         */
-        @NonNull
-        public MediaRouteSelector build() {
-            if (mControlCategories == null) {
-                return EMPTY;
-            }
-            Bundle bundle = new Bundle();
-            bundle.putStringArrayList(KEY_CONTROL_CATEGORIES, mControlCategories);
-            return new MediaRouteSelector(bundle, mControlCategories);
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/media/MediaRouter.java b/android/support/v7/media/MediaRouter.java
deleted file mode 100644
index cc372ec..0000000
--- a/android/support/v7/media/MediaRouter.java
+++ /dev/null
@@ -1,3001 +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.v7.media;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.ActivityManagerCompat;
-import android.support.v4.hardware.display.DisplayManagerCompat;
-import android.support.v4.media.VolumeProviderCompat;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.util.Pair;
-import android.support.v7.media.MediaRouteProvider.ProviderMetadata;
-import android.support.v7.media.MediaRouteProvider.RouteController;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.Display;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * MediaRouter allows applications to control the routing of media channels
- * and streams from the current device to external speakers and destination devices.
- * <p>
- * A MediaRouter instance is retrieved through {@link #getInstance}.  Applications
- * can query the media router about the currently selected route and its capabilities
- * to determine how to send content to the route's destination.  Applications can
- * also {@link RouteInfo#sendControlRequest send control requests} to the route
- * to ask the route's destination to perform certain remote control functions
- * such as playing media.
- * </p><p>
- * See also {@link MediaRouteProvider} for information on how an application
- * can publish new media routes to the media router.
- * </p><p>
- * The media router API is not thread-safe; all interactions with it must be
- * done from the main thread of the process.
- * </p>
- */
-public final class MediaRouter {
-    static final String TAG = "MediaRouter";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route
-     * was unselected is unknown.
-     */
-    public static final int UNSELECT_REASON_UNKNOWN = 0;
-    /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
-     * the disconnect button to disconnect and keep playing.
-     * <p>
-     *
-     * @see MediaRouteDescriptor#canDisconnectAndKeepPlaying()
-     */
-    public static final int UNSELECT_REASON_DISCONNECTED = 1;
-    /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
-     * the stop casting button.
-     */
-    public static final int UNSELECT_REASON_STOPPED = 2;
-    /**
-     * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected
-     * a different route.
-     */
-    public static final int UNSELECT_REASON_ROUTE_CHANGED = 3;
-
-    // Maintains global media router state for the process.
-    // This field is initialized in MediaRouter.getInstance() before any
-    // MediaRouter objects are instantiated so it is guaranteed to be
-    // valid whenever any instance method is invoked.
-    static GlobalMediaRouter sGlobal;
-
-    // Context-bound state of the media router.
-    final Context mContext;
-    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>();
-
-    @IntDef(flag = true,
-            value = {
-                    CALLBACK_FLAG_PERFORM_ACTIVE_SCAN,
-                    CALLBACK_FLAG_REQUEST_DISCOVERY,
-                    CALLBACK_FLAG_UNFILTERED_EVENTS
-            }
-    )
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface CallbackFlags {}
-
-    /**
-     * Flag for {@link #addCallback}: Actively scan for routes while this callback
-     * is registered.
-     * <p>
-     * When this flag is specified, the media router will actively scan for new
-     * routes.  Certain routes, such as wifi display routes, may not be discoverable
-     * except when actively scanning.  This flag is typically used when the route picker
-     * dialog has been opened by the user to ensure that the route information is
-     * up to date.
-     * </p><p>
-     * Active scanning may consume a significant amount of power and may have intrusive
-     * effects on wireless connectivity.  Therefore it is important that active scanning
-     * only be requested when it is actually needed to satisfy a user request to
-     * discover and select a new route.
-     * </p><p>
-     * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing
-     * active scans is much more expensive than a normal discovery request.
-     * </p>
-     *
-     * @see #CALLBACK_FLAG_REQUEST_DISCOVERY
-     */
-    public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0;
-
-    /**
-     * Flag for {@link #addCallback}: Do not filter route events.
-     * <p>
-     * When this flag is specified, the callback will be invoked for events that affect any
-     * route even if they do not match the callback's filter.
-     * </p>
-     */
-    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
-
-    /**
-     * Flag for {@link #addCallback}: Request passive route discovery while this
-     * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}.
-     * <p>
-     * When this flag is specified, the media router will try to discover routes.
-     * Although route discovery is intended to be efficient, checking for new routes may
-     * result in some network activity and could slowly drain the battery.  Therefore
-     * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when
-     * they are running in the foreground and would like to provide the user with the
-     * option of connecting to new routes.
-     * </p><p>
-     * Applications should typically add a callback using this flag in the
-     * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart}
-     * method and remove it in the {@link android.app.Activity#onStop onStop} method.
-     * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may
-     * also be used for this purpose.
-     * </p><p class="note">
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag
-     * will be ignored.  Refer to
-     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
-     * </p>
-     *
-     * @see android.support.v7.app.MediaRouteDiscoveryFragment
-     */
-    public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2;
-
-    /**
-     * Flag for {@link #addCallback}: Request passive route discovery while this
-     * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}.
-     * <p class="note">
-     * This flag has a significant performance impact on low-RAM devices
-     * since it may cause many media route providers to be started simultaneously.
-     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
-     * performing passive discovery on these devices altogether.  Refer to
-     * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details.
-     * </p>
-     *
-     * @see android.support.v7.app.MediaRouteDiscoveryFragment
-     */
-    public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3;
-
-    /**
-     * Flag for {@link #isRouteAvailable}: Ignore the default route.
-     * <p>
-     * This flag is used to determine whether a matching non-default route is available.
-     * This constraint may be used to decide whether to offer the route chooser dialog
-     * to the user.  There is no point offering the chooser if there are no
-     * non-default choices.
-     * </p>
-     */
-    public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0;
-
-    /**
-     * Flag for {@link #isRouteAvailable}: Require an actual route to be matched.
-     * <p>
-     * If this flag is not set, then {@link #isRouteAvailable} will return true
-     * if it is possible to discover a matching route even if discovery is not in
-     * progress or if no matching route has yet been found.  This feature is used to
-     * save resources by removing the need to perform passive route discovery on
-     * {@link ActivityManager#isLowRamDevice low-RAM devices}.
-     * </p><p>
-     * If this flag is set, then {@link #isRouteAvailable} will only return true if
-     * a matching route has actually been discovered.
-     * </p>
-     */
-    public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1;
-
-    private MediaRouter(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Gets an instance of the media router service associated with the context.
-     * <p>
-     * The application is responsible for holding a strong reference to the returned
-     * {@link MediaRouter} instance, such as by storing the instance in a field of
-     * the {@link android.app.Activity}, to ensure that the media router remains alive
-     * as long as the application is using its features.
-     * </p><p>
-     * In other words, the support library only holds a {@link WeakReference weak reference}
-     * to each media router instance.  When there are no remaining strong references to the
-     * media router instance, all of its callbacks will be removed and route discovery
-     * will no longer be performed on its behalf.
-     * </p>
-     *
-     * @return The media router instance for the context.  The application must hold
-     * a strong reference to this object as long as it is in use.
-     */
-    public static MediaRouter getInstance(@NonNull Context context) {
-        if (context == null) {
-            throw new IllegalArgumentException("context must not be null");
-        }
-        checkCallingThread();
-
-        if (sGlobal == null) {
-            sGlobal = new GlobalMediaRouter(context.getApplicationContext());
-            sGlobal.start();
-        }
-        return sGlobal.getRouter(context);
-    }
-
-    /**
-     * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to
-     * this media router.
-     */
-    public List<RouteInfo> getRoutes() {
-        checkCallingThread();
-        return sGlobal.getRoutes();
-    }
-
-    /**
-     * Gets information about the {@link MediaRouter.ProviderInfo route providers}
-     * currently known to this media router.
-     */
-    public List<ProviderInfo> getProviders() {
-        checkCallingThread();
-        return sGlobal.getProviders();
-    }
-
-    /**
-     * Gets the default route for playing media content on the system.
-     * <p>
-     * The system always provides a default route.
-     * </p>
-     *
-     * @return The default route, which is guaranteed to never be null.
-     */
-    @NonNull
-    public RouteInfo getDefaultRoute() {
-        checkCallingThread();
-        return sGlobal.getDefaultRoute();
-    }
-
-    /**
-     * Gets a bluetooth route for playing media content on the system.
-     *
-     * @return A bluetooth route, if exist, otherwise null.
-     */
-    public RouteInfo getBluetoothRoute() {
-        checkCallingThread();
-        return sGlobal.getBluetoothRoute();
-    }
-
-    /**
-     * Gets the currently selected route.
-     * <p>
-     * The application should examine the route's
-     * {@link RouteInfo#getControlFilters media control intent filters} to assess the
-     * capabilities of the route before attempting to use it.
-     * </p>
-     *
-     * <h3>Example</h3>
-     * <pre>
-     * public boolean playMovie() {
-     *     MediaRouter mediaRouter = MediaRouter.getInstance(context);
-     *     MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute();
-     *
-     *     // First try using the remote playback interface, if supported.
-     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
-     *         // The route supports remote playback.
-     *         // Try to send it the Uri of the movie to play.
-     *         Intent intent = new Intent(MediaControlIntent.ACTION_PLAY);
-     *         intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-     *         intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4");
-     *         if (route.supportsControlRequest(intent)) {
-     *             route.sendControlRequest(intent, null);
-     *             return true; // sent the request to play the movie
-     *         }
-     *     }
-     *
-     *     // If remote playback was not possible, then play locally.
-     *     if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
-     *         // The route supports live video streaming.
-     *         // Prepare to play content locally in a window or in a presentation.
-     *         return playMovieInWindow();
-     *     }
-     *
-     *     // Neither interface is supported, so we can't play the movie to this route.
-     *     return false;
-     * }
-     * </pre>
-     *
-     * @return The selected route, which is guaranteed to never be null.
-     *
-     * @see RouteInfo#getControlFilters
-     * @see RouteInfo#supportsControlCategory
-     * @see RouteInfo#supportsControlRequest
-     */
-    @NonNull
-    public RouteInfo getSelectedRoute() {
-        checkCallingThread();
-        return sGlobal.getSelectedRoute();
-    }
-
-    /**
-     * Returns the selected route if it matches the specified selector, otherwise
-     * selects the default route and returns it. If there is one live audio route
-     * (usually Bluetooth A2DP), it will be selected instead of default route.
-     *
-     * @param selector The selector to match.
-     * @return The previously selected route if it matched the selector, otherwise the
-     * newly selected default route which is guaranteed to never be null.
-     *
-     * @see MediaRouteSelector
-     * @see RouteInfo#matchesSelector
-     */
-    @NonNull
-    public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "updateSelectedRoute: " + selector);
-        }
-        RouteInfo route = sGlobal.getSelectedRoute();
-        if (!route.isDefaultOrBluetooth() && !route.matchesSelector(selector)) {
-            route = sGlobal.chooseFallbackRoute();
-            sGlobal.selectRoute(route);
-        }
-        return route;
-    }
-
-    /**
-     * Selects the specified route.
-     *
-     * @param route The route to select.
-     */
-    public void selectRoute(@NonNull RouteInfo route) {
-        if (route == null) {
-            throw new IllegalArgumentException("route must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "selectRoute: " + route);
-        }
-        sGlobal.selectRoute(route);
-    }
-
-    /**
-     * Unselects the current round and selects the default route instead.
-     * <p>
-     * The reason given must be one of:
-     * <ul>
-     * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-     * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-     * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-     * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
-     * </ul>
-     *
-     * @param reason The reason for disconnecting the current route.
-     */
-    public void unselect(int reason) {
-        if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN ||
-                reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) {
-            throw new IllegalArgumentException("Unsupported reason to unselect route");
-        }
-        checkCallingThread();
-
-        // Choose the fallback route if it's not already selected.
-        // Otherwise, select the default route.
-        RouteInfo fallbackRoute = sGlobal.chooseFallbackRoute();
-        if (sGlobal.getSelectedRoute() != fallbackRoute) {
-            sGlobal.selectRoute(fallbackRoute, reason);
-        } else {
-            sGlobal.selectRoute(sGlobal.getDefaultRoute(), reason);
-        }
-    }
-
-    /**
-     * Returns true if there is a route that matches the specified selector.
-     * <p>
-     * This method returns true if there are any available routes that match the
-     * selector regardless of whether they are enabled or disabled. If the
-     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
-     * the method will only consider non-default routes.
-     * </p>
-     * <p class="note">
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method
-     * will return true if it is possible to discover a matching route even if
-     * discovery is not in progress or if no matching route has yet been found.
-     * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match.
-     * </p>
-     *
-     * @param selector The selector to match.
-     * @param flags Flags to control the determination of whether a route may be
-     *            available. May be zero or some combination of
-     *            {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and
-     *            {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}.
-     * @return True if a matching route may be available.
-     */
-    public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-        checkCallingThread();
-
-        return sGlobal.isRouteAvailable(selector, flags);
-    }
-
-    /**
-     * Registers a callback to discover routes that match the selector and to receive
-     * events when they change.
-     * <p>
-     * This is a convenience method that has the same effect as calling
-     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
-     * </p>
-     *
-     * @param selector A route selector that indicates the kinds of routes that the
-     * callback would like to discover.
-     * @param callback The callback to add.
-     * @see #removeCallback
-     */
-    public void addCallback(MediaRouteSelector selector, Callback callback) {
-        addCallback(selector, callback, 0);
-    }
-
-    /**
-     * Registers a callback to discover routes that match the selector and to receive
-     * events when they change.
-     * <p>
-     * The selector describes the kinds of routes that the application wants to
-     * discover.  For example, if the application wants to use
-     * live audio routes then it should include the
-     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
-     * in its selector when it adds a callback to the media router.
-     * The selector may include any number of categories.
-     * </p><p>
-     * If the callback has already been registered, then the selector is added to
-     * the set of selectors being monitored by the callback.
-     * </p><p>
-     * By default, the callback will only be invoked for events that affect routes
-     * that match the specified selector.  Event filtering may be disabled by specifying
-     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
-     * </p><p>
-     * Applications should use the {@link #isRouteAvailable} method to determine
-     * whether is it possible to discover a route with the desired capabilities
-     * and therefore whether the media route button should be shown to the user.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application
-     * is in the foreground to request that passive discovery be performed if there are
-     * sufficient resources to allow continuous passive discovery.
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be
-     * ignored to conserve resources.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when
-     * passive discovery absolutely must be performed, even on low-RAM devices.
-     * This flag has a significant performance impact on low-RAM devices
-     * since it may cause many media route providers to be started simultaneously.
-     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
-     * performing passive discovery on these devices altogether.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the
-     * media route chooser dialog is showing to confirm the presence of available
-     * routes that the user may connect to.  This flag may use substantially more
-     * power.
-     * </p>
-     *
-     * <h3>Example</h3>
-     * <pre>
-     * public class MyActivity extends Activity {
-     *     private MediaRouter mRouter;
-     *     private MediaRouter.Callback mCallback;
-     *     private MediaRouteSelector mSelector;
-     *
-     *     protected void onCreate(Bundle savedInstanceState) {
-     *         super.onCreate(savedInstanceState);
-     *
-     *         mRouter = Mediarouter.getInstance(this);
-     *         mCallback = new MyCallback();
-     *         mSelector = new MediaRouteSelector.Builder()
-     *                 .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
-     *                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
-     *                 .build();
-     *     }
-     *
-     *     // Add the callback on start to tell the media router what kinds of routes
-     *     // the application is interested in so that it can try to discover suitable ones.
-     *     public void onStart() {
-     *         super.onStart();
-     *
-     *         mediaRouter.addCallback(mSelector, mCallback,
-     *                 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
-     *
-     *         MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector);
-     *         // do something with the route...
-     *     }
-     *
-     *     // Remove the selector on stop to tell the media router that it no longer
-     *     // needs to invest effort trying to discover routes of these kinds for now.
-     *     public void onStop() {
-     *         super.onStop();
-     *
-     *         mediaRouter.removeCallback(mCallback);
-     *     }
-     *
-     *     private final class MyCallback extends MediaRouter.Callback {
-     *         // Implement callback methods as needed.
-     *     }
-     * }
-     * </pre>
-     *
-     * @param selector A route selector that indicates the kinds of routes that the
-     * callback would like to discover.
-     * @param callback The callback to add.
-     * @param flags Flags to control the behavior of the callback.
-     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
-     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
-     * @see #removeCallback
-     */
-    public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback,
-            @CallbackFlags int flags) {
-        if (selector == null) {
-            throw new IllegalArgumentException("selector must not be null");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("callback must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "addCallback: selector=" + selector
-                    + ", callback=" + callback + ", flags=" + Integer.toHexString(flags));
-        }
-
-        CallbackRecord record;
-        int index = findCallbackRecord(callback);
-        if (index < 0) {
-            record = new CallbackRecord(this, callback);
-            mCallbackRecords.add(record);
-        } else {
-            record = mCallbackRecords.get(index);
-        }
-        boolean updateNeeded = false;
-        if ((flags & ~record.mFlags) != 0) {
-            record.mFlags |= flags;
-            updateNeeded = true;
-        }
-        if (!record.mSelector.contains(selector)) {
-            record.mSelector = new MediaRouteSelector.Builder(record.mSelector)
-                    .addSelector(selector)
-                    .build();
-            updateNeeded = true;
-        }
-        if (updateNeeded) {
-            sGlobal.updateDiscoveryRequest();
-        }
-    }
-
-    /**
-     * Removes the specified callback.  It will no longer receive events about
-     * changes to media routes.
-     *
-     * @param callback The callback to remove.
-     * @see #addCallback
-     */
-    public void removeCallback(@NonNull Callback callback) {
-        if (callback == null) {
-            throw new IllegalArgumentException("callback must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "removeCallback: callback=" + callback);
-        }
-
-        int index = findCallbackRecord(callback);
-        if (index >= 0) {
-            mCallbackRecords.remove(index);
-            sGlobal.updateDiscoveryRequest();
-        }
-    }
-
-    private int findCallbackRecord(Callback callback) {
-        final int count = mCallbackRecords.size();
-        for (int i = 0; i < count; i++) {
-            if (mCallbackRecords.get(i).mCallback == callback) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Registers a media route provider within this application process.
-     * <p>
-     * The provider will be added to the list of providers that all {@link MediaRouter}
-     * instances within this process can use to discover routes.
-     * </p>
-     *
-     * @param providerInstance The media route provider instance to add.
-     *
-     * @see MediaRouteProvider
-     * @see #removeCallback
-     */
-    public void addProvider(@NonNull MediaRouteProvider providerInstance) {
-        if (providerInstance == null) {
-            throw new IllegalArgumentException("providerInstance must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "addProvider: " + providerInstance);
-        }
-        sGlobal.addProvider(providerInstance);
-    }
-
-    /**
-     * Unregisters a media route provider within this application process.
-     * <p>
-     * The provider will be removed from the list of providers that all {@link MediaRouter}
-     * instances within this process can use to discover routes.
-     * </p>
-     *
-     * @param providerInstance The media route provider instance to remove.
-     *
-     * @see MediaRouteProvider
-     * @see #addCallback
-     */
-    public void removeProvider(@NonNull MediaRouteProvider providerInstance) {
-        if (providerInstance == null) {
-            throw new IllegalArgumentException("providerInstance must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "removeProvider: " + providerInstance);
-        }
-        sGlobal.removeProvider(providerInstance);
-    }
-
-    /**
-     * Adds a remote control client to enable remote control of the volume
-     * of the selected route.
-     * <p>
-     * The remote control client must have previously been registered with
-     * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient
-     * AudioManager.registerRemoteControlClient} method.
-     * </p>
-     *
-     * @param remoteControlClient The {@link android.media.RemoteControlClient} to register.
-     */
-    public void addRemoteControlClient(@NonNull Object remoteControlClient) {
-        if (remoteControlClient == null) {
-            throw new IllegalArgumentException("remoteControlClient must not be null");
-        }
-        checkCallingThread();
-
-        if (DEBUG) {
-            Log.d(TAG, "addRemoteControlClient: " + remoteControlClient);
-        }
-        sGlobal.addRemoteControlClient(remoteControlClient);
-    }
-
-    /**
-     * Removes a remote control client.
-     *
-     * @param remoteControlClient The {@link android.media.RemoteControlClient}
-     *            to unregister.
-     */
-    public void removeRemoteControlClient(@NonNull Object remoteControlClient) {
-        if (remoteControlClient == null) {
-            throw new IllegalArgumentException("remoteControlClient must not be null");
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient);
-        }
-        sGlobal.removeRemoteControlClient(remoteControlClient);
-    }
-
-    /**
-     * Sets the media session to enable remote control of the volume of the
-     * selected route. This should be used instead of
-     * {@link #addRemoteControlClient} when using media sessions. Set the
-     * session to null to clear it.
-     *
-     * @param mediaSession The {@link android.media.session.MediaSession} to
-     *            use.
-     */
-    public void setMediaSession(Object mediaSession) {
-        if (DEBUG) {
-            Log.d(TAG, "addMediaSession: " + mediaSession);
-        }
-        sGlobal.setMediaSession(mediaSession);
-    }
-
-    /**
-     * Sets a compat media session to enable remote control of the volume of the
-     * selected route. This should be used instead of
-     * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}.
-     * Set the session to null to clear it.
-     *
-     * @param mediaSession
-     */
-    public void setMediaSessionCompat(MediaSessionCompat mediaSession) {
-        if (DEBUG) {
-            Log.d(TAG, "addMediaSessionCompat: " + mediaSession);
-        }
-        sGlobal.setMediaSessionCompat(mediaSession);
-    }
-
-    public MediaSessionCompat.Token getMediaSessionToken() {
-        return sGlobal.getMediaSessionToken();
-    }
-
-    /**
-     * Ensures that calls into the media router are on the correct thread.
-     * It pays to be a little paranoid when global state invariants are at risk.
-     */
-    static void checkCallingThread() {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new IllegalStateException("The media router service must only be "
-                    + "accessed on the application's main thread.");
-        }
-    }
-
-    static <T> boolean equal(T a, T b) {
-        return a == b || (a != null && b != null && a.equals(b));
-    }
-
-    /**
-     * Provides information about a media route.
-     * <p>
-     * Each media route has a list of {@link MediaControlIntent media control}
-     * {@link #getControlFilters intent filters} that describe the capabilities of the
-     * route and the manner in which it is used and controlled.
-     * </p>
-     */
-    public static class RouteInfo {
-        private final ProviderInfo mProvider;
-        private final String mDescriptorId;
-        private final String mUniqueId;
-        private String mName;
-        private String mDescription;
-        private Uri mIconUri;
-        private boolean mEnabled;
-        private boolean mConnecting;
-        private int mConnectionState;
-        private boolean mCanDisconnect;
-        private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>();
-        private int mPlaybackType;
-        private int mPlaybackStream;
-        private int mDeviceType;
-        private int mVolumeHandling;
-        private int mVolume;
-        private int mVolumeMax;
-        private Display mPresentationDisplay;
-        private int mPresentationDisplayId = PRESENTATION_DISPLAY_ID_NONE;
-        private Bundle mExtras;
-        private IntentSender mSettingsIntent;
-        MediaRouteDescriptor mDescriptor;
-
-        @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
-                CONNECTION_STATE_CONNECTED})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface ConnectionState {}
-
-        /**
-         * The default connection state indicating the route is disconnected.
-         *
-         * @see #getConnectionState
-         */
-        public static final int CONNECTION_STATE_DISCONNECTED = 0;
-
-        /**
-         * A connection state indicating the route is in the process of connecting and is not yet
-         * ready for use.
-         *
-         * @see #getConnectionState
-         */
-        public static final int CONNECTION_STATE_CONNECTING = 1;
-
-        /**
-         * A connection state indicating the route is connected.
-         *
-         * @see #getConnectionState
-         */
-        public static final int CONNECTION_STATE_CONNECTED = 2;
-
-        @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface PlaybackType {}
-
-        /**
-         * The default playback type, "local", indicating the presentation of the media
-         * is happening on the same device (e.g. a phone, a tablet) as where it is
-         * controlled from.
-         *
-         * @see #getPlaybackType
-         */
-        public static final int PLAYBACK_TYPE_LOCAL = 0;
-
-        /**
-         * A playback type indicating the presentation of the media is happening on
-         * a different device (i.e. the remote device) than where it is controlled from.
-         *
-         * @see #getPlaybackType
-         */
-        public static final int PLAYBACK_TYPE_REMOTE = 1;
-
-        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface DeviceType {}
-
-        /**
-         * The default receiver device type of the route indicating the type is unknown.
-         *
-         * @see #getDeviceType
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public static final int DEVICE_TYPE_UNKNOWN = 0;
-
-        /**
-         * A receiver device type of the route indicating the presentation of the media is happening
-         * on a TV.
-         *
-         * @see #getDeviceType
-         */
-        public static final int DEVICE_TYPE_TV = 1;
-
-        /**
-         * A receiver device type of the route indicating the presentation of the media is happening
-         * on a speaker.
-         *
-         * @see #getDeviceType
-         */
-        public static final int DEVICE_TYPE_SPEAKER = 2;
-
-        /**
-         * A receiver device type of the route indicating the presentation of the media is happening
-         * on a bluetooth device such as a bluetooth speaker.
-         *
-         * @see #getDeviceType
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public static final int DEVICE_TYPE_BLUETOOTH = 3;
-
-        @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
-        @Retention(RetentionPolicy.SOURCE)
-        private @interface PlaybackVolume {}
-
-        /**
-         * Playback information indicating the playback volume is fixed, i.e. it cannot be
-         * controlled from this object. An example of fixed playback volume is a remote player,
-         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
-         * than attenuate at the source.
-         *
-         * @see #getVolumeHandling
-         */
-        public static final int PLAYBACK_VOLUME_FIXED = 0;
-
-        /**
-         * Playback information indicating the playback volume is variable and can be controlled
-         * from this object.
-         *
-         * @see #getVolumeHandling
-         */
-        public static final int PLAYBACK_VOLUME_VARIABLE = 1;
-
-        /**
-         * The default presentation display id indicating no presentation display is associated
-         * with the route.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public static final int PRESENTATION_DISPLAY_ID_NONE = -1;
-
-        static final int CHANGE_GENERAL = 1 << 0;
-        static final int CHANGE_VOLUME = 1 << 1;
-        static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2;
-
-        // Should match to SystemMediaRouteProvider.PACKAGE_NAME.
-        static final String SYSTEM_MEDIA_ROUTE_PROVIDER_PACKAGE_NAME = "android";
-
-        RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) {
-            mProvider = provider;
-            mDescriptorId = descriptorId;
-            mUniqueId = uniqueId;
-        }
-
-        /**
-         * Gets information about the provider of this media route.
-         */
-        public ProviderInfo getProvider() {
-            return mProvider;
-        }
-
-        /**
-         * Gets the unique id of the route.
-         * <p>
-         * The route unique id functions as a stable identifier by which the route is known.
-         * For example, an application can use this id as a token to remember the
-         * selected route across restarts or to communicate its identity to a service.
-         * </p>
-         *
-         * @return The unique id of the route, never null.
-         */
-        @NonNull
-        public String getId() {
-            return mUniqueId;
-        }
-
-        /**
-         * Gets the user-visible name of the route.
-         * <p>
-         * The route name identifies the destination represented by the route.
-         * It may be a user-supplied name, an alias, or device serial number.
-         * </p>
-         *
-         * @return The user-visible name of a media route.  This is the string presented
-         * to users who may select this as the active route.
-         */
-        public String getName() {
-            return mName;
-        }
-
-        /**
-         * Gets the user-visible description of the route.
-         * <p>
-         * The route description describes the kind of destination represented by the route.
-         * It may be a user-supplied string, a model number or brand of device.
-         * </p>
-         *
-         * @return The description of the route, or null if none.
-         */
-        @Nullable
-        public String getDescription() {
-            return mDescription;
-        }
-
-        /**
-         * Gets the URI of the icon representing this route.
-         * <p>
-         * This icon will be used in picker UIs if available.
-         * </p>
-         *
-         * @return The URI of the icon representing this route, or null if none.
-         */
-        public Uri getIconUri() {
-            return mIconUri;
-        }
-
-        /**
-         * Returns true if this route is enabled and may be selected.
-         *
-         * @return True if this route is enabled.
-         */
-        public boolean isEnabled() {
-            return mEnabled;
-        }
-
-        /**
-         * Returns true if the route is in the process of connecting and is not
-         * yet ready for use.
-         *
-         * @return True if this route is in the process of connecting.
-         */
-        public boolean isConnecting() {
-            return mConnecting;
-        }
-
-        /**
-         * Gets the connection state of the route.
-         *
-         * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
-         * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
-         */
-        @ConnectionState
-        public int getConnectionState() {
-            return mConnectionState;
-        }
-
-        /**
-         * Returns true if this route is currently selected.
-         *
-         * @return True if this route is currently selected.
-         *
-         * @see MediaRouter#getSelectedRoute
-         */
-        public boolean isSelected() {
-            checkCallingThread();
-            return sGlobal.getSelectedRoute() == this;
-        }
-
-        /**
-         * Returns true if this route is the default route.
-         *
-         * @return True if this route is the default route.
-         *
-         * @see MediaRouter#getDefaultRoute
-         */
-        public boolean isDefault() {
-            checkCallingThread();
-            return sGlobal.getDefaultRoute() == this;
-        }
-
-        /**
-         * Returns true if this route is a bluetooth route.
-         *
-         * @return True if this route is a bluetooth route.
-         *
-         * @see MediaRouter#getBluetoothRoute
-         */
-        public boolean isBluetooth() {
-            checkCallingThread();
-            return sGlobal.getBluetoothRoute() == this;
-        }
-
-        /**
-         * Returns true if this route is the default route and the device speaker.
-         *
-         * @return True if this route is the default route and the device speaker.
-         */
-        public boolean isDeviceSpeaker() {
-            int defaultAudioRouteNameResourceId = Resources.getSystem().getIdentifier(
-                    "default_audio_route_name", "string", "android");
-            return isDefault()
-                    && Resources.getSystem().getText(defaultAudioRouteNameResourceId).equals(mName);
-        }
-
-        /**
-         * Gets a list of {@link MediaControlIntent media control intent} filters that
-         * describe the capabilities of this route and the media control actions that
-         * it supports.
-         *
-         * @return A list of intent filters that specifies the media control intents that
-         * this route supports.
-         *
-         * @see MediaControlIntent
-         * @see #supportsControlCategory
-         * @see #supportsControlRequest
-         */
-        public List<IntentFilter> getControlFilters() {
-            return mControlFilters;
-        }
-
-        /**
-         * Returns true if the route supports at least one of the capabilities
-         * described by a media route selector.
-         *
-         * @param selector The selector that specifies the capabilities to check.
-         * @return True if the route supports at least one of the capabilities
-         * described in the media route selector.
-         */
-        public boolean matchesSelector(@NonNull MediaRouteSelector selector) {
-            if (selector == null) {
-                throw new IllegalArgumentException("selector must not be null");
-            }
-            checkCallingThread();
-            return selector.matchesControlFilters(mControlFilters);
-        }
-
-        /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} category.
-         * <p>
-         * Media control categories describe the capabilities of this route
-         * such as whether it supports live audio streaming or remote playback.
-         * </p>
-         *
-         * @param category A {@link MediaControlIntent media control} category
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
-         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
-         * media control category.
-         * @return True if the route supports the specified intent category.
-         *
-         * @see MediaControlIntent
-         * @see #getControlFilters
-         */
-        public boolean supportsControlCategory(@NonNull String category) {
-            if (category == null) {
-                throw new IllegalArgumentException("category must not be null");
-            }
-            checkCallingThread();
-
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                if (mControlFilters.get(i).hasCategory(category)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} category and action.
-         * <p>
-         * Media control actions describe specific requests that an application
-         * can ask a route to perform.
-         * </p>
-         *
-         * @param category A {@link MediaControlIntent media control} category
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
-         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
-         * media control category.
-         * @param action A {@link MediaControlIntent media control} action
-         * such as {@link MediaControlIntent#ACTION_PLAY}.
-         * @return True if the route supports the specified intent action.
-         *
-         * @see MediaControlIntent
-         * @see #getControlFilters
-         */
-        public boolean supportsControlAction(@NonNull String category, @NonNull String action) {
-            if (category == null) {
-                throw new IllegalArgumentException("category must not be null");
-            }
-            if (action == null) {
-                throw new IllegalArgumentException("action must not be null");
-            }
-            checkCallingThread();
-
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                IntentFilter filter = mControlFilters.get(i);
-                if (filter.hasCategory(category) && filter.hasAction(action)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} request.
-         * <p>
-         * Media control requests are used to request the route to perform
-         * actions such as starting remote playback of a media item.
-         * </p>
-         *
-         * @param intent A {@link MediaControlIntent media control intent}.
-         * @return True if the route can handle the specified intent.
-         *
-         * @see MediaControlIntent
-         * @see #getControlFilters
-         */
-        public boolean supportsControlRequest(@NonNull Intent intent) {
-            if (intent == null) {
-                throw new IllegalArgumentException("intent must not be null");
-            }
-            checkCallingThread();
-
-            ContentResolver contentResolver = sGlobal.getContentResolver();
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Sends a {@link MediaControlIntent media control} request to be performed
-         * asynchronously by the route's destination.
-         * <p>
-         * Media control requests are used to request the route to perform
-         * actions such as starting remote playback of a media item.
-         * </p><p>
-         * This function may only be called on a selected route.  Control requests
-         * sent to unselected routes will fail.
-         * </p>
-         *
-         * @param intent A {@link MediaControlIntent media control intent}.
-         * @param callback A {@link ControlRequestCallback} to invoke with the result
-         * of the request, or null if no result is required.
-         *
-         * @see MediaControlIntent
-         */
-        public void sendControlRequest(@NonNull Intent intent,
-                @Nullable ControlRequestCallback callback) {
-            if (intent == null) {
-                throw new IllegalArgumentException("intent must not be null");
-            }
-            checkCallingThread();
-
-            sGlobal.sendControlRequest(this, intent, callback);
-        }
-
-        /**
-         * Gets the type of playback associated with this route.
-         *
-         * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL}
-         * or {@link #PLAYBACK_TYPE_REMOTE}.
-         */
-        @PlaybackType
-        public int getPlaybackType() {
-            return mPlaybackType;
-        }
-
-        /**
-         * Gets the audio stream over which the playback associated with this route is performed.
-         *
-         * @return The stream over which the playback associated with this route is performed.
-         */
-        public int getPlaybackStream() {
-            return mPlaybackStream;
-        }
-
-        /**
-         * Gets the type of the receiver device associated with this route.
-         *
-         * @return The type of the receiver device associated with this route:
-         * {@link #DEVICE_TYPE_TV} or {@link #DEVICE_TYPE_SPEAKER}.
-         */
-        public int getDeviceType() {
-            return mDeviceType;
-        }
-
-
-        /**
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public boolean isDefaultOrBluetooth() {
-            if (isDefault() || mDeviceType == DEVICE_TYPE_BLUETOOTH) {
-                return true;
-            }
-            // This is a workaround for platform version 23 or below where the system route
-            // provider doesn't specify device type for bluetooth media routes.
-            return isSystemMediaRouteProvider(this)
-                    && supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
-                    && !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);
-        }
-
-        /**
-         * Gets information about how volume is handled on the route.
-         *
-         * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED}
-         * or {@link #PLAYBACK_VOLUME_VARIABLE}.
-         */
-        @PlaybackVolume
-        public int getVolumeHandling() {
-            return mVolumeHandling;
-        }
-
-        /**
-         * Gets the current volume for this route. Depending on the route, this may only
-         * be valid if the route is currently selected.
-         *
-         * @return The volume at which the playback associated with this route is performed.
-         */
-        public int getVolume() {
-            return mVolume;
-        }
-
-        /**
-         * Gets the maximum volume at which the playback associated with this route is performed.
-         *
-         * @return The maximum volume at which the playback associated with
-         * this route is performed.
-         */
-        public int getVolumeMax() {
-            return mVolumeMax;
-        }
-
-        /**
-         * Gets whether this route supports disconnecting without interrupting
-         * playback.
-         *
-         * @return True if this route can disconnect without stopping playback,
-         *         false otherwise.
-         */
-        public boolean canDisconnect() {
-            return mCanDisconnect;
-        }
-
-        /**
-         * Requests a volume change for this route asynchronously.
-         * <p>
-         * This function may only be called on a selected route.  It will have
-         * no effect if the route is currently unselected.
-         * </p>
-         *
-         * @param volume The new volume value between 0 and {@link #getVolumeMax}.
-         */
-        public void requestSetVolume(int volume) {
-            checkCallingThread();
-            sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume)));
-        }
-
-        /**
-         * Requests an incremental volume update for this route asynchronously.
-         * <p>
-         * This function may only be called on a selected route.  It will have
-         * no effect if the route is currently unselected.
-         * </p>
-         *
-         * @param delta The delta to add to the current volume.
-         */
-        public void requestUpdateVolume(int delta) {
-            checkCallingThread();
-            if (delta != 0) {
-                sGlobal.requestUpdateVolume(this, delta);
-            }
-        }
-
-        /**
-         * Gets the {@link Display} that should be used by the application to show
-         * a {@link android.app.Presentation} on an external display when this route is selected.
-         * Depending on the route, this may only be valid if the route is currently
-         * selected.
-         * <p>
-         * The preferred presentation display may change independently of the route
-         * being selected or unselected.  For example, the presentation display
-         * of the default system route may change when an external HDMI display is connected
-         * or disconnected even though the route itself has not changed.
-         * </p><p>
-         * This method may return null if there is no external display associated with
-         * the route or if the display is not ready to show UI yet.
-         * </p><p>
-         * The application should listen for changes to the presentation display
-         * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
-         * show or dismiss its {@link android.app.Presentation} accordingly when the display
-         * becomes available or is removed.
-         * </p><p>
-         * This method only makes sense for
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes.
-         * </p>
-         *
-         * @return The preferred presentation display to use when this route is
-         * selected or null if none.
-         *
-         * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
-         * @see android.app.Presentation
-         */
-        @Nullable
-        public Display getPresentationDisplay() {
-            checkCallingThread();
-            if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) {
-                mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId);
-            }
-            return mPresentationDisplay;
-        }
-
-        /**
-         * Gets the route's presentation display id, or -1 if none.
-         * @hide
-         */
-        @RestrictTo(LIBRARY_GROUP)
-        public int getPresentationDisplayId() {
-            return mPresentationDisplayId;
-        }
-
-        /**
-         * Gets a collection of extra properties about this route that were supplied
-         * by its media route provider, or null if none.
-         */
-        @Nullable
-        public Bundle getExtras() {
-            return mExtras;
-        }
-
-        /**
-         * Gets an intent sender for launching a settings activity for this
-         * route.
-         */
-        @Nullable
-        public IntentSender getSettingsIntent() {
-            return mSettingsIntent;
-        }
-
-        /**
-         * Selects this media route.
-         */
-        public void select() {
-            checkCallingThread();
-            sGlobal.selectRoute(this);
-        }
-
-        @Override
-        public String toString() {
-            return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId
-                    + ", name=" + mName
-                    + ", description=" + mDescription
-                    + ", iconUri=" + mIconUri
-                    + ", enabled=" + mEnabled
-                    + ", connecting=" + mConnecting
-                    + ", connectionState=" + mConnectionState
-                    + ", canDisconnect=" + mCanDisconnect
-                    + ", playbackType=" + mPlaybackType
-                    + ", playbackStream=" + mPlaybackStream
-                    + ", deviceType=" + mDeviceType
-                    + ", volumeHandling=" + mVolumeHandling
-                    + ", volume=" + mVolume
-                    + ", volumeMax=" + mVolumeMax
-                    + ", presentationDisplayId=" + mPresentationDisplayId
-                    + ", extras=" + mExtras
-                    + ", settingsIntent=" + mSettingsIntent
-                    + ", providerPackageName=" + mProvider.getPackageName()
-                    + " }";
-        }
-
-        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
-            int changes = 0;
-            if (mDescriptor != descriptor) {
-                changes = updateDescriptor(descriptor);
-            }
-            return changes;
-        }
-
-        int updateDescriptor(MediaRouteDescriptor descriptor) {
-            int changes = 0;
-            mDescriptor = descriptor;
-            if (descriptor != null) {
-                if (!equal(mName, descriptor.getName())) {
-                    mName = descriptor.getName();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!equal(mDescription, descriptor.getDescription())) {
-                    mDescription = descriptor.getDescription();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!equal(mIconUri, descriptor.getIconUri())) {
-                    mIconUri = descriptor.getIconUri();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mEnabled != descriptor.isEnabled()) {
-                    mEnabled = descriptor.isEnabled();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mConnecting != descriptor.isConnecting()) {
-                    mConnecting = descriptor.isConnecting();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mConnectionState != descriptor.getConnectionState()) {
-                    mConnectionState = descriptor.getConnectionState();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!mControlFilters.equals(descriptor.getControlFilters())) {
-                    mControlFilters.clear();
-                    mControlFilters.addAll(descriptor.getControlFilters());
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mPlaybackType != descriptor.getPlaybackType()) {
-                    mPlaybackType = descriptor.getPlaybackType();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mPlaybackStream != descriptor.getPlaybackStream()) {
-                    mPlaybackStream = descriptor.getPlaybackStream();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mDeviceType != descriptor.getDeviceType()) {
-                    mDeviceType = descriptor.getDeviceType();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mVolumeHandling != descriptor.getVolumeHandling()) {
-                    mVolumeHandling = descriptor.getVolumeHandling();
-                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                }
-                if (mVolume != descriptor.getVolume()) {
-                    mVolume = descriptor.getVolume();
-                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                }
-                if (mVolumeMax != descriptor.getVolumeMax()) {
-                    mVolumeMax = descriptor.getVolumeMax();
-                    changes |= CHANGE_GENERAL | CHANGE_VOLUME;
-                }
-                if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) {
-                    mPresentationDisplayId = descriptor.getPresentationDisplayId();
-                    mPresentationDisplay = null;
-                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
-                }
-                if (!equal(mExtras, descriptor.getExtras())) {
-                    mExtras = descriptor.getExtras();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) {
-                    mSettingsIntent = descriptor.getSettingsActivity();
-                    changes |= CHANGE_GENERAL;
-                }
-                if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) {
-                    mCanDisconnect = descriptor.canDisconnectAndKeepPlaying();
-                    changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY;
-                }
-            }
-            return changes;
-        }
-
-        String getDescriptorId() {
-            return mDescriptorId;
-        }
-
-        /** @hide */
-        @RestrictTo(LIBRARY_GROUP)
-        public MediaRouteProvider getProviderInstance() {
-            return mProvider.getProviderInstance();
-        }
-    }
-
-    /**
-     * Information about a route that consists of multiple other routes in a group.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class RouteGroup extends RouteInfo {
-        private List<RouteInfo> mRoutes = new ArrayList<>();
-
-        RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) {
-            super(provider, descriptorId, uniqueId);
-        }
-
-        /**
-         * @return The number of routes in this group
-         */
-        public int getRouteCount() {
-            return mRoutes.size();
-        }
-
-        /**
-         * Returns the route in this group at the specified index
-         *
-         * @param index Index to fetch
-         * @return The route at index
-         */
-        public RouteInfo getRouteAt(int index) {
-            return mRoutes.get(index);
-        }
-
-        /**
-         * Returns the routes in this group
-         *
-         * @return The list of the routes in this group
-         */
-        public List<RouteInfo> getRoutes() {
-            return mRoutes;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(super.toString());
-            sb.append('[');
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (i > 0) sb.append(", ");
-                sb.append(mRoutes.get(i));
-            }
-            sb.append(']');
-            return sb.toString();
-        }
-
-        @Override
-        int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) {
-            boolean changed = false;
-            if (mDescriptor != descriptor) {
-                mDescriptor = descriptor;
-                if (descriptor != null) {
-                    List<String> groupMemberIds = descriptor.getGroupMemberIds();
-                    List<RouteInfo> routes = new ArrayList<>();
-                    changed = groupMemberIds.size() != mRoutes.size();
-                    for (String groupMemberId : groupMemberIds) {
-                        String uniqueId = sGlobal.getUniqueId(getProvider(), groupMemberId);
-                        RouteInfo groupMember = sGlobal.getRoute(uniqueId);
-                        if (groupMember != null) {
-                            routes.add(groupMember);
-                            if (!changed && !mRoutes.contains(groupMember)) {
-                                changed = true;
-                            }
-                        }
-                    }
-                    if (changed) {
-                        mRoutes = routes;
-                    }
-                }
-            }
-            return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor);
-        }
-    }
-
-    /**
-     * Provides information about a media route provider.
-     * <p>
-     * This object may be used to determine which media route provider has
-     * published a particular route.
-     * </p>
-     */
-    public static final class ProviderInfo {
-        private final MediaRouteProvider mProviderInstance;
-        private final List<RouteInfo> mRoutes = new ArrayList<>();
-
-        private final ProviderMetadata mMetadata;
-        private MediaRouteProviderDescriptor mDescriptor;
-        private Resources mResources;
-        private boolean mResourcesNotAvailable;
-
-        ProviderInfo(MediaRouteProvider provider) {
-            mProviderInstance = provider;
-            mMetadata = provider.getMetadata();
-        }
-
-        /**
-         * Gets the provider's underlying {@link MediaRouteProvider} instance.
-         */
-        public MediaRouteProvider getProviderInstance() {
-            checkCallingThread();
-            return mProviderInstance;
-        }
-
-        /**
-         * Gets the package name of the media route provider.
-         */
-        public String getPackageName() {
-            return mMetadata.getPackageName();
-        }
-
-        /**
-         * Gets the component name of the media route provider.
-         */
-        public ComponentName getComponentName() {
-            return mMetadata.getComponentName();
-        }
-
-        /**
-         * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider.
-         */
-        public List<RouteInfo> getRoutes() {
-            checkCallingThread();
-            return mRoutes;
-        }
-
-        Resources getResources() {
-            if (mResources == null && !mResourcesNotAvailable) {
-                String packageName = getPackageName();
-                Context context = sGlobal.getProviderContext(packageName);
-                if (context != null) {
-                    mResources = context.getResources();
-                } else {
-                    Log.w(TAG, "Unable to obtain resources for route provider package: "
-                            + packageName);
-                    mResourcesNotAvailable = true;
-                }
-            }
-            return mResources;
-        }
-
-        boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) {
-            if (mDescriptor != descriptor) {
-                mDescriptor = descriptor;
-                return true;
-            }
-            return false;
-        }
-
-        int findRouteByDescriptorId(String id) {
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (mRoutes.get(i).mDescriptorId.equals(id)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        @Override
-        public String toString() {
-            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
-                    + " }";
-        }
-    }
-
-    /**
-     * Interface for receiving events about media routing changes.
-     * All methods of this interface will be called from the application's main thread.
-     * <p>
-     * A Callback will only receive events relevant to routes that the callback
-     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
-     * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}.
-     * </p>
-     *
-     * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int)
-     * @see MediaRouter#removeCallback(Callback)
-     */
-    public static abstract class Callback {
-        /**
-         * Called when the supplied media route becomes selected as the active route.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been selected.
-         */
-        public void onRouteSelected(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when the supplied media route becomes unselected as the active route.
-         * For detailed reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)}
-         * instead.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been unselected.
-         */
-        public void onRouteUnselected(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when the supplied media route becomes unselected as the active route.
-         * The default implementation calls {@link #onRouteUnselected}.
-         * <p>
-         * The reason provided will be one of the following:
-         * <ul>
-         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
-         * </ul>
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been unselected.
-         * @param reason The reason for unselecting the route.
-         */
-        public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) {
-            onRouteUnselected(router, route);
-        }
-
-        /**
-         * Called when a media route has been added.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has become available for use.
-         */
-        public void onRouteAdded(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route has been removed.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that has been removed from availability.
-         */
-        public void onRouteRemoved(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a property of the indicated media route has changed.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route that was changed.
-         */
-        public void onRouteChanged(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route's volume changes.
-         *
-         * @param router The media router reporting the event.
-         * @param route The route whose volume changed.
-         */
-        public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route's presentation display changes.
-         * <p>
-         * This method is called whenever the route's presentation display becomes
-         * available, is removed or has changes to some of its properties (such as its size).
-         * </p>
-         *
-         * @param router The media router reporting the event.
-         * @param route The route whose presentation display changed.
-         *
-         * @see RouteInfo#getPresentationDisplay()
-         */
-        public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
-        }
-
-        /**
-         * Called when a media route provider has been added.
-         *
-         * @param router The media router reporting the event.
-         * @param provider The provider that has become available for use.
-         */
-        public void onProviderAdded(MediaRouter router, ProviderInfo provider) {
-        }
-
-        /**
-         * Called when a media route provider has been removed.
-         *
-         * @param router The media router reporting the event.
-         * @param provider The provider that has been removed from availability.
-         */
-        public void onProviderRemoved(MediaRouter router, ProviderInfo provider) {
-        }
-
-        /**
-         * Called when a property of the indicated media route provider has changed.
-         *
-         * @param router The media router reporting the event.
-         * @param provider The provider that was changed.
-         */
-        public void onProviderChanged(MediaRouter router, ProviderInfo provider) {
-        }
-    }
-
-    /**
-     * Callback which is invoked with the result of a media control request.
-     *
-     * @see RouteInfo#sendControlRequest
-     */
-    public static abstract class ControlRequestCallback {
-        /**
-         * Called when a media control request succeeds.
-         *
-         * @param data Result data, or null if none.
-         * Contents depend on the {@link MediaControlIntent media control action}.
-         */
-        public void onResult(Bundle data) {
-        }
-
-        /**
-         * Called when a media control request fails.
-         *
-         * @param error A localized error message which may be shown to the user, or null
-         * if the cause of the error is unclear.
-         * @param data Error data, or null if none.
-         * Contents depend on the {@link MediaControlIntent media control action}.
-         */
-        public void onError(String error, Bundle data) {
-        }
-    }
-
-    private static final class CallbackRecord {
-        public final MediaRouter mRouter;
-        public final Callback mCallback;
-        public MediaRouteSelector mSelector;
-        public int mFlags;
-
-        public CallbackRecord(MediaRouter router, Callback callback) {
-            mRouter = router;
-            mCallback = callback;
-            mSelector = MediaRouteSelector.EMPTY;
-        }
-
-        public boolean filterRouteEvent(RouteInfo route) {
-            return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
-                    || route.matchesSelector(mSelector);
-        }
-    }
-
-    /**
-     * Global state for the media router.
-     * <p>
-     * Media routes and media route providers are global to the process; their
-     * state and the bulk of the media router implementation lives here.
-     * </p>
-     */
-    private static final class GlobalMediaRouter
-            implements SystemMediaRouteProvider.SyncCallback,
-            RegisteredMediaRouteProviderWatcher.Callback {
-        final Context mApplicationContext;
-        final ArrayList<WeakReference<MediaRouter>> mRouters = new ArrayList<>();
-        private final ArrayList<RouteInfo> mRoutes = new ArrayList<>();
-        private final Map<Pair<String, String>, String> mUniqueIdMap = new HashMap<>();
-        private final ArrayList<ProviderInfo> mProviders = new ArrayList<>();
-        private final ArrayList<RemoteControlClientRecord> mRemoteControlClients =
-                new ArrayList<>();
-        final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo =
-                new RemoteControlClientCompat.PlaybackInfo();
-        private final ProviderCallback mProviderCallback = new ProviderCallback();
-        final CallbackHandler mCallbackHandler = new CallbackHandler();
-        private final DisplayManagerCompat mDisplayManager;
-        final SystemMediaRouteProvider mSystemProvider;
-        private final boolean mLowRam;
-
-        private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher;
-        private RouteInfo mDefaultRoute;
-        private RouteInfo mBluetoothRoute;
-        RouteInfo mSelectedRoute;
-        private RouteController mSelectedRouteController;
-        // A map from route descriptor ID to RouteController for the member routes in the currently
-        // selected route group.
-        private final Map<String, RouteController> mRouteControllerMap = new HashMap<>();
-        private MediaRouteDiscoveryRequest mDiscoveryRequest;
-        private MediaSessionRecord mMediaSession;
-        MediaSessionCompat mRccMediaSession;
-        private MediaSessionCompat mCompatSession;
-        private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener =
-                new MediaSessionCompat.OnActiveChangeListener() {
-            @Override
-            public void onActiveChanged() {
-                if(mRccMediaSession != null) {
-                    if (mRccMediaSession.isActive()) {
-                        addRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    } else {
-                        removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    }
-                }
-            }
-        };
-
-        GlobalMediaRouter(Context applicationContext) {
-            mApplicationContext = applicationContext;
-            mDisplayManager = DisplayManagerCompat.getInstance(applicationContext);
-            mLowRam = ActivityManagerCompat.isLowRamDevice(
-                    (ActivityManager)applicationContext.getSystemService(
-                            Context.ACTIVITY_SERVICE));
-
-            // Add the system media route provider for interoperating with
-            // the framework media router.  This one is special and receives
-            // synchronization messages from the media router.
-            mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this);
-        }
-
-        public void start() {
-            addProvider(mSystemProvider);
-
-            // Start watching for routes published by registered media route
-            // provider services.
-            mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher(
-                    mApplicationContext, this);
-            mRegisteredProviderWatcher.start();
-        }
-
-        public MediaRouter getRouter(Context context) {
-            MediaRouter router;
-            for (int i = mRouters.size(); --i >= 0; ) {
-                router = mRouters.get(i).get();
-                if (router == null) {
-                    mRouters.remove(i);
-                } else if (router.mContext == context) {
-                    return router;
-                }
-            }
-            router = new MediaRouter(context);
-            mRouters.add(new WeakReference<MediaRouter>(router));
-            return router;
-        }
-
-        public ContentResolver getContentResolver() {
-            return mApplicationContext.getContentResolver();
-        }
-
-        public Context getProviderContext(String packageName) {
-            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
-                return mApplicationContext;
-            }
-            try {
-                return mApplicationContext.createPackageContext(
-                        packageName, Context.CONTEXT_RESTRICTED);
-            } catch (NameNotFoundException ex) {
-                return null;
-            }
-        }
-
-        public Display getDisplay(int displayId) {
-            return mDisplayManager.getDisplay(displayId);
-        }
-
-        public void sendControlRequest(RouteInfo route,
-                Intent intent, ControlRequestCallback callback) {
-            if (route == mSelectedRoute && mSelectedRouteController != null) {
-                if (mSelectedRouteController.onControlRequest(intent, callback)) {
-                    return;
-                }
-            }
-            if (callback != null) {
-                callback.onError(null, null);
-            }
-        }
-
-        public void requestSetVolume(RouteInfo route, int volume) {
-            if (route == mSelectedRoute && mSelectedRouteController != null) {
-                mSelectedRouteController.onSetVolume(volume);
-            } else if (!mRouteControllerMap.isEmpty()) {
-                RouteController controller = mRouteControllerMap.get(route.mDescriptorId);
-                if (controller != null) {
-                    controller.onSetVolume(volume);
-                }
-            }
-        }
-
-        public void requestUpdateVolume(RouteInfo route, int delta) {
-            if (route == mSelectedRoute && mSelectedRouteController != null) {
-                mSelectedRouteController.onUpdateVolume(delta);
-            }
-        }
-
-        public RouteInfo getRoute(String uniqueId) {
-            for (RouteInfo info : mRoutes) {
-                if (info.mUniqueId.equals(uniqueId)) {
-                    return info;
-                }
-            }
-            return null;
-        }
-
-        public List<RouteInfo> getRoutes() {
-            return mRoutes;
-        }
-
-        List<ProviderInfo> getProviders() {
-            return mProviders;
-        }
-
-        @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
-                // is a bug in provider initialization.
-                throw new IllegalStateException("There is no default route.  "
-                        + "The media router has not yet been fully initialized.");
-            }
-            return mDefaultRoute;
-        }
-
-        RouteInfo getBluetoothRoute() {
-            return mBluetoothRoute;
-        }
-
-        @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
-                // is a bug in provider initialization.
-                throw new IllegalStateException("There is no currently selected route.  "
-                        + "The media router has not yet been fully initialized.");
-            }
-            return mSelectedRoute;
-        }
-
-        void selectRoute(@NonNull RouteInfo route) {
-            selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED);
-        }
-
-        void selectRoute(@NonNull RouteInfo route, int unselectReason) {
-            if (!mRoutes.contains(route)) {
-                Log.w(TAG, "Ignoring attempt to select removed route: " + route);
-                return;
-            }
-            if (!route.mEnabled) {
-                Log.w(TAG, "Ignoring attempt to select disabled route: " + route);
-                return;
-            }
-            setSelectedRouteInternal(route, unselectReason);
-        }
-
-        public boolean isRouteAvailable(MediaRouteSelector selector, int flags) {
-            if (selector.isEmpty()) {
-                return false;
-            }
-
-            // On low-RAM devices, do not rely on actual discovery results unless asked to.
-            if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) {
-                return true;
-            }
-
-            // Check whether any existing routes match the selector.
-            final int routeCount = mRoutes.size();
-            for (int i = 0; i < routeCount; i++) {
-                RouteInfo route = mRoutes.get(i);
-                if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0
-                        && route.isDefaultOrBluetooth()) {
-                    continue;
-                }
-                if (route.matchesSelector(selector)) {
-                    return true;
-                }
-            }
-
-            // It doesn't look like we can find a matching route right now.
-            return false;
-        }
-
-        public void updateDiscoveryRequest() {
-            // Combine all of the callback selectors and active scan flags.
-            boolean discover = false;
-            boolean activeScan = false;
-            MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder();
-            for (int i = mRouters.size(); --i >= 0; ) {
-                MediaRouter router = mRouters.get(i).get();
-                if (router == null) {
-                    mRouters.remove(i);
-                } else {
-                    final int count = router.mCallbackRecords.size();
-                    for (int j = 0; j < count; j++) {
-                        CallbackRecord callback = router.mCallbackRecords.get(j);
-                        builder.addSelector(callback.mSelector);
-                        if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) {
-                            activeScan = true;
-                            discover = true; // perform active scan implies request discovery
-                        }
-                        if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) {
-                            if (!mLowRam) {
-                                discover = true;
-                            }
-                        }
-                        if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) {
-                            discover = true;
-                        }
-                    }
-                }
-            }
-            MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY;
-
-            // Create a new discovery request.
-            if (mDiscoveryRequest != null
-                    && mDiscoveryRequest.getSelector().equals(selector)
-                    && mDiscoveryRequest.isActiveScan() == activeScan) {
-                return; // no change
-            }
-            if (selector.isEmpty() && !activeScan) {
-                // Discovery is not needed.
-                if (mDiscoveryRequest == null) {
-                    return; // no change
-                }
-                mDiscoveryRequest = null;
-            } else {
-                // Discovery is needed.
-                mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan);
-            }
-            if (DEBUG) {
-                Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest);
-            }
-            if (discover && !activeScan && mLowRam) {
-                Log.i(TAG, "Forcing passive route discovery on a low-RAM device, "
-                        + "system performance may be affected.  Please consider using "
-                        + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of "
-                        + "CALLBACK_FLAG_FORCE_DISCOVERY.");
-            }
-
-            // Notify providers.
-            final int providerCount = mProviders.size();
-            for (int i = 0; i < providerCount; i++) {
-                mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest);
-            }
-        }
-
-        @Override
-        public void addProvider(MediaRouteProvider providerInstance) {
-            int index = findProviderInfo(providerInstance);
-            if (index < 0) {
-                // 1. Add the provider to the list.
-                ProviderInfo provider = new ProviderInfo(providerInstance);
-                mProviders.add(provider);
-                if (DEBUG) {
-                    Log.d(TAG, "Provider added: " + provider);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider);
-                // 2. Create the provider's contents.
-                updateProviderContents(provider, providerInstance.getDescriptor());
-                // 3. Register the provider callback.
-                providerInstance.setCallback(mProviderCallback);
-                // 4. Set the discovery request.
-                providerInstance.setDiscoveryRequest(mDiscoveryRequest);
-            }
-        }
-
-        @Override
-        public void removeProvider(MediaRouteProvider providerInstance) {
-            int index = findProviderInfo(providerInstance);
-            if (index >= 0) {
-                // 1. Unregister the provider callback.
-                providerInstance.setCallback(null);
-                // 2. Clear the discovery request.
-                providerInstance.setDiscoveryRequest(null);
-                // 3. Delete the provider's contents.
-                ProviderInfo provider = mProviders.get(index);
-                updateProviderContents(provider, null);
-                // 4. Remove the provider from the list.
-                if (DEBUG) {
-                    Log.d(TAG, "Provider removed: " + provider);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
-                mProviders.remove(index);
-            }
-        }
-
-        void updateProviderDescriptor(MediaRouteProvider providerInstance,
-                MediaRouteProviderDescriptor descriptor) {
-            int index = findProviderInfo(providerInstance);
-            if (index >= 0) {
-                // Update the provider's contents.
-                ProviderInfo provider = mProviders.get(index);
-                updateProviderContents(provider, descriptor);
-            }
-        }
-
-        private int findProviderInfo(MediaRouteProvider providerInstance) {
-            final int count = mProviders.size();
-            for (int i = 0; i < count; i++) {
-                if (mProviders.get(i).mProviderInstance == providerInstance) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        private void updateProviderContents(ProviderInfo provider,
-                MediaRouteProviderDescriptor providerDescriptor) {
-            if (provider.updateDescriptor(providerDescriptor)) {
-                // Update all existing routes and reorder them to match
-                // the order of their descriptors.
-                int targetIndex = 0;
-                boolean selectedRouteDescriptorChanged = false;
-                if (providerDescriptor != null) {
-                    if (providerDescriptor.isValid()) {
-                        final List<MediaRouteDescriptor> routeDescriptors =
-                                providerDescriptor.getRoutes();
-                        final int routeCount = routeDescriptors.size();
-                        // Updating route group's contents requires all member routes' information.
-                        // Add the groups to the lists and update them later.
-                        List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
-                        List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups =
-                                new ArrayList<>();
-                        for (int i = 0; i < routeCount; i++) {
-                            final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
-                            final String id = routeDescriptor.getId();
-                            final int sourceIndex = provider.findRouteByDescriptorId(id);
-                            if (sourceIndex < 0) {
-                                // 1. Add the route to the list.
-                                String uniqueId = assignRouteUniqueId(provider, id);
-                                boolean isGroup = routeDescriptor.getGroupMemberIds() != null;
-                                RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
-                                        new RouteInfo(provider, id, uniqueId);
-                                provider.mRoutes.add(targetIndex++, route);
-                                mRoutes.add(route);
-                                // 2. Create the route's contents.
-                                if (isGroup) {
-                                    addedGroups.add(new Pair<>(route, routeDescriptor));
-                                } else {
-                                    route.maybeUpdateDescriptor(routeDescriptor);
-                                    // 3. Notify clients about addition.
-                                    if (DEBUG) {
-                                        Log.d(TAG, "Route added: " + route);
-                                    }
-                                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
-                                }
-
-                            } else if (sourceIndex < targetIndex) {
-                                Log.w(TAG, "Ignoring route descriptor with duplicate id: "
-                                        + routeDescriptor);
-                            } else {
-                                // 1. Reorder the route within the list.
-                                RouteInfo route = provider.mRoutes.get(sourceIndex);
-                                Collections.swap(provider.mRoutes,
-                                        sourceIndex, targetIndex++);
-                                // 2. Update the route's contents.
-                                if (route instanceof RouteGroup) {
-                                    updatedGroups.add(new Pair<>(route, routeDescriptor));
-                                } else {
-                                    // 3. Notify clients about changes.
-                                    if (updateRouteDescriptorAndNotify(route, routeDescriptor)
-                                            != 0) {
-                                        if (route == mSelectedRoute) {
-                                            selectedRouteDescriptorChanged = true;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        // Update the new and/or existing groups.
-                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
-                            RouteInfo route = pair.first;
-                            route.maybeUpdateDescriptor(pair.second);
-                            if (DEBUG) {
-                                Log.d(TAG, "Route added: " + route);
-                            }
-                            mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
-                        }
-                        for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
-                            RouteInfo route = pair.first;
-                            if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
-                                if (route == mSelectedRoute) {
-                                    selectedRouteDescriptorChanged = true;
-                                }
-                            }
-                        }
-                    } else {
-                        Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
-                    }
-                }
-
-                // Dispose all remaining routes that do not have matching descriptors.
-                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
-                    // 1. Delete the route's contents.
-                    RouteInfo route = provider.mRoutes.get(i);
-                    route.maybeUpdateDescriptor(null);
-                    // 2. Remove the route from the list.
-                    mRoutes.remove(route);
-                }
-
-                // Update the selected route if needed.
-                updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
-
-                // Now notify clients about routes that were removed.
-                // We do this after updating the selected route to ensure
-                // that the framework media router observes the new route
-                // selection before the removal since removing the currently
-                // selected route may have side-effects.
-                for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
-                    RouteInfo route = provider.mRoutes.remove(i);
-                    if (DEBUG) {
-                        Log.d(TAG, "Route removed: " + route);
-                    }
-                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
-                }
-
-                // Notify provider changed.
-                if (DEBUG) {
-                    Log.d(TAG, "Provider changed: " + provider);
-                }
-                mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
-            }
-        }
-
-        private int updateRouteDescriptorAndNotify(RouteInfo route,
-                MediaRouteDescriptor routeDescriptor) {
-            int changes = route.maybeUpdateDescriptor(routeDescriptor);
-            if (changes != 0) {
-                if ((changes & RouteInfo.CHANGE_GENERAL) != 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route changed: " + route);
-                    }
-                    mCallbackHandler.post(
-                            CallbackHandler.MSG_ROUTE_CHANGED, route);
-                }
-                if ((changes & RouteInfo.CHANGE_VOLUME) != 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route volume changed: " + route);
-                    }
-                    mCallbackHandler.post(
-                            CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route);
-                }
-                if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Route presentation display changed: "
-                                + route);
-                    }
-                    mCallbackHandler.post(CallbackHandler.
-                            MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route);
-                }
-            }
-            return changes;
-        }
-
-        private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
-            // Although route descriptor ids are unique within a provider, it's
-            // possible for there to be two providers with the same package name.
-            // Therefore we must dedupe the composite id.
-            String componentName = provider.getComponentName().flattenToShortString();
-            String uniqueId = componentName + ":" + routeDescriptorId;
-            if (findRouteByUniqueId(uniqueId) < 0) {
-                mUniqueIdMap.put(new Pair<>(componentName, routeDescriptorId), uniqueId);
-                return uniqueId;
-            }
-            Log.w(TAG, "Either " + routeDescriptorId + " isn't unique in " + componentName
-                    + " or we're trying to assign a unique ID for an already added route");
-            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);
-                    return newUniqueId;
-                }
-            }
-        }
-
-        private int findRouteByUniqueId(String uniqueId) {
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (mRoutes.get(i).mUniqueId.equals(uniqueId)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        private String getUniqueId(ProviderInfo provider, String routeDescriptorId) {
-            String componentName = provider.getComponentName().flattenToShortString();
-            return mUniqueIdMap.get(new Pair<>(componentName, routeDescriptorId));
-        }
-
-        private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) {
-            // Update default route.
-            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) && route.isSelectable()) {
-                        mDefaultRoute = route;
-                        Log.i(TAG, "Found default route: " + mDefaultRoute);
-                        break;
-                    }
-                }
-            }
-
-            // Update bluetooth route.
-            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) && route.isSelectable()) {
-                        mBluetoothRoute = route;
-                        Log.i(TAG, "Found bluetooth route: " + mBluetoothRoute);
-                        break;
-                    }
-                }
-            }
-
-            // Update selected route.
-            if (mSelectedRoute == null || !mSelectedRoute.isSelectable()) {
-                Log.i(TAG, "Unselecting the current route because it "
-                        + "is no longer selectable: " + mSelectedRoute);
-                setSelectedRouteInternal(chooseFallbackRoute(),
-                        MediaRouter.UNSELECT_REASON_UNKNOWN);
-            } else if (selectedRouteDescriptorChanged) {
-                // In case the selected route is a route group, select/unselect route controllers
-                // for the added/removed route members.
-                if (mSelectedRoute instanceof RouteGroup) {
-                    List<RouteInfo> routes = ((RouteGroup) mSelectedRoute).getRoutes();
-                    // Build a set of descriptor IDs for the new route group.
-                    Set<String> idSet = new HashSet<>();
-                    for (RouteInfo route : routes) {
-                        idSet.add(route.mDescriptorId);
-                    }
-                    // Unselect route controllers for the removed routes.
-                    Iterator<Map.Entry<String, RouteController>> iter =
-                            mRouteControllerMap.entrySet().iterator();
-                    while (iter.hasNext()) {
-                        Map.Entry<String, RouteController> entry = iter.next();
-                        if (!idSet.contains(entry.getKey())) {
-                            RouteController controller = entry.getValue();
-                            controller.onUnselect();
-                            controller.onRelease();
-                            iter.remove();
-                        }
-                    }
-                    // Select route controllers for the added routes.
-                    for (RouteInfo route : routes) {
-                        if (!mRouteControllerMap.containsKey(route.mDescriptorId)) {
-                            RouteController controller = route.getProviderInstance()
-                                    .onCreateRouteController(
-                                            route.mDescriptorId, mSelectedRoute.mDescriptorId);
-                            controller.onSelect();
-                            mRouteControllerMap.put(route.mDescriptorId, controller);
-                        }
-                    }
-                }
-                // Update the playback info because the properties of the route have changed.
-                updatePlaybackInfoFromSelectedRoute();
-            }
-        }
-
-        RouteInfo chooseFallbackRoute() {
-            // When the current route is removed or no longer selectable,
-            // we want to revert to a live audio route if there is
-            // one (usually Bluetooth A2DP).  Failing that, use
-            // the default route.
-            for (RouteInfo route : mRoutes) {
-                if (route != mDefaultRoute
-                        && isSystemLiveAudioOnlyRoute(route)
-                        && route.isSelectable()) {
-                    return route;
-                }
-            }
-            return mDefaultRoute;
-        }
-
-        private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) {
-            return route.getProviderInstance() == mSystemProvider
-                    && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
-                    && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-        }
-
-        private boolean isSystemDefaultRoute(RouteInfo route) {
-            return route.getProviderInstance() == mSystemProvider
-                    && route.mDescriptorId.equals(
-                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
-        }
-
-        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();
-                StringBuilder sb = new StringBuilder();
-                // callStack[3] is the caller of this method.
-                for (int i = 3; i < callStack.length; i++) {
-                    StackTraceElement caller = callStack[i];
-                    sb.append(caller.getClassName())
-                            .append(".")
-                            .append(caller.getMethodName())
-                            .append(":")
-                            .append(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) {
-                        Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: "
-                                + unselectReason);
-                    }
-                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute,
-                            unselectReason);
-                    if (mSelectedRouteController != null) {
-                        mSelectedRouteController.onUnselect(unselectReason);
-                        mSelectedRouteController.onRelease();
-                        mSelectedRouteController = null;
-                    }
-                    if (!mRouteControllerMap.isEmpty()) {
-                        for (RouteController controller : mRouteControllerMap.values()) {
-                            controller.onUnselect(unselectReason);
-                            controller.onRelease();
-                        }
-                        mRouteControllerMap.clear();
-                    }
-                }
-
-                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 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);
-                    }
-                }
-
-                updatePlaybackInfoFromSelectedRoute();
-            }
-        }
-
-        @Override
-        public void onSystemRouteSelectedByDescriptorId(String id) {
-            // System route is selected, do not sync the route we selected before.
-            mCallbackHandler.removeMessages(CallbackHandler.MSG_ROUTE_SELECTED);
-            int providerIndex = findProviderInfo(mSystemProvider);
-            if (providerIndex >= 0) {
-                ProviderInfo provider = mProviders.get(providerIndex);
-                int routeIndex = provider.findRouteByDescriptorId(id);
-                if (routeIndex >= 0) {
-                    provider.mRoutes.get(routeIndex).select();
-                }
-            }
-        }
-
-        public void addRemoteControlClient(Object rcc) {
-            int index = findRemoteControlClientRecord(rcc);
-            if (index < 0) {
-                RemoteControlClientRecord record = new RemoteControlClientRecord(rcc);
-                mRemoteControlClients.add(record);
-            }
-        }
-
-        public void removeRemoteControlClient(Object rcc) {
-            int index = findRemoteControlClientRecord(rcc);
-            if (index >= 0) {
-                RemoteControlClientRecord record = mRemoteControlClients.remove(index);
-                record.disconnect();
-            }
-        }
-
-        public void setMediaSession(Object session) {
-            setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null);
-        }
-
-        public void setMediaSessionCompat(final MediaSessionCompat session) {
-            mCompatSession = session;
-            if (android.os.Build.VERSION.SDK_INT >= 21) {
-                setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null);
-            } else if (android.os.Build.VERSION.SDK_INT >= 14) {
-                if (mRccMediaSession != null) {
-                    removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
-                }
-                mRccMediaSession = session;
-                if (session != null) {
-                    session.addOnActiveChangeListener(mSessionActiveListener);
-                    if (session.isActive()) {
-                        addRemoteControlClient(session.getRemoteControlClient());
-                    }
-                }
-            }
-        }
-
-        private void setMediaSessionRecord(MediaSessionRecord mediaSessionRecord) {
-            if (mMediaSession != null) {
-                mMediaSession.clearVolumeHandling();
-            }
-            mMediaSession = mediaSessionRecord;
-            if (mediaSessionRecord != null) {
-                updatePlaybackInfoFromSelectedRoute();
-            }
-        }
-
-        public MediaSessionCompat.Token getMediaSessionToken() {
-            if (mMediaSession != null) {
-                return mMediaSession.getToken();
-            } else if (mCompatSession != null) {
-                return mCompatSession.getSessionToken();
-            }
-            return null;
-        }
-
-        private int findRemoteControlClientRecord(Object rcc) {
-            final int count = mRemoteControlClients.size();
-            for (int i = 0; i < count; i++) {
-                RemoteControlClientRecord record = mRemoteControlClients.get(i);
-                if (record.getRemoteControlClient() == rcc) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        private void updatePlaybackInfoFromSelectedRoute() {
-            if (mSelectedRoute != null) {
-                mPlaybackInfo.volume = mSelectedRoute.getVolume();
-                mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax();
-                mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling();
-                mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream();
-                mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType();
-
-                final int count = mRemoteControlClients.size();
-                for (int i = 0; i < count; i++) {
-                    RemoteControlClientRecord record = mRemoteControlClients.get(i);
-                    record.updatePlaybackInfo();
-                }
-                if (mMediaSession != null) {
-                    if (mSelectedRoute == getDefaultRoute()
-                            || mSelectedRoute == getBluetoothRoute()) {
-                        // Local route
-                        mMediaSession.clearVolumeHandling();
-                    } else {
-                        @VolumeProviderCompat.ControlType int controlType =
-                                VolumeProviderCompat.VOLUME_CONTROL_FIXED;
-                        if (mPlaybackInfo.volumeHandling
-                                == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
-                            controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
-                        }
-                        mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax,
-                                mPlaybackInfo.volume);
-                    }
-                }
-            } else {
-                if (mMediaSession != null) {
-                    mMediaSession.clearVolumeHandling();
-                }
-            }
-        }
-
-        private final class ProviderCallback extends MediaRouteProvider.Callback {
-            ProviderCallback() {
-            }
-
-            @Override
-            public void onDescriptorChanged(MediaRouteProvider provider,
-                    MediaRouteProviderDescriptor descriptor) {
-                updateProviderDescriptor(provider, descriptor);
-            }
-        }
-
-        private final class MediaSessionRecord {
-            private final MediaSessionCompat mMsCompat;
-
-            private @VolumeProviderCompat.ControlType int mControlType;
-            private int mMaxVolume;
-            private VolumeProviderCompat mVpCompat;
-
-            public MediaSessionRecord(Object mediaSession) {
-                mMsCompat = MediaSessionCompat.fromMediaSession(mApplicationContext, mediaSession);
-            }
-
-            public MediaSessionRecord(MediaSessionCompat mediaSessionCompat) {
-                mMsCompat = mediaSessionCompat;
-            }
-
-            public void configureVolume(@VolumeProviderCompat.ControlType int controlType,
-                    int max, int current) {
-                if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) {
-                    // If we haven't changed control type or max just set the
-                    // new current volume
-                    mVpCompat.setCurrentVolume(current);
-                } else {
-                    // Otherwise create a new provider and update
-                    mVpCompat = new VolumeProviderCompat(controlType, max, current) {
-                        @Override
-                        public void onSetVolumeTo(final int volume) {
-                            mCallbackHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (mSelectedRoute != null) {
-                                        mSelectedRoute.requestSetVolume(volume);
-                                    }
-                                }
-                            });
-                        }
-
-                        @Override
-                        public void onAdjustVolume(final int direction) {
-                            mCallbackHandler.post(new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (mSelectedRoute != null) {
-                                        mSelectedRoute.requestUpdateVolume(direction);
-                                    }
-                                }
-                            });
-                        }
-                    };
-                    mMsCompat.setPlaybackToRemote(mVpCompat);
-                }
-            }
-
-            public void clearVolumeHandling() {
-                mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream);
-                mVpCompat = null;
-            }
-
-            public MediaSessionCompat.Token getToken() {
-                return mMsCompat.getSessionToken();
-            }
-        }
-
-        private final class RemoteControlClientRecord
-                implements RemoteControlClientCompat.VolumeCallback {
-            private final RemoteControlClientCompat mRccCompat;
-            private boolean mDisconnected;
-
-            public RemoteControlClientRecord(Object rcc) {
-                mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc);
-                mRccCompat.setVolumeCallback(this);
-                updatePlaybackInfo();
-            }
-
-            public Object getRemoteControlClient() {
-                return mRccCompat.getRemoteControlClient();
-            }
-
-            public void disconnect() {
-                mDisconnected = true;
-                mRccCompat.setVolumeCallback(null);
-            }
-
-            public void updatePlaybackInfo() {
-                mRccCompat.setPlaybackInfo(mPlaybackInfo);
-            }
-
-            @Override
-            public void onVolumeSetRequest(int volume) {
-                if (!mDisconnected && mSelectedRoute != null) {
-                    mSelectedRoute.requestSetVolume(volume);
-                }
-            }
-
-            @Override
-            public void onVolumeUpdateRequest(int direction) {
-                if (!mDisconnected && mSelectedRoute != null) {
-                    mSelectedRoute.requestUpdateVolume(direction);
-                }
-            }
-        }
-
-        private final class CallbackHandler extends Handler {
-            private final ArrayList<CallbackRecord> mTempCallbackRecords =
-                    new ArrayList<CallbackRecord>();
-
-            private static final int MSG_TYPE_MASK = 0xff00;
-            private static final int MSG_TYPE_ROUTE = 0x0100;
-            private static final int MSG_TYPE_PROVIDER = 0x0200;
-
-            public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1;
-            public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2;
-            public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3;
-            public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4;
-            public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5;
-            public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6;
-            public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7;
-
-            public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1;
-            public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2;
-            public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3;
-
-            CallbackHandler() {
-            }
-
-            public void post(int msg, Object obj) {
-                obtainMessage(msg, obj).sendToTarget();
-            }
-
-            public void post(int msg, Object obj, int arg) {
-                Message message = obtainMessage(msg, obj);
-                message.arg1 = arg;
-                message.sendToTarget();
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                final int what = msg.what;
-                final Object obj = msg.obj;
-                final int arg = msg.arg1;
-
-                if (what == MSG_ROUTE_CHANGED
-                        && getSelectedRoute().getId().equals(((RouteInfo) obj).getId())) {
-                    updateSelectedRouteIfNeeded(true);
-                }
-
-                // Synchronize state with the system media router.
-                syncWithSystemProvider(what, obj);
-
-                // Invoke all registered callbacks.
-                // Build a list of callbacks before invoking them in case callbacks
-                // are added or removed during dispatch.
-                try {
-                    for (int i = mRouters.size(); --i >= 0; ) {
-                        MediaRouter router = mRouters.get(i).get();
-                        if (router == null) {
-                            mRouters.remove(i);
-                        } else {
-                            mTempCallbackRecords.addAll(router.mCallbackRecords);
-                        }
-                    }
-
-                    final int callbackCount = mTempCallbackRecords.size();
-                    for (int i = 0; i < callbackCount; i++) {
-                        invokeCallback(mTempCallbackRecords.get(i), what, obj, arg);
-                    }
-                } finally {
-                    mTempCallbackRecords.clear();
-                }
-            }
-
-            private void syncWithSystemProvider(int what, Object obj) {
-                switch (what) {
-                    case MSG_ROUTE_ADDED:
-                        mSystemProvider.onSyncRouteAdded((RouteInfo) obj);
-                        break;
-                    case MSG_ROUTE_REMOVED:
-                        mSystemProvider.onSyncRouteRemoved((RouteInfo) obj);
-                        break;
-                    case MSG_ROUTE_CHANGED:
-                        mSystemProvider.onSyncRouteChanged((RouteInfo) obj);
-                        break;
-                    case MSG_ROUTE_SELECTED:
-                        mSystemProvider.onSyncRouteSelected((RouteInfo) obj);
-                        break;
-                }
-            }
-
-            private void invokeCallback(CallbackRecord record, int what, Object obj, int arg) {
-                final MediaRouter router = record.mRouter;
-                final MediaRouter.Callback callback = record.mCallback;
-                switch (what & MSG_TYPE_MASK) {
-                    case MSG_TYPE_ROUTE: {
-                        final RouteInfo route = (RouteInfo)obj;
-                        if (!record.filterRouteEvent(route)) {
-                            break;
-                        }
-                        switch (what) {
-                            case MSG_ROUTE_ADDED:
-                                callback.onRouteAdded(router, route);
-                                break;
-                            case MSG_ROUTE_REMOVED:
-                                callback.onRouteRemoved(router, route);
-                                break;
-                            case MSG_ROUTE_CHANGED:
-                                callback.onRouteChanged(router, route);
-                                break;
-                            case MSG_ROUTE_VOLUME_CHANGED:
-                                callback.onRouteVolumeChanged(router, route);
-                                break;
-                            case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED:
-                                callback.onRoutePresentationDisplayChanged(router, route);
-                                break;
-                            case MSG_ROUTE_SELECTED:
-                                callback.onRouteSelected(router, route);
-                                break;
-                            case MSG_ROUTE_UNSELECTED:
-                                callback.onRouteUnselected(router, route, arg);
-                                break;
-                        }
-                        break;
-                    }
-                    case MSG_TYPE_PROVIDER: {
-                        final ProviderInfo provider = (ProviderInfo)obj;
-                        switch (what) {
-                            case MSG_PROVIDER_ADDED:
-                                callback.onProviderAdded(router, provider);
-                                break;
-                            case MSG_PROVIDER_REMOVED:
-                                callback.onProviderRemoved(router, provider);
-                                break;
-                            case MSG_PROVIDER_CHANGED:
-                                callback.onProviderChanged(router, provider);
-                                break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouterApi24.java b/android/support/v7/media/MediaRouterApi24.java
deleted file mode 100644
index 0b1eda9..0000000
--- a/android/support/v7/media/MediaRouterApi24.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.v7.media;
-
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(24)
-final class MediaRouterApi24 {
-    public static final class RouteInfo {
-        public static int getDeviceType(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getDeviceType();
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouterJellybean.java b/android/support/v7/media/MediaRouterJellybean.java
deleted file mode 100644
index 51bd498..0000000
--- a/android/support/v7/media/MediaRouterJellybean.java
+++ /dev/null
@@ -1,463 +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.v7.media;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-@RequiresApi(16)
-final class MediaRouterJellybean {
-    private static final String TAG = "MediaRouterJellybean";
-
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
-    public static final int DEVICE_OUT_BLUETOOTH = 0x80 | 0x100 | 0x200;
-
-    public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
-    public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
-    public static final int ROUTE_TYPE_USER = 0x00800000;
-
-    public static final int ALL_ROUTE_TYPES =
-            MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO
-            | MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO
-            | MediaRouterJellybean.ROUTE_TYPE_USER;
-
-    public static Object getMediaRouter(Context context) {
-        return context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static List getRoutes(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-        final int count = router.getRouteCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getRouteAt(i));
-        }
-        return out;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static List getCategories(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-        final int count = router.getCategoryCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getCategoryAt(i));
-        }
-        return out;
-    }
-
-    public static Object getSelectedRoute(Object routerObj, int type) {
-        return ((android.media.MediaRouter)routerObj).getSelectedRoute(type);
-    }
-
-    public static void selectRoute(Object routerObj, int types, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).selectRoute(types,
-                (android.media.MediaRouter.RouteInfo)routeObj);
-    }
-
-    public static void addCallback(Object routerObj, int types, Object callbackObj) {
-        ((android.media.MediaRouter)routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback)callbackObj);
-    }
-
-    public static void removeCallback(Object routerObj, Object callbackObj) {
-        ((android.media.MediaRouter)routerObj).removeCallback(
-                (android.media.MediaRouter.Callback)callbackObj);
-    }
-
-    public static Object createRouteCategory(Object routerObj,
-            String name, boolean isGroupable) {
-        return ((android.media.MediaRouter)routerObj).createRouteCategory(name, isGroupable);
-    }
-
-    public static Object createUserRoute(Object routerObj, Object categoryObj) {
-        return ((android.media.MediaRouter)routerObj).createUserRoute(
-                (android.media.MediaRouter.RouteCategory)categoryObj);
-    }
-
-    public static void addUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).addUserRoute(
-                (android.media.MediaRouter.UserRouteInfo)routeObj);
-    }
-
-    public static void removeUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).removeUserRoute(
-                (android.media.MediaRouter.UserRouteInfo)routeObj);
-    }
-
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
-    }
-
-    public static Object createVolumeCallback(VolumeCallback callback) {
-        return new VolumeCallbackProxy<VolumeCallback>(callback);
-    }
-
-    static boolean checkRoutedToBluetooth(Context context) {
-        try {
-            AudioManager audioManager = (AudioManager) context.getSystemService(
-                    Context.AUDIO_SERVICE);
-            Method method = audioManager.getClass().getDeclaredMethod(
-                    "getDevicesForStream", int.class);
-            int device = (Integer) method.invoke(audioManager, AudioManager.STREAM_MUSIC);
-            return (device & DEVICE_OUT_BLUETOOTH) != 0;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
-    public static final class RouteInfo {
-        public static CharSequence getName(Object routeObj, Context context) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context);
-        }
-
-        public static CharSequence getStatus(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getStatus();
-        }
-
-        public static int getSupportedTypes(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getSupportedTypes();
-        }
-
-        public static Object getCategory(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getCategory();
-        }
-
-        public static Drawable getIconDrawable(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getIconDrawable();
-        }
-
-        public static int getPlaybackType(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackType();
-        }
-
-        public static int getPlaybackStream(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackStream();
-        }
-
-        public static int getVolume(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolume();
-        }
-
-        public static int getVolumeMax(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeMax();
-        }
-
-        public static int getVolumeHandling(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeHandling();
-        }
-
-        public static Object getTag(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getTag();
-        }
-
-        public static void setTag(Object routeObj, Object tag) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).setTag(tag);
-        }
-
-        public static void requestSetVolume(Object routeObj, int volume) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).requestSetVolume(volume);
-        }
-
-        public static void requestUpdateVolume(Object routeObj, int direction) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).requestUpdateVolume(direction);
-        }
-
-        public static Object getGroup(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getGroup();
-        }
-
-        public static boolean isGroup(Object routeObj) {
-            return routeObj instanceof android.media.MediaRouter.RouteGroup;
-        }
-    }
-
-    public static final class RouteGroup {
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public static List getGroupedRoutes(Object groupObj) {
-            final android.media.MediaRouter.RouteGroup group =
-                    (android.media.MediaRouter.RouteGroup)groupObj;
-            final int count = group.getRouteCount();
-            List out = new ArrayList(count);
-            for (int i = 0; i < count; i++) {
-                out.add(group.getRouteAt(i));
-            }
-            return out;
-        }
-    }
-
-    public static final class UserRouteInfo {
-        public static void setName(Object routeObj, CharSequence name) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setName(name);
-        }
-
-        public static void setStatus(Object routeObj, CharSequence status) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setStatus(status);
-        }
-
-        public static void setIconDrawable(Object routeObj, Drawable icon) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setIconDrawable(icon);
-        }
-
-        public static void setPlaybackType(Object routeObj, int type) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackType(type);
-        }
-
-        public static void setPlaybackStream(Object routeObj, int stream) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackStream(stream);
-        }
-
-        public static void setVolume(Object routeObj, int volume) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolume(volume);
-        }
-
-        public static void setVolumeMax(Object routeObj, int volumeMax) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeMax(volumeMax);
-        }
-
-        public static void setVolumeHandling(Object routeObj, int volumeHandling) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeHandling(volumeHandling);
-        }
-
-        public static void setVolumeCallback(Object routeObj, Object volumeCallbackObj) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeCallback(
-                    (android.media.MediaRouter.VolumeCallback)volumeCallbackObj);
-        }
-
-        public static void setRemoteControlClient(Object routeObj, Object rccObj) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient(
-                    (android.media.RemoteControlClient)rccObj);
-        }
-    }
-
-    public static final class RouteCategory {
-        public static CharSequence getName(Object categoryObj, Context context) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context);
-        }
-
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public static List getRoutes(Object categoryObj) {
-            ArrayList out = new ArrayList();
-            ((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out);
-            return out;
-        }
-
-        public static int getSupportedTypes(Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes();
-        }
-
-        public static boolean isGroupable(Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable();
-        }
-    }
-
-    public static interface Callback {
-        public void onRouteSelected(int type, Object routeObj);
-        public void onRouteUnselected(int type, Object routeObj);
-        public void onRouteAdded(Object routeObj);
-        public void onRouteRemoved(Object routeObj);
-        public void onRouteChanged(Object routeObj);
-        public void onRouteGrouped(Object routeObj, Object groupObj, int index);
-        public void onRouteUngrouped(Object routeObj, Object groupObj);
-        public void onRouteVolumeChanged(Object routeObj);
-    }
-
-    public static interface VolumeCallback {
-        public void onVolumeSetRequest(Object routeObj, int volume);
-        public void onVolumeUpdateRequest(Object routeObj, int direction);
-    }
-
-    /**
-     * Workaround for limitations of selectRoute() on JB and JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class SelectRouteWorkaround {
-        private Method mSelectRouteIntMethod;
-
-        public SelectRouteWorkaround() {
-            if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
-                throw new UnsupportedOperationException();
-            }
-            try {
-                mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod(
-                        "selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class);
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public void selectRoute(Object routerObj, int types, Object routeObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo)routeObj;
-
-            int routeTypes = route.getSupportedTypes();
-            if ((routeTypes & ROUTE_TYPE_USER) == 0) {
-                // Handle non-user routes.
-                // On JB and JB MR1, the selectRoute() API only supports programmatically
-                // selecting user routes.  So instead we rely on the hidden selectRouteInt()
-                // method on these versions of the platform.
-                // This limitation was removed in JB MR2.
-                if (mSelectRouteIntMethod != null) {
-                    try {
-                        mSelectRouteIntMethod.invoke(router, types, route);
-                        return; // success!
-                    } catch (IllegalAccessException ex) {
-                        Log.w(TAG, "Cannot programmatically select non-user route.  "
-                                + "Media routing may not work.", ex);
-                    } catch (InvocationTargetException ex) {
-                        Log.w(TAG, "Cannot programmatically select non-user route.  "
-                                + "Media routing may not work.", ex);
-                    }
-                } else {
-                    Log.w(TAG, "Cannot programmatically select non-user route "
-                            + "because the platform is missing the selectRouteInt() "
-                            + "method.  Media routing may not work.");
-                }
-            }
-
-            // Default handling.
-            router.selectRoute(types, route);
-        }
-    }
-
-    /**
-     * Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class GetDefaultRouteWorkaround {
-        private Method mGetSystemAudioRouteMethod;
-
-        public GetDefaultRouteWorkaround() {
-            if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
-                throw new UnsupportedOperationException();
-            }
-            try {
-                mGetSystemAudioRouteMethod =
-                        android.media.MediaRouter.class.getMethod("getSystemAudioRoute");
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public Object getDefaultRoute(Object routerObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-
-            if (mGetSystemAudioRouteMethod != null) {
-                try {
-                    return mGetSystemAudioRouteMethod.invoke(router);
-                } catch (IllegalAccessException ex) {
-                } catch (InvocationTargetException ex) {
-                }
-            }
-
-            // Could not find the method or it does not work.
-            // Return the first route and hope for the best.
-            return router.getRouteAt(0);
-        }
-    }
-
-    static class CallbackProxy<T extends Callback>
-            extends android.media.MediaRouter.Callback {
-        protected final T mCallback;
-
-        public CallbackProxy(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onRouteSelected(android.media.MediaRouter router,
-                int type, android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteSelected(type, route);
-        }
-
-        @Override
-        public void onRouteUnselected(android.media.MediaRouter router,
-                int type, android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteUnselected(type, route);
-        }
-
-        @Override
-        public void onRouteAdded(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteAdded(route);
-        }
-
-        @Override
-        public void onRouteRemoved(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteRemoved(route);
-        }
-
-        @Override
-        public void onRouteChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteChanged(route);
-        }
-
-        @Override
-        public void onRouteGrouped(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route,
-                android.media.MediaRouter.RouteGroup group, int index) {
-            mCallback.onRouteGrouped(route, group, index);
-        }
-
-        @Override
-        public void onRouteUngrouped(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route,
-                android.media.MediaRouter.RouteGroup group) {
-            mCallback.onRouteUngrouped(route, group);
-        }
-
-        @Override
-        public void onRouteVolumeChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteVolumeChanged(route);
-        }
-    }
-
-    static class VolumeCallbackProxy<T extends VolumeCallback>
-            extends android.media.MediaRouter.VolumeCallback {
-        protected final T mCallback;
-
-        public VolumeCallbackProxy(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route,
-                int volume) {
-            mCallback.onVolumeSetRequest(route, volume);
-        }
-
-        @Override
-        public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route,
-                int direction) {
-            mCallback.onVolumeUpdateRequest(route, direction);
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouterJellybeanMr1.java b/android/support/v7/media/MediaRouterJellybeanMr1.java
deleted file mode 100644
index 6fc5ba5..0000000
--- a/android/support/v7/media/MediaRouterJellybeanMr1.java
+++ /dev/null
@@ -1,185 +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.v7.media;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Build;
-import android.os.Handler;
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.view.Display;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-@RequiresApi(17)
-final class MediaRouterJellybeanMr1 {
-    private static final String TAG = "MediaRouterJellybeanMr1";
-
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
-    }
-
-    public static final class RouteInfo {
-        public static boolean isEnabled(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled();
-        }
-
-        public static Display getPresentationDisplay(Object routeObj) {
-            // android.media.MediaRouter.RouteInfo.getPresentationDisplay() was
-            // added in API 17. However, some factory releases of JB MR1 missed it.
-            try {
-                return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay();
-            } catch (NoSuchMethodError ex) {
-                Log.w(TAG, "Cannot get presentation display for the route.", ex);
-            }
-            return null;
-        }
-    }
-
-    public static interface Callback extends MediaRouterJellybean.Callback {
-        public void onRoutePresentationDisplayChanged(Object routeObj);
-    }
-
-    /**
-     * Workaround the fact that the version of MediaRouter.addCallback() that accepts a
-     * flag to perform an active scan does not exist in JB MR1 so we need to force
-     * wifi display scans directly through the DisplayManager.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class ActiveScanWorkaround implements Runnable {
-        // Time between wifi display scans when actively scanning in milliseconds.
-        private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
-
-        private final DisplayManager mDisplayManager;
-        private final Handler mHandler;
-        private Method mScanWifiDisplaysMethod;
-
-        private boolean mActivelyScanningWifiDisplays;
-
-        public ActiveScanWorkaround(Context context, Handler handler) {
-            if (Build.VERSION.SDK_INT != 17) {
-                throw new UnsupportedOperationException();
-            }
-
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
-            mHandler = handler;
-            try {
-                mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays");
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public void setActiveScanRouteTypes(int routeTypes) {
-            // On JB MR1, there is no API to scan wifi display routes.
-            // Instead we must make a direct call into the DisplayManager to scan
-            // wifi displays on this version but only when live video routes are requested.
-            // See also the JellybeanMr2Impl implementation of this method.
-            // This was fixed in JB MR2 by adding a new overload of addCallback() to
-            // enable active scanning on request.
-            if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                if (!mActivelyScanningWifiDisplays) {
-                    if (mScanWifiDisplaysMethod != null) {
-                        mActivelyScanningWifiDisplays = true;
-                        mHandler.post(this);
-                    } else {
-                        Log.w(TAG, "Cannot scan for wifi displays because the "
-                                + "DisplayManager.scanWifiDisplays() method is "
-                                + "not available on this device.");
-                    }
-                }
-            } else {
-                if (mActivelyScanningWifiDisplays) {
-                    mActivelyScanningWifiDisplays = false;
-                    mHandler.removeCallbacks(this);
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            if (mActivelyScanningWifiDisplays) {
-                try {
-                    mScanWifiDisplaysMethod.invoke(mDisplayManager);
-                } catch (IllegalAccessException ex) {
-                    Log.w(TAG, "Cannot scan for wifi displays.", ex);
-                } catch (InvocationTargetException ex) {
-                    Log.w(TAG, "Cannot scan for wifi displays.", ex);
-                }
-                mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
-            }
-        }
-    }
-
-    /**
-     * Workaround the fact that the isConnecting() method does not exist in JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class IsConnectingWorkaround {
-        private Method mGetStatusCodeMethod;
-        private int mStatusConnecting;
-
-        public IsConnectingWorkaround() {
-            if (Build.VERSION.SDK_INT != 17) {
-                throw new UnsupportedOperationException();
-            }
-
-            try {
-                Field statusConnectingField =
-                        android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING");
-                mStatusConnecting = statusConnectingField.getInt(null);
-                mGetStatusCodeMethod =
-                        android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode");
-            } catch (NoSuchFieldException ex) {
-            } catch (NoSuchMethodException ex) {
-            } catch (IllegalAccessException ex) {
-            }
-        }
-
-        public boolean isConnecting(Object routeObj) {
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo)routeObj;
-
-            if (mGetStatusCodeMethod != null) {
-                try {
-                    int statusCode = (Integer)mGetStatusCodeMethod.invoke(route);
-                    return statusCode == mStatusConnecting;
-                } catch (IllegalAccessException ex) {
-                } catch (InvocationTargetException ex) {
-                }
-            }
-
-            // Assume not connecting.
-            return false;
-        }
-    }
-
-    static class CallbackProxy<T extends Callback>
-            extends MediaRouterJellybean.CallbackProxy<T> {
-        public CallbackProxy(T callback) {
-            super(callback);
-        }
-
-        @Override
-        public void onRoutePresentationDisplayChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRoutePresentationDisplayChanged(route);
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaRouterJellybeanMr2.java b/android/support/v7/media/MediaRouterJellybeanMr2.java
deleted file mode 100644
index 8abfc7f..0000000
--- a/android/support/v7/media/MediaRouterJellybeanMr2.java
+++ /dev/null
@@ -1,47 +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.v7.media;
-
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(18)
-final class MediaRouterJellybeanMr2 {
-    public static Object getDefaultRoute(Object routerObj) {
-        return ((android.media.MediaRouter)routerObj).getDefaultRoute();
-    }
-
-    public static void addCallback(Object routerObj, int types, Object callbackObj, int flags) {
-        ((android.media.MediaRouter)routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback)callbackObj, flags);
-    }
-
-    public static final class RouteInfo {
-        public static CharSequence getDescription(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getDescription();
-        }
-
-        public static boolean isConnecting(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).isConnecting();
-        }
-    }
-
-    public static final class UserRouteInfo {
-        public static void setDescription(Object routeObj, CharSequence description) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setDescription(description);
-        }
-    }
-}
diff --git a/android/support/v7/media/MediaSessionStatus.java b/android/support/v7/media/MediaSessionStatus.java
deleted file mode 100644
index 511fc55..0000000
--- a/android/support/v7/media/MediaSessionStatus.java
+++ /dev/null
@@ -1,244 +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.v7.media;
-
-import android.app.PendingIntent;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.support.v4.util.TimeUtils;
-
-/**
- * Describes the playback status of a media session.
- * <p>
- * This class is part of the remote playback protocol described by the
- * {@link MediaControlIntent MediaControlIntent} class.
- * </p><p>
- * When a media session is created, it is initially in the
- * {@link #SESSION_STATE_ACTIVE active} state.  When the media session ends
- * normally, it transitions to the {@link #SESSION_STATE_ENDED ended} state.
- * If the media session is invalidated due to another session forcibly taking
- * control of the route, then it transitions to the
- * {@link #SESSION_STATE_INVALIDATED invalidated} state.
- * Refer to the documentation of each state for an explanation of its meaning.
- * </p><p>
- * To monitor session status, the application should supply a {@link PendingIntent} to use as the
- * {@link MediaControlIntent#EXTRA_SESSION_STATUS_UPDATE_RECEIVER session status update receiver}
- * for a given {@link MediaControlIntent#ACTION_START_SESSION session start request}.
- * </p><p>
- * This object is immutable once created using a {@link Builder} instance.
- * </p>
- */
-public final class MediaSessionStatus {
-    static final String KEY_TIMESTAMP = "timestamp";
-    static final String KEY_SESSION_STATE = "sessionState";
-    static final String KEY_QUEUE_PAUSED = "queuePaused";
-    static final String KEY_EXTRAS = "extras";
-
-    final Bundle mBundle;
-
-    /**
-     * Session state: Active.
-     * <p>
-     * Indicates that the media session is active and in control of the route.
-     * </p>
-     */
-    public static final int SESSION_STATE_ACTIVE = 0;
-
-    /**
-     * Session state: Ended.
-     * <p>
-     * Indicates that the media session was ended normally using the
-     * {@link MediaControlIntent#ACTION_END_SESSION end session} action.
-     * </p><p>
-     * A terminated media session cannot be used anymore.  To play more media, the
-     * application must start a new session.
-     * </p>
-     */
-    public static final int SESSION_STATE_ENDED = 1;
-
-    /**
-     * Session state: Invalidated.
-     * <p>
-     * Indicates that the media session was invalidated involuntarily due to
-     * another session taking control of the route.
-     * </p><p>
-     * An invalidated media session cannot be used anymore.  To play more media, the
-     * application must start a new session.
-     * </p>
-     */
-    public static final int SESSION_STATE_INVALIDATED = 2;
-
-    MediaSessionStatus(Bundle bundle) {
-        mBundle = bundle;
-    }
-
-    /**
-     * Gets the timestamp associated with the status information in
-     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-     *
-     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
-     */
-    public long getTimestamp() {
-        return mBundle.getLong(KEY_TIMESTAMP);
-    }
-
-    /**
-     * Gets the session state.
-     *
-     * @return The session state.  One of {@link #SESSION_STATE_ACTIVE},
-     * {@link #SESSION_STATE_ENDED}, or {@link #SESSION_STATE_INVALIDATED}.
-     */
-    public int getSessionState() {
-        return mBundle.getInt(KEY_SESSION_STATE, SESSION_STATE_INVALIDATED);
-    }
-
-    /**
-     * Returns true if the session's queue is paused.
-     *
-     * @return True if the session's queue is paused.
-     */
-    public boolean isQueuePaused() {
-        return mBundle.getBoolean(KEY_QUEUE_PAUSED);
-    }
-
-    /**
-     * Gets a bundle of extras for this status object.
-     * The extras will be ignored by the media router but they may be used
-     * by applications.
-     */
-    public Bundle getExtras() {
-        return mBundle.getBundle(KEY_EXTRAS);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder result = new StringBuilder();
-        result.append("MediaSessionStatus{ ");
-        result.append("timestamp=");
-        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
-        result.append(" ms ago");
-        result.append(", sessionState=").append(sessionStateToString(getSessionState()));
-        result.append(", queuePaused=").append(isQueuePaused());
-        result.append(", extras=").append(getExtras());
-        result.append(" }");
-        return result.toString();
-    }
-
-    private static String sessionStateToString(int sessionState) {
-        switch (sessionState) {
-            case SESSION_STATE_ACTIVE:
-                return "active";
-            case SESSION_STATE_ENDED:
-                return "ended";
-            case SESSION_STATE_INVALIDATED:
-                return "invalidated";
-        }
-        return Integer.toString(sessionState);
-    }
-
-    /**
-     * Converts this object to a bundle for serialization.
-     *
-     * @return The contents of the object represented as a bundle.
-     */
-    public Bundle asBundle() {
-        return mBundle;
-    }
-
-    /**
-     * Creates an instance from a bundle.
-     *
-     * @param bundle The bundle, or null if none.
-     * @return The new instance, or null if the bundle was null.
-     */
-    public static MediaSessionStatus fromBundle(Bundle bundle) {
-        return bundle != null ? new MediaSessionStatus(bundle) : null;
-    }
-
-    /**
-     * Builder for {@link MediaSessionStatus media session status objects}.
-     */
-    public static final class Builder {
-        private final Bundle mBundle;
-
-        /**
-         * Creates a media session status builder using the current time as the
-         * reference timestamp.
-         *
-         * @param sessionState The session state.
-         */
-        public Builder(int sessionState) {
-            mBundle = new Bundle();
-            setTimestamp(SystemClock.elapsedRealtime());
-            setSessionState(sessionState);
-        }
-
-        /**
-         * Creates a media session status builder whose initial contents are
-         * copied from an existing status.
-         */
-        public Builder(MediaSessionStatus status) {
-            if (status == null) {
-                throw new IllegalArgumentException("status must not be null");
-            }
-
-            mBundle = new Bundle(status.mBundle);
-        }
-
-        /**
-         * Sets the timestamp associated with the status information in
-         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
-         */
-        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
-            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
-            return this;
-        }
-
-        /**
-         * Sets the session state.
-         */
-        public Builder setSessionState(int sessionState) {
-            mBundle.putInt(KEY_SESSION_STATE, sessionState);
-            return this;
-        }
-
-        /**
-         * Sets whether the queue is paused.
-         */
-        public Builder setQueuePaused(boolean queuePaused) {
-            mBundle.putBoolean(KEY_QUEUE_PAUSED, queuePaused);
-            return this;
-        }
-
-        /**
-         * Sets a bundle of extras for this status object.
-         * The extras will be ignored by the media router but they may be used
-         * by applications.
-         */
-        public Builder setExtras(Bundle extras) {
-            mBundle.putBundle(KEY_EXTRAS, extras);
-            return this;
-        }
-
-        /**
-         * Builds the {@link MediaSessionStatus media session status object}.
-         */
-        public MediaSessionStatus build() {
-            return new MediaSessionStatus(mBundle);
-        }
-    }
-}
diff --git a/android/support/v7/media/RegisteredMediaRouteProvider.java b/android/support/v7/media/RegisteredMediaRouteProvider.java
deleted file mode 100644
index 1481ce6..0000000
--- a/android/support/v7/media/RegisteredMediaRouteProvider.java
+++ /dev/null
@@ -1,730 +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.v7.media;
-
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_ID;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_ROUTE_LIBRARY_GROUP;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_UNSELECT_REASON;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_DATA_VOLUME;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_CREATE_ROUTE_CONTROLLER;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_REGISTER;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .CLIENT_MSG_RELEASE_ROUTE_CONTROLLER;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_ROUTE_CONTROL_REQUEST;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_SELECT_ROUTE;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_SET_DISCOVERY_REQUEST;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_SET_ROUTE_VOLUME;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_UNREGISTER;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_UNSELECT_ROUTE;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_MSG_UPDATE_ROUTE_VOLUME;
-import static android.support.v7.media.MediaRouteProviderProtocol.CLIENT_VERSION_CURRENT;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_DATA_ERROR;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_FAILED;
-import static android.support.v7.media.MediaRouteProviderProtocol
-        .SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_DESCRIPTOR_CHANGED;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_GENERIC_FAILURE;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_GENERIC_SUCCESS;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_MSG_REGISTERED;
-import static android.support.v7.media.MediaRouteProviderProtocol.SERVICE_VERSION_1;
-import static android.support.v7.media.MediaRouteProviderProtocol.isValidRemoteMessenger;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.v7.media.MediaRouter.ControlRequestCallback;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Maintains a connection to a particular media route provider service.
- */
-final class RegisteredMediaRouteProvider extends MediaRouteProvider
-        implements ServiceConnection {
-    static final String TAG = "MediaRouteProviderProxy";  // max. 23 chars
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final ComponentName mComponentName;
-    final PrivateHandler mPrivateHandler;
-    private final ArrayList<Controller> mControllers = new ArrayList<Controller>();
-
-    private boolean mStarted;
-    private boolean mBound;
-    private Connection mActiveConnection;
-    private boolean mConnectionReady;
-
-    public RegisteredMediaRouteProvider(Context context, ComponentName componentName) {
-        super(context, new ProviderMetadata(componentName));
-
-        mComponentName = componentName;
-        mPrivateHandler = new PrivateHandler();
-    }
-
-    @Override
-    public RouteController onCreateRouteController(@NonNull String routeId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        return createRouteController(routeId, null);
-    }
-
-    @Override
-    public RouteController onCreateRouteController(
-            @NonNull String routeId, @NonNull String routeGroupId) {
-        if (routeId == null) {
-            throw new IllegalArgumentException("routeId cannot be null");
-        }
-        if (routeGroupId == null) {
-            throw new IllegalArgumentException("routeGroupId cannot be null");
-        }
-        return createRouteController(routeId, routeGroupId);
-    }
-
-    @Override
-    public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
-        if (mConnectionReady) {
-            mActiveConnection.setDiscoveryRequest(request);
-        }
-        updateBinding();
-    }
-
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-        if (DEBUG) {
-            Log.d(TAG, this + ": Connected");
-        }
-
-        if (mBound) {
-            disconnect();
-
-            Messenger messenger = (service != null ? new Messenger(service) : null);
-            if (isValidRemoteMessenger(messenger)) {
-                Connection connection = new Connection(messenger);
-                if (connection.register()) {
-                    mActiveConnection = connection;
-                } else {
-                    if (DEBUG) {
-                        Log.d(TAG, this + ": Registration failed");
-                    }
-                }
-            } else {
-                Log.e(TAG, this + ": Service returned invalid messenger binder");
-            }
-        }
-    }
-
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
-        if (DEBUG) {
-            Log.d(TAG, this + ": Service disconnected");
-        }
-        disconnect();
-    }
-
-    @Override
-    public String toString() {
-        return "Service connection " + mComponentName.flattenToShortString();
-    }
-
-    public boolean hasComponentName(String packageName, String className) {
-        return mComponentName.getPackageName().equals(packageName)
-                && mComponentName.getClassName().equals(className);
-    }
-
-    public void start() {
-        if (!mStarted) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Starting");
-            }
-
-            mStarted = true;
-            updateBinding();
-        }
-    }
-
-    public void stop() {
-        if (mStarted) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Stopping");
-            }
-
-            mStarted = false;
-            updateBinding();
-        }
-    }
-
-    public void rebindIfDisconnected() {
-        if (mActiveConnection == null && shouldBind()) {
-            unbind();
-            bind();
-        }
-    }
-
-    private void updateBinding() {
-        if (shouldBind()) {
-            bind();
-        } else {
-            unbind();
-        }
-    }
-
-    private boolean shouldBind() {
-        if (mStarted) {
-            // Bind whenever there is a discovery request.
-            if (getDiscoveryRequest() != null) {
-                return true;
-            }
-
-            // Bind whenever the application has an active route controller.
-            // This means that one of this provider's routes is selected.
-            if (!mControllers.isEmpty()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void bind() {
-        if (!mBound) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Binding");
-            }
-
-            Intent service = new Intent(MediaRouteProviderProtocol.SERVICE_INTERFACE);
-            service.setComponent(mComponentName);
-            try {
-                mBound = getContext().bindService(service, this, Context.BIND_AUTO_CREATE);
-                if (!mBound && DEBUG) {
-                    Log.d(TAG, this + ": Bind failed");
-                }
-            } catch (SecurityException ex) {
-                if (DEBUG) {
-                    Log.d(TAG, this + ": Bind failed", ex);
-                }
-            }
-        }
-    }
-
-    private void unbind() {
-        if (mBound) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Unbinding");
-            }
-
-            mBound = false;
-            disconnect();
-            getContext().unbindService(this);
-        }
-    }
-
-    private RouteController createRouteController(String routeId, String routeGroupId) {
-        MediaRouteProviderDescriptor descriptor = getDescriptor();
-        if (descriptor != null) {
-            List<MediaRouteDescriptor> routes = descriptor.getRoutes();
-            final int count = routes.size();
-            for (int i = 0; i < count; i++) {
-                final MediaRouteDescriptor route = routes.get(i);
-                if (route.getId().equals(routeId)) {
-                    Controller controller = new Controller(routeId, routeGroupId);
-                    mControllers.add(controller);
-                    if (mConnectionReady) {
-                        controller.attachConnection(mActiveConnection);
-                    }
-                    updateBinding();
-                    return controller;
-                }
-            }
-        }
-        return null;
-    }
-
-    void onConnectionReady(Connection connection) {
-        if (mActiveConnection == connection) {
-            mConnectionReady = true;
-            attachControllersToConnection();
-
-            MediaRouteDiscoveryRequest request = getDiscoveryRequest();
-            if (request != null) {
-                mActiveConnection.setDiscoveryRequest(request);
-            }
-        }
-    }
-
-    void onConnectionDied(Connection connection) {
-        if (mActiveConnection == connection) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Service connection died");
-            }
-            disconnect();
-        }
-    }
-
-    void onConnectionError(Connection connection, String error) {
-        if (mActiveConnection == connection) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Service connection error - " + error);
-            }
-            unbind();
-        }
-    }
-
-    void onConnectionDescriptorChanged(Connection connection,
-            MediaRouteProviderDescriptor descriptor) {
-        if (mActiveConnection == connection) {
-            if (DEBUG) {
-                Log.d(TAG, this + ": Descriptor changed, descriptor=" + descriptor);
-            }
-            setDescriptor(descriptor);
-        }
-    }
-
-    private void disconnect() {
-        if (mActiveConnection != null) {
-            setDescriptor(null);
-            mConnectionReady = false;
-            detachControllersFromConnection();
-            mActiveConnection.dispose();
-            mActiveConnection = null;
-        }
-    }
-
-    void onControllerReleased(Controller controller) {
-        mControllers.remove(controller);
-        controller.detachConnection();
-        updateBinding();
-    }
-
-    private void attachControllersToConnection() {
-        int count = mControllers.size();
-        for (int i = 0; i < count; i++) {
-            mControllers.get(i).attachConnection(mActiveConnection);
-        }
-    }
-
-    private void detachControllersFromConnection() {
-        int count = mControllers.size();
-        for (int i = 0; i < count; i++) {
-            mControllers.get(i).detachConnection();
-        }
-    }
-
-    private final class Controller extends RouteController {
-        private final String mRouteId;
-        private final String mRouteGroupId;
-
-        private boolean mSelected;
-        private int mPendingSetVolume = -1;
-        private int mPendingUpdateVolumeDelta;
-
-        private Connection mConnection;
-        private int mControllerId;
-
-        public Controller(String routeId, String routeGroupId) {
-            mRouteId = routeId;
-            mRouteGroupId = routeGroupId;
-        }
-
-        public void attachConnection(Connection connection) {
-            mConnection = connection;
-            mControllerId = connection.createRouteController(mRouteId, mRouteGroupId);
-            if (mSelected) {
-                connection.selectRoute(mControllerId);
-                if (mPendingSetVolume >= 0) {
-                    connection.setVolume(mControllerId, mPendingSetVolume);
-                    mPendingSetVolume = -1;
-                }
-                if (mPendingUpdateVolumeDelta != 0) {
-                    connection.updateVolume(mControllerId, mPendingUpdateVolumeDelta);
-                    mPendingUpdateVolumeDelta = 0;
-                }
-            }
-        }
-
-        public void detachConnection() {
-            if (mConnection != null) {
-                mConnection.releaseRouteController(mControllerId);
-                mConnection = null;
-                mControllerId = 0;
-            }
-        }
-
-        @Override
-        public void onRelease() {
-            onControllerReleased(this);
-        }
-
-        @Override
-        public void onSelect() {
-            mSelected = true;
-            if (mConnection != null) {
-                mConnection.selectRoute(mControllerId);
-            }
-        }
-
-        @Override
-        public void onUnselect() {
-            onUnselect(MediaRouter.UNSELECT_REASON_UNKNOWN);
-        }
-
-        @Override
-        public void onUnselect(int reason) {
-            mSelected = false;
-            if (mConnection != null) {
-                mConnection.unselectRoute(mControllerId, reason);
-            }
-        }
-
-        @Override
-        public void onSetVolume(int volume) {
-            if (mConnection != null) {
-                mConnection.setVolume(mControllerId, volume);
-            } else {
-                mPendingSetVolume = volume;
-                mPendingUpdateVolumeDelta = 0;
-            }
-        }
-
-        @Override
-        public void onUpdateVolume(int delta) {
-            if (mConnection != null) {
-                mConnection.updateVolume(mControllerId, delta);
-            } else {
-                mPendingUpdateVolumeDelta += delta;
-            }
-        }
-
-        @Override
-        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
-            if (mConnection != null) {
-                return mConnection.sendControlRequest(mControllerId, intent, callback);
-            }
-            return false;
-        }
-    }
-
-    private final class Connection implements DeathRecipient {
-        private final Messenger mServiceMessenger;
-        private final ReceiveHandler mReceiveHandler;
-        private final Messenger mReceiveMessenger;
-
-        private int mNextRequestId = 1;
-        private int mNextControllerId = 1;
-        private int mServiceVersion; // non-zero when registration complete
-
-        private int mPendingRegisterRequestId;
-        private final SparseArray<ControlRequestCallback> mPendingCallbacks =
-                new SparseArray<ControlRequestCallback>();
-
-        public Connection(Messenger serviceMessenger) {
-            mServiceMessenger = serviceMessenger;
-            mReceiveHandler = new ReceiveHandler(this);
-            mReceiveMessenger = new Messenger(mReceiveHandler);
-        }
-
-        public boolean register() {
-            mPendingRegisterRequestId = mNextRequestId++;
-            if (!sendRequest(CLIENT_MSG_REGISTER,
-                    mPendingRegisterRequestId,
-                    CLIENT_VERSION_CURRENT, null, null)) {
-                return false;
-            }
-
-            try {
-                mServiceMessenger.getBinder().linkToDeath(this, 0);
-                return true;
-            } catch (RemoteException ex) {
-                binderDied();
-            }
-            return false;
-        }
-
-        public void dispose() {
-            sendRequest(CLIENT_MSG_UNREGISTER, 0, 0, null, null);
-            mReceiveHandler.dispose();
-            mServiceMessenger.getBinder().unlinkToDeath(this, 0);
-
-            mPrivateHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    failPendingCallbacks();
-                }
-            });
-        }
-
-        void failPendingCallbacks() {
-            int count = 0;
-            for (int i = 0; i < mPendingCallbacks.size(); i++) {
-                mPendingCallbacks.valueAt(i).onError(null, null);
-            }
-            mPendingCallbacks.clear();
-        }
-
-        public boolean onGenericFailure(int requestId) {
-            if (requestId == mPendingRegisterRequestId) {
-                mPendingRegisterRequestId = 0;
-                onConnectionError(this, "Registration failed");
-            }
-            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
-            if (callback != null) {
-                mPendingCallbacks.remove(requestId);
-                callback.onError(null, null);
-            }
-            return true;
-        }
-
-        public boolean onGenericSuccess(int requestId) {
-            return true;
-        }
-
-        public boolean onRegistered(int requestId, int serviceVersion,
-                Bundle descriptorBundle) {
-            if (mServiceVersion == 0
-                    && requestId == mPendingRegisterRequestId
-                    && serviceVersion >= SERVICE_VERSION_1) {
-                mPendingRegisterRequestId = 0;
-                mServiceVersion = serviceVersion;
-                onConnectionDescriptorChanged(this,
-                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
-                onConnectionReady(this);
-                return true;
-            }
-            return false;
-        }
-
-        public boolean onDescriptorChanged(Bundle descriptorBundle) {
-            if (mServiceVersion != 0) {
-                onConnectionDescriptorChanged(this,
-                        MediaRouteProviderDescriptor.fromBundle(descriptorBundle));
-                return true;
-            }
-            return false;
-        }
-
-        public boolean onControlRequestSucceeded(int requestId, Bundle data) {
-            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
-            if (callback != null) {
-                mPendingCallbacks.remove(requestId);
-                callback.onResult(data);
-                return true;
-            }
-            return false;
-        }
-
-        public boolean onControlRequestFailed(int requestId, String error, Bundle data) {
-            ControlRequestCallback callback = mPendingCallbacks.get(requestId);
-            if (callback != null) {
-                mPendingCallbacks.remove(requestId);
-                callback.onError(error, data);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void binderDied() {
-            mPrivateHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    onConnectionDied(Connection.this);
-                }
-            });
-        }
-
-        public int createRouteController(String routeId, String routeGroupId) {
-            int controllerId = mNextControllerId++;
-            Bundle data = new Bundle();
-            data.putString(CLIENT_DATA_ROUTE_ID, routeId);
-            data.putString(CLIENT_DATA_ROUTE_LIBRARY_GROUP, routeGroupId);
-            sendRequest(CLIENT_MSG_CREATE_ROUTE_CONTROLLER,
-                    mNextRequestId++, controllerId, null, data);
-            return controllerId;
-        }
-
-        public void releaseRouteController(int controllerId) {
-            sendRequest(CLIENT_MSG_RELEASE_ROUTE_CONTROLLER,
-                    mNextRequestId++, controllerId, null, null);
-        }
-
-        public void selectRoute(int controllerId) {
-            sendRequest(CLIENT_MSG_SELECT_ROUTE,
-                    mNextRequestId++, controllerId, null, null);
-        }
-
-        public void unselectRoute(int controllerId, int reason) {
-            Bundle extras = new Bundle();
-            extras.putInt(CLIENT_DATA_UNSELECT_REASON, reason);
-            sendRequest(CLIENT_MSG_UNSELECT_ROUTE,
-                    mNextRequestId++, controllerId, null, extras);
-        }
-
-        public void setVolume(int controllerId, int volume) {
-            Bundle data = new Bundle();
-            data.putInt(CLIENT_DATA_VOLUME, volume);
-            sendRequest(CLIENT_MSG_SET_ROUTE_VOLUME,
-                    mNextRequestId++, controllerId, null, data);
-        }
-
-        public void updateVolume(int controllerId, int delta) {
-            Bundle data = new Bundle();
-            data.putInt(CLIENT_DATA_VOLUME, delta);
-            sendRequest(CLIENT_MSG_UPDATE_ROUTE_VOLUME,
-                    mNextRequestId++, controllerId, null, data);
-        }
-
-        public boolean sendControlRequest(int controllerId, Intent intent,
-                ControlRequestCallback callback) {
-            int requestId = mNextRequestId++;
-            if (sendRequest(CLIENT_MSG_ROUTE_CONTROL_REQUEST,
-                    requestId, controllerId, intent, null)) {
-                if (callback != null) {
-                    mPendingCallbacks.put(requestId, callback);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        public void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
-            sendRequest(CLIENT_MSG_SET_DISCOVERY_REQUEST,
-                    mNextRequestId++, 0, request != null ? request.asBundle() : null, null);
-        }
-
-        private boolean sendRequest(int what, int requestId, int arg, Object obj, Bundle data) {
-            Message msg = Message.obtain();
-            msg.what = what;
-            msg.arg1 = requestId;
-            msg.arg2 = arg;
-            msg.obj = obj;
-            msg.setData(data);
-            msg.replyTo = mReceiveMessenger;
-            try {
-                mServiceMessenger.send(msg);
-                return true;
-            } catch (DeadObjectException ex) {
-                // The service died.
-            } catch (RemoteException ex) {
-                if (what != CLIENT_MSG_UNREGISTER) {
-                    Log.e(TAG, "Could not send message to service.", ex);
-                }
-            }
-            return false;
-        }
-    }
-
-    private static final class PrivateHandler extends Handler {
-        PrivateHandler() {
-        }
-    }
-
-    /**
-     * Handler that receives messages from the server.
-     * <p>
-     * This inner class is static and only retains a weak reference to the connection
-     * to prevent the client from being leaked in case the service is holding an
-     * active reference to the client's messenger.
-     * </p><p>
-     * This handler should not be used to handle any messages other than those
-     * that come from the service.
-     * </p>
-     */
-    private static final class ReceiveHandler extends Handler {
-        private final WeakReference<Connection> mConnectionRef;
-
-        public ReceiveHandler(Connection connection) {
-            mConnectionRef = new WeakReference<Connection>(connection);
-        }
-
-        public void dispose() {
-            mConnectionRef.clear();
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            Connection connection = mConnectionRef.get();
-            if (connection != null) {
-                final int what = msg.what;
-                final int requestId = msg.arg1;
-                final int arg = msg.arg2;
-                final Object obj = msg.obj;
-                final Bundle data = msg.peekData();
-                if (!processMessage(connection, what, requestId, arg, obj, data)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Unhandled message from server: " + msg);
-                    }
-                }
-            }
-        }
-
-        private boolean processMessage(Connection connection,
-                int what, int requestId, int arg, Object obj, Bundle data) {
-            switch (what) {
-                case SERVICE_MSG_GENERIC_FAILURE:
-                    connection.onGenericFailure(requestId);
-                    return true;
-
-                case SERVICE_MSG_GENERIC_SUCCESS:
-                    connection.onGenericSuccess(requestId);
-                    return true;
-
-                case SERVICE_MSG_REGISTERED:
-                    if (obj == null || obj instanceof Bundle) {
-                        return connection.onRegistered(requestId, arg, (Bundle)obj);
-                    }
-                    break;
-
-                case SERVICE_MSG_DESCRIPTOR_CHANGED:
-                    if (obj == null || obj instanceof Bundle) {
-                        return connection.onDescriptorChanged((Bundle)obj);
-                    }
-                    break;
-
-                case SERVICE_MSG_CONTROL_REQUEST_SUCCEEDED:
-                    if (obj == null || obj instanceof Bundle) {
-                        return connection.onControlRequestSucceeded(
-                                requestId, (Bundle)obj);
-                    }
-                    break;
-
-                case SERVICE_MSG_CONTROL_REQUEST_FAILED:
-                    if (obj == null || obj instanceof Bundle) {
-                        String error = (data == null ? null :
-                                data.getString(SERVICE_DATA_ERROR));
-                        return connection.onControlRequestFailed(
-                                requestId, error, (Bundle)obj);
-                    }
-                    break;
-            }
-            return false;
-        }
-    }
-}
diff --git a/android/support/v7/media/RegisteredMediaRouteProviderWatcher.java b/android/support/v7/media/RegisteredMediaRouteProviderWatcher.java
deleted file mode 100644
index e763597..0000000
--- a/android/support/v7/media/RegisteredMediaRouteProviderWatcher.java
+++ /dev/null
@@ -1,157 +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.v7.media;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Handler;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Watches for media route provider services to be installed.
- * Adds a provider to the media router for each registered service.
- *
- * @see RegisteredMediaRouteProvider
- */
-final class RegisteredMediaRouteProviderWatcher {
-    private final Context mContext;
-    private final Callback mCallback;
-    private final Handler mHandler;
-    private final PackageManager mPackageManager;
-
-    private final ArrayList<RegisteredMediaRouteProvider> mProviders =
-            new ArrayList<RegisteredMediaRouteProvider>();
-    private boolean mRunning;
-
-    public RegisteredMediaRouteProviderWatcher(Context context, Callback callback) {
-        mContext = context;
-        mCallback = callback;
-        mHandler = new Handler();
-        mPackageManager = context.getPackageManager();
-    }
-
-    public void start() {
-        if (!mRunning) {
-            mRunning = true;
-
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-            filter.addDataScheme("package");
-            mContext.registerReceiver(mScanPackagesReceiver, filter, null, mHandler);
-
-            // Scan packages.
-            // Also has the side-effect of restarting providers if needed.
-            mHandler.post(mScanPackagesRunnable);
-        }
-    }
-
-    public void stop() {
-        if (mRunning) {
-            mRunning = false;
-
-            mContext.unregisterReceiver(mScanPackagesReceiver);
-            mHandler.removeCallbacks(mScanPackagesRunnable);
-
-            // Stop all providers.
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                mProviders.get(i).stop();
-            }
-        }
-    }
-
-    void scanPackages() {
-        if (!mRunning) {
-            return;
-        }
-
-        // Add providers for all new services.
-        // Reorder the list so that providers left at the end will be the ones to remove.
-        int targetIndex = 0;
-        Intent intent = new Intent(MediaRouteProviderService.SERVICE_INTERFACE);
-        for (ResolveInfo resolveInfo : mPackageManager.queryIntentServices(intent, 0)) {
-            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (serviceInfo != null) {
-                int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
-                if (sourceIndex < 0) {
-                    RegisteredMediaRouteProvider provider =
-                            new RegisteredMediaRouteProvider(mContext,
-                            new ComponentName(serviceInfo.packageName, serviceInfo.name));
-                    provider.start();
-                    mProviders.add(targetIndex++, provider);
-                    mCallback.addProvider(provider);
-                } else if (sourceIndex >= targetIndex) {
-                    RegisteredMediaRouteProvider provider = mProviders.get(sourceIndex);
-                    provider.start(); // restart the provider if needed
-                    provider.rebindIfDisconnected();
-                    Collections.swap(mProviders, sourceIndex, targetIndex++);
-                }
-            }
-        }
-
-        // Remove providers for missing services.
-        if (targetIndex < mProviders.size()) {
-            for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
-                RegisteredMediaRouteProvider provider = mProviders.get(i);
-                mCallback.removeProvider(provider);
-                mProviders.remove(provider);
-                provider.stop();
-            }
-        }
-    }
-
-    private int findProvider(String packageName, String className) {
-        int count = mProviders.size();
-        for (int i = 0; i < count; i++) {
-            RegisteredMediaRouteProvider provider = mProviders.get(i);
-            if (provider.hasComponentName(packageName, className)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            scanPackages();
-        }
-    };
-
-    private final Runnable mScanPackagesRunnable = new Runnable() {
-        @Override
-        public void run() {
-            scanPackages();
-        }
-    };
-
-    public interface Callback {
-        void addProvider(MediaRouteProvider provider);
-        void removeProvider(MediaRouteProvider provider);
-    }
-}
diff --git a/android/support/v7/media/RemoteControlClientCompat.java b/android/support/v7/media/RemoteControlClientCompat.java
deleted file mode 100644
index 085d6ff..0000000
--- a/android/support/v7/media/RemoteControlClientCompat.java
+++ /dev/null
@@ -1,190 +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.v7.media;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.os.Build;
-import android.support.annotation.RequiresApi;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Provides access to features of the remote control client.
- *
- * Hidden for now but we might want to make this available to applications
- * in the future.
- */
-abstract class RemoteControlClientCompat {
-    protected final Context mContext;
-    protected final Object mRcc;
-    protected VolumeCallback mVolumeCallback;
-
-    protected RemoteControlClientCompat(Context context, Object rcc) {
-        mContext = context;
-        mRcc = rcc;
-    }
-
-    public static RemoteControlClientCompat obtain(Context context, Object rcc) {
-        if (Build.VERSION.SDK_INT >= 16) {
-            return new JellybeanImpl(context, rcc);
-        }
-        return new LegacyImpl(context, rcc);
-    }
-
-    public Object getRemoteControlClient() {
-        return mRcc;
-    }
-
-    /**
-     * Sets the current playback information.
-     * Must be called at least once to attach to the remote control client.
-     *
-     * @param info The playback information.  Must not be null.
-     */
-    public void setPlaybackInfo(PlaybackInfo info) {
-    }
-
-    /**
-     * Sets a callback to receive volume change requests from the remote control client.
-     *
-     * @param callback The volume callback to use or null if none.
-     */
-    public void setVolumeCallback(VolumeCallback callback) {
-        mVolumeCallback = callback;
-    }
-
-    /**
-     * Specifies information about the playback.
-     */
-    public static final class PlaybackInfo {
-        public int volume;
-        public int volumeMax;
-        public int volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
-        public int playbackStream = AudioManager.STREAM_MUSIC;
-        public int playbackType = MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE;
-    }
-
-    /**
-     * Called when volume updates are requested by the remote control client.
-     */
-    public interface VolumeCallback {
-        /**
-         * Called when the volume should be increased or decreased.
-         *
-         * @param direction An integer indicating whether the volume is to be increased
-         * (positive value) or decreased (negative value).
-         * For bundled changes, the absolute value indicates the number of changes
-         * in the same direction, e.g. +3 corresponds to three "volume up" changes.
-         */
-        public void onVolumeUpdateRequest(int direction);
-
-        /**
-         * Called when the volume for the route should be set to the given value.
-         *
-         * @param volume An integer indicating the new volume value that should be used,
-         * always between 0 and the value set by {@link PlaybackInfo#volumeMax}.
-         */
-        public void onVolumeSetRequest(int volume);
-    }
-
-    /**
-     * Legacy implementation for platform versions prior to Jellybean.
-     * Does nothing.
-     */
-    static class LegacyImpl extends RemoteControlClientCompat {
-        public LegacyImpl(Context context, Object rcc) {
-            super(context, rcc);
-        }
-    }
-
-    /**
-     * Implementation for Jellybean.
-     *
-     * The basic idea of this implementation is to attach the RCC to a UserRouteInfo
-     * in order to hook up stream metadata and volume callbacks because there is no
-     * 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;
-        private final Object mUserRouteObj;
-        private boolean mRegistered;
-
-        public JellybeanImpl(Context context, Object rcc) {
-            super(context, rcc);
-
-            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
-            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
-                    mRouterObj, "", false);
-            mUserRouteObj = MediaRouterJellybean.createUserRoute(
-                    mRouterObj, mUserRouteCategoryObj);
-        }
-
-        @Override
-        public void setPlaybackInfo(PlaybackInfo info) {
-            MediaRouterJellybean.UserRouteInfo.setVolume(
-                    mUserRouteObj, info.volume);
-            MediaRouterJellybean.UserRouteInfo.setVolumeMax(
-                    mUserRouteObj, info.volumeMax);
-            MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
-                    mUserRouteObj, info.volumeHandling);
-            MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
-                    mUserRouteObj, info.playbackStream);
-            MediaRouterJellybean.UserRouteInfo.setPlaybackType(
-                    mUserRouteObj, info.playbackType);
-
-            if (!mRegistered) {
-                mRegistered = true;
-                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRouteObj,
-                        MediaRouterJellybean.createVolumeCallback(
-                                new VolumeCallbackWrapper(this)));
-                MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRouteObj, mRcc);
-            }
-        }
-
-        private static final class VolumeCallbackWrapper
-                implements MediaRouterJellybean.VolumeCallback {
-            // Unfortunately, the framework never unregisters its volume observer from
-            // the audio service so the UserRouteInfo object may leak along with
-            // any callbacks that we attach to it.  Use a weak reference to prevent
-            // the volume callback from holding strong references to anything important.
-            private final WeakReference<JellybeanImpl> mImplWeak;
-
-            public VolumeCallbackWrapper(JellybeanImpl impl) {
-                mImplWeak = new WeakReference<JellybeanImpl>(impl);
-            }
-
-            @Override
-            public void onVolumeUpdateRequest(Object routeObj, int direction) {
-                JellybeanImpl impl = mImplWeak.get();
-                if (impl != null && impl.mVolumeCallback != null) {
-                    impl.mVolumeCallback.onVolumeUpdateRequest(direction);
-                }
-            }
-
-            @Override
-            public void onVolumeSetRequest(Object routeObj, int volume) {
-                JellybeanImpl impl = mImplWeak.get();
-                if (impl != null && impl.mVolumeCallback != null) {
-                    impl.mVolumeCallback.onVolumeSetRequest(volume);
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v7/media/RemotePlaybackClient.java b/android/support/v7/media/RemotePlaybackClient.java
deleted file mode 100644
index fb05ac7..0000000
--- a/android/support/v7/media/RemotePlaybackClient.java
+++ /dev/null
@@ -1,1044 +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.v7.media;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.util.ObjectsCompat;
-import android.util.Log;
-
-/**
- * A helper class for playing media on remote routes using the remote playback protocol
- * defined by {@link MediaControlIntent}.
- * <p>
- * The client maintains session state and offers a simplified interface for issuing
- * remote playback media control intents to a single route.
- * </p>
- */
-public class RemotePlaybackClient {
-    static final String TAG = "RemotePlaybackClient";
-    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final Context mContext;
-    private final MediaRouter.RouteInfo mRoute;
-    private final ActionReceiver mActionReceiver;
-    private final PendingIntent mItemStatusPendingIntent;
-    private final PendingIntent mSessionStatusPendingIntent;
-    private final PendingIntent mMessagePendingIntent;
-
-    private boolean mRouteSupportsRemotePlayback;
-    private boolean mRouteSupportsQueuing;
-    private boolean mRouteSupportsSessionManagement;
-    private boolean mRouteSupportsMessaging;
-
-    String mSessionId;
-    StatusCallback mStatusCallback;
-    OnMessageReceivedListener mOnMessageReceivedListener;
-
-    /**
-     * Creates a remote playback client for a route.
-     *
-     * @param route The media route.
-     */
-    public RemotePlaybackClient(Context context, MediaRouter.RouteInfo route) {
-        if (context == null) {
-            throw new IllegalArgumentException("context must not be null");
-        }
-        if (route == null) {
-            throw new IllegalArgumentException("route must not be null");
-        }
-
-        mContext = context;
-        mRoute = route;
-
-        IntentFilter actionFilter = new IntentFilter();
-        actionFilter.addAction(ActionReceiver.ACTION_ITEM_STATUS_CHANGED);
-        actionFilter.addAction(ActionReceiver.ACTION_SESSION_STATUS_CHANGED);
-        actionFilter.addAction(ActionReceiver.ACTION_MESSAGE_RECEIVED);
-        mActionReceiver = new ActionReceiver();
-        context.registerReceiver(mActionReceiver, actionFilter);
-
-        Intent itemStatusIntent = new Intent(ActionReceiver.ACTION_ITEM_STATUS_CHANGED);
-        itemStatusIntent.setPackage(context.getPackageName());
-        mItemStatusPendingIntent = PendingIntent.getBroadcast(
-                context, 0, itemStatusIntent, 0);
-
-        Intent sessionStatusIntent = new Intent(ActionReceiver.ACTION_SESSION_STATUS_CHANGED);
-        sessionStatusIntent.setPackage(context.getPackageName());
-        mSessionStatusPendingIntent = PendingIntent.getBroadcast(
-                context, 0, sessionStatusIntent, 0);
-
-        Intent messageIntent = new Intent(ActionReceiver.ACTION_MESSAGE_RECEIVED);
-        messageIntent.setPackage(context.getPackageName());
-        mMessagePendingIntent = PendingIntent.getBroadcast(
-                context, 0, messageIntent, 0);
-        detectFeatures();
-    }
-
-    /**
-     * Releases resources owned by the client.
-     */
-    public void release() {
-        mContext.unregisterReceiver(mActionReceiver);
-    }
-
-    /**
-     * Returns true if the route supports remote playback.
-     * <p>
-     * If the route does not support remote playback, then none of the functionality
-     * offered by the client will be available.
-     * </p><p>
-     * This method returns true if the route supports all of the following
-     * actions: {@link MediaControlIntent#ACTION_PLAY play},
-     * {@link MediaControlIntent#ACTION_SEEK seek},
-     * {@link MediaControlIntent#ACTION_GET_STATUS get status},
-     * {@link MediaControlIntent#ACTION_PAUSE pause},
-     * {@link MediaControlIntent#ACTION_RESUME resume},
-     * {@link MediaControlIntent#ACTION_STOP stop}.
-     * </p>
-     *
-     * @return True if remote playback is supported.
-     */
-    public boolean isRemotePlaybackSupported() {
-        return mRouteSupportsRemotePlayback;
-    }
-
-    /**
-     * Returns true if the route supports queuing features.
-     * <p>
-     * If the route does not support queuing, then at most one media item can be played
-     * at a time and the {@link #enqueue} method will not be available.
-     * </p><p>
-     * This method returns true if the route supports all of the basic remote playback
-     * actions and all of the following actions:
-     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue},
-     * {@link MediaControlIntent#ACTION_REMOVE remove}.
-     * </p>
-     *
-     * @return True if queuing is supported.  Implies {@link #isRemotePlaybackSupported}
-     * is also true.
-     *
-     * @see #isRemotePlaybackSupported
-     */
-    public boolean isQueuingSupported() {
-        return mRouteSupportsQueuing;
-    }
-
-    /**
-     * Returns true if the route supports session management features.
-     * <p>
-     * If the route does not support session management, then the session will
-     * not be created until the first media item is played.
-     * </p><p>
-     * This method returns true if the route supports all of the basic remote playback
-     * actions and all of the following actions:
-     * {@link MediaControlIntent#ACTION_START_SESSION start session},
-     * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS get session status},
-     * {@link MediaControlIntent#ACTION_END_SESSION end session}.
-     * </p>
-     *
-     * @return True if session management is supported.
-     * Implies {@link #isRemotePlaybackSupported} is also true.
-     *
-     * @see #isRemotePlaybackSupported
-     */
-    public boolean isSessionManagementSupported() {
-        return mRouteSupportsSessionManagement;
-    }
-
-    /**
-     * Returns true if the route supports messages.
-     * <p>
-     * This method returns true if the route supports all of the basic remote playback
-     * actions and all of the following actions:
-     * {@link MediaControlIntent#ACTION_START_SESSION start session},
-     * {@link MediaControlIntent#ACTION_SEND_MESSAGE send message},
-     * {@link MediaControlIntent#ACTION_END_SESSION end session}.
-     * </p>
-     *
-     * @return True if session management is supported.
-     * Implies {@link #isRemotePlaybackSupported} is also true.
-     *
-     * @see #isRemotePlaybackSupported
-     */
-    public boolean isMessagingSupported() {
-        return mRouteSupportsMessaging;
-    }
-
-    /**
-     * Gets the current session id if there is one.
-     *
-     * @return The current session id, or null if none.
-     */
-    public String getSessionId() {
-        return mSessionId;
-    }
-
-    /**
-     * Sets the current session id.
-     * <p>
-     * It is usually not necessary to set the session id explicitly since
-     * it is created as a side-effect of other requests such as
-     * {@link #play}, {@link #enqueue}, and {@link #startSession}.
-     * </p>
-     *
-     * @param sessionId The new session id, or null if none.
-     */
-    public void setSessionId(String sessionId) {
-        if (!ObjectsCompat.equals(mSessionId, sessionId)) {
-            if (DEBUG) {
-                Log.d(TAG, "Session id is now: " + sessionId);
-            }
-            mSessionId = sessionId;
-            if (mStatusCallback != null) {
-                mStatusCallback.onSessionChanged(sessionId);
-            }
-        }
-    }
-
-    /**
-     * Returns true if the client currently has a session.
-     * <p>
-     * Equivalent to checking whether {@link #getSessionId} returns a non-null result.
-     * </p>
-     *
-     * @return True if there is a current session.
-     */
-    public boolean hasSession() {
-        return mSessionId != null;
-    }
-
-    /**
-     * Sets a callback that should receive status updates when the state of
-     * media sessions or media items created by this instance of the remote
-     * playback client changes.
-     * <p>
-     * The callback should be set before the session is created or any play
-     * commands are issued.
-     * </p>
-     *
-     * @param callback The callback to set.  May be null to remove the previous callback.
-     */
-    public void setStatusCallback(StatusCallback callback) {
-        mStatusCallback = callback;
-    }
-
-    /**
-     * Sets a callback that should receive messages when a message is sent from
-     * media sessions created by this instance of the remote playback client changes.
-     * <p>
-     * The callback should be set before the session is created.
-     * </p>
-     *
-     * @param listener The callback to set.  May be null to remove the previous callback.
-     */
-    public void setOnMessageReceivedListener(OnMessageReceivedListener listener) {
-        mOnMessageReceivedListener = listener;
-    }
-
-    /**
-     * Sends a request to play a media item.
-     * <p>
-     * Clears the queue and starts playing the new item immediately.  If the queue
-     * was previously paused, then it is resumed as a side-effect of this request.
-     * </p><p>
-     * The request is issued in the current session.  If no session is available, then
-     * one is created implicitly.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_PLAY ACTION_PLAY} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param contentUri The content Uri to play.
-     * @param mimeType The mime type of the content, or null if unknown.
-     * @param positionMillis The initial content position for the item in milliseconds,
-     * or <code>0</code> to start at the beginning.
-     * @param metadata The media item metadata bundle, or null if none.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_PLAY} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws UnsupportedOperationException if the route does not support remote playback.
-     *
-     * @see MediaControlIntent#ACTION_PLAY
-     * @see #isRemotePlaybackSupported
-     */
-    public void play(Uri contentUri, String mimeType, Bundle metadata,
-            long positionMillis, Bundle extras, ItemActionCallback callback) {
-        playOrEnqueue(contentUri, mimeType, metadata, positionMillis,
-                extras, callback, MediaControlIntent.ACTION_PLAY);
-    }
-
-    /**
-     * Sends a request to enqueue a media item.
-     * <p>
-     * Enqueues a new item to play.  If the queue was previously paused, then will
-     * remain paused.
-     * </p><p>
-     * The request is issued in the current session.  If no session is available, then
-     * one is created implicitly.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_ENQUEUE ACTION_ENQUEUE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param contentUri The content Uri to enqueue.
-     * @param mimeType The mime type of the content, or null if unknown.
-     * @param positionMillis The initial content position for the item in milliseconds,
-     * or <code>0</code> to start at the beginning.
-     * @param metadata The media item metadata bundle, or null if none.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_ENQUEUE} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws UnsupportedOperationException if the route does not support queuing.
-     *
-     * @see MediaControlIntent#ACTION_ENQUEUE
-     * @see #isRemotePlaybackSupported
-     * @see #isQueuingSupported
-     */
-    public void enqueue(Uri contentUri, String mimeType, Bundle metadata,
-            long positionMillis, Bundle extras, ItemActionCallback callback) {
-        playOrEnqueue(contentUri, mimeType, metadata, positionMillis,
-                extras, callback, MediaControlIntent.ACTION_ENQUEUE);
-    }
-
-    private void playOrEnqueue(Uri contentUri, String mimeType, Bundle metadata,
-            long positionMillis, Bundle extras,
-            final ItemActionCallback callback, String action) {
-        if (contentUri == null) {
-            throw new IllegalArgumentException("contentUri must not be null");
-        }
-        throwIfRemotePlaybackNotSupported();
-        if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
-            throwIfQueuingNotSupported();
-        }
-
-        Intent intent = new Intent(action);
-        intent.setDataAndType(contentUri, mimeType);
-        intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER,
-                mItemStatusPendingIntent);
-        if (metadata != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata);
-        }
-        if (positionMillis != 0) {
-            intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis);
-        }
-        performItemAction(intent, mSessionId, null, extras, callback);
-    }
-
-    /**
-     * Sends a request to seek to a new position in a media item.
-     * <p>
-     * Seeks to a new position.  If the queue was previously paused then it
-     * remains paused but the item's new position is still remembered.
-     * </p><p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_SEEK ACTION_SEEK} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param itemId The item id.
-     * @param positionMillis The new content position for the item in milliseconds,
-     * or <code>0</code> to start at the beginning.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_SEEK} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_SEEK
-     * @see #isRemotePlaybackSupported
-     */
-    public void seek(String itemId, long positionMillis, Bundle extras,
-            ItemActionCallback callback) {
-        if (itemId == null) {
-            throw new IllegalArgumentException("itemId must not be null");
-        }
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_SEEK);
-        intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis);
-        performItemAction(intent, mSessionId, itemId, extras, callback);
-    }
-
-    /**
-     * Sends a request to get the status of a media item.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_GET_STATUS ACTION_GET_STATUS} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param itemId The item id.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_GET_STATUS} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_GET_STATUS
-     * @see #isRemotePlaybackSupported
-     */
-    public void getStatus(String itemId, Bundle extras, ItemActionCallback callback) {
-        if (itemId == null) {
-            throw new IllegalArgumentException("itemId must not be null");
-        }
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_GET_STATUS);
-        performItemAction(intent, mSessionId, itemId, extras, callback);
-    }
-
-    /**
-     * Sends a request to remove a media item from the queue.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_REMOVE ACTION_REMOVE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param itemId The item id.
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_REMOVE} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support queuing.
-     *
-     * @see MediaControlIntent#ACTION_REMOVE
-     * @see #isRemotePlaybackSupported
-     * @see #isQueuingSupported
-     */
-    public void remove(String itemId, Bundle extras, ItemActionCallback callback) {
-        if (itemId == null) {
-            throw new IllegalArgumentException("itemId must not be null");
-        }
-        throwIfQueuingNotSupported();
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_REMOVE);
-        performItemAction(intent, mSessionId, itemId, extras, callback);
-    }
-
-    /**
-     * Sends a request to pause media playback.
-     * <p>
-     * The request is issued in the current session.  If playback is already paused
-     * then the request has no effect.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_PAUSE ACTION_PAUSE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_PAUSE} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_PAUSE
-     * @see #isRemotePlaybackSupported
-     */
-    public void pause(Bundle extras, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to resume (unpause) media playback.
-     * <p>
-     * The request is issued in the current session.  If playback is not paused
-     * then the request has no effect.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_RESUME ACTION_RESUME} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_RESUME} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_RESUME
-     * @see #isRemotePlaybackSupported
-     */
-    public void resume(Bundle extras, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_RESUME);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to stop media playback and clear the media playback queue.
-     * <p>
-     * The request is issued in the current session.  If the queue is already
-     * empty then the request has no effect.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_STOP ACTION_STOP} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_STOP} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     *
-     * @see MediaControlIntent#ACTION_STOP
-     * @see #isRemotePlaybackSupported
-     */
-    public void stop(Bundle extras, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_STOP);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to start a new media playback session.
-     * <p>
-     * The application must wait for the callback to indicate that this request
-     * is complete before issuing other requests that affect the session.  If this
-     * request is successful then the previous session will be invalidated.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_START_SESSION ACTION_START_SESSION}
-     * for more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_START_SESSION} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws UnsupportedOperationException if the route does not support session management.
-     *
-     * @see MediaControlIntent#ACTION_START_SESSION
-     * @see #isRemotePlaybackSupported
-     * @see #isSessionManagementSupported
-     */
-    public void startSession(Bundle extras, SessionActionCallback callback) {
-        throwIfSessionManagementNotSupported();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_START_SESSION);
-        intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER,
-                mSessionStatusPendingIntent);
-        if (mRouteSupportsMessaging) {
-            intent.putExtra(MediaControlIntent.EXTRA_MESSAGE_RECEIVER, mMessagePendingIntent);
-        }
-        performSessionAction(intent, null, extras, callback);
-    }
-
-    /**
-     * Sends a message.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_SEND_MESSAGE} for
-     * more information about the semantics of this request.
-     * </p>
-     *
-     * @param message A bundle message denoting {@link MediaControlIntent#EXTRA_MESSAGE}.
-     * @param callback A callback to invoke when the request has been processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support messages.
-     *
-     * @see MediaControlIntent#ACTION_SEND_MESSAGE
-     * @see #isMessagingSupported
-     */
-    public void sendMessage(Bundle message, SessionActionCallback callback) {
-        throwIfNoCurrentSession();
-        throwIfMessageNotSupported();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_SEND_MESSAGE);
-        performSessionAction(intent, mSessionId, message, callback);
-    }
-
-    /**
-     * Sends a request to get the status of the media playback session.
-     * <p>
-     * The request is issued in the current session.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_GET_SESSION_STATUS
-     * ACTION_GET_SESSION_STATUS} for more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support session management.
-     *
-     * @see MediaControlIntent#ACTION_GET_SESSION_STATUS
-     * @see #isRemotePlaybackSupported
-     * @see #isSessionManagementSupported
-     */
-    public void getSessionStatus(Bundle extras, SessionActionCallback callback) {
-        throwIfSessionManagementNotSupported();
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_GET_SESSION_STATUS);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    /**
-     * Sends a request to end the media playback session.
-     * <p>
-     * The request is issued in the current session.  If this request is successful,
-     * the {@link #getSessionId session id property} will be set to null after
-     * the callback is invoked.
-     * </p><p>
-     * Please refer to {@link MediaControlIntent#ACTION_END_SESSION ACTION_END_SESSION}
-     * for more information about the semantics of this request.
-     * </p>
-     *
-     * @param extras A bundle of extra arguments to be added to the
-     * {@link MediaControlIntent#ACTION_END_SESSION} intent, or null if none.
-     * @param callback A callback to invoke when the request has been
-     * processed, or null if none.
-     *
-     * @throws IllegalStateException if there is no current session.
-     * @throws UnsupportedOperationException if the route does not support session management.
-     *
-     * @see MediaControlIntent#ACTION_END_SESSION
-     * @see #isRemotePlaybackSupported
-     * @see #isSessionManagementSupported
-     */
-    public void endSession(Bundle extras, SessionActionCallback callback) {
-        throwIfSessionManagementNotSupported();
-        throwIfNoCurrentSession();
-
-        Intent intent = new Intent(MediaControlIntent.ACTION_END_SESSION);
-        performSessionAction(intent, mSessionId, extras, callback);
-    }
-
-    private void performItemAction(final Intent intent,
-            final String sessionId, final String itemId,
-            Bundle extras, final ItemActionCallback callback) {
-        intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-        if (sessionId != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId);
-        }
-        if (itemId != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, itemId);
-        }
-        if (extras != null) {
-            intent.putExtras(extras);
-        }
-        logRequest(intent);
-        mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() {
-            @Override
-            public void onResult(Bundle data) {
-                if (data != null) {
-                    String sessionIdResult = inferMissingResult(sessionId,
-                            data.getString(MediaControlIntent.EXTRA_SESSION_ID));
-                    MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
-                            data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS));
-                    String itemIdResult = inferMissingResult(itemId,
-                            data.getString(MediaControlIntent.EXTRA_ITEM_ID));
-                    MediaItemStatus itemStatus = MediaItemStatus.fromBundle(
-                            data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS));
-                    adoptSession(sessionIdResult);
-                    if (sessionIdResult != null && itemIdResult != null && itemStatus != null) {
-                        if (DEBUG) {
-                            Log.d(TAG, "Received result from " + intent.getAction()
-                                    + ": data=" + bundleToString(data)
-                                    + ", sessionId=" + sessionIdResult
-                                    + ", sessionStatus=" + sessionStatus
-                                    + ", itemId=" + itemIdResult
-                                    + ", itemStatus=" + itemStatus);
-                        }
-                        callback.onResult(data, sessionIdResult, sessionStatus,
-                                itemIdResult, itemStatus);
-                        return;
-                    }
-                }
-                handleInvalidResult(intent, callback, data);
-            }
-
-            @Override
-            public void onError(String error, Bundle data) {
-                handleError(intent, callback, error, data);
-            }
-        });
-    }
-
-    private void performSessionAction(final Intent intent, final String sessionId,
-            Bundle extras, final SessionActionCallback callback) {
-        intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
-        if (sessionId != null) {
-            intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId);
-        }
-        if (extras != null) {
-            intent.putExtras(extras);
-        }
-        logRequest(intent);
-        mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() {
-            @Override
-            public void onResult(Bundle data) {
-                if (data != null) {
-                    String sessionIdResult = inferMissingResult(sessionId,
-                            data.getString(MediaControlIntent.EXTRA_SESSION_ID));
-                    MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
-                            data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS));
-                    adoptSession(sessionIdResult);
-                    if (sessionIdResult != null) {
-                        if (DEBUG) {
-                            Log.d(TAG, "Received result from " + intent.getAction()
-                                    + ": data=" + bundleToString(data)
-                                    + ", sessionId=" + sessionIdResult
-                                    + ", sessionStatus=" + sessionStatus);
-                        }
-                        try {
-                            callback.onResult(data, sessionIdResult, sessionStatus);
-                        } finally {
-                            if (intent.getAction().equals(MediaControlIntent.ACTION_END_SESSION)
-                                    && sessionIdResult.equals(mSessionId)) {
-                                setSessionId(null);
-                            }
-                        }
-                        return;
-                    }
-                }
-                handleInvalidResult(intent, callback, data);
-            }
-
-            @Override
-            public void onError(String error, Bundle data) {
-                handleError(intent, callback, error, data);
-            }
-        });
-    }
-
-    void adoptSession(String sessionId) {
-        if (sessionId != null) {
-            setSessionId(sessionId);
-        }
-    }
-
-    void handleInvalidResult(Intent intent, ActionCallback callback,
-            Bundle data) {
-        Log.w(TAG, "Received invalid result data from " + intent.getAction()
-                + ": data=" + bundleToString(data));
-        callback.onError(null, MediaControlIntent.ERROR_UNKNOWN, data);
-    }
-
-    void handleError(Intent intent, ActionCallback callback,
-            String error, Bundle data) {
-        final int code;
-        if (data != null) {
-            code = data.getInt(MediaControlIntent.EXTRA_ERROR_CODE,
-                    MediaControlIntent.ERROR_UNKNOWN);
-        } else {
-            code = MediaControlIntent.ERROR_UNKNOWN;
-        }
-        if (DEBUG) {
-            Log.w(TAG, "Received error from " + intent.getAction()
-                    + ": error=" + error
-                    + ", code=" + code
-                    + ", data=" + bundleToString(data));
-        }
-        callback.onError(error, code, data);
-    }
-
-    private void detectFeatures() {
-        mRouteSupportsRemotePlayback = routeSupportsAction(MediaControlIntent.ACTION_PLAY)
-                && routeSupportsAction(MediaControlIntent.ACTION_SEEK)
-                && routeSupportsAction(MediaControlIntent.ACTION_GET_STATUS)
-                && routeSupportsAction(MediaControlIntent.ACTION_PAUSE)
-                && routeSupportsAction(MediaControlIntent.ACTION_RESUME)
-                && routeSupportsAction(MediaControlIntent.ACTION_STOP);
-        mRouteSupportsQueuing = mRouteSupportsRemotePlayback
-                && routeSupportsAction(MediaControlIntent.ACTION_ENQUEUE)
-                && routeSupportsAction(MediaControlIntent.ACTION_REMOVE);
-        mRouteSupportsSessionManagement = mRouteSupportsRemotePlayback
-                && routeSupportsAction(MediaControlIntent.ACTION_START_SESSION)
-                && routeSupportsAction(MediaControlIntent.ACTION_GET_SESSION_STATUS)
-                && routeSupportsAction(MediaControlIntent.ACTION_END_SESSION);
-        mRouteSupportsMessaging = doesRouteSupportMessaging();
-    }
-
-    private boolean routeSupportsAction(String action) {
-        return mRoute.supportsControlAction(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK, action);
-    }
-
-    private boolean doesRouteSupportMessaging() {
-        for (IntentFilter filter : mRoute.getControlFilters()) {
-            if (filter.hasAction(MediaControlIntent.ACTION_SEND_MESSAGE)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void throwIfRemotePlaybackNotSupported() {
-        if (!mRouteSupportsRemotePlayback) {
-            throw new UnsupportedOperationException("The route does not support remote playback.");
-        }
-    }
-
-    private void throwIfQueuingNotSupported() {
-        if (!mRouteSupportsQueuing) {
-            throw new UnsupportedOperationException("The route does not support queuing.");
-        }
-    }
-
-    private void throwIfSessionManagementNotSupported() {
-        if (!mRouteSupportsSessionManagement) {
-            throw new UnsupportedOperationException("The route does not support "
-                    + "session management.");
-        }
-    }
-
-    private void throwIfMessageNotSupported() {
-        if (!mRouteSupportsMessaging) {
-            throw new UnsupportedOperationException("The route does not support message.");
-        }
-    }
-
-    private void throwIfNoCurrentSession() {
-        if (mSessionId == null) {
-            throw new IllegalStateException("There is no current session.");
-        }
-    }
-
-    static String inferMissingResult(String request, String result) {
-        if (result == null) {
-            // Result is missing.
-            return request;
-        }
-        if (request == null || request.equals(result)) {
-            // Request didn't specify a value or result matches request.
-            return result;
-        }
-        // Result conflicts with request.
-        return null;
-    }
-
-    private static void logRequest(Intent intent) {
-        if (DEBUG) {
-            Log.d(TAG, "Sending request: " + intent);
-        }
-    }
-
-    static String bundleToString(Bundle bundle) {
-        if (bundle != null) {
-            bundle.size(); // force bundle to be unparcelled
-            return bundle.toString();
-        }
-        return "null";
-    }
-
-    private final class ActionReceiver extends BroadcastReceiver {
-        public static final String ACTION_ITEM_STATUS_CHANGED =
-                "android.support.v7.media.actions.ACTION_ITEM_STATUS_CHANGED";
-        public static final String ACTION_SESSION_STATUS_CHANGED =
-                "android.support.v7.media.actions.ACTION_SESSION_STATUS_CHANGED";
-        public static final String ACTION_MESSAGE_RECEIVED =
-                "android.support.v7.media.actions.ACTION_MESSAGE_RECEIVED";
-
-        ActionReceiver() {
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String sessionId = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
-            if (sessionId == null || !sessionId.equals(mSessionId)) {
-                Log.w(TAG, "Discarding spurious status callback "
-                        + "with missing or invalid session id: sessionId=" + sessionId);
-                return;
-            }
-
-            MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
-                    intent.getBundleExtra(MediaControlIntent.EXTRA_SESSION_STATUS));
-            String action = intent.getAction();
-            if (action.equals(ACTION_ITEM_STATUS_CHANGED)) {
-                String itemId = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
-                if (itemId == null) {
-                    Log.w(TAG, "Discarding spurious status callback with missing item id.");
-                    return;
-                }
-
-                MediaItemStatus itemStatus = MediaItemStatus.fromBundle(
-                        intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_STATUS));
-                if (itemStatus == null) {
-                    Log.w(TAG, "Discarding spurious status callback with missing item status.");
-                    return;
-                }
-
-                if (DEBUG) {
-                    Log.d(TAG, "Received item status callback: sessionId=" + sessionId
-                            + ", sessionStatus=" + sessionStatus
-                            + ", itemId=" + itemId
-                            + ", itemStatus=" + itemStatus);
-                }
-
-                if (mStatusCallback != null) {
-                    mStatusCallback.onItemStatusChanged(intent.getExtras(),
-                            sessionId, sessionStatus, itemId, itemStatus);
-                }
-            } else if (action.equals(ACTION_SESSION_STATUS_CHANGED)) {
-                if (sessionStatus == null) {
-                    Log.w(TAG, "Discarding spurious media status callback with "
-                            +"missing session status.");
-                    return;
-                }
-
-                if (DEBUG) {
-                    Log.d(TAG, "Received session status callback: sessionId=" + sessionId
-                            + ", sessionStatus=" + sessionStatus);
-                }
-
-                if (mStatusCallback != null) {
-                    mStatusCallback.onSessionStatusChanged(intent.getExtras(),
-                            sessionId, sessionStatus);
-                }
-            } else if (action.equals(ACTION_MESSAGE_RECEIVED)) {
-                if (DEBUG) {
-                    Log.d(TAG, "Received message callback: sessionId=" + sessionId);
-                }
-
-                if (mOnMessageReceivedListener != null) {
-                    mOnMessageReceivedListener.onMessageReceived(sessionId,
-                            intent.getBundleExtra(MediaControlIntent.EXTRA_MESSAGE));
-                }
-            }
-        }
-    }
-
-    /**
-     * A callback that will receive media status updates.
-     */
-    public static abstract class StatusCallback {
-        /**
-         * Called when the status of a media item changes.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         * @param itemId The item id.
-         * @param itemStatus The item status.
-         */
-        public void onItemStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus,
-                String itemId, MediaItemStatus itemStatus) {
-        }
-
-        /**
-         * Called when the status of a media session changes.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         */
-        public void onSessionStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus) {
-        }
-
-        /**
-         * Called when the session of the remote playback client changes.
-         *
-         * @param sessionId The new session id.
-         */
-        public void onSessionChanged(String sessionId) {
-        }
-    }
-
-    /**
-     * Base callback type for remote playback requests.
-     */
-    public static abstract class ActionCallback {
-        /**
-         * Called when a media control request fails.
-         *
-         * @param error A localized error message which may be shown to the user, or null
-         * if the cause of the error is unclear.
-         * @param code The error code, or {@link MediaControlIntent#ERROR_UNKNOWN} if unknown.
-         * @param data The error data bundle, or null if none.
-         */
-        public void onError(String error, int code, Bundle data) {
-        }
-    }
-
-    /**
-     * Callback for remote playback requests that operate on items.
-     */
-    public static abstract class ItemActionCallback extends ActionCallback {
-        /**
-         * Called when the request succeeds.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         * @param itemId The item id.
-         * @param itemStatus The item status.
-         */
-        public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                String itemId, MediaItemStatus itemStatus) {
-        }
-    }
-
-    /**
-     * Callback for remote playback requests that operate on sessions.
-     */
-    public static abstract class SessionActionCallback extends ActionCallback {
-        /**
-         * Called when the request succeeds.
-         *
-         * @param data The result data bundle.
-         * @param sessionId The session id.
-         * @param sessionStatus The session status, or null if unknown.
-         */
-        public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
-        }
-    }
-
-    /**
-     * A callback that will receive messages from media sessions.
-     */
-    public interface OnMessageReceivedListener {
-        /**
-         * Called when a message received.
-         *
-         * @param sessionId The session id.
-         * @param message A bundle message denoting {@link MediaControlIntent#EXTRA_MESSAGE}.
-         */
-        void onMessageReceived(String sessionId, Bundle message);
-    }
-}
diff --git a/android/support/v7/media/SystemMediaRouteProvider.java b/android/support/v7/media/SystemMediaRouteProvider.java
deleted file mode 100644
index 2aab6b7..0000000
--- a/android/support/v7/media/SystemMediaRouteProvider.java
+++ /dev/null
@@ -1,881 +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.v7.media;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-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;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Provides routes for built-in system destinations such as the local display
- * and speaker.  On Jellybean and newer platform releases, queries the framework
- * MediaRouter for framework-provided routes and registers non-framework-provided
- * routes as user routes.
- */
-abstract class SystemMediaRouteProvider extends MediaRouteProvider {
-    private static final String TAG = "SystemMediaRouteProvider";
-
-    public static final String PACKAGE_NAME = "android";
-    public static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
-
-    protected SystemMediaRouteProvider(Context context) {
-        super(context, new ProviderMetadata(new ComponentName(PACKAGE_NAME,
-                SystemMediaRouteProvider.class.getName())));
-    }
-
-    public static SystemMediaRouteProvider obtain(Context context, SyncCallback syncCallback) {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return new Api24Impl(context, syncCallback);
-        }
-        if (Build.VERSION.SDK_INT >= 18) {
-            return new JellybeanMr2Impl(context, syncCallback);
-        }
-        if (Build.VERSION.SDK_INT >= 17) {
-            return new JellybeanMr1Impl(context, syncCallback);
-        }
-        if (Build.VERSION.SDK_INT >= 16) {
-            return new JellybeanImpl(context, syncCallback);
-        }
-        return new LegacyImpl(context);
-    }
-
-    /**
-     * Called by the media router when a route is added to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Called by the media router when a route is removed to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteRemoved(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Called by the media router when a route is changed to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteChanged(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Called by the media router when a route is selected to synchronize state with
-     * the framework media router.
-     */
-    public void onSyncRouteSelected(MediaRouter.RouteInfo route) {
-    }
-
-    /**
-     * Callbacks into the media router to synchronize state with the framework media router.
-     */
-    public interface SyncCallback {
-        void onSystemRouteSelectedByDescriptorId(String id);
-    }
-
-    protected Object getDefaultRoute() {
-        return null;
-    }
-
-    protected Object getSystemRoute(MediaRouter.RouteInfo route) {
-        return null;
-    }
-
-    /**
-     * Legacy implementation for platform versions prior to Jellybean.
-     */
-    static class LegacyImpl extends SystemMediaRouteProvider {
-        static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC;
-
-        private static final ArrayList<IntentFilter> CONTROL_FILTERS;
-        static {
-            IntentFilter f = new IntentFilter();
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-
-            CONTROL_FILTERS = new ArrayList<IntentFilter>();
-            CONTROL_FILTERS.add(f);
-        }
-
-        final AudioManager mAudioManager;
-        private final VolumeChangeReceiver mVolumeChangeReceiver;
-        int mLastReportedVolume = -1;
-
-        public LegacyImpl(Context context) {
-            super(context);
-            mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
-            mVolumeChangeReceiver = new VolumeChangeReceiver();
-
-            context.registerReceiver(mVolumeChangeReceiver,
-                    new IntentFilter(VolumeChangeReceiver.VOLUME_CHANGED_ACTION));
-            publishRoutes();
-        }
-
-        void publishRoutes() {
-            Resources r = getContext().getResources();
-            int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM);
-            mLastReportedVolume = mAudioManager.getStreamVolume(PLAYBACK_STREAM);
-            MediaRouteDescriptor defaultRoute = new MediaRouteDescriptor.Builder(
-                    DEFAULT_ROUTE_ID, r.getString(R.string.mr_system_route_name))
-                    .addControlFilters(CONTROL_FILTERS)
-                    .setPlaybackStream(PLAYBACK_STREAM)
-                    .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL)
-                    .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
-                    .setVolumeMax(maxVolume)
-                    .setVolume(mLastReportedVolume)
-                    .build();
-
-            MediaRouteProviderDescriptor providerDescriptor =
-                    new MediaRouteProviderDescriptor.Builder()
-                    .addRoute(defaultRoute)
-                    .build();
-            setDescriptor(providerDescriptor);
-        }
-
-        @Override
-        public RouteController onCreateRouteController(String routeId) {
-            if (routeId.equals(DEFAULT_ROUTE_ID)) {
-                return new DefaultRouteController();
-            }
-            return null;
-        }
-
-        final class DefaultRouteController extends RouteController {
-            @Override
-            public void onSetVolume(int volume) {
-                mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0);
-                publishRoutes();
-            }
-
-            @Override
-            public void onUpdateVolume(int delta) {
-                int volume = mAudioManager.getStreamVolume(PLAYBACK_STREAM);
-                int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM);
-                int newVolume = Math.min(maxVolume, Math.max(0, volume + delta));
-                if (newVolume != volume) {
-                    mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0);
-                }
-                publishRoutes();
-            }
-        }
-
-        final class VolumeChangeReceiver extends BroadcastReceiver {
-            // These constants come from AudioManager.
-            public static final String VOLUME_CHANGED_ACTION =
-                    "android.media.VOLUME_CHANGED_ACTION";
-            public static final String EXTRA_VOLUME_STREAM_TYPE =
-                    "android.media.EXTRA_VOLUME_STREAM_TYPE";
-            public static final String EXTRA_VOLUME_STREAM_VALUE =
-                    "android.media.EXTRA_VOLUME_STREAM_VALUE";
-
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) {
-                    final int streamType = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1);
-                    if (streamType == PLAYBACK_STREAM) {
-                        final int volume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, -1);
-                        if (volume >= 0 && volume != mLastReportedVolume) {
-                            publishRoutes();
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Jellybean implementation.
-     */
-    @RequiresApi(16)
-    static class JellybeanImpl extends SystemMediaRouteProvider
-            implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback {
-        private static final ArrayList<IntentFilter> LIVE_AUDIO_CONTROL_FILTERS;
-        static {
-            IntentFilter f = new IntentFilter();
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
-
-            LIVE_AUDIO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
-            LIVE_AUDIO_CONTROL_FILTERS.add(f);
-        }
-
-        private static final ArrayList<IntentFilter> LIVE_VIDEO_CONTROL_FILTERS;
-        static {
-            IntentFilter f = new IntentFilter();
-            f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
-
-            LIVE_VIDEO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
-            LIVE_VIDEO_CONTROL_FILTERS.add(f);
-        }
-
-        private final SyncCallback mSyncCallback;
-
-        protected final Object mRouterObj;
-        protected final Object mCallbackObj;
-        protected final Object mVolumeCallbackObj;
-        protected final Object mUserRouteCategoryObj;
-        protected int mRouteTypes;
-        protected boolean mActiveScan;
-        protected boolean mCallbackRegistered;
-
-        // Maintains an association from framework routes to support library routes.
-        // Note that we cannot use the tag field for this because an application may
-        // have published its own user routes to the framework media router and already
-        // used the tag for its own purposes.
-        protected final ArrayList<SystemRouteRecord> mSystemRouteRecords =
-                new ArrayList<SystemRouteRecord>();
-
-        // Maintains an association from support library routes to framework routes.
-        protected final ArrayList<UserRouteRecord> mUserRouteRecords =
-                new ArrayList<UserRouteRecord>();
-
-        private MediaRouterJellybean.SelectRouteWorkaround mSelectRouteWorkaround;
-        private MediaRouterJellybean.GetDefaultRouteWorkaround mGetDefaultRouteWorkaround;
-
-        public JellybeanImpl(Context context, SyncCallback syncCallback) {
-            super(context);
-            mSyncCallback = syncCallback;
-            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
-            mCallbackObj = createCallbackObj();
-            mVolumeCallbackObj = createVolumeCallbackObj();
-
-            Resources r = context.getResources();
-            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
-                    mRouterObj, r.getString(R.string.mr_user_route_category_name), false);
-
-            updateSystemRoutes();
-        }
-
-        @Override
-        public RouteController onCreateRouteController(String routeId) {
-            int index = findSystemRouteRecordByDescriptorId(routeId);
-            if (index >= 0) {
-                SystemRouteRecord record = mSystemRouteRecords.get(index);
-                return new SystemRouteController(record.mRouteObj);
-            }
-            return null;
-        }
-
-        @Override
-        public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
-            int newRouteTypes = 0;
-            boolean newActiveScan = false;
-            if (request != null) {
-                final MediaRouteSelector selector = request.getSelector();
-                final List<String> categories = selector.getControlCategories();
-                final int count = categories.size();
-                for (int i = 0; i < count; i++) {
-                    String category = categories.get(i);
-                    if (category.equals(MediaControlIntent.CATEGORY_LIVE_AUDIO)) {
-                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO;
-                    } else if (category.equals(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
-                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO;
-                    } else {
-                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_USER;
-                    }
-                }
-                newActiveScan = request.isActiveScan();
-            }
-
-            if (mRouteTypes != newRouteTypes || mActiveScan != newActiveScan) {
-                mRouteTypes = newRouteTypes;
-                mActiveScan = newActiveScan;
-                updateSystemRoutes();
-            }
-        }
-
-        @Override
-        public void onRouteAdded(Object routeObj) {
-            if (addSystemRouteNoPublish(routeObj)) {
-                publishRoutes();
-            }
-        }
-
-        private void updateSystemRoutes() {
-            updateCallback();
-            boolean changed = false;
-            for (Object routeObj : MediaRouterJellybean.getRoutes(mRouterObj)) {
-                changed |= addSystemRouteNoPublish(routeObj);
-            }
-            if (changed) {
-                publishRoutes();
-            }
-        }
-
-        private boolean addSystemRouteNoPublish(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null
-                    && findSystemRouteRecord(routeObj) < 0) {
-                String id = assignRouteId(routeObj);
-                SystemRouteRecord record = new SystemRouteRecord(routeObj, id);
-                updateSystemRouteDescriptor(record);
-                mSystemRouteRecords.add(record);
-                return true;
-            }
-            return false;
-        }
-
-        private String assignRouteId(Object routeObj) {
-            // TODO: The framework media router should supply a unique route id that
-            // we can use here.  For now we use a hash of the route name and take care
-            // to dedupe it.
-            boolean isDefault = (getDefaultRoute() == routeObj);
-            String id = isDefault ? DEFAULT_ROUTE_ID :
-                    String.format(Locale.US, "ROUTE_%08x", getRouteName(routeObj).hashCode());
-            if (findSystemRouteRecordByDescriptorId(id) < 0) {
-                return id;
-            }
-            for (int i = 2; ; i++) {
-                String newId = String.format(Locale.US, "%s_%d", id, i);
-                if (findSystemRouteRecordByDescriptorId(newId) < 0) {
-                    return newId;
-                }
-            }
-        }
-
-        @Override
-        public void onRouteRemoved(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    mSystemRouteRecords.remove(index);
-                    publishRoutes();
-                }
-            }
-        }
-
-        @Override
-        public void onRouteChanged(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    updateSystemRouteDescriptor(record);
-                    publishRoutes();
-                }
-            }
-        }
-
-        @Override
-        public void onRouteVolumeChanged(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    int newVolume = MediaRouterJellybean.RouteInfo.getVolume(routeObj);
-                    if (newVolume != record.mRouteDescriptor.getVolume()) {
-                        record.mRouteDescriptor =
-                                new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
-                                .setVolume(newVolume)
-                                .build();
-                        publishRoutes();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onRouteSelected(int type, Object routeObj) {
-            if (routeObj != MediaRouterJellybean.getSelectedRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES)) {
-                // The currently selected route has already changed so this callback
-                // is stale.  Drop it to prevent getting into sync loops.
-                return;
-            }
-
-            UserRouteRecord userRouteRecord = getUserRouteRecord(routeObj);
-            if (userRouteRecord != null) {
-                userRouteRecord.mRoute.select();
-            } else {
-                // Select the route if it already exists in the compat media router.
-                // If not, we will select it instead when the route is added.
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    mSyncCallback.onSystemRouteSelectedByDescriptorId(record.mRouteDescriptorId);
-                }
-            }
-        }
-
-        @Override
-        public void onRouteUnselected(int type, Object routeObj) {
-            // Nothing to do when a route is unselected.
-            // We only need to handle when a route is selected.
-        }
-
-        @Override
-        public void onRouteGrouped(Object routeObj, Object groupObj, int index) {
-            // Route grouping is deprecated and no longer supported.
-        }
-
-        @Override
-        public void onRouteUngrouped(Object routeObj, Object groupObj) {
-            // Route grouping is deprecated and no longer supported.
-        }
-
-        @Override
-        public void onVolumeSetRequest(Object routeObj, int volume) {
-            UserRouteRecord record = getUserRouteRecord(routeObj);
-            if (record != null) {
-                record.mRoute.requestSetVolume(volume);
-            }
-        }
-
-        @Override
-        public void onVolumeUpdateRequest(Object routeObj, int direction) {
-            UserRouteRecord record = getUserRouteRecord(routeObj);
-            if (record != null) {
-                record.mRoute.requestUpdateVolume(direction);
-            }
-        }
-
-        @Override
-        public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
-            if (route.getProviderInstance() != this) {
-                Object routeObj = MediaRouterJellybean.createUserRoute(
-                        mRouterObj, mUserRouteCategoryObj);
-                UserRouteRecord record = new UserRouteRecord(route, routeObj);
-                MediaRouterJellybean.RouteInfo.setTag(routeObj, record);
-                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(routeObj, mVolumeCallbackObj);
-                updateUserRouteProperties(record);
-                mUserRouteRecords.add(record);
-                MediaRouterJellybean.addUserRoute(mRouterObj, routeObj);
-            } else {
-                // If the newly added route is the counterpart of the currently selected
-                // route in the framework media router then ensure it is selected in
-                // the compat media router.
-                Object routeObj = MediaRouterJellybean.getSelectedRoute(
-                        mRouterObj, MediaRouterJellybean.ALL_ROUTE_TYPES);
-                int index = findSystemRouteRecord(routeObj);
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    if (record.mRouteDescriptorId.equals(route.getDescriptorId())) {
-                        route.select();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onSyncRouteRemoved(MediaRouter.RouteInfo route) {
-            if (route.getProviderInstance() != this) {
-                int index = findUserRouteRecord(route);
-                if (index >= 0) {
-                    UserRouteRecord record = mUserRouteRecords.remove(index);
-                    MediaRouterJellybean.RouteInfo.setTag(record.mRouteObj, null);
-                    MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mRouteObj, null);
-                    MediaRouterJellybean.removeUserRoute(mRouterObj, record.mRouteObj);
-                }
-            }
-        }
-
-        @Override
-        public void onSyncRouteChanged(MediaRouter.RouteInfo route) {
-            if (route.getProviderInstance() != this) {
-                int index = findUserRouteRecord(route);
-                if (index >= 0) {
-                    UserRouteRecord record = mUserRouteRecords.get(index);
-                    updateUserRouteProperties(record);
-                }
-            }
-        }
-
-        @Override
-        public void onSyncRouteSelected(MediaRouter.RouteInfo route) {
-            if (!route.isSelected()) {
-                // The currently selected route has already changed so this callback
-                // is stale.  Drop it to prevent getting into sync loops.
-                return;
-            }
-
-            if (route.getProviderInstance() != this) {
-                int index = findUserRouteRecord(route);
-                if (index >= 0) {
-                    UserRouteRecord record = mUserRouteRecords.get(index);
-                    selectRoute(record.mRouteObj);
-                }
-            } else {
-                int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
-                if (index >= 0) {
-                    SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    selectRoute(record.mRouteObj);
-                }
-            }
-        }
-
-        protected void publishRoutes() {
-            MediaRouteProviderDescriptor.Builder builder =
-                    new MediaRouteProviderDescriptor.Builder();
-            int count = mSystemRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                builder.addRoute(mSystemRouteRecords.get(i).mRouteDescriptor);
-            }
-
-            setDescriptor(builder.build());
-        }
-
-        protected int findSystemRouteRecord(Object routeObj) {
-            final int count = mSystemRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                if (mSystemRouteRecords.get(i).mRouteObj == routeObj) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        protected int findSystemRouteRecordByDescriptorId(String id) {
-            final int count = mSystemRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                if (mSystemRouteRecords.get(i).mRouteDescriptorId.equals(id)) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        protected int findUserRouteRecord(MediaRouter.RouteInfo route) {
-            final int count = mUserRouteRecords.size();
-            for (int i = 0; i < count; i++) {
-                if (mUserRouteRecords.get(i).mRoute == route) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        protected UserRouteRecord getUserRouteRecord(Object routeObj) {
-            Object tag = MediaRouterJellybean.RouteInfo.getTag(routeObj);
-            return tag instanceof UserRouteRecord ? (UserRouteRecord)tag : null;
-        }
-
-        protected void updateSystemRouteDescriptor(SystemRouteRecord record) {
-            // We must always recreate the route descriptor when making any changes
-            // because they are intended to be immutable once published.
-            MediaRouteDescriptor.Builder builder = new MediaRouteDescriptor.Builder(
-                    record.mRouteDescriptorId, getRouteName(record.mRouteObj));
-            onBuildSystemRouteDescriptor(record, builder);
-            record.mRouteDescriptor = builder.build();
-        }
-
-        protected String getRouteName(Object routeObj) {
-            // Routes should not have null names but it may happen for badly configured
-            // user routes.  We tolerate this by using an empty name string here but
-            // such unnamed routes will be discarded by the media router upstream
-            // (with a log message so we can track down the problem).
-            CharSequence name = MediaRouterJellybean.RouteInfo.getName(routeObj, getContext());
-            return name != null ? name.toString() : "";
-        }
-
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                MediaRouteDescriptor.Builder builder) {
-            int supportedTypes = MediaRouterJellybean.RouteInfo.getSupportedTypes(
-                    record.mRouteObj);
-            if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO) != 0) {
-                builder.addControlFilters(LIVE_AUDIO_CONTROL_FILTERS);
-            }
-            if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                builder.addControlFilters(LIVE_VIDEO_CONTROL_FILTERS);
-            }
-
-            builder.setPlaybackType(
-                    MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRouteObj));
-            builder.setPlaybackStream(
-                    MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRouteObj));
-            builder.setVolume(
-                    MediaRouterJellybean.RouteInfo.getVolume(record.mRouteObj));
-            builder.setVolumeMax(
-                    MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRouteObj));
-            builder.setVolumeHandling(
-                    MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRouteObj));
-        }
-
-        protected void updateUserRouteProperties(UserRouteRecord record) {
-            MediaRouterJellybean.UserRouteInfo.setName(
-                    record.mRouteObj, record.mRoute.getName());
-            MediaRouterJellybean.UserRouteInfo.setPlaybackType(
-                    record.mRouteObj, record.mRoute.getPlaybackType());
-            MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
-                    record.mRouteObj, record.mRoute.getPlaybackStream());
-            MediaRouterJellybean.UserRouteInfo.setVolume(
-                    record.mRouteObj, record.mRoute.getVolume());
-            MediaRouterJellybean.UserRouteInfo.setVolumeMax(
-                    record.mRouteObj, record.mRoute.getVolumeMax());
-            MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
-                    record.mRouteObj, record.mRoute.getVolumeHandling());
-        }
-
-        protected void updateCallback() {
-            if (mCallbackRegistered) {
-                mCallbackRegistered = false;
-                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
-            }
-
-            if (mRouteTypes != 0) {
-                mCallbackRegistered = true;
-                MediaRouterJellybean.addCallback(mRouterObj, mRouteTypes, mCallbackObj);
-            }
-        }
-
-        protected Object createCallbackObj() {
-            return MediaRouterJellybean.createCallback(this);
-        }
-
-        protected Object createVolumeCallbackObj() {
-            return MediaRouterJellybean.createVolumeCallback(this);
-        }
-
-        protected void selectRoute(Object routeObj) {
-            if (mSelectRouteWorkaround == null) {
-                mSelectRouteWorkaround = new MediaRouterJellybean.SelectRouteWorkaround();
-            }
-            mSelectRouteWorkaround.selectRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
-        }
-
-        @Override
-        protected Object getDefaultRoute() {
-            if (mGetDefaultRouteWorkaround == null) {
-                mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround();
-            }
-            return mGetDefaultRouteWorkaround.getDefaultRoute(mRouterObj);
-        }
-
-        @Override
-        protected Object getSystemRoute(MediaRouter.RouteInfo route) {
-            if (route == null) {
-                return null;
-            }
-            int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
-            if (index >= 0) {
-                return mSystemRouteRecords.get(index).mRouteObj;
-            }
-            return null;
-        }
-
-        /**
-         * Represents a route that is provided by the framework media router
-         * and published by this route provider to the support library media router.
-         */
-        protected static final class SystemRouteRecord {
-            public final Object mRouteObj;
-            public final String mRouteDescriptorId;
-            public MediaRouteDescriptor mRouteDescriptor; // assigned immediately after creation
-
-            public SystemRouteRecord(Object routeObj, String id) {
-                mRouteObj = routeObj;
-                mRouteDescriptorId = id;
-            }
-        }
-
-        /**
-         * Represents a route that is provided by the support library media router
-         * and published by this route provider to the framework media router.
-         */
-        protected static final class UserRouteRecord {
-            public final MediaRouter.RouteInfo mRoute;
-            public final Object mRouteObj;
-
-            public UserRouteRecord(MediaRouter.RouteInfo route, Object routeObj) {
-                mRoute = route;
-                mRouteObj = routeObj;
-            }
-        }
-
-        protected static final class SystemRouteController extends RouteController {
-            private final Object mRouteObj;
-
-            public SystemRouteController(Object routeObj) {
-                mRouteObj = routeObj;
-            }
-
-            @Override
-            public void onSetVolume(int volume) {
-                MediaRouterJellybean.RouteInfo.requestSetVolume(mRouteObj, volume);
-            }
-
-            @Override
-            public void onUpdateVolume(int delta) {
-                MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRouteObj, delta);
-            }
-        }
-    }
-
-    /**
-     * Jellybean MR1 implementation.
-     */
-    @RequiresApi(17)
-    private static class JellybeanMr1Impl extends JellybeanImpl
-            implements MediaRouterJellybeanMr1.Callback {
-        private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround;
-        private MediaRouterJellybeanMr1.IsConnectingWorkaround mIsConnectingWorkaround;
-
-        public JellybeanMr1Impl(Context context, SyncCallback syncCallback) {
-            super(context, syncCallback);
-        }
-
-        @Override
-        public void onRoutePresentationDisplayChanged(Object routeObj) {
-            int index = findSystemRouteRecord(routeObj);
-            if (index >= 0) {
-                SystemRouteRecord record = mSystemRouteRecords.get(index);
-                Display newPresentationDisplay =
-                        MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(routeObj);
-                int newPresentationDisplayId = (newPresentationDisplay != null
-                        ? newPresentationDisplay.getDisplayId() : -1);
-                if (newPresentationDisplayId
-                        != record.mRouteDescriptor.getPresentationDisplayId()) {
-                    record.mRouteDescriptor =
-                            new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
-                            .setPresentationDisplayId(newPresentationDisplayId)
-                            .build();
-                    publishRoutes();
-                }
-            }
-        }
-
-        @Override
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                MediaRouteDescriptor.Builder builder) {
-            super.onBuildSystemRouteDescriptor(record, builder);
-
-            if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRouteObj)) {
-                builder.setEnabled(false);
-            }
-
-            if (isConnecting(record)) {
-                builder.setConnecting(true);
-            }
-
-            Display presentationDisplay =
-                    MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRouteObj);
-            if (presentationDisplay != null) {
-                builder.setPresentationDisplayId(presentationDisplay.getDisplayId());
-            }
-        }
-
-        @Override
-        protected void updateCallback() {
-            super.updateCallback();
-
-            if (mActiveScanWorkaround == null) {
-                mActiveScanWorkaround = new MediaRouterJellybeanMr1.ActiveScanWorkaround(
-                        getContext(), getHandler());
-            }
-            mActiveScanWorkaround.setActiveScanRouteTypes(mActiveScan ? mRouteTypes : 0);
-        }
-
-        @Override
-        protected Object createCallbackObj() {
-            return MediaRouterJellybeanMr1.createCallback(this);
-        }
-
-        protected boolean isConnecting(SystemRouteRecord record) {
-            if (mIsConnectingWorkaround == null) {
-                mIsConnectingWorkaround = new MediaRouterJellybeanMr1.IsConnectingWorkaround();
-            }
-            return mIsConnectingWorkaround.isConnecting(record.mRouteObj);
-        }
-    }
-
-    /**
-     * Jellybean MR2 implementation.
-     */
-    @RequiresApi(18)
-    private static class JellybeanMr2Impl extends JellybeanMr1Impl {
-        public JellybeanMr2Impl(Context context, SyncCallback syncCallback) {
-            super(context, syncCallback);
-        }
-
-        @Override
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                MediaRouteDescriptor.Builder builder) {
-            super.onBuildSystemRouteDescriptor(record, builder);
-
-            CharSequence description =
-                    MediaRouterJellybeanMr2.RouteInfo.getDescription(record.mRouteObj);
-            if (description != null) {
-                builder.setDescription(description.toString());
-            }
-        }
-
-        @Override
-        protected void selectRoute(Object routeObj) {
-            MediaRouterJellybean.selectRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
-        }
-
-        @Override
-        protected Object getDefaultRoute() {
-            return MediaRouterJellybeanMr2.getDefaultRoute(mRouterObj);
-        }
-
-        @Override
-        protected void updateUserRouteProperties(UserRouteRecord record) {
-            super.updateUserRouteProperties(record);
-
-            MediaRouterJellybeanMr2.UserRouteInfo.setDescription(
-                    record.mRouteObj, record.mRoute.getDescription());
-        }
-
-        @Override
-        protected void updateCallback() {
-            if (mCallbackRegistered) {
-                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
-            }
-
-            mCallbackRegistered = true;
-            MediaRouterJellybeanMr2.addCallback(mRouterObj, mRouteTypes, mCallbackObj,
-                    MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS
-                    | (mActiveScan ? MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN : 0));
-        }
-
-        @Override
-        protected boolean isConnecting(SystemRouteRecord record) {
-            return MediaRouterJellybeanMr2.RouteInfo.isConnecting(record.mRouteObj);
-        }
-    }
-
-    /**
-     * Api24 implementation.
-     */
-    @RequiresApi(24)
-    private static class Api24Impl extends JellybeanMr2Impl {
-        public Api24Impl(Context context, SyncCallback syncCallback) {
-            super(context, syncCallback);
-        }
-
-        @Override
-        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
-                                                    MediaRouteDescriptor.Builder builder) {
-            super.onBuildSystemRouteDescriptor(record, builder);
-
-            builder.setDeviceType(MediaRouterApi24.RouteInfo.getDeviceType(record.mRouteObj));
-        }
-    }
-}
diff --git a/android/support/v7/preference/AndroidResources.java b/android/support/v7/preference/AndroidResources.java
deleted file mode 100644
index d08a887..0000000
--- a/android/support/v7/preference/AndroidResources.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.v7.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class AndroidResources {
-
-    public static final int ANDROID_R_ICON_FRAME = android.R.id.icon_frame;
-    public static final int ANDROID_R_LIST_CONTAINER = android.R.id.list_container;
-    public static final int ANDROID_R_SWITCH_WIDGET = android.R.id.switch_widget;
-    public static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE
-            = android.R.attr.preferenceFragmentStyle;
-    public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE
-            = android.R.attr.editTextPreferenceStyle;
-
-}
diff --git a/android/support/v7/preference/CheckBoxPreference.java b/android/support/v7/preference/CheckBoxPreference.java
deleted file mode 100644
index 086f22d..0000000
--- a/android/support/v7/preference/CheckBoxPreference.java
+++ /dev/null
@@ -1,134 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.Checkable;
-import android.widget.CompoundButton;
-
-/**
- * A {@link Preference} that provides checkbox widget
- * functionality.
- * <p>
- * This preference will store a boolean into the SharedPreferences.
- *
- * @attr name android:summaryOff
- * @attr name android:summaryOn
- * @attr name android:disableDependentsState
- */
-public class CheckBoxPreference extends TwoStatePreference {
-    private final Listener mListener = new Listener();
-
-    private class Listener implements CompoundButton.OnCheckedChangeListener {
-        @Override
-        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            if (!callChangeListener(isChecked)) {
-                // Listener didn't like it, change it back.
-                // CompoundButton will make sure we don't recurse.
-                buttonView.setChecked(!isChecked);
-                return;
-            }
-            CheckBoxPreference.this.setChecked(isChecked);
-        }
-    }
-
-    public CheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public CheckBoxPreference(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.CheckBoxPreference, defStyleAttr, defStyleRes);
-
-        setSummaryOn(TypedArrayUtils.getString(a, R.styleable.CheckBoxPreference_summaryOn,
-                R.styleable.CheckBoxPreference_android_summaryOn));
-
-        setSummaryOff(TypedArrayUtils.getString(a, R.styleable.CheckBoxPreference_summaryOff,
-                R.styleable.CheckBoxPreference_android_summaryOff));
-
-        setDisableDependentsState(TypedArrayUtils.getBoolean(a,
-                R.styleable.CheckBoxPreference_disableDependentsState,
-                R.styleable.CheckBoxPreference_android_disableDependentsState, false));
-
-        a.recycle();
-    }
-
-    public CheckBoxPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.checkBoxPreferenceStyle,
-                android.R.attr.checkBoxPreferenceStyle));
-    }
-
-    public CheckBoxPreference(Context context) {
-        this(context, null);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-
-        syncCheckboxView(holder.findViewById(android.R.id.checkbox));
-
-        syncSummaryView(holder);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected void performClick(View view) {
-        super.performClick(view);
-        syncViewIfAccessibilityEnabled(view);
-    }
-
-    private void syncViewIfAccessibilityEnabled(View view) {
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (!accessibilityManager.isEnabled()) {
-            return;
-        }
-
-        View checkboxView = view.findViewById(android.R.id.checkbox);
-        syncCheckboxView(checkboxView);
-
-        View summaryView = view.findViewById(android.R.id.summary);
-        syncSummaryView(summaryView);
-    }
-
-    private void syncCheckboxView(View view) {
-        if (view instanceof CompoundButton) {
-            ((CompoundButton) view).setOnCheckedChangeListener(null);
-        }
-        if (view instanceof Checkable) {
-            ((Checkable) view).setChecked(mChecked);
-        }
-        if (view instanceof CompoundButton) {
-            ((CompoundButton) view).setOnCheckedChangeListener(mListener);
-        }
-    }
-}
diff --git a/android/support/v7/preference/CollapsiblePreferenceGroupController.java b/android/support/v7/preference/CollapsiblePreferenceGroupController.java
deleted file mode 100644
index b63ff75..0000000
--- a/android/support/v7/preference/CollapsiblePreferenceGroupController.java
+++ /dev/null
@@ -1,226 +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.v7.preference;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A controller to handle advanced children display logic with collapsible functionality.
- */
-final class CollapsiblePreferenceGroupController
-        implements PreferenceGroup.PreferenceInstanceStateCallback {
-
-    private final PreferenceGroupAdapter mPreferenceGroupAdapter;
-    private int mMaxPreferenceToShow;
-    private final Context mContext;
-
-    CollapsiblePreferenceGroupController(PreferenceGroup preferenceGroup,
-            PreferenceGroupAdapter preferenceGroupAdapter) {
-        mPreferenceGroupAdapter = preferenceGroupAdapter;
-        mMaxPreferenceToShow = preferenceGroup.getInitialExpandedChildrenCount();
-        mContext = preferenceGroup.getContext();
-        preferenceGroup.setPreferenceInstanceStateCallback(this);
-    }
-
-    /**
-     * Creates the visible portion of the flattened preferences.
-     *
-     * @param flattenedPreferenceList the flattened children of the preference group
-     * @return the visible portion of the flattened preferences
-     */
-    public List<Preference> createVisiblePreferencesList(List<Preference> flattenedPreferenceList) {
-        int visiblePreferenceCount = 0;
-        final List<Preference> visiblePreferenceList =
-                new ArrayList<>(flattenedPreferenceList.size());
-        // Copy only the visible preferences to the active list up to the maximum specified
-        for (final Preference preference : flattenedPreferenceList) {
-            if (preference.isVisible()) {
-                if (visiblePreferenceCount < mMaxPreferenceToShow) {
-                    visiblePreferenceList.add(preference);
-                }
-                // Do no count PreferenceGroup as expanded preference because the list of its child
-                // is already contained in the flattenedPreferenceList
-                if (!(preference instanceof PreferenceGroup)) {
-                    visiblePreferenceCount++;
-                }
-            }
-        }
-        // If there are any visible preferences being hidden, add an expand button to show the rest
-        // of the preferences. Clicking the expand button will show all the visible preferences and
-        // reset mMaxPreferenceToShow
-        if (showLimitedChildren() && visiblePreferenceCount > mMaxPreferenceToShow) {
-            final ExpandButton expandButton  = createExpandButton(visiblePreferenceList,
-                    flattenedPreferenceList);
-            visiblePreferenceList.add(expandButton);
-        }
-        return visiblePreferenceList;
-    }
-
-    /**
-     * Called when a preference has changed its visibility.
-     *
-     * @param preference The preference whose visibility has changed.
-     * @return {@code true} if view update has been handled by this controller.
-     */
-    public boolean onPreferenceVisibilityChange(Preference preference) {
-        if (showLimitedChildren()) {
-            // We only want to show up to the max number of preferences. Preference visibility
-            // change can result in the expand button being added/removed, as well as expand button
-            // summary change. Rebulid the data to ensure the correct data is shown.
-            mPreferenceGroupAdapter.onPreferenceHierarchyChange(preference);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public Parcelable saveInstanceState(Parcelable state) {
-        final SavedState myState = new SavedState(state);
-        myState.mMaxPreferenceToShow = mMaxPreferenceToShow;
-        return myState;
-    }
-
-    @Override
-    public Parcelable restoreInstanceState(Parcelable state) {
-        if (state == null || !state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in saveInstanceState
-            return state;
-        }
-        SavedState myState = (SavedState) state;
-        final int restoredMaxToShow = myState.mMaxPreferenceToShow;
-        if (mMaxPreferenceToShow != restoredMaxToShow) {
-            mMaxPreferenceToShow = restoredMaxToShow;
-            mPreferenceGroupAdapter.onPreferenceHierarchyChange(null);
-        }
-        return myState.getSuperState();
-    }
-
-    private ExpandButton createExpandButton(List<Preference> visiblePreferenceList,
-            List<Preference> flattenedPreferenceList) {
-        final ExpandButton preference = new ExpandButton(mContext, visiblePreferenceList,
-                flattenedPreferenceList);
-        preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                mMaxPreferenceToShow = Integer.MAX_VALUE;
-                mPreferenceGroupAdapter.onPreferenceHierarchyChange(preference);
-                return true;
-            }
-        });
-        return preference;
-    }
-
-    private boolean showLimitedChildren() {
-        return mMaxPreferenceToShow != Integer.MAX_VALUE;
-    }
-
-    /**
-     * A {@link Preference} that provides capability to expand the collapsed items in the
-     * {@link PreferenceGroup}.
-     */
-    static class ExpandButton extends Preference {
-        ExpandButton(Context context, List<Preference> visiblePreferenceList,
-                List<Preference> flattenedPreferenceList) {
-            super(context);
-            initLayout();
-            setSummary(visiblePreferenceList, flattenedPreferenceList);
-        }
-
-        private void initLayout() {
-            setLayoutResource(R.layout.expand_button);
-            setIcon(R.drawable.ic_arrow_down_24dp);
-            setTitle(R.string.expand_button_title);
-            // Sets a high order so that the expand button will be placed at the bottom of the group
-            setOrder(999);
-        }
-
-        /*
-         * The summary of this will be the list of title for collapsed preferences. Iterate through
-         * the preferences not in the visible list and add its title to the summary text.
-         */
-        private void setSummary(List<Preference> visiblePreferenceList,
-                List<Preference> flattenedPreferenceList) {
-            final Preference lastVisiblePreference =
-                    visiblePreferenceList.get(visiblePreferenceList.size() - 1);
-            final int collapsedIndex = flattenedPreferenceList.indexOf(lastVisiblePreference) + 1;
-            CharSequence summary = null;
-            for (int i = collapsedIndex; i < flattenedPreferenceList.size(); i++) {
-                final Preference preference = flattenedPreferenceList.get(i);
-                if (preference instanceof PreferenceGroup || !preference.isVisible()) {
-                    continue;
-                }
-                final CharSequence title = preference.getTitle();
-                if (!TextUtils.isEmpty(title)) {
-                    if (summary == null) {
-                        summary = title;
-                    } else {
-                        summary = getContext().getString(
-                                R.string.summary_collapsed_preference_list, summary, title);
-                    }
-                }
-            }
-            setSummary(summary);
-        }
-
-        @Override
-        public void onBindViewHolder(PreferenceViewHolder holder) {
-            super.onBindViewHolder(holder);
-            holder.setDividerAllowedAbove(false);
-        }
-    }
-
-    /**
-     * A class for managing the instance state of a {@link PreferenceGroup}.
-     */
-    static class SavedState extends Preference.BaseSavedState {
-        int mMaxPreferenceToShow;
-
-        SavedState(Parcel source) {
-            super(source);
-            mMaxPreferenceToShow = source.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mMaxPreferenceToShow);
-        }
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        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/android/support/v7/preference/DialogPreference.java b/android/support/v7/preference/DialogPreference.java
deleted file mode 100644
index aa7efee..0000000
--- a/android/support/v7/preference/DialogPreference.java
+++ /dev/null
@@ -1,265 +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.preference;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * A base class for {@link Preference} objects that are
- * dialog-based. These preferences will, when clicked, open a dialog showing the
- * actual preference controls.
- *
- * @attr name android:dialogTitle
- * @attr name android:dialogMessage
- * @attr name android:dialogIcon
- * @attr name android:dialogLayout
- * @attr name android:positiveButtonText
- * @attr name android:negativeButtonText
- */
-public abstract class DialogPreference extends Preference {
-
-    public interface TargetFragment {
-        Preference findPreference(CharSequence key);
-    }
-
-    private CharSequence mDialogTitle;
-    private CharSequence mDialogMessage;
-    private Drawable mDialogIcon;
-    private CharSequence mPositiveButtonText;
-    private CharSequence mNegativeButtonText;
-    private int mDialogLayoutResId;
-
-    public DialogPreference(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.DialogPreference, defStyleAttr, defStyleRes);
-
-        mDialogTitle = TypedArrayUtils.getString(a, R.styleable.DialogPreference_dialogTitle,
-                R.styleable.DialogPreference_android_dialogTitle);
-        if (mDialogTitle == null) {
-            // Fall back on the regular title of the preference
-            // (the one that is seen in the list)
-            mDialogTitle = getTitle();
-        }
-
-        mDialogMessage = TypedArrayUtils.getString(a, R.styleable.DialogPreference_dialogMessage,
-                R.styleable.DialogPreference_android_dialogMessage);
-
-        mDialogIcon = TypedArrayUtils.getDrawable(a, R.styleable.DialogPreference_dialogIcon,
-                R.styleable.DialogPreference_android_dialogIcon);
-
-        mPositiveButtonText = TypedArrayUtils.getString(a,
-                R.styleable.DialogPreference_positiveButtonText,
-                R.styleable.DialogPreference_android_positiveButtonText);
-
-        mNegativeButtonText = TypedArrayUtils.getString(a,
-                R.styleable.DialogPreference_negativeButtonText,
-                R.styleable.DialogPreference_android_negativeButtonText);
-
-        mDialogLayoutResId = TypedArrayUtils.getResourceId(a,
-                R.styleable.DialogPreference_dialogLayout,
-                R.styleable.DialogPreference_android_dialogLayout, 0);
-
-        a.recycle();
-    }
-
-    public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public DialogPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
-                android.R.attr.dialogPreferenceStyle));
-    }
-
-    public DialogPreference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Sets the title of the dialog. This will be shown on subsequent dialogs.
-     *
-     * @param dialogTitle The title.
-     */
-    public void setDialogTitle(CharSequence dialogTitle) {
-        mDialogTitle = dialogTitle;
-    }
-
-    /**
-     * @see #setDialogTitle(CharSequence)
-     * @param dialogTitleResId The dialog title as a resource.
-     */
-    public void setDialogTitle(int dialogTitleResId) {
-        setDialogTitle(getContext().getString(dialogTitleResId));
-    }
-
-    /**
-     * Returns the title to be shown on subsequent dialogs.
-     * @return The title.
-     */
-    public CharSequence getDialogTitle() {
-        return mDialogTitle;
-    }
-
-    /**
-     * Sets the message of the dialog. This will be shown on subsequent dialogs.
-     * <p>
-     * This message forms the content View of the dialog and conflicts with
-     * list-based dialogs, for example. If setting a custom View on a dialog via
-     * {@link #setDialogLayoutResource(int)}, include a text View with ID
-     * {@link android.R.id#message} and it will be populated with this message.
-     *
-     * @param dialogMessage The message.
-     */
-    public void setDialogMessage(CharSequence dialogMessage) {
-        mDialogMessage = dialogMessage;
-    }
-
-    /**
-     * @see #setDialogMessage(CharSequence)
-     * @param dialogMessageResId The dialog message as a resource.
-     */
-    public void setDialogMessage(int dialogMessageResId) {
-        setDialogMessage(getContext().getString(dialogMessageResId));
-    }
-
-    /**
-     * Returns the message to be shown on subsequent dialogs.
-     * @return The message.
-     */
-    public CharSequence getDialogMessage() {
-        return mDialogMessage;
-    }
-
-    /**
-     * Sets the icon of the dialog. This will be shown on subsequent dialogs.
-     *
-     * @param dialogIcon The icon, as a {@link Drawable}.
-     */
-    public void setDialogIcon(Drawable dialogIcon) {
-        mDialogIcon = dialogIcon;
-    }
-
-    /**
-     * Sets the icon (resource ID) of the dialog. This will be shown on
-     * subsequent dialogs.
-     *
-     * @param dialogIconRes The icon, as a resource ID.
-     */
-    public void setDialogIcon(int dialogIconRes) {
-        mDialogIcon = ContextCompat.getDrawable(getContext(), dialogIconRes);
-    }
-
-    /**
-     * Returns the icon to be shown on subsequent dialogs.
-     * @return The icon, as a {@link Drawable}.
-     */
-    public Drawable getDialogIcon() {
-        return mDialogIcon;
-    }
-
-    /**
-     * Sets the text of the positive button of the dialog. This will be shown on
-     * subsequent dialogs.
-     *
-     * @param positiveButtonText The text of the positive button.
-     */
-    public void setPositiveButtonText(CharSequence positiveButtonText) {
-        mPositiveButtonText = positiveButtonText;
-    }
-
-    /**
-     * @see #setPositiveButtonText(CharSequence)
-     * @param positiveButtonTextResId The positive button text as a resource.
-     */
-    public void setPositiveButtonText(int positiveButtonTextResId) {
-        setPositiveButtonText(getContext().getString(positiveButtonTextResId));
-    }
-
-    /**
-     * Returns the text of the positive button to be shown on subsequent
-     * dialogs.
-     *
-     * @return The text of the positive button.
-     */
-    public CharSequence getPositiveButtonText() {
-        return mPositiveButtonText;
-    }
-
-    /**
-     * Sets the text of the negative button of the dialog. This will be shown on
-     * subsequent dialogs.
-     *
-     * @param negativeButtonText The text of the negative button.
-     */
-    public void setNegativeButtonText(CharSequence negativeButtonText) {
-        mNegativeButtonText = negativeButtonText;
-    }
-
-    /**
-     * @see #setNegativeButtonText(CharSequence)
-     * @param negativeButtonTextResId The negative button text as a resource.
-     */
-    public void setNegativeButtonText(int negativeButtonTextResId) {
-        setNegativeButtonText(getContext().getString(negativeButtonTextResId));
-    }
-
-    /**
-     * Returns the text of the negative button to be shown on subsequent
-     * dialogs.
-     *
-     * @return The text of the negative button.
-     */
-    public CharSequence getNegativeButtonText() {
-        return mNegativeButtonText;
-    }
-
-    /**
-     * Sets the layout resource that is inflated as the {@link View} to be shown
-     * as the content View of subsequent dialogs.
-     *
-     * @param dialogLayoutResId The layout resource ID to be inflated.
-     * @see #setDialogMessage(CharSequence)
-     */
-    public void setDialogLayoutResource(int dialogLayoutResId) {
-        mDialogLayoutResId = dialogLayoutResId;
-    }
-
-    /**
-     * Returns the layout resource that is used as the content View for
-     * subsequent dialogs.
-     *
-     * @return The layout resource.
-     */
-    public int getDialogLayoutResource() {
-        return mDialogLayoutResId;
-    }
-
-    @Override
-    protected void onClick() {
-        getPreferenceManager().showDialog(this);
-    }
-
-}
diff --git a/android/support/v7/preference/DropDownPreference.java b/android/support/v7/preference/DropDownPreference.java
deleted file mode 100644
index 3638b71..0000000
--- a/android/support/v7/preference/DropDownPreference.java
+++ /dev/null
@@ -1,150 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-
-/**
- * A version of {@link ListPreference} that presents the options in a
- * drop down menu rather than a dialog.
- */
-public class DropDownPreference extends ListPreference {
-
-    private final Context mContext;
-    private final ArrayAdapter mAdapter;
-
-    private Spinner mSpinner;
-
-    public DropDownPreference(Context context) {
-        this(context, null);
-    }
-
-    public DropDownPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.dropdownPreferenceStyle);
-    }
-
-    public DropDownPreference(Context context, AttributeSet attrs, int defStyle) {
-        this(context, attrs, defStyle, 0);
-    }
-
-    public DropDownPreference(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        mContext = context;
-        mAdapter = createAdapter();
-
-        updateEntries();
-    }
-
-    @Override
-    protected void onClick() {
-        mSpinner.performClick();
-    }
-
-    @Override
-    public void setEntries(@NonNull CharSequence[] entries) {
-        super.setEntries(entries);
-        updateEntries();
-    }
-
-
-    /**
-     * By default, this class uses a simple {@link android.widget.ArrayAdapter}. But if you need
-     * a more complicated {@link android.widget.ArrayAdapter}, this method can be overridden to
-     * create a custom one.
-     * <p> Note: This method is called from the constructor. So, overridden methods will get called
-     * before any subclass initialization.
-     *
-     * @return The custom {@link android.widget.ArrayAdapter} that needs to be used with this class.
-     */
-    protected ArrayAdapter createAdapter() {
-        return new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_dropdown_item);
-    }
-
-    private void updateEntries() {
-        mAdapter.clear();
-        if (getEntries() != null) {
-            for (CharSequence c : getEntries()) {
-                mAdapter.add(c.toString());
-            }
-        }
-    }
-
-    @Override
-    public void setValueIndex(int index) {
-        setValue(getEntryValues()[index].toString());
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int findSpinnerIndexOfValue(String value) {
-        CharSequence[] entryValues = getEntryValues();
-        if (value != null && entryValues != null) {
-            for (int i = entryValues.length - 1; i >= 0; i--) {
-                if (entryValues[i].equals(value)) {
-                    return i;
-                }
-            }
-        }
-        return Spinner.INVALID_POSITION;
-    }
-
-    @Override
-    protected void notifyChanged() {
-        super.notifyChanged();
-        mAdapter.notifyDataSetChanged();
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder view) {
-        mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner);
-        mSpinner.setAdapter(mAdapter);
-        mSpinner.setOnItemSelectedListener(mItemSelectedListener);
-        mSpinner.setSelection(findSpinnerIndexOfValue(getValue()));
-        super.onBindViewHolder(view);
-    }
-
-    private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
-            if (position >= 0) {
-                String value = getEntryValues()[position].toString();
-                if (!value.equals(getValue()) && callChangeListener(value)) {
-                    setValue(value);
-                }
-            }
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // noop
-        }
-    };
-}
-
diff --git a/android/support/v7/preference/EditTextPreference.java b/android/support/v7/preference/EditTextPreference.java
deleted file mode 100644
index 0ebb229..0000000
--- a/android/support/v7/preference/EditTextPreference.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.v7.preference;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.widget.EditText;
-
-/**
- * A {@link Preference} that allows for string
- * input.
- * <p>
- * It is a subclass of {@link DialogPreference} and shows the {@link EditText}
- * in a dialog.
- * <p>
- * This preference will store a string into the SharedPreferences.
- */
-public class EditTextPreference extends DialogPreference {
-    private String mText;
-
-    public EditTextPreference(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public EditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public EditTextPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.editTextPreferenceStyle,
-                AndroidResources.ANDROID_R_EDITTEXT_PREFERENCE_STYLE));
-    }
-
-    public EditTextPreference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Saves the text to the {@link android.content.SharedPreferences}.
-     *
-     * @param text The text to save
-     */
-    public void setText(String text) {
-        final boolean wasBlocking = shouldDisableDependents();
-
-        mText = text;
-
-        persistString(text);
-
-        final boolean isBlocking = shouldDisableDependents();
-        if (isBlocking != wasBlocking) {
-            notifyDependencyChange(isBlocking);
-        }
-    }
-
-    /**
-     * Gets the text from the {@link android.content.SharedPreferences}.
-     *
-     * @return The current preference value.
-     */
-    public String getText() {
-        return mText;
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getString(index);
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
-        setText(restoreValue ? getPersistedString(mText) : (String) defaultValue);
-    }
-
-    @Override
-    public boolean shouldDisableDependents() {
-        return TextUtils.isEmpty(mText) || super.shouldDisableDependents();
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (isPersistent()) {
-            // No need to save instance state since it's persistent
-            return superState;
-        }
-
-        final SavedState myState = new SavedState(superState);
-        myState.text = getText();
-        return myState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state == null || !state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in onSaveInstanceState
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState myState = (SavedState) state;
-        super.onRestoreInstanceState(myState.getSuperState());
-        setText(myState.text);
-    }
-
-    private static class SavedState extends BaseSavedState {
-        String text;
-
-        public SavedState(Parcel source) {
-            super(source);
-            text = source.readString();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeString(text);
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        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/android/support/v7/preference/EditTextPreferenceDialogFragmentCompat.java b/android/support/v7/preference/EditTextPreferenceDialogFragmentCompat.java
deleted file mode 100644
index a6cc81f..0000000
--- a/android/support/v7/preference/EditTextPreferenceDialogFragmentCompat.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.v7.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.view.View;
-import android.widget.EditText;
-
-public class EditTextPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
-
-    private static final String SAVE_STATE_TEXT = "EditTextPreferenceDialogFragment.text";
-
-    private EditText mEditText;
-
-    private CharSequence mText;
-
-    public static EditTextPreferenceDialogFragmentCompat newInstance(String key) {
-        final EditTextPreferenceDialogFragmentCompat
-                fragment = new EditTextPreferenceDialogFragmentCompat();
-        final Bundle b = new Bundle(1);
-        b.putString(ARG_KEY, key);
-        fragment.setArguments(b);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (savedInstanceState == null) {
-            mText = getEditTextPreference().getText();
-        } else {
-            mText = savedInstanceState.getCharSequence(SAVE_STATE_TEXT);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putCharSequence(SAVE_STATE_TEXT, mText);
-    }
-
-    @Override
-    protected void onBindDialogView(View view) {
-        super.onBindDialogView(view);
-
-        mEditText = (EditText) view.findViewById(android.R.id.edit);
-
-        if (mEditText == null) {
-            throw new IllegalStateException("Dialog view must contain an EditText with id" +
-                    " @android:id/edit");
-        }
-
-        mEditText.setText(mText);
-    }
-
-    private EditTextPreference getEditTextPreference() {
-        return (EditTextPreference) getPreference();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected boolean needInputMethod() {
-        // We want the input method to show, if possible, when dialog is displayed
-        return true;
-    }
-
-    @Override
-    public void onDialogClosed(boolean positiveResult) {
-
-        if (positiveResult) {
-            String value = mEditText.getText().toString();
-            if (getEditTextPreference().callChangeListener(value)) {
-                getEditTextPreference().setText(value);
-            }
-        }
-    }
-
-}
diff --git a/android/support/v7/preference/ListPreference.java b/android/support/v7/preference/ListPreference.java
deleted file mode 100644
index 13dd9e5..0000000
--- a/android/support/v7/preference/ListPreference.java
+++ /dev/null
@@ -1,320 +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.preference;
-
-import android.content.Context;
-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.v4.content.res.TypedArrayUtils;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-
-/**
- * A {@link Preference} that displays a list of entries as
- * a dialog.
- * <p>
- * This preference will store a string into the SharedPreferences. This string will be the value
- * from the {@link #setEntryValues(CharSequence[])} array.
- *
- * @attr name android:entries
- * @attr name android:entryValues
- */
-public class ListPreference extends DialogPreference {
-    private CharSequence[] mEntries;
-    private CharSequence[] mEntryValues;
-    private String mValue;
-    private String mSummary;
-    private boolean mValueSet;
-
-    public ListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.ListPreference, defStyleAttr, defStyleRes);
-
-        mEntries = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entries,
-                R.styleable.ListPreference_android_entries);
-
-        mEntryValues = TypedArrayUtils.getTextArray(a, R.styleable.ListPreference_entryValues,
-                R.styleable.ListPreference_android_entryValues);
-
-        a.recycle();
-
-        /* Retrieve the Preference summary attribute since it's private
-         * in the Preference class.
-         */
-        a = context.obtainStyledAttributes(attrs,
-                R.styleable.Preference, defStyleAttr, defStyleRes);
-
-        mSummary = TypedArrayUtils.getString(a, R.styleable.Preference_summary,
-                R.styleable.Preference_android_summary);
-
-        a.recycle();
-    }
-
-    public ListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public ListPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle,
-                android.R.attr.dialogPreferenceStyle));
-    }
-
-    public ListPreference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Sets the human-readable entries to be shown in the list. This will be
-     * shown in subsequent dialogs.
-     * <p>
-     * Each entry must have a corresponding index in
-     * {@link #setEntryValues(CharSequence[])}.
-     *
-     * @param entries The entries.
-     * @see #setEntryValues(CharSequence[])
-     */
-    public void setEntries(CharSequence[] entries) {
-        mEntries = entries;
-    }
-
-    /**
-     * @see #setEntries(CharSequence[])
-     * @param entriesResId The entries array as a resource.
-     */
-    public void setEntries(@ArrayRes int entriesResId) {
-        setEntries(getContext().getResources().getTextArray(entriesResId));
-    }
-
-    /**
-     * The list of entries to be shown in the list in subsequent dialogs.
-     *
-     * @return The list as an array.
-     */
-    public CharSequence[] getEntries() {
-        return mEntries;
-    }
-
-    /**
-     * The array to find the value to save for a preference when an entry from
-     * entries is selected. If a user clicks on the second item in entries, the
-     * second item in this array will be saved to the preference.
-     *
-     * @param entryValues The array to be used as values to save for the preference.
-     */
-    public void setEntryValues(CharSequence[] entryValues) {
-        mEntryValues = entryValues;
-    }
-
-    /**
-     * @see #setEntryValues(CharSequence[])
-     * @param entryValuesResId The entry values array as a resource.
-     */
-    public void setEntryValues(@ArrayRes int entryValuesResId) {
-        setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
-    }
-
-    /**
-     * Returns the array of values to be saved for the preference.
-     *
-     * @return The array of values.
-     */
-    public CharSequence[] getEntryValues() {
-        return mEntryValues;
-    }
-
-    /**
-     * Sets the value of the key. This should be one of the entries in
-     * {@link #getEntryValues()}.
-     *
-     * @param value The value to set for the key.
-     */
-    public void setValue(String value) {
-        // Always persist/notify the first time.
-        final boolean changed = !TextUtils.equals(mValue, value);
-        if (changed || !mValueSet) {
-            mValue = value;
-            mValueSet = true;
-            persistString(value);
-            if (changed) {
-                notifyChanged();
-            }
-        }
-    }
-
-    /**
-     * Returns the summary of this ListPreference. If the summary
-     * has a {@linkplain java.lang.String#format String formatting}
-     * marker in it (i.e. "%s" or "%1$s"), then the current entry
-     * value will be substituted in its place.
-     *
-     * @return the summary with appropriate string substitution
-     */
-    @Override
-    public CharSequence getSummary() {
-        final CharSequence entry = getEntry();
-        if (mSummary == null) {
-            return super.getSummary();
-        } else {
-            return String.format(mSummary, entry == null ? "" : entry);
-        }
-    }
-
-    /**
-     * Sets the summary for this Preference with a CharSequence.
-     * If the summary has a
-     * {@linkplain java.lang.String#format String formatting}
-     * marker in it (i.e. "%s" or "%1$s"), then the current entry
-     * value will be substituted in its place when it's retrieved.
-     *
-     * @param summary The summary for the preference.
-     */
-    @Override
-    public void setSummary(CharSequence summary) {
-        super.setSummary(summary);
-        if (summary == null && mSummary != null) {
-            mSummary = null;
-        } else if (summary != null && !summary.equals(mSummary)) {
-            mSummary = summary.toString();
-        }
-    }
-
-    /**
-     * Sets the value to the given index from the entry values.
-     *
-     * @param index The index of the value to set.
-     */
-    public void setValueIndex(int index) {
-        if (mEntryValues != null) {
-            setValue(mEntryValues[index].toString());
-        }
-    }
-
-    /**
-     * Returns the value of the key. This should be one of the entries in
-     * {@link #getEntryValues()}.
-     *
-     * @return The value of the key.
-     */
-    public String getValue() {
-        return mValue;
-    }
-
-    /**
-     * Returns the entry corresponding to the current value.
-     *
-     * @return The entry corresponding to the current value, or null.
-     */
-    public CharSequence getEntry() {
-        int index = getValueIndex();
-        return index >= 0 && mEntries != null ? mEntries[index] : null;
-    }
-
-    /**
-     * Returns the index of the given value (in the entry values array).
-     *
-     * @param value The value whose index should be returned.
-     * @return The index of the value, or -1 if not found.
-     */
-    public int findIndexOfValue(String value) {
-        if (value != null && mEntryValues != null) {
-            for (int i = mEntryValues.length - 1; i >= 0; i--) {
-                if (mEntryValues[i].equals(value)) {
-                    return i;
-                }
-            }
-        }
-        return -1;
-    }
-
-    private int getValueIndex() {
-        return findIndexOfValue(mValue);
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getString(index);
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
-        setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (isPersistent()) {
-            // No need to save instance state since it's persistent
-            return superState;
-        }
-
-        final SavedState myState = new SavedState(superState);
-        myState.value = getValue();
-        return myState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state == null || !state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in onSaveInstanceState
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState myState = (SavedState) state;
-        super.onRestoreInstanceState(myState.getSuperState());
-        setValue(myState.value);
-    }
-
-    private static class SavedState extends BaseSavedState {
-        String value;
-
-        public SavedState(Parcel source) {
-            super(source);
-            value = source.readString();
-        }
-
-        @Override
-        public void writeToParcel(@NonNull Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeString(value);
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        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/android/support/v7/preference/ListPreferenceDialogFragmentCompat.java b/android/support/v7/preference/ListPreferenceDialogFragmentCompat.java
deleted file mode 100644
index cbce40f..0000000
--- a/android/support/v7/preference/ListPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,134 +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.preference;
-
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AlertDialog;
-
-import java.util.ArrayList;
-
-public class ListPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
-
-    private static final String SAVE_STATE_INDEX = "ListPreferenceDialogFragment.index";
-    private static final String SAVE_STATE_ENTRIES = "ListPreferenceDialogFragment.entries";
-    private static final String SAVE_STATE_ENTRY_VALUES =
-            "ListPreferenceDialogFragment.entryValues";
-
-    private int mClickedDialogEntryIndex;
-    private CharSequence[] mEntries;
-    private CharSequence[] mEntryValues;
-
-    public static ListPreferenceDialogFragmentCompat newInstance(String key) {
-        final ListPreferenceDialogFragmentCompat fragment =
-                new ListPreferenceDialogFragmentCompat();
-        final Bundle b = new Bundle(1);
-        b.putString(ARG_KEY, key);
-        fragment.setArguments(b);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (savedInstanceState == null) {
-            final ListPreference preference = getListPreference();
-
-            if (preference.getEntries() == null || preference.getEntryValues() == null) {
-                throw new IllegalStateException(
-                        "ListPreference requires an entries array and an entryValues array.");
-            }
-
-            mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
-            mEntries = preference.getEntries();
-            mEntryValues = preference.getEntryValues();
-        } else {
-            mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
-            mEntries = getCharSequenceArray(savedInstanceState, SAVE_STATE_ENTRIES);
-            mEntryValues = getCharSequenceArray(savedInstanceState, SAVE_STATE_ENTRY_VALUES);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
-        putCharSequenceArray(outState, SAVE_STATE_ENTRIES, mEntries);
-        putCharSequenceArray(outState, SAVE_STATE_ENTRY_VALUES, mEntryValues);
-    }
-
-    private static void putCharSequenceArray(Bundle out, String key, CharSequence[] entries) {
-        final ArrayList<String> stored = new ArrayList<>(entries.length);
-
-        for (final CharSequence cs : entries) {
-            stored.add(cs.toString());
-        }
-
-        out.putStringArrayList(key, stored);
-    }
-
-    private static CharSequence[] getCharSequenceArray(Bundle in, String key) {
-        final ArrayList<String> stored = in.getStringArrayList(key);
-
-        return stored == null ? null : stored.toArray(new CharSequence[stored.size()]);
-    }
-
-    private ListPreference getListPreference() {
-        return (ListPreference) getPreference();
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
-        super.onPrepareDialogBuilder(builder);
-
-        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
-                new DialogInterface.OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mClickedDialogEntryIndex = which;
-
-                        /*
-                         * Clicking on an item simulates the positive button
-                         * click, and dismisses the dialog.
-                         */
-                        ListPreferenceDialogFragmentCompat.this.onClick(dialog,
-                                DialogInterface.BUTTON_POSITIVE);
-                        dialog.dismiss();
-                    }
-                });
-
-        /*
-         * The typical interaction for list-based dialogs is to have
-         * click-on-an-item dismiss the dialog instead of the user having to
-         * press 'Ok'.
-         */
-        builder.setPositiveButton(null, null);
-    }
-
-    @Override
-    public void onDialogClosed(boolean positiveResult) {
-        final ListPreference preference = getListPreference();
-        if (positiveResult && mClickedDialogEntryIndex >= 0) {
-            String value = mEntryValues[mClickedDialogEntryIndex].toString();
-            if (preference.callChangeListener(value)) {
-                preference.setValue(value);
-            }
-        }
-    }
-
-}
diff --git a/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java b/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
deleted file mode 100644
index 791c299..0000000
--- a/android/support/v7/preference/MultiSelectListPreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,129 +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.preference;
-
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.preference.internal.AbstractMultiSelectListPreference;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-public class MultiSelectListPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
-
-    private static final String SAVE_STATE_VALUES =
-            "MultiSelectListPreferenceDialogFragmentCompat.values";
-    private static final String SAVE_STATE_CHANGED =
-            "MultiSelectListPreferenceDialogFragmentCompat.changed";
-    private static final String SAVE_STATE_ENTRIES =
-            "MultiSelectListPreferenceDialogFragmentCompat.entries";
-    private static final String SAVE_STATE_ENTRY_VALUES =
-            "MultiSelectListPreferenceDialogFragmentCompat.entryValues";
-
-    private Set<String> mNewValues = new HashSet<>();
-    private boolean mPreferenceChanged;
-    private CharSequence[] mEntries;
-    private CharSequence[] mEntryValues;
-
-    public static MultiSelectListPreferenceDialogFragmentCompat newInstance(String key) {
-        final MultiSelectListPreferenceDialogFragmentCompat fragment =
-                new MultiSelectListPreferenceDialogFragmentCompat();
-        final Bundle b = new Bundle(1);
-        b.putString(ARG_KEY, key);
-        fragment.setArguments(b);
-        return fragment;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState == null) {
-            final AbstractMultiSelectListPreference preference = getListPreference();
-
-            if (preference.getEntries() == null || preference.getEntryValues() == null) {
-                throw new IllegalStateException(
-                        "MultiSelectListPreference requires an entries array and " +
-                                "an entryValues array.");
-            }
-
-            mNewValues.clear();
-            mNewValues.addAll(preference.getValues());
-            mPreferenceChanged = false;
-            mEntries = preference.getEntries();
-            mEntryValues = preference.getEntryValues();
-        } else {
-            mNewValues.clear();
-            mNewValues.addAll(savedInstanceState.getStringArrayList(SAVE_STATE_VALUES));
-            mPreferenceChanged = savedInstanceState.getBoolean(SAVE_STATE_CHANGED, false);
-            mEntries = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRIES);
-            mEntryValues = savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putStringArrayList(SAVE_STATE_VALUES, new ArrayList<>(mNewValues));
-        outState.putBoolean(SAVE_STATE_CHANGED, mPreferenceChanged);
-        outState.putCharSequenceArray(SAVE_STATE_ENTRIES, mEntries);
-        outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
-    }
-
-    private AbstractMultiSelectListPreference getListPreference() {
-        return (AbstractMultiSelectListPreference) getPreference();
-    }
-
-    @Override
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
-        super.onPrepareDialogBuilder(builder);
-
-        final int entryCount = mEntryValues.length;
-        final boolean[] checkedItems = new boolean[entryCount];
-        for (int i = 0; i < entryCount; i++) {
-            checkedItems[i] = mNewValues.contains(mEntryValues[i].toString());
-        }
-        builder.setMultiChoiceItems(mEntries, checkedItems,
-                new DialogInterface.OnMultiChoiceClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
-                        if (isChecked) {
-                            mPreferenceChanged |= mNewValues.add(
-                                    mEntryValues[which].toString());
-                        } else {
-                            mPreferenceChanged |= mNewValues.remove(
-                                    mEntryValues[which].toString());
-                        }
-                    }
-                });
-    }
-
-    @Override
-    public void onDialogClosed(boolean positiveResult) {
-        final AbstractMultiSelectListPreference preference = getListPreference();
-        if (positiveResult && mPreferenceChanged) {
-            final Set<String> values = mNewValues;
-            if (preference.callChangeListener(values)) {
-                preference.setValues(values);
-            }
-        }
-        mPreferenceChanged = false;
-    }
-}
diff --git a/android/support/v7/preference/Preference.java b/android/support/v7/preference/Preference.java
deleted file mode 100644
index 88262cd..0000000
--- a/android/support/v7/preference/Preference.java
+++ /dev/null
@@ -1,2052 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcel;
-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.res.TypedArrayUtils;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.AbsSavedState;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Represents the basic Preference UI building
- * block displayed by a {@link PreferenceFragmentCompat} in the form of a
- * {@link android.support.v7.widget.RecyclerView}. This class provides data for the
- * {@link android.view.View} to be displayed
- * in the list and associates with a {@link SharedPreferences} to
- * store/retrieve the preference data.
- * <p>
- * When specifying a preference hierarchy in XML, each element can point to a
- * subclass of {@link Preference}, similar to the view hierarchy and layouts.
- * <p>
- * This class contains a {@code key} that will be used as the key into the
- * {@link SharedPreferences}. It is up to the subclass to decide how to store
- * the value.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about building a settings UI with Preferences,
- * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
- * guide.</p>
- * </div>
- *
- * @attr name android:icon
- * @attr name android:key
- * @attr name android:title
- * @attr name android:summary
- * @attr name android:order
- * @attr name android:fragment
- * @attr name android:layout
- * @attr name android:widgetLayout
- * @attr name android:enabled
- * @attr name android:selectable
- * @attr name android:dependency
- * @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> {
-    /**
-     * Specify for {@link #setOrder(int)} if a specific order is not required.
-     */
-    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.
-     */
-    private long mId;
-
-    /**
-     * Set true temporarily to keep {@link #onAttachedToHierarchy(PreferenceManager)} from
-     * overwriting mId
-     */
-    private boolean mHasId;
-
-    private OnPreferenceChangeListener mOnChangeListener;
-    private OnPreferenceClickListener mOnClickListener;
-
-    private int mOrder = DEFAULT_ORDER;
-    private int mViewId = 0;
-    private CharSequence mTitle;
-    private CharSequence mSummary;
-    /**
-     * mIconResId is overridden by mIcon, if mIcon is specified.
-     */
-    private int mIconResId;
-    private Drawable mIcon;
-    private String mKey;
-    private Intent mIntent;
-    private String mFragment;
-    private Bundle mExtras;
-    private boolean mEnabled = true;
-    private boolean mSelectable = true;
-    private boolean mRequiresKey;
-    private boolean mPersistent = true;
-    private String mDependencyKey;
-    private Object mDefaultValue;
-    private boolean mDependencyMet = true;
-    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)
-     */
-    private boolean mShouldDisableView = true;
-
-    private int mLayoutResId = R.layout.preference;
-    private int mWidgetLayoutResId;
-
-    private OnPreferenceChangeInternalListener mListener;
-
-    private List<Preference> mDependents;
-    private PreferenceGroup mParentGroup;
-
-    private boolean mWasDetached;
-    private boolean mBaseMethodCalled;
-
-    private final View.OnClickListener mClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            performClick(v);
-        }
-    };
-
-    /**
-     * Interface definition for a callback to be invoked when the value of this
-     * {@link Preference} has been changed by the user and is
-     * about to be set and/or persisted.  This gives the client a chance
-     * to prevent setting and/or persisting the value.
-     */
-    public interface OnPreferenceChangeListener {
-        /**
-         * Called when a Preference has been changed by the user. This is
-         * called before the state of the Preference is about to be updated and
-         * before the state is persisted.
-         *
-         * @param preference The changed Preference.
-         * @param newValue The new value of the Preference.
-         * @return True to update the state of the Preference with the new value.
-         */
-        boolean onPreferenceChange(Preference preference, Object newValue);
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when a {@link Preference} is
-     * clicked.
-     */
-    public interface OnPreferenceClickListener {
-        /**
-         * Called when a Preference has been clicked.
-         *
-         * @param preference The Preference that was clicked.
-         * @return True if the click was handled.
-         */
-        boolean onPreferenceClick(Preference preference);
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when this
-     * {@link Preference} is changed or, if this is a group, there is an
-     * addition/removal of {@link Preference}(s). This is used internally.
-     */
-    interface OnPreferenceChangeInternalListener {
-        /**
-         * Called when this Preference has changed.
-         *
-         * @param preference This preference.
-         */
-        void onPreferenceChange(Preference preference);
-
-        /**
-         * Called when this group has added/removed {@link Preference}(s).
-         *
-         * @param preference This Preference.
-         */
-        void onPreferenceHierarchyChange(Preference preference);
-
-        /**
-         * Called when this preference has changed its visibility.
-         *
-         * @param preference This Preference.
-         */
-        void onPreferenceVisibilityChange(Preference preference);
-    }
-
-    /**
-     * Perform inflation from XML and apply a class-specific base style. This
-     * constructor of Preference allows subclasses to use their own base style
-     * when they are inflating. For example, a {@link CheckBoxPreference}
-     * constructor calls this version of the super class constructor and
-     * supplies {@code android.R.attr.checkBoxPreferenceStyle} for
-     * <var>defStyleAttr</var>. This allows the theme's checkbox preference
-     * style to modify all of the base preference attributes as well as the
-     * {@link CheckBoxPreference} class's attributes.
-     *
-     * @param context The Context this is associated with, through which it can
-     *            access the current theme, resources,
-     *            {@link android.content.SharedPreferences}, etc.
-     * @param attrs The attributes of the XML tag that is inflating the
-     *            preference.
-     * @param defStyleAttr 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 A resource identifier of a style resource that
-     *            supplies default values for the view, used only if
-     *            defStyleAttr is 0 or can not be found in the theme. Can be 0
-     *            to not look for defaults.
-     * @see #Preference(Context, android.util.AttributeSet)
-     */
-    public Preference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        mContext = context;
-
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.Preference, defStyleAttr, defStyleRes);
-
-        mIconResId = TypedArrayUtils.getResourceId(a, R.styleable.Preference_icon,
-                R.styleable.Preference_android_icon, 0);
-
-        mKey = TypedArrayUtils.getString(a, R.styleable.Preference_key,
-                R.styleable.Preference_android_key);
-
-        mTitle = TypedArrayUtils.getText(a, R.styleable.Preference_title,
-                R.styleable.Preference_android_title);
-
-        mSummary = TypedArrayUtils.getText(a, R.styleable.Preference_summary,
-                R.styleable.Preference_android_summary);
-
-        mOrder = TypedArrayUtils.getInt(a, R.styleable.Preference_order,
-                R.styleable.Preference_android_order, DEFAULT_ORDER);
-
-        mFragment = TypedArrayUtils.getString(a, R.styleable.Preference_fragment,
-                R.styleable.Preference_android_fragment);
-
-        mLayoutResId = TypedArrayUtils.getResourceId(a, R.styleable.Preference_layout,
-                R.styleable.Preference_android_layout, R.layout.preference);
-
-        mWidgetLayoutResId = TypedArrayUtils.getResourceId(a, R.styleable.Preference_widgetLayout,
-                R.styleable.Preference_android_widgetLayout, 0);
-
-        mEnabled = TypedArrayUtils.getBoolean(a, R.styleable.Preference_enabled,
-                R.styleable.Preference_android_enabled, true);
-
-        mSelectable = TypedArrayUtils.getBoolean(a, R.styleable.Preference_selectable,
-                R.styleable.Preference_android_selectable, true);
-
-        mPersistent = TypedArrayUtils.getBoolean(a, R.styleable.Preference_persistent,
-                R.styleable.Preference_android_persistent, true);
-
-        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)) {
-            mDefaultValue = onGetDefaultValue(a, R.styleable.Preference_android_defaultValue);
-        }
-
-        mShouldDisableView =
-                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();
-    }
-
-    /**
-     * Perform inflation from XML and apply a class-specific base style. This
-     * constructor of Preference allows subclasses to use their own base style
-     * when they are inflating. For example, a {@link CheckBoxPreference}
-     * constructor calls this version of the super class constructor and
-     * supplies {@code android.R.attr.checkBoxPreferenceStyle} for
-     * <var>defStyleAttr</var>. This allows the theme's checkbox preference
-     * style to modify all of the base preference attributes as well as the
-     * {@link CheckBoxPreference} class's attributes.
-     *
-     * @param context The Context this is associated with, through which it can
-     *            access the current theme, resources,
-     *            {@link android.content.SharedPreferences}, etc.
-     * @param attrs The attributes of the XML tag that is inflating the
-     *            preference.
-     * @param defStyleAttr 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.
-     * @see #Preference(Context, AttributeSet)
-     */
-    public Preference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    /**
-     * Constructor that is called when inflating a Preference from XML. This is
-     * called when a Preference 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.
-     *
-     * @param context The Context this is associated with, through which it can
-     *            access the current theme, resources, {@link android.content.SharedPreferences},
-     *            etc.
-     * @param attrs The attributes of the XML tag that is inflating the
-     *            preference.
-     * @see #Preference(Context, AttributeSet, int)
-     */
-    public Preference(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
-                android.R.attr.preferenceStyle));
-    }
-
-    /**
-     * Constructor to create a Preference.
-     *
-     * @param context The Context in which to store Preference values.
-     */
-    public Preference(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Called when a Preference is being inflated and the default value
-     * attribute needs to be read. Since different Preference types have
-     * different value types, the subclass should get and return the default
-     * value which will be its value type.
-     * <p>
-     * For example, if the value type is String, the body of the method would
-     * proxy to {@link TypedArray#getString(int)}.
-     *
-     * @param a The set of attributes.
-     * @param index The index of the default value attribute.
-     * @return The default value of this preference type.
-     */
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return null;
-    }
-
-    /**
-     * Sets an {@link Intent} to be used for
-     * {@link Context#startActivity(Intent)} when this Preference is clicked.
-     *
-     * @param intent The intent associated with this Preference.
-     */
-    public void setIntent(Intent intent) {
-        mIntent = intent;
-    }
-
-    /**
-     * Return the {@link Intent} associated with this Preference.
-     *
-     * @return The {@link Intent} last set via {@link #setIntent(Intent)} or XML.
-     */
-    public Intent getIntent() {
-        return mIntent;
-    }
-
-    /**
-     * Sets the class name of a fragment to be shown when this Preference is clicked.
-     *
-     * @param fragment The class name of the fragment associated with this Preference.
-     */
-    public void setFragment(String fragment) {
-        mFragment = fragment;
-    }
-
-    /**
-     * Return the fragment class name associated with this Preference.
-     *
-     * @return The fragment class name last set via {@link #setFragment} or XML.
-     */
-    public String getFragment() {
-        return mFragment;
-    }
-
-    /**
-     * 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.
-     */
-    public Bundle getExtras() {
-        if (mExtras == null) {
-            mExtras = new Bundle();
-        }
-        return mExtras;
-    }
-
-    /**
-     * Return the extras Bundle object associated with this preference,
-     * returning null if there is not currently one.
-     */
-    public Bundle peekExtras() {
-        return mExtras;
-    }
-
-    /**
-     * Sets the layout resource that is inflated as the {@link View} to be shown
-     * for this Preference. In most cases, the default layout is sufficient for
-     * custom Preference objects and only the widget layout needs to be changed.
-     * <p>
-     * This layout should contain a {@link ViewGroup} with ID
-     * {@link android.R.id#widget_frame} to be the parent of the specific widget
-     * for this Preference. It should similarly contain
-     * {@link android.R.id#title} and {@link android.R.id#summary}.
-     * <p>
-     * It is an error to change the layout after adding the preference to a {@link PreferenceGroup}
-     *
-     * @param layoutResId The layout resource ID to be inflated and returned as
-     *            a {@link View}.
-     * @see #setWidgetLayoutResource(int)
-     */
-    public void setLayoutResource(int layoutResId) {
-        mLayoutResId = layoutResId;
-    }
-
-    /**
-     * Gets the layout resource that will be shown as the {@link View} for this Preference.
-     *
-     * @return The layout resource ID.
-     */
-    public final int getLayoutResource() {
-        return mLayoutResId;
-    }
-
-    /**
-     * Sets the layout for the controllable widget portion of this Preference. This
-     * is inflated into the main layout. For example, a {@link CheckBoxPreference}
-     * would specify a custom layout (consisting of just the CheckBox) here,
-     * instead of creating its own main layout.
-     * <p>
-     * It is an error to change the layout after adding the preference to a {@link PreferenceGroup}
-     *
-     * @param widgetLayoutResId The layout resource ID to be inflated into the
-     *            main layout.
-     * @see #setLayoutResource(int)
-     */
-    public void setWidgetLayoutResource(int widgetLayoutResId) {
-        mWidgetLayoutResId = widgetLayoutResId;
-    }
-
-    /**
-     * Gets the layout resource for the controllable widget portion of this Preference.
-     *
-     * @return The layout resource ID.
-     */
-    public final int getWidgetLayoutResource() {
-        return mWidgetLayoutResId;
-    }
-
-    /**
-     * Binds the created View to the data for this Preference.
-     * <p>
-     * This is a good place to grab references to custom Views in the layout and
-     * set properties on them.
-     * <p>
-     * Make sure to call through to the superclass's implementation.
-     *
-     * @param holder The ViewHolder that provides references to the views to fill in. These views
-     *               will be recycled, so you should not hold a reference to them after this method
-     *               returns.
-     */
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        holder.itemView.setOnClickListener(mClickListener);
-        holder.itemView.setId(mViewId);
-
-        final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
-        if (titleView != null) {
-            final CharSequence title = getTitle();
-            if (!TextUtils.isEmpty(title)) {
-                titleView.setText(title);
-                titleView.setVisibility(View.VISIBLE);
-                if (mHasSingleLineTitleAttr) {
-                    titleView.setSingleLine(mSingleLineTitle);
-                }
-            } else {
-                titleView.setVisibility(View.GONE);
-            }
-        }
-
-        final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
-        if (summaryView != null) {
-            final CharSequence summary = getSummary();
-            if (!TextUtils.isEmpty(summary)) {
-                summaryView.setText(summary);
-                summaryView.setVisibility(View.VISIBLE);
-            } else {
-                summaryView.setVisibility(View.GONE);
-            }
-        }
-
-        final ImageView imageView = (ImageView) holder.findViewById(android.R.id.icon);
-        if (imageView != null) {
-            if (mIconResId != 0 || mIcon != null) {
-                if (mIcon == null) {
-                    mIcon = ContextCompat.getDrawable(getContext(), mIconResId);
-                }
-                if (mIcon != null) {
-                    imageView.setImageDrawable(mIcon);
-                }
-            }
-            if (mIcon != null) {
-                imageView.setVisibility(View.VISIBLE);
-            } else {
-                imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
-            }
-        }
-
-        View imageFrame = holder.findViewById(R.id.icon_frame);
-        if (imageFrame == null) {
-            imageFrame = holder.findViewById(AndroidResources.ANDROID_R_ICON_FRAME);
-        }
-        if (imageFrame != null) {
-            if (mIcon != null) {
-                imageFrame.setVisibility(View.VISIBLE);
-            } else {
-                imageFrame.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
-            }
-        }
-
-        if (mShouldDisableView) {
-            setEnabledStateOnViews(holder.itemView, isEnabled());
-        } else {
-            setEnabledStateOnViews(holder.itemView, true);
-        }
-
-        final boolean selectable = isSelectable();
-        holder.itemView.setFocusable(selectable);
-        holder.itemView.setClickable(selectable);
-
-        holder.setDividerAllowedAbove(mAllowDividerAbove);
-        holder.setDividerAllowedBelow(mAllowDividerBelow);
-    }
-
-    /**
-     * Makes sure the view (and any children) get the enabled state changed.
-     */
-    private void setEnabledStateOnViews(View v, boolean enabled) {
-        v.setEnabled(enabled);
-
-        if (v instanceof ViewGroup) {
-            final ViewGroup vg = (ViewGroup) v;
-            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
-                setEnabledStateOnViews(vg.getChildAt(i), enabled);
-            }
-        }
-    }
-
-    /**
-     * Sets the order of this Preference with respect to other
-     * Preference objects on the same level. If this is not specified, the
-     * default behavior is to sort alphabetically. The
-     * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
-     * Preference objects based on the order they appear in the XML.
-     *
-     * @param order The order for this Preference. A lower value will be shown
-     *            first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
-     *            allow ordering from XML.
-     * @see PreferenceGroup#setOrderingAsAdded(boolean)
-     * @see #DEFAULT_ORDER
-     */
-    public void setOrder(int order) {
-        if (order != mOrder) {
-            mOrder = order;
-
-            // Reorder the list
-            notifyHierarchyChanged();
-        }
-    }
-
-    /**
-     * Gets the order of this Preference with respect to other Preference objects
-     * on the same level.
-     *
-     * @return The order of this Preference.
-     * @see #setOrder(int)
-     */
-    public int getOrder() {
-        return mOrder;
-    }
-
-    /**
-     * Set the ID that will be assigned to the overall View representing this
-     * preference, once bound.
-     *
-     * @see View#setId(int)
-     */
-    public void setViewId(int viewId) {
-        mViewId = viewId;
-    }
-
-    /**
-     * Sets the title for this Preference with a CharSequence.
-     * This title will be placed into the ID
-     * {@link android.R.id#title} within the View bound by
-     * {@link #onBindViewHolder(PreferenceViewHolder)}.
-     *
-     * @param title The title for this Preference.
-     */
-    public void setTitle(CharSequence title) {
-        if ((title == null && mTitle != null) || (title != null && !title.equals(mTitle))) {
-            mTitle = title;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sets the title for this Preference with a resource ID.
-     *
-     * @see #setTitle(CharSequence)
-     * @param titleResId The title as a resource ID.
-     */
-    public void setTitle(int titleResId) {
-        setTitle(mContext.getString(titleResId));
-    }
-
-    /**
-     * Returns the title of this Preference.
-     *
-     * @return The title.
-     * @see #setTitle(CharSequence)
-     */
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Sets the icon for this Preference with a Drawable.
-     * This icon will be placed into the ID
-     * {@link android.R.id#icon} within the View created by
-     * {@link #onBindViewHolder(PreferenceViewHolder)}.
-     *
-     * @param icon The optional icon for this Preference.
-     */
-    public void setIcon(Drawable icon) {
-        if ((icon == null && mIcon != null) || (icon != null && mIcon != icon)) {
-            mIcon = icon;
-            mIconResId = 0;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sets the icon for this Preference with a resource ID.
-     *
-     * @see #setIcon(Drawable)
-     * @param iconResId The icon as a resource ID.
-     */
-    public void setIcon(int iconResId) {
-        setIcon(ContextCompat.getDrawable(mContext, iconResId));
-        mIconResId = iconResId;
-    }
-
-    /**
-     * Returns the icon of this Preference.
-     *
-     * @return The icon.
-     * @see #setIcon(Drawable)
-     */
-    public Drawable getIcon() {
-        if (mIcon == null && mIconResId != 0) {
-            mIcon = ContextCompat.getDrawable(mContext, mIconResId);
-        }
-        return mIcon;
-    }
-
-    /**
-     * Returns the summary of this Preference.
-     *
-     * @return The summary.
-     * @see #setSummary(CharSequence)
-     */
-    public CharSequence getSummary() {
-        return mSummary;
-    }
-
-    /**
-     * Sets the summary for this Preference with a CharSequence.
-     *
-     * @param summary The summary for the preference.
-     */
-    public void setSummary(CharSequence summary) {
-        if ((summary == null && mSummary != null)
-                || (summary != null && !summary.equals(mSummary))) {
-            mSummary = summary;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sets the summary for this Preference with a resource ID.
-     *
-     * @see #setSummary(CharSequence)
-     * @param summaryResId The summary as a resource.
-     */
-    public void setSummary(int summaryResId) {
-        setSummary(mContext.getString(summaryResId));
-    }
-
-    /**
-     * Sets whether this Preference is enabled. If disabled, it will
-     * not handle clicks.
-     *
-     * @param enabled Set true to enable it.
-     */
-    public void setEnabled(boolean enabled) {
-        if (mEnabled != enabled) {
-            mEnabled = enabled;
-
-            // Enabled state can change dependent preferences' states, so notify
-            notifyDependencyChange(shouldDisableDependents());
-
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Checks whether this Preference should be enabled in the list.
-     *
-     * @return True if this Preference is enabled, false otherwise.
-     */
-    public boolean isEnabled() {
-        return mEnabled && mDependencyMet && mParentDependencyMet;
-    }
-
-    /**
-     * Sets whether this Preference is selectable.
-     *
-     * @param selectable Set true to make it selectable.
-     */
-    public void setSelectable(boolean selectable) {
-        if (mSelectable != selectable) {
-            mSelectable = selectable;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Checks whether this Preference should be selectable in the list.
-     *
-     * @return True if it is selectable, false otherwise.
-     */
-    public boolean isSelectable() {
-        return mSelectable;
-    }
-
-    /**
-     * Sets whether this Preference should disable its view when it gets
-     * disabled.
-     * <p>
-     * For example, set this and {@link #setEnabled(boolean)} to false for
-     * preferences that are only displaying information and 1) should not be
-     * clickable 2) should not have the view set to the disabled state.
-     *
-     * @param shouldDisableView Set true if this preference should disable its view
-     *            when the preference is disabled.
-     */
-    public void setShouldDisableView(boolean shouldDisableView) {
-        mShouldDisableView = shouldDisableView;
-        notifyChanged();
-    }
-
-    /**
-     * Checks whether this Preference should disable its view when it's action is disabled.
-     * @see #setShouldDisableView(boolean)
-     * @return True if it should disable the view.
-     */
-    public boolean getShouldDisableView() {
-        return mShouldDisableView;
-    }
-
-    /**
-     * Sets whether this preference should be visible in the list. If false, it is excluded from
-     * the adapter, but can still be retrieved using
-     * {@link PreferenceFragmentCompat#findPreference(CharSequence)}.
-     *
-     * @param visible Set false if this preference should be hidden from the list.
-     */
-    public final void setVisible(boolean visible) {
-        if (mVisible != visible) {
-            mVisible = visible;
-            if (mListener != null) {
-                mListener.onPreferenceVisibilityChange(this);
-            }
-        }
-    }
-
-    /**
-     * Checks whether this preference should be visible to the user in the list.
-     * @see #setVisible(boolean)
-     * @return True if this preference should be displayed.
-     */
-    public final boolean isVisible() {
-        return mVisible;
-    }
-
-    /**
-     * Returns a unique ID for this Preference.  This ID should be unique across all
-     * Preference objects in a hierarchy.
-     *
-     * @return A unique ID for this Preference.
-     */
-    long getId() {
-        return mId;
-    }
-
-    /**
-     * Processes a click on the preference. This includes saving the value to
-     * the {@link android.content.SharedPreferences}. However, the overridden method should
-     * call {@link #callChangeListener(Object)} to make sure the client wants to
-     * update the preference's state with the new value.
-     */
-    protected void onClick() {
-    }
-
-    /**
-     * 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.
-     */
-    public void setKey(String key) {
-        mKey = key;
-
-        if (mRequiresKey && !hasKey()) {
-            requireKey();
-        }
-    }
-
-    /**
-     * Gets the key for this Preference, which is also the key used for storing values into
-     * {@link SharedPreferences} or {@link PreferenceDataStore}.
-     *
-     * @return The key.
-     */
-    public String getKey() {
-        return mKey;
-    }
-
-    /**
-     * 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.
-     */
-    void requireKey() {
-        if (TextUtils.isEmpty(mKey)) {
-            throw new IllegalStateException("Preference does not have a key assigned.");
-        }
-
-        mRequiresKey = true;
-    }
-
-    /**
-     * Checks whether this Preference has a valid key.
-     *
-     * @return True if the key exists and is not a blank string, false otherwise.
-     */
-    public boolean hasKey() {
-        return !TextUtils.isEmpty(mKey);
-    }
-
-    /**
-     * Checks whether this Preference is persistent. If it is, it stores its value(s) into
-     * the persistent {@link SharedPreferences} storage by default or into
-     * {@link PreferenceDataStore} if assigned.
-     *
-     * @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 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 {@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 SharedPreferences} storage by default or into
-     * {@link PreferenceDataStore} if assigned.
-     *
-     * @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.
-     *
-     * @param newValue The new value of this Preference.
-     * @return True if the user value should be set as the preference
-     *         value (and persisted).
-     */
-    public boolean callChangeListener(Object newValue) {
-        return mOnChangeListener == null || mOnChangeListener.onPreferenceChange(this, newValue);
-    }
-
-    /**
-     * Sets the callback to be invoked when this Preference is changed by the
-     * user (but before the internal state has been updated).
-     *
-     * @param onPreferenceChangeListener The callback to be invoked.
-     */
-    public void setOnPreferenceChangeListener(
-            OnPreferenceChangeListener onPreferenceChangeListener) {
-        mOnChangeListener = onPreferenceChangeListener;
-    }
-
-    /**
-     * Returns the callback to be invoked when this Preference is changed by the
-     * user (but before the internal state has been updated).
-     *
-     * @return The callback to be invoked.
-     */
-    public OnPreferenceChangeListener getOnPreferenceChangeListener() {
-        return mOnChangeListener;
-    }
-
-    /**
-     * Sets the callback to be invoked when this Preference is clicked.
-     *
-     * @param onPreferenceClickListener The callback to be invoked.
-     */
-    public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
-        mOnClickListener = onPreferenceClickListener;
-    }
-
-    /**
-     * Returns the callback to be invoked when this Preference is clicked.
-     *
-     * @return The callback to be invoked.
-     */
-    public OnPreferenceClickListener getOnPreferenceClickListener() {
-        return mOnClickListener;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void performClick(View view) {
-        performClick();
-    }
-
-    /**
-     * Called when a click should be performed.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void performClick() {
-
-        if (!isEnabled()) {
-            return;
-        }
-
-        onClick();
-
-        if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
-            return;
-        }
-
-        PreferenceManager preferenceManager = getPreferenceManager();
-        if (preferenceManager != null) {
-            PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
-                    .getOnPreferenceTreeClickListener();
-            if (listener != null && listener.onPreferenceTreeClick(this)) {
-                return;
-            }
-        }
-
-        if (mIntent != null) {
-            Context context = getContext();
-            context.startActivity(mIntent);
-        }
-    }
-
-    /**
-     * Returns the {@link android.content.Context} of this Preference.
-     * Each Preference in a Preference hierarchy can be
-     * from different Context (for example, if multiple activities provide preferences into a single
-     * {@link PreferenceFragmentCompat}). This Context will be used to save the Preference values.
-     *
-     * @return The Context of this Preference.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Returns the {@link android.content.SharedPreferences} where this Preference can read its
-     * value(s). Usually, it's easier to use one of the helper read methods:
-     * {@link #getPersistedBoolean(boolean)}, {@link #getPersistedFloat(float)},
-     * {@link #getPersistedInt(int)}, {@link #getPersistedLong(long)},
-     * {@link #getPersistedString(String)}.
-     *
-     * @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 || getPreferenceDataStore() != null) {
-            return null;
-        }
-
-        return mPreferenceManager.getSharedPreferences();
-    }
-
-    /**
-     * Compares Preference objects based on order (if set), otherwise alphabetically on the titles.
-     *
-     * @param another The Preference to compare to this one.
-     * @return 0 if the same; less than 0 if this Preference sorts ahead of <var>another</var>;
-     *          greater than 0 if this Preference sorts after <var>another</var>.
-     */
-    @Override
-    public int compareTo(@NonNull Preference another) {
-        if (mOrder != another.mOrder) {
-            // Do order comparison
-            return mOrder - another.mOrder;
-        } else if (mTitle == another.mTitle) {
-            // If titles are null or share same object comparison
-            return 0;
-        } else if (mTitle == null) {
-            return 1;
-        } else if (another.mTitle == null) {
-            return -1;
-        } else {
-            // Do name comparison
-            return mTitle.toString().compareToIgnoreCase(another.mTitle.toString());
-        }
-    }
-
-    /**
-     * Sets the internal change listener.
-     *
-     * @param listener The listener.
-     * @see #notifyChanged()
-     */
-    final void setOnPreferenceChangeInternalListener(OnPreferenceChangeInternalListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Should be called when the data of this {@link Preference} has changed.
-     */
-    protected void notifyChanged() {
-        if (mListener != null) {
-            mListener.onPreferenceChange(this);
-        }
-    }
-
-    /**
-     * Should be called when a Preference has been
-     * added/removed from this group, or the ordering should be
-     * re-evaluated.
-     */
-    protected void notifyHierarchyChanged() {
-        if (mListener != null) {
-            mListener.onPreferenceHierarchyChange(this);
-        }
-    }
-
-    /**
-     * Gets the {@link PreferenceManager} that manages this Preference object's tree.
-     *
-     * @return The {@link PreferenceManager}.
-     */
-    public PreferenceManager getPreferenceManager() {
-        return mPreferenceManager;
-    }
-
-    /**
-     * Called when this Preference has been attached to a Preference hierarchy.
-     * Make sure to call the super implementation.
-     *
-     * @param preferenceManager The PreferenceManager of the hierarchy.
-     */
-    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
-        mPreferenceManager = preferenceManager;
-
-        if (!mHasId) {
-            mId = preferenceManager.getNextId();
-        }
-
-        dispatchSetInitialValue();
-    }
-
-    /**
-     * Called from {@link PreferenceGroup} to pass in an ID for reuse
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void onAttachedToHierarchy(PreferenceManager preferenceManager, long id) {
-        mId = id;
-        mHasId = true;
-        try {
-            onAttachedToHierarchy(preferenceManager);
-        } finally {
-            mHasId = false;
-        }
-    }
-
-    /**
-     * 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
-     * to the list of preferences.
-     */
-    public void onAttached() {
-        // At this point, the hierarchy that this preference is in is connected
-        // with all other preferences.
-        registerDependency();
-    }
-
-    /**
-     * Called when the Preference hierarchy has been detached from the
-     * list of preferences. This can also be called when this
-     * Preference has been removed from a group that was attached
-     * to the list of preferences.
-     */
-    public void onDetached() {
-        unregisterDependency();
-        mWasDetached = true;
-    }
-
-    /**
-     * Returns true if {@link #onDetached()} was called. Used for handling the case when a
-     * preference was removed, modified, and re-added to a {@link PreferenceGroup}
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    public final boolean wasDetached() {
-        return mWasDetached;
-    }
-
-    /**
-     * Clears the {@link #wasDetached()} status
-     * @hide
-     */
-    @RestrictTo(LIBRARY)
-    public final void clearWasDetached() {
-        mWasDetached = false;
-    }
-
-    private void registerDependency() {
-
-        if (TextUtils.isEmpty(mDependencyKey)) return;
-
-        Preference preference = findPreferenceInHierarchy(mDependencyKey);
-        if (preference != null) {
-            preference.registerDependent(this);
-        } else {
-            throw new IllegalStateException("Dependency \"" + mDependencyKey
-                    + "\" not found for preference \"" + mKey + "\" (title: \"" + mTitle + "\"");
-        }
-    }
-
-    private void unregisterDependency() {
-        if (mDependencyKey != null) {
-            final Preference oldDependency = findPreferenceInHierarchy(mDependencyKey);
-            if (oldDependency != null) {
-                oldDependency.unregisterDependent(this);
-            }
-        }
-    }
-
-    /**
-     * Finds a Preference in this hierarchy (the whole thing,
-     * even above/below your {@link PreferenceScreen} screen break) with the given
-     * key.
-     * <p>
-     * This only functions after we have been attached to a hierarchy.
-     *
-     * @param key The key of the Preference to find.
-     * @return The Preference that uses the given key.
-     */
-    protected Preference findPreferenceInHierarchy(String key) {
-        if (TextUtils.isEmpty(key) || mPreferenceManager == null) {
-            return null;
-        }
-
-        return mPreferenceManager.findPreference(key);
-    }
-
-    /**
-     * Adds a dependent Preference on this Preference so we can notify it.
-     * Usually, the dependent Preference registers itself (it's good for it to
-     * know it depends on something), so please use
-     * {@link Preference#setDependency(String)} on the dependent Preference.
-     *
-     * @param dependent The dependent Preference that will be enabled/disabled
-     *            according to the state of this Preference.
-     */
-    private void registerDependent(Preference dependent) {
-        if (mDependents == null) {
-            mDependents = new ArrayList<Preference>();
-        }
-
-        mDependents.add(dependent);
-
-        dependent.onDependencyChanged(this, shouldDisableDependents());
-    }
-
-    /**
-     * Removes a dependent Preference on this Preference.
-     *
-     * @param dependent The dependent Preference that will be enabled/disabled
-     *            according to the state of this Preference.
-     */
-    private void unregisterDependent(Preference dependent) {
-        if (mDependents != null) {
-            mDependents.remove(dependent);
-        }
-    }
-
-    /**
-     * Notifies any listening dependents of a change that affects the
-     * dependency.
-     *
-     * @param disableDependents Whether this Preference should disable
-     *            its dependents.
-     */
-    public void notifyDependencyChange(boolean disableDependents) {
-        final List<Preference> dependents = mDependents;
-
-        if (dependents == null) {
-            return;
-        }
-
-        final int dependentsCount = dependents.size();
-        for (int i = 0; i < dependentsCount; i++) {
-            dependents.get(i).onDependencyChanged(this, disableDependents);
-        }
-    }
-
-    /**
-     * Called when the dependency changes.
-     *
-     * @param dependency The Preference that this Preference depends on.
-     * @param disableDependent Set true to disable this Preference.
-     */
-    public void onDependencyChanged(Preference dependency, boolean disableDependent) {
-        if (mDependencyMet == disableDependent) {
-            mDependencyMet = !disableDependent;
-
-            // Enabled state can change dependent preferences' states, so notify
-            notifyDependencyChange(shouldDisableDependents());
-
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Called when the implicit parent dependency changes.
-     *
-     * @param parent The Preference that this Preference depends on.
-     * @param disableChild Set true to disable this Preference.
-     */
-    public void onParentChanged(Preference parent, boolean disableChild) {
-        if (mParentDependencyMet == disableChild) {
-            mParentDependencyMet = !disableChild;
-
-            // Enabled state can change dependent preferences' states, so notify
-            notifyDependencyChange(shouldDisableDependents());
-
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Checks whether this preference's dependents should currently be
-     * disabled.
-     *
-     * @return True if the dependents should be disabled, otherwise false.
-     */
-    public boolean shouldDisableDependents() {
-        return !isEnabled();
-    }
-
-    /**
-     * Sets the key of a Preference that this Preference will depend on. If that
-     * Preference is not set or is off, this Preference will be disabled.
-     *
-     * @param dependencyKey The key of the Preference that this depends on.
-     */
-    public void setDependency(String dependencyKey) {
-        // Unregister the old dependency, if we had one
-        unregisterDependency();
-
-        // Register the new
-        mDependencyKey = dependencyKey;
-        registerDependency();
-    }
-
-    /**
-     * Returns the key of the dependency on this Preference.
-     *
-     * @return The key of the dependency.
-     * @see #setDependency(String)
-     */
-    public String getDependency() {
-        return mDependencyKey;
-    }
-
-    /**
-     * 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.
-     */
-    protected void onPrepareForRemoval() {
-        unregisterDependency();
-    }
-
-    /**
-     * Sets the default value for this Preference, which will be set either if
-     * persistence is off or persistence is on and the preference is not found
-     * in the persistent storage.
-     *
-     * @param defaultValue The default value.
-     */
-    public void setDefaultValue(Object defaultValue) {
-        mDefaultValue = defaultValue;
-    }
-
-    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)) {
-            if (mDefaultValue != null) {
-                onSetInitialValue(false, mDefaultValue);
-            }
-        } else {
-            onSetInitialValue(true, null);
-        }
-    }
-
-    /**
-     * Implement this to set the initial value of the Preference.
-     *
-     * <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>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;
-     *            false to use the given <var>defaultValue</var>.
-     * @param defaultValue The default value for this Preference. Only use this
-     *            if <var>restorePersistedValue</var> is false.
-     */
-    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
-    }
-
-    private void tryCommit(@NonNull SharedPreferences.Editor editor) {
-        if (mPreferenceManager.shouldCommit()) {
-            editor.apply();
-        }
-    }
-
-    /**
-     * 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 {@code true} if the Preference is persistent, {@code false} otherwise
-     * @see #getPersistedString(String)
-     */
-    protected boolean persistString(String value) {
-        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;
-    }
-
-    /**
-     * 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 storage or the default return value
-     * @see #persistString(String)
-     */
-    protected String getPersistedString(String defaultReturnValue) {
-        if (!shouldPersist()) {
-            return defaultReturnValue;
-        }
-
-        PreferenceDataStore dataStore = getPreferenceDataStore();
-        if (dataStore != null) {
-            return dataStore.getString(mKey, defaultReturnValue);
-        }
-
-        return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
-    }
-
-    /**
-     * 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 {@code true} if the Preference is persistent, {@code false} otherwise
-     * @see #persistString(String)
-     * @see #getPersistedInt(int)
-     */
-    protected boolean persistInt(int value) {
-        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;
-    }
-
-    /**
-     * 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 storage or the default return value
-     * @see #getPersistedString(String)
-     * @see #persistInt(int)
-     */
-    protected int getPersistedInt(int defaultReturnValue) {
-        if (!shouldPersist()) {
-            return defaultReturnValue;
-        }
-
-        PreferenceDataStore dataStore = getPreferenceDataStore();
-        if (dataStore != null) {
-            return dataStore.getInt(mKey, defaultReturnValue);
-        }
-
-        return mPreferenceManager.getSharedPreferences().getInt(mKey, defaultReturnValue);
-    }
-
-    /**
-     * 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 {@code true} if the Preference is persistent, {@code false} otherwise
-     * @see #persistString(String)
-     * @see #getPersistedFloat(float)
-     */
-    protected boolean persistFloat(float value) {
-        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;
-    }
-
-    /**
-     * 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 storage or the default return value
-     * @see #getPersistedString(String)
-     * @see #persistFloat(float)
-     */
-    protected float getPersistedFloat(float defaultReturnValue) {
-        if (!shouldPersist()) {
-            return defaultReturnValue;
-        }
-
-        PreferenceDataStore dataStore = getPreferenceDataStore();
-        if (dataStore != null) {
-            return dataStore.getFloat(mKey, defaultReturnValue);
-        }
-
-        return mPreferenceManager.getSharedPreferences().getFloat(mKey, defaultReturnValue);
-    }
-
-    /**
-     * 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 {@code true} if the Preference is persistent, {@code false} otherwise
-     * @see #persistString(String)
-     * @see #getPersistedLong(long)
-     */
-    protected boolean persistLong(long value) {
-        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;
-    }
-
-    /**
-     * 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 storage or the default return value
-     * @see #getPersistedString(String)
-     * @see #persistLong(long)
-     */
-    protected long getPersistedLong(long defaultReturnValue) {
-        if (!shouldPersist()) {
-            return defaultReturnValue;
-        }
-
-        PreferenceDataStore dataStore = getPreferenceDataStore();
-        if (dataStore != null) {
-            return dataStore.getLong(mKey, defaultReturnValue);
-        }
-
-        return mPreferenceManager.getSharedPreferences().getLong(mKey, defaultReturnValue);
-    }
-
-    /**
-     * 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 {@code true} if the Preference is persistent, {@code false} otherwise
-     * @see #persistString(String)
-     * @see #getPersistedBoolean(boolean)
-     */
-    protected boolean persistBoolean(boolean value) {
-        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;
-    }
-
-    /**
-     * 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 storage or the default return value
-     * @see #getPersistedString(String)
-     * @see #persistBoolean(boolean)
-     */
-    protected boolean getPersistedBoolean(boolean defaultReturnValue) {
-        if (!shouldPersist()) {
-            return defaultReturnValue;
-        }
-
-        PreferenceDataStore dataStore = getPreferenceDataStore();
-        if (dataStore != null) {
-            return dataStore.getBoolean(mKey, defaultReturnValue);
-        }
-
-        return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
-    }
-
-    @Override
-    public String toString() {
-        return getFilterableStringBuilder().toString();
-    }
-
-    /**
-     * Returns the text that will be used to filter this Preference depending on
-     * user input.
-     * <p>
-     * 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
-     *         preference. By default, this is the title and summary
-     *         (concatenated with a space).
-     */
-    StringBuilder getFilterableStringBuilder() {
-        StringBuilder sb = new StringBuilder();
-        CharSequence title = getTitle();
-        if (!TextUtils.isEmpty(title)) {
-            sb.append(title).append(' ');
-        }
-        CharSequence summary = getSummary();
-        if (!TextUtils.isEmpty(summary)) {
-            sb.append(summary).append(' ');
-        }
-        if (sb.length() > 0) {
-            // Drop the last space
-            sb.setLength(sb.length() - 1);
-        }
-        return sb;
-    }
-
-    /**
-     * Store this Preference hierarchy's frozen state into the given container.
-     *
-     * @param container The Bundle in which to save the instance of this Preference.
-     *
-     * @see #restoreHierarchyState
-     * @see #onSaveInstanceState
-     */
-    public void saveHierarchyState(Bundle container) {
-        dispatchSaveInstanceState(container);
-    }
-
-    /**
-     * 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.
-     *
-     * @see #saveHierarchyState
-     * @see #onSaveInstanceState
-     */
-    void dispatchSaveInstanceState(Bundle container) {
-        if (hasKey()) {
-            mBaseMethodCalled = false;
-            Parcelable state = onSaveInstanceState();
-            if (!mBaseMethodCalled) {
-                throw new IllegalStateException(
-                        "Derived class did not call super.onSaveInstanceState()");
-            }
-            if (state != null) {
-                container.putParcelable(mKey, state);
-            }
-        }
-    }
-
-    /**
-     * Hook allowing a Preference to generate a representation of its internal
-     * state that can later be used to create a new instance with that same
-     * state. This state should only contain information that is not persistent
-     * or can be reconstructed later.
-     *
-     * @return A Parcelable object containing the current dynamic state of
-     *         this Preference, or null if there is nothing interesting to save.
-     *         The default implementation returns null.
-     * @see #onRestoreInstanceState
-     * @see #saveHierarchyState
-     */
-    protected Parcelable onSaveInstanceState() {
-        mBaseMethodCalled = true;
-        return BaseSavedState.EMPTY_STATE;
-    }
-
-    /**
-     * Restore this Preference hierarchy's previously saved state from the given container.
-     *
-     * @param container The Bundle that holds the previously saved state.
-     *
-     * @see #saveHierarchyState
-     * @see #onRestoreInstanceState
-     */
-    public void restoreHierarchyState(Bundle container) {
-        dispatchRestoreInstanceState(container);
-    }
-
-    /**
-     * Called by {@link #restoreHierarchyState} to retrieve the saved state for this
-     * Preference and its children. May be overridden to modify how restoring
-     * happens to the children of a Preference. For example, some Preference objects may
-     * not want to save state for their children.
-     *
-     * @param container The Bundle that holds the previously saved state.
-     * @see #restoreHierarchyState
-     * @see #onRestoreInstanceState
-     */
-    void dispatchRestoreInstanceState(Bundle container) {
-        if (hasKey()) {
-            Parcelable state = container.getParcelable(mKey);
-            if (state != null) {
-                mBaseMethodCalled = false;
-                onRestoreInstanceState(state);
-                if (!mBaseMethodCalled) {
-                    throw new IllegalStateException(
-                            "Derived class did not call super.onRestoreInstanceState()");
-                }
-            }
-        }
-    }
-
-    /**
-     * Hook allowing a Preference to re-apply a representation of its internal
-     * state that had previously been generated by {@link #onSaveInstanceState}.
-     * This function will never be called with a null state.
-     *
-     * @param state The saved state that had previously been returned by
-     *            {@link #onSaveInstanceState}.
-     * @see #onSaveInstanceState
-     * @see #restoreHierarchyState
-     */
-    protected void onRestoreInstanceState(Parcelable state) {
-        mBaseMethodCalled = true;
-        if (state != BaseSavedState.EMPTY_STATE && state != null) {
-            throw new IllegalArgumentException("Wrong state class -- expecting Preference State");
-        }
-    }
-
-    /**
-     * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information
-     * about the View for this Preference.
-     */
-    @CallSuper
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
-    }
-
-    /**
-     * A base class for managing the instance state of a {@link Preference}.
-     */
-    public static class BaseSavedState extends AbsSavedState {
-        public BaseSavedState(Parcel source) {
-            super(source);
-        }
-
-        public BaseSavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        public static final Parcelable.Creator<BaseSavedState> CREATOR =
-                new Parcelable.Creator<BaseSavedState>() {
-                    @Override
-                    public BaseSavedState createFromParcel(Parcel in) {
-                        return new BaseSavedState(in);
-                    }
-
-                    @Override
-                    public BaseSavedState[] newArray(int size) {
-                        return new BaseSavedState[size];
-                    }
-                };
-    }
-
-}
diff --git a/android/support/v7/preference/PreferenceCategory.java b/android/support/v7/preference/PreferenceCategory.java
deleted file mode 100644
index 25f0b69..0000000
--- a/android/support/v7/preference/PreferenceCategory.java
+++ /dev/null
@@ -1,95 +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.preference;
-
-import android.content.Context;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat;
-import android.util.AttributeSet;
-
-/**
- * Used to group {@link Preference} objects and provide a disabled title above
- * the group.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about building a settings UI with Preferences,
- * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
- * guide.</p>
- * </div>
- */
-public class PreferenceCategory extends PreferenceGroup {
-    private static final String TAG = "PreferenceCategory";
-
-    public PreferenceCategory(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public PreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public PreferenceCategory(Context context, AttributeSet attrs) {
-        this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceCategoryStyle,
-                android.R.attr.preferenceCategoryStyle));
-    }
-
-    public PreferenceCategory(Context context) {
-        this(context, null);
-    }
-
-    @Override
-    protected boolean onPrepareAddPreference(Preference preference) {
-        if (preference instanceof PreferenceCategory) {
-            throw new IllegalArgumentException(
-                    "Cannot add a " + TAG + " directly to a " + TAG);
-        }
-
-        return super.onPrepareAddPreference(preference);
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return false;
-    }
-
-    @Override
-    public boolean shouldDisableDependents() {
-        return !super.isEnabled();
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-
-        CollectionItemInfoCompat existingItemInfo = info.getCollectionItemInfo();
-        if (existingItemInfo == null) {
-            return;
-        }
-
-        final CollectionItemInfoCompat newItemInfo = CollectionItemInfoCompat.obtain(
-                existingItemInfo.getRowIndex(),
-                existingItemInfo.getRowSpan(),
-                existingItemInfo.getColumnIndex(),
-                existingItemInfo.getColumnSpan(),
-                true /* heading */,
-                existingItemInfo.isSelected());
-        info.setCollectionItemInfo(newItemInfo);
-    }
-}
diff --git a/android/support/v7/preference/PreferenceDataStore.java b/android/support/v7/preference/PreferenceDataStore.java
deleted file mode 100644
index fa5de18..0000000
--- a/android/support/v7/preference/PreferenceDataStore.java
+++ /dev/null
@@ -1,197 +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.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/android/support/v7/preference/PreferenceDialogFragmentCompat.java b/android/support/v7/preference/PreferenceDialogFragmentCompat.java
deleted file mode 100644
index b61bae5..0000000
--- a/android/support/v7/preference/PreferenceDialogFragmentCompat.java
+++ /dev/null
@@ -1,268 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.app.DialogFragment;
-import android.support.v4.app.Fragment;
-import android.support.v7.app.AlertDialog;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-/**
- * Abstract base class which presents a dialog associated with a
- * {@link android.support.v7.preference.DialogPreference}. Since the preference object may
- * not be available during fragment re-creation, the necessary information for displaying the dialog
- * is read once during the initial call to {@link #onCreate(Bundle)} and saved/restored in the saved
- * instance state. Custom subclasses should also follow this pattern.
- */
-public abstract class PreferenceDialogFragmentCompat extends DialogFragment implements
-        DialogInterface.OnClickListener {
-
-    protected static final String ARG_KEY = "key";
-
-    private static final String SAVE_STATE_TITLE = "PreferenceDialogFragment.title";
-    private static final String SAVE_STATE_POSITIVE_TEXT = "PreferenceDialogFragment.positiveText";
-    private static final String SAVE_STATE_NEGATIVE_TEXT = "PreferenceDialogFragment.negativeText";
-    private static final String SAVE_STATE_MESSAGE = "PreferenceDialogFragment.message";
-    private static final String SAVE_STATE_LAYOUT = "PreferenceDialogFragment.layout";
-    private static final String SAVE_STATE_ICON = "PreferenceDialogFragment.icon";
-
-    private DialogPreference mPreference;
-
-    private CharSequence mDialogTitle;
-    private CharSequence mPositiveButtonText;
-    private CharSequence mNegativeButtonText;
-    private CharSequence mDialogMessage;
-    private @LayoutRes int mDialogLayoutRes;
-
-    private BitmapDrawable mDialogIcon;
-
-    /** Which button was clicked. */
-    private int mWhichButtonClicked;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final Fragment rawFragment = getTargetFragment();
-        if (!(rawFragment instanceof DialogPreference.TargetFragment)) {
-            throw new IllegalStateException("Target fragment must implement TargetFragment" +
-                    " interface");
-        }
-
-        final DialogPreference.TargetFragment fragment =
-                (DialogPreference.TargetFragment) rawFragment;
-
-        final String key = getArguments().getString(ARG_KEY);
-        if (savedInstanceState == null) {
-            mPreference = (DialogPreference) fragment.findPreference(key);
-            mDialogTitle = mPreference.getDialogTitle();
-            mPositiveButtonText = mPreference.getPositiveButtonText();
-            mNegativeButtonText = mPreference.getNegativeButtonText();
-            mDialogMessage = mPreference.getDialogMessage();
-            mDialogLayoutRes = mPreference.getDialogLayoutResource();
-
-            final Drawable icon = mPreference.getDialogIcon();
-            if (icon == null || icon instanceof BitmapDrawable) {
-                mDialogIcon = (BitmapDrawable) icon;
-            } else {
-                final Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(),
-                        icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-                final Canvas canvas = new Canvas(bitmap);
-                icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-                icon.draw(canvas);
-                mDialogIcon = new BitmapDrawable(getResources(), bitmap);
-            }
-        } else {
-            mDialogTitle = savedInstanceState.getCharSequence(SAVE_STATE_TITLE);
-            mPositiveButtonText = savedInstanceState.getCharSequence(SAVE_STATE_POSITIVE_TEXT);
-            mNegativeButtonText = savedInstanceState.getCharSequence(SAVE_STATE_NEGATIVE_TEXT);
-            mDialogMessage = savedInstanceState.getCharSequence(SAVE_STATE_MESSAGE);
-            mDialogLayoutRes = savedInstanceState.getInt(SAVE_STATE_LAYOUT, 0);
-            final Bitmap bitmap = savedInstanceState.getParcelable(SAVE_STATE_ICON);
-            if (bitmap != null) {
-                mDialogIcon = new BitmapDrawable(getResources(), bitmap);
-            }
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putCharSequence(SAVE_STATE_TITLE, mDialogTitle);
-        outState.putCharSequence(SAVE_STATE_POSITIVE_TEXT, mPositiveButtonText);
-        outState.putCharSequence(SAVE_STATE_NEGATIVE_TEXT, mNegativeButtonText);
-        outState.putCharSequence(SAVE_STATE_MESSAGE, mDialogMessage);
-        outState.putInt(SAVE_STATE_LAYOUT, mDialogLayoutRes);
-        if (mDialogIcon != null) {
-            outState.putParcelable(SAVE_STATE_ICON, mDialogIcon.getBitmap());
-        }
-    }
-
-    @Override
-    public @NonNull
-    Dialog onCreateDialog(Bundle savedInstanceState) {
-        final Context context = getActivity();
-        mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
-                .setTitle(mDialogTitle)
-                .setIcon(mDialogIcon)
-                .setPositiveButton(mPositiveButtonText, this)
-                .setNegativeButton(mNegativeButtonText, this);
-
-        View contentView = onCreateDialogView(context);
-        if (contentView != null) {
-            onBindDialogView(contentView);
-            builder.setView(contentView);
-        } else {
-            builder.setMessage(mDialogMessage);
-        }
-
-        onPrepareDialogBuilder(builder);
-
-        // Create the dialog
-        final Dialog dialog = builder.create();
-        if (needInputMethod()) {
-            requestInputMethod(dialog);
-        }
-
-        return dialog;
-    }
-
-    /**
-     * Get the preference that requested this dialog. Available after {@link #onCreate(Bundle)} has
-     * been called on the {@link PreferenceFragmentCompat} which launched this dialog.
-     *
-     * @return The {@link DialogPreference} associated with this
-     * dialog.
-     */
-    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;
-    }
-
-    /**
-     * Prepares the dialog builder to be shown when the preference is clicked.
-     * Use this to set custom properties on the dialog.
-     * <p>
-     * Do not {@link AlertDialog.Builder#create()} or
-     * {@link AlertDialog.Builder#show()}.
-     */
-    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {}
-
-    /**
-     * Returns whether the preference needs to display a soft input method when the dialog
-     * is displayed. Default is false. Subclasses should override this method if they need
-     * the soft input method brought up automatically.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean needInputMethod() {
-        return false;
-    }
-
-    /**
-     * Sets the required flags on the dialog window to enable input method window to show up.
-     */
-    private void requestInputMethod(Dialog dialog) {
-        Window window = dialog.getWindow();
-        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
-    }
-
-    /**
-     * Creates the content view for the dialog (if a custom content view is
-     * required). By default, it inflates the dialog layout resource if it is
-     * set.
-     *
-     * @return The content View for the dialog.
-     * @see DialogPreference#setLayoutResource(int)
-     */
-    protected View onCreateDialogView(Context context) {
-        final int resId = mDialogLayoutRes;
-        if (resId == 0) {
-            return null;
-        }
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        return inflater.inflate(resId, null);
-    }
-
-    /**
-     * Binds views in the content View of the dialog to data.
-     * <p>
-     * Make sure to call through to the superclass implementation.
-     *
-     * @param view The content View of the dialog, if it is custom.
-     */
-    protected void onBindDialogView(View view) {
-        View dialogMessageView = view.findViewById(android.R.id.message);
-
-        if (dialogMessageView != null) {
-            final CharSequence message = mDialogMessage;
-            int newVisibility = View.GONE;
-
-            if (!TextUtils.isEmpty(message)) {
-                if (dialogMessageView instanceof TextView) {
-                    ((TextView) dialogMessageView).setText(message);
-                }
-
-                newVisibility = View.VISIBLE;
-            }
-
-            if (dialogMessageView.getVisibility() != newVisibility) {
-                dialogMessageView.setVisibility(newVisibility);
-            }
-        }
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        mWhichButtonClicked = which;
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        super.onDismiss(dialog);
-        onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
-    }
-
-    public abstract void onDialogClosed(boolean positiveResult);
-}
diff --git a/android/support/v7/preference/PreferenceFragmentCompat.java b/android/support/v7/preference/PreferenceFragmentCompat.java
deleted file mode 100644
index 6094217..0000000
--- a/android/support/v7/preference/PreferenceFragmentCompat.java
+++ /dev/null
@@ -1,839 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-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.v7.preference.internal.AbstractMultiSelectListPreference;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Shows a hierarchy of {@link Preference} objects as
- * lists. These preferences will
- * automatically save to {@link android.content.SharedPreferences} as the user interacts with
- * them. To retrieve an instance of {@link android.content.SharedPreferences} that the
- * preference hierarchy in this fragment will use, call
- * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
- * with a context in the same package as this fragment.
- * <p>
- * Furthermore, the preferences shown will follow the visual style of system
- * preferences. It is easy to create a hierarchy of preferences (that can be
- * shown on multiple screens) via XML. For these reasons, it is recommended to
- * use this fragment (as a superclass) to deal with preferences in applications.
- * <p>
- * A {@link PreferenceScreen} object should be at the top of the preference
- * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
- * denote a screen break--that is the preferences contained within subsequent
- * {@link PreferenceScreen} should be shown on another screen. The preference
- * framework handles this by calling {@link #onNavigateToScreen(PreferenceScreen)}.
- * <p>
- * The preference hierarchy can be formed in multiple ways:
- * <li> From an XML file specifying the hierarchy
- * <li> From different {@link android.app.Activity Activities} that each specify its own
- * preferences in an XML file via {@link android.app.Activity} meta-data
- * <li> From an object hierarchy rooted with {@link PreferenceScreen}
- * <p>
- * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
- * root element should be a {@link PreferenceScreen}. Subsequent elements can point
- * to actual {@link Preference} subclasses. As mentioned above, subsequent
- * {@link PreferenceScreen} in the hierarchy will result in the screen break.
- * <p>
- * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
- * {@link #setPreferenceScreen(PreferenceScreen)}.
- * <p>
- * As a convenience, this fragment implements a click listener for any
- * preference in the current hierarchy, see
- * {@link #onPreferenceTreeClick(Preference)}.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about using {@code PreferenceFragment},
- * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
- * guide.</p>
- * </div>
- *
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference fragment that is
- * populated from a resource.  The resource it loads is:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences}
- *
- * <p>The fragment implementation itself simply populates the preferences
- * when created.  Note that the preferences framework takes care of loading
- * the current values out of the app preferences and writing them when changed:</p>
- *
- * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/android/supportpreference/FragmentSupportPreferencesCompat.java
- *      support_fragment_compat}
- *
- * @see Preference
- * @see PreferenceScreen
- */
-public abstract class PreferenceFragmentCompat extends Fragment implements
-        PreferenceManager.OnPreferenceTreeClickListener,
-        PreferenceManager.OnDisplayPreferenceDialogListener,
-        PreferenceManager.OnNavigateToScreenListener,
-        DialogPreference.TargetFragment {
-
-    /**
-     * Fragment argument used to specify the tag of the desired root
-     * {@link android.support.v7.preference.PreferenceScreen} object.
-     */
-    public static final String ARG_PREFERENCE_ROOT =
-            "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
-
-    private static final String PREFERENCES_TAG = "android:preferences";
-
-    private static final String DIALOG_FRAGMENT_TAG =
-            "android.support.v7.preference.PreferenceFragment.DIALOG";
-
-    private PreferenceManager mPreferenceManager;
-    private RecyclerView mList;
-    private boolean mHavePrefs;
-    private boolean mInitDone;
-
-    private Context mStyledContext;
-
-    private int mLayoutResId = R.layout.preference_list_fragment;
-
-    private final DividerDecoration mDividerDecoration = new DividerDecoration();
-
-    private static final int MSG_BIND_PREFERENCES = 1;
-    private Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-
-                case MSG_BIND_PREFERENCES:
-                    bindPreferences();
-                    break;
-            }
-        }
-    };
-
-    final private Runnable mRequestFocus = new Runnable() {
-        @Override
-        public void run() {
-            mList.focusableViewAvailable(mList);
-        }
-    };
-
-    private Runnable mSelectPreferenceRunnable;
-
-    /**
-     * Interface that PreferenceFragment's containing activity should
-     * implement to be able to process preference items that wish to
-     * switch to a specified fragment.
-     */
-    public interface OnPreferenceStartFragmentCallback {
-        /**
-         * Called when the user has clicked on a Preference that has
-         * a fragment class name associated with it.  The implementation
-         * should instantiate and switch to an instance of the given
-         * fragment.
-         * @param caller The fragment requesting navigation.
-         * @param pref The preference requesting the fragment.
-         * @return true if the fragment creation has been handled
-         */
-        boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref);
-    }
-
-    /**
-     * Interface that PreferenceFragment's containing activity should
-     * implement to be able to process preference items that wish to
-     * switch to a new screen of preferences.
-     */
-    public interface OnPreferenceStartScreenCallback {
-        /**
-         * Called when the user has clicked on a PreferenceScreen item in order to navigate to a new
-         * screen of preferences.
-         * @param caller The fragment requesting navigation.
-         * @param pref The preference screen to navigate to.
-         * @return true if the screen navigation has been handled
-         */
-        boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref);
-    }
-
-    public interface OnPreferenceDisplayDialogCallback {
-
-        /**
-         *
-         * @param caller The fragment containing the preference requesting the dialog.
-         * @param pref The preference requesting the dialog.
-         * @return true if the dialog creation has been handled.
-         */
-        boolean onPreferenceDisplayDialog(@NonNull PreferenceFragmentCompat caller,
-                Preference pref);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        final TypedValue tv = new TypedValue();
-        getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true);
-        final int theme = tv.resourceId;
-        if (theme == 0) {
-            throw new IllegalStateException("Must specify preferenceTheme in theme");
-        }
-        mStyledContext = new ContextThemeWrapper(getActivity(), theme);
-
-        mPreferenceManager = new PreferenceManager(mStyledContext);
-        mPreferenceManager.setOnNavigateToScreenListener(this);
-        final Bundle args = getArguments();
-        final String rootKey;
-        if (args != null) {
-            rootKey = getArguments().getString(ARG_PREFERENCE_ROOT);
-        } else {
-            rootKey = null;
-        }
-        onCreatePreferences(savedInstanceState, rootKey);
-    }
-
-    /**
-     * Called during {@link #onCreate(Bundle)} to supply the preferences for this fragment.
-     * Subclasses are expected to call {@link #setPreferenceScreen(PreferenceScreen)} either
-     * directly or via helper methods such as {@link #addPreferencesFromResource(int)}.
-     *
-     * @param savedInstanceState If the fragment is being re-created from
-     *                           a previous saved state, this is the state.
-     * @param rootKey If non-null, this preference fragment should be rooted at the
-     *                {@link android.support.v7.preference.PreferenceScreen} with this key.
-     */
-    public abstract void onCreatePreferences(Bundle savedInstanceState, String rootKey);
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-
-        TypedArray a = mStyledContext.obtainStyledAttributes(null,
-                R.styleable.PreferenceFragmentCompat,
-                R.attr.preferenceFragmentCompatStyle,
-                0);
-
-        mLayoutResId = a.getResourceId(R.styleable.PreferenceFragmentCompat_android_layout,
-                mLayoutResId);
-
-        final Drawable divider = a.getDrawable(
-                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();
-
-        // Need to theme the inflater to pick up the preferenceFragmentListStyle
-        final TypedValue tv = new TypedValue();
-        getActivity().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true);
-        final int theme = tv.resourceId;
-
-        final Context themedContext = new ContextThemeWrapper(inflater.getContext(), theme);
-        final LayoutInflater themedInflater = inflater.cloneInContext(themedContext);
-
-        final View view = themedInflater.inflate(mLayoutResId, container, false);
-
-        final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
-        if (!(rawListContainer instanceof ViewGroup)) {
-            throw new RuntimeException("Content has view with id attribute "
-                    + "'android.R.id.list_container' that is not a ViewGroup class");
-        }
-
-        final ViewGroup listContainer = (ViewGroup) rawListContainer;
-
-        final RecyclerView listView = onCreateRecyclerView(themedInflater, listContainer,
-                savedInstanceState);
-        if (listView == null) {
-            throw new RuntimeException("Could not create RecyclerView");
-        }
-
-        mList = listView;
-
-        listView.addItemDecoration(mDividerDecoration);
-        setDivider(divider);
-        if (dividerHeight != -1) {
-            setDividerHeight(dividerHeight);
-        }
-        mDividerDecoration.setAllowDividerAfterLastItem(allowDividerAfterLastItem);
-
-        listContainer.addView(mList);
-        mHandler.post(mRequestFocus);
-
-        return view;
-    }
-
-    /**
-     * Sets the drawable that will be drawn between each item in the list.
-     * <p>
-     * <strong>Note:</strong> If the drawable does not have an intrinsic
-     * height, you should also call {@link #setDividerHeight(int)}.
-     *
-     * @param divider the drawable to use
-     * @attr ref R.styleable#PreferenceFragmentCompat_android_divider
-     */
-    public void setDivider(Drawable divider) {
-        mDividerDecoration.setDivider(divider);
-    }
-
-    /**
-     * Sets the height of the divider that will be drawn between each item in the list. Calling
-     * this will override the intrinsic height as set by {@link #setDivider(Drawable)}
-     *
-     * @param height The new height of the divider in pixels.
-     * @attr ref R.styleable#PreferenceFragmentCompat_android_dividerHeight
-     */
-    public void setDividerHeight(int height) {
-        mDividerDecoration.setDividerHeight(height);
-    }
-
-    @Override
-    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-
-        if (mHavePrefs) {
-            bindPreferences();
-            if (mSelectPreferenceRunnable != null) {
-                mSelectPreferenceRunnable.run();
-                mSelectPreferenceRunnable = null;
-            }
-        }
-
-        mInitDone = true;
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        if (savedInstanceState != null) {
-            Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
-            if (container != null) {
-                final PreferenceScreen preferenceScreen = getPreferenceScreen();
-                if (preferenceScreen != null) {
-                    preferenceScreen.restoreHierarchyState(container);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mPreferenceManager.setOnPreferenceTreeClickListener(this);
-        mPreferenceManager.setOnDisplayPreferenceDialogListener(this);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        mPreferenceManager.setOnPreferenceTreeClickListener(null);
-        mPreferenceManager.setOnDisplayPreferenceDialogListener(null);
-    }
-
-    @Override
-    public void onDestroyView() {
-        mHandler.removeCallbacks(mRequestFocus);
-        mHandler.removeMessages(MSG_BIND_PREFERENCES);
-        if (mHavePrefs) {
-            unbindPreferences();
-        }
-        mList = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            Bundle container = new Bundle();
-            preferenceScreen.saveHierarchyState(container);
-            outState.putBundle(PREFERENCES_TAG, container);
-        }
-    }
-
-    /**
-     * Returns the {@link PreferenceManager} used by this fragment.
-     * @return The {@link PreferenceManager}.
-     */
-    public PreferenceManager getPreferenceManager() {
-        return mPreferenceManager;
-    }
-
-    /**
-     * Sets the root of the preference hierarchy that this fragment is showing.
-     *
-     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
-     */
-    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
-        if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
-            onUnbindPreferences();
-            mHavePrefs = true;
-            if (mInitDone) {
-                postBindPreferences();
-            }
-        }
-    }
-
-    /**
-     * Gets the root of the preference hierarchy that this fragment is showing.
-     *
-     * @return The {@link PreferenceScreen} that is the root of the preference
-     *         hierarchy.
-     */
-    public PreferenceScreen getPreferenceScreen() {
-        return mPreferenceManager.getPreferenceScreen();
-    }
-
-    /**
-     * Inflates the given XML resource and adds the preference hierarchy to the current
-     * preference hierarchy.
-     *
-     * @param preferencesResId The XML resource ID to inflate.
-     */
-    public void addPreferencesFromResource(@XmlRes int preferencesResId) {
-        requirePreferenceManager();
-
-        setPreferenceScreen(mPreferenceManager.inflateFromResource(mStyledContext,
-                preferencesResId, getPreferenceScreen()));
-    }
-
-    /**
-     * Inflates the given XML resource and replaces the current preference hierarchy (if any) with
-     * the preference hierarchy rooted at {@code key}.
-     *
-     * @param preferencesResId The XML resource ID to inflate.
-     * @param key The preference key of the {@link android.support.v7.preference.PreferenceScreen}
-     *            to use as the root of the preference hierarchy, or null to use the root
-     *            {@link android.support.v7.preference.PreferenceScreen}.
-     */
-    public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key) {
-        requirePreferenceManager();
-
-        final PreferenceScreen xmlRoot = mPreferenceManager.inflateFromResource(mStyledContext,
-                preferencesResId, null);
-
-        final Preference root;
-        if (key != null) {
-            root = xmlRoot.findPreference(key);
-            if (!(root instanceof PreferenceScreen)) {
-                throw new IllegalArgumentException("Preference object with key " + key
-                        + " is not a PreferenceScreen");
-            }
-        } else {
-            root = xmlRoot;
-        }
-
-        setPreferenceScreen((PreferenceScreen) root);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onPreferenceTreeClick(Preference preference) {
-        if (preference.getFragment() != null) {
-            boolean handled = false;
-            if (getCallbackFragment() instanceof OnPreferenceStartFragmentCallback) {
-                handled = ((OnPreferenceStartFragmentCallback) getCallbackFragment())
-                        .onPreferenceStartFragment(this, preference);
-            }
-            if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback){
-                handled = ((OnPreferenceStartFragmentCallback) getActivity())
-                        .onPreferenceStartFragment(this, preference);
-            }
-            return handled;
-        }
-        return false;
-    }
-
-    /**
-     * Called by
-     * {@link android.support.v7.preference.PreferenceScreen#onClick()} in order to navigate to a
-     * new screen of preferences. Calls
-     * {@link PreferenceFragmentCompat.OnPreferenceStartScreenCallback#onPreferenceStartScreen}
-     * if the target fragment or containing activity implements
-     * {@link PreferenceFragmentCompat.OnPreferenceStartScreenCallback}.
-     * @param preferenceScreen The {@link android.support.v7.preference.PreferenceScreen} to
-     *                         navigate to.
-     */
-    @Override
-    public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
-        boolean handled = false;
-        if (getCallbackFragment() instanceof OnPreferenceStartScreenCallback) {
-            handled = ((OnPreferenceStartScreenCallback) getCallbackFragment())
-                    .onPreferenceStartScreen(this, preferenceScreen);
-        }
-        if (!handled && getActivity() instanceof OnPreferenceStartScreenCallback) {
-            ((OnPreferenceStartScreenCallback) getActivity())
-                    .onPreferenceStartScreen(this, preferenceScreen);
-        }
-    }
-
-    /**
-     * Finds a {@link Preference} based on its key.
-     *
-     * @param key The key of the preference to retrieve.
-     * @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;
-        }
-        return mPreferenceManager.findPreference(key);
-    }
-
-    private void requirePreferenceManager() {
-        if (mPreferenceManager == null) {
-            throw new RuntimeException("This should be called after super.onCreate.");
-        }
-    }
-
-    private void postBindPreferences() {
-        if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
-        mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
-    }
-
-    private void bindPreferences() {
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            getListView().setAdapter(onCreateAdapter(preferenceScreen));
-            preferenceScreen.onAttached();
-        }
-        onBindPreferences();
-    }
-
-    private void unbindPreferences() {
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            preferenceScreen.onDetached();
-        }
-        onUnbindPreferences();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void onBindPreferences() {
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void onUnbindPreferences() {
-    }
-
-    public final RecyclerView getListView() {
-        return mList;
-    }
-
-    /**
-     * Creates the {@link android.support.v7.widget.RecyclerView} used to display the preferences.
-     * Subclasses may override this to return a customized
-     * {@link android.support.v7.widget.RecyclerView}.
-     * @param inflater The LayoutInflater object that can be used to inflate the
-     *                 {@link android.support.v7.widget.RecyclerView}.
-     * @param parent The parent {@link android.view.View} that the RecyclerView will be attached to.
-     *               This method should not add the view itself, but this can be used to generate
-     *               the LayoutParams of the view.
-     * @param savedInstanceState If non-null, this view is being re-constructed from a previous
-     *                           saved state as given here
-     * @return A new RecyclerView object to be placed into the view hierarchy
-     */
-    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        RecyclerView recyclerView = (RecyclerView) inflater
-                .inflate(R.layout.preference_recyclerview, parent, false);
-
-        recyclerView.setLayoutManager(onCreateLayoutManager());
-        recyclerView.setAccessibilityDelegateCompat(
-                new PreferenceRecyclerViewAccessibilityDelegate(recyclerView));
-
-        return recyclerView;
-    }
-
-    /**
-     * Called from {@link #onCreateRecyclerView} to create the
-     * {@link android.support.v7.widget.RecyclerView.LayoutManager} for the created
-     * {@link android.support.v7.widget.RecyclerView}.
-     * @return A new {@link android.support.v7.widget.RecyclerView.LayoutManager} instance.
-     */
-    public RecyclerView.LayoutManager onCreateLayoutManager() {
-        return new LinearLayoutManager(getActivity());
-    }
-
-    /**
-     * Creates the root adapter.
-     *
-     * @param preferenceScreen Preference screen object to create the adapter for.
-     * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}.
-     */
-    protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
-        return new PreferenceGroupAdapter(preferenceScreen);
-    }
-
-    /**
-     * Called when a preference in the tree requests to display a dialog. Subclasses should
-     * override this method to display custom dialogs or to handle dialogs for custom preference
-     * classes.
-     *
-     * @param preference The Preference object requesting the dialog.
-     */
-    @Override
-    public void onDisplayPreferenceDialog(Preference preference) {
-
-        boolean handled = false;
-        if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) {
-            handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment())
-                    .onPreferenceDisplayDialog(this, preference);
-        }
-        if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) {
-            handled = ((OnPreferenceDisplayDialogCallback) getActivity())
-                    .onPreferenceDisplayDialog(this, preference);
-        }
-
-        if (handled) {
-            return;
-        }
-
-        // check if dialog is already showing
-        if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
-            return;
-        }
-
-        final DialogFragment f;
-        if (preference instanceof EditTextPreference) {
-            f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey());
-        } else if (preference instanceof ListPreference) {
-            f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
-        } else if (preference instanceof AbstractMultiSelectListPreference) {
-            f = MultiSelectListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
-        } else {
-            throw new IllegalArgumentException("Tried to display dialog for unknown " +
-                    "preference type. Did you forget to override onDisplayPreferenceDialog()?");
-        }
-        f.setTargetFragment(this, 0);
-        f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
-    }
-
-    /**
-     * Basically a wrapper for getParentFragment which is v17+. Used by the leanback preference lib.
-     * @return Fragment to possibly use as a callback
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public Fragment getCallbackFragment() {
-        return null;
-    }
-
-    public void scrollToPreference(final String key) {
-        scrollToPreferenceInternal(null, key);
-    }
-
-    public void scrollToPreference(final Preference preference) {
-        scrollToPreferenceInternal(preference, null);
-    }
-
-    private void scrollToPreferenceInternal(final Preference preference, final String key) {
-        final Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                final RecyclerView.Adapter adapter = mList.getAdapter();
-                if (!(adapter instanceof
-                        PreferenceGroup.PreferencePositionCallback)) {
-                    if (adapter != null) {
-                        throw new IllegalStateException("Adapter must implement "
-                                + "PreferencePositionCallback");
-                    } else {
-                        // Adapter was set to null, so don't scroll I guess?
-                        return;
-                    }
-                }
-                final int position;
-                if (preference != null) {
-                    position = ((PreferenceGroup.PreferencePositionCallback) adapter)
-                            .getPreferenceAdapterPosition(preference);
-                } else {
-                    position = ((PreferenceGroup.PreferencePositionCallback) adapter)
-                            .getPreferenceAdapterPosition(key);
-                }
-                if (position != RecyclerView.NO_POSITION) {
-                    mList.scrollToPosition(position);
-                } else {
-                    // Item not found, wait for an update and try again
-                    adapter.registerAdapterDataObserver(
-                            new ScrollToPreferenceObserver(adapter, mList, preference, key));
-                }
-            }
-        };
-        if (mList == null) {
-            mSelectPreferenceRunnable = r;
-        } else {
-            r.run();
-        }
-    }
-
-    private static class ScrollToPreferenceObserver extends RecyclerView.AdapterDataObserver {
-        private final RecyclerView.Adapter mAdapter;
-        private final RecyclerView mList;
-        private final Preference mPreference;
-        private final String mKey;
-
-        public ScrollToPreferenceObserver(RecyclerView.Adapter adapter, RecyclerView list,
-                Preference preference, String key) {
-            mAdapter = adapter;
-            mList = list;
-            mPreference = preference;
-            mKey = key;
-        }
-
-        private void scrollToPreference() {
-            mAdapter.unregisterAdapterDataObserver(this);
-            final int position;
-            if (mPreference != null) {
-                position = ((PreferenceGroup.PreferencePositionCallback) mAdapter)
-                        .getPreferenceAdapterPosition(mPreference);
-            } else {
-                position = ((PreferenceGroup.PreferencePositionCallback) mAdapter)
-                        .getPreferenceAdapterPosition(mKey);
-            }
-            if (position != RecyclerView.NO_POSITION) {
-                mList.scrollToPosition(position);
-            }
-        }
-
-        @Override
-        public void onChanged() {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            scrollToPreference();
-        }
-
-        @Override
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            scrollToPreference();
-        }
-    }
-
-    private class DividerDecoration extends RecyclerView.ItemDecoration {
-
-        private Drawable mDivider;
-        private int mDividerHeight;
-        private boolean mAllowDividerAfterLastItem = true;
-
-        @Override
-        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
-            if (mDivider == null) {
-                return;
-            }
-            final int childCount = parent.getChildCount();
-            final int width = parent.getWidth();
-            for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
-                final View view = parent.getChildAt(childViewIndex);
-                if (shouldDrawDividerBelow(view, parent)) {
-                    int top = (int) view.getY() + view.getHeight();
-                    mDivider.setBounds(0, top, width, top + mDividerHeight);
-                    mDivider.draw(c);
-                }
-            }
-        }
-
-        @Override
-        public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
-                RecyclerView.State state) {
-            if (shouldDrawDividerBelow(view, parent)) {
-                outRect.bottom = mDividerHeight;
-            }
-        }
-
-        private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
-            final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
-            final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder
-                    && ((PreferenceViewHolder) holder).isDividerAllowedBelow();
-            if (!dividerAllowedBelow) {
-                return false;
-            }
-            boolean nextAllowed = mAllowDividerAfterLastItem;
-            int index = parent.indexOfChild(view);
-            if (index < parent.getChildCount() - 1) {
-                final View nextView = parent.getChildAt(index + 1);
-                final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView);
-                nextAllowed = nextHolder instanceof PreferenceViewHolder
-                        && ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove();
-            }
-            return nextAllowed;
-        }
-
-        public void setDivider(Drawable divider) {
-            if (divider != null) {
-                mDividerHeight = divider.getIntrinsicHeight();
-            } else {
-                mDividerHeight = 0;
-            }
-            mDivider = divider;
-            mList.invalidateItemDecorations();
-        }
-
-        public void setDividerHeight(int dividerHeight) {
-            mDividerHeight = dividerHeight;
-            mList.invalidateItemDecorations();
-        }
-
-        public void setAllowDividerAfterLastItem(boolean allowDividerAfterLastItem) {
-            mAllowDividerAfterLastItem = allowDividerAfterLastItem;
-        }
-    }
-}
diff --git a/android/support/v7/preference/PreferenceGroup.java b/android/support/v7/preference/PreferenceGroup.java
deleted file mode 100644
index a951e70..0000000
--- a/android/support/v7/preference/PreferenceGroup.java
+++ /dev/null
@@ -1,532 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v4.util.SimpleArrayMap;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A container for multiple
- * {@link Preference} objects. It is a base class for  Preference objects that are
- * parents, such as {@link PreferenceCategory} and {@link PreferenceScreen}.
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about building a settings UI with Preferences,
- * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
- * guide.</p>
- * </div>
- *
- * @attr name android:orderingFromXml
- * @attr name initialExpandedChildrenCount
- */
-public abstract class PreferenceGroup extends Preference {
-    /**
-     * The container for child {@link Preference}s. This is sorted based on the
-     * ordering, please use {@link #addPreference(Preference)} instead of adding
-     * to this directly.
-     */
-    private List<Preference> mPreferenceList;
-
-    private boolean mOrderingAsAdded = true;
-
-    private int mCurrentPreferenceOrder = 0;
-
-    private boolean mAttachedToHierarchy = false;
-
-    private int mInitialExpandedChildrenCount = Integer.MAX_VALUE;
-    private PreferenceInstanceStateCallback mPreferenceInstanceStateCallback;
-
-    private final SimpleArrayMap<String, Long> mIdRecycleCache = new SimpleArrayMap<>();
-    private final Handler mHandler = new Handler();
-    private final Runnable mClearRecycleCacheRunnable = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (this) {
-                mIdRecycleCache.clear();
-            }
-        }
-    };
-
-    public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        mPreferenceList = new ArrayList<>();
-
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.PreferenceGroup, defStyleAttr, defStyleRes);
-
-        mOrderingAsAdded =
-                TypedArrayUtils.getBoolean(a, R.styleable.PreferenceGroup_orderingFromXml,
-                        R.styleable.PreferenceGroup_orderingFromXml, true);
-
-        if (a.hasValue(R.styleable.PreferenceGroup_initialExpandedChildrenCount)) {
-            mInitialExpandedChildrenCount = TypedArrayUtils.getInt(
-                    a, R.styleable.PreferenceGroup_initialExpandedChildrenCount,
-                            R.styleable.PreferenceGroup_initialExpandedChildrenCount, -1);
-        }
-        a.recycle();
-    }
-
-    public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public PreferenceGroup(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * Whether to order the {@link Preference} children of this group as they
-     * are added. If this is false, the ordering will follow each Preference
-     * order and default to alphabetic for those without an order.
-     * <p>
-     * If this is called after preferences are added, they will not be
-     * re-ordered in the order they were added, hence call this method early on.
-     *
-     * @param orderingAsAdded Whether to order according to the order added.
-     * @see Preference#setOrder(int)
-     */
-    public void setOrderingAsAdded(boolean orderingAsAdded) {
-        mOrderingAsAdded = orderingAsAdded;
-    }
-
-    /**
-     * Whether this group is ordering preferences in the order they are added.
-     *
-     * @return Whether this group orders based on the order the children are added.
-     * @see #setOrderingAsAdded(boolean)
-     */
-    public boolean isOrderingAsAdded() {
-        return mOrderingAsAdded;
-    }
-
-    /**
-     * Sets the maximal number of children that are shown when the preference group is launched
-     * where the rest of the children will be hidden.
-     * If some children are hidden an expand button will be provided to show all the hidden
-     * children. Any child in any level of the hierarchy that is also a preference group (e.g.
-     * preference category) will not be counted towards the limit. But instead the children of such
-     * group will be counted.
-     * By default, all children will be shown, so the default value of this attribute is equal to
-     * Integer.MAX_VALUE.
-     *
-     * @param expandedCount the number of children that is initially shown.
-     *
-     * @attr ref R.styleable#PreferenceGroup_initialExpandedChildrenCount
-     */
-    public void setInitialExpandedChildrenCount(int expandedCount) {
-        mInitialExpandedChildrenCount = expandedCount;
-    }
-
-    /**
-     * Gets the maximal number of children that is initially shown.
-     *
-     * @return the maximal number of children that is initially shown.
-     *
-     * @attr ref R.styleable#PreferenceGroup_initialExpandedChildrenCount
-     */
-    public int getInitialExpandedChildrenCount() {
-        return mInitialExpandedChildrenCount;
-    }
-
-    /**
-     * Called by the inflater to add an item to this group.
-     */
-    public void addItemFromInflater(Preference preference) {
-        addPreference(preference);
-    }
-
-    /**
-     * Returns the number of children {@link Preference}s.
-     * @return The number of preference children in this group.
-     */
-    public int getPreferenceCount() {
-        return mPreferenceList.size();
-    }
-
-    /**
-     * Returns the {@link Preference} at a particular index.
-     *
-     * @param index The index of the {@link Preference} to retrieve.
-     * @return The {@link Preference}.
-     */
-    public Preference getPreference(int index) {
-        return mPreferenceList.get(index);
-    }
-
-    /**
-     * Adds a {@link Preference} at the correct position based on the
-     * preference's order.
-     *
-     * @param preference The preference to add.
-     * @return Whether the preference is now in this group.
-     */
-    public boolean addPreference(Preference preference) {
-        if (mPreferenceList.contains(preference)) {
-            // Exists
-            return true;
-        }
-
-        if (preference.getOrder() == DEFAULT_ORDER) {
-            if (mOrderingAsAdded) {
-                preference.setOrder(mCurrentPreferenceOrder++);
-            }
-
-            if (preference instanceof PreferenceGroup) {
-                // TODO: fix (method is called tail recursively when inflating,
-                // so we won't end up properly passing this flag down to children
-                ((PreferenceGroup)preference).setOrderingAsAdded(mOrderingAsAdded);
-            }
-        }
-
-        int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
-        if (insertionIndex < 0) {
-            insertionIndex = insertionIndex * -1 - 1;
-        }
-
-        if (!onPrepareAddPreference(preference)) {
-            return false;
-        }
-
-        synchronized(this) {
-            mPreferenceList.add(insertionIndex, preference);
-        }
-
-        final PreferenceManager preferenceManager = getPreferenceManager();
-        final String key = preference.getKey();
-        final long id;
-        if (key != null && mIdRecycleCache.containsKey(key)) {
-            id = mIdRecycleCache.get(key);
-            mIdRecycleCache.remove(key);
-        } else {
-            id = preferenceManager.getNextId();
-        }
-        preference.onAttachedToHierarchy(preferenceManager, id);
-        preference.assignParent(this);
-
-        if (mAttachedToHierarchy) {
-            preference.onAttached();
-        }
-
-        notifyHierarchyChanged();
-
-        return true;
-    }
-
-    /**
-     * Removes a {@link Preference} from this group.
-     *
-     * @param preference The preference to remove.
-     * @return Whether the preference was found and removed.
-     */
-    public boolean removePreference(Preference preference) {
-        final boolean returnValue = removePreferenceInt(preference);
-        notifyHierarchyChanged();
-        return returnValue;
-    }
-
-    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
-                // immediately, we want it to have the same id so that it can be correctly tracked
-                // in the adapter by RecyclerView, to make it appear as if it has only been
-                // seamlessly updated. If the preference is not re-added by the time the handler
-                // runs, we take that as a signal that the preference will not be re-added soon
-                // in which case it does not need to retain the same id.
-
-                // If two (or more) preferences have the same (or null) key and both are removed
-                // and then re-added, only one id will be recycled and the second (and later)
-                // preferences will receive a newly generated id. This use pattern of the preference
-                // API is strongly discouraged.
-                final String key = preference.getKey();
-                if (key != null) {
-                    mIdRecycleCache.put(key, preference.getId());
-                    mHandler.removeCallbacks(mClearRecycleCacheRunnable);
-                    mHandler.post(mClearRecycleCacheRunnable);
-                }
-                if (mAttachedToHierarchy) {
-                    preference.onDetached();
-                }
-            }
-
-            return success;
-        }
-    }
-
-    /**
-     * Removes all {@link Preference Preferences} from this group.
-     */
-    public void removeAll() {
-        synchronized(this) {
-            List<Preference> preferenceList = mPreferenceList;
-            for (int i = preferenceList.size() - 1; i >= 0; i--) {
-                removePreferenceInt(preferenceList.get(0));
-            }
-        }
-        notifyHierarchyChanged();
-    }
-
-    /**
-     * Prepares a {@link Preference} to be added to the group.
-     *
-     * @param preference The preference to add.
-     * @return Whether to allow adding the preference (true), or not (false).
-     */
-    protected boolean onPrepareAddPreference(Preference preference) {
-        preference.onParentChanged(this, shouldDisableDependents());
-        return true;
-    }
-
-    /**
-     * Finds a {@link Preference} based on its key. If two {@link Preference}
-     * share the same key (not recommended), the first to appear will be
-     * returned (to retrieve the other preference with the same key, call this
-     * method on the first preference). If this preference has the key, it will
-     * not be returned.
-     * <p>
-     * This will recursively search for the preference into children that are
-     * also {@link PreferenceGroup PreferenceGroups}.
-     *
-     * @param key The key of the preference to retrieve.
-     * @return The {@link Preference} with the key, or null.
-     */
-    public Preference findPreference(CharSequence key) {
-        if (TextUtils.equals(getKey(), key)) {
-            return this;
-        }
-        final int preferenceCount = getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            final Preference preference = getPreference(i);
-            final String curKey = preference.getKey();
-
-            if (curKey != null && curKey.equals(key)) {
-                return preference;
-            }
-
-            if (preference instanceof PreferenceGroup) {
-                final Preference returnedPreference = ((PreferenceGroup)preference)
-                        .findPreference(key);
-                if (returnedPreference != null) {
-                    return returnedPreference;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Whether this preference group should be shown on the same screen as its
-     * contained preferences.
-     *
-     * @return True if the contained preferences should be shown on the same
-     *         screen as this preference.
-     */
-    protected boolean isOnSameScreenAsChildren() {
-        return true;
-    }
-
-    /**
-     * Returns true if we're between {@link #onAttached()} and {@link #onPrepareForRemoval()}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isAttached() {
-        return mAttachedToHierarchy;
-    }
-
-    @Override
-    public void onAttached() {
-        super.onAttached();
-
-        // Mark as attached so if a preference is later added to this group, we
-        // can tell it we are already attached
-        mAttachedToHierarchy = true;
-
-        // Dispatch to all contained preferences
-        final int preferenceCount = getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            getPreference(i).onAttached();
-        }
-    }
-
-    @Override
-    public void onDetached() {
-        super.onDetached();
-
-        // We won't be attached to the activity anymore
-        mAttachedToHierarchy = false;
-
-        // Dispatch to all contained preferences
-        final int preferenceCount = getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            getPreference(i).onDetached();
-        }
-    }
-
-    @Override
-    public void notifyDependencyChange(boolean disableDependents) {
-        super.notifyDependencyChange(disableDependents);
-
-        // Child preferences have an implicit dependency on their containing
-        // group. Dispatch dependency change to all contained preferences.
-        final int preferenceCount = getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            getPreference(i).onParentChanged(this, disableDependents);
-        }
-    }
-
-    void sortPreferences() {
-        synchronized (this) {
-            Collections.sort(mPreferenceList);
-        }
-    }
-
-    @Override
-    protected void dispatchSaveInstanceState(Bundle container) {
-        super.dispatchSaveInstanceState(container);
-
-        // Dispatch to all contained preferences
-        final int preferenceCount = getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            getPreference(i).dispatchSaveInstanceState(container);
-        }
-    }
-
-    @Override
-    protected void dispatchRestoreInstanceState(Bundle container) {
-        super.dispatchRestoreInstanceState(container);
-
-        // Dispatch to all contained preferences
-        final int preferenceCount = getPreferenceCount();
-        for (int i = 0; i < preferenceCount; i++) {
-            getPreference(i).dispatchRestoreInstanceState(container);
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (mPreferenceInstanceStateCallback != null) {
-            return mPreferenceInstanceStateCallback.saveInstanceState(superState);
-        }
-        return superState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (mPreferenceInstanceStateCallback != null) {
-            state = mPreferenceInstanceStateCallback.restoreInstanceState(state);
-        }
-        super.onRestoreInstanceState(state);
-    }
-
-    /**
-     * Sets the instance state callback.
-     *
-     * @param callback The callback.
-     * @see #onSaveInstanceState()
-     * @see #onRestoreInstanceState()
-     */
-    final void setPreferenceInstanceStateCallback(PreferenceInstanceStateCallback callback) {
-        mPreferenceInstanceStateCallback = callback;
-    }
-
-    /**
-     * Gets the instance state callback.
-     *
-     * @return the instance state callback.
-     */
-    @VisibleForTesting
-    final PreferenceInstanceStateCallback getPreferenceInstanceStateCallback() {
-        return mPreferenceInstanceStateCallback;
-    }
-
-    /**
-     * Interface for PreferenceGroup Adapters to implement so that
-     * {@link android.support.v14.preference.PreferenceFragment#scrollToPreference(String)} and
-     * {@link android.support.v14.preference.PreferenceFragment#scrollToPreference(Preference)} or
-     * {@link PreferenceFragmentCompat#scrollToPreference(String)} and
-     * {@link PreferenceFragmentCompat#scrollToPreference(Preference)}
-     * can determine the correct scroll position to request.
-     */
-    public interface PreferencePositionCallback {
-
-        /**
-         * Return the adapter position of the first {@link Preference} with the specified key
-         * @param key Key of {@link Preference} to find
-         * @return Adapter position of the {@link Preference} or
-         *         {@link android.support.v7.widget.RecyclerView#NO_POSITION} if not found
-         */
-        int getPreferenceAdapterPosition(String key);
-
-        /**
-         * Return the adapter position of the specified {@link Preference} object
-         * @param preference {@link Preference} object to find
-         * @return Adapter position of the {@link Preference} or
-         *         {@link android.support.v7.widget.RecyclerView#NO_POSITION} if not found
-         */
-        int getPreferenceAdapterPosition(Preference preference);
-    }
-
-    /**
-     * Interface for callback to implement so that they can save and restore the preference group's
-     * instance state.
-     */
-    interface PreferenceInstanceStateCallback {
-
-        /**
-         * Save the internal state that can later be used to create a new instance with that
-         * same state.
-         *
-         * @param state The Parcelable to save the current dynamic state.
-         */
-        Parcelable saveInstanceState(Parcelable state);
-
-        /**
-         * Restore the previously saved state from the given parcelable.
-         *
-         * @param state The Parcelable that holds the previously saved state.
-         * @return the super state if data has been saved in the state in {@link saveInstanceState}
-         *         or state otherwise
-         */
-        Parcelable restoreInstanceState(Parcelable state);
-    }
-
-}
diff --git a/android/support/v7/preference/PreferenceGroupAdapter.java b/android/support/v7/preference/PreferenceGroupAdapter.java
deleted file mode 100644
index 00a0c5b..0000000
--- a/android/support/v7/preference/PreferenceGroupAdapter.java
+++ /dev/null
@@ -1,402 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.widget.RecyclerView;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An adapter that connects a RecyclerView to the {@link Preference} objects contained in the
- * associated {@link PreferenceGroup}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class PreferenceGroupAdapter extends RecyclerView.Adapter<PreferenceViewHolder>
-        implements Preference.OnPreferenceChangeInternalListener,
-        PreferenceGroup.PreferencePositionCallback {
-
-    private static final String TAG = "PreferenceGroupAdapter";
-
-    /**
-     * The group that we are providing data from.
-     */
-    private PreferenceGroup mPreferenceGroup;
-
-    /**
-     * Maps a position into this adapter -> {@link Preference}. These
-     * {@link Preference}s don't have to be direct children of this
-     * {@link PreferenceGroup}, they can be grand children or younger)
-     */
-    private List<Preference> mPreferenceList;
-
-    /**
-     * Contains a sorted list of all preferences in this adapter regardless of visibility. This is
-     * used to construct {@link #mPreferenceList}
-     */
-    private List<Preference> mPreferenceListInternal;
-
-    /**
-     * List of unique Preference and its subclasses' names and layouts.
-     */
-    private List<PreferenceLayout> mPreferenceLayouts;
-
-
-    private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
-
-    private Handler mHandler;
-
-    private CollapsiblePreferenceGroupController mPreferenceGroupController;
-
-    private Runnable mSyncRunnable = new Runnable() {
-        @Override
-        public void run() {
-            syncMyPreferences();
-        }
-    };
-
-    private static class PreferenceLayout {
-        private int resId;
-        private int widgetResId;
-        private String name;
-
-        public PreferenceLayout() {}
-
-        public PreferenceLayout(PreferenceLayout other) {
-            resId = other.resId;
-            widgetResId = other.widgetResId;
-            name = other.name;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!(o instanceof PreferenceLayout)) {
-                return false;
-            }
-            final PreferenceLayout other = (PreferenceLayout) o;
-            return resId == other.resId
-                    && widgetResId == other.widgetResId
-                    && TextUtils.equals(name, other.name);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = 17;
-            result = 31 * result + resId;
-            result = 31 * result + widgetResId;
-            result = 31 * result + name.hashCode();
-            return result;
-        }
-    }
-
-    public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
-        this(preferenceGroup, new Handler());
-    }
-
-    private PreferenceGroupAdapter(PreferenceGroup preferenceGroup, Handler handler) {
-        mPreferenceGroup = preferenceGroup;
-        mHandler = handler;
-        mPreferenceGroupController =
-                new CollapsiblePreferenceGroupController(preferenceGroup, this);
-        // If this group gets or loses any children, let us know
-        mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
-
-        mPreferenceList = new ArrayList<>();
-        mPreferenceListInternal = new ArrayList<>();
-        mPreferenceLayouts = new ArrayList<>();
-
-        if (mPreferenceGroup instanceof PreferenceScreen) {
-            setHasStableIds(((PreferenceScreen) mPreferenceGroup).shouldUseGeneratedIds());
-        } else {
-            setHasStableIds(true);
-        }
-
-        syncMyPreferences();
-    }
-
-    @VisibleForTesting
-    static PreferenceGroupAdapter createInstanceWithCustomHandler(PreferenceGroup preferenceGroup,
-            Handler handler) {
-        return new PreferenceGroupAdapter(preferenceGroup, handler);
-    }
-
-    private void syncMyPreferences() {
-        for (final Preference preference : mPreferenceListInternal) {
-            // Clear out the listeners in anticipation of some items being removed. This listener
-            // will be (re-)added to the remaining prefs when we flatten.
-            preference.setOnPreferenceChangeInternalListener(null);
-        }
-        final List<Preference> fullPreferenceList = new ArrayList<>(mPreferenceListInternal.size());
-        flattenPreferenceGroup(fullPreferenceList, mPreferenceGroup);
-
-        final List<Preference> visiblePreferenceList =
-                mPreferenceGroupController.createVisiblePreferencesList(fullPreferenceList);
-
-        final List<Preference> oldVisibleList = mPreferenceList;
-        mPreferenceList = visiblePreferenceList;
-        mPreferenceListInternal = fullPreferenceList;
-
-        final PreferenceManager preferenceManager = mPreferenceGroup.getPreferenceManager();
-        if (preferenceManager != null
-                && preferenceManager.getPreferenceComparisonCallback() != null) {
-            final PreferenceManager.PreferenceComparisonCallback comparisonCallback =
-                    preferenceManager.getPreferenceComparisonCallback();
-            final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
-                @Override
-                public int getOldListSize() {
-                    return oldVisibleList.size();
-                }
-
-                @Override
-                public int getNewListSize() {
-                    return visiblePreferenceList.size();
-                }
-
-                @Override
-                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-                    return comparisonCallback.arePreferenceItemsTheSame(
-                            oldVisibleList.get(oldItemPosition),
-                            visiblePreferenceList.get(newItemPosition));
-                }
-
-                @Override
-                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
-                    return comparisonCallback.arePreferenceContentsTheSame(
-                            oldVisibleList.get(oldItemPosition),
-                            visiblePreferenceList.get(newItemPosition));
-                }
-            });
-
-            result.dispatchUpdatesTo(this);
-        } else {
-            notifyDataSetChanged();
-        }
-
-        for (final Preference preference : fullPreferenceList) {
-            preference.clearWasDetached();
-        }
-    }
-
-    private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {
-        group.sortPreferences();
-
-        final int groupSize = group.getPreferenceCount();
-        for (int i = 0; i < groupSize; i++) {
-            final Preference preference = group.getPreference(i);
-
-            preferences.add(preference);
-
-            addPreferenceClassName(preference);
-
-            if (preference instanceof PreferenceGroup) {
-                final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;
-                if (preferenceAsGroup.isOnSameScreenAsChildren()) {
-                    flattenPreferenceGroup(preferences, preferenceAsGroup);
-                }
-            }
-
-            preference.setOnPreferenceChangeInternalListener(this);
-        }
-    }
-
-    /**
-     * Creates a string that includes the preference name, layout id and widget layout id.
-     * If a particular preference type uses 2 different resources, they will be treated as
-     * different view types.
-     */
-    private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
-        PreferenceLayout pl = in != null? in : new PreferenceLayout();
-        pl.name = preference.getClass().getName();
-        pl.resId = preference.getLayoutResource();
-        pl.widgetResId = preference.getWidgetLayoutResource();
-        return pl;
-    }
-
-    private void addPreferenceClassName(Preference preference) {
-        final PreferenceLayout pl = createPreferenceLayout(preference, null);
-        if (!mPreferenceLayouts.contains(pl)) {
-            mPreferenceLayouts.add(pl);
-        }
-    }
-
-    @Override
-    public int getItemCount() {
-        return mPreferenceList.size();
-    }
-
-    public Preference getItem(int position) {
-        if (position < 0 || position >= getItemCount()) return null;
-        return mPreferenceList.get(position);
-    }
-
-    @Override
-    public long getItemId(int position) {
-        if (!hasStableIds()) {
-            return RecyclerView.NO_ID;
-        }
-        return this.getItem(position).getId();
-    }
-
-    @Override
-    public void onPreferenceChange(Preference preference) {
-        final int index = mPreferenceList.indexOf(preference);
-        // If we don't find the preference, we don't need to notify anyone
-        if (index != -1) {
-            // Send the pref object as a placeholder to ensure the view holder is recycled in place
-            notifyItemChanged(index, preference);
-        }
-    }
-
-    @Override
-    public void onPreferenceHierarchyChange(Preference preference) {
-        mHandler.removeCallbacks(mSyncRunnable);
-        mHandler.post(mSyncRunnable);
-    }
-
-    @Override
-    public void onPreferenceVisibilityChange(Preference preference) {
-        if (!mPreferenceListInternal.contains(preference)) {
-            return;
-        }
-        if (mPreferenceGroupController.onPreferenceVisibilityChange(preference)) {
-            return;
-        }
-        if (preference.isVisible()) {
-            // The preference has become visible, we need to add it in the correct location.
-
-            // Index (inferred) in mPreferenceList of the item preceding the newly visible pref
-            int previousVisibleIndex = -1;
-            for (final Preference pref : mPreferenceListInternal) {
-                if (preference.equals(pref)) {
-                    break;
-                }
-                if (pref.isVisible()) {
-                    previousVisibleIndex++;
-                }
-            }
-            // Insert this preference into the active list just after the previous visible entry
-            mPreferenceList.add(previousVisibleIndex + 1, preference);
-
-            notifyItemInserted(previousVisibleIndex + 1);
-        } else {
-            // The preference has become invisible. Find it in the list and remove it.
-
-            int removalIndex;
-            final int listSize = mPreferenceList.size();
-            for (removalIndex = 0; removalIndex < listSize; removalIndex++) {
-                if (preference.equals(mPreferenceList.get(removalIndex))) {
-                    break;
-                }
-            }
-            mPreferenceList.remove(removalIndex);
-            notifyItemRemoved(removalIndex);
-        }
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        final Preference preference = this.getItem(position);
-
-        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
-
-        int viewType = mPreferenceLayouts.indexOf(mTempPreferenceLayout);
-        if (viewType != -1) {
-            return viewType;
-        } else {
-            viewType = mPreferenceLayouts.size();
-            mPreferenceLayouts.add(new PreferenceLayout(mTempPreferenceLayout));
-            return viewType;
-        }
-    }
-
-    @Override
-    public PreferenceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        final PreferenceLayout pl = mPreferenceLayouts.get(viewType);
-        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-        TypedArray a
-                = parent.getContext().obtainStyledAttributes(null, R.styleable.BackgroundStyle);
-        Drawable background
-                = a.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground);
-        if (background == null) {
-            background = ContextCompat.getDrawable(parent.getContext(),
-                    android.R.drawable.list_selector_background);
-        }
-        a.recycle();
-
-        final View view = inflater.inflate(pl.resId, parent, false);
-        if (view.getBackground() == null) {
-            ViewCompat.setBackground(view, background);
-        }
-
-        final ViewGroup widgetFrame = (ViewGroup) view.findViewById(android.R.id.widget_frame);
-        if (widgetFrame != null) {
-            if (pl.widgetResId != 0) {
-                inflater.inflate(pl.widgetResId, widgetFrame);
-            } else {
-                widgetFrame.setVisibility(View.GONE);
-            }
-        }
-
-        return new PreferenceViewHolder(view);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder, int position) {
-        final Preference preference = getItem(position);
-        preference.onBindViewHolder(holder);
-    }
-
-    @Override
-    public int getPreferenceAdapterPosition(String key) {
-        final int size = mPreferenceList.size();
-        for (int i = 0; i < size; i++) {
-            final Preference candidate = mPreferenceList.get(i);
-            if (TextUtils.equals(key, candidate.getKey())) {
-                return i;
-            }
-        }
-        return RecyclerView.NO_POSITION;
-    }
-
-    @Override
-    public int getPreferenceAdapterPosition(Preference preference) {
-        final int size = mPreferenceList.size();
-        for (int i = 0; i < size; i++) {
-            final Preference candidate = mPreferenceList.get(i);
-            if (candidate != null && candidate.equals(preference)) {
-                return i;
-            }
-        }
-        return RecyclerView.NO_POSITION;
-    }
-}
diff --git a/android/support/v7/preference/PreferenceInflater.java b/android/support/v7/preference/PreferenceInflater.java
deleted file mode 100644
index 453b4d4..0000000
--- a/android/support/v7/preference/PreferenceInflater.java
+++ /dev/null
@@ -1,372 +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.preference;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.XmlResourceParser;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.util.Xml;
-import android.view.InflateException;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-
-/**
- * The {@link PreferenceInflater} is used to inflate preference hierarchies from
- * XML files.
- */
-class PreferenceInflater {
-    private static final String TAG = "PreferenceInflater";
-
-    private static final Class<?>[] CONSTRUCTOR_SIGNATURE = new Class[] {
-            Context.class, AttributeSet.class};
-
-    private static final HashMap<String, Constructor> CONSTRUCTOR_MAP = new HashMap<>();
-
-    private final Context mContext;
-
-    private final Object[] mConstructorArgs = new Object[2];
-
-    private PreferenceManager mPreferenceManager;
-
-    private String[] mDefaultPackages;
-
-    private static final String INTENT_TAG_NAME = "intent";
-    private static final String EXTRA_TAG_NAME = "extra";
-
-    public PreferenceInflater(Context context, PreferenceManager preferenceManager) {
-        mContext = context;
-        init(preferenceManager);
-    }
-
-    private void init(PreferenceManager preferenceManager) {
-        mPreferenceManager = preferenceManager;
-        setDefaultPackages(new String[] {"android.support.v14.preference.",
-                "android.support.v7.preference."});
-    }
-
-    /**
-     * Sets the default package that will be searched for classes to construct
-     * for tag names that have no explicit package.
-     *
-     * @param defaultPackage The default package. This will be prepended to the
-     *            tag name, so it should end with a period.
-     */
-    public void setDefaultPackages(String[] defaultPackage) {
-        mDefaultPackages = defaultPackage;
-    }
-
-    /**
-     * Returns the default package, or null if it is not set.
-     *
-     * @see #setDefaultPackages(String[])
-     * @return The default package.
-     */
-    public String[] getDefaultPackages() {
-        return mDefaultPackages;
-    }
-
-    /**
-     * Return the context we are running in, for access to resources, class
-     * loader, etc.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Inflate a new item hierarchy from the specified xml resource. Throws
-     * InflaterException if there is an error.
-     *
-     * @param resource ID for an XML resource to load (e.g.,
-     *        <code>R.layout.main_page</code>)
-     * @param root Optional parent of the generated hierarchy.
-     * @return The root of the inflated hierarchy. If root was supplied,
-     *         this is the root item; otherwise it is the root of the inflated
-     *         XML file.
-     */
-    public Preference inflate(int resource, @Nullable PreferenceGroup root) {
-        XmlResourceParser parser = getContext().getResources().getXml(resource);
-        try {
-            return inflate(parser, root);
-        } finally {
-            parser.close();
-        }
-    }
-
-    /**
-     * Inflate a new hierarchy from the specified XML node. Throws
-     * InflaterException if there is an error.
-     * <p>
-     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
-     * reasons, inflation relies heavily on pre-processing of XML files
-     * that is done at build time. Therefore, it is not currently possible to
-     * use inflater with an XmlPullParser over a plain XML file at runtime.
-     *
-     * @param parser XML dom node containing the description of the
-     *        hierarchy.
-     * @param root Optional to be the parent of the generated hierarchy (if
-     *        <em>attachToRoot</em> is true), or else simply an object that
-     *        provides a set of values for root of the returned
-     *        hierarchy (if <em>attachToRoot</em> is false.)
-     * @return The root of the inflated hierarchy. If root was supplied,
-     *         this is root; otherwise it is the root of
-     *         the inflated XML file.
-     */
-    public Preference inflate(XmlPullParser parser, @Nullable PreferenceGroup root) {
-        synchronized (mConstructorArgs) {
-            final AttributeSet attrs = Xml.asAttributeSet(parser);
-            mConstructorArgs[0] = mContext;
-            final Preference result;
-
-            try {
-                // Look for the root node.
-                int type;
-                do {
-                    type = parser.next();
-                } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
-
-                if (type != XmlPullParser.START_TAG) {
-                    throw new InflateException(parser.getPositionDescription()
-                            + ": No start tag found!");
-                }
-
-                // Temp is the root that was found in the xml
-                Preference xmlRoot = createItemFromTag(parser.getName(),
-                        attrs);
-
-                result = onMergeRoots(root, (PreferenceGroup) xmlRoot);
-
-                // Inflate all children under temp
-                rInflate(parser, result, attrs);
-
-            } catch (InflateException e) {
-                throw e;
-            } catch (XmlPullParserException e) {
-                final InflateException ex = new InflateException(e.getMessage());
-                ex.initCause(e);
-                throw ex;
-            } catch (IOException e) {
-                final InflateException ex = new InflateException(
-                        parser.getPositionDescription()
-                                + ": " + e.getMessage());
-                ex.initCause(e);
-                throw ex;
-            }
-
-            return result;
-        }
-    }
-
-    private @NonNull PreferenceGroup onMergeRoots(PreferenceGroup givenRoot,
-            @NonNull PreferenceGroup xmlRoot) {
-        // If we were given a Preferences, use it as the root (ignoring the root
-        // Preferences from the XML file).
-        if (givenRoot == null) {
-            xmlRoot.onAttachedToHierarchy(mPreferenceManager);
-            return xmlRoot;
-        } else {
-            return givenRoot;
-        }
-    }
-
-    /**
-     * Low-level function for instantiating by name. This attempts to
-     * instantiate class of the given <var>name</var> found in this
-     * inflater's ClassLoader.
-     *
-     * <p>
-     * There are two things that can happen in an error case: either the
-     * exception describing the error will be thrown, or a null will be
-     * returned. You must deal with both possibilities -- the former will happen
-     * the first time createItem() is called for a class of a particular name,
-     * the latter every time there-after for that class name.
-     *
-     * @param name The full name of the class to be instantiated.
-     * @param attrs The XML attributes supplied for this instance.
-     *
-     * @return The newly instantiated item, or null.
-     */
-    private Preference createItem(@NonNull String name, @Nullable String[] prefixes,
-            AttributeSet attrs)
-            throws ClassNotFoundException, InflateException {
-        Constructor constructor = CONSTRUCTOR_MAP.get(name);
-
-        try {
-            if (constructor == null) {
-                // Class not found in the cache, see if it's real,
-                // and try to add it
-                final ClassLoader classLoader = mContext.getClassLoader();
-                Class<?> clazz = null;
-                if (prefixes == null || prefixes.length == 0) {
-                    clazz = classLoader.loadClass(name);
-                } else {
-                    ClassNotFoundException notFoundException = null;
-                    for (final String prefix : prefixes) {
-                        try {
-                            clazz = classLoader.loadClass(prefix + name);
-                            break;
-                        } catch (final ClassNotFoundException e) {
-                            notFoundException = e;
-                        }
-                    }
-                    if (clazz == null) {
-                        if (notFoundException == null) {
-                            throw new InflateException(attrs
-                                    .getPositionDescription()
-                                    + ": Error inflating class " + name);
-                        } else {
-                            throw notFoundException;
-                        }
-                    }
-                }
-                constructor = clazz.getConstructor(CONSTRUCTOR_SIGNATURE);
-                constructor.setAccessible(true);
-                CONSTRUCTOR_MAP.put(name, constructor);
-            }
-
-            Object[] args = mConstructorArgs;
-            args[1] = attrs;
-            return (Preference) constructor.newInstance(args);
-
-        } catch (ClassNotFoundException e) {
-            // If loadClass fails, we should propagate the exception.
-            throw e;
-        } catch (Exception e) {
-            final InflateException ie = new InflateException(attrs
-                    .getPositionDescription() + ": Error inflating class " + name);
-            ie.initCause(e);
-            throw ie;
-        }
-    }
-
-    /**
-     * This routine is responsible for creating the correct subclass of item
-     * given the xml element name. Override it to handle custom item objects. If
-     * you override this in your subclass be sure to call through to
-     * super.onCreateItem(name) for names you do not recognize.
-     *
-     * @param name The fully qualified class name of the item to be create.
-     * @param attrs An AttributeSet of attributes to apply to the item.
-     * @return The item created.
-     */
-    protected Preference onCreateItem(String name, AttributeSet attrs)
-            throws ClassNotFoundException {
-        return createItem(name, mDefaultPackages, attrs);
-    }
-
-    private Preference createItemFromTag(String name,
-            AttributeSet attrs) {
-        try {
-            final Preference item;
-
-            if (-1 == name.indexOf('.')) {
-                item = onCreateItem(name, attrs);
-            } else {
-                item = createItem(name, null, attrs);
-            }
-
-            return item;
-
-        } catch (InflateException e) {
-            throw e;
-
-        } catch (ClassNotFoundException e) {
-            final InflateException ie = new InflateException(attrs
-                    .getPositionDescription()
-                    + ": Error inflating class (not found)" + name);
-            ie.initCause(e);
-            throw ie;
-
-        } catch (Exception e) {
-            final InflateException ie = new InflateException(attrs
-                    .getPositionDescription()
-                    + ": Error inflating class " + name);
-            ie.initCause(e);
-            throw ie;
-        }
-    }
-
-    /**
-     * Recursive method used to descend down the xml hierarchy and instantiate
-     * items, instantiate their children, and then call onFinishInflate().
-     */
-    private void rInflate(XmlPullParser parser, Preference parent, final AttributeSet attrs)
-            throws XmlPullParserException, IOException {
-        final int depth = parser.getDepth();
-
-        int type;
-        while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            final String name = parser.getName();
-
-            if (INTENT_TAG_NAME.equals(name)) {
-                final Intent intent;
-
-                try {
-                    intent = Intent.parseIntent(getContext().getResources(), parser, attrs);
-                } catch (IOException e) {
-                    XmlPullParserException ex = new XmlPullParserException(
-                            "Error parsing preference");
-                    ex.initCause(e);
-                    throw ex;
-                }
-
-                parent.setIntent(intent);
-            } else if (EXTRA_TAG_NAME.equals(name)) {
-                getContext().getResources().parseBundleExtra(EXTRA_TAG_NAME, attrs,
-                        parent.getExtras());
-                try {
-                    skipCurrentTag(parser);
-                } catch (IOException e) {
-                    XmlPullParserException ex = new XmlPullParserException(
-                            "Error parsing preference");
-                    ex.initCause(e);
-                    throw ex;
-                }
-            } else {
-                final Preference item = createItemFromTag(name, attrs);
-                ((PreferenceGroup) parent).addItemFromInflater(item);
-                rInflate(parser, item, attrs);
-            }
-        }
-
-    }
-
-    private static void skipCurrentTag(XmlPullParser parser)
-            throws XmlPullParserException, IOException {
-        int outerDepth = parser.getDepth();
-        int type;
-        do {
-            type = parser.next();
-        } while (type != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth));
-    }
-
-}
diff --git a/android/support/v7/preference/PreferenceManager.java b/android/support/v7/preference/PreferenceManager.java
deleted file mode 100644
index 19b6908..0000000
--- a/android/support/v7/preference/PreferenceManager.java
+++ /dev/null
@@ -1,726 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.text.TextUtils;
-
-/**
- * Used to help create {@link Preference} hierarchies
- * from activities or XML.
- * <p>
- * In most cases, clients should use
- * {@link android.support.v14.preference.PreferenceFragment#addPreferencesFromResource(int)}, or
- * {@link PreferenceFragmentCompat#addPreferencesFromResource(int)}.
- *
- * @see android.support.v14.preference.PreferenceFragment
- * @see PreferenceFragmentCompat
- */
-public class PreferenceManager {
-
-    public static final String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
-
-    /**
-     * The context to use. This should always be set.
-     */
-    private Context mContext;
-
-    /**
-     * The counter for unique IDs.
-     */
-    private long mNextId = 0;
-
-    /**
-     * 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;
-
-    /**
-     * Blocks commits from happening on the shared editor. This is used when
-     * inflating the hierarchy. Do not set this directly, use {@link #setNoCommit(boolean)}
-     */
-    private boolean mNoCommit;
-
-    /**
-     * The SharedPreferences name that will be used for all {@link Preference}s
-     * managed by this instance.
-     */
-    private String mSharedPreferencesName;
-
-    /**
-     * The SharedPreferences mode that will be used for all {@link Preference}s
-     * managed by this instance.
-     */
-    private int mSharedPreferencesMode;
-
-    private static final int STORAGE_DEFAULT = 0;
-    private static final int STORAGE_DEVICE_PROTECTED = 1;
-
-    private int mStorage = STORAGE_DEFAULT;
-
-    /**
-     * The {@link PreferenceScreen} at the root of the preference hierarchy.
-     */
-    private PreferenceScreen mPreferenceScreen;
-
-    private PreferenceComparisonCallback mPreferenceComparisonCallback;
-    private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
-    private OnDisplayPreferenceDialogListener mOnDisplayPreferenceDialogListener;
-    private OnNavigateToScreenListener mOnNavigateToScreenListener;
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public PreferenceManager(Context context) {
-        mContext = context;
-
-        setSharedPreferencesName(getDefaultSharedPreferencesName(context));
-    }
-
-    /**
-     * Inflates a preference hierarchy from XML. If a preference hierarchy is
-     * given, the new preference hierarchies will be merged in.
-     *
-     * @param context The context of the resource.
-     * @param resId The resource ID of the XML to inflate.
-     * @param rootPreferences Optional existing hierarchy to merge the new
-     *            hierarchies into.
-     * @return The root hierarchy (if one was not provided, the new hierarchy's
-     *         root).
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public PreferenceScreen inflateFromResource(Context context, int resId,
-            PreferenceScreen rootPreferences) {
-        // Block commits
-        setNoCommit(true);
-
-        final PreferenceInflater inflater = new PreferenceInflater(context, this);
-        rootPreferences = (PreferenceScreen) inflater.inflate(resId, rootPreferences);
-        rootPreferences.onAttachedToHierarchy(this);
-
-        // Unblock commits
-        setNoCommit(false);
-
-        return rootPreferences;
-    }
-
-    public PreferenceScreen createPreferenceScreen(Context context) {
-        final PreferenceScreen preferenceScreen = new PreferenceScreen(context, null);
-        preferenceScreen.onAttachedToHierarchy(this);
-        return preferenceScreen;
-    }
-
-    /**
-     * Called by a preference to get a unique ID in its hierarchy.
-     *
-     * @return A unique ID.
-     */
-    long getNextId() {
-        synchronized (this) {
-            return mNextId++;
-        }
-    }
-
-    /**
-     * 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)}.
-     * @see Context#getSharedPreferences(String, int)
-     */
-    public String getSharedPreferencesName() {
-        return mSharedPreferencesName;
-    }
-
-    /**
-     * 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;
-        mSharedPreferences = null;
-    }
-
-    /**
-     * Returns the current mode of the SharedPreferences file that preferences managed by
-     * this will use.
-     *
-     * @return The mode that can be passed to {@link Context#getSharedPreferences(String, int)}.
-     * @see Context#getSharedPreferences(String, int)
-     */
-    public int getSharedPreferencesMode() {
-        return mSharedPreferencesMode;
-    }
-
-    /**
-     * Sets the mode of the SharedPreferences file that preferences managed by this
-     * will use.
-     *
-     * @param sharedPreferencesMode The mode of the SharedPreferences file.
-     * @see Context#getSharedPreferences(String, int)
-     */
-    public void setSharedPreferencesMode(int sharedPreferencesMode) {
-        mSharedPreferencesMode = sharedPreferencesMode;
-        mSharedPreferences = null;
-    }
-
-    /**
-     * Sets the storage location used internally by this class to be the default
-     * provided by the hosting {@link Context}.
-     */
-    public void setStorageDefault() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            mStorage = STORAGE_DEFAULT;
-            mSharedPreferences = null;
-        }
-    }
-
-    /**
-     * Explicitly set the storage location used internally by this class to be
-     * device-protected storage.
-     * <p>
-     * On devices with direct boot, data stored in this location is encrypted
-     * with a key tied to the physical device, and it can be accessed
-     * immediately after the device has booted successfully, both
-     * <em>before and after</em> the user has authenticated with their
-     * credentials (such as a lock pattern or PIN).
-     * <p>
-     * Because device-protected data is available without user authentication,
-     * you should carefully limit the data you store using this Context. For
-     * example, storing sensitive authentication tokens or passwords in the
-     * device-protected area is strongly discouraged.
-     * <p>
-     * Prior to API 24 this method has no effect,
-     * since device-protected storage is not available.
-     *
-     * @see Context#createDeviceProtectedStorageContext()
-     */
-    public void setStorageDeviceProtected() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            mStorage = STORAGE_DEVICE_PROTECTED;
-            mSharedPreferences = null;
-        }
-    }
-
-    /**
-     * Indicates if the storage location used internally by this class is the
-     * default provided by the hosting {@link Context}.
-     *
-     * @see #setStorageDefault()
-     * @see #setStorageDeviceProtected()
-     */
-    public boolean isStorageDefault() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return mStorage == STORAGE_DEFAULT;
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Indicates if the storage location used internally by this class is backed
-     * by device-protected storage.
-     *
-     * @see #setStorageDefault()
-     * @see #setStorageDeviceProtected()
-     */
-    public boolean isStorageDeviceProtected() {
-        if (Build.VERSION.SDK_INT >= 24) {
-            return mStorage == STORAGE_DEVICE_PROTECTED;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * 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 {@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) {
-                case STORAGE_DEVICE_PROTECTED:
-                    storageContext = ContextCompat.createDeviceProtectedStorageContext(mContext);
-                    break;
-                default:
-                    storageContext = mContext;
-                    break;
-            }
-
-            mSharedPreferences = storageContext.getSharedPreferences(mSharedPreferencesName,
-                    mSharedPreferencesMode);
-        }
-
-        return mSharedPreferences;
-    }
-
-    /**
-     * Gets a SharedPreferences instance that points to the default file that is
-     * used by the preference framework in the given context.
-     *
-     * @param context The context of the preferences whose values are wanted.
-     * @return A SharedPreferences instance that can be used to retrieve and
-     *         listen to values of the preferences.
-     */
-    public static SharedPreferences getDefaultSharedPreferences(Context context) {
-        return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
-                getDefaultSharedPreferencesMode());
-    }
-
-    private static String getDefaultSharedPreferencesName(Context context) {
-        return context.getPackageName() + "_preferences";
-    }
-
-    private static int getDefaultSharedPreferencesMode() {
-        return Context.MODE_PRIVATE;
-    }
-
-    /**
-     * Returns the root of the preference hierarchy managed by this class.
-     *
-     * @return The {@link PreferenceScreen} object that is at the root of the hierarchy.
-     */
-    public PreferenceScreen getPreferenceScreen() {
-        return mPreferenceScreen;
-    }
-
-    /**
-     * Sets the root of the preference hierarchy.
-     *
-     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
-     * @return Whether the {@link PreferenceScreen} given is different than the previous.
-     */
-    public boolean setPreferences(PreferenceScreen preferenceScreen) {
-        if (preferenceScreen != mPreferenceScreen) {
-            if (mPreferenceScreen != null) {
-                mPreferenceScreen.onDetached();
-            }
-            mPreferenceScreen = preferenceScreen;
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Finds a {@link Preference} based on its key.
-     *
-     * @param key The key of the preference to retrieve.
-     * @return The {@link Preference} with the key, or null.
-     * @see PreferenceGroup#findPreference(CharSequence)
-     */
-    public Preference findPreference(CharSequence key) {
-        if (mPreferenceScreen == null) {
-            return null;
-        }
-
-        return mPreferenceScreen.findPreference(key);
-    }
-
-    /**
-     * Sets the default values from an XML preference file by reading the values defined
-     * by each {@link Preference} item's {@code android:defaultValue} attribute. This should
-     * be called by the application's main activity.
-     * <p>
-     *
-     * @param context The context of the shared preferences.
-     * @param resId The resource ID of the preference XML file.
-     * @param readAgain Whether to re-read the default values.
-     * If false, this method sets the default values only if this
-     * method has never been called in the past (or if the
-     * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
-     * preferences file is false). To attempt to set the default values again
-     * bypassing this check, set {@code readAgain} to true.
-     *            <p class="note">
-     *            Note: this will NOT reset preferences back to their default
-     *            values. For that functionality, use
-     *            {@link PreferenceManager#getDefaultSharedPreferences(Context)}
-     *            and clear it followed by a call to this method with this
-     *            parameter set to true.
-     */
-    public static void setDefaultValues(Context context, int resId, boolean readAgain) {
-        // Use the default shared preferences name and mode
-        setDefaultValues(context, getDefaultSharedPreferencesName(context),
-                getDefaultSharedPreferencesMode(), resId, readAgain);
-    }
-
-    /**
-     * Similar to {@link #setDefaultValues(Context, int, boolean)} but allows
-     * the client to provide the filename and mode of the shared preferences
-     * file.
-     *
-     * @param context The context of the shared preferences.
-     * @param sharedPreferencesName A custom name for the shared preferences file.
-     * @param sharedPreferencesMode The file creation mode for the shared preferences file, such
-     * as {@link android.content.Context#MODE_PRIVATE} or {@link
-     * android.content.Context#MODE_PRIVATE}
-     * @param resId The resource ID of the preference XML file.
-     * @param readAgain Whether to re-read the default values.
-     * If false, this method will set the default values only if this
-     * method has never been called in the past (or if the
-     * {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
-     * preferences file is false). To attempt to set the default values again
-     * bypassing this check, set {@code readAgain} to true.
-     *            <p class="note">
-     *            Note: this will NOT reset preferences back to their default
-     *            values. For that functionality, use
-     *            {@link PreferenceManager#getDefaultSharedPreferences(Context)}
-     *            and clear it followed by a call to this method with this
-     *            parameter set to true.
-     *
-     * @see #setDefaultValues(Context, int, boolean)
-     * @see #setSharedPreferencesName(String)
-     * @see #setSharedPreferencesMode(int)
-     */
-    public static void setDefaultValues(Context context, String sharedPreferencesName,
-            int sharedPreferencesMode, int resId, boolean readAgain) {
-        final SharedPreferences defaultValueSp = context.getSharedPreferences(
-                KEY_HAS_SET_DEFAULT_VALUES, Context.MODE_PRIVATE);
-
-        if (readAgain || !defaultValueSp.getBoolean(KEY_HAS_SET_DEFAULT_VALUES, false)) {
-            final PreferenceManager pm = new PreferenceManager(context);
-            pm.setSharedPreferencesName(sharedPreferencesName);
-            pm.setSharedPreferencesMode(sharedPreferencesMode);
-            pm.inflateFromResource(context, resId, null);
-
-            defaultValueSp.edit()
-                    .putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true)
-                    .apply();
-        }
-    }
-
-    /**
-     * 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. 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) {
-                mEditor = getSharedPreferences().edit();
-            }
-
-            return mEditor;
-        } else {
-            return getSharedPreferences().edit();
-        }
-    }
-
-    /**
-     * Whether it is the client's responsibility to commit on the
-     * {@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() {
-        return !mNoCommit;
-    }
-
-    private void setNoCommit(boolean noCommit) {
-        if (!noCommit && mEditor != null) {
-            mEditor.apply();
-        }
-        mNoCommit = noCommit;
-    }
-
-    /**
-     * Returns the context.
-     *
-     * @return The context.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    public PreferenceComparisonCallback getPreferenceComparisonCallback() {
-        return mPreferenceComparisonCallback;
-    }
-
-    public void setPreferenceComparisonCallback(
-            PreferenceComparisonCallback preferenceComparisonCallback) {
-        mPreferenceComparisonCallback = preferenceComparisonCallback;
-    }
-
-    public OnDisplayPreferenceDialogListener getOnDisplayPreferenceDialogListener() {
-        return mOnDisplayPreferenceDialogListener;
-    }
-
-    public void setOnDisplayPreferenceDialogListener(
-            OnDisplayPreferenceDialogListener onDisplayPreferenceDialogListener) {
-        mOnDisplayPreferenceDialogListener = onDisplayPreferenceDialogListener;
-    }
-
-    /**
-     * Called when a preference requests that a dialog be shown to complete a user interaction.
-     *
-     * @param preference The preference requesting the dialog.
-     */
-    public void showDialog(Preference preference) {
-        if (mOnDisplayPreferenceDialogListener != null) {
-            mOnDisplayPreferenceDialogListener.onDisplayPreferenceDialog(preference);
-        }
-    }
-
-    /**
-     * Sets the callback to be invoked when a {@link Preference} in the
-     * hierarchy rooted at this {@link PreferenceManager} is clicked.
-     *
-     * @param listener The callback to be invoked.
-     */
-    public void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) {
-        mOnPreferenceTreeClickListener = listener;
-    }
-
-    public OnPreferenceTreeClickListener getOnPreferenceTreeClickListener() {
-        return mOnPreferenceTreeClickListener;
-    }
-
-    /**
-     * Sets the callback to be invoked when a {@link PreferenceScreen} in the hierarchy rooted at
-     * this {@link PreferenceManager} is clicked.
-     *
-     * @param listener The callback to be invoked.
-     */
-    public void setOnNavigateToScreenListener(OnNavigateToScreenListener listener) {
-        mOnNavigateToScreenListener = listener;
-    }
-
-    /**
-     * Returns the {@link PreferenceManager.OnNavigateToScreenListener}, if one has been set.
-     */
-    public OnNavigateToScreenListener getOnNavigateToScreenListener() {
-        return mOnNavigateToScreenListener;
-    }
-
-    /**
-     * Callback class to be used by the {@link android.support.v7.widget.RecyclerView.Adapter}
-     * associated with the {@link PreferenceScreen}, used to determine when two {@link Preference}
-     * objects are semantically and visually the same.
-     */
-    public static abstract class PreferenceComparisonCallback {
-        /**
-         * Called to determine if two {@link Preference} objects represent the same item
-         *
-         * @param p1 {@link Preference} object to compare
-         * @param p2 {@link Preference} object to compare
-         * @return {@code true} if the objects represent the same item
-         */
-        public abstract boolean arePreferenceItemsTheSame(Preference p1, Preference p2);
-
-        /**
-         * Called to determine if two {@link Preference} objects will display the same data
-         *
-         * @param p1 {@link Preference} object to compare
-         * @param p2 {@link Preference} object to compare
-         * @return {@code true} if the objects are visually identical
-         */
-        public abstract boolean arePreferenceContentsTheSame(Preference p1, Preference p2);
-    }
-
-    /**
-     * A basic implementation of {@link PreferenceComparisonCallback} suitable for use with the
-     * default {@link Preference} classes. If the {@link PreferenceScreen} contains custom
-     * {@link Preference} subclasses, you must override
-     * {@link #arePreferenceContentsTheSame(Preference, Preference)}
-     */
-    public static class SimplePreferenceComparisonCallback extends PreferenceComparisonCallback {
-        /**
-         * {@inheritDoc}
-         *
-         * <p>This method will not be able to track replaced {@link Preference} objects if they
-         * do not have a unique key.</p>
-         *
-         * @see Preference#setKey(String)
-         */
-        @Override
-        public boolean arePreferenceItemsTheSame(Preference p1, Preference p2) {
-            return p1.getId() == p2.getId();
-        }
-
-        /**
-         * {@inheritDoc}
-         *
-         * <p>The result of this method is only valid for the default {@link Preference} objects,
-         * and custom subclasses which do not override
-         * {@link Preference#onBindViewHolder(PreferenceViewHolder)}. This method also assumes
-         * that if a preference object is being replaced by a new instance, the old instance was
-         * not modified after being removed from its containing {@link PreferenceGroup}.</p>
-         */
-        @Override
-        public boolean arePreferenceContentsTheSame(Preference p1, Preference p2) {
-            if (p1.getClass() != p2.getClass()) {
-                return false;
-            }
-            if (p1 == p2 && p1.wasDetached()) {
-                // Defensively handle the case where a preference was removed, updated and re-added.
-                // Hopefully this is rare.
-                return false;
-            }
-            if (!TextUtils.equals(p1.getTitle(), p2.getTitle())) {
-                return false;
-            }
-            if (!TextUtils.equals(p1.getSummary(), p2.getSummary())) {
-                return false;
-            }
-            final Drawable p1Icon = p1.getIcon();
-            final Drawable p2Icon = p2.getIcon();
-            if (p1Icon != p2Icon && (p1Icon == null || !p1Icon.equals(p2Icon))) {
-                return false;
-            }
-            if (p1.isEnabled() != p2.isEnabled()) {
-                return false;
-            }
-            if (p1.isSelectable() != p2.isSelectable()) {
-                return false;
-            }
-            if (p1 instanceof TwoStatePreference) {
-                if (((TwoStatePreference) p1).isChecked()
-                        != ((TwoStatePreference) p2).isChecked()) {
-                    return false;
-                }
-            }
-            if (p1 instanceof DropDownPreference && p1 != p2) {
-                // Different object, must re-bind spinner adapter
-                return false;
-            }
-
-            return true;
-        }
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when a
-     * {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is
-     * clicked.
-     */
-    public interface OnPreferenceTreeClickListener {
-        /**
-         * Called when a preference in the tree rooted at this
-         * {@link PreferenceScreen} has been clicked.
-         *
-         * @param preference The preference that was clicked.
-         * @return Whether the click was handled.
-         */
-        boolean onPreferenceTreeClick(Preference preference);
-    }
-
-    /**
-     * Interface definition for a class that will be called when a
-     * {@link android.support.v7.preference.Preference} requests to display a dialog.
-     */
-    public interface OnDisplayPreferenceDialogListener {
-
-        /**
-         * Called when a preference in the tree requests to display a dialog.
-         *
-         * @param preference The Preference object requesting the dialog.
-         */
-        void onDisplayPreferenceDialog(Preference preference);
-    }
-
-    /**
-     * Interface definition for a class that will be called when a
-     * {@link android.support.v7.preference.PreferenceScreen} requests navigation.
-     */
-    public interface OnNavigateToScreenListener {
-
-        /**
-         * Called when a PreferenceScreen in the tree requests to navigate to its contents.
-         *
-         * @param preferenceScreen The PreferenceScreen requesting navigation.
-         */
-        void onNavigateToScreen(PreferenceScreen preferenceScreen);
-    }
-
-}
diff --git a/android/support/v7/preference/PreferenceRecyclerViewAccessibilityDelegate.java b/android/support/v7/preference/PreferenceRecyclerViewAccessibilityDelegate.java
deleted file mode 100644
index e42bcd8..0000000
--- a/android/support/v7/preference/PreferenceRecyclerViewAccessibilityDelegate.java
+++ /dev/null
@@ -1,76 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerViewAccessibilityDelegate;
-import android.view.View;
-
-/**
- * The AccessibilityDelegate used by the RecyclerView that displays Views for Preferences.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class PreferenceRecyclerViewAccessibilityDelegate
-        extends RecyclerViewAccessibilityDelegate {
-    final RecyclerView mRecyclerView;
-    final AccessibilityDelegateCompat mDefaultItemDelegate = super.getItemDelegate();
-
-    public PreferenceRecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
-        super(recyclerView);
-        mRecyclerView = recyclerView;
-    }
-
-    @Override
-    public AccessibilityDelegateCompat getItemDelegate() {
-        return mItemDelegate;
-    }
-
-    final AccessibilityDelegateCompat mItemDelegate = new AccessibilityDelegateCompat() {
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-            mDefaultItemDelegate.onInitializeAccessibilityNodeInfo(host, info);
-            int position = mRecyclerView.getChildAdapterPosition(host);
-
-            RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
-            if (!(adapter instanceof PreferenceGroupAdapter)) {
-                return;
-            }
-
-            PreferenceGroupAdapter preferenceGroupAdapter = (PreferenceGroupAdapter) adapter;
-            Preference preference = preferenceGroupAdapter.getItem(position);
-            if (preference == null) {
-                return;
-            }
-
-            preference.onInitializeAccessibilityNodeInfo(info);
-        }
-
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle args) {
-            // Must forward actions since the default delegate will handle actions.
-            return mDefaultItemDelegate.performAccessibilityAction(host, action, args);
-        }
-    };
-}
diff --git a/android/support/v7/preference/PreferenceScreen.java b/android/support/v7/preference/PreferenceScreen.java
deleted file mode 100644
index e29264a..0000000
--- a/android/support/v7/preference/PreferenceScreen.java
+++ /dev/null
@@ -1,140 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.util.AttributeSet;
-
-/**
- * Represents a top-level {@link Preference} that
- * is the root of a Preference hierarchy. A {@link PreferenceFragmentCompat}
- * points to an instance of this class to show the preferences. To instantiate
- * this class, use {@link PreferenceManager#createPreferenceScreen(android.content.Context)}.
- * <ul>
- * This class can appear in two places:
- * <li> When a {@link PreferenceFragmentCompat} points to this, it is used as the root
- * and is not shown (only the contained preferences are shown).
- * <li> When it appears inside another preference hierarchy, it is shown and
- * serves as the gateway to another screen of preferences (either by showing
- * another screen of preferences as a {@link android.app.Dialog} or via a
- * {@link android.content.Context#startActivity(android.content.Intent)} from the
- * {@link Preference#getIntent()}). The children of this {@link PreferenceScreen}
- * are NOT shown in the screen that this {@link PreferenceScreen} is shown in.
- * Instead, a separate screen will be shown when this preference is clicked.
- * </ul>
- * <p>Here's an example XML layout of a PreferenceScreen:</p>
- * <pre>
- &lt;PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:key="first_preferencescreen"&gt;
- &lt;CheckBoxPreference
- android:key="wifi enabled"
- android:title="WiFi" /&gt;
- &lt;PreferenceScreen
- android:key="second_preferencescreen"
- android:title="WiFi settings"&gt;
- &lt;CheckBoxPreference
- android:key="prefer wifi"
- android:title="Prefer WiFi" /&gt;
- ... other preferences here ...
- &lt;/PreferenceScreen&gt;
- &lt;/PreferenceScreen&gt; </pre>
- * <p>
- * In this example, the "first_preferencescreen" will be used as the root of the
- * hierarchy and given to a {@link android.support.v14.preference.PreferenceFragment}
- * or {@link PreferenceFragmentCompat}.
- * The first screen will
- * show preferences "WiFi" (which can be used to quickly enable/disable WiFi)
- * and "WiFi settings". The "WiFi settings" is the "second_preferencescreen" and when
- * clicked will show another screen of preferences such as "Prefer WiFi" (and
- * the other preferences that are children of the "second_preferencescreen" tag).
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about building a settings UI with Preferences,
- * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
- * guide.</p>
- * </div>
- *
- * @see PreferenceCategory
- */
-public final class PreferenceScreen extends PreferenceGroup  {
-
-    private boolean mShouldUseGeneratedIds = true;
-
-    /**
-     * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public PreferenceScreen(Context context, AttributeSet attrs) {
-        super(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceScreenStyle,
-                android.R.attr.preferenceScreenStyle));
-    }
-
-    @Override
-    protected void onClick() {
-        if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) {
-            return;
-        }
-        final PreferenceManager.OnNavigateToScreenListener listener =
-                getPreferenceManager().getOnNavigateToScreenListener();
-        if (listener != null) {
-            listener.onNavigateToScreen(this);
-        }
-    }
-
-    @Override
-    protected boolean isOnSameScreenAsChildren() {
-        return false;
-    }
-
-    /**
-     * See {@link #setShouldUseGeneratedIds(boolean)}
-     * @return {@code true} if the adapter should use the preference IDs generated by
-     *         {@link PreferenceGroup#addPreference(Preference)} as stable item IDs
-     */
-    public boolean shouldUseGeneratedIds() {
-        return mShouldUseGeneratedIds;
-    }
-
-    /**
-     * Set whether the adapter created for this screen should attempt to use the preference IDs
-     * generated by {@link PreferenceGroup#addPreference(Preference)} as stable item IDs. Setting
-     * this to false can suppress unwanted animations if {@link Preference} objects are frequently
-     * removed from and re-added to their containing {@link PreferenceGroup}.
-     * <p>
-     * This method may only be called when the preference screen is not attached to the hierarchy.
-     * <p>
-     * Default value is {@code true}.
-     *
-     * @param shouldUseGeneratedIds {@code true} if the adapter should use the preference ID as a
-     *                                          stable ID, or {@code false} to disable the use of
-     *                                          stable IDs
-     */
-    public void setShouldUseGeneratedIds(boolean shouldUseGeneratedIds) {
-        if (isAttached()) {
-            throw new IllegalStateException("Cannot change the usage of generated IDs while" +
-                    " attached to the preference hierarchy");
-        }
-        mShouldUseGeneratedIds = shouldUseGeneratedIds;
-    }
-}
diff --git a/android/support/v7/preference/PreferenceViewHolder.java b/android/support/v7/preference/PreferenceViewHolder.java
deleted file mode 100644
index 5a6426a..0000000
--- a/android/support/v7/preference/PreferenceViewHolder.java
+++ /dev/null
@@ -1,120 +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.preference;
-
-import android.support.annotation.IdRes;
-import android.support.annotation.RestrictTo;
-import android.support.v7.widget.RecyclerView;
-import android.util.SparseArray;
-import android.view.View;
-
-/**
- * A {@link android.support.v7.widget.RecyclerView.ViewHolder} class which caches views associated
- * with the default {@link Preference} layouts. Cached views can be retrieved by calling
- * {@link #findViewById(int)}.
- */
-public class PreferenceViewHolder extends RecyclerView.ViewHolder {
-    private final SparseArray<View> mCachedViews = new SparseArray<>(4);
-    private boolean mDividerAllowedAbove;
-    private boolean mDividerAllowedBelow;
-
-    /* package */ PreferenceViewHolder(View itemView) {
-        super(itemView);
-
-        // Pre-cache the views that we know in advance we'll want to find
-        mCachedViews.put(android.R.id.title, itemView.findViewById(android.R.id.title));
-        mCachedViews.put(android.R.id.summary, itemView.findViewById(android.R.id.summary));
-        mCachedViews.put(android.R.id.icon, itemView.findViewById(android.R.id.icon));
-        mCachedViews.put(R.id.icon_frame, itemView.findViewById(R.id.icon_frame));
-        mCachedViews.put(AndroidResources.ANDROID_R_ICON_FRAME,
-                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.
-     *
-     * @param id Resource ID of the view to find
-     * @return The view, or null if no view with the requested ID is found.
-     */
-    public View findViewById(@IdRes int id) {
-        final View cachedView = mCachedViews.get(id);
-        if (cachedView != null) {
-            return cachedView;
-        } else {
-            final View v = itemView.findViewById(id);
-            if (v != null) {
-                mCachedViews.put(id, v);
-            }
-            return v;
-        }
-    }
-
-    /**
-     * Dividers are only drawn between items if both items allow it, or above the first and below
-     * the last item if that item allows it.
-     *
-     * @return true if dividers are allowed above this item
-     */
-    public boolean isDividerAllowedAbove() {
-        return mDividerAllowedAbove;
-    }
-
-    /**
-     * Dividers are only drawn between items if both items allow it, or above the first and below
-     * the last item if that item allows it.
-     *
-     * By default, {@link Preference#onBindViewHolder(PreferenceViewHolder)} will set this to the
-     * same value as returned by {@link Preference#isSelectable()}, so that non-selectable items
-     * do not have a divider drawn above them.
-     *
-     * @param allowed false to prevent dividers being drawn above this item
-     */
-    public void setDividerAllowedAbove(boolean allowed) {
-        mDividerAllowedAbove = allowed;
-    }
-
-    /**
-     * Dividers are only drawn between items if both items allow it, or above the first and below
-     * the last item if that item allows it.
-     *
-     * @return true if dividers are allowed below this item
-     */
-    public boolean isDividerAllowedBelow() {
-        return mDividerAllowedBelow;
-    }
-
-    /**
-     * Dividers are only drawn between items if both items allow it, or above the first and below
-     * the last item if that item allows it.
-     *
-     * By default, {@link Preference#onBindViewHolder(PreferenceViewHolder)} will set this to the
-     * same value as returned by {@link Preference#isSelectable()}, so that non-selectable items
-     * do not have a divider drawn below them.
-     *
-     * @param allowed false to prevent dividers being drawn below this item
-     */
-    public void setDividerAllowedBelow(boolean allowed) {
-        mDividerAllowedBelow = allowed;
-    }
-}
diff --git a/android/support/v7/preference/SeekBarPreference.java b/android/support/v7/preference/SeekBarPreference.java
deleted file mode 100644
index 963604c..0000000
--- a/android/support/v7/preference/SeekBarPreference.java
+++ /dev/null
@@ -1,376 +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.preference;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
-
-/**
- * Preference based on android.preference.SeekBarPreference but uses support v7 preference as base.
- * It contains a title and a seekbar and an optional seekbar value TextView. The actual preference
- * layout is customizable by setting {@code android:layout} on the preference widget layout or
- * {@code seekBarPreferenceStyle} attribute.
- * The seekbar within the preference can be defined adjustable or not by setting {@code
- * adjustable} attribute. If adjustable, the preference will be responsive to DPAD left/right keys.
- * Otherwise, it skips those keys.
- * The seekbar value view can be shown or disabled by setting {@code showSeekBarValue} attribute
- * to true or false, respectively.
- * Other SeekBar specific attributes (e.g. {@code title, summary, defaultValue, min, max}) can be
- * set directly on the preference widget layout.
- */
-public class SeekBarPreference extends Preference {
-
-    private int mSeekBarValue;
-    private int mMin;
-    private int mMax;
-    private int mSeekBarIncrement;
-    private boolean mTrackingTouch;
-    private SeekBar mSeekBar;
-    private TextView mSeekBarValueTextView;
-    private boolean mAdjustable; // whether the seekbar should respond to the left/right keys
-    private boolean mShowSeekBarValue; // whether to show the seekbar value TextView next to the bar
-
-    private static final String TAG = "SeekBarPreference";
-
-    /**
-     * Listener reacting to the SeekBar changing value by the user
-     */
-    private OnSeekBarChangeListener mSeekBarChangeListener = new OnSeekBarChangeListener() {
-        @Override
-        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            if (fromUser && !mTrackingTouch) {
-                syncValueInternal(seekBar);
-            }
-        }
-
-        @Override
-        public void onStartTrackingTouch(SeekBar seekBar) {
-            mTrackingTouch = true;
-        }
-
-        @Override
-        public void onStopTrackingTouch(SeekBar seekBar) {
-            mTrackingTouch = false;
-            if (seekBar.getProgress() + mMin != mSeekBarValue) {
-                syncValueInternal(seekBar);
-            }
-        }
-    };
-
-    /**
-     * Listener reacting to the user pressing DPAD left/right keys if {@code
-     * adjustable} attribute is set to true; it transfers the key presses to the SeekBar
-     * to be handled accordingly.
-     */
-    private View.OnKeyListener mSeekBarKeyListener = new View.OnKeyListener() {
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent event) {
-            if (event.getAction() != KeyEvent.ACTION_DOWN) {
-                return false;
-            }
-
-            if (!mAdjustable && (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
-                    || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)) {
-                // Right or left keys are pressed when in non-adjustable mode; Skip the keys.
-                return false;
-            }
-
-            // We don't want to propagate the click keys down to the seekbar view since it will
-            // create the ripple effect for the thumb.
-            if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
-                return false;
-            }
-
-            if (mSeekBar == null) {
-                Log.e(TAG, "SeekBar view is null and hence cannot be adjusted.");
-                return false;
-            }
-            return mSeekBar.onKeyDown(keyCode, event);
-        }
-    };
-
-    public SeekBarPreference(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
-
-        /**
-         * The ordering of these two statements are important. If we want to set max first, we need
-         * to perform the same steps by changing min/max to max/min as following:
-         * mMax = a.getInt(...) and setMin(...).
-         */
-        mMin = a.getInt(R.styleable.SeekBarPreference_min, 0);
-        setMax(a.getInt(R.styleable.SeekBarPreference_android_max, 100));
-        setSeekBarIncrement(a.getInt(R.styleable.SeekBarPreference_seekBarIncrement, 0));
-        mAdjustable = a.getBoolean(R.styleable.SeekBarPreference_adjustable, true);
-        mShowSeekBarValue = a.getBoolean(R.styleable.SeekBarPreference_showSeekBarValue, true);
-        a.recycle();
-    }
-
-    public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public SeekBarPreference(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.seekBarPreferenceStyle);
-    }
-
-    public SeekBarPreference(Context context) {
-        this(context, null);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder view) {
-        super.onBindViewHolder(view);
-        view.itemView.setOnKeyListener(mSeekBarKeyListener);
-        mSeekBar = (SeekBar) view.findViewById(R.id.seekbar);
-        mSeekBarValueTextView = (TextView) view.findViewById(R.id.seekbar_value);
-        if (mShowSeekBarValue) {
-            mSeekBarValueTextView.setVisibility(View.VISIBLE);
-        } else {
-            mSeekBarValueTextView.setVisibility(View.GONE);
-            mSeekBarValueTextView = null;
-        }
-
-        if (mSeekBar == null) {
-            Log.e(TAG, "SeekBar view is null in onBindViewHolder.");
-            return;
-        }
-        mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
-        mSeekBar.setMax(mMax - mMin);
-        // If the increment is not zero, use that. Otherwise, use the default mKeyProgressIncrement
-        // in AbsSeekBar when it's zero. This default increment value is set by AbsSeekBar
-        // after calling setMax. That's why it's important to call setKeyProgressIncrement after
-        // calling setMax() since setMax() can change the increment value.
-        if (mSeekBarIncrement != 0) {
-            mSeekBar.setKeyProgressIncrement(mSeekBarIncrement);
-        } else {
-            mSeekBarIncrement = mSeekBar.getKeyProgressIncrement();
-        }
-
-        mSeekBar.setProgress(mSeekBarValue - mMin);
-        if (mSeekBarValueTextView != null) {
-            mSeekBarValueTextView.setText(String.valueOf(mSeekBarValue));
-        }
-        mSeekBar.setEnabled(isEnabled());
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
-        setValue(restoreValue ? getPersistedInt(mSeekBarValue)
-                : (Integer) defaultValue);
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getInt(index, 0);
-    }
-
-    public void setMin(int min) {
-        if (min > mMax) {
-            min = mMax;
-        }
-        if (min != mMin) {
-            mMin = min;
-            notifyChanged();
-        }
-    }
-
-    public int getMin() {
-        return mMin;
-    }
-
-    public final void setMax(int max) {
-        if (max < mMin) {
-            max = mMin;
-        }
-        if (max != mMax) {
-            mMax = max;
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Returns the amount of increment change via each arrow key click. This value is derived from
-     * user's specified increment value if it's not zero. Otherwise, the default value is picked
-     * from the default mKeyProgressIncrement value in {@link android.widget.AbsSeekBar}.
-     * @return The amount of increment on the SeekBar performed after each user's arrow key press.
-     */
-    public final int getSeekBarIncrement() {
-        return mSeekBarIncrement;
-    }
-
-    /**
-     * Sets the increment amount on the SeekBar for each arrow key press.
-     * @param seekBarIncrement The amount to increment or decrement when the user presses an
-     *                         arrow key.
-     */
-    public final void setSeekBarIncrement(int seekBarIncrement) {
-        if (seekBarIncrement != mSeekBarIncrement) {
-            mSeekBarIncrement =  Math.min(mMax - mMin, Math.abs(seekBarIncrement));
-            notifyChanged();
-        }
-    }
-
-    public int getMax() {
-        return mMax;
-    }
-
-    public void setAdjustable(boolean adjustable) {
-        mAdjustable = adjustable;
-    }
-
-    public boolean isAdjustable() {
-        return mAdjustable;
-    }
-
-    public void setValue(int seekBarValue) {
-        setValueInternal(seekBarValue, true);
-    }
-
-    private void setValueInternal(int seekBarValue, boolean notifyChanged) {
-        if (seekBarValue < mMin) {
-            seekBarValue = mMin;
-        }
-        if (seekBarValue > mMax) {
-            seekBarValue = mMax;
-        }
-
-        if (seekBarValue != mSeekBarValue) {
-            mSeekBarValue = seekBarValue;
-            if (mSeekBarValueTextView != null) {
-                mSeekBarValueTextView.setText(String.valueOf(mSeekBarValue));
-            }
-            persistInt(seekBarValue);
-            if (notifyChanged) {
-                notifyChanged();
-            }
-        }
-    }
-
-    public int getValue() {
-        return mSeekBarValue;
-    }
-
-    /**
-     * Persist the seekBar's seekbar value if callChangeListener
-     * returns true, otherwise set the seekBar's value to the stored value
-     */
-    private void syncValueInternal(SeekBar seekBar) {
-        int seekBarValue = mMin + seekBar.getProgress();
-        if (seekBarValue != mSeekBarValue) {
-            if (callChangeListener(seekBarValue)) {
-                setValueInternal(seekBarValue, false);
-            } else {
-                seekBar.setProgress(mSeekBarValue - mMin);
-            }
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (isPersistent()) {
-            // No need to save instance state since it's persistent
-            return superState;
-        }
-
-        // Save the instance state
-        final SavedState myState = new SavedState(superState);
-        myState.seekBarValue = mSeekBarValue;
-        myState.min = mMin;
-        myState.max = mMax;
-        return myState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in onSaveInstanceState
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        // Restore the instance state
-        SavedState myState = (SavedState) state;
-        super.onRestoreInstanceState(myState.getSuperState());
-        mSeekBarValue = myState.seekBarValue;
-        mMin = myState.min;
-        mMax = myState.max;
-        notifyChanged();
-    }
-
-    /**
-     * SavedState, a subclass of {@link BaseSavedState}, will store the state
-     * of MyPreference, a subclass of Preference.
-     * <p>
-     * It is important to always call through to super methods.
-     */
-    private static class SavedState extends BaseSavedState {
-        int seekBarValue;
-        int min;
-        int max;
-
-        public SavedState(Parcel source) {
-            super(source);
-
-            // Restore the click counter
-            seekBarValue = source.readInt();
-            min = source.readInt();
-            max = source.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-
-            // Save the click counter
-            dest.writeInt(seekBarValue);
-            dest.writeInt(min);
-            dest.writeInt(max);
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @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/android/support/v7/preference/SwitchPreferenceCompat.java b/android/support/v7/preference/SwitchPreferenceCompat.java
deleted file mode 100644
index 6b7b93f..0000000
--- a/android/support/v7/preference/SwitchPreferenceCompat.java
+++ /dev/null
@@ -1,240 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.res.TypedArrayUtils;
-import android.support.v7.widget.SwitchCompat;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.Checkable;
-import android.widget.CompoundButton;
-
-/**
-* A {@link Preference} that provides a two-state toggleable option.
-* <p>
-* This preference will store a boolean into the SharedPreferences.
-*
-* @attr name android:summaryOff
-* @attr name android:summaryOn
-* @attr name android:switchTextOff
-* @attr name android:switchTextOn
-* @attr name android:disableDependentsState
-*/
-public class SwitchPreferenceCompat extends TwoStatePreference {
-    private final Listener mListener = new Listener();
-
-    // Switch text for on and off states
-    private CharSequence mSwitchOn;
-    private CharSequence mSwitchOff;
-
-    private class Listener implements CompoundButton.OnCheckedChangeListener {
-        @Override
-        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            if (!callChangeListener(isChecked)) {
-                // Listener didn't like it, change it back.
-                // CompoundButton will make sure we don't recurse.
-                buttonView.setChecked(!isChecked);
-                return;
-            }
-
-            SwitchPreferenceCompat.this.setChecked(isChecked);
-        }
-    }
-
-    /**
-     * Construct a new SwitchPreference with the given style options.
-     *
-     * @param context The Context that will style this preference
-     * @param attrs Style attributes that differ from the default
-     * @param defStyleAttr 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 A resource identifier of a style resource that
-     *        supplies default values for the view, used only if
-     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
-     *        to not look for defaults.
-     */
-    public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.SwitchPreferenceCompat, defStyleAttr, defStyleRes);
-
-        setSummaryOn(TypedArrayUtils.getString(a, R.styleable.SwitchPreferenceCompat_summaryOn,
-                R.styleable.SwitchPreferenceCompat_android_summaryOn));
-
-        setSummaryOff(TypedArrayUtils.getString(a, R.styleable.SwitchPreferenceCompat_summaryOff,
-                R.styleable.SwitchPreferenceCompat_android_summaryOff));
-
-        setSwitchTextOn(TypedArrayUtils.getString(a,
-                R.styleable.SwitchPreferenceCompat_switchTextOn,
-                R.styleable.SwitchPreferenceCompat_android_switchTextOn));
-
-        setSwitchTextOff(TypedArrayUtils.getString(a,
-                R.styleable.SwitchPreferenceCompat_switchTextOff,
-                R.styleable.SwitchPreferenceCompat_android_switchTextOff));
-
-        setDisableDependentsState(TypedArrayUtils.getBoolean(a,
-                R.styleable.SwitchPreferenceCompat_disableDependentsState,
-                R.styleable.SwitchPreferenceCompat_android_disableDependentsState, false));
-
-        a.recycle();
-    }
-
-    /**
-     * Construct a new SwitchPreference with the given style options.
-     *
-     * @param context The Context that will style this preference
-     * @param attrs Style attributes that differ from the default
-     * @param defStyleAttr 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 SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    /**
-     * Construct a new SwitchPreference with the given style options.
-     *
-     * @param context The Context that will style this preference
-     * @param attrs Style attributes that differ from the default
-     */
-    public SwitchPreferenceCompat(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.switchPreferenceCompatStyle);
-    }
-
-    /**
-     * Construct a new SwitchPreference with default style options.
-     *
-     * @param context The Context that will style this preference
-     */
-    public SwitchPreferenceCompat(Context context) {
-        this(context, null);
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder holder) {
-        super.onBindViewHolder(holder);
-        View switchView = holder.findViewById(R.id.switchWidget);
-        syncSwitchView(switchView);
-        syncSummaryView(holder);
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the on state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param onText Text to display in the on state
-     */
-    public void setSwitchTextOn(CharSequence onText) {
-        mSwitchOn = onText;
-        notifyChanged();
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the off state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param offText Text to display in the off state
-     */
-    public void setSwitchTextOff(CharSequence offText) {
-        mSwitchOff = offText;
-        notifyChanged();
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the on state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param resId The text as a string resource ID
-     */
-    public void setSwitchTextOn(int resId) {
-        setSwitchTextOn(getContext().getString(resId));
-    }
-
-    /**
-     * Set the text displayed on the switch widget in the off state.
-     * This should be a very short string; one word if possible.
-     *
-     * @param resId The text as a string resource ID
-     */
-    public void setSwitchTextOff(int resId) {
-        setSwitchTextOff(getContext().getString(resId));
-    }
-
-    /**
-     * @return The text that will be displayed on the switch widget in the on state
-     */
-    public CharSequence getSwitchTextOn() {
-        return mSwitchOn;
-    }
-
-    /**
-     * @return The text that will be displayed on the switch widget in the off state
-     */
-    public CharSequence getSwitchTextOff() {
-        return mSwitchOff;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    protected void performClick(View view) {
-        super.performClick(view);
-        syncViewIfAccessibilityEnabled(view);
-    }
-
-    private void syncViewIfAccessibilityEnabled(View view) {
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (!accessibilityManager.isEnabled()) {
-            return;
-        }
-
-        View switchView = view.findViewById(R.id.switchWidget);
-        syncSwitchView(switchView);
-
-        View summaryView = view.findViewById(android.R.id.summary);
-        syncSummaryView(summaryView);
-    }
-
-    private void syncSwitchView(View view) {
-        if (view instanceof SwitchCompat) {
-            final SwitchCompat switchView = (SwitchCompat) view;
-            switchView.setOnCheckedChangeListener(null);
-        }
-        if (view instanceof Checkable) {
-            ((Checkable) view).setChecked(mChecked);
-        }
-        if (view instanceof SwitchCompat) {
-            final SwitchCompat switchView = (SwitchCompat) view;
-            switchView.setTextOn(mSwitchOn);
-            switchView.setTextOff(mSwitchOff);
-            switchView.setOnCheckedChangeListener(mListener);
-        }
-    }
-}
diff --git a/android/support/v7/preference/TwoStatePreference.java b/android/support/v7/preference/TwoStatePreference.java
deleted file mode 100644
index 537c88a..0000000
--- a/android/support/v7/preference/TwoStatePreference.java
+++ /dev/null
@@ -1,294 +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.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * Common base class for preferences that have two selectable states, persist a
- * boolean value in SharedPreferences, and may have dependent preferences that are
- * enabled/disabled based on the current state.
- */
-public abstract class TwoStatePreference extends Preference {
-
-    private CharSequence mSummaryOn;
-    private CharSequence mSummaryOff;
-    protected boolean mChecked;
-    private boolean mCheckedSet;
-    private boolean mDisableDependentsState;
-
-    public TwoStatePreference(
-            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public TwoStatePreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TwoStatePreference(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TwoStatePreference(Context context) {
-        this(context, null);
-    }
-
-    @Override
-    protected void onClick() {
-        super.onClick();
-
-        final boolean newValue = !isChecked();
-        if (callChangeListener(newValue)) {
-            setChecked(newValue);
-        }
-    }
-
-    /**
-     * Sets the checked state and saves it to the {@link android.content.SharedPreferences}.
-     *
-     * @param checked The checked state.
-     */
-    public void setChecked(boolean checked) {
-        // Always persist/notify the first time; don't assume the field's default of false.
-        final boolean changed = mChecked != checked;
-        if (changed || !mCheckedSet) {
-            mChecked = checked;
-            mCheckedSet = true;
-            persistBoolean(checked);
-            if (changed) {
-                notifyDependencyChange(shouldDisableDependents());
-                notifyChanged();
-            }
-        }
-    }
-
-    /**
-     * Returns the checked state.
-     *
-     * @return The checked state.
-     */
-    public boolean isChecked() {
-        return mChecked;
-    }
-
-    @Override
-    public boolean shouldDisableDependents() {
-        boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
-        return shouldDisable || super.shouldDisableDependents();
-    }
-
-    /**
-     * Sets the summary to be shown when checked.
-     *
-     * @param summary The summary to be shown when checked.
-     */
-    public void setSummaryOn(CharSequence summary) {
-        mSummaryOn = summary;
-        if (isChecked()) {
-            notifyChanged();
-        }
-    }
-
-    /**
-     * @see #setSummaryOn(CharSequence)
-     * @param summaryResId The summary as a resource.
-     */
-    public void setSummaryOn(int summaryResId) {
-        setSummaryOn(getContext().getString(summaryResId));
-    }
-
-    /**
-     * Returns the summary to be shown when checked.
-     * @return The summary.
-     */
-    public CharSequence getSummaryOn() {
-        return mSummaryOn;
-    }
-
-    /**
-     * Sets the summary to be shown when unchecked.
-     *
-     * @param summary The summary to be shown when unchecked.
-     */
-    public void setSummaryOff(CharSequence summary) {
-        mSummaryOff = summary;
-        if (!isChecked()) {
-            notifyChanged();
-        }
-    }
-
-    /**
-     * @see #setSummaryOff(CharSequence)
-     * @param summaryResId The summary as a resource.
-     */
-    public void setSummaryOff(int summaryResId) {
-        setSummaryOff(getContext().getString(summaryResId));
-    }
-
-    /**
-     * Returns the summary to be shown when unchecked.
-     * @return The summary.
-     */
-    public CharSequence getSummaryOff() {
-        return mSummaryOff;
-    }
-
-    /**
-     * Returns whether dependents are disabled when this preference is on ({@code true})
-     * or when this preference is off ({@code false}).
-     *
-     * @return Whether dependents are disabled when this preference is on ({@code true})
-     *         or when this preference is off ({@code false}).
-     */
-    public boolean getDisableDependentsState() {
-        return mDisableDependentsState;
-    }
-
-    /**
-     * Sets whether dependents are disabled when this preference is on ({@code true})
-     * or when this preference is off ({@code false}).
-     *
-     * @param disableDependentsState The preference state that should disable dependents.
-     */
-    public void setDisableDependentsState(boolean disableDependentsState) {
-        mDisableDependentsState = disableDependentsState;
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getBoolean(index, false);
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
-        setChecked(restoreValue ? getPersistedBoolean(mChecked)
-                : (Boolean) defaultValue);
-    }
-
-    /**
-     * Sync a summary holder contained within holder's subhierarchy with the correct summary text.
-     * @param holder PreferenceViewHolder which holds a reference to the summary view
-     */
-    protected void syncSummaryView(PreferenceViewHolder holder) {
-        // Sync the summary holder
-        View view = holder.findViewById(android.R.id.summary);
-        syncSummaryView(view);
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected void syncSummaryView(View view) {
-        if (!(view instanceof TextView)) {
-            return;
-        }
-        TextView summaryView = (TextView) view;
-        boolean useDefaultSummary = true;
-        if (mChecked && !TextUtils.isEmpty(mSummaryOn)) {
-            summaryView.setText(mSummaryOn);
-            useDefaultSummary = false;
-        } else if (!mChecked && !TextUtils.isEmpty(mSummaryOff)) {
-            summaryView.setText(mSummaryOff);
-            useDefaultSummary = false;
-        }
-        if (useDefaultSummary) {
-            final CharSequence summary = getSummary();
-            if (!TextUtils.isEmpty(summary)) {
-                summaryView.setText(summary);
-                useDefaultSummary = false;
-            }
-        }
-        int newVisibility = View.GONE;
-        if (!useDefaultSummary) {
-            // Someone has written to it
-            newVisibility = View.VISIBLE;
-        }
-        if (newVisibility != summaryView.getVisibility()) {
-            summaryView.setVisibility(newVisibility);
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (isPersistent()) {
-            // No need to save instance state since it's persistent
-            return superState;
-        }
-
-        final SavedState myState = new SavedState(superState);
-        myState.checked = isChecked();
-        return myState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state == null || !state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in onSaveInstanceState
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        SavedState myState = (SavedState) state;
-        super.onRestoreInstanceState(myState.getSuperState());
-        setChecked(myState.checked);
-    }
-
-    static class SavedState extends BaseSavedState {
-        boolean checked;
-
-        public SavedState(Parcel source) {
-            super(source);
-            checked = source.readInt() == 1;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(checked ? 1 : 0);
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        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/android/support/v7/preference/UnPressableLinearLayout.java b/android/support/v7/preference/UnPressableLinearLayout.java
deleted file mode 100644
index 1524d31..0000000
--- a/android/support/v7/preference/UnPressableLinearLayout.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.v7.preference;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-
-/**
- * Custom LinearLayout that does not propagate the pressed state down to its children.
- * By default, the pressed state is propagated to all the children that are not clickable
- * or long-clickable.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class UnPressableLinearLayout extends LinearLayout {
-    public UnPressableLinearLayout(Context context) {
-        this(context, null);
-    }
-
-    public UnPressableLinearLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void dispatchSetPressed(boolean pressed) {
-        // Skip dispatching the pressed key state to the children so that they don't trigger any
-        // pressed state animation on their stateful drawables.
-    }
-}
diff --git a/android/support/v7/preference/internal/AbstractMultiSelectListPreference.java b/android/support/v7/preference/internal/AbstractMultiSelectListPreference.java
deleted file mode 100644
index 64d02ef..0000000
--- a/android/support/v7/preference/internal/AbstractMultiSelectListPreference.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.v7.preference.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.preference.DialogPreference;
-import android.util.AttributeSet;
-
-import java.util.Set;
-
-/**
- * Stub superclass for {@link android.support.v14.preference.MultiSelectListPreference} so that we
- * can reference it from
- * {@link android.support.v7.preference.MultiSelectListPreferenceDialogFragmentCompat}
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public abstract class AbstractMultiSelectListPreference extends DialogPreference {
-    public AbstractMultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public AbstractMultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public AbstractMultiSelectListPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public AbstractMultiSelectListPreference(Context context) {
-        super(context);
-    }
-
-    public abstract CharSequence[] getEntries();
-    public abstract Set<String> getValues();
-    public abstract CharSequence[] getEntryValues();
-    public abstract void setValues(Set<String> values);
-}
diff --git a/android/support/v7/preference/internal/package-info.java b/android/support/v7/preference/internal/package-info.java
deleted file mode 100644
index b110d3a..0000000
--- a/android/support/v7/preference/internal/package-info.java
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-package android.support.v7.preference.internal;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
\ No newline at end of file
diff --git a/android/support/v7/recyclerview/extensions/DiffCallback.java b/android/support/v7/recyclerview/extensions/DiffCallback.java
deleted file mode 100644
index 70fc049..0000000
--- a/android/support/v7/recyclerview/extensions/DiffCallback.java
+++ /dev/null
@@ -1,65 +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.v7.recyclerview.extensions;
-
-import android.arch.paging.PagedListAdapterHelper;
-import android.support.annotation.NonNull;
-
-/**
- * Callback that informs {@link PagedListAdapterHelper} 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.
- * <p>
- * Note that this class is likely to move prior to the final release of the Paging library.
- *
- * @param <T> Type of items to compare.
- */
-public abstract class DiffCallback<T> {
-    /**
-     * 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.
-     * @see android.support.v7.util.DiffUtil.Callback#areItemsTheSame(int, int)
-     */
-    public abstract boolean areItemsTheSame(@NonNull T oldItem, @NonNull T 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.
-     * @see android.support.v7.util.DiffUtil.Callback#areContentsTheSame(int, int)
-     */
-    public abstract boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem);
-
-    /**
-     * Called to get a change payload between an old and new version of an item.
-     *
-     * @see android.support.v7.util.DiffUtil.Callback#getChangePayload(int, int)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public Object getChangePayload(@NonNull T oldItem, @NonNull T newItem) {
-        return null;
-    }
-}
diff --git a/android/support/v7/recyclerview/extensions/ListAdapter.java b/android/support/v7/recyclerview/extensions/ListAdapter.java
deleted file mode 100644
index 721e0da..0000000
--- a/android/support/v7/recyclerview/extensions/ListAdapter.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.recyclerview.extensions;
-
-import android.support.annotation.NonNull;
-import android.support.v7.util.AdapterListUpdateCallback;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.widget.RecyclerView;
-
-import java.util.List;
-
-/**
- * {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting List data in a
- * {@link RecyclerView}, including computing diffs between Lists on a background thread.
- * <p>
- * This class is a convenience wrapper around ListAdapterHelper that implements common default
- * behavior for item access and counting.
- * <p>
- * While using a LiveData&lt;List> is an easy way to provide data to the adapter, it isn't required
- * - you can use {@link #setList(List)} when new lists are available.
- * <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")
- *     public abstract LiveData&lt;List&lt;User>> usersByLastName();
- * }
- *
- * class MyViewModel extends ViewModel {
- *     public final LiveData&lt;List&lt;User>> usersList;
- *     public MyViewModel(UserDao userDao) {
- *         usersList = userDao.usersByLastName();
- *     }
- * }
- *
- * class MyActivity extends AppCompatActivity {
- *     {@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();
- *         viewModel.usersList.observe(this, list -> adapter.setList(list));
- *         recyclerView.setAdapter(adapter);
- *     }
- * }
- *
- * class UserAdapter extends ListAdapter&lt;User, UserViewHolder> {
- *     public UserAdapter() {
- *         super(User.DIFF_CALLBACK);
- *     }
- *     {@literal @}Override
- *     public void onBindViewHolder(UserViewHolder holder, int position) {
- *         holder.bindTo(getItem(position));
- *     }
- *     public static final DiffUtil.ItemCallback&lt;User> DIFF_CALLBACK =
- *             new DiffUtil.ItemCallback&lt;User>() {
- *         {@literal @}Override
- *         public boolean areItemsTheSame(
- *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *             // User properties may have changed if reloaded from the DB, but ID is fixed
- *             return oldUser.getId() == newUser.getId();
- *         }
- *         {@literal @}Override
- *         public boolean areContentsTheSame(
- *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *             // NOTE: if you use equals, your object must properly override Object#equals()
- *             // Incorrectly returning false here will result in too many animations.
- *             return oldUser.equals(newUser);
- *         }
- *     }
- * }</pre>
- *
- * Advanced users that wish for more control over adapter behavior, or to provide a specific base
- * class should refer to {@link ListAdapterHelper}, which provides custom mapping from diff events
- * to adapter positions.
- *
- * @param <T> Type of the Lists this Adapter will receive.
- * @param <VH> A class that extends ViewHolder that will be used by the adapter.
- */
-public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
-        extends RecyclerView.Adapter<VH> {
-    private final ListAdapterHelper<T> mHelper;
-
-    @SuppressWarnings("unused")
-    protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
-        mHelper = new ListAdapterHelper<>(new AdapterListUpdateCallback(this),
-                new ListAdapterConfig.Builder<>(diffCallback).build());
-    }
-
-    @SuppressWarnings("unused")
-    protected ListAdapter(@NonNull ListAdapterConfig<T> config) {
-        mHelper = new ListAdapterHelper<>(new AdapterListUpdateCallback(this), config);
-    }
-
-    /**
-     * Set the new list to be displayed.
-     * <p>
-     * If a list is already being displayed, a diff will be computed on a background thread, which
-     * will dispatch Adapter.notifyItem events on the main thread.
-     *
-     * @param list The new list to be displayed.
-     */
-    public void setList(List<T> list) {
-        mHelper.setList(list);
-    }
-
-    @SuppressWarnings("unused")
-    protected T getItem(int position) {
-        return mHelper.getItem(position);
-    }
-
-    @Override
-    public int getItemCount() {
-        return mHelper.getItemCount();
-    }
-}
diff --git a/android/support/v7/recyclerview/extensions/ListAdapterConfig.java b/android/support/v7/recyclerview/extensions/ListAdapterConfig.java
deleted file mode 100644
index 53fe4bb..0000000
--- a/android/support/v7/recyclerview/extensions/ListAdapterConfig.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.recyclerview.extensions;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v7.util.DiffUtil;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-/**
- * Configuration object for {@link ListAdapter}, {@link ListAdapterHelper}, and similar
- * background-thread list diffing adapter logic.
- * <p>
- * At minimum, defines item diffing behavior with a {@link DiffUtil.ItemCallback}, used to compute
- * item differences to pass to a RecyclerView adapter.
- *
- * @param <T> Type of items in the lists, and being compared.
- */
-public final class ListAdapterConfig<T> {
-    @NonNull
-    private final Executor mMainThreadExecutor;
-    @NonNull
-    private final Executor mBackgroundThreadExecutor;
-    @NonNull
-    private final DiffUtil.ItemCallback<T> mDiffCallback;
-
-    private ListAdapterConfig(
-            @NonNull Executor mainThreadExecutor,
-            @NonNull Executor backgroundThreadExecutor,
-            @NonNull DiffUtil.ItemCallback<T> diffCallback) {
-        mMainThreadExecutor = mainThreadExecutor;
-        mBackgroundThreadExecutor = backgroundThreadExecutor;
-        mDiffCallback = diffCallback;
-    }
-
-    /** @hide */
-    @SuppressWarnings("WeakerAccess")
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Executor getMainThreadExecutor() {
-        return mMainThreadExecutor;
-    }
-
-    /** @hide */
-    @SuppressWarnings("WeakerAccess")
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    @NonNull
-    public Executor getBackgroundThreadExecutor() {
-        return mBackgroundThreadExecutor;
-    }
-
-    @SuppressWarnings("WeakerAccess")
-    @NonNull
-    public DiffUtil.ItemCallback<T> getDiffCallback() {
-        return mDiffCallback;
-    }
-
-    /**
-     * Builder class for {@link ListAdapterConfig}.
-     *
-     * @param <T>
-     */
-    public static class Builder<T> {
-        private Executor mMainThreadExecutor;
-        private Executor mBackgroundThreadExecutor;
-        private final DiffUtil.ItemCallback<T> mDiffCallback;
-
-        public Builder(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
-            mDiffCallback = diffCallback;
-        }
-
-        /**
-         * If provided, defines the main thread executor used to dispatch adapter update
-         * notifications on the main thread.
-         * <p>
-         * If not provided, it will default to the main thread.
-         *
-         * @param executor The executor which can run tasks in the UI thread.
-         * @return this
-         *
-         * @hide
-         */
-        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-        @NonNull
-        public Builder<T> setMainThreadExecutor(Executor executor) {
-            mMainThreadExecutor = executor;
-            return this;
-        }
-
-        /**
-         * If provided, defines the background executor used to calculate the diff between an old
-         * and a new list.
-         * <p>
-         * If not provided, defaults to two thread pool executor, shared by all ListAdapterConfigs.
-         *
-         * @param executor The background executor to run list diffing.
-         * @return this
-         */
-        @SuppressWarnings({"unused", "WeakerAccess"})
-        @NonNull
-        public Builder<T> setBackgroundThreadExecutor(Executor executor) {
-            mBackgroundThreadExecutor = executor;
-            return this;
-        }
-
-        private static class MainThreadExecutor implements Executor {
-            final Handler mHandler = new Handler(Looper.getMainLooper());
-            @Override
-            public void execute(@NonNull Runnable command) {
-                mHandler.post(command);
-            }
-        }
-
-        /**
-         * Creates a {@link ListAdapterHelper} with the given parameters.
-         *
-         * @return A new ListAdapterConfig.
-         */
-        @NonNull
-        public ListAdapterConfig<T> build() {
-            if (mMainThreadExecutor == null) {
-                mMainThreadExecutor = sMainThreadExecutor;
-            }
-            if (mBackgroundThreadExecutor == null) {
-                synchronized (sExecutorLock) {
-                    if (sDiffExecutor == null) {
-                        sDiffExecutor = Executors.newFixedThreadPool(2);
-                    }
-                }
-                mBackgroundThreadExecutor = sDiffExecutor;
-            }
-            return new ListAdapterConfig<>(
-                    mMainThreadExecutor,
-                    mBackgroundThreadExecutor,
-                    mDiffCallback);
-        }
-
-        // TODO: remove the below once supportlib has its own appropriate executors
-        private static final Object sExecutorLock = new Object();
-        private static Executor sDiffExecutor = null;
-
-        // TODO: use MainThreadExecutor from supportlib once one exists
-        private static final Executor sMainThreadExecutor = new MainThreadExecutor();
-    }
-}
diff --git a/android/support/v7/recyclerview/extensions/ListAdapterHelper.java b/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
deleted file mode 100644
index bb231b1..0000000
--- a/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.recyclerview.extensions;
-
-import android.support.annotation.NonNull;
-import android.support.v7.util.AdapterListUpdateCallback;
-import android.support.v7.util.DiffUtil;
-import android.support.v7.util.ListUpdateCallback;
-import android.support.v7.widget.RecyclerView;
-
-import java.util.List;
-
-/**
- * Helper object for displaying a List in
- * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, which signals the
- * adapter of changes when the List is changed by computing changes with DiffUtil in the
- * background.
- * <p>
- * For simplicity, the {@link ListAdapter} wrapper class can often be used instead of the
- * helper directly. This helper class is exposed for complex cases, and where overriding an adapter
- * base class to support List diffing isn't convenient.
- * <p>
- * The ListAdapterHelper can consume the values from a LiveData of <code>List</code> and present the
- * data simply for an adapter. It computes differences in List contents via {@link DiffUtil} on a
- * background thread as new <code>List</code>s are received.
- * <p>
- * It provides a simple list-like API with {@link #getItem(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")
- *     public abstract LiveData&lt;List&lt;User>> usersByLastName();
- * }
- *
- * class MyViewModel extends ViewModel {
- *     public final LiveData&lt;List&lt;User>> usersList;
- *     public MyViewModel(UserDao userDao) {
- *         usersList = userDao.usersByLastName();
- *     }
- * }
- *
- * class MyActivity extends AppCompatActivity {
- *     {@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();
- *         viewModel.usersList.observe(this, list -> adapter.setList(list));
- *         recyclerView.setAdapter(adapter);
- *     }
- * }
- *
- * class UserAdapter extends RecyclerView.Adapter&lt;UserViewHolder> {
- *     private final ListAdapterHelper&lt;User> mHelper =
- *             new ListAdapterHelper(this, DIFF_CALLBACK);
- *     {@literal @}Override
- *     public int getItemCount() {
- *         return mHelper.getItemCount();
- *     }
- *     public void setList(List&lt;User> list) {
- *         mHelper.setList(list);
- *     }
- *     {@literal @}Override
- *     public void onBindViewHolder(UserViewHolder holder, int position) {
- *         User user = mHelper.getItem(position);
- *         holder.bindTo(user);
- *     }
- *     public static final DiffUtil.ItemCallback&lt;User> DIFF_CALLBACK
- *             = new DiffUtil.ItemCallback&lt;User>() {
- *         {@literal @}Override
- *         public boolean areItemsTheSame(
- *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *             // User properties may have changed if reloaded from the DB, but ID is fixed
- *             return oldUser.getId() == newUser.getId();
- *         }
- *         {@literal @}Override
- *         public boolean areContentsTheSame(
- *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
- *             // NOTE: if you use equals, your object must properly override Object#equals()
- *             // Incorrectly returning false here will result in too many animations.
- *             return oldUser.equals(newUser);
- *         }
- *     }
- * }</pre>
- *
- * @param <T> Type of the lists this helper will receive.
- */
-public class ListAdapterHelper<T> {
-    private final ListUpdateCallback mUpdateCallback;
-    private final ListAdapterConfig<T> mConfig;
-
-    /**
-     * Convenience for
-     * {@code PagedListAdapterHelper(new AdapterListUpdateCallback(adapter),
-     * new ListAdapterConfig.Builder().setDiffCallback(diffCallback).build());}
-     *
-     * @param adapter Adapter to dispatch position updates to.
-     * @param diffCallback ItemCallback that compares items to dispatch appropriate animations when
-     *
-     * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
-     */
-    public ListAdapterHelper(@NonNull RecyclerView.Adapter adapter,
-            @NonNull DiffUtil.ItemCallback<T> diffCallback) {
-        mUpdateCallback = new AdapterListUpdateCallback(adapter);
-        mConfig = new ListAdapterConfig.Builder<>(diffCallback).build();
-    }
-
-    /**
-     * Create a ListAdapterHelper with the provided config, and ListUpdateCallback to dispatch
-     * updates to.
-     *
-     * @param listUpdateCallback Callback to dispatch updates to.
-     * @param config Config to define background work Executor, and DiffUtil.ItemCallback for
-     *               computing List diffs.
-     *
-     * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
-     */
-    @SuppressWarnings("WeakerAccess")
-    public ListAdapterHelper(@NonNull ListUpdateCallback listUpdateCallback,
-            @NonNull ListAdapterConfig<T> config) {
-        mUpdateCallback = listUpdateCallback;
-        mConfig = config;
-    }
-
-    private List<T> mList;
-
-    // Max generation of currently scheduled runnable
-    private int mMaxScheduledGeneration;
-
-
-    /**
-     * Get the item from the current List at the specified index.
-     *
-     * @param index Index of item to get, must be >= 0, and &lt; {@link #getItemCount()}.
-     * @return The item at the specified List position.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public T getItem(int index) {
-        if (mList == null) {
-            throw new IndexOutOfBoundsException("Item count is zero, getItem() call is invalid");
-        }
-
-        return mList.get(index);
-    }
-
-    /**
-     * Get the number of items currently presented by this AdapterHelper. This value can be directly
-     * returned to {@link android.support.v7.widget.RecyclerView.Adapter#getItemCount()
-     * RecyclerView.Adapter.getItemCount()}.
-     *
-     * @return Number of items being presented.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public int getItemCount() {
-        return mList == null ? 0 : mList.size();
-    }
-
-    /**
-     * Pass a new List to the AdapterHelper. Adapter updates will be computed on a background
-     * thread.
-     * <p>
-     * If a List is already present, a diff will be computed asynchronously on a background thread.
-     * When the diff is computed, it will be applied (dispatched to the {@link ListUpdateCallback}),
-     * and the new List will be swapped in.
-     *
-     * @param newList The new List.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void setList(final List<T> newList) {
-        if (newList == mList) {
-            // nothing to do
-            return;
-        }
-
-        // incrementing generation means any currently-running diffs are discarded when they finish
-        final int runGeneration = ++mMaxScheduledGeneration;
-
-        if (newList == null) {
-            mUpdateCallback.onRemoved(0, mList.size());
-            mList = null;
-            return;
-        }
-
-        if (mList == null) {
-            // fast simple first insert
-            mUpdateCallback.onInserted(0, newList.size());
-            mList = newList;
-            return;
-        }
-
-        final List<T> oldList = mList;
-        mConfig.getBackgroundThreadExecutor().execute(new Runnable() {
-            @Override
-            public void run() {
-                final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
-                    @Override
-                    public int getOldListSize() {
-                        return oldList.size();
-                    }
-
-                    @Override
-                    public int getNewListSize() {
-                        return newList.size();
-                    }
-
-                    @Override
-                    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
-                        return mConfig.getDiffCallback().areItemsTheSame(
-                                oldList.get(oldItemPosition), newList.get(newItemPosition));
-                    }
-
-                    @Override
-                    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
-                        return mConfig.getDiffCallback().areContentsTheSame(
-                                oldList.get(oldItemPosition), newList.get(newItemPosition));
-                    }
-                });
-
-                mConfig.getMainThreadExecutor().execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mMaxScheduledGeneration == runGeneration) {
-                            latchList(newList, result);
-                        }
-                    }
-                });
-            }
-        });
-    }
-
-    private void latchList(List<T> newList, DiffUtil.DiffResult diffResult) {
-        diffResult.dispatchUpdatesTo(mUpdateCallback);
-        mList = newList;
-    }
-}
diff --git a/android/support/v7/text/AllCapsTransformationMethod.java b/android/support/v7/text/AllCapsTransformationMethod.java
deleted file mode 100644
index 1f670a5..0000000
--- a/android/support/v7/text/AllCapsTransformationMethod.java
+++ /dev/null
@@ -1,49 +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.text;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.annotation.RestrictTo;
-import android.text.method.TransformationMethod;
-import android.view.View;
-
-import java.util.Locale;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class AllCapsTransformationMethod implements TransformationMethod {
-    private Locale mLocale;
-
-    public AllCapsTransformationMethod(Context context) {
-        mLocale = context.getResources().getConfiguration().locale;
-    }
-
-    @Override
-    public CharSequence getTransformation(CharSequence source, View view) {
-        return source != null ? source.toString().toUpperCase(mLocale) : null;
-    }
-
-    @Override
-    public void onFocusChanged(View view, CharSequence sourceText, boolean focused,
-            int direction, Rect previouslyFocusedRect) {
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/util/AdapterListUpdateCallback.java b/android/support/v7/util/AdapterListUpdateCallback.java
deleted file mode 100644
index f86ba7d..0000000
--- a/android/support/v7/util/AdapterListUpdateCallback.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.util;
-
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * ListUpdateCallback that dispatches update events to the given adapter.
- *
- * @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
- */
-public final class AdapterListUpdateCallback implements ListUpdateCallback {
-    @NonNull
-    private final RecyclerView.Adapter mAdapter;
-
-    /**
-     * Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.
-     *
-     * @param adapter The Adapter to send updates to.
-     */
-    public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {
-        mAdapter = adapter;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onInserted(int position, int count) {
-        mAdapter.notifyItemRangeInserted(position, count);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onRemoved(int position, int count) {
-        mAdapter.notifyItemRangeRemoved(position, count);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onMoved(int fromPosition, int toPosition) {
-        mAdapter.notifyItemMoved(fromPosition, toPosition);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void onChanged(int position, int count, Object payload) {
-        mAdapter.notifyItemRangeChanged(position, count, payload);
-    }
-}
diff --git a/android/support/v7/util/AsyncListUtil.java b/android/support/v7/util/AsyncListUtil.java
deleted file mode 100644
index bb31eab..0000000
--- a/android/support/v7/util/AsyncListUtil.java
+++ /dev/null
@@ -1,592 +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.util;
-
-import android.support.annotation.UiThread;
-import android.support.annotation.WorkerThread;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-
-/**
- * A utility class that supports asynchronous content loading.
- * <p>
- * It can be used to load Cursor data in chunks without querying the Cursor on the UI Thread while
- * keeping UI and cache synchronous for better user experience.
- * <p>
- * It loads the data on a background thread and keeps only a limited number of fixed sized
- * chunks in memory at all times.
- * <p>
- * {@link AsyncListUtil} queries the currently visible range through {@link ViewCallback},
- * loads the required data items in the background through {@link DataCallback}, and notifies a
- * {@link ViewCallback} when the data is loaded. It may load some extra items for smoother
- * scrolling.
- * <p>
- * Note that this class uses a single thread to load the data, so it suitable to load data from
- * secondary storage such as disk, but not from network.
- * <p>
- * This class is designed to work with {@link android.support.v7.widget.RecyclerView}, but it does
- * not depend on it and can be used with other list views.
- *
- */
-public class AsyncListUtil<T> {
-    static final String TAG = "AsyncListUtil";
-
-    static final boolean DEBUG = false;
-
-    final Class<T> mTClass;
-    final int mTileSize;
-    final DataCallback<T> mDataCallback;
-    final ViewCallback mViewCallback;
-
-    final TileList<T> mTileList;
-
-    final ThreadUtil.MainThreadCallback<T> mMainThreadProxy;
-    final ThreadUtil.BackgroundCallback<T> mBackgroundProxy;
-
-    final int[] mTmpRange = new int[2];
-    final int[] mPrevRange = new int[2];
-    final int[] mTmpRangeExtended = new int[2];
-
-    boolean mAllowScrollHints;
-    private int mScrollHint = ViewCallback.HINT_SCROLL_NONE;
-
-    int mItemCount = 0;
-
-    int mDisplayedGeneration = 0;
-    int mRequestedGeneration = mDisplayedGeneration;
-
-    final SparseIntArray mMissingPositions = new SparseIntArray();
-
-    void log(String s, Object... args) {
-        Log.d(TAG, "[MAIN] " + String.format(s, args));
-    }
-
-    /**
-     * Creates an AsyncListUtil.
-     *
-     * @param klass Class of the data item.
-     * @param tileSize Number of item per chunk loaded at once.
-     * @param dataCallback Data access callback.
-     * @param viewCallback Callback for querying visible item range and update notifications.
-     */
-    public AsyncListUtil(Class<T> klass, int tileSize, DataCallback<T> dataCallback,
-                         ViewCallback viewCallback) {
-        mTClass = klass;
-        mTileSize = tileSize;
-        mDataCallback = dataCallback;
-        mViewCallback = viewCallback;
-
-        mTileList = new TileList<T>(mTileSize);
-
-        ThreadUtil<T> threadUtil = new MessageThreadUtil<T>();
-        mMainThreadProxy = threadUtil.getMainThreadProxy(mMainThreadCallback);
-        mBackgroundProxy = threadUtil.getBackgroundProxy(mBackgroundCallback);
-
-        refresh();
-    }
-
-    private boolean isRefreshPending() {
-        return mRequestedGeneration != mDisplayedGeneration;
-    }
-
-    /**
-     * Updates the currently visible item range.
-     *
-     * <p>
-     * Identifies the data items that have not been loaded yet and initiates loading them in the
-     * background. Should be called from the view's scroll listener (such as
-     * {@link android.support.v7.widget.RecyclerView.OnScrollListener#onScrolled}).
-     */
-    public void onRangeChanged() {
-        if (isRefreshPending()) {
-            return;  // Will update range will the refresh result arrives.
-        }
-        updateRange();
-        mAllowScrollHints = true;
-    }
-
-    /**
-     * Forces reloading the data.
-     * <p>
-     * Discards all the cached data and reloads all required data items for the currently visible
-     * range. To be called when the data item count and/or contents has changed.
-     */
-    public void refresh() {
-        mMissingPositions.clear();
-        mBackgroundProxy.refresh(++mRequestedGeneration);
-    }
-
-    /**
-     * Returns the data item at the given position or <code>null</code> if it has not been loaded
-     * yet.
-     *
-     * <p>
-     * If this method has been called for a specific position and returned <code>null</code>, then
-     * {@link ViewCallback#onItemLoaded(int)} will be called when it finally loads. Note that if
-     * this position stays outside of the cached item range (as defined by
-     * {@link ViewCallback#extendRangeInto} method), then the callback will never be called for
-     * this position.
-     *
-     * @param position Item position.
-     *
-     * @return The data item at the given position or <code>null</code> if it has not been loaded
-     *         yet.
-     */
-    public T getItem(int position) {
-        if (position < 0 || position >= mItemCount) {
-            throw new IndexOutOfBoundsException(position + " is not within 0 and " + mItemCount);
-        }
-        T item = mTileList.getItemAt(position);
-        if (item == null && !isRefreshPending()) {
-            mMissingPositions.put(position, 0);
-        }
-        return item;
-    }
-
-    /**
-     * Returns the number of items in the data set.
-     *
-     * <p>
-     * This is the number returned by a recent call to
-     * {@link DataCallback#refreshData()}.
-     *
-     * @return Number of items.
-     */
-    public int getItemCount() {
-        return mItemCount;
-    }
-
-    void updateRange() {
-        mViewCallback.getItemRangeInto(mTmpRange);
-        if (mTmpRange[0] > mTmpRange[1] || mTmpRange[0] < 0) {
-            return;
-        }
-        if (mTmpRange[1] >= mItemCount) {
-            // Invalid range may arrive soon after the refresh.
-            return;
-        }
-
-        if (!mAllowScrollHints) {
-            mScrollHint = ViewCallback.HINT_SCROLL_NONE;
-        } else if (mTmpRange[0] > mPrevRange[1] || mPrevRange[0] > mTmpRange[1]) {
-            // Ranges do not intersect, long leap not a scroll.
-            mScrollHint = ViewCallback.HINT_SCROLL_NONE;
-        } else if (mTmpRange[0] < mPrevRange[0]) {
-            mScrollHint = ViewCallback.HINT_SCROLL_DESC;
-        } else if (mTmpRange[0] > mPrevRange[0]) {
-            mScrollHint = ViewCallback.HINT_SCROLL_ASC;
-        }
-
-        mPrevRange[0] = mTmpRange[0];
-        mPrevRange[1] = mTmpRange[1];
-
-        mViewCallback.extendRangeInto(mTmpRange, mTmpRangeExtended, mScrollHint);
-        mTmpRangeExtended[0] = Math.min(mTmpRange[0], Math.max(mTmpRangeExtended[0], 0));
-        mTmpRangeExtended[1] =
-                Math.max(mTmpRange[1], Math.min(mTmpRangeExtended[1], mItemCount - 1));
-
-        mBackgroundProxy.updateRange(mTmpRange[0], mTmpRange[1],
-                mTmpRangeExtended[0], mTmpRangeExtended[1], mScrollHint);
-    }
-
-    private final ThreadUtil.MainThreadCallback<T>
-            mMainThreadCallback = new ThreadUtil.MainThreadCallback<T>() {
-        @Override
-        public void updateItemCount(int generation, int itemCount) {
-            if (DEBUG) {
-                log("updateItemCount: size=%d, gen #%d", itemCount, generation);
-            }
-            if (!isRequestedGeneration(generation)) {
-                return;
-            }
-            mItemCount = itemCount;
-            mViewCallback.onDataRefresh();
-            mDisplayedGeneration = mRequestedGeneration;
-            recycleAllTiles();
-
-            mAllowScrollHints = false;  // Will be set to true after a first real scroll.
-            // There will be no scroll event if the size change does not affect the current range.
-            updateRange();
-        }
-
-        @Override
-        public void addTile(int generation, TileList.Tile<T> tile) {
-            if (!isRequestedGeneration(generation)) {
-                if (DEBUG) {
-                    log("recycling an older generation tile @%d", tile.mStartPosition);
-                }
-                mBackgroundProxy.recycleTile(tile);
-                return;
-            }
-            TileList.Tile<T> duplicate = mTileList.addOrReplace(tile);
-            if (duplicate != null) {
-                Log.e(TAG, "duplicate tile @" + duplicate.mStartPosition);
-                mBackgroundProxy.recycleTile(duplicate);
-            }
-            if (DEBUG) {
-                log("gen #%d, added tile @%d, total tiles: %d",
-                        generation, tile.mStartPosition, mTileList.size());
-            }
-            int endPosition = tile.mStartPosition + tile.mItemCount;
-            int index = 0;
-            while (index < mMissingPositions.size()) {
-                final int position = mMissingPositions.keyAt(index);
-                if (tile.mStartPosition <= position && position < endPosition) {
-                    mMissingPositions.removeAt(index);
-                    mViewCallback.onItemLoaded(position);
-                } else {
-                    index++;
-                }
-            }
-        }
-
-        @Override
-        public void removeTile(int generation, int position) {
-            if (!isRequestedGeneration(generation)) {
-                return;
-            }
-            TileList.Tile<T> tile = mTileList.removeAtPos(position);
-            if (tile == null) {
-                Log.e(TAG, "tile not found @" + position);
-                return;
-            }
-            if (DEBUG) {
-                log("recycling tile @%d, total tiles: %d", tile.mStartPosition, mTileList.size());
-            }
-            mBackgroundProxy.recycleTile(tile);
-        }
-
-        private void recycleAllTiles() {
-            if (DEBUG) {
-                log("recycling all %d tiles", mTileList.size());
-            }
-            for (int i = 0; i < mTileList.size(); i++) {
-                mBackgroundProxy.recycleTile(mTileList.getAtIndex(i));
-            }
-            mTileList.clear();
-        }
-
-        private boolean isRequestedGeneration(int generation) {
-            return generation == mRequestedGeneration;
-        }
-    };
-
-    private final ThreadUtil.BackgroundCallback<T>
-            mBackgroundCallback = new ThreadUtil.BackgroundCallback<T>() {
-
-        private TileList.Tile<T> mRecycledRoot;
-
-        final SparseBooleanArray mLoadedTiles = new SparseBooleanArray();
-
-        private int mGeneration;
-        private int mItemCount;
-
-        private int mFirstRequiredTileStart;
-        private int mLastRequiredTileStart;
-
-        @Override
-        public void refresh(int generation) {
-            mGeneration = generation;
-            mLoadedTiles.clear();
-            mItemCount = mDataCallback.refreshData();
-            mMainThreadProxy.updateItemCount(mGeneration, mItemCount);
-        }
-
-        @Override
-        public void updateRange(int rangeStart, int rangeEnd, int extRangeStart, int extRangeEnd,
-                int scrollHint) {
-            if (DEBUG) {
-                log("updateRange: %d..%d extended to %d..%d, scroll hint: %d",
-                        rangeStart, rangeEnd, extRangeStart, extRangeEnd, scrollHint);
-            }
-
-            if (rangeStart > rangeEnd) {
-                return;
-            }
-
-            final int firstVisibleTileStart = getTileStart(rangeStart);
-            final int lastVisibleTileStart = getTileStart(rangeEnd);
-
-            mFirstRequiredTileStart = getTileStart(extRangeStart);
-            mLastRequiredTileStart = getTileStart(extRangeEnd);
-            if (DEBUG) {
-                log("requesting tile range: %d..%d",
-                        mFirstRequiredTileStart, mLastRequiredTileStart);
-            }
-
-            // All pending tile requests are removed by ThreadUtil at this point.
-            // Re-request all required tiles in the most optimal order.
-            if (scrollHint == ViewCallback.HINT_SCROLL_DESC) {
-                requestTiles(mFirstRequiredTileStart, lastVisibleTileStart, scrollHint, true);
-                requestTiles(lastVisibleTileStart + mTileSize, mLastRequiredTileStart, scrollHint,
-                        false);
-            } else {
-                requestTiles(firstVisibleTileStart, mLastRequiredTileStart, scrollHint, false);
-                requestTiles(mFirstRequiredTileStart, firstVisibleTileStart - mTileSize, scrollHint,
-                        true);
-            }
-        }
-
-        private int getTileStart(int position) {
-            return position - position % mTileSize;
-        }
-
-        private void requestTiles(int firstTileStart, int lastTileStart, int scrollHint,
-                                  boolean backwards) {
-            for (int i = firstTileStart; i <= lastTileStart; i += mTileSize) {
-                int tileStart = backwards ? (lastTileStart + firstTileStart - i) : i;
-                if (DEBUG) {
-                    log("requesting tile @%d", tileStart);
-                }
-                mBackgroundProxy.loadTile(tileStart, scrollHint);
-            }
-        }
-
-        @Override
-        public void loadTile(int position, int scrollHint) {
-            if (isTileLoaded(position)) {
-                if (DEBUG) {
-                    log("already loaded tile @%d", position);
-                }
-                return;
-            }
-            TileList.Tile<T> tile = acquireTile();
-            tile.mStartPosition = position;
-            tile.mItemCount = Math.min(mTileSize, mItemCount - tile.mStartPosition);
-            mDataCallback.fillData(tile.mItems, tile.mStartPosition, tile.mItemCount);
-            flushTileCache(scrollHint);
-            addTile(tile);
-        }
-
-        @Override
-        public void recycleTile(TileList.Tile<T> tile) {
-            if (DEBUG) {
-                log("recycling tile @%d", tile.mStartPosition);
-            }
-            mDataCallback.recycleData(tile.mItems, tile.mItemCount);
-
-            tile.mNext = mRecycledRoot;
-            mRecycledRoot = tile;
-        }
-
-        private TileList.Tile<T> acquireTile() {
-            if (mRecycledRoot != null) {
-                TileList.Tile<T> result = mRecycledRoot;
-                mRecycledRoot = mRecycledRoot.mNext;
-                return result;
-            }
-            return new TileList.Tile<T>(mTClass, mTileSize);
-        }
-
-        private boolean isTileLoaded(int position) {
-            return mLoadedTiles.get(position);
-        }
-
-        private void addTile(TileList.Tile<T> tile) {
-            mLoadedTiles.put(tile.mStartPosition, true);
-            mMainThreadProxy.addTile(mGeneration, tile);
-            if (DEBUG) {
-                log("loaded tile @%d, total tiles: %d", tile.mStartPosition, mLoadedTiles.size());
-            }
-        }
-
-        private void removeTile(int position) {
-            mLoadedTiles.delete(position);
-            mMainThreadProxy.removeTile(mGeneration, position);
-            if (DEBUG) {
-                log("flushed tile @%d, total tiles: %s", position, mLoadedTiles.size());
-            }
-        }
-
-        private void flushTileCache(int scrollHint) {
-            final int cacheSizeLimit = mDataCallback.getMaxCachedTiles();
-            while (mLoadedTiles.size() >= cacheSizeLimit) {
-                int firstLoadedTileStart = mLoadedTiles.keyAt(0);
-                int lastLoadedTileStart = mLoadedTiles.keyAt(mLoadedTiles.size() - 1);
-                int startMargin = mFirstRequiredTileStart - firstLoadedTileStart;
-                int endMargin = lastLoadedTileStart - mLastRequiredTileStart;
-                if (startMargin > 0 && (startMargin >= endMargin ||
-                        (scrollHint == ViewCallback.HINT_SCROLL_ASC))) {
-                    removeTile(firstLoadedTileStart);
-                } else if (endMargin > 0 && (startMargin < endMargin ||
-                        (scrollHint == ViewCallback.HINT_SCROLL_DESC))){
-                    removeTile(lastLoadedTileStart);
-                } else {
-                    // Could not flush on either side, bail out.
-                    return;
-                }
-            }
-        }
-
-        private void log(String s, Object... args) {
-            Log.d(TAG, "[BKGR] " + String.format(s, args));
-        }
-    };
-
-    /**
-     * The callback that provides data access for {@link AsyncListUtil}.
-     *
-     * <p>
-     * All methods are called on the background thread.
-     */
-    public static abstract class DataCallback<T> {
-
-        /**
-         * Refresh the data set and return the new data item count.
-         *
-         * <p>
-         * If the data is being accessed through {@link android.database.Cursor} this is where
-         * the new cursor should be created.
-         *
-         * @return Data item count.
-         */
-        @WorkerThread
-        public abstract int refreshData();
-
-        /**
-         * Fill the given tile.
-         *
-         * <p>
-         * The provided tile might be a recycled tile, in which case it will already have objects.
-         * It is suggested to re-use these objects if possible in your use case.
-         *
-         * @param startPosition The start position in the list.
-         * @param itemCount The data item count.
-         * @param data The data item array to fill into. Should not be accessed beyond
-         *             <code>itemCount</code>.
-         */
-        @WorkerThread
-        public abstract void fillData(T[] data, int startPosition, int itemCount);
-
-        /**
-         * Recycle the objects created in {@link #fillData} if necessary.
-         *
-         *
-         * @param data Array of data items. Should not be accessed beyond <code>itemCount</code>.
-         * @param itemCount The data item count.
-         */
-        @WorkerThread
-        public void recycleData(T[] data, int itemCount) {
-        }
-
-        /**
-         * Returns tile cache size limit (in tiles).
-         *
-         * <p>
-         * The actual number of cached tiles will be the maximum of this value and the number of
-         * tiles that is required to cover the range returned by
-         * {@link ViewCallback#extendRangeInto(int[], int[], int)}.
-         * <p>
-         * For example, if this method returns 10, and the most
-         * recent call to {@link ViewCallback#extendRangeInto(int[], int[], int)} returned
-         * {100, 179}, and the tile size is 5, then the maximum number of cached tiles will be 16.
-         * <p>
-         * However, if the tile size is 20, then the maximum number of cached tiles will be 10.
-         * <p>
-         * The default implementation returns 10.
-         *
-         * @return Maximum cache size.
-         */
-        @WorkerThread
-        public int getMaxCachedTiles() {
-            return 10;
-        }
-    }
-
-    /**
-     * The callback that links {@link AsyncListUtil} with the list view.
-     *
-     * <p>
-     * All methods are called on the main thread.
-          */
-    public static abstract class ViewCallback {
-
-        /**
-         * No scroll direction hint available.
-         */
-        public static final int HINT_SCROLL_NONE = 0;
-
-        /**
-         * Scrolling in descending order (from higher to lower positions in the order of the backing
-         * storage).
-         */
-        public static final int HINT_SCROLL_DESC = 1;
-
-        /**
-         * Scrolling in ascending order (from lower to higher positions in the order of the backing
-         * storage).
-         */
-        public static final int HINT_SCROLL_ASC = 2;
-
-        /**
-         * Compute the range of visible item positions.
-         * <p>
-         * outRange[0] is the position of the first visible item (in the order of the backing
-         * storage).
-         * <p>
-         * outRange[1] is the position of the last visible item (in the order of the backing
-         * storage).
-         * <p>
-         * Negative positions and positions greater or equal to {@link #getItemCount} are invalid.
-         * If the returned range contains invalid positions it is ignored (no item will be loaded).
-         *
-         * @param outRange The visible item range.
-         */
-        @UiThread
-        public abstract void getItemRangeInto(int[] outRange);
-
-        /**
-         * Compute a wider range of items that will be loaded for smoother scrolling.
-         *
-         * <p>
-         * If there is no scroll hint, the default implementation extends the visible range by half
-         * its length in both directions. If there is a scroll hint, the range is extended by
-         * its full length in the scroll direction, and by half in the other direction.
-         * <p>
-         * For example, if <code>range</code> is <code>{100, 200}</code> and <code>scrollHint</code>
-         * is {@link #HINT_SCROLL_ASC}, then <code>outRange</code> will be <code>{50, 300}</code>.
-         * <p>
-         * However, if <code>scrollHint</code> is {@link #HINT_SCROLL_NONE}, then
-         * <code>outRange</code> will be <code>{50, 250}</code>
-         *
-         * @param range Visible item range.
-         * @param outRange Extended range.
-         * @param scrollHint The scroll direction hint.
-         */
-        @UiThread
-        public void extendRangeInto(int[] range, int[] outRange, int scrollHint) {
-            final int fullRange = range[1] - range[0] + 1;
-            final int halfRange = fullRange / 2;
-            outRange[0] = range[0] - (scrollHint == HINT_SCROLL_DESC ? fullRange : halfRange);
-            outRange[1] = range[1] + (scrollHint == HINT_SCROLL_ASC ? fullRange : halfRange);
-        }
-
-        /**
-         * Called when the entire data set has changed.
-         */
-        @UiThread
-        public abstract void onDataRefresh();
-
-        /**
-         * Called when an item at the given position is loaded.
-         * @param position Item position.
-         */
-        @UiThread
-        public abstract void onItemLoaded(int position);
-    }
-}
diff --git a/android/support/v7/util/BatchingListUpdateCallback.java b/android/support/v7/util/BatchingListUpdateCallback.java
deleted file mode 100644
index c8bc1a4..0000000
--- a/android/support/v7/util/BatchingListUpdateCallback.java
+++ /dev/null
@@ -1,123 +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;
-
-/**
- * Wraps a {@link ListUpdateCallback} callback and batches operations that can be merged.
- * <p>
- * For instance, when 2 add operations comes that adds 2 consecutive elements,
- * BatchingListUpdateCallback merges them and calls the wrapped callback only once.
- * <p>
- * This is a general purpose class and is also used by
- * {@link android.support.v7.util.DiffUtil.DiffResult DiffResult} and
- * {@link SortedList} to minimize the number of updates that are dispatched.
- * <p>
- * If you use this class to batch updates, you must call {@link #dispatchLastEvent()} when the
- * stream of update events drain.
- */
-public class BatchingListUpdateCallback implements ListUpdateCallback {
-    private static final int TYPE_NONE = 0;
-    private static final int TYPE_ADD = 1;
-    private static final int TYPE_REMOVE = 2;
-    private static final int TYPE_CHANGE = 3;
-
-    final ListUpdateCallback mWrapped;
-
-    int mLastEventType = TYPE_NONE;
-    int mLastEventPosition = -1;
-    int mLastEventCount = -1;
-    Object mLastEventPayload = null;
-
-    public BatchingListUpdateCallback(ListUpdateCallback callback) {
-        mWrapped = callback;
-    }
-
-    /**
-     * BatchingListUpdateCallback holds onto the last event to see if it can be merged with the
-     * next one. When stream of events finish, you should call this method to dispatch the last
-     * event.
-     */
-    public void dispatchLastEvent() {
-        if (mLastEventType == TYPE_NONE) {
-            return;
-        }
-        switch (mLastEventType) {
-            case TYPE_ADD:
-                mWrapped.onInserted(mLastEventPosition, mLastEventCount);
-                break;
-            case TYPE_REMOVE:
-                mWrapped.onRemoved(mLastEventPosition, mLastEventCount);
-                break;
-            case TYPE_CHANGE:
-                mWrapped.onChanged(mLastEventPosition, mLastEventCount, mLastEventPayload);
-                break;
-        }
-        mLastEventPayload = null;
-        mLastEventType = TYPE_NONE;
-    }
-
-    @Override
-    public void onInserted(int position, int count) {
-        if (mLastEventType == TYPE_ADD && position >= mLastEventPosition
-                && position <= mLastEventPosition + mLastEventCount) {
-            mLastEventCount += count;
-            mLastEventPosition = Math.min(position, mLastEventPosition);
-            return;
-        }
-        dispatchLastEvent();
-        mLastEventPosition = position;
-        mLastEventCount = count;
-        mLastEventType = TYPE_ADD;
-    }
-
-    @Override
-    public void onRemoved(int position, int count) {
-        if (mLastEventType == TYPE_REMOVE && mLastEventPosition >= position &&
-                mLastEventPosition <= position + count) {
-            mLastEventCount += count;
-            mLastEventPosition = position;
-            return;
-        }
-        dispatchLastEvent();
-        mLastEventPosition = position;
-        mLastEventCount = count;
-        mLastEventType = TYPE_REMOVE;
-    }
-
-    @Override
-    public void onMoved(int fromPosition, int toPosition) {
-        dispatchLastEvent(); // moves are not merged
-        mWrapped.onMoved(fromPosition, toPosition);
-    }
-
-    @Override
-    public void onChanged(int position, int count, Object payload) {
-        if (mLastEventType == TYPE_CHANGE &&
-                !(position > mLastEventPosition + mLastEventCount
-                        || position + count < mLastEventPosition || mLastEventPayload != payload)) {
-            // take potential overlap into account
-            int previousEnd = mLastEventPosition + mLastEventCount;
-            mLastEventPosition = Math.min(position, mLastEventPosition);
-            mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition;
-            return;
-        }
-        dispatchLastEvent();
-        mLastEventPosition = position;
-        mLastEventCount = count;
-        mLastEventPayload = payload;
-        mLastEventType = TYPE_CHANGE;
-    }
-}
diff --git a/android/support/v7/util/BatchingListUpdateCallbackTest.java b/android/support/v7/util/BatchingListUpdateCallbackTest.java
deleted file mode 100644
index bb3e6ea..0000000
--- a/android/support/v7/util/BatchingListUpdateCallbackTest.java
+++ /dev/null
@@ -1,294 +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 static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import android.support.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class BatchingListUpdateCallbackTest {
-    BatchingListUpdateCallback mBatching;
-    ListUpdateCallback mCallback;
-
-    @Before
-    public void setup() {
-        mCallback = mock(ListUpdateCallback.class);
-        mBatching = new BatchingListUpdateCallback(mCallback);
-    }
-
-    @Test
-    public void addSimple() {
-        mBatching.onInserted(3, 2);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onInserted(3, 2);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void addToSamePos() {
-        mBatching.onInserted(3, 2);
-        mBatching.onInserted(3, 1);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onInserted(3, 3);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void addInsidePrevious() {
-        mBatching.onInserted(3, 5);
-        mBatching.onInserted(5, 1);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onInserted(3, 6);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void addBefore() {
-        mBatching.onInserted(3, 5);
-        mBatching.onInserted(2, 1);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onInserted(3, 5);
-        verify(mCallback).onInserted(2, 1);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void removeSimple() {
-        mBatching.onRemoved(3, 2);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onRemoved(3, 2);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void removeSamePosition() {
-        mBatching.onRemoved(3, 2);
-        mBatching.onRemoved(3, 1);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onRemoved(3, 3);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void removeInside() {
-        mBatching.onRemoved(3, 5);
-        mBatching.onRemoved(4, 2);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onRemoved(3, 5);
-        verify(mCallback).onRemoved(4, 2);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void removeBefore() {
-        mBatching.onRemoved(3, 2);
-        mBatching.onRemoved(2, 1);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onRemoved(2, 3);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void removeBefore2() {
-        mBatching.onRemoved(3, 2);
-        mBatching.onRemoved(2, 4);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onRemoved(2, 6);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void removeBefore3() {
-        mBatching.onRemoved(3, 2);
-        mBatching.onRemoved(1, 1);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onRemoved(3, 2);
-        verify(mCallback).onRemoved(1, 1);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void moveSimple() {
-        mBatching.onMoved(3, 2);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onMoved(3, 2);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void moveTwice() {
-        mBatching.onMoved(3, 2);
-        mBatching.onMoved(5, 6);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onMoved(3, 2);
-        verify(mCallback).onMoved(5, 6);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeSimple() {
-        mBatching.onChanged(3, 2, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeConsecutive() {
-        mBatching.onChanged(3, 2, null);
-        mBatching.onChanged(5, 2, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 4, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeTheSame() {
-        mBatching.onChanged(3, 2, null);
-        mBatching.onChanged(4, 2, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 3, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeTheSame2() {
-        mBatching.onChanged(3, 2, null);
-        mBatching.onChanged(3, 2, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeBefore() {
-        mBatching.onChanged(3, 2, null);
-        mBatching.onChanged(2, 1, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(2, 3, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeBeforeOverlap() {
-        mBatching.onChanged(3, 2, null);
-        mBatching.onChanged(2, 2, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(2, 3, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeSimpleWithPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, payload);
-    }
-
-    @Test
-    public void changeConsecutiveWithPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.onChanged(5, 2, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 4, payload);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeTheSameWithPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.onChanged(4, 2, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 3, payload);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeTheSame2WithPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.onChanged(3, 2, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, payload);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeBeforeWithPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.onChanged(2, 1, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(2, 3, payload);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeBeforeOverlapWithPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.onChanged(2, 2, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(2, 3, payload);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeWithNewPayload() {
-        Object payload1 = new Object();
-        Object payload2 = new Object();
-        mBatching.onChanged(3, 2, payload1);
-        mBatching.onChanged(2, 2, payload2);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, payload1);
-        verify(mCallback).onChanged(2, 2, payload2);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeWithEmptyPayload() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, payload);
-        mBatching.onChanged(2, 2, null);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, payload);
-        verify(mCallback).onChanged(2, 2, null);
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
-    public void changeWithEmptyPayload2() {
-        Object payload = new Object();
-        mBatching.onChanged(3, 2, null);
-        mBatching.onChanged(2, 2, payload);
-        mBatching.dispatchLastEvent();
-        verify(mCallback).onChanged(3, 2, null);
-        verify(mCallback).onChanged(2, 2, payload);
-        verifyNoMoreInteractions(mCallback);
-    }
-}
diff --git a/android/support/v7/util/DiffUtil.java b/android/support/v7/util/DiffUtil.java
deleted file mode 100644
index a55a21d..0000000
--- a/android/support/v7/util/DiffUtil.java
+++ /dev/null
@@ -1,903 +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 android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.widget.RecyclerView;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * DiffUtil is a utility class that can calculate the difference between two lists and output a
- * list of update operations that converts the first list into the second one.
- * <p>
- * It can be used to calculate updates for a RecyclerView Adapter.
- * <p>
- * DiffUtil uses Eugene W. Myers's difference algorithm to calculate the minimal number of updates
- * to convert one list into another. Myers's algorithm does not handle items that are moved so
- * DiffUtil runs a second pass on the result to detect items that were moved.
- * <p>
- * If the lists are large, this operation may take significant time so you are advised to run this
- * on a background thread, get the {@link DiffResult} then apply it on the RecyclerView on the main
- * thread.
- * <p>
- * This algorithm is optimized for space and uses O(N) space to find the minimal
- * number of addition and removal operations between the two lists. It has O(N + D^2) expected time
- * performance where D is the length of the edit script.
- * <p>
- * If move detection is enabled, it takes an additional O(N^2) time where N is the total number of
- * added and removed items. If your lists are already sorted by the same constraint (e.g. a created
- * timestamp for a list of posts), you can disable move detection to improve performance.
- * <p>
- * The actual runtime of the algorithm significantly depends on the number of changes in the list
- * and the cost of your comparison methods. Below are some average run times for reference:
- * (The test list is composed of random UUID Strings and the tests are run on Nexus 5X with M)
- * <ul>
- *     <li>100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms
- *     <li>100 items and 100 modifications: 3.82 ms, median: 3.75 ms
- *     <li>100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms
- *     <li>1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms
- *     <li>1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms
- *     <li>1000 items and 200 modifications: 27.07 ms, median: 26.92 ms
- *     <li>1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms
- * </ul>
- * <p>
- * Due to implementation constraints, the max size of the list can be 2^26.
- */
-public class DiffUtil {
-
-    private DiffUtil() {
-        // utility class, no instance.
-    }
-
-    private static final Comparator<Snake> SNAKE_COMPARATOR = new Comparator<Snake>() {
-        @Override
-        public int compare(Snake o1, Snake o2) {
-            int cmpX = o1.x - o2.x;
-            return cmpX == 0 ? o1.y - o2.y : cmpX;
-        }
-    };
-
-    // Myers' algorithm uses two lists as axis labels. In DiffUtil's implementation, `x` axis is
-    // used for old list and `y` axis is used for new list.
-
-    /**
-     * Calculates the list of update operations that can covert one list into the other one.
-     *
-     * @param cb The callback that acts as a gateway to the backing list data
-     *
-     * @return A DiffResult that contains the information about the edit sequence to convert the
-     * old list into the new list.
-     */
-    public static DiffResult calculateDiff(Callback cb) {
-        return calculateDiff(cb, true);
-    }
-
-    /**
-     * Calculates the list of update operations that can covert one list into the other one.
-     * <p>
-     * If your old and new lists are sorted by the same constraint and items never move (swap
-     * positions), you can disable move detection which takes <code>O(N^2)</code> time where
-     * N is the number of added, moved, removed items.
-     *
-     * @param cb The callback that acts as a gateway to the backing list data
-     * @param detectMoves True if DiffUtil should try to detect moved items, false otherwise.
-     *
-     * @return A DiffResult that contains the information about the edit sequence to convert the
-     * old list into the new list.
-     */
-    public static DiffResult calculateDiff(Callback cb, boolean detectMoves) {
-        final int oldSize = cb.getOldListSize();
-        final int newSize = cb.getNewListSize();
-
-        final List<Snake> snakes = new ArrayList<>();
-
-        // instead of a recursive implementation, we keep our own stack to avoid potential stack
-        // overflow exceptions
-        final List<Range> stack = new ArrayList<>();
-
-        stack.add(new Range(0, oldSize, 0, newSize));
-
-        final int max = oldSize + newSize + Math.abs(oldSize - newSize);
-        // allocate forward and backward k-lines. K lines are diagonal lines in the matrix. (see the
-        // paper for details)
-        // These arrays lines keep the max reachable position for each k-line.
-        final int[] forward = new int[max * 2];
-        final int[] backward = new int[max * 2];
-
-        // We pool the ranges to avoid allocations for each recursive call.
-        final List<Range> rangePool = new ArrayList<>();
-        while (!stack.isEmpty()) {
-            final Range range = stack.remove(stack.size() - 1);
-            final Snake snake = diffPartial(cb, range.oldListStart, range.oldListEnd,
-                    range.newListStart, range.newListEnd, forward, backward, max);
-            if (snake != null) {
-                if (snake.size > 0) {
-                    snakes.add(snake);
-                }
-                // offset the snake to convert its coordinates from the Range's area to global
-                snake.x += range.oldListStart;
-                snake.y += range.newListStart;
-
-                // add new ranges for left and right
-                final Range left = rangePool.isEmpty() ? new Range() : rangePool.remove(
-                        rangePool.size() - 1);
-                left.oldListStart = range.oldListStart;
-                left.newListStart = range.newListStart;
-                if (snake.reverse) {
-                    left.oldListEnd = snake.x;
-                    left.newListEnd = snake.y;
-                } else {
-                    if (snake.removal) {
-                        left.oldListEnd = snake.x - 1;
-                        left.newListEnd = snake.y;
-                    } else {
-                        left.oldListEnd = snake.x;
-                        left.newListEnd = snake.y - 1;
-                    }
-                }
-                stack.add(left);
-
-                // re-use range for right
-                //noinspection UnnecessaryLocalVariable
-                final Range right = range;
-                if (snake.reverse) {
-                    if (snake.removal) {
-                        right.oldListStart = snake.x + snake.size + 1;
-                        right.newListStart = snake.y + snake.size;
-                    } else {
-                        right.oldListStart = snake.x + snake.size;
-                        right.newListStart = snake.y + snake.size + 1;
-                    }
-                } else {
-                    right.oldListStart = snake.x + snake.size;
-                    right.newListStart = snake.y + snake.size;
-                }
-                stack.add(right);
-            } else {
-                rangePool.add(range);
-            }
-
-        }
-        // sort snakes
-        Collections.sort(snakes, SNAKE_COMPARATOR);
-
-        return new DiffResult(cb, snakes, forward, backward, detectMoves);
-
-    }
-
-    private static Snake diffPartial(Callback cb, int startOld, int endOld,
-            int startNew, int endNew, int[] forward, int[] backward, int kOffset) {
-        final int oldSize = endOld - startOld;
-        final int newSize = endNew - startNew;
-
-        if (endOld - startOld < 1 || endNew - startNew < 1) {
-            return null;
-        }
-
-        final int delta = oldSize - newSize;
-        final int dLimit = (oldSize + newSize + 1) / 2;
-        Arrays.fill(forward, kOffset - dLimit - 1, kOffset + dLimit + 1, 0);
-        Arrays.fill(backward, kOffset - dLimit - 1 + delta, kOffset + dLimit + 1 + delta, oldSize);
-        final boolean checkInFwd = delta % 2 != 0;
-        for (int d = 0; d <= dLimit; d++) {
-            for (int k = -d; k <= d; k += 2) {
-                // find forward path
-                // 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])) {
-                    x = forward[kOffset + k + 1];
-                    removal = false;
-                } else {
-                    x = forward[kOffset + k - 1] + 1;
-                    removal = true;
-                }
-                // set y based on x
-                int y = x - k;
-                // move diagonal as long as items match
-                while (x < oldSize && y < newSize
-                        && cb.areItemsTheSame(startOld + x, startNew + y)) {
-                    x++;
-                    y++;
-                }
-                forward[kOffset + k] = x;
-                if (checkInFwd && k >= delta - d + 1 && k <= delta + d - 1) {
-                    if (forward[kOffset + k] >= backward[kOffset + k]) {
-                        Snake outSnake = new Snake();
-                        outSnake.x = backward[kOffset + k];
-                        outSnake.y = outSnake.x - k;
-                        outSnake.size = forward[kOffset + k] - backward[kOffset + k];
-                        outSnake.removal = removal;
-                        outSnake.reverse = false;
-                        return outSnake;
-                    }
-                }
-            }
-            for (int k = -d; k <= d; k += 2) {
-                // find reverse path at k + delta, in reverse
-                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])) {
-                    x = backward[kOffset + backwardK - 1];
-                    removal = false;
-                } else {
-                    x = backward[kOffset + backwardK + 1] - 1;
-                    removal = true;
-                }
-
-                // set y based on x
-                int y = x - backwardK;
-                // move diagonal as long as items match
-                while (x > 0 && y > 0
-                        && cb.areItemsTheSame(startOld + x - 1, startNew + y - 1)) {
-                    x--;
-                    y--;
-                }
-                backward[kOffset + backwardK] = x;
-                if (!checkInFwd && k + delta >= -d && k + delta <= d) {
-                    if (forward[kOffset + backwardK] >= backward[kOffset + backwardK]) {
-                        Snake outSnake = new Snake();
-                        outSnake.x = backward[kOffset + backwardK];
-                        outSnake.y = outSnake.x - backwardK;
-                        outSnake.size =
-                                forward[kOffset + backwardK] - backward[kOffset + backwardK];
-                        outSnake.removal = removal;
-                        outSnake.reverse = true;
-                        return outSnake;
-                    }
-                }
-            }
-        }
-        throw new IllegalStateException("DiffUtil hit an unexpected case while trying to calculate"
-                + " the optimal path. Please make sure your data is not changing during the"
-                + " diff calculation.");
-    }
-
-    /**
-     * A Callback class used by DiffUtil while calculating the diff between two lists.
-     */
-    public abstract static class Callback {
-        /**
-         * Returns the size of the old list.
-         *
-         * @return The size of the old list.
-         */
-        public abstract int getOldListSize();
-
-        /**
-         * Returns the size of the new list.
-         *
-         * @return The size of the new list.
-         */
-        public abstract int getNewListSize();
-
-        /**
-         * Called by the DiffUtil to decide whether two object represent the same Item.
-         * <p>
-         * For example, if your items have unique ids, this method should check their id equality.
-         *
-         * @param oldItemPosition The position of the item in the old list
-         * @param newItemPosition The position of 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(int oldItemPosition, int newItemPosition);
-
-        /**
-         * Called by the DiffUtil when it wants to check whether two items have the same data.
-         * DiffUtil uses this information to detect if the contents of an item has changed.
-         * <p>
-         * DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}
-         * so that you can change its behavior depending on your UI.
-         * For example, if you are using DiffUtil with a
-         * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
-         * return whether the items' visual representations are the same.
-         * <p>
-         * This method is called only if {@link #areItemsTheSame(int, int)} returns
-         * {@code true} for these items.
-         *
-         * @param oldItemPosition The position of the item in the old list
-         * @param newItemPosition The position of the item in the new list which replaces the
-         *                        oldItem
-         * @return True if the contents of the items are the same or false if they are different.
-         */
-        public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
-
-        /**
-         * When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and
-         * {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil
-         * calls this method to get a payload about the change.
-         * <p>
-         * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
-         * particular field that changed in the item and your
-         * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
-         * information to run the correct animation.
-         * <p>
-         * Default implementation returns {@code null}.
-         *
-         * @param oldItemPosition The position of the item in the old list
-         * @param newItemPosition The position of the item in the new list
-         *
-         * @return A payload object that represents the change between the two items.
-         */
-        @Nullable
-        public Object getChangePayload(int oldItemPosition, int newItemPosition) {
-            return null;
-        }
-    }
-
-    /**
-     * Callback for calculating the diff between two non-null items in a list.
-     * <p>
-     * {@link Callback} serves two roles - list indexing, and item diffing. ItemCallback handles
-     * just the second of these, which allows separation of code that indexes into an array or List
-     * from the presentation-layer and content specific diffing code.
-     *
-     * @param <T> Type of items to compare.
-     */
-    public abstract static class ItemCallback<T> {
-        /**
-         * Called to check whether two objects represent the same item.
-         * <p>
-         * For example, if your items have unique ids, this method should check their id equality.
-         *
-         * @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.
-         *
-         * @see Callback#areItemsTheSame(int, int)
-         */
-        public abstract boolean areItemsTheSame(T oldItem, T newItem);
-
-        /**
-         * Called to check whether two items have the same data.
-         * <p>
-         * This information is used to detect if the contents of an item have changed.
-         * <p>
-         * This method to check equality instead of {@link Object#equals(Object)} so that you can
-         * change its behavior depending on your UI.
-         * <p>
-         * For example, if you are using DiffUtil with a
-         * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
-         * return whether the items' visual representations are the same.
-         * <p>
-         * This method is called only if {@link #areItemsTheSame(T, T)} returns {@code true} for
-         * these items.
-         *
-         * @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.
-         *
-         * @see Callback#areContentsTheSame(int, int)
-         */
-        public abstract boolean areContentsTheSame(T oldItem, T newItem);
-
-        /**
-         * When {@link #areItemsTheSame(T, T)} returns {@code true} for two items and
-         * {@link #areContentsTheSame(T, T)} returns false for them, this method is called to
-         * get a payload about the change.
-         * <p>
-         * For example, if you are using DiffUtil with {@link RecyclerView}, you can return the
-         * particular field that changed in the item and your
-         * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
-         * information to run the correct animation.
-         * <p>
-         * Default implementation returns {@code null}.
-         *
-         * @see Callback#getChangePayload(int, int)
-         */
-        @SuppressWarnings({"WeakerAccess", "unused"})
-        public Object getChangePayload(T oldItem, T newItem) {
-            return null;
-        }
-    }
-
-    /**
-     * Snakes represent a match between two lists. It is optionally prefixed or postfixed with an
-     * add or remove operation. See the Myers' paper for details.
-     */
-    static class Snake {
-        /**
-         * Position in the old list
-         */
-        int x;
-
-        /**
-         * Position in the new list
-         */
-        int y;
-
-        /**
-         * Number of matches. Might be 0.
-         */
-        int size;
-
-        /**
-         * If true, this is a removal from the original list followed by {@code size} matches.
-         * If false, this is an addition from the new list followed by {@code size} matches.
-         */
-        boolean removal;
-
-        /**
-         * If true, the addition or removal is at the end of the snake.
-         * If false, the addition or removal is at the beginning of the snake.
-         */
-        boolean reverse;
-    }
-
-    /**
-     * Represents a range in two lists that needs to be solved.
-     * <p>
-     * This internal class is used when running Myers' algorithm without recursion.
-     */
-    static class Range {
-
-        int oldListStart, oldListEnd;
-
-        int newListStart, newListEnd;
-
-        public Range() {
-        }
-
-        public Range(int oldListStart, int oldListEnd, int newListStart, int newListEnd) {
-            this.oldListStart = oldListStart;
-            this.oldListEnd = oldListEnd;
-            this.newListStart = newListStart;
-            this.newListEnd = newListEnd;
-        }
-    }
-
-    /**
-     * This class holds the information about the result of a
-     * {@link DiffUtil#calculateDiff(Callback, boolean)} call.
-     * <p>
-     * You can consume the updates in a DiffResult via
-     * {@link #dispatchUpdatesTo(ListUpdateCallback)} or directly stream the results into a
-     * {@link RecyclerView.Adapter} via {@link #dispatchUpdatesTo(RecyclerView.Adapter)}.
-     */
-    public static class DiffResult {
-        /**
-         * While reading the flags below, keep in mind that when multiple items move in a list,
-         * Myers's may pick any of them as the anchor item and consider that one NOT_CHANGED while
-         * picking others as additions and removals. This is completely fine as we later detect
-         * all moves.
-         * <p>
-         * Below, when an item is mentioned to stay in the same "location", it means we won't
-         * dispatch a move/add/remove for it, it DOES NOT mean the item is still in the same
-         * position.
-         */
-        // item stayed the same.
-        private static final int FLAG_NOT_CHANGED = 1;
-        // item stayed in the same location but changed.
-        private static final int FLAG_CHANGED = FLAG_NOT_CHANGED << 1;
-        // Item has moved and also changed.
-        private static final int FLAG_MOVED_CHANGED = FLAG_CHANGED << 1;
-        // Item has moved but did not change.
-        private static final int FLAG_MOVED_NOT_CHANGED = FLAG_MOVED_CHANGED << 1;
-        // Ignore this update.
-        // If this is an addition from the new list, it means the item is actually removed from an
-        // earlier position and its move will be dispatched when we process the matching removal
-        // from the old list.
-        // If this is a removal from the old list, it means the item is actually added back to an
-        // earlier index in the new list and we'll dispatch its move when we are processing that
-        // addition.
-        private static final int FLAG_IGNORE = FLAG_MOVED_NOT_CHANGED << 1;
-
-        // since we are re-using the int arrays that were created in the Myers' step, we mask
-        // change flags
-        private static final int FLAG_OFFSET = 5;
-
-        private static final int FLAG_MASK = (1 << FLAG_OFFSET) - 1;
-
-        // The Myers' snakes. At this point, we only care about their diagonal sections.
-        private final List<Snake> mSnakes;
-
-        // The list to keep oldItemStatuses. As we traverse old items, we assign flags to them
-        // which also includes whether they were a real removal or a move (and its new index).
-        private final int[] mOldItemStatuses;
-        // The list to keep newItemStatuses. As we traverse new items, we assign flags to them
-        // which also includes whether they were a real addition or a move(and its old index).
-        private final int[] mNewItemStatuses;
-        // The callback that was given to calcualte diff method.
-        private final Callback mCallback;
-
-        private final int mOldListSize;
-
-        private final int mNewListSize;
-
-        private final boolean mDetectMoves;
-
-        /**
-         * @param callback The callback that was used to calculate the diff
-         * @param snakes The list of Myers' snakes
-         * @param oldItemStatuses An int[] that can be re-purposed to keep metadata
-         * @param newItemStatuses An int[] that can be re-purposed to keep metadata
-         * @param detectMoves True if this DiffResult will try to detect moved items
-         */
-        DiffResult(Callback callback, List<Snake> snakes, int[] oldItemStatuses,
-                int[] newItemStatuses, boolean detectMoves) {
-            mSnakes = snakes;
-            mOldItemStatuses = oldItemStatuses;
-            mNewItemStatuses = newItemStatuses;
-            Arrays.fill(mOldItemStatuses, 0);
-            Arrays.fill(mNewItemStatuses, 0);
-            mCallback = callback;
-            mOldListSize = callback.getOldListSize();
-            mNewListSize = callback.getNewListSize();
-            mDetectMoves = detectMoves;
-            addRootSnake();
-            findMatchingItems();
-        }
-
-        /**
-         * We always add a Snake to 0/0 so that we can run loops from end to beginning and be done
-         * when we run out of snakes.
-         */
-        private void addRootSnake() {
-            Snake firstSnake = mSnakes.isEmpty() ? null : mSnakes.get(0);
-            if (firstSnake == null || firstSnake.x != 0 || firstSnake.y != 0) {
-                Snake root = new Snake();
-                root.x = 0;
-                root.y = 0;
-                root.removal = false;
-                root.size = 0;
-                root.reverse = false;
-                mSnakes.add(0, root);
-            }
-        }
-
-        /**
-         * This method traverses each addition / removal and tries to match it to a previous
-         * removal / addition. This is how we detect move operations.
-         * <p>
-         * This class also flags whether an item has been changed or not.
-         * <p>
-         * DiffUtil does this pre-processing so that if it is running on a big list, it can be moved
-         * to background thread where most of the expensive stuff will be calculated and kept in
-         * the statuses maps. DiffResult uses this pre-calculated information while dispatching
-         * the updates (which is probably being called on the main thread).
-         */
-        private void findMatchingItems() {
-            int posOld = mOldListSize;
-            int posNew = mNewListSize;
-            // traverse the matrix from right bottom to 0,0.
-            for (int i = mSnakes.size() - 1; i >= 0; i--) {
-                final Snake snake = mSnakes.get(i);
-                final int endX = snake.x + snake.size;
-                final int endY = snake.y + snake.size;
-                if (mDetectMoves) {
-                    while (posOld > endX) {
-                        // this is a removal. Check remaining snakes to see if this was added before
-                        findAddition(posOld, posNew, i);
-                        posOld--;
-                    }
-                    while (posNew > endY) {
-                        // this is an addition. Check remaining snakes to see if this was removed
-                        // before
-                        findRemoval(posOld, posNew, i);
-                        posNew--;
-                    }
-                }
-                for (int j = 0; j < snake.size; j++) {
-                    // matching items. Check if it is changed or not
-                    final int oldItemPos = snake.x + j;
-                    final int newItemPos = snake.y + j;
-                    final boolean theSame = mCallback
-                            .areContentsTheSame(oldItemPos, newItemPos);
-                    final int changeFlag = theSame ? FLAG_NOT_CHANGED : FLAG_CHANGED;
-                    mOldItemStatuses[oldItemPos] = (newItemPos << FLAG_OFFSET) | changeFlag;
-                    mNewItemStatuses[newItemPos] = (oldItemPos << FLAG_OFFSET) | changeFlag;
-                }
-                posOld = snake.x;
-                posNew = snake.y;
-            }
-        }
-
-        private void findAddition(int x, int y, int snakeIndex) {
-            if (mOldItemStatuses[x - 1] != 0) {
-                return; // already set by a latter item
-            }
-            findMatchingItem(x, y, snakeIndex, false);
-        }
-
-        private void findRemoval(int x, int y, int snakeIndex) {
-            if (mNewItemStatuses[y - 1] != 0) {
-                return; // already set by a latter item
-            }
-            findMatchingItem(x, y, snakeIndex, true);
-        }
-
-        /**
-         * Finds a matching item that is before the given coordinates in the matrix
-         * (before : left and above).
-         *
-         * @param x The x position in the matrix (position in the old list)
-         * @param y The y position in the matrix (position in the new list)
-         * @param snakeIndex The current snake index
-         * @param removal True if we are looking for a removal, false otherwise
-         *
-         * @return True if such item is found.
-         */
-        private boolean findMatchingItem(final int x, final int y, final int snakeIndex,
-                final boolean removal) {
-            final int myItemPos;
-            int curX;
-            int curY;
-            if (removal) {
-                myItemPos = y - 1;
-                curX = x;
-                curY = y - 1;
-            } else {
-                myItemPos = x - 1;
-                curX = x - 1;
-                curY = y;
-            }
-            for (int i = snakeIndex; i >= 0; i--) {
-                final Snake snake = mSnakes.get(i);
-                final int endX = snake.x + snake.size;
-                final int endY = snake.y + snake.size;
-                if (removal) {
-                    // check removals for a match
-                    for (int pos = curX - 1; pos >= endX; pos--) {
-                        if (mCallback.areItemsTheSame(pos, myItemPos)) {
-                            // found!
-                            final boolean theSame = mCallback.areContentsTheSame(pos, myItemPos);
-                            final int changeFlag = theSame ? FLAG_MOVED_NOT_CHANGED
-                                    : FLAG_MOVED_CHANGED;
-                            mNewItemStatuses[myItemPos] = (pos << FLAG_OFFSET) | FLAG_IGNORE;
-                            mOldItemStatuses[pos] = (myItemPos << FLAG_OFFSET) | changeFlag;
-                            return true;
-                        }
-                    }
-                } else {
-                    // check for additions for a match
-                    for (int pos = curY - 1; pos >= endY; pos--) {
-                        if (mCallback.areItemsTheSame(myItemPos, pos)) {
-                            // found
-                            final boolean theSame = mCallback.areContentsTheSame(myItemPos, pos);
-                            final int changeFlag = theSame ? FLAG_MOVED_NOT_CHANGED
-                                    : FLAG_MOVED_CHANGED;
-                            mOldItemStatuses[x - 1] = (pos << FLAG_OFFSET) | FLAG_IGNORE;
-                            mNewItemStatuses[pos] = ((x - 1) << FLAG_OFFSET) | changeFlag;
-                            return true;
-                        }
-                    }
-                }
-                curX = snake.x;
-                curY = snake.y;
-            }
-            return false;
-        }
-
-        /**
-         * Dispatches the update events to the given adapter.
-         * <p>
-         * For example, if you have an {@link android.support.v7.widget.RecyclerView.Adapter Adapter}
-         * that is backed by a {@link List}, you can swap the list with the new one then call this
-         * method to dispatch all updates to the RecyclerView.
-         * <pre>
-         *     List oldList = mAdapter.getData();
-         *     DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldList, newList));
-         *     mAdapter.setData(newList);
-         *     result.dispatchUpdatesTo(mAdapter);
-         * </pre>
-         * <p>
-         * Note that the RecyclerView requires you to dispatch adapter updates immediately when you
-         * change the data (you cannot defer {@code notify*} calls). The usage above adheres to this
-         * rule because updates are sent to the adapter right after the backing data is changed,
-         * before RecyclerView tries to read it.
-         * <p>
-         * On the other hand, if you have another
-         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver AdapterDataObserver}
-         * that tries to process events synchronously, this may confuse that observer because the
-         * list is instantly moved to its final state while the adapter updates are dispatched later
-         * on, one by one. If you have such an
-         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver AdapterDataObserver},
-         * you can use
-         * {@link #dispatchUpdatesTo(ListUpdateCallback)} to handle each modification
-         * manually.
-         *
-         * @param adapter A RecyclerView adapter which was displaying the old list and will start
-         *                displaying the new list.
-         * @see AdapterListUpdateCallback
-         */
-        public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
-            dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));
-        }
-
-        /**
-         * Dispatches update operations to the given Callback.
-         * <p>
-         * These updates are atomic such that the first update call affects every update call that
-         * comes after it (the same as RecyclerView).
-         *
-         * @param updateCallback The callback to receive the update operations.
-         * @see #dispatchUpdatesTo(RecyclerView.Adapter)
-         */
-        public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {
-            final BatchingListUpdateCallback batchingCallback;
-            if (updateCallback instanceof BatchingListUpdateCallback) {
-                batchingCallback = (BatchingListUpdateCallback) updateCallback;
-            } else {
-                batchingCallback = new BatchingListUpdateCallback(updateCallback);
-                // replace updateCallback with a batching callback and override references to
-                // updateCallback so that we don't call it directly by mistake
-                //noinspection UnusedAssignment
-                updateCallback = batchingCallback;
-            }
-            // These are add/remove ops that are converted to moves. We track their positions until
-            // their respective update operations are processed.
-            final List<PostponedUpdate> postponedUpdates = new ArrayList<>();
-            int posOld = mOldListSize;
-            int posNew = mNewListSize;
-            for (int snakeIndex = mSnakes.size() - 1; snakeIndex >= 0; snakeIndex--) {
-                final Snake snake = mSnakes.get(snakeIndex);
-                final int snakeSize = snake.size;
-                final int endX = snake.x + snakeSize;
-                final int endY = snake.y + snakeSize;
-                if (endX < posOld) {
-                    dispatchRemovals(postponedUpdates, batchingCallback, endX, posOld - endX, endX);
-                }
-
-                if (endY < posNew) {
-                    dispatchAdditions(postponedUpdates, batchingCallback, endX, posNew - endY,
-                            endY);
-                }
-                for (int i = snakeSize - 1; i >= 0; i--) {
-                    if ((mOldItemStatuses[snake.x + i] & FLAG_MASK) == FLAG_CHANGED) {
-                        batchingCallback.onChanged(snake.x + i, 1,
-                                mCallback.getChangePayload(snake.x + i, snake.y + i));
-                    }
-                }
-                posOld = snake.x;
-                posNew = snake.y;
-            }
-            batchingCallback.dispatchLastEvent();
-        }
-
-        private static PostponedUpdate removePostponedUpdate(List<PostponedUpdate> updates,
-                int pos, boolean removal) {
-            for (int i = updates.size() - 1; i >= 0; i--) {
-                final PostponedUpdate update = updates.get(i);
-                if (update.posInOwnerList == pos && update.removal == removal) {
-                    updates.remove(i);
-                    for (int j = i; j < updates.size(); j++) {
-                        // offset other ops since they swapped positions
-                        updates.get(j).currentPos += removal ? 1 : -1;
-                    }
-                    return update;
-                }
-            }
-            return null;
-        }
-
-        private void dispatchAdditions(List<PostponedUpdate> postponedUpdates,
-                ListUpdateCallback updateCallback, int start, int count, int globalIndex) {
-            if (!mDetectMoves) {
-                updateCallback.onInserted(start, count);
-                return;
-            }
-            for (int i = count - 1; i >= 0; i--) {
-                int status = mNewItemStatuses[globalIndex + i] & FLAG_MASK;
-                switch (status) {
-                    case 0: // real addition
-                        updateCallback.onInserted(start, 1);
-                        for (PostponedUpdate update : postponedUpdates) {
-                            update.currentPos += 1;
-                        }
-                        break;
-                    case FLAG_MOVED_CHANGED:
-                    case FLAG_MOVED_NOT_CHANGED:
-                        final int pos = mNewItemStatuses[globalIndex + i] >> FLAG_OFFSET;
-                        final PostponedUpdate update = removePostponedUpdate(postponedUpdates, pos,
-                                true);
-                        // the item was moved from that position
-                        //noinspection ConstantConditions
-                        updateCallback.onMoved(update.currentPos, start);
-                        if (status == FLAG_MOVED_CHANGED) {
-                            // also dispatch a change
-                            updateCallback.onChanged(start, 1,
-                                    mCallback.getChangePayload(pos, globalIndex + i));
-                        }
-                        break;
-                    case FLAG_IGNORE: // ignoring this
-                        postponedUpdates.add(new PostponedUpdate(globalIndex + i, start, false));
-                        break;
-                    default:
-                        throw new IllegalStateException(
-                                "unknown flag for pos " + (globalIndex + i) + " " + Long
-                                        .toBinaryString(status));
-                }
-            }
-        }
-
-        private void dispatchRemovals(List<PostponedUpdate> postponedUpdates,
-                ListUpdateCallback updateCallback, int start, int count, int globalIndex) {
-            if (!mDetectMoves) {
-                updateCallback.onRemoved(start, count);
-                return;
-            }
-            for (int i = count - 1; i >= 0; i--) {
-                final int status = mOldItemStatuses[globalIndex + i] & FLAG_MASK;
-                switch (status) {
-                    case 0: // real removal
-                        updateCallback.onRemoved(start + i, 1);
-                        for (PostponedUpdate update : postponedUpdates) {
-                            update.currentPos -= 1;
-                        }
-                        break;
-                    case FLAG_MOVED_CHANGED:
-                    case FLAG_MOVED_NOT_CHANGED:
-                        final int pos = mOldItemStatuses[globalIndex + i] >> FLAG_OFFSET;
-                        final PostponedUpdate update = removePostponedUpdate(postponedUpdates, pos,
-                                false);
-                        // the item was moved to that position. we do -1 because this is a move not
-                        // add and removing current item offsets the target move by 1
-                        //noinspection ConstantConditions
-                        updateCallback.onMoved(start + i, update.currentPos - 1);
-                        if (status == FLAG_MOVED_CHANGED) {
-                            // also dispatch a change
-                            updateCallback.onChanged(update.currentPos - 1, 1,
-                                    mCallback.getChangePayload(globalIndex + i, pos));
-                        }
-                        break;
-                    case FLAG_IGNORE: // ignoring this
-                        postponedUpdates.add(new PostponedUpdate(globalIndex + i, start + i, true));
-                        break;
-                    default:
-                        throw new IllegalStateException(
-                                "unknown flag for pos " + (globalIndex + i) + " " + Long
-                                        .toBinaryString(status));
-                }
-            }
-        }
-
-        @VisibleForTesting
-        List<Snake> getSnakes() {
-            return mSnakes;
-        }
-    }
-
-    /**
-     * Represents an update that we skipped because it was a move.
-     * <p>
-     * When an update is skipped, it is tracked as other updates are dispatched until the matching
-     * add/remove operation is found at which point the tracked position is used to dispatch the
-     * update.
-     */
-    private static class PostponedUpdate {
-
-        int posInOwnerList;
-
-        int currentPos;
-
-        boolean removal;
-
-        public PostponedUpdate(int posInOwnerList, int currentPos, boolean removal) {
-            this.posInOwnerList = posInOwnerList;
-            this.currentPos = currentPos;
-            this.removal = removal;
-        }
-    }
-}
diff --git a/android/support/v7/util/DiffUtilTest.java b/android/support/v7/util/DiffUtilTest.java
deleted file mode 100644
index 4ac406a..0000000
--- a/android/support/v7/util/DiffUtilTest.java
+++ /dev/null
@@ -1,511 +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 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 android.support.annotation.Nullable;
-import android.support.test.filters.SmallTest;
-
-import org.hamcrest.CoreMatchers;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.UUID;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class DiffUtilTest {
-    private static Random sRand = new Random(System.nanoTime());
-    private List<Item> mBefore = new ArrayList<>();
-    private List<Item> mAfter = new ArrayList<>();
-    private StringBuilder mLog = new StringBuilder();
-
-    private DiffUtil.Callback mCallback = new DiffUtil.Callback() {
-        @Override
-        public int getOldListSize() {
-            return mBefore.size();
-        }
-
-        @Override
-        public int getNewListSize() {
-            return mAfter.size();
-        }
-
-        @Override
-        public boolean areItemsTheSame(int oldItemIndex, int newItemIndex) {
-            return mBefore.get(oldItemIndex).id == mAfter.get(newItemIndex).id;
-        }
-
-        @Override
-        public boolean areContentsTheSame(int oldItemIndex, int newItemIndex) {
-            assertThat(mBefore.get(oldItemIndex).id,
-                    CoreMatchers.equalTo(mAfter.get(newItemIndex).id));
-            return mBefore.get(oldItemIndex).data.equals(mAfter.get(newItemIndex).data);
-        }
-
-        @Nullable
-        @Override
-        public Object getChangePayload(int oldItemIndex, int newItemIndex) {
-            assertThat(mBefore.get(oldItemIndex).id,
-                    CoreMatchers.equalTo(mAfter.get(newItemIndex).id));
-            assertThat(mBefore.get(oldItemIndex).data,
-                    not(CoreMatchers.equalTo(mAfter.get(newItemIndex).data)));
-            return mAfter.get(newItemIndex).payload;
-        }
-    };
-
-    @Rule
-    public TestWatcher mLogOnExceptionWatcher = new TestWatcher() {
-        @Override
-        protected void failed(Throwable e, Description description) {
-            System.err.println(mLog.toString());
-        }
-    };
-
-
-    @Test
-    public void testNoChange() {
-        initWithSize(5);
-        check();
-    }
-
-    @Test
-    public void testAddItems() {
-        initWithSize(2);
-        add(1);
-        check();
-    }
-
-    //@Test
-    //@LargeTest
-    // Used for development
-    public void testRandom() {
-        for (int x = 0; x < 100; x++) {
-            for (int i = 0; i < 100; i++) {
-                for (int j = 2; j < 40; j++) {
-                    testRandom(i, j);
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testGen2() {
-        initWithSize(5);
-        add(5);
-        delete(3);
-        delete(1);
-        check();
-    }
-
-    @Test
-    public void testGen3() {
-        initWithSize(5);
-        add(0);
-        delete(1);
-        delete(3);
-        check();
-    }
-
-    @Test
-    public void testGen4() {
-        initWithSize(5);
-        add(5);
-        add(1);
-        add(4);
-        add(4);
-        check();
-    }
-
-    @Test
-    public void testGen5() {
-        initWithSize(5);
-        delete(0);
-        delete(2);
-        add(0);
-        add(2);
-        check();
-    }
-
-    @Test
-    public void testGen6() {
-        initWithSize(2);
-        delete(0);
-        delete(0);
-        check();
-    }
-
-    @Test
-    public void testGen7() {
-        initWithSize(3);
-        move(2, 0);
-        delete(2);
-        add(2);
-        check();
-    }
-
-    @Test
-    public void testGen8() {
-        initWithSize(3);
-        delete(1);
-        add(0);
-        move(2, 0);
-        check();
-    }
-
-    @Test
-    public void testGen9() {
-        initWithSize(2);
-        add(2);
-        move(0, 2);
-        check();
-    }
-
-    @Test
-    public void testGen10() {
-        initWithSize(3);
-        move(0, 1);
-        move(1, 2);
-        add(0);
-        check();
-    }
-
-    @Test
-    public void testGen11() {
-        initWithSize(4);
-        move(2, 0);
-        move(2, 3);
-        check();
-    }
-
-    @Test
-    public void testGen12() {
-        initWithSize(4);
-        move(3, 0);
-        move(2, 1);
-        check();
-    }
-
-    @Test
-    public void testGen13() {
-        initWithSize(4);
-        move(3, 2);
-        move(0, 3);
-        check();
-    }
-
-    @Test
-    public void testGen14() {
-        initWithSize(4);
-        move(3, 2);
-        add(4);
-        move(0, 4);
-        check();
-    }
-
-    @Test
-    public void testAdd1() {
-        initWithSize(1);
-        add(1);
-        check();
-    }
-
-    @Test
-    public void testMove1() {
-        initWithSize(3);
-        move(0, 2);
-        check();
-    }
-
-    @Test
-    public void tmp() {
-        initWithSize(4);
-        move(0, 2);
-        check();
-    }
-
-    @Test
-    public void testUpdate1() {
-        initWithSize(3);
-        update(2);
-        check();
-    }
-
-    @Test
-    public void testUpdate2() {
-        initWithSize(2);
-        add(1);
-        update(1);
-        update(2);
-        check();
-    }
-
-    @Test
-    public void testDisableMoveDetection() {
-        initWithSize(5);
-        move(0, 4);
-        List<Item> applied = applyUpdates(mBefore, DiffUtil.calculateDiff(mCallback, false));
-        assertThat(applied.size(), is(5));
-        assertThat(applied.get(4).newItem, is(true));
-        assertThat(applied.contains(mBefore.get(0)), is(false));
-    }
-
-    private void testRandom(int initialSize, int operationCount) {
-        mLog.setLength(0);
-        initWithSize(initialSize);
-        for (int i = 0; i < operationCount; i++) {
-            int op = sRand.nextInt(5);
-            switch (op) {
-                case 0:
-                    add(sRand.nextInt(mAfter.size() + 1));
-                    break;
-                case 1:
-                    if (!mAfter.isEmpty()) {
-                        delete(sRand.nextInt(mAfter.size()));
-                    }
-                    break;
-                case 2:
-                    // move
-                    if (mAfter.size() > 0) {
-                        move(sRand.nextInt(mAfter.size()), sRand.nextInt(mAfter.size()));
-                    }
-                    break;
-                case 3:
-                    // update
-                    if (mAfter.size() > 0) {
-                        update(sRand.nextInt(mAfter.size()));
-                    }
-                    break;
-                case 4:
-                    // update with payload
-                    if (mAfter.size() > 0) {
-                        updateWithPayload(sRand.nextInt(mAfter.size()));
-                    }
-                    break;
-            }
-        }
-        check();
-    }
-
-    private void check() {
-        DiffUtil.DiffResult result = DiffUtil.calculateDiff(mCallback);
-        log("before", mBefore);
-        log("after", mAfter);
-        log("snakes", result.getSnakes());
-
-        List<Item> applied = applyUpdates(mBefore, result);
-        assertEquals(applied, mAfter);
-    }
-
-    private void initWithSize(int size) {
-        mBefore.clear();
-        mAfter.clear();
-        for (int i = 0; i < size; i++) {
-            mBefore.add(new Item(false));
-        }
-        mAfter.addAll(mBefore);
-        mLog.append("initWithSize(" + size + ");\n");
-    }
-
-    private void log(String title, List<?> items) {
-        mLog.append(title).append(":").append(items.size()).append("\n");
-        for (Object item : items) {
-            mLog.append("  ").append(item).append("\n");
-        }
-    }
-
-    private void assertEquals(List<Item> applied, List<Item> after) {
-        log("applied", applied);
-
-        String report = mLog.toString();
-        assertThat(report, applied.size(), is(after.size()));
-        for (int i = 0; i < after.size(); i++) {
-            Item item = applied.get(i);
-            if (after.get(i).newItem) {
-                assertThat(report, item.newItem, is(true));
-            } else if (after.get(i).changed) {
-                assertThat(report, item.newItem, is(false));
-                assertThat(report, item.changed, is(true));
-                assertThat(report, item.id, is(after.get(i).id));
-                assertThat(report, item.payload, is(after.get(i).payload));
-            } else {
-                assertThat(report, item, equalTo(after.get(i)));
-            }
-        }
-    }
-
-    private List<Item> applyUpdates(List<Item> before, DiffUtil.DiffResult result) {
-        final List<Item> target = new ArrayList<>();
-        target.addAll(before);
-        result.dispatchUpdatesTo(new ListUpdateCallback() {
-            @Override
-            public void onInserted(int position, int count) {
-                for (int i = 0; i < count; i++) {
-                    target.add(i + position, new Item(true));
-                }
-            }
-
-            @Override
-            public void onRemoved(int position, int count) {
-                for (int i = 0; i < count; i++) {
-                    target.remove(position);
-                }
-            }
-
-            @Override
-            public void onMoved(int fromPosition, int toPosition) {
-                Item item = target.remove(fromPosition);
-                target.add(toPosition, item);
-            }
-
-            @Override
-            public void onChanged(int position, int count, Object payload) {
-                for (int i = 0; i < count; i++) {
-                    int positionInList = position + i;
-                    Item existing = target.get(positionInList);
-                    // make sure we don't update same item twice in callbacks
-                    assertThat(existing.changed, is(false));
-                    assertThat(existing.newItem, is(false));
-                    assertThat(existing.payload, is(nullValue()));
-                    Item replica = new Item(existing);
-                    replica.payload = (String) payload;
-                    replica.changed = true;
-                    target.remove(positionInList);
-                    target.add(positionInList, replica);
-                }
-            }
-        });
-        return target;
-    }
-
-    private void add(int index) {
-        mAfter.add(index, new Item(true));
-        mLog.append("add(").append(index).append(");\n");
-    }
-
-    private void delete(int index) {
-        mAfter.remove(index);
-        mLog.append("delete(").append(index).append(");\n");
-    }
-
-    private void update(int index) {
-        Item existing = mAfter.get(index);
-        if (existing.newItem) {
-            return;//new item cannot be changed
-        }
-        Item replica = new Item(existing);
-        replica.changed = true;
-        // clean the payload since this might be after an updateWithPayload call
-        replica.payload = null;
-        replica.data = UUID.randomUUID().toString();
-        mAfter.remove(index);
-        mAfter.add(index, replica);
-        mLog.append("update(").append(index).append(");\n");
-    }
-
-    private void updateWithPayload(int index) {
-        Item existing = mAfter.get(index);
-        if (existing.newItem) {
-            return;//new item cannot be changed
-        }
-        Item replica = new Item(existing);
-        replica.changed = true;
-        replica.data = UUID.randomUUID().toString();
-        replica.payload = UUID.randomUUID().toString();
-        mAfter.remove(index);
-        mAfter.add(index, replica);
-        mLog.append("update(").append(index).append(");\n");
-    }
-
-    private void move(int from, int to) {
-        Item removed = mAfter.remove(from);
-        mAfter.add(to, removed);
-        mLog.append("move(").append(from).append(",").append(to).append(");\n");
-    }
-
-    static class Item {
-        static long idCounter = 0;
-        final long id;
-        final boolean newItem;
-        boolean changed = false;
-        String payload;
-
-        String data = UUID.randomUUID().toString();
-
-        public Item(boolean newItem) {
-            id = idCounter++;
-            this.newItem = newItem;
-        }
-
-        public Item(Item other) {
-            id = other.id;
-            newItem = other.newItem;
-            changed = other.changed;
-            payload = other.payload;
-            data = other.data;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            Item item = (Item) o;
-
-            if (id != item.id) return false;
-            if (newItem != item.newItem) return false;
-            if (changed != item.changed) return false;
-            if (payload != null ? !payload.equals(item.payload) : item.payload != null) {
-                return false;
-            }
-            return data.equals(item.data);
-
-        }
-
-        @Override
-        public int hashCode() {
-            int result = (int) (id ^ (id >>> 32));
-            result = 31 * result + (newItem ? 1 : 0);
-            result = 31 * result + (changed ? 1 : 0);
-            result = 31 * result + (payload != null ? payload.hashCode() : 0);
-            result = 31 * result + data.hashCode();
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return "Item{" +
-                    "id=" + id +
-                    ", newItem=" + newItem +
-                    ", changed=" + changed +
-                    ", payload='" + payload + '\'' +
-                    ", data='" + data + '\'' +
-                    '}';
-        }
-    }
-}
diff --git a/android/support/v7/util/ListUpdateCallback.java b/android/support/v7/util/ListUpdateCallback.java
deleted file mode 100644
index 2136202..0000000
--- a/android/support/v7/util/ListUpdateCallback.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.v7.util;
-
-/**
- * An interface that can receive Update operations that are applied to a list.
- * <p>
- * This class can be used together with DiffUtil to detect changes between two lists.
- */
-public interface ListUpdateCallback {
-    /**
-     * Called when {@code count} number of items are inserted at the given position.
-     *
-     * @param position The position of the new item.
-     * @param count    The number of items that have been added.
-     */
-    void onInserted(int position, int count);
-
-    /**
-     * Called when {@code count} number of items are removed from the given position.
-     *
-     * @param position The position of the item which has been removed.
-     * @param count    The number of items which have been removed.
-     */
-    void onRemoved(int position, int count);
-
-    /**
-     * Called when an item changes its position in the list.
-     *
-     * @param fromPosition The previous position of the item before the move.
-     * @param toPosition   The new position of the item.
-     */
-    void onMoved(int fromPosition, int toPosition);
-
-    /**
-     * Called when {@code count} number of items are updated at the given position.
-     *
-     * @param position The position of the item which has been updated.
-     * @param count    The number of items which has changed.
-     */
-    void onChanged(int position, int count, Object payload);
-}
diff --git a/android/support/v7/util/MessageThreadUtil.java b/android/support/v7/util/MessageThreadUtil.java
deleted file mode 100644
index a932894..0000000
--- a/android/support/v7/util/MessageThreadUtil.java
+++ /dev/null
@@ -1,283 +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.util;
-
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-class MessageThreadUtil<T> implements ThreadUtil<T> {
-
-    @Override
-    public MainThreadCallback<T> getMainThreadProxy(final MainThreadCallback<T> callback) {
-        return new MainThreadCallback<T>() {
-            final MessageQueue mQueue = new MessageQueue();
-            final private Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
-
-            static final int UPDATE_ITEM_COUNT = 1;
-            static final int ADD_TILE = 2;
-            static final int REMOVE_TILE = 3;
-
-            @Override
-            public void updateItemCount(int generation, int itemCount) {
-                sendMessage(SyncQueueItem.obtainMessage(UPDATE_ITEM_COUNT, generation, itemCount));
-            }
-
-            @Override
-            public void addTile(int generation, TileList.Tile<T> tile) {
-                sendMessage(SyncQueueItem.obtainMessage(ADD_TILE, generation, tile));
-            }
-
-            @Override
-            public void removeTile(int generation, int position) {
-                sendMessage(SyncQueueItem.obtainMessage(REMOVE_TILE, generation, position));
-            }
-
-            private void sendMessage(SyncQueueItem msg) {
-                mQueue.sendMessage(msg);
-                mMainThreadHandler.post(mMainThreadRunnable);
-            }
-
-            private Runnable mMainThreadRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    SyncQueueItem msg = mQueue.next();
-                    while (msg != null) {
-                        switch (msg.what) {
-                            case UPDATE_ITEM_COUNT:
-                                callback.updateItemCount(msg.arg1, msg.arg2);
-                                break;
-                            case ADD_TILE:
-                                //noinspection unchecked
-                                callback.addTile(msg.arg1, (TileList.Tile<T>) msg.data);
-                                break;
-                            case REMOVE_TILE:
-                                callback.removeTile(msg.arg1, msg.arg2);
-                                break;
-                            default:
-                                Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
-                        }
-                        msg = mQueue.next();
-                    }
-                }
-            };
-        };
-    }
-
-    @Override
-    public BackgroundCallback<T> getBackgroundProxy(final BackgroundCallback<T> callback) {
-        return new BackgroundCallback<T>() {
-            final MessageQueue mQueue = new MessageQueue();
-            private final Executor mExecutor = AsyncTask.THREAD_POOL_EXECUTOR;
-            AtomicBoolean mBackgroundRunning = new AtomicBoolean(false);
-
-            static final int REFRESH = 1;
-            static final int UPDATE_RANGE = 2;
-            static final int LOAD_TILE = 3;
-            static final int RECYCLE_TILE = 4;
-
-            @Override
-            public void refresh(int generation) {
-                sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(REFRESH, generation, null));
-            }
-
-            @Override
-            public void updateRange(int rangeStart, int rangeEnd,
-                                    int extRangeStart, int extRangeEnd, int scrollHint) {
-                sendMessageAtFrontOfQueue(SyncQueueItem.obtainMessage(UPDATE_RANGE,
-                        rangeStart, rangeEnd, extRangeStart, extRangeEnd, scrollHint, null));
-            }
-
-            @Override
-            public void loadTile(int position, int scrollHint) {
-                sendMessage(SyncQueueItem.obtainMessage(LOAD_TILE, position, scrollHint));
-            }
-
-            @Override
-            public void recycleTile(TileList.Tile<T> tile) {
-                sendMessage(SyncQueueItem.obtainMessage(RECYCLE_TILE, 0, tile));
-            }
-
-            private void sendMessage(SyncQueueItem msg) {
-                mQueue.sendMessage(msg);
-                maybeExecuteBackgroundRunnable();
-            }
-
-            private void sendMessageAtFrontOfQueue(SyncQueueItem msg) {
-                mQueue.sendMessageAtFrontOfQueue(msg);
-                maybeExecuteBackgroundRunnable();
-            }
-
-            private void maybeExecuteBackgroundRunnable() {
-                if (mBackgroundRunning.compareAndSet(false, true)) {
-                    mExecutor.execute(mBackgroundRunnable);
-                }
-            }
-
-            private Runnable mBackgroundRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    while (true) {
-                        SyncQueueItem msg = mQueue.next();
-                        if (msg == null) {
-                            break;
-                        }
-                        switch (msg.what) {
-                            case REFRESH:
-                                mQueue.removeMessages(REFRESH);
-                                callback.refresh(msg.arg1);
-                                break;
-                            case UPDATE_RANGE:
-                                mQueue.removeMessages(UPDATE_RANGE);
-                                mQueue.removeMessages(LOAD_TILE);
-                                callback.updateRange(
-                                        msg.arg1, msg.arg2, msg.arg3, msg.arg4, msg.arg5);
-                                break;
-                            case LOAD_TILE:
-                                callback.loadTile(msg.arg1, msg.arg2);
-                                break;
-                            case RECYCLE_TILE:
-                                //noinspection unchecked
-                                callback.recycleTile((TileList.Tile<T>) msg.data);
-                                break;
-                            default:
-                                Log.e("ThreadUtil", "Unsupported message, what=" + msg.what);
-                        }
-                    }
-                    mBackgroundRunning.set(false);
-                }
-            };
-        };
-    }
-
-    /**
-     * Replica of android.os.Message. Unfortunately, cannot use it without a Handler and don't want
-     * to create a thread just for this component.
-     */
-    static class SyncQueueItem {
-
-        private static SyncQueueItem sPool;
-        private static final Object sPoolLock = new Object();
-        private SyncQueueItem next;
-        public int what;
-        public int arg1;
-        public int arg2;
-        public int arg3;
-        public int arg4;
-        public int arg5;
-        public Object data;
-
-        void recycle() {
-            next = null;
-            what = arg1 = arg2 = arg3 = arg4 = arg5 = 0;
-            data = null;
-            synchronized (sPoolLock) {
-                if (sPool != null) {
-                    next = sPool;
-                }
-                sPool = this;
-            }
-        }
-
-        static SyncQueueItem obtainMessage(int what, int arg1, int arg2, int arg3, int arg4,
-                                           int arg5, Object data) {
-            synchronized (sPoolLock) {
-                final SyncQueueItem item;
-                if (sPool == null) {
-                    item = new SyncQueueItem();
-                } else {
-                    item = sPool;
-                    sPool = sPool.next;
-                    item.next = null;
-                }
-                item.what = what;
-                item.arg1 = arg1;
-                item.arg2 = arg2;
-                item.arg3 = arg3;
-                item.arg4 = arg4;
-                item.arg5 = arg5;
-                item.data = data;
-                return item;
-            }
-        }
-
-        static SyncQueueItem obtainMessage(int what, int arg1, int arg2) {
-            return obtainMessage(what, arg1, arg2, 0, 0, 0, null);
-        }
-
-        static SyncQueueItem obtainMessage(int what, int arg1, Object data) {
-            return obtainMessage(what, arg1, 0, 0, 0, 0, data);
-        }
-    }
-
-    static class MessageQueue {
-
-        private SyncQueueItem mRoot;
-
-        synchronized SyncQueueItem next() {
-            if (mRoot == null) {
-                return null;
-            }
-            final SyncQueueItem next = mRoot;
-            mRoot = mRoot.next;
-            return next;
-        }
-
-        synchronized void sendMessageAtFrontOfQueue(SyncQueueItem item) {
-            item.next = mRoot;
-            mRoot = item;
-        }
-
-        synchronized void sendMessage(SyncQueueItem item) {
-            if (mRoot == null) {
-                mRoot = item;
-                return;
-            }
-            SyncQueueItem last = mRoot;
-            while (last.next != null) {
-                last = last.next;
-            }
-            last.next = item;
-        }
-
-        synchronized void removeMessages(int what) {
-            while (mRoot != null && mRoot.what == what) {
-                SyncQueueItem item = mRoot;
-                mRoot = mRoot.next;
-                item.recycle();
-            }
-            if (mRoot != null) {
-                SyncQueueItem prev = mRoot;
-                SyncQueueItem item = prev.next;
-                while (item != null) {
-                    SyncQueueItem next = item.next;
-                    if (item.what == what) {
-                        prev.next = next;
-                        item.recycle();
-                    } else {
-                        prev = item;
-                    }
-                    item = next;
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v7/util/SortedList.java b/android/support/v7/util/SortedList.java
deleted file mode 100644
index bd07b01..0000000
--- a/android/support/v7/util/SortedList.java
+++ /dev/null
@@ -1,1010 +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.util;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-
-/**
- * A Sorted list implementation that can keep items in order and also notify for changes in the
- * list
- * such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter
- * RecyclerView.Adapter}.
- * <p>
- * It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses
- * binary search to retrieve items. If the sorting criteria of your items may change, make sure you
- * call appropriate methods while editing them to avoid data inconsistencies.
- * <p>
- * You can control the order of items and change notifications via the {@link Callback} parameter.
- */
-@SuppressWarnings("unchecked")
-public class SortedList<T> {
-
-    /**
-     * Used by {@link #indexOf(Object)} when he item cannot be found in the list.
-     */
-    public static final int INVALID_POSITION = -1;
-
-    private static final int MIN_CAPACITY = 10;
-    private static final int CAPACITY_GROWTH = MIN_CAPACITY;
-    private static final int INSERTION = 1;
-    private static final int DELETION = 1 << 1;
-    private static final int LOOKUP = 1 << 2;
-    T[] mData;
-
-    /**
-     * A reference to the previous set of data that is kept during a mutation operation (addAll or
-     * replaceAll).
-     */
-    private T[] mOldData;
-
-    /**
-     * The current index into mOldData that has not yet been processed during a mutation operation
-     * (addAll or replaceAll).
-     */
-    private int mOldDataStart;
-    private int mOldDataSize;
-
-    /**
-     * The current index into the new data that has not yet been processed during a mutation
-     * operation (addAll or replaceAll).
-     */
-    private int mNewDataStart;
-
-    /**
-     * The callback instance that controls the behavior of the SortedList and get notified when
-     * changes happen.
-     */
-    private Callback mCallback;
-
-    private BatchedCallback mBatchedCallback;
-
-    private int mSize;
-    private final Class<T> mTClass;
-
-    /**
-     * Creates a new SortedList of type T.
-     *
-     * @param klass    The class of the contents of the SortedList.
-     * @param callback The callback that controls the behavior of SortedList.
-     */
-    public SortedList(Class<T> klass, Callback<T> callback) {
-        this(klass, callback, MIN_CAPACITY);
-    }
-
-    /**
-     * Creates a new SortedList of type T.
-     *
-     * @param klass           The class of the contents of the SortedList.
-     * @param callback        The callback that controls the behavior of SortedList.
-     * @param initialCapacity The initial capacity to hold items.
-     */
-    public SortedList(Class<T> klass, Callback<T> callback, int initialCapacity) {
-        mTClass = klass;
-        mData = (T[]) Array.newInstance(klass, initialCapacity);
-        mCallback = callback;
-        mSize = 0;
-    }
-
-    /**
-     * The number of items in the list.
-     *
-     * @return The number of items in the list.
-     */
-    public int size() {
-        return mSize;
-    }
-
-    /**
-     * Adds the given item to the list. If this is a new item, SortedList calls
-     * {@link Callback#onInserted(int, int)}.
-     * <p>
-     * If the item already exists in the list and its sorting criteria is not changed, it is
-     * replaced with the existing Item. SortedList uses
-     * {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item
-     * and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should
-     * call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the
-     * reference to the old item and puts the new item into the backing array even if
-     * {@link Callback#areContentsTheSame(Object, Object)} returns false.
-     * <p>
-     * If the sorting criteria of the item is changed, SortedList won't be able to find
-     * its duplicate in the list which will result in having a duplicate of the Item in the list.
-     * If you need to update sorting criteria of an item that already exists in the list,
-     * use {@link #updateItemAt(int, Object)}. You can find the index of the item using
-     * {@link #indexOf(Object)} before you update the object.
-     *
-     * @param item The item to be added into the list.
-     *
-     * @return The index of the newly added item.
-     * @see Callback#compare(Object, Object)
-     * @see Callback#areItemsTheSame(Object, Object)
-     * @see Callback#areContentsTheSame(Object, Object)}
-     */
-    public int add(T item) {
-        throwIfInMutationOperation();
-        return add(item, true);
-    }
-
-    /**
-     * Adds the given items to the list. Equivalent to calling {@link SortedList#add} in a loop,
-     * except the callback events may be in a different order/granularity since addAll can batch
-     * them for better performance.
-     * <p>
-     * If allowed, will reference the input array during, and possibly after, the operation to avoid
-     * extra memory allocation, in which case you should not continue to reference or modify the
-     * array yourself.
-     * <p>
-     * @param items Array of items to be added into the list.
-     * @param mayModifyInput If true, SortedList is allowed to modify and permanently reference the
-     *                       input array.
-     * @see SortedList#addAll(T[] items)
-     */
-    public void addAll(T[] items, boolean mayModifyInput) {
-        throwIfInMutationOperation();
-        if (items.length == 0) {
-            return;
-        }
-
-        if (mayModifyInput) {
-            addAllInternal(items);
-        } else {
-            addAllInternal(copyArray(items));
-        }
-    }
-
-    /**
-     * Adds the given items to the list. Does not modify or retain the input.
-     *
-     * @see SortedList#addAll(T[] items, boolean mayModifyInput)
-     *
-     * @param items Array of items to be added into the list.
-     */
-    public void addAll(T... items) {
-        addAll(items, false);
-    }
-
-    /**
-     * Adds the given items to the list. Does not modify or retain the input.
-     *
-     * @see SortedList#addAll(T[] items, boolean mayModifyInput)
-     *
-     * @param items Collection of items to be added into the list.
-     */
-    public void addAll(Collection<T> items) {
-        T[] copy = (T[]) Array.newInstance(mTClass, items.size());
-        addAll(items.toArray(copy), true);
-    }
-
-    /**
-     * Replaces the current items with the new items, dispatching {@link ListUpdateCallback} events
-     * for each change detected as appropriate.
-     * <p>
-     * If allowed, will reference the input array during, and possibly after, the operation to avoid
-     * extra memory allocation, in which case you should not continue to reference or modify the
-     * array yourself.
-     * <p>
-     * Note: this method does not detect moves or dispatch
-     * {@link ListUpdateCallback#onMoved(int, int)} events. It instead treats moves as a remove
-     * followed by an add and therefore dispatches {@link ListUpdateCallback#onRemoved(int, int)}
-     * and {@link ListUpdateCallback#onRemoved(int, int)} events.  See {@link DiffUtil} if you want
-     * your implementation to dispatch move events.
-     * <p>
-     * @param items Array of items to replace current items.
-     * @param mayModifyInput If true, SortedList is allowed to modify and permanently reference the
-     *                       input array.
-     * @see #replaceAll(T[])
-     */
-    public void replaceAll(@NonNull T[] items, boolean mayModifyInput) {
-        throwIfInMutationOperation();
-
-        if (mayModifyInput) {
-            replaceAllInternal(items);
-        } else {
-            replaceAllInternal(copyArray(items));
-        }
-    }
-
-    /**
-     * Replaces the current items with the new items, dispatching {@link ListUpdateCallback} events
-     * for each change detected as appropriate.  Does not modify or retain the input.
-     *
-     * @see #replaceAll(T[], boolean)
-     *
-     * @param items Array of items to replace current items.
-     */
-    public void replaceAll(@NonNull T... items) {
-        replaceAll(items, false);
-    }
-
-    /**
-     * Replaces the current items with the new items, dispatching {@link ListUpdateCallback} events
-     * for each change detected as appropriate. Does not modify or retain the input.
-     *
-     * @see #replaceAll(T[], boolean)
-     *
-     * @param items Array of items to replace current items.
-     */
-    public void replaceAll(@NonNull Collection<T> items) {
-        T[] copy = (T[]) Array.newInstance(mTClass, items.size());
-        replaceAll(items.toArray(copy), true);
-    }
-
-    private void addAllInternal(T[] newItems) {
-        if (newItems.length < 1) {
-            return;
-        }
-
-        final int newSize = sortAndDedup(newItems);
-
-        if (mSize == 0) {
-            mData = newItems;
-            mSize = newSize;
-            mCallback.onInserted(0, newSize);
-        } else {
-            merge(newItems, newSize);
-        }
-    }
-
-    private void replaceAllInternal(@NonNull T[] newData) {
-        final boolean forceBatchedUpdates = !(mCallback instanceof BatchedCallback);
-        if (forceBatchedUpdates) {
-            beginBatchedUpdates();
-        }
-
-        mOldDataStart = 0;
-        mOldDataSize = mSize;
-        mOldData = mData;
-
-        mNewDataStart = 0;
-        int newSize = sortAndDedup(newData);
-        mData = (T[]) Array.newInstance(mTClass, newSize);
-
-        while (mNewDataStart < newSize || mOldDataStart < mOldDataSize) {
-            if (mOldDataStart >= mOldDataSize) {
-                int insertIndex = mNewDataStart;
-                int itemCount = newSize - mNewDataStart;
-                System.arraycopy(newData, insertIndex, mData, insertIndex, itemCount);
-                mNewDataStart += itemCount;
-                mSize += itemCount;
-                mCallback.onInserted(insertIndex, itemCount);
-                break;
-            }
-            if (mNewDataStart >= newSize) {
-                int itemCount = mOldDataSize - mOldDataStart;
-                mSize -= itemCount;
-                mCallback.onRemoved(mNewDataStart, itemCount);
-                break;
-            }
-
-            T oldItem = mOldData[mOldDataStart];
-            T newItem = newData[mNewDataStart];
-
-            int result = mCallback.compare(oldItem, newItem);
-            if (result < 0) {
-                replaceAllRemove();
-            } else if (result > 0) {
-                replaceAllInsert(newItem);
-            } else {
-                if (!mCallback.areItemsTheSame(oldItem, newItem)) {
-                    // The items aren't the same even though they were supposed to occupy the same
-                    // place, so both notify to remove and add an item in the current location.
-                    replaceAllRemove();
-                    replaceAllInsert(newItem);
-                } else {
-                    mData[mNewDataStart] = newItem;
-                    mOldDataStart++;
-                    mNewDataStart++;
-                    if (!mCallback.areContentsTheSame(oldItem, newItem)) {
-                        // The item is the same but the contents have changed, so notify that an
-                        // onChanged event has occurred.
-                        mCallback.onChanged(mNewDataStart - 1, 1,
-                                mCallback.getChangePayload(oldItem, newItem));
-                    }
-                }
-            }
-        }
-
-        mOldData = null;
-
-        if (forceBatchedUpdates) {
-            endBatchedUpdates();
-        }
-    }
-
-    private void replaceAllInsert(T newItem) {
-        mData[mNewDataStart] = newItem;
-        mNewDataStart++;
-        mSize++;
-        mCallback.onInserted(mNewDataStart - 1, 1);
-    }
-
-    private void replaceAllRemove() {
-        mSize--;
-        mOldDataStart++;
-        mCallback.onRemoved(mNewDataStart, 1);
-    }
-
-    /**
-     * Sorts and removes duplicate items, leaving only the last item from each group of "same"
-     * items. Move the remaining items to the beginning of the array.
-     *
-     * @return Number of deduplicated items at the beginning of the array.
-     */
-    private int sortAndDedup(@NonNull T[] items) {
-        if (items.length == 0) {
-            return 0;
-        }
-
-        // Arrays.sort is stable.
-        Arrays.sort(items, mCallback);
-
-        // Keep track of the range of equal items at the end of the output.
-        // Start with the range containing just the first item.
-        int rangeStart = 0;
-        int rangeEnd = 1;
-
-        for (int i = 1; i < items.length; ++i) {
-            T currentItem = items[i];
-
-            int compare = mCallback.compare(items[rangeStart], currentItem);
-
-            if (compare == 0) {
-                // The range of equal items continues, update it.
-                final int sameItemPos = findSameItem(currentItem, items, rangeStart, rangeEnd);
-                if (sameItemPos != INVALID_POSITION) {
-                    // Replace the duplicate item.
-                    items[sameItemPos] = currentItem;
-                } else {
-                    // Expand the range.
-                    if (rangeEnd != i) {  // Avoid redundant copy.
-                        items[rangeEnd] = currentItem;
-                    }
-                    rangeEnd++;
-                }
-            } else {
-                // The range has ended. Reset it to contain just the current item.
-                if (rangeEnd != i) {  // Avoid redundant copy.
-                    items[rangeEnd] = currentItem;
-                }
-                rangeStart = rangeEnd++;
-            }
-        }
-        return rangeEnd;
-    }
-
-
-    private int findSameItem(T item, T[] items, int from, int to) {
-        for (int pos = from; pos < to; pos++) {
-            if (mCallback.areItemsTheSame(items[pos], item)) {
-                return pos;
-            }
-        }
-        return INVALID_POSITION;
-    }
-
-    /**
-     * This method assumes that newItems are sorted and deduplicated.
-     */
-    private void merge(T[] newData, int newDataSize) {
-        final boolean forceBatchedUpdates = !(mCallback instanceof BatchedCallback);
-        if (forceBatchedUpdates) {
-            beginBatchedUpdates();
-        }
-
-        mOldData = mData;
-        mOldDataStart = 0;
-        mOldDataSize = mSize;
-
-        final int mergedCapacity = mSize + newDataSize + CAPACITY_GROWTH;
-        mData = (T[]) Array.newInstance(mTClass, mergedCapacity);
-        mNewDataStart = 0;
-
-        int newDataStart = 0;
-        while (mOldDataStart < mOldDataSize || newDataStart < newDataSize) {
-            if (mOldDataStart == mOldDataSize) {
-                // No more old items, copy the remaining new items.
-                int itemCount = newDataSize - newDataStart;
-                System.arraycopy(newData, newDataStart, mData, mNewDataStart, itemCount);
-                mNewDataStart += itemCount;
-                mSize += itemCount;
-                mCallback.onInserted(mNewDataStart - itemCount, itemCount);
-                break;
-            }
-
-            if (newDataStart == newDataSize) {
-                // No more new items, copy the remaining old items.
-                int itemCount = mOldDataSize - mOldDataStart;
-                System.arraycopy(mOldData, mOldDataStart, mData, mNewDataStart, itemCount);
-                mNewDataStart += itemCount;
-                break;
-            }
-
-            T oldItem = mOldData[mOldDataStart];
-            T newItem = newData[newDataStart];
-            int compare = mCallback.compare(oldItem, newItem);
-            if (compare > 0) {
-                // New item is lower, output it.
-                mData[mNewDataStart++] = newItem;
-                mSize++;
-                newDataStart++;
-                mCallback.onInserted(mNewDataStart - 1, 1);
-            } else if (compare == 0 && mCallback.areItemsTheSame(oldItem, newItem)) {
-                // Items are the same. Output the new item, but consume both.
-                mData[mNewDataStart++] = newItem;
-                newDataStart++;
-                mOldDataStart++;
-                if (!mCallback.areContentsTheSame(oldItem, newItem)) {
-                    mCallback.onChanged(mNewDataStart - 1, 1,
-                            mCallback.getChangePayload(oldItem, newItem));
-                }
-            } else {
-                // Old item is lower than or equal to (but not the same as the new). Output it.
-                // New item with the same sort order will be inserted later.
-                mData[mNewDataStart++] = oldItem;
-                mOldDataStart++;
-            }
-        }
-
-        mOldData = null;
-
-        if (forceBatchedUpdates) {
-            endBatchedUpdates();
-        }
-    }
-
-    /**
-     * Throws an exception if called while we are in the middle of a mutation operation (addAll or
-     * replaceAll).
-     */
-    private void throwIfInMutationOperation() {
-        if (mOldData != null) {
-            throw new IllegalStateException("Data cannot be mutated in the middle of a batch "
-                    + "update operation such as addAll or replaceAll.");
-        }
-    }
-
-    /**
-     * Batches adapter updates that happen after calling this method and before calling
-     * {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop
-     * and they are placed into consecutive indices, SortedList calls
-     * {@link Callback#onInserted(int, int)} only once with the proper item count. If an event
-     * cannot be merged with the previous event, the previous event is dispatched
-     * to the callback instantly.
-     * <p>
-     * After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()}
-     * which will dispatch any deferred data change event to the current callback.
-     * <p>
-     * A sample implementation may look like this:
-     * <pre>
-     *     mSortedList.beginBatchedUpdates();
-     *     try {
-     *         mSortedList.add(item1)
-     *         mSortedList.add(item2)
-     *         mSortedList.remove(item3)
-     *         ...
-     *     } finally {
-     *         mSortedList.endBatchedUpdates();
-     *     }
-     * </pre>
-     * <p>
-     * Instead of using this method to batch calls, you can use a Callback that extends
-     * {@link BatchedCallback}. In that case, you must make sure that you are manually calling
-     * {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes.
-     * Failing to do so may create data inconsistencies with the Callback.
-     * <p>
-     * If the current Callback is an instance of {@link BatchedCallback}, calling this method
-     * has no effect.
-     */
-    public void beginBatchedUpdates() {
-        throwIfInMutationOperation();
-        if (mCallback instanceof BatchedCallback) {
-            return;
-        }
-        if (mBatchedCallback == null) {
-            mBatchedCallback = new BatchedCallback(mCallback);
-        }
-        mCallback = mBatchedCallback;
-    }
-
-    /**
-     * Ends the update transaction and dispatches any remaining event to the callback.
-     */
-    public void endBatchedUpdates() {
-        throwIfInMutationOperation();
-        if (mCallback instanceof BatchedCallback) {
-            ((BatchedCallback) mCallback).dispatchLastEvent();
-        }
-        if (mCallback == mBatchedCallback) {
-            mCallback = mBatchedCallback.mWrappedCallback;
-        }
-    }
-
-    private int add(T item, boolean notify) {
-        int index = findIndexOf(item, mData, 0, mSize, INSERTION);
-        if (index == INVALID_POSITION) {
-            index = 0;
-        } else if (index < mSize) {
-            T existing = mData[index];
-            if (mCallback.areItemsTheSame(existing, item)) {
-                if (mCallback.areContentsTheSame(existing, item)) {
-                    //no change but still replace the item
-                    mData[index] = item;
-                    return index;
-                } else {
-                    mData[index] = item;
-                    mCallback.onChanged(index, 1, mCallback.getChangePayload(existing, item));
-                    return index;
-                }
-            }
-        }
-        addToData(index, item);
-        if (notify) {
-            mCallback.onInserted(index, 1);
-        }
-        return index;
-    }
-
-    /**
-     * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}.
-     *
-     * @param item The item to be removed from the list.
-     *
-     * @return True if item is removed, false if item cannot be found in the list.
-     */
-    public boolean remove(T item) {
-        throwIfInMutationOperation();
-        return remove(item, true);
-    }
-
-    /**
-     * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}.
-     *
-     * @param index The index of the item to be removed.
-     *
-     * @return The removed item.
-     */
-    public T removeItemAt(int index) {
-        throwIfInMutationOperation();
-        T item = get(index);
-        removeItemAtIndex(index, true);
-        return item;
-    }
-
-    private boolean remove(T item, boolean notify) {
-        int index = findIndexOf(item, mData, 0, mSize, DELETION);
-        if (index == INVALID_POSITION) {
-            return false;
-        }
-        removeItemAtIndex(index, notify);
-        return true;
-    }
-
-    private void removeItemAtIndex(int index, boolean notify) {
-        System.arraycopy(mData, index + 1, mData, index, mSize - index - 1);
-        mSize--;
-        mData[mSize] = null;
-        if (notify) {
-            mCallback.onRemoved(index, 1);
-        }
-    }
-
-    /**
-     * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or
-     * {@link Callback#onMoved(int, int)} if necessary.
-     * <p>
-     * You can use this method if you need to change an existing Item such that its position in the
-     * list may change.
-     * <p>
-     * If the new object is a different object (<code>get(index) != item</code>) and
-     * {@link Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList
-     * avoids calling {@link Callback#onChanged(int, int)} otherwise it calls
-     * {@link Callback#onChanged(int, int)}.
-     * <p>
-     * If the new position of the item is different than the provided <code>index</code>,
-     * SortedList
-     * calls {@link Callback#onMoved(int, int)}.
-     *
-     * @param index The index of the item to replace
-     * @param item  The item to replace the item at the given Index.
-     * @see #add(Object)
-     */
-    public void updateItemAt(int index, T item) {
-        throwIfInMutationOperation();
-        final T existing = get(index);
-        // assume changed if the same object is given back
-        boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item);
-        if (existing != item) {
-            // different items, we can use comparison and may avoid lookup
-            final int cmp = mCallback.compare(existing, item);
-            if (cmp == 0) {
-                mData[index] = item;
-                if (contentsChanged) {
-                    mCallback.onChanged(index, 1, mCallback.getChangePayload(existing, item));
-                }
-                return;
-            }
-        }
-        if (contentsChanged) {
-            mCallback.onChanged(index, 1, mCallback.getChangePayload(existing, item));
-        }
-        // TODO this done in 1 pass to avoid shifting twice.
-        removeItemAtIndex(index, false);
-        int newIndex = add(item, false);
-        if (index != newIndex) {
-            mCallback.onMoved(index, newIndex);
-        }
-    }
-
-    /**
-     * This method can be used to recalculate the position of the item at the given index, without
-     * triggering an {@link Callback#onChanged(int, int)} callback.
-     * <p>
-     * If you are editing objects in the list such that their position in the list may change but
-     * you don't want to trigger an onChange animation, you can use this method to re-position it.
-     * If the item changes position, SortedList will call {@link Callback#onMoved(int, int)}
-     * without
-     * calling {@link Callback#onChanged(int, int)}.
-     * <p>
-     * A sample usage may look like:
-     *
-     * <pre>
-     *     final int position = mSortedList.indexOf(item);
-     *     item.incrementPriority(); // assume items are sorted by priority
-     *     mSortedList.recalculatePositionOfItemAt(position);
-     * </pre>
-     * In the example above, because the sorting criteria of the item has been changed,
-     * mSortedList.indexOf(item) will not be able to find the item. This is why the code above
-     * first
-     * gets the position before editing the item, edits it and informs the SortedList that item
-     * should be repositioned.
-     *
-     * @param index The current index of the Item whose position should be re-calculated.
-     * @see #updateItemAt(int, Object)
-     * @see #add(Object)
-     */
-    public void recalculatePositionOfItemAt(int index) {
-        throwIfInMutationOperation();
-        // TODO can be improved
-        final T item = get(index);
-        removeItemAtIndex(index, false);
-        int newIndex = add(item, false);
-        if (index != newIndex) {
-            mCallback.onMoved(index, newIndex);
-        }
-    }
-
-    /**
-     * Returns the item at the given index.
-     *
-     * @param index The index of the item to retrieve.
-     *
-     * @return The item at the given index.
-     * @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the
-     *                                             size of the list.
-     */
-    public T get(int index) throws IndexOutOfBoundsException {
-        if (index >= mSize || index < 0) {
-            throw new IndexOutOfBoundsException("Asked to get item at " + index + " but size is "
-                    + mSize);
-        }
-        if (mOldData != null) {
-            // The call is made from a callback during addAll execution. The data is split
-            // between mData and mOldData.
-            if (index >= mNewDataStart) {
-                return mOldData[index - mNewDataStart + mOldDataStart];
-            }
-        }
-        return mData[index];
-    }
-
-    /**
-     * Returns the position of the provided item.
-     *
-     * @param item The item to query for position.
-     *
-     * @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the
-     * list.
-     */
-    public int indexOf(T item) {
-        if (mOldData != null) {
-            int index = findIndexOf(item, mData, 0, mNewDataStart, LOOKUP);
-            if (index != INVALID_POSITION) {
-                return index;
-            }
-            index = findIndexOf(item, mOldData, mOldDataStart, mOldDataSize, LOOKUP);
-            if (index != INVALID_POSITION) {
-                return index - mOldDataStart + mNewDataStart;
-            }
-            return INVALID_POSITION;
-        }
-        return findIndexOf(item, mData, 0, mSize, LOOKUP);
-    }
-
-    private int findIndexOf(T item, T[] mData, int left, int right, int reason) {
-        while (left < right) {
-            final int middle = (left + right) / 2;
-            T myItem = mData[middle];
-            final int cmp = mCallback.compare(myItem, item);
-            if (cmp < 0) {
-                left = middle + 1;
-            } else if (cmp == 0) {
-                if (mCallback.areItemsTheSame(myItem, item)) {
-                    return middle;
-                } else {
-                    int exact = linearEqualitySearch(item, middle, left, right);
-                    if (reason == INSERTION) {
-                        return exact == INVALID_POSITION ? middle : exact;
-                    } else {
-                        return exact;
-                    }
-                }
-            } else {
-                right = middle;
-            }
-        }
-        return reason == INSERTION ? left : INVALID_POSITION;
-    }
-
-    private int linearEqualitySearch(T item, int middle, int left, int right) {
-        // go left
-        for (int next = middle - 1; next >= left; next--) {
-            T nextItem = mData[next];
-            int cmp = mCallback.compare(nextItem, item);
-            if (cmp != 0) {
-                break;
-            }
-            if (mCallback.areItemsTheSame(nextItem, item)) {
-                return next;
-            }
-        }
-        for (int next = middle + 1; next < right; next++) {
-            T nextItem = mData[next];
-            int cmp = mCallback.compare(nextItem, item);
-            if (cmp != 0) {
-                break;
-            }
-            if (mCallback.areItemsTheSame(nextItem, item)) {
-                return next;
-            }
-        }
-        return INVALID_POSITION;
-    }
-
-    private void addToData(int index, T item) {
-        if (index > mSize) {
-            throw new IndexOutOfBoundsException(
-                    "cannot add item to " + index + " because size is " + mSize);
-        }
-        if (mSize == mData.length) {
-            // we are at the limit enlarge
-            T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH);
-            System.arraycopy(mData, 0, newData, 0, index);
-            newData[index] = item;
-            System.arraycopy(mData, index, newData, index + 1, mSize - index);
-            mData = newData;
-        } else {
-            // just shift, we fit
-            System.arraycopy(mData, index, mData, index + 1, mSize - index);
-            mData[index] = item;
-        }
-        mSize++;
-    }
-
-    private T[] copyArray(T[] items) {
-        T[] copy = (T[]) Array.newInstance(mTClass, items.length);
-        System.arraycopy(items, 0, copy, 0, items.length);
-        return copy;
-    }
-
-    /**
-     * Removes all items from the SortedList.
-     */
-    public void clear() {
-        throwIfInMutationOperation();
-        if (mSize == 0) {
-            return;
-        }
-        final int prevSize = mSize;
-        Arrays.fill(mData, 0, prevSize, null);
-        mSize = 0;
-        mCallback.onRemoved(0, prevSize);
-    }
-
-    /**
-     * The class that controls the behavior of the {@link SortedList}.
-     * <p>
-     * It defines how items should be sorted and how duplicates should be handled.
-     * <p>
-     * SortedList calls the callback methods on this class to notify changes about the underlying
-     * data.
-     */
-    public static abstract class Callback<T2> implements Comparator<T2>, ListUpdateCallback {
-
-        /**
-         * Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and
-         * return how they should be ordered.
-         *
-         * @param o1 The first object to compare.
-         * @param o2 The second object to compare.
-         *
-         * @return a negative integer, zero, or a positive integer as the
-         * first argument is less than, equal to, or greater than the
-         * second.
-         */
-        @Override
-        abstract public int compare(T2 o1, T2 o2);
-
-        /**
-         * Called by the SortedList when the item at the given position is updated.
-         *
-         * @param position The position of the item which has been updated.
-         * @param count    The number of items which has changed.
-         */
-        abstract public void onChanged(int position, int count);
-
-        @Override
-        public void onChanged(int position, int count, Object payload) {
-            onChanged(position, count);
-        }
-
-        /**
-         * Called by the SortedList when it wants to check whether two items have the same data
-         * or not. SortedList uses this information to decide whether it should call
-         * {@link #onChanged(int, int)} or not.
-         * <p>
-         * SortedList uses this method to check equality instead of {@link Object#equals(Object)}
-         * so
-         * that you can change its behavior depending on your UI.
-         * <p>
-         * For example, if you are using SortedList with a
-         * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should
-         * return whether the items' visual representations are the same or not.
-         *
-         * @param oldItem The previous representation of the object.
-         * @param newItem The new object that replaces the previous one.
-         *
-         * @return True if the contents of the items are the same or false if they are different.
-         */
-        abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);
-
-        /**
-         * Called by the SortedList to decide whether two objects represent the same Item or not.
-         * <p>
-         * For example, if your items have unique ids, this method should check their equality.
-         *
-         * @param item1 The first item to check.
-         * @param item2 The second item to check.
-         *
-         * @return True if the two items represent the same object or false if they are different.
-         */
-        abstract public boolean areItemsTheSame(T2 item1, T2 item2);
-
-        /**
-         * When {@link #areItemsTheSame(T2, T2)} returns {@code true} for two items and
-         * {@link #areContentsTheSame(T2, T2)} returns false for them, {@link Callback} calls this
-         * method to get a payload about the change.
-         * <p>
-         * For example, if you are using {@link Callback} with
-         * {@link android.support.v7.widget.RecyclerView}, you can return the particular field that
-         * changed in the item and your
-         * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
-         * information to run the correct animation.
-         * <p>
-         * Default implementation returns {@code null}.
-         *
-         * @param item1 The first item to check.
-         * @param item2 The second item to check.
-         * @return A payload object that represents the changes between the two items.
-         */
-        @Nullable
-        public Object getChangePayload(T2 item1, T2 item2) {
-            return null;
-        }
-    }
-
-    /**
-     * A callback implementation that can batch notify events dispatched by the SortedList.
-     * <p>
-     * This class can be useful if you want to do multiple operations on a SortedList but don't
-     * want to dispatch each event one by one, which may result in a performance issue.
-     * <p>
-     * For example, if you are going to add multiple items to a SortedList, BatchedCallback call
-     * convert individual <code>onInserted(index, 1)</code> calls into one
-     * <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
-     * can help RecyclerView resolve changes much more easily.
-     * <p>
-     * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback
-     * dispatches them as soon as such case is detected. After your edits on the SortedList is
-     * complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
-     * all changes to the Callback.
-     */
-    public static class BatchedCallback<T2> extends Callback<T2> {
-
-        final Callback<T2> mWrappedCallback;
-        private final BatchingListUpdateCallback mBatchingListUpdateCallback;
-        /**
-         * Creates a new BatchedCallback that wraps the provided Callback.
-         *
-         * @param wrappedCallback The Callback which should received the data change callbacks.
-         *                        Other method calls (e.g. {@link #compare(Object, Object)} from
-         *                        the SortedList are directly forwarded to this Callback.
-         */
-        public BatchedCallback(Callback<T2> wrappedCallback) {
-            mWrappedCallback = wrappedCallback;
-            mBatchingListUpdateCallback = new BatchingListUpdateCallback(mWrappedCallback);
-        }
-
-        @Override
-        public int compare(T2 o1, T2 o2) {
-            return mWrappedCallback.compare(o1, o2);
-        }
-
-        @Override
-        public void onInserted(int position, int count) {
-            mBatchingListUpdateCallback.onInserted(position, count);
-        }
-
-        @Override
-        public void onRemoved(int position, int count) {
-            mBatchingListUpdateCallback.onRemoved(position, count);
-        }
-
-        @Override
-        public void onMoved(int fromPosition, int toPosition) {
-            mBatchingListUpdateCallback.onMoved(fromPosition, toPosition);
-        }
-
-        @Override
-        public void onChanged(int position, int count) {
-            mBatchingListUpdateCallback.onChanged(position, count, null);
-        }
-
-        @Override
-        public void onChanged(int position, int count, Object payload) {
-            mBatchingListUpdateCallback.onChanged(position, count, payload);
-        }
-
-        @Override
-        public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
-            return mWrappedCallback.areContentsTheSame(oldItem, newItem);
-        }
-
-        @Override
-        public boolean areItemsTheSame(T2 item1, T2 item2) {
-            return mWrappedCallback.areItemsTheSame(item1, item2);
-        }
-
-        @Nullable
-        @Override
-        public Object getChangePayload(T2 item1, T2 item2) {
-            return mWrappedCallback.getChangePayload(item1, item2);
-        }
-
-        /**
-         * This method dispatches any pending event notifications to the wrapped Callback.
-         * You <b>must</b> always call this method after you are done with editing the SortedList.
-         */
-        public void dispatchLastEvent() {
-            mBatchingListUpdateCallback.dispatchLastEvent();
-        }
-    }
-}
diff --git a/android/support/v7/util/SortedListBatchedCallbackTest.java b/android/support/v7/util/SortedListBatchedCallbackTest.java
deleted file mode 100644
index bc50415..0000000
--- a/android/support/v7/util/SortedListBatchedCallbackTest.java
+++ /dev/null
@@ -1,114 +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 static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-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.Mockito;
-
-@SuppressWarnings("unchecked")
-@RunWith(JUnit4.class)
-@SmallTest
-public class SortedListBatchedCallbackTest {
-    SortedList.BatchedCallback mBatchedCallback;
-    SortedList.Callback mMockCallback;
-    @Before
-    public void init() {
-        mMockCallback = Mockito.mock(SortedList.Callback.class);
-        mBatchedCallback = new SortedList.BatchedCallback(mMockCallback);
-    }
-
-    @Test
-    public void onChange() {
-        mBatchedCallback.onChanged(1, 2);
-        verifyZeroInteractions(mMockCallback);
-        mBatchedCallback.dispatchLastEvent();
-        verify(mMockCallback).onChanged(1, 2, null);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void onChangeWithPayload() {
-        final Object payload = 7;
-        mBatchedCallback.onChanged(1, 2, payload);
-        verifyZeroInteractions(mMockCallback);
-        mBatchedCallback.dispatchLastEvent();
-        verify(mMockCallback).onChanged(1, 2, payload);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void onRemoved() {
-        mBatchedCallback.onRemoved(2, 3);
-        verifyZeroInteractions(mMockCallback);
-        mBatchedCallback.dispatchLastEvent();
-        verify(mMockCallback).onRemoved(2, 3);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void onInserted() {
-        mBatchedCallback.onInserted(3, 4);
-        verifyNoMoreInteractions(mMockCallback);
-        mBatchedCallback.dispatchLastEvent();
-        verify(mMockCallback).onInserted(3, 4);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void onMoved() {
-        mBatchedCallback.onMoved(5, 6);
-        // moves are not merged
-        verify(mMockCallback).onMoved(5, 6);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void compare() {
-        Object o1 = new Object();
-        Object o2 = new Object();
-        mBatchedCallback.compare(o1, o2);
-        verify(mMockCallback).compare(o1, o2);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void areContentsTheSame() {
-        Object o1 = new Object();
-        Object o2 = new Object();
-        mBatchedCallback.areContentsTheSame(o1, o2);
-        verify(mMockCallback).areContentsTheSame(o1, o2);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-
-    @Test
-    public void areItemsTheSame() {
-        Object o1 = new Object();
-        Object o2 = new Object();
-        mBatchedCallback.areItemsTheSame(o1, o2);
-        verify(mMockCallback).areItemsTheSame(o1, o2);
-        verifyNoMoreInteractions(mMockCallback);
-    }
-}
diff --git a/android/support/v7/util/SortedListTest.java b/android/support/v7/util/SortedListTest.java
deleted file mode 100644
index e628de1..0000000
--- a/android/support/v7/util/SortedListTest.java
+++ /dev/null
@@ -1,1698 +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.util;
-
-import static org.junit.Assert.assertEquals;
-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.junit.Assert.fail;
-
-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;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class SortedListTest {
-
-    SortedList<Item> mList;
-    List<Pair> mAdditions = new ArrayList<>();
-    List<Pair> mRemovals = new ArrayList<>();
-    List<Pair> mMoves = new ArrayList<>();
-    List<Pair> mUpdates = new ArrayList<>();
-    private boolean mPayloadChanges = false;
-    List<PayloadChange> mPayloadUpdates = new ArrayList<>();
-    Queue<AssertListStateRunnable> mCallbackRunnables;
-    List<Event> mEvents = new ArrayList<>();
-    private SortedList.Callback<Item> mCallback;
-    InsertedCallback<Item> mInsertedCallback;
-    ChangedCallback<Item> mChangedCallback;
-
-    private Comparator<? super Item> sItemComparator = new Comparator<Item>() {
-        @Override
-        public int compare(Item o1, Item o2) {
-            return mCallback.compare(o1, o2);
-        }
-    };
-
-    private abstract class InsertedCallback<T> {
-        public abstract void onInserted(int position, int count);
-    }
-
-    private abstract class ChangedCallback<T> {
-        public abstract void onChanged(int position, int count);
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        mCallback = new SortedList.Callback<Item>() {
-            @Override
-            public int compare(Item o1, Item o2) {
-                return o1.cmpField < o2.cmpField ? -1 : (o1.cmpField == o2.cmpField ? 0 : 1);
-            }
-
-            @Override
-            public void onInserted(int position, int count) {
-                mEvents.add(new Event(TYPE.ADD, position, count));
-                mAdditions.add(new Pair(position, count));
-                if (mInsertedCallback != null) {
-                    mInsertedCallback.onInserted(position, count);
-                }
-                pollAndRun(mCallbackRunnables);
-            }
-
-            @Override
-            public void onRemoved(int position, int count) {
-                mEvents.add(new Event(TYPE.REMOVE, position, count));
-                mRemovals.add(new Pair(position, count));
-                pollAndRun(mCallbackRunnables);
-            }
-
-            @Override
-            public void onMoved(int fromPosition, int toPosition) {
-                mEvents.add(new Event(TYPE.MOVE, fromPosition, toPosition));
-                mMoves.add(new Pair(fromPosition, toPosition));
-            }
-
-            @Override
-            public void onChanged(int position, int count) {
-                mEvents.add(new Event(TYPE.CHANGE, position, count));
-                mUpdates.add(new Pair(position, count));
-                if (mChangedCallback != null) {
-                    mChangedCallback.onChanged(position, count);
-                }
-                pollAndRun(mCallbackRunnables);
-            }
-
-            @Override
-            public void onChanged(int position, int count, Object payload) {
-                if (mPayloadChanges) {
-                    mPayloadUpdates.add(new PayloadChange(position, count, payload));
-                } else {
-                    onChanged(position, count);
-                }
-            }
-
-            @Override
-            public boolean areContentsTheSame(Item oldItem, Item newItem) {
-                return oldItem.data == newItem.data;
-            }
-
-            @Override
-            public boolean areItemsTheSame(Item item1, Item item2) {
-                return item1.id == item2.id;
-            }
-
-            @Nullable
-            @Override
-            public Object getChangePayload(Item item1, Item item2) {
-                if (mPayloadChanges) {
-                    return item2.data;
-                }
-                return null;
-            }
-        };
-        mList = new SortedList<Item>(Item.class, mCallback);
-    }
-
-    private void pollAndRun(Queue<AssertListStateRunnable> queue) {
-        if (queue != null) {
-            Runnable runnable = queue.poll();
-            assertNotNull(runnable);
-            runnable.run();
-        }
-    }
-
-    @Test
-    public void testValidMethodsDuringOnInsertedCallbackFromEmptyList() {
-
-        final Item[] items =
-                new Item[] {new Item(0), new Item(1), new Item(2)};
-
-        final AtomicInteger atomicInteger = new AtomicInteger(0);
-        mInsertedCallback = new InsertedCallback<Item>() {
-            @Override
-            public void onInserted(int position, int count) {
-                for (int i = 0; i < count; i++) {
-                    assertEquals(mList.get(i), items[i]);
-                    assertEquals(mList.indexOf(items[i]), i);
-                    atomicInteger.incrementAndGet();
-                }
-            }
-        };
-
-        mList.add(items[0]);
-        mList.clear();
-        mList.addAll(items, false);
-        assertEquals(4, atomicInteger.get());
-    }
-
-    @Test
-    public void testEmpty() {
-        assertEquals("empty", mList.size(), 0);
-    }
-
-    @Test
-    public void testAdd() {
-        Item item = new Item(1);
-        assertEquals(insert(item), 0);
-        assertEquals(size(), 1);
-        assertTrue(mAdditions.contains(new Pair(0, 1)));
-        Item item2 = new Item(2);
-        item2.cmpField = item.cmpField + 1;
-        assertEquals(insert(item2), 1);
-        assertEquals(size(), 2);
-        assertTrue(mAdditions.contains(new Pair(1, 1)));
-        Item item3 = new Item(3);
-        item3.cmpField = item.cmpField - 1;
-        mAdditions.clear();
-        assertEquals(insert(item3), 0);
-        assertEquals(size(), 3);
-        assertTrue(mAdditions.contains(new Pair(0, 1)));
-    }
-
-    @Test
-    public void testAddDuplicate() {
-        Item item = new Item(1);
-        Item item2 = new Item(item.id);
-        insert(item);
-        assertEquals(0, insert(item2));
-        assertEquals(1, size());
-        assertEquals(1, mAdditions.size());
-        assertEquals(0, mUpdates.size());
-    }
-
-    @Test
-    public void testRemove() {
-        Item item = new Item(1);
-        assertFalse(remove(item));
-        assertEquals(0, mRemovals.size());
-        insert(item);
-        assertTrue(remove(item));
-        assertEquals(1, mRemovals.size());
-        assertTrue(mRemovals.contains(new Pair(0, 1)));
-        assertEquals(0, size());
-        assertFalse(remove(item));
-        assertEquals(1, mRemovals.size());
-    }
-
-    @Test
-    public void testRemove2() {
-        Item item = new Item(1);
-        Item item2 = new Item(2, 1, 1);
-        insert(item);
-        assertFalse(remove(item2));
-        assertEquals(0, mRemovals.size());
-    }
-
-    @Test
-    public void clearTest() {
-        insert(new Item(1));
-        insert(new Item(2));
-        assertEquals(2, mList.size());
-        mList.clear();
-        assertEquals(0, mList.size());
-        insert(new Item(3));
-        assertEquals(1, mList.size());
-    }
-
-    @Test
-    public void testBatch() {
-        mList.beginBatchedUpdates();
-        for (int i = 0; i < 5; i++) {
-            mList.add(new Item(i));
-        }
-        assertEquals(0, mAdditions.size());
-        mList.endBatchedUpdates();
-        assertTrue(mAdditions.contains(new Pair(0, 5)));
-    }
-
-    @Test
-    public void testRandom() throws Throwable {
-        Random random = new Random(System.nanoTime());
-        List<Item> copy = new ArrayList<Item>();
-        StringBuilder log = new StringBuilder();
-        int id = 1;
-        try {
-            for (int i = 0; i < 10000; i++) {
-                switch (random.nextInt(3)) {
-                    case 0://ADD
-                        Item item = new Item(id++);
-                        copy.add(item);
-                        insert(item);
-                        log.append("add ").append(item).append("\n");
-                        break;
-                    case 1://REMOVE
-                        if (copy.size() > 0) {
-                            int index = random.nextInt(mList.size());
-                            item = mList.get(index);
-                            log.append("remove ").append(item).append("\n");
-                            assertTrue(copy.remove(item));
-                            assertTrue(mList.remove(item));
-                        }
-                        break;
-                    case 2://UPDATE
-                        if (copy.size() > 0) {
-                            int index = random.nextInt(mList.size());
-                            item = mList.get(index);
-                            // TODO this cannot work
-                            Item newItem =
-                                    new Item(item.id, item.cmpField, random.nextInt(1000));
-                            while (newItem.data == item.data) {
-                                newItem.data = random.nextInt(1000);
-                            }
-                            log.append("update ").append(item).append(" to ").append(newItem)
-                                    .append("\n");
-                            int itemIndex = mList.add(newItem);
-                            copy.remove(item);
-                            copy.add(newItem);
-                            assertSame(mList.get(itemIndex), newItem);
-                            assertNotSame(mList.get(index), item);
-                        }
-                        break;
-                    case 3:// UPDATE AT
-                        if (copy.size() > 0) {
-                            int index = random.nextInt(mList.size());
-                            item = mList.get(index);
-                            Item newItem = new Item(item.id, random.nextInt(), random.nextInt());
-                            mList.updateItemAt(index, newItem);
-                            copy.remove(item);
-                            copy.add(newItem);
-                            log.append("update at ").append(index).append(" ").append(item)
-                                    .append(" to ").append(newItem).append("\n");
-                        }
-                }
-                int lastCmp = Integer.MIN_VALUE;
-                for (int index = 0; index < copy.size(); index++) {
-                    assertFalse(mList.indexOf(copy.get(index)) == SortedList.INVALID_POSITION);
-                    assertTrue(mList.get(index).cmpField >= lastCmp);
-                    lastCmp = mList.get(index).cmpField;
-                    assertTrue(copy.contains(mList.get(index)));
-                }
-
-                for (int index = 0; index < mList.size(); index++) {
-                    assertNotNull(mList.mData[index]);
-                }
-                for (int index = mList.size(); index < mList.mData.length; index++) {
-                    assertNull(mList.mData[index]);
-                }
-            }
-        } catch (Throwable t) {
-            Collections.sort(copy, sItemComparator);
-            log.append("Items:\n");
-            for (Item item : copy) {
-                log.append(item).append("\n");
-            }
-            log.append("SortedList:\n");
-            for (int i = 0; i < mList.size(); i++) {
-                log.append(mList.get(i)).append("\n");
-            }
-
-            throw new Throwable(" \nlog:\n" + log.toString(), t);
-        }
-    }
-
-    private static Item[] createItems(int idFrom, int idTo, int idStep) {
-        final int count = (idTo - idFrom) / idStep + 1;
-        Item[] items = new Item[count];
-        int id = idFrom;
-        for (int i = 0; i < count; i++) {
-            Item item = new Item(id);
-            items[i] = item;
-            id += idStep;
-        }
-        return items;
-    }
-
-    private static Item[] createItemsFromInts(int ... ints) {
-        Item[] items = new Item[ints.length];
-        for (int i = ints.length - 1; i >= 0; i--) {
-            items[i] = new Item(ints[i]);
-        }
-        return items;
-    }
-
-    private static Item[] shuffle(Item[] items) {
-        Random random = new Random(System.nanoTime());
-        final int count = items.length;
-        for (int i = 0; i < count; i++) {
-            int pos1 = random.nextInt(count);
-            int pos2 = random.nextInt(count);
-            if (pos1 != pos2) {
-                Item temp = items[pos1];
-                items[pos1] = items[pos2];
-                items[pos2] = temp;
-            }
-        }
-        return items;
-    }
-
-    private void assertIntegrity(int size, String context) {
-        assertEquals(context + ": incorrect size", size, size());
-        int rangeStart = 0;
-        for (int i = 0; i < size(); i++) {
-            Item item = mList.get(i);
-            assertNotNull(context + ": get returned null @" + i, item);
-            assertEquals(context + ": incorrect indexOf result @" + i, i, mList.indexOf(item));
-            if (i == 0) {
-                continue;
-            }
-
-            final int compare = mCallback.compare(mList.get(i - 1), item);
-            assertTrue(context + ": incorrect sorting order @" + i, compare <= 0);
-
-            if (compare == 0) {
-                for (int j = rangeStart; j < i; j++) {
-                    assertFalse(context + ": duplicates found @" + j + " and " + i,
-                            mCallback.areItemsTheSame(mList.get(j), item));
-                }
-            } else {
-                rangeStart = i;
-            }
-        }
-    }
-
-    private void assertSequentialOrder() {
-        for (int i = 0; i < size(); i++) {
-            assertEquals(i, mList.get(i).cmpField);
-        }
-    }
-
-    @Test
-    public void testAddAllMerge() throws Throwable {
-        mList.addAll(new Item[0]);
-        assertIntegrity(0, "addAll, empty list, empty input");
-        assertEquals(0, mAdditions.size());
-
-        // Add first 5 even numbers. Test adding to an empty list.
-        mList.addAll(createItems(0, 8, 2));
-        assertIntegrity(5, "addAll, empty list, non-empty input");
-        assertEquals(1, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(0, 5)));
-
-        mList.addAll(new Item[0]);
-        assertIntegrity(5, "addAll, non-empty list, empty input");
-        assertEquals(1, mAdditions.size());
-
-        // Add 5 more even numbers, shuffled (test pre-sorting).
-        mList.addAll(shuffle(createItems(10, 18, 2)));
-        assertIntegrity(10, "addAll, shuffled input");
-        assertEquals(2, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(5, 5)));
-
-        // Add 5 more even numbers, reversed (test pre-sorting).
-        mList.addAll(shuffle(createItems(28, 20, -2)));
-        assertIntegrity(15, "addAll, reversed input");
-        assertEquals(3, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(10, 5)));
-
-        // Add first 10 odd numbers.
-        // Test the merge when the new items run out first.
-        mList.addAll(createItems(1, 19, 2));
-        assertIntegrity(25, "addAll, merging in the middle");
-        assertEquals(13, mAdditions.size());
-        for (int i = 1; i <= 19; i += 2) {
-            assertTrue(mAdditions.contains(new Pair(i, 1)));
-        }
-
-        // Add 10 more odd numbers.
-        // Test the merge when the old items run out first.
-        mList.addAll(createItems(21, 39, 2));
-        assertIntegrity(35, "addAll, merging at the end");
-        assertEquals(18, mAdditions.size());
-        for (int i = 21; i <= 27; i += 2) {
-            assertTrue(mAdditions.contains(new Pair(i, 1)));
-        }
-        assertTrue(mAdditions.contains(new Pair(29, 6)));
-
-        // Add 5 more even numbers.
-        mList.addAll(createItems(30, 38, 2));
-        assertIntegrity(40, "addAll, merging more");
-        assertEquals(23, mAdditions.size());
-        for (int i = 30; i <= 38; i += 2) {
-            assertTrue(mAdditions.contains(new Pair(i, 1)));
-        }
-
-        assertEquals(0, mMoves.size());
-        assertEquals(0, mUpdates.size());
-        assertEquals(0, mRemovals.size());
-
-        assertSequentialOrder();
-    }
-
-    @Test
-    public void testAddAllUpdates() throws Throwable {
-        // Add first 5 even numbers.
-        Item[] evenItems = createItems(0, 8, 2);
-        for (Item item : evenItems) {
-            item.data = 1;
-        }
-        mList.addAll(evenItems);
-        assertEquals(5, size());
-        assertEquals(1, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(0, 5)));
-        assertEquals(0, mUpdates.size());
-
-        Item[] sameEvenItems = createItems(0, 8, 2);
-        for (Item item : sameEvenItems) {
-            item.data = 1;
-        }
-        mList.addAll(sameEvenItems);
-        assertEquals(1, mAdditions.size());
-        assertEquals(0, mUpdates.size());
-
-        Item[] newEvenItems = createItems(0, 8, 2);
-        for (Item item : newEvenItems) {
-            item.data = 2;
-        }
-        mList.addAll(newEvenItems);
-        assertEquals(5, size());
-        assertEquals(1, mAdditions.size());
-        assertEquals(1, mUpdates.size());
-        assertTrue(mUpdates.contains(new Pair(0, 5)));
-        for (int i = 0; i < 5; i++) {
-            assertEquals(2, mList.get(i).data);
-        }
-
-        // Add all numbers from 0 to 9
-        Item[] sequentialItems = createItems(0, 9, 1);
-        for (Item item : sequentialItems) {
-            item.data = 3;
-        }
-        mList.addAll(sequentialItems);
-
-        // Odd numbers should have been added.
-        assertEquals(6, mAdditions.size());
-        for (int i = 0; i < 5; i++) {
-            assertTrue(mAdditions.contains(new Pair(i * 2 + 1, 1)));
-        }
-
-        // All even items should have been updated.
-        assertEquals(6, mUpdates.size());
-        for (int i = 0; i < 5; i++) {
-            assertTrue(mUpdates.contains(new Pair(i * 2, 1)));
-        }
-
-        assertEquals(10, size());
-
-        // All items should have the latest data value.
-        for (int i = 0; i < 10; i++) {
-            assertEquals(3, mList.get(i).data);
-        }
-        assertEquals(0, mMoves.size());
-        assertEquals(0, mRemovals.size());
-        assertSequentialOrder();
-    }
-
-    @Test
-    public void testAddAllWithDuplicates() throws Throwable {
-        final int maxCmpField = 5;
-        final int idsPerCmpField = 10;
-        final int maxUniqueId = maxCmpField * idsPerCmpField;
-        final int maxGeneration = 5;
-
-        Item[] items = new Item[maxUniqueId * maxGeneration];
-
-        int index = 0;
-        for (int generation = 0; generation < maxGeneration; generation++) {
-            int uniqueId = 0;
-            for (int cmpField = 0; cmpField < maxCmpField; cmpField++) {
-                for (int id = 0; id < idsPerCmpField; id++) {
-                    Item item = new Item(uniqueId++, cmpField, generation);
-                    items[index++] = item;
-                }
-            }
-        }
-
-        mList.addAll(items);
-
-        assertIntegrity(maxUniqueId, "addAll with duplicates");
-
-        // Check that the most recent items have made it to the list.
-        for (int i = 0; i != size(); i++) {
-            Item item = mList.get(i);
-            assertEquals(maxGeneration - 1, item.data);
-        }
-    }
-
-    @Test
-    public void testAddAllFast() throws Throwable {
-        mList.addAll(new Item[0], true);
-        assertIntegrity(0, "addAll(T[],boolean), empty list, with empty input");
-        assertEquals(0, mAdditions.size());
-
-        mList.addAll(createItems(0, 9, 1), true);
-        assertIntegrity(10, "addAll(T[],boolean), empty list, non-empty input");
-        assertEquals(1, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(0, 10)));
-
-        mList.addAll(new Item[0], true);
-        assertEquals(1, mAdditions.size());
-        assertIntegrity(10, "addAll(T[],boolean), non-empty list, empty input");
-
-        mList.addAll(createItems(10, 19, 1), true);
-        assertEquals(2, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(10, 10)));
-        assertIntegrity(20, "addAll(T[],boolean), non-empty list, non-empty input");
-    }
-
-    @Test
-    public void testAddAllCollection() throws Throwable {
-        Collection<Item> itemList = new ArrayList<Item>();
-        for (int i = 0; i < 5; i++) {
-            itemList.add(new Item(i));
-        }
-        mList.addAll(itemList);
-
-        assertEquals(1, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(0, itemList.size())));
-        assertIntegrity(itemList.size(), "addAll on collection");
-    }
-
-    @Test
-    public void testAddAllStableSort() {
-        int id = 0;
-        Item item = new Item(id++, 0, 0);
-        mList.add(item);
-
-        // Create a few items with the same sort order.
-        Item[] items = new Item[3];
-        for (int i = 0; i < 3; i++) {
-            items[i] = new Item(id++, item.cmpField, 0);
-            assertEquals(0, mCallback.compare(item, items[i]));
-        }
-
-        mList.addAll(items);
-        assertEquals(1 + items.length, size());
-
-        // Check that the order has been preserved.
-        for (int i = 0; i < size(); i++) {
-            assertEquals(i, mList.get(i).id);
-        }
-    }
-
-
-    @Test
-    public void testAddAllAccessFromCallbacks() {
-        // Add first 5 even numbers.
-        Item[] evenItems = createItems(0, 8, 2);
-        for (Item item : evenItems) {
-            item.data = 1;
-        }
-
-
-        mInsertedCallback = new InsertedCallback<Item>() {
-            @Override
-            public void onInserted(int position, int count) {
-                assertEquals(0, position);
-                assertEquals(5, count);
-                for (int i = 0; i < count; i++) {
-                    assertEquals(i * 2, mList.get(i).id);
-                }
-                assertIntegrity(5, "onInserted(" + position + ", " + count + ")");
-
-            }
-        };
-
-        mList.addAll(evenItems);
-        assertEquals(1, mAdditions.size());
-        assertEquals(0, mUpdates.size());
-
-        // Add all numbers from 0 to 9. This should trigger 5 change and 5 insert notifications.
-        Item[] sequentialItems = createItems(0, 9, 1);
-        for (Item item : sequentialItems) {
-            item.data = 2;
-        }
-
-        mChangedCallback = new ChangedCallback<Item>() {
-            int expectedSize = 5;
-
-            @Override
-            public void onChanged(int position, int count) {
-                assertEquals(1, count);
-                assertEquals(position, mList.get(position).id);
-                assertIntegrity(++expectedSize, "onChanged(" + position + ")");
-            }
-        };
-
-        mInsertedCallback = new InsertedCallback<Item>() {
-            int expectedSize = 5;
-
-            @Override
-            public void onInserted(int position, int count) {
-                assertEquals(1, count);
-                assertEquals(position, mList.get(position).id);
-                assertIntegrity(++expectedSize, "onInserted(" + position + ")");
-            }
-        };
-
-        mList.addAll(sequentialItems);
-        assertEquals(6, mAdditions.size());
-        assertEquals(5, mUpdates.size());
-    }
-
-    @Test
-    public void testModificationFromCallbackThrows() {
-        final Item extraItem = new Item(0);
-
-        Item[] items = createItems(1, 5, 2);
-        for (Item item : items) {
-            item.data = 1;
-        }
-        mList.addAll(items);
-
-        mInsertedCallback = new InsertedCallback<Item>() {
-            @Override
-            public void onInserted(int position, int count) {
-                try {
-                    mList.add(new Item(1));
-                    fail("add must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.addAll(createItems(0, 0, 1));
-                    fail("addAll must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.addAll(createItems(0, 0, 1), true);
-                    fail("addAll(T[],boolean) must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.remove(extraItem);
-                    fail("remove must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.removeItemAt(0);
-                    fail("removeItemAt must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.updateItemAt(0, extraItem);
-                    fail("updateItemAt must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.recalculatePositionOfItemAt(0);
-                    fail("recalculatePositionOfItemAt must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-                try {
-                    mList.clear();
-                    fail("recalculatePositionOfItemAt must throw from within a callback");
-                } catch (IllegalStateException e) {
-                }
-            }
-        };
-
-        // Make sure that the last one notification is change, so that the above callback is
-        // not called from endBatchUpdates when the nested alls are actually OK.
-        items = createItems(1, 5, 1);
-        for (Item item : items) {
-            item.data = 2;
-        }
-        mList.addAll(items);
-        assertIntegrity(5, "Modification from callback");
-    }
-
-    @Test
-    public void testAddAllOutsideBatchedUpdates() {
-        mList.add(new Item(1));
-        assertEquals(1, mAdditions.size());
-        mList.add(new Item(2));
-        assertEquals(2, mAdditions.size());
-        mList.addAll(new Item(3), new Item(4));
-        assertEquals(3, mAdditions.size());
-        mList.add(new Item(5));
-        assertEquals(4, mAdditions.size());
-        mList.add(new Item(6));
-        assertEquals(5, mAdditions.size());
-    }
-
-    @Test
-    public void testAddAllInsideBatchedUpdates() {
-        mList.beginBatchedUpdates();
-
-        mList.add(new Item(1));
-        assertEquals(0, mAdditions.size());
-        mList.add(new Item(2));
-        assertEquals(0, mAdditions.size());
-        mList.addAll(new Item(3), new Item(4));
-        assertEquals(0, mAdditions.size());
-        mList.add(new Item(5));
-        assertEquals(0, mAdditions.size());
-        mList.add(new Item(6));
-        assertEquals(0, mAdditions.size());
-
-        mList.endBatchedUpdates();
-
-        assertEquals(1, mAdditions.size());
-        assertTrue(mAdditions.contains(new Pair(0, 6)));
-    }
-
-    @Test
-    public void testAddExistingItemCallsChangeWithPayload() {
-        mList.addAll(
-                new Item(1),
-                new Item(2),
-                new Item(3)
-        );
-        mPayloadChanges = true;
-
-        // add an item with the same id but a new data field i.e. send an update
-        final Item twoUpdate = new Item(2);
-        twoUpdate.data = 1337;
-        mList.add(twoUpdate);
-        assertEquals(1, mPayloadUpdates.size());
-        final PayloadChange update = mPayloadUpdates.get(0);
-        assertEquals(1, update.position);
-        assertEquals(1, update.count);
-        assertEquals(1337, update.payload);
-        assertEquals(3, size());
-    }
-
-    @Test
-    public void testUpdateItemCallsChangeWithPayload() {
-        mList.addAll(
-                new Item(1),
-                new Item(2),
-                new Item(3)
-        );
-        mPayloadChanges = true;
-
-        // add an item with the same id but a new data field i.e. send an update
-        final Item twoUpdate = new Item(2);
-        twoUpdate.data = 1337;
-        mList.updateItemAt(1, twoUpdate);
-        assertEquals(1, mPayloadUpdates.size());
-        final PayloadChange update = mPayloadUpdates.get(0);
-        assertEquals(1, update.position);
-        assertEquals(1, update.count);
-        assertEquals(1337, update.payload);
-        assertEquals(3, size());
-        assertEquals(1337, mList.get(1).data);
-    }
-
-    @Test
-    public void testAddMultipleExistingItemCallsChangeWithPayload() {
-        mList.addAll(
-                new Item(1),
-                new Item(2),
-                new Item(3)
-        );
-        mPayloadChanges = true;
-
-        // add two items with the same ids but a new data fields i.e. send two updates
-        final Item twoUpdate = new Item(2);
-        twoUpdate.data = 222;
-        final Item threeUpdate = new Item(3);
-        threeUpdate.data = 333;
-        mList.addAll(twoUpdate, threeUpdate);
-        assertEquals(2, mPayloadUpdates.size());
-        final PayloadChange update1 = mPayloadUpdates.get(0);
-        assertEquals(1, update1.position);
-        assertEquals(1, update1.count);
-        assertEquals(222, update1.payload);
-        final PayloadChange update2 = mPayloadUpdates.get(1);
-        assertEquals(2, update2.position);
-        assertEquals(1, update2.count);
-        assertEquals(333, update2.payload);
-        assertEquals(3, size());
-    }
-
-    @Test
-    public void replaceAll_mayModifyInputFalse_doesNotModify() {
-        mList.addAll(
-                new Item(1),
-                new Item(2)
-        );
-        Item replacement0 = new Item(4);
-        Item replacement1 = new Item(3);
-        Item[] replacements = new Item[]{
-                replacement0,
-                replacement1
-        };
-
-        mList.replaceAll(replacements, false);
-
-        assertSame(replacement0, replacements[0]);
-        assertSame(replacement1, replacements[1]);
-    }
-
-    @Test
-    public void replaceAll_varArgs_isEquivalentToDefault() {
-        mList.addAll(
-                new Item(1),
-                new Item(2)
-        );
-        Item replacement0 = new Item(3);
-        Item replacement1 = new Item(4);
-
-        mList.replaceAll(replacement0, replacement1);
-
-        assertEquals(mList.get(0), replacement0);
-        assertEquals(mList.get(1), replacement1);
-        assertEquals(2, mList.size());
-    }
-
-    @Test
-    public void replaceAll_collection_isEquivalentToDefaultWithMayModifyInputFalse() {
-        mList.addAll(
-                new Item(1),
-                new Item(2)
-        );
-        Item replacement0 = new Item(4);
-        Item replacement1 = new Item(3);
-        List<Item> replacements = new ArrayList<>();
-        replacements.add(replacement0);
-        replacements.add(replacement1);
-
-        mList.replaceAll(replacements);
-
-        assertEquals(mList.get(0), replacement1);
-        assertEquals(mList.get(1), replacement0);
-        assertSame(replacements.get(0), replacement0);
-        assertSame(replacements.get(1), replacement1);
-        assertEquals(2, mList.size());
-    }
-
-    @Test
-    public void replaceAll_callsChangeWithPayload() {
-        mList.addAll(
-                new Item(1),
-                new Item(2),
-                new Item(3)
-        );
-        mPayloadChanges = true;
-        final Item twoUpdate = new Item(2);
-        twoUpdate.data = 222;
-        final Item threeUpdate = new Item(3);
-        threeUpdate.data = 333;
-
-        mList.replaceAll(twoUpdate, threeUpdate);
-
-        assertEquals(2, mPayloadUpdates.size());
-        final PayloadChange update1 = mPayloadUpdates.get(0);
-        assertEquals(0, update1.position);
-        assertEquals(1, update1.count);
-        assertEquals(222, update1.payload);
-        final PayloadChange update2 = mPayloadUpdates.get(1);
-        assertEquals(1, update2.position);
-        assertEquals(1, update2.count);
-        assertEquals(333, update2.payload);
-    }
-
-    @Test
-    public void replaceAll_totallyEquivalentData_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 2, 3);
-        Item[] items2 = createItemsFromInts(1, 2, 3);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mList.replaceAll(items2);
-
-        assertEquals(0, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-    }
-
-    @Test
-    public void replaceAll_removalsAndAdds1_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 3, 5);
-        Item[] items2 = createItemsFromInts(2, 4);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(2, 3, 5)));
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(2, 5)));
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(2, 4, 5)));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.REMOVE, 1, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(3));
-        assertEquals(new Event(TYPE.REMOVE, 2, 1), mEvents.get(4));
-        assertEquals(5, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_removalsAndAdds2_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(2, 4);
-        Item[] items2 = createItemsFromInts(1, 3, 5);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(1, 4)));
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(1, 3, 4)));
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(1, 3)));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.REMOVE, 1, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.REMOVE, 2, 1), mEvents.get(3));
-        assertEquals(new Event(TYPE.ADD, 2, 1), mEvents.get(4));
-        assertEquals(5, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_removalsAndAdds3_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 3, 5);
-        Item[] items2 = createItemsFromInts(2, 3, 4);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(2, 3, 5)));
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(2, 3, 4, 5)));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.ADD, 2, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.REMOVE, 3, 1), mEvents.get(3));
-        assertEquals(4, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_removalsAndAdds4_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(2, 3, 4);
-        Item[] items2 = createItemsFromInts(1, 3, 5);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(1, 3, 4)));
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(1, 3)));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.REMOVE, 1, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.REMOVE, 2, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.ADD, 2, 1), mEvents.get(3));
-        assertEquals(4, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_removalsAndAdds5_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 2, 3);
-        Item[] items2 = createItemsFromInts(3, 4, 5);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 2), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 1, 2), mEvents.get(1));
-        assertEquals(2, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_removalsAndAdds6_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(3, 4, 5);
-        Item[] items2 = createItemsFromInts(1, 2, 3);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.ADD, 0, 2), mEvents.get(0));
-        assertEquals(new Event(TYPE.REMOVE, 3, 2), mEvents.get(1));
-        assertEquals(2, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_move1_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 2, 3);
-        Item[] items2 = new Item[]{
-                new Item(2),
-                new Item(3),
-                new Item(1, 4, 1)};
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 2, 1), mEvents.get(1));
-        assertEquals(2, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_move2_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 2, 3);
-        Item[] items2 = new Item[]{
-                new Item(3, 0, 3),
-                new Item(1),
-                new Item(2)};
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.REMOVE, 3, 1), mEvents.get(1));
-        assertEquals(2, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_move3_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 3, 5, 7, 9);
-        Item[] items2 = new Item[]{
-                new Item(3, 0, 3),
-                new Item(1),
-                new Item(5),
-                new Item(9),
-                new Item(7, 10, 7),
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(3, 0, 3),
-                new Item(1),
-                new Item(5),
-                new Item(7),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(3, 0, 3),
-                new Item(1),
-                new Item(5),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.REMOVE, 2, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.REMOVE, 3, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.ADD, 4, 1), mEvents.get(3));
-        assertEquals(4, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_move4_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 3, 5, 7, 9);
-        Item[] items2 = new Item[]{
-                new Item(3),
-                new Item(1, 4, 1),
-                new Item(5),
-                new Item(9, 6, 9),
-                new Item(7),
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(3),
-                new Item(1, 4, 1),
-                new Item(5),
-                new Item(7),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(3),
-                new Item(1, 4, 1),
-                new Item(5),
-                new Item(9, 6, 9),
-                new Item(7),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.ADD, 3, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.REMOVE, 5, 1), mEvents.get(3));
-        assertEquals(4, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_move5_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 3, 5, 7, 9);
-        Item[] items2 = new Item[]{
-                new Item(9, 1, 9),
-                new Item(7, 3, 7),
-                new Item(5),
-                new Item(3, 7, 3),
-                new Item(1, 9, 1),
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(9, 1, 9),
-                new Item(3),
-                new Item(5),
-                new Item(7),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(9, 1, 9),
-                new Item(5),
-                new Item(7),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(9, 1, 9),
-                new Item(7, 3, 7),
-                new Item(5),
-                new Item(7),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(9, 1, 9),
-                new Item(7, 3, 7),
-                new Item(5),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(9, 1, 9),
-                new Item(7, 3, 7),
-                new Item(5),
-                new Item(3, 7, 3),
-                new Item(9)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(9, 1, 9),
-                new Item(7, 3, 7),
-                new Item(5),
-                new Item(3, 7, 3)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.REMOVE, 1, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(3));
-        assertEquals(new Event(TYPE.REMOVE, 3, 1), mEvents.get(4));
-        assertEquals(new Event(TYPE.ADD, 3, 1), mEvents.get(5));
-        assertEquals(new Event(TYPE.REMOVE, 4, 1), mEvents.get(6));
-        assertEquals(new Event(TYPE.ADD, 4, 1), mEvents.get(7));
-        assertEquals(8, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_orderSameItemDifferent_worksCorrectly() {
-        Item[] items1 = new Item[]{
-                new Item(1),
-                new Item(2, 3, 2),
-                new Item(5)
-        };
-        Item[] items2 = new Item[]{
-                new Item(1),
-                new Item(4, 3, 4),
-                new Item(5)
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 1, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(1));
-        assertEquals(2, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_orderSameItemSameContentsDifferent_worksCorrectly() {
-        Item[] items1 = new Item[]{
-                new Item(1),
-                new Item(3, 3, 2),
-                new Item(5)
-        };
-        Item[] items2 = new Item[]{
-                new Item(1),
-                new Item(3, 3, 4),
-                new Item(5)
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.CHANGE, 1, 1), mEvents.get(0));
-        assertEquals(1, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_allTypesOfChanges1_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(2, 5, 6);
-        Item[] items2 = new Item[]{
-                new Item(1),
-                new Item(3, 2, 3),
-                new Item(6, 6, 7)
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(createItemsFromInts(1, 5, 6)));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(1),
-                new Item(3, 2, 3),
-                new Item(5),
-                new Item(6)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(1),
-                new Item(3, 2, 3),
-                new Item(6)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.ADD, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.REMOVE, 1, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.REMOVE, 2, 1), mEvents.get(3));
-        assertEquals(new Event(TYPE.CHANGE, 2, 1), mEvents.get(4));
-        assertEquals(5, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_allTypesOfChanges2_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 4, 6);
-        Item[] items2 = new Item[]{
-                new Item(1, 1, 2),
-                new Item(3),
-                new Item(5, 4, 5)
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(1, 1, 2),
-                new Item(3),
-                new Item(4),
-                new Item(6)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(1, 1, 2),
-                new Item(3),
-                new Item(6)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(1, 1, 2),
-                new Item(3),
-                new Item(5, 4, 5),
-                new Item(6)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.CHANGE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 1, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.REMOVE, 2, 1), mEvents.get(2));
-        assertEquals(new Event(TYPE.ADD, 2, 1), mEvents.get(3));
-        assertEquals(new Event(TYPE.REMOVE, 3, 1), mEvents.get(4));
-        assertEquals(5, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_allTypesOfChanges3_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 2);
-        Item[] items2 = new Item[]{
-                new Item(2, 2, 3),
-                new Item(3, 2, 4),
-                new Item(5)
-        };
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(
-                new Item(2, 2, 3)
-        ));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.replaceAll(items2);
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 1), mEvents.get(0));
-        assertEquals(new Event(TYPE.CHANGE, 0, 1), mEvents.get(1));
-        assertEquals(new Event(TYPE.ADD, 1, 2), mEvents.get(2));
-        assertEquals(3, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    @Test
-    public void replaceAll_newItemsAreIdentical_resultIsDeduped() {
-        Item[] items = createItemsFromInts(1, 1);
-        mList.replaceAll(items);
-
-        assertEquals(new Item(1), mList.get(0));
-        assertEquals(1, mList.size());
-    }
-
-    @Test
-    public void replaceAll_newItemsUnsorted_resultIsSorted() {
-        Item[] items = createItemsFromInts(2, 1);
-        mList.replaceAll(items);
-
-        assertEquals(new Item(1), mList.get(0));
-        assertEquals(new Item(2), mList.get(1));
-        assertEquals(2, mList.size());
-    }
-
-    @Test
-    public void replaceAll_calledAfterBeginBatchedUpdates_worksCorrectly() {
-        Item[] items1 = createItemsFromInts(1, 2, 3);
-        Item[] items2 = createItemsFromInts(4, 5, 6);
-        mList.addAll(items1);
-        mEvents.clear();
-
-        mCallbackRunnables = new LinkedList<>();
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-        mCallbackRunnables.add(new AssertListStateRunnable(items2));
-
-        mList.beginBatchedUpdates();
-        mList.replaceAll(items2);
-        mList.endBatchedUpdates();
-
-        assertEquals(new Event(TYPE.REMOVE, 0, 3), mEvents.get(0));
-        assertEquals(new Event(TYPE.ADD, 0, 3), mEvents.get(1));
-        assertEquals(2, mEvents.size());
-        assertTrue(sortedListEquals(mList, items2));
-        assertTrue(mCallbackRunnables.isEmpty());
-    }
-
-    private int size() {
-        return mList.size();
-    }
-
-    private int insert(Item item) {
-        return mList.add(item);
-    }
-
-    private boolean remove(Item item) {
-        return mList.remove(item);
-    }
-
-    static class Item {
-
-        final int id;
-        int cmpField;
-        int data;
-
-        Item(int allFields) {
-            this(allFields, allFields, allFields);
-        }
-
-        Item(int id, int compField, int data) {
-            this.id = id;
-            this.cmpField = compField;
-            this.data = data;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            Item item = (Item) o;
-
-            return id == item.id && cmpField == item.cmpField && data == item.data;
-        }
-
-        @Override
-        public String toString() {
-            return "Item(id=" + id + ", cmpField=" + cmpField + ", data=" + data + ')';
-        }
-    }
-
-    private static final class Pair {
-
-        final int first, second;
-
-        public Pair(int first) {
-            this.first = first;
-            this.second = Integer.MIN_VALUE;
-        }
-
-        public Pair(int first, int second) {
-            this.first = first;
-            this.second = second;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            Pair pair = (Pair) o;
-
-            if (first != pair.first) {
-                return false;
-            }
-            if (second != pair.second) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = first;
-            result = 31 * result + second;
-            return result;
-        }
-    }
-
-    private enum TYPE {
-        ADD, REMOVE, MOVE, CHANGE
-    }
-
-    private final class AssertListStateRunnable implements Runnable {
-
-        private Item[] mExpectedItems;
-
-        AssertListStateRunnable(Item... expectedItems) {
-            this.mExpectedItems = expectedItems;
-        }
-
-        @Override
-        public void run() {
-            try {
-                assertEquals(mExpectedItems.length, mList.size());
-                for (int i = mExpectedItems.length - 1; i >= 0; i--) {
-                    assertEquals(mExpectedItems[i], mList.get(i));
-                    assertEquals(i, mList.indexOf(mExpectedItems[i]));
-                }
-            } catch (AssertionError assertionError) {
-                throw new AssertionError(
-                        assertionError.getMessage()
-                        + "\nExpected: "
-                        + Arrays.toString(mExpectedItems)
-                        + "\nActual: "
-                        + sortedListToString(mList));
-            }
-        }
-    }
-
-    private static final class Event {
-        private final TYPE mType;
-        private final int mVal1;
-        private final int mVal2;
-
-        Event(TYPE type, int val1, int val2) {
-            this.mType = type;
-            this.mVal1 = val1;
-            this.mVal2 = val2;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            Event that = (Event) o;
-            return mType == that.mType && mVal1 == that.mVal1 && mVal2 == that.mVal2;
-        }
-
-        @Override
-        public String toString() {
-            return "Event(" + mType + ", " + mVal1 + ", " + mVal2 + ")";
-        }
-    }
-
-    private <T> boolean sortedListEquals(SortedList<T> sortedList, T[] array) {
-        if (sortedList.size() != array.length) {
-            return false;
-        }
-        for (int i = sortedList.size() - 1; i >= 0; i--) {
-            if (!sortedList.get(i).equals(array[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static String sortedListToString(SortedList sortedList) {
-        StringBuilder stringBuilder = new StringBuilder("[");
-        int size = sortedList.size();
-        for (int i = 0; i < size; i++) {
-            stringBuilder.append(sortedList.get(i).toString() + ", ");
-        }
-        stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
-        stringBuilder.append("]");
-        return stringBuilder.toString();
-    }
-
-    private static final class PayloadChange {
-        public final int position;
-        public final int count;
-        public final Object payload;
-
-        PayloadChange(int position, int count, Object payload) {
-            this.position = position;
-            this.count = count;
-            this.payload = payload;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            PayloadChange payloadChange = (PayloadChange) o;
-
-            if (position != payloadChange.position) return false;
-            if (count != payloadChange.count) return false;
-            return payload != null ? payload.equals(payloadChange.payload)
-                    : payloadChange.payload == null;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = position;
-            result = 31 * result + count;
-            result = 31 * result + (payload != null ? payload.hashCode() : 0);
-            return result;
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/util/ThreadUtil.java b/android/support/v7/util/ThreadUtil.java
deleted file mode 100644
index 05db034..0000000
--- a/android/support/v7/util/ThreadUtil.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.v7.util;
-
-interface ThreadUtil<T> {
-
-    interface MainThreadCallback<T> {
-
-        void updateItemCount(int generation, int itemCount);
-
-        void addTile(int generation, TileList.Tile<T> tile);
-
-        void removeTile(int generation, int position);
-    }
-
-    interface BackgroundCallback<T> {
-
-        void refresh(int generation);
-
-        void updateRange(int rangeStart, int rangeEnd, int extRangeStart, int extRangeEnd,
-                         int scrollHint);
-
-        void loadTile(int position, int scrollHint);
-
-        void recycleTile(TileList.Tile<T> tile);
-    }
-
-    MainThreadCallback<T> getMainThreadProxy(MainThreadCallback<T> callback);
-
-    BackgroundCallback<T> getBackgroundProxy(BackgroundCallback<T> callback);
-}
diff --git a/android/support/v7/util/TileList.java b/android/support/v7/util/TileList.java
deleted file mode 100644
index f686a31..0000000
--- a/android/support/v7/util/TileList.java
+++ /dev/null
@@ -1,105 +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.util;
-
-import android.util.SparseArray;
-
-import java.lang.reflect.Array;
-
-/**
- * A sparse collection of tiles sorted for efficient access.
- */
-class TileList<T> {
-
-    final int mTileSize;
-
-    // Keyed by start position.
-    private final SparseArray<Tile<T>> mTiles = new SparseArray<Tile<T>>(10);
-
-    Tile<T> mLastAccessedTile;
-
-    public TileList(int tileSize) {
-        mTileSize = tileSize;
-    }
-
-    public T getItemAt(int pos) {
-        if (mLastAccessedTile == null || !mLastAccessedTile.containsPosition(pos)) {
-            final int startPosition = pos - (pos % mTileSize);
-            final int index = mTiles.indexOfKey(startPosition);
-            if (index < 0) {
-                return null;
-            }
-            mLastAccessedTile = mTiles.valueAt(index);
-        }
-        return mLastAccessedTile.getByPosition(pos);
-    }
-
-    public int size() {
-        return mTiles.size();
-    }
-
-    public void clear() {
-        mTiles.clear();
-    }
-
-    public Tile<T> getAtIndex(int index) {
-        return mTiles.valueAt(index);
-    }
-
-    public Tile<T> addOrReplace(Tile<T> newTile) {
-        final int index = mTiles.indexOfKey(newTile.mStartPosition);
-        if (index < 0) {
-            mTiles.put(newTile.mStartPosition, newTile);
-            return null;
-        }
-        Tile<T> oldTile = mTiles.valueAt(index);
-        mTiles.setValueAt(index, newTile);
-        if (mLastAccessedTile == oldTile) {
-            mLastAccessedTile = newTile;
-        }
-        return oldTile;
-    }
-
-    public Tile<T> removeAtPos(int startPosition) {
-        Tile<T> tile = mTiles.get(startPosition);
-        if (mLastAccessedTile == tile) {
-            mLastAccessedTile = null;
-        }
-        mTiles.delete(startPosition);
-        return tile;
-    }
-
-    public static class Tile<T> {
-        public final T[] mItems;
-        public int mStartPosition;
-        public int mItemCount;
-        Tile<T> mNext;  // Used only for pooling recycled tiles.
-
-        public Tile(Class<T> klass, int size) {
-            //noinspection unchecked
-            mItems = (T[]) Array.newInstance(klass, size);
-        }
-
-        boolean containsPosition(int pos) {
-            return mStartPosition <= pos && pos < mStartPosition + mItemCount;
-        }
-
-        T getByPosition(int pos) {
-            return mItems[pos - mStartPosition];
-        }
-    }
-}
diff --git a/android/support/v7/view/ActionBarPolicy.java b/android/support/v7/view/ActionBarPolicy.java
deleted file mode 100644
index d9135d2..0000000
--- a/android/support/v7/view/ActionBarPolicy.java
+++ /dev/null
@@ -1,117 +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.view;
-
-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.v7.appcompat.R;
-import android.view.ViewConfiguration;
-
-/**
- * Allows components to query for various configuration policy decisions about how the action bar
- * should lay out and behave on the current device.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionBarPolicy {
-
-    private Context mContext;
-
-    public static ActionBarPolicy get(Context context) {
-        return new ActionBarPolicy(context);
-    }
-
-    private ActionBarPolicy(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Returns the maximum number of action buttons that should be permitted within an action
-     * bar/action mode. This will be used to determine how many showAsAction="ifRoom" items can fit.
-     * "always" items can override this.
-     */
-    public int getMaxActionButtons() {
-        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)) {
-            // For values-w600dp, values-sw600dp and values-xlarge.
-            return 5;
-        } else if (widthDp >= 500 || (widthDp > 640 && heightDp > 480)
-                || (widthDp > 480 && heightDp > 640)) {
-            // For values-w500dp and values-large.
-            return 4;
-        } else if (widthDp >= 360) {
-            // For values-w360dp.
-            return 3;
-        } else {
-            return 2;
-        }
-    }
-
-    public boolean showsOverflowMenuButton() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            return true;
-        } else {
-            return !ViewConfiguration.get(mContext).hasPermanentMenuKey();
-        }
-    }
-
-    public int getEmbeddedMenuWidthLimit() {
-        return mContext.getResources().getDisplayMetrics().widthPixels / 2;
-    }
-
-    public boolean hasEmbeddedTabs() {
-        return mContext.getResources().getBoolean(R.bool.abc_action_bar_embed_tabs);
-    }
-
-    public int getTabContainerHeight() {
-        TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar,
-                R.attr.actionBarStyle, 0);
-        int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
-        Resources r = mContext.getResources();
-        if (!hasEmbeddedTabs()) {
-            // Stacked tabs; limit the height
-            height = Math.min(height,
-                    r.getDimensionPixelSize(R.dimen.abc_action_bar_stacked_max_height));
-        }
-        a.recycle();
-        return height;
-    }
-
-    public boolean enableHomeButtonByDefault() {
-        // Older apps get the home button interaction enabled by default.
-        // Newer apps need to enable it explicitly.
-        return mContext.getApplicationInfo().targetSdkVersion <
-                Build.VERSION_CODES.ICE_CREAM_SANDWICH;
-    }
-
-    public int getStackedTabMaxWidth() {
-        return mContext.getResources().getDimensionPixelSize(
-                R.dimen.abc_action_bar_stacked_tab_max_width);
-    }
-}
diff --git a/android/support/v7/view/ActionMode.java b/android/support/v7/view/ActionMode.java
deleted file mode 100644
index 55d02b8..0000000
--- a/android/support/v7/view/ActionMode.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-
-/**
- * Represents a contextual mode of the user interface. Action modes can be used to provide
- * alternative interaction modes and replace parts of the normal UI until finished.
- * Examples of good action modes include text selection and contextual actions.
- * <div class="special reference">
- *
- * <h3>Developer Guides</h3>
- * <p>For information about how to provide contextual actions with {@code ActionMode},
- * read the <a href="{@docRoot}guide/topics/ui/menus.html#context-menu">Menus</a>
- * developer guide.</p>
- *
- * </div>
- */
-public abstract class ActionMode {
-
-    private Object mTag;
-    private boolean mTitleOptionalHint;
-
-    /**
-     * Set a tag object associated with this ActionMode.
-     *
-     * <p>Like the tag available to views, this allows applications to associate arbitrary
-     * data with an ActionMode for later reference.
-     *
-     * @param tag Tag to associate with this ActionMode
-     *
-     * @see #getTag()
-     */
-    public void setTag(Object tag) {
-        mTag = tag;
-    }
-
-    /**
-     * Retrieve the tag object associated with this ActionMode.
-     *
-     * <p>Like the tag available to views, this allows applications to associate arbitrary
-     * data with an ActionMode for later reference.
-     *
-     * @return Tag associated with this ActionMode
-     *
-     * @see #setTag(Object)
-     */
-    public Object getTag() {
-        return mTag;
-    }
-
-    /**
-     * Set the title of the action mode. This method will have no visible effect if
-     * a custom view has been set.
-     *
-     * @param title Title string to set
-     *
-     * @see #setTitle(int)
-     * @see #setCustomView(View)
-     */
-    public abstract void setTitle(CharSequence title);
-
-    /**
-     * Set the title of the action mode. This method will have no visible effect if
-     * a custom view has been set.
-     *
-     * @param resId Resource ID of a string to set as the title
-     *
-     * @see #setTitle(CharSequence)
-     * @see #setCustomView(View)
-     */
-    public abstract void setTitle(int resId);
-
-    /**
-     * Set the subtitle of the action mode. This method will have no visible effect if
-     * a custom view has been set.
-     *
-     * @param subtitle Subtitle string to set
-     *
-     * @see #setSubtitle(int)
-     * @see #setCustomView(View)
-     */
-    public abstract void setSubtitle(CharSequence subtitle);
-
-    /**
-     * Set the subtitle of the action mode. This method will have no visible effect if
-     * a custom view has been set.
-     *
-     * @param resId Resource ID of a string to set as the subtitle
-     *
-     * @see #setSubtitle(CharSequence)
-     * @see #setCustomView(View)
-     */
-    public abstract void setSubtitle(int resId);
-
-    /**
-     * Set whether or not the title/subtitle display for this action mode
-     * is optional.
-     *
-     * <p>In many cases the supplied title for an action mode is merely
-     * meant to add context and is not strictly required for the action
-     * mode to be useful. If the title is optional, the system may choose
-     * to hide the title entirely rather than truncate it due to a lack
-     * of available space.</p>
-     *
-     * <p>Note that this is merely a hint; the underlying implementation
-     * may choose to ignore this setting under some circumstances.</p>
-     *
-     * @param titleOptional true if the title only presents optional information.
-     */
-    public void setTitleOptionalHint(boolean titleOptional) {
-        mTitleOptionalHint = titleOptional;
-    }
-
-    /**
-     * @return true if this action mode has been given a hint to consider the
-     *         title/subtitle display to be optional.
-     *
-     * @see #setTitleOptionalHint(boolean)
-     * @see #isTitleOptional()
-     */
-    public boolean getTitleOptionalHint() {
-        return mTitleOptionalHint;
-    }
-
-    /**
-     * @return true if this action mode considers the title and subtitle fields
-     *         as optional. Optional titles may not be displayed to the user.
-     */
-    public boolean isTitleOptional() {
-        return false;
-    }
-
-    /**
-     * Set a custom view for this action mode. The custom view will take the place of
-     * the title and subtitle. Useful for things like search boxes.
-     *
-     * @param view Custom view to use in place of the title/subtitle.
-     *
-     * @see #setTitle(CharSequence)
-     * @see #setSubtitle(CharSequence)
-     */
-    public abstract void setCustomView(View view);
-
-    /**
-     * Invalidate the action mode and refresh menu content. The mode's
-     * {@link ActionMode.Callback} will have its
-     * {@link Callback#onPrepareActionMode(ActionMode, Menu)} method called.
-     * If it returns true the menu will be scanned for updated content and any relevant changes
-     * will be reflected to the user.
-     */
-    public abstract void invalidate();
-
-    /**
-     * Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
-     * have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
-     */
-    public abstract void finish();
-
-    /**
-     * Returns the menu of actions that this action mode presents.
-     *
-     * @return The action mode's menu.
-     */
-    public abstract Menu getMenu();
-
-    /**
-     * Returns the current title of this action mode.
-     *
-     * @return Title text
-     */
-    public abstract CharSequence getTitle();
-
-    /**
-     * Returns the current subtitle of this action mode.
-     *
-     * @return Subtitle text
-     */
-    public abstract CharSequence getSubtitle();
-
-    /**
-     * Returns the current custom view for this action mode.
-     *
-     * @return The current custom view
-     */
-    public abstract View getCustomView();
-
-    /**
-     * Returns a {@link MenuInflater} with the ActionMode's context.
-     */
-    public abstract MenuInflater getMenuInflater();
-
-    /**
-     * Returns whether the UI presenting this action mode can take focus or not.
-     * This is used by internal components within the framework that would otherwise
-     * present an action mode UI that requires focus, such as an EditText as a custom view.
-     *
-     * @return true if the UI used to show this action mode can take focus
-     * @hide Internal use only
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isUiFocusable() {
-        return true;
-    }
-
-    /**
-     * Callback interface for action modes. Supplied to
-     * {@link android.support.v7.app.AppCompatDelegate#startSupportActionMode(Callback)} (Callback)},
-     * a Callback configures and handles events raised by a user's interaction with an action mode.
-     *
-     * <p>An action mode's lifecycle is as follows:
-     * <ul>
-     * <li>{@link Callback#onCreateActionMode(ActionMode, Menu)} once on initial
-     * creation</li>
-     * <li>{@link Callback#onPrepareActionMode(ActionMode, Menu)} after creation
-     * and any time the {@link ActionMode} is invalidated</li>
-     * <li>{@link Callback#onActionItemClicked(ActionMode, MenuItem)} any time a
-     * contextual action button is clicked</li>
-     * <li>{@link Callback#onDestroyActionMode(ActionMode)} when the action mode
-     * is closed</li>
-     * </ul>
-     */
-    public interface Callback {
-
-        /**
-         * Called when action mode is first created. The menu supplied will be used to
-         * generate action buttons for the action mode.
-         *
-         * @param mode ActionMode being created
-         * @param menu Menu used to populate action buttons
-         * @return true if the action mode should be created, false if entering this
-         *              mode should be aborted.
-         */
-        public boolean onCreateActionMode(ActionMode mode, Menu menu);
-
-        /**
-         * Called to refresh an action mode's action menu whenever it is invalidated.
-         *
-         * @param mode ActionMode being prepared
-         * @param menu Menu used to populate action buttons
-         * @return true if the menu or action mode was updated, false otherwise.
-         */
-        public boolean onPrepareActionMode(ActionMode mode, Menu menu);
-
-        /**
-         * Called to report a user click on an action button.
-         *
-         * @param mode The current ActionMode
-         * @param item The item that was clicked
-         * @return true if this callback handled the event, false if the standard MenuItem
-         *         invocation should continue.
-         */
-        public boolean onActionItemClicked(ActionMode mode, MenuItem item);
-
-        /**
-         * Called when an action mode is about to be exited and destroyed.
-         *
-         * @param mode The current ActionMode being destroyed
-         */
-        public void onDestroyActionMode(ActionMode mode);
-    }
-}
diff --git a/android/support/v7/view/CollapsibleActionView.java b/android/support/v7/view/CollapsibleActionView.java
deleted file mode 100644
index e84eaac..0000000
--- a/android/support/v7/view/CollapsibleActionView.java
+++ /dev/null
@@ -1,40 +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.v7.view;
-
-/**
- * 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
- * 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.
- */
-public interface CollapsibleActionView {
-
-    /**
-     * Called when this view is expanded as an action view. See
-     * {@link android.view.MenuItem#expandActionView()}.
-     */
-    void onActionViewExpanded();
-
-    /**
-     * Called when this view is collapsed as an action view. See
-     * {@link android.view.MenuItem#collapseActionView()}.
-     */
-    void onActionViewCollapsed();
-}
diff --git a/android/support/v7/view/ContextThemeWrapper.java b/android/support/v7/view/ContextThemeWrapper.java
deleted file mode 100644
index cc63480..0000000
--- a/android/support/v7/view/ContextThemeWrapper.java
+++ /dev/null
@@ -1,197 +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.view;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.AssetManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Build;
-import android.support.annotation.StyleRes;
-import android.support.v7.appcompat.R;
-import android.view.LayoutInflater;
-
-/**
- * A context wrapper that allows you to modify or replace the theme of the wrapped context.
- */
-public class ContextThemeWrapper extends ContextWrapper {
-    private int mThemeResource;
-    private Resources.Theme mTheme;
-    private LayoutInflater mInflater;
-    private Configuration mOverrideConfiguration;
-    private Resources mResources;
-
-    /**
-     * Creates a new context wrapper with no theme and no base context.
-     * <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.
-     */
-    public ContextThemeWrapper() {
-        super(null);
-    }
-
-    /**
-     * Creates a new context wrapper with the specified theme.
-     * <p>
-     * The specified theme will be applied on top of the base context's theme.
-     * Any attributes not explicitly defined in the theme identified by
-     * <var>themeResId</var> will retain their original values.
-     *
-     * @param base the base context
-     * @param themeResId the resource ID of the theme to be applied on top of
-     *                   the base context's theme
-     */
-    public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
-        super(base);
-        mThemeResource = themeResId;
-    }
-
-    /**
-     * Creates a new context wrapper with the specified theme.
-     * <p>
-     * Unlike {@link #ContextThemeWrapper(Context, int)}, the theme passed to
-     * this constructor will completely replace the base context's theme.
-     *
-     * @param base the base context
-     * @param theme the theme against which resources should be inflated
-     */
-    public ContextThemeWrapper(Context base, Resources.Theme theme) {
-        super(base);
-        mTheme = theme;
-    }
-
-    @Override
-    protected void attachBaseContext(Context newBase) {
-        super.attachBaseContext(newBase);
-    }
-
-    /**
-     * Call to set an "override configuration" on this context -- this is
-     * a configuration that replies one or more values of the standard
-     * configuration that is applied to the context.  See
-     * {@link Context#createConfigurationContext(Configuration)} for more
-     * information.
-     *
-     * <p>This method can only be called once, and must be called before any
-     * calls to {@link #getResources()} or {@link #getAssets()} are made.
-     */
-    public void applyOverrideConfiguration(Configuration overrideConfiguration) {
-        if (mResources != null) {
-            throw new IllegalStateException(
-                    "getResources() or getAssets() has already been called");
-        }
-        if (mOverrideConfiguration != null) {
-            throw new IllegalStateException("Override configuration has already been set");
-        }
-        mOverrideConfiguration = new Configuration(overrideConfiguration);
-    }
-
-    @Override
-    public Resources getResources() {
-        return getResourcesInternal();
-    }
-
-    private Resources getResourcesInternal() {
-        if (mResources == null) {
-            if (mOverrideConfiguration == null) {
-                mResources = super.getResources();
-            } else if (Build.VERSION.SDK_INT >= 17) {
-                final Context resContext = createConfigurationContext(mOverrideConfiguration);
-                mResources = resContext.getResources();
-            }
-        }
-        return mResources;
-    }
-
-    @Override
-    public void setTheme(int resid) {
-        if (mThemeResource != resid) {
-            mThemeResource = resid;
-            initializeTheme();
-        }
-    }
-
-    /**
-     * Returns the resource ID of the theme that is to be applied on top of the base context's
-     * theme.
-     */
-    public int getThemeResId() {
-        return mThemeResource;
-    }
-
-    @Override
-    public Resources.Theme getTheme() {
-        if (mTheme != null) {
-            return mTheme;
-        }
-
-        if (mThemeResource == 0) {
-            mThemeResource = R.style.Theme_AppCompat_Light;
-        }
-        initializeTheme();
-
-        return mTheme;
-    }
-
-    @Override
-    public Object getSystemService(String name) {
-        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
-            if (mInflater == null) {
-                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
-            }
-            return mInflater;
-        }
-        return getBaseContext().getSystemService(name);
-    }
-
-    /**
-     * Called by {@link #setTheme} and {@link #getTheme} to apply a theme
-     * resource to the current Theme object.  Can override to change the
-     * default (simple) behavior.  This method will not be called in multiple
-     * threads simultaneously.
-     *
-     * @param theme The Theme object being modified.
-     * @param resid The theme style resource being applied to <var>theme</var>.
-     * @param first Set to true if this is the first time a style is being
-     *              applied to <var>theme</var>.
-     */
-    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
-        theme.applyStyle(resid, true);
-    }
-
-    private void initializeTheme() {
-        final boolean first = mTheme == null;
-        if (first) {
-            mTheme = getResources().newTheme();
-            Resources.Theme theme = getBaseContext().getTheme();
-            if (theme != null) {
-                mTheme.setTo(theme);
-            }
-        }
-        onApplyThemeResource(mTheme, mThemeResource, first);
-    }
-
-    @Override
-    public AssetManager getAssets() {
-        // Ensure we're returning assets with the correct configuration.
-        return getResources().getAssets();
-    }
-}
-
diff --git a/android/support/v7/view/StandaloneActionMode.java b/android/support/v7/view/StandaloneActionMode.java
deleted file mode 100644
index bfe98d0..0000000
--- a/android/support/v7/view/StandaloneActionMode.java
+++ /dev/null
@@ -1,168 +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.view;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPopupHelper;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.support.v7.widget.ActionBarContextView;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.lang.ref.WeakReference;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class StandaloneActionMode extends ActionMode implements MenuBuilder.Callback {
-    private Context mContext;
-    private ActionBarContextView mContextView;
-    private ActionMode.Callback mCallback;
-    private WeakReference<View> mCustomView;
-    private boolean mFinished;
-    private boolean mFocusable;
-
-    private MenuBuilder mMenu;
-
-    public StandaloneActionMode(Context context, ActionBarContextView view,
-            ActionMode.Callback callback, boolean isFocusable) {
-        mContext = context;
-        mContextView = view;
-        mCallback = callback;
-
-        mMenu = new MenuBuilder(view.getContext()).setDefaultShowAsAction(
-                MenuItem.SHOW_AS_ACTION_IF_ROOM);
-        mMenu.setCallback(this);
-        mFocusable = isFocusable;
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mContextView.setTitle(title);
-    }
-
-    @Override
-    public void setSubtitle(CharSequence subtitle) {
-        mContextView.setSubtitle(subtitle);
-    }
-
-    @Override
-    public void setTitle(int resId) {
-        setTitle(mContext.getString(resId));
-    }
-
-    @Override
-    public void setSubtitle(int resId) {
-        setSubtitle(mContext.getString(resId));
-    }
-
-    @Override
-    public void setTitleOptionalHint(boolean titleOptional) {
-        super.setTitleOptionalHint(titleOptional);
-        mContextView.setTitleOptional(titleOptional);
-    }
-
-    @Override
-    public boolean isTitleOptional() {
-        return mContextView.isTitleOptional();
-    }
-
-    @Override
-    public void setCustomView(View view) {
-        mContextView.setCustomView(view);
-        mCustomView = view != null ? new WeakReference<View>(view) : null;
-    }
-
-    @Override
-    public void invalidate() {
-        mCallback.onPrepareActionMode(this, mMenu);
-    }
-
-    @Override
-    public void finish() {
-        if (mFinished) {
-            return;
-        }
-        mFinished = true;
-
-        mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-        mCallback.onDestroyActionMode(this);
-    }
-
-    @Override
-    public Menu getMenu() {
-        return mMenu;
-    }
-
-    @Override
-    public CharSequence getTitle() {
-        return mContextView.getTitle();
-    }
-
-    @Override
-    public CharSequence getSubtitle() {
-        return mContextView.getSubtitle();
-    }
-
-    @Override
-    public View getCustomView() {
-        return mCustomView != null ? mCustomView.get() : null;
-    }
-
-    @Override
-    public MenuInflater getMenuInflater() {
-        return new SupportMenuInflater(mContextView.getContext());
-    }
-
-    @Override
-    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-        return mCallback.onActionItemClicked(this, item);
-    }
-
-    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-    }
-
-    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-        if (!subMenu.hasVisibleItems()) {
-            return true;
-        }
-
-        new MenuPopupHelper(mContextView.getContext(), subMenu).show();
-        return true;
-    }
-
-    public void onCloseSubMenu(SubMenuBuilder menu) {
-    }
-
-    @Override
-    public void onMenuModeChange(MenuBuilder menu) {
-        invalidate();
-        mContextView.showOverflowMenu();
-    }
-
-    @Override
-    public boolean isUiFocusable() {
-        return mFocusable;
-    }
-}
diff --git a/android/support/v7/view/SupportActionModeWrapper.java b/android/support/v7/view/SupportActionModeWrapper.java
deleted file mode 100644
index fa7fe1b..0000000
--- a/android/support/v7/view/SupportActionModeWrapper.java
+++ /dev/null
@@ -1,205 +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.v7.view;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v4.internal.view.SupportMenu;
-import android.support.v4.internal.view.SupportMenuItem;
-import android.support.v4.util.SimpleArrayMap;
-import android.support.v7.view.menu.MenuWrapperFactory;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.View;
-
-import java.util.ArrayList;
-
-/**
- * Wraps a support {@link android.support.v7.view.ActionMode} as a framework
- * {@link android.view.ActionMode}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class SupportActionModeWrapper extends ActionMode {
-
-    final Context mContext;
-    final android.support.v7.view.ActionMode mWrappedObject;
-
-    public SupportActionModeWrapper(Context context,
-            android.support.v7.view.ActionMode supportActionMode) {
-        mContext = context;
-        mWrappedObject = supportActionMode;
-    }
-
-    @Override
-    public Object getTag() {
-        return mWrappedObject.getTag();
-    }
-
-    @Override
-    public void setTag(Object tag) {
-        mWrappedObject.setTag(tag);
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mWrappedObject.setTitle(title);
-    }
-
-    @Override
-    public void setSubtitle(CharSequence subtitle) {
-        mWrappedObject.setSubtitle(subtitle);
-    }
-
-    @Override
-    public void invalidate() {
-        mWrappedObject.invalidate();
-    }
-
-    @Override
-    public void finish() {
-        mWrappedObject.finish();
-    }
-
-    @Override
-    public Menu getMenu() {
-        return MenuWrapperFactory.wrapSupportMenu(mContext, (SupportMenu) mWrappedObject.getMenu());
-    }
-
-    @Override
-    public CharSequence getTitle() {
-        return mWrappedObject.getTitle();
-    }
-
-    @Override
-    public void setTitle(int resId) {
-        mWrappedObject.setTitle(resId);
-    }
-
-    @Override
-    public CharSequence getSubtitle() {
-        return mWrappedObject.getSubtitle();
-    }
-
-    @Override
-    public void setSubtitle(int resId) {
-        mWrappedObject.setSubtitle(resId);
-    }
-
-    @Override
-    public View getCustomView() {
-        return mWrappedObject.getCustomView();
-    }
-
-    @Override
-    public void setCustomView(View view) {
-        mWrappedObject.setCustomView(view);
-    }
-
-    @Override
-    public MenuInflater getMenuInflater() {
-        return mWrappedObject.getMenuInflater();
-    }
-
-    @Override
-    public boolean getTitleOptionalHint() {
-        return mWrappedObject.getTitleOptionalHint();
-    }
-
-    @Override
-    public void setTitleOptionalHint(boolean titleOptional) {
-        mWrappedObject.setTitleOptionalHint(titleOptional);
-    }
-
-    @Override
-    public boolean isTitleOptional() {
-        return mWrappedObject.isTitleOptional();
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class CallbackWrapper implements android.support.v7.view.ActionMode.Callback {
-        final Callback mWrappedCallback;
-        final Context mContext;
-
-        final ArrayList<SupportActionModeWrapper> mActionModes;
-        final SimpleArrayMap<Menu, Menu> mMenus;
-
-        public CallbackWrapper(Context context, Callback supportCallback) {
-            mContext = context;
-            mWrappedCallback = supportCallback;
-            mActionModes = new ArrayList<>();
-            mMenus = new SimpleArrayMap<>();
-        }
-
-        @Override
-        public boolean onCreateActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
-            return mWrappedCallback.onCreateActionMode(getActionModeWrapper(mode),
-                    getMenuWrapper(menu));
-        }
-
-        @Override
-        public boolean onPrepareActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
-            return mWrappedCallback.onPrepareActionMode(getActionModeWrapper(mode),
-                    getMenuWrapper(menu));
-        }
-
-        @Override
-        public boolean onActionItemClicked(android.support.v7.view.ActionMode mode,
-                android.view.MenuItem item) {
-            return mWrappedCallback.onActionItemClicked(getActionModeWrapper(mode),
-                    MenuWrapperFactory.wrapSupportMenuItem(mContext, (SupportMenuItem) item));
-        }
-
-        @Override
-        public void onDestroyActionMode(android.support.v7.view.ActionMode mode) {
-            mWrappedCallback.onDestroyActionMode(getActionModeWrapper(mode));
-        }
-
-        private Menu getMenuWrapper(Menu menu) {
-            Menu wrapper = mMenus.get(menu);
-            if (wrapper == null) {
-                wrapper = MenuWrapperFactory.wrapSupportMenu(mContext, (SupportMenu) menu);
-                mMenus.put(menu, wrapper);
-            }
-            return wrapper;
-        }
-
-        public ActionMode getActionModeWrapper(android.support.v7.view.ActionMode mode) {
-            // First see if we already have a wrapper for this mode
-            for (int i = 0, count = mActionModes.size(); i < count; i++) {
-                SupportActionModeWrapper wrapper = mActionModes.get(i);
-                if (wrapper != null && wrapper.mWrappedObject == mode) {
-                    // We've found a wrapper, return it
-                    return wrapper;
-                }
-            }
-
-            // If we reach here then we haven't seen this mode before. Create a new wrapper and
-            // add it to our collection
-            SupportActionModeWrapper wrapper = new SupportActionModeWrapper(mContext, mode);
-            mActionModes.add(wrapper);
-            return wrapper;
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/view/SupportMenuInflater.java b/android/support/v7/view/SupportMenuInflater.java
deleted file mode 100644
index 0522361..0000000
--- a/android/support/v7/view/SupportMenuInflater.java
+++ /dev/null
@@ -1,557 +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.v7.view;
-
-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.ColorStateList;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.PorterDuff;
-import android.support.annotation.LayoutRes;
-import android.support.annotation.RestrictTo;
-import android.support.v4.internal.view.SupportMenu;
-import android.support.v4.view.ActionProvider;
-import android.support.v4.view.MenuItemCompat;
-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;
-import android.view.SubMenu;
-import android.view.View;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-
-/**
- * This class is used to instantiate menu XML files into Menu objects.
- * <p>
- * For performance reasons, menu inflation relies heavily on pre-processing of
- * XML files that is done at build time. Therefore, it is not currently possible
- * to use SupportMenuInflater with an XmlPullParser over a plain XML file at runtime;
- * it only works with an XmlPullParser returned from a compiled resource (R.
- * <em>something</em> file.)
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class SupportMenuInflater extends MenuInflater {
-    static final String LOG_TAG = "SupportMenuInflater";
-
-    /** Menu tag name in XML. */
-    private static final String XML_MENU = "menu";
-
-    /** Group tag name in XML. */
-    private static final String XML_GROUP = "group";
-
-    /** Item tag name in XML. */
-    private static final String XML_ITEM = "item";
-
-    static final int NO_ID = 0;
-
-    static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
-
-    static final Class<?>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE =
-            ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
-
-    final Object[] mActionViewConstructorArguments;
-
-    final Object[] mActionProviderConstructorArguments;
-
-    Context mContext;
-    private Object mRealOwner;
-
-    /**
-     * Constructs a menu inflater.
-     *
-     * @see Activity#getMenuInflater()
-     */
-    public SupportMenuInflater(Context context) {
-        super(context);
-        mContext = context;
-        mActionViewConstructorArguments = new Object[] {context};
-        mActionProviderConstructorArguments = mActionViewConstructorArguments;
-    }
-
-    /**
-     * Inflate a menu hierarchy from the specified XML resource. Throws
-     * {@link InflateException} if there is an error.
-     *
-     * @param menuRes Resource ID for an XML layout resource to load (e.g.,
-     *            <code>R.menu.main_activity</code>)
-     * @param menu The Menu to inflate into. The items and submenus will be
-     *            added to this Menu.
-     */
-    @Override
-    public void inflate(@LayoutRes int menuRes, Menu menu) {
-        // If we're not dealing with a SupportMenu instance, let super handle
-        if (!(menu instanceof SupportMenu)) {
-            super.inflate(menuRes, menu);
-            return;
-        }
-
-        XmlResourceParser parser = null;
-        try {
-            parser = mContext.getResources().getLayout(menuRes);
-            AttributeSet attrs = Xml.asAttributeSet(parser);
-
-            parseMenu(parser, attrs, menu);
-        } catch (XmlPullParserException e) {
-            throw new InflateException("Error inflating menu XML", e);
-        } catch (IOException e) {
-            throw new InflateException("Error inflating menu XML", e);
-        } finally {
-            if (parser != null) parser.close();
-        }
-    }
-
-    /**
-     * Called internally to fill the given menu. If a sub menu is seen, it will
-     * call this recursively.
-     */
-    private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
-            throws XmlPullParserException, IOException {
-        MenuState menuState = new MenuState(menu);
-
-        int eventType = parser.getEventType();
-        String tagName;
-        boolean lookingForEndOfUnknownTag = false;
-        String unknownTagName = null;
-
-        // This loop will skip to the menu start tag
-        do {
-            if (eventType == XmlPullParser.START_TAG) {
-                tagName = parser.getName();
-                if (tagName.equals(XML_MENU)) {
-                    // Go to next tag
-                    eventType = parser.next();
-                    break;
-                }
-
-                throw new RuntimeException("Expecting menu, got " + tagName);
-            }
-            eventType = parser.next();
-        } while (eventType != XmlPullParser.END_DOCUMENT);
-
-        boolean reachedEndOfMenu = false;
-        while (!reachedEndOfMenu) {
-            switch (eventType) {
-                case XmlPullParser.START_TAG:
-                    if (lookingForEndOfUnknownTag) {
-                        break;
-                    }
-
-                    tagName = parser.getName();
-                    if (tagName.equals(XML_GROUP)) {
-                        menuState.readGroup(attrs);
-                    } else if (tagName.equals(XML_ITEM)) {
-                        menuState.readItem(attrs);
-                    } else if (tagName.equals(XML_MENU)) {
-                        // A menu start tag denotes a submenu for an item
-                        SubMenu subMenu = menuState.addSubMenuItem();
-
-                        // Parse the submenu into returned SubMenu
-                        parseMenu(parser, attrs, subMenu);
-                    } else {
-                        lookingForEndOfUnknownTag = true;
-                        unknownTagName = tagName;
-                    }
-                    break;
-
-                case XmlPullParser.END_TAG:
-                    tagName = parser.getName();
-                    if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
-                        lookingForEndOfUnknownTag = false;
-                        unknownTagName = null;
-                    } else if (tagName.equals(XML_GROUP)) {
-                        menuState.resetGroup();
-                    } else if (tagName.equals(XML_ITEM)) {
-                        // Add the item if it hasn't been added (if the item was
-                        // a submenu, it would have been added already)
-                        if (!menuState.hasAddedItem()) {
-                            if (menuState.itemActionProvider != null &&
-                                    menuState.itemActionProvider.hasSubMenu()) {
-                                menuState.addSubMenuItem();
-                            } else {
-                                menuState.addItem();
-                            }
-                        }
-                    } else if (tagName.equals(XML_MENU)) {
-                        reachedEndOfMenu = true;
-                    }
-                    break;
-
-                case XmlPullParser.END_DOCUMENT:
-                    throw new RuntimeException("Unexpected end of document");
-            }
-
-            eventType = parser.next();
-        }
-    }
-
-    Object getRealOwner() {
-        if (mRealOwner == null) {
-            mRealOwner = findRealOwner(mContext);
-        }
-        return mRealOwner;
-    }
-
-    private Object findRealOwner(Object owner) {
-        if (owner instanceof Activity) {
-            return owner;
-        }
-        if (owner instanceof ContextWrapper) {
-            return findRealOwner(((ContextWrapper) owner).getBaseContext());
-        }
-        return owner;
-    }
-
-    private static class InflatedOnMenuItemClickListener
-            implements MenuItem.OnMenuItemClickListener {
-        private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
-
-        private Object mRealOwner;
-        private Method mMethod;
-
-        public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
-            mRealOwner = realOwner;
-            Class<?> c = realOwner.getClass();
-            try {
-                mMethod = c.getMethod(methodName, PARAM_TYPES);
-            } catch (Exception e) {
-                InflateException ex = new InflateException(
-                        "Couldn't resolve menu item onClick handler " + methodName +
-                                " in class " + c.getName());
-                ex.initCause(e);
-                throw ex;
-            }
-        }
-
-        @Override
-        public boolean onMenuItemClick(MenuItem item) {
-            try {
-                if (mMethod.getReturnType() == Boolean.TYPE) {
-                    return (Boolean) mMethod.invoke(mRealOwner, item);
-                } else {
-                    mMethod.invoke(mRealOwner, item);
-                    return true;
-                }
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    /**
-     * State for the current menu.
-     * <p>
-     * Groups can not be nested unless there is another menu (which will have
-     * its state class).
-     */
-    private class MenuState {
-        private Menu menu;
-
-        /*
-         * Group state is set on items as they are added, allowing an item to
-         * override its group state. (As opposed to set on items at the group end tag.)
-         */
-        private int groupId;
-        private int groupCategory;
-        private int groupOrder;
-        private int groupCheckable;
-        private boolean groupVisible;
-        private boolean groupEnabled;
-
-        private boolean itemAdded;
-        private int itemId;
-        private int itemCategoryOrder;
-        private CharSequence itemTitle;
-        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
-         * - 1: all
-         * - 2: exclusive
-         */
-        private int itemCheckable;
-        private boolean itemChecked;
-        private boolean itemVisible;
-        private boolean itemEnabled;
-
-        /**
-         * Sync to attrs.xml enum, values in MenuItem:
-         * - 0: never
-         * - 1: ifRoom
-         * - 2: always
-         * - -1: Safe sentinel for "no value".
-         */
-        private int itemShowAsAction;
-
-        private int itemActionViewLayout;
-        private String itemActionViewClassName;
-        private String itemActionProviderClassName;
-
-        private String itemListenerMethodName;
-
-        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;
-        private static final int defaultItemOrder = 0;
-        private static final int defaultItemCheckable = 0;
-        private static final boolean defaultItemChecked = false;
-        private static final boolean defaultItemVisible = true;
-        private static final boolean defaultItemEnabled = true;
-
-        public MenuState(final Menu menu) {
-            this.menu = menu;
-
-            resetGroup();
-        }
-
-        public void resetGroup() {
-            groupId = defaultGroupId;
-            groupCategory = defaultItemCategory;
-            groupOrder = defaultItemOrder;
-            groupCheckable = defaultItemCheckable;
-            groupVisible = defaultItemVisible;
-            groupEnabled = defaultItemEnabled;
-        }
-
-        /**
-         * Called when the parser is pointing to a group tag.
-         */
-        public void readGroup(AttributeSet attrs) {
-            TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuGroup);
-
-            groupId = a.getResourceId(R.styleable.MenuGroup_android_id, defaultGroupId);
-            groupCategory = a.getInt(
-                    R.styleable.MenuGroup_android_menuCategory, defaultItemCategory);
-            groupOrder = a.getInt(R.styleable.MenuGroup_android_orderInCategory, defaultItemOrder);
-            groupCheckable = a.getInt(
-                    R.styleable.MenuGroup_android_checkableBehavior, defaultItemCheckable);
-            groupVisible = a.getBoolean(R.styleable.MenuGroup_android_visible, defaultItemVisible);
-            groupEnabled = a.getBoolean(R.styleable.MenuGroup_android_enabled, defaultItemEnabled);
-
-            a.recycle();
-        }
-
-        /**
-         * Called when the parser is pointing to an item tag.
-         */
-        public void readItem(AttributeSet attrs) {
-            TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.MenuItem);
-
-            // Inherit attributes from the group as default value
-            itemId = a.getResourceId(R.styleable.MenuItem_android_id, defaultItemId);
-            final int category = a.getInt(R.styleable.MenuItem_android_menuCategory, groupCategory);
-            final int order = a.getInt(R.styleable.MenuItem_android_orderInCategory, groupOrder);
-            itemCategoryOrder = (category & SupportMenu.CATEGORY_MASK) |
-                    (order & SupportMenu.USER_MASK);
-            itemTitle = a.getText(R.styleable.MenuItem_android_title);
-            itemTitleCondensed = a.getText(R.styleable.MenuItem_android_titleCondensed);
-            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;
-            } else {
-                // Item does not have attribute, use the group's (group can have one more state
-                // for checkable that represents the exclusive checkable)
-                itemCheckable = groupCheckable;
-            }
-            itemChecked = a.getBoolean(R.styleable.MenuItem_android_checked, defaultItemChecked);
-            itemVisible = a.getBoolean(R.styleable.MenuItem_android_visible, groupVisible);
-            itemEnabled = a.getBoolean(R.styleable.MenuItem_android_enabled, groupEnabled);
-            itemShowAsAction = a.getInt(R.styleable.MenuItem_showAsAction, -1);
-            itemListenerMethodName = a.getString(R.styleable.MenuItem_android_onClick);
-            itemActionViewLayout = a.getResourceId(R.styleable.MenuItem_actionLayout, 0);
-            itemActionViewClassName = a.getString(R.styleable.MenuItem_actionViewClass);
-            itemActionProviderClassName = a.getString(R.styleable.MenuItem_actionProviderClass);
-
-            final boolean hasActionProvider = itemActionProviderClassName != null;
-            if (hasActionProvider && itemActionViewLayout == 0 && itemActionViewClassName == null) {
-                itemActionProvider = newInstance(itemActionProviderClassName,
-                        ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
-                        mActionProviderConstructorArguments);
-            } else {
-                if (hasActionProvider) {
-                    Log.w(LOG_TAG, "Ignoring attribute 'actionProviderClass'."
-                            + " Action view already specified.");
-                }
-                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;
-        }
-
-        private char getShortcut(String shortcutString) {
-            if (shortcutString == null) {
-                return 0;
-            } else {
-                return shortcutString.charAt(0);
-            }
-        }
-
-        private void setItem(MenuItem item) {
-            item.setChecked(itemChecked)
-                    .setVisible(itemVisible)
-                    .setEnabled(itemEnabled)
-                    .setCheckable(itemCheckable >= 1)
-                    .setTitleCondensed(itemTitleCondensed)
-                    .setIcon(itemIconResId);
-
-            if (itemShowAsAction >= 0) {
-                item.setShowAsAction(itemShowAsAction);
-            }
-
-            if (itemListenerMethodName != null) {
-                if (mContext.isRestricted()) {
-                    throw new IllegalStateException("The android:onClick attribute cannot "
-                            + "be used within a restricted context");
-                }
-                item.setOnMenuItemClickListener(
-                        new InflatedOnMenuItemClickListener(getRealOwner(), itemListenerMethodName));
-            }
-
-            final MenuItemImpl impl = item instanceof MenuItemImpl ? (MenuItemImpl) item : null;
-            if (itemCheckable >= 2) {
-                if (item instanceof MenuItemImpl) {
-                    ((MenuItemImpl) item).setExclusiveCheckable(true);
-                } else if (item instanceof MenuItemWrapperICS) {
-                    ((MenuItemWrapperICS) item).setExclusiveCheckable(true);
-                }
-            }
-
-            boolean actionViewSpecified = false;
-            if (itemActionViewClassName != null) {
-                View actionView = (View) newInstance(itemActionViewClassName,
-                        ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
-                item.setActionView(actionView);
-                actionViewSpecified = true;
-            }
-            if (itemActionViewLayout > 0) {
-                if (!actionViewSpecified) {
-                    item.setActionView(itemActionViewLayout);
-                    actionViewSpecified = true;
-                } else {
-                    Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
-                            + " Action view already specified.");
-                }
-            }
-            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() {
-            itemAdded = true;
-            setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
-        }
-
-        public SubMenu addSubMenuItem() {
-            itemAdded = true;
-            SubMenu subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle);
-            setItem(subMenu.getItem());
-            return subMenu;
-        }
-
-        public boolean hasAddedItem() {
-            return itemAdded;
-        }
-
-        @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
-        private <T> T newInstance(String className, Class<?>[] constructorSignature,
-                Object[] arguments) {
-            try {
-                Class<?> clazz = mContext.getClassLoader().loadClass(className);
-                Constructor<?> constructor = clazz.getConstructor(constructorSignature);
-                constructor.setAccessible(true);
-                return (T) constructor.newInstance(arguments);
-            } catch (Exception e) {
-                Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
-            }
-            return null;
-        }
-    }
-}
diff --git a/android/support/v7/view/ViewPropertyAnimatorCompatSet.java b/android/support/v7/view/ViewPropertyAnimatorCompatSet.java
deleted file mode 100644
index 735030e..0000000
--- a/android/support/v7/view/ViewPropertyAnimatorCompatSet.java
+++ /dev/null
@@ -1,151 +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.view;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-
-/**
- * A very naive implementation of a set of
- * {@link android.support.v4.view.ViewPropertyAnimatorCompat}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ViewPropertyAnimatorCompatSet {
-
-    final ArrayList<ViewPropertyAnimatorCompat> mAnimators;
-
-    private long mDuration = -1;
-    private Interpolator mInterpolator;
-    ViewPropertyAnimatorListener mListener;
-
-    private boolean mIsStarted;
-
-    public ViewPropertyAnimatorCompatSet() {
-        mAnimators = new ArrayList<ViewPropertyAnimatorCompat>();
-    }
-
-    public ViewPropertyAnimatorCompatSet play(ViewPropertyAnimatorCompat animator) {
-        if (!mIsStarted) {
-            mAnimators.add(animator);
-        }
-        return this;
-    }
-
-    public ViewPropertyAnimatorCompatSet playSequentially(ViewPropertyAnimatorCompat anim1,
-            ViewPropertyAnimatorCompat anim2) {
-        mAnimators.add(anim1);
-        anim2.setStartDelay(anim1.getDuration());
-        mAnimators.add(anim2);
-        return this;
-    }
-
-    public void start() {
-        if (mIsStarted) return;
-        for (ViewPropertyAnimatorCompat animator : mAnimators) {
-            if (mDuration >= 0) {
-                animator.setDuration(mDuration);
-            }
-            if (mInterpolator != null) {
-                animator.setInterpolator(mInterpolator);
-            }
-            if (mListener != null) {
-                animator.setListener(mProxyListener);
-            }
-            animator.start();
-        }
-
-        mIsStarted = true;
-    }
-
-    void onAnimationsEnded() {
-        mIsStarted = false;
-    }
-
-    public void cancel() {
-        if (!mIsStarted) {
-            return;
-        }
-        for (ViewPropertyAnimatorCompat animator : mAnimators) {
-            animator.cancel();
-        }
-        mIsStarted = false;
-    }
-
-    public ViewPropertyAnimatorCompatSet setDuration(long duration) {
-        if (!mIsStarted) {
-            mDuration = duration;
-        }
-        return this;
-    }
-
-    public ViewPropertyAnimatorCompatSet setInterpolator(Interpolator interpolator) {
-        if (!mIsStarted) {
-            mInterpolator = interpolator;
-        }
-        return this;
-    }
-
-    public ViewPropertyAnimatorCompatSet setListener(ViewPropertyAnimatorListener listener) {
-        if (!mIsStarted) {
-            mListener = listener;
-        }
-        return this;
-    }
-
-    private final ViewPropertyAnimatorListenerAdapter mProxyListener
-            = new ViewPropertyAnimatorListenerAdapter() {
-        private boolean mProxyStarted = false;
-        private int mProxyEndCount = 0;
-
-        @Override
-        public void onAnimationStart(View view) {
-            if (mProxyStarted) {
-                return;
-            }
-            mProxyStarted = true;
-            if (mListener != null) {
-                mListener.onAnimationStart(null);
-            }
-        }
-
-        void onEnd() {
-            mProxyEndCount = 0;
-            mProxyStarted = false;
-            onAnimationsEnded();
-        }
-
-        @Override
-        public void onAnimationEnd(View view) {
-            if (++mProxyEndCount == mAnimators.size()) {
-                if (mListener != null) {
-                    mListener.onAnimationEnd(null);
-                }
-                onEnd();
-            }
-        }
-    };
-}
diff --git a/android/support/v7/view/WindowCallbackWrapper.java b/android/support/v7/view/WindowCallbackWrapper.java
deleted file mode 100644
index e25a663..0000000
--- a/android/support/v7/view/WindowCallbackWrapper.java
+++ /dev/null
@@ -1,185 +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.view;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.view.ActionMode;
-import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.SearchEvent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.util.List;
-
-/**
- * A simple decorator stub for Window.Callback that passes through any calls
- * to the wrapped instance as a base implementation. Call super.foo() to call into
- * the wrapped callback for any subclasses.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class WindowCallbackWrapper implements Window.Callback {
-
-    final Window.Callback mWrapped;
-
-    public WindowCallbackWrapper(Window.Callback wrapped) {
-        if (wrapped == null) {
-            throw new IllegalArgumentException("Window callback may not be null");
-        }
-        mWrapped = wrapped;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return mWrapped.dispatchKeyEvent(event);
-    }
-
-    @Override
-    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
-        return mWrapped.dispatchKeyShortcutEvent(event);
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        return mWrapped.dispatchTouchEvent(event);
-    }
-
-    @Override
-    public boolean dispatchTrackballEvent(MotionEvent event) {
-        return mWrapped.dispatchTrackballEvent(event);
-    }
-
-    @Override
-    public boolean dispatchGenericMotionEvent(MotionEvent event) {
-        return mWrapped.dispatchGenericMotionEvent(event);
-    }
-
-    @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        return mWrapped.dispatchPopulateAccessibilityEvent(event);
-    }
-
-    @Override
-    public View onCreatePanelView(int featureId) {
-        return mWrapped.onCreatePanelView(featureId);
-    }
-
-    @Override
-    public boolean onCreatePanelMenu(int featureId, Menu menu) {
-        return mWrapped.onCreatePanelMenu(featureId, menu);
-    }
-
-    @Override
-    public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        return mWrapped.onPreparePanel(featureId, view, menu);
-    }
-
-    @Override
-    public boolean onMenuOpened(int featureId, Menu menu) {
-        return mWrapped.onMenuOpened(featureId, menu);
-    }
-
-    @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {
-        return mWrapped.onMenuItemSelected(featureId, item);
-    }
-
-    @Override
-    public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {
-        mWrapped.onWindowAttributesChanged(attrs);
-    }
-
-    @Override
-    public void onContentChanged() {
-        mWrapped.onContentChanged();
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        mWrapped.onWindowFocusChanged(hasFocus);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        mWrapped.onAttachedToWindow();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        mWrapped.onDetachedFromWindow();
-    }
-
-    @Override
-    public void onPanelClosed(int featureId, Menu menu) {
-        mWrapped.onPanelClosed(featureId, menu);
-    }
-
-    @RequiresApi(23)
-    @Override
-    public boolean onSearchRequested(SearchEvent searchEvent) {
-        return mWrapped.onSearchRequested(searchEvent);
-    }
-
-    @Override
-    public boolean onSearchRequested() {
-        return mWrapped.onSearchRequested();
-    }
-
-    @Override
-    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
-        return mWrapped.onWindowStartingActionMode(callback);
-    }
-
-    @RequiresApi(23)
-    @Override
-    public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
-        return mWrapped.onWindowStartingActionMode(callback, type);
-    }
-
-    @Override
-    public void onActionModeStarted(ActionMode mode) {
-        mWrapped.onActionModeStarted(mode);
-    }
-
-    @Override
-    public void onActionModeFinished(ActionMode mode) {
-        mWrapped.onActionModeFinished(mode);
-    }
-
-    @RequiresApi(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/android/support/v7/view/menu/ActionMenuItem.java b/android/support/v7/view/menu/ActionMenuItem.java
deleted file mode 100644
index 1074202..0000000
--- a/android/support/v7/view/menu/ActionMenuItem.java
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.view.ContextMenu.ContextMenuInfo;
-import android.view.KeyEvent;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionMenuItem implements SupportMenuItem {
-
-    private final int mId;
-    private final int mGroup;
-    private final int mCategoryOrder;
-    private final int mOrdering;
-
-    private CharSequence mTitle;
-    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;
-
-    private Context mContext;
-
-    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;
-    private static final int CHECKABLE = 0x00000001;
-    private static final int CHECKED = 0x00000002;
-    private static final int EXCLUSIVE = 0x00000004;
-    private static final int HIDDEN = 0x00000008;
-    private static final int ENABLED = 0x00000010;
-
-    public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
-            CharSequence title) {
-        mContext = context;
-        mId = id;
-        mGroup = group;
-        mCategoryOrder = categoryOrder;
-        mOrdering = ordering;
-        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 = 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;
-    }
-
-    public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
-        mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
-        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 = 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;
-    }
-
-    public boolean invoke() {
-        if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
-            return true;
-        }
-
-        if (mIntent != null) {
-            mContext.startActivity(mIntent);
-            return true;
-        }
-
-        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;
-    }
-
-    @Override
-    public MenuItem setActionProvider(android.view.ActionProvider actionProvider) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public android.view.ActionProvider getActionProvider() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public SupportMenuItem setActionView(int resId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public ActionProvider getSupportActionProvider() {
-        return null;
-    }
-
-    @Override
-    public SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public SupportMenuItem setShowAsActionFlags(int actionEnum) {
-        setShowAsAction(actionEnum);
-        return this;
-    }
-
-    @Override
-    public boolean expandActionView() {
-        return false;
-    }
-
-    @Override
-    public boolean collapseActionView() {
-        return false;
-    }
-
-    @Override
-    public boolean isActionViewExpanded() {
-        return false;
-    }
-
-    @Override
-    public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    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/android/support/v7/view/menu/ActionMenuItemView.java b/android/support/v7/view/menu/ActionMenuItemView.java
deleted file mode 100644
index 27cb073..0000000
--- a/android/support/v7/view/menu/ActionMenuItemView.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-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.graphics.drawable.Drawable;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-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.MotionEvent;
-import android.view.View;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionMenuItemView extends AppCompatTextView
-        implements MenuView.ItemView, View.OnClickListener, ActionMenuView.ActionMenuChildView {
-
-    private static final String TAG = "ActionMenuItemView";
-
-    MenuItemImpl mItemData;
-    private CharSequence mTitle;
-    private Drawable mIcon;
-    MenuBuilder.ItemInvoker mItemInvoker;
-    private ForwardingListener mForwardingListener;
-    PopupCallback mPopupCallback;
-
-    private boolean mAllowTextWithIcon;
-    private boolean mExpandedFormat;
-    private int mMinWidth;
-    private int mSavedPaddingLeft;
-
-    private static final int MAX_ICON_SIZE = 32; // dp
-    private int mMaxIconSize;
-
-    public ActionMenuItemView(Context context) {
-        this(context, null);
-    }
-
-    public ActionMenuItemView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        final Resources res = context.getResources();
-        mAllowTextWithIcon = shouldAllowTextWithIcon();
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.ActionMenuItemView, defStyle, 0);
-        mMinWidth = a.getDimensionPixelSize(
-                R.styleable.ActionMenuItemView_android_minWidth, 0);
-        a.recycle();
-
-        final float density = res.getDisplayMetrics().density;
-        mMaxIconSize = (int) (MAX_ICON_SIZE * density + 0.5f);
-
-        setOnClickListener(this);
-
-        mSavedPaddingLeft = -1;
-        setSaveEnabled(false);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        mAllowTextWithIcon = shouldAllowTextWithIcon();
-        updateTextButtonVisibility();
-    }
-
-    /**
-     * Whether action menu items should obey the "withText" showAsAction flag. This may be set to
-     * false for situations where space is extremely limited. -->
-     */
-    private boolean shouldAllowTextWithIcon() {
-        final Configuration config = getContext().getResources().getConfiguration();
-        final int widthDp = config.screenWidthDp;
-        final int heightDp = config.screenHeightDp;
-
-        return widthDp >= 480 || (widthDp >= 640 && heightDp >= 480)
-                || config.orientation == Configuration.ORIENTATION_LANDSCAPE;
-    }
-
-    @Override
-    public void setPadding(int l, int t, int r, int b) {
-        mSavedPaddingLeft = l;
-        super.setPadding(l, t, r, b);
-    }
-
-    @Override
-    public MenuItemImpl getItemData() {
-        return mItemData;
-    }
-
-    @Override
-    public void initialize(MenuItemImpl itemData, int menuType) {
-        mItemData = itemData;
-
-        setIcon(itemData.getIcon());
-        setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
-        setId(itemData.getItemId());
-
-        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
-        setEnabled(itemData.isEnabled());
-        if (itemData.hasSubMenu()) {
-            if (mForwardingListener == null) {
-                mForwardingListener = new ActionMenuItemForwardingListener();
-            }
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent e) {
-        if (mItemData.hasSubMenu() && mForwardingListener != null
-                && mForwardingListener.onTouch(this, e)) {
-            return true;
-        }
-        return super.onTouchEvent(e);
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (mItemInvoker != null) {
-            mItemInvoker.invokeItem(mItemData);
-        }
-    }
-
-    public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
-        mItemInvoker = invoker;
-    }
-
-    public void setPopupCallback(PopupCallback popupCallback) {
-        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
-    }
-
-    public void setExpandedFormat(boolean expandedFormat) {
-        if (mExpandedFormat != expandedFormat) {
-            mExpandedFormat = expandedFormat;
-            if (mItemData != null) {
-                mItemData.actionFormatChanged();
-            }
-        }
-    }
-
-    private void updateTextButtonVisibility() {
-        boolean visible = !TextUtils.isEmpty(mTitle);
-        visible &= mIcon == null ||
-                (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) {
-            int width = icon.getIntrinsicWidth();
-            int height = icon.getIntrinsicHeight();
-            if (width > mMaxIconSize) {
-                final float scale = (float) mMaxIconSize / width;
-                width = mMaxIconSize;
-                height = (int) (height * scale);
-            }
-            if (height > mMaxIconSize) {
-                final float scale = (float) mMaxIconSize / height;
-                height = mMaxIconSize;
-                width = (int) (width * scale);
-            }
-            icon.setBounds(0, 0, width, height);
-        }
-        setCompoundDrawables(icon, null, null, null);
-
-        updateTextButtonVisibility();
-    }
-
-    public boolean hasText() {
-        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;
-
-        updateTextButtonVisibility();
-    }
-
-    @Override
-    public boolean showsIcon() {
-        return true;
-    }
-
-    @Override
-    public boolean needsDividerBefore() {
-        return hasText() && mItemData.getIcon() == null;
-    }
-
-    @Override
-    public boolean needsDividerAfter() {
-        return hasText();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final boolean textVisible = hasText();
-        if (textVisible && mSavedPaddingLeft >= 0) {
-            super.setPadding(mSavedPaddingLeft, getPaddingTop(),
-                    getPaddingRight(), getPaddingBottom());
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        final int oldMeasuredWidth = getMeasuredWidth();
-        final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
-                : mMinWidth;
-
-        if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
-            // Remeasure at exactly the minimum width.
-            super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
-                    heightMeasureSpec);
-        }
-
-        if (!textVisible && mIcon != null) {
-            // TextView won't center compound drawables in both dimensions without
-            // a little coercion. Pad in to center the icon after we've measured.
-            final int w = getMeasuredWidth();
-            final int dw = mIcon.getBounds().width();
-            super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
-        }
-    }
-
-    private class ActionMenuItemForwardingListener extends ForwardingListener {
-        public ActionMenuItemForwardingListener() {
-            super(ActionMenuItemView.this);
-        }
-
-        @Override
-        public ShowableListMenu getPopup() {
-            if (mPopupCallback != null) {
-                return mPopupCallback.getPopup();
-            }
-            return null;
-        }
-
-        @Override
-        protected boolean onForwardingStarted() {
-            // Call the invoker, then check if the expected popup is showing.
-            if (mItemInvoker != null && mItemInvoker.invokeItem(mItemData)) {
-                final ShowableListMenu popup = getPopup();
-                return popup != null && popup.isShowing();
-            }
-            return false;
-        }
-
-        // Do not backport the framework impl here.
-        // The framework's ListPopupWindow uses an animation before performing the item click
-        // after selecting an item. As AppCompat doesn't use an animation, the popup is
-        // dismissed and thus null'ed out before onForwardingStopped() has been called.
-        // This messes up ActionMenuItemView's onForwardingStopped() impl since it will now
-        // return false and make ListPopupWindow think it's still forwarding.
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        // This might get called with the state of ActionView since it shares the same ID with
-        // ActionMenuItemView. Do not restore this state as ActionMenuItemView never saved it.
-        super.onRestoreInstanceState(null);
-    }
-
-    public static abstract class PopupCallback {
-        public abstract ShowableListMenu getPopup();
-    }
-}
diff --git a/android/support/v7/view/menu/BaseMenuPresenter.java b/android/support/v7/view/menu/BaseMenuPresenter.java
deleted file mode 100644
index 1e89ad2..0000000
--- a/android/support/v7/view/menu/BaseMenuPresenter.java
+++ /dev/null
@@ -1,248 +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.v7.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * Base class for MenuPresenters that have a consistent container view and item views. Behaves
- * similarly to an AdapterView in that existing item views will be reused if possible when items
- * change.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public abstract class BaseMenuPresenter implements MenuPresenter {
-
-    protected Context mSystemContext;
-    protected Context mContext;
-    protected MenuBuilder mMenu;
-    protected LayoutInflater mSystemInflater;
-    protected LayoutInflater mInflater;
-    private Callback mCallback;
-
-    private int mMenuLayoutRes;
-    private int mItemLayoutRes;
-
-    protected MenuView mMenuView;
-
-    private int mId;
-
-    /**
-     * Construct a new BaseMenuPresenter.
-     *
-     * @param context Context for generating system-supplied views
-     * @param menuLayoutRes Layout resource ID for the menu container view
-     * @param itemLayoutRes Layout resource ID for a single item view
-     */
-    public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
-        mSystemContext = context;
-        mSystemInflater = LayoutInflater.from(context);
-        mMenuLayoutRes = menuLayoutRes;
-        mItemLayoutRes = itemLayoutRes;
-    }
-
-    @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
-        mContext = context;
-        mInflater = LayoutInflater.from(mContext);
-        mMenu = menu;
-    }
-
-    @Override
-    public MenuView getMenuView(ViewGroup root) {
-        if (mMenuView == null) {
-            mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
-            mMenuView.initialize(mMenu);
-            updateMenuView(true);
-        }
-
-        return mMenuView;
-    }
-
-    /**
-     * Reuses item views when it can
-     */
-    @Override
-    public void updateMenuView(boolean cleared) {
-        final ViewGroup parent = (ViewGroup) mMenuView;
-        if (parent == null) return;
-
-        int childIndex = 0;
-        if (mMenu != null) {
-            mMenu.flagActionItems();
-            ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
-            final int itemCount = visibleItems.size();
-            for (int i = 0; i < itemCount; i++) {
-                MenuItemImpl item = visibleItems.get(i);
-                if (shouldIncludeItem(childIndex, item)) {
-                    final View convertView = parent.getChildAt(childIndex);
-                    final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
-                            ((MenuView.ItemView) convertView).getItemData() : null;
-                    final View itemView = getItemView(item, convertView, parent);
-                    if (item != oldItem) {
-                        // Don't let old states linger with new data.
-                        itemView.setPressed(false);
-                        itemView.jumpDrawablesToCurrentState();
-                    }
-                    if (itemView != convertView) {
-                        addItemView(itemView, childIndex);
-                    }
-                    childIndex++;
-                }
-            }
-        }
-
-        // Remove leftover views.
-        while (childIndex < parent.getChildCount()) {
-            if (!filterLeftoverView(parent, childIndex)) {
-                childIndex++;
-            }
-        }
-    }
-
-    /**
-     * Add an item view at the given index.
-     *
-     * @param itemView View to add
-     * @param childIndex Index within the parent to insert at
-     */
-    protected void addItemView(View itemView, int childIndex) {
-        final ViewGroup currentParent = (ViewGroup) itemView.getParent();
-        if (currentParent != null) {
-            currentParent.removeView(itemView);
-        }
-        ((ViewGroup) mMenuView).addView(itemView, childIndex);
-    }
-
-    /**
-     * Filter the child view at index and remove it if appropriate.
-     * @param parent Parent to filter from
-     * @param childIndex Index to filter
-     * @return true if the child view at index was removed
-     */
-    protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
-        parent.removeViewAt(childIndex);
-        return true;
-    }
-
-    @Override
-    public void setCallback(Callback cb) {
-        mCallback = cb;
-    }
-
-    public Callback getCallback() {
-        return mCallback;
-    }
-
-    /**
-     * Create a new item view that can be re-bound to other item data later.
-     *
-     * @return The new item view
-     */
-    public MenuView.ItemView createItemView(ViewGroup parent) {
-        return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
-    }
-
-    /**
-     * Prepare an item view for use. See AdapterView for the basic idea at work here.
-     * This may require creating a new item view, but well-behaved implementations will
-     * re-use the view passed as convertView if present. The returned view will be populated
-     * with data from the item parameter.
-     *
-     * @param item Item to present
-     * @param convertView Existing view to reuse
-     * @param parent Intended parent view - use for inflation.
-     * @return View that presents the requested menu item
-     */
-    public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
-        MenuView.ItemView itemView;
-        if (convertView instanceof MenuView.ItemView) {
-            itemView = (MenuView.ItemView) convertView;
-        } else {
-            itemView = createItemView(parent);
-        }
-        bindItemView(item, itemView);
-        return (View) itemView;
-    }
-
-    /**
-     * Bind item data to an existing item view.
-     *
-     * @param item Item to bind
-     * @param itemView View to populate with item data
-     */
-    public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
-
-    /**
-     * Filter item by child index and item data.
-     *
-     * @param childIndex Indended presentation index of this item
-     * @param item Item to present
-     * @return true if this item should be included in this menu presentation; false otherwise
-     */
-    public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
-        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);
-        }
-        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;
-    }
-
-    public void setId(int id) {
-        mId = id;
-    }
-}
diff --git a/android/support/v7/view/menu/BaseMenuWrapper.java b/android/support/v7/view/menu/BaseMenuWrapper.java
deleted file mode 100644
index 1a31ec6..0000000
--- a/android/support/v7/view/menu/BaseMenuWrapper.java
+++ /dev/null
@@ -1,126 +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.view.menu;
-
-import android.content.Context;
-import android.support.v4.internal.view.SupportMenuItem;
-import android.support.v4.internal.view.SupportSubMenu;
-import android.support.v4.util.ArrayMap;
-import android.view.MenuItem;
-import android.view.SubMenu;
-
-import java.util.Iterator;
-import java.util.Map;
-
-abstract class BaseMenuWrapper<T> extends BaseWrapper<T> {
-
-    final Context mContext;
-
-    private Map<SupportMenuItem, MenuItem> mMenuItems;
-    private Map<SupportSubMenu, SubMenu> mSubMenus;
-
-    BaseMenuWrapper(Context context, T object) {
-        super(object);
-        mContext = context;
-    }
-
-    final MenuItem getMenuItemWrapper(final MenuItem menuItem) {
-        if (menuItem instanceof SupportMenuItem) {
-            final SupportMenuItem supportMenuItem = (SupportMenuItem) menuItem;
-
-            // Instantiate Map if null
-            if (mMenuItems == null) {
-                mMenuItems = new ArrayMap<>();
-            }
-
-            // First check if we already have a wrapper for this item
-            MenuItem wrappedItem = mMenuItems.get(menuItem);
-
-            if (null == wrappedItem) {
-                // ... if not, create one and add it to our map
-                wrappedItem = MenuWrapperFactory.wrapSupportMenuItem(mContext, supportMenuItem);
-                mMenuItems.put(supportMenuItem, wrappedItem);
-            }
-
-            return wrappedItem;
-        }
-        return menuItem;
-    }
-
-    final SubMenu getSubMenuWrapper(final SubMenu subMenu) {
-        if (subMenu instanceof SupportSubMenu) {
-            final SupportSubMenu supportSubMenu = (SupportSubMenu) subMenu;
-
-            // Instantiate Map if null
-            if (mSubMenus == null) {
-                mSubMenus = new ArrayMap<>();
-            }
-
-            SubMenu wrappedMenu = mSubMenus.get(supportSubMenu);
-
-            if (null == wrappedMenu) {
-                wrappedMenu = MenuWrapperFactory.wrapSupportSubMenu(mContext, supportSubMenu);
-                mSubMenus.put(supportSubMenu, wrappedMenu);
-            }
-            return wrappedMenu;
-        }
-        return subMenu;
-    }
-
-
-    final void internalClear() {
-        if (mMenuItems != null) {
-            mMenuItems.clear();
-        }
-        if (mSubMenus != null) {
-            mSubMenus.clear();
-        }
-    }
-
-    final void internalRemoveGroup(final int groupId) {
-        if (mMenuItems == null) {
-            return;
-        }
-
-        Iterator<SupportMenuItem> iterator = mMenuItems.keySet().iterator();
-        android.view.MenuItem menuItem;
-
-        while (iterator.hasNext()) {
-            menuItem = iterator.next();
-            if (groupId == menuItem.getGroupId()) {
-                iterator.remove();
-            }
-        }
-    }
-
-    final void internalRemoveItem(final int id) {
-        if (mMenuItems == null) {
-            return;
-        }
-
-        Iterator<SupportMenuItem> iterator = mMenuItems.keySet().iterator();
-        android.view.MenuItem menuItem;
-
-        while (iterator.hasNext()) {
-            menuItem = iterator.next();
-            if (id == menuItem.getItemId()) {
-                iterator.remove();
-                break;
-            }
-        }
-    }
-}
diff --git a/android/support/v7/view/menu/BaseWrapper.java b/android/support/v7/view/menu/BaseWrapper.java
deleted file mode 100644
index 35422ca..0000000
--- a/android/support/v7/view/menu/BaseWrapper.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.v7.view.menu;
-
-class BaseWrapper<T> {
-
-    final T mWrappedObject;
-
-    BaseWrapper(T object) {
-        if (null == object) {
-            throw new IllegalArgumentException("Wrapped Object can not be null.");
-        }
-        mWrappedObject = object;
-    }
-
-    public T getWrappedObject() {
-        return mWrappedObject;
-    }
-
-}
diff --git a/android/support/v7/view/menu/CascadingMenuPopup.java b/android/support/v7/view/menu/CascadingMenuPopup.java
deleted file mode 100644
index 834f854..0000000
--- a/android/support/v7/view/menu/CascadingMenuPopup.java
+++ /dev/null
@@ -1,806 +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.view.menu;
-
-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;
-import android.support.annotation.AttrRes;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StyleRes;
-import android.support.v4.internal.view.SupportMenu;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.widget.MenuItemHoverListener;
-import android.support.v7.widget.MenuPopupWindow;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnKeyListener;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.widget.AbsListView;
-import android.widget.FrameLayout;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.PopupWindow;
-import android.widget.PopupWindow.OnDismissListener;
-import android.widget.TextView;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A popup for a menu which will allow multiple submenus to appear in a cascading fashion, side by
- * side.
- */
-final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener,
-        PopupWindow.OnDismissListener {
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
-    public @interface HorizPosition {}
-
-    static final int HORIZ_POSITION_LEFT = 0;
-    static final int HORIZ_POSITION_RIGHT = 1;
-
-    /**
-     * Delay between hovering over a menu item with a mouse and receiving
-     * side-effects (ex. opening a sub-menu or closing unrelated menus).
-     */
-    static final int SUBMENU_TIMEOUT_MS = 200;
-
-    private final Context mContext;
-    private final int mMenuMaxWidth;
-    private final int mPopupStyleAttr;
-    private final int mPopupStyleRes;
-    private final boolean mOverflowOnly;
-    final Handler mSubMenuHoverHandler;
-
-    /** List of menus that were added before this popup was shown. */
-    private final List<MenuBuilder> mPendingMenus = new ArrayList<>();
-
-    /**
-     * List of open menus. The first item is the root menu and each
-     * subsequent item is a direct submenu of the previous item.
-     */
-    final List<CascadingMenuInfo> mShowingMenus = new ArrayList<>();
-
-    private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
-        @Override
-        public void onGlobalLayout() {
-            // Only move the popup if it's showing and non-modal. We don't want
-            // to be moving around the only interactive window, since there's a
-            // good chance the user is interacting with it.
-            if (isShowing() && mShowingMenus.size() > 0
-                    && !mShowingMenus.get(0).window.isModal()) {
-                final View anchor = mShownAnchorView;
-                if (anchor == null || !anchor.isShown()) {
-                    dismiss();
-                } else {
-                    // Recompute window sizes and positions.
-                    for (CascadingMenuInfo info : mShowingMenus) {
-                        info.window.show();
-                    }
-                }
-            }
-        }
-    };
-
-    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) {
-            // If the mouse moves between two windows, hover enter/exit pairs
-            // may be received out of order. So, instead of canceling all
-            // pending runnables, only cancel runnables for the host menu.
-            mSubMenuHoverHandler.removeCallbacksAndMessages(menu);
-        }
-
-        @Override
-        public void onItemHoverEnter(
-                @NonNull final MenuBuilder menu, @NonNull final MenuItem item) {
-            // Something new was hovered, cancel all scheduled runnables.
-            mSubMenuHoverHandler.removeCallbacksAndMessages(null);
-
-            // Find the position of the hovered menu within the added menus.
-            int menuIndex = -1;
-            for (int i = 0, count = mShowingMenus.size(); i < count; i++) {
-                if (menu == mShowingMenus.get(i).menu) {
-                    menuIndex = i;
-                    break;
-                }
-            }
-
-            if (menuIndex == -1) {
-                return;
-            }
-
-            final CascadingMenuInfo nextInfo;
-            final int nextIndex = menuIndex + 1;
-            if (nextIndex < mShowingMenus.size()) {
-                nextInfo = mShowingMenus.get(nextIndex);
-            } else {
-                nextInfo = null;
-            }
-
-            final Runnable runnable = new Runnable() {
-                @Override
-                public void run() {
-                    // Close any other submenus that might be open at the
-                    // current or a deeper level.
-                    if (nextInfo != null) {
-                        // Disable exit animations to prevent overlapping
-                        // fading out submenus.
-                        mShouldCloseImmediately = true;
-                        nextInfo.menu.close(false /* closeAllMenus */);
-                        mShouldCloseImmediately = false;
-                    }
-
-                    // Then open the selected submenu, if there is one.
-                    if (item.isEnabled() && item.hasSubMenu()) {
-                        menu.performItemAction(item, SupportMenu.FLAG_KEEP_OPEN_ON_SUBMENU_OPENED);
-                    }
-                }
-            };
-            final long uptimeMillis = SystemClock.uptimeMillis() + SUBMENU_TIMEOUT_MS;
-            mSubMenuHoverHandler.postAtTime(runnable, menu, uptimeMillis);
-        }
-    };
-
-    private int mRawDropDownGravity = Gravity.NO_GRAVITY;
-    private int mDropDownGravity = Gravity.NO_GRAVITY;
-    private View mAnchorView;
-    View mShownAnchorView;
-    private int mLastPosition;
-    private boolean mHasXOffset;
-    private boolean mHasYOffset;
-    private int mXOffset;
-    private int mYOffset;
-    private boolean mForceShowIcon;
-    private boolean mShowTitle;
-    private Callback mPresenterCallback;
-    private ViewTreeObserver mTreeObserver;
-    private PopupWindow.OnDismissListener mOnDismissListener;
-
-    /** Whether popup menus should disable exit animations when closing. */
-    boolean mShouldCloseImmediately;
-
-    /**
-     * Initializes a new cascading-capable menu popup.
-     *
-     * @param anchor A parent view to get the {@link android.view.View#getWindowToken()} token from.
-     */
-    public CascadingMenuPopup(@NonNull Context context, @NonNull View anchor,
-            @AttrRes int popupStyleAttr, @StyleRes int popupStyleRes, boolean overflowOnly) {
-        mContext = context;
-        mAnchorView = anchor;
-        mPopupStyleAttr = popupStyleAttr;
-        mPopupStyleRes = popupStyleRes;
-        mOverflowOnly = overflowOnly;
-
-        mForceShowIcon = false;
-        mLastPosition = getInitialMenuPosition();
-
-        final Resources res = context.getResources();
-        mMenuMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
-                res.getDimensionPixelSize(R.dimen.abc_config_prefDialogWidth));
-
-        mSubMenuHoverHandler = new Handler();
-    }
-
-    @Override
-    public void setForceShowIcon(boolean forceShow) {
-        mForceShowIcon = forceShow;
-    }
-
-    private MenuPopupWindow createPopupWindow() {
-        MenuPopupWindow popupWindow = new MenuPopupWindow(
-                mContext, null, mPopupStyleAttr, mPopupStyleRes);
-        popupWindow.setHoverListener(mMenuItemHoverListener);
-        popupWindow.setOnItemClickListener(this);
-        popupWindow.setOnDismissListener(this);
-        popupWindow.setAnchorView(mAnchorView);
-        popupWindow.setDropDownGravity(mDropDownGravity);
-        popupWindow.setModal(true);
-        popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-        return popupWindow;
-    }
-
-    @Override
-    public void show() {
-        if (isShowing()) {
-            return;
-        }
-
-        // Display all pending menus.
-        for (MenuBuilder menu : mPendingMenus) {
-            showMenu(menu);
-        }
-        mPendingMenus.clear();
-
-        mShownAnchorView = mAnchorView;
-
-        if (mShownAnchorView != null) {
-            final boolean addGlobalListener = mTreeObserver == null;
-            mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest
-            if (addGlobalListener) {
-                mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
-            }
-            mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener);
-        }
-    }
-
-    @Override
-    public void dismiss() {
-        // Need to make another list to avoid a concurrent modification
-        // exception, as #onDismiss may clear mPopupWindows while we are
-        // iterating. Remove from the last added menu so that the callbacks
-        // are received in order from foreground to background.
-        final int length = mShowingMenus.size();
-        if (length > 0) {
-            final CascadingMenuInfo[] addedMenus =
-                    mShowingMenus.toArray(new CascadingMenuInfo[length]);
-            for (int i = length - 1; i >= 0; i--) {
-                final CascadingMenuInfo info = addedMenus[i];
-                if (info.window.isShowing()) {
-                    info.window.dismiss();
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
-            dismiss();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Determines the proper initial menu position for the current LTR/RTL configuration.
-     * @return The initial position.
-     */
-    @HorizPosition
-    private int getInitialMenuPosition() {
-        final int layoutDirection = ViewCompat.getLayoutDirection(mAnchorView);
-        return layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL ? HORIZ_POSITION_LEFT :
-                HORIZ_POSITION_RIGHT;
-    }
-
-    /**
-     * Determines whether the next submenu (of the given width) should display on the right or on
-     * the left of the most recent menu.
-     *
-     * @param nextMenuWidth Width of the next submenu to display.
-     * @return The position to display it.
-     */
-    @HorizPosition
-    private int getNextMenuPosition(int nextMenuWidth) {
-        ListView lastListView = mShowingMenus.get(mShowingMenus.size() - 1).getListView();
-
-        final int[] screenLocation = new int[2];
-        lastListView.getLocationOnScreen(screenLocation);
-
-        final Rect displayFrame = new Rect();
-        mShownAnchorView.getWindowVisibleDisplayFrame(displayFrame);
-
-        if (mLastPosition == HORIZ_POSITION_RIGHT) {
-            final int right = screenLocation[0] + lastListView.getWidth() + nextMenuWidth;
-            if (right > displayFrame.right) {
-                return HORIZ_POSITION_LEFT;
-            }
-            return HORIZ_POSITION_RIGHT;
-        } else { // LEFT
-            final int left = screenLocation[0] - nextMenuWidth;
-            if (left < 0) {
-                return HORIZ_POSITION_RIGHT;
-            }
-            return HORIZ_POSITION_LEFT;
-        }
-    }
-
-    @Override
-    public void addMenu(MenuBuilder menu) {
-        menu.addMenuPresenter(this, mContext);
-
-        if (isShowing()) {
-            showMenu(menu);
-        } else {
-            mPendingMenus.add(menu);
-        }
-    }
-
-    /**
-     * Prepares and shows the specified menu immediately.
-     *
-     * @param menu the menu to show
-     */
-    private void showMenu(@NonNull MenuBuilder menu) {
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-        final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
-
-        // Apply "force show icon" setting. There are 3 cases:
-        // (1) This is the top level menu and icon spacing is forced. Add spacing.
-        // (2) This is a submenu. Add spacing if any of the visible menu items has an icon.
-        // (3) This is the top level menu and icon spacing isn't forced. Do not add spacing.
-        if (!isShowing() && mForceShowIcon) {
-          // Case 1
-          adapter.setForceShowIcon(true);
-        } else if (isShowing()) {
-          // Case 2
-          adapter.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(menu));
-        }
-        // Case 3: Else, don't allow spacing for icons (default behavior; do nothing).
-
-        final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
-        final MenuPopupWindow popupWindow = createPopupWindow();
-        popupWindow.setAdapter(adapter);
-        popupWindow.setContentWidth(menuWidth);
-        popupWindow.setDropDownGravity(mDropDownGravity);
-
-        final CascadingMenuInfo parentInfo;
-        final View parentView;
-        if (mShowingMenus.size() > 0) {
-            parentInfo = mShowingMenus.get(mShowingMenus.size() - 1);
-            parentView = findParentViewForSubmenu(parentInfo, menu);
-        } else {
-            parentInfo = null;
-            parentView = null;
-        }
-
-        if (parentView != null) {
-            // This menu is a cascading submenu anchored to a parent view.
-            popupWindow.setTouchModal(false);
-            popupWindow.setEnterTransition(null);
-
-            final @HorizPosition int nextMenuPosition = getNextMenuPosition(menuWidth);
-            final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
-            mLastPosition = nextMenuPosition;
-
-            final int parentOffsetX;
-            final int parentOffsetY;
-            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);
-                parentOffsetX = 0;
-                parentOffsetY = 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.
-
-                // 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);
-
-                // For Gravity.LEFT case, the baseline is just the left border of the view. So we
-                // can use the X of the location directly. But for Gravity.RIGHT case, the baseline
-                // is the right border. So we need add view's width with the location to make the
-                // baseline as the right border correctly.
-                if ((mDropDownGravity & (Gravity.RIGHT | Gravity.LEFT)) == Gravity.RIGHT) {
-                    anchorScreenLocation[0] += mAnchorView.getWidth();
-                    parentViewScreenLocation[0] += parentView.getWidth();
-                }
-
-                // If used as horizontal/vertical offsets, these values would position the submenu
-                // at the exact same position as the parent item.
-                parentOffsetX = parentViewScreenLocation[0] - anchorScreenLocation[0];
-                parentOffsetY = 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;
-            if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) {
-                if (showOnRight) {
-                    x = parentOffsetX + menuWidth;
-                } else {
-                    x = parentOffsetX - parentView.getWidth();
-                }
-            } else {
-                if (showOnRight) {
-                    x = parentOffsetX + parentView.getWidth();
-                } else {
-                    x = parentOffsetX - menuWidth;
-                }
-            }
-            popupWindow.setHorizontalOffset(x);
-
-            // Vertically align with the parent item.
-            popupWindow.setOverlapAnchor(true);
-            popupWindow.setVerticalOffset(parentOffsetY);
-        } else {
-            if (mHasXOffset) {
-                popupWindow.setHorizontalOffset(mXOffset);
-            }
-            if (mHasYOffset) {
-                popupWindow.setVerticalOffset(mYOffset);
-            }
-            final Rect epicenterBounds = getEpicenterBounds();
-            popupWindow.setEpicenterBounds(epicenterBounds);
-        }
-
-        final CascadingMenuInfo menuInfo = new CascadingMenuInfo(popupWindow, menu, mLastPosition);
-        mShowingMenus.add(menuInfo);
-
-        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 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);
-            titleItemView.setEnabled(false);
-            titleView.setText(menu.getHeaderTitle());
-            listView.addHeaderView(titleItemView, null, false);
-
-            // Show again to update the title.
-            popupWindow.show();
-        }
-    }
-
-    /**
-     * Returns the menu item within the specified parent menu that owns
-     * specified submenu.
-     *
-     * @param parent the parent menu
-     * @param submenu the submenu for which the index should be returned
-     * @return the menu item that owns the submenu, or {@code null} if not
-     *         present
-     */
-    private MenuItem findMenuItemForSubmenu(
-            @NonNull MenuBuilder parent, @NonNull MenuBuilder submenu) {
-        for (int i = 0, count = parent.size(); i < count; i++) {
-            final MenuItem item = parent.getItem(i);
-            if (item.hasSubMenu() && submenu == item.getSubMenu()) {
-                return item;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Attempts to find the view for the menu item that owns the specified
-     * submenu.
-     *
-     * @param parentInfo info for the parent menu
-     * @param submenu the submenu whose parent view should be obtained
-     * @return the parent view, or {@code null} if one could not be found
-     */
-    @Nullable
-    private View findParentViewForSubmenu(
-            @NonNull CascadingMenuInfo parentInfo, @NonNull MenuBuilder submenu) {
-        final MenuItem owner = findMenuItemForSubmenu(parentInfo.menu, submenu);
-        if (owner == null) {
-            // Couldn't find the submenu owner.
-            return null;
-        }
-
-        // The adapter may be wrapped. Adjust the index if necessary.
-        final int headersCount;
-        final MenuAdapter menuAdapter;
-        final ListView listView = parentInfo.getListView();
-        final ListAdapter listAdapter = listView.getAdapter();
-        if (listAdapter instanceof HeaderViewListAdapter) {
-            final HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) listAdapter;
-            headersCount = headerAdapter.getHeadersCount();
-            menuAdapter = (MenuAdapter) headerAdapter.getWrappedAdapter();
-        } else {
-            headersCount = 0;
-            menuAdapter = (MenuAdapter) listAdapter;
-        }
-
-        // Find the index within the menu adapter's data set of the menu item.
-        int ownerPosition = AbsListView.INVALID_POSITION;
-        for (int i = 0, count = menuAdapter.getCount(); i < count; i++) {
-            if (owner == menuAdapter.getItem(i)) {
-                ownerPosition = i;
-                break;
-            }
-        }
-        if (ownerPosition == AbsListView.INVALID_POSITION) {
-            // Couldn't find the owner within the menu adapter.
-            return null;
-        }
-
-        // Adjust the index for the adapter used to display views.
-        ownerPosition += headersCount;
-
-        // Adjust the index for the visible views.
-        final int ownerViewPosition = ownerPosition - listView.getFirstVisiblePosition();
-        if (ownerViewPosition < 0 || ownerViewPosition >= listView.getChildCount()) {
-            // Not visible on screen.
-            return null;
-        }
-
-        return listView.getChildAt(ownerViewPosition);
-    }
-
-    /**
-     * @return {@code true} if the popup is currently showing, {@code false} otherwise.
-     */
-    @Override
-    public boolean isShowing() {
-        return mShowingMenus.size() > 0 && mShowingMenus.get(0).window.isShowing();
-    }
-
-    /**
-     * Called when one or more of the popup windows was dismissed.
-     */
-    @Override
-    public void onDismiss() {
-        // The dismiss listener doesn't pass the calling window, so walk
-        // through the stack to figure out which one was just dismissed.
-        CascadingMenuInfo dismissedInfo = null;
-        for (int i = 0, count = mShowingMenus.size(); i < count; i++) {
-            final CascadingMenuInfo info = mShowingMenus.get(i);
-            if (!info.window.isShowing()) {
-                dismissedInfo = info;
-                break;
-            }
-        }
-
-        // Close all menus starting from the dismissed menu, passing false
-        // since we are manually closing only a subset of windows.
-        if (dismissedInfo != null) {
-            dismissedInfo.menu.close(false);
-        }
-    }
-
-    @Override
-    public void updateMenuView(boolean cleared) {
-        for (CascadingMenuInfo info : mShowingMenus) {
-            toMenuAdapter(info.getListView().getAdapter()).notifyDataSetChanged();
-        }
-    }
-
-    @Override
-    public void setCallback(Callback cb) {
-        mPresenterCallback = cb;
-    }
-
-    @Override
-    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-        // Don't allow double-opening of the same submenu.
-        for (CascadingMenuInfo info : mShowingMenus) {
-            if (subMenu == info.menu) {
-                // Just re-focus that one.
-                info.getListView().requestFocus();
-                return true;
-            }
-        }
-
-        if (subMenu.hasVisibleItems()) {
-            addMenu(subMenu);
-
-            if (mPresenterCallback != null) {
-                mPresenterCallback.onOpenSubMenu(subMenu);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Finds the index of the specified menu within the list of added menus.
-     *
-     * @param menu the menu to find
-     * @return the index of the menu, or {@code -1} if not present
-     */
-    private int findIndexOfAddedMenu(@NonNull MenuBuilder menu) {
-        for (int i = 0, count = mShowingMenus.size(); i < count; i++) {
-            final CascadingMenuInfo info  = mShowingMenus.get(i);
-            if (menu == info.menu) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
-    @Override
-    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        final int menuIndex = findIndexOfAddedMenu(menu);
-        if (menuIndex < 0) {
-            return;
-        }
-
-        // Recursively close descendant menus.
-        final int nextMenuIndex = menuIndex + 1;
-        if (nextMenuIndex < mShowingMenus.size()) {
-            final CascadingMenuInfo childInfo = mShowingMenus.get(nextMenuIndex);
-            childInfo.menu.close(false /* closeAllMenus */);
-        }
-
-        // Close the target menu.
-        final CascadingMenuInfo info = mShowingMenus.remove(menuIndex);
-        info.menu.removeMenuPresenter(this);
-        if (mShouldCloseImmediately) {
-            // Disable all exit animations.
-            info.window.setExitTransition(null);
-            info.window.setAnimationStyle(0);
-        }
-        info.window.dismiss();
-
-        final int count = mShowingMenus.size();
-        if (count > 0) {
-            mLastPosition = mShowingMenus.get(count - 1).position;
-        } else {
-            mLastPosition = getInitialMenuPosition();
-        }
-
-        if (count == 0) {
-            // This was the last window. Clean up.
-            dismiss();
-
-            if (mPresenterCallback != null) {
-                mPresenterCallback.onCloseMenu(menu, true);
-            }
-
-            if (mTreeObserver != null) {
-                if (mTreeObserver.isAlive()) {
-                    mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
-                }
-                mTreeObserver = null;
-            }
-            mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
-
-            // If every [sub]menu was dismissed, that means the whole thing was
-            // dismissed, so notify the owner.
-            mOnDismissListener.onDismiss();
-        } else if (allMenusAreClosing) {
-            // Close all menus starting from the root. This will recursively
-            // close any remaining menus, so we don't need to propagate the
-            // "closeAllMenus" flag. The last window will clean up.
-            final CascadingMenuInfo rootInfo = mShowingMenus.get(0);
-            rootInfo.menu.close(false /* closeAllMenus */);
-        }
-    }
-
-    @Override
-    public boolean flagActionItems() {
-        return false;
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        return null;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-    }
-
-    @Override
-    public void setGravity(int dropDownGravity) {
-        if (mRawDropDownGravity != dropDownGravity) {
-            mRawDropDownGravity = dropDownGravity;
-            mDropDownGravity = GravityCompat.getAbsoluteGravity(
-                    dropDownGravity, ViewCompat.getLayoutDirection(mAnchorView));
-        }
-    }
-
-    @Override
-    public void setAnchorView(@NonNull View anchor) {
-        if (mAnchorView != anchor) {
-            mAnchorView = anchor;
-
-            // Gravity resolution may have changed, update from raw gravity.
-            mDropDownGravity = GravityCompat.getAbsoluteGravity(
-                    mRawDropDownGravity, ViewCompat.getLayoutDirection(mAnchorView));
-        }
-    }
-
-    @Override
-    public void setOnDismissListener(OnDismissListener listener) {
-        mOnDismissListener = listener;
-    }
-
-    @Override
-    public ListView getListView() {
-        return mShowingMenus.isEmpty()
-                ? null
-                : mShowingMenus.get(mShowingMenus.size() - 1).getListView();
-    }
-
-    @Override
-    public void setHorizontalOffset(int x) {
-        mHasXOffset = true;
-        mXOffset = x;
-    }
-
-    @Override
-    public void setVerticalOffset(int y) {
-        mHasYOffset = true;
-        mYOffset = y;
-    }
-
-    @Override
-    public void setShowTitle(boolean showTitle) {
-        mShowTitle = showTitle;
-    }
-
-    @Override
-    protected boolean closeMenuOnSubMenuOpened() {
-        // Since we're cascading, we don't want the parent menu to be closed when a submenu
-        // is opened
-        return false;
-    }
-
-    private static class CascadingMenuInfo {
-        public final MenuPopupWindow window;
-        public final MenuBuilder menu;
-        public final int position;
-
-        public CascadingMenuInfo(@NonNull MenuPopupWindow window, @NonNull MenuBuilder menu,
-                int position) {
-            this.window = window;
-            this.menu = menu;
-            this.position = position;
-        }
-
-        public ListView getListView() {
-            return window.getListView();
-        }
-    }
-}
diff --git a/android/support/v7/view/menu/ExpandedMenuView.java b/android/support/v7/view/menu/ExpandedMenuView.java
deleted file mode 100644
index 113d56b..0000000
--- a/android/support/v7/view/menu/ExpandedMenuView.java
+++ /dev/null
@@ -1,99 +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.v7.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder.ItemInvoker;
-import android.support.v7.widget.TintTypedArray;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListView;
-
-/**
- * The expanded menu view is a list-like menu with all of the available menu items.  It is opened
- * by the user clicking no the 'More' button on the icon menu view.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class ExpandedMenuView extends ListView
-        implements ItemInvoker, MenuView, OnItemClickListener {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.background,
-            android.R.attr.divider
-    };
-
-    private MenuBuilder mMenu;
-
-    /** Default animations for this menu */
-    private int mAnimations;
-
-    public ExpandedMenuView(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.listViewStyle);
-    }
-
-    public ExpandedMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs);
-        setOnItemClickListener(this);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS,
-                defStyleAttr, 0);
-        if (a.hasValue(0)) {
-            setBackgroundDrawable(a.getDrawable(0));
-        }
-        if (a.hasValue(1)) {
-            setDivider(a.getDrawable(1));
-        }
-        a.recycle();
-    }
-
-    @Override
-    public void initialize(MenuBuilder menu) {
-        mMenu = menu;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        // Clear the cached bitmaps of children
-        setChildrenDrawingCacheEnabled(false);
-    }
-
-    @Override
-    public boolean invokeItem(MenuItemImpl item) {
-        return mMenu.performItemAction(item, 0);
-    }
-
-    @Override
-    @SuppressWarnings("rawtypes")
-    public void onItemClick(AdapterView parent, View v, int position, long id) {
-        invokeItem((MenuItemImpl) getAdapter().getItem(position));
-    }
-
-    @Override
-    public int getWindowAnimations() {
-        return mAnimations;
-    }
-
-}
diff --git a/android/support/v7/view/menu/ListMenuItemView.java b/android/support/v7/view/menu/ListMenuItemView.java
deleted file mode 100644
index 081f304..0000000
--- a/android/support/v7/view/menu/ListMenuItemView.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.widget.TintTypedArray;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import android.widget.TextView;
-
-/**
- * The item view for each item in the ListView-based MenuViews.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
-    private static final String TAG = "ListMenuItemView";
-    private MenuItemImpl mItemData;
-
-    private ImageView mIconView;
-    private RadioButton mRadioButton;
-    private TextView mTitleView;
-    private CheckBox mCheckBox;
-    private TextView mShortcutView;
-    private ImageView mSubMenuArrowView;
-
-    private Drawable mBackground;
-    private int mTextAppearance;
-    private Context mTextAppearanceContext;
-    private boolean mPreserveIconSpacing;
-    private Drawable mSubMenuArrow;
-
-    private int mMenuType;
-
-    private LayoutInflater mInflater;
-
-    private boolean mForceShowIcon;
-
-    public ListMenuItemView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.listMenuViewStyle);
-    }
-
-    public ListMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs);
-
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(),
-                attrs, R.styleable.MenuView, defStyleAttr, 0);
-
-        mBackground = a.getDrawable(R.styleable.MenuView_android_itemBackground);
-        mTextAppearance = a.getResourceId(R.styleable.
-                MenuView_android_itemTextAppearance, -1);
-        mPreserveIconSpacing = a.getBoolean(
-                R.styleable.MenuView_preserveIconSpacing, false);
-        mTextAppearanceContext = context;
-        mSubMenuArrow = a.getDrawable(R.styleable.MenuView_subMenuArrow);
-
-        a.recycle();
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        ViewCompat.setBackground(this, mBackground);
-
-        mTitleView = findViewById(R.id.title);
-        if (mTextAppearance != -1) {
-            mTitleView.setTextAppearance(mTextAppearanceContext,
-                    mTextAppearance);
-        }
-
-        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;
-
-        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
-
-        setTitle(itemData.getTitleForItemView(this));
-        setCheckable(itemData.isCheckable());
-        setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
-        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);
-
-            if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
-        } else {
-            if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
-        }
-    }
-
-    @Override
-    public MenuItemImpl getItemData() {
-        return mItemData;
-    }
-
-    @Override
-    public void setCheckable(boolean checkable) {
-        if (!checkable && mRadioButton == null && mCheckBox == null) {
-            return;
-        }
-
-        // Depending on whether its exclusive check or not, the checkbox or
-        // radio button will be the one in use (and the other will be otherCompoundButton)
-        final CompoundButton compoundButton;
-        final CompoundButton otherCompoundButton;
-
-        if (mItemData.isExclusiveCheckable()) {
-            if (mRadioButton == null) {
-                insertRadioButton();
-            }
-            compoundButton = mRadioButton;
-            otherCompoundButton = mCheckBox;
-        } else {
-            if (mCheckBox == null) {
-                insertCheckBox();
-            }
-            compoundButton = mCheckBox;
-            otherCompoundButton = mRadioButton;
-        }
-
-        if (checkable) {
-            compoundButton.setChecked(mItemData.isChecked());
-
-            final int newVisibility = checkable ? VISIBLE : GONE;
-            if (compoundButton.getVisibility() != newVisibility) {
-                compoundButton.setVisibility(newVisibility);
-            }
-
-            // Make sure the other compound button isn't visible
-            if (otherCompoundButton != null && otherCompoundButton.getVisibility() != GONE) {
-                otherCompoundButton.setVisibility(GONE);
-            }
-        } else {
-            if (mCheckBox != null) {
-                mCheckBox.setVisibility(GONE);
-            }
-            if (mRadioButton != null) {
-                mRadioButton.setVisibility(GONE);
-            }
-        }
-    }
-
-    @Override
-    public void setChecked(boolean checked) {
-        CompoundButton compoundButton;
-
-        if (mItemData.isExclusiveCheckable()) {
-            if (mRadioButton == null) {
-                insertRadioButton();
-            }
-            compoundButton = mRadioButton;
-        } else {
-            if (mCheckBox == null) {
-                insertCheckBox();
-            }
-            compoundButton = mCheckBox;
-        }
-
-        compoundButton.setChecked(checked);
-    }
-
-    private void setSubMenuArrowVisible(boolean hasSubmenu) {
-        if (mSubMenuArrowView != null) {
-            mSubMenuArrowView.setVisibility(hasSubmenu ? View.VISIBLE : View.GONE);
-        }
-    }
-
-    @Override
-    public void setShortcut(boolean showShortcut, char shortcutKey) {
-        final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
-                ? VISIBLE : GONE;
-
-        if (newVisibility == VISIBLE) {
-            mShortcutView.setText(mItemData.getShortcutLabel());
-        }
-
-        if (mShortcutView.getVisibility() != newVisibility) {
-            mShortcutView.setVisibility(newVisibility);
-        }
-    }
-
-    @Override
-    public void setIcon(Drawable icon) {
-        final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
-        if (!showIcon && !mPreserveIconSpacing) {
-            return;
-        }
-
-        if (mIconView == null && icon == null && !mPreserveIconSpacing) {
-            return;
-        }
-
-        if (mIconView == null) {
-            insertIconView();
-        }
-
-        if (icon != null || mPreserveIconSpacing) {
-            mIconView.setImageDrawable(showIcon ? icon : null);
-
-            if (mIconView.getVisibility() != VISIBLE) {
-                mIconView.setVisibility(VISIBLE);
-            }
-        } else {
-            mIconView.setVisibility(GONE);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mIconView != null && mPreserveIconSpacing) {
-            // Enforce minimum icon spacing
-            ViewGroup.LayoutParams lp = getLayoutParams();
-            LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
-            if (lp.height > 0 && iconLp.width <= 0) {
-                iconLp.width = lp.height;
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    private void insertIconView() {
-        LayoutInflater inflater = getInflater();
-        mIconView = (ImageView) inflater.inflate(R.layout.abc_list_menu_item_icon,
-                this, false);
-        addView(mIconView, 0);
-    }
-
-    private void insertRadioButton() {
-        LayoutInflater inflater = getInflater();
-        mRadioButton =
-                (RadioButton) inflater.inflate(R.layout.abc_list_menu_item_radio,
-                        this, false);
-        addView(mRadioButton);
-    }
-
-    private void insertCheckBox() {
-        LayoutInflater inflater = getInflater();
-        mCheckBox =
-                (CheckBox) inflater.inflate(R.layout.abc_list_menu_item_checkbox,
-                        this, false);
-        addView(mCheckBox);
-    }
-
-    @Override
-    public boolean prefersCondensedTitle() {
-        return false;
-    }
-
-    @Override
-    public boolean showsIcon() {
-        return mForceShowIcon;
-    }
-
-    private LayoutInflater getInflater() {
-        if (mInflater == null) {
-            mInflater = LayoutInflater.from(getContext());
-        }
-        return mInflater;
-    }
-}
-
diff --git a/android/support/v7/view/menu/ListMenuPresenter.java b/android/support/v7/view/menu/ListMenuPresenter.java
deleted file mode 100644
index e3179f4..0000000
--- a/android/support/v7/view/menu/ListMenuPresenter.java
+++ /dev/null
@@ -1,297 +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.v7.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.v7.appcompat.R;
-import android.util.SparseArray;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-
-import java.util.ArrayList;
-
-/**
- * MenuPresenter for list-style menus.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
-    private static final String TAG = "ListMenuPresenter";
-
-    Context mContext;
-    LayoutInflater mInflater;
-    MenuBuilder mMenu;
-
-    ExpandedMenuView mMenuView;
-
-    int mItemIndexOffset;
-    int mThemeRes;
-    int mItemLayoutRes;
-
-    private Callback mCallback;
-    MenuAdapter mAdapter;
-
-    private int mId;
-
-    public static final String VIEWS_TAG = "android:menu:list";
-
-    /**
-     * Construct a new ListMenuPresenter.
-     * @param context Context to use for theming. This will supersede the context provided
-     *                to initForMenu when this presenter is added.
-     * @param itemLayoutRes Layout resource for individual item views.
-     */
-    public ListMenuPresenter(Context context, int itemLayoutRes) {
-        this(itemLayoutRes, 0);
-        mContext = context;
-        mInflater = LayoutInflater.from(mContext);
-    }
-
-    /**
-     * Construct a new ListMenuPresenter.
-     * @param itemLayoutRes Layout resource for individual item views.
-     * @param themeRes Resource ID of a theme to use for views.
-     */
-    public ListMenuPresenter(int itemLayoutRes, int themeRes) {
-        mItemLayoutRes = itemLayoutRes;
-        mThemeRes = themeRes;
-    }
-
-    @Override
-    public void initForMenu(Context context, MenuBuilder menu) {
-        if (mThemeRes != 0) {
-            mContext = new ContextThemeWrapper(context, mThemeRes);
-            mInflater = LayoutInflater.from(mContext);
-        } else if (mContext != null) {
-            mContext = context;
-            if (mInflater == null) {
-                mInflater = LayoutInflater.from(mContext);
-            }
-        }
-        mMenu = menu;
-        if (mAdapter != null) {
-            mAdapter.notifyDataSetChanged();
-        }
-    }
-
-    @Override
-    public MenuView getMenuView(ViewGroup root) {
-        if (mMenuView == null) {
-            mMenuView = (ExpandedMenuView) mInflater.inflate(
-                    R.layout.abc_expanded_menu_layout, root, false);
-            if (mAdapter == null) {
-                mAdapter = new MenuAdapter();
-            }
-            mMenuView.setAdapter(mAdapter);
-            mMenuView.setOnItemClickListener(this);
-        }
-        return mMenuView;
-    }
-
-    /**
-     * Call this instead of getMenuView if you want to manage your own ListView.
-     * For proper operation, the ListView hosting this adapter should add
-     * this presenter as an OnItemClickListener.
-     *
-     * @return A ListAdapter containing the items in the menu.
-     */
-    public ListAdapter getAdapter() {
-        if (mAdapter == null) {
-            mAdapter = new MenuAdapter();
-        }
-        return mAdapter;
-    }
-
-    @Override
-    public void updateMenuView(boolean cleared) {
-        if (mAdapter != null) mAdapter.notifyDataSetChanged();
-    }
-
-    @Override
-    public void setCallback(Callback cb) {
-        mCallback = cb;
-    }
-
-    @Override
-    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-        if (!subMenu.hasVisibleItems()) return false;
-
-        // The window manager will give us a token.
-        new MenuDialogHelper(subMenu).show(null);
-        if (mCallback != null) {
-            mCallback.onOpenSubMenu(subMenu);
-        }
-        return true;
-    }
-
-    @Override
-    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        if (mCallback != null) {
-            mCallback.onCloseMenu(menu, allMenusAreClosing);
-        }
-    }
-
-    int getItemIndexOffset() {
-        return mItemIndexOffset;
-    }
-
-    public void setItemIndexOffset(int offset) {
-        mItemIndexOffset = offset;
-        if (mMenuView != null) {
-            updateMenuView(false);
-        }
-    }
-
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        mMenu.performItemAction(mAdapter.getItem(position), this, 0);
-    }
-
-    @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;
-    }
-
-    public void saveHierarchyState(Bundle outState) {
-        SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>();
-        if (mMenuView != null) {
-            ((View) mMenuView).saveHierarchyState(viewStates);
-        }
-        outState.putSparseParcelableArray(VIEWS_TAG, viewStates);
-    }
-
-    public void restoreHierarchyState(Bundle inState) {
-        SparseArray<Parcelable> viewStates = inState.getSparseParcelableArray(VIEWS_TAG);
-        if (viewStates != null) {
-            ((View) mMenuView).restoreHierarchyState(viewStates);
-        }
-    }
-
-    public void setId(int id) {
-        mId = id;
-    }
-
-    @Override
-    public int getId() {
-        return mId;
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        if (mMenuView == null) {
-            return null;
-        }
-
-        Bundle state = new Bundle();
-        saveHierarchyState(state);
-        return state;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        restoreHierarchyState((Bundle) state);
-    }
-
-    private class MenuAdapter extends BaseAdapter {
-        private int mExpandedIndex = -1;
-
-        public MenuAdapter() {
-            findExpandedIndex();
-        }
-
-        @Override
-        public int getCount() {
-            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
-            int count = items.size() - mItemIndexOffset;
-            if (mExpandedIndex < 0) {
-                return count;
-            }
-            return count - 1;
-        }
-
-        @Override
-        public MenuItemImpl getItem(int position) {
-            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
-            position += mItemIndexOffset;
-            if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
-                position++;
-            }
-            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);
-            }
-
-            MenuView.ItemView itemView = (MenuView.ItemView) convertView;
-            itemView.initialize(getItem(position), 0);
-            return convertView;
-        }
-
-        void findExpandedIndex() {
-            final MenuItemImpl expandedItem = mMenu.getExpandedItem();
-            if (expandedItem != null) {
-                final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
-                final int count = items.size();
-                for (int i = 0; i < count; i++) {
-                    final MenuItemImpl item = items.get(i);
-                    if (item == expandedItem) {
-                        mExpandedIndex = i;
-                        return;
-                    }
-                }
-            }
-            mExpandedIndex = -1;
-        }
-
-        @Override
-        public void notifyDataSetChanged() {
-            findExpandedIndex();
-            super.notifyDataSetChanged();
-        }
-    }
-}
diff --git a/android/support/v7/view/menu/MenuAdapter.java b/android/support/v7/view/menu/MenuAdapter.java
deleted file mode 100644
index 3d34a16..0000000
--- a/android/support/v7/view/menu/MenuAdapter.java
+++ /dev/null
@@ -1,125 +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.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.support.v7.appcompat.R;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import java.util.ArrayList;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class MenuAdapter extends BaseAdapter {
-    static final int ITEM_LAYOUT = R.layout.abc_popup_menu_item_layout;
-
-    MenuBuilder mAdapterMenu;
-
-    private int mExpandedIndex = -1;
-
-    private boolean mForceShowIcon;
-    private final boolean mOverflowOnly;
-    private final LayoutInflater mInflater;
-
-    public MenuAdapter(MenuBuilder menu, LayoutInflater inflater, boolean overflowOnly) {
-        mOverflowOnly = overflowOnly;
-        mInflater = inflater;
-        mAdapterMenu = menu;
-        findExpandedIndex();
-    }
-
-    public boolean getForceShowIcon() {
-        return mForceShowIcon;
-    }
-
-    public void setForceShowIcon(boolean forceShow) {
-        mForceShowIcon = forceShow;
-    }
-
-    @Override
-    public int getCount() {
-        ArrayList<MenuItemImpl> items = mOverflowOnly ?
-                mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
-        if (mExpandedIndex < 0) {
-            return items.size();
-        }
-        return items.size() - 1;
-    }
-
-    public MenuBuilder getAdapterMenu() {
-        return mAdapterMenu;
-    }
-
-    @Override
-    public MenuItemImpl getItem(int position) {
-        ArrayList<MenuItemImpl> items = mOverflowOnly ?
-                mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
-        if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
-            position++;
-        }
-        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(ITEM_LAYOUT, parent, false);
-        }
-
-        MenuView.ItemView itemView = (MenuView.ItemView) convertView;
-        if (mForceShowIcon) {
-            ((ListMenuItemView) convertView).setForceShowIcon(true);
-        }
-        itemView.initialize(getItem(position), 0);
-        return convertView;
-    }
-
-    void findExpandedIndex() {
-        final MenuItemImpl expandedItem = mAdapterMenu.getExpandedItem();
-        if (expandedItem != null) {
-            final ArrayList<MenuItemImpl> items = mAdapterMenu.getNonActionItems();
-            final int count = items.size();
-            for (int i = 0; i < count; i++) {
-                final MenuItemImpl item = items.get(i);
-                if (item == expandedItem) {
-                    mExpandedIndex = i;
-                    return;
-                }
-            }
-        }
-        mExpandedIndex = -1;
-    }
-
-    @Override
-    public void notifyDataSetChanged() {
-        findExpandedIndex();
-        super.notifyDataSetChanged();
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/view/menu/MenuBuilder.java b/android/support/v7/view/menu/MenuBuilder.java
deleted file mode 100644
index e6dee8d..0000000
--- a/android/support/v7/view/menu/MenuBuilder.java
+++ /dev/null
@@ -1,1395 +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.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.internal.view.SupportMenu;
-import android.support.v4.internal.view.SupportMenuItem;
-import android.support.v4.view.ActionProvider;
-import android.support.v7.appcompat.R;
-import android.util.SparseArray;
-import android.view.ContextMenu;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * Implementation of the {@link android.support.v4.internal.view.SupportMenu} interface for creating a
- * standard menu UI.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class MenuBuilder implements SupportMenu {
-
-    private static final String TAG = "MenuBuilder";
-
-    private static final String PRESENTER_KEY = "android:menu:presenters";
-    private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates";
-    private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview";
-
-    private static final int[] sCategoryToOrder = new int[]{
-            1, /* No category */
-            4, /* CONTAINER */
-            5, /* SYSTEM */
-            3, /* SECONDARY */
-            2, /* ALTERNATIVE */
-            0, /* SELECTED_ALTERNATIVE */
-    };
-
-    private final Context mContext;
-    private final Resources mResources;
-
-    /**
-     * Whether the shortcuts should be qwerty-accessible. Use isQwertyMode() instead of accessing
-     * this directly.
-     */
-    private boolean mQwertyMode;
-
-    /**
-     * Whether the shortcuts should be visible on menus. Use isShortcutsVisible() instead of
-     * accessing this directly.
-     */
-    private boolean mShortcutsVisible;
-
-    /**
-     * Callback that will receive the various menu-related events generated by this class. Use
-     * getCallback to get a reference to the callback.
-     */
-    private Callback mCallback;
-
-    /**
-     * Contains all of the items for this menu
-     */
-    private ArrayList<MenuItemImpl> mItems;
-
-    /**
-     * Contains only the items that are currently visible.  This will be created/refreshed from
-     * {@link #getVisibleItems()}
-     */
-    private ArrayList<MenuItemImpl> mVisibleItems;
-
-    /**
-     * Whether or not the items (or any one item's shown state) has changed since it was last
-     * fetched from {@link #getVisibleItems()}
-     */
-    private boolean mIsVisibleItemsStale;
-
-    /**
-     * Contains only the items that should appear in the Action Bar, if present.
-     */
-    private ArrayList<MenuItemImpl> mActionItems;
-
-    /**
-     * Contains items that should NOT appear in the Action Bar, if present.
-     */
-    private ArrayList<MenuItemImpl> mNonActionItems;
-
-    /**
-     * Whether or not the items (or any one item's action state) has changed since it was last
-     * fetched.
-     */
-    private boolean mIsActionItemsStale;
-
-    /**
-     * Default value for how added items should show in the action list.
-     */
-    private int mDefaultShowAsAction = SupportMenuItem.SHOW_AS_ACTION_NEVER;
-
-    /**
-     * Current use case is Context Menus: As Views populate the context menu, each one has extra
-     * information that should be passed along.  This is the current menu info that should be set on
-     * all items added to this menu.
-     */
-    private ContextMenu.ContextMenuInfo mCurrentMenuInfo;
-
-    /**
-     * Header title for menu types that have a header (context and submenus)
-     */
-    CharSequence mHeaderTitle;
-
-    /**
-     * Header icon for menu types that have a header and support icons (context)
-     */
-    Drawable mHeaderIcon;
-    /** Header custom view for menu types that have a header and support custom views (context) */
-    View mHeaderView;
-
-    /**
-     * Contains the state of the View hierarchy for all menu views when the menu
-     * was frozen.
-     */
-    private SparseArray<Parcelable> mFrozenViewStates;
-
-    /**
-     * Prevents onItemsChanged from doing its junk, useful for batching commands
-     * that may individually call onItemsChanged.
-     */
-    private boolean mPreventDispatchingItemsChanged = false;
-
-    private boolean mItemsChangedWhileDispatchPrevented = false;
-
-    private boolean mStructureChangedWhileDispatchPrevented = false;
-
-    private boolean mOptionalIconsVisible = false;
-
-    private boolean mIsClosing = false;
-
-    private ArrayList<MenuItemImpl> mTempShortcutItemList = new ArrayList<MenuItemImpl>();
-
-    private CopyOnWriteArrayList<WeakReference<MenuPresenter>> mPresenters =
-            new CopyOnWriteArrayList<WeakReference<MenuPresenter>>();
-
-    /**
-     * Currently expanded menu item; must be collapsed when we clear.
-     */
-    private MenuItemImpl mExpandedItem;
-
-    /**
-     * Whether to override the result of {@link #hasVisibleItems()} and always return true
-     */
-    private boolean mOverrideVisibleItems;
-
-    /**
-     * Called by menu to notify of close and selection changes.
-     * @hide
-     */
-
-    @RestrictTo(LIBRARY_GROUP)
-    public interface Callback {
-
-        /**
-         * Called when a menu item is selected.
-         *
-         * @param menu The menu that is the parent of the item
-         * @param item The menu item that is selected
-         * @return whether the menu item selection was handled
-         */
-        boolean onMenuItemSelected(MenuBuilder menu, MenuItem item);
-
-        /**
-         * Called when the mode of the menu changes (for example, from icon to expanded).
-         *
-         * @param menu the menu that has changed modes
-         */
-        void onMenuModeChange(MenuBuilder menu);
-    }
-
-    /**
-     * Called by menu items to execute their associated action
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public interface ItemInvoker {
-        boolean invokeItem(MenuItemImpl item);
-    }
-
-    public MenuBuilder(Context context) {
-        mContext = context;
-        mResources = context.getResources();
-        mItems = new ArrayList<>();
-
-        mVisibleItems = new ArrayList<>();
-        mIsVisibleItemsStale = true;
-
-        mActionItems = new ArrayList<>();
-        mNonActionItems = new ArrayList<>();
-        mIsActionItemsStale = true;
-
-        setShortcutsVisibleInner(true);
-    }
-
-    public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
-        mDefaultShowAsAction = defaultShowAsAction;
-        return this;
-    }
-
-    /**
-     * Add a presenter to this menu. This will only hold a WeakReference; you do not need to
-     * explicitly remove a presenter, but you can using {@link #removeMenuPresenter(MenuPresenter)}.
-     *
-     * @param presenter The presenter to add
-     */
-    public void addMenuPresenter(MenuPresenter presenter) {
-        addMenuPresenter(presenter, mContext);
-    }
-
-    /**
-     * Add a presenter to this menu that uses an alternate context for
-     * inflating menu items. This will only hold a WeakReference; you do not
-     * need to explicitly remove a presenter, but you can using
-     * {@link #removeMenuPresenter(MenuPresenter)}.
-     *
-     * @param presenter The presenter to add
-     * @param menuContext The context used to inflate menu items
-     */
-    public void addMenuPresenter(MenuPresenter presenter, Context menuContext) {
-        mPresenters.add(new WeakReference<MenuPresenter>(presenter));
-        presenter.initForMenu(menuContext, this);
-        mIsActionItemsStale = true;
-    }
-
-    /**
-     * Remove a presenter from this menu. That presenter will no longer receive notifications of
-     * updates to this menu's data.
-     *
-     * @param presenter The presenter to remove
-     */
-    public void removeMenuPresenter(MenuPresenter presenter) {
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter item = ref.get();
-            if (item == null || item == presenter) {
-                mPresenters.remove(ref);
-            }
-        }
-    }
-
-    private void dispatchPresenterUpdate(boolean cleared) {
-        if (mPresenters.isEmpty()) return;
-
-        stopDispatchingItemsChanged();
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else {
-                presenter.updateMenuView(cleared);
-            }
-        }
-        startDispatchingItemsChanged();
-    }
-
-    private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu,
-            MenuPresenter preferredPresenter) {
-        if (mPresenters.isEmpty()) return false;
-
-        boolean result = false;
-
-        // Try the preferred presenter first.
-        if (preferredPresenter != null) {
-            result = preferredPresenter.onSubMenuSelected(subMenu);
-        }
-
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else if (!result) {
-                result = presenter.onSubMenuSelected(subMenu);
-            }
-        }
-        return result;
-    }
-
-    private void dispatchSaveInstanceState(Bundle outState) {
-        if (mPresenters.isEmpty()) return;
-
-        SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>();
-
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else {
-                final int id = presenter.getId();
-                if (id > 0) {
-                    final Parcelable state = presenter.onSaveInstanceState();
-                    if (state != null) {
-                        presenterStates.put(id, state);
-                    }
-                }
-            }
-        }
-
-        outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
-    }
-
-    private void dispatchRestoreInstanceState(Bundle state) {
-        SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
-
-        if (presenterStates == null || mPresenters.isEmpty()) return;
-
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else {
-                final int id = presenter.getId();
-                if (id > 0) {
-                    Parcelable parcel = presenterStates.get(id);
-                    if (parcel != null) {
-                        presenter.onRestoreInstanceState(parcel);
-                    }
-                }
-            }
-        }
-    }
-
-    public void savePresenterStates(Bundle outState) {
-        dispatchSaveInstanceState(outState);
-    }
-
-    public void restorePresenterStates(Bundle state) {
-        dispatchRestoreInstanceState(state);
-    }
-
-    public void saveActionViewStates(Bundle outStates) {
-        SparseArray<Parcelable> viewStates = null;
-
-        final int itemCount = size();
-        for (int i = 0; i < itemCount; i++) {
-            final MenuItem item = getItem(i);
-            final View v = item.getActionView();
-            if (v != null && v.getId() != View.NO_ID) {
-                if (viewStates == null) {
-                    viewStates = new SparseArray<Parcelable>();
-                }
-                v.saveHierarchyState(viewStates);
-                if (item.isActionViewExpanded()) {
-                    outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId());
-                }
-            }
-            if (item.hasSubMenu()) {
-                final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
-                subMenu.saveActionViewStates(outStates);
-            }
-        }
-
-        if (viewStates != null) {
-            outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates);
-        }
-    }
-
-    public void restoreActionViewStates(Bundle states) {
-        if (states == null) {
-            return;
-        }
-
-        SparseArray<Parcelable> viewStates = states.getSparseParcelableArray(
-                getActionViewStatesKey());
-
-        final int itemCount = size();
-        for (int i = 0; i < itemCount; i++) {
-            final MenuItem item = getItem(i);
-            final View v = item.getActionView();
-            if (v != null && v.getId() != View.NO_ID) {
-                v.restoreHierarchyState(viewStates);
-            }
-            if (item.hasSubMenu()) {
-                final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
-                subMenu.restoreActionViewStates(states);
-            }
-        }
-
-        final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID);
-        if (expandedId > 0) {
-            MenuItem itemToExpand = findItem(expandedId);
-            if (itemToExpand != null) {
-                itemToExpand.expandActionView();
-            }
-        }
-    }
-
-    protected String getActionViewStatesKey() {
-        return ACTION_VIEW_STATES_KEY;
-    }
-
-    public void setCallback(Callback cb) {
-        mCallback = cb;
-    }
-
-    /**
-     * Adds an item to the menu.  The other add methods funnel to this.
-     */
-    protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
-        final int ordering = getOrdering(categoryOrder);
-
-        final MenuItemImpl item = createNewMenuItem(group, id, categoryOrder, ordering, title,
-                mDefaultShowAsAction);
-
-        if (mCurrentMenuInfo != null) {
-            // Pass along the current menu info
-            item.setMenuInfo(mCurrentMenuInfo);
-        }
-
-        mItems.add(findInsertIndex(mItems, ordering), item);
-        onItemsChanged(true);
-
-        return item;
-    }
-
-    // Layoutlib overrides this method to return its custom implementation of MenuItemImpl
-    private MenuItemImpl createNewMenuItem(int group, int id, int categoryOrder, int ordering,
-            CharSequence title, int defaultShowAsAction) {
-        return new MenuItemImpl(this, group, id, categoryOrder, ordering, title,
-                defaultShowAsAction);
-    }
-
-    @Override
-    public MenuItem add(CharSequence title) {
-        return addInternal(0, 0, 0, title);
-    }
-
-    @Override
-    public MenuItem add(int titleRes) {
-        return addInternal(0, 0, 0, mResources.getString(titleRes));
-    }
-
-    @Override
-    public MenuItem add(int group, int id, int categoryOrder, CharSequence title) {
-        return addInternal(group, id, categoryOrder, title);
-    }
-
-    @Override
-    public MenuItem add(int group, int id, int categoryOrder, int title) {
-        return addInternal(group, id, categoryOrder, mResources.getString(title));
-    }
-
-    @Override
-    public SubMenu addSubMenu(CharSequence title) {
-        return addSubMenu(0, 0, 0, title);
-    }
-
-    @Override
-    public SubMenu addSubMenu(int titleRes) {
-        return addSubMenu(0, 0, 0, mResources.getString(titleRes));
-    }
-
-    @Override
-    public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
-        final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title);
-        final SubMenuBuilder subMenu = new SubMenuBuilder(mContext, this, item);
-        item.setSubMenu(subMenu);
-
-        return subMenu;
-    }
-
-    @Override
-    public SubMenu addSubMenu(int group, int id, int categoryOrder, int title) {
-        return addSubMenu(group, id, categoryOrder, mResources.getString(title));
-    }
-
-    @Override
-    public int addIntentOptions(int group, int id, int categoryOrder, ComponentName caller,
-            Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
-        PackageManager pm = mContext.getPackageManager();
-        final List<ResolveInfo> lri =
-                pm.queryIntentActivityOptions(caller, specifics, intent, 0);
-        final int N = lri != null ? lri.size() : 0;
-
-        if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
-            removeGroup(group);
-        }
-
-        for (int i = 0; i < N; i++) {
-            final ResolveInfo ri = lri.get(i);
-            Intent rintent = new Intent(
-                    ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
-            rintent.setComponent(new ComponentName(
-                    ri.activityInfo.applicationInfo.packageName,
-                    ri.activityInfo.name));
-            final MenuItem item = add(group, id, categoryOrder, ri.loadLabel(pm))
-                    .setIcon(ri.loadIcon(pm))
-                    .setIntent(rintent);
-            if (outSpecificItems != null && ri.specificIndex >= 0) {
-                outSpecificItems[ri.specificIndex] = item;
-            }
-        }
-
-        return N;
-    }
-
-    @Override
-    public void removeItem(int id) {
-        removeItemAtInt(findItemIndex(id), true);
-    }
-
-    @Override
-    public void removeGroup(int group) {
-        final int i = findGroupIndex(group);
-
-        if (i >= 0) {
-            final int maxRemovable = mItems.size() - i;
-            int numRemoved = 0;
-            while ((numRemoved++ < maxRemovable) && (mItems.get(i).getGroupId() == group)) {
-                // Don't force update for each one, this method will do it at the end
-                removeItemAtInt(i, false);
-            }
-
-            // Notify menu views
-            onItemsChanged(true);
-        }
-    }
-
-    /**
-     * Remove the item at the given index and optionally forces menu views to
-     * update.
-     *
-     * @param index The index of the item to be removed. If this index is
-     *            invalid an exception is thrown.
-     * @param updateChildrenOnMenuViews Whether to force update on menu views.
-     *            Please make sure you eventually call this after your batch of
-     *            removals.
-     */
-    private void removeItemAtInt(int index, boolean updateChildrenOnMenuViews) {
-        if ((index < 0) || (index >= mItems.size())) return;
-
-        mItems.remove(index);
-
-        if (updateChildrenOnMenuViews) onItemsChanged(true);
-    }
-
-    public void removeItemAt(int index) {
-        removeItemAtInt(index, true);
-    }
-
-    public void clearAll() {
-        mPreventDispatchingItemsChanged = true;
-        clear();
-        clearHeader();
-        mPreventDispatchingItemsChanged = false;
-        mItemsChangedWhileDispatchPrevented = false;
-        mStructureChangedWhileDispatchPrevented = false;
-        onItemsChanged(true);
-    }
-
-    @Override
-    public void clear() {
-        if (mExpandedItem != null) {
-            collapseItemActionView(mExpandedItem);
-        }
-        mItems.clear();
-
-        onItemsChanged(true);
-    }
-
-    void setExclusiveItemChecked(MenuItem item) {
-        final int group = item.getGroupId();
-
-        final int N = mItems.size();
-        stopDispatchingItemsChanged();
-        for (int i = 0; i < N; i++) {
-            MenuItemImpl curItem = mItems.get(i);
-            if (curItem.getGroupId() == group) {
-                if (!curItem.isExclusiveCheckable()) continue;
-                if (!curItem.isCheckable()) continue;
-
-                // Check the item meant to be checked, uncheck the others (that are in the group)
-                curItem.setCheckedInt(curItem == item);
-            }
-        }
-        startDispatchingItemsChanged();
-    }
-
-    @Override
-    public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
-        final int N = mItems.size();
-
-        for (int i = 0; i < N; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.getGroupId() == group) {
-                item.setExclusiveCheckable(exclusive);
-                item.setCheckable(checkable);
-            }
-        }
-    }
-
-    @Override
-    public void setGroupVisible(int group, boolean visible) {
-        final int N = mItems.size();
-
-        // We handle the notification of items being changed ourselves, so we use setVisibleInt rather
-        // than setVisible and at the end notify of items being changed
-
-        boolean changedAtLeastOneItem = false;
-        for (int i = 0; i < N; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.getGroupId() == group) {
-                if (item.setVisibleInt(visible)) changedAtLeastOneItem = true;
-            }
-        }
-
-        if (changedAtLeastOneItem) onItemsChanged(true);
-    }
-
-    @Override
-    public void setGroupEnabled(int group, boolean enabled) {
-        final int N = mItems.size();
-
-        for (int i = 0; i < N; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.getGroupId() == group) {
-                item.setEnabled(enabled);
-            }
-        }
-    }
-
-    @Override
-    public boolean hasVisibleItems() {
-        if (mOverrideVisibleItems) {
-            return true;
-        }
-
-        final int size = size();
-
-        for (int i = 0; i < size; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.isVisible()) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public MenuItem findItem(int id) {
-        final int size = size();
-        for (int i = 0; i < size; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.getItemId() == id) {
-                return item;
-            } else if (item.hasSubMenu()) {
-                MenuItem possibleItem = item.getSubMenu().findItem(id);
-
-                if (possibleItem != null) {
-                    return possibleItem;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    public int findItemIndex(int id) {
-        final int size = size();
-
-        for (int i = 0; i < size; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.getItemId() == id) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
-    public int findGroupIndex(int group) {
-        return findGroupIndex(group, 0);
-    }
-
-    public int findGroupIndex(int group, int start) {
-        final int size = size();
-
-        if (start < 0) {
-            start = 0;
-        }
-
-        for (int i = start; i < size; i++) {
-            final MenuItemImpl item = mItems.get(i);
-
-            if (item.getGroupId() == group) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
-    @Override
-    public int size() {
-        return mItems.size();
-    }
-
-    @Override
-    public MenuItem getItem(int index) {
-        return mItems.get(index);
-    }
-
-    @Override
-    public boolean isShortcutKey(int keyCode, KeyEvent event) {
-        return findItemWithShortcutForKey(keyCode, event) != null;
-    }
-
-    @Override
-    public void setQwertyMode(boolean isQwerty) {
-        mQwertyMode = isQwerty;
-
-        onItemsChanged(false);
-    }
-
-    /**
-     * Returns the ordering across all items. This will grab the category from
-     * the upper bits, find out how to order the category with respect to other
-     * categories, and combine it with the lower bits.
-     *
-     * @param categoryOrder The category order for a particular item (if it has
-     *            not been or/add with a category, the default category is
-     *            assumed).
-     * @return An ordering integer that can be used to order this item across
-     *         all the items (even from other categories).
-     */
-    private static int getOrdering(int categoryOrder) {
-        final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT;
-
-        if (index < 0 || index >= sCategoryToOrder.length) {
-            throw new IllegalArgumentException("order does not contain a valid category.");
-        }
-
-        return (sCategoryToOrder[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK);
-    }
-
-    /**
-     * @return whether the menu shortcuts are in qwerty mode or not
-     */
-    boolean isQwertyMode() {
-        return mQwertyMode;
-    }
-
-    /**
-     * Sets whether the shortcuts should be visible on menus.  Devices without hardware key input
-     * will never make shortcuts visible even if this method is passed 'true'.
-     *
-     * @param shortcutsVisible Whether shortcuts should be visible (if true and a menu item does not
-     *                         have a shortcut defined, that item will still NOT show a shortcut)
-     */
-    public void setShortcutsVisible(boolean shortcutsVisible) {
-        if (mShortcutsVisible == shortcutsVisible) {
-            return;
-        }
-
-        setShortcutsVisibleInner(shortcutsVisible);
-        onItemsChanged(false);
-    }
-
-    private void setShortcutsVisibleInner(boolean shortcutsVisible) {
-        mShortcutsVisible = shortcutsVisible
-                && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
-                && mResources.getBoolean(R.bool.abc_config_showMenuShortcutsWhenKeyboardPresent);
-    }
-
-    /**
-     * @return Whether shortcuts should be visible on menus.
-     */
-    public boolean isShortcutsVisible() {
-        return mShortcutsVisible;
-    }
-
-    Resources getResources() {
-        return mResources;
-    }
-
-    public Context getContext() {
-        return mContext;
-    }
-
-    boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
-        return mCallback != null && mCallback.onMenuItemSelected(menu, item);
-    }
-
-    /**
-     * Dispatch a mode change event to this menu's callback.
-     */
-    public void changeMenuMode() {
-        if (mCallback != null) {
-            mCallback.onMenuModeChange(this);
-        }
-    }
-
-    private static int findInsertIndex(ArrayList<MenuItemImpl> items, int ordering) {
-        for (int i = items.size() - 1; i >= 0; i--) {
-            MenuItemImpl item = items.get(i);
-            if (item.getOrdering() <= ordering) {
-                return i + 1;
-            }
-        }
-
-        return 0;
-    }
-
-    @Override
-    public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
-        final MenuItemImpl item = findItemWithShortcutForKey(keyCode, event);
-
-        boolean handled = false;
-
-        if (item != null) {
-            handled = performItemAction(item, flags);
-        }
-
-        if ((flags & FLAG_ALWAYS_PERFORM_CLOSE) != 0) {
-            close(true /* closeAllMenus */);
-        }
-
-        return handled;
-    }
-
-    /*
-     * This function will return all the menu and sub-menu items that can
-     * be directly (the shortcut directly corresponds) and indirectly
-     * (the ALT-enabled char corresponds to the shortcut) associated
-     * with the keyCode.
-     */
-    @SuppressWarnings("deprecation")
-    void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) {
-        final boolean qwerty = isQwertyMode();
-        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);
-        // The delete key is not mapped to '\b' so we treat it specially
-        if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) {
-            return;
-        }
-
-        // Look for an item whose shortcut is this key.
-        final int N = mItems.size();
-        for (int i = 0; i < N; i++) {
-            MenuItemImpl item = mItems.get(i);
-            if (item.hasSubMenu()) {
-                ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
-            }
-            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);
-            }
-        }
-    }
-
-    /*
-     * We want to return the menu item associated with the key, but if there is no
-     * ambiguity (i.e. there is only one menu item corresponding to the key) we want
-     * to return it even if it's not an exact match; this allow the user to
-     * _not_ use the ALT key for example, making the use of shortcuts slightly more
-     * user-friendly. An example is on the G1, '!' and '1' are on the same key, and
-     * in Gmail, Menu+1 will trigger Menu+! (the actual shortcut).
-     *
-     * On the other hand, if two (or more) shortcuts corresponds to the same key,
-     * we have to only return the exact match.
-     */
-    @SuppressWarnings("deprecation")
-    MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) {
-        // Get all items that can be associated directly or indirectly with the keyCode
-        ArrayList<MenuItemImpl> items = mTempShortcutItemList;
-        items.clear();
-        findItemsWithShortcutForKey(items, keyCode, event);
-
-        if (items.isEmpty()) {
-            return null;
-        }
-
-        final int metaState = event.getMetaState();
-        final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
-        // Get the chars associated with the keyCode (i.e using any chording combo)
-        event.getKeyData(possibleChars);
-
-        // If we have only one element, we can safely returns it
-        final int size = items.size();
-        if (size == 1) {
-            return items.get(0);
-        }
-
-        final boolean qwerty = isQwertyMode();
-        // If we found more than one item associated with the key,
-        // we have to return the exact match
-        for (int i = 0; i < size; i++) {
-            final MenuItemImpl item = items.get(i);
-            final char shortcutChar = qwerty ? item.getAlphabeticShortcut() :
-                    item.getNumericShortcut();
-            if ((shortcutChar == possibleChars.meta[0] &&
-                    (metaState & KeyEvent.META_ALT_ON) == 0)
-                || (shortcutChar == possibleChars.meta[2] &&
-                    (metaState & KeyEvent.META_ALT_ON) != 0)
-                || (qwerty && shortcutChar == '\b' &&
-                    keyCode == KeyEvent.KEYCODE_DEL)) {
-                return item;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean performIdentifierAction(int id, int flags) {
-        // Look for an item whose identifier is the id.
-        return performItemAction(findItem(id), flags);
-    }
-
-    public boolean performItemAction(MenuItem item, int flags) {
-        return performItemAction(item, null, flags);
-    }
-
-    public boolean performItemAction(MenuItem item, MenuPresenter preferredPresenter, int flags) {
-        MenuItemImpl itemImpl = (MenuItemImpl) item;
-
-        if (itemImpl == null || !itemImpl.isEnabled()) {
-            return false;
-        }
-
-        boolean invoked = itemImpl.invoke();
-
-        final ActionProvider provider = itemImpl.getSupportActionProvider();
-        final boolean providerHasSubMenu = provider != null && provider.hasSubMenu();
-        if (itemImpl.hasCollapsibleActionView()) {
-            invoked |= itemImpl.expandActionView();
-            if (invoked) {
-                close(true /* closeAllMenus */);
-            }
-        } else if (itemImpl.hasSubMenu() || providerHasSubMenu) {
-            if ((flags & SupportMenu.FLAG_KEEP_OPEN_ON_SUBMENU_OPENED) == 0) {
-                // If we're not flagged to keep the menu open, close it
-                close(false);
-            }
-
-            if (!itemImpl.hasSubMenu()) {
-                itemImpl.setSubMenu(new SubMenuBuilder(getContext(), this, itemImpl));
-            }
-
-            final SubMenuBuilder subMenu = (SubMenuBuilder) itemImpl.getSubMenu();
-            if (providerHasSubMenu) {
-                provider.onPrepareSubMenu(subMenu);
-            }
-            invoked |= dispatchSubMenuSelected(subMenu, preferredPresenter);
-            if (!invoked) {
-                close(true /* closeAllMenus */);
-            }
-        } else {
-            if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
-                close(true /* closeAllMenus */);
-            }
-        }
-
-        return invoked;
-    }
-
-    /**
-     * Closes the menu.
-     *
-     * @param closeAllMenus {@code true} if all displayed menus and submenus
-     *                      should be completely closed (as when a menu item is
-     *                      selected) or {@code false} if only this menu should
-     *                      be closed
-     */
-    public final void close(boolean closeAllMenus) {
-        if (mIsClosing) return;
-
-        mIsClosing = true;
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else {
-                presenter.onCloseMenu(this, closeAllMenus);
-            }
-        }
-        mIsClosing = false;
-    }
-
-    @Override
-    public void close() {
-        close(true /* closeAllMenus */);
-    }
-
-    /**
-     * Called when an item is added or removed.
-     *
-     * @param structureChanged true if the menu structure changed,
-     *                         false if only item properties changed.
-     *                         (Visibility is a structural property since it affects layout.)
-     */
-    public void onItemsChanged(boolean structureChanged) {
-        if (!mPreventDispatchingItemsChanged) {
-            if (structureChanged) {
-                mIsVisibleItemsStale = true;
-                mIsActionItemsStale = true;
-            }
-
-            dispatchPresenterUpdate(structureChanged);
-        } else {
-            mItemsChangedWhileDispatchPrevented = true;
-            if (structureChanged) {
-                mStructureChangedWhileDispatchPrevented = true;
-            }
-        }
-    }
-
-    /**
-     * Stop dispatching item changed events to presenters until
-     * {@link #startDispatchingItemsChanged()} is called. Useful when
-     * many menu operations are going to be performed as a batch.
-     */
-    public void stopDispatchingItemsChanged() {
-        if (!mPreventDispatchingItemsChanged) {
-            mPreventDispatchingItemsChanged = true;
-            mItemsChangedWhileDispatchPrevented = false;
-            mStructureChangedWhileDispatchPrevented = false;
-        }
-    }
-
-    public void startDispatchingItemsChanged() {
-        mPreventDispatchingItemsChanged = false;
-
-        if (mItemsChangedWhileDispatchPrevented) {
-            mItemsChangedWhileDispatchPrevented = false;
-            onItemsChanged(mStructureChangedWhileDispatchPrevented);
-        }
-    }
-
-    /**
-     * Called by {@link MenuItemImpl} when its visible flag is changed.
-     *
-     * @param item The item that has gone through a visibility change.
-     */
-    void onItemVisibleChanged(MenuItemImpl item) {
-        // Notify of items being changed
-        mIsVisibleItemsStale = true;
-        onItemsChanged(true);
-    }
-
-    /**
-     * Called by {@link MenuItemImpl} when its action request status is changed.
-     *
-     * @param item The item that has gone through a change in action request status.
-     */
-    void onItemActionRequestChanged(MenuItemImpl item) {
-        // Notify of items being changed
-        mIsActionItemsStale = true;
-        onItemsChanged(true);
-    }
-
-    @NonNull
-    public ArrayList<MenuItemImpl> getVisibleItems() {
-        if (!mIsVisibleItemsStale) return mVisibleItems;
-
-        // Refresh the visible items
-        mVisibleItems.clear();
-
-        final int itemsSize = mItems.size();
-        MenuItemImpl item;
-        for (int i = 0; i < itemsSize; i++) {
-            item = mItems.get(i);
-            if (item.isVisible()) mVisibleItems.add(item);
-        }
-
-        mIsVisibleItemsStale = false;
-        mIsActionItemsStale = true;
-
-        return mVisibleItems;
-    }
-
-    /**
-     * This method determines which menu items get to be 'action items' that will appear
-     * in an action bar and which items should be 'overflow items' in a secondary menu.
-     * The rules are as follows:
-     *
-     * <p>Items are considered for inclusion in the order specified within the menu.
-     * There is a limit of mMaxActionItems as a total count, optionally including the overflow
-     * menu button itself. This is a soft limit; if an item shares a group ID with an item
-     * previously included as an action item, the new item will stay with its group and become
-     * an action item itself even if it breaks the max item count limit. This is done to
-     * limit the conceptual complexity of the items presented within an action bar. Only a few
-     * unrelated concepts should be presented to the user in this space, and groups are treated
-     * as a single concept.
-     *
-     * <p>There is also a hard limit of consumed measurable space: mActionWidthLimit. This
-     * limit may be broken by a single item that exceeds the remaining space, but no further
-     * items may be added. If an item that is part of a group cannot fit within the remaining
-     * measured width, the entire group will be demoted to overflow. This is done to ensure room
-     * for navigation and other affordances in the action bar as well as reduce general UI clutter.
-     *
-     * <p>The space freed by demoting a full group cannot be consumed by future menu items.
-     * Once items begin to overflow, all future items become overflow items as well. This is
-     * to avoid inadvertent reordering that may break the app's intended design.
-     */
-    public void flagActionItems() {
-        // Important side effect: if getVisibleItems is stale it may refresh,
-        // which can affect action items staleness.
-        final ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
-
-        if (!mIsActionItemsStale) {
-            return;
-        }
-
-        // Presenters flag action items as needed.
-        boolean flagged = false;
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else {
-                flagged |= presenter.flagActionItems();
-            }
-        }
-
-        if (flagged) {
-            mActionItems.clear();
-            mNonActionItems.clear();
-            final int itemsSize = visibleItems.size();
-            for (int i = 0; i < itemsSize; i++) {
-                MenuItemImpl item = visibleItems.get(i);
-                if (item.isActionButton()) {
-                    mActionItems.add(item);
-                } else {
-                    mNonActionItems.add(item);
-                }
-            }
-        } else {
-            // Nobody flagged anything, everything is a non-action item.
-            // (This happens during a first pass with no action-item presenters.)
-            mActionItems.clear();
-            mNonActionItems.clear();
-            mNonActionItems.addAll(getVisibleItems());
-        }
-        mIsActionItemsStale = false;
-    }
-
-    public ArrayList<MenuItemImpl> getActionItems() {
-        flagActionItems();
-        return mActionItems;
-    }
-
-    public ArrayList<MenuItemImpl> getNonActionItems() {
-        flagActionItems();
-        return mNonActionItems;
-    }
-
-    public void clearHeader() {
-        mHeaderIcon = null;
-        mHeaderTitle = null;
-        mHeaderView = null;
-
-        onItemsChanged(false);
-    }
-
-    private void setHeaderInternal(final int titleRes, final CharSequence title, final int iconRes,
-            final Drawable icon, final View view) {
-        final Resources r = getResources();
-
-        if (view != null) {
-            mHeaderView = view;
-
-            // If using a custom view, then the title and icon aren't used
-            mHeaderTitle = null;
-            mHeaderIcon = null;
-        } else {
-            if (titleRes > 0) {
-                mHeaderTitle = r.getText(titleRes);
-            } else if (title != null) {
-                mHeaderTitle = title;
-            }
-
-            if (iconRes > 0) {
-                mHeaderIcon = ContextCompat.getDrawable(getContext(), iconRes);
-            } else if (icon != null) {
-                mHeaderIcon = icon;
-            }
-
-            // If using the title or icon, then a custom view isn't used
-            mHeaderView = null;
-        }
-
-        // Notify of change
-        onItemsChanged(false);
-    }
-
-    /**
-     * Sets the header's title. This replaces the header view. Called by the
-     * builder-style methods of subclasses.
-     *
-     * @param title The new title.
-     * @return This MenuBuilder so additional setters can be called.
-     */
-    protected MenuBuilder setHeaderTitleInt(CharSequence title) {
-        setHeaderInternal(0, title, 0, null, null);
-        return this;
-    }
-
-    /**
-     * Sets the header's title. This replaces the header view. Called by the
-     * builder-style methods of subclasses.
-     *
-     * @param titleRes The new title (as a resource ID).
-     * @return This MenuBuilder so additional setters can be called.
-     */
-    protected MenuBuilder setHeaderTitleInt(int titleRes) {
-        setHeaderInternal(titleRes, null, 0, null, null);
-        return this;
-    }
-
-    /**
-     * Sets the header's icon. This replaces the header view. Called by the
-     * builder-style methods of subclasses.
-     *
-     * @param icon The new icon.
-     * @return This MenuBuilder so additional setters can be called.
-     */
-    protected MenuBuilder setHeaderIconInt(Drawable icon) {
-        setHeaderInternal(0, null, 0, icon, null);
-        return this;
-    }
-
-    /**
-     * Sets the header's icon. This replaces the header view. Called by the
-     * builder-style methods of subclasses.
-     *
-     * @param iconRes The new icon (as a resource ID).
-     * @return This MenuBuilder so additional setters can be called.
-     */
-    protected MenuBuilder setHeaderIconInt(int iconRes) {
-        setHeaderInternal(0, null, iconRes, null, null);
-        return this;
-    }
-
-    /**
-     * Sets the header's view. This replaces the title and icon. Called by the
-     * builder-style methods of subclasses.
-     *
-     * @param view The new view.
-     * @return This MenuBuilder so additional setters can be called.
-     */
-    protected MenuBuilder setHeaderViewInt(View view) {
-        setHeaderInternal(0, null, 0, null, view);
-        return this;
-    }
-
-    public CharSequence getHeaderTitle() {
-        return mHeaderTitle;
-    }
-
-    public Drawable getHeaderIcon() {
-        return mHeaderIcon;
-    }
-
-    public View getHeaderView() {
-        return mHeaderView;
-    }
-
-    /**
-     * Gets the root menu (if this is a submenu, find its root menu).
-     * @return The root menu.
-     */
-    public MenuBuilder getRootMenu() {
-        return this;
-    }
-
-    /**
-     * Sets the current menu info that is set on all items added to this menu
-     * (until this is called again with different menu info, in which case that
-     * one will be added to all subsequent item additions).
-     *
-     * @param menuInfo The extra menu information to add.
-     */
-    public void setCurrentMenuInfo(ContextMenu.ContextMenuInfo menuInfo) {
-        mCurrentMenuInfo = menuInfo;
-    }
-
-    public void setOptionalIconsVisible(boolean visible) {
-        mOptionalIconsVisible = visible;
-    }
-
-    boolean getOptionalIconsVisible() {
-        return mOptionalIconsVisible;
-    }
-
-    public boolean expandItemActionView(MenuItemImpl item) {
-        if (mPresenters.isEmpty()) return false;
-
-        boolean expanded = false;
-
-        stopDispatchingItemsChanged();
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else if ((expanded = presenter.expandItemActionView(this, item))) {
-                break;
-            }
-        }
-        startDispatchingItemsChanged();
-
-        if (expanded) {
-            mExpandedItem = item;
-        }
-        return expanded;
-    }
-
-    public boolean collapseItemActionView(MenuItemImpl item) {
-        if (mPresenters.isEmpty() || mExpandedItem != item) return false;
-
-        boolean collapsed = false;
-
-        stopDispatchingItemsChanged();
-        for (WeakReference<MenuPresenter> ref : mPresenters) {
-            final MenuPresenter presenter = ref.get();
-            if (presenter == null) {
-                mPresenters.remove(ref);
-            } else if ((collapsed = presenter.collapseItemActionView(this, item))) {
-                break;
-            }
-        }
-        startDispatchingItemsChanged();
-
-        if (collapsed) {
-            mExpandedItem = null;
-        }
-        return collapsed;
-    }
-
-    public MenuItemImpl getExpandedItem() {
-        return mExpandedItem;
-    }
-
-    /**
-     * Allows us to override the value of {@link #hasVisibleItems()} and make it always return true.
-     *
-     * @param override
-     */
-    public void setOverrideVisibleItems(boolean override) {
-        mOverrideVisibleItems = override;
-    }
-}
-
diff --git a/android/support/v7/view/menu/MenuDialogHelper.java b/android/support/v7/view/menu/MenuDialogHelper.java
deleted file mode 100644
index eaf7c82..0000000
--- a/android/support/v7/view/menu/MenuDialogHelper.java
+++ /dev/null
@@ -1,170 +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.v7.view.menu;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.IBinder;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.appcompat.R;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-
-/**
- * Helper for menus that appear as Dialogs (context and submenus).
- */
-class MenuDialogHelper implements DialogInterface.OnKeyListener,
-        DialogInterface.OnClickListener,
-        DialogInterface.OnDismissListener,
-        MenuPresenter.Callback {
-    private MenuBuilder mMenu;
-    private AlertDialog mDialog;
-    ListMenuPresenter mPresenter;
-    private MenuPresenter.Callback mPresenterCallback;
-
-    public MenuDialogHelper(MenuBuilder menu) {
-        mMenu = menu;
-    }
-
-    /**
-     * Shows menu as a dialog.
-     *
-     * @param windowToken Optional token to assign to the window.
-     */
-    public void show(IBinder windowToken) {
-        // Many references to mMenu, create local reference
-        final MenuBuilder menu = mMenu;
-
-        // Get the builder for the dialog
-        final AlertDialog.Builder builder = new AlertDialog.Builder(menu.getContext());
-
-        mPresenter = new ListMenuPresenter(builder.getContext(),
-                R.layout.abc_list_menu_item_layout);
-
-        mPresenter.setCallback(this);
-        mMenu.addMenuPresenter(mPresenter);
-        builder.setAdapter(mPresenter.getAdapter(), this);
-
-        // Set the title
-        final View headerView = menu.getHeaderView();
-        if (headerView != null) {
-            // Menu's client has given a custom header view, use it
-            builder.setCustomTitle(headerView);
-        } else {
-            // Otherwise use the (text) title and icon
-            builder.setIcon(menu.getHeaderIcon()).setTitle(menu.getHeaderTitle());
-        }
-
-        // Set the key listener
-        builder.setOnKeyListener(this);
-
-        // Show the menu
-        mDialog = builder.create();
-        mDialog.setOnDismissListener(this);
-
-        WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes();
-        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-        if (windowToken != null) {
-            lp.token = windowToken;
-        }
-        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-
-        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
-                    && event.getRepeatCount() == 0) {
-                Window win = mDialog.getWindow();
-                if (win != null) {
-                    View decor = win.getDecorView();
-                    if (decor != null) {
-                        KeyEvent.DispatcherState ds = decor.getKeyDispatcherState();
-                        if (ds != null) {
-                            ds.startTracking(event, this);
-                            return true;
-                        }
-                    }
-                }
-            } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) {
-                Window win = mDialog.getWindow();
-                if (win != null) {
-                    View decor = win.getDecorView();
-                    if (decor != null) {
-                        KeyEvent.DispatcherState ds = decor.getKeyDispatcherState();
-                        if (ds != null && ds.isTracking(event)) {
-                            mMenu.close(true);
-                            dialog.dismiss();
-                            return true;
-                        }
-                    }
-                }
-            }
-        }
-
-        // Menu shortcut matching
-        return mMenu.performShortcut(keyCode, event, 0);
-
-    }
-
-    public void setPresenterCallback(MenuPresenter.Callback cb) {
-        mPresenterCallback = cb;
-    }
-
-    /**
-     * Dismisses the menu's dialog.
-     *
-     * @see Dialog#dismiss()
-     */
-    public void dismiss() {
-        if (mDialog != null) {
-            mDialog.dismiss();
-        }
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        mPresenter.onCloseMenu(mMenu, true);
-    }
-
-    @Override
-    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        if (allMenusAreClosing || menu == mMenu) {
-            dismiss();
-        }
-        if (mPresenterCallback != null) {
-            mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
-        }
-    }
-
-    @Override
-    public boolean onOpenSubMenu(MenuBuilder subMenu) {
-        if (mPresenterCallback != null) {
-            return mPresenterCallback.onOpenSubMenu(subMenu);
-        }
-        return false;
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        mMenu.performItemAction((MenuItemImpl) mPresenter.getAdapter().getItem(which), 0);
-    }
-}
diff --git a/android/support/v7/view/menu/MenuHelper.java b/android/support/v7/view/menu/MenuHelper.java
deleted file mode 100644
index b861643..0000000
--- a/android/support/v7/view/menu/MenuHelper.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.v7.view.menu;
-
-/**
- * Interface for a helper capable of presenting a menu.
- */
-interface MenuHelper {
-    void setPresenterCallback(MenuPresenter.Callback cb);
-    void dismiss();
-}
\ No newline at end of file
diff --git a/android/support/v7/view/menu/MenuItemImpl.java b/android/support/v7/view/menu/MenuItemImpl.java
deleted file mode 100644
index d606aa0..0000000
--- a/android/support/v7/view/menu/MenuItemImpl.java
+++ /dev/null
@@ -1,887 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.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;
-import android.view.View;
-import android.view.ViewDebug;
-import android.widget.LinearLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class MenuItemImpl implements SupportMenuItem {
-
-    private static final String TAG = "MenuItemImpl";
-
-    private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
-            SHOW_AS_ACTION_IF_ROOM |
-            SHOW_AS_ACTION_ALWAYS;
-
-    private final int mId;
-    private final int mGroup;
-    private final int mCategoryOrder;
-    private final int mOrdering;
-    private CharSequence mTitle;
-    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;
-
-    /**
-     * The icon's resource ID which is used to get the Drawable when it is
-     * needed (if the Drawable isn't already obtained--only one of the two is
-     * needed).
-     */
-    private int mIconResId = NO_ICON;
-
-    /** The menu to which this item belongs */
-    MenuBuilder mMenu;
-    /** If this item should launch a sub menu, this is the sub menu to launch */
-    private SubMenuBuilder mSubMenu;
-
-    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;
-    private static final int EXCLUSIVE = 0x00000004;
-    private static final int HIDDEN = 0x00000008;
-    private static final int ENABLED = 0x00000010;
-    private static final int IS_ACTION = 0x00000020;
-
-    private int mShowAsAction = SHOW_AS_ACTION_NEVER;
-
-    private View mActionView;
-    private ActionProvider mActionProvider;
-    private MenuItem.OnActionExpandListener mOnActionExpandListener;
-    private boolean mIsActionViewExpanded = false;
-
-    /** Used for the icon resource ID if this item does not have an icon */
-    static final int NO_ICON = 0;
-
-    /**
-     * Current use case is for context menu: Extra information linked to the
-     * View that added this item to the context menu.
-     */
-    private ContextMenuInfo mMenuInfo;
-
-    private static String sPrependShortcutLabel;
-    private static String sEnterShortcutLabel;
-    private static String sDeleteShortcutLabel;
-    private static String sSpaceShortcutLabel;
-
-
-    /**
-     * Instantiates this menu item.
-     *
-     * @param menu
-     * @param group Item ordering grouping control. The item will be added after
-     *            all other items whose order is <= this number, and before any
-     *            that are larger than it. This can also be used to define
-     *            groups of items for batch state changes. Normally use 0.
-     * @param id Unique item ID. Use 0 if you do not need a unique ID.
-     * @param categoryOrder The ordering for this item.
-     * @param title The text to display for the item.
-     */
-    MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
-            CharSequence title, int showAsAction) {
-
-        /*if (sPrependShortcutLabel == null) {
-          // This is instantiated from the UI thread, so no chance of sync issues
-          sPrependShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.prepend_shortcut_label);
-          sEnterShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.menu_enter_shortcut_label);
-          sDeleteShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.menu_delete_shortcut_label);
-          sSpaceShortcutLabel = menu.getContext().getResources().getString(
-              com.android.internal.R.string.menu_space_shortcut_label);
-        }*/
-
-        mMenu = menu;
-        mId = id;
-        mGroup = group;
-        mCategoryOrder = categoryOrder;
-        mOrdering = ordering;
-        mTitle = title;
-        mShowAsAction = showAsAction;
-    }
-
-    /**
-     * Invokes the item by calling various listeners or callbacks.
-     *
-     * @return true if the invocation was handled, false otherwise
-     */
-    public boolean invoke() {
-        if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
-            return true;
-        }
-
-        if (mMenu.dispatchMenuItemSelected(mMenu, this)) {
-            return true;
-        }
-
-        if (mItemCallback != null) {
-            mItemCallback.run();
-            return true;
-        }
-
-        if (mIntent != null) {
-            try {
-                mMenu.getContext().startActivity(mIntent);
-                return true;
-            } catch (ActivityNotFoundException e) {
-                Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
-            }
-        }
-
-        if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
-            return true;
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return (mFlags & ENABLED) != 0;
-    }
-
-    @Override
-    public MenuItem setEnabled(boolean enabled) {
-        if (enabled) {
-            mFlags |= ENABLED;
-        } else {
-            mFlags &= ~ENABLED;
-        }
-
-        mMenu.onItemsChanged(false);
-
-        return this;
-    }
-
-    @Override
-    public int getGroupId() {
-        return mGroup;
-    }
-
-    @Override
-    @ViewDebug.CapturedViewProperty
-    public int getItemId() {
-        return mId;
-    }
-
-    @Override
-    public int getOrder() {
-        return mCategoryOrder;
-    }
-
-    public int getOrdering() {
-        return mOrdering;
-    }
-
-    @Override
-    public Intent getIntent() {
-        return mIntent;
-    }
-
-    @Override
-    public MenuItem setIntent(Intent intent) {
-        mIntent = intent;
-        return this;
-    }
-
-    Runnable getCallback() {
-        return mItemCallback;
-    }
-
-    public MenuItem setCallback(Runnable callback) {
-        mItemCallback = callback;
-        return this;
-    }
-
-    @Override
-    public char getAlphabeticShortcut() {
-        return mShortcutAlphabeticChar;
-    }
-
-    @Override
-    public MenuItem setAlphabeticShortcut(char alphaChar) {
-        if (mShortcutAlphabeticChar == alphaChar) {
-            return this;
-        }
-
-        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
-
-        mMenu.onItemsChanged(false);
-
-        return this;
-    }
-
-    @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;
-        }
-
-        mShortcutNumericChar = numericChar;
-
-        mMenu.onItemsChanged(false);
-
-        return this;
-    }
-
-    @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);
-
-        mMenu.onItemsChanged(false);
-
-        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).
-     */
-    char getShortcut() {
-        return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar);
-    }
-
-    /**
-     * @return The label to show for the shortcut. This includes the chording key (for example
-     *         'Menu+a'). Also, any non-human readable characters should be human readable (for
-     *         example 'Menu+enter').
-     */
-    String getShortcutLabel() {
-
-        char shortcut = getShortcut();
-        if (shortcut == 0) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder(sPrependShortcutLabel);
-        switch (shortcut) {
-
-            case '\n':
-                sb.append(sEnterShortcutLabel);
-                break;
-
-            case '\b':
-                sb.append(sDeleteShortcutLabel);
-                break;
-
-            case ' ':
-                sb.append(sSpaceShortcutLabel);
-                break;
-
-            default:
-                sb.append(shortcut);
-                break;
-        }
-
-        return sb.toString();
-    }
-
-    /**
-     * @return Whether this menu item should be showing shortcuts (depends on
-     *         whether the menu should show shortcuts and whether this item has
-     *         a shortcut defined)
-     */
-    boolean shouldShowShortcut() {
-        // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
-        return mMenu.isShortcutsVisible() && (getShortcut() != 0);
-    }
-
-    @Override
-    public SubMenu getSubMenu() {
-        return mSubMenu;
-    }
-
-    @Override
-    public boolean hasSubMenu() {
-        return mSubMenu != null;
-    }
-
-    public void setSubMenu(SubMenuBuilder subMenu) {
-        mSubMenu = subMenu;
-
-        subMenu.setHeaderTitle(getTitle());
-    }
-
-    @Override
-    @ViewDebug.CapturedViewProperty
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    /**
-     * Gets the title for a particular {@link MenuView.ItemView}
-     *
-     * @param itemView The ItemView that is receiving the title
-     * @return Either the title or condensed title based on what the ItemView prefers
-     */
-    CharSequence getTitleForItemView(MenuView.ItemView itemView) {
-        return ((itemView != null) && itemView.prefersCondensedTitle())
-                ? getTitleCondensed()
-                : getTitle();
-    }
-
-    @Override
-    public MenuItem setTitle(CharSequence title) {
-        mTitle = title;
-
-        mMenu.onItemsChanged(false);
-
-        if (mSubMenu != null) {
-            mSubMenu.setHeaderTitle(title);
-        }
-
-        return this;
-    }
-
-    @Override
-    public MenuItem setTitle(int title) {
-        return setTitle(mMenu.getContext().getString(title));
-    }
-
-    @Override
-    public CharSequence getTitleCondensed() {
-        final CharSequence ctitle = mTitleCondensed != null ? mTitleCondensed : mTitle;
-
-        if (Build.VERSION.SDK_INT < 18 && ctitle != null && !(ctitle instanceof String)) {
-            // For devices pre-JB-MR2, where we have a non-String CharSequence, we need to
-            // convert this to a String so that EventLog.writeEvent() does not throw an exception
-            // in Activity.onMenuItemSelected()
-            return ctitle.toString();
-        } else {
-            // Else, we just return the condensed title
-            return ctitle;
-        }
-    }
-
-    @Override
-    public MenuItem setTitleCondensed(CharSequence title) {
-        mTitleCondensed = title;
-
-        // Could use getTitle() in the loop below, but just cache what it would do here
-        if (title == null) {
-            title = mTitle;
-        }
-
-        mMenu.onItemsChanged(false);
-
-        return this;
-    }
-
-    @Override
-    public Drawable getIcon() {
-        if (mIconDrawable != null) {
-            return applyIconTintIfNecessary(mIconDrawable);
-        }
-
-        if (mIconResId != NO_ICON) {
-            Drawable icon = AppCompatResources.getDrawable(mMenu.getContext(), mIconResId);
-            mIconResId = NO_ICON;
-            mIconDrawable = icon;
-            return applyIconTintIfNecessary(icon);
-        }
-
-        return null;
-    }
-
-    @Override
-    public MenuItem setIcon(Drawable icon) {
-        mIconResId = NO_ICON;
-        mIconDrawable = icon;
-        mNeedToApplyIconTint = true;
-        mMenu.onItemsChanged(false);
-
-        return this;
-    }
-
-    @Override
-    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);
-
-        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;
-    }
-
-    @Override
-    public MenuItem setCheckable(boolean checkable) {
-        final int oldFlags = mFlags;
-        mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
-        if (oldFlags != mFlags) {
-            mMenu.onItemsChanged(false);
-        }
-
-        return this;
-    }
-
-    public void setExclusiveCheckable(boolean exclusive) {
-        mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
-    }
-
-    public boolean isExclusiveCheckable() {
-        return (mFlags & EXCLUSIVE) != 0;
-    }
-
-    @Override
-    public boolean isChecked() {
-        return (mFlags & CHECKED) == CHECKED;
-    }
-
-    @Override
-    public MenuItem setChecked(boolean checked) {
-        if ((mFlags & EXCLUSIVE) != 0) {
-            // Call the method on the Menu since it knows about the others in this
-            // exclusive checkable group
-            mMenu.setExclusiveItemChecked(this);
-        } else {
-            setCheckedInt(checked);
-        }
-
-        return this;
-    }
-
-    void setCheckedInt(boolean checked) {
-        final int oldFlags = mFlags;
-        mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
-        if (oldFlags != mFlags) {
-            mMenu.onItemsChanged(false);
-        }
-    }
-
-    @Override
-    public boolean isVisible() {
-        if (mActionProvider != null && mActionProvider.overridesItemVisibility()) {
-            return (mFlags & HIDDEN) == 0 && mActionProvider.isVisible();
-        }
-        return (mFlags & HIDDEN) == 0;
-    }
-
-    /**
-     * Changes the visibility of the item. This method DOES NOT notify the parent menu of a change
-     * in this item, so this should only be called from methods that will eventually trigger this
-     * change.  If unsure, use {@link #setVisible(boolean)} instead.
-     *
-     * @param shown Whether to show (true) or hide (false).
-     * @return Whether the item's shown state was changed
-     */
-    boolean setVisibleInt(boolean shown) {
-        final int oldFlags = mFlags;
-        mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
-        return oldFlags != mFlags;
-    }
-
-    @Override
-    public MenuItem setVisible(boolean shown) {
-        // Try to set the shown state to the given state. If the shown state was changed
-        // (i.e. the previous state isn't the same as given state), notify the parent menu that
-        // the shown state has changed for this item
-        if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this);
-
-        return this;
-    }
-
-    @Override
-    public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) {
-        mClickListener = clickListener;
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        return mTitle != null ? mTitle.toString() : null;
-    }
-
-    void setMenuInfo(ContextMenuInfo menuInfo) {
-        mMenuInfo = menuInfo;
-    }
-
-    @Override
-    public ContextMenuInfo getMenuInfo() {
-        return mMenuInfo;
-    }
-
-    public void actionFormatChanged() {
-        mMenu.onItemActionRequestChanged(this);
-    }
-
-    /**
-     * @return Whether the menu should show icons for menu items.
-     */
-    public boolean shouldShowIcon() {
-        return mMenu.getOptionalIconsVisible();
-    }
-
-    public boolean isActionButton() {
-        return (mFlags & IS_ACTION) == IS_ACTION;
-    }
-
-    public boolean requestsActionButton() {
-        return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
-    }
-
-    public boolean requiresActionButton() {
-        return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
-    }
-
-    public void setIsActionButton(boolean isActionButton) {
-        if (isActionButton) {
-            mFlags |= IS_ACTION;
-        } else {
-            mFlags &= ~IS_ACTION;
-        }
-    }
-
-    public boolean showsTextAsAction() {
-        return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
-    }
-
-    @Override
-    public void setShowAsAction(int actionEnum) {
-        switch (actionEnum & SHOW_AS_ACTION_MASK) {
-            case SHOW_AS_ACTION_ALWAYS:
-            case SHOW_AS_ACTION_IF_ROOM:
-            case SHOW_AS_ACTION_NEVER:
-                // Looks good!
-                break;
-
-            default:
-                // Mutually exclusive options selected!
-                throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
-                        + " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
-        }
-        mShowAsAction = actionEnum;
-        mMenu.onItemActionRequestChanged(this);
-    }
-
-    @Override
-    public SupportMenuItem setActionView(View view) {
-        mActionView = view;
-        mActionProvider = null;
-        if (view != null && view.getId() == View.NO_ID && mId > 0) {
-            view.setId(mId);
-        }
-        mMenu.onItemActionRequestChanged(this);
-        return this;
-    }
-
-    @Override
-    public SupportMenuItem setActionView(int resId) {
-        final Context context = mMenu.getContext();
-        final LayoutInflater inflater = LayoutInflater.from(context);
-        setActionView(inflater.inflate(resId, new LinearLayout(context), false));
-        return this;
-    }
-
-    @Override
-    public View getActionView() {
-        if (mActionView != null) {
-            return mActionView;
-        } else if (mActionProvider != null) {
-            mActionView = mActionProvider.onCreateActionView(this);
-            return mActionView;
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public MenuItem setActionProvider(android.view.ActionProvider actionProvider) {
-        throw new UnsupportedOperationException(
-                "This is not supported, use MenuItemCompat.setActionProvider()");
-    }
-
-    @Override
-    public android.view.ActionProvider getActionProvider() {
-        throw new UnsupportedOperationException(
-                "This is not supported, use MenuItemCompat.getActionProvider()");
-    }
-
-    @Override
-    public ActionProvider getSupportActionProvider() {
-        return mActionProvider;
-    }
-
-    @Override
-    public SupportMenuItem setSupportActionProvider(ActionProvider actionProvider) {
-        if (mActionProvider != null) {
-            mActionProvider.reset();
-        }
-        mActionView = null;
-        mActionProvider = actionProvider;
-        mMenu.onItemsChanged(true); // Measurement can be changed
-        if (mActionProvider != null) {
-            mActionProvider.setVisibilityListener(new ActionProvider.VisibilityListener() {
-                @Override
-                public void onActionProviderVisibilityChanged(boolean isVisible) {
-                    mMenu.onItemVisibleChanged(MenuItemImpl.this);
-                }
-            });
-        }
-        return this;
-    }
-
-    @Override
-    public SupportMenuItem setShowAsActionFlags(int actionEnum) {
-        setShowAsAction(actionEnum);
-        return this;
-    }
-
-    @Override
-    public boolean expandActionView() {
-        if (!hasCollapsibleActionView()) {
-            return false;
-        }
-
-        if (mOnActionExpandListener == null ||
-                mOnActionExpandListener.onMenuItemActionExpand(this)) {
-            return mMenu.expandItemActionView(this);
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean collapseActionView() {
-        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) {
-            return false;
-        }
-        if (mActionView == null) {
-            // We're already collapsed if we have no action view.
-            return true;
-        }
-
-        if (mOnActionExpandListener == null ||
-                mOnActionExpandListener.onMenuItemActionCollapse(this)) {
-            return mMenu.collapseItemActionView(this);
-        }
-
-        return false;
-    }
-
-    public boolean hasCollapsibleActionView() {
-        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) {
-            if (mActionView == null && mActionProvider != null) {
-                mActionView = mActionProvider.onCreateActionView(this);
-            }
-            return mActionView != null;
-        }
-        return false;
-    }
-
-    public void setActionViewExpanded(boolean isExpanded) {
-        mIsActionViewExpanded = isExpanded;
-        mMenu.onItemsChanged(false);
-    }
-
-    @Override
-    public boolean isActionViewExpanded() {
-        return mIsActionViewExpanded;
-    }
-
-    @Override
-    public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
-        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/android/support/v7/view/menu/MenuItemWrapperICS.java b/android/support/v7/view/menu/MenuItemWrapperICS.java
deleted file mode 100644
index 5a44f1a..0000000
--- a/android/support/v7/view/menu/MenuItemWrapperICS.java
+++ /dev/null
@@ -1,478 +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.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.v7.view.CollapsibleActionView;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import java.lang.reflect.Method;
-
-/**
- * Wraps a support {@link SupportMenuItem} as a framework {@link android.view.MenuItem}
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-@RequiresApi(14)
-public class MenuItemWrapperICS extends BaseMenuWrapper<SupportMenuItem> implements MenuItem {
-    static final String LOG_TAG = "MenuItemWrapper";
-
-    // Reflection Method to call setExclusiveCheckable
-    private Method mSetExclusiveCheckableMethod;
-
-    MenuItemWrapperICS(Context context, SupportMenuItem object) {
-        super(context, object);
-    }
-
-    @Override
-    public int getItemId() {
-        return mWrappedObject.getItemId();
-    }
-
-    @Override
-    public int getGroupId() {
-        return mWrappedObject.getGroupId();
-    }
-
-    @Override
-    public int getOrder() {
-        return mWrappedObject.getOrder();
-    }
-
-    @Override
-    public MenuItem setTitle(CharSequence title) {
-        mWrappedObject.setTitle(title);
-        return this;
-    }
-
-    @Override
-    public MenuItem setTitle(int title) {
-        mWrappedObject.setTitle(title);
-        return this;
-    }
-
-    @Override
-    public CharSequence getTitle() {
-        return mWrappedObject.getTitle();
-    }
-
-    @Override
-    public MenuItem setTitleCondensed(CharSequence title) {
-        mWrappedObject.setTitleCondensed(title);
-        return this;
-    }
-
-    @Override
-    public CharSequence getTitleCondensed() {
-        return mWrappedObject.getTitleCondensed();
-    }
-
-    @Override
-    public MenuItem setIcon(Drawable icon) {
-        mWrappedObject.setIcon(icon);
-        return this;
-    }
-
-    @Override
-    public MenuItem setIcon(int iconRes) {
-        mWrappedObject.setIcon(iconRes);
-        return this;
-    }
-
-    @Override
-    public Drawable getIcon() {
-        return mWrappedObject.getIcon();
-    }
-
-    @Override
-    public MenuItem setIntent(Intent intent) {
-        mWrappedObject.setIntent(intent);
-        return this;
-    }
-
-    @Override
-    public Intent getIntent() {
-        return mWrappedObject.getIntent();
-    }
-
-    @Override
-    public MenuItem setShortcut(char numericChar, char alphaChar) {
-        mWrappedObject.setShortcut(numericChar, alphaChar);
-        return this;
-    }
-
-    @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;
-    }
-
-    @Override
-    public boolean isCheckable() {
-        return mWrappedObject.isCheckable();
-    }
-
-    @Override
-    public MenuItem setChecked(boolean checked) {
-        mWrappedObject.setChecked(checked);
-        return this;
-    }
-
-    @Override
-    public boolean isChecked() {
-        return mWrappedObject.isChecked();
-    }
-
-    @Override
-    public MenuItem setVisible(boolean visible) {
-        return mWrappedObject.setVisible(visible);
-    }
-
-    @Override
-    public boolean isVisible() {
-        return mWrappedObject.isVisible();
-    }
-
-    @Override
-    public MenuItem setEnabled(boolean enabled) {
-        mWrappedObject.setEnabled(enabled);
-        return this;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return mWrappedObject.isEnabled();
-    }
-
-    @Override
-    public boolean hasSubMenu() {
-        return mWrappedObject.hasSubMenu();
-    }
-
-    @Override
-    public SubMenu getSubMenu() {
-        return getSubMenuWrapper(mWrappedObject.getSubMenu());
-    }
-
-    @Override
-    public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
-        mWrappedObject.setOnMenuItemClickListener(menuItemClickListener != null ?
-                new OnMenuItemClickListenerWrapper(menuItemClickListener) : null);
-        return this;
-    }
-
-    @Override
-    public ContextMenu.ContextMenuInfo getMenuInfo() {
-        return mWrappedObject.getMenuInfo();
-    }
-
-    @Override
-    public void setShowAsAction(int actionEnum) {
-        mWrappedObject.setShowAsAction(actionEnum);
-    }
-
-    @Override
-    public MenuItem setShowAsActionFlags(int actionEnum) {
-        mWrappedObject.setShowAsActionFlags(actionEnum);
-        return this;
-    }
-
-    @Override
-    public MenuItem setActionView(View view) {
-        if (view instanceof android.view.CollapsibleActionView) {
-            view = new CollapsibleActionViewWrapper(view);
-        }
-        mWrappedObject.setActionView(view);
-        return this;
-    }
-
-    @Override
-    public MenuItem setActionView(int resId) {
-        // Make framework menu item inflate the view
-        mWrappedObject.setActionView(resId);
-
-        View actionView = mWrappedObject.getActionView();
-        if (actionView instanceof android.view.CollapsibleActionView) {
-            // If the inflated Action View is support-collapsible, wrap it
-            mWrappedObject.setActionView(new CollapsibleActionViewWrapper(actionView));
-        }
-        return this;
-    }
-
-    @Override
-    public View getActionView() {
-        View actionView = mWrappedObject.getActionView();
-        if (actionView instanceof CollapsibleActionViewWrapper) {
-            return ((CollapsibleActionViewWrapper) actionView).getWrappedView();
-        }
-        return actionView;
-    }
-
-    @Override
-    public MenuItem setActionProvider(android.view.ActionProvider provider) {
-        mWrappedObject.setSupportActionProvider(
-                provider != null ? createActionProviderWrapper(provider) : null);
-        return this;
-    }
-
-    @Override
-    public android.view.ActionProvider getActionProvider() {
-        ActionProvider provider = mWrappedObject.getSupportActionProvider();
-        if (provider instanceof ActionProviderWrapper) {
-            return ((ActionProviderWrapper) provider).mInner;
-        }
-        return null;
-    }
-
-    @Override
-    public boolean expandActionView() {
-        return mWrappedObject.expandActionView();
-    }
-
-    @Override
-    public boolean collapseActionView() {
-        return mWrappedObject.collapseActionView();
-    }
-
-    @Override
-    public boolean isActionViewExpanded() {
-        return mWrappedObject.isActionViewExpanded();
-    }
-
-    @Override
-    public MenuItem setOnActionExpandListener(MenuItem.OnActionExpandListener listener) {
-        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) {
-                mSetExclusiveCheckableMethod = mWrappedObject.getClass()
-                        .getDeclaredMethod("setExclusiveCheckable", Boolean.TYPE);
-            }
-            mSetExclusiveCheckableMethod.invoke(mWrappedObject, checkable);
-        } catch (Exception e) {
-            Log.w(LOG_TAG, "Error while calling setExclusiveCheckable", e);
-        }
-    }
-
-    ActionProviderWrapper createActionProviderWrapper(android.view.ActionProvider provider) {
-        return new ActionProviderWrapper(mContext, provider);
-    }
-
-    private class OnMenuItemClickListenerWrapper extends BaseWrapper<OnMenuItemClickListener>
-            implements android.view.MenuItem.OnMenuItemClickListener {
-
-        OnMenuItemClickListenerWrapper(OnMenuItemClickListener object) {
-            super(object);
-        }
-
-        @Override
-        public boolean onMenuItemClick(android.view.MenuItem item) {
-            return mWrappedObject.onMenuItemClick(getMenuItemWrapper(item));
-        }
-    }
-
-    private class OnActionExpandListenerWrapper extends BaseWrapper<MenuItem.OnActionExpandListener>
-            implements MenuItem.OnActionExpandListener {
-
-        OnActionExpandListenerWrapper(MenuItem.OnActionExpandListener object) {
-            super(object);
-        }
-
-        @Override
-        public boolean onMenuItemActionExpand(android.view.MenuItem item) {
-            return mWrappedObject.onMenuItemActionExpand(getMenuItemWrapper(item));
-        }
-
-        @Override
-        public boolean onMenuItemActionCollapse(android.view.MenuItem item) {
-            return mWrappedObject.onMenuItemActionCollapse(getMenuItemWrapper(item));
-        }
-    }
-
-    class ActionProviderWrapper extends android.support.v4.view.ActionProvider {
-        final android.view.ActionProvider mInner;
-
-        public ActionProviderWrapper(Context context, android.view.ActionProvider inner) {
-            super(context);
-            mInner = inner;
-        }
-
-        @Override
-        public View onCreateActionView() {
-            return mInner.onCreateActionView();
-        }
-
-        @Override
-        public boolean onPerformDefaultAction() {
-            return mInner.onPerformDefaultAction();
-        }
-
-        @Override
-        public boolean hasSubMenu() {
-            return mInner.hasSubMenu();
-        }
-
-        @Override
-        public void onPrepareSubMenu(android.view.SubMenu subMenu) {
-            mInner.onPrepareSubMenu(getSubMenuWrapper(subMenu));
-        }
-    }
-
-    /**
-     * Wrap a support {@link android.support.v7.view.CollapsibleActionView} into a framework
-     * {@link android.view.CollapsibleActionView}.
-     */
-    static class CollapsibleActionViewWrapper extends FrameLayout
-            implements CollapsibleActionView {
-
-        final android.view.CollapsibleActionView mWrappedView;
-
-        CollapsibleActionViewWrapper(View actionView) {
-            super(actionView.getContext());
-            mWrappedView = (android.view.CollapsibleActionView) actionView;
-            addView(actionView);
-        }
-
-        @Override
-        public void onActionViewExpanded() {
-            mWrappedView.onActionViewExpanded();
-        }
-
-        @Override
-        public void onActionViewCollapsed() {
-            mWrappedView.onActionViewCollapsed();
-        }
-
-        View getWrappedView() {
-            return (View) mWrappedView;
-        }
-    }
-}
diff --git a/android/support/v7/view/menu/MenuItemWrapperJB.java b/android/support/v7/view/menu/MenuItemWrapperJB.java
deleted file mode 100644
index 267903b..0000000
--- a/android/support/v7/view/menu/MenuItemWrapperJB.java
+++ /dev/null
@@ -1,87 +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.v7.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-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.view.MenuItem;
-import android.view.View;
-
-/**
- * Wraps a support {@link SupportMenuItem} as a framework {@link android.view.MenuItem}
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-@RequiresApi(16)
-class MenuItemWrapperJB extends MenuItemWrapperICS {
-
-    MenuItemWrapperJB(Context context, SupportMenuItem object) {
-        super(context, object);
-    }
-
-    @Override
-    ActionProviderWrapper createActionProviderWrapper(android.view.ActionProvider provider) {
-        return new ActionProviderWrapperJB(mContext, provider);
-    }
-
-    class ActionProviderWrapperJB extends ActionProviderWrapper
-            implements android.view.ActionProvider.VisibilityListener {
-        ActionProvider.VisibilityListener mListener;
-
-        public ActionProviderWrapperJB(Context context, android.view.ActionProvider inner) {
-            super(context, inner);
-        }
-
-        @Override
-        public View onCreateActionView(MenuItem forItem) {
-            return mInner.onCreateActionView(forItem);
-        }
-
-        @Override
-        public boolean overridesItemVisibility() {
-            return mInner.overridesItemVisibility();
-        }
-
-        @Override
-        public boolean isVisible() {
-            return mInner.isVisible();
-        }
-
-        @Override
-        public void refreshVisibility() {
-            mInner.refreshVisibility();
-        }
-
-        @Override
-        public void setVisibilityListener(ActionProvider.VisibilityListener listener) {
-            mListener = listener;
-            mInner.setVisibilityListener(listener != null ? this : null);
-        }
-
-        @Override
-        public void onActionProviderVisibilityChanged(boolean isVisible) {
-            if (mListener != null) {
-                mListener.onActionProviderVisibilityChanged(isVisible);
-            }
-        }
-    }
-}
diff --git a/android/support/v7/view/menu/MenuPopup.java b/android/support/v7/view/menu/MenuPopup.java
deleted file mode 100644
index e4945e4..0000000
--- a/android/support/v7/view/menu/MenuPopup.java
+++ /dev/null
@@ -1,218 +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.view.menu;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.internal.view.SupportMenu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.FrameLayout;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
-import android.widget.PopupWindow;
-
-/**
- * Base class for a menu popup abstraction - i.e., some type of menu, housed in a popup window
- * environment.
- */
-abstract class MenuPopup implements ShowableListMenu, MenuPresenter,
-        AdapterView.OnItemClickListener {
-    private Rect mEpicenterBounds;
-
-    public abstract void setForceShowIcon(boolean forceShow);
-
-    /**
-     * Adds the given menu to the popup, if it is capable of displaying submenus within itself.
-     * If menu is the first menu shown, it won't be displayed until show() is called.
-     * If the popup was already showing, adding a submenu via this method will cause that new
-     * submenu to be shown immediately (that is, if this MenuPopup implementation is capable of
-     * showing its own submenus).
-     *
-     * @param menu
-     */
-    public abstract void addMenu(MenuBuilder menu);
-
-    public abstract void setGravity(int dropDownGravity);
-
-    public abstract void setAnchorView(View anchor);
-
-    public abstract void setHorizontalOffset(int x);
-
-    public abstract void setVerticalOffset(int y);
-
-    /**
-     * Specifies the anchor-relative bounds of the popup's transition
-     * epicenter.
-     *
-     * @param bounds anchor-relative bounds
-     */
-    public void setEpicenterBounds(Rect bounds) {
-        mEpicenterBounds = bounds;
-    }
-
-    /**
-     * @return anchor-relative bounds of the popup's transition epicenter
-     */
-    public Rect getEpicenterBounds() {
-        return mEpicenterBounds;
-    }
-
-    /**
-     * Set whether a title entry should be shown in the popup menu (if a title exists for the
-     * menu).
-     *
-     * @param showTitle
-     */
-    public abstract void setShowTitle(boolean showTitle);
-
-    /**
-     * Set a listener to receive a callback when the popup is dismissed.
-     *
-     * @param listener Listener that will be notified when the popup is dismissed.
-     */
-    public abstract void setOnDismissListener(PopupWindow.OnDismissListener listener);
-
-    @Override
-    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
-        // Don't need to do anything; we added as a presenter in the constructor.
-    }
-
-    @Override
-    public MenuView getMenuView(ViewGroup root) {
-        throw new UnsupportedOperationException("MenuPopups manage their own views");
-    }
-
-    @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 0;
-    }
-
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        ListAdapter outerAdapter = (ListAdapter) parent.getAdapter();
-        MenuAdapter wrappedAdapter = toMenuAdapter(outerAdapter);
-
-        // Use the position from the outer adapter so that if a header view was added, we don't get
-        // an off-by-1 error in position.
-        wrappedAdapter.mAdapterMenu.performItemAction(
-                (MenuItem) outerAdapter.getItem(position),
-                this, // always make sure that we show the sub-menu
-                closeMenuOnSubMenuOpened() ? 0 : SupportMenu.FLAG_KEEP_OPEN_ON_SUBMENU_OPENED);
-    }
-
-    /**
-     * Measures the width of the given menu view.
-     *
-     * @param view The view to measure.
-     * @return The width.
-     */
-    protected static int measureIndividualMenuWidth(ListAdapter adapter, ViewGroup parent,
-            Context context, int maxAllowedWidth) {
-        // Menus don't tend to be long, so this is more sane than it looks.
-        int maxWidth = 0;
-        View itemView = null;
-        int itemType = 0;
-
-        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        final int count = adapter.getCount();
-        for (int i = 0; i < count; i++) {
-            final int positionType = adapter.getItemViewType(i);
-            if (positionType != itemType) {
-                itemType = positionType;
-                itemView = null;
-            }
-
-            if (parent == null) {
-                parent = new FrameLayout(context);
-            }
-
-            itemView = adapter.getView(i, itemView, parent);
-            itemView.measure(widthMeasureSpec, heightMeasureSpec);
-
-            final int itemWidth = itemView.getMeasuredWidth();
-            if (itemWidth >= maxAllowedWidth) {
-                return maxAllowedWidth;
-            } else if (itemWidth > maxWidth) {
-                maxWidth = itemWidth;
-            }
-        }
-
-        return maxWidth;
-    }
-
-    /**
-     * Converts the given ListAdapter originating from a menu, to a MenuAdapter, accounting for
-     * the possibility of the parameter adapter actually wrapping the MenuAdapter. (That could
-     * happen if a header view was added on the menu.)
-     *
-     * @param adapter
-     * @return
-     */
-    protected static MenuAdapter toMenuAdapter(ListAdapter adapter) {
-        if (adapter instanceof HeaderViewListAdapter) {
-            return (MenuAdapter) ((HeaderViewListAdapter) adapter).getWrappedAdapter();
-        }
-        return (MenuAdapter) adapter;
-    }
-
-    /**
-     * Returns whether icon spacing needs to be preserved for the given menu, based on whether any
-     * of its items contains an icon.
-     *
-     * NOTE: This should only be used for non-overflow-only menus, because this method does not
-     * take into account whether the menu items are being shown as part of the popup or or being
-     * shown as actions in the action bar.
-     *
-     * @param menu
-     * @return Whether to preserve icon spacing.
-     */
-    protected static boolean shouldPreserveIconSpacing(MenuBuilder menu) {
-        boolean preserveIconSpacing = false;
-        final int count = menu.size();
-
-        for (int i = 0; i < count; i++) {
-            MenuItem childItem = menu.getItem(i);
-            if (childItem.isVisible() && childItem.getIcon() != null) {
-                preserveIconSpacing = true;
-                break;
-            }
-        }
-
-        return preserveIconSpacing;
-    }
-
-    protected boolean closeMenuOnSubMenuOpened() {
-        return true;
-    }
-}
diff --git a/android/support/v7/view/menu/MenuPopupHelper.java b/android/support/v7/view/menu/MenuPopupHelper.java
deleted file mode 100644
index 654ef7d..0000000
--- a/android/support/v7/view/menu/MenuPopupHelper.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Build;
-import android.support.annotation.AttrRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleRes;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.PopupWindow.OnDismissListener;
-
-/**
- * Presents a menu as a small, simple popup anchored to another view.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class MenuPopupHelper implements MenuHelper {
-    private static final int TOUCH_EPICENTER_SIZE_DP = 48;
-
-    private final Context mContext;
-
-    // Immutable cached popup menu properties.
-    private final MenuBuilder mMenu;
-    private final boolean mOverflowOnly;
-    private final int mPopupStyleAttr;
-    private final int mPopupStyleRes;
-
-    // Mutable cached popup menu properties.
-    private View mAnchorView;
-    private int mDropDownGravity = Gravity.START;
-    private boolean mForceShowIcon;
-    private MenuPresenter.Callback mPresenterCallback;
-
-    private MenuPopup mPopup;
-    private OnDismissListener mOnDismissListener;
-
-    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) {
-        this(context, menu, null, false, R.attr.popupMenuStyle, 0);
-    }
-
-    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
-            @NonNull View anchorView) {
-        this(context, menu, anchorView, false, R.attr.popupMenuStyle, 0);
-    }
-
-    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
-            @NonNull View anchorView,
-            boolean overflowOnly, @AttrRes int popupStyleAttr) {
-        this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0);
-    }
-
-    public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu,
-            @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr,
-            @StyleRes int popupStyleRes) {
-        mContext = context;
-        mMenu = menu;
-        mAnchorView = anchorView;
-        mOverflowOnly = overflowOnly;
-        mPopupStyleAttr = popupStyleAttr;
-        mPopupStyleRes = popupStyleRes;
-    }
-
-    public void setOnDismissListener(@Nullable OnDismissListener listener) {
-        mOnDismissListener = listener;
-    }
-
-    /**
-      * Sets the view to which the popup window is anchored.
-      * <p>
-      * Changes take effect on the next call to show().
-      *
-      * @param anchor the view to which the popup window should be anchored
-      */
-    public void setAnchorView(@NonNull View anchor) {
-        mAnchorView = anchor;
-    }
-
-    /**
-     * Sets whether the popup menu's adapter is forced to show icons in the
-     * menu item views.
-     * <p>
-     * Changes take effect on the next call to show().
-     *
-     * @param forceShowIcon {@code true} to force icons to be shown, or
-     *                  {@code false} for icons to be optionally shown
-     */
-    public void setForceShowIcon(boolean forceShowIcon) {
-        mForceShowIcon = forceShowIcon;
-        if (mPopup != null) {
-            mPopup.setForceShowIcon(forceShowIcon);
-        }
-    }
-
-    /**
-      * Sets the alignment of the popup window relative to the anchor view.
-      * <p>
-      * Changes take effect on the next call to show().
-      *
-      * @param gravity alignment of the popup relative to the anchor
-      */
-    public void setGravity(int gravity) {
-        mDropDownGravity = gravity;
-    }
-
-    /**
-     * @return alignment of the popup relative to the anchor
-     */
-    public int getGravity() {
-        return mDropDownGravity;
-    }
-
-    public void show() {
-        if (!tryShow()) {
-            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
-        }
-    }
-
-    public void show(int x, int y) {
-        if (!tryShow(x, y)) {
-            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
-        }
-    }
-
-    @NonNull
-    public MenuPopup getPopup() {
-        if (mPopup == null) {
-            mPopup = createPopup();
-        }
-        return mPopup;
-    }
-
-    /**
-     * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}.
-     *
-     * @return {@code true} if the popup was shown or was already showing prior to calling this
-     *         method, {@code false} otherwise
-     */
-    public boolean tryShow() {
-        if (isShowing()) {
-            return true;
-        }
-
-        if (mAnchorView == null) {
-            return false;
-        }
-
-        showPopup(0, 0, false, false);
-        return true;
-    }
-
-    /**
-     * Shows the popup menu and makes a best-effort to anchor it to the
-     * specified (x,y) coordinate relative to the anchor view.
-     * <p>
-     * Additionally, the popup's transition epicenter (see
-     * {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be
-     * centered on the specified coordinate, rather than using the bounds of
-     * the anchor view.
-     * <p>
-     * If the popup's resolved gravity is {@link Gravity#LEFT}, this will
-     * display the popup with its top-left corner at (x,y) relative to the
-     * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the
-     * popup's top-right corner will be at (x,y).
-     * <p>
-     * If the popup cannot be displayed fully on-screen, this method will
-     * attempt to scroll the anchor view's ancestors and/or offset the popup
-     * such that it may be displayed fully on-screen.
-     *
-     * @param x x coordinate relative to the anchor view
-     * @param y y coordinate relative to the anchor view
-     * @return {@code true} if the popup was shown or was already showing prior
-     *         to calling this method, {@code false} otherwise
-     */
-    public boolean tryShow(int x, int y) {
-        if (isShowing()) {
-            return true;
-        }
-
-        if (mAnchorView == null) {
-            return false;
-        }
-
-        showPopup(x, y, true, true);
-        return true;
-    }
-
-    /**
-     * Creates the popup and assigns cached properties.
-     *
-     * @return an initialized popup
-     */
-    @NonNull
-    private MenuPopup createPopup() {
-        final WindowManager windowManager = (WindowManager) mContext.getSystemService(
-                Context.WINDOW_SERVICE);
-        final Display display = windowManager.getDefaultDisplay();
-        final Point displaySize = new Point();
-
-        if (Build.VERSION.SDK_INT >= 17) {
-            display.getRealSize(displaySize);
-        } else {
-            display.getSize(displaySize);
-        }
-
-        final int smallestWidth = Math.min(displaySize.x, displaySize.y);
-        final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize(
-                R.dimen.abc_cascading_menus_min_smallest_width);
-        final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading;
-
-        final MenuPopup popup;
-        if (enableCascadingSubmenus) {
-            popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr,
-                    mPopupStyleRes, mOverflowOnly);
-        } else {
-            popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr,
-                    mPopupStyleRes, mOverflowOnly);
-        }
-
-        // Assign immutable properties.
-        popup.addMenu(mMenu);
-        popup.setOnDismissListener(mInternalOnDismissListener);
-
-        // Assign mutable properties. These may be reassigned later.
-        popup.setAnchorView(mAnchorView);
-        popup.setCallback(mPresenterCallback);
-        popup.setForceShowIcon(mForceShowIcon);
-        popup.setGravity(mDropDownGravity);
-
-        return popup;
-    }
-
-    private void showPopup(int xOffset, int yOffset, boolean useOffsets, boolean showTitle) {
-        final MenuPopup popup = getPopup();
-        popup.setShowTitle(showTitle);
-
-        if (useOffsets) {
-            // If the resolved drop-down gravity is RIGHT, the popup's right
-            // edge will be aligned with the anchor view. Adjust by the anchor
-            // width such that the top-right corner is at the X offset.
-            final int hgrav = GravityCompat.getAbsoluteGravity(mDropDownGravity,
-                    ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK;
-            if (hgrav == Gravity.RIGHT) {
-                xOffset += mAnchorView.getWidth();
-            }
-
-            popup.setHorizontalOffset(xOffset);
-            popup.setVerticalOffset(yOffset);
-
-            // Set the transition epicenter to be roughly finger (or mouse
-            // cursor) sized and centered around the offset position. This
-            // will give the appearance that the window is emerging from
-            // the touch point.
-            final float density = mContext.getResources().getDisplayMetrics().density;
-            final int halfSize = (int) (TOUCH_EPICENTER_SIZE_DP * density / 2);
-            final Rect epicenter = new Rect(xOffset - halfSize, yOffset - halfSize,
-                    xOffset + halfSize, yOffset + halfSize);
-            popup.setEpicenterBounds(epicenter);
-        }
-
-        popup.show();
-    }
-
-    /**
-     * Dismisses the popup, if showing.
-     */
-    @Override
-    public void dismiss() {
-        if (isShowing()) {
-            mPopup.dismiss();
-        }
-    }
-
-    /**
-     * Called after the popup has been dismissed.
-     * <p>
-     * <strong>Note:</strong> Subclasses should call the super implementation
-     * last to ensure that any necessary tear down has occurred before the
-     * listener specified by {@link #setOnDismissListener(OnDismissListener)}
-     * is called.
-     */
-    protected void onDismiss() {
-        mPopup = null;
-
-        if (mOnDismissListener != null) {
-            mOnDismissListener.onDismiss();
-        }
-    }
-
-    public boolean isShowing() {
-        return mPopup != null && mPopup.isShowing();
-    }
-
-    @Override
-    public void setPresenterCallback(@Nullable MenuPresenter.Callback cb) {
-        mPresenterCallback = cb;
-        if (mPopup != null) {
-            mPopup.setCallback(cb);
-        }
-    }
-
-    /**
-     * Listener used to proxy dismiss callbacks to the helper's owner.
-     */
-    private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() {
-        @Override
-        public void onDismiss() {
-            MenuPopupHelper.this.onDismiss();
-        }
-    };
-}
diff --git a/android/support/v7/view/menu/MenuPresenter.java b/android/support/v7/view/menu/MenuPresenter.java
deleted file mode 100644
index e32e387..0000000
--- a/android/support/v7/view/menu/MenuPresenter.java
+++ /dev/null
@@ -1,159 +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.v7.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.view.ViewGroup;
-
-/**
- * A MenuPresenter is responsible for building views for a Menu object. It takes over some
- * responsibility from the old style monolithic MenuBuilder class.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface MenuPresenter {
-
-    /**
-     * Called by menu implementation to notify another component of open/close events.
-     */
-    interface Callback {
-        /**
-         * Called when a menu is closing.
-         * @param menu
-         * @param allMenusAreClosing
-         */
-        void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
-
-        /**
-         * Called when a submenu opens. Useful for notifying the application
-         * of menu state so that it does not attempt to hide the action bar
-         * while a submenu is open or similar.
-         *
-         * @param subMenu Submenu currently being opened
-         * @return true if the Callback will handle presenting the submenu, false if
-         *         the presenter should attempt to do so.
-         */
-        boolean onOpenSubMenu(MenuBuilder subMenu);
-    }
-
-    /**
-     * 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 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);
-
-    /**
-     * Retrieve a MenuView to display the menu specified in
-     * {@link #initForMenu(Context, MenuBuilder)}.
-     *
-     * @param root Intended parent of the MenuView.
-     * @return A freshly created MenuView.
-     */
-    MenuView getMenuView(ViewGroup root);
-
-    /**
-     * Update the menu UI in response to a change. Called by
-     * MenuBuilder during the normal course of operation.
-     *
-     * @param cleared true if the menu was entirely cleared
-     */
-    void updateMenuView(boolean cleared);
-
-    /**
-     * Set a callback object that will be notified of menu events
-     * related to this specific presentation.
-     * @param cb Callback that will be notified of future events
-     */
-    void setCallback(Callback cb);
-
-    /**
-     * Called by Menu implementations to indicate that a submenu item
-     * has been selected. An active Callback should be notified, and
-     * if applicable the presenter should present the submenu.
-     *
-     * @param subMenu SubMenu being opened
-     * @return true if the the event was handled, false otherwise.
-     */
-    boolean onSubMenuSelected(SubMenuBuilder subMenu);
-
-    /**
-     * Called by Menu implementations to indicate that a menu or submenu is
-     * closing. Presenter implementations should close the representation
-     * of the menu indicated as necessary and notify a registered callback.
-     *
-     * @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);
-
-    /**
-     * Called by Menu implementations to flag items that will be shown as actions.
-     * @return true if this presenter changed the action status of any items.
-     */
-    boolean flagActionItems();
-
-    /**
-     * Called when a menu item with a collapsible action view should expand its action view.
-     *
-     * @param menu Menu containing the item to be expanded
-     * @param item Item to be expanded
-     * @return true if this presenter expanded the action view, false otherwise.
-     */
-    boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item);
-
-    /**
-     * Called when a menu item with a collapsible action view should collapse its action view.
-     *
-     * @param menu Menu containing the item to be collapsed
-     * @param item Item to be collapsed
-     * @return true if this presenter collapsed the action view, false otherwise.
-     */
-    boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
-
-    /**
-     * Returns an ID for determining how to save/restore instance state.
-     * @return a valid ID value.
-     */
-    int getId();
-
-    /**
-     * Returns a Parcelable describing the current state of the presenter.
-     * It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
-     * method of the presenter sharing the same ID later.
-     * @return The saved instance state
-     */
-    Parcelable onSaveInstanceState();
-
-    /**
-     * Supplies the previously saved instance state to be restored.
-     * @param state The previously saved instance state
-     */
-    void onRestoreInstanceState(Parcelable state);
-}
diff --git a/android/support/v7/view/menu/MenuView.java b/android/support/v7/view/menu/MenuView.java
deleted file mode 100644
index 2d2f027..0000000
--- a/android/support/v7/view/menu/MenuView.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-
-/**
- * Minimal interface for a menu view.  {@link #initialize(MenuBuilder)} must be called for the
- * menu to be functional.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface MenuView {
-    /**
-     * Initializes the menu to the given menu. This should be called after the
-     * view is inflated.
-     *
-     * @param menu The menu that this MenuView should display.
-     */
-    void initialize(MenuBuilder menu);
-
-    /**
-     * Returns the default animations to be used for this menu when entering/exiting.
-     * @return A resource ID for the default animations to be used for this menu.
-     */
-    int getWindowAnimations();
-
-    /**
-     * Minimal interface for a menu item view.  {@link #initialize(MenuItemImpl, int)} must be called
-     * for the item to be functional.
-     */
-    interface ItemView {
-        /**
-         * Initializes with the provided MenuItemData.  This should be called after the view is
-         * inflated.
-         * @param itemData The item that this ItemView should display.
-         * @param menuType The type of this menu, one of
-         *            {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
-         *            {@link MenuBuilder#TYPE_DIALOG}).
-         */
-        void initialize(MenuItemImpl itemData, int menuType);
-
-        /**
-         * Gets the item data that this view is displaying.
-         * @return the item data, or null if there is not one
-         */
-        MenuItemImpl getItemData();
-
-        /**
-         * Sets the title of the item view.
-         * @param title The title to set.
-         */
-        void setTitle(CharSequence title);
-
-        /**
-         * Sets the enabled state of the item view.
-         * @param enabled Whether the item view should be enabled.
-         */
-        void setEnabled(boolean enabled);
-
-        /**
-         * Displays the checkbox for the item view.  This does not ensure the item view will be
-         * checked, for that use {@link #setChecked}.
-         * @param checkable Whether to display the checkbox or to hide it
-         */
-        void setCheckable(boolean checkable);
-
-        /**
-         * Checks the checkbox for the item view.  If the checkbox is hidden, it will NOT be
-         * made visible, call {@link #setCheckable(boolean)} for that.
-         * @param checked Whether the checkbox should be checked
-         */
-        void setChecked(boolean checked);
-
-        /**
-         * Sets the shortcut for the item.
-         * @param showShortcut Whether a shortcut should be shown(if false, the value of
-         * shortcutKey should be ignored).
-         * @param shortcutKey The shortcut key that should be shown on the ItemView.
-         */
-        void setShortcut(boolean showShortcut, char shortcutKey);
-
-        /**
-         * Set the icon of this item view.
-         * @param icon The icon of this item. null to hide the icon.
-         */
-        void setIcon(Drawable icon);
-
-        /**
-         * Whether this item view prefers displaying the condensed title rather
-         * than the normal title. If a condensed title is not available, the
-         * normal title will be used.
-         *
-         * @return Whether this item view prefers displaying the condensed
-         *         title.
-         */
-        boolean prefersCondensedTitle();
-
-        /**
-         * Whether this item view shows an icon.
-         *
-         * @return Whether this item view shows an icon.
-         */
-        boolean showsIcon();
-    }
-}
diff --git a/android/support/v7/view/menu/MenuWrapperFactory.java b/android/support/v7/view/menu/MenuWrapperFactory.java
deleted file mode 100644
index 1839a17..0000000
--- a/android/support/v7/view/menu/MenuWrapperFactory.java
+++ /dev/null
@@ -1,54 +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.view.menu;
-
-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.internal.view.SupportMenu;
-import android.support.v4.internal.view.SupportMenuItem;
-import android.support.v4.internal.view.SupportSubMenu;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class MenuWrapperFactory {
-    private MenuWrapperFactory() {
-    }
-
-    public static Menu wrapSupportMenu(Context context, SupportMenu supportMenu) {
-        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 {
-            return new MenuItemWrapperICS(context, supportMenuItem);
-        }
-    }
-
-    public static SubMenu wrapSupportSubMenu(Context context, SupportSubMenu supportSubMenu) {
-        return new SubMenuWrapperICS(context, supportSubMenu);
-    }
-}
diff --git a/android/support/v7/view/menu/MenuWrapperICS.java b/android/support/v7/view/menu/MenuWrapperICS.java
deleted file mode 100644
index 9672449..0000000
--- a/android/support/v7/view/menu/MenuWrapperICS.java
+++ /dev/null
@@ -1,177 +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.view.menu;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.support.annotation.RequiresApi;
-import android.support.v4.internal.view.SupportMenu;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-
-/**
- * Wraps a support {@link SupportMenu} as a framework {@link android.view.Menu}
- */
-@RequiresApi(14)
-class MenuWrapperICS extends BaseMenuWrapper<SupportMenu> implements Menu {
-
-    MenuWrapperICS(Context context, SupportMenu object) {
-        super(context, object);
-    }
-
-    @Override
-    public MenuItem add(CharSequence title) {
-        return getMenuItemWrapper(mWrappedObject.add(title));
-    }
-
-    @Override
-    public MenuItem add(int titleRes) {
-        return getMenuItemWrapper(mWrappedObject.add(titleRes));
-    }
-
-    @Override
-    public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
-        return getMenuItemWrapper(mWrappedObject.add(groupId, itemId, order, title));
-    }
-
-    @Override
-    public MenuItem add(int groupId, int itemId, int order, int titleRes) {
-        return getMenuItemWrapper(mWrappedObject.add(groupId, itemId, order, titleRes));
-    }
-
-    @Override
-    public SubMenu addSubMenu(CharSequence title) {
-        return getSubMenuWrapper(mWrappedObject.addSubMenu(title));
-    }
-
-    @Override
-    public SubMenu addSubMenu(int titleRes) {
-        return getSubMenuWrapper(mWrappedObject.addSubMenu(titleRes));
-    }
-
-    @Override
-    public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
-        return getSubMenuWrapper(mWrappedObject.addSubMenu(groupId, itemId, order, title));
-    }
-
-    @Override
-    public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
-        return getSubMenuWrapper(
-                mWrappedObject.addSubMenu(groupId, itemId, order, titleRes));
-    }
-
-    @Override
-    public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller,
-            Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
-        android.view.MenuItem[] items = null;
-        if (outSpecificItems != null) {
-            items = new android.view.MenuItem[outSpecificItems.length];
-        }
-
-        int result = mWrappedObject
-                .addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, items);
-
-        if (items != null) {
-            for (int i = 0, z = items.length; i < z; i++) {
-                outSpecificItems[i] = getMenuItemWrapper(items[i]);
-            }
-        }
-
-        return result;
-    }
-
-    @Override
-    public void removeItem(int id) {
-        internalRemoveItem(id);
-        mWrappedObject.removeItem(id);
-    }
-
-    @Override
-    public void removeGroup(int groupId) {
-        internalRemoveGroup(groupId);
-        mWrappedObject.removeGroup(groupId);
-    }
-
-    @Override
-    public void clear() {
-        internalClear();
-        mWrappedObject.clear();
-    }
-
-    @Override
-    public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
-        mWrappedObject.setGroupCheckable(group, checkable, exclusive);
-    }
-
-    @Override
-    public void setGroupVisible(int group, boolean visible) {
-        mWrappedObject.setGroupVisible(group, visible);
-    }
-
-    @Override
-    public void setGroupEnabled(int group, boolean enabled) {
-        mWrappedObject.setGroupEnabled(group, enabled);
-    }
-
-    @Override
-    public boolean hasVisibleItems() {
-        return mWrappedObject.hasVisibleItems();
-    }
-
-    @Override
-    public MenuItem findItem(int id) {
-        return getMenuItemWrapper(mWrappedObject.findItem(id));
-    }
-
-    @Override
-    public int size() {
-        return mWrappedObject.size();
-    }
-
-    @Override
-    public MenuItem getItem(int index) {
-        return getMenuItemWrapper(mWrappedObject.getItem(index));
-    }
-
-    @Override
-    public void close() {
-        mWrappedObject.close();
-    }
-
-    @Override
-    public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
-        return mWrappedObject.performShortcut(keyCode, event, flags);
-    }
-
-    @Override
-    public boolean isShortcutKey(int keyCode, KeyEvent event) {
-        return mWrappedObject.isShortcutKey(keyCode, event);
-    }
-
-    @Override
-    public boolean performIdentifierAction(int id, int flags) {
-        return mWrappedObject.performIdentifierAction(id, flags);
-    }
-
-    @Override
-    public void setQwertyMode(boolean isQwerty) {
-        mWrappedObject.setQwertyMode(isQwerty);
-    }
-}
diff --git a/android/support/v7/view/menu/ShowableListMenu.java b/android/support/v7/view/menu/ShowableListMenu.java
deleted file mode 100644
index 40b7f4e..0000000
--- a/android/support/v7/view/menu/ShowableListMenu.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.v7.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.widget.ListView;
-
-/**
- * A list menu which can be shown and hidden and which is internally represented by a ListView.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface ShowableListMenu {
-    void show();
-
-    void dismiss();
-
-    boolean isShowing();
-
-    /**
-     * @return The internal ListView for the visible menu.
-     */
-    ListView getListView();
-}
\ No newline at end of file
diff --git a/android/support/v7/view/menu/StandardMenuPopup.java b/android/support/v7/view/menu/StandardMenuPopup.java
deleted file mode 100644
index d94ff72..0000000
--- a/android/support/v7/view/menu/StandardMenuPopup.java
+++ /dev/null
@@ -1,349 +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.view.menu;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.os.Parcelable;
-import android.support.v7.appcompat.R;
-import android.support.v7.widget.MenuPopupWindow;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnKeyListener;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.FrameLayout;
-import android.widget.ListView;
-import android.widget.PopupWindow;
-import android.widget.PopupWindow.OnDismissListener;
-import android.widget.TextView;
-
-/**
- * A standard menu popup in which when a submenu is opened, it replaces its parent menu in the
- * viewport.
- */
-final class StandardMenuPopup extends MenuPopup implements OnDismissListener, OnItemClickListener,
-        MenuPresenter, OnKeyListener {
-
-    private final Context mContext;
-
-    private final MenuBuilder mMenu;
-    private final MenuAdapter mAdapter;
-    private final boolean mOverflowOnly;
-    private final int mPopupMaxWidth;
-    private final int mPopupStyleAttr;
-    private final int mPopupStyleRes;
-    // The popup window is final in order to couple its lifecycle to the lifecycle of the
-    // StandardMenuPopup.
-    final MenuPopupWindow mPopup;
-
-    private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
-        @Override
-        public void onGlobalLayout() {
-            // Only move the popup if it's showing and non-modal. We don't want
-            // to be moving around the only interactive window, since there's a
-            // good chance the user is interacting with it.
-            if (isShowing() && !mPopup.isModal()) {
-                final View anchor = mShownAnchorView;
-                if (anchor == null || !anchor.isShown()) {
-                    dismiss();
-                } else {
-                    // Recompute window size and position
-                    mPopup.show();
-                }
-            }
-        }
-    };
-
-    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;
-    View mShownAnchorView;
-    private Callback mPresenterCallback;
-    private ViewTreeObserver mTreeObserver;
-
-    /** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
-    private boolean mWasDismissed;
-
-    /** Whether the cached content width value is valid. */
-    private boolean mHasContentWidth;
-
-    /** Cached content width. */
-    private int mContentWidth;
-
-    private int mDropDownGravity = Gravity.NO_GRAVITY;
-
-    private boolean mShowTitle;
-
-    public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
-            int popupStyleRes, boolean overflowOnly) {
-        mContext = context;
-        mMenu = menu;
-        mOverflowOnly = overflowOnly;
-        final LayoutInflater inflater = LayoutInflater.from(context);
-        mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly);
-        mPopupStyleAttr = popupStyleAttr;
-        mPopupStyleRes = popupStyleRes;
-
-        final Resources res = context.getResources();
-        mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
-                res.getDimensionPixelSize(R.dimen.abc_config_prefDialogWidth));
-
-        mAnchorView = anchorView;
-
-        mPopup = new MenuPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes);
-
-        // Present the menu using our context, not the menu builder's context.
-        menu.addMenuPresenter(this, context);
-    }
-
-    @Override
-    public void setForceShowIcon(boolean forceShow) {
-        mAdapter.setForceShowIcon(forceShow);
-    }
-
-    @Override
-    public void setGravity(int gravity) {
-        mDropDownGravity = gravity;
-    }
-
-    private boolean tryShow() {
-        if (isShowing()) {
-            return true;
-        }
-
-        if (mWasDismissed || mAnchorView == null) {
-            return false;
-        }
-
-        mShownAnchorView = mAnchorView;
-
-        mPopup.setOnDismissListener(this);
-        mPopup.setOnItemClickListener(this);
-        mPopup.setModal(true);
-
-        final View anchor = mShownAnchorView;
-        final boolean addGlobalListener = mTreeObserver == null;
-        mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
-        if (addGlobalListener) {
-            mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
-        }
-        anchor.addOnAttachStateChangeListener(mAttachStateChangeListener);
-        mPopup.setAnchorView(anchor);
-        mPopup.setDropDownGravity(mDropDownGravity);
-
-        if (!mHasContentWidth) {
-            mContentWidth = measureIndividualMenuWidth(mAdapter, null, mContext, mPopupMaxWidth);
-            mHasContentWidth = true;
-        }
-
-        mPopup.setContentWidth(mContentWidth);
-        mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-        mPopup.setEpicenterBounds(getEpicenterBounds());
-        mPopup.show();
-
-        final ListView listView = mPopup.getListView();
-        listView.setOnKeyListener(this);
-
-        if (mShowTitle && mMenu.getHeaderTitle() != null) {
-            FrameLayout titleItemView =
-                    (FrameLayout) LayoutInflater.from(mContext).inflate(
-                            R.layout.abc_popup_menu_header_item_layout, listView, false);
-            TextView titleView = (TextView) titleItemView.findViewById(android.R.id.title);
-            if (titleView != null) {
-                titleView.setText(mMenu.getHeaderTitle());
-            }
-            titleItemView.setEnabled(false);
-            listView.addHeaderView(titleItemView, null, false);
-        }
-
-        // Since addHeaderView() needs to be called before setAdapter() pre-v14, we have to set the
-        // adapter as late as possible, and then call show again to update
-        mPopup.setAdapter(mAdapter);
-        mPopup.show();
-
-        return true;
-    }
-
-    @Override
-    public void show() {
-        if (!tryShow()) {
-            throw new IllegalStateException("StandardMenuPopup cannot be used without an anchor");
-        }
-    }
-
-    @Override
-    public void dismiss() {
-        if (isShowing()) {
-            mPopup.dismiss();
-        }
-    }
-
-    @Override
-    public void addMenu(MenuBuilder menu) {
-        // No-op: standard implementation has only one menu which is set in the constructor.
-    }
-
-    @Override
-    public boolean isShowing() {
-        return !mWasDismissed && mPopup.isShowing();
-    }
-
-    @Override
-    public void onDismiss() {
-        mWasDismissed = true;
-        mMenu.close();
-
-        if (mTreeObserver != null) {
-            if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver();
-            mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
-            mTreeObserver = null;
-        }
-        mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
-
-        if (mOnDismissListener != null) {
-            mOnDismissListener.onDismiss();
-        }
-    }
-
-    @Override
-    public void updateMenuView(boolean cleared) {
-        mHasContentWidth = false;
-
-        if (mAdapter != null) {
-            mAdapter.notifyDataSetChanged();
-        }
-    }
-
-    @Override
-    public void setCallback(Callback cb) {
-        mPresenterCallback = cb;
-    }
-
-    @Override
-    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-        if (subMenu.hasVisibleItems()) {
-            final MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu,
-                    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);
-            mOnDismissListener = null;
-
-            // Close this menu popup to make room for the submenu popup.
-            mMenu.close(false /* closeAllMenus */);
-
-            // Show the new sub-menu popup at the same location as this popup.
-            final int horizontalOffset = mPopup.getHorizontalOffset();
-            final int verticalOffset = mPopup.getVerticalOffset();
-            if (subPopup.tryShow(horizontalOffset, verticalOffset)) {
-                if (mPresenterCallback != null) {
-                    mPresenterCallback.onOpenSubMenu(subMenu);
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        // Only care about the (sub)menu we're presenting.
-        if (menu != mMenu) return;
-
-        dismiss();
-        if (mPresenterCallback != null) {
-            mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
-        }
-    }
-
-    @Override
-    public boolean flagActionItems() {
-        return false;
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        return null;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-    }
-
-    @Override
-    public void setAnchorView(View anchor) {
-        mAnchorView = anchor;
-    }
-
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
-            dismiss();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void setOnDismissListener(OnDismissListener listener) {
-        mOnDismissListener = listener;
-    }
-
-    @Override
-    public ListView getListView() {
-        return mPopup.getListView();
-    }
-
-
-    @Override
-    public void setHorizontalOffset(int x) {
-        mPopup.setHorizontalOffset(x);
-    }
-
-    @Override
-    public void setVerticalOffset(int y) {
-        mPopup.setVerticalOffset(y);
-    }
-
-    @Override
-    public void setShowTitle(boolean showTitle) {
-        mShowTitle = showTitle;
-    }
-}
diff --git a/android/support/v7/view/menu/SubMenuBuilder.java b/android/support/v7/view/menu/SubMenuBuilder.java
deleted file mode 100644
index 94300ed..0000000
--- a/android/support/v7/view/menu/SubMenuBuilder.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RestrictTo;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-
-/**
- * The model for a sub menu, which is an extension of the menu.  Most methods are proxied to the
- * parent menu.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class SubMenuBuilder extends MenuBuilder implements SubMenu {
-    private MenuBuilder mParentMenu;
-    private MenuItemImpl mItem;
-
-    public SubMenuBuilder(Context context, MenuBuilder parentMenu, MenuItemImpl item) {
-        super(context);
-
-        mParentMenu = parentMenu;
-        mItem = item;
-    }
-
-    @Override
-    public void setQwertyMode(boolean isQwerty) {
-        mParentMenu.setQwertyMode(isQwerty);
-    }
-
-    @Override
-    public boolean isQwertyMode() {
-        return mParentMenu.isQwertyMode();
-    }
-
-    @Override
-    public void setShortcutsVisible(boolean shortcutsVisible) {
-        mParentMenu.setShortcutsVisible(shortcutsVisible);
-    }
-
-    @Override
-    public boolean isShortcutsVisible() {
-        return mParentMenu.isShortcutsVisible();
-    }
-
-    public Menu getParentMenu() {
-        return mParentMenu;
-    }
-
-    @Override
-    public MenuItem getItem() {
-        return mItem;
-    }
-
-    @Override
-    public void setCallback(Callback callback) {
-        mParentMenu.setCallback(callback);
-    }
-
-    @Override
-    public MenuBuilder getRootMenu() {
-        return mParentMenu.getRootMenu();
-    }
-
-    @Override
-    boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
-        return super.dispatchMenuItemSelected(menu, item) ||
-                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);
-    }
-
-    @Override
-    public boolean expandItemActionView(MenuItemImpl item) {
-        return mParentMenu.expandItemActionView(item);
-    }
-
-    @Override
-    public boolean collapseItemActionView(MenuItemImpl item) {
-        return mParentMenu.collapseItemActionView(item);
-    }
-
-    @Override
-    public String getActionViewStatesKey() {
-        final int itemId = mItem != null ? mItem.getItemId() : 0;
-        if (itemId == 0) {
-            return null;
-        }
-        return super.getActionViewStatesKey() + ":" + itemId;
-    }
-}
diff --git a/android/support/v7/view/menu/SubMenuWrapperICS.java b/android/support/v7/view/menu/SubMenuWrapperICS.java
deleted file mode 100644
index b7478a9..0000000
--- a/android/support/v7/view/menu/SubMenuWrapperICS.java
+++ /dev/null
@@ -1,98 +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.view.menu;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RestrictTo;
-import android.support.v4.internal.view.SupportSubMenu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-
-/**
- * Wraps a support {@link SupportSubMenu} as a framework {@link android.view.SubMenu}
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-@RequiresApi(14)
-class SubMenuWrapperICS extends MenuWrapperICS implements SubMenu {
-
-    SubMenuWrapperICS(Context context, SupportSubMenu subMenu) {
-        super(context, subMenu);
-    }
-
-    @Override
-    public SupportSubMenu getWrappedObject() {
-        return (SupportSubMenu) mWrappedObject;
-    }
-
-    @Override
-    public SubMenu setHeaderTitle(int titleRes) {
-        getWrappedObject().setHeaderTitle(titleRes);
-        return this;
-    }
-
-    @Override
-    public SubMenu setHeaderTitle(CharSequence title) {
-        getWrappedObject().setHeaderTitle(title);
-        return this;
-    }
-
-    @Override
-    public SubMenu setHeaderIcon(int iconRes) {
-        getWrappedObject().setHeaderIcon(iconRes);
-        return this;
-    }
-
-    @Override
-    public SubMenu setHeaderIcon(Drawable icon) {
-        getWrappedObject().setHeaderIcon(icon);
-        return this;
-    }
-
-    @Override
-    public SubMenu setHeaderView(View view) {
-        getWrappedObject().setHeaderView(view);
-        return this;
-    }
-
-    @Override
-    public void clearHeader() {
-        getWrappedObject().clearHeader();
-    }
-
-    @Override
-    public SubMenu setIcon(int iconRes) {
-        getWrappedObject().setIcon(iconRes);
-        return this;
-    }
-
-    @Override
-    public SubMenu setIcon(Drawable icon) {
-        getWrappedObject().setIcon(icon);
-        return this;
-    }
-
-    @Override
-    public MenuItem getItem() {
-        return getMenuItemWrapper(getWrappedObject().getItem());
-    }
-}
diff --git a/android/support/v7/widget/AbsActionBarView.java b/android/support/v7/widget/AbsActionBarView.java
deleted file mode 100644
index 58a130c..0000000
--- a/android/support/v7/widget/AbsActionBarView.java
+++ /dev/null
@@ -1,302 +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.v7.widget;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-abstract class AbsActionBarView extends ViewGroup {
-    private static final int FADE_DURATION = 200;
-
-    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
-
-    /** Context against which to inflate popup menus. */
-    protected final Context mPopupContext;
-
-    protected ActionMenuView mMenuView;
-    protected ActionMenuPresenter mActionMenuPresenter;
-    protected int mContentHeight;
-
-    protected ViewPropertyAnimatorCompat mVisibilityAnim;
-
-    private boolean mEatingTouch;
-    private boolean mEatingHover;
-
-    AbsActionBarView(Context context) {
-        this(context, null);
-    }
-
-    AbsActionBarView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        final TypedValue tv = new TypedValue();
-        if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true)
-                && tv.resourceId != 0) {
-            mPopupContext = new ContextThemeWrapper(context, tv.resourceId);
-        } else {
-            mPopupContext = context;
-        }
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        // Action bar can change size on configuration changes.
-        // Reread the desired height from the theme-specified style.
-        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar,
-                R.attr.actionBarStyle, 0);
-        setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
-        a.recycle();
-
-        if (mActionMenuPresenter != null) {
-            mActionMenuPresenter.onConfigurationChanged(newConfig);
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        // ActionBarViews always eat touch events, but should still respect the touch event dispatch
-        // contract. If the normal View implementation doesn't want the events, we'll just silently
-        // eat the rest of the gesture without reporting the events to the default implementation
-        // since that's what it expects.
-
-        final int action = ev.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN) {
-            mEatingTouch = false;
-        }
-
-        if (!mEatingTouch) {
-            final boolean handled = super.onTouchEvent(ev);
-            if (action == MotionEvent.ACTION_DOWN && !handled) {
-                mEatingTouch = true;
-            }
-        }
-
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            mEatingTouch = false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent ev) {
-        // Same deal as onTouchEvent() above. Eat all hover events, but still
-        // respect the touch event dispatch contract.
-
-        final int action = ev.getActionMasked();
-        if (action == MotionEvent.ACTION_HOVER_ENTER) {
-            mEatingHover = false;
-        }
-
-        if (!mEatingHover) {
-            final boolean handled = super.onHoverEvent(ev);
-            if (action == MotionEvent.ACTION_HOVER_ENTER && !handled) {
-                mEatingHover = true;
-            }
-        }
-
-        if (action == MotionEvent.ACTION_HOVER_EXIT
-                || action == MotionEvent.ACTION_CANCEL) {
-            mEatingHover = false;
-        }
-
-        return true;
-    }
-
-    public void setContentHeight(int height) {
-        mContentHeight = height;
-        requestLayout();
-    }
-
-    public int getContentHeight() {
-        return mContentHeight;
-    }
-
-    /**
-     * @return Current visibility or if animating, the visibility being animated to.
-     */
-    public int getAnimatedVisibility() {
-        if (mVisibilityAnim != null) {
-            return mVisAnimListener.mFinalVisibility;
-        }
-        return getVisibility();
-    }
-
-    public ViewPropertyAnimatorCompat setupAnimatorToVisibility(int visibility, long duration) {
-        if (mVisibilityAnim != null) {
-            mVisibilityAnim.cancel();
-        }
-
-        if (visibility == VISIBLE) {
-            if (getVisibility() != VISIBLE) {
-                setAlpha(0f);
-            }
-            ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(1f);
-            anim.setDuration(duration);
-            anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
-            return anim;
-        } else {
-            ViewPropertyAnimatorCompat anim = ViewCompat.animate(this).alpha(0f);
-            anim.setDuration(duration);
-            anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
-            return anim;
-        }
-    }
-
-    public void animateToVisibility(int visibility) {
-        ViewPropertyAnimatorCompat anim = setupAnimatorToVisibility(visibility, FADE_DURATION);
-        anim.start();
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        if (visibility != getVisibility()) {
-            if (mVisibilityAnim != null) {
-                mVisibilityAnim.cancel();
-            }
-            super.setVisibility(visibility);
-        }
-    }
-
-    public boolean showOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.showOverflowMenu();
-        }
-        return false;
-    }
-
-    public void postShowOverflowMenu() {
-        post(new Runnable() {
-            @Override
-            public void run() {
-                showOverflowMenu();
-            }
-        });
-    }
-
-    public boolean hideOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.hideOverflowMenu();
-        }
-        return false;
-    }
-
-    public boolean isOverflowMenuShowing() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.isOverflowMenuShowing();
-        }
-        return false;
-    }
-
-    public boolean isOverflowMenuShowPending() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.isOverflowMenuShowPending();
-        }
-        return false;
-    }
-
-    public boolean isOverflowReserved() {
-        return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
-    }
-
-    public boolean canShowOverflowMenu() {
-        return isOverflowReserved() && getVisibility() == VISIBLE;
-    }
-
-    public void dismissPopupMenus() {
-        if (mActionMenuPresenter != null) {
-            mActionMenuPresenter.dismissPopupMenus();
-        }
-    }
-
-    protected int measureChildView(View child, int availableWidth, int childSpecHeight,
-            int spacing) {
-        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
-                childSpecHeight);
-
-        availableWidth -= child.getMeasuredWidth();
-        availableWidth -= spacing;
-
-        return Math.max(0, availableWidth);
-    }
-
-    static protected int next(int x, int val, boolean isRtl) {
-        return isRtl ? x - val : x + val;
-    }
-
-    protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) {
-        int childWidth = child.getMeasuredWidth();
-        int childHeight = child.getMeasuredHeight();
-        int childTop = y + (contentHeight - childHeight) / 2;
-
-        if (reverse) {
-            child.layout(x - childWidth, childTop, x, childTop + childHeight);
-        } else {
-            child.layout(x, childTop, x + childWidth, childTop + childHeight);
-        }
-
-        return  (reverse ? -childWidth : childWidth);
-    }
-
-    protected class VisibilityAnimListener implements ViewPropertyAnimatorListener {
-        private boolean mCanceled = false;
-        int mFinalVisibility;
-
-        public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimatorCompat animation,
-                int visibility) {
-            mVisibilityAnim = animation;
-            mFinalVisibility = visibility;
-            return this;
-        }
-
-        @Override
-        public void onAnimationStart(View view) {
-            AbsActionBarView.super.setVisibility(VISIBLE);
-            mCanceled = false;
-        }
-
-        @Override
-        public void onAnimationEnd(View view) {
-            if (mCanceled) return;
-
-            mVisibilityAnim = null;
-            AbsActionBarView.super.setVisibility(mFinalVisibility);
-        }
-
-        @Override
-        public void onAnimationCancel(View view) {
-            mCanceled = true;
-        }
-    }
-}
diff --git a/android/support/v7/widget/ActionBarBackgroundDrawable.java b/android/support/v7/widget/ActionBarBackgroundDrawable.java
deleted file mode 100644
index 5394c76..0000000
--- a/android/support/v7/widget/ActionBarBackgroundDrawable.java
+++ /dev/null
@@ -1,63 +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.widget;
-
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(9)
-class ActionBarBackgroundDrawable extends Drawable {
-
-    final ActionBarContainer mContainer;
-
-    public ActionBarBackgroundDrawable(ActionBarContainer container) {
-        mContainer = container;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mContainer.mIsSplit) {
-            if (mContainer.mSplitBackground != null) {
-                mContainer.mSplitBackground.draw(canvas);
-            }
-        } else {
-            if (mContainer.mBackground != null) {
-                mContainer.mBackground.draw(canvas);
-            }
-            if (mContainer.mStackedBackground != null && mContainer.mIsStacked) {
-                mContainer.mStackedBackground.draw(canvas);
-            }
-        }
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.UNKNOWN;
-    }
-
-}
diff --git a/android/support/v7/widget/ActionBarBackgroundDrawableV21.java b/android/support/v7/widget/ActionBarBackgroundDrawableV21.java
deleted file mode 100644
index 989fc4c..0000000
--- a/android/support/v7/widget/ActionBarBackgroundDrawableV21.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.v7.widget;
-
-import android.graphics.Outline;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-
-@RequiresApi(21)
-class ActionBarBackgroundDrawableV21 extends ActionBarBackgroundDrawable {
-
-    public ActionBarBackgroundDrawableV21(ActionBarContainer container) {
-        super(container);
-    }
-
-    @Override
-    public void getOutline(@NonNull Outline outline) {
-        if (mContainer.mIsSplit) {
-            if (mContainer.mSplitBackground != null) {
-                mContainer.mSplitBackground.getOutline(outline);
-            }
-        } else {
-            // ignore the stacked background for shadow casting
-            if (mContainer.mBackground != null) {
-                mContainer.mBackground.getOutline(outline);
-            }
-        }
-    }
-}
diff --git a/android/support/v7/widget/ActionBarContainer.java b/android/support/v7/widget/ActionBarContainer.java
deleted file mode 100644
index afe0bb4..0000000
--- a/android/support/v7/widget/ActionBarContainer.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-/**
- * This class acts as a container for the action bar view and action mode context views.
- * It applies special styles as needed to help handle animated transitions between them.
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionBarContainer extends FrameLayout {
-    private boolean mIsTransitioning;
-    private View mTabContainer;
-    private View mActionBarView;
-    private View mContextView;
-
-    Drawable mBackground;
-    Drawable mStackedBackground;
-    Drawable mSplitBackground;
-    boolean mIsSplit;
-    boolean mIsStacked;
-    private int mHeight;
-
-    public ActionBarContainer(Context context) {
-        this(context, null);
-    }
-
-    public ActionBarContainer(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        // Set a transparent background so that we project appropriately.
-        final Drawable bg = Build.VERSION.SDK_INT >= 21
-                ? new ActionBarBackgroundDrawableV21(this)
-                : new ActionBarBackgroundDrawable(this);
-        ViewCompat.setBackground(this, bg);
-
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.ActionBar);
-        mBackground = a.getDrawable(R.styleable.ActionBar_background);
-        mStackedBackground = a.getDrawable(
-                R.styleable.ActionBar_backgroundStacked);
-        mHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, -1);
-
-        if (getId() == R.id.split_action_bar) {
-            mIsSplit = true;
-            mSplitBackground = a.getDrawable(R.styleable.ActionBar_backgroundSplit);
-        }
-        a.recycle();
-
-        setWillNotDraw(mIsSplit ? mSplitBackground == null :
-                mBackground == null && mStackedBackground == null);
-    }
-
-    @Override
-    public void onFinishInflate() {
-        super.onFinishInflate();
-        mActionBarView = findViewById(R.id.action_bar);
-        mContextView = findViewById(R.id.action_context_bar);
-    }
-
-    public void setPrimaryBackground(Drawable bg) {
-        if (mBackground != null) {
-            mBackground.setCallback(null);
-            unscheduleDrawable(mBackground);
-        }
-        mBackground = bg;
-        if (bg != null) {
-            bg.setCallback(this);
-            if (mActionBarView != null) {
-                mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
-                        mActionBarView.getRight(), mActionBarView.getBottom());
-            }
-        }
-        setWillNotDraw(mIsSplit ? mSplitBackground == null :
-                mBackground == null && mStackedBackground == null);
-        invalidate();
-    }
-
-    public void setStackedBackground(Drawable bg) {
-        if (mStackedBackground != null) {
-            mStackedBackground.setCallback(null);
-            unscheduleDrawable(mStackedBackground);
-        }
-        mStackedBackground = bg;
-        if (bg != null) {
-            bg.setCallback(this);
-            if ((mIsStacked && mStackedBackground != null)) {
-                mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
-                        mTabContainer.getRight(), mTabContainer.getBottom());
-            }
-        }
-        setWillNotDraw(mIsSplit ? mSplitBackground == null :
-                mBackground == null && mStackedBackground == null);
-        invalidate();
-    }
-
-    public void setSplitBackground(Drawable bg) {
-        if (mSplitBackground != null) {
-            mSplitBackground.setCallback(null);
-            unscheduleDrawable(mSplitBackground);
-        }
-        mSplitBackground = bg;
-        if (bg != null) {
-            bg.setCallback(this);
-            if (mIsSplit && mSplitBackground != null) {
-                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
-            }
-        }
-        setWillNotDraw(mIsSplit ? mSplitBackground == null :
-                mBackground == null && mStackedBackground == null);
-        invalidate();
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        final boolean isVisible = visibility == VISIBLE;
-        if (mBackground != null) mBackground.setVisible(isVisible, false);
-        if (mStackedBackground != null) mStackedBackground.setVisible(isVisible, false);
-        if (mSplitBackground != null) mSplitBackground.setVisible(isVisible, false);
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
-                (who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackground != null && mBackground.isStateful()) {
-            mBackground.setState(getDrawableState());
-        }
-        if (mStackedBackground != null && mStackedBackground.isStateful()) {
-            mStackedBackground.setState(getDrawableState());
-        }
-        if (mSplitBackground != null && mSplitBackground.isStateful()) {
-            mSplitBackground.setState(getDrawableState());
-        }
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        super.jumpDrawablesToCurrentState();
-        if (mBackground != null) {
-            mBackground.jumpToCurrentState();
-        }
-        if (mStackedBackground != null) {
-            mStackedBackground.jumpToCurrentState();
-        }
-        if (mSplitBackground != null) {
-            mSplitBackground.jumpToCurrentState();
-        }
-    }
-
-    /**
-     * Set the action bar into a "transitioning" state. While transitioning the bar will block focus
-     * and touch from all of its descendants. This prevents the user from interacting with the bar
-     * while it is animating in or out.
-     *
-     * @param isTransitioning true if the bar is currently transitioning, false otherwise.
-     */
-    public void setTransitioning(boolean isTransitioning) {
-        mIsTransitioning = isTransitioning;
-        setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
-                : FOCUS_AFTER_DESCENDANTS);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        return mIsTransitioning || super.onInterceptTouchEvent(ev);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        super.onTouchEvent(ev);
-
-        // An action bar always eats touch events.
-        return true;
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent ev) {
-        super.onHoverEvent(ev);
-
-        // An action bar always eats hover events.
-        return true;
-    }
-
-    public void setTabContainer(ScrollingTabContainerView tabView) {
-        if (mTabContainer != null) {
-            removeView(mTabContainer);
-        }
-        mTabContainer = tabView;
-        if (tabView != null) {
-            addView(tabView);
-            final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
-            lp.width = LayoutParams.MATCH_PARENT;
-            lp.height = LayoutParams.WRAP_CONTENT;
-            tabView.setAllowCollapse(false);
-        }
-    }
-
-    public View getTabContainer() {
-        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) {
-            return super.startActionModeForChild(child, callback, type);
-        }
-        return null;
-    }
-
-    private boolean isCollapsed(View view) {
-        return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0;
-    }
-
-    private int getMeasuredHeightWithMargins(View view) {
-        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        return view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
-    }
-
-    @Override
-    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mActionBarView == null &&
-                MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST && mHeight >= 0) {
-            heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                    Math.min(mHeight, MeasureSpec.getSize(heightMeasureSpec)), MeasureSpec.AT_MOST);
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        if (mActionBarView == null) return;
-
-        final int mode = MeasureSpec.getMode(heightMeasureSpec);
-        if (mTabContainer != null && mTabContainer.getVisibility() != GONE
-                && mode != MeasureSpec.EXACTLY) {
-            final int topMarginForTabs;
-            if (!isCollapsed(mActionBarView)) {
-                topMarginForTabs = getMeasuredHeightWithMargins(mActionBarView);
-            } else if (!isCollapsed(mContextView)) {
-                topMarginForTabs = getMeasuredHeightWithMargins(mContextView);
-            } else {
-                topMarginForTabs = 0;
-            }
-            final int maxHeight = mode == MeasureSpec.AT_MOST ?
-                    MeasureSpec.getSize(heightMeasureSpec) : Integer.MAX_VALUE;
-            setMeasuredDimension(getMeasuredWidth(),
-                    Math.min(topMarginForTabs + getMeasuredHeightWithMargins(mTabContainer),
-                            maxHeight));
-        }
-    }
-
-    @Override
-    public void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-
-        final View tabContainer = mTabContainer;
-        final boolean hasTabs = tabContainer != null && tabContainer.getVisibility() != GONE;
-
-        if (tabContainer != null && tabContainer.getVisibility() != GONE) {
-            final int containerHeight = getMeasuredHeight();
-            final LayoutParams lp = (LayoutParams) tabContainer.getLayoutParams();
-            final int tabHeight = tabContainer.getMeasuredHeight();
-            tabContainer.layout(l, containerHeight - tabHeight - lp.bottomMargin, r,
-                    containerHeight - lp.bottomMargin);
-        }
-
-        boolean needsInvalidate = false;
-        if (mIsSplit) {
-            if (mSplitBackground != null) {
-                mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
-                needsInvalidate = true;
-            }
-        } else {
-            if (mBackground != null) {
-                if (mActionBarView.getVisibility() == View.VISIBLE) {
-                    mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
-                            mActionBarView.getRight(), mActionBarView.getBottom());
-                } else if (mContextView != null &&
-                        mContextView.getVisibility() == View.VISIBLE) {
-                    mBackground.setBounds(mContextView.getLeft(), mContextView.getTop(),
-                            mContextView.getRight(), mContextView.getBottom());
-                } else {
-                    mBackground.setBounds(0, 0, 0, 0);
-                }
-                needsInvalidate = true;
-            }
-            mIsStacked = hasTabs;
-            if (hasTabs && mStackedBackground != null) {
-                mStackedBackground.setBounds(tabContainer.getLeft(), tabContainer.getTop(),
-                        tabContainer.getRight(), tabContainer.getBottom());
-                needsInvalidate = true;
-            }
-        }
-
-        if (needsInvalidate) {
-            invalidate();
-        }
-    }
-}
diff --git a/android/support/v7/widget/ActionBarContextView.java b/android/support/v7/widget/ActionBarContextView.java
deleted file mode 100644
index 35074ef..0000000
--- a/android/support/v7/widget/ActionBarContextView.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.content.Context;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ActionMode;
-import android.support.v7.view.menu.MenuBuilder;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionBarContextView extends AbsActionBarView {
-    private static final String TAG = "ActionBarContextView";
-
-    private CharSequence mTitle;
-    private CharSequence mSubtitle;
-
-    private View mClose;
-    private View mCustomView;
-    private LinearLayout mTitleLayout;
-    private TextView mTitleView;
-    private TextView mSubtitleView;
-    private int mTitleStyleRes;
-    private int mSubtitleStyleRes;
-    private boolean mTitleOptional;
-    private int mCloseItemLayout;
-
-    public ActionBarContextView(Context context) {
-        this(context, null);
-    }
-
-    public ActionBarContextView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.actionModeStyle);
-    }
-
-    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
-                R.styleable.ActionMode, defStyle, 0);
-        ViewCompat.setBackground(this, a.getDrawable(R.styleable.ActionMode_background));
-        mTitleStyleRes = a.getResourceId(
-                R.styleable.ActionMode_titleTextStyle, 0);
-        mSubtitleStyleRes = a.getResourceId(
-                R.styleable.ActionMode_subtitleTextStyle, 0);
-
-        mContentHeight = a.getLayoutDimension(
-                R.styleable.ActionMode_height, 0);
-
-        mCloseItemLayout = a.getResourceId(
-                R.styleable.ActionMode_closeItemLayout,
-                R.layout.abc_action_mode_close_item_material);
-
-        a.recycle();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mActionMenuPresenter != null) {
-            mActionMenuPresenter.hideOverflowMenu();
-            mActionMenuPresenter.hideSubMenus();
-        }
-    }
-
-    @Override
-    public void setContentHeight(int height) {
-        mContentHeight = height;
-    }
-
-    public void setCustomView(View view) {
-        if (mCustomView != null) {
-            removeView(mCustomView);
-        }
-        mCustomView = view;
-        if (view != null && mTitleLayout != null) {
-            removeView(mTitleLayout);
-            mTitleLayout = null;
-        }
-        if (view != null) {
-            addView(view);
-        }
-        requestLayout();
-    }
-
-    public void setTitle(CharSequence title) {
-        mTitle = title;
-        initTitle();
-    }
-
-    public void setSubtitle(CharSequence subtitle) {
-        mSubtitle = subtitle;
-        initTitle();
-    }
-
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    public CharSequence getSubtitle() {
-        return mSubtitle;
-    }
-
-    private void initTitle() {
-        if (mTitleLayout == null) {
-            LayoutInflater inflater = LayoutInflater.from(getContext());
-            inflater.inflate(R.layout.abc_action_bar_title_item, this);
-            mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
-            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
-            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
-            if (mTitleStyleRes != 0) {
-                mTitleView.setTextAppearance(getContext(), mTitleStyleRes);
-            }
-            if (mSubtitleStyleRes != 0) {
-                mSubtitleView.setTextAppearance(getContext(), mSubtitleStyleRes);
-            }
-        }
-
-        mTitleView.setText(mTitle);
-        mSubtitleView.setText(mSubtitle);
-
-        final boolean hasTitle = !TextUtils.isEmpty(mTitle);
-        final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
-        mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
-        mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
-        if (mTitleLayout.getParent() == null) {
-            addView(mTitleLayout);
-        }
-    }
-
-    public void initForMode(final ActionMode mode) {
-        if (mClose == null) {
-            LayoutInflater inflater = LayoutInflater.from(getContext());
-            mClose = inflater.inflate(mCloseItemLayout, this, false);
-            addView(mClose);
-        } else if (mClose.getParent() == null) {
-            addView(mClose);
-        }
-
-        View closeButton = mClose.findViewById(R.id.action_mode_close_button);
-        closeButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mode.finish();
-            }
-        });
-
-        final MenuBuilder menu = (MenuBuilder) mode.getMenu();
-        if (mActionMenuPresenter != null) {
-            mActionMenuPresenter.dismissPopupMenus();
-        }
-        mActionMenuPresenter = new ActionMenuPresenter(getContext());
-        mActionMenuPresenter.setReserveOverflow(true);
-
-        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                LayoutParams.MATCH_PARENT);
-        menu.addMenuPresenter(mActionMenuPresenter, mPopupContext);
-        mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
-        ViewCompat.setBackground(mMenuView, null);
-        addView(mMenuView, layoutParams);
-    }
-
-    public void closeMode() {
-        if (mClose == null) {
-            killMode();
-            return;
-        }
-    }
-
-    public void killMode() {
-        removeAllViews();
-        mCustomView = null;
-        mMenuView = null;
-    }
-
-    @Override
-    public boolean showOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.showOverflowMenu();
-        }
-        return false;
-    }
-
-    @Override
-    public boolean hideOverflowMenu() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.hideOverflowMenu();
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isOverflowMenuShowing() {
-        if (mActionMenuPresenter != null) {
-            return mActionMenuPresenter.isOverflowMenuShowing();
-        }
-        return false;
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
-        // Used by custom views if they don't supply layout params. Everything else
-        // added to an ActionBarContextView should have them already.
-        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new MarginLayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        if (widthMode != MeasureSpec.EXACTLY) {
-            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
-                    "with android:layout_width=\"match_parent\" (or fill_parent)");
-        }
-
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        if (heightMode == MeasureSpec.UNSPECIFIED) {
-            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
-                    "with android:layout_height=\"wrap_content\"");
-        }
-
-        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
-
-        int maxHeight = mContentHeight > 0 ?
-                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
-
-        final int verticalPadding = getPaddingTop() + getPaddingBottom();
-        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
-        final int height = maxHeight - verticalPadding;
-        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
-
-        if (mClose != null) {
-            availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
-            MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
-            availableWidth -= lp.leftMargin + lp.rightMargin;
-        }
-
-        if (mMenuView != null && mMenuView.getParent() == this) {
-            availableWidth = measureChildView(mMenuView, availableWidth,
-                    childSpecHeight, 0);
-        }
-
-        if (mTitleLayout != null && mCustomView == null) {
-            if (mTitleOptional) {
-                final int titleWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-                mTitleLayout.measure(titleWidthSpec, childSpecHeight);
-                final int titleWidth = mTitleLayout.getMeasuredWidth();
-                final boolean titleFits = titleWidth <= availableWidth;
-                if (titleFits) {
-                    availableWidth -= titleWidth;
-                }
-                mTitleLayout.setVisibility(titleFits ? VISIBLE : GONE);
-            } else {
-                availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
-            }
-        }
-
-        if (mCustomView != null) {
-            ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
-            final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
-                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
-            final int customWidth = lp.width >= 0 ?
-                    Math.min(lp.width, availableWidth) : availableWidth;
-            final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
-                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
-            final int customHeight = lp.height >= 0 ?
-                    Math.min(lp.height, height) : height;
-            mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
-                    MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
-        }
-
-        if (mContentHeight <= 0) {
-            int measuredHeight = 0;
-            final int count = getChildCount();
-            for (int i = 0; i < count; i++) {
-                View v = getChildAt(i);
-                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
-                if (paddedViewHeight > measuredHeight) {
-                    measuredHeight = paddedViewHeight;
-                }
-            }
-            setMeasuredDimension(contentWidth, measuredHeight);
-        } else {
-            setMeasuredDimension(contentWidth, maxHeight);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
-        int x = isLayoutRtl ? r - l - getPaddingRight() : getPaddingLeft();
-        final int y = getPaddingTop();
-        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
-
-        if (mClose != null && mClose.getVisibility() != GONE) {
-            MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
-            final int startMargin = (isLayoutRtl ? lp.rightMargin : lp.leftMargin);
-            final int endMargin = (isLayoutRtl ? lp.leftMargin : lp.rightMargin);
-            x = next(x, startMargin, isLayoutRtl);
-            x += positionChild(mClose, x, y, contentHeight, isLayoutRtl);
-            x = next(x, endMargin, isLayoutRtl);
-        }
-
-        if (mTitleLayout != null && mCustomView == null && mTitleLayout.getVisibility() != GONE) {
-            x += positionChild(mTitleLayout, x, y, contentHeight, isLayoutRtl);
-        }
-
-        if (mCustomView != null) {
-            x += positionChild(mCustomView, x, y, contentHeight, isLayoutRtl);
-        }
-
-        x = isLayoutRtl ? getPaddingLeft() : r - l - getPaddingRight();
-
-        if (mMenuView != null) {
-            x += positionChild(mMenuView, x, y, contentHeight, !isLayoutRtl);
-        }
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return false;
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent 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);
-        }
-    }
-
-    public void setTitleOptional(boolean titleOptional) {
-        if (titleOptional != mTitleOptional) {
-            requestLayout();
-        }
-        mTitleOptional = titleOptional;
-    }
-
-    public boolean isTitleOptional() {
-        return mTitleOptional;
-    }
-}
diff --git a/android/support/v7/widget/ActionBarOverlayLayout.java b/android/support/v7/widget/ActionBarOverlayLayout.java
deleted file mode 100644
index b5cdc7a..0000000
--- a/android/support/v7/widget/ActionBarOverlayLayout.java
+++ /dev/null
@@ -1,767 +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.widget;
-
-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;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.NestedScrollingParent;
-import android.support.v4.view.NestedScrollingParentHelper;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.app.AppCompatDelegate;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.menu.MenuPresenter;
-import android.util.AttributeSet;
-import android.util.SparseArray;
-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
- * fitting system windows when the content has request that its layout ignore them.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent,
-        NestedScrollingParent {
-    private static final String TAG = "ActionBarOverlayLayout";
-
-    private int mActionBarHeight;
-    //private WindowDecorActionBar mActionBar;
-    private int mWindowVisibility = View.VISIBLE;
-
-    // The main UI elements that we handle the layout of.
-    private ContentFrameLayout mContent;
-    ActionBarContainer mActionBarTop;
-
-    // Some interior UI elements.
-    private DecorToolbar mDecorToolbar;
-
-    // Content overlay drawable - generally the action bar's shadow
-    private Drawable mWindowContentOverlay;
-    private boolean mIgnoreWindowContentOverlay;
-
-    private boolean mOverlayMode;
-    private boolean mHasNonEmbeddedTabs;
-    private boolean mHideOnContentScroll;
-    boolean mAnimatingForFling;
-    private int mHideOnContentScrollReference;
-    private int mLastSystemUiVisibility;
-    private final Rect mBaseContentInsets = new Rect();
-    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();
-
-    private ActionBarVisibilityCallback mActionBarVisibilityCallback;
-
-    private final int ACTION_BAR_ANIMATE_DELAY = 600; // ms
-
-    private OverScroller mFlingEstimator;
-
-    ViewPropertyAnimator mCurrentActionBarTopAnimator;
-
-    final AnimatorListenerAdapter mTopAnimatorListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            mCurrentActionBarTopAnimator = null;
-            mAnimatingForFling = false;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            mCurrentActionBarTopAnimator = null;
-            mAnimatingForFling = false;
-        }
-    };
-
-    private final Runnable mRemoveActionBarHideOffset = new Runnable() {
-        @Override
-        public void run() {
-            haltActionBarHideOffsetAnimations();
-            mCurrentActionBarTopAnimator = mActionBarTop.animate().translationY(0)
-                    .setListener(mTopAnimatorListener);
-        }
-    };
-
-    private final Runnable mAddActionBarHideOffset = new Runnable() {
-        @Override
-        public void run() {
-            haltActionBarHideOffsetAnimations();
-            mCurrentActionBarTopAnimator = mActionBarTop.animate()
-                    .translationY(-mActionBarTop.getHeight())
-                    .setListener(mTopAnimatorListener);
-        }
-    };
-
-    static final int[] ATTRS = new int [] {
-            R.attr.actionBarSize,
-            android.R.attr.windowContentOverlay
-    };
-
-    private final NestedScrollingParentHelper mParentHelper;
-
-    public ActionBarOverlayLayout(Context context) {
-        this(context, null);
-    }
-
-    public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context);
-
-        mParentHelper = new NestedScrollingParentHelper(this);
-    }
-
-    private void init(Context context) {
-        TypedArray ta = getContext().getTheme().obtainStyledAttributes(ATTRS);
-        mActionBarHeight = ta.getDimensionPixelSize(0, 0);
-        mWindowContentOverlay = ta.getDrawable(1);
-        setWillNotDraw(mWindowContentOverlay == null);
-        ta.recycle();
-
-        mIgnoreWindowContentOverlay = context.getApplicationInfo().targetSdkVersion <
-                Build.VERSION_CODES.KITKAT;
-
-        mFlingEstimator = new OverScroller(context);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        haltActionBarHideOffsetAnimations();
-    }
-
-    public void setActionBarVisibilityCallback(ActionBarVisibilityCallback cb) {
-        mActionBarVisibilityCallback = cb;
-        if (getWindowToken() != null) {
-            // This is being initialized after being added to a window;
-            // make sure to update all state now.
-            mActionBarVisibilityCallback.onWindowVisibilityChanged(mWindowVisibility);
-            if (mLastSystemUiVisibility != 0) {
-                int newVis = mLastSystemUiVisibility;
-                onWindowSystemUiVisibilityChanged(newVis);
-                ViewCompat.requestApplyInsets(this);
-            }
-        }
-    }
-
-    public void setOverlayMode(boolean overlayMode) {
-        mOverlayMode = overlayMode;
-
-        /*
-         * Drawing the window content overlay was broken before K so starting to draw it
-         * again unexpectedly will cause artifacts in some apps. They should fix it.
-         */
-        mIgnoreWindowContentOverlay = overlayMode &&
-                getContext().getApplicationInfo().targetSdkVersion <
-                        Build.VERSION_CODES.KITKAT;
-    }
-
-    public boolean isInOverlayMode() {
-        return mOverlayMode;
-    }
-
-    public void setHasNonEmbeddedTabs(boolean hasNonEmbeddedTabs) {
-        mHasNonEmbeddedTabs = hasNonEmbeddedTabs;
-    }
-
-    public void setShowingForActionMode(boolean showing) {
-        // TODO: Add workaround for this
-//        if (showing) {
-//            // Here's a fun hack: if the status bar is currently being hidden,
-//            // and the application has asked for stable content insets, then
-//            // we will end up with the action mode action bar being shown
-//            // without the status bar, but moved below where the status bar
-//            // would be.  Not nice.  Trying to have this be positioned
-//            // correctly is not easy (basically we need yet *another* content
-//            // inset from the window manager to know where to put it), so
-//            // instead we will just temporarily force the status bar to be shown.
-//            if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-//                    | SYSTEM_UI_FLAG_LAYOUT_STABLE))
-//                    == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
-//                setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
-//            }
-//        } else {
-//            setDisabledSystemUiVisibility(0);
-//        }
-    }
-
-    @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);
-        }
-        pullChildren();
-        final int diff = mLastSystemUiVisibility ^ visible;
-        mLastSystemUiVisibility = visible;
-        final boolean barVisible = (visible & SYSTEM_UI_FLAG_FULLSCREEN) == 0;
-        final boolean stable = (visible & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
-        if (mActionBarVisibilityCallback != null) {
-            // We want the bar to be visible if it is not being hidden,
-            // or the app has not turned on a stable UI mode (meaning they
-            // are performing explicit layout around the action bar).
-            mActionBarVisibilityCallback.enableContentAnimations(!stable);
-            if (barVisible || !stable) mActionBarVisibilityCallback.showForSystem();
-            else mActionBarVisibilityCallback.hideForSystem();
-        }
-        if ((diff & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
-            if (mActionBarVisibilityCallback != null) {
-                ViewCompat.requestApplyInsets(this);
-            }
-        }
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        mWindowVisibility = visibility;
-        if (mActionBarVisibilityCallback != null) {
-            mActionBarVisibilityCallback.onWindowVisibilityChanged(visibility);
-        }
-    }
-
-    private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
-            boolean bottom, boolean right) {
-        boolean changed = false;
-        LayoutParams lp = (LayoutParams)view.getLayoutParams();
-        if (left && lp.leftMargin != insets.left) {
-            changed = true;
-            lp.leftMargin = insets.left;
-        }
-        if (top && lp.topMargin != insets.top) {
-            changed = true;
-            lp.topMargin = insets.top;
-        }
-        if (right && lp.rightMargin != insets.right) {
-            changed = true;
-            lp.rightMargin = insets.right;
-        }
-        if (bottom && lp.bottomMargin != insets.bottom) {
-            changed = true;
-            lp.bottomMargin = insets.bottom;
-        }
-        return changed;
-    }
-
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        pullChildren();
-
-        final int vis = ViewCompat.getWindowSystemUiVisibility(this);
-        final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
-        final Rect systemInsets = insets;
-
-        // The top action bar is always within the content area.
-        boolean changed = applyInsets(mActionBarTop, systemInsets, true, true, false, true);
-
-        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);
-        }
-
-        if (changed) {
-            requestLayout();
-        }
-
-        // We don't do any more at this point.  To correctly compute the content/inner
-        // insets in all cases, we need to know the measured size of the various action
-        // bar elements. fitSystemWindows() happens before the measure pass, so we can't
-        // do that here. Instead we will take this up in onMeasure().
-        return true;
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        return new LayoutParams(p);
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        pullChildren();
-
-        int maxHeight = 0;
-        int maxWidth = 0;
-        int childState = 0;
-
-        int topInset = 0;
-        int bottomInset = 0;
-
-        measureChildWithMargins(mActionBarTop, widthMeasureSpec, 0, heightMeasureSpec, 0);
-        LayoutParams lp = (LayoutParams) mActionBarTop.getLayoutParams();
-        maxWidth = Math.max(maxWidth,
-                mActionBarTop.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
-        maxHeight = Math.max(maxHeight,
-                mActionBarTop.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-        childState = View.combineMeasuredStates(childState, mActionBarTop.getMeasuredState());
-
-        final int vis = ViewCompat.getWindowSystemUiVisibility(this);
-        final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
-
-        if (stable) {
-            // This is the standard space needed for the action bar.  For stable measurement,
-            // we can't depend on the size currently reported by it -- this must remain constant.
-            topInset = mActionBarHeight;
-            if (mHasNonEmbeddedTabs) {
-                final View tabs = mActionBarTop.getTabContainer();
-                if (tabs != null) {
-                    // If tabs are not embedded, increase space on top to account for them.
-                    topInset += mActionBarHeight;
-                }
-            }
-        } else if (mActionBarTop.getVisibility() != GONE) {
-            // This is the space needed on top of the window for all of the action bar
-            // and tabs.
-            topInset = mActionBarTop.getMeasuredHeight();
-        }
-
-        // If the window has not requested system UI layout flags, we need to
-        // make sure its content is not being covered by system UI...  though it
-        // will still be covered by the action bar if they have requested it to
-        // overlay.
-        mContentInsets.set(mBaseContentInsets);
-        mInnerInsets.set(mBaseInnerInsets);
-        if (!mOverlayMode && !stable) {
-            mContentInsets.top += topInset;
-            mContentInsets.bottom += bottomInset;
-        } else {
-            mInnerInsets.top += topInset;
-            mInnerInsets.bottom += bottomInset;
-        }
-        applyInsets(mContent, mContentInsets, true, true, true, true);
-
-        if (!mLastInnerInsets.equals(mInnerInsets)) {
-            // If the inner insets have changed, we need to dispatch this down to
-            // the app's fitSystemWindows().  We do this before measuring the content
-            // view to keep the same semantics as the normal fitSystemWindows() call.
-            mLastInnerInsets.set(mInnerInsets);
-
-            mContent.dispatchFitSystemWindows(mInnerInsets);
-        }
-
-        measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
-        lp = (LayoutParams) mContent.getLayoutParams();
-        maxWidth = Math.max(maxWidth,
-                mContent.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
-        maxHeight = Math.max(maxHeight,
-                mContent.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
-        childState = View.combineMeasuredStates(childState, mContent.getMeasuredState());
-
-        // Account for padding too
-        maxWidth += getPaddingLeft() + getPaddingRight();
-        maxHeight += getPaddingTop() + getPaddingBottom();
-
-        // Check against our minimum height and width
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
-        setMeasuredDimension(
-                View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                View.resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        childState << MEASURED_HEIGHT_STATE_SHIFT));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final int count = getChildCount();
-
-        final int parentLeft = getPaddingLeft();
-        final int parentRight = right - left - getPaddingRight();
-
-        final int parentTop = getPaddingTop();
-        final int parentBottom = bottom - top - getPaddingBottom();
-
-        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 = parentLeft + lp.leftMargin;
-                int childTop = parentTop + lp.topMargin;
-
-                child.layout(childLeft, childTop, childLeft + width, childTop + height);
-            }
-        }
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        super.draw(c);
-        if (mWindowContentOverlay != null && !mIgnoreWindowContentOverlay) {
-            final int top = mActionBarTop.getVisibility() == VISIBLE ?
-                    (int) (mActionBarTop.getBottom() + mActionBarTop.getTranslationY() + 0.5f)
-                    : 0;
-            mWindowContentOverlay.setBounds(0, top, getWidth(),
-                    top + mWindowContentOverlay.getIntrinsicHeight());
-            mWindowContentOverlay.draw(c);
-        }
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return false;
-    }
-
-    @Override
-    public boolean onStartNestedScroll(View child, View target, int axes) {
-        if ((axes & SCROLL_AXIS_VERTICAL) == 0 || mActionBarTop.getVisibility() != VISIBLE) {
-            return false;
-        }
-        return mHideOnContentScroll;
-    }
-
-    @Override
-    public void onNestedScrollAccepted(View child, View target, int axes) {
-        mParentHelper.onNestedScrollAccepted(child, target, axes);
-        mHideOnContentScrollReference = getActionBarHideOffset();
-        haltActionBarHideOffsetAnimations();
-        if (mActionBarVisibilityCallback != null) {
-            mActionBarVisibilityCallback.onContentScrollStarted();
-        }
-    }
-
-    @Override
-    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
-            int dxUnconsumed, int dyUnconsumed) {
-        mHideOnContentScrollReference += dyConsumed;
-        setActionBarHideOffset(mHideOnContentScrollReference);
-    }
-
-    @Override
-    public void onStopNestedScroll(View target) {
-        if (mHideOnContentScroll && !mAnimatingForFling) {
-            if (mHideOnContentScrollReference <= mActionBarTop.getHeight()) {
-                postRemoveActionBarHideOffset();
-            } else {
-                postAddActionBarHideOffset();
-            }
-        }
-        if (mActionBarVisibilityCallback != null) {
-            mActionBarVisibilityCallback.onContentScrollStopped();
-        }
-    }
-
-    @Override
-    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
-        if (!mHideOnContentScroll || !consumed) {
-            return false;
-        }
-        if (shouldHideActionBarOnFling(velocityX, velocityY)) {
-            addActionBarHideOffset();
-        } else {
-            removeActionBarHideOffset();
-        }
-        mAnimatingForFling = true;
-        return true;
-    }
-
-    @Override
-    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
-        // no-op
-    }
-
-    @Override
-    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
-        return false;
-    }
-
-    @Override
-    public int getNestedScrollAxes() {
-        return mParentHelper.getNestedScrollAxes();
-    }
-
-    void pullChildren() {
-        if (mContent == null) {
-            mContent = findViewById(R.id.action_bar_activity_content);
-            mActionBarTop = findViewById(R.id.action_bar_container);
-            mDecorToolbar = getDecorToolbar(findViewById(R.id.action_bar));
-        }
-    }
-
-    private DecorToolbar getDecorToolbar(View view) {
-        if (view instanceof DecorToolbar) {
-            return (DecorToolbar) view;
-        } else if (view instanceof Toolbar) {
-            return ((Toolbar) view).getWrapper();
-        } else {
-            throw new IllegalStateException("Can't make a decor toolbar out of " +
-                    view.getClass().getSimpleName());
-        }
-    }
-
-    public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) {
-        if (hideOnContentScroll != mHideOnContentScroll) {
-            mHideOnContentScroll = hideOnContentScroll;
-            if (!hideOnContentScroll) {
-                haltActionBarHideOffsetAnimations();
-                setActionBarHideOffset(0);
-            }
-        }
-    }
-
-    public boolean isHideOnContentScrollEnabled() {
-        return mHideOnContentScroll;
-    }
-
-    public int getActionBarHideOffset() {
-        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));
-        mActionBarTop.setTranslationY(-offset);
-    }
-
-    void haltActionBarHideOffsetAnimations() {
-        removeCallbacks(mRemoveActionBarHideOffset);
-        removeCallbacks(mAddActionBarHideOffset);
-        if (mCurrentActionBarTopAnimator != null) {
-            mCurrentActionBarTopAnimator.cancel();
-        }
-    }
-
-    private void postRemoveActionBarHideOffset() {
-        haltActionBarHideOffsetAnimations();
-        postDelayed(mRemoveActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
-    }
-
-    private void postAddActionBarHideOffset() {
-        haltActionBarHideOffsetAnimations();
-        postDelayed(mAddActionBarHideOffset, ACTION_BAR_ANIMATE_DELAY);
-    }
-
-    private void removeActionBarHideOffset() {
-        haltActionBarHideOffsetAnimations();
-        mRemoveActionBarHideOffset.run();
-    }
-
-    private void addActionBarHideOffset() {
-        haltActionBarHideOffsetAnimations();
-        mAddActionBarHideOffset.run();
-    }
-
-    private boolean shouldHideActionBarOnFling(float velocityX, float velocityY) {
-        mFlingEstimator.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
-        final int finalY = mFlingEstimator.getFinalY();
-        return finalY > mActionBarTop.getHeight();
-    }
-
-    @Override
-    public void setWindowCallback(Window.Callback cb) {
-        pullChildren();
-        mDecorToolbar.setWindowCallback(cb);
-    }
-
-    @Override
-    public void setWindowTitle(CharSequence title) {
-        pullChildren();
-        mDecorToolbar.setWindowTitle(title);
-    }
-
-    @Override
-    public CharSequence getTitle() {
-        pullChildren();
-        return mDecorToolbar.getTitle();
-    }
-
-    @Override
-    public void initFeature(int windowFeature) {
-        pullChildren();
-        switch (windowFeature) {
-            case Window.FEATURE_PROGRESS:
-                mDecorToolbar.initProgress();
-                break;
-            case Window.FEATURE_INDETERMINATE_PROGRESS:
-                mDecorToolbar.initIndeterminateProgress();
-                break;
-            case AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR_OVERLAY:
-                setOverlayMode(true);
-                break;
-        }
-    }
-
-    @Override
-    public void setUiOptions(int uiOptions) {
-        // Split Action Bar not included.
-    }
-
-    @Override
-    public boolean hasIcon() {
-        pullChildren();
-        return mDecorToolbar.hasIcon();
-    }
-
-    @Override
-    public boolean hasLogo() {
-        pullChildren();
-        return mDecorToolbar.hasLogo();
-    }
-
-    @Override
-    public void setIcon(int resId) {
-        pullChildren();
-        mDecorToolbar.setIcon(resId);
-    }
-
-    @Override
-    public void setIcon(Drawable d) {
-        pullChildren();
-        mDecorToolbar.setIcon(d);
-    }
-
-    @Override
-    public void setLogo(int resId) {
-        pullChildren();
-        mDecorToolbar.setLogo(resId);
-    }
-
-    @Override
-    public boolean canShowOverflowMenu() {
-        pullChildren();
-        return mDecorToolbar.canShowOverflowMenu();
-    }
-
-    @Override
-    public boolean isOverflowMenuShowing() {
-        pullChildren();
-        return mDecorToolbar.isOverflowMenuShowing();
-    }
-
-    @Override
-    public boolean isOverflowMenuShowPending() {
-        pullChildren();
-        return mDecorToolbar.isOverflowMenuShowPending();
-    }
-
-    @Override
-    public boolean showOverflowMenu() {
-        pullChildren();
-        return mDecorToolbar.showOverflowMenu();
-    }
-
-    @Override
-    public boolean hideOverflowMenu() {
-        pullChildren();
-        return mDecorToolbar.hideOverflowMenu();
-    }
-
-    @Override
-    public void setMenuPrepared() {
-        pullChildren();
-        mDecorToolbar.setMenuPrepared();
-    }
-
-    @Override
-    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
-        pullChildren();
-        mDecorToolbar.setMenu(menu, cb);
-    }
-
-    @Override
-    public void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
-        pullChildren();
-        mDecorToolbar.saveHierarchyState(toolbarStates);
-    }
-
-    @Override
-    public void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates) {
-        pullChildren();
-        mDecorToolbar.restoreHierarchyState(toolbarStates);
-    }
-
-    @Override
-    public void dismissPopups() {
-        pullChildren();
-        mDecorToolbar.dismissPopupMenus();
-    }
-
-    public static class LayoutParams extends MarginLayoutParams {
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(ViewGroup.MarginLayoutParams source) {
-            super(source);
-        }
-    }
-
-    public interface ActionBarVisibilityCallback {
-        void onWindowVisibilityChanged(int visibility);
-        void showForSystem();
-        void hideForSystem();
-        void enableContentAnimations(boolean enable);
-        void onContentScrollStarted();
-        void onContentScrollStopped();
-    }
-}
diff --git a/android/support/v7/widget/ActionMenuPresenter.java b/android/support/v7/widget/ActionMenuPresenter.java
deleted file mode 100644
index 8ed599b..0000000
--- a/android/support/v7/widget/ActionMenuPresenter.java
+++ /dev/null
@@ -1,821 +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.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.ActionProvider;
-import android.support.v4.view.GravityCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ActionBarPolicy;
-import android.support.v7.view.menu.ActionMenuItemView;
-import android.support.v7.view.menu.BaseMenuPresenter;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuPopupHelper;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.view.menu.ShowableListMenu;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.util.SparseBooleanArray;
-import android.view.MenuItem;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * MenuPresenter for building action menus as seen in the action bar and action modes.
- */
-class ActionMenuPresenter extends BaseMenuPresenter
-        implements ActionProvider.SubUiVisibilityListener {
-
-    private static final String TAG = "ActionMenuPresenter";
-
-    OverflowMenuButton mOverflowButton;
-    private Drawable mPendingOverflowIcon;
-    private boolean mPendingOverflowIconSet;
-    private boolean mReserveOverflow;
-    private boolean mReserveOverflowSet;
-    private int mWidthLimit;
-    private int mActionItemWidthLimit;
-    private int mMaxItems;
-    private boolean mMaxItemsSet;
-    private boolean mStrictWidthLimit;
-    private boolean mWidthLimitSet;
-    private boolean mExpandedActionViewsExclusive;
-
-    private int mMinCellSize;
-
-    // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
-    private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
-
-    private View mScrapActionButtonView;
-
-    OverflowPopup mOverflowPopup;
-    ActionButtonSubmenu mActionButtonPopup;
-
-    OpenOverflowRunnable mPostedOpenRunnable;
-    private ActionMenuPopupCallback mPopupCallback;
-
-    final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
-    int mOpenSubMenuId;
-
-    public ActionMenuPresenter(Context context) {
-        super(context, R.layout.abc_action_menu_layout, R.layout.abc_action_menu_item_layout);
-    }
-
-    @Override
-    public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
-        super.initForMenu(context, menu);
-
-        final Resources res = context.getResources();
-
-        final ActionBarPolicy abp = ActionBarPolicy.get(context);
-        if (!mReserveOverflowSet) {
-            mReserveOverflow = abp.showsOverflowMenuButton();
-        }
-
-        if (!mWidthLimitSet) {
-            mWidthLimit = abp.getEmbeddedMenuWidthLimit();
-        }
-
-        // Measure for initial configuration
-        if (!mMaxItemsSet) {
-            mMaxItems = abp.getMaxActionButtons();
-        }
-
-        int width = mWidthLimit;
-        if (mReserveOverflow) {
-            if (mOverflowButton == null) {
-                mOverflowButton = new OverflowMenuButton(mSystemContext);
-                if (mPendingOverflowIconSet) {
-                    mOverflowButton.setImageDrawable(mPendingOverflowIcon);
-                    mPendingOverflowIcon = null;
-                    mPendingOverflowIconSet = false;
-                }
-                final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-                mOverflowButton.measure(spec, spec);
-            }
-            width -= mOverflowButton.getMeasuredWidth();
-        } else {
-            mOverflowButton = null;
-        }
-
-        mActionItemWidthLimit = width;
-
-        mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
-
-        // Drop a scrap view as it may no longer reflect the proper context/config.
-        mScrapActionButtonView = null;
-    }
-
-    public void onConfigurationChanged(Configuration newConfig) {
-        if (!mMaxItemsSet) {
-            mMaxItems = ActionBarPolicy.get(mContext).getMaxActionButtons();
-        }
-        if (mMenu != null) {
-            mMenu.onItemsChanged(true);
-        }
-    }
-
-    public void setWidthLimit(int width, boolean strict) {
-        mWidthLimit = width;
-        mStrictWidthLimit = strict;
-        mWidthLimitSet = true;
-    }
-
-    public void setReserveOverflow(boolean reserveOverflow) {
-        mReserveOverflow = reserveOverflow;
-        mReserveOverflowSet = true;
-    }
-
-    public void setItemLimit(int itemCount) {
-        mMaxItems = itemCount;
-        mMaxItemsSet = true;
-    }
-
-    public void setExpandedActionViewsExclusive(boolean isExclusive) {
-        mExpandedActionViewsExclusive = isExclusive;
-    }
-
-    public void setOverflowIcon(Drawable icon) {
-        if (mOverflowButton != null) {
-            mOverflowButton.setImageDrawable(icon);
-        } else {
-            mPendingOverflowIconSet = true;
-            mPendingOverflowIcon = icon;
-        }
-    }
-
-    public Drawable getOverflowIcon() {
-        if (mOverflowButton != null) {
-            return mOverflowButton.getDrawable();
-        } else if (mPendingOverflowIconSet) {
-            return mPendingOverflowIcon;
-        }
-        return null;
-    }
-
-    @Override
-    public MenuView getMenuView(ViewGroup root) {
-        MenuView oldMenuView = mMenuView;
-        MenuView result = super.getMenuView(root);
-        if (oldMenuView != result) {
-            ((ActionMenuView) result).setPresenter(this);
-        }
-        return result;
-    }
-
-    @Override
-    public View getItemView(final MenuItemImpl item, View convertView, ViewGroup parent) {
-        View actionView = item.getActionView();
-        if (actionView == null || item.hasCollapsibleActionView()) {
-            actionView = super.getItemView(item, convertView, parent);
-        }
-        actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
-
-        final ActionMenuView menuParent = (ActionMenuView) parent;
-        final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
-        if (!menuParent.checkLayoutParams(lp)) {
-            actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
-        }
-        return actionView;
-    }
-
-    @Override
-    public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
-        itemView.initialize(item, 0);
-
-        final ActionMenuView menuView = (ActionMenuView) mMenuView;
-        final ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
-        actionItemView.setItemInvoker(menuView);
-
-        if (mPopupCallback == null) {
-            mPopupCallback = new ActionMenuPopupCallback();
-        }
-        actionItemView.setPopupCallback(mPopupCallback);
-    }
-
-    @Override
-    public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
-        return item.isActionButton();
-    }
-
-    @Override
-    public void updateMenuView(boolean cleared) {
-        super.updateMenuView(cleared);
-
-        ((View) mMenuView).requestLayout();
-
-        if (mMenu != null) {
-            final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems();
-            final int count = actionItems.size();
-            for (int i = 0; i < count; i++) {
-                final ActionProvider provider = actionItems.get(i).getSupportActionProvider();
-                if (provider != null) {
-                    provider.setSubUiVisibilityListener(this);
-                }
-            }
-        }
-
-        final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
-                mMenu.getNonActionItems() : null;
-
-        boolean hasOverflow = false;
-        if (mReserveOverflow && nonActionItems != null) {
-            final int count = nonActionItems.size();
-            if (count == 1) {
-                hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
-            } else {
-                hasOverflow = count > 0;
-            }
-        }
-
-        if (hasOverflow) {
-            if (mOverflowButton == null) {
-                mOverflowButton = new OverflowMenuButton(mSystemContext);
-            }
-            ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
-            if (parent != mMenuView) {
-                if (parent != null) {
-                    parent.removeView(mOverflowButton);
-                }
-                ActionMenuView menuView = (ActionMenuView) mMenuView;
-                menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
-            }
-        } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
-            ((ViewGroup) mMenuView).removeView(mOverflowButton);
-        }
-
-        ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
-    }
-
-    @Override
-    public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
-        if (parent.getChildAt(childIndex) == mOverflowButton) return false;
-        return super.filterLeftoverView(parent, childIndex);
-    }
-
-    @Override
-    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-        if (!subMenu.hasVisibleItems()) return false;
-
-        SubMenuBuilder topSubMenu = subMenu;
-        while (topSubMenu.getParentMenu() != mMenu) {
-            topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
-        }
-        View anchor = findViewForItem(topSubMenu.getItem());
-        if (anchor == null) {
-            // This means the submenu was opened from an overflow menu item, indicating the
-            // MenuPopupHelper will handle opening the submenu via its MenuPopup. Return false to
-            // ensure that the MenuPopup acts as presenter for the submenu, and acts on its
-            // responsibility to display the new submenu.
-            return false;
-        }
-
-        mOpenSubMenuId = subMenu.getItem().getItemId();
-
-        boolean preserveIconSpacing = false;
-        final int count = subMenu.size();
-        for (int i = 0; i < count; i++) {
-            MenuItem childItem = subMenu.getItem(i);
-            if (childItem.isVisible() && childItem.getIcon() != null) {
-                preserveIconSpacing = true;
-                break;
-            }
-        }
-
-        mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu, anchor);
-        mActionButtonPopup.setForceShowIcon(preserveIconSpacing);
-        mActionButtonPopup.show();
-
-        super.onSubMenuSelected(subMenu);
-        return true;
-    }
-
-    private View findViewForItem(MenuItem item) {
-        final ViewGroup parent = (ViewGroup) mMenuView;
-        if (parent == null) return null;
-
-        final int count = parent.getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = parent.getChildAt(i);
-            if (child instanceof MenuView.ItemView &&
-                    ((MenuView.ItemView) child).getItemData() == item) {
-                return child;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Display the overflow menu if one is present.
-     * @return true if the overflow menu was shown, false otherwise.
-     */
-    public boolean showOverflowMenu() {
-        if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
-                mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
-            OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
-            mPostedOpenRunnable = new OpenOverflowRunnable(popup);
-            // Post this for later; we might still need a layout for the anchor to be right.
-            ((View) mMenuView).post(mPostedOpenRunnable);
-
-            // ActionMenuPresenter uses null as a callback argument here
-            // to indicate overflow is opening.
-            super.onSubMenuSelected(null);
-
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Hide the overflow menu if it is currently showing.
-     *
-     * @return true if the overflow menu was hidden, false otherwise.
-     */
-    public boolean hideOverflowMenu() {
-        if (mPostedOpenRunnable != null && mMenuView != null) {
-            ((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
-            mPostedOpenRunnable = null;
-            return true;
-        }
-
-        MenuPopupHelper popup = mOverflowPopup;
-        if (popup != null) {
-            popup.dismiss();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Dismiss all popup menus - overflow and submenus.
-     * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
-     */
-    public boolean dismissPopupMenus() {
-        boolean result = hideOverflowMenu();
-        result |= hideSubMenus();
-        return result;
-    }
-
-    /**
-     * Dismiss all submenu popups.
-     *
-     * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
-     */
-    public boolean hideSubMenus() {
-        if (mActionButtonPopup != null) {
-            mActionButtonPopup.dismiss();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return true if the overflow menu is currently showing
-     */
-    public boolean isOverflowMenuShowing() {
-        return mOverflowPopup != null && mOverflowPopup.isShowing();
-    }
-
-    public boolean isOverflowMenuShowPending() {
-        return mPostedOpenRunnable != null || isOverflowMenuShowing();
-    }
-
-    /**
-     * @return true if space has been reserved in the action menu for an overflow item.
-     */
-    public boolean isOverflowReserved() {
-        return mReserveOverflow;
-    }
-
-    @Override
-    public boolean flagActionItems() {
-        final ArrayList<MenuItemImpl> visibleItems;
-        final int itemsSize;
-        if (mMenu != null) {
-            visibleItems = mMenu.getVisibleItems();
-            itemsSize = visibleItems.size();
-        } else {
-            visibleItems = null;
-            itemsSize = 0;
-        }
-
-        int maxActions = mMaxItems;
-        int widthLimit = mActionItemWidthLimit;
-        final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-        final ViewGroup parent = (ViewGroup) mMenuView;
-
-        int requiredItems = 0;
-        int requestedItems = 0;
-        int firstActionWidth = 0;
-        boolean hasOverflow = false;
-        for (int i = 0; i < itemsSize; i++) {
-            MenuItemImpl item = visibleItems.get(i);
-            if (item.requiresActionButton()) {
-                requiredItems++;
-            } else if (item.requestsActionButton()) {
-                requestedItems++;
-            } else {
-                hasOverflow = true;
-            }
-            if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
-                // Overflow everything if we have an expanded action view and we're
-                // space constrained.
-                maxActions = 0;
-            }
-        }
-
-        // Reserve a spot for the overflow item if needed.
-        if (mReserveOverflow &&
-                (hasOverflow || requiredItems + requestedItems > maxActions)) {
-            maxActions--;
-        }
-        maxActions -= requiredItems;
-
-        final SparseBooleanArray seenGroups = mActionButtonGroups;
-        seenGroups.clear();
-
-        int cellSize = 0;
-        int cellsRemaining = 0;
-        if (mStrictWidthLimit) {
-            cellsRemaining = widthLimit / mMinCellSize;
-            final int cellSizeRemaining = widthLimit % mMinCellSize;
-            cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
-        }
-
-        // Flag as many more requested items as will fit.
-        for (int i = 0; i < itemsSize; i++) {
-            MenuItemImpl item = visibleItems.get(i);
-
-            if (item.requiresActionButton()) {
-                View v = getItemView(item, mScrapActionButtonView, parent);
-                if (mScrapActionButtonView == null) {
-                    mScrapActionButtonView = v;
-                }
-                if (mStrictWidthLimit) {
-                    cellsRemaining -= ActionMenuView.measureChildForCells(v,
-                            cellSize, cellsRemaining, querySpec, 0);
-                } else {
-                    v.measure(querySpec, querySpec);
-                }
-                final int measuredWidth = v.getMeasuredWidth();
-                widthLimit -= measuredWidth;
-                if (firstActionWidth == 0) {
-                    firstActionWidth = measuredWidth;
-                }
-                final int groupId = item.getGroupId();
-                if (groupId != 0) {
-                    seenGroups.put(groupId, true);
-                }
-                item.setIsActionButton(true);
-            } else if (item.requestsActionButton()) {
-                // Items in a group with other items that already have an action slot
-                // can break the max actions rule, but not the width limit.
-                final int groupId = item.getGroupId();
-                final boolean inGroup = seenGroups.get(groupId);
-                boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
-                        (!mStrictWidthLimit || cellsRemaining > 0);
-
-                if (isAction) {
-                    View v = getItemView(item, mScrapActionButtonView, parent);
-                    if (mScrapActionButtonView == null) {
-                        mScrapActionButtonView = v;
-                    }
-                    if (mStrictWidthLimit) {
-                        final int cells = ActionMenuView.measureChildForCells(v,
-                                cellSize, cellsRemaining, querySpec, 0);
-                        cellsRemaining -= cells;
-                        if (cells == 0) {
-                            isAction = false;
-                        }
-                    } else {
-                        v.measure(querySpec, querySpec);
-                    }
-                    final int measuredWidth = v.getMeasuredWidth();
-                    widthLimit -= measuredWidth;
-                    if (firstActionWidth == 0) {
-                        firstActionWidth = measuredWidth;
-                    }
-
-                    if (mStrictWidthLimit) {
-                        isAction &= widthLimit >= 0;
-                    } else {
-                        // Did this push the entire first item past the limit?
-                        isAction &= widthLimit + firstActionWidth > 0;
-                    }
-                }
-
-                if (isAction && groupId != 0) {
-                    seenGroups.put(groupId, true);
-                } else if (inGroup) {
-                    // We broke the width limit. Demote the whole group, they all overflow now.
-                    seenGroups.put(groupId, false);
-                    for (int j = 0; j < i; j++) {
-                        MenuItemImpl areYouMyGroupie = visibleItems.get(j);
-                        if (areYouMyGroupie.getGroupId() == groupId) {
-                            // Give back the action slot
-                            if (areYouMyGroupie.isActionButton()) maxActions++;
-                            areYouMyGroupie.setIsActionButton(false);
-                        }
-                    }
-                }
-
-                if (isAction) maxActions--;
-
-                item.setIsActionButton(isAction);
-            } else {
-                // Neither requires nor requests an action button.
-                item.setIsActionButton(false);
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        dismissPopupMenus();
-        super.onCloseMenu(menu, allMenusAreClosing);
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        SavedState state = new SavedState();
-        state.openSubMenuId = mOpenSubMenuId;
-        return state;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            return;
-        }
-
-        SavedState saved = (SavedState) state;
-        if (saved.openSubMenuId > 0) {
-            MenuItem item = mMenu.findItem(saved.openSubMenuId);
-            if (item != null) {
-                SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
-                onSubMenuSelected(subMenu);
-            }
-        }
-    }
-
-    @Override
-    public void onSubUiVisibilityChanged(boolean isVisible) {
-        if (isVisible) {
-            // Not a submenu, but treat it like one.
-            super.onSubMenuSelected(null);
-        } else if (mMenu != null) {
-            mMenu.close(false /* closeAllMenus */);
-        }
-    }
-
-    public void setMenuView(ActionMenuView menuView) {
-        mMenuView = menuView;
-        menuView.initialize(mMenu);
-    }
-
-    private static class SavedState implements Parcelable {
-        public int openSubMenuId;
-
-        SavedState() {
-        }
-
-        SavedState(Parcel in) {
-            openSubMenuId = in.readInt();
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(openSubMenuId);
-        }
-
-        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];
-            }
-        };
-    }
-
-    private class OverflowMenuButton extends AppCompatImageView
-            implements ActionMenuView.ActionMenuChildView {
-        private final float[] mTempPts = new float[2];
-
-        public OverflowMenuButton(Context context) {
-            super(context, null, R.attr.actionOverflowButtonStyle);
-
-            setClickable(true);
-            setFocusable(true);
-            setVisibility(VISIBLE);
-            setEnabled(true);
-
-            TooltipCompat.setTooltipText(this, getContentDescription());
-
-            setOnTouchListener(new ForwardingListener(this) {
-                @Override
-                public ShowableListMenu getPopup() {
-                    if (mOverflowPopup == null) {
-                        return null;
-                    }
-
-                    return mOverflowPopup.getPopup();
-                }
-
-                @Override
-                public boolean onForwardingStarted() {
-                    showOverflowMenu();
-                    return true;
-                }
-
-                @Override
-                public boolean onForwardingStopped() {
-                    // Displaying the popup occurs asynchronously, so wait for
-                    // the runnable to finish before deciding whether to stop
-                    // forwarding.
-                    if (mPostedOpenRunnable != null) {
-                        return false;
-                    }
-
-                    hideOverflowMenu();
-                    return true;
-                }
-            });
-        }
-
-        @Override
-        public boolean performClick() {
-            if (super.performClick()) {
-                return true;
-            }
-
-            playSoundEffect(SoundEffectConstants.CLICK);
-            showOverflowMenu();
-            return true;
-        }
-
-        @Override
-        public boolean needsDividerBefore() {
-            return false;
-        }
-
-        @Override
-        public boolean needsDividerAfter() {
-            return false;
-        }
-
-        @Override
-        protected boolean setFrame(int l, int t, int r, int b) {
-            final boolean changed = super.setFrame(l, t, r, b);
-
-            // Set up the hotspot bounds to be centered on the image.
-            final Drawable d = getDrawable();
-            final Drawable bg = getBackground();
-            if (d != null && bg != null) {
-                final int width = getWidth();
-                final int height = getHeight();
-                final int halfEdge = Math.max(width, height) / 2;
-                final int offsetX = getPaddingLeft() - getPaddingRight();
-                final int offsetY = getPaddingTop() - getPaddingBottom();
-                final int centerX = (width + offsetX) / 2;
-                final int centerY = (height + offsetY) / 2;
-                DrawableCompat.setHotspotBounds(bg, centerX - halfEdge, centerY - halfEdge,
-                        centerX + halfEdge, centerY + halfEdge);
-            }
-
-            return changed;
-        }
-    }
-
-    private class OverflowPopup extends MenuPopupHelper {
-        public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
-                boolean overflowOnly) {
-            super(context, menu, anchorView, overflowOnly, R.attr.actionOverflowMenuStyle);
-            setGravity(GravityCompat.END);
-            setPresenterCallback(mPopupPresenterCallback);
-        }
-
-        @Override
-        protected void onDismiss() {
-            if (mMenu != null) {
-                mMenu.close();
-            }
-            mOverflowPopup = null;
-
-            super.onDismiss();
-        }
-    }
-
-    private class ActionButtonSubmenu extends MenuPopupHelper {
-        public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu, View anchorView) {
-            super(context, subMenu, anchorView, false, R.attr.actionOverflowMenuStyle);
-
-            MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
-            if (!item.isActionButton()) {
-                // Give a reasonable anchor to nested submenus.
-                setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
-            }
-
-            setPresenterCallback(mPopupPresenterCallback);
-        }
-
-        @Override
-        protected void onDismiss() {
-            mActionButtonPopup = null;
-            mOpenSubMenuId = 0;
-
-            super.onDismiss();
-        }
-    }
-
-    private class PopupPresenterCallback implements Callback {
-        PopupPresenterCallback() {
-        }
-
-        @Override
-        public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            if (subMenu == null) return false;
-
-            mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
-            final Callback cb = getCallback();
-            return cb != null ? cb.onOpenSubMenu(subMenu) : false;
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-            if (menu instanceof SubMenuBuilder) {
-                menu.getRootMenu().close(false /* closeAllMenus */);
-            }
-            final Callback cb = getCallback();
-            if (cb != null) {
-                cb.onCloseMenu(menu, allMenusAreClosing);
-            }
-        }
-    }
-
-    private class OpenOverflowRunnable implements Runnable {
-        private OverflowPopup mPopup;
-
-        public OpenOverflowRunnable(OverflowPopup popup) {
-            mPopup = popup;
-        }
-
-        @Override
-        public void run() {
-            if (mMenu != null) {
-                mMenu.changeMenuMode();
-            }
-            final View menuView = (View) mMenuView;
-            if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
-                mOverflowPopup = mPopup;
-            }
-            mPostedOpenRunnable = null;
-        }
-    }
-
-    private class ActionMenuPopupCallback extends ActionMenuItemView.PopupCallback {
-        ActionMenuPopupCallback() {
-        }
-
-        @Override
-        public ShowableListMenu getPopup() {
-            return mActionButtonPopup != null ? mActionButtonPopup.getPopup() : null;
-        }
-    }
-}
diff --git a/android/support/v7/widget/ActionMenuView.java b/android/support/v7/widget/ActionMenuView.java
deleted file mode 100644
index 14723a0..0000000
--- a/android/support/v7/widget/ActionMenuView.java
+++ /dev/null
@@ -1,854 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleRes;
-import android.support.v7.view.menu.ActionMenuItemView;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuPresenter;
-import android.support.v7.view.menu.MenuView;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * ActionMenuView is a presentation of a series of menu options as a View. It provides
- * several top level options as action buttons while spilling remaining options over as
- * items in an overflow menu. This allows applications to present packs of actions inline with
- * specific or repeating content.
- */
-public class ActionMenuView extends LinearLayoutCompat implements MenuBuilder.ItemInvoker,
-        MenuView {
-
-    private static final String TAG = "ActionMenuView";
-
-    static final int MIN_CELL_SIZE = 56; // dips
-    static final int GENERATED_ITEM_PADDING = 4; // dips
-
-    private MenuBuilder mMenu;
-
-    /** Context against which to inflate popup menus. */
-    private Context mPopupContext;
-
-    /** Theme resource against which to inflate popup menus. */
-    private int mPopupTheme;
-
-    private boolean mReserveOverflow;
-    private ActionMenuPresenter mPresenter;
-    private MenuPresenter.Callback mActionMenuPresenterCallback;
-    MenuBuilder.Callback mMenuBuilderCallback;
-    private boolean mFormatItems;
-    private int mFormatItemsWidth;
-    private int mMinCellSize;
-    private int mGeneratedItemPadding;
-
-    OnMenuItemClickListener mOnMenuItemClickListener;
-
-    public ActionMenuView(Context context) {
-        this(context, null);
-    }
-
-    public ActionMenuView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setBaselineAligned(false);
-        final float density = context.getResources().getDisplayMetrics().density;
-        mMinCellSize = (int) (MIN_CELL_SIZE * density);
-        mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
-        mPopupContext = context;
-        mPopupTheme = 0;
-    }
-
-    /**
-     * Specifies the theme to use when inflating popup menus. By default, uses
-     * the same theme as the action menu view itself.
-     *
-     * @param resId theme used to inflate popup menus
-     * @see #getPopupTheme()
-     */
-    public void setPopupTheme(@StyleRes int resId) {
-        if (mPopupTheme != resId) {
-            mPopupTheme = resId;
-            if (resId == 0) {
-                mPopupContext = getContext();
-            } else {
-                mPopupContext = new ContextThemeWrapper(getContext(), resId);
-            }
-        }
-    }
-
-    /**
-     * @return resource identifier of the theme used to inflate popup menus, or
-     *         0 if menus are inflated against the action menu view theme
-     * @see #setPopupTheme(int)
-     */
-    public int getPopupTheme() {
-        return mPopupTheme;
-    }
-
-    /**
-     * @param presenter Menu presenter used to display popup menu
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setPresenter(ActionMenuPresenter presenter) {
-        mPresenter = presenter;
-        mPresenter.setMenuView(this);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        if (mPresenter != null) {
-            mPresenter.updateMenuView(false);
-
-            if (mPresenter.isOverflowMenuShowing()) {
-                mPresenter.hideOverflowMenu();
-                mPresenter.showOverflowMenu();
-            }
-        }
-    }
-
-    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
-        mOnMenuItemClickListener = listener;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // If we've been given an exact size to match, apply special formatting during layout.
-        final boolean wasFormatted = mFormatItems;
-        mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
-
-        if (wasFormatted != mFormatItems) {
-            mFormatItemsWidth = 0; // Reset this when switching modes
-        }
-
-        // Special formatting can change whether items can fit as action buttons.
-        // Kick the menu and update presenters when this changes.
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
-            mFormatItemsWidth = widthSize;
-            mMenu.onItemsChanged(true);
-        }
-
-        final int childCount = getChildCount();
-        if (mFormatItems && childCount > 0) {
-            onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
-        } else {
-            // Previous measurement at exact format may have set margins - reset them.
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                lp.leftMargin = lp.rightMargin = 0;
-            }
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
-        // We already know the width mode is EXACTLY if we're here.
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
-        final int widthPadding = getPaddingLeft() + getPaddingRight();
-        final int heightPadding = getPaddingTop() + getPaddingBottom();
-
-        final int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-
-        widthSize -= widthPadding;
-
-        // Divide the view into cells.
-        final int cellCount = widthSize / mMinCellSize;
-        final int cellSizeRemaining = widthSize % mMinCellSize;
-
-        if (cellCount == 0) {
-            // Give up, nothing fits.
-            setMeasuredDimension(widthSize, 0);
-            return;
-        }
-
-        final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
-
-        int cellsRemaining = cellCount;
-        int maxChildHeight = 0;
-        int maxCellsUsed = 0;
-        int expandableItemCount = 0;
-        int visibleItemCount = 0;
-        boolean hasOverflow = false;
-
-        // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
-        long smallestItemsAt = 0;
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == GONE) continue;
-
-            final boolean isGeneratedItem = child instanceof ActionMenuItemView;
-            visibleItemCount++;
-
-            if (isGeneratedItem) {
-                // Reset padding for generated menu item views; it may change below
-                // and views are recycled.
-                child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
-            }
-
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            lp.expanded = false;
-            lp.extraPixels = 0;
-            lp.cellsUsed = 0;
-            lp.expandable = false;
-            lp.leftMargin = 0;
-            lp.rightMargin = 0;
-            lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
-
-            // Overflow always gets 1 cell. No more, no less.
-            final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
-
-            final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
-                    itemHeightSpec, heightPadding);
-
-            maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
-            if (lp.expandable) expandableItemCount++;
-            if (lp.isOverflowButton) hasOverflow = true;
-
-            cellsRemaining -= cellsUsed;
-            maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
-            if (cellsUsed == 1) smallestItemsAt |= (1 << i);
-        }
-
-        // When we have overflow and a single expanded (text) item, we want to try centering it
-        // visually in the available space even though overflow consumes some of it.
-        final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
-
-        // Divide space for remaining cells if we have items that can expand.
-        // Try distributing whole leftover cells to smaller items first.
-
-        boolean needsExpansion = false;
-        while (expandableItemCount > 0 && cellsRemaining > 0) {
-            int minCells = Integer.MAX_VALUE;
-            long minCellsAt = 0; // Bit locations are indices of relevant child views
-            int minCellsItemCount = 0;
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-                // Don't try to expand items that shouldn't.
-                if (!lp.expandable) continue;
-
-                // Mark indices of children that can receive an extra cell.
-                if (lp.cellsUsed < minCells) {
-                    minCells = lp.cellsUsed;
-                    minCellsAt = 1L << i;
-                    minCellsItemCount = 1;
-                } else if (lp.cellsUsed == minCells) {
-                    minCellsAt |= 1L << i;
-                    minCellsItemCount++;
-                }
-            }
-
-            // Items that get expanded will always be in the set of smallest items when we're done.
-            smallestItemsAt |= minCellsAt;
-
-            if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
-
-            // We have enough cells, all minimum size items will be incremented.
-            minCells++;
-
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if ((minCellsAt & (1 << i)) == 0) {
-                    // If this item is already at our small item count, mark it for later.
-                    if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
-                    continue;
-                }
-
-                if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
-                    // Add padding to this item such that it centers.
-                    child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
-                }
-                lp.cellsUsed++;
-                lp.expanded = true;
-                cellsRemaining--;
-            }
-
-            needsExpansion = true;
-        }
-
-        // Divide any space left that wouldn't divide along cell boundaries
-        // evenly among the smallest items
-
-        final boolean singleItem = !hasOverflow && visibleItemCount == 1;
-        if (cellsRemaining > 0 && smallestItemsAt != 0 &&
-                (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
-            float expandCount = Long.bitCount(smallestItemsAt);
-
-            if (!singleItem) {
-                // The items at the far edges may only expand by half in order to pin to either side.
-                if ((smallestItemsAt & 1) != 0) {
-                    LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
-                    if (!lp.preventEdgeOffset) expandCount -= 0.5f;
-                }
-                if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
-                    LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
-                    if (!lp.preventEdgeOffset) expandCount -= 0.5f;
-                }
-            }
-
-            final int extraPixels = expandCount > 0 ?
-                    (int) (cellsRemaining * cellSize / expandCount) : 0;
-
-            for (int i = 0; i < childCount; i++) {
-                if ((smallestItemsAt & (1 << i)) == 0) continue;
-
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (child instanceof ActionMenuItemView) {
-                    // If this is one of our views, expand and measure at the larger size.
-                    lp.extraPixels = extraPixels;
-                    lp.expanded = true;
-                    if (i == 0 && !lp.preventEdgeOffset) {
-                        // First item gets part of its new padding pushed out of sight.
-                        // The last item will get this implicitly from layout.
-                        lp.leftMargin = -extraPixels / 2;
-                    }
-                    needsExpansion = true;
-                } else if (lp.isOverflowButton) {
-                    lp.extraPixels = extraPixels;
-                    lp.expanded = true;
-                    lp.rightMargin = -extraPixels / 2;
-                    needsExpansion = true;
-                } else {
-                    // If we don't know what it is, give it some margins instead
-                    // and let it center within its space. We still want to pin
-                    // against the edges.
-                    if (i != 0) {
-                        lp.leftMargin = extraPixels / 2;
-                    }
-                    if (i != childCount - 1) {
-                        lp.rightMargin = extraPixels / 2;
-                    }
-                }
-            }
-
-            cellsRemaining = 0;
-        }
-
-        // Remeasure any items that have had extra space allocated to them.
-        if (needsExpansion) {
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-                if (!lp.expanded) continue;
-
-                final int width = lp.cellsUsed * cellSize + lp.extraPixels;
-                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        itemHeightSpec);
-            }
-        }
-
-        if (heightMode != MeasureSpec.EXACTLY) {
-            heightSize = maxChildHeight;
-        }
-
-        setMeasuredDimension(widthSize, heightSize);
-    }
-
-    /**
-     * Measure a child view to fit within cell-based formatting. The child's width
-     * will be measured to a whole multiple of cellSize.
-     *
-     * <p>Sets the expandable and cellsUsed fields of LayoutParams.
-     *
-     * @param child Child to measure
-     * @param cellSize Size of one cell
-     * @param cellsRemaining Number of cells remaining that this view can expand to fill
-     * @param parentHeightMeasureSpec MeasureSpec used by the parent view
-     * @param parentHeightPadding Padding present in the parent view
-     * @return Number of cells this child was measured to occupy
-     */
-    static int measureChildForCells(View child, int cellSize, int cellsRemaining,
-            int parentHeightMeasureSpec, int parentHeightPadding) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-        final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
-                parentHeightPadding;
-        final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
-        final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
-
-        final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
-                (ActionMenuItemView) child : null;
-        final boolean hasText = itemView != null && itemView.hasText();
-
-        int cellsUsed = 0;
-        if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) {
-            final int childWidthSpec = MeasureSpec.makeMeasureSpec(
-                    cellSize * cellsRemaining, MeasureSpec.AT_MOST);
-            child.measure(childWidthSpec, childHeightSpec);
-
-            final int measuredWidth = child.getMeasuredWidth();
-            cellsUsed = measuredWidth / cellSize;
-            if (measuredWidth % cellSize != 0) cellsUsed++;
-            if (hasText && cellsUsed < 2) cellsUsed = 2;
-        }
-
-        final boolean expandable = !lp.isOverflowButton && hasText;
-        lp.expandable = expandable;
-
-        lp.cellsUsed = cellsUsed;
-        final int targetWidth = cellsUsed * cellSize;
-        child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
-                childHeightSpec);
-        return cellsUsed;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (!mFormatItems) {
-            super.onLayout(changed, left, top, right, bottom);
-            return;
-        }
-
-        final int childCount = getChildCount();
-        final int midVertical = (bottom - top) / 2;
-        final int dividerWidth = getDividerWidth();
-        int overflowWidth = 0;
-        int nonOverflowWidth = 0;
-        int nonOverflowCount = 0;
-        int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
-        boolean hasOverflow = false;
-        final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
-        for (int i = 0; i < childCount; i++) {
-            final View v = getChildAt(i);
-            if (v.getVisibility() == GONE) {
-                continue;
-            }
-
-            LayoutParams p = (LayoutParams) v.getLayoutParams();
-            if (p.isOverflowButton) {
-                overflowWidth = v.getMeasuredWidth();
-                if (hasSupportDividerBeforeChildAt(i)) {
-                    overflowWidth += dividerWidth;
-                }
-                int height = v.getMeasuredHeight();
-                int r;
-                int l;
-                if (isLayoutRtl) {
-                    l = getPaddingLeft() + p.leftMargin;
-                    r = l + overflowWidth;
-                } else {
-                    r = getWidth() - getPaddingRight() - p.rightMargin;
-                    l = r - overflowWidth;
-                }
-                int t = midVertical - (height / 2);
-                int b = t + height;
-                v.layout(l, t, r, b);
-
-                widthRemaining -= overflowWidth;
-                hasOverflow = true;
-            } else {
-                final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
-                nonOverflowWidth += size;
-                widthRemaining -= size;
-                if (hasSupportDividerBeforeChildAt(i)) {
-                    nonOverflowWidth += dividerWidth;
-                }
-                nonOverflowCount++;
-            }
-        }
-
-        if (childCount == 1 && !hasOverflow) {
-            // Center a single child
-            final View v = getChildAt(0);
-            final int width = v.getMeasuredWidth();
-            final int height = v.getMeasuredHeight();
-            final int midHorizontal = (right - left) / 2;
-            final int l = midHorizontal - width / 2;
-            final int t = midVertical - height / 2;
-            v.layout(l, t, l + width, t + height);
-            return;
-        }
-
-        final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
-        final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
-
-        if (isLayoutRtl) {
-            int startRight = getWidth() - getPaddingRight();
-            for (int i = 0; i < childCount; i++) {
-                final View v = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-                if (v.getVisibility() == GONE || lp.isOverflowButton) {
-                    continue;
-                }
-
-                startRight -= lp.rightMargin;
-                int width = v.getMeasuredWidth();
-                int height = v.getMeasuredHeight();
-                int t = midVertical - height / 2;
-                v.layout(startRight - width, t, startRight, t + height);
-                startRight -= width + lp.leftMargin + spacerSize;
-            }
-        } else {
-            int startLeft = getPaddingLeft();
-            for (int i = 0; i < childCount; i++) {
-                final View v = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-                if (v.getVisibility() == GONE || lp.isOverflowButton) {
-                    continue;
-                }
-
-                startLeft += lp.leftMargin;
-                int width = v.getMeasuredWidth();
-                int height = v.getMeasuredHeight();
-                int t = midVertical - height / 2;
-                v.layout(startLeft, t, startLeft + width, t + height);
-                startLeft += width + lp.rightMargin + spacerSize;
-            }
-        }
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        dismissPopupMenus();
-    }
-
-    /**
-     * Set the icon to use for the overflow button.
-     *
-     * @param icon Drawable to set, may be null to clear the icon
-     */
-    public void setOverflowIcon(@Nullable Drawable icon) {
-        getMenu();
-        mPresenter.setOverflowIcon(icon);
-    }
-
-    /**
-     * Return the current drawable used as the overflow icon.
-     *
-     * @return The overflow icon drawable
-     */
-    @Nullable
-    public Drawable getOverflowIcon() {
-        getMenu();
-        return mPresenter.getOverflowIcon();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isOverflowReserved() {
-        return mReserveOverflow;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setOverflowReserved(boolean reserveOverflow) {
-        mReserveOverflow = reserveOverflow;
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                LayoutParams.WRAP_CONTENT);
-        params.gravity = Gravity.CENTER_VERTICAL;
-        return params;
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        if (p != null) {
-            final LayoutParams result = p instanceof LayoutParams
-                    ? new LayoutParams((LayoutParams) p)
-                    : new LayoutParams(p);
-            if (result.gravity <= Gravity.NO_GRAVITY) {
-                result.gravity = Gravity.CENTER_VERTICAL;
-            }
-            return result;
-        }
-        return generateDefaultLayoutParams();
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p != null && p instanceof LayoutParams;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public LayoutParams generateOverflowButtonLayoutParams() {
-        LayoutParams result = generateDefaultLayoutParams();
-        result.isOverflowButton = true;
-        return result;
-    }
-
-    /** @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;
-    }
-
-    /**
-     * Returns the Menu object that this ActionMenuView is currently presenting.
-     *
-     * <p>Applications should use this method to obtain the ActionMenuView's Menu object
-     * and inflate or add content to it as necessary.</p>
-     *
-     * @return the Menu presented by this view
-     */
-    public Menu getMenu() {
-        if (mMenu == null) {
-            final Context context = getContext();
-            mMenu = new MenuBuilder(context);
-            mMenu.setCallback(new MenuBuilderCallback());
-            mPresenter = new ActionMenuPresenter(context);
-            mPresenter.setReserveOverflow(true);
-            mPresenter.setCallback(mActionMenuPresenterCallback != null
-                    ? mActionMenuPresenterCallback : new ActionMenuPresenterCallback());
-            mMenu.addMenuPresenter(mPresenter, mPopupContext);
-            mPresenter.setMenuView(this);
-        }
-
-        return mMenu;
-    }
-
-    /**
-     * Must be called before the first call to getMenu()
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
-        mActionMenuPresenterCallback = pcb;
-        mMenuBuilderCallback = mcb;
-    }
-
-    /**
-     * Returns the current menu or null if one has not yet been configured.
-     * @hide Internal use only for action bar integration
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public MenuBuilder peekMenu() {
-        return mMenu;
-    }
-
-    /**
-     * Show the overflow items from the associated menu.
-     *
-     * @return true if the menu was able to be shown, false otherwise
-     */
-    public boolean showOverflowMenu() {
-        return mPresenter != null && mPresenter.showOverflowMenu();
-    }
-
-    /**
-     * Hide the overflow items from the associated menu.
-     *
-     * @return true if the menu was able to be hidden, false otherwise
-     */
-    public boolean hideOverflowMenu() {
-        return mPresenter != null && mPresenter.hideOverflowMenu();
-    }
-
-    /**
-     * Check whether the overflow menu is currently showing. This may not reflect
-     * a pending show operation in progress.
-     *
-     * @return true if the overflow menu is currently showing
-     */
-    public boolean isOverflowMenuShowing() {
-        return mPresenter != null && mPresenter.isOverflowMenuShowing();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isOverflowMenuShowPending() {
-        return mPresenter != null && mPresenter.isOverflowMenuShowPending();
-    }
-
-    /**
-     * Dismiss any popups associated with this menu view.
-     */
-    public void dismissPopupMenus() {
-        if (mPresenter != null) {
-            mPresenter.dismissPopupMenus();
-        }
-    }
-
-    /**
-     * @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    protected boolean hasSupportDividerBeforeChildAt(int childIndex) {
-        if (childIndex == 0) {
-            return false;
-        }
-        final View childBefore = getChildAt(childIndex - 1);
-        final View child = getChildAt(childIndex);
-        boolean result = false;
-        if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
-            result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
-        }
-        if (childIndex > 0 && child instanceof ActionMenuChildView) {
-            result |= ((ActionMenuChildView) child).needsDividerBefore();
-        }
-        return result;
-    }
-
-    @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        return false;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setExpandedActionViewsExclusive(boolean exclusive) {
-        mPresenter.setExpandedActionViewsExclusive(exclusive);
-    }
-
-    /**
-     * Interface responsible for receiving menu item click events if the items themselves
-     * do not have individual item click listeners.
-     */
-    public interface OnMenuItemClickListener {
-        /**
-         * This method will be invoked when a menu item is clicked if the item itself did
-         * not already handle the event.
-         *
-         * @param item {@link MenuItem} that was clicked
-         * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
-         */
-        public boolean onMenuItemClick(MenuItem item);
-    }
-
-    private class MenuBuilderCallback implements MenuBuilder.Callback {
-        MenuBuilderCallback() {
-        }
-
-        @Override
-        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-            return mOnMenuItemClickListener != null &&
-                    mOnMenuItemClickListener.onMenuItemClick(item);
-        }
-
-        @Override
-        public void onMenuModeChange(MenuBuilder menu) {
-            if (mMenuBuilderCallback != null) {
-                mMenuBuilderCallback.onMenuModeChange(menu);
-            }
-        }
-    }
-
-    private static class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback {
-        ActionMenuPresenterCallback() {
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        }
-
-        @Override
-        public boolean onOpenSubMenu(MenuBuilder subMenu) {
-            return false;
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public interface ActionMenuChildView {
-        boolean needsDividerBefore();
-        boolean needsDividerAfter();
-    }
-
-    public static class LayoutParams extends LinearLayoutCompat.LayoutParams {
-
-        @ViewDebug.ExportedProperty()
-        public boolean isOverflowButton;
-
-        @ViewDebug.ExportedProperty()
-        public int cellsUsed;
-
-        @ViewDebug.ExportedProperty()
-        public int extraPixels;
-
-        @ViewDebug.ExportedProperty()
-        public boolean expandable;
-
-        @ViewDebug.ExportedProperty()
-        public boolean preventEdgeOffset;
-
-        boolean expanded;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams other) {
-            super(other);
-        }
-
-        public LayoutParams(LayoutParams other) {
-            super((ViewGroup.LayoutParams) other);
-            isOverflowButton = other.isOverflowButton;
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-            isOverflowButton = false;
-        }
-
-        LayoutParams(int width, int height, boolean isOverflowButton) {
-            super(width, height);
-            this.isOverflowButton = isOverflowButton;
-        }
-    }
-}
diff --git a/android/support/v7/widget/ActivityChooserModel.java b/android/support/v7/widget/ActivityChooserModel.java
deleted file mode 100644
index 698f1a3..0000000
--- a/android/support/v7/widget/ActivityChooserModel.java
+++ /dev/null
@@ -1,1103 +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.v7.widget;
-
-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.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;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * <p>
- * This class represents a data model for choosing a component for handing a
- * given {@link Intent}. The model is responsible for querying the system for
- * activities that can handle the given intent and order found activities
- * based on historical data of previous choices. The historical data is stored
- * in an application private file. If a client does not want to have persistent
- * choice history the file can be omitted, thus the activities will be ordered
- * based on historical usage for the current session.
- * <p>
- * </p>
- * For each backing history file there is a singleton instance of this class. Thus,
- * several clients that specify the same history file will share the same model. Note
- * that if multiple clients are sharing the same model they should implement semantically
- * equivalent functionality since setting the model intent will change the found
- * activities and they may be inconsistent with the functionality of some of the clients.
- * For example, choosing a share activity can be implemented by a single backing
- * model and two different views for performing the selection. If however, one of the
- * views is used for sharing but the other for importing, for example, then each
- * view should be backed by a separate model.
- * </p>
- * <p>
- * The way clients interact with this class is as follows:
- * </p>
- * <p>
- * <pre>
- * <code>
- *  // Get a model and set it to a couple of clients with semantically similar function.
- *  ActivityChooserModel dataModel =
- *      ActivityChooserModel.get(context, "task_specific_history_file_name.xml");
- *
- *  ActivityChooserModelClient modelClient1 = getActivityChooserModelClient1();
- *  modelClient1.setActivityChooserModel(dataModel);
- *
- *  ActivityChooserModelClient modelClient2 = getActivityChooserModelClient2();
- *  modelClient2.setActivityChooserModel(dataModel);
- *
- *  // Set an intent to choose a an activity for.
- *  dataModel.setIntent(intent);
- * <pre>
- * <code>
- * </p>
- * <p>
- * <strong>Note:</strong> This class is thread safe.
- * </p>
- */
-class ActivityChooserModel extends DataSetObservable {
-
-    /**
-     * Client that utilizes an {@link ActivityChooserModel}.
-     */
-    public interface ActivityChooserModelClient {
-
-        /**
-         * Sets the {@link ActivityChooserModel}.
-         *
-         * @param dataModel The model.
-         */
-        public void setActivityChooserModel(ActivityChooserModel dataModel);
-    }
-
-    /**
-     * Defines a sorter that is responsible for sorting the activities
-     * based on the provided historical choices and an intent.
-     */
-    public interface ActivitySorter {
-
-        /**
-         * Sorts the <code>activities</code> in descending order of relevance
-         * based on previous history and an intent.
-         *
-         * @param intent The {@link Intent}.
-         * @param activities Activities to be sorted.
-         * @param historicalRecords Historical records.
-         */
-        // This cannot be done by a simple comparator since an Activity weight
-        // is computed from history. Note that Activity implements Comparable.
-        public void sort(Intent intent, List<ActivityResolveInfo> activities,
-                List<HistoricalRecord> historicalRecords);
-    }
-
-    /**
-     * Listener for choosing an activity.
-     */
-    public interface OnChooseActivityListener {
-
-        /**
-         * Called when an activity has been chosen. The client can decide whether
-         * an activity can be chosen and if so the caller of
-         * {@link ActivityChooserModel#chooseActivity(int)} will receive and {@link Intent}
-         * for launching it.
-         * <p>
-         * <strong>Note:</strong> Modifying the intent is not permitted and
-         *     any changes to the latter will be ignored.
-         * </p>
-         *
-         * @param host The listener's host model.
-         * @param intent The intent for launching the chosen activity.
-         * @return Whether the intent is handled and should not be delivered to clients.
-         *
-         * @see ActivityChooserModel#chooseActivity(int)
-         */
-        public boolean onChooseActivity(ActivityChooserModel host, Intent intent);
-    }
-
-    /**
-     * Flag for selecting debug mode.
-     */
-    static final boolean DEBUG = false;
-
-    /**
-     * Tag used for logging.
-     */
-    static final String LOG_TAG = ActivityChooserModel.class.getSimpleName();
-
-    /**
-     * The root tag in the history file.
-     */
-    static final String TAG_HISTORICAL_RECORDS = "historical-records";
-
-    /**
-     * The tag for a record in the history file.
-     */
-    static final String TAG_HISTORICAL_RECORD = "historical-record";
-
-    /**
-     * Attribute for the activity.
-     */
-    static final String ATTRIBUTE_ACTIVITY = "activity";
-
-    /**
-     * Attribute for the choice time.
-     */
-    static final String ATTRIBUTE_TIME = "time";
-
-    /**
-     * Attribute for the choice weight.
-     */
-    static final String ATTRIBUTE_WEIGHT = "weight";
-
-    /**
-     * The default name of the choice history file.
-     */
-    public static final String DEFAULT_HISTORY_FILE_NAME =
-            "activity_choser_model_history.xml";
-
-    /**
-     * The default maximal length of the choice history.
-     */
-    public static final int DEFAULT_HISTORY_MAX_LENGTH = 50;
-
-    /**
-     * The amount with which to inflate a chosen activity when set as default.
-     */
-    private static final int DEFAULT_ACTIVITY_INFLATION = 5;
-
-    /**
-     * Default weight for a choice record.
-     */
-    private static final float DEFAULT_HISTORICAL_RECORD_WEIGHT = 1.0f;
-
-    /**
-     * The extension of the history file.
-     */
-    private static final String HISTORY_FILE_EXTENSION = ".xml";
-
-    /**
-     * An invalid item index.
-     */
-    private static final int INVALID_INDEX = -1;
-
-    /**
-     * Lock to guard the model registry.
-     */
-    private static final Object sRegistryLock = new Object();
-
-    /**
-     * This the registry for data models.
-     */
-    private static final Map<String, ActivityChooserModel> sDataModelRegistry =
-            new HashMap<String, ActivityChooserModel>();
-
-    /**
-     * Lock for synchronizing on this instance.
-     */
-    private final Object mInstanceLock = new Object();
-
-    /**
-     * List of activities that can handle the current intent.
-     */
-    private final List<ActivityResolveInfo> mActivities = new ArrayList<ActivityResolveInfo>();
-
-    /**
-     * List with historical choice records.
-     */
-    private final List<HistoricalRecord> mHistoricalRecords = new ArrayList<HistoricalRecord>();
-
-    /**
-     * Context for accessing resources.
-     */
-    final Context mContext;
-
-    /**
-     * The name of the history file that backs this model.
-     */
-    final String mHistoryFileName;
-
-    /**
-     * The intent for which a activity is being chosen.
-     */
-    private Intent mIntent;
-
-    /**
-     * The sorter for ordering activities based on intent and past choices.
-     */
-    private ActivitySorter mActivitySorter = new DefaultSorter();
-
-    /**
-     * The maximal length of the choice history.
-     */
-    private int mHistoryMaxSize = DEFAULT_HISTORY_MAX_LENGTH;
-
-    /**
-     * Flag whether choice history can be read. In general many clients can
-     * share the same data model and {@link #readHistoricalDataIfNeeded()} may be called
-     * by arbitrary of them any number of times. Therefore, this class guarantees
-     * that the very first read succeeds and subsequent reads can be performed
-     * only after a call to {@link #persistHistoricalDataIfNeeded()} followed by change
-     * of the share records.
-     */
-    boolean mCanReadHistoricalData = true;
-
-    /**
-     * Flag whether the choice history was read. This is used to enforce that
-     * before calling {@link #persistHistoricalDataIfNeeded()} a call to
-     * {@link #persistHistoricalDataIfNeeded()} has been made. This aims to avoid a
-     * scenario in which a choice history file exits, it is not read yet and
-     * it is overwritten. Note that always all historical records are read in
-     * full and the file is rewritten. This is necessary since we need to
-     * purge old records that are outside of the sliding window of past choices.
-     */
-    private boolean mReadShareHistoryCalled = false;
-
-    /**
-     * Flag whether the choice records have changed. In general many clients can
-     * share the same data model and {@link #persistHistoricalDataIfNeeded()} may be called
-     * by arbitrary of them any number of times. Therefore, this class guarantees
-     * that choice history will be persisted only if it has changed.
-     */
-    private boolean mHistoricalRecordsChanged = true;
-
-    /**
-     * Flag whether to reload the activities for the current intent.
-     */
-    private boolean mReloadActivities = false;
-
-    /**
-     * Policy for controlling how the model handles chosen activities.
-     */
-    private OnChooseActivityListener mActivityChoserModelPolicy;
-
-    /**
-     * Gets the data model backed by the contents of the provided file with historical data.
-     * Note that only one data model is backed by a given file, thus multiple calls with
-     * the same file name will return the same model instance. If no such instance is present
-     * it is created.
-     * <p>
-     * <strong>Note:</strong> To use the default historical data file clients should explicitly
-     * pass as file name {@link #DEFAULT_HISTORY_FILE_NAME}. If no persistence of the choice
-     * history is desired clients should pass <code>null</code> for the file name. In such
-     * case a new model is returned for each invocation.
-     * </p>
-     *
-     * <p>
-     * <strong>Always use difference historical data files for semantically different actions.
-     * For example, sharing is different from importing.</strong>
-     * </p>
-     *
-     * @param context Context for loading resources.
-     * @param historyFileName File name with choice history, <code>null</code>
-     *        if the model should not be backed by a file. In this case the activities
-     *        will be ordered only by data from the current session.
-     *
-     * @return The model.
-     */
-    public static ActivityChooserModel get(Context context, String historyFileName) {
-        synchronized (sRegistryLock) {
-            ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName);
-            if (dataModel == null) {
-                dataModel = new ActivityChooserModel(context, historyFileName);
-                sDataModelRegistry.put(historyFileName, dataModel);
-            }
-            return dataModel;
-        }
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param context Context for loading resources.
-     * @param historyFileName The history XML file.
-     */
-    private ActivityChooserModel(Context context, String historyFileName) {
-        mContext = context.getApplicationContext();
-        if (!TextUtils.isEmpty(historyFileName)
-                && !historyFileName.endsWith(HISTORY_FILE_EXTENSION)) {
-            mHistoryFileName = historyFileName + HISTORY_FILE_EXTENSION;
-        } else {
-            mHistoryFileName = historyFileName;
-        }
-    }
-
-    /**
-     * Sets an intent for which to choose a activity.
-     * <p>
-     * <strong>Note:</strong> Clients must set only semantically similar
-     * intents for each data model.
-     * <p>
-     *
-     * @param intent The intent.
-     */
-    public void setIntent(Intent intent) {
-        synchronized (mInstanceLock) {
-            if (mIntent == intent) {
-                return;
-            }
-            mIntent = intent;
-            mReloadActivities = true;
-            ensureConsistentState();
-        }
-    }
-
-    /**
-     * Gets the intent for which a activity is being chosen.
-     *
-     * @return The intent.
-     */
-    public Intent getIntent() {
-        synchronized (mInstanceLock) {
-            return mIntent;
-        }
-    }
-
-    /**
-     * Gets the number of activities that can handle the intent.
-     *
-     * @return The activity count.
-     *
-     * @see #setIntent(Intent)
-     */
-    public int getActivityCount() {
-        synchronized (mInstanceLock) {
-            ensureConsistentState();
-            return mActivities.size();
-        }
-    }
-
-    /**
-     * Gets an activity at a given index.
-     *
-     * @return The activity.
-     *
-     * @see ActivityResolveInfo
-     * @see #setIntent(Intent)
-     */
-    public ResolveInfo getActivity(int index) {
-        synchronized (mInstanceLock) {
-            ensureConsistentState();
-            return mActivities.get(index).resolveInfo;
-        }
-    }
-
-    /**
-     * Gets the index of a the given activity.
-     *
-     * @param activity The activity index.
-     *
-     * @return The index if found, -1 otherwise.
-     */
-    public int getActivityIndex(ResolveInfo activity) {
-        synchronized (mInstanceLock) {
-            ensureConsistentState();
-            List<ActivityResolveInfo> activities = mActivities;
-            final int activityCount = activities.size();
-            for (int i = 0; i < activityCount; i++) {
-                ActivityResolveInfo currentActivity = activities.get(i);
-                if (currentActivity.resolveInfo == activity) {
-                    return i;
-                }
-            }
-            return INVALID_INDEX;
-        }
-    }
-
-    /**
-     * Chooses a activity to handle the current intent. This will result in
-     * adding a historical record for that action and construct intent with
-     * its component name set such that it can be immediately started by the
-     * client.
-     * <p>
-     * <strong>Note:</strong> By calling this method the client guarantees
-     * that the returned intent will be started. This intent is returned to
-     * the client solely to let additional customization before the start.
-     * </p>
-     *
-     * @return An {@link Intent} for launching the activity or null if the
-     *         policy has consumed the intent or there is not current intent
-     *         set via {@link #setIntent(Intent)}.
-     *
-     * @see HistoricalRecord
-     * @see OnChooseActivityListener
-     */
-    public Intent chooseActivity(int index) {
-        synchronized (mInstanceLock) {
-            if (mIntent == null) {
-                return null;
-            }
-
-            ensureConsistentState();
-
-            ActivityResolveInfo chosenActivity = mActivities.get(index);
-
-            ComponentName chosenName = new ComponentName(
-                    chosenActivity.resolveInfo.activityInfo.packageName,
-                    chosenActivity.resolveInfo.activityInfo.name);
-
-            Intent choiceIntent = new Intent(mIntent);
-            choiceIntent.setComponent(chosenName);
-
-            if (mActivityChoserModelPolicy != null) {
-                // Do not allow the policy to change the intent.
-                Intent choiceIntentCopy = new Intent(choiceIntent);
-                final boolean handled = mActivityChoserModelPolicy.onChooseActivity(this,
-                        choiceIntentCopy);
-                if (handled) {
-                    return null;
-                }
-            }
-
-            HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
-                    System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
-            addHistoricalRecord(historicalRecord);
-
-            return choiceIntent;
-        }
-    }
-
-    /**
-     * Sets the listener for choosing an activity.
-     *
-     * @param listener The listener.
-     */
-    public void setOnChooseActivityListener(OnChooseActivityListener listener) {
-        synchronized (mInstanceLock) {
-            mActivityChoserModelPolicy = listener;
-        }
-    }
-
-    /**
-     * Gets the default activity, The default activity is defined as the one
-     * with highest rank i.e. the first one in the list of activities that can
-     * handle the intent.
-     *
-     * @return The default activity, <code>null</code> id not activities.
-     *
-     * @see #getActivity(int)
-     */
-    public ResolveInfo getDefaultActivity() {
-        synchronized (mInstanceLock) {
-            ensureConsistentState();
-            if (!mActivities.isEmpty()) {
-                return mActivities.get(0).resolveInfo;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Sets the default activity. The default activity is set by adding a
-     * historical record with weight high enough that this activity will
-     * become the highest ranked. Such a strategy guarantees that the default
-     * will eventually change if not used. Also the weight of the record for
-     * setting a default is inflated with a constant amount to guarantee that
-     * it will stay as default for awhile.
-     *
-     * @param index The index of the activity to set as default.
-     */
-    public void setDefaultActivity(int index) {
-        synchronized (mInstanceLock) {
-            ensureConsistentState();
-
-            ActivityResolveInfo newDefaultActivity = mActivities.get(index);
-            ActivityResolveInfo oldDefaultActivity = mActivities.get(0);
-
-            final float weight;
-            if (oldDefaultActivity != null) {
-                // Add a record with weight enough to boost the chosen at the top.
-                weight = oldDefaultActivity.weight - newDefaultActivity.weight
-                        + DEFAULT_ACTIVITY_INFLATION;
-            } else {
-                weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
-            }
-
-            ComponentName defaultName = new ComponentName(
-                    newDefaultActivity.resolveInfo.activityInfo.packageName,
-                    newDefaultActivity.resolveInfo.activityInfo.name);
-            HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
-                    System.currentTimeMillis(), weight);
-            addHistoricalRecord(historicalRecord);
-        }
-    }
-
-    /**
-     * Persists the history data to the backing file if the latter
-     * was provided. Calling this method before a call to {@link #readHistoricalDataIfNeeded()}
-     * throws an exception. Calling this method more than one without choosing an
-     * activity has not effect.
-     *
-     * @throws IllegalStateException If this method is called before a call to
-     *         {@link #readHistoricalDataIfNeeded()}.
-     */
-    private void persistHistoricalDataIfNeeded() {
-        if (!mReadShareHistoryCalled) {
-            throw new IllegalStateException("No preceding call to #readHistoricalData");
-        }
-        if (!mHistoricalRecordsChanged) {
-            return;
-        }
-        mHistoricalRecordsChanged = false;
-        if (!TextUtils.isEmpty(mHistoryFileName)) {
-            new PersistHistoryAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
-                    new ArrayList<HistoricalRecord>(mHistoricalRecords), mHistoryFileName);
-        }
-    }
-
-    /**
-     * Sets the sorter for ordering activities based on historical data and an intent.
-     *
-     * @param activitySorter The sorter.
-     *
-     * @see ActivitySorter
-     */
-    public void setActivitySorter(ActivitySorter activitySorter) {
-        synchronized (mInstanceLock) {
-            if (mActivitySorter == activitySorter) {
-                return;
-            }
-            mActivitySorter = activitySorter;
-            if (sortActivitiesIfNeeded()) {
-                notifyChanged();
-            }
-        }
-    }
-
-    /**
-     * Sets the maximal size of the historical data. Defaults to
-     * {@link #DEFAULT_HISTORY_MAX_LENGTH}
-     * <p>
-     *   <strong>Note:</strong> Setting this property will immediately
-     *   enforce the specified max history size by dropping enough old
-     *   historical records to enforce the desired size. Thus, any
-     *   records that exceed the history size will be discarded and
-     *   irreversibly lost.
-     * </p>
-     *
-     * @param historyMaxSize The max history size.
-     */
-    public void setHistoryMaxSize(int historyMaxSize) {
-        synchronized (mInstanceLock) {
-            if (mHistoryMaxSize == historyMaxSize) {
-                return;
-            }
-            mHistoryMaxSize = historyMaxSize;
-            pruneExcessiveHistoricalRecordsIfNeeded();
-            if (sortActivitiesIfNeeded()) {
-                notifyChanged();
-            }
-        }
-    }
-
-    /**
-     * Gets the history max size.
-     *
-     * @return The history max size.
-     */
-    public int getHistoryMaxSize() {
-        synchronized (mInstanceLock) {
-            return mHistoryMaxSize;
-        }
-    }
-
-    /**
-     * Gets the history size.
-     *
-     * @return The history size.
-     */
-    public int getHistorySize() {
-        synchronized (mInstanceLock) {
-            ensureConsistentState();
-            return mHistoricalRecords.size();
-        }
-    }
-
-    /**
-     * Ensures the model is in a consistent state which is the
-     * activities for the current intent have been loaded, the
-     * most recent history has been read, and the activities
-     * are sorted.
-     */
-    private void ensureConsistentState() {
-        boolean stateChanged = loadActivitiesIfNeeded();
-        stateChanged |= readHistoricalDataIfNeeded();
-        pruneExcessiveHistoricalRecordsIfNeeded();
-        if (stateChanged) {
-            sortActivitiesIfNeeded();
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Sorts the activities if necessary which is if there is a
-     * sorter, there are some activities to sort, and there is some
-     * historical data.
-     *
-     * @return Whether sorting was performed.
-     */
-    private boolean sortActivitiesIfNeeded() {
-        if (mActivitySorter != null && mIntent != null
-                && !mActivities.isEmpty() && !mHistoricalRecords.isEmpty()) {
-            mActivitySorter.sort(mIntent, mActivities,
-                    Collections.unmodifiableList(mHistoricalRecords));
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Loads the activities for the current intent if needed which is
-     * if they are not already loaded for the current intent.
-     *
-     * @return Whether loading was performed.
-     */
-    private boolean loadActivitiesIfNeeded() {
-        if (mReloadActivities && mIntent != null) {
-            mReloadActivities = false;
-            mActivities.clear();
-            List<ResolveInfo> resolveInfos = mContext.getPackageManager()
-                    .queryIntentActivities(mIntent, 0);
-            final int resolveInfoCount = resolveInfos.size();
-            for (int i = 0; i < resolveInfoCount; i++) {
-                ResolveInfo resolveInfo = resolveInfos.get(i);
-                mActivities.add(new ActivityResolveInfo(resolveInfo));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Reads the historical data if necessary which is it has
-     * changed, there is a history file, and there is not persist
-     * in progress.
-     *
-     * @return Whether reading was performed.
-     */
-    private boolean readHistoricalDataIfNeeded() {
-        if (mCanReadHistoricalData && mHistoricalRecordsChanged &&
-                !TextUtils.isEmpty(mHistoryFileName)) {
-            mCanReadHistoricalData = false;
-            mReadShareHistoryCalled = true;
-            readHistoricalDataImpl();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Adds a historical record.
-     *
-     * @param historicalRecord The record to add.
-     * @return True if the record was added.
-     */
-    private boolean addHistoricalRecord(HistoricalRecord historicalRecord) {
-        final boolean added = mHistoricalRecords.add(historicalRecord);
-        if (added) {
-            mHistoricalRecordsChanged = true;
-            pruneExcessiveHistoricalRecordsIfNeeded();
-            persistHistoricalDataIfNeeded();
-            sortActivitiesIfNeeded();
-            notifyChanged();
-        }
-        return added;
-    }
-
-    /**
-     * Prunes older excessive records to guarantee maxHistorySize.
-     */
-    private void pruneExcessiveHistoricalRecordsIfNeeded() {
-        final int pruneCount = mHistoricalRecords.size() - mHistoryMaxSize;
-        if (pruneCount <= 0) {
-            return;
-        }
-        mHistoricalRecordsChanged = true;
-        for (int i = 0; i < pruneCount; i++) {
-            HistoricalRecord prunedRecord = mHistoricalRecords.remove(0);
-            if (DEBUG) {
-                Log.i(LOG_TAG, "Pruned: " + prunedRecord);
-            }
-        }
-    }
-
-    /**
-     * Represents a record in the history.
-     */
-    public final static class HistoricalRecord {
-
-        /**
-         * The activity name.
-         */
-        public final ComponentName activity;
-
-        /**
-         * The choice time.
-         */
-        public final long time;
-
-        /**
-         * The record weight.
-         */
-        public final float weight;
-
-        /**
-         * Creates a new instance.
-         *
-         * @param activityName The activity component name flattened to string.
-         * @param time The time the activity was chosen.
-         * @param weight The weight of the record.
-         */
-        public HistoricalRecord(String activityName, long time, float weight) {
-            this(ComponentName.unflattenFromString(activityName), time, weight);
-        }
-
-        /**
-         * Creates a new instance.
-         *
-         * @param activityName The activity name.
-         * @param time The time the activity was chosen.
-         * @param weight The weight of the record.
-         */
-        public HistoricalRecord(ComponentName activityName, long time, float weight) {
-            this.activity = activityName;
-            this.time = time;
-            this.weight = weight;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((activity == null) ? 0 : activity.hashCode());
-            result = prime * result + (int) (time ^ (time >>> 32));
-            result = prime * result + Float.floatToIntBits(weight);
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            HistoricalRecord other = (HistoricalRecord) obj;
-            if (activity == null) {
-                if (other.activity != null) {
-                    return false;
-                }
-            } else if (!activity.equals(other.activity)) {
-                return false;
-            }
-            if (time != other.time) {
-                return false;
-            }
-            if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
-                return false;
-            }
-            return true;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("[");
-            builder.append("; activity:").append(activity);
-            builder.append("; time:").append(time);
-            builder.append("; weight:").append(new BigDecimal(weight));
-            builder.append("]");
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Represents an activity.
-     */
-    public static final class ActivityResolveInfo implements Comparable<ActivityResolveInfo> {
-
-        /**
-         * The {@link ResolveInfo} of the activity.
-         */
-        public final ResolveInfo resolveInfo;
-
-        /**
-         * Weight of the activity. Useful for sorting.
-         */
-        public float weight;
-
-        /**
-         * Creates a new instance.
-         *
-         * @param resolveInfo activity {@link ResolveInfo}.
-         */
-        public ActivityResolveInfo(ResolveInfo resolveInfo) {
-            this.resolveInfo = resolveInfo;
-        }
-
-        @Override
-        public int hashCode() {
-            return 31 + Float.floatToIntBits(weight);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            ActivityResolveInfo other = (ActivityResolveInfo) obj;
-            if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
-                return false;
-            }
-            return true;
-        }
-
-        @Override
-        public int compareTo(ActivityResolveInfo another) {
-            return  Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("[");
-            builder.append("resolveInfo:").append(resolveInfo.toString());
-            builder.append("; weight:").append(new BigDecimal(weight));
-            builder.append("]");
-            return builder.toString();
-        }
-    }
-
-    /**
-     * Default activity sorter implementation.
-     */
-    private static final class DefaultSorter implements ActivitySorter {
-        private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f;
-
-        private final Map<ComponentName, ActivityResolveInfo> mPackageNameToActivityMap =
-                new HashMap<ComponentName, ActivityResolveInfo>();
-
-        DefaultSorter() {
-        }
-
-        @Override
-        public void sort(Intent intent, List<ActivityResolveInfo> activities,
-                List<HistoricalRecord> historicalRecords) {
-            Map<ComponentName, ActivityResolveInfo> componentNameToActivityMap =
-                    mPackageNameToActivityMap;
-            componentNameToActivityMap.clear();
-
-            final int activityCount = activities.size();
-            for (int i = 0; i < activityCount; i++) {
-                ActivityResolveInfo activity = activities.get(i);
-                activity.weight = 0.0f;
-                ComponentName componentName = new ComponentName(
-                        activity.resolveInfo.activityInfo.packageName,
-                        activity.resolveInfo.activityInfo.name);
-                componentNameToActivityMap.put(componentName, activity);
-            }
-
-            final int lastShareIndex = historicalRecords.size() - 1;
-            float nextRecordWeight = 1;
-            for (int i = lastShareIndex; i >= 0; i--) {
-                HistoricalRecord historicalRecord = historicalRecords.get(i);
-                ComponentName componentName = historicalRecord.activity;
-                ActivityResolveInfo activity = componentNameToActivityMap.get(componentName);
-                if (activity != null) {
-                    activity.weight += historicalRecord.weight * nextRecordWeight;
-                    nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT;
-                }
-            }
-
-            Collections.sort(activities);
-
-            if (DEBUG) {
-                for (int i = 0; i < activityCount; i++) {
-                    Log.i(LOG_TAG, "Sorted: " + activities.get(i));
-                }
-            }
-        }
-    }
-
-    private void readHistoricalDataImpl() {
-        FileInputStream fis = null;
-        try {
-            fis = mContext.openFileInput(mHistoryFileName);
-        } catch (FileNotFoundException fnfe) {
-            if (DEBUG) {
-                Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
-            }
-            return;
-        }
-        try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(fis, "UTF-8");
-
-            int type = XmlPullParser.START_DOCUMENT;
-            while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
-                type = parser.next();
-            }
-
-            if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
-                throw new XmlPullParserException("Share records file does not start with "
-                        + TAG_HISTORICAL_RECORDS + " tag.");
-            }
-
-            List<HistoricalRecord> historicalRecords = mHistoricalRecords;
-            historicalRecords.clear();
-
-            while (true) {
-                type = parser.next();
-                if (type == XmlPullParser.END_DOCUMENT) {
-                    break;
-                }
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                    continue;
-                }
-                String nodeName = parser.getName();
-                if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
-                    throw new XmlPullParserException("Share records file not well-formed.");
-                }
-
-                String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
-                final long time =
-                        Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
-                final float weight =
-                        Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
-                HistoricalRecord readRecord = new HistoricalRecord(activity, time, weight);
-                historicalRecords.add(readRecord);
-
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Read " + readRecord.toString());
-                }
-            }
-
-            if (DEBUG) {
-                Log.i(LOG_TAG, "Read " + historicalRecords.size() + " historical records.");
-            }
-        } catch (XmlPullParserException xppe) {
-            Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
-        } catch (IOException ioe) {
-            Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
-        } finally {
-            if (fis != null) {
-                try {
-                    fis.close();
-                } catch (IOException ioe) {
-                    /* ignore */
-                }
-            }
-        }
-    }
-
-    /**
-     * Command for persisting the historical records to a file off the UI thread.
-     */
-    private final class PersistHistoryAsyncTask extends AsyncTask<Object, Void, Void> {
-
-        PersistHistoryAsyncTask() {
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public Void doInBackground(Object... args) {
-            List<HistoricalRecord> historicalRecords = (List<HistoricalRecord>) args[0];
-            String historyFileName = (String) args[1];
-
-            FileOutputStream fos = null;
-
-            try {
-                fos = mContext.openFileOutput(historyFileName, Context.MODE_PRIVATE);
-            } catch (FileNotFoundException fnfe) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + historyFileName, fnfe);
-                return null;
-            }
-
-            XmlSerializer serializer = Xml.newSerializer();
-
-            try {
-                serializer.setOutput(fos, null);
-                serializer.startDocument("UTF-8", true);
-                serializer.startTag(null, TAG_HISTORICAL_RECORDS);
-
-                final int recordCount = historicalRecords.size();
-                for (int i = 0; i < recordCount; i++) {
-                    HistoricalRecord record = historicalRecords.remove(0);
-                    serializer.startTag(null, TAG_HISTORICAL_RECORD);
-                    serializer.attribute(null, ATTRIBUTE_ACTIVITY,
-                            record.activity.flattenToString());
-                    serializer.attribute(null, ATTRIBUTE_TIME, String.valueOf(record.time));
-                    serializer.attribute(null, ATTRIBUTE_WEIGHT, String.valueOf(record.weight));
-                    serializer.endTag(null, TAG_HISTORICAL_RECORD);
-                    if (DEBUG) {
-                        Log.i(LOG_TAG, "Wrote " + record.toString());
-                    }
-                }
-
-                serializer.endTag(null, TAG_HISTORICAL_RECORDS);
-                serializer.endDocument();
-
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Wrote " + recordCount + " historical records.");
-                }
-            } catch (IllegalArgumentException iae) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, iae);
-            } catch (IllegalStateException ise) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, ise);
-            } catch (IOException ioe) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, ioe);
-            } finally {
-                mCanReadHistoricalData = true;
-                if (fos != null) {
-                    try {
-                        fos.close();
-                    } catch (IOException e) {
-                        /* ignore */
-                    }
-                }
-            }
-            return null;
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/ActivityChooserView.java b/android/support/v7/widget/ActivityChooserView.java
deleted file mode 100644
index ddcb9fc..0000000
--- a/android/support/v7/widget/ActivityChooserView.java
+++ /dev/null
@@ -1,873 +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.v7.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-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.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.menu.ShowableListMenu;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-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;
-import android.widget.ImageView;
-import android.widget.PopupWindow;
-import android.widget.TextView;
-
-/**
- * This class is a view for choosing an activity for handling a given {@link Intent}.
- * <p>
- * The view is composed of two adjacent buttons:
- * <ul>
- * <li>
- * The left button is an immediate action and allows one click activity choosing.
- * Tapping this button immediately executes the intent without requiring any further
- * user input. Long press on this button shows a popup for changing the default
- * activity.
- * </li>
- * <li>
- * The right button is an overflow action and provides an optimized menu
- * of additional activities. Tapping this button shows a popup anchored to this
- * view, listing the most frequently used activities. This list is initially
- * limited to a small number of items in frequency used order. The last item,
- * "Show all..." serves as an affordance to display all available activities.
- * </li>
- * </ul>
- * </p>
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ActivityChooserView extends ViewGroup implements
-        ActivityChooserModel.ActivityChooserModelClient {
-
-    private static final String LOG_TAG = "ActivityChooserView";
-
-    /**
-     * An adapter for displaying the activities in an {@link android.widget.AdapterView}.
-     */
-    final ActivityChooserViewAdapter mAdapter;
-
-    /**
-     * Implementation of various interfaces to avoid publishing them in the APIs.
-     */
-    private final Callbacks mCallbacks;
-
-    /**
-     * The content of this view.
-     */
-    private final LinearLayoutCompat mActivityChooserContent;
-
-    /**
-     * Stores the background drawable to allow hiding and latter showing.
-     */
-    private final Drawable mActivityChooserContentBackground;
-
-    /**
-     * The expand activities action button;
-     */
-    final FrameLayout mExpandActivityOverflowButton;
-
-    /**
-     * The image for the expand activities action button;
-     */
-    private final ImageView mExpandActivityOverflowButtonImage;
-
-    /**
-     * The default activities action button;
-     */
-    final FrameLayout mDefaultActivityButton;
-
-    /**
-     * The image for the default activities action button;
-     */
-    private final ImageView mDefaultActivityButtonImage;
-
-    /**
-     * The maximal width of the list popup.
-     */
-    private final int mListPopupMaxWidth;
-
-    /**
-     * The ActionProvider hosting this view, if applicable.
-     */
-    ActionProvider mProvider;
-
-    /**
-     * Observer for the model data.
-     */
-    final DataSetObserver mModelDataSetObserver = new DataSetObserver() {
-
-        @Override
-        public void onChanged() {
-            super.onChanged();
-            mAdapter.notifyDataSetChanged();
-        }
-        @Override
-        public void onInvalidated() {
-            super.onInvalidated();
-            mAdapter.notifyDataSetInvalidated();
-        }
-    };
-
-    private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
-        @Override
-        public void onGlobalLayout() {
-            if (isShowingPopup()) {
-                if (!isShown()) {
-                    getListPopupWindow().dismiss();
-                } else {
-                    getListPopupWindow().show();
-                    if (mProvider != null) {
-                        mProvider.subUiVisibilityChanged(true);
-                    }
-                }
-            }
-        }
-    };
-
-    /**
-     * Popup window for showing the activity overflow list.
-     */
-    private ListPopupWindow mListPopupWindow;
-
-    /**
-     * Listener for the dismissal of the popup/alert.
-     */
-    PopupWindow.OnDismissListener mOnDismissListener;
-
-    /**
-     * Flag whether a default activity currently being selected.
-     */
-    boolean mIsSelectingDefaultActivity;
-
-    /**
-     * The count of activities in the popup.
-     */
-    int mInitialActivityCount = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT;
-
-    /**
-     * Flag whether this view is attached to a window.
-     */
-    private boolean mIsAttachedToWindow;
-
-    /**
-     * String resource for formatting content description of the default target.
-     */
-    private int mDefaultActionButtonContentDescription;
-
-    /**
-     * Create a new instance.
-     *
-     * @param context The application environment.
-     */
-    public ActivityChooserView(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Create a new instance.
-     *
-     * @param context The application environment.
-     * @param attrs A collection of attributes.
-     */
-    public ActivityChooserView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * Create a new instance.
-     *
-     * @param context The application environment.
-     * @param attrs A collection of attributes.
-     * @param defStyle The default style to apply to this view.
-     */
-    public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        TypedArray attributesArray = context.obtainStyledAttributes(attrs,
-                R.styleable.ActivityChooserView, defStyle, 0);
-
-        mInitialActivityCount = attributesArray.getInt(
-                R.styleable.ActivityChooserView_initialActivityCount,
-                ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT);
-
-        Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable(
-                R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable);
-
-        attributesArray.recycle();
-
-        LayoutInflater inflater = LayoutInflater.from(getContext());
-        inflater.inflate(R.layout.abc_activity_chooser_view, this, true);
-
-        mCallbacks = new Callbacks();
-
-        mActivityChooserContent = findViewById(R.id.activity_chooser_view_content);
-        mActivityChooserContentBackground = mActivityChooserContent.getBackground();
-
-        mDefaultActivityButton = findViewById(R.id.default_activity_button);
-        mDefaultActivityButton.setOnClickListener(mCallbacks);
-        mDefaultActivityButton.setOnLongClickListener(mCallbacks);
-        mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
-
-        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() {
-                return getListPopupWindow();
-            }
-
-            @Override
-            protected boolean onForwardingStarted() {
-                showPopup();
-                return true;
-            }
-
-            @Override
-            protected boolean onForwardingStopped() {
-                dismissPopup();
-                return true;
-            }
-        });
-        mExpandActivityOverflowButton = expandButton;
-        mExpandActivityOverflowButtonImage =
-            (ImageView) expandButton.findViewById(R.id.image);
-        mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
-
-        mAdapter = new ActivityChooserViewAdapter();
-        mAdapter.registerDataSetObserver(new DataSetObserver() {
-            @Override
-            public void onChanged() {
-                super.onChanged();
-                updateAppearance();
-            }
-        });
-
-        Resources resources = context.getResources();
-        mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2,
-                resources.getDimensionPixelSize(R.dimen.abc_config_prefDialogWidth));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setActivityChooserModel(ActivityChooserModel dataModel) {
-        mAdapter.setDataModel(dataModel);
-        if (isShowingPopup()) {
-            dismissPopup();
-            showPopup();
-        }
-    }
-
-    /**
-     * Sets the background for the button that expands the activity
-     * overflow list.
-     *
-     * <strong>Note:</strong> Clients would like to set this drawable
-     * as a clue about the action the chosen activity will perform. For
-     * example, if a share activity is to be chosen the drawable should
-     * give a clue that sharing is to be performed.
-     *
-     * @param drawable The drawable.
-     */
-    public void setExpandActivityOverflowButtonDrawable(Drawable drawable) {
-        mExpandActivityOverflowButtonImage.setImageDrawable(drawable);
-    }
-
-    /**
-     * Sets the content description for the button that expands the activity
-     * overflow list.
-     *
-     * description as a clue about the action performed by the button.
-     * For example, if a share activity is to be chosen the content
-     * description should be something like "Share with".
-     *
-     * @param resourceId The content description resource id.
-     */
-    public void setExpandActivityOverflowButtonContentDescription(int resourceId) {
-        CharSequence contentDescription = getContext().getString(resourceId);
-        mExpandActivityOverflowButtonImage.setContentDescription(contentDescription);
-    }
-
-    /**
-     * Set the provider hosting this view, if applicable.
-     * @hide Internal use only
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setProvider(ActionProvider provider) {
-        mProvider = provider;
-    }
-
-    /**
-     * Shows the popup window with activities.
-     *
-     * @return True if the popup was shown, false if already showing.
-     */
-    public boolean showPopup() {
-        if (isShowingPopup() || !mIsAttachedToWindow) {
-            return false;
-        }
-        mIsSelectingDefaultActivity = false;
-        showPopupUnchecked(mInitialActivityCount);
-        return true;
-    }
-
-    /**
-     * Shows the popup no matter if it was already showing.
-     *
-     * @param maxActivityCount The max number of activities to display.
-     */
-    void showPopupUnchecked(int maxActivityCount) {
-        if (mAdapter.getDataModel() == null) {
-            throw new IllegalStateException("No data model. Did you call #setDataModel?");
-        }
-
-        getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
-
-        final boolean defaultActivityButtonShown =
-                mDefaultActivityButton.getVisibility() == VISIBLE;
-
-        final int activityCount = mAdapter.getActivityCount();
-        final int maxActivityCountOffset = defaultActivityButtonShown ? 1 : 0;
-        if (maxActivityCount != ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED
-                && activityCount > maxActivityCount + maxActivityCountOffset) {
-            mAdapter.setShowFooterView(true);
-            mAdapter.setMaxActivityCount(maxActivityCount - 1);
-        } else {
-            mAdapter.setShowFooterView(false);
-            mAdapter.setMaxActivityCount(maxActivityCount);
-        }
-
-        ListPopupWindow popupWindow = getListPopupWindow();
-        if (!popupWindow.isShowing()) {
-            if (mIsSelectingDefaultActivity || !defaultActivityButtonShown) {
-                mAdapter.setShowDefaultActivity(true, defaultActivityButtonShown);
-            } else {
-                mAdapter.setShowDefaultActivity(false, false);
-            }
-            final int contentWidth = Math.min(mAdapter.measureContentWidth(), mListPopupMaxWidth);
-            popupWindow.setContentWidth(contentWidth);
-            popupWindow.show();
-            if (mProvider != null) {
-                mProvider.subUiVisibilityChanged(true);
-            }
-            popupWindow.getListView().setContentDescription(getContext().getString(
-                    R.string.abc_activitychooserview_choose_application));
-            popupWindow.getListView().setSelector(new ColorDrawable(Color.TRANSPARENT));
-        }
-    }
-
-    /**
-     * Dismisses the popup window with activities.
-     *
-     * @return True if dismissed, false if already dismissed.
-     */
-    public boolean dismissPopup() {
-        if (isShowingPopup()) {
-            getListPopupWindow().dismiss();
-            ViewTreeObserver viewTreeObserver = getViewTreeObserver();
-            if (viewTreeObserver.isAlive()) {
-                viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Gets whether the popup window with activities is shown.
-     *
-     * @return True if the popup is shown.
-     */
-    public boolean isShowingPopup() {
-        return getListPopupWindow().isShowing();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        ActivityChooserModel dataModel = mAdapter.getDataModel();
-        if (dataModel != null) {
-            dataModel.registerObserver(mModelDataSetObserver);
-        }
-        mIsAttachedToWindow = true;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        ActivityChooserModel dataModel = mAdapter.getDataModel();
-        if (dataModel != null) {
-            dataModel.unregisterObserver(mModelDataSetObserver);
-        }
-        ViewTreeObserver viewTreeObserver = getViewTreeObserver();
-        if (viewTreeObserver.isAlive()) {
-            viewTreeObserver.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
-        }
-        if (isShowingPopup()) {
-            dismissPopup();
-        }
-        mIsAttachedToWindow = false;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        View child = mActivityChooserContent;
-        // If the default action is not visible we want to be as tall as the
-        // ActionBar so if this widget is used in the latter it will look as
-        // a normal action button.
-        if (mDefaultActivityButton.getVisibility() != VISIBLE) {
-            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
-                    MeasureSpec.EXACTLY);
-        }
-        measureChild(child, widthMeasureSpec, heightMeasureSpec);
-        setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        mActivityChooserContent.layout(0, 0, right - left, bottom - top);
-        if (!isShowingPopup()) {
-            dismissPopup();
-        }
-    }
-
-    public ActivityChooserModel getDataModel() {
-        return mAdapter.getDataModel();
-    }
-
-    /**
-     * Sets a listener to receive a callback when the popup is dismissed.
-     *
-     * @param listener The listener to be notified.
-     */
-    public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
-        mOnDismissListener = listener;
-    }
-
-    /**
-     * Sets the initial count of items shown in the activities popup
-     * i.e. the items before the popup is expanded. This is an upper
-     * bound since it is not guaranteed that such number of intent
-     * handlers exist.
-     *
-     * @param itemCount The initial popup item count.
-     */
-    public void setInitialActivityCount(int itemCount) {
-        mInitialActivityCount = itemCount;
-    }
-
-    /**
-     * Sets a content description of the default action button. This
-     * resource should be a string taking one formatting argument and
-     * will be used for formatting the content description of the button
-     * dynamically as the default target changes. For example, a resource
-     * pointing to the string "share with %1$s" will result in a content
-     * description "share with Bluetooth" for the Bluetooth activity.
-     *
-     * @param resourceId The resource id.
-     */
-    public void setDefaultActionButtonContentDescription(int resourceId) {
-        mDefaultActionButtonContentDescription = resourceId;
-    }
-
-    /**
-     * Gets the list popup window which is lazily initialized.
-     *
-     * @return The popup.
-     */
-    ListPopupWindow getListPopupWindow() {
-        if (mListPopupWindow == null) {
-            mListPopupWindow = new ListPopupWindow(getContext());
-            mListPopupWindow.setAdapter(mAdapter);
-            mListPopupWindow.setAnchorView(ActivityChooserView.this);
-            mListPopupWindow.setModal(true);
-            mListPopupWindow.setOnItemClickListener(mCallbacks);
-            mListPopupWindow.setOnDismissListener(mCallbacks);
-        }
-        return mListPopupWindow;
-    }
-
-    /**
-     * Updates the buttons state.
-     */
-    void updateAppearance() {
-        // Expand overflow button.
-        if (mAdapter.getCount() > 0) {
-            mExpandActivityOverflowButton.setEnabled(true);
-        } else {
-            mExpandActivityOverflowButton.setEnabled(false);
-        }
-        // Default activity button.
-        final int activityCount = mAdapter.getActivityCount();
-        final int historySize = mAdapter.getHistorySize();
-        if (activityCount == 1 || (activityCount > 1 && historySize > 0)) {
-            mDefaultActivityButton.setVisibility(VISIBLE);
-            ResolveInfo activity = mAdapter.getDefaultActivity();
-            PackageManager packageManager = getContext().getPackageManager();
-            mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager));
-            if (mDefaultActionButtonContentDescription != 0) {
-                CharSequence label = activity.loadLabel(packageManager);
-                String contentDescription = getContext().getString(
-                        mDefaultActionButtonContentDescription, label);
-                mDefaultActivityButton.setContentDescription(contentDescription);
-            }
-        } else {
-            mDefaultActivityButton.setVisibility(View.GONE);
-        }
-        // Activity chooser content.
-        if (mDefaultActivityButton.getVisibility() == VISIBLE) {
-            mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground);
-        } else {
-            mActivityChooserContent.setBackgroundDrawable(null);
-        }
-    }
-
-    /**
-     * Interface implementation to avoid publishing them in the APIs.
-     */
-    private class Callbacks implements AdapterView.OnItemClickListener,
-            View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener {
-
-        Callbacks() {
-        }
-
-        // AdapterView#OnItemClickListener
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter();
-            final int itemViewType = adapter.getItemViewType(position);
-            switch (itemViewType) {
-                case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: {
-                    showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
-                } break;
-                case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: {
-                    dismissPopup();
-                    if (mIsSelectingDefaultActivity) {
-                        // The item at position zero is the default already.
-                        if (position > 0) {
-                            mAdapter.getDataModel().setDefaultActivity(position);
-                        }
-                    } else {
-                        // If the default target is not shown in the list, the first
-                        // item in the model is default action => adjust index
-                        position = mAdapter.getShowDefaultActivity() ? position : position + 1;
-                        Intent launchIntent = mAdapter.getDataModel().chooseActivity(position);
-                        if (launchIntent != null) {
-                            launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-                            getContext().startActivity(launchIntent);
-                        }
-                    }
-                } break;
-                default:
-                    throw new IllegalArgumentException();
-            }
-        }
-
-        // View.OnClickListener
-        @Override
-        public void onClick(View view) {
-            if (view == mDefaultActivityButton) {
-                dismissPopup();
-                ResolveInfo defaultActivity = mAdapter.getDefaultActivity();
-                final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity);
-                Intent launchIntent = mAdapter.getDataModel().chooseActivity(index);
-                if (launchIntent != null) {
-                    launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-                    getContext().startActivity(launchIntent);
-                }
-            } else if (view == mExpandActivityOverflowButton) {
-                mIsSelectingDefaultActivity = false;
-                showPopupUnchecked(mInitialActivityCount);
-            } else {
-                throw new IllegalArgumentException();
-            }
-        }
-
-        // OnLongClickListener#onLongClick
-        @Override
-        public boolean onLongClick(View view) {
-            if (view == mDefaultActivityButton) {
-                if (mAdapter.getCount() > 0) {
-                    mIsSelectingDefaultActivity = true;
-                    showPopupUnchecked(mInitialActivityCount);
-                }
-            } else {
-                throw new IllegalArgumentException();
-            }
-            return true;
-        }
-
-        // PopUpWindow.OnDismissListener#onDismiss
-        @Override
-        public void onDismiss() {
-            notifyOnDismissListener();
-            if (mProvider != null) {
-                mProvider.subUiVisibilityChanged(false);
-            }
-        }
-
-        private void notifyOnDismissListener() {
-            if (mOnDismissListener != null) {
-                mOnDismissListener.onDismiss();
-            }
-        }
-    }
-
-    /**
-     * Adapter for backing the list of activities shown in the popup.
-     */
-    private class ActivityChooserViewAdapter extends BaseAdapter {
-
-        public static final int MAX_ACTIVITY_COUNT_UNLIMITED = Integer.MAX_VALUE;
-
-        public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4;
-
-        private static final int ITEM_VIEW_TYPE_ACTIVITY = 0;
-
-        private static final int ITEM_VIEW_TYPE_FOOTER = 1;
-
-        private static final int ITEM_VIEW_TYPE_COUNT = 3;
-
-        private ActivityChooserModel mDataModel;
-
-        private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT;
-
-        private boolean mShowDefaultActivity;
-
-        private boolean mHighlightDefaultActivity;
-
-        private boolean mShowFooterView;
-
-        ActivityChooserViewAdapter() {
-        }
-
-        public void setDataModel(ActivityChooserModel dataModel) {
-            ActivityChooserModel oldDataModel = mAdapter.getDataModel();
-            if (oldDataModel != null && isShown()) {
-                oldDataModel.unregisterObserver(mModelDataSetObserver);
-            }
-            mDataModel = dataModel;
-            if (dataModel != null && isShown()) {
-                dataModel.registerObserver(mModelDataSetObserver);
-            }
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            if (mShowFooterView && position == getCount() - 1) {
-                return ITEM_VIEW_TYPE_FOOTER;
-            } else {
-                return ITEM_VIEW_TYPE_ACTIVITY;
-            }
-        }
-
-        @Override
-        public int getViewTypeCount() {
-            return ITEM_VIEW_TYPE_COUNT;
-        }
-
-        @Override
-        public int getCount() {
-            int count = 0;
-            int activityCount = mDataModel.getActivityCount();
-            if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
-                activityCount--;
-            }
-            count = Math.min(activityCount, mMaxActivityCount);
-            if (mShowFooterView) {
-                count++;
-            }
-            return count;
-        }
-
-        @Override
-        public Object getItem(int position) {
-            final int itemViewType = getItemViewType(position);
-            switch (itemViewType) {
-                case ITEM_VIEW_TYPE_FOOTER:
-                    return null;
-                case ITEM_VIEW_TYPE_ACTIVITY:
-                    if (!mShowDefaultActivity && mDataModel.getDefaultActivity() != null) {
-                        position++;
-                    }
-                    return mDataModel.getActivity(position);
-                default:
-                    throw new IllegalArgumentException();
-            }
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final int itemViewType = getItemViewType(position);
-            switch (itemViewType) {
-                case ITEM_VIEW_TYPE_FOOTER:
-                    if (convertView == null || convertView.getId() != ITEM_VIEW_TYPE_FOOTER) {
-                        convertView = LayoutInflater.from(getContext()).inflate(
-                                R.layout.abc_activity_chooser_view_list_item, parent, false);
-                        convertView.setId(ITEM_VIEW_TYPE_FOOTER);
-                        TextView titleView = (TextView) convertView.findViewById(R.id.title);
-                        titleView.setText(getContext().getString(
-                                R.string.abc_activity_chooser_view_see_all));
-                    }
-                    return convertView;
-                case ITEM_VIEW_TYPE_ACTIVITY:
-                    if (convertView == null || convertView.getId() != R.id.list_item) {
-                        convertView = LayoutInflater.from(getContext()).inflate(
-                                R.layout.abc_activity_chooser_view_list_item, parent, false);
-                    }
-                    PackageManager packageManager = getContext().getPackageManager();
-                    // Set the icon
-                    ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
-                    ResolveInfo activity = (ResolveInfo) getItem(position);
-                    iconView.setImageDrawable(activity.loadIcon(packageManager));
-                    // Set the title.
-                    TextView titleView = (TextView) convertView.findViewById(R.id.title);
-                    titleView.setText(activity.loadLabel(packageManager));
-                    // Highlight the default.
-                    if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
-                        convertView.setActivated(true);
-                    } else {
-                        convertView.setActivated(false);
-                    }
-                    return convertView;
-                default:
-                    throw new IllegalArgumentException();
-            }
-        }
-
-        public int measureContentWidth() {
-            // The user may have specified some of the target not to be shown but we
-            // want to measure all of them since after expansion they should fit.
-            final int oldMaxActivityCount = mMaxActivityCount;
-            mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED;
-
-            int contentWidth = 0;
-            View itemView = null;
-
-            final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-            final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-            final int count = getCount();
-
-            for (int i = 0; i < count; i++) {
-                itemView = getView(i, itemView, null);
-                itemView.measure(widthMeasureSpec, heightMeasureSpec);
-                contentWidth = Math.max(contentWidth, itemView.getMeasuredWidth());
-            }
-
-            mMaxActivityCount = oldMaxActivityCount;
-
-            return contentWidth;
-        }
-
-        public void setMaxActivityCount(int maxActivityCount) {
-            if (mMaxActivityCount != maxActivityCount) {
-                mMaxActivityCount = maxActivityCount;
-                notifyDataSetChanged();
-            }
-        }
-
-        public ResolveInfo getDefaultActivity() {
-            return mDataModel.getDefaultActivity();
-        }
-
-        public void setShowFooterView(boolean showFooterView) {
-            if (mShowFooterView != showFooterView) {
-                mShowFooterView = showFooterView;
-                notifyDataSetChanged();
-            }
-        }
-
-        public int getActivityCount() {
-            return mDataModel.getActivityCount();
-        }
-
-        public int getHistorySize() {
-            return mDataModel.getHistorySize();
-        }
-
-        public ActivityChooserModel getDataModel() {
-            return mDataModel;
-        }
-
-        public void setShowDefaultActivity(boolean showDefaultActivity,
-                boolean highlightDefaultActivity) {
-            if (mShowDefaultActivity != showDefaultActivity
-                    || mHighlightDefaultActivity != highlightDefaultActivity) {
-                mShowDefaultActivity = showDefaultActivity;
-                mHighlightDefaultActivity = highlightDefaultActivity;
-                notifyDataSetChanged();
-            }
-        }
-
-        public boolean getShowDefaultActivity() {
-            return mShowDefaultActivity;
-        }
-    }
-
-    /**
-     * Allows us to set the background using TintTypedArray
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class InnerLayout extends LinearLayoutCompat {
-
-        private static final int[] TINT_ATTRS = {
-                android.R.attr.background
-        };
-
-        public InnerLayout(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS);
-            setBackgroundDrawable(a.getDrawable(0));
-            a.recycle();
-        }
-    }
-}
diff --git a/android/support/v7/widget/AdapterHelper.java b/android/support/v7/widget/AdapterHelper.java
deleted file mode 100644
index 663e484..0000000
--- a/android/support/v7/widget/AdapterHelper.java
+++ /dev/null
@@ -1,775 +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.support.v4.util.Pools;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Helper class that can enqueue and process adapter update operations.
- * <p>
- * To support animations, RecyclerView presents an older version the Adapter to best represent
- * previous state of the layout. Sometimes, this is not trivial when items are removed that were
- * not laid out, in which case, RecyclerView has no way of providing that item's view for
- * animations.
- * <p>
- * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During
- * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass
- * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them
- * according to previously deferred operation and dispatch them before the first layout pass. It
- * also takes care of updating deferred UpdateOps since order of operations is changed by this
- * process.
- * <p>
- * Although operations may be forwarded to LayoutManager in different orders, resulting data set
- * is guaranteed to be the consistent.
- */
-class AdapterHelper implements OpReorderer.Callback {
-
-    static final int POSITION_TYPE_INVISIBLE = 0;
-
-    static final int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
-
-    private static final boolean DEBUG = false;
-
-    private static final String TAG = "AHT";
-
-    private Pools.Pool<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);
-
-    final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
-
-    final ArrayList<UpdateOp> mPostponedList = new ArrayList<UpdateOp>();
-
-    final Callback mCallback;
-
-    Runnable mOnItemProcessedCallback;
-
-    final boolean mDisableRecycler;
-
-    final OpReorderer mOpReorderer;
-
-    private int mExistingUpdateTypes = 0;
-
-    AdapterHelper(Callback callback) {
-        this(callback, false);
-    }
-
-    AdapterHelper(Callback callback, boolean disableRecycler) {
-        mCallback = callback;
-        mDisableRecycler = disableRecycler;
-        mOpReorderer = new OpReorderer(this);
-    }
-
-    AdapterHelper addUpdateOp(UpdateOp... ops) {
-        Collections.addAll(mPendingUpdates, ops);
-        return this;
-    }
-
-    void reset() {
-        recycleUpdateOpsAndClearList(mPendingUpdates);
-        recycleUpdateOpsAndClearList(mPostponedList);
-        mExistingUpdateTypes = 0;
-    }
-
-    void preProcess() {
-        mOpReorderer.reorderOps(mPendingUpdates);
-        final int count = mPendingUpdates.size();
-        for (int i = 0; i < count; i++) {
-            UpdateOp op = mPendingUpdates.get(i);
-            switch (op.cmd) {
-                case UpdateOp.ADD:
-                    applyAdd(op);
-                    break;
-                case UpdateOp.REMOVE:
-                    applyRemove(op);
-                    break;
-                case UpdateOp.UPDATE:
-                    applyUpdate(op);
-                    break;
-                case UpdateOp.MOVE:
-                    applyMove(op);
-                    break;
-            }
-            if (mOnItemProcessedCallback != null) {
-                mOnItemProcessedCallback.run();
-            }
-        }
-        mPendingUpdates.clear();
-    }
-
-    void consumePostponedUpdates() {
-        final int count = mPostponedList.size();
-        for (int i = 0; i < count; i++) {
-            mCallback.onDispatchSecondPass(mPostponedList.get(i));
-        }
-        recycleUpdateOpsAndClearList(mPostponedList);
-        mExistingUpdateTypes = 0;
-    }
-
-    private void applyMove(UpdateOp op) {
-        // MOVE ops are pre-processed so at this point, we know that item is still in the adapter.
-        // otherwise, it would be converted into a REMOVE operation
-        postponeAndUpdateViewHolders(op);
-    }
-
-    private void applyRemove(UpdateOp op) {
-        int tmpStart = op.positionStart;
-        int tmpCount = 0;
-        int tmpEnd = op.positionStart + op.itemCount;
-        int type = -1;
-        for (int position = op.positionStart; position < tmpEnd; position++) {
-            boolean typeChanged = false;
-            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
-            if (vh != null || canFindInPreLayout(position)) {
-                // If a ViewHolder exists or this is a newly added item, we can defer this update
-                // to post layout stage.
-                // * For existing ViewHolders, we'll fake its existence in the pre-layout phase.
-                // * For items that are added and removed in the same process cycle, they won't
-                // have any effect in pre-layout since their add ops are already deferred to
-                // post-layout pass.
-                if (type == POSITION_TYPE_INVISIBLE) {
-                    // Looks like we have other updates that we cannot merge with this one.
-                    // Create an UpdateOp and dispatch it to LayoutManager.
-                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
-                    dispatchAndUpdateViewHolders(newOp);
-                    typeChanged = true;
-                }
-                type = POSITION_TYPE_NEW_OR_LAID_OUT;
-            } else {
-                // This update cannot be recovered because we don't have a ViewHolder representing
-                // this position. Instead, post it to LayoutManager immediately
-                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
-                    // Looks like we have other updates that we cannot merge with this one.
-                    // Create UpdateOp op and dispatch it to LayoutManager.
-                    UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
-                    postponeAndUpdateViewHolders(newOp);
-                    typeChanged = true;
-                }
-                type = POSITION_TYPE_INVISIBLE;
-            }
-            if (typeChanged) {
-                position -= tmpCount; // also equal to tmpStart
-                tmpEnd -= tmpCount;
-                tmpCount = 1;
-            } else {
-                tmpCount++;
-            }
-        }
-        if (tmpCount != op.itemCount) { // all 1 effect
-            recycleUpdateOp(op);
-            op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount, null);
-        }
-        if (type == POSITION_TYPE_INVISIBLE) {
-            dispatchAndUpdateViewHolders(op);
-        } else {
-            postponeAndUpdateViewHolders(op);
-        }
-    }
-
-    private void applyUpdate(UpdateOp op) {
-        int tmpStart = op.positionStart;
-        int tmpCount = 0;
-        int tmpEnd = op.positionStart + op.itemCount;
-        int type = -1;
-        for (int position = op.positionStart; position < tmpEnd; position++) {
-            RecyclerView.ViewHolder vh = mCallback.findViewHolder(position);
-            if (vh != null || canFindInPreLayout(position)) { // deferred
-                if (type == POSITION_TYPE_INVISIBLE) {
-                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
-                            op.payload);
-                    dispatchAndUpdateViewHolders(newOp);
-                    tmpCount = 0;
-                    tmpStart = position;
-                }
-                type = POSITION_TYPE_NEW_OR_LAID_OUT;
-            } else { // applied
-                if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
-                    UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount,
-                            op.payload);
-                    postponeAndUpdateViewHolders(newOp);
-                    tmpCount = 0;
-                    tmpStart = position;
-                }
-                type = POSITION_TYPE_INVISIBLE;
-            }
-            tmpCount++;
-        }
-        if (tmpCount != op.itemCount) { // all 1 effect
-            Object payload = op.payload;
-            recycleUpdateOp(op);
-            op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount, payload);
-        }
-        if (type == POSITION_TYPE_INVISIBLE) {
-            dispatchAndUpdateViewHolders(op);
-        } else {
-            postponeAndUpdateViewHolders(op);
-        }
-    }
-
-    private void dispatchAndUpdateViewHolders(UpdateOp op) {
-        // tricky part.
-        // traverse all postpones and revert their changes on this op if necessary, apply updated
-        // dispatch to them since now they are after this op.
-        if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) {
-            throw new IllegalArgumentException("should not dispatch add or move for pre layout");
-        }
-        if (DEBUG) {
-            Log.d(TAG, "dispatch (pre)" + op);
-            Log.d(TAG, "postponed state before:");
-            for (UpdateOp updateOp : mPostponedList) {
-                Log.d(TAG, updateOp.toString());
-            }
-            Log.d(TAG, "----");
-        }
-
-        // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
-        // TODO Since move ops are pushed to end, we should not need this anymore
-        int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd);
-        if (DEBUG) {
-            Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart);
-        }
-        int tmpCnt = 1;
-        int offsetPositionForPartial = op.positionStart;
-        final int positionMultiplier;
-        switch (op.cmd) {
-            case UpdateOp.UPDATE:
-                positionMultiplier = 1;
-                break;
-            case UpdateOp.REMOVE:
-                positionMultiplier = 0;
-                break;
-            default:
-                throw new IllegalArgumentException("op should be remove or update." + op);
-        }
-        for (int p = 1; p < op.itemCount; p++) {
-            final int pos = op.positionStart + (positionMultiplier * p);
-            int updatedPos = updatePositionWithPostponed(pos, op.cmd);
-            if (DEBUG) {
-                Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos);
-            }
-            boolean continuous = false;
-            switch (op.cmd) {
-                case UpdateOp.UPDATE:
-                    continuous = updatedPos == tmpStart + 1;
-                    break;
-                case UpdateOp.REMOVE:
-                    continuous = updatedPos == tmpStart;
-                    break;
-            }
-            if (continuous) {
-                tmpCnt++;
-            } else {
-                // need to dispatch this separately
-                UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, op.payload);
-                if (DEBUG) {
-                    Log.d(TAG, "need to dispatch separately " + tmp);
-                }
-                dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
-                recycleUpdateOp(tmp);
-                if (op.cmd == UpdateOp.UPDATE) {
-                    offsetPositionForPartial += tmpCnt;
-                }
-                tmpStart = updatedPos; // need to remove previously dispatched
-                tmpCnt = 1;
-            }
-        }
-        Object payload = op.payload;
-        recycleUpdateOp(op);
-        if (tmpCnt > 0) {
-            UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt, payload);
-            if (DEBUG) {
-                Log.d(TAG, "dispatching:" + tmp);
-            }
-            dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
-            recycleUpdateOp(tmp);
-        }
-        if (DEBUG) {
-            Log.d(TAG, "post dispatch");
-            Log.d(TAG, "postponed state after:");
-            for (UpdateOp updateOp : mPostponedList) {
-                Log.d(TAG, updateOp.toString());
-            }
-            Log.d(TAG, "----");
-        }
-    }
-
-    void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) {
-        mCallback.onDispatchFirstPass(op);
-        switch (op.cmd) {
-            case UpdateOp.REMOVE:
-                mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount);
-                break;
-            case UpdateOp.UPDATE:
-                mCallback.markViewHoldersUpdated(offsetStart, op.itemCount, op.payload);
-                break;
-            default:
-                throw new IllegalArgumentException("only remove and update ops can be dispatched"
-                        + " in first pass");
-        }
-    }
-
-    private int updatePositionWithPostponed(int pos, int cmd) {
-        final int count = mPostponedList.size();
-        for (int i = count - 1; i >= 0; i--) {
-            UpdateOp postponed = mPostponedList.get(i);
-            if (postponed.cmd == UpdateOp.MOVE) {
-                int start, end;
-                if (postponed.positionStart < postponed.itemCount) {
-                    start = postponed.positionStart;
-                    end = postponed.itemCount;
-                } else {
-                    start = postponed.itemCount;
-                    end = postponed.positionStart;
-                }
-                if (pos >= start && pos <= end) {
-                    //i'm affected
-                    if (start == postponed.positionStart) {
-                        if (cmd == UpdateOp.ADD) {
-                            postponed.itemCount++;
-                        } else if (cmd == UpdateOp.REMOVE) {
-                            postponed.itemCount--;
-                        }
-                        // op moved to left, move it right to revert
-                        pos++;
-                    } else {
-                        if (cmd == UpdateOp.ADD) {
-                            postponed.positionStart++;
-                        } else if (cmd == UpdateOp.REMOVE) {
-                            postponed.positionStart--;
-                        }
-                        // op was moved right, move left to revert
-                        pos--;
-                    }
-                } else if (pos < postponed.positionStart) {
-                    // postponed MV is outside the dispatched OP. if it is before, offset
-                    if (cmd == UpdateOp.ADD) {
-                        postponed.positionStart++;
-                        postponed.itemCount++;
-                    } else if (cmd == UpdateOp.REMOVE) {
-                        postponed.positionStart--;
-                        postponed.itemCount--;
-                    }
-                }
-            } else {
-                if (postponed.positionStart <= pos) {
-                    if (postponed.cmd == UpdateOp.ADD) {
-                        pos -= postponed.itemCount;
-                    } else if (postponed.cmd == UpdateOp.REMOVE) {
-                        pos += postponed.itemCount;
-                    }
-                } else {
-                    if (cmd == UpdateOp.ADD) {
-                        postponed.positionStart++;
-                    } else if (cmd == UpdateOp.REMOVE) {
-                        postponed.positionStart--;
-                    }
-                }
-            }
-            if (DEBUG) {
-                Log.d(TAG, "dispath (step" + i + ")");
-                Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
-                for (UpdateOp updateOp : mPostponedList) {
-                    Log.d(TAG, updateOp.toString());
-                }
-                Log.d(TAG, "----");
-            }
-        }
-        for (int i = mPostponedList.size() - 1; i >= 0; i--) {
-            UpdateOp op = mPostponedList.get(i);
-            if (op.cmd == UpdateOp.MOVE) {
-                if (op.itemCount == op.positionStart || op.itemCount < 0) {
-                    mPostponedList.remove(i);
-                    recycleUpdateOp(op);
-                }
-            } else if (op.itemCount <= 0) {
-                mPostponedList.remove(i);
-                recycleUpdateOp(op);
-            }
-        }
-        return pos;
-    }
-
-    private boolean canFindInPreLayout(int position) {
-        final int count = mPostponedList.size();
-        for (int i = 0; i < count; i++) {
-            UpdateOp op = mPostponedList.get(i);
-            if (op.cmd == UpdateOp.MOVE) {
-                if (findPositionOffset(op.itemCount, i + 1) == position) {
-                    return true;
-                }
-            } else if (op.cmd == UpdateOp.ADD) {
-                // TODO optimize.
-                final int end = op.positionStart + op.itemCount;
-                for (int pos = op.positionStart; pos < end; pos++) {
-                    if (findPositionOffset(pos, i + 1) == position) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    private void applyAdd(UpdateOp op) {
-        postponeAndUpdateViewHolders(op);
-    }
-
-    private void postponeAndUpdateViewHolders(UpdateOp op) {
-        if (DEBUG) {
-            Log.d(TAG, "postponing " + op);
-        }
-        mPostponedList.add(op);
-        switch (op.cmd) {
-            case UpdateOp.ADD:
-                mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
-                break;
-            case UpdateOp.MOVE:
-                mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
-                break;
-            case UpdateOp.REMOVE:
-                mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
-                        op.itemCount);
-                break;
-            case UpdateOp.UPDATE:
-                mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown update op type for " + op);
-        }
-    }
-
-    boolean hasPendingUpdates() {
-        return mPendingUpdates.size() > 0;
-    }
-
-    boolean hasAnyUpdateTypes(int updateTypes) {
-        return (mExistingUpdateTypes & updateTypes) != 0;
-    }
-
-    int findPositionOffset(int position) {
-        return findPositionOffset(position, 0);
-    }
-
-    int findPositionOffset(int position, int firstPostponedItem) {
-        int count = mPostponedList.size();
-        for (int i = firstPostponedItem; i < count; ++i) {
-            UpdateOp op = mPostponedList.get(i);
-            if (op.cmd == UpdateOp.MOVE) {
-                if (op.positionStart == position) {
-                    position = op.itemCount;
-                } else {
-                    if (op.positionStart < position) {
-                        position--; // like a remove
-                    }
-                    if (op.itemCount <= position) {
-                        position++; // like an add
-                    }
-                }
-            } else if (op.positionStart <= position) {
-                if (op.cmd == UpdateOp.REMOVE) {
-                    if (position < op.positionStart + op.itemCount) {
-                        return -1;
-                    }
-                    position -= op.itemCount;
-                } else if (op.cmd == UpdateOp.ADD) {
-                    position += op.itemCount;
-                }
-            }
-        }
-        return position;
-    }
-
-    /**
-     * @return True if updates should be processed.
-     */
-    boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
-        if (itemCount < 1) {
-            return false;
-        }
-        mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
-        mExistingUpdateTypes |= UpdateOp.UPDATE;
-        return mPendingUpdates.size() == 1;
-    }
-
-    /**
-     * @return True if updates should be processed.
-     */
-    boolean onItemRangeInserted(int positionStart, int itemCount) {
-        if (itemCount < 1) {
-            return false;
-        }
-        mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));
-        mExistingUpdateTypes |= UpdateOp.ADD;
-        return mPendingUpdates.size() == 1;
-    }
-
-    /**
-     * @return True if updates should be processed.
-     */
-    boolean onItemRangeRemoved(int positionStart, int itemCount) {
-        if (itemCount < 1) {
-            return false;
-        }
-        mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
-        mExistingUpdateTypes |= UpdateOp.REMOVE;
-        return mPendingUpdates.size() == 1;
-    }
-
-    /**
-     * @return True if updates should be processed.
-     */
-    boolean onItemRangeMoved(int from, int to, int itemCount) {
-        if (from == to) {
-            return false; // no-op
-        }
-        if (itemCount != 1) {
-            throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
-        }
-        mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to, null));
-        mExistingUpdateTypes |= UpdateOp.MOVE;
-        return mPendingUpdates.size() == 1;
-    }
-
-    /**
-     * Skips pre-processing and applies all updates in one pass.
-     */
-    void consumeUpdatesInOnePass() {
-        // we still consume postponed updates (if there is) in case there was a pre-process call
-        // w/o a matching consumePostponedUpdates.
-        consumePostponedUpdates();
-        final int count = mPendingUpdates.size();
-        for (int i = 0; i < count; i++) {
-            UpdateOp op = mPendingUpdates.get(i);
-            switch (op.cmd) {
-                case UpdateOp.ADD:
-                    mCallback.onDispatchSecondPass(op);
-                    mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
-                    break;
-                case UpdateOp.REMOVE:
-                    mCallback.onDispatchSecondPass(op);
-                    mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
-                    break;
-                case UpdateOp.UPDATE:
-                    mCallback.onDispatchSecondPass(op);
-                    mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
-                    break;
-                case UpdateOp.MOVE:
-                    mCallback.onDispatchSecondPass(op);
-                    mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
-                    break;
-            }
-            if (mOnItemProcessedCallback != null) {
-                mOnItemProcessedCallback.run();
-            }
-        }
-        recycleUpdateOpsAndClearList(mPendingUpdates);
-        mExistingUpdateTypes = 0;
-    }
-
-    public int applyPendingUpdatesToPosition(int position) {
-        final int size = mPendingUpdates.size();
-        for (int i = 0; i < size; i++) {
-            UpdateOp op = mPendingUpdates.get(i);
-            switch (op.cmd) {
-                case UpdateOp.ADD:
-                    if (op.positionStart <= position) {
-                        position += op.itemCount;
-                    }
-                    break;
-                case UpdateOp.REMOVE:
-                    if (op.positionStart <= position) {
-                        final int end = op.positionStart + op.itemCount;
-                        if (end > position) {
-                            return RecyclerView.NO_POSITION;
-                        }
-                        position -= op.itemCount;
-                    }
-                    break;
-                case UpdateOp.MOVE:
-                    if (op.positionStart == position) {
-                        position = op.itemCount; //position end
-                    } else {
-                        if (op.positionStart < position) {
-                            position -= 1;
-                        }
-                        if (op.itemCount <= position) {
-                            position += 1;
-                        }
-                    }
-                    break;
-            }
-        }
-        return position;
-    }
-
-    boolean hasUpdates() {
-        return !mPostponedList.isEmpty() && !mPendingUpdates.isEmpty();
-    }
-
-    /**
-     * Queued operation to happen when child views are updated.
-     */
-    static class UpdateOp {
-
-        static final int ADD = 1;
-
-        static final int REMOVE = 1 << 1;
-
-        static final int UPDATE = 1 << 2;
-
-        static final int MOVE = 1 << 3;
-
-        static final int POOL_SIZE = 30;
-
-        int cmd;
-
-        int positionStart;
-
-        Object payload;
-
-        // holds the target position if this is a MOVE
-        int itemCount;
-
-        UpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
-            this.cmd = cmd;
-            this.positionStart = positionStart;
-            this.itemCount = itemCount;
-            this.payload = payload;
-        }
-
-        String cmdToString() {
-            switch (cmd) {
-                case ADD:
-                    return "add";
-                case REMOVE:
-                    return "rm";
-                case UPDATE:
-                    return "up";
-                case MOVE:
-                    return "mv";
-            }
-            return "??";
-        }
-
-        @Override
-        public String toString() {
-            return Integer.toHexString(System.identityHashCode(this))
-                    + "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount
-                    + ",p:" + payload + "]";
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            UpdateOp op = (UpdateOp) o;
-
-            if (cmd != op.cmd) {
-                return false;
-            }
-            if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) {
-                // reverse of this is also true
-                if (itemCount == op.positionStart && positionStart == op.itemCount) {
-                    return true;
-                }
-            }
-            if (itemCount != op.itemCount) {
-                return false;
-            }
-            if (positionStart != op.positionStart) {
-                return false;
-            }
-            if (payload != null) {
-                if (!payload.equals(op.payload)) {
-                    return false;
-                }
-            } else if (op.payload != null) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = cmd;
-            result = 31 * result + positionStart;
-            result = 31 * result + itemCount;
-            return result;
-        }
-    }
-
-    @Override
-    public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount, Object payload) {
-        UpdateOp op = mUpdateOpPool.acquire();
-        if (op == null) {
-            op = new UpdateOp(cmd, positionStart, itemCount, payload);
-        } else {
-            op.cmd = cmd;
-            op.positionStart = positionStart;
-            op.itemCount = itemCount;
-            op.payload = payload;
-        }
-        return op;
-    }
-
-    @Override
-    public void recycleUpdateOp(UpdateOp op) {
-        if (!mDisableRecycler) {
-            op.payload = null;
-            mUpdateOpPool.release(op);
-        }
-    }
-
-    void recycleUpdateOpsAndClearList(List<UpdateOp> ops) {
-        final int count = ops.size();
-        for (int i = 0; i < count; i++) {
-            recycleUpdateOp(ops.get(i));
-        }
-        ops.clear();
-    }
-
-    /**
-     * Contract between AdapterHelper and RecyclerView.
-     */
-    interface Callback {
-
-        RecyclerView.ViewHolder findViewHolder(int position);
-
-        void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
-
-        void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount);
-
-        void markViewHoldersUpdated(int positionStart, int itemCount, Object payloads);
-
-        void onDispatchFirstPass(UpdateOp updateOp);
-
-        void onDispatchSecondPass(UpdateOp updateOp);
-
-        void offsetPositionsForAdd(int positionStart, int itemCount);
-
-        void offsetPositionsForMove(int from, int to);
-    }
-}
diff --git a/android/support/v7/widget/AdapterHelperTest.java b/android/support/v7/widget/AdapterHelperTest.java
deleted file mode 100644
index a76f40e..0000000
--- a/android/support/v7/widget/AdapterHelperTest.java
+++ /dev/null
@@ -1,1188 +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 static android.support.v7.widget.RecyclerView.ViewHolder;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
-
-import android.support.test.filters.SmallTest;
-import android.view.View;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-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.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class AdapterHelperTest {
-
-    private static final boolean DEBUG = false;
-
-    private boolean mCollectLogs = false;
-
-    private static final String TAG = "AHT";
-
-    private List<MockViewHolder> mViewHolders;
-
-    private AdapterHelper mAdapterHelper;
-
-    private List<AdapterHelper.UpdateOp> mFirstPassUpdates, mSecondPassUpdates;
-
-    TestAdapter mTestAdapter;
-
-    private TestAdapter mPreProcessClone;
-    // we clone adapter pre-process to run operations to see result
-
-    private List<TestAdapter.Item> mPreLayoutItems;
-
-    private StringBuilder mLog = new StringBuilder();
-
-    @Rule
-    public TestWatcher reportErrorLog = new TestWatcher() {
-        @Override
-        protected void failed(Throwable e, Description description) {
-            System.out.println(mLog.toString());
-        }
-
-        @Override
-        protected void succeeded(Description description) {
-        }
-    };
-
-    @Before
-    public void cleanState() {
-        mLog.setLength(0);
-        mPreLayoutItems = new ArrayList<>();
-        mViewHolders = new ArrayList<>();
-        mFirstPassUpdates = new ArrayList<>();
-        mSecondPassUpdates = new ArrayList<>();
-        mPreProcessClone = null;
-        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
-            @Override
-            public RecyclerView.ViewHolder findViewHolder(int position) {
-                for (ViewHolder vh : mViewHolders) {
-                    if (vh.mPosition == position && !vh.isRemoved()) {
-                        return vh;
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            public void offsetPositionsForRemovingInvisible(int positionStart, int itemCount) {
-                final int positionEnd = positionStart + itemCount;
-                for (ViewHolder holder : mViewHolders) {
-                    if (holder.mPosition >= positionEnd) {
-                        holder.offsetPosition(-itemCount, true);
-                    } else if (holder.mPosition >= positionStart) {
-                        holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount, true);
-                    }
-                }
-            }
-
-            @Override
-            public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart,
-                    int itemCount) {
-                final int positionEnd = positionStart + itemCount;
-                for (ViewHolder holder : mViewHolders) {
-                    if (holder.mPosition >= positionEnd) {
-                        holder.offsetPosition(-itemCount, false);
-                    } else if (holder.mPosition >= positionStart) {
-                        holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount, false);
-                    }
-                }
-            }
-
-            @Override
-            public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
-                final int positionEnd = positionStart + itemCount;
-                for (ViewHolder holder : mViewHolders) {
-                    if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
-                        holder.addFlags(ViewHolder.FLAG_UPDATE);
-                        holder.addChangePayload(payload);
-                    }
-                }
-            }
-
-            @Override
-            public void onDispatchFirstPass(AdapterHelper.UpdateOp updateOp) {
-                if (DEBUG) {
-                    log("first pass:" + updateOp.toString());
-                }
-                for (ViewHolder viewHolder : mViewHolders) {
-                    for (int i = 0; i < updateOp.itemCount; i++) {
-                        // events are dispatched before view holders are updated for consistency
-                        assertFalse("update op should not match any existing view holders",
-                                viewHolder.getLayoutPosition() == updateOp.positionStart + i);
-                    }
-                }
-
-                mFirstPassUpdates.add(updateOp);
-            }
-
-            @Override
-            public void onDispatchSecondPass(AdapterHelper.UpdateOp updateOp) {
-                if (DEBUG) {
-                    log("second pass:" + updateOp.toString());
-                }
-                mSecondPassUpdates.add(updateOp);
-            }
-
-            @Override
-            public void offsetPositionsForAdd(int positionStart, int itemCount) {
-                for (ViewHolder holder : mViewHolders) {
-                    if (holder != null && holder.mPosition >= positionStart) {
-                        holder.offsetPosition(itemCount, false);
-                    }
-                }
-            }
-
-            @Override
-            public void offsetPositionsForMove(int from, int to) {
-                final int start, end, inBetweenOffset;
-                if (from < to) {
-                    start = from;
-                    end = to;
-                    inBetweenOffset = -1;
-                } else {
-                    start = to;
-                    end = from;
-                    inBetweenOffset = 1;
-                }
-                for (ViewHolder holder : mViewHolders) {
-                    if (holder == null || holder.mPosition < start || holder.mPosition > end) {
-                        continue;
-                    }
-                    if (holder.mPosition == from) {
-                        holder.offsetPosition(to - from, false);
-                    } else {
-                        holder.offsetPosition(inBetweenOffset, false);
-                    }
-                }
-            }
-        }, true);
-    }
-
-    void log(String msg) {
-        if (mCollectLogs) {
-            mLog.append(msg).append("\n");
-        } else {
-            System.out.println(TAG + ":" + msg);
-        }
-    }
-
-    void setupBasic(int count, int visibleStart, int visibleCount) {
-        if (DEBUG) {
-            log("setupBasic(" + count + "," + visibleStart + "," + visibleCount + ");");
-        }
-        mTestAdapter = new TestAdapter(count, mAdapterHelper);
-        for (int i = 0; i < visibleCount; i++) {
-            addViewHolder(visibleStart + i);
-        }
-        mPreProcessClone = mTestAdapter.createCopy();
-    }
-
-    private void addViewHolder(int position) {
-        MockViewHolder viewHolder = new MockViewHolder();
-        viewHolder.mPosition = position;
-        viewHolder.mItem = mTestAdapter.mItems.get(position);
-        mViewHolders.add(viewHolder);
-    }
-
-    @Test
-    public void testChangeAll() throws Exception {
-        try {
-            setupBasic(5, 0, 3);
-            up(0, 5);
-            mAdapterHelper.preProcess();
-        } catch (Throwable t) {
-            throw new Exception(mLog.toString());
-        }
-    }
-
-    @Test
-    public void testFindPositionOffsetInPreLayout() {
-        setupBasic(50, 25, 10);
-        rm(24, 5);
-        mAdapterHelper.preProcess();
-        // since 25 is invisible, we offset by one while checking
-        assertEquals("find position for view 23",
-                23, mAdapterHelper.findPositionOffset(23));
-        assertEquals("find position for view 24",
-                -1, mAdapterHelper.findPositionOffset(24));
-        assertEquals("find position for view 25",
-                -1, mAdapterHelper.findPositionOffset(25));
-        assertEquals("find position for view 26",
-                -1, mAdapterHelper.findPositionOffset(26));
-        assertEquals("find position for view 27",
-                -1, mAdapterHelper.findPositionOffset(27));
-        assertEquals("find position for view 28",
-                24, mAdapterHelper.findPositionOffset(28));
-        assertEquals("find position for view 29",
-                25, mAdapterHelper.findPositionOffset(29));
-    }
-
-    @Test
-    public void testNotifyAfterPre() {
-        setupBasic(10, 2, 3);
-        add(2, 1);
-        mAdapterHelper.preProcess();
-        add(3, 1);
-        mAdapterHelper.consumeUpdatesInOnePass();
-        mPreProcessClone.applyOps(mFirstPassUpdates, mTestAdapter);
-        mPreProcessClone.applyOps(mSecondPassUpdates, mTestAdapter);
-        assertAdaptersEqual(mTestAdapter, mPreProcessClone);
-    }
-
-    @Test
-    public void testSinglePass() {
-        setupBasic(10, 2, 3);
-        add(2, 1);
-        rm(1, 2);
-        add(1, 5);
-        mAdapterHelper.consumeUpdatesInOnePass();
-        assertDispatch(0, 3);
-    }
-
-    @Test
-    public void testDeleteVisible() {
-        setupBasic(10, 2, 3);
-        rm(2, 1);
-        preProcess();
-        assertDispatch(0, 1);
-    }
-
-    @Test
-    public void testDeleteInvisible() {
-        setupBasic(10, 3, 4);
-        rm(2, 1);
-        preProcess();
-        assertDispatch(1, 0);
-    }
-
-    @Test
-    public void testAddCount() {
-        setupBasic(0, 0, 0);
-        add(0, 1);
-        assertEquals(1, mAdapterHelper.mPendingUpdates.size());
-    }
-
-    @Test
-    public void testDeleteCount() {
-        setupBasic(1, 0, 0);
-        rm(0, 1);
-        assertEquals(1, mAdapterHelper.mPendingUpdates.size());
-    }
-
-    @Test
-    public void testAddProcess() {
-        setupBasic(0, 0, 0);
-        add(0, 1);
-        preProcess();
-        assertEquals(0, mAdapterHelper.mPendingUpdates.size());
-    }
-
-    @Test
-    public void testAddRemoveSeparate() {
-        setupBasic(10, 2, 2);
-        add(6, 1);
-        rm(5, 1);
-        preProcess();
-        assertDispatch(1, 1);
-    }
-
-    @Test
-    public void testScenario1() {
-        setupBasic(10, 3, 2);
-        rm(4, 1);
-        rm(3, 1);
-        rm(3, 1);
-        preProcess();
-        assertDispatch(1, 2);
-    }
-
-    @Test
-    public void testDivideDelete() {
-        setupBasic(10, 3, 4);
-        rm(2, 2);
-        preProcess();
-        assertDispatch(1, 1);
-    }
-
-    @Test
-    public void testScenario2() {
-        setupBasic(10, 3, 3); // 3-4-5
-        add(4, 2); // 3 a b 4 5
-        rm(0, 1); // (0) 3(2) a(3) b(4) 4(3) 5(4)
-        rm(1, 3); // (1,2) (x) a(1) b(2) 4(3)
-        preProcess();
-        assertDispatch(2, 2);
-    }
-
-    @Test
-    public void testScenario3() {
-        setupBasic(10, 2, 2);
-        rm(0, 5);
-        preProcess();
-        assertDispatch(2, 1);
-        assertOps(mFirstPassUpdates, rmOp(0, 2), rmOp(2, 1));
-        assertOps(mSecondPassUpdates, rmOp(0, 2));
-    }
-    // TODO test MOVE then remove items in between.
-    // TODO test MOVE then remove it, make sure it is not dispatched
-
-    @Test
-    public void testScenario4() {
-        setupBasic(5, 0, 5);
-        // 0 1 2 3 4
-        // 0 1 2 a b 3 4
-        // 0 2 a b 3 4
-        // 0 c d 2 a b 3 4
-        // 0 c d 2 a 4
-        // c d 2 a 4
-        // pre: 0 1 2 3 4
-        add(3, 2);
-        rm(1, 1);
-        add(1, 2);
-        rm(5, 2);
-        rm(0, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario5() {
-        setupBasic(5, 0, 5);
-        // 0 1 2 3 4
-        // 0 1 2 a b 3 4
-        // 0 1 b 3 4
-        // pre: 0 1 2 3 4
-        // pre w/ adap: 0 1 2 b 3 4
-        add(3, 2);
-        rm(2, 2);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario6() {
-//        setupBasic(47, 19, 24);
-//        mv(11, 12);
-//        add(24, 16);
-//        rm(9, 3);
-        setupBasic(10, 5, 3);
-        mv(2, 3);
-        add(6, 4);
-        rm(4, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario8() {
-        setupBasic(68, 51, 13);
-        mv(22, 11);
-        mv(22, 52);
-        rm(37, 19);
-        add(12, 38);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario9() {
-        setupBasic(44, 3, 7);
-        add(7, 21);
-        rm(31, 3);
-        rm(32, 11);
-        mv(29, 5);
-        mv(30, 32);
-        add(25, 32);
-        rm(15, 66);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario10() {
-        setupBasic(14, 10, 3);
-        rm(4, 4);
-        add(5, 11);
-        mv(5, 18);
-        rm(2, 9);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario11() {
-        setupBasic(78, 3, 64);
-        mv(34, 28);
-        add(1, 11);
-        rm(9, 74);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario12() {
-        setupBasic(38, 9, 7);
-        rm(26, 3);
-        mv(29, 15);
-        rm(30, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario13() {
-        setupBasic(49, 41, 3);
-        rm(30, 13);
-        add(4, 10);
-        mv(3, 38);
-        mv(20, 17);
-        rm(18, 23);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario14() {
-        setupBasic(24, 3, 11);
-        rm(2, 15);
-        mv(2, 1);
-        add(2, 34);
-        add(11, 3);
-        rm(10, 25);
-        rm(13, 6);
-        rm(4, 4);
-        rm(6, 4);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario15() {
-        setupBasic(10, 8, 1);
-        mv(6, 1);
-        mv(1, 4);
-        rm(3, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario16() {
-        setupBasic(10, 3, 3);
-        rm(2, 1);
-        rm(1, 7);
-        rm(0, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario17() {
-        setupBasic(10, 8, 1);
-        mv(1, 0);
-        mv(5, 1);
-        rm(1, 7);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario18() {
-        setupBasic(10, 1, 4);
-        add(2, 11);
-        rm(16, 1);
-        add(3, 1);
-        rm(9, 10);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario19() {
-        setupBasic(10, 8, 1);
-        mv(9, 7);
-        mv(9, 3);
-        rm(5, 4);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario20() {
-        setupBasic(10, 7, 1);
-        mv(9, 1);
-        mv(3, 9);
-        rm(7, 2);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario21() {
-        setupBasic(10, 5, 2);
-        mv(1, 0);
-        mv(9, 1);
-        rm(2, 3);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario22() {
-        setupBasic(10, 7, 2);
-        add(2, 16);
-        mv(20, 9);
-        rm(17, 6);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario23() {
-        setupBasic(10, 5, 3);
-        mv(9, 6);
-        add(4, 15);
-        rm(21, 3);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario24() {
-        setupBasic(10, 1, 6);
-        add(6, 5);
-        mv(14, 6);
-        rm(7, 6);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario25() {
-        setupBasic(10, 3, 4);
-        mv(3, 9);
-        rm(5, 4);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario25a() {
-        setupBasic(10, 3, 4);
-        rm(6, 4);
-        mv(3, 5);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario26() {
-        setupBasic(10, 4, 4);
-        rm(3, 5);
-        mv(2, 0);
-        mv(1, 0);
-        rm(1, 1);
-        mv(0, 2);
-        preProcess();
-    }
-
-    @Test
-    public void testScenario27() {
-        setupBasic(10, 0, 3);
-        mv(9, 4);
-        mv(8, 4);
-        add(7, 6);
-        rm(5, 5);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio28() {
-        setupBasic(10, 4, 1);
-        mv(8, 6);
-        rm(8, 1);
-        mv(7, 5);
-        rm(3, 3);
-        rm(1, 4);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio29() {
-        setupBasic(10, 6, 3);
-        mv(3, 6);
-        up(6, 2);
-        add(5, 5);
-    }
-
-    @Test
-    public void testScenerio30() {
-        mCollectLogs = true;
-        setupBasic(10, 3, 1);
-        rm(3, 2);
-        rm(2, 5);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio31() {
-        mCollectLogs = true;
-        setupBasic(10, 3, 1);
-        rm(3, 1);
-        rm(2, 3);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio32() {
-        setupBasic(10, 8, 1);
-        add(9, 2);
-        add(7, 39);
-        up(0, 39);
-        mv(36, 20);
-        add(1, 48);
-        mv(22, 98);
-        mv(96, 29);
-        up(36, 29);
-        add(60, 36);
-        add(127, 34);
-        rm(142, 22);
-        up(12, 69);
-        up(116, 13);
-        up(118, 19);
-        mv(94, 69);
-        up(98, 21);
-        add(89, 18);
-        rm(94, 70);
-        up(71, 8);
-        rm(54, 26);
-        add(2, 20);
-        mv(78, 84);
-        mv(56, 2);
-        mv(1, 79);
-        rm(76, 7);
-        rm(57, 12);
-        rm(30, 27);
-        add(24, 13);
-        add(21, 5);
-        rm(11, 27);
-        rm(32, 1);
-        up(0, 5);
-        mv(14, 9);
-        rm(15, 12);
-        up(19, 1);
-        rm(7, 1);
-        mv(10, 4);
-        up(4, 3);
-        rm(16, 1);
-        up(13, 5);
-        up(2, 8);
-        add(10, 19);
-        add(15, 42);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio33() throws Throwable {
-        try {
-            mCollectLogs = true;
-            setupBasic(10, 7, 1);
-            mv(0, 6);
-            up(0, 7);
-            preProcess();
-        } catch (Throwable t) {
-            throw new Throwable(t.getMessage() + "\n" + mLog.toString());
-        }
-    }
-
-    @Test
-    public void testScenerio34() {
-        setupBasic(10, 6, 1);
-        mv(9, 7);
-        rm(5, 2);
-        up(4, 3);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio35() {
-        setupBasic(10, 4, 4);
-        mv(1, 4);
-        up(2, 7);
-        up(0, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio36() {
-        setupBasic(10, 7, 2);
-        rm(4, 1);
-        mv(1, 6);
-        up(4, 4);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio37() throws Throwable {
-        try {
-            mCollectLogs = true;
-            setupBasic(10, 5, 2);
-            mv(3, 6);
-            rm(4, 4);
-            rm(3, 2);
-            preProcess();
-        } catch (Throwable t) {
-            throw new Throwable(t.getMessage() + "\n" + mLog.toString());
-        }
-    }
-
-    @Test
-    public void testScenerio38() {
-        setupBasic(10, 2, 2);
-        add(0, 24);
-        rm(26, 4);
-        rm(1, 24);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio39() {
-        setupBasic(10, 7, 1);
-        mv(0, 2);
-        rm(8, 1);
-        rm(2, 6);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio40() {
-        setupBasic(10, 5, 3);
-        rm(5, 4);
-        mv(0, 5);
-        rm(2, 3);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio41() {
-        setupBasic(10, 7, 2);
-        mv(4, 9);
-        rm(0, 6);
-        rm(0, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio42() {
-        setupBasic(10, 6, 2);
-        mv(5, 9);
-        rm(5, 1);
-        rm(2, 6);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio43() {
-        setupBasic(10, 1, 6);
-        mv(6, 8);
-        rm(3, 5);
-        up(3, 1);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio44() {
-        setupBasic(10, 5, 2);
-        mv(6, 4);
-        mv(4, 1);
-        rm(5, 3);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio45() {
-        setupBasic(10, 4, 2);
-        rm(1, 4);
-        preProcess();
-    }
-
-    @Test
-    public void testScenerio46() {
-        setupBasic(10, 4, 3);
-        up(6, 1);
-        mv(8, 0);
-        rm(2, 7);
-        preProcess();
-    }
-
-    @Test
-    public void testMoveAdded() {
-        setupBasic(10, 2, 2);
-        add(3, 5);
-        mv(4, 2);
-        preProcess();
-    }
-
-    @Test
-    public void testPayloads() {
-        setupBasic(10, 2, 2);
-        up(3, 3, "payload");
-        preProcess();
-        assertOps(mFirstPassUpdates, upOp(4, 2, "payload"));
-        assertOps(mSecondPassUpdates, upOp(3, 1, "payload"));
-    }
-
-    @Test
-    public void testRandom() throws Throwable {
-        mCollectLogs = true;
-        Random random = new Random(System.nanoTime());
-        for (int i = 0; i < 100; i++) {
-            try {
-                log("running random test " + i);
-                randomTest(random, Math.max(40, 10 + nextInt(random, i)));
-            } catch (Throwable t) {
-                throw new Throwable("failure at random test " + i + "\n" + t.getMessage()
-                        + "\n" + mLog.toString(), t);
-            }
-        }
-    }
-
-    private void randomTest(Random random, int opCount) {
-        cleanState();
-        if (DEBUG) {
-            log("randomTest");
-        }
-        final int count = 10;// + nextInt(random,100);
-        final int start = nextInt(random, count - 1);
-        final int layoutCount = Math.max(1, nextInt(random, count - start));
-        setupBasic(count, start, layoutCount);
-
-        while (opCount-- > 0) {
-            final int op = nextInt(random, 5);
-            switch (op) {
-                case 0:
-                    if (mTestAdapter.mItems.size() > 1) {
-                        int s = nextInt(random, mTestAdapter.mItems.size() - 1);
-                        int len = Math.max(1, nextInt(random, mTestAdapter.mItems.size() - s));
-                        rm(s, len);
-                    }
-                    break;
-                case 1:
-                    int s = mTestAdapter.mItems.size() == 0 ? 0 :
-                            nextInt(random, mTestAdapter.mItems.size());
-                    add(s, nextInt(random, 50));
-                    break;
-                case 2:
-                    if (mTestAdapter.mItems.size() >= 2) {
-                        int from = nextInt(random, mTestAdapter.mItems.size());
-                        int to;
-                        do {
-                            to = nextInt(random, mTestAdapter.mItems.size());
-                        } while (to == from);
-                        mv(from, to);
-                    }
-                    break;
-                case 3:
-                    if (mTestAdapter.mItems.size() > 1) {
-                        s = nextInt(random, mTestAdapter.mItems.size() - 1);
-                        int len = Math.max(1, nextInt(random, mTestAdapter.mItems.size() - s));
-                        up(s, len);
-                    }
-                    break;
-                case 4:
-                    if (mTestAdapter.mItems.size() > 1) {
-                        s = nextInt(random, mTestAdapter.mItems.size() - 1);
-                        int len = Math.max(1, nextInt(random, mTestAdapter.mItems.size() - s));
-                        up(s, len, Integer.toString(s));
-                    }
-                    break;
-            }
-        }
-        preProcess();
-    }
-
-    private int nextInt(Random random, int n) {
-        if (n == 0) {
-            return 0;
-        }
-        return random.nextInt(n);
-    }
-
-    private void assertOps(List<AdapterHelper.UpdateOp> actual,
-            AdapterHelper.UpdateOp... expected) {
-        assertEquals(expected.length, actual.size());
-        for (int i = 0; i < expected.length; i++) {
-            assertEquals(expected[i], actual.get(i));
-        }
-    }
-
-    private void assertDispatch(int firstPass, int secondPass) {
-        assertEquals(firstPass, mFirstPassUpdates.size());
-        assertEquals(secondPass, mSecondPassUpdates.size());
-    }
-
-    private void preProcess() {
-        for (MockViewHolder vh : mViewHolders) {
-            final int ind = mTestAdapter.mItems.indexOf(vh.mItem);
-            assertEquals("actual adapter position should match", ind,
-                    mAdapterHelper.applyPendingUpdatesToPosition(vh.mPosition));
-        }
-        mAdapterHelper.preProcess();
-        for (int i = 0; i < mPreProcessClone.mItems.size(); i++) {
-            TestAdapter.Item item = mPreProcessClone.mItems.get(i);
-            final int preLayoutIndex = mPreLayoutItems.indexOf(item);
-            final int endIndex = mTestAdapter.mItems.indexOf(item);
-            if (preLayoutIndex != -1) {
-                assertEquals("find position offset should work properly for existing elements" + i
-                        + " at pre layout position " + preLayoutIndex + " and post layout position "
-                        + endIndex, endIndex, mAdapterHelper.findPositionOffset(preLayoutIndex));
-            }
-        }
-        // make sure visible view holders still have continuous positions
-        final StringBuilder vhLogBuilder = new StringBuilder();
-        for (ViewHolder vh : mViewHolders) {
-            vhLogBuilder.append("\n").append(vh.toString());
-        }
-        if (mViewHolders.size() > 0) {
-            final String vhLog = vhLogBuilder.toString();
-            final int start = mViewHolders.get(0).getLayoutPosition();
-            for (int i = 1; i < mViewHolders.size(); i++) {
-                assertEquals("view holder positions should be continious in pre-layout" + vhLog,
-                        start + i, mViewHolders.get(i).getLayoutPosition());
-            }
-        }
-        mAdapterHelper.consumePostponedUpdates();
-        // now assert these two adapters have identical data.
-        mPreProcessClone.applyOps(mFirstPassUpdates, mTestAdapter);
-        mPreProcessClone.applyOps(mSecondPassUpdates, mTestAdapter);
-        assertAdaptersEqual(mTestAdapter, mPreProcessClone);
-    }
-
-    private void assertAdaptersEqual(TestAdapter a1, TestAdapter a2) {
-        assertEquals(a1.mItems.size(), a2.mItems.size());
-        for (int i = 0; i < a1.mItems.size(); i++) {
-            TestAdapter.Item item = a1.mItems.get(i);
-            assertSame(item, a2.mItems.get(i));
-            assertEquals(0, item.getUpdateCount());
-        }
-        assertEquals(0, a1.mPendingAdded.size());
-        assertEquals(0, a2.mPendingAdded.size());
-    }
-
-    private AdapterHelper.UpdateOp op(int cmd, int start, int count) {
-        return new AdapterHelper.UpdateOp(cmd, start, count, null);
-    }
-
-    private AdapterHelper.UpdateOp op(int cmd, int start, int count, Object payload) {
-        return new AdapterHelper.UpdateOp(cmd, start, count, payload);
-    }
-
-    private AdapterHelper.UpdateOp rmOp(int start, int count) {
-        return op(AdapterHelper.UpdateOp.REMOVE, start, count);
-    }
-
-    private AdapterHelper.UpdateOp upOp(int start, int count, @SuppressWarnings
-            ("SameParameterValue") Object payload) {
-        return op(AdapterHelper.UpdateOp.UPDATE, start, count, payload);
-    }
-
-    void add(int start, int count) {
-        if (DEBUG) {
-            log("add(" + start + "," + count + ");");
-        }
-        mTestAdapter.add(start, count);
-    }
-
-    private boolean isItemLaidOut(int pos) {
-        for (ViewHolder viewHolder : mViewHolders) {
-            if (viewHolder.mOldPosition == pos) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void mv(int from, int to) {
-        if (DEBUG) {
-            log("mv(" + from + "," + to + ");");
-        }
-        mTestAdapter.move(from, to);
-    }
-
-    private void rm(int start, int count) {
-        if (DEBUG) {
-            log("rm(" + start + "," + count + ");");
-        }
-        for (int i = start; i < start + count; i++) {
-            if (!isItemLaidOut(i)) {
-                TestAdapter.Item item = mTestAdapter.mItems.get(i);
-                mPreLayoutItems.remove(item);
-            }
-        }
-        mTestAdapter.remove(start, count);
-    }
-
-    void up(int start, int count) {
-        if (DEBUG) {
-            log("up(" + start + "," + count + ");");
-        }
-        mTestAdapter.update(start, count);
-    }
-
-    void up(int start, int count, Object payload) {
-        if (DEBUG) {
-            log("up(" + start + "," + count + "," + payload + ");");
-        }
-        mTestAdapter.update(start, count, payload);
-    }
-
-    static class TestAdapter {
-
-        List<Item> mItems;
-
-        final AdapterHelper mAdapterHelper;
-
-        Queue<Item> mPendingAdded;
-
-        public TestAdapter(int initialCount, AdapterHelper container) {
-            mItems = new ArrayList<>();
-            mAdapterHelper = container;
-            mPendingAdded = new LinkedList<>();
-            for (int i = 0; i < initialCount; i++) {
-                mItems.add(new Item());
-            }
-        }
-
-        public void add(int index, int count) {
-            for (int i = 0; i < count; i++) {
-                Item item = new Item();
-                mPendingAdded.add(item);
-                mItems.add(index + i, item);
-            }
-            mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp(
-                    AdapterHelper.UpdateOp.ADD, index, count, null
-            ));
-        }
-
-        public void move(int from, int to) {
-            mItems.add(to, mItems.remove(from));
-            mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp(
-                    AdapterHelper.UpdateOp.MOVE, from, to, null
-            ));
-        }
-
-        public void remove(int index, int count) {
-            for (int i = 0; i < count; i++) {
-                mItems.remove(index);
-            }
-            mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp(
-                    AdapterHelper.UpdateOp.REMOVE, index, count, null
-            ));
-        }
-
-        public void update(int index, int count) {
-            update(index, count, null);
-        }
-
-        public void update(int index, int count, Object payload) {
-            for (int i = 0; i < count; i++) {
-                mItems.get(index + i).update(payload);
-            }
-            mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp(
-                    AdapterHelper.UpdateOp.UPDATE, index, count, payload
-            ));
-        }
-
-        TestAdapter createCopy() {
-            TestAdapter adapter = new TestAdapter(0, mAdapterHelper);
-            adapter.mItems.addAll(mItems);
-            return adapter;
-        }
-
-        void applyOps(List<AdapterHelper.UpdateOp> updates,
-                TestAdapter dataSource) {
-            for (AdapterHelper.UpdateOp op : updates) {
-                switch (op.cmd) {
-                    case AdapterHelper.UpdateOp.ADD:
-                        for (int i = 0; i < op.itemCount; i++) {
-                            mItems.add(op.positionStart + i, dataSource.consumeNextAdded());
-                        }
-                        break;
-                    case AdapterHelper.UpdateOp.REMOVE:
-                        for (int i = 0; i < op.itemCount; i++) {
-                            mItems.remove(op.positionStart);
-                        }
-                        break;
-                    case AdapterHelper.UpdateOp.UPDATE:
-                        for (int i = 0; i < op.itemCount; i++) {
-                            mItems.get(op.positionStart + i).handleUpdate(op.payload);
-                        }
-                        break;
-                    case AdapterHelper.UpdateOp.MOVE:
-                        mItems.add(op.itemCount, mItems.remove(op.positionStart));
-                        break;
-                }
-            }
-        }
-
-        private Item consumeNextAdded() {
-            return mPendingAdded.remove();
-        }
-
-        public static class Item {
-
-            private static AtomicInteger itemCounter = new AtomicInteger();
-
-            @SuppressWarnings("unused")
-            private final int id;
-
-            private int mVersionCount = 0;
-
-            private ArrayList<Object> mPayloads = new ArrayList<>();
-
-            public Item() {
-                id = itemCounter.incrementAndGet();
-            }
-
-            public void update(Object payload) {
-                mPayloads.add(payload);
-                mVersionCount++;
-            }
-
-            void handleUpdate(Object payload) {
-                assertSame(payload, mPayloads.get(0));
-                mPayloads.remove(0);
-                mVersionCount--;
-            }
-
-            int getUpdateCount() {
-                return mVersionCount;
-            }
-        }
-    }
-
-    static class MockViewHolder extends RecyclerView.ViewHolder {
-        TestAdapter.Item mItem;
-
-        MockViewHolder() {
-            super(Mockito.mock(View.class));
-        }
-
-        @Override
-        public String toString() {
-            return mItem == null ? "null" : mItem.toString();
-        }
-    }
-}
diff --git a/android/support/v7/widget/AlertDialogLayout.java b/android/support/v7/widget/AlertDialogLayout.java
deleted file mode 100644
index 6739c81..0000000
--- a/android/support/v7/widget/AlertDialogLayout.java
+++ /dev/null
@@ -1,349 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Special implementation of linear layout that's capable of laying out alert
- * dialog components.
- * <p>
- * A dialog consists of up to three panels. All panels are optional, and a
- * dialog may contain only a single panel. The panels are laid out according
- * to the following guidelines:
- * <ul>
- *     <li>topPanel: exactly wrap_content</li>
- *     <li>contentPanel OR customPanel: at most fill_parent, first priority for
- *         extra space</li>
- *     <li>buttonPanel: at least minHeight, at most wrap_content, second
- *         priority for extra space</li>
- * </ul>
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class AlertDialogLayout extends LinearLayoutCompat {
-
-    public AlertDialogLayout(@Nullable Context context) {
-        super(context);
-    }
-
-    public AlertDialogLayout(@Nullable Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (!tryOnMeasure(widthMeasureSpec, heightMeasureSpec)) {
-            // Failed to perform custom measurement, let superclass handle it.
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    private boolean tryOnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        View topPanel = null;
-        View buttonPanel = null;
-        View middlePanel = null;
-
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == View.GONE) {
-                continue;
-            }
-
-            final int id = child.getId();
-            if (id == R.id.topPanel) {
-                topPanel = child;
-            } else if (id == R.id.buttonPanel) {
-                buttonPanel = child;
-            } else if (id == R.id.contentPanel || id == R.id.customPanel) {
-                if (middlePanel != null) {
-                    // Both the content and custom are visible. Abort!
-                    return false;
-                }
-                middlePanel = child;
-            } else {
-                // Unknown top-level child. Abort!
-                return false;
-            }
-        }
-
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-
-        int childState = 0;
-        int usedHeight = getPaddingTop() + getPaddingBottom();
-
-        if (topPanel != null) {
-            topPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
-
-            usedHeight += topPanel.getMeasuredHeight();
-            childState = View.combineMeasuredStates(childState, topPanel.getMeasuredState());
-        }
-
-        int buttonHeight = 0;
-        int buttonWantsHeight = 0;
-        if (buttonPanel != null) {
-            buttonPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
-            buttonHeight = resolveMinimumHeight(buttonPanel);
-            buttonWantsHeight = buttonPanel.getMeasuredHeight() - buttonHeight;
-
-            usedHeight += buttonHeight;
-            childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState());
-        }
-
-        int middleHeight = 0;
-        if (middlePanel != null) {
-            final int childHeightSpec;
-            if (heightMode == MeasureSpec.UNSPECIFIED) {
-                childHeightSpec = MeasureSpec.UNSPECIFIED;
-            } else {
-                childHeightSpec = MeasureSpec.makeMeasureSpec(
-                        Math.max(0, heightSize - usedHeight), heightMode);
-            }
-
-            middlePanel.measure(widthMeasureSpec, childHeightSpec);
-            middleHeight = middlePanel.getMeasuredHeight();
-
-            usedHeight += middleHeight;
-            childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState());
-        }
-
-        int remainingHeight = heightSize - usedHeight;
-
-        // Time for the "real" button measure pass. If we have remaining space,
-        // make the button pane bigger up to its target height. Otherwise,
-        // just remeasure the button at whatever height it needs.
-        if (buttonPanel != null) {
-            usedHeight -= buttonHeight;
-
-            final int heightToGive = Math.min(remainingHeight, buttonWantsHeight);
-            if (heightToGive > 0) {
-                remainingHeight -= heightToGive;
-                buttonHeight += heightToGive;
-            }
-
-            final int childHeightSpec = MeasureSpec.makeMeasureSpec(
-                    buttonHeight, MeasureSpec.EXACTLY);
-            buttonPanel.measure(widthMeasureSpec, childHeightSpec);
-
-            usedHeight += buttonPanel.getMeasuredHeight();
-            childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState());
-        }
-
-        // If we still have remaining space, make the middle pane bigger up
-        // to the maximum height.
-        if (middlePanel != null && remainingHeight > 0) {
-            usedHeight -= middleHeight;
-
-            final int heightToGive = remainingHeight;
-            remainingHeight -= heightToGive;
-            middleHeight += heightToGive;
-
-            // Pass the same height mode as we're using for the dialog itself.
-            // If it's EXACTLY, then the middle pane MUST use the entire
-            // height.
-            final int childHeightSpec = MeasureSpec.makeMeasureSpec(
-                    middleHeight, heightMode);
-            middlePanel.measure(widthMeasureSpec, childHeightSpec);
-
-            usedHeight += middlePanel.getMeasuredHeight();
-            childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState());
-        }
-
-        // Compute desired width as maximum child width.
-        int maxWidth = 0;
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != View.GONE) {
-                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
-            }
-        }
-
-        maxWidth += getPaddingLeft() + getPaddingRight();
-
-        final int widthSizeAndState = View.resolveSizeAndState(
-                maxWidth, widthMeasureSpec, childState);
-        final int heightSizeAndState = View.resolveSizeAndState(
-                usedHeight, heightMeasureSpec, 0);
-        setMeasuredDimension(widthSizeAndState, heightSizeAndState);
-
-        // If the children weren't already measured EXACTLY, we need to run
-        // another measure pass to for MATCH_PARENT widths.
-        if (widthMode != MeasureSpec.EXACTLY) {
-            forceUniformWidth(count, heightMeasureSpec);
-        }
-
-        return true;
-    }
-
-    /**
-     * Remeasures child views to exactly match the layout's measured width.
-     *
-     * @param count the number of child views
-     * @param heightMeasureSpec the original height measure spec
-     */
-    private void forceUniformWidth(int count, int heightMeasureSpec) {
-        // Pretend that the linear layout has an exact size.
-        final int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(
-                getMeasuredWidth(), MeasureSpec.EXACTLY);
-
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() != GONE) {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.width == LayoutParams.MATCH_PARENT) {
-                    // Temporarily force children to reuse their old measured
-                    // height.
-                    final int oldHeight = lp.height;
-                    lp.height = child.getMeasuredHeight();
-
-                    // Remeasure with new dimensions.
-                    measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
-                    lp.height = oldHeight;
-                }
-            }
-        }
-    }
-
-    /**
-     * Attempts to resolve the minimum height of a view.
-     * <p>
-     * If the view doesn't have a minimum height set and only contains a single
-     * child, attempts to resolve the minimum height of the child view.
-     *
-     * @param v the view whose minimum height to resolve
-     * @return the minimum height
-     */
-    private static int resolveMinimumHeight(View v) {
-        final int minHeight = ViewCompat.getMinimumHeight(v);
-        if (minHeight > 0) {
-            return minHeight;
-        }
-
-        if (v instanceof ViewGroup) {
-            final ViewGroup vg = (ViewGroup) v;
-            if (vg.getChildCount() == 1) {
-                return resolveMinimumHeight(vg.getChildAt(0));
-            }
-        }
-
-        return 0;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final int paddingLeft = getPaddingLeft();
-
-        // Where right end of child should go
-        final int width = right - left;
-        final int childRight = width - getPaddingRight();
-
-        // Space available for child
-        final int childSpace = width - paddingLeft - getPaddingRight();
-
-        final int totalLength = getMeasuredHeight();
-        final int count = getChildCount();
-        final int gravity = getGravity();
-        final int majorGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int minorGravity = gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
-
-        int childTop;
-        switch (majorGravity) {
-            case Gravity.BOTTOM:
-                // totalLength contains the padding already
-                childTop = getPaddingTop() + bottom - top - totalLength;
-                break;
-
-            // totalLength contains the padding already
-            case Gravity.CENTER_VERTICAL:
-                childTop = getPaddingTop() + (bottom - top - totalLength) / 2;
-                break;
-
-            case Gravity.TOP:
-            default:
-                childTop = getPaddingTop();
-                break;
-        }
-
-        final Drawable dividerDrawable = getDividerDrawable();
-        final int dividerHeight = dividerDrawable == null ?
-                0 : dividerDrawable.getIntrinsicHeight();
-
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            if (child != null && child.getVisibility() != GONE) {
-                final int childWidth = child.getMeasuredWidth();
-                final int childHeight = child.getMeasuredHeight();
-
-                final LinearLayoutCompat.LayoutParams lp =
-                        (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                int layoutGravity = lp.gravity;
-                if (layoutGravity < 0) {
-                    layoutGravity = minorGravity;
-                }
-                final int layoutDirection = ViewCompat.getLayoutDirection(this);
-                final int absoluteGravity = GravityCompat.getAbsoluteGravity(
-                        layoutGravity, layoutDirection);
-
-                final int childLeft;
-                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-                    case Gravity.CENTER_HORIZONTAL:
-                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
-                                + lp.leftMargin - lp.rightMargin;
-                        break;
-
-                    case Gravity.RIGHT:
-                        childLeft = childRight - childWidth - lp.rightMargin;
-                        break;
-
-                    case Gravity.LEFT:
-                    default:
-                        childLeft = paddingLeft + lp.leftMargin;
-                        break;
-                }
-
-                if (hasDividerBeforeChildAt(i)) {
-                    childTop += dividerHeight;
-                }
-
-                childTop += lp.topMargin;
-                setChildFrame(child, childLeft, childTop, childWidth, childHeight);
-                childTop += childHeight + lp.bottomMargin;
-            }
-        }
-    }
-
-    private void setChildFrame(View child, int left, int top, int width, int height) {
-        child.layout(left, top, left + width, top + height);
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/AppCompatAutoCompleteTextView.java b/android/support/v7/widget/AppCompatAutoCompleteTextView.java
deleted file mode 100644
index e41bec7..0000000
--- a/android/support/v7/widget/AppCompatAutoCompleteTextView.java
+++ /dev/null
@@ -1,188 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.TintableBackgroundView;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.AutoCompleteTextView;
-
-/**
- * A {@link AutoCompleteTextView} 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
- *     {@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
- * 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
-        TintableBackgroundView {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.popupBackground
-    };
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatAutoCompleteTextView(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.autoCompleteTextViewStyle);
-    }
-
-    public AppCompatAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
-                TINT_ATTRS, defStyleAttr, 0);
-        if (a.hasValue(0)) {
-            setDropDownBackgroundDrawable(a.getDrawable(0));
-        }
-        a.recycle();
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mTextHelper = AppCompatTextHelper.create(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper.applyCompoundDrawablesTints();
-    }
-
-    @Override
-    public void setDropDownBackgroundResource(@DrawableRes int resId) {
-        setDropDownBackgroundDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mTextHelper != null) {
-            mTextHelper.applyCompoundDrawablesTints();
-        }
-    }
-
-    @Override
-    public void setTextAppearance(Context context, int resId) {
-        super.setTextAppearance(context, resId);
-        if (mTextHelper != null) {
-            mTextHelper.onSetTextAppearance(context, resId);
-        }
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return AppCompatHintHelper.onCreateInputConnection(super.onCreateInputConnection(outAttrs),
-                outAttrs, this);
-    }
-}
diff --git a/android/support/v7/widget/AppCompatBackgroundHelper.java b/android/support/v7/widget/AppCompatBackgroundHelper.java
deleted file mode 100644
index cb0ef68..0000000
--- a/android/support/v7/widget/AppCompatBackgroundHelper.java
+++ /dev/null
@@ -1,196 +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.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.View;
-
-class AppCompatBackgroundHelper {
-
-    private final View mView;
-    private final AppCompatDrawableManager mDrawableManager;
-
-    private int mBackgroundResId = -1;
-
-    private TintInfo mInternalBackgroundTint;
-    private TintInfo mBackgroundTint;
-    private TintInfo mTmpInfo;
-
-    AppCompatBackgroundHelper(View view) {
-        mView = view;
-        mDrawableManager = AppCompatDrawableManager.get();
-    }
-
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
-                R.styleable.ViewBackgroundHelper, defStyleAttr, 0);
-        try {
-            if (a.hasValue(R.styleable.ViewBackgroundHelper_android_background)) {
-                mBackgroundResId = a.getResourceId(
-                        R.styleable.ViewBackgroundHelper_android_background, -1);
-                ColorStateList tint = mDrawableManager
-                        .getTintList(mView.getContext(), mBackgroundResId);
-                if (tint != null) {
-                    setInternalBackgroundTint(tint);
-                }
-            }
-            if (a.hasValue(R.styleable.ViewBackgroundHelper_backgroundTint)) {
-                ViewCompat.setBackgroundTintList(mView,
-                        a.getColorStateList(R.styleable.ViewBackgroundHelper_backgroundTint));
-            }
-            if (a.hasValue(R.styleable.ViewBackgroundHelper_backgroundTintMode)) {
-                ViewCompat.setBackgroundTintMode(mView,
-                        DrawableUtils.parseTintMode(
-                                a.getInt(R.styleable.ViewBackgroundHelper_backgroundTintMode, -1),
-                                null));
-            }
-        } finally {
-            a.recycle();
-        }
-    }
-
-    void onSetBackgroundResource(int resId) {
-        mBackgroundResId = resId;
-        // Update the default background tint
-        setInternalBackgroundTint(mDrawableManager != null
-                ? mDrawableManager.getTintList(mView.getContext(), resId)
-                : null);
-        applySupportBackgroundTint();
-    }
-
-    void onSetBackgroundDrawable(Drawable background) {
-        mBackgroundResId = -1;
-        // We don't know that this drawable is, so we need to clear the default background tint
-        setInternalBackgroundTint(null);
-        applySupportBackgroundTint();
-    }
-
-    void setSupportBackgroundTintList(ColorStateList tint) {
-        if (mBackgroundTint == null) {
-            mBackgroundTint = new TintInfo();
-        }
-        mBackgroundTint.mTintList = tint;
-        mBackgroundTint.mHasTintList = true;
-        applySupportBackgroundTint();
-    }
-
-    ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
-    }
-
-    void setSupportBackgroundTintMode(PorterDuff.Mode tintMode) {
-        if (mBackgroundTint == null) {
-            mBackgroundTint = new TintInfo();
-        }
-        mBackgroundTint.mTintMode = tintMode;
-        mBackgroundTint.mHasTintMode = true;
-
-        applySupportBackgroundTint();
-    }
-
-    PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
-    }
-
-    void applySupportBackgroundTint() {
-        final Drawable background = mView.getBackground();
-        if (background != null) {
-            if (shouldApplyFrameworkTintUsingColorFilter()
-                    && applyFrameworkTintUsingColorFilter(background)) {
-                // This needs to be called before the internal tints below so it takes
-                // effect on any widgets using the compat tint on API 21 (EditText)
-                return;
-            }
-
-            if (mBackgroundTint != null) {
-                AppCompatDrawableManager.tintDrawable(background, mBackgroundTint,
-                        mView.getDrawableState());
-            } else if (mInternalBackgroundTint != null) {
-                AppCompatDrawableManager.tintDrawable(background, mInternalBackgroundTint,
-                        mView.getDrawableState());
-            }
-        }
-    }
-
-    void setInternalBackgroundTint(ColorStateList tint) {
-        if (tint != null) {
-            if (mInternalBackgroundTint == null) {
-                mInternalBackgroundTint = new TintInfo();
-            }
-            mInternalBackgroundTint.mTintList = tint;
-            mInternalBackgroundTint.mHasTintList = true;
-        } else {
-            mInternalBackgroundTint = null;
-        }
-        applySupportBackgroundTint();
-    }
-
-    private boolean shouldApplyFrameworkTintUsingColorFilter() {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk > 21) {
-            // On API 22+, if we're using an internal compat background tint, we're also
-            // responsible for applying any custom tint set via the framework impl
-            return mInternalBackgroundTint != null;
-        } else if (sdk == 21) {
-            // GradientDrawable doesn't implement setTintList on API 21, and since there is
-            // no nice way to unwrap DrawableContainers we have to blanket apply this
-            // on API 21
-            return true;
-        } else {
-            // API 19 and below doesn't have framework tint
-            return false;
-        }
-    }
-
-    /**
-     * Applies the framework background tint to a view, but using the compat method (ColorFilter)
-     *
-     * @return true if a tint was applied
-     */
-    private boolean applyFrameworkTintUsingColorFilter(@NonNull Drawable background) {
-        if (mTmpInfo == null) {
-            mTmpInfo = new TintInfo();
-        }
-        final TintInfo info = mTmpInfo;
-        info.clear();
-
-        final ColorStateList tintList = ViewCompat.getBackgroundTintList(mView);
-        if (tintList != null) {
-            info.mHasTintList = true;
-            info.mTintList = tintList;
-        }
-        final PorterDuff.Mode mode = ViewCompat.getBackgroundTintMode(mView);
-        if (mode != null) {
-            info.mHasTintMode = true;
-            info.mTintMode = mode;
-        }
-
-        if (info.mHasTintList || info.mHasTintMode) {
-            AppCompatDrawableManager.tintDrawable(background, info, mView.getDrawableState());
-            return true;
-        }
-
-        return false;
-    }
-}
diff --git a/android/support/v7/widget/AppCompatButton.java b/android/support/v7/widget/AppCompatButton.java
deleted file mode 100644
index 478d42d..0000000
--- a/android/support/v7/widget/AppCompatButton.java
+++ /dev/null
@@ -1,361 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-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.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 versions of the platform,
- * including:
- * <ul>
- *     <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 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,
-        AutoSizeableTextView {
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatButton(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatButton(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.buttonStyle);
-    }
-
-    public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mTextHelper = AppCompatTextHelper.create(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper.applyCompoundDrawablesTints();
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mTextHelper != null) {
-            mTextHelper.applyCompoundDrawablesTints();
-        }
-    }
-
-    @Override
-    public void setTextAppearance(Context context, int resId) {
-        super.setTextAppearance(context, resId);
-        if (mTextHelper != null) {
-            mTextHelper.onSetTextAppearance(context, resId);
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setClassName(Button.class.getName());
-    }
-
-    @RequiresApi(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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 && !PLATFORM_SUPPORTS_AUTOSIZE && mTextHelper.isAutoSizeEnabled()) {
-            mTextHelper.autoSizeText();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setAutoSizeTextTypeWithDefaults(
-            @TextViewCompat.AutoSizeTextType int autoSizeTextType) {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            super.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
-        } else {
-            if (mTextHelper != null) {
-                mTextHelper.setAutoSizeTextTypeUniformWithPresetSizes(presetSizes, unit);
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @TextViewCompat.AutoSizeTextType
-    public int getAutoSizeTextType() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeStepGranularity();
-        } else {
-            if (mTextHelper != null) {
-                return mTextHelper.getAutoSizeStepGranularity();
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int getAutoSizeMinTextSize() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeMinTextSize();
-        } else {
-            if (mTextHelper != null) {
-                return mTextHelper.getAutoSizeMinTextSize();
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int getAutoSizeMaxTextSize() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeMaxTextSize();
-        } else {
-            if (mTextHelper != null) {
-                return mTextHelper.getAutoSizeMaxTextSize();
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public int[] getAutoSizeTextAvailableSizes() {
-        if (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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.
-     * This setting will be ignored if this field is editable or selectable.
-     *
-     * This call replaces the current transformation method. Disabling this
-     * will not necessarily restore the previous behavior from before this
-     * was enabled.
-     */
-    public void setSupportAllCaps(boolean allCaps) {
-        if (mTextHelper != null) {
-            mTextHelper.setAllCaps(allCaps);
-        }
-    }
-}
diff --git a/android/support/v7/widget/AppCompatCheckBox.java b/android/support/v7/widget/AppCompatCheckBox.java
deleted file mode 100644
index 0d4f4cb..0000000
--- a/android/support/v7/widget/AppCompatCheckBox.java
+++ /dev/null
@@ -1,137 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.widget.TintableCompoundButton;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.widget.CheckBox;
-
-/**
- * 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
- *     {@link android.support.v4.widget.CompoundButtonCompat}.</li>
- *     <li>Allows setting of the background tint using {@link R.attr#buttonTint} and
- *     {@link R.attr#buttonTintMode}.</li>
- * </ul>
- *
- * <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 {
-
-    private final AppCompatCompoundButtonHelper mCompoundButtonHelper;
-
-    public AppCompatCheckBox(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatCheckBox(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.checkboxStyle);
-    }
-
-    public AppCompatCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-        mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this);
-        mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    @Override
-    public void setButtonDrawable(Drawable buttonDrawable) {
-        super.setButtonDrawable(buttonDrawable);
-        if (mCompoundButtonHelper != null) {
-            mCompoundButtonHelper.onSetButtonDrawable();
-        }
-    }
-
-    @Override
-    public void setButtonDrawable(@DrawableRes int resId) {
-        setButtonDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    public int getCompoundPaddingLeft() {
-        final int value = super.getCompoundPaddingLeft();
-        return mCompoundButtonHelper != null
-                ? mCompoundButtonHelper.getCompoundPaddingLeft(value)
-                : value;
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportButtonTintList(@Nullable ColorStateList tint) {
-        if (mCompoundButtonHelper != null) {
-            mCompoundButtonHelper.setSupportButtonTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    @Override
-    public ColorStateList getSupportButtonTintList() {
-        return mCompoundButtonHelper != null
-                ? mCompoundButtonHelper.getSupportButtonTintList()
-                : null;
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mCompoundButtonHelper != null) {
-            mCompoundButtonHelper.setSupportButtonTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    @Override
-    public PorterDuff.Mode getSupportButtonTintMode() {
-        return mCompoundButtonHelper != null
-                ? mCompoundButtonHelper.getSupportButtonTintMode()
-                : null;
-    }
-}
diff --git a/android/support/v7/widget/AppCompatCheckedTextView.java b/android/support/v7/widget/AppCompatCheckedTextView.java
deleted file mode 100644
index dca409c..0000000
--- a/android/support/v7/widget/AppCompatCheckedTextView.java
+++ /dev/null
@@ -1,90 +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.content.Context;
-import android.support.annotation.DrawableRes;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.CheckedTextView;
-
-/**
- * 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
- * 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 {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.checkMark
-    };
-
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatCheckedTextView(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatCheckedTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.checkedTextViewStyle);
-    }
-
-    public AppCompatCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        mTextHelper = AppCompatTextHelper.create(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper.applyCompoundDrawablesTints();
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
-                TINT_ATTRS, defStyleAttr, 0);
-        setCheckMarkDrawable(a.getDrawable(0));
-        a.recycle();
-    }
-
-    @Override
-    public void setCheckMarkDrawable(@DrawableRes int resId) {
-        setCheckMarkDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    public void setTextAppearance(Context context, int resId) {
-        super.setTextAppearance(context, resId);
-        if (mTextHelper != null) {
-            mTextHelper.onSetTextAppearance(context, resId);
-        }
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mTextHelper != null) {
-            mTextHelper.applyCompoundDrawablesTints();
-        }
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return AppCompatHintHelper.onCreateInputConnection(super.onCreateInputConnection(outAttrs),
-                outAttrs, this);
-    }
-}
diff --git a/android/support/v7/widget/AppCompatCompoundButtonHelper.java b/android/support/v7/widget/AppCompatCompoundButtonHelper.java
deleted file mode 100644
index 339706b..0000000
--- a/android/support/v7/widget/AppCompatCompoundButtonHelper.java
+++ /dev/null
@@ -1,145 +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.widget;
-
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.widget.CompoundButtonCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.widget.CompoundButton;
-
-class AppCompatCompoundButtonHelper {
-
-    private final CompoundButton mView;
-
-    private ColorStateList mButtonTintList = null;
-    private PorterDuff.Mode mButtonTintMode = null;
-    private boolean mHasButtonTint = false;
-    private boolean mHasButtonTintMode = false;
-
-    private boolean mSkipNextApply;
-
-    /**
-     * Interface which allows us to directly set a button, bypass any calls back to ourselves.
-     */
-    interface DirectSetButtonDrawableInterface {
-        void setButtonDrawable(Drawable buttonDrawable);
-    }
-
-    AppCompatCompoundButtonHelper(CompoundButton view) {
-        mView = view;
-    }
-
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        TypedArray a = mView.getContext().obtainStyledAttributes(attrs, R.styleable.CompoundButton,
-                defStyleAttr, 0);
-        try {
-            if (a.hasValue(R.styleable.CompoundButton_android_button)) {
-                final int resourceId = a.getResourceId(
-                        R.styleable.CompoundButton_android_button, 0);
-                if (resourceId != 0) {
-                    mView.setButtonDrawable(
-                            AppCompatResources.getDrawable(mView.getContext(), resourceId));
-                }
-            }
-            if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
-                CompoundButtonCompat.setButtonTintList(mView,
-                        a.getColorStateList(R.styleable.CompoundButton_buttonTint));
-            }
-            if (a.hasValue(R.styleable.CompoundButton_buttonTintMode)) {
-                CompoundButtonCompat.setButtonTintMode(mView,
-                        DrawableUtils.parseTintMode(
-                                a.getInt(R.styleable.CompoundButton_buttonTintMode, -1),
-                                null));
-            }
-        } finally {
-            a.recycle();
-        }
-    }
-
-    void setSupportButtonTintList(ColorStateList tint) {
-        mButtonTintList = tint;
-        mHasButtonTint = true;
-
-        applyButtonTint();
-    }
-
-    ColorStateList getSupportButtonTintList() {
-        return mButtonTintList;
-    }
-
-    void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
-        mButtonTintMode = tintMode;
-        mHasButtonTintMode = true;
-
-        applyButtonTint();
-    }
-
-    PorterDuff.Mode getSupportButtonTintMode() {
-        return mButtonTintMode;
-    }
-
-    void onSetButtonDrawable() {
-        if (mSkipNextApply) {
-            mSkipNextApply = false;
-            return;
-        }
-
-        mSkipNextApply = true;
-        applyButtonTint();
-    }
-
-    void applyButtonTint() {
-        Drawable buttonDrawable = CompoundButtonCompat.getButtonDrawable(mView);
-
-        if (buttonDrawable != null && (mHasButtonTint || mHasButtonTintMode)) {
-            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
-            buttonDrawable = buttonDrawable.mutate();
-            if (mHasButtonTint) {
-                DrawableCompat.setTintList(buttonDrawable, mButtonTintList);
-            }
-            if (mHasButtonTintMode) {
-                DrawableCompat.setTintMode(buttonDrawable, mButtonTintMode);
-            }
-            // The drawable (or one of its children) may not have been
-            // stateful before applying the tint, so let's try again.
-            if (buttonDrawable.isStateful()) {
-                buttonDrawable.setState(mView.getDrawableState());
-            }
-            mView.setButtonDrawable(buttonDrawable);
-        }
-    }
-
-    int getCompoundPaddingLeft(int superValue) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            // Before JB-MR1 the button drawable wasn't taken into account for padding. We'll
-            // workaround that here
-            Drawable buttonDrawable = CompoundButtonCompat.getButtonDrawable(mView);
-            if (buttonDrawable != null) {
-                superValue += buttonDrawable.getIntrinsicWidth();
-            }
-        }
-        return superValue;
-    }
-}
diff --git a/android/support/v7/widget/AppCompatDrawableManager.java b/android/support/v7/widget/AppCompatDrawableManager.java
deleted file mode 100644
index 36e91a3..0000000
--- a/android/support/v7/widget/AppCompatDrawableManager.java
+++ /dev/null
@@ -1,800 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v4.graphics.ColorUtils.compositeColors;
-import static android.support.v7.content.res.AppCompatResources.getColorStateList;
-import static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
-import static android.support.v7.widget.ThemeUtils.getThemeAttrColor;
-import static android.support.v7.widget.ThemeUtils.getThemeAttrColorStateList;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Drawable.ConstantState;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.support.annotation.ColorInt;
-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.graphics.drawable.AnimatedVectorDrawableCompat;
-import android.support.graphics.drawable.VectorDrawableCompat;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.util.LongSparseArray;
-import android.support.v4.util.LruCache;
-import android.support.v4.util.SparseArrayCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.lang.ref.WeakReference;
-import java.util.WeakHashMap;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class AppCompatDrawableManager {
-
-    private interface InflateDelegate {
-        Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
-                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme);
-    }
-
-    private static final String TAG = "AppCompatDrawableManag";
-    private static final boolean DEBUG = false;
-    private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
-    private static final String SKIP_DRAWABLE_TAG = "appcompat_skip_skip";
-
-    private static final String PLATFORM_VD_CLAZZ = "android.graphics.drawable.VectorDrawable";
-
-    private static AppCompatDrawableManager INSTANCE;
-
-    public static AppCompatDrawableManager get() {
-        if (INSTANCE == null) {
-            INSTANCE = new AppCompatDrawableManager();
-            installDefaultInflateDelegates(INSTANCE);
-        }
-        return INSTANCE;
-    }
-
-    private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) {
-        // This sdk version check will affect src:appCompat code path.
-        // Although VectorDrawable exists in Android framework from Lollipop, AppCompat will use the
-        // VectorDrawableCompat before Nougat to utilize the bug fixes in VectorDrawableCompat.
-        if (Build.VERSION.SDK_INT < 24) {
-            manager.addDelegate("vector", new VdcInflateDelegate());
-            manager.addDelegate("animated-vector", new AvdcInflateDelegate());
-        }
-    }
-
-    private static final ColorFilterLruCache COLOR_FILTER_CACHE = new ColorFilterLruCache(6);
-
-    /**
-     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal},
-     * using the default mode using a raw color filter.
-     */
-    private static final int[] COLORFILTER_TINT_COLOR_CONTROL_NORMAL = {
-            R.drawable.abc_textfield_search_default_mtrl_alpha,
-            R.drawable.abc_textfield_default_mtrl_alpha,
-            R.drawable.abc_ab_share_pack_mtrl_alpha
-    };
-
-    /**
-     * Drawables which should be tinted with the value of {@code R.attr.colorControlNormal}, using
-     * {@link DrawableCompat}'s tinting functionality.
-     */
-    private static final int[] TINT_COLOR_CONTROL_NORMAL = {
-            R.drawable.abc_ic_commit_search_api_mtrl_alpha,
-            R.drawable.abc_seekbar_tick_mark_material,
-            R.drawable.abc_ic_menu_share_mtrl_alpha,
-            R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
-            R.drawable.abc_ic_menu_cut_mtrl_alpha,
-            R.drawable.abc_ic_menu_selectall_mtrl_alpha,
-            R.drawable.abc_ic_menu_paste_mtrl_am_alpha
-    };
-
-    /**
-     * Drawables which should be tinted with the value of {@code R.attr.colorControlActivated},
-     * using a color filter.
-     */
-    private static final int[] COLORFILTER_COLOR_CONTROL_ACTIVATED = {
-            R.drawable.abc_textfield_activated_mtrl_alpha,
-            R.drawable.abc_textfield_search_activated_mtrl_alpha,
-            R.drawable.abc_cab_background_top_mtrl_alpha,
-            R.drawable.abc_text_cursor_material,
-            R.drawable.abc_text_select_handle_left_mtrl_dark,
-            R.drawable.abc_text_select_handle_middle_mtrl_dark,
-            R.drawable.abc_text_select_handle_right_mtrl_dark,
-            R.drawable.abc_text_select_handle_left_mtrl_light,
-            R.drawable.abc_text_select_handle_middle_mtrl_light,
-            R.drawable.abc_text_select_handle_right_mtrl_light
-    };
-
-    /**
-     * Drawables which should be tinted with the value of {@code android.R.attr.colorBackground},
-     * using the {@link android.graphics.PorterDuff.Mode#MULTIPLY} mode and a color filter.
-     */
-    private static final int[] COLORFILTER_COLOR_BACKGROUND_MULTIPLY = {
-            R.drawable.abc_popup_background_mtrl_mult,
-            R.drawable.abc_cab_background_internal_bg,
-            R.drawable.abc_menu_hardkey_panel_mtrl_mult
-    };
-
-    /**
-     * Drawables which should be tinted using a state list containing values of
-     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
-     */
-    private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
-            R.drawable.abc_tab_indicator_material,
-            R.drawable.abc_textfield_search_material
-    };
-
-    /**
-     * Drawables which should be tinted using a state list containing values of
-     * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked
-     * state.
-     */
-    private static final int[] TINT_CHECKABLE_BUTTON_LIST = {
-            R.drawable.abc_btn_check_material,
-            R.drawable.abc_btn_radio_material
-    };
-
-    private WeakHashMap<Context, SparseArrayCompat<ColorStateList>> mTintLists;
-    private ArrayMap<String, InflateDelegate> mDelegates;
-    private SparseArrayCompat<String> mKnownDrawableIdTags;
-
-    private final Object mDrawableCacheLock = new Object();
-    private final WeakHashMap<Context, LongSparseArray<WeakReference<Drawable.ConstantState>>>
-            mDrawableCaches = new WeakHashMap<>(0);
-
-    private TypedValue mTypedValue;
-
-    private boolean mHasCheckedVectorDrawableSetup;
-
-    public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
-        return getDrawable(context, resId, false);
-    }
-
-    Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
-            boolean failIfNotKnown) {
-        checkVectorDrawableSetup(context);
-
-        Drawable drawable = loadDrawableFromDelegates(context, resId);
-        if (drawable == null) {
-            drawable = createDrawableIfNeeded(context, resId);
-        }
-        if (drawable == null) {
-            drawable = ContextCompat.getDrawable(context, resId);
-        }
-
-        if (drawable != null) {
-            // Tint it if needed
-            drawable = tintDrawable(context, resId, failIfNotKnown, drawable);
-        }
-        if (drawable != null) {
-            // See if we need to 'fix' the drawable
-            DrawableUtils.fixDrawable(drawable);
-        }
-        return drawable;
-    }
-
-    public void onConfigurationChanged(@NonNull Context context) {
-        synchronized (mDrawableCacheLock) {
-            LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context);
-            if (cache != null) {
-                // Crude, but we'll just clear the cache when the configuration changes
-                cache.clear();
-            }
-        }
-    }
-
-    private static long createCacheKey(TypedValue tv) {
-        return (((long) tv.assetCookie) << 32) | tv.data;
-    }
-
-    private Drawable createDrawableIfNeeded(@NonNull Context context,
-            @DrawableRes final int resId) {
-        if (mTypedValue == null) {
-            mTypedValue = new TypedValue();
-        }
-        final TypedValue tv = mTypedValue;
-        context.getResources().getValue(resId, tv, true);
-        final long key = createCacheKey(tv);
-
-        Drawable dr = getCachedDrawable(context, key);
-        if (dr != null) {
-            // If we got a cached drawable, return it
-            return dr;
-        }
-
-        // Else we need to try and create one...
-        if (resId == R.drawable.abc_cab_background_top_material) {
-            dr = new LayerDrawable(new Drawable[]{
-                    getDrawable(context, R.drawable.abc_cab_background_internal_bg),
-                    getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha)
-            });
-        }
-
-        if (dr != null) {
-            dr.setChangingConfigurations(tv.changingConfigurations);
-            // If we reached here then we created a new drawable, add it to the cache
-            addDrawableToCache(context, key, dr);
-        }
-
-        return dr;
-    }
-
-    private Drawable tintDrawable(@NonNull Context context, @DrawableRes int resId,
-            boolean failIfNotKnown, @NonNull Drawable drawable) {
-        final ColorStateList tintList = getTintList(context, resId);
-        if (tintList != null) {
-            // First mutate the Drawable, then wrap it and set the tint list
-            if (DrawableUtils.canSafelyMutateDrawable(drawable)) {
-                drawable = drawable.mutate();
-            }
-            drawable = DrawableCompat.wrap(drawable);
-            DrawableCompat.setTintList(drawable, tintList);
-
-            // If there is a blending mode specified for the drawable, use it
-            final PorterDuff.Mode tintMode = getTintMode(resId);
-            if (tintMode != null) {
-                DrawableCompat.setTintMode(drawable, tintMode);
-            }
-        } else if (resId == R.drawable.abc_seekbar_track_material) {
-            LayerDrawable ld = (LayerDrawable) drawable;
-            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
-                    getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
-            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
-                    getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
-            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
-                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
-        } else if (resId == R.drawable.abc_ratingbar_material
-                || resId == R.drawable.abc_ratingbar_indicator_material
-                || resId == R.drawable.abc_ratingbar_small_material) {
-            LayerDrawable ld = (LayerDrawable) drawable;
-            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
-                    getDisabledThemeAttrColor(context, R.attr.colorControlNormal),
-                    DEFAULT_MODE);
-            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
-                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
-            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
-                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
-        } else {
-            final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable);
-            if (!tinted && failIfNotKnown) {
-                // If we didn't tint using a ColorFilter, and we're set to fail if we don't
-                // know the id, return null
-                drawable = null;
-            }
-        }
-        return drawable;
-    }
-
-    private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) {
-        if (mDelegates != null && !mDelegates.isEmpty()) {
-            if (mKnownDrawableIdTags != null) {
-                final String cachedTagName = mKnownDrawableIdTags.get(resId);
-                if (SKIP_DRAWABLE_TAG.equals(cachedTagName)
-                        || (cachedTagName != null && mDelegates.get(cachedTagName) == null)) {
-                    // If we don't have a delegate for the drawable tag, or we've been set to
-                    // skip it, fail fast and return null
-                    if (DEBUG) {
-                        Log.d(TAG, "[loadDrawableFromDelegates] Skipping drawable: "
-                                + context.getResources().getResourceName(resId));
-                    }
-                    return null;
-                }
-            } else {
-                // Create an id cache as we'll need one later
-                mKnownDrawableIdTags = new SparseArrayCompat<>();
-            }
-
-            if (mTypedValue == null) {
-                mTypedValue = new TypedValue();
-            }
-            final TypedValue tv = mTypedValue;
-            final Resources res = context.getResources();
-            res.getValue(resId, tv, true);
-
-            final long key = createCacheKey(tv);
-
-            Drawable dr = getCachedDrawable(context, key);
-            if (dr != null) {
-                if (DEBUG) {
-                    Log.i(TAG, "[loadDrawableFromDelegates] Returning cached drawable: " +
-                            context.getResources().getResourceName(resId));
-                }
-                // We have a cached drawable, return it!
-                return dr;
-            }
-
-            if (tv.string != null && tv.string.toString().endsWith(".xml")) {
-                // If the resource is an XML file, let's try and parse it
-                try {
-                    @SuppressLint("ResourceType") final XmlPullParser parser = res.getXml(resId);
-                    final AttributeSet attrs = Xml.asAttributeSet(parser);
-                    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");
-                    }
-
-                    final String tagName = parser.getName();
-                    // Add the tag name to the cache
-                    mKnownDrawableIdTags.append(resId, tagName);
-
-                    // Now try and find a delegate for the tag name and inflate if found
-                    final InflateDelegate delegate = mDelegates.get(tagName);
-                    if (delegate != null) {
-                        dr = delegate.createFromXmlInner(context, parser, attrs,
-                                context.getTheme());
-                    }
-                    if (dr != null) {
-                        // Add it to the drawable cache
-                        dr.setChangingConfigurations(tv.changingConfigurations);
-                        if (addDrawableToCache(context, key, dr) && DEBUG) {
-                            Log.i(TAG, "[loadDrawableFromDelegates] Saved drawable to cache: " +
-                                    context.getResources().getResourceName(resId));
-                        }
-                    }
-                } catch (Exception e) {
-                    Log.e(TAG, "Exception while inflating drawable", e);
-                }
-            }
-            if (dr == null) {
-                // If we reach here then the delegate inflation of the resource failed. Mark it as
-                // bad so we skip the id next time
-                mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG);
-            }
-            return dr;
-        }
-
-        return null;
-    }
-
-    private Drawable getCachedDrawable(@NonNull final Context context, final long key) {
-        synchronized (mDrawableCacheLock) {
-            final LongSparseArray<WeakReference<ConstantState>> cache
-                    = mDrawableCaches.get(context);
-            if (cache == null) {
-                return null;
-            }
-
-            final WeakReference<ConstantState> wr = cache.get(key);
-            if (wr != null) {
-                // We have the key, and the secret
-                ConstantState entry = wr.get();
-                if (entry != null) {
-                    return entry.newDrawable(context.getResources());
-                } else {
-                    // Our entry has been purged
-                    cache.delete(key);
-                }
-            }
-        }
-        return null;
-    }
-
-    private boolean addDrawableToCache(@NonNull final Context context, final long key,
-            @NonNull final Drawable drawable) {
-        final ConstantState cs = drawable.getConstantState();
-        if (cs != null) {
-            synchronized (mDrawableCacheLock) {
-                LongSparseArray<WeakReference<ConstantState>> cache = mDrawableCaches.get(context);
-                if (cache == null) {
-                    cache = new LongSparseArray<>();
-                    mDrawableCaches.put(context, cache);
-                }
-                cache.put(key, new WeakReference<>(cs));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    Drawable onDrawableLoadedFromResources(@NonNull Context context,
-            @NonNull VectorEnabledTintResources resources, @DrawableRes final int resId) {
-        Drawable drawable = loadDrawableFromDelegates(context, resId);
-        if (drawable == null) {
-            drawable = resources.superGetDrawable(resId);
-        }
-        if (drawable != null) {
-            return tintDrawable(context, resId, false, drawable);
-        }
-        return null;
-    }
-
-    static boolean tintDrawableUsingColorFilter(@NonNull Context context,
-            @DrawableRes final int resId, @NonNull Drawable drawable) {
-        PorterDuff.Mode tintMode = DEFAULT_MODE;
-        boolean colorAttrSet = false;
-        int colorAttr = 0;
-        int alpha = -1;
-
-        if (arrayContains(COLORFILTER_TINT_COLOR_CONTROL_NORMAL, resId)) {
-            colorAttr = R.attr.colorControlNormal;
-            colorAttrSet = true;
-        } else if (arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, resId)) {
-            colorAttr = R.attr.colorControlActivated;
-            colorAttrSet = true;
-        } else if (arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, resId)) {
-            colorAttr = android.R.attr.colorBackground;
-            colorAttrSet = true;
-            tintMode = PorterDuff.Mode.MULTIPLY;
-        } else if (resId == R.drawable.abc_list_divider_mtrl_alpha) {
-            colorAttr = android.R.attr.colorForeground;
-            colorAttrSet = true;
-            alpha = Math.round(0.16f * 255);
-        } else if (resId == R.drawable.abc_dialog_material_background) {
-            colorAttr = android.R.attr.colorBackground;
-            colorAttrSet = true;
-        }
-
-        if (colorAttrSet) {
-            if (DrawableUtils.canSafelyMutateDrawable(drawable)) {
-                drawable = drawable.mutate();
-            }
-
-            final int color = getThemeAttrColor(context, colorAttr);
-            drawable.setColorFilter(getPorterDuffColorFilter(color, tintMode));
-
-            if (alpha != -1) {
-                drawable.setAlpha(alpha);
-            }
-
-            if (DEBUG) {
-                Log.d(TAG, "[tintDrawableUsingColorFilter] Tinted "
-                        + context.getResources().getResourceName(resId) +
-                        " with color: #" + Integer.toHexString(color));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    private void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
-        if (mDelegates == null) {
-            mDelegates = new ArrayMap<>();
-        }
-        mDelegates.put(tagName, delegate);
-    }
-
-    private void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
-        if (mDelegates != null && mDelegates.get(tagName) == delegate) {
-            mDelegates.remove(tagName);
-        }
-    }
-
-    private static boolean arrayContains(int[] array, int value) {
-        for (int id : array) {
-            if (id == value) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    static PorterDuff.Mode getTintMode(final int resId) {
-        PorterDuff.Mode mode = null;
-
-        if (resId == R.drawable.abc_switch_thumb_material) {
-            mode = PorterDuff.Mode.MULTIPLY;
-        }
-
-        return mode;
-    }
-
-    ColorStateList getTintList(@NonNull Context context, @DrawableRes int resId) {
-        // Try the cache first (if it exists)
-        ColorStateList tint = getTintListFromCache(context, resId);
-
-        if (tint == null) {
-            // ...if the cache did not contain a color state list, try and create one
-            if (resId == R.drawable.abc_edit_text_material) {
-                tint = getColorStateList(context, R.color.abc_tint_edittext);
-            } 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 = createSwitchThumbColorStateList(context);
-            } else if (resId == R.drawable.abc_btn_default_mtrl_shape) {
-                tint = createDefaultButtonColorStateList(context);
-            } else if (resId == R.drawable.abc_btn_borderless_material) {
-                tint = createBorderlessButtonColorStateList(context);
-            } else if (resId == R.drawable.abc_btn_colored_material) {
-                tint = createColoredButtonColorStateList(context);
-            } else if (resId == R.drawable.abc_spinner_mtrl_am_alpha
-                    || resId == R.drawable.abc_spinner_textfield_background_material) {
-                tint = getColorStateList(context, R.color.abc_tint_spinner);
-            } else if (arrayContains(TINT_COLOR_CONTROL_NORMAL, resId)) {
-                tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
-            } else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
-                tint = getColorStateList(context, R.color.abc_tint_default);
-            } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
-                tint = getColorStateList(context, R.color.abc_tint_btn_checkable);
-            } else if (resId == R.drawable.abc_seekbar_thumb_material) {
-                tint = getColorStateList(context, R.color.abc_tint_seek_thumb);
-            }
-
-            if (tint != null) {
-                addTintListToCache(context, resId, tint);
-            }
-        }
-        return tint;
-    }
-
-    private ColorStateList getTintListFromCache(@NonNull Context context, @DrawableRes int resId) {
-        if (mTintLists != null) {
-            final SparseArrayCompat<ColorStateList> tints = mTintLists.get(context);
-            return tints != null ? tints.get(resId) : null;
-        }
-        return null;
-    }
-
-    private void addTintListToCache(@NonNull Context context, @DrawableRes int resId,
-            @NonNull ColorStateList tintList) {
-        if (mTintLists == null) {
-            mTintLists = new WeakHashMap<>();
-        }
-        SparseArrayCompat<ColorStateList> themeTints = mTintLists.get(context);
-        if (themeTints == null) {
-            themeTints = new SparseArrayCompat<>();
-            mTintLists.put(context, themeTints);
-        }
-        themeTints.append(resId, tintList);
-    }
-
-    private ColorStateList createDefaultButtonColorStateList(@NonNull Context context) {
-        return createButtonColorStateList(context,
-                getThemeAttrColor(context, R.attr.colorButtonNormal));
-    }
-
-    private ColorStateList createBorderlessButtonColorStateList(@NonNull Context context) {
-        // We ignore the custom tint for borderless buttons
-        return createButtonColorStateList(context, Color.TRANSPARENT);
-    }
-
-    private ColorStateList createColoredButtonColorStateList(@NonNull Context context) {
-        return createButtonColorStateList(context,
-                getThemeAttrColor(context, R.attr.colorAccent));
-    }
-
-    private ColorStateList createButtonColorStateList(@NonNull final Context context,
-            @ColorInt final int baseColor) {
-        final int[][] states = new int[4][];
-        final int[] colors = new int[4];
-        int i = 0;
-
-        final int colorControlHighlight = getThemeAttrColor(context, R.attr.colorControlHighlight);
-        final int disabledColor = getDisabledThemeAttrColor(context, R.attr.colorButtonNormal);
-
-        // Disabled state
-        states[i] = ThemeUtils.DISABLED_STATE_SET;
-        colors[i] = disabledColor;
-        i++;
-
-        states[i] = ThemeUtils.PRESSED_STATE_SET;
-        colors[i] = compositeColors(colorControlHighlight, baseColor);
-        i++;
-
-        states[i] = ThemeUtils.FOCUSED_STATE_SET;
-        colors[i] = compositeColors(colorControlHighlight, baseColor);
-        i++;
-
-        // Default enabled state
-        states[i] = ThemeUtils.EMPTY_STATE_SET;
-        colors[i] = baseColor;
-        i++;
-
-        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) {
-            super(maxSize);
-        }
-
-        PorterDuffColorFilter get(int color, PorterDuff.Mode mode) {
-            return get(generateCacheKey(color, mode));
-        }
-
-        PorterDuffColorFilter put(int color, PorterDuff.Mode mode, PorterDuffColorFilter filter) {
-            return put(generateCacheKey(color, mode), filter);
-        }
-
-        private static int generateCacheKey(int color, PorterDuff.Mode mode) {
-            int hashCode = 1;
-            hashCode = 31 * hashCode + color;
-            hashCode = 31 * hashCode + mode.hashCode();
-            return hashCode;
-        }
-    }
-
-    static void tintDrawable(Drawable drawable, TintInfo tint, int[] state) {
-        if (DrawableUtils.canSafelyMutateDrawable(drawable)
-                && drawable.mutate() != drawable) {
-            Log.d(TAG, "Mutated drawable is not the same instance as the input.");
-            return;
-        }
-
-        if (tint.mHasTintList || tint.mHasTintMode) {
-            drawable.setColorFilter(createTintFilter(
-                    tint.mHasTintList ? tint.mTintList : null,
-                    tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE,
-                    state));
-        } else {
-            drawable.clearColorFilter();
-        }
-
-        if (Build.VERSION.SDK_INT <= 23) {
-            // Pre-v23 there is no guarantee that a state change will invoke an invalidation,
-            // so we force it ourselves
-            drawable.invalidateSelf();
-        }
-    }
-
-    private static PorterDuffColorFilter createTintFilter(ColorStateList tint,
-            PorterDuff.Mode tintMode, final int[] state) {
-        if (tint == null || tintMode == null) {
-            return null;
-        }
-        final int color = tint.getColorForState(state, Color.TRANSPARENT);
-        return getPorterDuffColorFilter(color, tintMode);
-    }
-
-    public static PorterDuffColorFilter getPorterDuffColorFilter(int color, PorterDuff.Mode mode) {
-        // First, lets see if the cache already contains the color filter
-        PorterDuffColorFilter filter = COLOR_FILTER_CACHE.get(color, mode);
-
-        if (filter == null) {
-            // Cache miss, so create a color filter and add it to the cache
-            filter = new PorterDuffColorFilter(color, mode);
-            COLOR_FILTER_CACHE.put(color, mode, filter);
-        }
-
-        return filter;
-    }
-
-    private static void setPorterDuffColorFilter(Drawable d, int color, PorterDuff.Mode mode) {
-        if (DrawableUtils.canSafelyMutateDrawable(d)) {
-            d = d.mutate();
-        }
-        d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode));
-    }
-
-    private void checkVectorDrawableSetup(@NonNull Context context) {
-        if (mHasCheckedVectorDrawableSetup) {
-            // We've already checked so return now...
-            return;
-        }
-        // Here we will check that a known Vector drawable resource inside AppCompat can be
-        // correctly decoded
-        mHasCheckedVectorDrawableSetup = true;
-        final Drawable d = getDrawable(context, R.drawable.abc_vector_test);
-        if (d == null || !isVectorDrawable(d)) {
-            mHasCheckedVectorDrawableSetup = false;
-            throw new IllegalStateException("This app has been built with an incorrect "
-                    + "configuration. Please configure your build for VectorDrawableCompat.");
-        }
-    }
-
-    private static boolean isVectorDrawable(@NonNull Drawable d) {
-        return d instanceof VectorDrawableCompat
-                || PLATFORM_VD_CLAZZ.equals(d.getClass().getName());
-    }
-
-    private static class VdcInflateDelegate implements InflateDelegate {
-        VdcInflateDelegate() {
-        }
-
-        @Override
-        public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
-                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
-            try {
-                return VectorDrawableCompat
-                        .createFromXmlInner(context.getResources(), parser, attrs, theme);
-            } catch (Exception e) {
-                Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e);
-                return null;
-            }
-        }
-    }
-
-    @RequiresApi(11)
-    private static class AvdcInflateDelegate implements InflateDelegate {
-        AvdcInflateDelegate() {
-        }
-
-        @Override
-        public Drawable createFromXmlInner(@NonNull Context context, @NonNull XmlPullParser parser,
-                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
-            try {
-                return AnimatedVectorDrawableCompat
-                        .createFromXmlInner(context, context.getResources(), parser, attrs, theme);
-            } catch (Exception e) {
-                Log.e("AvdcInflateDelegate", "Exception while inflating <animated-vector>", e);
-                return null;
-            }
-        }
-    }
-}
diff --git a/android/support/v7/widget/AppCompatEditText.java b/android/support/v7/widget/AppCompatEditText.java
deleted file mode 100644
index fdda68e..0000000
--- a/android/support/v7/widget/AppCompatEditText.java
+++ /dev/null
@@ -1,186 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.os.BuildCompat;
-import android.support.v4.view.TintableBackgroundView;
-import android.support.v7.appcompat.R;
-import android.text.Editable;
-import android.util.AttributeSet;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.EditText;
-
-/**
- * A {@link EditText} 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
- *     {@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 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 {
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatEditText(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatEditText(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.editTextStyle);
-    }
-
-    public AppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mTextHelper = AppCompatTextHelper.create(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper.applyCompoundDrawablesTints();
-    }
-
-    /**
-     * Return the text that the view is displaying. If an editable text has not been set yet, this
-     * will return null.
-     */
-    @Override
-    @Nullable public Editable getText() {
-        if (BuildCompat.isAtLeastP()) {
-            return super.getText();
-        }
-        // A bug pre-P makes getText() crash if called before the first setText due to a cast, so
-        // retrieve the editable text.
-        return super.getEditableText();
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mTextHelper != null) {
-            mTextHelper.applyCompoundDrawablesTints();
-        }
-    }
-
-    @Override
-    public void setTextAppearance(Context context, int resId) {
-        super.setTextAppearance(context, resId);
-        if (mTextHelper != null) {
-            mTextHelper.onSetTextAppearance(context, resId);
-        }
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return AppCompatHintHelper.onCreateInputConnection(super.onCreateInputConnection(outAttrs),
-                outAttrs, this);
-    }
-}
diff --git a/android/support/v7/widget/AppCompatHintHelper.java b/android/support/v7/widget/AppCompatHintHelper.java
deleted file mode 100644
index 0d30fb7..0000000
--- a/android/support/v7/widget/AppCompatHintHelper.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.widget;
-
-import android.view.View;
-import android.view.ViewParent;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-class AppCompatHintHelper {
-
-    static InputConnection onCreateInputConnection(InputConnection ic, EditorInfo outAttrs,
-            View view) {
-        if (ic != null && outAttrs.hintText == null) {
-            // If we don't have a hint and the parent implements WithHint, use its hint for the
-            // EditorInfo. This allows us to display a hint in 'extract mode'.
-            ViewParent parent = view.getParent();
-            while (parent instanceof View) {
-                if (parent instanceof WithHint) {
-                    outAttrs.hintText = ((WithHint) parent).getHint();
-                    break;
-                }
-                parent = parent.getParent();
-            }
-        }
-        return ic;
-    }
-
-}
diff --git a/android/support/v7/widget/AppCompatImageButton.java b/android/support/v7/widget/AppCompatImageButton.java
deleted file mode 100644
index b2b1f10..0000000
--- a/android/support/v7/widget/AppCompatImageButton.java
+++ /dev/null
@@ -1,253 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.TintableBackgroundView;
-import android.support.v4.widget.ImageViewCompat;
-import android.support.v4.widget.TintableImageSourceView;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-
-/**
- * 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
- *     {@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>Allows dynamic tint of its image via the image tint methods in
- *     {@link ImageViewCompat}.</li>
- *     <li>Allows setting of the image tint using {@link R.attr#tint} and
- *     {@link R.attr#tintMode}.</li>
- * </ul>
- *
- * <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 {
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatImageHelper mImageHelper;
-
-    public AppCompatImageButton(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatImageButton(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.imageButtonStyle);
-    }
-
-    public AppCompatImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mImageHelper = new AppCompatImageHelper(this);
-        mImageHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    @Override
-    public void setImageResource(@DrawableRes int resId) {
-        // Intercept this call and instead retrieve the Drawable via the image helper
-        mImageHelper.setImageResource(resId);
-    }
-
-    @Override
-    public void setImageDrawable(@Nullable Drawable drawable) {
-        super.setImageDrawable(drawable);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public void setImageBitmap(Bitmap bm) {
-        super.setImageBitmap(bm);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public void setImageURI(@Nullable Uri uri) {
-        super.setImageURI(uri);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#setImageTintList(ImageView, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportImageTintList(@Nullable ColorStateList tint) {
-        if (mImageHelper != null) {
-            mImageHelper.setSupportImageTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#getImageTintList(ImageView)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportImageTintList() {
-        return mImageHelper != null
-                ? mImageHelper.getSupportImageTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#setImageTintMode(ImageView, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportImageTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mImageHelper != null) {
-            mImageHelper.setSupportImageTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#getImageTintMode(ImageView)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportImageTintMode() {
-        return mImageHelper != null
-                ? mImageHelper.getSupportImageTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return mImageHelper.hasOverlappingRendering() && super.hasOverlappingRendering();
-    }
-}
diff --git a/android/support/v7/widget/AppCompatImageHelper.java b/android/support/v7/widget/AppCompatImageHelper.java
deleted file mode 100644
index 12b8520..0000000
--- a/android/support/v7/widget/AppCompatImageHelper.java
+++ /dev/null
@@ -1,219 +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.widget;
-
-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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.widget.ImageViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class AppCompatImageHelper {
-    private final ImageView mView;
-
-    private TintInfo mInternalImageTint;
-    private TintInfo mImageTint;
-    private TintInfo mTmpInfo;
-
-    public AppCompatImageHelper(ImageView view) {
-        mView = view;
-    }
-
-    public void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
-                R.styleable.AppCompatImageView, defStyleAttr, 0);
-        try {
-            Drawable drawable = mView.getDrawable();
-            if (drawable == null) {
-                // If the view doesn't already have a drawable (from android:src), try loading
-                // it from srcCompat
-                final int id = a.getResourceId(R.styleable.AppCompatImageView_srcCompat, -1);
-                if (id != -1) {
-                    drawable = AppCompatResources.getDrawable(mView.getContext(), id);
-                    if (drawable != null) {
-                        mView.setImageDrawable(drawable);
-                    }
-                }
-            }
-
-            if (drawable != null) {
-                DrawableUtils.fixDrawable(drawable);
-            }
-
-            if (a.hasValue(R.styleable.AppCompatImageView_tint)) {
-                ImageViewCompat.setImageTintList(mView,
-                        a.getColorStateList(R.styleable.AppCompatImageView_tint));
-            }
-            if (a.hasValue(R.styleable.AppCompatImageView_tintMode)) {
-                ImageViewCompat.setImageTintMode(mView,
-                        DrawableUtils.parseTintMode(
-                                a.getInt(R.styleable.AppCompatImageView_tintMode, -1), null));
-            }
-        } finally {
-            a.recycle();
-        }
-    }
-
-    public void setImageResource(int resId) {
-        if (resId != 0) {
-            final Drawable d = AppCompatResources.getDrawable(mView.getContext(), resId);
-            if (d != null) {
-                DrawableUtils.fixDrawable(d);
-            }
-            mView.setImageDrawable(d);
-        } else {
-            mView.setImageDrawable(null);
-        }
-
-        applySupportImageTint();
-    }
-
-    boolean hasOverlappingRendering() {
-        final Drawable background = mView.getBackground();
-        if (Build.VERSION.SDK_INT >= 21
-                && background instanceof android.graphics.drawable.RippleDrawable) {
-            // RippleDrawable has an issue on L+ when used with an alpha animation.
-            // This workaround should be disabled when the platform bug is fixed. See b/27715789
-            return false;
-        }
-        return true;
-    }
-
-    void setSupportImageTintList(ColorStateList tint) {
-        if (mImageTint == null) {
-            mImageTint = new TintInfo();
-        }
-        mImageTint.mTintList = tint;
-        mImageTint.mHasTintList = true;
-        applySupportImageTint();
-    }
-
-    ColorStateList getSupportImageTintList() {
-        return mImageTint != null ? mImageTint.mTintList : null;
-    }
-
-    void setSupportImageTintMode(PorterDuff.Mode tintMode) {
-        if (mImageTint == null) {
-            mImageTint = new TintInfo();
-        }
-        mImageTint.mTintMode = tintMode;
-        mImageTint.mHasTintMode = true;
-
-        applySupportImageTint();
-    }
-
-    PorterDuff.Mode getSupportImageTintMode() {
-        return mImageTint != null ? mImageTint.mTintMode : null;
-    }
-
-    void applySupportImageTint() {
-        final Drawable imageViewDrawable = mView.getDrawable();
-        if (imageViewDrawable != null) {
-            DrawableUtils.fixDrawable(imageViewDrawable);
-        }
-
-        if (imageViewDrawable != null) {
-            if (shouldApplyFrameworkTintUsingColorFilter()
-                    && applyFrameworkTintUsingColorFilter(imageViewDrawable)) {
-                // This needs to be called before the internal tints below so it takes
-                // effect on any widgets using the compat tint on API 21
-                return;
-            }
-
-            if (mImageTint != null) {
-                AppCompatDrawableManager.tintDrawable(imageViewDrawable, mImageTint,
-                        mView.getDrawableState());
-            } else if (mInternalImageTint != null) {
-                AppCompatDrawableManager.tintDrawable(imageViewDrawable, mInternalImageTint,
-                        mView.getDrawableState());
-            }
-        }
-    }
-
-    void setInternalImageTint(ColorStateList tint) {
-        if (tint != null) {
-            if (mInternalImageTint == null) {
-                mInternalImageTint = new TintInfo();
-            }
-            mInternalImageTint.mTintList = tint;
-            mInternalImageTint.mHasTintList = true;
-        } else {
-            mInternalImageTint = null;
-        }
-        applySupportImageTint();
-    }
-
-    private boolean shouldApplyFrameworkTintUsingColorFilter() {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk > 21) {
-            // On API 22+, if we're using an internal compat image source tint, we're also
-            // responsible for applying any custom tint set via the framework impl
-            return mInternalImageTint != null;
-        } else if (sdk == 21) {
-            // GradientDrawable doesn't implement setTintList on API 21, and since there is
-            // no nice way to unwrap DrawableContainers we have to blanket apply this
-            // on API 21
-            return true;
-        } else {
-            // API 19 and below doesn't have framework tint
-            return false;
-        }
-    }
-
-    /**
-     * Applies the framework image source tint to a view, but using the compat method (ColorFilter)
-     *
-     * @return true if a tint was applied
-     */
-    private boolean applyFrameworkTintUsingColorFilter(@NonNull Drawable imageSource) {
-        if (mTmpInfo == null) {
-            mTmpInfo = new TintInfo();
-        }
-        final TintInfo info = mTmpInfo;
-        info.clear();
-
-        final ColorStateList tintList = ImageViewCompat.getImageTintList(mView);
-        if (tintList != null) {
-            info.mHasTintList = true;
-            info.mTintList = tintList;
-        }
-        final PorterDuff.Mode mode = ImageViewCompat.getImageTintMode(mView);
-        if (mode != null) {
-            info.mHasTintMode = true;
-            info.mTintMode = mode;
-        }
-
-        if (info.mHasTintList || info.mHasTintMode) {
-            AppCompatDrawableManager.tintDrawable(imageSource, info, mView.getDrawableState());
-            return true;
-        }
-
-        return false;
-    }
-}
diff --git a/android/support/v7/widget/AppCompatImageView.java b/android/support/v7/widget/AppCompatImageView.java
deleted file mode 100644
index f50799e..0000000
--- a/android/support/v7/widget/AppCompatImageView.java
+++ /dev/null
@@ -1,264 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.TintableBackgroundView;
-import android.support.v4.widget.ImageViewCompat;
-import android.support.v4.widget.TintableImageSourceView;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * 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
- *     {@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>Allows dynamic tint of its image via the image tint methods in
- *     {@link ImageViewCompat}.</li>
- *     <li>Allows setting of the image tint using {@link R.attr#tint} and
- *     {@link R.attr#tintMode}.</li>
- * </ul>
- *
- * <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 {
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatImageHelper mImageHelper;
-
-    public AppCompatImageView(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatImageView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public AppCompatImageView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mImageHelper = new AppCompatImageHelper(this);
-        mImageHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    /**
-     * Sets a drawable as the content of this ImageView.
-     *
-     * <p>Allows the use of vector drawables when running on older versions of the platform.</p>
-     *
-     * @param resId the resource identifier of the drawable
-     * @see ImageView#setImageResource(int)
-     * @attr ref R.styleable#AppCompatImageView_srcCompat
-     */
-    @Override
-    public void setImageResource(@DrawableRes int resId) {
-        if (mImageHelper != null) {
-            // Intercept this call and instead retrieve the Drawable via the image helper
-            mImageHelper.setImageResource(resId);
-        }
-    }
-
-    @Override
-    public void setImageDrawable(@Nullable Drawable drawable) {
-        super.setImageDrawable(drawable);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public void setImageBitmap(Bitmap bm) {
-        super.setImageBitmap(bm);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public void setImageURI(@Nullable Uri uri) {
-        super.setImageURI(uri);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#setImageTintList(ImageView, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportImageTintList(@Nullable ColorStateList tint) {
-        if (mImageHelper != null) {
-            mImageHelper.setSupportImageTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#getImageTintList(ImageView)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportImageTintList() {
-        return mImageHelper != null
-                ? mImageHelper.getSupportImageTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#setImageTintMode(ImageView, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportImageTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mImageHelper != null) {
-            mImageHelper.setSupportImageTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.widget.ImageViewCompat#getImageTintMode(ImageView)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportImageTintMode() {
-        return mImageHelper != null
-                ? mImageHelper.getSupportImageTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return mImageHelper.hasOverlappingRendering() && super.hasOverlappingRendering();
-    }
-}
diff --git a/android/support/v7/widget/AppCompatMultiAutoCompleteTextView.java b/android/support/v7/widget/AppCompatMultiAutoCompleteTextView.java
deleted file mode 100644
index b71b08a..0000000
--- a/android/support/v7/widget/AppCompatMultiAutoCompleteTextView.java
+++ /dev/null
@@ -1,188 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.TintableBackgroundView;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.MultiAutoCompleteTextView;
-
-/**
- * A {@link MultiAutoCompleteTextView} which supports compatible features on older version 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 MultiAutoCompleteTextView} in your layouts.
- * You should only need to manually use this class when writing custom views.</p>
- */
-public class AppCompatMultiAutoCompleteTextView extends MultiAutoCompleteTextView
-        implements TintableBackgroundView {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.popupBackground
-    };
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatMultiAutoCompleteTextView(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.autoCompleteTextViewStyle);
-    }
-
-    public AppCompatMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
-                TINT_ATTRS, defStyleAttr, 0);
-        if (a.hasValue(0)) {
-            setDropDownBackgroundDrawable(a.getDrawable(0));
-        }
-        a.recycle();
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mTextHelper = AppCompatTextHelper.create(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper.applyCompoundDrawablesTints();
-    }
-
-    @Override
-    public void setDropDownBackgroundResource(@DrawableRes int resId) {
-        setDropDownBackgroundDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mTextHelper != null) {
-            mTextHelper.applyCompoundDrawablesTints();
-        }
-    }
-
-    @Override
-    public void setTextAppearance(Context context, int resId) {
-        super.setTextAppearance(context, resId);
-        if (mTextHelper != null) {
-            mTextHelper.onSetTextAppearance(context, resId);
-        }
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return AppCompatHintHelper.onCreateInputConnection(super.onCreateInputConnection(outAttrs),
-                outAttrs, this);
-    }
-}
diff --git a/android/support/v7/widget/AppCompatPopupWindow.java b/android/support/v7/widget/AppCompatPopupWindow.java
deleted file mode 100644
index 821d71c..0000000
--- a/android/support/v7/widget/AppCompatPopupWindow.java
+++ /dev/null
@@ -1,95 +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.content.Context;
-import android.os.Build;
-import android.support.annotation.AttrRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StyleRes;
-import android.support.v4.widget.PopupWindowCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.PopupWindow;
-
-class AppCompatPopupWindow extends PopupWindow {
-
-    private static final boolean COMPAT_OVERLAP_ANCHOR = Build.VERSION.SDK_INT < 21;
-
-    private boolean mOverlapAnchor;
-
-    public AppCompatPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
-            @AttrRes int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr, 0);
-    }
-
-    public AppCompatPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
-            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        init(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
-                R.styleable.PopupWindow, defStyleAttr, defStyleRes);
-        if (a.hasValue(R.styleable.PopupWindow_overlapAnchor)) {
-            setSupportOverlapAnchor(a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false));
-        }
-        // We re-set this for tinting purposes
-        setBackgroundDrawable(a.getDrawable(R.styleable.PopupWindow_android_popupBackground));
-
-        a.recycle();
-    }
-
-    @Override
-    public void showAsDropDown(View anchor, int xoff, int yoff) {
-        if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
-            // If we're pre-L, emulate overlapAnchor by modifying the yOff
-            yoff -= anchor.getHeight();
-        }
-        super.showAsDropDown(anchor, xoff, yoff);
-    }
-
-    @Override
-    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
-        if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
-            // If we're pre-L, emulate overlapAnchor by modifying the yOff
-            yoff -= anchor.getHeight();
-        }
-        super.showAsDropDown(anchor, xoff, yoff, gravity);
-    }
-
-    @Override
-    public void update(View anchor, int xoff, int yoff, int width, int height) {
-        if (COMPAT_OVERLAP_ANCHOR && mOverlapAnchor) {
-            // If we're pre-L, emulate overlapAnchor by modifying the yOff
-            yoff -= anchor.getHeight();
-        }
-        super.update(anchor, xoff, yoff, width, height);
-    }
-
-    private void setSupportOverlapAnchor(boolean overlapAnchor) {
-        if (COMPAT_OVERLAP_ANCHOR) {
-            mOverlapAnchor = overlapAnchor;
-        } else {
-            PopupWindowCompat.setOverlapAnchor(this, overlapAnchor);
-        }
-    }
-}
diff --git a/android/support/v7/widget/AppCompatProgressBarHelper.java b/android/support/v7/widget/AppCompatProgressBarHelper.java
deleted file mode 100644
index a95873c..0000000
--- a/android/support/v7/widget/AppCompatProgressBarHelper.java
+++ /dev/null
@@ -1,147 +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.widget;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Shader;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ClipDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RoundRectShape;
-import android.graphics.drawable.shapes.Shape;
-import android.support.v4.graphics.drawable.WrappedDrawable;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.widget.ProgressBar;
-
-class AppCompatProgressBarHelper {
-
-    private static final int[] TINT_ATTRS = {
-            android.R.attr.indeterminateDrawable,
-            android.R.attr.progressDrawable
-    };
-
-    private final ProgressBar mView;
-
-    private Bitmap mSampleTile;
-
-    AppCompatProgressBarHelper(ProgressBar view) {
-        mView = view;
-    }
-
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
-                TINT_ATTRS, defStyleAttr, 0);
-
-        Drawable drawable = a.getDrawableIfKnown(0);
-        if (drawable != null) {
-            mView.setIndeterminateDrawable(tileifyIndeterminate(drawable));
-        }
-
-        drawable = a.getDrawableIfKnown(1);
-        if (drawable != null) {
-            mView.setProgressDrawable(tileify(drawable, false));
-        }
-
-        a.recycle();
-    }
-
-    /**
-     * Converts a drawable to a tiled version of itself. It will recursively
-     * traverse layer and state list drawables.
-     */
-    private Drawable tileify(Drawable drawable, boolean clip) {
-        if (drawable instanceof WrappedDrawable) {
-            Drawable inner = ((WrappedDrawable) drawable).getWrappedDrawable();
-            if (inner != null) {
-                inner = tileify(inner, clip);
-                ((WrappedDrawable) drawable).setWrappedDrawable(inner);
-            }
-        } else if (drawable instanceof LayerDrawable) {
-            LayerDrawable background = (LayerDrawable) drawable;
-            final int N = background.getNumberOfLayers();
-            Drawable[] outDrawables = new Drawable[N];
-
-            for (int i = 0; i < N; i++) {
-                int id = background.getId(i);
-                outDrawables[i] = tileify(background.getDrawable(i),
-                        (id == android.R.id.progress || id == android.R.id.secondaryProgress));
-            }
-            LayerDrawable newBg = new LayerDrawable(outDrawables);
-
-            for (int i = 0; i < N; i++) {
-                newBg.setId(i, background.getId(i));
-            }
-
-            return newBg;
-
-        } else if (drawable instanceof BitmapDrawable) {
-            final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
-            final Bitmap tileBitmap = bitmapDrawable.getBitmap();
-            if (mSampleTile == null) {
-                mSampleTile = tileBitmap;
-            }
-
-            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
-            final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
-                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
-            shapeDrawable.getPaint().setShader(bitmapShader);
-            shapeDrawable.getPaint().setColorFilter(bitmapDrawable.getPaint().getColorFilter());
-            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
-                    ClipDrawable.HORIZONTAL) : shapeDrawable;
-        }
-
-        return drawable;
-    }
-
-    /**
-     * Convert a AnimationDrawable for use as a barberpole animation.
-     * Each frame of the animation is wrapped in a ClipDrawable and
-     * given a tiling BitmapShader.
-     */
-    private Drawable tileifyIndeterminate(Drawable drawable) {
-        if (drawable instanceof AnimationDrawable) {
-            AnimationDrawable background = (AnimationDrawable) drawable;
-            final int N = background.getNumberOfFrames();
-            AnimationDrawable newBg = new AnimationDrawable();
-            newBg.setOneShot(background.isOneShot());
-
-            for (int i = 0; i < N; i++) {
-                Drawable frame = tileify(background.getFrame(i), true);
-                frame.setLevel(10000);
-                newBg.addFrame(frame, background.getDuration(i));
-            }
-            newBg.setLevel(10000);
-            drawable = newBg;
-        }
-        return drawable;
-    }
-
-    private Shape getDrawableShape() {
-        final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
-        return new RoundRectShape(roundedCorners, null, null);
-    }
-
-    Bitmap getSampleTime() {
-        return mSampleTile;
-    }
-
-}
diff --git a/android/support/v7/widget/AppCompatRadioButton.java b/android/support/v7/widget/AppCompatRadioButton.java
deleted file mode 100644
index 3c73671..0000000
--- a/android/support/v7/widget/AppCompatRadioButton.java
+++ /dev/null
@@ -1,140 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.widget.TintableCompoundButton;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.AttributeSet;
-import android.widget.RadioButton;
-
-/**
- * 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
- *     {@link android.support.v4.widget.CompoundButtonCompat}.</li>
- *     <li>Allows setting of the background tint using {@link R.attr#buttonTint} and
- *     {@link R.attr#buttonTintMode}.</li>
- * </ul>
- *
- * <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 {
-
-    private final AppCompatCompoundButtonHelper mCompoundButtonHelper;
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatRadioButton(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatRadioButton(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.radioButtonStyle);
-    }
-
-    public AppCompatRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-        mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this);
-        mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper = new AppCompatTextHelper(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    @Override
-    public void setButtonDrawable(Drawable buttonDrawable) {
-        super.setButtonDrawable(buttonDrawable);
-        if (mCompoundButtonHelper != null) {
-            mCompoundButtonHelper.onSetButtonDrawable();
-        }
-    }
-
-    @Override
-    public void setButtonDrawable(@DrawableRes int resId) {
-        setButtonDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    @Override
-    public int getCompoundPaddingLeft() {
-        final int value = super.getCompoundPaddingLeft();
-        return mCompoundButtonHelper != null
-                ? mCompoundButtonHelper.getCompoundPaddingLeft(value)
-                : value;
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportButtonTintList(@Nullable ColorStateList tint) {
-        if (mCompoundButtonHelper != null) {
-            mCompoundButtonHelper.setSupportButtonTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    @Override
-    public ColorStateList getSupportButtonTintList() {
-        return mCompoundButtonHelper != null
-                ? mCompoundButtonHelper.getSupportButtonTintList()
-                : null;
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mCompoundButtonHelper != null) {
-            mCompoundButtonHelper.setSupportButtonTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed from {@link android.support.v4.widget.CompoundButtonCompat}
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Nullable
-    @Override
-    public PorterDuff.Mode getSupportButtonTintMode() {
-        return mCompoundButtonHelper != null
-                ? mCompoundButtonHelper.getSupportButtonTintMode()
-                : null;
-    }
-}
diff --git a/android/support/v7/widget/AppCompatRatingBar.java b/android/support/v7/widget/AppCompatRatingBar.java
deleted file mode 100644
index b03b842..0000000
--- a/android/support/v7/widget/AppCompatRatingBar.java
+++ /dev/null
@@ -1,65 +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.content.Context;
-import android.graphics.Bitmap;
-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 versions of the platform.
- *
- * <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 {
-
-    private final AppCompatProgressBarHelper mAppCompatProgressBarHelper;
-
-    public AppCompatRatingBar(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatRatingBar(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.ratingBarStyle);
-    }
-
-    public AppCompatRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mAppCompatProgressBarHelper = new AppCompatProgressBarHelper(this);
-        mAppCompatProgressBarHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    @Override
-    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        Bitmap sampleTile = mAppCompatProgressBarHelper.getSampleTime();
-        if (sampleTile != null) {
-            final int width = sampleTile.getWidth() * getNumStars();
-            setMeasuredDimension(View.resolveSizeAndState(width, widthMeasureSpec, 0),
-                    getMeasuredHeight());
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/AppCompatSeekBar.java b/android/support/v7/widget/AppCompatSeekBar.java
deleted file mode 100644
index 674e46b..0000000
--- a/android/support/v7/widget/AppCompatSeekBar.java
+++ /dev/null
@@ -1,71 +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.content.Context;
-import android.graphics.Canvas;
-import android.support.annotation.RequiresApi;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.widget.SeekBar;
-
-/**
- * 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
- * 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 {
-
-    private final AppCompatSeekBarHelper mAppCompatSeekBarHelper;
-
-    public AppCompatSeekBar(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatSeekBar(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.seekBarStyle);
-    }
-
-    public AppCompatSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mAppCompatSeekBarHelper = new AppCompatSeekBarHelper(this);
-        mAppCompatSeekBarHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    @Override
-    protected synchronized void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        mAppCompatSeekBarHelper.drawTickMarks(canvas);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        mAppCompatSeekBarHelper.drawableStateChanged();
-    }
-
-    @RequiresApi(11)
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        super.jumpDrawablesToCurrentState();
-        mAppCompatSeekBarHelper.jumpDrawablesToCurrentState();
-    }
-}
diff --git a/android/support/v7/widget/AppCompatSeekBarHelper.java b/android/support/v7/widget/AppCompatSeekBarHelper.java
deleted file mode 100644
index e1e62ee..0000000
--- a/android/support/v7/widget/AppCompatSeekBarHelper.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.v7.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.widget.SeekBar;
-
-class AppCompatSeekBarHelper extends AppCompatProgressBarHelper {
-
-    private final SeekBar mView;
-
-    private Drawable mTickMark;
-    private ColorStateList mTickMarkTintList = null;
-    private PorterDuff.Mode mTickMarkTintMode = null;
-    private boolean mHasTickMarkTint = false;
-    private boolean mHasTickMarkTintMode = false;
-
-    AppCompatSeekBarHelper(SeekBar view) {
-        super(view);
-        mView = view;
-    }
-
-    @Override
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        super.loadFromAttributes(attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
-                R.styleable.AppCompatSeekBar, defStyleAttr, 0);
-        final Drawable drawable = a.getDrawableIfKnown(R.styleable.AppCompatSeekBar_android_thumb);
-        if (drawable != null) {
-            mView.setThumb(drawable);
-        }
-
-        final Drawable tickMark = a.getDrawable(R.styleable.AppCompatSeekBar_tickMark);
-        setTickMark(tickMark);
-
-        if (a.hasValue(R.styleable.AppCompatSeekBar_tickMarkTintMode)) {
-            mTickMarkTintMode = DrawableUtils.parseTintMode(a.getInt(
-                    R.styleable.AppCompatSeekBar_tickMarkTintMode, -1), mTickMarkTintMode);
-            mHasTickMarkTintMode = true;
-        }
-
-        if (a.hasValue(R.styleable.AppCompatSeekBar_tickMarkTint)) {
-            mTickMarkTintList = a.getColorStateList(R.styleable.AppCompatSeekBar_tickMarkTint);
-            mHasTickMarkTint = true;
-        }
-
-        a.recycle();
-
-        applyTickMarkTint();
-    }
-
-    void setTickMark(@Nullable Drawable tickMark) {
-        if (mTickMark != null) {
-            mTickMark.setCallback(null);
-        }
-
-        mTickMark = tickMark;
-
-        if (tickMark != null) {
-            tickMark.setCallback(mView);
-            DrawableCompat.setLayoutDirection(tickMark, ViewCompat.getLayoutDirection(mView));
-            if (tickMark.isStateful()) {
-                tickMark.setState(mView.getDrawableState());
-            }
-            applyTickMarkTint();
-        }
-
-        mView.invalidate();
-    }
-
-    @Nullable
-    Drawable getTickMark() {
-        return mTickMark;
-    }
-
-    void setTickMarkTintList(@Nullable ColorStateList tint) {
-        mTickMarkTintList = tint;
-        mHasTickMarkTint = true;
-
-        applyTickMarkTint();
-    }
-
-    @Nullable
-    ColorStateList getTickMarkTintList() {
-        return mTickMarkTintList;
-    }
-
-    void setTickMarkTintMode(@Nullable PorterDuff.Mode tintMode) {
-        mTickMarkTintMode = tintMode;
-        mHasTickMarkTintMode = true;
-
-        applyTickMarkTint();
-    }
-
-    @Nullable
-    PorterDuff.Mode getTickMarkTintMode() {
-        return mTickMarkTintMode;
-    }
-
-    private void applyTickMarkTint() {
-        if (mTickMark != null && (mHasTickMarkTint || mHasTickMarkTintMode)) {
-            mTickMark = DrawableCompat.wrap(mTickMark.mutate());
-
-            if (mHasTickMarkTint) {
-                DrawableCompat.setTintList(mTickMark, mTickMarkTintList);
-            }
-
-            if (mHasTickMarkTintMode) {
-                DrawableCompat.setTintMode(mTickMark, mTickMarkTintMode);
-            }
-
-            // The drawable (or one of its children) may not have been
-            // stateful before applying the tint, so let's try again.
-            if (mTickMark.isStateful()) {
-                mTickMark.setState(mView.getDrawableState());
-            }
-        }
-    }
-
-    @RequiresApi(11)
-    void jumpDrawablesToCurrentState() {
-        if (mTickMark != null) {
-            mTickMark.jumpToCurrentState();
-        }
-    }
-
-    void drawableStateChanged() {
-        final Drawable tickMark = mTickMark;
-        if (tickMark != null && tickMark.isStateful()
-                && tickMark.setState(mView.getDrawableState())) {
-            mView.invalidateDrawable(tickMark);
-        }
-    }
-
-    /**
-     * Draw the tick marks.
-     */
-    void drawTickMarks(Canvas canvas) {
-        if (mTickMark != null) {
-            final int count = mView.getMax();
-            if (count > 1) {
-                final int w = mTickMark.getIntrinsicWidth();
-                final int h = mTickMark.getIntrinsicHeight();
-                final int halfW = w >= 0 ? w / 2 : 1;
-                final int halfH = h >= 0 ? h / 2 : 1;
-                mTickMark.setBounds(-halfW, -halfH, halfW, halfH);
-
-                final float spacing = (mView.getWidth() - mView.getPaddingLeft()
-                        - mView.getPaddingRight()) / (float) count;
-                final int saveCount = canvas.save();
-                canvas.translate(mView.getPaddingLeft(), mView.getHeight() / 2);
-                for (int i = 0; i <= count; i++) {
-                    mTickMark.draw(canvas);
-                    canvas.translate(spacing, 0);
-                }
-                canvas.restoreToCount(saveCount);
-            }
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/AppCompatSpinner.java b/android/support/v7/widget/AppCompatSpinner.java
deleted file mode 100644
index 020cc20..0000000
--- a/android/support/v7/widget/AppCompatSpinner.java
+++ /dev/null
@@ -1,851 +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 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.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.TintableBackgroundView;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.ContextThemeWrapper;
-import android.support.v7.view.menu.ShowableListMenu;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.PopupWindow;
-import android.widget.Spinner;
-import android.widget.SpinnerAdapter;
-
-
-/**
- * A {@link Spinner} 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
- *     {@link android.support.v4.widget.CompoundButtonCompat}.</li>
- *     <li>Allows setting of the background tint using {@link R.attr#buttonTint} and
- *     {@link R.attr#buttonTintMode}.</li>
- *     <li>Setting the popup theme using {@link R.attr#popupTheme}.</li>
- * </ul>
- *
- * <p>This will automatically be used when you use {@link Spinner} in your layouts.
- * You should only need to manually use this class when writing custom views.</p>
- */
-public class AppCompatSpinner extends Spinner implements TintableBackgroundView {
-
-    private static final int[] ATTRS_ANDROID_SPINNERMODE = {android.R.attr.spinnerMode};
-
-    private static final int MAX_ITEMS_MEASURED = 15;
-
-    private static final String TAG = "AppCompatSpinner";
-
-    private static final int MODE_DIALOG = 0;
-    private static final int MODE_DROPDOWN = 1;
-    private static final int MODE_THEME = -1;
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-
-    /** Context used to inflate the popup window or dialog. */
-    private final Context mPopupContext;
-
-    /** Forwarding listener used to implement drag-to-open. */
-    private ForwardingListener mForwardingListener;
-
-    /** Temporary holder for setAdapter() calls from the super constructor. */
-    private SpinnerAdapter mTempAdapter;
-
-    private final boolean mPopupSet;
-
-    private DropdownPopup mPopup;
-
-    private int mDropDownWidth;
-
-    private final Rect mTempRect = new Rect();
-
-    /**
-     * Construct a new spinner with the given context's theme.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     */
-    public AppCompatSpinner(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Construct a new spinner with the given context's theme and the supplied
-     * mode of displaying choices. <code>mode</code> may be one of
-     * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}.
-     *
-     * @param context The Context the view is running in, through which it can
-     *                access the current theme, resources, etc.
-     * @param mode    Constant describing how the user will select choices from the spinner.
-     * @see #MODE_DIALOG
-     * @see #MODE_DROPDOWN
-     */
-    public AppCompatSpinner(Context context, int mode) {
-        this(context, null, R.attr.spinnerStyle, mode);
-    }
-
-    /**
-     * Construct a new spinner with the given context's theme and the supplied attribute set.
-     *
-     * @param context The 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 AppCompatSpinner(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.spinnerStyle);
-    }
-
-    /**
-     * Construct a new spinner with the given context's theme, the supplied attribute set,
-     * and default style attribute.
-     *
-     * @param context      The 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 defStyleAttr 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 AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, MODE_THEME);
-    }
-
-    /**
-     * Construct a new spinner with the given context's theme, the supplied attribute set,
-     * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or
-     * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
-     *
-     * @param context      The 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 defStyleAttr 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 mode         Constant describing how the user will select choices from the spinner.
-     * @see #MODE_DIALOG
-     * @see #MODE_DROPDOWN
-     */
-    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
-        this(context, attrs, defStyleAttr, mode, null);
-    }
-
-
-    /**
-     * Constructs a new spinner with the given context's theme, the supplied
-     * attribute set, default styles, popup mode (one of {@link #MODE_DIALOG}
-     * or {@link #MODE_DROPDOWN}), and the context against which the popup
-     * should be inflated.
-     *
-     * @param context      The context against which the view is inflated, which
-     *                     provides access to the current theme, resources, etc.
-     * @param attrs        The attributes of the XML tag that is inflating the view.
-     * @param defStyleAttr 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 mode         Constant describing how the user will select choices from
-     *                     the spinner.
-     * @param popupTheme   The theme against which the dialog or dropdown popup
-     *                     should be inflated. May be {@code null} to use the
-     *                     view theme. If set, this will override any value
-     *                     specified by
-     *                     {@link R.styleable#Spinner_popupTheme}.
-     * @see #MODE_DIALOG
-     * @see #MODE_DROPDOWN
-     */
-    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode,
-            Resources.Theme popupTheme) {
-        super(context, attrs, defStyleAttr);
-
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
-                R.styleable.Spinner, defStyleAttr, 0);
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-
-        if (popupTheme != null) {
-            mPopupContext = new ContextThemeWrapper(context, popupTheme);
-        } else {
-            final int popupThemeResId = a.getResourceId(R.styleable.Spinner_popupTheme, 0);
-            if (popupThemeResId != 0) {
-                mPopupContext = new ContextThemeWrapper(context, popupThemeResId);
-            } else {
-                // If we're running on a < M device, we'll use the current context and still handle
-                // any dropdown popup
-                mPopupContext = !(Build.VERSION.SDK_INT >= 23) ? context : null;
-            }
-        }
-
-        if (mPopupContext != null) {
-            if (mode == MODE_THEME) {
-                TypedArray aa = null;
-                try {
-                    aa = context.obtainStyledAttributes(attrs, ATTRS_ANDROID_SPINNERMODE,
-                            defStyleAttr, 0);
-                    if (aa.hasValue(0)) {
-                        mode = aa.getInt(0, MODE_DIALOG);
-                    }
-                } catch (Exception e) {
-                    Log.i(TAG, "Could not read android:spinnerMode", e);
-                } finally {
-                    if (aa != null) {
-                        aa.recycle();
-                    }
-                }
-            }
-
-            if (mode == MODE_DROPDOWN) {
-                final DropdownPopup popup = new DropdownPopup(mPopupContext, attrs, defStyleAttr);
-                final TintTypedArray pa = TintTypedArray.obtainStyledAttributes(
-                        mPopupContext, attrs, R.styleable.Spinner, defStyleAttr, 0);
-                mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_android_dropDownWidth,
-                        LayoutParams.WRAP_CONTENT);
-                popup.setBackgroundDrawable(
-                        pa.getDrawable(R.styleable.Spinner_android_popupBackground));
-                popup.setPromptText(a.getString(R.styleable.Spinner_android_prompt));
-                pa.recycle();
-
-                mPopup = popup;
-                mForwardingListener = new ForwardingListener(this) {
-                    @Override
-                    public ShowableListMenu getPopup() {
-                        return popup;
-                    }
-
-                    @Override
-                    public boolean onForwardingStarted() {
-                        if (!mPopup.isShowing()) {
-                            mPopup.show();
-                        }
-                        return true;
-                    }
-                };
-            }
-        }
-
-        final CharSequence[] entries = a.getTextArray(R.styleable.Spinner_android_entries);
-        if (entries != null) {
-            final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<>(
-                    context, android.R.layout.simple_spinner_item, entries);
-            adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
-            setAdapter(adapter);
-        }
-
-        a.recycle();
-
-        mPopupSet = true;
-
-        // Base constructors can call setAdapter before we initialize mPopup.
-        // Finish setting things up if this happened.
-        if (mTempAdapter != null) {
-            setAdapter(mTempAdapter);
-            mTempAdapter = null;
-        }
-
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-    }
-
-    /**
-     * @return the context used to inflate the Spinner's popup or dialog window
-     */
-    @Override
-    public Context getPopupContext() {
-        if (mPopup != null) {
-            return mPopupContext;
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            return super.getPopupContext();
-        }
-        return null;
-    }
-
-    @Override
-    public void setPopupBackgroundDrawable(Drawable background) {
-        if (mPopup != null) {
-            mPopup.setBackgroundDrawable(background);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            super.setPopupBackgroundDrawable(background);
-        }
-    }
-
-    @Override
-    public void setPopupBackgroundResource(@DrawableRes int resId) {
-        setPopupBackgroundDrawable(AppCompatResources.getDrawable(getPopupContext(), resId));
-    }
-
-    @Override
-    public Drawable getPopupBackground() {
-        if (mPopup != null) {
-            return mPopup.getBackground();
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return super.getPopupBackground();
-        }
-        return null;
-    }
-
-    @Override
-    public void setDropDownVerticalOffset(int pixels) {
-        if (mPopup != null) {
-            mPopup.setVerticalOffset(pixels);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            super.setDropDownVerticalOffset(pixels);
-        }
-    }
-
-    @Override
-    public int getDropDownVerticalOffset() {
-        if (mPopup != null) {
-            return mPopup.getVerticalOffset();
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return super.getDropDownVerticalOffset();
-        }
-        return 0;
-    }
-
-    @Override
-    public void setDropDownHorizontalOffset(int pixels) {
-        if (mPopup != null) {
-            mPopup.setHorizontalOffset(pixels);
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            super.setDropDownHorizontalOffset(pixels);
-        }
-    }
-
-    /**
-     * Get the configured horizontal offset in pixels for the spinner's popup window of choices.
-     * Only valid in {@link #MODE_DROPDOWN}; other modes will return 0.
-     *
-     * @return Horizontal offset in pixels
-     */
-    @Override
-    public int getDropDownHorizontalOffset() {
-        if (mPopup != null) {
-            return mPopup.getHorizontalOffset();
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return super.getDropDownHorizontalOffset();
-        }
-        return 0;
-    }
-
-    @Override
-    public void setDropDownWidth(int pixels) {
-        if (mPopup != null) {
-            mDropDownWidth = pixels;
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            super.setDropDownWidth(pixels);
-        }
-    }
-
-    @Override
-    public int getDropDownWidth() {
-        if (mPopup != null) {
-            return mDropDownWidth;
-        } else if (Build.VERSION.SDK_INT >= 16) {
-            return super.getDropDownWidth();
-        }
-        return 0;
-    }
-
-    @Override
-    public void setAdapter(SpinnerAdapter adapter) {
-        // The super constructor may call setAdapter before we're prepared.
-        // Postpone doing anything until we've finished construction.
-        if (!mPopupSet) {
-            mTempAdapter = adapter;
-            return;
-        }
-
-        super.setAdapter(adapter);
-
-        if (mPopup != null) {
-            final Context popupContext = mPopupContext == null ? getContext() : mPopupContext;
-            mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme()));
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        if (mPopup != null && mPopup.isShowing()) {
-            mPopup.dismiss();
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mForwardingListener != null && mForwardingListener.onTouch(this, event)) {
-            return true;
-        }
-        return super.onTouchEvent(event);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
-            final int measuredWidth = getMeasuredWidth();
-            setMeasuredDimension(Math.min(Math.max(measuredWidth,
-                                    compatMeasureContentWidth(getAdapter(), getBackground())),
-                            MeasureSpec.getSize(widthMeasureSpec)),
-                    getMeasuredHeight());
-        }
-    }
-
-    @Override
-    public boolean performClick() {
-        if (mPopup != null) {
-            // If we have a popup, show it if needed, or just consume the click...
-            if (!mPopup.isShowing()) {
-                mPopup.show();
-            }
-            return true;
-        }
-
-        // Else let the platform handle the click
-        return super.performClick();
-    }
-
-    @Override
-    public void setPrompt(CharSequence prompt) {
-        if (mPopup != null) {
-            mPopup.setPromptText(prompt);
-        } else {
-            super.setPrompt(prompt);
-        }
-    }
-
-    @Override
-    public CharSequence getPrompt() {
-        return mPopup != null ? mPopup.getHintText() : super.getPrompt();
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
-     * ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View,
-     * PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-    }
-
-    int compatMeasureContentWidth(SpinnerAdapter adapter, Drawable background) {
-        if (adapter == null) {
-            return 0;
-        }
-
-        int width = 0;
-        View itemView = null;
-        int itemType = 0;
-        final int widthMeasureSpec =
-                MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.UNSPECIFIED);
-        final int heightMeasureSpec =
-                MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.UNSPECIFIED);
-
-        // Make sure the number of items we'll measure is capped. If it's a huge data set
-        // with wildly varying sizes, oh well.
-        int start = Math.max(0, getSelectedItemPosition());
-        final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED);
-        final int count = end - start;
-        start = Math.max(0, start - (MAX_ITEMS_MEASURED - count));
-        for (int i = start; i < end; i++) {
-            final int positionType = adapter.getItemViewType(i);
-            if (positionType != itemType) {
-                itemType = positionType;
-                itemView = null;
-            }
-            itemView = adapter.getView(i, itemView, this);
-            if (itemView.getLayoutParams() == null) {
-                itemView.setLayoutParams(new LayoutParams(
-                        LayoutParams.WRAP_CONTENT,
-                        LayoutParams.WRAP_CONTENT));
-            }
-            itemView.measure(widthMeasureSpec, heightMeasureSpec);
-            width = Math.max(width, itemView.getMeasuredWidth());
-        }
-
-        // Add background padding to measured width
-        if (background != null) {
-            background.getPadding(mTempRect);
-            width += mTempRect.left + mTempRect.right;
-        }
-
-        return width;
-    }
-
-    /**
-     * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
-     * into a ListAdapter.</p>
-     */
-    private static class DropDownAdapter implements ListAdapter, SpinnerAdapter {
-
-        private SpinnerAdapter mAdapter;
-
-        private ListAdapter mListAdapter;
-
-        /**
-         * Creates a new ListAdapter wrapper for the specified adapter.
-         *
-         * @param adapter       the SpinnerAdapter to transform into a ListAdapter
-         * @param dropDownTheme the theme against which to inflate drop-down
-         *                      views, may be {@null} to use default theme
-         */
-        public DropDownAdapter(@Nullable SpinnerAdapter adapter,
-                @Nullable Resources.Theme dropDownTheme) {
-            mAdapter = adapter;
-
-            if (adapter instanceof ListAdapter) {
-                mListAdapter = (ListAdapter) adapter;
-            }
-
-            if (dropDownTheme != null) {
-                 if (Build.VERSION.SDK_INT >= 23
-                         && adapter instanceof android.widget.ThemedSpinnerAdapter) {
-                    final android.widget.ThemedSpinnerAdapter themedAdapter =
-                            (android.widget.ThemedSpinnerAdapter) adapter;
-                    if (themedAdapter.getDropDownViewTheme() != dropDownTheme) {
-                        themedAdapter.setDropDownViewTheme(dropDownTheme);
-                    }
-                } else if (adapter instanceof ThemedSpinnerAdapter) {
-                    final ThemedSpinnerAdapter themedAdapter = (ThemedSpinnerAdapter) adapter;
-                    if (themedAdapter.getDropDownViewTheme() == null) {
-                        themedAdapter.setDropDownViewTheme(dropDownTheme);
-                    }
-                }
-            }
-        }
-
-        @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);
-            }
-        }
-
-        /**
-         * 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) {
-                return adapter.areAllItemsEnabled();
-            } else {
-                return true;
-            }
-        }
-
-        /**
-         * 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) {
-                return adapter.isEnabled(position);
-            } else {
-                return true;
-            }
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            return 0;
-        }
-
-        @Override
-        public int getViewTypeCount() {
-            return 1;
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return getCount() == 0;
-        }
-    }
-
-    private class DropdownPopup extends ListPopupWindow {
-        private CharSequence mHintText;
-        ListAdapter mAdapter;
-        private final Rect mVisibleRect = new Rect();
-
-        public DropdownPopup(Context context, AttributeSet attrs, int defStyleAttr) {
-            super(context, attrs, defStyleAttr);
-
-            setAnchorView(AppCompatSpinner.this);
-            setModal(true);
-            setPromptPosition(POSITION_PROMPT_ABOVE);
-
-            setOnItemClickListener(new AdapterView.OnItemClickListener() {
-                @Override
-                public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
-                    AppCompatSpinner.this.setSelection(position);
-                    if (getOnItemClickListener() != null) {
-                        AppCompatSpinner.this
-                                .performItemClick(v, position, mAdapter.getItemId(position));
-                    }
-                    dismiss();
-                }
-            });
-        }
-
-        @Override
-        public void setAdapter(ListAdapter adapter) {
-            super.setAdapter(adapter);
-            mAdapter = adapter;
-        }
-
-        public CharSequence getHintText() {
-            return mHintText;
-        }
-
-        public void setPromptText(CharSequence hintText) {
-            // Hint text is ignored for dropdowns, but maintain it here.
-            mHintText = hintText;
-        }
-
-        void computeContentWidth() {
-            final Drawable background = getBackground();
-            int hOffset = 0;
-            if (background != null) {
-                background.getPadding(mTempRect);
-                hOffset = ViewUtils.isLayoutRtl(AppCompatSpinner.this) ? mTempRect.right
-                        : -mTempRect.left;
-            } else {
-                mTempRect.left = mTempRect.right = 0;
-            }
-
-            final int spinnerPaddingLeft = AppCompatSpinner.this.getPaddingLeft();
-            final int spinnerPaddingRight = AppCompatSpinner.this.getPaddingRight();
-            final int spinnerWidth = AppCompatSpinner.this.getWidth();
-            if (mDropDownWidth == WRAP_CONTENT) {
-                int contentWidth = compatMeasureContentWidth(
-                        (SpinnerAdapter) mAdapter, getBackground());
-                final int contentWidthLimit = getContext().getResources()
-                        .getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
-                if (contentWidth > contentWidthLimit) {
-                    contentWidth = contentWidthLimit;
-                }
-                setContentWidth(Math.max(
-                        contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
-            } else if (mDropDownWidth == MATCH_PARENT) {
-                setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
-            } else {
-                setContentWidth(mDropDownWidth);
-            }
-            if (ViewUtils.isLayoutRtl(AppCompatSpinner.this)) {
-                hOffset += spinnerWidth - spinnerPaddingRight - getWidth();
-            } else {
-                hOffset += spinnerPaddingLeft;
-            }
-            setHorizontalOffset(hOffset);
-        }
-
-        @Override
-        public void show() {
-            final boolean wasShowing = isShowing();
-
-            computeContentWidth();
-
-            setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
-            super.show();
-            final ListView listView = getListView();
-            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-            setSelection(AppCompatSpinner.this.getSelectedItemPosition());
-
-            if (wasShowing) {
-                // Skip setting up the layout/dismiss listener below. If we were previously
-                // showing it will still stick around.
-                return;
-            }
-
-            // Make sure we hide if our anchor goes away.
-            // TODO: This might be appropriate to push all the way down to PopupWindow,
-            // but it may have other side effects to investigate first. (Text editing handles, etc.)
-            final ViewTreeObserver vto = getViewTreeObserver();
-            if (vto != null) {
-                final ViewTreeObserver.OnGlobalLayoutListener layoutListener
-                        = new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        if (!isVisibleToUser(AppCompatSpinner.this)) {
-                            dismiss();
-                        } else {
-                            computeContentWidth();
-
-                            // Use super.show here to update; we don't want to move the selected
-                            // position or adjust other things that would be reset otherwise.
-                            DropdownPopup.super.show();
-                        }
-                    }
-                };
-                vto.addOnGlobalLayoutListener(layoutListener);
-                setOnDismissListener(new PopupWindow.OnDismissListener() {
-                    @Override
-                    public void onDismiss() {
-                        final ViewTreeObserver vto = getViewTreeObserver();
-                        if (vto != null) {
-                            vto.removeGlobalOnLayoutListener(layoutListener);
-                        }
-                    }
-                });
-            }
-        }
-
-        /**
-         * Simplified version of the the hidden View.isVisibleToUser()
-         */
-        boolean isVisibleToUser(View view) {
-            return ViewCompat.isAttachedToWindow(view) && view.getGlobalVisibleRect(mVisibleRect);
-        }
-    }
-}
diff --git a/android/support/v7/widget/AppCompatTextHelper.java b/android/support/v7/widget/AppCompatTextHelper.java
deleted file mode 100644
index b8ce82a..0000000
--- a/android/support/v7/widget/AppCompatTextHelper.java
+++ /dev/null
@@ -1,414 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v4.widget.AutoSizeableTextView.PLATFORM_SUPPORTS_AUTOSIZE;
-
-import android.annotation.SuppressLint;
-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.content.res.ResourcesCompat;
-import android.support.v4.widget.TextViewCompat;
-import android.support.v7.appcompat.R;
-import android.text.method.PasswordTransformationMethod;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-import java.lang.ref.WeakReference;
-
-@RequiresApi(9)
-class AppCompatTextHelper {
-
-    // Enum for the "typeface" XML parameter.
-    private static final int SANS = 1;
-    private static final int SERIF = 2;
-    private static final int MONOSPACE = 3;
-
-
-    static AppCompatTextHelper create(TextView textView) {
-        if (Build.VERSION.SDK_INT >= 17) {
-            return new AppCompatTextHelperV17(textView);
-        }
-        return new AppCompatTextHelper(textView);
-    }
-
-    final TextView mView;
-
-    private TintInfo mDrawableLeftTint;
-    private TintInfo mDrawableTopTint;
-    private TintInfo mDrawableRightTint;
-    private TintInfo mDrawableBottomTint;
-
-    private final @NonNull AppCompatTextViewAutoSizeHelper mAutoSizeTextHelper;
-
-    private int mStyle = Typeface.NORMAL;
-    private Typeface mFontTypeface;
-    private boolean mAsyncFontPending;
-
-    AppCompatTextHelper(TextView view) {
-        mView = view;
-        mAutoSizeTextHelper = new AppCompatTextViewAutoSizeHelper(mView);
-    }
-
-    @SuppressLint("NewApi")
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        final Context context = mView.getContext();
-        final AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
-
-        // First read the TextAppearance style id
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
-                R.styleable.AppCompatTextHelper, defStyleAttr, 0);
-        final int ap = a.getResourceId(R.styleable.AppCompatTextHelper_android_textAppearance, -1);
-        // Now read the compound drawable and grab any tints
-        if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableLeft)) {
-            mDrawableLeftTint = createTintInfo(context, drawableManager,
-                    a.getResourceId(R.styleable.AppCompatTextHelper_android_drawableLeft, 0));
-        }
-        if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableTop)) {
-            mDrawableTopTint = createTintInfo(context, drawableManager,
-                    a.getResourceId(R.styleable.AppCompatTextHelper_android_drawableTop, 0));
-        }
-        if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableRight)) {
-            mDrawableRightTint = createTintInfo(context, drawableManager,
-                    a.getResourceId(R.styleable.AppCompatTextHelper_android_drawableRight, 0));
-        }
-        if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableBottom)) {
-            mDrawableBottomTint = createTintInfo(context, drawableManager,
-                    a.getResourceId(R.styleable.AppCompatTextHelper_android_drawableBottom, 0));
-        }
-        a.recycle();
-
-        // PasswordTransformationMethod wipes out all other TransformationMethod instances
-        // in TextView's constructor, so we should only set a new transformation method
-        // if we don't have a PasswordTransformationMethod currently...
-        final boolean hasPwdTm =
-                mView.getTransformationMethod() instanceof PasswordTransformationMethod;
-        boolean allCaps = false;
-        boolean allCapsSet = false;
-        ColorStateList textColor = null;
-        ColorStateList textColorHint = null;
-        ColorStateList textColorLink = null;
-
-        // First check TextAppearance's textAllCaps value
-        if (ap != -1) {
-            a = TintTypedArray.obtainStyledAttributes(context, ap, R.styleable.TextAppearance);
-            if (!hasPwdTm && a.hasValue(R.styleable.TextAppearance_textAllCaps)) {
-                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
-                if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {
-                    textColor = a.getColorStateList(R.styleable.TextAppearance_android_textColor);
-                }
-                if (a.hasValue(R.styleable.TextAppearance_android_textColorHint)) {
-                    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();
-        }
-
-        // Now read the style's values
-        a = TintTypedArray.obtainStyledAttributes(context, attrs, R.styleable.TextAppearance,
-                defStyleAttr, 0);
-        if (!hasPwdTm && a.hasValue(R.styleable.TextAppearance_textAllCaps)) {
-            allCapsSet = true;
-            allCaps = a.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
-        }
-        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
-            if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {
-                textColor = a.getColorStateList(R.styleable.TextAppearance_android_textColor);
-            }
-            if (a.hasValue(R.styleable.TextAppearance_android_textColorHint)) {
-                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) {
-            mView.setTextColor(textColor);
-        }
-        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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            // 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_fontFamily)
-                    ? R.styleable.TextAppearance_fontFamily
-                    : R.styleable.TextAppearance_android_fontFamily;
-            if (!context.isRestricted()) {
-                final WeakReference<TextView> textViewWeak = new WeakReference<>(mView);
-                ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() {
-                    @Override
-                    public void onFontRetrieved(@NonNull Typeface typeface) {
-                        onAsyncTypefaceReceived(textViewWeak, typeface);
-                    }
-
-                    @Override
-                    public void onFontRetrievalFailed(int reason) {
-                        // Do nothing.
-                    }
-                };
-                try {
-                    // Note the callback will be triggered on the UI thread.
-                    mFontTypeface = a.getFont(fontFamilyId, mStyle, replyCallback);
-                    // If this call gave us an immediate result, ignore any pending callbacks.
-                    mAsyncFontPending = mFontTypeface == null;
-                } 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);
-                if (fontFamilyName != null) {
-                    mFontTypeface = Typeface.create(fontFamilyName, mStyle);
-                }
-            }
-            return;
-        }
-
-        if (a.hasValue(R.styleable.TextAppearance_android_typeface)) {
-            // Ignore previous pending fonts
-            mAsyncFontPending = false;
-            int typefaceIndex = a.getInt(R.styleable.TextAppearance_android_typeface, SANS);
-            switch (typefaceIndex) {
-                case SANS:
-                    mFontTypeface = Typeface.SANS_SERIF;
-                    break;
-
-                case SERIF:
-                    mFontTypeface = Typeface.SERIF;
-                    break;
-
-                case MONOSPACE:
-                    mFontTypeface = Typeface.MONOSPACE;
-                    break;
-            }
-        }
-    }
-
-    private void onAsyncTypefaceReceived(WeakReference<TextView> textViewWeak, Typeface typeface) {
-        if (mAsyncFontPending) {
-            mFontTypeface = typeface;
-            final TextView textView = textViewWeak.get();
-            if (textView != null) {
-                textView.setTypeface(typeface, mStyle);
-            }
-        }
-    }
-
-    void onSetTextAppearance(Context context, int resId) {
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
-                resId, R.styleable.TextAppearance);
-        if (a.hasValue(R.styleable.TextAppearance_textAllCaps)) {
-            // This breaks away slightly from the logic in TextView.setTextAppearance that serves
-            // as an "overlay" on the current state of the TextView. Since android:textAllCaps
-            // may have been set to true in this text appearance, we need to make sure that
-            // app:textAllCaps has the chance to override it
-            setAllCaps(a.getBoolean(R.styleable.TextAppearance_textAllCaps, false));
-        }
-        if (Build.VERSION.SDK_INT < 23
-                && a.hasValue(R.styleable.TextAppearance_android_textColor)) {
-            // If we're running on < API 23, the text color may contain theme references
-            // so let's re-set using our own inflater
-            final ColorStateList textColor
-                    = a.getColorStateList(R.styleable.TextAppearance_android_textColor);
-            if (textColor != null) {
-                mView.setTextColor(textColor);
-            }
-        }
-
-        updateTypefaceAndStyle(context, a);
-        a.recycle();
-        if (mFontTypeface != null) {
-            mView.setTypeface(mFontTypeface, mStyle);
-        }
-    }
-
-    void setAllCaps(boolean allCaps) {
-        mView.setAllCaps(allCaps);
-    }
-
-    void applyCompoundDrawablesTints() {
-        if (mDrawableLeftTint != null || mDrawableTopTint != null ||
-                mDrawableRightTint != null || mDrawableBottomTint != null) {
-            final Drawable[] compoundDrawables = mView.getCompoundDrawables();
-            applyCompoundDrawableTint(compoundDrawables[0], mDrawableLeftTint);
-            applyCompoundDrawableTint(compoundDrawables[1], mDrawableTopTint);
-            applyCompoundDrawableTint(compoundDrawables[2], mDrawableRightTint);
-            applyCompoundDrawableTint(compoundDrawables[3], mDrawableBottomTint);
-        }
-    }
-
-    final void applyCompoundDrawableTint(Drawable drawable, TintInfo info) {
-        if (drawable != null && info != null) {
-            AppCompatDrawableManager.tintDrawable(drawable, info, mView.getDrawableState());
-        }
-    }
-
-    protected static TintInfo createTintInfo(Context context,
-            AppCompatDrawableManager drawableManager, int drawableId) {
-        final ColorStateList tintList = drawableManager.getTintList(context, drawableId);
-        if (tintList != null) {
-            final TintInfo tintInfo = new TintInfo();
-            tintInfo.mHasTintList = true;
-            tintInfo.mTintList = tintList;
-            return tintInfo;
-        }
-        return null;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (!PLATFORM_SUPPORTS_AUTOSIZE) {
-            autoSizeText();
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    void setTextSize(int unit, float size) {
-        if (!PLATFORM_SUPPORTS_AUTOSIZE) {
-            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/android/support/v7/widget/AppCompatTextHelperV17.java b/android/support/v7/widget/AppCompatTextHelperV17.java
deleted file mode 100644
index d5cc872..0000000
--- a/android/support/v7/widget/AppCompatTextHelperV17.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.v7.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.RequiresApi;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-@RequiresApi(17)
-class AppCompatTextHelperV17 extends AppCompatTextHelper {
-    private TintInfo mDrawableStartTint;
-    private TintInfo mDrawableEndTint;
-
-    AppCompatTextHelperV17(TextView view) {
-        super(view);
-    }
-
-    @Override
-    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
-        super.loadFromAttributes(attrs, defStyleAttr);
-
-        final Context context = mView.getContext();
-        final AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppCompatTextHelper,
-                defStyleAttr, 0);
-        if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableStart)) {
-            mDrawableStartTint = createTintInfo(context, drawableManager,
-                    a.getResourceId(R.styleable.AppCompatTextHelper_android_drawableStart, 0));
-        }
-        if (a.hasValue(R.styleable.AppCompatTextHelper_android_drawableEnd)) {
-            mDrawableEndTint = createTintInfo(context, drawableManager,
-                    a.getResourceId(R.styleable.AppCompatTextHelper_android_drawableEnd, 0));
-        }
-        a.recycle();
-    }
-
-    @Override
-    void applyCompoundDrawablesTints() {
-        super.applyCompoundDrawablesTints();
-
-        if (mDrawableStartTint != null || mDrawableEndTint != null) {
-            final Drawable[] compoundDrawables = mView.getCompoundDrawablesRelative();
-            applyCompoundDrawableTint(compoundDrawables[0], mDrawableStartTint);
-            applyCompoundDrawableTint(compoundDrawables[2], mDrawableEndTint);
-        }
-    }
-}
diff --git a/android/support/v7/widget/AppCompatTextView.java b/android/support/v7/widget/AppCompatTextView.java
deleted file mode 100644
index d813277..0000000
--- a/android/support/v7/widget/AppCompatTextView.java
+++ /dev/null
@@ -1,372 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-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.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.TextView;
-
-/**
- * A {@link TextView} 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
- *     {@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
- * 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,
-        AutoSizeableTextView {
-
-    private final AppCompatBackgroundHelper mBackgroundTintHelper;
-    private final AppCompatTextHelper mTextHelper;
-
-    public AppCompatTextView(Context context) {
-        this(context, null);
-    }
-
-    public AppCompatTextView(Context context, AttributeSet attrs) {
-        this(context, attrs, android.R.attr.textViewStyle);
-    }
-
-    public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
-
-        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
-        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
-
-        mTextHelper = AppCompatTextHelper.create(this);
-        mTextHelper.loadFromAttributes(attrs, defStyleAttr);
-        mTextHelper.applyCompoundDrawablesTints();
-    }
-
-    @Override
-    public void setBackgroundResource(@DrawableRes int resId) {
-        super.setBackgroundResource(resId);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundResource(resId);
-        }
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        super.setBackgroundDrawable(background);
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.onSetBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public ColorStateList getSupportBackgroundTintList() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
-        }
-    }
-
-    /**
-     * This should be accessed via
-     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    @Nullable
-    public PorterDuff.Mode getSupportBackgroundTintMode() {
-        return mBackgroundTintHelper != null
-                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
-    }
-
-    @Override
-    public void setTextAppearance(Context context, int resId) {
-        super.setTextAppearance(context, resId);
-        if (mTextHelper != null) {
-            mTextHelper.onSetTextAppearance(context, resId);
-        }
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mBackgroundTintHelper != null) {
-            mBackgroundTintHelper.applySupportBackgroundTint();
-        }
-        if (mTextHelper != null) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 && !PLATFORM_SUPPORTS_AUTOSIZE && 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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            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 (PLATFORM_SUPPORTS_AUTOSIZE) {
-            return super.getAutoSizeTextAvailableSizes();
-        } else {
-            if (mTextHelper != null) {
-                return mTextHelper.getAutoSizeTextAvailableSizes();
-            }
-        }
-        return new int[0];
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return AppCompatHintHelper.onCreateInputConnection(super.onCreateInputConnection(outAttrs),
-                outAttrs, this);
-    }
-}
diff --git a/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java b/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
deleted file mode 100644
index 6b9d05a..0000000
--- a/android/support/v7/widget/AppCompatTextViewAutoSizeHelper.java
+++ /dev/null
@@ -1,821 +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.v7.widget;
-
-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.RectF;
-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.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.text.method.TransformationMethod;
-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.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 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 ConcurrentHashMap<String, Method> sTextViewMethodByNameCache =
-            new ConcurrentHashMap<>();
-    // 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) {
-        CharSequence text = mTextView.getText();
-        TransformationMethod transformationMethod = mTextView.getTransformationMethod();
-        if (transformationMethod != null) {
-            CharSequence transformedText = transformationMethod.getTransformation(text, mTextView);
-            if (transformedText != null) {
-                text = transformedText;
-            }
-        }
-
-        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;
-    }
-
-    @RequiresApi(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();
-    }
-
-    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/android/support/v7/widget/ButtonBarLayout.java b/android/support/v7/widget/ButtonBarLayout.java
deleted file mode 100644
index f4bbc6c..0000000
--- a/android/support/v7/widget/ButtonBarLayout.java
+++ /dev/null
@@ -1,172 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * An extension of LinearLayout that automatically switches to vertical
- * orientation when it can't fit its child views horizontally.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ButtonBarLayout extends LinearLayout {
-    /** Amount of the second button to "peek" above the fold when stacked. */
-    private static final int PEEK_BUTTON_DP = 16;
-
-    /** Whether the current configuration allows stacking. */
-    private boolean mAllowStacking;
-
-    private int mLastWidthSize = -1;
-
-    private int mMinimumHeight = 0;
-
-    public ButtonBarLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
-        mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, true);
-        ta.recycle();
-    }
-
-    public void setAllowStacking(boolean allowStacking) {
-        if (mAllowStacking != allowStacking) {
-            mAllowStacking = allowStacking;
-            if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
-                setStacked(false);
-            }
-            requestLayout();
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-
-        if (mAllowStacking) {
-            if (widthSize > mLastWidthSize && isStacked()) {
-                // We're being measured wider this time, try un-stacking.
-                setStacked(false);
-            }
-
-            mLastWidthSize = widthSize;
-        }
-
-        boolean needsRemeasure = false;
-
-        // If we're not stacked, make sure the measure spec is AT_MOST rather
-        // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
-        // know to stack the buttons.
-        final int initialWidthMeasureSpec;
-        if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
-            initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
-
-            // We'll need to remeasure again to fill excess space.
-            needsRemeasure = true;
-        } else {
-            initialWidthMeasureSpec = widthMeasureSpec;
-        }
-
-        super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
-
-        if (mAllowStacking && !isStacked()) {
-            final boolean stack;
-
-            final int measuredWidth = getMeasuredWidthAndState();
-            final int measuredWidthState = measuredWidth & View.MEASURED_STATE_MASK;
-            stack = measuredWidthState == View.MEASURED_STATE_TOO_SMALL;
-
-            if (stack) {
-                setStacked(true);
-                // Measure again in the new orientation.
-                needsRemeasure = true;
-            }
-        }
-
-        if (needsRemeasure) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-
-        // Compute minimum height such that, when stacked, some portion of the
-        // second button is visible.
-        int minHeight = 0;
-        final int firstVisible = getNextVisibleChildIndex(0);
-        if (firstVisible >= 0) {
-            final View firstButton = getChildAt(firstVisible);
-            final LayoutParams firstParams = (LayoutParams) firstButton.getLayoutParams();
-            minHeight += getPaddingTop() + firstButton.getMeasuredHeight()
-                    + firstParams.topMargin + firstParams.bottomMargin;
-            if (isStacked()) {
-                final int secondVisible = getNextVisibleChildIndex(firstVisible + 1);
-                if (secondVisible >= 0) {
-                    minHeight += getChildAt(secondVisible).getPaddingTop()
-                            + (int) (PEEK_BUTTON_DP * getResources().getDisplayMetrics().density);
-                }
-            } else {
-                minHeight += getPaddingBottom();
-            }
-        }
-
-        if (ViewCompat.getMinimumHeight(this) != minHeight) {
-            setMinimumHeight(minHeight);
-        }
-    }
-
-    private int getNextVisibleChildIndex(int index) {
-        for (int i = index, count = getChildCount(); i < count; i++) {
-            if (getChildAt(i).getVisibility() == View.VISIBLE) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        return Math.max(mMinimumHeight, super.getMinimumHeight());
-    }
-
-    private void setStacked(boolean stacked) {
-        setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
-        setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM);
-
-        final View spacer = findViewById(R.id.spacer);
-        if (spacer != null) {
-            spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
-        }
-
-        // Reverse the child order. This is specific to the Material button
-        // bar's layout XML and will probably not generalize.
-        final int childCount = getChildCount();
-        for (int i = childCount - 2; i >= 0; i--) {
-            bringChildToFront(getChildAt(i));
-        }
-    }
-
-    private boolean isStacked() {
-        return getOrientation() == LinearLayout.VERTICAL;
-    }
-}
diff --git a/android/support/v7/widget/CardView.java b/android/support/v7/widget/CardView.java
deleted file mode 100644
index a45ee98..0000000
--- a/android/support/v7/widget/CardView.java
+++ /dev/null
@@ -1,487 +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.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Rect;
-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.v7.cardview.R;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-/**
- * A FrameLayout with a rounded corner background and shadow.
- * <p>
- * CardView uses <code>elevation</code> property on Lollipop for shadows and falls back to a
- * custom emulated shadow implementation on older platforms.
- * <p>
- * Due to expensive nature of rounded corner clipping, on platforms before Lollipop, CardView does
- * not clip its children that intersect with rounded corners. Instead, it adds padding to avoid such
- * intersection (See {@link #setPreventCornerOverlap(boolean)} to change this behavior).
- * <p>
- * Before Lollipop, CardView adds padding to its content and draws shadows to that area. This
- * padding amount is equal to <code>maxCardElevation + (1 - cos45) * cornerRadius</code> on the
- * sides and <code>maxCardElevation * 1.5 + (1 - cos45) * cornerRadius</code> on top and bottom.
- * <p>
- * Since padding is used to offset content for shadows, you cannot set padding on CardView.
- * Instead, you can use content padding attributes in XML or
- * {@link #setContentPadding(int, int, int, int)} in code to set the padding between the edges of
- * the CardView and children of CardView.
- * <p>
- * Note that, if you specify exact dimensions for the CardView, because of the shadows, its content
- * area will be different between platforms before Lollipop and after Lollipop. By using api version
- * specific resource values, you can avoid these changes. Alternatively, If you want CardView to add
- * inner padding on platforms Lollipop and after as well, you can call
- * {@link #setUseCompatPadding(boolean)} and pass <code>true</code>.
- * <p>
- * To change CardView's elevation in a backward compatible way, use
- * {@link #setCardElevation(float)}. CardView will use elevation API on Lollipop and before
- * Lollipop, it will change the shadow size. To avoid moving the View while shadow size is changing,
- * shadow size is clamped by {@link #getMaxCardElevation()}. If you want to change elevation
- * dynamically, you should call {@link #setMaxCardElevation(float)} when CardView is initialized.
- *
- * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor
- * @attr ref android.support.v7.cardview.R.styleable#CardView_cardCornerRadius
- * @attr ref android.support.v7.cardview.R.styleable#CardView_cardElevation
- * @attr ref android.support.v7.cardview.R.styleable#CardView_cardMaxElevation
- * @attr ref android.support.v7.cardview.R.styleable#CardView_cardUseCompatPadding
- * @attr ref android.support.v7.cardview.R.styleable#CardView_cardPreventCornerOverlap
- * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPadding
- * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingLeft
- * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingTop
- * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingRight
- * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingBottom
- */
-public class CardView extends FrameLayout {
-
-    private static final int[] COLOR_BACKGROUND_ATTR = {android.R.attr.colorBackground};
-    private static final CardViewImpl IMPL;
-
-    static {
-        if (Build.VERSION.SDK_INT >= 21) {
-            IMPL = new CardViewApi21Impl();
-        } else if (Build.VERSION.SDK_INT >= 17) {
-            IMPL = new CardViewApi17Impl();
-        } else {
-            IMPL = new CardViewBaseImpl();
-        }
-        IMPL.initStatic();
-    }
-
-    private boolean mCompatPadding;
-
-    private boolean mPreventCornerOverlap;
-
-    /**
-     * CardView requires to have a particular minimum size to draw shadows before API 21. If
-     * developer also sets min width/height, they might be overridden.
-     *
-     * CardView works around this issue by recording user given parameters and using an internal
-     * method to set them.
-     */
-    int mUserSetMinWidth, mUserSetMinHeight;
-
-    final Rect mContentPadding = new Rect();
-
-    final Rect mShadowBounds = new Rect();
-
-    public CardView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public CardView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, R.attr.cardViewStyle);
-    }
-
-    public CardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr,
-                R.style.CardView);
-        ColorStateList backgroundColor;
-        if (a.hasValue(R.styleable.CardView_cardBackgroundColor)) {
-            backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);
-        } else {
-            // There isn't one set, so we'll compute one based on the theme
-            final TypedArray aa = getContext().obtainStyledAttributes(COLOR_BACKGROUND_ATTR);
-            final int themeColorBackground = aa.getColor(0, 0);
-            aa.recycle();
-
-            // If the theme colorBackground is light, use our own light color, otherwise dark
-            final float[] hsv = new float[3];
-            Color.colorToHSV(themeColorBackground, hsv);
-            backgroundColor = ColorStateList.valueOf(hsv[2] > 0.5f
-                    ? getResources().getColor(R.color.cardview_light_background)
-                    : getResources().getColor(R.color.cardview_dark_background));
-        }
-        float radius = a.getDimension(R.styleable.CardView_cardCornerRadius, 0);
-        float elevation = a.getDimension(R.styleable.CardView_cardElevation, 0);
-        float maxElevation = a.getDimension(R.styleable.CardView_cardMaxElevation, 0);
-        mCompatPadding = a.getBoolean(R.styleable.CardView_cardUseCompatPadding, false);
-        mPreventCornerOverlap = a.getBoolean(R.styleable.CardView_cardPreventCornerOverlap, true);
-        int defaultPadding = a.getDimensionPixelSize(R.styleable.CardView_contentPadding, 0);
-        mContentPadding.left = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingLeft,
-                defaultPadding);
-        mContentPadding.top = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingTop,
-                defaultPadding);
-        mContentPadding.right = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingRight,
-                defaultPadding);
-        mContentPadding.bottom = a.getDimensionPixelSize(R.styleable.CardView_contentPaddingBottom,
-                defaultPadding);
-        if (elevation > maxElevation) {
-            maxElevation = elevation;
-        }
-        mUserSetMinWidth = a.getDimensionPixelSize(R.styleable.CardView_android_minWidth, 0);
-        mUserSetMinHeight = a.getDimensionPixelSize(R.styleable.CardView_android_minHeight, 0);
-        a.recycle();
-
-        IMPL.initialize(mCardViewDelegate, context, backgroundColor, radius,
-                elevation, maxElevation);
-    }
-
-    @Override
-    public void setPadding(int left, int top, int right, int bottom) {
-        // NO OP
-    }
-
-    @Override
-    public void setPaddingRelative(int start, int top, int end, int bottom) {
-        // NO OP
-    }
-
-    /**
-     * Returns whether CardView will add inner padding on platforms Lollipop and after.
-     *
-     * @return <code>true</code> if CardView adds inner padding on platforms Lollipop and after to
-     * have same dimensions with platforms before Lollipop.
-     */
-    public boolean getUseCompatPadding() {
-        return mCompatPadding;
-    }
-
-    /**
-     * CardView adds additional padding to draw shadows on platforms before Lollipop.
-     * <p>
-     * This may cause Cards to have different sizes between Lollipop and before Lollipop. If you
-     * need to align CardView with other Views, you may need api version specific dimension
-     * resources to account for the changes.
-     * As an alternative, you can set this flag to <code>true</code> and CardView will add the same
-     * padding values on platforms Lollipop and after.
-     * <p>
-     * Since setting this flag to true adds unnecessary gaps in the UI, default value is
-     * <code>false</code>.
-     *
-     * @param useCompatPadding <code>true></code> if CardView should add padding for the shadows on
-     *      platforms Lollipop and above.
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardUseCompatPadding
-     */
-    public void setUseCompatPadding(boolean useCompatPadding) {
-        if (mCompatPadding != useCompatPadding) {
-            mCompatPadding = useCompatPadding;
-            IMPL.onCompatPaddingChanged(mCardViewDelegate);
-        }
-    }
-
-    /**
-     * Sets the padding between the Card's edges and the children of CardView.
-     * <p>
-     * Depending on platform version or {@link #getUseCompatPadding()} settings, CardView may
-     * update these values before calling {@link android.view.View#setPadding(int, int, int, int)}.
-     *
-     * @param left   The left padding in pixels
-     * @param top    The top padding in pixels
-     * @param right  The right padding in pixels
-     * @param bottom The bottom padding in pixels
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPadding
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingLeft
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingTop
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingRight
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingBottom
-     */
-    public void setContentPadding(int left, int top, int right, int bottom) {
-        mContentPadding.set(left, top, right, bottom);
-        IMPL.updatePadding(mCardViewDelegate);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (!(IMPL instanceof CardViewApi21Impl)) {
-            final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-            switch (widthMode) {
-                case MeasureSpec.EXACTLY:
-                case MeasureSpec.AT_MOST:
-                    final int minWidth = (int) Math.ceil(IMPL.getMinWidth(mCardViewDelegate));
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minWidth,
-                            MeasureSpec.getSize(widthMeasureSpec)), widthMode);
-                    break;
-                case MeasureSpec.UNSPECIFIED:
-                    // Do nothing
-                    break;
-            }
-
-            final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-            switch (heightMode) {
-                case MeasureSpec.EXACTLY:
-                case MeasureSpec.AT_MOST:
-                    final int minHeight = (int) Math.ceil(IMPL.getMinHeight(mCardViewDelegate));
-                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minHeight,
-                            MeasureSpec.getSize(heightMeasureSpec)), heightMode);
-                    break;
-                case MeasureSpec.UNSPECIFIED:
-                    // Do nothing
-                    break;
-            }
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        } else {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    @Override
-    public void setMinimumWidth(int minWidth) {
-        mUserSetMinWidth = minWidth;
-        super.setMinimumWidth(minWidth);
-    }
-
-    @Override
-    public void setMinimumHeight(int minHeight) {
-        mUserSetMinHeight = minHeight;
-        super.setMinimumHeight(minHeight);
-    }
-
-    /**
-     * Updates the background color of the CardView
-     *
-     * @param color The new color to set for the card background
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor
-     */
-    public void setCardBackgroundColor(@ColorInt int color) {
-        IMPL.setBackgroundColor(mCardViewDelegate, ColorStateList.valueOf(color));
-    }
-
-    /**
-     * Updates the background ColorStateList of the CardView
-     *
-     * @param color The new ColorStateList to set for the card background
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor
-     */
-    public void setCardBackgroundColor(@Nullable ColorStateList color) {
-        IMPL.setBackgroundColor(mCardViewDelegate, color);
-    }
-
-    /**
-     * Returns the background color state list of the CardView.
-     *
-     * @return The background color state list of the CardView.
-     */
-    @NonNull
-    public ColorStateList getCardBackgroundColor() {
-        return IMPL.getBackgroundColor(mCardViewDelegate);
-    }
-
-    /**
-     * Returns the inner padding after the Card's left edge
-     *
-     * @return the inner padding after the Card's left edge
-     */
-    public int getContentPaddingLeft() {
-        return mContentPadding.left;
-    }
-
-    /**
-     * Returns the inner padding before the Card's right edge
-     *
-     * @return the inner padding before the Card's right edge
-     */
-    public int getContentPaddingRight() {
-        return mContentPadding.right;
-    }
-
-    /**
-     * Returns the inner padding after the Card's top edge
-     *
-     * @return the inner padding after the Card's top edge
-     */
-    public int getContentPaddingTop() {
-        return mContentPadding.top;
-    }
-
-    /**
-     * Returns the inner padding before the Card's bottom edge
-     *
-     * @return the inner padding before the Card's bottom edge
-     */
-    public int getContentPaddingBottom() {
-        return mContentPadding.bottom;
-    }
-
-    /**
-     * Updates the corner radius of the CardView.
-     *
-     * @param radius The radius in pixels of the corners of the rectangle shape
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardCornerRadius
-     * @see #setRadius(float)
-     */
-    public void setRadius(float radius) {
-        IMPL.setRadius(mCardViewDelegate, radius);
-    }
-
-    /**
-     * Returns the corner radius of the CardView.
-     *
-     * @return Corner radius of the CardView
-     * @see #getRadius()
-     */
-    public float getRadius() {
-        return IMPL.getRadius(mCardViewDelegate);
-    }
-
-    /**
-     * Updates the backward compatible elevation of the CardView.
-     *
-     * @param elevation The backward compatible elevation in pixels.
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardElevation
-     * @see #getCardElevation()
-     * @see #setMaxCardElevation(float)
-     */
-    public void setCardElevation(float elevation) {
-        IMPL.setElevation(mCardViewDelegate, elevation);
-    }
-
-    /**
-     * Returns the backward compatible elevation of the CardView.
-     *
-     * @return Elevation of the CardView
-     * @see #setCardElevation(float)
-     * @see #getMaxCardElevation()
-     */
-    public float getCardElevation() {
-        return IMPL.getElevation(mCardViewDelegate);
-    }
-
-    /**
-     * Updates the backward compatible maximum elevation of the CardView.
-     * <p>
-     * Calling this method has no effect if device OS version is Lollipop or newer and
-     * {@link #getUseCompatPadding()} is <code>false</code>.
-     *
-     * @param maxElevation The backward compatible maximum elevation in pixels.
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardMaxElevation
-     * @see #setCardElevation(float)
-     * @see #getMaxCardElevation()
-     */
-    public void setMaxCardElevation(float maxElevation) {
-        IMPL.setMaxElevation(mCardViewDelegate, maxElevation);
-    }
-
-    /**
-     * Returns the backward compatible maximum elevation of the CardView.
-     *
-     * @return Maximum elevation of the CardView
-     * @see #setMaxCardElevation(float)
-     * @see #getCardElevation()
-     */
-    public float getMaxCardElevation() {
-        return IMPL.getMaxElevation(mCardViewDelegate);
-    }
-
-    /**
-     * Returns whether CardView should add extra padding to content to avoid overlaps with rounded
-     * corners on pre-Lollipop platforms.
-     *
-     * @return True if CardView prevents overlaps with rounded corners on platforms before Lollipop.
-     *         Default value is <code>true</code>.
-     */
-    public boolean getPreventCornerOverlap() {
-        return mPreventCornerOverlap;
-    }
-
-    /**
-     * On pre-Lollipop platforms, CardView does not clip the bounds of the Card for the rounded
-     * corners. Instead, it adds padding to content so that it won't overlap with the rounded
-     * corners. You can disable this behavior by setting this field to <code>false</code>.
-     * <p>
-     * Setting this value on Lollipop and above does not have any effect unless you have enabled
-     * compatibility padding.
-     *
-     * @param preventCornerOverlap Whether CardView should add extra padding to content to avoid
-     *                             overlaps with the CardView corners.
-     * @attr ref android.support.v7.cardview.R.styleable#CardView_cardPreventCornerOverlap
-     * @see #setUseCompatPadding(boolean)
-     */
-    public void setPreventCornerOverlap(boolean preventCornerOverlap) {
-        if (preventCornerOverlap != mPreventCornerOverlap) {
-            mPreventCornerOverlap = preventCornerOverlap;
-            IMPL.onPreventCornerOverlapChanged(mCardViewDelegate);
-        }
-    }
-
-    private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {
-        private Drawable mCardBackground;
-
-        @Override
-        public void setCardBackground(Drawable drawable) {
-            mCardBackground = drawable;
-            setBackgroundDrawable(drawable);
-        }
-
-        @Override
-        public boolean getUseCompatPadding() {
-            return CardView.this.getUseCompatPadding();
-        }
-
-        @Override
-        public boolean getPreventCornerOverlap() {
-            return CardView.this.getPreventCornerOverlap();
-        }
-
-        @Override
-        public void setShadowPadding(int left, int top, int right, int bottom) {
-            mShadowBounds.set(left, top, right, bottom);
-            CardView.super.setPadding(left + mContentPadding.left, top + mContentPadding.top,
-                    right + mContentPadding.right, bottom + mContentPadding.bottom);
-        }
-
-        @Override
-        public void setMinWidthHeightInternal(int width, int height) {
-            if (width > mUserSetMinWidth) {
-                CardView.super.setMinimumWidth(width);
-            }
-            if (height > mUserSetMinHeight) {
-                CardView.super.setMinimumHeight(height);
-            }
-        }
-
-        @Override
-        public Drawable getCardBackground() {
-            return mCardBackground;
-        }
-
-        @Override
-        public View getCardView() {
-            return CardView.this;
-        }
-    };
-}
diff --git a/android/support/v7/widget/CardViewApi17Impl.java b/android/support/v7/widget/CardViewApi17Impl.java
deleted file mode 100644
index 3a1a4e1..0000000
--- a/android/support/v7/widget/CardViewApi17Impl.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.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/android/support/v7/widget/CardViewApi21Impl.java b/android/support/v7/widget/CardViewApi21Impl.java
deleted file mode 100644
index 7af50e5..0000000
--- a/android/support/v7/widget/CardViewApi21Impl.java
+++ /dev/null
@@ -1,123 +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.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/android/support/v7/widget/CardViewBaseImpl.java b/android/support/v7/widget/CardViewBaseImpl.java
deleted file mode 100644
index 8cb38be..0000000
--- a/android/support/v7/widget/CardViewBaseImpl.java
+++ /dev/null
@@ -1,171 +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.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/android/support/v7/widget/CardViewDelegate.java b/android/support/v7/widget/CardViewDelegate.java
deleted file mode 100644
index 7b1b83a..0000000
--- a/android/support/v7/widget/CardViewDelegate.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.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/android/support/v7/widget/CardViewImpl.java b/android/support/v7/widget/CardViewImpl.java
deleted file mode 100644
index 26799da..0000000
--- a/android/support/v7/widget/CardViewImpl.java
+++ /dev/null
@@ -1,56 +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.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/android/support/v7/widget/ChildHelper.java b/android/support/v7/widget/ChildHelper.java
deleted file mode 100644
index f306931..0000000
--- a/android/support/v7/widget/ChildHelper.java
+++ /dev/null
@@ -1,537 +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.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class to manage children.
- * <p>
- * It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
- * provided by this class. <b>Regular</b> methods are the ones that replicate ViewGroup methods
- * like getChildAt, getChildCount etc. These methods ignore hidden children.
- * <p>
- * When RecyclerView needs direct access to the view group children, it can call unfiltered
- * methods like get getUnfilteredChildCount or getUnfilteredChildAt.
- */
-class ChildHelper {
-
-    private static final boolean DEBUG = false;
-
-    private static final String TAG = "ChildrenHelper";
-
-    final Callback mCallback;
-
-    final Bucket mBucket;
-
-    final List<View> mHiddenViews;
-
-    ChildHelper(Callback callback) {
-        mCallback = callback;
-        mBucket = new Bucket();
-        mHiddenViews = new ArrayList<View>();
-    }
-
-    /**
-     * Marks a child view as hidden
-     *
-     * @param child  View to hide.
-     */
-    private void hideViewInternal(View child) {
-        mHiddenViews.add(child);
-        mCallback.onEnteredHiddenState(child);
-    }
-
-    /**
-     * Unmarks a child view as hidden.
-     *
-     * @param child  View to hide.
-     */
-    private boolean unhideViewInternal(View child) {
-        if (mHiddenViews.remove(child)) {
-            mCallback.onLeftHiddenState(child);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Adds a view to the ViewGroup
-     *
-     * @param child  View to add.
-     * @param hidden If set to true, this item will be invisible from regular methods.
-     */
-    void addView(View child, boolean hidden) {
-        addView(child, -1, hidden);
-    }
-
-    /**
-     * Add a view to the ViewGroup at an index
-     *
-     * @param child  View to add.
-     * @param index  Index of the child from the regular perspective (excluding hidden views).
-     *               ChildHelper offsets this index to actual ViewGroup index.
-     * @param hidden If set to true, this item will be invisible from regular methods.
-     */
-    void addView(View child, int index, boolean hidden) {
-        final int offset;
-        if (index < 0) {
-            offset = mCallback.getChildCount();
-        } else {
-            offset = getOffset(index);
-        }
-        mBucket.insert(offset, hidden);
-        if (hidden) {
-            hideViewInternal(child);
-        }
-        mCallback.addView(child, offset);
-        if (DEBUG) {
-            Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
-        }
-    }
-
-    private int getOffset(int index) {
-        if (index < 0) {
-            return -1; //anything below 0 won't work as diff will be undefined.
-        }
-        final int limit = mCallback.getChildCount();
-        int offset = index;
-        while (offset < limit) {
-            final int removedBefore = mBucket.countOnesBefore(offset);
-            final int diff = index - (offset - removedBefore);
-            if (diff == 0) {
-                while (mBucket.get(offset)) { // ensure this offset is not hidden
-                    offset++;
-                }
-                return offset;
-            } else {
-                offset += diff;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Removes the provided View from underlying RecyclerView.
-     *
-     * @param view The view to remove.
-     */
-    void removeView(View view) {
-        int index = mCallback.indexOfChild(view);
-        if (index < 0) {
-            return;
-        }
-        if (mBucket.remove(index)) {
-            unhideViewInternal(view);
-        }
-        mCallback.removeViewAt(index);
-        if (DEBUG) {
-            Log.d(TAG, "remove View off:" + index + "," + this);
-        }
-    }
-
-    /**
-     * Removes the view at the provided index from RecyclerView.
-     *
-     * @param index Index of the child from the regular perspective (excluding hidden views).
-     *              ChildHelper offsets this index to actual ViewGroup index.
-     */
-    void removeViewAt(int index) {
-        final int offset = getOffset(index);
-        final View view = mCallback.getChildAt(offset);
-        if (view == null) {
-            return;
-        }
-        if (mBucket.remove(offset)) {
-            unhideViewInternal(view);
-        }
-        mCallback.removeViewAt(offset);
-        if (DEBUG) {
-            Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
-        }
-    }
-
-    /**
-     * Returns the child at provided index.
-     *
-     * @param index Index of the child to return in regular perspective.
-     */
-    View getChildAt(int index) {
-        final int offset = getOffset(index);
-        return mCallback.getChildAt(offset);
-    }
-
-    /**
-     * Removes all views from the ViewGroup including the hidden ones.
-     */
-    void removeAllViewsUnfiltered() {
-        mBucket.reset();
-        for (int i = mHiddenViews.size() - 1; i >= 0; i--) {
-            mCallback.onLeftHiddenState(mHiddenViews.get(i));
-            mHiddenViews.remove(i);
-        }
-        mCallback.removeAllViews();
-        if (DEBUG) {
-            Log.d(TAG, "removeAllViewsUnfiltered");
-        }
-    }
-
-    /**
-     * This can be used to find a disappearing view by position.
-     *
-     * @param position The adapter position of the item.
-     * @return         A hidden view with a valid ViewHolder that matches the position.
-     */
-    View findHiddenNonRemovedView(int position) {
-        final int count = mHiddenViews.size();
-        for (int i = 0; i < count; i++) {
-            final View view = mHiddenViews.get(i);
-            RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
-            if (holder.getLayoutPosition() == position
-                    && !holder.isInvalid()
-                    && !holder.isRemoved()) {
-                return view;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Attaches the provided view to the underlying ViewGroup.
-     *
-     * @param child        Child to attach.
-     * @param index        Index of the child to attach in regular perspective.
-     * @param layoutParams LayoutParams for the child.
-     * @param hidden       If set to true, this item will be invisible to the regular methods.
-     */
-    void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,
-            boolean hidden) {
-        final int offset;
-        if (index < 0) {
-            offset = mCallback.getChildCount();
-        } else {
-            offset = getOffset(index);
-        }
-        mBucket.insert(offset, hidden);
-        if (hidden) {
-            hideViewInternal(child);
-        }
-        mCallback.attachViewToParent(child, offset, layoutParams);
-        if (DEBUG) {
-            Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + ","
-                    + "h:" + hidden + ", " + this);
-        }
-    }
-
-    /**
-     * Returns the number of children that are not hidden.
-     *
-     * @return Number of children that are not hidden.
-     * @see #getChildAt(int)
-     */
-    int getChildCount() {
-        return mCallback.getChildCount() - mHiddenViews.size();
-    }
-
-    /**
-     * Returns the total number of children.
-     *
-     * @return The total number of children including the hidden views.
-     * @see #getUnfilteredChildAt(int)
-     */
-    int getUnfilteredChildCount() {
-        return mCallback.getChildCount();
-    }
-
-    /**
-     * Returns a child by ViewGroup offset. ChildHelper won't offset this index.
-     *
-     * @param index ViewGroup index of the child to return.
-     * @return The view in the provided index.
-     */
-    View getUnfilteredChildAt(int index) {
-        return mCallback.getChildAt(index);
-    }
-
-    /**
-     * Detaches the view at the provided index.
-     *
-     * @param index Index of the child to return in regular perspective.
-     */
-    void detachViewFromParent(int index) {
-        final int offset = getOffset(index);
-        mBucket.remove(offset);
-        mCallback.detachViewFromParent(offset);
-        if (DEBUG) {
-            Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
-        }
-    }
-
-    /**
-     * Returns the index of the child in regular perspective.
-     *
-     * @param child The child whose index will be returned.
-     * @return The regular perspective index of the child or -1 if it does not exists.
-     */
-    int indexOfChild(View child) {
-        final int index = mCallback.indexOfChild(child);
-        if (index == -1) {
-            return -1;
-        }
-        if (mBucket.get(index)) {
-            if (DEBUG) {
-                throw new IllegalArgumentException("cannot get index of a hidden child");
-            } else {
-                return -1;
-            }
-        }
-        // reverse the index
-        return index - mBucket.countOnesBefore(index);
-    }
-
-    /**
-     * Returns whether a View is visible to LayoutManager or not.
-     *
-     * @param view The child view to check. Should be a child of the Callback.
-     * @return True if the View is not visible to LayoutManager
-     */
-    boolean isHidden(View view) {
-        return mHiddenViews.contains(view);
-    }
-
-    /**
-     * Marks a child view as hidden.
-     *
-     * @param view The view to hide.
-     */
-    void hide(View view) {
-        final int offset = mCallback.indexOfChild(view);
-        if (offset < 0) {
-            throw new IllegalArgumentException("view is not a child, cannot hide " + view);
-        }
-        if (DEBUG && mBucket.get(offset)) {
-            throw new RuntimeException("trying to hide same view twice, how come ? " + view);
-        }
-        mBucket.set(offset);
-        hideViewInternal(view);
-        if (DEBUG) {
-            Log.d(TAG, "hiding child " + view + " at offset " + offset + ", " + this);
-        }
-    }
-
-    /**
-     * Moves a child view from hidden list to regular list.
-     * Calling this method should probably be followed by a detach, otherwise, it will suddenly
-     * show up in LayoutManager's children list.
-     *
-     * @param view The hidden View to unhide
-     */
-    void unhide(View view) {
-        final int offset = mCallback.indexOfChild(view);
-        if (offset < 0) {
-            throw new IllegalArgumentException("view is not a child, cannot hide " + view);
-        }
-        if (!mBucket.get(offset)) {
-            throw new RuntimeException("trying to unhide a view that was not hidden" + view);
-        }
-        mBucket.clear(offset);
-        unhideViewInternal(view);
-    }
-
-    @Override
-    public String toString() {
-        return mBucket.toString() + ", hidden list:" + mHiddenViews.size();
-    }
-
-    /**
-     * Removes a view from the ViewGroup if it is hidden.
-     *
-     * @param view The view to remove.
-     * @return True if the View is found and it is hidden. False otherwise.
-     */
-    boolean removeViewIfHidden(View view) {
-        final int index = mCallback.indexOfChild(view);
-        if (index == -1) {
-            if (unhideViewInternal(view) && DEBUG) {
-                throw new IllegalStateException("view is in hidden list but not in view group");
-            }
-            return true;
-        }
-        if (mBucket.get(index)) {
-            mBucket.remove(index);
-            if (!unhideViewInternal(view) && DEBUG) {
-                throw new IllegalStateException(
-                        "removed a hidden view but it is not in hidden views list");
-            }
-            mCallback.removeViewAt(index);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Bitset implementation that provides methods to offset indices.
-     */
-    static class Bucket {
-
-        static final int BITS_PER_WORD = Long.SIZE;
-
-        static final long LAST_BIT = 1L << (Long.SIZE - 1);
-
-        long mData = 0;
-
-        Bucket mNext;
-
-        void set(int index) {
-            if (index >= BITS_PER_WORD) {
-                ensureNext();
-                mNext.set(index - BITS_PER_WORD);
-            } else {
-                mData |= 1L << index;
-            }
-        }
-
-        private void ensureNext() {
-            if (mNext == null) {
-                mNext = new Bucket();
-            }
-        }
-
-        void clear(int index) {
-            if (index >= BITS_PER_WORD) {
-                if (mNext != null) {
-                    mNext.clear(index - BITS_PER_WORD);
-                }
-            } else {
-                mData &= ~(1L << index);
-            }
-
-        }
-
-        boolean get(int index) {
-            if (index >= BITS_PER_WORD) {
-                ensureNext();
-                return mNext.get(index - BITS_PER_WORD);
-            } else {
-                return (mData & (1L << index)) != 0;
-            }
-        }
-
-        void reset() {
-            mData = 0;
-            if (mNext != null) {
-                mNext.reset();
-            }
-        }
-
-        void insert(int index, boolean value) {
-            if (index >= BITS_PER_WORD) {
-                ensureNext();
-                mNext.insert(index - BITS_PER_WORD, value);
-            } else {
-                final boolean lastBit = (mData & LAST_BIT) != 0;
-                long mask = (1L << index) - 1;
-                final long before = mData & mask;
-                final long after = ((mData & ~mask)) << 1;
-                mData = before | after;
-                if (value) {
-                    set(index);
-                } else {
-                    clear(index);
-                }
-                if (lastBit || mNext != null) {
-                    ensureNext();
-                    mNext.insert(0, lastBit);
-                }
-            }
-        }
-
-        boolean remove(int index) {
-            if (index >= BITS_PER_WORD) {
-                ensureNext();
-                return mNext.remove(index - BITS_PER_WORD);
-            } else {
-                long mask = (1L << index);
-                final boolean value = (mData & mask) != 0;
-                mData &= ~mask;
-                mask = mask - 1;
-                final long before = mData & mask;
-                // cannot use >> because it adds one.
-                final long after = Long.rotateRight(mData & ~mask, 1);
-                mData = before | after;
-                if (mNext != null) {
-                    if (mNext.get(0)) {
-                        set(BITS_PER_WORD - 1);
-                    }
-                    mNext.remove(0);
-                }
-                return value;
-            }
-        }
-
-        int countOnesBefore(int index) {
-            if (mNext == null) {
-                if (index >= BITS_PER_WORD) {
-                    return Long.bitCount(mData);
-                }
-                return Long.bitCount(mData & ((1L << index) - 1));
-            }
-            if (index < BITS_PER_WORD) {
-                return Long.bitCount(mData & ((1L << index) - 1));
-            } else {
-                return mNext.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return mNext == null ? Long.toBinaryString(mData)
-                    : mNext.toString() + "xx" + Long.toBinaryString(mData);
-        }
-    }
-
-    interface Callback {
-
-        int getChildCount();
-
-        void addView(View child, int index);
-
-        int indexOfChild(View view);
-
-        void removeViewAt(int index);
-
-        View getChildAt(int offset);
-
-        void removeAllViews();
-
-        RecyclerView.ViewHolder getChildViewHolder(View view);
-
-        void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams);
-
-        void detachViewFromParent(int offset);
-
-        void onEnteredHiddenState(View child);
-
-        void onLeftHiddenState(View child);
-    }
-}
diff --git a/android/support/v7/widget/ContentFrameLayout.java b/android/support/v7/widget/ContentFrameLayout.java
deleted file mode 100644
index f777901..0000000
--- a/android/support/v7/widget/ContentFrameLayout.java
+++ /dev/null
@@ -1,217 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.view.View.MeasureSpec.AT_MOST;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.getMode;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.widget.FrameLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY)
-public class ContentFrameLayout extends FrameLayout {
-
-    public interface OnAttachListener {
-        void onDetachedFromWindow();
-        void onAttachedFromWindow();
-    }
-
-    private TypedValue mMinWidthMajor;
-    private TypedValue mMinWidthMinor;
-    private TypedValue mFixedWidthMajor;
-    private TypedValue mFixedWidthMinor;
-    private TypedValue mFixedHeightMajor;
-    private TypedValue mFixedHeightMinor;
-
-    private final Rect mDecorPadding;
-
-    private OnAttachListener mAttachListener;
-
-    public ContentFrameLayout(Context context) {
-        this(context, null);
-    }
-
-    public ContentFrameLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ContentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mDecorPadding = new Rect();
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void dispatchFitSystemWindows(Rect insets) {
-        fitSystemWindows(insets);
-    }
-
-    public void setAttachListener(OnAttachListener attachListener) {
-        mAttachListener = attachListener;
-    }
-
-    /**
-     * Notify this view of the window decor view's padding. We use these values when working out
-     * our size for the window size attributes.
-     *
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setDecorPadding(int left, int top, int right, int bottom) {
-        mDecorPadding.set(left, top, right, bottom);
-        if (ViewCompat.isLaidOut(this)) {
-            requestLayout();
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
-        final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
-
-        final int widthMode = getMode(widthMeasureSpec);
-        final int heightMode = getMode(heightMeasureSpec);
-
-        boolean fixedWidth = false;
-        if (widthMode == AT_MOST) {
-            final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
-            if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
-                int w = 0;
-                if (tvw.type == TypedValue.TYPE_DIMENSION) {
-                    w = (int) tvw.getDimension(metrics);
-                } else if (tvw.type == TypedValue.TYPE_FRACTION) {
-                    w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
-                }
-                if (w > 0) {
-                    w -= (mDecorPadding.left + mDecorPadding.right);
-                    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            Math.min(w, widthSize), EXACTLY);
-                    fixedWidth = true;
-                }
-            }
-        }
-
-        if (heightMode == AT_MOST) {
-            final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
-            if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
-                int h = 0;
-                if (tvh.type == TypedValue.TYPE_DIMENSION) {
-                    h = (int) tvh.getDimension(metrics);
-                } else if (tvh.type == TypedValue.TYPE_FRACTION) {
-                    h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
-                }
-                if (h > 0) {
-                    h -= (mDecorPadding.top + mDecorPadding.bottom);
-                    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                            Math.min(h, heightSize), EXACTLY);
-                }
-            }
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        int width = getMeasuredWidth();
-        boolean measure = false;
-
-        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
-
-        if (!fixedWidth && widthMode == AT_MOST) {
-            final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
-            if (tv != null && tv.type != TypedValue.TYPE_NULL) {
-                int min = 0;
-                if (tv.type == TypedValue.TYPE_DIMENSION) {
-                    min = (int) tv.getDimension(metrics);
-                } else if (tv.type == TypedValue.TYPE_FRACTION) {
-                    min = (int) tv.getFraction(metrics.widthPixels, metrics.widthPixels);
-                }
-                if (min > 0) {
-                    min -= (mDecorPadding.left + mDecorPadding.right);
-                }
-                if (width < min) {
-                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
-                    measure = true;
-                }
-            }
-        }
-
-        if (measure) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    public TypedValue getMinWidthMajor() {
-        if (mMinWidthMajor == null) mMinWidthMajor = new TypedValue();
-        return mMinWidthMajor;
-    }
-
-    public TypedValue getMinWidthMinor() {
-        if (mMinWidthMinor == null) mMinWidthMinor = new TypedValue();
-        return mMinWidthMinor;
-    }
-
-    public TypedValue getFixedWidthMajor() {
-        if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();
-        return mFixedWidthMajor;
-    }
-
-    public TypedValue getFixedWidthMinor() {
-        if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();
-        return mFixedWidthMinor;
-    }
-
-    public TypedValue getFixedHeightMajor() {
-        if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();
-        return mFixedHeightMajor;
-    }
-
-    public TypedValue getFixedHeightMinor() {
-        if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();
-        return mFixedHeightMinor;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (mAttachListener != null) {
-            mAttachListener.onAttachedFromWindow();
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mAttachListener != null) {
-            mAttachListener.onDetachedFromWindow();
-        }
-    }
-}
diff --git a/android/support/v7/widget/DecorContentParent.java b/android/support/v7/widget/DecorContentParent.java
deleted file mode 100644
index bc36f97..0000000
--- a/android/support/v7/widget/DecorContentParent.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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.drawable.Drawable;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuPresenter;
-import android.util.SparseArray;
-import android.view.Menu;
-import android.view.Window;
-
-/**
- * Implemented by the top-level decor layout for a window. DecorContentParent offers
- * entry points for a number of title/window decor features.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface DecorContentParent {
-    void setWindowCallback(Window.Callback cb);
-    void setWindowTitle(CharSequence title);
-    CharSequence getTitle();
-    void initFeature(int windowFeature);
-    void setUiOptions(int uiOptions);
-    boolean hasIcon();
-    boolean hasLogo();
-    void setIcon(int resId);
-    void setIcon(Drawable d);
-    void setLogo(int resId);
-    boolean canShowOverflowMenu();
-    boolean isOverflowMenuShowing();
-    boolean isOverflowMenuShowPending();
-    boolean showOverflowMenu();
-    boolean hideOverflowMenu();
-    void setMenuPrepared();
-    void setMenu(Menu menu, MenuPresenter.Callback cb);
-    void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
-    void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
-    void dismissPopups();
-
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/DecorToolbar.java b/android/support/v7/widget/DecorToolbar.java
deleted file mode 100644
index d9fe0c3..0000000
--- a/android/support/v7/widget/DecorToolbar.java
+++ /dev/null
@@ -1,105 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPresenter;
-import android.util.SparseArray;
-import android.view.Menu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.SpinnerAdapter;
-
-/**
- * Common interface for a toolbar that sits as part of the window decor.
- * Layouts that control window decor use this as a point of interaction with different
- * bar implementations.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface DecorToolbar {
-    ViewGroup getViewGroup();
-    Context getContext();
-    boolean hasExpandedActionView();
-    void collapseActionView();
-    void setWindowCallback(Window.Callback cb);
-    void setWindowTitle(CharSequence title);
-    CharSequence getTitle();
-    void setTitle(CharSequence title);
-    CharSequence getSubtitle();
-    void setSubtitle(CharSequence subtitle);
-    void initProgress();
-    void initIndeterminateProgress();
-    boolean hasIcon();
-    boolean hasLogo();
-    void setIcon(int resId);
-    void setIcon(Drawable d);
-    void setLogo(int resId);
-    void setLogo(Drawable d);
-    boolean canShowOverflowMenu();
-    boolean isOverflowMenuShowing();
-    boolean isOverflowMenuShowPending();
-    boolean showOverflowMenu();
-    boolean hideOverflowMenu();
-    void setMenuPrepared();
-    void setMenu(Menu menu, MenuPresenter.Callback cb);
-    void dismissPopupMenus();
-
-    int getDisplayOptions();
-    void setDisplayOptions(int opts);
-    void setEmbeddedTabView(ScrollingTabContainerView tabView);
-    boolean hasEmbeddedTabs();
-    boolean isTitleTruncated();
-    void setCollapsible(boolean collapsible);
-    void setHomeButtonEnabled(boolean enable);
-    int getNavigationMode();
-    void setNavigationMode(int mode);
-    void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener listener);
-    void setDropdownSelectedPosition(int position);
-    int getDropdownSelectedPosition();
-    int getDropdownItemCount();
-    void setCustomView(View view);
-    View getCustomView();
-    void animateToVisibility(int visibility);
-    ViewPropertyAnimatorCompat setupAnimatorToVisibility(int visibility, long duration);
-    void setNavigationIcon(Drawable icon);
-    void setNavigationIcon(int resId);
-    void setNavigationContentDescription(CharSequence description);
-    void setNavigationContentDescription(int resId);
-    void setDefaultNavigationContentDescription(int defaultNavigationContentDescription);
-    void setDefaultNavigationIcon(Drawable icon);
-    void saveHierarchyState(SparseArray<Parcelable> toolbarStates);
-    void restoreHierarchyState(SparseArray<Parcelable> toolbarStates);
-    void setBackgroundDrawable(Drawable d);
-    int getHeight();
-    void setVisibility(int visible);
-    int getVisibility();
-    void setMenuCallbacks(MenuPresenter.Callback presenterCallback,
-            MenuBuilder.Callback menuBuilderCallback);
-    Menu getMenu();
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/DefaultItemAnimator.java b/android/support/v7/widget/DefaultItemAnimator.java
deleted file mode 100644
index 11600a8..0000000
--- a/android/support/v7/widget/DefaultItemAnimator.java
+++ /dev/null
@@ -1,667 +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.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This implementation of {@link RecyclerView.ItemAnimator} provides basic
- * animations on remove, add, and move events that happen to the items in
- * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
- *
- * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
- */
-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<>();
-    private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<>();
-
-    ArrayList<ArrayList<ViewHolder>> mAdditionsList = new ArrayList<>();
-    ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<>();
-    ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<>();
-
-    ArrayList<ViewHolder> mAddAnimations = new ArrayList<>();
-    ArrayList<ViewHolder> mMoveAnimations = new ArrayList<>();
-    ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<>();
-    ArrayList<ViewHolder> mChangeAnimations = new ArrayList<>();
-
-    private static class MoveInfo {
-        public ViewHolder holder;
-        public int fromX, fromY, toX, toY;
-
-        MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
-            this.holder = holder;
-            this.fromX = fromX;
-            this.fromY = fromY;
-            this.toX = toX;
-            this.toY = toY;
-        }
-    }
-
-    private static class ChangeInfo {
-        public ViewHolder oldHolder, newHolder;
-        public int fromX, fromY, toX, toY;
-        private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
-            this.oldHolder = oldHolder;
-            this.newHolder = newHolder;
-        }
-
-        ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
-                int fromX, int fromY, int toX, int toY) {
-            this(oldHolder, newHolder);
-            this.fromX = fromX;
-            this.fromY = fromY;
-            this.toX = toX;
-            this.toY = toY;
-        }
-
-        @Override
-        public String toString() {
-            return "ChangeInfo{"
-                    + "oldHolder=" + oldHolder
-                    + ", newHolder=" + newHolder
-                    + ", fromX=" + fromX
-                    + ", fromY=" + fromY
-                    + ", toX=" + toX
-                    + ", toY=" + toY
-                    + '}';
-        }
-    }
-
-    @Override
-    public void runPendingAnimations() {
-        boolean removalsPending = !mPendingRemovals.isEmpty();
-        boolean movesPending = !mPendingMoves.isEmpty();
-        boolean changesPending = !mPendingChanges.isEmpty();
-        boolean additionsPending = !mPendingAdditions.isEmpty();
-        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
-            // nothing to animate
-            return;
-        }
-        // First, remove stuff
-        for (ViewHolder holder : mPendingRemovals) {
-            animateRemoveImpl(holder);
-        }
-        mPendingRemovals.clear();
-        // Next, move stuff
-        if (movesPending) {
-            final ArrayList<MoveInfo> moves = new ArrayList<>();
-            moves.addAll(mPendingMoves);
-            mMovesList.add(moves);
-            mPendingMoves.clear();
-            Runnable mover = new Runnable() {
-                @Override
-                public void run() {
-                    for (MoveInfo moveInfo : moves) {
-                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
-                                moveInfo.toX, moveInfo.toY);
-                    }
-                    moves.clear();
-                    mMovesList.remove(moves);
-                }
-            };
-            if (removalsPending) {
-                View view = moves.get(0).holder.itemView;
-                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
-            } else {
-                mover.run();
-            }
-        }
-        // Next, change stuff, to run in parallel with move animations
-        if (changesPending) {
-            final ArrayList<ChangeInfo> changes = new ArrayList<>();
-            changes.addAll(mPendingChanges);
-            mChangesList.add(changes);
-            mPendingChanges.clear();
-            Runnable changer = new Runnable() {
-                @Override
-                public void run() {
-                    for (ChangeInfo change : changes) {
-                        animateChangeImpl(change);
-                    }
-                    changes.clear();
-                    mChangesList.remove(changes);
-                }
-            };
-            if (removalsPending) {
-                ViewHolder holder = changes.get(0).oldHolder;
-                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
-            } else {
-                changer.run();
-            }
-        }
-        // Next, add stuff
-        if (additionsPending) {
-            final ArrayList<ViewHolder> additions = new ArrayList<>();
-            additions.addAll(mPendingAdditions);
-            mAdditionsList.add(additions);
-            mPendingAdditions.clear();
-            Runnable adder = new Runnable() {
-                @Override
-                public void run() {
-                    for (ViewHolder holder : additions) {
-                        animateAddImpl(holder);
-                    }
-                    additions.clear();
-                    mAdditionsList.remove(additions);
-                }
-            };
-            if (removalsPending || movesPending || changesPending) {
-                long removeDuration = removalsPending ? getRemoveDuration() : 0;
-                long moveDuration = movesPending ? getMoveDuration() : 0;
-                long changeDuration = changesPending ? getChangeDuration() : 0;
-                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
-                View view = additions.get(0).itemView;
-                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
-            } else {
-                adder.run();
-            }
-        }
-    }
-
-    @Override
-    public boolean animateRemove(final ViewHolder holder) {
-        resetAnimation(holder);
-        mPendingRemovals.add(holder);
-        return true;
-    }
-
-    private void animateRemoveImpl(final ViewHolder holder) {
-        final View view = holder.itemView;
-        final ViewPropertyAnimator animation = view.animate();
-        mRemoveAnimations.add(holder);
-        animation.setDuration(getRemoveDuration()).alpha(0).setListener(
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animator) {
-                        dispatchRemoveStarting(holder);
-                    }
-
-                    @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);
-        holder.itemView.setAlpha(0);
-        mPendingAdditions.add(holder);
-        return true;
-    }
-
-    void animateAddImpl(final ViewHolder holder) {
-        final View view = holder.itemView;
-        final ViewPropertyAnimator animation = view.animate();
-        mAddAnimations.add(holder);
-        animation.alpha(1).setDuration(getAddDuration())
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animator) {
-                        dispatchAddStarting(holder);
-                    }
-
-                    @Override
-                    public void onAnimationCancel(Animator animator) {
-                        view.setAlpha(1);
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animator) {
-                        animation.setListener(null);
-                        dispatchAddFinished(holder);
-                        mAddAnimations.remove(holder);
-                        dispatchFinishedWhenDone();
-                    }
-                }).start();
-    }
-
-    @Override
-    public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
-            int toX, int toY) {
-        final View view = holder.itemView;
-        fromX += (int) holder.itemView.getTranslationX();
-        fromY += (int) holder.itemView.getTranslationY();
-        resetAnimation(holder);
-        int deltaX = toX - fromX;
-        int deltaY = toY - fromY;
-        if (deltaX == 0 && deltaY == 0) {
-            dispatchMoveFinished(holder);
-            return false;
-        }
-        if (deltaX != 0) {
-            view.setTranslationX(-deltaX);
-        }
-        if (deltaY != 0) {
-            view.setTranslationY(-deltaY);
-        }
-        mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
-        return true;
-    }
-
-    void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
-        final View view = holder.itemView;
-        final int deltaX = toX - fromX;
-        final int deltaY = toY - fromY;
-        if (deltaX != 0) {
-            view.animate().translationX(0);
-        }
-        if (deltaY != 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 ViewPropertyAnimator animation = view.animate();
-        mMoveAnimations.add(holder);
-        animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animator) {
-                dispatchMoveStarting(holder);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animator) {
-                if (deltaX != 0) {
-                    view.setTranslationX(0);
-                }
-                if (deltaY != 0) {
-                    view.setTranslationY(0);
-                }
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animator) {
-                animation.setListener(null);
-                dispatchMoveFinished(holder);
-                mMoveAnimations.remove(holder);
-                dispatchFinishedWhenDone();
-            }
-        }).start();
-    }
-
-    @Override
-    public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
-            int fromX, int fromY, int toX, int toY) {
-        if (oldHolder == newHolder) {
-            // Don't know how to run change animations when the same view holder is re-used.
-            // run a move animation to handle position changes.
-            return animateMove(oldHolder, fromX, fromY, toX, toY);
-        }
-        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
-        oldHolder.itemView.setTranslationX(prevTranslationX);
-        oldHolder.itemView.setTranslationY(prevTranslationY);
-        oldHolder.itemView.setAlpha(prevAlpha);
-        if (newHolder != null) {
-            // carry over translation values
-            resetAnimation(newHolder);
-            newHolder.itemView.setTranslationX(-deltaX);
-            newHolder.itemView.setTranslationY(-deltaY);
-            newHolder.itemView.setAlpha(0);
-        }
-        mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
-        return true;
-    }
-
-    void animateChangeImpl(final ChangeInfo changeInfo) {
-        final ViewHolder holder = changeInfo.oldHolder;
-        final View view = holder == null ? null : holder.itemView;
-        final ViewHolder newHolder = changeInfo.newHolder;
-        final View newView = newHolder != null ? newHolder.itemView : null;
-        if (view != null) {
-            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 AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animator) {
-                    dispatchChangeStarting(changeInfo.oldHolder, true);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animator) {
-                    oldViewAnim.setListener(null);
-                    view.setAlpha(1);
-                    view.setTranslationX(0);
-                    view.setTranslationY(0);
-                    dispatchChangeFinished(changeInfo.oldHolder, true);
-                    mChangeAnimations.remove(changeInfo.oldHolder);
-                    dispatchFinishedWhenDone();
-                }
-            }).start();
-        }
-        if (newView != null) {
-            final ViewPropertyAnimator newViewAnimation = newView.animate();
-            mChangeAnimations.add(changeInfo.newHolder);
-            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();
-        }
-    }
-
-    private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
-        for (int i = infoList.size() - 1; i >= 0; i--) {
-            ChangeInfo changeInfo = infoList.get(i);
-            if (endChangeAnimationIfNecessary(changeInfo, item)) {
-                if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
-                    infoList.remove(changeInfo);
-                }
-            }
-        }
-    }
-
-    private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
-        if (changeInfo.oldHolder != null) {
-            endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
-        }
-        if (changeInfo.newHolder != null) {
-            endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
-        }
-    }
-    private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
-        boolean oldItem = false;
-        if (changeInfo.newHolder == item) {
-            changeInfo.newHolder = null;
-        } else if (changeInfo.oldHolder == item) {
-            changeInfo.oldHolder = null;
-            oldItem = true;
-        } else {
-            return false;
-        }
-        item.itemView.setAlpha(1);
-        item.itemView.setTranslationX(0);
-        item.itemView.setTranslationY(0);
-        dispatchChangeFinished(item, oldItem);
-        return true;
-    }
-
-    @Override
-    public void endAnimation(ViewHolder item) {
-        final View view = item.itemView;
-        // this will trigger end callback which should set properties to their target values.
-        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) {
-                view.setTranslationY(0);
-                view.setTranslationX(0);
-                dispatchMoveFinished(item);
-                mPendingMoves.remove(i);
-            }
-        }
-        endChangeAnimation(mPendingChanges, item);
-        if (mPendingRemovals.remove(item)) {
-            view.setAlpha(1);
-            dispatchRemoveFinished(item);
-        }
-        if (mPendingAdditions.remove(item)) {
-            view.setAlpha(1);
-            dispatchAddFinished(item);
-        }
-
-        for (int i = mChangesList.size() - 1; i >= 0; i--) {
-            ArrayList<ChangeInfo> changes = mChangesList.get(i);
-            endChangeAnimation(changes, item);
-            if (changes.isEmpty()) {
-                mChangesList.remove(i);
-            }
-        }
-        for (int i = mMovesList.size() - 1; i >= 0; i--) {
-            ArrayList<MoveInfo> moves = mMovesList.get(i);
-            for (int j = moves.size() - 1; j >= 0; j--) {
-                MoveInfo moveInfo = moves.get(j);
-                if (moveInfo.holder == item) {
-                    view.setTranslationY(0);
-                    view.setTranslationX(0);
-                    dispatchMoveFinished(item);
-                    moves.remove(j);
-                    if (moves.isEmpty()) {
-                        mMovesList.remove(i);
-                    }
-                    break;
-                }
-            }
-        }
-        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
-            ArrayList<ViewHolder> additions = mAdditionsList.get(i);
-            if (additions.remove(item)) {
-                view.setAlpha(1);
-                dispatchAddFinished(item);
-                if (additions.isEmpty()) {
-                    mAdditionsList.remove(i);
-                }
-            }
-        }
-
-        // animations should be ended by the cancel above.
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (mRemoveAnimations.remove(item) && DEBUG) {
-            throw new IllegalStateException("after animation is cancelled, item should not be in "
-                    + "mRemoveAnimations list");
-        }
-
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (mAddAnimations.remove(item) && DEBUG) {
-            throw new IllegalStateException("after animation is cancelled, item should not be in "
-                    + "mAddAnimations list");
-        }
-
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (mChangeAnimations.remove(item) && DEBUG) {
-            throw new IllegalStateException("after animation is cancelled, item should not be in "
-                    + "mChangeAnimations list");
-        }
-
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        if (mMoveAnimations.remove(item) && DEBUG) {
-            throw new IllegalStateException("after animation is cancelled, item should not be in "
-                    + "mMoveAnimations list");
-        }
-        dispatchFinishedWhenDone();
-    }
-
-    private void resetAnimation(ViewHolder holder) {
-        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());
-    }
-
-    /**
-     * Check the state of currently pending and running animations. If there are none
-     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
-     * listeners.
-     */
-    void dispatchFinishedWhenDone() {
-        if (!isRunning()) {
-            dispatchAnimationsFinished();
-        }
-    }
-
-    @Override
-    public void endAnimations() {
-        int count = mPendingMoves.size();
-        for (int i = count - 1; i >= 0; i--) {
-            MoveInfo item = mPendingMoves.get(i);
-            View view = item.holder.itemView;
-            view.setTranslationY(0);
-            view.setTranslationX(0);
-            dispatchMoveFinished(item.holder);
-            mPendingMoves.remove(i);
-        }
-        count = mPendingRemovals.size();
-        for (int i = count - 1; i >= 0; i--) {
-            ViewHolder item = mPendingRemovals.get(i);
-            dispatchRemoveFinished(item);
-            mPendingRemovals.remove(i);
-        }
-        count = mPendingAdditions.size();
-        for (int i = count - 1; i >= 0; i--) {
-            ViewHolder item = mPendingAdditions.get(i);
-            item.itemView.setAlpha(1);
-            dispatchAddFinished(item);
-            mPendingAdditions.remove(i);
-        }
-        count = mPendingChanges.size();
-        for (int i = count - 1; i >= 0; i--) {
-            endChangeAnimationIfNecessary(mPendingChanges.get(i));
-        }
-        mPendingChanges.clear();
-        if (!isRunning()) {
-            return;
-        }
-
-        int listCount = mMovesList.size();
-        for (int i = listCount - 1; i >= 0; i--) {
-            ArrayList<MoveInfo> moves = mMovesList.get(i);
-            count = moves.size();
-            for (int j = count - 1; j >= 0; j--) {
-                MoveInfo moveInfo = moves.get(j);
-                ViewHolder item = moveInfo.holder;
-                View view = item.itemView;
-                view.setTranslationY(0);
-                view.setTranslationX(0);
-                dispatchMoveFinished(moveInfo.holder);
-                moves.remove(j);
-                if (moves.isEmpty()) {
-                    mMovesList.remove(moves);
-                }
-            }
-        }
-        listCount = mAdditionsList.size();
-        for (int i = listCount - 1; i >= 0; i--) {
-            ArrayList<ViewHolder> additions = mAdditionsList.get(i);
-            count = additions.size();
-            for (int j = count - 1; j >= 0; j--) {
-                ViewHolder item = additions.get(j);
-                View view = item.itemView;
-                view.setAlpha(1);
-                dispatchAddFinished(item);
-                additions.remove(j);
-                if (additions.isEmpty()) {
-                    mAdditionsList.remove(additions);
-                }
-            }
-        }
-        listCount = mChangesList.size();
-        for (int i = listCount - 1; i >= 0; i--) {
-            ArrayList<ChangeInfo> changes = mChangesList.get(i);
-            count = changes.size();
-            for (int j = count - 1; j >= 0; j--) {
-                endChangeAnimationIfNecessary(changes.get(j));
-                if (changes.isEmpty()) {
-                    mChangesList.remove(changes);
-                }
-            }
-        }
-
-        cancelAll(mRemoveAnimations);
-        cancelAll(mMoveAnimations);
-        cancelAll(mAddAnimations);
-        cancelAll(mChangeAnimations);
-
-        dispatchAnimationsFinished();
-    }
-
-    void cancelAll(List<ViewHolder> viewHolders) {
-        for (int i = viewHolders.size() - 1; i >= 0; i--) {
-            viewHolders.get(i).itemView.animate().cancel();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * If the payload list is not empty, DefaultItemAnimator returns <code>true</code>.
-     * When this is the case:
-     * <ul>
-     * <li>If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both
-     * ViewHolder arguments will be the same instance.
-     * </li>
-     * <li>
-     * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)},
-     * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and
-     * run a move animation instead.
-     * </li>
-     * </ul>
-     */
-    @Override
-    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
-            @NonNull List<Object> payloads) {
-        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
-    }
-}
diff --git a/android/support/v7/widget/DialogTitle.java b/android/support/v7/widget/DialogTitle.java
deleted file mode 100644
index 313d748..0000000
--- a/android/support/v7/widget/DialogTitle.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2015 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.content.Context;
-import android.content.res.TypedArray;
-import android.support.annotation.RestrictTo;
-import android.support.v7.appcompat.R;
-import android.text.Layout;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-/**
- * Used by dialogs to change the font size and number of lines to try to fit
- * the text to the available space.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class DialogTitle extends TextView {
-
-    public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public DialogTitle(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public DialogTitle(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        final Layout layout = getLayout();
-        if (layout != null) {
-            final int lineCount = layout.getLineCount();
-            if (lineCount > 0) {
-                final int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
-                if (ellipsisCount > 0) {
-                    setSingleLine(false);
-                    setMaxLines(2);
-
-                    final TypedArray a = getContext().obtainStyledAttributes(null,
-                            R.styleable.TextAppearance,
-                            android.R.attr.textAppearanceMedium,
-                            android.R.style.TextAppearance_Medium);
-                    final int textSize = a.getDimensionPixelSize(
-                            R.styleable.TextAppearance_android_textSize, 0);
-                    if (textSize != 0) {
-                        // textSize is already expressed in pixels
-                        setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-                    }
-                    a.recycle();
-
-                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/DividerItemDecoration.java b/android/support/v7/widget/DividerItemDecoration.java
deleted file mode 100644
index 80524eb..0000000
--- a/android/support/v7/widget/DividerItemDecoration.java
+++ /dev/null
@@ -1,180 +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.widget;
-
-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.util.Log;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
- * between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
- * {@link #VERTICAL} orientations.
- *
- * <pre>
- *     mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
- *             mLayoutManager.getOrientation());
- *     recyclerView.addItemDecoration(mDividerItemDecoration);
- * </pre>
- */
-public class DividerItemDecoration extends RecyclerView.ItemDecoration {
-    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;
-
-    /**
-     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
-     */
-    private int mOrientation;
-
-    private final Rect mBounds = new Rect();
-
-    /**
-     * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
-     * {@link LinearLayoutManager}.
-     *
-     * @param context Current context, it will be used to access resources.
-     * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
-     */
-    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);
-    }
-
-    /**
-     * Sets the orientation for this divider. This should be called if
-     * {@link RecyclerView.LayoutManager} changes orientation.
-     *
-     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
-     */
-    public void setOrientation(int orientation) {
-        if (orientation != HORIZONTAL && orientation != VERTICAL) {
-            throw new IllegalArgumentException(
-                    "Invalid orientation. It should be either HORIZONTAL or VERTICAL");
-        }
-        mOrientation = orientation;
-    }
-
-    /**
-     * Sets the {@link Drawable} for this divider.
-     *
-     * @param drawable Drawable that should be used as a divider.
-     */
-    public void setDrawable(@NonNull Drawable drawable) {
-        if (drawable == null) {
-            throw new IllegalArgumentException("Drawable cannot be null.");
-        }
-        mDivider = drawable;
-    }
-
-    @Override
-    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        if (parent.getLayoutManager() == null || mDivider == null) {
-            return;
-        }
-        if (mOrientation == VERTICAL) {
-            drawVertical(c, parent);
-        } else {
-            drawHorizontal(c, parent);
-        }
-    }
-
-    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();
-            canvas.clipRect(left, parent.getPaddingTop(), right,
-                    parent.getHeight() - parent.getPaddingBottom());
-        } else {
-            left = 0;
-            right = parent.getWidth();
-        }
-
-        final int childCount = parent.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = parent.getChildAt(i);
-            parent.getDecoratedBoundsWithMargins(child, mBounds);
-            final int bottom = mBounds.bottom + Math.round(child.getTranslationY());
-            final int top = bottom - mDivider.getIntrinsicHeight();
-            mDivider.setBounds(left, top, right, bottom);
-            mDivider.draw(canvas);
-        }
-        canvas.restore();
-    }
-
-    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();
-            canvas.clipRect(parent.getPaddingLeft(), top,
-                    parent.getWidth() - parent.getPaddingRight(), bottom);
-        } else {
-            top = 0;
-            bottom = parent.getHeight();
-        }
-
-        final int childCount = parent.getChildCount();
-        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(child.getTranslationX());
-            final int left = right - mDivider.getIntrinsicWidth();
-            mDivider.setBounds(left, top, right, bottom);
-            mDivider.draw(canvas);
-        }
-        canvas.restore();
-    }
-
-    @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 {
-            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
-        }
-    }
-}
diff --git a/android/support/v7/widget/DrawableUtils.java b/android/support/v7/widget/DrawableUtils.java
deleted file mode 100644
index 9216726..0000000
--- a/android/support/v7/widget/DrawableUtils.java
+++ /dev/null
@@ -1,197 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-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.LayerDrawable;
-import android.graphics.drawable.ScaleDrawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.graphics.drawable.WrappedDrawable;
-import android.util.Log;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-/** @hide */
-@RestrictTo(LIBRARY_GROUP)
-public class DrawableUtils {
-
-    private static final String TAG = "DrawableUtils";
-
-    public static final Rect INSETS_NONE = new Rect();
-    private static Class<?> sInsetsClazz;
-
-    private static final String VECTOR_DRAWABLE_CLAZZ_NAME
-            = "android.graphics.drawable.VectorDrawable";
-
-    static {
-        if (Build.VERSION.SDK_INT >= 18) {
-            try {
-                sInsetsClazz = Class.forName("android.graphics.Insets");
-            } catch (ClassNotFoundException e) {
-                // Oh well...
-            }
-        }
-    }
-
-    private DrawableUtils() {}
-
-    /**
-     * Allows us to get the optical insets for a {@link Drawable}. Since this is hidden we need to
-     * use reflection. Since the {@code Insets} class is hidden also, we return a Rect instead.
-     */
-    public static Rect getOpticalBounds(Drawable drawable) {
-        if (sInsetsClazz != null) {
-            try {
-                // If the Drawable is wrapped, we need to manually unwrap it and process
-                // the wrapped drawable.
-                drawable = DrawableCompat.unwrap(drawable);
-
-                final Method getOpticalInsetsMethod = drawable.getClass()
-                        .getMethod("getOpticalInsets");
-                final Object insets = getOpticalInsetsMethod.invoke(drawable);
-
-                if (insets != null) {
-                    // If the drawable has some optical insets, let's copy them into a Rect
-                    final Rect result = new Rect();
-
-                    for (Field field : sInsetsClazz.getFields()) {
-                        switch (field.getName()) {
-                            case "left":
-                               result.left = field.getInt(insets);
-                                break;
-                            case "top":
-                                result.top = field.getInt(insets);
-                                break;
-                            case "right":
-                                result.right = field.getInt(insets);
-                                break;
-                            case "bottom":
-                                result.bottom = field.getInt(insets);
-                                break;
-                        }
-                    }
-                    return result;
-                }
-            } catch (Exception e) {
-                // Eugh, we hit some kind of reflection issue...
-                Log.e(TAG, "Couldn't obtain the optical insets. Ignoring.");
-            }
-        }
-
-        // If we reach here, either we're running on a device pre-v18, the Drawable didn't have
-        // any optical insets, or a reflection issue, so we'll just return an empty rect
-        return INSETS_NONE;
-    }
-
-    /**
-     * Attempt the fix any issues in the given drawable, usually caused by platform bugs in the
-     * implementation. This method should be call after retrieval from
-     * {@link android.content.res.Resources} or a {@link android.content.res.TypedArray}.
-     */
-    static void fixDrawable(@NonNull final Drawable drawable) {
-        if (Build.VERSION.SDK_INT == 21
-                && VECTOR_DRAWABLE_CLAZZ_NAME.equals(drawable.getClass().getName())) {
-            fixVectorDrawableTinting(drawable);
-        }
-    }
-
-    /**
-     * Some drawable implementations have problems with mutation. This method returns false if
-     * there is a known issue in the given drawable's implementation.
-     */
-    public static boolean canSafelyMutateDrawable(@NonNull Drawable drawable) {
-        if (Build.VERSION.SDK_INT < 15 && drawable instanceof InsetDrawable) {
-            return false;
-        }  else if (Build.VERSION.SDK_INT < 15 && drawable instanceof GradientDrawable) {
-            // GradientDrawable has a bug pre-ICS which results in mutate() resulting
-            // in loss of color
-            return false;
-        } else if (Build.VERSION.SDK_INT < 17 && drawable instanceof LayerDrawable) {
-            return false;
-        }
-
-        if (drawable instanceof DrawableContainer) {
-            // If we have a DrawableContainer, let's traverse its child array
-            final Drawable.ConstantState state = drawable.getConstantState();
-            if (state instanceof DrawableContainer.DrawableContainerState) {
-                final DrawableContainer.DrawableContainerState containerState =
-                        (DrawableContainer.DrawableContainerState) state;
-                for (final Drawable child : containerState.getChildren()) {
-                    if (!canSafelyMutateDrawable(child)) {
-                        return false;
-                    }
-                }
-            }
-        } else if (drawable instanceof WrappedDrawable) {
-            return canSafelyMutateDrawable(
-                    ((WrappedDrawable) drawable)
-                            .getWrappedDrawable());
-        } else if (drawable instanceof android.support.v7.graphics.drawable.DrawableWrapper) {
-            return canSafelyMutateDrawable(
-                    ((android.support.v7.graphics.drawable.DrawableWrapper) drawable)
-                            .getWrappedDrawable());
-        } else if (drawable instanceof ScaleDrawable) {
-            return canSafelyMutateDrawable(((ScaleDrawable) drawable).getDrawable());
-        }
-
-        return true;
-    }
-
-    /**
-     * VectorDrawable has an issue on API 21 where it sometimes doesn't create its tint filter.
-     * Fixed by toggling its state to force a filter creation.
-     */
-    private static void fixVectorDrawableTinting(final Drawable drawable) {
-        final int[] originalState = drawable.getState();
-        if (originalState == null || originalState.length == 0) {
-            // The drawable doesn't have a state, so set it to be checked
-            drawable.setState(ThemeUtils.CHECKED_STATE_SET);
-        } else {
-            // Else the drawable does have a state, so clear it
-            drawable.setState(ThemeUtils.EMPTY_STATE_SET);
-        }
-        // Now set the original state
-        drawable.setState(originalState);
-    }
-
-    /**
-     * 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;
-            case 9: return PorterDuff.Mode.SRC_ATOP;
-            case 14: return PorterDuff.Mode.MULTIPLY;
-            case 15: return PorterDuff.Mode.SCREEN;
-            case 16: return PorterDuff.Mode.ADD;
-            default: return defaultMode;
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/DropDownListView.java b/android/support/v7/widget/DropDownListView.java
deleted file mode 100644
index cccb82b..0000000
--- a/android/support/v7/widget/DropDownListView.java
+++ /dev/null
@@ -1,631 +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.widget;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.widget.ListViewAutoScrollHelper;
-import android.support.v7.appcompat.R;
-import android.support.v7.graphics.drawable.DrawableWrapper;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-
-import java.lang.reflect.Field;
-
-/**
- * <p>Wrapper class for a ListView. This wrapper can hijack the focus to
- * make sure the list uses the appropriate drawables and states when
- * displayed on screen within a drop down. The focus is never actually
- * passed to the drop down in this mode; the list only looks focused.</p>
- */
-class DropDownListView extends ListView {
-    public static final int INVALID_POSITION = -1;
-    public static final int NO_POSITION = -1;
-
-    private final Rect mSelectorRect = new Rect();
-    private int mSelectionLeftPadding = 0;
-    private int mSelectionTopPadding = 0;
-    private int mSelectionRightPadding = 0;
-    private int mSelectionBottomPadding = 0;
-
-    private int mMotionPosition;
-
-    private Field mIsChildViewEnabled;
-
-    private GateKeeperDrawable mSelector;
-
-    /*
-    * WARNING: This is a workaround for a touch mode issue.
-    *
-    * Touch mode is propagated lazily to windows. This causes problems in
-    * the following scenario:
-    * - Type something in the AutoCompleteTextView and get some results
-    * - Move down with the d-pad to select an item in the list
-    * - Move up with the d-pad until the selection disappears
-    * - Type more text in the AutoCompleteTextView *using the soft keyboard*
-    *   and get new results; you are now in touch mode
-    * - The selection comes back on the first item in the list, even though
-    *   the list is supposed to be in touch mode
-    *
-    * Using the soft keyboard triggers the touch mode change but that change
-    * is propagated to our window only after the first list layout, therefore
-    * after the list attempts to resurrect the selection.
-    *
-    * The trick to work around this issue is to pretend the list is in touch
-    * mode when we know that the selection should not appear, that is when
-    * we know the user moved the selection away from the list.
-    *
-    * This boolean is set to true whenever we explicitly hide the list's
-    * selection and reset to false whenever we know the user moved the
-    * selection back to the list.
-    *
-    * When this boolean is true, isInTouchMode() returns true, otherwise it
-    * returns super.isInTouchMode().
-    */
-    private boolean mListSelectionHidden;
-
-    /**
-     * True if this wrapper should fake focus.
-     */
-    private boolean mHijackFocus;
-
-    /** Whether to force drawing of the pressed state selector. */
-    private boolean mDrawsInPressedState;
-
-    /** Current drag-to-open click animation, if any. */
-    private ViewPropertyAnimatorCompat mClickAnimation;
-
-    /** Helper for drag-to-open auto scrolling. */
-    private ListViewAutoScrollHelper mScrollHelper;
-
-    /**
-     * <p>Creates a new list view wrapper.</p>
-     *
-     * @param context this view's context
-     */
-    DropDownListView(Context context, boolean hijackFocus) {
-        super(context, null, R.attr.dropDownListViewStyle);
-        mHijackFocus = hijackFocus;
-        setCacheColorHint(0); // Transparent, since the background drawable could be anything.
-
-        try {
-            mIsChildViewEnabled = AbsListView.class.getDeclaredField("mIsChildViewEnabled");
-            mIsChildViewEnabled.setAccessible(true);
-        } catch (NoSuchFieldException e) {
-            e.printStackTrace();
-        }
-    }
-
-
-    @Override
-    public boolean isInTouchMode() {
-        // WARNING: Please read the comment where mListSelectionHidden is declared
-        return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
-    }
-
-    /**
-     * <p>Returns the focus state in the drop down.</p>
-     *
-     * @return true always if hijacking focus
-     */
-    @Override
-    public boolean hasWindowFocus() {
-        return mHijackFocus || super.hasWindowFocus();
-    }
-
-    /**
-     * <p>Returns the focus state in the drop down.</p>
-     *
-     * @return true always if hijacking focus
-     */
-    @Override
-    public boolean isFocused() {
-        return mHijackFocus || super.isFocused();
-    }
-
-    /**
-     * <p>Returns the focus state in the drop down.</p>
-     *
-     * @return true always if hijacking focus
-     */
-    @Override
-    public boolean hasFocus() {
-        return mHijackFocus || super.hasFocus();
-    }
-
-    @Override
-    public void setSelector(Drawable sel) {
-        mSelector = sel != null ? new GateKeeperDrawable(sel) : null;
-        super.setSelector(mSelector);
-
-        final Rect padding = new Rect();
-        if (sel != null) {
-            sel.getPadding(padding);
-        }
-
-        mSelectionLeftPadding = padding.left;
-        mSelectionTopPadding = padding.top;
-        mSelectionRightPadding = padding.right;
-        mSelectionBottomPadding = padding.bottom;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        setSelectorEnabled(true);
-        updateSelectorStateCompat();
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        final boolean drawSelectorOnTop = false;
-        if (!drawSelectorOnTop) {
-            drawSelectorCompat(canvas);
-        }
-
-        super.dispatchDraw(canvas);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                mMotionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
-                break;
-        }
-        return super.onTouchEvent(ev);
-    }
-
-    /**
-     * Find a position that can be selected (i.e., is not a separator).
-     *
-     * @param position The starting position to look at.
-     * @param lookDown Whether to look down for other positions.
-     * @return The next selectable position starting at position and then searching either up or
-     *         down. Returns {@link #INVALID_POSITION} if nothing can be found.
-     */
-    public int lookForSelectablePosition(int position, boolean lookDown) {
-        final ListAdapter adapter = getAdapter();
-        if (adapter == null || isInTouchMode()) {
-            return INVALID_POSITION;
-        }
-
-        final int count = adapter.getCount();
-        if (!getAdapter().areAllItemsEnabled()) {
-            if (lookDown) {
-                position = Math.max(0, position);
-                while (position < count && !adapter.isEnabled(position)) {
-                    position++;
-                }
-            } else {
-                position = Math.min(position, count - 1);
-                while (position >= 0 && !adapter.isEnabled(position)) {
-                    position--;
-                }
-            }
-
-            if (position < 0 || position >= count) {
-                return INVALID_POSITION;
-            }
-            return position;
-        } else {
-            if (position < 0 || position >= count) {
-                return INVALID_POSITION;
-            }
-            return position;
-        }
-    }
-
-    /**
-     * Measures the height of the given range of children (inclusive) and returns the height
-     * with this ListView's padding and divider heights included. If maxHeight is provided, the
-     * measuring will stop when the current height reaches maxHeight.
-     *
-     * @param widthMeasureSpec             The width measure spec to be given to a child's
-     *                                     {@link View#measure(int, int)}.
-     * @param startPosition                The position of the first child to be shown.
-     * @param endPosition                  The (inclusive) position of the last child to be
-     *                                     shown. Specify {@link #NO_POSITION} if the last child
-     *                                     should be the last available child from the adapter.
-     * @param maxHeight                    The maximum height that will be returned (if all the
-     *                                     children don't fit in this value, this value will be
-     *                                     returned).
-     * @param disallowPartialChildPosition In general, whether the returned height should only
-     *                                     contain entire children. This is more powerful--it is
-     *                                     the first inclusive position at which partial
-     *                                     children will not be allowed. Example: it looks nice
-     *                                     to have at least 3 completely visible children, and
-     *                                     in portrait this will most likely fit; but in
-     *                                     landscape there could be times when even 2 children
-     *                                     can not be completely shown, so a value of 2
-     *                                     (remember, inclusive) would be good (assuming
-     *                                     startPosition is 0).
-     * @return The height of this ListView with the given children.
-     */
-    public int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition,
-            int endPosition, final int maxHeight,
-            int disallowPartialChildPosition) {
-
-        final int paddingTop = getListPaddingTop();
-        final int paddingBottom = getListPaddingBottom();
-        final int paddingLeft = getListPaddingLeft();
-        final int paddingRight = getListPaddingRight();
-        final int reportedDividerHeight = getDividerHeight();
-        final Drawable divider = getDivider();
-
-        final ListAdapter adapter = getAdapter();
-
-        if (adapter == null) {
-            return paddingTop + paddingBottom;
-        }
-
-        // Include the padding of the list
-        int returnedHeight = paddingTop + paddingBottom;
-        final int dividerHeight = ((reportedDividerHeight > 0) && divider != null)
-                ? reportedDividerHeight : 0;
-
-        // The previous height value that was less than maxHeight and contained
-        // no partial children
-        int prevHeightWithoutPartialChild = 0;
-
-        View child = null;
-        int viewType = 0;
-        int count = adapter.getCount();
-        for (int i = 0; i < count; i++) {
-            int newType = adapter.getItemViewType(i);
-            if (newType != viewType) {
-                child = null;
-                viewType = newType;
-            }
-            child = adapter.getView(i, child, this);
-
-            // Compute child height spec
-            int heightMeasureSpec;
-            ViewGroup.LayoutParams childLp = child.getLayoutParams();
-
-            if (childLp == null) {
-                childLp = generateDefaultLayoutParams();
-                child.setLayoutParams(childLp);
-            }
-
-            if (childLp.height > 0) {
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(childLp.height,
-                        MeasureSpec.EXACTLY);
-            } else {
-                heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-            }
-            child.measure(widthMeasureSpec, heightMeasureSpec);
-
-            // Since this view was measured directly against the parent measure
-            // spec, we must measure it again before reuse.
-            child.forceLayout();
-
-            if (i > 0) {
-                // Count the divider for all but one child
-                returnedHeight += dividerHeight;
-            }
-
-            returnedHeight += child.getMeasuredHeight();
-
-            if (returnedHeight >= maxHeight) {
-                // We went over, figure out which height to return.  If returnedHeight >
-                // maxHeight, then the i'th position did not fit completely.
-                return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
-                        && (i > disallowPartialChildPosition) // We've past the min pos
-                        && (prevHeightWithoutPartialChild > 0) // We have a prev height
-                        && (returnedHeight != maxHeight) // i'th child did not fit completely
-                        ? prevHeightWithoutPartialChild
-                        : maxHeight;
-            }
-
-            if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
-                prevHeightWithoutPartialChild = returnedHeight;
-            }
-        }
-
-        // At this point, we went through the range of children, and they each
-        // completely fit, so return the returnedHeight
-        return returnedHeight;
-    }
-
-    private void setSelectorEnabled(boolean enabled) {
-        if (mSelector != null) {
-            mSelector.setEnabled(enabled);
-        }
-    }
-
-    private static class GateKeeperDrawable extends DrawableWrapper {
-        private boolean mEnabled;
-
-        GateKeeperDrawable(Drawable drawable) {
-            super(drawable);
-            mEnabled = true;
-        }
-
-        void setEnabled(boolean enabled) {
-            mEnabled = enabled;
-        }
-
-        @Override
-        public boolean setState(int[] stateSet) {
-            if (mEnabled) {
-                return super.setState(stateSet);
-            }
-            return false;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            if (mEnabled) {
-                super.draw(canvas);
-            }
-        }
-
-        @Override
-        public void setHotspot(float x, float y) {
-            if (mEnabled) {
-                super.setHotspot(x, y);
-            }
-        }
-
-        @Override
-        public void setHotspotBounds(int left, int top, int right, int bottom) {
-            if (mEnabled) {
-                super.setHotspotBounds(left, top, right, bottom);
-            }
-        }
-
-        @Override
-        public boolean setVisible(boolean visible, boolean restart) {
-            if (mEnabled) {
-                return super.setVisible(visible, restart);
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Handles forwarded events.
-     *
-     * @param activePointerId id of the pointer that activated forwarding
-     * @return whether the event was handled
-     */
-    public boolean onForwardedEvent(MotionEvent event, int activePointerId) {
-        boolean handledEvent = true;
-        boolean clearPressedItem = false;
-
-        final int actionMasked = event.getActionMasked();
-        switch (actionMasked) {
-            case MotionEvent.ACTION_CANCEL:
-                handledEvent = false;
-                break;
-            case MotionEvent.ACTION_UP:
-                handledEvent = false;
-                // $FALL-THROUGH$
-            case MotionEvent.ACTION_MOVE:
-                final int activeIndex = event.findPointerIndex(activePointerId);
-                if (activeIndex < 0) {
-                    handledEvent = false;
-                    break;
-                }
-
-                final int x = (int) event.getX(activeIndex);
-                final int y = (int) event.getY(activeIndex);
-                final int position = pointToPosition(x, y);
-                if (position == INVALID_POSITION) {
-                    clearPressedItem = true;
-                    break;
-                }
-
-                final View child = getChildAt(position - getFirstVisiblePosition());
-                setPressedItem(child, position, x, y);
-                handledEvent = true;
-
-                if (actionMasked == MotionEvent.ACTION_UP) {
-                    clickPressedItem(child, position);
-                }
-                break;
-        }
-
-        // Failure to handle the event cancels forwarding.
-        if (!handledEvent || clearPressedItem) {
-            clearPressedItem();
-        }
-
-        // Manage automatic scrolling.
-        if (handledEvent) {
-            if (mScrollHelper == null) {
-                mScrollHelper = new ListViewAutoScrollHelper(this);
-            }
-            mScrollHelper.setEnabled(true);
-            mScrollHelper.onTouch(this, event);
-        } else if (mScrollHelper != null) {
-            mScrollHelper.setEnabled(false);
-        }
-
-        return handledEvent;
-    }
-
-    /**
-     * Starts an alpha animation on the selector. When the animation ends,
-     * the list performs a click on the item.
-     */
-    private void clickPressedItem(final View child, final int position) {
-        final long id = getItemIdAtPosition(position);
-        performItemClick(child, position, id);
-    }
-
-    /**
-     * Sets whether the list selection is hidden, as part of a workaround for a
-     * touch mode issue (see the declaration for mListSelectionHidden).
-     *
-     * @param hideListSelection {@code true} to hide list selection,
-     *                          {@code false} to show
-     */
-    void setListSelectionHidden(boolean hideListSelection) {
-        mListSelectionHidden = hideListSelection;
-    }
-
-    private void updateSelectorStateCompat() {
-        Drawable selector = getSelector();
-        if (selector != null && touchModeDrawsInPressedStateCompat() && isPressed()) {
-            selector.setState(getDrawableState());
-        }
-    }
-
-    private void drawSelectorCompat(Canvas canvas) {
-        if (!mSelectorRect.isEmpty()) {
-            final Drawable selector = getSelector();
-            if (selector != null) {
-                selector.setBounds(mSelectorRect);
-                selector.draw(canvas);
-            }
-        }
-    }
-
-    private void positionSelectorLikeTouchCompat(int position, View sel, float x, float y) {
-        positionSelectorLikeFocusCompat(position, sel);
-
-        Drawable selector = getSelector();
-        if (selector != null && position != INVALID_POSITION) {
-            DrawableCompat.setHotspot(selector, x, y);
-        }
-    }
-
-    private void positionSelectorLikeFocusCompat(int position, View sel) {
-        // If we're changing position, update the visibility since the selector
-        // is technically being detached from the previous selection.
-        final Drawable selector = getSelector();
-        final boolean manageState = selector != null && position != INVALID_POSITION;
-        if (manageState) {
-            selector.setVisible(false, false);
-        }
-
-        positionSelectorCompat(position, sel);
-
-        if (manageState) {
-            final Rect bounds = mSelectorRect;
-            final float x = bounds.exactCenterX();
-            final float y = bounds.exactCenterY();
-            selector.setVisible(getVisibility() == VISIBLE, false);
-            DrawableCompat.setHotspot(selector, x, y);
-        }
-    }
-
-    private void positionSelectorCompat(int position, View sel) {
-        final Rect selectorRect = mSelectorRect;
-        selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
-
-        // Adjust for selection padding.
-        selectorRect.left -= mSelectionLeftPadding;
-        selectorRect.top -= mSelectionTopPadding;
-        selectorRect.right += mSelectionRightPadding;
-        selectorRect.bottom += mSelectionBottomPadding;
-
-        try {
-            // AbsListView.mIsChildViewEnabled controls the selector's state so we need to
-            // modify its value
-            final boolean isChildViewEnabled = mIsChildViewEnabled.getBoolean(this);
-            if (sel.isEnabled() != isChildViewEnabled) {
-                mIsChildViewEnabled.set(this, !isChildViewEnabled);
-                if (position != INVALID_POSITION) {
-                    refreshDrawableState();
-                }
-            }
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        }
-    }
-
-    private void clearPressedItem() {
-        mDrawsInPressedState = false;
-        setPressed(false);
-        // This will call through to updateSelectorState()
-        drawableStateChanged();
-
-        final View motionView = getChildAt(mMotionPosition - getFirstVisiblePosition());
-        if (motionView != null) {
-            motionView.setPressed(false);
-        }
-
-        if (mClickAnimation != null) {
-            mClickAnimation.cancel();
-            mClickAnimation = null;
-        }
-    }
-
-    private void setPressedItem(View child, int position, float x, float y) {
-        mDrawsInPressedState = true;
-
-        // Ordering is essential. First, update the container's pressed state.
-        if (Build.VERSION.SDK_INT >= 21) {
-            drawableHotspotChanged(x, y);
-        }
-        if (!isPressed()) {
-            setPressed(true);
-        }
-
-        // Next, run layout to stabilize child positions.
-        layoutChildren();
-
-        // Manage the pressed view based on motion position. This allows us to
-        // play nicely with actual touch and scroll events.
-        if (mMotionPosition != INVALID_POSITION) {
-            final View motionView = getChildAt(mMotionPosition - getFirstVisiblePosition());
-            if (motionView != null && motionView != child && motionView.isPressed()) {
-                motionView.setPressed(false);
-            }
-        }
-        mMotionPosition = position;
-
-        // Offset for child coordinates.
-        final float childX = x - child.getLeft();
-        final float childY = y - child.getTop();
-        if (Build.VERSION.SDK_INT >= 21) {
-            child.drawableHotspotChanged(childX, childY);
-        }
-        if (!child.isPressed()) {
-            child.setPressed(true);
-        }
-
-        // Ensure that keyboard focus starts from the last touched position.
-        positionSelectorLikeTouchCompat(position, child, x, y);
-
-        // This needs some explanation. We need to disable the selector for this next call
-        // due to the way that ListViewCompat works. Otherwise both ListView and ListViewCompat
-        // will draw the selector and bad things happen.
-        setSelectorEnabled(false);
-
-        // Refresh the drawable state to reflect the new pressed state,
-        // which will also update the selector state.
-        refreshDrawableState();
-    }
-
-    private boolean touchModeDrawsInPressedStateCompat() {
-        return mDrawsInPressedState;
-    }
-}
diff --git a/android/support/v7/widget/FastScroller.java b/android/support/v7/widget/FastScroller.java
deleted file mode 100644
index fbe234b..0000000
--- a/android/support/v7/widget/FastScroller.java
+++ /dev/null
@@ -1,586 +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.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/android/support/v7/widget/FitWindowsFrameLayout.java b/android/support/v7/widget/FitWindowsFrameLayout.java
deleted file mode 100644
index d9ae921..0000000
--- a/android/support/v7/widget/FitWindowsFrameLayout.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.v7.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.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class FitWindowsFrameLayout extends FrameLayout implements FitWindowsViewGroup {
-
-    private OnFitSystemWindowsListener mListener;
-
-    public FitWindowsFrameLayout(Context context) {
-        super(context);
-    }
-
-    public FitWindowsFrameLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        if (mListener != null) {
-            mListener.onFitSystemWindows(insets);
-        }
-        return super.fitSystemWindows(insets);
-    }
-}
diff --git a/android/support/v7/widget/FitWindowsLinearLayout.java b/android/support/v7/widget/FitWindowsLinearLayout.java
deleted file mode 100644
index 163a211..0000000
--- a/android/support/v7/widget/FitWindowsLinearLayout.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.v7.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.util.AttributeSet;
-import android.widget.LinearLayout;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class FitWindowsLinearLayout extends LinearLayout implements FitWindowsViewGroup {
-
-    private OnFitSystemWindowsListener mListener;
-
-    public FitWindowsLinearLayout(Context context) {
-        super(context);
-    }
-
-    public FitWindowsLinearLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    public void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        if (mListener != null) {
-            mListener.onFitSystemWindows(insets);
-        }
-        return super.fitSystemWindows(insets);
-    }
-}
diff --git a/android/support/v7/widget/FitWindowsViewGroup.java b/android/support/v7/widget/FitWindowsViewGroup.java
deleted file mode 100644
index ffec343..0000000
--- a/android/support/v7/widget/FitWindowsViewGroup.java
+++ /dev/null
@@ -1,35 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.Rect;
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface FitWindowsViewGroup {
-
-    interface OnFitSystemWindowsListener {
-        void onFitSystemWindows(Rect insets);
-    }
-
-    void setOnFitSystemWindowsListener(OnFitSystemWindowsListener listener);
-}
diff --git a/android/support/v7/widget/ForwardingListener.java b/android/support/v7/widget/ForwardingListener.java
deleted file mode 100644
index 284a4ea..0000000
--- a/android/support/v7/widget/ForwardingListener.java
+++ /dev/null
@@ -1,336 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.SystemClock;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.ShowableListMenu;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewParent;
-
-/**
- * Abstract class that forwards touch events to a {@link ShowableListMenu}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public abstract class ForwardingListener
-        implements View.OnTouchListener, View.OnAttachStateChangeListener {
-
-    /** Scaled touch slop, used for detecting movement outside bounds. */
-    private final float mScaledTouchSlop;
-
-    /** Timeout before disallowing intercept on the source's parent. */
-    private final int mTapTimeout;
-
-    /** Timeout before accepting a long-press to start forwarding. */
-    private final int mLongPressTimeout;
-
-    /** Source view from which events are forwarded. */
-    final View mSrc;
-
-    /** Runnable used to prevent conflicts with scrolling parents. */
-    private Runnable mDisallowIntercept;
-
-    /** Runnable used to trigger forwarding on long-press. */
-    private Runnable mTriggerLongPress;
-
-    /** Whether this listener is currently forwarding touch events. */
-    private boolean mForwarding;
-
-    /** The id of the first pointer down in the current event stream. */
-    private int mActivePointerId;
-
-    /**
-     * Temporary Matrix instance
-     */
-    private final int[] mTmpLocation = new int[2];
-
-    public ForwardingListener(View src) {
-        mSrc = src;
-        src.setLongClickable(true);
-        src.addOnAttachStateChangeListener(this);
-
-        mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop();
-        mTapTimeout = ViewConfiguration.getTapTimeout();
-
-        // Use a medium-press timeout. Halfway between tap and long-press.
-        mLongPressTimeout = (mTapTimeout + ViewConfiguration.getLongPressTimeout()) / 2;
-    }
-
-    /**
-     * Returns the popup to which this listener is forwarding events.
-     * <p>
-     * Override this to return the correct popup. If the popup is displayed
-     * asynchronously, you may also need to override
-     * {@link #onForwardingStopped} to prevent premature cancelation of
-     * forwarding.
-     *
-     * @return the popup to which this listener is forwarding events
-     */
-    public abstract ShowableListMenu getPopup();
-
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        final boolean wasForwarding = mForwarding;
-        final boolean forwarding;
-        if (wasForwarding) {
-            forwarding = onTouchForwarded(event) || !onForwardingStopped();
-        } else {
-            forwarding = onTouchObserved(event) && onForwardingStarted();
-
-            if (forwarding) {
-                // Make sure we cancel any ongoing source event stream.
-                final long now = SystemClock.uptimeMillis();
-                final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL,
-                        0.0f, 0.0f, 0);
-                mSrc.onTouchEvent(e);
-                e.recycle();
-            }
-        }
-
-        mForwarding = forwarding;
-        return forwarding || wasForwarding;
-    }
-
-    @Override
-    public void onViewAttachedToWindow(View v) {
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View v) {
-        mForwarding = false;
-        mActivePointerId = MotionEvent.INVALID_POINTER_ID;
-
-        if (mDisallowIntercept != null) {
-            mSrc.removeCallbacks(mDisallowIntercept);
-        }
-    }
-
-    /**
-     * Called when forwarding would like to start.
-     * <p>
-     * By default, this will show the popup returned by {@link #getPopup()}.
-     * It may be overridden to perform another action, like clicking the
-     * source view or preparing the popup before showing it.
-     *
-     * @return true to start forwarding, false otherwise
-     */
-    protected boolean onForwardingStarted() {
-        final ShowableListMenu popup = getPopup();
-        if (popup != null && !popup.isShowing()) {
-            popup.show();
-        }
-        return true;
-    }
-
-    /**
-     * Called when forwarding would like to stop.
-     * <p>
-     * By default, this will dismiss the popup returned by
-     * {@link #getPopup()}. It may be overridden to perform some other
-     * action.
-     *
-     * @return true to stop forwarding, false otherwise
-     */
-    protected boolean onForwardingStopped() {
-        final ShowableListMenu popup = getPopup();
-        if (popup != null && popup.isShowing()) {
-            popup.dismiss();
-        }
-        return true;
-    }
-
-    /**
-     * Observes motion events and determines when to start forwarding.
-     *
-     * @param srcEvent motion event in source view coordinates
-     * @return true to start forwarding motion events, false otherwise
-     */
-    private boolean onTouchObserved(MotionEvent srcEvent) {
-        final View src = mSrc;
-        if (!src.isEnabled()) {
-            return false;
-        }
-
-        final int actionMasked = srcEvent.getActionMasked();
-        switch (actionMasked) {
-            case MotionEvent.ACTION_DOWN:
-                mActivePointerId = srcEvent.getPointerId(0);
-
-                if (mDisallowIntercept == null) {
-                    mDisallowIntercept = new DisallowIntercept();
-                }
-                src.postDelayed(mDisallowIntercept, mTapTimeout);
-
-                if (mTriggerLongPress == null) {
-                    mTriggerLongPress = new TriggerLongPress();
-                }
-                src.postDelayed(mTriggerLongPress, mLongPressTimeout);
-                break;
-            case MotionEvent.ACTION_MOVE:
-                final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId);
-                if (activePointerIndex >= 0) {
-                    final float x = srcEvent.getX(activePointerIndex);
-                    final float y = srcEvent.getY(activePointerIndex);
-
-                    // Has the pointer moved outside of the view?
-                    if (!pointInView(src, x, y, mScaledTouchSlop)) {
-                        clearCallbacks();
-
-                        // Don't let the parent intercept our events.
-                        src.getParent().requestDisallowInterceptTouchEvent(true);
-                        return true;
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                clearCallbacks();
-                break;
-        }
-
-        return false;
-    }
-
-    private void clearCallbacks() {
-        if (mTriggerLongPress != null) {
-            mSrc.removeCallbacks(mTriggerLongPress);
-        }
-
-        if (mDisallowIntercept != null) {
-            mSrc.removeCallbacks(mDisallowIntercept);
-        }
-    }
-
-    void onLongPress() {
-        clearCallbacks();
-
-        final View src = mSrc;
-        if (!src.isEnabled() || src.isLongClickable()) {
-            // Ignore long-press if the view is disabled or has its own
-            // handler.
-            return;
-        }
-
-        if (!onForwardingStarted()) {
-            return;
-        }
-
-        // Don't let the parent intercept our events.
-        src.getParent().requestDisallowInterceptTouchEvent(true);
-
-        // Make sure we cancel any ongoing source event stream.
-        final long now = SystemClock.uptimeMillis();
-        final MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
-        src.onTouchEvent(e);
-        e.recycle();
-
-        mForwarding = true;
-    }
-
-    /**
-     * Handles forwarded motion events and determines when to stop
-     * forwarding.
-     *
-     * @param srcEvent motion event in source view coordinates
-     * @return true to continue forwarding motion events, false to cancel
-     */
-    private boolean onTouchForwarded(MotionEvent srcEvent) {
-        final View src = mSrc;
-        final ShowableListMenu popup = getPopup();
-        if (popup == null || !popup.isShowing()) {
-            return false;
-        }
-
-        final DropDownListView dst = (DropDownListView) popup.getListView();
-        if (dst == null || !dst.isShown()) {
-            return false;
-        }
-
-        // Convert event to destination-local coordinates.
-        final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
-        toGlobalMotionEvent(src, dstEvent);
-        toLocalMotionEvent(dst, dstEvent);
-
-        // Forward converted event to destination view, then recycle it.
-        final boolean handled = dst.onForwardedEvent(dstEvent, mActivePointerId);
-        dstEvent.recycle();
-
-        // Always cancel forwarding when the touch stream ends.
-        final int action = srcEvent.getActionMasked();
-        final boolean keepForwarding = action != MotionEvent.ACTION_UP
-                && action != MotionEvent.ACTION_CANCEL;
-
-        return handled && keepForwarding;
-    }
-
-    private static boolean pointInView(View view, float localX, float localY, float slop) {
-        return localX >= -slop && localY >= -slop &&
-                localX < ((view.getRight() - view.getLeft()) + slop) &&
-                localY < ((view.getBottom() - view.getTop()) + slop);
-    }
-
-    /**
-     * Emulates View.toLocalMotionEvent(). This implementation does not handle transformations
-     * (scaleX, scaleY, etc).
-     */
-    private boolean toLocalMotionEvent(View view, MotionEvent event) {
-        final int[] loc = mTmpLocation;
-        view.getLocationOnScreen(loc);
-        event.offsetLocation(-loc[0], -loc[1]);
-        return true;
-    }
-
-    /**
-     * Emulates View.toGlobalMotionEvent(). This implementation does not handle transformations
-     * (scaleX, scaleY, etc).
-     */
-    private boolean toGlobalMotionEvent(View view, MotionEvent event) {
-        final int[] loc = mTmpLocation;
-        view.getLocationOnScreen(loc);
-        event.offsetLocation(loc[0], loc[1]);
-        return true;
-    }
-
-    private class DisallowIntercept implements Runnable {
-        DisallowIntercept() {
-        }
-
-        @Override
-        public void run() {
-            final ViewParent parent = mSrc.getParent();
-            if (parent != null) {
-                parent.requestDisallowInterceptTouchEvent(true);
-            }
-        }
-    }
-
-    private class TriggerLongPress implements Runnable {
-        TriggerLongPress() {
-        }
-
-        @Override
-        public void run() {
-            onLongPress();
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/GapWorker.java b/android/support/v7/widget/GapWorker.java
deleted file mode 100644
index c7e81b5..0000000
--- a/android/support/v7/widget/GapWorker.java
+++ /dev/null
@@ -1,404 +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.widget;
-
-import android.support.annotation.Nullable;
-import android.support.v4.os.TraceCompat;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.concurrent.TimeUnit;
-
-final class GapWorker implements Runnable {
-
-    static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
-
-    ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
-    long mPostTimeNs;
-    long mFrameIntervalNs;
-
-    static class Task {
-        public boolean immediate;
-        public int viewVelocity;
-        public int distanceToItem;
-        public RecyclerView view;
-        public int position;
-
-        public void clear() {
-            immediate = false;
-            viewVelocity = 0;
-            distanceToItem = 0;
-            view = null;
-            position = 0;
-        }
-    }
-
-    /**
-     * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects
-     * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared
-     * in between calls.
-     */
-    private ArrayList<Task> mTasks = new ArrayList<>();
-
-    /**
-     * Prefetch information associated with a specific RecyclerView.
-     */
-    static class LayoutPrefetchRegistryImpl
-            implements RecyclerView.LayoutManager.LayoutPrefetchRegistry {
-        int mPrefetchDx;
-        int mPrefetchDy;
-        int[] mPrefetchArray;
-
-        int mCount;
-
-        void setPrefetchVector(int dx, int dy) {
-            mPrefetchDx = dx;
-            mPrefetchDy = dy;
-        }
-
-        void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) {
-            mCount = 0;
-            if (mPrefetchArray != null) {
-                Arrays.fill(mPrefetchArray, -1);
-            }
-
-            final RecyclerView.LayoutManager layout = view.mLayout;
-            if (view.mAdapter != null
-                    && layout != null
-                    && layout.isItemPrefetchEnabled()) {
-                if (nested) {
-                    // nested prefetch, only if no adapter updates pending. Note: we don't query
-                    // view.hasPendingAdapterUpdates(), as first layout may not have occurred
-                    if (!view.mAdapterHelper.hasPendingUpdates()) {
-                        layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this);
-                    }
-                } else {
-                    // momentum based prefetch, only if we trust current child/adapter state
-                    if (!view.hasPendingAdapterUpdates()) {
-                        layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,
-                                view.mState, this);
-                    }
-                }
-
-                if (mCount > layout.mPrefetchMaxCountObserved) {
-                    layout.mPrefetchMaxCountObserved = mCount;
-                    layout.mPrefetchMaxObservedInInitialPrefetch = nested;
-                    view.mRecycler.updateViewCacheSize();
-                }
-            }
-        }
-
-        @Override
-        public void addPosition(int layoutPosition, int pixelDistance) {
-            if (layoutPosition < 0) {
-                throw new IllegalArgumentException("Layout positions must be non-negative");
-            }
-
-            if (pixelDistance < 0) {
-                throw new IllegalArgumentException("Pixel distance must be non-negative");
-            }
-
-            // allocate or expand array as needed, doubling when needed
-            final int storagePosition = mCount * 2;
-            if (mPrefetchArray == null) {
-                mPrefetchArray = new int[4];
-                Arrays.fill(mPrefetchArray, -1);
-            } else if (storagePosition >= mPrefetchArray.length) {
-                final int[] oldArray = mPrefetchArray;
-                mPrefetchArray = new int[storagePosition * 2];
-                System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);
-            }
-
-            // add position
-            mPrefetchArray[storagePosition] = layoutPosition;
-            mPrefetchArray[storagePosition + 1] = pixelDistance;
-
-            mCount++;
-        }
-
-        boolean lastPrefetchIncludedPosition(int position) {
-            if (mPrefetchArray != null) {
-                final int count = mCount * 2;
-                for (int i = 0; i < count; i += 2) {
-                    if (mPrefetchArray[i] == position) return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Called when prefetch indices are no longer valid for cache prioritization.
-         */
-        void clearPrefetchPositions() {
-            if (mPrefetchArray != null) {
-                Arrays.fill(mPrefetchArray, -1);
-            }
-            mCount = 0;
-        }
-    }
-
-    public void add(RecyclerView recyclerView) {
-        if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
-            throw new IllegalStateException("RecyclerView already present in worker list!");
-        }
-        mRecyclerViews.add(recyclerView);
-    }
-
-    public void remove(RecyclerView recyclerView) {
-        boolean removeSuccess = mRecyclerViews.remove(recyclerView);
-        if (RecyclerView.DEBUG && !removeSuccess) {
-            throw new IllegalStateException("RecyclerView removal failed!");
-        }
-    }
-
-    /**
-     * Schedule a prefetch immediately after the current traversal.
-     */
-    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
-        if (recyclerView.isAttachedToWindow()) {
-            if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
-                throw new IllegalStateException("attempting to post unregistered view!");
-            }
-            if (mPostTimeNs == 0) {
-                mPostTimeNs = recyclerView.getNanoTime();
-                recyclerView.post(this);
-            }
-        }
-
-        recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
-    }
-
-    static Comparator<Task> sTaskComparator = new Comparator<Task>() {
-        @Override
-        public int compare(Task lhs, Task rhs) {
-            // first, prioritize non-cleared tasks
-            if ((lhs.view == null) != (rhs.view == null)) {
-                return lhs.view == null ? 1 : -1;
-            }
-
-            // then prioritize immediate
-            if (lhs.immediate != rhs.immediate) {
-                return lhs.immediate ? -1 : 1;
-            }
-
-            // then prioritize _highest_ view velocity
-            int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity;
-            if (deltaViewVelocity != 0) return deltaViewVelocity;
-
-            // then prioritize _lowest_ distance to item
-            int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem;
-            if (deltaDistanceToItem != 0) return deltaDistanceToItem;
-
-            return 0;
-        }
-    };
-
-    private void buildTaskList() {
-        // Update PrefetchRegistry in each view
-        final int viewCount = mRecyclerViews.size();
-        int totalTaskCount = 0;
-        for (int i = 0; i < viewCount; i++) {
-            RecyclerView view = mRecyclerViews.get(i);
-            if (view.getWindowVisibility() == View.VISIBLE) {
-                view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false);
-                totalTaskCount += view.mPrefetchRegistry.mCount;
-            }
-        }
-
-        // Populate task list from prefetch data...
-        mTasks.ensureCapacity(totalTaskCount);
-        int totalTaskIndex = 0;
-        for (int i = 0; i < viewCount; i++) {
-            RecyclerView view = mRecyclerViews.get(i);
-            if (view.getWindowVisibility() != View.VISIBLE) {
-                // Invisible view, don't bother prefetching
-                continue;
-            }
-
-            LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
-            final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
-                    + Math.abs(prefetchRegistry.mPrefetchDy);
-            for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) {
-                final Task task;
-                if (totalTaskIndex >= mTasks.size()) {
-                    task = new Task();
-                    mTasks.add(task);
-                } else {
-                    task = mTasks.get(totalTaskIndex);
-                }
-                final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];
-
-                task.immediate = distanceToItem <= viewVelocity;
-                task.viewVelocity = viewVelocity;
-                task.distanceToItem = distanceToItem;
-                task.view = view;
-                task.position = prefetchRegistry.mPrefetchArray[j];
-
-                totalTaskIndex++;
-            }
-        }
-
-        // ... and priority sort
-        Collections.sort(mTasks, sTaskComparator);
-    }
-
-    static boolean isPrefetchPositionAttached(RecyclerView view, int position) {
-        final int childCount = view.mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View attachedView = view.mChildHelper.getUnfilteredChildAt(i);
-            RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView);
-            // Note: can use mPosition here because adapter doesn't have pending updates
-            if (holder.mPosition == position && !holder.isInvalid()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
-            int position, long deadlineNs) {
-        if (isPrefetchPositionAttached(view, position)) {
-            // don't attempt to prefetch attached views
-            return null;
-        }
-
-        RecyclerView.Recycler recycler = view.mRecycler;
-        RecyclerView.ViewHolder holder;
-        try {
-            view.onEnterLayoutOrScroll();
-            holder = recycler.tryGetViewHolderForPositionByDeadline(
-                    position, false, deadlineNs);
-
-            if (holder != null) {
-                if (holder.isBound() && !holder.isInvalid()) {
-                    // Only give the view a chance to go into the cache if binding succeeded
-                    // Note that we must use public method, since item may need cleanup
-                    recycler.recycleView(holder.itemView);
-                } else {
-                    // Didn't bind, so we can't cache the view, but it will stay in the pool until
-                    // next prefetch/traversal. If a View fails to bind, it means we didn't have
-                    // enough time prior to the deadline (and won't for other instances of this
-                    // type, during this GapWorker prefetch pass).
-                    recycler.addViewHolderToRecycledViewPool(holder, false);
-                }
-            }
-        } finally {
-            view.onExitLayoutOrScroll(false);
-        }
-        return holder;
-    }
-
-    private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView,
-            long deadlineNs) {
-        if (innerView == null) {
-            return;
-        }
-
-        if (innerView.mDataSetHasChangedAfterLayout
-                && innerView.mChildHelper.getUnfilteredChildCount() != 0) {
-            // RecyclerView has new data, but old attached views. Clear everything, so that
-            // we can prefetch without partially stale data.
-            innerView.removeAndRecycleViews();
-        }
-
-        // do nested prefetch!
-        final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry;
-        innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true);
-
-        if (innerPrefetchRegistry.mCount != 0) {
-            try {
-                TraceCompat.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG);
-                innerView.mState.prepareForNestedPrefetch(innerView.mAdapter);
-                for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) {
-                    // Note that we ignore immediate flag for inner items because
-                    // we have lower confidence they're needed next frame.
-                    final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i];
-                    prefetchPositionWithDeadline(innerView, innerPosition, deadlineNs);
-                }
-            } finally {
-                TraceCompat.endSection();
-            }
-        }
-    }
-
-    private void flushTaskWithDeadline(Task task, long deadlineNs) {
-        long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
-        RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
-                task.position, taskDeadlineNs);
-        if (holder != null
-                && holder.mNestedRecyclerView != null
-                && holder.isBound()
-                && !holder.isInvalid()) {
-            prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs);
-        }
-    }
-
-    private void flushTasksWithDeadline(long deadlineNs) {
-        for (int i = 0; i < mTasks.size(); i++) {
-            final Task task = mTasks.get(i);
-            if (task.view == null) {
-                break; // done with populated tasks
-            }
-            flushTaskWithDeadline(task, deadlineNs);
-            task.clear();
-        }
-    }
-
-    void prefetch(long deadlineNs) {
-        buildTaskList();
-        flushTasksWithDeadline(deadlineNs);
-    }
-
-    @Override
-    public void run() {
-        try {
-            TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
-
-            if (mRecyclerViews.isEmpty()) {
-                // abort - no work to do
-                return;
-            }
-
-            // Query most recent vsync so we can predict next one. Note that drawing time not yet
-            // valid in animation/input callbacks, so query it here to be safe.
-            final int size = mRecyclerViews.size();
-            long latestFrameVsyncMs = 0;
-            for (int i = 0; i < size; i++) {
-                RecyclerView view = mRecyclerViews.get(i);
-                if (view.getWindowVisibility() == View.VISIBLE) {
-                    latestFrameVsyncMs = Math.max(view.getDrawingTime(), latestFrameVsyncMs);
-                }
-            }
-
-            if (latestFrameVsyncMs == 0) {
-                // abort - either no views visible, or couldn't get last vsync for estimating next
-                return;
-            }
-
-            long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs;
-
-            prefetch(nextFrameNs);
-
-            // TODO: consider rescheduling self, if there's more work to do
-        } finally {
-            mPostTimeNs = 0;
-            TraceCompat.endSection();
-        }
-    }
-}
diff --git a/android/support/v7/widget/GridLayout.java b/android/support/v7/widget/GridLayout.java
deleted file mode 100644
index ad9ece5..0000000
--- a/android/support/v7/widget/GridLayout.java
+++ /dev/null
@@ -1,2927 +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.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;
-import android.graphics.Paint;
-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;
-import android.util.Pair;
-import android.util.Printer;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A layout that places its children in a rectangular <em>grid</em>.
- * <p>
- * The grid is composed of a set of infinitely thin lines that separate the
- * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
- * by grid <em>indices</em>. A grid with {@code N} columns
- * has {@code N + 1} grid indices that run from {@code 0}
- * through {@code N} inclusive. Regardless of how GridLayout is
- * configured, grid index {@code 0} is fixed to the leading edge of the
- * container and grid index {@code N} is fixed to its trailing edge
- * (after padding is taken into account).
- *
- * <h4>Row and Column Specs</h4>
- *
- * Children occupy one or more contiguous cells, as defined
- * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and
- * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters.
- * Each spec defines the set of rows or columns that are to be
- * occupied; and how children should be aligned within the resulting group of cells.
- * Although cells do not normally overlap in a GridLayout, GridLayout does
- * not prevent children being defined to occupy the same cell or group of cells.
- * In this case however, there is no guarantee that children will not themselves
- * overlap after the layout operation completes.
- *
- * <h4>Default Cell Assignment</h4>
- *
- * If a child does not specify the row and column indices of the cell it
- * wishes to occupy, GridLayout assigns cell locations automatically using its:
- * {@link GridLayout#setOrientation(int) orientation},
- * {@link GridLayout#setRowCount(int) rowCount} and
- * {@link GridLayout#setColumnCount(int) columnCount} properties.
- *
- * <h4>Space</h4>
- *
- * Space between children may be specified either by using instances of the
- * dedicated {@link android.support.v4.widget.Space} view or by setting the
- *
- * {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin},
- * {@link ViewGroup.MarginLayoutParams#topMargin topMargin},
- * {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and
- * {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin}
- *
- * layout parameters. When the
- * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
- * property is set, default margins around children are automatically
- * allocated based on the prevailing UI style guide for the platform.
- * Each of the margins so defined may be independently overridden by an assignment
- * to the appropriate layout parameter.
- * Default values will generally produce a reasonable spacing between components
- * but values may change between different releases of the platform.
- *
- * <h4>Excess Space Distribution</h4>
- *
- * GridLayout's distribution of excess space accommodates the principle of weight.
- * In the event that no weights are specified, columns and rows are taken as
- * flexible if their views specify some form of alignment within their groups.
- * <p>
- * The flexibility of a view is therefore influenced by its alignment which is,
- * in turn, typically defined by setting the
- * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
- * If either a weight or alignment were defined along a given axis then the component
- * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
- * the component is instead assumed to be <em>inflexible</em>.
- * <p>
- * Multiple components in the same row or column group are
- * considered to act in <em>parallel</em>. Such a
- * group is flexible only if <em>all</em> of the components
- * within it are flexible. Row and column groups that sit either side of a common boundary
- * are instead considered to act in <em>series</em>. The composite group made of these two
- * elements is flexible if <em>one</em> of its elements is flexible.
- * <p>
- * To make a column stretch, make sure all of the components inside it define a
- * weight or a gravity. To prevent a column from stretching, ensure that one of the components
- * in the column does not define a weight or a gravity.
- * <p>
- * When the principle of flexibility does not provide complete disambiguation,
- * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
- * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
- * parameters as a constraint in the a set of variables that define the grid-lines along a
- * given axis. During layout, GridLayout solves the constraints so as to return the unique
- * solution to those constraints for which all variables are less-than-or-equal-to
- * the corresponding value in any other valid solution.
- *
- * <h4>Interpretation of GONE</h4>
- *
- * For layout purposes, GridLayout treats views whose visibility status is
- * {@link View#GONE GONE}, as having zero width and height. This is subtly different from
- * the policy of ignoring views that are marked as GONE outright. If, for example, a gone-marked
- * view was alone in a column, that column would itself collapse to zero width if and only if
- * no gravity was defined on the view. If gravity was defined, then the gone-marked
- * view has no effect on the layout and the container should be laid out as if the view
- * had never been added to it. GONE views are taken to have zero weight during excess space
- * distribution.
- * <p>
- * These statements apply equally to rows as well as columns, and to groups of rows or columns.
- *
- *
- * <p>
- * See {@link GridLayout.LayoutParams} for a full description of the
- * layout parameters used by GridLayout.
- *
- * @attr name android:orientation
- * @attr name android:rowCount
- * @attr name android:columnCount
- * @attr name android:useDefaultMargins
- * @attr name android:rowOrderPreserved
- * @attr name android:columnOrderPreserved
- */
-public class GridLayout extends ViewGroup {
-
-    // Public constants
-
-    /**
-     * The horizontal orientation.
-     */
-    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
-
-    /**
-     * The vertical orientation.
-     */
-    public static final int VERTICAL = LinearLayout.VERTICAL;
-
-    /**
-     * The constant used to indicate that a value is undefined.
-     * Fields can use this value to indicate that their values
-     * have not yet been set. Similarly, methods can return this value
-     * to indicate that there is no suitable value that the implementation
-     * can return.
-     * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
-     * intended to avoid confusion between valid values whose sign may not be known.
-     */
-    public static final int UNDEFINED = Integer.MIN_VALUE;
-
-    /**
-     * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
-     * When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment
-     * is made between the edges of each component's raw
-     * view boundary: i.e. the area delimited by the component's:
-     * {@link android.view.View#getTop() top},
-     * {@link android.view.View#getLeft() left},
-     * {@link android.view.View#getBottom() bottom} and
-     * {@link android.view.View#getRight() right} properties.
-     * <p>
-     * For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode,
-     * children that belong to a row group that uses {@link #TOP} alignment will
-     * all return the same value when their {@link android.view.View#getTop()}
-     * method is called.
-     *
-     * @see #setAlignmentMode(int)
-     */
-    public static final int ALIGN_BOUNDS = 0;
-
-    /**
-     * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
-     * When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS},
-     * the bounds of each view are extended outwards, according
-     * to their margins, before the edges of the resulting rectangle are aligned.
-     * <p>
-     * For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode,
-     * the quantity {@code top - layoutParams.topMargin} is the same for all children that
-     * belong to a row group that uses {@link #TOP} alignment.
-     *
-     * @see #setAlignmentMode(int)
-     */
-    public static final int ALIGN_MARGINS = 1;
-
-    // Misc constants
-
-    static final int MAX_SIZE = 100000;
-    static final int DEFAULT_CONTAINER_MARGIN = 0;
-    static final int UNINITIALIZED_HASH = 0;
-    static final Printer LOG_PRINTER = new LogPrinter(Log.DEBUG, GridLayout.class.getName());
-    static final Printer NO_PRINTER = new Printer() {
-        @Override
-        public void println(String x) {
-        }
-    };
-
-    // Defaults
-
-    private static final int DEFAULT_ORIENTATION = HORIZONTAL;
-    private static final int DEFAULT_COUNT = UNDEFINED;
-    private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
-    static final boolean DEFAULT_ORDER_PRESERVED = true;
-    private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
-
-    // TypedArray indices
-
-    private static final int ORIENTATION = R.styleable.GridLayout_orientation;
-    private static final int ROW_COUNT = R.styleable.GridLayout_rowCount;
-    private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount;
-    private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins;
-    private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode;
-    private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved;
-    private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved;
-
-    // Instance variables
-
-    final Axis mHorizontalAxis = new Axis(true);
-    final Axis mVerticalAxis = new Axis(false);
-    int mOrientation = DEFAULT_ORIENTATION;
-    boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
-    int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
-    int mDefaultGap;
-    int mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
-    Printer mPrinter = LOG_PRINTER;
-
-    // Constructors
-
-    /**
-     * {@inheritDoc}
-     */
-    public GridLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout);
-        try {
-            setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
-            setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
-            setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
-            setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
-            setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
-            setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
-            setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
-        } finally {
-            a.recycle();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public GridLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public GridLayout(Context context) {
-        //noinspection NullableProblems
-        this(context, null);
-    }
-
-    // Implementation
-
-    /**
-     * Returns the current orientation.
-     *
-     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
-     *
-     * @see #setOrientation(int)
-     *
-     * @attr name android:orientation
-     */
-    public int getOrientation() {
-        return mOrientation;
-    }
-
-    /**
-     *
-     * GridLayout uses the orientation property for two purposes:
-     * <ul>
-     *  <li>
-     *      To control the 'direction' in which default row/column indices are generated
-     *      when they are not specified in a component's layout parameters.
-     *  </li>
-     *  <li>
-     *      To control which axis should be processed first during the layout operation:
-     *      when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
-     *  </li>
-     * </ul>
-     *
-     * The order in which axes are laid out is important if, for example, the height of
-     * one of GridLayout's children is dependent on its width - and its width is, in turn,
-     * dependent on the widths of other components.
-     * <p>
-     * If your layout contains a {@link android.widget.TextView} (or derivative:
-     * {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is
-     * in multi-line mode (the default) it is normally best to leave GridLayout's
-     * orientation as {@code HORIZONTAL} - because {@code TextView} is capable of
-     * deriving its height for a given width, but not the other way around.
-     * <p>
-     * Other than the effects above, orientation does not affect the actual layout operation of
-     * GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if
-     * the height of the intended layout greatly exceeds its width.
-     * <p>
-     * The default value of this property is {@link #HORIZONTAL}.
-     *
-     * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
-     *
-     * @see #getOrientation()
-     *
-     * @attr name android:orientation
-     */
-    public void setOrientation(int orientation) {
-        if (this.mOrientation != orientation) {
-            this.mOrientation = orientation;
-            invalidateStructure();
-            requestLayout();
-        }
-    }
-
-    /**
-     * Returns the current number of rows. This is either the last value that was set
-     * with {@link #setRowCount(int)} or, if no such value was set, the maximum
-     * value of each the upper bounds defined in {@link LayoutParams#rowSpec}.
-     *
-     * @return the current number of rows
-     *
-     * @see #setRowCount(int)
-     * @see LayoutParams#rowSpec
-     *
-     * @attr name android:rowCount
-     */
-    public int getRowCount() {
-        return mVerticalAxis.getCount();
-    }
-
-    /**
-     * RowCount is used only to generate default row/column indices when
-     * they are not specified by a component's layout parameters.
-     *
-     * @param rowCount the number of rows
-     *
-     * @see #getRowCount()
-     * @see LayoutParams#rowSpec
-     *
-     * @attr name android:rowCount
-     */
-    public void setRowCount(int rowCount) {
-        mVerticalAxis.setCount(rowCount);
-        invalidateStructure();
-        requestLayout();
-    }
-
-    /**
-     * Returns the current number of columns. This is either the last value that was set
-     * with {@link #setColumnCount(int)} or, if no such value was set, the maximum
-     * value of each the upper bounds defined in {@link LayoutParams#columnSpec}.
-     *
-     * @return the current number of columns
-     *
-     * @see #setColumnCount(int)
-     * @see LayoutParams#columnSpec
-     *
-     * @attr name android:columnCount
-     */
-    public int getColumnCount() {
-        return mHorizontalAxis.getCount();
-    }
-
-    /**
-     * ColumnCount is used only to generate default column/column indices when
-     * they are not specified by a component's layout parameters.
-     *
-     * @param columnCount the number of columns.
-     *
-     * @see #getColumnCount()
-     * @see LayoutParams#columnSpec
-     *
-     * @attr name android:columnCount
-     */
-    public void setColumnCount(int columnCount) {
-        mHorizontalAxis.setCount(columnCount);
-        invalidateStructure();
-        requestLayout();
-    }
-
-    /**
-     * Returns whether or not this GridLayout will allocate default margins when no
-     * corresponding layout parameters are defined.
-     *
-     * @return {@code true} if default margins should be allocated
-     *
-     * @see #setUseDefaultMargins(boolean)
-     *
-     * @attr name android:useDefaultMargins
-     */
-    public boolean getUseDefaultMargins() {
-        return mUseDefaultMargins;
-    }
-
-    /**
-     * When {@code true}, GridLayout allocates default margins around children
-     * based on the child's visual characteristics. Each of the
-     * margins so defined may be independently overridden by an assignment
-     * to the appropriate layout parameter.
-     * <p>
-     * When {@code false}, the default value of all margins is zero.
-     * <p>
-     * When setting to {@code true}, consider setting the value of the
-     * {@link #setAlignmentMode(int) alignmentMode}
-     * property to {@link #ALIGN_BOUNDS}.
-     * <p>
-     * The default value of this property is {@code false}.
-     *
-     * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
-     *
-     * @see #getUseDefaultMargins()
-     * @see #setAlignmentMode(int)
-     *
-     * @see MarginLayoutParams#leftMargin
-     * @see MarginLayoutParams#topMargin
-     * @see MarginLayoutParams#rightMargin
-     * @see MarginLayoutParams#bottomMargin
-     *
-     * @attr name android:useDefaultMargins
-     */
-    public void setUseDefaultMargins(boolean useDefaultMargins) {
-        this.mUseDefaultMargins = useDefaultMargins;
-        requestLayout();
-    }
-
-    /**
-     * Returns the alignment mode.
-     *
-     * @return the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
-     *
-     * @see #ALIGN_BOUNDS
-     * @see #ALIGN_MARGINS
-     *
-     * @see #setAlignmentMode(int)
-     *
-     * @attr name android:alignmentMode
-     */
-    public int getAlignmentMode() {
-        return mAlignmentMode;
-    }
-
-    /**
-     * Sets the alignment mode to be used for all of the alignments between the
-     * children of this container.
-     * <p>
-     * The default value of this property is {@link #ALIGN_MARGINS}.
-     *
-     * @param alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
-     *
-     * @see #ALIGN_BOUNDS
-     * @see #ALIGN_MARGINS
-     *
-     * @see #getAlignmentMode()
-     *
-     * @attr name android:alignmentMode
-     */
-    public void setAlignmentMode(int alignmentMode) {
-        this.mAlignmentMode = alignmentMode;
-        requestLayout();
-    }
-
-    /**
-     * Returns whether or not row boundaries are ordered by their grid indices.
-     *
-     * @return {@code true} if row boundaries must appear in the order of their indices,
-     *         {@code false} otherwise
-     *
-     * @see #setRowOrderPreserved(boolean)
-     *
-     * @attr name android:rowOrderPreserved
-     */
-    public boolean isRowOrderPreserved() {
-        return mVerticalAxis.isOrderPreserved();
-    }
-
-    /**
-     * When this property is {@code true}, GridLayout is forced to place the row boundaries
-     * so that their associated grid indices are in ascending order in the view.
-     * <p>
-     * When this property is {@code false} GridLayout is at liberty to place the vertical row
-     * boundaries in whatever order best fits the given constraints.
-     * <p>
-     * The default value of this property is {@code true}.
-
-     * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
-     *        of row boundaries
-     *
-     * @see #isRowOrderPreserved()
-     *
-     * @attr name android:rowOrderPreserved
-     */
-    public void setRowOrderPreserved(boolean rowOrderPreserved) {
-        mVerticalAxis.setOrderPreserved(rowOrderPreserved);
-        invalidateStructure();
-        requestLayout();
-    }
-
-    /**
-     * Returns whether or not column boundaries are ordered by their grid indices.
-     *
-     * @return {@code true} if column boundaries must appear in the order of their indices,
-     *         {@code false} otherwise
-     *
-     * @see #setColumnOrderPreserved(boolean)
-     *
-     * @attr name android:columnOrderPreserved
-     */
-    public boolean isColumnOrderPreserved() {
-        return mHorizontalAxis.isOrderPreserved();
-    }
-
-    /**
-     * When this property is {@code true}, GridLayout is forced to place the column boundaries
-     * so that their associated grid indices are in ascending order in the view.
-     * <p>
-     * When this property is {@code false} GridLayout is at liberty to place the horizontal column
-     * boundaries in whatever order best fits the given constraints.
-     * <p>
-     * The default value of this property is {@code true}.
-     *
-     * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
-     *        of column boundaries.
-     *
-     * @see #isColumnOrderPreserved()
-     *
-     * @attr name android:columnOrderPreserved
-     */
-    public void setColumnOrderPreserved(boolean columnOrderPreserved) {
-        mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
-        invalidateStructure();
-        requestLayout();
-    }
-
-    /**
-     * Return the printer that will log diagnostics from this layout.
-     *
-     * @see #setPrinter(android.util.Printer)
-     *
-     * @return the printer associated with this view
-     */
-    public Printer getPrinter() {
-        return mPrinter;
-    }
-
-    /**
-     * Set the printer that will log diagnostics from this layout.
-     * The default value is created by {@link android.util.LogPrinter}.
-     *
-     * @param printer the printer associated with this layout
-     *
-     * @see #getPrinter()
-     */
-    public void setPrinter(Printer printer) {
-        this.mPrinter = (printer == null) ? NO_PRINTER : printer;
-    }
-
-    // Static utility methods
-
-    static int max2(int[] a, int valueIfEmpty) {
-        int result = valueIfEmpty;
-        for (int i = 0, N = a.length; i < N; i++) {
-            result = Math.max(result, a[i]);
-        }
-        return result;
-    }
-
-    @SuppressWarnings("unchecked")
-    static <T> T[] append(T[] a, T[] b) {
-        T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
-        System.arraycopy(a, 0, result, 0, a.length);
-        System.arraycopy(b, 0, result, a.length, b.length);
-        return result;
-    }
-
-    static Alignment getAlignment(int gravity, boolean horizontal) {
-        int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
-        int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
-        int flags = (gravity & mask) >> shift;
-        switch (flags) {
-            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
-                return horizontal ? LEFT : TOP;
-            case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
-                return horizontal ? RIGHT : BOTTOM;
-            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
-                return FILL;
-            case AXIS_SPECIFIED:
-                return CENTER;
-            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | GravityCompat.RELATIVE_LAYOUT_DIRECTION):
-                return START;
-            case (AXIS_SPECIFIED | AXIS_PULL_AFTER | GravityCompat.RELATIVE_LAYOUT_DIRECTION):
-                return END;
-            default:
-                return UNDEFINED_ALIGNMENT;
-        }
-    }
-
-    /** @noinspection UnusedParameters*/
-    private int getDefaultMargin(View c, boolean horizontal, boolean leading) {
-        if (c.getClass() == android.support.v4.widget.Space.class) {
-            return 0;
-        }
-        return mDefaultGap / 2;
-    }
-
-    private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
-        return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
-    }
-
-    private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) {
-        if (!mUseDefaultMargins) {
-            return 0;
-        }
-        Spec spec = horizontal ? p.columnSpec : p.rowSpec;
-        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
-        Interval span = spec.span;
-        boolean leading1 = (horizontal && isLayoutRtlCompat()) ? !leading : leading;
-        boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
-
-        return getDefaultMargin(c, isAtEdge, horizontal, leading);
-    }
-
-    int getMargin1(View view, boolean horizontal, boolean leading) {
-        LayoutParams lp = getLayoutParams(view);
-        int margin = horizontal ?
-                (leading ? lp.leftMargin : lp.rightMargin) :
-                (leading ? lp.topMargin : lp.bottomMargin);
-        return margin == UNDEFINED ? getDefaultMargin(view, lp, horizontal, leading) : margin;
-    }
-
-    private boolean isLayoutRtlCompat() {
-        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
-    }
-
-    private int getMargin(View view, boolean horizontal, boolean leading) {
-        if (mAlignmentMode == ALIGN_MARGINS) {
-            return getMargin1(view, horizontal, leading);
-        } else {
-            Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
-            int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
-            LayoutParams lp = getLayoutParams(view);
-            Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-            int index = leading ? spec.span.min : spec.span.max;
-            return margins[index];
-        }
-    }
-
-    private int getTotalMargin(View child, boolean horizontal) {
-        return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
-    }
-
-    private static boolean fits(int[] a, int value, int start, int end) {
-        if (end > a.length) {
-            return false;
-        }
-        for (int i = start; i < end; i++) {
-            if (a[i] > value) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static void procrusteanFill(int[] a, int start, int end, int value) {
-        int length = a.length;
-        Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
-    }
-
-    private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
-        lp.setRowSpecSpan(new Interval(row, row + rowSpan));
-        lp.setColumnSpecSpan(new Interval(col, col + colSpan));
-    }
-
-    // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
-    private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
-        int size = minorRange.size();
-        if (count == 0) {
-            return size;
-        }
-        int min = minorWasDefined ? min(minorRange.min, count) : 0;
-        return min(size, count - min);
-    }
-
-    // install default indices for cells that don't define them
-    private void validateLayoutParams() {
-        final boolean horizontal = (mOrientation == HORIZONTAL);
-        final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
-        final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
-
-        int major = 0;
-        int minor = 0;
-        int[] maxSizes = new int[count];
-
-        for (int i = 0, N = getChildCount(); i < N; i++) {
-            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
-
-            final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
-            final Interval majorRange = majorSpec.span;
-            final boolean majorWasDefined = majorSpec.startDefined;
-            final int majorSpan = majorRange.size();
-            if (majorWasDefined) {
-                major = majorRange.min;
-            }
-
-            final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
-            final Interval minorRange = minorSpec.span;
-            final boolean minorWasDefined = minorSpec.startDefined;
-            final int minorSpan = clip(minorRange, minorWasDefined, count);
-            if (minorWasDefined) {
-                minor = minorRange.min;
-            }
-
-            if (count != 0) {
-                // Find suitable row/col values when at least one is undefined.
-                if (!majorWasDefined || !minorWasDefined) {
-                    while (!fits(maxSizes, major, minor, minor + minorSpan)) {
-                        if (minorWasDefined) {
-                            major++;
-                        } else {
-                            if (minor + minorSpan <= count) {
-                                minor++;
-                            } else {
-                                minor = 0;
-                                major++;
-                            }
-                        }
-                    }
-                }
-                procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
-            }
-
-            if (horizontal) {
-                setCellGroup(lp, major, majorSpan, minor, minorSpan);
-            } else {
-                setCellGroup(lp, minor, minorSpan, major, majorSpan);
-            }
-
-            minor = minor + minorSpan;
-        }
-    }
-
-    private void invalidateStructure() {
-        mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
-        if (mHorizontalAxis != null) mHorizontalAxis.invalidateStructure();
-        if (mVerticalAxis != null) mVerticalAxis.invalidateStructure();
-        // This can end up being done twice. Better twice than not at all.
-        invalidateValues();
-    }
-
-    private void invalidateValues() {
-        // Need null check because requestLayout() is called in View's initializer,
-        // before we are set up.
-        if (mHorizontalAxis != null && mVerticalAxis != null) {
-            mHorizontalAxis.invalidateValues();
-            mVerticalAxis.invalidateValues();
-        }
-    }
-
-    final LayoutParams getLayoutParams(View c) {
-        return (LayoutParams) c.getLayoutParams();
-    }
-
-    static void handleInvalidParams(String msg) {
-        throw new IllegalArgumentException(msg + ". ");
-    }
-
-    private void checkLayoutParams(LayoutParams lp, boolean horizontal) {
-        String groupName = horizontal ? "column" : "row";
-        Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-        Interval span = spec.span;
-        if (span.min != UNDEFINED && span.min < 0) {
-            handleInvalidParams(groupName + " indices must be positive");
-        }
-        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
-        int count = axis.definedCount;
-        if (count != UNDEFINED) {
-            if (span.max > count) {
-                handleInvalidParams(groupName +
-                        " indices (start + span) mustn't exceed the " + groupName + " count");
-            }
-            if (span.size() > count) {
-                handleInvalidParams(groupName + " span mustn't exceed the " + groupName + " count");
-            }
-        }
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        if (!(p instanceof LayoutParams)) {
-            return false;
-        }
-        LayoutParams lp = (LayoutParams) p;
-
-        checkLayoutParams(lp, true);
-        checkLayoutParams(lp, false);
-
-        return true;
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams();
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
-        if (lp instanceof LayoutParams) {
-            return new LayoutParams((LayoutParams) lp);
-        } else if (lp instanceof MarginLayoutParams) {
-            return new LayoutParams((MarginLayoutParams) lp);
-        } else {
-            return new LayoutParams(lp);
-        }
-    }
-
-    // Draw grid
-
-    private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
-        if (isLayoutRtlCompat()) {
-            int width = getWidth();
-            graphics.drawLine(width - x1, y1, width - x2, y2, paint);
-        } else {
-            graphics.drawLine(x1, y1, x2, y2, paint);
-        }
-    }
-
-    private int computeLayoutParamsHashCode() {
-        int result = 1;
-        for (int i = 0, N = getChildCount(); i < N; i++) {
-            View c = getChildAt(i);
-            if (c.getVisibility() == View.GONE) continue;
-            LayoutParams lp = (LayoutParams) c.getLayoutParams();
-            result = 31 * result + lp.hashCode();
-        }
-        return result;
-    }
-
-    private void consistencyCheck() {
-        if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
-            validateLayoutParams();
-            mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
-        } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
-            mPrinter.println("The fields of some layout parameters were modified in between "
-                    + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
-            invalidateStructure();
-            consistencyCheck();
-        }
-    }
-
-    // Measurement
-
-    // Note: padding has already been removed from the supplied specs
-    private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
-                                          int childWidth, int childHeight) {
-        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
-                getTotalMargin(child, true), childWidth);
-        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
-                getTotalMargin(child, false), childHeight);
-        child.measure(childWidthSpec, childHeightSpec);
-    }
-
-    // Note: padding has already been removed from the supplied specs
-    private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
-        for (int i = 0, N = getChildCount(); i < N; i++) {
-            View c = getChildAt(i);
-            if (c.getVisibility() == View.GONE) continue;
-            LayoutParams lp = getLayoutParams(c);
-            if (firstPass) {
-                measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
-            } else {
-                boolean horizontal = (mOrientation == HORIZONTAL);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                if (spec.getAbsoluteAlignment(horizontal) == FILL) {
-                    Interval span = spec.span;
-                    Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
-                    int[] locations = axis.getLocations();
-                    int cellSize = locations[span.max] - locations[span.min];
-                    int viewSize = cellSize - getTotalMargin(c, horizontal);
-                    if (horizontal) {
-                        measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
-                    } else {
-                        measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
-                    }
-                }
-            }
-        }
-    }
-
-    static int adjust(int measureSpec, int delta) {
-        return makeMeasureSpec(
-                MeasureSpec.getSize(measureSpec + delta),  MeasureSpec.getMode(measureSpec));
-    }
-
-    @Override
-    protected void onMeasure(int widthSpec, int heightSpec) {
-        consistencyCheck();
-
-        /** If we have been called by {@link View#measure(int, int)}, one of width or height
-         *  is  likely to have changed. We must invalidate if so. */
-        invalidateValues();
-
-        int hPadding = getPaddingLeft() + getPaddingRight();
-        int vPadding = getPaddingTop()  + getPaddingBottom();
-
-        int widthSpecSansPadding =  adjust( widthSpec, -hPadding);
-        int heightSpecSansPadding = adjust(heightSpec, -vPadding);
-
-        measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);
-
-        int widthSansPadding;
-        int heightSansPadding;
-
-        // Use the orientation property to decide which axis should be laid out first.
-        if (mOrientation == HORIZONTAL) {
-            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
-            measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
-            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
-        } else {
-            heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
-            measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
-            widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
-        }
-
-        int measuredWidth  = Math.max(widthSansPadding  + hPadding, getSuggestedMinimumWidth());
-        int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
-
-        setMeasuredDimension(
-                View.resolveSizeAndState(measuredWidth, widthSpec, 0),
-                View.resolveSizeAndState(measuredHeight, heightSpec, 0));
-    }
-
-    private int getMeasurement(View c, boolean horizontal) {
-        return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
-    }
-
-    final int getMeasurementIncludingMargin(View c, boolean horizontal) {
-        if (c.getVisibility() == View.GONE) {
-            return 0;
-        }
-        return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
-    }
-
-    @Override
-    public void requestLayout() {
-        super.requestLayout();
-        invalidateStructure();
-    }
-
-    // Layout container
-
-    /**
-     * {@inheritDoc}
-     */
-    /*
-     The layout operation is implemented by delegating the heavy lifting to the
-     to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
-     Together they compute the locations of the vertical and horizontal lines of
-     the grid (respectively!).
-
-     This method is then left with the simpler task of applying margins, gravity
-     and sizing to each child view and then placing it in its cell.
-     */
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        consistencyCheck();
-
-        int targetWidth = right - left;
-        int targetHeight = bottom - top;
-
-        int paddingLeft = getPaddingLeft();
-        int paddingTop = getPaddingTop();
-        int paddingRight = getPaddingRight();
-        int paddingBottom = getPaddingBottom();
-
-        mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
-        mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
-
-        int[] hLocations = mHorizontalAxis.getLocations();
-        int[] vLocations = mVerticalAxis.getLocations();
-
-        for (int i = 0, N = getChildCount(); i < N; i++) {
-            View c = getChildAt(i);
-            if (c.getVisibility() == View.GONE) continue;
-            LayoutParams lp = getLayoutParams(c);
-            Spec columnSpec = lp.columnSpec;
-            Spec rowSpec = lp.rowSpec;
-
-            Interval colSpan = columnSpec.span;
-            Interval rowSpan = rowSpec.span;
-
-            int x1 = hLocations[colSpan.min];
-            int y1 = vLocations[rowSpan.min];
-
-            int x2 = hLocations[colSpan.max];
-            int y2 = vLocations[rowSpan.max];
-
-            int cellWidth = x2 - x1;
-            int cellHeight = y2 - y1;
-
-            int pWidth = getMeasurement(c, true);
-            int pHeight = getMeasurement(c, false);
-
-            Alignment hAlign = columnSpec.getAbsoluteAlignment(true);
-            Alignment vAlign = rowSpec.getAbsoluteAlignment(false);
-
-            Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
-            Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
-
-            // Gravity offsets: the location of the alignment group relative to its cell group.
-            int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
-            int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
-
-            int leftMargin = getMargin(c, true, true);
-            int topMargin = getMargin(c, false, true);
-            int rightMargin = getMargin(c, true, false);
-            int bottomMargin = getMargin(c, false, false);
-
-            int sumMarginsX = leftMargin + rightMargin;
-            int sumMarginsY = topMargin + bottomMargin;
-
-            // Alignment offsets: the location of the view relative to its alignment group.
-            int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
-            int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
-
-            int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
-            int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
-
-            int dx = x1 + gravityOffsetX + alignmentOffsetX;
-
-            int cx = !isLayoutRtlCompat() ? paddingLeft + leftMargin + dx :
-                    targetWidth - width - paddingRight - rightMargin - dx;
-            int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
-
-            if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
-                c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
-            }
-            c.layout(cx, cy, cx + width, cy + height);
-        }
-    }
-
-    // Inner classes
-
-    /*
-     This internal class houses the algorithm for computing the locations of grid lines;
-     along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
-     distinguished by the "horizontal" flag which is true for the horizontal axis and false
-     for the vertical one.
-     */
-    final class Axis {
-        static final int NEW = 0;
-        static final int PENDING = 1;
-        static final int COMPLETE = 2;
-
-        public final boolean horizontal;
-
-        public int definedCount = UNDEFINED;
-        private int maxIndex = UNDEFINED;
-
-        PackedMap<Spec, Bounds> groupBounds;
-        public boolean groupBoundsValid = false;
-
-        PackedMap<Interval, MutableInt> forwardLinks;
-        public boolean forwardLinksValid = false;
-
-        PackedMap<Interval, MutableInt> backwardLinks;
-        public boolean backwardLinksValid = false;
-
-        public int[] leadingMargins;
-        public boolean leadingMarginsValid = false;
-
-        public int[] trailingMargins;
-        public boolean trailingMarginsValid = false;
-
-        public Arc[] arcs;
-        public boolean arcsValid = false;
-
-        public int[] locations;
-        public boolean locationsValid = false;
-
-        public boolean hasWeights;
-        public boolean hasWeightsValid = false;
-        public int[] deltas;
-
-        boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
-
-        private MutableInt parentMin = new MutableInt(0);
-        private MutableInt parentMax = new MutableInt(-MAX_SIZE);
-
-        Axis(boolean horizontal) {
-            this.horizontal = horizontal;
-        }
-
-        private int calculateMaxIndex() {
-            // the number Integer.MIN_VALUE + 1 comes up in undefined cells
-            int result = -1;
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
-                LayoutParams params = getLayoutParams(c);
-                Spec spec = horizontal ? params.columnSpec : params.rowSpec;
-                Interval span = spec.span;
-                result = max(result, span.min);
-                result = max(result, span.max);
-                result = max(result, span.size());
-            }
-            return result == -1 ? UNDEFINED : result;
-        }
-
-        private int getMaxIndex() {
-            if (maxIndex == UNDEFINED) {
-                maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
-            }
-            return maxIndex;
-        }
-
-        public int getCount() {
-            return max(definedCount, getMaxIndex());
-        }
-
-        public void setCount(int count) {
-            if (count != UNDEFINED && count < getMaxIndex()) {
-                handleInvalidParams((horizontal ? "column" : "row") +
-                        "Count must be greater than or equal to the maximum of all grid indices " +
-                        "(and spans) defined in the LayoutParams of each child");
-            }
-            this.definedCount = count;
-        }
-
-        public boolean isOrderPreserved() {
-            return orderPreserved;
-        }
-
-        public void setOrderPreserved(boolean orderPreserved) {
-            this.orderPreserved = orderPreserved;
-            invalidateStructure();
-        }
-
-        private PackedMap<Spec, Bounds> createGroupBounds() {
-            Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
-                // we must include views that are GONE here, see introductory javadoc
-                LayoutParams lp = getLayoutParams(c);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds();
-                assoc.put(spec, bounds);
-            }
-            return assoc.pack();
-        }
-
-        private void computeGroupBounds() {
-            Bounds[] values = groupBounds.values;
-            for (int i = 0; i < values.length; i++) {
-                values[i].reset();
-            }
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
-                // we must include views that are GONE here, see introductory javadoc
-                LayoutParams lp = getLayoutParams(c);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                int size = getMeasurementIncludingMargin(c, horizontal) +
-                                ((spec.weight == 0) ? 0 : getDeltas()[i]);
-                groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
-            }
-        }
-
-        public PackedMap<Spec, Bounds> getGroupBounds() {
-            if (groupBounds == null) {
-                groupBounds = createGroupBounds();
-            }
-            if (!groupBoundsValid) {
-                computeGroupBounds();
-                groupBoundsValid = true;
-            }
-            return groupBounds;
-        }
-
-        // Add values computed by alignment - taking the max of all alignments in each span
-        private PackedMap<Interval, MutableInt> createLinks(boolean min) {
-            Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
-            Spec[] keys = getGroupBounds().keys;
-            for (int i = 0, N = keys.length; i < N; i++) {
-                Interval span = min ? keys[i].span : keys[i].span.inverse();
-                result.put(span, new MutableInt());
-            }
-            return result.pack();
-        }
-
-        private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
-            MutableInt[] spans = links.values;
-            for (int i = 0; i < spans.length; i++) {
-                spans[i].reset();
-            }
-
-            // Use getter to trigger a re-evaluation
-            Bounds[] bounds = getGroupBounds().values;
-            for (int i = 0; i < bounds.length; i++) {
-                int size = bounds[i].size(min);
-                MutableInt valueHolder = links.getValue(i);
-                // this effectively takes the max() of the minima and the min() of the maxima
-                valueHolder.value = max(valueHolder.value, min ? size : -size);
-            }
-        }
-
-        private PackedMap<Interval, MutableInt> getForwardLinks() {
-            if (forwardLinks == null) {
-                forwardLinks = createLinks(true);
-            }
-            if (!forwardLinksValid) {
-                computeLinks(forwardLinks, true);
-                forwardLinksValid = true;
-            }
-            return forwardLinks;
-        }
-
-        private PackedMap<Interval, MutableInt> getBackwardLinks() {
-            if (backwardLinks == null) {
-                backwardLinks = createLinks(false);
-            }
-            if (!backwardLinksValid) {
-                computeLinks(backwardLinks, false);
-                backwardLinksValid = true;
-            }
-            return backwardLinks;
-        }
-
-        private void include(List<Arc> arcs, Interval key, MutableInt size,
-                             boolean ignoreIfAlreadyPresent) {
-            /*
-            Remove self referential links.
-            These appear:
-                . as parental constraints when GridLayout has no children
-                . when components have been marked as GONE
-            */
-            if (key.size() == 0) {
-                return;
-            }
-            // this bit below should really be computed outside here -
-            // its just to stop default (row/col > 0) constraints obliterating valid entries
-            if (ignoreIfAlreadyPresent) {
-                for (Arc arc : arcs) {
-                    Interval span = arc.span;
-                    if (span.equals(key)) {
-                        return;
-                    }
-                }
-            }
-            arcs.add(new Arc(key, size));
-        }
-
-        private void include(List<Arc> arcs, Interval key, MutableInt size) {
-            include(arcs, key, size, true);
-        }
-
-        // Group arcs by their first vertex, returning an array of arrays.
-        // This is linear in the number of arcs.
-        Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
-            int N = getCount() + 1; // the number of vertices
-            Arc[][] result = new Arc[N][];
-            int[] sizes = new int[N];
-            for (Arc arc : arcs) {
-                sizes[arc.span.min]++;
-            }
-            for (int i = 0; i < sizes.length; i++) {
-                result[i] = new Arc[sizes[i]];
-            }
-            // reuse the sizes array to hold the current last elements as we insert each arc
-            Arrays.fill(sizes, 0);
-            for (Arc arc : arcs) {
-                int i = arc.span.min;
-                result[i][sizes[i]++] = arc;
-            }
-
-            return result;
-        }
-
-        private Arc[] topologicalSort(final Arc[] arcs) {
-            return new Object() {
-                Arc[] result = new Arc[arcs.length];
-                int cursor = result.length - 1;
-                Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
-                int[] visited = new int[getCount() + 1];
-
-                void walk(int loc) {
-                    switch (visited[loc]) {
-                        case NEW: {
-                            visited[loc] = PENDING;
-                            for (Arc arc : arcsByVertex[loc]) {
-                                walk(arc.span.max);
-                                result[cursor--] = arc;
-                            }
-                            visited[loc] = COMPLETE;
-                            break;
-                        }
-                        case PENDING: {
-                            // le singe est dans l'arbre
-                            assert false;
-                            break;
-                        }
-                        case COMPLETE: {
-                            break;
-                        }
-                    }
-                }
-
-                Arc[] sort() {
-                    for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
-                        walk(loc);
-                    }
-                    assert cursor == -1;
-                    return result;
-                }
-            }.sort();
-        }
-
-        private Arc[] topologicalSort(List<Arc> arcs) {
-            return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
-        }
-
-        private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
-            for (int i = 0; i < links.keys.length; i++) {
-                Interval key = links.keys[i];
-                include(result, key, links.values[i], false);
-            }
-        }
-
-        private Arc[] createArcs() {
-            List<Arc> mins = new ArrayList<Arc>();
-            List<Arc> maxs = new ArrayList<Arc>();
-
-            // Add the minimum values from the components.
-            addComponentSizes(mins, getForwardLinks());
-            // Add the maximum values from the components.
-            addComponentSizes(maxs, getBackwardLinks());
-
-            // Add ordering constraints to prevent row/col sizes from going negative
-            if (orderPreserved) {
-                // Add a constraint for every row/col
-                for (int i = 0; i < getCount(); i++) {
-                    include(mins, new Interval(i, i + 1), new MutableInt(0));
-                }
-            }
-
-            // Add the container constraints. Use the version of include that allows
-            // duplicate entries in case a child spans the entire grid.
-            int N = getCount();
-            include(mins, new Interval(0, N), parentMin, false);
-            include(maxs, new Interval(N, 0), parentMax, false);
-
-            // Sort
-            Arc[] sMins = topologicalSort(mins);
-            Arc[] sMaxs = topologicalSort(maxs);
-
-            return append(sMins, sMaxs);
-        }
-
-        private void computeArcs() {
-            // getting the links validates the values that are shared by the arc list
-            getForwardLinks();
-            getBackwardLinks();
-        }
-
-        public Arc[] getArcs() {
-            if (arcs == null) {
-                arcs = createArcs();
-            }
-            if (!arcsValid) {
-                computeArcs();
-                arcsValid = true;
-            }
-            return arcs;
-        }
-
-        private boolean relax(int[] locations, Arc entry) {
-            if (!entry.valid) {
-                return false;
-            }
-            Interval span = entry.span;
-            int u = span.min;
-            int v = span.max;
-            int value = entry.value.value;
-            int candidate = locations[u] + value;
-            if (candidate > locations[v]) {
-                locations[v] = candidate;
-                return true;
-            }
-            return false;
-        }
-
-        private void init(int[] locations) {
-            Arrays.fill(locations, 0);
-        }
-
-        private String arcsToString(List<Arc> arcs) {
-            String var = horizontal ? "x" : "y";
-            StringBuilder result = new StringBuilder();
-            boolean first = true;
-            for (Arc arc : arcs) {
-                if (first) {
-                    first = false;
-                } else {
-                    result = result.append(", ");
-                }
-                int src = arc.span.min;
-                int dst = arc.span.max;
-                int value = arc.value.value;
-                result.append((src < dst) ?
-                        var + dst + "-" + var + src + ">=" + value :
-                        var + src + "-" + var + dst + "<=" + -value);
-
-            }
-            return result.toString();
-        }
-
-        private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
-            List<Arc> culprits = new ArrayList<Arc>();
-            List<Arc> removed = new ArrayList<Arc>();
-            for (int c = 0; c < arcs.length; c++) {
-                Arc arc = arcs[c];
-                if (culprits0[c]) {
-                    culprits.add(arc);
-                }
-                if (!arc.valid) {
-                    removed.add(arc);
-                }
-            }
-            mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
-                    " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
-        }
-
-        /*
-        Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
-
-        GridLayout converts its requirements into a system of linear constraints of the
-        form:
-
-        x[i] - x[j] < a[k]
-
-        Where the x[i] are variables and the a[k] are constants.
-
-        For example, if the variables were instead labeled x, y, z we might have:
-
-            x - y < 17
-            y - z < 23
-            z - x < 42
-
-        This is a special case of the Linear Programming problem that is, in turn,
-        equivalent to the single-source shortest paths problem on a digraph, for
-        which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
-        */
-        private boolean solve(Arc[] arcs, int[] locations) {
-            return solve(arcs, locations, true);
-        }
-
-        private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
-            String axisName = horizontal ? "horizontal" : "vertical";
-            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
-            boolean[] originalCulprits = null;
-
-            for (int p = 0; p < arcs.length; p++) {
-                init(locations);
-
-                // We take one extra pass over traditional Bellman-Ford (and omit their final step)
-                for (int i = 0; i < N; i++) {
-                    boolean changed = false;
-                    for (int j = 0, length = arcs.length; j < length; j++) {
-                        changed |= relax(locations, arcs[j]);
-                    }
-                    if (!changed) {
-                        if (originalCulprits != null) {
-                            logError(axisName, arcs, originalCulprits);
-                        }
-                        return true;
-                    }
-                }
-
-                if (!modifyOnError) {
-                    return false; // cannot solve with these constraints
-                }
-
-                boolean[] culprits = new boolean[arcs.length];
-                for (int i = 0; i < N; i++) {
-                    for (int j = 0, length = arcs.length; j < length; j++) {
-                        culprits[j] |= relax(locations, arcs[j]);
-                    }
-                }
-
-                if (p == 0) {
-                    originalCulprits = culprits;
-                }
-
-                for (int i = 0; i < arcs.length; i++) {
-                    if (culprits[i]) {
-                        Arc arc = arcs[i];
-                        // Only remove max values, min values alone cannot be inconsistent
-                        if (arc.span.min < arc.span.max) {
-                            continue;
-                        }
-                        arc.valid = false;
-                        break;
-                    }
-                }
-            }
-            return true;
-        }
-
-        private void computeMargins(boolean leading) {
-            int[] margins = leading ? leadingMargins : trailingMargins;
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
-                if (c.getVisibility() == View.GONE) continue;
-                LayoutParams lp = getLayoutParams(c);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                Interval span = spec.span;
-                int index = leading ? span.min : span.max;
-                margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
-            }
-        }
-
-        // External entry points
-
-        public int[] getLeadingMargins() {
-            if (leadingMargins == null) {
-                leadingMargins = new int[getCount() + 1];
-            }
-            if (!leadingMarginsValid) {
-                computeMargins(true);
-                leadingMarginsValid = true;
-            }
-            return leadingMargins;
-        }
-
-        public int[] getTrailingMargins() {
-            if (trailingMargins == null) {
-                trailingMargins = new int[getCount() + 1];
-            }
-            if (!trailingMarginsValid) {
-                computeMargins(false);
-                trailingMarginsValid = true;
-            }
-            return trailingMargins;
-        }
-
-        private boolean solve(int[] a) {
-            return solve(getArcs(), a);
-        }
-
-        private boolean computeHasWeights() {
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                final View child = getChildAt(i);
-                if (child.getVisibility() == View.GONE) {
-                    continue;
-                }
-                LayoutParams lp = getLayoutParams(child);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                if (spec.weight != 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private boolean hasWeights() {
-            if (!hasWeightsValid) {
-                hasWeights = computeHasWeights();
-                hasWeightsValid = true;
-            }
-            return hasWeights;
-        }
-
-        public int[] getDeltas() {
-            if (deltas == null) {
-                deltas = new int[getChildCount()];
-            }
-            return deltas;
-        }
-
-        private void shareOutDelta(int totalDelta, float totalWeight) {
-            Arrays.fill(deltas, 0);
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                final View c = getChildAt(i);
-                if (c.getVisibility() == View.GONE) {
-                    continue;
-                }
-                LayoutParams lp = getLayoutParams(c);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                float weight = spec.weight;
-                if (weight != 0) {
-                    int delta = Math.round((weight * totalDelta / totalWeight));
-                    deltas[i] = delta;
-                    // the two adjustments below are to counter the above rounding and avoid
-                    // off-by-ones at the end
-                    totalDelta -= delta;
-                    totalWeight -= weight;
-                }
-            }
-        }
-
-        private void solveAndDistributeSpace(int[] a) {
-            Arrays.fill(getDeltas(), 0);
-            solve(a);
-            int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
-            if (deltaMax < 2) {
-                return; //don't have any delta to distribute
-            }
-            int deltaMin = 0; //inclusive
-
-            float totalWeight = calculateTotalWeight();
-
-            int validDelta = -1; //delta for which a solution exists
-            boolean validSolution = true;
-            // do a binary search to find the max delta that won't conflict with constraints
-            while(deltaMin < deltaMax) {
-                // cast to long to prevent overflow.
-                final int delta = (int)(((long)deltaMin + deltaMax) / 2);
-                invalidateValues();
-                shareOutDelta(delta, totalWeight);
-                validSolution = solve(getArcs(), a, false);
-                if (validSolution) {
-                    validDelta = delta;
-                    deltaMin = delta + 1;
-                } else {
-                    deltaMax = delta;
-                }
-            }
-            if (validDelta > 0 && !validSolution) {
-                // last solution was not successful but we have a successful one. Use it.
-                invalidateValues();
-                shareOutDelta(validDelta, totalWeight);
-                solve(a);
-            }
-        }
-
-        private float calculateTotalWeight() {
-            float totalWeight = 0f;
-            for (int i = 0, N = getChildCount(); i < N; i++) {
-                View c = getChildAt(i);
-                if (c.getVisibility() == View.GONE) {
-                    continue;
-                }
-                LayoutParams lp = getLayoutParams(c);
-                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
-                totalWeight += spec.weight;
-            }
-            return totalWeight;
-        }
-
-        private void computeLocations(int[] a) {
-            if (!hasWeights()) {
-                solve(a);
-            } else {
-                solveAndDistributeSpace(a);
-            }
-            if (!orderPreserved) {
-                // Solve returns the smallest solution to the constraint system for which all
-                // values are positive. One value is therefore zero - though if the row/col
-                // order is not preserved this may not be the first vertex. For consistency,
-                // translate all the values so that they measure the distance from a[0]; the
-                // leading edge of the parent. After this transformation some values may be
-                // negative.
-                int a0 = a[0];
-                for (int i = 0, N = a.length; i < N; i++) {
-                    a[i] = a[i] - a0;
-                }
-            }
-        }
-
-        public int[] getLocations() {
-            if (locations == null) {
-                int N = getCount() + 1;
-                locations = new int[N];
-            }
-            if (!locationsValid) {
-                computeLocations(locations);
-                locationsValid = true;
-            }
-            return locations;
-        }
-
-        private int size(int[] locations) {
-            // The parental edges are attached to vertices 0 and N - even when order is not
-            // being preserved and other vertices fall outside this range. Measure the distance
-            // between vertices 0 and N, assuming that locations[0] = 0.
-            return locations[getCount()];
-        }
-
-        private void setParentConstraints(int min, int max) {
-            parentMin.value = min;
-            parentMax.value = -max;
-            locationsValid = false;
-        }
-
-        private int getMeasure(int min, int max) {
-            setParentConstraints(min, max);
-            return size(getLocations());
-        }
-
-        public int getMeasure(int measureSpec) {
-            int mode = MeasureSpec.getMode(measureSpec);
-            int size = MeasureSpec.getSize(measureSpec);
-            switch (mode) {
-                case MeasureSpec.UNSPECIFIED: {
-                    return getMeasure(0, MAX_SIZE);
-                }
-                case MeasureSpec.EXACTLY: {
-                    return getMeasure(size, size);
-                }
-                case MeasureSpec.AT_MOST: {
-                    return getMeasure(0, size);
-                }
-                default: {
-                    assert false;
-                    return 0;
-                }
-            }
-        }
-
-        public void layout(int size) {
-            setParentConstraints(size, size);
-            getLocations();
-        }
-
-        public void invalidateStructure() {
-            maxIndex = UNDEFINED;
-
-            groupBounds = null;
-            forwardLinks = null;
-            backwardLinks = null;
-
-            leadingMargins = null;
-            trailingMargins = null;
-            arcs = null;
-
-            locations = null;
-
-            deltas = null;
-            hasWeightsValid = false;
-
-            invalidateValues();
-        }
-
-        public void invalidateValues() {
-            groupBoundsValid = false;
-            forwardLinksValid = false;
-            backwardLinksValid = false;
-
-            leadingMarginsValid = false;
-            trailingMarginsValid = false;
-            arcsValid = false;
-
-            locationsValid = false;
-        }
-    }
-
-    /**
-     * Layout information associated with each of the children of a GridLayout.
-     * <p>
-     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
-     * each cell group. The fundamental parameters associated with each cell group are
-     * gathered into their vertical and horizontal components and stored
-     * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
-     * {@link GridLayout.Spec Specs} are immutable structures
-     * and may be shared between the layout parameters of different children.
-     * <p>
-     * The row and column specs contain the leading and trailing indices along each axis
-     * and together specify the four grid indices that delimit the cells of this cell group.
-     * <p>
-     * The  alignment properties of the row and column specs together specify
-     * both aspects of alignment within the cell group. It is also possible to specify a child's
-     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
-     * method.
-     * <p>
-     * The weight property is also included in Spec and specifies the proportion of any
-     * excess space that is due to the associated view.
-     *
-     * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
-     *
-     * Because the default values of the {@link #width} and {@link #height}
-     * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
-     * declared in the layout parameters of GridLayout's children. In addition,
-     * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
-     * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
-     * instead controlled by the principle of <em>flexibility</em>,
-     * as discussed in {@link GridLayout}.
-     *
-     * <h4>Summary</h4>
-     *
-     * You should not need to use either of the special size values:
-     * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
-     * a GridLayout.
-     *
-     * <h4>Default values</h4>
-     *
-     * <ul>
-     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
-     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
-     *     <li>{@link #topMargin} = 0 when
-     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          {@code false}; otherwise {@link #UNDEFINED}, to
-     *          indicate that a default value should be computed on demand. </li>
-     *     <li>{@link #leftMargin} = 0 when
-     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          {@code false}; otherwise {@link #UNDEFINED}, to
-     *          indicate that a default value should be computed on demand. </li>
-     *     <li>{@link #bottomMargin} = 0 when
-     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          {@code false}; otherwise {@link #UNDEFINED}, to
-     *          indicate that a default value should be computed on demand. </li>
-     *     <li>{@link #rightMargin} = 0 when
-     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          {@code false}; otherwise {@link #UNDEFINED}, to
-     *          indicate that a default value should be computed on demand. </li>
-     *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
-     *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
-     *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
-     *     <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
-     *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
-     *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
-     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
-     *     <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
-     * </ul>
-     *
-     * See {@link GridLayout} for a more complete description of the conventions
-     * used by GridLayout in the interpretation of the properties of this class.
-     *
-     * @attr name android:row
-     * @attr name android:rowSpan
-     * @attr name android:rowWeight
-     * @attr name android:column
-     * @attr name android:columnSpan
-     * @attr name android:columnWeight
-     * @attr name android:gravity
-     */
-    public static class LayoutParams extends MarginLayoutParams {
-
-        // Default values
-
-        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
-        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
-        private static final int DEFAULT_MARGIN = UNDEFINED;
-        private static final int DEFAULT_ROW = UNDEFINED;
-        private static final int DEFAULT_COLUMN = UNDEFINED;
-        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
-        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
-
-        // TypedArray indices
-
-        private static final int MARGIN = R.styleable.GridLayout_Layout_android_layout_margin;
-        private static final int LEFT_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginLeft;
-        private static final int TOP_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginTop;
-        private static final int RIGHT_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginRight;
-        private static final int BOTTOM_MARGIN = R.styleable.GridLayout_Layout_android_layout_marginBottom;
-
-        private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
-        private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
-        private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
-
-        private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
-        private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
-        private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
-
-        private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
-
-        // Instance variables
-
-        /**
-         * The spec that defines the vertical characteristics of the cell group
-         * described by these layout parameters.
-         * If an assignment is made to this field after a measurement or layout operation
-         * has already taken place, a call to
-         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
-         * must be made to notify GridLayout of the change. GridLayout is normally able
-         * to detect when code fails to observe this rule, issue a warning and take steps to
-         * compensate for the omission. This facility is implemented on a best effort basis
-         * and should not be relied upon in production code - so it is best to include the above
-         * calls to remove the warnings as soon as it is practical.
-         */
-        public Spec rowSpec = Spec.UNDEFINED;
-
-        /**
-         * The spec that defines the horizontal characteristics of the cell group
-         * described by these layout parameters.
-         * If an assignment is made to this field after a measurement or layout operation
-         * has already taken place, a call to
-         * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
-         * must be made to notify GridLayout of the change. GridLayout is normally able
-         * to detect when code fails to observe this rule, issue a warning and take steps to
-         * compensate for the omission. This facility is implemented on a best effort basis
-         * and should not be relied upon in production code - so it is best to include the above
-         * calls to remove the warnings as soon as it is practical.
-         */
-        public Spec columnSpec = Spec.UNDEFINED;
-
-        // Constructors
-
-        private LayoutParams(
-                int width, int height,
-                int left, int top, int right, int bottom,
-                Spec rowSpec, Spec columnSpec) {
-            super(width, height);
-            setMargins(left, top, right, bottom);
-            this.rowSpec = rowSpec;
-            this.columnSpec = columnSpec;
-        }
-
-        /**
-         * Constructs a new LayoutParams instance for this <code>rowSpec</code>
-         * and <code>columnSpec</code>. All other fields are initialized with
-         * default values as defined in {@link LayoutParams}.
-         *
-         * @param rowSpec    the rowSpec
-         * @param columnSpec the columnSpec
-         */
-        public LayoutParams(Spec rowSpec, Spec columnSpec) {
-            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
-                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
-                    rowSpec, columnSpec);
-        }
-
-        /**
-         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
-         */
-        public LayoutParams() {
-            this(Spec.UNDEFINED, Spec.UNDEFINED);
-        }
-
-        // Copying constructors
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.LayoutParams params) {
-            super(params);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(MarginLayoutParams params) {
-            super(params);
-        }
-
-        /**
-         * Copy constructor. Clones the width, height, margin values, row spec,
-         * and column spec of the source.
-         *
-         * @param source The layout params to copy from.
-         */
-        public LayoutParams(LayoutParams source) {
-            super(source);
-
-            this.rowSpec = source.rowSpec;
-            this.columnSpec = source.columnSpec;
-        }
-
-        // AttributeSet constructors
-
-        /**
-         * {@inheritDoc}
-         *
-         * Values not defined in the attribute set take the default values
-         * defined in {@link LayoutParams}.
-         */
-        public LayoutParams(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            reInitSuper(context, attrs);
-            init(context, attrs);
-        }
-
-        // Implementation
-
-        // Reinitialise the margins using a different default policy than MarginLayoutParams.
-        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
-        // so that a layout manager default can be accessed post set up. We need this as, at the
-        // point of installation, we do not know how many rows/cols there are and therefore
-        // which elements are positioned next to the container's trailing edges. We need to
-        // know this as margins around the container's boundary should have different
-        // defaults to those between peers.
-
-        // This method could be parametrized and moved into MarginLayout.
-        private void reInitSuper(Context context, AttributeSet attrs) {
-            TypedArray a =
-                    context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
-            try {
-                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
-
-                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
-                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
-                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
-                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
-            } finally {
-                a.recycle();
-            }
-        }
-
-        private void init(Context context, AttributeSet attrs) {
-            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
-            try {
-                int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
-
-                int column = a.getInt(COLUMN, DEFAULT_COLUMN);
-                int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
-                float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
-                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
-
-                int row = a.getInt(ROW, DEFAULT_ROW);
-                int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
-                float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
-                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
-            } finally {
-                a.recycle();
-            }
-        }
-
-        /**
-         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
-         * See {@link Gravity}.
-         *
-         * @param gravity the new gravity value
-         *
-         * @attr name android:gravity
-         */
-        public void setGravity(int gravity) {
-            rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
-            columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
-        }
-
-        @Override
-        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
-            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
-            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
-        }
-
-        final void setRowSpecSpan(Interval span) {
-            rowSpec = rowSpec.copyWriteSpan(span);
-        }
-
-        final void setColumnSpecSpan(Interval span) {
-            columnSpec = columnSpec.copyWriteSpan(span);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-
-            LayoutParams that = (LayoutParams) o;
-
-            if (!columnSpec.equals(that.columnSpec)) return false;
-            if (!rowSpec.equals(that.rowSpec)) return false;
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = rowSpec.hashCode();
-            result = 31 * result + columnSpec.hashCode();
-            return result;
-        }
-    }
-
-    /*
-    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
-    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
-     */
-    final static class Arc {
-        public final Interval span;
-        public final MutableInt value;
-        public boolean valid = true;
-
-        public Arc(Interval span, MutableInt value) {
-            this.span = span;
-            this.value = value;
-        }
-
-        @Override
-        public String toString() {
-            return span + " " + (!valid ? "+>" : "->") + " " + value;
-        }
-    }
-
-    // A mutable Integer - used to avoid heap allocation during the layout operation
-
-    final static class MutableInt {
-        public int value;
-
-        public MutableInt() {
-            reset();
-        }
-
-        public MutableInt(int value) {
-            this.value = value;
-        }
-
-        public void reset() {
-            value = Integer.MIN_VALUE;
-        }
-
-        @Override
-        public String toString() {
-            return Integer.toString(value);
-        }
-    }
-
-    final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
-        private final Class<K> keyType;
-        private final Class<V> valueType;
-
-        private Assoc(Class<K> keyType, Class<V> valueType) {
-            this.keyType = keyType;
-            this.valueType = valueType;
-        }
-
-        public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
-            return new Assoc<K, V>(keyType, valueType);
-        }
-
-        public void put(K key, V value) {
-            add(Pair.create(key, value));
-        }
-
-        @SuppressWarnings(value = "unchecked")
-        public PackedMap<K, V> pack() {
-            int N = size();
-            K[] keys = (K[]) Array.newInstance(keyType, N);
-            V[] values = (V[]) Array.newInstance(valueType, N);
-            for (int i = 0; i < N; i++) {
-                keys[i] = get(i).first;
-                values[i] = get(i).second;
-            }
-            return new PackedMap<K, V>(keys, values);
-        }
-    }
-
-    /*
-    This data structure is used in place of a Map where we have an index that refers to the order
-    in which each key/value pairs were added to the map. In this case we store keys and values
-    in arrays of a length that is equal to the number of unique keys. We also maintain an
-    array of indexes from insertion order to the compacted arrays of keys and values.
-
-    Note that behavior differs from that of a LinkedHashMap in that repeated entries
-    *do* get added multiples times. So the length of index is equals to the number of
-    items added.
-
-    This is useful in the GridLayout class where we can rely on the order of children not
-    changing during layout - to use integer-based lookup for our internal structures
-    rather than using (and storing) an implementation of Map<Key, ?>.
-     */
-    @SuppressWarnings(value = "unchecked")
-    final static class PackedMap<K, V> {
-        public final int[] index;
-        public final K[] keys;
-        public final V[] values;
-
-        PackedMap(K[] keys, V[] values) {
-            this.index = createIndex(keys);
-
-            this.keys = compact(keys, index);
-            this.values = compact(values, index);
-        }
-
-        public V getValue(int i) {
-            return values[index[i]];
-        }
-
-        private static <K> int[] createIndex(K[] keys) {
-            int size = keys.length;
-            int[] result = new int[size];
-
-            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
-            for (int i = 0; i < size; i++) {
-                K key = keys[i];
-                Integer index = keyToIndex.get(key);
-                if (index == null) {
-                    index = keyToIndex.size();
-                    keyToIndex.put(key, index);
-                }
-                result[i] = index;
-            }
-            return result;
-        }
-
-        /*
-        Create a compact array of keys or values using the supplied index.
-         */
-        private static <K> K[] compact(K[] a, int[] index) {
-            int size = a.length;
-            Class<?> componentType = a.getClass().getComponentType();
-            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
-
-            // this overwrite duplicates, retaining the last equivalent entry
-            for (int i = 0; i < size; i++) {
-                result[index[i]] = a[i];
-            }
-            return result;
-        }
-    }
-
-    /*
-    For each group (with a given alignment) we need to store the amount of space required
-    before the alignment point and the amount of space required after it. One side of this
-    calculation is always 0 for START and END alignments but we don't make use of this.
-    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
-    simple optimisations are possible.
-
-    The general algorithm therefore is to create a Map (actually a PackedMap) from
-    group to Bounds and to loop through all Views in the group taking the maximum
-    of the values for each View.
-    */
-    static class Bounds {
-        public int before;
-        public int after;
-        public int flexibility; // we're flexible iff all included specs are flexible
-
-        Bounds() {
-            reset();
-        }
-
-        protected void reset() {
-            before = Integer.MIN_VALUE;
-            after = Integer.MIN_VALUE;
-            flexibility = CAN_STRETCH; // from the above, we're flexible when empty
-        }
-
-        protected void include(int before, int after) {
-            this.before = max(this.before, before);
-            this.after = max(this.after, after);
-        }
-
-        protected int size(boolean min) {
-            if (!min) {
-                if (canStretch(flexibility)) {
-                    return MAX_SIZE;
-                }
-            }
-            return before + after;
-        }
-
-        protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
-            return before - a.getAlignmentValue(c, size, ViewGroupCompat.getLayoutMode(gl));
-        }
-
-        protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
-            this.flexibility &= spec.getFlexibility();
-            boolean horizontal = axis.horizontal;
-            Alignment alignment = spec.getAbsoluteAlignment(horizontal);
-            // todo test this works correctly when the returned value is UNDEFINED
-            int before = alignment.getAlignmentValue(c, size, ViewGroupCompat.getLayoutMode(gl));
-            include(before, size - before);
-        }
-
-        @Override
-        public String toString() {
-            return "Bounds{" +
-                    "before=" + before +
-                    ", after=" + after +
-                    '}';
-        }
-    }
-
-    /**
-     * An Interval represents a contiguous range of values that lie between
-     * the interval's {@link #min} and {@link #max} values.
-     * <p>
-     * Intervals are immutable so may be passed as values and used as keys in hash tables.
-     * It is not necessary to have multiple instances of Intervals which have the same
-     * {@link #min} and {@link #max} values.
-     * <p>
-     * Intervals are often written as {@code [min, max]} and represent the set of values
-     * {@code x} such that {@code min <= x < max}.
-     */
-    final static class Interval {
-        /**
-         * The minimum value.
-         */
-        public final int min;
-
-        /**
-         * The maximum value.
-         */
-        public final int max;
-
-        /**
-         * Construct a new Interval, {@code interval}, where:
-         * <ul>
-         *     <li> {@code interval.min = min} </li>
-         *     <li> {@code interval.max = max} </li>
-         * </ul>
-         *
-         * @param min the minimum value.
-         * @param max the maximum value.
-         */
-        public Interval(int min, int max) {
-            this.min = min;
-            this.max = max;
-        }
-
-        int size() {
-            return max - min;
-        }
-
-        Interval inverse() {
-            return new Interval(max, min);
-        }
-
-        /**
-         * Returns {@code true} if the {@link #getClass class},
-         * {@link #min} and {@link #max} properties of this Interval and the
-         * supplied parameter are pairwise equal; {@code false} otherwise.
-         *
-         * @param that the object to compare this interval with
-         *
-         * @return {@code true} if the specified object is equal to this
-         *         {@code Interval}, {@code false} otherwise.
-         */
-        @Override
-        public boolean equals(Object that) {
-            if (this == that) {
-                return true;
-            }
-            if (that == null || getClass() != that.getClass()) {
-                return false;
-            }
-
-            Interval interval = (Interval) that;
-
-            if (max != interval.max) {
-                return false;
-            }
-            //noinspection RedundantIfStatement
-            if (min != interval.min) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = min;
-            result = 31 * result + max;
-            return result;
-        }
-
-        @Override
-        public String toString() {
-            return "[" + min + ", " + max + "]";
-        }
-    }
-
-    /**
-     * A Spec defines the horizontal or vertical characteristics of a group of
-     * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
-     * along the appropriate axis.
-     * <p>
-     * The <em>grid indices</em> are the leading and trailing edges of this cell group.
-     * See {@link GridLayout} for a description of the conventions used by GridLayout
-     * for grid indices.
-     * <p>
-     * The <em>alignment</em> property specifies how cells should be aligned in this group.
-     * For row groups, this specifies the vertical alignment.
-     * For column groups, this specifies the horizontal alignment.
-     * <p>
-     * Use the following static methods to create specs:
-     * <ul>
-     *   <li>{@link #spec(int)}</li>
-     *   <li>{@link #spec(int, int)}</li>
-     *   <li>{@link #spec(int, Alignment)}</li>
-     *   <li>{@link #spec(int, int, Alignment)}</li>
-     *   <li>{@link #spec(int, float)}</li>
-     *   <li>{@link #spec(int, int, float)}</li>
-     *   <li>{@link #spec(int, Alignment, float)}</li>
-     *   <li>{@link #spec(int, int, Alignment, float)}</li>
-     * </ul>
-     *
-     */
-    public static class Spec {
-        static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
-        static final float DEFAULT_WEIGHT = 0;
-
-        final boolean startDefined;
-        final Interval span;
-        final Alignment alignment;
-        final float weight;
-
-        private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
-            this.startDefined = startDefined;
-            this.span = span;
-            this.alignment = alignment;
-            this.weight = weight;
-        }
-
-        Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
-            this(startDefined, new Interval(start, start + size), alignment, weight);
-        }
-
-        public Alignment getAbsoluteAlignment(boolean horizontal) {
-            if (alignment != UNDEFINED_ALIGNMENT) {
-                return alignment;
-            }
-            if (weight == 0f) {
-                return horizontal ? START : BASELINE;
-            }
-            return FILL;
-        }
-
-        final Spec copyWriteSpan(Interval span) {
-            return new Spec(startDefined, span, alignment, weight);
-        }
-
-        final Spec copyWriteAlignment(Alignment alignment) {
-            return new Spec(startDefined, span, alignment, weight);
-        }
-
-        final int getFlexibility() {
-            return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
-        }
-
-        /**
-         * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
-         * properties of this Spec and the supplied parameter are pairwise equal,
-         * {@code false} otherwise.
-         *
-         * @param that the object to compare this spec with
-         *
-         * @return {@code true} if the specified object is equal to this
-         *         {@code Spec}; {@code false} otherwise
-         */
-        @Override
-        public boolean equals(Object that) {
-            if (this == that) {
-                return true;
-            }
-            if (that == null || getClass() != that.getClass()) {
-                return false;
-            }
-
-            Spec spec = (Spec) that;
-
-            if (!alignment.equals(spec.alignment)) {
-                return false;
-            }
-            //noinspection RedundantIfStatement
-            if (!span.equals(spec.span)) {
-                return false;
-            }
-
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = span.hashCode();
-            result = 31 * result + alignment.hashCode();
-            return result;
-        }
-    }
-
-    /**
-     * Return a Spec, {@code spec}, where:
-     * <ul>
-     *     <li> {@code spec.span = [start, start + size]} </li>
-     *     <li> {@code spec.alignment = alignment} </li>
-     *     <li> {@code spec.weight = weight} </li>
-     * </ul>
-     * <p>
-     * To leave the start index undefined, use the value {@link #UNDEFINED}.
-     *
-     * @param start     the start
-     * @param size      the size
-     * @param alignment the alignment
-     * @param weight    the weight
-     */
-    public static Spec spec(int start, int size, Alignment alignment, float weight) {
-        return new Spec(start != UNDEFINED, start, size, alignment, weight);
-    }
-
-    /**
-     * Equivalent to: {@code spec(start, 1, alignment, weight)}.
-     *
-     * @param start     the start
-     * @param alignment the alignment
-     * @param weight    the weight
-     */
-    public static Spec spec(int start, Alignment alignment, float weight) {
-        return spec(start, 1, alignment, weight);
-    }
-
-    /**
-     * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
-     * where {@code default_alignment} is specified in
-     * {@link android.widget.GridLayout.LayoutParams}.
-     *
-     * @param start  the start
-     * @param size   the size
-     * @param weight the weight
-     */
-    public static Spec spec(int start, int size, float weight) {
-        return spec(start, size, UNDEFINED_ALIGNMENT, weight);
-    }
-
-    /**
-     * Equivalent to: {@code spec(start, 1, weight)}.
-     *
-     * @param start  the start
-     * @param weight the weight
-     */
-    public static Spec spec(int start, float weight) {
-        return spec(start, 1, weight);
-    }
-
-    /**
-     * Equivalent to: {@code spec(start, size, alignment, 0f)}.
-     *
-     * @param start     the start
-     * @param size      the size
-     * @param alignment the alignment
-     */
-    public static Spec spec(int start, int size, Alignment alignment) {
-        return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
-    }
-
-    /**
-     * Return a Spec, {@code spec}, where:
-     * <ul>
-     *     <li> {@code spec.span = [start, start + 1]} </li>
-     *     <li> {@code spec.alignment = alignment} </li>
-     * </ul>
-     * <p>
-     * To leave the start index undefined, use the value {@link #UNDEFINED}.
-     *
-     * @param start     the start index
-     * @param alignment the alignment
-     *
-     * @see #spec(int, int, Alignment)
-     */
-    public static Spec spec(int start, Alignment alignment) {
-        return spec(start, 1, alignment);
-    }
-
-    /**
-     * Return a Spec, {@code spec}, where:
-     * <ul>
-     *     <li> {@code spec.span = [start, start + size]} </li>
-     * </ul>
-     * <p>
-     * To leave the start index undefined, use the value {@link #UNDEFINED}.
-     *
-     * @param start     the start
-     * @param size      the size
-     *
-     * @see #spec(int, Alignment)
-     */
-    public static Spec spec(int start, int size) {
-        return spec(start, size, UNDEFINED_ALIGNMENT);
-    }
-
-    /**
-     * Return a Spec, {@code spec}, where:
-     * <ul>
-     *     <li> {@code spec.span = [start, start + 1]} </li>
-     * </ul>
-     * <p>
-     * To leave the start index undefined, use the value {@link #UNDEFINED}.
-     *
-     * @param start     the start index
-     *
-     * @see #spec(int, int)
-     */
-    public static Spec spec(int start) {
-        return spec(start, 1);
-    }
-
-    /**
-     * Alignments specify where a view should be placed within a cell group and
-     * what size it should be.
-     * <p>
-     * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
-     * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
-     * {@code alignment}. Overall placement of the view in the cell
-     * group is specified by the two alignments which act along each axis independently.
-     * <p>
-     *  The GridLayout class defines the most common alignments used in general layout:
-     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
-     * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
-     */
-    /*
-     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
-     * to return the appropriate value for the type of alignment being defined.
-     * The enclosing algorithms position the children
-     * so that the locations defined by the alignment values
-     * are the same for all of the views in a group.
-     * <p>
-     */
-    public static abstract class Alignment {
-        Alignment() {
-        }
-
-        abstract int getGravityOffset(View view, int cellDelta);
-
-        /**
-         * Returns an alignment value. In the case of vertical alignments the value
-         * returned should indicate the distance from the top of the view to the
-         * alignment location.
-         * For horizontal alignments measurement is made from the left edge of the component.
-         *
-         * @param view              the view to which this alignment should be applied
-         * @param viewSize          the measured size of the view
-         * @param mode              the basis of alignment: CLIP or OPTICAL
-         * @return the alignment value
-         */
-        abstract int getAlignmentValue(View view, int viewSize, int mode);
-
-        /**
-         * Returns the size of the view specified by this alignment.
-         * In the case of vertical alignments this method should return a height; for
-         * horizontal alignments this method should return the width.
-         * <p>
-         * The default implementation returns {@code viewSize}.
-         *
-         * @param view              the view to which this alignment should be applied
-         * @param viewSize          the measured size of the view
-         * @param cellSize          the size of the cell into which this view will be placed
-         * @return the aligned size
-         */
-        int getSizeInCell(View view, int viewSize, int cellSize) {
-            return viewSize;
-        }
-
-        Bounds getBounds() {
-            return new Bounds();
-        }
-
-        abstract String getDebugString();
-
-        @Override
-        public String toString() {
-            return "Alignment:" + getDebugString();
-        }
-    }
-
-    static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
-        @Override
-        int getGravityOffset(View view, int cellDelta) {
-            return UNDEFINED;
-        }
-
-        @Override
-        public int getAlignmentValue(View view, int viewSize, int mode) {
-            return UNDEFINED;
-        }
-
-        @Override
-        String getDebugString() {
-            return "UNDEFINED";
-        }
-    };
-
-    /**
-     * Indicates that a view should be aligned with the <em>start</em>
-     * edges of the other views in its cell group.
-     */
-    private static final Alignment LEADING = new Alignment() {
-        @Override
-        int getGravityOffset(View view, int cellDelta) {
-            return 0;
-        }
-
-        @Override
-        public int getAlignmentValue(View view, int viewSize, int mode) {
-            return 0;
-        }
-
-        @Override
-        String getDebugString() {
-            return "LEADING";
-        }
-    };
-
-    /**
-     * Indicates that a view should be aligned with the <em>end</em>
-     * edges of the other views in its cell group.
-     */
-    private static final Alignment TRAILING = new Alignment() {
-        @Override
-        int getGravityOffset(View view, int cellDelta) {
-            return cellDelta;
-        }
-
-        @Override
-        public int getAlignmentValue(View view, int viewSize, int mode) {
-            return viewSize;
-        }
-
-        @Override
-        String getDebugString() {
-            return "TRAILING";
-        }
-    };
-
-    /**
-     * Indicates that a view should be aligned with the <em>top</em>
-     * edges of the other views in its cell group.
-     */
-    public static final Alignment TOP = LEADING;
-
-    /**
-     * Indicates that a view should be aligned with the <em>bottom</em>
-     * edges of the other views in its cell group.
-     */
-    public static final Alignment BOTTOM = TRAILING;
-
-    /**
-     * Indicates that a view should be aligned with the <em>start</em>
-     * edges of the other views in its cell group.
-     */
-    public static final Alignment START = LEADING;
-
-    /**
-     * Indicates that a view should be aligned with the <em>end</em>
-     * edges of the other views in its cell group.
-     */
-    public static final Alignment END = TRAILING;
-
-    private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
-        return new Alignment() {
-            @Override
-            int getGravityOffset(View view, int cellDelta) {
-                boolean isLayoutRtl = ViewCompat.getLayoutDirection(view) ==
-                        ViewCompat.LAYOUT_DIRECTION_RTL;
-                return (!isLayoutRtl ? ltr : rtl).getGravityOffset(view, cellDelta);
-            }
-
-            @Override
-            public int getAlignmentValue(View view, int viewSize, int mode) {
-                boolean isLayoutRtl = ViewCompat.getLayoutDirection(view) ==
-                        ViewCompat.LAYOUT_DIRECTION_RTL;
-                return (!isLayoutRtl ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
-            }
-
-            @Override
-            String getDebugString() {
-                return "SWITCHING[L:" + ltr.getDebugString() + ", R:" + rtl.getDebugString() + "]";
-            }
-        };
-    }
-
-    /**
-     * Indicates that a view should be aligned with the <em>left</em>
-     * edges of the other views in its cell group.
-     */
-    public static final Alignment LEFT = createSwitchingAlignment(START, END);
-
-    /**
-     * Indicates that a view should be aligned with the <em>right</em>
-     * edges of the other views in its cell group.
-     */
-    public static final Alignment RIGHT = createSwitchingAlignment(END, START);
-
-    /**
-     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
-     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
-     * LayoutParams#columnSpec columnSpecs}.
-     */
-    public static final Alignment CENTER = new Alignment() {
-        @Override
-        int getGravityOffset(View view, int cellDelta) {
-            return cellDelta >> 1;
-        }
-
-        @Override
-        public int getAlignmentValue(View view, int viewSize, int mode) {
-            return viewSize >> 1;
-        }
-
-        @Override
-        String getDebugString() {
-            return "CENTER";
-        }
-    };
-
-    /**
-     * Indicates that a view should be aligned with the <em>baselines</em>
-     * of the other views in its cell group.
-     * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
-     *
-     * @see View#getBaseline()
-     */
-    public static final Alignment BASELINE = new Alignment() {
-        @Override
-        int getGravityOffset(View view, int cellDelta) {
-            return 0; // baseline gravity is top
-        }
-
-        @Override
-        public int getAlignmentValue(View view, int viewSize, int mode) {
-            if (view.getVisibility() == GONE) {
-                return 0;
-            }
-            int baseline = view.getBaseline();
-            return baseline == -1 ? UNDEFINED : baseline;
-        }
-
-        @Override
-        public Bounds getBounds() {
-            return new Bounds() {
-                /*
-                In a baseline aligned row in which some components define a baseline
-                and some don't, we need a third variable to properly account for all
-                the sizes. This tracks the maximum size of all the components -
-                including those that don't define a baseline.
-                */
-                private int size;
-
-                @Override
-                protected void reset() {
-                    super.reset();
-                    size = Integer.MIN_VALUE;
-                }
-
-                @Override
-                protected void include(int before, int after) {
-                    super.include(before, after);
-                    size = max(size, before + after);
-                }
-
-                @Override
-                protected int size(boolean min) {
-                    return max(super.size(min), size);
-                }
-
-                @Override
-                protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
-                    return max(0, super.getOffset(gl, c, a, size, hrz));
-                }
-            };
-        }
-
-        @Override
-        String getDebugString() {
-            return "BASELINE";
-        }
-    };
-
-    /**
-     * Indicates that a view should expanded to fit the boundaries of its cell group.
-     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
-     * {@link LayoutParams#columnSpec columnSpecs}.
-     */
-    public static final Alignment FILL = new Alignment() {
-        @Override
-        int getGravityOffset(View view, int cellDelta) {
-            return 0;
-        }
-
-        @Override
-        public int getAlignmentValue(View view, int viewSize, int mode) {
-            return UNDEFINED;
-        }
-
-        @Override
-        public int getSizeInCell(View view, int viewSize, int cellSize) {
-            return cellSize;
-        }
-
-        @Override
-        String getDebugString() {
-            return "FILL";
-        }
-    };
-
-    static boolean canStretch(int flexibility) {
-        return (flexibility & CAN_STRETCH) != 0;
-    }
-
-    static final int INFLEXIBLE = 0;
-    static final int CAN_STRETCH = 2;
-}
diff --git a/android/support/v7/widget/GridLayoutManager.java b/android/support/v7/widget/GridLayoutManager.java
deleted file mode 100644
index 67605b9..0000000
--- a/android/support/v7/widget/GridLayoutManager.java
+++ /dev/null
@@ -1,1202 +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.content.Context;
-import android.graphics.Rect;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.Arrays;
-
-/**
- * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid.
- * <p>
- * By default, each item occupies 1 span. You can change it by providing a custom
- * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.
- */
-public class GridLayoutManager extends LinearLayoutManager {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "GridLayoutManager";
-    public static final int DEFAULT_SPAN_COUNT = -1;
-    /**
-     * Span size have been changed but we've not done a new layout calculation.
-     */
-    boolean mPendingSpanCountChange = false;
-    int mSpanCount = DEFAULT_SPAN_COUNT;
-    /**
-     * Right borders for each span.
-     * <p>For <b>i-th</b> item start is {@link #mCachedBorders}[i-1] + 1
-     * and end is {@link #mCachedBorders}[i].
-     */
-    int [] mCachedBorders;
-    /**
-     * Temporary array to keep views in layoutChunk method
-     */
-    View[] mSet;
-    final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray();
-    final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray();
-    SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();
-    // re-used variable to acquire decor insets from RecyclerView
-    final Rect mDecorInsets = new Rect();
-
-
-    /**
-     * Constructor used when layout manager is set in XML by RecyclerView attribute
-     * "layoutManager". If spanCount is not specified in the XML, it defaults to a
-     * single column.
-     *
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
-     */
-    public GridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
-                             int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
-        setSpanCount(properties.spanCount);
-    }
-
-    /**
-     * Creates a vertical GridLayoutManager
-     *
-     * @param context Current context, will be used to access resources.
-     * @param spanCount The number of columns in the grid
-     */
-    public GridLayoutManager(Context context, int spanCount) {
-        super(context);
-        setSpanCount(spanCount);
-    }
-
-    /**
-     * @param context Current context, will be used to access resources.
-     * @param spanCount The number of columns or rows in the grid
-     * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
-     *                      #VERTICAL}.
-     * @param reverseLayout When set to true, layouts from end to start.
-     */
-    public GridLayoutManager(Context context, int spanCount,
-            @RecyclerView.Orientation int orientation, boolean reverseLayout) {
-        super(context, orientation, reverseLayout);
-        setSpanCount(spanCount);
-    }
-
-    /**
-     * stackFromEnd is not supported by GridLayoutManager. Consider using
-     * {@link #setReverseLayout(boolean)}.
-     */
-    @Override
-    public void setStackFromEnd(boolean stackFromEnd) {
-        if (stackFromEnd) {
-            throw new UnsupportedOperationException(
-                    "GridLayoutManager does not support stack from end."
-                            + " Consider using reverse layout");
-        }
-        super.setStackFromEnd(false);
-    }
-
-    @Override
-    public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == HORIZONTAL) {
-            return mSpanCount;
-        }
-        if (state.getItemCount() < 1) {
-            return 0;
-        }
-
-        // Row count is one more than the last item's row index.
-        return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
-    }
-
-    @Override
-    public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == VERTICAL) {
-            return mSpanCount;
-        }
-        if (state.getItemCount() < 1) {
-            return 0;
-        }
-
-        // Column count is one more than the last item's column index.
-        return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
-            RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
-        ViewGroup.LayoutParams lp = host.getLayoutParams();
-        if (!(lp instanceof LayoutParams)) {
-            super.onInitializeAccessibilityNodeInfoForItem(host, info);
-            return;
-        }
-        LayoutParams glp = (LayoutParams) lp;
-        int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition());
-        if (mOrientation == HORIZONTAL) {
-            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-                    glp.getSpanIndex(), glp.getSpanSize(),
-                    spanGroupIndex, 1,
-                    mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
-        } else { // VERTICAL
-            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-                    spanGroupIndex , 1,
-                    glp.getSpanIndex(), glp.getSpanSize(),
-                    mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
-        }
-    }
-
-    @Override
-    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
-        if (state.isPreLayout()) {
-            cachePreLayoutSpanMapping();
-        }
-        super.onLayoutChildren(recycler, state);
-        if (DEBUG) {
-            validateChildOrder();
-        }
-        clearPreLayoutSpanMappingCache();
-    }
-
-    @Override
-    public void onLayoutCompleted(RecyclerView.State state) {
-        super.onLayoutCompleted(state);
-        mPendingSpanCountChange = false;
-    }
-
-    private void clearPreLayoutSpanMappingCache() {
-        mPreLayoutSpanSizeCache.clear();
-        mPreLayoutSpanIndexCache.clear();
-    }
-
-    private void cachePreLayoutSpanMapping() {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
-            final int viewPosition = lp.getViewLayoutPosition();
-            mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize());
-            mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex());
-        }
-    }
-
-    @Override
-    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
-        mSpanSizeLookup.invalidateSpanIndexCache();
-    }
-
-    @Override
-    public void onItemsChanged(RecyclerView recyclerView) {
-        mSpanSizeLookup.invalidateSpanIndexCache();
-    }
-
-    @Override
-    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
-        mSpanSizeLookup.invalidateSpanIndexCache();
-    }
-
-    @Override
-    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
-            Object payload) {
-        mSpanSizeLookup.invalidateSpanIndexCache();
-    }
-
-    @Override
-    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
-        mSpanSizeLookup.invalidateSpanIndexCache();
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
-        if (mOrientation == HORIZONTAL) {
-            return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                    ViewGroup.LayoutParams.MATCH_PARENT);
-        } else {
-            return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT);
-        }
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
-        return new LayoutParams(c, attrs);
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
-        if (lp instanceof ViewGroup.MarginLayoutParams) {
-            return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
-        } else {
-            return new LayoutParams(lp);
-        }
-    }
-
-    @Override
-    public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
-        return lp instanceof LayoutParams;
-    }
-
-    /**
-     * Sets the source to get the number of spans occupied by each item in the adapter.
-     *
-     * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans
-     *                       occupied by each item
-     */
-    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
-        mSpanSizeLookup = spanSizeLookup;
-    }
-
-    /**
-     * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager.
-     *
-     * @return The current {@link SpanSizeLookup} used by the GridLayoutManager.
-     */
-    public SpanSizeLookup getSpanSizeLookup() {
-        return mSpanSizeLookup;
-    }
-
-    private void updateMeasurements() {
-        int totalSpace;
-        if (getOrientation() == VERTICAL) {
-            totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
-        } else {
-            totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
-        }
-        calculateItemBorders(totalSpace);
-    }
-
-    @Override
-    public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
-        if (mCachedBorders == null) {
-            super.setMeasuredDimension(childrenBounds, wSpec, hSpec);
-        }
-        final int width, height;
-        final int horizontalPadding = getPaddingLeft() + getPaddingRight();
-        final int verticalPadding = getPaddingTop() + getPaddingBottom();
-        if (mOrientation == VERTICAL) {
-            final int usedHeight = childrenBounds.height() + verticalPadding;
-            height = chooseSize(hSpec, usedHeight, getMinimumHeight());
-            width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding,
-                    getMinimumWidth());
-        } else {
-            final int usedWidth = childrenBounds.width() + horizontalPadding;
-            width = chooseSize(wSpec, usedWidth, getMinimumWidth());
-            height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding,
-                    getMinimumHeight());
-        }
-        setMeasuredDimension(width, height);
-    }
-
-    /**
-     * @param totalSpace Total available space after padding is removed
-     */
-    private void calculateItemBorders(int totalSpace) {
-        mCachedBorders = calculateItemBorders(mCachedBorders, mSpanCount, totalSpace);
-    }
-
-    /**
-     * @param cachedBorders The out array
-     * @param spanCount number of spans
-     * @param totalSpace total available space after padding is removed
-     * @return The updated array. Might be the same instance as the provided array if its size
-     * has not changed.
-     */
-    static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) {
-        if (cachedBorders == null || cachedBorders.length != spanCount + 1
-                || cachedBorders[cachedBorders.length - 1] != totalSpace) {
-            cachedBorders = new int[spanCount + 1];
-        }
-        cachedBorders[0] = 0;
-        int sizePerSpan = totalSpace / spanCount;
-        int sizePerSpanRemainder = totalSpace % spanCount;
-        int consumedPixels = 0;
-        int additionalSize = 0;
-        for (int i = 1; i <= spanCount; i++) {
-            int itemSize = sizePerSpan;
-            additionalSize += sizePerSpanRemainder;
-            if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) {
-                itemSize += 1;
-                additionalSize -= spanCount;
-            }
-            consumedPixels += itemSize;
-            cachedBorders[i] = consumedPixels;
-        }
-        return cachedBorders;
-    }
-
-    int getSpaceForSpanRange(int startSpan, int spanSize) {
-        if (mOrientation == VERTICAL && isLayoutRTL()) {
-            return mCachedBorders[mSpanCount - startSpan]
-                    - mCachedBorders[mSpanCount - startSpan - spanSize];
-        } else {
-            return mCachedBorders[startSpan + spanSize] - mCachedBorders[startSpan];
-        }
-    }
-
-    @Override
-    void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
-                       AnchorInfo anchorInfo, int itemDirection) {
-        super.onAnchorReady(recycler, state, anchorInfo, itemDirection);
-        updateMeasurements();
-        if (state.getItemCount() > 0 && !state.isPreLayout()) {
-            ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection);
-        }
-        ensureViewSet();
-    }
-
-    private void ensureViewSet() {
-        if (mSet == null || mSet.length != mSpanCount) {
-            mSet = new View[mSpanCount];
-        }
-    }
-
-    @Override
-    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        updateMeasurements();
-        ensureViewSet();
-        return super.scrollHorizontallyBy(dx, recycler, state);
-    }
-
-    @Override
-    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        updateMeasurements();
-        ensureViewSet();
-        return super.scrollVerticallyBy(dy, recycler, state);
-    }
-
-    private void ensureAnchorIsInCorrectSpan(RecyclerView.Recycler recycler,
-            RecyclerView.State state, AnchorInfo anchorInfo, int itemDirection) {
-        final boolean layingOutInPrimaryDirection =
-                itemDirection == LayoutState.ITEM_DIRECTION_TAIL;
-        int span = getSpanIndex(recycler, state, anchorInfo.mPosition);
-        if (layingOutInPrimaryDirection) {
-            // choose span 0
-            while (span > 0 && anchorInfo.mPosition > 0) {
-                anchorInfo.mPosition--;
-                span = getSpanIndex(recycler, state, anchorInfo.mPosition);
-            }
-        } else {
-            // choose the max span we can get. hopefully last one
-            final int indexLimit = state.getItemCount() - 1;
-            int pos = anchorInfo.mPosition;
-            int bestSpan = span;
-            while (pos < indexLimit) {
-                int next = getSpanIndex(recycler, state, pos + 1);
-                if (next > bestSpan) {
-                    pos += 1;
-                    bestSpan = next;
-                } else {
-                    break;
-                }
-            }
-            anchorInfo.mPosition = pos;
-        }
-    }
-
-    @Override
-    View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
-                            int start, int end, int itemCount) {
-        ensureLayoutState();
-        View invalidMatch = null;
-        View outOfBoundsMatch = null;
-        final int boundsStart = mOrientationHelper.getStartAfterPadding();
-        final int boundsEnd = mOrientationHelper.getEndAfterPadding();
-        final int diff = end > start ? 1 : -1;
-
-        for (int i = start; i != end; i += diff) {
-            final View view = getChildAt(i);
-            final int position = getPosition(view);
-            if (position >= 0 && position < itemCount) {
-                final int span = getSpanIndex(recycler, state, position);
-                if (span != 0) {
-                    continue;
-                }
-                if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
-                    if (invalidMatch == null) {
-                        invalidMatch = view; // removed item, least preferred
-                    }
-                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
-                        || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
-                    if (outOfBoundsMatch == null) {
-                        outOfBoundsMatch = view; // item is not visible, less preferred
-                    }
-                } else {
-                    return view;
-                }
-            }
-        }
-        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
-    }
-
-    private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state,
-            int viewPosition) {
-        if (!state.isPreLayout()) {
-            return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount);
-        }
-        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition);
-        if (adapterPosition == -1) {
-            if (DEBUG) {
-                throw new RuntimeException("Cannot find span group index for position "
-                        + viewPosition);
-            }
-            Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition);
-            return 0;
-        }
-        return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount);
-    }
-
-    private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
-        if (!state.isPreLayout()) {
-            return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);
-        }
-        final int cached = mPreLayoutSpanIndexCache.get(pos, -1);
-        if (cached != -1) {
-            return cached;
-        }
-        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
-        if (adapterPosition == -1) {
-            if (DEBUG) {
-                throw new RuntimeException("Cannot find span index for pre layout position. It is"
-                        + " not cached, not in the adapter. Pos:" + pos);
-            }
-            Log.w(TAG, "Cannot find span size for pre layout position. It is"
-                    + " not cached, not in the adapter. Pos:" + pos);
-            return 0;
-        }
-        return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);
-    }
-
-    private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
-        if (!state.isPreLayout()) {
-            return mSpanSizeLookup.getSpanSize(pos);
-        }
-        final int cached = mPreLayoutSpanSizeCache.get(pos, -1);
-        if (cached != -1) {
-            return cached;
-        }
-        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
-        if (adapterPosition == -1) {
-            if (DEBUG) {
-                throw new RuntimeException("Cannot find span size for pre layout position. It is"
-                        + " not cached, not in the adapter. Pos:" + pos);
-            }
-            Log.w(TAG, "Cannot find span size for pre layout position. It is"
-                    + " not cached, not in the adapter. Pos:" + pos);
-            return 1;
-        }
-        return mSpanSizeLookup.getSpanSize(adapterPosition);
-    }
-
-    @Override
-    void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        int remainingSpan = mSpanCount;
-        int count = 0;
-        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
-            final int pos = layoutState.mCurrentPosition;
-            layoutPrefetchRegistry.addPosition(pos, Math.max(0, layoutState.mScrollingOffset));
-            final int spanSize = mSpanSizeLookup.getSpanSize(pos);
-            remainingSpan -= spanSize;
-            layoutState.mCurrentPosition += layoutState.mItemDirection;
-            count++;
-        }
-    }
-
-    @Override
-    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
-            LayoutState layoutState, LayoutChunkResult result) {
-        final int otherDirSpecMode = mOrientationHelper.getModeInOther();
-        final boolean flexibleInOtherDir = otherDirSpecMode != View.MeasureSpec.EXACTLY;
-        final int currentOtherDirSize = getChildCount() > 0 ? mCachedBorders[mSpanCount] : 0;
-        // if grid layout's dimensions are not specified, let the new row change the measurements
-        // This is not perfect since we not covering all rows but still solves an important case
-        // where they may have a header row which should be laid out according to children.
-        if (flexibleInOtherDir) {
-            updateMeasurements(); //  reset measurements
-        }
-        final boolean layingOutInPrimaryDirection =
-                layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
-        int count = 0;
-        int consumedSpanCount = 0;
-        int remainingSpan = mSpanCount;
-        if (!layingOutInPrimaryDirection) {
-            int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
-            int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
-            remainingSpan = itemSpanIndex + itemSpanSize;
-        }
-        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
-            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
-                        + " spans.");
-            }
-            remainingSpan -= spanSize;
-            if (remainingSpan < 0) {
-                break; // item did not fit into this row or column
-            }
-            View view = layoutState.next(recycler);
-            if (view == null) {
-                break;
-            }
-            consumedSpanCount += spanSize;
-            mSet[count] = view;
-            count++;
-        }
-
-        if (count == 0) {
-            result.mFinished = true;
-            return;
-        }
-
-        int maxSize = 0;
-        float maxSizeInOther = 0; // use a float to get size per span
-
-        // we should assign spans before item decor offsets are calculated
-        assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
-        for (int i = 0; i < count; i++) {
-            View view = mSet[i];
-            if (layoutState.mScrapList == null) {
-                if (layingOutInPrimaryDirection) {
-                    addView(view);
-                } else {
-                    addView(view, 0);
-                }
-            } else {
-                if (layingOutInPrimaryDirection) {
-                    addDisappearingView(view);
-                } else {
-                    addDisappearingView(view, 0);
-                }
-            }
-            calculateItemDecorationsForChild(view, mDecorInsets);
-
-            measureChild(view, otherDirSpecMode, false);
-            final int size = mOrientationHelper.getDecoratedMeasurement(view);
-            if (size > maxSize) {
-                maxSize = size;
-            }
-            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-            final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view)
-                    / lp.mSpanSize;
-            if (otherSize > maxSizeInOther) {
-                maxSizeInOther = otherSize;
-            }
-        }
-        if (flexibleInOtherDir) {
-            // re-distribute columns
-            guessMeasurement(maxSizeInOther, currentOtherDirSize);
-            // now we should re-measure any item that was match parent.
-            maxSize = 0;
-            for (int i = 0; i < count; i++) {
-                View view = mSet[i];
-                measureChild(view, View.MeasureSpec.EXACTLY, true);
-                final int size = mOrientationHelper.getDecoratedMeasurement(view);
-                if (size > maxSize) {
-                    maxSize = size;
-                }
-            }
-        }
-
-        // 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++) {
-            final View view = mSet[i];
-            if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                final Rect decorInsets = lp.mDecorInsets;
-                final int verticalInsets = decorInsets.top + decorInsets.bottom
-                        + lp.topMargin + lp.bottomMargin;
-                final int horizontalInsets = decorInsets.left + decorInsets.right
-                        + lp.leftMargin + lp.rightMargin;
-                final int totalSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize);
-                final int wSpec;
-                final int hSpec;
-                if (mOrientation == VERTICAL) {
-                    wSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY,
-                            horizontalInsets, lp.width, false);
-                    hSpec = View.MeasureSpec.makeMeasureSpec(maxSize - verticalInsets,
-                            View.MeasureSpec.EXACTLY);
-                } else {
-                    wSpec = View.MeasureSpec.makeMeasureSpec(maxSize - horizontalInsets,
-                            View.MeasureSpec.EXACTLY);
-                    hSpec = getChildMeasureSpec(totalSpaceInOther, View.MeasureSpec.EXACTLY,
-                            verticalInsets, lp.height, false);
-                }
-                measureChildWithDecorationsAndMargin(view, wSpec, hSpec, true);
-            }
-        }
-
-        result.mConsumed = maxSize;
-
-        int left = 0, right = 0, top = 0, bottom = 0;
-        if (mOrientation == VERTICAL) {
-            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
-                bottom = layoutState.mOffset;
-                top = bottom - maxSize;
-            } else {
-                top = layoutState.mOffset;
-                bottom = top + maxSize;
-            }
-        } else {
-            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
-                right = layoutState.mOffset;
-                left = right - maxSize;
-            } else {
-                left = layoutState.mOffset;
-                right = left + maxSize;
-            }
-        }
-        for (int i = 0; i < count; i++) {
-            View view = mSet[i];
-            LayoutParams params = (LayoutParams) view.getLayoutParams();
-            if (mOrientation == VERTICAL) {
-                if (isLayoutRTL()) {
-                    right = getPaddingLeft() + mCachedBorders[mSpanCount - params.mSpanIndex];
-                    left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
-                } else {
-                    left = getPaddingLeft() + mCachedBorders[params.mSpanIndex];
-                    right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
-                }
-            } else {
-                top = getPaddingTop() + mCachedBorders[params.mSpanIndex];
-                bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
-            }
-            // We calculate everything with View's bounding box (which includes decor and margins)
-            // To calculate correct layout position, we subtract margins.
-            layoutDecoratedWithMargins(view, left, top, right, bottom);
-            if (DEBUG) {
-                Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
-                        + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
-                        + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)
-                        + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize);
-            }
-            // Consume the available space if the view is not removed OR changed
-            if (params.isItemRemoved() || params.isItemChanged()) {
-                result.mIgnoreConsumed = true;
-            }
-            result.mFocusable |= view.hasFocusable();
-        }
-        Arrays.fill(mSet, null);
-    }
-
-    /**
-     * Measures a child with currently known information. This is not necessarily the child's final
-     * measurement. (see fillChunk for details).
-     *
-     * @param view The child view to be measured
-     * @param otherDirParentSpecMode The RV measure spec that should be used in the secondary
-     *                               orientation
-     * @param alreadyMeasured True if we've already measured this view once
-     */
-    private void measureChild(View view, int otherDirParentSpecMode, boolean alreadyMeasured) {
-        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        final Rect decorInsets = lp.mDecorInsets;
-        final int verticalInsets = decorInsets.top + decorInsets.bottom
-                + lp.topMargin + lp.bottomMargin;
-        final int horizontalInsets = decorInsets.left + decorInsets.right
-                + lp.leftMargin + lp.rightMargin;
-        final int availableSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize);
-        final int wSpec;
-        final int hSpec;
-        if (mOrientation == VERTICAL) {
-            wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
-                    horizontalInsets, lp.width, false);
-            hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(),
-                    verticalInsets, lp.height, true);
-        } else {
-            hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
-                    verticalInsets, lp.height, false);
-            wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(),
-                    horizontalInsets, lp.width, true);
-        }
-        measureChildWithDecorationsAndMargin(view, wSpec, hSpec, alreadyMeasured);
-    }
-
-    /**
-     * This is called after laying out a row (if vertical) or a column (if horizontal) when the
-     * RecyclerView does not have exact measurement specs.
-     * <p>
-     * Here we try to assign a best guess width or height and re-do the layout to update other
-     * views that wanted to MATCH_PARENT in the non-scroll orientation.
-     *
-     * @param maxSizeInOther The maximum size per span ratio from the measurement of the children.
-     * @param currentOtherDirSize The size before this layout chunk. There is no reason to go below.
-     */
-    private void guessMeasurement(float maxSizeInOther, int currentOtherDirSize) {
-        final int contentSize = Math.round(maxSizeInOther * mSpanCount);
-        // always re-calculate because borders were stretched during the fill
-        calculateItemBorders(Math.max(contentSize, currentOtherDirSize));
-    }
-
-    private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec,
-            boolean alreadyMeasured) {
-        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
-        final boolean measure;
-        if (alreadyMeasured) {
-            measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp);
-        } else {
-            measure = shouldMeasureChild(child, widthSpec, heightSpec, lp);
-        }
-        if (measure) {
-            child.measure(widthSpec, heightSpec);
-        }
-    }
-
-    private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
-            int consumedSpanCount, boolean layingOutInPrimaryDirection) {
-        // spans are always assigned from 0 to N no matter if it is RTL or not.
-        // RTL is used only when positioning the view.
-        int span, start, end, diff;
-        // make sure we traverse from min position to max position
-        if (layingOutInPrimaryDirection) {
-            start = 0;
-            end = count;
-            diff = 1;
-        } else {
-            start = count - 1;
-            end = -1;
-            diff = -1;
-        }
-        span = 0;
-        for (int i = start; i != end; i += diff) {
-            View view = mSet[i];
-            LayoutParams params = (LayoutParams) view.getLayoutParams();
-            params.mSpanSize = getSpanSize(recycler, state, getPosition(view));
-            params.mSpanIndex = span;
-            span += params.mSpanSize;
-        }
-    }
-
-    /**
-     * Returns the number of spans laid out by this grid.
-     *
-     * @return The number of spans
-     * @see #setSpanCount(int)
-     */
-    public int getSpanCount() {
-        return mSpanCount;
-    }
-
-    /**
-     * Sets the number of spans to be laid out.
-     * <p>
-     * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns.
-     * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows.
-     *
-     * @param spanCount The total number of spans in the grid
-     * @see #getSpanCount()
-     */
-    public void setSpanCount(int spanCount) {
-        if (spanCount == mSpanCount) {
-            return;
-        }
-        mPendingSpanCountChange = true;
-        if (spanCount < 1) {
-            throw new IllegalArgumentException("Span count should be at least 1. Provided "
-                    + spanCount);
-        }
-        mSpanCount = spanCount;
-        mSpanSizeLookup.invalidateSpanIndexCache();
-        requestLayout();
-    }
-
-    /**
-     * A helper class to provide the number of spans each item occupies.
-     * <p>
-     * Default implementation sets each item to occupy exactly 1 span.
-     *
-     * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
-     */
-    public abstract static class SpanSizeLookup {
-
-        final SparseIntArray mSpanIndexCache = new SparseIntArray();
-
-        private boolean mCacheSpanIndices = false;
-
-        /**
-         * Returns the number of span occupied by the item at <code>position</code>.
-         *
-         * @param position The adapter position of the item
-         * @return The number of spans occupied by the item at the provided position
-         */
-        public abstract int getSpanSize(int position);
-
-        /**
-         * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
-         * not. By default these values are not cached. If you are not overriding
-         * {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
-         *
-         * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
-         */
-        public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
-            mCacheSpanIndices = cacheSpanIndices;
-        }
-
-        /**
-         * Clears the span index cache. GridLayoutManager automatically calls this method when
-         * adapter changes occur.
-         */
-        public void invalidateSpanIndexCache() {
-            mSpanIndexCache.clear();
-        }
-
-        /**
-         * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
-         *
-         * @return True if results of {@link #getSpanIndex(int, int)} are cached.
-         */
-        public boolean isSpanIndexCacheEnabled() {
-            return mCacheSpanIndices;
-        }
-
-        int getCachedSpanIndex(int position, int spanCount) {
-            if (!mCacheSpanIndices) {
-                return getSpanIndex(position, spanCount);
-            }
-            final int existing = mSpanIndexCache.get(position, -1);
-            if (existing != -1) {
-                return existing;
-            }
-            final int value = getSpanIndex(position, spanCount);
-            mSpanIndexCache.put(position, value);
-            return value;
-        }
-
-        /**
-         * Returns the final span index of the provided position.
-         * <p>
-         * If you have a faster way to calculate span index for your items, you should override
-         * this method. Otherwise, you should enable span index cache
-         * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
-         * disabled, default implementation traverses all items from 0 to
-         * <code>position</code>. When caching is enabled, it calculates from the closest cached
-         * value before the <code>position</code>.
-         * <p>
-         * If you override this method, you need to make sure it is consistent with
-         * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for
-         * each item. It is called only for the reference item and rest of the items
-         * are assigned to spans based on the reference item. For example, you cannot assign a
-         * position to span 2 while span 1 is empty.
-         * <p>
-         * Note that span offsets always start with 0 and are not affected by RTL.
-         *
-         * @param position  The position of the item
-         * @param spanCount The total number of spans in the grid
-         * @return The final span position of the item. Should be between 0 (inclusive) and
-         * <code>spanCount</code>(exclusive)
-         */
-        public int getSpanIndex(int position, int spanCount) {
-            int positionSpanSize = getSpanSize(position);
-            if (positionSpanSize == spanCount) {
-                return 0; // quick return for full-span items
-            }
-            int span = 0;
-            int startPos = 0;
-            // If caching is enabled, try to jump
-            if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
-                int prevKey = findReferenceIndexFromCache(position);
-                if (prevKey >= 0) {
-                    span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
-                    startPos = prevKey + 1;
-                }
-            }
-            for (int i = startPos; i < position; i++) {
-                int size = getSpanSize(i);
-                span += size;
-                if (span == spanCount) {
-                    span = 0;
-                } else if (span > spanCount) {
-                    // did not fit, moving to next row / column
-                    span = size;
-                }
-            }
-            if (span + positionSpanSize <= spanCount) {
-                return span;
-            }
-            return 0;
-        }
-
-        int findReferenceIndexFromCache(int position) {
-            int lo = 0;
-            int hi = mSpanIndexCache.size() - 1;
-
-            while (lo <= hi) {
-                final int mid = (lo + hi) >>> 1;
-                final int midVal = mSpanIndexCache.keyAt(mid);
-                if (midVal < position) {
-                    lo = mid + 1;
-                } else {
-                    hi = mid - 1;
-                }
-            }
-            int index = lo - 1;
-            if (index >= 0 && index < mSpanIndexCache.size()) {
-                return mSpanIndexCache.keyAt(index);
-            }
-            return -1;
-        }
-
-        /**
-         * Returns the index of the group this position belongs.
-         * <p>
-         * For example, if grid has 3 columns and each item occupies 1 span, span group index
-         * for item 1 will be 0, item 5 will be 1.
-         *
-         * @param adapterPosition The position in adapter
-         * @param spanCount The total number of spans in the grid
-         * @return The index of the span group including the item at the given adapter position
-         */
-        public int getSpanGroupIndex(int adapterPosition, int spanCount) {
-            int span = 0;
-            int group = 0;
-            int positionSpanSize = getSpanSize(adapterPosition);
-            for (int i = 0; i < adapterPosition; i++) {
-                int size = getSpanSize(i);
-                span += size;
-                if (span == spanCount) {
-                    span = 0;
-                    group++;
-                } else if (span > spanCount) {
-                    // did not fit, moving to next row / column
-                    span = size;
-                    group++;
-                }
-            }
-            if (span + positionSpanSize > spanCount) {
-                group++;
-            }
-            return group;
-        }
-    }
-
-    @Override
-    public View onFocusSearchFailed(View focused, int focusDirection,
-            RecyclerView.Recycler recycler, RecyclerView.State state) {
-        View prevFocusedChild = findContainingItemView(focused);
-        if (prevFocusedChild == null) {
-            return null;
-        }
-        LayoutParams lp = (LayoutParams) prevFocusedChild.getLayoutParams();
-        final int prevSpanStart = lp.mSpanIndex;
-        final int prevSpanEnd = lp.mSpanIndex + lp.mSpanSize;
-        View view = super.onFocusSearchFailed(focused, focusDirection, recycler, state);
-        if (view == null) {
-            return null;
-        }
-        // LinearLayoutManager finds the last child. What we want is the child which has the same
-        // spanIndex.
-        final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
-        final boolean ascend = (layoutDir == LayoutState.LAYOUT_END) != mShouldReverseLayout;
-        final int start, inc, limit;
-        if (ascend) {
-            start = getChildCount() - 1;
-            inc = -1;
-            limit = -1;
-        } else {
-            start = 0;
-            inc = 1;
-            limit = getChildCount();
-        }
-        final boolean preferLastSpan = mOrientation == VERTICAL && isLayoutRTL();
-
-        // The focusable candidate to be picked if no perfect focusable candidate is found.
-        // The best focusable candidate is the one with the highest amount of span overlap with
-        // the currently focused view.
-        View focusableWeakCandidate = null; // somewhat matches but not strong
-        int focusableWeakCandidateSpanIndex = -1;
-        int focusableWeakCandidateOverlap = 0; // how many spans overlap
-
-        // The unfocusable candidate to become visible on the screen next, if no perfect or
-        // weak focusable candidates are found to receive focus next.
-        // We are only interested in partially visible unfocusable views. These are views that are
-        // not fully visible, that is either partially overlapping, or out-of-bounds and right below
-        // or above RV's padded bounded area. The best unfocusable candidate is the one with the
-        // highest amount of span overlap with the currently focused view.
-        View unfocusableWeakCandidate = null; // somewhat matches but not strong
-        int unfocusableWeakCandidateSpanIndex = -1;
-        int unfocusableWeakCandidateOverlap = 0; // how many spans overlap
-
-        // The span group index of the start child. This indicates the span group index of the
-        // next focusable item to receive focus, if a focusable item within the same span group
-        // exists. Any focusable item beyond this group index are not relevant since they
-        // were already stored in the layout before onFocusSearchFailed call and were not picked
-        // by the focusSearch algorithm.
-        int focusableSpanGroupIndex = getSpanGroupIndex(recycler, state, start);
-        for (int i = start; i != limit; i += inc) {
-            int spanGroupIndex = getSpanGroupIndex(recycler, state, i);
-            View candidate = getChildAt(i);
-            if (candidate == prevFocusedChild) {
-                break;
-            }
-
-            if (candidate.hasFocusable() && spanGroupIndex != focusableSpanGroupIndex) {
-                // We are past the allowable span group index for the next focusable item.
-                // The search only continues if no focusable weak candidates have been found up
-                // until this point, in order to find the best unfocusable candidate to become
-                // visible on the screen next.
-                if (focusableWeakCandidate != null) {
-                    break;
-                }
-                continue;
-            }
-
-            final LayoutParams candidateLp = (LayoutParams) candidate.getLayoutParams();
-            final int candidateStart = candidateLp.mSpanIndex;
-            final int candidateEnd = candidateLp.mSpanIndex + candidateLp.mSpanSize;
-            if (candidate.hasFocusable() && candidateStart == prevSpanStart
-                    && candidateEnd == prevSpanEnd) {
-                return candidate; // perfect match
-            }
-            boolean assignAsWeek = false;
-            if ((candidate.hasFocusable() && focusableWeakCandidate == null)
-                    || (!candidate.hasFocusable() && unfocusableWeakCandidate == null)) {
-                assignAsWeek = true;
-            } else {
-                int maxStart = Math.max(candidateStart, prevSpanStart);
-                int minEnd = Math.min(candidateEnd, prevSpanEnd);
-                int overlap = minEnd - maxStart;
-                if (candidate.hasFocusable()) {
-                    if (overlap > focusableWeakCandidateOverlap) {
-                        assignAsWeek = true;
-                    } else if (overlap == focusableWeakCandidateOverlap
-                            && preferLastSpan == (candidateStart
-                            > focusableWeakCandidateSpanIndex)) {
-                        assignAsWeek = true;
-                    }
-                } else if (focusableWeakCandidate == null
-                        && isViewPartiallyVisible(candidate, false, true)) {
-                    if (overlap > unfocusableWeakCandidateOverlap) {
-                        assignAsWeek = true;
-                    } else if (overlap == unfocusableWeakCandidateOverlap
-                            && preferLastSpan == (candidateStart
-                                    > unfocusableWeakCandidateSpanIndex)) {
-                        assignAsWeek = true;
-                    }
-                }
-            }
-
-            if (assignAsWeek) {
-                if (candidate.hasFocusable()) {
-                    focusableWeakCandidate = candidate;
-                    focusableWeakCandidateSpanIndex = candidateLp.mSpanIndex;
-                    focusableWeakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd)
-                            - Math.max(candidateStart, prevSpanStart);
-                } else {
-                    unfocusableWeakCandidate = candidate;
-                    unfocusableWeakCandidateSpanIndex = candidateLp.mSpanIndex;
-                    unfocusableWeakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd)
-                            - Math.max(candidateStart, prevSpanStart);
-                }
-            }
-        }
-        return (focusableWeakCandidate != null) ? focusableWeakCandidate : unfocusableWeakCandidate;
-    }
-
-    @Override
-    public boolean supportsPredictiveItemAnimations() {
-        return mPendingSavedState == null && !mPendingSpanCountChange;
-    }
-
-    /**
-     * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
-     */
-    public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
-
-        @Override
-        public int getSpanSize(int position) {
-            return 1;
-        }
-
-        @Override
-        public int getSpanIndex(int position, int spanCount) {
-            return position % spanCount;
-        }
-    }
-
-    /**
-     * LayoutParams used by GridLayoutManager.
-     * <p>
-     * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the
-     * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is
-     * expected to fill all of the space given to it.
-     */
-    public static class LayoutParams extends RecyclerView.LayoutParams {
-
-        /**
-         * Span Id for Views that are not laid out yet.
-         */
-        public static final int INVALID_SPAN_ID = -1;
-
-        int mSpanIndex = INVALID_SPAN_ID;
-
-        int mSpanSize = 0;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(ViewGroup.MarginLayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(RecyclerView.LayoutParams source) {
-            super(source);
-        }
-
-        /**
-         * Returns the current span index of this View. If the View is not laid out yet, the return
-         * value is <code>undefined</code>.
-         * <p>
-         * Starting with RecyclerView <b>24.2.0</b>, span indices are always indexed from position 0
-         * even if the layout is RTL. In a vertical GridLayoutManager, <b>leftmost</b> span is span
-         * 0 if the layout is <b>LTR</b> and <b>rightmost</b> span is span 0 if the layout is
-         * <b>RTL</b>. Prior to 24.2.0, it was the opposite which was conflicting with
-         * {@link SpanSizeLookup#getSpanIndex(int, int)}.
-         * <p>
-         * If the View occupies multiple spans, span with the minimum index is returned.
-         *
-         * @return The span index of the View.
-         */
-        public int getSpanIndex() {
-            return mSpanIndex;
-        }
-
-        /**
-         * Returns the number of spans occupied by this View. If the View not laid out yet, the
-         * return value is <code>undefined</code>.
-         *
-         * @return The number of spans occupied by this View.
-         */
-        public int getSpanSize() {
-            return mSpanSize;
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/LayoutState.java b/android/support/v7/widget/LayoutState.java
deleted file mode 100644
index f75da1c..0000000
--- a/android/support/v7/widget/LayoutState.java
+++ /dev/null
@@ -1,116 +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.view.View;
-
-/**
- * Helper class that keeps temporary state while {LayoutManager} is filling out the empty
- * space.
- */
-class LayoutState {
-
-    static final String TAG = "LayoutState";
-
-    static final int LAYOUT_START = -1;
-
-    static final int LAYOUT_END = 1;
-
-    static final int INVALID_LAYOUT = Integer.MIN_VALUE;
-
-    static final int ITEM_DIRECTION_HEAD = -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.
-     */
-    int mAvailable;
-
-    /**
-     * Current position on the adapter to get the next item.
-     */
-    int mCurrentPosition;
-
-    /**
-     * Defines the direction in which the data adapter is traversed.
-     * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
-     */
-    int mItemDirection;
-
-    /**
-     * Defines the direction in which the layout is filled.
-     * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
-     */
-    int mLayoutDirection;
-
-    /**
-     * This is the target pixel closest to the start of the layout that we are trying to fill
-     */
-    int mStartLine = 0;
-
-    /**
-     * This is the target pixel closest to the end of the layout that we are trying to fill
-     */
-    int mEndLine = 0;
-
-    /**
-     * If true, layout should stop if a focusable view is added
-     */
-    boolean mStopInFocusable;
-
-    /**
-     * If the content is not wrapped with any value
-     */
-    boolean mInfinite;
-
-    /**
-     * @return true if there are more items in the data adapter
-     */
-    boolean hasMore(RecyclerView.State state) {
-        return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
-    }
-
-    /**
-     * Gets the view for the next element that we should render.
-     * Also updates current item index to the next item, based on {@link #mItemDirection}
-     *
-     * @return The next element that we should render.
-     */
-    View next(RecyclerView.Recycler recycler) {
-        final View view = recycler.getViewForPosition(mCurrentPosition);
-        mCurrentPosition += mItemDirection;
-        return view;
-    }
-
-    @Override
-    public String toString() {
-        return "LayoutState{"
-                + "mAvailable=" + mAvailable
-                + ", mCurrentPosition=" + mCurrentPosition
-                + ", mItemDirection=" + mItemDirection
-                + ", mLayoutDirection=" + mLayoutDirection
-                + ", mStartLine=" + mStartLine
-                + ", mEndLine=" + mEndLine
-                + '}';
-    }
-}
diff --git a/android/support/v7/widget/LinearLayoutCompat.java b/android/support/v7/widget/LinearLayoutCompat.java
deleted file mode 100644
index ef68896..0000000
--- a/android/support/v7/widget/LinearLayoutCompat.java
+++ /dev/null
@@ -1,1848 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A Layout that arranges its children in a single column or a single row. The direction of
- * the row can be set by calling {@link #setOrientation(int) setOrientation()}.
- * You can also specify gravity, which specifies the alignment of all the child elements by
- * calling {@link #setGravity(int) setGravity()} or specify that specific children
- * grow to fill up any remaining space in the layout by setting the <em>weight</em> member of
- * {@link LinearLayoutCompat.LayoutParams LinearLayoutCompat.LayoutParams}.
- * The default orientation is horizontal.
- *
- * <p>See the <a href="{@docRoot}guide/topics/ui/layout/linear.html">Linear Layout</a>
- * guide.</p>
- *
- * <p>
- * Also see {@link LinearLayoutCompat.LayoutParams} for layout attributes </p>
- */
-public class LinearLayoutCompat extends ViewGroup {
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({HORIZONTAL, VERTICAL})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface OrientationMode {}
-
-    public static final int HORIZONTAL = 0;
-    public static final int VERTICAL = 1;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef(flag = true,
-            value = {
-                    SHOW_DIVIDER_NONE,
-                    SHOW_DIVIDER_BEGINNING,
-                    SHOW_DIVIDER_MIDDLE,
-                    SHOW_DIVIDER_END
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DividerMode {}
-
-    /**
-     * Don't show any dividers.
-     */
-    public static final int SHOW_DIVIDER_NONE = 0;
-    /**
-     * Show a divider at the beginning of the group.
-     */
-    public static final int SHOW_DIVIDER_BEGINNING = 1;
-    /**
-     * Show dividers between each item in the group.
-     */
-    public static final int SHOW_DIVIDER_MIDDLE = 2;
-    /**
-     * Show a divider at the end of the group.
-     */
-    public static final int SHOW_DIVIDER_END = 4;
-
-    /**
-     * Whether the children of this layout are baseline aligned.  Only applicable
-     * if {@link #mOrientation} is horizontal.
-     */
-    private boolean mBaselineAligned = true;
-
-    /**
-     * If this layout is part of another layout that is baseline aligned,
-     * use the child at this index as the baseline.
-     *
-     * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
-     * with whether the children of this layout are baseline aligned.
-     */
-    private int mBaselineAlignedChildIndex = -1;
-
-    /**
-     * The additional offset to the child's baseline.
-     * We'll calculate the baseline of this layout as we measure vertically; for
-     * horizontal linear layouts, the offset of 0 is appropriate.
-     */
-    private int mBaselineChildTop = 0;
-
-    private int mOrientation;
-
-    private int mGravity = GravityCompat.START | Gravity.TOP;
-
-    private int mTotalLength;
-
-    private float mWeightSum;
-
-    private boolean mUseLargestChild;
-
-    private int[] mMaxAscent;
-    private int[] mMaxDescent;
-
-    private static final int VERTICAL_GRAVITY_COUNT = 4;
-
-    private static final int INDEX_CENTER_VERTICAL = 0;
-    private static final int INDEX_TOP = 1;
-    private static final int INDEX_BOTTOM = 2;
-    private static final int INDEX_FILL = 3;
-
-    private Drawable mDivider;
-    private int mDividerWidth;
-    private int mDividerHeight;
-    private int mShowDividers;
-    private int mDividerPadding;
-
-    public LinearLayoutCompat(Context context) {
-        this(context, null);
-    }
-
-    public LinearLayoutCompat(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public LinearLayoutCompat(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
-                R.styleable.LinearLayoutCompat, defStyleAttr, 0);
-
-        int index = a.getInt(R.styleable.LinearLayoutCompat_android_orientation, -1);
-        if (index >= 0) {
-            setOrientation(index);
-        }
-
-        index = a.getInt(R.styleable.LinearLayoutCompat_android_gravity, -1);
-        if (index >= 0) {
-            setGravity(index);
-        }
-
-        boolean baselineAligned = a.getBoolean(R.styleable.LinearLayoutCompat_android_baselineAligned, true);
-        if (!baselineAligned) {
-            setBaselineAligned(baselineAligned);
-        }
-
-        mWeightSum = a.getFloat(R.styleable.LinearLayoutCompat_android_weightSum, -1.0f);
-
-        mBaselineAlignedChildIndex =
-                a.getInt(R.styleable.LinearLayoutCompat_android_baselineAlignedChildIndex, -1);
-
-        mUseLargestChild = a.getBoolean(R.styleable.LinearLayoutCompat_measureWithLargestChild, false);
-
-        setDividerDrawable(a.getDrawable(R.styleable.LinearLayoutCompat_divider));
-        mShowDividers = a.getInt(R.styleable.LinearLayoutCompat_showDividers, SHOW_DIVIDER_NONE);
-        mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayoutCompat_dividerPadding, 0);
-
-        a.recycle();
-    }
-
-    /**
-     * Set how dividers should be shown between items in this layout
-     *
-     * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
-     *                     {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
-     *                     or {@link #SHOW_DIVIDER_NONE} to show no dividers.
-     */
-    public void setShowDividers(@DividerMode int showDividers) {
-        if (showDividers != mShowDividers) {
-            requestLayout();
-        }
-        mShowDividers = showDividers;
-    }
-
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return false;
-    }
-
-    /**
-     * @return A flag set indicating how dividers should be shown around items.
-     * @see #setShowDividers(int)
-     */
-    @DividerMode
-    public int getShowDividers() {
-        return mShowDividers;
-    }
-
-    /**
-     * @return the divider Drawable that will divide each item.
-     *
-     * @see #setDividerDrawable(Drawable)
-     */
-    public Drawable getDividerDrawable() {
-        return mDivider;
-    }
-
-    /**
-     * Set a drawable to be used as a divider between items.
-     *
-     * @param divider Drawable that will divide each item.
-     *
-     * @see #setShowDividers(int)
-     */
-    public void setDividerDrawable(Drawable divider) {
-        if (divider == mDivider) {
-            return;
-        }
-        mDivider = divider;
-        if (divider != null) {
-            mDividerWidth = divider.getIntrinsicWidth();
-            mDividerHeight = divider.getIntrinsicHeight();
-        } else {
-            mDividerWidth = 0;
-            mDividerHeight = 0;
-        }
-        setWillNotDraw(divider == null);
-        requestLayout();
-    }
-
-    /**
-     * Set padding displayed on both ends of dividers.
-     *
-     * @param padding Padding value in pixels that will be applied to each end
-     *
-     * @see #setShowDividers(int)
-     * @see #setDividerDrawable(Drawable)
-     * @see #getDividerPadding()
-     */
-    public void setDividerPadding(int padding) {
-        mDividerPadding = padding;
-    }
-
-    /**
-     * Get the padding size used to inset dividers in pixels
-     *
-     * @see #setShowDividers(int)
-     * @see #setDividerDrawable(Drawable)
-     * @see #setDividerPadding(int)
-     */
-    public int getDividerPadding() {
-        return mDividerPadding;
-    }
-
-    /**
-     * Get the width of the current divider drawable.
-     *
-     * @hide Used internally by framework.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public int getDividerWidth() {
-        return mDividerWidth;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mDivider == null) {
-            return;
-        }
-
-        if (mOrientation == VERTICAL) {
-            drawDividersVertical(canvas);
-        } else {
-            drawDividersHorizontal(canvas);
-        }
-    }
-
-    void drawDividersVertical(Canvas canvas) {
-        final int count = getVirtualChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = getVirtualChildAt(i);
-
-            if (child != null && child.getVisibility() != GONE) {
-                if (hasDividerBeforeChildAt(i)) {
-                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                    final int top = child.getTop() - lp.topMargin - mDividerHeight;
-                    drawHorizontalDivider(canvas, top);
-                }
-            }
-        }
-
-        if (hasDividerBeforeChildAt(count)) {
-            final View child = getVirtualChildAt(count - 1);
-            int bottom = 0;
-            if (child == null) {
-                bottom = getHeight() - getPaddingBottom() - mDividerHeight;
-            } else {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                bottom = child.getBottom() + lp.bottomMargin;
-            }
-            drawHorizontalDivider(canvas, bottom);
-        }
-    }
-
-    void drawDividersHorizontal(Canvas canvas) {
-        final int count = getVirtualChildCount();
-        final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
-        for (int i = 0; i < count; i++) {
-            final View child = getVirtualChildAt(i);
-
-            if (child != null && child.getVisibility() != GONE) {
-                if (hasDividerBeforeChildAt(i)) {
-                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                    final int position;
-                    if (isLayoutRtl) {
-                        position = child.getRight() + lp.rightMargin;
-                    } else {
-                        position = child.getLeft() - lp.leftMargin - mDividerWidth;
-                    }
-                    drawVerticalDivider(canvas, position);
-                }
-            }
-        }
-
-        if (hasDividerBeforeChildAt(count)) {
-            final View child = getVirtualChildAt(count - 1);
-            int position;
-            if (child == null) {
-                if (isLayoutRtl) {
-                    position = getPaddingLeft();
-                } else {
-                    position = getWidth() - getPaddingRight() - mDividerWidth;
-                }
-            } else {
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (isLayoutRtl) {
-                    position = child.getLeft() - lp.leftMargin - mDividerWidth;
-                } else {
-                    position = child.getRight() + lp.rightMargin;
-                }
-            }
-            drawVerticalDivider(canvas, position);
-        }
-    }
-
-    void drawHorizontalDivider(Canvas canvas, int top) {
-        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
-                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
-        mDivider.draw(canvas);
-    }
-
-    void drawVerticalDivider(Canvas canvas, int left) {
-        mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
-                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
-        mDivider.draw(canvas);
-    }
-
-    /**
-     * <p>Indicates whether widgets contained within this layout are aligned
-     * on their baseline or not.</p>
-     *
-     * @return true when widgets are baseline-aligned, false otherwise
-     */
-    public boolean isBaselineAligned() {
-        return mBaselineAligned;
-    }
-
-    /**
-     * <p>Defines whether widgets contained in this layout are
-     * baseline-aligned or not.</p>
-     *
-     * @param baselineAligned true to align widgets on their baseline,
-     *         false otherwise
-     */
-    public void setBaselineAligned(boolean baselineAligned) {
-        mBaselineAligned = baselineAligned;
-    }
-
-    /**
-     * When true, all children with a weight will be considered having
-     * the minimum size of the largest child. If false, all children are
-     * measured normally.
-     *
-     * @return True to measure children with a weight using the minimum
-     *         size of the largest child, false otherwise.
-     */
-    public boolean isMeasureWithLargestChildEnabled() {
-        return mUseLargestChild;
-    }
-
-    /**
-     * When set to true, all children with a weight will be considered having
-     * the minimum size of the largest child. If false, all children are
-     * measured normally.
-     *
-     * Disabled by default.
-     *
-     * @param enabled True to measure children with a weight using the
-     *        minimum size of the largest child, false otherwise.
-     */
-    public void setMeasureWithLargestChildEnabled(boolean enabled) {
-        mUseLargestChild = enabled;
-    }
-
-    @Override
-    public int getBaseline() {
-        if (mBaselineAlignedChildIndex < 0) {
-            return super.getBaseline();
-        }
-
-        if (getChildCount() <= mBaselineAlignedChildIndex) {
-            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
-                    + "set to an index that is out of bounds.");
-        }
-
-        final View child = getChildAt(mBaselineAlignedChildIndex);
-        final int childBaseline = child.getBaseline();
-
-        if (childBaseline == -1) {
-            if (mBaselineAlignedChildIndex == 0) {
-                // this is just the default case, safe to return -1
-                return -1;
-            }
-            // the user picked an index that points to something that doesn't
-            // know how to calculate its baseline.
-            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
-                    + "points to a View that doesn't know how to get its baseline.");
-        }
-
-        // TODO: This should try to take into account the virtual offsets
-        // (See getNextLocationOffset and getLocationOffset)
-        // We should add to childTop:
-        // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
-        // and also add:
-        // getLocationOffset(child)
-        int childTop = mBaselineChildTop;
-
-        if (mOrientation == VERTICAL) {
-            final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-            if (majorGravity != Gravity.TOP) {
-                switch (majorGravity) {
-                    case Gravity.BOTTOM:
-                        childTop = getBottom() - getTop() - getPaddingBottom() - mTotalLength;
-                        break;
-
-                    case Gravity.CENTER_VERTICAL:
-                        childTop += ((getBottom() - getTop() - getPaddingTop() - getPaddingBottom()) -
-                                mTotalLength) / 2;
-                        break;
-                }
-            }
-        }
-
-        LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-        return childTop + lp.topMargin + childBaseline;
-    }
-
-    /**
-     * @return The index of the child that will be used if this layout is
-     *   part of a larger layout that is baseline aligned, or -1 if none has
-     *   been set.
-     */
-    public int getBaselineAlignedChildIndex() {
-        return mBaselineAlignedChildIndex;
-    }
-
-    /**
-     * @param i The index of the child that will be used if this layout is
-     *          part of a larger layout that is baseline aligned.
-     */
-    public void setBaselineAlignedChildIndex(int i) {
-        if ((i < 0) || (i >= getChildCount())) {
-            throw new IllegalArgumentException("base aligned child index out "
-                    + "of range (0, " + getChildCount() + ")");
-        }
-        mBaselineAlignedChildIndex = i;
-    }
-
-    /**
-     * <p>Returns the view at the specified index. This method can be overridden
-     * to take into account virtual children. Refer to
-     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
-     * for an example.</p>
-     *
-     * @param index the child's index
-     * @return the child at the specified index
-     */
-    View getVirtualChildAt(int index) {
-        return getChildAt(index);
-    }
-
-    /**
-     * <p>Returns the virtual number of children. This number might be different
-     * than the actual number of children if the layout can hold virtual
-     * children. Refer to
-     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
-     * for an example.</p>
-     *
-     * @return the virtual number of children
-     */
-    int getVirtualChildCount() {
-        return getChildCount();
-    }
-
-    /**
-     * Returns the desired weights sum.
-     *
-     * @return A number greater than 0.0f if the weight sum is defined, or
-     *         a number lower than or equals to 0.0f if not weight sum is
-     *         to be used.
-     */
-    public float getWeightSum() {
-        return mWeightSum;
-    }
-
-    /**
-     * Defines the desired weights sum. If unspecified the weights sum is computed
-     * at layout time by adding the layout_weight of each child.
-     *
-     * This can be used for instance to give a single child 50% of the total
-     * available space by giving it a layout_weight of 0.5 and setting the
-     * weightSum to 1.0.
-     *
-     * @param weightSum a number greater than 0.0f, or a number lower than or equals
-     *        to 0.0f if the weight sum should be computed from the children's
-     *        layout_weight
-     */
-    public void setWeightSum(float weightSum) {
-        mWeightSum = Math.max(0.0f, weightSum);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mOrientation == VERTICAL) {
-            measureVertical(widthMeasureSpec, heightMeasureSpec);
-        } else {
-            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
-        }
-    }
-
-    /**
-     * Determines where to position dividers between children.
-     *
-     * @param childIndex Index of child to check for preceding divider
-     * @return true if there should be a divider before the child at childIndex
-     * @hide Pending API consideration. Currently only used internally by the system.
-     */
-    @RestrictTo(LIBRARY)
-    protected boolean hasDividerBeforeChildAt(int childIndex) {
-        if (childIndex == 0) {
-            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
-        } else if (childIndex == getChildCount()) {
-            return (mShowDividers & SHOW_DIVIDER_END) != 0;
-        } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
-            boolean hasVisibleViewBefore = false;
-            for (int i = childIndex - 1; i >= 0; i--) {
-                if (getChildAt(i).getVisibility() != GONE) {
-                    hasVisibleViewBefore = true;
-                    break;
-                }
-            }
-            return hasVisibleViewBefore;
-        }
-        return false;
-    }
-
-    /**
-     * Measures the children when the orientation of this LinearLayout is set
-     * to {@link #VERTICAL}.
-     *
-     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
-     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
-     *
-     * @see #getOrientation()
-     * @see #setOrientation(int)
-     * @see #onMeasure(int, int)
-     */
-    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
-        mTotalLength = 0;
-        int maxWidth = 0;
-        int childState = 0;
-        int alternativeMaxWidth = 0;
-        int weightedMaxWidth = 0;
-        boolean allFillParent = true;
-        float totalWeight = 0;
-
-        final int count = getVirtualChildCount();
-
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-
-        boolean matchWidth = false;
-        boolean skippedMeasure = false;
-
-        final int baselineChildIndex = mBaselineAlignedChildIndex;
-        final boolean useLargestChild = mUseLargestChild;
-
-        int largestChildHeight = 0;
-
-        // See how tall everyone is. Also remember max width.
-        for (int i = 0; i < count; ++i) {
-            final View child = getVirtualChildAt(i);
-
-            if (child == null) {
-                mTotalLength += measureNullChild(i);
-                continue;
-            }
-
-            if (child.getVisibility() == View.GONE) {
-                i += getChildrenSkipCount(child, i);
-                continue;
-            }
-
-            if (hasDividerBeforeChildAt(i)) {
-                mTotalLength += mDividerHeight;
-            }
-
-            LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-            totalWeight += lp.weight;
-
-            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
-                // Optimization: don't bother measuring children who are going to use
-                // leftover space. These views will get measured again down below if
-                // there is any leftover space.
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
-                skippedMeasure = true;
-            } else {
-                int oldHeight = Integer.MIN_VALUE;
-
-                if (lp.height == 0 && lp.weight > 0) {
-                    // heightMode is either UNSPECIFIED or AT_MOST, and this
-                    // child wanted to stretch to fill available space.
-                    // Translate that to WRAP_CONTENT so that it does not end up
-                    // with a height of 0
-                    oldHeight = 0;
-                    lp.height = LayoutParams.WRAP_CONTENT;
-                }
-
-                // Determine how big this child would like to be. If this or
-                // previous children have given a weight, then we allow it to
-                // use all available space (and we will shrink things later
-                // if needed).
-                measureChildBeforeLayout(
-                        child, i, widthMeasureSpec, 0, heightMeasureSpec,
-                        totalWeight == 0 ? mTotalLength : 0);
-
-                if (oldHeight != Integer.MIN_VALUE) {
-                    lp.height = oldHeight;
-                }
-
-                final int childHeight = child.getMeasuredHeight();
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
-                        lp.bottomMargin + getNextLocationOffset(child));
-
-                if (useLargestChild) {
-                    largestChildHeight = Math.max(childHeight, largestChildHeight);
-                }
-            }
-
-            /**
-             * If applicable, compute the additional offset to the child's baseline
-             * we'll need later when asked {@link #getBaseline}.
-             */
-            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
-                mBaselineChildTop = mTotalLength;
-            }
-
-            // if we are trying to use a child index for our baseline, the above
-            // book keeping only works if there are no children above it with
-            // weight.  fail fast to aid the developer.
-            if (i < baselineChildIndex && lp.weight > 0) {
-                throw new RuntimeException("A child of LinearLayout with index "
-                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
-                        + "won't work.  Either remove the weight, or don't set "
-                        + "mBaselineAlignedChildIndex.");
-            }
-
-            boolean matchWidthLocally = false;
-            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
-                // The width of the linear layout will scale, and at least one
-                // child said it wanted to match our width. Set a flag
-                // indicating that we need to remeasure at least that view when
-                // we know our width.
-                matchWidth = true;
-                matchWidthLocally = true;
-            }
-
-            final int margin = lp.leftMargin + lp.rightMargin;
-            final int measuredWidth = child.getMeasuredWidth() + margin;
-            maxWidth = Math.max(maxWidth, measuredWidth);
-            childState = View.combineMeasuredStates(childState,
-                    child.getMeasuredState());
-
-            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
-            if (lp.weight > 0) {
-                /*
-                 * Widths of weighted Views are bogus if we end up
-                 * remeasuring, so keep them separate.
-                 */
-                weightedMaxWidth = Math.max(weightedMaxWidth,
-                        matchWidthLocally ? margin : measuredWidth);
-            } else {
-                alternativeMaxWidth = Math.max(alternativeMaxWidth,
-                        matchWidthLocally ? margin : measuredWidth);
-            }
-
-            i += getChildrenSkipCount(child, i);
-        }
-
-        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
-            mTotalLength += mDividerHeight;
-        }
-
-        if (useLargestChild &&
-                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
-            mTotalLength = 0;
-
-            for (int i = 0; i < count; ++i) {
-                final View child = getVirtualChildAt(i);
-
-                if (child == null) {
-                    mTotalLength += measureNullChild(i);
-                    continue;
-                }
-
-                if (child.getVisibility() == GONE) {
-                    i += getChildrenSkipCount(child, i);
-                    continue;
-                }
-
-                final LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams)
-                        child.getLayoutParams();
-                // Account for negative margins
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
-                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
-            }
-        }
-
-        // Add in our padding
-        mTotalLength += getPaddingTop() + getPaddingBottom();
-
-        int heightSize = mTotalLength;
-
-        // Check against our minimum height
-        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
-
-        // Reconcile our calculated size with the heightMeasureSpec
-        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)) {
-            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
-
-            mTotalLength = 0;
-
-            for (int i = 0; i < count; ++i) {
-                final View child = getVirtualChildAt(i);
-
-                if (child.getVisibility() == View.GONE) {
-                    continue;
-                }
-
-                LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                float childExtra = lp.weight;
-                if (childExtra > 0) {
-                    // Child said it could absorb extra space -- give him his share
-                    int share = (int) (childExtra * delta / weightSum);
-                    weightSum -= childExtra;
-                    delta -= share;
-
-                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
-                            getPaddingLeft() + getPaddingRight() +
-                                    lp.leftMargin + lp.rightMargin, lp.width);
-
-                    // TODO: Use a field like lp.isMeasured to figure out if this
-                    // child has been previously measured
-                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
-                        // child was measured once already above...
-                        // base new measurement on stored values
-                        int childHeight = child.getMeasuredHeight() + share;
-                        if (childHeight < 0) {
-                            childHeight = 0;
-                        }
-
-                        child.measure(childWidthMeasureSpec,
-                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
-                    } else {
-                        // child was skipped in the loop above.
-                        // Measure for this first time here
-                        child.measure(childWidthMeasureSpec,
-                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
-                                        MeasureSpec.EXACTLY));
-                    }
-
-                    // Child may now not fit in vertical dimension.
-                    childState = View.combineMeasuredStates(childState,
-                            child.getMeasuredState() & (View.MEASURED_STATE_MASK
-                                    >> View.MEASURED_HEIGHT_STATE_SHIFT));
-                }
-
-                final int margin =  lp.leftMargin + lp.rightMargin;
-                final int measuredWidth = child.getMeasuredWidth() + margin;
-                maxWidth = Math.max(maxWidth, measuredWidth);
-
-                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
-                        lp.width == LayoutParams.MATCH_PARENT;
-
-                alternativeMaxWidth = Math.max(alternativeMaxWidth,
-                        matchWidthLocally ? margin : measuredWidth);
-
-                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
-
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
-                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
-            }
-
-            // Add in our padding
-            mTotalLength += getPaddingTop() + getPaddingBottom();
-            // TODO: Should we recompute the heightSpec based on the new total length?
-        } else {
-            alternativeMaxWidth = Math.max(alternativeMaxWidth,
-                    weightedMaxWidth);
-
-
-            // We have no limit, so make all weighted views as tall as the largest child.
-            // Children will have already been measured once.
-            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
-                for (int i = 0; i < count; i++) {
-                    final View child = getVirtualChildAt(i);
-
-                    if (child == null || child.getVisibility() == View.GONE) {
-                        continue;
-                    }
-
-                    final LinearLayoutCompat.LayoutParams lp =
-                            (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                    float childExtra = lp.weight;
-                    if (childExtra > 0) {
-                        child.measure(
-                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
-                                        MeasureSpec.EXACTLY),
-                                MeasureSpec.makeMeasureSpec(largestChildHeight,
-                                        MeasureSpec.EXACTLY));
-                    }
-                }
-            }
-        }
-
-        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
-            maxWidth = alternativeMaxWidth;
-        }
-
-        maxWidth += getPaddingLeft() + getPaddingRight();
-
-        // Check against our minimum width
-        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
-        setMeasuredDimension(View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
-                heightSizeAndState);
-
-        if (matchWidth) {
-            forceUniformWidth(count, heightMeasureSpec);
-        }
-    }
-
-    private void forceUniformWidth(int count, int heightMeasureSpec) {
-        // Pretend that the linear layout has an exact size.
-        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
-                MeasureSpec.EXACTLY);
-        for (int i = 0; i< count; ++i) {
-            final View child = getVirtualChildAt(i);
-            if (child.getVisibility() != GONE) {
-                LinearLayoutCompat.LayoutParams lp = ((LinearLayoutCompat.LayoutParams)child.getLayoutParams());
-
-                if (lp.width == LayoutParams.MATCH_PARENT) {
-                    // Temporarily force children to reuse their old measured height
-                    // FIXME: this may not be right for something like wrapping text?
-                    int oldHeight = lp.height;
-                    lp.height = child.getMeasuredHeight();
-
-                    // Remeasue with new dimensions
-                    measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
-                    lp.height = oldHeight;
-                }
-            }
-        }
-    }
-
-    /**
-     * Measures the children when the orientation of this LinearLayout is set
-     * to {@link #HORIZONTAL}.
-     *
-     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
-     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
-     *
-     * @see #getOrientation()
-     * @see #setOrientation(int)
-     * @see #onMeasure(int, int)
-     */
-    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
-        mTotalLength = 0;
-        int maxHeight = 0;
-        int childState = 0;
-        int alternativeMaxHeight = 0;
-        int weightedMaxHeight = 0;
-        boolean allFillParent = true;
-        float totalWeight = 0;
-
-        final int count = getVirtualChildCount();
-
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-
-        boolean matchHeight = false;
-        boolean skippedMeasure = false;
-
-        if (mMaxAscent == null || mMaxDescent == null) {
-            mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
-            mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
-        }
-
-        final int[] maxAscent = mMaxAscent;
-        final int[] maxDescent = mMaxDescent;
-
-        maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
-        maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
-
-        final boolean baselineAligned = mBaselineAligned;
-        final boolean useLargestChild = mUseLargestChild;
-
-        final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
-
-        int largestChildWidth = 0;
-
-        // See how wide everyone is. Also remember max height.
-        for (int i = 0; i < count; ++i) {
-            final View child = getVirtualChildAt(i);
-
-            if (child == null) {
-                mTotalLength += measureNullChild(i);
-                continue;
-            }
-
-            if (child.getVisibility() == GONE) {
-                i += getChildrenSkipCount(child, i);
-                continue;
-            }
-
-            if (hasDividerBeforeChildAt(i)) {
-                mTotalLength += mDividerWidth;
-            }
-
-            final LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams)
-                    child.getLayoutParams();
-
-            totalWeight += lp.weight;
-
-            if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
-                // Optimization: don't bother measuring children who are going to use
-                // leftover space. These views will get measured again down below if
-                // there is any leftover space.
-                if (isExactly) {
-                    mTotalLength += lp.leftMargin + lp.rightMargin;
-                } else {
-                    final int totalLength = mTotalLength;
-                    mTotalLength = Math.max(totalLength, totalLength +
-                            lp.leftMargin + lp.rightMargin);
-                }
-
-                // Baseline alignment requires to measure widgets to obtain the
-                // baseline offset (in particular for TextViews). The following
-                // defeats the optimization mentioned above. Allow the child to
-                // use as much space as it wants because we can shrink things
-                // later (and re-measure).
-                if (baselineAligned) {
-                    final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-                    child.measure(freeSpec, freeSpec);
-                } else {
-                    skippedMeasure = true;
-                }
-            } else {
-                int oldWidth = Integer.MIN_VALUE;
-
-                if (lp.width == 0 && lp.weight > 0) {
-                    // widthMode is either UNSPECIFIED or AT_MOST, and this
-                    // child
-                    // wanted to stretch to fill available space. Translate that to
-                    // WRAP_CONTENT so that it does not end up with a width of 0
-                    oldWidth = 0;
-                    lp.width = LayoutParams.WRAP_CONTENT;
-                }
-
-                // Determine how big this child would like to be. If this or
-                // previous children have given a weight, then we allow it to
-                // use all available space (and we will shrink things later
-                // if needed).
-                measureChildBeforeLayout(child, i, widthMeasureSpec,
-                        totalWeight == 0 ? mTotalLength : 0,
-                        heightMeasureSpec, 0);
-
-                if (oldWidth != Integer.MIN_VALUE) {
-                    lp.width = oldWidth;
-                }
-
-                final int childWidth = child.getMeasuredWidth();
-                if (isExactly) {
-                    mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
-                            getNextLocationOffset(child);
-                } else {
-                    final int totalLength = mTotalLength;
-                    mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
-                            lp.rightMargin + getNextLocationOffset(child));
-                }
-
-                if (useLargestChild) {
-                    largestChildWidth = Math.max(childWidth, largestChildWidth);
-                }
-            }
-
-            boolean matchHeightLocally = false;
-            if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
-                // The height of the linear layout will scale, and at least one
-                // child said it wanted to match our height. Set a flag indicating that
-                // we need to remeasure at least that view when we know our height.
-                matchHeight = true;
-                matchHeightLocally = true;
-            }
-
-            final int margin = lp.topMargin + lp.bottomMargin;
-            final int childHeight = child.getMeasuredHeight() + margin;
-            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
-
-            if (baselineAligned) {
-                final int childBaseline = child.getBaseline();
-                if (childBaseline != -1) {
-                    // Translates the child's vertical gravity into an index
-                    // in the range 0..VERTICAL_GRAVITY_COUNT
-                    final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
-                            & Gravity.VERTICAL_GRAVITY_MASK;
-                    final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
-                            & ~Gravity.AXIS_SPECIFIED) >> 1;
-
-                    maxAscent[index] = Math.max(maxAscent[index], childBaseline);
-                    maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
-                }
-            }
-
-            maxHeight = Math.max(maxHeight, childHeight);
-
-            allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
-            if (lp.weight > 0) {
-                /*
-                 * Heights of weighted Views are bogus if we end up
-                 * remeasuring, so keep them separate.
-                 */
-                weightedMaxHeight = Math.max(weightedMaxHeight,
-                        matchHeightLocally ? margin : childHeight);
-            } else {
-                alternativeMaxHeight = Math.max(alternativeMaxHeight,
-                        matchHeightLocally ? margin : childHeight);
-            }
-
-            i += getChildrenSkipCount(child, i);
-        }
-
-        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
-            mTotalLength += mDividerWidth;
-        }
-
-        // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
-        // the most common case
-        if (maxAscent[INDEX_TOP] != -1 ||
-                maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
-                maxAscent[INDEX_BOTTOM] != -1 ||
-                maxAscent[INDEX_FILL] != -1) {
-            final int ascent = Math.max(maxAscent[INDEX_FILL],
-                    Math.max(maxAscent[INDEX_CENTER_VERTICAL],
-                            Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
-            final int descent = Math.max(maxDescent[INDEX_FILL],
-                    Math.max(maxDescent[INDEX_CENTER_VERTICAL],
-                            Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
-            maxHeight = Math.max(maxHeight, ascent + descent);
-        }
-
-        if (useLargestChild &&
-                (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
-            mTotalLength = 0;
-
-            for (int i = 0; i < count; ++i) {
-                final View child = getVirtualChildAt(i);
-
-                if (child == null) {
-                    mTotalLength += measureNullChild(i);
-                    continue;
-                }
-
-                if (child.getVisibility() == GONE) {
-                    i += getChildrenSkipCount(child, i);
-                    continue;
-                }
-
-                final LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams)
-                        child.getLayoutParams();
-                if (isExactly) {
-                    mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
-                            getNextLocationOffset(child);
-                } else {
-                    final int totalLength = mTotalLength;
-                    mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
-                            lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
-                }
-            }
-        }
-
-        // Add in our padding
-        mTotalLength += getPaddingLeft() + getPaddingRight();
-
-        int widthSize = mTotalLength;
-
-        // Check against our minimum width
-        widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
-
-        // Reconcile our calculated size with the widthMeasureSpec
-        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)) {
-            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
-
-            maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
-            maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
-            maxHeight = -1;
-
-            mTotalLength = 0;
-
-            for (int i = 0; i < count; ++i) {
-                final View child = getVirtualChildAt(i);
-
-                if (child == null || child.getVisibility() == View.GONE) {
-                    continue;
-                }
-
-                final LinearLayoutCompat.LayoutParams lp =
-                        (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                float childExtra = lp.weight;
-                if (childExtra > 0) {
-                    // Child said it could absorb extra space -- give him his share
-                    int share = (int) (childExtra * delta / weightSum);
-                    weightSum -= childExtra;
-                    delta -= share;
-
-                    final int childHeightMeasureSpec = getChildMeasureSpec(
-                            heightMeasureSpec,
-                            getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin,
-                            lp.height);
-
-                    // TODO: Use a field like lp.isMeasured to figure out if this
-                    // child has been previously measured
-                    if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
-                        // child was measured once already above ... base new measurement
-                        // on stored values
-                        int childWidth = child.getMeasuredWidth() + share;
-                        if (childWidth < 0) {
-                            childWidth = 0;
-                        }
-
-                        child.measure(
-                                MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
-                                childHeightMeasureSpec);
-                    } else {
-                        // child was skipped in the loop above. Measure for this first time here
-                        child.measure(MeasureSpec.makeMeasureSpec(
-                                        share > 0 ? share : 0, MeasureSpec.EXACTLY),
-                                childHeightMeasureSpec);
-                    }
-
-                    // Child may now not fit in horizontal dimension.
-                    childState = View.combineMeasuredStates(childState,
-                            child.getMeasuredState() & View.MEASURED_STATE_MASK);
-                }
-
-                if (isExactly) {
-                    mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
-                            getNextLocationOffset(child);
-                } else {
-                    final int totalLength = mTotalLength;
-                    mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
-                            lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
-                }
-
-                boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
-                        lp.height == LayoutParams.MATCH_PARENT;
-
-                final int margin = lp.topMargin + lp .bottomMargin;
-                int childHeight = child.getMeasuredHeight() + margin;
-                maxHeight = Math.max(maxHeight, childHeight);
-                alternativeMaxHeight = Math.max(alternativeMaxHeight,
-                        matchHeightLocally ? margin : childHeight);
-
-                allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
-
-                if (baselineAligned) {
-                    final int childBaseline = child.getBaseline();
-                    if (childBaseline != -1) {
-                        // Translates the child's vertical gravity into an index in the range 0..2
-                        final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
-                                & Gravity.VERTICAL_GRAVITY_MASK;
-                        final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
-                                & ~Gravity.AXIS_SPECIFIED) >> 1;
-
-                        maxAscent[index] = Math.max(maxAscent[index], childBaseline);
-                        maxDescent[index] = Math.max(maxDescent[index],
-                                childHeight - childBaseline);
-                    }
-                }
-            }
-
-            // Add in our padding
-            mTotalLength += getPaddingLeft() + getPaddingRight();
-            // TODO: Should we update widthSize with the new total length?
-
-            // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
-            // the most common case
-            if (maxAscent[INDEX_TOP] != -1 ||
-                    maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
-                    maxAscent[INDEX_BOTTOM] != -1 ||
-                    maxAscent[INDEX_FILL] != -1) {
-                final int ascent = Math.max(maxAscent[INDEX_FILL],
-                        Math.max(maxAscent[INDEX_CENTER_VERTICAL],
-                                Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
-                final int descent = Math.max(maxDescent[INDEX_FILL],
-                        Math.max(maxDescent[INDEX_CENTER_VERTICAL],
-                                Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
-                maxHeight = Math.max(maxHeight, ascent + descent);
-            }
-        } else {
-            alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
-
-            // We have no limit, so make all weighted views as wide as the largest child.
-            // Children will have already been measured once.
-            if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
-                for (int i = 0; i < count; i++) {
-                    final View child = getVirtualChildAt(i);
-
-                    if (child == null || child.getVisibility() == View.GONE) {
-                        continue;
-                    }
-
-                    final LinearLayoutCompat.LayoutParams lp =
-                            (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                    float childExtra = lp.weight;
-                    if (childExtra > 0) {
-                        child.measure(
-                                MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
-                                MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
-                                        MeasureSpec.EXACTLY));
-                    }
-                }
-            }
-        }
-
-        if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
-            maxHeight = alternativeMaxHeight;
-        }
-
-        maxHeight += getPaddingTop() + getPaddingBottom();
-
-        // Check against our minimum height
-        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
-
-        setMeasuredDimension(widthSizeAndState | (childState & View.MEASURED_STATE_MASK),
-                View.resolveSizeAndState(maxHeight, heightMeasureSpec,
-                        (childState << View.MEASURED_HEIGHT_STATE_SHIFT)));
-
-        if (matchHeight) {
-            forceUniformHeight(count, widthMeasureSpec);
-        }
-    }
-
-    private void forceUniformHeight(int count, int widthMeasureSpec) {
-        // Pretend that the linear layout has an exact size. This is the measured height of
-        // ourselves. The measured height should be the max height of the children, changed
-        // to accommodate the heightMeasureSpec from the parent
-        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
-                MeasureSpec.EXACTLY);
-        for (int i = 0; i < count; ++i) {
-            final View child = getVirtualChildAt(i);
-            if (child.getVisibility() != GONE) {
-                LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                if (lp.height == LayoutParams.MATCH_PARENT) {
-                    // Temporarily force children to reuse their old measured width
-                    // FIXME: this may not be right for something like wrapping text?
-                    int oldWidth = lp.width;
-                    lp.width = child.getMeasuredWidth();
-
-                    // Remeasure with new dimensions
-                    measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
-                    lp.width = oldWidth;
-                }
-            }
-        }
-    }
-
-    /**
-     * <p>Returns the number of children to skip after measuring/laying out
-     * the specified child.</p>
-     *
-     * @param child the child after which we want to skip children
-     * @param index the index of the child after which we want to skip children
-     * @return the number of children to skip, 0 by default
-     */
-    int getChildrenSkipCount(View child, int index) {
-        return 0;
-    }
-
-    /**
-     * <p>Returns the size (width or height) that should be occupied by a null
-     * child.</p>
-     *
-     * @param childIndex the index of the null child
-     * @return the width or height of the child depending on the orientation
-     */
-    int measureNullChild(int childIndex) {
-        return 0;
-    }
-
-    /**
-     * <p>Measure the child according to the parent's measure specs. This
-     * method should be overridden by subclasses to force the sizing of
-     * children. This method is called by {@link #measureVertical(int, int)} and
-     * {@link #measureHorizontal(int, int)}.</p>
-     *
-     * @param child the child to measure
-     * @param childIndex the index of the child in this view
-     * @param widthMeasureSpec horizontal space requirements as imposed by the parent
-     * @param totalWidth extra space that has been used up by the parent horizontally
-     * @param heightMeasureSpec vertical space requirements as imposed by the parent
-     * @param totalHeight extra space that has been used up by the parent vertically
-     */
-    void measureChildBeforeLayout(View child, int childIndex,
-            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
-            int totalHeight) {
-        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
-                heightMeasureSpec, totalHeight);
-    }
-
-    /**
-     * <p>Return the location offset of the specified child. This can be used
-     * by subclasses to change the location of a given widget.</p>
-     *
-     * @param child the child for which to obtain the location offset
-     * @return the location offset in pixels
-     */
-    int getLocationOffset(View child) {
-        return 0;
-    }
-
-    /**
-     * <p>Return the size offset of the next sibling of the specified child.
-     * This can be used by subclasses to change the location of the widget
-     * following <code>child</code>.</p>
-     *
-     * @param child the child whose next sibling will be moved
-     * @return the location offset of the next child in pixels
-     */
-    int getNextLocationOffset(View child) {
-        return 0;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        if (mOrientation == VERTICAL) {
-            layoutVertical(l, t, r, b);
-        } else {
-            layoutHorizontal(l, t, r, b);
-        }
-    }
-
-    /**
-     * Position the children during a layout pass if the orientation of this
-     * LinearLayout is set to {@link #VERTICAL}.
-     *
-     * @see #getOrientation()
-     * @see #setOrientation(int)
-     * @see #onLayout(boolean, int, int, int, int)
-     * @param left
-     * @param top
-     * @param right
-     * @param bottom
-     */
-    void layoutVertical(int left, int top, int right, int bottom) {
-        final int paddingLeft = getPaddingLeft();
-
-        int childTop;
-        int childLeft;
-
-        // Where right end of child should go
-        final int width = right - left;
-        int childRight = width - getPaddingRight();
-
-        // Space available for child
-        int childSpace = width - paddingLeft - getPaddingRight();
-
-        final int count = getVirtualChildCount();
-
-        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int minorGravity = mGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
-
-        switch (majorGravity) {
-            case Gravity.BOTTOM:
-                // mTotalLength contains the padding already
-                childTop = getPaddingTop() + bottom - top - mTotalLength;
-                break;
-
-            // mTotalLength contains the padding already
-            case Gravity.CENTER_VERTICAL:
-                childTop = getPaddingTop() + (bottom - top - mTotalLength) / 2;
-                break;
-
-            case Gravity.TOP:
-            default:
-                childTop = getPaddingTop();
-                break;
-        }
-
-        for (int i = 0; i < count; i++) {
-            final View child = getVirtualChildAt(i);
-            if (child == null) {
-                childTop += measureNullChild(i);
-            } else if (child.getVisibility() != GONE) {
-                final int childWidth = child.getMeasuredWidth();
-                final int childHeight = child.getMeasuredHeight();
-
-                final LinearLayoutCompat.LayoutParams lp =
-                        (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                int gravity = lp.gravity;
-                if (gravity < 0) {
-                    gravity = minorGravity;
-                }
-                final int layoutDirection = ViewCompat.getLayoutDirection(this);
-                final int absoluteGravity = GravityCompat.getAbsoluteGravity(gravity,
-                        layoutDirection);
-                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-                    case Gravity.CENTER_HORIZONTAL:
-                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
-                                + lp.leftMargin - lp.rightMargin;
-                        break;
-
-                    case Gravity.RIGHT:
-                        childLeft = childRight - childWidth - lp.rightMargin;
-                        break;
-
-                    case Gravity.LEFT:
-                    default:
-                        childLeft = paddingLeft + lp.leftMargin;
-                        break;
-                }
-
-                if (hasDividerBeforeChildAt(i)) {
-                    childTop += mDividerHeight;
-                }
-
-                childTop += lp.topMargin;
-                setChildFrame(child, childLeft, childTop + getLocationOffset(child),
-                        childWidth, childHeight);
-                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
-
-                i += getChildrenSkipCount(child, i);
-            }
-        }
-    }
-
-    /**
-     * Position the children during a layout pass if the orientation of this
-     * LinearLayout is set to {@link #HORIZONTAL}.
-     *
-     * @see #getOrientation()
-     * @see #setOrientation(int)
-     * @see #onLayout(boolean, int, int, int, int)
-     * @param left
-     * @param top
-     * @param right
-     * @param bottom
-     */
-    void layoutHorizontal(int left, int top, int right, int bottom) {
-        final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
-        final int paddingTop = getPaddingTop();
-
-        int childTop;
-        int childLeft;
-
-        // Where bottom of child should go
-        final int height = bottom - top;
-        int childBottom = height - getPaddingBottom();
-
-        // Space available for child
-        int childSpace = height - paddingTop - getPaddingBottom();
-
-        final int count = getVirtualChildCount();
-
-        final int majorGravity = mGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
-        final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-
-        final boolean baselineAligned = mBaselineAligned;
-
-        final int[] maxAscent = mMaxAscent;
-        final int[] maxDescent = mMaxDescent;
-
-        final int layoutDirection = ViewCompat.getLayoutDirection(this);
-        switch (GravityCompat.getAbsoluteGravity(majorGravity, layoutDirection)) {
-            case Gravity.RIGHT:
-                // mTotalLength contains the padding already
-                childLeft = getPaddingLeft() + right - left - mTotalLength;
-                break;
-
-            case Gravity.CENTER_HORIZONTAL:
-                // mTotalLength contains the padding already
-                childLeft = getPaddingLeft() + (right - left - mTotalLength) / 2;
-                break;
-
-            case Gravity.LEFT:
-            default:
-                childLeft = getPaddingLeft();
-                break;
-        }
-
-        int start = 0;
-        int dir = 1;
-        //In case of RTL, start drawing from the last child.
-        if (isLayoutRtl) {
-            start = count - 1;
-            dir = -1;
-        }
-
-        for (int i = 0; i < count; i++) {
-            int childIndex = start + dir * i;
-            final View child = getVirtualChildAt(childIndex);
-
-            if (child == null) {
-                childLeft += measureNullChild(childIndex);
-            } else if (child.getVisibility() != GONE) {
-                final int childWidth = child.getMeasuredWidth();
-                final int childHeight = child.getMeasuredHeight();
-                int childBaseline = -1;
-
-                final LinearLayoutCompat.LayoutParams lp =
-                        (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
-
-                if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
-                    childBaseline = child.getBaseline();
-                }
-
-                int gravity = lp.gravity;
-                if (gravity < 0) {
-                    gravity = minorGravity;
-                }
-
-                switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
-                    case Gravity.TOP:
-                        childTop = paddingTop + lp.topMargin;
-                        if (childBaseline != -1) {
-                            childTop += maxAscent[INDEX_TOP] - childBaseline;
-                        }
-                        break;
-
-                    case Gravity.CENTER_VERTICAL:
-                        // Removed support for baseline alignment when layout_gravity or
-                        // gravity == center_vertical. See bug #1038483.
-                        // Keep the code around if we need to re-enable this feature
-                        // if (childBaseline != -1) {
-                        //     // Align baselines vertically only if the child is smaller than us
-                        //     if (childSpace - childHeight > 0) {
-                        //         childTop = paddingTop + (childSpace / 2) - childBaseline;
-                        //     } else {
-                        //         childTop = paddingTop + (childSpace - childHeight) / 2;
-                        //     }
-                        // } else {
-                        childTop = paddingTop + ((childSpace - childHeight) / 2)
-                                + lp.topMargin - lp.bottomMargin;
-                        break;
-
-                    case Gravity.BOTTOM:
-                        childTop = childBottom - childHeight - lp.bottomMargin;
-                        if (childBaseline != -1) {
-                            int descent = child.getMeasuredHeight() - childBaseline;
-                            childTop -= (maxDescent[INDEX_BOTTOM] - descent);
-                        }
-                        break;
-                    default:
-                        childTop = paddingTop;
-                        break;
-                }
-
-                if (hasDividerBeforeChildAt(childIndex)) {
-                    childLeft += mDividerWidth;
-                }
-
-                childLeft += lp.leftMargin;
-                setChildFrame(child, childLeft + getLocationOffset(child), childTop,
-                        childWidth, childHeight);
-                childLeft += childWidth + lp.rightMargin +
-                        getNextLocationOffset(child);
-
-                i += getChildrenSkipCount(child, childIndex);
-            }
-        }
-    }
-
-    private void setChildFrame(View child, int left, int top, int width, int height) {
-        child.layout(left, top, left + width, top + height);
-    }
-
-    /**
-     * Should the layout be a column or a row.
-     * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
-     * value is {@link #HORIZONTAL}.
-     */
-    public void setOrientation(@OrientationMode int orientation) {
-        if (mOrientation != orientation) {
-            mOrientation = orientation;
-            requestLayout();
-        }
-    }
-
-    /**
-     * Returns the current orientation.
-     *
-     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
-     */
-    @OrientationMode
-    public int getOrientation() {
-        return mOrientation;
-    }
-
-    /**
-     * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
-     * this layout has a VERTICAL orientation, this controls where all the child
-     * views are placed if there is extra vertical space. If this layout has a
-     * HORIZONTAL orientation, this controls the alignment of the children.
-     *
-     * @param gravity See {@link android.view.Gravity}
-     */
-    public void setGravity(int gravity) {
-        if (mGravity != gravity) {
-            if ((gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= GravityCompat.START;
-            }
-
-            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.TOP;
-            }
-
-            mGravity = gravity;
-            requestLayout();
-        }
-    }
-
-    /**
-     * Returns the current gravity. See {@link android.view.Gravity}
-     *
-     * @return the current gravity.
-     * @see #setGravity
-     */
-    public int getGravity() {
-        return mGravity;
-    }
-
-    public void setHorizontalGravity(int horizontalGravity) {
-        final int gravity = horizontalGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
-        if ((mGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
-            mGravity = (mGravity & ~GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
-            requestLayout();
-        }
-    }
-
-    public void setVerticalGravity(int verticalGravity) {
-        final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
-            mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
-            requestLayout();
-        }
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LinearLayoutCompat.LayoutParams(getContext(), attrs);
-    }
-
-    /**
-     * Returns a set of layout parameters with a width of
-     * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
-     * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
-     * when the layout's orientation is {@link #VERTICAL}. When the orientation is
-     * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
-     * and the height to {@link LayoutParams#WRAP_CONTENT}.
-     */
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        if (mOrientation == HORIZONTAL) {
-            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-        } else if (mOrientation == VERTICAL) {
-            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-        }
-        return null;
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        return new LayoutParams(p);
-    }
-
-
-    // Override to allow type-checking of LayoutParams.
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LinearLayoutCompat.LayoutParams;
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setClassName(LinearLayoutCompat.class.getName());
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(LinearLayoutCompat.class.getName());
-    }
-
-    /**
-     * Per-child layout information associated with ViewLinearLayout.
-     */
-    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
-        /**
-         * Indicates how much of the extra space in the LinearLayout will be
-         * allocated to the view associated with these LayoutParams. Specify
-         * 0 if the view should not be stretched. Otherwise the extra pixels
-         * will be pro-rated among all views whose weight is greater than 0.
-         */
-        public float weight;
-
-        /**
-         * Gravity for the view associated with these LayoutParams.
-         *
-         * @see android.view.Gravity
-         */
-        public int gravity = -1;
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-            TypedArray a =
-                    c.obtainStyledAttributes(attrs, R.styleable.LinearLayoutCompat_Layout);
-
-            weight = a.getFloat(R.styleable.LinearLayoutCompat_Layout_android_layout_weight, 0);
-            gravity = a.getInt(R.styleable.LinearLayoutCompat_Layout_android_layout_gravity, -1);
-
-            a.recycle();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(int width, int height) {
-            super(width, height);
-            weight = 0;
-        }
-
-        /**
-         * Creates a new set of layout parameters with the specified width, height
-         * and weight.
-         *
-         * @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 weight the weight
-         */
-        public LayoutParams(int width, int height, float weight) {
-            super(width, height);
-            this.weight = weight;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.LayoutParams p) {
-            super(p);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public LayoutParams(ViewGroup.MarginLayoutParams source) {
-            super(source);
-        }
-
-        /**
-         * Copy constructor. Clones the width, height, margin values, weight,
-         * and gravity of the source.
-         *
-         * @param source The layout params to copy from.
-         */
-        public LayoutParams(LayoutParams source) {
-            super(source);
-
-            this.weight = source.weight;
-            this.gravity = source.gravity;
-        }
-    }
-}
diff --git a/android/support/v7/widget/LinearLayoutManager.java b/android/support/v7/widget/LinearLayoutManager.java
deleted file mode 100644
index fe4a37a..0000000
--- a/android/support/v7/widget/LinearLayoutManager.java
+++ /dev/null
@@ -1,2487 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-import static android.support.v7.widget.RecyclerView.VERBOSE_TRACING;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.os.TraceCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.RecyclerView.LayoutParams;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.util.List;
-
-/**
- * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides
- * similar functionality to {@link android.widget.ListView}.
- */
-public class LinearLayoutManager extends RecyclerView.LayoutManager implements
-        ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider {
-
-    private static final String TAG = "LinearLayoutManager";
-
-    static final boolean DEBUG = false;
-
-    public static final int HORIZONTAL = RecyclerView.HORIZONTAL;
-
-    public static final int VERTICAL = RecyclerView.VERTICAL;
-
-    public static final int INVALID_OFFSET = Integer.MIN_VALUE;
-
-
-    /**
-     * While trying to find next view to focus, LayoutManager will not try to scroll more
-     * than this factor times the total space of the list. If layout is vertical, total space is the
-     * height minus padding, if layout is horizontal, total space is the width minus padding.
-     */
-    private static final float MAX_SCROLL_FACTOR = 1 / 3f;
-
-    /**
-     * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
-     */
-    @RecyclerView.Orientation
-    int mOrientation = RecyclerView.DEFAULT_ORIENTATION;
-
-    /**
-     * Helper class that keeps temporary layout state.
-     * It does not keep state after layout is complete but we still keep a reference to re-use
-     * the same object.
-     */
-    private LayoutState mLayoutState;
-
-    /**
-     * Many calculations are made depending on orientation. To keep it clean, this interface
-     * helps {@link LinearLayoutManager} make those decisions.
-     */
-    OrientationHelper mOrientationHelper;
-
-    /**
-     * We need to track this so that we can ignore current position when it changes.
-     */
-    private boolean mLastStackFromEnd;
-
-
-    /**
-     * Defines if layout should be calculated from end to start.
-     *
-     * @see #mShouldReverseLayout
-     */
-    private boolean mReverseLayout = false;
-
-    /**
-     * This keeps the final value for how LayoutManager should start laying out views.
-     * It is calculated by checking {@link #getReverseLayout()} and View's layout direction.
-     * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.
-     */
-    boolean mShouldReverseLayout = false;
-
-    /**
-     * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and
-     * it supports both orientations.
-     * see {@link android.widget.AbsListView#setStackFromBottom(boolean)}
-     */
-    private boolean mStackFromEnd = false;
-
-    /**
-     * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
-     * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
-     */
-    private boolean mSmoothScrollbarEnabled = true;
-
-    /**
-     * When LayoutManager needs to scroll to a position, it sets this variable and requests a
-     * layout which will check this variable and re-layout accordingly.
-     */
-    int mPendingScrollPosition = NO_POSITION;
-
-    /**
-     * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is
-     * called.
-     */
-    int mPendingScrollPositionOffset = INVALID_OFFSET;
-
-    private boolean mRecycleChildrenOnDetach;
-
-    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.
-     * */
-    final AnchorInfo mAnchorInfo = new AnchorInfo();
-
-    /**
-     * Stashed to avoid allocation, currently only used in #fill()
-     */
-    private final LayoutChunkResult mLayoutChunkResult = new LayoutChunkResult();
-
-    /**
-     * Number of items to prefetch when first coming on screen with new data.
-     */
-    private int mInitialPrefetchItemCount = 2;
-
-    /**
-     * Creates a vertical LinearLayoutManager
-     *
-     * @param context Current context, will be used to access resources.
-     */
-    public LinearLayoutManager(Context context) {
-        this(context, RecyclerView.DEFAULT_ORIENTATION, false);
-    }
-
-    /**
-     * @param context       Current context, will be used to access resources.
-     * @param orientation   Layout orientation. Should be {@link #HORIZONTAL} or {@link
-     *                      #VERTICAL}.
-     * @param reverseLayout When set to true, layouts from end to start.
-     */
-    public LinearLayoutManager(Context context, @RecyclerView.Orientation int orientation,
-            boolean reverseLayout) {
-        setOrientation(orientation);
-        setReverseLayout(reverseLayout);
-        setAutoMeasureEnabled(true);
-    }
-
-    /**
-     * Constructor used when layout manager is set in XML by RecyclerView attribute
-     * "layoutManager". Defaults to vertical orientation.
-     *
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
-     */
-    public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
-        setOrientation(properties.orientation);
-        setReverseLayout(properties.reverseLayout);
-        setStackFromEnd(properties.stackFromEnd);
-        setAutoMeasureEnabled(true);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-    }
-
-    /**
-     * Returns whether LayoutManager will recycle its children when it is detached from
-     * RecyclerView.
-     *
-     * @return true if LayoutManager will recycle its children when it is detached from
-     * RecyclerView.
-     */
-    public boolean getRecycleChildrenOnDetach() {
-        return mRecycleChildrenOnDetach;
-    }
-
-    /**
-     * Set whether LayoutManager will recycle its children when it is detached from
-     * RecyclerView.
-     * <p>
-     * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
-     * this flag to <code>true</code> so that views will be available to other RecyclerViews
-     * immediately.
-     * <p>
-     * Note that, setting this flag will result in a performance drop if RecyclerView
-     * is restored.
-     *
-     * @param recycleChildrenOnDetach Whether children should be recycled in detach or not.
-     */
-    public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
-        mRecycleChildrenOnDetach = recycleChildrenOnDetach;
-    }
-
-    @Override
-    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
-        super.onDetachedFromWindow(view, recycler);
-        if (mRecycleChildrenOnDetach) {
-            removeAndRecycleAllViews(recycler);
-            recycler.clear();
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        if (getChildCount() > 0) {
-            event.setFromIndex(findFirstVisibleItemPosition());
-            event.setToIndex(findLastVisibleItemPosition());
-        }
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        if (mPendingSavedState != null) {
-            return new SavedState(mPendingSavedState);
-        }
-        SavedState state = new SavedState();
-        if (getChildCount() > 0) {
-            ensureLayoutState();
-            boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout;
-            state.mAnchorLayoutFromEnd = didLayoutFromEnd;
-            if (didLayoutFromEnd) {
-                final View refChild = getChildClosestToEnd();
-                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();
-            }
-        } else {
-            state.invalidateAnchor();
-        }
-        return state;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        if (state instanceof SavedState) {
-            mPendingSavedState = (SavedState) state;
-            requestLayout();
-            if (DEBUG) {
-                Log.d(TAG, "loaded saved state");
-            }
-        } else if (DEBUG) {
-            Log.d(TAG, "invalid saved state class");
-        }
-    }
-
-    /**
-     * @return true if {@link #getOrientation()} is {@link #HORIZONTAL}
-     */
-    @Override
-    public boolean canScrollHorizontally() {
-        return mOrientation == HORIZONTAL;
-    }
-
-    /**
-     * @return true if {@link #getOrientation()} is {@link #VERTICAL}
-     */
-    @Override
-    public boolean canScrollVertically() {
-        return mOrientation == VERTICAL;
-    }
-
-    /**
-     * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}
-     */
-    public void setStackFromEnd(boolean stackFromEnd) {
-        assertNotInLayoutOrScroll(null);
-        if (mStackFromEnd == stackFromEnd) {
-            return;
-        }
-        mStackFromEnd = stackFromEnd;
-        requestLayout();
-    }
-
-    public boolean getStackFromEnd() {
-        return mStackFromEnd;
-    }
-
-    /**
-     * Returns the current orientation of the layout.
-     *
-     * @return Current orientation,  either {@link #HORIZONTAL} or {@link #VERTICAL}
-     * @see #setOrientation(int)
-     */
-    @RecyclerView.Orientation
-    public int getOrientation() {
-        return mOrientation;
-    }
-
-    /**
-     * Sets the orientation of the layout. {@link android.support.v7.widget.LinearLayoutManager}
-     * will do its best to keep scroll position.
-     *
-     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
-     */
-    public void setOrientation(@RecyclerView.Orientation int orientation) {
-        if (orientation != HORIZONTAL && orientation != VERTICAL) {
-            throw new IllegalArgumentException("invalid orientation:" + orientation);
-        }
-
-        assertNotInLayoutOrScroll(null);
-
-        if (orientation != mOrientation || mOrientationHelper == null) {
-            mOrientationHelper =
-                    OrientationHelper.createOrientationHelper(this, orientation);
-            mAnchorInfo.mOrientationHelper = mOrientationHelper;
-            mOrientation = orientation;
-            requestLayout();
-        }
-    }
-
-    /**
-     * Calculates the view layout order. (e.g. from end to start or start to end)
-     * RTL layout support is applied automatically. So if layout is RTL and
-     * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
-     */
-    private void resolveShouldLayoutReverse() {
-        // A == B is the same result, but we rather keep it readable
-        if (mOrientation == VERTICAL || !isLayoutRTL()) {
-            mShouldReverseLayout = mReverseLayout;
-        } else {
-            mShouldReverseLayout = !mReverseLayout;
-        }
-    }
-
-    /**
-     * Returns if views are laid out from the opposite direction of the layout.
-     *
-     * @return If layout is reversed or not.
-     * @see #setReverseLayout(boolean)
-     */
-    public boolean getReverseLayout() {
-        return mReverseLayout;
-    }
-
-    /**
-     * Used to reverse item traversal and layout order.
-     * This behaves similar to the layout change for RTL views. When set to true, first item is
-     * laid out at the end of the UI, second item is laid out before it etc.
-     *
-     * For horizontal layouts, it depends on the layout direction.
-     * When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will
-     * layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
-     * from LTR.
-     *
-     * If you are looking for the exact same behavior of
-     * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use
-     * {@link #setStackFromEnd(boolean)}
-     */
-    public void setReverseLayout(boolean reverseLayout) {
-        assertNotInLayoutOrScroll(null);
-        if (reverseLayout == mReverseLayout) {
-            return;
-        }
-        mReverseLayout = reverseLayout;
-        requestLayout();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View findViewByPosition(int position) {
-        final int childCount = getChildCount();
-        if (childCount == 0) {
-            return null;
-        }
-        final int firstChild = getPosition(getChildAt(0));
-        final int viewPosition = position - firstChild;
-        if (viewPosition >= 0 && viewPosition < childCount) {
-            final View child = getChildAt(viewPosition);
-            if (getPosition(child) == position) {
-                return child; // in pre-layout, this may not match
-            }
-        }
-        // fallback to traversal. This might be necessary in pre-layout.
-        return super.findViewByPosition(position);
-    }
-
-    /**
-     * <p>Returns the amount of extra space that should be laid out by LayoutManager.</p>
-     *
-     * <p>By default, {@link android.support.v7.widget.LinearLayoutManager} lays out 1 extra page
-     * of items while smooth scrolling and 0 otherwise. You can override this method to implement
-     * your custom layout pre-cache logic.</p>
-     *
-     * <p><strong>Note:</strong>Laying out invisible elements generally comes with significant
-     * performance cost. It's typically only desirable in places like smooth scrolling to an unknown
-     * location, where 1) the extra content helps LinearLayoutManager know in advance when its
-     * target is approaching, so it can decelerate early and smoothly and 2) while motion is
-     * continuous.</p>
-     *
-     * <p>Extending the extra layout space is especially expensive if done while the user may change
-     * scrolling direction. Changing direction will cause the extra layout space to swap to the
-     * opposite side of the viewport, incurring many rebinds/recycles, unless the cache is large
-     * enough to handle it.</p>
-     *
-     * @return The extra space that should be laid out (in pixels).
-     */
-    protected int getExtraLayoutSpace(RecyclerView.State state) {
-        if (state.hasTargetScrollPosition()) {
-            return mOrientationHelper.getTotalSpace();
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
-            int position) {
-        LinearSmoothScroller linearSmoothScroller =
-                new LinearSmoothScroller(recyclerView.getContext());
-        linearSmoothScroller.setTargetPosition(position);
-        startSmoothScroll(linearSmoothScroller);
-    }
-
-    @Override
-    public PointF computeScrollVectorForPosition(int targetPosition) {
-        if (getChildCount() == 0) {
-            return null;
-        }
-        final int firstChildPos = getPosition(getChildAt(0));
-        final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1;
-        if (mOrientation == HORIZONTAL) {
-            return new PointF(direction, 0);
-        } else {
-            return new PointF(0, direction);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
-        // layout algorithm:
-        // 1) by checking children and other variables, find an anchor coordinate and an anchor
-        //  item position.
-        // 2) fill towards start, stacking from bottom
-        // 3) fill towards end, stacking from top
-        // 4) scroll to fulfill requirements like stack from bottom.
-        // create layout state
-        if (DEBUG) {
-            Log.d(TAG, "is pre layout:" + state.isPreLayout());
-        }
-        if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {
-            if (state.getItemCount() == 0) {
-                removeAndRecycleAllViews(recycler);
-                return;
-            }
-        }
-        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
-            mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
-        }
-
-        ensureLayoutState();
-        mLayoutState.mRecycle = false;
-        // resolve layout direction
-        resolveShouldLayoutReverse();
-
-        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, getPosition(focused));
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Anchor info:" + mAnchorInfo);
-        }
-
-        // LLM may decide to layout items for "extra" pixels to account for scrolling target,
-        // caching or predictive animations.
-        int extraForStart;
-        int extraForEnd;
-        final int extra = getExtraLayoutSpace(state);
-        // If the previous scroll delta was less than zero, the extra space should be laid out
-        // at the start. Otherwise, it should be at the end.
-        if (mLayoutState.mLastScrollDelta >= 0) {
-            extraForEnd = extra;
-            extraForStart = 0;
-        } else {
-            extraForStart = extra;
-            extraForEnd = 0;
-        }
-        extraForStart += mOrientationHelper.getStartAfterPadding();
-        extraForEnd += mOrientationHelper.getEndPadding();
-        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
-            final View existing = findViewByPosition(mPendingScrollPosition);
-            if (existing != null) {
-                final int current;
-                final int upcomingOffset;
-                if (mShouldReverseLayout) {
-                    current = mOrientationHelper.getEndAfterPadding()
-                            - mOrientationHelper.getDecoratedEnd(existing);
-                    upcomingOffset = current - mPendingScrollPositionOffset;
-                } else {
-                    current = mOrientationHelper.getDecoratedStart(existing)
-                            - mOrientationHelper.getStartAfterPadding();
-                    upcomingOffset = mPendingScrollPositionOffset - current;
-                }
-                if (upcomingOffset > 0) {
-                    extraForStart += upcomingOffset;
-                } else {
-                    extraForEnd -= upcomingOffset;
-                }
-            }
-        }
-        int startOffset;
-        int endOffset;
-        final int firstLayoutDirection;
-        if (mAnchorInfo.mLayoutFromEnd) {
-            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
-                    : LayoutState.ITEM_DIRECTION_HEAD;
-        } else {
-            firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
-                    : LayoutState.ITEM_DIRECTION_TAIL;
-        }
-
-        onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
-        detachAndScrapAttachedViews(recycler);
-        mLayoutState.mInfinite = resolveIsInfinite();
-        mLayoutState.mIsPreLayout = state.isPreLayout();
-        if (mAnchorInfo.mLayoutFromEnd) {
-            // fill towards start
-            updateLayoutStateToFillStart(mAnchorInfo);
-            mLayoutState.mExtra = extraForStart;
-            fill(recycler, mLayoutState, state, false);
-            startOffset = mLayoutState.mOffset;
-            final int firstElement = mLayoutState.mCurrentPosition;
-            if (mLayoutState.mAvailable > 0) {
-                extraForEnd += mLayoutState.mAvailable;
-            }
-            // fill towards end
-            updateLayoutStateToFillEnd(mAnchorInfo);
-            mLayoutState.mExtra = extraForEnd;
-            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
-            fill(recycler, mLayoutState, state, false);
-            endOffset = mLayoutState.mOffset;
-
-            if (mLayoutState.mAvailable > 0) {
-                // end could not consume all. add more items towards start
-                extraForStart = mLayoutState.mAvailable;
-                updateLayoutStateToFillStart(firstElement, startOffset);
-                mLayoutState.mExtra = extraForStart;
-                fill(recycler, mLayoutState, state, false);
-                startOffset = mLayoutState.mOffset;
-            }
-        } else {
-            // fill towards end
-            updateLayoutStateToFillEnd(mAnchorInfo);
-            mLayoutState.mExtra = extraForEnd;
-            fill(recycler, mLayoutState, state, false);
-            endOffset = mLayoutState.mOffset;
-            final int lastElement = mLayoutState.mCurrentPosition;
-            if (mLayoutState.mAvailable > 0) {
-                extraForStart += mLayoutState.mAvailable;
-            }
-            // fill towards start
-            updateLayoutStateToFillStart(mAnchorInfo);
-            mLayoutState.mExtra = extraForStart;
-            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
-            fill(recycler, mLayoutState, state, false);
-            startOffset = mLayoutState.mOffset;
-
-            if (mLayoutState.mAvailable > 0) {
-                extraForEnd = mLayoutState.mAvailable;
-                // start could not consume all it should. add more items towards end
-                updateLayoutStateToFillEnd(lastElement, endOffset);
-                mLayoutState.mExtra = extraForEnd;
-                fill(recycler, mLayoutState, state, false);
-                endOffset = mLayoutState.mOffset;
-            }
-        }
-
-        // changes may cause gaps on the UI, try to fix them.
-        // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have
-        // changed
-        if (getChildCount() > 0) {
-            // because layout from end may be changed by scroll to position
-            // we re-calculate it.
-            // find which side we should check for gaps.
-            if (mShouldReverseLayout ^ mStackFromEnd) {
-                int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);
-                startOffset += fixOffset;
-                endOffset += fixOffset;
-                fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);
-                startOffset += fixOffset;
-                endOffset += fixOffset;
-            } else {
-                int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);
-                startOffset += fixOffset;
-                endOffset += fixOffset;
-                fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);
-                startOffset += fixOffset;
-                endOffset += fixOffset;
-            }
-        }
-        layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
-        if (!state.isPreLayout()) {
-            mOrientationHelper.onLayoutComplete();
-        } else {
-            mAnchorInfo.reset();
-        }
-        mLastStackFromEnd = mStackFromEnd;
-        if (DEBUG) {
-            validateChildOrder();
-        }
-    }
-
-    @Override
-    public void onLayoutCompleted(RecyclerView.State state) {
-        super.onLayoutCompleted(state);
-        mPendingSavedState = null; // we don't need this anymore
-        mPendingScrollPosition = NO_POSITION;
-        mPendingScrollPositionOffset = INVALID_OFFSET;
-        mAnchorInfo.reset();
-    }
-
-    /**
-     * Method called when Anchor position is decided. Extending class can setup accordingly or
-     * even update anchor info if necessary.
-     * @param recycler The recycler for the layout
-     * @param state The layout state
-     * @param anchorInfo The mutable POJO that keeps the position and offset.
-     * @param firstLayoutItemDirection The direction of the first layout filling in terms of adapter
-     *                                 indices.
-     */
-    void onAnchorReady(RecyclerView.Recycler recycler, RecyclerView.State state,
-            AnchorInfo anchorInfo, int firstLayoutItemDirection) {
-    }
-
-    /**
-     * If necessary, layouts new items for predictive animations
-     */
-    private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler,
-            RecyclerView.State state, int startOffset,
-            int endOffset) {
-        // If there are scrap children that we did not layout, we need to find where they did go
-        // and layout them accordingly so that animations can work as expected.
-        // This case may happen if new views are added or an existing view expands and pushes
-        // another view out of bounds.
-        if (!state.willRunPredictiveAnimations() ||  getChildCount() == 0 || state.isPreLayout()
-                || !supportsPredictiveItemAnimations()) {
-            return;
-        }
-        // to make the logic simpler, we calculate the size of children and call fill.
-        int scrapExtraStart = 0, scrapExtraEnd = 0;
-        final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
-        final int scrapSize = scrapList.size();
-        final int firstChildPos = getPosition(getChildAt(0));
-        for (int i = 0; i < scrapSize; i++) {
-            RecyclerView.ViewHolder scrap = scrapList.get(i);
-            if (scrap.isRemoved()) {
-                continue;
-            }
-            final int position = scrap.getLayoutPosition();
-            final int direction = position < firstChildPos != mShouldReverseLayout
-                    ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END;
-            if (direction == LayoutState.LAYOUT_START) {
-                scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
-            } else {
-                scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart
-                    + " towards start and " + scrapExtraEnd + " towards end");
-        }
-        mLayoutState.mScrapList = scrapList;
-        if (scrapExtraStart > 0) {
-            View anchor = getChildClosestToStart();
-            updateLayoutStateToFillStart(getPosition(anchor), startOffset);
-            mLayoutState.mExtra = scrapExtraStart;
-            mLayoutState.mAvailable = 0;
-            mLayoutState.assignPositionFromScrapList();
-            fill(recycler, mLayoutState, state, false);
-        }
-
-        if (scrapExtraEnd > 0) {
-            View anchor = getChildClosestToEnd();
-            updateLayoutStateToFillEnd(getPosition(anchor), endOffset);
-            mLayoutState.mExtra = scrapExtraEnd;
-            mLayoutState.mAvailable = 0;
-            mLayoutState.assignPositionFromScrapList();
-            fill(recycler, mLayoutState, state, false);
-        }
-        mLayoutState.mScrapList = null;
-    }
-
-    private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state,
-            AnchorInfo anchorInfo) {
-        if (updateAnchorFromPendingData(state, anchorInfo)) {
-            if (DEBUG) {
-                Log.d(TAG, "updated anchor info from pending information");
-            }
-            return;
-        }
-
-        if (updateAnchorFromChildren(recycler, state, anchorInfo)) {
-            if (DEBUG) {
-                Log.d(TAG, "updated anchor info from existing children");
-            }
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "deciding anchor info for fresh state");
-        }
-        anchorInfo.assignCoordinateFromPadding();
-        anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;
-    }
-
-    /**
-     * Finds an anchor child from existing Views. Most of the time, this is the view closest to
-     * start or end that has a valid position (e.g. not removed).
-     * <p>
-     * If a child has focus, it is given priority.
-     */
-    private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler,
-            RecyclerView.State state, AnchorInfo anchorInfo) {
-        if (getChildCount() == 0) {
-            return false;
-        }
-        final View focused = getFocusedChild();
-        if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) {
-            anchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));
-            return true;
-        }
-        if (mLastStackFromEnd != mStackFromEnd) {
-            return false;
-        }
-        View referenceChild = anchorInfo.mLayoutFromEnd
-                ? findReferenceChildClosestToEnd(recycler, state)
-                : findReferenceChildClosestToStart(recycler, state);
-        if (referenceChild != null) {
-            anchorInfo.assignFromView(referenceChild, getPosition(referenceChild));
-            // If all visible views are removed in 1 pass, reference child might be out of bounds.
-            // If that is the case, offset it back to 0 so that we use these pre-layout children.
-            if (!state.isPreLayout() && supportsPredictiveItemAnimations()) {
-                // validate this child is at least partially visible. if not, offset it to start
-                final boolean notVisible =
-                        mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper
-                                .getEndAfterPadding()
-                                || mOrientationHelper.getDecoratedEnd(referenceChild)
-                                < mOrientationHelper.getStartAfterPadding();
-                if (notVisible) {
-                    anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
-                            ? mOrientationHelper.getEndAfterPadding()
-                            : mOrientationHelper.getStartAfterPadding();
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * If there is a pending scroll position or saved states, updates the anchor info from that
-     * data and returns true
-     */
-    private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) {
-        if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) {
-            return false;
-        }
-        // validate scroll position
-        if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {
-            mPendingScrollPosition = NO_POSITION;
-            mPendingScrollPositionOffset = INVALID_OFFSET;
-            if (DEBUG) {
-                Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition);
-            }
-            return false;
-        }
-
-        // if child is visible, try to make it a reference child and ensure it is fully visible.
-        // if child is not visible, align it depending on its virtual position.
-        anchorInfo.mPosition = mPendingScrollPosition;
-        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
-            // Anchor offset depends on how that child was laid out. Here, we update it
-            // according to our current view bounds
-            anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
-            if (anchorInfo.mLayoutFromEnd) {
-                anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding()
-                        - mPendingSavedState.mAnchorOffset;
-            } else {
-                anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding()
-                        + mPendingSavedState.mAnchorOffset;
-            }
-            return true;
-        }
-
-        if (mPendingScrollPositionOffset == INVALID_OFFSET) {
-            View child = findViewByPosition(mPendingScrollPosition);
-            if (child != null) {
-                final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
-                if (childSize > mOrientationHelper.getTotalSpace()) {
-                    // item does not fit. fix depending on layout direction
-                    anchorInfo.assignCoordinateFromPadding();
-                    return true;
-                }
-                final int startGap = mOrientationHelper.getDecoratedStart(child)
-                        - mOrientationHelper.getStartAfterPadding();
-                if (startGap < 0) {
-                    anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding();
-                    anchorInfo.mLayoutFromEnd = false;
-                    return true;
-                }
-                final int endGap = mOrientationHelper.getEndAfterPadding()
-                        - mOrientationHelper.getDecoratedEnd(child);
-                if (endGap < 0) {
-                    anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();
-                    anchorInfo.mLayoutFromEnd = true;
-                    return true;
-                }
-                anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
-                        ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper
-                        .getTotalSpaceChange())
-                        : mOrientationHelper.getDecoratedStart(child);
-            } else { // item is not visible.
-                if (getChildCount() > 0) {
-                    // get position of any child, does not matter
-                    int pos = getPosition(getChildAt(0));
-                    anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos
-                            == mShouldReverseLayout;
-                }
-                anchorInfo.assignCoordinateFromPadding();
-            }
-            return true;
-        }
-        // override layout from end values for consistency
-        anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
-        // if this changes, we should update prepareForDrop as well
-        if (mShouldReverseLayout) {
-            anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding()
-                    - mPendingScrollPositionOffset;
-        } else {
-            anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding()
-                    + mPendingScrollPositionOffset;
-        }
-        return true;
-    }
-
-    /**
-     * @return The final offset amount for children
-     */
-    private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler,
-            RecyclerView.State state, boolean canOffsetChildren) {
-        int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
-        int fixOffset = 0;
-        if (gap > 0) {
-            fixOffset = -scrollBy(-gap, recycler, state);
-        } else {
-            return 0; // nothing to fix
-        }
-        // move offset according to scroll amount
-        endOffset += fixOffset;
-        if (canOffsetChildren) {
-            // re-calculate gap, see if we could fix it
-            gap = mOrientationHelper.getEndAfterPadding() - endOffset;
-            if (gap > 0) {
-                mOrientationHelper.offsetChildren(gap);
-                return gap + fixOffset;
-            }
-        }
-        return fixOffset;
-    }
-
-    /**
-     * @return The final offset amount for children
-     */
-    private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler,
-            RecyclerView.State state, boolean canOffsetChildren) {
-        int gap = startOffset - mOrientationHelper.getStartAfterPadding();
-        int fixOffset = 0;
-        if (gap > 0) {
-            // check if we should fix this gap.
-            fixOffset = -scrollBy(gap, recycler, state);
-        } else {
-            return 0; // nothing to fix
-        }
-        startOffset += fixOffset;
-        if (canOffsetChildren) {
-            // re-calculate gap, see if we could fix it
-            gap = startOffset - mOrientationHelper.getStartAfterPadding();
-            if (gap > 0) {
-                mOrientationHelper.offsetChildren(-gap);
-                return fixOffset - gap;
-            }
-        }
-        return fixOffset;
-    }
-
-    private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) {
-        updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate);
-    }
-
-    private void updateLayoutStateToFillEnd(int itemPosition, int offset) {
-        mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset;
-        mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
-                LayoutState.ITEM_DIRECTION_TAIL;
-        mLayoutState.mCurrentPosition = itemPosition;
-        mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END;
-        mLayoutState.mOffset = offset;
-        mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
-    }
-
-    private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) {
-        updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate);
-    }
-
-    private void updateLayoutStateToFillStart(int itemPosition, int offset) {
-        mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding();
-        mLayoutState.mCurrentPosition = itemPosition;
-        mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :
-                LayoutState.ITEM_DIRECTION_HEAD;
-        mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START;
-        mLayoutState.mOffset = offset;
-        mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
-
-    }
-
-    protected boolean isLayoutRTL() {
-        return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
-    }
-
-    void ensureLayoutState() {
-        if (mLayoutState == null) {
-            mLayoutState = createLayoutState();
-        }
-    }
-
-    /**
-     * Test overrides this to plug some tracking and verification.
-     *
-     * @return A new LayoutState
-     */
-    LayoutState createLayoutState() {
-        return new LayoutState();
-    }
-
-    /**
-     * <p>Scroll the RecyclerView to make the position visible.</p>
-     *
-     * <p>RecyclerView will scroll the minimum amount that is necessary to make the
-     * target position visible. If you are looking for a similar behavior to
-     * {@link android.widget.ListView#setSelection(int)} or
-     * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use
-     * {@link #scrollToPositionWithOffset(int, int)}.</p>
-     *
-     * <p>Note that scroll position change will not be reflected until the next layout call.</p>
-     *
-     * @param position Scroll to this adapter position
-     * @see #scrollToPositionWithOffset(int, int)
-     */
-    @Override
-    public void scrollToPosition(int position) {
-        mPendingScrollPosition = position;
-        mPendingScrollPositionOffset = INVALID_OFFSET;
-        if (mPendingSavedState != null) {
-            mPendingSavedState.invalidateAnchor();
-        }
-        requestLayout();
-    }
-
-    /**
-     * Scroll to the specified adapter position with the given offset from resolved layout
-     * start. Resolved layout start depends on {@link #getReverseLayout()},
-     * {@link ViewCompat#getLayoutDirection(android.view.View)} and {@link #getStackFromEnd()}.
-     * <p>
-     * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling
-     * <code>scrollToPositionWithOffset(10, 20)</code> will layout such that
-     * <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom.
-     * <p>
-     * Note that scroll position change will not be reflected until the next layout call.
-     * <p>
-     * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
-     *
-     * @param position Index (starting at 0) of the reference item.
-     * @param offset   The distance (in pixels) between the start edge of the item view and
-     *                 start edge of the RecyclerView.
-     * @see #setReverseLayout(boolean)
-     * @see #scrollToPosition(int)
-     */
-    public void scrollToPositionWithOffset(int position, int offset) {
-        mPendingScrollPosition = position;
-        mPendingScrollPositionOffset = offset;
-        if (mPendingSavedState != null) {
-            mPendingSavedState.invalidateAnchor();
-        }
-        requestLayout();
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == VERTICAL) {
-            return 0;
-        }
-        return scrollBy(dx, recycler, state);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == HORIZONTAL) {
-            return 0;
-        }
-        return scrollBy(dy, recycler, state);
-    }
-
-    @Override
-    public int computeHorizontalScrollOffset(RecyclerView.State state) {
-        return computeScrollOffset(state);
-    }
-
-    @Override
-    public int computeVerticalScrollOffset(RecyclerView.State state) {
-        return computeScrollOffset(state);
-    }
-
-    @Override
-    public int computeHorizontalScrollExtent(RecyclerView.State state) {
-        return computeScrollExtent(state);
-    }
-
-    @Override
-    public int computeVerticalScrollExtent(RecyclerView.State state) {
-        return computeScrollExtent(state);
-    }
-
-    @Override
-    public int computeHorizontalScrollRange(RecyclerView.State state) {
-        return computeScrollRange(state);
-    }
-
-    @Override
-    public int computeVerticalScrollRange(RecyclerView.State state) {
-        return computeScrollRange(state);
-    }
-
-    private int computeScrollOffset(RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return 0;
-        }
-        ensureLayoutState();
-        return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
-                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
-                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
-                this, mSmoothScrollbarEnabled, mShouldReverseLayout);
-    }
-
-    private int computeScrollExtent(RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return 0;
-        }
-        ensureLayoutState();
-        return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper,
-                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
-                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
-                this,  mSmoothScrollbarEnabled);
-    }
-
-    private int computeScrollRange(RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return 0;
-        }
-        ensureLayoutState();
-        return ScrollbarHelper.computeScrollRange(state, mOrientationHelper,
-                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
-                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
-                this, mSmoothScrollbarEnabled);
-    }
-
-    /**
-     * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
-     * based on the number of visible pixels in the visible items. This however assumes that all
-     * list items have similar or equal widths or heights (depending on list orientation).
-     * If you use a list in which items have different dimensions, the scrollbar will change
-     * appearance as the user scrolls through the list. To avoid this issue,  you need to disable
-     * this property.
-     *
-     * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
-     * solely on the number of items in the adapter and the position of the visible items inside
-     * the adapter. This provides a stable scrollbar as the user navigates through a list of items
-     * with varying widths / heights.
-     *
-     * @param enabled Whether or not to enable smooth scrollbar.
-     *
-     * @see #setSmoothScrollbarEnabled(boolean)
-     */
-    public void setSmoothScrollbarEnabled(boolean enabled) {
-        mSmoothScrollbarEnabled = enabled;
-    }
-
-    /**
-     * Returns the current state of the smooth scrollbar feature. It is enabled by default.
-     *
-     * @return True if smooth scrollbar is enabled, false otherwise.
-     *
-     * @see #setSmoothScrollbarEnabled(boolean)
-     */
-    public boolean isSmoothScrollbarEnabled() {
-        return mSmoothScrollbarEnabled;
-    }
-
-    private void updateLayoutState(int layoutDirection, int requiredSpace,
-            boolean canUseExistingSpace, RecyclerView.State state) {
-        // If parent provides a hint, don't measure unlimited.
-        mLayoutState.mInfinite = resolveIsInfinite();
-        mLayoutState.mExtra = getExtraLayoutSpace(state);
-        mLayoutState.mLayoutDirection = layoutDirection;
-        int scrollingOffset;
-        if (layoutDirection == LayoutState.LAYOUT_END) {
-            mLayoutState.mExtra += mOrientationHelper.getEndPadding();
-            // get the first child in the direction we are going
-            final View child = getChildClosestToEnd();
-            // the direction in which we are traversing children
-            mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
-                    : LayoutState.ITEM_DIRECTION_TAIL;
-            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
-            mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
-            // calculate how much we can scroll without adding new children (independent of layout)
-            scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
-                    - mOrientationHelper.getEndAfterPadding();
-
-        } else {
-            final View child = getChildClosestToStart();
-            mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding();
-            mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
-                    : LayoutState.ITEM_DIRECTION_HEAD;
-            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
-            mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
-            scrollingOffset = -mOrientationHelper.getDecoratedStart(child)
-                    + mOrientationHelper.getStartAfterPadding();
-        }
-        mLayoutState.mAvailable = requiredSpace;
-        if (canUseExistingSpace) {
-            mLayoutState.mAvailable -= scrollingOffset;
-        }
-        mLayoutState.mScrollingOffset = scrollingOffset;
-    }
-
-    boolean resolveIsInfinite() {
-        return mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED
-                && mOrientationHelper.getEnd() == 0;
-    }
-
-    void collectPrefetchPositionsForLayoutState(RecyclerView.State state, LayoutState layoutState,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        final int pos = layoutState.mCurrentPosition;
-        if (pos >= 0 && pos < state.getItemCount()) {
-            layoutPrefetchRegistry.addPosition(pos, Math.max(0, layoutState.mScrollingOffset));
-        }
-    }
-
-    @Override
-    public void collectInitialPrefetchPositions(int adapterItemCount,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        final boolean fromEnd;
-        final int anchorPos;
-        if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
-            // use restored state, since it hasn't been resolved yet
-            fromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
-            anchorPos = mPendingSavedState.mAnchorPosition;
-        } else {
-            resolveShouldLayoutReverse();
-            fromEnd = mShouldReverseLayout;
-            if (mPendingScrollPosition == NO_POSITION) {
-                anchorPos = fromEnd ? adapterItemCount - 1 : 0;
-            } else {
-                anchorPos = mPendingScrollPosition;
-            }
-        }
-
-        final int direction = fromEnd
-                ? LayoutState.ITEM_DIRECTION_HEAD
-                : LayoutState.ITEM_DIRECTION_TAIL;
-        int targetPos = anchorPos;
-        for (int i = 0; i < mInitialPrefetchItemCount; i++) {
-            if (targetPos >= 0 && targetPos < adapterItemCount) {
-                layoutPrefetchRegistry.addPosition(targetPos, 0);
-            } else {
-                break; // no more to prefetch
-            }
-            targetPos += direction;
-        }
-    }
-
-    /**
-     * Sets the number of items to prefetch in
-     * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines
-     * how many inner items should be prefetched when this LayoutManager's RecyclerView
-     * is nested inside another RecyclerView.
-     *
-     * <p>Set this value to the number of items this inner LayoutManager will display when it is
-     * first scrolled into the viewport. RecyclerView will attempt to prefetch that number of items
-     * so they are ready, avoiding jank as the inner RecyclerView is scrolled into the viewport.</p>
-     *
-     * <p>For example, take a vertically scrolling RecyclerView with horizontally scrolling inner
-     * RecyclerViews. The rows always have 4 items visible in them (or 5 if not aligned). Passing
-     * <code>4</code> to this method for each inner RecyclerView's LinearLayoutManager will enable
-     * RecyclerView's prefetching feature to do create/bind work for 4 views within a row early,
-     * before it is scrolled on screen, instead of just the default 2.</p>
-     *
-     * <p>Calling this method does nothing unless the LayoutManager is in a RecyclerView
-     * nested in another RecyclerView.</p>
-     *
-     * <p class="note"><strong>Note:</strong> Setting this value to be larger than the number of
-     * views that will be visible in this view can incur unnecessary bind work, and an increase to
-     * the number of Views created and in active use.</p>
-     *
-     * @param itemCount Number of items to prefetch
-     *
-     * @see #isItemPrefetchEnabled()
-     * @see #getInitialPrefetchItemCount()
-     * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
-     */
-    public void setInitialPrefetchItemCount(int itemCount) {
-        mInitialPrefetchItemCount = itemCount;
-    }
-
-    /**
-     * Gets the number of items to prefetch in
-     * {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)}, which defines
-     * how many inner items should be prefetched when this LayoutManager's RecyclerView
-     * is nested inside another RecyclerView.
-     *
-     * @see #isItemPrefetchEnabled()
-     * @see #setInitialPrefetchItemCount(int)
-     * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
-     *
-     * @return number of items to prefetch.
-     */
-    public int getInitialPrefetchItemCount() {
-        return mInitialPrefetchItemCount;
-    }
-
-    @Override
-    public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        int delta = (mOrientation == HORIZONTAL) ? dx : dy;
-        if (getChildCount() == 0 || delta == 0) {
-            // can't support this scroll, so don't bother prefetching
-            return;
-        }
-
-        ensureLayoutState();
-        final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
-        final int absDy = Math.abs(delta);
-        updateLayoutState(layoutDirection, absDy, true, state);
-        collectPrefetchPositionsForLayoutState(state, mLayoutState, layoutPrefetchRegistry);
-    }
-
-    int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
-        if (getChildCount() == 0 || dy == 0) {
-            return 0;
-        }
-        mLayoutState.mRecycle = true;
-        ensureLayoutState();
-        final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
-        final int absDy = Math.abs(dy);
-        updateLayoutState(layoutDirection, absDy, true, state);
-        final int consumed = mLayoutState.mScrollingOffset
-                + fill(recycler, mLayoutState, state, false);
-        if (consumed < 0) {
-            if (DEBUG) {
-                Log.d(TAG, "Don't have any more elements to scroll");
-            }
-            return 0;
-        }
-        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
-        mOrientationHelper.offsetChildren(-scrolled);
-        if (DEBUG) {
-            Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
-        }
-        mLayoutState.mLastScrollDelta = scrolled;
-        return scrolled;
-    }
-
-    @Override
-    public void assertNotInLayoutOrScroll(String message) {
-        if (mPendingSavedState == null) {
-            super.assertNotInLayoutOrScroll(message);
-        }
-    }
-
-    /**
-     * Recycles children between given indices.
-     *
-     * @param startIndex inclusive
-     * @param endIndex   exclusive
-     */
-    private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
-        if (startIndex == endIndex) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
-        }
-        if (endIndex > startIndex) {
-            for (int i = endIndex - 1; i >= startIndex; i--) {
-                removeAndRecycleViewAt(i, recycler);
-            }
-        } else {
-            for (int i = startIndex; i > endIndex; i--) {
-                removeAndRecycleViewAt(i, recycler);
-            }
-        }
-    }
-
-    /**
-     * Recycles views that went out of bounds after scrolling towards the end of the layout.
-     * <p>
-     * Checks both layout position and visible position to guarantee that the view is not visible.
-     *
-     * @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
-     * @param dt       This can be used to add additional padding to the visible area. This is used
-     *                 to detect children that will go out of bounds after scrolling, without
-     *                 actually moving them.
-     */
-    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
-        if (dt < 0) {
-            if (DEBUG) {
-                Log.d(TAG, "Called recycle from start with a negative value. This might happen"
-                        + " during layout changes but may be sign of a bug");
-            }
-            return;
-        }
-        // ignore padding, ViewGroup may not clip children.
-        final int limit = dt;
-        final int childCount = getChildCount();
-        if (mShouldReverseLayout) {
-            for (int i = childCount - 1; i >= 0; i--) {
-                View child = getChildAt(i);
-                if (mOrientationHelper.getDecoratedEnd(child) > limit
-                        || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
-                    // stop here
-                    recycleChildren(recycler, childCount - 1, i);
-                    return;
-                }
-            }
-        } else {
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                if (mOrientationHelper.getDecoratedEnd(child) > limit
-                        || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
-                    // stop here
-                    recycleChildren(recycler, 0, i);
-                    return;
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Recycles views that went out of bounds after scrolling towards the start of the layout.
-     * <p>
-     * Checks both layout position and visible position to guarantee that the view is not visible.
-     *
-     * @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
-     * @param dt       This can be used to add additional padding to the visible area. This is used
-     *                 to detect children that will go out of bounds after scrolling, without
-     *                 actually moving them.
-     */
-    private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
-        final int childCount = getChildCount();
-        if (dt < 0) {
-            if (DEBUG) {
-                Log.d(TAG, "Called recycle from end with a negative value. This might happen"
-                        + " during layout changes but may be sign of a bug");
-            }
-            return;
-        }
-        final int limit = mOrientationHelper.getEnd() - dt;
-        if (mShouldReverseLayout) {
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                if (mOrientationHelper.getDecoratedStart(child) < limit
-                        || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
-                    // stop here
-                    recycleChildren(recycler, 0, i);
-                    return;
-                }
-            }
-        } else {
-            for (int i = childCount - 1; i >= 0; i--) {
-                View child = getChildAt(i);
-                if (mOrientationHelper.getDecoratedStart(child) < limit
-                        || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
-                    // stop here
-                    recycleChildren(recycler, childCount - 1, i);
-                    return;
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper method to call appropriate recycle method depending on current layout direction
-     *
-     * @param recycler    Current recycler that is attached to RecyclerView
-     * @param layoutState Current layout state. Right now, this object does not change but
-     *                    we may consider moving it out of this view so passing around as a
-     *                    parameter for now, rather than accessing {@link #mLayoutState}
-     * @see #recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int)
-     * @see #recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int)
-     * @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection
-     */
-    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
-        if (!layoutState.mRecycle || layoutState.mInfinite) {
-            return;
-        }
-        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
-            recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
-        } else {
-            recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
-        }
-    }
-
-    /**
-     * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
-     * independent from the rest of the {@link android.support.v7.widget.LinearLayoutManager}
-     * and with little change, can be made publicly available as a helper class.
-     *
-     * @param recycler        Current recycler that is attached to RecyclerView
-     * @param layoutState     Configuration on how we should fill out the available space.
-     * @param state           Context passed by the RecyclerView to control scroll steps.
-     * @param stopOnFocusable If true, filling stops in the first focusable new child
-     * @return Number of pixels that it added. Useful for scroll functions.
-     */
-    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
-            RecyclerView.State state, boolean stopOnFocusable) {
-        // max offset we should set is mFastScroll + available
-        final int start = layoutState.mAvailable;
-        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
-            // TODO ugly bug fix. should not happen
-            if (layoutState.mAvailable < 0) {
-                layoutState.mScrollingOffset += layoutState.mAvailable;
-            }
-            recycleByLayoutState(recycler, layoutState);
-        }
-        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
-        LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
-        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
-            layoutChunkResult.resetInternal();
-            if (VERBOSE_TRACING) {
-                TraceCompat.beginSection("LLM LayoutChunk");
-            }
-            layoutChunk(recycler, state, layoutState, layoutChunkResult);
-            if (VERBOSE_TRACING) {
-                TraceCompat.endSection();
-            }
-            if (layoutChunkResult.mFinished) {
-                break;
-            }
-            layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
-            /**
-             * Consume the available space if:
-             * * layoutChunk did not request to be ignored
-             * * OR we are laying out scrap children
-             * * OR we are not doing pre-layout
-             */
-            if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
-                    || !state.isPreLayout()) {
-                layoutState.mAvailable -= layoutChunkResult.mConsumed;
-                // we keep a separate remaining space because mAvailable is important for recycling
-                remainingSpace -= layoutChunkResult.mConsumed;
-            }
-
-            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
-                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
-                if (layoutState.mAvailable < 0) {
-                    layoutState.mScrollingOffset += layoutState.mAvailable;
-                }
-                recycleByLayoutState(recycler, layoutState);
-            }
-            if (stopOnFocusable && layoutChunkResult.mFocusable) {
-                break;
-            }
-        }
-        if (DEBUG) {
-            validateChildOrder();
-        }
-        return start - layoutState.mAvailable;
-    }
-
-    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
-            LayoutState layoutState, LayoutChunkResult result) {
-        View view = layoutState.next(recycler);
-        if (view == null) {
-            if (DEBUG && layoutState.mScrapList == null) {
-                throw new RuntimeException("received null view when unexpected");
-            }
-            // if we are laying out views in scrap, this may return null which means there is
-            // no more items to layout.
-            result.mFinished = true;
-            return;
-        }
-        LayoutParams params = (LayoutParams) view.getLayoutParams();
-        if (layoutState.mScrapList == null) {
-            if (mShouldReverseLayout == (layoutState.mLayoutDirection
-                    == LayoutState.LAYOUT_START)) {
-                addView(view);
-            } else {
-                addView(view, 0);
-            }
-        } else {
-            if (mShouldReverseLayout == (layoutState.mLayoutDirection
-                    == LayoutState.LAYOUT_START)) {
-                addDisappearingView(view);
-            } else {
-                addDisappearingView(view, 0);
-            }
-        }
-        measureChildWithMargins(view, 0, 0);
-        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
-        int left, top, right, bottom;
-        if (mOrientation == VERTICAL) {
-            if (isLayoutRTL()) {
-                right = getWidth() - getPaddingRight();
-                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
-            } else {
-                left = getPaddingLeft();
-                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
-            }
-            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
-                bottom = layoutState.mOffset;
-                top = layoutState.mOffset - result.mConsumed;
-            } else {
-                top = layoutState.mOffset;
-                bottom = layoutState.mOffset + result.mConsumed;
-            }
-        } else {
-            top = getPaddingTop();
-            bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
-
-            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
-                right = layoutState.mOffset;
-                left = layoutState.mOffset - result.mConsumed;
-            } else {
-                left = layoutState.mOffset;
-                right = layoutState.mOffset + result.mConsumed;
-            }
-        }
-        // We calculate everything with View's bounding box (which includes decor and margins)
-        // To calculate correct layout position, we subtract margins.
-        layoutDecoratedWithMargins(view, left, top, right, bottom);
-        if (DEBUG) {
-            Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
-                    + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
-                    + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
-        }
-        // Consume the available space if the view is not removed OR changed
-        if (params.isItemRemoved() || params.isItemChanged()) {
-            result.mIgnoreConsumed = true;
-        }
-        result.mFocusable = view.hasFocusable();
-    }
-
-    @Override
-    boolean shouldMeasureTwice() {
-        return getHeightMode() != View.MeasureSpec.EXACTLY
-                && getWidthMode() != View.MeasureSpec.EXACTLY
-                && hasFlexibleChildInBothOrientations();
-    }
-
-    /**
-     * Converts a focusDirection to orientation.
-     *
-     * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-     *                       {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
-     *                       {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
-     *                       or 0 for not applicable
-     * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
-     * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
-     */
-    int convertFocusDirectionToLayoutDirection(int focusDirection) {
-        switch (focusDirection) {
-            case View.FOCUS_BACKWARD:
-                if (mOrientation == VERTICAL) {
-                    return LayoutState.LAYOUT_START;
-                } else if (isLayoutRTL()) {
-                    return LayoutState.LAYOUT_END;
-                } else {
-                    return LayoutState.LAYOUT_START;
-                }
-            case View.FOCUS_FORWARD:
-                if (mOrientation == VERTICAL) {
-                    return LayoutState.LAYOUT_END;
-                } else if (isLayoutRTL()) {
-                    return LayoutState.LAYOUT_START;
-                } else {
-                    return LayoutState.LAYOUT_END;
-                }
-            case View.FOCUS_UP:
-                return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
-                        : LayoutState.INVALID_LAYOUT;
-            case View.FOCUS_DOWN:
-                return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
-                        : LayoutState.INVALID_LAYOUT;
-            case View.FOCUS_LEFT:
-                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
-                        : LayoutState.INVALID_LAYOUT;
-            case View.FOCUS_RIGHT:
-                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
-                        : LayoutState.INVALID_LAYOUT;
-            default:
-                if (DEBUG) {
-                    Log.d(TAG, "Unknown focus request:" + focusDirection);
-                }
-                return LayoutState.INVALID_LAYOUT;
-        }
-
-    }
-
-    /**
-     * Convenience method to find the child closes to start. Caller should check it has enough
-     * children.
-     *
-     * @return The child closes to start of the layout from user's perspective.
-     */
-    private View getChildClosestToStart() {
-        return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0);
-    }
-
-    /**
-     * Convenience method to find the child closes to end. Caller should check it has enough
-     * children.
-     *
-     * @return The child closes to end of the layout from user's perspective.
-     */
-    private View getChildClosestToEnd() {
-        return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1);
-    }
-
-    /**
-     * Convenience method to find the visible child closes to start. Caller should check if it has
-     * enough children.
-     *
-     * @param completelyVisible Whether child should be completely visible or not
-     * @return The first visible child closest to start of the layout from user's perspective.
-     */
-    private View findFirstVisibleChildClosestToStart(boolean completelyVisible,
-            boolean acceptPartiallyVisible) {
-        if (mShouldReverseLayout) {
-            return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
-                    acceptPartiallyVisible);
-        } else {
-            return findOneVisibleChild(0, getChildCount(), completelyVisible,
-                    acceptPartiallyVisible);
-        }
-    }
-
-    /**
-     * Convenience method to find the visible child closes to end. Caller should check if it has
-     * enough children.
-     *
-     * @param completelyVisible Whether child should be completely visible or not
-     * @return The first visible child closest to end of the layout from user's perspective.
-     */
-    private View findFirstVisibleChildClosestToEnd(boolean completelyVisible,
-            boolean acceptPartiallyVisible) {
-        if (mShouldReverseLayout) {
-            return findOneVisibleChild(0, getChildCount(), completelyVisible,
-                    acceptPartiallyVisible);
-        } else {
-            return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
-                    acceptPartiallyVisible);
-        }
-    }
-
-
-    /**
-     * Among the children that are suitable to be considered as an anchor child, returns the one
-     * closest to the end of the layout.
-     * <p>
-     * Due to ambiguous adapter updates or children being removed, some children's positions may be
-     * invalid. This method is a best effort to find a position within adapter bounds if possible.
-     * <p>
-     * It also prioritizes children that are within the visible bounds.
-     * @return A View that can be used an an anchor View.
-     */
-    private View findReferenceChildClosestToEnd(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return mShouldReverseLayout ? findFirstReferenceChild(recycler, state) :
-                findLastReferenceChild(recycler, state);
-    }
-
-    /**
-     * Among the children that are suitable to be considered as an anchor child, returns the one
-     * closest to the start of the layout.
-     * <p>
-     * Due to ambiguous adapter updates or children being removed, some children's positions may be
-     * invalid. This method is a best effort to find a position within adapter bounds if possible.
-     * <p>
-     * It also prioritizes children that are within the visible bounds.
-     *
-     * @return A View that can be used an an anchor View.
-     */
-    private View findReferenceChildClosestToStart(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return mShouldReverseLayout ? findLastReferenceChild(recycler, state) :
-                findFirstReferenceChild(recycler, state);
-    }
-
-    private View findFirstReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) {
-        return findReferenceChild(recycler, state, 0, getChildCount(), state.getItemCount());
-    }
-
-    private View findLastReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state) {
-        return findReferenceChild(recycler, state, getChildCount() - 1, -1, state.getItemCount());
-    }
-
-    // overridden by GridLayoutManager
-    View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
-            int start, int end, int itemCount) {
-        ensureLayoutState();
-        View invalidMatch = null;
-        View outOfBoundsMatch = null;
-        final int boundsStart = mOrientationHelper.getStartAfterPadding();
-        final int boundsEnd = mOrientationHelper.getEndAfterPadding();
-        final int diff = end > start ? 1 : -1;
-        for (int i = start; i != end; i += diff) {
-            final View view = getChildAt(i);
-            final int position = getPosition(view);
-            if (position >= 0 && position < itemCount) {
-                if (((LayoutParams) view.getLayoutParams()).isItemRemoved()) {
-                    if (invalidMatch == null) {
-                        invalidMatch = view; // removed item, least preferred
-                    }
-                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd
-                        || mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
-                    if (outOfBoundsMatch == null) {
-                        outOfBoundsMatch = view; // item is not visible, less preferred
-                    }
-                } else {
-                    return view;
-                }
-            }
-        }
-        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
-    }
-
-    // returns the out-of-bound child view closest to RV's end bounds. An out-of-bound child is
-    // defined as a child that's either partially or fully invisible (outside RV's padding area).
-    private View findPartiallyOrCompletelyInvisibleChildClosestToEnd(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return mShouldReverseLayout ? findFirstPartiallyOrCompletelyInvisibleChild(recycler, state)
-                : findLastPartiallyOrCompletelyInvisibleChild(recycler, state);
-    }
-
-    // returns the out-of-bound child view closest to RV's starting bounds. An out-of-bound child is
-    // defined as a child that's either partially or fully invisible (outside RV's padding area).
-    private View findPartiallyOrCompletelyInvisibleChildClosestToStart(
-            RecyclerView.Recycler recycler, RecyclerView.State state) {
-        return mShouldReverseLayout ? findLastPartiallyOrCompletelyInvisibleChild(recycler, state) :
-                findFirstPartiallyOrCompletelyInvisibleChild(recycler, state);
-    }
-
-    private View findFirstPartiallyOrCompletelyInvisibleChild(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return findOnePartiallyOrCompletelyInvisibleChild(0, getChildCount());
-    }
-
-    private View findLastPartiallyOrCompletelyInvisibleChild(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return findOnePartiallyOrCompletelyInvisibleChild(getChildCount() - 1, -1);
-    }
-
-    /**
-     * Returns the adapter position of the first visible view. This position does not include
-     * adapter changes that were dispatched after the last layout pass.
-     * <p>
-     * Note that, this value is not affected by layout orientation or item order traversal.
-     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
-     * not in the layout.
-     * <p>
-     * If RecyclerView has item decorators, they will be considered in calculations as well.
-     * <p>
-     * LayoutManager may pre-cache some views that are not necessarily visible. Those views
-     * are ignored in this method.
-     *
-     * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if
-     * there aren't any visible items.
-     * @see #findFirstCompletelyVisibleItemPosition()
-     * @see #findLastVisibleItemPosition()
-     */
-    public int findFirstVisibleItemPosition() {
-        final View child = findOneVisibleChild(0, getChildCount(), false, true);
-        return child == null ? NO_POSITION : getPosition(child);
-    }
-
-    /**
-     * Returns the adapter position of the first fully visible view. This position does not include
-     * adapter changes that were dispatched after the last layout pass.
-     * <p>
-     * Note that bounds check is only performed in the current orientation. That means, if
-     * LayoutManager is horizontal, it will only check the view's left and right edges.
-     *
-     * @return The adapter position of the first fully visible item or
-     * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
-     * @see #findFirstVisibleItemPosition()
-     * @see #findLastCompletelyVisibleItemPosition()
-     */
-    public int findFirstCompletelyVisibleItemPosition() {
-        final View child = findOneVisibleChild(0, getChildCount(), true, false);
-        return child == null ? NO_POSITION : getPosition(child);
-    }
-
-    /**
-     * Returns the adapter position of the last visible view. This position does not include
-     * adapter changes that were dispatched after the last layout pass.
-     * <p>
-     * Note that, this value is not affected by layout orientation or item order traversal.
-     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
-     * not in the layout.
-     * <p>
-     * If RecyclerView has item decorators, they will be considered in calculations as well.
-     * <p>
-     * LayoutManager may pre-cache some views that are not necessarily visible. Those views
-     * are ignored in this method.
-     *
-     * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if
-     * there aren't any visible items.
-     * @see #findLastCompletelyVisibleItemPosition()
-     * @see #findFirstVisibleItemPosition()
-     */
-    public int findLastVisibleItemPosition() {
-        final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true);
-        return child == null ? NO_POSITION : getPosition(child);
-    }
-
-    /**
-     * Returns the adapter position of the last fully visible view. This position does not include
-     * adapter changes that were dispatched after the last layout pass.
-     * <p>
-     * Note that bounds check is only performed in the current orientation. That means, if
-     * LayoutManager is horizontal, it will only check the view's left and right edges.
-     *
-     * @return The adapter position of the last fully visible view or
-     * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
-     * @see #findLastVisibleItemPosition()
-     * @see #findFirstCompletelyVisibleItemPosition()
-     */
-    public int findLastCompletelyVisibleItemPosition() {
-        final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false);
-        return child == null ? NO_POSITION : getPosition(child);
-    }
-
-    // Returns the first child that is visible in the provided index range, i.e. either partially or
-    // fully visible depending on the arguments provided. Completely invisible children are not
-    // acceptable by this method, but could be returned
-    // using #findOnePartiallyOrCompletelyInvisibleChild
-    View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible,
-            boolean acceptPartiallyVisible) {
-        ensureLayoutState();
-        @ViewBoundsCheck.ViewBounds int preferredBoundsFlag = 0;
-        @ViewBoundsCheck.ViewBounds int acceptableBoundsFlag = 0;
-        if (completelyVisible) {
-            preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
-                    | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
-        } else {
-            preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVE
-                    | ViewBoundsCheck.FLAG_CVE_GT_PVS);
-        }
-        if (acceptPartiallyVisible) {
-            acceptableBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVE
-                    | ViewBoundsCheck.FLAG_CVE_GT_PVS);
-        }
-        return (mOrientation == HORIZONTAL) ? mHorizontalBoundCheck
-                .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag,
-                        acceptableBoundsFlag) : mVerticalBoundCheck
-                .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag,
-                        acceptableBoundsFlag);
-    }
-
-    View findOnePartiallyOrCompletelyInvisibleChild(int fromIndex, int toIndex) {
-        ensureLayoutState();
-        final int next = toIndex > fromIndex ? 1 : (toIndex < fromIndex ? -1 : 0);
-        if (next == 0) {
-            return getChildAt(fromIndex);
-        }
-        @ViewBoundsCheck.ViewBounds int preferredBoundsFlag = 0;
-        @ViewBoundsCheck.ViewBounds int acceptableBoundsFlag = 0;
-        if (mOrientationHelper.getDecoratedStart(getChildAt(fromIndex))
-                < mOrientationHelper.getStartAfterPadding()) {
-            preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVS | ViewBoundsCheck.FLAG_CVE_LT_PVE
-                    | ViewBoundsCheck.FLAG_CVE_GT_PVS);
-            acceptableBoundsFlag = (ViewBoundsCheck.FLAG_CVS_LT_PVS
-                    | ViewBoundsCheck.FLAG_CVE_LT_PVE);
-        } else {
-            preferredBoundsFlag = (ViewBoundsCheck.FLAG_CVE_GT_PVE | ViewBoundsCheck.FLAG_CVS_GT_PVS
-                    | ViewBoundsCheck.FLAG_CVS_LT_PVE);
-            acceptableBoundsFlag = (ViewBoundsCheck.FLAG_CVE_GT_PVE
-                    | ViewBoundsCheck.FLAG_CVS_GT_PVS);
-        }
-        return (mOrientation == HORIZONTAL) ? mHorizontalBoundCheck
-                .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag,
-                        acceptableBoundsFlag) : mVerticalBoundCheck
-                .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag,
-                        acceptableBoundsFlag);
-    }
-
-    @Override
-    public View onFocusSearchFailed(View focused, int focusDirection,
-            RecyclerView.Recycler recycler, RecyclerView.State state) {
-        resolveShouldLayoutReverse();
-        if (getChildCount() == 0) {
-            return null;
-        }
-
-        final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
-        if (layoutDir == LayoutState.INVALID_LAYOUT) {
-            return null;
-        }
-        ensureLayoutState();
-        ensureLayoutState();
-        final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace());
-        updateLayoutState(layoutDir, maxScroll, false, state);
-        mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
-        mLayoutState.mRecycle = false;
-        fill(recycler, mLayoutState, state, true);
-
-        // nextCandidate is the first child view in the layout direction that's partially
-        // within RV's bounds, i.e. part of it is visible or it's completely invisible but still
-        // touching RV's bounds. This will be the unfocusable candidate view to become visible onto
-        // the screen if no focusable views are found in the given layout direction.
-        final View nextCandidate;
-        if (layoutDir == LayoutState.LAYOUT_START) {
-            nextCandidate = findPartiallyOrCompletelyInvisibleChildClosestToStart(recycler, state);
-        } else {
-            nextCandidate = findPartiallyOrCompletelyInvisibleChildClosestToEnd(recycler, state);
-        }
-        // nextFocus is meaningful only if it refers to a focusable child, in which case it
-        // indicates the next view to gain focus.
-        final View nextFocus;
-        if (layoutDir == LayoutState.LAYOUT_START) {
-            nextFocus = getChildClosestToStart();
-        } else {
-            nextFocus = getChildClosestToEnd();
-        }
-        if (nextFocus.hasFocusable()) {
-            if (nextCandidate == null) {
-                return null;
-            }
-            return nextFocus;
-        }
-        return nextCandidate;
-    }
-
-    /**
-     * Used for debugging.
-     * Logs the internal representation of children to default logger.
-     */
-    private void logChildren() {
-        Log.d(TAG, "internal representation of views on the screen");
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            Log.d(TAG, "item " + getPosition(child) + ", coord:"
-                    + mOrientationHelper.getDecoratedStart(child));
-        }
-        Log.d(TAG, "==============");
-    }
-
-    /**
-     * Used for debugging.
-     * Validates that child views are laid out in correct order. This is important because rest of
-     * the algorithm relies on this constraint.
-     *
-     * In default layout, child 0 should be closest to screen position 0 and last child should be
-     * closest to position WIDTH or HEIGHT.
-     * In reverse layout, last child should be closes to screen position 0 and first child should
-     * be closest to position WIDTH  or HEIGHT
-     */
-    void validateChildOrder() {
-        Log.d(TAG, "validating child count " + getChildCount());
-        if (getChildCount() < 1) {
-            return;
-        }
-        int lastPos = getPosition(getChildAt(0));
-        int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0));
-        if (mShouldReverseLayout) {
-            for (int i = 1; i < getChildCount(); i++) {
-                View child = getChildAt(i);
-                int pos = getPosition(child);
-                int screenLoc = mOrientationHelper.getDecoratedStart(child);
-                if (pos < lastPos) {
-                    logChildren();
-                    throw new RuntimeException("detected invalid position. loc invalid? "
-                            + (screenLoc < lastScreenLoc));
-                }
-                if (screenLoc > lastScreenLoc) {
-                    logChildren();
-                    throw new RuntimeException("detected invalid location");
-                }
-            }
-        } else {
-            for (int i = 1; i < getChildCount(); i++) {
-                View child = getChildAt(i);
-                int pos = getPosition(child);
-                int screenLoc = mOrientationHelper.getDecoratedStart(child);
-                if (pos < lastPos) {
-                    logChildren();
-                    throw new RuntimeException("detected invalid position. loc invalid? "
-                            + (screenLoc < lastScreenLoc));
-                }
-                if (screenLoc < lastScreenLoc) {
-                    logChildren();
-                    throw new RuntimeException("detected invalid location");
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean supportsPredictiveItemAnimations() {
-        return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
-    }
-
-    /**
-     * @hide This method should be called by ItemTouchHelper only.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    @Override
-    public void prepareForDrop(View view, View target, int x, int y) {
-        assertNotInLayoutOrScroll("Cannot drop a view during a scroll or layout calculation");
-        ensureLayoutState();
-        resolveShouldLayoutReverse();
-        final int myPos = getPosition(view);
-        final int targetPos = getPosition(target);
-        final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL
-                : LayoutState.ITEM_DIRECTION_HEAD;
-        if (mShouldReverseLayout) {
-            if (dropDirection == LayoutState.ITEM_DIRECTION_TAIL) {
-                scrollToPositionWithOffset(targetPos,
-                        mOrientationHelper.getEndAfterPadding()
-                                - (mOrientationHelper.getDecoratedStart(target)
-                                + mOrientationHelper.getDecoratedMeasurement(view)));
-            } else {
-                scrollToPositionWithOffset(targetPos,
-                        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));
-            }
-        }
-    }
-
-    /**
-     * Helper class that keeps temporary state while {LayoutManager} is filling out the empty
-     * space.
-     */
-    static class LayoutState {
-
-        static final String TAG = "LLM#LayoutState";
-
-        static final int LAYOUT_START = -1;
-
-        static final int LAYOUT_END = 1;
-
-        static final int INVALID_LAYOUT = Integer.MIN_VALUE;
-
-        static final int ITEM_DIRECTION_HEAD = -1;
-
-        static final int ITEM_DIRECTION_TAIL = 1;
-
-        static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE;
-
-        /**
-         * We may not want to recycle children in some cases (e.g. layout)
-         */
-        boolean mRecycle = true;
-
-        /**
-         * Pixel offset where layout should start
-         */
-        int mOffset;
-
-        /**
-         * Number of pixels that we should fill, in the layout direction.
-         */
-        int mAvailable;
-
-        /**
-         * Current position on the adapter to get the next item.
-         */
-        int mCurrentPosition;
-
-        /**
-         * Defines the direction in which the data adapter is traversed.
-         * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
-         */
-        int mItemDirection;
-
-        /**
-         * Defines the direction in which the layout is filled.
-         * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
-         */
-        int mLayoutDirection;
-
-        /**
-         * Used when LayoutState is constructed in a scrolling state.
-         * It should be set the amount of scrolling we can make without creating a new view.
-         * Settings this is required for efficient view recycling.
-         */
-        int mScrollingOffset;
-
-        /**
-         * Used if you want to pre-layout items that are not yet visible.
-         * The difference with {@link #mAvailable} is that, when recycling, distance laid out for
-         * {@link #mExtra} is not considered to avoid recycling visible children.
-         */
-        int mExtra = 0;
-
-        /**
-         * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value
-         * is set to true, we skip removed views since they should not be laid out in post layout
-         * step.
-         */
-        boolean mIsPreLayout = false;
-
-        /**
-         * The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)}
-         * amount.
-         */
-        int mLastScrollDelta;
-
-        /**
-         * When LLM needs to layout particular views, it sets this list in which case, LayoutState
-         * will only return views from this list and return null if it cannot find an item.
-         */
-        List<RecyclerView.ViewHolder> mScrapList = null;
-
-        /**
-         * Used when there is no limit in how many views can be laid out.
-         */
-        boolean mInfinite;
-
-        /**
-         * @return true if there are more items in the data adapter
-         */
-        boolean hasMore(RecyclerView.State state) {
-            return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
-        }
-
-        /**
-         * Gets the view for the next element that we should layout.
-         * Also updates current item index to the next item, based on {@link #mItemDirection}
-         *
-         * @return The next element that we should layout.
-         */
-        View next(RecyclerView.Recycler recycler) {
-            if (mScrapList != null) {
-                return nextViewFromScrapList();
-            }
-            final View view = recycler.getViewForPosition(mCurrentPosition);
-            mCurrentPosition += mItemDirection;
-            return view;
-        }
-
-        /**
-         * Returns the next item from the scrap list.
-         * <p>
-         * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection
-         *
-         * @return View if an item in the current position or direction exists if not null.
-         */
-        private View nextViewFromScrapList() {
-            final int size = mScrapList.size();
-            for (int i = 0; i < size; i++) {
-                final View view = mScrapList.get(i).itemView;
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                if (lp.isItemRemoved()) {
-                    continue;
-                }
-                if (mCurrentPosition == lp.getViewLayoutPosition()) {
-                    assignPositionFromScrapList(view);
-                    return view;
-                }
-            }
-            return null;
-        }
-
-        public void assignPositionFromScrapList() {
-            assignPositionFromScrapList(null);
-        }
-
-        public void assignPositionFromScrapList(View ignore) {
-            final View closest = nextViewInLimitedList(ignore);
-            if (closest == null) {
-                mCurrentPosition = NO_POSITION;
-            } else {
-                mCurrentPosition = ((LayoutParams) closest.getLayoutParams())
-                        .getViewLayoutPosition();
-            }
-        }
-
-        public View nextViewInLimitedList(View ignore) {
-            int size = mScrapList.size();
-            View closest = null;
-            int closestDistance = Integer.MAX_VALUE;
-            if (DEBUG && mIsPreLayout) {
-                throw new IllegalStateException("Scrap list cannot be used in pre layout");
-            }
-            for (int i = 0; i < size; i++) {
-                View view = mScrapList.get(i).itemView;
-                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                if (view == ignore || lp.isItemRemoved()) {
-                    continue;
-                }
-                final int distance = (lp.getViewLayoutPosition() - mCurrentPosition)
-                        * mItemDirection;
-                if (distance < 0) {
-                    continue; // item is not in current direction
-                }
-                if (distance < closestDistance) {
-                    closest = view;
-                    closestDistance = distance;
-                    if (distance == 0) {
-                        break;
-                    }
-                }
-            }
-            return closest;
-        }
-
-        void log() {
-            Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:"
-                    + mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class SavedState implements Parcelable {
-
-        int mAnchorPosition;
-
-        int mAnchorOffset;
-
-        boolean mAnchorLayoutFromEnd;
-
-        public SavedState() {
-
-        }
-
-        SavedState(Parcel in) {
-            mAnchorPosition = in.readInt();
-            mAnchorOffset = in.readInt();
-            mAnchorLayoutFromEnd = in.readInt() == 1;
-        }
-
-        public SavedState(SavedState other) {
-            mAnchorPosition = other.mAnchorPosition;
-            mAnchorOffset = other.mAnchorOffset;
-            mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd;
-        }
-
-        boolean hasValidAnchor() {
-            return mAnchorPosition >= 0;
-        }
-
-        void invalidateAnchor() {
-            mAnchorPosition = NO_POSITION;
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mAnchorPosition);
-            dest.writeInt(mAnchorOffset);
-            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);
-                    }
-
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                };
-    }
-
-    /**
-     * Simple data class to keep Anchor information
-     */
-    static class AnchorInfo {
-        OrientationHelper mOrientationHelper;
-        int mPosition;
-        int mCoordinate;
-        boolean mLayoutFromEnd;
-        boolean mValid;
-
-        AnchorInfo() {
-            reset();
-        }
-
-        void reset() {
-            mPosition = NO_POSITION;
-            mCoordinate = INVALID_OFFSET;
-            mLayoutFromEnd = false;
-            mValid = false;
-        }
-
-        /**
-         * assigns anchor coordinate from the RecyclerView's padding depending on current
-         * layoutFromEnd value
-         */
-        void assignCoordinateFromPadding() {
-            mCoordinate = mLayoutFromEnd
-                    ? mOrientationHelper.getEndAfterPadding()
-                    : mOrientationHelper.getStartAfterPadding();
-        }
-
-        @Override
-        public String toString() {
-            return "AnchorInfo{"
-                    + "mPosition=" + mPosition
-                    + ", mCoordinate=" + mCoordinate
-                    + ", mLayoutFromEnd=" + mLayoutFromEnd
-                    + ", mValid=" + mValid
-                    + '}';
-        }
-
-        boolean isViewValidAsAnchor(View child, RecyclerView.State state) {
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            return !lp.isItemRemoved() && lp.getViewLayoutPosition() >= 0
-                    && lp.getViewLayoutPosition() < state.getItemCount();
-        }
-
-        public void assignFromViewAndKeepVisibleRect(View child, int position) {
-            final int spaceChange = mOrientationHelper.getTotalSpaceChange();
-            if (spaceChange >= 0) {
-                assignFromView(child, position);
-                return;
-            }
-            mPosition = position;
-            if (mLayoutFromEnd) {
-                final int prevLayoutEnd = mOrientationHelper.getEndAfterPadding() - spaceChange;
-                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
-                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
-                    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 startReference = layoutStart + Math.min(previousStartMargin, 0);
-                    final int startMargin = estimatedChildStart - startReference;
-                    if (startMargin < 0) {
-                        // offset to make top visible but not too much
-                        mCoordinate += Math.min(previousEndMargin, -startMargin);
-                    }
-                }
-            } else {
-                final int childStart = mOrientationHelper.getDecoratedStart(child);
-                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 endMargin = endReference - estimatedEnd;
-                    if (endMargin < 0) {
-                        mCoordinate -= Math.min(startMargin, -endMargin);
-                    }
-                }
-            }
-        }
-
-        public void assignFromView(View child, int position) {
-            if (mLayoutFromEnd) {
-                mCoordinate = mOrientationHelper.getDecoratedEnd(child)
-                        + mOrientationHelper.getTotalSpaceChange();
-            } else {
-                mCoordinate = mOrientationHelper.getDecoratedStart(child);
-            }
-
-            mPosition = position;
-        }
-    }
-
-    protected static class LayoutChunkResult {
-        public int mConsumed;
-        public boolean mFinished;
-        public boolean mIgnoreConsumed;
-        public boolean mFocusable;
-
-        void resetInternal() {
-            mConsumed = 0;
-            mFinished = false;
-            mIgnoreConsumed = false;
-            mFocusable = false;
-        }
-    }
-}
diff --git a/android/support/v7/widget/LinearSmoothScroller.java b/android/support/v7/widget/LinearSmoothScroller.java
deleted file mode 100644
index 74d6a3b..0000000
--- a/android/support/v7/widget/LinearSmoothScroller.java
+++ /dev/null
@@ -1,361 +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.content.Context;
-import android.graphics.PointF;
-import android.support.annotation.Nullable;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
-
-/**
- * {@link RecyclerView.SmoothScroller} implementation which uses a {@link LinearInterpolator} until
- * the target position becomes a child of the RecyclerView and then uses a
- * {@link DecelerateInterpolator} to slowly approach to target position.
- * <p>
- * If the {@link RecyclerView.LayoutManager} you are using does not implement the
- * {@link RecyclerView.SmoothScroller.ScrollVectorProvider} interface, then you must override the
- * {@link #computeScrollVectorForPosition(int)} method. All the LayoutManagers bundled with
- * the support library implement this interface.
- */
-public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
-
-    private static final String TAG = "LinearSmoothScroller";
-
-    private static final boolean DEBUG = false;
-
-    private static final float MILLISECONDS_PER_INCH = 25f;
-
-    private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
-
-    /**
-     * Align child view's left or top with parent view's left or top
-     *
-     * @see #calculateDtToFit(int, int, int, int, int)
-     * @see #calculateDxToMakeVisible(android.view.View, int)
-     * @see #calculateDyToMakeVisible(android.view.View, int)
-     */
-    public static final int SNAP_TO_START = -1;
-
-    /**
-     * Align child view's right or bottom with parent view's right or bottom
-     *
-     * @see #calculateDtToFit(int, int, int, int, int)
-     * @see #calculateDxToMakeVisible(android.view.View, int)
-     * @see #calculateDyToMakeVisible(android.view.View, int)
-     */
-    public static final int SNAP_TO_END = 1;
-
-    /**
-     * <p>Decides if the child should be snapped from start or end, depending on where it
-     * currently is in relation to its parent.</p>
-     * <p>For instance, if the view is virtually on the left of RecyclerView, using
-     * {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}</p>
-     *
-     * @see #calculateDtToFit(int, int, int, int, int)
-     * @see #calculateDxToMakeVisible(android.view.View, int)
-     * @see #calculateDyToMakeVisible(android.view.View, int)
-     */
-    public static final int SNAP_TO_ANY = 0;
-
-    // Trigger a scroll to a further distance than TARGET_SEEK_SCROLL_DISTANCE_PX so that if target
-    // view is not laid out until interim target position is reached, we can detect the case before
-    // scrolling slows down and reschedule another interim target scroll
-    private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f;
-
-    protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
-
-    protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
-
-    protected PointF mTargetVector;
-
-    private final float MILLISECONDS_PER_PX;
-
-    // Temporary variables to keep track of the interim scroll target. These values do not
-    // point to a real item position, rather point to an estimated location pixels.
-    protected int mInterimTargetDx = 0, mInterimTargetDy = 0;
-
-    public LinearSmoothScroller(Context context) {
-        MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onStart() {
-
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
-        final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
-        final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
-        final int distance = (int) Math.sqrt(dx * dx + dy * dy);
-        final int time = calculateTimeForDeceleration(distance);
-        if (time > 0) {
-            action.update(-dx, -dy, time, mDecelerateInterpolator);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) {
-        if (getChildCount() == 0) {
-            stop();
-            return;
-        }
-        //noinspection PointlessBooleanExpression
-        if (DEBUG && mTargetVector != null
-                && ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) {
-            throw new IllegalStateException("Scroll happened in the opposite direction"
-                    + " of the target. Some calculations are wrong");
-        }
-        mInterimTargetDx = clampApplyScroll(mInterimTargetDx, dx);
-        mInterimTargetDy = clampApplyScroll(mInterimTargetDy, dy);
-
-        if (mInterimTargetDx == 0 && mInterimTargetDy == 0) {
-            updateActionForInterimTarget(action);
-        } // everything is valid, keep going
-
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onStop() {
-        mInterimTargetDx = mInterimTargetDy = 0;
-        mTargetVector = null;
-    }
-
-    /**
-     * Calculates the scroll speed.
-     *
-     * @param displayMetrics DisplayMetrics to be used for real dimension calculations
-     * @return The time (in ms) it should take for each pixel. For instance, if returned value is
-     * 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
-     */
-    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
-        return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
-    }
-
-    /**
-     * <p>Calculates the time for deceleration so that transition from LinearInterpolator to
-     * DecelerateInterpolator looks smooth.</p>
-     *
-     * @param dx Distance to scroll
-     * @return Time for DecelerateInterpolator to smoothly traverse the distance when transitioning
-     * from LinearInterpolation
-     */
-    protected int calculateTimeForDeceleration(int dx) {
-        // we want to cover same area with the linear interpolator for the first 10% of the
-        // interpolation. After that, deceleration will take control.
-        // area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x
-        // which gives 0.100028 when x = .3356
-        // this is why we divide linear scrolling time with .3356
-        return  (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
-    }
-
-    /**
-     * Calculates the time it should take to scroll the given distance (in pixels)
-     *
-     * @param dx Distance in pixels that we want to scroll
-     * @return Time in milliseconds
-     * @see #calculateSpeedPerPixel(android.util.DisplayMetrics)
-     */
-    protected int calculateTimeForScrolling(int dx) {
-        // In a case where dx is very small, rounding may return 0 although dx > 0.
-        // To avoid that issue, ceil the result so that if dx > 0, we'll always return positive
-        // time.
-        return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX);
-    }
-
-    /**
-     * When scrolling towards a child view, this method defines whether we should align the left
-     * or the right edge of the child with the parent RecyclerView.
-     *
-     * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
-     * @see #SNAP_TO_START
-     * @see #SNAP_TO_END
-     * @see #SNAP_TO_ANY
-     */
-    protected int getHorizontalSnapPreference() {
-        return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY :
-                mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START;
-    }
-
-    /**
-     * When scrolling towards a child view, this method defines whether we should align the top
-     * or the bottom edge of the child with the parent RecyclerView.
-     *
-     * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
-     * @see #SNAP_TO_START
-     * @see #SNAP_TO_END
-     * @see #SNAP_TO_ANY
-     */
-    protected int getVerticalSnapPreference() {
-        return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
-                mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
-    }
-
-    /**
-     * When the target scroll position is not a child of the RecyclerView, this method calculates
-     * a direction vector towards that child and triggers a smooth scroll.
-     *
-     * @see #computeScrollVectorForPosition(int)
-     */
-    protected void updateActionForInterimTarget(Action action) {
-        // find an interim target position
-        PointF scrollVector = computeScrollVectorForPosition(getTargetPosition());
-        if (scrollVector == null || (scrollVector.x == 0 && scrollVector.y == 0)) {
-            final int target = getTargetPosition();
-            action.jumpTo(target);
-            stop();
-            return;
-        }
-        normalize(scrollVector);
-        mTargetVector = scrollVector;
-
-        mInterimTargetDx = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.x);
-        mInterimTargetDy = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.y);
-        final int time = calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX);
-        // 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);
-    }
-
-    private int clampApplyScroll(int tmpDt, int dt) {
-        final int before = tmpDt;
-        tmpDt -= dt;
-        if (before * tmpDt <= 0) { // changed sign, reached 0 or was 0, reset
-            return 0;
-        }
-        return tmpDt;
-    }
-
-    /**
-     * Helper method for {@link #calculateDxToMakeVisible(android.view.View, int)} and
-     * {@link #calculateDyToMakeVisible(android.view.View, int)}
-     */
-    public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
-            snapPreference) {
-        switch (snapPreference) {
-            case SNAP_TO_START:
-                return boxStart - viewStart;
-            case SNAP_TO_END:
-                return boxEnd - viewEnd;
-            case SNAP_TO_ANY:
-                final int dtStart = boxStart - viewStart;
-                if (dtStart > 0) {
-                    return dtStart;
-                }
-                final int dtEnd = boxEnd - viewEnd;
-                if (dtEnd < 0) {
-                    return dtEnd;
-                }
-                break;
-            default:
-                throw new IllegalArgumentException("snap preference should be one of the"
-                        + " constants defined in SmoothScroller, starting with SNAP_");
-        }
-        return 0;
-    }
-
-    /**
-     * Calculates the vertical scroll amount necessary to make the given view fully visible
-     * inside the RecyclerView.
-     *
-     * @param view           The view which we want to make fully visible
-     * @param snapPreference The edge which the view should snap to when entering the visible
-     *                       area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
-     *                       {@link #SNAP_TO_ANY}.
-     * @return The vertical scroll amount necessary to make the view visible with the given
-     * snap preference.
-     */
-    public int calculateDyToMakeVisible(View view, int snapPreference) {
-        final RecyclerView.LayoutManager layoutManager = getLayoutManager();
-        if (layoutManager == null || !layoutManager.canScrollVertically()) {
-            return 0;
-        }
-        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                view.getLayoutParams();
-        final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
-        final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
-        final int start = layoutManager.getPaddingTop();
-        final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
-        return calculateDtToFit(top, bottom, start, end, snapPreference);
-    }
-
-    /**
-     * Calculates the horizontal scroll amount necessary to make the given view fully visible
-     * inside the RecyclerView.
-     *
-     * @param view           The view which we want to make fully visible
-     * @param snapPreference The edge which the view should snap to when entering the visible
-     *                       area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
-     *                       {@link #SNAP_TO_END}
-     * @return The vertical scroll amount necessary to make the view visible with the given
-     * snap preference.
-     */
-    public int calculateDxToMakeVisible(View view, int snapPreference) {
-        final RecyclerView.LayoutManager layoutManager = getLayoutManager();
-        if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
-            return 0;
-        }
-        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                view.getLayoutParams();
-        final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin;
-        final int right = layoutManager.getDecoratedRight(view) + params.rightMargin;
-        final int start = layoutManager.getPaddingLeft();
-        final int end = layoutManager.getWidth() - layoutManager.getPaddingRight();
-        return calculateDtToFit(left, right, start, end, snapPreference);
-    }
-
-    /**
-     * Compute the scroll vector for a given target position.
-     * <p>
-     * This method can return null if the layout manager cannot calculate a scroll vector
-     * for the given position (e.g. it has no current scroll position).
-     *
-     * @param targetPosition the position to which the scroller is scrolling
-     *
-     * @return the scroll vector for a given target position
-     */
-    @Nullable
-    public PointF computeScrollVectorForPosition(int targetPosition) {
-        RecyclerView.LayoutManager layoutManager = getLayoutManager();
-        if (layoutManager instanceof ScrollVectorProvider) {
-            return ((ScrollVectorProvider) layoutManager)
-                    .computeScrollVectorForPosition(targetPosition);
-        }
-        Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
-                + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
-        return null;
-    }
-}
diff --git a/android/support/v7/widget/LinearSnapHelper.java b/android/support/v7/widget/LinearSnapHelper.java
deleted file mode 100644
index 869bc0d..0000000
--- a/android/support/v7/widget/LinearSnapHelper.java
+++ /dev/null
@@ -1,286 +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.widget;
-
-import android.graphics.PointF;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.View;
-
-/**
- * Implementation of the {@link SnapHelper} supporting snapping in either vertical or horizontal
- * orientation.
- * <p>
- * The implementation will snap the center of the target child view to the center of
- * the attached {@link RecyclerView}. If you intend to change this behavior then override
- * {@link SnapHelper#calculateDistanceToFinalSnap}.
- */
-public class LinearSnapHelper extends SnapHelper {
-
-    private static final float INVALID_DISTANCE = 1f;
-
-    // Orientation helpers are lazily created per LayoutManager.
-    @Nullable
-    private OrientationHelper mVerticalHelper;
-    @Nullable
-    private OrientationHelper mHorizontalHelper;
-
-    @Override
-    public int[] calculateDistanceToFinalSnap(
-            @NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
-        int[] out = new int[2];
-        if (layoutManager.canScrollHorizontally()) {
-            out[0] = distanceToCenter(layoutManager, targetView,
-                    getHorizontalHelper(layoutManager));
-        } else {
-            out[0] = 0;
-        }
-
-        if (layoutManager.canScrollVertically()) {
-            out[1] = distanceToCenter(layoutManager, targetView,
-                    getVerticalHelper(layoutManager));
-        } else {
-            out[1] = 0;
-        }
-        return out;
-    }
-
-    @Override
-    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
-            int velocityY) {
-        if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        final int itemCount = layoutManager.getItemCount();
-        if (itemCount == 0) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        final View currentView = findSnapView(layoutManager);
-        if (currentView == null) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        final int currentPosition = layoutManager.getPosition(currentView);
-        if (currentPosition == RecyclerView.NO_POSITION) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider =
-                (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager;
-        // deltaJumps sign comes from the velocity which may not match the order of children in
-        // the LayoutManager. To overcome this, we ask for a vector from the LayoutManager to
-        // get the direction.
-        PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1);
-        if (vectorForEnd == null) {
-            // cannot get a vector for the given position.
-            return RecyclerView.NO_POSITION;
-        }
-
-        int vDeltaJump, hDeltaJump;
-        if (layoutManager.canScrollHorizontally()) {
-            hDeltaJump = estimateNextPositionDiffForFling(layoutManager,
-                    getHorizontalHelper(layoutManager), velocityX, 0);
-            if (vectorForEnd.x < 0) {
-                hDeltaJump = -hDeltaJump;
-            }
-        } else {
-            hDeltaJump = 0;
-        }
-        if (layoutManager.canScrollVertically()) {
-            vDeltaJump = estimateNextPositionDiffForFling(layoutManager,
-                    getVerticalHelper(layoutManager), 0, velocityY);
-            if (vectorForEnd.y < 0) {
-                vDeltaJump = -vDeltaJump;
-            }
-        } else {
-            vDeltaJump = 0;
-        }
-
-        int deltaJump = layoutManager.canScrollVertically() ? vDeltaJump : hDeltaJump;
-        if (deltaJump == 0) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        int targetPos = currentPosition + deltaJump;
-        if (targetPos < 0) {
-            targetPos = 0;
-        }
-        if (targetPos >= itemCount) {
-            targetPos = itemCount - 1;
-        }
-        return targetPos;
-    }
-
-    @Override
-    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
-        if (layoutManager.canScrollVertically()) {
-            return findCenterView(layoutManager, getVerticalHelper(layoutManager));
-        } else if (layoutManager.canScrollHorizontally()) {
-            return findCenterView(layoutManager, getHorizontalHelper(layoutManager));
-        }
-        return null;
-    }
-
-    private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
-            @NonNull View targetView, OrientationHelper helper) {
-        final int childCenter = helper.getDecoratedStart(targetView)
-                + (helper.getDecoratedMeasurement(targetView) / 2);
-        final int containerCenter;
-        if (layoutManager.getClipToPadding()) {
-            containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
-        } else {
-            containerCenter = helper.getEnd() / 2;
-        }
-        return childCenter - containerCenter;
-    }
-
-    /**
-     * Estimates a position to which SnapHelper will try to scroll to in response to a fling.
-     *
-     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}.
-     * @param helper        The {@link OrientationHelper} that is created from the LayoutManager.
-     * @param velocityX     The velocity on the x axis.
-     * @param velocityY     The velocity on the y axis.
-     *
-     * @return The diff between the target scroll position and the current position.
-     */
-    private int estimateNextPositionDiffForFling(RecyclerView.LayoutManager layoutManager,
-            OrientationHelper helper, int velocityX, int velocityY) {
-        int[] distances = calculateScrollDistance(velocityX, velocityY);
-        float distancePerChild = computeDistancePerChild(layoutManager, helper);
-        if (distancePerChild <= 0) {
-            return 0;
-        }
-        int distance =
-                Math.abs(distances[0]) > Math.abs(distances[1]) ? distances[0] : distances[1];
-        return (int) Math.round(distance / distancePerChild);
-    }
-
-    /**
-     * Return the child view that is currently closest to the center of this parent.
-     *
-     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}.
-     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
-     *
-     * @return the child view that is currently closest to the center of this parent.
-     */
-    @Nullable
-    private View findCenterView(RecyclerView.LayoutManager layoutManager,
-            OrientationHelper helper) {
-        int childCount = layoutManager.getChildCount();
-        if (childCount == 0) {
-            return null;
-        }
-
-        View closestChild = null;
-        final int center;
-        if (layoutManager.getClipToPadding()) {
-            center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
-        } else {
-            center = helper.getEnd() / 2;
-        }
-        int absClosest = Integer.MAX_VALUE;
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = layoutManager.getChildAt(i);
-            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  **/
-            if (absDistance < absClosest) {
-                absClosest = absDistance;
-                closestChild = child;
-            }
-        }
-        return closestChild;
-    }
-
-    /**
-     * Computes an average pixel value to pass a single child.
-     * <p>
-     * Returns a negative value if it cannot be calculated.
-     *
-     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}.
-     * @param helper        The relevant {@link OrientationHelper} for the attached
-     *                      {@link RecyclerView.LayoutManager}.
-     *
-     * @return A float value that is the average number of pixels needed to scroll by one view in
-     * the relevant direction.
-     */
-    private float computeDistancePerChild(RecyclerView.LayoutManager layoutManager,
-            OrientationHelper helper) {
-        View minPosView = null;
-        View maxPosView = null;
-        int minPos = Integer.MAX_VALUE;
-        int maxPos = Integer.MIN_VALUE;
-        int childCount = layoutManager.getChildCount();
-        if (childCount == 0) {
-            return INVALID_DISTANCE;
-        }
-
-        for (int i = 0; i < childCount; i++) {
-            View child = layoutManager.getChildAt(i);
-            final int pos = layoutManager.getPosition(child);
-            if (pos == RecyclerView.NO_POSITION) {
-                continue;
-            }
-            if (pos < minPos) {
-                minPos = pos;
-                minPosView = child;
-            }
-            if (pos > maxPos) {
-                maxPos = pos;
-                maxPosView = child;
-            }
-        }
-        if (minPosView == null || maxPosView == null) {
-            return INVALID_DISTANCE;
-        }
-        int start = Math.min(helper.getDecoratedStart(minPosView),
-                helper.getDecoratedStart(maxPosView));
-        int end = Math.max(helper.getDecoratedEnd(minPosView),
-                helper.getDecoratedEnd(maxPosView));
-        int distance = end - start;
-        if (distance == 0) {
-            return INVALID_DISTANCE;
-        }
-        return 1f * distance / ((maxPos - minPos) + 1);
-    }
-
-    @NonNull
-    private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
-        if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) {
-            mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
-        }
-        return mVerticalHelper;
-    }
-
-    @NonNull
-    private OrientationHelper getHorizontalHelper(
-            @NonNull RecyclerView.LayoutManager layoutManager) {
-        if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) {
-            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
-        }
-        return mHorizontalHelper;
-    }
-}
diff --git a/android/support/v7/widget/ListPopupWindow.java b/android/support/v7/widget/ListPopupWindow.java
deleted file mode 100644
index b98197c..0000000
--- a/android/support/v7/widget/ListPopupWindow.java
+++ /dev/null
@@ -1,1431 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.support.annotation.AttrRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StyleRes;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.PopupWindowCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.menu.ShowableListMenu;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.WindowManager;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.PopupWindow;
-
-import java.lang.reflect.Method;
-
-/**
- * Static library support version of the framework's {@link android.widget.ListPopupWindow}.
- * Used to write apps that run on platforms prior to Android L. When running
- * on Android L or above, this implementation is still used; it does not try
- * to switch to the framework's implementation. See the framework SDK
- * documentation for a class overview.
- *
- * @see android.widget.ListPopupWindow
- */
-public class ListPopupWindow implements ShowableListMenu {
-    private static final String TAG = "ListPopupWindow";
-    private static final boolean DEBUG = false;
-
-    /**
-     * This value controls the length of time that the user
-     * must leave a pointer down without scrolling to expand
-     * the autocomplete dropdown list to cover the IME.
-     */
-    static final int EXPAND_LIST_TIMEOUT = 250;
-
-    private static Method sClipToWindowEnabledMethod;
-    private static Method sGetMaxAvailableHeightMethod;
-    private static Method sSetEpicenterBoundsMethod;
-
-    static {
-        try {
-            sClipToWindowEnabledMethod = PopupWindow.class.getDeclaredMethod(
-                    "setClipToScreenEnabled", boolean.class);
-        } catch (NoSuchMethodException e) {
-            Log.i(TAG, "Could not find method setClipToScreenEnabled() on PopupWindow. Oh well.");
-        }
-        try {
-            sGetMaxAvailableHeightMethod = PopupWindow.class.getDeclaredMethod(
-                    "getMaxAvailableHeight", View.class, int.class, boolean.class);
-        } catch (NoSuchMethodException e) {
-            Log.i(TAG, "Could not find method getMaxAvailableHeight(View, int, boolean)"
-                    + " on PopupWindow. Oh well.");
-        }
-        try {
-            sSetEpicenterBoundsMethod = PopupWindow.class.getDeclaredMethod(
-                    "setEpicenterBounds", Rect.class);
-        } catch (NoSuchMethodException e) {
-            Log.i(TAG, "Could not find method setEpicenterBounds(Rect) on PopupWindow. Oh well.");
-        }
-    }
-
-    private Context mContext;
-    private ListAdapter mAdapter;
-    DropDownListView mDropDownList;
-
-    private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
-    private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
-    private int mDropDownHorizontalOffset;
-    private int mDropDownVerticalOffset;
-    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;
-
-    private boolean mDropDownAlwaysVisible = false;
-    private boolean mForceIgnoreOutsideTouch = false;
-    int mListItemExpandMaximum = Integer.MAX_VALUE;
-
-    private View mPromptView;
-    private int mPromptPosition = POSITION_PROMPT_ABOVE;
-
-    private DataSetObserver mObserver;
-
-    private View mDropDownAnchorView;
-
-    private Drawable mDropDownListHighlight;
-
-    private AdapterView.OnItemClickListener mItemClickListener;
-    private OnItemSelectedListener mItemSelectedListener;
-
-    final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
-    private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
-    private final PopupScrollListener mScrollListener = new PopupScrollListener();
-    private final ListSelectorHider mHideSelector = new ListSelectorHider();
-    private Runnable mShowDropDownRunnable;
-
-    final Handler mHandler;
-
-    private final Rect mTempRect = new Rect();
-
-    /**
-     * Optional anchor-relative bounds to be used as the transition epicenter.
-     * When {@code null}, the anchor bounds are used as the epicenter.
-     */
-    private Rect mEpicenterBounds;
-
-    private boolean mModal;
-
-    PopupWindow mPopup;
-
-    /**
-     * The provided prompt view should appear above list content.
-     *
-     * @see #setPromptPosition(int)
-     * @see #getPromptPosition()
-     * @see #setPromptView(View)
-     */
-    public static final int POSITION_PROMPT_ABOVE = 0;
-
-    /**
-     * The provided prompt view should appear below list content.
-     *
-     * @see #setPromptPosition(int)
-     * @see #getPromptPosition()
-     * @see #setPromptView(View)
-     */
-    public static final int POSITION_PROMPT_BELOW = 1;
-
-    /**
-     * Alias for {@link ViewGroup.LayoutParams#MATCH_PARENT}.
-     * If used to specify a popup width, the popup will match the width of the anchor view.
-     * If used to specify a popup height, the popup will fill available space.
-     */
-    public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
-
-    /**
-     * Alias for {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
-     * If used to specify a popup width, the popup will use the width of its content.
-     */
-    public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
-
-    /**
-     * Mode for {@link #setInputMethodMode(int)}: the requirements for the
-     * input method should be based on the focusability of the popup.  That is
-     * if it is focusable than it needs to work with the input method, else
-     * it doesn't.
-     */
-    public static final int INPUT_METHOD_FROM_FOCUSABLE = PopupWindow.INPUT_METHOD_FROM_FOCUSABLE;
-
-    /**
-     * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
-     * work with an input method, regardless of whether it is focusable.  This
-     * means that it will always be displayed so that the user can also operate
-     * the input method while it is shown.
-     */
-    public static final int INPUT_METHOD_NEEDED = PopupWindow.INPUT_METHOD_NEEDED;
-
-    /**
-     * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
-     * work with an input method, regardless of whether it is focusable.  This
-     * means that it will always be displayed to use as much space on the
-     * screen as needed, regardless of whether this covers the input method.
-     */
-    public static final int INPUT_METHOD_NOT_NEEDED = PopupWindow.INPUT_METHOD_NOT_NEEDED;
-
-    /**
-     * Create a new, empty popup window capable of displaying items from a ListAdapter.
-     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
-     *
-     * @param context Context used for contained views.
-     */
-    public ListPopupWindow(@NonNull Context context) {
-        this(context, null, R.attr.listPopupWindowStyle);
-    }
-
-    /**
-     * Create a new, empty popup window capable of displaying items from a ListAdapter.
-     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
-     *
-     * @param context Context used for contained views.
-     * @param attrs   Attributes from inflating parent views used to style the popup.
-     */
-    public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, R.attr.listPopupWindowStyle);
-    }
-
-    /**
-     * Create a new, empty popup window capable of displaying items from a ListAdapter.
-     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
-     *
-     * @param context Context used for contained views.
-     * @param attrs Attributes from inflating parent views used to style the popup.
-     * @param defStyleAttr Default style attribute to use for popup content.
-     */
-    public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
-            @AttrRes int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    /**
-     * Create a new, empty popup window capable of displaying items from a ListAdapter.
-     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
-     *
-     * @param context Context used for contained views.
-     * @param attrs Attributes from inflating parent views used to style the popup.
-     * @param defStyleAttr Style attribute to read for default styling of popup content.
-     * @param defStyleRes Style resource ID to use for default styling of popup content.
-     */
-    public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
-            @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
-        mContext = context;
-        mHandler = new Handler(context.getMainLooper());
-
-        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ListPopupWindow,
-                defStyleAttr, defStyleRes);
-        mDropDownHorizontalOffset = a.getDimensionPixelOffset(
-                R.styleable.ListPopupWindow_android_dropDownHorizontalOffset, 0);
-        mDropDownVerticalOffset = a.getDimensionPixelOffset(
-                R.styleable.ListPopupWindow_android_dropDownVerticalOffset, 0);
-        if (mDropDownVerticalOffset != 0) {
-            mDropDownVerticalOffsetSet = true;
-        }
-        a.recycle();
-
-        mPopup = new AppCompatPopupWindow(context, attrs, defStyleAttr, defStyleRes);
-        mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-    }
-
-    /**
-     * Sets the adapter that provides the data and the views to represent the data
-     * in this popup window.
-     *
-     * @param adapter The adapter to use to create this window's content.
-     */
-    public void setAdapter(@Nullable ListAdapter adapter) {
-        if (mObserver == null) {
-            mObserver = new PopupDataSetObserver();
-        } else if (mAdapter != null) {
-            mAdapter.unregisterDataSetObserver(mObserver);
-        }
-        mAdapter = adapter;
-        if (adapter != null) {
-            adapter.registerDataSetObserver(mObserver);
-        }
-
-        if (mDropDownList != null) {
-            mDropDownList.setAdapter(mAdapter);
-        }
-    }
-
-    /**
-     * Set where the optional prompt view should appear. The default is
-     * {@link #POSITION_PROMPT_ABOVE}.
-     *
-     * @param position A position constant declaring where the prompt should be displayed.
-     *
-     * @see #POSITION_PROMPT_ABOVE
-     * @see #POSITION_PROMPT_BELOW
-     */
-    public void setPromptPosition(int position) {
-        mPromptPosition = position;
-    }
-
-    /**
-     * @return Where the optional prompt view should appear.
-     *
-     * @see #POSITION_PROMPT_ABOVE
-     * @see #POSITION_PROMPT_BELOW
-     */
-    public int getPromptPosition() {
-        return mPromptPosition;
-    }
-
-    /**
-     * Set whether this window should be modal when shown.
-     *
-     * <p>If a popup window is modal, it will receive all touch and key input.
-     * If the user touches outside the popup window's content area the popup window
-     * will be dismissed.
-     *
-     * @param modal {@code true} if the popup window should be modal, {@code false} otherwise.
-     */
-    public void setModal(boolean modal) {
-        mModal = modal;
-        mPopup.setFocusable(modal);
-    }
-
-    /**
-     * Returns whether the popup window will be modal when shown.
-     *
-     * @return {@code true} if the popup window will be modal, {@code false} otherwise.
-     */
-    public boolean isModal() {
-        return mModal;
-    }
-
-    /**
-     * Forces outside touches to be ignored. Normally if {@link #isDropDownAlwaysVisible()} is
-     * false, we allow outside touch to dismiss the dropdown. If this is set to true, then we
-     * ignore outside touch even when the drop down is not set to always visible.
-     *
-     * @hide Used only by AutoCompleteTextView to handle some internal special cases.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
-        mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
-    }
-
-    /**
-     * Sets whether the drop-down should remain visible under certain conditions.
-     *
-     * The drop-down will occupy the entire screen below {@link #getAnchorView} regardless
-     * of the size or content of the list.  {@link #getBackground()} will fill any space
-     * that is not used by the list.
-     *
-     * @param dropDownAlwaysVisible Whether to keep the drop-down visible.
-     *
-     * @hide Only used by AutoCompleteTextView under special conditions.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
-        mDropDownAlwaysVisible = dropDownAlwaysVisible;
-    }
-
-    /**
-     * @return Whether the drop-down is visible under special conditions.
-     *
-     * @hide Only used by AutoCompleteTextView under special conditions.
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isDropDownAlwaysVisible() {
-        return mDropDownAlwaysVisible;
-    }
-
-    /**
-     * Sets the operating mode for the soft input area.
-     *
-     * @param mode The desired mode, see
-     *        {@link android.view.WindowManager.LayoutParams#softInputMode}
-     *        for the full list
-     *
-     * @see android.view.WindowManager.LayoutParams#softInputMode
-     * @see #getSoftInputMode()
-     */
-    public void setSoftInputMode(int mode) {
-        mPopup.setSoftInputMode(mode);
-    }
-
-    /**
-     * Returns the current value in {@link #setSoftInputMode(int)}.
-     *
-     * @see #setSoftInputMode(int)
-     * @see android.view.WindowManager.LayoutParams#softInputMode
-     */
-    public int getSoftInputMode() {
-        return mPopup.getSoftInputMode();
-    }
-
-    /**
-     * Sets a drawable to use as the list item selector.
-     *
-     * @param selector List selector drawable to use in the popup.
-     */
-    public void setListSelector(Drawable selector) {
-        mDropDownListHighlight = selector;
-    }
-
-    /**
-     * @return The background drawable for the popup window.
-     */
-    public @Nullable Drawable getBackground() {
-        return mPopup.getBackground();
-    }
-
-    /**
-     * Sets a drawable to be the background for the popup window.
-     *
-     * @param d A drawable to set as the background.
-     */
-    public void setBackgroundDrawable(@Nullable Drawable d) {
-        mPopup.setBackgroundDrawable(d);
-    }
-
-    /**
-     * Set an animation style to use when the popup window is shown or dismissed.
-     *
-     * @param animationStyle Animation style to use.
-     */
-    public void setAnimationStyle(@StyleRes int animationStyle) {
-        mPopup.setAnimationStyle(animationStyle);
-    }
-
-    /**
-     * Returns the animation style that will be used when the popup window is
-     * shown or dismissed.
-     *
-     * @return Animation style that will be used.
-     */
-    public @StyleRes int getAnimationStyle() {
-        return mPopup.getAnimationStyle();
-    }
-
-    /**
-     * Returns the view that will be used to anchor this popup.
-     *
-     * @return The popup's anchor view
-     */
-    public @Nullable View getAnchorView() {
-        return mDropDownAnchorView;
-    }
-
-    /**
-     * Sets the popup's anchor view. This popup will always be positioned relative to
-     * the anchor view when shown.
-     *
-     * @param anchor The view to use as an anchor.
-     */
-    public void setAnchorView(@Nullable View anchor) {
-        mDropDownAnchorView = anchor;
-    }
-
-    /**
-     * @return The horizontal offset of the popup from its anchor in pixels.
-     */
-    public int getHorizontalOffset() {
-        return mDropDownHorizontalOffset;
-    }
-
-    /**
-     * Set the horizontal offset of this popup from its anchor view in pixels.
-     *
-     * @param offset The horizontal offset of the popup from its anchor.
-     */
-    public void setHorizontalOffset(int offset) {
-        mDropDownHorizontalOffset = offset;
-    }
-
-    /**
-     * @return The vertical offset of the popup from its anchor in pixels.
-     */
-    public int getVerticalOffset() {
-        if (!mDropDownVerticalOffsetSet) {
-            return 0;
-        }
-        return mDropDownVerticalOffset;
-    }
-
-    /**
-     * Set the vertical offset of this popup from its anchor view in pixels.
-     *
-     * @param offset The vertical offset of the popup from its anchor.
-     */
-    public void setVerticalOffset(int offset) {
-        mDropDownVerticalOffset = offset;
-        mDropDownVerticalOffsetSet = true;
-    }
-
-    /**
-     * Specifies the anchor-relative bounds of the popup's transition
-     * epicenter.
-     *
-     * @param bounds anchor-relative bounds
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setEpicenterBounds(Rect bounds) {
-        mEpicenterBounds = bounds;
-    }
-
-    /**
-     * Set the gravity of the dropdown list. This is commonly used to
-     * set gravity to START or END for alignment with the anchor.
-     *
-     * @param gravity Gravity value to use
-     */
-    public void setDropDownGravity(int gravity) {
-        mDropDownGravity = gravity;
-    }
-
-    /**
-     * @return The width of the popup window in pixels.
-     */
-    public int getWidth() {
-        return mDropDownWidth;
-    }
-
-    /**
-     * Sets the width of the popup window in pixels. Can also be {@link #MATCH_PARENT}
-     * or {@link #WRAP_CONTENT}.
-     *
-     * @param width Width of the popup window.
-     */
-    public void setWidth(int width) {
-        mDropDownWidth = width;
-    }
-
-    /**
-     * Sets the width of the popup window by the size of its content. The final width may be
-     * larger to accommodate styled window dressing.
-     *
-     * @param width Desired width of content in pixels.
-     */
-    public void setContentWidth(int width) {
-        Drawable popupBackground = mPopup.getBackground();
-        if (popupBackground != null) {
-            popupBackground.getPadding(mTempRect);
-            mDropDownWidth = mTempRect.left + mTempRect.right + width;
-        } else {
-            setWidth(width);
-        }
-    }
-
-    /**
-     * @return The height of the popup window in pixels.
-     */
-    public int getHeight() {
-        return mDropDownHeight;
-    }
-
-    /**
-     * Sets the height of the popup window in pixels. Can also be {@link #MATCH_PARENT}.
-     *
-     * @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;
-    }
-
-    /**
-     * Set the layout type for this popup window.
-     * <p>
-     * See {@link WindowManager.LayoutParams#type} for possible values.
-     *
-     * @param layoutType Layout type for this window.
-     *
-     * @see WindowManager.LayoutParams#type
-     */
-    public void setWindowLayoutType(int layoutType) {
-        mDropDownWindowLayoutType = layoutType;
-    }
-
-    /**
-     * Sets a listener to receive events when a list item is clicked.
-     *
-     * @param clickListener Listener to register
-     *
-     * @see ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
-     */
-    public void setOnItemClickListener(@Nullable AdapterView.OnItemClickListener clickListener) {
-        mItemClickListener = clickListener;
-    }
-
-    /**
-     * Sets a listener to receive events when a list item is selected.
-     *
-     * @param selectedListener Listener to register.
-     *
-     * @see ListView#setOnItemSelectedListener(OnItemSelectedListener)
-     */
-    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener selectedListener) {
-        mItemSelectedListener = selectedListener;
-    }
-
-    /**
-     * Set a view to act as a user prompt for this popup window. Where the prompt view will appear
-     * is controlled by {@link #setPromptPosition(int)}.
-     *
-     * @param prompt View to use as an informational prompt.
-     */
-    public void setPromptView(@Nullable View prompt) {
-        boolean showing = isShowing();
-        if (showing) {
-            removePromptView();
-        }
-        mPromptView = prompt;
-        if (showing) {
-            show();
-        }
-    }
-
-    /**
-     * Post a {@link #show()} call to the UI thread.
-     */
-    public void postShow() {
-        mHandler.post(mShowDropDownRunnable);
-    }
-
-    /**
-     * Show the popup list. If the list is already showing, this method
-     * will recalculate the popup's size and position.
-     */
-    @Override
-    public void show() {
-        int height = buildDropDown();
-
-        final boolean noInputMethod = isInputMethodNotNeeded();
-        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
-                // value you do not want to update.
-                widthSpec = -1;
-            } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                widthSpec = getAnchorView().getWidth();
-            } else {
-                widthSpec = mDropDownWidth;
-            }
-
-            final int heightSpec;
-            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-                // The call to PopupWindow's update method below can accept -1 for any
-                // value you do not want to update.
-                heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
-                if (noInputMethod) {
-                    mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                            ViewGroup.LayoutParams.MATCH_PARENT : 0);
-                    mPopup.setHeight(0);
-                } else {
-                    mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0);
-                    mPopup.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
-                }
-            } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                heightSpec = height;
-            } else {
-                heightSpec = mDropDownHeight;
-            }
-
-            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
-
-            mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
-                            mDropDownVerticalOffset, (widthSpec < 0)? -1 : widthSpec,
-                            (heightSpec < 0)? -1 : heightSpec);
-        } else {
-            final int widthSpec;
-            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
-                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
-            } else {
-                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    widthSpec = getAnchorView().getWidth();
-                } else {
-                    widthSpec = mDropDownWidth;
-                }
-            }
-
-            final int heightSpec;
-            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-                heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
-            } else {
-                if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    heightSpec = height;
-                } else {
-                    heightSpec = mDropDownHeight;
-                }
-            }
-
-            mPopup.setWidth(widthSpec);
-            mPopup.setHeight(heightSpec);
-            setPopupClipToScreenEnabled(true);
-
-            // use outside touchable to dismiss drop down when touching outside of it, so
-            // 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);
-                } catch (Exception e) {
-                    Log.e(TAG, "Could not invoke setEpicenterBounds on PopupWindow", e);
-                }
-            }
-            PopupWindowCompat.showAsDropDown(mPopup, getAnchorView(), mDropDownHorizontalOffset,
-                    mDropDownVerticalOffset, mDropDownGravity);
-            mDropDownList.setSelection(ListView.INVALID_POSITION);
-
-            if (!mModal || mDropDownList.isInTouchMode()) {
-                clearListSelection();
-            }
-            if (!mModal) {
-                mHandler.post(mHideSelector);
-            }
-        }
-    }
-
-    /**
-     * Dismiss the popup window.
-     */
-    @Override
-    public void dismiss() {
-        mPopup.dismiss();
-        removePromptView();
-        mPopup.setContentView(null);
-        mDropDownList = null;
-        mHandler.removeCallbacks(mResizePopupRunnable);
-    }
-
-    /**
-     * Set a listener to receive a callback when the popup is dismissed.
-     *
-     * @param listener Listener that will be notified when the popup is dismissed.
-     */
-    public void setOnDismissListener(@Nullable PopupWindow.OnDismissListener listener) {
-        mPopup.setOnDismissListener(listener);
-    }
-
-    private void removePromptView() {
-        if (mPromptView != null) {
-            final ViewParent parent = mPromptView.getParent();
-            if (parent instanceof ViewGroup) {
-                final ViewGroup group = (ViewGroup) parent;
-                group.removeView(mPromptView);
-            }
-        }
-    }
-
-    /**
-     * Control how the popup operates with an input method: one of
-     * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
-     * or {@link #INPUT_METHOD_NOT_NEEDED}.
-     *
-     * <p>If the popup is showing, calling this method will take effect only
-     * the next time the popup is shown or through a manual call to the {@link #show()}
-     * method.</p>
-     *
-     * @see #getInputMethodMode()
-     * @see #show()
-     */
-    public void setInputMethodMode(int mode) {
-        mPopup.setInputMethodMode(mode);
-    }
-
-    /**
-     * Return the current value in {@link #setInputMethodMode(int)}.
-     *
-     * @see #setInputMethodMode(int)
-     */
-    public int getInputMethodMode() {
-        return mPopup.getInputMethodMode();
-    }
-
-    /**
-     * Set the selected position of the list.
-     * Only valid when {@link #isShowing()} == {@code true}.
-     *
-     * @param position List position to set as selected.
-     */
-    public void setSelection(int position) {
-        DropDownListView list = mDropDownList;
-        if (isShowing() && list != null) {
-            list.setListSelectionHidden(false);
-            list.setSelection(position);
-
-            if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
-                list.setItemChecked(position, true);
-            }
-        }
-    }
-
-    /**
-     * Clear any current list selection.
-     * Only valid when {@link #isShowing()} == {@code true}.
-     */
-    public void clearListSelection() {
-        final DropDownListView list = mDropDownList;
-        if (list != null) {
-            // WARNING: Please read the comment where mListSelectionHidden is declared
-            list.setListSelectionHidden(true);
-            //list.hideSelector();
-            list.requestLayout();
-        }
-    }
-
-    /**
-     * @return {@code true} if the popup is currently showing, {@code false} otherwise.
-     */
-    @Override
-    public boolean isShowing() {
-        return mPopup.isShowing();
-    }
-
-    /**
-     * @return {@code true} if this popup is configured to assume the user does not need
-     * to interact with the IME while it is showing, {@code false} otherwise.
-     */
-    public boolean isInputMethodNotNeeded() {
-        return mPopup.getInputMethodMode() == INPUT_METHOD_NOT_NEEDED;
-    }
-
-    /**
-     * Perform an item click operation on the specified list adapter position.
-     *
-     * @param position Adapter position for performing the click
-     * @return true if the click action could be performed, false if not.
-     *         (e.g. if the popup was not showing, this method would return false.)
-     */
-    public boolean performItemClick(int position) {
-        if (isShowing()) {
-            if (mItemClickListener != null) {
-                final DropDownListView list = mDropDownList;
-                final View child = list.getChildAt(position - list.getFirstVisiblePosition());
-                final ListAdapter adapter = list.getAdapter();
-                mItemClickListener.onItemClick(list, child, position, adapter.getItemId(position));
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * @return The currently selected item or null if the popup is not showing.
-     */
-    public @Nullable Object getSelectedItem() {
-        if (!isShowing()) {
-            return null;
-        }
-        return mDropDownList.getSelectedItem();
-    }
-
-    /**
-     * @return The position of the currently selected item or {@link ListView#INVALID_POSITION}
-     * if {@link #isShowing()} == {@code false}.
-     *
-     * @see ListView#getSelectedItemPosition()
-     */
-    public int getSelectedItemPosition() {
-        if (!isShowing()) {
-            return ListView.INVALID_POSITION;
-        }
-        return mDropDownList.getSelectedItemPosition();
-    }
-
-    /**
-     * @return The ID of the currently selected item or {@link ListView#INVALID_ROW_ID}
-     * if {@link #isShowing()} == {@code false}.
-     *
-     * @see ListView#getSelectedItemId()
-     */
-    public long getSelectedItemId() {
-        if (!isShowing()) {
-            return ListView.INVALID_ROW_ID;
-        }
-        return mDropDownList.getSelectedItemId();
-    }
-
-    /**
-     * @return The View for the currently selected item or null if
-     * {@link #isShowing()} == {@code false}.
-     *
-     * @see ListView#getSelectedView()
-     */
-    public @Nullable View getSelectedView() {
-        if (!isShowing()) {
-            return null;
-        }
-        return mDropDownList.getSelectedView();
-    }
-
-    /**
-     * @return The {@link ListView} displayed within the popup window.
-     * Only valid when {@link #isShowing()} == {@code true}.
-     */
-    @Override
-    public @Nullable ListView getListView() {
-        return mDropDownList;
-    }
-
-    @NonNull DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
-        return new DropDownListView(context, hijackFocus);
-    }
-
-    /**
-     * The maximum number of list items that can be visible and still have
-     * the list expand when touched.
-     *
-     * @param max Max number of items that can be visible and still allow the list to expand.
-     */
-    void setListItemExpandMax(int max) {
-        mListItemExpandMaximum = max;
-    }
-
-    /**
-     * Filter key down events. By forwarding key down 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 onKeyDown
-     * @param event event param passed to the host view's onKeyDown
-     * @return true if the event was handled, false if it was ignored.
-     *
-     * @see #setModal(boolean)
-     * @see #onKeyUp(int, KeyEvent)
-     */
-    public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
-        // when the drop down is shown, we drive it directly
-        if (isShowing()) {
-            // the key events are forwarded to the list in the drop down view
-            // note that ListView handles space but we don't want that to happen
-            // also if selection is not currently in the drop down, then don't
-            // let center or enter presses go there since that would cause it
-            // to select one of its items
-            if (keyCode != KeyEvent.KEYCODE_SPACE
-                    && (mDropDownList.getSelectedItemPosition() >= 0
-                    || !isConfirmKey(keyCode))) {
-                int curIndex = mDropDownList.getSelectedItemPosition();
-                boolean consumed;
-
-                final boolean below = !mPopup.isAboveAnchor();
-
-                final ListAdapter adapter = mAdapter;
-
-                boolean allEnabled;
-                int firstItem = Integer.MAX_VALUE;
-                int lastItem = Integer.MIN_VALUE;
-
-                if (adapter != null) {
-                    allEnabled = adapter.areAllItemsEnabled();
-                    firstItem = allEnabled ? 0 :
-                            mDropDownList.lookForSelectablePosition(0, true);
-                    lastItem = allEnabled ? adapter.getCount() - 1 :
-                            mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);
-                }
-
-                if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
-                        (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
-                    // When the selection is at the top, we block the key
-                    // event to prevent focus from moving.
-                    clearListSelection();
-                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-                    show();
-                    return true;
-                } else {
-                    // WARNING: Please read the comment where mListSelectionHidden
-                    //          is declared
-                    mDropDownList.setListSelectionHidden(false);
-                }
-
-                consumed = mDropDownList.onKeyDown(keyCode, event);
-                if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
-
-                if (consumed) {
-                    // If it handled the key event, then the user is
-                    // navigating in the list, so we should put it in front.
-                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-                    // Here's a little trick we need to do to make sure that
-                    // the list view is actually showing its focus indicator,
-                    // by ensuring it has focus and getting its window out
-                    // of touch mode.
-                    mDropDownList.requestFocusFromTouch();
-                    show();
-
-                    switch (keyCode) {
-                        // avoid passing the focus from the text view to the
-                        // next component
-                        case KeyEvent.KEYCODE_ENTER:
-                        case KeyEvent.KEYCODE_DPAD_CENTER:
-                        case KeyEvent.KEYCODE_DPAD_DOWN:
-                        case KeyEvent.KEYCODE_DPAD_UP:
-                            return true;
-                    }
-                } else {
-                    if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
-                        // when the selection is at the bottom, we block the
-                        // event to avoid going to the next focusable widget
-                        if (curIndex == lastItem) {
-                            return true;
-                        }
-                    } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
-                            curIndex == firstItem) {
-                        return true;
-                    }
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * 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
-     * @param event event param passed to the host view's onKeyUp
-     * @return true if the event was handled, false if it was ignored.
-     *
-     * @see #setModal(boolean)
-     * @see #onKeyDown(int, KeyEvent)
-     */
-    public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
-        if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
-            boolean consumed = mDropDownList.onKeyUp(keyCode, event);
-            if (consumed && isConfirmKey(keyCode)) {
-                // if the list accepts the key events and the key event was a click, the text view
-                // gets the selected item from the drop down as its content
-                dismiss();
-            }
-            return consumed;
-        }
-        return false;
-    }
-
-    /**
-     * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)}
-     * events to this function, views using ListPopupWindow can have it dismiss the popup
-     * when the back key is pressed.
-     *
-     * @param keyCode keyCode param passed to the host view's onKeyPreIme
-     * @param event event param passed to the host view's onKeyPreIme
-     * @return true if the event was handled, false if it was ignored.
-     *
-     * @see #setModal(boolean)
-     */
-    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
-            // special case for the back key, we do not even try to send it
-            // to the drop down list but instead, consume it immediately
-            final View anchorView = mDropDownAnchorView;
-            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
-                KeyEvent.DispatcherState state = anchorView.getKeyDispatcherState();
-                if (state != null) {
-                    state.startTracking(event, this);
-                }
-                return true;
-            } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                KeyEvent.DispatcherState state = anchorView.getKeyDispatcherState();
-                if (state != null) {
-                    state.handleUpEvent(event);
-                }
-                if (event.isTracking() && !event.isCanceled()) {
-                    dismiss();
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * 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 {@link #setAnchorView}.
-     * <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 src the view on which the resulting listener will be set
-     * @return a touch listener that controls drag-to-open behavior
-     */
-    public OnTouchListener createDragToOpenListener(View src) {
-        return new ForwardingListener(src) {
-            @Override
-            public ListPopupWindow getPopup() {
-                return ListPopupWindow.this;
-            }
-        };
-    }
-
-    /**
-     * <p>Builds the popup window's content and returns the height the popup
-     * should have. Returns -1 when the content already exists.</p>
-     *
-     * @return the content's height or -1 if content already exists
-     */
-    private int buildDropDown() {
-        ViewGroup dropDownView;
-        int otherHeights = 0;
-
-        if (mDropDownList == null) {
-            Context context = mContext;
-
-            /**
-             * This Runnable exists for the sole purpose of checking if the view layout has got
-             * completed and if so call showDropDown to display the drop down. This is used to show
-             * the drop down as soon as possible after user opens up the search dialog, without
-             * waiting for the normal UI pipeline to do its job which is slower than this method.
-             */
-            mShowDropDownRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    // View layout should be all done before displaying the drop down.
-                    View view = getAnchorView();
-                    if (view != null && view.getWindowToken() != null) {
-                        show();
-                    }
-                }
-            };
-
-            mDropDownList = createDropDownListView(context, !mModal);
-            if (mDropDownListHighlight != null) {
-                mDropDownList.setSelector(mDropDownListHighlight);
-            }
-            mDropDownList.setAdapter(mAdapter);
-            mDropDownList.setOnItemClickListener(mItemClickListener);
-            mDropDownList.setFocusable(true);
-            mDropDownList.setFocusableInTouchMode(true);
-            mDropDownList.setOnItemSelectedListener(new OnItemSelectedListener() {
-                @Override
-                public void onItemSelected(AdapterView<?> parent, View view,
-                        int position, long id) {
-
-                    if (position != -1) {
-                        DropDownListView dropDownList = mDropDownList;
-
-                        if (dropDownList != null) {
-                            dropDownList.setListSelectionHidden(false);
-                        }
-                    }
-                }
-
-                @Override
-                public void onNothingSelected(AdapterView<?> parent) {
-                }
-            });
-            mDropDownList.setOnScrollListener(mScrollListener);
-
-            if (mItemSelectedListener != null) {
-                mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
-            }
-
-            dropDownView = mDropDownList;
-
-            View hintView = mPromptView;
-            if (hintView != null) {
-                // if a hint has been specified, we accommodate more space for it and
-                // add a text view in the drop down menu, at the bottom of the list
-                LinearLayout hintContainer = new LinearLayout(context);
-                hintContainer.setOrientation(LinearLayout.VERTICAL);
-
-                LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
-                );
-
-                switch (mPromptPosition) {
-                    case POSITION_PROMPT_BELOW:
-                        hintContainer.addView(dropDownView, hintParams);
-                        hintContainer.addView(hintView);
-                        break;
-
-                    case POSITION_PROMPT_ABOVE:
-                        hintContainer.addView(hintView);
-                        hintContainer.addView(dropDownView, hintParams);
-                        break;
-
-                    default:
-                        Log.e(TAG, "Invalid hint position " + mPromptPosition);
-                        break;
-                }
-
-                // Measure the hint's height to find how much more vertical
-                // space we need to add to the drop down's height.
-                final int widthSize;
-                final int widthMode;
-                if (mDropDownWidth >= 0) {
-                    widthMode = MeasureSpec.AT_MOST;
-                    widthSize = mDropDownWidth;
-                } else {
-                    widthMode = MeasureSpec.UNSPECIFIED;
-                    widthSize = 0;
-                }
-                final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
-                final int heightSpec = MeasureSpec.UNSPECIFIED;
-                hintView.measure(widthSpec, heightSpec);
-
-                hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
-                otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
-                        + hintParams.bottomMargin;
-
-                dropDownView = hintContainer;
-            }
-
-            mPopup.setContentView(dropDownView);
-        } else {
-            dropDownView = (ViewGroup) mPopup.getContentView();
-            final View view = mPromptView;
-            if (view != null) {
-                LinearLayout.LayoutParams hintParams =
-                        (LinearLayout.LayoutParams) view.getLayoutParams();
-                otherHeights = view.getMeasuredHeight() + hintParams.topMargin
-                        + hintParams.bottomMargin;
-            }
-        }
-
-        // getMaxAvailableHeight() subtracts the padding, so we put it back
-        // to get the available height for the whole window.
-        final int padding;
-        final Drawable background = mPopup.getBackground();
-        if (background != null) {
-            background.getPadding(mTempRect);
-            padding = mTempRect.top + mTempRect.bottom;
-
-            // If we don't have an explicit vertical offset, determine one from
-            // the window background so that content will line up.
-            if (!mDropDownVerticalOffsetSet) {
-                mDropDownVerticalOffset = -mTempRect.top;
-            }
-        } else {
-            mTempRect.setEmpty();
-            padding = 0;
-        }
-
-        // Max height available on the screen for a popup.
-        final boolean ignoreBottomDecorations =
-                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
-        final int maxHeight = getMaxAvailableHeight(getAnchorView(), mDropDownVerticalOffset,
-                ignoreBottomDecorations);
-        if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-            return maxHeight + padding;
-        }
-
-        final int childWidthSpec;
-        switch (mDropDownWidth) {
-            case ViewGroup.LayoutParams.WRAP_CONTENT:
-                childWidthSpec = MeasureSpec.makeMeasureSpec(
-                        mContext.getResources().getDisplayMetrics().widthPixels
-                                - (mTempRect.left + mTempRect.right),
-                        MeasureSpec.AT_MOST);
-                break;
-            case ViewGroup.LayoutParams.MATCH_PARENT:
-                childWidthSpec = MeasureSpec.makeMeasureSpec(
-                        mContext.getResources().getDisplayMetrics().widthPixels
-                                - (mTempRect.left + mTempRect.right),
-                        MeasureSpec.EXACTLY);
-                break;
-            default:
-                childWidthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.EXACTLY);
-                break;
-        }
-
-        // Add padding only if the list has items in it, that way we don't show
-        // the popup if it is not needed.
-        final int listContent = mDropDownList.measureHeightOfChildrenCompat(childWidthSpec,
-                0, DropDownListView.NO_POSITION, maxHeight - otherHeights, -1);
-        if (listContent > 0) {
-            final int listPadding = mDropDownList.getPaddingTop()
-                    + mDropDownList.getPaddingBottom();
-            otherHeights += padding + listPadding;
-        }
-
-        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() {
-        }
-
-        @Override
-        public void onChanged() {
-            if (isShowing()) {
-                // Resize the popup to fit new content
-                show();
-            }
-        }
-
-        @Override
-        public void onInvalidated() {
-            dismiss();
-        }
-    }
-
-    private class ListSelectorHider implements Runnable {
-        ListSelectorHider() {
-        }
-
-        @Override
-        public void run() {
-            clearListSelection();
-        }
-    }
-
-    private class ResizePopupRunnable implements Runnable {
-        ResizePopupRunnable() {
-        }
-
-        @Override
-        public void run() {
-            if (mDropDownList != null && ViewCompat.isAttachedToWindow(mDropDownList)
-                    && mDropDownList.getCount() > mDropDownList.getChildCount()
-                    && mDropDownList.getChildCount() <= mListItemExpandMaximum) {
-                mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-                show();
-            }
-        }
-    }
-
-    private class PopupTouchInterceptor implements OnTouchListener {
-        PopupTouchInterceptor() {
-        }
-
-        @Override
-        public boolean onTouch(View v, MotionEvent event) {
-            final int action = event.getAction();
-            final int x = (int) event.getX();
-            final int y = (int) event.getY();
-
-            if (action == MotionEvent.ACTION_DOWN &&
-                    mPopup != null && mPopup.isShowing() &&
-                    (x >= 0 && x < mPopup.getWidth() && y >= 0 && y < mPopup.getHeight())) {
-                mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
-            } else if (action == MotionEvent.ACTION_UP) {
-                mHandler.removeCallbacks(mResizePopupRunnable);
-            }
-            return false;
-        }
-    }
-
-    private class PopupScrollListener implements ListView.OnScrollListener {
-        PopupScrollListener() {
-        }
-
-        @Override
-        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
-                int totalItemCount) {
-
-        }
-
-        @Override
-        public void onScrollStateChanged(AbsListView view, int scrollState) {
-            if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
-                    !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
-                mHandler.removeCallbacks(mResizePopupRunnable);
-                mResizePopupRunnable.run();
-            }
-        }
-    }
-
-    private static boolean isConfirmKey(int keyCode) {
-        return keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER;
-    }
-
-    private void setPopupClipToScreenEnabled(boolean clip) {
-        if (sClipToWindowEnabledMethod != null) {
-            try {
-                sClipToWindowEnabledMethod.invoke(mPopup, clip);
-            } catch (Exception e) {
-                Log.i(TAG, "Could not call setClipToScreenEnabled() on PopupWindow. Oh well.");
-            }
-        }
-    }
-
-    private int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
-        if (sGetMaxAvailableHeightMethod != null) {
-            try {
-                return (int) sGetMaxAvailableHeightMethod.invoke(mPopup, anchor, yOffset,
-                        ignoreBottomDecorations);
-            } catch (Exception e) {
-                Log.i(TAG, "Could not call getMaxAvailableHeightMethod(View, int, boolean)"
-                        + " on PopupWindow. Using the public version.");
-            }
-        }
-        return mPopup.getMaxAvailableHeight(anchor, yOffset);
-    }
-}
diff --git a/android/support/v7/widget/MenuItemHoverListener.java b/android/support/v7/widget/MenuItemHoverListener.java
deleted file mode 100644
index 70f844d..0000000
--- a/android/support/v7/widget/MenuItemHoverListener.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.v7.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v7.view.menu.MenuBuilder;
-import android.view.MenuItem;
-
-/**
- * An interface notified when a menu item is hovered. Useful for cases when hover should trigger
- * some behavior at a higher level, like managing the opening and closing of submenus.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface MenuItemHoverListener {
-    /**
-     * Called when hover exits a menu item.
-     * <p>
-     * If hover is moving to another item, this method will be called before
-     * {@link #onItemHoverEnter(MenuBuilder, MenuItem)} for the newly-hovered item.
-     *
-     * @param menu the item's parent menu
-     * @param item the hovered menu item
-     */
-    void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item);
-
-    /**
-     * Called when hover enters a menu item.
-     *
-     * @param menu the item's parent menu
-     * @param item the hovered menu item
-     */
-    void onItemHoverEnter(@NonNull MenuBuilder menu, @NonNull MenuItem item);
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/MenuPopupWindow.java b/android/support/v7/widget/MenuPopupWindow.java
deleted file mode 100644
index cc6628a..0000000
--- a/android/support/v7/widget/MenuPopupWindow.java
+++ /dev/null
@@ -1,227 +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.widget;
-
-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.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.view.menu.ListMenuItemView;
-import android.support.v7.view.menu.MenuAdapter;
-import android.support.v7.view.menu.MenuBuilder;
-import android.transition.Transition;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
-import android.widget.PopupWindow;
-
-import java.lang.reflect.Method;
-
-/**
- * A MenuPopupWindow represents the popup window for menu.
- *
- * MenuPopupWindow is mostly same as ListPopupWindow, but it has customized
- * behaviors specific to menus,
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener {
-    private static final String TAG = "MenuPopupWindow";
-
-    private static Method sSetTouchModalMethod;
-
-    static {
-        try {
-            sSetTouchModalMethod = PopupWindow.class.getDeclaredMethod(
-                    "setTouchModal", boolean.class);
-        } catch (NoSuchMethodException e) {
-            Log.i(TAG, "Could not find method setTouchModal() on PopupWindow. Oh well.");
-        }
-    }
-
-    private MenuItemHoverListener mHoverListener;
-
-    public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
-        MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
-        view.setHoverListener(this);
-        return view;
-    }
-
-    public void setEnterTransition(Object enterTransition) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            mPopup.setEnterTransition((Transition) enterTransition);
-        }
-    }
-
-    public void setExitTransition(Object exitTransition) {
-        if (Build.VERSION.SDK_INT >= 23) {
-            mPopup.setExitTransition((Transition) exitTransition);
-        }
-    }
-
-    public void setHoverListener(MenuItemHoverListener hoverListener) {
-        mHoverListener = hoverListener;
-    }
-
-    /**
-     * Set whether this window is touch modal or if outside touches will be sent to
-     * other windows behind it.
-     */
-    public void setTouchModal(final boolean touchModal) {
-        if (sSetTouchModalMethod != null) {
-            try {
-                sSetTouchModalMethod.invoke(mPopup, touchModal);
-            } catch (Exception e) {
-                Log.i(TAG, "Could not invoke setTouchModal() on PopupWindow. Oh well.");
-            }
-        }
-    }
-
-    @Override
-    public void onItemHoverEnter(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
-        // Forward up the chain
-        if (mHoverListener != null) {
-            mHoverListener.onItemHoverEnter(menu, item);
-        }
-    }
-
-    @Override
-    public void onItemHoverExit(@NonNull MenuBuilder menu, @NonNull MenuItem item) {
-        // Forward up the chain
-        if (mHoverListener != null) {
-            mHoverListener.onItemHoverExit(menu, item);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class MenuDropDownListView extends DropDownListView {
-        final int mAdvanceKey;
-        final int mRetreatKey;
-
-        private MenuItemHoverListener mHoverListener;
-        private MenuItem mHoveredMenuItem;
-
-        public MenuDropDownListView(Context context, boolean hijackFocus) {
-            super(context, hijackFocus);
-
-            final Resources res = context.getResources();
-            final Configuration config = res.getConfiguration();
-            if (Build.VERSION.SDK_INT >= 17
-                    && ViewCompat.LAYOUT_DIRECTION_RTL == config.getLayoutDirection()) {
-                mAdvanceKey = KeyEvent.KEYCODE_DPAD_LEFT;
-                mRetreatKey = KeyEvent.KEYCODE_DPAD_RIGHT;
-            } else {
-                mAdvanceKey = KeyEvent.KEYCODE_DPAD_RIGHT;
-                mRetreatKey = KeyEvent.KEYCODE_DPAD_LEFT;
-            }
-        }
-
-        public void setHoverListener(MenuItemHoverListener hoverListener) {
-            mHoverListener = hoverListener;
-        }
-
-        public void clearSelection() {
-            setSelection(INVALID_POSITION);
-        }
-
-        @Override
-        public boolean onKeyDown(int keyCode, KeyEvent event) {
-            ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
-            if (selectedItem != null && keyCode == mAdvanceKey) {
-                if (selectedItem.isEnabled() && selectedItem.getItemData().hasSubMenu()) {
-                    performItemClick(
-                            selectedItem,
-                            getSelectedItemPosition(),
-                            getSelectedItemId());
-                }
-                return true;
-            } else if (selectedItem != null && keyCode == mRetreatKey) {
-                setSelection(INVALID_POSITION);
-
-                // Close only the top-level menu.
-                ((MenuAdapter) getAdapter()).getAdapterMenu().close(false /* closeAllMenus */);
-                return true;
-            }
-            return super.onKeyDown(keyCode, event);
-        }
-
-        @Override
-        public boolean onHoverEvent(MotionEvent ev) {
-            // Dispatch any changes in hovered item index to the listener.
-            if (mHoverListener != null) {
-                // The adapter may be wrapped. Adjust the index if necessary.
-                final int headersCount;
-                final MenuAdapter menuAdapter;
-                final ListAdapter adapter = getAdapter();
-                if (adapter instanceof HeaderViewListAdapter) {
-                    final HeaderViewListAdapter headerAdapter = (HeaderViewListAdapter) adapter;
-                    headersCount = headerAdapter.getHeadersCount();
-                    menuAdapter = (MenuAdapter) headerAdapter.getWrappedAdapter();
-                } else {
-                    headersCount = 0;
-                    menuAdapter = (MenuAdapter) adapter;
-                }
-
-                // Find the menu item for the view at the event coordinates.
-                MenuItem menuItem = null;
-                if (ev.getAction() != MotionEvent.ACTION_HOVER_EXIT) {
-                    final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
-                    if (position != INVALID_POSITION) {
-                        final int itemPosition = position - headersCount;
-                        if (itemPosition >= 0 && itemPosition < menuAdapter.getCount()) {
-                            menuItem = menuAdapter.getItem(itemPosition);
-                        }
-                    }
-                }
-
-                final MenuItem oldMenuItem = mHoveredMenuItem;
-                if (oldMenuItem != menuItem) {
-                    final MenuBuilder menu = menuAdapter.getAdapterMenu();
-                    if (oldMenuItem != null) {
-                        mHoverListener.onItemHoverExit(menu, oldMenuItem);
-                    }
-
-                    mHoveredMenuItem = menuItem;
-
-                    if (menuItem != null) {
-                        mHoverListener.onItemHoverEnter(menu, menuItem);
-                    }
-                }
-            }
-
-            return super.onHoverEvent(ev);
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/OpReorderTest.java b/android/support/v7/widget/OpReorderTest.java
deleted file mode 100644
index f3cd131..0000000
--- a/android/support/v7/widget/OpReorderTest.java
+++ /dev/null
@@ -1,658 +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 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import android.support.test.filters.SmallTest;
-import android.support.v7.widget.AdapterHelper.UpdateOp;
-import android.util.Log;
-
-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.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class OpReorderTest {
-
-    private static final String TAG = "OpReorderTest";
-
-    List<UpdateOp> mUpdateOps = new ArrayList<UpdateOp>();
-    List<Item> mAddedItems = new ArrayList<Item>();
-    List<Item> mRemovedItems = new ArrayList<Item>();
-    Set<UpdateOp> mRecycledOps = new HashSet<UpdateOp>();
-    static Random random = new Random(System.nanoTime());
-
-    OpReorderer mOpReorderer = new OpReorderer(new OpReorderer.Callback() {
-        @Override
-        public UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount, Object payload) {
-            return new UpdateOp(cmd, startPosition, itemCount, payload);
-        }
-
-        @Override
-        public void recycleUpdateOp(UpdateOp op) {
-            mRecycledOps.add(op);
-        }
-    });
-
-    int itemCount = 10;
-    int updatedItemCount = 0;
-
-    public void setup(int count) {
-        itemCount = count;
-        updatedItemCount = itemCount;
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        cleanState();
-    }
-
-    void cleanState() {
-        mUpdateOps = new ArrayList<UpdateOp>();
-        mAddedItems = new ArrayList<Item>();
-        mRemovedItems = new ArrayList<Item>();
-        mRecycledOps = new HashSet<UpdateOp>();
-        Item.idCounter = 0;
-    }
-
-    @Test
-    public void testMoveRemoved() throws Exception {
-        setup(10);
-        mv(3, 8);
-        rm(7, 3);
-        process();
-    }
-
-    @Test
-    public void testMoveRemove() throws Exception {
-        setup(10);
-        mv(3, 8);
-        rm(3, 5);
-        process();
-    }
-
-    @Test
-    public void test1() {
-        setup(10);
-        mv(3, 5);
-        rm(3, 4);
-        process();
-    }
-
-    @Test
-    public void test2() {
-        setup(5);
-        mv(1, 3);
-        rm(1, 1);
-        process();
-    }
-
-    @Test
-    public void test3() {
-        setup(5);
-        mv(0, 4);
-        rm(2, 1);
-        process();
-    }
-
-    @Test
-    public void test4() {
-        setup(5);
-        mv(3, 0);
-        rm(3, 1);
-        process();
-    }
-
-    @Test
-    public void test5() {
-        setup(10);
-        mv(8, 1);
-        rm(6, 3);
-        process();
-    }
-
-    @Test
-    public void test6() {
-        setup(5);
-        mv(1, 3);
-        rm(0, 3);
-        process();
-    }
-
-    @Test
-    public void test7() {
-        setup(5);
-        mv(3, 4);
-        rm(3, 1);
-        process();
-    }
-
-    @Test
-    public void test8() {
-        setup(5);
-        mv(4, 3);
-        rm(3, 1);
-        process();
-    }
-
-    @Test
-    public void test9() {
-        setup(5);
-        mv(2, 0);
-        rm(2, 2);
-        process();
-    }
-
-    @Test
-    public void testRandom() throws Exception {
-        for (int i = 0; i < 150; i++) {
-            try {
-                cleanState();
-                setup(50);
-                for (int j = 0; j < 50; j++) {
-                    randOp(nextInt(random, nextInt(random, 4)));
-                }
-                Log.d(TAG, "running random test " + i);
-                process();
-            } catch (Throwable t) {
-                throw new Exception(t.getMessage() + "\n" + opsToString(mUpdateOps));
-            }
-        }
-    }
-
-    @Test
-    public void testRandomMoveRemove() throws Exception {
-        for (int i = 0; i < 1000; i++) {
-            try {
-                cleanState();
-                setup(5);
-                orderedRandom(MOVE, REMOVE);
-                process();
-            } catch (Throwable t) {
-                throw new Exception(t.getMessage() + "\n" + opsToString(mUpdateOps));
-            }
-        }
-    }
-
-    @Test
-    public void testRandomMoveAdd() throws Exception {
-        for (int i = 0; i < 1000; i++) {
-            try {
-                cleanState();
-                setup(5);
-                orderedRandom(MOVE, ADD);
-                process();
-            } catch (Throwable t) {
-                throw new Exception(t.getMessage() + "\n" + opsToString(mUpdateOps));
-            }
-        }
-    }
-
-    @Test
-    public void testRandomMoveUpdate() throws Exception {
-        for (int i = 0; i < 1000; i++) {
-            try {
-                cleanState();
-                setup(5);
-                orderedRandom(MOVE, UPDATE);
-                process();
-            } catch (Throwable t) {
-                throw new Exception(t.getMessage() + "\n" + opsToString(mUpdateOps));
-            }
-        }
-    }
-
-    private String opsToString(List<UpdateOp> updateOps) {
-        StringBuilder sb = new StringBuilder();
-        for (UpdateOp op : updateOps) {
-            sb.append("\n").append(op.toString());
-        }
-        return sb.append("\n").toString();
-    }
-
-    public void orderedRandom(int... ops) {
-        for (int op : ops) {
-            randOp(op);
-        }
-    }
-
-    void randOp(int cmd) {
-        switch (cmd) {
-            case REMOVE:
-                if (updatedItemCount > 1) {
-                    int s = nextInt(random, updatedItemCount - 1);
-                    int len = Math.max(1, nextInt(random, updatedItemCount - s));
-                    rm(s, len);
-                }
-                break;
-            case ADD:
-                int s = updatedItemCount == 0 ? 0 : nextInt(random, updatedItemCount);
-                add(s, nextInt(random, 50));
-                break;
-            case MOVE:
-                if (updatedItemCount >= 2) {
-                    int from = nextInt(random, updatedItemCount);
-                    int to;
-                    do {
-                        to = nextInt(random, updatedItemCount);
-                    } while (to == from);
-                    mv(from, to);
-                }
-                break;
-            case UPDATE:
-                if (updatedItemCount > 1) {
-                    s = nextInt(random, updatedItemCount - 1);
-                    int len = Math.max(1, nextInt(random, updatedItemCount - s));
-                    up(s, len);
-                }
-                break;
-        }
-    }
-
-    int nextInt(Random random, int n) {
-        if (n == 0) {
-            return 0;
-        }
-        return random.nextInt(n);
-    }
-
-    UpdateOp rm(int start, int count) {
-        updatedItemCount -= count;
-        return record(new UpdateOp(REMOVE, start, count, null));
-    }
-
-    UpdateOp mv(int from, int to) {
-        return record(new UpdateOp(MOVE, from, to, null));
-    }
-
-    UpdateOp add(int start, int count) {
-        updatedItemCount += count;
-        return record(new UpdateOp(ADD, start, count, null));
-    }
-
-    UpdateOp up(int start, int count) {
-        return record(new UpdateOp(UPDATE, start, count, null));
-    }
-
-    UpdateOp record(UpdateOp op) {
-        mUpdateOps.add(op);
-        return op;
-    }
-
-    void process() {
-        List<Item> items = new ArrayList<Item>(itemCount);
-        for (int i = 0; i < itemCount; i++) {
-            items.add(Item.create());
-        }
-        List<Item> clones = new ArrayList<Item>(itemCount);
-        for (int i = 0; i < itemCount; i++) {
-            clones.add(Item.clone(items.get(i)));
-        }
-        List<UpdateOp> rewritten = rewriteOps(mUpdateOps);
-
-        assertAllMovesAtTheEnd(rewritten);
-
-        apply(items, mUpdateOps);
-        List<Item> originalAdded = mAddedItems;
-        List<Item> originalRemoved = mRemovedItems;
-        if (originalAdded.size() > 0) {
-            Item.idCounter = originalAdded.get(0).id;
-        }
-        mAddedItems = new ArrayList<Item>();
-        mRemovedItems = new ArrayList<Item>();
-        apply(clones, rewritten);
-
-        // now check equality
-        assertListsIdentical(items, clones);
-        assertHasTheSameItems(originalAdded, mAddedItems);
-        assertHasTheSameItems(originalRemoved, mRemovedItems);
-
-        assertRecycledOpsAreNotReused(items);
-        assertRecycledOpsAreNotReused(clones);
-    }
-
-    private void assertRecycledOpsAreNotReused(List<Item> items) {
-        for (Item item : items) {
-            assertFalse(mRecycledOps.contains(item));
-        }
-    }
-
-    private void assertAllMovesAtTheEnd(List<UpdateOp> ops) {
-        boolean foundMove = false;
-        for (UpdateOp op : ops) {
-            if (op.cmd == MOVE) {
-                foundMove = true;
-            } else {
-                assertFalse(foundMove);
-            }
-        }
-    }
-
-    private void assertHasTheSameItems(List<Item> items,
-            List<Item> clones) {
-        String log = "has the same items\n" + toString(items) + "--\n" + toString(clones);
-        assertEquals(log, items.size(), clones.size());
-        for (Item item : items) {
-            for (Item clone : clones) {
-                if (item.id == clone.id && item.version == clone.version) {
-                    clones.remove(clone);
-                    break;
-                }
-            }
-        }
-        assertEquals(log, 0, clones.size());
-    }
-
-    private void assertListsIdentical(List<Item> items, List<Item> clones) {
-        String log = "is identical\n" + toString(items) + "--\n" + toString(clones);
-        assertEquals(items.size(), clones.size());
-        for (int i = 0; i < items.size(); i++) {
-            Item.assertIdentical(log, items.get(i), clones.get(i));
-        }
-    }
-
-    private void apply(List<Item> items, List<UpdateOp> updateOps) {
-        for (UpdateOp op : updateOps) {
-            switch (op.cmd) {
-                case UpdateOp.ADD:
-                    for (int i = 0; i < op.itemCount; i++) {
-                        final Item newItem = Item.create();
-                        mAddedItems.add(newItem);
-                        items.add(op.positionStart + i, newItem);
-                    }
-                    break;
-                case UpdateOp.REMOVE:
-                    for (int i = 0; i < op.itemCount; i++) {
-                        mRemovedItems.add(items.remove(op.positionStart));
-                    }
-                    break;
-                case UpdateOp.MOVE:
-                    items.add(op.itemCount, items.remove(op.positionStart));
-                    break;
-                case UpdateOp.UPDATE:
-                    for (int i = 0; i < op.itemCount; i++) {
-                        final int index = op.positionStart + i;
-                        items.get(index).version = items.get(index).version + 1;
-                    }
-                    break;
-            }
-        }
-    }
-
-    private List<UpdateOp> rewriteOps(List<UpdateOp> updateOps) {
-        List<UpdateOp> copy = new ArrayList<UpdateOp>();
-        for (UpdateOp op : updateOps) {
-            copy.add(new UpdateOp(op.cmd, op.positionStart, op.itemCount, null));
-        }
-        mOpReorderer.reorderOps(copy);
-        return copy;
-    }
-
-    @Test
-    public void testSwapMoveRemove_1() {
-        mv(10, 15);
-        rm(2, 3);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(mv(7, 12), mUpdateOps.get(1));
-        assertEquals(rm(2, 3), mUpdateOps.get(0));
-    }
-
-    @Test
-    public void testSwapMoveRemove_2() {
-        mv(3, 8);
-        rm(4, 2);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(5, 2), mUpdateOps.get(0));
-        assertEquals(mv(3, 6), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_3() {
-        mv(3, 8);
-        rm(3, 2);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(4, 2), mUpdateOps.get(0));
-        assertEquals(mv(3, 6), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_4() {
-        mv(3, 8);
-        rm(2, 3);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(3, mUpdateOps.size());
-        assertEquals(rm(4, 2), mUpdateOps.get(0));
-        assertEquals(rm(2, 1), mUpdateOps.get(1));
-        assertEquals(mv(2, 5), mUpdateOps.get(2));
-    }
-
-    @Test
-    public void testSwapMoveRemove_5() {
-        mv(3, 0);
-        rm(2, 3);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(3, mUpdateOps.size());
-        assertEquals(rm(4, 1), mUpdateOps.get(0));
-        assertEquals(rm(1, 2), mUpdateOps.get(1));
-        assertEquals(mv(1, 0), mUpdateOps.get(2));
-    }
-
-    @Test
-    public void testSwapMoveRemove_6() {
-        mv(3, 10);
-        rm(2, 3);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(3, mUpdateOps.size());
-        assertEquals(rm(4, 2), mUpdateOps.get(0));
-        assertEquals(rm(2, 1), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_7() {
-        mv(3, 2);
-        rm(6, 2);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(6, 2), mUpdateOps.get(0));
-        assertEquals(mv(3, 2), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_8() {
-        mv(3, 4);
-        rm(3, 1);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(1, mUpdateOps.size());
-        assertEquals(rm(4, 1), mUpdateOps.get(0));
-    }
-
-    @Test
-    public void testSwapMoveRemove_9() {
-        mv(3, 4);
-        rm(4, 1);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(1, mUpdateOps.size());
-        assertEquals(rm(3, 1), mUpdateOps.get(0));
-    }
-
-    @Test
-    public void testSwapMoveRemove_10() {
-        mv(1, 3);
-        rm(0, 3);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(2, 2), mUpdateOps.get(0));
-        assertEquals(rm(0, 1), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_11() {
-        mv(3, 8);
-        rm(7, 3);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(3, 1), mUpdateOps.get(0));
-        assertEquals(rm(7, 2), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_12() {
-        mv(1, 3);
-        rm(2, 1);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(3, 1), mUpdateOps.get(0));
-        assertEquals(mv(1, 2), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_13() {
-        mv(1, 3);
-        rm(1, 2);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(1, mUpdateOps.size());
-        assertEquals(rm(2, 2), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_14() {
-        mv(4, 2);
-        rm(3, 1);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(2, 1), mUpdateOps.get(0));
-        assertEquals(mv(2, 3), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveRemove_15() {
-        mv(4, 2);
-        rm(3, 2);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(1, mUpdateOps.size());
-        assertEquals(rm(2, 2), mUpdateOps.get(0));
-    }
-
-    @Test
-    public void testSwapMoveRemove_16() {
-        mv(2, 3);
-        rm(1, 2);
-        swapMoveRemove(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(rm(3, 1), mUpdateOps.get(0));
-        assertEquals(rm(1, 1), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveUpdate_0() {
-        mv(1, 3);
-        up(1, 2);
-        swapMoveUpdate(mUpdateOps, 0);
-        assertEquals(2, mUpdateOps.size());
-        assertEquals(up(2, 2), mUpdateOps.get(0));
-        assertEquals(mv(1, 3), mUpdateOps.get(1));
-    }
-
-    @Test
-    public void testSwapMoveUpdate_1() {
-        mv(0, 2);
-        up(0, 4);
-        swapMoveUpdate(mUpdateOps, 0);
-        assertEquals(3, mUpdateOps.size());
-        assertEquals(up(0, 1), mUpdateOps.get(0));
-        assertEquals(up(1, 3), mUpdateOps.get(1));
-        assertEquals(mv(0, 2), mUpdateOps.get(2));
-    }
-
-    @Test
-    public void testSwapMoveUpdate_2() {
-        mv(2, 0);
-        up(1, 3);
-        swapMoveUpdate(mUpdateOps, 0);
-        assertEquals(3, mUpdateOps.size());
-        assertEquals(up(3, 1), mUpdateOps.get(0));
-        assertEquals(up(0, 2), mUpdateOps.get(1));
-        assertEquals(mv(2, 0), mUpdateOps.get(2));
-    }
-
-    private void swapMoveUpdate(List<UpdateOp> list, int move) {
-        mOpReorderer.swapMoveUpdate(list, move, list.get(move), move + 1, list.get(move + 1));
-    }
-
-    private void swapMoveRemove(List<UpdateOp> list, int move) {
-        mOpReorderer.swapMoveRemove(list, move, list.get(move), move + 1, list.get(move + 1));
-    }
-
-    private String toString(List<Item> items) {
-        StringBuilder sb = new StringBuilder();
-        for (Item item : items) {
-            sb.append(item.toString()).append("\n");
-        }
-        return sb.toString();
-    }
-
-    static class Item {
-
-        static int idCounter = 0;
-        int id;
-        int version;
-
-        Item(int id, int version) {
-            this.id = id;
-            this.version = version;
-        }
-
-        static Item create() {
-            return new Item(idCounter++, 1);
-        }
-
-        static Item clone(Item other) {
-            return new Item(other.id, other.version);
-        }
-
-        public static void assertIdentical(String logPrefix, Item item1, Item item2) {
-            assertEquals(logPrefix + "\n" + item1 + " vs " + item2, item1.id, item2.id);
-            assertEquals(logPrefix + "\n" + item1 + " vs " + item2, item1.version, item2.version);
-        }
-
-        @Override
-        public String toString() {
-            return "Item{" +
-                    "id=" + id +
-                    ", version=" + version +
-                    '}';
-        }
-    }
-}
diff --git a/android/support/v7/widget/OpReorderer.java b/android/support/v7/widget/OpReorderer.java
deleted file mode 100644
index b288061..0000000
--- a/android/support/v7/widget/OpReorderer.java
+++ /dev/null
@@ -1,239 +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 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;
-
-    OpReorderer(Callback callback) {
-        mCallback = callback;
-    }
-
-    void reorderOps(List<UpdateOp> ops) {
-        // since move operations breaks continuity, their effects on ADD/RM are hard to handle.
-        // we push them to the end of the list so that they can be handled easily.
-        int badMove;
-        while ((badMove = getLastMoveOutOfOrder(ops)) != -1) {
-            swapMoveOp(ops, badMove, badMove + 1);
-        }
-    }
-
-    private void swapMoveOp(List<UpdateOp> list, int badMove, int next) {
-        final UpdateOp moveOp = list.get(badMove);
-        final UpdateOp nextOp = list.get(next);
-        switch (nextOp.cmd) {
-            case REMOVE:
-                swapMoveRemove(list, badMove, moveOp, next, nextOp);
-                break;
-            case ADD:
-                swapMoveAdd(list, badMove, moveOp, next, nextOp);
-                break;
-            case UPDATE:
-                swapMoveUpdate(list, badMove, moveOp, next, nextOp);
-                break;
-        }
-    }
-
-    void swapMoveRemove(List<UpdateOp> list, int movePos, UpdateOp moveOp,
-            int removePos, UpdateOp removeOp) {
-        UpdateOp extraRm = null;
-        // check if move is nulled out by remove
-        boolean revertedMove = false;
-        final boolean moveIsBackwards;
-
-        if (moveOp.positionStart < moveOp.itemCount) {
-            moveIsBackwards = false;
-            if (removeOp.positionStart == moveOp.positionStart
-                    && removeOp.itemCount == moveOp.itemCount - moveOp.positionStart) {
-                revertedMove = true;
-            }
-        } else {
-            moveIsBackwards = true;
-            if (removeOp.positionStart == moveOp.itemCount + 1
-                    && removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) {
-                revertedMove = true;
-            }
-        }
-
-        // going in reverse, first revert the effect of add
-        if (moveOp.itemCount < removeOp.positionStart) {
-            removeOp.positionStart--;
-        } else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
-            // move is removed.
-            removeOp.itemCount--;
-            moveOp.cmd = REMOVE;
-            moveOp.itemCount = 1;
-            if (removeOp.itemCount == 0) {
-                list.remove(removePos);
-                mCallback.recycleUpdateOp(removeOp);
-            }
-            // no need to swap, it is already a remove
-            return;
-        }
-
-        // now affect of add is consumed. now apply effect of first remove
-        if (moveOp.positionStart <= removeOp.positionStart) {
-            removeOp.positionStart++;
-        } else if (moveOp.positionStart < removeOp.positionStart + removeOp.itemCount) {
-            final int remaining = removeOp.positionStart + removeOp.itemCount
-                    - moveOp.positionStart;
-            extraRm = mCallback.obtainUpdateOp(REMOVE, moveOp.positionStart + 1, remaining, null);
-            removeOp.itemCount = moveOp.positionStart - removeOp.positionStart;
-        }
-
-        // if effects of move is reverted by remove, we are done.
-        if (revertedMove) {
-            list.set(movePos, removeOp);
-            list.remove(removePos);
-            mCallback.recycleUpdateOp(moveOp);
-            return;
-        }
-
-        // now find out the new locations for move actions
-        if (moveIsBackwards) {
-            if (extraRm != null) {
-                if (moveOp.positionStart > extraRm.positionStart) {
-                    moveOp.positionStart -= extraRm.itemCount;
-                }
-                if (moveOp.itemCount > extraRm.positionStart) {
-                    moveOp.itemCount -= extraRm.itemCount;
-                }
-            }
-            if (moveOp.positionStart > removeOp.positionStart) {
-                moveOp.positionStart -= removeOp.itemCount;
-            }
-            if (moveOp.itemCount > removeOp.positionStart) {
-                moveOp.itemCount -= removeOp.itemCount;
-            }
-        } else {
-            if (extraRm != null) {
-                if (moveOp.positionStart >= extraRm.positionStart) {
-                    moveOp.positionStart -= extraRm.itemCount;
-                }
-                if (moveOp.itemCount >= extraRm.positionStart) {
-                    moveOp.itemCount -= extraRm.itemCount;
-                }
-            }
-            if (moveOp.positionStart >= removeOp.positionStart) {
-                moveOp.positionStart -= removeOp.itemCount;
-            }
-            if (moveOp.itemCount >= removeOp.positionStart) {
-                moveOp.itemCount -= removeOp.itemCount;
-            }
-        }
-
-        list.set(movePos, removeOp);
-        if (moveOp.positionStart != moveOp.itemCount) {
-            list.set(removePos, moveOp);
-        } else {
-            list.remove(removePos);
-        }
-        if (extraRm != null) {
-            list.add(movePos, extraRm);
-        }
-    }
-
-    private void swapMoveAdd(List<UpdateOp> list, int move, UpdateOp moveOp, int add,
-            UpdateOp addOp) {
-        int offset = 0;
-        // going in reverse, first revert the effect of add
-        if (moveOp.itemCount < addOp.positionStart) {
-            offset--;
-        }
-        if (moveOp.positionStart < addOp.positionStart) {
-            offset++;
-        }
-        if (addOp.positionStart <= moveOp.positionStart) {
-            moveOp.positionStart += addOp.itemCount;
-        }
-        if (addOp.positionStart <= moveOp.itemCount) {
-            moveOp.itemCount += addOp.itemCount;
-        }
-        addOp.positionStart += offset;
-        list.set(move, addOp);
-        list.set(add, moveOp);
-    }
-
-    void swapMoveUpdate(List<UpdateOp> list, int move, UpdateOp moveOp, int update,
-            UpdateOp updateOp) {
-        UpdateOp extraUp1 = null;
-        UpdateOp extraUp2 = null;
-        // going in reverse, first revert the effect of add
-        if (moveOp.itemCount < updateOp.positionStart) {
-            updateOp.positionStart--;
-        } else if (moveOp.itemCount < updateOp.positionStart + updateOp.itemCount) {
-            // moved item is updated. add an update for it
-            updateOp.itemCount--;
-            extraUp1 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart, 1, updateOp.payload);
-        }
-        // now affect of add is consumed. now apply effect of first remove
-        if (moveOp.positionStart <= updateOp.positionStart) {
-            updateOp.positionStart++;
-        } else if (moveOp.positionStart < updateOp.positionStart + updateOp.itemCount) {
-            final int remaining = updateOp.positionStart + updateOp.itemCount
-                    - moveOp.positionStart;
-            extraUp2 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart + 1, remaining,
-                    updateOp.payload);
-            updateOp.itemCount -= remaining;
-        }
-        list.set(update, moveOp);
-        if (updateOp.itemCount > 0) {
-            list.set(move, updateOp);
-        } else {
-            list.remove(move);
-            mCallback.recycleUpdateOp(updateOp);
-        }
-        if (extraUp1 != null) {
-            list.add(move, extraUp1);
-        }
-        if (extraUp2 != null) {
-            list.add(move, extraUp2);
-        }
-    }
-
-    private int getLastMoveOutOfOrder(List<UpdateOp> list) {
-        boolean foundNonMove = false;
-        for (int i = list.size() - 1; i >= 0; i--) {
-            final UpdateOp op1 = list.get(i);
-            if (op1.cmd == MOVE) {
-                if (foundNonMove) {
-                    return i;
-                }
-            } else {
-                foundNonMove = true;
-            }
-        }
-        return -1;
-    }
-
-    interface Callback {
-
-        UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount, Object payload);
-
-        void recycleUpdateOp(UpdateOp op);
-    }
-}
diff --git a/android/support/v7/widget/OrientationHelper.java b/android/support/v7/widget/OrientationHelper.java
deleted file mode 100644
index 99bcbaa..0000000
--- a/android/support/v7/widget/OrientationHelper.java
+++ /dev/null
@@ -1,446 +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.graphics.Rect;
-import android.view.View;
-
-/**
- * Helper class for LayoutManagers to abstract measurements depending on the View's orientation.
- * <p>
- * It is developed to easily support vertical and horizontal orientations in a LayoutManager but
- * can also be used to abstract calls around view bounds and child measurements with margins and
- * decorations.
- *
- * @see #createHorizontalHelper(RecyclerView.LayoutManager)
- * @see #createVerticalHelper(RecyclerView.LayoutManager)
- */
-public abstract class OrientationHelper {
-
-    private static final int INVALID_SIZE = Integer.MIN_VALUE;
-
-    protected final RecyclerView.LayoutManager mLayoutManager;
-
-    public static final int HORIZONTAL = RecyclerView.HORIZONTAL;
-
-    public static final int VERTICAL = RecyclerView.VERTICAL;
-
-    private int mLastTotalSpace = INVALID_SIZE;
-
-    final Rect mTmpRect = new Rect();
-
-    private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
-        mLayoutManager = layoutManager;
-    }
-
-    /**
-     * Returns the {@link android.support.v7.widget.RecyclerView.LayoutManager LayoutManager} that
-     * is associated with this OrientationHelper.
-     */
-    public RecyclerView.LayoutManager getLayoutManager() {
-        return mLayoutManager;
-    }
-
-    /**
-     * Call this method after onLayout method is complete if state is NOT pre-layout.
-     * This method records information like layout bounds that might be useful in the next layout
-     * calculations.
-     */
-    public void onLayoutComplete() {
-        mLastTotalSpace = getTotalSpace();
-    }
-
-    /**
-     * Returns the layout space change between the previous layout pass and current layout pass.
-     * <p>
-     * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
-     * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
-     * RecyclerView.State)} method.
-     *
-     * @return The difference between the current total space and previous layout's total space.
-     * @see #onLayoutComplete()
-     */
-    public int getTotalSpaceChange() {
-        return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
-    }
-
-    /**
-     * Returns the start of the view including its decoration and margin.
-     * <p>
-     * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
-     * decoration and 3px left margin, returned value will be 15px.
-     *
-     * @param view The view element to check
-     * @return The first pixel of the element
-     * @see #getDecoratedEnd(android.view.View)
-     */
-    public abstract int getDecoratedStart(View view);
-
-    /**
-     * Returns the end of the view including its decoration and margin.
-     * <p>
-     * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
-     * decoration and 3px right margin, returned value will be 205.
-     *
-     * @param view The view element to check
-     * @return The last pixel of the element
-     * @see #getDecoratedStart(android.view.View)
-     */
-    public abstract int getDecoratedEnd(View view);
-
-    /**
-     * Returns the end of the View after its matrix transformations are applied to its layout
-     * position.
-     * <p>
-     * This method is useful when trying to detect the visible edge of a View.
-     * <p>
-     * It includes the decorations but does not include the margins.
-     *
-     * @param view The view whose transformed end will be returned
-     * @return The end of the View after its decor insets and transformation matrix is applied to
-     * its position
-     *
-     * @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
-     */
-    public abstract int getTransformedEndWithDecoration(View view);
-
-    /**
-     * Returns the start of the View after its matrix transformations are applied to its layout
-     * position.
-     * <p>
-     * This method is useful when trying to detect the visible edge of a View.
-     * <p>
-     * It includes the decorations but does not include the margins.
-     *
-     * @param view The view whose transformed start will be returned
-     * @return The start of the View after its decor insets and transformation matrix is applied to
-     * its position
-     *
-     * @see RecyclerView.LayoutManager#getTransformedBoundingBox(View, boolean, Rect)
-     */
-    public abstract int getTransformedStartWithDecoration(View view);
-
-    /**
-     * Returns the space occupied by this View in the current orientation including decorations and
-     * margins.
-     *
-     * @param view The view element to check
-     * @return Total space occupied by this view
-     * @see #getDecoratedMeasurementInOther(View)
-     */
-    public abstract int getDecoratedMeasurement(View view);
-
-    /**
-     * Returns the space occupied by this View in the perpendicular orientation including
-     * decorations and margins.
-     *
-     * @param view The view element to check
-     * @return Total space occupied by this view in the perpendicular orientation to current one
-     * @see #getDecoratedMeasurement(View)
-     */
-    public abstract int getDecoratedMeasurementInOther(View view);
-
-    /**
-     * Returns the start position of the layout after the start padding is added.
-     *
-     * @return The very first pixel we can draw.
-     */
-    public abstract int getStartAfterPadding();
-
-    /**
-     * Returns the end position of the layout after the end padding is removed.
-     *
-     * @return The end boundary for this layout.
-     */
-    public abstract int getEndAfterPadding();
-
-    /**
-     * Returns the end position of the layout without taking padding into account.
-     *
-     * @return The end boundary for this layout without considering padding.
-     */
-    public abstract int getEnd();
-
-    /**
-     * Offsets all children's positions by the given amount.
-     *
-     * @param amount Value to add to each child's layout parameters
-     */
-    public abstract void offsetChildren(int amount);
-
-    /**
-     * Returns the total space to layout. This number is the difference between
-     * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
-     *
-     * @return Total space to layout children
-     */
-    public abstract int getTotalSpace();
-
-    /**
-     * Offsets the child in this orientation.
-     *
-     * @param view   View to offset
-     * @param offset offset amount
-     */
-    public abstract void offsetChild(View view, int offset);
-
-    /**
-     * Returns the padding at the end of the layout. For horizontal helper, this is the right
-     * padding and for vertical helper, this is the bottom padding. This method does not check
-     * whether the layout is RTL or not.
-     *
-     * @return The padding at the end of the layout.
-     */
-    public abstract int getEndPadding();
-
-    /**
-     * Returns the MeasureSpec mode for the current orientation from the LayoutManager.
-     *
-     * @return The current measure spec mode.
-     *
-     * @see View.MeasureSpec
-     * @see RecyclerView.LayoutManager#getWidthMode()
-     * @see RecyclerView.LayoutManager#getHeightMode()
-     */
-    public abstract int getMode();
-
-    /**
-     * Returns the MeasureSpec mode for the perpendicular orientation from the LayoutManager.
-     *
-     * @return The current measure spec mode.
-     *
-     * @see View.MeasureSpec
-     * @see RecyclerView.LayoutManager#getWidthMode()
-     * @see RecyclerView.LayoutManager#getHeightMode()
-     */
-    public abstract int getModeInOther();
-
-    /**
-     * Creates an OrientationHelper for the given LayoutManager and orientation.
-     *
-     * @param layoutManager LayoutManager to attach to
-     * @param orientation   Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
-     * @return A new OrientationHelper
-     */
-    public static OrientationHelper createOrientationHelper(
-            RecyclerView.LayoutManager layoutManager, @RecyclerView.Orientation int orientation) {
-        switch (orientation) {
-            case HORIZONTAL:
-                return createHorizontalHelper(layoutManager);
-            case VERTICAL:
-                return createVerticalHelper(layoutManager);
-        }
-        throw new IllegalArgumentException("invalid orientation");
-    }
-
-    /**
-     * Creates a horizontal OrientationHelper for the given LayoutManager.
-     *
-     * @param layoutManager The LayoutManager to attach to.
-     * @return A new OrientationHelper
-     */
-    public static OrientationHelper createHorizontalHelper(
-            RecyclerView.LayoutManager layoutManager) {
-        return new OrientationHelper(layoutManager) {
-            @Override
-            public int getEndAfterPadding() {
-                return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
-            }
-
-            @Override
-            public int getEnd() {
-                return mLayoutManager.getWidth();
-            }
-
-            @Override
-            public void offsetChildren(int amount) {
-                mLayoutManager.offsetChildrenHorizontal(amount);
-            }
-
-            @Override
-            public int getStartAfterPadding() {
-                return mLayoutManager.getPaddingLeft();
-            }
-
-            @Override
-            public int getDecoratedMeasurement(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
-                        + params.rightMargin;
-            }
-
-            @Override
-            public int getDecoratedMeasurementInOther(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
-                        + params.bottomMargin;
-            }
-
-            @Override
-            public int getDecoratedEnd(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
-            }
-
-            @Override
-            public int getDecoratedStart(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
-            }
-
-            @Override
-            public int getTransformedEndWithDecoration(View view) {
-                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
-                return mTmpRect.right;
-            }
-
-            @Override
-            public int getTransformedStartWithDecoration(View view) {
-                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
-                return mTmpRect.left;
-            }
-
-            @Override
-            public int getTotalSpace() {
-                return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
-                        - mLayoutManager.getPaddingRight();
-            }
-
-            @Override
-            public void offsetChild(View view, int offset) {
-                view.offsetLeftAndRight(offset);
-            }
-
-            @Override
-            public int getEndPadding() {
-                return mLayoutManager.getPaddingRight();
-            }
-
-            @Override
-            public int getMode() {
-                return mLayoutManager.getWidthMode();
-            }
-
-            @Override
-            public int getModeInOther() {
-                return mLayoutManager.getHeightMode();
-            }
-        };
-    }
-
-    /**
-     * Creates a vertical OrientationHelper for the given LayoutManager.
-     *
-     * @param layoutManager The LayoutManager to attach to.
-     * @return A new OrientationHelper
-     */
-    public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
-        return new OrientationHelper(layoutManager) {
-            @Override
-            public int getEndAfterPadding() {
-                return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
-            }
-
-            @Override
-            public int getEnd() {
-                return mLayoutManager.getHeight();
-            }
-
-            @Override
-            public void offsetChildren(int amount) {
-                mLayoutManager.offsetChildrenVertical(amount);
-            }
-
-            @Override
-            public int getStartAfterPadding() {
-                return mLayoutManager.getPaddingTop();
-            }
-
-            @Override
-            public int getDecoratedMeasurement(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
-                        + params.bottomMargin;
-            }
-
-            @Override
-            public int getDecoratedMeasurementInOther(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
-                        + params.rightMargin;
-            }
-
-            @Override
-            public int getDecoratedEnd(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
-            }
-
-            @Override
-            public int getDecoratedStart(View view) {
-                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                        view.getLayoutParams();
-                return mLayoutManager.getDecoratedTop(view) - params.topMargin;
-            }
-
-            @Override
-            public int getTransformedEndWithDecoration(View view) {
-                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
-                return mTmpRect.bottom;
-            }
-
-            @Override
-            public int getTransformedStartWithDecoration(View view) {
-                mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
-                return mTmpRect.top;
-            }
-
-            @Override
-            public int getTotalSpace() {
-                return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
-                        - mLayoutManager.getPaddingBottom();
-            }
-
-            @Override
-            public void offsetChild(View view, int offset) {
-                view.offsetTopAndBottom(offset);
-            }
-
-            @Override
-            public int getEndPadding() {
-                return mLayoutManager.getPaddingBottom();
-            }
-
-            @Override
-            public int getMode() {
-                return mLayoutManager.getHeightMode();
-            }
-
-            @Override
-            public int getModeInOther() {
-                return mLayoutManager.getWidthMode();
-            }
-        };
-    }
-}
diff --git a/android/support/v7/widget/PagerSnapHelper.java b/android/support/v7/widget/PagerSnapHelper.java
deleted file mode 100644
index 953e091..0000000
--- a/android/support/v7/widget/PagerSnapHelper.java
+++ /dev/null
@@ -1,254 +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.widget;
-
-import android.graphics.PointF;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.DisplayMetrics;
-import android.view.View;
-
-/**
- * Implementation of the {@link SnapHelper} supporting pager style snapping in either vertical or
- * horizontal orientation.
- *
- * <p>
- *
- * PagerSnapHelper can help achieve a similar behavior to {@link android.support.v4.view.ViewPager}.
- * Set both {@link RecyclerView} and the items of the
- * {@link android.support.v7.widget.RecyclerView.Adapter} to have
- * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} height and width and then attach
- * PagerSnapHelper to the {@link RecyclerView} using {@link #attachToRecyclerView(RecyclerView)}.
- */
-public class PagerSnapHelper extends SnapHelper {
-    private static final int MAX_SCROLL_ON_FLING_DURATION = 100; // ms
-
-    // Orientation helpers are lazily created per LayoutManager.
-    @Nullable
-    private OrientationHelper mVerticalHelper;
-    @Nullable
-    private OrientationHelper mHorizontalHelper;
-
-    @Nullable
-    @Override
-    public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,
-            @NonNull View targetView) {
-        int[] out = new int[2];
-        if (layoutManager.canScrollHorizontally()) {
-            out[0] = distanceToCenter(layoutManager, targetView,
-                    getHorizontalHelper(layoutManager));
-        } else {
-            out[0] = 0;
-        }
-
-        if (layoutManager.canScrollVertically()) {
-            out[1] = distanceToCenter(layoutManager, targetView,
-                    getVerticalHelper(layoutManager));
-        } else {
-            out[1] = 0;
-        }
-        return out;
-    }
-
-    @Nullable
-    @Override
-    public View findSnapView(RecyclerView.LayoutManager layoutManager) {
-        if (layoutManager.canScrollVertically()) {
-            return findCenterView(layoutManager, getVerticalHelper(layoutManager));
-        } else if (layoutManager.canScrollHorizontally()) {
-            return findCenterView(layoutManager, getHorizontalHelper(layoutManager));
-        }
-        return null;
-    }
-
-    @Override
-    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
-            int velocityY) {
-        final int itemCount = layoutManager.getItemCount();
-        if (itemCount == 0) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        View mStartMostChildView = null;
-        if (layoutManager.canScrollVertically()) {
-            mStartMostChildView = findStartView(layoutManager, getVerticalHelper(layoutManager));
-        } else if (layoutManager.canScrollHorizontally()) {
-            mStartMostChildView = findStartView(layoutManager, getHorizontalHelper(layoutManager));
-        }
-
-        if (mStartMostChildView == null) {
-            return RecyclerView.NO_POSITION;
-        }
-        final int centerPosition = layoutManager.getPosition(mStartMostChildView);
-        if (centerPosition == RecyclerView.NO_POSITION) {
-            return RecyclerView.NO_POSITION;
-        }
-
-        final boolean forwardDirection;
-        if (layoutManager.canScrollHorizontally()) {
-            forwardDirection = velocityX > 0;
-        } else {
-            forwardDirection = velocityY > 0;
-        }
-        boolean reverseLayout = false;
-        if ((layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
-            RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider =
-                    (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager;
-            PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1);
-            if (vectorForEnd != null) {
-                reverseLayout = vectorForEnd.x < 0 || vectorForEnd.y < 0;
-            }
-        }
-        return reverseLayout
-                ? (forwardDirection ? centerPosition - 1 : centerPosition)
-                : (forwardDirection ? centerPosition + 1 : centerPosition);
-    }
-
-    @Override
-    protected LinearSmoothScroller createSnapScroller(RecyclerView.LayoutManager layoutManager) {
-        if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
-            return null;
-        }
-        return new LinearSmoothScroller(mRecyclerView.getContext()) {
-            @Override
-            protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
-                int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
-                        targetView);
-                final int dx = snapDistances[0];
-                final int dy = snapDistances[1];
-                final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
-                if (time > 0) {
-                    action.update(dx, dy, time, mDecelerateInterpolator);
-                }
-            }
-
-            @Override
-            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
-                return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
-            }
-
-            @Override
-            protected int calculateTimeForScrolling(int dx) {
-                return Math.min(MAX_SCROLL_ON_FLING_DURATION, super.calculateTimeForScrolling(dx));
-            }
-        };
-    }
-
-    private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager,
-            @NonNull View targetView, OrientationHelper helper) {
-        final int childCenter = helper.getDecoratedStart(targetView)
-                + (helper.getDecoratedMeasurement(targetView) / 2);
-        final int containerCenter;
-        if (layoutManager.getClipToPadding()) {
-            containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
-        } else {
-            containerCenter = helper.getEnd() / 2;
-        }
-        return childCenter - containerCenter;
-    }
-
-    /**
-     * Return the child view that is currently closest to the center of this parent.
-     *
-     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}.
-     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
-     *
-     * @return the child view that is currently closest to the center of this parent.
-     */
-    @Nullable
-    private View findCenterView(RecyclerView.LayoutManager layoutManager,
-            OrientationHelper helper) {
-        int childCount = layoutManager.getChildCount();
-        if (childCount == 0) {
-            return null;
-        }
-
-        View closestChild = null;
-        final int center;
-        if (layoutManager.getClipToPadding()) {
-            center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
-        } else {
-            center = helper.getEnd() / 2;
-        }
-        int absClosest = Integer.MAX_VALUE;
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = layoutManager.getChildAt(i);
-            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  **/
-            if (absDistance < absClosest) {
-                absClosest = absDistance;
-                closestChild = child;
-            }
-        }
-        return closestChild;
-    }
-
-    /**
-     * Return the child view that is currently closest to the start of this parent.
-     *
-     * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}.
-     * @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
-     *
-     * @return the child view that is currently closest to the start of this parent.
-     */
-    @Nullable
-    private View findStartView(RecyclerView.LayoutManager layoutManager,
-            OrientationHelper helper) {
-        int childCount = layoutManager.getChildCount();
-        if (childCount == 0) {
-            return null;
-        }
-
-        View closestChild = null;
-        int startest = Integer.MAX_VALUE;
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = layoutManager.getChildAt(i);
-            int childStart = helper.getDecoratedStart(child);
-
-            /** if child is more to start than previous closest, set it as closest  **/
-            if (childStart < startest) {
-                startest = childStart;
-                closestChild = child;
-            }
-        }
-        return closestChild;
-    }
-
-    @NonNull
-    private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
-        if (mVerticalHelper == null || mVerticalHelper.mLayoutManager != layoutManager) {
-            mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
-        }
-        return mVerticalHelper;
-    }
-
-    @NonNull
-    private OrientationHelper getHorizontalHelper(
-            @NonNull RecyclerView.LayoutManager layoutManager) {
-        if (mHorizontalHelper == null || mHorizontalHelper.mLayoutManager != layoutManager) {
-            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
-        }
-        return mHorizontalHelper;
-    }
-}
diff --git a/android/support/v7/widget/PopupMenu.java b/android/support/v7/widget/PopupMenu.java
deleted file mode 100644
index 8377010..0000000
--- a/android/support/v7/widget/PopupMenu.java
+++ /dev/null
@@ -1,291 +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.content.Context;
-import android.support.annotation.AttrRes;
-import android.support.annotation.MenuRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StyleRes;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.SupportMenuInflater;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPopupHelper;
-import android.support.v7.view.menu.ShowableListMenu;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.PopupWindow;
-
-/**
- * Static library support version of the framework's {@link android.widget.PopupMenu}.
- * Used to write apps that run on platforms prior to Android 3.0.  When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation. See the framework SDK
- * documentation for a class overview.
- */
-public class PopupMenu {
-    private final Context mContext;
-    private final MenuBuilder mMenu;
-    private final View mAnchor;
-    final MenuPopupHelper mPopup;
-
-    OnMenuItemClickListener mMenuItemClickListener;
-    OnDismissListener mOnDismissListener;
-    private View.OnTouchListener mDragListener;
-
-    /**
-     * Constructor to create a new popup menu with an anchor view.
-         *
-     * @param context Context the popup menu is running in, through which it
-     *        can access the current theme, resources, etc.
-     * @param anchor Anchor view for this popup. The popup will appear below
-     *        the anchor if there is room, or above it if there is not.
-     */
-    public PopupMenu(@NonNull Context context, @NonNull View anchor) {
-        this(context, anchor, Gravity.NO_GRAVITY);
-    }
-
-    /**
-     * Constructor to create a new popup menu with an anchor view and alignment
-     * gravity.
-     *
-     * @param context Context the popup menu is running in, through which it
-     *        can access the current theme, resources, etc.
-     * @param anchor Anchor view for this popup. The popup will appear below
-     *        the anchor if there is room, or above it if there is not.
-     * @param gravity The {@link Gravity} value for aligning the popup with its
-     *        anchor.
-     */
-    public PopupMenu(@NonNull Context context, @NonNull View anchor, int gravity) {
-        this(context, anchor, gravity, R.attr.popupMenuStyle, 0);
-    }
-
-    /**
-     * Constructor a create a new popup menu with a specific style.
-     *
-     * @param context Context the popup menu is running in, through which it
-     *        can access the current theme, resources, etc.
-     * @param anchor Anchor view for this popup. The popup will appear below
-     *        the anchor if there is room, or above it if there is not.
-     * @param gravity The {@link Gravity} value for aligning the popup with its
-     *        anchor.
-     * @param popupStyleAttr An attribute in the current theme that contains a
-     *        reference to a style resource that supplies default values for
-     *        the popup window. Can be 0 to not look for defaults.
-     * @param popupStyleRes A resource identifier of a style resource that
-     *        supplies default values for the popup window, used only if
-     *        popupStyleAttr is 0 or can not be found in the theme. Can be 0
-     *        to not look for defaults.
-     */
-    public PopupMenu(@NonNull Context context, @NonNull View anchor, int gravity,
-            @AttrRes int popupStyleAttr, @StyleRes int popupStyleRes) {
-        mContext = context;
-        mAnchor = anchor;
-
-        mMenu = new MenuBuilder(context);
-        mMenu.setCallback(new MenuBuilder.Callback() {
-            @Override
-            public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
-                if (mMenuItemClickListener != null) {
-                    return mMenuItemClickListener.onMenuItemClick(item);
-                }
-                return false;
-            }
-
-            @Override
-            public void onMenuModeChange(MenuBuilder menu) {
-            }
-        });
-
-        mPopup = new MenuPopupHelper(context, mMenu, anchor, false, popupStyleAttr, popupStyleRes);
-        mPopup.setGravity(gravity);
-        mPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
-            @Override
-            public void onDismiss() {
-                if (mOnDismissListener != null) {
-                    mOnDismissListener.onDismiss(PopupMenu.this);
-                }
-            }
-        });
-    }
-
-    /**
-     * Sets the gravity used to align the popup window to its anchor view.
-     * <p>
-     * If the popup is showing, calling this method will take effect only
-     * the next time the popup is shown.
-     *
-     * @param gravity the gravity used to align the popup window
-     * @see #getGravity()
-     */
-    public void setGravity(int gravity) {
-        mPopup.setGravity(gravity);
-    }
-
-    /**
-     * @return the gravity used to align the popup window to its anchor view
-     * @see #setGravity(int)
-     */
-    public int getGravity() {
-        return mPopup.getGravity();
-    }
-
-    /**
-     * Returns an {@link View.OnTouchListener} that can be added to the anchor view
-     * to implement drag-to-open behavior.
-     * <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>
-     * PopupMenu myPopup = new PopupMenu(context, myAnchor);
-     * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener());
-     * </pre>
-     *
-     * @return a touch listener that controls drag-to-open behavior
-     */
-    @NonNull
-    public View.OnTouchListener getDragToOpenListener() {
-        if (mDragListener == null) {
-            mDragListener = new ForwardingListener(mAnchor) {
-                @Override
-                protected boolean onForwardingStarted() {
-                    show();
-                    return true;
-                }
-
-                @Override
-                protected boolean onForwardingStopped() {
-                    dismiss();
-                    return true;
-                }
-
-                @Override
-                public ShowableListMenu getPopup() {
-                    // This will be null until show() is called.
-                    return mPopup.getPopup();
-                }
-            };
-        }
-
-        return mDragListener;
-    }
-
-    /**
-     * Returns the {@link Menu} associated with this popup. Populate the
-     * returned Menu with items before calling {@link #show()}.
-     *
-     * @return the {@link Menu} associated with this popup
-     * @see #show()
-     * @see #getMenuInflater()
-     */
-    @NonNull
-    public Menu getMenu() {
-        return mMenu;
-    }
-
-    /**
-     * @return a {@link MenuInflater} that can be used to inflate menu items
-     *         from XML into the menu returned by {@link #getMenu()}
-     * @see #getMenu()
-     */
-    @NonNull
-    public MenuInflater getMenuInflater() {
-        return new SupportMenuInflater(mContext);
-    }
-
-    /**
-     * Inflate a menu resource into this PopupMenu. This is equivalent to
-     * calling {@code popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu())}.
-     *
-     * @param menuRes Menu resource to inflate
-     */
-    public void inflate(@MenuRes int menuRes) {
-        getMenuInflater().inflate(menuRes, mMenu);
-    }
-
-    /**
-     * Show the menu popup anchored to the view specified during construction.
-     *
-     * @see #dismiss()
-     */
-    public void show() {
-        mPopup.show();
-    }
-
-    /**
-     * Dismiss the menu popup.
-     *
-     * @see #show()
-     */
-    public void dismiss() {
-        mPopup.dismiss();
-    }
-
-    /**
-     * Sets a listener that will be notified when the user selects an item from
-     * the menu.
-     *
-     * @param listener the listener to notify
-     */
-    public void setOnMenuItemClickListener(@Nullable OnMenuItemClickListener listener) {
-        mMenuItemClickListener = listener;
-    }
-
-    /**
-     * Sets a listener that will be notified when this menu is dismissed.
-     *
-     * @param listener the listener to notify
-     */
-    public void setOnDismissListener(@Nullable OnDismissListener listener) {
-        mOnDismissListener = listener;
-    }
-
-    /**
-     * Interface responsible for receiving menu item click events if the items
-     * themselves do not have individual item click listeners.
-     */
-    public interface OnMenuItemClickListener {
-        /**
-         * This method will be invoked when a menu item is clicked if the item
-         * itself did not already handle the event.
-         *
-         * @param item the menu item that was clicked
-         * @return {@code true} if the event was handled, {@code false}
-         * otherwise
-         */
-        boolean onMenuItemClick(MenuItem item);
-    }
-
-    /**
-     * Callback interface used to notify the application that the menu has closed.
-     */
-    public interface OnDismissListener {
-        /**
-         * Called when the associated menu has been dismissed.
-         *
-         * @param menu the popup menu that was dismissed
-         */
-        void onDismiss(PopupMenu menu);
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/PositionMap.java b/android/support/v7/widget/PositionMap.java
deleted file mode 100644
index 8225da4..0000000
--- a/android/support/v7/widget/PositionMap.java
+++ /dev/null
@@ -1,464 +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.v7.widget;
-
-import java.util.ArrayList;
-
-/**
- * Like a SparseArray, but with the ability to offset key ranges for bulk insertions/deletions.
- */
-class PositionMap<E> implements Cloneable {
-    private static final Object DELETED = new Object();
-    private boolean mGarbage = false;
-
-    private int[] mKeys;
-    private Object[] mValues;
-    private int mSize;
-
-    /**
-     * Creates a new SparseArray containing no mappings.
-     */
-    PositionMap() {
-        this(10);
-    }
-
-    /**
-     * Creates a new PositionMap containing no mappings that will not
-     * require any additional memory allocation to store the specified
-     * number of mappings.  If you supply an initial capacity of 0, the
-     * sparse array will be initialized with a light-weight representation
-     * not requiring any additional array allocations.
-     */
-    PositionMap(int initialCapacity) {
-        if (initialCapacity == 0) {
-            mKeys = ContainerHelpers.EMPTY_INTS;
-            mValues = ContainerHelpers.EMPTY_OBJECTS;
-        } else {
-            initialCapacity = idealIntArraySize(initialCapacity);
-            mKeys = new int[initialCapacity];
-            mValues = new Object[initialCapacity];
-        }
-        mSize = 0;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public PositionMap<E> clone() {
-        PositionMap<E> clone = null;
-        try {
-            clone = (PositionMap<E>) super.clone();
-            clone.mKeys = mKeys.clone();
-            clone.mValues = mValues.clone();
-        } catch (CloneNotSupportedException cnse) {
-            /* ignore */
-        }
-        return clone;
-    }
-
-    /**
-     * Gets the Object mapped from the specified key, or <code>null</code>
-     * if no such mapping has been made.
-     */
-    public E get(int key) {
-        return get(key, null);
-    }
-
-    /**
-     * Gets the Object mapped from the specified key, or the specified Object
-     * if no such mapping has been made.
-     */
-    @SuppressWarnings("unchecked")
-    public E get(int key, E valueIfKeyNotFound) {
-        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i < 0 || mValues[i] == DELETED) {
-            return valueIfKeyNotFound;
-        } else {
-            return (E) mValues[i];
-        }
-    }
-
-    /**
-     * Removes the mapping from the specified key, if there was any.
-     */
-    public void delete(int key) {
-        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i >= 0) {
-            if (mValues[i] != DELETED) {
-                mValues[i] = DELETED;
-                mGarbage = true;
-            }
-        }
-    }
-
-    /**
-     * Alias for {@link #delete(int)}.
-     */
-    public void remove(int key) {
-        delete(key);
-    }
-
-    /**
-     * Removes the mapping at the specified index.
-     */
-    public void removeAt(int index) {
-        if (mValues[index] != DELETED) {
-            mValues[index] = DELETED;
-            mGarbage = true;
-        }
-    }
-
-    /**
-     * Remove a range of mappings as a batch.
-     *
-     * @param index Index to begin at
-     * @param size Number of mappings to remove
-     */
-    public void removeAtRange(int index, int size) {
-        final int end = Math.min(mSize, index + size);
-        for (int i = index; i < end; i++) {
-            removeAt(i);
-        }
-    }
-
-    public void insertKeyRange(int keyStart, int count) {
-
-    }
-
-    public void removeKeyRange(ArrayList<E> removedItems, int keyStart, int count) {
-
-    }
-
-    private void gc() {
-        // Log.e("SparseArray", "gc start with " + mSize);
-
-        int n = mSize;
-        int o = 0;
-        int[] keys = mKeys;
-        Object[] values = mValues;
-
-        for (int i = 0; i < n; i++) {
-            Object val = values[i];
-
-            if (val != DELETED) {
-                if (i != o) {
-                    keys[o] = keys[i];
-                    values[o] = val;
-                    values[i] = null;
-                }
-
-                o++;
-            }
-        }
-
-        mGarbage = false;
-        mSize = o;
-
-        // Log.e("SparseArray", "gc end with " + mSize);
-    }
-
-    /**
-     * Adds a mapping from the specified key to the specified value,
-     * replacing the previous mapping from the specified key if there
-     * was one.
-     */
-    public void put(int key, E value) {
-        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
-        if (i >= 0) {
-            mValues[i] = value;
-        } else {
-            i = ~i;
-
-            if (i < mSize && mValues[i] == DELETED) {
-                mKeys[i] = key;
-                mValues[i] = value;
-                return;
-            }
-
-            if (mGarbage && mSize >= mKeys.length) {
-                gc();
-
-                // Search again because indices may have changed.
-                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
-            }
-
-            if (mSize >= mKeys.length) {
-                int n = idealIntArraySize(mSize + 1);
-
-                int[] nkeys = new int[n];
-                Object[] nvalues = new Object[n];
-
-                // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
-                System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
-                System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
-                mKeys = nkeys;
-                mValues = nvalues;
-            }
-
-            if (mSize - i != 0) {
-                // Log.e("SparseArray", "move " + (mSize - i));
-                System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
-                System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
-            }
-
-            mKeys[i] = key;
-            mValues[i] = value;
-            mSize++;
-        }
-    }
-
-    /**
-     * Returns the number of key-value mappings that this SparseArray
-     * currently stores.
-     */
-    public int size() {
-        if (mGarbage) {
-            gc();
-        }
-
-        return mSize;
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, returns
-     * the key from the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
-     */
-    public int keyAt(int index) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return mKeys[index];
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, returns
-     * the value from the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
-     */
-    @SuppressWarnings("unchecked")
-    public E valueAt(int index) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return (E) mValues[index];
-    }
-
-    /**
-     * Given an index in the range <code>0...size()-1</code>, sets a new
-     * value for the <code>index</code>th key-value mapping that this
-     * SparseArray stores.
-     */
-    public void setValueAt(int index, E value) {
-        if (mGarbage) {
-            gc();
-        }
-
-        mValues[index] = value;
-    }
-
-    /**
-     * Returns the index for which {@link #keyAt} would return the
-     * specified key, or a negative number if the specified
-     * key is not mapped.
-     */
-    public int indexOfKey(int key) {
-        if (mGarbage) {
-            gc();
-        }
-
-        return ContainerHelpers.binarySearch(mKeys, mSize, key);
-    }
-
-    /**
-     * Returns an index for which {@link #valueAt} would return the
-     * specified key, or a negative number if no keys map to the
-     * specified value.
-     * <p>Beware that this is a linear search, unlike lookups by key,
-     * and that multiple keys can map to the same value and this will
-     * find only one of them.
-     * <p>Note also that unlike most collections' {@code indexOf} methods,
-     * this method compares values using {@code ==} rather than {@code equals}.
-     */
-    public int indexOfValue(E value) {
-        if (mGarbage) {
-            gc();
-        }
-
-        for (int i = 0; i < mSize; i++) {
-            if (mValues[i] == value) {
-                return i;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Removes all key-value mappings from this SparseArray.
-     */
-    public void clear() {
-        int n = mSize;
-        Object[] values = mValues;
-
-        for (int i = 0; i < n; i++) {
-            values[i] = null;
-        }
-
-        mSize = 0;
-        mGarbage = false;
-    }
-
-    /**
-     * Puts a key/value pair into the array, optimizing for the case where
-     * the key is greater than all existing keys in the array.
-     */
-    public void append(int key, E value) {
-        if (mSize != 0 && key <= mKeys[mSize - 1]) {
-            put(key, value);
-            return;
-        }
-
-        if (mGarbage && mSize >= mKeys.length) {
-            gc();
-        }
-
-        int pos = mSize;
-        if (pos >= mKeys.length) {
-            int n = idealIntArraySize(pos + 1);
-
-            int[] nkeys = new int[n];
-            Object[] nvalues = new Object[n];
-
-            // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
-            System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
-            System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
-            mKeys = nkeys;
-            mValues = nvalues;
-        }
-
-        mKeys[pos] = key;
-        mValues[pos] = value;
-        mSize = pos + 1;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>This implementation composes a string by iterating over its mappings. If
-     * this map contains itself as a value, the string "(this Map)"
-     * will appear in its place.
-     */
-    @Override
-    public String toString() {
-        if (size() <= 0) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 28);
-        buffer.append('{');
-        for (int i = 0; i < mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            int key = keyAt(i);
-            buffer.append(key);
-            buffer.append('=');
-            Object value = valueAt(i);
-            if (value != this) {
-                buffer.append(value);
-            } else {
-                buffer.append("(this Map)");
-            }
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-    static int idealByteArraySize(int need) {
-        for (int i = 4; i < 32; i++) {
-            if (need <= (1 << i) - 12) {
-                return (1 << i) - 12;
-            }
-        }
-
-        return need;
-    }
-
-    static int idealBooleanArraySize(int need) {
-        return idealByteArraySize(need);
-    }
-
-    static int idealShortArraySize(int need) {
-        return idealByteArraySize(need * 2) / 2;
-    }
-
-    static int idealCharArraySize(int need) {
-        return idealByteArraySize(need * 2) / 2;
-    }
-
-    static int idealIntArraySize(int need) {
-        return idealByteArraySize(need * 4) / 4;
-    }
-
-    static int idealFloatArraySize(int need) {
-        return idealByteArraySize(need * 4) / 4;
-    }
-
-    static int idealObjectArraySize(int need) {
-        return idealByteArraySize(need * 4) / 4;
-    }
-
-    static int idealLongArraySize(int need) {
-        return idealByteArraySize(need * 8) / 8;
-    }
-
-    static class ContainerHelpers {
-        static final boolean[] EMPTY_BOOLEANS = new boolean[0];
-        static final int[] EMPTY_INTS = new int[0];
-        static final long[] EMPTY_LONGS = new long[0];
-        static final Object[] EMPTY_OBJECTS = new Object[0];
-
-        // This is Arrays.binarySearch(), but doesn't do any argument validation.
-        static int binarySearch(int[] array, int size, int value) {
-            int lo = 0;
-            int hi = size - 1;
-
-            while (lo <= hi) {
-                final int mid = (lo + hi) >>> 1;
-                final int midVal = array[mid];
-
-                if (midVal < value) {
-                    lo = mid + 1;
-                } else if (midVal > value) {
-                    hi = mid - 1;
-                } else {
-                    return mid;  // value found
-                }
-            }
-            return ~lo;  // value not present
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/RecyclerView.java b/android/support/v7/widget/RecyclerView.java
deleted file mode 100644
index b195d3c..0000000
--- a/android/support/v7/widget/RecyclerView.java
+++ /dev/null
@@ -1,12968 +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.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;
-import android.graphics.Matrix;
-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;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.annotation.CallSuper;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.os.TraceCompat;
-import android.support.v4.util.Preconditions;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.InputDeviceCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.NestedScrollingChild2;
-import android.support.v4.view.NestedScrollingChildHelper;
-import android.support.v4.view.ScrollingView;
-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.widget.EdgeEffectCompat;
-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.view.Display;
-import android.view.FocusFinder;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Interpolator;
-import android.widget.EdgeEffect;
-import android.widget.LinearLayout;
-import android.widget.OverScroller;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A flexible view for providing a limited window into a large data set.
- *
- * <h3>Glossary of terms:</h3>
- *
- * <ul>
- *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
- *     that represent items in a data set.</li>
- *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
- *     <li><em>Index:</em> The index of an attached child view as used in a call to
- *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
- *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
- *     to a <em>position</em> within the adapter.</li>
- *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
- *     position may be placed in a cache for later reuse to display the same type of data again
- *     later. This can drastically improve performance by skipping initial layout inflation
- *     or construction.</li>
- *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
- *     state during layout. Scrap views may be reused without becoming fully detached
- *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
- *     by the adapter if the view was considered <em>dirty</em>.</li>
- *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
- *     being displayed.</li>
- * </ul>
- *
- * <h4>Positions in RecyclerView:</h4>
- * <p>
- * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
- * {@link LayoutManager} to be able to detect data set changes in batches during a layout
- * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
- * It also helps with performance because all view bindings happen at the same time and unnecessary
- * bindings are avoided.
- * <p>
- * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
- * <ul>
- *     <li>layout position: Position of an item in the latest layout calculation. This is the
- *     position from the LayoutManager's perspective.</li>
- *     <li>adapter position: Position of an item in the adapter. This is the position from
- *     the Adapter's perspective.</li>
- * </ul>
- * <p>
- * These two positions are the same except the time between dispatching <code>adapter.notify*
- * </code> events and calculating the updated layout.
- * <p>
- * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
- * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
- * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
- * last layout calculation. You can rely on these positions to be consistent with what user is
- * currently seeing on the screen. For example, if you have a list of items on the screen and user
- * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
- * is seeing.
- * <p>
- * The other set of position related methods are in the form of
- * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
- * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
- * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
- * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
- * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
- * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
- * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
- * <code>null</code> results from these methods.
- * <p>
- * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
- * writing an {@link Adapter}, you probably want to use adapter positions.
- *
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
- */
-public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
-
-    static final String TAG = "RecyclerView";
-
-    static final boolean DEBUG = false;
-
-    static final boolean VERBOSE_TRACING = false;
-
-    private static final int[]  NESTED_SCROLLING_ATTRS =
-            {16843830 /* android.R.attr.nestedScrollingEnabled */};
-
-    private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
-
-    /**
-     * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
-     * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
-     * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
-     * recursively traverses itemView and invalidates display list for each ViewGroup that matches
-     * this criteria.
-     */
-    static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
-            || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
-    /**
-     * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
-     * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
-     * 0 when mode is unspecified.
-     */
-    static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
-
-    static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
-
-    /**
-     * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
-     * RenderThread but before the next frame begins. We schedule prefetch work in this window.
-     */
-    private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
-
-    /**
-     * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
-     * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
-     */
-    private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
-
-    /**
-     * on API 15-, a focused child can still be considered a focused child of RV even after
-     * it's being removed or its focusable flag is set to false. This is because when this focused
-     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
-     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
-     * to request focus on a new child, which will clear the focus on the old (detached) child as a
-     * side-effect.
-     */
-    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
-
-    static final boolean DISPATCH_TEMP_DETACH = false;
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    @IntDef({HORIZONTAL, VERTICAL})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Orientation {}
-
-    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
-    public static final int VERTICAL = LinearLayout.VERTICAL;
-
-    static final int DEFAULT_ORIENTATION = VERTICAL;
-    public static final int NO_POSITION = -1;
-    public static final long NO_ID = -1;
-    public static final int INVALID_TYPE = -1;
-
-    /**
-     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
-     * that the RecyclerView should use the standard touch slop for smooth,
-     * continuous scrolling.
-     */
-    public static final int TOUCH_SLOP_DEFAULT = 0;
-
-    /**
-     * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
-     * that the RecyclerView should use the standard touch slop for scrolling
-     * widgets that snap to a page or other coarse-grained barrier.
-     */
-    public static final int TOUCH_SLOP_PAGING = 1;
-
-    static final int MAX_SCROLL_DURATION = 2000;
-
-    /**
-     * RecyclerView is calculating a scroll.
-     * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
-     * it. Try to avoid using EditText, focusable views or handle them with care.
-     */
-    static final String TRACE_SCROLL_TAG = "RV Scroll";
-
-    /**
-     * OnLayout has been called by the View system.
-     * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
-     * update themselves directly. This will cause a full re-layout but when it happens via the
-     * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
-     */
-    private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
-
-    /**
-     * NotifyDataSetChanged or equal has been called.
-     * If this is taking a long time, try sending granular notify adapter changes instead of just
-     * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
-     * might help.
-     */
-    private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
-
-    /**
-     * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
-     * If this is taking a long time, you may have dispatched too many Adapter updates causing too
-     * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
-     * methods.
-     */
-    private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
-
-    /**
-     * RecyclerView is rebinding a View.
-     * If this is taking a lot of time, consider optimizing your layout or make sure you are not
-     * doing extra operations in onBindViewHolder call.
-     */
-    static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
-
-    /**
-     * RecyclerView is attempting to pre-populate off screen views.
-     */
-    static final String TRACE_PREFETCH_TAG = "RV Prefetch";
-
-    /**
-     * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
-     * RecyclerView.
-     */
-    static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
-
-    /**
-     * RecyclerView is creating a new View.
-     * If too many of these present in Systrace:
-     * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
-     * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
-     * > Adapter#onFailedToRecycleView(ViewHolder)})
-     *
-     * - There might be too many item view types.
-     * > Try merging them
-     *
-     * - There might be too many itemChange animations and not enough space in RecyclerPool.
-     * >Try increasing your pool size and item cache size.
-     */
-    static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
-    private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
-            new Class[]{Context.class, AttributeSet.class, int.class, int.class};
-
-    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
-
-    final Recycler mRecycler = new Recycler();
-
-    private SavedState mPendingSavedState;
-
-    /**
-     * Handles adapter updates
-     */
-    AdapterHelper mAdapterHelper;
-
-    /**
-     * Handles abstraction between LayoutManager children and RecyclerView children
-     */
-    ChildHelper mChildHelper;
-
-    /**
-     * Keeps data about views to be used for animations
-     */
-    final ViewInfoStore mViewInfoStore = new ViewInfoStore();
-
-    /**
-     * Prior to L, there is no way to query this variable which is why we override the setter and
-     * track it here.
-     */
-    boolean mClipToPadding;
-
-    /**
-     * Note: this Runnable is only ever posted if:
-     * 1) We've been through first layout
-     * 2) We know we have a fixed size (mHasFixedSize)
-     * 3) We're attached
-     */
-    final Runnable mUpdateChildViewsRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (!mFirstLayoutComplete || isLayoutRequested()) {
-                // a layout request will happen, we should not do layout here.
-                return;
-            }
-            if (!mIsAttached) {
-                requestLayout();
-                // if we are not attached yet, mark us as requiring layout and skip
-                return;
-            }
-            if (mLayoutFrozen) {
-                mLayoutWasDefered = true;
-                return; //we'll process updates when ice age ends.
-            }
-            consumePendingUpdateOperations();
-        }
-    };
-
-    final Rect mTempRect = new Rect();
-    private final Rect mTempRect2 = new Rect();
-    final RectF mTempRectF = new RectF();
-    Adapter mAdapter;
-    @VisibleForTesting LayoutManager mLayout;
-    RecyclerListener mRecyclerListener;
-    final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
-    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
-            new ArrayList<>();
-    private OnItemTouchListener mActiveOnItemTouchListener;
-    boolean mIsAttached;
-    boolean mHasFixedSize;
-    boolean mEnableFastScroller;
-    @VisibleForTesting boolean mFirstLayoutComplete;
-
-    /**
-     * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
-     * calls to {@link #startInterceptRequestLayout()} - number of calls to
-     * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
-     * should defer layout operations caused by layout requests from children of
-     * {@link RecyclerView}.
-     */
-    private int mInterceptRequestLayoutDepth = 0;
-
-    /**
-     * True if a call to requestLayout was intercepted and prevented from executing like normal and
-     * we plan on continuing with normal execution later.
-     */
-    boolean mLayoutWasDefered;
-
-    boolean mLayoutFrozen;
-    private boolean mIgnoreMotionEventTillDown;
-
-    // binary OR of change events that were eaten during a layout or scroll.
-    private int mEatenAccessibilityChangeFlags;
-    boolean mAdapterUpdateDuringMeasure;
-
-    private final AccessibilityManager mAccessibilityManager;
-    private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
-
-    /**
-     * True after an event occurs that signals that the entire data set has changed. In that case,
-     * we cannot run any animations since we don't know what happened until layout.
-     *
-     * Attached items are invalid until next layout, at which point layout will animate/replace
-     * items as necessary, building up content from the (effectively) new adapter from scratch.
-     *
-     * Cached items must be discarded when setting this to true, so that the cache may be freely
-     * used by prefetching until the next layout occurs.
-     *
-     * @see #processDataSetCompletelyChanged(boolean)
-     */
-    boolean mDataSetHasChangedAfterLayout = false;
-
-    /**
-     * True after the data set has completely changed and
-     * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
-     * measure/layout.
-     *
-     * @see #processDataSetCompletelyChanged(boolean)
-     */
-    boolean mDispatchItemsChangedEvent = false;
-
-    /**
-     * This variable is incremented during a dispatchLayout and/or scroll.
-     * Some methods should not be called during these periods (e.g. adapter data change).
-     * Doing so will create hard to find bugs so we better check it and throw an exception.
-     *
-     * @see #assertInLayoutOrScroll(String)
-     * @see #assertNotInLayoutOrScroll(String)
-     */
-    private int mLayoutOrScrollCounter = 0;
-
-    /**
-     * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
-     * (for API compatibility).
-     * <p>
-     * It is a bad practice for a developer to update the data in a scroll callback since it is
-     * potentially called during a layout.
-     */
-    private int mDispatchScrollCounter = 0;
-
-    @NonNull
-    private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
-    private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
-
-    ItemAnimator mItemAnimator = new DefaultItemAnimator();
-
-    private static final int INVALID_POINTER = -1;
-
-    /**
-     * The RecyclerView is not currently scrolling.
-     * @see #getScrollState()
-     */
-    public static final int SCROLL_STATE_IDLE = 0;
-
-    /**
-     * The RecyclerView is currently being dragged by outside input such as user touch input.
-     * @see #getScrollState()
-     */
-    public static final int SCROLL_STATE_DRAGGING = 1;
-
-    /**
-     * The RecyclerView is currently animating to a final position while not under
-     * outside control.
-     * @see #getScrollState()
-     */
-    public static final int SCROLL_STATE_SETTLING = 2;
-
-    static final long FOREVER_NS = Long.MAX_VALUE;
-
-    // Touch/scrolling handling
-
-    private int mScrollState = SCROLL_STATE_IDLE;
-    private int mScrollPointerId = INVALID_POINTER;
-    private VelocityTracker mVelocityTracker;
-    private int mInitialTouchX;
-    private int mInitialTouchY;
-    private int mLastTouchX;
-    private int mLastTouchY;
-    private int mTouchSlop;
-    private OnFlingListener mOnFlingListener;
-    private final int mMinFlingVelocity;
-    private final int mMaxFlingVelocity;
-
-    // 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();
-
-    GapWorker mGapWorker;
-    GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
-            ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
-
-    final State mState = new State();
-
-    private OnScrollListener mScrollListener;
-    private List<OnScrollListener> mScrollListeners;
-
-    // For use in item animations
-    boolean mItemsAddedOrRemoved = false;
-    boolean mItemsChanged = false;
-    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
-            new ItemAnimatorRestoreListener();
-    boolean mPostedAnimatorRunner = false;
-    RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
-    private ChildDrawingOrderCallback mChildDrawingOrderCallback;
-
-    // simple array to keep min and max child position during a layout calculation
-    // preserved not to create a new one in each layout pass
-    private final int[] mMinMaxLayoutPositions = new int[2];
-
-    private NestedScrollingChildHelper mScrollingChildHelper;
-    private final int[] mScrollOffset = new int[2];
-    private final int[] mScrollConsumed = new int[2];
-    private final int[] mNestedOffsets = new int[2];
-
-    /**
-     * These are views that had their a11y importance changed during a layout. We defer these events
-     * until the end of the layout because a11y service may make sync calls back to the RV while
-     * the View's state is undefined.
-     */
-    @VisibleForTesting
-    final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
-
-    private Runnable mItemAnimatorRunner = new Runnable() {
-        @Override
-        public void run() {
-            if (mItemAnimator != null) {
-                mItemAnimator.runPendingAnimations();
-            }
-            mPostedAnimatorRunner = false;
-        }
-    };
-
-    static final Interpolator sQuinticInterpolator = new Interpolator() {
-        @Override
-        public float getInterpolation(float t) {
-            t -= 1.0f;
-            return t * t * t * t * t + 1.0f;
-        }
-    };
-
-    /**
-     * The callback to convert view info diffs into animations.
-     */
-    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
-            new ViewInfoStore.ProcessCallback() {
-                @Override
-                public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
-                        @Nullable ItemHolderInfo postInfo) {
-                    mRecycler.unscrapView(viewHolder);
-                    animateDisappearance(viewHolder, info, postInfo);
-                }
-                @Override
-                public void processAppeared(ViewHolder viewHolder,
-                        ItemHolderInfo preInfo, ItemHolderInfo info) {
-                    animateAppearance(viewHolder, preInfo, info);
-                }
-
-                @Override
-                public void processPersistent(ViewHolder viewHolder,
-                        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
-                    viewHolder.setIsRecyclable(false);
-                    if (mDataSetHasChangedAfterLayout) {
-                        // since it was rebound, use change instead as we'll be mapping them from
-                        // stable ids. If stable ids were false, we would not be running any
-                        // animations
-                        if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
-                                postInfo)) {
-                            postAnimationRunner();
-                        }
-                    } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
-                        postAnimationRunner();
-                    }
-                }
-                @Override
-                public void unused(ViewHolder viewHolder) {
-                    mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
-                }
-            };
-
-    public RecyclerView(Context context) {
-        this(context, null);
-    }
-
-    public RecyclerView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        if (attrs != null) {
-            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
-            mClipToPadding = a.getBoolean(0, true);
-            a.recycle();
-        } else {
-            mClipToPadding = true;
-        }
-        setScrollContainer(true);
-        setFocusableInTouchMode(true);
-
-        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);
-
-        mItemAnimator.setListener(mItemAnimatorListener);
-        initAdapterManager();
-        initChildrenHelper();
-        // If not explicitly specified this view is important for accessibility.
-        if (ViewCompat.getImportantForAccessibility(this)
-                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-            ViewCompat.setImportantForAccessibility(this,
-                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
-        }
-        mAccessibilityManager = (AccessibilityManager) getContext()
-                .getSystemService(Context.ACCESSIBILITY_SERVICE);
-        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
-        // Create the layoutManager if specified.
-
-        boolean nestedScrollingEnabled = true;
-
-        if (attrs != null) {
-            int defStyleRes = 0;
-            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
-                    defStyle, defStyleRes);
-            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
-            int descendantFocusability = a.getInt(
-                    R.styleable.RecyclerView_android_descendantFocusability, -1);
-            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);
-
-            if (Build.VERSION.SDK_INT >= 21) {
-                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
-                        defStyle, defStyleRes);
-                nestedScrollingEnabled = a.getBoolean(0, true);
-                a.recycle();
-            }
-        } else {
-            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
-        }
-
-        // Re-set whether nested scrolling is enabled so that it is set on all API levels
-        setNestedScrollingEnabled(nestedScrollingEnabled);
-    }
-
-    /**
-     * 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
-     */
-    public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
-        return mAccessibilityDelegate;
-    }
-
-    /**
-     * Sets the accessibility delegate compatibility implementation used by RecyclerView.
-     * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
-     */
-    public void setAccessibilityDelegateCompat(
-            RecyclerViewAccessibilityDelegate accessibilityDelegate) {
-        mAccessibilityDelegate = accessibilityDelegate;
-        ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
-    }
-
-    /**
-     * Instantiate and set a LayoutManager, if specified in the attributes.
-     */
-    private void createLayoutManager(Context context, String className, AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        if (className != null) {
-            className = className.trim();
-            if (!className.isEmpty()) {
-                className = getFullClassName(context, className);
-                try {
-                    ClassLoader classLoader;
-                    if (isInEditMode()) {
-                        // Stupid layoutlib cannot handle simple class loaders.
-                        classLoader = this.getClass().getClassLoader();
-                    } else {
-                        classLoader = context.getClassLoader();
-                    }
-                    Class<? extends LayoutManager> layoutManagerClass =
-                            classLoader.loadClass(className).asSubclass(LayoutManager.class);
-                    Constructor<? extends LayoutManager> constructor;
-                    Object[] constructorArgs = null;
-                    try {
-                        constructor = layoutManagerClass
-                                .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
-                        constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
-                    } catch (NoSuchMethodException e) {
-                        try {
-                            constructor = layoutManagerClass.getConstructor();
-                        } catch (NoSuchMethodException e1) {
-                            e1.initCause(e);
-                            throw new IllegalStateException(attrs.getPositionDescription()
-                                    + ": Error creating LayoutManager " + className, e1);
-                        }
-                    }
-                    constructor.setAccessible(true);
-                    setLayoutManager(constructor.newInstance(constructorArgs));
-                } catch (ClassNotFoundException e) {
-                    throw new IllegalStateException(attrs.getPositionDescription()
-                            + ": Unable to find LayoutManager " + className, e);
-                } catch (InvocationTargetException e) {
-                    throw new IllegalStateException(attrs.getPositionDescription()
-                            + ": Could not instantiate the LayoutManager: " + className, e);
-                } catch (InstantiationException e) {
-                    throw new IllegalStateException(attrs.getPositionDescription()
-                            + ": Could not instantiate the LayoutManager: " + className, e);
-                } catch (IllegalAccessException e) {
-                    throw new IllegalStateException(attrs.getPositionDescription()
-                            + ": Cannot access non-public constructor " + className, e);
-                } catch (ClassCastException e) {
-                    throw new IllegalStateException(attrs.getPositionDescription()
-                            + ": Class is not a LayoutManager " + className, e);
-                }
-            }
-        }
-    }
-
-    private String getFullClassName(Context context, String className) {
-        if (className.charAt(0) == '.') {
-            return context.getPackageName() + className;
-        }
-        if (className.contains(".")) {
-            return className;
-        }
-        return RecyclerView.class.getPackage().getName() + '.' + className;
-    }
-
-    private void initChildrenHelper() {
-        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
-            @Override
-            public int getChildCount() {
-                return RecyclerView.this.getChildCount();
-            }
-
-            @Override
-            public void addView(View child, int index) {
-                if (VERBOSE_TRACING) {
-                    TraceCompat.beginSection("RV addView");
-                }
-                RecyclerView.this.addView(child, index);
-                if (VERBOSE_TRACING) {
-                    TraceCompat.endSection();
-                }
-                dispatchChildAttached(child);
-            }
-
-            @Override
-            public int indexOfChild(View view) {
-                return RecyclerView.this.indexOfChild(view);
-            }
-
-            @Override
-            public void removeViewAt(int index) {
-                final View child = RecyclerView.this.getChildAt(index);
-                if (child != null) {
-                    dispatchChildDetached(child);
-
-                    // Clear any android.view.animation.Animation that may prevent the item from
-                    // detaching when being removed. If a child is re-added before the
-                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
-                    child.clearAnimation();
-                }
-                if (VERBOSE_TRACING) {
-                    TraceCompat.beginSection("RV removeViewAt");
-                }
-                RecyclerView.this.removeViewAt(index);
-                if (VERBOSE_TRACING) {
-                    TraceCompat.endSection();
-                }
-            }
-
-            @Override
-            public View getChildAt(int offset) {
-                return RecyclerView.this.getChildAt(offset);
-            }
-
-            @Override
-            public void removeAllViews() {
-                final int count = getChildCount();
-                for (int i = 0; i < count; i++) {
-                    View child = getChildAt(i);
-                    dispatchChildDetached(child);
-
-                    // Clear any android.view.animation.Animation that may prevent the item from
-                    // detaching when being removed. If a child is re-added before the
-                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
-                    child.clearAnimation();
-                }
-                RecyclerView.this.removeAllViews();
-            }
-
-            @Override
-            public ViewHolder getChildViewHolder(View view) {
-                return getChildViewHolderInt(view);
-            }
-
-            @Override
-            public void attachViewToParent(View child, int index,
-                    ViewGroup.LayoutParams layoutParams) {
-                final ViewHolder vh = getChildViewHolderInt(child);
-                if (vh != null) {
-                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
-                        throw new IllegalArgumentException("Called attach on a child which is not"
-                                + " detached: " + vh + exceptionLabel());
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "reAttach " + vh);
-                    }
-                    vh.clearTmpDetachFlag();
-                }
-                RecyclerView.this.attachViewToParent(child, index, layoutParams);
-            }
-
-            @Override
-            public void detachViewFromParent(int offset) {
-                final View view = getChildAt(offset);
-                if (view != null) {
-                    final ViewHolder vh = getChildViewHolderInt(view);
-                    if (vh != null) {
-                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {
-                            throw new IllegalArgumentException("called detach on an already"
-                                    + " detached child " + vh + exceptionLabel());
-                        }
-                        if (DEBUG) {
-                            Log.d(TAG, "tmpDetach " + vh);
-                        }
-                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
-                    }
-                }
-                RecyclerView.this.detachViewFromParent(offset);
-            }
-
-            @Override
-            public void onEnteredHiddenState(View child) {
-                final ViewHolder vh = getChildViewHolderInt(child);
-                if (vh != null) {
-                    vh.onEnteredHiddenState(RecyclerView.this);
-                }
-            }
-
-            @Override
-            public void onLeftHiddenState(View child) {
-                final ViewHolder vh = getChildViewHolderInt(child);
-                if (vh != null) {
-                    vh.onLeftHiddenState(RecyclerView.this);
-                }
-            }
-        });
-    }
-
-    void initAdapterManager() {
-        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
-            @Override
-            public ViewHolder findViewHolder(int position) {
-                final ViewHolder vh = findViewHolderForPosition(position, true);
-                if (vh == null) {
-                    return null;
-                }
-                // ensure it is not hidden because for adapter helper, the only thing matter is that
-                // LM thinks view is a child.
-                if (mChildHelper.isHidden(vh.itemView)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
-                    }
-                    return null;
-                }
-                return vh;
-            }
-
-            @Override
-            public void offsetPositionsForRemovingInvisible(int start, int count) {
-                offsetPositionRecordsForRemove(start, count, true);
-                mItemsAddedOrRemoved = true;
-                mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
-            }
-
-            @Override
-            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);
-                mItemsChanged = true;
-            }
-
-            @Override
-            public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
-                dispatchUpdate(op);
-            }
-
-            void dispatchUpdate(AdapterHelper.UpdateOp op) {
-                switch (op.cmd) {
-                    case AdapterHelper.UpdateOp.ADD:
-                        mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
-                        break;
-                    case AdapterHelper.UpdateOp.REMOVE:
-                        mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
-                        break;
-                    case AdapterHelper.UpdateOp.UPDATE:
-                        mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
-                                op.payload);
-                        break;
-                    case AdapterHelper.UpdateOp.MOVE:
-                        mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
-                        break;
-                }
-            }
-
-            @Override
-            public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
-                dispatchUpdate(op);
-            }
-
-            @Override
-            public void offsetPositionsForAdd(int positionStart, int itemCount) {
-                offsetPositionRecordsForInsert(positionStart, itemCount);
-                mItemsAddedOrRemoved = true;
-            }
-
-            @Override
-            public void offsetPositionsForMove(int from, int to) {
-                offsetPositionRecordsForMove(from, to);
-                // should we create mItemsMoved ?
-                mItemsAddedOrRemoved = true;
-            }
-        });
-    }
-
-    /**
-     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
-     * size is not affected by the adapter contents. RecyclerView can still change its size based
-     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
-     * size of its children or contents of its adapter (except the number of items in the adapter).
-     * <p>
-     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
-     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
-     *
-     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
-     */
-    public void setHasFixedSize(boolean hasFixedSize) {
-        mHasFixedSize = hasFixedSize;
-    }
-
-    /**
-     * @return true if the app has specified that changes in adapter content cannot change
-     * the size of the RecyclerView itself.
-     */
-    public boolean hasFixedSize() {
-        return mHasFixedSize;
-    }
-
-    @Override
-    public void setClipToPadding(boolean clipToPadding) {
-        if (clipToPadding != mClipToPadding) {
-            invalidateGlows();
-        }
-        mClipToPadding = clipToPadding;
-        super.setClipToPadding(clipToPadding);
-        if (mFirstLayoutComplete) {
-            requestLayout();
-        }
-    }
-
-    /**
-     * Returns whether this RecyclerView will clip its children to its padding, and resize (but
-     * not clip) any EdgeEffect to the padded region, if padding is present.
-     * <p>
-     * By default, children are clipped to the padding of their parent
-     * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
-     *
-     * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
-     *         clip) any EdgeEffect to the padded region, false otherwise.
-     *
-     * @attr name android:clipToPadding
-     */
-    @Override
-    public boolean getClipToPadding() {
-        return mClipToPadding;
-    }
-
-    /**
-     * Configure the scrolling touch slop for a specific use case.
-     *
-     * Set up the RecyclerView's scrolling motion threshold based on common usages.
-     * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
-     *
-     * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
-     *                     the intended usage of this RecyclerView
-     */
-    public void setScrollingTouchSlop(int slopConstant) {
-        final ViewConfiguration vc = ViewConfiguration.get(getContext());
-        switch (slopConstant) {
-            default:
-                Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
-                        + slopConstant + "; using default value");
-                // fall-through
-            case TOUCH_SLOP_DEFAULT:
-                mTouchSlop = vc.getScaledTouchSlop();
-                break;
-
-            case TOUCH_SLOP_PAGING:
-                mTouchSlop = vc.getScaledPagingTouchSlop();
-                break;
-        }
-    }
-
-    /**
-     * Swaps the current adapter with the provided one. It is similar to
-     * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
-     * {@link ViewHolder} and does not clear the RecycledViewPool.
-     * <p>
-     * Note that it still calls onAdapterChanged callbacks.
-     *
-     * @param adapter The new adapter to set, or null to set no adapter.
-     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
-     *                                      Views. If adapters have stable ids and/or you want to
-     *                                      animate the disappearing views, you may prefer to set
-     *                                      this to false.
-     * @see #setAdapter(Adapter)
-     */
-    public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
-        // bail out if layout is frozen
-        setLayoutFrozen(false);
-        setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
-        processDataSetCompletelyChanged(true);
-        requestLayout();
-    }
-    /**
-     * Set a new adapter to provide child views on demand.
-     * <p>
-     * When adapter is changed, all existing views are recycled back to the pool. If the pool has
-     * only one adapter, it will be cleared.
-     *
-     * @param adapter The new adapter to set, or null to set no adapter.
-     * @see #swapAdapter(Adapter, boolean)
-     */
-    public void setAdapter(Adapter adapter) {
-        // bail out if layout is frozen
-        setLayoutFrozen(false);
-        setAdapterInternal(adapter, false, true);
-        processDataSetCompletelyChanged(false);
-        requestLayout();
-    }
-
-    /**
-     * Removes and recycles all views - both those currently attached, and those in the Recycler.
-     */
-    void removeAndRecycleViews() {
-        // end all running animations
-        if (mItemAnimator != null) {
-            mItemAnimator.endAnimations();
-        }
-        // Since animations are ended, mLayout.children should be equal to
-        // recyclerView.children. This may not be true if item animator's end does not work as
-        // expected. (e.g. not release children instantly). It is safer to use mLayout's child
-        // count.
-        if (mLayout != null) {
-            mLayout.removeAndRecycleAllViews(mRecycler);
-            mLayout.removeAndRecycleScrapInt(mRecycler);
-        }
-        // we should clear it here before adapters are swapped to ensure correct callbacks.
-        mRecycler.clear();
-    }
-
-    /**
-     * Replaces the current adapter with the new one and triggers listeners.
-     * @param adapter The new adapter
-     * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
-     *                               item types with the current adapter (helps us avoid cache
-     *                               invalidation).
-     * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
-     *                               compatibleWithPrevious is false, this parameter is ignored.
-     */
-    private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
-            boolean removeAndRecycleViews) {
-        if (mAdapter != null) {
-            mAdapter.unregisterAdapterDataObserver(mObserver);
-            mAdapter.onDetachedFromRecyclerView(this);
-        }
-        if (!compatibleWithPrevious || removeAndRecycleViews) {
-            removeAndRecycleViews();
-        }
-        mAdapterHelper.reset();
-        final Adapter oldAdapter = mAdapter;
-        mAdapter = adapter;
-        if (adapter != null) {
-            adapter.registerAdapterDataObserver(mObserver);
-            adapter.onAttachedToRecyclerView(this);
-        }
-        if (mLayout != null) {
-            mLayout.onAdapterChanged(oldAdapter, mAdapter);
-        }
-        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
-        mState.mStructureChanged = true;
-    }
-
-    /**
-     * Retrieves the previously set adapter or null if no adapter is set.
-     *
-     * @return The previously set adapter
-     * @see #setAdapter(Adapter)
-     */
-    public Adapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Register a listener that will be notified whenever a child view is recycled.
-     *
-     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
-     * that a child view is no longer needed. If an application associates expensive
-     * or heavyweight data with item views, this may be a good place to release
-     * or free those resources.</p>
-     *
-     * @param listener Listener to register, or null to clear
-     */
-    public void setRecyclerListener(RecyclerListener listener) {
-        mRecyclerListener = listener;
-    }
-
-    /**
-     * <p>Return the offset of the RecyclerView's text baseline from the its top
-     * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
-     * this method returns -1.</p>
-     *
-     * @return the offset of the baseline within the RecyclerView's bounds or -1
-     *         if baseline alignment is not supported
-     */
-    @Override
-    public int getBaseline() {
-        if (mLayout != null) {
-            return mLayout.getBaseline();
-        } else {
-            return super.getBaseline();
-        }
-    }
-
-    /**
-     * Register a listener that will be notified whenever a child view is attached to or detached
-     * from RecyclerView.
-     *
-     * <p>This listener will be called when a LayoutManager or the RecyclerView decides
-     * that a child view is no longer needed. If an application associates expensive
-     * or heavyweight data with item views, this may be a good place to release
-     * or free those resources.</p>
-     *
-     * @param listener Listener to register
-     */
-    public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
-        if (mOnChildAttachStateListeners == null) {
-            mOnChildAttachStateListeners = new ArrayList<>();
-        }
-        mOnChildAttachStateListeners.add(listener);
-    }
-
-    /**
-     * Removes the provided listener from child attached state listeners list.
-     *
-     * @param listener Listener to unregister
-     */
-    public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
-        if (mOnChildAttachStateListeners == null) {
-            return;
-        }
-        mOnChildAttachStateListeners.remove(listener);
-    }
-
-    /**
-     * Removes all listeners that were added via
-     * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
-     */
-    public void clearOnChildAttachStateChangeListeners() {
-        if (mOnChildAttachStateListeners != null) {
-            mOnChildAttachStateListeners.clear();
-        }
-    }
-
-    /**
-     * Set the {@link LayoutManager} that this RecyclerView will use.
-     *
-     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
-     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
-     * layout arrangements for child views. These arrangements are controlled by the
-     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
-     *
-     * <p>Several default strategies are provided for common uses such as lists and grids.</p>
-     *
-     * @param layout LayoutManager to use
-     */
-    public void setLayoutManager(LayoutManager layout) {
-        if (layout == mLayout) {
-            return;
-        }
-        stopScroll();
-        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
-        // chance that LayoutManagers will re-use views.
-        if (mLayout != null) {
-            // end all running animations
-            if (mItemAnimator != null) {
-                mItemAnimator.endAnimations();
-            }
-            mLayout.removeAndRecycleAllViews(mRecycler);
-            mLayout.removeAndRecycleScrapInt(mRecycler);
-            mRecycler.clear();
-
-            if (mIsAttached) {
-                mLayout.dispatchDetachedFromWindow(this, mRecycler);
-            }
-            mLayout.setRecyclerView(null);
-            mLayout = null;
-        } else {
-            mRecycler.clear();
-        }
-        // this is just a defensive measure for faulty item animators.
-        mChildHelper.removeAllViewsUnfiltered();
-        mLayout = layout;
-        if (layout != null) {
-            if (layout.mRecyclerView != null) {
-                throw new IllegalArgumentException("LayoutManager " + layout
-                        + " is already attached to a RecyclerView:"
-                        + layout.mRecyclerView.exceptionLabel());
-            }
-            mLayout.setRecyclerView(this);
-            if (mIsAttached) {
-                mLayout.dispatchAttachedToWindow(this);
-            }
-        }
-        mRecycler.updateViewCacheSize();
-        requestLayout();
-    }
-
-    /**
-     * Set a {@link OnFlingListener} for this {@link RecyclerView}.
-     * <p>
-     * If the {@link OnFlingListener} is set then it will receive
-     * calls to {@link #fling(int,int)} and will be able to intercept them.
-     *
-     * @param onFlingListener The {@link OnFlingListener} instance.
-     */
-    public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
-        mOnFlingListener = onFlingListener;
-    }
-
-    /**
-     * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
-     *
-     * @return The {@link OnFlingListener} instance currently set (can be null).
-     */
-    @Nullable
-    public OnFlingListener getOnFlingListener() {
-        return mOnFlingListener;
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        SavedState state = new SavedState(super.onSaveInstanceState());
-        if (mPendingSavedState != null) {
-            state.copyFrom(mPendingSavedState);
-        } else if (mLayout != null) {
-            state.mLayoutState = mLayout.onSaveInstanceState();
-        } else {
-            state.mLayoutState = null;
-        }
-
-        return state;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        mPendingSavedState = (SavedState) state;
-        super.onRestoreInstanceState(mPendingSavedState.getSuperState());
-        if (mLayout != null && mPendingSavedState.mLayoutState != null) {
-            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
-        }
-    }
-
-    /**
-     * Override to prevent freezing of any views created by the adapter.
-     */
-    @Override
-    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
-        dispatchFreezeSelfOnly(container);
-    }
-
-    /**
-     * Override to prevent thawing of any views created by the adapter.
-     */
-    @Override
-    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
-        dispatchThawSelfOnly(container);
-    }
-
-    /**
-     * Adds a view to the animatingViews list.
-     * mAnimatingViews holds the child views that are currently being kept around
-     * purely for the purpose of being animated out of view. They are drawn as a regular
-     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
-     * as they are managed separately from the regular child views.
-     * @param viewHolder The ViewHolder to be removed
-     */
-    private void addAnimatingView(ViewHolder viewHolder) {
-        final View view = viewHolder.itemView;
-        final boolean alreadyParented = view.getParent() == this;
-        mRecycler.unscrapView(getChildViewHolder(view));
-        if (viewHolder.isTmpDetached()) {
-            // re-attach
-            mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
-        } else if (!alreadyParented) {
-            mChildHelper.addView(view, true);
-        } else {
-            mChildHelper.hide(view);
-        }
-    }
-
-    /**
-     * Removes a view from the animatingViews list.
-     * @param view The view to be removed
-     * @see #addAnimatingView(RecyclerView.ViewHolder)
-     * @return true if an animating view is removed
-     */
-    boolean removeAnimatingView(View view) {
-        startInterceptRequestLayout();
-        final boolean removed = mChildHelper.removeViewIfHidden(view);
-        if (removed) {
-            final ViewHolder viewHolder = getChildViewHolderInt(view);
-            mRecycler.unscrapView(viewHolder);
-            mRecycler.recycleViewHolderInternal(viewHolder);
-            if (DEBUG) {
-                Log.d(TAG, "after removing animated view: " + view + ", " + this);
-            }
-        }
-        // only clear request eaten flag if we removed the view.
-        stopInterceptRequestLayout(!removed);
-        return removed;
-    }
-
-    /**
-     * Return the {@link LayoutManager} currently responsible for
-     * layout policy for this RecyclerView.
-     *
-     * @return The currently bound LayoutManager
-     */
-    public LayoutManager getLayoutManager() {
-        return mLayout;
-    }
-
-    /**
-     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
-     * if no pool is set for this view a new one will be created. See
-     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
-     *
-     * @return The pool used to store recycled item views for reuse.
-     * @see #setRecycledViewPool(RecycledViewPool)
-     */
-    public RecycledViewPool getRecycledViewPool() {
-        return mRecycler.getRecycledViewPool();
-    }
-
-    /**
-     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
-     * This can be useful if you have multiple RecyclerViews with adapters that use the same
-     * view types, for example if you have several data sets with the same kinds of item views
-     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
-     *
-     * @param pool Pool to set. If this parameter is null a new pool will be created and used.
-     */
-    public void setRecycledViewPool(RecycledViewPool pool) {
-        mRecycler.setRecycledViewPool(pool);
-    }
-
-    /**
-     * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
-     *
-     * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
-     *
-     * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
-     */
-    public void setViewCacheExtension(ViewCacheExtension extension) {
-        mRecycler.setViewCacheExtension(extension);
-    }
-
-    /**
-     * Set the number of offscreen views to retain before adding them to the potentially shared
-     * {@link #getRecycledViewPool() recycled view pool}.
-     *
-     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
-     * a LayoutManager to reuse those views unmodified without needing to return to the adapter
-     * to rebind them.</p>
-     *
-     * @param size Number of views to cache offscreen before returning them to the general
-     *             recycled view pool
-     */
-    public void setItemViewCacheSize(int size) {
-        mRecycler.setViewCacheSize(size);
-    }
-
-    /**
-     * Return the current scrolling state of the RecyclerView.
-     *
-     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
-     * {@link #SCROLL_STATE_SETTLING}
-     */
-    public int getScrollState() {
-        return mScrollState;
-    }
-
-    void setScrollState(int state) {
-        if (state == mScrollState) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
-                    new Exception());
-        }
-        mScrollState = state;
-        if (state != SCROLL_STATE_SETTLING) {
-            stopScrollersInternal();
-        }
-        dispatchOnScrollStateChanged(state);
-    }
-
-    /**
-     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
-     * affect both measurement and drawing of individual item views.
-     *
-     * <p>Item decorations are ordered. Decorations placed earlier in the list will
-     * be run/queried/drawn first for their effects on item views. Padding added to views
-     * will be nested; a padding added by an earlier decoration will mean further
-     * item decorations in the list will be asked to draw/pad within the previous decoration's
-     * given area.</p>
-     *
-     * @param decor Decoration to add
-     * @param index Position in the decoration chain to insert this decoration at. If this value
-     *              is negative the decoration will be added at the end.
-     */
-    public void addItemDecoration(ItemDecoration decor, int index) {
-        if (mLayout != null) {
-            mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
-                    + " layout");
-        }
-        if (mItemDecorations.isEmpty()) {
-            setWillNotDraw(false);
-        }
-        if (index < 0) {
-            mItemDecorations.add(decor);
-        } else {
-            mItemDecorations.add(index, decor);
-        }
-        markItemDecorInsetsDirty();
-        requestLayout();
-    }
-
-    /**
-     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
-     * affect both measurement and drawing of individual item views.
-     *
-     * <p>Item decorations are ordered. Decorations placed earlier in the list will
-     * be run/queried/drawn first for their effects on item views. Padding added to views
-     * will be nested; a padding added by an earlier decoration will mean further
-     * item decorations in the list will be asked to draw/pad within the previous decoration's
-     * given area.</p>
-     *
-     * @param decor Decoration to add
-     */
-    public void addItemDecoration(ItemDecoration decor) {
-        addItemDecoration(decor, -1);
-    }
-
-    /**
-     * 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) {
-        final int size = getItemDecorationCount();
-        if (index < 0 || index >= size) {
-            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
-        }
-
-        return mItemDecorations.get(index);
-    }
-
-    /**
-     * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
-     *
-     * @return number of ItemDecorations currently added added to this RecyclerView.
-     */
-    public int getItemDecorationCount() {
-        return mItemDecorations.size();
-    }
-
-    /**
-     * Removes the {@link ItemDecoration} associated with the supplied index position.
-     *
-     * @param index The index position of the ItemDecoration to be removed.
-     */
-    public void removeItemDecorationAt(int index) {
-        final int size = getItemDecorationCount();
-        if (index < 0 || index >= size) {
-            throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
-        }
-
-        removeItemDecoration(getItemDecorationAt(index));
-    }
-
-    /**
-     * Remove an {@link ItemDecoration} from this RecyclerView.
-     *
-     * <p>The given decoration will no longer impact the measurement and drawing of
-     * item views.</p>
-     *
-     * @param decor Decoration to remove
-     * @see #addItemDecoration(ItemDecoration)
-     */
-    public void removeItemDecoration(ItemDecoration decor) {
-        if (mLayout != null) {
-            mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
-                    + " layout");
-        }
-        mItemDecorations.remove(decor);
-        if (mItemDecorations.isEmpty()) {
-            setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
-        }
-        markItemDecorInsetsDirty();
-        requestLayout();
-    }
-
-    /**
-     * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
-     * <p>
-     * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
-     * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
-     * true if childDrawingOrderCallback is not null, false otherwise.
-     * <p>
-     * Note that child drawing order may be overridden by View's elevation.
-     *
-     * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
-     *                                  system.
-     */
-    public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
-        if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
-            return;
-        }
-        mChildDrawingOrderCallback = childDrawingOrderCallback;
-        setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
-    }
-
-    /**
-     * Set a listener that will be notified of any changes in scroll state or position.
-     *
-     * @param listener Listener to set or null to clear
-     *
-     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
-     *             {@link #removeOnScrollListener(OnScrollListener)}
-     */
-    @Deprecated
-    public void setOnScrollListener(OnScrollListener listener) {
-        mScrollListener = listener;
-    }
-
-    /**
-     * Add a listener that will be notified of any changes in scroll state or position.
-     *
-     * <p>Components that add a listener should take care to remove it when finished.
-     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
-     * to remove all attached listeners.</p>
-     *
-     * @param listener listener to set or null to clear
-     */
-    public void addOnScrollListener(OnScrollListener listener) {
-        if (mScrollListeners == null) {
-            mScrollListeners = new ArrayList<>();
-        }
-        mScrollListeners.add(listener);
-    }
-
-    /**
-     * Remove a listener that was notified of any changes in scroll state or position.
-     *
-     * @param listener listener to set or null to clear
-     */
-    public void removeOnScrollListener(OnScrollListener listener) {
-        if (mScrollListeners != null) {
-            mScrollListeners.remove(listener);
-        }
-    }
-
-    /**
-     * Remove all secondary listener that were notified of any changes in scroll state or position.
-     */
-    public void clearOnScrollListeners() {
-        if (mScrollListeners != null) {
-            mScrollListeners.clear();
-        }
-    }
-
-    /**
-     * Convenience method to scroll to a certain position.
-     *
-     * RecyclerView does not implement scrolling logic, rather forwards the call to
-     * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
-     * @param position Scroll to this adapter position
-     * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
-     */
-    public void scrollToPosition(int position) {
-        if (mLayoutFrozen) {
-            return;
-        }
-        stopScroll();
-        if (mLayout == null) {
-            Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
-                    + "Call setLayoutManager with a non-null argument.");
-            return;
-        }
-        mLayout.scrollToPosition(position);
-        awakenScrollBars();
-    }
-
-    void jumpToPositionForSmoothScroller(int position) {
-        if (mLayout == null) {
-            return;
-        }
-        mLayout.scrollToPosition(position);
-        awakenScrollBars();
-    }
-
-    /**
-     * Starts a smooth scroll to an adapter position.
-     * <p>
-     * To support smooth scrolling, you must override
-     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
-     * {@link SmoothScroller}.
-     * <p>
-     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
-     * provide a custom smooth scroll logic, override
-     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
-     * LayoutManager.
-     *
-     * @param position The adapter position to scroll to
-     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
-     */
-    public void smoothScrollToPosition(int position) {
-        if (mLayoutFrozen) {
-            return;
-        }
-        if (mLayout == null) {
-            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
-                    + "Call setLayoutManager with a non-null argument.");
-            return;
-        }
-        mLayout.smoothScrollToPosition(this, mState, position);
-    }
-
-    @Override
-    public void scrollTo(int x, int y) {
-        Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
-                + "Use scrollToPosition instead");
-    }
-
-    @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.");
-            return;
-        }
-        if (mLayoutFrozen) {
-            return;
-        }
-        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
-        final boolean canScrollVertical = mLayout.canScrollVertically();
-        if (canScrollHorizontal || canScrollVertical) {
-            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
-        }
-    }
-
-    /**
-     * Helper method reflect data changes to the state.
-     * <p>
-     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
-     * but data actually changed.
-     * <p>
-     * This method consumes all deferred changes to avoid that case.
-     */
-    void consumePendingUpdateOperations() {
-        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
-            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
-            dispatchLayout();
-            TraceCompat.endSection();
-            return;
-        }
-        if (!mAdapterHelper.hasPendingUpdates()) {
-            return;
-        }
-
-        // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
-        // of the visible items is affected and if not, just ignore the change.
-        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
-                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
-                        | AdapterHelper.UpdateOp.MOVE)) {
-            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
-            startInterceptRequestLayout();
-            onEnterLayoutOrScroll();
-            mAdapterHelper.preProcess();
-            if (!mLayoutWasDefered) {
-                if (hasUpdatedView()) {
-                    dispatchLayout();
-                } else {
-                    // no need to layout, clean state
-                    mAdapterHelper.consumePostponedUpdates();
-                }
-            }
-            stopInterceptRequestLayout(true);
-            onExitLayoutOrScroll();
-            TraceCompat.endSection();
-        } else if (mAdapterHelper.hasPendingUpdates()) {
-            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
-            dispatchLayout();
-            TraceCompat.endSection();
-        }
-    }
-
-    /**
-     * @return True if an existing view holder needs to be updated
-     */
-    private boolean hasUpdatedView() {
-        final int childCount = mChildHelper.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
-            if (holder == null || holder.shouldIgnore()) {
-                continue;
-            }
-            if (holder.isUpdated()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Does not perform bounds checking. Used by internal methods that have already validated input.
-     * <p>
-     * It also reports any unused scroll request to the related EdgeEffect.
-     *
-     * @param x The amount of horizontal scroll request
-     * @param y The amount of vertical scroll request
-     * @param ev The originating MotionEvent, or null if not from a touch event.
-     *
-     * @return Whether any scroll was consumed in either direction.
-     */
-    boolean scrollByInternal(int x, int y, MotionEvent ev) {
-        int unconsumedX = 0, unconsumedY = 0;
-        int consumedX = 0, consumedY = 0;
-
-        consumePendingUpdateOperations();
-        if (mAdapter != null) {
-            startInterceptRequestLayout();
-            onEnterLayoutOrScroll();
-            TraceCompat.beginSection(TRACE_SCROLL_TAG);
-            fillRemainingScrollValues(mState);
-            if (x != 0) {
-                consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
-                unconsumedX = x - consumedX;
-            }
-            if (y != 0) {
-                consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
-                unconsumedY = y - consumedY;
-            }
-            TraceCompat.endSection();
-            repositionShadowingViews();
-            onExitLayoutOrScroll();
-            stopInterceptRequestLayout(false);
-        }
-        if (!mItemDecorations.isEmpty()) {
-            invalidate();
-        }
-
-        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];
-            if (ev != null) {
-                ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
-            }
-            mNestedOffsets[0] += mScrollOffset[0];
-            mNestedOffsets[1] += mScrollOffset[1];
-        } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
-            if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
-                pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
-            }
-            considerReleasingGlowsOnScroll(x, y);
-        }
-        if (consumedX != 0 || consumedY != 0) {
-            dispatchOnScrolled(consumedX, consumedY);
-        }
-        if (!awakenScrollBars()) {
-            invalidate();
-        }
-        return consumedX != 0 || consumedY != 0;
-    }
-
-    /**
-     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
-     * range. This value is used to compute the length of the thumb within the scrollbar's track.
-     * </p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the units used by
-     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
-     *
-     * <p>Default implementation returns 0.</p>
-     *
-     * <p>If you want to support scroll bars, override
-     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
-     * LayoutManager. </p>
-     *
-     * @return The horizontal offset of the scrollbar's thumb
-     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
-     * (RecyclerView.State)
-     */
-    @Override
-    public int computeHorizontalScrollOffset() {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
-    }
-
-    /**
-     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
-     * horizontal range. This value is used to compute the length of the thumb within the
-     * scrollbar's track.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the units used by
-     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
-     *
-     * <p>Default implementation returns 0.</p>
-     *
-     * <p>If you want to support scroll bars, override
-     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
-     * LayoutManager.</p>
-     *
-     * @return The horizontal extent of the scrollbar's thumb
-     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
-     */
-    @Override
-    public int computeHorizontalScrollExtent() {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
-    }
-
-    /**
-     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the units used by
-     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
-     *
-     * <p>Default implementation returns 0.</p>
-     *
-     * <p>If you want to support scroll bars, override
-     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
-     * LayoutManager.</p>
-     *
-     * @return The total horizontal range represented by the vertical scrollbar
-     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
-     */
-    @Override
-    public int computeHorizontalScrollRange() {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
-    }
-
-    /**
-     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
-     * This value is used to compute the length of the thumb within the scrollbar's track. </p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the units used by
-     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
-     *
-     * <p>Default implementation returns 0.</p>
-     *
-     * <p>If you want to support scroll bars, override
-     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
-     * LayoutManager.</p>
-     *
-     * @return The vertical offset of the scrollbar's thumb
-     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
-     * (RecyclerView.State)
-     */
-    @Override
-    public int computeVerticalScrollOffset() {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
-    }
-
-    /**
-     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
-     * This value is used to compute the length of the thumb within the scrollbar's track.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the units used by
-     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
-     *
-     * <p>Default implementation returns 0.</p>
-     *
-     * <p>If you want to support scroll bars, override
-     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
-     * LayoutManager.</p>
-     *
-     * @return The vertical extent of the scrollbar's thumb
-     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
-     */
-    @Override
-    public int computeVerticalScrollExtent() {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
-    }
-
-    /**
-     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
-     *
-     * <p>The range is expressed in arbitrary units that must be the same as the units used by
-     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
-     *
-     * <p>Default implementation returns 0.</p>
-     *
-     * <p>If you want to support scroll bars, override
-     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
-     * LayoutManager.</p>
-     *
-     * @return The total vertical range represented by the vertical scrollbar
-     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
-     */
-    @Override
-    public int computeVerticalScrollRange() {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
-    }
-
-    /**
-     * This method should be called before any code that may trigger a child view to cause a call to
-     * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
-     * reacting to additional redundant calls to {@link #requestLayout()}.
-     * <p>
-     * A call to this method must always be accompanied by a call to
-     * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
-     * child View to cause a call to {@link RecyclerView#requestLayout()}.
-     *
-     * @see #stopInterceptRequestLayout(boolean)
-     */
-    void startInterceptRequestLayout() {
-        mInterceptRequestLayoutDepth++;
-        if (mInterceptRequestLayoutDepth == 1 && !mLayoutFrozen) {
-            mLayoutWasDefered = false;
-        }
-    }
-
-    /**
-     * This method should be called after any code that may trigger a child view to cause a call to
-     * {@link RecyclerView#requestLayout()}.
-     * <p>
-     * A call to this method must always be accompanied by a call to
-     * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
-     * View to cause a call to {@link RecyclerView#requestLayout()}.
-     *
-     * @see #startInterceptRequestLayout()
-     */
-    void stopInterceptRequestLayout(boolean performLayoutChildren) {
-        if (mInterceptRequestLayoutDepth < 1) {
-            //noinspection PointlessBooleanExpression
-            if (DEBUG) {
-                throw new IllegalStateException("stopInterceptRequestLayout was called more "
-                        + "times than startInterceptRequestLayout."
-                        + exceptionLabel());
-            }
-            mInterceptRequestLayoutDepth = 1;
-        }
-        if (!performLayoutChildren && !mLayoutFrozen) {
-            // Reset the layout request eaten counter.
-            // This is necessary since eatRequest calls can be nested in which case the other
-            // call will override the inner one.
-            // for instance:
-            // eat layout for process adapter updates
-            //   eat layout for dispatchLayout
-            //     a bunch of req layout calls arrive
-
-            mLayoutWasDefered = false;
-        }
-        if (mInterceptRequestLayoutDepth == 1) {
-            // when layout is frozen we should delay dispatchLayout()
-            if (performLayoutChildren && mLayoutWasDefered && !mLayoutFrozen
-                    && mLayout != null && mAdapter != null) {
-                dispatchLayout();
-            }
-            if (!mLayoutFrozen) {
-                mLayoutWasDefered = false;
-            }
-        }
-        mInterceptRequestLayoutDepth--;
-    }
-
-    /**
-     * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
-     * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
-     * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
-     * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
-     * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
-     * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
-     * called.
-     *
-     * <p>
-     * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
-     * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
-     * RecyclerView, State, int)}.
-     * <p>
-     * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
-     * stop frozen.
-     * <p>
-     * Note: Running ItemAnimator is not stopped automatically,  it's caller's
-     * responsibility to call ItemAnimator.end().
-     *
-     * @param frozen   true to freeze layout and scroll, false to re-enable.
-     */
-    public void setLayoutFrozen(boolean frozen) {
-        if (frozen != mLayoutFrozen) {
-            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
-            if (!frozen) {
-                mLayoutFrozen = false;
-                if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
-                    requestLayout();
-                }
-                mLayoutWasDefered = false;
-            } else {
-                final long now = SystemClock.uptimeMillis();
-                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
-                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-                onTouchEvent(cancelEvent);
-                mLayoutFrozen = true;
-                mIgnoreMotionEventTillDown = true;
-                stopScroll();
-            }
-        }
-    }
-
-    /**
-     * Returns true if layout and scroll are frozen.
-     *
-     * @return true if layout and scroll are frozen
-     * @see #setLayoutFrozen(boolean)
-     */
-    public boolean isLayoutFrozen() {
-        return mLayoutFrozen;
-    }
-
-    /**
-     * Animate a scroll by the given amount of pixels along either axis.
-     *
-     * @param dx Pixels to scroll horizontally
-     * @param dy Pixels to scroll vertically
-     */
-    public void smoothScrollBy(int dx, int dy) {
-        smoothScrollBy(dx, dy, null);
-    }
-
-    /**
-     * Animate a scroll by the given amount of pixels along either axis.
-     *
-     * @param dx Pixels to scroll horizontally
-     * @param dy Pixels to scroll vertically
-     * @param interpolator {@link Interpolator} to be used for scrolling. If it is
-     *                     {@code null}, RecyclerView is going to use the default interpolator.
-     */
-    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.");
-            return;
-        }
-        if (mLayoutFrozen) {
-            return;
-        }
-        if (!mLayout.canScrollHorizontally()) {
-            dx = 0;
-        }
-        if (!mLayout.canScrollVertically()) {
-            dy = 0;
-        }
-        if (dx != 0 || dy != 0) {
-            mViewFlinger.smoothScrollBy(dx, dy, interpolator);
-        }
-    }
-
-    /**
-     * Begin a standard fling with an initial velocity along each axis in pixels per second.
-     * If the velocity given is below the system-defined minimum this method will return false
-     * and no fling will occur.
-     *
-     * @param velocityX Initial horizontal velocity in pixels per second
-     * @param velocityY Initial vertical velocity in pixels per second
-     * @return true if the fling was started, false if the velocity was too low to fling or
-     * LayoutManager does not support scrolling in the axis fling is issued.
-     *
-     * @see LayoutManager#canScrollVertically()
-     * @see LayoutManager#canScrollHorizontally()
-     */
-    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.");
-            return false;
-        }
-        if (mLayoutFrozen) {
-            return false;
-        }
-
-        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
-        final boolean canScrollVertical = mLayout.canScrollVertically();
-
-        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
-            velocityX = 0;
-        }
-        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
-            velocityY = 0;
-        }
-        if (velocityX == 0 && velocityY == 0) {
-            // If we don't have any velocity, return false
-            return false;
-        }
-
-        if (!dispatchNestedPreFling(velocityX, velocityY)) {
-            final boolean canScroll = canScrollHorizontal || canScrollVertical;
-            dispatchNestedFling(velocityX, velocityY, canScroll);
-
-            if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
-                return true;
-            }
-
-            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);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Stop any current scroll in progress, such as one started by
-     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
-     */
-    public void stopScroll() {
-        setScrollState(SCROLL_STATE_IDLE);
-        stopScrollersInternal();
-    }
-
-    /**
-     * Similar to {@link #stopScroll()} but does not set the state.
-     */
-    private void stopScrollersInternal() {
-        mViewFlinger.stop();
-        if (mLayout != null) {
-            mLayout.stopSmoothScroller();
-        }
-    }
-
-    /**
-     * Returns the minimum velocity to start a fling.
-     *
-     * @return The minimum velocity to start a fling
-     */
-    public int getMinFlingVelocity() {
-        return mMinFlingVelocity;
-    }
-
-
-    /**
-     * Returns the maximum fling velocity used by this RecyclerView.
-     *
-     * @return The maximum fling velocity used by this RecyclerView.
-     */
-    public int getMaxFlingVelocity() {
-        return mMaxFlingVelocity;
-    }
-
-    /**
-     * Apply a pull to relevant overscroll glow effects
-     */
-    private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
-        boolean invalidate = false;
-        if (overscrollX < 0) {
-            ensureLeftGlow();
-            EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
-            invalidate = true;
-        } else if (overscrollX > 0) {
-            ensureRightGlow();
-            EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
-            invalidate = true;
-        }
-
-        if (overscrollY < 0) {
-            ensureTopGlow();
-            EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
-            invalidate = true;
-        } else if (overscrollY > 0) {
-            ensureBottomGlow();
-            EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
-            invalidate = true;
-        }
-
-        if (invalidate || overscrollX != 0 || overscrollY != 0) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    private void releaseGlows() {
-        boolean needsInvalidate = false;
-        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);
-        }
-    }
-
-    void considerReleasingGlowsOnScroll(int dx, int dy) {
-        boolean needsInvalidate = false;
-        if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
-            mLeftGlow.onRelease();
-            needsInvalidate = mLeftGlow.isFinished();
-        }
-        if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
-            mRightGlow.onRelease();
-            needsInvalidate |= mRightGlow.isFinished();
-        }
-        if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
-            mTopGlow.onRelease();
-            needsInvalidate |= mTopGlow.isFinished();
-        }
-        if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
-            mBottomGlow.onRelease();
-            needsInvalidate |= mBottomGlow.isFinished();
-        }
-        if (needsInvalidate) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    void absorbGlows(int velocityX, int velocityY) {
-        if (velocityX < 0) {
-            ensureLeftGlow();
-            mLeftGlow.onAbsorb(-velocityX);
-        } else if (velocityX > 0) {
-            ensureRightGlow();
-            mRightGlow.onAbsorb(velocityX);
-        }
-
-        if (velocityY < 0) {
-            ensureTopGlow();
-            mTopGlow.onAbsorb(-velocityY);
-        } else if (velocityY > 0) {
-            ensureBottomGlow();
-            mBottomGlow.onAbsorb(velocityY);
-        }
-
-        if (velocityX != 0 || velocityY != 0) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    void ensureLeftGlow() {
-        if (mLeftGlow != null) {
-            return;
-        }
-        mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
-        if (mClipToPadding) {
-            mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
-                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
-        } else {
-            mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
-        }
-    }
-
-    void ensureRightGlow() {
-        if (mRightGlow != null) {
-            return;
-        }
-        mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
-        if (mClipToPadding) {
-            mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
-                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
-        } else {
-            mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
-        }
-    }
-
-    void ensureTopGlow() {
-        if (mTopGlow != null) {
-            return;
-        }
-        mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
-        if (mClipToPadding) {
-            mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
-                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
-        } else {
-            mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
-        }
-
-    }
-
-    void ensureBottomGlow() {
-        if (mBottomGlow != null) {
-            return;
-        }
-        mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
-        if (mClipToPadding) {
-            mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
-                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
-        } else {
-            mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
-        }
-    }
-
-    void invalidateGlows() {
-        mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
-    }
-
-    /**
-     * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
-     * <p>
-     * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
-     * and new effects are created as needed using
-     * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
-     *
-     * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
-     */
-    public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
-        Preconditions.checkNotNull(edgeEffectFactory);
-        mEdgeEffectFactory = edgeEffectFactory;
-        invalidateGlows();
-    }
-
-    /**
-     * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
-     * was set.
-     *
-     * @return The previously set {@link EdgeEffectFactory}
-     * @see #setEdgeEffectFactory(EdgeEffectFactory)
-     */
-    public EdgeEffectFactory getEdgeEffectFactory() {
-        return mEdgeEffectFactory;
-    }
-
-    /**
-     * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
-     * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
-     * that differs from other ViewGroups.
-     * <p>
-     * It first does a focus search within the RecyclerView. If this search finds a View that is in
-     * the focus direction with respect to the currently focused View, RecyclerView returns that
-     * child as the next focus target. When it cannot find such child, it calls
-     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
-     * in the focus search direction. If LayoutManager adds a View that matches the
-     * focus search criteria, it will be returned as the focus search result. Otherwise,
-     * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
-     * <p>
-     * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
-     * is not in the focus direction is still valid focus target which may not be the desired
-     * behavior if the Adapter has more children in the focus direction. To handle this case,
-     * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
-     * focus search in that direction. If there are no Views to gain focus, it will call
-     * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
-     * focus search with the original (relative) direction. This allows RecyclerView to provide
-     * better candidates to the focus search while still allowing the view system to take focus from
-     * the RecyclerView and give it to a more suitable child if such child exists.
-     *
-     * @param focused The view that currently has focus
-     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-     * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
-     * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
-     *
-     * @return A new View that can be the next focus after the focused View
-     */
-    @Override
-    public View focusSearch(View focused, int direction) {
-        View result = mLayout.onInterceptFocusSearch(focused, direction);
-        if (result != null) {
-            return result;
-        }
-        final boolean canRunFocusFailure = mAdapter != null && mLayout != null
-                && !isComputingLayout() && !mLayoutFrozen;
-
-        final FocusFinder ff = FocusFinder.getInstance();
-        if (canRunFocusFailure
-                && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
-            // convert direction to absolute direction and see if we have a view there and if not
-            // tell LayoutManager to add if it can.
-            boolean needsFocusFailureLayout = false;
-            if (mLayout.canScrollVertically()) {
-                final int absDir =
-                        direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
-                final View found = ff.findNextFocus(this, focused, absDir);
-                needsFocusFailureLayout = found == null;
-                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
-                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
-                    direction = absDir;
-                }
-            }
-            if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
-                boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
-                final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
-                        ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
-                final View found = ff.findNextFocus(this, focused, absDir);
-                needsFocusFailureLayout = found == null;
-                if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
-                    // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
-                    direction = absDir;
-                }
-            }
-            if (needsFocusFailureLayout) {
-                consumePendingUpdateOperations();
-                final View focusedItemView = findContainingItemView(focused);
-                if (focusedItemView == null) {
-                    // panic, focused view is not a child anymore, cannot call super.
-                    return null;
-                }
-                startInterceptRequestLayout();
-                mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
-                stopInterceptRequestLayout(false);
-            }
-            result = ff.findNextFocus(this, focused, direction);
-        } else {
-            result = ff.findNextFocus(this, focused, direction);
-            if (result == null && canRunFocusFailure) {
-                consumePendingUpdateOperations();
-                final View focusedItemView = findContainingItemView(focused);
-                if (focusedItemView == null) {
-                    // panic, focused view is not a child anymore, cannot call super.
-                    return null;
-                }
-                startInterceptRequestLayout();
-                result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
-                stopInterceptRequestLayout(false);
-            }
-        }
-        if (result != null && !result.hasFocusable()) {
-            if (getFocusedChild() == null) {
-                // Scrolling to this unfocusable view is not meaningful since there is no currently
-                // focused view which RV needs to keep visible.
-                return super.focusSearch(focused, direction);
-            }
-            // If the next view returned by onFocusSearchFailed in layout manager has no focusable
-            // views, we still scroll to that view in order to make it visible on the screen.
-            // If it's focusable, framework already calls RV's requestChildFocus which handles
-            // bringing this newly focused item onto the screen.
-            requestChildOnScreen(result, null);
-            return focused;
-        }
-        return isPreferredNextFocus(focused, result, direction)
-                ? result : super.focusSearch(focused, direction);
-    }
-
-    /**
-     * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
-     * assign it as the next focus View instead of letting view hierarchy decide.
-     * A good candidate means a View that is aligned in the focus direction wrt the focused View
-     * and is not the RecyclerView itself.
-     * When this method returns false, RecyclerView will let the parent make the decision so the
-     * same View may still get the focus as a result of that search.
-     */
-    private boolean isPreferredNextFocus(View focused, View next, int direction) {
-        if (next == null || next == this) {
-            return false;
-        }
-        // panic, result view is not a child anymore, maybe workaround b/37864393
-        if (findContainingItemView(next) == null) {
-            return false;
-        }
-        if (focused == null) {
-            return true;
-        }
-        // panic, focused view is not a child anymore, maybe workaround b/37864393
-        if (findContainingItemView(focused) == null) {
-            return true;
-        }
-
-        mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
-        mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
-        offsetDescendantRectToMyCoords(focused, mTempRect);
-        offsetDescendantRectToMyCoords(next, mTempRect2);
-        final int rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL ? -1 : 1;
-        int rightness = 0;
-        if ((mTempRect.left < mTempRect2.left
-                || mTempRect.right <= mTempRect2.left)
-                && mTempRect.right < mTempRect2.right) {
-            rightness = 1;
-        } else if ((mTempRect.right > mTempRect2.right
-                || mTempRect.left >= mTempRect2.right)
-                && mTempRect.left > mTempRect2.left) {
-            rightness = -1;
-        }
-        int downness = 0;
-        if ((mTempRect.top < mTempRect2.top
-                || mTempRect.bottom <= mTempRect2.top)
-                && mTempRect.bottom < mTempRect2.bottom) {
-            downness = 1;
-        } else if ((mTempRect.bottom > mTempRect2.bottom
-                || mTempRect.top >= mTempRect2.bottom)
-                && mTempRect.top > mTempRect2.top) {
-            downness = -1;
-        }
-        switch (direction) {
-            case View.FOCUS_LEFT:
-                return rightness < 0;
-            case View.FOCUS_RIGHT:
-                return rightness > 0;
-            case View.FOCUS_UP:
-                return downness < 0;
-            case View.FOCUS_DOWN:
-                return downness > 0;
-            case View.FOCUS_FORWARD:
-                return downness > 0 || (downness == 0 && rightness * rtl >= 0);
-            case View.FOCUS_BACKWARD:
-                return downness < 0 || (downness == 0 && rightness * rtl <= 0);
-        }
-        throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
-            requestChildOnScreen(child, focused);
-        }
-        super.requestChildFocus(child, focused);
-    }
-
-    /**
-     * Requests that the given child of the RecyclerView be positioned onto the screen. This method
-     * can be called for both unfocusable and focusable child views. For unfocusable child views,
-     * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
-     * indicates the actual descendant view within this child view that holds the focus.
-     * @param child The child view of this RecyclerView that wants to come onto the screen.
-     * @param focused The descendant view that actually has the focus if child is focusable, null
-     *                otherwise.
-     */
-    private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
-        View rectView = (focused != null) ? focused : child;
-        mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
-
-        // get item decor offsets w/o refreshing. If they are invalid, there will be another
-        // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
-        // View in viewport.
-        final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
-        if (focusedLayoutParams instanceof LayoutParams) {
-            // if focused child has item decors, use them. Otherwise, ignore.
-            final LayoutParams lp = (LayoutParams) focusedLayoutParams;
-            if (!lp.mInsetsDirty) {
-                final Rect insets = lp.mDecorInsets;
-                mTempRect.left -= insets.left;
-                mTempRect.right += insets.right;
-                mTempRect.top -= insets.top;
-                mTempRect.bottom += insets.bottom;
-            }
-        }
-
-        if (focused != null) {
-            offsetDescendantRectToMyCoords(focused, mTempRect);
-            offsetRectIntoDescendantCoords(child, mTempRect);
-        }
-        mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
-                (focused == null));
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
-        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
-    }
-
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
-            super.addFocusables(views, direction, focusableMode);
-        }
-    }
-
-    @Override
-    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-        if (isComputingLayout()) {
-            // if we are in the middle of a layout calculation, don't let any child take focus.
-            // RV will handle it after layout calculation is finished.
-            return false;
-        }
-        return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mLayoutOrScrollCounter = 0;
-        mIsAttached = true;
-        mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
-        if (mLayout != null) {
-            mLayout.dispatchAttachedToWindow(this);
-        }
-        mPostedAnimatorRunner = false;
-
-        if (ALLOW_THREAD_GAP_WORK) {
-            // Register with gap worker
-            mGapWorker = GapWorker.sGapWorker.get();
-            if (mGapWorker == null) {
-                mGapWorker = new GapWorker();
-
-                // break 60 fps assumption if data from display appears valid
-                // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
-                Display display = ViewCompat.getDisplay(this);
-                float refreshRate = 60.0f;
-                if (!isInEditMode() && display != null) {
-                    float displayRefreshRate = display.getRefreshRate();
-                    if (displayRefreshRate >= 30.0f) {
-                        refreshRate = displayRefreshRate;
-                    }
-                }
-                mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
-                GapWorker.sGapWorker.set(mGapWorker);
-            }
-            mGapWorker.add(this);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mItemAnimator != null) {
-            mItemAnimator.endAnimations();
-        }
-        stopScroll();
-        mIsAttached = false;
-        if (mLayout != null) {
-            mLayout.dispatchDetachedFromWindow(this, mRecycler);
-        }
-        mPendingAccessibilityImportanceChange.clear();
-        removeCallbacks(mItemAnimatorRunner);
-        mViewInfoStore.onDetach();
-
-        if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
-            // Unregister with gap worker
-            mGapWorker.remove(this);
-            mGapWorker = null;
-        }
-    }
-
-    /**
-     * Returns true if RecyclerView is attached to window.
-     */
-    @Override
-    public boolean isAttachedToWindow() {
-        return mIsAttached;
-    }
-
-    /**
-     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
-     * {@link IllegalStateException} if it <b>is not</b>.
-     *
-     * @param message The message for the exception. Can be null.
-     * @see #assertNotInLayoutOrScroll(String)
-     */
-    void assertInLayoutOrScroll(String message) {
-        if (!isComputingLayout()) {
-            if (message == null) {
-                throw new IllegalStateException("Cannot call this method unless RecyclerView is "
-                        + "computing a layout or scrolling" + exceptionLabel());
-            }
-            throw new IllegalStateException(message + exceptionLabel());
-
-        }
-    }
-
-    /**
-     * Checks if RecyclerView is in the middle of a layout or scroll and throws an
-     * {@link IllegalStateException} if it <b>is</b>.
-     *
-     * @param message The message for the exception. Can be null.
-     * @see #assertInLayoutOrScroll(String)
-     */
-    void assertNotInLayoutOrScroll(String message) {
-        if (isComputingLayout()) {
-            if (message == null) {
-                throw new IllegalStateException("Cannot call this method while RecyclerView is "
-                        + "computing a layout or scrolling" + exceptionLabel());
-            }
-            throw new IllegalStateException(message);
-        }
-        if (mDispatchScrollCounter > 0) {
-            Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
-                            + "be run during a measure & layout pass where you cannot change the"
-                            + "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("" + exceptionLabel()));
-        }
-    }
-
-    /**
-     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
-     * to child views or this view's standard scrolling behavior.
-     *
-     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
-     * returns true from
-     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
-     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
-     * for each incoming MotionEvent until the end of the gesture.</p>
-     *
-     * @param listener Listener to add
-     * @see SimpleOnItemTouchListener
-     */
-    public void addOnItemTouchListener(OnItemTouchListener listener) {
-        mOnItemTouchListeners.add(listener);
-    }
-
-    /**
-     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
-     *
-     * @param listener Listener to remove
-     */
-    public void removeOnItemTouchListener(OnItemTouchListener listener) {
-        mOnItemTouchListeners.remove(listener);
-        if (mActiveOnItemTouchListener == listener) {
-            mActiveOnItemTouchListener = null;
-        }
-    }
-
-    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
-        final int action = e.getAction();
-        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
-            mActiveOnItemTouchListener = null;
-        }
-
-        final int listenerCount = mOnItemTouchListeners.size();
-        for (int i = 0; i < listenerCount; i++) {
-            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
-            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
-                mActiveOnItemTouchListener = listener;
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean dispatchOnItemTouch(MotionEvent e) {
-        final int action = e.getAction();
-        if (mActiveOnItemTouchListener != null) {
-            if (action == MotionEvent.ACTION_DOWN) {
-                // Stale state from a previous gesture, we're starting a new one. Clear it.
-                mActiveOnItemTouchListener = null;
-            } else {
-                mActiveOnItemTouchListener.onTouchEvent(this, e);
-                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
-                    // Clean up for the next gesture.
-                    mActiveOnItemTouchListener = null;
-                }
-                return true;
-            }
-        }
-
-        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
-        // as called from onInterceptTouchEvent; skip it.
-        if (action != MotionEvent.ACTION_DOWN) {
-            final int listenerCount = mOnItemTouchListeners.size();
-            for (int i = 0; i < listenerCount; i++) {
-                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
-                if (listener.onInterceptTouchEvent(this, e)) {
-                    mActiveOnItemTouchListener = listener;
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent e) {
-        if (mLayoutFrozen) {
-            // When layout is frozen,  RV does not intercept the motion event.
-            // A child view e.g. a button may still get the click.
-            return false;
-        }
-        if (dispatchOnItemTouchIntercept(e)) {
-            cancelTouch();
-            return true;
-        }
-
-        if (mLayout == null) {
-            return false;
-        }
-
-        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
-        final boolean canScrollVertically = mLayout.canScrollVertically();
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        mVelocityTracker.addMovement(e);
-
-        final int action = e.getActionMasked();
-        final int actionIndex = e.getActionIndex();
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                if (mIgnoreMotionEventTillDown) {
-                    mIgnoreMotionEventTillDown = false;
-                }
-                mScrollPointerId = e.getPointerId(0);
-                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
-                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
-
-                if (mScrollState == SCROLL_STATE_SETTLING) {
-                    getParent().requestDisallowInterceptTouchEvent(true);
-                    setScrollState(SCROLL_STATE_DRAGGING);
-                }
-
-                // Clear the nested offsets
-                mNestedOffsets[0] = mNestedOffsets[1] = 0;
-
-                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
-                if (canScrollHorizontally) {
-                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
-                }
-                if (canScrollVertically) {
-                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
-                }
-                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
-                break;
-
-            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);
-                break;
-
-            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?");
-                    return false;
-                }
-
-                final int x = (int) (e.getX(index) + 0.5f);
-                final int y = (int) (e.getY(index) + 0.5f);
-                if (mScrollState != SCROLL_STATE_DRAGGING) {
-                    final int dx = x - mInitialTouchX;
-                    final int dy = y - mInitialTouchY;
-                    boolean startScroll = false;
-                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
-                        mLastTouchX = x;
-                        startScroll = true;
-                    }
-                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
-                        mLastTouchY = y;
-                        startScroll = true;
-                    }
-                    if (startScroll) {
-                        setScrollState(SCROLL_STATE_DRAGGING);
-                    }
-                }
-            } break;
-
-            case MotionEvent.ACTION_POINTER_UP: {
-                onPointerUp(e);
-            } break;
-
-            case MotionEvent.ACTION_UP: {
-                mVelocityTracker.clear();
-                stopNestedScroll(TYPE_TOUCH);
-            } break;
-
-            case MotionEvent.ACTION_CANCEL: {
-                cancelTouch();
-            }
-        }
-        return mScrollState == SCROLL_STATE_DRAGGING;
-    }
-
-    @Override
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        final int listenerCount = mOnItemTouchListeners.size();
-        for (int i = 0; i < listenerCount; i++) {
-            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
-            listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
-        }
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent e) {
-        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
-            return false;
-        }
-        if (dispatchOnItemTouch(e)) {
-            cancelTouch();
-            return true;
-        }
-
-        if (mLayout == null) {
-            return false;
-        }
-
-        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
-        final boolean canScrollVertically = mLayout.canScrollVertically();
-
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-        boolean eventAddedToVelocityTracker = false;
-
-        final MotionEvent vtev = MotionEvent.obtain(e);
-        final int action = e.getActionMasked();
-        final int actionIndex = e.getActionIndex();
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mNestedOffsets[0] = mNestedOffsets[1] = 0;
-        }
-        vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                mScrollPointerId = e.getPointerId(0);
-                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
-                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
-
-                int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
-                if (canScrollHorizontally) {
-                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
-                }
-                if (canScrollVertically) {
-                    nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
-                }
-                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
-            } break;
-
-            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);
-            } break;
-
-            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?");
-                    return false;
-                }
-
-                final int x = (int) (e.getX(index) + 0.5f);
-                final int y = (int) (e.getY(index) + 0.5f);
-                int dx = mLastTouchX - x;
-                int dy = mLastTouchY - y;
-
-                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
-                    dx -= mScrollConsumed[0];
-                    dy -= mScrollConsumed[1];
-                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
-                    // Updated the nested offsets
-                    mNestedOffsets[0] += mScrollOffset[0];
-                    mNestedOffsets[1] += mScrollOffset[1];
-                }
-
-                if (mScrollState != SCROLL_STATE_DRAGGING) {
-                    boolean startScroll = false;
-                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
-                        if (dx > 0) {
-                            dx -= mTouchSlop;
-                        } else {
-                            dx += mTouchSlop;
-                        }
-                        startScroll = true;
-                    }
-                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
-                        if (dy > 0) {
-                            dy -= mTouchSlop;
-                        } else {
-                            dy += mTouchSlop;
-                        }
-                        startScroll = true;
-                    }
-                    if (startScroll) {
-                        setScrollState(SCROLL_STATE_DRAGGING);
-                    }
-                }
-
-                if (mScrollState == SCROLL_STATE_DRAGGING) {
-                    mLastTouchX = x - mScrollOffset[0];
-                    mLastTouchY = y - mScrollOffset[1];
-
-                    if (scrollByInternal(
-                            canScrollHorizontally ? dx : 0,
-                            canScrollVertically ? dy : 0,
-                            vtev)) {
-                        getParent().requestDisallowInterceptTouchEvent(true);
-                    }
-                    if (mGapWorker != null && (dx != 0 || dy != 0)) {
-                        mGapWorker.postFromTraversal(this, dx, dy);
-                    }
-                }
-            } break;
-
-            case MotionEvent.ACTION_POINTER_UP: {
-                onPointerUp(e);
-            } break;
-
-            case MotionEvent.ACTION_UP: {
-                mVelocityTracker.addMovement(vtev);
-                eventAddedToVelocityTracker = true;
-                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
-                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);
-                }
-                resetTouch();
-            } break;
-
-            case MotionEvent.ACTION_CANCEL: {
-                cancelTouch();
-            } break;
-        }
-
-        if (!eventAddedToVelocityTracker) {
-            mVelocityTracker.addMovement(vtev);
-        }
-        vtev.recycle();
-
-        return true;
-    }
-
-    private void resetTouch() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.clear();
-        }
-        stopNestedScroll(TYPE_TOUCH);
-        releaseGlows();
-    }
-
-    private void cancelTouch() {
-        resetTouch();
-        setScrollState(SCROLL_STATE_IDLE);
-    }
-
-    private void onPointerUp(MotionEvent 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;
-            mScrollPointerId = e.getPointerId(newIndex);
-            mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
-            mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
-        }
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if (mLayout == null) {
-            return false;
-        }
-        if (mLayoutFrozen) {
-            return false;
-        }
-        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 = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                } else {
-                    vScroll = 0f;
-                }
-                if (mLayout.canScrollHorizontally()) {
-                    hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
-                } else {
-                    hScroll = 0f;
-                }
-            } 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;
-    }
-
-    @Override
-    protected void onMeasure(int widthSpec, int heightSpec) {
-        if (mLayout == null) {
-            defaultOnMeasure(widthSpec, heightSpec);
-            return;
-        }
-        if (mLayout.mAutoMeasure) {
-            final int widthMode = MeasureSpec.getMode(widthSpec);
-            final int heightMode = MeasureSpec.getMode(heightSpec);
-            final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
-                    && heightMode == MeasureSpec.EXACTLY;
-            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
-            if (skipMeasure || mAdapter == null) {
-                return;
-            }
-            if (mState.mLayoutStep == State.STEP_START) {
-                dispatchLayoutStep1();
-            }
-            // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
-            // consistency
-            mLayout.setMeasureSpecs(widthSpec, heightSpec);
-            mState.mIsMeasuring = true;
-            dispatchLayoutStep2();
-
-            // now we can get the width and height from the children.
-            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
-
-            // if RecyclerView has non-exact width and height and if there is at least one child
-            // which also has non-exact width & height, we have to re-measure.
-            if (mLayout.shouldMeasureTwice()) {
-                mLayout.setMeasureSpecs(
-                        MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
-                mState.mIsMeasuring = true;
-                dispatchLayoutStep2();
-                // now we can get the width and height from the children.
-                mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
-            }
-        } else {
-            if (mHasFixedSize) {
-                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
-                return;
-            }
-            // custom onMeasure
-            if (mAdapterUpdateDuringMeasure) {
-                startInterceptRequestLayout();
-                onEnterLayoutOrScroll();
-                processAdapterUpdatesAndSetAnimationFlags();
-                onExitLayoutOrScroll();
-
-                if (mState.mRunPredictiveAnimations) {
-                    mState.mInPreLayout = true;
-                } else {
-                    // consume remaining updates to provide a consistent state with the layout pass.
-                    mAdapterHelper.consumeUpdatesInOnePass();
-                    mState.mInPreLayout = false;
-                }
-                mAdapterUpdateDuringMeasure = false;
-                stopInterceptRequestLayout(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) {
-                mState.mItemCount = mAdapter.getItemCount();
-            } else {
-                mState.mItemCount = 0;
-            }
-            startInterceptRequestLayout();
-            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
-            stopInterceptRequestLayout(false);
-            mState.mInPreLayout = false; // clear
-        }
-    }
-
-    /**
-     * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
-     * where this RecyclerView is otherwise lacking better information.
-     */
-    void defaultOnMeasure(int widthSpec, int heightSpec) {
-        // calling LayoutManager here is not pretty but that API is already public and it is better
-        // than creating another method since this is internal.
-        final int width = LayoutManager.chooseSize(widthSpec,
-                getPaddingLeft() + getPaddingRight(),
-                ViewCompat.getMinimumWidth(this));
-        final int height = LayoutManager.chooseSize(heightSpec,
-                getPaddingTop() + getPaddingBottom(),
-                ViewCompat.getMinimumHeight(this));
-
-        setMeasuredDimension(width, height);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        if (w != oldw || h != oldh) {
-            invalidateGlows();
-            // layout's w/h are updated during measure/layout steps.
-        }
-    }
-
-    /**
-     * Sets the {@link ItemAnimator} that will handle animations involving changes
-     * to the items in this RecyclerView. By default, RecyclerView instantiates and
-     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
-     * enabled for the RecyclerView depends on the ItemAnimator and whether
-     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
-     * supports item animations}.
-     *
-     * @param animator The ItemAnimator being set. If null, no animations will occur
-     * when changes occur to the items in this RecyclerView.
-     */
-    public void setItemAnimator(ItemAnimator animator) {
-        if (mItemAnimator != null) {
-            mItemAnimator.endAnimations();
-            mItemAnimator.setListener(null);
-        }
-        mItemAnimator = animator;
-        if (mItemAnimator != null) {
-            mItemAnimator.setListener(mItemAnimatorListener);
-        }
-    }
-
-    void onEnterLayoutOrScroll() {
-        mLayoutOrScrollCounter++;
-    }
-
-    void onExitLayoutOrScroll() {
-        onExitLayoutOrScroll(true);
-    }
-
-    void onExitLayoutOrScroll(boolean enableChangeEvents) {
-        mLayoutOrScrollCounter--;
-        if (mLayoutOrScrollCounter < 1) {
-            if (DEBUG && mLayoutOrScrollCounter < 0) {
-                throw new IllegalStateException("layout or scroll counter cannot go below zero."
-                        + "Some calls are not matching" + exceptionLabel());
-            }
-            mLayoutOrScrollCounter = 0;
-            if (enableChangeEvents) {
-                dispatchContentChangedIfNecessary();
-                dispatchPendingImportantForAccessibilityChanges();
-            }
-        }
-    }
-
-    boolean isAccessibilityEnabled() {
-        return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
-    }
-
-    private void dispatchContentChangedIfNecessary() {
-        final int flags = mEatenAccessibilityChangeFlags;
-        mEatenAccessibilityChangeFlags = 0;
-        if (flags != 0 && isAccessibilityEnabled()) {
-            final AccessibilityEvent event = AccessibilityEvent.obtain();
-            event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
-            AccessibilityEventCompat.setContentChangeTypes(event, flags);
-            sendAccessibilityEventUnchecked(event);
-        }
-    }
-
-    /**
-     * Returns whether RecyclerView is currently computing a layout.
-     * <p>
-     * If this method returns true, it means that RecyclerView is in a lockdown state and any
-     * attempt to update adapter contents will result in an exception because adapter contents
-     * cannot be changed while RecyclerView is trying to compute the layout.
-     * <p>
-     * It is very unlikely that your code will be running during this state as it is
-     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
-     * in response to system events (touch, accessibility etc).
-     * <p>
-     * This case may happen if you have some custom logic to change adapter contents in
-     * response to a View callback (e.g. focus change callback) which might be triggered during a
-     * layout calculation. In these cases, you should just postpone the change using a Handler or a
-     * similar mechanism.
-     *
-     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
-     *         otherwise
-     */
-    public boolean isComputingLayout() {
-        return mLayoutOrScrollCounter > 0;
-    }
-
-    /**
-     * Returns true if an accessibility event should not be dispatched now. This happens when an
-     * accessibility request arrives while RecyclerView does not have a stable state which is very
-     * hard to handle for a LayoutManager. Instead, this method records necessary information about
-     * the event and dispatches a window change event after the critical section is finished.
-     *
-     * @return True if the accessibility event should be postponed.
-     */
-    boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
-        if (isComputingLayout()) {
-            int type = 0;
-            if (event != null) {
-                type = AccessibilityEventCompat.getContentChangeTypes(event);
-            }
-            if (type == 0) {
-                type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
-            }
-            mEatenAccessibilityChangeFlags |= type;
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
-        if (shouldDeferAccessibilityEvent(event)) {
-            return;
-        }
-        super.sendAccessibilityEventUnchecked(event);
-    }
-
-    /**
-     * Gets the current ItemAnimator for this RecyclerView. A null return value
-     * indicates that there is no animator and that item changes will happen without
-     * any animations. By default, RecyclerView instantiates and
-     * uses an instance of {@link DefaultItemAnimator}.
-     *
-     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
-     * when changes occur to the items in this RecyclerView.
-     */
-    public ItemAnimator getItemAnimator() {
-        return mItemAnimator;
-    }
-
-    /**
-     * Post a runnable to the next frame to run pending item animations. Only the first such
-     * request will be posted, governed by the mPostedAnimatorRunner flag.
-     */
-    void postAnimationRunner() {
-        if (!mPostedAnimatorRunner && mIsAttached) {
-            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
-            mPostedAnimatorRunner = true;
-        }
-    }
-
-    private boolean predictiveItemAnimationsEnabled() {
-        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
-    }
-
-    /**
-     * Consumes adapter updates and calculates which type of animations we want to run.
-     * Called in onMeasure and dispatchLayout.
-     * <p>
-     * This method may process only the pre-layout state of updates or all of them.
-     */
-    private void processAdapterUpdatesAndSetAnimationFlags() {
-        if (mDataSetHasChangedAfterLayout) {
-            // Processing these items have no value since data set changed unexpectedly.
-            // Instead, we just reset it.
-            mAdapterHelper.reset();
-            if (mDispatchItemsChangedEvent) {
-                mLayout.onItemsChanged(this);
-            }
-        }
-        // simple animations are a subset of advanced animations (which will cause a
-        // pre-layout step)
-        // If layout supports predictive animations, pre-process to decide if we want to run them
-        if (predictiveItemAnimationsEnabled()) {
-            mAdapterHelper.preProcess();
-        } else {
-            mAdapterHelper.consumeUpdatesInOnePass();
-        }
-        boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
-        mState.mRunSimpleAnimations = mFirstLayoutComplete
-                && mItemAnimator != null
-                && (mDataSetHasChangedAfterLayout
-                || animationTypeSupported
-                || mLayout.mRequestedSimpleAnimations)
-                && (!mDataSetHasChangedAfterLayout
-                || mAdapter.hasStableIds());
-        mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
-                && animationTypeSupported
-                && !mDataSetHasChangedAfterLayout
-                && predictiveItemAnimationsEnabled();
-    }
-
-    /**
-     * Wrapper around layoutChildren() that handles animating changes caused by layout.
-     * Animations work on the assumption that there are five different kinds of items
-     * in play:
-     * PERSISTENT: items are visible before and after layout
-     * REMOVED: items were visible before layout and were removed by the app
-     * ADDED: items did not exist before layout and were added by the app
-     * DISAPPEARING: items exist in the data set before/after, but changed from
-     * visible to non-visible in the process of layout (they were moved off
-     * screen as a side-effect of other changes)
-     * APPEARING: items exist in the data set before/after, but changed from
-     * non-visible to visible in the process of layout (they were moved on
-     * screen as a side-effect of other changes)
-     * The overall approach figures out what items exist before/after layout and
-     * infers one of the five above states for each of the items. Then the animations
-     * are set up accordingly:
-     * PERSISTENT views are animated via
-     * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
-     * DISAPPEARING views are animated via
-     * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
-     * APPEARING views are animated via
-     * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
-     * and changed views are animated via
-     * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
-     */
-    void dispatchLayout() {
-        if (mAdapter == null) {
-            Log.e(TAG, "No adapter attached; skipping layout");
-            // leave the state in START
-            return;
-        }
-        if (mLayout == null) {
-            Log.e(TAG, "No layout manager attached; skipping layout");
-            // leave the state in START
-            return;
-        }
-        mState.mIsMeasuring = false;
-        if (mState.mLayoutStep == State.STEP_START) {
-            dispatchLayoutStep1();
-            mLayout.setExactMeasureSpecsFrom(this);
-            dispatchLayoutStep2();
-        } 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);
-            dispatchLayoutStep2();
-        } else {
-            // always make sure we sync them (to ensure mode is exact)
-            mLayout.setExactMeasureSpecsFrom(this);
-        }
-        dispatchLayoutStep3();
-    }
-
-    private void saveFocusInfo() {
-        View child = null;
-        if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
-            child = getFocusedChild();
-        }
-
-        final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
-        if (focusedVh == null) {
-            resetFocusInfo();
-        } else {
-            mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
-            // 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
-                            : focusedVh.getAdapterPosition());
-            mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
-        }
-    }
-
-    private void resetFocusInfo() {
-        mState.mFocusedItemId = NO_ID;
-        mState.mFocusedItemPosition = NO_POSITION;
-        mState.mFocusedSubChildId = View.NO_ID;
-    }
-
-    /**
-     * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
-     * previously focused item. It first traverses the adapter forward to find a focusable candidate
-     * and if no such candidate is found, it reverses the focus search direction for the items
-     * before the mFocusedItemPosition'th index;
-     * @return The best candidate to request focus on, or null if no such candidate exists. Null
-     * indicates all the existing adapter items are unfocusable.
-     */
-    @Nullable
-    private View findNextViewToFocus() {
-        int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
-                : 0;
-        ViewHolder nextFocus;
-        final int itemCount = mState.getItemCount();
-        for (int i = startFocusSearchIndex; i < itemCount; i++) {
-            nextFocus = findViewHolderForAdapterPosition(i);
-            if (nextFocus == null) {
-                break;
-            }
-            if (nextFocus.itemView.hasFocusable()) {
-                return nextFocus.itemView;
-            }
-        }
-        final int limit = Math.min(itemCount, startFocusSearchIndex);
-        for (int i = limit - 1; i >= 0; i--) {
-            nextFocus = findViewHolderForAdapterPosition(i);
-            if (nextFocus == null) {
-                return null;
-            }
-            if (nextFocus.itemView.hasFocusable()) {
-                return nextFocus.itemView;
-            }
-        }
-        return null;
-    }
-
-    private void recoverFocusFromState() {
-        if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
-                || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
-                || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
-            // No-op if either of these cases happens:
-            // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
-            // before its children and is focused (i.e. it already stole the focus away from its
-            // descendants).
-            return;
-        }
-        // only recover focus if RV itself has the focus or the focused view is hidden
-        if (!isFocused()) {
-            final View focusedChild = getFocusedChild();
-            if (IGNORE_DETACHED_FOCUSED_CHILD
-                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
-                // Special handling of API 15-. A focused child can be invalid because mFocus is not
-                // cleared when the child is detached (mParent = null),
-                // This happens because clearFocus on API 15- does not invalidate mFocus of its
-                // parent when this child is detached.
-                // For API 16+, this is not an issue because requestFocus takes care of clearing the
-                // prior detached focused child. For API 15- the problem happens in 2 cases because
-                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
-                // for the current focused item which calls clearChild or 2. when the prior focused
-                // child is removed, removeDetachedView called in layout step 3 which calls
-                // clearChild. We should ignore this invalid focused child in all our calculations
-                // for the next view to receive focus, and apply the focus recovery logic instead.
-                if (mChildHelper.getChildCount() == 0) {
-                    // No children left. Request focus on the RV itself since one of its children
-                    // was holding focus previously.
-                    requestFocus();
-                    return;
-                }
-            } else if (!mChildHelper.isHidden(focusedChild)) {
-                // If the currently focused child is hidden, apply the focus recovery logic.
-                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
-                return;
-            }
-        }
-        ViewHolder focusTarget = null;
-        // RV first attempts to locate the previously focused item to request focus on using
-        // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
-        // find the next best candidate to request focus on based on mFocusedItemPosition.
-        if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
-            focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
-        }
-        View viewToFocus = null;
-        if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
-                || !focusTarget.itemView.hasFocusable()) {
-            if (mChildHelper.getChildCount() > 0) {
-                // At this point, RV has focus and either of these conditions are true:
-                // 1. There's no previously focused item either because RV received focused before
-                // layout, or the previously focused item was removed, or RV doesn't have stable IDs
-                // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
-                // focusable. In either of these cases, we make sure that RV still passes down the
-                // focus to one of its focusable children using a best-effort algorithm.
-                viewToFocus = findNextViewToFocus();
-            }
-        } else {
-            // looks like the focused item has been replaced with another view that represents the
-            // same item in the adapter. Request focus on that.
-            viewToFocus = focusTarget.itemView;
-        }
-
-        if (viewToFocus != null) {
-            if (mState.mFocusedSubChildId != NO_ID) {
-                View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
-                if (child != null && child.isFocusable()) {
-                    viewToFocus = child;
-                }
-            }
-            viewToFocus.requestFocus();
-        }
-    }
-
-    private int getDeepestFocusedViewWithId(View view) {
-        int lastKnownId = view.getId();
-        while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
-            view = ((ViewGroup) view).getFocusedChild();
-            final int id = view.getId();
-            if (id != View.NO_ID) {
-                lastKnownId = view.getId();
-            }
-        }
-        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
-     * - decide which animation should run
-     * - save information about current views
-     * - If necessary, run predictive layout and save its information
-     */
-    private void dispatchLayoutStep1() {
-        mState.assertLayoutStep(State.STEP_START);
-        fillRemainingScrollValues(mState);
-        mState.mIsMeasuring = false;
-        startInterceptRequestLayout();
-        mViewInfoStore.clear();
-        onEnterLayoutOrScroll();
-        processAdapterUpdatesAndSetAnimationFlags();
-        saveFocusInfo();
-        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
-        mItemsAddedOrRemoved = mItemsChanged = false;
-        mState.mInPreLayout = mState.mRunPredictiveAnimations;
-        mState.mItemCount = mAdapter.getItemCount();
-        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
-
-        if (mState.mRunSimpleAnimations) {
-            // Step 0: Find out where all non-removed items are, pre-layout
-            int count = mChildHelper.getChildCount();
-            for (int i = 0; i < count; ++i) {
-                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
-                if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
-                    continue;
-                }
-                final ItemHolderInfo animationInfo = mItemAnimator
-                        .recordPreLayoutInformation(mState, holder,
-                                ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
-                                holder.getUnmodifiedPayloads());
-                mViewInfoStore.addToPreLayout(holder, animationInfo);
-                if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
-                        && !holder.shouldIgnore() && !holder.isInvalid()) {
-                    long key = getChangedHolderKey(holder);
-                    // This is NOT the only place where a ViewHolder is added to old change holders
-                    // list. There is another case where:
-                    //    * A VH is currently hidden but not deleted
-                    //    * The hidden item is changed in the adapter
-                    //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
-                    // When this case is detected, RV will un-hide that view and add to the old
-                    // change holders list.
-                    mViewInfoStore.addToOldChangeHolders(key, holder);
-                }
-            }
-        }
-        if (mState.mRunPredictiveAnimations) {
-            // Step 1: run prelayout: This will use the old positions of items. The layout manager
-            // is expected to layout everything, even removed items (though not to add removed
-            // items back to the container). This gives the pre-layout position of APPEARING views
-            // which come into existence as part of the real layout.
-
-            // Save old positions so that LayoutManager can run its mapping logic.
-            saveOldPositions();
-            final boolean didStructureChange = mState.mStructureChanged;
-            mState.mStructureChanged = false;
-            // temporarily disable flag because we are asking for previous layout
-            mLayout.onLayoutChildren(mRecycler, mState);
-            mState.mStructureChanged = didStructureChange;
-
-            for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
-                final View child = mChildHelper.getChildAt(i);
-                final ViewHolder viewHolder = getChildViewHolderInt(child);
-                if (viewHolder.shouldIgnore()) {
-                    continue;
-                }
-                if (!mViewInfoStore.isInPreLayout(viewHolder)) {
-                    int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
-                    boolean wasHidden = viewHolder
-                            .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
-                    if (!wasHidden) {
-                        flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
-                    }
-                    final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
-                            mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
-                    if (wasHidden) {
-                        recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
-                    } else {
-                        mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
-                    }
-                }
-            }
-            // we don't process disappearing list because they may re-appear in post layout pass.
-            clearOldPositions();
-        } else {
-            clearOldPositions();
-        }
-        onExitLayoutOrScroll();
-        stopInterceptRequestLayout(false);
-        mState.mLayoutStep = State.STEP_LAYOUT;
-    }
-
-    /**
-     * The second layout step where we do the actual layout of the views for the final state.
-     * This step might be run multiple times if necessary (e.g. measure).
-     */
-    private void dispatchLayoutStep2() {
-        startInterceptRequestLayout();
-        onEnterLayoutOrScroll();
-        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
-        mAdapterHelper.consumeUpdatesInOnePass();
-        mState.mItemCount = mAdapter.getItemCount();
-        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
-
-        // Step 2: Run layout
-        mState.mInPreLayout = false;
-        mLayout.onLayoutChildren(mRecycler, mState);
-
-        mState.mStructureChanged = false;
-        mPendingSavedState = null;
-
-        // onLayoutChildren may have caused client code to disable item animations; re-check
-        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
-        mState.mLayoutStep = State.STEP_ANIMATIONS;
-        onExitLayoutOrScroll();
-        stopInterceptRequestLayout(false);
-    }
-
-    /**
-     * The final step of the layout where we save the information about views for animations,
-     * trigger animations and do any necessary cleanup.
-     */
-    private void dispatchLayoutStep3() {
-        mState.assertLayoutStep(State.STEP_ANIMATIONS);
-        startInterceptRequestLayout();
-        onEnterLayoutOrScroll();
-        mState.mLayoutStep = State.STEP_START;
-        if (mState.mRunSimpleAnimations) {
-            // Step 3: Find out where things are now, and process change animations.
-            // traverse list in reverse because we may call animateChange in the loop which may
-            // remove the target view holder.
-            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
-                ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
-                if (holder.shouldIgnore()) {
-                    continue;
-                }
-                long key = getChangedHolderKey(holder);
-                final ItemHolderInfo animationInfo = mItemAnimator
-                        .recordPostLayoutInformation(mState, holder);
-                ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
-                if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
-                    // run a change animation
-
-                    // If an Item is CHANGED but the updated version is disappearing, it creates
-                    // a conflicting case.
-                    // Since a view that is marked as disappearing is likely to be going out of
-                    // bounds, we run a change animation. Both views will be cleaned automatically
-                    // once their animations finish.
-                    // On the other hand, if it is the same view holder instance, we run a
-                    // disappearing animation instead because we are not going to rebind the updated
-                    // VH unless it is enforced by the layout manager.
-                    final boolean oldDisappearing = mViewInfoStore.isDisappearing(
-                            oldChangeViewHolder);
-                    final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
-                    if (oldDisappearing && oldChangeViewHolder == holder) {
-                        // run disappear animation instead of change
-                        mViewInfoStore.addToPostLayout(holder, animationInfo);
-                    } else {
-                        final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
-                                oldChangeViewHolder);
-                        // we add and remove so that any post info is merged.
-                        mViewInfoStore.addToPostLayout(holder, animationInfo);
-                        ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
-                        if (preInfo == null) {
-                            handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
-                        } else {
-                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
-                                    oldDisappearing, newDisappearing);
-                        }
-                    }
-                } else {
-                    mViewInfoStore.addToPostLayout(holder, animationInfo);
-                }
-            }
-
-            // Step 4: Process view info lists and trigger animations
-            mViewInfoStore.process(mViewInfoProcessCallback);
-        }
-
-        mLayout.removeAndRecycleScrapInt(mRecycler);
-        mState.mPreviousLayoutItemCount = mState.mItemCount;
-        mDataSetHasChangedAfterLayout = false;
-        mDispatchItemsChangedEvent = false;
-        mState.mRunSimpleAnimations = false;
-
-        mState.mRunPredictiveAnimations = false;
-        mLayout.mRequestedSimpleAnimations = false;
-        if (mRecycler.mChangedScrap != null) {
-            mRecycler.mChangedScrap.clear();
-        }
-        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
-            // Initial prefetch has expanded cache, so reset until next prefetch.
-            // This prevents initial prefetches from expanding the cache permanently.
-            mLayout.mPrefetchMaxCountObserved = 0;
-            mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
-            mRecycler.updateViewCacheSize();
-        }
-
-        mLayout.onLayoutCompleted(mState);
-        onExitLayoutOrScroll();
-        stopInterceptRequestLayout(false);
-        mViewInfoStore.clear();
-        if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
-            dispatchOnScrolled(0, 0);
-        }
-        recoverFocusFromState();
-        resetFocusInfo();
-    }
-
-    /**
-     * This handles the case where there is an unexpected VH missing in the pre-layout map.
-     * <p>
-     * We might be able to detect the error in the application which will help the developer to
-     * resolve the issue.
-     * <p>
-     * If it is not an expected error, we at least print an error to notify the developer and ignore
-     * the animation.
-     *
-     * https://code.google.com/p/android/issues/detail?id=193958
-     *
-     * @param key The change key
-     * @param holder Current ViewHolder
-     * @param oldChangeViewHolder Changed ViewHolder
-     */
-    private void handleMissingPreInfoForChangeError(long key,
-            ViewHolder holder, ViewHolder oldChangeViewHolder) {
-        // check if two VH have the same key, if so, print that as an error
-        final int childCount = mChildHelper.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View view = mChildHelper.getChildAt(i);
-            ViewHolder other = getChildViewHolderInt(view);
-            if (other == holder) {
-                continue;
-            }
-            final long otherKey = getChangedHolderKey(other);
-            if (otherKey == key) {
-                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
-                            + 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
-                            + 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 + exceptionLabel());
-    }
-
-    /**
-     * Records the animation information for a view holder that was bounced from hidden list. It
-     * also clears the bounce back flag.
-     */
-    void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
-            ItemHolderInfo animationInfo) {
-        // looks like this view bounced back from hidden list!
-        viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
-        if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
-                && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
-            long key = getChangedHolderKey(viewHolder);
-            mViewInfoStore.addToOldChangeHolders(key, viewHolder);
-        }
-        mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
-    }
-
-    private void findMinMaxChildLayoutPositions(int[] into) {
-        final int count = mChildHelper.getChildCount();
-        if (count == 0) {
-            into[0] = NO_POSITION;
-            into[1] = NO_POSITION;
-            return;
-        }
-        int minPositionPreLayout = Integer.MAX_VALUE;
-        int maxPositionPreLayout = Integer.MIN_VALUE;
-        for (int i = 0; i < count; ++i) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
-            if (holder.shouldIgnore()) {
-                continue;
-            }
-            final int pos = holder.getLayoutPosition();
-            if (pos < minPositionPreLayout) {
-                minPositionPreLayout = pos;
-            }
-            if (pos > maxPositionPreLayout) {
-                maxPositionPreLayout = pos;
-            }
-        }
-        into[0] = minPositionPreLayout;
-        into[1] = maxPositionPreLayout;
-    }
-
-    private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
-        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
-        return mMinMaxLayoutPositions[0] != minPositionPreLayout
-                || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
-    }
-
-    @Override
-    protected void removeDetachedView(View child, boolean animate) {
-        ViewHolder vh = getChildViewHolderInt(child);
-        if (vh != null) {
-            if (vh.isTmpDetached()) {
-                vh.clearTmpDetachFlag();
-            } else if (!vh.shouldIgnore()) {
-                throw new IllegalArgumentException("Called removeDetachedView with a view which"
-                        + " is not flagged as tmp detached." + vh + exceptionLabel());
-            }
-        }
-
-        // Clear any android.view.animation.Animation that may prevent the item from
-        // detaching when being removed. If a child is re-added before the
-        // lazy detach occurs, it will receive invalid attach/detach sequencing.
-        child.clearAnimation();
-
-        dispatchChildDetached(child);
-        super.removeDetachedView(child, animate);
-    }
-
-    /**
-     * Returns a unique key to be used while handling change animations.
-     * It might be child's position or stable id depending on the adapter type.
-     */
-    long getChangedHolderKey(ViewHolder holder) {
-        return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
-    }
-
-    void animateAppearance(@NonNull ViewHolder itemHolder,
-            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
-        itemHolder.setIsRecyclable(false);
-        if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
-            postAnimationRunner();
-        }
-    }
-
-    void animateDisappearance(@NonNull ViewHolder holder,
-            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
-        addAnimatingView(holder);
-        holder.setIsRecyclable(false);
-        if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
-            postAnimationRunner();
-        }
-    }
-
-    private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
-            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
-            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
-        oldHolder.setIsRecyclable(false);
-        if (oldHolderDisappearing) {
-            addAnimatingView(oldHolder);
-        }
-        if (oldHolder != newHolder) {
-            if (newHolderDisappearing) {
-                addAnimatingView(newHolder);
-            }
-            oldHolder.mShadowedHolder = newHolder;
-            // old holder should disappear after animation ends
-            addAnimatingView(oldHolder);
-            mRecycler.unscrapView(oldHolder);
-            newHolder.setIsRecyclable(false);
-            newHolder.mShadowingHolder = oldHolder;
-        }
-        if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
-            postAnimationRunner();
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
-        dispatchLayout();
-        TraceCompat.endSection();
-        mFirstLayoutComplete = true;
-    }
-
-    @Override
-    public void requestLayout() {
-        if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
-            super.requestLayout();
-        } else {
-            mLayoutWasDefered = true;
-        }
-    }
-
-    void markItemDecorInsetsDirty() {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = mChildHelper.getUnfilteredChildAt(i);
-            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
-        }
-        mRecycler.markItemDecorInsetsDirty();
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        super.draw(c);
-
-        final int count = mItemDecorations.size();
-        for (int i = 0; i < count; i++) {
-            mItemDecorations.get(i).onDrawOver(c, this, mState);
-        }
-        // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
-        // need find children closest to edges. Not sure if it is worth the effort.
-        boolean needsInvalidate = false;
-        if (mLeftGlow != null && !mLeftGlow.isFinished()) {
-            final int restore = c.save();
-            final int padding = mClipToPadding ? getPaddingBottom() : 0;
-            c.rotate(270);
-            c.translate(-getHeight() + padding, 0);
-            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
-            c.restoreToCount(restore);
-        }
-        if (mTopGlow != null && !mTopGlow.isFinished()) {
-            final int restore = c.save();
-            if (mClipToPadding) {
-                c.translate(getPaddingLeft(), getPaddingTop());
-            }
-            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
-            c.restoreToCount(restore);
-        }
-        if (mRightGlow != null && !mRightGlow.isFinished()) {
-            final int restore = c.save();
-            final int width = getWidth();
-            final int padding = mClipToPadding ? getPaddingTop() : 0;
-            c.rotate(90);
-            c.translate(-padding, -width);
-            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
-            c.restoreToCount(restore);
-        }
-        if (mBottomGlow != null && !mBottomGlow.isFinished()) {
-            final int restore = c.save();
-            c.rotate(180);
-            if (mClipToPadding) {
-                c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
-            } else {
-                c.translate(-getWidth(), -getHeight());
-            }
-            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
-            c.restoreToCount(restore);
-        }
-
-        // 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()) {
-            needsInvalidate = true;
-        }
-
-        if (needsInvalidate) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    @Override
-    public void onDraw(Canvas c) {
-        super.onDraw(c);
-
-        final int count = mItemDecorations.size();
-        for (int i = 0; i < count; i++) {
-            mItemDecorations.get(i).onDraw(c, this, mState);
-        }
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
-        if (mLayout == null) {
-            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
-        }
-        return mLayout.generateDefaultLayoutParams();
-    }
-
-    @Override
-    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
-        if (mLayout == null) {
-            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
-        }
-        return mLayout.generateLayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        if (mLayout == null) {
-            throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
-        }
-        return mLayout.generateLayoutParams(p);
-    }
-
-    /**
-     * Returns true if RecyclerView is currently running some animations.
-     * <p>
-     * If you want to be notified when animations are finished, use
-     * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
-     *
-     * @return True if there are some item animations currently running or waiting to be started.
-     */
-    public boolean isAnimating() {
-        return mItemAnimator != null && mItemAnimator.isRunning();
-    }
-
-    void saveOldPositions() {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            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" + exceptionLabel());
-            }
-            if (!holder.shouldIgnore()) {
-                holder.saveOldPosition();
-            }
-        }
-    }
-
-    void clearOldPositions() {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            if (!holder.shouldIgnore()) {
-                holder.clearOldPosition();
-            }
-        }
-        mRecycler.clearOldPositions();
-    }
-
-    void offsetPositionRecordsForMove(int from, int to) {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        final int start, end, inBetweenOffset;
-        if (from < to) {
-            start = from;
-            end = to;
-            inBetweenOffset = -1;
-        } else {
-            start = to;
-            end = from;
-            inBetweenOffset = 1;
-        }
-
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            if (holder == null || holder.mPosition < start || holder.mPosition > end) {
-                continue;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
-                        + holder);
-            }
-            if (holder.mPosition == from) {
-                holder.offsetPosition(to - from, false);
-            } else {
-                holder.offsetPosition(inBetweenOffset, false);
-            }
-
-            mState.mStructureChanged = true;
-        }
-        mRecycler.offsetPositionRecordsForMove(from, to);
-        requestLayout();
-    }
-
-    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            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));
-                }
-                holder.offsetPosition(itemCount, false);
-                mState.mStructureChanged = true;
-            }
-        }
-        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
-        requestLayout();
-    }
-
-    void offsetPositionRecordsForRemove(int positionStart, int itemCount,
-            boolean applyToPreLayout) {
-        final int positionEnd = positionStart + itemCount;
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            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));
-                    }
-                    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");
-                    }
-                    holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
-                            applyToPreLayout);
-                    mState.mStructureChanged = true;
-                }
-            }
-        }
-        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
-        requestLayout();
-    }
-
-    /**
-     * Rebind existing views for the given range, or create as needed.
-     *
-     * @param positionStart Adapter position to start at
-     * @param itemCount Number of views that must explicitly be rebound
-     */
-    void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        final int positionEnd = positionStart + itemCount;
-
-        for (int i = 0; i < childCount; i++) {
-            final View child = mChildHelper.getUnfilteredChildAt(i);
-            final ViewHolder holder = getChildViewHolderInt(child);
-            if (holder == null || holder.shouldIgnore()) {
-                continue;
-            }
-            if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
-                // We re-bind these view holders after pre-processing is complete so that
-                // ViewHolders have their final positions assigned.
-                holder.addFlags(ViewHolder.FLAG_UPDATE);
-                holder.addChangePayload(payload);
-                // lp cannot be null since we get ViewHolder from it.
-                ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
-            }
-        }
-        mRecycler.viewRangeUpdate(positionStart, itemCount);
-    }
-
-    boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
-        return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
-                viewHolder.getUnmodifiedPayloads());
-    }
-
-    /**
-     * Processes the fact that, as far as we can tell, the data set has completely changed.
-     *
-     * <ul>
-     *   <li>Once layout occurs, all attached items should be discarded or animated.
-     *   <li>Attached items are labeled as invalid.
-     *   <li>Because items may still be prefetched between a "data set completely changed"
-     *       event and a layout event, all cached items are discarded.
-     * </ul>
-     *
-     * @param dispatchItemsChanged Whether to call
-     * {@link LayoutManager#onItemsChanged(RecyclerView)} during measure/layout.
-     */
-    void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
-        mDispatchItemsChangedEvent |= dispatchItemsChanged;
-        mDataSetHasChangedAfterLayout = true;
-        markKnownViewsInvalid();
-    }
-
-    /**
-     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
-     * data change event.
-     */
-    void markKnownViewsInvalid() {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            if (holder != null && !holder.shouldIgnore()) {
-                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
-            }
-        }
-        markItemDecorInsetsDirty();
-        mRecycler.markKnownViewsInvalid();
-    }
-
-    /**
-     * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
-     * will trigger a {@link #requestLayout()} call.
-     */
-    public void invalidateItemDecorations() {
-        if (mItemDecorations.size() == 0) {
-            return;
-        }
-        if (mLayout != null) {
-            mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
-                    + " or layout");
-        }
-        markItemDecorInsetsDirty();
-        requestLayout();
-    }
-
-    /**
-     * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
-     * focus even if the View representing the Item is replaced during a layout calculation.
-     * <p>
-     * By default, this value is {@code true}.
-     *
-     * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
-     * focus.
-     *
-     * @see #setPreserveFocusAfterLayout(boolean)
-     */
-    public boolean getPreserveFocusAfterLayout() {
-        return mPreserveFocusAfterLayout;
-    }
-
-    /**
-     * Set whether the RecyclerView should try to keep the same Item focused after a layout
-     * calculation or not.
-     * <p>
-     * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
-     * views may lose focus during a layout calculation as their state changes or they are replaced
-     * with another view due to type change or animation. In these cases, RecyclerView can request
-     * focus on the new view automatically.
-     *
-     * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
-     *                                 layout calculations. Defaults to true.
-     *
-     * @see #getPreserveFocusAfterLayout()
-     */
-    public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
-        mPreserveFocusAfterLayout = preserveFocusAfterLayout;
-    }
-
-    /**
-     * Retrieve the {@link ViewHolder} for the given child view.
-     *
-     * @param child Child of this RecyclerView to query for its ViewHolder
-     * @return The child view's ViewHolder
-     */
-    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);
-        }
-        return getChildViewHolderInt(child);
-    }
-
-    /**
-     * Traverses the ancestors of the given view and returns the item view that contains it and
-     * also a direct child of the RecyclerView. This returned view can be used to get the
-     * ViewHolder by calling {@link #getChildViewHolder(View)}.
-     *
-     * @param view The view that is a descendant of the RecyclerView.
-     *
-     * @return The direct child of the RecyclerView which contains the given view or null if the
-     * provided view is not a descendant of this RecyclerView.
-     *
-     * @see #getChildViewHolder(View)
-     * @see #findContainingViewHolder(View)
-     */
-    @Nullable
-    public View findContainingItemView(View view) {
-        ViewParent parent = view.getParent();
-        while (parent != null && parent != this && parent instanceof View) {
-            view = (View) parent;
-            parent = view.getParent();
-        }
-        return parent == this ? view : null;
-    }
-
-    /**
-     * Returns the ViewHolder that contains the given view.
-     *
-     * @param view The view that is a descendant of the RecyclerView.
-     *
-     * @return The ViewHolder that contains the given view or null if the provided view is not a
-     * descendant of this RecyclerView.
-     */
-    @Nullable
-    public ViewHolder findContainingViewHolder(View view) {
-        View itemView = findContainingItemView(view);
-        return itemView == null ? null : getChildViewHolder(itemView);
-    }
-
-
-    static ViewHolder getChildViewHolderInt(View child) {
-        if (child == null) {
-            return null;
-        }
-        return ((LayoutParams) child.getLayoutParams()).mViewHolder;
-    }
-
-    /**
-     * @deprecated use {@link #getChildAdapterPosition(View)} or
-     * {@link #getChildLayoutPosition(View)}.
-     */
-    @Deprecated
-    public int getChildPosition(View child) {
-        return getChildAdapterPosition(child);
-    }
-
-    /**
-     * Return the adapter position that the given child view corresponds to.
-     *
-     * @param child Child View to query
-     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
-     */
-    public int getChildAdapterPosition(View child) {
-        final ViewHolder holder = getChildViewHolderInt(child);
-        return holder != null ? holder.getAdapterPosition() : NO_POSITION;
-    }
-
-    /**
-     * Return the adapter position of the given child view as of the latest completed layout pass.
-     * <p>
-     * This position may not be equal to Item's adapter position if there are pending changes
-     * in the adapter which have not been reflected to the layout yet.
-     *
-     * @param child Child View to query
-     * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
-     * the View is representing a removed item.
-     */
-    public int getChildLayoutPosition(View child) {
-        final ViewHolder holder = getChildViewHolderInt(child);
-        return holder != null ? holder.getLayoutPosition() : NO_POSITION;
-    }
-
-    /**
-     * Return the stable item id that the given child view corresponds to.
-     *
-     * @param child Child View to query
-     * @return Item id corresponding to the given view or {@link #NO_ID}
-     */
-    public long getChildItemId(View child) {
-        if (mAdapter == null || !mAdapter.hasStableIds()) {
-            return NO_ID;
-        }
-        final ViewHolder holder = getChildViewHolderInt(child);
-        return holder != null ? holder.getItemId() : NO_ID;
-    }
-
-    /**
-     * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
-     * {@link #findViewHolderForAdapterPosition(int)}
-     */
-    @Deprecated
-    public ViewHolder findViewHolderForPosition(int position) {
-        return findViewHolderForPosition(position, false);
-    }
-
-    /**
-     * Return the ViewHolder for the item in the given position of the data set as of the latest
-     * layout pass.
-     * <p>
-     * This method checks only the children of RecyclerView. If the item at the given
-     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
-     * <p>
-     * Note that when Adapter contents change, ViewHolder positions are not updated until the
-     * next layout calculation. If there are pending adapter updates, the return value of this
-     * method may not match your adapter contents. You can use
-     * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
-     * <p>
-     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
-     * with the same layout position representing the same Item. In this case, the updated
-     * ViewHolder will be returned.
-     *
-     * @param position The position of the item in the data set of the adapter
-     * @return The ViewHolder at <code>position</code> or null if there is no such item
-     */
-    public ViewHolder findViewHolderForLayoutPosition(int position) {
-        return findViewHolderForPosition(position, false);
-    }
-
-    /**
-     * Return the ViewHolder for the item in the given position of the data set. Unlike
-     * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
-     * adapter changes that may not be reflected to the layout yet. On the other hand, if
-     * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
-     * calculated yet, this method will return <code>null</code> since the new positions of views
-     * are unknown until the layout is calculated.
-     * <p>
-     * This method checks only the children of RecyclerView. If the item at the given
-     * <code>position</code> is not laid out, it <em>will not</em> create a new one.
-     * <p>
-     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
-     * representing the same Item. In this case, the updated ViewHolder will be returned.
-     *
-     * @param position The position of the item in the data set of the adapter
-     * @return The ViewHolder at <code>position</code> or null if there is no such item
-     */
-    public ViewHolder findViewHolderForAdapterPosition(int position) {
-        if (mDataSetHasChangedAfterLayout) {
-            return null;
-        }
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        // hidden VHs are not preferred but if that is the only one we find, we rather return it
-        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 (mChildHelper.isHidden(holder.itemView)) {
-                    hidden = holder;
-                } else {
-                    return holder;
-                }
-            }
-        }
-        return hidden;
-    }
-
-    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        ViewHolder hidden = null;
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            if (holder != null && !holder.isRemoved()) {
-                if (checkNewPosition) {
-                    if (holder.mPosition != position) {
-                        continue;
-                    }
-                } else if (holder.getLayoutPosition() != position) {
-                    continue;
-                }
-                if (mChildHelper.isHidden(holder.itemView)) {
-                    hidden = holder;
-                } else {
-                    return holder;
-                }
-            }
-        }
-        // This method should not query cached views. It creates a problem during adapter updates
-        // when we are dealing with already laid out views. Also, for the public method, it is more
-        // reasonable to return null if position is not laid out.
-        return hidden;
-    }
-
-    /**
-     * Return the ViewHolder for the item with the given id. The RecyclerView must
-     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
-     * return a non-null value.
-     * <p>
-     * This method checks only the children of RecyclerView. If the item with the given
-     * <code>id</code> is not laid out, it <em>will not</em> create a new one.
-     *
-     * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
-     * same id. In this case, the updated ViewHolder will be returned.
-     *
-     * @param id The id for the requested item
-     * @return The ViewHolder with the given <code>id</code> or null if there is no such item
-     */
-    public ViewHolder findViewHolderForItemId(long id) {
-        if (mAdapter == null || !mAdapter.hasStableIds()) {
-            return null;
-        }
-        final int childCount = mChildHelper.getUnfilteredChildCount();
-        ViewHolder hidden = null;
-        for (int i = 0; i < childCount; i++) {
-            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
-            if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
-                if (mChildHelper.isHidden(holder.itemView)) {
-                    hidden = holder;
-                } else {
-                    return holder;
-                }
-            }
-        }
-        return hidden;
-    }
-
-    /**
-     * Find the topmost view under the given point.
-     *
-     * @param x Horizontal position in pixels to search
-     * @param y Vertical position in pixels to search
-     * @return The child view under (x, y) or null if no matching child is found
-     */
-    public View findChildViewUnder(float x, float y) {
-        final int count = mChildHelper.getChildCount();
-        for (int i = count - 1; i >= 0; i--) {
-            final View child = mChildHelper.getChildAt(i);
-            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;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    /**
-     * Offset the bounds of all child views by <code>dy</code> pixels.
-     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
-     *
-     * @param dy Vertical pixel offset to apply to the bounds of all child views
-     */
-    public void offsetChildrenVertical(int dy) {
-        final int childCount = mChildHelper.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
-        }
-    }
-
-    /**
-     * Called when an item view is attached to this RecyclerView.
-     *
-     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
-     * of child views as they become attached. This will be called before a
-     * {@link LayoutManager} measures or lays out the view and is a good time to perform these
-     * changes.</p>
-     *
-     * @param child Child view that is now attached to this RecyclerView and its associated window
-     */
-    public void onChildAttachedToWindow(View child) {
-    }
-
-    /**
-     * Called when an item view is detached from this RecyclerView.
-     *
-     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
-     * of child views as they become detached. This will be called as a
-     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
-     *
-     * @param child Child view that is now detached from this RecyclerView and its associated window
-     */
-    public void onChildDetachedFromWindow(View child) {
-    }
-
-    /**
-     * Offset the bounds of all child views by <code>dx</code> pixels.
-     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
-     *
-     * @param dx Horizontal pixel offset to apply to the bounds of all child views
-     */
-    public void offsetChildrenHorizontal(int dx) {
-        final int childCount = mChildHelper.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
-        }
-    }
-
-    /**
-     * Returns the bounds of the view including its decoration and margins.
-     *
-     * @param view The view element to check
-     * @param outBounds A rect that will receive the bounds of the element including its
-     *                  decoration and margins.
-     */
-    public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
-        getDecoratedBoundsWithMarginsInt(view, outBounds);
-    }
-
-    static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
-        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        final Rect insets = lp.mDecorInsets;
-        outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
-                view.getTop() - insets.top - lp.topMargin,
-                view.getRight() + insets.right + lp.rightMargin,
-                view.getBottom() + insets.bottom + lp.bottomMargin);
-    }
-
-    Rect getItemDecorInsetsForChild(View child) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (!lp.mInsetsDirty) {
-            return lp.mDecorInsets;
-        }
-
-        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
-            // changed/invalid items should not be updated until they are rebound.
-            return lp.mDecorInsets;
-        }
-        final Rect insets = lp.mDecorInsets;
-        insets.set(0, 0, 0, 0);
-        final int decorCount = mItemDecorations.size();
-        for (int i = 0; i < decorCount; i++) {
-            mTempRect.set(0, 0, 0, 0);
-            mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
-            insets.left += mTempRect.left;
-            insets.top += mTempRect.top;
-            insets.right += mTempRect.right;
-            insets.bottom += mTempRect.bottom;
-        }
-        lp.mInsetsDirty = false;
-        return insets;
-    }
-
-    /**
-     * Called when the scroll position of this RecyclerView changes. Subclasses should use
-     * this method to respond to scrolling within the adapter's data set instead of an explicit
-     * listener.
-     *
-     * <p>This method will always be invoked before listeners. If a subclass needs to perform
-     * any additional upkeep or bookkeeping after scrolling but before listeners run,
-     * this is a good place to do so.</p>
-     *
-     * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
-     * the distance scrolled in either direction within the adapter's data set instead of absolute
-     * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
-     * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
-     * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
-     * do not correspond to the data set scroll position. However, some subclasses may choose
-     * to use these fields as special offsets.</p>
-     *
-     * @param dx horizontal distance scrolled in pixels
-     * @param dy vertical distance scrolled in pixels
-     */
-    public void onScrolled(int dx, int dy) {
-        // Do nothing
-    }
-
-    void dispatchOnScrolled(int hresult, int vresult) {
-        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();
-        final int scrollY = getScrollY();
-        onScrollChanged(scrollX, scrollY, scrollX, scrollY);
-
-        // Pass the real deltas to onScrolled, the RecyclerView-specific method.
-        onScrolled(hresult, vresult);
-
-        // Invoke listeners last. Subclassed view methods always handle the event first.
-        // All internal state is consistent by the time listeners are invoked.
-        if (mScrollListener != null) {
-            mScrollListener.onScrolled(this, hresult, vresult);
-        }
-        if (mScrollListeners != null) {
-            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
-                mScrollListeners.get(i).onScrolled(this, hresult, vresult);
-            }
-        }
-        mDispatchScrollCounter--;
-    }
-
-    /**
-     * Called when the scroll state of this RecyclerView changes. Subclasses should use this
-     * method to respond to state changes instead of an explicit listener.
-     *
-     * <p>This method will always be invoked before listeners, but after the LayoutManager
-     * responds to the scroll state change.</p>
-     *
-     * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
-     *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
-     */
-    public void onScrollStateChanged(int state) {
-        // Do nothing
-    }
-
-    void dispatchOnScrollStateChanged(int state) {
-        // Let the LayoutManager go first; this allows it to bring any properties into
-        // a consistent state before the RecyclerView subclass responds.
-        if (mLayout != null) {
-            mLayout.onScrollStateChanged(state);
-        }
-
-        // Let the RecyclerView subclass handle this event next; any LayoutManager property
-        // changes will be reflected by this time.
-        onScrollStateChanged(state);
-
-        // Listeners go last. All other internal state is consistent by this point.
-        if (mScrollListener != null) {
-            mScrollListener.onScrollStateChanged(this, state);
-        }
-        if (mScrollListeners != null) {
-            for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
-                mScrollListeners.get(i).onScrollStateChanged(this, state);
-            }
-        }
-    }
-
-    /**
-     * Returns whether there are pending adapter updates which are not yet applied to the layout.
-     * <p>
-     * If this method returns <code>true</code>, it means that what user is currently seeing may not
-     * reflect them adapter contents (depending on what has changed).
-     * You may use this information to defer or cancel some operations.
-     * <p>
-     * This method returns true if RecyclerView has not yet calculated the first layout after it is
-     * attached to the Window or the Adapter has been replaced.
-     *
-     * @return True if there are some adapter updates which are not yet reflected to layout or false
-     * if layout is up to date.
-     */
-    public boolean hasPendingAdapterUpdates() {
-        return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
-                || mAdapterHelper.hasPendingUpdates();
-    }
-
-    class ViewFlinger implements Runnable {
-        private int mLastFlingX;
-        private int mLastFlingY;
-        private OverScroller mScroller;
-        Interpolator mInterpolator = sQuinticInterpolator;
-
-
-        // When set to true, postOnAnimation callbacks are delayed until the run method completes
-        private boolean mEatRunOnAnimationRequest = false;
-
-        // Tracks if postAnimationCallback should be re-attached when it is done
-        private boolean mReSchedulePostAnimationCallback = false;
-
-        ViewFlinger() {
-            mScroller = new OverScroller(getContext(), sQuinticInterpolator);
-        }
-
-        @Override
-        public void run() {
-            if (mLayout == null) {
-                stop();
-                return; // no layout, cannot scroll.
-            }
-            disableRunOnAnimationRequests();
-            consumePendingUpdateOperations();
-            // keep a local reference so that if it is changed during onAnimation method, it won't
-            // cause unexpected behaviors
-            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();
-                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) {
-                    startInterceptRequestLayout();
-                    onEnterLayoutOrScroll();
-                    TraceCompat.beginSection(TRACE_SCROLL_TAG);
-                    fillRemainingScrollValues(mState);
-                    if (dx != 0) {
-                        hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
-                        overscrollX = dx - hresult;
-                    }
-                    if (dy != 0) {
-                        vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
-                        overscrollY = dy - vresult;
-                    }
-                    TraceCompat.endSection();
-                    repositionShadowingViews();
-
-                    onExitLayoutOrScroll();
-                    stopInterceptRequestLayout(false);
-
-                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
-                            && smoothScroller.isRunning()) {
-                        final int adapterSize = mState.getItemCount();
-                        if (adapterSize == 0) {
-                            smoothScroller.stop();
-                        } else if (smoothScroller.getTargetPosition() >= adapterSize) {
-                            smoothScroller.setTargetPosition(adapterSize - 1);
-                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
-                        } else {
-                            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
-                        }
-                    }
-                }
-                if (!mItemDecorations.isEmpty()) {
-                    invalidate();
-                }
-                if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
-                    considerReleasingGlowsOnScroll(dx, dy);
-                }
-
-                if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
-                        TYPE_NON_TOUCH)
-                        && (overscrollX != 0 || overscrollY != 0)) {
-                    final int vel = (int) scroller.getCurrVelocity();
-
-                    int velX = 0;
-                    if (overscrollX != x) {
-                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
-                    }
-
-                    int velY = 0;
-                    if (overscrollY != y) {
-                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
-                    }
-
-                    if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
-                        absorbGlows(velX, velY);
-                    }
-                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
-                            && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
-                        scroller.abortAnimation();
-                    }
-                }
-                if (hresult != 0 || vresult != 0) {
-                    dispatchOnScrolled(hresult, vresult);
-                }
-
-                if (!awakenScrollBars()) {
-                    invalidate();
-                }
-
-                final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
-                        && vresult == dy;
-                final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
-                        && hresult == dx;
-                final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
-                        || fullyConsumedVertical;
-
-                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) {
-                        mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
-                    }
-                }
-            }
-            // call this after the onAnimation is complete not to have inconsistent callbacks etc.
-            if (smoothScroller != null) {
-                if (smoothScroller.isPendingInitialRun()) {
-                    smoothScroller.onAnimation(0, 0);
-                }
-                if (!mReSchedulePostAnimationCallback) {
-                    smoothScroller.stop(); //stop if it does not trigger any scroll
-                }
-            }
-            enableRunOnAnimationRequests();
-        }
-
-        private void disableRunOnAnimationRequests() {
-            mReSchedulePostAnimationCallback = false;
-            mEatRunOnAnimationRequest = true;
-        }
-
-        private void enableRunOnAnimationRequests() {
-            mEatRunOnAnimationRequest = false;
-            if (mReSchedulePostAnimationCallback) {
-                postOnAnimation();
-            }
-        }
-
-        void postOnAnimation() {
-            if (mEatRunOnAnimationRequest) {
-                mReSchedulePostAnimationCallback = true;
-            } else {
-                removeCallbacks(this);
-                ViewCompat.postOnAnimation(RecyclerView.this, this);
-            }
-        }
-
-        public void fling(int velocityX, int velocityY) {
-            setScrollState(SCROLL_STATE_SETTLING);
-            mLastFlingX = mLastFlingY = 0;
-            mScroller.fling(0, 0, velocityX, velocityY,
-                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
-            postOnAnimation();
-        }
-
-        public void smoothScrollBy(int dx, int dy) {
-            smoothScrollBy(dx, dy, 0, 0);
-        }
-
-        public void smoothScrollBy(int dx, int dy, int vx, int vy) {
-            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
-        }
-
-        private float distanceInfluenceForSnapDuration(float f) {
-            f -= 0.5f; // center the values about 0.
-            f *= 0.3f * (float) Math.PI / 2.0f;
-            return (float) Math.sin(f);
-        }
-
-        private int computeScrollDuration(int dx, int dy, int vx, int vy) {
-            final int absDx = Math.abs(dx);
-            final int absDy = Math.abs(dy);
-            final boolean horizontal = absDx > absDy;
-            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
-            final int delta = (int) Math.sqrt(dx * dx + dy * dy);
-            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 int duration;
-            if (velocity > 0) {
-                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
-            } else {
-                float absDelta = (float) (horizontal ? absDx : absDy);
-                duration = (int) (((absDelta / containerSize) + 1) * 300);
-            }
-            return Math.min(duration, MAX_SCROLL_DURATION);
-        }
-
-        public void smoothScrollBy(int dx, int dy, int duration) {
-            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
-        }
-
-        public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
-            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
-                    interpolator == null ? sQuinticInterpolator : interpolator);
-        }
-
-        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
-            if (mInterpolator != interpolator) {
-                mInterpolator = interpolator;
-                mScroller = new OverScroller(getContext(), interpolator);
-            }
-            setScrollState(SCROLL_STATE_SETTLING);
-            mLastFlingX = mLastFlingY = 0;
-            mScroller.startScroll(0, 0, dx, dy, duration);
-            if (Build.VERSION.SDK_INT < 23) {
-                // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
-                // to start values, which causes fillRemainingScrollValues() put in obsolete values
-                // for LayoutManager.onLayoutChildren().
-                mScroller.computeScrollOffset();
-            }
-            postOnAnimation();
-        }
-
-        public void stop() {
-            removeCallbacks(this);
-            mScroller.abortAnimation();
-        }
-
-    }
-
-    void repositionShadowingViews() {
-        // Fix up shadow views used by change animations
-        int count = mChildHelper.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View view = mChildHelper.getChildAt(i);
-            ViewHolder holder = getChildViewHolder(view);
-            if (holder != null && holder.mShadowingHolder != null) {
-                View shadowingView = holder.mShadowingHolder.itemView;
-                int left = view.getLeft();
-                int top = view.getTop();
-                if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
-                    shadowingView.layout(left, top,
-                            left + shadowingView.getWidth(),
-                            top + shadowingView.getHeight());
-                }
-            }
-        }
-    }
-
-    private class RecyclerViewDataObserver extends AdapterDataObserver {
-        RecyclerViewDataObserver() {
-        }
-
-        @Override
-        public void onChanged() {
-            assertNotInLayoutOrScroll(null);
-            mState.mStructureChanged = true;
-
-            processDataSetCompletelyChanged(true);
-            if (!mAdapterHelper.hasPendingUpdates()) {
-                requestLayout();
-            }
-        }
-
-        @Override
-        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
-            assertNotInLayoutOrScroll(null);
-            if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
-                triggerUpdateProcessor();
-            }
-        }
-
-        @Override
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            assertNotInLayoutOrScroll(null);
-            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
-                triggerUpdateProcessor();
-            }
-        }
-
-        @Override
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            assertNotInLayoutOrScroll(null);
-            if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
-                triggerUpdateProcessor();
-            }
-        }
-
-        @Override
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            assertNotInLayoutOrScroll(null);
-            if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
-                triggerUpdateProcessor();
-            }
-        }
-
-        void triggerUpdateProcessor() {
-            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
-                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
-            } else {
-                mAdapterUpdateDuringMeasure = true;
-                requestLayout();
-            }
-        }
-    }
-
-    /**
-     * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
-     *
-     * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
-     */
-    public static class EdgeEffectFactory {
-
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
-        public @interface EdgeDirection {}
-
-        /**
-         * Direction constant for the left edge
-         */
-        public static final int DIRECTION_LEFT = 0;
-
-        /**
-         * Direction constant for the top edge
-         */
-        public static final int DIRECTION_TOP = 1;
-
-        /**
-         * Direction constant for the right edge
-         */
-        public static final int DIRECTION_RIGHT = 2;
-
-        /**
-         * Direction constant for the bottom edge
-         */
-        public static final int DIRECTION_BOTTOM = 3;
-
-        /**
-         * Create a new EdgeEffect for the provided direction.
-         */
-        protected @NonNull EdgeEffect createEdgeEffect(RecyclerView view,
-                @EdgeDirection int direction) {
-            return new EdgeEffect(view.getContext());
-        }
-    }
-
-    /**
-     * RecycledViewPool lets you share Views between multiple RecyclerViews.
-     * <p>
-     * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
-     * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
-     * <p>
-     * RecyclerView automatically creates a pool for itself if you don't provide one.
-     *
-     */
-    public static class RecycledViewPool {
-        private static final int DEFAULT_MAX_SCRAP = 5;
-
-        /**
-         * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
-         *
-         * Note that this tracks running averages of create/bind time across all RecyclerViews
-         * (and, indirectly, Adapters) that use this pool.
-         *
-         * 1) This enables us to track average create and bind times across multiple adapters. Even
-         * though create (and especially bind) may behave differently for different Adapter
-         * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
-         *
-         * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
-         * false for all other views of its type for the same deadline. This prevents items
-         * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
-         */
-        static class ScrapData {
-            ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
-            int mMaxScrap = DEFAULT_MAX_SCRAP;
-            long mCreateRunningAverageNs = 0;
-            long mBindRunningAverageNs = 0;
-        }
-        SparseArray<ScrapData> mScrap = new SparseArray<>();
-
-        private int mAttachCount = 0;
-
-        public void clear() {
-            for (int i = 0; i < mScrap.size(); i++) {
-                ScrapData data = mScrap.valueAt(i);
-                data.mScrapHeap.clear();
-            }
-        }
-
-        public void setMaxRecycledViews(int viewType, int max) {
-            ScrapData scrapData = getScrapDataForType(viewType);
-            scrapData.mMaxScrap = max;
-            final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
-            if (scrapHeap != null) {
-                while (scrapHeap.size() > max) {
-                    scrapHeap.remove(scrapHeap.size() - 1);
-                }
-            }
-        }
-
-        /**
-         * Returns the current number of Views held by the RecycledViewPool of the given view type.
-         */
-        public int getRecycledViewCount(int viewType) {
-            return getScrapDataForType(viewType).mScrapHeap.size();
-        }
-
-        public ViewHolder getRecycledView(int viewType) {
-            final ScrapData scrapData = mScrap.get(viewType);
-            if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
-                final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
-                return scrapHeap.remove(scrapHeap.size() - 1);
-            }
-            return null;
-        }
-
-        int size() {
-            int count = 0;
-            for (int i = 0; i < mScrap.size(); i++) {
-                ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
-                if (viewHolders != null) {
-                    count += viewHolders.size();
-                }
-            }
-            return count;
-        }
-
-        public void putRecycledView(ViewHolder scrap) {
-            final int viewType = scrap.getItemViewType();
-            final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
-            if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
-                return;
-            }
-            if (DEBUG && scrapHeap.contains(scrap)) {
-                throw new IllegalArgumentException("this scrap item already exists");
-            }
-            scrap.resetInternal();
-            scrapHeap.add(scrap);
-        }
-
-        long runningAverage(long oldAverage, long newValue) {
-            if (oldAverage == 0) {
-                return newValue;
-            }
-            return (oldAverage / 4 * 3) + (newValue / 4);
-        }
-
-        void factorInCreateTime(int viewType, long createTimeNs) {
-            ScrapData scrapData = getScrapDataForType(viewType);
-            scrapData.mCreateRunningAverageNs = runningAverage(
-                    scrapData.mCreateRunningAverageNs, createTimeNs);
-        }
-
-        void factorInBindTime(int viewType, long bindTimeNs) {
-            ScrapData scrapData = getScrapDataForType(viewType);
-            scrapData.mBindRunningAverageNs = runningAverage(
-                    scrapData.mBindRunningAverageNs, bindTimeNs);
-        }
-
-        boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
-            long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
-            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
-        }
-
-        boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
-            long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
-            return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
-        }
-
-        void attach(Adapter adapter) {
-            mAttachCount++;
-        }
-
-        void detach() {
-            mAttachCount--;
-        }
-
-
-        /**
-         * Detaches the old adapter and attaches the new one.
-         * <p>
-         * RecycledViewPool will clear its cache if it has only one adapter attached and the new
-         * adapter uses a different ViewHolder than the oldAdapter.
-         *
-         * @param oldAdapter The previous adapter instance. Will be detached.
-         * @param newAdapter The new adapter instance. Will be attached.
-         * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
-         *                               ViewHolder and view types.
-         */
-        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
-                boolean compatibleWithPrevious) {
-            if (oldAdapter != null) {
-                detach();
-            }
-            if (!compatibleWithPrevious && mAttachCount == 0) {
-                clear();
-            }
-            if (newAdapter != null) {
-                attach(newAdapter);
-            }
-        }
-
-        private ScrapData getScrapDataForType(int viewType) {
-            ScrapData scrapData = mScrap.get(viewType);
-            if (scrapData == null) {
-                scrapData = new ScrapData();
-                mScrap.put(viewType, scrapData);
-            }
-            return scrapData;
-        }
-    }
-
-    /**
-     * Utility method for finding an internal RecyclerView, if present
-     */
-    @Nullable
-    static RecyclerView findNestedRecyclerView(@NonNull View view) {
-        if (!(view instanceof ViewGroup)) {
-            return null;
-        }
-        if (view instanceof RecyclerView) {
-            return (RecyclerView) view;
-        }
-        final ViewGroup parent = (ViewGroup) view;
-        final int count = parent.getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = parent.getChildAt(i);
-            final RecyclerView descendant = findNestedRecyclerView(child);
-            if (descendant != null) {
-                return descendant;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Utility method for clearing holder's internal RecyclerView, if present
-     */
-    static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
-        if (holder.mNestedRecyclerView != null) {
-            View item = holder.mNestedRecyclerView.get();
-            while (item != null) {
-                if (item == holder.itemView) {
-                    return; // match found, don't need to clear
-                }
-
-                ViewParent parent = item.getParent();
-                if (parent instanceof View) {
-                    item = (View) parent;
-                } else {
-                    item = null;
-                }
-            }
-            holder.mNestedRecyclerView = null; // not nested
-        }
-    }
-
-    /**
-     * Time base for deadline-aware work scheduling. Overridable for testing.
-     *
-     * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
-     * isn't relevant.
-     */
-    long getNanoTime() {
-        if (ALLOW_THREAD_GAP_WORK) {
-            return System.nanoTime();
-        } else {
-            return 0;
-        }
-    }
-
-    /**
-     * A Recycler is responsible for managing scrapped or detached item views for reuse.
-     *
-     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
-     * that has been marked for removal or reuse.</p>
-     *
-     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
-     * an adapter's data set representing the data at a given position or item ID.
-     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
-     * If not, the view can be quickly reused by the LayoutManager with no further work.
-     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
-     * may be repositioned by a LayoutManager without remeasurement.</p>
-     */
-    public final class Recycler {
-        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
-        ArrayList<ViewHolder> mChangedScrap = null;
-
-        final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
-
-        private final List<ViewHolder>
-                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
-
-        private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
-        int mViewCacheMax = DEFAULT_CACHE_SIZE;
-
-        RecycledViewPool mRecyclerPool;
-
-        private ViewCacheExtension mViewCacheExtension;
-
-        static final int DEFAULT_CACHE_SIZE = 2;
-
-        /**
-         * Clear scrap views out of this recycler. Detached views contained within a
-         * recycled view pool will remain.
-         */
-        public void clear() {
-            mAttachedScrap.clear();
-            recycleAndClearCachedViews();
-        }
-
-        /**
-         * Set the maximum number of detached, valid views we should retain for later use.
-         *
-         * @param viewCount Number of views to keep before sending views to the shared pool
-         */
-        public void setViewCacheSize(int viewCount) {
-            mRequestedCacheMax = viewCount;
-            updateViewCacheSize();
-        }
-
-        void updateViewCacheSize() {
-            int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
-            mViewCacheMax = mRequestedCacheMax + extraCache;
-
-            // first, try the views that can be recycled
-            for (int i = mCachedViews.size() - 1;
-                    i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
-                recycleCachedViewAt(i);
-            }
-        }
-
-        /**
-         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
-         *
-         * @return List of ViewHolders in the scrap list.
-         */
-        public List<ViewHolder> getScrapList() {
-            return mUnmodifiableAttachedScrap;
-        }
-
-        /**
-         * Helper method for getViewForPosition.
-         * <p>
-         * Checks whether a given view holder can be used for the provided position.
-         *
-         * @param holder ViewHolder
-         * @return true if ViewHolder matches the provided position, false otherwise
-         */
-        boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
-            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
-            // if it is not removed, verify the type and id.
-            if (holder.isRemoved()) {
-                if (DEBUG && !mState.isPreLayout()) {
-                    throw new IllegalStateException("should not receive a removed view unless it"
-                            + " 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 + exceptionLabel());
-            }
-            if (!mState.isPreLayout()) {
-                // don't check type if it is pre-layout.
-                final int type = mAdapter.getItemViewType(holder.mPosition);
-                if (type != holder.getItemViewType()) {
-                    return false;
-                }
-            }
-            if (mAdapter.hasStableIds()) {
-                return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
-            }
-            return true;
-        }
-
-        /**
-         * Attempts to bind view, and account for relevant timing information. If
-         * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
-         *
-         * @param holder Holder to be bound.
-         * @param offsetPosition Position of item to be bound.
-         * @param position Pre-layout position of item to be bound.
-         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
-         *                   complete. If FOREVER_NS is passed, this method will not fail to
-         *                   bind the holder.
-         * @return
-         */
-        private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
-                int position, long deadlineNs) {
-            holder.mOwnerRecyclerView = RecyclerView.this;
-            final int viewType = holder.getItemViewType();
-            long startBindNs = getNanoTime();
-            if (deadlineNs != FOREVER_NS
-                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
-                // abort - we have a deadline we can't meet
-                return false;
-            }
-            mAdapter.bindViewHolder(holder, offsetPosition);
-            long endBindNs = getNanoTime();
-            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
-            attachAccessibilityDelegateOnBind(holder);
-            if (mState.isPreLayout()) {
-                holder.mPreLayoutPosition = position;
-            }
-            return true;
-        }
-
-        /**
-         * Binds the given View to the position. The View can be a View previously retrieved via
-         * {@link #getViewForPosition(int)} or created by
-         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
-         * <p>
-         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
-         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
-         * wants to handle its own recycling logic.
-         * <p>
-         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
-         * you don't need to call this method unless you want to bind this View to another position.
-         *
-         * @param view The view to update.
-         * @param position The position of the item to bind to this View.
-         */
-        public void bindViewToPosition(View view, int position) {
-            ViewHolder holder = getChildViewHolderInt(view);
-            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" + 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() + exceptionLabel());
-            }
-            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
-
-            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
-            final LayoutParams rvLayoutParams;
-            if (lp == null) {
-                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
-                holder.itemView.setLayoutParams(rvLayoutParams);
-            } else if (!checkLayoutParams(lp)) {
-                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
-                holder.itemView.setLayoutParams(rvLayoutParams);
-            } else {
-                rvLayoutParams = (LayoutParams) lp;
-            }
-
-            rvLayoutParams.mInsetsDirty = true;
-            rvLayoutParams.mViewHolder = holder;
-            rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
-        }
-
-        /**
-         * RecyclerView provides artificial position range (item count) in pre-layout state and
-         * automatically maps these positions to {@link Adapter} positions when
-         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
-         * <p>
-         * Usually, LayoutManager does not need to worry about this. However, in some cases, your
-         * LayoutManager may need to call some custom component with item positions in which
-         * case you need the actual adapter position instead of the pre layout position. You
-         * can use this method to convert a pre-layout position to adapter (post layout) position.
-         * <p>
-         * Note that if the provided position belongs to a deleted ViewHolder, this method will
-         * return -1.
-         * <p>
-         * Calling this method in post-layout state returns the same value back.
-         *
-         * @param position The pre-layout position to convert. Must be greater or equal to 0 and
-         *                 less than {@link State#getItemCount()}.
-         */
-        public int convertPreLayoutPositionToPostLayout(int position) {
-            if (position < 0 || position >= mState.getItemCount()) {
-                throw new IndexOutOfBoundsException("invalid position " + position + ". State "
-                        + "item count is " + mState.getItemCount() + exceptionLabel());
-            }
-            if (!mState.isPreLayout()) {
-                return position;
-            }
-            return mAdapterHelper.findPositionOffset(position);
-        }
-
-        /**
-         * Obtain a view initialized for the given position.
-         *
-         * This method should be used by {@link LayoutManager} implementations to obtain
-         * views to represent data from an {@link Adapter}.
-         * <p>
-         * The Recycler may reuse a scrap or detached view from a shared pool if one is
-         * available for the correct view type. If the adapter has not indicated that the
-         * data at the given position has changed, the Recycler will attempt to hand back
-         * a scrap view that was previously initialized for that data without rebinding.
-         *
-         * @param position Position to obtain a view for
-         * @return A view representing the data at <code>position</code> from <code>adapter</code>
-         */
-        public View getViewForPosition(int position) {
-            return getViewForPosition(position, false);
-        }
-
-        View getViewForPosition(int position, boolean dryRun) {
-            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
-        }
-
-        /**
-         * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
-         * cache, the RecycledViewPool, or creating it directly.
-         * <p>
-         * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
-         * rather than constructing or binding a ViewHolder if it doesn't think it has time.
-         * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
-         * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
-         * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
-         *
-         * @param position Position of ViewHolder to be returned.
-         * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
-         * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
-         *                   complete. If FOREVER_NS is passed, this method will not fail to
-         *                   create/bind the holder if needed.
-         *
-         * @return ViewHolder for requested position
-         */
-        @Nullable
-        ViewHolder tryGetViewHolderForPositionByDeadline(int position,
-                boolean dryRun, long deadlineNs) {
-            if (position < 0 || position >= mState.getItemCount()) {
-                throw new IndexOutOfBoundsException("Invalid item position " + position
-                        + "(" + position + "). Item count:" + mState.getItemCount()
-                        + exceptionLabel());
-            }
-            boolean fromScrapOrHiddenOrCache = false;
-            ViewHolder holder = null;
-            // 0) If there is a changed scrap, try to find from there
-            if (mState.isPreLayout()) {
-                holder = getChangedScrapViewForPosition(position);
-                fromScrapOrHiddenOrCache = holder != null;
-            }
-            // 1) Find by position from scrap/hidden list/cache
-            if (holder == null) {
-                holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
-                if (holder != null) {
-                    if (!validateViewHolderForOffsetPosition(holder)) {
-                        // recycle holder (and unscrap if relevant) since it can't be used
-                        if (!dryRun) {
-                            // we would like to recycle this but need to make sure it is not used by
-                            // animation logic etc.
-                            holder.addFlags(ViewHolder.FLAG_INVALID);
-                            if (holder.isScrap()) {
-                                removeDetachedView(holder.itemView, false);
-                                holder.unScrap();
-                            } else if (holder.wasReturnedFromScrap()) {
-                                holder.clearReturnedFromScrapFlag();
-                            }
-                            recycleViewHolderInternal(holder);
-                        }
-                        holder = null;
-                    } else {
-                        fromScrapOrHiddenOrCache = true;
-                    }
-                }
-            }
-            if (holder == null) {
-                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() + exceptionLabel());
-                }
-
-                final int type = mAdapter.getItemViewType(offsetPosition);
-                // 2) Find from scrap/cache via stable ids, if exists
-                if (mAdapter.hasStableIds()) {
-                    holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
-                            type, dryRun);
-                    if (holder != null) {
-                        // update position
-                        holder.mPosition = offsetPosition;
-                        fromScrapOrHiddenOrCache = true;
-                    }
-                }
-                if (holder == null && mViewCacheExtension != null) {
-                    // We are NOT sending the offsetPosition because LayoutManager does not
-                    // know it.
-                    final View view = mViewCacheExtension
-                            .getViewForPositionAndType(this, position, type);
-                    if (view != null) {
-                        holder = getChildViewHolder(view);
-                        if (holder == null) {
-                            throw new IllegalArgumentException("getViewForPositionAndType returned"
-                                    + " 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." + exceptionLabel());
-                        }
-                    }
-                }
-                if (holder == null) { // fallback to pool
-                    if (DEBUG) {
-                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
-                                + position + ") fetching from shared pool");
-                    }
-                    holder = getRecycledViewPool().getRecycledView(type);
-                    if (holder != null) {
-                        holder.resetInternal();
-                        if (FORCE_INVALIDATE_DISPLAY_LIST) {
-                            invalidateDisplayListInt(holder);
-                        }
-                    }
-                }
-                if (holder == null) {
-                    long start = getNanoTime();
-                    if (deadlineNs != FOREVER_NS
-                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
-                        // abort - we have a deadline we can't meet
-                        return null;
-                    }
-                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
-                    if (ALLOW_THREAD_GAP_WORK) {
-                        // only bother finding nested RV if prefetching
-                        RecyclerView innerView = findNestedRecyclerView(holder.itemView);
-                        if (innerView != null) {
-                            holder.mNestedRecyclerView = new WeakReference<>(innerView);
-                        }
-                    }
-
-                    long end = getNanoTime();
-                    mRecyclerPool.factorInCreateTime(type, end - start);
-                    if (DEBUG) {
-                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
-                    }
-                }
-            }
-
-            // This is very ugly but the only place we can grab this information
-            // before the View is rebound and returned to the LayoutManager for post layout ops.
-            // We don't need this in pre-layout since the VH is not updated by the LM.
-            if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
-                    .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
-                holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
-                if (mState.mRunSimpleAnimations) {
-                    int changeFlags = ItemAnimator
-                            .buildAdapterChangeFlagsForAnimations(holder);
-                    changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
-                    final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
-                            holder, changeFlags, holder.getUnmodifiedPayloads());
-                    recordAnimationInfoIfBouncedHiddenView(holder, info);
-                }
-            }
-
-            boolean bound = false;
-            if (mState.isPreLayout() && holder.isBound()) {
-                // do not update unless we absolutely have to.
-                holder.mPreLayoutPosition = position;
-            } 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
-                            + exceptionLabel());
-                }
-                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
-                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
-            }
-
-            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
-            final LayoutParams rvLayoutParams;
-            if (lp == null) {
-                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
-                holder.itemView.setLayoutParams(rvLayoutParams);
-            } else if (!checkLayoutParams(lp)) {
-                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
-                holder.itemView.setLayoutParams(rvLayoutParams);
-            } else {
-                rvLayoutParams = (LayoutParams) lp;
-            }
-            rvLayoutParams.mViewHolder = holder;
-            rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
-            return holder;
-        }
-
-        private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
-            if (isAccessibilityEnabled()) {
-                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());
-                }
-            }
-        }
-
-        private void invalidateDisplayListInt(ViewHolder holder) {
-            if (holder.itemView instanceof ViewGroup) {
-                invalidateDisplayListInt((ViewGroup) holder.itemView, false);
-            }
-        }
-
-        private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
-            for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
-                final View view = viewGroup.getChildAt(i);
-                if (view instanceof ViewGroup) {
-                    invalidateDisplayListInt((ViewGroup) view, true);
-                }
-            }
-            if (!invalidateThis) {
-                return;
-            }
-            // we need to force it to become invisible
-            if (viewGroup.getVisibility() == View.INVISIBLE) {
-                viewGroup.setVisibility(View.VISIBLE);
-                viewGroup.setVisibility(View.INVISIBLE);
-            } else {
-                final int visibility = viewGroup.getVisibility();
-                viewGroup.setVisibility(View.INVISIBLE);
-                viewGroup.setVisibility(visibility);
-            }
-        }
-
-        /**
-         * Recycle a detached view. The specified view will be added to a pool of views
-         * for later rebinding and reuse.
-         *
-         * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
-         * View is scrapped, it will be removed from scrap list.</p>
-         *
-         * @param view Removed view for recycling
-         * @see LayoutManager#removeAndRecycleView(View, Recycler)
-         */
-        public void recycleView(View view) {
-            // This public recycle method tries to make view recycle-able since layout manager
-            // intended to recycle this view (e.g. even if it is in scrap or change cache)
-            ViewHolder holder = getChildViewHolderInt(view);
-            if (holder.isTmpDetached()) {
-                removeDetachedView(view, false);
-            }
-            if (holder.isScrap()) {
-                holder.unScrap();
-            } else if (holder.wasReturnedFromScrap()) {
-                holder.clearReturnedFromScrapFlag();
-            }
-            recycleViewHolderInternal(holder);
-        }
-
-        /**
-         * Internally, use this method instead of {@link #recycleView(android.view.View)} to
-         * catch potential bugs.
-         * @param view
-         */
-        void recycleViewInternal(View view) {
-            recycleViewHolderInternal(getChildViewHolderInt(view));
-        }
-
-        void recycleAndClearCachedViews() {
-            final int count = mCachedViews.size();
-            for (int i = count - 1; i >= 0; i--) {
-                recycleCachedViewAt(i);
-            }
-            mCachedViews.clear();
-            if (ALLOW_THREAD_GAP_WORK) {
-                mPrefetchRegistry.clearPrefetchPositions();
-            }
-        }
-
-        /**
-         * Recycles a cached view and removes the view from the list. Views are added to cache
-         * if and only if they are recyclable, so this method does not check it again.
-         * <p>
-         * A small exception to this rule is when the view does not have an animator reference
-         * but transient state is true (due to animations created outside ItemAnimator). In that
-         * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
-         * still recyclable since Adapter wants to do so.
-         *
-         * @param cachedViewIndex The index of the view in cached views list
-         */
-        void recycleCachedViewAt(int cachedViewIndex) {
-            if (DEBUG) {
-                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
-            }
-            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
-            if (DEBUG) {
-                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
-            }
-            addViewHolderToRecycledViewPool(viewHolder, true);
-            mCachedViews.remove(cachedViewIndex);
-        }
-
-        /**
-         * internal implementation checks if view is scrapped or attached and throws an exception
-         * if so.
-         * Public version un-scraps before calling recycle.
-         */
-        void recycleViewHolderInternal(ViewHolder holder) {
-            if (holder.isScrap() || holder.itemView.getParent() != null) {
-                throw new IllegalArgumentException(
-                        "Scrapped or attached views may not be recycled. isScrap:"
-                                + holder.isScrap() + " isAttached:"
-                                + (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
-                        + exceptionLabel());
-            }
-
-            if (holder.shouldIgnore()) {
-                throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
-                        + " should first call stopIgnoringView(view) before calling recycle."
-                        + exceptionLabel());
-            }
-            //noinspection unchecked
-            final boolean transientStatePreventsRecycling = holder
-                    .doesTransientStatePreventRecycling();
-            final boolean forceRecycle = mAdapter != null
-                    && transientStatePreventsRecycling
-                    && mAdapter.onFailedToRecycleView(holder);
-            boolean cached = false;
-            boolean recycled = false;
-            if (DEBUG && mCachedViews.contains(holder)) {
-                throw new IllegalArgumentException("cached view received recycle internal? "
-                        + holder + exceptionLabel());
-            }
-            if (forceRecycle || holder.isRecyclable()) {
-                if (mViewCacheMax > 0
-                        && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
-                        | ViewHolder.FLAG_REMOVED
-                        | ViewHolder.FLAG_UPDATE
-                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
-                    // Retire oldest cached view
-                    int cachedViewSize = mCachedViews.size();
-                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
-                        recycleCachedViewAt(0);
-                        cachedViewSize--;
-                    }
-
-                    int targetCacheIndex = cachedViewSize;
-                    if (ALLOW_THREAD_GAP_WORK
-                            && cachedViewSize > 0
-                            && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
-                        // when adding the view, skip past most recently prefetched views
-                        int cacheIndex = cachedViewSize - 1;
-                        while (cacheIndex >= 0) {
-                            int cachedPos = mCachedViews.get(cacheIndex).mPosition;
-                            if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
-                                break;
-                            }
-                            cacheIndex--;
-                        }
-                        targetCacheIndex = cacheIndex + 1;
-                    }
-                    mCachedViews.add(targetCacheIndex, holder);
-                    cached = true;
-                }
-                if (!cached) {
-                    addViewHolderToRecycledViewPool(holder, true);
-                    recycled = true;
-                }
-            } else {
-                // NOTE: A view can fail to be recycled when it is scrolled off while an animation
-                // runs. In this case, the item is eventually recycled by
-                // ItemAnimatorRestoreListener#onAnimationFinished.
-
-                // TODO: consider cancelling an animation when an item is removed scrollBy,
-                // 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"
-                            + exceptionLabel());
-                }
-            }
-            // even if the holder is not removed, we still call this method so that it is removed
-            // from view holder lists.
-            mViewInfoStore.removeViewHolder(holder);
-            if (!cached && !recycled && transientStatePreventsRecycling) {
-                holder.mOwnerRecyclerView = null;
-            }
-        }
-
-        /**
-         * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
-         *
-         * Pass false to dispatchRecycled for views that have not been bound.
-         *
-         * @param holder Holder to be added to the pool.
-         * @param dispatchRecycled True to dispatch View recycled callbacks.
-         */
-        void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
-            clearNestedRecyclerViewIfNotNested(holder);
-            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);
-            }
-            holder.mOwnerRecyclerView = null;
-            getRecycledViewPool().putRecycledView(holder);
-        }
-
-        /**
-         * Used as a fast path for unscrapping and recycling a view during a bulk operation.
-         * The caller must call {@link #clearScrap()} when it's done to update the recycler's
-         * internal bookkeeping.
-         */
-        void quickRecycleScrapView(View view) {
-            final ViewHolder holder = getChildViewHolderInt(view);
-            holder.mScrapContainer = null;
-            holder.mInChangeScrap = false;
-            holder.clearReturnedFromScrapFlag();
-            recycleViewHolderInternal(holder);
-        }
-
-        /**
-         * Mark an attached view as scrap.
-         *
-         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
-         * for rebinding and reuse. Requests for a view for a given position may return a
-         * reused or rebound scrap view instance.</p>
-         *
-         * @param view View to scrap
-         */
-        void scrapView(View view) {
-            final ViewHolder holder = getChildViewHolderInt(view);
-            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
-                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
-                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." + exceptionLabel());
-                }
-                holder.setScrapContainer(this, false);
-                mAttachedScrap.add(holder);
-            } else {
-                if (mChangedScrap == null) {
-                    mChangedScrap = new ArrayList<ViewHolder>();
-                }
-                holder.setScrapContainer(this, true);
-                mChangedScrap.add(holder);
-            }
-        }
-
-        /**
-         * Remove a previously scrapped view from the pool of eligible scrap.
-         *
-         * <p>This view will no longer be eligible for reuse until re-scrapped or
-         * until it is explicitly removed and recycled.</p>
-         */
-        void unscrapView(ViewHolder holder) {
-            if (holder.mInChangeScrap) {
-                mChangedScrap.remove(holder);
-            } else {
-                mAttachedScrap.remove(holder);
-            }
-            holder.mScrapContainer = null;
-            holder.mInChangeScrap = false;
-            holder.clearReturnedFromScrapFlag();
-        }
-
-        int getScrapCount() {
-            return mAttachedScrap.size();
-        }
-
-        View getScrapViewAt(int index) {
-            return mAttachedScrap.get(index).itemView;
-        }
-
-        void clearScrap() {
-            mAttachedScrap.clear();
-            if (mChangedScrap != null) {
-                mChangedScrap.clear();
-            }
-        }
-
-        ViewHolder getChangedScrapViewForPosition(int position) {
-            // If pre-layout, check the changed scrap for an exact match.
-            final int changedScrapSize;
-            if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
-                return null;
-            }
-            // find by position
-            for (int i = 0; i < changedScrapSize; i++) {
-                final ViewHolder holder = mChangedScrap.get(i);
-                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
-                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
-                    return holder;
-                }
-            }
-            // find by id
-            if (mAdapter.hasStableIds()) {
-                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
-                if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
-                    final long id = mAdapter.getItemId(offsetPosition);
-                    for (int i = 0; i < changedScrapSize; i++) {
-                        final ViewHolder holder = mChangedScrap.get(i);
-                        if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
-                            holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
-                            return holder;
-                        }
-                    }
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Returns a view for the position either from attach scrap, hidden children, or cache.
-         *
-         * @param position Item position
-         * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
-         * @return a ViewHolder that can be re-used for this position.
-         */
-        ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
-            final int scrapCount = mAttachedScrap.size();
-
-            // Try first for an exact, non-invalid match from scrap.
-            for (int i = 0; i < scrapCount; i++) {
-                final ViewHolder holder = mAttachedScrap.get(i);
-                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
-                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
-                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
-                    return holder;
-                }
-            }
-
-            if (!dryRun) {
-                View view = mChildHelper.findHiddenNonRemovedView(position);
-                if (view != null) {
-                    // This View is good to be used. We just need to unhide, detach and move to the
-                    // scrap list.
-                    final ViewHolder vh = getChildViewHolderInt(view);
-                    mChildHelper.unhide(view);
-                    int layoutIndex = mChildHelper.indexOfChild(view);
-                    if (layoutIndex == RecyclerView.NO_POSITION) {
-                        throw new IllegalStateException("layout index should not be -1 after "
-                                + "unhiding a view:" + vh + exceptionLabel());
-                    }
-                    mChildHelper.detachViewFromParent(layoutIndex);
-                    scrapView(view);
-                    vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
-                            | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
-                    return vh;
-                }
-            }
-
-            // Search in our first-level recycled view cache.
-            final int cacheSize = mCachedViews.size();
-            for (int i = 0; i < cacheSize; i++) {
-                final ViewHolder holder = mCachedViews.get(i);
-                // invalid view holders may be in cache if adapter has stable ids as they can be
-                // retrieved via getScrapOrCachedViewForId
-                if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
-                    if (!dryRun) {
-                        mCachedViews.remove(i);
-                    }
-                    if (DEBUG) {
-                        Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
-                                + ") found match in cache: " + holder);
-                    }
-                    return holder;
-                }
-            }
-            return null;
-        }
-
-        ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
-            // Look in our attached views first
-            final int count = mAttachedScrap.size();
-            for (int i = count - 1; i >= 0; i--) {
-                final ViewHolder holder = mAttachedScrap.get(i);
-                if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
-                    if (type == holder.getItemViewType()) {
-                        holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
-                        if (holder.isRemoved()) {
-                            // this might be valid in two cases:
-                            // > item is removed but we are in pre-layout pass
-                            // >> do nothing. return as is. make sure we don't rebind
-                            // > item is removed then added to another position and we are in
-                            // post layout.
-                            // >> remove removed and invalid flags, add update flag to rebind
-                            // 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);
-                            }
-                        }
-                        return holder;
-                    } else if (!dryRun) {
-                        // if we are running animations, it is actually better to keep it in scrap
-                        // but this would force layout manager to lay it out which would be bad.
-                        // Recycle this scrap. Type mismatch.
-                        mAttachedScrap.remove(i);
-                        removeDetachedView(holder.itemView, false);
-                        quickRecycleScrapView(holder.itemView);
-                    }
-                }
-            }
-
-            // Search the first-level cache
-            final int cacheSize = mCachedViews.size();
-            for (int i = cacheSize - 1; i >= 0; i--) {
-                final ViewHolder holder = mCachedViews.get(i);
-                if (holder.getItemId() == id) {
-                    if (type == holder.getItemViewType()) {
-                        if (!dryRun) {
-                            mCachedViews.remove(i);
-                        }
-                        return holder;
-                    } else if (!dryRun) {
-                        recycleCachedViewAt(i);
-                        return null;
-                    }
-                }
-            }
-            return null;
-        }
-
-        void dispatchViewRecycled(ViewHolder holder) {
-            if (mRecyclerListener != null) {
-                mRecyclerListener.onViewRecycled(holder);
-            }
-            if (mAdapter != null) {
-                mAdapter.onViewRecycled(holder);
-            }
-            if (mState != null) {
-                mViewInfoStore.removeViewHolder(holder);
-            }
-            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
-        }
-
-        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
-                boolean compatibleWithPrevious) {
-            clear();
-            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
-        }
-
-        void offsetPositionRecordsForMove(int from, int to) {
-            final int start, end, inBetweenOffset;
-            if (from < to) {
-                start = from;
-                end = to;
-                inBetweenOffset = -1;
-            } else {
-                start = to;
-                end = from;
-                inBetweenOffset = 1;
-            }
-            final int cachedCount = mCachedViews.size();
-            for (int i = 0; i < cachedCount; i++) {
-                final ViewHolder holder = mCachedViews.get(i);
-                if (holder == null || holder.mPosition < start || holder.mPosition > end) {
-                    continue;
-                }
-                if (holder.mPosition == from) {
-                    holder.offsetPosition(to - from, false);
-                } else {
-                    holder.offsetPosition(inBetweenOffset, false);
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
-                            + holder);
-                }
-            }
-        }
-
-        void offsetPositionRecordsForInsert(int insertedAt, int count) {
-            final int cachedCount = mCachedViews.size();
-            for (int i = 0; i < cachedCount; i++) {
-                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));
-                    }
-                    holder.offsetPosition(count, true);
-                }
-            }
-        }
-
-        /**
-         * @param removedFrom Remove start index
-         * @param count Remove count
-         * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
-         *                         false, they'll be applied before the second layout pass
-         */
-        void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
-            final int removedEnd = removedFrom + count;
-            final int cachedCount = mCachedViews.size();
-            for (int i = cachedCount - 1; i >= 0; i--) {
-                final ViewHolder holder = mCachedViews.get(i);
-                if (holder != null) {
-                    if (holder.mPosition >= removedEnd) {
-                        if (DEBUG) {
-                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
-                                    + " holder " + holder + " now at position "
-                                    + (holder.mPosition - count));
-                        }
-                        holder.offsetPosition(-count, applyToPreLayout);
-                    } else if (holder.mPosition >= removedFrom) {
-                        // Item for this view was removed. Dump it from the cache.
-                        holder.addFlags(ViewHolder.FLAG_REMOVED);
-                        recycleCachedViewAt(i);
-                    }
-                }
-            }
-        }
-
-        void setViewCacheExtension(ViewCacheExtension extension) {
-            mViewCacheExtension = extension;
-        }
-
-        void setRecycledViewPool(RecycledViewPool pool) {
-            if (mRecyclerPool != null) {
-                mRecyclerPool.detach();
-            }
-            mRecyclerPool = pool;
-            if (pool != null) {
-                mRecyclerPool.attach(getAdapter());
-            }
-        }
-
-        RecycledViewPool getRecycledViewPool() {
-            if (mRecyclerPool == null) {
-                mRecyclerPool = new RecycledViewPool();
-            }
-            return mRecyclerPool;
-        }
-
-        void viewRangeUpdate(int positionStart, int itemCount) {
-            final int positionEnd = positionStart + itemCount;
-            final int cachedCount = mCachedViews.size();
-            for (int i = cachedCount - 1; i >= 0; i--) {
-                final ViewHolder holder = mCachedViews.get(i);
-                if (holder == null) {
-                    continue;
-                }
-
-                final int pos = holder.mPosition;
-                if (pos >= positionStart && pos < positionEnd) {
-                    holder.addFlags(ViewHolder.FLAG_UPDATE);
-                    recycleCachedViewAt(i);
-                    // cached views should not be flagged as changed because this will cause them
-                    // to animate when they are returned from cache.
-                }
-            }
-        }
-
-        void markKnownViewsInvalid() {
-            final int cachedCount = mCachedViews.size();
-            for (int i = 0; i < cachedCount; i++) {
-                final ViewHolder holder = mCachedViews.get(i);
-                if (holder != null) {
-                    holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
-                    holder.addChangePayload(null);
-                }
-            }
-
-            if (mAdapter == null || !mAdapter.hasStableIds()) {
-                // we cannot re-use cached views in this case. Recycle them all
-                recycleAndClearCachedViews();
-            }
-        }
-
-        void clearOldPositions() {
-            final int cachedCount = mCachedViews.size();
-            for (int i = 0; i < cachedCount; i++) {
-                final ViewHolder holder = mCachedViews.get(i);
-                holder.clearOldPosition();
-            }
-            final int scrapCount = mAttachedScrap.size();
-            for (int i = 0; i < scrapCount; i++) {
-                mAttachedScrap.get(i).clearOldPosition();
-            }
-            if (mChangedScrap != null) {
-                final int changedScrapCount = mChangedScrap.size();
-                for (int i = 0; i < changedScrapCount; i++) {
-                    mChangedScrap.get(i).clearOldPosition();
-                }
-            }
-        }
-
-        void markItemDecorInsetsDirty() {
-            final int cachedCount = mCachedViews.size();
-            for (int i = 0; i < cachedCount; i++) {
-                final ViewHolder holder = mCachedViews.get(i);
-                LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
-                if (layoutParams != null) {
-                    layoutParams.mInsetsDirty = true;
-                }
-            }
-        }
-    }
-
-    /**
-     * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
-     * be controlled by the developer.
-     * <p>
-     * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
-     * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
-     * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
-     * {@link RecycledViewPool}.
-     * <p>
-     * Note that, Recycler never sends Views to this method to be cached. It is developers
-     * responsibility to decide whether they want to keep their Views in this custom cache or let
-     * the default recycling policy handle it.
-     */
-    public abstract static class ViewCacheExtension {
-
-        /**
-         * Returns a View that can be binded to the given Adapter position.
-         * <p>
-         * This method should <b>not</b> create a new View. Instead, it is expected to return
-         * an already created View that can be re-used for the given type and position.
-         * If the View is marked as ignored, it should first call
-         * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
-         * <p>
-         * RecyclerView will re-bind the returned View to the position if necessary.
-         *
-         * @param recycler The Recycler that can be used to bind the View
-         * @param position The adapter position
-         * @param type     The type of the View, defined by adapter
-         * @return A View that is bound to the given position or NULL if there is no View to re-use
-         * @see LayoutManager#ignoreView(View)
-         */
-        public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
-    }
-
-    /**
-     * Base class for an Adapter
-     *
-     * <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 abstract static class Adapter<VH extends ViewHolder> {
-        private final AdapterDataObservable mObservable = new AdapterDataObservable();
-        private boolean mHasStableIds = false;
-
-        /**
-         * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
-         * an item.
-         * <p>
-         * This new ViewHolder should be constructed with a new View that can represent the items
-         * of the given type. You can either create a new View manually or inflate it from an XML
-         * layout file.
-         * <p>
-         * The new ViewHolder will be used to display items of the adapter using
-         * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
-         * different items in the data set, it is a good idea to cache references to sub views of
-         * the View to avoid unnecessary {@link View#findViewById(int)} calls.
-         *
-         * @param parent The ViewGroup into which the new View will be added after it is bound to
-         *               an adapter position.
-         * @param viewType The view type of the new View.
-         *
-         * @return A new ViewHolder that holds a View of the given view type.
-         * @see #getItemViewType(int)
-         * @see #onBindViewHolder(ViewHolder, int)
-         */
-        @NonNull
-        public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
-
-        /**
-         * Called by RecyclerView to display the data at the specified position. This method should
-         * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
-         * position.
-         * <p>
-         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
-         * again if the position of the item changes in the data set unless the item itself is
-         * invalidated or the new position cannot be determined. For this reason, you should only
-         * use the <code>position</code> parameter while acquiring the related data item inside
-         * this method and should not keep a copy of it. If you need the position of an item later
-         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
-         * have the updated adapter position.
-         *
-         * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
-         * handle efficient partial bind.
-         *
-         * @param holder The ViewHolder which should be updated to represent the contents of the
-         *        item at the given position in the data set.
-         * @param position The position of the item within the adapter's data set.
-         */
-        public abstract void onBindViewHolder(@NonNull VH holder, int position);
-
-        /**
-         * Called by RecyclerView to display the data at the specified position. This method
-         * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
-         * the given position.
-         * <p>
-         * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
-         * again if the position of the item changes in the data set unless the item itself is
-         * invalidated or the new position cannot be determined. For this reason, you should only
-         * use the <code>position</code> parameter while acquiring the related data item inside
-         * this method and should not keep a copy of it. If you need the position of an item later
-         * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
-         * have the updated adapter position.
-         * <p>
-         * Partial bind vs full bind:
-         * <p>
-         * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
-         * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
-         * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
-         * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
-         * Adapter should not assume that the payload passed in notify methods will be received by
-         * onBindViewHolder().  For example when the view is not attached to the screen, the
-         * payload in notifyItemChange() will be simply dropped.
-         *
-         * @param holder The ViewHolder which should be updated to represent the contents of the
-         *               item at the given position in the data set.
-         * @param position The position of the item within the adapter's data set.
-         * @param payloads A non-null list of merged payloads. Can be empty list if requires full
-         *                 update.
-         */
-        public void onBindViewHolder(@NonNull VH holder, int position,
-                @NonNull List<Object> payloads) {
-            onBindViewHolder(holder, position);
-        }
-
-        /**
-         * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
-         * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
-         *
-         * @see #onCreateViewHolder(ViewGroup, int)
-         */
-        public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
-            try {
-                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
-                final VH holder = onCreateViewHolder(parent, viewType);
-                if (holder.itemView.getParent() != null) {
-                    throw new IllegalStateException("ViewHolder views must not be attached when"
-                            + " created. Ensure that you are not passing 'true' to the attachToRoot"
-                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
-                }
-                holder.mItemViewType = viewType;
-                return holder;
-            } finally {
-                TraceCompat.endSection();
-            }
-        }
-
-        /**
-         * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
-         * {@link ViewHolder} contents with the item at the given position and also sets up some
-         * private fields to be used by RecyclerView.
-         *
-         * @see #onBindViewHolder(ViewHolder, int)
-         */
-        public final void bindViewHolder(@NonNull VH holder, int position) {
-            holder.mPosition = position;
-            if (hasStableIds()) {
-                holder.mItemId = getItemId(position);
-            }
-            holder.setFlags(ViewHolder.FLAG_BOUND,
-                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
-                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
-            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
-            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
-            holder.clearPayload();
-            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
-            if (layoutParams instanceof RecyclerView.LayoutParams) {
-                ((LayoutParams) layoutParams).mInsetsDirty = true;
-            }
-            TraceCompat.endSection();
-        }
-
-        /**
-         * Return the view type of the item at <code>position</code> for the purposes
-         * of view recycling.
-         *
-         * <p>The default implementation of this method returns 0, making the assumption of
-         * a single view type for the adapter. Unlike ListView adapters, types need not
-         * be contiguous. Consider using id resources to uniquely identify item view types.
-         *
-         * @param position position to query
-         * @return integer value identifying the type of the view needed to represent the item at
-         *                 <code>position</code>. Type codes need not be contiguous.
-         */
-        public int getItemViewType(int position) {
-            return 0;
-        }
-
-        /**
-         * Indicates whether each item in the data set can be represented with a unique identifier
-         * of type {@link java.lang.Long}.
-         *
-         * @param hasStableIds Whether items in data set have unique identifiers or not.
-         * @see #hasStableIds()
-         * @see #getItemId(int)
-         */
-        public void setHasStableIds(boolean hasStableIds) {
-            if (hasObservers()) {
-                throw new IllegalStateException("Cannot change whether this adapter has "
-                        + "stable IDs while the adapter has registered observers.");
-            }
-            mHasStableIds = hasStableIds;
-        }
-
-        /**
-         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
-         * would return false this method should return {@link #NO_ID}. The default implementation
-         * of this method returns {@link #NO_ID}.
-         *
-         * @param position Adapter position to query
-         * @return the stable ID of the item at position
-         */
-        public long getItemId(int position) {
-            return NO_ID;
-        }
-
-        /**
-         * Returns the total number of items in the data set held by the adapter.
-         *
-         * @return The total number of items in this adapter.
-         */
-        public abstract int getItemCount();
-
-        /**
-         * Returns true if this adapter publishes a unique <code>long</code> value that can
-         * act as a key for the item at a given position in the data set. If that item is relocated
-         * in the data set, the ID returned for that item should be the same.
-         *
-         * @return true if this adapter's items have stable IDs
-         */
-        public final boolean hasStableIds() {
-            return mHasStableIds;
-        }
-
-        /**
-         * Called when a view created by this adapter has been recycled.
-         *
-         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
-         * needs to be attached to its parent {@link RecyclerView}. This can be because it has
-         * fallen out of visibility or a set of cached views represented by views still
-         * attached to the parent RecyclerView. If an item view has large or expensive data
-         * bound to it such as large bitmaps, this may be a good place to release those
-         * resources.</p>
-         * <p>
-         * RecyclerView calls this method right before clearing ViewHolder's internal data and
-         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
-         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
-         * its adapter position.
-         *
-         * @param holder The ViewHolder for the view being recycled
-         */
-        public void onViewRecycled(@NonNull VH holder) {
-        }
-
-        /**
-         * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
-         * due to its transient state. Upon receiving this callback, Adapter can clear the
-         * animation(s) that effect the View's transient state and return <code>true</code> so that
-         * the View can be recycled. Keep in mind that the View in question is already removed from
-         * the RecyclerView.
-         * <p>
-         * In some cases, it is acceptable to recycle a View although it has transient state. Most
-         * of the time, this is a case where the transient state will be cleared in
-         * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
-         * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
-         * value of this method to decide whether the View should be recycled or not.
-         * <p>
-         * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
-         * should never receive this callback because RecyclerView keeps those Views as children
-         * until their animations are complete. This callback is useful when children of the item
-         * views create animations which may not be easy to implement using an {@link ItemAnimator}.
-         * <p>
-         * You should <em>never</em> fix this issue by calling
-         * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
-         * <code>holder.itemView.setHasTransientState(true);</code>. Each
-         * <code>View.setHasTransientState(true)</code> call must be matched by a
-         * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
-         * may become inconsistent. You should always prefer to end or cancel animations that are
-         * triggering the transient state instead of handling it manually.
-         *
-         * @param holder The ViewHolder containing the View that could not be recycled due to its
-         *               transient state.
-         * @return True if the View should be recycled, false otherwise. Note that if this method
-         * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
-         * the View and recycle it regardless. If this method returns <code>false</code>,
-         * RecyclerView will check the View's transient state again before giving a final decision.
-         * Default implementation returns false.
-         */
-        public boolean onFailedToRecycleView(@NonNull VH holder) {
-            return false;
-        }
-
-        /**
-         * Called when a view created by this adapter has been attached to a window.
-         *
-         * <p>This can be used as a reasonable signal that the view is about to be seen
-         * by the user. If the adapter previously freed any resources in
-         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
-         * those resources should be restored here.</p>
-         *
-         * @param holder Holder of the view being attached
-         */
-        public void onViewAttachedToWindow(@NonNull VH holder) {
-        }
-
-        /**
-         * Called when a view created by this adapter has been detached from its window.
-         *
-         * <p>Becoming detached from the window is not necessarily a permanent condition;
-         * the consumer of an Adapter's views may choose to cache views offscreen while they
-         * are not visible, attaching and detaching them as appropriate.</p>
-         *
-         * @param holder Holder of the view being detached
-         */
-        public void onViewDetachedFromWindow(@NonNull VH holder) {
-        }
-
-        /**
-         * Returns true if one or more observers are attached to this adapter.
-         *
-         * @return true if this adapter has observers
-         */
-        public final boolean hasObservers() {
-            return mObservable.hasObservers();
-        }
-
-        /**
-         * Register a new observer to listen for data changes.
-         *
-         * <p>The adapter may publish a variety of events describing specific changes.
-         * Not all adapters may support all change types and some may fall back to a generic
-         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
-         * "something changed"} event if more specific data is not available.</p>
-         *
-         * <p>Components registering observers with an adapter are responsible for
-         * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
-         * unregistering} those observers when finished.</p>
-         *
-         * @param observer Observer to register
-         *
-         * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
-         */
-        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
-            mObservable.registerObserver(observer);
-        }
-
-        /**
-         * Unregister an observer currently listening for data changes.
-         *
-         * <p>The unregistered observer will no longer receive events about changes
-         * to the adapter.</p>
-         *
-         * @param observer Observer to unregister
-         *
-         * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
-         */
-        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
-            mObservable.unregisterObserver(observer);
-        }
-
-        /**
-         * Called by RecyclerView when it starts observing this Adapter.
-         * <p>
-         * Keep in mind that same adapter may be observed by multiple RecyclerViews.
-         *
-         * @param recyclerView The RecyclerView instance which started observing this adapter.
-         * @see #onDetachedFromRecyclerView(RecyclerView)
-         */
-        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
-        }
-
-        /**
-         * Called by RecyclerView when it stops observing this Adapter.
-         *
-         * @param recyclerView The RecyclerView instance which stopped observing this adapter.
-         * @see #onAttachedToRecyclerView(RecyclerView)
-         */
-        public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
-        }
-
-        /**
-         * Notify any registered observers that the data set has changed.
-         *
-         * <p>There are two different classes of data change events, item changes and structural
-         * changes. Item changes are when a single item has its data updated but no positional
-         * changes have occurred. Structural changes are when items are inserted, removed or moved
-         * within the data set.</p>
-         *
-         * <p>This event does not specify what about the data set has changed, forcing
-         * any observers to assume that all existing items and structure may no longer be valid.
-         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
-         *
-         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
-         * for adapters that report that they have {@link #hasStableIds() stable IDs} when
-         * this method is used. This can help for the purposes of animation and visual
-         * object persistence but individual item views will still need to be rebound
-         * and relaid out.</p>
-         *
-         * <p>If you are writing an adapter it will always be more efficient to use the more
-         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
-         * as a last resort.</p>
-         *
-         * @see #notifyItemChanged(int)
-         * @see #notifyItemInserted(int)
-         * @see #notifyItemRemoved(int)
-         * @see #notifyItemRangeChanged(int, int)
-         * @see #notifyItemRangeInserted(int, int)
-         * @see #notifyItemRangeRemoved(int, int)
-         */
-        public final void notifyDataSetChanged() {
-            mObservable.notifyChanged();
-        }
-
-        /**
-         * Notify any registered observers that the item at <code>position</code> has changed.
-         * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
-         *
-         * <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.
-         * The item at <code>position</code> retains the same identity.</p>
-         *
-         * @param position Position of the item that has changed
-         *
-         * @see #notifyItemRangeChanged(int, int)
-         */
-        public final void notifyItemChanged(int position) {
-            mObservable.notifyItemRangeChanged(position, 1);
-        }
-
-        /**
-         * 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.
-         * The item at <code>position</code> retains the same identity.
-         * </p>
-         *
-         * <p>
-         * Client can optionally pass a payload for partial change. These payloads will be merged
-         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
-         * item is already represented by a ViewHolder and it will be rebound to the same
-         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
-         * payloads on that item and prevent future payload until
-         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
-         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
-         * attached, the payload will be simply dropped.
-         *
-         * @param position Position of the item that has changed
-         * @param payload Optional parameter, use null to identify a "full" update
-         *
-         * @see #notifyItemRangeChanged(int, int)
-         */
-        public final void notifyItemChanged(int position, @Nullable Object payload) {
-            mObservable.notifyItemRangeChanged(position, 1, payload);
-        }
-
-        /**
-         * Notify any registered observers that the <code>itemCount</code> items starting at
-         * position <code>positionStart</code> have changed.
-         * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
-         *
-         * <p>This is an item change event, not a structural change event. It indicates that
-         * any reflection of the data in the given position range is out of date and should
-         * be updated. The items in the given range retain the same identity.</p>
-         *
-         * @param positionStart Position of the first item that has changed
-         * @param itemCount Number of items that have changed
-         *
-         * @see #notifyItemChanged(int)
-         */
-        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
-            mObservable.notifyItemRangeChanged(positionStart, itemCount);
-        }
-
-        /**
-         * Notify any registered observers that the <code>itemCount</code> items starting at
-         * position <code>positionStart</code> have changed. An optional payload can be
-         * passed to each changed item.
-         *
-         * <p>This is an item change event, not a structural change event. It indicates that any
-         * reflection of the data in the given position range is out of date and should be updated.
-         * The items in the given range retain the same identity.
-         * </p>
-         *
-         * <p>
-         * Client can optionally pass a payload for partial change. These payloads will be merged
-         * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
-         * item is already represented by a ViewHolder and it will be rebound to the same
-         * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
-         * payloads on that item and prevent future payload until
-         * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
-         * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
-         * attached, the payload will be simply dropped.
-         *
-         * @param positionStart Position of the first item that has changed
-         * @param itemCount Number of items that have changed
-         * @param payload  Optional parameter, use null to identify a "full" update
-         *
-         * @see #notifyItemChanged(int)
-         */
-        public final void notifyItemRangeChanged(int positionStart, int itemCount,
-                @Nullable Object payload) {
-            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
-        }
-
-        /**
-         * Notify any registered observers that the item reflected at <code>position</code>
-         * has been newly inserted. The item previously at <code>position</code> is now at
-         * position <code>position + 1</code>.
-         *
-         * <p>This is a structural change event. Representations of other existing items in the
-         * data set are still considered up to date and will not be rebound, though their
-         * positions may be altered.</p>
-         *
-         * @param position Position of the newly inserted item in the data set
-         *
-         * @see #notifyItemRangeInserted(int, int)
-         */
-        public final void notifyItemInserted(int position) {
-            mObservable.notifyItemRangeInserted(position, 1);
-        }
-
-        /**
-         * Notify any registered observers that the item reflected at <code>fromPosition</code>
-         * has been moved to <code>toPosition</code>.
-         *
-         * <p>This is a structural change event. Representations of other existing items in the
-         * data set are still considered up to date and will not be rebound, though their
-         * positions may be altered.</p>
-         *
-         * @param fromPosition Previous position of the item.
-         * @param toPosition New position of the item.
-         */
-        public final void notifyItemMoved(int fromPosition, int toPosition) {
-            mObservable.notifyItemMoved(fromPosition, toPosition);
-        }
-
-        /**
-         * Notify any registered observers that the currently reflected <code>itemCount</code>
-         * items starting at <code>positionStart</code> have been newly inserted. The items
-         * previously located at <code>positionStart</code> and beyond can now be found starting
-         * at position <code>positionStart + itemCount</code>.
-         *
-         * <p>This is a structural change event. Representations of other existing items in the
-         * data set are still considered up to date and will not be rebound, though their positions
-         * may be altered.</p>
-         *
-         * @param positionStart Position of the first item that was inserted
-         * @param itemCount Number of items inserted
-         *
-         * @see #notifyItemInserted(int)
-         */
-        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
-            mObservable.notifyItemRangeInserted(positionStart, itemCount);
-        }
-
-        /**
-         * Notify any registered observers that the item previously located at <code>position</code>
-         * has been removed from the data set. The items previously located at and after
-         * <code>position</code> may now be found at <code>oldPosition - 1</code>.
-         *
-         * <p>This is a structural change event. Representations of other existing items in the
-         * data set are still considered up to date and will not be rebound, though their positions
-         * may be altered.</p>
-         *
-         * @param position Position of the item that has now been removed
-         *
-         * @see #notifyItemRangeRemoved(int, int)
-         */
-        public final void notifyItemRemoved(int position) {
-            mObservable.notifyItemRangeRemoved(position, 1);
-        }
-
-        /**
-         * Notify any registered observers that the <code>itemCount</code> items previously
-         * located at <code>positionStart</code> have been removed from the data set. The items
-         * previously located at and after <code>positionStart + itemCount</code> may now be found
-         * at <code>oldPosition - itemCount</code>.
-         *
-         * <p>This is a structural change event. Representations of other existing items in the data
-         * set are still considered up to date and will not be rebound, though their positions
-         * may be altered.</p>
-         *
-         * @param positionStart Previous position of the first item that was removed
-         * @param itemCount Number of items removed from the data set
-         */
-        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
-            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
-        }
-    }
-
-    void dispatchChildDetached(View child) {
-        final ViewHolder viewHolder = getChildViewHolderInt(child);
-        onChildDetachedFromWindow(child);
-        if (mAdapter != null && viewHolder != null) {
-            mAdapter.onViewDetachedFromWindow(viewHolder);
-        }
-        if (mOnChildAttachStateListeners != null) {
-            final int cnt = mOnChildAttachStateListeners.size();
-            for (int i = cnt - 1; i >= 0; i--) {
-                mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
-            }
-        }
-    }
-
-    void dispatchChildAttached(View child) {
-        final ViewHolder viewHolder = getChildViewHolderInt(child);
-        onChildAttachedToWindow(child);
-        if (mAdapter != null && viewHolder != null) {
-            mAdapter.onViewAttachedToWindow(viewHolder);
-        }
-        if (mOnChildAttachStateListeners != null) {
-            final int cnt = mOnChildAttachStateListeners.size();
-            for (int i = cnt - 1; i >= 0; i--) {
-                mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
-            }
-        }
-    }
-
-    /**
-     * A <code>LayoutManager</code> is responsible for measuring and positioning item views
-     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
-     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
-     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
-     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
-     * layout managers are provided for general use.
-     * <p/>
-     * If the LayoutManager specifies a default constructor or one with the signature
-     * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
-     * instantiate and set the LayoutManager when being inflated. Most used properties can
-     * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
-     * a LayoutManager specifies both constructors, the non-default constructor will take
-     * precedence.
-     *
-     */
-    public abstract static class LayoutManager {
-        ChildHelper mChildHelper;
-        RecyclerView mRecyclerView;
-
-        /**
-         * The callback used for retrieving information about a RecyclerView and its children in the
-         * horizontal direction.
-         */
-        private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
-                new ViewBoundsCheck.Callback() {
-                    @Override
-                    public int getChildCount() {
-                        return LayoutManager.this.getChildCount();
-                    }
-
-                    @Override
-                    public View getParent() {
-                        return mRecyclerView;
-                    }
-
-                    @Override
-                    public View getChildAt(int index) {
-                        return LayoutManager.this.getChildAt(index);
-                    }
-
-                    @Override
-                    public int getParentStart() {
-                        return LayoutManager.this.getPaddingLeft();
-                    }
-
-                    @Override
-                    public int getParentEnd() {
-                        return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
-                    }
-
-                    @Override
-                    public int getChildStart(View view) {
-                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                                view.getLayoutParams();
-                        return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
-                    }
-
-                    @Override
-                    public int getChildEnd(View view) {
-                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                                view.getLayoutParams();
-                        return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
-                    }
-                };
-
-        /**
-         * The callback used for retrieving information about a RecyclerView and its children in the
-         * vertical direction.
-         */
-        private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
-                new ViewBoundsCheck.Callback() {
-                    @Override
-                    public int getChildCount() {
-                        return LayoutManager.this.getChildCount();
-                    }
-
-                    @Override
-                    public View getParent() {
-                        return mRecyclerView;
-                    }
-
-                    @Override
-                    public View getChildAt(int index) {
-                        return LayoutManager.this.getChildAt(index);
-                    }
-
-                    @Override
-                    public int getParentStart() {
-                        return LayoutManager.this.getPaddingTop();
-                    }
-
-                    @Override
-                    public int getParentEnd() {
-                        return LayoutManager.this.getHeight()
-                                - LayoutManager.this.getPaddingBottom();
-                    }
-
-                    @Override
-                    public int getChildStart(View view) {
-                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                                view.getLayoutParams();
-                        return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
-                    }
-
-                    @Override
-                    public int getChildEnd(View view) {
-                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
-                                view.getLayoutParams();
-                        return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
-                    }
-                };
-
-        /**
-         * Utility objects used to check the boundaries of children against their parent
-         * RecyclerView.
-         * @see #isViewPartiallyVisible(View, boolean, boolean),
-         * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
-         * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
-         */
-        ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
-        ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
-
-        @Nullable
-        SmoothScroller mSmoothScroller;
-
-        boolean mRequestedSimpleAnimations = false;
-
-        boolean mIsAttachedToWindow = false;
-
-        boolean mAutoMeasure = false;
-
-        /**
-         * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
-         * if the space that will be given to it is already larger than what it has measured before.
-         */
-        private boolean mMeasurementCacheEnabled = true;
-
-        private boolean mItemPrefetchEnabled = true;
-
-        /**
-         * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
-         * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
-         * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
-         *
-         * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
-         * will be reset upon layout to prevent initial prefetches (often large, since they're
-         * proportional to expected child count) from expanding cache permanently.
-         */
-        int mPrefetchMaxCountObserved;
-
-        /**
-         * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
-         */
-        boolean mPrefetchMaxObservedInInitialPrefetch;
-
-        /**
-         * These measure specs might be the measure specs that were passed into RecyclerView's
-         * onMeasure method OR fake measure specs created by the RecyclerView.
-         * For example, when a layout is run, RecyclerView always sets these specs to be
-         * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
-         * <p>
-         * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
-         * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
-         * corrupt values. Older platforms have no responsibility to provide a size if they set
-         * mode to unspecified.
-         */
-        private int mWidthMode, mHeightMode;
-        private int mWidth, mHeight;
-
-
-        /**
-         * Interface for LayoutManagers to request items to be prefetched, based on position, with
-         * specified distance from viewport, which indicates priority.
-         *
-         * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
-         * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
-         */
-        public interface LayoutPrefetchRegistry {
-            /**
-             * Requests an an item to be prefetched, based on position, with a specified distance,
-             * indicating priority.
-             *
-             * @param layoutPosition Position of the item to prefetch.
-             * @param pixelDistance Distance from the current viewport to the bounds of the item,
-             *                      must be non-negative.
-             */
-            void addPosition(int layoutPosition, int pixelDistance);
-        }
-
-        void setRecyclerView(RecyclerView recyclerView) {
-            if (recyclerView == null) {
-                mRecyclerView = null;
-                mChildHelper = null;
-                mWidth = 0;
-                mHeight = 0;
-            } else {
-                mRecyclerView = recyclerView;
-                mChildHelper = recyclerView.mChildHelper;
-                mWidth = recyclerView.getWidth();
-                mHeight = recyclerView.getHeight();
-            }
-            mWidthMode = MeasureSpec.EXACTLY;
-            mHeightMode = MeasureSpec.EXACTLY;
-        }
-
-        void setMeasureSpecs(int wSpec, int hSpec) {
-            mWidth = MeasureSpec.getSize(wSpec);
-            mWidthMode = MeasureSpec.getMode(wSpec);
-            if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
-                mWidth = 0;
-            }
-
-            mHeight = MeasureSpec.getSize(hSpec);
-            mHeightMode = MeasureSpec.getMode(hSpec);
-            if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
-                mHeight = 0;
-            }
-        }
-
-        /**
-         * Called after a layout is calculated during a measure pass when using auto-measure.
-         * <p>
-         * It simply traverses all children to calculate a bounding box then calls
-         * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
-         * if they need to handle the bounding box differently.
-         * <p>
-         * For example, GridLayoutManager override that method to ensure that even if a column is
-         * empty, the GridLayoutManager still measures wide enough to include it.
-         *
-         * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
-         * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
-         */
-        void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
-            final int count = getChildCount();
-            if (count == 0) {
-                mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
-                return;
-            }
-            int minX = Integer.MAX_VALUE;
-            int minY = Integer.MAX_VALUE;
-            int maxX = Integer.MIN_VALUE;
-            int maxY = Integer.MIN_VALUE;
-
-            for (int i = 0; i < count; i++) {
-                View child = getChildAt(i);
-                final Rect bounds = mRecyclerView.mTempRect;
-                getDecoratedBoundsWithMargins(child, bounds);
-                if (bounds.left < minX) {
-                    minX = bounds.left;
-                }
-                if (bounds.right > maxX) {
-                    maxX = bounds.right;
-                }
-                if (bounds.top < minY) {
-                    minY = bounds.top;
-                }
-                if (bounds.bottom > maxY) {
-                    maxY = bounds.bottom;
-                }
-            }
-            mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
-            setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
-        }
-
-        /**
-         * Sets the measured dimensions from the given bounding box of the children and the
-         * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
-         * called after the RecyclerView calls
-         * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
-         * <p>
-         * This method should call {@link #setMeasuredDimension(int, int)}.
-         * <p>
-         * The default implementation adds the RecyclerView's padding to the given bounding box
-         * then caps the value to be within the given measurement specs.
-         * <p>
-         * This method is only called if the LayoutManager opted into the auto measurement API.
-         *
-         * @param childrenBounds The bounding box of all children
-         * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
-         * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
-         *
-         * @see #setAutoMeasureEnabled(boolean)
-         */
-        public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
-            int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
-            int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
-            int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
-            int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
-            setMeasuredDimension(width, height);
-        }
-
-        /**
-         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
-         */
-        public void requestLayout() {
-            if (mRecyclerView != null) {
-                mRecyclerView.requestLayout();
-            }
-        }
-
-        /**
-         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
-         * {@link IllegalStateException} if it <b>is not</b>.
-         *
-         * @param message The message for the exception. Can be null.
-         * @see #assertNotInLayoutOrScroll(String)
-         */
-        public void assertInLayoutOrScroll(String message) {
-            if (mRecyclerView != null) {
-                mRecyclerView.assertInLayoutOrScroll(message);
-            }
-        }
-
-        /**
-         * Chooses a size from the given specs and parameters that is closest to the desired size
-         * and also complies with the spec.
-         *
-         * @param spec The measureSpec
-         * @param desired The preferred measurement
-         * @param min The minimum value
-         *
-         * @return A size that fits to the given specs
-         */
-        public static int chooseSize(int spec, int desired, int min) {
-            final int mode = View.MeasureSpec.getMode(spec);
-            final int size = View.MeasureSpec.getSize(spec);
-            switch (mode) {
-                case View.MeasureSpec.EXACTLY:
-                    return size;
-                case View.MeasureSpec.AT_MOST:
-                    return Math.min(size, Math.max(desired, min));
-                case View.MeasureSpec.UNSPECIFIED:
-                default:
-                    return Math.max(desired, min);
-            }
-        }
-
-        /**
-         * Checks if RecyclerView is in the middle of a layout or scroll and throws an
-         * {@link IllegalStateException} if it <b>is</b>.
-         *
-         * @param message The message for the exception. Can be null.
-         * @see #assertInLayoutOrScroll(String)
-         */
-        public void assertNotInLayoutOrScroll(String message) {
-            if (mRecyclerView != null) {
-                mRecyclerView.assertNotInLayoutOrScroll(message);
-            }
-        }
-
-        /**
-         * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
-         * wants to handle the layout measurements itself.
-         * <p>
-         * This method is usually called by the LayoutManager with value {@code true} if it wants
-         * to support {@link ViewGroup.LayoutParams#WRAP_CONTENT}. If you are using a public
-         * LayoutManager but want to customize the measurement logic, you can call this method with
-         * {@code false} and override {@link LayoutManager#onMeasure(Recycler, State, int, int)} to
-         * implement your custom measurement logic.
-         * <p>
-         * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
-         * handle various specs provided by the RecyclerView's parent.
-         * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
-         * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
-         * on children's positions. It does this while supporting all existing animation
-         * capabilities of the RecyclerView.
-         * <p>
-         * AutoMeasure works as follows:
-         * <ol>
-         * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
-         * the framework LayoutManagers use {@code auto-measure}.</li>
-         * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
-         * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
-         * doing any layout calculation.</li>
-         * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
-         * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
-         * decide whether to run a predictive layout or not. If it decides to do so, it will first
-         * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
-         * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
-         * return the width and height of the RecyclerView as of the last layout calculation.
-         * <p>
-         * After handling the predictive case, RecyclerView will call
-         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
-         * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
-         * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
-         * {@link #getWidth()} and {@link #getWidthMode()}.</li>
-         * <li>After the layout calculation, RecyclerView sets the measured width & height by
-         * calculating the bounding box for the children (+ RecyclerView's padding). The
-         * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
-         * different values. For instance, GridLayoutManager overrides this value to handle the case
-         * where if it is vertical and has 3 columns but only 2 items, it should still measure its
-         * width to fit 3 items, not 2.</li>
-         * <li>Any following on measure call to the RecyclerView will run
-         * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
-         * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
-         * take care of which views are actually added / removed / moved / changed for animations so
-         * that the LayoutManager should not worry about them and handle each
-         * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
-         * </li>
-         * <li>When measure is complete and RecyclerView's
-         * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
-         * whether it already did layout calculations during the measure pass and if so, it re-uses
-         * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
-         * if the last measure spec was different from the final dimensions or adapter contents
-         * have changed between the measure call and the layout call.</li>
-         * <li>Finally, animations are calculated and run as usual.</li>
-         * </ol>
-         *
-         * @param enabled <code>True</code> if the Layout should be measured by the
-         *                             RecyclerView, <code>false</code> if the LayoutManager wants
-         *                             to measure itself.
-         *
-         * @see #setMeasuredDimension(Rect, int, int)
-         * @see #isAutoMeasureEnabled()
-         */
-        public void setAutoMeasureEnabled(boolean enabled) {
-            mAutoMeasure = enabled;
-        }
-
-        /**
-         * Returns whether the LayoutManager uses the automatic measurement API or not.
-         *
-         * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
-         * <code>false</code> if it measures itself.
-         *
-         * @see #setAutoMeasureEnabled(boolean)
-         */
-        public boolean isAutoMeasureEnabled() {
-            return mAutoMeasure;
-        }
-
-        /**
-         * Returns whether this LayoutManager supports "predictive item animations".
-         * <p>
-         * "Predictive item animations" are automatically created animations that show
-         * where items came from, and where they are going to, as items are added, removed,
-         * or moved within a layout.
-         * <p>
-         * A LayoutManager wishing to support predictive item animations must override this
-         * method to return true (the default implementation returns false) and must obey certain
-         * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
-         * <p>
-         * Whether item animations actually occur in a RecyclerView is actually determined by both
-         * the return value from this method and the
-         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
-         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
-         * method returns false, then only "simple item animations" will be enabled in the
-         * RecyclerView, in which views whose position are changing are simply faded in/out. If the
-         * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
-         * item animations will be enabled in the RecyclerView.
-         *
-         * @return true if this LayoutManager supports predictive item animations, false otherwise.
-         */
-        public boolean supportsPredictiveItemAnimations() {
-            return false;
-        }
-
-        /**
-         * Sets whether the LayoutManager should be queried for views outside of
-         * its viewport while the UI thread is idle between frames.
-         *
-         * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
-         * view system traversals on devices running API 21 or greater. Default value is true.</p>
-         *
-         * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
-         * to RenderThread and the starting up its next frame at the next VSync pulse. By
-         * prefetching out of window views in this time period, delays from inflation and view
-         * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
-         *
-         * <p>While prefetch is enabled, it will have the side effect of expanding the effective
-         * size of the View cache to hold prefetched views.</p>
-         *
-         * @param enabled <code>True</code> if items should be prefetched in between traversals.
-         *
-         * @see #isItemPrefetchEnabled()
-         */
-        public final void setItemPrefetchEnabled(boolean enabled) {
-            if (enabled != mItemPrefetchEnabled) {
-                mItemPrefetchEnabled = enabled;
-                mPrefetchMaxCountObserved = 0;
-                if (mRecyclerView != null) {
-                    mRecyclerView.mRecycler.updateViewCacheSize();
-                }
-            }
-        }
-
-        /**
-         * Sets whether the LayoutManager should be queried for views outside of
-         * its viewport while the UI thread is idle between frames.
-         *
-         * @see #setItemPrefetchEnabled(boolean)
-         *
-         * @return true if item prefetch is enabled, false otherwise
-         */
-        public final boolean isItemPrefetchEnabled() {
-            return mItemPrefetchEnabled;
-        }
-
-        /**
-         * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
-         *
-         * <p>If item prefetch is enabled, this method is called in between traversals to gather
-         * 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
-         * scroll or layout.</p>
-         *
-         * @param dx X movement component.
-         * @param dy Y movement component.
-         * @param state State of RecyclerView
-         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
-         *
-         * @see #isItemPrefetchEnabled()
-         * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
-         */
-        public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
-                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
-
-        /**
-         * Gather all positions from the LayoutManager to be prefetched in preperation for its
-         * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
-         *
-         * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
-         *
-         * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
-         * LayoutManager, this method is called in between draw traversals to gather
-         * which positions this LayoutManager will first need, once it appears on the screen.</p>
-         *
-         * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
-         * 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
-         * scroll or layout.</p>
-         *
-         * @param adapterItemCount number of items in the associated adapter.
-         * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
-         *
-         * @see #isItemPrefetchEnabled()
-         * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
-         */
-        public void collectInitialPrefetchPositions(int adapterItemCount,
-                LayoutPrefetchRegistry layoutPrefetchRegistry) {}
-
-        void dispatchAttachedToWindow(RecyclerView view) {
-            mIsAttachedToWindow = true;
-            onAttachedToWindow(view);
-        }
-
-        void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
-            mIsAttachedToWindow = false;
-            onDetachedFromWindow(view, recycler);
-        }
-
-        /**
-         * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
-         * to a window.
-         *
-         * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
-         * is attached to window.
-         */
-        public boolean isAttachedToWindow() {
-            return mIsAttachedToWindow;
-        }
-
-        /**
-         * Causes the Runnable to execute on the next animation time step.
-         * The runnable will be run on the user interface thread.
-         * <p>
-         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
-         *
-         * @param action The Runnable that will be executed.
-         *
-         * @see #removeCallbacks
-         */
-        public void postOnAnimation(Runnable action) {
-            if (mRecyclerView != null) {
-                ViewCompat.postOnAnimation(mRecyclerView, action);
-            }
-        }
-
-        /**
-         * Removes the specified Runnable from the message queue.
-         * <p>
-         * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
-         *
-         * @param action The Runnable to remove from the message handling queue
-         *
-         * @return true if RecyclerView could ask the Handler to remove the Runnable,
-         *         false otherwise. When the returned value is true, the Runnable
-         *         may or may not have been actually removed from the message queue
-         *         (for instance, if the Runnable was not in the queue already.)
-         *
-         * @see #postOnAnimation
-         */
-        public boolean removeCallbacks(Runnable action) {
-            if (mRecyclerView != null) {
-                return mRecyclerView.removeCallbacks(action);
-            }
-            return false;
-        }
-        /**
-         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
-         * is attached to a window.
-         * <p>
-         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
-         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
-         * not requested on the RecyclerView while it was detached.
-         * <p>
-         * Subclass implementations should always call through to the superclass implementation.
-         *
-         * @param view The RecyclerView this LayoutManager is bound to
-         *
-         * @see #onDetachedFromWindow(RecyclerView, Recycler)
-         */
-        @CallSuper
-        public void onAttachedToWindow(RecyclerView view) {
-        }
-
-        /**
-         * @deprecated
-         * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
-         */
-        @Deprecated
-        public void onDetachedFromWindow(RecyclerView view) {
-
-        }
-
-        /**
-         * Called when this LayoutManager is detached from its parent RecyclerView or when
-         * its parent RecyclerView is detached from its window.
-         * <p>
-         * LayoutManager should clear all of its View references as another LayoutManager might be
-         * assigned to the RecyclerView.
-         * <p>
-         * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
-         * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
-         * not requested on the RecyclerView while it was detached.
-         * <p>
-         * If your LayoutManager has View references that it cleans in on-detach, it should also
-         * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
-         * RecyclerView is re-attached.
-         * <p>
-         * Subclass implementations should always call through to the superclass implementation.
-         *
-         * @param view The RecyclerView this LayoutManager is bound to
-         * @param recycler The recycler to use if you prefer to recycle your children instead of
-         *                 keeping them around.
-         *
-         * @see #onAttachedToWindow(RecyclerView)
-         */
-        @CallSuper
-        public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
-            onDetachedFromWindow(view);
-        }
-
-        /**
-         * Check if the RecyclerView is configured to clip child views to its padding.
-         *
-         * @return true if this RecyclerView clips children to its padding, false otherwise
-         */
-        public boolean getClipToPadding() {
-            return mRecyclerView != null && mRecyclerView.mClipToPadding;
-        }
-
-        /**
-         * Lay out all relevant child views from the given adapter.
-         *
-         * The LayoutManager is in charge of the behavior of item animations. By default,
-         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
-         * item animations are enabled. This means that add/remove operations on the
-         * adapter will result in animations to add new or appearing items, removed or
-         * disappearing items, and moved items. If a LayoutManager returns false from
-         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
-         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
-         * RecyclerView will have enough information to run those animations in a simple
-         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
-         * simply fade views in and out, whether they are actually added/removed or whether
-         * they are moved on or off the screen due to other add/remove operations.
-         *
-         * <p>A LayoutManager wanting a better item animation experience, where items can be
-         * animated onto and off of the screen according to where the items exist when they
-         * are not on screen, then the LayoutManager should return true from
-         * {@link #supportsPredictiveItemAnimations()} and add additional logic to
-         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
-         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
-         * once as a "pre" layout step to determine where items would have been prior to
-         * a real layout, and again to do the "real" layout. In the pre-layout phase,
-         * items will remember their pre-layout positions to allow them to be laid out
-         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
-         * be returned from the scrap to help determine correct placement of other items.
-         * These removed items should not be added to the child list, but should be used
-         * to help calculate correct positioning of other views, including views that
-         * were not previously onscreen (referred to as APPEARING views), but whose
-         * pre-layout offscreen position can be determined given the extra
-         * information about the pre-layout removed views.</p>
-         *
-         * <p>The second layout pass is the real layout in which only non-removed views
-         * will be used. The only additional requirement during this pass is, if
-         * {@link #supportsPredictiveItemAnimations()} returns true, to note which
-         * views exist in the child list prior to layout and which are not there after
-         * layout (referred to as DISAPPEARING views), and to position/layout those views
-         * appropriately, without regard to the actual bounds of the RecyclerView. This allows
-         * the animation system to know the location to which to animate these disappearing
-         * views.</p>
-         *
-         * <p>The default LayoutManager implementations for RecyclerView handle all of these
-         * requirements for animations already. Clients of RecyclerView can either use one
-         * of these layout managers directly or look at their implementations of
-         * onLayoutChildren() to see how they account for the APPEARING and
-         * DISAPPEARING views.</p>
-         *
-         * @param recycler         Recycler to use for fetching potentially cached views for a
-         *                         position
-         * @param state            Transient state of RecyclerView
-         */
-        public void onLayoutChildren(Recycler recycler, State state) {
-            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
-        }
-
-        /**
-         * Called after a full layout calculation is finished. The layout calculation may include
-         * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
-         * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
-         * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
-         * <p>
-         * This is a good place for the LayoutManager to do some cleanup like pending scroll
-         * position, saved state etc.
-         *
-         * @param state Transient state of RecyclerView
-         */
-        public void onLayoutCompleted(State state) {
-        }
-
-        /**
-         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
-         *
-         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
-         * to store extra information specific to the layout. Client code should subclass
-         * {@link RecyclerView.LayoutParams} for this purpose.</p>
-         *
-         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
-         * you must also override
-         * {@link #checkLayoutParams(LayoutParams)},
-         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
-         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
-         *
-         * @return A new LayoutParams for a child view
-         */
-        public abstract LayoutParams generateDefaultLayoutParams();
-
-        /**
-         * Determines the validity of the supplied LayoutParams object.
-         *
-         * <p>This should check to make sure that the object is of the correct type
-         * and all values are within acceptable ranges. The default implementation
-         * returns <code>true</code> for non-null params.</p>
-         *
-         * @param lp LayoutParams object to check
-         * @return true if this LayoutParams object is valid, false otherwise
-         */
-        public boolean checkLayoutParams(LayoutParams lp) {
-            return lp != null;
-        }
-
-        /**
-         * Create a LayoutParams object suitable for this LayoutManager, copying relevant
-         * values from the supplied LayoutParams object if possible.
-         *
-         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
-         * you must also override
-         * {@link #checkLayoutParams(LayoutParams)},
-         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
-         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
-         *
-         * @param lp Source LayoutParams object to copy values from
-         * @return a new LayoutParams object
-         */
-        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
-            if (lp instanceof LayoutParams) {
-                return new LayoutParams((LayoutParams) lp);
-            } else if (lp instanceof MarginLayoutParams) {
-                return new LayoutParams((MarginLayoutParams) lp);
-            } else {
-                return new LayoutParams(lp);
-            }
-        }
-
-        /**
-         * Create a LayoutParams object suitable for this LayoutManager from
-         * an inflated layout resource.
-         *
-         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
-         * you must also override
-         * {@link #checkLayoutParams(LayoutParams)},
-         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
-         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
-         *
-         * @param c Context for obtaining styled attributes
-         * @param attrs AttributeSet describing the supplied arguments
-         * @return a new LayoutParams object
-         */
-        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
-            return new LayoutParams(c, attrs);
-        }
-
-        /**
-         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
-         * The default implementation does nothing and returns 0.
-         *
-         * @param dx            distance to scroll by in pixels. X increases as scroll position
-         *                      approaches the right.
-         * @param recycler      Recycler to use for fetching potentially cached views for a
-         *                      position
-         * @param state         Transient state of RecyclerView
-         * @return The actual distance scrolled. The return value will be negative if dx was
-         * negative and scrolling proceeeded in that direction.
-         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
-         */
-        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
-            return 0;
-        }
-
-        /**
-         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
-         * The default implementation does nothing and returns 0.
-         *
-         * @param dy            distance to scroll in pixels. Y increases as scroll position
-         *                      approaches the bottom.
-         * @param recycler      Recycler to use for fetching potentially cached views for a
-         *                      position
-         * @param state         Transient state of RecyclerView
-         * @return The actual distance scrolled. The return value will be negative if dy was
-         * negative and scrolling proceeeded in that direction.
-         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
-         */
-        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
-            return 0;
-        }
-
-        /**
-         * Query if horizontal scrolling is currently supported. The default implementation
-         * returns false.
-         *
-         * @return True if this LayoutManager can scroll the current contents horizontally
-         */
-        public boolean canScrollHorizontally() {
-            return false;
-        }
-
-        /**
-         * Query if vertical scrolling is currently supported. The default implementation
-         * returns false.
-         *
-         * @return True if this LayoutManager can scroll the current contents vertically
-         */
-        public boolean canScrollVertically() {
-            return false;
-        }
-
-        /**
-         * Scroll to the specified adapter position.
-         *
-         * Actual position of the item on the screen depends on the LayoutManager implementation.
-         * @param position Scroll to this adapter position.
-         */
-        public void scrollToPosition(int position) {
-            if (DEBUG) {
-                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
-            }
-        }
-
-        /**
-         * <p>Smooth scroll to the specified adapter position.</p>
-         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
-         * instance and call {@link #startSmoothScroll(SmoothScroller)}.
-         * </p>
-         * @param recyclerView The RecyclerView to which this layout manager is attached
-         * @param state    Current State of RecyclerView
-         * @param position Scroll to this adapter position.
-         */
-        public void smoothScrollToPosition(RecyclerView recyclerView, State state,
-                int position) {
-            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
-        }
-
-        /**
-         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
-         * <p>Calling this method will cancel any previous smooth scroll request.</p>
-         * @param smoothScroller Instance which defines how smooth scroll should be animated
-         */
-        public void startSmoothScroll(SmoothScroller smoothScroller) {
-            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
-                    && mSmoothScroller.isRunning()) {
-                mSmoothScroller.stop();
-            }
-            mSmoothScroller = smoothScroller;
-            mSmoothScroller.start(mRecyclerView, this);
-        }
-
-        /**
-         * @return true if RecycylerView is currently in the state of smooth scrolling.
-         */
-        public boolean isSmoothScrolling() {
-            return mSmoothScroller != null && mSmoothScroller.isRunning();
-        }
-
-
-        /**
-         * Returns the resolved layout direction for this RecyclerView.
-         *
-         * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
-         * direction is RTL or returns
-         * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
-         * is not RTL.
-         */
-        public int getLayoutDirection() {
-            return ViewCompat.getLayoutDirection(mRecyclerView);
-        }
-
-        /**
-         * Ends all animations on the view created by the {@link ItemAnimator}.
-         *
-         * @param view The View for which the animations should be ended.
-         * @see RecyclerView.ItemAnimator#endAnimations()
-         */
-        public void endAnimation(View view) {
-            if (mRecyclerView.mItemAnimator != null) {
-                mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
-            }
-        }
-
-        /**
-         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
-         * to the layout that is known to be going away, either because it has been
-         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
-         * visible portion of the container but is being laid out in order to inform RecyclerView
-         * in how to animate the item out of view.
-         * <p>
-         * Views added via this method are going to be invisible to LayoutManager after the
-         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
-         * or won't be included in {@link #getChildCount()} method.
-         *
-         * @param child View to add and then remove with animation.
-         */
-        public void addDisappearingView(View child) {
-            addDisappearingView(child, -1);
-        }
-
-        /**
-         * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
-         * to the layout that is known to be going away, either because it has been
-         * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
-         * visible portion of the container but is being laid out in order to inform RecyclerView
-         * in how to animate the item out of view.
-         * <p>
-         * Views added via this method are going to be invisible to LayoutManager after the
-         * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
-         * or won't be included in {@link #getChildCount()} method.
-         *
-         * @param child View to add and then remove with animation.
-         * @param index Index of the view.
-         */
-        public void addDisappearingView(View child, int index) {
-            addViewInt(child, index, true);
-        }
-
-        /**
-         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
-         * use this method to add views obtained from a {@link Recycler} using
-         * {@link Recycler#getViewForPosition(int)}.
-         *
-         * @param child View to add
-         */
-        public void addView(View child) {
-            addView(child, -1);
-        }
-
-        /**
-         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
-         * use this method to add views obtained from a {@link Recycler} using
-         * {@link Recycler#getViewForPosition(int)}.
-         *
-         * @param child View to add
-         * @param index Index to add child at
-         */
-        public void addView(View child, int index) {
-            addViewInt(child, index, false);
-        }
-
-        private void addViewInt(View child, int index, boolean disappearing) {
-            final ViewHolder holder = getChildViewHolderInt(child);
-            if (disappearing || holder.isRemoved()) {
-                // these views will be hidden at the end of the layout pass.
-                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
-            } else {
-                // This may look like unnecessary but may happen if layout manager supports
-                // predictive layouts and adapter removed then re-added the same item.
-                // In this case, added version will be visible in the post layout (because add is
-                // deferred) but RV will still bind it to the same View.
-                // So if a View re-appears in post layout pass, remove it from disappearing list.
-                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
-            }
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (holder.wasReturnedFromScrap() || holder.isScrap()) {
-                if (holder.isScrap()) {
-                    holder.unScrap();
-                } else {
-                    holder.clearReturnedFromScrapFlag();
-                }
-                mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
-                if (DISPATCH_TEMP_DETACH) {
-                    ViewCompat.dispatchFinishTemporaryDetach(child);
-                }
-            } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
-                // ensure in correct position
-                int currentIndex = mChildHelper.indexOfChild(child);
-                if (index == -1) {
-                    index = mChildHelper.getChildCount();
-                }
-                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.exceptionLabel());
-                }
-                if (currentIndex != index) {
-                    mRecyclerView.mLayout.moveView(currentIndex, index);
-                }
-            } else {
-                mChildHelper.addView(child, index, false);
-                lp.mInsetsDirty = true;
-                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
-                    mSmoothScroller.onChildAttachedToWindow(child);
-                }
-            }
-            if (lp.mPendingInvalidate) {
-                if (DEBUG) {
-                    Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
-                }
-                holder.itemView.invalidate();
-                lp.mPendingInvalidate = false;
-            }
-        }
-
-        /**
-         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
-         * use this method to completely remove a child view that is no longer needed.
-         * LayoutManagers should strongly consider recycling removed views using
-         * {@link Recycler#recycleView(android.view.View)}.
-         *
-         * @param child View to remove
-         */
-        public void removeView(View child) {
-            mChildHelper.removeView(child);
-        }
-
-        /**
-         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
-         * use this method to completely remove a child view that is no longer needed.
-         * LayoutManagers should strongly consider recycling removed views using
-         * {@link Recycler#recycleView(android.view.View)}.
-         *
-         * @param index Index of the child view to remove
-         */
-        public void removeViewAt(int index) {
-            final View child = getChildAt(index);
-            if (child != null) {
-                mChildHelper.removeViewAt(index);
-            }
-        }
-
-        /**
-         * Remove all views from the currently attached RecyclerView. This will not recycle
-         * any of the affected views; the LayoutManager is responsible for doing so if desired.
-         */
-        public void removeAllViews() {
-            // Only remove non-animating views
-            final int childCount = getChildCount();
-            for (int i = childCount - 1; i >= 0; i--) {
-                mChildHelper.removeViewAt(i);
-            }
-        }
-
-        /**
-         * Returns offset of the RecyclerView's text baseline from the its top boundary.
-         *
-         * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
-         * there is no baseline.
-         */
-        public int getBaseline() {
-            return -1;
-        }
-
-        /**
-         * Returns the adapter position of the item represented by the given View. This does not
-         * contain any adapter changes that might have happened after the last layout.
-         *
-         * @param view The view to query
-         * @return The adapter position of the item which is rendered by this View.
-         */
-        public int getPosition(View view) {
-            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
-        }
-
-        /**
-         * Returns the View type defined by the adapter.
-         *
-         * @param view The view to query
-         * @return The type of the view assigned by the adapter.
-         */
-        public int getItemViewType(View view) {
-            return getChildViewHolderInt(view).getItemViewType();
-        }
-
-        /**
-         * Traverses the ancestors of the given view and returns the item view that contains it
-         * and also a direct child of the LayoutManager.
-         * <p>
-         * Note that this method may return null if the view is a child of the RecyclerView but
-         * not a child of the LayoutManager (e.g. running a disappear animation).
-         *
-         * @param view The view that is a descendant of the LayoutManager.
-         *
-         * @return The direct child of the LayoutManager which contains the given view or null if
-         * the provided view is not a descendant of this LayoutManager.
-         *
-         * @see RecyclerView#getChildViewHolder(View)
-         * @see RecyclerView#findContainingViewHolder(View)
-         */
-        @Nullable
-        public View findContainingItemView(View view) {
-            if (mRecyclerView == null) {
-                return null;
-            }
-            View found = mRecyclerView.findContainingItemView(view);
-            if (found == null) {
-                return null;
-            }
-            if (mChildHelper.isHidden(found)) {
-                return null;
-            }
-            return found;
-        }
-
-        /**
-         * Finds the view which represents the given adapter position.
-         * <p>
-         * This method traverses each child since it has no information about child order.
-         * Override this method to improve performance if your LayoutManager keeps data about
-         * child views.
-         * <p>
-         * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
-         *
-         * @param position Position of the item in adapter
-         * @return The child view that represents the given position or null if the position is not
-         * laid out
-         */
-        public View findViewByPosition(int position) {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                ViewHolder vh = getChildViewHolderInt(child);
-                if (vh == null) {
-                    continue;
-                }
-                if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
-                        && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
-                    return child;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Temporarily detach a child view.
-         *
-         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
-         * views currently attached to the RecyclerView. Generally LayoutManager implementations
-         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
-         * so that the detached view may be rebound and reused.</p>
-         *
-         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
-         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
-         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
-         * before the LayoutManager entry point method called by RecyclerView returns.</p>
-         *
-         * @param child Child to detach
-         */
-        public void detachView(View child) {
-            final int ind = mChildHelper.indexOfChild(child);
-            if (ind >= 0) {
-                detachViewInternal(ind, child);
-            }
-        }
-
-        /**
-         * Temporarily detach a child view.
-         *
-         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
-         * views currently attached to the RecyclerView. Generally LayoutManager implementations
-         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
-         * so that the detached view may be rebound and reused.</p>
-         *
-         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
-         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
-         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
-         * before the LayoutManager entry point method called by RecyclerView returns.</p>
-         *
-         * @param index Index of the child to detach
-         */
-        public void detachViewAt(int index) {
-            detachViewInternal(index, getChildAt(index));
-        }
-
-        private void detachViewInternal(int index, View view) {
-            if (DISPATCH_TEMP_DETACH) {
-                ViewCompat.dispatchStartTemporaryDetach(view);
-            }
-            mChildHelper.detachViewFromParent(index);
-        }
-
-        /**
-         * Reattach a previously {@link #detachView(android.view.View) detached} view.
-         * This method should not be used to reattach views that were previously
-         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
-         *
-         * @param child Child to reattach
-         * @param index Intended child index for child
-         * @param lp LayoutParams for child
-         */
-        public void attachView(View child, int index, LayoutParams lp) {
-            ViewHolder vh = getChildViewHolderInt(child);
-            if (vh.isRemoved()) {
-                mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
-            } else {
-                mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
-            }
-            mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
-            if (DISPATCH_TEMP_DETACH)  {
-                ViewCompat.dispatchFinishTemporaryDetach(child);
-            }
-        }
-
-        /**
-         * Reattach a previously {@link #detachView(android.view.View) detached} view.
-         * This method should not be used to reattach views that were previously
-         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
-         *
-         * @param child Child to reattach
-         * @param index Intended child index for child
-         */
-        public void attachView(View child, int index) {
-            attachView(child, index, (LayoutParams) child.getLayoutParams());
-        }
-
-        /**
-         * Reattach a previously {@link #detachView(android.view.View) detached} view.
-         * This method should not be used to reattach views that were previously
-         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
-         *
-         * @param child Child to reattach
-         */
-        public void attachView(View child) {
-            attachView(child, -1);
-        }
-
-        /**
-         * Finish removing a view that was previously temporarily
-         * {@link #detachView(android.view.View) detached}.
-         *
-         * @param child Detached child to remove
-         */
-        public void removeDetachedView(View child) {
-            mRecyclerView.removeDetachedView(child, false);
-        }
-
-        /**
-         * Moves a View from one position to another.
-         *
-         * @param fromIndex The View's initial index
-         * @param toIndex The View's target index
-         */
-        public void moveView(int fromIndex, int toIndex) {
-            View view = getChildAt(fromIndex);
-            if (view == null) {
-                throw new IllegalArgumentException("Cannot move a child from non-existing index:"
-                        + fromIndex + mRecyclerView.toString());
-            }
-            detachViewAt(fromIndex);
-            attachView(view, toIndex);
-        }
-
-        /**
-         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
-         *
-         * <p>Scrapping a view allows it to be rebound and reused to show updated or
-         * different data.</p>
-         *
-         * @param child Child to detach and scrap
-         * @param recycler Recycler to deposit the new scrap view into
-         */
-        public void detachAndScrapView(View child, Recycler recycler) {
-            int index = mChildHelper.indexOfChild(child);
-            scrapOrRecycleView(recycler, index, child);
-        }
-
-        /**
-         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
-         *
-         * <p>Scrapping a view allows it to be rebound and reused to show updated or
-         * different data.</p>
-         *
-         * @param index Index of child to detach and scrap
-         * @param recycler Recycler to deposit the new scrap view into
-         */
-        public void detachAndScrapViewAt(int index, Recycler recycler) {
-            final View child = getChildAt(index);
-            scrapOrRecycleView(recycler, index, child);
-        }
-
-        /**
-         * Remove a child view and recycle it using the given Recycler.
-         *
-         * @param child Child to remove and recycle
-         * @param recycler Recycler to use to recycle child
-         */
-        public void removeAndRecycleView(View child, Recycler recycler) {
-            removeView(child);
-            recycler.recycleView(child);
-        }
-
-        /**
-         * Remove a child view and recycle it using the given Recycler.
-         *
-         * @param index Index of child to remove and recycle
-         * @param recycler Recycler to use to recycle child
-         */
-        public void removeAndRecycleViewAt(int index, Recycler recycler) {
-            final View view = getChildAt(index);
-            removeViewAt(index);
-            recycler.recycleView(view);
-        }
-
-        /**
-         * Return the current number of child views attached to the parent RecyclerView.
-         * This does not include child views that were temporarily detached and/or scrapped.
-         *
-         * @return Number of attached children
-         */
-        public int getChildCount() {
-            return mChildHelper != null ? mChildHelper.getChildCount() : 0;
-        }
-
-        /**
-         * Return the child view at the given index
-         * @param index Index of child to return
-         * @return Child view at index
-         */
-        public View getChildAt(int index) {
-            return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
-        }
-
-        /**
-         * Return the width measurement spec mode of the RecyclerView.
-         * <p>
-         * This value is set only if the LayoutManager opts into the auto measure api via
-         * {@link #setAutoMeasureEnabled(boolean)}.
-         * <p>
-         * When RecyclerView is running a layout, this value is always set to
-         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
-         *
-         * @return Width measure spec mode.
-         *
-         * @see View.MeasureSpec#getMode(int)
-         * @see View#onMeasure(int, int)
-         */
-        public int getWidthMode() {
-            return mWidthMode;
-        }
-
-        /**
-         * Return the height measurement spec mode of the RecyclerView.
-         * <p>
-         * This value is set only if the LayoutManager opts into the auto measure api via
-         * {@link #setAutoMeasureEnabled(boolean)}.
-         * <p>
-         * When RecyclerView is running a layout, this value is always set to
-         * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
-         *
-         * @return Height measure spec mode.
-         *
-         * @see View.MeasureSpec#getMode(int)
-         * @see View#onMeasure(int, int)
-         */
-        public int getHeightMode() {
-            return mHeightMode;
-        }
-
-        /**
-         * Return the width of the parent RecyclerView
-         *
-         * @return Width in pixels
-         */
-        public int getWidth() {
-            return mWidth;
-        }
-
-        /**
-         * Return the height of the parent RecyclerView
-         *
-         * @return Height in pixels
-         */
-        public int getHeight() {
-            return mHeight;
-        }
-
-        /**
-         * Return the left padding of the parent RecyclerView
-         *
-         * @return Padding in pixels
-         */
-        public int getPaddingLeft() {
-            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
-        }
-
-        /**
-         * Return the top padding of the parent RecyclerView
-         *
-         * @return Padding in pixels
-         */
-        public int getPaddingTop() {
-            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
-        }
-
-        /**
-         * Return the right padding of the parent RecyclerView
-         *
-         * @return Padding in pixels
-         */
-        public int getPaddingRight() {
-            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
-        }
-
-        /**
-         * Return the bottom padding of the parent RecyclerView
-         *
-         * @return Padding in pixels
-         */
-        public int getPaddingBottom() {
-            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
-        }
-
-        /**
-         * Return the start padding of the parent RecyclerView
-         *
-         * @return Padding in pixels
-         */
-        public int getPaddingStart() {
-            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
-        }
-
-        /**
-         * Return the end padding of the parent RecyclerView
-         *
-         * @return Padding in pixels
-         */
-        public int getPaddingEnd() {
-            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
-        }
-
-        /**
-         * Returns true if the RecyclerView this LayoutManager is bound to has focus.
-         *
-         * @return True if the RecyclerView has focus, false otherwise.
-         * @see View#isFocused()
-         */
-        public boolean isFocused() {
-            return mRecyclerView != null && mRecyclerView.isFocused();
-        }
-
-        /**
-         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
-         *
-         * @return true if the RecyclerView has or contains focus
-         * @see View#hasFocus()
-         */
-        public boolean hasFocus() {
-            return mRecyclerView != null && mRecyclerView.hasFocus();
-        }
-
-        /**
-         * Returns the item View which has or contains focus.
-         *
-         * @return A direct child of RecyclerView which has focus or contains the focused child.
-         */
-        public View getFocusedChild() {
-            if (mRecyclerView == null) {
-                return null;
-            }
-            final View focused = mRecyclerView.getFocusedChild();
-            if (focused == null || mChildHelper.isHidden(focused)) {
-                return null;
-            }
-            return focused;
-        }
-
-        /**
-         * 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() 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()
-         */
-        public int getItemCount() {
-            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
-            return a != null ? a.getItemCount() : 0;
-        }
-
-        /**
-         * Offset all child views attached to the parent RecyclerView by dx pixels along
-         * the horizontal axis.
-         *
-         * @param dx Pixels to offset by
-         */
-        public void offsetChildrenHorizontal(int dx) {
-            if (mRecyclerView != null) {
-                mRecyclerView.offsetChildrenHorizontal(dx);
-            }
-        }
-
-        /**
-         * Offset all child views attached to the parent RecyclerView by dy pixels along
-         * the vertical axis.
-         *
-         * @param dy Pixels to offset by
-         */
-        public void offsetChildrenVertical(int dy) {
-            if (mRecyclerView != null) {
-                mRecyclerView.offsetChildrenVertical(dy);
-            }
-        }
-
-        /**
-         * Flags a view so that it will not be scrapped or recycled.
-         * <p>
-         * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
-         * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
-         * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
-         * ignore the child.
-         * <p>
-         * Before this child can be recycled again, you have to call
-         * {@link #stopIgnoringView(View)}.
-         * <p>
-         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
-         *
-         * @param view View to ignore.
-         * @see #stopIgnoringView(View)
-         */
-        public void ignoreView(View view) {
-            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"
-                        + mRecyclerView.exceptionLabel());
-            }
-            final ViewHolder vh = getChildViewHolderInt(view);
-            vh.addFlags(ViewHolder.FLAG_IGNORE);
-            mRecyclerView.mViewInfoStore.removeViewHolder(vh);
-        }
-
-        /**
-         * View can be scrapped and recycled again.
-         * <p>
-         * Note that calling this method removes all information in the view holder.
-         * <p>
-         * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
-         *
-         * @param view View to ignore.
-         */
-        public void stopIgnoringView(View view) {
-            final ViewHolder vh = getChildViewHolderInt(view);
-            vh.stopIgnoring();
-            vh.resetInternal();
-            vh.addFlags(ViewHolder.FLAG_INVALID);
-        }
-
-        /**
-         * Temporarily detach and scrap all currently attached child views. Views will be scrapped
-         * into the given Recycler. The Recycler may prefer to reuse scrap views before
-         * other views that were previously recycled.
-         *
-         * @param recycler Recycler to scrap views into
-         */
-        public void detachAndScrapAttachedViews(Recycler recycler) {
-            final int childCount = getChildCount();
-            for (int i = childCount - 1; i >= 0; i--) {
-                final View v = getChildAt(i);
-                scrapOrRecycleView(recycler, i, v);
-            }
-        }
-
-        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
-            final ViewHolder viewHolder = getChildViewHolderInt(view);
-            if (viewHolder.shouldIgnore()) {
-                if (DEBUG) {
-                    Log.d(TAG, "ignoring view " + viewHolder);
-                }
-                return;
-            }
-            if (viewHolder.isInvalid() && !viewHolder.isRemoved()
-                    && !mRecyclerView.mAdapter.hasStableIds()) {
-                removeViewAt(index);
-                recycler.recycleViewHolderInternal(viewHolder);
-            } else {
-                detachViewAt(index);
-                recycler.scrapView(view);
-                mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
-            }
-        }
-
-        /**
-         * Recycles the scrapped views.
-         * <p>
-         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
-         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
-         * call remove and invalidate RecyclerView to ensure UI update.
-         *
-         * @param recycler Recycler
-         */
-        void removeAndRecycleScrapInt(Recycler recycler) {
-            final int scrapCount = recycler.getScrapCount();
-            // Loop backward, recycler might be changed by removeDetachedView()
-            for (int i = scrapCount - 1; i >= 0; i--) {
-                final View scrap = recycler.getScrapViewAt(i);
-                final ViewHolder vh = getChildViewHolderInt(scrap);
-                if (vh.shouldIgnore()) {
-                    continue;
-                }
-                // If the scrap view is animating, we need to cancel them first. If we cancel it
-                // here, ItemAnimator callback may recycle it which will cause double recycling.
-                // To avoid this, we mark it as not recycleable before calling the item animator.
-                // Since removeDetachedView calls a user API, a common mistake (ending animations on
-                // the view) may recycle it too, so we guard it before we call user APIs.
-                vh.setIsRecyclable(false);
-                if (vh.isTmpDetached()) {
-                    mRecyclerView.removeDetachedView(scrap, false);
-                }
-                if (mRecyclerView.mItemAnimator != null) {
-                    mRecyclerView.mItemAnimator.endAnimation(vh);
-                }
-                vh.setIsRecyclable(true);
-                recycler.quickRecycleScrapView(scrap);
-            }
-            recycler.clearScrap();
-            if (scrapCount > 0) {
-                mRecyclerView.invalidate();
-            }
-        }
-
-
-        /**
-         * Measure a child view using standard measurement policy, taking the padding
-         * of the parent RecyclerView and any added item decorations into account.
-         *
-         * <p>If the RecyclerView can be scrolled in either dimension the caller may
-         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
-         *
-         * @param child Child view to measure
-         * @param widthUsed Width in pixels currently consumed by other views, if relevant
-         * @param heightUsed Height in pixels currently consumed by other views, if relevant
-         */
-        public void measureChild(View child, int widthUsed, int heightUsed) {
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
-            widthUsed += insets.left + insets.right;
-            heightUsed += insets.top + insets.bottom;
-            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
-                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
-                    canScrollHorizontally());
-            final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
-                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
-                    canScrollVertically());
-            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
-                child.measure(widthSpec, heightSpec);
-            }
-        }
-
-        /**
-         * RecyclerView internally does its own View measurement caching which should help with
-         * WRAP_CONTENT.
-         * <p>
-         * Use this method if the View is already measured once in this layout pass.
-         */
-        boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
-            return !mMeasurementCacheEnabled
-                    || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
-                    || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
-        }
-
-        // we may consider making this public
-        /**
-         * RecyclerView internally does its own View measurement caching which should help with
-         * WRAP_CONTENT.
-         * <p>
-         * Use this method if the View is not yet measured and you need to decide whether to
-         * measure this View or not.
-         */
-        boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
-            return child.isLayoutRequested()
-                    || !mMeasurementCacheEnabled
-                    || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
-                    || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
-        }
-
-        /**
-         * In addition to the View Framework's measurement cache, RecyclerView uses its own
-         * additional measurement cache for its children to avoid re-measuring them when not
-         * necessary. It is on by default but it can be turned off via
-         * {@link #setMeasurementCacheEnabled(boolean)}.
-         *
-         * @return True if measurement cache is enabled, false otherwise.
-         *
-         * @see #setMeasurementCacheEnabled(boolean)
-         */
-        public boolean isMeasurementCacheEnabled() {
-            return mMeasurementCacheEnabled;
-        }
-
-        /**
-         * Sets whether RecyclerView should use its own measurement cache for the children. This is
-         * a more aggressive cache than the framework uses.
-         *
-         * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
-         *
-         * @see #isMeasurementCacheEnabled()
-         */
-        public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
-            mMeasurementCacheEnabled = measurementCacheEnabled;
-        }
-
-        private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
-            final int specMode = MeasureSpec.getMode(spec);
-            final int specSize = MeasureSpec.getSize(spec);
-            if (dimension > 0 && childSize != dimension) {
-                return false;
-            }
-            switch (specMode) {
-                case MeasureSpec.UNSPECIFIED:
-                    return true;
-                case MeasureSpec.AT_MOST:
-                    return specSize >= childSize;
-                case MeasureSpec.EXACTLY:
-                    return  specSize == childSize;
-            }
-            return false;
-        }
-
-        /**
-         * Measure a child view using standard measurement policy, taking the padding
-         * of the parent RecyclerView, any added item decorations and the child margins
-         * into account.
-         *
-         * <p>If the RecyclerView can be scrolled in either dimension the caller may
-         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
-         *
-         * @param child Child view to measure
-         * @param widthUsed Width in pixels currently consumed by other views, if relevant
-         * @param heightUsed Height in pixels currently consumed by other views, if relevant
-         */
-        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
-            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
-            widthUsed += insets.left + insets.right;
-            heightUsed += insets.top + insets.bottom;
-
-            final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
-                    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,
-                    canScrollVertically());
-            if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
-                child.measure(widthSpec, heightSpec);
-            }
-        }
-
-        /**
-         * Calculate a MeasureSpec value for measuring a child view in one dimension.
-         *
-         * @param parentSize Size of the parent view where the child will be placed
-         * @param padding Total space currently consumed by other elements of the parent
-         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
-         *                       Generally obtained from the child view's LayoutParams
-         * @param canScroll true if the parent RecyclerView can scroll in this dimension
-         *
-         * @return a MeasureSpec value for the child view
-         * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
-         */
-        @Deprecated
-        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
-                boolean canScroll) {
-            int size = Math.max(0, parentSize - padding);
-            int resultSize = 0;
-            int resultMode = 0;
-            if (canScroll) {
-                if (childDimension >= 0) {
-                    resultSize = childDimension;
-                    resultMode = MeasureSpec.EXACTLY;
-                } else {
-                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
-                    // instead using UNSPECIFIED.
-                    resultSize = 0;
-                    resultMode = MeasureSpec.UNSPECIFIED;
-                }
-            } else {
-                if (childDimension >= 0) {
-                    resultSize = childDimension;
-                    resultMode = MeasureSpec.EXACTLY;
-                } else if (childDimension == LayoutParams.MATCH_PARENT) {
-                    resultSize = size;
-                    // TODO this should be my spec.
-                    resultMode = MeasureSpec.EXACTLY;
-                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
-                    resultSize = size;
-                    resultMode = MeasureSpec.AT_MOST;
-                }
-            }
-            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
-        }
-
-        /**
-         * Calculate a MeasureSpec value for measuring a child view in one dimension.
-         *
-         * @param parentSize Size of the parent view where the child will be placed
-         * @param parentMode The measurement spec mode of the parent
-         * @param padding Total space currently consumed by other elements of parent
-         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
-         *                       Generally obtained from the child view's LayoutParams
-         * @param canScroll true if the parent RecyclerView can scroll in this dimension
-         *
-         * @return a MeasureSpec value for the child view
-         */
-        public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
-                int childDimension, boolean canScroll) {
-            int size = Math.max(0, parentSize - padding);
-            int resultSize = 0;
-            int resultMode = 0;
-            if (canScroll) {
-                if (childDimension >= 0) {
-                    resultSize = childDimension;
-                    resultMode = MeasureSpec.EXACTLY;
-                } else if (childDimension == LayoutParams.MATCH_PARENT) {
-                    switch (parentMode) {
-                        case MeasureSpec.AT_MOST:
-                        case MeasureSpec.EXACTLY:
-                            resultSize = size;
-                            resultMode = parentMode;
-                            break;
-                        case MeasureSpec.UNSPECIFIED:
-                            resultSize = 0;
-                            resultMode = MeasureSpec.UNSPECIFIED;
-                            break;
-                    }
-                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
-                    resultSize = 0;
-                    resultMode = MeasureSpec.UNSPECIFIED;
-                }
-            } else {
-                if (childDimension >= 0) {
-                    resultSize = childDimension;
-                    resultMode = MeasureSpec.EXACTLY;
-                } else if (childDimension == LayoutParams.MATCH_PARENT) {
-                    resultSize = size;
-                    resultMode = parentMode;
-                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
-                    resultSize = size;
-                    if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
-                        resultMode = MeasureSpec.AT_MOST;
-                    } else {
-                        resultMode = MeasureSpec.UNSPECIFIED;
-                    }
-
-                }
-            }
-            //noinspection WrongConstant
-            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
-        }
-
-        /**
-         * Returns the measured width of the given child, plus the additional size of
-         * any insets applied by {@link ItemDecoration ItemDecorations}.
-         *
-         * @param child Child view to query
-         * @return child's measured width plus <code>ItemDecoration</code> insets
-         *
-         * @see View#getMeasuredWidth()
-         */
-        public int getDecoratedMeasuredWidth(View child) {
-            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
-            return child.getMeasuredWidth() + insets.left + insets.right;
-        }
-
-        /**
-         * Returns the measured height of the given child, plus the additional size of
-         * any insets applied by {@link ItemDecoration ItemDecorations}.
-         *
-         * @param child Child view to query
-         * @return child's measured height plus <code>ItemDecoration</code> insets
-         *
-         * @see View#getMeasuredHeight()
-         */
-        public int getDecoratedMeasuredHeight(View child) {
-            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
-            return child.getMeasuredHeight() + insets.top + insets.bottom;
-        }
-
-        /**
-         * Lay out the given child view within the RecyclerView using coordinates that
-         * include any current {@link ItemDecoration ItemDecorations}.
-         *
-         * <p>LayoutManagers should prefer working in sizes and coordinates that include
-         * item decoration insets whenever possible. This allows the LayoutManager to effectively
-         * ignore decoration insets within measurement and layout code. See the following
-         * methods:</p>
-         * <ul>
-         *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
-         *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
-         *     <li>{@link #measureChild(View, int, int)}</li>
-         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
-         *     <li>{@link #getDecoratedLeft(View)}</li>
-         *     <li>{@link #getDecoratedTop(View)}</li>
-         *     <li>{@link #getDecoratedRight(View)}</li>
-         *     <li>{@link #getDecoratedBottom(View)}</li>
-         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
-         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
-         * </ul>
-         *
-         * @param child Child to lay out
-         * @param left Left edge, with item decoration insets included
-         * @param top Top edge, with item decoration insets included
-         * @param right Right edge, with item decoration insets included
-         * @param bottom Bottom edge, with item decoration insets included
-         *
-         * @see View#layout(int, int, int, int)
-         * @see #layoutDecoratedWithMargins(View, int, int, int, int)
-         */
-        public void layoutDecorated(View child, int left, int top, int right, int bottom) {
-            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
-            child.layout(left + insets.left, top + insets.top, right - insets.right,
-                    bottom - insets.bottom);
-        }
-
-        /**
-         * Lay out the given child view within the RecyclerView using coordinates that
-         * include any current {@link ItemDecoration ItemDecorations} and margins.
-         *
-         * <p>LayoutManagers should prefer working in sizes and coordinates that include
-         * item decoration insets whenever possible. This allows the LayoutManager to effectively
-         * ignore decoration insets within measurement and layout code. See the following
-         * methods:</p>
-         * <ul>
-         *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
-         *     <li>{@link #measureChild(View, int, int)}</li>
-         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
-         *     <li>{@link #getDecoratedLeft(View)}</li>
-         *     <li>{@link #getDecoratedTop(View)}</li>
-         *     <li>{@link #getDecoratedRight(View)}</li>
-         *     <li>{@link #getDecoratedBottom(View)}</li>
-         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
-         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
-         * </ul>
-         *
-         * @param child Child to lay out
-         * @param left Left edge, with item decoration insets and left margin included
-         * @param top Top edge, with item decoration insets and top margin included
-         * @param right Right edge, with item decoration insets and right margin included
-         * @param bottom Bottom edge, with item decoration insets and bottom margin included
-         *
-         * @see View#layout(int, int, int, int)
-         * @see #layoutDecorated(View, int, int, int, int)
-         */
-        public void layoutDecoratedWithMargins(View child, int left, int top, int right,
-                int bottom) {
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            final Rect insets = lp.mDecorInsets;
-            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
-                    right - insets.right - lp.rightMargin,
-                    bottom - insets.bottom - lp.bottomMargin);
-        }
-
-        /**
-         * Calculates the bounding box of the View while taking into account its matrix changes
-         * (translation, scale etc) with respect to the RecyclerView.
-         * <p>
-         * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
-         * the View's matrix so that the decor offsets also go through the same transformation.
-         *
-         * @param child The ItemView whose bounding box should be calculated.
-         * @param includeDecorInsets True if the decor insets should be included in the bounding box
-         * @param out The rectangle into which the output will be written.
-         */
-        public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
-            if (includeDecorInsets) {
-                Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
-                out.set(-insets.left, -insets.top,
-                        child.getWidth() + insets.right, child.getHeight() + insets.bottom);
-            } else {
-                out.set(0, 0, child.getWidth(), child.getHeight());
-            }
-
-            if (mRecyclerView != null) {
-                final Matrix childMatrix = child.getMatrix();
-                if (childMatrix != null && !childMatrix.isIdentity()) {
-                    final RectF tempRectF = mRecyclerView.mTempRectF;
-                    tempRectF.set(out);
-                    childMatrix.mapRect(tempRectF);
-                    out.set(
-                            (int) Math.floor(tempRectF.left),
-                            (int) Math.floor(tempRectF.top),
-                            (int) Math.ceil(tempRectF.right),
-                            (int) Math.ceil(tempRectF.bottom)
-                    );
-                }
-            }
-            out.offset(child.getLeft(), child.getTop());
-        }
-
-        /**
-         * Returns the bounds of the view including its decoration and margins.
-         *
-         * @param view The view element to check
-         * @param outBounds A rect that will receive the bounds of the element including its
-         *                  decoration and margins.
-         */
-        public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
-            RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
-        }
-
-        /**
-         * Returns the left edge of the given child view within its parent, offset by any applied
-         * {@link ItemDecoration ItemDecorations}.
-         *
-         * @param child Child to query
-         * @return Child left edge with offsets applied
-         * @see #getLeftDecorationWidth(View)
-         */
-        public int getDecoratedLeft(View child) {
-            return child.getLeft() - getLeftDecorationWidth(child);
-        }
-
-        /**
-         * Returns the top edge of the given child view within its parent, offset by any applied
-         * {@link ItemDecoration ItemDecorations}.
-         *
-         * @param child Child to query
-         * @return Child top edge with offsets applied
-         * @see #getTopDecorationHeight(View)
-         */
-        public int getDecoratedTop(View child) {
-            return child.getTop() - getTopDecorationHeight(child);
-        }
-
-        /**
-         * Returns the right edge of the given child view within its parent, offset by any applied
-         * {@link ItemDecoration ItemDecorations}.
-         *
-         * @param child Child to query
-         * @return Child right edge with offsets applied
-         * @see #getRightDecorationWidth(View)
-         */
-        public int getDecoratedRight(View child) {
-            return child.getRight() + getRightDecorationWidth(child);
-        }
-
-        /**
-         * Returns the bottom edge of the given child view within its parent, offset by any applied
-         * {@link ItemDecoration ItemDecorations}.
-         *
-         * @param child Child to query
-         * @return Child bottom edge with offsets applied
-         * @see #getBottomDecorationHeight(View)
-         */
-        public int getDecoratedBottom(View child) {
-            return child.getBottom() + getBottomDecorationHeight(child);
-        }
-
-        /**
-         * Calculates the item decor insets applied to the given child and updates the provided
-         * Rect instance with the inset values.
-         * <ul>
-         *     <li>The Rect's left is set to the total width of left decorations.</li>
-         *     <li>The Rect's top is set to the total height of top decorations.</li>
-         *     <li>The Rect's right is set to the total width of right decorations.</li>
-         *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
-         * </ul>
-         * <p>
-         * Note that item decorations are automatically calculated when one of the LayoutManager's
-         * measure child methods is called. If you need to measure the child with custom specs via
-         * {@link View#measure(int, int)}, you can use this method to get decorations.
-         *
-         * @param child The child view whose decorations should be calculated
-         * @param outRect The Rect to hold result values
-         */
-        public void calculateItemDecorationsForChild(View child, Rect outRect) {
-            if (mRecyclerView == null) {
-                outRect.set(0, 0, 0, 0);
-                return;
-            }
-            Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
-            outRect.set(insets);
-        }
-
-        /**
-         * Returns the total height of item decorations applied to child's top.
-         * <p>
-         * Note that this value is not updated until the View is measured or
-         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
-         *
-         * @param child Child to query
-         * @return The total height of item decorations applied to the child's top.
-         * @see #getDecoratedTop(View)
-         * @see #calculateItemDecorationsForChild(View, Rect)
-         */
-        public int getTopDecorationHeight(View child) {
-            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
-        }
-
-        /**
-         * Returns the total height of item decorations applied to child's bottom.
-         * <p>
-         * Note that this value is not updated until the View is measured or
-         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
-         *
-         * @param child Child to query
-         * @return The total height of item decorations applied to the child's bottom.
-         * @see #getDecoratedBottom(View)
-         * @see #calculateItemDecorationsForChild(View, Rect)
-         */
-        public int getBottomDecorationHeight(View child) {
-            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
-        }
-
-        /**
-         * Returns the total width of item decorations applied to child's left.
-         * <p>
-         * Note that this value is not updated until the View is measured or
-         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
-         *
-         * @param child Child to query
-         * @return The total width of item decorations applied to the child's left.
-         * @see #getDecoratedLeft(View)
-         * @see #calculateItemDecorationsForChild(View, Rect)
-         */
-        public int getLeftDecorationWidth(View child) {
-            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
-        }
-
-        /**
-         * Returns the total width of item decorations applied to child's right.
-         * <p>
-         * Note that this value is not updated until the View is measured or
-         * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
-         *
-         * @param child Child to query
-         * @return The total width of item decorations applied to the child's right.
-         * @see #getDecoratedRight(View)
-         * @see #calculateItemDecorationsForChild(View, Rect)
-         */
-        public int getRightDecorationWidth(View child) {
-            return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
-        }
-
-        /**
-         * Called when searching for a focusable view in the given direction has failed
-         * for the current content of the RecyclerView.
-         *
-         * <p>This is the LayoutManager's opportunity to populate views in the given direction
-         * to fulfill the request if it can. The LayoutManager should attach and return
-         * the view to be focused, if a focusable view in the given direction is found.
-         * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
-         * the next unfocusable view to become visible on the screen. This unfocusable view is
-         * typically the first view that's either partially or fully out of RV's padded bounded
-         * area in the given direction. The default implementation returns null.</p>
-         *
-         * @param focused   The currently focused view
-         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
-         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
-         *                  or 0 for not applicable
-         * @param recycler  The recycler to use for obtaining views for currently offscreen items
-         * @param state     Transient state of RecyclerView
-         * @return The chosen view to be focused if a focusable view is found, otherwise an
-         * unfocusable view to become visible onto the screen, else null.
-         */
-        @Nullable
-        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
-                State state) {
-            return null;
-        }
-
-        /**
-         * This method gives a LayoutManager an opportunity to intercept the initial focus search
-         * before the default behavior of {@link FocusFinder} is used. If this method returns
-         * null FocusFinder will attempt to find a focusable child view. If it fails
-         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
-         * will be called to give the LayoutManager an opportunity to add new views for items
-         * that did not have attached views representing them. The LayoutManager should not add
-         * or remove views from this method.
-         *
-         * @param focused The currently focused view
-         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
-         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
-         * @return A descendant view to focus or null to fall back to default behavior.
-         *         The default implementation returns null.
-         */
-        public View onInterceptFocusSearch(View focused, int direction) {
-            return null;
-        }
-
-        /**
-         * Returns the scroll amount that brings the given rect in child's coordinate system within
-         * the padded area of RecyclerView.
-         * @param parent The parent RecyclerView.
-         * @param child The direct child making the request.
-         * @param rect The rectangle in the child's coordinates the child
-         *             wishes to be on the screen.
-         * @param immediate True to forbid animated or delayed scrolling,
-         *                  false otherwise
-         * @return The array containing the scroll amount in x and y directions that brings the
-         * given rect into RV's padded area.
-         */
-        private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
-                Rect rect, boolean immediate) {
-            int[] out = new int[2];
-            final int parentLeft = getPaddingLeft();
-            final int parentTop = getPaddingTop();
-            final int parentRight = getWidth() - getPaddingRight();
-            final int parentBottom = getHeight() - getPaddingBottom();
-            final int childLeft = child.getLeft() + rect.left - child.getScrollX();
-            final int childTop = child.getTop() + rect.top - child.getScrollY();
-            final int childRight = childLeft + rect.width();
-            final int childBottom = childTop + rect.height();
-
-            final int offScreenLeft = Math.min(0, childLeft - parentLeft);
-            final int offScreenTop = Math.min(0, childTop - parentTop);
-            final int offScreenRight = Math.max(0, childRight - parentRight);
-            final int offScreenBottom = Math.max(0, childBottom - parentBottom);
-
-            // Favor the "start" layout direction over the end when bringing one side or the other
-            // of a large rect into view. If we decide to bring in end because start is already
-            // visible, limit the scroll such that start won't go out of bounds.
-            final int dx;
-            if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
-                dx = offScreenRight != 0 ? offScreenRight
-                        : Math.max(offScreenLeft, childRight - parentRight);
-            } else {
-                dx = offScreenLeft != 0 ? offScreenLeft
-                        : Math.min(childLeft - parentLeft, offScreenRight);
-            }
-
-            // Favor bringing the top into view over the bottom. If top is already visible and
-            // we should scroll to make bottom visible, make sure top does not go out of bounds.
-            final int dy = offScreenTop != 0 ? offScreenTop
-                    : Math.min(childTop - parentTop, offScreenBottom);
-            out[0] = dx;
-            out[1] = dy;
-            return out;
-        }
-        /**
-         * Called when a child of the RecyclerView wants a particular rectangle to be positioned
-         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
-         * android.graphics.Rect, boolean)} for more details.
-         *
-         * <p>The base implementation will attempt to perform a standard programmatic scroll
-         * to bring the given rect into view, within the padded area of the RecyclerView.</p>
-         *
-         * @param child The direct child making the request.
-         * @param rect  The rectangle in the child's coordinates the child
-         *              wishes to be on the screen.
-         * @param immediate True to forbid animated or delayed scrolling,
-         *                  false otherwise
-         * @return Whether the group scrolled to handle the operation
-         */
-        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
-                boolean immediate) {
-            return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
-        }
-
-        /**
-         * Requests that the given child of the RecyclerView be positioned onto the screen. This
-         * method can be called for both unfocusable and focusable child views. For unfocusable
-         * child views, focusedChildVisible is typically true in which case, layout manager
-         * makes the child view visible only if the currently focused child stays in-bounds of RV.
-         * @param parent The parent RecyclerView.
-         * @param child The direct child making the request.
-         * @param rect The rectangle in the child's coordinates the child
-         *              wishes to be on the screen.
-         * @param immediate True to forbid animated or delayed scrolling,
-         *                  false otherwise
-         * @param focusedChildVisible Whether the currently focused view must stay visible.
-         * @return Whether the group scrolled to handle the operation
-         */
-        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
-                boolean immediate,
-                boolean focusedChildVisible) {
-            int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
-                    immediate);
-            int dx = scrollAmount[0];
-            int dy = scrollAmount[1];
-            if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
-                if (dx != 0 || dy != 0) {
-                    if (immediate) {
-                        parent.scrollBy(dx, dy);
-                    } else {
-                        parent.smoothScrollBy(dx, dy);
-                    }
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Returns whether the given child view is partially or fully visible within the padded
-         * bounded area of RecyclerView, depending on the input parameters.
-         * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
-         * If acceptEndPointInclusion flag is set to true, it's also considered partially
-         * visible if it's located outside RV's bounds and it's hitting either RV's start or end
-         * bounds.
-         *
-         * @param child The child view to be examined.
-         * @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.
-         * @return True if the given child is partially or fully visible, false otherwise.
-         */
-        public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
-                boolean acceptEndPointInclusion) {
-            int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
-                    | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
-            boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
-                    boundsFlag)
-                    && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
-            if (completelyVisible) {
-                return isViewFullyVisible;
-            } else {
-                return !isViewFullyVisible;
-            }
-        }
-
-        /**
-         * Returns whether the currently focused child stays within RV's bounds with the given
-         * amount of scrolling.
-         * @param parent The parent RecyclerView.
-         * @param dx The scrolling in x-axis direction to be performed.
-         * @param dy The scrolling in y-axis direction to be performed.
-         * @return {@code false} if the focused child is not at least partially visible after
-         *         scrolling or no focused child exists, {@code true} otherwise.
-         */
-        private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
-            final View focusedChild = parent.getFocusedChild();
-            if (focusedChild == null) {
-                return false;
-            }
-            final int parentLeft = getPaddingLeft();
-            final int parentTop = getPaddingTop();
-            final int parentRight = getWidth() - getPaddingRight();
-            final int parentBottom = getHeight() - getPaddingBottom();
-            final Rect bounds = mRecyclerView.mTempRect;
-            getDecoratedBoundsWithMargins(focusedChild, bounds);
-
-            if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
-                    || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
-                return false;
-            }
-            return true;
-        }
-
-        /**
-         * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
-         */
-        @Deprecated
-        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
-            // eat the request if we are in the middle of a scroll or layout
-            return isSmoothScrolling() || parent.isComputingLayout();
-        }
-
-        /**
-         * Called when a descendant view of the RecyclerView requests focus.
-         *
-         * <p>A LayoutManager wishing to keep focused views aligned in a specific
-         * portion of the view may implement that behavior in an override of this method.</p>
-         *
-         * <p>If the LayoutManager executes different behavior that should override the default
-         * behavior of scrolling the focused child on screen instead of running alongside it,
-         * this method should return true.</p>
-         *
-         * @param parent  The RecyclerView hosting this LayoutManager
-         * @param state   Current state of RecyclerView
-         * @param child   Direct child of the RecyclerView containing the newly focused view
-         * @param focused The newly focused view. This may be the same view as child or it may be
-         *                null
-         * @return true if the default scroll behavior should be suppressed
-         */
-        public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
-                View focused) {
-            return onRequestChildFocus(parent, child, focused);
-        }
-
-        /**
-         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
-         * {@link RecyclerView#setAdapter(Adapter)} or
-         * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
-         * opportunity to clear caches and configure state such that it can relayout appropriately
-         * with the new data and potentially new view types.
-         *
-         * <p>The default implementation removes all currently attached views.</p>
-         *
-         * @param oldAdapter The previous adapter instance. Will be null if there was previously no
-         *                   adapter.
-         * @param newAdapter The new adapter instance. Might be null if
-         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
-         */
-        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
-        }
-
-        /**
-         * Called to populate focusable views within the RecyclerView.
-         *
-         * <p>The LayoutManager implementation should return <code>true</code> if the default
-         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
-         * suppressed.</p>
-         *
-         * <p>The default implementation returns <code>false</code> to trigger RecyclerView
-         * to fall back to the default ViewGroup behavior.</p>
-         *
-         * @param recyclerView The RecyclerView hosting this LayoutManager
-         * @param views List of output views. This method should add valid focusable views
-         *              to this list.
-         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
-         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
-         * @param focusableMode The type of focusables to be added.
-         *
-         * @return true to suppress the default behavior, false to add default focusables after
-         *         this method returns.
-         *
-         * @see #FOCUSABLES_ALL
-         * @see #FOCUSABLES_TOUCH_MODE
-         */
-        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
-                int direction, int focusableMode) {
-            return false;
-        }
-
-        /**
-         * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
-         * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
-         * data set has changed.
-         *
-         * @param recyclerView
-         */
-        public void onItemsChanged(RecyclerView recyclerView) {
-        }
-
-        /**
-         * Called when items have been added to the adapter. The LayoutManager may choose to
-         * requestLayout if the inserted items would require refreshing the currently visible set
-         * of child views. (e.g. currently empty space would be filled by appended items, etc.)
-         *
-         * @param recyclerView
-         * @param positionStart
-         * @param itemCount
-         */
-        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
-        }
-
-        /**
-         * Called when items have been removed from the adapter.
-         *
-         * @param recyclerView
-         * @param positionStart
-         * @param itemCount
-         */
-        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
-        }
-
-        /**
-         * Called when items have been changed in the adapter.
-         * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
-         * instead, then this callback will not be invoked.
-         *
-         * @param recyclerView
-         * @param positionStart
-         * @param itemCount
-         */
-        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
-        }
-
-        /**
-         * Called when items have been changed in the adapter and with optional payload.
-         * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
-         *
-         * @param recyclerView
-         * @param positionStart
-         * @param itemCount
-         * @param payload
-         */
-        public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
-                Object payload) {
-            onItemsUpdated(recyclerView, positionStart, itemCount);
-        }
-
-        /**
-         * Called when an item is moved withing the adapter.
-         * <p>
-         * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
-         * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
-         * is called.
-         *
-         * @param recyclerView
-         * @param from
-         * @param to
-         * @param itemCount
-         */
-        public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
-
-        }
-
-
-        /**
-         * <p>Override this method if you want to support scroll bars.</p>
-         *
-         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
-         *
-         * <p>Default implementation returns 0.</p>
-         *
-         * @param state Current state of RecyclerView
-         * @return The horizontal extent of the scrollbar's thumb
-         * @see RecyclerView#computeHorizontalScrollExtent()
-         */
-        public int computeHorizontalScrollExtent(State state) {
-            return 0;
-        }
-
-        /**
-         * <p>Override this method if you want to support scroll bars.</p>
-         *
-         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
-         *
-         * <p>Default implementation returns 0.</p>
-         *
-         * @param state Current State of RecyclerView where you can find total item count
-         * @return The horizontal offset of the scrollbar's thumb
-         * @see RecyclerView#computeHorizontalScrollOffset()
-         */
-        public int computeHorizontalScrollOffset(State state) {
-            return 0;
-        }
-
-        /**
-         * <p>Override this method if you want to support scroll bars.</p>
-         *
-         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
-         *
-         * <p>Default implementation returns 0.</p>
-         *
-         * @param state Current State of RecyclerView where you can find total item count
-         * @return The total horizontal range represented by the vertical scrollbar
-         * @see RecyclerView#computeHorizontalScrollRange()
-         */
-        public int computeHorizontalScrollRange(State state) {
-            return 0;
-        }
-
-        /**
-         * <p>Override this method if you want to support scroll bars.</p>
-         *
-         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
-         *
-         * <p>Default implementation returns 0.</p>
-         *
-         * @param state Current state of RecyclerView
-         * @return The vertical extent of the scrollbar's thumb
-         * @see RecyclerView#computeVerticalScrollExtent()
-         */
-        public int computeVerticalScrollExtent(State state) {
-            return 0;
-        }
-
-        /**
-         * <p>Override this method if you want to support scroll bars.</p>
-         *
-         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
-         *
-         * <p>Default implementation returns 0.</p>
-         *
-         * @param state Current State of RecyclerView where you can find total item count
-         * @return The vertical offset of the scrollbar's thumb
-         * @see RecyclerView#computeVerticalScrollOffset()
-         */
-        public int computeVerticalScrollOffset(State state) {
-            return 0;
-        }
-
-        /**
-         * <p>Override this method if you want to support scroll bars.</p>
-         *
-         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
-         *
-         * <p>Default implementation returns 0.</p>
-         *
-         * @param state Current State of RecyclerView where you can find total item count
-         * @return The total vertical range represented by the vertical scrollbar
-         * @see RecyclerView#computeVerticalScrollRange()
-         */
-        public int computeVerticalScrollRange(State state) {
-            return 0;
-        }
-
-        /**
-         * Measure the attached RecyclerView. Implementations must call
-         * {@link #setMeasuredDimension(int, int)} before returning.
-         *
-         * <p>The default implementation will handle EXACTLY measurements and respect
-         * the minimum width and height properties of the host RecyclerView if measured
-         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
-         * will consume all available space.</p>
-         *
-         * @param recycler Recycler
-         * @param state Transient state of RecyclerView
-         * @param widthSpec Width {@link android.view.View.MeasureSpec}
-         * @param heightSpec Height {@link android.view.View.MeasureSpec}
-         */
-        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
-            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
-        }
-
-        /**
-         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
-         * host RecyclerView.
-         *
-         * @param widthSize Measured width
-         * @param heightSize Measured height
-         */
-        public void setMeasuredDimension(int widthSize, int heightSize) {
-            mRecyclerView.setMeasuredDimension(widthSize, heightSize);
-        }
-
-        /**
-         * @return The host RecyclerView's {@link View#getMinimumWidth()}
-         */
-        public int getMinimumWidth() {
-            return ViewCompat.getMinimumWidth(mRecyclerView);
-        }
-
-        /**
-         * @return The host RecyclerView's {@link View#getMinimumHeight()}
-         */
-        public int getMinimumHeight() {
-            return ViewCompat.getMinimumHeight(mRecyclerView);
-        }
-        /**
-         * <p>Called when the LayoutManager should save its state. This is a good time to save your
-         * scroll position, configuration and anything else that may be required to restore the same
-         * layout state if the LayoutManager is recreated.</p>
-         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
-         * restore. This will let you share information between your LayoutManagers but it is also
-         * your responsibility to make sure they use the same parcelable class.</p>
-         *
-         * @return Necessary information for LayoutManager to be able to restore its state
-         */
-        public Parcelable onSaveInstanceState() {
-            return null;
-        }
-
-
-        public void onRestoreInstanceState(Parcelable state) {
-
-        }
-
-        void stopSmoothScroller() {
-            if (mSmoothScroller != null) {
-                mSmoothScroller.stop();
-            }
-        }
-
-        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
-            if (mSmoothScroller == smoothScroller) {
-                mSmoothScroller = null;
-            }
-        }
-
-        /**
-         * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
-         *
-         * @param state The new scroll state for RecyclerView
-         */
-        public void onScrollStateChanged(int state) {
-        }
-
-        /**
-         * Removes all views and recycles them using the given recycler.
-         * <p>
-         * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
-         * <p>
-         * If a View is marked as "ignored", it is not removed nor recycled.
-         *
-         * @param recycler Recycler to use to recycle children
-         * @see #removeAndRecycleView(View, Recycler)
-         * @see #removeAndRecycleViewAt(int, Recycler)
-         * @see #ignoreView(View)
-         */
-        public void removeAndRecycleAllViews(Recycler recycler) {
-            for (int i = getChildCount() - 1; i >= 0; i--) {
-                final View view = getChildAt(i);
-                if (!getChildViewHolderInt(view).shouldIgnore()) {
-                    removeAndRecycleViewAt(i, recycler);
-                }
-            }
-        }
-
-        // called by accessibility delegate
-        void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
-            onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
-        }
-
-        /**
-         * Called by the AccessibilityDelegate when the information about the current layout should
-         * be populated.
-         * <p>
-         * Default implementation adds a {@link
-         * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
-         * <p>
-         * You should override
-         * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
-         * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
-         * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
-         * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
-         * more accurate accessibility information.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @param info     The info that should be filled by the LayoutManager
-         * @see View#onInitializeAccessibilityNodeInfo(
-         *android.view.accessibility.AccessibilityNodeInfo)
-         * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
-         * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
-         * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
-         * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
-         */
-        public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
-                AccessibilityNodeInfoCompat info) {
-            if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
-                info.setScrollable(true);
-            }
-            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));
-            info.setCollectionInfo(collectionInfo);
-        }
-
-        // called by accessibility delegate
-        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-            onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
-        }
-
-        /**
-         * Called by the accessibility delegate to initialize an accessibility event.
-         * <p>
-         * Default implementation adds item count and scroll information to the event.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @param event    The event instance to initialize
-         * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
-         */
-        public void onInitializeAccessibilityEvent(Recycler recycler, State state,
-                AccessibilityEvent event) {
-            if (mRecyclerView == null || event == null) {
-                return;
-            }
-            event.setScrollable(mRecyclerView.canScrollVertically(1)
-                    || mRecyclerView.canScrollVertically(-1)
-                    || mRecyclerView.canScrollHorizontally(-1)
-                    || mRecyclerView.canScrollHorizontally(1));
-
-            if (mRecyclerView.mAdapter != null) {
-                event.setItemCount(mRecyclerView.mAdapter.getItemCount());
-            }
-        }
-
-        // called by accessibility delegate
-        void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
-            final ViewHolder vh = getChildViewHolderInt(host);
-            // avoid trying to create accessibility node info for removed children
-            if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
-                onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
-                        mRecyclerView.mState, host, info);
-            }
-        }
-
-        /**
-         * Called by the AccessibilityDelegate when the accessibility information for a specific
-         * item should be populated.
-         * <p>
-         * Default implementation adds basic positioning information about the item.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @param host     The child for which accessibility node info should be populated
-         * @param info     The info to fill out about the item
-         * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
-         * android.view.accessibility.AccessibilityNodeInfo)
-         */
-        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
-                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);
-            info.setCollectionItemInfo(itemInfo);
-        }
-
-        /**
-         * A LayoutManager can call this method to force RecyclerView to run simple animations in
-         * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
-         * change).
-         * <p>
-         * Note that, calling this method will not guarantee that RecyclerView will run animations
-         * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
-         * not run any animations but will still clear this flag after the layout is complete.
-         *
-         */
-        public void requestSimpleAnimationsInNextLayout() {
-            mRequestedSimpleAnimations = true;
-        }
-
-        /**
-         * Returns the selection mode for accessibility. Should be
-         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
-         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
-         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
-         * <p>
-         * Default implementation returns
-         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @return Selection mode for accessibility. Default implementation returns
-         * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
-         */
-        public int getSelectionModeForAccessibility(Recycler recycler, State state) {
-            return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
-        }
-
-        /**
-         * Returns the number of rows for accessibility.
-         * <p>
-         * Default implementation returns the number of items in the adapter if LayoutManager
-         * supports vertical scrolling or 1 if LayoutManager does not support vertical
-         * scrolling.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @return The number of rows in LayoutManager for accessibility.
-         */
-        public int getRowCountForAccessibility(Recycler recycler, State state) {
-            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
-                return 1;
-            }
-            return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
-        }
-
-        /**
-         * Returns the number of columns for accessibility.
-         * <p>
-         * Default implementation returns the number of items in the adapter if LayoutManager
-         * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
-         * scrolling.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @return The number of rows in LayoutManager for accessibility.
-         */
-        public int getColumnCountForAccessibility(Recycler recycler, State state) {
-            if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
-                return 1;
-            }
-            return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
-        }
-
-        /**
-         * Returns whether layout is hierarchical or not to be used for accessibility.
-         * <p>
-         * Default implementation returns false.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @return True if layout is hierarchical.
-         */
-        public boolean isLayoutHierarchical(Recycler recycler, State state) {
-            return false;
-        }
-
-        // called by accessibility delegate
-        boolean performAccessibilityAction(int action, Bundle args) {
-            return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
-                    action, args);
-        }
-
-        /**
-         * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
-         *
-         * @param recycler  The Recycler that can be used to convert view positions into adapter
-         *                  positions
-         * @param state     The current state of RecyclerView
-         * @param action    The action to perform
-         * @param args      Optional action arguments
-         * @see View#performAccessibilityAction(int, android.os.Bundle)
-         */
-        public boolean performAccessibilityAction(Recycler recycler, State state, int action,
-                Bundle args) {
-            if (mRecyclerView == null) {
-                return false;
-            }
-            int vScroll = 0, hScroll = 0;
-            switch (action) {
-                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
-                    if (mRecyclerView.canScrollVertically(-1)) {
-                        vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
-                    }
-                    if (mRecyclerView.canScrollHorizontally(-1)) {
-                        hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
-                    }
-                    break;
-                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
-                    if (mRecyclerView.canScrollVertically(1)) {
-                        vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
-                    }
-                    if (mRecyclerView.canScrollHorizontally(1)) {
-                        hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
-                    }
-                    break;
-            }
-            if (vScroll == 0 && hScroll == 0) {
-                return false;
-            }
-            mRecyclerView.smoothScrollBy(hScroll, vScroll);
-            return true;
-        }
-
-        // called by accessibility delegate
-        boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
-            return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
-                    view, action, args);
-        }
-
-        /**
-         * Called by AccessibilityDelegate when an accessibility action is requested on one of the
-         * children of LayoutManager.
-         * <p>
-         * Default implementation does not do anything.
-         *
-         * @param recycler The Recycler that can be used to convert view positions into adapter
-         *                 positions
-         * @param state    The current state of RecyclerView
-         * @param view     The child view on which the action is performed
-         * @param action   The action to perform
-         * @param args     Optional action arguments
-         * @return true if action is handled
-         * @see View#performAccessibilityAction(int, android.os.Bundle)
-         */
-        public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
-                int action, Bundle args) {
-            return false;
-        }
-
-        /**
-         * Parse the xml attributes to get the most common properties used by layout managers.
-         *
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
-         *
-         * @return an object containing the properties as specified in the attrs.
-         */
-        public static Properties getProperties(Context context, AttributeSet attrs,
-                int defStyleAttr, int defStyleRes) {
-            Properties properties = new Properties();
-            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
-                    defStyleAttr, defStyleRes);
-            properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
-                    DEFAULT_ORIENTATION);
-            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);
-            a.recycle();
-            return properties;
-        }
-
-        void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
-            setMeasureSpecs(
-                    MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
-            );
-        }
-
-        /**
-         * Internal API to allow LayoutManagers to be measured twice.
-         * <p>
-         * This is not public because LayoutManagers should be able to handle their layouts in one
-         * pass but it is very convenient to make existing LayoutManagers support wrapping content
-         * when both orientations are undefined.
-         * <p>
-         * This API will be removed after default LayoutManagers properly implement wrap content in
-         * non-scroll orientation.
-         */
-        boolean shouldMeasureTwice() {
-            return false;
-        }
-
-        boolean hasFlexibleChildInBothOrientations() {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final ViewGroup.LayoutParams lp = child.getLayoutParams();
-                if (lp.width < 0 && lp.height < 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * Some general properties that a LayoutManager may want to use.
-         */
-        public static class Properties {
-            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
-            public int orientation;
-            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
-            public int spanCount;
-            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
-            public boolean reverseLayout;
-            /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
-            public boolean stackFromEnd;
-        }
-    }
-
-    /**
-     * An ItemDecoration allows the application to add a special drawing and layout offset
-     * to specific item views from the adapter's data set. This can be useful for drawing dividers
-     * between items, highlights, visual grouping boundaries and more.
-     *
-     * <p>All ItemDecorations are drawn in the order they were added, before the item
-     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
-     * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
-     * RecyclerView.State)}.</p>
-     */
-    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,
-         * and will thus appear underneath the views.
-         *
-         * @param c Canvas to draw into
-         * @param parent RecyclerView this ItemDecoration is drawing into
-         * @param state The current state of RecyclerView
-         */
-        public void onDraw(Canvas c, RecyclerView parent, State state) {
-            onDraw(c, parent);
-        }
-
-        /**
-         * @deprecated
-         * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
-         */
-        @Deprecated
-        public void onDraw(Canvas c, RecyclerView parent) {
-        }
-
-        /**
-         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
-         * Any content drawn by this method will be drawn after the item views are drawn
-         * and will thus appear over the views.
-         *
-         * @param c Canvas to draw into
-         * @param parent RecyclerView this ItemDecoration is drawing into
-         * @param state The current state of RecyclerView.
-         */
-        public void onDrawOver(Canvas c, RecyclerView parent, State state) {
-            onDrawOver(c, parent);
-        }
-
-        /**
-         * @deprecated
-         * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
-         */
-        @Deprecated
-        public void onDrawOver(Canvas c, RecyclerView parent) {
-        }
-
-
-        /**
-         * @deprecated
-         * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
-         */
-        @Deprecated
-        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
-            outRect.set(0, 0, 0, 0);
-        }
-
-        /**
-         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
-         * the number of pixels that the item view should be inset by, similar to padding or margin.
-         * The default implementation sets the bounds of outRect to 0 and returns.
-         *
-         * <p>
-         * If this ItemDecoration does not affect the positioning of item views, it should set
-         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
-         * before returning.
-         *
-         * <p>
-         * If you need to access Adapter for additional data, you can call
-         * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
-         * View.
-         *
-         * @param outRect Rect to receive the output.
-         * @param view    The child view to decorate
-         * @param parent  RecyclerView this ItemDecoration is decorating
-         * @param state   The current state of RecyclerView.
-         */
-        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
-            getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
-                    parent);
-        }
-    }
-
-    /**
-     * An OnItemTouchListener allows the application to intercept touch events in progress at the
-     * view hierarchy level of the RecyclerView before those touch events are considered for
-     * RecyclerView's own scrolling behavior.
-     *
-     * <p>This can be useful for applications that wish to implement various forms of gestural
-     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
-     * a touch interaction already in progress even if the RecyclerView is already handling that
-     * gesture stream itself for the purposes of scrolling.</p>
-     *
-     * @see SimpleOnItemTouchListener
-     */
-    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.
-         *
-         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
-         * in the order in which each listener was added, before any other touch processing
-         * by the RecyclerView itself or child views occurs.</p>
-         *
-         * @param e MotionEvent describing the touch event. All coordinates are in
-         *          the RecyclerView's coordinate system.
-         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
-         *         to continue with the current behavior and continue observing future events in
-         *         the gesture.
-         */
-        boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
-
-        /**
-         * Process a touch event as part of a gesture that was claimed by returning true from
-         * a previous call to {@link #onInterceptTouchEvent}.
-         *
-         * @param e MotionEvent describing the touch event. All coordinates are in
-         *          the RecyclerView's coordinate system.
-         */
-        void onTouchEvent(RecyclerView rv, MotionEvent e);
-
-        /**
-         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
-         * intercept touch events with
-         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
-         *
-         * @param disallowIntercept True if the child does not want the parent to
-         *            intercept touch events.
-         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
-         */
-        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
-    }
-
-    /**
-     * 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
-     * always provide a default implementation on this class so that your code won't break when
-     * you update to a new version of the support library.
-     */
-    public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
-        @Override
-        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
-            return false;
-        }
-
-        @Override
-        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
-        }
-
-        @Override
-        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        }
-    }
-
-
-    /**
-     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
-     * has occurred on that RecyclerView.
-     * <p>
-     * @see RecyclerView#addOnScrollListener(OnScrollListener)
-     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
-     *
-     */
-    public abstract static class OnScrollListener {
-        /**
-         * Callback method to be invoked when RecyclerView's scroll state changes.
-         *
-         * @param recyclerView The RecyclerView whose scroll state has changed.
-         * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
-         *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
-         */
-        public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
-
-        /**
-         * Callback method to be invoked when the RecyclerView has been scrolled. This will be
-         * called after the scroll has completed.
-         * <p>
-         * This callback will also be called if visible item range changes after a layout
-         * calculation. In that case, dx and dy will be 0.
-         *
-         * @param recyclerView The RecyclerView which scrolled.
-         * @param dx The amount of horizontal scroll.
-         * @param dy The amount of vertical scroll.
-         */
-        public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
-    }
-
-    /**
-     * A RecyclerListener can be set on a RecyclerView to receive messages whenever
-     * a view is recycled.
-     *
-     * @see RecyclerView#setRecyclerListener(RecyclerListener)
-     */
-    public interface RecyclerListener {
-
-        /**
-         * This method is called whenever the view in the ViewHolder is recycled.
-         *
-         * RecyclerView calls this method right before clearing ViewHolder's internal data and
-         * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
-         * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
-         * its adapter position.
-         *
-         * @param holder The ViewHolder containing the view that was recycled
-         */
-        void onViewRecycled(ViewHolder holder);
-    }
-
-    /**
-     * A Listener interface that can be attached to a RecylcerView to get notified
-     * whenever a ViewHolder is attached to or detached from RecyclerView.
-     */
-    public interface OnChildAttachStateChangeListener {
-
-        /**
-         * Called when a view is attached to the RecyclerView.
-         *
-         * @param view The View which is attached to the RecyclerView
-         */
-        void onChildViewAttachedToWindow(View view);
-
-        /**
-         * Called when a view is detached from RecyclerView.
-         *
-         * @param view The View which is being detached from the RecyclerView
-         */
-        void onChildViewDetachedFromWindow(View view);
-    }
-
-    /**
-     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
-     *
-     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
-     * potentially expensive {@link View#findViewById(int)} results.</p>
-     *
-     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
-     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
-     * their own custom ViewHolder implementations to store data that makes binding view contents
-     * easier. Implementations should assume that individual item views will hold strong references
-     * 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 abstract static class ViewHolder {
-        public final View itemView;
-        WeakReference<RecyclerView> mNestedRecyclerView;
-        int mPosition = NO_POSITION;
-        int mOldPosition = NO_POSITION;
-        long mItemId = NO_ID;
-        int mItemViewType = INVALID_TYPE;
-        int mPreLayoutPosition = NO_POSITION;
-
-        // The item that this holder is shadowing during an item change event/animation
-        ViewHolder mShadowedHolder = null;
-        // The item that is shadowing this holder during an item change event/animation
-        ViewHolder mShadowingHolder = null;
-
-        /**
-         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
-         * are all valid.
-         */
-        static final int FLAG_BOUND = 1 << 0;
-
-        /**
-         * The data this ViewHolder's view reflects is stale and needs to be rebound
-         * by the adapter. mPosition and mItemId are consistent.
-         */
-        static final int FLAG_UPDATE = 1 << 1;
-
-        /**
-         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
-         * are not to be trusted and may no longer match the item view type.
-         * This ViewHolder must be fully rebound to different data.
-         */
-        static final int FLAG_INVALID = 1 << 2;
-
-        /**
-         * This ViewHolder points at data that represents an item previously removed from the
-         * data set. Its view may still be used for things like outgoing animations.
-         */
-        static final int FLAG_REMOVED = 1 << 3;
-
-        /**
-         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
-         * and is intended to keep views around during animations.
-         */
-        static final int FLAG_NOT_RECYCLABLE = 1 << 4;
-
-        /**
-         * This ViewHolder is returned from scrap which means we are expecting an addView call
-         * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
-         * the end of the layout pass and then recycled by RecyclerView if it is not added back to
-         * the RecyclerView.
-         */
-        static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
-
-        /**
-         * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
-         * it unless LayoutManager is replaced.
-         * It is still fully visible to the LayoutManager.
-         */
-        static final int FLAG_IGNORE = 1 << 7;
-
-        /**
-         * When the View is detached form the parent, we set this flag so that we can take correct
-         * action when we need to remove it or add it back.
-         */
-        static final int FLAG_TMP_DETACHED = 1 << 8;
-
-        /**
-         * Set when we can no longer determine the adapter position of this ViewHolder until it is
-         * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
-         * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
-         * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
-         * re-calculated.
-         */
-        static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
-
-        /**
-         * Set when a addChangePayload(null) is called
-         */
-        static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
-
-        /**
-         * Used by ItemAnimator when a ViewHolder's position changes
-         */
-        static final int FLAG_MOVED = 1 << 11;
-
-        /**
-         * Used by ItemAnimator when a ViewHolder appears in pre-layout
-         */
-        static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
-
-        static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
-
-        /**
-         * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
-         * hidden list (as if it was scrap) without being recycled in between.
-         *
-         * When a ViewHolder is hidden, there are 2 paths it can be re-used:
-         *   a) Animation ends, view is recycled and used from the recycle pool.
-         *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
-         *
-         * This flag is used to represent "case b" where the ViewHolder is reused without being
-         * recycled (thus "bounced" from the hidden list). This state requires special handling
-         * because the ViewHolder must be added to pre layout maps for animations as if it was
-         * already there.
-         */
-        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;
-
-        List<Object> mPayloads = null;
-        List<Object> mUnmodifiedPayloads = null;
-
-        private int mIsRecyclableCount = 0;
-
-        // If non-null, view is currently considered scrap and may be reused for other data by the
-        // scrap container.
-        private Recycler mScrapContainer = null;
-        // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
-        private boolean mInChangeScrap = false;
-
-        // Saves isImportantForAccessibility value for the view item while it's in hidden state and
-        // marked as unimportant for accessibility.
-        private int mWasImportantForAccessibilityBeforeHidden =
-                ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-        // set if we defer the accessibility state change of the view holder
-        @VisibleForTesting
-        int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
-
-        /**
-         * Is set when VH is bound from the adapter and cleaned right before it is sent to
-         * {@link RecycledViewPool}.
-         */
-        RecyclerView mOwnerRecyclerView;
-
-        public ViewHolder(View itemView) {
-            if (itemView == null) {
-                throw new IllegalArgumentException("itemView may not be null");
-            }
-            this.itemView = itemView;
-        }
-
-        void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
-            addFlags(ViewHolder.FLAG_REMOVED);
-            offsetPosition(offset, applyToPreLayout);
-            mPosition = mNewPosition;
-        }
-
-        void offsetPosition(int offset, boolean applyToPreLayout) {
-            if (mOldPosition == NO_POSITION) {
-                mOldPosition = mPosition;
-            }
-            if (mPreLayoutPosition == NO_POSITION) {
-                mPreLayoutPosition = mPosition;
-            }
-            if (applyToPreLayout) {
-                mPreLayoutPosition += offset;
-            }
-            mPosition += offset;
-            if (itemView.getLayoutParams() != null) {
-                ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
-            }
-        }
-
-        void clearOldPosition() {
-            mOldPosition = NO_POSITION;
-            mPreLayoutPosition = NO_POSITION;
-        }
-
-        void saveOldPosition() {
-            if (mOldPosition == NO_POSITION) {
-                mOldPosition = mPosition;
-            }
-        }
-
-        boolean shouldIgnore() {
-            return (mFlags & FLAG_IGNORE) != 0;
-        }
-
-        /**
-         * @deprecated This method is deprecated because its meaning is ambiguous due to the async
-         * handling of adapter updates. Please use {@link #getLayoutPosition()} or
-         * {@link #getAdapterPosition()} depending on your use case.
-         *
-         * @see #getLayoutPosition()
-         * @see #getAdapterPosition()
-         */
-        @Deprecated
-        public final int getPosition() {
-            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
-        }
-
-        /**
-         * Returns the position of the ViewHolder in terms of the latest layout pass.
-         * <p>
-         * This position is mostly used by RecyclerView components to be consistent while
-         * RecyclerView lazily processes adapter updates.
-         * <p>
-         * For performance and animation reasons, RecyclerView batches all adapter updates until the
-         * next layout pass. This may cause mismatches between the Adapter position of the item and
-         * the position it had in the latest layout calculations.
-         * <p>
-         * LayoutManagers should always call this method while doing calculations based on item
-         * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
-         * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
-         * of the item.
-         * <p>
-         * If LayoutManager needs to call an external method that requires the adapter position of
-         * the item, it can use {@link #getAdapterPosition()} or
-         * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
-         *
-         * @return Returns the adapter position of the ViewHolder in the latest layout pass.
-         * @see #getAdapterPosition()
-         */
-        public final int getLayoutPosition() {
-            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
-        }
-
-        /**
-         * Returns the Adapter position of the item represented by this ViewHolder.
-         * <p>
-         * Note that this might be different than the {@link #getLayoutPosition()} if there are
-         * pending adapter updates but a new layout pass has not happened yet.
-         * <p>
-         * RecyclerView does not handle any adapter updates until the next layout traversal. This
-         * may create temporary inconsistencies between what user sees on the screen and what
-         * adapter contents have. This inconsistency is not important since it will be less than
-         * 16ms but it might be a problem if you want to use ViewHolder position to access the
-         * adapter. Sometimes, you may need to get the exact adapter position to do
-         * some actions in response to user events. In that case, you should use this method which
-         * will calculate the Adapter position of the ViewHolder.
-         * <p>
-         * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
-         * next layout pass, the return value of this method will be {@link #NO_POSITION}.
-         *
-         * @return The adapter position of the item if it still exists in the adapter.
-         * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
-         * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
-         * layout pass or the ViewHolder has already been recycled.
-         */
-        public final int getAdapterPosition() {
-            if (mOwnerRecyclerView == null) {
-                return NO_POSITION;
-            }
-            return mOwnerRecyclerView.getAdapterPositionFor(this);
-        }
-
-        /**
-         * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
-         * to perform animations.
-         * <p>
-         * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
-         * adapter index in the previous layout.
-         *
-         * @return The previous adapter index of the Item represented by this ViewHolder or
-         * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
-         * complete).
-         */
-        public final int getOldPosition() {
-            return mOldPosition;
-        }
-
-        /**
-         * Returns The itemId represented by this ViewHolder.
-         *
-         * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
-         * otherwise
-         */
-        public final long getItemId() {
-            return mItemId;
-        }
-
-        /**
-         * @return The view type of this ViewHolder.
-         */
-        public final int getItemViewType() {
-            return mItemViewType;
-        }
-
-        boolean isScrap() {
-            return mScrapContainer != null;
-        }
-
-        void unScrap() {
-            mScrapContainer.unscrapView(this);
-        }
-
-        boolean wasReturnedFromScrap() {
-            return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
-        }
-
-        void clearReturnedFromScrapFlag() {
-            mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
-        }
-
-        void clearTmpDetachFlag() {
-            mFlags = mFlags & ~FLAG_TMP_DETACHED;
-        }
-
-        void stopIgnoring() {
-            mFlags = mFlags & ~FLAG_IGNORE;
-        }
-
-        void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
-            mScrapContainer = recycler;
-            mInChangeScrap = isChangeScrap;
-        }
-
-        boolean isInvalid() {
-            return (mFlags & FLAG_INVALID) != 0;
-        }
-
-        boolean needsUpdate() {
-            return (mFlags & FLAG_UPDATE) != 0;
-        }
-
-        boolean isBound() {
-            return (mFlags & FLAG_BOUND) != 0;
-        }
-
-        boolean isRemoved() {
-            return (mFlags & FLAG_REMOVED) != 0;
-        }
-
-        boolean hasAnyOfTheFlags(int flags) {
-            return (mFlags & flags) != 0;
-        }
-
-        boolean isTmpDetached() {
-            return (mFlags & FLAG_TMP_DETACHED) != 0;
-        }
-
-        boolean isAdapterPositionUnknown() {
-            return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
-        }
-
-        void setFlags(int flags, int mask) {
-            mFlags = (mFlags & ~mask) | (flags & mask);
-        }
-
-        void addFlags(int flags) {
-            mFlags |= flags;
-        }
-
-        void addChangePayload(Object payload) {
-            if (payload == null) {
-                addFlags(FLAG_ADAPTER_FULLUPDATE);
-            } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
-                createPayloadsIfNeeded();
-                mPayloads.add(payload);
-            }
-        }
-
-        private void createPayloadsIfNeeded() {
-            if (mPayloads == null) {
-                mPayloads = new ArrayList<Object>();
-                mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
-            }
-        }
-
-        void clearPayload() {
-            if (mPayloads != null) {
-                mPayloads.clear();
-            }
-            mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
-        }
-
-        List<Object> getUnmodifiedPayloads() {
-            if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
-                if (mPayloads == null || mPayloads.size() == 0) {
-                    // Initial state,  no update being called.
-                    return FULLUPDATE_PAYLOADS;
-                }
-                // there are none-null payloads
-                return mUnmodifiedPayloads;
-            } else {
-                // a full update has been called.
-                return FULLUPDATE_PAYLOADS;
-            }
-        }
-
-        void resetInternal() {
-            mFlags = 0;
-            mPosition = NO_POSITION;
-            mOldPosition = NO_POSITION;
-            mItemId = NO_ID;
-            mPreLayoutPosition = NO_POSITION;
-            mIsRecyclableCount = 0;
-            mShadowedHolder = null;
-            mShadowingHolder = null;
-            clearPayload();
-            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-            mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
-            clearNestedRecyclerViewIfNotNested(this);
-        }
-
-        /**
-         * Called when the child view enters the hidden state
-         */
-        private void onEnteredHiddenState(RecyclerView parent) {
-            // While the view item is in hidden state, make it invisible for the accessibility.
-            if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
-                mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
-            } else {
-                mWasImportantForAccessibilityBeforeHidden =
-                        ViewCompat.getImportantForAccessibility(itemView);
-            }
-            parent.setChildImportantForAccessibilityInternal(this,
-                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-        }
-
-        /**
-         * Called when the child view leaves the hidden state
-         */
-        private void onLeftHiddenState(RecyclerView parent) {
-            parent.setChildImportantForAccessibilityInternal(this,
-                    mWasImportantForAccessibilityBeforeHidden);
-            mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-        }
-
-        @Override
-        public String toString() {
-            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]");
-            }
-            if (isInvalid()) sb.append(" invalid");
-            if (!isBound()) sb.append(" unbound");
-            if (needsUpdate()) sb.append(" update");
-            if (isRemoved()) sb.append(" removed");
-            if (shouldIgnore()) sb.append(" ignored");
-            if (isTmpDetached()) sb.append(" tmpDetached");
-            if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
-            if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
-
-            if (itemView.getParent() == null) sb.append(" no parent");
-            sb.append("}");
-            return sb.toString();
-        }
-
-        /**
-         * Informs the recycler whether this item can be recycled. Views which are not
-         * recyclable will not be reused for other items until setIsRecyclable() is
-         * later set to true. Calls to setIsRecyclable() should always be paired (one
-         * call to setIsRecyclabe(false) should always be matched with a later call to
-         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
-         * reference-counted.
-         *
-         * @param recyclable Whether this item is available to be recycled. Default value
-         * is true.
-         *
-         * @see #isRecyclable()
-         */
-        public final void setIsRecyclable(boolean recyclable) {
-            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
-            if (mIsRecyclableCount < 0) {
-                mIsRecyclableCount = 0;
-                if (DEBUG) {
-                    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);
-            } else if (!recyclable && mIsRecyclableCount == 1) {
-                mFlags |= FLAG_NOT_RECYCLABLE;
-            } else if (recyclable && mIsRecyclableCount == 0) {
-                mFlags &= ~FLAG_NOT_RECYCLABLE;
-            }
-            if (DEBUG) {
-                Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
-            }
-        }
-
-        /**
-         * @return true if this item is available to be recycled, false otherwise.
-         *
-         * @see #setIsRecyclable(boolean)
-         */
-        public final boolean isRecyclable() {
-            return (mFlags & FLAG_NOT_RECYCLABLE) == 0
-                    && !ViewCompat.hasTransientState(itemView);
-        }
-
-        /**
-         * Returns whether we have animations referring to this view holder or not.
-         * This is similar to isRecyclable flag but does not check transient state.
-         */
-        private boolean shouldBeKeptAsChild() {
-            return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
-        }
-
-        /**
-         * @return True if ViewHolder is not referenced by RecyclerView animations but has
-         * transient state which will prevent it from being recycled.
-         */
-        private boolean doesTransientStatePreventRecycling() {
-            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
-        }
-
-        boolean isUpdated() {
-            return (mFlags & FLAG_UPDATE) != 0;
-        }
-    }
-
-    /**
-     * This method is here so that we can control the important for a11y changes and test it.
-     */
-    @VisibleForTesting
-    boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
-            int importantForAccessibility) {
-        if (isComputingLayout()) {
-            viewHolder.mPendingAccessibilityState = importantForAccessibility;
-            mPendingAccessibilityImportanceChange.add(viewHolder);
-            return false;
-        }
-        ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
-        return true;
-    }
-
-    void dispatchPendingImportantForAccessibilityChanges() {
-        for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
-            ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
-            if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
-                continue;
-            }
-            int state = viewHolder.mPendingAccessibilityState;
-            if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
-                //noinspection WrongConstant
-                ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
-                viewHolder.mPendingAccessibilityState =
-                        ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
-            }
-        }
-        mPendingAccessibilityImportanceChange.clear();
-    }
-
-    int getAdapterPositionFor(ViewHolder viewHolder) {
-        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
-    public void setNestedScrollingEnabled(boolean enabled) {
-        getScrollingChildHelper().setNestedScrollingEnabled(enabled);
-    }
-
-    @Override
-    public boolean isNestedScrollingEnabled() {
-        return getScrollingChildHelper().isNestedScrollingEnabled();
-    }
-
-    @Override
-    public boolean startNestedScroll(int axes) {
-        return getScrollingChildHelper().startNestedScroll(axes);
-    }
-
-    @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,
-                dxUnconsumed, dyUnconsumed, offsetInWindow);
-    }
-
-    @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);
-    }
-
-    @Override
-    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
-        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
-    }
-
-    /**
-     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
-     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
-     * to create their own subclass of this <code>LayoutParams</code> class
-     * to store any additional required per-child view metadata about the layout.
-     */
-    public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
-        ViewHolder mViewHolder;
-        final Rect mDecorInsets = new Rect();
-        boolean mInsetsDirty = true;
-        // Flag is set to true if the view is bound while it is detached from RV.
-        // In this case, we need to manually call invalidate after view is added to guarantee that
-        // invalidation is populated through the View hierarchy
-        boolean mPendingInvalidate = false;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(MarginLayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(LayoutParams source) {
-            super((ViewGroup.LayoutParams) source);
-        }
-
-        /**
-         * Returns true if the view this LayoutParams is attached to needs to have its content
-         * updated from the corresponding adapter.
-         *
-         * @return true if the view should have its content updated
-         */
-        public boolean viewNeedsUpdate() {
-            return mViewHolder.needsUpdate();
-        }
-
-        /**
-         * Returns true if the view this LayoutParams is attached to is now representing
-         * potentially invalid data. A LayoutManager should scrap/recycle it.
-         *
-         * @return true if the view is invalid
-         */
-        public boolean isViewInvalid() {
-            return mViewHolder.isInvalid();
-        }
-
-        /**
-         * Returns true if the adapter data item corresponding to the view this LayoutParams
-         * is attached to has been removed from the data set. A LayoutManager may choose to
-         * treat it differently in order to animate its outgoing or disappearing state.
-         *
-         * @return true if the item the view corresponds to was removed from the data set
-         */
-        public boolean isItemRemoved() {
-            return mViewHolder.isRemoved();
-        }
-
-        /**
-         * Returns true if the adapter data item corresponding to the view this LayoutParams
-         * is attached to has been changed in the data set. A LayoutManager may choose to
-         * treat it differently in order to animate its changing state.
-         *
-         * @return true if the item the view corresponds to was changed in the data set
-         */
-        public boolean isItemChanged() {
-            return mViewHolder.isUpdated();
-        }
-
-        /**
-         * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
-         */
-        @Deprecated
-        public int getViewPosition() {
-            return mViewHolder.getPosition();
-        }
-
-        /**
-         * Returns the adapter position that the view this LayoutParams is attached to corresponds
-         * to as of latest layout calculation.
-         *
-         * @return the adapter position this view as of latest layout pass
-         */
-        public int getViewLayoutPosition() {
-            return mViewHolder.getLayoutPosition();
-        }
-
-        /**
-         * Returns the up-to-date adapter position that the view this LayoutParams is attached to
-         * corresponds to.
-         *
-         * @return the up-to-date adapter position this view. It may return
-         * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
-         * its up-to-date position cannot be calculated.
-         */
-        public int getViewAdapterPosition() {
-            return mViewHolder.getAdapterPosition();
-        }
-    }
-
-    /**
-     * Observer base class for watching changes to an {@link Adapter}.
-     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
-     */
-    public abstract static class AdapterDataObserver {
-        public void onChanged() {
-            // Do nothing
-        }
-
-        public void onItemRangeChanged(int positionStart, int itemCount) {
-            // do nothing
-        }
-
-        public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
-            // fallback to onItemRangeChanged(positionStart, itemCount) if app
-            // does not override this method.
-            onItemRangeChanged(positionStart, itemCount);
-        }
-
-        public void onItemRangeInserted(int positionStart, int itemCount) {
-            // do nothing
-        }
-
-        public void onItemRangeRemoved(int positionStart, int itemCount) {
-            // do nothing
-        }
-
-        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-            // do nothing
-        }
-    }
-
-    /**
-     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
-     * provides methods to trigger a programmatic scroll.</p>
-     *
-     * @see LinearSmoothScroller
-     */
-    public abstract static class SmoothScroller {
-
-        private int mTargetPosition = RecyclerView.NO_POSITION;
-
-        private RecyclerView mRecyclerView;
-
-        private LayoutManager mLayoutManager;
-
-        private boolean mPendingInitialRun;
-
-        private boolean mRunning;
-
-        private View mTargetView;
-
-        private final Action mRecyclingAction;
-
-        public SmoothScroller() {
-            mRecyclingAction = new Action(0, 0);
-        }
-
-        /**
-         * Starts a smooth scroll for the given target position.
-         * <p>In each animation step, {@link RecyclerView} will check
-         * for the target view and call either
-         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
-         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
-         * SmoothScroller is stopped.</p>
-         *
-         * <p>Note that if RecyclerView finds the target view, it will automatically stop the
-         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
-         * stop calling SmoothScroller in each animation step.</p>
-         */
-        void start(RecyclerView recyclerView, LayoutManager layoutManager) {
-            mRecyclerView = recyclerView;
-            mLayoutManager = layoutManager;
-            if (mTargetPosition == RecyclerView.NO_POSITION) {
-                throw new IllegalArgumentException("Invalid target position");
-            }
-            mRecyclerView.mState.mTargetPosition = mTargetPosition;
-            mRunning = true;
-            mPendingInitialRun = true;
-            mTargetView = findViewByPosition(getTargetPosition());
-            onStart();
-            mRecyclerView.mViewFlinger.postOnAnimation();
-        }
-
-        public void setTargetPosition(int targetPosition) {
-            mTargetPosition = targetPosition;
-        }
-
-        /**
-         * @return The LayoutManager to which this SmoothScroller is attached. Will return
-         * <code>null</code> after the SmoothScroller is stopped.
-         */
-        @Nullable
-        public LayoutManager getLayoutManager() {
-            return mLayoutManager;
-        }
-
-        /**
-         * Stops running the SmoothScroller in each animation callback. Note that this does not
-         * cancel any existing {@link Action} updated by
-         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
-         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
-         */
-        protected final void stop() {
-            if (!mRunning) {
-                return;
-            }
-            onStop();
-            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
-            mTargetView = null;
-            mTargetPosition = RecyclerView.NO_POSITION;
-            mPendingInitialRun = false;
-            mRunning = false;
-            // trigger a cleanup
-            mLayoutManager.onSmoothScrollerStopped(this);
-            // clear references to avoid any potential leak by a custom smooth scroller
-            mLayoutManager = null;
-            mRecyclerView = null;
-        }
-
-        /**
-         * Returns true if SmoothScroller has been started but has not received the first
-         * animation
-         * callback yet.
-         *
-         * @return True if this SmoothScroller is waiting to start
-         */
-        public boolean isPendingInitialRun() {
-            return mPendingInitialRun;
-        }
-
-
-        /**
-         * @return True if SmoothScroller is currently active
-         */
-        public boolean isRunning() {
-            return mRunning;
-        }
-
-        /**
-         * Returns the adapter position of the target item
-         *
-         * @return Adapter position of the target item or
-         * {@link RecyclerView#NO_POSITION} if no target view is set.
-         */
-        public int getTargetPosition() {
-            return mTargetPosition;
-        }
-
-        private void onAnimation(int dx, int dy) {
-            final RecyclerView recyclerView = mRecyclerView;
-            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
-                stop();
-            }
-            mPendingInitialRun = false;
-            if (mTargetView != null) {
-                // verify target position
-                if (getChildPosition(mTargetView) == mTargetPosition) {
-                    onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
-                    mRecyclingAction.runIfNecessary(recyclerView);
-                    stop();
-                } else {
-                    Log.e(TAG, "Passed over target position while smooth scrolling.");
-                    mTargetView = null;
-                }
-            }
-            if (mRunning) {
-                onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
-                boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
-                mRecyclingAction.runIfNecessary(recyclerView);
-                if (hadJumpTarget) {
-                    // It is not stopped so needs to be restarted
-                    if (mRunning) {
-                        mPendingInitialRun = true;
-                        recyclerView.mViewFlinger.postOnAnimation();
-                    } else {
-                        stop(); // done
-                    }
-                }
-            }
-        }
-
-        /**
-         * @see RecyclerView#getChildLayoutPosition(android.view.View)
-         */
-        public int getChildPosition(View view) {
-            return mRecyclerView.getChildLayoutPosition(view);
-        }
-
-        /**
-         * @see RecyclerView.LayoutManager#getChildCount()
-         */
-        public int getChildCount() {
-            return mRecyclerView.mLayout.getChildCount();
-        }
-
-        /**
-         * @see RecyclerView.LayoutManager#findViewByPosition(int)
-         */
-        public View findViewByPosition(int position) {
-            return mRecyclerView.mLayout.findViewByPosition(position);
-        }
-
-        /**
-         * @see RecyclerView#scrollToPosition(int)
-         * @deprecated Use {@link Action#jumpTo(int)}.
-         */
-        @Deprecated
-        public void instantScrollToPosition(int position) {
-            mRecyclerView.scrollToPosition(position);
-        }
-
-        protected void onChildAttachedToWindow(View child) {
-            if (getChildPosition(child) == getTargetPosition()) {
-                mTargetView = child;
-                if (DEBUG) {
-                    Log.d(TAG, "smooth scroll target view has been attached");
-                }
-            }
-        }
-
-        /**
-         * Normalizes the vector.
-         * @param scrollVector The vector that points to the target scroll position
-         */
-        protected void normalize(PointF scrollVector) {
-            final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
-                    + scrollVector.y * scrollVector.y);
-            scrollVector.x /= magnitude;
-            scrollVector.y /= magnitude;
-        }
-
-        /**
-         * Called when smooth scroll is started. This might be a good time to do setup.
-         */
-        protected abstract void onStart();
-
-        /**
-         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
-         * @see #stop()
-         */
-        protected abstract void onStop();
-
-        /**
-         * <p>RecyclerView will call this method each time it scrolls until it can find the target
-         * position in the layout.</p>
-         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
-         * provided {@link Action} to define the next scroll.</p>
-         *
-         * @param dx        Last scroll amount horizontally
-         * @param dy        Last scroll amount vertically
-         * @param state     Transient state of RecyclerView
-         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
-         *                  update this object.
-         */
-        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
-         * will receive and it should update the provided {@link Action} to define the scroll
-         * details towards the target view.
-         * @param targetView    The view element which render the target position.
-         * @param state         Transient state of RecyclerView
-         * @param action        Action instance that you should update to define final scroll action
-         *                      towards the targetView
-         */
-        protected abstract void onTargetFound(View targetView, State state, Action action);
-
-        /**
-         * Holds information about a smooth scroll request by a {@link SmoothScroller}.
-         */
-        public static class Action {
-
-            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
-
-            private int mDx;
-
-            private int mDy;
-
-            private int mDuration;
-
-            private int mJumpToPosition = NO_POSITION;
-
-            private Interpolator mInterpolator;
-
-            private boolean mChanged = false;
-
-            // we track this variable to inform custom implementer if they are updating the action
-            // in every animation callback
-            private int mConsecutiveUpdates = 0;
-
-            /**
-             * @param dx Pixels to scroll horizontally
-             * @param dy Pixels to scroll vertically
-             */
-            public Action(int dx, int dy) {
-                this(dx, dy, UNDEFINED_DURATION, null);
-            }
-
-            /**
-             * @param dx       Pixels to scroll horizontally
-             * @param dy       Pixels to scroll vertically
-             * @param duration Duration of the animation in milliseconds
-             */
-            public Action(int dx, int dy, int duration) {
-                this(dx, dy, duration, null);
-            }
-
-            /**
-             * @param dx           Pixels to scroll horizontally
-             * @param dy           Pixels to scroll vertically
-             * @param duration     Duration of the animation in milliseconds
-             * @param interpolator Interpolator to be used when calculating scroll position in each
-             *                     animation step
-             */
-            public Action(int dx, int dy, int duration, Interpolator interpolator) {
-                mDx = dx;
-                mDy = dy;
-                mDuration = duration;
-                mInterpolator = interpolator;
-            }
-
-            /**
-             * Instead of specifying pixels to scroll, use the target position to jump using
-             * {@link RecyclerView#scrollToPosition(int)}.
-             * <p>
-             * You may prefer using this method if scroll target is really far away and you prefer
-             * to jump to a location and smooth scroll afterwards.
-             * <p>
-             * Note that calling this method takes priority over other update methods such as
-             * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
-             * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
-             * {@link #jumpTo(int)}, the other changes will not be considered for this animation
-             * frame.
-             *
-             * @param targetPosition The target item position to scroll to using instant scrolling.
-             */
-            public void jumpTo(int targetPosition) {
-                mJumpToPosition = targetPosition;
-            }
-
-            boolean hasJumpTarget() {
-                return mJumpToPosition >= 0;
-            }
-
-            void runIfNecessary(RecyclerView recyclerView) {
-                if (mJumpToPosition >= 0) {
-                    final int position = mJumpToPosition;
-                    mJumpToPosition = NO_POSITION;
-                    recyclerView.jumpToPositionForSmoothScroller(position);
-                    mChanged = false;
-                    return;
-                }
-                if (mChanged) {
-                    validate();
-                    if (mInterpolator == null) {
-                        if (mDuration == UNDEFINED_DURATION) {
-                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
-                        } else {
-                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
-                        }
-                    } else {
-                        recyclerView.mViewFlinger.smoothScrollBy(
-                                mDx, mDy, mDuration, mInterpolator);
-                    }
-                    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");
-                    }
-                    mChanged = false;
-                } else {
-                    mConsecutiveUpdates = 0;
-                }
-            }
-
-            private void validate() {
-                if (mInterpolator != null && mDuration < 1) {
-                    throw new IllegalStateException("If you provide an interpolator, you must"
-                            + " set a positive duration");
-                } else if (mDuration < 1) {
-                    throw new IllegalStateException("Scroll duration must be a positive number");
-                }
-            }
-
-            public int getDx() {
-                return mDx;
-            }
-
-            public void setDx(int dx) {
-                mChanged = true;
-                mDx = dx;
-            }
-
-            public int getDy() {
-                return mDy;
-            }
-
-            public void setDy(int dy) {
-                mChanged = true;
-                mDy = dy;
-            }
-
-            public int getDuration() {
-                return mDuration;
-            }
-
-            public void setDuration(int duration) {
-                mChanged = true;
-                mDuration = duration;
-            }
-
-            public Interpolator getInterpolator() {
-                return mInterpolator;
-            }
-
-            /**
-             * Sets the interpolator to calculate scroll steps
-             * @param interpolator The interpolator to use. If you specify an interpolator, you must
-             *                     also set the duration.
-             * @see #setDuration(int)
-             */
-            public void setInterpolator(Interpolator interpolator) {
-                mChanged = true;
-                mInterpolator = interpolator;
-            }
-
-            /**
-             * Updates the action with given parameters.
-             * @param dx Pixels to scroll horizontally
-             * @param dy Pixels to scroll vertically
-             * @param duration Duration of the animation in milliseconds
-             * @param interpolator Interpolator to be used when calculating scroll position in each
-             *                     animation step
-             */
-            public void update(int dx, int dy, int duration, Interpolator interpolator) {
-                mDx = dx;
-                mDy = dy;
-                mDuration = duration;
-                mInterpolator = interpolator;
-                mChanged = true;
-            }
-        }
-
-        /**
-         * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
-         * to provide a hint to a {@link SmoothScroller} about the location of the target position.
-         */
-        public interface ScrollVectorProvider {
-            /**
-             * Should calculate the vector that points to the direction where the target position
-             * can be found.
-             * <p>
-             * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
-             * the target position.
-             * <p>
-             * The magnitude of the vector is not important. It is always normalized before being
-             * used by the {@link LinearSmoothScroller}.
-             * <p>
-             * LayoutManager should not check whether the position exists in the adapter or not.
-             *
-             * @param targetPosition the target position to which the returned vector should point
-             *
-             * @return the scroll vector for a given position.
-             */
-            PointF computeScrollVectorForPosition(int targetPosition);
-        }
-    }
-
-    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
-        public boolean hasObservers() {
-            return !mObservers.isEmpty();
-        }
-
-        public void notifyChanged() {
-            // since onChanged() is implemented by the app, it could do anything, including
-            // removing itself from {@link mObservers} - and that could cause problems if
-            // an iterator is used on the ArrayList {@link mObservers}.
-            // to avoid such problems, just march thru the list in the reverse order.
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onChanged();
-            }
-        }
-
-        public void notifyItemRangeChanged(int positionStart, int itemCount) {
-            notifyItemRangeChanged(positionStart, itemCount, null);
-        }
-
-        public void notifyItemRangeChanged(int positionStart, int itemCount,
-                @Nullable Object payload) {
-            // since onItemRangeChanged() is implemented by the app, it could do anything, including
-            // removing itself from {@link mObservers} - and that could cause problems if
-            // an iterator is used on the ArrayList {@link mObservers}.
-            // to avoid such problems, just march thru the list in the reverse order.
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
-            }
-        }
-
-        public void notifyItemRangeInserted(int positionStart, int itemCount) {
-            // since onItemRangeInserted() is implemented by the app, it could do anything,
-            // including removing itself from {@link mObservers} - and that could cause problems if
-            // an iterator is used on the ArrayList {@link mObservers}.
-            // to avoid such problems, just march thru the list in the reverse order.
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
-            }
-        }
-
-        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
-            // since onItemRangeRemoved() is implemented by the app, it could do anything, including
-            // removing itself from {@link mObservers} - and that could cause problems if
-            // an iterator is used on the ArrayList {@link mObservers}.
-            // to avoid such problems, just march thru the list in the reverse order.
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
-            }
-        }
-
-        public void notifyItemMoved(int fromPosition, int toPosition) {
-            for (int i = mObservers.size() - 1; i >= 0; i--) {
-                mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
-            }
-        }
-    }
-
-    /**
-     * This is public so that the CREATOR can be accessed on cold launch.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class SavedState extends AbsSavedState {
-
-        Parcelable mLayoutState;
-
-        /**
-         * called by CREATOR
-         */
-        SavedState(Parcel in, ClassLoader loader) {
-            super(in, loader);
-            mLayoutState = in.readParcelable(
-                    loader != null ? loader : LayoutManager.class.getClassLoader());
-        }
-
-        /**
-         * Called by onSaveInstanceState
-         */
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeParcelable(mLayoutState, 0);
-        }
-
-        void copyFrom(SavedState other) {
-            mLayoutState = other.mLayoutState;
-        }
-
-        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];
-            }
-        };
-    }
-
-    /**
-     * <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
-     * ids.</p>
-     * <p>Often times, RecyclerView components will need to pass information between each other.
-     * To provide a well defined data bus between components, RecyclerView passes the same State
-     * object to component callbacks and these components can use it to exchange data.</p>
-     * <p>If you implement custom components, you can use State's put/get/remove methods to pass
-     * data between your components without needing to manage their lifecycles.</p>
-     */
-    public static class State {
-        static final int STEP_START = 1;
-        static final int STEP_LAYOUT = 1 << 1;
-        static final int STEP_ANIMATIONS = 1 << 2;
-
-        void assertLayoutStep(int accepted) {
-            if ((accepted & mLayoutStep) == 0) {
-                throw new IllegalStateException("Layout state should be one of "
-                        + Integer.toBinaryString(accepted) + " but it is "
-                        + Integer.toBinaryString(mLayoutStep));
-            }
-        }
-
-
-        /** Owned by SmoothScroller */
-        private int mTargetPosition = RecyclerView.NO_POSITION;
-
-        private SparseArray<Object> mData;
-
-        ////////////////////////////////////////////////////////////////////////////////////////////
-        // Fields below are carried from one layout pass to the next
-        ////////////////////////////////////////////////////////////////////////////////////////////
-
-        /**
-         * Number of items adapter had in the previous layout.
-         */
-        int mPreviousLayoutItemCount = 0;
-
-        /**
-         * Number of items that were NOT laid out but has been deleted from the adapter after the
-         * previous layout.
-         */
-        int mDeletedInvisibleItemCountSincePreviousLayout = 0;
-
-        ////////////////////////////////////////////////////////////////////////////////////////////
-        // Fields below must be updated or cleared before they are used (generally before a pass)
-        ////////////////////////////////////////////////////////////////////////////////////////////
-
-        @IntDef(flag = true, value = {
-                STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        @interface LayoutState {}
-
-        @LayoutState
-        int mLayoutStep = STEP_START;
-
-        /**
-         * Number of items adapter has.
-         */
-        int mItemCount = 0;
-
-        boolean mStructureChanged = false;
-
-        /**
-         * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
-         * its {@link LayoutManager} layout items where they will be at the beginning of a set of
-         * predictive item animations.
-         */
-        boolean mInPreLayout = false;
-
-        boolean mTrackOldChangeHolders = false;
-
-        boolean mIsMeasuring = false;
-
-        ////////////////////////////////////////////////////////////////////////////////////////////
-        // Fields below are always reset outside of the pass (or passes) that use them
-        ////////////////////////////////////////////////////////////////////////////////////////////
-
-        boolean mRunSimpleAnimations = false;
-
-        boolean mRunPredictiveAnimations = false;
-
-        /**
-         * This data is saved before a layout calculation happens. After the layout is finished,
-         * if the previously focused view has been replaced with another view for the same item, we
-         * move the focus to the new item automatically.
-         */
-        int mFocusedItemPosition;
-        long mFocusedItemId;
-        // when a sub child has focus, record its id and see if we can directly request focus on
-        // that one instead
-        int mFocusedSubChildId;
-
-        int mRemainingScrollHorizontal;
-        int mRemainingScrollVertical;
-
-        ////////////////////////////////////////////////////////////////////////////////////////////
-
-        State reset() {
-            mTargetPosition = RecyclerView.NO_POSITION;
-            if (mData != null) {
-                mData.clear();
-            }
-            mItemCount = 0;
-            mStructureChanged = false;
-            mIsMeasuring = false;
-            return this;
-        }
-
-        /**
-         * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
-         * prior to any layout passes.
-         *
-         * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
-         * that Recycler#getViewForPosition() can function safely.</p>
-         */
-        void prepareForNestedPrefetch(Adapter adapter) {
-            mLayoutStep = STEP_START;
-            mItemCount = adapter.getItemCount();
-            mInPreLayout = false;
-            mTrackOldChangeHolders = false;
-            mIsMeasuring = false;
-        }
-
-        /**
-         * Returns true if the RecyclerView is currently measuring the layout. This value is
-         * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
-         * has non-exact measurement specs.
-         * <p>
-         * Note that if the LayoutManager supports predictive animations and it is calculating the
-         * pre-layout step, this value will be {@code false} even if the RecyclerView is in
-         * {@code onMeasure} call. This is because pre-layout means the previous state of the
-         * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
-         * LayoutManager is always guaranteed to receive another call to
-         * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
-         *
-         * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
-         */
-        public boolean isMeasuring() {
-            return mIsMeasuring;
-        }
-
-        /**
-         * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
-         * {@link LayoutManager} layout items where they will be at the beginning of a set of
-         * predictive item animations.
-         */
-        public boolean isPreLayout() {
-            return mInPreLayout;
-        }
-
-        /**
-         * Returns whether RecyclerView will run predictive animations in this layout pass
-         * or not.
-         *
-         * @return true if RecyclerView is calculating predictive animations to be run at the end
-         *         of the layout pass.
-         */
-        public boolean willRunPredictiveAnimations() {
-            return mRunPredictiveAnimations;
-        }
-
-        /**
-         * Returns whether RecyclerView will run simple animations in this layout pass
-         * or not.
-         *
-         * @return true if RecyclerView is calculating simple animations to be run at the end of
-         *         the layout pass.
-         */
-        public boolean willRunSimpleAnimations() {
-            return mRunSimpleAnimations;
-        }
-
-        /**
-         * Removes the mapping from the specified id, if there was any.
-         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
-         *                   preserve cross functionality and avoid conflicts.
-         */
-        public void remove(int resourceId) {
-            if (mData == null) {
-                return;
-            }
-            mData.remove(resourceId);
-        }
-
-        /**
-         * Gets the Object mapped from the specified id, or <code>null</code>
-         * if no such data exists.
-         *
-         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
-         *                   to
-         *                   preserve cross functionality and avoid conflicts.
-         */
-        @SuppressWarnings("TypeParameterUnusedInFormals")
-        public <T> T get(int resourceId) {
-            if (mData == null) {
-                return null;
-            }
-            return (T) mData.get(resourceId);
-        }
-
-        /**
-         * Adds a mapping from the specified id to the specified value, replacing the previous
-         * mapping from the specified key if there was one.
-         *
-         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
-         *                   preserve cross functionality and avoid conflicts.
-         * @param data       The data you want to associate with the resourceId.
-         */
-        public void put(int resourceId, Object data) {
-            if (mData == null) {
-                mData = new SparseArray<Object>();
-            }
-            mData.put(resourceId, data);
-        }
-
-        /**
-         * If scroll is triggered to make a certain item visible, this value will return the
-         * adapter index of that item.
-         * @return Adapter index of the target item or
-         * {@link RecyclerView#NO_POSITION} if there is no target
-         * position.
-         */
-        public int getTargetScrollPosition() {
-            return mTargetPosition;
-        }
-
-        /**
-         * Returns if current scroll has a target position.
-         * @return true if scroll is being triggered to make a certain position visible
-         * @see #getTargetScrollPosition()
-         */
-        public boolean hasTargetScrollPosition() {
-            return mTargetPosition != RecyclerView.NO_POSITION;
-        }
-
-        /**
-         * @return true if the structure of the data set has changed since the last call to
-         *         onLayoutChildren, false otherwise
-         */
-        public boolean didStructureChange() {
-            return mStructureChanged;
-        }
-
-        /**
-         * Returns the total number of items that can be laid out. Note that this number is not
-         * necessarily equal to the number of items in the adapter, so you should always use this
-         * number for your position calculations and never access the adapter directly.
-         * <p>
-         * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
-         * data changes on existing Views. These calculations are used to decide which animations
-         * should be run.
-         * <p>
-         * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
-         * present the correct state to LayoutManager in pre-layout pass.
-         * <p>
-         * For example, a newly added item is not included in pre-layout item count because
-         * pre-layout reflects the contents of the adapter before the item is added. Behind the
-         * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
-         * LayoutManager does not know about the new item's existence in pre-layout. The item will
-         * be available in second layout pass and will be included in the item count. Similar
-         * adjustments are made for moved and removed items as well.
-         * <p>
-         * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
-         *
-         * @return The number of items currently available
-         * @see LayoutManager#getItemCount()
-         */
-        public int getItemCount() {
-            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
-                    + ", mIsMeasuring=" + mIsMeasuring
-                    + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
-                    + ", mDeletedInvisibleItemCountSincePreviousLayout="
-                    + mDeletedInvisibleItemCountSincePreviousLayout
-                    + ", mStructureChanged=" + mStructureChanged
-                    + ", mInPreLayout=" + mInPreLayout
-                    + ", mRunSimpleAnimations=" + mRunSimpleAnimations
-                    + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
-                    + '}';
-        }
-    }
-
-    /**
-     * This class defines the behavior of fling if the developer wishes to handle it.
-     * <p>
-     * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
-     *
-     * @see #setOnFlingListener(OnFlingListener)
-     */
-    public abstract static class OnFlingListener {
-
-        /**
-         * Override this to handle a fling given the velocities in both x and y directions.
-         * Note that this method will only be called if the associated {@link LayoutManager}
-         * supports scrolling and the fling is not handled by nested scrolls first.
-         *
-         * @param velocityX the fling velocity on the X axis
-         * @param velocityY the fling velocity on the Y axis
-         *
-         * @return true if the fling was handled, false otherwise.
-         */
-        public abstract boolean onFling(int velocityX, int velocityY);
-    }
-
-    /**
-     * Internal listener that manages items after animations finish. This is how items are
-     * retained (not recycled) during animations, but allowed to be recycled afterwards.
-     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
-     * method on the animator's listener when it is done animating any item.
-     */
-    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
-
-        ItemAnimatorRestoreListener() {
-        }
-
-        @Override
-        public void onAnimationFinished(ViewHolder item) {
-            item.setIsRecyclable(true);
-            if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
-                item.mShadowedHolder = null;
-            }
-            // always null this because an OldViewHolder can never become NewViewHolder w/o being
-            // recycled.
-            item.mShadowingHolder = null;
-            if (!item.shouldBeKeptAsChild()) {
-                if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
-                    removeDetachedView(item.itemView, false);
-                }
-            }
-        }
-    }
-
-    /**
-     * This class defines the animations that take place on items as changes are made
-     * to the adapter.
-     *
-     * Subclasses of ItemAnimator can be used to implement custom animations for actions on
-     * ViewHolder items. The RecyclerView will manage retaining these items while they
-     * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
-     * when a ViewHolder's animation is finished. In other words, there must be a matching
-     * {@link #dispatchAnimationFinished(ViewHolder)} call for each
-     * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
-     * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-     * animateChange()}
-     * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
-     * and
-     * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-     * animateDisappearance()} call.
-     *
-     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
-     *
-     * @see #setItemAnimator(ItemAnimator)
-     */
-    @SuppressWarnings("UnusedParameters")
-    public abstract static class ItemAnimator {
-
-        /**
-         * The Item represented by this ViewHolder is updated.
-         * <p>
-         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
-         */
-        public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
-
-        /**
-         * The Item represented by this ViewHolder is removed from the adapter.
-         * <p>
-         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
-         */
-        public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
-
-        /**
-         * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
-         * represented by this ViewHolder is invalid.
-         * <p>
-         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
-         */
-        public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
-
-        /**
-         * The position of the Item represented by this ViewHolder has been changed. This flag is
-         * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
-         * any adapter change that may have a side effect on this item. (e.g. The item before this
-         * one has been removed from the Adapter).
-         * <p>
-         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
-         */
-        public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
-
-        /**
-         * This ViewHolder was not laid out but has been added to the layout in pre-layout state
-         * by the {@link LayoutManager}. This means that the item was already in the Adapter but
-         * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
-         * to add new items in pre-layout to specify their virtual location when they are invisible
-         * (e.g. to specify the item should <i>animate in</i> from below the visible area).
-         * <p>
-         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
-         */
-        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 = {
-                FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
-                FLAG_APPEARED_IN_PRE_LAYOUT
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface AdapterChanges {}
-        private ItemAnimatorListener mListener = null;
-        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
-                new ArrayList<ItemAnimatorFinishedListener>();
-
-        private long mAddDuration = 120;
-        private long mRemoveDuration = 120;
-        private long mMoveDuration = 250;
-        private long mChangeDuration = 250;
-
-        /**
-         * Gets the current duration for which all move animations will run.
-         *
-         * @return The current move duration
-         */
-        public long getMoveDuration() {
-            return mMoveDuration;
-        }
-
-        /**
-         * Sets the duration for which all move animations will run.
-         *
-         * @param moveDuration The move duration
-         */
-        public void setMoveDuration(long moveDuration) {
-            mMoveDuration = moveDuration;
-        }
-
-        /**
-         * Gets the current duration for which all add animations will run.
-         *
-         * @return The current add duration
-         */
-        public long getAddDuration() {
-            return mAddDuration;
-        }
-
-        /**
-         * Sets the duration for which all add animations will run.
-         *
-         * @param addDuration The add duration
-         */
-        public void setAddDuration(long addDuration) {
-            mAddDuration = addDuration;
-        }
-
-        /**
-         * Gets the current duration for which all remove animations will run.
-         *
-         * @return The current remove duration
-         */
-        public long getRemoveDuration() {
-            return mRemoveDuration;
-        }
-
-        /**
-         * Sets the duration for which all remove animations will run.
-         *
-         * @param removeDuration The remove duration
-         */
-        public void setRemoveDuration(long removeDuration) {
-            mRemoveDuration = removeDuration;
-        }
-
-        /**
-         * Gets the current duration for which all change animations will run.
-         *
-         * @return The current change duration
-         */
-        public long getChangeDuration() {
-            return mChangeDuration;
-        }
-
-        /**
-         * Sets the duration for which all change animations will run.
-         *
-         * @param changeDuration The change duration
-         */
-        public void setChangeDuration(long changeDuration) {
-            mChangeDuration = changeDuration;
-        }
-
-        /**
-         * Internal only:
-         * Sets the listener that must be called when the animator is finished
-         * animating the item (or immediately if no animation happens). This is set
-         * internally and is not intended to be set by external code.
-         *
-         * @param listener The listener that must be called.
-         */
-        void setListener(ItemAnimatorListener listener) {
-            mListener = listener;
-        }
-
-        /**
-         * Called by the RecyclerView before the layout begins. Item animator should record
-         * necessary information about the View before it is potentially rebound, moved or removed.
-         * <p>
-         * The data returned from this method will be passed to the related <code>animate**</code>
-         * methods.
-         * <p>
-         * Note that this method may be called after pre-layout phase if LayoutManager adds new
-         * Views to the layout in pre-layout pass.
-         * <p>
-         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
-         * the View and the adapter change flags.
-         *
-         * @param state       The current State of RecyclerView which includes some useful data
-         *                    about the layout that will be calculated.
-         * @param viewHolder  The ViewHolder whose information should be recorded.
-         * @param changeFlags Additional information about what changes happened in the Adapter
-         *                    about the Item represented by this ViewHolder. For instance, if
-         *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
-         * @param payloads    The payload list that was previously passed to
-         *                    {@link Adapter#notifyItemChanged(int, Object)} or
-         *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
-         *
-         * @return An ItemHolderInfo instance that preserves necessary information about the
-         * ViewHolder. This object will be passed back to related <code>animate**</code> methods
-         * after layout is complete.
-         *
-         * @see #recordPostLayoutInformation(State, ViewHolder)
-         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         */
-        public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
-                @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
-                @NonNull List<Object> payloads) {
-            return obtainHolderInfo().setFrom(viewHolder);
-        }
-
-        /**
-         * Called by the RecyclerView after the layout is complete. Item animator should record
-         * necessary information about the View's final state.
-         * <p>
-         * The data returned from this method will be passed to the related <code>animate**</code>
-         * methods.
-         * <p>
-         * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
-         * the View.
-         *
-         * @param state      The current State of RecyclerView which includes some useful data about
-         *                   the layout that will be calculated.
-         * @param viewHolder The ViewHolder whose information should be recorded.
-         *
-         * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
-         * This object will be passed back to related <code>animate**</code> methods when
-         * RecyclerView decides how items should be animated.
-         *
-         * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
-         * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         */
-        public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
-                @NonNull ViewHolder viewHolder) {
-            return obtainHolderInfo().setFrom(viewHolder);
-        }
-
-        /**
-         * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
-         * <p>
-         * This means that the View was a child of the LayoutManager when layout started but has
-         * been removed by the LayoutManager. It might have been removed from the adapter or simply
-         * become invisible due to other factors. You can distinguish these two cases by checking
-         * the change flags that were passed to
-         * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         * <p>
-         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
-         * animation callback method which will be called by the RecyclerView depends on the
-         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
-         * LayoutManager's decision whether to layout the changed version of a disappearing
-         * ViewHolder or not. RecyclerView will call
-         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
-         * returns {@code false} from
-         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
-         * LayoutManager lays out a new disappearing view that holds the updated information.
-         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
-         * <p>
-         * If LayoutManager supports predictive animations, it might provide a target disappear
-         * location for the View by laying it out in that location. When that happens,
-         * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
-         * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
-         * <p>
-         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
-         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
-         * decides not to animate the view).
-         *
-         * @param viewHolder    The ViewHolder which should be animated
-         * @param preLayoutInfo The information that was returned from
-         *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         * @param postLayoutInfo The information that was returned from
-         *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
-         *                       null if the LayoutManager did not layout the item.
-         *
-         * @return true if a later call to {@link #runPendingAnimations()} is requested,
-         * false otherwise.
-         */
-        public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
-                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
-
-        /**
-         * Called by the RecyclerView when a ViewHolder is added to the layout.
-         * <p>
-         * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
-         * but has  been added by the LayoutManager. It might be newly added to the adapter or
-         * simply become visible due to other factors.
-         * <p>
-         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
-         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
-         * decides not to animate the view).
-         *
-         * @param viewHolder     The ViewHolder which should be animated
-         * @param preLayoutInfo  The information that was returned from
-         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         *                       Might be null if Item was just added to the adapter or
-         *                       LayoutManager does not support predictive animations or it could
-         *                       not predict that this ViewHolder will become visible.
-         * @param postLayoutInfo The information that was returned from {@link
-         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         *
-         * @return true if a later call to {@link #runPendingAnimations()} is requested,
-         * false otherwise.
-         */
-        public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
-                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
-
-        /**
-         * Called by the RecyclerView when a ViewHolder is present in both before and after the
-         * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
-         * for it or a {@link Adapter#notifyDataSetChanged()} call.
-         * <p>
-         * This ViewHolder still represents the same data that it was representing when the layout
-         * started but its position / size may be changed by the LayoutManager.
-         * <p>
-         * If the Item's layout position didn't change, RecyclerView still calls this method because
-         * it does not track this information (or does not necessarily know that an animation is
-         * not required). Your ItemAnimator should handle this case and if there is nothing to
-         * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
-         * <code>false</code>.
-         * <p>
-         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
-         * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
-         * decides not to animate the view).
-         *
-         * @param viewHolder     The ViewHolder which should be animated
-         * @param preLayoutInfo  The information that was returned from
-         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         * @param postLayoutInfo The information that was returned from {@link
-         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         *
-         * @return true if a later call to {@link #runPendingAnimations()} is requested,
-         * false otherwise.
-         */
-        public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
-                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
-
-        /**
-         * Called by the RecyclerView when an adapter item is present both before and after the
-         * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
-         * for it. This method may also be called when
-         * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
-         * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
-         * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
-         * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
-         * called for the new ViewHolder and the old one will be recycled.
-         * <p>
-         * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
-         * a good possibility that item contents didn't really change but it is rebound from the
-         * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
-         * screen didn't change and your animator should handle this case as well and avoid creating
-         * unnecessary animations.
-         * <p>
-         * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
-         * previous presentation of the item as-is and supply a new ViewHolder for the updated
-         * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
-         * This is useful if you don't know the contents of the Item and would like
-         * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
-         * <p>
-         * When you are writing a custom item animator for your layout, it might be more performant
-         * and elegant to re-use the same ViewHolder and animate the content changes manually.
-         * <p>
-         * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
-         * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
-         * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
-         * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
-         * which represent the same Item. In that case, only the new ViewHolder is visible
-         * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
-         * <p>
-         * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
-         * ViewHolder when their animation is complete
-         * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
-         * animate the view).
-         * <p>
-         *  If oldHolder and newHolder are the same instance, you should call
-         * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
-         * <p>
-         * Note that when a ViewHolder both changes and disappears in the same layout pass, the
-         * animation callback method which will be called by the RecyclerView depends on the
-         * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
-         * LayoutManager's decision whether to layout the changed version of a disappearing
-         * ViewHolder or not. RecyclerView will call
-         * {@code animateChange} instead of
-         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
-         * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
-         * LayoutManager lays out a new disappearing view that holds the updated information.
-         * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
-         *
-         * @param oldHolder     The ViewHolder before the layout is started, might be the same
-         *                      instance with newHolder.
-         * @param newHolder     The ViewHolder after the layout is finished, might be the same
-         *                      instance with oldHolder.
-         * @param preLayoutInfo  The information that was returned from
-         *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         * @param postLayoutInfo The information that was returned from {@link
-         *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
-         *
-         * @return true if a later call to {@link #runPendingAnimations()} is requested,
-         * false otherwise.
-         */
-        public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
-                @NonNull ViewHolder newHolder,
-                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
-
-        @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
-            int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
-            if (viewHolder.isInvalid()) {
-                return FLAG_INVALIDATED;
-            }
-            if ((flags & FLAG_INVALIDATED) == 0) {
-                final int oldPos = viewHolder.getOldPosition();
-                final int pos = viewHolder.getAdapterPosition();
-                if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
-                    flags |= FLAG_MOVED;
-                }
-            }
-            return flags;
-        }
-
-        /**
-         * Called when there are pending animations waiting to be started. This state
-         * is governed by the return values from
-         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateAppearance()},
-         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateChange()}
-         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animatePersistence()}, and
-         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
-         * called later to start the associated animations. runPendingAnimations() will be scheduled
-         * to be run on the next frame.
-         */
-        public abstract void runPendingAnimations();
-
-        /**
-         * Method called when an animation on a view should be ended immediately.
-         * This could happen when other events, like scrolling, occur, so that
-         * animating views can be quickly put into their proper end locations.
-         * Implementations should ensure that any animations running on the item
-         * are canceled and affected properties are set to their end values.
-         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
-         * animation since the animations are effectively done when this method is called.
-         *
-         * @param item The item for which an animation should be stopped.
-         */
-        public abstract void endAnimation(ViewHolder item);
-
-        /**
-         * Method called when all item animations should be ended immediately.
-         * This could happen when other events, like scrolling, occur, so that
-         * animating views can be quickly put into their proper end locations.
-         * Implementations should ensure that any animations running on any items
-         * are canceled and affected properties are set to their end values.
-         * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
-         * animation since the animations are effectively done when this method is called.
-         */
-        public abstract void endAnimations();
-
-        /**
-         * Method which returns whether there are any item animations currently running.
-         * This method can be used to determine whether to delay other actions until
-         * animations end.
-         *
-         * @return true if there are any item animations currently running, false otherwise.
-         */
-        public abstract boolean isRunning();
-
-        /**
-         * Method to be called by subclasses when an animation is finished.
-         * <p>
-         * For each call RecyclerView makes to
-         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateAppearance()},
-         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animatePersistence()}, or
-         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateDisappearance()}, there
-         * should
-         * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
-         * <p>
-         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
-         * and <code>newHolder</code>  (if they are not the same instance).
-         *
-         * @param viewHolder The ViewHolder whose animation is finished.
-         * @see #onAnimationFinished(ViewHolder)
-         */
-        public final void dispatchAnimationFinished(ViewHolder viewHolder) {
-            onAnimationFinished(viewHolder);
-            if (mListener != null) {
-                mListener.onAnimationFinished(viewHolder);
-            }
-        }
-
-        /**
-         * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
-         * ItemAnimator.
-         *
-         * @param viewHolder The ViewHolder whose animation is finished. There might still be other
-         *                   animations running on this ViewHolder.
-         * @see #dispatchAnimationFinished(ViewHolder)
-         */
-        public void onAnimationFinished(ViewHolder viewHolder) {
-        }
-
-        /**
-         * Method to be called by subclasses when an animation is started.
-         * <p>
-         * For each call RecyclerView makes to
-         * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateAppearance()},
-         * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animatePersistence()}, or
-         * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateDisappearance()}, there should be a matching
-         * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
-         * <p>
-         * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
-         * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
-         * and <code>newHolder</code> (if they are not the same instance).
-         * <p>
-         * If your ItemAnimator decides not to animate a ViewHolder, it should call
-         * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
-         * {@link #dispatchAnimationStarted(ViewHolder)}.
-         *
-         * @param viewHolder The ViewHolder whose animation is starting.
-         * @see #onAnimationStarted(ViewHolder)
-         */
-        public final void dispatchAnimationStarted(ViewHolder viewHolder) {
-            onAnimationStarted(viewHolder);
-        }
-
-        /**
-         * Called when a new animation is started on the given ViewHolder.
-         *
-         * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
-         *                   might already be animating and this might be another animation.
-         * @see #dispatchAnimationStarted(ViewHolder)
-         */
-        public void onAnimationStarted(ViewHolder viewHolder) {
-
-        }
-
-        /**
-         * Like {@link #isRunning()}, this method returns whether there are any item
-         * animations currently running. Additionally, the listener passed in will be called
-         * when there are no item animations running, either immediately (before the method
-         * returns) if no animations are currently running, or when the currently running
-         * animations are {@link #dispatchAnimationsFinished() finished}.
-         *
-         * <p>Note that the listener is transient - it is either called immediately and not
-         * stored at all, or stored only until it is called when running animations
-         * are finished sometime later.</p>
-         *
-         * @param listener A listener to be called immediately if no animations are running
-         * or later when currently-running animations have finished. A null listener is
-         * equivalent to calling {@link #isRunning()}.
-         * @return true if there are any item animations currently running, false otherwise.
-         */
-        public final boolean isRunning(ItemAnimatorFinishedListener listener) {
-            boolean running = isRunning();
-            if (listener != null) {
-                if (!running) {
-                    listener.onAnimationsFinished();
-                } else {
-                    mFinishedListeners.add(listener);
-                }
-            }
-            return running;
-        }
-
-        /**
-         * When an item is changed, ItemAnimator can decide whether it wants to re-use
-         * the same ViewHolder for animations or RecyclerView should create a copy of the
-         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
-         * <p>
-         * Note that this method will only be called if the {@link ViewHolder} still has the same
-         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
-         * both {@link ViewHolder}s in the
-         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
-         * <p>
-         * If your application is using change payloads, you can override
-         * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
-         *
-         * @param viewHolder The ViewHolder which represents the changed item's old content.
-         *
-         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
-         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
-         *         ItemAnimator to animate. Default implementation returns <code>true</code>.
-         *
-         * @see #canReuseUpdatedViewHolder(ViewHolder, List)
-         */
-        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
-            return true;
-        }
-
-        /**
-         * When an item is changed, ItemAnimator can decide whether it wants to re-use
-         * the same ViewHolder for animations or RecyclerView should create a copy of the
-         * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
-         * <p>
-         * Note that this method will only be called if the {@link ViewHolder} still has the same
-         * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
-         * both {@link ViewHolder}s in the
-         * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
-         *
-         * @param viewHolder The ViewHolder which represents the changed item's old content.
-         * @param payloads A non-null list of merged payloads that were sent with change
-         *                 notifications. Can be empty if the adapter is invalidated via
-         *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
-         *                 payloads will be passed into
-         *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
-         *                 method <b>if</b> this method returns <code>true</code>.
-         *
-         * @return True if RecyclerView should just rebind to the same ViewHolder or false if
-         *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
-         *         ItemAnimator to animate. Default implementation calls
-         *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
-         *
-         * @see #canReuseUpdatedViewHolder(ViewHolder)
-         */
-        public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
-                @NonNull List<Object> payloads) {
-            return canReuseUpdatedViewHolder(viewHolder);
-        }
-
-        /**
-         * This method should be called by ItemAnimator implementations to notify
-         * any listeners that all pending and active item animations are finished.
-         */
-        public final void dispatchAnimationsFinished() {
-            final int count = mFinishedListeners.size();
-            for (int i = 0; i < count; ++i) {
-                mFinishedListeners.get(i).onAnimationsFinished();
-            }
-            mFinishedListeners.clear();
-        }
-
-        /**
-         * Returns a new {@link ItemHolderInfo} which will be used to store information about the
-         * ViewHolder. This information will later be passed into <code>animate**</code> methods.
-         * <p>
-         * You can override this method if you want to extend {@link ItemHolderInfo} and provide
-         * your own instances.
-         *
-         * @return A new {@link ItemHolderInfo}.
-         */
-        public ItemHolderInfo obtainHolderInfo() {
-            return new ItemHolderInfo();
-        }
-
-        /**
-         * The interface to be implemented by listeners to animation events from this
-         * ItemAnimator. This is used internally and is not intended for developers to
-         * create directly.
-         */
-        interface ItemAnimatorListener {
-            void onAnimationFinished(ViewHolder item);
-        }
-
-        /**
-         * This interface is used to inform listeners when all pending or running animations
-         * in an ItemAnimator are finished. This can be used, for example, to delay an action
-         * in a data set until currently-running animations are complete.
-         *
-         * @see #isRunning(ItemAnimatorFinishedListener)
-         */
-        public interface ItemAnimatorFinishedListener {
-            /**
-             * Notifies when all pending or running animations in an ItemAnimator are finished.
-             */
-            void onAnimationsFinished();
-        }
-
-        /**
-         * A simple data structure that holds information about an item's bounds.
-         * This information is used in calculating item animations. Default implementation of
-         * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
-         * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
-         * structure. You can extend this class if you would like to keep more information about
-         * the Views.
-         * <p>
-         * If you want to provide your own implementation but still use `super` methods to record
-         * basic information, you can override {@link #obtainHolderInfo()} to provide your own
-         * instances.
-         */
-        public static class ItemHolderInfo {
-
-            /**
-             * The left edge of the View (excluding decorations)
-             */
-            public int left;
-
-            /**
-             * The top edge of the View (excluding decorations)
-             */
-            public int top;
-
-            /**
-             * The right edge of the View (excluding decorations)
-             */
-            public int right;
-
-            /**
-             * The bottom edge of the View (excluding decorations)
-             */
-            public int bottom;
-
-            /**
-             * The change flags that were passed to
-             * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
-             */
-            @AdapterChanges
-            public int changeFlags;
-
-            public ItemHolderInfo() {
-            }
-
-            /**
-             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
-             * the given ViewHolder. Clears all {@link #changeFlags}.
-             *
-             * @param holder The ViewHolder whose bounds should be copied.
-             * @return This {@link ItemHolderInfo}
-             */
-            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
-                return setFrom(holder, 0);
-            }
-
-            /**
-             * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
-             * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
-             *
-             * @param holder The ViewHolder whose bounds should be copied.
-             * @param flags  The adapter change flags that were passed into
-             *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
-             *               List)}.
-             * @return This {@link ItemHolderInfo}
-             */
-            public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
-                    @AdapterChanges int flags) {
-                final View view = holder.itemView;
-                this.left = view.getLeft();
-                this.top = view.getTop();
-                this.right = view.getRight();
-                this.bottom = view.getBottom();
-                return this;
-            }
-        }
-    }
-
-    @Override
-    protected int getChildDrawingOrder(int childCount, int i) {
-        if (mChildDrawingOrderCallback == null) {
-            return super.getChildDrawingOrder(childCount, i);
-        } else {
-            return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
-        }
-    }
-
-    /**
-     * A callback interface that can be used to alter the drawing order of RecyclerView children.
-     * <p>
-     * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
-     * that applies to that method also applies to this callback. For example, changing the drawing
-     * order of two views will not have any effect if their elevation values are different since
-     * elevation overrides the result of this callback.
-     */
-    public interface ChildDrawingOrderCallback {
-        /**
-         * Returns the index of the child to draw for this iteration. Override this
-         * if you want to change the drawing order of children. By default, it
-         * returns i.
-         *
-         * @param i The current iteration.
-         * @return The index of the child to draw this iteration.
-         *
-         * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
-         */
-        int onGetChildDrawingOrder(int childCount, int i);
-    }
-
-    private NestedScrollingChildHelper getScrollingChildHelper() {
-        if (mScrollingChildHelper == null) {
-            mScrollingChildHelper = new NestedScrollingChildHelper(this);
-        }
-        return mScrollingChildHelper;
-    }
-}
diff --git a/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java b/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
deleted file mode 100644
index ba7260d..0000000
--- a/android/support/v7/widget/RecyclerViewAccessibilityDelegate.java
+++ /dev/null
@@ -1,130 +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.os.Bundle;
-import android.support.v4.view.AccessibilityDelegateCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-
-/**
- * The AccessibilityDelegate used by RecyclerView.
- * <p>
- * This class handles basic accessibility actions and delegates them to LayoutManager.
- */
-public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegateCompat {
-    final RecyclerView mRecyclerView;
-    final AccessibilityDelegateCompat mItemDelegate;
-
-
-    public RecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
-        mRecyclerView = recyclerView;
-        mItemDelegate = new ItemDelegate(this);
-    }
-
-    boolean shouldIgnore() {
-        return mRecyclerView.hasPendingAdapterUpdates();
-    }
-
-    @Override
-    public boolean performAccessibilityAction(View host, int action, Bundle args) {
-        if (super.performAccessibilityAction(host, action, args)) {
-            return true;
-        }
-        if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
-            return mRecyclerView.getLayoutManager().performAccessibilityAction(action, args);
-        }
-
-        return false;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-        super.onInitializeAccessibilityNodeInfo(host, info);
-        info.setClassName(RecyclerView.class.getName());
-        if (!shouldIgnore() && mRecyclerView.getLayoutManager() != null) {
-            mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(info);
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(host, event);
-        event.setClassName(RecyclerView.class.getName());
-        if (host instanceof RecyclerView && !shouldIgnore()) {
-            RecyclerView rv = (RecyclerView) host;
-            if (rv.getLayoutManager() != null) {
-                rv.getLayoutManager().onInitializeAccessibilityEvent(event);
-            }
-        }
-    }
-
-    /**
-     * Gets the AccessibilityDelegate for an individual item in the RecyclerView.
-     * A basic item delegate is provided by default, but you can override this
-     * method to provide a custom per-item delegate.
-     */
-    public AccessibilityDelegateCompat getItemDelegate() {
-        return mItemDelegate;
-    }
-
-    /**
-     * 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 (!mRecyclerViewDelegate.shouldIgnore()
-                    && mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
-                mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
-                        .onInitializeAccessibilityNodeInfoForItem(host, info);
-            }
-        }
-
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle args) {
-            if (super.performAccessibilityAction(host, action, args)) {
-                return true;
-            }
-            if (!mRecyclerViewDelegate.shouldIgnore()
-                    && mRecyclerViewDelegate.mRecyclerView.getLayoutManager() != null) {
-                return mRecyclerViewDelegate.mRecyclerView.getLayoutManager()
-                        .performAccessibilityActionForItem(host, action, args);
-            }
-            return false;
-        }
-    }
-}
-
diff --git a/android/support/v7/widget/ResourcesWrapper.java b/android/support/v7/widget/ResourcesWrapper.java
deleted file mode 100644
index 8c9c864..0000000
--- a/android/support/v7/widget/ResourcesWrapper.java
+++ /dev/null
@@ -1,284 +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.widget;
-
-import android.content.res.AssetFileDescriptor;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-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;
-
-/**
- * This extends Resources but delegates the calls to another Resources object. This enables
- * any customization done by some subclass of Resources to be also picked up.
- */
-class ResourcesWrapper extends Resources {
-
-    private final Resources mResources;
-
-    public ResourcesWrapper(Resources resources) {
-        super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
-        mResources = resources;
-    }
-
-    @Override
-    public CharSequence getText(int id) throws NotFoundException {
-        return mResources.getText(id);
-    }
-
-    @Override
-    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
-        return mResources.getQuantityText(id, quantity);
-    }
-
-    @Override
-    public String getString(int id) throws NotFoundException {
-        return mResources.getString(id);
-    }
-
-    @Override
-    public String getString(int id, Object... formatArgs) throws NotFoundException {
-        return mResources.getString(id, formatArgs);
-    }
-
-    @Override
-    public String getQuantityString(int id, int quantity, Object... formatArgs)
-            throws NotFoundException {
-        return mResources.getQuantityString(id, quantity, formatArgs);
-    }
-
-    @Override
-    public String getQuantityString(int id, int quantity) throws NotFoundException {
-        return mResources.getQuantityString(id, quantity);
-    }
-
-    @Override
-    public CharSequence getText(int id, CharSequence def) {
-        return mResources.getText(id, def);
-    }
-
-    @Override
-    public CharSequence[] getTextArray(int id) throws NotFoundException {
-        return mResources.getTextArray(id);
-    }
-
-    @Override
-    public String[] getStringArray(int id) throws NotFoundException {
-        return mResources.getStringArray(id);
-    }
-
-    @Override
-    public int[] getIntArray(int id) throws NotFoundException {
-        return mResources.getIntArray(id);
-    }
-
-    @Override
-    public TypedArray obtainTypedArray(int id) throws NotFoundException {
-        return mResources.obtainTypedArray(id);
-    }
-
-    @Override
-    public float getDimension(int id) throws NotFoundException {
-        return mResources.getDimension(id);
-    }
-
-    @Override
-    public int getDimensionPixelOffset(int id) throws NotFoundException {
-        return mResources.getDimensionPixelOffset(id);
-    }
-
-    @Override
-    public int getDimensionPixelSize(int id) throws NotFoundException {
-        return mResources.getDimensionPixelSize(id);
-    }
-
-    @Override
-    public float getFraction(int id, int base, int pbase) {
-        return mResources.getFraction(id, base, pbase);
-    }
-
-    @Override
-    public Drawable getDrawable(int id) throws NotFoundException {
-        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);
-    }
-
-    @Override
-    public Movie getMovie(int id) throws NotFoundException {
-        return mResources.getMovie(id);
-    }
-
-    @Override
-    public int getColor(int id) throws NotFoundException {
-        return mResources.getColor(id);
-    }
-
-    @Override
-    public ColorStateList getColorStateList(int id) throws NotFoundException {
-        return mResources.getColorStateList(id);
-    }
-
-    @Override
-    public boolean getBoolean(int id) throws NotFoundException {
-        return mResources.getBoolean(id);
-    }
-
-    @Override
-    public int getInteger(int id) throws NotFoundException {
-        return mResources.getInteger(id);
-    }
-
-    @Override
-    public XmlResourceParser getLayout(int id) throws NotFoundException {
-        return mResources.getLayout(id);
-    }
-
-    @Override
-    public XmlResourceParser getAnimation(int id) throws NotFoundException {
-        return mResources.getAnimation(id);
-    }
-
-    @Override
-    public XmlResourceParser getXml(int id) throws NotFoundException {
-        return mResources.getXml(id);
-    }
-
-    @Override
-    public InputStream openRawResource(int id) throws NotFoundException {
-        return mResources.openRawResource(id);
-    }
-
-    @Override
-    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
-        return mResources.openRawResource(id, value);
-    }
-
-    @Override
-    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
-        return mResources.openRawResourceFd(id);
-    }
-
-    @Override
-    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
-            throws NotFoundException {
-        mResources.getValue(id, outValue, resolveRefs);
-    }
-
-    @RequiresApi(15)
-    @Override
-    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
-            throws NotFoundException {
-        mResources.getValueForDensity(id, density, outValue, resolveRefs);
-    }
-
-    @Override
-    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
-            throws NotFoundException {
-        mResources.getValue(name, outValue, resolveRefs);
-    }
-
-    @Override
-    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
-        return mResources.obtainAttributes(set, attrs);
-    }
-
-    @Override
-    public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
-        super.updateConfiguration(config, metrics);
-        if (mResources != null) { // called from super's constructor. So, need to check.
-            mResources.updateConfiguration(config, metrics);
-        }
-    }
-
-    @Override
-    public DisplayMetrics getDisplayMetrics() {
-        return mResources.getDisplayMetrics();
-    }
-
-    @Override
-    public Configuration getConfiguration() {
-        return mResources.getConfiguration();
-    }
-
-    @Override
-    public int getIdentifier(String name, String defType, String defPackage) {
-        return mResources.getIdentifier(name, defType, defPackage);
-    }
-
-    @Override
-    public String getResourceName(int resid) throws NotFoundException {
-        return mResources.getResourceName(resid);
-    }
-
-    @Override
-    public String getResourcePackageName(int resid) throws NotFoundException {
-        return mResources.getResourcePackageName(resid);
-    }
-
-    @Override
-    public String getResourceTypeName(int resid) throws NotFoundException {
-        return mResources.getResourceTypeName(resid);
-    }
-
-    @Override
-    public String getResourceEntryName(int resid) throws NotFoundException {
-        return mResources.getResourceEntryName(resid);
-    }
-
-    @Override
-    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
-            throws XmlPullParserException, IOException {
-        mResources.parseBundleExtras(parser, outBundle);
-    }
-
-    @Override
-    public void parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle)
-            throws XmlPullParserException {
-        mResources.parseBundleExtra(tagName, attrs, outBundle);
-    }
-}
-
diff --git a/android/support/v7/widget/RoundRectDrawable.java b/android/support/v7/widget/RoundRectDrawable.java
deleted file mode 100644
index b8afdf7..0000000
--- a/android/support/v7/widget/RoundRectDrawable.java
+++ /dev/null
@@ -1,214 +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 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/android/support/v7/widget/RoundRectDrawableWithShadow.java b/android/support/v7/widget/RoundRectDrawableWithShadow.java
deleted file mode 100644
index 3250f8d..0000000
--- a/android/support/v7/widget/RoundRectDrawableWithShadow.java
+++ /dev/null
@@ -1,387 +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.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/android/support/v7/widget/RtlSpacingHelper.java b/android/support/v7/widget/RtlSpacingHelper.java
deleted file mode 100644
index 6a4e360..0000000
--- a/android/support/v7/widget/RtlSpacingHelper.java
+++ /dev/null
@@ -1,91 +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;
-
-/**
- * RtlSpacingHelper manages the relationship between left/right and start/end for views
- * that need to maintain both absolute and relative settings for a form of spacing similar
- * to view padding.
- */
-class RtlSpacingHelper {
-    public static final int UNDEFINED = Integer.MIN_VALUE;
-
-    private int mLeft = 0;
-    private int mRight = 0;
-    private int mStart = UNDEFINED;
-    private int mEnd = UNDEFINED;
-    private int mExplicitLeft = 0;
-    private int mExplicitRight = 0;
-
-    private boolean mIsRtl = false;
-    private boolean mIsRelative = false;
-
-    public int getLeft() {
-        return mLeft;
-    }
-
-    public int getRight() {
-        return mRight;
-    }
-
-    public int getStart() {
-        return mIsRtl ? mRight : mLeft;
-    }
-
-    public int getEnd() {
-        return mIsRtl ? mLeft : mRight;
-    }
-
-    public void setRelative(int start, int end) {
-        mStart = start;
-        mEnd = end;
-        mIsRelative = true;
-        if (mIsRtl) {
-            if (end != UNDEFINED) mLeft = end;
-            if (start != UNDEFINED) mRight = start;
-        } else {
-            if (start != UNDEFINED) mLeft = start;
-            if (end != UNDEFINED) mRight = end;
-        }
-    }
-
-    public void setAbsolute(int left, int right) {
-        mIsRelative = false;
-        if (left != UNDEFINED) mLeft = mExplicitLeft = left;
-        if (right != UNDEFINED) mRight = mExplicitRight = right;
-    }
-
-    public void setDirection(boolean isRtl) {
-        if (isRtl == mIsRtl) {
-            return;
-        }
-        mIsRtl = isRtl;
-        if (mIsRelative) {
-            if (isRtl) {
-                mLeft = mEnd != UNDEFINED ? mEnd : mExplicitLeft;
-                mRight = mStart != UNDEFINED ? mStart : mExplicitRight;
-            } else {
-                mLeft = mStart != UNDEFINED ? mStart : mExplicitLeft;
-                mRight = mEnd != UNDEFINED ? mEnd : mExplicitRight;
-            }
-        } else {
-            mLeft = mExplicitLeft;
-            mRight = mExplicitRight;
-        }
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/ScrollbarHelper.java b/android/support/v7/widget/ScrollbarHelper.java
deleted file mode 100644
index ae42baa..0000000
--- a/android/support/v7/widget/ScrollbarHelper.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.v7.widget;
-
-import android.view.View;
-
-/**
- * A helper class to do scroll offset calculations.
- */
-class ScrollbarHelper {
-
-    /**
-     * @param startChild View closest to start of the list. (top or left)
-     * @param endChild   View closest to end of the list (bottom or right)
-     */
-    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) {
-            return 0;
-        }
-        final int minPosition = Math.min(lm.getPosition(startChild),
-                lm.getPosition(endChild));
-        final int maxPosition = Math.max(lm.getPosition(startChild),
-                lm.getPosition(endChild));
-        final int itemsBefore = reverseLayout
-                ? Math.max(0, state.getItemCount() - maxPosition - 1)
-                : Math.max(0, minPosition);
-        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 float avgSizePerRow = (float) laidOutArea / itemRange;
-
-        return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
-                - orientation.getDecoratedStart(startChild)));
-    }
-
-    /**
-     * @param startChild View closest to start of the list. (top or left)
-     * @param endChild   View closest to end of the list (bottom or right)
-     */
-    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) {
-            return 0;
-        }
-        if (!smoothScrollbarEnabled) {
-            return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
-        }
-        final int extend = orientation.getDecoratedEnd(endChild)
-                - orientation.getDecoratedStart(startChild);
-        return Math.min(orientation.getTotalSpace(), extend);
-    }
-
-    /**
-     * @param startChild View closest to start of the list. (top or left)
-     * @param endChild   View closest to end of the list (bottom or right)
-     */
-    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) {
-            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))
-                + 1;
-        // estimate a size for full list.
-        return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
-    }
-}
diff --git a/android/support/v7/widget/ScrollingTabContainerView.java b/android/support/v7/widget/ScrollingTabContainerView.java
deleted file mode 100644
index 345e318..0000000
--- a/android/support/v7/widget/ScrollingTabContainerView.java
+++ /dev/null
@@ -1,596 +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.v7.widget;
-
-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.support.annotation.RestrictTo;
-import android.support.v4.view.GravityCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.ActionBarPolicy;
-import android.text.TextUtils;
-import android.text.TextUtils.TruncateAt;
-import android.view.Gravity;
-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;
-import android.view.animation.Interpolator;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-/**
- * This widget implements the dynamic action bar tab behavior that can change across different
- * configurations or circumstances.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ScrollingTabContainerView extends HorizontalScrollView
-        implements AdapterView.OnItemSelectedListener {
-
-    private static final String TAG = "ScrollingTabContainerView";
-    Runnable mTabSelector;
-    private TabClickListener mTabClickListener;
-
-    LinearLayoutCompat mTabLayout;
-    private Spinner mTabSpinner;
-    private boolean mAllowCollapse;
-
-    int mMaxTabWidth;
-    int mStackedTabMaxWidth;
-    private int mContentHeight;
-    private int mSelectedTabIndex;
-
-    protected ViewPropertyAnimator mVisibilityAnim;
-    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
-
-    private static final Interpolator sAlphaInterpolator = new DecelerateInterpolator();
-
-    private static final int FADE_DURATION = 200;
-
-    public ScrollingTabContainerView(Context context) {
-        super(context);
-
-        setHorizontalScrollBarEnabled(false);
-
-        ActionBarPolicy abp = ActionBarPolicy.get(context);
-        setContentHeight(abp.getTabContainerHeight());
-        mStackedTabMaxWidth = abp.getStackedTabMaxWidth();
-
-        mTabLayout = createTabLayout();
-        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-    }
-
-    @Override
-    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
-        setFillViewport(lockedExpanded);
-
-        final int childCount = mTabLayout.getChildCount();
-        if (childCount > 1 &&
-                (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
-            if (childCount > 2) {
-                mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
-            } else {
-                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
-            }
-            mMaxTabWidth = Math.min(mMaxTabWidth, mStackedTabMaxWidth);
-        } else {
-            mMaxTabWidth = -1;
-        }
-
-        heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
-
-        final boolean canCollapse = !lockedExpanded && mAllowCollapse;
-
-        if (canCollapse) {
-            // See if we should expand
-            mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec);
-            if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) {
-                performCollapse();
-            } else {
-                performExpand();
-            }
-        } else {
-            performExpand();
-        }
-
-        final int oldWidth = getMeasuredWidth();
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        final int newWidth = getMeasuredWidth();
-
-        if (lockedExpanded && oldWidth != newWidth) {
-            // Recenter the tab display if we're at a new (scrollable) size.
-            setTabSelected(mSelectedTabIndex);
-        }
-    }
-
-    /**
-     * Indicates whether this view is collapsed into a dropdown menu instead
-     * of traditional tabs.
-     * @return true if showing as a spinner
-     */
-    private boolean isCollapsed() {
-        return mTabSpinner != null && mTabSpinner.getParent() == this;
-    }
-
-    public void setAllowCollapse(boolean allowCollapse) {
-        mAllowCollapse = allowCollapse;
-    }
-
-    private void performCollapse() {
-        if (isCollapsed()) return;
-
-        if (mTabSpinner == null) {
-            mTabSpinner = createSpinner();
-        }
-        removeView(mTabLayout);
-        addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-        if (mTabSpinner.getAdapter() == null) {
-            mTabSpinner.setAdapter(new TabAdapter());
-        }
-        if (mTabSelector != null) {
-            removeCallbacks(mTabSelector);
-            mTabSelector = null;
-        }
-        mTabSpinner.setSelection(mSelectedTabIndex);
-    }
-
-    private boolean performExpand() {
-        if (!isCollapsed()) return false;
-
-        removeView(mTabSpinner);
-        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-        setTabSelected(mTabSpinner.getSelectedItemPosition());
-        return false;
-    }
-
-    public void setTabSelected(int position) {
-        mSelectedTabIndex = position;
-        final int tabCount = mTabLayout.getChildCount();
-        for (int i = 0; i < tabCount; i++) {
-            final View child = mTabLayout.getChildAt(i);
-            final boolean isSelected = i == position;
-            child.setSelected(isSelected);
-            if (isSelected) {
-                animateToTab(position);
-            }
-        }
-        if (mTabSpinner != null && position >= 0) {
-            mTabSpinner.setSelection(position);
-        }
-    }
-
-    public void setContentHeight(int contentHeight) {
-        mContentHeight = contentHeight;
-        requestLayout();
-    }
-
-    private LinearLayoutCompat createTabLayout() {
-        final LinearLayoutCompat tabLayout = new LinearLayoutCompat(getContext(), null,
-                R.attr.actionBarTabBarStyle);
-        tabLayout.setMeasureWithLargestChildEnabled(true);
-        tabLayout.setGravity(Gravity.CENTER);
-        tabLayout.setLayoutParams(new LinearLayoutCompat.LayoutParams(
-                LinearLayoutCompat.LayoutParams.WRAP_CONTENT, LinearLayoutCompat.LayoutParams.MATCH_PARENT));
-        return tabLayout;
-    }
-
-    private Spinner createSpinner() {
-        final Spinner spinner = new AppCompatSpinner(getContext(), null,
-                R.attr.actionDropDownStyle);
-        spinner.setLayoutParams(new LinearLayoutCompat.LayoutParams(
-                LinearLayoutCompat.LayoutParams.WRAP_CONTENT,
-                LinearLayoutCompat.LayoutParams.MATCH_PARENT));
-        spinner.setOnItemSelectedListener(this);
-        return spinner;
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        ActionBarPolicy abp = ActionBarPolicy.get(getContext());
-        // Action bar can change size on configuration changes.
-        // Reread the desired height from the theme-specified style.
-        setContentHeight(abp.getTabContainerHeight());
-        mStackedTabMaxWidth = abp.getStackedTabMaxWidth();
-    }
-
-    public void animateToVisibility(int visibility) {
-        if (mVisibilityAnim != null) {
-            mVisibilityAnim.cancel();
-        }
-        if (visibility == VISIBLE) {
-            if (getVisibility() != VISIBLE) {
-                setAlpha(0f);
-            }
-
-            ViewPropertyAnimator anim = animate().alpha(1f);
-            anim.setDuration(FADE_DURATION);
-
-            anim.setInterpolator(sAlphaInterpolator);
-            anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
-            anim.start();
-        } else {
-            ViewPropertyAnimator anim = animate().alpha(0f);
-            anim.setDuration(FADE_DURATION);
-
-            anim.setInterpolator(sAlphaInterpolator);
-            anim.setListener(mVisAnimListener.withFinalVisibility(anim, visibility));
-            anim.start();
-        }
-    }
-
-    public void animateToTab(final int position) {
-        final View tabView = mTabLayout.getChildAt(position);
-        if (mTabSelector != null) {
-            removeCallbacks(mTabSelector);
-        }
-        mTabSelector = new Runnable() {
-            @Override
-            public void run() {
-                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
-                smoothScrollTo(scrollPos, 0);
-                mTabSelector = null;
-            }
-        };
-        post(mTabSelector);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (mTabSelector != null) {
-            // Re-post the selector we saved
-            post(mTabSelector);
-        }
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mTabSelector != null) {
-            removeCallbacks(mTabSelector);
-        }
-    }
-
-    TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
-        final TabView tabView = new TabView(getContext(), tab, forAdapter);
-        if (forAdapter) {
-            tabView.setBackgroundDrawable(null);
-            tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
-                    mContentHeight));
-        } else {
-            tabView.setFocusable(true);
-
-            if (mTabClickListener == null) {
-                mTabClickListener = new TabClickListener();
-            }
-            tabView.setOnClickListener(mTabClickListener);
-        }
-        return tabView;
-    }
-
-    public void addTab(ActionBar.Tab tab, boolean setSelected) {
-        TabView tabView = createTabView(tab, false);
-        mTabLayout.addView(tabView, new LinearLayoutCompat.LayoutParams(0,
-                LayoutParams.MATCH_PARENT, 1));
-        if (mTabSpinner != null) {
-            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
-        }
-        if (setSelected) {
-            tabView.setSelected(true);
-        }
-        if (mAllowCollapse) {
-            requestLayout();
-        }
-    }
-
-    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
-        final TabView tabView = createTabView(tab, false);
-        mTabLayout.addView(tabView, position, new LinearLayoutCompat.LayoutParams(
-                0, LayoutParams.MATCH_PARENT, 1));
-        if (mTabSpinner != null) {
-            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
-        }
-        if (setSelected) {
-            tabView.setSelected(true);
-        }
-        if (mAllowCollapse) {
-            requestLayout();
-        }
-    }
-
-    public void updateTab(int position) {
-        ((TabView) mTabLayout.getChildAt(position)).update();
-        if (mTabSpinner != null) {
-            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
-        }
-        if (mAllowCollapse) {
-            requestLayout();
-        }
-    }
-
-    public void removeTabAt(int position) {
-        mTabLayout.removeViewAt(position);
-        if (mTabSpinner != null) {
-            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
-        }
-        if (mAllowCollapse) {
-            requestLayout();
-        }
-    }
-
-    public void removeAllTabs() {
-        mTabLayout.removeAllViews();
-        if (mTabSpinner != null) {
-            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
-        }
-        if (mAllowCollapse) {
-            requestLayout();
-        }
-    }
-
-    @Override
-    public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
-        TabView tabView = (TabView) view;
-        tabView.getTab().select();
-    }
-
-    @Override
-    public void onNothingSelected(AdapterView<?> adapterView) {
-        // no-op
-    }
-
-    private class TabView extends LinearLayoutCompat {
-        private final int[] BG_ATTRS = {
-                android.R.attr.background
-        };
-
-        private ActionBar.Tab mTab;
-        private TextView mTextView;
-        private ImageView mIconView;
-        private View mCustomView;
-
-        public TabView(Context context, ActionBar.Tab tab, boolean forList) {
-            super(context, null, R.attr.actionBarTabStyle);
-            mTab = tab;
-
-            TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, BG_ATTRS,
-                    R.attr.actionBarTabStyle, 0);
-            if (a.hasValue(0)) {
-                setBackgroundDrawable(a.getDrawable(0));
-            }
-            a.recycle();
-
-            if (forList) {
-                setGravity(GravityCompat.START | Gravity.CENTER_VERTICAL);
-            }
-
-            update();
-        }
-
-        public void bindTab(ActionBar.Tab tab) {
-            mTab = tab;
-            update();
-        }
-
-        @Override
-        public void setSelected(boolean selected) {
-            final boolean changed = (isSelected() != selected);
-            super.setSelected(selected);
-            if (changed && selected) {
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
-            }
-        }
-
-        @Override
-        public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(event);
-            // This view masquerades as an action bar tab.
-            event.setClassName(ActionBar.Tab.class.getName());
-        }
-
-        @Override
-        public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-            super.onInitializeAccessibilityNodeInfo(info);
-
-            // This view masquerades as an action bar tab.
-            info.setClassName(ActionBar.Tab.class.getName());
-        }
-
-        @Override
-        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            // Re-measure if we went beyond our maximum size.
-            if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
-                super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
-                        heightMeasureSpec);
-            }
-        }
-
-        public void update() {
-            final ActionBar.Tab tab = mTab;
-            final View custom = tab.getCustomView();
-            if (custom != null) {
-                final ViewParent customParent = custom.getParent();
-                if (customParent != this) {
-                    if (customParent != null) ((ViewGroup) customParent).removeView(custom);
-                    addView(custom);
-                }
-                mCustomView = custom;
-                if (mTextView != null) mTextView.setVisibility(GONE);
-                if (mIconView != null) {
-                    mIconView.setVisibility(GONE);
-                    mIconView.setImageDrawable(null);
-                }
-            } else {
-                if (mCustomView != null) {
-                    removeView(mCustomView);
-                    mCustomView = null;
-                }
-
-                final Drawable icon = tab.getIcon();
-                final CharSequence text = tab.getText();
-
-                if (icon != null) {
-                    if (mIconView == null) {
-                        ImageView iconView = new AppCompatImageView(getContext());
-                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                                LayoutParams.WRAP_CONTENT);
-                        lp.gravity = Gravity.CENTER_VERTICAL;
-                        iconView.setLayoutParams(lp);
-                        addView(iconView, 0);
-                        mIconView = iconView;
-                    }
-                    mIconView.setImageDrawable(icon);
-                    mIconView.setVisibility(VISIBLE);
-                } else if (mIconView != null) {
-                    mIconView.setVisibility(GONE);
-                    mIconView.setImageDrawable(null);
-                }
-
-                final boolean hasText = !TextUtils.isEmpty(text);
-                if (hasText) {
-                    if (mTextView == null) {
-                        TextView textView = new AppCompatTextView(getContext(), null,
-                                R.attr.actionBarTabTextStyle);
-                        textView.setEllipsize(TruncateAt.END);
-                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                                LayoutParams.WRAP_CONTENT);
-                        lp.gravity = Gravity.CENTER_VERTICAL;
-                        textView.setLayoutParams(lp);
-                        addView(textView);
-                        mTextView = textView;
-                    }
-                    mTextView.setText(text);
-                    mTextView.setVisibility(VISIBLE);
-                } else if (mTextView != null) {
-                    mTextView.setVisibility(GONE);
-                    mTextView.setText(null);
-                }
-
-                if (mIconView != null) {
-                    mIconView.setContentDescription(tab.getContentDescription());
-                }
-                TooltipCompat.setTooltipText(this, hasText ? null : tab.getContentDescription());
-            }
-        }
-
-        public ActionBar.Tab getTab() {
-            return mTab;
-        }
-    }
-
-    private class TabAdapter extends BaseAdapter {
-        TabAdapter() {
-        }
-
-        @Override
-        public int getCount() {
-            return mTabLayout.getChildCount();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return ((TabView) mTabLayout.getChildAt(position)).getTab();
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = createTabView((ActionBar.Tab) getItem(position), true);
-            } else {
-                ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
-            }
-            return convertView;
-        }
-    }
-
-    private class TabClickListener implements OnClickListener {
-        TabClickListener() {
-        }
-
-        @Override
-        public void onClick(View view) {
-            TabView tabView = (TabView) view;
-            tabView.getTab().select();
-            final int tabCount = mTabLayout.getChildCount();
-            for (int i = 0; i < tabCount; i++) {
-                final View child = mTabLayout.getChildAt(i);
-                child.setSelected(child == view);
-            }
-        }
-    }
-
-    protected class VisibilityAnimListener extends AnimatorListenerAdapter {
-        private boolean mCanceled = false;
-        private int mFinalVisibility;
-
-        public VisibilityAnimListener withFinalVisibility(ViewPropertyAnimator animation,
-                int visibility) {
-            mFinalVisibility = visibility;
-            mVisibilityAnim = animation;
-            return this;
-        }
-
-        @Override
-        public void onAnimationStart(Animator animator) {
-            setVisibility(VISIBLE);
-            mCanceled = false;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            if (mCanceled) return;
-
-            mVisibilityAnim = null;
-            setVisibility(mFinalVisibility);
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            mCanceled = true;
-        }
-    }
-}
-
diff --git a/android/support/v7/widget/SearchView.java b/android/support/v7/widget/SearchView.java
deleted file mode 100644
index f017cf5..0000000
--- a/android/support/v7/widget/SearchView.java
+++ /dev/null
@@ -1,2075 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v7.widget.SuggestionsAdapter.getColumnString;
-
-import android.app.PendingIntent;
-import android.app.SearchManager;
-import android.app.SearchableInfo;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.speech.RecognizerIntent;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.CursorAdapter;
-import android.support.v7.appcompat.R;
-import android.support.v7.view.CollapsibleActionView;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.style.ImageSpan;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.TouchDelegate;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.AutoCompleteTextView;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
-
-import java.lang.reflect.Method;
-import java.util.WeakHashMap;
-
-/**
- * A widget that provides a user interface for the user to enter a search query and submit a request
- * to a search provider. Shows a list of query suggestions or results, if available, and allows the
- * user to pick a suggestion or result to launch into.
- *
- * <p class="note"><strong>Note:</strong> This class is included in the <a
- * href="{@docRoot}tools/extras/support-library.html">support library</a> for compatibility
- * with API level 7 and higher. If you're developing your app for API level 11 and higher
- * <em>only</em>, you should instead use the framework {@link android.widget.SearchView} class.</p>
- *
- * <p>
- * When the SearchView is used in an {@link android.support.v7.app.ActionBar}
- * as an action view, it's collapsed by default, so you must provide an icon for the action.
- * </p>
- * <p>
- * If you want the search field to always be visible, then call
- * {@link #setIconifiedByDefault(boolean) setIconifiedByDefault(false)}.
- * </p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For information about using {@code SearchView}, read the
- * <a href="{@docRoot}guide/topics/search/index.html">Search</a> API guide.
- * Additional information about action views is also available in the <<a
- * href="{@docRoot}guide/topics/ui/actionbar.html#ActionView">Action Bar</a> API guide</p>
- * </div>
- *
- * @see android.view.MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
- */
-public class SearchView extends LinearLayoutCompat implements CollapsibleActionView {
-
-    static final boolean DBG = false;
-    static final String LOG_TAG = "SearchView";
-
-    /**
-     * Private constant for removing the microphone in the keyboard.
-     */
-    private static final String IME_OPTION_NO_MICROPHONE = "nm";
-
-    final SearchAutoComplete mSearchSrcTextView;
-    private final View mSearchEditFrame;
-    private final View mSearchPlate;
-    private final View mSubmitArea;
-    final ImageView mSearchButton;
-    final ImageView mGoButton;
-    final ImageView mCloseButton;
-    final ImageView mVoiceButton;
-    private final View mDropDownAnchor;
-
-    private UpdatableTouchDelegate mTouchDelegate;
-    private Rect mSearchSrcTextViewBounds = new Rect();
-    private Rect mSearchSrtTextViewBoundsExpanded = new Rect();
-    private int[] mTemp = new int[2];
-    private int[] mTemp2 = new int[2];
-
-    /** Icon optionally displayed when the SearchView is collapsed. */
-    private final ImageView mCollapsedIcon;
-
-    /** Drawable used as an EditText hint. */
-    private final Drawable mSearchHintIcon;
-
-    // Resources used by SuggestionsAdapter to display suggestions.
-    private final int mSuggestionRowLayout;
-    private final int mSuggestionCommitIconResId;
-
-    // Intents used for voice searching.
-    private final Intent mVoiceWebSearchIntent;
-    private final Intent mVoiceAppSearchIntent;
-
-    private final CharSequence mDefaultQueryHint;
-
-    private OnQueryTextListener mOnQueryChangeListener;
-    private OnCloseListener mOnCloseListener;
-    OnFocusChangeListener mOnQueryTextFocusChangeListener;
-    private OnSuggestionListener mOnSuggestionListener;
-    private OnClickListener mOnSearchClickListener;
-
-    private boolean mIconifiedByDefault;
-    private boolean mIconified;
-    CursorAdapter mSuggestionsAdapter;
-    private boolean mSubmitButtonEnabled;
-    private CharSequence mQueryHint;
-    private boolean mQueryRefinement;
-    private boolean mClearingFocus;
-    private int mMaxWidth;
-    private boolean mVoiceButtonEnabled;
-    private CharSequence mOldQueryText;
-    private CharSequence mUserQuery;
-    private boolean mExpandedInActionView;
-    private int mCollapsedImeOptions;
-
-    SearchableInfo mSearchable;
-    private Bundle mAppSearchData;
-
-    static final AutoCompleteTextViewReflector HIDDEN_METHOD_INVOKER = new AutoCompleteTextViewReflector();
-
-    private final Runnable mUpdateDrawableStateRunnable = new Runnable() {
-        @Override
-        public void run() {
-            updateFocusedState();
-        }
-    };
-
-    private Runnable mReleaseCursorRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mSuggestionsAdapter != null && mSuggestionsAdapter instanceof SuggestionsAdapter) {
-                mSuggestionsAdapter.changeCursor(null);
-            }
-        }
-    };
-
-    // A weak map of drawables we've gotten from other packages, so we don't load them
-    // more than once.
-    private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache =
-            new WeakHashMap<String, Drawable.ConstantState>();
-
-    /**
-     * Callbacks for changes to the query text.
-     */
-    public interface OnQueryTextListener {
-
-        /**
-         * Called when the user submits the query. This could be due to a key press on the
-         * keyboard or due to pressing a submit button.
-         * The listener can override the standard behavior by returning true
-         * to indicate that it has handled the submit request. Otherwise return false to
-         * let the SearchView handle the submission by launching any associated intent.
-         *
-         * @param query the query text that is to be submitted
-         *
-         * @return true if the query has been handled by the listener, false to let the
-         * SearchView perform the default action.
-         */
-        boolean onQueryTextSubmit(String query);
-
-        /**
-         * Called when the query text is changed by the user.
-         *
-         * @param newText the new content of the query text field.
-         *
-         * @return false if the SearchView should perform the default action of showing any
-         * suggestions if available, true if the action was handled by the listener.
-         */
-        boolean onQueryTextChange(String newText);
-    }
-
-    public interface OnCloseListener {
-
-        /**
-         * The user is attempting to close the SearchView.
-         *
-         * @return true if the listener wants to override the default behavior of clearing the
-         * text field and dismissing it, false otherwise.
-         */
-        boolean onClose();
-    }
-
-    /**
-     * Callback interface for selection events on suggestions. These callbacks
-     * are only relevant when a SearchableInfo has been specified by {@link #setSearchableInfo}.
-     */
-    public interface OnSuggestionListener {
-
-        /**
-         * Called when a suggestion was selected by navigating to it.
-         * @param position the absolute position in the list of suggestions.
-         *
-         * @return true if the listener handles the event and wants to override the default
-         * behavior of possibly rewriting the query based on the selected item, false otherwise.
-         */
-        boolean onSuggestionSelect(int position);
-
-        /**
-         * Called when a suggestion was clicked.
-         * @param position the absolute position of the clicked item in the list of suggestions.
-         *
-         * @return true if the listener handles the event and wants to override the default
-         * behavior of launching any intent or submitting a search query specified on that item.
-         * Return false otherwise.
-         */
-        boolean onSuggestionClick(int position);
-    }
-
-    public SearchView(Context context) {
-        this(context, null);
-    }
-
-    public SearchView(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.searchViewStyle);
-    }
-
-    public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
-                attrs, R.styleable.SearchView, defStyleAttr, 0);
-
-        final LayoutInflater inflater = LayoutInflater.from(context);
-        final int layoutResId = a.getResourceId(
-                R.styleable.SearchView_layout, R.layout.abc_search_view);
-        inflater.inflate(layoutResId, this, true);
-
-        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 = 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,
-                a.getDrawable(R.styleable.SearchView_queryBackground));
-        ViewCompat.setBackground(mSubmitArea,
-                a.getDrawable(R.styleable.SearchView_submitBackground));
-        mSearchButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
-        mGoButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
-        mCloseButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_closeIcon));
-        mVoiceButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_voiceIcon));
-        mCollapsedIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));
-
-        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);
-        mSuggestionCommitIconResId = a.getResourceId(R.styleable.SearchView_commitIcon, 0);
-
-        mSearchButton.setOnClickListener(mOnClickListener);
-        mCloseButton.setOnClickListener(mOnClickListener);
-        mGoButton.setOnClickListener(mOnClickListener);
-        mVoiceButton.setOnClickListener(mOnClickListener);
-        mSearchSrcTextView.setOnClickListener(mOnClickListener);
-
-        mSearchSrcTextView.addTextChangedListener(mTextWatcher);
-        mSearchSrcTextView.setOnEditorActionListener(mOnEditorActionListener);
-        mSearchSrcTextView.setOnItemClickListener(mOnItemClickListener);
-        mSearchSrcTextView.setOnItemSelectedListener(mOnItemSelectedListener);
-        mSearchSrcTextView.setOnKeyListener(mTextKeyListener);
-
-        // Inform any listener of focus changes
-        mSearchSrcTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (mOnQueryTextFocusChangeListener != null) {
-                    mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus);
-                }
-            }
-        });
-        setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true));
-
-        final int maxWidth = a.getDimensionPixelSize(R.styleable.SearchView_android_maxWidth, -1);
-        if (maxWidth != -1) {
-            setMaxWidth(maxWidth);
-        }
-
-        mDefaultQueryHint = a.getText(R.styleable.SearchView_defaultQueryHint);
-        mQueryHint = a.getText(R.styleable.SearchView_queryHint);
-
-        final int imeOptions = a.getInt(R.styleable.SearchView_android_imeOptions, -1);
-        if (imeOptions != -1) {
-            setImeOptions(imeOptions);
-        }
-
-        final int inputType = a.getInt(R.styleable.SearchView_android_inputType, -1);
-        if (inputType != -1) {
-            setInputType(inputType);
-        }
-
-        boolean focusable = true;
-        focusable = a.getBoolean(R.styleable.SearchView_android_focusable, focusable);
-        setFocusable(focusable);
-
-        a.recycle();
-
-        // Save voice intent for later queries/launching
-        mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
-        mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
-                RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
-
-        mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
-        mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        mDropDownAnchor = findViewById(mSearchSrcTextView.getDropDownAnchor());
-        if (mDropDownAnchor != null) {
-            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();
-    }
-
-    int getSuggestionRowLayout() {
-        return mSuggestionRowLayout;
-    }
-
-    int getSuggestionCommitIconResId() {
-        return mSuggestionCommitIconResId;
-    }
-
-    /**
-     * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
-     * to display labels, hints, suggestions, create intents for launching search results screens
-     * and controlling other affordances such as a voice button.
-     *
-     * @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific
-     * activity or a global search provider.
-     */
-    public void setSearchableInfo(SearchableInfo searchable) {
-        mSearchable = searchable;
-        if (mSearchable != null) {
-            updateSearchAutoComplete();
-            updateQueryHint();
-        }
-        // Cache the voice search capability
-        mVoiceButtonEnabled = hasVoiceSearch();
-
-        if (mVoiceButtonEnabled) {
-            // Disable the microphone on the keyboard, as a mic is displayed near the text box
-            // TODO: use imeOptions to disable voice input when the new API will be available
-            mSearchSrcTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
-        }
-        updateViewsVisibility(isIconified());
-    }
-
-    /**
-     * Sets the APP_DATA for legacy SearchDialog use.
-     * @param appSearchData bundle provided by the app when launching the search dialog
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setAppSearchData(Bundle appSearchData) {
-        mAppSearchData = appSearchData;
-    }
-
-    /**
-     * Sets the IME options on the query text field.
-     *
-     * @see TextView#setImeOptions(int)
-     * @param imeOptions the options to set on the query text field
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_android_imeOptions
-     */
-    public void setImeOptions(int imeOptions) {
-        mSearchSrcTextView.setImeOptions(imeOptions);
-    }
-
-    /**
-     * Returns the IME options set on the query text field.
-     * @return the ime options
-     * @see TextView#setImeOptions(int)
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_android_imeOptions
-     */
-    public int getImeOptions() {
-        return mSearchSrcTextView.getImeOptions();
-    }
-
-    /**
-     * Sets the input type on the query text field.
-     *
-     * @see TextView#setInputType(int)
-     * @param inputType the input type to set on the query text field
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_android_inputType
-     */
-    public void setInputType(int inputType) {
-        mSearchSrcTextView.setInputType(inputType);
-    }
-
-    /**
-     * Returns the input type set on the query text field.
-     * @return the input type
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_android_inputType
-     */
-    public int getInputType() {
-        return mSearchSrcTextView.getInputType();
-    }
-
-    @Override
-    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
-        // Don't accept focus if in the middle of clearing focus
-        if (mClearingFocus) return false;
-        // Check if SearchView is focusable.
-        if (!isFocusable()) return false;
-        // If it is not iconified, then give the focus to the text field
-        if (!isIconified()) {
-            boolean result = mSearchSrcTextView.requestFocus(direction, previouslyFocusedRect);
-            if (result) {
-                updateViewsVisibility(false);
-            }
-            return result;
-        } else {
-            return super.requestFocus(direction, previouslyFocusedRect);
-        }
-    }
-
-    @Override
-    public void clearFocus() {
-        mClearingFocus = true;
-        super.clearFocus();
-        mSearchSrcTextView.clearFocus();
-        mSearchSrcTextView.setImeVisibility(false);
-        mClearingFocus = false;
-    }
-
-    /**
-     * Sets a listener for user actions within the SearchView.
-     *
-     * @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.
-     */
-    public void setOnQueryTextListener(OnQueryTextListener listener) {
-        mOnQueryChangeListener = listener;
-    }
-
-    /**
-     * Sets a listener to inform when the user closes the SearchView.
-     *
-     * @param listener the listener to call when the user closes the SearchView.
-     */
-    public void setOnCloseListener(OnCloseListener listener) {
-        mOnCloseListener = listener;
-    }
-
-    /**
-     * Sets a listener to inform when the focus of the query text field changes.
-     *
-     * @param listener the listener to inform of focus changes.
-     */
-    public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) {
-        mOnQueryTextFocusChangeListener = listener;
-    }
-
-    /**
-     * Sets a listener to inform when a suggestion is focused or clicked.
-     *
-     * @param listener the listener to inform of suggestion selection events.
-     */
-    public void setOnSuggestionListener(OnSuggestionListener listener) {
-        mOnSuggestionListener = listener;
-    }
-
-    /**
-     * Sets a listener to inform when the search button is pressed. This is only
-     * relevant when the text field is not visible by default. Calling {@link #setIconified
-     * setIconified(false)} can also cause this listener to be informed.
-     *
-     * @param listener the listener to inform when the search button is clicked or
-     * the text field is programmatically de-iconified.
-     */
-    public void setOnSearchClickListener(OnClickListener listener) {
-        mOnSearchClickListener = listener;
-    }
-
-    /**
-     * Returns the query string currently in the text field.
-     *
-     * @return the query string
-     */
-    public CharSequence getQuery() {
-        return mSearchSrcTextView.getText();
-    }
-
-    /**
-     * Sets a query string in the text field and optionally submits the query as well.
-     *
-     * @param query the query string. This replaces any query text already present in the
-     * text field.
-     * @param submit whether to submit the query right now or only update the contents of
-     * text field.
-     */
-    public void setQuery(CharSequence query, boolean submit) {
-        mSearchSrcTextView.setText(query);
-        if (query != null) {
-            mSearchSrcTextView.setSelection(mSearchSrcTextView.length());
-            mUserQuery = query;
-        }
-
-        // If the query is not empty and submit is requested, submit the query
-        if (submit && !TextUtils.isEmpty(query)) {
-            onSubmitQuery();
-        }
-    }
-
-    /**
-     * Sets the hint text to display in the query text field. This overrides
-     * any hint specified in the {@link SearchableInfo}.
-     * <p>
-     * This value may be specified as an empty string to prevent any query hint
-     * from being displayed.
-     *
-     * @param hint the hint text to display or {@code null} to clear
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_queryHint
-     */
-    public void setQueryHint(@Nullable CharSequence hint) {
-        mQueryHint = hint;
-        updateQueryHint();
-    }
-
-    /**
-     * Returns the hint text that will be displayed in the query text field.
-     * <p>
-     * The displayed query hint is chosen in the following order:
-     * <ol>
-     * <li>Non-null value set with {@link #setQueryHint(CharSequence)}
-     * <li>Value specified in XML using {@code app:queryHint}
-     * <li>Valid string resource ID exposed by the {@link SearchableInfo} via
-     *     {@link SearchableInfo#getHintId()}
-     * <li>Default hint provided by the theme against which the view was
-     *     inflated
-     * </ol>
-     *
-     *
-     *
-     * @return the displayed query hint text, or {@code null} if none set
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_queryHint
-     */
-    @Nullable
-    public CharSequence getQueryHint() {
-        final CharSequence hint;
-        if (mQueryHint != null) {
-            hint = mQueryHint;
-        } else if (mSearchable != null && mSearchable.getHintId() != 0) {
-            hint = getContext().getText(mSearchable.getHintId());
-        } else {
-            hint = mDefaultQueryHint;
-        }
-        return hint;
-    }
-
-    /**
-     * Sets the default or resting state of the search field. If true, a single search icon is
-     * shown by default and expands to show the text field and other buttons when pressed. Also,
-     * if the default state is iconified, then it collapses to that state when the close button
-     * is pressed. Changes to this property will take effect immediately.
-     *
-     * <p>The default value is true.</p>
-     *
-     * @param iconified whether the search field should be iconified by default
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_iconifiedByDefault
-     */
-    public void setIconifiedByDefault(boolean iconified) {
-        if (mIconifiedByDefault == iconified) return;
-        mIconifiedByDefault = iconified;
-        updateViewsVisibility(iconified);
-        updateQueryHint();
-    }
-
-    /**
-     * Returns the default iconified state of the search field.
-     * @return
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_iconifiedByDefault
-     */
-    public boolean isIconfiedByDefault() {
-        return mIconifiedByDefault;
-    }
-
-    /**
-     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
-     * a temporary state and does not override the default iconified state set by
-     * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
-     * a false here will only be valid until the user closes the field. And if the default
-     * state is expanded, then a true here will only clear the text field and not close it.
-     *
-     * @param iconify a true value will collapse the SearchView to an icon, while a false will
-     * expand it.
-     */
-    public void setIconified(boolean iconify) {
-        if (iconify) {
-            onCloseClicked();
-        } else {
-            onSearchClicked();
-        }
-    }
-
-    /**
-     * Returns the current iconified state of the SearchView.
-     *
-     * @return true if the SearchView is currently iconified, false if the search field is
-     * fully visible.
-     */
-    public boolean isIconified() {
-        return mIconified;
-    }
-
-    /**
-     * Enables showing a submit button when the query is non-empty. In cases where the SearchView
-     * is being used to filter the contents of the current activity and doesn't launch a separate
-     * results activity, then the submit button should be disabled.
-     *
-     * @param enabled true to show a submit button for submitting queries, false if a submit
-     * button is not required.
-     */
-    public void setSubmitButtonEnabled(boolean enabled) {
-        mSubmitButtonEnabled = enabled;
-        updateViewsVisibility(isIconified());
-    }
-
-    /**
-     * Returns whether the submit button is enabled when necessary or never displayed.
-     *
-     * @return whether the submit button is enabled automatically when necessary
-     */
-    public boolean isSubmitButtonEnabled() {
-        return mSubmitButtonEnabled;
-    }
-
-    /**
-     * Specifies if a query refinement button should be displayed alongside each suggestion
-     * or if it should depend on the flags set in the individual items retrieved from the
-     * suggestions provider. Clicking on the query refinement button will replace the text
-     * in the query text field with the text from the suggestion. This flag only takes effect
-     * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)}
-     * and not when using a custom adapter.
-     *
-     * @param enable true if all items should have a query refinement button, false if only
-     * those items that have a query refinement flag set should have the button.
-     *
-     * @see SearchManager#SUGGEST_COLUMN_FLAGS
-     * @see SearchManager#FLAG_QUERY_REFINEMENT
-     */
-    public void setQueryRefinementEnabled(boolean enable) {
-        mQueryRefinement = enable;
-        if (mSuggestionsAdapter instanceof SuggestionsAdapter) {
-            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
-                    enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY);
-        }
-    }
-
-    /**
-     * Returns whether query refinement is enabled for all items or only specific ones.
-     * @return true if enabled for all items, false otherwise.
-     */
-    public boolean isQueryRefinementEnabled() {
-        return mQueryRefinement;
-    }
-
-    /**
-     * You can set a custom adapter if you wish. Otherwise the default adapter is used to
-     * display the suggestions from the suggestions provider associated with the SearchableInfo.
-     *
-     * @see #setSearchableInfo(SearchableInfo)
-     */
-    public void setSuggestionsAdapter(CursorAdapter adapter) {
-        mSuggestionsAdapter = adapter;
-
-        mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
-    }
-
-    /**
-     * Returns the adapter used for suggestions, if any.
-     * @return the suggestions adapter
-     */
-    public CursorAdapter getSuggestionsAdapter() {
-        return mSuggestionsAdapter;
-    }
-
-    /**
-     * Makes the view at most this many pixels wide
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_android_maxWidth
-     */
-    public void setMaxWidth(int maxpixels) {
-        mMaxWidth = maxpixels;
-
-        requestLayout();
-    }
-
-    /**
-     * Gets the specified maximum width in pixels, if set. Returns zero if
-     * no maximum width was specified.
-     * @return the maximum width of the view
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SearchView_android_maxWidth
-     */
-    public int getMaxWidth() {
-        return mMaxWidth;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Let the standard measurements take effect in iconified state.
-        if (isIconified()) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            return;
-        }
-
-        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-
-        switch (widthMode) {
-            case MeasureSpec.AT_MOST:
-                // If there is an upper limit, don't exceed maximum width (explicit or implicit)
-                if (mMaxWidth > 0) {
-                    width = Math.min(mMaxWidth, width);
-                } else {
-                    width = Math.min(getPreferredWidth(), width);
-                }
-                break;
-            case MeasureSpec.EXACTLY:
-                // If an exact width is specified, still don't exceed any specified maximum width
-                if (mMaxWidth > 0) {
-                    width = Math.min(mMaxWidth, width);
-                }
-                break;
-            case MeasureSpec.UNSPECIFIED:
-                // Use maximum width, if specified, else preferred width
-                width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth();
-                break;
-        }
-        widthMode = MeasureSpec.EXACTLY;
-
-        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
-
-        switch (heightMode) {
-            case MeasureSpec.AT_MOST:
-                height = Math.min(getPreferredHeight(), height);
-                break;
-            case MeasureSpec.UNSPECIFIED:
-                height = getPreferredHeight();
-                break;
-        }
-        heightMode = MeasureSpec.EXACTLY;
-
-        super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode),
-                MeasureSpec.makeMeasureSpec(height, heightMode));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        if (changed) {
-            // Expand mSearchSrcTextView touch target to be the height of the parent in order to
-            // allow it to be up to 48dp.
-            getChildBoundsWithinSearchView(mSearchSrcTextView, mSearchSrcTextViewBounds);
-            mSearchSrtTextViewBoundsExpanded.set(
-                    mSearchSrcTextViewBounds.left, 0, mSearchSrcTextViewBounds.right, bottom - top);
-            if (mTouchDelegate == null) {
-                mTouchDelegate = new UpdatableTouchDelegate(mSearchSrtTextViewBoundsExpanded,
-                        mSearchSrcTextViewBounds, mSearchSrcTextView);
-                setTouchDelegate(mTouchDelegate);
-            } else {
-                mTouchDelegate.setBounds(mSearchSrtTextViewBoundsExpanded, mSearchSrcTextViewBounds);
-            }
-        }
-    }
-
-    private void getChildBoundsWithinSearchView(View view, Rect rect) {
-        view.getLocationInWindow(mTemp);
-        getLocationInWindow(mTemp2);
-        final int top = mTemp[1] - mTemp2[1];
-        final int left = mTemp[0] - mTemp2[0];
-        rect.set(left, top, left + view.getWidth(), top + view.getHeight());
-    }
-
-    private int getPreferredWidth() {
-        return getContext().getResources()
-                .getDimensionPixelSize(R.dimen.abc_search_view_preferred_width);
-    }
-
-    private int getPreferredHeight() {
-        return getContext().getResources()
-                .getDimensionPixelSize(R.dimen.abc_search_view_preferred_height);
-    }
-
-    private void updateViewsVisibility(final boolean collapsed) {
-        mIconified = collapsed;
-        // Visibility of views that are visible when collapsed
-        final int visCollapsed = collapsed ? VISIBLE : GONE;
-        // Is there text in the query
-        final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
-
-        mSearchButton.setVisibility(visCollapsed);
-        updateSubmitButton(hasText);
-        mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
-
-        final int iconVisibility;
-        if (mCollapsedIcon.getDrawable() == null || mIconifiedByDefault) {
-            iconVisibility = GONE;
-        } else {
-            iconVisibility = VISIBLE;
-        }
-        mCollapsedIcon.setVisibility(iconVisibility);
-
-        updateCloseButton();
-        updateVoiceButton(!hasText);
-        updateSubmitArea();
-    }
-
-    private boolean hasVoiceSearch() {
-        if (mSearchable != null && mSearchable.getVoiceSearchEnabled()) {
-            Intent testIntent = null;
-            if (mSearchable.getVoiceSearchLaunchWebSearch()) {
-                testIntent = mVoiceWebSearchIntent;
-            } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
-                testIntent = mVoiceAppSearchIntent;
-            }
-            if (testIntent != null) {
-                ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent,
-                        PackageManager.MATCH_DEFAULT_ONLY);
-                return ri != null;
-            }
-        }
-        return false;
-    }
-
-    private boolean isSubmitAreaEnabled() {
-        return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified();
-    }
-
-    private void updateSubmitButton(boolean hasText) {
-        int visibility = GONE;
-        if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus()
-                && (hasText || !mVoiceButtonEnabled)) {
-            visibility = VISIBLE;
-        }
-        mGoButton.setVisibility(visibility);
-    }
-
-    private void updateSubmitArea() {
-        int visibility = GONE;
-        if (isSubmitAreaEnabled()
-                && (mGoButton.getVisibility() == VISIBLE
-                        || mVoiceButton.getVisibility() == VISIBLE)) {
-            visibility = VISIBLE;
-        }
-        mSubmitArea.setVisibility(visibility);
-    }
-
-    private void updateCloseButton() {
-        final boolean hasText = !TextUtils.isEmpty(mSearchSrcTextView.getText());
-        // Should we show the close button? It is not shown if there's no focus,
-        // field is not iconified by default and there is no text in it.
-        final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
-        mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
-        final Drawable closeButtonImg = mCloseButton.getDrawable();
-        if (closeButtonImg != null){
-            closeButtonImg.setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
-        }
-    }
-
-    private void postUpdateFocusedState() {
-        post(mUpdateDrawableStateRunnable);
-    }
-
-    void updateFocusedState() {
-        final boolean focused = mSearchSrcTextView.hasFocus();
-        final int[] stateSet = focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET;
-        final Drawable searchPlateBg = mSearchPlate.getBackground();
-        if (searchPlateBg != null) {
-            searchPlateBg.setState(stateSet);
-        }
-        final Drawable submitAreaBg = mSubmitArea.getBackground();
-        if (submitAreaBg != null) {
-            submitAreaBg.setState(stateSet);
-        }
-        invalidate();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        removeCallbacks(mUpdateDrawableStateRunnable);
-        post(mReleaseCursorRunnable);
-        super.onDetachedFromWindow();
-    }
-
-    /**
-     * Called by the SuggestionsAdapter
-     */
-    void onQueryRefine(CharSequence queryText) {
-        setQuery(queryText);
-    }
-
-    private final OnClickListener mOnClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            if (v == mSearchButton) {
-                onSearchClicked();
-            } else if (v == mCloseButton) {
-                onCloseClicked();
-            } else if (v == mGoButton) {
-                onSubmitQuery();
-            } else if (v == mVoiceButton) {
-                onVoiceClicked();
-            } else if (v == mSearchSrcTextView) {
-                forceSuggestionQuery();
-            }
-        }
-    };
-
-    /**
-     * React to the user typing "enter" or other hardwired keys while typing in
-     * the search box. This handles these special keys while the edit box has
-     * focus.
-     */
-    View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent event) {
-            // guard against possible race conditions
-            if (mSearchable == null) {
-                return false;
-            }
-
-            if (DBG) {
-                Log.d(LOG_TAG, "mTextListener.onKey(" + keyCode + "," + event + "), selection: "
-                        + mSearchSrcTextView.getListSelection());
-            }
-
-            // If a suggestion is selected, handle enter, search key, and action keys
-            // as presses on the selected suggestion
-            if (mSearchSrcTextView.isPopupShowing()
-                    && mSearchSrcTextView.getListSelection() != ListView.INVALID_POSITION) {
-                return onSuggestionsKey(v, keyCode, event);
-            }
-
-            // 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() && event.hasNoModifiers()) {
-                if (event.getAction() == KeyEvent.ACTION_UP) {
-                    if (keyCode == KeyEvent.KEYCODE_ENTER) {
-                        v.cancelLongPress();
-
-                        // Launch as a regular search.
-                        launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, mSearchSrcTextView.getText()
-                                .toString());
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-    };
-
-    /**
-     * React to the user typing while in the suggestions list. First, check for
-     * action keys. If not handled, try refocusing regular characters into the
-     * EditText.
-     */
-    boolean onSuggestionsKey(View v, int keyCode, KeyEvent event) {
-        // guard against possible race conditions (late arrival after dismiss)
-        if (mSearchable == null) {
-            return false;
-        }
-        if (mSuggestionsAdapter == null) {
-            return false;
-        }
-        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
-                    || keyCode == KeyEvent.KEYCODE_TAB) {
-                int position = mSearchSrcTextView.getListSelection();
-                return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
-            }
-
-            // Next, check for left/right moves, which we use to "return" the
-            // user to the edit view
-            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
-                // give "focus" to text editor, with cursor at the beginning if
-                // left key, at end if right key
-                // TODO: Reverse left/right for right-to-left languages, e.g.
-                // Arabic
-                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mSearchSrcTextView
-                        .length();
-                mSearchSrcTextView.setSelection(selPoint);
-                mSearchSrcTextView.setListSelection(0);
-                mSearchSrcTextView.clearListSelection();
-                HIDDEN_METHOD_INVOKER.ensureImeVisible(mSearchSrcTextView, true);
-
-                return true;
-            }
-
-            // Next, check for an "up and out" move
-            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mSearchSrcTextView.getListSelection()) {
-                // TODO: restoreUserQuery();
-                // let ACTV complete the move
-                return false;
-            }
-        }
-        return false;
-    }
-
-    private CharSequence getDecoratedHint(CharSequence hintText) {
-        // If the field is always expanded or we don't have a search hint icon,
-        // then don't add the search icon to the hint.
-        if (!mIconifiedByDefault || mSearchHintIcon == null) {
-            return hintText;
-        }
-
-        final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
-        mSearchHintIcon.setBounds(0, 0, textSize, textSize);
-
-        final SpannableStringBuilder ssb = new SpannableStringBuilder("   ");
-        ssb.setSpan(new ImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        ssb.append(hintText);
-        return ssb;
-    }
-
-    private void updateQueryHint() {
-        final CharSequence hint = getQueryHint();
-        mSearchSrcTextView.setHint(getDecoratedHint(hint == null ? "" : hint));
-    }
-
-    /**
-     * Updates the auto-complete text view.
-     */
-    private void updateSearchAutoComplete() {
-        mSearchSrcTextView.setThreshold(mSearchable.getSuggestThreshold());
-        mSearchSrcTextView.setImeOptions(mSearchable.getImeOptions());
-        int inputType = mSearchable.getInputType();
-        // We only touch this if the input type is set up for text (which it almost certainly
-        // should be, in the case of search!)
-        if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
-            // The existence of a suggestions authority is the proxy for "suggestions
-            // are available here"
-            inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
-            if (mSearchable.getSuggestAuthority() != null) {
-                inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
-                // TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is performing
-                // auto-completion based on its own semantics, which it will present to the user
-                // as they type. This generally means that the input method should not show its
-                // own candidates, and the spell checker should not be in action. The text editor
-                // supplies its candidates by calling InputMethodManager.displayCompletions(),
-                // which in turn will call InputMethodSession.displayCompletions().
-                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
-            }
-        }
-        mSearchSrcTextView.setInputType(inputType);
-        if (mSuggestionsAdapter != null) {
-            mSuggestionsAdapter.changeCursor(null);
-        }
-        // attach the suggestions adapter, if suggestions are available
-        // The existence of a suggestions authority is the proxy for "suggestions available here"
-        if (mSearchable.getSuggestAuthority() != null) {
-            mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
-                    this, mSearchable, mOutsideDrawablesCache);
-            mSearchSrcTextView.setAdapter(mSuggestionsAdapter);
-            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
-                    mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
-                    : SuggestionsAdapter.REFINE_BY_ENTRY);
-        }
-    }
-
-    /**
-     * Update the visibility of the voice button.  There are actually two voice search modes,
-     * either of which will activate the button.
-     * @param empty whether the search query text field is empty. If it is, then the other
-     * criteria apply to make the voice button visible.
-     */
-    private void updateVoiceButton(boolean empty) {
-        int visibility = GONE;
-        if (mVoiceButtonEnabled && !isIconified() && empty) {
-            visibility = VISIBLE;
-            mGoButton.setVisibility(GONE);
-        }
-        mVoiceButton.setVisibility(visibility);
-    }
-
-    private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() {
-
-        /**
-         * Called when the input method default action key is pressed.
-         */
-        @Override
-        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-            onSubmitQuery();
-            return true;
-        }
-    };
-
-    void onTextChanged(CharSequence newText) {
-        CharSequence text = mSearchSrcTextView.getText();
-        mUserQuery = text;
-        boolean hasText = !TextUtils.isEmpty(text);
-        updateSubmitButton(hasText);
-        updateVoiceButton(!hasText);
-        updateCloseButton();
-        updateSubmitArea();
-        if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) {
-            mOnQueryChangeListener.onQueryTextChange(newText.toString());
-        }
-        mOldQueryText = newText.toString();
-    }
-
-    void onSubmitQuery() {
-        CharSequence query = mSearchSrcTextView.getText();
-        if (query != null && TextUtils.getTrimmedLength(query) > 0) {
-            if (mOnQueryChangeListener == null
-                    || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
-                if (mSearchable != null) {
-                    launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
-                }
-                mSearchSrcTextView.setImeVisibility(false);
-                dismissSuggestions();
-            }
-        }
-    }
-
-    private void dismissSuggestions() {
-        mSearchSrcTextView.dismissDropDown();
-    }
-
-    void onCloseClicked() {
-        CharSequence text = mSearchSrcTextView.getText();
-        if (TextUtils.isEmpty(text)) {
-            if (mIconifiedByDefault) {
-                // If the app doesn't override the close behavior
-                if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
-                    // hide the keyboard and remove focus
-                    clearFocus();
-                    // collapse the search field
-                    updateViewsVisibility(true);
-                }
-            }
-        } else {
-            mSearchSrcTextView.setText("");
-            mSearchSrcTextView.requestFocus();
-            mSearchSrcTextView.setImeVisibility(true);
-        }
-
-    }
-
-    void onSearchClicked() {
-        updateViewsVisibility(false);
-        mSearchSrcTextView.requestFocus();
-        mSearchSrcTextView.setImeVisibility(true);
-        if (mOnSearchClickListener != null) {
-            mOnSearchClickListener.onClick(this);
-        }
-    }
-
-    void onVoiceClicked() {
-        // guard against possible race conditions
-        if (mSearchable == null) {
-            return;
-        }
-        SearchableInfo searchable = mSearchable;
-        try {
-            if (searchable.getVoiceSearchLaunchWebSearch()) {
-                Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent,
-                        searchable);
-                getContext().startActivity(webSearchIntent);
-            } else if (searchable.getVoiceSearchLaunchRecognizer()) {
-                Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
-                        searchable);
-                getContext().startActivity(appSearchIntent);
-            }
-        } catch (ActivityNotFoundException e) {
-            // Should not happen, since we check the availability of
-            // voice search before showing the button. But just in case...
-            Log.w(LOG_TAG, "Could not find voice search activity");
-        }
-    }
-
-    void onTextFocusChanged() {
-        updateViewsVisibility(isIconified());
-        // Delayed update to make sure that the focus has settled down and window focus changes
-        // don't affect it. A synchronous update was not working.
-        postUpdateFocusedState();
-        if (mSearchSrcTextView.hasFocus()) {
-            forceSuggestionQuery();
-        }
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-
-        postUpdateFocusedState();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActionViewCollapsed() {
-        setQuery("", false);
-        clearFocus();
-        updateViewsVisibility(true);
-        mSearchSrcTextView.setImeOptions(mCollapsedImeOptions);
-        mExpandedInActionView = false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActionViewExpanded() {
-        if (mExpandedInActionView) return;
-
-        mExpandedInActionView = true;
-        mCollapsedImeOptions = mSearchSrcTextView.getImeOptions();
-        mSearchSrcTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
-        mSearchSrcTextView.setText("");
-        setIconified(false);
-    }
-
-    static class SavedState extends AbsSavedState {
-        boolean isIconified;
-
-        SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        public SavedState(Parcel source, ClassLoader loader) {
-            super(source, loader);
-            isIconified = (Boolean) source.readValue(null);
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeValue(isIconified);
-        }
-
-        @Override
-        public String toString() {
-            return "SearchView.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " isIconified=" + isIconified + "}";
-        }
-
-        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];
-            }
-        };
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        SavedState ss = new SavedState(superState);
-        ss.isIconified = isIconified();
-        return ss;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-        SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-        updateViewsVisibility(ss.isIconified);
-        requestLayout();
-    }
-
-    void adjustDropDownSizeAndPosition() {
-        if (mDropDownAnchor.getWidth() > 1) {
-            Resources res = getContext().getResources();
-            int anchorPadding = mSearchPlate.getPaddingLeft();
-            Rect dropDownPadding = new Rect();
-            final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
-            int iconOffset = mIconifiedByDefault
-                    ? res.getDimensionPixelSize(R.dimen.abc_dropdownitem_icon_width)
-                    + res.getDimensionPixelSize(R.dimen.abc_dropdownitem_text_padding_left)
-                    : 0;
-            mSearchSrcTextView.getDropDownBackground().getPadding(dropDownPadding);
-            int offset;
-            if (isLayoutRtl) {
-                offset = - dropDownPadding.left;
-            } else {
-                offset = anchorPadding - (dropDownPadding.left + iconOffset);
-            }
-            mSearchSrcTextView.setDropDownHorizontalOffset(offset);
-            final int width = mDropDownAnchor.getWidth() + dropDownPadding.left
-                    + dropDownPadding.right + iconOffset - anchorPadding;
-            mSearchSrcTextView.setDropDownWidth(width);
-        }
-    }
-
-    boolean onItemClicked(int position, int actionKey, String actionMsg) {
-        if (mOnSuggestionListener == null
-                || !mOnSuggestionListener.onSuggestionClick(position)) {
-            launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
-            mSearchSrcTextView.setImeVisibility(false);
-            dismissSuggestions();
-            return true;
-        }
-        return false;
-    }
-
-    boolean onItemSelected(int position) {
-        if (mOnSuggestionListener == null
-                || !mOnSuggestionListener.onSuggestionSelect(position)) {
-            rewriteQueryFromSuggestion(position);
-            return true;
-        }
-        return false;
-    }
-
-    private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
-
-        /**
-         * Implements OnItemClickListener
-         */
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            if (DBG) Log.d(LOG_TAG, "onItemClick() position " + position);
-            onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
-        }
-    };
-
-    private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() {
-
-        /**
-         * Implements OnItemSelectedListener
-         */
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (DBG) Log.d(LOG_TAG, "onItemSelected() position " + position);
-            SearchView.this.onItemSelected(position);
-        }
-
-        /**
-         * Implements OnItemSelectedListener
-         */
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            if (DBG)
-                Log.d(LOG_TAG, "onNothingSelected()");
-        }
-    };
-
-    /**
-     * Query rewriting.
-     */
-    private void rewriteQueryFromSuggestion(int position) {
-        CharSequence oldQuery = mSearchSrcTextView.getText();
-        Cursor c = mSuggestionsAdapter.getCursor();
-        if (c == null) {
-            return;
-        }
-        if (c.moveToPosition(position)) {
-            // Get the new query from the suggestion.
-            CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
-            if (newQuery != null) {
-                // The suggestion rewrites the query.
-                // Update the text field, without getting new suggestions.
-                setQuery(newQuery);
-            } else {
-                // The suggestion does not rewrite the query, restore the user's query.
-                setQuery(oldQuery);
-            }
-        } else {
-            // We got a bad position, restore the user's query.
-            setQuery(oldQuery);
-        }
-    }
-
-    /**
-     * Launches an intent based on a suggestion.
-     *
-     * @param position The index of the suggestion to create the intent from.
-     * @param actionKey The key code of the action key that was pressed,
-     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
-     * @param actionMsg The message for the action key that was pressed,
-     *        or <code>null</code> if none.
-     * @return true if a successful launch, false if could not (e.g. bad position).
-     */
-    private boolean launchSuggestion(int position, int actionKey, String actionMsg) {
-        Cursor c = mSuggestionsAdapter.getCursor();
-        if ((c != null) && c.moveToPosition(position)) {
-
-            Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
-
-            // launch the intent
-            launchIntent(intent);
-
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Launches an intent, including any special intent handling.
-     */
-    private void launchIntent(Intent intent) {
-        if (intent == null) {
-            return;
-        }
-        try {
-            // If the intent was created from a suggestion, it will always have an explicit
-            // component here.
-            getContext().startActivity(intent);
-        } catch (RuntimeException ex) {
-            Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
-        }
-    }
-
-    /**
-     * Sets the text in the query box, without updating the suggestions.
-     */
-    private void setQuery(CharSequence query) {
-        mSearchSrcTextView.setText(query);
-        // Move the cursor to the end
-        mSearchSrcTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
-    }
-
-    void launchQuerySearch(int actionKey, String actionMsg, String query) {
-        String action = Intent.ACTION_SEARCH;
-        Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
-        getContext().startActivity(intent);
-    }
-
-    /**
-     * Constructs an intent from the given information and the search dialog state.
-     *
-     * @param action Intent action.
-     * @param data Intent data, or <code>null</code>.
-     * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
-     * @param query Intent query, or <code>null</code>.
-     * @param actionKey The key code of the action key that was pressed,
-     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
-     * @param actionMsg The message for the action key that was pressed,
-     *        or <code>null</code> if none.
-     * @return The intent.
-     */
-    private Intent createIntent(String action, Uri data, String extraData, String query,
-            int actionKey, String actionMsg) {
-        // Now build the Intent
-        Intent intent = new Intent(action);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        // We need CLEAR_TOP to avoid reusing an old task that has other activities
-        // on top of the one we want. We don't want to do this in in-app search though,
-        // as it can be destructive to the activity stack.
-        if (data != null) {
-            intent.setData(data);
-        }
-        intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
-        if (query != null) {
-            intent.putExtra(SearchManager.QUERY, query);
-        }
-        if (extraData != null) {
-            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
-        }
-        if (mAppSearchData != null) {
-            intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
-        }
-        if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
-            intent.putExtra(SearchManager.ACTION_KEY, actionKey);
-            intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
-        }
-        intent.setComponent(mSearchable.getSearchActivity());
-        return intent;
-    }
-
-    /**
-     * Create and return an Intent that can launch the voice search activity for web search.
-     */
-    private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) {
-        Intent voiceIntent = new Intent(baseIntent);
-        ComponentName searchActivity = searchable.getSearchActivity();
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
-                : searchActivity.flattenToShortString());
-        return voiceIntent;
-    }
-
-    /**
-     * Create and return an Intent that can launch the voice search activity, perform a specific
-     * voice transcription, and forward the results to the searchable activity.
-     *
-     * @param baseIntent The voice app search intent to start from
-     * @return A completely-configured intent ready to send to the voice search activity
-     */
-    private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) {
-        ComponentName searchActivity = searchable.getSearchActivity();
-
-        // create the necessary intent to set up a search-and-forward operation
-        // in the voice search system.   We have to keep the bundle separate,
-        // because it becomes immutable once it enters the PendingIntent
-        Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
-        queryIntent.setComponent(searchActivity);
-        PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
-                PendingIntent.FLAG_ONE_SHOT);
-
-        // Now set up the bundle that will be inserted into the pending intent
-        // when it's time to do the search.  We always build it here (even if empty)
-        // because the voice search activity will always need to insert "QUERY" into
-        // it anyway.
-        Bundle queryExtras = new Bundle();
-        if (mAppSearchData != null) {
-            queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
-        }
-
-        // Now build the intent to launch the voice search.  Add all necessary
-        // extras to launch the voice recognizer, and then all the necessary extras
-        // to forward the results to the searchable activity
-        Intent voiceIntent = new Intent(baseIntent);
-
-        // Add all of the configuration options supplied by the searchable's metadata
-        String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
-        String prompt = null;
-        String language = null;
-        int maxResults = 1;
-
-        Resources resources = getResources();
-        if (searchable.getVoiceLanguageModeId() != 0) {
-            languageModel = resources.getString(searchable.getVoiceLanguageModeId());
-        }
-        if (searchable.getVoicePromptTextId() != 0) {
-            prompt = resources.getString(searchable.getVoicePromptTextId());
-        }
-        if (searchable.getVoiceLanguageId() != 0) {
-            language = resources.getString(searchable.getVoiceLanguageId());
-        }
-        if (searchable.getVoiceMaxResults() != 0) {
-            maxResults = searchable.getVoiceMaxResults();
-        }
-
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
-                : searchActivity.flattenToShortString());
-
-        // Add the values that configure forwarding the results
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
-        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
-
-        return voiceIntent;
-    }
-
-    /**
-     * When a particular suggestion has been selected, perform the various lookups required
-     * to use the suggestion.  This includes checking the cursor for suggestion-specific data,
-     * and/or falling back to the XML for defaults;  It also creates REST style Uri data when
-     * the suggestion includes a data id.
-     *
-     * @param c The suggestions cursor, moved to the row of the user's selection
-     * @param actionKey The key code of the action key that was pressed,
-     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
-     * @param actionMsg The message for the action key that was pressed,
-     *        or <code>null</code> if none.
-     * @return An intent for the suggestion at the cursor's position.
-     */
-    private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
-        try {
-            // use specific action if supplied, or default action if supplied, or fixed default
-            String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
-
-            if (action == null) {
-                action = mSearchable.getSuggestIntentAction();
-            }
-            if (action == null) {
-                action = Intent.ACTION_SEARCH;
-            }
-
-            // use specific data if supplied, or default data if supplied
-            String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
-            if (data == null) {
-                data = mSearchable.getSuggestIntentData();
-            }
-            // then, if an ID was provided, append it.
-            if (data != null) {
-                String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
-                if (id != null) {
-                    data = data + "/" + Uri.encode(id);
-                }
-            }
-            Uri dataUri = (data == null) ? null : Uri.parse(data);
-
-            String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
-            String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
-
-            return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
-        } catch (RuntimeException e ) {
-            int rowNum;
-            try {                       // be really paranoid now
-                rowNum = c.getPosition();
-            } catch (RuntimeException e2 ) {
-                rowNum = -1;
-            }
-            Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum +
-                            " returned exception.", e);
-            return null;
-        }
-    }
-
-    void forceSuggestionQuery() {
-        HIDDEN_METHOD_INVOKER.doBeforeTextChanged(mSearchSrcTextView);
-        HIDDEN_METHOD_INVOKER.doAfterTextChanged(mSearchSrcTextView);
-    }
-
-    static boolean isLandscapeMode(Context context) {
-        return context.getResources().getConfiguration().orientation
-                == Configuration.ORIENTATION_LANDSCAPE;
-    }
-
-    /**
-     * Callback to watch the text field for empty/non-empty
-     */
-    private TextWatcher mTextWatcher = new TextWatcher() {
-        @Override
-        public void beforeTextChanged(CharSequence s, int start, int before, int after) { }
-
-        @Override
-        public void onTextChanged(CharSequence s, int start,
-                int before, int after) {
-            SearchView.this.onTextChanged(s);
-        }
-
-        @Override
-        public void afterTextChanged(Editable s) {
-        }
-    };
-
-    private static class UpdatableTouchDelegate extends TouchDelegate {
-        /**
-         * View that should receive forwarded touch events
-         */
-        private final View mDelegateView;
-
-        /**
-         * Bounds in local coordinates of the containing view that should be mapped to the delegate
-         * view. This rect is used for initial hit testing.
-         */
-        private final Rect mTargetBounds;
-
-        /**
-         * Bounds in local coordinates of the containing view that are actual bounds of the delegate
-         * view. This rect is used for event coordinate mapping.
-         */
-        private final Rect mActualBounds;
-
-        /**
-         * mTargetBounds inflated to include some slop. This rect is to track whether the motion events
-         * should be considered to be be within the delegate view.
-         */
-        private final Rect mSlopBounds;
-
-        private final int mSlop;
-
-        /**
-         * True if the delegate had been targeted on a down event (intersected mTargetBounds).
-         */
-        private boolean mDelegateTargeted;
-
-        public UpdatableTouchDelegate(Rect targetBounds, Rect actualBounds, View delegateView) {
-            super(targetBounds, delegateView);
-            mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
-            mTargetBounds = new Rect();
-            mSlopBounds = new Rect();
-            mActualBounds = new Rect();
-            setBounds(targetBounds, actualBounds);
-            mDelegateView = delegateView;
-        }
-
-        public void setBounds(Rect desiredBounds, Rect actualBounds) {
-            mTargetBounds.set(desiredBounds);
-            mSlopBounds.set(desiredBounds);
-            mSlopBounds.inset(-mSlop, -mSlop);
-            mActualBounds.set(actualBounds);
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            final int x = (int) event.getX();
-            final int y = (int) event.getY();
-            boolean sendToDelegate = false;
-            boolean hit = true;
-            boolean handled = false;
-
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    if (mTargetBounds.contains(x, y)) {
-                        mDelegateTargeted = true;
-                        sendToDelegate = true;
-                    }
-                    break;
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_MOVE:
-                    sendToDelegate = mDelegateTargeted;
-                    if (sendToDelegate) {
-                        if (!mSlopBounds.contains(x, y)) {
-                            hit = false;
-                        }
-                    }
-                    break;
-                case MotionEvent.ACTION_CANCEL:
-                    sendToDelegate = mDelegateTargeted;
-                    mDelegateTargeted = false;
-                    break;
-            }
-            if (sendToDelegate) {
-                if (hit && !mActualBounds.contains(x, y)) {
-                    // Offset event coordinates to be in the center of the target view since we
-                    // are within the targetBounds, but not inside the actual bounds of
-                    // mDelegateView
-                    event.setLocation(mDelegateView.getWidth() / 2,
-                            mDelegateView.getHeight() / 2);
-                } else {
-                    // Offset event coordinates to the target view coordinates.
-                    event.setLocation(x - mActualBounds.left, y - mActualBounds.top);
-                }
-
-                handled = mDelegateView.dispatchTouchEvent(event);
-            }
-            return handled;
-        }
-    }
-
-    /**
-     * Local subclass for AutoCompleteTextView.
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class SearchAutoComplete extends AppCompatAutoCompleteTextView {
-
-        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);
-        }
-
-        public SearchAutoComplete(Context context, AttributeSet attrs) {
-            this(context, attrs, R.attr.autoCompleteTextViewStyle);
-        }
-
-        public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
-            super(context, attrs, defStyle);
-            mThreshold = getThreshold();
-        }
-
-        @Override
-        protected void onFinishInflate() {
-            super.onFinishInflate();
-            DisplayMetrics metrics = getResources().getDisplayMetrics();
-            setMinWidth((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    getSearchViewTextMinWidthDp(), metrics));
-        }
-
-        void setSearchView(SearchView searchView) {
-            mSearchView = searchView;
-        }
-
-        @Override
-        public void setThreshold(int threshold) {
-            super.setThreshold(threshold);
-            mThreshold = threshold;
-        }
-
-        /**
-         * Returns true if the text field is empty, or contains only whitespace.
-         */
-        private boolean isEmpty() {
-            return TextUtils.getTrimmedLength(getText()) == 0;
-        }
-
-        /**
-         * We override this method to avoid replacing the query box text when a
-         * suggestion is clicked.
-         */
-        @Override
-        protected void replaceText(CharSequence text) {
-        }
-
-        /**
-         * We override this method to avoid an extra onItemClick being called on
-         * the drop-down's OnItemClickListener by
-         * {@link AutoCompleteTextView#onKeyUp(int, KeyEvent)} when an item is
-         * clicked with the trackball.
-         */
-        @Override
-        public void performCompletion() {
-        }
-
-        /**
-         * We override this method to be sure and show the soft keyboard if
-         * appropriate when the TextView has focus.
-         */
-        @Override
-        public void onWindowFocusChanged(boolean hasWindowFocus) {
-            super.onWindowFocusChanged(hasWindowFocus);
-
-            if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) {
-                // 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);
-                }
-            }
-        }
-
-        @Override
-        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-            super.onFocusChanged(focused, direction, previouslyFocusedRect);
-            mSearchView.onTextFocusChanged();
-        }
-
-        /**
-         * We override this method so that we can allow a threshold of zero,
-         * which ACTV does not.
-         */
-        @Override
-        public boolean enoughToFilter() {
-            return mThreshold <= 0 || super.enoughToFilter();
-        }
-
-        @Override
-        public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                // special case for the back key, we do not even try to send it
-                // to the drop down list but instead, consume it immediately
-                if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
-                    if (state != null) {
-                        state.startTracking(event, this);
-                    }
-                    return true;
-                } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
-                    if (state != null) {
-                        state.handleUpEvent(event);
-                    }
-                    if (event.isTracking() && !event.isCanceled()) {
-                        mSearchView.clearFocus();
-                        setImeVisibility(false);
-                        return true;
-                    }
-                }
-            }
-            return super.onKeyPreIme(keyCode, event);
-        }
-
-        /**
-         * Get minimum width of the search view text entry area.
-         */
-        private int getSearchViewTextMinWidthDp() {
-            final Configuration config = getResources().getConfiguration();
-            final int widthDp = config.screenWidthDp;
-            final int heightDp = config.screenHeightDp;
-
-            if (widthDp >= 960 && heightDp >= 720
-                    && config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-                return 256;
-            } else if (widthDp >= 600 || (widthDp >= 640 && heightDp >= 480)) {
-                return 192;
-            }
-            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 {
-        private Method doBeforeTextChanged, doAfterTextChanged;
-        private Method ensureImeVisible;
-        private Method showSoftInputUnchecked;
-
-        AutoCompleteTextViewReflector() {
-            try {
-                doBeforeTextChanged = AutoCompleteTextView.class
-                        .getDeclaredMethod("doBeforeTextChanged");
-                doBeforeTextChanged.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                // Ah well.
-            }
-            try {
-                doAfterTextChanged = AutoCompleteTextView.class
-                        .getDeclaredMethod("doAfterTextChanged");
-                doAfterTextChanged.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                // Ah well.
-            }
-            try {
-                ensureImeVisible = AutoCompleteTextView.class
-                        .getMethod("ensureImeVisible", boolean.class);
-                ensureImeVisible.setAccessible(true);
-            } catch (NoSuchMethodException e) {
-                // Ah well.
-            }
-        }
-
-        void doBeforeTextChanged(AutoCompleteTextView view) {
-            if (doBeforeTextChanged != null) {
-                try {
-                    doBeforeTextChanged.invoke(view);
-                } catch (Exception e) {
-                }
-            }
-        }
-
-        void doAfterTextChanged(AutoCompleteTextView view) {
-            if (doAfterTextChanged != null) {
-                try {
-                    doAfterTextChanged.invoke(view);
-                } catch (Exception e) {
-                }
-            }
-        }
-
-        void ensureImeVisible(AutoCompleteTextView view, boolean visible) {
-            if (ensureImeVisible != null) {
-                try {
-                    ensureImeVisible.invoke(view, visible);
-                } catch (Exception e) {
-                }
-            }
-        }
-    }
-}
diff --git a/android/support/v7/widget/ShareActionProvider.java b/android/support/v7/widget/ShareActionProvider.java
deleted file mode 100644
index 046252b..0000000
--- a/android/support/v7/widget/ShareActionProvider.java
+++ /dev/null
@@ -1,419 +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.v7.widget;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.v4.view.ActionProvider;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.widget.ActivityChooserModel.OnChooseActivityListener;
-import android.util.TypedValue;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.SubMenu;
-import android.view.View;
-
-/**
- * Provides a share action, which is suitable for an activity's app bar. Creates
- * views that enable data sharing. If the provider appears in the
- * overflow menu, it creates a submenu with the appropriate sharing
- * actions.
- *
- * <h3 id="add-share-action">Adding a share action</h3>
- *
- * <p>To add a "share" action to your activity, put a
- * <code>ShareActionProvider</code> in the app bar's menu resource. For
- * example:</p>
- *
- * <pre>
- * &lt;item android:id="&#64;+id/action_share"
- *      android:title="&#64;string/share"
- *      app:showAsAction="ifRoom"
- *      app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/&gt;
- * </pre>
- *
- * <p>You do not need to specify an icon, since the
- * <code>ShareActionProvider</code> widget takes care of its own appearance and
- * behavior. However, you do need to specify a title with
- * <code>android:title</code>, in case the action ends up in the overflow
- * menu.</p>
- *
- * <p>Next, set up the intent that contains the content your activity is
- * able to share. You should create this intent in your handler for
- * {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()},
- * and update it every time the shareable content changes. To set up the
- * intent:</p>
- *
- * <ol>
- * <li>Get a reference to the ShareActionProvider by calling {@link
- * android.view.MenuItem#getActionProvider getActionProvider()} and
- * passing the share action's {@link android.view.MenuItem}. For
- * example:
- *
- * <pre>
- * MenuItem shareItem = menu.findItem(R.id.action_share);
- * ShareActionProvider myShareActionProvider =
- *     (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);</pre></li>
- *
- * <li>Create an intent with the {@link android.content.Intent#ACTION_SEND}
- * action, and attach the content shared by the activity. For example, the
- * following intent shares an image:
- *
- * <pre>
- * Intent myShareIntent = new Intent(Intent.ACTION_SEND);
- * myShareIntent.setType("image/*");
- * myShareIntent.putExtra(Intent.EXTRA_STREAM, myImageUri);</pre></li>
- *
- * <li>Call {@link #setShareIntent setShareIntent()} to attach this intent to
- * the action provider:
- *
- * <pre>
- * myShareActionProvider.setShareIntent(myShareIntent);
- * </pre></li>
- *
- * <li>When the content changes, modify the intent or create a new one,
- * and call {@link #setShareIntent setShareIntent()} again. For example:
- *
- * <pre>
- * // Image has changed! Update the intent:
- * myShareIntent.putExtra(Intent.EXTRA_STREAM, myNewImageUri);
- * myShareActionProvider.setShareIntent(myShareIntent);</pre></li>
- * </ol>
- *
- * <h3 id="rankings">Share target rankings</h3>
- *
- * <p>The share action provider retains a ranking for each share target,
- * based on how often the user chooses each one. The more often a user
- * chooses a target, the higher its rank; the
- * most-commonly used target appears in the app bar as the default target.</p>
- *
- * <p>By default, the target ranking information is stored in a private
- * file with the name specified by {@link
- * #DEFAULT_SHARE_HISTORY_FILE_NAME}. Ordinarily, the share action provider stores
- * all the history in this single file. However, using a single set of
- * rankings may not make sense if the
- * share action provider is used for different kinds of content. For
- * example, if the activity sometimes shares images and sometimes shares
- * contacts, you would want to maintain two different sets of rankings.</p>
- *
- * <p>To set the history file, call {@link #setShareHistoryFileName
- * setShareHistoryFileName()} and pass the name of an XML file. The file
- * you specify is used until the next time you call {@link
- * #setShareHistoryFileName setShareHistoryFileName()}.</p>
- *
- * @see ActionProvider
- */
-public class ShareActionProvider extends ActionProvider {
-
-    /**
-     * Listener for the event of selecting a share target.
-     */
-    public interface OnShareTargetSelectedListener {
-
-        /**
-         * Called when a share target has been selected. The client can
-         * decide whether to perform some action before the sharing is
-         * actually performed.
-         * <p>
-         * <strong>Note:</strong> Modifying the intent is not permitted and
-         *     any changes to the latter will be ignored.
-         * </p>
-         * <p>
-         * <strong>Note:</strong> You should <strong>not</strong> handle the
-         *     intent here. This callback aims to notify the client that a
-         *     sharing is being performed, so the client can update the UI
-         *     if necessary.
-         * </p>
-         *
-         * @param source The source of the notification.
-         * @param intent The intent for launching the chosen share target.
-         * @return The return result is ignored. Always return false for consistency.
-         */
-        public boolean onShareTargetSelected(ShareActionProvider source, Intent intent);
-    }
-
-    /**
-     * The default for the maximal number of activities shown in the sub-menu.
-     */
-    private static final int DEFAULT_INITIAL_ACTIVITY_COUNT = 4;
-
-    /**
-     * The the maximum number activities shown in the sub-menu.
-     */
-    private int mMaxShownActivityCount = DEFAULT_INITIAL_ACTIVITY_COUNT;
-
-    /**
-     * Listener for handling menu item clicks.
-     */
-    private final ShareMenuItemOnMenuItemClickListener mOnMenuItemClickListener =
-            new ShareMenuItemOnMenuItemClickListener();
-
-    /**
-     * The default name for storing share history.
-     */
-    public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
-
-    /**
-     * Context for accessing resources.
-     */
-    final Context mContext;
-
-    /**
-     * The name of the file with share history data.
-     */
-    String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
-
-    OnShareTargetSelectedListener mOnShareTargetSelectedListener;
-
-    private OnChooseActivityListener mOnChooseActivityListener;
-
-    /**
-     * Creates a new instance.
-     *
-     * @param context Context for accessing resources.
-     */
-    public ShareActionProvider(Context context) {
-        super(context);
-        mContext = context;
-    }
-
-    /**
-     * Sets a listener to be notified when a share target has been selected.
-     * The listener can optionally decide to handle the selection and
-     * not rely on the default behavior which is to launch the activity.
-     * <p>
-     * <strong>Note:</strong> If you choose the backing share history file
-     *     you will still be notified in this callback.
-     * </p>
-     * @param listener The listener.
-     */
-    public void setOnShareTargetSelectedListener(OnShareTargetSelectedListener listener) {
-        mOnShareTargetSelectedListener = listener;
-        setActivityChooserPolicyIfNeeded();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View onCreateActionView() {
-        // Create the view and set its data model.
-        ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
-        if (!activityChooserView.isInEditMode()) {
-            ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
-            activityChooserView.setActivityChooserModel(dataModel);
-        }
-
-        // Lookup and set the expand action icon.
-        TypedValue outTypedValue = new TypedValue();
-        mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
-        Drawable drawable = AppCompatResources.getDrawable(mContext, outTypedValue.resourceId);
-        activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
-        activityChooserView.setProvider(this);
-
-        // Set content description.
-        activityChooserView.setDefaultActionButtonContentDescription(
-                R.string.abc_shareactionprovider_share_with_application);
-        activityChooserView.setExpandActivityOverflowButtonContentDescription(
-                R.string.abc_shareactionprovider_share_with);
-
-        return activityChooserView;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean hasSubMenu() {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onPrepareSubMenu(SubMenu subMenu) {
-        // Clear since the order of items may change.
-        subMenu.clear();
-
-        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
-        PackageManager packageManager = mContext.getPackageManager();
-
-        final int expandedActivityCount = dataModel.getActivityCount();
-        final int collapsedActivityCount = Math.min(expandedActivityCount, mMaxShownActivityCount);
-
-        // Populate the sub-menu with a sub set of the activities.
-        for (int i = 0; i < collapsedActivityCount; i++) {
-            ResolveInfo activity = dataModel.getActivity(i);
-            subMenu.add(0, i, i, activity.loadLabel(packageManager))
-                    .setIcon(activity.loadIcon(packageManager))
-                    .setOnMenuItemClickListener(mOnMenuItemClickListener);
-        }
-
-        if (collapsedActivityCount < expandedActivityCount) {
-            // Add a sub-menu for showing all activities as a list item.
-            SubMenu expandedSubMenu = subMenu.addSubMenu(Menu.NONE, collapsedActivityCount,
-                    collapsedActivityCount,
-                    mContext.getString(R.string.abc_activity_chooser_view_see_all));
-            for (int i = 0; i < expandedActivityCount; i++) {
-                ResolveInfo activity = dataModel.getActivity(i);
-                expandedSubMenu.add(0, i, i, activity.loadLabel(packageManager))
-                        .setIcon(activity.loadIcon(packageManager))
-                        .setOnMenuItemClickListener(mOnMenuItemClickListener);
-            }
-        }
-    }
-
-    /**
-     * Sets the file name of a file for persisting the share history which
-     * history will be used for ordering share targets. This file will be used
-     * for all view created by {@link #onCreateActionView()}. Defaults to
-     * {@link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to <code>null</code>
-     * if share history should not be persisted between sessions.
-     *
-     * <p class="note">
-     * <strong>Note:</strong> The history file name can be set any time, however
-     * only the action views created by {@link #onCreateActionView()} after setting
-     * the file name will be backed by the provided file. Therefore, if you want to
-     * use different history files for sharing specific types of content, every time
-     * you change the history file with {@link #setShareHistoryFileName(String)} you must
-     * call {@link android.support.v7.app.AppCompatActivity#supportInvalidateOptionsMenu()}
-     * to recreate the action view. You should <strong>not</strong> call
-     * {@link android.support.v7.app.AppCompatActivity#supportInvalidateOptionsMenu()} from
-     * {@link android.support.v7.app.AppCompatActivity#onCreateOptionsMenu(Menu)}.
-     *
-     * <pre>
-     * private void doShare(Intent intent) {
-     *     if (IMAGE.equals(intent.getMimeType())) {
-     *         mShareActionProvider.setHistoryFileName(SHARE_IMAGE_HISTORY_FILE_NAME);
-     *     } else if (TEXT.equals(intent.getMimeType())) {
-     *         mShareActionProvider.setHistoryFileName(SHARE_TEXT_HISTORY_FILE_NAME);
-     *     }
-     *     mShareActionProvider.setIntent(intent);
-     *     supportInvalidateOptionsMenu();
-     * }
-     * </pre>
-     *
-     * @param shareHistoryFile The share history file name.
-     */
-    public void setShareHistoryFileName(String shareHistoryFile) {
-        mShareHistoryFileName = shareHistoryFile;
-        setActivityChooserPolicyIfNeeded();
-    }
-
-    /**
-     * Sets an intent with information about the share action. Here is a
-     * sample for constructing a share intent:
-     *
-     * <pre>
-     *  Intent shareIntent = new Intent(Intent.ACTION_SEND);
-     *  shareIntent.setType("image/*");
-     *  Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
-     *  shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
-     * </pre>
-     *
-     * @param shareIntent The share intent.
-     *
-     * @see Intent#ACTION_SEND
-     * @see Intent#ACTION_SEND_MULTIPLE
-     */
-    public void setShareIntent(Intent shareIntent) {
-        if (shareIntent != null) {
-            final String action = shareIntent.getAction();
-            if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
-                updateIntent(shareIntent);
-            }
-        }
-        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
-                mShareHistoryFileName);
-        dataModel.setIntent(shareIntent);
-    }
-
-    /**
-     * Reusable listener for handling share item clicks.
-     */
-    private class ShareMenuItemOnMenuItemClickListener implements OnMenuItemClickListener {
-        ShareMenuItemOnMenuItemClickListener() {
-        }
-
-        @Override
-        public boolean onMenuItemClick(MenuItem item) {
-            ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
-                    mShareHistoryFileName);
-            final int itemId = item.getItemId();
-            Intent launchIntent = dataModel.chooseActivity(itemId);
-            if (launchIntent != null) {
-                final String action = launchIntent.getAction();
-                if (Intent.ACTION_SEND.equals(action) ||
-                        Intent.ACTION_SEND_MULTIPLE.equals(action)) {
-                    updateIntent(launchIntent);
-                }
-                mContext.startActivity(launchIntent);
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Set the activity chooser policy of the model backed by the current
-     * share history file if needed which is if there is a registered callback.
-     */
-    private void setActivityChooserPolicyIfNeeded() {
-        if (mOnShareTargetSelectedListener == null) {
-            return;
-        }
-        if (mOnChooseActivityListener == null) {
-            mOnChooseActivityListener = new ShareActivityChooserModelPolicy();
-        }
-        ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
-        dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
-    }
-
-    /**
-     * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such.
-     */
-    private class ShareActivityChooserModelPolicy implements OnChooseActivityListener {
-        ShareActivityChooserModelPolicy() {
-        }
-
-        @Override
-        public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
-            if (mOnShareTargetSelectedListener != null) {
-                mOnShareTargetSelectedListener.onShareTargetSelected(
-                        ShareActionProvider.this, intent);
-            }
-            return false;
-        }
-    }
-
-    void updateIntent(Intent intent) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            // If we're on Lollipop, we can open the intent as a document
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
-                    Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-        } else {
-            // Else, we will use the old CLEAR_WHEN_TASK_RESET flag
-            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-        }
-    }
-}
diff --git a/android/support/v7/widget/SimpleItemAnimator.java b/android/support/v7/widget/SimpleItemAnimator.java
deleted file mode 100644
index fe704dc..0000000
--- a/android/support/v7/widget/SimpleItemAnimator.java
+++ /dev/null
@@ -1,457 +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.widget;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView.Adapter;
-import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.util.Log;
-import android.view.View;
-
-/**
- * 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
- * API.
- * <p>
- * It uses {@link ItemHolderInfo} to track the bounds information of the Views. If you would like
- * to
- * extend this class, you can override {@link #obtainHolderInfo()} method to provide your own info
- * class that extends {@link ItemHolderInfo}.
- */
-public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
-
-    private static final boolean DEBUG = false;
-
-    private static final String TAG = "SimpleItemAnimator";
-
-    boolean mSupportsChangeAnimations = true;
-
-    /**
-     * Returns whether this ItemAnimator supports animations of change events.
-     *
-     * @return true if change animations are supported, false otherwise
-     */
-    @SuppressWarnings("unused")
-    public boolean getSupportsChangeAnimations() {
-        return mSupportsChangeAnimations;
-    }
-
-    /**
-     * Sets whether this ItemAnimator supports animations of item change events.
-     * If you set this property to false, actions on the data set which change the
-     * contents of items will not be animated. What those animations do is left
-     * up to the discretion of the ItemAnimator subclass, in its
-     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
-     * The value of this property is true by default.
-     *
-     * @param supportsChangeAnimations true if change animations are supported by
-     *                                 this ItemAnimator, false otherwise. If the property is false,
-     *                                 the ItemAnimator
-     *                                 will not receive a call to
-     *                                 {@link #animateChange(ViewHolder, ViewHolder, int, int, int,
-     *                                 int)} when changes occur.
-     * @see Adapter#notifyItemChanged(int)
-     * @see Adapter#notifyItemRangeChanged(int, int)
-     */
-    public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
-        mSupportsChangeAnimations = supportsChangeAnimations;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @return True if change animations are not supported or the ViewHolder is invalid,
-     * false otherwise.
-     *
-     * @see #setSupportsChangeAnimations(boolean)
-     */
-    @Override
-    public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
-        return !mSupportsChangeAnimations || viewHolder.isInvalid();
-    }
-
-    @Override
-    public boolean animateDisappearance(@NonNull ViewHolder viewHolder,
-            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
-        int oldLeft = preLayoutInfo.left;
-        int oldTop = preLayoutInfo.top;
-        View disappearingItemView = viewHolder.itemView;
-        int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
-        int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
-        if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
-            disappearingItemView.layout(newLeft, newTop,
-                    newLeft + disappearingItemView.getWidth(),
-                    newTop + disappearingItemView.getHeight());
-            if (DEBUG) {
-                Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
-            }
-            return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
-        } else {
-            if (DEBUG) {
-                Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
-            }
-            return animateRemove(viewHolder);
-        }
-    }
-
-    @Override
-    public boolean animateAppearance(@NonNull ViewHolder viewHolder,
-            @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
-        if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
-                || preLayoutInfo.top != postLayoutInfo.top)) {
-            // slide items in if before/after locations differ
-            if (DEBUG) {
-                Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
-            }
-            return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
-                    postLayoutInfo.left, postLayoutInfo.top);
-        } else {
-            if (DEBUG) {
-                Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
-            }
-            return animateAdd(viewHolder);
-        }
-    }
-
-    @Override
-    public boolean animatePersistence(@NonNull ViewHolder viewHolder,
-            @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);
-            }
-            return animateMove(viewHolder,
-                    preInfo.left, preInfo.top, postInfo.left, postInfo.top);
-        }
-        dispatchMoveFinished(viewHolder);
-        return false;
-    }
-
-    @Override
-    public boolean animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
-            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
-        if (DEBUG) {
-            Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
-        }
-        final int fromLeft = preInfo.left;
-        final int fromTop = preInfo.top;
-        final int toLeft, toTop;
-        if (newHolder.shouldIgnore()) {
-            toLeft = preInfo.left;
-            toTop = preInfo.top;
-        } else {
-            toLeft = postInfo.left;
-            toTop = postInfo.top;
-        }
-        return animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft, toTop);
-    }
-
-    /**
-     * Called when an item is removed from the RecyclerView. Implementors can choose
-     * whether and how to animate that change, but must always call
-     * {@link #dispatchRemoveFinished(ViewHolder)} when done, either
-     * immediately (if no animation will occur) or after the animation actually finishes.
-     * The return value indicates whether an animation has been set up and whether the
-     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
-     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
-     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
-     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
-     * {@link #animateRemove(ViewHolder) animateRemove()}, and
-     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
-     * then start the animations together in the later call to {@link #runPendingAnimations()}.
-     *
-     * <p>This method may also be called for disappearing items which continue to exist in the
-     * RecyclerView, but for which the system does not have enough information to animate
-     * them out of view. In that case, the default animation for removing items is run
-     * on those items as well.</p>
-     *
-     * @param holder The item that is being removed.
-     * @return true if a later call to {@link #runPendingAnimations()} is requested,
-     * false otherwise.
-     */
-    public abstract boolean animateRemove(ViewHolder holder);
-
-    /**
-     * Called when an item is added to the RecyclerView. Implementors can choose
-     * whether and how to animate that change, but must always call
-     * {@link #dispatchAddFinished(ViewHolder)} when done, either
-     * immediately (if no animation will occur) or after the animation actually finishes.
-     * The return value indicates whether an animation has been set up and whether the
-     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
-     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
-     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
-     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
-     * {@link #animateRemove(ViewHolder) animateRemove()}, and
-     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
-     * then start the animations together in the later call to {@link #runPendingAnimations()}.
-     *
-     * <p>This method may also be called for appearing items which were already in the
-     * RecyclerView, but for which the system does not have enough information to animate
-     * them into view. In that case, the default animation for adding items is run
-     * on those items as well.</p>
-     *
-     * @param holder The item that is being added.
-     * @return true if a later call to {@link #runPendingAnimations()} is requested,
-     * false otherwise.
-     */
-    public abstract boolean animateAdd(ViewHolder holder);
-
-    /**
-     * Called when an item is moved in the RecyclerView. Implementors can choose
-     * whether and how to animate that change, but must always call
-     * {@link #dispatchMoveFinished(ViewHolder)} when done, either
-     * immediately (if no animation will occur) or after the animation actually finishes.
-     * The return value indicates whether an animation has been set up and whether the
-     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
-     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
-     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
-     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
-     * {@link #animateRemove(ViewHolder) animateRemove()}, and
-     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
-     * then start the animations together in the later call to {@link #runPendingAnimations()}.
-     *
-     * @param holder The item that is being moved.
-     * @return true if a later call to {@link #runPendingAnimations()} is requested,
-     * false otherwise.
-     */
-    public abstract boolean animateMove(ViewHolder holder, int fromX, int fromY,
-            int toX, int toY);
-
-    /**
-     * Called when an item is changed in the RecyclerView, as indicated by a call to
-     * {@link Adapter#notifyItemChanged(int)} or
-     * {@link Adapter#notifyItemRangeChanged(int, int)}.
-     * <p>
-     * Implementers can choose whether and how to animate changes, but must always call
-     * {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null distinct ViewHolder,
-     * either immediately (if no animation will occur) or after the animation actually finishes.
-     * If the {@code oldHolder} is the same ViewHolder as the {@code newHolder}, you must call
-     * {@link #dispatchChangeFinished(ViewHolder, boolean)} once and only once. In that case, the
-     * second parameter of {@code dispatchChangeFinished} is ignored.
-     * <p>
-     * The return value indicates whether an animation has been set up and whether the
-     * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
-     * next opportunity. This mechanism allows ItemAnimator to set up individual animations
-     * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
-     * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
-     * {@link #animateRemove(ViewHolder) animateRemove()}, and
-     * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
-     * then start the animations together in the later call to {@link #runPendingAnimations()}.
-     *
-     * @param oldHolder The original item that changed.
-     * @param newHolder The new item that was created with the changed content. Might be null
-     * @param fromLeft  Left of the old view holder
-     * @param fromTop   Top of the old view holder
-     * @param toLeft    Left of the new view holder
-     * @param toTop     Top of the new view holder
-     * @return true if a later call to {@link #runPendingAnimations()} is requested,
-     * false otherwise.
-     */
-    public abstract boolean animateChange(ViewHolder oldHolder,
-            ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
-
-    /**
-     * Method to be called by subclasses when a remove animation is done.
-     *
-     * @param item The item which has been removed
-     * @see RecyclerView.ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo,
-     * ItemHolderInfo)
-     */
-    public final void dispatchRemoveFinished(ViewHolder item) {
-        onRemoveFinished(item);
-        dispatchAnimationFinished(item);
-    }
-
-    /**
-     * Method to be called by subclasses when a move animation is done.
-     *
-     * @param item The item which has been moved
-     * @see RecyclerView.ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo,
-     * ItemHolderInfo)
-     * @see RecyclerView.ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-     * @see RecyclerView.ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
-     */
-    public final void dispatchMoveFinished(ViewHolder item) {
-        onMoveFinished(item);
-        dispatchAnimationFinished(item);
-    }
-
-    /**
-     * Method to be called by subclasses when an add animation is done.
-     *
-     * @param item The item which has been added
-     */
-    public final void dispatchAddFinished(ViewHolder item) {
-        onAddFinished(item);
-        dispatchAnimationFinished(item);
-    }
-
-    /**
-     * Method to be called by subclasses when a change animation is done.
-     *
-     * @param item    The item which has been changed (this method must be called for
-     *                each non-null ViewHolder passed into
-     *                {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
-     * @param oldItem true if this is the old item that was changed, false if
-     *                it is the new item that replaced the old item.
-     * @see #animateChange(ViewHolder, ViewHolder, int, int, int, int)
-     */
-    public final void dispatchChangeFinished(ViewHolder item, boolean oldItem) {
-        onChangeFinished(item, oldItem);
-        dispatchAnimationFinished(item);
-    }
-
-    /**
-     * Method to be called by subclasses when a remove animation is being started.
-     *
-     * @param item The item being removed
-     */
-    public final void dispatchRemoveStarting(ViewHolder item) {
-        onRemoveStarting(item);
-    }
-
-    /**
-     * Method to be called by subclasses when a move animation is being started.
-     *
-     * @param item The item being moved
-     */
-    public final void dispatchMoveStarting(ViewHolder item) {
-        onMoveStarting(item);
-    }
-
-    /**
-     * Method to be called by subclasses when an add animation is being started.
-     *
-     * @param item The item being added
-     */
-    public final void dispatchAddStarting(ViewHolder item) {
-        onAddStarting(item);
-    }
-
-    /**
-     * Method to be called by subclasses when a change animation is being started.
-     *
-     * @param item    The item which has been changed (this method must be called for
-     *                each non-null ViewHolder passed into
-     *                {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
-     * @param oldItem true if this is the old item that was changed, false if
-     *                it is the new item that replaced the old item.
-     */
-    public final void dispatchChangeStarting(ViewHolder item, boolean oldItem) {
-        onChangeStarting(item, oldItem);
-    }
-
-    /**
-     * Called when a remove animation is being started on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item The ViewHolder being animated.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public void onRemoveStarting(ViewHolder item) {
-    }
-
-    /**
-     * Called when a remove animation has ended on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item The ViewHolder being animated.
-     */
-    public void onRemoveFinished(ViewHolder item) {
-    }
-
-    /**
-     * Called when an add animation is being started on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item The ViewHolder being animated.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public void onAddStarting(ViewHolder item) {
-    }
-
-    /**
-     * Called when an add animation has ended on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item The ViewHolder being animated.
-     */
-    public void onAddFinished(ViewHolder item) {
-    }
-
-    /**
-     * Called when a move animation is being started on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item The ViewHolder being animated.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public void onMoveStarting(ViewHolder item) {
-    }
-
-    /**
-     * Called when a move animation has ended on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item The ViewHolder being animated.
-     */
-    public void onMoveFinished(ViewHolder item) {
-    }
-
-    /**
-     * Called when a change animation is being started on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item    The ViewHolder being animated.
-     * @param oldItem true if this is the old item that was changed, false if
-     *                it is the new item that replaced the old item.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public void onChangeStarting(ViewHolder item, boolean oldItem) {
-    }
-
-    /**
-     * Called when a change animation has ended on the given ViewHolder.
-     * The default implementation does nothing. Subclasses may wish to override
-     * this method to handle any ViewHolder-specific operations linked to animation
-     * lifecycles.
-     *
-     * @param item    The ViewHolder being animated.
-     * @param oldItem true if this is the old item that was changed, false if
-     *                it is the new item that replaced the old item.
-     */
-    public void onChangeFinished(ViewHolder item, boolean oldItem) {
-    }
-}
-
diff --git a/android/support/v7/widget/SnapHelper.java b/android/support/v7/widget/SnapHelper.java
deleted file mode 100644
index 4c41370..0000000
--- a/android/support/v7/widget/SnapHelper.java
+++ /dev/null
@@ -1,304 +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.widget;
-
-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;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Scroller;
-
-/**
- * Class intended to support snapping for a {@link RecyclerView}.
- * <p>
- * SnapHelper tries to handle fling as well but for this to work properly, the
- * {@link RecyclerView.LayoutManager} must implement the {@link ScrollVectorProvider} interface or
- * you should override {@link #onFling(int, int)} and handle fling manually.
- */
-public abstract class SnapHelper extends RecyclerView.OnFlingListener {
-
-    static final float MILLISECONDS_PER_INCH = 100f;
-
-    RecyclerView mRecyclerView;
-    private Scroller mGravityScroller;
-
-    // Handles the snap on scroll case.
-    private final RecyclerView.OnScrollListener mScrollListener =
-            new RecyclerView.OnScrollListener() {
-                boolean mScrolled = false;
-
-                @Override
-                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-                    super.onScrollStateChanged(recyclerView, newState);
-                    if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
-                        mScrolled = false;
-                        snapToTargetExistingView();
-                    }
-                }
-
-                @Override
-                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-                    if (dx != 0 || dy != 0) {
-                        mScrolled = true;
-                    }
-                }
-            };
-
-    @Override
-    public boolean onFling(int velocityX, int velocityY) {
-        LayoutManager layoutManager = mRecyclerView.getLayoutManager();
-        if (layoutManager == null) {
-            return false;
-        }
-        RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
-        if (adapter == null) {
-            return false;
-        }
-        int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
-        return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
-                && snapFromFling(layoutManager, velocityX, velocityY);
-    }
-
-    /**
-     * Attaches the {@link SnapHelper} to the provided RecyclerView, by calling
-     * {@link RecyclerView#setOnFlingListener(RecyclerView.OnFlingListener)}.
-     * You can call this method with {@code null} to detach it from the current RecyclerView.
-     *
-     * @param recyclerView The RecyclerView instance to which you want to add this helper or
-     *                     {@code null} if you want to remove SnapHelper from the current
-     *                     RecyclerView.
-     *
-     * @throws IllegalArgumentException if there is already a {@link RecyclerView.OnFlingListener}
-     * attached to the provided {@link RecyclerView}.
-     *
-     */
-    public void attachToRecyclerView(@Nullable RecyclerView recyclerView)
-            throws IllegalStateException {
-        if (mRecyclerView == recyclerView) {
-            return; // nothing to do
-        }
-        if (mRecyclerView != null) {
-            destroyCallbacks();
-        }
-        mRecyclerView = recyclerView;
-        if (mRecyclerView != null) {
-            setupCallbacks();
-            mGravityScroller = new Scroller(mRecyclerView.getContext(),
-                    new DecelerateInterpolator());
-            snapToTargetExistingView();
-        }
-    }
-
-    /**
-     * Called when an instance of a {@link RecyclerView} is attached.
-     */
-    private void setupCallbacks() throws IllegalStateException {
-        if (mRecyclerView.getOnFlingListener() != null) {
-            throw new IllegalStateException("An instance of OnFlingListener already set.");
-        }
-        mRecyclerView.addOnScrollListener(mScrollListener);
-        mRecyclerView.setOnFlingListener(this);
-    }
-
-    /**
-     * Called when the instance of a {@link RecyclerView} is detached.
-     */
-    private void destroyCallbacks() {
-        mRecyclerView.removeOnScrollListener(mScrollListener);
-        mRecyclerView.setOnFlingListener(null);
-    }
-
-    /**
-     * Calculated the estimated scroll distance in each direction given velocities on both axes.
-     *
-     * @param velocityX     Fling velocity on the horizontal axis.
-     * @param velocityY     Fling velocity on the vertical axis.
-     *
-     * @return array holding the calculated distances in x and y directions
-     * respectively.
-     */
-    public int[] calculateScrollDistance(int velocityX, int velocityY) {
-        int[] outDist = new int[2];
-        mGravityScroller.fling(0, 0, velocityX, velocityY,
-                Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
-        outDist[0] = mGravityScroller.getFinalX();
-        outDist[1] = mGravityScroller.getFinalY();
-        return outDist;
-    }
-
-    /**
-     * Helper method to facilitate for snapping triggered by a fling.
-     *
-     * @param layoutManager The {@link LayoutManager} associated with the attached
-     *                      {@link RecyclerView}.
-     * @param velocityX     Fling velocity on the horizontal axis.
-     * @param velocityY     Fling velocity on the vertical axis.
-     *
-     * @return true if it is handled, false otherwise.
-     */
-    private boolean snapFromFling(@NonNull LayoutManager layoutManager, int velocityX,
-            int velocityY) {
-        if (!(layoutManager instanceof ScrollVectorProvider)) {
-            return false;
-        }
-
-        SmoothScroller smoothScroller = createScroller(layoutManager);
-        if (smoothScroller == null) {
-            return false;
-        }
-
-        int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
-        if (targetPosition == RecyclerView.NO_POSITION) {
-            return false;
-        }
-
-        smoothScroller.setTargetPosition(targetPosition);
-        layoutManager.startSmoothScroll(smoothScroller);
-        return true;
-    }
-
-    /**
-     * Snaps to a target view which currently exists in the attached {@link RecyclerView}. This
-     * method is used to snap the view when the {@link RecyclerView} is first attached; when
-     * snapping was triggered by a scroll and when the fling is at its final stages.
-     */
-    void snapToTargetExistingView() {
-        if (mRecyclerView == null) {
-            return;
-        }
-        LayoutManager layoutManager = mRecyclerView.getLayoutManager();
-        if (layoutManager == null) {
-            return;
-        }
-        View snapView = findSnapView(layoutManager);
-        if (snapView == null) {
-            return;
-        }
-        int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, snapView);
-        if (snapDistance[0] != 0 || snapDistance[1] != 0) {
-            mRecyclerView.smoothScrollBy(snapDistance[0], snapDistance[1]);
-        }
-    }
-
-    /**
-     * 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 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;
-        }
-        return new LinearSmoothScroller(mRecyclerView.getContext()) {
-            @Override
-            protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
-                if (mRecyclerView == null) {
-                    // The associated RecyclerView has been removed so there is no action to take.
-                    return;
-                }
-                int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(),
-                        targetView);
-                final int dx = snapDistances[0];
-                final int dy = snapDistances[1];
-                final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
-                if (time > 0) {
-                    action.update(dx, dy, time, mDecelerateInterpolator);
-                }
-            }
-
-            @Override
-            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
-                return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
-            }
-        };
-    }
-
-    /**
-     * Override this method to snap to a particular point within the target view or the container
-     * view on any axis.
-     * <p>
-     * This method is called when the {@link SnapHelper} has intercepted a fling and it needs
-     * to know the exact distance required to scroll by in order to snap to the target view.
-     *
-     * @param layoutManager the {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}
-     * @param targetView the target view that is chosen as the view to snap
-     *
-     * @return the output coordinates the put the result into. out[0] is the distance
-     * on horizontal axis and out[1] is the distance on vertical axis.
-     */
-    @SuppressWarnings("WeakerAccess")
-    @Nullable
-    public abstract int[] calculateDistanceToFinalSnap(@NonNull LayoutManager layoutManager,
-            @NonNull View targetView);
-
-    /**
-     * Override this method to provide a particular target view for snapping.
-     * <p>
-     * This method is called when the {@link SnapHelper} is ready to start snapping and requires
-     * a target view to snap to. It will be explicitly called when the scroll state becomes idle
-     * after a scroll. It will also be called when the {@link SnapHelper} is preparing to snap
-     * after a fling and requires a reference view from the current set of child views.
-     * <p>
-     * If this method returns {@code null}, SnapHelper will not snap to any view.
-     *
-     * @param layoutManager the {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}
-     *
-     * @return the target view to which to snap on fling or end of scroll
-     */
-    @SuppressWarnings("WeakerAccess")
-    @Nullable
-    public abstract View findSnapView(LayoutManager layoutManager);
-
-    /**
-     * Override to provide a particular adapter target position for snapping.
-     *
-     * @param layoutManager the {@link RecyclerView.LayoutManager} associated with the attached
-     *                      {@link RecyclerView}
-     * @param velocityX fling velocity on the horizontal axis
-     * @param velocityY fling velocity on the vertical axis
-     *
-     * @return the target adapter position to you want to snap or {@link RecyclerView#NO_POSITION}
-     *         if no snapping should happen
-     */
-    public abstract int findTargetSnapPosition(LayoutManager layoutManager, int velocityX,
-            int velocityY);
-}
diff --git a/android/support/v7/widget/StaggeredGridLayoutManager.java b/android/support/v7/widget/StaggeredGridLayoutManager.java
deleted file mode 100644
index 4e560b4..0000000
--- a/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ /dev/null
@@ -1,3267 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD;
-import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL;
-import static android.support.v7.widget.LayoutState.LAYOUT_END;
-import static android.support.v7.widget.LayoutState.LAYOUT_START;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.List;
-
-/**
- * A LayoutManager that lays out children in a staggered grid formation.
- * It supports horizontal & vertical layout as well as an ability to layout children in reverse.
- * <p>
- * Staggered grids are likely to have gaps at the edges of the layout. To avoid these gaps,
- * StaggeredGridLayoutManager can offset spans independently or move items between spans. You can
- * control this behavior via {@link #setGapStrategy(int)}.
- */
-public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager implements
-        RecyclerView.SmoothScroller.ScrollVectorProvider {
-
-    private static final String TAG = "StaggeredGridLManager";
-
-    static final boolean DEBUG = false;
-
-    public static final int HORIZONTAL = RecyclerView.HORIZONTAL;
-
-    public static final int VERTICAL = RecyclerView.VERTICAL;
-
-    /**
-     * Does not do anything to hide gaps.
-     */
-    public static final int GAP_HANDLING_NONE = 0;
-
-    /**
-     * @deprecated No longer supported.
-     */
-    @SuppressWarnings("unused")
-    @Deprecated
-    public static final int GAP_HANDLING_LAZY = 1;
-
-    /**
-     * When scroll state is changed to {@link RecyclerView#SCROLL_STATE_IDLE}, StaggeredGrid will
-     * check if there are gaps in the because of full span items. If it finds, it will re-layout
-     * and move items to correct positions with animations.
-     * <p>
-     * For example, if LayoutManager ends up with the following layout due to adapter changes:
-     * <pre>
-     * AAA
-     * _BC
-     * DDD
-     * </pre>
-     * <p>
-     * It will animate to the following state:
-     * <pre>
-     * AAA
-     * BC_
-     * DDD
-     * </pre>
-     */
-    public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2;
-
-    static final int INVALID_OFFSET = Integer.MIN_VALUE;
-    /**
-     * While trying to find next view to focus, LayoutManager will not try to scroll more
-     * than this factor times the total space of the list. If layout is vertical, total space is the
-     * height minus padding, if layout is horizontal, total space is the width minus padding.
-     */
-    private static final float MAX_SCROLL_FACTOR = 1 / 3f;
-
-    /**
-     * Number of spans
-     */
-    private int mSpanCount = -1;
-
-    Span[] mSpans;
-
-    /**
-     * Primary orientation is the layout's orientation, secondary orientation is the orientation
-     * for spans. Having both makes code much cleaner for calculations.
-     */
-    @NonNull
-    OrientationHelper mPrimaryOrientation;
-    @NonNull
-    OrientationHelper mSecondaryOrientation;
-
-    private int mOrientation;
-
-    /**
-     * The width or height per span, depending on the orientation.
-     */
-    private int mSizePerSpan;
-
-    @NonNull
-    private final LayoutState mLayoutState;
-
-    boolean mReverseLayout = false;
-
-    /**
-     * Aggregated reverse layout value that takes RTL into account.
-     */
-    boolean mShouldReverseLayout = false;
-
-    /**
-     * Temporary variable used during fill method to check which spans needs to be filled.
-     */
-    private BitSet mRemainingSpans;
-
-    /**
-     * When LayoutManager needs to scroll to a position, it sets this variable and requests a
-     * layout which will check this variable and re-layout accordingly.
-     */
-    int mPendingScrollPosition = NO_POSITION;
-
-    /**
-     * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is
-     * called.
-     */
-    int mPendingScrollPositionOffset = INVALID_OFFSET;
-
-    /**
-     * Keeps the mapping between the adapter positions and spans. This is necessary to provide
-     * a consistent experience when user scrolls the list.
-     */
-    LazySpanLookup mLazySpanLookup = new LazySpanLookup();
-
-    /**
-     * how we handle gaps in UI.
-     */
-    private int mGapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
-
-    /**
-     * Saved state needs this information to properly layout on restore.
-     */
-    private boolean mLastLayoutFromEnd;
-
-    /**
-     * Saved state and onLayout needs this information to re-layout properly
-     */
-    private boolean mLastLayoutRTL;
-
-    /**
-     * SavedState is not handled until a layout happens. This is where we keep it until next
-     * layout.
-     */
-    private SavedState mPendingSavedState;
-
-    /**
-     * Re-used measurement specs. updated by onLayout.
-     */
-    private int mFullSizeSpec;
-
-    /**
-     * Re-used rectangle to get child decor offsets.
-     */
-    private final Rect mTmpRect = new Rect();
-
-    /**
-     * Re-used anchor info.
-     */
-    private final AnchorInfo mAnchorInfo = new AnchorInfo();
-
-    /**
-     * If a full span item is invalid / or created in reverse direction; it may create gaps in
-     * the UI. While laying out, if such case is detected, we set this flag.
-     * <p>
-     * After scrolling stops, we check this flag and if it is set, re-layout.
-     */
-    private boolean mLaidOutInvalidFullSpan = false;
-
-    /**
-     * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
-     * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
-     */
-    private boolean mSmoothScrollbarEnabled = true;
-
-    /**
-     * Temporary array used (solely in {@link #collectAdjacentPrefetchPositions}) for stashing and
-     * sorting distances to views being prefetched.
-     */
-    private int[] mPrefetchDistances;
-
-    private final Runnable mCheckForGapsRunnable = new Runnable() {
-        @Override
-        public void run() {
-            checkForGaps();
-        }
-    };
-
-    /**
-     * Constructor used when layout manager is set in XML by RecyclerView attribute
-     * "layoutManager". Defaults to single column and vertical.
-     */
-    @SuppressWarnings("unused")
-    public StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
-        setOrientation(properties.orientation);
-        setSpanCount(properties.spanCount);
-        setReverseLayout(properties.reverseLayout);
-        setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE);
-        mLayoutState = new LayoutState();
-        createOrientationHelpers();
-    }
-
-    /**
-     * Creates a StaggeredGridLayoutManager with given parameters.
-     *
-     * @param spanCount   If orientation is vertical, spanCount is number of columns. If
-     *                    orientation is horizontal, spanCount is number of rows.
-     * @param orientation {@link #VERTICAL} or {@link #HORIZONTAL}
-     */
-    public StaggeredGridLayoutManager(int spanCount, int orientation) {
-        mOrientation = orientation;
-        setSpanCount(spanCount);
-        setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE);
-        mLayoutState = new LayoutState();
-        createOrientationHelpers();
-    }
-
-    private void createOrientationHelpers() {
-        mPrimaryOrientation = OrientationHelper.createOrientationHelper(this, mOrientation);
-        mSecondaryOrientation = OrientationHelper
-                .createOrientationHelper(this, 1 - mOrientation);
-    }
-
-    /**
-     * Checks for gaps in the UI that may be caused by adapter changes.
-     * <p>
-     * When a full span item is laid out in reverse direction, it sets a flag which we check when
-     * scroll is stopped (or re-layout happens) and re-layout after first valid item.
-     */
-    boolean checkForGaps() {
-        if (getChildCount() == 0 || mGapStrategy == GAP_HANDLING_NONE || !isAttachedToWindow()) {
-            return false;
-        }
-        final int minPos, maxPos;
-        if (mShouldReverseLayout) {
-            minPos = getLastChildPosition();
-            maxPos = getFirstChildPosition();
-        } else {
-            minPos = getFirstChildPosition();
-            maxPos = getLastChildPosition();
-        }
-        if (minPos == 0) {
-            View gapView = hasGapsToFix();
-            if (gapView != null) {
-                mLazySpanLookup.clear();
-                requestSimpleAnimationsInNextLayout();
-                requestLayout();
-                return true;
-            }
-        }
-        if (!mLaidOutInvalidFullSpan) {
-            return false;
-        }
-        int invalidGapDir = mShouldReverseLayout ? LAYOUT_START : LAYOUT_END;
-        final LazySpanLookup.FullSpanItem invalidFsi = mLazySpanLookup
-                .getFirstFullSpanItemInRange(minPos, maxPos + 1, invalidGapDir, true);
-        if (invalidFsi == null) {
-            mLaidOutInvalidFullSpan = false;
-            mLazySpanLookup.forceInvalidateAfter(maxPos + 1);
-            return false;
-        }
-        final LazySpanLookup.FullSpanItem validFsi = mLazySpanLookup
-                .getFirstFullSpanItemInRange(minPos, invalidFsi.mPosition,
-                        invalidGapDir * -1, true);
-        if (validFsi == null) {
-            mLazySpanLookup.forceInvalidateAfter(invalidFsi.mPosition);
-        } else {
-            mLazySpanLookup.forceInvalidateAfter(validFsi.mPosition + 1);
-        }
-        requestSimpleAnimationsInNextLayout();
-        requestLayout();
-        return true;
-    }
-
-    @Override
-    public void onScrollStateChanged(int state) {
-        if (state == RecyclerView.SCROLL_STATE_IDLE) {
-            checkForGaps();
-        }
-    }
-
-    @Override
-    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
-        removeCallbacks(mCheckForGapsRunnable);
-        for (int i = 0; i < mSpanCount; i++) {
-            mSpans[i].clear();
-        }
-        // SGLM will require fresh layout call to recover state after detach
-        view.requestLayout();
-    }
-
-    /**
-     * Checks for gaps if we've reached to the top of the list.
-     * <p>
-     * Intermediate gaps created by full span items are tracked via mLaidOutInvalidFullSpan field.
-     */
-    View hasGapsToFix() {
-        int startChildIndex = 0;
-        int endChildIndex = getChildCount() - 1;
-        BitSet mSpansToCheck = new BitSet(mSpanCount);
-        mSpansToCheck.set(0, mSpanCount, true);
-
-        final int firstChildIndex, childLimit;
-        final int preferredSpanDir = mOrientation == VERTICAL && isLayoutRTL() ? 1 : -1;
-
-        if (mShouldReverseLayout) {
-            firstChildIndex = endChildIndex;
-            childLimit = startChildIndex - 1;
-        } else {
-            firstChildIndex = startChildIndex;
-            childLimit = endChildIndex + 1;
-        }
-        final int nextChildDiff = firstChildIndex < childLimit ? 1 : -1;
-        for (int i = firstChildIndex; i != childLimit; i += nextChildDiff) {
-            View child = getChildAt(i);
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (mSpansToCheck.get(lp.mSpan.mIndex)) {
-                if (checkSpanForGap(lp.mSpan)) {
-                    return child;
-                }
-                mSpansToCheck.clear(lp.mSpan.mIndex);
-            }
-            if (lp.mFullSpan) {
-                continue; // quick reject
-            }
-
-            if (i + nextChildDiff != childLimit) {
-                View nextChild = getChildAt(i + nextChildDiff);
-                boolean compareSpans = false;
-                if (mShouldReverseLayout) {
-                    // ensure child's end is below nextChild's end
-                    int myEnd = mPrimaryOrientation.getDecoratedEnd(child);
-                    int nextEnd = mPrimaryOrientation.getDecoratedEnd(nextChild);
-                    if (myEnd < nextEnd) {
-                        return child; //i should have a better position
-                    } else if (myEnd == nextEnd) {
-                        compareSpans = true;
-                    }
-                } else {
-                    int myStart = mPrimaryOrientation.getDecoratedStart(child);
-                    int nextStart = mPrimaryOrientation.getDecoratedStart(nextChild);
-                    if (myStart > nextStart) {
-                        return child; //i should have a better position
-                    } else if (myStart == nextStart) {
-                        compareSpans = true;
-                    }
-                }
-                if (compareSpans) {
-                    // equal, check span indices.
-                    LayoutParams nextLp = (LayoutParams) nextChild.getLayoutParams();
-                    if (lp.mSpan.mIndex - nextLp.mSpan.mIndex < 0 != preferredSpanDir < 0) {
-                        return child;
-                    }
-                }
-            }
-        }
-        // everything looks good
-        return null;
-    }
-
-    private boolean checkSpanForGap(Span span) {
-        if (mShouldReverseLayout) {
-            if (span.getEndLine() < mPrimaryOrientation.getEndAfterPadding()) {
-                // if it is full span, it is OK
-                final View endView = span.mViews.get(span.mViews.size() - 1);
-                final LayoutParams lp = span.getLayoutParams(endView);
-                return !lp.mFullSpan;
-            }
-        } else if (span.getStartLine() > mPrimaryOrientation.getStartAfterPadding()) {
-            // if it is full span, it is OK
-            final View startView = span.mViews.get(0);
-            final LayoutParams lp = span.getLayoutParams(startView);
-            return !lp.mFullSpan;
-        }
-        return false;
-    }
-
-    /**
-     * Sets the number of spans for the layout. This will invalidate all of the span assignments
-     * for Views.
-     * <p>
-     * Calling this method will automatically result in a new layout request unless the spanCount
-     * parameter is equal to current span count.
-     *
-     * @param spanCount Number of spans to layout
-     */
-    public void setSpanCount(int spanCount) {
-        assertNotInLayoutOrScroll(null);
-        if (spanCount != mSpanCount) {
-            invalidateSpanAssignments();
-            mSpanCount = spanCount;
-            mRemainingSpans = new BitSet(mSpanCount);
-            mSpans = new Span[mSpanCount];
-            for (int i = 0; i < mSpanCount; i++) {
-                mSpans[i] = new Span(i);
-            }
-            requestLayout();
-        }
-    }
-
-    /**
-     * Sets the orientation of the layout. StaggeredGridLayoutManager will do its best to keep
-     * scroll position if this method is called after views are laid out.
-     *
-     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
-     */
-    public void setOrientation(int orientation) {
-        if (orientation != HORIZONTAL && orientation != VERTICAL) {
-            throw new IllegalArgumentException("invalid orientation.");
-        }
-        assertNotInLayoutOrScroll(null);
-        if (orientation == mOrientation) {
-            return;
-        }
-        mOrientation = orientation;
-        OrientationHelper tmp = mPrimaryOrientation;
-        mPrimaryOrientation = mSecondaryOrientation;
-        mSecondaryOrientation = tmp;
-        requestLayout();
-    }
-
-    /**
-     * Sets whether LayoutManager should start laying out items from the end of the UI. The order
-     * items are traversed is not affected by this call.
-     * <p>
-     * For vertical layout, if it is set to <code>true</code>, first item will be at the bottom of
-     * the list.
-     * <p>
-     * For horizontal layouts, it depends on the layout direction.
-     * When set to true, If {@link RecyclerView} is LTR, than it will layout from RTL, if
-     * {@link RecyclerView}} is RTL, it will layout from LTR.
-     *
-     * @param reverseLayout Whether layout should be in reverse or not
-     */
-    public void setReverseLayout(boolean reverseLayout) {
-        assertNotInLayoutOrScroll(null);
-        if (mPendingSavedState != null && mPendingSavedState.mReverseLayout != reverseLayout) {
-            mPendingSavedState.mReverseLayout = reverseLayout;
-        }
-        mReverseLayout = reverseLayout;
-        requestLayout();
-    }
-
-    /**
-     * Returns the current gap handling strategy for StaggeredGridLayoutManager.
-     * <p>
-     * Staggered grid may have gaps in the layout due to changes in the adapter. To avoid gaps,
-     * StaggeredGridLayoutManager provides 2 options. Check {@link #GAP_HANDLING_NONE} and
-     * {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} for details.
-     * <p>
-     * By default, StaggeredGridLayoutManager uses {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}.
-     *
-     * @return Current gap handling strategy.
-     * @see #setGapStrategy(int)
-     * @see #GAP_HANDLING_NONE
-     * @see #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
-     */
-    public int getGapStrategy() {
-        return mGapStrategy;
-    }
-
-    /**
-     * Sets the gap handling strategy for StaggeredGridLayoutManager. If the gapStrategy parameter
-     * is different than the current strategy, calling this method will trigger a layout request.
-     *
-     * @param gapStrategy The new gap handling strategy. Should be
-     *                    {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} or {@link
-     *                    #GAP_HANDLING_NONE}.
-     * @see #getGapStrategy()
-     */
-    public void setGapStrategy(int gapStrategy) {
-        assertNotInLayoutOrScroll(null);
-        if (gapStrategy == mGapStrategy) {
-            return;
-        }
-        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");
-        }
-        mGapStrategy = gapStrategy;
-        setAutoMeasureEnabled(mGapStrategy != GAP_HANDLING_NONE);
-        requestLayout();
-    }
-
-    @Override
-    public void assertNotInLayoutOrScroll(String message) {
-        if (mPendingSavedState == null) {
-            super.assertNotInLayoutOrScroll(message);
-        }
-    }
-
-    /**
-     * Returns the number of spans laid out by StaggeredGridLayoutManager.
-     *
-     * @return Number of spans in the layout
-     */
-    public int getSpanCount() {
-        return mSpanCount;
-    }
-
-    /**
-     * For consistency, StaggeredGridLayoutManager keeps a mapping between spans and items.
-     * <p>
-     * If you need to cancel current assignments, you can call this method which will clear all
-     * assignments and request a new layout.
-     */
-    public void invalidateSpanAssignments() {
-        mLazySpanLookup.clear();
-        requestLayout();
-    }
-
-    /**
-     * Calculates the views' layout order. (e.g. from end to start or start to end)
-     * RTL layout support is applied automatically. So if layout is RTL and
-     * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
-     */
-    private void resolveShouldLayoutReverse() {
-        // A == B is the same result, but we rather keep it readable
-        if (mOrientation == VERTICAL || !isLayoutRTL()) {
-            mShouldReverseLayout = mReverseLayout;
-        } else {
-            mShouldReverseLayout = !mReverseLayout;
-        }
-    }
-
-    boolean isLayoutRTL() {
-        return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
-    }
-
-    /**
-     * Returns whether views are laid out in reverse order or not.
-     * <p>
-     * Not that this value is not affected by RecyclerView's layout direction.
-     *
-     * @return True if layout is reversed, false otherwise
-     * @see #setReverseLayout(boolean)
-     */
-    public boolean getReverseLayout() {
-        return mReverseLayout;
-    }
-
-    @Override
-    public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
-        // we don't like it to wrap content in our non-scroll direction.
-        final int width, height;
-        final int horizontalPadding = getPaddingLeft() + getPaddingRight();
-        final int verticalPadding = getPaddingTop() + getPaddingBottom();
-        if (mOrientation == VERTICAL) {
-            final int usedHeight = childrenBounds.height() + verticalPadding;
-            height = chooseSize(hSpec, usedHeight, getMinimumHeight());
-            width = chooseSize(wSpec, mSizePerSpan * mSpanCount + horizontalPadding,
-                    getMinimumWidth());
-        } else {
-            final int usedWidth = childrenBounds.width() + horizontalPadding;
-            width = chooseSize(wSpec, usedWidth, getMinimumWidth());
-            height = chooseSize(hSpec, mSizePerSpan * mSpanCount + verticalPadding,
-                    getMinimumHeight());
-        }
-        setMeasuredDimension(width, height);
-    }
-
-    @Override
-    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
-        onLayoutChildren(recycler, state, true);
-    }
-
-
-    private void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state,
-            boolean shouldCheckForGaps) {
-        final AnchorInfo anchorInfo = mAnchorInfo;
-        if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {
-            if (state.getItemCount() == 0) {
-                removeAndRecycleAllViews(recycler);
-                anchorInfo.reset();
-                return;
-            }
-        }
-
-        boolean recalculateAnchor = !anchorInfo.mValid || mPendingScrollPosition != NO_POSITION
-                || mPendingSavedState != null;
-        if (recalculateAnchor) {
-            anchorInfo.reset();
-            if (mPendingSavedState != null) {
-                applyPendingSavedState(anchorInfo);
-            } else {
-                resolveShouldLayoutReverse();
-                anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
-            }
-            updateAnchorInfoForLayout(state, anchorInfo);
-            anchorInfo.mValid = true;
-        }
-        if (mPendingSavedState == null && mPendingScrollPosition == NO_POSITION) {
-            if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd
-                    || isLayoutRTL() != mLastLayoutRTL) {
-                mLazySpanLookup.clear();
-                anchorInfo.mInvalidateOffsets = true;
-            }
-        }
-
-        if (getChildCount() > 0 && (mPendingSavedState == null
-                || mPendingSavedState.mSpanOffsetsSize < 1)) {
-            if (anchorInfo.mInvalidateOffsets) {
-                for (int i = 0; i < mSpanCount; i++) {
-                    // Scroll to position is set, clear.
-                    mSpans[i].clear();
-                    if (anchorInfo.mOffset != INVALID_OFFSET) {
-                        mSpans[i].setLine(anchorInfo.mOffset);
-                    }
-                }
-            } else {
-                if (recalculateAnchor || mAnchorInfo.mSpanReferenceLines == null) {
-                    for (int i = 0; i < mSpanCount; i++) {
-                        mSpans[i].cacheReferenceLineAndClear(mShouldReverseLayout,
-                                anchorInfo.mOffset);
-                    }
-                    mAnchorInfo.saveSpanReferenceLines(mSpans);
-                } else {
-                    for (int i = 0; i < mSpanCount; i++) {
-                        final Span span = mSpans[i];
-                        span.clear();
-                        span.setLine(mAnchorInfo.mSpanReferenceLines[i]);
-                    }
-                }
-            }
-        }
-        detachAndScrapAttachedViews(recycler);
-        mLayoutState.mRecycle = false;
-        mLaidOutInvalidFullSpan = false;
-        updateMeasureSpecs(mSecondaryOrientation.getTotalSpace());
-        updateLayoutState(anchorInfo.mPosition, state);
-        if (anchorInfo.mLayoutFromEnd) {
-            // Layout start.
-            setLayoutStateDirection(LAYOUT_START);
-            fill(recycler, mLayoutState, state);
-            // Layout end.
-            setLayoutStateDirection(LAYOUT_END);
-            mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection;
-            fill(recycler, mLayoutState, state);
-        } else {
-            // Layout end.
-            setLayoutStateDirection(LAYOUT_END);
-            fill(recycler, mLayoutState, state);
-            // Layout start.
-            setLayoutStateDirection(LAYOUT_START);
-            mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection;
-            fill(recycler, mLayoutState, state);
-        }
-
-        repositionToWrapContentIfNecessary();
-
-        if (getChildCount() > 0) {
-            if (mShouldReverseLayout) {
-                fixEndGap(recycler, state, true);
-                fixStartGap(recycler, state, false);
-            } else {
-                fixStartGap(recycler, state, true);
-                fixEndGap(recycler, state, false);
-            }
-        }
-        boolean hasGaps = false;
-        if (shouldCheckForGaps && !state.isPreLayout()) {
-            final boolean needToCheckForGaps = mGapStrategy != GAP_HANDLING_NONE
-                    && getChildCount() > 0
-                    && (mLaidOutInvalidFullSpan || hasGapsToFix() != null);
-            if (needToCheckForGaps) {
-                removeCallbacks(mCheckForGapsRunnable);
-                if (checkForGaps()) {
-                    hasGaps = true;
-                }
-            }
-        }
-        if (state.isPreLayout()) {
-            mAnchorInfo.reset();
-        }
-        mLastLayoutFromEnd = anchorInfo.mLayoutFromEnd;
-        mLastLayoutRTL = isLayoutRTL();
-        if (hasGaps) {
-            mAnchorInfo.reset();
-            onLayoutChildren(recycler, state, false);
-        }
-    }
-
-    @Override
-    public void onLayoutCompleted(RecyclerView.State state) {
-        super.onLayoutCompleted(state);
-        mPendingScrollPosition = NO_POSITION;
-        mPendingScrollPositionOffset = INVALID_OFFSET;
-        mPendingSavedState = null; // we don't need this anymore
-        mAnchorInfo.reset();
-    }
-
-    private void repositionToWrapContentIfNecessary() {
-        if (mSecondaryOrientation.getMode() == View.MeasureSpec.EXACTLY) {
-            return; // nothing to do
-        }
-        float maxSize = 0;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            float size = mSecondaryOrientation.getDecoratedMeasurement(child);
-            if (size < maxSize) {
-                continue;
-            }
-            LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
-            if (layoutParams.isFullSpan()) {
-                size = 1f * size / mSpanCount;
-            }
-            maxSize = Math.max(maxSize, size);
-        }
-        int before = mSizePerSpan;
-        int desired = Math.round(maxSize * mSpanCount);
-        if (mSecondaryOrientation.getMode() == View.MeasureSpec.AT_MOST) {
-            desired = Math.min(desired, mSecondaryOrientation.getTotalSpace());
-        }
-        updateMeasureSpecs(desired);
-        if (mSizePerSpan == before) {
-            return; // nothing has changed
-        }
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (lp.mFullSpan) {
-                continue;
-            }
-            if (isLayoutRTL() && mOrientation == VERTICAL) {
-                int newOffset = -(mSpanCount - 1 - lp.mSpan.mIndex) * mSizePerSpan;
-                int prevOffset = -(mSpanCount - 1 - lp.mSpan.mIndex) * before;
-                child.offsetLeftAndRight(newOffset - prevOffset);
-            } else {
-                int newOffset = lp.mSpan.mIndex * mSizePerSpan;
-                int prevOffset = lp.mSpan.mIndex * before;
-                if (mOrientation == VERTICAL) {
-                    child.offsetLeftAndRight(newOffset - prevOffset);
-                } else {
-                    child.offsetTopAndBottom(newOffset - prevOffset);
-                }
-            }
-        }
-    }
-
-    private void applyPendingSavedState(AnchorInfo anchorInfo) {
-        if (DEBUG) {
-            Log.d(TAG, "found saved state: " + mPendingSavedState);
-        }
-        if (mPendingSavedState.mSpanOffsetsSize > 0) {
-            if (mPendingSavedState.mSpanOffsetsSize == mSpanCount) {
-                for (int i = 0; i < mSpanCount; i++) {
-                    mSpans[i].clear();
-                    int line = mPendingSavedState.mSpanOffsets[i];
-                    if (line != Span.INVALID_LINE) {
-                        if (mPendingSavedState.mAnchorLayoutFromEnd) {
-                            line += mPrimaryOrientation.getEndAfterPadding();
-                        } else {
-                            line += mPrimaryOrientation.getStartAfterPadding();
-                        }
-                    }
-                    mSpans[i].setLine(line);
-                }
-            } else {
-                mPendingSavedState.invalidateSpanInfo();
-                mPendingSavedState.mAnchorPosition = mPendingSavedState.mVisibleAnchorPosition;
-            }
-        }
-        mLastLayoutRTL = mPendingSavedState.mLastLayoutRTL;
-        setReverseLayout(mPendingSavedState.mReverseLayout);
-        resolveShouldLayoutReverse();
-
-        if (mPendingSavedState.mAnchorPosition != NO_POSITION) {
-            mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
-            anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
-        } else {
-            anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
-        }
-        if (mPendingSavedState.mSpanLookupSize > 1) {
-            mLazySpanLookup.mData = mPendingSavedState.mSpanLookup;
-            mLazySpanLookup.mFullSpanItems = mPendingSavedState.mFullSpanItems;
-        }
-    }
-
-    void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) {
-        if (updateAnchorFromPendingData(state, anchorInfo)) {
-            return;
-        }
-        if (updateAnchorFromChildren(state, anchorInfo)) {
-            return;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Deciding anchor info from fresh state");
-        }
-        anchorInfo.assignCoordinateFromPadding();
-        anchorInfo.mPosition = 0;
-    }
-
-    private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) {
-        // We don't recycle views out of adapter order. This way, we can rely on the first or
-        // last child as the anchor position.
-        // Layout direction may change but we should select the child depending on the latest
-        // layout direction. Otherwise, we'll choose the wrong child.
-        anchorInfo.mPosition = mLastLayoutFromEnd
-                ? findLastReferenceChildPosition(state.getItemCount())
-                : findFirstReferenceChildPosition(state.getItemCount());
-        anchorInfo.mOffset = INVALID_OFFSET;
-        return true;
-    }
-
-    boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) {
-        // Validate scroll position if exists.
-        if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) {
-            return false;
-        }
-        // Validate it.
-        if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {
-            mPendingScrollPosition = NO_POSITION;
-            mPendingScrollPositionOffset = INVALID_OFFSET;
-            return false;
-        }
-
-        if (mPendingSavedState == null || mPendingSavedState.mAnchorPosition == NO_POSITION
-                || mPendingSavedState.mSpanOffsetsSize < 1) {
-            // If item is visible, make it fully visible.
-            final View child = findViewByPosition(mPendingScrollPosition);
-            if (child != null) {
-                // Use regular anchor position, offset according to pending offset and target
-                // child
-                anchorInfo.mPosition = mShouldReverseLayout ? getLastChildPosition()
-                        : getFirstChildPosition();
-                if (mPendingScrollPositionOffset != INVALID_OFFSET) {
-                    if (anchorInfo.mLayoutFromEnd) {
-                        final int target = mPrimaryOrientation.getEndAfterPadding()
-                                - mPendingScrollPositionOffset;
-                        anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedEnd(child);
-                    } else {
-                        final int target = mPrimaryOrientation.getStartAfterPadding()
-                                + mPendingScrollPositionOffset;
-                        anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedStart(child);
-                    }
-                    return true;
-                }
-
-                // no offset provided. Decide according to the child location
-                final int childSize = mPrimaryOrientation.getDecoratedMeasurement(child);
-                if (childSize > mPrimaryOrientation.getTotalSpace()) {
-                    // Item does not fit. Fix depending on layout direction.
-                    anchorInfo.mOffset = anchorInfo.mLayoutFromEnd
-                            ? mPrimaryOrientation.getEndAfterPadding()
-                            : mPrimaryOrientation.getStartAfterPadding();
-                    return true;
-                }
-
-                final int startGap = mPrimaryOrientation.getDecoratedStart(child)
-                        - mPrimaryOrientation.getStartAfterPadding();
-                if (startGap < 0) {
-                    anchorInfo.mOffset = -startGap;
-                    return true;
-                }
-                final int endGap = mPrimaryOrientation.getEndAfterPadding()
-                        - mPrimaryOrientation.getDecoratedEnd(child);
-                if (endGap < 0) {
-                    anchorInfo.mOffset = endGap;
-                    return true;
-                }
-                // child already visible. just layout as usual
-                anchorInfo.mOffset = INVALID_OFFSET;
-            } else {
-                // Child is not visible. Set anchor coordinate depending on in which direction
-                // child will be visible.
-                anchorInfo.mPosition = mPendingScrollPosition;
-                if (mPendingScrollPositionOffset == INVALID_OFFSET) {
-                    final int position = calculateScrollDirectionForPosition(
-                            anchorInfo.mPosition);
-                    anchorInfo.mLayoutFromEnd = position == LAYOUT_END;
-                    anchorInfo.assignCoordinateFromPadding();
-                } else {
-                    anchorInfo.assignCoordinateFromPadding(mPendingScrollPositionOffset);
-                }
-                anchorInfo.mInvalidateOffsets = true;
-            }
-        } else {
-            anchorInfo.mOffset = INVALID_OFFSET;
-            anchorInfo.mPosition = mPendingScrollPosition;
-        }
-        return true;
-    }
-
-    void updateMeasureSpecs(int totalSpace) {
-        mSizePerSpan = totalSpace / mSpanCount;
-        //noinspection ResourceType
-        mFullSizeSpec = View.MeasureSpec.makeMeasureSpec(
-                totalSpace, mSecondaryOrientation.getMode());
-    }
-
-    @Override
-    public boolean supportsPredictiveItemAnimations() {
-        return mPendingSavedState == null;
-    }
-
-    /**
-     * Returns the adapter position of the first visible view for each span.
-     * <p>
-     * Note that, this value is not affected by layout orientation or item order traversal.
-     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
-     * not in the layout.
-     * <p>
-     * If RecyclerView has item decorators, they will be considered in calculations as well.
-     * <p>
-     * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
-     * views are ignored in this method.
-     *
-     * @param into An array to put the results into. If you don't provide any, LayoutManager will
-     *             create a new one.
-     * @return The adapter position of the first visible item in each span. If a span does not have
-     * any items, {@link RecyclerView#NO_POSITION} is returned for that span.
-     * @see #findFirstCompletelyVisibleItemPositions(int[])
-     * @see #findLastVisibleItemPositions(int[])
-     */
-    public int[] findFirstVisibleItemPositions(int[] into) {
-        if (into == null) {
-            into = new int[mSpanCount];
-        } else if (into.length < mSpanCount) {
-            throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
-                    + " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
-        }
-        for (int i = 0; i < mSpanCount; i++) {
-            into[i] = mSpans[i].findFirstVisibleItemPosition();
-        }
-        return into;
-    }
-
-    /**
-     * Returns the adapter position of the first completely visible view for each span.
-     * <p>
-     * Note that, this value is not affected by layout orientation or item order traversal.
-     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
-     * not in the layout.
-     * <p>
-     * If RecyclerView has item decorators, they will be considered in calculations as well.
-     * <p>
-     * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
-     * views are ignored in this method.
-     *
-     * @param into An array to put the results into. If you don't provide any, LayoutManager will
-     *             create a new one.
-     * @return The adapter position of the first fully visible item in each span. If a span does
-     * not have any items, {@link RecyclerView#NO_POSITION} is returned for that span.
-     * @see #findFirstVisibleItemPositions(int[])
-     * @see #findLastCompletelyVisibleItemPositions(int[])
-     */
-    public int[] findFirstCompletelyVisibleItemPositions(int[] into) {
-        if (into == null) {
-            into = new int[mSpanCount];
-        } else if (into.length < mSpanCount) {
-            throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
-                    + " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
-        }
-        for (int i = 0; i < mSpanCount; i++) {
-            into[i] = mSpans[i].findFirstCompletelyVisibleItemPosition();
-        }
-        return into;
-    }
-
-    /**
-     * Returns the adapter position of the last visible view for each span.
-     * <p>
-     * Note that, this value is not affected by layout orientation or item order traversal.
-     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
-     * not in the layout.
-     * <p>
-     * If RecyclerView has item decorators, they will be considered in calculations as well.
-     * <p>
-     * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
-     * views are ignored in this method.
-     *
-     * @param into An array to put the results into. If you don't provide any, LayoutManager will
-     *             create a new one.
-     * @return The adapter position of the last visible item in each span. If a span does not have
-     * any items, {@link RecyclerView#NO_POSITION} is returned for that span.
-     * @see #findLastCompletelyVisibleItemPositions(int[])
-     * @see #findFirstVisibleItemPositions(int[])
-     */
-    public int[] findLastVisibleItemPositions(int[] into) {
-        if (into == null) {
-            into = new int[mSpanCount];
-        } else if (into.length < mSpanCount) {
-            throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
-                    + " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
-        }
-        for (int i = 0; i < mSpanCount; i++) {
-            into[i] = mSpans[i].findLastVisibleItemPosition();
-        }
-        return into;
-    }
-
-    /**
-     * Returns the adapter position of the last completely visible view for each span.
-     * <p>
-     * Note that, this value is not affected by layout orientation or item order traversal.
-     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
-     * not in the layout.
-     * <p>
-     * If RecyclerView has item decorators, they will be considered in calculations as well.
-     * <p>
-     * StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
-     * views are ignored in this method.
-     *
-     * @param into An array to put the results into. If you don't provide any, LayoutManager will
-     *             create a new one.
-     * @return The adapter position of the last fully visible item in each span. If a span does not
-     * have any items, {@link RecyclerView#NO_POSITION} is returned for that span.
-     * @see #findFirstCompletelyVisibleItemPositions(int[])
-     * @see #findLastVisibleItemPositions(int[])
-     */
-    public int[] findLastCompletelyVisibleItemPositions(int[] into) {
-        if (into == null) {
-            into = new int[mSpanCount];
-        } else if (into.length < mSpanCount) {
-            throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
-                    + " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
-        }
-        for (int i = 0; i < mSpanCount; i++) {
-            into[i] = mSpans[i].findLastCompletelyVisibleItemPosition();
-        }
-        return into;
-    }
-
-    @Override
-    public int computeHorizontalScrollOffset(RecyclerView.State state) {
-        return computeScrollOffset(state);
-    }
-
-    private int computeScrollOffset(RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return 0;
-        }
-        return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation,
-                findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled),
-                findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled),
-                this, mSmoothScrollbarEnabled, mShouldReverseLayout);
-    }
-
-    @Override
-    public int computeVerticalScrollOffset(RecyclerView.State state) {
-        return computeScrollOffset(state);
-    }
-
-    @Override
-    public int computeHorizontalScrollExtent(RecyclerView.State state) {
-        return computeScrollExtent(state);
-    }
-
-    private int computeScrollExtent(RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return 0;
-        }
-        return ScrollbarHelper.computeScrollExtent(state, mPrimaryOrientation,
-                findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled),
-                findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled),
-                this, mSmoothScrollbarEnabled);
-    }
-
-    @Override
-    public int computeVerticalScrollExtent(RecyclerView.State state) {
-        return computeScrollExtent(state);
-    }
-
-    @Override
-    public int computeHorizontalScrollRange(RecyclerView.State state) {
-        return computeScrollRange(state);
-    }
-
-    private int computeScrollRange(RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return 0;
-        }
-        return ScrollbarHelper.computeScrollRange(state, mPrimaryOrientation,
-                findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled),
-                findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled),
-                this, mSmoothScrollbarEnabled);
-    }
-
-    @Override
-    public int computeVerticalScrollRange(RecyclerView.State state) {
-        return computeScrollRange(state);
-    }
-
-    private void measureChildWithDecorationsAndMargin(View child, LayoutParams lp,
-            boolean alreadyMeasured) {
-        if (lp.mFullSpan) {
-            if (mOrientation == VERTICAL) {
-                measureChildWithDecorationsAndMargin(child, mFullSizeSpec,
-                        getChildMeasureSpec(getHeight(), getHeightMode(), 0, lp.height, true),
-                        alreadyMeasured);
-            } else {
-                measureChildWithDecorationsAndMargin(child,
-                        getChildMeasureSpec(getWidth(), getWidthMode(), 0, lp.width, true),
-                        mFullSizeSpec, alreadyMeasured);
-            }
-        } else {
-            if (mOrientation == VERTICAL) {
-                measureChildWithDecorationsAndMargin(child,
-                        getChildMeasureSpec(mSizePerSpan, getWidthMode(), 0, lp.width, false),
-                        getChildMeasureSpec(getHeight(), getHeightMode(), 0, lp.height, true),
-                        alreadyMeasured);
-            } else {
-                measureChildWithDecorationsAndMargin(child,
-                        getChildMeasureSpec(getWidth(), getWidthMode(), 0, lp.width, true),
-                        getChildMeasureSpec(mSizePerSpan, getHeightMode(), 0, lp.height, false),
-                        alreadyMeasured);
-            }
-        }
-    }
-
-    private void measureChildWithDecorationsAndMargin(View child, int widthSpec,
-            int heightSpec, boolean alreadyMeasured) {
-        calculateItemDecorationsForChild(child, mTmpRect);
-        LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mTmpRect.left,
-                lp.rightMargin + mTmpRect.right);
-        heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mTmpRect.top,
-                lp.bottomMargin + mTmpRect.bottom);
-        final boolean measure = alreadyMeasured
-                ? shouldReMeasureChild(child, widthSpec, heightSpec, lp)
-                : shouldMeasureChild(child, widthSpec, heightSpec, lp);
-        if (measure) {
-            child.measure(widthSpec, heightSpec);
-        }
-
-    }
-
-    private int updateSpecWithExtra(int spec, int startInset, int endInset) {
-        if (startInset == 0 && endInset == 0) {
-            return spec;
-        }
-        final int mode = View.MeasureSpec.getMode(spec);
-        if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
-            return View.MeasureSpec.makeMeasureSpec(
-                    Math.max(0, View.MeasureSpec.getSize(spec) - startInset - endInset), mode);
-        }
-        return spec;
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        if (state instanceof SavedState) {
-            mPendingSavedState = (SavedState) state;
-            requestLayout();
-        } else if (DEBUG) {
-            Log.d(TAG, "invalid saved state class");
-        }
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        if (mPendingSavedState != null) {
-            return new SavedState(mPendingSavedState);
-        }
-        SavedState state = new SavedState();
-        state.mReverseLayout = mReverseLayout;
-        state.mAnchorLayoutFromEnd = mLastLayoutFromEnd;
-        state.mLastLayoutRTL = mLastLayoutRTL;
-
-        if (mLazySpanLookup != null && mLazySpanLookup.mData != null) {
-            state.mSpanLookup = mLazySpanLookup.mData;
-            state.mSpanLookupSize = state.mSpanLookup.length;
-            state.mFullSpanItems = mLazySpanLookup.mFullSpanItems;
-        } else {
-            state.mSpanLookupSize = 0;
-        }
-
-        if (getChildCount() > 0) {
-            state.mAnchorPosition = mLastLayoutFromEnd ? getLastChildPosition()
-                    : getFirstChildPosition();
-            state.mVisibleAnchorPosition = findFirstVisibleItemPositionInt();
-            state.mSpanOffsetsSize = mSpanCount;
-            state.mSpanOffsets = new int[mSpanCount];
-            for (int i = 0; i < mSpanCount; i++) {
-                int line;
-                if (mLastLayoutFromEnd) {
-                    line = mSpans[i].getEndLine(Span.INVALID_LINE);
-                    if (line != Span.INVALID_LINE) {
-                        line -= mPrimaryOrientation.getEndAfterPadding();
-                    }
-                } else {
-                    line = mSpans[i].getStartLine(Span.INVALID_LINE);
-                    if (line != Span.INVALID_LINE) {
-                        line -= mPrimaryOrientation.getStartAfterPadding();
-                    }
-                }
-                state.mSpanOffsets[i] = line;
-            }
-        } else {
-            state.mAnchorPosition = NO_POSITION;
-            state.mVisibleAnchorPosition = NO_POSITION;
-            state.mSpanOffsetsSize = 0;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "saved state:\n" + state);
-        }
-        return state;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
-            RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
-        ViewGroup.LayoutParams lp = host.getLayoutParams();
-        if (!(lp instanceof LayoutParams)) {
-            super.onInitializeAccessibilityNodeInfoForItem(host, info);
-            return;
-        }
-        LayoutParams sglp = (LayoutParams) lp;
-        if (mOrientation == HORIZONTAL) {
-            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-                    sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1,
-                    -1, -1,
-                    sglp.mFullSpan, false));
-        } else { // VERTICAL
-            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-                    -1, -1,
-                    sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1,
-                    sglp.mFullSpan, false));
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        if (getChildCount() > 0) {
-            final View start = findFirstVisibleItemClosestToStart(false);
-            final View end = findFirstVisibleItemClosestToEnd(false);
-            if (start == null || end == null) {
-                return;
-            }
-            final int startPos = getPosition(start);
-            final int endPos = getPosition(end);
-            if (startPos < endPos) {
-                event.setFromIndex(startPos);
-                event.setToIndex(endPos);
-            } else {
-                event.setFromIndex(endPos);
-                event.setToIndex(startPos);
-            }
-        }
-    }
-
-    /**
-     * Finds the first fully visible child to be used as an anchor child if span count changes when
-     * state is restored. If no children is fully visible, returns a partially visible child instead
-     * of returning null.
-     */
-    int findFirstVisibleItemPositionInt() {
-        final View first = mShouldReverseLayout ? findFirstVisibleItemClosestToEnd(true) :
-                findFirstVisibleItemClosestToStart(true);
-        return first == null ? NO_POSITION : getPosition(first);
-    }
-
-    @Override
-    public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == HORIZONTAL) {
-            return mSpanCount;
-        }
-        return super.getRowCountForAccessibility(recycler, state);
-    }
-
-    @Override
-    public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (mOrientation == VERTICAL) {
-            return mSpanCount;
-        }
-        return super.getColumnCountForAccessibility(recycler, state);
-    }
-
-    /**
-     * This is for internal use. Not necessarily the child closest to start but the first child
-     * we find that matches the criteria.
-     * This method does not do any sorting based on child's start coordinate, instead, it uses
-     * children order.
-     */
-    View findFirstVisibleItemClosestToStart(boolean fullyVisible) {
-        final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
-        final int boundsEnd = mPrimaryOrientation.getEndAfterPadding();
-        final int limit = getChildCount();
-        View partiallyVisible = null;
-        for (int i = 0; i < limit; i++) {
-            final View child = getChildAt(i);
-            final int childStart = mPrimaryOrientation.getDecoratedStart(child);
-            final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
-            if (childEnd <= boundsStart || childStart >= boundsEnd) {
-                continue; // not visible at all
-            }
-            if (childStart >= boundsStart || !fullyVisible) {
-                // when checking for start, it is enough even if part of the child's top is visible
-                // as long as fully visible is not requested.
-                return child;
-            }
-            if (partiallyVisible == null) {
-                partiallyVisible = child;
-            }
-        }
-        return partiallyVisible;
-    }
-
-    /**
-     * This is for internal use. Not necessarily the child closest to bottom but the first child
-     * we find that matches the criteria.
-     * This method does not do any sorting based on child's end coordinate, instead, it uses
-     * children order.
-     */
-    View findFirstVisibleItemClosestToEnd(boolean fullyVisible) {
-        final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
-        final int boundsEnd = mPrimaryOrientation.getEndAfterPadding();
-        View partiallyVisible = null;
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final View child = getChildAt(i);
-            final int childStart = mPrimaryOrientation.getDecoratedStart(child);
-            final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
-            if (childEnd <= boundsStart || childStart >= boundsEnd) {
-                continue; // not visible at all
-            }
-            if (childEnd <= boundsEnd || !fullyVisible) {
-                // when checking for end, it is enough even if part of the child's bottom is visible
-                // as long as fully visible is not requested.
-                return child;
-            }
-            if (partiallyVisible == null) {
-                partiallyVisible = child;
-            }
-        }
-        return partiallyVisible;
-    }
-
-    private void fixEndGap(RecyclerView.Recycler recycler, RecyclerView.State state,
-            boolean canOffsetChildren) {
-        final int maxEndLine = getMaxEnd(Integer.MIN_VALUE);
-        if (maxEndLine == Integer.MIN_VALUE) {
-            return;
-        }
-        int gap = mPrimaryOrientation.getEndAfterPadding() - maxEndLine;
-        int fixOffset;
-        if (gap > 0) {
-            fixOffset = -scrollBy(-gap, recycler, state);
-        } else {
-            return; // nothing to fix
-        }
-        gap -= fixOffset;
-        if (canOffsetChildren && gap > 0) {
-            mPrimaryOrientation.offsetChildren(gap);
-        }
-    }
-
-    private void fixStartGap(RecyclerView.Recycler recycler, RecyclerView.State state,
-            boolean canOffsetChildren) {
-        final int minStartLine = getMinStart(Integer.MAX_VALUE);
-        if (minStartLine == Integer.MAX_VALUE) {
-            return;
-        }
-        int gap = minStartLine - mPrimaryOrientation.getStartAfterPadding();
-        int fixOffset;
-        if (gap > 0) {
-            fixOffset = scrollBy(gap, recycler, state);
-        } else {
-            return; // nothing to fix
-        }
-        gap -= fixOffset;
-        if (canOffsetChildren && gap > 0) {
-            mPrimaryOrientation.offsetChildren(-gap);
-        }
-    }
-
-    private void updateLayoutState(int anchorPosition, RecyclerView.State state) {
-        mLayoutState.mAvailable = 0;
-        mLayoutState.mCurrentPosition = anchorPosition;
-        int startExtra = 0;
-        int endExtra = 0;
-        if (isSmoothScrolling()) {
-            final int targetPos = state.getTargetScrollPosition();
-            if (targetPos != NO_POSITION) {
-                if (mShouldReverseLayout == targetPos < anchorPosition) {
-                    endExtra = mPrimaryOrientation.getTotalSpace();
-                } else {
-                    startExtra = mPrimaryOrientation.getTotalSpace();
-                }
-            }
-        }
-
-        // Line of the furthest row.
-        final boolean clipToPadding = getClipToPadding();
-        if (clipToPadding) {
-            mLayoutState.mStartLine = mPrimaryOrientation.getStartAfterPadding() - startExtra;
-            mLayoutState.mEndLine = mPrimaryOrientation.getEndAfterPadding() + endExtra;
-        } else {
-            mLayoutState.mEndLine = mPrimaryOrientation.getEnd() + endExtra;
-            mLayoutState.mStartLine = -startExtra;
-        }
-        mLayoutState.mStopInFocusable = false;
-        mLayoutState.mRecycle = true;
-        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;
-    }
-
-    @Override
-    public void offsetChildrenHorizontal(int dx) {
-        super.offsetChildrenHorizontal(dx);
-        for (int i = 0; i < mSpanCount; i++) {
-            mSpans[i].onOffset(dx);
-        }
-    }
-
-    @Override
-    public void offsetChildrenVertical(int dy) {
-        super.offsetChildrenVertical(dy);
-        for (int i = 0; i < mSpanCount; i++) {
-            mSpans[i].onOffset(dy);
-        }
-    }
-
-    @Override
-    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
-        handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.REMOVE);
-    }
-
-    @Override
-    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
-        handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.ADD);
-    }
-
-    @Override
-    public void onItemsChanged(RecyclerView recyclerView) {
-        mLazySpanLookup.clear();
-        requestLayout();
-    }
-
-    @Override
-    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
-        handleUpdate(from, to, AdapterHelper.UpdateOp.MOVE);
-    }
-
-    @Override
-    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
-            Object payload) {
-        handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.UPDATE);
-    }
-
-    /**
-     * Checks whether it should invalidate span assignments in response to an adapter change.
-     */
-    private void handleUpdate(int positionStart, int itemCountOrToPosition, int cmd) {
-        int minPosition = mShouldReverseLayout ? getLastChildPosition() : getFirstChildPosition();
-        final int affectedRangeEnd; // exclusive
-        final int affectedRangeStart; // inclusive
-
-        if (cmd == AdapterHelper.UpdateOp.MOVE) {
-            if (positionStart < itemCountOrToPosition) {
-                affectedRangeEnd = itemCountOrToPosition + 1;
-                affectedRangeStart = positionStart;
-            } else {
-                affectedRangeEnd = positionStart + 1;
-                affectedRangeStart = itemCountOrToPosition;
-            }
-        } else {
-            affectedRangeStart = positionStart;
-            affectedRangeEnd = positionStart + itemCountOrToPosition;
-        }
-
-        mLazySpanLookup.invalidateAfter(affectedRangeStart);
-        switch (cmd) {
-            case AdapterHelper.UpdateOp.ADD:
-                mLazySpanLookup.offsetForAddition(positionStart, itemCountOrToPosition);
-                break;
-            case AdapterHelper.UpdateOp.REMOVE:
-                mLazySpanLookup.offsetForRemoval(positionStart, itemCountOrToPosition);
-                break;
-            case AdapterHelper.UpdateOp.MOVE:
-                // TODO optimize
-                mLazySpanLookup.offsetForRemoval(positionStart, 1);
-                mLazySpanLookup.offsetForAddition(itemCountOrToPosition, 1);
-                break;
-        }
-
-        if (affectedRangeEnd <= minPosition) {
-            return;
-        }
-
-        int maxPosition = mShouldReverseLayout ? getFirstChildPosition() : getLastChildPosition();
-        if (affectedRangeStart <= maxPosition) {
-            requestLayout();
-        }
-    }
-
-    private int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
-            RecyclerView.State state) {
-        mRemainingSpans.set(0, mSpanCount, true);
-        // The target position we are trying to reach.
-        final int targetLine;
-
-        // Line of the furthest row.
-        if (mLayoutState.mInfinite) {
-            if (layoutState.mLayoutDirection == LAYOUT_END) {
-                targetLine = Integer.MAX_VALUE;
-            } else { // LAYOUT_START
-                targetLine = Integer.MIN_VALUE;
-            }
-        } else {
-            if (layoutState.mLayoutDirection == LAYOUT_END) {
-                targetLine = layoutState.mEndLine + layoutState.mAvailable;
-            } else { // LAYOUT_START
-                targetLine = layoutState.mStartLine - layoutState.mAvailable;
-            }
-        }
-
-        updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
-        if (DEBUG) {
-            Log.d(TAG, "FILLING targetLine: " + targetLine + ","
-                    + "remaining spans:" + mRemainingSpans + ", state: " + layoutState);
-        }
-
-        // the default coordinate to add new view.
-        final int defaultNewViewLine = mShouldReverseLayout
-                ? mPrimaryOrientation.getEndAfterPadding()
-                : mPrimaryOrientation.getStartAfterPadding();
-        boolean added = false;
-        while (layoutState.hasMore(state)
-                && (mLayoutState.mInfinite || !mRemainingSpans.isEmpty())) {
-            View view = layoutState.next(recycler);
-            LayoutParams lp = ((LayoutParams) view.getLayoutParams());
-            final int position = lp.getViewLayoutPosition();
-            final int spanIndex = mLazySpanLookup.getSpan(position);
-            Span currentSpan;
-            final boolean assignSpan = spanIndex == LayoutParams.INVALID_SPAN_ID;
-            if (assignSpan) {
-                currentSpan = lp.mFullSpan ? mSpans[0] : getNextSpan(layoutState);
-                mLazySpanLookup.setSpan(position, currentSpan);
-                if (DEBUG) {
-                    Log.d(TAG, "assigned " + currentSpan.mIndex + " for " + position);
-                }
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "using " + spanIndex + " for pos " + position);
-                }
-                currentSpan = mSpans[spanIndex];
-            }
-            // assign span before measuring so that item decorators can get updated span index
-            lp.mSpan = currentSpan;
-            if (layoutState.mLayoutDirection == LAYOUT_END) {
-                addView(view);
-            } else {
-                addView(view, 0);
-            }
-            measureChildWithDecorationsAndMargin(view, lp, false);
-
-            final int start;
-            final int end;
-            if (layoutState.mLayoutDirection == LAYOUT_END) {
-                start = lp.mFullSpan ? getMaxEnd(defaultNewViewLine)
-                        : currentSpan.getEndLine(defaultNewViewLine);
-                end = start + mPrimaryOrientation.getDecoratedMeasurement(view);
-                if (assignSpan && lp.mFullSpan) {
-                    LazySpanLookup.FullSpanItem fullSpanItem;
-                    fullSpanItem = createFullSpanItemFromEnd(start);
-                    fullSpanItem.mGapDir = LAYOUT_START;
-                    fullSpanItem.mPosition = position;
-                    mLazySpanLookup.addFullSpanItem(fullSpanItem);
-                }
-            } else {
-                end = lp.mFullSpan ? getMinStart(defaultNewViewLine)
-                        : currentSpan.getStartLine(defaultNewViewLine);
-                start = end - mPrimaryOrientation.getDecoratedMeasurement(view);
-                if (assignSpan && lp.mFullSpan) {
-                    LazySpanLookup.FullSpanItem fullSpanItem;
-                    fullSpanItem = createFullSpanItemFromStart(end);
-                    fullSpanItem.mGapDir = LAYOUT_END;
-                    fullSpanItem.mPosition = position;
-                    mLazySpanLookup.addFullSpanItem(fullSpanItem);
-                }
-            }
-
-            // check if this item may create gaps in the future
-            if (lp.mFullSpan && layoutState.mItemDirection == ITEM_DIRECTION_HEAD) {
-                if (assignSpan) {
-                    mLaidOutInvalidFullSpan = true;
-                } else {
-                    final boolean hasInvalidGap;
-                    if (layoutState.mLayoutDirection == LAYOUT_END) {
-                        hasInvalidGap = !areAllEndsEqual();
-                    } else { // layoutState.mLayoutDirection == LAYOUT_START
-                        hasInvalidGap = !areAllStartsEqual();
-                    }
-                    if (hasInvalidGap) {
-                        final LazySpanLookup.FullSpanItem fullSpanItem = mLazySpanLookup
-                                .getFullSpanItem(position);
-                        if (fullSpanItem != null) {
-                            fullSpanItem.mHasUnwantedGapAfter = true;
-                        }
-                        mLaidOutInvalidFullSpan = true;
-                    }
-                }
-            }
-            attachViewToSpans(view, lp, layoutState);
-            final int otherStart;
-            final int otherEnd;
-            if (isLayoutRTL() && mOrientation == VERTICAL) {
-                otherEnd = lp.mFullSpan ? mSecondaryOrientation.getEndAfterPadding() :
-                        mSecondaryOrientation.getEndAfterPadding()
-                                - (mSpanCount - 1 - currentSpan.mIndex) * mSizePerSpan;
-                otherStart = otherEnd - mSecondaryOrientation.getDecoratedMeasurement(view);
-            } else {
-                otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding()
-                        : currentSpan.mIndex * mSizePerSpan
-                                + mSecondaryOrientation.getStartAfterPadding();
-                otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view);
-            }
-
-            if (mOrientation == VERTICAL) {
-                layoutDecoratedWithMargins(view, otherStart, start, otherEnd, end);
-            } else {
-                layoutDecoratedWithMargins(view, start, otherStart, end, otherEnd);
-            }
-
-            if (lp.mFullSpan) {
-                updateAllRemainingSpans(mLayoutState.mLayoutDirection, targetLine);
-            } else {
-                updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
-            }
-            recycle(recycler, mLayoutState);
-            if (mLayoutState.mStopInFocusable && view.hasFocusable()) {
-                if (lp.mFullSpan) {
-                    mRemainingSpans.clear();
-                } else {
-                    mRemainingSpans.set(currentSpan.mIndex, false);
-                }
-            }
-            added = true;
-        }
-        if (!added) {
-            recycle(recycler, mLayoutState);
-        }
-        final int diff;
-        if (mLayoutState.mLayoutDirection == LAYOUT_START) {
-            final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding());
-            diff = mPrimaryOrientation.getStartAfterPadding() - minStart;
-        } else {
-            final int maxEnd = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
-            diff = maxEnd - mPrimaryOrientation.getEndAfterPadding();
-        }
-        return diff > 0 ? Math.min(layoutState.mAvailable, diff) : 0;
-    }
-
-    private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) {
-        LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem();
-        fsi.mGapPerSpan = new int[mSpanCount];
-        for (int i = 0; i < mSpanCount; i++) {
-            fsi.mGapPerSpan[i] = newItemTop - mSpans[i].getEndLine(newItemTop);
-        }
-        return fsi;
-    }
-
-    private LazySpanLookup.FullSpanItem createFullSpanItemFromStart(int newItemBottom) {
-        LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem();
-        fsi.mGapPerSpan = new int[mSpanCount];
-        for (int i = 0; i < mSpanCount; i++) {
-            fsi.mGapPerSpan[i] = mSpans[i].getStartLine(newItemBottom) - newItemBottom;
-        }
-        return fsi;
-    }
-
-    private void attachViewToSpans(View view, LayoutParams lp, LayoutState layoutState) {
-        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_END) {
-            if (lp.mFullSpan) {
-                appendViewToAllSpans(view);
-            } else {
-                lp.mSpan.appendToSpan(view);
-            }
-        } else {
-            if (lp.mFullSpan) {
-                prependViewToAllSpans(view);
-            } else {
-                lp.mSpan.prependToSpan(view);
-            }
-        }
-    }
-
-    private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState) {
-        if (!layoutState.mRecycle || layoutState.mInfinite) {
-            return;
-        }
-        if (layoutState.mAvailable == 0) {
-            // easy, recycle line is still valid
-            if (layoutState.mLayoutDirection == LAYOUT_START) {
-                recycleFromEnd(recycler, layoutState.mEndLine);
-            } else {
-                recycleFromStart(recycler, layoutState.mStartLine);
-            }
-        } else {
-            // scrolling case, recycle line can be shifted by how much space we could cover
-            // by adding new views
-            if (layoutState.mLayoutDirection == LAYOUT_START) {
-                // calculate recycle line
-                int scrolled = layoutState.mStartLine - getMaxStart(layoutState.mStartLine);
-                final int line;
-                if (scrolled < 0) {
-                    line = layoutState.mEndLine;
-                } else {
-                    line = layoutState.mEndLine - Math.min(scrolled, layoutState.mAvailable);
-                }
-                recycleFromEnd(recycler, line);
-            } else {
-                // calculate recycle line
-                int scrolled = getMinEnd(layoutState.mEndLine) - layoutState.mEndLine;
-                final int line;
-                if (scrolled < 0) {
-                    line = layoutState.mStartLine;
-                } else {
-                    line = layoutState.mStartLine + Math.min(scrolled, layoutState.mAvailable);
-                }
-                recycleFromStart(recycler, line);
-            }
-        }
-
-    }
-
-    private void appendViewToAllSpans(View view) {
-        // traverse in reverse so that we end up assigning full span items to 0
-        for (int i = mSpanCount - 1; i >= 0; i--) {
-            mSpans[i].appendToSpan(view);
-        }
-    }
-
-    private void prependViewToAllSpans(View view) {
-        // traverse in reverse so that we end up assigning full span items to 0
-        for (int i = mSpanCount - 1; i >= 0; i--) {
-            mSpans[i].prependToSpan(view);
-        }
-    }
-
-    private void updateAllRemainingSpans(int layoutDir, int targetLine) {
-        for (int i = 0; i < mSpanCount; i++) {
-            if (mSpans[i].mViews.isEmpty()) {
-                continue;
-            }
-            updateRemainingSpans(mSpans[i], layoutDir, targetLine);
-        }
-    }
-
-    private void updateRemainingSpans(Span span, int layoutDir, int targetLine) {
-        final int deletedSize = span.getDeletedSize();
-        if (layoutDir == LAYOUT_START) {
-            final int line = span.getStartLine();
-            if (line + deletedSize <= targetLine) {
-                mRemainingSpans.set(span.mIndex, false);
-            }
-        } else {
-            final int line = span.getEndLine();
-            if (line - deletedSize >= targetLine) {
-                mRemainingSpans.set(span.mIndex, false);
-            }
-        }
-    }
-
-    private int getMaxStart(int def) {
-        int maxStart = mSpans[0].getStartLine(def);
-        for (int i = 1; i < mSpanCount; i++) {
-            final int spanStart = mSpans[i].getStartLine(def);
-            if (spanStart > maxStart) {
-                maxStart = spanStart;
-            }
-        }
-        return maxStart;
-    }
-
-    private int getMinStart(int def) {
-        int minStart = mSpans[0].getStartLine(def);
-        for (int i = 1; i < mSpanCount; i++) {
-            final int spanStart = mSpans[i].getStartLine(def);
-            if (spanStart < minStart) {
-                minStart = spanStart;
-            }
-        }
-        return minStart;
-    }
-
-    boolean areAllEndsEqual() {
-        int end = mSpans[0].getEndLine(Span.INVALID_LINE);
-        for (int i = 1; i < mSpanCount; i++) {
-            if (mSpans[i].getEndLine(Span.INVALID_LINE) != end) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    boolean areAllStartsEqual() {
-        int start = mSpans[0].getStartLine(Span.INVALID_LINE);
-        for (int i = 1; i < mSpanCount; i++) {
-            if (mSpans[i].getStartLine(Span.INVALID_LINE) != start) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private int getMaxEnd(int def) {
-        int maxEnd = mSpans[0].getEndLine(def);
-        for (int i = 1; i < mSpanCount; i++) {
-            final int spanEnd = mSpans[i].getEndLine(def);
-            if (spanEnd > maxEnd) {
-                maxEnd = spanEnd;
-            }
-        }
-        return maxEnd;
-    }
-
-    private int getMinEnd(int def) {
-        int minEnd = mSpans[0].getEndLine(def);
-        for (int i = 1; i < mSpanCount; i++) {
-            final int spanEnd = mSpans[i].getEndLine(def);
-            if (spanEnd < minEnd) {
-                minEnd = spanEnd;
-            }
-        }
-        return minEnd;
-    }
-
-    private void recycleFromStart(RecyclerView.Recycler recycler, int line) {
-        while (getChildCount() > 0) {
-            View child = getChildAt(0);
-            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) {
-                    for (int j = 0; j < mSpanCount; j++) {
-                        if (mSpans[j].mViews.size() == 1) {
-                            return;
-                        }
-                    }
-                    for (int j = 0; j < mSpanCount; j++) {
-                        mSpans[j].popStart();
-                    }
-                } else {
-                    if (lp.mSpan.mViews.size() == 1) {
-                        return;
-                    }
-                    lp.mSpan.popStart();
-                }
-                removeAndRecycleView(child, recycler);
-            } else {
-                return; // done
-            }
-        }
-    }
-
-    private void recycleFromEnd(RecyclerView.Recycler recycler, int line) {
-        final int childCount = getChildCount();
-        int i;
-        for (i = childCount - 1; i >= 0; i--) {
-            View child = getChildAt(i);
-            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) {
-                    for (int j = 0; j < mSpanCount; j++) {
-                        if (mSpans[j].mViews.size() == 1) {
-                            return;
-                        }
-                    }
-                    for (int j = 0; j < mSpanCount; j++) {
-                        mSpans[j].popEnd();
-                    }
-                } else {
-                    if (lp.mSpan.mViews.size() == 1) {
-                        return;
-                    }
-                    lp.mSpan.popEnd();
-                }
-                removeAndRecycleView(child, recycler);
-            } else {
-                return; // done
-            }
-        }
-    }
-
-    /**
-     * @return True if last span is the first one we want to fill
-     */
-    private boolean preferLastSpan(int layoutDir) {
-        if (mOrientation == HORIZONTAL) {
-            return (layoutDir == LAYOUT_START) != mShouldReverseLayout;
-        }
-        return ((layoutDir == LAYOUT_START) == mShouldReverseLayout) == isLayoutRTL();
-    }
-
-    /**
-     * Finds the span for the next view.
-     */
-    private Span getNextSpan(LayoutState layoutState) {
-        final boolean preferLastSpan = preferLastSpan(layoutState.mLayoutDirection);
-        final int startIndex, endIndex, diff;
-        if (preferLastSpan) {
-            startIndex = mSpanCount - 1;
-            endIndex = -1;
-            diff = -1;
-        } else {
-            startIndex = 0;
-            endIndex = mSpanCount;
-            diff = 1;
-        }
-        if (layoutState.mLayoutDirection == LAYOUT_END) {
-            Span min = null;
-            int minLine = Integer.MAX_VALUE;
-            final int defaultLine = mPrimaryOrientation.getStartAfterPadding();
-            for (int i = startIndex; i != endIndex; i += diff) {
-                final Span other = mSpans[i];
-                int otherLine = other.getEndLine(defaultLine);
-                if (otherLine < minLine) {
-                    min = other;
-                    minLine = otherLine;
-                }
-            }
-            return min;
-        } else {
-            Span max = null;
-            int maxLine = Integer.MIN_VALUE;
-            final int defaultLine = mPrimaryOrientation.getEndAfterPadding();
-            for (int i = startIndex; i != endIndex; i += diff) {
-                final Span other = mSpans[i];
-                int otherLine = other.getStartLine(defaultLine);
-                if (otherLine > maxLine) {
-                    max = other;
-                    maxLine = otherLine;
-                }
-            }
-            return max;
-        }
-    }
-
-    @Override
-    public boolean canScrollVertically() {
-        return mOrientation == VERTICAL;
-    }
-
-    @Override
-    public boolean canScrollHorizontally() {
-        return mOrientation == HORIZONTAL;
-    }
-
-    @Override
-    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return scrollBy(dx, recycler, state);
-    }
-
-    @Override
-    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        return scrollBy(dy, recycler, state);
-    }
-
-    private int calculateScrollDirectionForPosition(int position) {
-        if (getChildCount() == 0) {
-            return mShouldReverseLayout ? LAYOUT_END : LAYOUT_START;
-        }
-        final int firstChildPos = getFirstChildPosition();
-        return position < firstChildPos != mShouldReverseLayout ? LAYOUT_START : LAYOUT_END;
-    }
-
-    @Override
-    public PointF computeScrollVectorForPosition(int targetPosition) {
-        final int direction = calculateScrollDirectionForPosition(targetPosition);
-        PointF outVector = new PointF();
-        if (direction == 0) {
-            return null;
-        }
-        if (mOrientation == HORIZONTAL) {
-            outVector.x = direction;
-            outVector.y = 0;
-        } else {
-            outVector.x = 0;
-            outVector.y = direction;
-        }
-        return outVector;
-    }
-
-    @Override
-    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
-            int position) {
-        LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext());
-        scroller.setTargetPosition(position);
-        startSmoothScroll(scroller);
-    }
-
-    @Override
-    public void scrollToPosition(int position) {
-        if (mPendingSavedState != null && mPendingSavedState.mAnchorPosition != position) {
-            mPendingSavedState.invalidateAnchorPositionInfo();
-        }
-        mPendingScrollPosition = position;
-        mPendingScrollPositionOffset = INVALID_OFFSET;
-        requestLayout();
-    }
-
-    /**
-     * Scroll to the specified adapter position with the given offset from layout start.
-     * <p>
-     * Note that scroll position change will not be reflected until the next layout call.
-     * <p>
-     * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
-     *
-     * @param position Index (starting at 0) of the reference item.
-     * @param offset   The distance (in pixels) between the start edge of the item view and
-     *                 start edge of the RecyclerView.
-     * @see #setReverseLayout(boolean)
-     * @see #scrollToPosition(int)
-     */
-    public void scrollToPositionWithOffset(int position, int offset) {
-        if (mPendingSavedState != null) {
-            mPendingSavedState.invalidateAnchorPositionInfo();
-        }
-        mPendingScrollPosition = position;
-        mPendingScrollPositionOffset = offset;
-        requestLayout();
-    }
-
-    /** @hide */
-    @Override
-    @RestrictTo(LIBRARY)
-    public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
-            LayoutPrefetchRegistry layoutPrefetchRegistry) {
-        /* This method uses the simplifying assumption that the next N items (where N = span count)
-         * will be assigned, one-to-one, to spans, where ordering is based on which span  extends
-         * least beyond the viewport.
-         *
-         * While this simplified model will be incorrect in some cases, it's difficult to know
-         * item heights, or whether individual items will be full span prior to construction.
-         *
-         * While this greedy estimation approach may underestimate the distance to prefetch items,
-         * it's very unlikely to overestimate them, so distances can be conservatively used to know
-         * the soonest (in terms of scroll distance) a prefetched view may come on screen.
-         */
-        int delta = (mOrientation == HORIZONTAL) ? dx : dy;
-        if (getChildCount() == 0 || delta == 0) {
-            // can't support this scroll, so don't bother prefetching
-            return;
-        }
-        prepareLayoutStateForDelta(delta, state);
-
-        // build sorted list of distances to end of each span (though we don't care which is which)
-        if (mPrefetchDistances == null || mPrefetchDistances.length < mSpanCount) {
-            mPrefetchDistances = new int[mSpanCount];
-        }
-
-        int itemPrefetchCount = 0;
-        for (int i = 0; i < mSpanCount; i++) {
-            // compute number of pixels past the edge of the viewport that the current span extends
-            int distance = mLayoutState.mItemDirection == LAYOUT_START
-                    ? mLayoutState.mStartLine - mSpans[i].getStartLine(mLayoutState.mStartLine)
-                    : mSpans[i].getEndLine(mLayoutState.mEndLine) - mLayoutState.mEndLine;
-            if (distance >= 0) {
-                // span extends to the edge, so prefetch next item
-                mPrefetchDistances[itemPrefetchCount] = distance;
-                itemPrefetchCount++;
-            }
-        }
-        Arrays.sort(mPrefetchDistances, 0, itemPrefetchCount);
-
-        // 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]);
-            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
-        }
-    }
-
-    void prepareLayoutStateForDelta(int delta, RecyclerView.State state) {
-        final int referenceChildPosition;
-        final int layoutDir;
-        if (delta > 0) { // layout towards end
-            layoutDir = LAYOUT_END;
-            referenceChildPosition = getLastChildPosition();
-        } else {
-            layoutDir = LAYOUT_START;
-            referenceChildPosition = getFirstChildPosition();
-        }
-        mLayoutState.mRecycle = true;
-        updateLayoutState(referenceChildPosition, state);
-        setLayoutStateDirection(layoutDir);
-        mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
-        mLayoutState.mAvailable = Math.abs(delta);
-    }
-
-    int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
-        if (getChildCount() == 0 || dt == 0) {
-            return 0;
-        }
-
-        prepareLayoutStateForDelta(dt, state);
-        int consumed = fill(recycler, mLayoutState, state);
-        final int available = mLayoutState.mAvailable;
-        final int totalScroll;
-        if (available < consumed) {
-            totalScroll = dt;
-        } else if (dt < 0) {
-            totalScroll = -consumed;
-        } else { // dt > 0
-            totalScroll = consumed;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "asked " + dt + " scrolled" + totalScroll);
-        }
-
-        mPrimaryOrientation.offsetChildren(-totalScroll);
-        // always reset this if we scroll for a proper save instance state
-        mLastLayoutFromEnd = mShouldReverseLayout;
-        mLayoutState.mAvailable = 0;
-        recycle(recycler, mLayoutState);
-        return totalScroll;
-    }
-
-    int getLastChildPosition() {
-        final int childCount = getChildCount();
-        return childCount == 0 ? 0 : getPosition(getChildAt(childCount - 1));
-    }
-
-    int getFirstChildPosition() {
-        final int childCount = getChildCount();
-        return childCount == 0 ? 0 : getPosition(getChildAt(0));
-    }
-
-    /**
-     * Finds the first View that can be used as an anchor View.
-     *
-     * @return Position of the View or 0 if it cannot find any such View.
-     */
-    private int findFirstReferenceChildPosition(int itemCount) {
-        final int limit = getChildCount();
-        for (int i = 0; i < limit; i++) {
-            final View view = getChildAt(i);
-            final int position = getPosition(view);
-            if (position >= 0 && position < itemCount) {
-                return position;
-            }
-        }
-        return 0;
-    }
-
-    /**
-     * Finds the last View that can be used as an anchor View.
-     *
-     * @return Position of the View or 0 if it cannot find any such View.
-     */
-    private int findLastReferenceChildPosition(int itemCount) {
-        for (int i = getChildCount() - 1; i >= 0; i--) {
-            final View view = getChildAt(i);
-            final int position = getPosition(view);
-            if (position >= 0 && position < itemCount) {
-                return position;
-            }
-        }
-        return 0;
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
-        if (mOrientation == HORIZONTAL) {
-            return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                    ViewGroup.LayoutParams.MATCH_PARENT);
-        } else {
-            return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT);
-        }
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
-        return new LayoutParams(c, attrs);
-    }
-
-    @Override
-    public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
-        if (lp instanceof ViewGroup.MarginLayoutParams) {
-            return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
-        } else {
-            return new LayoutParams(lp);
-        }
-    }
-
-    @Override
-    public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
-        return lp instanceof LayoutParams;
-    }
-
-    public int getOrientation() {
-        return mOrientation;
-    }
-
-    @Nullable
-    @Override
-    public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
-            RecyclerView.State state) {
-        if (getChildCount() == 0) {
-            return null;
-        }
-
-        final View directChild = findContainingItemView(focused);
-        if (directChild == null) {
-            return null;
-        }
-
-        resolveShouldLayoutReverse();
-        final int layoutDir = convertFocusDirectionToLayoutDirection(direction);
-        if (layoutDir == LayoutState.INVALID_LAYOUT) {
-            return null;
-        }
-        LayoutParams prevFocusLayoutParams = (LayoutParams) directChild.getLayoutParams();
-        boolean prevFocusFullSpan = prevFocusLayoutParams.mFullSpan;
-        final Span prevFocusSpan = prevFocusLayoutParams.mSpan;
-        final int referenceChildPosition;
-        if (layoutDir == LAYOUT_END) { // layout towards end
-            referenceChildPosition = getLastChildPosition();
-        } else {
-            referenceChildPosition = getFirstChildPosition();
-        }
-        updateLayoutState(referenceChildPosition, state);
-        setLayoutStateDirection(layoutDir);
-
-        mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
-        mLayoutState.mAvailable = (int) (MAX_SCROLL_FACTOR * mPrimaryOrientation.getTotalSpace());
-        mLayoutState.mStopInFocusable = true;
-        mLayoutState.mRecycle = false;
-        fill(recycler, mLayoutState, state);
-        mLastLayoutFromEnd = mShouldReverseLayout;
-        if (!prevFocusFullSpan) {
-            View view = prevFocusSpan.getFocusableViewAfter(referenceChildPosition, layoutDir);
-            if (view != null && view != directChild) {
-                return view;
-            }
-        }
-
-        // 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--) {
-                View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
-                if (view != null && view != directChild) {
-                    return view;
-                }
-            }
-        } else {
-            for (int i = 0; i < mSpanCount; i++) {
-                View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
-                if (view != null && view != directChild) {
-                    return view;
-                }
-            }
-        }
-
-        // Could not find any focusable views from any of the existing spans. Now start the search
-        // to find the best unfocusable candidate to become visible on the screen next. The search
-        // is done in the same fashion: first, check the views in the desired span and if no
-        // candidate is found, traverse the views in all the remaining spans.
-        boolean shouldSearchFromStart = !mReverseLayout == (layoutDir == LayoutState.LAYOUT_START);
-        View unfocusableCandidate = null;
-        if (!prevFocusFullSpan) {
-            unfocusableCandidate = findViewByPosition(shouldSearchFromStart
-                    ? prevFocusSpan.findFirstPartiallyVisibleItemPosition() :
-                    prevFocusSpan.findLastPartiallyVisibleItemPosition());
-            if (unfocusableCandidate != null && unfocusableCandidate != directChild) {
-                return unfocusableCandidate;
-            }
-        }
-
-        if (preferLastSpan(layoutDir)) {
-            for (int i = mSpanCount - 1; i >= 0; i--) {
-                if (i == prevFocusSpan.mIndex) {
-                    continue;
-                }
-                unfocusableCandidate = findViewByPosition(shouldSearchFromStart
-                        ? mSpans[i].findFirstPartiallyVisibleItemPosition() :
-                        mSpans[i].findLastPartiallyVisibleItemPosition());
-                if (unfocusableCandidate != null && unfocusableCandidate != directChild) {
-                    return unfocusableCandidate;
-                }
-            }
-        } else {
-            for (int i = 0; i < mSpanCount; i++) {
-                unfocusableCandidate = findViewByPosition(shouldSearchFromStart
-                        ? mSpans[i].findFirstPartiallyVisibleItemPosition() :
-                        mSpans[i].findLastPartiallyVisibleItemPosition());
-                if (unfocusableCandidate != null && unfocusableCandidate != directChild) {
-                    return unfocusableCandidate;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Converts a focusDirection to orientation.
-     *
-     * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
-     *                       {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
-     *                       {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
-     *                       or 0 for not applicable
-     * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
-     * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
-     */
-    private int convertFocusDirectionToLayoutDirection(int focusDirection) {
-        switch (focusDirection) {
-            case View.FOCUS_BACKWARD:
-                if (mOrientation == VERTICAL) {
-                    return LayoutState.LAYOUT_START;
-                } else if (isLayoutRTL()) {
-                    return LayoutState.LAYOUT_END;
-                } else {
-                    return LayoutState.LAYOUT_START;
-                }
-            case View.FOCUS_FORWARD:
-                if (mOrientation == VERTICAL) {
-                    return LayoutState.LAYOUT_END;
-                } else if (isLayoutRTL()) {
-                    return LayoutState.LAYOUT_START;
-                } else {
-                    return LayoutState.LAYOUT_END;
-                }
-            case View.FOCUS_UP:
-                return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
-                        : LayoutState.INVALID_LAYOUT;
-            case View.FOCUS_DOWN:
-                return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
-                        : LayoutState.INVALID_LAYOUT;
-            case View.FOCUS_LEFT:
-                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
-                        : LayoutState.INVALID_LAYOUT;
-            case View.FOCUS_RIGHT:
-                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
-                        : LayoutState.INVALID_LAYOUT;
-            default:
-                if (DEBUG) {
-                    Log.d(TAG, "Unknown focus request:" + focusDirection);
-                }
-                return LayoutState.INVALID_LAYOUT;
-        }
-
-    }
-
-    /**
-     * LayoutParams used by StaggeredGridLayoutManager.
-     * <p>
-     * Note that if the orientation is {@link #VERTICAL}, the width parameter is ignored and if the
-     * orientation is {@link #HORIZONTAL} the height parameter is ignored because child view is
-     * expected to fill all of the space given to it.
-     */
-    public static class LayoutParams extends RecyclerView.LayoutParams {
-
-        /**
-         * Span Id for Views that are not laid out yet.
-         */
-        public static final int INVALID_SPAN_ID = -1;
-
-        // Package scope to be able to access from tests.
-        Span mSpan;
-
-        boolean mFullSpan;
-
-        public LayoutParams(Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-        }
-
-        public LayoutParams(ViewGroup.MarginLayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(RecyclerView.LayoutParams source) {
-            super(source);
-        }
-
-        /**
-         * When set to true, the item will layout using all span area. That means, if orientation
-         * is vertical, the view will have full width; if orientation is horizontal, the view will
-         * have full height.
-         *
-         * @param fullSpan True if this item should traverse all spans.
-         * @see #isFullSpan()
-         */
-        public void setFullSpan(boolean fullSpan) {
-            mFullSpan = fullSpan;
-        }
-
-        /**
-         * Returns whether this View occupies all available spans or just one.
-         *
-         * @return True if the View occupies all spans or false otherwise.
-         * @see #setFullSpan(boolean)
-         */
-        public boolean isFullSpan() {
-            return mFullSpan;
-        }
-
-        /**
-         * Returns the Span index to which this View is assigned.
-         *
-         * @return The Span index of the View. If View is not yet assigned to any span, returns
-         * {@link #INVALID_SPAN_ID}.
-         */
-        public final int getSpanIndex() {
-            if (mSpan == null) {
-                return INVALID_SPAN_ID;
-            }
-            return mSpan.mIndex;
-        }
-    }
-
-    // Package scoped to access from tests.
-    class Span {
-
-        static final int INVALID_LINE = Integer.MIN_VALUE;
-        ArrayList<View> mViews = new ArrayList<>();
-        int mCachedStart = INVALID_LINE;
-        int mCachedEnd = INVALID_LINE;
-        int mDeletedSize = 0;
-        final int mIndex;
-
-        Span(int index) {
-            mIndex = index;
-        }
-
-        int getStartLine(int def) {
-            if (mCachedStart != INVALID_LINE) {
-                return mCachedStart;
-            }
-            if (mViews.size() == 0) {
-                return def;
-            }
-            calculateCachedStart();
-            return mCachedStart;
-        }
-
-        void calculateCachedStart() {
-            final View startView = mViews.get(0);
-            final LayoutParams lp = getLayoutParams(startView);
-            mCachedStart = mPrimaryOrientation.getDecoratedStart(startView);
-            if (lp.mFullSpan) {
-                LazySpanLookup.FullSpanItem fsi = mLazySpanLookup
-                        .getFullSpanItem(lp.getViewLayoutPosition());
-                if (fsi != null && fsi.mGapDir == LAYOUT_START) {
-                    mCachedStart -= fsi.getGapForSpan(mIndex);
-                }
-            }
-        }
-
-        // Use this one when default value does not make sense and not having a value means a bug.
-        int getStartLine() {
-            if (mCachedStart != INVALID_LINE) {
-                return mCachedStart;
-            }
-            calculateCachedStart();
-            return mCachedStart;
-        }
-
-        int getEndLine(int def) {
-            if (mCachedEnd != INVALID_LINE) {
-                return mCachedEnd;
-            }
-            final int size = mViews.size();
-            if (size == 0) {
-                return def;
-            }
-            calculateCachedEnd();
-            return mCachedEnd;
-        }
-
-        void calculateCachedEnd() {
-            final View endView = mViews.get(mViews.size() - 1);
-            final LayoutParams lp = getLayoutParams(endView);
-            mCachedEnd = mPrimaryOrientation.getDecoratedEnd(endView);
-            if (lp.mFullSpan) {
-                LazySpanLookup.FullSpanItem fsi = mLazySpanLookup
-                        .getFullSpanItem(lp.getViewLayoutPosition());
-                if (fsi != null && fsi.mGapDir == LAYOUT_END) {
-                    mCachedEnd += fsi.getGapForSpan(mIndex);
-                }
-            }
-        }
-
-        // Use this one when default value does not make sense and not having a value means a bug.
-        int getEndLine() {
-            if (mCachedEnd != INVALID_LINE) {
-                return mCachedEnd;
-            }
-            calculateCachedEnd();
-            return mCachedEnd;
-        }
-
-        void prependToSpan(View view) {
-            LayoutParams lp = getLayoutParams(view);
-            lp.mSpan = this;
-            mViews.add(0, view);
-            mCachedStart = INVALID_LINE;
-            if (mViews.size() == 1) {
-                mCachedEnd = INVALID_LINE;
-            }
-            if (lp.isItemRemoved() || lp.isItemChanged()) {
-                mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view);
-            }
-        }
-
-        void appendToSpan(View view) {
-            LayoutParams lp = getLayoutParams(view);
-            lp.mSpan = this;
-            mViews.add(view);
-            mCachedEnd = INVALID_LINE;
-            if (mViews.size() == 1) {
-                mCachedStart = INVALID_LINE;
-            }
-            if (lp.isItemRemoved() || lp.isItemChanged()) {
-                mDeletedSize += mPrimaryOrientation.getDecoratedMeasurement(view);
-            }
-        }
-
-        // Useful method to preserve positions on a re-layout.
-        void cacheReferenceLineAndClear(boolean reverseLayout, int offset) {
-            int reference;
-            if (reverseLayout) {
-                reference = getEndLine(INVALID_LINE);
-            } else {
-                reference = getStartLine(INVALID_LINE);
-            }
-            clear();
-            if (reference == INVALID_LINE) {
-                return;
-            }
-            if ((reverseLayout && reference < mPrimaryOrientation.getEndAfterPadding())
-                    || (!reverseLayout && reference > mPrimaryOrientation.getStartAfterPadding())) {
-                return;
-            }
-            if (offset != INVALID_OFFSET) {
-                reference += offset;
-            }
-            mCachedStart = mCachedEnd = reference;
-        }
-
-        void clear() {
-            mViews.clear();
-            invalidateCache();
-            mDeletedSize = 0;
-        }
-
-        void invalidateCache() {
-            mCachedStart = INVALID_LINE;
-            mCachedEnd = INVALID_LINE;
-        }
-
-        void setLine(int line) {
-            mCachedEnd = mCachedStart = line;
-        }
-
-        void popEnd() {
-            final int size = mViews.size();
-            View end = mViews.remove(size - 1);
-            final LayoutParams lp = getLayoutParams(end);
-            lp.mSpan = null;
-            if (lp.isItemRemoved() || lp.isItemChanged()) {
-                mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(end);
-            }
-            if (size == 1) {
-                mCachedStart = INVALID_LINE;
-            }
-            mCachedEnd = INVALID_LINE;
-        }
-
-        void popStart() {
-            View start = mViews.remove(0);
-            final LayoutParams lp = getLayoutParams(start);
-            lp.mSpan = null;
-            if (mViews.size() == 0) {
-                mCachedEnd = INVALID_LINE;
-            }
-            if (lp.isItemRemoved() || lp.isItemChanged()) {
-                mDeletedSize -= mPrimaryOrientation.getDecoratedMeasurement(start);
-            }
-            mCachedStart = INVALID_LINE;
-        }
-
-        public int getDeletedSize() {
-            return mDeletedSize;
-        }
-
-        LayoutParams getLayoutParams(View view) {
-            return (LayoutParams) view.getLayoutParams();
-        }
-
-        void onOffset(int dt) {
-            if (mCachedStart != INVALID_LINE) {
-                mCachedStart += dt;
-            }
-            if (mCachedEnd != INVALID_LINE) {
-                mCachedEnd += dt;
-            }
-        }
-
-        public int findFirstVisibleItemPosition() {
-            return mReverseLayout
-                    ? findOneVisibleChild(mViews.size() - 1, -1, false)
-                    : findOneVisibleChild(0, mViews.size(), false);
-        }
-
-        public int findFirstPartiallyVisibleItemPosition() {
-            return mReverseLayout
-                    ? findOnePartiallyVisibleChild(mViews.size() - 1, -1, true)
-                    : findOnePartiallyVisibleChild(0, mViews.size(), true);
-        }
-
-        public int findFirstCompletelyVisibleItemPosition() {
-            return mReverseLayout
-                    ? findOneVisibleChild(mViews.size() - 1, -1, true)
-                    : findOneVisibleChild(0, mViews.size(), true);
-        }
-
-        public int findLastVisibleItemPosition() {
-            return mReverseLayout
-                    ? findOneVisibleChild(0, mViews.size(), false)
-                    : findOneVisibleChild(mViews.size() - 1, -1, false);
-        }
-
-        public int findLastPartiallyVisibleItemPosition() {
-            return mReverseLayout
-                    ? findOnePartiallyVisibleChild(0, mViews.size(), true)
-                    : findOnePartiallyVisibleChild(mViews.size() - 1, -1, true);
-        }
-
-        public int findLastCompletelyVisibleItemPosition() {
-            return mReverseLayout
-                    ? findOneVisibleChild(0, mViews.size(), true)
-                    : findOneVisibleChild(mViews.size() - 1, -1, true);
-        }
-
-        /**
-         * Returns the first view within this span that is partially or fully visible. Partially
-         * visible refers to a view that overlaps but is not fully contained within RV's padded
-         * bounded area. This view returned can be defined to have an area of overlap strictly
-         * greater than zero if acceptEndPointInclusion is false. If true, the view's endpoint
-         * inclusion is enough to consider it partially visible. The latter case can then refer to
-         * an out-of-bounds view positioned right at the top (or bottom) boundaries of RV's padded
-         * area. This is used e.g. inside
-         * {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)} for
-         * calculating the next unfocusable child to become visible on the screen.
-         * @param fromIndex The child position index to start the search from.
-         * @param toIndex The child position index to end the search at.
-         * @param completelyVisible True if we have to only consider completely visible views,
-         *                          false otherwise.
-         * @param acceptCompletelyVisible True if we can consider both partially or fully visible
-         *                                views, false, if only a partially visible child should be
-         *                                returned.
-         * @param acceptEndPointInclusion If the view's endpoint intersection with RV's padded
-         *                                bounded area is enough to consider it partially visible,
-         *                                false otherwise
-         * @return The adapter position of the first view that's either partially or fully visible.
-         * {@link RecyclerView#NO_POSITION} if no such view is found.
-         */
-        int findOnePartiallyOrCompletelyVisibleChild(int fromIndex, int toIndex,
-                boolean completelyVisible,
-                boolean acceptCompletelyVisible,
-                boolean acceptEndPointInclusion) {
-            final int start = mPrimaryOrientation.getStartAfterPadding();
-            final int end = mPrimaryOrientation.getEndAfterPadding();
-            final int next = toIndex > fromIndex ? 1 : -1;
-            for (int i = fromIndex; i != toIndex; i += next) {
-                final View child = mViews.get(i);
-                final int childStart = mPrimaryOrientation.getDecoratedStart(child);
-                final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
-                boolean childStartInclusion = acceptEndPointInclusion ? (childStart <= end)
-                        : (childStart < end);
-                boolean childEndInclusion = acceptEndPointInclusion ? (childEnd >= start)
-                        : (childEnd > start);
-                if (childStartInclusion && childEndInclusion) {
-                    if (completelyVisible && acceptCompletelyVisible) {
-                        // the child has to be completely visible to be returned.
-                        if (childStart >= start && childEnd <= end) {
-                            return getPosition(child);
-                        }
-                    } else if (acceptCompletelyVisible) {
-                        // can return either a partially or completely visible child.
-                        return getPosition(child);
-                    } else if (childStart < start || childEnd > end) {
-                        // should return a partially visible child if exists and a completely
-                        // visible child is not acceptable in this case.
-                        return getPosition(child);
-                    }
-                }
-            }
-            return NO_POSITION;
-        }
-
-        int findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible) {
-            return findOnePartiallyOrCompletelyVisibleChild(fromIndex, toIndex, completelyVisible,
-                    true, false);
-        }
-
-        int findOnePartiallyVisibleChild(int fromIndex, int toIndex,
-                boolean acceptEndPointInclusion) {
-            return findOnePartiallyOrCompletelyVisibleChild(fromIndex, toIndex, false, false,
-                    acceptEndPointInclusion);
-        }
-
-        /**
-         * Depending on the layout direction, returns the View that is after the given position.
-         */
-        public View getFocusableViewAfter(int referenceChildPosition, int layoutDir) {
-            View candidate = null;
-            if (layoutDir == LAYOUT_START) {
-                final int limit = mViews.size();
-                for (int i = 0; i < limit; i++) {
-                    final View view = mViews.get(i);
-                    if ((mReverseLayout && getPosition(view) <= referenceChildPosition)
-                            || (!mReverseLayout && getPosition(view) >= referenceChildPosition)) {
-                        break;
-                    }
-                    if (view.hasFocusable()) {
-                        candidate = view;
-                    } else {
-                        break;
-                    }
-                }
-            } else {
-                for (int i = mViews.size() - 1; i >= 0; i--) {
-                    final View view = mViews.get(i);
-                    if ((mReverseLayout && getPosition(view) >= referenceChildPosition)
-                            || (!mReverseLayout && getPosition(view) <= referenceChildPosition)) {
-                        break;
-                    }
-                    if (view.hasFocusable()) {
-                        candidate = view;
-                    } else {
-                        break;
-                    }
-                }
-            }
-            return candidate;
-        }
-    }
-
-    /**
-     * An array of mappings from adapter position to span.
-     * This only grows when a write happens and it grows up to the size of the adapter.
-     */
-    static class LazySpanLookup {
-
-        private static final int MIN_SIZE = 10;
-        int[] mData;
-        List<FullSpanItem> mFullSpanItems;
-
-
-        /**
-         * Invalidates everything after this position, including full span information
-         */
-        int forceInvalidateAfter(int position) {
-            if (mFullSpanItems != null) {
-                for (int i = mFullSpanItems.size() - 1; i >= 0; i--) {
-                    FullSpanItem fsi = mFullSpanItems.get(i);
-                    if (fsi.mPosition >= position) {
-                        mFullSpanItems.remove(i);
-                    }
-                }
-            }
-            return invalidateAfter(position);
-        }
-
-        /**
-         * returns end position for invalidation.
-         */
-        int invalidateAfter(int position) {
-            if (mData == null) {
-                return RecyclerView.NO_POSITION;
-            }
-            if (position >= mData.length) {
-                return RecyclerView.NO_POSITION;
-            }
-            int endPosition = invalidateFullSpansAfter(position);
-            if (endPosition == RecyclerView.NO_POSITION) {
-                Arrays.fill(mData, position, mData.length, LayoutParams.INVALID_SPAN_ID);
-                return mData.length;
-            } else {
-                // just invalidate items in between
-                Arrays.fill(mData, position, endPosition + 1, LayoutParams.INVALID_SPAN_ID);
-                return endPosition + 1;
-            }
-        }
-
-        int getSpan(int position) {
-            if (mData == null || position >= mData.length) {
-                return LayoutParams.INVALID_SPAN_ID;
-            } else {
-                return mData[position];
-            }
-        }
-
-        void setSpan(int position, Span span) {
-            ensureSize(position);
-            mData[position] = span.mIndex;
-        }
-
-        int sizeForPosition(int position) {
-            int len = mData.length;
-            while (len <= position) {
-                len *= 2;
-            }
-            return len;
-        }
-
-        void ensureSize(int position) {
-            if (mData == null) {
-                mData = new int[Math.max(position, MIN_SIZE) + 1];
-                Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID);
-            } else if (position >= mData.length) {
-                int[] old = mData;
-                mData = new int[sizeForPosition(position)];
-                System.arraycopy(old, 0, mData, 0, old.length);
-                Arrays.fill(mData, old.length, mData.length, LayoutParams.INVALID_SPAN_ID);
-            }
-        }
-
-        void clear() {
-            if (mData != null) {
-                Arrays.fill(mData, LayoutParams.INVALID_SPAN_ID);
-            }
-            mFullSpanItems = null;
-        }
-
-        void offsetForRemoval(int positionStart, int itemCount) {
-            if (mData == null || positionStart >= mData.length) {
-                return;
-            }
-            ensureSize(positionStart + itemCount);
-            System.arraycopy(mData, positionStart + itemCount, mData, positionStart,
-                    mData.length - positionStart - itemCount);
-            Arrays.fill(mData, mData.length - itemCount, mData.length,
-                    LayoutParams.INVALID_SPAN_ID);
-            offsetFullSpansForRemoval(positionStart, itemCount);
-        }
-
-        private void offsetFullSpansForRemoval(int positionStart, int itemCount) {
-            if (mFullSpanItems == null) {
-                return;
-            }
-            final int end = positionStart + itemCount;
-            for (int i = mFullSpanItems.size() - 1; i >= 0; i--) {
-                FullSpanItem fsi = mFullSpanItems.get(i);
-                if (fsi.mPosition < positionStart) {
-                    continue;
-                }
-                if (fsi.mPosition < end) {
-                    mFullSpanItems.remove(i);
-                } else {
-                    fsi.mPosition -= itemCount;
-                }
-            }
-        }
-
-        void offsetForAddition(int positionStart, int itemCount) {
-            if (mData == null || positionStart >= mData.length) {
-                return;
-            }
-            ensureSize(positionStart + itemCount);
-            System.arraycopy(mData, positionStart, mData, positionStart + itemCount,
-                    mData.length - positionStart - itemCount);
-            Arrays.fill(mData, positionStart, positionStart + itemCount,
-                    LayoutParams.INVALID_SPAN_ID);
-            offsetFullSpansForAddition(positionStart, itemCount);
-        }
-
-        private void offsetFullSpansForAddition(int positionStart, int itemCount) {
-            if (mFullSpanItems == null) {
-                return;
-            }
-            for (int i = mFullSpanItems.size() - 1; i >= 0; i--) {
-                FullSpanItem fsi = mFullSpanItems.get(i);
-                if (fsi.mPosition < positionStart) {
-                    continue;
-                }
-                fsi.mPosition += itemCount;
-            }
-        }
-
-        /**
-         * Returns when invalidation should end. e.g. hitting a full span position.
-         * Returned position SHOULD BE invalidated.
-         */
-        private int invalidateFullSpansAfter(int position) {
-            if (mFullSpanItems == null) {
-                return RecyclerView.NO_POSITION;
-            }
-            final FullSpanItem item = getFullSpanItem(position);
-            // if there is an fsi at this position, get rid of it.
-            if (item != null) {
-                mFullSpanItems.remove(item);
-            }
-            int nextFsiIndex = -1;
-            final int count = mFullSpanItems.size();
-            for (int i = 0; i < count; i++) {
-                FullSpanItem fsi = mFullSpanItems.get(i);
-                if (fsi.mPosition >= position) {
-                    nextFsiIndex = i;
-                    break;
-                }
-            }
-            if (nextFsiIndex != -1) {
-                FullSpanItem fsi = mFullSpanItems.get(nextFsiIndex);
-                mFullSpanItems.remove(nextFsiIndex);
-                return fsi.mPosition;
-            }
-            return RecyclerView.NO_POSITION;
-        }
-
-        public void addFullSpanItem(FullSpanItem fullSpanItem) {
-            if (mFullSpanItems == null) {
-                mFullSpanItems = new ArrayList<>();
-            }
-            final int size = mFullSpanItems.size();
-            for (int i = 0; i < size; i++) {
-                FullSpanItem other = mFullSpanItems.get(i);
-                if (other.mPosition == fullSpanItem.mPosition) {
-                    if (DEBUG) {
-                        throw new IllegalStateException("two fsis for same position");
-                    } else {
-                        mFullSpanItems.remove(i);
-                    }
-                }
-                if (other.mPosition >= fullSpanItem.mPosition) {
-                    mFullSpanItems.add(i, fullSpanItem);
-                    return;
-                }
-            }
-            // if it is not added to a position.
-            mFullSpanItems.add(fullSpanItem);
-        }
-
-        public FullSpanItem getFullSpanItem(int position) {
-            if (mFullSpanItems == null) {
-                return null;
-            }
-            for (int i = mFullSpanItems.size() - 1; i >= 0; i--) {
-                final FullSpanItem fsi = mFullSpanItems.get(i);
-                if (fsi.mPosition == position) {
-                    return fsi;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * @param minPos inclusive
-         * @param maxPos exclusive
-         * @param gapDir if not 0, returns FSIs on in that direction
-         * @param hasUnwantedGapAfter If true, when full span item has unwanted gaps, it will be
-         *                        returned even if its gap direction does not match.
-         */
-        public FullSpanItem getFirstFullSpanItemInRange(int minPos, int maxPos, int gapDir,
-                boolean hasUnwantedGapAfter) {
-            if (mFullSpanItems == null) {
-                return null;
-            }
-            final int limit = mFullSpanItems.size();
-            for (int i = 0; i < limit; i++) {
-                FullSpanItem fsi = mFullSpanItems.get(i);
-                if (fsi.mPosition >= maxPos) {
-                    return null;
-                }
-                if (fsi.mPosition >= minPos
-                        && (gapDir == 0 || fsi.mGapDir == gapDir
-                        || (hasUnwantedGapAfter && fsi.mHasUnwantedGapAfter))) {
-                    return fsi;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * We keep information about full span items because they may create gaps in the UI.
-         */
-        static class FullSpanItem implements Parcelable {
-
-            int mPosition;
-            int mGapDir;
-            int[] mGapPerSpan;
-            // A full span may be laid out in primary direction but may have gaps due to
-            // invalidation of views after it. This is recorded during a reverse scroll and if
-            // view is still on the screen after scroll stops, we have to recalculate layout
-            boolean mHasUnwantedGapAfter;
-
-            FullSpanItem(Parcel in) {
-                mPosition = in.readInt();
-                mGapDir = in.readInt();
-                mHasUnwantedGapAfter = in.readInt() == 1;
-                int spanCount = in.readInt();
-                if (spanCount > 0) {
-                    mGapPerSpan = new int[spanCount];
-                    in.readIntArray(mGapPerSpan);
-                }
-            }
-
-            FullSpanItem() {
-            }
-
-            int getGapForSpan(int spanIndex) {
-                return mGapPerSpan == null ? 0 : mGapPerSpan[spanIndex];
-            }
-
-            @Override
-            public int describeContents() {
-                return 0;
-            }
-
-            @Override
-            public void writeToParcel(Parcel dest, int flags) {
-                dest.writeInt(mPosition);
-                dest.writeInt(mGapDir);
-                dest.writeInt(mHasUnwantedGapAfter ? 1 : 0);
-                if (mGapPerSpan != null && mGapPerSpan.length > 0) {
-                    dest.writeInt(mGapPerSpan.length);
-                    dest.writeIntArray(mGapPerSpan);
-                } else {
-                    dest.writeInt(0);
-                }
-            }
-
-            @Override
-            public String toString() {
-                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);
-                        }
-
-                        @Override
-                        public FullSpanItem[] newArray(int size) {
-                            return new FullSpanItem[size];
-                        }
-                    };
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public static class SavedState implements Parcelable {
-
-        int mAnchorPosition;
-        int mVisibleAnchorPosition; // Replacement for span info when spans are invalidated
-        int mSpanOffsetsSize;
-        int[] mSpanOffsets;
-        int mSpanLookupSize;
-        int[] mSpanLookup;
-        List<LazySpanLookup.FullSpanItem> mFullSpanItems;
-        boolean mReverseLayout;
-        boolean mAnchorLayoutFromEnd;
-        boolean mLastLayoutRTL;
-
-        public SavedState() {
-        }
-
-        SavedState(Parcel in) {
-            mAnchorPosition = in.readInt();
-            mVisibleAnchorPosition = in.readInt();
-            mSpanOffsetsSize = in.readInt();
-            if (mSpanOffsetsSize > 0) {
-                mSpanOffsets = new int[mSpanOffsetsSize];
-                in.readIntArray(mSpanOffsets);
-            }
-
-            mSpanLookupSize = in.readInt();
-            if (mSpanLookupSize > 0) {
-                mSpanLookup = new int[mSpanLookupSize];
-                in.readIntArray(mSpanLookup);
-            }
-            mReverseLayout = in.readInt() == 1;
-            mAnchorLayoutFromEnd = in.readInt() == 1;
-            mLastLayoutRTL = in.readInt() == 1;
-            //noinspection unchecked
-            mFullSpanItems = in.readArrayList(
-                    LazySpanLookup.FullSpanItem.class.getClassLoader());
-        }
-
-        public SavedState(SavedState other) {
-            mSpanOffsetsSize = other.mSpanOffsetsSize;
-            mAnchorPosition = other.mAnchorPosition;
-            mVisibleAnchorPosition = other.mVisibleAnchorPosition;
-            mSpanOffsets = other.mSpanOffsets;
-            mSpanLookupSize = other.mSpanLookupSize;
-            mSpanLookup = other.mSpanLookup;
-            mReverseLayout = other.mReverseLayout;
-            mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd;
-            mLastLayoutRTL = other.mLastLayoutRTL;
-            mFullSpanItems = other.mFullSpanItems;
-        }
-
-        void invalidateSpanInfo() {
-            mSpanOffsets = null;
-            mSpanOffsetsSize = 0;
-            mSpanLookupSize = 0;
-            mSpanLookup = null;
-            mFullSpanItems = null;
-        }
-
-        void invalidateAnchorPositionInfo() {
-            mSpanOffsets = null;
-            mSpanOffsetsSize = 0;
-            mAnchorPosition = NO_POSITION;
-            mVisibleAnchorPosition = NO_POSITION;
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mAnchorPosition);
-            dest.writeInt(mVisibleAnchorPosition);
-            dest.writeInt(mSpanOffsetsSize);
-            if (mSpanOffsetsSize > 0) {
-                dest.writeIntArray(mSpanOffsets);
-            }
-            dest.writeInt(mSpanLookupSize);
-            if (mSpanLookupSize > 0) {
-                dest.writeIntArray(mSpanLookup);
-            }
-            dest.writeInt(mReverseLayout ? 1 : 0);
-            dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0);
-            dest.writeInt(mLastLayoutRTL ? 1 : 0);
-            dest.writeList(mFullSpanItems);
-        }
-
-        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];
-                    }
-                };
-    }
-
-    /**
-     * Data class to hold the information about an anchor position which is used in onLayout call.
-     */
-    class AnchorInfo {
-
-        int mPosition;
-        int mOffset;
-        boolean mLayoutFromEnd;
-        boolean mInvalidateOffsets;
-        boolean mValid;
-        // this is where we save span reference lines in case we need to re-use them for multi-pass
-        // measure steps
-        int[] mSpanReferenceLines;
-
-        AnchorInfo() {
-            reset();
-        }
-
-        void reset() {
-            mPosition = NO_POSITION;
-            mOffset = INVALID_OFFSET;
-            mLayoutFromEnd = false;
-            mInvalidateOffsets = false;
-            mValid = false;
-            if (mSpanReferenceLines != null) {
-                Arrays.fill(mSpanReferenceLines, -1);
-            }
-        }
-
-        void saveSpanReferenceLines(Span[] spans) {
-            int spanCount = spans.length;
-            if (mSpanReferenceLines == null || mSpanReferenceLines.length < spanCount) {
-                mSpanReferenceLines = new int[mSpans.length];
-            }
-            for (int i = 0; i < spanCount; i++) {
-                // does not matter start or end since this is only recorded when span is reset
-                mSpanReferenceLines[i] = spans[i].getStartLine(Span.INVALID_LINE);
-            }
-        }
-
-        void assignCoordinateFromPadding() {
-            mOffset = mLayoutFromEnd ? mPrimaryOrientation.getEndAfterPadding()
-                    : mPrimaryOrientation.getStartAfterPadding();
-        }
-
-        void assignCoordinateFromPadding(int addedDistance) {
-            if (mLayoutFromEnd) {
-                mOffset = mPrimaryOrientation.getEndAfterPadding() - addedDistance;
-            } else {
-                mOffset = mPrimaryOrientation.getStartAfterPadding() + addedDistance;
-            }
-        }
-    }
-}
diff --git a/android/support/v7/widget/SuggestionsAdapter.java b/android/support/v7/widget/SuggestionsAdapter.java
deleted file mode 100644
index d6f0099..0000000
--- a/android/support/v7/widget/SuggestionsAdapter.java
+++ /dev/null
@@ -1,779 +0,0 @@
-package android.support.v7.widget;
-
-/*
- * 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.
- */
-
-import android.app.SearchManager;
-import android.app.SearchableInfo;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.widget.ResourceCursorAdapter;
-import android.support.v7.appcompat.R;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * Provides the contents for the suggestion drop-down list.in {@link SearchView}.
- */
-class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListener {
-
-    private static final boolean DBG = false;
-    private static final String LOG_TAG = "SuggestionsAdapter";
-    private static final int QUERY_LIMIT = 50;
-
-    static final int REFINE_NONE = 0;
-    static final int REFINE_BY_ENTRY = 1;
-    static final int REFINE_ALL = 2;
-
-    private final SearchManager mSearchManager;
-    private final SearchView mSearchView;
-    private final SearchableInfo mSearchable;
-    private final Context mProviderContext;
-    private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
-    private final int mCommitIconResId;
-    private boolean mClosed = false;
-    private int mQueryRefinement = REFINE_BY_ENTRY;
-
-    // URL color
-    private ColorStateList mUrlColor;
-
-    static final int INVALID_INDEX = -1;
-
-    // Cached column indexes, updated when the cursor changes.
-    private int mText1Col = INVALID_INDEX;
-    private int mText2Col = INVALID_INDEX;
-    private int mText2UrlCol = INVALID_INDEX;
-    private int mIconName1Col = INVALID_INDEX;
-    private int mIconName2Col = INVALID_INDEX;
-    private int mFlagsCol = INVALID_INDEX;
-
-    // private final Runnable mStartSpinnerRunnable;
-    // private final Runnable mStopSpinnerRunnable;
-
-    public SuggestionsAdapter(Context context, SearchView searchView, SearchableInfo searchable,
-            WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) {
-        super(context, searchView.getSuggestionRowLayout(), null /* no initial cursor */,
-                true /* auto-requery */);
-        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
-        mSearchView = searchView;
-        mSearchable = searchable;
-        mCommitIconResId = searchView.getSuggestionCommitIconResId();
-
-        // set up provider resources (gives us icons, etc.)
-        mProviderContext = context;
-
-        mOutsideDrawablesCache = outsideDrawablesCache;
-    }
-
-    /**
-     * Enables query refinement for all suggestions. This means that an additional icon
-     * will be shown for each entry. When clicked, the suggested text on that line will be
-     * copied to the query text field.
-     * <p>
-     *
-     * @param refineWhat which queries to refine. Possible values are {@link #REFINE_NONE},
-     * {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}.
-     */
-    public void setQueryRefinement(int refineWhat) {
-        mQueryRefinement = refineWhat;
-    }
-
-    /**
-     * Returns the current query refinement preference.
-     * @return value of query refinement preference
-     */
-    public int getQueryRefinement() {
-        return mQueryRefinement;
-    }
-
-    /**
-     * Overridden to always return <code>false</code>, since we cannot be sure that
-     * suggestion sources return stable IDs.
-     */
-    @Override
-    public boolean hasStableIds() {
-        return false;
-    }
-
-    /**
-     * Use the search suggestions provider to obtain a live cursor.  This will be called
-     * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
-     * The results will be processed in the UI thread and changeCursor() will be called.
-     */
-    @Override
-    public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
-        if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
-        String query = (constraint == null) ? "" : constraint.toString();
-        /**
-         * for in app search we show the progress spinner until the cursor is returned with
-         * the results.
-         */
-        Cursor cursor = null;
-        if (mSearchView.getVisibility() != View.VISIBLE
-                || mSearchView.getWindowVisibility() != View.VISIBLE) {
-            return null;
-        }
-        try {
-            cursor = getSearchManagerSuggestions(mSearchable, query, QUERY_LIMIT);
-            // trigger fill window so the spinner stays up until the results are copied over and
-            // closer to being ready
-            if (cursor != null) {
-                cursor.getCount();
-                return cursor;
-            }
-        } catch (RuntimeException e) {
-            Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
-        }
-        // If cursor is null or an exception was thrown, stop the spinner and return null.
-        // changeCursor doesn't get called if cursor is null
-        return null;
-    }
-
-    public void close() {
-        if (DBG) Log.d(LOG_TAG, "close()");
-        changeCursor(null);
-        mClosed = true;
-    }
-
-    @Override
-    public void notifyDataSetChanged() {
-        if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
-        super.notifyDataSetChanged();
-
-        updateSpinnerState(getCursor());
-    }
-
-    @Override
-    public void notifyDataSetInvalidated() {
-        if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated");
-        super.notifyDataSetInvalidated();
-
-        updateSpinnerState(getCursor());
-    }
-
-    private void updateSpinnerState(Cursor cursor) {
-        Bundle extras = cursor != null ? cursor.getExtras() : null;
-        if (DBG) {
-            Log.d(LOG_TAG, "updateSpinnerState - extra = "
-                + (extras != null
-                        ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
-                        : null));
-        }
-        // Check if the Cursor indicates that the query is not complete and show the spinner
-        if (extras != null
-                && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
-            return;
-        }
-        // If cursor is null or is done, stop the spinner
-    }
-
-    /**
-     * Cache columns.
-     */
-    @Override
-    public void changeCursor(Cursor c) {
-        if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")");
-
-        if (mClosed) {
-            Log.w(LOG_TAG, "Tried to change cursor after adapter was closed.");
-            if (c != null) c.close();
-            return;
-        }
-
-        try {
-            super.changeCursor(c);
-
-            if (c != null) {
-                mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
-                mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
-                mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
-                mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
-                mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
-                mFlagsCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FLAGS);
-            }
-        } catch (Exception e) {
-            Log.e(LOG_TAG, "error changing cursor and caching columns", e);
-        }
-    }
-
-    /**
-     * Tags the view with cached child view look-ups.
-     */
-    @Override
-    public View newView(Context context, Cursor cursor, ViewGroup parent) {
-        final View v = super.newView(context, cursor, parent);
-        v.setTag(new ChildViewCache(v));
-
-        // Set up icon.
-        final ImageView iconRefine = (ImageView) v.findViewById(R.id.edit_query);
-        iconRefine.setImageResource(mCommitIconResId);
-        return v;
-    }
-
-    /**
-     * Cache of the child views of drop-drown list items, to avoid looking up the children
-     * each time the contents of a list item are changed.
-     */
-    private final static class ChildViewCache {
-        public final TextView mText1;
-        public final TextView mText2;
-        public final ImageView mIcon1;
-        public final ImageView mIcon2;
-        public final ImageView mIconRefine;
-
-        public ChildViewCache(View v) {
-            mText1 = (TextView) v.findViewById(android.R.id.text1);
-            mText2 = (TextView) v.findViewById(android.R.id.text2);
-            mIcon1 = (ImageView) v.findViewById(android.R.id.icon1);
-            mIcon2 = (ImageView) v.findViewById(android.R.id.icon2);
-            mIconRefine = (ImageView) v.findViewById(R.id.edit_query);
-        }
-    }
-
-    @Override
-    public void bindView(View view, Context context, Cursor cursor) {
-        ChildViewCache views = (ChildViewCache) view.getTag();
-
-        int flags = 0;
-        if (mFlagsCol != INVALID_INDEX) {
-            flags = cursor.getInt(mFlagsCol);
-        }
-        if (views.mText1 != null) {
-            String text1 = getStringOrNull(cursor, mText1Col);
-            setViewText(views.mText1, text1);
-        }
-        if (views.mText2 != null) {
-            // First check TEXT_2_URL
-            CharSequence text2 = getStringOrNull(cursor, mText2UrlCol);
-            if (text2 != null) {
-                text2 = formatUrl(text2);
-            } else {
-                text2 = getStringOrNull(cursor, mText2Col);
-            }
-
-            // If no second line of text is indicated, allow the first line of text
-            // to be up to two lines if it wants to be.
-            if (TextUtils.isEmpty(text2)) {
-                if (views.mText1 != null) {
-                    views.mText1.setSingleLine(false);
-                    views.mText1.setMaxLines(2);
-                }
-            } else {
-                if (views.mText1 != null) {
-                    views.mText1.setSingleLine(true);
-                    views.mText1.setMaxLines(1);
-                }
-            }
-            setViewText(views.mText2, text2);
-        }
-
-        if (views.mIcon1 != null) {
-            setViewDrawable(views.mIcon1, getIcon1(cursor), View.INVISIBLE);
-        }
-        if (views.mIcon2 != null) {
-            setViewDrawable(views.mIcon2, getIcon2(cursor), View.GONE);
-        }
-        if (mQueryRefinement == REFINE_ALL
-                || (mQueryRefinement == REFINE_BY_ENTRY
-                        && (flags & SearchManager.FLAG_QUERY_REFINEMENT) != 0)) {
-            views.mIconRefine.setVisibility(View.VISIBLE);
-            views.mIconRefine.setTag(views.mText1.getText());
-            views.mIconRefine.setOnClickListener(this);
-        } else {
-            views.mIconRefine.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    public void onClick(View v) {
-        Object tag = v.getTag();
-        if (tag instanceof CharSequence) {
-            mSearchView.onQueryRefine((CharSequence) tag);
-        }
-    }
-
-    private CharSequence formatUrl(CharSequence url) {
-        if (mUrlColor == null) {
-            // Lazily get the URL color from the current theme.
-            TypedValue colorValue = new TypedValue();
-            mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true);
-            mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId);
-        }
-
-        SpannableString text = new SpannableString(url);
-        text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null),
-                0, url.length(),
-                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        return text;
-    }
-
-    private void setViewText(TextView v, CharSequence text) {
-        // Set the text even if it's null, since we need to clear any previous text.
-        v.setText(text);
-
-        if (TextUtils.isEmpty(text)) {
-            v.setVisibility(View.GONE);
-        } else {
-            v.setVisibility(View.VISIBLE);
-        }
-    }
-
-    private Drawable getIcon1(Cursor cursor) {
-        if (mIconName1Col == INVALID_INDEX) {
-            return null;
-        }
-        String value = cursor.getString(mIconName1Col);
-        Drawable drawable = getDrawableFromResourceValue(value);
-        if (drawable != null) {
-            return drawable;
-        }
-        return getDefaultIcon1(cursor);
-    }
-
-    private Drawable getIcon2(Cursor cursor) {
-        if (mIconName2Col == INVALID_INDEX) {
-            return null;
-        }
-        String value = cursor.getString(mIconName2Col);
-        return getDrawableFromResourceValue(value);
-    }
-
-    /**
-     * Sets the drawable in an image view, makes sure the view is only visible if there
-     * is a drawable.
-     */
-    private void setViewDrawable(ImageView v, Drawable drawable, int nullVisibility) {
-        // Set the icon even if the drawable is null, since we need to clear any
-        // previous icon.
-        v.setImageDrawable(drawable);
-
-        if (drawable == null) {
-            v.setVisibility(nullVisibility);
-        } else {
-            v.setVisibility(View.VISIBLE);
-
-            // This is a hack to get any animated drawables (like a 'working' spinner)
-            // to animate. You have to setVisible true on an AnimationDrawable to get
-            // it to start animating, but it must first have been false or else the
-            // call to setVisible will be ineffective. We need to clear up the story
-            // about animated drawables in the future, see http://b/1878430.
-            drawable.setVisible(false, false);
-            drawable.setVisible(true, false);
-        }
-    }
-
-    /**
-     * Gets the text to show in the query field when a suggestion is selected.
-     *
-     * @param cursor The Cursor to read the suggestion data from. The Cursor should already
-     *        be moved to the suggestion that is to be read from.
-     * @return The text to show, or <code>null</code> if the query should not be
-     *         changed when selecting this suggestion.
-     */
-    @Override
-    public CharSequence convertToString(Cursor cursor) {
-        if (cursor == null) {
-            return null;
-        }
-
-        String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY);
-        if (query != null) {
-            return query;
-        }
-
-        if (mSearchable.shouldRewriteQueryFromData()) {
-            String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
-            if (data != null) {
-                return data;
-            }
-        }
-
-        if (mSearchable.shouldRewriteQueryFromText()) {
-            String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
-            if (text1 != null) {
-                return text1;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * This method is overridden purely to provide a bit of protection against
-     * flaky content providers.
-     *
-     * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
-     */
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        try {
-            return super.getView(position, convertView, parent);
-        } catch (RuntimeException e) {
-            Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
-            // Put exception string in item title
-            View v = newView(mContext, mCursor, parent);
-            if (v != null) {
-                ChildViewCache views = (ChildViewCache) v.getTag();
-                TextView tv = views.mText1;
-                tv.setText(e.toString());
-            }
-            return v;
-        }
-    }
-
-    /**
-     * This method is overridden purely to provide a bit of protection against
-     * flaky content providers.
-     *
-     * @see android.widget.CursorAdapter#getDropDownView(int, View, ViewGroup)
-     */
-    @Override
-    public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        try {
-            return super.getDropDownView(position, convertView, parent);
-        } catch (RuntimeException e) {
-            Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
-            // Put exception string in item title
-            final View v = newDropDownView(mContext, mCursor, parent);
-            if (v != null) {
-                final ChildViewCache views = (ChildViewCache) v.getTag();
-                final TextView tv = views.mText1;
-                tv.setText(e.toString());
-            }
-            return v;
-        }
-    }
-
-    /**
-     * Gets a drawable given a value provided by a suggestion provider.
-     *
-     * This value could be just the string value of a resource id
-     * (e.g., "2130837524"), in which case we will try to retrieve a drawable from
-     * the provider's resources. If the value is not an integer, it is
-     * treated as a Uri and opened with
-     * {@link ContentResolver#openOutputStream(android.net.Uri, String)}.
-     *
-     * All resources and URIs are read using the suggestion provider's context.
-     *
-     * If the string is not formatted as expected, or no drawable can be found for
-     * the provided value, this method returns null.
-     *
-     * @param drawableId a string like "2130837524",
-     *        "android.resource://com.android.alarmclock/2130837524",
-     *        or "content://contacts/photos/253".
-     * @return a Drawable, or null if none found
-     */
-    private Drawable getDrawableFromResourceValue(String drawableId) {
-        if (drawableId == null || drawableId.isEmpty() || "0".equals(drawableId)) {
-            return null;
-        }
-        try {
-            // First, see if it's just an integer
-            int resourceId = Integer.parseInt(drawableId);
-            // It's an int, look for it in the cache
-            String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE
-                    + "://" + mProviderContext.getPackageName() + "/" + resourceId;
-            // Must use URI as cache key, since ints are app-specific
-            Drawable drawable = checkIconCache(drawableUri);
-            if (drawable != null) {
-                return drawable;
-            }
-            // Not cached, find it by resource ID
-            drawable = ContextCompat.getDrawable(mProviderContext, resourceId);
-            // Stick it in the cache, using the URI as key
-            storeInIconCache(drawableUri, drawable);
-            return drawable;
-        } catch (NumberFormatException nfe) {
-            // It's not an integer, use it as a URI
-            Drawable drawable = checkIconCache(drawableId);
-            if (drawable != null) {
-                return drawable;
-            }
-            Uri uri = Uri.parse(drawableId);
-            drawable = getDrawable(uri);
-            storeInIconCache(drawableId, drawable);
-            return drawable;
-        } catch (Resources.NotFoundException nfe) {
-            // It was an integer, but it couldn't be found, bail out
-            Log.w(LOG_TAG, "Icon resource not found: " + drawableId);
-            return null;
-        }
-    }
-
-    /**
-     * Gets a drawable by URI, without using the cache.
-     *
-     * @return A drawable, or {@code null} if the drawable could not be loaded.
-     */
-    private Drawable getDrawable(Uri uri) {
-        try {
-            String scheme = uri.getScheme();
-            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
-                // Load drawables through Resources, to get the source density information
-                try {
-                    return getDrawableFromResourceUri(uri);
-                } catch (Resources.NotFoundException ex) {
-                    throw new FileNotFoundException("Resource does not exist: " + uri);
-                }
-            } else {
-                // Let the ContentResolver handle content and file URIs.
-                InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
-                if (stream == null) {
-                    throw new FileNotFoundException("Failed to open " + uri);
-                }
-                try {
-                    return Drawable.createFromStream(stream, null);
-                } finally {
-                    try {
-                        stream.close();
-                    } catch (IOException ex) {
-                        Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
-                    }
-                }
-            }
-        } catch (FileNotFoundException fnfe) {
-            Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
-            return null;
-        }
-    }
-
-
-
-    private Drawable checkIconCache(String resourceUri) {
-        Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri);
-        if (cached == null) {
-            return null;
-        }
-        if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri);
-        return cached.newDrawable();
-    }
-
-    private void storeInIconCache(String resourceUri, Drawable drawable) {
-        if (drawable != null) {
-            mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState());
-        }
-    }
-
-    /**
-     * Gets the left-hand side icon that will be used for the current suggestion
-     * if the suggestion contains an icon column but no icon or a broken icon.
-     *
-     * @param cursor A cursor positioned at the current suggestion.
-     * @return A non-null drawable.
-     */
-    private Drawable getDefaultIcon1(Cursor cursor) {
-        // Check the component that gave us the suggestion
-        Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity());
-        if (drawable != null) {
-            return drawable;
-        }
-
-        // Fall back to a default icon
-        return mContext.getPackageManager().getDefaultActivityIcon();
-    }
-
-    /**
-     * Gets the activity or application icon for an activity.
-     * Uses the local icon cache for fast repeated lookups.
-     *
-     * @param component Name of an activity.
-     * @return A drawable, or {@code null} if neither the activity nor the application
-     *         has an icon set.
-     */
-    private Drawable getActivityIconWithCache(ComponentName component) {
-        // First check the icon cache
-        String componentIconKey = component.flattenToShortString();
-        // Using containsKey() since we also store null values.
-        if (mOutsideDrawablesCache.containsKey(componentIconKey)) {
-            Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey);
-            return cached == null ? null : cached.newDrawable(mProviderContext.getResources());
-        }
-        // Then try the activity or application icon
-        Drawable drawable = getActivityIcon(component);
-        // Stick it in the cache so we don't do this lookup again.
-        Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState();
-        mOutsideDrawablesCache.put(componentIconKey, toCache);
-        return drawable;
-    }
-
-    /**
-     * Gets the activity or application icon for an activity.
-     *
-     * @param component Name of an activity.
-     * @return A drawable, or {@code null} if neither the activity or the application
-     *         have an icon set.
-     */
-    private Drawable getActivityIcon(ComponentName component) {
-        PackageManager pm = mContext.getPackageManager();
-        final ActivityInfo activityInfo;
-        try {
-            activityInfo = pm.getActivityInfo(component, PackageManager.GET_META_DATA);
-        } catch (NameNotFoundException ex) {
-            Log.w(LOG_TAG, ex.toString());
-            return null;
-        }
-        int iconId = activityInfo.getIconResource();
-        if (iconId == 0) return null;
-        String pkg = component.getPackageName();
-        Drawable drawable = pm.getDrawable(pkg, iconId, activityInfo.applicationInfo);
-        if (drawable == null) {
-            Log.w(LOG_TAG, "Invalid icon resource " + iconId + " for "
-                    + component.flattenToShortString());
-            return null;
-        }
-        return drawable;
-    }
-
-    /**
-     * Gets the value of a string column by name.
-     *
-     * @param cursor Cursor to read the value from.
-     * @param columnName The name of the column to read.
-     * @return The value of the given column, or <code>null</null>
-     *         if the cursor does not contain the given column.
-     */
-    public static String getColumnString(Cursor cursor, String columnName) {
-        int col = cursor.getColumnIndex(columnName);
-        return getStringOrNull(cursor, col);
-    }
-
-    private static String getStringOrNull(Cursor cursor, int col) {
-        if (col == INVALID_INDEX) {
-            return null;
-        }
-        try {
-            return cursor.getString(col);
-        } catch (Exception e) {
-            Log.e(LOG_TAG,
-                    "unexpected error retrieving valid column from cursor, "
-                            + "did the remote process die?", e);
-            return null;
-        }
-    }
-
-    /**
-     * Import of hidden method: ContentResolver.getResourceId(Uri).
-     * Modified to return a drawable, rather than a hidden type.
-     */
-    Drawable getDrawableFromResourceUri(Uri uri) throws FileNotFoundException {
-        String authority = uri.getAuthority();
-        Resources r;
-        if (TextUtils.isEmpty(authority)) {
-            throw new FileNotFoundException("No authority: " + uri);
-        } else {
-            try {
-                r = mContext.getPackageManager().getResourcesForApplication(authority);
-            } catch (NameNotFoundException ex) {
-                throw new FileNotFoundException("No package found for authority: " + uri);
-            }
-        }
-        List<String> path = uri.getPathSegments();
-        if (path == null) {
-            throw new FileNotFoundException("No path: " + uri);
-        }
-        int len = path.size();
-        int id;
-        if (len == 1) {
-            try {
-                id = Integer.parseInt(path.get(0));
-            } catch (NumberFormatException e) {
-                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
-            }
-        } else if (len == 2) {
-            id = r.getIdentifier(path.get(1), path.get(0), authority);
-        } else {
-            throw new FileNotFoundException("More than two path segments: " + uri);
-        }
-        if (id == 0) {
-            throw new FileNotFoundException("No resource found for: " + uri);
-        }
-        return r.getDrawable(id);
-    }
-
-    /**
-     * Import of hidden method: SearchManager.getSuggestions(SearchableInfo, String, int).
-     */
-    Cursor getSearchManagerSuggestions(SearchableInfo searchable, String query, int limit) {
-        if (searchable == null) {
-            return null;
-        }
-
-        String authority = searchable.getSuggestAuthority();
-        if (authority == null) {
-            return null;
-        }
-
-        Uri.Builder uriBuilder = new Uri.Builder()
-                .scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(authority)
-                .query("")  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
-                .fragment("");  // TODO: Remove, workaround for a bug in Uri.writeToParcel()
-
-        // if content path provided, insert it now
-        final String contentPath = searchable.getSuggestPath();
-        if (contentPath != null) {
-            uriBuilder.appendEncodedPath(contentPath);
-        }
-
-        // append standard suggestion query path
-        uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
-
-        // get the query selection, may be null
-        String selection = searchable.getSuggestSelection();
-        // inject query, either as selection args or inline
-        String[] selArgs = null;
-        if (selection != null) {    // use selection if provided
-            selArgs = new String[] { query };
-        } else {                    // no selection, use REST pattern
-            uriBuilder.appendPath(query);
-        }
-
-        if (limit > 0) {
-            uriBuilder.appendQueryParameter("limit", String.valueOf(limit));
-        }
-
-        Uri uri = uriBuilder.build();
-
-        // finally, make the query
-        return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/SwitchCompat.java b/android/support/v7/widget/SwitchCompat.java
deleted file mode 100644
index b5fa587..0000000
--- a/android/support/v7/widget/SwitchCompat.java
+++ /dev/null
@@ -1,1414 +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.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.text.AllCapsTransformationMethod;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.method.TransformationMethod;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.SoundEffectConstants;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.CompoundButton;
-
-/**
- * SwitchCompat is a version of the Switch widget which on devices back to API v7. It does not
- * make any attempt to use the platform provided widget on those devices which it is available
- * normally.
- * <p>
- * A Switch is a two-state toggle switch widget that can select between two
- * options. The user may drag the "thumb" back and forth to choose the selected option,
- * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
- * property controls the text displayed in the label for the switch, whereas the
- * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
- * controls the text on the thumb. Similarly, the
- * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
- * setTypeface() methods control the typeface and style of label text, whereas the
- * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
- * the related setSwitchTypeface() methods control that of the thumb.
- *
- * <p>See the <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a>
- * guide.</p>
- *
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOn
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOff
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchMinWidth
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchPadding
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchTextAppearance
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTextPadding
- * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
- */
-public class SwitchCompat extends CompoundButton {
-    private static final int THUMB_ANIMATION_DURATION = 250;
-
-    private static final int TOUCH_MODE_IDLE = 0;
-    private static final int TOUCH_MODE_DOWN = 1;
-    private static final int TOUCH_MODE_DRAGGING = 2;
-
-    // We force the accessibility events to have a class name of Switch, since screen readers
-    // already know how to handle their events
-    private static final String ACCESSIBILITY_EVENT_CLASS_NAME = "android.widget.Switch";
-
-    // Enum for the "typeface" XML parameter.
-    private static final int SANS = 1;
-    private static final int SERIF = 2;
-    private static final int MONOSPACE = 3;
-
-    private static final Property<SwitchCompat, Float> THUMB_POS =
-            new Property<SwitchCompat, Float>(Float.class, "thumbPos") {
-                @Override
-                public Float get(SwitchCompat object) {
-                    return object.mThumbPosition;
-                }
-
-                @Override
-                public void set(SwitchCompat object, Float value) {
-                    object.setThumbPosition(value);
-                }
-            };
-
-    private Drawable mThumbDrawable;
-    private ColorStateList mThumbTintList = null;
-    private PorterDuff.Mode mThumbTintMode = null;
-    private boolean mHasThumbTint = false;
-    private boolean mHasThumbTintMode = false;
-
-    private Drawable mTrackDrawable;
-    private ColorStateList mTrackTintList = null;
-    private PorterDuff.Mode mTrackTintMode = null;
-    private boolean mHasTrackTint = false;
-    private boolean mHasTrackTintMode = false;
-
-    private int mThumbTextPadding;
-    private int mSwitchMinWidth;
-    private int mSwitchPadding;
-    private boolean mSplitTrack;
-    private CharSequence mTextOn;
-    private CharSequence mTextOff;
-    private boolean mShowText;
-
-    private int mTouchMode;
-    private int mTouchSlop;
-    private float mTouchX;
-    private float mTouchY;
-    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
-    private int mMinFlingVelocity;
-
-    private float mThumbPosition;
-
-    /**
-     * Width required to draw the switch track and thumb. Includes padding and
-     * optical bounds for both the track and thumb.
-     */
-    private int mSwitchWidth;
-
-    /**
-     * Height required to draw the switch track and thumb. Includes padding and
-     * optical bounds for both the track and thumb.
-     */
-    private int mSwitchHeight;
-
-    /**
-     * Width of the thumb's content region. Does not include padding or
-     * optical bounds.
-     */
-    private int mThumbWidth;
-
-    /** Left bound for drawing the switch track and thumb. */
-    private int mSwitchLeft;
-
-    /** Top bound for drawing the switch track and thumb. */
-    private int mSwitchTop;
-
-    /** Right bound for drawing the switch track and thumb. */
-    private int mSwitchRight;
-
-    /** Bottom bound for drawing the switch track and thumb. */
-    private int mSwitchBottom;
-
-    private final TextPaint mTextPaint;
-    private ColorStateList mTextColors;
-    private Layout mOnLayout;
-    private Layout mOffLayout;
-    private TransformationMethod mSwitchTransformationMethod;
-    ObjectAnimator mPositionAnimator;
-
-    @SuppressWarnings("hiding")
-    private final Rect mTempRect = new Rect();
-
-    private static final int[] CHECKED_STATE_SET = {
-            android.R.attr.state_checked
-    };
-
-    /**
-     * Construct a new Switch with default styling.
-     *
-     * @param context The Context that will determine this widget's theming.
-     */
-    public SwitchCompat(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Construct a new Switch with default styling, overriding specific style
-     * attributes as requested.
-     *
-     * @param context The Context that will determine this widget's theming.
-     * @param attrs Specification of attributes that should deviate from default styling.
-     */
-    public SwitchCompat(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.switchStyle);
-    }
-
-    /**
-     * Construct a new Switch with a default style determined by the given theme attribute,
-     * overriding specific style attributes as requested.
-     *
-     * @param context The Context that will determine this widget's theming.
-     * @param attrs Specification of attributes that should deviate from the default styling.
-     * @param defStyleAttr 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 SwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
-
-        final Resources res = getResources();
-        mTextPaint.density = res.getDisplayMetrics().density;
-
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
-                attrs, R.styleable.SwitchCompat, defStyleAttr, 0);
-        mThumbDrawable = a.getDrawable(R.styleable.SwitchCompat_android_thumb);
-        if (mThumbDrawable != null) {
-            mThumbDrawable.setCallback(this);
-        }
-        mTrackDrawable = a.getDrawable(R.styleable.SwitchCompat_track);
-        if (mTrackDrawable != null) {
-            mTrackDrawable.setCallback(this);
-        }
-        mTextOn = a.getText(R.styleable.SwitchCompat_android_textOn);
-        mTextOff = a.getText(R.styleable.SwitchCompat_android_textOff);
-        mShowText = a.getBoolean(R.styleable.SwitchCompat_showText, true);
-        mThumbTextPadding = a.getDimensionPixelSize(
-                R.styleable.SwitchCompat_thumbTextPadding, 0);
-        mSwitchMinWidth = a.getDimensionPixelSize(
-                R.styleable.SwitchCompat_switchMinWidth, 0);
-        mSwitchPadding = a.getDimensionPixelSize(
-                R.styleable.SwitchCompat_switchPadding, 0);
-        mSplitTrack = a.getBoolean(R.styleable.SwitchCompat_splitTrack, false);
-
-        ColorStateList thumbTintList = a.getColorStateList(R.styleable.SwitchCompat_thumbTint);
-        if (thumbTintList != null) {
-            mThumbTintList = thumbTintList;
-            mHasThumbTint = true;
-        }
-        PorterDuff.Mode thumbTintMode = DrawableUtils.parseTintMode(
-                a.getInt(R.styleable.SwitchCompat_thumbTintMode, -1), null);
-        if (mThumbTintMode != thumbTintMode) {
-            mThumbTintMode = thumbTintMode;
-            mHasThumbTintMode = true;
-        }
-        if (mHasThumbTint || mHasThumbTintMode) {
-            applyThumbTint();
-        }
-
-        ColorStateList trackTintList = a.getColorStateList(R.styleable.SwitchCompat_trackTint);
-        if (trackTintList != null) {
-            mTrackTintList = trackTintList;
-            mHasTrackTint = true;
-        }
-        PorterDuff.Mode trackTintMode = DrawableUtils.parseTintMode(
-                a.getInt(R.styleable.SwitchCompat_trackTintMode, -1), null);
-        if (mTrackTintMode != trackTintMode) {
-            mTrackTintMode = trackTintMode;
-            mHasTrackTintMode = true;
-        }
-        if (mHasTrackTint || mHasTrackTintMode) {
-            applyTrackTint();
-        }
-
-        final int appearance = a.getResourceId(
-                R.styleable.SwitchCompat_switchTextAppearance, 0);
-        if (appearance != 0) {
-            setSwitchTextAppearance(context, appearance);
-        }
-
-        a.recycle();
-
-        final ViewConfiguration config = ViewConfiguration.get(context);
-        mTouchSlop = config.getScaledTouchSlop();
-        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
-
-        // Refresh display with current params
-        refreshDrawableState();
-        setChecked(isChecked());
-    }
-
-    /**
-     * Sets the switch text color, size, style, hint color, and highlight color
-     * from the specified TextAppearance resource.
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchTextAppearance
-     */
-    public void setSwitchTextAppearance(Context context, int resid) {
-        final TintTypedArray appearance = TintTypedArray.obtainStyledAttributes(context, resid,
-                R.styleable.TextAppearance);
-
-        ColorStateList colors;
-        int ts;
-
-        colors = appearance.getColorStateList(R.styleable.TextAppearance_android_textColor);
-        if (colors != null) {
-            mTextColors = colors;
-        } else {
-            // If no color set in TextAppearance, default to the view's textColor
-            mTextColors = getTextColors();
-        }
-
-        ts = appearance.getDimensionPixelSize(R.styleable.TextAppearance_android_textSize, 0);
-        if (ts != 0) {
-            if (ts != mTextPaint.getTextSize()) {
-                mTextPaint.setTextSize(ts);
-                requestLayout();
-            }
-        }
-
-        int typefaceIndex, styleIndex;
-        typefaceIndex = appearance.getInt(R.styleable.TextAppearance_android_typeface, -1);
-        styleIndex = appearance.getInt(R.styleable.TextAppearance_android_textStyle, -1);
-
-        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
-
-        boolean allCaps = appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
-        if (allCaps) {
-            mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
-        } else {
-            mSwitchTransformationMethod = null;
-        }
-
-        appearance.recycle();
-    }
-
-    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
-        Typeface tf = null;
-        switch (typefaceIndex) {
-            case SANS:
-                tf = Typeface.SANS_SERIF;
-                break;
-
-            case SERIF:
-                tf = Typeface.SERIF;
-                break;
-
-            case MONOSPACE:
-                tf = Typeface.MONOSPACE;
-                break;
-        }
-
-        setSwitchTypeface(tf, styleIndex);
-    }
-
-    /**
-     * Sets the typeface and style in which the text should be displayed on the
-     * switch, and turns on the fake bold and italic bits in the Paint if the
-     * Typeface that you provided does not have all the bits in the
-     * style that you specified.
-     */
-    public void setSwitchTypeface(Typeface tf, int style) {
-        if (style > 0) {
-            if (tf == null) {
-                tf = Typeface.defaultFromStyle(style);
-            } else {
-                tf = Typeface.create(tf, style);
-            }
-
-            setSwitchTypeface(tf);
-            // now compute what (if any) algorithmic styling is needed
-            int typefaceStyle = tf != null ? tf.getStyle() : 0;
-            int need = style & ~typefaceStyle;
-            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
-            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
-        } else {
-            mTextPaint.setFakeBoldText(false);
-            mTextPaint.setTextSkewX(0);
-            setSwitchTypeface(tf);
-        }
-    }
-
-    /**
-     * Sets the typeface in which the text should be displayed on the switch.
-     * Note that not all Typeface families actually have bold and italic
-     * variants, so you may need to use
-     * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
-     * that you actually want.
-     */
-    public void setSwitchTypeface(Typeface typeface) {
-        if ((mTextPaint.getTypeface() != null && !mTextPaint.getTypeface().equals(typeface))
-                || (mTextPaint.getTypeface() == null && typeface != null)) {
-            mTextPaint.setTypeface(typeface);
-
-            requestLayout();
-            invalidate();
-        }
-    }
-
-    /**
-     * Set the amount of horizontal padding between the switch and the associated text.
-     *
-     * @param pixels Amount of padding in pixels
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchPadding
-     */
-    public void setSwitchPadding(int pixels) {
-        mSwitchPadding = pixels;
-        requestLayout();
-    }
-
-    /**
-     * Get the amount of horizontal padding between the switch and the associated text.
-     *
-     * @return Amount of padding in pixels
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchPadding
-     */
-    public int getSwitchPadding() {
-        return mSwitchPadding;
-    }
-
-    /**
-     * Set the minimum width of the switch in pixels. The switch's width will be the maximum
-     * of this value and its measured width as determined by the switch drawables and text used.
-     *
-     * @param pixels Minimum width of the switch in pixels
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchMinWidth
-     */
-    public void setSwitchMinWidth(int pixels) {
-        mSwitchMinWidth = pixels;
-        requestLayout();
-    }
-
-    /**
-     * Get the minimum width of the switch in pixels. The switch's width will be the maximum
-     * of this value and its measured width as determined by the switch drawables and text used.
-     *
-     * @return Minimum width of the switch in pixels
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchMinWidth
-     */
-    public int getSwitchMinWidth() {
-        return mSwitchMinWidth;
-    }
-
-    /**
-     * Set the horizontal padding around the text drawn on the switch itself.
-     *
-     * @param pixels Horizontal padding for switch thumb text in pixels
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTextPadding
-     */
-    public void setThumbTextPadding(int pixels) {
-        mThumbTextPadding = pixels;
-        requestLayout();
-    }
-
-    /**
-     * Get the horizontal padding around the text drawn on the switch itself.
-     *
-     * @return Horizontal padding for switch thumb text in pixels
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTextPadding
-     */
-    public int getThumbTextPadding() {
-        return mThumbTextPadding;
-    }
-
-    /**
-     * Set the drawable used for the track that the switch slides within.
-     *
-     * @param track Track drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
-     */
-    public void setTrackDrawable(Drawable track) {
-        if (mTrackDrawable != null) {
-            mTrackDrawable.setCallback(null);
-        }
-        mTrackDrawable = track;
-        if (track != null) {
-            track.setCallback(this);
-        }
-        requestLayout();
-    }
-
-    /**
-     * Set the drawable used for the track that the switch slides within.
-     *
-     * @param resId Resource ID of a track drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
-     */
-    public void setTrackResource(int resId) {
-        setTrackDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    /**
-     * Get the drawable used for the track that the switch slides within.
-     *
-     * @return Track drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
-     */
-    public Drawable getTrackDrawable() {
-        return mTrackDrawable;
-    }
-
-    /**
-     * Applies a tint to the track drawable. Does not modify the current
-     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
-     * <p>
-     * Subsequent calls to {@link #setTrackDrawable(Drawable)} will
-     * automatically mutate the drawable and apply the specified tint and tint
-     * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTint
-     * @see #getTrackTintList()
-     */
-    public void setTrackTintList(@Nullable ColorStateList tint) {
-        mTrackTintList = tint;
-        mHasTrackTint = true;
-
-        applyTrackTint();
-    }
-
-    /**
-     * @return the tint applied to the track drawable
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTint
-     * @see #setTrackTintList(ColorStateList)
-     */
-    @Nullable
-    public ColorStateList getTrackTintList() {
-        return mTrackTintList;
-    }
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setTrackTintList(ColorStateList)}} to the track 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
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTintMode
-     * @see #getTrackTintMode()
-     */
-    public void setTrackTintMode(@Nullable PorterDuff.Mode tintMode) {
-        mTrackTintMode = tintMode;
-        mHasTrackTintMode = true;
-
-        applyTrackTint();
-    }
-
-    /**
-     * @return the blending mode used to apply the tint to the track
-     *         drawable
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTintMode
-     * @see #setTrackTintMode(PorterDuff.Mode)
-     */
-    @Nullable
-    public PorterDuff.Mode getTrackTintMode() {
-        return mTrackTintMode;
-    }
-
-    private void applyTrackTint() {
-        if (mTrackDrawable != null && (mHasTrackTint || mHasTrackTintMode)) {
-            mTrackDrawable = mTrackDrawable.mutate();
-
-            if (mHasTrackTint) {
-                DrawableCompat.setTintList(mTrackDrawable, mTrackTintList);
-            }
-
-            if (mHasTrackTintMode) {
-                DrawableCompat.setTintMode(mTrackDrawable, mTrackTintMode);
-            }
-
-            // The drawable (or one of its children) may not have been
-            // stateful before applying the tint, so let's try again.
-            if (mTrackDrawable.isStateful()) {
-                mTrackDrawable.setState(getDrawableState());
-            }
-        }
-    }
-
-    /**
-     * Set the drawable used for the switch "thumb" - the piece that the user
-     * can physically touch and drag along the track.
-     *
-     * @param thumb Thumb drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
-     */
-    public void setThumbDrawable(Drawable thumb) {
-        if (mThumbDrawable != null) {
-            mThumbDrawable.setCallback(null);
-        }
-        mThumbDrawable = thumb;
-        if (thumb != null) {
-            thumb.setCallback(this);
-        }
-        requestLayout();
-    }
-
-    /**
-     * Set the drawable used for the switch "thumb" - the piece that the user
-     * can physically touch and drag along the track.
-     *
-     * @param resId Resource ID of a thumb drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
-     */
-    public void setThumbResource(int resId) {
-        setThumbDrawable(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    /**
-     * Get the drawable used for the switch "thumb" - the piece that the user
-     * can physically touch and drag along the track.
-     *
-     * @return Thumb drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
-     */
-    public Drawable getThumbDrawable() {
-        return mThumbDrawable;
-    }
-
-    /**
-     * Applies a tint to the thumb drawable. Does not modify the current
-     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
-     * <p>
-     * Subsequent calls to {@link #setThumbDrawable(Drawable)} will
-     * automatically mutate the drawable and apply the specified tint and tint
-     * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTint
-     * @see #getThumbTintList()
-     * @see Drawable#setTintList(ColorStateList)
-     */
-    public void setThumbTintList(@Nullable ColorStateList tint) {
-        mThumbTintList = tint;
-        mHasThumbTint = true;
-
-        applyThumbTint();
-    }
-
-    /**
-     * @return the tint applied to the thumb drawable
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTint
-     * @see #setThumbTintList(ColorStateList)
-     */
-    @Nullable
-    public ColorStateList getThumbTintList() {
-        return mThumbTintList;
-    }
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setThumbTintList(ColorStateList)}} to the thumb 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
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTintMode
-     * @see #getThumbTintMode()
-     * @see Drawable#setTintMode(PorterDuff.Mode)
-     */
-    public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
-        mThumbTintMode = tintMode;
-        mHasThumbTintMode = true;
-
-        applyThumbTint();
-    }
-
-    /**
-     * @return the blending mode used to apply the tint to the thumb
-     *         drawable
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTintMode
-     * @see #setThumbTintMode(PorterDuff.Mode)
-     */
-    @Nullable
-    public PorterDuff.Mode getThumbTintMode() {
-        return mThumbTintMode;
-    }
-
-    private void applyThumbTint() {
-        if (mThumbDrawable != null && (mHasThumbTint || mHasThumbTintMode)) {
-            mThumbDrawable = mThumbDrawable.mutate();
-
-            if (mHasThumbTint) {
-                DrawableCompat.setTintList(mThumbDrawable, mThumbTintList);
-            }
-
-            if (mHasThumbTintMode) {
-                DrawableCompat.setTintMode(mThumbDrawable, mThumbTintMode);
-            }
-
-            // The drawable (or one of its children) may not have been
-            // stateful before applying the tint, so let's try again.
-            if (mThumbDrawable.isStateful()) {
-                mThumbDrawable.setState(getDrawableState());
-            }
-        }
-    }
-
-    /**
-     * Specifies whether the track should be split by the thumb. When true,
-     * the thumb's optical bounds will be clipped out of the track drawable,
-     * then the thumb will be drawn into the resulting gap.
-     *
-     * @param splitTrack Whether the track should be split by the thumb
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_splitTrack
-     */
-    public void setSplitTrack(boolean splitTrack) {
-        mSplitTrack = splitTrack;
-        invalidate();
-    }
-
-    /**
-     * Returns whether the track should be split by the thumb.
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_splitTrack
-     */
-    public boolean getSplitTrack() {
-        return mSplitTrack;
-    }
-
-    /**
-     * Returns the text displayed when the button is in the checked state.
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOn
-     */
-    public CharSequence getTextOn() {
-        return mTextOn;
-    }
-
-    /**
-     * Sets the text displayed when the button is in the checked state.
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOn
-     */
-    public void setTextOn(CharSequence textOn) {
-        mTextOn = textOn;
-        requestLayout();
-    }
-
-    /**
-     * Returns the text displayed when the button is not in the checked state.
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOff
-     */
-    public CharSequence getTextOff() {
-        return mTextOff;
-    }
-
-    /**
-     * Sets the text displayed when the button is not in the checked state.
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOff
-     */
-    public void setTextOff(CharSequence textOff) {
-        mTextOff = textOff;
-        requestLayout();
-    }
-
-    /**
-     * Sets whether the on/off text should be displayed.
-     *
-     * @param showText {@code true} to display on/off text
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_showText
-     */
-    public void setShowText(boolean showText) {
-        if (mShowText != showText) {
-            mShowText = showText;
-            requestLayout();
-        }
-    }
-
-    /**
-     * @return whether the on/off text should be displayed
-     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_showText
-     */
-    public boolean getShowText() {
-        return mShowText;
-    }
-
-    @Override
-    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mShowText) {
-            if (mOnLayout == null) {
-                mOnLayout = makeLayout(mTextOn);
-            }
-
-            if (mOffLayout == null) {
-                mOffLayout = makeLayout(mTextOff);
-            }
-        }
-
-        final Rect padding = mTempRect;
-        final int thumbWidth;
-        final int thumbHeight;
-        if (mThumbDrawable != null) {
-            // Cached thumb width does not include padding.
-            mThumbDrawable.getPadding(padding);
-            thumbWidth = mThumbDrawable.getIntrinsicWidth() - padding.left - padding.right;
-            thumbHeight = mThumbDrawable.getIntrinsicHeight();
-        } else {
-            thumbWidth = 0;
-            thumbHeight = 0;
-        }
-
-        final int maxTextWidth;
-        if (mShowText) {
-            maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
-                    + mThumbTextPadding * 2;
-        } else {
-            maxTextWidth = 0;
-        }
-
-        mThumbWidth = Math.max(maxTextWidth, thumbWidth);
-
-        final int trackHeight;
-        if (mTrackDrawable != null) {
-            mTrackDrawable.getPadding(padding);
-            trackHeight = mTrackDrawable.getIntrinsicHeight();
-        } else {
-            padding.setEmpty();
-            trackHeight = 0;
-        }
-
-        // Adjust left and right padding to ensure there's enough room for the
-        // thumb's padding (when present).
-        int paddingLeft = padding.left;
-        int paddingRight = padding.right;
-        if (mThumbDrawable != null) {
-            final Rect inset = DrawableUtils.getOpticalBounds(mThumbDrawable);
-            paddingLeft = Math.max(paddingLeft, inset.left);
-            paddingRight = Math.max(paddingRight, inset.right);
-        }
-
-        final int switchWidth = Math.max(mSwitchMinWidth,
-                2 * mThumbWidth + paddingLeft + paddingRight);
-        final int switchHeight = Math.max(trackHeight, thumbHeight);
-        mSwitchWidth = switchWidth;
-        mSwitchHeight = switchHeight;
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        final int measuredHeight = getMeasuredHeight();
-        if (measuredHeight < switchHeight) {
-            setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
-        }
-    }
-
-    @Override
-    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
-        super.onPopulateAccessibilityEvent(event);
-
-        final CharSequence text = isChecked() ? mTextOn : mTextOff;
-        if (text != null) {
-            event.getText().add(text);
-        }
-    }
-
-    private Layout makeLayout(CharSequence text) {
-        final CharSequence transformed = (mSwitchTransformationMethod != null)
-                ? mSwitchTransformationMethod.getTransformation(text, this)
-                : text;
-
-        return new StaticLayout(transformed, mTextPaint,
-                transformed != null ?
-                        (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)) : 0,
-                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
-    }
-
-    /**
-     * @return true if (x, y) is within the target area of the switch thumb
-     */
-    private boolean hitThumb(float x, float y) {
-        if (mThumbDrawable == null) {
-            return false;
-        }
-
-        // Relies on mTempRect, MUST be called first!
-        final int thumbOffset = getThumbOffset();
-
-        mThumbDrawable.getPadding(mTempRect);
-        final int thumbTop = mSwitchTop - mTouchSlop;
-        final int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
-        final int thumbRight = thumbLeft + mThumbWidth +
-                mTempRect.left + mTempRect.right + mTouchSlop;
-        final int thumbBottom = mSwitchBottom + mTouchSlop;
-        return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        mVelocityTracker.addMovement(ev);
-        final int action = ev.getActionMasked();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN: {
-                final float x = ev.getX();
-                final float y = ev.getY();
-                if (isEnabled() && hitThumb(x, y)) {
-                    mTouchMode = TOUCH_MODE_DOWN;
-                    mTouchX = x;
-                    mTouchY = y;
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_MOVE: {
-                switch (mTouchMode) {
-                    case TOUCH_MODE_IDLE:
-                        // Didn't target the thumb, treat normally.
-                        break;
-
-                    case TOUCH_MODE_DOWN: {
-                        final float x = ev.getX();
-                        final float y = ev.getY();
-                        if (Math.abs(x - mTouchX) > mTouchSlop ||
-                                Math.abs(y - mTouchY) > mTouchSlop) {
-                            mTouchMode = TOUCH_MODE_DRAGGING;
-                            getParent().requestDisallowInterceptTouchEvent(true);
-                            mTouchX = x;
-                            mTouchY = y;
-                            return true;
-                        }
-                        break;
-                    }
-
-                    case TOUCH_MODE_DRAGGING: {
-                        final float x = ev.getX();
-                        final int thumbScrollRange = getThumbScrollRange();
-                        final float thumbScrollOffset = x - mTouchX;
-                        float dPos;
-                        if (thumbScrollRange != 0) {
-                            dPos = thumbScrollOffset / thumbScrollRange;
-                        } else {
-                            // If the thumb scroll range is empty, just use the
-                            // movement direction to snap on or off.
-                            dPos = thumbScrollOffset > 0 ? 1 : -1;
-                        }
-                        if (ViewUtils.isLayoutRtl(this)) {
-                            dPos = -dPos;
-                        }
-                        final float newPos = constrain(mThumbPosition + dPos, 0, 1);
-                        if (newPos != mThumbPosition) {
-                            mTouchX = x;
-                            setThumbPosition(newPos);
-                        }
-                        return true;
-                    }
-                }
-                break;
-            }
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                if (mTouchMode == TOUCH_MODE_DRAGGING) {
-                    stopDrag(ev);
-                    // Allow super class to handle pressed state, etc.
-                    super.onTouchEvent(ev);
-                    return true;
-                }
-                mTouchMode = TOUCH_MODE_IDLE;
-                mVelocityTracker.clear();
-                break;
-            }
-        }
-
-        return super.onTouchEvent(ev);
-    }
-
-    private void cancelSuperTouch(MotionEvent ev) {
-        MotionEvent cancel = MotionEvent.obtain(ev);
-        cancel.setAction(MotionEvent.ACTION_CANCEL);
-        super.onTouchEvent(cancel);
-        cancel.recycle();
-    }
-
-    /**
-     * Called from onTouchEvent to end a drag operation.
-     *
-     * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
-     */
-    private void stopDrag(MotionEvent ev) {
-        mTouchMode = TOUCH_MODE_IDLE;
-
-        // Commit the change if the event is up and not canceled and the switch
-        // has not been disabled during the drag.
-        final boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
-        final boolean oldState = isChecked();
-        final boolean newState;
-        if (commitChange) {
-            mVelocityTracker.computeCurrentVelocity(1000);
-            final float xvel = mVelocityTracker.getXVelocity();
-            if (Math.abs(xvel) > mMinFlingVelocity) {
-                newState = ViewUtils.isLayoutRtl(this) ? (xvel < 0) : (xvel > 0);
-            } else {
-                newState = getTargetCheckedState();
-            }
-        } else {
-            newState = oldState;
-        }
-
-        if (newState != oldState) {
-            playSoundEffect(SoundEffectConstants.CLICK);
-        }
-        // Always call setChecked so that the thumb is moved back to the correct edge
-        setChecked(newState);
-        cancelSuperTouch(ev);
-    }
-
-    private void animateThumbToCheckedState(final boolean newCheckedState) {
-        final float targetPosition = newCheckedState ? 1 : 0;
-        mPositionAnimator = ObjectAnimator.ofFloat(this, THUMB_POS, targetPosition);
-        mPositionAnimator.setDuration(THUMB_ANIMATION_DURATION);
-        if (Build.VERSION.SDK_INT >= 18) {
-            mPositionAnimator.setAutoCancel(true);
-        }
-        mPositionAnimator.start();
-    }
-
-    private void cancelPositionAnimator() {
-        if (mPositionAnimator != null) {
-            mPositionAnimator.cancel();
-        }
-    }
-
-    private boolean getTargetCheckedState() {
-        return mThumbPosition > 0.5f;
-    }
-
-    /**
-     * Sets the thumb position as a decimal value between 0 (off) and 1 (on).
-     *
-     * @param position new position between [0,1]
-     */
-    void setThumbPosition(float position) {
-        mThumbPosition = position;
-        invalidate();
-    }
-
-    @Override
-    public void toggle() {
-        setChecked(!isChecked());
-    }
-
-    @Override
-    public void setChecked(boolean checked) {
-        super.setChecked(checked);
-
-        // Calling the super method may result in setChecked() getting called
-        // recursively with a different value, so load the REAL value...
-        checked = isChecked();
-
-        if (getWindowToken() != null && ViewCompat.isLaidOut(this)) {
-            animateThumbToCheckedState(checked);
-        } else {
-            // Immediately move the thumb to the new position.
-            cancelPositionAnimator();
-            setThumbPosition(checked ? 1 : 0);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        int opticalInsetLeft = 0;
-        int opticalInsetRight = 0;
-        if (mThumbDrawable != null) {
-            final Rect trackPadding = mTempRect;
-            if (mTrackDrawable != null) {
-                mTrackDrawable.getPadding(trackPadding);
-            } else {
-                trackPadding.setEmpty();
-            }
-
-            final Rect insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
-            opticalInsetLeft = Math.max(0, insets.left - trackPadding.left);
-            opticalInsetRight = Math.max(0, insets.right - trackPadding.right);
-        }
-
-        final int switchRight;
-        final int switchLeft;
-        if (ViewUtils.isLayoutRtl(this)) {
-            switchLeft = getPaddingLeft() + opticalInsetLeft;
-            switchRight = switchLeft + mSwitchWidth - opticalInsetLeft - opticalInsetRight;
-        } else {
-            switchRight = getWidth() - getPaddingRight() - opticalInsetRight;
-            switchLeft = switchRight - mSwitchWidth + opticalInsetLeft + opticalInsetRight;
-        }
-
-        final int switchTop;
-        final int switchBottom;
-        switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
-            default:
-            case Gravity.TOP:
-                switchTop = getPaddingTop();
-                switchBottom = switchTop + mSwitchHeight;
-                break;
-
-            case Gravity.CENTER_VERTICAL:
-                switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
-                        mSwitchHeight / 2;
-                switchBottom = switchTop + mSwitchHeight;
-                break;
-
-            case Gravity.BOTTOM:
-                switchBottom = getHeight() - getPaddingBottom();
-                switchTop = switchBottom - mSwitchHeight;
-                break;
-        }
-
-        mSwitchLeft = switchLeft;
-        mSwitchTop = switchTop;
-        mSwitchBottom = switchBottom;
-        mSwitchRight = switchRight;
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        final Rect padding = mTempRect;
-        final int switchLeft = mSwitchLeft;
-        final int switchTop = mSwitchTop;
-        final int switchRight = mSwitchRight;
-        final int switchBottom = mSwitchBottom;
-
-        int thumbInitialLeft = switchLeft + getThumbOffset();
-
-        final Rect thumbInsets;
-        if (mThumbDrawable != null) {
-            thumbInsets = DrawableUtils.getOpticalBounds(mThumbDrawable);
-        } else {
-            thumbInsets = DrawableUtils.INSETS_NONE;
-        }
-
-        // Layout the track.
-        if (mTrackDrawable != null) {
-            mTrackDrawable.getPadding(padding);
-
-            // Adjust thumb position for track padding.
-            thumbInitialLeft += padding.left;
-
-            // If necessary, offset by the optical insets of the thumb asset.
-            int trackLeft = switchLeft;
-            int trackTop = switchTop;
-            int trackRight = switchRight;
-            int trackBottom = switchBottom;
-            if (thumbInsets != null) {
-                if (thumbInsets.left > padding.left) {
-                    trackLeft += thumbInsets.left - padding.left;
-                }
-                if (thumbInsets.top > padding.top) {
-                    trackTop += thumbInsets.top - padding.top;
-                }
-                if (thumbInsets.right > padding.right) {
-                    trackRight -= thumbInsets.right - padding.right;
-                }
-                if (thumbInsets.bottom > padding.bottom) {
-                    trackBottom -= thumbInsets.bottom - padding.bottom;
-                }
-            }
-            mTrackDrawable.setBounds(trackLeft, trackTop, trackRight, trackBottom);
-        }
-
-        // Layout the thumb.
-        if (mThumbDrawable != null) {
-            mThumbDrawable.getPadding(padding);
-
-            final int thumbLeft = thumbInitialLeft - padding.left;
-            final int thumbRight = thumbInitialLeft + mThumbWidth + padding.right;
-            mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
-
-            final Drawable background = getBackground();
-            if (background != null) {
-                DrawableCompat.setHotspotBounds(background, thumbLeft, switchTop,
-                        thumbRight, switchBottom);
-            }
-        }
-
-        // Draw the background.
-        super.draw(c);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        final Rect padding = mTempRect;
-        final Drawable trackDrawable = mTrackDrawable;
-        if (trackDrawable != null) {
-            trackDrawable.getPadding(padding);
-        } else {
-            padding.setEmpty();
-        }
-
-        final int switchTop = mSwitchTop;
-        final int switchBottom = mSwitchBottom;
-        final int switchInnerTop = switchTop + padding.top;
-        final int switchInnerBottom = switchBottom - padding.bottom;
-
-        final Drawable thumbDrawable = mThumbDrawable;
-        if (trackDrawable != null) {
-            if (mSplitTrack && thumbDrawable != null) {
-                final Rect insets = DrawableUtils.getOpticalBounds(thumbDrawable);
-                thumbDrawable.copyBounds(padding);
-                padding.left += insets.left;
-                padding.right -= insets.right;
-
-                final int saveCount = canvas.save();
-                canvas.clipRect(padding, Region.Op.DIFFERENCE);
-                trackDrawable.draw(canvas);
-                canvas.restoreToCount(saveCount);
-            } else {
-                trackDrawable.draw(canvas);
-            }
-        }
-
-        final int saveCount = canvas.save();
-
-        if (thumbDrawable != null) {
-            thumbDrawable.draw(canvas);
-        }
-
-        final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
-        if (switchText != null) {
-            final int drawableState[] = getDrawableState();
-            if (mTextColors != null) {
-                mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
-            }
-            mTextPaint.drawableState = drawableState;
-
-            final int cX;
-            if (thumbDrawable != null) {
-                final Rect bounds = thumbDrawable.getBounds();
-                cX = bounds.left + bounds.right;
-            } else {
-                cX = getWidth();
-            }
-
-            final int left = cX / 2 - switchText.getWidth() / 2;
-            final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
-            canvas.translate(left, top);
-            switchText.draw(canvas);
-        }
-
-        canvas.restoreToCount(saveCount);
-    }
-
-    @Override
-    public int getCompoundPaddingLeft() {
-        if (!ViewUtils.isLayoutRtl(this)) {
-            return super.getCompoundPaddingLeft();
-        }
-        int padding = super.getCompoundPaddingLeft() + mSwitchWidth;
-        if (!TextUtils.isEmpty(getText())) {
-            padding += mSwitchPadding;
-        }
-        return padding;
-    }
-
-    @Override
-    public int getCompoundPaddingRight() {
-        if (ViewUtils.isLayoutRtl(this)) {
-            return super.getCompoundPaddingRight();
-        }
-        int padding = super.getCompoundPaddingRight() + mSwitchWidth;
-        if (!TextUtils.isEmpty(getText())) {
-            padding += mSwitchPadding;
-        }
-        return padding;
-    }
-
-    /**
-     * Translates thumb position to offset according to current RTL setting and
-     * thumb scroll range. Accounts for both track and thumb padding.
-     *
-     * @return thumb offset
-     */
-    private int getThumbOffset() {
-        final float thumbPosition;
-        if (ViewUtils.isLayoutRtl(this)) {
-            thumbPosition = 1 - mThumbPosition;
-        } else {
-            thumbPosition = mThumbPosition;
-        }
-        return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
-    }
-
-    private int getThumbScrollRange() {
-        if (mTrackDrawable != null) {
-            final Rect padding = mTempRect;
-            mTrackDrawable.getPadding(padding);
-
-            final Rect insets;
-            if (mThumbDrawable != null) {
-                insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
-            } else {
-                insets = DrawableUtils.INSETS_NONE;
-            }
-
-            return mSwitchWidth - mThumbWidth - padding.left - padding.right
-                    - insets.left - insets.right;
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
-        if (isChecked()) {
-            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
-        }
-        return drawableState;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        final int[] state = getDrawableState();
-        boolean changed = false;
-
-        final Drawable thumbDrawable = mThumbDrawable;
-        if (thumbDrawable != null && thumbDrawable.isStateful()) {
-            changed |= thumbDrawable.setState(state);
-        }
-
-        final Drawable trackDrawable = mTrackDrawable;
-        if (trackDrawable != null && trackDrawable.isStateful()) {
-            changed |= trackDrawable.setState(state);
-        }
-
-        if (changed) {
-            invalidate();
-        }
-    }
-
-    @Override
-    public void drawableHotspotChanged(float x, float y) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            super.drawableHotspotChanged(x, y);
-        }
-
-        if (mThumbDrawable != null) {
-            DrawableCompat.setHotspot(mThumbDrawable, x, y);
-        }
-
-        if (mTrackDrawable != null) {
-            DrawableCompat.setHotspot(mTrackDrawable, x, y);
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        super.jumpDrawablesToCurrentState();
-
-        if (mThumbDrawable != null) {
-            mThumbDrawable.jumpToCurrentState();
-        }
-
-        if (mTrackDrawable != null) {
-            mTrackDrawable.jumpToCurrentState();
-        }
-
-        if (mPositionAnimator != null && mPositionAnimator.isStarted()) {
-            mPositionAnimator.end();
-            mPositionAnimator = null;
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-        event.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
-        CharSequence switchText = isChecked() ? mTextOn : mTextOff;
-        if (!TextUtils.isEmpty(switchText)) {
-            CharSequence oldText = info.getText();
-            if (TextUtils.isEmpty(oldText)) {
-                info.setText(switchText);
-            } else {
-                StringBuilder newText = new StringBuilder();
-                newText.append(oldText).append(' ').append(switchText);
-                info.setText(newText);
-            }
-        }
-    }
-
-    /**
-     * Taken from android.util.MathUtils
-     */
-    private static float constrain(float amount, float low, float high) {
-        return amount < low ? low : (amount > high ? high : amount);
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/ThemeUtils.java b/android/support/v7/widget/ThemeUtils.java
deleted file mode 100644
index 02ee49a..0000000
--- a/android/support/v7/widget/ThemeUtils.java
+++ /dev/null
@@ -1,113 +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.widget;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
-import android.util.TypedValue;
-
-class ThemeUtils {
-
-    private static final ThreadLocal<TypedValue> TL_TYPED_VALUE = new ThreadLocal<>();
-
-    static final int[] DISABLED_STATE_SET = new int[]{-android.R.attr.state_enabled};
-    static final int[] FOCUSED_STATE_SET = new int[]{android.R.attr.state_focused};
-    static final int[] ACTIVATED_STATE_SET = new int[]{android.R.attr.state_activated};
-    static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed};
-    static final int[] CHECKED_STATE_SET = new int[]{android.R.attr.state_checked};
-    static final int[] SELECTED_STATE_SET = new int[]{android.R.attr.state_selected};
-    static final int[] NOT_PRESSED_OR_FOCUSED_STATE_SET = new int[]{
-            -android.R.attr.state_pressed, -android.R.attr.state_focused};
-    static final int[] EMPTY_STATE_SET = new int[0];
-
-    private static final int[] TEMP_ARRAY = new int[1];
-
-    public static ColorStateList createDisabledStateList(int textColor, int disabledTextColor) {
-        // Now create a new ColorStateList with the default color, and the new disabled
-        // color
-        final int[][] states = new int[2][];
-        final int[] colors = new int[2];
-        int i = 0;
-
-        // Disabled state
-        states[i] = DISABLED_STATE_SET;
-        colors[i] = disabledTextColor;
-        i++;
-
-        // Default state
-        states[i] = EMPTY_STATE_SET;
-        colors[i] = textColor;
-        i++;
-
-        return new ColorStateList(states, colors);
-    }
-
-    public static int getThemeAttrColor(Context context, int attr) {
-        TEMP_ARRAY[0] = attr;
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, TEMP_ARRAY);
-        try {
-            return a.getColor(0, 0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    public static ColorStateList getThemeAttrColorStateList(Context context, int attr) {
-        TEMP_ARRAY[0] = attr;
-        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, null, TEMP_ARRAY);
-        try {
-            return a.getColorStateList(0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    public static int getDisabledThemeAttrColor(Context context, int attr) {
-        final ColorStateList csl = getThemeAttrColorStateList(context, attr);
-        if (csl != null && csl.isStateful()) {
-            // If the CSL is stateful, we'll assume it has a disabled state and use it
-            return csl.getColorForState(DISABLED_STATE_SET, csl.getDefaultColor());
-        } else {
-            // Else, we'll generate the color using disabledAlpha from the theme
-
-            final TypedValue tv = getTypedValue();
-            // Now retrieve the disabledAlpha value from the theme
-            context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, tv, true);
-            final float disabledAlpha = tv.getFloat();
-
-            return getThemeAttrColor(context, attr, disabledAlpha);
-        }
-    }
-
-    private static TypedValue getTypedValue() {
-        TypedValue typedValue = TL_TYPED_VALUE.get();
-        if (typedValue == null) {
-            typedValue = new TypedValue();
-            TL_TYPED_VALUE.set(typedValue);
-        }
-        return typedValue;
-    }
-
-    static int getThemeAttrColor(Context context, int attr, float alpha) {
-        final int color = getThemeAttrColor(context, attr);
-        final int originalAlpha = Color.alpha(color);
-        return ColorUtils.setAlphaComponent(color, Math.round(originalAlpha * alpha));
-    }
-
-}
diff --git a/android/support/v7/widget/ThemedSpinnerAdapter.java b/android/support/v7/widget/ThemedSpinnerAdapter.java
deleted file mode 100644
index 6016b57..0000000
--- a/android/support/v7/widget/ThemedSpinnerAdapter.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.v7.widget;
-
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.SpinnerAdapter;
-
-/**
- * An extension of SpinnerAdapter that is capable of inflating drop-down views
- * against a different theme than normal views.
- * <p>
- * Classes that implement this interface should use the theme provided to
- * {@link #setDropDownViewTheme(Theme)} when creating views in
- * {@link SpinnerAdapter#getDropDownView(int, View, ViewGroup)}.
- *
- * <p>The {@link Helper} class is provided to aide implementation in a backwards compatible way.
- * </p>
- */
-public interface ThemedSpinnerAdapter extends SpinnerAdapter {
-    /**
-     * Sets the {@link Resources.Theme} against which drop-down views are
-     * inflated.
-     *
-     * @param theme the context against which to inflate drop-down views, or
-     *              {@code null} to use the default theme
-     * @see SpinnerAdapter#getDropDownView(int, View, ViewGroup)
-     */
-    void setDropDownViewTheme(@Nullable Resources.Theme theme);
-
-    /**
-     * Returns the value previously set by a call to
-     * {@link #setDropDownViewTheme(Theme)}.
-     *
-     * @return the {@link Resources.Theme} against which drop-down views are
-     *         inflated, or {@code null} if one has not been explicitly set
-     */
-    @Nullable
-    Resources.Theme getDropDownViewTheme();
-
-    /**
-     * A helper class which allows easy integration of {@link ThemedSpinnerAdapter} into existing
-     * {@link SpinnerAdapter}s in a backwards compatible way.
-     *
-     * <p>An example {@link android.widget.BaseAdapter BaseAdapter} implementation would be:</p>
-     *
-     * <pre>
-     * public class MyAdapter extends BaseAdapter implements ThemedSpinnerAdapter {
-     *     private final ThemedSpinnerAdapter.Helper mDropDownHelper;
-     *
-     *     public CheeseAdapter(Context context) {
-     *         mDropDownHelper = new ThemedSpinnerAdapter.Helper(context);
-     *         // ...
-     *     }
-     *
-     *     &#064;Override
-     *     public View getDropDownView(int position, View convertView, ViewGroup parent) {
-     *         View view;
-     *
-     *         if (convertView == null) {
-     *             // Inflate the drop down using the helper's LayoutInflater
-     *             LayoutInflater inflater = mDropDownHelper.getDropDownViewInflater();
-     *             view = inflater.inflate(R.layout.my_dropdown, parent, false);
-     *         }
-     *
-     *         // ...
-     *     }
-     *
-     *     &#064;Override
-     *     public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
-     *         // Pass the new theme to the helper
-     *         mDropDownHelper.setDropDownViewTheme(theme);
-     *     }
-     *
-     *     &#064;Override
-     *     public Resources.Theme getDropDownViewTheme() {
-     *         // Return the helper's value
-     *         return mDropDownHelper.getDropDownViewTheme();
-     *     }
-     * }
-     * </pre>
-     */
-    public final static class Helper {
-        private final Context mContext;
-        private final LayoutInflater mInflater;
-        private LayoutInflater mDropDownInflater;
-
-        public Helper(@NonNull Context context) {
-            mContext = context;
-            mInflater = LayoutInflater.from(context);
-        }
-
-        /**
-         * Should be called from your adapter's
-         * {@link ThemedSpinnerAdapter#setDropDownViewTheme(Theme)}
-         *
-         * @param theme the theme passed in to
-         *              {@link ThemedSpinnerAdapter#setDropDownViewTheme(Theme)}
-         */
-        public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
-            if (theme == null) {
-                mDropDownInflater = null;
-            } else if (theme == mContext.getTheme()) {
-                mDropDownInflater = mInflater;
-            } else {
-                final Context context = new ContextThemeWrapper(mContext, theme);
-                mDropDownInflater = LayoutInflater.from(context);
-            }
-        }
-
-        /**
-         * Should be called from your adapter's {@link ThemedSpinnerAdapter#getDropDownViewTheme()},
-         * returning the value returned from this method.
-         */
-        @Nullable
-        public Resources.Theme getDropDownViewTheme() {
-            return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
-        }
-
-        /**
-         * Returns the {@link LayoutInflater} which should be used when inflating any layouts
-         * from your {@link SpinnerAdapter#getDropDownView(int, View, ViewGroup)}.
-         *
-         * <p>The instance returned will have a correct theme, meaning that any inflated views
-         * will be created with the same theme.</p>
-         */
-        @NonNull
-        public LayoutInflater getDropDownViewInflater() {
-            return mDropDownInflater != null ? mDropDownInflater : mInflater;
-        }
-    }
-}
diff --git a/android/support/v7/widget/TintContextWrapper.java b/android/support/v7/widget/TintContextWrapper.java
deleted file mode 100644
index 1454e59..0000000
--- a/android/support/v7/widget/TintContextWrapper.java
+++ /dev/null
@@ -1,129 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * A {@link android.content.ContextWrapper} which returns a tint-aware
- * {@link android.content.res.Resources} instance from {@link #getResources()}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class TintContextWrapper extends ContextWrapper {
-
-    private static final Object CACHE_LOCK = new Object();
-    private static ArrayList<WeakReference<TintContextWrapper>> sCache;
-
-    public static Context wrap(@NonNull final Context context) {
-        if (shouldWrap(context)) {
-            synchronized (CACHE_LOCK) {
-                if (sCache == null) {
-                    sCache = new ArrayList<>();
-                } else {
-                    // This is a convenient place to prune any dead reference entries
-                    for (int i = sCache.size() - 1; i >= 0; i--) {
-                        final WeakReference<TintContextWrapper> ref = sCache.get(i);
-                        if (ref == null || ref.get() == null) {
-                            sCache.remove(i);
-                        }
-                    }
-                    // Now check our instance cache
-                    for (int i = sCache.size() - 1; i >= 0; i--) {
-                        final WeakReference<TintContextWrapper> ref = sCache.get(i);
-                        final TintContextWrapper wrapper = ref != null ? ref.get() : null;
-                        if (wrapper != null && wrapper.getBaseContext() == context) {
-                            return wrapper;
-                        }
-                    }
-                }
-                // If we reach here then the cache didn't have a hit, so create a new instance
-                // and add it to the cache
-                final TintContextWrapper wrapper = new TintContextWrapper(context);
-                sCache.add(new WeakReference<>(wrapper));
-                return wrapper;
-            }
-        }
-        return context;
-    }
-
-    private static boolean shouldWrap(@NonNull final Context context) {
-        if (context instanceof TintContextWrapper
-                || context.getResources() instanceof TintResources
-                || context.getResources() instanceof VectorEnabledTintResources) {
-            // If the Context already has a TintResources[Experimental] impl, no need to wrap again
-            // If the Context is already a TintContextWrapper, no need to wrap again
-            return false;
-        }
-        return Build.VERSION.SDK_INT < 21 || VectorEnabledTintResources.shouldBeUsed();
-    }
-
-    private final Resources mResources;
-    private final Resources.Theme mTheme;
-
-    private TintContextWrapper(@NonNull final Context base) {
-        super(base);
-
-        if (VectorEnabledTintResources.shouldBeUsed()) {
-            // We need to create a copy of the Theme so that the Theme references our
-            // new Resources instance
-            mResources = new VectorEnabledTintResources(this, base.getResources());
-            mTheme = mResources.newTheme();
-            mTheme.setTo(base.getTheme());
-        } else {
-            mResources = new TintResources(this, base.getResources());
-            mTheme = null;
-        }
-    }
-
-    @Override
-    public Resources.Theme getTheme() {
-        return mTheme == null ? super.getTheme() : mTheme;
-    }
-
-    @Override
-    public void setTheme(int resid) {
-        if (mTheme == null) {
-            super.setTheme(resid);
-        } else {
-            mTheme.applyStyle(resid, true);
-        }
-    }
-
-    @Override
-    public Resources getResources() {
-        return mResources;
-    }
-
-    @Override
-    public AssetManager getAssets() {
-        // Ensure we're returning assets with the correct configuration.
-        return mResources.getAssets();
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/TintInfo.java b/android/support/v7/widget/TintInfo.java
deleted file mode 100644
index 0e7a667..0000000
--- a/android/support/v7/widget/TintInfo.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.v7.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-
-class TintInfo {
-    public ColorStateList mTintList;
-    public PorterDuff.Mode mTintMode;
-    public boolean mHasTintMode;
-    public boolean mHasTintList;
-
-    void clear() {
-        mTintList = null;
-        mHasTintList = false;
-        mTintMode = null;
-        mHasTintMode = false;
-    }
-}
diff --git a/android/support/v7/widget/TintResources.java b/android/support/v7/widget/TintResources.java
deleted file mode 100644
index 39e03a5..0000000
--- a/android/support/v7/widget/TintResources.java
+++ /dev/null
@@ -1,52 +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.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-
-import java.lang.ref.WeakReference;
-
-/**
- * This class allows us to intercept calls so that we can tint resources (if applicable).
- */
-class TintResources extends ResourcesWrapper {
-
-    private final WeakReference<Context> mContextRef;
-
-    public TintResources(@NonNull Context context, @NonNull final Resources res) {
-        super(res);
-        mContextRef = new WeakReference<>(context);
-    }
-
-    /**
-     * We intercept this call so that we tint the result (if applicable). This is needed for
-     * things like {@link android.graphics.drawable.DrawableContainer}s which can retrieve
-     * their children via this method.
-     */
-    @Override
-    public Drawable getDrawable(int id) throws NotFoundException {
-        Drawable d = super.getDrawable(id);
-        Context context = mContextRef.get();
-        if (d != null && context != null) {
-            AppCompatDrawableManager.get().tintDrawableUsingColorFilter(context, id, d);
-        }
-        return d;
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/TintTypedArray.java b/android/support/v7/widget/TintTypedArray.java
deleted file mode 100644
index 384c461..0000000
--- a/android/support/v7/widget/TintTypedArray.java
+++ /dev/null
@@ -1,252 +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 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.TypedArray;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-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;
-
-/**
- * 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 calls to new APIs.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class TintTypedArray {
-
-    private final Context mContext;
-    private final TypedArray mWrapped;
-
-    private TypedValue mTypedValue;
-
-    public static TintTypedArray obtainStyledAttributes(Context context, AttributeSet set,
-            int[] attrs) {
-        return new TintTypedArray(context, context.obtainStyledAttributes(set, attrs));
-    }
-
-    public static TintTypedArray obtainStyledAttributes(Context context, AttributeSet set,
-            int[] attrs, int defStyleAttr, int defStyleRes) {
-        return new TintTypedArray(context,
-                context.obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes));
-    }
-
-    public static TintTypedArray obtainStyledAttributes(Context context, int resid, int[] attrs) {
-        return new TintTypedArray(context, context.obtainStyledAttributes(resid, attrs));
-    }
-
-    private TintTypedArray(Context context, TypedArray array) {
-        mContext = context;
-        mWrapped = array;
-    }
-
-    public Drawable getDrawable(int index) {
-        if (mWrapped.hasValue(index)) {
-            final int resourceId = mWrapped.getResourceId(index, 0);
-            if (resourceId != 0) {
-                return AppCompatResources.getDrawable(mContext, resourceId);
-            }
-        }
-        return mWrapped.getDrawable(index);
-    }
-
-    public Drawable getDrawableIfKnown(int index) {
-        if (mWrapped.hasValue(index)) {
-            final int resourceId = mWrapped.getResourceId(index, 0);
-            if (resourceId != 0) {
-                return AppCompatDrawableManager.get().getDrawable(mContext, resourceId, true);
-            }
-        }
-        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 fontCallback A callback to receive async fetching of this font. If async loading is
-     *                     specified in XML, this callback will be triggered.
-     *
-     * @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,
-            @Nullable ResourcesCompat.FontCallback fontCallback) {
-        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, fontCallback);
-    }
-
-    public int length() {
-        return mWrapped.length();
-    }
-
-    public int getIndexCount() {
-        return mWrapped.getIndexCount();
-    }
-
-    public int getIndex(int at) {
-        return mWrapped.getIndex(at);
-    }
-
-    public Resources getResources() {
-        return mWrapped.getResources();
-    }
-
-    public CharSequence getText(int index) {
-        return mWrapped.getText(index);
-    }
-
-    public String getString(int index) {
-        return mWrapped.getString(index);
-    }
-
-    public String getNonResourceString(int index) {
-        return mWrapped.getNonResourceString(index);
-    }
-
-    public boolean getBoolean(int index, boolean defValue) {
-        return mWrapped.getBoolean(index, defValue);
-    }
-
-    public int getInt(int index, int defValue) {
-        return mWrapped.getInt(index, defValue);
-    }
-
-    public float getFloat(int index, float defValue) {
-        return mWrapped.getFloat(index, defValue);
-    }
-
-    public int getColor(int index, int defValue) {
-        return mWrapped.getColor(index, defValue);
-    }
-
-    public ColorStateList getColorStateList(int index) {
-        if (mWrapped.hasValue(index)) {
-            final int resourceId = mWrapped.getResourceId(index, 0);
-            if (resourceId != 0) {
-                final ColorStateList value =
-                        AppCompatResources.getColorStateList(mContext, resourceId);
-                if (value != null) {
-                    return value;
-                }
-            }
-        }
-        return mWrapped.getColorStateList(index);
-    }
-
-    public int getInteger(int index, int defValue) {
-        return mWrapped.getInteger(index, defValue);
-    }
-
-    public float getDimension(int index, float defValue) {
-        return mWrapped.getDimension(index, defValue);
-    }
-
-    public int getDimensionPixelOffset(int index, int defValue) {
-        return mWrapped.getDimensionPixelOffset(index, defValue);
-    }
-
-    public int getDimensionPixelSize(int index, int defValue) {
-        return mWrapped.getDimensionPixelSize(index, defValue);
-    }
-
-    public int getLayoutDimension(int index, String name) {
-        return mWrapped.getLayoutDimension(index, name);
-    }
-
-    public int getLayoutDimension(int index, int defValue) {
-        return mWrapped.getLayoutDimension(index, defValue);
-    }
-
-    public float getFraction(int index, int base, int pbase, float defValue) {
-        return mWrapped.getFraction(index, base, pbase, defValue);
-    }
-
-    public int getResourceId(int index, int defValue) {
-        return mWrapped.getResourceId(index, defValue);
-    }
-
-    public CharSequence[] getTextArray(int index) {
-        return mWrapped.getTextArray(index);
-    }
-
-    public boolean getValue(int index, TypedValue outValue) {
-        return mWrapped.getValue(index, outValue);
-    }
-
-    public int getType(int index) {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return mWrapped.getType(index);
-        } else {
-            if (mTypedValue == null) {
-                mTypedValue = new TypedValue();
-            }
-            mWrapped.getValue(index, mTypedValue);
-            return mTypedValue.type;
-        }
-    }
-
-    public boolean hasValue(int index) {
-        return mWrapped.hasValue(index);
-    }
-
-    public TypedValue peekValue(int index) {
-        return mWrapped.peekValue(index);
-    }
-
-    public String getPositionDescription() {
-        return mWrapped.getPositionDescription();
-    }
-
-    public void recycle() {
-        mWrapped.recycle();
-    }
-
-    @RequiresApi(21)
-    public int getChangingConfigurations() {
-        return mWrapped.getChangingConfigurations();
-    }
-
-}
diff --git a/android/support/v7/widget/Toolbar.java b/android/support/v7/widget/Toolbar.java
deleted file mode 100644
index f383e90..0000000
--- a/android/support/v7/widget/Toolbar.java
+++ /dev/null
@@ -1,2437 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.MenuRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.StringRes;
-import android.support.annotation.StyleRes;
-import android.support.v4.view.AbsSavedState;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.MarginLayoutParamsCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.CollapsibleActionView;
-import android.support.v7.view.SupportMenuInflater;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuItemImpl;
-import android.support.v7.view.menu.MenuPresenter;
-import android.support.v7.view.menu.MenuView;
-import android.support.v7.view.menu.SubMenuBuilder;
-import android.text.Layout;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A standard toolbar for use within application content.
- *
- * <p>A Toolbar is a generalization of {@link ActionBar action bars} for use
- * within application layouts. While an action bar is traditionally part of an
- * {@link android.app.Activity Activity's} opaque window decor controlled by the framework,
- * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy.
- * An application may choose to designate a Toolbar as the action bar for an Activity
- * using the {@link android.support.v7.app.AppCompatActivity#setSupportActionBar(Toolbar)
- * setSupportActionBar()} method.</p>
- *
- * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar
- * may contain a combination of the following optional elements:
- *
- * <ul>
- *     <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close,
- *     collapse, done or another glyph of the app's choosing. This button should always be used
- *     to access other navigational destinations within the container of the Toolbar and
- *     its signified content or otherwise leave the current context signified by the Toolbar.
- *     The navigation button is vertically aligned within the Toolbar's minimum height,
- *     if set.</li>
- *     <li><em>A branded logo image.</em> This may extend to the height of the bar and can be
- *     arbitrarily wide.</li>
- *     <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current
- *     position in the navigation hierarchy and the content contained there. The subtitle,
- *     if present should indicate any extended information about the current content.
- *     If an app uses a logo image it should strongly consider omitting a title and subtitle.</li>
- *     <li><em>One or more custom views.</em> The application may add arbitrary child views
- *     to the Toolbar. They will appear at this position within the layout. If a child view's
- *     {@link LayoutParams} indicates a {@link Gravity} value of
- *     {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center
- *     within the available space remaining in the Toolbar after all other elements have been
- *     measured.</li>
- *     <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the
- *     end of the Toolbar offering a few
- *     <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons">
- *     frequent, important or typical</a> actions along with an optional overflow menu for
- *     additional actions. Action buttons are vertically aligned within the Toolbar's
- *     minimum height, if set.</li>
- * </ul>
- * </p>
- *
- * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
- * toolbars than on their application icon. The use of application icon plus title as a standard
- * layout is discouraged on API 21 devices and newer.</p>
- *
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_buttonGravity
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_collapseContentDescription
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_collapseIcon
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEnd
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetLeft
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetRight
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStart
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStartWithNavigation
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEndWithActions
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_android_gravity
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_logo
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_logoDescription
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_maxButtonHeight
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_popupTheme
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitle
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitleTextAppearance
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_subtitleTextColor
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_title
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMargin
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleTextAppearance
- * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleTextColor
- */
-public class Toolbar extends ViewGroup {
-    private static final String TAG = "Toolbar";
-
-    private ActionMenuView mMenuView;
-    private TextView mTitleTextView;
-    private TextView mSubtitleTextView;
-    private ImageButton mNavButtonView;
-    private ImageView mLogoView;
-
-    private Drawable mCollapseIcon;
-    private CharSequence mCollapseDescription;
-    ImageButton mCollapseButtonView;
-    View mExpandedActionView;
-
-    /** Context against which to inflate popup menus. */
-    private Context mPopupContext;
-
-    /** Theme resource against which to inflate popup menus. */
-    private int mPopupTheme;
-
-    private int mTitleTextAppearance;
-    private int mSubtitleTextAppearance;
-
-    int mButtonGravity;
-
-    private int mMaxButtonHeight;
-
-    private int mTitleMarginStart;
-    private int mTitleMarginEnd;
-    private int mTitleMarginTop;
-    private int mTitleMarginBottom;
-
-    private RtlSpacingHelper mContentInsets;
-    private int mContentInsetStartWithNavigation;
-    private int mContentInsetEndWithActions;
-
-    private int mGravity = GravityCompat.START | Gravity.CENTER_VERTICAL;
-
-    private CharSequence mTitleText;
-    private CharSequence mSubtitleText;
-
-    private int mTitleTextColor;
-    private int mSubtitleTextColor;
-
-    private boolean mEatingTouch;
-    private boolean mEatingHover;
-
-    // Clear me after use.
-    private final ArrayList<View> mTempViews = new ArrayList<View>();
-
-    // Used to hold views that will be removed while we have an expanded action view.
-    private final ArrayList<View> mHiddenViews = new ArrayList<>();
-
-    private final int[] mTempMargins = new int[2];
-
-    OnMenuItemClickListener mOnMenuItemClickListener;
-
-    private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
-            new ActionMenuView.OnMenuItemClickListener() {
-                @Override
-                public boolean onMenuItemClick(MenuItem item) {
-                    if (mOnMenuItemClickListener != null) {
-                        return mOnMenuItemClickListener.onMenuItemClick(item);
-                    }
-                    return false;
-                }
-            };
-
-    private ToolbarWidgetWrapper mWrapper;
-    private ActionMenuPresenter mOuterActionMenuPresenter;
-    private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
-    private MenuPresenter.Callback mActionMenuPresenterCallback;
-    private MenuBuilder.Callback mMenuBuilderCallback;
-
-    private boolean mCollapsible;
-
-    private final Runnable mShowOverflowMenuRunnable = new Runnable() {
-        @Override public void run() {
-            showOverflowMenu();
-        }
-    };
-
-    public Toolbar(Context context) {
-        this(context, null);
-    }
-
-    public Toolbar(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, R.attr.toolbarStyle);
-    }
-
-    public Toolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        // Need to use getContext() here so that we use the themed context
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
-                R.styleable.Toolbar, defStyleAttr, 0);
-
-        mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
-        mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
-        mGravity = a.getInteger(R.styleable.Toolbar_android_gravity, mGravity);
-        mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
-
-        // First read the correct attribute
-        int titleMargin = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargin, 0);
-        if (a.hasValue(R.styleable.Toolbar_titleMargins)) {
-            // Now read the deprecated attribute, if it has a value
-            titleMargin = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, titleMargin);
-        }
-        mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom = titleMargin;
-
-        final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
-        if (marginStart >= 0) {
-            mTitleMarginStart = marginStart;
-        }
-
-        final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1);
-        if (marginEnd >= 0) {
-            mTitleMarginEnd = marginEnd;
-        }
-
-        final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1);
-        if (marginTop >= 0) {
-            mTitleMarginTop = marginTop;
-        }
-
-        final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom,
-                -1);
-        if (marginBottom >= 0) {
-            mTitleMarginBottom = marginBottom;
-        }
-
-        mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1);
-
-        final int contentInsetStart =
-                a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart,
-                        RtlSpacingHelper.UNDEFINED);
-        final int contentInsetEnd =
-                a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetEnd,
-                        RtlSpacingHelper.UNDEFINED);
-        final int contentInsetLeft =
-                a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetLeft, 0);
-        final int contentInsetRight =
-                a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0);
-
-        ensureContentInsets();
-        mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
-
-        if (contentInsetStart != RtlSpacingHelper.UNDEFINED ||
-                contentInsetEnd != RtlSpacingHelper.UNDEFINED) {
-            mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
-        }
-
-        mContentInsetStartWithNavigation = a.getDimensionPixelOffset(
-                R.styleable.Toolbar_contentInsetStartWithNavigation, RtlSpacingHelper.UNDEFINED);
-        mContentInsetEndWithActions = a.getDimensionPixelOffset(
-                R.styleable.Toolbar_contentInsetEndWithActions, RtlSpacingHelper.UNDEFINED);
-
-        mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
-        mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription);
-
-        final CharSequence title = a.getText(R.styleable.Toolbar_title);
-        if (!TextUtils.isEmpty(title)) {
-            setTitle(title);
-        }
-
-        final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle);
-        if (!TextUtils.isEmpty(subtitle)) {
-            setSubtitle(subtitle);
-        }
-
-        // Set the default context, since setPopupTheme() may be a no-op.
-        mPopupContext = getContext();
-        setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
-
-        final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon);
-        if (navIcon != null) {
-            setNavigationIcon(navIcon);
-        }
-        final CharSequence navDesc = a.getText(R.styleable.Toolbar_navigationContentDescription);
-        if (!TextUtils.isEmpty(navDesc)) {
-            setNavigationContentDescription(navDesc);
-        }
-
-        final Drawable logo = a.getDrawable(R.styleable.Toolbar_logo);
-        if (logo != null) {
-            setLogo(logo);
-        }
-
-        final CharSequence logoDesc = a.getText(R.styleable.Toolbar_logoDescription);
-        if (!TextUtils.isEmpty(logoDesc)) {
-            setLogoDescription(logoDesc);
-        }
-
-        if (a.hasValue(R.styleable.Toolbar_titleTextColor)) {
-            setTitleTextColor(a.getColor(R.styleable.Toolbar_titleTextColor, 0xffffffff));
-        }
-
-        if (a.hasValue(R.styleable.Toolbar_subtitleTextColor)) {
-            setSubtitleTextColor(a.getColor(R.styleable.Toolbar_subtitleTextColor, 0xffffffff));
-        }
-        a.recycle();
-    }
-
-    /**
-     * Specifies the theme to use when inflating popup menus. By default, uses
-     * the same theme as the toolbar itself.
-     *
-     * @param resId theme used to inflate popup menus
-     * @see #getPopupTheme()
-     */
-    public void setPopupTheme(@StyleRes int resId) {
-        if (mPopupTheme != resId) {
-            mPopupTheme = resId;
-            if (resId == 0) {
-                mPopupContext = getContext();
-            } else {
-                mPopupContext = new ContextThemeWrapper(getContext(), resId);
-            }
-        }
-    }
-
-    /**
-     * @return resource identifier of the theme used to inflate popup menus, or
-     *         0 if menus are inflated against the toolbar theme
-     * @see #setPopupTheme(int)
-     */
-    public int getPopupTheme() {
-        return mPopupTheme;
-    }
-
-    /**
-     * Sets the title margin.
-     *
-     * @param start the starting title margin in pixels
-     * @param top the top title margin in pixels
-     * @param end the ending title margin in pixels
-     * @param bottom the bottom title margin in pixels
-     * @see #getTitleMarginStart()
-     * @see #getTitleMarginTop()
-     * @see #getTitleMarginEnd()
-     * @see #getTitleMarginBottom()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMargin
-     */
-    public void setTitleMargin(int start, int top, int end, int bottom) {
-        mTitleMarginStart = start;
-        mTitleMarginTop = top;
-        mTitleMarginEnd = end;
-        mTitleMarginBottom = bottom;
-
-        requestLayout();
-    }
-
-    /**
-     * @return the starting title margin in pixels
-     * @see #setTitleMarginStart(int)
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
-     */
-    public int getTitleMarginStart() {
-        return mTitleMarginStart;
-    }
-
-    /**
-     * Sets the starting title margin in pixels.
-     *
-     * @param margin the starting title margin in pixels
-     * @see #getTitleMarginStart()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginStart
-     */
-    public void setTitleMarginStart(int margin) {
-        mTitleMarginStart = margin;
-
-        requestLayout();
-    }
-
-    /**
-     * @return the top title margin in pixels
-     * @see #setTitleMarginTop(int)
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
-     */
-    public int getTitleMarginTop() {
-        return mTitleMarginTop;
-    }
-
-    /**
-     * Sets the top title margin in pixels.
-     *
-     * @param margin the top title margin in pixels
-     * @see #getTitleMarginTop()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginTop
-     */
-    public void setTitleMarginTop(int margin) {
-        mTitleMarginTop = margin;
-
-        requestLayout();
-    }
-
-    /**
-     * @return the ending title margin in pixels
-     * @see #setTitleMarginEnd(int)
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
-     */
-    public int getTitleMarginEnd() {
-        return mTitleMarginEnd;
-    }
-
-    /**
-     * Sets the ending title margin in pixels.
-     *
-     * @param margin the ending title margin in pixels
-     * @see #getTitleMarginEnd()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginEnd
-     */
-    public void setTitleMarginEnd(int margin) {
-        mTitleMarginEnd = margin;
-
-        requestLayout();
-    }
-
-    /**
-     * @return the bottom title margin in pixels
-     * @see #setTitleMarginBottom(int)
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
-     */
-    public int getTitleMarginBottom() {
-        return mTitleMarginBottom;
-    }
-
-    /**
-     * Sets the bottom title margin in pixels.
-     *
-     * @param margin the bottom title margin in pixels
-     * @see #getTitleMarginBottom()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_titleMarginBottom
-     */
-    public void setTitleMarginBottom(int margin) {
-        mTitleMarginBottom = margin;
-        requestLayout();
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        if (Build.VERSION.SDK_INT >= 17) {
-            super.onRtlPropertiesChanged(layoutDirection);
-        }
-
-        ensureContentInsets();
-        mContentInsets.setDirection(layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL);
-    }
-
-    /**
-     * Set a logo drawable from a resource id.
-     *
-     * <p>This drawable should generally take the place of title text. The logo cannot be
-     * clicked. Apps using a logo should also supply a description using
-     * {@link #setLogoDescription(int)}.</p>
-     *
-     * @param resId ID of a drawable resource
-     */
-    public void setLogo(@DrawableRes int resId) {
-        setLogo(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean canShowOverflowMenu() {
-        return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved();
-    }
-
-    /**
-     * Check whether the overflow menu is currently showing. This may not reflect
-     * a pending show operation in progress.
-     *
-     * @return true if the overflow menu is currently showing
-     */
-    public boolean isOverflowMenuShowing() {
-        return mMenuView != null && mMenuView.isOverflowMenuShowing();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isOverflowMenuShowPending() {
-        return mMenuView != null && mMenuView.isOverflowMenuShowPending();
-    }
-
-    /**
-     * Show the overflow items from the associated menu.
-     *
-     * @return true if the menu was able to be shown, false otherwise
-     */
-    public boolean showOverflowMenu() {
-        return mMenuView != null && mMenuView.showOverflowMenu();
-    }
-
-    /**
-     * Hide the overflow items from the associated menu.
-     *
-     * @return true if the menu was able to be hidden, false otherwise
-     */
-    public boolean hideOverflowMenu() {
-        return mMenuView != null && mMenuView.hideOverflowMenu();
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {
-        if (menu == null && mMenuView == null) {
-            return;
-        }
-
-        ensureMenuView();
-        final MenuBuilder oldMenu = mMenuView.peekMenu();
-        if (oldMenu == menu) {
-            return;
-        }
-
-        if (oldMenu != null) {
-            oldMenu.removeMenuPresenter(mOuterActionMenuPresenter);
-            oldMenu.removeMenuPresenter(mExpandedMenuPresenter);
-        }
-
-        if (mExpandedMenuPresenter == null) {
-            mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
-        }
-
-        outerPresenter.setExpandedActionViewsExclusive(true);
-        if (menu != null) {
-            menu.addMenuPresenter(outerPresenter, mPopupContext);
-            menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext);
-        } else {
-            outerPresenter.initForMenu(mPopupContext, null);
-            mExpandedMenuPresenter.initForMenu(mPopupContext, null);
-            outerPresenter.updateMenuView(true);
-            mExpandedMenuPresenter.updateMenuView(true);
-        }
-        mMenuView.setPopupTheme(mPopupTheme);
-        mMenuView.setPresenter(outerPresenter);
-        mOuterActionMenuPresenter = outerPresenter;
-    }
-
-    /**
-     * Dismiss all currently showing popup menus, including overflow or submenus.
-     */
-    public void dismissPopupMenus() {
-        if (mMenuView != null) {
-            mMenuView.dismissPopupMenus();
-        }
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public boolean isTitleTruncated() {
-        if (mTitleTextView == null) {
-            return false;
-        }
-
-        final Layout titleLayout = mTitleTextView.getLayout();
-        if (titleLayout == null) {
-            return false;
-        }
-
-        final int lineCount = titleLayout.getLineCount();
-        for (int i = 0; i < lineCount; i++) {
-            if (titleLayout.getEllipsisCount(i) > 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Set a logo drawable.
-     *
-     * <p>This drawable should generally take the place of title text. The logo cannot be
-     * clicked. Apps using a logo should also supply a description using
-     * {@link #setLogoDescription(int)}.</p>
-     *
-     * @param drawable Drawable to use as a logo
-     */
-    public void setLogo(Drawable drawable) {
-        if (drawable != null) {
-            ensureLogoView();
-            if (!isChildOrHidden(mLogoView)) {
-                addSystemView(mLogoView, true);
-            }
-        } else if (mLogoView != null && isChildOrHidden(mLogoView)) {
-            removeView(mLogoView);
-            mHiddenViews.remove(mLogoView);
-        }
-        if (mLogoView != null) {
-            mLogoView.setImageDrawable(drawable);
-        }
-    }
-
-    /**
-     * Return the current logo drawable.
-     *
-     * @return The current logo drawable
-     * @see #setLogo(int)
-     * @see #setLogo(android.graphics.drawable.Drawable)
-     */
-    public Drawable getLogo() {
-        return mLogoView != null ? mLogoView.getDrawable() : null;
-    }
-
-    /**
-     * Set a description of the toolbar's logo.
-     *
-     * <p>This description will be used for accessibility or other similar descriptions
-     * of the UI.</p>
-     *
-     * @param resId String resource id
-     */
-    public void setLogoDescription(@StringRes int resId) {
-        setLogoDescription(getContext().getText(resId));
-    }
-
-    /**
-     * Set a description of the toolbar's logo.
-     *
-     * <p>This description will be used for accessibility or other similar descriptions
-     * of the UI.</p>
-     *
-     * @param description Description to set
-     */
-    public void setLogoDescription(CharSequence description) {
-        if (!TextUtils.isEmpty(description)) {
-            ensureLogoView();
-        }
-        if (mLogoView != null) {
-            mLogoView.setContentDescription(description);
-        }
-    }
-
-    /**
-     * Return the description of the toolbar's logo.
-     *
-     * @return A description of the logo
-     */
-    public CharSequence getLogoDescription() {
-        return mLogoView != null ? mLogoView.getContentDescription() : null;
-    }
-
-    private void ensureLogoView() {
-        if (mLogoView == null) {
-            mLogoView = new AppCompatImageView(getContext());
-        }
-    }
-
-    /**
-     * Check whether this Toolbar is currently hosting an expanded action view.
-     *
-     * <p>An action view may be expanded either directly from the
-     * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar
-     * has an expanded action view it can be collapsed using the {@link #collapseActionView()}
-     * method.</p>
-     *
-     * @return true if the Toolbar has an expanded action view
-     */
-    public boolean hasExpandedActionView() {
-        return mExpandedMenuPresenter != null &&
-                mExpandedMenuPresenter.mCurrentExpandedItem != null;
-    }
-
-    /**
-     * Collapse a currently expanded action view. If this Toolbar does not have an
-     * expanded action view this method has no effect.
-     *
-     * <p>An action view may be expanded either directly from the
-     * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p>
-     *
-     * @see #hasExpandedActionView()
-     */
-    public void collapseActionView() {
-        final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
-                mExpandedMenuPresenter.mCurrentExpandedItem;
-        if (item != null) {
-            item.collapseActionView();
-        }
-    }
-
-    /**
-     * Returns the title of this toolbar.
-     *
-     * @return The current title.
-     */
-    public CharSequence getTitle() {
-        return mTitleText;
-    }
-
-    /**
-     * Set the title of this toolbar.
-     *
-     * <p>A title should be used as the anchor for a section of content. It should
-     * describe or name the content being viewed.</p>
-     *
-     * @param resId Resource ID of a string to set as the title
-     */
-    public void setTitle(@StringRes int resId) {
-        setTitle(getContext().getText(resId));
-    }
-
-    /**
-     * Set the title of this toolbar.
-     *
-     * <p>A title should be used as the anchor for a section of content. It should
-     * describe or name the content being viewed.</p>
-     *
-     * @param title Title to set
-     */
-    public void setTitle(CharSequence title) {
-        if (!TextUtils.isEmpty(title)) {
-            if (mTitleTextView == null) {
-                final Context context = getContext();
-                mTitleTextView = new AppCompatTextView(context);
-                mTitleTextView.setSingleLine();
-                mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
-                if (mTitleTextAppearance != 0) {
-                    mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
-                }
-                if (mTitleTextColor != 0) {
-                    mTitleTextView.setTextColor(mTitleTextColor);
-                }
-            }
-            if (!isChildOrHidden(mTitleTextView)) {
-                addSystemView(mTitleTextView, true);
-            }
-        } else if (mTitleTextView != null && isChildOrHidden(mTitleTextView)) {
-            removeView(mTitleTextView);
-            mHiddenViews.remove(mTitleTextView);
-        }
-        if (mTitleTextView != null) {
-            mTitleTextView.setText(title);
-        }
-        mTitleText = title;
-    }
-
-    /**
-     * Return the subtitle of this toolbar.
-     *
-     * @return The current subtitle
-     */
-    public CharSequence getSubtitle() {
-        return mSubtitleText;
-    }
-
-    /**
-     * Set the subtitle of this toolbar.
-     *
-     * <p>Subtitles should express extended information about the current content.</p>
-     *
-     * @param resId String resource ID
-     */
-    public void setSubtitle(@StringRes int resId) {
-        setSubtitle(getContext().getText(resId));
-    }
-
-    /**
-     * Set the subtitle of this toolbar.
-     *
-     * <p>Subtitles should express extended information about the current content.</p>
-     *
-     * @param subtitle Subtitle to set
-     */
-    public void setSubtitle(CharSequence subtitle) {
-        if (!TextUtils.isEmpty(subtitle)) {
-            if (mSubtitleTextView == null) {
-                final Context context = getContext();
-                mSubtitleTextView = new AppCompatTextView(context);
-                mSubtitleTextView.setSingleLine();
-                mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
-                if (mSubtitleTextAppearance != 0) {
-                    mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
-                }
-                if (mSubtitleTextColor != 0) {
-                    mSubtitleTextView.setTextColor(mSubtitleTextColor);
-                }
-            }
-            if (!isChildOrHidden(mSubtitleTextView)) {
-                addSystemView(mSubtitleTextView, true);
-            }
-        } else if (mSubtitleTextView != null && isChildOrHidden(mSubtitleTextView)) {
-            removeView(mSubtitleTextView);
-            mHiddenViews.remove(mSubtitleTextView);
-        }
-        if (mSubtitleTextView != null) {
-            mSubtitleTextView.setText(subtitle);
-        }
-        mSubtitleText = subtitle;
-    }
-
-    /**
-     * Sets the text color, size, style, hint color, and highlight color
-     * from the specified TextAppearance resource.
-     */
-    public void setTitleTextAppearance(Context context, @StyleRes int resId) {
-        mTitleTextAppearance = resId;
-        if (mTitleTextView != null) {
-            mTitleTextView.setTextAppearance(context, resId);
-        }
-    }
-
-    /**
-     * Sets the text color, size, style, hint color, and highlight color
-     * from the specified TextAppearance resource.
-     */
-    public void setSubtitleTextAppearance(Context context, @StyleRes int resId) {
-        mSubtitleTextAppearance = resId;
-        if (mSubtitleTextView != null) {
-            mSubtitleTextView.setTextAppearance(context, resId);
-        }
-    }
-
-    /**
-     * Sets the text color of the title, if present.
-     *
-     * @param color The new text color in 0xAARRGGBB format
-     */
-    public void setTitleTextColor(@ColorInt int color) {
-        mTitleTextColor = color;
-        if (mTitleTextView != null) {
-            mTitleTextView.setTextColor(color);
-        }
-    }
-
-    /**
-     * Sets the text color of the subtitle, if present.
-     *
-     * @param color The new text color in 0xAARRGGBB format
-     */
-    public void setSubtitleTextColor(@ColorInt int color) {
-        mSubtitleTextColor = color;
-        if (mSubtitleTextView != null) {
-            mSubtitleTextView.setTextColor(color);
-        }
-    }
-
-    /**
-     * Retrieve the currently configured content description for the navigation button view.
-     * This will be used to describe the navigation action to users through mechanisms such
-     * as screen readers or tooltips.
-     *
-     * @return The navigation button's content description
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
-     */
-    @Nullable
-    public CharSequence getNavigationContentDescription() {
-        return mNavButtonView != null ? mNavButtonView.getContentDescription() : null;
-    }
-
-    /**
-     * Set a content description for the navigation button if one is present. The content
-     * description will be read via screen readers or other accessibility systems to explain
-     * the action of the navigation button.
-     *
-     * @param resId Resource ID of a content description string to set, or 0 to
-     *              clear the description
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
-     */
-    public void setNavigationContentDescription(@StringRes int resId) {
-        setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
-    }
-
-    /**
-     * Set a content description for the navigation button if one is present. The content
-     * description will be read via screen readers or other accessibility systems to explain
-     * the action of the navigation button.
-     *
-     * @param description Content description to set, or <code>null</code> to
-     *                    clear the content description
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationContentDescription
-     */
-    public void setNavigationContentDescription(@Nullable CharSequence description) {
-        if (!TextUtils.isEmpty(description)) {
-            ensureNavButtonView();
-        }
-        if (mNavButtonView != null) {
-            mNavButtonView.setContentDescription(description);
-        }
-    }
-
-    /**
-     * Set the icon to use for the toolbar's navigation button.
-     *
-     * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
-     * will make the navigation button visible.</p>
-     *
-     * <p>If you use a navigation icon you should also set a description for its action using
-     * {@link #setNavigationContentDescription(int)}. This is used for accessibility and
-     * tooltips.</p>
-     *
-     * @param resId Resource ID of a drawable to set
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
-     */
-    public void setNavigationIcon(@DrawableRes int resId) {
-        setNavigationIcon(AppCompatResources.getDrawable(getContext(), resId));
-    }
-
-    /**
-     * Set the icon to use for the toolbar's navigation button.
-     *
-     * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
-     * will make the navigation button visible.</p>
-     *
-     * <p>If you use a navigation icon you should also set a description for its action using
-     * {@link #setNavigationContentDescription(int)}. This is used for accessibility and
-     * tooltips.</p>
-     *
-     * @param icon Drawable to set, may be null to clear the icon
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
-     */
-    public void setNavigationIcon(@Nullable Drawable icon) {
-        if (icon != null) {
-            ensureNavButtonView();
-            if (!isChildOrHidden(mNavButtonView)) {
-                addSystemView(mNavButtonView, true);
-            }
-        } else if (mNavButtonView != null && isChildOrHidden(mNavButtonView)) {
-            removeView(mNavButtonView);
-            mHiddenViews.remove(mNavButtonView);
-        }
-        if (mNavButtonView != null) {
-            mNavButtonView.setImageDrawable(icon);
-        }
-    }
-
-    /**
-     * Return the current drawable used as the navigation icon.
-     *
-     * @return The navigation icon drawable
-     *
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_navigationIcon
-     */
-    @Nullable
-    public Drawable getNavigationIcon() {
-        return mNavButtonView != null ? mNavButtonView.getDrawable() : null;
-    }
-
-    /**
-     * Set a listener to respond to navigation events.
-     *
-     * <p>This listener will be called whenever the user clicks the navigation button
-     * at the start of the toolbar. An icon must be set for the navigation button to appear.</p>
-     *
-     * @param listener Listener to set
-     * @see #setNavigationIcon(android.graphics.drawable.Drawable)
-     */
-    public void setNavigationOnClickListener(OnClickListener listener) {
-        ensureNavButtonView();
-        mNavButtonView.setOnClickListener(listener);
-    }
-
-    /**
-     * Return the Menu shown in the toolbar.
-     *
-     * <p>Applications that wish to populate the toolbar's menu can do so from here. To use
-     * an XML menu resource, use {@link #inflateMenu(int)}.</p>
-     *
-     * @return The toolbar's Menu
-     */
-    public Menu getMenu() {
-        ensureMenu();
-        return mMenuView.getMenu();
-    }
-
-    /**
-     * Set the icon to use for the overflow button.
-     *
-     * @param icon Drawable to set, may be null to clear the icon
-     */
-    public void setOverflowIcon(@Nullable Drawable icon) {
-        ensureMenu();
-        mMenuView.setOverflowIcon(icon);
-    }
-
-    /**
-     * Return the current drawable used as the overflow icon.
-     *
-     * @return The overflow icon drawable
-     */
-    @Nullable
-    public Drawable getOverflowIcon() {
-        ensureMenu();
-        return mMenuView.getOverflowIcon();
-    }
-
-    private void ensureMenu() {
-        ensureMenuView();
-        if (mMenuView.peekMenu() == null) {
-            // Initialize a new menu for the first time.
-            final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu();
-            if (mExpandedMenuPresenter == null) {
-                mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
-            }
-            mMenuView.setExpandedActionViewsExclusive(true);
-            menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext);
-        }
-    }
-
-    private void ensureMenuView() {
-        if (mMenuView == null) {
-            mMenuView = new ActionMenuView(getContext());
-            mMenuView.setPopupTheme(mPopupTheme);
-            mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
-            mMenuView.setMenuCallbacks(mActionMenuPresenterCallback, mMenuBuilderCallback);
-            final LayoutParams lp = generateDefaultLayoutParams();
-            lp.gravity = GravityCompat.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
-            mMenuView.setLayoutParams(lp);
-            addSystemView(mMenuView, false);
-        }
-    }
-
-    private MenuInflater getMenuInflater() {
-        return new SupportMenuInflater(getContext());
-    }
-
-    /**
-     * Inflate a menu resource into this toolbar.
-     *
-     * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not
-     * be modified or removed.</p>
-     *
-     * @param resId ID of a menu resource to inflate
-     */
-    public void inflateMenu(@MenuRes int resId) {
-        getMenuInflater().inflate(resId, getMenu());
-    }
-
-    /**
-     * Set a listener to respond to menu item click events.
-     *
-     * <p>This listener will be invoked whenever a user selects a menu item from
-     * the action buttons presented at the end of the toolbar or the associated overflow.</p>
-     *
-     * @param listener Listener to set
-     */
-    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
-        mOnMenuItemClickListener = listener;
-    }
-
-    /**
-     * Sets the content insets for this toolbar relative to layout direction.
-     *
-     * <p>The content inset affects the valid area for Toolbar content other than
-     * the navigation button and menu. Insets define the minimum margin for these components
-     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
-     *
-     * @param contentInsetStart Content inset for the toolbar starting edge
-     * @param contentInsetEnd Content inset for the toolbar ending edge
-     *
-     * @see #setContentInsetsAbsolute(int, int)
-     * @see #getContentInsetStart()
-     * @see #getContentInsetEnd()
-     * @see #getContentInsetLeft()
-     * @see #getContentInsetRight()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEnd
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStart
-     */
-    public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
-        ensureContentInsets();
-        mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
-    }
-
-    /**
-     * Gets the starting content inset for this toolbar.
-     *
-     * <p>The content inset affects the valid area for Toolbar content other than
-     * the navigation button and menu. Insets define the minimum margin for these components
-     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
-     *
-     * @return The starting content inset for this toolbar
-     *
-     * @see #setContentInsetsRelative(int, int)
-     * @see #setContentInsetsAbsolute(int, int)
-     * @see #getContentInsetEnd()
-     * @see #getContentInsetLeft()
-     * @see #getContentInsetRight()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStart
-     */
-    public int getContentInsetStart() {
-        return mContentInsets != null ? mContentInsets.getStart() : 0;
-    }
-
-    /**
-     * Gets the ending content inset for this toolbar.
-     *
-     * <p>The content inset affects the valid area for Toolbar content other than
-     * the navigation button and menu. Insets define the minimum margin for these components
-     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
-     *
-     * @return The ending content inset for this toolbar
-     *
-     * @see #setContentInsetsRelative(int, int)
-     * @see #setContentInsetsAbsolute(int, int)
-     * @see #getContentInsetStart()
-     * @see #getContentInsetLeft()
-     * @see #getContentInsetRight()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEnd
-     */
-    public int getContentInsetEnd() {
-        return mContentInsets != null ? mContentInsets.getEnd() : 0;
-    }
-
-    /**
-     * Sets the content insets for this toolbar.
-     *
-     * <p>The content inset affects the valid area for Toolbar content other than
-     * the navigation button and menu. Insets define the minimum margin for these components
-     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
-     *
-     * @param contentInsetLeft Content inset for the toolbar's left edge
-     * @param contentInsetRight Content inset for the toolbar's right edge
-     *
-     * @see #setContentInsetsAbsolute(int, int)
-     * @see #getContentInsetStart()
-     * @see #getContentInsetEnd()
-     * @see #getContentInsetLeft()
-     * @see #getContentInsetRight()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetLeft
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetRight
-     */
-    public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
-        ensureContentInsets();
-        mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
-    }
-
-    /**
-     * Gets the left content inset for this toolbar.
-     *
-     * <p>The content inset affects the valid area for Toolbar content other than
-     * the navigation button and menu. Insets define the minimum margin for these components
-     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
-     *
-     * @return The left content inset for this toolbar
-     *
-     * @see #setContentInsetsRelative(int, int)
-     * @see #setContentInsetsAbsolute(int, int)
-     * @see #getContentInsetStart()
-     * @see #getContentInsetEnd()
-     * @see #getContentInsetRight()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetLeft
-     */
-    public int getContentInsetLeft() {
-        return mContentInsets != null ? mContentInsets.getLeft() : 0;
-    }
-
-    /**
-     * Gets the right content inset for this toolbar.
-     *
-     * <p>The content inset affects the valid area for Toolbar content other than
-     * the navigation button and menu. Insets define the minimum margin for these components
-     * and can be used to effectively align Toolbar content along well-known gridlines.</p>
-     *
-     * @return The right content inset for this toolbar
-     *
-     * @see #setContentInsetsRelative(int, int)
-     * @see #setContentInsetsAbsolute(int, int)
-     * @see #getContentInsetStart()
-     * @see #getContentInsetEnd()
-     * @see #getContentInsetLeft()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetRight
-     */
-    public int getContentInsetRight() {
-        return mContentInsets != null ? mContentInsets.getRight() : 0;
-    }
-
-    /**
-     * Gets the start content inset to use when a navigation button is present.
-     *
-     * <p>Different content insets are often called for when additional buttons are present
-     * in the toolbar, as well as at different toolbar sizes. The larger value of
-     * {@link #getContentInsetStart()} and this value will be used during layout.</p>
-     *
-     * @return the start content inset used when a navigation icon has been set in pixels
-     *
-     * @see #setContentInsetStartWithNavigation(int)
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStartWithNavigation
-     */
-    public int getContentInsetStartWithNavigation() {
-        return mContentInsetStartWithNavigation != RtlSpacingHelper.UNDEFINED
-                ? mContentInsetStartWithNavigation
-                : getContentInsetStart();
-    }
-
-    /**
-     * Sets the start content inset to use when a navigation button is present.
-     *
-     * <p>Different content insets are often called for when additional buttons are present
-     * in the toolbar, as well as at different toolbar sizes. The larger value of
-     * {@link #getContentInsetStart()} and this value will be used during layout.</p>
-     *
-     * @param insetStartWithNavigation the inset to use when a navigation icon has been set
-     *                                 in pixels
-     *
-     * @see #getContentInsetStartWithNavigation()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetStartWithNavigation
-     */
-    public void setContentInsetStartWithNavigation(int insetStartWithNavigation) {
-        if (insetStartWithNavigation < 0) {
-            insetStartWithNavigation = RtlSpacingHelper.UNDEFINED;
-        }
-        if (insetStartWithNavigation != mContentInsetStartWithNavigation) {
-            mContentInsetStartWithNavigation = insetStartWithNavigation;
-            if (getNavigationIcon() != null) {
-                requestLayout();
-            }
-        }
-    }
-
-    /**
-     * Gets the end content inset to use when action buttons are present.
-     *
-     * <p>Different content insets are often called for when additional buttons are present
-     * in the toolbar, as well as at different toolbar sizes. The larger value of
-     * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
-     *
-     * @return the end content inset used when a menu has been set in pixels
-     *
-     * @see #setContentInsetEndWithActions(int)
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEndWithActions
-     */
-    public int getContentInsetEndWithActions() {
-        return mContentInsetEndWithActions != RtlSpacingHelper.UNDEFINED
-                ? mContentInsetEndWithActions
-                : getContentInsetEnd();
-    }
-
-    /**
-     * Sets the start content inset to use when action buttons are present.
-     *
-     * <p>Different content insets are often called for when additional buttons are present
-     * in the toolbar, as well as at different toolbar sizes. The larger value of
-     * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
-     *
-     * @param insetEndWithActions the inset to use when a menu has been set in pixels
-     *
-     * @see #getContentInsetEndWithActions()
-     * @attr ref android.support.v7.appcompat.R.styleable#Toolbar_contentInsetEndWithActions
-     */
-    public void setContentInsetEndWithActions(int insetEndWithActions) {
-        if (insetEndWithActions < 0) {
-            insetEndWithActions = RtlSpacingHelper.UNDEFINED;
-        }
-        if (insetEndWithActions != mContentInsetEndWithActions) {
-            mContentInsetEndWithActions = insetEndWithActions;
-            if (getNavigationIcon() != null) {
-                requestLayout();
-            }
-        }
-    }
-
-    /**
-     * Gets the content inset that will be used on the starting side of the bar in the current
-     * toolbar configuration.
-     *
-     * @return the current content inset start in pixels
-     *
-     * @see #getContentInsetStartWithNavigation()
-     */
-    public int getCurrentContentInsetStart() {
-        return getNavigationIcon() != null
-                ? Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0))
-                : getContentInsetStart();
-    }
-
-    /**
-     * Gets the content inset that will be used on the ending side of the bar in the current
-     * toolbar configuration.
-     *
-     * @return the current content inset end in pixels
-     *
-     * @see #getContentInsetEndWithActions()
-     */
-    public int getCurrentContentInsetEnd() {
-        boolean hasActions = false;
-        if (mMenuView != null) {
-            final MenuBuilder mb = mMenuView.peekMenu();
-            hasActions = mb != null && mb.hasVisibleItems();
-        }
-        return hasActions
-                ? Math.max(getContentInsetEnd(), Math.max(mContentInsetEndWithActions, 0))
-                : getContentInsetEnd();
-    }
-
-    /**
-     * Gets the content inset that will be used on the left side of the bar in the current
-     * toolbar configuration.
-     *
-     * @return the current content inset left in pixels
-     *
-     * @see #getContentInsetStartWithNavigation()
-     * @see #getContentInsetEndWithActions()
-     */
-    public int getCurrentContentInsetLeft() {
-        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
-                ? getCurrentContentInsetEnd()
-                : getCurrentContentInsetStart();
-    }
-
-    /**
-     * Gets the content inset that will be used on the right side of the bar in the current
-     * toolbar configuration.
-     *
-     * @return the current content inset right in pixels
-     *
-     * @see #getContentInsetStartWithNavigation()
-     * @see #getContentInsetEndWithActions()
-     */
-    public int getCurrentContentInsetRight() {
-        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
-                ? getCurrentContentInsetStart()
-                : getCurrentContentInsetEnd();
-    }
-
-    private void ensureNavButtonView() {
-        if (mNavButtonView == null) {
-            mNavButtonView = new AppCompatImageButton(getContext(), null,
-                    R.attr.toolbarNavigationButtonStyle);
-            final LayoutParams lp = generateDefaultLayoutParams();
-            lp.gravity = GravityCompat.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
-            mNavButtonView.setLayoutParams(lp);
-        }
-    }
-
-    void ensureCollapseButtonView() {
-        if (mCollapseButtonView == null) {
-            mCollapseButtonView = new AppCompatImageButton(getContext(), null,
-                    R.attr.toolbarNavigationButtonStyle);
-            mCollapseButtonView.setImageDrawable(mCollapseIcon);
-            mCollapseButtonView.setContentDescription(mCollapseDescription);
-            final LayoutParams lp = generateDefaultLayoutParams();
-            lp.gravity = GravityCompat.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
-            lp.mViewType = LayoutParams.EXPANDED;
-            mCollapseButtonView.setLayoutParams(lp);
-            mCollapseButtonView.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    collapseActionView();
-                }
-            });
-        }
-    }
-
-    private void addSystemView(View v, boolean allowHide) {
-        final ViewGroup.LayoutParams vlp = v.getLayoutParams();
-        final LayoutParams lp;
-        if (vlp == null) {
-            lp = generateDefaultLayoutParams();
-        } else if (!checkLayoutParams(vlp)) {
-            lp = generateLayoutParams(vlp);
-        } else {
-            lp = (LayoutParams) vlp;
-        }
-        lp.mViewType = LayoutParams.SYSTEM;
-
-        if (allowHide && mExpandedActionView != null) {
-            v.setLayoutParams(lp);
-            mHiddenViews.add(v);
-        } else {
-            addView(v, lp);
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        SavedState state = new SavedState(super.onSaveInstanceState());
-
-        if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
-            state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
-        }
-
-        state.isOverflowOpen = isOverflowMenuShowing();
-        return state;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-
-        final SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-
-        final Menu menu = mMenuView != null ? mMenuView.peekMenu() : null;
-        if (ss.expandedMenuItemId != 0 && mExpandedMenuPresenter != null && menu != null) {
-            final MenuItem item = menu.findItem(ss.expandedMenuItemId);
-            if (item != null) {
-                item.expandActionView();
-            }
-        }
-
-        if (ss.isOverflowOpen) {
-            postShowOverflowMenu();
-        }
-    }
-
-    private void postShowOverflowMenu() {
-        removeCallbacks(mShowOverflowMenuRunnable);
-        post(mShowOverflowMenuRunnable);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        removeCallbacks(mShowOverflowMenuRunnable);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        // Toolbars always eat touch events, but should still respect the touch event dispatch
-        // contract. If the normal View implementation doesn't want the events, we'll just silently
-        // eat the rest of the gesture without reporting the events to the default implementation
-        // since that's what it expects.
-
-        final int action = ev.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN) {
-            mEatingTouch = false;
-        }
-
-        if (!mEatingTouch) {
-            final boolean handled = super.onTouchEvent(ev);
-            if (action == MotionEvent.ACTION_DOWN && !handled) {
-                mEatingTouch = true;
-            }
-        }
-
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
-            mEatingTouch = false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent ev) {
-        // Same deal as onTouchEvent() above. Eat all hover events, but still
-        // respect the touch event dispatch contract.
-
-        final int action = ev.getActionMasked();
-        if (action == MotionEvent.ACTION_HOVER_ENTER) {
-            mEatingHover = false;
-        }
-
-        if (!mEatingHover) {
-            final boolean handled = super.onHoverEvent(ev);
-            if (action == MotionEvent.ACTION_HOVER_ENTER && !handled) {
-                mEatingHover = true;
-            }
-        }
-
-        if (action == MotionEvent.ACTION_HOVER_EXIT || action == MotionEvent.ACTION_CANCEL) {
-            mEatingHover = false;
-        }
-
-        return true;
-    }
-
-    private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed,
-            int parentHeightSpec, int heightUsed, int heightConstraint) {
-        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-
-        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
-                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
-                        + widthUsed, lp.width);
-        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
-                getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin
-                        + heightUsed, lp.height);
-
-        final int childHeightMode = MeasureSpec.getMode(childHeightSpec);
-        if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) {
-            final int size = childHeightMode != MeasureSpec.UNSPECIFIED ?
-                    Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) :
-                    heightConstraint;
-            childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
-        }
-        child.measure(childWidthSpec, childHeightSpec);
-    }
-
-    /**
-     * Returns the width + uncollapsed margins
-     */
-    private int measureChildCollapseMargins(View child,
-            int parentWidthMeasureSpec, int widthUsed,
-            int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) {
-        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-
-        final int leftDiff = lp.leftMargin - collapsingMargins[0];
-        final int rightDiff = lp.rightMargin - collapsingMargins[1];
-        final int leftMargin = Math.max(0, leftDiff);
-        final int rightMargin = Math.max(0, rightDiff);
-        final int hMargins = leftMargin + rightMargin;
-        collapsingMargins[0] = Math.max(0, -leftDiff);
-        collapsingMargins[1] = Math.max(0, -rightDiff);
-
-        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
-                getPaddingLeft() + getPaddingRight() + hMargins + widthUsed, lp.width);
-        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
-                getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin
-                        + heightUsed, lp.height);
-
-        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-        return child.getMeasuredWidth() + hMargins;
-    }
-
-    /**
-     * Returns true if the Toolbar is collapsible and has no child views with a measured size > 0.
-     */
-    private boolean shouldCollapse() {
-        if (!mCollapsible) return false;
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (shouldLayout(child) && child.getMeasuredWidth() > 0 &&
-                    child.getMeasuredHeight() > 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = 0;
-        int height = 0;
-        int childState = 0;
-
-        final int[] collapsingMargins = mTempMargins;
-        final int marginStartIndex;
-        final int marginEndIndex;
-        if (ViewUtils.isLayoutRtl(this)) {
-            marginStartIndex = 1;
-            marginEndIndex = 0;
-        } else {
-            marginStartIndex = 0;
-            marginEndIndex = 1;
-        }
-
-        // System views measure first.
-
-        int navWidth = 0;
-        if (shouldLayout(mNavButtonView)) {
-            measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0,
-                    mMaxButtonHeight);
-            navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
-            height = Math.max(height, mNavButtonView.getMeasuredHeight() +
-                    getVerticalMargins(mNavButtonView));
-            childState = View.combineMeasuredStates(childState,
-                    mNavButtonView.getMeasuredState());
-        }
-
-        if (shouldLayout(mCollapseButtonView)) {
-            measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width,
-                    heightMeasureSpec, 0, mMaxButtonHeight);
-            navWidth = mCollapseButtonView.getMeasuredWidth() +
-                    getHorizontalMargins(mCollapseButtonView);
-            height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
-                    getVerticalMargins(mCollapseButtonView));
-            childState = View.combineMeasuredStates(childState,
-                    mCollapseButtonView.getMeasuredState());
-        }
-
-        final int contentInsetStart = getCurrentContentInsetStart();
-        width += Math.max(contentInsetStart, navWidth);
-        collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
-
-        int menuWidth = 0;
-        if (shouldLayout(mMenuView)) {
-            measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0,
-                    mMaxButtonHeight);
-            menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
-            height = Math.max(height, mMenuView.getMeasuredHeight() +
-                    getVerticalMargins(mMenuView));
-            childState = View.combineMeasuredStates(childState,
-                    mMenuView.getMeasuredState());
-        }
-
-        final int contentInsetEnd = getCurrentContentInsetEnd();
-        width += Math.max(contentInsetEnd, menuWidth);
-        collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
-
-        if (shouldLayout(mExpandedActionView)) {
-            width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width,
-                    heightMeasureSpec, 0, collapsingMargins);
-            height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
-                    getVerticalMargins(mExpandedActionView));
-            childState = View.combineMeasuredStates(childState,
-                    mExpandedActionView.getMeasuredState());
-        }
-
-        if (shouldLayout(mLogoView)) {
-            width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width,
-                    heightMeasureSpec, 0, collapsingMargins);
-            height = Math.max(height, mLogoView.getMeasuredHeight() +
-                    getVerticalMargins(mLogoView));
-            childState = View.combineMeasuredStates(childState,
-                    mLogoView.getMeasuredState());
-        }
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) {
-                // We already got all system views above. Skip them and GONE views.
-                continue;
-            }
-
-            width += measureChildCollapseMargins(child, widthMeasureSpec, width,
-                    heightMeasureSpec, 0, collapsingMargins);
-            height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
-            childState = View.combineMeasuredStates(childState, child.getMeasuredState());
-        }
-
-        int titleWidth = 0;
-        int titleHeight = 0;
-        final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
-        final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
-        if (shouldLayout(mTitleTextView)) {
-            titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec,
-                    width + titleHorizMargins, heightMeasureSpec, titleVertMargins,
-                    collapsingMargins);
-            titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
-            titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
-            childState = View.combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
-        }
-        if (shouldLayout(mSubtitleTextView)) {
-            titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
-                    widthMeasureSpec, width + titleHorizMargins,
-                    heightMeasureSpec, titleHeight + titleVertMargins,
-                    collapsingMargins));
-            titleHeight += mSubtitleTextView.getMeasuredHeight() +
-                    getVerticalMargins(mSubtitleTextView);
-            childState = View.combineMeasuredStates(childState,
-                    mSubtitleTextView.getMeasuredState());
-        }
-
-        width += titleWidth;
-        height = Math.max(height, titleHeight);
-
-        // Measurement already took padding into account for available space for the children,
-        // add it in for the final size.
-        width += getPaddingLeft() + getPaddingRight();
-        height += getPaddingTop() + getPaddingBottom();
-
-        final int measuredWidth = View.resolveSizeAndState(
-                Math.max(width, getSuggestedMinimumWidth()),
-                widthMeasureSpec, childState & View.MEASURED_STATE_MASK);
-        final int measuredHeight = View.resolveSizeAndState(
-                Math.max(height, getSuggestedMinimumHeight()),
-                heightMeasureSpec, childState << View.MEASURED_HEIGHT_STATE_SHIFT);
-
-        setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        final boolean isRtl =  ViewCompat.getLayoutDirection(this) ==  ViewCompat.LAYOUT_DIRECTION_RTL;
-        final int width = getWidth();
-        final int height = getHeight();
-        final int paddingLeft = getPaddingLeft();
-        final int paddingRight = getPaddingRight();
-        final int paddingTop = getPaddingTop();
-        final int paddingBottom = getPaddingBottom();
-        int left = paddingLeft;
-        int right = width - paddingRight;
-
-        final int[] collapsingMargins = mTempMargins;
-        collapsingMargins[0] = collapsingMargins[1] = 0;
-
-        // Align views within the minimum toolbar height, if set.
-        final int minHeight = ViewCompat.getMinimumHeight(this);
-        final int alignmentHeight = minHeight >= 0 ? Math.min(minHeight, b - t) : 0;
-
-        if (shouldLayout(mNavButtonView)) {
-            if (isRtl) {
-                right = layoutChildRight(mNavButtonView, right, collapsingMargins,
-                        alignmentHeight);
-            } else {
-                left = layoutChildLeft(mNavButtonView, left, collapsingMargins,
-                        alignmentHeight);
-            }
-        }
-
-        if (shouldLayout(mCollapseButtonView)) {
-            if (isRtl) {
-                right = layoutChildRight(mCollapseButtonView, right, collapsingMargins,
-                        alignmentHeight);
-            } else {
-                left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins,
-                        alignmentHeight);
-            }
-        }
-
-        if (shouldLayout(mMenuView)) {
-            if (isRtl) {
-                left = layoutChildLeft(mMenuView, left, collapsingMargins,
-                        alignmentHeight);
-            } else {
-                right = layoutChildRight(mMenuView, right, collapsingMargins,
-                        alignmentHeight);
-            }
-        }
-
-        final int contentInsetLeft = getCurrentContentInsetLeft();
-        final int contentInsetRight = getCurrentContentInsetRight();
-        collapsingMargins[0] = Math.max(0, contentInsetLeft - left);
-        collapsingMargins[1] = Math.max(0, contentInsetRight - (width - paddingRight - right));
-        left = Math.max(left, contentInsetLeft);
-        right = Math.min(right, width - paddingRight - contentInsetRight);
-
-        if (shouldLayout(mExpandedActionView)) {
-            if (isRtl) {
-                right = layoutChildRight(mExpandedActionView, right, collapsingMargins,
-                        alignmentHeight);
-            } else {
-                left = layoutChildLeft(mExpandedActionView, left, collapsingMargins,
-                        alignmentHeight);
-            }
-        }
-
-        if (shouldLayout(mLogoView)) {
-            if (isRtl) {
-                right = layoutChildRight(mLogoView, right, collapsingMargins,
-                        alignmentHeight);
-            } else {
-                left = layoutChildLeft(mLogoView, left, collapsingMargins,
-                        alignmentHeight);
-            }
-        }
-
-        final boolean layoutTitle = shouldLayout(mTitleTextView);
-        final boolean layoutSubtitle = shouldLayout(mSubtitleTextView);
-        int titleHeight = 0;
-        if (layoutTitle) {
-            final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
-            titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
-        }
-        if (layoutSubtitle) {
-            final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
-            titleHeight += lp.topMargin + mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin;
-        }
-
-        if (layoutTitle || layoutSubtitle) {
-            int titleTop;
-            final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView;
-            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);
-
-            switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
-                case Gravity.TOP:
-                    titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop;
-                    break;
-                default:
-                case Gravity.CENTER_VERTICAL:
-                    final int space = height - paddingTop - paddingBottom;
-                    int spaceAbove = (space - titleHeight) / 2;
-                    if (spaceAbove < toplp.topMargin + mTitleMarginTop) {
-                        spaceAbove = toplp.topMargin + mTitleMarginTop;
-                    } else {
-                        final int spaceBelow = height - paddingBottom - titleHeight -
-                                spaceAbove - paddingTop;
-                        if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) {
-                            spaceAbove = Math.max(0, spaceAbove -
-                                    (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow));
-                        }
-                    }
-                    titleTop = paddingTop + spaceAbove;
-                    break;
-                case Gravity.BOTTOM:
-                    titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom -
-                            titleHeight;
-                    break;
-            }
-            if (isRtl) {
-                final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1];
-                right -= Math.max(0, rd);
-                collapsingMargins[1] = Math.max(0, -rd);
-                int titleRight = right;
-                int subtitleRight = right;
-
-                if (layoutTitle) {
-                    final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
-                    final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
-                    final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
-                    mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
-                    titleRight = titleLeft - mTitleMarginEnd;
-                    titleTop = titleBottom + lp.bottomMargin;
-                }
-                if (layoutSubtitle) {
-                    final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
-                    titleTop += lp.topMargin;
-                    final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
-                    final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
-                    mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
-                    subtitleRight = subtitleRight - mTitleMarginEnd;
-                    titleTop = subtitleBottom + lp.bottomMargin;
-                }
-                if (titleHasWidth) {
-                    right = Math.min(titleRight, subtitleRight);
-                }
-            } else {
-                final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0];
-                left += Math.max(0, ld);
-                collapsingMargins[0] = Math.max(0, -ld);
-                int titleLeft = left;
-                int subtitleLeft = left;
-
-                if (layoutTitle) {
-                    final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
-                    final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
-                    final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
-                    mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
-                    titleLeft = titleRight + mTitleMarginEnd;
-                    titleTop = titleBottom + lp.bottomMargin;
-                }
-                if (layoutSubtitle) {
-                    final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
-                    titleTop += lp.topMargin;
-                    final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
-                    final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
-                    mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
-                    subtitleLeft = subtitleRight + mTitleMarginEnd;
-                    titleTop = subtitleBottom + lp.bottomMargin;
-                }
-                if (titleHasWidth) {
-                    left = Math.max(titleLeft, subtitleLeft);
-                }
-            }
-        }
-
-        // Get all remaining children sorted for layout. This is all prepared
-        // such that absolute layout direction can be used below.
-
-        addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
-        final int leftViewsCount = mTempViews.size();
-        for (int i = 0; i < leftViewsCount; i++) {
-            left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins,
-                    alignmentHeight);
-        }
-
-        addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
-        final int rightViewsCount = mTempViews.size();
-        for (int i = 0; i < rightViewsCount; i++) {
-            right = layoutChildRight(mTempViews.get(i), right, collapsingMargins,
-                    alignmentHeight);
-        }
-
-        // Centered views try to center with respect to the whole bar, but views pinned
-        // to the left or right can push the mass of centered views to one side or the other.
-        addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
-        final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins);
-        final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
-        final int halfCenterViewsWidth = centerViewsWidth / 2;
-        int centerLeft = parentCenter - halfCenterViewsWidth;
-        final int centerRight = centerLeft + centerViewsWidth;
-        if (centerLeft < left) {
-            centerLeft = left;
-        } else if (centerRight > right) {
-            centerLeft -= centerRight - right;
-        }
-
-        final int centerViewsCount = mTempViews.size();
-        for (int i = 0; i < centerViewsCount; i++) {
-            centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins,
-                    alignmentHeight);
-        }
-
-        mTempViews.clear();
-    }
-
-    private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) {
-        int collapseLeft = collapsingMargins[0];
-        int collapseRight = collapsingMargins[1];
-        int width = 0;
-        final int count = views.size();
-        for (int i = 0; i < count; i++) {
-            final View v = views.get(i);
-            final LayoutParams lp = (LayoutParams) v.getLayoutParams();
-            final int l = lp.leftMargin - collapseLeft;
-            final int r = lp.rightMargin - collapseRight;
-            final int leftMargin = Math.max(0, l);
-            final int rightMargin = Math.max(0, r);
-            collapseLeft = Math.max(0, -l);
-            collapseRight = Math.max(0, -r);
-            width += leftMargin + v.getMeasuredWidth() + rightMargin;
-        }
-        return width;
-    }
-
-    private int layoutChildLeft(View child, int left, int[] collapsingMargins,
-            int alignmentHeight) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final int l = lp.leftMargin - collapsingMargins[0];
-        left += Math.max(0, l);
-        collapsingMargins[0] = Math.max(0, -l);
-        final int top = getChildTop(child, alignmentHeight);
-        final int childWidth = child.getMeasuredWidth();
-        child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
-        left += childWidth + lp.rightMargin;
-        return left;
-    }
-
-    private int layoutChildRight(View child, int right, int[] collapsingMargins,
-            int alignmentHeight) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final int r = lp.rightMargin - collapsingMargins[1];
-        right -= Math.max(0, r);
-        collapsingMargins[1] = Math.max(0, -r);
-        final int top = getChildTop(child, alignmentHeight);
-        final int childWidth = child.getMeasuredWidth();
-        child.layout(right - childWidth, top, right, top + child.getMeasuredHeight());
-        right -= childWidth + lp.leftMargin;
-        return right;
-    }
-
-    private int getChildTop(View child, int alignmentHeight) {
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        final int childHeight = child.getMeasuredHeight();
-        final int alignmentOffset = alignmentHeight > 0 ? (childHeight - alignmentHeight) / 2 : 0;
-        switch (getChildVerticalGravity(lp.gravity)) {
-            case Gravity.TOP:
-                return getPaddingTop() - alignmentOffset;
-
-            case Gravity.BOTTOM:
-                return getHeight() - getPaddingBottom() - childHeight
-                        - lp.bottomMargin - alignmentOffset;
-
-            default:
-            case Gravity.CENTER_VERTICAL:
-                final int paddingTop = getPaddingTop();
-                final int paddingBottom = getPaddingBottom();
-                final int height = getHeight();
-                final int space = height - paddingTop - paddingBottom;
-                int spaceAbove = (space - childHeight) / 2;
-                if (spaceAbove < lp.topMargin) {
-                    spaceAbove = lp.topMargin;
-                } else {
-                    final int spaceBelow = height - paddingBottom - childHeight -
-                            spaceAbove - paddingTop;
-                    if (spaceBelow < lp.bottomMargin) {
-                        spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow));
-                    }
-                }
-                return paddingTop + spaceAbove;
-        }
-    }
-
-    private int getChildVerticalGravity(int gravity) {
-        final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK;
-        switch (vgrav) {
-            case Gravity.TOP:
-            case Gravity.BOTTOM:
-            case Gravity.CENTER_VERTICAL:
-                return vgrav;
-            default:
-                return mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        }
-    }
-
-    /**
-     * Prepare a list of non-SYSTEM child views. If the layout direction is RTL
-     * this will be in reverse child order.
-     *
-     * @param views List to populate. It will be cleared before use.
-     * @param gravity Horizontal gravity to match against
-     */
-    private void addCustomViewsWithGravity(List<View> views, int gravity) {
-        final boolean isRtl =  ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL;
-        final int childCount = getChildCount();
-        final int absGrav = GravityCompat.getAbsoluteGravity(gravity,
-                ViewCompat.getLayoutDirection(this));
-
-        views.clear();
-
-        if (isRtl) {
-            for (int i = childCount - 1; i >= 0; i--) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
-                        getChildHorizontalGravity(lp.gravity) == absGrav) {
-                    views.add(child);
-                }
-            }
-        } else {
-            for (int i = 0; i < childCount; i++) {
-                final View child = getChildAt(i);
-                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
-                        getChildHorizontalGravity(lp.gravity) == absGrav) {
-                    views.add(child);
-                }
-            }
-        }
-    }
-
-    private int getChildHorizontalGravity(int gravity) {
-        final int ld =  ViewCompat.getLayoutDirection(this);
-        final int absGrav = GravityCompat.getAbsoluteGravity(gravity, ld);
-        final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK;
-        switch (hGrav) {
-            case Gravity.LEFT:
-            case Gravity.RIGHT:
-            case Gravity.CENTER_HORIZONTAL:
-                return hGrav;
-            default:
-                return ld == ViewCompat.LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT;
-        }
-    }
-
-    private boolean shouldLayout(View view) {
-        return view != null && view.getParent() == this && view.getVisibility() != GONE;
-    }
-
-    private int getHorizontalMargins(View v) {
-        final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
-        return MarginLayoutParamsCompat.getMarginStart(mlp) +
-                MarginLayoutParamsCompat.getMarginEnd(mlp);
-    }
-
-    private int getVerticalMargins(View v) {
-        final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
-        return mlp.topMargin + mlp.bottomMargin;
-    }
-
-    @Override
-    public LayoutParams generateLayoutParams(AttributeSet attrs) {
-        return new LayoutParams(getContext(), attrs);
-    }
-
-    @Override
-    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        if (p instanceof LayoutParams) {
-            return new LayoutParams((LayoutParams) p);
-        } else if (p instanceof ActionBar.LayoutParams) {
-            return new LayoutParams((ActionBar.LayoutParams) p);
-        } else if (p instanceof MarginLayoutParams) {
-            return new LayoutParams((MarginLayoutParams) p);
-        } else {
-            return new LayoutParams(p);
-        }
-    }
-
-    @Override
-    protected LayoutParams generateDefaultLayoutParams() {
-        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-    }
-
-    @Override
-    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
-        return super.checkLayoutParams(p) && p instanceof LayoutParams;
-    }
-
-    private static boolean isCustomView(View child) {
-        return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
-    }
-
-    /** @hide */
-    @RestrictTo(LIBRARY_GROUP)
-    public DecorToolbar getWrapper() {
-        if (mWrapper == null) {
-            mWrapper = new ToolbarWidgetWrapper(this, true);
-        }
-        return mWrapper;
-    }
-
-    void removeChildrenForExpandedActionView() {
-        final int childCount = getChildCount();
-        // Go backwards since we're removing from the list
-        for (int i = childCount - 1; i >= 0; i--) {
-            final View child = getChildAt(i);
-            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
-                removeViewAt(i);
-                mHiddenViews.add(child);
-            }
-        }
-    }
-
-    void addChildrenForExpandedActionView() {
-        final int count = mHiddenViews.size();
-        // Re-add in reverse order since we removed in reverse order
-        for (int i = count - 1; i >= 0; i--) {
-            addView(mHiddenViews.get(i));
-        }
-        mHiddenViews.clear();
-    }
-
-    private boolean isChildOrHidden(View child) {
-        return child.getParent() == this || mHiddenViews.contains(child);
-    }
-
-    /**
-     * Force the toolbar to collapse to zero-height during measurement if
-     * it could be considered "empty" (no visible elements with nonzero measured size)
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setCollapsible(boolean collapsible) {
-        mCollapsible = collapsible;
-        requestLayout();
-    }
-
-    /**
-     * Must be called before the menu is accessed
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP)
-    public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
-        mActionMenuPresenterCallback = pcb;
-        mMenuBuilderCallback = mcb;
-        if (mMenuView != null) {
-            mMenuView.setMenuCallbacks(pcb, mcb);
-        }
-    }
-
-    private void ensureContentInsets() {
-        if (mContentInsets == null) {
-            mContentInsets = new RtlSpacingHelper();
-        }
-    }
-
-    /**
-     * Accessor to enable LayoutLib to get ActionMenuPresenter directly.
-     */
-    ActionMenuPresenter getOuterActionMenuPresenter() {
-        return mOuterActionMenuPresenter;
-    }
-
-    Context getPopupContext() {
-        return mPopupContext;
-    }
-
-    /**
-     * Interface responsible for receiving menu item click events if the items themselves
-     * do not have individual item click listeners.
-     */
-    public interface OnMenuItemClickListener {
-        /**
-         * This method will be invoked when a menu item is clicked if the item itself did
-         * not already handle the event.
-         *
-         * @param item {@link MenuItem} that was clicked
-         * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
-         */
-        public boolean onMenuItemClick(MenuItem item);
-    }
-
-    /**
-     * Layout information for child views of Toolbars.
-     *
-     * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
-     * ActionBar API. See
-     * {@link android.support.v7.app.AppCompatActivity#setSupportActionBar(Toolbar)
-     * AppCompatActivity.setSupportActionBar}
-     * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
-     */
-    public static class LayoutParams extends ActionBar.LayoutParams {
-        static final int CUSTOM = 0;
-        static final int SYSTEM = 1;
-        static final int EXPANDED = 2;
-
-        int mViewType = CUSTOM;
-
-        public LayoutParams(@NonNull Context c, AttributeSet attrs) {
-            super(c, attrs);
-        }
-
-        public LayoutParams(int width, int height) {
-            super(width, height);
-            this.gravity = Gravity.CENTER_VERTICAL | GravityCompat.START;
-        }
-
-        public LayoutParams(int width, int height, int gravity) {
-            super(width, height);
-            this.gravity = gravity;
-        }
-
-        public LayoutParams(int gravity) {
-            this(WRAP_CONTENT, MATCH_PARENT, gravity);
-        }
-
-        public LayoutParams(LayoutParams source) {
-            super(source);
-
-            mViewType = source.mViewType;
-        }
-
-        public LayoutParams(ActionBar.LayoutParams source) {
-            super(source);
-        }
-
-        public LayoutParams(MarginLayoutParams source) {
-            super(source);
-            // ActionBar.LayoutParams doesn't have a MarginLayoutParams constructor.
-            // Fake it here and copy over the relevant data.
-            copyMarginsFromCompat(source);
-        }
-
-        public LayoutParams(ViewGroup.LayoutParams source) {
-            super(source);
-        }
-
-        void copyMarginsFromCompat(MarginLayoutParams source) {
-            this.leftMargin = source.leftMargin;
-            this.topMargin = source.topMargin;
-            this.rightMargin = source.rightMargin;
-            this.bottomMargin = source.bottomMargin;
-        }
-    }
-
-    public static class SavedState extends AbsSavedState {
-        int expandedMenuItemId;
-        boolean isOverflowOpen;
-
-        public SavedState(Parcel source) {
-            this(source, null);
-        }
-
-        public SavedState(Parcel source, ClassLoader loader) {
-            super(source, loader);
-            expandedMenuItemId = source.readInt();
-            isOverflowOpen = source.readInt() != 0;
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeInt(expandedMenuItemId);
-            out.writeInt(isOverflowOpen ? 1 : 0);
-        }
-
-        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];
-            }
-        };
-    }
-
-    private class ExpandedActionViewMenuPresenter implements MenuPresenter {
-        MenuBuilder mMenu;
-        MenuItemImpl mCurrentExpandedItem;
-
-        ExpandedActionViewMenuPresenter() {
-        }
-
-        @Override
-        public void initForMenu(Context context, MenuBuilder menu) {
-            // Clear the expanded action view when menus change.
-            if (mMenu != null && mCurrentExpandedItem != null) {
-                mMenu.collapseItemActionView(mCurrentExpandedItem);
-            }
-            mMenu = menu;
-        }
-
-        @Override
-        public MenuView getMenuView(ViewGroup root) {
-            return null;
-        }
-
-        @Override
-        public void updateMenuView(boolean cleared) {
-            // Make sure the expanded item we have is still there.
-            if (mCurrentExpandedItem != null) {
-                boolean found = false;
-
-                if (mMenu != null) {
-                    final int count = mMenu.size();
-                    for (int i = 0; i < count; i++) {
-                        final MenuItem item = mMenu.getItem(i);
-                        if (item == mCurrentExpandedItem) {
-                            found = true;
-                            break;
-                        }
-                    }
-                }
-
-                if (!found) {
-                    // The item we had expanded disappeared. Collapse.
-                    collapseItemActionView(mMenu, mCurrentExpandedItem);
-                }
-            }
-        }
-
-        @Override
-        public void setCallback(Callback cb) {
-        }
-
-        @Override
-        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
-            return false;
-        }
-
-        @Override
-        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
-        }
-
-        @Override
-        public boolean flagActionItems() {
-            return false;
-        }
-
-        @Override
-        public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
-            ensureCollapseButtonView();
-            ViewParent collapseButtonParent = mCollapseButtonView.getParent();
-            if (collapseButtonParent != Toolbar.this) {
-                if (collapseButtonParent instanceof ViewGroup) {
-                    ((ViewGroup) collapseButtonParent).removeView(mCollapseButtonView);
-                }
-                addView(mCollapseButtonView);
-            }
-            mExpandedActionView = item.getActionView();
-            mCurrentExpandedItem = item;
-            ViewParent expandedActionParent = mExpandedActionView.getParent();
-            if (expandedActionParent != Toolbar.this) {
-                if (expandedActionParent instanceof ViewGroup) {
-                    ((ViewGroup) expandedActionParent).removeView(mExpandedActionView);
-                }
-                final LayoutParams lp = generateDefaultLayoutParams();
-                lp.gravity = GravityCompat.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
-                lp.mViewType = LayoutParams.EXPANDED;
-                mExpandedActionView.setLayoutParams(lp);
-                addView(mExpandedActionView);
-            }
-
-            removeChildrenForExpandedActionView();
-            requestLayout();
-            item.setActionViewExpanded(true);
-
-            if (mExpandedActionView instanceof CollapsibleActionView) {
-                ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
-            }
-
-            return true;
-        }
-
-        @Override
-        public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
-            // Do this before detaching the actionview from the hierarchy, in case
-            // it needs to dismiss the soft keyboard, etc.
-            if (mExpandedActionView instanceof CollapsibleActionView) {
-                ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
-            }
-
-            removeView(mExpandedActionView);
-            removeView(mCollapseButtonView);
-            mExpandedActionView = null;
-
-            addChildrenForExpandedActionView();
-            mCurrentExpandedItem = null;
-            requestLayout();
-            item.setActionViewExpanded(false);
-
-            return true;
-        }
-
-        @Override
-        public int getId() {
-            return 0;
-        }
-
-        @Override
-        public Parcelable onSaveInstanceState() {
-            return null;
-        }
-
-        @Override
-        public void onRestoreInstanceState(Parcelable state) {
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/ToolbarWidgetWrapper.java b/android/support/v7/widget/ToolbarWidgetWrapper.java
deleted file mode 100644
index 491ed90..0000000
--- a/android/support/v7/widget/ToolbarWidgetWrapper.java
+++ /dev/null
@@ -1,679 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.app.ActionBar;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Parcelable;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.support.v7.app.WindowDecorActionBar;
-import android.support.v7.appcompat.R;
-import android.support.v7.content.res.AppCompatResources;
-import android.support.v7.view.menu.ActionMenuItem;
-import android.support.v7.view.menu.MenuBuilder;
-import android.support.v7.view.menu.MenuPresenter;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.Spinner;
-import android.widget.SpinnerAdapter;
-
-/**
- * Internal class used to interact with the Toolbar widget without
- * exposing interface methods to the public API.
- *
- * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView
- * so that either variant acting as a
- * {@link WindowDecorActionBar WindowDecorActionBar} can behave
- * in the same way.</p>
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ToolbarWidgetWrapper implements DecorToolbar {
-    private static final String TAG = "ToolbarWidgetWrapper";
-
-    private static final int AFFECTS_LOGO_MASK =
-            ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO;
-    // Default fade duration for fading in/out tool bar.
-    private static final long DEFAULT_FADE_DURATION_MS = 200;
-
-    Toolbar mToolbar;
-
-    private int mDisplayOpts;
-    private View mTabView;
-    private Spinner mSpinner;
-    private View mCustomView;
-
-    private Drawable mIcon;
-    private Drawable mLogo;
-    private Drawable mNavIcon;
-
-    private boolean mTitleSet;
-    CharSequence mTitle;
-    private CharSequence mSubtitle;
-    private CharSequence mHomeDescription;
-
-    Window.Callback mWindowCallback;
-    boolean mMenuPrepared;
-    private ActionMenuPresenter mActionMenuPresenter;
-
-    private int mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
-
-    private int mDefaultNavigationContentDescription = 0;
-    private Drawable mDefaultNavigationIcon;
-
-    public ToolbarWidgetWrapper(Toolbar toolbar, boolean style) {
-        this(toolbar, style, R.string.abc_action_bar_up_description,
-                R.drawable.abc_ic_ab_back_material);
-    }
-
-    public ToolbarWidgetWrapper(Toolbar toolbar, boolean style,
-            int defaultNavigationContentDescription, int defaultNavigationIcon) {
-        mToolbar = toolbar;
-        mTitle = toolbar.getTitle();
-        mSubtitle = toolbar.getSubtitle();
-        mTitleSet = mTitle != null;
-        mNavIcon = toolbar.getNavigationIcon();
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(),
-                    null, R.styleable.ActionBar, R.attr.actionBarStyle, 0);
-        mDefaultNavigationIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
-        if (style) {
-            final CharSequence title = a.getText(R.styleable.ActionBar_title);
-            if (!TextUtils.isEmpty(title)) {
-                setTitle(title);
-            }
-
-            final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
-            if (!TextUtils.isEmpty(subtitle)) {
-                setSubtitle(subtitle);
-            }
-
-            final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo);
-            if (logo != null) {
-                setLogo(logo);
-            }
-
-            final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
-            if (icon != null) {
-                setIcon(icon);
-            }
-            if (mNavIcon == null && mDefaultNavigationIcon != null) {
-                setNavigationIcon(mDefaultNavigationIcon);
-            }
-            setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
-
-            final int customNavId = a.getResourceId(
-                    R.styleable.ActionBar_customNavigationLayout, 0);
-            if (customNavId != 0) {
-                setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,
-                        mToolbar, false));
-                setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM);
-            }
-
-            final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
-            if (height > 0) {
-                final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams();
-                lp.height = height;
-                mToolbar.setLayoutParams(lp);
-            }
-
-            final int contentInsetStart = a.getDimensionPixelOffset(
-                    R.styleable.ActionBar_contentInsetStart, -1);
-            final int contentInsetEnd = a.getDimensionPixelOffset(
-                    R.styleable.ActionBar_contentInsetEnd, -1);
-            if (contentInsetStart >= 0 || contentInsetEnd >= 0) {
-                mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0),
-                        Math.max(contentInsetEnd, 0));
-            }
-
-            final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
-            if (titleTextStyle != 0) {
-                mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle);
-            }
-
-            final int subtitleTextStyle = a.getResourceId(
-                    R.styleable.ActionBar_subtitleTextStyle, 0);
-            if (subtitleTextStyle != 0) {
-                mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
-            }
-
-            final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0);
-            if (popupTheme != 0) {
-                mToolbar.setPopupTheme(popupTheme);
-            }
-        } else {
-            mDisplayOpts = detectDisplayOptions();
-        }
-        a.recycle();
-
-        setDefaultNavigationContentDescription(defaultNavigationContentDescription);
-        mHomeDescription = mToolbar.getNavigationContentDescription();
-
-        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
-            final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(),
-                    0, android.R.id.home, 0, 0, mTitle);
-            @Override
-            public void onClick(View v) {
-                if (mWindowCallback != null && mMenuPrepared) {
-                    mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem);
-                }
-            }
-        });
-    }
-
-    @Override
-    public void setDefaultNavigationContentDescription(int defaultNavigationContentDescription) {
-        if (defaultNavigationContentDescription == mDefaultNavigationContentDescription) {
-            return;
-        }
-        mDefaultNavigationContentDescription = defaultNavigationContentDescription;
-        if (TextUtils.isEmpty(mToolbar.getNavigationContentDescription())) {
-            setNavigationContentDescription(mDefaultNavigationContentDescription);
-        }
-    }
-
-    private int detectDisplayOptions() {
-        int opts = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME |
-                ActionBar.DISPLAY_USE_LOGO;
-        if (mToolbar.getNavigationIcon() != null) {
-            opts |= ActionBar.DISPLAY_HOME_AS_UP;
-            mDefaultNavigationIcon = mToolbar.getNavigationIcon();
-        }
-        return opts;
-    }
-
-    @Override
-    public ViewGroup getViewGroup() {
-        return mToolbar;
-    }
-
-    @Override
-    public Context getContext() {
-        return mToolbar.getContext();
-    }
-
-    @Override
-    public boolean hasExpandedActionView() {
-        return mToolbar.hasExpandedActionView();
-    }
-
-    @Override
-    public void collapseActionView() {
-        mToolbar.collapseActionView();
-    }
-
-    @Override
-    public void setWindowCallback(Window.Callback cb) {
-        mWindowCallback = cb;
-    }
-
-    @Override
-    public void setWindowTitle(CharSequence title) {
-        // "Real" title always trumps window title.
-        if (!mTitleSet) {
-            setTitleInt(title);
-        }
-    }
-
-    @Override
-    public CharSequence getTitle() {
-        return mToolbar.getTitle();
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mTitleSet = true;
-        setTitleInt(title);
-    }
-
-    private void setTitleInt(CharSequence title) {
-        mTitle = title;
-        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
-            mToolbar.setTitle(title);
-        }
-    }
-
-    @Override
-    public CharSequence getSubtitle() {
-        return mToolbar.getSubtitle();
-    }
-
-    @Override
-    public void setSubtitle(CharSequence subtitle) {
-        mSubtitle = subtitle;
-        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
-            mToolbar.setSubtitle(subtitle);
-        }
-    }
-
-    @Override
-    public void initProgress() {
-        Log.i(TAG, "Progress display unsupported");
-    }
-
-    @Override
-    public void initIndeterminateProgress() {
-        Log.i(TAG, "Progress display unsupported");
-    }
-
-    @Override
-    public boolean hasIcon() {
-        return mIcon != null;
-    }
-
-    @Override
-    public boolean hasLogo() {
-        return mLogo != null;
-    }
-
-    @Override
-    public void setIcon(int resId) {
-        setIcon(resId != 0 ? AppCompatResources.getDrawable(getContext(), resId) : null);
-    }
-
-    @Override
-    public void setIcon(Drawable d) {
-        mIcon = d;
-        updateToolbarLogo();
-    }
-
-    @Override
-    public void setLogo(int resId) {
-        setLogo(resId != 0 ? AppCompatResources.getDrawable(getContext(), resId) : null);
-    }
-
-    @Override
-    public void setLogo(Drawable d) {
-        mLogo = d;
-        updateToolbarLogo();
-    }
-
-    private void updateToolbarLogo() {
-        Drawable logo = null;
-        if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) {
-            if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) {
-                logo = mLogo != null ? mLogo : mIcon;
-            } else {
-                logo = mIcon;
-            }
-        }
-        mToolbar.setLogo(logo);
-    }
-
-    @Override
-    public boolean canShowOverflowMenu() {
-        return mToolbar.canShowOverflowMenu();
-    }
-
-    @Override
-    public boolean isOverflowMenuShowing() {
-        return mToolbar.isOverflowMenuShowing();
-    }
-
-    @Override
-    public boolean isOverflowMenuShowPending() {
-        return mToolbar.isOverflowMenuShowPending();
-    }
-
-    @Override
-    public boolean showOverflowMenu() {
-        return mToolbar.showOverflowMenu();
-    }
-
-    @Override
-    public boolean hideOverflowMenu() {
-        return mToolbar.hideOverflowMenu();
-    }
-
-    @Override
-    public void setMenuPrepared() {
-        mMenuPrepared = true;
-    }
-
-    @Override
-    public void setMenu(Menu menu, MenuPresenter.Callback cb) {
-        if (mActionMenuPresenter == null) {
-            mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext());
-            mActionMenuPresenter.setId(R.id.action_menu_presenter);
-        }
-        mActionMenuPresenter.setCallback(cb);
-        mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter);
-    }
-
-    @Override
-    public void dismissPopupMenus() {
-        mToolbar.dismissPopupMenus();
-    }
-
-    @Override
-    public int getDisplayOptions() {
-        return mDisplayOpts;
-    }
-
-    @Override
-    public void setDisplayOptions(int newOpts) {
-        final int oldOpts = mDisplayOpts;
-        final int changed = oldOpts ^ newOpts;
-        mDisplayOpts = newOpts;
-        if (changed != 0) {
-            if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-                if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-                    updateHomeAccessibility();
-                }
-                updateNavigationIcon();
-            }
-
-            if ((changed & AFFECTS_LOGO_MASK) != 0) {
-                updateToolbarLogo();
-            }
-
-            if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
-                if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
-                    mToolbar.setTitle(mTitle);
-                    mToolbar.setSubtitle(mSubtitle);
-                } else {
-                    mToolbar.setTitle(null);
-                    mToolbar.setSubtitle(null);
-                }
-            }
-
-            if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) {
-                if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
-                    mToolbar.addView(mCustomView);
-                } else {
-                    mToolbar.removeView(mCustomView);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void setEmbeddedTabView(ScrollingTabContainerView tabView) {
-        if (mTabView != null && mTabView.getParent() == mToolbar) {
-            mToolbar.removeView(mTabView);
-        }
-        mTabView = tabView;
-        if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
-            mToolbar.addView(mTabView, 0);
-            Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams();
-            lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
-            lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-            lp.gravity = Gravity.START | Gravity.BOTTOM;
-            tabView.setAllowCollapse(true);
-        }
-    }
-
-    @Override
-    public boolean hasEmbeddedTabs() {
-        return mTabView != null;
-    }
-
-    @Override
-    public boolean isTitleTruncated() {
-        return mToolbar.isTitleTruncated();
-    }
-
-    @Override
-    public void setCollapsible(boolean collapsible) {
-        mToolbar.setCollapsible(collapsible);
-    }
-
-    @Override
-    public void setHomeButtonEnabled(boolean enable) {
-        // Ignore
-    }
-
-    @Override
-    public int getNavigationMode() {
-        return mNavigationMode;
-    }
-
-    @Override
-    public void setNavigationMode(int mode) {
-        final int oldMode = mNavigationMode;
-        if (mode != oldMode) {
-            switch (oldMode) {
-                case ActionBar.NAVIGATION_MODE_LIST:
-                    if (mSpinner != null && mSpinner.getParent() == mToolbar) {
-                        mToolbar.removeView(mSpinner);
-                    }
-                    break;
-                case ActionBar.NAVIGATION_MODE_TABS:
-                    if (mTabView != null && mTabView.getParent() == mToolbar) {
-                        mToolbar.removeView(mTabView);
-                    }
-                    break;
-            }
-
-            mNavigationMode = mode;
-
-            switch (mode) {
-                case ActionBar.NAVIGATION_MODE_STANDARD:
-                    break;
-                case ActionBar.NAVIGATION_MODE_LIST:
-                    ensureSpinner();
-                    mToolbar.addView(mSpinner, 0);
-                    break;
-                case ActionBar.NAVIGATION_MODE_TABS:
-                    if (mTabView != null) {
-                        mToolbar.addView(mTabView, 0);
-                        Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams();
-                        lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
-                        lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-                        lp.gravity = Gravity.START | Gravity.BOTTOM;
-                    }
-                    break;
-                default:
-                    throw new IllegalArgumentException("Invalid navigation mode " + mode);
-            }
-        }
-    }
-
-    private void ensureSpinner() {
-        if (mSpinner == null) {
-            mSpinner = new AppCompatSpinner(getContext(), null, R.attr.actionDropDownStyle);
-            Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
-            mSpinner.setLayoutParams(lp);
-        }
-    }
-
-    @Override
-    public void setDropdownParams(SpinnerAdapter adapter,
-            AdapterView.OnItemSelectedListener listener) {
-        ensureSpinner();
-        mSpinner.setAdapter(adapter);
-        mSpinner.setOnItemSelectedListener(listener);
-    }
-
-    @Override
-    public void setDropdownSelectedPosition(int position) {
-        if (mSpinner == null) {
-            throw new IllegalStateException(
-                    "Can't set dropdown selected position without an adapter");
-        }
-        mSpinner.setSelection(position);
-    }
-
-    @Override
-    public int getDropdownSelectedPosition() {
-        return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0;
-    }
-
-    @Override
-    public int getDropdownItemCount() {
-        return mSpinner != null ? mSpinner.getCount() : 0;
-    }
-
-    @Override
-    public void setCustomView(View view) {
-        if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
-            mToolbar.removeView(mCustomView);
-        }
-        mCustomView = view;
-        if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
-            mToolbar.addView(mCustomView);
-        }
-    }
-
-    @Override
-    public View getCustomView() {
-        return mCustomView;
-    }
-
-    @Override
-    public void animateToVisibility(int visibility) {
-        ViewPropertyAnimatorCompat anim = setupAnimatorToVisibility(visibility,
-                DEFAULT_FADE_DURATION_MS);
-        if (anim != null) {
-            anim.start();
-        }
-    }
-
-    @Override
-    public ViewPropertyAnimatorCompat setupAnimatorToVisibility(final int visibility,
-            final long duration) {
-        return ViewCompat.animate(mToolbar)
-                .alpha(visibility == View.VISIBLE ? 1f : 0f)
-                .setDuration(duration)
-                .setListener(new ViewPropertyAnimatorListenerAdapter() {
-                    private boolean mCanceled = false;
-
-                    @Override
-                    public void onAnimationStart(View view) {
-                        mToolbar.setVisibility(View.VISIBLE);
-                    }
-
-                    @Override
-                    public void onAnimationEnd(View view) {
-                        if (!mCanceled) {
-                            mToolbar.setVisibility(visibility);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationCancel(View view) {
-                        mCanceled = true;
-                    }
-                });
-    }
-
-    @Override
-    public void setNavigationIcon(Drawable icon) {
-        mNavIcon = icon;
-        updateNavigationIcon();
-    }
-
-    @Override
-    public void setNavigationIcon(int resId) {
-        setNavigationIcon(resId != 0 ? AppCompatResources.getDrawable(getContext(), resId) : null);
-    }
-
-    @Override
-    public void setDefaultNavigationIcon(Drawable defaultNavigationIcon) {
-        if (mDefaultNavigationIcon != defaultNavigationIcon) {
-            mDefaultNavigationIcon = defaultNavigationIcon;
-            updateNavigationIcon();
-        }
-    }
-
-    private void updateNavigationIcon() {
-        if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-            mToolbar.setNavigationIcon(mNavIcon != null ? mNavIcon : mDefaultNavigationIcon);
-        } else {
-            mToolbar.setNavigationIcon(null);
-        }
-    }
-
-    @Override
-    public void setNavigationContentDescription(CharSequence description) {
-        mHomeDescription = description;
-        updateHomeAccessibility();
-    }
-
-    @Override
-    public void setNavigationContentDescription(int resId) {
-        setNavigationContentDescription(resId == 0 ? null : getContext().getString(resId));
-    }
-
-    private void updateHomeAccessibility() {
-        if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
-            if (TextUtils.isEmpty(mHomeDescription)) {
-                mToolbar.setNavigationContentDescription(mDefaultNavigationContentDescription);
-            } else {
-                mToolbar.setNavigationContentDescription(mHomeDescription);
-            }
-        }
-    }
-
-    @Override
-    public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) {
-        mToolbar.saveHierarchyState(toolbarStates);
-    }
-
-    @Override
-    public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) {
-        mToolbar.restoreHierarchyState(toolbarStates);
-    }
-
-    @Override
-    public void setBackgroundDrawable(Drawable d) {
-        ViewCompat.setBackground(mToolbar, d);
-    }
-
-    @Override
-    public int getHeight() {
-        return mToolbar.getHeight();
-    }
-
-    @Override
-    public void setVisibility(int visible) {
-        mToolbar.setVisibility(visible);
-    }
-
-    @Override
-    public int getVisibility() {
-        return mToolbar.getVisibility();
-    }
-
-    @Override
-    public void setMenuCallbacks(MenuPresenter.Callback actionMenuPresenterCallback,
-            MenuBuilder.Callback menuBuilderCallback) {
-        mToolbar.setMenuCallbacks(actionMenuPresenterCallback, menuBuilderCallback);
-    }
-
-    @Override
-    public Menu getMenu() {
-        return mToolbar.getMenu();
-    }
-
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/TooltipCompat.java b/android/support/v7/widget/TooltipCompat.java
deleted file mode 100644
index 4a583da..0000000
--- a/android/support/v7/widget/TooltipCompat.java
+++ /dev/null
@@ -1,48 +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.v7.widget;
-
-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  {
-    /**
-     * 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) {
-        if (Build.VERSION.SDK_INT >= 26) {
-            view.setTooltipText(tooltipText);
-        } else {
-            TooltipCompatHandler.setTooltipText(view, tooltipText);
-        }
-    }
-
-    private TooltipCompat() {}
-}
diff --git a/android/support/v7/widget/TooltipCompatHandler.java b/android/support/v7/widget/TooltipCompatHandler.java
deleted file mode 100644
index 8de44e6..0000000
--- a/android/support/v7/widget/TooltipCompatHandler.java
+++ /dev/null
@@ -1,251 +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.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.support.v4.view.ViewConfigurationCompat;
-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 int mHoverSlop;
-
-    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 scheduled to show a tooltip, triggered by a hover
-    // (there can be only one).
-    private static TooltipCompatHandler sPendingHandler;
-
-    // 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) {
-        // The code below is not attempting to update the tooltip text
-        // for a pending or currently active tooltip, because it may lead
-        // to updating the wrong tooltip in in some rare cases (e.g. when
-        // action menu item views are recycled). Instead, the tooltip is
-        // canceled/hidden. This might still be the wrong tooltip,
-        // but hiding a wrong tooltip is less disruptive UX.
-        if (sPendingHandler != null && sPendingHandler.mAnchor == view) {
-            setPendingHandler(null);
-        }
-        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;
-        mHoverSlop = ViewConfigurationCompat.getScaledHoverSlop(
-                ViewConfiguration.get(mAnchor.getContext()));
-        clearAnchorPos();
-
-        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 && updateAnchorPos(event)) {
-                    setPendingHandler(this);
-                }
-                break;
-            case MotionEvent.ACTION_HOVER_EXIT:
-                clearAnchorPos();
-                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;
-        }
-        setPendingHandler(null);
-        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;
-                clearAnchorPos();
-                mAnchor.removeOnAttachStateChangeListener(this);
-            } else {
-                Log.e(TAG, "sActiveHandler.mPopup == null");
-            }
-        }
-        if (sPendingHandler == this) {
-            setPendingHandler(null);
-        }
-        mAnchor.removeCallbacks(mHideRunnable);
-    }
-
-    private static void setPendingHandler(TooltipCompatHandler handler) {
-        if (sPendingHandler != null) {
-            sPendingHandler.cancelPendingShow();
-        }
-        sPendingHandler = handler;
-        if (sPendingHandler != null) {
-            sPendingHandler.scheduleShow();
-        }
-    }
-
-    private void scheduleShow() {
-        mAnchor.postDelayed(mShowRunnable, ViewConfiguration.getLongPressTimeout());
-    }
-
-    private void cancelPendingShow() {
-        mAnchor.removeCallbacks(mShowRunnable);
-    }
-
-    /**
-     * Update the anchor position if it significantly (that is by at least mHoverSlope)
-     * different from the previously stored position. Ignoring insignificant changes
-     * filters out the jitter which is typical for such input sources as stylus.
-     *
-     * @return True if the position has been updated.
-     */
-    private boolean updateAnchorPos(MotionEvent event) {
-        final int newAnchorX = (int) event.getX();
-        final int newAnchorY = (int) event.getY();
-        if (Math.abs(newAnchorX - mAnchorX) <= mHoverSlop
-                && Math.abs(newAnchorY - mAnchorY) <= mHoverSlop) {
-            return false;
-        }
-        mAnchorX = newAnchorX;
-        mAnchorY = newAnchorY;
-        return true;
-    }
-
-    /**
-     *  Clear the anchor position to ensure that the next change is considered significant.
-     */
-    private void clearAnchorPos() {
-        mAnchorX = Integer.MAX_VALUE;
-        mAnchorY = Integer.MAX_VALUE;
-    }
-}
diff --git a/android/support/v7/widget/TooltipPopup.java b/android/support/v7/widget/TooltipPopup.java
deleted file mode 100644
index 396fe05..0000000
--- a/android/support/v7/widget/TooltipPopup.java
+++ /dev/null
@@ -1,208 +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.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.ViewGroup;
-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.abc_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) {
-        outParams.token = anchorView.getApplicationWindowToken();
-        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 - appView.getWidth() / 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) {
-        View rootView = anchorView.getRootView();
-        ViewGroup.LayoutParams lp = rootView.getLayoutParams();
-        if (lp instanceof WindowManager.LayoutParams
-                && (((WindowManager.LayoutParams) lp).type
-                    == WindowManager.LayoutParams.TYPE_APPLICATION)) {
-            // This covers regular app windows and Dialog windows.
-            return rootView;
-        }
-        // For non-application window types (such as popup windows) try to find the main app window
-        // through the context.
-        Context context = anchorView.getContext();
-        while (context instanceof ContextWrapper) {
-            if (context instanceof Activity) {
-                return ((Activity) context).getWindow().getDecorView();
-            } else {
-                context = ((ContextWrapper) context).getBaseContext();
-            }
-        }
-        // Main app window not found, fall back to the anchor's root view. There is no guarantee
-        // that the tooltip position will be computed correctly.
-        return rootView;
-    }
-}
diff --git a/android/support/v7/widget/VectorEnabledTintResources.java b/android/support/v7/widget/VectorEnabledTintResources.java
deleted file mode 100644
index e184e85..0000000
--- a/android/support/v7/widget/VectorEnabledTintResources.java
+++ /dev/null
@@ -1,76 +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.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RestrictTo;
-import android.support.v7.app.AppCompatDelegate;
-
-import java.lang.ref.WeakReference;
-
-/**
- * This class allows us to intercept calls so that we can tint resources (if applicable), and
- * inflate vector resources from within drawable containers pre-L.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class VectorEnabledTintResources extends Resources {
-
-    public static boolean shouldBeUsed() {
-        return AppCompatDelegate.isCompatVectorFromResourcesEnabled()
-                && Build.VERSION.SDK_INT <= MAX_SDK_WHERE_REQUIRED;
-    }
-
-    /**
-     * The maximum API level where this class is needed.
-     */
-    public static final int MAX_SDK_WHERE_REQUIRED = 20;
-
-    private final WeakReference<Context> mContextRef;
-
-    public VectorEnabledTintResources(@NonNull final Context context,
-            @NonNull final Resources res) {
-        super(res.getAssets(), res.getDisplayMetrics(), res.getConfiguration());
-        mContextRef = new WeakReference<>(context);
-    }
-
-    /**
-     * We intercept this call so that we tint the result (if applicable). This is needed for
-     * things like {@link android.graphics.drawable.DrawableContainer}s which can retrieve
-     * their children via this method.
-     */
-    @Override
-    public Drawable getDrawable(int id) throws NotFoundException {
-        final Context context = mContextRef.get();
-        if (context != null) {
-            return AppCompatDrawableManager.get().onDrawableLoadedFromResources(context, this, id);
-        } else {
-            return super.getDrawable(id);
-        }
-    }
-
-    final Drawable superGetDrawable(int id) {
-        return super.getDrawable(id);
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/ViewBoundsCheck.java b/android/support/v7/widget/ViewBoundsCheck.java
deleted file mode 100644
index 191a069..0000000
--- a/android/support/v7/widget/ViewBoundsCheck.java
+++ /dev/null
@@ -1,274 +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.v7.widget;
-
-import android.support.annotation.IntDef;
-import android.view.View;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A utility class used to check the boundaries of a given view within its parent view based on
- * a set of boundary flags.
- */
-class ViewBoundsCheck {
-
-    static final int GT = 1 << 0;
-    static final int EQ = 1 << 1;
-    static final int LT = 1 << 2;
-
-
-    static final int CVS_PVS_POS = 0;
-    /**
-     * The child view's start should be strictly greater than parent view's start.
-     */
-    static final int FLAG_CVS_GT_PVS = GT << CVS_PVS_POS;
-
-    /**
-     * The child view's start can be equal to its parent view's start. This flag follows with GT
-     * or LT indicating greater (less) than or equal relation.
-     */
-    static final int FLAG_CVS_EQ_PVS = EQ << CVS_PVS_POS;
-
-    /**
-     * The child view's start should be strictly less than parent view's start.
-     */
-    static final int FLAG_CVS_LT_PVS = LT << CVS_PVS_POS;
-
-
-    static final int CVS_PVE_POS = 4;
-    /**
-     * The child view's start should be strictly greater than parent view's end.
-     */
-    static final int FLAG_CVS_GT_PVE = GT << CVS_PVE_POS;
-
-    /**
-     * The child view's start can be equal to its parent view's end. This flag follows with GT
-     * or LT indicating greater (less) than or equal relation.
-     */
-    static final int FLAG_CVS_EQ_PVE = EQ << CVS_PVE_POS;
-
-    /**
-     * The child view's start should be strictly less than parent view's end.
-     */
-    static final int FLAG_CVS_LT_PVE = LT << CVS_PVE_POS;
-
-
-    static final int CVE_PVS_POS = 8;
-    /**
-     * The child view's end should be strictly greater than parent view's start.
-     */
-    static final int FLAG_CVE_GT_PVS = GT << CVE_PVS_POS;
-
-    /**
-     * The child view's end can be equal to its parent view's start. This flag follows with GT
-     * or LT indicating greater (less) than or equal relation.
-     */
-    static final int FLAG_CVE_EQ_PVS = EQ << CVE_PVS_POS;
-
-    /**
-     * The child view's end should be strictly less than parent view's start.
-     */
-    static final int FLAG_CVE_LT_PVS = LT << CVE_PVS_POS;
-
-
-    static final int CVE_PVE_POS = 12;
-    /**
-     * The child view's end should be strictly greater than parent view's end.
-     */
-    static final int FLAG_CVE_GT_PVE = GT << CVE_PVE_POS;
-
-    /**
-     * The child view's end can be equal to its parent view's end. This flag follows with GT
-     * or LT indicating greater (less) than or equal relation.
-     */
-    static final int FLAG_CVE_EQ_PVE = EQ << CVE_PVE_POS;
-
-    /**
-     * The child view's end should be strictly less than parent view's end.
-     */
-    static final int FLAG_CVE_LT_PVE = LT << CVE_PVE_POS;
-
-    static final int MASK = GT | EQ | LT;
-
-    final Callback mCallback;
-    BoundFlags mBoundFlags;
-    /**
-     * The set of flags that can be passed for checking the view boundary conditions.
-     * CVS in the flag name indicates the child view, and PV indicates the parent view.\
-     * The following S, E indicate a view's start and end points, respectively.
-     * GT and LT indicate a strictly greater and less than relationship.
-     * Greater than or equal (or less than or equal) can be specified by setting both GT and EQ (or
-     * LT and EQ) flags.
-     * For instance, setting both {@link #FLAG_CVS_GT_PVS} and {@link #FLAG_CVS_EQ_PVS} indicate the
-     * child view's start should be greater than or equal to its parent start.
-     */
-    @IntDef(flag = true, value = {
-            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_GT_PVE, FLAG_CVE_EQ_PVE, FLAG_CVE_LT_PVE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ViewBounds {}
-
-    ViewBoundsCheck(Callback callback) {
-        mCallback = callback;
-        mBoundFlags = new BoundFlags();
-    }
-
-    static class BoundFlags {
-        int mBoundFlags = 0;
-        int mRvStart, mRvEnd, mChildStart, mChildEnd;
-
-        void setBounds(int rvStart, int rvEnd, int childStart, int childEnd) {
-            mRvStart = rvStart;
-            mRvEnd = rvEnd;
-            mChildStart = childStart;
-            mChildEnd = childEnd;
-        }
-
-        void setFlags(@ViewBounds int flags, int mask) {
-            mBoundFlags = (mBoundFlags & ~mask) | (flags & mask);
-        }
-
-        void addFlags(@ViewBounds int flags) {
-            mBoundFlags |= flags;
-        }
-
-        void resetFlags() {
-            mBoundFlags = 0;
-        }
-
-        int compare(int x, int y) {
-            if (x > y) {
-                return GT;
-            }
-            if (x == y) {
-                return EQ;
-            }
-            return LT;
-        }
-
-        boolean boundsMatch() {
-            if ((mBoundFlags & (MASK << CVS_PVS_POS)) != 0) {
-                if ((mBoundFlags & (compare(mChildStart, mRvStart) << CVS_PVS_POS)) == 0) {
-                    return false;
-                }
-            }
-
-            if ((mBoundFlags & (MASK << CVS_PVE_POS)) != 0) {
-                if ((mBoundFlags & (compare(mChildStart, mRvEnd) << CVS_PVE_POS)) == 0) {
-                    return false;
-                }
-            }
-
-            if ((mBoundFlags & (MASK << CVE_PVS_POS)) != 0) {
-                if ((mBoundFlags & (compare(mChildEnd, mRvStart) << CVE_PVS_POS)) == 0) {
-                    return false;
-                }
-            }
-
-            if ((mBoundFlags & (MASK << CVE_PVE_POS)) != 0) {
-                if ((mBoundFlags & (compare(mChildEnd, mRvEnd) << CVE_PVE_POS)) == 0) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    };
-
-    /**
-     * Returns the first view starting from fromIndex to toIndex in views whose bounds lie within
-     * its parent bounds based on the provided preferredBoundFlags. If no match is found based on
-     * the preferred flags, and a nonzero acceptableBoundFlags is specified, the last view whose
-     * bounds lie within its parent view based on the acceptableBoundFlags is returned. If no such
-     * view is found based on either of these two flags, null is returned.
-     * @param fromIndex The view position index to start the search from.
-     * @param toIndex The view position index to end the search at.
-     * @param preferredBoundFlags The flags indicating the preferred match. Once a match is found
-     *                            based on this flag, that view is returned instantly.
-     * @param acceptableBoundFlags The flags indicating the acceptable match if no preferred match
-     *                             is found. If so, and if acceptableBoundFlags is non-zero, the
-     *                             last matching acceptable view is returned. Otherwise, null is
-     *                             returned.
-     * @return The first view that satisfies acceptableBoundFlags or the last view satisfying
-     * acceptableBoundFlags boundary conditions.
-     */
-    View findOneViewWithinBoundFlags(int fromIndex, int toIndex,
-            @ViewBounds int preferredBoundFlags,
-            @ViewBounds int acceptableBoundFlags) {
-        final int start = mCallback.getParentStart();
-        final int end = mCallback.getParentEnd();
-        final int next = toIndex > fromIndex ? 1 : -1;
-        View acceptableMatch = null;
-        for (int i = fromIndex; i != toIndex; i += next) {
-            final View child = mCallback.getChildAt(i);
-            final int childStart = mCallback.getChildStart(child);
-            final int childEnd = mCallback.getChildEnd(child);
-            mBoundFlags.setBounds(start, end, childStart, childEnd);
-            if (preferredBoundFlags != 0) {
-                mBoundFlags.resetFlags();
-                mBoundFlags.addFlags(preferredBoundFlags);
-                if (mBoundFlags.boundsMatch()) {
-                    // found a perfect match
-                    return child;
-                }
-            }
-            if (acceptableBoundFlags != 0) {
-                mBoundFlags.resetFlags();
-                mBoundFlags.addFlags(acceptableBoundFlags);
-                if (mBoundFlags.boundsMatch()) {
-                    acceptableMatch = child;
-                }
-            }
-        }
-        return acceptableMatch;
-    }
-
-    /**
-     * Returns whether the specified view lies within the boundary condition of its parent view.
-     * @param child The child view to be checked.
-     * @param boundsFlags The flag against which the child view and parent view are matched.
-     * @return True if the view meets the boundsFlag, false otherwise.
-     */
-    boolean isViewWithinBoundFlags(View child, @ViewBounds int boundsFlags) {
-        mBoundFlags.setBounds(mCallback.getParentStart(), mCallback.getParentEnd(),
-                mCallback.getChildStart(child), mCallback.getChildEnd(child));
-        if (boundsFlags != 0) {
-            mBoundFlags.resetFlags();
-            mBoundFlags.addFlags(boundsFlags);
-            return mBoundFlags.boundsMatch();
-        }
-        return false;
-    }
-
-    /**
-     * Callback provided by the user of this class in order to retrieve information about child and
-     * parent boundaries.
-     */
-    interface Callback {
-        int getChildCount();
-        View getParent();
-        View getChildAt(int index);
-        int getParentStart();
-        int getParentEnd();
-        int getChildStart(View view);
-        int getChildEnd(View view);
-    }
-}
diff --git a/android/support/v7/widget/ViewInfoStore.java b/android/support/v7/widget/ViewInfoStore.java
deleted file mode 100644
index 68bbde4..0000000
--- a/android/support/v7/widget/ViewInfoStore.java
+++ /dev/null
@@ -1,327 +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.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;
-/**
- * This class abstracts all tracking for Views to run animations.
- */
-class ViewInfoStore {
-
-    private static final boolean DEBUG = false;
-
-    /**
-     * View data records for pre-layout
-     */
-    @VisibleForTesting
-    final ArrayMap<ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>();
-
-    @VisibleForTesting
-    final LongSparseArray<ViewHolder> mOldChangedHolders = new LongSparseArray<>();
-
-    /**
-     * Clears the state and all existing tracking data
-     */
-    void clear() {
-        mLayoutHolderMap.clear();
-        mOldChangedHolders.clear();
-    }
-
-    /**
-     * Adds the item information to the prelayout tracking
-     * @param holder The ViewHolder whose information is being saved
-     * @param info The information to save
-     */
-    void addToPreLayout(ViewHolder holder, ItemHolderInfo info) {
-        InfoRecord record = mLayoutHolderMap.get(holder);
-        if (record == null) {
-            record = InfoRecord.obtain();
-            mLayoutHolderMap.put(holder, record);
-        }
-        record.preInfo = info;
-        record.flags |= FLAG_PRE;
-    }
-
-    boolean isDisappearing(ViewHolder holder) {
-        final InfoRecord record = mLayoutHolderMap.get(holder);
-        return record != null && ((record.flags & FLAG_DISAPPEARED) != 0);
-    }
-
-    /**
-     * Finds the ItemHolderInfo for the given ViewHolder in preLayout list and removes it.
-     *
-     * @param vh The ViewHolder whose information is being queried
-     * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
-     */
-    @Nullable
-    ItemHolderInfo popFromPreLayout(ViewHolder vh) {
-        return popFromLayoutStep(vh, FLAG_PRE);
-    }
-
-    /**
-     * Finds the ItemHolderInfo for the given ViewHolder in postLayout list and removes it.
-     *
-     * @param vh The ViewHolder whose information is being queried
-     * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist
-     */
-    @Nullable
-    ItemHolderInfo popFromPostLayout(ViewHolder vh) {
-        return popFromLayoutStep(vh, FLAG_POST);
-    }
-
-    private ItemHolderInfo popFromLayoutStep(ViewHolder vh, int flag) {
-        int index = mLayoutHolderMap.indexOfKey(vh);
-        if (index < 0) {
-            return null;
-        }
-        final InfoRecord record = mLayoutHolderMap.valueAt(index);
-        if (record != null && (record.flags & flag) != 0) {
-            record.flags &= ~flag;
-            final ItemHolderInfo info;
-            if (flag == FLAG_PRE) {
-                info = record.preInfo;
-            } else if (flag == FLAG_POST) {
-                info = record.postInfo;
-            } else {
-                throw new IllegalArgumentException("Must provide flag PRE or POST");
-            }
-            // if not pre-post flag is left, clear.
-            if ((record.flags & (FLAG_PRE | FLAG_POST)) == 0) {
-                mLayoutHolderMap.removeAt(index);
-                InfoRecord.recycle(record);
-            }
-            return info;
-        }
-        return null;
-    }
-
-    /**
-     * Adds the given ViewHolder to the oldChangeHolders list
-     * @param key The key to identify the ViewHolder.
-     * @param holder The ViewHolder to store
-     */
-    void addToOldChangeHolders(long key, ViewHolder holder) {
-        mOldChangedHolders.put(key, holder);
-    }
-
-    /**
-     * Adds the given ViewHolder to the appeared in pre layout list. These are Views added by the
-     * LayoutManager during a pre-layout pass. We distinguish them from other views that were
-     * already in the pre-layout so that ItemAnimator can choose to run a different animation for
-     * them.
-     *
-     * @param holder The ViewHolder to store
-     * @param info The information to save
-     */
-    void addToAppearedInPreLayoutHolders(ViewHolder holder, ItemHolderInfo info) {
-        InfoRecord record = mLayoutHolderMap.get(holder);
-        if (record == null) {
-            record = InfoRecord.obtain();
-            mLayoutHolderMap.put(holder, record);
-        }
-        record.flags |= FLAG_APPEAR;
-        record.preInfo = info;
-    }
-
-    /**
-     * Checks whether the given ViewHolder is in preLayout list
-     * @param viewHolder The ViewHolder to query
-     *
-     * @return True if the ViewHolder is present in preLayout, false otherwise
-     */
-    boolean isInPreLayout(ViewHolder viewHolder) {
-        final InfoRecord record = mLayoutHolderMap.get(viewHolder);
-        return record != null && (record.flags & FLAG_PRE) != 0;
-    }
-
-    /**
-     * Queries the oldChangeHolder list for the given key. If they are not tracked, simply returns
-     * null.
-     * @param key The key to be used to find the ViewHolder.
-     *
-     * @return A ViewHolder if exists or null if it does not exist.
-     */
-    ViewHolder getFromOldChangeHolders(long key) {
-        return mOldChangedHolders.get(key);
-    }
-
-    /**
-     * Adds the item information to the post layout list
-     * @param holder The ViewHolder whose information is being saved
-     * @param info The information to save
-     */
-    void addToPostLayout(ViewHolder holder, ItemHolderInfo info) {
-        InfoRecord record = mLayoutHolderMap.get(holder);
-        if (record == null) {
-            record = InfoRecord.obtain();
-            mLayoutHolderMap.put(holder, record);
-        }
-        record.postInfo = info;
-        record.flags |= FLAG_POST;
-    }
-
-    /**
-     * A ViewHolder might be added by the LayoutManager just to animate its disappearance.
-     * This list holds such items so that we can animate / recycle these ViewHolders properly.
-     *
-     * @param holder The ViewHolder which disappeared during a layout.
-     */
-    void addToDisappearedInLayout(ViewHolder holder) {
-        InfoRecord record = mLayoutHolderMap.get(holder);
-        if (record == null) {
-            record = InfoRecord.obtain();
-            mLayoutHolderMap.put(holder, record);
-        }
-        record.flags |= FLAG_DISAPPEARED;
-    }
-
-    /**
-     * Removes a ViewHolder from disappearing list.
-     * @param holder The ViewHolder to be removed from the disappearing list.
-     */
-    void removeFromDisappearedInLayout(ViewHolder holder) {
-        InfoRecord record = mLayoutHolderMap.get(holder);
-        if (record == null) {
-            return;
-        }
-        record.flags &= ~FLAG_DISAPPEARED;
-    }
-
-    void process(ProcessCallback callback) {
-        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) {
-                // Appeared then disappeared. Not useful for animations.
-                callback.unused(viewHolder);
-            } else if ((record.flags & FLAG_DISAPPEARED) != 0) {
-                // Set as "disappeared" by the LayoutManager (addDisappearingView)
-                if (record.preInfo == null) {
-                    // similar to appear disappear but happened between different layout passes.
-                    // this can happen when the layout manager is using auto-measure
-                    callback.unused(viewHolder);
-                } else {
-                    callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
-                }
-            } else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {
-                // Appeared in the layout but not in the adapter (e.g. entered the viewport)
-                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
-            } else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
-                // Persistent in both passes. Animate persistence
-                callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
-            } else if ((record.flags & FLAG_PRE) != 0) {
-                // Was in pre-layout, never been added to post layout
-                callback.processDisappeared(viewHolder, record.preInfo, null);
-            } else if ((record.flags & FLAG_POST) != 0) {
-                // Was not in pre-layout, been added to post layout
-                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
-            } else if ((record.flags & FLAG_APPEAR) != 0) {
-                // Scrap view. RecyclerView will handle removing/recycling this.
-            } else if (DEBUG) {
-                throw new IllegalStateException("record without any reasonable flag combination:/");
-            }
-            InfoRecord.recycle(record);
-        }
-    }
-
-    /**
-     * Removes the ViewHolder from all list
-     * @param holder The ViewHolder which we should stop tracking
-     */
-    void removeViewHolder(ViewHolder holder) {
-        for (int i = mOldChangedHolders.size() - 1; i >= 0; i--) {
-            if (holder == mOldChangedHolders.valueAt(i)) {
-                mOldChangedHolders.removeAt(i);
-                break;
-            }
-        }
-        final InfoRecord info = mLayoutHolderMap.remove(holder);
-        if (info != null) {
-            InfoRecord.recycle(info);
-        }
-    }
-
-    void onDetach() {
-        InfoRecord.drainCache();
-    }
-
-    public void onViewDetached(ViewHolder viewHolder) {
-        removeFromDisappearedInLayout(viewHolder);
-    }
-
-    interface ProcessCallback {
-        void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
-                @Nullable ItemHolderInfo postInfo);
-        void processAppeared(ViewHolder viewHolder, @Nullable ItemHolderInfo preInfo,
-                ItemHolderInfo postInfo);
-        void processPersistent(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
-                @NonNull ItemHolderInfo postInfo);
-        void unused(ViewHolder holder);
-    }
-
-    static class InfoRecord {
-        // disappearing list
-        static final int FLAG_DISAPPEARED = 1;
-        // appear in pre layout list
-        static final int FLAG_APPEAR = 1 << 1;
-        // pre layout, this is necessary to distinguish null item info
-        static final int FLAG_PRE = 1 << 2;
-        // post layout, this is necessary to distinguish null item info
-        static final int FLAG_POST = 1 << 3;
-        static final int FLAG_APPEAR_AND_DISAPPEAR = FLAG_APPEAR | FLAG_DISAPPEARED;
-        static final int FLAG_PRE_AND_POST = FLAG_PRE | FLAG_POST;
-        static final int FLAG_APPEAR_PRE_AND_POST = FLAG_APPEAR | FLAG_PRE | FLAG_POST;
-        int flags;
-        @Nullable ItemHolderInfo preInfo;
-        @Nullable ItemHolderInfo postInfo;
-        static Pools.Pool<InfoRecord> sPool = new Pools.SimplePool<>(20);
-
-        private InfoRecord() {
-        }
-
-        static InfoRecord obtain() {
-            InfoRecord record = sPool.acquire();
-            return record == null ? new InfoRecord() : record;
-        }
-
-        static void recycle(InfoRecord record) {
-            record.flags = 0;
-            record.preInfo = null;
-            record.postInfo = null;
-            sPool.release(record);
-        }
-
-        static void drainCache() {
-            //noinspection StatementWithEmptyBody
-            while (sPool.acquire() != null);
-        }
-    }
-}
diff --git a/android/support/v7/widget/ViewInfoStoreTest.java b/android/support/v7/widget/ViewInfoStoreTest.java
deleted file mode 100644
index c416314..0000000
--- a/android/support/v7/widget/ViewInfoStoreTest.java
+++ /dev/null
@@ -1,319 +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.widget;
-
-import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
-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 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.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.test.filters.SmallTest;
-import android.support.v4.util.Pair;
-import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.View;
-
-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.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@SuppressWarnings("ConstantConditions")
-@RunWith(JUnit4.class)
-@SmallTest
-public class ViewInfoStoreTest {
-    ViewInfoStore mStore;
-    LoggingProcessCallback mCallback;
-    @Before
-    public void prepare() {
-        mStore = new ViewInfoStore();
-        mCallback = new LoggingProcessCallback();
-    }
-
-    @Test
-    public void addOverridePre() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPreLayout(vh, info);
-        MockInfo info2 = new MockInfo();
-        mStore.addToPreLayout(vh, info2);
-        assertSame(info2, find(vh, FLAG_PRE));
-    }
-
-    @Test
-    public void addOverridePost() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPostLayout(vh, info);
-        MockInfo info2 = new MockInfo();
-        mStore.addToPostLayout(vh, info2);
-        assertSame(info2, find(vh, FLAG_POST));
-    }
-
-    @Test
-    public void addRemoveAndReAdd() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        MockInfo pre = new MockInfo();
-        mStore.addToPreLayout(vh, pre);
-        MockInfo post1 = new MockInfo();
-        mStore.addToPostLayout(vh, post1);
-        mStore.onViewDetached(vh);
-        mStore.addToDisappearedInLayout(vh);
-    }
-
-    @Test
-    public void addToPreLayout() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPreLayout(vh, info);
-        assertSame(info, find(vh, FLAG_PRE));
-        assertTrue(mStore.isInPreLayout(vh));
-        mStore.removeViewHolder(vh);
-        assertFalse(mStore.isInPreLayout(vh));
-    }
-
-    @Test
-    public void addToPostLayout() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPostLayout(vh, info);
-        assertSame(info, find(vh, FLAG_POST));
-        mStore.removeViewHolder(vh);
-        assertNull(find(vh, FLAG_POST));
-    }
-
-    @Test
-    public void popFromPreLayout() {
-        assertEquals(0, sizeOf(FLAG_PRE));
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPreLayout(vh, info);
-        assertSame(info, mStore.popFromPreLayout(vh));
-        assertNull(mStore.popFromPreLayout(vh));
-    }
-
-    @Test
-    public void addToOldChangeHolders() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        mStore.addToOldChangeHolders(1, vh);
-        assertSame(vh, mStore.getFromOldChangeHolders(1));
-        mStore.removeViewHolder(vh);
-        assertNull(mStore.getFromOldChangeHolders(1));
-    }
-
-    @Test
-    public void appearListTests() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        RecyclerView.ItemAnimator.ItemHolderInfo info = new MockInfo();
-        mStore.addToAppearedInPreLayoutHolders(vh, info);
-        assertEquals(1, sizeOf(FLAG_APPEAR));
-        RecyclerView.ViewHolder vh2 = new MockViewHolder();
-        mStore.addToAppearedInPreLayoutHolders(vh2, info);
-        assertEquals(2, sizeOf(FLAG_APPEAR));
-        mStore.removeViewHolder(vh2);
-        assertEquals(1, sizeOf(FLAG_APPEAR));
-    }
-
-    @Test
-    public void disappearListTest() {
-        RecyclerView.ViewHolder vh = new MockViewHolder();
-        mStore.addToDisappearedInLayout(vh);
-        assertEquals(1, sizeOf(FLAG_DISAPPEARED));
-        mStore.addToDisappearedInLayout(vh);
-        assertEquals(1, sizeOf(FLAG_DISAPPEARED));
-        RecyclerView.ViewHolder vh2 = new MockViewHolder();
-        mStore.addToDisappearedInLayout(vh2);
-        assertEquals(2, sizeOf(FLAG_DISAPPEARED));
-        mStore.removeViewHolder(vh2);
-        assertEquals(1, sizeOf(FLAG_DISAPPEARED));
-        mStore.removeFromDisappearedInLayout(vh);
-        assertEquals(0, sizeOf(FLAG_DISAPPEARED));
-    }
-
-    @Test
-    public void processAppear() {
-        ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPostLayout(vh, info);
-        mStore.process(mCallback);
-        assertEquals(new Pair<>(null, info), mCallback.appeared.get(vh));
-        assertTrue(mCallback.disappeared.isEmpty());
-        assertTrue(mCallback.unused.isEmpty());
-        assertTrue(mCallback.persistent.isEmpty());
-    }
-
-    @Test
-    public void processDisappearNormal() {
-        ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPreLayout(vh, info);
-        mStore.process(mCallback);
-        assertEquals(new Pair<>(info, null), mCallback.disappeared.get(vh));
-        assertTrue(mCallback.appeared.isEmpty());
-        assertTrue(mCallback.unused.isEmpty());
-        assertTrue(mCallback.persistent.isEmpty());
-    }
-
-    @Test
-    public void processDisappearMissingLayout() {
-        ViewHolder vh = new MockViewHolder();
-        MockInfo info = new MockInfo();
-        mStore.addToPreLayout(vh, info);
-        mStore.addToDisappearedInLayout(vh);
-        mStore.process(mCallback);
-        assertEquals(new Pair<>(info, null), mCallback.disappeared.get(vh));
-        assertTrue(mCallback.appeared.isEmpty());
-        assertTrue(mCallback.unused.isEmpty());
-        assertTrue(mCallback.persistent.isEmpty());
-    }
-
-    @Test
-    public void processDisappearMoveOut() {
-        ViewHolder vh = new MockViewHolder();
-        MockInfo pre = new MockInfo();
-        MockInfo post = new MockInfo();
-        mStore.addToPreLayout(vh, pre);
-        mStore.addToDisappearedInLayout(vh);
-        mStore.addToPostLayout(vh, post);
-        mStore.process(mCallback);
-        assertEquals(new Pair<>(pre, post), mCallback.disappeared.get(vh));
-        assertTrue(mCallback.appeared.isEmpty());
-        assertTrue(mCallback.unused.isEmpty());
-        assertTrue(mCallback.persistent.isEmpty());
-    }
-
-    @Test
-    public void processDisappearAppear() {
-        ViewHolder vh = new MockViewHolder();
-        MockInfo pre = new MockInfo();
-        MockInfo post = new MockInfo();
-        mStore.addToPreLayout(vh, pre);
-        mStore.addToDisappearedInLayout(vh);
-        mStore.addToPostLayout(vh, post);
-        mStore.removeFromDisappearedInLayout(vh);
-        mStore.process(mCallback);
-        assertTrue(mCallback.disappeared.isEmpty());
-        assertTrue(mCallback.appeared.isEmpty());
-        assertTrue(mCallback.unused.isEmpty());
-        assertEquals(mCallback.persistent.get(vh), new Pair<>(pre, post));
-    }
-
-    @Test
-    public void processAppearAndDisappearInPostLayout() {
-        ViewHolder vh = new MockViewHolder();
-        MockInfo info1 = new MockInfo();
-        mStore.addToPostLayout(vh, info1);
-        mStore.addToDisappearedInLayout(vh);
-        mStore.process(mCallback);
-        assertTrue(mCallback.disappeared.isEmpty());
-        assertTrue(mCallback.appeared.isEmpty());
-        assertTrue(mCallback.persistent.isEmpty());
-        assertSame(mCallback.unused.get(0), vh);
-    }
-
-    static class MockViewHolder extends RecyclerView.ViewHolder {
-        public MockViewHolder() {
-            super(new View(null));
-        }
-    }
-
-    static class MockInfo extends RecyclerView.ItemAnimator.ItemHolderInfo {
-
-    }
-
-    private int sizeOf(int flags) {
-        int cnt = 0;
-        final int size = mStore.mLayoutHolderMap.size();
-        for (int i = 0; i < size; i ++) {
-            ViewInfoStore.InfoRecord record = mStore.mLayoutHolderMap.valueAt(i);
-            if ((record.flags & flags) != 0) {
-                cnt ++;
-            }
-        }
-        return cnt;
-    }
-
-    private RecyclerView.ItemAnimator.ItemHolderInfo find(RecyclerView.ViewHolder viewHolder,
-            int flags) {
-        final int size = mStore.mLayoutHolderMap.size();
-        for (int i = 0; i < size; i ++) {
-            ViewInfoStore.InfoRecord record = mStore.mLayoutHolderMap.valueAt(i);
-            RecyclerView.ViewHolder holder = mStore.mLayoutHolderMap.keyAt(i);
-            if ((record.flags & flags) != 0 && holder == viewHolder) {
-                if (flags == FLAG_PRE || flags == FLAG_APPEAR) {
-                    return record.preInfo;
-                } else if (flags == FLAG_POST) {
-                    return record.postInfo;
-                }
-                throw new UnsupportedOperationException("don't know this flag");
-            }
-        }
-        return null;
-    }
-
-    private static class LoggingProcessCallback implements ViewInfoStore.ProcessCallback {
-        final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> disappeared = new HashMap<>();
-        final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> appeared = new HashMap<>();
-        final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> persistent = new HashMap<>();
-        final List<ViewHolder> unused = new ArrayList<>();
-        @Override
-        public void processDisappeared(ViewHolder viewHolder,
-                ItemHolderInfo preInfo,
-                @Nullable ItemHolderInfo postInfo) {
-            assertNotNull(preInfo);
-            assertFalse(disappeared.containsKey(viewHolder));
-            disappeared.put(viewHolder, new Pair<>(preInfo, postInfo));
-        }
-
-        @Override
-        public void processAppeared(ViewHolder viewHolder,
-                @Nullable ItemHolderInfo preInfo, @NonNull ItemHolderInfo info) {
-            assertNotNull(info);
-            assertFalse(appeared.containsKey(viewHolder));
-            appeared.put(viewHolder, new Pair<>(preInfo, info));
-        }
-
-        @Override
-        public void processPersistent(ViewHolder viewHolder,
-                @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
-            assertFalse(persistent.containsKey(viewHolder));
-            assertNotNull(preInfo);
-            assertNotNull(postInfo);
-            persistent.put(viewHolder, new Pair<>(preInfo, postInfo));
-        }
-
-        @Override
-        public void unused(ViewHolder holder) {
-            unused.add(holder);
-        }
-    }
-
-}
diff --git a/android/support/v7/widget/ViewStubCompat.java b/android/support/v7/widget/ViewStubCompat.java
deleted file mode 100644
index 4512466..0000000
--- a/android/support/v7/widget/ViewStubCompat.java
+++ /dev/null
@@ -1,267 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.support.annotation.RestrictTo;
-import android.support.v7.appcompat.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Backport of {@link android.view.ViewStub} so that we can set the
- * {@link android.view.LayoutInflater} on devices before Jelly Bean.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class ViewStubCompat extends View {
-    private int mLayoutResource = 0;
-    private int mInflatedId;
-
-    private WeakReference<View> mInflatedViewRef;
-
-    private LayoutInflater mInflater;
-    private OnInflateListener mInflateListener;
-
-    public ViewStubCompat(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStubCompat,
-                defStyle, 0);
-
-        mInflatedId = a.getResourceId(R.styleable.ViewStubCompat_android_inflatedId, NO_ID);
-        mLayoutResource = a.getResourceId(R.styleable.ViewStubCompat_android_layout, 0);
-
-        setId(a.getResourceId(R.styleable.ViewStubCompat_android_id, NO_ID));
-        a.recycle();
-
-        setVisibility(GONE);
-        setWillNotDraw(true);
-    }
-
-    /**
-     * Returns the id taken by the inflated view. If the inflated id is
-     * {@link View#NO_ID}, the inflated view keeps its original id.
-     *
-     * @return A positive integer used to identify the inflated view or
-     *         {@link #NO_ID} if the inflated view should keep its id.
-     *
-     * @see #setInflatedId(int)
-     * @attr name android:inflatedId
-     */
-    public int getInflatedId() {
-        return mInflatedId;
-    }
-
-    /**
-     * Defines the id taken by the inflated view. If the inflated id is
-     * {@link View#NO_ID}, the inflated view keeps its original id.
-     *
-     * @param inflatedId A positive integer used to identify the inflated view or
-     *                   {@link #NO_ID} if the inflated view should keep its id.
-     *
-     * @see #getInflatedId()
-     * @attr name android:inflatedId
-     */
-    public void setInflatedId(int inflatedId) {
-        mInflatedId = inflatedId;
-    }
-
-    /**
-     * Returns the layout resource that will be used by {@link #setVisibility(int)} or
-     * {@link #inflate()} to replace this StubbedView
-     * in its parent by another view.
-     *
-     * @return The layout resource identifier used to inflate the new View.
-     *
-     * @see #setLayoutResource(int)
-     * @see #setVisibility(int)
-     * @see #inflate()
-     * @attr name android:layout
-     */
-    public int getLayoutResource() {
-        return mLayoutResource;
-    }
-
-    /**
-     * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
-     * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
-     * used to replace this StubbedView in its parent.
-     *
-     * @param layoutResource A valid layout resource identifier (different from 0.)
-     *
-     * @see #getLayoutResource()
-     * @see #setVisibility(int)
-     * @see #inflate()
-     * @attr name android:layout
-     */
-    public void setLayoutResource(int layoutResource) {
-        mLayoutResource = layoutResource;
-    }
-
-    /**
-     * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}
-     * to use the default.
-     */
-    public void setLayoutInflater(LayoutInflater inflater) {
-        mInflater = inflater;
-    }
-
-    /**
-     * Get current {@link LayoutInflater} used in {@link #inflate()}.
-     */
-    public LayoutInflater getLayoutInflater() {
-        return mInflater;
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(0, 0);
-    }
-
-    @SuppressLint("MissingSuperCall") // Intentionally not calling super method.
-    @Override
-    public void draw(Canvas canvas) {
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-    }
-
-    /**
-     * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
-     * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
-     * by the inflated layout resource. After that calls to this function are passed
-     * through to the inflated view.
-     *
-     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
-     *
-     * @see #inflate()
-     */
-    @Override
-    public void setVisibility(int visibility) {
-        if (mInflatedViewRef != null) {
-            View view = mInflatedViewRef.get();
-            if (view != null) {
-                view.setVisibility(visibility);
-            } else {
-                throw new IllegalStateException("setVisibility called on un-referenced view");
-            }
-        } else {
-            super.setVisibility(visibility);
-            if (visibility == VISIBLE || visibility == INVISIBLE) {
-                inflate();
-            }
-        }
-    }
-
-    /**
-     * Inflates the layout resource identified by {@link #getLayoutResource()}
-     * and replaces this StubbedView in its parent by the inflated layout resource.
-     *
-     * @return The inflated layout resource.
-     *
-     */
-    public View inflate() {
-        final ViewParent viewParent = getParent();
-
-        if (viewParent != null && viewParent instanceof ViewGroup) {
-            if (mLayoutResource != 0) {
-                final ViewGroup parent = (ViewGroup) viewParent;
-                final LayoutInflater factory;
-                if (mInflater != null) {
-                    factory = mInflater;
-                } else {
-                    factory = LayoutInflater.from(getContext());
-                }
-                final View view = factory.inflate(mLayoutResource, parent,
-                        false);
-
-                if (mInflatedId != NO_ID) {
-                    view.setId(mInflatedId);
-                }
-
-                final int index = parent.indexOfChild(this);
-                parent.removeViewInLayout(this);
-
-                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
-                if (layoutParams != null) {
-                    parent.addView(view, index, layoutParams);
-                } else {
-                    parent.addView(view, index);
-                }
-
-                mInflatedViewRef = new WeakReference<View>(view);
-
-                if (mInflateListener != null) {
-                    mInflateListener.onInflate(this, view);
-                }
-
-                return view;
-            } else {
-                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
-            }
-        } else {
-            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
-        }
-    }
-
-    /**
-     * Specifies the inflate listener to be notified after this ViewStub successfully
-     * inflated its layout resource.
-     *
-     * @param inflateListener The OnInflateListener to notify of successful inflation.
-     *
-     * @see android.view.ViewStub.OnInflateListener
-     */
-    public void setOnInflateListener(OnInflateListener inflateListener) {
-        mInflateListener = inflateListener;
-    }
-
-    /**
-     * Listener used to receive a notification after a ViewStub has successfully
-     * inflated its layout resource.
-     *
-     * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
-     */
-    public static interface OnInflateListener {
-        /**
-         * Invoked after a ViewStub successfully inflated its layout resource.
-         * This method is invoked after the inflated view was added to the
-         * hierarchy but before the layout pass.
-         *
-         * @param stub The ViewStub that initiated the inflation.
-         * @param inflated The inflated View.
-         */
-        void onInflate(ViewStubCompat stub, View inflated);
-    }
-}
\ No newline at end of file
diff --git a/android/support/v7/widget/ViewUtils.java b/android/support/v7/widget/ViewUtils.java
deleted file mode 100644
index 0f910c3..0000000
--- a/android/support/v7/widget/ViewUtils.java
+++ /dev/null
@@ -1,97 +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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.graphics.Rect;
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.support.v4.view.ViewCompat;
-import android.util.Log;
-import android.view.View;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public class ViewUtils {
-    private static final String TAG = "ViewUtils";
-
-    private static Method sComputeFitSystemWindowsMethod;
-
-    static {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            try {
-                sComputeFitSystemWindowsMethod = View.class.getDeclaredMethod(
-                        "computeFitSystemWindows", Rect.class, Rect.class);
-                if (!sComputeFitSystemWindowsMethod.isAccessible()) {
-                    sComputeFitSystemWindowsMethod.setAccessible(true);
-                }
-            } catch (NoSuchMethodException e) {
-                Log.d(TAG, "Could not find method computeFitSystemWindows. Oh well.");
-            }
-        }
-    }
-
-    private ViewUtils() {}
-
-    public static boolean isLayoutRtl(View view) {
-        return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
-    }
-
-    /**
-     * Allow calling the hidden method {@code computeFitSystemWindows(Rect, Rect)} through
-     * reflection on {@code view}.
-     */
-    public static void computeFitSystemWindows(View view, Rect inoutInsets, Rect outLocalInsets) {
-        if (sComputeFitSystemWindowsMethod != null) {
-            try {
-                sComputeFitSystemWindowsMethod.invoke(view, inoutInsets, outLocalInsets);
-            } catch (Exception e) {
-                Log.d(TAG, "Could not invoke computeFitSystemWindows", e);
-            }
-        }
-    }
-
-    /**
-     * Allow calling the hidden method {@code makeOptionalFitsSystem()} through reflection on
-     * {@code view}.
-     */
-    public static void makeOptionalFitsSystemWindows(View view) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-            try {
-                // We need to use getMethod() for makeOptionalFitsSystemWindows since both View
-                // and ViewGroup implement the method
-                Method method = view.getClass().getMethod("makeOptionalFitsSystemWindows");
-                if (!method.isAccessible()) {
-                    method.setAccessible(true);
-                }
-                method.invoke(view);
-            } catch (NoSuchMethodException e) {
-                Log.d(TAG, "Could not find method makeOptionalFitsSystemWindows. Oh well...");
-            } catch (InvocationTargetException e) {
-                Log.d(TAG, "Could not invoke makeOptionalFitsSystemWindows", e);
-            } catch (IllegalAccessException e) {
-                Log.d(TAG, "Could not invoke makeOptionalFitsSystemWindows", e);
-            }
-        }
-    }
-}
diff --git a/android/support/v7/widget/WithHint.java b/android/support/v7/widget/WithHint.java
deleted file mode 100644
index d14f483..0000000
--- a/android/support/v7/widget/WithHint.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.widget;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface WithHint {
-    /**
-     * Returns the hint which is displayed in the floating label, if enabled.
-     *
-     * @return the hint, or null if there isn't one set, or the hint is not enabled.
-     */
-    @Nullable
-    CharSequence getHint();
-}
diff --git a/android/support/v7/widget/helper/ItemTouchHelper.java b/android/support/v7/widget/helper/ItemTouchHelper.java
deleted file mode 100644
index d2b6a20..0000000
--- a/android/support/v7/widget/helper/ItemTouchHelper.java
+++ /dev/null
@@ -1,2438 +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.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.view.GestureDetectorCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v7.recyclerview.R;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.OnItemTouchListener;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewParent;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
- * <p>
- * It works with a RecyclerView and a Callback class, which configures what type of interactions
- * are enabled and also receives events when user performs these actions.
- * <p>
- * Depending on which functionality you support, you should override
- * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or
- * {@link Callback#onSwiped(ViewHolder, int)}.
- * <p>
- * This class is designed to work with any LayoutManager but for certain situations, it can be
- * optimized for your custom LayoutManager by extending methods in the
- * {@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. 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>.
- */
-public class ItemTouchHelper extends RecyclerView.ItemDecoration
-        implements RecyclerView.OnChildAttachStateChangeListener {
-
-    /**
-     * Up direction, used for swipe & drag control.
-     */
-    public static final int UP = 1;
-
-    /**
-     * Down direction, used for swipe & drag control.
-     */
-    public static final int DOWN = 1 << 1;
-
-    /**
-     * Left direction, used for swipe & drag control.
-     */
-    public static final int LEFT = 1 << 2;
-
-    /**
-     * Right direction, used for swipe & drag control.
-     */
-    public static final int RIGHT = 1 << 3;
-
-    // If you change these relative direction values, update Callback#convertToAbsoluteDirection,
-    // Callback#convertToRelativeDirection.
-    /**
-     * Horizontal start direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout
-     * direction. Used for swipe & drag control.
-     */
-    public static final int START = LEFT << 2;
-
-    /**
-     * Horizontal end direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout
-     * direction. Used for swipe & drag control.
-     */
-    public static final int END = RIGHT << 2;
-
-    /**
-     * ItemTouchHelper is in idle state. At this state, either there is no related motion event by
-     * the user or latest motion events have not yet triggered a swipe or drag.
-     */
-    public static final int ACTION_STATE_IDLE = 0;
-
-    /**
-     * A View is currently being swiped.
-     */
-    public static final int ACTION_STATE_SWIPE = 1;
-
-    /**
-     * A View is currently being dragged.
-     */
-    public static final int ACTION_STATE_DRAG = 2;
-
-    /**
-     * Animation type for views which are swiped successfully.
-     */
-    public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 1 << 1;
-
-    /**
-     * Animation type for views which are not completely swiped thus will animate back to their
-     * original position.
-     */
-    public static final int ANIMATION_TYPE_SWIPE_CANCEL = 1 << 2;
-
-    /**
-     * Animation type for views that were dragged and now will animate to their final position.
-     */
-    public static final int ANIMATION_TYPE_DRAG = 1 << 3;
-
-    static final String TAG = "ItemTouchHelper";
-
-    static final boolean DEBUG = false;
-
-    static final int ACTIVE_POINTER_ID_NONE = -1;
-
-    static final int DIRECTION_FLAG_COUNT = 8;
-
-    private static final int ACTION_MODE_IDLE_MASK = (1 << DIRECTION_FLAG_COUNT) - 1;
-
-    static final int ACTION_MODE_SWIPE_MASK = ACTION_MODE_IDLE_MASK << DIRECTION_FLAG_COUNT;
-
-    static final int ACTION_MODE_DRAG_MASK = ACTION_MODE_SWIPE_MASK << DIRECTION_FLAG_COUNT;
-
-    /**
-     * The unit we are using to track velocity
-     */
-    private static final int PIXELS_PER_SECOND = 1000;
-
-    /**
-     * Views, whose state should be cleared after they are detached from RecyclerView.
-     * This is necessary after swipe dismissing an item. We wait until animator finishes its job
-     * to clean these views.
-     */
-    final List<View> mPendingCleanup = new ArrayList<View>();
-
-    /**
-     * Re-use array to calculate dx dy for a ViewHolder
-     */
-    private final float[] mTmpPosition = new float[2];
-
-    /**
-     * Currently selected view holder
-     */
-    ViewHolder mSelected = null;
-
-    /**
-     * The reference coordinates for the action start. For drag & drop, this is the time long
-     * press is completed vs for swipe, this is the initial touch point.
-     */
-    float mInitialTouchX;
-
-    float mInitialTouchY;
-
-    /**
-     * Set when ItemTouchHelper is assigned to a RecyclerView.
-     */
-    float mSwipeEscapeVelocity;
-
-    /**
-     * Set when ItemTouchHelper is assigned to a RecyclerView.
-     */
-    float mMaxSwipeVelocity;
-
-    /**
-     * The diff between the last event and initial touch.
-     */
-    float mDx;
-
-    float mDy;
-
-    /**
-     * The coordinates of the selected view at the time it is selected. We record these values
-     * when action starts so that we can consistently position it even if LayoutManager moves the
-     * View.
-     */
-    float mSelectedStartX;
-
-    float mSelectedStartY;
-
-    /**
-     * The pointer we are tracking.
-     */
-    int mActivePointerId = ACTIVE_POINTER_ID_NONE;
-
-    /**
-     * Developer callback which controls the behavior of ItemTouchHelper.
-     */
-    Callback mCallback;
-
-    /**
-     * Current mode.
-     */
-    int mActionState = ACTION_STATE_IDLE;
-
-    /**
-     * The direction flags obtained from unmasking
-     * {@link Callback#getAbsoluteMovementFlags(RecyclerView, ViewHolder)} for the current
-     * action state.
-     */
-    int mSelectedFlags;
-
-    /**
-     * When a View is dragged or swiped and needs to go back to where it was, we create a Recover
-     * Animation and animate it to its location using this custom Animator, instead of using
-     * framework Animators.
-     * Using framework animators has the side effect of clashing with ItemAnimator, creating
-     * jumpy UIs.
-     */
-    List<RecoverAnimation> mRecoverAnimations = new ArrayList<RecoverAnimation>();
-
-    private int mSlop;
-
-    RecyclerView mRecyclerView;
-
-    /**
-     * When user drags a view to the edge, we start scrolling the LayoutManager as long as View
-     * is partially out of bounds.
-     */
-    final Runnable mScrollRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mSelected != null && scrollIfNecessary()) {
-                if (mSelected != null) { //it might be lost during scrolling
-                    moveIfNecessary(mSelected);
-                }
-                mRecyclerView.removeCallbacks(mScrollRunnable);
-                ViewCompat.postOnAnimation(mRecyclerView, this);
-            }
-        }
-    };
-
-    /**
-     * Used for detecting fling swipe
-     */
-    VelocityTracker mVelocityTracker;
-
-    //re-used list for selecting a swap target
-    private List<ViewHolder> mSwapTargets;
-
-    //re used for for sorting swap targets
-    private List<Integer> mDistances;
-
-    /**
-     * If drag & drop is supported, we use child drawing order to bring them to front.
-     */
-    private RecyclerView.ChildDrawingOrderCallback mChildDrawingOrderCallback = null;
-
-    /**
-     * This keeps a reference to the child dragged by the user. Even after user stops dragging,
-     * until view reaches its final position (end of recover animation), we keep a reference so
-     * that it can be drawn above other children.
-     */
-    View mOverdrawChild = null;
-
-    /**
-     * We cache the position of the overdraw child to avoid recalculating it each time child
-     * position callback is called. This value is invalidated whenever a child is attached or
-     * detached.
-     */
-    int mOverdrawChildPosition = -1;
-
-    /**
-     * Used to detect long press.
-     */
-    GestureDetectorCompat mGestureDetector;
-
-    /**
-     * Callback for when long press occurs.
-     */
-    private ItemTouchHelperGestureListener mItemTouchHelperGestureListener;
-
-    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 = event.getActionMasked();
-            if (action == MotionEvent.ACTION_DOWN) {
-                mActivePointerId = event.getPointerId(0);
-                mInitialTouchX = event.getX();
-                mInitialTouchY = event.getY();
-                obtainVelocityTracker();
-                if (mSelected == null) {
-                    final RecoverAnimation animation = findAnimation(event);
-                    if (animation != null) {
-                        mInitialTouchX -= animation.mX;
-                        mInitialTouchY -= animation.mY;
-                        endRecoverAnimation(animation.mViewHolder, true);
-                        if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {
-                            mCallback.clearView(mRecyclerView, animation.mViewHolder);
-                        }
-                        select(animation.mViewHolder, animation.mActionState);
-                        updateDxDy(event, mSelectedFlags, 0);
-                    }
-                }
-            } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
-                mActivePointerId = ACTIVE_POINTER_ID_NONE;
-                select(null, ACTION_STATE_IDLE);
-            } else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {
-                // in a non scroll orientation, if distance change is above threshold, we
-                // can select the item
-                final int index = event.findPointerIndex(mActivePointerId);
-                if (DEBUG) {
-                    Log.d(TAG, "pointer index " + index);
-                }
-                if (index >= 0) {
-                    checkSelectForSwipe(action, event, index);
-                }
-            }
-            if (mVelocityTracker != null) {
-                mVelocityTracker.addMovement(event);
-            }
-            return mSelected != null;
-        }
-
-        @Override
-        public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {
-            mGestureDetector.onTouchEvent(event);
-            if (DEBUG) {
-                Log.d(TAG,
-                        "on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event);
-            }
-            if (mVelocityTracker != null) {
-                mVelocityTracker.addMovement(event);
-            }
-            if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
-                return;
-            }
-            final int action = event.getActionMasked();
-            final int activePointerIndex = event.findPointerIndex(mActivePointerId);
-            if (activePointerIndex >= 0) {
-                checkSelectForSwipe(action, event, activePointerIndex);
-            }
-            ViewHolder viewHolder = mSelected;
-            if (viewHolder == null) {
-                return;
-            }
-            switch (action) {
-                case MotionEvent.ACTION_MOVE: {
-                    // Find the index of the active pointer and fetch its position
-                    if (activePointerIndex >= 0) {
-                        updateDxDy(event, mSelectedFlags, activePointerIndex);
-                        moveIfNecessary(viewHolder);
-                        mRecyclerView.removeCallbacks(mScrollRunnable);
-                        mScrollRunnable.run();
-                        mRecyclerView.invalidate();
-                    }
-                    break;
-                }
-                case MotionEvent.ACTION_CANCEL:
-                    if (mVelocityTracker != null) {
-                        mVelocityTracker.clear();
-                    }
-                    // fall through
-                case MotionEvent.ACTION_UP:
-                    select(null, ACTION_STATE_IDLE);
-                    mActivePointerId = ACTIVE_POINTER_ID_NONE;
-                    break;
-                case MotionEvent.ACTION_POINTER_UP: {
-                    final int pointerIndex = event.getActionIndex();
-                    final int pointerId = event.getPointerId(pointerIndex);
-                    if (pointerId == mActivePointerId) {
-                        // This was our active pointer going up. Choose a new
-                        // active pointer and adjust accordingly.
-                        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-                        mActivePointerId = event.getPointerId(newPointerIndex);
-                        updateDxDy(event, mSelectedFlags, pointerIndex);
-                    }
-                    break;
-                }
-            }
-        }
-
-        @Override
-        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-            if (!disallowIntercept) {
-                return;
-            }
-            select(null, ACTION_STATE_IDLE);
-        }
-    };
-
-    /**
-     * Temporary rect instance that is used when we need to lookup Item decorations.
-     */
-    private Rect mTmpRect;
-
-    /**
-     * When user started to drag scroll. Reset when we don't scroll
-     */
-    private long mDragScrollStartTimeInMs;
-
-    /**
-     * Creates an ItemTouchHelper that will work with the given Callback.
-     * <p>
-     * You can attach ItemTouchHelper to a RecyclerView via
-     * {@link #attachToRecyclerView(RecyclerView)}. Upon attaching, it will add an item decoration,
-     * an onItemTouchListener and a Child attach / detach listener to the RecyclerView.
-     *
-     * @param callback The Callback which controls the behavior of this touch helper.
-     */
-    public ItemTouchHelper(Callback callback) {
-        mCallback = callback;
-    }
-
-    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();
-    }
-
-    /**
-     * Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already
-     * attached to a RecyclerView, it will first detach from the previous one. You can call this
-     * method with {@code null} to detach it from the current RecyclerView.
-     *
-     * @param recyclerView The RecyclerView instance to which you want to add this helper or
-     *                     {@code null} if you want to remove ItemTouchHelper from the current
-     *                     RecyclerView.
-     */
-    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
-        if (mRecyclerView == recyclerView) {
-            return; // nothing to do
-        }
-        if (mRecyclerView != null) {
-            destroyCallbacks();
-        }
-        mRecyclerView = recyclerView;
-        if (recyclerView != null) {
-            final Resources resources = recyclerView.getResources();
-            mSwipeEscapeVelocity = resources
-                    .getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);
-            mMaxSwipeVelocity = resources
-                    .getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);
-            setupCallbacks();
-        }
-    }
-
-    private void setupCallbacks() {
-        ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
-        mSlop = vc.getScaledTouchSlop();
-        mRecyclerView.addItemDecoration(this);
-        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);
-        mRecyclerView.addOnChildAttachStateChangeListener(this);
-        startGestureDetection();
-    }
-
-    private void destroyCallbacks() {
-        mRecyclerView.removeItemDecoration(this);
-        mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener);
-        mRecyclerView.removeOnChildAttachStateChangeListener(this);
-        // clean all attached
-        final int recoverAnimSize = mRecoverAnimations.size();
-        for (int i = recoverAnimSize - 1; i >= 0; i--) {
-            final RecoverAnimation recoverAnimation = mRecoverAnimations.get(0);
-            mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder);
-        }
-        mRecoverAnimations.clear();
-        mOverdrawChild = null;
-        mOverdrawChildPosition = -1;
-        releaseVelocityTracker();
-        stopGestureDetection();
-    }
-
-    private void startGestureDetection() {
-        mItemTouchHelperGestureListener = new ItemTouchHelperGestureListener();
-        mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),
-                mItemTouchHelperGestureListener);
-    }
-
-    private void stopGestureDetection() {
-        if (mItemTouchHelperGestureListener != null) {
-            mItemTouchHelperGestureListener.doNotReactToLongPress();
-            mItemTouchHelperGestureListener = null;
-        }
-        if (mGestureDetector != null) {
-            mGestureDetector = null;
-        }
-    }
-
-    private void getSelectedDxDy(float[] outPosition) {
-        if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {
-            outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();
-        } else {
-            outPosition[0] = mSelected.itemView.getTranslationX();
-        }
-        if ((mSelectedFlags & (UP | DOWN)) != 0) {
-            outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();
-        } else {
-            outPosition[1] = mSelected.itemView.getTranslationY();
-        }
-    }
-
-    @Override
-    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        float dx = 0, dy = 0;
-        if (mSelected != null) {
-            getSelectedDxDy(mTmpPosition);
-            dx = mTmpPosition[0];
-            dy = mTmpPosition[1];
-        }
-        mCallback.onDrawOver(c, parent, mSelected,
-                mRecoverAnimations, mActionState, dx, dy);
-    }
-
-    @Override
-    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        // we don't know if RV changed something so we should invalidate this index.
-        mOverdrawChildPosition = -1;
-        float dx = 0, dy = 0;
-        if (mSelected != null) {
-            getSelectedDxDy(mTmpPosition);
-            dx = mTmpPosition[0];
-            dy = mTmpPosition[1];
-        }
-        mCallback.onDraw(c, parent, mSelected,
-                mRecoverAnimations, mActionState, dx, dy);
-    }
-
-    /**
-     * Starts dragging or swiping the given View. Call with null if you want to clear it.
-     *
-     * @param selected    The ViewHolder to drag or swipe. Can be null if you want to cancel the
-     *                    current action
-     * @param actionState The type of action
-     */
-    void select(ViewHolder selected, int actionState) {
-        if (selected == mSelected && actionState == mActionState) {
-            return;
-        }
-        mDragScrollStartTimeInMs = Long.MIN_VALUE;
-        final int prevActionState = mActionState;
-        // prevent duplicate animations
-        endRecoverAnimation(selected, true);
-        mActionState = actionState;
-        if (actionState == ACTION_STATE_DRAG) {
-            // we remove after animation is complete. this means we only elevate the last drag
-            // child but that should perform good enough as it is very hard to start dragging a
-            // new child before the previous one settles.
-            mOverdrawChild = selected.itemView;
-            addChildDrawingOrderCallback();
-        }
-        int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))
-                - 1;
-        boolean preventLayout = false;
-
-        if (mSelected != null) {
-            final ViewHolder prevSelected = mSelected;
-            if (prevSelected.itemView.getParent() != null) {
-                final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0
-                        : swipeIfNecessary(prevSelected);
-                releaseVelocityTracker();
-                // find where we should animate to
-                final float targetTranslateX, targetTranslateY;
-                int animationType;
-                switch (swipeDir) {
-                    case LEFT:
-                    case RIGHT:
-                    case START:
-                    case END:
-                        targetTranslateY = 0;
-                        targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();
-                        break;
-                    case UP:
-                    case DOWN:
-                        targetTranslateX = 0;
-                        targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();
-                        break;
-                    default:
-                        targetTranslateX = 0;
-                        targetTranslateY = 0;
-                }
-                if (prevActionState == ACTION_STATE_DRAG) {
-                    animationType = ANIMATION_TYPE_DRAG;
-                } else if (swipeDir > 0) {
-                    animationType = ANIMATION_TYPE_SWIPE_SUCCESS;
-                } else {
-                    animationType = ANIMATION_TYPE_SWIPE_CANCEL;
-                }
-                getSelectedDxDy(mTmpPosition);
-                final float currentTranslateX = mTmpPosition[0];
-                final float currentTranslateY = mTmpPosition[1];
-                final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType,
-                        prevActionState, currentTranslateX, currentTranslateY,
-                        targetTranslateX, targetTranslateY) {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        super.onAnimationEnd(animation);
-                        if (this.mOverridden) {
-                            return;
-                        }
-                        if (swipeDir <= 0) {
-                            // this is a drag or failed swipe. recover immediately
-                            mCallback.clearView(mRecyclerView, prevSelected);
-                            // full cleanup will happen on onDrawOver
-                        } else {
-                            // wait until remove animation is complete.
-                            mPendingCleanup.add(prevSelected.itemView);
-                            mIsPendingCleanup = true;
-                            if (swipeDir > 0) {
-                                // Animation might be ended by other animators during a layout.
-                                // We defer callback to avoid editing adapter during a layout.
-                                postDispatchSwipe(this, swipeDir);
-                            }
-                        }
-                        // removed from the list after it is drawn for the last time
-                        if (mOverdrawChild == prevSelected.itemView) {
-                            removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
-                        }
-                    }
-                };
-                final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,
-                        targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);
-                rv.setDuration(duration);
-                mRecoverAnimations.add(rv);
-                rv.start();
-                preventLayout = true;
-            } else {
-                removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
-                mCallback.clearView(mRecyclerView, prevSelected);
-            }
-            mSelected = null;
-        }
-        if (selected != null) {
-            mSelectedFlags =
-                    (mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)
-                            >> (mActionState * DIRECTION_FLAG_COUNT);
-            mSelectedStartX = selected.itemView.getLeft();
-            mSelectedStartY = selected.itemView.getTop();
-            mSelected = selected;
-
-            if (actionState == ACTION_STATE_DRAG) {
-                mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-            }
-        }
-        final ViewParent rvParent = mRecyclerView.getParent();
-        if (rvParent != null) {
-            rvParent.requestDisallowInterceptTouchEvent(mSelected != null);
-        }
-        if (!preventLayout) {
-            mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();
-        }
-        mCallback.onSelectedChanged(mSelected, mActionState);
-        mRecyclerView.invalidate();
-    }
-
-    void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) {
-        // wait until animations are complete.
-        mRecyclerView.post(new Runnable() {
-            @Override
-            public void run() {
-                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
-                    // animations. Instead, we wait and batch.
-                    if ((animator == null || !animator.isRunning(null))
-                            && !hasRunningRecoverAnim()) {
-                        mCallback.onSwiped(anim.mViewHolder, swipeDir);
-                    } else {
-                        mRecyclerView.post(this);
-                    }
-                }
-            }
-        });
-    }
-
-    boolean hasRunningRecoverAnim() {
-        final int size = mRecoverAnimations.size();
-        for (int i = 0; i < size; i++) {
-            if (!mRecoverAnimations.get(i).mEnded) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * If user drags the view to the edge, trigger a scroll if necessary.
-     */
-    boolean scrollIfNecessary() {
-        if (mSelected == null) {
-            mDragScrollStartTimeInMs = Long.MIN_VALUE;
-            return false;
-        }
-        final long now = System.currentTimeMillis();
-        final long scrollDuration = mDragScrollStartTimeInMs
-                == Long.MIN_VALUE ? 0 : now - mDragScrollStartTimeInMs;
-        RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
-        if (mTmpRect == null) {
-            mTmpRect = new Rect();
-        }
-        int scrollX = 0;
-        int scrollY = 0;
-        lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect);
-        if (lm.canScrollHorizontally()) {
-            int curX = (int) (mSelectedStartX + mDx);
-            final int leftDiff = curX - mTmpRect.left - mRecyclerView.getPaddingLeft();
-            if (mDx < 0 && leftDiff < 0) {
-                scrollX = leftDiff;
-            } else if (mDx > 0) {
-                final int rightDiff =
-                        curX + mSelected.itemView.getWidth() + mTmpRect.right
-                                - (mRecyclerView.getWidth() - mRecyclerView.getPaddingRight());
-                if (rightDiff > 0) {
-                    scrollX = rightDiff;
-                }
-            }
-        }
-        if (lm.canScrollVertically()) {
-            int curY = (int) (mSelectedStartY + mDy);
-            final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop();
-            if (mDy < 0 && topDiff < 0) {
-                scrollY = topDiff;
-            } else if (mDy > 0) {
-                final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom
-                        - (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom());
-                if (bottomDiff > 0) {
-                    scrollY = bottomDiff;
-                }
-            }
-        }
-        if (scrollX != 0) {
-            scrollX = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
-                    mSelected.itemView.getWidth(), scrollX,
-                    mRecyclerView.getWidth(), scrollDuration);
-        }
-        if (scrollY != 0) {
-            scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView,
-                    mSelected.itemView.getHeight(), scrollY,
-                    mRecyclerView.getHeight(), scrollDuration);
-        }
-        if (scrollX != 0 || scrollY != 0) {
-            if (mDragScrollStartTimeInMs == Long.MIN_VALUE) {
-                mDragScrollStartTimeInMs = now;
-            }
-            mRecyclerView.scrollBy(scrollX, scrollY);
-            return true;
-        }
-        mDragScrollStartTimeInMs = Long.MIN_VALUE;
-        return false;
-    }
-
-    private List<ViewHolder> findSwapTargets(ViewHolder viewHolder) {
-        if (mSwapTargets == null) {
-            mSwapTargets = new ArrayList<ViewHolder>();
-            mDistances = new ArrayList<Integer>();
-        } else {
-            mSwapTargets.clear();
-            mDistances.clear();
-        }
-        final int margin = mCallback.getBoundingBoxMargin();
-        final int left = Math.round(mSelectedStartX + mDx) - margin;
-        final int top = Math.round(mSelectedStartY + mDy) - margin;
-        final int right = left + viewHolder.itemView.getWidth() + 2 * margin;
-        final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin;
-        final int centerX = (left + right) / 2;
-        final int centerY = (top + bottom) / 2;
-        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
-        final int childCount = lm.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View other = lm.getChildAt(i);
-            if (other == viewHolder.itemView) {
-                continue; //myself!
-            }
-            if (other.getBottom() < top || other.getTop() > bottom
-                    || other.getRight() < left || other.getLeft() > right) {
-                continue;
-            }
-            final ViewHolder otherVh = mRecyclerView.getChildViewHolder(other);
-            if (mCallback.canDropOver(mRecyclerView, mSelected, otherVh)) {
-                // find the index to add
-                final int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2);
-                final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2);
-                final int dist = dx * dx + dy * dy;
-
-                int pos = 0;
-                final int cnt = mSwapTargets.size();
-                for (int j = 0; j < cnt; j++) {
-                    if (dist > mDistances.get(j)) {
-                        pos++;
-                    } else {
-                        break;
-                    }
-                }
-                mSwapTargets.add(pos, otherVh);
-                mDistances.add(pos, dist);
-            }
-        }
-        return mSwapTargets;
-    }
-
-    /**
-     * Checks if we should swap w/ another view holder.
-     */
-    void moveIfNecessary(ViewHolder viewHolder) {
-        if (mRecyclerView.isLayoutRequested()) {
-            return;
-        }
-        if (mActionState != ACTION_STATE_DRAG) {
-            return;
-        }
-
-        final float threshold = mCallback.getMoveThreshold(viewHolder);
-        final int x = (int) (mSelectedStartX + mDx);
-        final int y = (int) (mSelectedStartY + mDy);
-        if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold
-                && Math.abs(x - viewHolder.itemView.getLeft())
-                < viewHolder.itemView.getWidth() * threshold) {
-            return;
-        }
-        List<ViewHolder> swapTargets = findSwapTargets(viewHolder);
-        if (swapTargets.size() == 0) {
-            return;
-        }
-        // may swap.
-        ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y);
-        if (target == null) {
-            mSwapTargets.clear();
-            mDistances.clear();
-            return;
-        }
-        final int toPosition = target.getAdapterPosition();
-        final int fromPosition = viewHolder.getAdapterPosition();
-        if (mCallback.onMove(mRecyclerView, viewHolder, target)) {
-            // keep target visible
-            mCallback.onMoved(mRecyclerView, viewHolder, fromPosition,
-                    target, toPosition, x, y);
-        }
-    }
-
-    @Override
-    public void onChildViewAttachedToWindow(View view) {
-    }
-
-    @Override
-    public void onChildViewDetachedFromWindow(View view) {
-        removeChildDrawingOrderCallbackIfNecessary(view);
-        final ViewHolder holder = mRecyclerView.getChildViewHolder(view);
-        if (holder == null) {
-            return;
-        }
-        if (mSelected != null && holder == mSelected) {
-            select(null, ACTION_STATE_IDLE);
-        } else {
-            endRecoverAnimation(holder, false); // this may push it into pending cleanup list.
-            if (mPendingCleanup.remove(holder.itemView)) {
-                mCallback.clearView(mRecyclerView, holder);
-            }
-        }
-    }
-
-    /**
-     * Returns the animation type or 0 if cannot be found.
-     */
-    int endRecoverAnimation(ViewHolder viewHolder, boolean override) {
-        final int recoverAnimSize = mRecoverAnimations.size();
-        for (int i = recoverAnimSize - 1; i >= 0; i--) {
-            final RecoverAnimation anim = mRecoverAnimations.get(i);
-            if (anim.mViewHolder == viewHolder) {
-                anim.mOverridden |= override;
-                if (!anim.mEnded) {
-                    anim.cancel();
-                }
-                mRecoverAnimations.remove(i);
-                return anim.mAnimationType;
-            }
-        }
-        return 0;
-    }
-
-    @Override
-    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
-            RecyclerView.State state) {
-        outRect.setEmpty();
-    }
-
-    void obtainVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-        }
-        mVelocityTracker = VelocityTracker.obtain();
-    }
-
-    private void releaseVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    private ViewHolder findSwipedView(MotionEvent motionEvent) {
-        final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
-        if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
-            return null;
-        }
-        final int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);
-        final float dx = motionEvent.getX(pointerIndex) - mInitialTouchX;
-        final float dy = motionEvent.getY(pointerIndex) - mInitialTouchY;
-        final float absDx = Math.abs(dx);
-        final float absDy = Math.abs(dy);
-
-        if (absDx < mSlop && absDy < mSlop) {
-            return null;
-        }
-        if (absDx > absDy && lm.canScrollHorizontally()) {
-            return null;
-        } else if (absDy > absDx && lm.canScrollVertically()) {
-            return null;
-        }
-        View child = findChildView(motionEvent);
-        if (child == null) {
-            return null;
-        }
-        return mRecyclerView.getChildViewHolder(child);
-    }
-
-    /**
-     * Checks whether we should select a View for swiping.
-     */
-    boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) {
-        if (mSelected != null || action != MotionEvent.ACTION_MOVE
-                || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) {
-            return false;
-        }
-        if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
-            return false;
-        }
-        final ViewHolder vh = findSwipedView(motionEvent);
-        if (vh == null) {
-            return false;
-        }
-        final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh);
-
-        final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK)
-                >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE);
-
-        if (swipeFlags == 0) {
-            return false;
-        }
-
-        // mDx and mDy are only set in allowed directions. We use custom x/y here instead of
-        // updateDxDy to avoid swiping if user moves more in the other direction
-        final float x = motionEvent.getX(pointerIndex);
-        final float y = motionEvent.getY(pointerIndex);
-
-        // Calculate the distance moved
-        final float dx = x - mInitialTouchX;
-        final float dy = y - mInitialTouchY;
-        // swipe target is chose w/o applying flags so it does not really check if swiping in that
-        // direction is allowed. This why here, we use mDx mDy to check slope value again.
-        final float absDx = Math.abs(dx);
-        final float absDy = Math.abs(dy);
-
-        if (absDx < mSlop && absDy < mSlop) {
-            return false;
-        }
-        if (absDx > absDy) {
-            if (dx < 0 && (swipeFlags & LEFT) == 0) {
-                return false;
-            }
-            if (dx > 0 && (swipeFlags & RIGHT) == 0) {
-                return false;
-            }
-        } else {
-            if (dy < 0 && (swipeFlags & UP) == 0) {
-                return false;
-            }
-            if (dy > 0 && (swipeFlags & DOWN) == 0) {
-                return false;
-            }
-        }
-        mDx = mDy = 0f;
-        mActivePointerId = motionEvent.getPointerId(0);
-        select(vh, ACTION_STATE_SWIPE);
-        return true;
-    }
-
-    View findChildView(MotionEvent event) {
-        // first check elevated views, if none, then call RV
-        final float x = event.getX();
-        final float y = event.getY();
-        if (mSelected != null) {
-            final View selectedView = mSelected.itemView;
-            if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) {
-                return selectedView;
-            }
-        }
-        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
-            final RecoverAnimation anim = mRecoverAnimations.get(i);
-            final View view = anim.mViewHolder.itemView;
-            if (hitTest(view, x, y, anim.mX, anim.mY)) {
-                return view;
-            }
-        }
-        return mRecyclerView.findChildViewUnder(x, y);
-    }
-
-    /**
-     * Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a
-     * View is long pressed. You can disable that behavior by overriding
-     * {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}.
-     * <p>
-     * For this method to work:
-     * <ul>
-     * <li>The provided ViewHolder must be a child of the RecyclerView to which this
-     * ItemTouchHelper
-     * is attached.</li>
-     * <li>{@link ItemTouchHelper.Callback} must have dragging enabled.</li>
-     * <li>There must be a previous touch event that was reported to the ItemTouchHelper
-     * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener
-     * grabs previous events, this should work as expected.</li>
-     * </ul>
-     *
-     * For example, if you would like to let your user to be able to drag an Item by touching one
-     * of its descendants, you may implement it as follows:
-     * <pre>
-     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
-     *         public boolean onTouch(View v, MotionEvent event) {
-     *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
-     *                 mItemTouchHelper.startDrag(viewHolder);
-     *             }
-     *             return false;
-     *         }
-     *     });
-     * </pre>
-     * <p>
-     *
-     * @param viewHolder The ViewHolder to start dragging. It must be a direct child of
-     *                   RecyclerView.
-     * @see ItemTouchHelper.Callback#isItemViewSwipeEnabled()
-     */
-    public void startDrag(ViewHolder viewHolder) {
-        if (!mCallback.hasDragFlag(mRecyclerView, viewHolder)) {
-            Log.e(TAG, "Start drag has been called but dragging is not enabled");
-            return;
-        }
-        if (viewHolder.itemView.getParent() != mRecyclerView) {
-            Log.e(TAG, "Start drag has been called with a view holder which is not a child of "
-                    + "the RecyclerView which is controlled by this ItemTouchHelper.");
-            return;
-        }
-        obtainVelocityTracker();
-        mDx = mDy = 0f;
-        select(viewHolder, ACTION_STATE_DRAG);
-    }
-
-    /**
-     * Starts swiping the provided ViewHolder. By default, ItemTouchHelper starts swiping a View
-     * when user swipes their finger (or mouse pointer) over the View. You can disable this
-     * behavior
-     * by overriding {@link ItemTouchHelper.Callback}
-     * <p>
-     * For this method to work:
-     * <ul>
-     * <li>The provided ViewHolder must be a child of the RecyclerView to which this
-     * ItemTouchHelper is attached.</li>
-     * <li>{@link ItemTouchHelper.Callback} must have swiping enabled.</li>
-     * <li>There must be a previous touch event that was reported to the ItemTouchHelper
-     * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener
-     * grabs previous events, this should work as expected.</li>
-     * </ul>
-     *
-     * For example, if you would like to let your user to be able to swipe an Item by touching one
-     * of its descendants, you may implement it as follows:
-     * <pre>
-     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
-     *         public boolean onTouch(View v, MotionEvent event) {
-     *             if (MotionEvent.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
-     *                 mItemTouchHelper.startSwipe(viewHolder);
-     *             }
-     *             return false;
-     *         }
-     *     });
-     * </pre>
-     *
-     * @param viewHolder The ViewHolder to start swiping. It must be a direct child of
-     *                   RecyclerView.
-     */
-    public void startSwipe(ViewHolder viewHolder) {
-        if (!mCallback.hasSwipeFlag(mRecyclerView, viewHolder)) {
-            Log.e(TAG, "Start swipe has been called but swiping is not enabled");
-            return;
-        }
-        if (viewHolder.itemView.getParent() != mRecyclerView) {
-            Log.e(TAG, "Start swipe has been called with a view holder which is not a child of "
-                    + "the RecyclerView controlled by this ItemTouchHelper.");
-            return;
-        }
-        obtainVelocityTracker();
-        mDx = mDy = 0f;
-        select(viewHolder, ACTION_STATE_SWIPE);
-    }
-
-    RecoverAnimation findAnimation(MotionEvent event) {
-        if (mRecoverAnimations.isEmpty()) {
-            return null;
-        }
-        View target = findChildView(event);
-        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
-            final RecoverAnimation anim = mRecoverAnimations.get(i);
-            if (anim.mViewHolder.itemView == target) {
-                return anim;
-            }
-        }
-        return null;
-    }
-
-    void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {
-        final float x = ev.getX(pointerIndex);
-        final float y = ev.getY(pointerIndex);
-
-        // Calculate the distance moved
-        mDx = x - mInitialTouchX;
-        mDy = y - mInitialTouchY;
-        if ((directionFlags & LEFT) == 0) {
-            mDx = Math.max(0, mDx);
-        }
-        if ((directionFlags & RIGHT) == 0) {
-            mDx = Math.min(0, mDx);
-        }
-        if ((directionFlags & UP) == 0) {
-            mDy = Math.max(0, mDy);
-        }
-        if ((directionFlags & DOWN) == 0) {
-            mDy = Math.min(0, mDy);
-        }
-    }
-
-    private int swipeIfNecessary(ViewHolder viewHolder) {
-        if (mActionState == ACTION_STATE_DRAG) {
-            return 0;
-        }
-        final int originalMovementFlags = mCallback.getMovementFlags(mRecyclerView, viewHolder);
-        final int absoluteMovementFlags = mCallback.convertToAbsoluteDirection(
-                originalMovementFlags,
-                ViewCompat.getLayoutDirection(mRecyclerView));
-        final int flags = (absoluteMovementFlags
-                & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);
-        if (flags == 0) {
-            return 0;
-        }
-        final int originalFlags = (originalMovementFlags
-                & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT);
-        int swipeDir;
-        if (Math.abs(mDx) > Math.abs(mDy)) {
-            if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
-                // if swipe dir is not in original flags, it should be the relative direction
-                if ((originalFlags & swipeDir) == 0) {
-                    // convert to relative
-                    return Callback.convertToRelativeDirection(swipeDir,
-                            ViewCompat.getLayoutDirection(mRecyclerView));
-                }
-                return swipeDir;
-            }
-            if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {
-                return swipeDir;
-            }
-        } else {
-            if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) {
-                return swipeDir;
-            }
-            if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) {
-                // if swipe dir is not in original flags, it should be the relative direction
-                if ((originalFlags & swipeDir) == 0) {
-                    // convert to relative
-                    return Callback.convertToRelativeDirection(swipeDir,
-                            ViewCompat.getLayoutDirection(mRecyclerView));
-                }
-                return swipeDir;
-            }
-        }
-        return 0;
-    }
-
-    private int checkHorizontalSwipe(ViewHolder viewHolder, int flags) {
-        if ((flags & (LEFT | RIGHT)) != 0) {
-            final int dirFlag = mDx > 0 ? RIGHT : LEFT;
-            if (mVelocityTracker != null && mActivePointerId > -1) {
-                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
-                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
-                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)) {
-                    return velDirFlag;
-                }
-            }
-
-            final float threshold = mRecyclerView.getWidth() * mCallback
-                    .getSwipeThreshold(viewHolder);
-
-            if ((flags & dirFlag) != 0 && Math.abs(mDx) > threshold) {
-                return dirFlag;
-            }
-        }
-        return 0;
-    }
-
-    private int checkVerticalSwipe(ViewHolder viewHolder, int flags) {
-        if ((flags & (UP | DOWN)) != 0) {
-            final int dirFlag = mDy > 0 ? DOWN : UP;
-            if (mVelocityTracker != null && mActivePointerId > -1) {
-                mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND,
-                        mCallback.getSwipeVelocityThreshold(mMaxSwipeVelocity));
-                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)) {
-                    return velDirFlag;
-                }
-            }
-
-            final float threshold = mRecyclerView.getHeight() * mCallback
-                    .getSwipeThreshold(viewHolder);
-            if ((flags & dirFlag) != 0 && Math.abs(mDy) > threshold) {
-                return dirFlag;
-            }
-        }
-        return 0;
-    }
-
-    private void addChildDrawingOrderCallback() {
-        if (Build.VERSION.SDK_INT >= 21) {
-            return; // we use elevation on Lollipop
-        }
-        if (mChildDrawingOrderCallback == null) {
-            mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
-                @Override
-                public int onGetChildDrawingOrder(int childCount, int i) {
-                    if (mOverdrawChild == null) {
-                        return i;
-                    }
-                    int childPosition = mOverdrawChildPosition;
-                    if (childPosition == -1) {
-                        childPosition = mRecyclerView.indexOfChild(mOverdrawChild);
-                        mOverdrawChildPosition = childPosition;
-                    }
-                    if (i == childCount - 1) {
-                        return childPosition;
-                    }
-                    return i < childPosition ? i : i + 1;
-                }
-            };
-        }
-        mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);
-    }
-
-    void removeChildDrawingOrderCallbackIfNecessary(View view) {
-        if (view == mOverdrawChild) {
-            mOverdrawChild = null;
-            // only remove if we've added
-            if (mChildDrawingOrderCallback != null) {
-                mRecyclerView.setChildDrawingOrderCallback(null);
-            }
-        }
-    }
-
-    /**
-     * An interface which can be implemented by LayoutManager for better integration with
-     * {@link ItemTouchHelper}.
-     */
-    public interface ViewDropHandler {
-
-        /**
-         * Called by the {@link ItemTouchHelper} after a View is dropped over another View.
-         * <p>
-         * A LayoutManager should implement this interface to get ready for the upcoming move
-         * operation.
-         * <p>
-         * For example, LinearLayoutManager sets up a "scrollToPositionWithOffset" calls so that
-         * the View under drag will be used as an anchor View while calculating the next layout,
-         * making layout stay consistent.
-         *
-         * @param view   The View which is being dragged. It is very likely that user is still
-         *               dragging this View so there might be other
-         *               {@link #prepareForDrop(View, View, int, int)} after this one.
-         * @param target The target view which is being dropped on.
-         * @param x      The <code>left</code> offset of the View that is being dragged. This value
-         *               includes the movement caused by the user.
-         * @param y      The <code>top</code> offset of the View that is being dragged. This value
-         *               includes the movement caused by the user.
-         */
-        void prepareForDrop(View view, View target, int x, int y);
-    }
-
-    /**
-     * This class is the contract between ItemTouchHelper and your application. It lets you control
-     * which touch behaviors are enabled per each ViewHolder and also receive callbacks when user
-     * performs these actions.
-     * <p>
-     * To control which actions user can take on each view, you should override
-     * {@link #getMovementFlags(RecyclerView, ViewHolder)} and return appropriate set
-     * of direction flags. ({@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link #END},
-     * {@link #UP}, {@link #DOWN}). You can use
-     * {@link #makeMovementFlags(int, int)} to easily construct it. Alternatively, you can use
-     * {@link SimpleCallback}.
-     * <p>
-     * If user drags an item, ItemTouchHelper will call
-     * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)
-     * onMove(recyclerView, dragged, target)}.
-     * Upon receiving this callback, you should move the item from the old position
-     * ({@code dragged.getAdapterPosition()}) to new position ({@code target.getAdapterPosition()})
-     * in your adapter and also call {@link RecyclerView.Adapter#notifyItemMoved(int, int)}.
-     * To control where a View can be dropped, you can override
-     * {@link #canDropOver(RecyclerView, ViewHolder, ViewHolder)}. When a
-     * dragging View overlaps multiple other views, Callback chooses the closest View with which
-     * dragged View might have changed positions. Although this approach works for many use cases,
-     * if you have a custom LayoutManager, you can override
-     * {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)} to select a
-     * custom drop target.
-     * <p>
-     * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls
-     * {@link #onSwiped(ViewHolder, int)}. At this point, you should update your
-     * adapter (e.g. remove the item) and call related Adapter#notify event.
-     */
-    @SuppressWarnings("UnusedParameters")
-    public abstract static class Callback {
-
-        public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200;
-
-        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));
-
-        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 Interpolator sDragScrollInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float t) {
-                return t * t * t * t * t;
-            }
-        };
-
-        private static final Interpolator sDragViewScrollCapInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float t) {
-                t -= 1.0f;
-                return t * t * t * t * t + 1.0f;
-            }
-        };
-
-        /**
-         * Drag scroll speed keeps accelerating until this many milliseconds before being capped.
-         */
-        private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000;
-
-        private int mCachedMaxScrollSpeed = -1;
-
-        static {
-            if (Build.VERSION.SDK_INT >= 21) {
-                sUICallback = new ItemTouchUIUtilImpl.Api21Impl();
-            } else {
-                sUICallback = new ItemTouchUIUtilImpl.BaseImpl();
-            }
-        }
-
-        /**
-         * Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for
-         * visual
-         * changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different
-         * implementations for different platform versions.
-         * <p>
-         * By default, {@link Callback} applies these changes on
-         * {@link RecyclerView.ViewHolder#itemView}.
-         * <p>
-         * For example, if you have a use case where you only want the text to move when user
-         * swipes over the view, you can do the following:
-         * <pre>
-         *     public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){
-         *         getDefaultUIUtil().clearView(((ItemTouchViewHolder) viewHolder).textView);
-         *     }
-         *     public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
-         *         if (viewHolder != null){
-         *             getDefaultUIUtil().onSelected(((ItemTouchViewHolder) viewHolder).textView);
-         *         }
-         *     }
-         *     public void onChildDraw(Canvas c, RecyclerView recyclerView,
-         *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
-         *             boolean isCurrentlyActive) {
-         *         getDefaultUIUtil().onDraw(c, recyclerView,
-         *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,
-         *                 actionState, isCurrentlyActive);
-         *         return true;
-         *     }
-         *     public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
-         *             RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
-         *             boolean isCurrentlyActive) {
-         *         getDefaultUIUtil().onDrawOver(c, recyclerView,
-         *                 ((ItemTouchViewHolder) viewHolder).textView, dX, dY,
-         *                 actionState, isCurrentlyActive);
-         *         return true;
-         *     }
-         * </pre>
-         *
-         * @return The {@link ItemTouchUIUtil} instance that is used by the {@link Callback}
-         */
-        public static ItemTouchUIUtil getDefaultUIUtil() {
-            return sUICallback;
-        }
-
-        /**
-         * Replaces a movement direction with its relative version by taking layout direction into
-         * account.
-         *
-         * @param flags           The flag value that include any number of movement flags.
-         * @param layoutDirection The layout direction of the View. Can be obtained from
-         *                        {@link ViewCompat#getLayoutDirection(android.view.View)}.
-         * @return Updated flags which uses relative flags ({@link #START}, {@link #END}) instead
-         * of {@link #LEFT}, {@link #RIGHT}.
-         * @see #convertToAbsoluteDirection(int, int)
-         */
-        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.
-            }
-            flags &= ~masked; //remove left / right.
-            if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
-                // no change. just OR with 2 bits shifted mask and return
-                flags |= masked << 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.
-                return flags;
-            } else {
-                // add RIGHT flag as START
-                flags |= ((masked << 1) & ~ABS_HORIZONTAL_DIR_FLAGS);
-                // first clean RIGHT bit then add LEFT flag as END
-                flags |= ((masked << 1) & ABS_HORIZONTAL_DIR_FLAGS) << 2;
-            }
-            return flags;
-        }
-
-        /**
-         * Convenience method to create movement flags.
-         * <p>
-         * For instance, if you want to let your items be drag & dropped vertically and swiped
-         * left to be dismissed, you can call this method with:
-         * <code>makeMovementFlags(UP | DOWN, LEFT);</code>
-         *
-         * @param dragFlags  The directions in which the item can be dragged.
-         * @param swipeFlags The directions in which the item can be swiped.
-         * @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);
-        }
-
-        /**
-         * Shifts the given direction flags to the offset of the given action state.
-         *
-         * @param actionState The action state you want to get flags in. Should be one of
-         *                    {@link #ACTION_STATE_IDLE}, {@link #ACTION_STATE_SWIPE} or
-         *                    {@link #ACTION_STATE_DRAG}.
-         * @param directions  The direction flags. Can be composed from {@link #UP}, {@link #DOWN},
-         *                    {@link #RIGHT}, {@link #LEFT} {@link #START} and {@link #END}.
-         * @return And integer that represents the given directions in the provided actionState.
-         */
-        public static int makeFlag(int actionState, int directions) {
-            return directions << (actionState * DIRECTION_FLAG_COUNT);
-        }
-
-        /**
-         * Should return a composite flag which defines the enabled move directions in each state
-         * (idle, swiping, dragging).
-         * <p>
-         * Instead of composing this flag manually, you can use {@link #makeMovementFlags(int,
-         * int)}
-         * or {@link #makeFlag(int, int)}.
-         * <p>
-         * This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next
-         * 8 bits are for SWIPE state and third 8 bits are for DRAG state.
-         * Each 8 bit sections can be constructed by simply OR'ing direction flags defined in
-         * {@link ItemTouchHelper}.
-         * <p>
-         * For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to
-         * swipe by swiping RIGHT, you can return:
-         * <pre>
-         *      makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT);
-         * </pre>
-         * This means, allow right movement while IDLE and allow right and left movement while
-         * swiping.
-         *
-         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached.
-         * @param viewHolder   The ViewHolder for which the movement information is necessary.
-         * @return flags specifying which movements are allowed on this ViewHolder.
-         * @see #makeMovementFlags(int, int)
-         * @see #makeFlag(int, int)
-         */
-        public abstract int getMovementFlags(RecyclerView recyclerView,
-                ViewHolder viewHolder);
-
-        /**
-         * Converts a given set of flags to absolution direction which means {@link #START} and
-         * {@link #END} are replaced with {@link #LEFT} and {@link #RIGHT} depending on the layout
-         * direction.
-         *
-         * @param flags           The flag value that include any number of movement flags.
-         * @param layoutDirection The layout direction of the RecyclerView.
-         * @return Updated flags which includes only absolute direction values.
-         */
-        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.
-            }
-            flags &= ~masked; //remove start / end
-            if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) {
-                // no change. just OR with 2 bits shifted mask and return
-                flags |= masked >> 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT.
-                return flags;
-            } else {
-                // add START flag as RIGHT
-                flags |= ((masked >> 1) & ~RELATIVE_DIR_FLAGS);
-                // first clean start bit then add END flag as LEFT
-                flags |= ((masked >> 1) & RELATIVE_DIR_FLAGS) >> 2;
-            }
-            return flags;
-        }
-
-        final int getAbsoluteMovementFlags(RecyclerView recyclerView,
-                ViewHolder viewHolder) {
-            final int flags = getMovementFlags(recyclerView, viewHolder);
-            return convertToAbsoluteDirection(flags, ViewCompat.getLayoutDirection(recyclerView));
-        }
-
-        boolean hasDragFlag(RecyclerView recyclerView, ViewHolder viewHolder) {
-            final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);
-            return (flags & ACTION_MODE_DRAG_MASK) != 0;
-        }
-
-        boolean hasSwipeFlag(RecyclerView recyclerView,
-                ViewHolder viewHolder) {
-            final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder);
-            return (flags & ACTION_MODE_SWIPE_MASK) != 0;
-        }
-
-        /**
-         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
-         * <p>
-         * This method is used when selecting drop target for the dragged View. After Views are
-         * eliminated either via bounds check or via this method, resulting set of views will be
-         * passed to {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)}.
-         * <p>
-         * Default implementation returns true.
-         *
-         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
-         * @param current      The ViewHolder that user is dragging.
-         * @param target       The ViewHolder which is below the dragged ViewHolder.
-         * @return True if the dragged ViewHolder can be replaced with the target ViewHolder, false
-         * otherwise.
-         */
-        public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
-                ViewHolder target) {
-            return true;
-        }
-
-        /**
-         * Called when ItemTouchHelper wants to move the dragged item from its old position to
-         * the new position.
-         * <p>
-         * If this method returns true, ItemTouchHelper assumes {@code viewHolder} has been moved
-         * to the adapter position of {@code target} ViewHolder
-         * ({@link ViewHolder#getAdapterPosition()
-         * ViewHolder#getAdapterPosition()}).
-         * <p>
-         * If you don't support drag & drop, this method will never be called.
-         *
-         * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to.
-         * @param viewHolder   The ViewHolder which is being dragged by the user.
-         * @param target       The ViewHolder over which the currently active item is being
-         *                     dragged.
-         * @return True if the {@code viewHolder} has been moved to the adapter position of
-         * {@code target}.
-         * @see #onMoved(RecyclerView, ViewHolder, int, ViewHolder, int, int, int)
-         */
-        public abstract boolean onMove(RecyclerView recyclerView,
-                ViewHolder viewHolder, ViewHolder target);
-
-        /**
-         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is
-         * long pressed.
-         * <p>
-         * Default value returns true but you may want to disable this if you want to start
-         * dragging on a custom view touch using {@link #startDrag(ViewHolder)}.
-         *
-         * @return True if ItemTouchHelper should start dragging an item when it is long pressed,
-         * false otherwise. Default value is <code>true</code>.
-         * @see #startDrag(ViewHolder)
-         */
-        public boolean isLongPressDragEnabled() {
-            return true;
-        }
-
-        /**
-         * Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped
-         * over the View.
-         * <p>
-         * Default value returns true but you may want to disable this if you want to start
-         * swiping on a custom view touch using {@link #startSwipe(ViewHolder)}.
-         *
-         * @return True if ItemTouchHelper should start swiping an item when user swipes a pointer
-         * over the View, false otherwise. Default value is <code>true</code>.
-         * @see #startSwipe(ViewHolder)
-         */
-        public boolean isItemViewSwipeEnabled() {
-            return true;
-        }
-
-        /**
-         * When finding views under a dragged view, by default, ItemTouchHelper searches for views
-         * that overlap with the dragged View. By overriding this method, you can extend or shrink
-         * the search box.
-         *
-         * @return The extra margin to be added to the hit box of the dragged View.
-         */
-        public int getBoundingBoxMargin() {
-            return 0;
-        }
-
-        /**
-         * Returns the fraction that the user should move the View to be considered as swiped.
-         * The fraction is calculated with respect to RecyclerView's bounds.
-         * <p>
-         * Default value is .5f, which means, to swipe a View, user must move the View at least
-         * half of RecyclerView's width or height, depending on the swipe direction.
-         *
-         * @param viewHolder The ViewHolder that is being dragged.
-         * @return A float value that denotes the fraction of the View size. Default value
-         * is .5f .
-         */
-        public float getSwipeThreshold(ViewHolder viewHolder) {
-            return .5f;
-        }
-
-        /**
-         * Returns the fraction that the user should move the View to be considered as it is
-         * dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views
-         * below it for a possible drop.
-         *
-         * @param viewHolder The ViewHolder that is being dragged.
-         * @return A float value that denotes the fraction of the View size. Default value is
-         * .5f .
-         */
-        public float getMoveThreshold(ViewHolder viewHolder) {
-            return .5f;
-        }
-
-        /**
-         * Defines the minimum velocity which will be considered as a swipe action by the user.
-         * <p>
-         * You can increase this value to make it harder to swipe or decrease it to make it easier.
-         * Keep in mind that ItemTouchHelper also checks the perpendicular velocity and makes sure
-         * current direction velocity is larger then the perpendicular one. Otherwise, user's
-         * movement is ambiguous. You can change the threshold by overriding
-         * {@link #getSwipeVelocityThreshold(float)}.
-         * <p>
-         * The velocity is calculated in pixels per second.
-         * <p>
-         * The default framework value is passed as a parameter so that you can modify it with a
-         * multiplier.
-         *
-         * @param defaultValue The default value (in pixels per second) used by the
-         *                     ItemTouchHelper.
-         * @return The minimum swipe velocity. The default implementation returns the
-         * <code>defaultValue</code> parameter.
-         * @see #getSwipeVelocityThreshold(float)
-         * @see #getSwipeThreshold(ViewHolder)
-         */
-        public float getSwipeEscapeVelocity(float defaultValue) {
-            return defaultValue;
-        }
-
-        /**
-         * Defines the maximum velocity ItemTouchHelper will ever calculate for pointer movements.
-         * <p>
-         * To consider a movement as swipe, ItemTouchHelper requires it to be larger than the
-         * perpendicular movement. If both directions reach to the max threshold, none of them will
-         * be considered as a swipe because it is usually an indication that user rather tried to
-         * scroll then swipe.
-         * <p>
-         * The velocity is calculated in pixels per second.
-         * <p>
-         * You can customize this behavior by changing this method. If you increase the value, it
-         * will be easier for the user to swipe diagonally and if you decrease the value, user will
-         * need to make a rather straight finger movement to trigger a swipe.
-         *
-         * @param defaultValue The default value(in pixels per second) used by the ItemTouchHelper.
-         * @return The velocity cap for pointer movements. The default implementation returns the
-         * <code>defaultValue</code> parameter.
-         * @see #getSwipeEscapeVelocity(float)
-         */
-        public float getSwipeVelocityThreshold(float defaultValue) {
-            return defaultValue;
-        }
-
-        /**
-         * Called by ItemTouchHelper to select a drop target from the list of ViewHolders that
-         * are under the dragged View.
-         * <p>
-         * Default implementation filters the View with which dragged item have changed position
-         * in the drag direction. For instance, if the view is dragged UP, it compares the
-         * <code>view.getTop()</code> of the two views before and after drag started. If that value
-         * is different, the target view passes the filter.
-         * <p>
-         * Among these Views which pass the test, the one closest to the dragged view is chosen.
-         * <p>
-         * This method is called on the main thread every time user moves the View. If you want to
-         * override it, make sure it does not do any expensive operations.
-         *
-         * @param selected    The ViewHolder being dragged by the user.
-         * @param dropTargets The list of ViewHolder that are under the dragged View and
-         *                    candidate as a drop.
-         * @param curX        The updated left value of the dragged View after drag translations
-         *                    are applied. This value does not include margins added by
-         *                    {@link RecyclerView.ItemDecoration}s.
-         * @param curY        The updated top value of the dragged View after drag translations
-         *                    are applied. This value does not include margins added by
-         *                    {@link RecyclerView.ItemDecoration}s.
-         * @return A ViewHolder to whose position the dragged ViewHolder should be
-         * moved to.
-         */
-        public ViewHolder chooseDropTarget(ViewHolder selected,
-                List<ViewHolder> dropTargets, int curX, int curY) {
-            int right = curX + selected.itemView.getWidth();
-            int bottom = curY + selected.itemView.getHeight();
-            ViewHolder winner = null;
-            int winnerScore = -1;
-            final int dx = curX - selected.itemView.getLeft();
-            final int dy = curY - selected.itemView.getTop();
-            final int targetsSize = dropTargets.size();
-            for (int i = 0; i < targetsSize; i++) {
-                final ViewHolder target = dropTargets.get(i);
-                if (dx > 0) {
-                    int diff = target.itemView.getRight() - right;
-                    if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) {
-                        final int score = Math.abs(diff);
-                        if (score > winnerScore) {
-                            winnerScore = score;
-                            winner = target;
-                        }
-                    }
-                }
-                if (dx < 0) {
-                    int diff = target.itemView.getLeft() - curX;
-                    if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) {
-                        final int score = Math.abs(diff);
-                        if (score > winnerScore) {
-                            winnerScore = score;
-                            winner = target;
-                        }
-                    }
-                }
-                if (dy < 0) {
-                    int diff = target.itemView.getTop() - curY;
-                    if (diff > 0 && target.itemView.getTop() < selected.itemView.getTop()) {
-                        final int score = Math.abs(diff);
-                        if (score > winnerScore) {
-                            winnerScore = score;
-                            winner = target;
-                        }
-                    }
-                }
-
-                if (dy > 0) {
-                    int diff = target.itemView.getBottom() - bottom;
-                    if (diff < 0 && target.itemView.getBottom() > selected.itemView.getBottom()) {
-                        final int score = Math.abs(diff);
-                        if (score > winnerScore) {
-                            winnerScore = score;
-                            winner = target;
-                        }
-                    }
-                }
-            }
-            return winner;
-        }
-
-        /**
-         * Called when a ViewHolder is swiped by the user.
-         * <p>
-         * If you are returning relative directions ({@link #START} , {@link #END}) from the
-         * {@link #getMovementFlags(RecyclerView, ViewHolder)} method, this method
-         * will also use relative directions. Otherwise, it will use absolute directions.
-         * <p>
-         * If you don't support swiping, this method will never be called.
-         * <p>
-         * ItemTouchHelper will keep a reference to the View until it is detached from
-         * RecyclerView.
-         * As soon as it is detached, ItemTouchHelper will call
-         * {@link #clearView(RecyclerView, ViewHolder)}.
-         *
-         * @param viewHolder The ViewHolder which has been swiped by the user.
-         * @param direction  The direction to which the ViewHolder is swiped. It is one of
-         *                   {@link #UP}, {@link #DOWN},
-         *                   {@link #LEFT} or {@link #RIGHT}. If your
-         *                   {@link #getMovementFlags(RecyclerView, ViewHolder)}
-         *                   method
-         *                   returned relative flags instead of {@link #LEFT} / {@link #RIGHT};
-         *                   `direction` will be relative as well. ({@link #START} or {@link
-         *                   #END}).
-         */
-        public abstract void onSwiped(ViewHolder viewHolder, int direction);
-
-        /**
-         * Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed.
-         * <p/>
-         * If you override this method, you should call super.
-         *
-         * @param viewHolder  The new ViewHolder that is being swiped or dragged. Might be null if
-         *                    it is cleared.
-         * @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE},
-         *                    {@link ItemTouchHelper#ACTION_STATE_SWIPE} or
-         *                    {@link ItemTouchHelper#ACTION_STATE_DRAG}.
-         * @see #clearView(RecyclerView, RecyclerView.ViewHolder)
-         */
-        public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
-            if (viewHolder != null) {
-                sUICallback.onSelected(viewHolder.itemView);
-            }
-        }
-
-        private int getMaxDragScroll(RecyclerView recyclerView) {
-            if (mCachedMaxScrollSpeed == -1) {
-                mCachedMaxScrollSpeed = recyclerView.getResources().getDimensionPixelSize(
-                        R.dimen.item_touch_helper_max_drag_scroll_per_frame);
-            }
-            return mCachedMaxScrollSpeed;
-        }
-
-        /**
-         * Called when {@link #onMove(RecyclerView, ViewHolder, ViewHolder)} returns true.
-         * <p>
-         * ItemTouchHelper does not create an extra Bitmap or View while dragging, instead, it
-         * modifies the existing View. Because of this reason, it is important that the View is
-         * still part of the layout after it is moved. This may not work as intended when swapped
-         * Views are close to RecyclerView bounds or there are gaps between them (e.g. other Views
-         * which were not eligible for dropping over).
-         * <p>
-         * This method is responsible to give necessary hint to the LayoutManager so that it will
-         * keep the View in visible area. For example, for LinearLayoutManager, this is as simple
-         * as calling {@link LinearLayoutManager#scrollToPositionWithOffset(int, int)}.
-         *
-         * Default implementation calls {@link RecyclerView#scrollToPosition(int)} if the View's
-         * new position is likely to be out of bounds.
-         * <p>
-         * It is important to ensure the ViewHolder will stay visible as otherwise, it might be
-         * removed by the LayoutManager if the move causes the View to go out of bounds. In that
-         * case, drag will end prematurely.
-         *
-         * @param recyclerView The RecyclerView controlled by the ItemTouchHelper.
-         * @param viewHolder   The ViewHolder under user's control.
-         * @param fromPos      The previous adapter position of the dragged item (before it was
-         *                     moved).
-         * @param target       The ViewHolder on which the currently active item has been dropped.
-         * @param toPos        The new adapter position of the dragged item.
-         * @param x            The updated left value of the dragged View after drag translations
-         *                     are applied. This value does not include margins added by
-         *                     {@link RecyclerView.ItemDecoration}s.
-         * @param y            The updated top value of the dragged View after drag translations
-         *                     are applied. This value does not include margins added by
-         *                     {@link RecyclerView.ItemDecoration}s.
-         */
-        public void onMoved(final RecyclerView recyclerView,
-                final ViewHolder viewHolder, int fromPos, final ViewHolder target, int toPos, int x,
-                int y) {
-            final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
-            if (layoutManager instanceof ViewDropHandler) {
-                ((ViewDropHandler) layoutManager).prepareForDrop(viewHolder.itemView,
-                        target.itemView, x, y);
-                return;
-            }
-
-            // if layout manager cannot handle it, do some guesswork
-            if (layoutManager.canScrollHorizontally()) {
-                final int minLeft = layoutManager.getDecoratedLeft(target.itemView);
-                if (minLeft <= recyclerView.getPaddingLeft()) {
-                    recyclerView.scrollToPosition(toPos);
-                }
-                final int maxRight = layoutManager.getDecoratedRight(target.itemView);
-                if (maxRight >= recyclerView.getWidth() - recyclerView.getPaddingRight()) {
-                    recyclerView.scrollToPosition(toPos);
-                }
-            }
-
-            if (layoutManager.canScrollVertically()) {
-                final int minTop = layoutManager.getDecoratedTop(target.itemView);
-                if (minTop <= recyclerView.getPaddingTop()) {
-                    recyclerView.scrollToPosition(toPos);
-                }
-                final int maxBottom = layoutManager.getDecoratedBottom(target.itemView);
-                if (maxBottom >= recyclerView.getHeight() - recyclerView.getPaddingBottom()) {
-                    recyclerView.scrollToPosition(toPos);
-                }
-            }
-        }
-
-        void onDraw(Canvas c, RecyclerView parent, ViewHolder selected,
-                List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
-                int actionState, float dX, float dY) {
-            final int recoverAnimSize = recoverAnimationList.size();
-            for (int i = 0; i < recoverAnimSize; i++) {
-                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
-                anim.update();
-                final int count = c.save();
-                onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
-                        false);
-                c.restoreToCount(count);
-            }
-            if (selected != null) {
-                final int count = c.save();
-                onChildDraw(c, parent, selected, dX, dY, actionState, true);
-                c.restoreToCount(count);
-            }
-        }
-
-        void onDrawOver(Canvas c, RecyclerView parent, ViewHolder selected,
-                List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
-                int actionState, float dX, float dY) {
-            final int recoverAnimSize = recoverAnimationList.size();
-            for (int i = 0; i < recoverAnimSize; i++) {
-                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
-                final int count = c.save();
-                onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
-                        false);
-                c.restoreToCount(count);
-            }
-            if (selected != null) {
-                final int count = c.save();
-                onChildDrawOver(c, parent, selected, dX, dY, actionState, true);
-                c.restoreToCount(count);
-            }
-            boolean hasRunningAnimation = false;
-            for (int i = recoverAnimSize - 1; i >= 0; i--) {
-                final RecoverAnimation anim = recoverAnimationList.get(i);
-                if (anim.mEnded && !anim.mIsPendingCleanup) {
-                    recoverAnimationList.remove(i);
-                } else if (!anim.mEnded) {
-                    hasRunningAnimation = true;
-                }
-            }
-            if (hasRunningAnimation) {
-                parent.invalidate();
-            }
-        }
-
-        /**
-         * Called by the ItemTouchHelper when the user interaction with an element is over and it
-         * also completed its animation.
-         * <p>
-         * This is a good place to clear all changes on the View that was done in
-         * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)},
-         * {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int,
-         * boolean)} or
-         * {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}.
-         *
-         * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper.
-         * @param viewHolder   The View that was interacted by the user.
-         */
-        public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
-            sUICallback.clearView(viewHolder.itemView);
-        }
-
-        /**
-         * Called by ItemTouchHelper on RecyclerView's onDraw callback.
-         * <p>
-         * If you would like to customize how your View's respond to user interactions, this is
-         * a good place to override.
-         * <p>
-         * Default implementation translates the child by the given <code>dX</code>,
-         * <code>dY</code>.
-         * ItemTouchHelper also takes care of drawing the child after other children if it is being
-         * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this
-         * is
-         * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L
-         * and after, it changes View's elevation value to be greater than all other children.)
-         *
-         * @param c                 The canvas which RecyclerView is drawing its children
-         * @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to
-         * @param viewHolder        The ViewHolder which is being interacted by the User or it was
-         *                          interacted and simply animating to its original position
-         * @param dX                The amount of horizontal displacement caused by user's action
-         * @param dY                The amount of vertical displacement caused by user's action
-         * @param actionState       The type of interaction on the View. Is either {@link
-         *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
-         * @param isCurrentlyActive True if this view is currently being controlled by the user or
-         *                          false it is simply animating back to its original state.
-         * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
-         * boolean)
-         */
-        public void onChildDraw(Canvas c, RecyclerView recyclerView,
-                ViewHolder viewHolder,
-                float dX, float dY, int actionState, boolean isCurrentlyActive) {
-            sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
-                    isCurrentlyActive);
-        }
-
-        /**
-         * Called by ItemTouchHelper on RecyclerView's onDraw callback.
-         * <p>
-         * If you would like to customize how your View's respond to user interactions, this is
-         * a good place to override.
-         * <p>
-         * Default implementation translates the child by the given <code>dX</code>,
-         * <code>dY</code>.
-         * ItemTouchHelper also takes care of drawing the child after other children if it is being
-         * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this
-         * is
-         * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L
-         * and after, it changes View's elevation value to be greater than all other children.)
-         *
-         * @param c                 The canvas which RecyclerView is drawing its children
-         * @param recyclerView      The RecyclerView to which ItemTouchHelper is attached to
-         * @param viewHolder        The ViewHolder which is being interacted by the User or it was
-         *                          interacted and simply animating to its original position
-         * @param dX                The amount of horizontal displacement caused by user's action
-         * @param dY                The amount of vertical displacement caused by user's action
-         * @param actionState       The type of interaction on the View. Is either {@link
-         *                          #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}.
-         * @param isCurrentlyActive True if this view is currently being controlled by the user or
-         *                          false it is simply animating back to its original state.
-         * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int,
-         * boolean)
-         */
-        public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
-                ViewHolder viewHolder,
-                float dX, float dY, int actionState, boolean isCurrentlyActive) {
-            sUICallback.onDrawOver(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
-                    isCurrentlyActive);
-        }
-
-        /**
-         * Called by the ItemTouchHelper when user action finished on a ViewHolder and now the View
-         * will be animated to its final position.
-         * <p>
-         * Default implementation uses ItemAnimator's duration values. If
-         * <code>animationType</code> is {@link #ANIMATION_TYPE_DRAG}, it returns
-         * {@link RecyclerView.ItemAnimator#getMoveDuration()}, otherwise, it returns
-         * {@link RecyclerView.ItemAnimator#getRemoveDuration()}. If RecyclerView does not have
-         * any {@link RecyclerView.ItemAnimator} attached, this method returns
-         * {@code DEFAULT_DRAG_ANIMATION_DURATION} or {@code DEFAULT_SWIPE_ANIMATION_DURATION}
-         * depending on the animation type.
-         *
-         * @param recyclerView  The RecyclerView to which the ItemTouchHelper is attached to.
-         * @param animationType The type of animation. Is one of {@link #ANIMATION_TYPE_DRAG},
-         *                      {@link #ANIMATION_TYPE_SWIPE_CANCEL} or
-         *                      {@link #ANIMATION_TYPE_SWIPE_SUCCESS}.
-         * @param animateDx     The horizontal distance that the animation will offset
-         * @param animateDy     The vertical distance that the animation will offset
-         * @return The duration for the animation
-         */
-        public long getAnimationDuration(RecyclerView recyclerView, int animationType,
-                float animateDx, float animateDy) {
-            final RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();
-            if (itemAnimator == null) {
-                return animationType == ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION
-                        : DEFAULT_SWIPE_ANIMATION_DURATION;
-            } else {
-                return animationType == ANIMATION_TYPE_DRAG ? itemAnimator.getMoveDuration()
-                        : itemAnimator.getRemoveDuration();
-            }
-        }
-
-        /**
-         * Called by the ItemTouchHelper when user is dragging a view out of bounds.
-         * <p>
-         * You can override this method to decide how much RecyclerView should scroll in response
-         * to this action. Default implementation calculates a value based on the amount of View
-         * out of bounds and the time it spent there. The longer user keeps the View out of bounds,
-         * the faster the list will scroll. Similarly, the larger portion of the View is out of
-         * bounds, the faster the RecyclerView will scroll.
-         *
-         * @param recyclerView        The RecyclerView instance to which ItemTouchHelper is
-         *                            attached to.
-         * @param viewSize            The total size of the View in scroll direction, excluding
-         *                            item decorations.
-         * @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value
-         *                            is negative if the View is dragged towards left or top edge.
-         * @param totalSize           The total size of RecyclerView in the scroll direction.
-         * @param msSinceStartScroll  The time passed since View is kept out of bounds.
-         * @return The amount that RecyclerView should scroll. Keep in mind that this value will
-         * be passed to {@link RecyclerView#scrollBy(int, int)} method.
-         */
-        public int interpolateOutOfBoundsScroll(RecyclerView recyclerView,
-                int viewSize, int viewSizeOutOfBounds,
-                int totalSize, long msSinceStartScroll) {
-            final int maxScroll = getMaxDragScroll(recyclerView);
-            final int absOutOfBounds = Math.abs(viewSizeOutOfBounds);
-            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 float timeRatio;
-            if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) {
-                timeRatio = 1f;
-            } else {
-                timeRatio = (float) msSinceStartScroll / DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS;
-            }
-            final int value = (int) (cappedScroll * sDragScrollInterpolator
-                    .getInterpolation(timeRatio));
-            if (value == 0) {
-                return viewSizeOutOfBounds > 0 ? 1 : -1;
-            }
-            return value;
-        }
-    }
-
-    /**
-     * A simple wrapper to the default Callback which you can construct with drag and swipe
-     * directions and this class will handle the flag callbacks. You should still override onMove
-     * or
-     * onSwiped depending on your use case.
-     *
-     * <pre>
-     * ItemTouchHelper mIth = new ItemTouchHelper(
-     *     new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
-     *         ItemTouchHelper.LEFT) {
-     *         public abstract boolean onMove(RecyclerView recyclerView,
-     *             ViewHolder viewHolder, ViewHolder target) {
-     *             final int fromPos = viewHolder.getAdapterPosition();
-     *             final int toPos = target.getAdapterPosition();
-     *             // move item in `fromPos` to `toPos` in adapter.
-     *             return true;// true if moved, false otherwise
-     *         }
-     *         public void onSwiped(ViewHolder viewHolder, int direction) {
-     *             // remove from adapter
-     *         }
-     * });
-     * </pre>
-     */
-    public abstract static class SimpleCallback extends Callback {
-
-        private int mDefaultSwipeDirs;
-
-        private int mDefaultDragDirs;
-
-        /**
-         * Creates a Callback for the given drag and swipe allowance. These values serve as
-         * defaults
-         * and if you want to customize behavior per ViewHolder, you can override
-         * {@link #getSwipeDirs(RecyclerView, ViewHolder)}
-         * and / or {@link #getDragDirs(RecyclerView, ViewHolder)}.
-         *
-         * @param dragDirs  Binary OR of direction flags in which the Views can be dragged. Must be
-         *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link
-         *                  #END},
-         *                  {@link #UP} and {@link #DOWN}.
-         * @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be
-         *                  composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link
-         *                  #END},
-         *                  {@link #UP} and {@link #DOWN}.
-         */
-        public SimpleCallback(int dragDirs, int swipeDirs) {
-            mDefaultSwipeDirs = swipeDirs;
-            mDefaultDragDirs = dragDirs;
-        }
-
-        /**
-         * Updates the default swipe directions. For example, you can use this method to toggle
-         * certain directions depending on your use case.
-         *
-         * @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped.
-         */
-        public void setDefaultSwipeDirs(int defaultSwipeDirs) {
-            mDefaultSwipeDirs = defaultSwipeDirs;
-        }
-
-        /**
-         * Updates the default drag directions. For example, you can use this method to toggle
-         * certain directions depending on your use case.
-         *
-         * @param defaultDragDirs Binary OR of directions in which the ViewHolders can be dragged.
-         */
-        public void setDefaultDragDirs(int defaultDragDirs) {
-            mDefaultDragDirs = defaultDragDirs;
-        }
-
-        /**
-         * Returns the swipe directions for the provided ViewHolder.
-         * Default implementation returns the swipe directions that was set via constructor or
-         * {@link #setDefaultSwipeDirs(int)}.
-         *
-         * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.
-         * @param viewHolder   The RecyclerView for which the swipe direction is queried.
-         * @return A binary OR of direction flags.
-         */
-        public int getSwipeDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
-            return mDefaultSwipeDirs;
-        }
-
-        /**
-         * Returns the drag directions for the provided ViewHolder.
-         * Default implementation returns the drag directions that was set via constructor or
-         * {@link #setDefaultDragDirs(int)}.
-         *
-         * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to.
-         * @param viewHolder   The RecyclerView for which the swipe direction is queried.
-         * @return A binary OR of direction flags.
-         */
-        public int getDragDirs(RecyclerView recyclerView, ViewHolder viewHolder) {
-            return mDefaultDragDirs;
-        }
-
-        @Override
-        public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
-            return makeMovementFlags(getDragDirs(recyclerView, viewHolder),
-                    getSwipeDirs(recyclerView, viewHolder));
-        }
-    }
-
-    private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
-
-        /**
-         * Whether to execute code in response to the the invoking of
-         * {@link ItemTouchHelperGestureListener#onLongPress(MotionEvent)}.
-         *
-         * It is necessary to control this here because
-         * {@link GestureDetector.SimpleOnGestureListener} can only be set on a
-         * {@link GestureDetector} in a GestureDetector's constructor, a GestureDetector will call
-         * onLongPress if an {@link MotionEvent#ACTION_DOWN} event is not followed by another event
-         * that would cancel it (like {@link MotionEvent#ACTION_UP} or
-         * {@link MotionEvent#ACTION_CANCEL}), the long press responding to the long press event
-         * needs to be cancellable to prevent unexpected behavior.
-         *
-         * @see #doNotReactToLongPress()
-         */
-        private boolean mShouldReactToLongPress = true;
-
-        ItemTouchHelperGestureListener() {
-        }
-
-        /**
-         * Call to prevent executing code in response to
-         * {@link ItemTouchHelperGestureListener#onLongPress(MotionEvent)} being called.
-         */
-        void doNotReactToLongPress() {
-            mShouldReactToLongPress = false;
-        }
-
-        @Override
-        public boolean onDown(MotionEvent e) {
-            return true;
-        }
-
-        @Override
-        public void onLongPress(MotionEvent e) {
-            if (!mShouldReactToLongPress) {
-                return;
-            }
-            View child = findChildView(e);
-            if (child != null) {
-                ViewHolder vh = mRecyclerView.getChildViewHolder(child);
-                if (vh != null) {
-                    if (!mCallback.hasDragFlag(mRecyclerView, vh)) {
-                        return;
-                    }
-                    int pointerId = e.getPointerId(0);
-                    // Long press is deferred.
-                    // Check w/ active pointer id to avoid selecting after motion
-                    // event is canceled.
-                    if (pointerId == mActivePointerId) {
-                        final int index = e.findPointerIndex(mActivePointerId);
-                        final float x = e.getX(index);
-                        final float y = e.getY(index);
-                        mInitialTouchX = x;
-                        mInitialTouchY = y;
-                        mDx = mDy = 0f;
-                        if (DEBUG) {
-                            Log.d(TAG,
-                                    "onlong press: x:" + mInitialTouchX + ",y:" + mInitialTouchY);
-                        }
-                        if (mCallback.isLongPressDragEnabled()) {
-                            select(vh, ACTION_STATE_DRAG);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private static class RecoverAnimation implements Animator.AnimatorListener {
-
-        final float mStartDx;
-
-        final float mStartDy;
-
-        final float mTargetX;
-
-        final float mTargetY;
-
-        final ViewHolder mViewHolder;
-
-        final int mActionState;
-
-        private final ValueAnimator mValueAnimator;
-
-        final int mAnimationType;
-
-        public boolean mIsPendingCleanup;
-
-        float mX;
-
-        float mY;
-
-        // if user starts touching a recovering view, we put it into interaction mode again,
-        // instantly.
-        boolean mOverridden = false;
-
-        boolean mEnded = false;
-
-        private float mFraction;
-
-        RecoverAnimation(ViewHolder viewHolder, int animationType,
-                int actionState, float startDx, float startDy, float targetX, float targetY) {
-            mActionState = actionState;
-            mAnimationType = animationType;
-            mViewHolder = viewHolder;
-            mStartDx = startDx;
-            mStartDy = startDy;
-            mTargetX = targetX;
-            mTargetY = targetY;
-            mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
-            mValueAnimator.addUpdateListener(
-                    new ValueAnimator.AnimatorUpdateListener() {
-                        @Override
-                        public void onAnimationUpdate(ValueAnimator animation) {
-                            setFraction(animation.getAnimatedFraction());
-                        }
-                    });
-            mValueAnimator.setTarget(viewHolder.itemView);
-            mValueAnimator.addListener(this);
-            setFraction(0f);
-        }
-
-        public void setDuration(long duration) {
-            mValueAnimator.setDuration(duration);
-        }
-
-        public void start() {
-            mViewHolder.setIsRecyclable(false);
-            mValueAnimator.start();
-        }
-
-        public void cancel() {
-            mValueAnimator.cancel();
-        }
-
-        public void setFraction(float fraction) {
-            mFraction = fraction;
-        }
-
-        /**
-         * We run updates on onDraw method but use the fraction from animator callback.
-         * This way, we can sync translate x/y values w/ the animators to avoid one-off frames.
-         */
-        public void update() {
-            if (mStartDx == mTargetX) {
-                mX = mViewHolder.itemView.getTranslationX();
-            } else {
-                mX = mStartDx + mFraction * (mTargetX - mStartDx);
-            }
-            if (mStartDy == mTargetY) {
-                mY = mViewHolder.itemView.getTranslationY();
-            } else {
-                mY = mStartDy + mFraction * (mTargetY - mStartDy);
-            }
-        }
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (!mEnded) {
-                mViewHolder.setIsRecyclable(true);
-            }
-            mEnded = true;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            setFraction(1f); //make sure we recover the view's state.
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-
-        }
-    }
-}
diff --git a/android/support/v7/widget/helper/ItemTouchUIUtil.java b/android/support/v7/widget/helper/ItemTouchUIUtil.java
deleted file mode 100644
index 520a95e..0000000
--- a/android/support/v7/widget/helper/ItemTouchUIUtil.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.v7.widget.helper;
-
-import android.graphics.Canvas;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-/**
- * Utility class for {@link ItemTouchHelper} which handles item transformations for different
- * API versions.
- * <p/>
- * This class has methods that map to {@link ItemTouchHelper.Callback}'s drawing methods. Default
- * implementations in {@link ItemTouchHelper.Callback} call these methods with
- * {@link RecyclerView.ViewHolder#itemView} and {@link ItemTouchUIUtil} makes necessary changes
- * on the View depending on the API level. You can access the instance of {@link ItemTouchUIUtil}
- * via {@link ItemTouchHelper.Callback#getDefaultUIUtil()} and call its methods with the children
- * of ViewHolder that you want to apply default effects.
- *
- * @see ItemTouchHelper.Callback#getDefaultUIUtil()
- */
-public interface ItemTouchUIUtil {
-
-    /**
-     * The default implementation for {@link ItemTouchHelper.Callback#onChildDraw(Canvas,
-     * RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}
-     */
-    void onDraw(Canvas c, RecyclerView recyclerView, View view,
-            float dX, float dY, int actionState, boolean isCurrentlyActive);
-
-    /**
-     * The default implementation for {@link ItemTouchHelper.Callback#onChildDrawOver(Canvas,
-     * RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}
-     */
-    void onDrawOver(Canvas c, RecyclerView recyclerView, View view,
-            float dX, float dY, int actionState, boolean isCurrentlyActive);
-
-    /**
-     * The default implementation for {@link ItemTouchHelper.Callback#clearView(RecyclerView,
-     * RecyclerView.ViewHolder)}
-     */
-    void clearView(View view);
-
-    /**
-     * The default implementation for {@link ItemTouchHelper.Callback#onSelectedChanged(
-     * RecyclerView.ViewHolder, int)}
-     */
-    void onSelected(View view);
-}
-
diff --git a/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java b/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java
deleted file mode 100644
index 559b37e..0000000
--- a/android/support/v7/widget/helper/ItemTouchUIUtilImpl.java
+++ /dev/null
@@ -1,99 +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.widget.helper;
-
-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;
-
-/**
- * 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 Api21Impl extends BaseImpl {
-        @Override
-        public void onDraw(Canvas c, RecyclerView recyclerView, View view,
-                float dX, float dY, int actionState, boolean isCurrentlyActive) {
-            if (isCurrentlyActive) {
-                Object originalElevation = view.getTag(R.id.item_touch_helper_previous_elevation);
-                if (originalElevation == null) {
-                    originalElevation = ViewCompat.getElevation(view);
-                    float newElevation = 1f + findMaxElevation(recyclerView, view);
-                    ViewCompat.setElevation(view, newElevation);
-                    view.setTag(R.id.item_touch_helper_previous_elevation, originalElevation);
-                }
-            }
-            super.onDraw(c, recyclerView, view, dX, dY, actionState, isCurrentlyActive);
-        }
-
-        private float findMaxElevation(RecyclerView recyclerView, View itemView) {
-            final int childCount = recyclerView.getChildCount();
-            float max = 0;
-            for (int i = 0; i < childCount; i++) {
-                final View child = recyclerView.getChildAt(i);
-                if (child == itemView) {
-                    continue;
-                }
-                final float elevation = ViewCompat.getElevation(child);
-                if (elevation > max) {
-                    max = elevation;
-                }
-            }
-            return max;
-        }
-
-        @Override
-        public void clearView(View view) {
-            final Object tag = view.getTag(R.id.item_touch_helper_previous_elevation);
-            if (tag != null && tag instanceof Float) {
-                ViewCompat.setElevation(view, (Float) tag);
-            }
-            view.setTag(R.id.item_touch_helper_previous_elevation, null);
-            super.clearView(view);
-        }
-    }
-
-    static class BaseImpl implements ItemTouchUIUtil {
-
-        @Override
-        public void clearView(View view) {
-            view.setTranslationX(0f);
-            view.setTranslationY(0f);
-        }
-
-        @Override
-        public void onSelected(View view) {
-
-        }
-
-        @Override
-        public void onDraw(Canvas c, RecyclerView recyclerView, View view,
-                float dX, float dY, int actionState, boolean isCurrentlyActive) {
-            view.setTranslationX(dX);
-            view.setTranslationY(dY);
-        }
-
-        @Override
-        public void onDrawOver(Canvas c, RecyclerView recyclerView,
-                View view, float dX, float dY, int actionState, boolean isCurrentlyActive) {
-
-        }
-    }
-}
diff --git a/android/support/v7/widget/util/SortedListAdapterCallback.java b/android/support/v7/widget/util/SortedListAdapterCallback.java
deleted file mode 100644
index a1203a6..0000000
--- a/android/support/v7/widget/util/SortedListAdapterCallback.java
+++ /dev/null
@@ -1,64 +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.util;
-
-import android.support.v7.util.SortedList;
-import android.support.v7.widget.RecyclerView;
-
-/**
- * A {@link SortedList.Callback} implementation that can bind a {@link SortedList} to a
- * {@link RecyclerView.Adapter}.
- */
-public abstract class SortedListAdapterCallback<T2> extends SortedList.Callback<T2> {
-
-    final RecyclerView.Adapter mAdapter;
-
-    /**
-     * Creates a {@link SortedList.Callback} that will forward data change events to the provided
-     * Adapter.
-     *
-     * @param adapter The Adapter instance which should receive events from the SortedList.
-     */
-    public SortedListAdapterCallback(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) {
-        mAdapter.notifyItemRangeChanged(position, count);
-    }
-
-    @Override
-    public void onChanged(int position, int count, Object payload) {
-        mAdapter.notifyItemRangeChanged(position, count, payload);
-    }
-}
diff --git a/android/support/wear/ambient/AmbientDelegate.java b/android/support/wear/ambient/AmbientDelegate.java
deleted file mode 100644
index 8e96a02..0000000
--- a/android/support/wear/ambient/AmbientDelegate.java
+++ /dev/null
@@ -1,165 +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.wear.ambient;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-
-/**
- * Provides compatibility for ambient mode.
- */
-final class AmbientDelegate {
-
-    private static final String TAG = "AmbientDelegate";
-
-    private WearableActivityController mWearableController;
-
-    private static boolean sInitAutoResumeEnabledMethod;
-    private static boolean sHasAutoResumeEnabledMethod;
-    private final WearableControllerProvider mWearableControllerProvider;
-    private final AmbientCallback mCallback;
-    private final WeakReference<Activity> mActivity;
-
-    /**
-     * AmbientCallback must be implemented by all users of the delegate.
-     */
-    interface AmbientCallback {
-        /**
-         * Called when an activity is entering ambient mode. This event is sent while an activity is
-         * running (after onResume, before onPause). All drawing should complete by the conclusion
-         * of this method. Note that {@code invalidate()} calls will be executed before resuming
-         * lower-power mode.
-         * <p>
-         * <p><em>Derived classes must call through to the super class's implementation of this
-         * method. If they do not, an exception will be thrown.</em>
-         *
-         * @param ambientDetails bundle containing information about the display being used.
-         *                      It includes information about low-bit color and burn-in protection.
-         */
-        void onEnterAmbient(Bundle ambientDetails);
-
-        /**
-         * Called when the system is updating the display for ambient mode. Activities may use this
-         * opportunity to update or invalidate views.
-         */
-        void onUpdateAmbient();
-
-        /**
-         * Called when an activity should exit ambient mode. This event is sent while an activity is
-         * running (after onResume, before onPause).
-         * <p>
-         * <p><em>Derived classes must call through to the super class's implementation of this
-         * method. If they do not, an exception will be thrown.</em>
-         */
-        void onExitAmbient();
-    }
-
-    AmbientDelegate(@Nullable Activity activity,
-                           @NonNull WearableControllerProvider wearableControllerProvider,
-                           @NonNull AmbientCallback callback) {
-        mActivity = new WeakReference<>(activity);
-        mCallback = callback;
-        mWearableControllerProvider = wearableControllerProvider;
-    }
-
-    /**
-     * Receives and handles the onCreate call from the associated {@link AmbientMode}
-     */
-    void onCreate() {
-        Activity activity = mActivity.get();
-        if (activity != null) {
-            mWearableController =
-                    mWearableControllerProvider.getWearableController(activity, mCallback);
-        }
-        if (mWearableController != null) {
-            mWearableController.onCreate();
-        }
-    }
-
-    /**
-     * Receives and handles the onResume call from the associated {@link AmbientMode}
-     */
-    void onResume() {
-        if (mWearableController != null) {
-            mWearableController.onResume();
-        }
-    }
-
-    /**
-     * Receives and handles the onPause call from the associated {@link AmbientMode}
-     */
-    void onPause() {
-        if (mWearableController != null) {
-            mWearableController.onPause();
-        }
-    }
-
-    /**
-     * Receives and handles the onStop call from the associated {@link AmbientMode}
-     */
-    void onStop() {
-        if (mWearableController != null) {
-            mWearableController.onStop();
-        }
-    }
-
-    /**
-     * Receives and handles the onDestroy call from the associated {@link AmbientMode}
-     */
-    void onDestroy() {
-        if (mWearableController != null) {
-            mWearableController.onDestroy();
-        }
-    }
-
-    /**
-     * Sets that this activity should remain displayed when the system enters ambient mode. The
-     * default is false. In this case, the activity is stopped when the system enters ambient mode.
-     */
-    void setAmbientEnabled() {
-        if (mWearableController != null) {
-            mWearableController.setAmbientEnabled();
-        }
-    }
-
-    /**
-     * @return {@code true} if the activity is currently in ambient.
-     */
-    boolean isAmbient() {
-        if (mWearableController != null) {
-            return mWearableController.isAmbient();
-        }
-        return false;
-    }
-
-    /**
-     * Dump the current state of the wearableController responsible for implementing the Ambient
-     * mode.
-     */
-    void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (mWearableController != null) {
-            mWearableController.dump(prefix, fd, writer, args);
-        }
-    }
-}
diff --git a/android/support/wear/ambient/AmbientDelegateTest.java b/android/support/wear/ambient/AmbientDelegateTest.java
deleted file mode 100644
index 6033232..0000000
--- a/android/support/wear/ambient/AmbientDelegateTest.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.wear.ambient;
-
-import static org.junit.Assert.assertFalse;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.app.FragmentActivity;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-/**
- * Tests for {@link AmbientDelegate}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AmbientDelegateTest {
-
-    @Mock
-    AmbientDelegate.AmbientCallback mMockAmbientCallback;
-    @Mock
-    WearableControllerProvider mMockWearableControllerProvider;
-    @Mock
-    WearableActivityController mMockWearableController;
-    @Mock
-    FragmentActivity mMockActivity;
-
-    private AmbientDelegate mAmbientDelegateUnderTest;
-
-    @Before
-    public void setUp() {
-        mMockAmbientCallback = mock(AmbientDelegate.AmbientCallback.class);
-        mMockWearableControllerProvider = mock(WearableControllerProvider.class);
-        mMockWearableController = mock(WearableActivityController.class);
-        mMockActivity = mock(FragmentActivity.class);
-        when(mMockWearableControllerProvider
-                .getWearableController(mMockActivity, mMockAmbientCallback))
-                .thenReturn(mMockWearableController);
-    }
-
-    @Test
-    public void testNullActivity() {
-        mAmbientDelegateUnderTest = new AmbientDelegate(null,
-                mMockWearableControllerProvider, mMockAmbientCallback);
-        verifyZeroInteractions(mMockWearableControllerProvider);
-
-        assertFalse(mAmbientDelegateUnderTest.isAmbient());
-
-    }
-
-    @Test
-    public void testActivityPresent() {
-        mAmbientDelegateUnderTest = new AmbientDelegate(mMockActivity,
-                mMockWearableControllerProvider, mMockAmbientCallback);
-
-        mAmbientDelegateUnderTest.onCreate();
-        verify(mMockWearableController).onCreate();
-
-        mAmbientDelegateUnderTest.onResume();
-        verify(mMockWearableController).onResume();
-
-        mAmbientDelegateUnderTest.onPause();
-        verify(mMockWearableController).onPause();
-
-        mAmbientDelegateUnderTest.onStop();
-        verify(mMockWearableController).onStop();
-
-        mAmbientDelegateUnderTest.onDestroy();
-        verify(mMockWearableController).onDestroy();
-    }
-}
diff --git a/android/support/wear/ambient/AmbientMode.java b/android/support/wear/ambient/AmbientMode.java
deleted file mode 100644
index d3a8a43..0000000
--- a/android/support/wear/ambient/AmbientMode.java
+++ /dev/null
@@ -1,281 +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.wear.ambient;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.util.Log;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Use this as a headless Fragment to add ambient support to an Activity on Wearable devices.
- * <p>
- * The application that uses this should add the {@link android.Manifest.permission#WAKE_LOCK}
- * permission to its manifest.
- * <p>
- * The primary entry  point for this code is the {@link #attachAmbientSupport(Activity)} method.
- * It should be called with an {@link Activity} as an argument and that {@link Activity} will then
- * be able to receive ambient lifecycle events through an {@link AmbientCallback}. The
- * {@link Activity} will also receive a {@link AmbientController} object from the attachment which
- * can be used to query the current status of the ambient mode.
- * An example of how to attach {@link AmbientMode} to your {@link Activity} and use
- * the {@link AmbientController} can be found below:
- * <p>
- * <pre class="prettyprint">{@code
- *     AmbientMode.AmbientController controller = AmbientMode.attachAmbientSupport(this);
- *     boolean isAmbient =  controller.isAmbient();
- * }</pre>
- * @deprecated please use {@link AmbientModeSupport} instead.
- */
-@Deprecated
-public final class AmbientMode extends Fragment {
-    private static final String TAG = "AmbientMode";
-
-    /**
-     * Property in bundle passed to {@code AmbientCallback#onEnterAmbient(Bundle)} to indicate
-     * whether burn-in protection is required. When this property is set to true, views must be
-     * shifted around periodically in ambient mode. To ensure that content isn't shifted off
-     * the screen, avoid placing content within 10 pixels of the edge of the screen. Activities
-     * should also avoid solid white areas to prevent pixel burn-in. Both of these requirements
-     * only apply in ambient mode, and only when this property is set to true.
-     */
-    public static final String EXTRA_BURN_IN_PROTECTION =
-            WearableActivityController.EXTRA_BURN_IN_PROTECTION;
-
-    /**
-     * Property in bundle passed to {@code AmbientCallback#onEnterAmbient(Bundle)} to indicate
-     * whether the device has low-bit ambient mode. When this property is set to true, the screen
-     * supports fewer bits for each color in ambient mode. In this case, activities should disable
-     * anti-aliasing in ambient mode.
-     */
-    public static final String EXTRA_LOWBIT_AMBIENT =
-            WearableActivityController.EXTRA_LOWBIT_AMBIENT;
-
-    /**
-     * Fragment tag used by default when adding {@link AmbientMode} to add ambient support to an
-     * {@link Activity}.
-     */
-    public static final String FRAGMENT_TAG = "android.support.wearable.ambient.AmbientMode";
-
-    /**
-     * Interface for any {@link Activity} that wishes to implement Ambient Mode. Use the
-     * {@link #getAmbientCallback()} method to return and {@link AmbientCallback} which can be used
-     * to bind the {@link AmbientMode} to the instantiation of this interface.
-     * <p>
-     * <pre class="prettyprint">{@code
-     * return new AmbientMode.AmbientCallback() {
-     *     public void onEnterAmbient(Bundle ambientDetails) {...}
-     *     public void onExitAmbient(Bundle ambientDetails) {...}
-     *  }
-     * }</pre>
-     */
-    public interface AmbientCallbackProvider {
-        /**
-         * @return the {@link AmbientCallback} to be used by this class to communicate with the
-         * entity interested in ambient events.
-         */
-        AmbientCallback getAmbientCallback();
-    }
-
-    /**
-     * Callback to receive ambient mode state changes. It must be used by all users of AmbientMode.
-     */
-    public abstract static class AmbientCallback {
-        /**
-         * Called when an activity is entering ambient mode. This event is sent while an activity is
-         * running (after onResume, before onPause). All drawing should complete by the conclusion
-         * of this method. Note that {@code invalidate()} calls will be executed before resuming
-         * lower-power mode.
-         *
-         * @param ambientDetails bundle containing information about the display being used.
-         *                      It includes information about low-bit color and burn-in protection.
-         */
-        public void onEnterAmbient(Bundle ambientDetails) {}
-
-        /**
-         * Called when the system is updating the display for ambient mode. Activities may use this
-         * opportunity to update or invalidate views.
-         */
-        public void onUpdateAmbient() {}
-
-        /**
-         * Called when an activity should exit ambient mode. This event is sent while an activity is
-         * running (after onResume, before onPause).
-         */
-        public void onExitAmbient() {}
-    }
-
-    private final AmbientDelegate.AmbientCallback mCallback =
-            new AmbientDelegate.AmbientCallback() {
-                @Override
-                public void onEnterAmbient(Bundle ambientDetails) {
-                    if (mSuppliedCallback != null) {
-                        mSuppliedCallback.onEnterAmbient(ambientDetails);
-                    }
-                }
-
-                @Override
-                public void onExitAmbient() {
-                    if (mSuppliedCallback != null) {
-                        mSuppliedCallback.onExitAmbient();
-                    }
-                }
-
-                @Override
-                public void onUpdateAmbient() {
-                    if (mSuppliedCallback != null) {
-                        mSuppliedCallback.onUpdateAmbient();
-                    }
-                }
-            };
-    private AmbientDelegate mDelegate;
-    @Nullable
-    private AmbientCallback mSuppliedCallback;
-    private AmbientController mController;
-
-    /**
-     * Constructor
-     */
-    public AmbientMode() {
-        mController = new AmbientController();
-    }
-
-    @Override
-    @CallSuper
-    public void onAttach(Context context) {
-        super.onAttach(context);
-        mDelegate = new AmbientDelegate(getActivity(), new WearableControllerProvider(), mCallback);
-
-        if (context instanceof AmbientCallbackProvider) {
-            mSuppliedCallback = ((AmbientCallbackProvider) context).getAmbientCallback();
-        } else {
-            Log.w(TAG, "No callback provided - enabling only smart resume");
-        }
-    }
-
-    @Override
-    @CallSuper
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mDelegate.onCreate();
-        if (mSuppliedCallback != null) {
-            mDelegate.setAmbientEnabled();
-        }
-    }
-
-    @Override
-    @CallSuper
-    public void onResume() {
-        super.onResume();
-        mDelegate.onResume();
-    }
-
-    @Override
-    @CallSuper
-    public void onPause() {
-        mDelegate.onPause();
-        super.onPause();
-    }
-
-    @Override
-    @CallSuper
-    public void onStop() {
-        mDelegate.onStop();
-        super.onStop();
-    }
-
-    @Override
-    @CallSuper
-    public void onDestroy() {
-        mDelegate.onDestroy();
-        super.onDestroy();
-    }
-
-    @Override
-    @CallSuper
-    public void onDetach() {
-        mDelegate = null;
-        super.onDetach();
-    }
-
-    /**
-     * Attach ambient support to the given activity. Calling this method with an Activity
-     * implementing the {@link AmbientCallbackProvider} interface will provide you with an
-     * opportunity to react to ambient events such as {@code onEnterAmbient}. Alternatively,
-     * you can call this method with an Activity which does not implement
-     * the {@link AmbientCallbackProvider} interface and that will only enable the auto-resume
-     * functionality. This is equivalent to providing (@code null} from
-     * the {@link AmbientCallbackProvider}.
-     *
-     * @param activity the activity to attach ambient support to.
-     * @return the associated {@link AmbientController} which can be used to query the state of
-     * ambient mode.
-     */
-    public static <T extends Activity> AmbientController attachAmbientSupport(T activity) {
-        FragmentManager fragmentManager = activity.getFragmentManager();
-        AmbientMode ambientFragment = (AmbientMode) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
-        if (ambientFragment == null) {
-            AmbientMode fragment = new AmbientMode();
-            fragmentManager
-                    .beginTransaction()
-                    .add(fragment, FRAGMENT_TAG)
-                    .commit();
-            ambientFragment = fragment;
-        }
-        return ambientFragment.mController;
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (mDelegate != null) {
-            mDelegate.dump(prefix, fd, writer, args);
-        }
-    }
-
-    @VisibleForTesting
-    void setAmbientDelegate(AmbientDelegate delegate) {
-        mDelegate = delegate;
-    }
-
-    /**
-     * A class for interacting with the ambient mode on a wearable device. This class can be used to
-     * query the current state of ambient mode. An instance of this class is returned to the user
-     * when they attach their {@link Activity} to {@link AmbientMode}.
-     */
-    public final class AmbientController {
-        private static final String TAG = "AmbientController";
-
-        // Do not initialize outside of this class.
-        AmbientController() {}
-
-        /**
-         * @return {@code true} if the activity is currently in ambient.
-         */
-        public boolean isAmbient() {
-            return mDelegate == null ? false : mDelegate.isAmbient();
-        }
-    }
-}
diff --git a/android/support/wear/ambient/AmbientModeResumeTest.java b/android/support/wear/ambient/AmbientModeResumeTest.java
deleted file mode 100644
index 007c619..0000000
--- a/android/support/wear/ambient/AmbientModeResumeTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.wear.ambient;
-
-import static org.junit.Assert.assertFalse;
-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.wear.widget.util.WakeLockRule;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class AmbientModeResumeTest {
-    @Rule
-    public final WakeLockRule mWakeLock = new WakeLockRule();
-
-    @Rule
-    public final ActivityTestRule<AmbientModeResumeTestActivity> mActivityRule =
-            new ActivityTestRule<>(AmbientModeResumeTestActivity.class);
-
-    @Test
-    public void testActivityDefaults() throws Throwable {
-        assertTrue(WearableActivityController.getLastInstance().isAutoResumeEnabled());
-        assertFalse(WearableActivityController.getLastInstance().isAmbientEnabled());
-    }
-}
diff --git a/android/support/wear/ambient/AmbientModeResumeTestActivity.java b/android/support/wear/ambient/AmbientModeResumeTestActivity.java
deleted file mode 100644
index 0ca3c15..0000000
--- a/android/support/wear/ambient/AmbientModeResumeTestActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.support.wear.ambient;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AmbientModeResumeTestActivity extends Activity {
-    AmbientMode.AmbientController mAmbientController;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mAmbientController = AmbientMode.attachAmbientSupport(this);
-    }
-}
diff --git a/android/support/wear/ambient/AmbientModeSupport.java b/android/support/wear/ambient/AmbientModeSupport.java
deleted file mode 100644
index 97e26d9..0000000
--- a/android/support/wear/ambient/AmbientModeSupport.java
+++ /dev/null
@@ -1,281 +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.wear.ambient;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.CallSuper;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-import android.util.Log;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Use this as a headless Fragment to add ambient support to an Activity on Wearable devices.
- * <p>
- * The application that uses this should add the {@link android.Manifest.permission#WAKE_LOCK}
- * permission to its manifest.
- * <p>
- * The primary entry  point for this code is the {@link #attach(FragmentActivity)} method.
- * It should be called with an {@link FragmentActivity} as an argument and that
- * {@link FragmentActivity} will then be able to receive ambient lifecycle events through
- * an {@link AmbientCallback}. The {@link FragmentActivity} will also receive a
- * {@link AmbientController} object from the attachment which can be used to query the current
- * status of the ambient mode. An example of how to attach {@link AmbientModeSupport} to your
- * {@link FragmentActivity} and use the {@link AmbientController} can be found below:
- * <p>
- * <pre class="prettyprint">{@code
- *     AmbientMode.AmbientController controller = AmbientMode.attachAmbientSupport(this);
- *     boolean isAmbient =  controller.isAmbient();
- * }</pre>
- */
-public final class AmbientModeSupport extends Fragment {
-    private static final String TAG = "AmbientMode";
-
-    /**
-     * Property in bundle passed to {@code AmbientCallback#onEnterAmbient(Bundle)} to indicate
-     * whether burn-in protection is required. When this property is set to true, views must be
-     * shifted around periodically in ambient mode. To ensure that content isn't shifted off
-     * the screen, avoid placing content within 10 pixels of the edge of the screen. Activities
-     * should also avoid solid white areas to prevent pixel burn-in. Both of these requirements
-     * only apply in ambient mode, and only when this property is set to true.
-     */
-    public static final String EXTRA_BURN_IN_PROTECTION =
-            WearableActivityController.EXTRA_BURN_IN_PROTECTION;
-
-    /**
-     * Property in bundle passed to {@code AmbientCallback#onEnterAmbient(Bundle)} to indicate
-     * whether the device has low-bit ambient mode. When this property is set to true, the screen
-     * supports fewer bits for each color in ambient mode. In this case, activities should disable
-     * anti-aliasing in ambient mode.
-     */
-    public static final String EXTRA_LOWBIT_AMBIENT =
-            WearableActivityController.EXTRA_LOWBIT_AMBIENT;
-
-    /**
-     * Fragment tag used by default when adding {@link AmbientModeSupport} to add ambient support to
-     * a {@link FragmentActivity}.
-     */
-    public static final String FRAGMENT_TAG = "android.support.wearable.ambient.AmbientMode";
-
-    /**
-     * Interface for any {@link Activity} that wishes to implement Ambient Mode. Use the
-     * {@link #getAmbientCallback()} method to return and {@link AmbientCallback} which can be used
-     * to bind the {@link AmbientModeSupport} to the instantiation of this interface.
-     * <p>
-     * <pre class="prettyprint">{@code
-     * return new AmbientMode.AmbientCallback() {
-     *     public void onEnterAmbient(Bundle ambientDetails) {...}
-     *     public void onExitAmbient(Bundle ambientDetails) {...}
-     *  }
-     * }</pre>
-     */
-    public interface AmbientCallbackProvider {
-        /**
-         * @return the {@link AmbientCallback} to be used by this class to communicate with the
-         * entity interested in ambient events.
-         */
-        AmbientCallback getAmbientCallback();
-    }
-
-    /**
-     * Callback to receive ambient mode state changes. It must be used by all users of AmbientMode.
-     */
-    public abstract static class AmbientCallback {
-        /**
-         * Called when an activity is entering ambient mode. This event is sent while an activity is
-         * running (after onResume, before onPause). All drawing should complete by the conclusion
-         * of this method. Note that {@code invalidate()} calls will be executed before resuming
-         * lower-power mode.
-         *
-         * @param ambientDetails bundle containing information about the display being used.
-         *                      It includes information about low-bit color and burn-in protection.
-         */
-        public void onEnterAmbient(Bundle ambientDetails) {}
-
-        /**
-         * Called when the system is updating the display for ambient mode. Activities may use this
-         * opportunity to update or invalidate views.
-         */
-        public void onUpdateAmbient() {}
-
-        /**
-         * Called when an activity should exit ambient mode. This event is sent while an activity is
-         * running (after onResume, before onPause).
-         */
-        public void onExitAmbient() {}
-    }
-
-    private final AmbientDelegate.AmbientCallback mCallback =
-            new AmbientDelegate.AmbientCallback() {
-                @Override
-                public void onEnterAmbient(Bundle ambientDetails) {
-                    if (mSuppliedCallback != null) {
-                        mSuppliedCallback.onEnterAmbient(ambientDetails);
-                    }
-                }
-
-                @Override
-                public void onExitAmbient() {
-                    if (mSuppliedCallback != null) {
-                        mSuppliedCallback.onExitAmbient();
-                    }
-                }
-
-                @Override
-                public void onUpdateAmbient() {
-                    if (mSuppliedCallback != null) {
-                        mSuppliedCallback.onUpdateAmbient();
-                    }
-                }
-            };
-    private AmbientDelegate mDelegate;
-    @Nullable
-    private AmbientCallback mSuppliedCallback;
-    private AmbientController mController;
-
-    /**
-     * Constructor
-     */
-    public AmbientModeSupport() {
-        mController = new AmbientController();
-    }
-
-    @Override
-    @CallSuper
-    public void onAttach(Context context) {
-        super.onAttach(context);
-        mDelegate = new AmbientDelegate(getActivity(), new WearableControllerProvider(), mCallback);
-
-        if (context instanceof AmbientCallbackProvider) {
-            mSuppliedCallback = ((AmbientCallbackProvider) context).getAmbientCallback();
-        } else {
-            Log.w(TAG, "No callback provided - enabling only smart resume");
-        }
-    }
-
-    @Override
-    @CallSuper
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mDelegate.onCreate();
-        if (mSuppliedCallback != null) {
-            mDelegate.setAmbientEnabled();
-        }
-    }
-
-    @Override
-    @CallSuper
-    public void onResume() {
-        super.onResume();
-        mDelegate.onResume();
-    }
-
-    @Override
-    @CallSuper
-    public void onPause() {
-        mDelegate.onPause();
-        super.onPause();
-    }
-
-    @Override
-    @CallSuper
-    public void onStop() {
-        mDelegate.onStop();
-        super.onStop();
-    }
-
-    @Override
-    @CallSuper
-    public void onDestroy() {
-        mDelegate.onDestroy();
-        super.onDestroy();
-    }
-
-    @Override
-    @CallSuper
-    public void onDetach() {
-        mDelegate = null;
-        super.onDetach();
-    }
-
-    /**
-     * Attach ambient support to the given activity. Calling this method with an Activity
-     * implementing the {@link AmbientCallbackProvider} interface will provide you with an
-     * opportunity to react to ambient events such as {@code onEnterAmbient}. Alternatively,
-     * you can call this method with an Activity which does not implement
-     * the {@link AmbientCallbackProvider} interface and that will only enable the auto-resume
-     * functionality. This is equivalent to providing (@code null} from
-     * the {@link AmbientCallbackProvider}.
-     *
-     * @param activity the activity to attach ambient support to.
-     * @return the associated {@link AmbientController} which can be used to query the state of
-     * ambient mode.
-     */
-    public static <T extends FragmentActivity> AmbientController attach(T activity) {
-        FragmentManager fragmentManager = activity.getSupportFragmentManager();
-        AmbientModeSupport ambientFragment =
-                (AmbientModeSupport) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
-        if (ambientFragment == null) {
-            AmbientModeSupport fragment = new AmbientModeSupport();
-            fragmentManager
-                    .beginTransaction()
-                    .add(fragment, FRAGMENT_TAG)
-                    .commit();
-            ambientFragment = fragment;
-        }
-        return ambientFragment.mController;
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (mDelegate != null) {
-            mDelegate.dump(prefix, fd, writer, args);
-        }
-    }
-
-    @VisibleForTesting
-    void setAmbientDelegate(AmbientDelegate delegate) {
-        mDelegate = delegate;
-    }
-
-    /**
-     * A class for interacting with the ambient mode on a wearable device. This class can be used to
-     * query the current state of ambient mode. An instance of this class is returned to the user
-     * when they attach their {@link Activity} to {@link AmbientModeSupport}.
-     */
-    public final class AmbientController {
-        private static final String TAG = "AmbientController";
-
-        // Do not initialize outside of this class.
-        AmbientController() {}
-
-        /**
-         * @return {@code true} if the activity is currently in ambient.
-         */
-        public boolean isAmbient() {
-            return mDelegate == null ? false : mDelegate.isAmbient();
-        }
-    }
-}
diff --git a/android/support/wear/ambient/AmbientModeTest.java b/android/support/wear/ambient/AmbientModeTest.java
deleted file mode 100644
index f96c0c2..0000000
--- a/android/support/wear/ambient/AmbientModeTest.java
+++ /dev/null
@@ -1,88 +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.wear.ambient;
-
-import static org.junit.Assert.assertFalse;
-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.wear.widget.util.WakeLockRule;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class AmbientModeTest {
-    @Rule
-    public final WakeLockRule mWakeLock = new WakeLockRule();
-
-    @Rule
-    public final ActivityTestRule<AmbientModeTestActivity> mActivityRule = new ActivityTestRule<>(
-            AmbientModeTestActivity.class);
-
-    @Test
-    public void testEnterAmbientCallback() throws Throwable {
-        AmbientModeTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().enterAmbient();
-        assertTrue(activity.mEnterAmbientCalled);
-        assertFalse(activity.mUpdateAmbientCalled);
-        assertFalse(activity.mExitAmbientCalled);
-    }
-
-    @Test
-    public void testUpdateAmbientCallback() throws Throwable {
-        AmbientModeTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().updateAmbient();
-        assertFalse(activity.mEnterAmbientCalled);
-        assertTrue(activity.mUpdateAmbientCalled);
-        assertFalse(activity.mExitAmbientCalled);
-    }
-
-    @Test
-    public void testExitAmbientCallback() throws Throwable {
-        AmbientModeTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().exitAmbient();
-        assertFalse(activity.mEnterAmbientCalled);
-        assertFalse(activity.mUpdateAmbientCalled);
-        assertTrue(activity.mExitAmbientCalled);
-    }
-
-    @Test
-    public void testIsAmbientEnabled() {
-        assertTrue(WearableActivityController.getLastInstance().isAmbientEnabled());
-    }
-
-    @Test
-    public void testCallsControllerIsAmbient() {
-        AmbientModeTestActivity activity = mActivityRule.getActivity();
-
-        WearableActivityController.getLastInstance().setAmbient(true);
-        assertTrue(activity.getAmbientController().isAmbient());
-
-        WearableActivityController.getLastInstance().setAmbient(false);
-        assertFalse(activity.getAmbientController().isAmbient());
-    }
-}
diff --git a/android/support/wear/ambient/AmbientModeTestActivity.java b/android/support/wear/ambient/AmbientModeTestActivity.java
deleted file mode 100644
index 26155d8..0000000
--- a/android/support/wear/ambient/AmbientModeTestActivity.java
+++ /dev/null
@@ -1,62 +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.wear.ambient;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-
-public class AmbientModeTestActivity extends FragmentActivity
-        implements AmbientMode.AmbientCallbackProvider {
-    AmbientMode.AmbientController mAmbientController;
-
-    boolean mEnterAmbientCalled;
-    boolean mUpdateAmbientCalled;
-    boolean mExitAmbientCalled;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mAmbientController = AmbientMode.attachAmbientSupport(this);
-    }
-
-    @Override
-    public AmbientMode.AmbientCallback getAmbientCallback() {
-        return new MyAmbientCallback();
-    }
-
-    private class MyAmbientCallback extends AmbientMode.AmbientCallback {
-
-        @Override
-        public void onEnterAmbient(Bundle ambientDetails) {
-            mEnterAmbientCalled = true;
-        }
-
-        @Override
-        public void onUpdateAmbient() {
-            mUpdateAmbientCalled = true;
-        }
-
-        @Override
-        public void onExitAmbient() {
-            mExitAmbientCalled = true;
-        }
-    }
-
-    public AmbientMode.AmbientController getAmbientController() {
-        return mAmbientController;
-    }
-
-}
diff --git a/android/support/wear/ambient/SharedLibraryVersion.java b/android/support/wear/ambient/SharedLibraryVersion.java
deleted file mode 100644
index 9421d9e..0000000
--- a/android/support/wear/ambient/SharedLibraryVersion.java
+++ /dev/null
@@ -1,93 +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.wear.ambient;
-
-import android.os.Build;
-import android.support.annotation.VisibleForTesting;
-
-import com.google.android.wearable.WearableSharedLib;
-
-/**
- * Internal class which can be used to determine the version of the wearable shared library that is
- * available on the current device.
- */
-final class SharedLibraryVersion {
-
-    private SharedLibraryVersion() {
-    }
-
-    /**
-     * Returns the version of the wearable shared library available on the current device.
-     * <p>
-     * <p>Version 1 was introduced on 2016-09-26, so any previous shared library will return 0. In
-     * those cases, it may be necessary to check {@code Build.VERSION.SDK_INT}.
-     *
-     * @throws IllegalStateException if the Wearable Shared Library is not present, which means that
-     * the {@code <uses-library>} tag is missing.
-     */
-    public static int version() {
-        verifySharedLibraryPresent();
-        return VersionHolder.VERSION;
-    }
-
-    /**
-     * Throws {@link IllegalStateException} if the Wearable Shared Library is not present and API
-     * level is at least LMP MR1.
-     * <p>
-     * <p>This validates that the developer hasn't forgotten to include a {@code <uses-library>} tag
-     * in their manifest. The method should be used in combination with API level checks for
-     * features added before {@link #version() version} 1.
-     */
-    public static void verifySharedLibraryPresent() {
-        if (!PresenceHolder.PRESENT) {
-            throw new IllegalStateException("Could not find wearable shared library classes. "
-                    + "Please add <uses-library android:name=\"com.google.android.wearable\" "
-                    + "android:required=\"false\" /> to the application manifest");
-        }
-    }
-
-    // Lazy initialization holder class (see Effective Java item 71)
-    @VisibleForTesting
-    static final class VersionHolder {
-        static final int VERSION = getSharedLibVersion(Build.VERSION.SDK_INT);
-
-        @VisibleForTesting
-        static int getSharedLibVersion(int sdkInt) {
-            if (sdkInt < Build.VERSION_CODES.N_MR1) {
-                // WearableSharedLib was introduced in N MR1 (Wear FDP 4)
-                return 0;
-            }
-            return WearableSharedLib.version();
-        }
-    }
-
-    // Lazy initialization holder class (see Effective Java item 71)
-    @VisibleForTesting
-    static final class PresenceHolder {
-        static final boolean PRESENT = isSharedLibPresent(Build.VERSION.SDK_INT);
-
-        @VisibleForTesting
-        static boolean isSharedLibPresent(int sdkInt) {
-            try {
-                // A class which has been available on the shared library from the first version.
-                Class.forName("com.google.android.wearable.compat.WearableActivityController");
-            } catch (ClassNotFoundException e) {
-                return false;
-            }
-            return true;
-        }
-    }
-}
diff --git a/android/support/wear/ambient/WearableControllerProvider.java b/android/support/wear/ambient/WearableControllerProvider.java
deleted file mode 100644
index 4b6ae8e..0000000
--- a/android/support/wear/ambient/WearableControllerProvider.java
+++ /dev/null
@@ -1,96 +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.wear.ambient;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.annotation.RestrictTo;
-
-import com.google.android.wearable.compat.WearableActivityController;
-
-import java.lang.reflect.Method;
-
-/**
- * Provides a {@link WearableActivityController} for ambient mode control.
- *
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class WearableControllerProvider {
-
-    private static final String TAG = "WearableControllerProvider";
-
-    private static volatile boolean sAmbientCallbacksVerifiedPresent;
-
-    /**
-     * Retrieves a {@link WearableActivityController} to use for ambient mode.
-     *
-     * @param activity The {@link Activity} to be associated with the Controller.
-     * @param callback The {@link AmbientDelegate.AmbientCallback} for the Controller.
-     * @return the platform-appropriate version of the {@link WearableActivityController}.
-     */
-    public WearableActivityController getWearableController(Activity activity,
-            final AmbientDelegate.AmbientCallback callback) {
-        SharedLibraryVersion.verifySharedLibraryPresent();
-
-        // The AmbientCallback is an abstract class instead of an interface.
-        WearableActivityController.AmbientCallback callbackBridge =
-                new WearableActivityController.AmbientCallback() {
-                    @Override
-                    public void onEnterAmbient(Bundle ambientDetails) {
-                        callback.onEnterAmbient(ambientDetails);
-                    }
-
-                    @Override
-                    public void onUpdateAmbient() {
-                        callback.onUpdateAmbient();
-                    }
-
-                    @Override
-                    public void onExitAmbient() {
-                        callback.onExitAmbient();
-                    }
-                };
-
-        verifyAmbientCallbacksPresent();
-
-        return new WearableActivityController(TAG, activity, callbackBridge);
-    }
-
-    private static void verifyAmbientCallbacksPresent() {
-        if (sAmbientCallbacksVerifiedPresent) {
-            return;
-        }
-        try {
-            Method method =
-                    WearableActivityController.AmbientCallback.class.getDeclaredMethod(
-                            "onEnterAmbient", Bundle.class);
-            // Proguard is sneaky -- it will actually rewrite strings it finds in addition to
-            // function names. Therefore add a "." prefix to the method name check to ensure the
-            // function was not renamed by proguard.
-            if (!(".onEnterAmbient".equals("." + method.getName()))) {
-                throw new NoSuchMethodException();
-            }
-        } catch (NoSuchMethodException e) {
-            throw new IllegalStateException(
-                    "Could not find a required method for "
-                            + "ambient support, likely due to proguard optimization. Please add "
-                            + "com.google.android.wearable:wearable jar to the list of library jars"
-                            + " for your project");
-        }
-        sAmbientCallbacksVerifiedPresent = true;
-    }
-}
diff --git a/android/support/wear/internal/widget/ResourcesUtil.java b/android/support/wear/internal/widget/ResourcesUtil.java
deleted file mode 100644
index 8ba3adf..0000000
--- a/android/support/wear/internal/widget/ResourcesUtil.java
+++ /dev/null
@@ -1,56 +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.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)
-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/android/support/wear/internal/widget/drawer/MultiPagePresenter.java b/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
deleted file mode 100644
index 4a7ce66..0000000
--- a/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
+++ /dev/null
@@ -1,125 +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.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)
-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/android/support/wear/internal/widget/drawer/MultiPageUi.java b/android/support/wear/internal/widget/drawer/MultiPageUi.java
deleted file mode 100644
index 0ba2f5d..0000000
--- a/android/support/wear/internal/widget/drawer/MultiPageUi.java
+++ /dev/null
@@ -1,170 +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.wear.internal.widget.drawer;
-
-import android.support.annotation.NonNull;
-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)
-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 = content.findViewById(R.id.ws_navigation_drawer_view_pager);
-        mPageIndicatorView = 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;
-        }
-
-        @NonNull
-        @Override
-        public Object instantiateItem(@NonNull 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 =
-                    view.findViewById(R.id.ws_navigation_drawer_item_icon);
-            final 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(@NonNull ViewGroup container, int position,
-                @NonNull Object object) {
-            container.removeView((View) object);
-        }
-
-        @Override
-        public int getCount() {
-            return mAdapter.getCount();
-        }
-
-        @Override
-        public int getItemPosition(@NonNull Object object) {
-            return POSITION_NONE;
-        }
-
-        @Override
-        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
-            return view == object;
-        }
-    }
-}
diff --git a/android/support/wear/internal/widget/drawer/SinglePagePresenter.java b/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
deleted file mode 100644
index 42cc7d0..0000000
--- a/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
+++ /dev/null
@@ -1,167 +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.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)
-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/android/support/wear/internal/widget/drawer/SinglePageUi.java b/android/support/wear/internal/widget/drawer/SinglePageUi.java
deleted file mode 100644
index ffc966f..0000000
--- a/android/support/wear/internal/widget/drawer/SinglePageUi.java
+++ /dev/null
@@ -1,182 +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.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)
-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 = content.findViewById(R.id.ws_nav_drawer_text);
-        mSinglePageImageViews = new CircledImageView[count];
-        for (int i = 0; i < count; i++) {
-            mSinglePageImageViews[i] = 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/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java b/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
deleted file mode 100644
index df108aa..0000000
--- a/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
+++ /dev/null
@@ -1,95 +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.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)
-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/android/support/wear/internal/widget/drawer/package-info.java b/android/support/wear/internal/widget/drawer/package-info.java
deleted file mode 100644
index 519428a..0000000
--- a/android/support/wear/internal/widget/drawer/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * @hide
- */
-package android.support.wear.internal.widget.drawer;
diff --git a/android/support/wear/utils/MetadataConstants.java b/android/support/wear/utils/MetadataConstants.java
deleted file mode 100644
index c7335c2..0000000
--- a/android/support/wear/utils/MetadataConstants.java
+++ /dev/null
@@ -1,193 +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.wear.utils;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-
-/**
- * Constants for android wear apps which are related to manifest meta-data.
- */
-public class MetadataConstants {
-
-    //  Constants for standalone apps. //
-
-    /**
-     * The name of the meta-data element in the Wear app manifest for specifying whether this app
-     * does not require a companion phone app. The value should be set to "true" or "false".
-     * <p>
-     * <p>Wear apps that do not require a phone side companion app to function can declare this in
-     * their AndroidManifest.xml file by setting the standalone meta-data element to true as shown
-     * in the following example. If this value is true, all users can discover this app regardless
-     * of what phone they have. If this value is false (or not set), only users with compatible
-     * Android phones can discover this app.
-     * <p>
-     * <pre class="prettyprint">{@code
-     * <meta-data
-     * android:name="com.google.android.wearable.standalone"
-     * android:value="true" />
-     * }</pre>
-     */
-    public static final String STANDALONE_METADATA_NAME = "com.google.android.wearable.standalone";
-
-    //  Constants for customizing bridging of notifications from the phone to the wearable. //
-
-    /**
-     * We support specifying whether notifications should be bridged from the phone to the wearable
-     * in the Wear app manifest file. Simply add a meta-data element to the Wear app manifest with
-     * the name "com.google.android.wearable.notificationBridgeMode" and either the value
-     * NO_BRIDGING or the value BRIDGING. If you choose not to update your Wear app manifest, then
-     * notifications will be bridged by default from the phone to the wearable.
-     *
-     * <p>NO_BRIDGING means that phone notifications will not be bridged to the wearable if the
-     * wearable app is installed.
-     *
-     * <p>BRIDGING means that phone notifications will be bridged to the wearable, unless they are
-     * posted with
-     * {@link android.app.Notification.Builder#setLocalOnly(boolean) setLocalOnly(true)}.
-     *
-     * <p>Example AndroidManifest.xml meta-data element for NO_BRIDGING:
-     *
-     * <pre class="prettyprint">{@code
-     * <meta-data
-     *     android:name="com.google.android.wearable.notificationBridgeMode"
-     *     android:value="NO_BRIDGING" />
-     * }</pre>
-     *
-     * <p>Example AndroidManifest.xml meta-data element for BRIDGING:
-     *
-     * <pre class="prettyprint">{@code
-     * <meta-data
-     *     android:name="com.google.android.wearable.notificationBridgeMode"
-     *     android:value="BRIDGING" />
-     * }</pre>
-     */
-    public static final String NOTIFICATION_BRIDGE_MODE_METADATA_NAME =
-            "com.google.android.wearable.notificationBridgeMode";
-
-    /**
-     * The value of the notification bridge mode meta-data element in the case where the Wear app
-     * wants notifications to be bridged from the phone to the wearable.
-     */
-    public static final String NOTIFICATION_BRIDGE_MODE_BRIDGING = "BRIDGING";
-
-    /**
-     * The value of the notification bridge mode meta-data element in the case where the Wear app
-     * does not want notifications to be bridged from the phone to the wearable.
-     */
-    public static final String NOTIFICATION_BRIDGE_MODE_NO_BRIDGING = "NO_BRIDGING";
-
-    //  Constants for watch face preview. //
-
-    /**
-     * The name of the meta-data element in the watch face service manifest declaration used
-     * to assign a non-circular preview image to the watch face. The value should be set to
-     * a drawable reference.
-     *
-     * <pre class="prettyprint">
-     * &lt;meta-data
-     *     android:name="com.google.android.wearable.watchface.preview"
-     *     android:resource="@drawable/preview_face" /&gt;
-     * </pre>
-     */
-    public static final String WATCH_FACE_PREVIEW_METADATA_NAME =
-            "com.google.android.wearable.watchface.preview";
-
-    /**
-     * The name of the meta-data element in the watch face service manifest declaration used
-     * to assign a circular preview image to the watch face. The value should be set to
-     * a drawable reference.
-     *
-     * <pre class="prettyprint">
-     * &lt;meta-data
-     *     android:name="com.google.android.wearable.watchface.preview_circular"
-     *     android:resource="@drawable/preview_face_circular" /&gt;
-     * </pre>
-     */
-    public static final String WATCH_FACE_PREVIEW_CIRCULAR_METADATA_NAME =
-            "com.google.android.wearable.watchface.preview_circular";
-
-    // HELPER METHODS //
-
-    /**
-     * Determines whether a given context comes from a standalone app. This can be used as a proxy
-     * to check if any given app is compatible with iOS Companion devices.
-     *
-     * @param context to be evaluated.
-     * @return Whether a given context comes from a standalone app.
-     */
-    public static boolean isStandalone(Context context) {
-        try {
-            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
-                    context.getPackageName(), PackageManager.GET_META_DATA);
-            if (appInfo.metaData != null) {
-                return appInfo.metaData.getBoolean(STANDALONE_METADATA_NAME);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // Do nothing
-        }
-
-        return false;
-    }
-
-    /**
-     * Determines whether a given context has notification bridging enabled.
-     *
-     * @param context to be evaluated.
-     * @return Whether a given context has notification bridging enabled.
-     */
-    public static boolean isNotificationBridgingEnabled(Context context) {
-        try {
-            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
-                    context.getPackageName(), PackageManager.GET_META_DATA);
-            if (appInfo.metaData != null) {
-                return NOTIFICATION_BRIDGE_MODE_BRIDGING.equals(
-                        appInfo.metaData.getString(NOTIFICATION_BRIDGE_MODE_METADATA_NAME));
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // Do nothing
-        }
-
-        return true;
-    }
-
-    /**
-     *
-     * @param context to be evaluated.
-     * @param circular Whether to return the circular or regular preview.
-     *
-     * @return an integer id representing the resource id of the requested drawable, or 0 if
-     * no drawable was found.
-     */
-    public static int getPreviewDrawableResourceId(Context context, boolean circular) {
-        try {
-            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
-                    context.getPackageName(), PackageManager.GET_META_DATA);
-            if (appInfo.metaData != null) {
-                return circular
-                        ?  appInfo.metaData.getInt(WATCH_FACE_PREVIEW_CIRCULAR_METADATA_NAME, 0)
-                        :  appInfo.metaData.getInt(WATCH_FACE_PREVIEW_METADATA_NAME, 0);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // Do nothing
-        }
-
-        return 0;
-    }
-
-    private MetadataConstants() {}
-}
diff --git a/android/support/wear/utils/MetadataTestActivity.java b/android/support/wear/utils/MetadataTestActivity.java
deleted file mode 100644
index f64247e..0000000
--- a/android/support/wear/utils/MetadataTestActivity.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.wear.utils;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.wear.test.R;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class MetadataTestActivity extends Activity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        assertTrue(MetadataConstants.isStandalone(this));
-        assertTrue(MetadataConstants.isNotificationBridgingEnabled(this));
-        assertEquals(R.drawable.preview_face,
-                MetadataConstants.getPreviewDrawableResourceId(this, false));
-        assertEquals(R.drawable.preview_face_circular,
-                MetadataConstants.getPreviewDrawableResourceId(this, true));
-    }
-}
diff --git a/android/support/wear/widget/BezierSCurveInterpolator.java b/android/support/wear/widget/BezierSCurveInterpolator.java
deleted file mode 100644
index 9c56a83..0000000
--- a/android/support/wear/widget/BezierSCurveInterpolator.java
+++ /dev/null
@@ -1,81 +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.wear.widget;
-
-import android.animation.TimeInterpolator;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.RestrictTo.Scope;
-
-/**
- * Interpolator that uses a Bezier derived S shaped curve.
- *
- * @hide
- */
-@RestrictTo(Scope.LIBRARY)
-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/android/support/wear/widget/BoxInsetLayout.java b/android/support/wear/widget/BoxInsetLayout.java
deleted file mode 100644
index 383bcb7..0000000
--- a/android/support/wear/widget/BoxInsetLayout.java
+++ /dev/null
@@ -1,508 +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.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.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.146447f; //(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 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();
-        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)
-        @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/android/support/wear/widget/BoxInsetLayoutTest.java b/android/support/wear/widget/BoxInsetLayoutTest.java
deleted file mode 100644
index 731f57a..0000000
--- a/android/support/wear/widget/BoxInsetLayoutTest.java
+++ /dev/null
@@ -1,364 +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.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/android/support/wear/widget/CircledImageView.java b/android/support/wear/widget/CircledImageView.java
deleted file mode 100644
index c441dd5..0000000
--- a/android/support/wear/widget/CircledImageView.java
+++ /dev/null
@@ -1,795 +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.wear.widget;
-
-import android.animation.ArgbEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-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.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
- */
-@RestrictTo(Scope.LIBRARY)
-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.
-            mDrawable =
-                    mDrawable.getConstantState()
-                            .newDrawable(context.getResources(), context.getTheme());
-            mDrawable = mDrawable.mutate();
-        }
-
-        mCircleColor = a.getColorStateList(R.styleable.CircledImageView_background_color);
-        if (mCircleColor == null) {
-            mCircleColor = ColorStateList.valueOf(context.getColor(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/android/support/wear/widget/CircularProgressLayout.java b/android/support/wear/widget/CircularProgressLayout.java
deleted file mode 100644
index c317f28..0000000
--- a/android/support/wear/widget/CircularProgressLayout.java
+++ /dev/null
@@ -1,372 +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.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/android/support/wear/widget/CircularProgressLayoutController.java b/android/support/wear/widget/CircularProgressLayoutController.java
deleted file mode 100644
index 4fd7156..0000000
--- a/android/support/wear/widget/CircularProgressLayoutController.java
+++ /dev/null
@@ -1,138 +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.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/android/support/wear/widget/CircularProgressLayoutControllerTest.java b/android/support/wear/widget/CircularProgressLayoutControllerTest.java
deleted file mode 100644
index 2f625b6..0000000
--- a/android/support/wear/widget/CircularProgressLayoutControllerTest.java
+++ /dev/null
@@ -1,119 +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.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.LargeTest;
-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();
-    }
-
-    @LargeTest
-    @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/android/support/wear/widget/CircularProgressLayoutTest.java b/android/support/wear/widget/CircularProgressLayoutTest.java
deleted file mode 100644
index ff98c30..0000000
--- a/android/support/wear/widget/CircularProgressLayoutTest.java
+++ /dev/null
@@ -1,109 +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.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/android/support/wear/widget/CurvingLayoutCallback.java b/android/support/wear/widget/CurvingLayoutCallback.java
deleted file mode 100644
index 5e88a8c..0000000
--- a/android/support/wear/widget/CurvingLayoutCallback.java
+++ /dev/null
@@ -1,154 +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.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/android/support/wear/widget/LayoutTestActivity.java b/android/support/wear/widget/LayoutTestActivity.java
deleted file mode 100644
index ec909db..0000000
--- a/android/support/wear/widget/LayoutTestActivity.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.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/android/support/wear/widget/ProgressDrawable.java b/android/support/wear/widget/ProgressDrawable.java
deleted file mode 100644
index 28e0570..0000000
--- a/android/support/wear/widget/ProgressDrawable.java
+++ /dev/null
@@ -1,180 +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.wear.widget;
-
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-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.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
- */
-@RestrictTo(Scope.LIBRARY)
-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/android/support/wear/widget/RoundedDrawable.java b/android/support/wear/widget/RoundedDrawable.java
deleted file mode 100644
index 300b6dd..0000000
--- a/android/support/wear/widget/RoundedDrawable.java
+++ /dev/null
@@ -1,295 +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.wear.widget;
-
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-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.support.annotation.ColorInt;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
-import android.support.wear.R;
-import android.util.AttributeSet;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-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.
- *
- * <p>RoundedDrawable can be inflated from XML (supported above API level 24) or constructed
- * programmatically. To inflate from XML, use {@link android.content.Context#getDrawable(int)}
- * method.
- *
- * <h4>Syntax:</h4>
- *
- * <pre>
- * &lt;?xml version="1.0" encoding="utf-8"?&gt;
- * &lt;android.support.wear.widget.RoundedDrawable
- *   xmlns:android="http://schemas.android.com/apk/res/android"
- *   xmlns:app="http://schemas.android.com/apk/res-auto"
- *   android:src="drawable"
- *   app:backgroundColor="color"
- *   app:radius="dimension"
- *   app:clipEnabled="boolean" /&gt;</pre>
- */
-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);
-    }
-
-    @Override
-    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
-            @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
-            throws XmlPullParserException, IOException {
-        super.inflate(r, parser, attrs, theme);
-        TypedArray a = r.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.RoundedDrawable);
-        if (a.hasValue(R.styleable.RoundedDrawable_android_src)) {
-            setDrawable(a.getDrawable(R.styleable.RoundedDrawable_android_src));
-        }
-        setRadius(a.getDimensionPixelSize(R.styleable.RoundedDrawable_radius, 0));
-        setClipEnabled(a.getBoolean(R.styleable.RoundedDrawable_clipEnabled, false));
-        setBackgroundColor(
-                a.getColor(R.styleable.RoundedDrawable_backgroundColor, Color.TRANSPARENT));
-        a.recycle();
-    }
-
-    /**
-     * Sets the drawable to be rendered.
-     *
-     * @param drawable {@link Drawable} to be rendered
-     * @attr ref android.support.wear.R.styleable#RoundedDrawable_android_src
-     */
-    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
-     * @attr ref android.support.wear.R.styleable#RoundedDrawable_backgroundColor
-     */
-    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.
-     * @attr ref android.support.wear.R.styleable#RoundedDrawable_clipEnabled
-     */
-    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
-     * @attr ref android.support.wear.R.styleable#RoundedDrawable_radius
-     */
-    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/android/support/wear/widget/RoundedDrawableTest.java b/android/support/wear/widget/RoundedDrawableTest.java
deleted file mode 100644
index b01b3fa..0000000
--- a/android/support/wear/widget/RoundedDrawableTest.java
+++ /dev/null
@@ -1,147 +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.wear.widget;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-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.os.Build;
-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.wear.test.R;
-
-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));
-    }
-
-    @Test
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
-    public void inflate() {
-        RoundedDrawable roundedDrawable =
-                (RoundedDrawable) mActivityRule.getActivity().getDrawable(
-                        R.drawable.rounded_drawable);
-        assertEquals(
-                mActivityRule.getActivity().getColor(R.color.rounded_drawable_background_color),
-                roundedDrawable.getBackgroundColor());
-        assertTrue(roundedDrawable.isClipEnabled());
-        assertNotNull(roundedDrawable.getDrawable());
-        assertEquals(mActivityRule.getActivity().getResources().getDimensionPixelSize(
-                R.dimen.rounded_drawable_radius), roundedDrawable.getRadius());
-    }
-}
diff --git a/android/support/wear/widget/ScrollManager.java b/android/support/wear/widget/ScrollManager.java
deleted file mode 100644
index e01a271..0000000
--- a/android/support/wear/widget/ScrollManager.java
+++ /dev/null
@@ -1,232 +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.wear.widget;
-
-import android.support.annotation.RestrictTo;
-import android.support.annotation.RestrictTo.Scope;
-import android.support.v7.widget.RecyclerView;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-
-/**
- * Class adding circular scrolling support to {@link WearableRecyclerView}.
- *
- * @hide
- */
-@RestrictTo(Scope.LIBRARY)
-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/android/support/wear/widget/ScrollManagerTest.java b/android/support/wear/widget/ScrollManagerTest.java
deleted file mode 100644
index 34faea3..0000000
--- a/android/support/wear/widget/ScrollManagerTest.java
+++ /dev/null
@@ -1,202 +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.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/android/support/wear/widget/SimpleAnimatorListener.java b/android/support/wear/widget/SimpleAnimatorListener.java
deleted file mode 100644
index 3a1e56b..0000000
--- a/android/support/wear/widget/SimpleAnimatorListener.java
+++ /dev/null
@@ -1,70 +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.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)
-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/android/support/wear/widget/SwipeDismissFrameLayout.java b/android/support/wear/widget/SwipeDismissFrameLayout.java
deleted file mode 100644
index c2b595d..0000000
--- a/android/support/wear/widget/SwipeDismissFrameLayout.java
+++ /dev/null
@@ -1,294 +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.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/android/support/wear/widget/SwipeDismissFrameLayoutTest.java b/android/support/wear/widget/SwipeDismissFrameLayoutTest.java
deleted file mode 100644
index e4e47aa..0000000
--- a/android/support/wear/widget/SwipeDismissFrameLayoutTest.java
+++ /dev/null
@@ -1,460 +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.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/android/support/wear/widget/SwipeDismissFrameLayoutTestActivity.java b/android/support/wear/widget/SwipeDismissFrameLayoutTestActivity.java
deleted file mode 100644
index 5d86832..0000000
--- a/android/support/wear/widget/SwipeDismissFrameLayoutTestActivity.java
+++ /dev/null
@@ -1,82 +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.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/android/support/wear/widget/SwipeDismissLayout.java b/android/support/wear/widget/SwipeDismissLayout.java
deleted file mode 100644
index 33da79c..0000000
--- a/android/support/wear/widget/SwipeDismissLayout.java
+++ /dev/null
@@ -1,451 +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.wear.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
-import android.support.annotation.RestrictTo.Scope;
-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(Scope.LIBRARY)
-@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/android/support/wear/widget/SwipeDismissPreferenceFragment.java b/android/support/wear/widget/SwipeDismissPreferenceFragment.java
deleted file mode 100644
index a892cb6..0000000
--- a/android/support/wear/widget/SwipeDismissPreferenceFragment.java
+++ /dev/null
@@ -1,105 +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.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/android/support/wear/widget/WearableLinearLayoutManager.java b/android/support/wear/widget/WearableLinearLayoutManager.java
deleted file mode 100644
index 9c882ab..0000000
--- a/android/support/wear/widget/WearableLinearLayoutManager.java
+++ /dev/null
@@ -1,122 +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.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/android/support/wear/widget/WearableLinearLayoutManagerTest.java b/android/support/wear/widget/WearableLinearLayoutManagerTest.java
deleted file mode 100644
index 49da7b2..0000000
--- a/android/support/wear/widget/WearableLinearLayoutManagerTest.java
+++ /dev/null
@@ -1,160 +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.wear.widget;
-
-import static org.junit.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/android/support/wear/widget/WearableRecyclerView.java b/android/support/wear/widget/WearableRecyclerView.java
deleted file mode 100644
index 1425e68..0000000
--- a/android/support/wear/widget/WearableRecyclerView.java
+++ /dev/null
@@ -1,258 +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.wear.widget;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Point;
-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)
- */
-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/android/support/wear/widget/WearableRecyclerViewTest.java b/android/support/wear/widget/WearableRecyclerViewTest.java
deleted file mode 100644
index 5c17638..0000000
--- a/android/support/wear/widget/WearableRecyclerViewTest.java
+++ /dev/null
@@ -1,226 +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.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 org.hamcrest.Matchers.allOf;
-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.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(), 0.01f);
-        assertEquals(180.0f, wrv.getScrollDegreesPerScreen(), 0.01f);
-    }
-
-    @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/android/support/wear/widget/WearableRecyclerViewTestActivity.java b/android/support/wear/widget/WearableRecyclerViewTestActivity.java
deleted file mode 100644
index 2329fc5..0000000
--- a/android/support/wear/widget/WearableRecyclerViewTestActivity.java
+++ /dev/null
@@ -1,64 +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.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/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java b/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
deleted file mode 100644
index e9b2a40..0000000
--- a/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
+++ /dev/null
@@ -1,65 +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.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)
-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/android/support/wear/widget/drawer/DrawerTestActivity.java b/android/support/wear/widget/drawer/DrawerTestActivity.java
deleted file mode 100644
index 414b97b..0000000
--- a/android/support/wear/widget/drawer/DrawerTestActivity.java
+++ /dev/null
@@ -1,198 +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.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/android/support/wear/widget/drawer/FlingWatcherFactory.java b/android/support/wear/widget/drawer/FlingWatcherFactory.java
deleted file mode 100644
index 2fdfa13..0000000
--- a/android/support/wear/widget/drawer/FlingWatcherFactory.java
+++ /dev/null
@@ -1,103 +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.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)
-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/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java b/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
deleted file mode 100644
index 4c0e5c8..0000000
--- a/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
+++ /dev/null
@@ -1,96 +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.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)
-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/android/support/wear/widget/drawer/PageIndicatorView.java b/android/support/wear/widget/drawer/PageIndicatorView.java
deleted file mode 100644
index 1285f72..0000000
--- a/android/support/wear/widget/drawer/PageIndicatorView.java
+++ /dev/null
@@ -1,627 +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.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)
-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/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java b/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
deleted file mode 100644
index 7916875..0000000
--- a/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
+++ /dev/null
@@ -1,60 +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.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)
-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/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java b/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
deleted file mode 100644
index 5154e7b..0000000
--- a/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
+++ /dev/null
@@ -1,97 +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.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)
-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/android/support/wear/widget/drawer/WearableActionDrawerMenu.java b/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
deleted file mode 100644
index 092ac72..0000000
--- a/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
+++ /dev/null
@@ -1,471 +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.wear.widget.drawer;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-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;
-
-/* 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/android/support/wear/widget/drawer/WearableActionDrawerView.java b/android/support/wear/widget/drawer/WearableActionDrawerView.java
deleted file mode 100644
index 99cd4ff..0000000
--- a/android/support/wear/widget/drawer/WearableActionDrawerView.java
+++ /dev/null
@@ -1,465 +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.wear.widget.drawer;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-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.AccessibilityEvent;
-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.
- */
-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 = peekView.findViewById(R.id.ws_action_drawer_peek_action_icon);
-            mPeekExpandIcon = 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 void onDrawerOpened() {
-        if (mActionListAdapter.getItemCount() > 0) {
-            RecyclerView.ViewHolder holder = mActionList.findViewHolderForAdapterPosition(0);
-            if (holder != null && holder.itemView != null) {
-                holder.itemView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-            }
-        }
-    }
-
-    @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) {
-                                int listPosition = hasTitle() ? position + 1 : position;
-                                mActionListAdapter.notifyItemChanged(listPosition);
-                            }
-                            if (position == 0) {
-                                updatePeekIcons();
-                            }
-                        }
-
-                        @Override
-                        public void menuItemAdded(int position) {
-                            if (mActionListAdapter != null) {
-                                int listPosition = hasTitle() ? position + 1 : position;
-                                mActionListAdapter.notifyItemInserted(listPosition);
-                            }
-                            // 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) {
-                                int listPosition = hasTitle() ? position + 1 : position;
-                                mActionListAdapter.notifyItemRemoved(listPosition);
-                            }
-                            // 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.setImageDrawable(icon);
-            } else if (viewHolder instanceof TitleViewHolder) {
-                TitleViewHolder holder = (TitleViewHolder) viewHolder;
-                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/android/support/wear/widget/drawer/WearableDrawerController.java b/android/support/wear/widget/drawer/WearableDrawerController.java
deleted file mode 100644
index f5d69ec..0000000
--- a/android/support/wear/widget/drawer/WearableDrawerController.java
+++ /dev/null
@@ -1,53 +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.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/android/support/wear/widget/drawer/WearableDrawerLayout.java b/android/support/wear/widget/drawer/WearableDrawerLayout.java
deleted file mode 100644
index e100a46..0000000
--- a/android/support/wear/widget/drawer/WearableDrawerLayout.java
+++ /dev/null
@@ -1,1195 +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.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.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.NonNull;
-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>
- */
-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(@NonNull View target, float velocityX, float velocityY,
-            boolean consumed) {
-        return false;
-    }
-
-    @Override // NestedScrollingParent
-    public boolean onNestedPreFling(@NonNull 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(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
-        maybeUpdateScrollingContentView(target);
-    }
-
-    @Override // NestedScrollingParent
-    public void onNestedScroll(@NonNull 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(@NonNull View child, @NonNull View target,
-            int nestedScrollAxes) {
-        mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
-    }
-
-    @Override // NestedScrollingParent
-    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
-            int nestedScrollAxes) {
-        mCurrentNestedScrollSlopTracker = 0;
-        return true;
-    }
-
-    @Override // NestedScrollingParent
-    public void onStopNestedScroll(@NonNull 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(@NonNull 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(@NonNull View child) {
-            // Defines the vertical drag range of the drawer.
-            return child == getDrawerView() ? child.getHeight() : 0;
-        }
-
-        @Override
-        public void onViewCaptured(@NonNull 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(@NonNull 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(@NonNull 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(@NonNull 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(@NonNull 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(@NonNull 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(@NonNull 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/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java b/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
deleted file mode 100644
index d1e44e6..0000000
--- a/android/support/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
+++ /dev/null
@@ -1,668 +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.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.isAssignableFrom;
-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.is;
-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.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.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.Menu;
-import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-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;
-
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.Nullable;
-
-/**
- * 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(withId(R.id.ws_drawer_view_peek_container))
-                .perform(waitForMatchingView(
-                        allOf(
-                                withId(R.id.ws_drawer_view_peek_container),
-                                isCompletelyDisplayed()),
-                        MAX_WAIT_MS))
-                .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));
-    }
-
-    @Test
-    public void changingActionDrawerItemShouldUpdateView() {
-        // GIVEN a drawer layout with an open action drawer
-        activityRule.launchActivity(
-                new DrawerTestActivity.Builder()
-                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
-                        .openBottomDrawerInOnCreate()
-                        .build());
-        WearableActionDrawerView actionDrawer =
-                activityRule.getActivity().findViewById(R.id.action_drawer);
-        final MenuItem secondItem = actionDrawer.getMenu().getItem(1);
-
-        // WHEN its second item is changed
-        actionDrawer.post(new Runnable() {
-            @Override
-            public void run() {
-                secondItem.setTitle("Modified item");
-            }
-        });
-
-        // THEN the new item should be displayed
-        onView(withText("Modified item")).check(matches(isDisplayed()));
-    }
-
-    @Test
-    public void removingActionDrawerItemShouldUpdateView() {
-        // GIVEN a drawer layout with an open action drawer
-        activityRule.launchActivity(
-                new DrawerTestActivity.Builder()
-                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
-                        .openBottomDrawerInOnCreate()
-                        .build());
-        final WearableActionDrawerView actionDrawer =
-                activityRule.getActivity().findViewById(R.id.action_drawer);
-        MenuItem secondItem = actionDrawer.getMenu().getItem(1);
-        final int itemId = secondItem.getItemId();
-        final String title = secondItem.getTitle().toString();
-        final int initialSize = getChildByType(actionDrawer, RecyclerView.class)
-                .getAdapter()
-                .getItemCount();
-
-        // WHEN its second item is removed
-        actionDrawer.post(new Runnable() {
-            @Override
-            public void run() {
-                actionDrawer.getMenu().removeItem(itemId);
-            }
-        });
-
-        // THEN it should decrease by 1 in size and it should no longer contain the item's text
-        onView(allOf(withParent(withId(R.id.action_drawer)), isAssignableFrom(RecyclerView.class)))
-                .perform(waitForRecyclerToBeSize(initialSize - 1, MAX_WAIT_MS))
-                .perform(waitForMatchingView(recyclerWithoutText(is(title)), MAX_WAIT_MS));
-    }
-
-    @Test
-    public void addingActionDrawerItemShouldUpdateView() {
-        // GIVEN a drawer layout with an open action drawer
-        activityRule.launchActivity(
-                new DrawerTestActivity.Builder()
-                        .setStyle(DrawerStyle.ONLY_ACTION_DRAWER_WITH_TITLE)
-                        .openBottomDrawerInOnCreate()
-                        .build());
-        final WearableActionDrawerView actionDrawer =
-                activityRule.getActivity().findViewById(R.id.action_drawer);
-
-        RecyclerView recycler = getChildByType(actionDrawer, RecyclerView.class);
-        final RecyclerView.LayoutManager layoutManager = recycler.getLayoutManager();
-        final int initialSize = recycler.getAdapter().getItemCount();
-
-        // WHEN an item is added and the view is scrolled down (to make sure the view is created)
-        actionDrawer.post(new Runnable() {
-            @Override
-            public void run() {
-                actionDrawer.getMenu().add(0, 42, Menu.NONE, "New Item");
-                layoutManager.scrollToPosition(initialSize);
-            }
-        });
-
-        // THEN it should decrease by 1 in size and the there should be a view with the item's text
-        onView(allOf(withParent(withId(R.id.action_drawer)), isAssignableFrom(RecyclerView.class)))
-                .perform(waitForRecyclerToBeSize(initialSize + 1, MAX_WAIT_MS))
-                .perform(waitForMatchingView(withText("New Item"), MAX_WAIT_MS));
-    }
-
-    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");
-            }
-        };
-    }
-
-    /**
-     * Returns a {@link TypeSafeMatcher} that returns {@code true} when the {@link RecyclerView}
-     * does not contain a {@link TextView} with text matched by {@code textMatcher}.
-     */
-    private TypeSafeMatcher<View> recyclerWithoutText(final Matcher<String> textMatcher) {
-        return new TypeSafeMatcher<View>() {
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("Without recycler text ");
-                textMatcher.describeTo(description);
-            }
-
-            @Override
-            public boolean matchesSafely(View view) {
-                if (!(view instanceof RecyclerView)) {
-                    return false;
-                }
-
-                RecyclerView recycler = ((RecyclerView) view);
-                if (recycler.isAnimating()) {
-                    // While the RecyclerView is animating, it will return null ViewHolders and we
-                    // won't be able to tell whether the item has been removed or not.
-                    return false;
-                }
-
-                for (int i = 0; i < recycler.getAdapter().getItemCount(); i++) {
-                    RecyclerView.ViewHolder holder = recycler.findViewHolderForAdapterPosition(i);
-                    if (holder != null) {
-                        TextView text = getChildByType(holder.itemView, TextView.class);
-                        if (text != null && textMatcher.matches(text.getText())) {
-                            return false;
-                        }
-                    }
-                }
-
-                return true;
-            }
-        };
-    }
-
-    /**
-     * Waits for the {@link RecyclerView} to contain {@code targetCount} items, up to {@code millis}
-     * milliseconds. Throws exception if the time limit is reached before reaching the desired
-     * number of items.
-     */
-    public ViewAction waitForRecyclerToBeSize(final int targetCount, final long millis) {
-        return new ViewAction() {
-            @Override
-            public Matcher<View> getConstraints() {
-                return isAssignableFrom(RecyclerView.class);
-            }
-
-            @Override
-            public String getDescription() {
-                return "Waiting for recycler to be size=" + targetCount;
-            }
-
-            @Override
-            public void perform(UiController uiController, View view) {
-                if (!(view instanceof RecyclerView)) {
-                    return;
-                }
-
-                RecyclerView recycler = (RecyclerView) view;
-                uiController.loopMainThreadUntilIdle();
-                final long startTime = System.currentTimeMillis();
-                final long endTime = startTime + millis;
-                do {
-                    if (recycler.getAdapter().getItemCount() == targetCount) {
-                        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();
-            }
-        };
-    }
-
-    /**
-     * Returns the first child of {@code root} to be an instance of class {@code T}, or {@code null}
-     * if none were found.
-     */
-    @Nullable
-    private <T> T getChildByType(View root, Class<T> classOfChildToFind) {
-        for (View child : TreeIterables.breadthFirstViewTraversal(root)) {
-            if (classOfChildToFind.isInstance(child)) {
-                return (T) child;
-            }
-        }
-
-        return null;
-    }
-}
diff --git a/android/support/wear/widget/drawer/WearableDrawerView.java b/android/support/wear/widget/drawer/WearableDrawerView.java
deleted file mode 100644
index 2462cba..0000000
--- a/android/support/wear/widget/drawer/WearableDrawerView.java
+++ /dev/null
@@ -1,507 +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.wear.widget.drawer;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-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>
- */
-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)
-    @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 = findViewById(R.id.ws_drawer_view_peek_container);
-        mPeekIcon = 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/android/support/wear/widget/drawer/WearableNavigationDrawerView.java b/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
deleted file mode 100644
index c5c49fe..0000000
--- a/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
+++ /dev/null
@@ -1,288 +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.wear.widget.drawer;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-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.
- */
-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)
-    @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)
-        public void setPresenter(WearableNavigationDrawerPresenter presenter) {
-            mPresenter = presenter;
-        }
-    }
-
-}
diff --git a/android/support/wear/widget/util/ArcSwipe.java b/android/support/wear/widget/util/ArcSwipe.java
deleted file mode 100644
index 2630d19..0000000
--- a/android/support/wear/widget/util/ArcSwipe.java
+++ /dev/null
@@ -1,176 +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.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/android/support/wear/widget/util/ArcSwipeTest.java b/android/support/wear/widget/util/ArcSwipeTest.java
deleted file mode 100644
index f403e1b..0000000
--- a/android/support/wear/widget/util/ArcSwipeTest.java
+++ /dev/null
@@ -1,70 +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.wear.widget.util;
-
-import static org.junit.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/android/support/wear/widget/util/AsyncViewActions.java b/android/support/wear/widget/util/AsyncViewActions.java
deleted file mode 100644
index 3db4619..0000000
--- a/android/support/wear/widget/util/AsyncViewActions.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.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/android/support/wear/widget/util/MoreViewAssertions.java b/android/support/wear/widget/util/MoreViewAssertions.java
deleted file mode 100644
index 503336b..0000000
--- a/android/support/wear/widget/util/MoreViewAssertions.java
+++ /dev/null
@@ -1,207 +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.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/android/support/wear/widget/util/WakeLockRule.java b/android/support/wear/widget/util/WakeLockRule.java
deleted file mode 100644
index 13b627e..0000000
--- a/android/support/wear/widget/util/WakeLockRule.java
+++ /dev/null
@@ -1,57 +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.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());
-    }
-}
diff --git a/android/system/Int32Ref.java b/android/system/Int32Ref.java
index 25a818d..9f8bbe5 100644
--- a/android/system/Int32Ref.java
+++ b/android/system/Int32Ref.java
@@ -17,6 +17,7 @@
 package android.system;
 
 /**
+ * @hide
  * A signed 32bit integer reference suitable for passing to lower-level system calls.
  */
 public class Int32Ref {
diff --git a/android/system/Os.java b/android/system/Os.java
index a4b90e3..404b822 100644
--- a/android/system/Os.java
+++ b/android/system/Os.java
@@ -638,7 +638,7 @@
     public static void unsetenv(String name) throws ErrnoException { Libcore.os.unsetenv(name); }
 
     /**
-     * See <a href="http://man7.org/linux/man-pages/man2/waitpid.2.html">waitpid(2)</a>.
+     * @hide See <a href="http://man7.org/linux/man-pages/man2/waitpid.2.html">waitpid(2)</a>.
      *
      * @throws IllegalArgumentException if {@code status != null && status.length != 1}
      */
diff --git a/android/telecom/Call.java b/android/telecom/Call.java
index 6799417..1c0e260 100644
--- a/android/telecom/Call.java
+++ b/android/telecom/Call.java
@@ -352,8 +352,11 @@
          */
         public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
 
+        /** Call supports the deflect feature. */
+        public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+
         //******************************************************************************************
-        // Next CAPABILITY value: 0x01000000
+        // Next CAPABILITY value: 0x02000000
         //******************************************************************************************
 
         /**
@@ -419,11 +422,18 @@
         /**
          * Indicates the call used Assisted Dialing.
          * See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED}
+         * @hide
          */
         public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
 
+        /**
+         * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the
+         * {@link RttCall} object that is used to send and receive text.
+         */
+        public static final int PROPERTY_RTT = 0x00000400;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000400
+        // Next PROPERTY value: 0x00000800
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -528,6 +538,9 @@
             if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
                 builder.append(" CAPABILITY_CAN_PULL_CALL");
             }
+            if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+                builder.append(" CAPABILITY_SUPPORT_DEFLECT");
+            }
             builder.append("]");
             return builder.toString();
         }
@@ -866,42 +879,76 @@
         /**
          * @hide
          */
-        @IntDef({HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_DEST_NOT_SUPPORTED,
-                HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED,
-                HANDOVER_FAILURE_ONGOING_EMERG_CALL})
+        @IntDef(prefix = { "HANDOVER_" },
+                value = {HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_NOT_SUPPORTED,
+                HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL,
+                HANDOVER_FAILURE_UNKNOWN})
         @Retention(RetentionPolicy.SOURCE)
         public @interface HandoverFailureErrors {}
 
         /**
          * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when the app
-         * to handover the call rejects handover.
+         * to handover the call to rejects the handover request.
+         * <p>
+         * Will be returned when {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} is called
+         * and the destination {@link PhoneAccountHandle}'s {@link ConnectionService} returns a
+         * {@code null} {@link Connection} from
+         * {@link ConnectionService#onCreateOutgoingHandoverConnection(PhoneAccountHandle,
+         * ConnectionRequest)}.
+         * <p>
+         * For more information on call handovers, see
+         * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
          */
         public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1;
 
         /**
-         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there is
-         * an error associated with unsupported handover.
+         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when a handover
+         * is initiated but the source or destination app does not support handover.
+         * <p>
+         * Will be returned when a handover is requested via
+         * {@link #handoverTo(PhoneAccountHandle, int, Bundle)} and the destination
+         * {@link PhoneAccountHandle} does not declare
+         * {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_TO}.  May also be returned when a handover is
+         * requested at the {@link PhoneAccountHandle} for the current call (i.e. the source call's
+         * {@link Details#getAccountHandle()}) does not declare
+         * {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}.
+         * <p>
+         * For more information on call handovers, see
+         * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
          */
-        public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2;
+        public static final int HANDOVER_FAILURE_NOT_SUPPORTED = 2;
 
         /**
-         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there
-         * are some permission errors associated with APIs doing handover.
+         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when the remote
+         * user rejects the handover request.
+         * <p>
+         * For more information on call handovers, see
+         * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
          */
-        public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3;
-
-        /**
-         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when user
-         * rejects handover.
-         */
-        public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4;
+        public static final int HANDOVER_FAILURE_USER_REJECTED = 3;
 
         /**
          * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there
          * is ongoing emergency call.
+         * <p>
+         * This error code is returned when {@link #handoverTo(PhoneAccountHandle, int, Bundle)} is
+         * called on an emergency call, or if any other call is an emergency call.
+         * <p>
+         * Handovers are not permitted while there are ongoing emergency calls.
+         * <p>
+         * For more information on call handovers, see
+         * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
          */
-        public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5;
+        public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4;
 
+        /**
+         * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when a handover
+         * fails for an unknown reason.
+         * <p>
+         * For more information on call handovers, see
+         * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
+         */
+        public static final int HANDOVER_FAILURE_UNKNOWN = 5;
 
         /**
          * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
@@ -1042,6 +1089,10 @@
         /**
          * Invoked when Call handover from one {@link PhoneAccount} to other {@link PhoneAccount}
          * has completed successfully.
+         * <p>
+         * For a full discussion of the handover process and the APIs involved, see
+         * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
+         *
          * @param call The call which had initiated handover.
          */
         public void onHandoverComplete(Call call) {}
@@ -1049,8 +1100,12 @@
         /**
          * Invoked when Call handover from one {@link PhoneAccount} to other {@link PhoneAccount}
          * has failed.
+         * <p>
+         * For a full discussion of the handover process and the APIs involved, see
+         * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
+         *
          * @param call The call which had initiated handover.
-         * @param failureReason Error reason for failure
+         * @param failureReason Error reason for failure.
          */
         public void onHandoverFailed(Call call, @HandoverFailureErrors int failureReason) {}
     }
@@ -1183,6 +1238,23 @@
                 return null;
             }
         }
+
+        /**
+         * Closes the underlying file descriptors
+         * @hide
+         */
+        public void close() {
+            try {
+                mReceiveStream.close();
+            } catch (IOException e) {
+                // ignore
+            }
+            try {
+                mTransmitStream.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
     }
 
     /**
@@ -1230,11 +1302,20 @@
      * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
      * @param videoState The video state in which to answer the call.
      */
-    public void answer(int videoState) {
+    public void answer(@VideoProfile.VideoState int videoState) {
         mInCallAdapter.answerCall(mTelecomCallId, videoState);
     }
 
     /**
+     * Instructs this {@link #STATE_RINGING} {@code Call} to deflect.
+     *
+     * @param address The address to which the call will be deflected.
+     */
+    public void deflect(Uri address) {
+        mInCallAdapter.deflectCall(mTelecomCallId, address);
+    }
+
+    /**
      * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
      *
      * @param rejectWithMessage Whether to reject with a text message.
@@ -1435,16 +1516,65 @@
      * by {@code toHandle}.  The videoState specified indicates the desired video state after the
      * handover.
      * <p>
-     * A handover request is initiated by the user from one app to indicate a desire
-     * to handover a call to another.
+     * A call handover is the process where an ongoing call is transferred from one app (i.e.
+     * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
+     * mobile network call in a video calling app.  The mobile network call via the Telephony stack
+     * is referred to as the source of the handover, and the video calling app is referred to as the
+     * destination.
+     * <p>
+     * When considering a handover scenario the device this method is called on is considered the
+     * <em>initiating</em> device (since the user initiates the handover from this device), and the
+     * other device is considered the <em>receiving</em> device.
+     * <p>
+     * When this method is called on the <em>initiating</em> device, the Telecom framework will bind
+     * to the {@link ConnectionService} defined by the {@code toHandle} {@link PhoneAccountHandle}
+     * and invoke
+     * {@link ConnectionService#onCreateOutgoingHandoverConnection(PhoneAccountHandle,
+     * ConnectionRequest)} to inform the destination app that a request has been made to handover a
+     * call to it.  The app returns an instance of {@link Connection} to represent the handover call
+     * At this point the app should display UI to indicate to the user that a call
+     * handover is in process.
+     * <p>
+     * The destination app is responsible for communicating the handover request from the
+     * <em>initiating</em> device to the <em>receiving</em> device.
+     * <p>
+     * When the app on the <em>receiving</em> device receives the handover request, it calls
+     * {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to continue the handover
+     * process from the <em>initiating</em> device to the <em>receiving</em> device.  At this point
+     * the destination app on the <em>receiving</em> device should show UI to allow the user to
+     * choose whether they want to continue their call in the destination app.
+     * <p>
+     * When the destination app on the <em>receiving</em> device calls
+     * {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)}, Telecom will bind to its
+     * {@link ConnectionService} and call
+     * {@link ConnectionService#onCreateIncomingHandoverConnection(PhoneAccountHandle,
+     * ConnectionRequest)} to inform it of the handover request.  The app returns an instance of
+     * {@link Connection} to represent the handover call.
+     * <p>
+     * If the user of the <em>receiving</em> device accepts the handover, the app calls
+     * {@link Connection#setActive()} to complete the handover process; Telecom will disconnect the
+     * original call.  If the user rejects the handover, the app calls
+     * {@link Connection#setDisconnected(DisconnectCause)} and specifies a {@link DisconnectCause}
+     * of {@link DisconnectCause#CANCELED} to indicate that the handover has been cancelled.
+     * <p>
+     * Telecom will only allow handovers from {@link PhoneAccount}s which declare
+     * {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}.  Similarly, the {@link PhoneAccount}
+     * specified by {@code toHandle} must declare {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_TO}.
+     * <p>
+     * Errors in the handover process are reported to the {@link InCallService} via
+     * {@link Callback#onHandoverFailed(Call, int)}.  Errors in the handover process are reported to
+     * the involved {@link ConnectionService}s via
+     * {@link ConnectionService#onHandoverFailed(ConnectionRequest, int)}.
      *
      * @param toHandle {@link PhoneAccountHandle} of the {@link ConnectionService} to handover
      *                 this call to.
-     * @param videoState Indicates the video state desired after the handover.
+     * @param videoState Indicates the video state desired after the handover (see the
+     *               {@code STATE_*} constants defined in {@link VideoProfile}).
      * @param extras Bundle containing extra information to be passed to the
      *               {@link ConnectionService}
      */
-    public void handoverTo(PhoneAccountHandle toHandle, int videoState, Bundle extras) {
+    public void handoverTo(PhoneAccountHandle toHandle, @VideoProfile.VideoState int videoState,
+            Bundle extras) {
         mInCallAdapter.handoverTo(mTelecomCallId, toHandle, videoState, extras);
     }
 
@@ -1649,7 +1779,7 @@
      * @return true if there is a connection, false otherwise.
      */
     public boolean isRttActive() {
-        return mRttCall != null;
+        return mRttCall != null && mDetails.hasProperty(Details.PROPERTY_RTT);
     }
 
     /**
@@ -1852,7 +1982,8 @@
 
         boolean isRttChanged = false;
         boolean rttModeChanged = false;
-        if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) {
+        if (parcelableCall.getIsRttCallChanged()
+                && mDetails.hasProperty(Details.PROPERTY_RTT)) {
             ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall();
             InputStreamReader receiveStream = new InputStreamReader(
                     new ParcelFileDescriptor.AutoCloseInputStream(
diff --git a/android/telecom/Conference.java b/android/telecom/Conference.java
index 5fcff18..024bd30 100644
--- a/android/telecom/Conference.java
+++ b/android/telecom/Conference.java
@@ -29,7 +29,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -82,7 +81,7 @@
     private int mConnectionProperties;
     private String mDisconnectMessage;
     private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
-    private long mConnectElapsedTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+    private long mConnectionStartElapsedRealTime = CONNECT_TIME_NOT_SPECIFIED;
     private StatusHints mStatusHints;
     private Bundle mExtras;
     private Set<String> mPreviousExtraKeys;
@@ -584,30 +583,36 @@
     }
 
     /**
-     * Sets the connection start time of the {@code Conference}.  Should be specified in wall-clock
-     * time returned by {@link System#currentTimeMillis()}.
+     * Sets the connection start time of the {@code Conference}.  This is used in the call log to
+     * indicate the date and time when the conference took place.
+     * <p>
+     * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
      * <p>
      * When setting the connection time, you should always set the connection elapsed time via
-     * {@link #setConnectionElapsedTime(long)}.
+     * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
      *
-     * @param connectionTimeMillis The connection time, in milliseconds.
+     * @param connectionTimeMillis The connection time, in milliseconds, as returned by
+     *                             {@link System#currentTimeMillis()}.
      */
     public final void setConnectionTime(long connectionTimeMillis) {
         mConnectTimeMillis = connectionTimeMillis;
     }
 
     /**
-     * Sets the elapsed time since system boot when the {@link Conference} was connected.
-     * This is used to determine the duration of the {@link Conference}.
+     * Sets the start time of the {@link Conference} which is the basis for the determining the
+     * duration of the {@link Conference}.
      * <p>
-     * When setting the connection elapsed time, you should always set the connection time via
+     * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
+     * zone changes do not impact the conference duration.
+     * <p>
+     * When setting this, you should also set the connection time via
      * {@link #setConnectionTime(long)}.
      *
-     * @param connectionElapsedTime The connection time, as measured by
+     * @param connectionStartElapsedRealTime The connection time, as measured by
      * {@link SystemClock#elapsedRealtime()}.
      */
-    public final void setConnectionElapsedTime(long connectionElapsedTime) {
-        mConnectElapsedTimeMillis = connectionElapsedTime;
+    public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
+        mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
     }
 
     /**
@@ -642,8 +647,8 @@
      * @return The elapsed time at which the {@link Conference} was connected.
      * @hide
      */
-    public final long getConnectElapsedTime() {
-        return mConnectElapsedTimeMillis;
+    public final long getConnectionStartElapsedRealTime() {
+        return mConnectionStartElapsedRealTime;
     }
 
     /**
diff --git a/android/telecom/Connection.java b/android/telecom/Connection.java
index 63f970a..36333e4 100644
--- a/android/telecom/Connection.java
+++ b/android/telecom/Connection.java
@@ -35,12 +35,13 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.view.Surface;
 
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
@@ -328,8 +329,11 @@
      */
     public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
 
+    /** Call supports the deflect feature. */
+    public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x02000000
+    // Next CAPABILITY value: 0x04000000
     //**********************************************************************************************
 
     /**
@@ -402,6 +406,7 @@
 
     /**
      * Set by the framework to indicate that a connection is using assisted dialing.
+     * @hide
      */
     public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
 
@@ -726,6 +731,9 @@
         if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
             builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
         }
+        if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+            builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
+        }
 
         builder.append("]");
         return builder.toString();
@@ -787,6 +795,10 @@
             builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
         }
 
+        if (can(properties, PROPERTY_IS_RTT)) {
+            builder.append(isLong ? " PROPERTY_IS_RTT" : " rtt");
+        }
+
         builder.append("]");
         return builder.toString();
     }
@@ -850,18 +862,19 @@
             mFdFromInCall = fromInCall;
             mFdToInCall = toInCall;
             mPipeFromInCall = new InputStreamReader(
-                    new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
+                    new FileInputStream(fromInCall.getFileDescriptor()));
             mPipeToInCall = new OutputStreamWriter(
-                    new ParcelFileDescriptor.AutoCloseOutputStream(toInCall));
+                    new FileOutputStream(toInCall.getFileDescriptor()));
         }
 
         /**
          * Writes the string {@param input} into the text stream to the UI for this RTT call. Since
          * RTT transmits text in real-time, this method should be called as often as text snippets
          * are received from the remote user, even if it is only one character.
-         *
+         * <p>
          * This method is not thread-safe -- calling it from multiple threads simultaneously may
          * lead to interleaved text.
+         *
          * @param input The message to send to the in-call app.
          */
         public void write(String input) throws IOException {
@@ -874,9 +887,10 @@
          * Reads a string from the in-call app, blocking if there is no data available. Returns
          * {@code null} if the RTT conversation has been terminated and there is no further data
          * to read.
-         *
+         * <p>
          * This method is not thread-safe -- calling it from multiple threads simultaneously may
          * lead to interleaved text.
+         *
          * @return A string containing text entered by the user, or {@code null} if the
          * conversation has been terminated or if there was an error while reading.
          */
@@ -891,6 +905,7 @@
         /**
          * Non-blocking version of {@link #read()}. Returns {@code null} if there is nothing to
          * be read.
+         *
          * @return A string containing text entered by the user, or {@code null} if the user has
          * not entered any new text yet.
          */
@@ -2289,7 +2304,7 @@
      *
      * @hide
      */
-    public final void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
+    public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
     }
 
@@ -2538,19 +2553,6 @@
     }
 
     /**
-     * Adds a parcelable extra to this {@code Connection}.
-     *
-     * @param key The extra key.
-     * @param value The value.
-     * @hide
-     */
-    public final void putExtra(@NonNull String key, @NonNull Parcelable value) {
-        Bundle newExtras = new Bundle();
-        newExtras.putParcelable(key, value);
-        putExtras(newExtras);
-    }
-
-    /**
      * Removes extras from this {@code Connection}.
      *
      * @param keys The keys of the extras to remove.
@@ -2625,7 +2627,6 @@
      * {@link #onStartRtt(RttTextStream)} has succeeded.
      */
     public final void sendRttInitiationSuccess() {
-        setRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
     }
 
@@ -2637,7 +2638,6 @@
      *               exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
      */
     public final void sendRttInitiationFailure(int reason) {
-        unsetRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
     }
 
@@ -2732,7 +2732,20 @@
     /**
      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
      * a request to accept.
-     *
+     * <p>
+     * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+     * the default dialer's {@link InCallService}.
+     * <p>
+     * Although a self-managed {@link ConnectionService} provides its own incoming call UI, the
+     * Telecom framework may request that the call is answered in the following circumstances:
+     * <ul>
+     *     <li>The user chooses to answer an incoming call via a Bluetooth device.</li>
+     *     <li>A car mode {@link InCallService} is in use which has declared
+     *     {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS} in its manifest.  Such an
+     *     {@link InCallService} will be able to see calls from self-managed
+     *     {@link ConnectionService}s, and will be able to display an incoming call UI on their
+     *     behalf.</li>
+     * </ul>
      * @param videoState The video state in which to answer the connection.
      */
     public void onAnswer(int videoState) {}
@@ -2740,6 +2753,20 @@
     /**
      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
      * a request to accept.
+     * <p>
+     * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+     * the default dialer's {@link InCallService}.
+     * <p>
+     * Although a self-managed {@link ConnectionService} provides its own incoming call UI, the
+     * Telecom framework may request that the call is answered in the following circumstances:
+     * <ul>
+     *     <li>The user chooses to answer an incoming call via a Bluetooth device.</li>
+     *     <li>A car mode {@link InCallService} is in use which has declared
+     *     {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS} in its manifest.  Such an
+     *     {@link InCallService} will be able to see calls from self-managed
+     *     {@link ConnectionService}s, and will be able to display an incoming call UI on their
+     *     behalf.</li>
+     * </ul>
      */
     public void onAnswer() {
         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
@@ -2747,7 +2774,27 @@
 
     /**
      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
+     * a request to deflect.
+     */
+    public void onDeflect(Uri address) {}
+
+    /**
+     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
      * a request to reject.
+     * <p>
+     * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+     * the default dialer's {@link InCallService}.
+     * <p>
+     * Although a self-managed {@link ConnectionService} provides its own incoming call UI, the
+     * Telecom framework may request that the call is rejected in the following circumstances:
+     * <ul>
+     *     <li>The user chooses to reject an incoming call via a Bluetooth device.</li>
+     *     <li>A car mode {@link InCallService} is in use which has declared
+     *     {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS} in its manifest.  Such an
+     *     {@link InCallService} will be able to see calls from self-managed
+     *     {@link ConnectionService}s, and will be able to display an incoming call UI on their
+     *     behalf.</li>
+     * </ul>
      */
     public void onReject() {}
 
@@ -2830,9 +2877,10 @@
      * should show its own incoming call user interface.
      * <p>
      * Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
-     * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
-     * user interface to allow the user to choose whether to answer the new incoming call and
-     * disconnect other ongoing calls, or to reject the new incoming call.
+     * regular {@link ConnectionService}, and it is not possible to hold these other calls, the
+     * Telecom framework will display its own incoming call user interface to allow the user to
+     * choose whether to answer the new incoming call and disconnect other ongoing calls, or to
+     * reject the new incoming call.
      * <p>
      * You should trigger the display of the incoming call user interface for your application by
      * showing a {@link Notification} with a full-screen {@link Intent} specified.
@@ -2897,22 +2945,6 @@
      */
     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
 
-    /**
-     * Internal method to set {@link #PROPERTY_IS_RTT}.
-     * @hide
-     */
-    void setRttProperty() {
-        setConnectionProperties(getConnectionProperties() | PROPERTY_IS_RTT);
-    }
-
-    /**
-     * Internal method to un-set {@link #PROPERTY_IS_RTT}.
-     * @hide
-     */
-    void unsetRttProperty() {
-        setConnectionProperties(getConnectionProperties() & (~PROPERTY_IS_RTT));
-    }
-
     static String toLogSafePhoneNumber(String number) {
         // For unknown number, log empty string.
         if (number == null) {
diff --git a/android/telecom/ConnectionRequest.java b/android/telecom/ConnectionRequest.java
index 658b473..b6e6b0e 100644
--- a/android/telecom/ConnectionRequest.java
+++ b/android/telecom/ConnectionRequest.java
@@ -143,6 +143,8 @@
     private final boolean mShouldShowIncomingCallUi;
     private final ParcelFileDescriptor mRttPipeToInCall;
     private final ParcelFileDescriptor mRttPipeFromInCall;
+    // Cached return value of getRttTextStream -- we don't want to wrap it more than once.
+    private Connection.RttTextStream mRttTextStream;
 
     /**
      * @param accountHandle The accountHandle which should be used to place the call.
@@ -312,7 +314,10 @@
      */
     public Connection.RttTextStream getRttTextStream() {
         if (isRequestingRtt()) {
-            return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+            if (mRttTextStream == null) {
+                mRttTextStream = new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+            }
+            return mRttTextStream;
         } else {
             return null;
         }
diff --git a/android/telecom/ConnectionService.java b/android/telecom/ConnectionService.java
index c1040ad..fc32b17 100644
--- a/android/telecom/ConnectionService.java
+++ b/android/telecom/ConnectionService.java
@@ -21,7 +21,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -124,6 +123,7 @@
     private static final String SESSION_ABORT = "CS.ab";
     private static final String SESSION_ANSWER = "CS.an";
     private static final String SESSION_ANSWER_VIDEO = "CS.anV";
+    private static final String SESSION_DEFLECT = "CS.def";
     private static final String SESSION_REJECT = "CS.r";
     private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
     private static final String SESSION_SILENCE = "CS.s";
@@ -143,6 +143,7 @@
     private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
     private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
     private static final String SESSION_START_RTT = "CS.+RTT";
+    private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT";
     private static final String SESSION_STOP_RTT = "CS.-RTT";
     private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
     private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
@@ -181,6 +182,7 @@
     private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
     private static final int MSG_HANDOVER_FAILED = 32;
     private static final int MSG_HANDOVER_COMPLETE = 33;
+    private static final int MSG_DEFLECT = 34;
 
     private static Connection sNullConnection;
 
@@ -353,6 +355,20 @@
         }
 
         @Override
+        public void deflect(String callId, Uri address, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_DEFLECT);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = address;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void reject(String callId, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_REJECT);
             try {
@@ -847,6 +863,17 @@
                     }
                     break;
                 }
+                case MSG_DEFLECT: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT);
+                    try {
+                        deflect((String) args.arg1, (Uri) args.arg2);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_REJECT: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
@@ -1605,6 +1632,11 @@
         findConnectionForAction(callId, "answer").onAnswer();
     }
 
+    private void deflect(String callId, Uri address) {
+        Log.d(this, "deflect %s", callId);
+        findConnectionForAction(callId, "deflect").onDeflect(address);
+    }
+
     private void reject(String callId) {
         Log.d(this, "reject %s", callId);
         findConnectionForAction(callId, "reject").onReject();
@@ -1833,7 +1865,6 @@
         Log.d(this, "stopRtt(%s)", callId);
         if (mConnectionById.containsKey(callId)) {
             findConnectionForAction(callId, "stopRtt").onStopRtt();
-            findConnectionForAction(callId, "stopRtt").unsetRttProperty();
         } else if (mConferenceById.containsKey(callId)) {
             Log.w(this, "stopRtt called on a conference.");
         }
@@ -1975,7 +2006,7 @@
                             null : conference.getVideoProvider().getInterface(),
                     conference.getVideoState(),
                     conference.getConnectTimeMillis(),
-                    conference.getConnectElapsedTime(),
+                    conference.getConnectionStartElapsedRealTime(),
                     conference.getStatusHints(),
                     conference.getExtras());
 
@@ -2188,12 +2219,50 @@
     }
 
     /**
-     * Called by Telecom on the initiating side of the handover to create an instance of a
-     * handover connection.
+     * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
+     * outgoing handover {@link Connection}.
+     * <p>
+     * A call handover is the process where an ongoing call is transferred from one app (i.e.
+     * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
+     * mobile network call in a video calling app.  The mobile network call via the Telephony stack
+     * is referred to as the source of the handover, and the video calling app is referred to as the
+     * destination.
+     * <p>
+     * When considering a handover scenario the <em>initiating</em> device is where a user initiated
+     * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
+     * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
+     * device.
+     * <p>
+     * This method is called on the destination {@link ConnectionService} on <em>initiating</em>
+     * device when the user initiates a handover request from one app to another.  The user request
+     * originates in the {@link InCallService} via
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
+     * <p>
+     * For a full discussion of the handover process and the APIs involved, see
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
+     * <p>
+     * Implementations of this method should return an instance of {@link Connection} which
+     * represents the handover.  If your app does not wish to accept a handover to it at this time,
+     * you can return {@code null}.  The code below shows an example of how this is done.
+     * <pre>
+     * {@code
+     * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle
+     *     fromPhoneAccountHandle, ConnectionRequest request) {
+     *   if (!isHandoverAvailable()) {
+     *       return null;
+     *   }
+     *   MyConnection connection = new MyConnection();
+     *   connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
+     *   connection.setVideoState(request.getVideoState());
+     *   return connection;
+     * }
+     * }
+     * </pre>
+     *
      * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
      *                               ConnectionService which needs to handover the call.
-     * @param request Details about the call which needs to be handover.
-     * @return Connection object corresponding to the handover call.
+     * @param request Details about the call to handover.
+     * @return {@link Connection} instance corresponding to the handover call.
      */
     public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
                                                          ConnectionRequest request) {
@@ -2201,12 +2270,46 @@
     }
 
     /**
-     * Called by Telecom on the receiving side of the handover to request the
-     * {@link ConnectionService} to create an instance of a handover connection.
+     * Called by Telecom to request that a {@link ConnectionService} creates an instance of an
+     * incoming handover {@link Connection}.
+     * <p>
+     * A call handover is the process where an ongoing call is transferred from one app (i.e.
+     * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
+     * mobile network call in a video calling app.  The mobile network call via the Telephony stack
+     * is referred to as the source of the handover, and the video calling app is referred to as the
+     * destination.
+     * <p>
+     * When considering a handover scenario the <em>initiating</em> device is where a user initiated
+     * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
+     * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
+     * device.
+     * <p>
+     * This method is called on the destination app on the <em>receiving</em> device when the
+     * destination app calls {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to
+     * accept an incoming handover from the <em>initiating</em> device.
+     * <p>
+     * For a full discussion of the handover process and the APIs involved, see
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
+     * <p>
+     * Implementations of this method should return an instance of {@link Connection} which
+     * represents the handover.  The code below shows an example of how this is done.
+     * <pre>
+     * {@code
+     * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle
+     *     fromPhoneAccountHandle, ConnectionRequest request) {
+     *   // Given that your app requested to accept the handover, you should not return null here.
+     *   MyConnection connection = new MyConnection();
+     *   connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
+     *   connection.setVideoState(request.getVideoState());
+     *   return connection;
+     * }
+     * }
+     * </pre>
+     *
      * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the
      *                               ConnectionService which needs to handover the call.
      * @param request Details about the call which needs to be handover.
-     * @return {@link Connection} object corresponding to the handover call.
+     * @return {@link Connection} instance corresponding to the handover call.
      */
     public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle,
                                                          ConnectionRequest request) {
@@ -2216,11 +2319,15 @@
     /**
      * Called by Telecom in response to a {@code TelecomManager#acceptHandover()}
      * invocation which failed.
-     * @param request Details about the call which needs to be handover.
-     * @param error Reason for handover failure as defined in
-     *              {@link android.telecom.Call.Callback#HANDOVER_FAILURE_DEST_INVALID_PERM}
+     * <p>
+     * For a full discussion of the handover process and the APIs involved, see
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}
+     *
+     * @param request Details about the call which failed to handover.
+     * @param error Reason for handover failure.  Will be one of the
      */
-    public void onHandoverFailed(ConnectionRequest request, int error) {
+    public void onHandoverFailed(ConnectionRequest request,
+            @Call.Callback.HandoverFailureErrors int error) {
         return;
     }
 
diff --git a/android/telecom/DisconnectCause.java b/android/telecom/DisconnectCause.java
index dcf5c27..1de67a5 100644
--- a/android/telecom/DisconnectCause.java
+++ b/android/telecom/DisconnectCause.java
@@ -33,47 +33,48 @@
 public final class DisconnectCause implements Parcelable {
 
     /** Disconnected because of an unknown or unspecified reason. */
-    public static final int UNKNOWN = 0;
+    public static final int UNKNOWN = TelecomProtoEnums.UNKNOWN; // = 0
     /** Disconnected because there was an error, such as a problem with the network. */
-    public static final int ERROR = 1;
+    public static final int ERROR = TelecomProtoEnums.ERROR; // = 1
     /** Disconnected because of a local user-initiated action, such as hanging up. */
-    public static final int LOCAL = 2;
+    public static final int LOCAL = TelecomProtoEnums.LOCAL; // = 2
     /**
      * Disconnected because of a remote user-initiated action, such as the other party hanging up
      * up.
      */
-    public static final int REMOTE = 3;
+    public static final int REMOTE = TelecomProtoEnums.REMOTE; // = 3
     /** Disconnected because it has been canceled. */
-    public static final int CANCELED = 4;
+    public static final int CANCELED = TelecomProtoEnums.CANCELED; // = 4
     /** Disconnected because there was no response to an incoming call. */
-    public static final int MISSED = 5;
+    public static final int MISSED = TelecomProtoEnums.MISSED; // = 5
     /** Disconnected because the user rejected an incoming call. */
-    public static final int REJECTED = 6;
+    public static final int REJECTED = TelecomProtoEnums.REJECTED; // = 6
     /** Disconnected because the other party was busy. */
-    public static final int BUSY = 7;
+    public static final int BUSY = TelecomProtoEnums.BUSY; // = 7
     /**
      * Disconnected because of a restriction on placing the call, such as dialing in airplane
      * mode.
      */
-    public static final int RESTRICTED = 8;
+    public static final int RESTRICTED = TelecomProtoEnums.RESTRICTED; // = 8
     /** Disconnected for reason not described by other disconnect codes. */
-    public static final int OTHER = 9;
+    public static final int OTHER = TelecomProtoEnums.OTHER; // = 9
     /**
      * Disconnected because the connection manager did not support the call. The call will be tried
      * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
      */
-    public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
+    public static final int CONNECTION_MANAGER_NOT_SUPPORTED =
+            TelecomProtoEnums.CONNECTION_MANAGER_NOT_SUPPORTED; // = 10
 
     /**
      * Disconnected because the user did not locally answer the incoming call, but it was answered
      * on another device where the call was ringing.
      */
-    public static final int ANSWERED_ELSEWHERE = 11;
+    public static final int ANSWERED_ELSEWHERE = TelecomProtoEnums.ANSWERED_ELSEWHERE; // = 11
 
     /**
      * Disconnected because the call was pulled from the current device to another device.
      */
-    public static final int CALL_PULLED = 12;
+    public static final int CALL_PULLED = TelecomProtoEnums.CALL_PULLED; // = 12
 
     /**
      * Reason code (returned via {@link #getReason()}) which indicates that a call could not be
diff --git a/android/telecom/InCallAdapter.java b/android/telecom/InCallAdapter.java
index 658685f..8678e33 100644
--- a/android/telecom/InCallAdapter.java
+++ b/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.net.Uri;
 import android.bluetooth.BluetoothDevice;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -61,6 +62,19 @@
     }
 
     /**
+     * Instructs Telecom to deflect the specified call.
+     *
+     * @param callId The identifier of the call to deflect.
+     * @param address The address to deflect.
+     */
+    public void deflectCall(String callId, Uri address) {
+        try {
+            mAdapter.deflectCall(callId, address);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Instructs Telecom to reject the specified call.
      *
      * @param callId The identifier of the call to reject.
diff --git a/android/telecom/InCallService.java b/android/telecom/InCallService.java
index fcf04c9..af65c65 100644
--- a/android/telecom/InCallService.java
+++ b/android/telecom/InCallService.java
@@ -60,6 +60,26 @@
  * </service>
  * }
  * </pre>
+ * <p>
+ * In addition to implementing the {@link InCallService} API, you must also declare an activity in
+ * your manifest which handles the {@link Intent#ACTION_DIAL} intent.  The example below illustrates
+ * how this is done:
+ * <pre>
+ * {@code
+ * <activity android:name="your.package.YourDialerActivity"
+ *           android:label="@string/yourDialerActivityLabel">
+ *      <intent-filter>
+ *           <action android:name="android.intent.action.DIAL" />
+ *           <category android:name="android.intent.category.DEFAULT" />
+ *      </intent-filter>
+ * </activity>
+ * }
+ * </pre>
+ * <p>
+ * When a user installs your application and runs it for the first time, you should prompt the user
+ * to see if they would like your application to be the new default phone app.  See the
+ * {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
+ * how to do this.
  */
 public abstract class InCallService extends Service {
 
diff --git a/android/telecom/Logging/EventManager.java b/android/telecom/Logging/EventManager.java
index 4fc3385..2bda648 100644
--- a/android/telecom/Logging/EventManager.java
+++ b/android/telecom/Logging/EventManager.java
@@ -24,21 +24,20 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.IllegalFormatException;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.TimeZone;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.stream.Collectors;
 
 /**
  * A utility class that provides the ability to define Events that a subsystem deems important, and
@@ -53,7 +52,8 @@
     public static final String TAG = "Logging.Events";
     @VisibleForTesting
     public static final int DEFAULT_EVENTS_TO_CACHE = 10;  // Arbitrarily chosen.
-    private final DateFormat sDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
+    public static final DateTimeFormatter DATE_TIME_FORMATTER =
+            DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
 
     public interface Loggable {
         /**
@@ -131,11 +131,17 @@
         public String sessionId;
         public long time;
         public Object data;
+        // String storing the date for display. This will be computed at the time/timezone when
+        // the event is recorded.
+        public final String timestampString;
 
         public Event(String eventId, String sessionId, long time, Object data) {
             this.eventId = eventId;
             this.sessionId = sessionId;
             this.time = time;
+            timestampString =
+                    ZonedDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault())
+                    .format(DATE_TIME_FORMATTER);
             this.data = data;
         }
     }
@@ -228,7 +234,7 @@
 
             pw.increaseIndent();
             for (Event event : mEvents) {
-                pw.print(sDateFormat.format(new Date(event.time)));
+                pw.print(event.timestampString);
                 pw.print(" - ");
                 pw.print(event.eventId);
                 if (event.data != null) {
@@ -269,7 +275,6 @@
 
     public EventManager(@NonNull SessionManager.ISessionIdQueryHandler l) {
         mSessionIdHandler = l;
-        sDateFormat.setTimeZone(TimeZone.getDefault());
     }
 
     public void event(Loggable recordEntry, String event, Object data) {
@@ -329,15 +334,15 @@
             }
         }
 
-        // Sort by event time.
-        Comparator<Pair<Loggable, Event>> byEventTime = (e1, e2) -> {
-          return Long.compare(e1.second.time, e2.second.time);
-        };
+        // Sort by event time. This might result in out-of-order seeming events if the timezone
+        // changes somewhere in the middle.
+        Comparator<Pair<Loggable, Event>> byEventTime =
+                Comparator.comparingLong(e -> e.second.time);
         events.sort(byEventTime);
 
         pw.increaseIndent();
         for (Pair<Loggable, Event> event : events) {
-            pw.print(sDateFormat.format(new Date(event.second.time)));
+            pw.print(event.second.timestampString);
             pw.print(",");
             pw.print(event.first.getId());
             pw.print(",");
diff --git a/android/telecom/PhoneAccount.java b/android/telecom/PhoneAccount.java
index fcfc593..95eb14a 100644
--- a/android/telecom/PhoneAccount.java
+++ b/android/telecom/PhoneAccount.java
@@ -134,6 +134,25 @@
             "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
 
     /**
+     * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+     * indicates whether calls for a {@link PhoneAccount} should generate a "call recording tone"
+     * when the user is recording audio on the device.
+     * <p>
+     * The call recording tone is played over the telephony audio stream so that the remote party
+     * has an audible indication that it is possible their call is being recorded using a call
+     * recording app on the device.
+     * <p>
+     * This extra only has an effect for calls placed via Telephony (e.g.
+     * {@link #CAPABILITY_SIM_SUBSCRIPTION}).
+     * <p>
+     * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is
+     * in progress.
+     * @hide
+     */
+    public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
+            "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+
+    /**
      * Flag indicating that this {@code PhoneAccount} can act as a connection manager for
      * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
      * will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/android/telecom/RemoteConnectionService.java b/android/telecom/RemoteConnectionService.java
index 59ce590..bb4b483 100644
--- a/android/telecom/RemoteConnectionService.java
+++ b/android/telecom/RemoteConnectionService.java
@@ -93,6 +93,10 @@
                     // failure on the providing end, so immediately mark it destroyed
                     connection.setDestroyed();
                 }
+                connection.setStatusHints(parcel.getStatusHints());
+                connection.setIsVoipAudioMode(parcel.getIsVoipAudioMode());
+                connection.setRingbackRequested(parcel.isRingbackRequested());
+                connection.putExtras(parcel.getExtras());
             }
         }
 
diff --git a/android/telecom/TelecomManager.java b/android/telecom/TelecomManager.java
index 1fe5db5..72c67d3 100644
--- a/android/telecom/TelecomManager.java
+++ b/android/telecom/TelecomManager.java
@@ -111,12 +111,6 @@
             "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
 
     /**
-     * The {@link android.content.Intent} action used to show the assisted dialing settings.
-     */
-    public static final String ACTION_SHOW_ASSISTED_DIALING_SETTINGS =
-            "android.telecom.action.SHOW_ASSISTED_DIALING_SETTINGS";
-
-    /**
      * The {@link android.content.Intent} action used to show the settings page used to configure
      * {@link PhoneAccount} preferences.
      */
@@ -619,17 +613,12 @@
     /**
      * The boolean indicated by this extra controls whether or not a call is eligible to undergo
      * assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}.
+     * @hide
      */
     public static final String EXTRA_USE_ASSISTED_DIALING =
             "android.telecom.extra.USE_ASSISTED_DIALING";
 
     /**
-     * The bundle indicated by this extra store information related to the assisted dialing action.
-     */
-    public static final String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO =
-            "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
-
-    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
@@ -1320,7 +1309,7 @@
     public boolean endCall() {
         try {
             if (isServiceConnected()) {
-                return getTelecomService().endCall();
+                return getTelecomService().endCall(mContext.getPackageName());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelecomService#endCall", e);
@@ -1805,8 +1794,25 @@
     }
 
     /**
-     * Called from the recipient side of a handover to indicate a desire to accept the handover
-     * of an ongoing call to another {@link ConnectionService} identified by
+     * Called by an app to indicate that it wishes to accept the handover of an ongoing call to a
+     * {@link PhoneAccountHandle} it defines.
+     * <p>
+     * A call handover is the process where an ongoing call is transferred from one app (i.e.
+     * {@link ConnectionService} to another app.  The user could, for example, choose to continue a
+     * mobile network call in a video calling app.  The mobile network call via the Telephony stack
+     * is referred to as the source of the handover, and the video calling app is referred to as the
+     * destination.
+     * <p>
+     * When considering a handover scenario the <em>initiating</em> device is where a user initiated
+     * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo(
+     * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em>
+     * device.
+     * <p>
+     * For a full discussion of the handover process and the APIs involved, see
+     * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}.
+     * <p>
+     * This method is called from the <em>receiving</em> side of a handover to indicate a desire to
+     * accept the handover of an ongoing call to another {@link ConnectionService} identified by
      * {@link PhoneAccountHandle} destAcct. For managed {@link ConnectionService}s, the specified
      * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
      * the user must have enabled the corresponding {@link PhoneAccount}.  This can be checked using
@@ -1830,7 +1836,8 @@
      * @param videoState Video state after the handover.
      * @param destAcct The {@link PhoneAccountHandle} registered to the calling package.
      */
-    public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) {
+    public void acceptHandover(Uri srcAddr, @VideoProfile.VideoState int videoState,
+            PhoneAccountHandle destAcct) {
         try {
             if (isServiceConnected()) {
                 getTelecomService().acceptHandover(srcAddr, videoState, destAcct);
diff --git a/android/telecom/TransformationInfo.java b/android/telecom/TransformationInfo.java
deleted file mode 100644
index 3e848c6..0000000
--- a/android/telecom/TransformationInfo.java
+++ /dev/null
@@ -1,127 +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.telecom;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-
-/**
- * A container class to hold information related to the Assisted Dialing operation. All member
- * variables must be set when constructing a new instance of this class.
- */
-public final class TransformationInfo implements Parcelable {
-    private String mOriginalNumber;
-    private String mTransformedNumber;
-    private String mUserHomeCountryCode;
-    private String mUserRoamingCountryCode;
-    private int mTransformedNumberCountryCallingCode;
-
-    public TransformationInfo(String originalNumber,
-                              String transformedNumber,
-                              String userHomeCountryCode,
-                              String userRoamingCountryCode,
-                              int transformedNumberCountryCallingCode) {
-        String missing = "";
-        if (originalNumber == null) {
-            missing += " mOriginalNumber";
-        }
-        if (transformedNumber == null) {
-            missing += " mTransformedNumber";
-        }
-        if (userHomeCountryCode == null) {
-            missing += " mUserHomeCountryCode";
-        }
-        if (userRoamingCountryCode == null) {
-            missing += " mUserRoamingCountryCode";
-        }
-
-        if (!missing.isEmpty()) {
-            throw new IllegalStateException("Missing required properties:" + missing);
-        }
-        this.mOriginalNumber = originalNumber;
-        this.mTransformedNumber = transformedNumber;
-        this.mUserHomeCountryCode = userHomeCountryCode;
-        this.mUserRoamingCountryCode = userRoamingCountryCode;
-        this.mTransformedNumberCountryCallingCode = transformedNumberCountryCallingCode;
-    }
-
-    public int describeContents() {
-        return 0;
-    }
-
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mOriginalNumber);
-        out.writeString(mTransformedNumber);
-        out.writeString(mUserHomeCountryCode);
-        out.writeString(mUserRoamingCountryCode);
-        out.writeInt(mTransformedNumberCountryCallingCode);
-    }
-
-    public static final Parcelable.Creator<TransformationInfo> CREATOR
-            = new Parcelable.Creator<TransformationInfo>() {
-        public TransformationInfo createFromParcel(Parcel in) {
-            return new TransformationInfo(in);
-        }
-
-        public TransformationInfo[] newArray(int size) {
-            return new TransformationInfo[size];
-        }
-    };
-
-    private TransformationInfo(Parcel in) {
-        mOriginalNumber = in.readString();
-        mTransformedNumber = in.readString();
-        mUserHomeCountryCode = in.readString();
-        mUserRoamingCountryCode = in.readString();
-        mTransformedNumberCountryCallingCode = in.readInt();
-    }
-
-    /**
-     * The original number that underwent Assisted Dialing.
-     */
-    public String getOriginalNumber() {
-        return mOriginalNumber;
-    }
-
-    /**
-     * The number after it underwent Assisted Dialing.
-     */
-    public String getTransformedNumber() {
-        return mTransformedNumber;
-    }
-
-    /**
-     * The user's home country code that was used when attempting to transform the number.
-     */
-    public String getUserHomeCountryCode() {
-        return mUserHomeCountryCode;
-    }
-
-    /**
-     * The users's roaming country code that was used when attempting to transform the number.
-     */
-    public String getUserRoamingCountryCode() {
-        return mUserRoamingCountryCode;
-    }
-
-    /**
-     * The country calling code that was used in the transformation.
-     */
-    public int getTransformedNumberCountryCallingCode() {
-        return mTransformedNumberCountryCallingCode;
-    }
-}
diff --git a/android/telecom/VideoProfile.java b/android/telecom/VideoProfile.java
index e0e3a08..90ed36f 100644
--- a/android/telecom/VideoProfile.java
+++ b/android/telecom/VideoProfile.java
@@ -62,6 +62,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(
             flag = true,
+            prefix = { "STATE_" },
             value = {STATE_AUDIO_ONLY, STATE_TX_ENABLED, STATE_RX_ENABLED, STATE_BIDIRECTIONAL,
                     STATE_PAUSED})
     public @interface VideoState {}
diff --git a/android/telephony/AccessNetworkConstants.java b/android/telephony/AccessNetworkConstants.java
index 7cd1612..cac9f2b 100644
--- a/android/telephony/AccessNetworkConstants.java
+++ b/android/telephony/AccessNetworkConstants.java
@@ -30,6 +30,9 @@
         public static final int EUTRAN = 3;
         public static final int CDMA2000 = 4;
         public static final int IWLAN = 5;
+
+        /** @hide */
+        private AccessNetworkType() {};
     }
 
     /**
@@ -42,6 +45,9 @@
         public static final int WWAN = 1;
         /** Wireless Local Area Networks (i.e. Wifi) */
         public static final int WLAN = 2;
+
+        /** @hide */
+        private TransportType() {};
     }
 
     /**
@@ -63,6 +69,9 @@
         public static final int BAND_DCS1800 = 12;
         public static final int BAND_PCS1900 = 13;
         public static final int BAND_ER900 = 14;
+
+        /** @hide */
+        private GeranBand() {};
     }
 
     /**
@@ -92,6 +101,9 @@
         /** band 23, 24 are reserved */
         public static final int BAND_25 = 25;
         public static final int BAND_26 = 26;
+
+        /** @hide */
+        private UtranBand() {};
     }
 
     /**
@@ -147,6 +159,9 @@
         public static final int BAND_66 = 66;
         public static final int BAND_68 = 68;
         public static final int BAND_70 = 70;
+
+        /** @hide */
+        private EutranBand() {};
     }
 
     /**
@@ -179,5 +194,11 @@
         public static final int BAND_19 = 20;
         public static final int BAND_20 = 21;
         public static final int BAND_21 = 22;
+
+        /** @hide */
+        private CdmaBands() {};
     }
+
+    /** @hide */
+    private AccessNetworkConstants() {};
 }
diff --git a/android/telephony/AccessNetworkUtils.java b/android/telephony/AccessNetworkUtils.java
new file mode 100644
index 0000000..5d2c225
--- /dev/null
+++ b/android/telephony/AccessNetworkUtils.java
@@ -0,0 +1,167 @@
+package android.telephony;
+
+import static android.telephony.ServiceState.DUPLEX_MODE_FDD;
+import static android.telephony.ServiceState.DUPLEX_MODE_TDD;
+import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN;
+
+import android.telephony.AccessNetworkConstants.EutranBand;
+import android.telephony.ServiceState.DuplexMode;
+
+
+/**
+ * Utilities to map between radio constants.
+ *
+ * @hide
+ */
+public class AccessNetworkUtils {
+
+    // do not instantiate
+    private AccessNetworkUtils() {}
+
+    public static final int INVALID_BAND = -1;
+
+    /**
+     * Gets the duplex mode for the given EUTRAN operating band.
+     *
+     * <p>See 3GPP 36.101 sec 5.5-1 for calculation
+     *
+     * @param band The EUTRAN band number
+     * @return The duplex mode of the given EUTRAN band
+     */
+    @DuplexMode
+    public static int getDuplexModeForEutranBand(int band) {
+        if (band == INVALID_BAND) {
+            return DUPLEX_MODE_UNKNOWN;
+        }
+
+        if (band >= EutranBand.BAND_68) {
+            return DUPLEX_MODE_UNKNOWN;
+        } else if (band >= EutranBand.BAND_65) {
+            return DUPLEX_MODE_FDD;
+        } else if (band >= EutranBand.BAND_47) {
+            return DUPLEX_MODE_UNKNOWN;
+        } else if (band >= EutranBand.BAND_33) {
+            return DUPLEX_MODE_TDD;
+        } else if (band >= EutranBand.BAND_1) {
+            return DUPLEX_MODE_FDD;
+        }
+
+        return DUPLEX_MODE_UNKNOWN;
+    }
+
+    /**
+     * Gets the EUTRAN Operating band for a given downlink EARFCN.
+     *
+     * <p>See 3GPP 36.101 sec 5.7.3-1 for calculation.
+     *
+     * @param earfcn The downlink EARFCN
+     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+     */
+    public static int getOperatingBandForEarfcn(int earfcn) {
+        if (earfcn > 67535) {
+            return INVALID_BAND;
+        } else if (earfcn >= 67366) {
+            return INVALID_BAND; // band 67 only for CarrierAgg
+        } else if (earfcn >= 66436) {
+            return EutranBand.BAND_66;
+        } else if (earfcn >= 65536) {
+            return EutranBand.BAND_65;
+        } else if (earfcn > 54339) {
+            return INVALID_BAND;
+        } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) {
+            return EutranBand.BAND_46;
+        } else if (earfcn >= 46590) {
+            return EutranBand.BAND_45;
+        } else if (earfcn >= 45590) {
+            return EutranBand.BAND_44;
+        } else if (earfcn >= 43590) {
+            return EutranBand.BAND_43;
+        } else if (earfcn >= 41590) {
+            return EutranBand.BAND_42;
+        } else if (earfcn >= 39650) {
+            return EutranBand.BAND_41;
+        } else if (earfcn >= 38650) {
+            return EutranBand.BAND_40;
+        } else if (earfcn >= 38250) {
+            return EutranBand.BAND_39;
+        } else if (earfcn >= 37750) {
+            return EutranBand.BAND_38;
+        } else if (earfcn >= 37550) {
+            return EutranBand.BAND_37;
+        } else if (earfcn >= 36950) {
+            return EutranBand.BAND_36;
+        } else if (earfcn >= 36350) {
+            return EutranBand.BAND_35;
+        } else if (earfcn >= 36200) {
+            return EutranBand.BAND_34;
+        } else if (earfcn >= 36000) {
+            return EutranBand.BAND_33;
+        } else if (earfcn > 10359) {
+            return INVALID_BAND;
+        } else if (earfcn >= 9920) {
+            return INVALID_BAND; // band 32 only for CarrierAgg
+        } else if (earfcn >= 9870) {
+            return EutranBand.BAND_31;
+        } else if (earfcn >= 9770) {
+            return EutranBand.BAND_30;
+        } else if (earfcn >= 9660) {
+            return INVALID_BAND; // band 29 only for CarrierAgg
+        } else if (earfcn >= 9210) {
+            return EutranBand.BAND_28;
+        } else if (earfcn >= 9040) {
+            return EutranBand.BAND_27;
+        } else if (earfcn >= 8690) {
+            return EutranBand.BAND_26;
+        } else if (earfcn >= 8040) {
+            return EutranBand.BAND_25;
+        } else if (earfcn >= 7700) {
+            return EutranBand.BAND_24;
+        } else if (earfcn >= 7500) {
+            return EutranBand.BAND_23;
+        } else if (earfcn >= 6600) {
+            return EutranBand.BAND_22;
+        } else if (earfcn >= 6450) {
+            return EutranBand.BAND_21;
+        } else if (earfcn >= 6150) {
+            return EutranBand.BAND_20;
+        } else if (earfcn >= 6000) {
+            return EutranBand.BAND_19;
+        } else if (earfcn >= 5850) {
+            return EutranBand.BAND_18;
+        } else if (earfcn >= 5730) {
+            return EutranBand.BAND_17;
+        } else if (earfcn > 5379) {
+            return INVALID_BAND;
+        } else if (earfcn >= 5280) {
+            return EutranBand.BAND_14;
+        } else if (earfcn >= 5180) {
+            return EutranBand.BAND_13;
+        } else if (earfcn >= 5010) {
+            return EutranBand.BAND_12;
+        } else if (earfcn >= 4750) {
+            return EutranBand.BAND_11;
+        } else if (earfcn >= 4150) {
+            return EutranBand.BAND_10;
+        } else if (earfcn >= 3800) {
+            return EutranBand.BAND_9;
+        } else if (earfcn >= 3450) {
+            return EutranBand.BAND_8;
+        } else if (earfcn >= 2750) {
+            return EutranBand.BAND_7;
+        } else if (earfcn >= 2650) {
+            return EutranBand.BAND_6;
+        } else if (earfcn >= 2400) {
+            return EutranBand.BAND_5;
+        } else if (earfcn >= 1950) {
+            return EutranBand.BAND_4;
+        } else if (earfcn >= 1200) {
+            return EutranBand.BAND_3;
+        } else if (earfcn >= 600) {
+            return EutranBand.BAND_2;
+        } else if (earfcn >= 0) {
+            return EutranBand.BAND_1;
+        }
+
+        return INVALID_BAND;
+    }
+}
diff --git a/android/telephony/CarrierConfigManager.java b/android/telephony/CarrierConfigManager.java
index 91d86c6..4683161 100644
--- a/android/telephony/CarrierConfigManager.java
+++ b/android/telephony/CarrierConfigManager.java
@@ -27,8 +27,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.carrier.CarrierService;
+import android.telephony.ims.ImsReasonInfo;
 
-import com.android.ims.ImsReasonInfo;
 import com.android.internal.telephony.ICarrierConfigLoader;
 
 /**
@@ -77,6 +77,14 @@
     public static final String
             KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
 
+   /**
+    * Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
+    * true means visible. false means gone.
+    * @hide
+    */
+    public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
+            "call_barring_visibility_bool";
+
     /**
      * Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
      * events from the Sim.
@@ -146,6 +154,15 @@
     public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
 
     /**
+     * Determines if the carrier requires that a tone be played to the remote party when an app is
+     * recording audio during a call (e.g. using a call recording app).
+     * <p>
+     * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
+     * in order to work.
+     * @hide
+     */
+    public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
+    /**
      * Determines if the carrier requires converting the destination number before sending out an
      * SMS. Certain networks and numbering plans require different formats.
      */
@@ -380,6 +397,15 @@
     public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
 
     /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number for roaming network.
+     * When empty string, no default voicemail number is specified for roaming network.
+     * @hide
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_STRING =
+            "default_vm_number_roaming_string";
+
+    /**
      * Flag that specifies to use the user's own phone number as the voicemail number when there is
      * no pre-loaded voicemail number on the SIM card.
      * <p>
@@ -503,6 +529,20 @@
     public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL
             = "carrier_volte_override_wfc_provisioning_bool";
 
+    /**
+     * Override the device's configuration for the cellular data service to use for this SIM card.
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
+            = "carrier_data_service_wwan_package_override_string";
+
+    /**
+     * Override the device's configuration for the IWLAN data service to use for this SIM card.
+     * @hide
+     */
+    public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
+            = "carrier_data_service_wlan_package_override_string";
+
     /** Flag specifying whether VoLTE TTY is supported. */
     public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
             = "carrier_volte_tty_supported_bool";
@@ -920,6 +960,8 @@
      * If user has explicitly disabled some packages in the list, won't re-enable.
      * Other carrier specific apps which are not in this list may be disabled for current carrier,
      * and only be re-enabled when this config for another carrier includes it.
+     *
+     * @hide
      */
     public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
 
@@ -969,8 +1011,13 @@
             "wfc_emergency_address_carrier_app_string";
 
     /**
-     * Boolean to decide whether to use #KEY_CARRIER_NAME_STRING from CarrierConfig app.
-     * @hide
+     * Unconditionally override the carrier name string using #KEY_CARRIER_NAME_STRING.
+     *
+     * If true, then the carrier display name will be #KEY_CARRIER_NAME_STRING, unconditionally.
+     *
+     * <p>If false, then the override will be performed conditionally and the
+     * #KEY_CARRIER_NAME_STRING will have the lowest-precedence; it will only be used in the event
+     * that the name string would otherwise be empty, allowing it to serve as a last-resort.
      */
     public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool";
 
@@ -978,7 +1025,6 @@
      * String to identify carrier name in CarrierConfig app. This string overrides SPN if
      * #KEY_CARRIER_NAME_OVERRIDE_BOOL is true; otherwise, it will be used if its value is provided
      * and SPN is unavailable
-     * @hide
      */
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
 
@@ -1250,13 +1296,38 @@
 
     /**
      * The duration in seconds that platform call and message blocking is disabled after the user
-     * contacts emergency services. Platform considers values in the range 0 to 604800 (one week) as
-     * valid. See {@link android.provider.BlockedNumberContract#isBlocked(Context, String)}).
+     * contacts emergency services. Platform considers values for below cases:
+     *  1) 0 <= VALUE <= 604800(one week): the value will be used as the duration directly.
+     *  2) VALUE > 604800(one week): will use the default value as duration instead.
+     *  3) VALUE < 0: block will be disabled forever until user re-eanble block manually,
+     *     the suggested value to disable forever is -1.
+     * See {@code android.provider.BlockedNumberContract#notifyEmergencyContact(Context)}
+     * See {@code android.provider.BlockedNumberContract#isBlocked(Context, String)}.
      */
     public static final String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT =
             "duration_blocking_disabled_after_emergency_int";
 
     /**
+     * Determines whether to enable enhanced call blocking feature on the device.
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE
+     * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN
+     *
+     * <p>
+     * 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml
+     *    and vendor.xml.
+     * <p>
+     * 2. For Dual SIM(DS) device, it should be customized in vendor.xml, since call blocking
+     *    function is used regardless of SIM.
+     * <p>
+     * If {@code true} enable enhanced call blocking feature on the device, {@code false} otherwise.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL =
+            "support_enhanced_call_blocking_bool";
+
+    /**
      * For carriers which require an empty flash to be sent before sending the normal 3-way calling
      * flash, the duration in milliseconds of the empty flash to send.  When {@code 0}, no empty
      * flash is sent.
@@ -1345,6 +1416,12 @@
      */
     public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
 
+    /**
+     * Flag indicating whether the carrier supports call deflection for an incoming IMS call.
+     * @hide
+     */
+    public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+            "carrier_allow_deflect_ims_call_bool";
 
     /**
      * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
@@ -1381,6 +1458,14 @@
     public static final String KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO = "video_calls_can_be_hd_audio";
 
     /**
+     * When true, indicates that the HD audio icon in the in-call screen should be shown for
+     * GSM/CDMA calls.
+     * @hide
+     */
+    public static final String KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO =
+            "gsm_cdma_calls_can_be_hd_audio";
+
+    /**
      * Whether system apps are allowed to use fallback if carrier video call is not available.
      * Defaults to {@code true}.
      *
@@ -1390,7 +1475,7 @@
             "allow_video_calling_fallback_bool";
 
     /**
-     * Defines operator-specific {@link com.android.ims.ImsReasonInfo} mappings.
+     * Defines operator-specific {@link ImsReasonInfo} mappings.
      *
      * Format: "ORIGINAL_CODE|MESSAGE|NEW_CODE"
      * Where {@code ORIGINAL_CODE} corresponds to a {@link ImsReasonInfo#getCode()} code,
@@ -1530,6 +1615,14 @@
             "notify_international_call_on_wfc_bool";
 
     /**
+     * Flag specifying whether to show an alert dialog for video call charges.
+     * By default this value is {@code false}.
+     * @hide
+     */
+    public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL =
+            "show_video_call_charges_alert_dialog_bool";
+
+    /**
      * An array containing custom call forwarding number prefixes that will be blocked while the
      * device is reporting that it is roaming. By default, there are no custom call
      * forwarding prefixes and none of these numbers will be filtered. If one or more entries are
@@ -1659,13 +1752,6 @@
             "roaming_operator_string_array";
 
     /**
-     * Controls whether Assisted Dialing is enabled and the preference is shown. This feature
-     * transforms numbers when the user is roaming.
-     */
-    public static final String KEY_ASSISTED_DIALING_ENABLED_BOOL =
-            "assisted_dialing_enabled_bool";
-
-    /**
      * URL from which the proto containing the public key of the Carrier used for
      * IMSI encryption will be downloaded.
      * @hide
@@ -1777,22 +1863,74 @@
             "check_pricing_with_carrier_data_roaming_bool";
 
     /**
-     * List of thresholds of RSRP for determining the display level of LTE signal bar.
+     * A list of 4 LTE RSRP thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -140 and -44, as explained in
+     * TS 136.133 9.1.4 - RSRP Measurement Report Mapping.
+     * <p>
+     * See SignalStrength#MAX_LTE_RSRP and SignalStrength#MIN_LTE_RSRP. Any signal level outside
+     * these boundaries is considered invalid.
      * @hide
      */
     public static final String KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY =
             "lte_rsrp_thresholds_int_array";
 
+    /**
+     * Decides when clients try to bind to iwlan network service, which package name will
+     * the binding intent go to.
+     * @hide
+     */
+    public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING
+             = "carrier_network_service_wlan_package_override_string";
+
+    /**
+     * Decides when clients try to bind to wwan (cellular) network service, which package name will
+     * the binding intent go to.
+     * @hide
+     */
+    public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING
+            = "carrier_network_service_wwan_package_override_string";
+
+    /**
+     * A list of 4 LTE RSCP thresholds above which a signal level is considered POOR,
+     * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
+     *
+     * Note that the min and max thresholds are fixed at -120 and -24, as set in 3GPP TS 27.007
+     * section 8.69.
+     * <p>
+     * See SignalStrength#MAX_WCDMA_RSCP and SignalStrength#MIN_WDCMA_RSCP. Any signal level outside
+     * these boundaries is considered invalid.
+     * @hide
+     */
+    public static final String KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY =
+            "wcdma_rscp_thresholds_int_array";
+
+    /**
+     * The default measurement to use for signal strength reporting. If this is not specified, the
+     * RSSI is used.
+     * <p>
+     * e.g.) To use RSCP by default, set the value to "rscp". The signal strength level will
+     * then be determined by #KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY
+     * <p>
+     * Currently this only supports the value "rscp"
+     * @hide
+     */
+    public static final String KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING =
+            "wcdma_default_signal_strength_measurement_string";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
     static {
         sDefaults = new PersistableBundle();
         sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
         sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
         sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_LOCAL_DTMF_TONES_BOOL, true);
+        sDefaults.putBoolean(KEY_PLAY_CALL_RECORDING_TONE_BOOL, false);
         sDefaults.putBoolean(KEY_APN_EXPAND_BOOL, true);
         sDefaults.putBoolean(KEY_AUTO_RETRY_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
@@ -1802,6 +1940,7 @@
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
         sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_ROAMING_STRING, "");
         sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
         sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
         sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
@@ -1815,11 +1954,15 @@
         sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
-        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING, "");
+        sDefaults.putString(KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
@@ -1833,6 +1976,7 @@
         sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
 
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+        sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
         sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
         sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
         sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
@@ -1945,6 +2089,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
         sDefaults.putBoolean(KEY_SKIP_CF_FAIL_TO_DISABLE_DIALOG_BOOL, false);
+        sDefaults.putBoolean(KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL, false);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -2033,6 +2178,7 @@
         sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
         sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
+        sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
         sDefaults.putBoolean(KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL, true);
 
         sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
@@ -2050,6 +2196,7 @@
         sDefaults.putBoolean(KEY_DISPLAY_VOICEMAIL_NUMBER_AS_DEFAULT_CALL_FORWARDING_NUMBER_BOOL,
                 false);
         sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL, false);
         sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
                 null);
         sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
@@ -2062,7 +2209,6 @@
                 false);
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
-        sDefaults.putBoolean(KEY_ASSISTED_DIALING_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
@@ -2075,19 +2221,27 @@
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
                 new int[] {
-                        -140, /* SIGNAL_STRENGTH_NONE_OR_UNKNOWN */
                         -128, /* SIGNAL_STRENGTH_POOR */
                         -118, /* SIGNAL_STRENGTH_MODERATE */
                         -108, /* SIGNAL_STRENGTH_GOOD */
                         -98,  /* SIGNAL_STRENGTH_GREAT */
-                        -44
                 });
+        sDefaults.putIntArray(KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                        -115,  /* SIGNAL_STRENGTH_POOR */
+                        -105, /* SIGNAL_STRENGTH_MODERATE */
+                        -95, /* SIGNAL_STRENGTH_GOOD */
+                        -85  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "");
     }
 
     /**
      * Gets the configuration values for a particular subscription, which is associated with a
      * specific SIM card. If an invalid subId is used, the returned config will contain default
-     * values.
+     * values. After using this method to get the configuration bundle,
+     * {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
+     * any carrier specific configuration has been applied.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2114,7 +2268,9 @@
     }
 
     /**
-     * Gets the configuration values for the default subscription.
+     * Gets the configuration values for the default subscription. After using this method to get
+     * the configuration bundle, {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be
+     * called to confirm whether any carrier specific configuration has been applied.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2143,6 +2299,9 @@
      * <p>
      * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
      * use this method to confirm whether any carrier specific configuration has been applied.
+     * Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
+     * still needs to get the current configuration, it must use this method to verify whether the
+     * configuration is default or carrier overridden.
      * </p>
      *
      * @param bundle the configuration bundle to be checked.
diff --git a/android/telephony/CellIdentity.java b/android/telephony/CellIdentity.java
index e092d52..890a6ea 100644
--- a/android/telephony/CellIdentity.java
+++ b/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * CellIdentity represents the identity of a unique cell. This is the base class for
@@ -68,6 +71,9 @@
      */
     public static final int TYPE_TDSCDMA        = 5;
 
+    /** @hide */
+    public static final int INVALID_CHANNEL_NUMBER = -1;
+
     // Log tag
     /** @hide */
     protected final String mTag;
@@ -81,8 +87,16 @@
     /** @hide */
     protected final String mMncStr;
 
+    // long alpha Operator Name String or Enhanced Operator Name String
     /** @hide */
-    protected CellIdentity(String tag, int type, String mcc, String mnc) {
+    protected final String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    /** @hide */
+    protected final String mAlphaShort;
+
+    /** @hide */
+    protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
+                           String alphas) {
         mTag = tag;
         mType = type;
 
@@ -110,6 +124,8 @@
             mMncStr = null;
             log("invalid MNC format: " + mnc);
         }
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
     }
 
     /** Implement the Parcelable interface */
@@ -125,6 +141,50 @@
     public @Type int getType() { return mType; }
 
     /**
+     * Returns the channel number of the cell identity.
+     *
+     * @hide
+     * @return The channel number, or {@link #INVALID_CHANNEL_NUMBER} if not implemented
+     */
+    public int getChannelNumber() {
+        return INVALID_CHANNEL_NUMBER;
+    }
+
+    /**
+     * @return The long alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string). May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaLong() {
+        return mAlphaLong;
+    }
+
+    /**
+     * @return The short alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string).  May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaShort() {
+        return mAlphaShort;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellIdentity)) {
+            return false;
+        }
+
+        CellIdentity o = (CellIdentity) other;
+        return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
+    }
+
+    /**
      * Used by child classes for parceling.
      *
      * @hide
@@ -134,6 +194,8 @@
         dest.writeInt(type);
         dest.writeString(mMccStr);
         dest.writeString(mMncStr);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
     }
 
     /**
@@ -141,7 +203,8 @@
      * @hide
      */
     protected CellIdentity(String tag, int type, Parcel source) {
-        this(tag, type, source.readString(), source.readString());
+        this(tag, type, source.readString(), source.readString(),
+                source.readString(), source.readString());
     }
 
     /** Implement the Parcelable interface */
diff --git a/android/telephony/CellIdentityCdma.java b/android/telephony/CellIdentityCdma.java
index 2e1d1dc..58a2c45 100644
--- a/android/telephony/CellIdentityCdma.java
+++ b/android/telephony/CellIdentityCdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -48,23 +49,17 @@
      * to +90 degrees).
      */
     private final int mLatitude;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityCdma() {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, null, null);
         mNetworkId = Integer.MAX_VALUE;
         mSystemId = Integer.MAX_VALUE;
         mBasestationId = Integer.MAX_VALUE;
         mLongitude = Integer.MAX_VALUE;
         mLatitude = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -99,14 +94,16 @@
      */
     public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
                              String alphas) {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = nid;
         mSystemId = sid;
         mBasestationId = bid;
-        mLongitude = lon;
-        mLatitude = lat;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
+        if (!isNullIsland(lat, lon)) {
+            mLongitude = lon;
+            mLatitude = lat;
+        } else {
+            mLongitude = mLatitude = Integer.MAX_VALUE;
+        }
     }
 
     private CellIdentityCdma(CellIdentityCdma cid) {
@@ -119,6 +116,18 @@
     }
 
     /**
+     * Take the latitude and longitude in 1/4 seconds and see if
+     * the reported location is on Null Island.
+     *
+     * @return whether the reported Lat/Long are for Null Island
+     *
+     * @hide
+     */
+    private boolean isNullIsland(int lat, int lon) {
+        return Math.abs(lat) <= 1 && Math.abs(lon) <= 1;
+    }
+
+    /**
      * @return Network Id 0..65535, Integer.MAX_VALUE if unknown
      */
     public int getNetworkId() {
@@ -161,26 +170,10 @@
         return mLatitude;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
         return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
-                mAlphaLong, mAlphaShort);
+                super.hashCode());
     }
 
     @Override
@@ -200,8 +193,7 @@
                 && mBasestationId == o.mBasestationId
                 && mLatitude == o.mLatitude
                 && mLongitude == o.mLongitude
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -227,8 +219,6 @@
         dest.writeInt(mBasestationId);
         dest.writeInt(mLongitude);
         dest.writeInt(mLatitude);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -239,8 +229,6 @@
         mBasestationId = in.readInt();
         mLongitude = in.readInt();
         mLatitude = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/android/telephony/CellIdentityGsm.java b/android/telephony/CellIdentityGsm.java
index f948f81..c697880 100644
--- a/android/telephony/CellIdentityGsm.java
+++ b/android/telephony/CellIdentityGsm.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -36,22 +37,16 @@
     private final int mArfcn;
     // 6-bit Base Station Identity Code
     private final int mBsic;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityGsm() {
-        super(TAG, TYPE_GSM, null, null);
+        super(TAG, TYPE_GSM, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mArfcn = Integer.MAX_VALUE;
         mBsic = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -97,16 +92,13 @@
      */
     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
                             String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_GSM, mccStr, mncStr);
+        super(TAG, TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
         // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
-
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -120,7 +112,7 @@
 
     /**
      * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMccStr} instead.
+     * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
@@ -129,7 +121,7 @@
 
     /**
      * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMncStr} instead.
+     * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
@@ -176,35 +168,24 @@
     /**
      * @return Mobile Country Code in string format, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
     /**
      * @return Mobile Network Code in string format, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mArfcn;
     }
 
     /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
-
-    /**
      * @deprecated Primary Scrambling Code is not applicable to GSM.
      * @return Integer.MAX_VALUE, undefined for GSM
      */
@@ -215,7 +196,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, super.hashCode());
     }
 
     @Override
@@ -235,8 +216,7 @@
                 && mBsic == o.mBsic
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -262,8 +242,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -273,8 +251,6 @@
         mCid = in.readInt();
         mArfcn = in.readInt();
         mBsic = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/android/telephony/CellIdentityLte.java b/android/telephony/CellIdentityLte.java
index 7f20c8a..177fced 100644
--- a/android/telephony/CellIdentityLte.java
+++ b/android/telephony/CellIdentityLte.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -36,22 +37,19 @@
     private final int mTac;
     // 18-bit Absolute RF Channel Number
     private final int mEarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
+    // cell bandwidth, in kHz
+    private final int mBandwidth;
 
     /**
      * @hide
      */
     public CellIdentityLte() {
-        super(TAG, TYPE_LTE, null, null);
+        super(TAG, TYPE_LTE, null, null, null, null);
         mCi = Integer.MAX_VALUE;
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
         mEarfcn = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
+        mBandwidth = Integer.MAX_VALUE;
     }
 
     /**
@@ -65,7 +63,8 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc), null, null);
+        this(ci, pci, tac, Integer.MAX_VALUE, Integer.MAX_VALUE, String.valueOf(mcc),
+                String.valueOf(mnc), null, null);
     }
 
     /**
@@ -80,7 +79,8 @@
      * @hide
      */
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
+        this(ci, pci, tac, earfcn, Integer.MAX_VALUE, String.valueOf(mcc), String.valueOf(mnc),
+                null, null);
     }
 
     /**
@@ -89,6 +89,7 @@
      * @param pci Physical Cell Id 0..503
      * @param tac 16-bit Tracking Area Code
      * @param earfcn 18-bit LTE Absolute RF Channel Number
+     * @param bandwidth cell bandwidth in kHz
      * @param mccStr 3-digit Mobile Country Code in string format
      * @param mncStr 2 or 3-digit Mobile Network Code in string format
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
@@ -96,19 +97,18 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, String mccStr,
-                            String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_LTE, mccStr, mncStr);
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
+            String mncStr, String alphal, String alphas) {
+        super(TAG, TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = ci;
         mPci = pci;
         mTac = tac;
         mEarfcn = earfcn;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
+        mBandwidth = bandwidth;
     }
 
     private CellIdentityLte(CellIdentityLte cid) {
-        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mMccStr,
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
     }
 
@@ -118,7 +118,7 @@
 
     /**
      * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMccStr} instead.
+     * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
@@ -127,7 +127,7 @@
 
     /**
      * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMncStr} instead.
+     * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
@@ -163,16 +163,23 @@
     }
 
     /**
+     * @return Cell bandwidth in kHz, Integer.MAX_VALUE if unknown
+     */
+    public int getBandwidth() {
+        return mBandwidth;
+    }
+
+    /**
      * @return Mobile Country Code in string format, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
     /**
      * @return Mobile Network Code in string format, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
@@ -183,25 +190,15 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mEarfcn;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mCi, mPci, mTac, mAlphaLong, mAlphaShort);
+        return Objects.hash(mCi, mPci, mTac, super.hashCode());
     }
 
     @Override
@@ -219,10 +216,10 @@
                 && mPci == o.mPci
                 && mTac == o.mTac
                 && mEarfcn == o.mEarfcn
+                && mBandwidth == o.mBandwidth
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -232,6 +229,7 @@
         .append(" mPci=").append(mPci)
         .append(" mTac=").append(mTac)
         .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBandwidth=").append(mBandwidth)
         .append(" mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
         .append(" mAlphaLong=").append(mAlphaLong)
@@ -248,8 +246,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
+        dest.writeInt(mBandwidth);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -259,8 +256,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mEarfcn = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
+        mBandwidth = in.readInt();
 
         if (DBG) log(toString());
     }
diff --git a/android/telephony/CellIdentityTdscdma.java b/android/telephony/CellIdentityTdscdma.java
index 001d19f..18ab6d4 100644
--- a/android/telephony/CellIdentityTdscdma.java
+++ b/android/telephony/CellIdentityTdscdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -39,7 +40,7 @@
      * @hide
      */
     public CellIdentityTdscdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
@@ -55,7 +56,7 @@
      * @hide
      */
     public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
     }
 
     /**
@@ -65,17 +66,38 @@
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
      *
+     * FIXME: This is a temporary constructor to facilitate migration.
      * @hide
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
-        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
+        mLac = lac;
+        mCid = cid;
+        mCpid = cpid;
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code in string format
+     * @param mnc 2 or 3-digit Mobile Network Code in string format
+     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+            String alphal, String alphas) {
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
-        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
+                cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
     }
 
     CellIdentityTdscdma copy() {
@@ -86,7 +108,7 @@
      * Get Mobile Country Code in string format
      * @return Mobile Country Code in string format, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
@@ -94,7 +116,7 @@
      * Get Mobile Network Code in string format
      * @return Mobile Network Code in string format, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
@@ -121,7 +143,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+        return Objects.hash(mLac, mCid, mCpid, super.hashCode());
     }
 
     @Override
@@ -139,7 +161,8 @@
                 && TextUtils.equals(mMncStr, o.mMncStr)
                 && mLac == o.mLac
                 && mCid == o.mCid
-                && mCpid == o.mCpid;
+                && mCpid == o.mCpid
+                && super.equals(other);
     }
 
     @Override
@@ -150,6 +173,8 @@
         .append(" mLac=").append(mLac)
         .append(" mCid=").append(mCid)
         .append(" mCpid=").append(mCpid)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
         .append("}").toString();
     }
 
diff --git a/android/telephony/CellIdentityWcdma.java b/android/telephony/CellIdentityWcdma.java
index 1aa1715..984483e 100644
--- a/android/telephony/CellIdentityWcdma.java
+++ b/android/telephony/CellIdentityWcdma.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.text.TextUtils;
 
@@ -36,22 +37,16 @@
     private final int mPsc;
     // 16-bit UMTS Absolute RF Channel Number
     private final int mUarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityWcdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mPsc = Integer.MAX_VALUE;
         mUarfcn = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -98,13 +93,11 @@
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
                               String mccStr, String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_WCDMA, mccStr, mncStr);
+        super(TAG, TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mPsc = psc;
         mUarfcn = uarfcn;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -118,7 +111,7 @@
 
     /**
      * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMccStr} instead.
+     * @deprecated Use {@link #getMccString} instead.
      */
     @Deprecated
     public int getMcc() {
@@ -127,7 +120,7 @@
 
     /**
      * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown
-     * @deprecated Use {@link #getMncStr} instead.
+     * @deprecated Use {@link #getMncString} instead.
      */
     @Deprecated
     public int getMnc() {
@@ -160,14 +153,14 @@
     /**
      * @return Mobile Country Code in string version, null if unknown
      */
-    public String getMccStr() {
+    public String getMccString() {
         return mMccStr;
     }
 
     /**
      * @return Mobile Network Code in string version, null if unknown
      */
-    public String getMncStr() {
+    public String getMncString() {
         return mMncStr;
     }
 
@@ -178,25 +171,9 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mPsc, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, mPsc, super.hashCode());
     }
 
     /**
@@ -206,6 +183,12 @@
         return mUarfcn;
     }
 
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mUarfcn;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (this == other) {
@@ -223,8 +206,7 @@
                 && mUarfcn == o.mUarfcn
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -250,8 +232,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -261,8 +241,6 @@
         mCid = in.readInt();
         mPsc = in.readInt();
         mUarfcn = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
         if (DBG) log(toString());
     }
 
diff --git a/android/telephony/CellInfo.java b/android/telephony/CellInfo.java
index b5e4eef..9232ed7 100644
--- a/android/telephony/CellInfo.java
+++ b/android/telephony/CellInfo.java
@@ -16,8 +16,11 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Immutable cell information from a point in time.
@@ -47,6 +50,34 @@
     /** @hide */
     public static final int TIMESTAMP_TYPE_JAVA_RIL = 4;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+        CONNECTION_NONE,
+        CONNECTION_PRIMARY_SERVING,
+        CONNECTION_SECONDARY_SERVING,
+        CONNECTION_UNKNOWN
+    })
+    public @interface CellConnectionStatus {}
+
+    /**
+     * Cell is not a serving cell.
+     *
+     * <p>The cell has been measured but is neither a camped nor serving cell (3GPP 36.304).
+     */
+    public static final int CONNECTION_NONE = 0;
+
+    /** UE is connected to cell for signalling and possibly data (3GPP 36.331, 25.331). */
+    public static final int CONNECTION_PRIMARY_SERVING = 1;
+
+    /** UE is connected to cell for data (3GPP 36.331, 25.331). */
+    public static final int CONNECTION_SECONDARY_SERVING = 2;
+
+    /** Connection status is unknown. */
+    public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
+    private int mCellConnectionStatus = CONNECTION_NONE;
+
     // True if device is mRegistered to the mobile network
     private boolean mRegistered;
 
@@ -69,6 +100,7 @@
         this.mRegistered = ci.mRegistered;
         this.mTimeStampType = ci.mTimeStampType;
         this.mTimeStamp = ci.mTimeStamp;
+        this.mCellConnectionStatus = ci.mCellConnectionStatus;
     }
 
     /** True if this cell is registered to the mobile network */
@@ -90,6 +122,25 @@
     }
 
     /**
+     * Gets the connection status of this cell.
+     *
+     * @see #CONNECTION_NONE
+     * @see #CONNECTION_PRIMARY_SERVING
+     * @see #CONNECTION_SECONDARY_SERVING
+     * @see #CONNECTION_UNKNOWN
+     *
+     * @return The connection status of the cell.
+     */
+    @CellConnectionStatus
+    public int getCellConnectionStatus() {
+        return mCellConnectionStatus;
+    }
+    /** @hide */
+    public void setCellConnectionStatus(@CellConnectionStatus int cellConnectionStatus) {
+        mCellConnectionStatus = cellConnectionStatus;
+    }
+
+    /**
      * Where time stamp gets recorded.
      * @return one of TIMESTAMP_TYPE_XXXX
      *
@@ -111,7 +162,7 @@
     public int hashCode() {
         int primeNum = 31;
         return ((mRegistered ? 0 : 1) * primeNum) + ((int)(mTimeStamp / 1000) * primeNum)
-                + (mTimeStampType * primeNum);
+                + (mTimeStampType * primeNum) + (mCellConnectionStatus * primeNum);
     }
 
     @Override
@@ -125,7 +176,9 @@
         try {
             CellInfo o = (CellInfo) other;
             return mRegistered == o.mRegistered
-                    && mTimeStamp == o.mTimeStamp && mTimeStampType == o.mTimeStampType;
+                    && mTimeStamp == o.mTimeStamp
+                    && mTimeStampType == o.mTimeStampType
+                    && mCellConnectionStatus == o.mCellConnectionStatus;
         } catch (ClassCastException e) {
             return false;
         }
@@ -155,6 +208,7 @@
         timeStampType = timeStampTypeToString(mTimeStampType);
         sb.append(" mTimeStampType=").append(timeStampType);
         sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
+        sb.append(" mCellConnectionStatus=").append(mCellConnectionStatus);
 
         return sb.toString();
     }
@@ -181,6 +235,7 @@
         dest.writeInt(mRegistered ? 1 : 0);
         dest.writeInt(mTimeStampType);
         dest.writeLong(mTimeStamp);
+        dest.writeInt(mCellConnectionStatus);
     }
 
     /**
@@ -192,6 +247,7 @@
         mRegistered = (in.readInt() == 1) ? true : false;
         mTimeStampType = in.readInt();
         mTimeStamp = in.readLong();
+        mCellConnectionStatus = in.readInt();
     }
 
     /** Implement the Parcelable interface */
diff --git a/android/telephony/CellSignalStrengthCdma.java b/android/telephony/CellSignalStrengthCdma.java
index 0474362..183f96d 100644
--- a/android/telephony/CellSignalStrengthCdma.java
+++ b/android/telephony/CellSignalStrengthCdma.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * Signal strength related information.
  */
@@ -34,58 +36,49 @@
     private int mEvdoEcio;  // This value is the EVDO Ec/Io
     private int mEvdoSnr;   // Valid values are 0-8.  8 is the highest signal to noise ratio
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthCdma() {
         setDefaultValues();
     }
 
     /**
-     * Constructor
+     * SignalStrength constructor for input from the HAL.
      *
+     * Note that values received from the HAL require coersion to be compatible here. All values
+     * reported through IRadio are the negative of the actual values (which results in a positive
+     * input to this method.
+     *
+     * <p>Note that this HAL is inconsistent with UMTS-based radio techs as the value indicating
+     * that a field is unreported is negative, rather than a large(r) positive number.
+     * <p>Also note that to keep the public-facing methods of this class consistent with others,
+     * unreported values are coerced to Integer.MAX_VALUE rather than left as -1, which is
+     * a departure from SignalStrength, which is stuck with the values it currently reports.
+     *
+     * @param cdmaDbm negative of the CDMA signal strength value or -1 if invalid.
+     * @param cdmaEcio negative of the CDMA pilot/noise ratio or -1 if invalid.
+     * @param evdoDbm negative of the EvDO signal strength value or -1 if invalid.
+     * @param evdoEcio negative of the EvDO pilot/noise ratio or -1 if invalid.
+     * @param evdoSnr an SNR value 0..8 or -1 if invalid.
      * @hide
      */
     public CellSignalStrengthCdma(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio,
             int evdoSnr) {
-        initialize(cdmaDbm, cdmaEcio, evdoDbm, evdoEcio, evdoSnr);
+        // The values here were lifted from SignalStrength.validateInput()
+        // FIXME: Combine all checking and setting logic between this and SignalStrength.
+        mCdmaDbm = ((cdmaDbm > 0) && (cdmaDbm < 120))  ? -cdmaDbm : Integer.MAX_VALUE;
+        mCdmaEcio = ((cdmaEcio > 0) && (cdmaEcio < 160)) ? -cdmaEcio : Integer.MAX_VALUE;
+
+        mEvdoDbm = ((evdoDbm > 0) && (evdoDbm < 120)) ? -evdoDbm : Integer.MAX_VALUE;
+        mEvdoEcio = ((evdoEcio > 0) && (evdoEcio < 160)) ? -evdoEcio : Integer.MAX_VALUE;
+        mEvdoSnr = ((evdoSnr > 0) && (evdoSnr <= 8)) ? evdoSnr : Integer.MAX_VALUE;
     }
 
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthCdma(CellSignalStrengthCdma s) {
         copyFrom(s);
     }
 
-    /**
-     * Initialize all the values
-     *
-     * @param cdmaDbm
-     * @param cdmaEcio
-     * @param evdoDbm
-     * @param evdoEcio
-     * @param evdoSnr
-     *
-     * @hide
-     */
-    public void initialize(int cdmaDbm, int cdmaEcio, int evdoDbm, int evdoEcio, int evdoSnr) {
-        mCdmaDbm = cdmaDbm;
-        mCdmaEcio = cdmaEcio;
-        mEvdoDbm = evdoDbm;
-        mEvdoEcio = evdoEcio;
-        mEvdoSnr = evdoSnr;
-    }
-
-    /**
-     * @hide
-     */
+    /** @hide */
     protected void copyFrom(CellSignalStrengthCdma s) {
         mCdmaDbm = s.mCdmaDbm;
         mCdmaEcio = s.mCdmaEcio;
@@ -94,9 +87,7 @@
         mEvdoSnr = s.mEvdoSnr;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthCdma copy() {
         return new CellSignalStrengthCdma(this);
@@ -293,9 +284,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return ((mCdmaDbm * primeNum) + (mCdmaEcio * primeNum)
-                + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum));
+        return Objects.hash(mCdmaDbm, mCdmaEcio, mEvdoDbm, mEvdoEcio, mEvdoSnr);
     }
 
     @Override
@@ -336,13 +325,10 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        // Need to multiply CdmaDbm, CdmaEcio, EvdoDbm and EvdoEcio by -1
-        // to ensure consistency when reading values written here
-        // unless the value is invalid
-        dest.writeInt(mCdmaDbm * (mCdmaDbm != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mCdmaEcio * (mCdmaEcio != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mEvdoDbm * (mEvdoDbm != Integer.MAX_VALUE ? -1 : 1));
-        dest.writeInt(mEvdoEcio * (mEvdoEcio != Integer.MAX_VALUE ? -1 : 1));
+        dest.writeInt(mCdmaDbm);
+        dest.writeInt(mCdmaEcio);
+        dest.writeInt(mEvdoDbm);
+        dest.writeInt(mEvdoEcio);
         dest.writeInt(mEvdoSnr);
     }
 
@@ -355,13 +341,9 @@
         // the parcel as positive values.
         // Need to convert into negative values unless the value is invalid
         mCdmaDbm = in.readInt();
-        if (mCdmaDbm != Integer.MAX_VALUE) mCdmaDbm *= -1;
         mCdmaEcio = in.readInt();
-        if (mCdmaEcio != Integer.MAX_VALUE) mCdmaEcio *= -1;
         mEvdoDbm = in.readInt();
-        if (mEvdoDbm != Integer.MAX_VALUE) mEvdoDbm *= -1;
         mEvdoEcio = in.readInt();
-        if (mEvdoEcio != Integer.MAX_VALUE) mEvdoEcio *= -1;
         mEvdoSnr = in.readInt();
         if (DBG) log("CellSignalStrengthCdma(Parcel): " + toString());
     }
diff --git a/android/telephony/CellSignalStrengthGsm.java b/android/telephony/CellSignalStrengthGsm.java
index 4137853..8687cd1 100644
--- a/android/telephony/CellSignalStrengthGsm.java
+++ b/android/telephony/CellSignalStrengthGsm.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * GSM signal strength related information.
  */
@@ -32,80 +34,40 @@
     private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
     private static final int GSM_SIGNAL_STRENGTH_MODERATE = 5;
 
-    private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
     private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
-    private int mTimingAdvance;
+    private int mTimingAdvance; // range from 0-219 or Integer.MAX_VALUE if unknown
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthGsm() {
         setDefaultValues();
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthGsm(int ss, int ber) {
-        initialize(ss, ber);
+        this(ss, ber, Integer.MAX_VALUE);
     }
 
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param ss SignalStrength as ASU value
-     * @param ber is Bit Error Rate
-     *
-     * @hide
-     */
-    public void initialize(int ss, int ber) {
-        mSignalStrength = ss;
-        mBitErrorRate = ber;
-        mTimingAdvance = Integer.MAX_VALUE;
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param ss SignalStrength as ASU value
-     * @param ber is Bit Error Rate
-     * @param ta timing advance
-     *
-     * @hide
-     */
-    public void initialize(int ss, int ber, int ta) {
+    /** @hide */
+    public CellSignalStrengthGsm(int ss, int ber, int ta) {
         mSignalStrength = ss;
         mBitErrorRate = ber;
         mTimingAdvance = ta;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
+    public CellSignalStrengthGsm(CellSignalStrengthGsm s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
     protected void copyFrom(CellSignalStrengthGsm s) {
         mSignalStrength = s.mSignalStrength;
         mBitErrorRate = s.mBitErrorRate;
         mTimingAdvance = s.mTimingAdvance;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthGsm copy() {
         return new CellSignalStrengthGsm(this);
@@ -185,8 +147,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+        return Objects.hash(mSignalStrength, mBitErrorRate, mTimingAdvance);
     }
 
     @Override
diff --git a/android/telephony/CellSignalStrengthLte.java b/android/telephony/CellSignalStrengthLte.java
index 0d07a40..7e86966 100644
--- a/android/telephony/CellSignalStrengthLte.java
+++ b/android/telephony/CellSignalStrengthLte.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * LTE signal strength related information.
  */
@@ -35,50 +37,15 @@
     private int mCqi;
     private int mTimingAdvance;
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthLte() {
         setDefaultValues();
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
             int timingAdvance) {
-        initialize(signalStrength, rsrp, rsrq, rssnr, cqi, timingAdvance);
-    }
-
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public CellSignalStrengthLte(CellSignalStrengthLte s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param lteSignalStrength
-     * @param rsrp
-     * @param rsrq
-     * @param rssnr
-     * @param cqi
-     *
-     * @hide
-     */
-    public void initialize(int lteSignalStrength, int rsrp, int rsrq, int rssnr, int cqi,
-            int timingAdvance) {
-        mSignalStrength = lteSignalStrength;
+        mSignalStrength = signalStrength;
         mRsrp = rsrp;
         mRsrq = rsrq;
         mRssnr = rssnr;
@@ -86,25 +53,12 @@
         mTimingAdvance = timingAdvance;
     }
 
-    /**
-     * Initialize from the SignalStrength structure.
-     *
-     * @param ss
-     *
-     * @hide
-     */
-    public void initialize(SignalStrength ss, int timingAdvance) {
-        mSignalStrength = ss.getLteSignalStrength();
-        mRsrp = ss.getLteRsrp();
-        mRsrq = ss.getLteRsrq();
-        mRssnr = ss.getLteRssnr();
-        mCqi = ss.getLteCqi();
-        mTimingAdvance = timingAdvance;
+    /** @hide */
+    public CellSignalStrengthLte(CellSignalStrengthLte s) {
+        copyFrom(s);
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     protected void copyFrom(CellSignalStrengthLte s) {
         mSignalStrength = s.mSignalStrength;
         mRsrp = s.mRsrp;
@@ -114,9 +68,7 @@
         mTimingAdvance = s.mTimingAdvance;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthLte copy() {
         return new CellSignalStrengthLte(this);
@@ -220,7 +172,7 @@
     }
 
     /**
-     * Get the timing advance value for LTE, as a value between 0..63.
+     * Get the timing advance value for LTE, as a value in range of 0..1282.
      * Integer.MAX_VALUE is reported when there is no active RRC
      * connection. Refer to 3GPP 36.213 Sec 4.2.3
      * @return the LTE timing advance, if available.
@@ -231,10 +183,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mRsrp * primeNum)
-                + (mRsrq * primeNum) + (mRssnr * primeNum) + (mCqi * primeNum)
-                + (mTimingAdvance * primeNum);
+        return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
     }
 
     @Override
diff --git a/android/telephony/CellSignalStrengthWcdma.java b/android/telephony/CellSignalStrengthWcdma.java
index b94b01d..dd32a96 100644
--- a/android/telephony/CellSignalStrengthWcdma.java
+++ b/android/telephony/CellSignalStrengthWcdma.java
@@ -20,6 +20,8 @@
 import android.os.Parcelable;
 import android.telephony.Rlog;
 
+import java.util.Objects;
+
 /**
  * Wcdma signal strength related information.
  */
@@ -32,62 +34,32 @@
     private static final int WCDMA_SIGNAL_STRENGTH_GOOD = 8;
     private static final int WCDMA_SIGNAL_STRENGTH_MODERATE = 5;
 
-    private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
-    private int mBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+    private int mSignalStrength; // in ASU; Valid values are (0-31, 99) as defined in TS 27.007 8.5
+    private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
 
-    /**
-     * Empty constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthWcdma() {
         setDefaultValues();
     }
 
-    /**
-     * Constructor
-     *
-     * @hide
-     */
+    /** @hide */
     public CellSignalStrengthWcdma(int ss, int ber) {
-        initialize(ss, ber);
-    }
-
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param ss SignalStrength as ASU value
-     * @param ber is Bit Error Rate
-     *
-     * @hide
-     */
-    public void initialize(int ss, int ber) {
         mSignalStrength = ss;
         mBitErrorRate = ber;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
+    public CellSignalStrengthWcdma(CellSignalStrengthWcdma s) {
+        copyFrom(s);
+    }
+
+    /** @hide */
     protected void copyFrom(CellSignalStrengthWcdma s) {
         mSignalStrength = s.mSignalStrength;
         mBitErrorRate = s.mBitErrorRate;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @Override
     public CellSignalStrengthWcdma copy() {
         return new CellSignalStrengthWcdma(this);
@@ -156,8 +128,7 @@
 
     @Override
     public int hashCode() {
-        int primeNum = 31;
-        return (mSignalStrength * primeNum) + (mBitErrorRate * primeNum);
+        return Objects.hash(mSignalStrength, mBitErrorRate);
     }
 
     @Override
diff --git a/android/telephony/DataConnectionRealTimeInfo.java b/android/telephony/DataConnectionRealTimeInfo.java
index f71f58d..fc4e17a 100644
--- a/android/telephony/DataConnectionRealTimeInfo.java
+++ b/android/telephony/DataConnectionRealTimeInfo.java
@@ -28,10 +28,14 @@
 public class DataConnectionRealTimeInfo implements Parcelable {
     private long mTime;             // Time the info was collected since boot in nanos;
 
-    public static final int DC_POWER_STATE_LOW       = 1;
-    public static final int DC_POWER_STATE_MEDIUM    = 2;
-    public static final int DC_POWER_STATE_HIGH      = 3;
-    public static final int DC_POWER_STATE_UNKNOWN   = Integer.MAX_VALUE;
+    public static final int DC_POWER_STATE_LOW
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_LOW ; // = 1
+    public static final int DC_POWER_STATE_MEDIUM
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_MEDIUM; // = 2
+    public static final int DC_POWER_STATE_HIGH
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_HIGH; // = 3
+    public static final int DC_POWER_STATE_UNKNOWN
+            = TelephonyProtoEnums.DATA_CONNECTION_POWER_STATE_UNKNOWN; // = Integer.MAX_VALUE
 
     private int mDcPowerState;      // DC_POWER_STATE_[LOW | MEDIUM | HIGH | UNKNOWN]
 
diff --git a/android/telephony/DataSpecificRegistrationStates.java b/android/telephony/DataSpecificRegistrationStates.java
new file mode 100644
index 0000000..97e3037
--- /dev/null
+++ b/android/telephony/DataSpecificRegistrationStates.java
@@ -0,0 +1,72 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to data network registration.
+ * @hide
+ */
+public class DataSpecificRegistrationStates implements Parcelable{
+    /**
+     * The maximum number of simultaneous Data Calls that
+     * must be established using setupDataCall().
+     */
+    public final int maxDataCalls;
+
+    DataSpecificRegistrationStates(int maxDataCalls) {
+        this.maxDataCalls = maxDataCalls;
+    }
+
+    private DataSpecificRegistrationStates(Parcel source) {
+        maxDataCalls = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(maxDataCalls);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(maxDataCalls);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof DataSpecificRegistrationStates)) {
+            return false;
+        }
+
+        DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
+        return this.maxDataCalls == other.maxDataCalls;
+    }
+
+    public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
+            new Parcelable.Creator<DataSpecificRegistrationStates>() {
+                @Override
+                public DataSpecificRegistrationStates createFromParcel(Parcel source) {
+                    return new DataSpecificRegistrationStates(source);
+                }
+
+                @Override
+                public DataSpecificRegistrationStates[] newArray(int size) {
+                    return new DataSpecificRegistrationStates[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/android/telephony/LocationAccessPolicy.java b/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..6db8e82
--- /dev/null
+++ b/android/telephony/LocationAccessPolicy.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.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+    private static final String LOG_TAG = LocationAccessPolicy.class.getSimpleName();
+
+    /**
+     * API to determine if the caller has permissions to get cell location.
+     *
+     * @param pkgName Package name of the application requesting access
+     * @param uid The uid of the package
+     * @param pid The pid of the package
+     * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
+     * @return boolean true or false if permissions is granted
+     */
+    public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+            int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
+        Trace.beginSection("TelephonyLohcationCheck");
+        try {
+            // Always allow the phone process to access location. This avoid breaking legacy code
+            // that rely on public-facing APIs to access cell location, and it doesn't create a
+            // info leak risk because the cell location is stored in the phone process anyway.
+            if (uid == Process.PHONE_UID) {
+                return true;
+            }
+
+            // We always require the location permission and also require the
+            // location mode to be on for non-legacy apps. Legacy apps are
+            // required to be in the foreground to at least mitigate the case
+            // where a legacy app the user is not using tracks their location.
+            // Granting ACCESS_FINE_LOCATION to an app automatically grants it
+            // ACCESS_COARSE_LOCATION.
+            if (throwOnDeniedPermission) {
+                context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+                        pid, uid, "canAccessCellLocation");
+            } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+                    pid, uid) == PackageManager.PERMISSION_DENIED) {
+                return false;
+            }
+            final int opCode = AppOpsManager.permissionToOpCode(
+                    Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
+                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+            if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+                return false;
+            }
+            // If the user or profile is current, permission is granted.
+            // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+            return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+        LocationManager locationManager = context.getSystemService(LocationManager.class);
+        if (locationManager == null) {
+            Log.w(LOG_TAG, "Couldn't get location manager, denying location access");
+            return false;
+        }
+        return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
+    }
+
+    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+        return context.checkCallingOrSelfPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            final int currentUser = ActivityManager.getCurrentUser();
+            final int callingUserId = UserHandle.getUserId(uid);
+            if (callingUserId == currentUser) {
+                return true;
+            } else {
+                List<UserInfo> userProfiles = context.getSystemService(
+                        UserManager.class).getProfiles(currentUser);
+                for (UserInfo user : userProfiles) {
+                    if (user.id == callingUserId) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/android/telephony/MbmsDownloadSession.java b/android/telephony/MbmsDownloadSession.java
index 059a2d0..38a6593 100644
--- a/android/telephony/MbmsDownloadSession.java
+++ b/android/telephony/MbmsDownloadSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,15 +32,16 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
-import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStatusListener;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.InternalDownloadProgressListener;
 import android.telephony.mbms.InternalDownloadSessionCallback;
-import android.telephony.mbms.InternalDownloadStateCallback;
-import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.InternalDownloadStatusListener;
 import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsErrors;
 import android.telephony.mbms.MbmsTempFileProvider;
 import android.telephony.mbms.MbmsUtils;
@@ -53,11 +56,10 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
 /**
  * This class provides functionality for file download over MBMS.
  */
@@ -107,11 +109,8 @@
     /**
      * {@link Uri} extra that Android will attach to the intent supplied via
      * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
-     * Indicates the location of the successfully downloaded file within the temp file root set
-     * via {@link #setTempFileRootDirectory(File)}.
-     * While you may use this file in-place, it is highly encouraged that you move
-     * this file to a different location after receiving the download completion intent, as this
-     * file resides within the temp file directory.
+     * Indicates the location of the successfully downloaded file within the directory that the
+     * app provided via the builder.
      *
      * Will always be set to a non-null value if
      * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
@@ -134,6 +133,14 @@
      */
     public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
 
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {RESULT_SUCCESSFUL, RESULT_CANCELLED, RESULT_EXPIRED, RESULT_IO_ERROR,
+            RESULT_SERVICE_ID_NOT_DEFINED, RESULT_DOWNLOAD_FAILURE, RESULT_OUT_OF_STORAGE,
+            RESULT_FILE_ROOT_UNREACHABLE}, prefix = { "RESULT_" })
+    public @interface DownloadResultCode{}
+
     /**
      * Indicates that the download was successful.
      */
@@ -220,6 +227,8 @@
      */
     public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
 
+    private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
+
     private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
 
     private final Context mContext;
@@ -233,26 +242,25 @@
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
     private final InternalDownloadSessionCallback mInternalCallback;
-    private final Map<DownloadStateCallback, InternalDownloadStateCallback>
-            mInternalDownloadCallbacks = new HashMap<>();
+    private final Map<DownloadStatusListener, InternalDownloadStatusListener>
+            mInternalDownloadStatusListeners = new HashMap<>();
+    private final Map<DownloadProgressListener, InternalDownloadProgressListener>
+            mInternalDownloadProgressListeners = new HashMap<>();
 
-    private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
-            int subscriptionId, Handler handler) {
+    private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
+            MbmsDownloadSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+        mInternalCallback = new InternalDownloadSessionCallback(callback, executor);
     }
 
     /**
      * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+     * See {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)}
      */
     public static MbmsDownloadSession create(@NonNull Context context,
-            @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsDownloadSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
@@ -279,24 +287,24 @@
      * {@link MbmsDownloadSession} that you received before calling this method again.
      *
      * @param context The instance of {@link Context} to use
-     * @param callback A callback to get asynchronous error messages and file service updates.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The data subscription ID to use
-     * @param handler The {@link Handler} on which callbacks should be enqueued.
+     * @param callback A callback to get asynchronous error messages and file service updates.
      * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
      * setup.
      */
     public static @Nullable MbmsDownloadSession create(@NonNull Context context,
-            final @NonNull MbmsDownloadSessionCallback callback,
-            int subscriptionId, @NonNull Handler handler) {
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsDownloadSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot have two active instances");
         }
         MbmsDownloadSession session =
-                new MbmsDownloadSession(context, callback, subscriptionId, handler);
+                new MbmsDownloadSession(context, executor, subscriptionId, callback);
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -329,6 +337,12 @@
                             sIsInitialized.set(false);
                             return;
                         }
+                        if (result == MbmsErrors.UNKNOWN) {
+                            // Unbind and throw an obvious error
+                            close();
+                            throw new IllegalStateException("Middleware must not return an"
+                                    + " unknown error code");
+                        }
                         if (result != MbmsErrors.SUCCESS) {
                             sendErrorToApp(result, "Error returned during initialization");
                             sIsInitialized.set(false);
@@ -380,6 +394,11 @@
         }
         try {
             int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (returnCode != MbmsErrors.SUCCESS) {
                 sendErrorToApp(returnCode, null);
             }
@@ -435,8 +454,14 @@
 
         try {
             int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 sendErrorToApp(result, null);
+                return;
             }
         } catch (RemoteException e) {
             mService.set(null);
@@ -499,13 +524,19 @@
      * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
      * file root directory.
      *
+     * If the {@link DownloadRequest} has a destination that is not on the same filesystem as the
+     * temp file directory provided via {@link #getTempFileRootDirectory()}, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
      * Asynchronous errors through the callback may include any error not specific to the
      * streaming use-case.
+     *
+     * If no error is delivered via the callback after calling this method, that means that the
+     * middleware has successfully started the download or scheduled the download, if the download
+     * is at a future time.
      * @param request The request that specifies what should be downloaded.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
      */
-    public int download(@NonNull DownloadRequest request) {
+    public void download(@NonNull DownloadRequest request) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -521,16 +552,25 @@
             setTempFileRootDirectory(tempRootDirectory);
         }
 
+        checkDownloadRequestDestination(request);
+
         try {
             int result = downloadService.download(request);
             if (result == MbmsErrors.SUCCESS) {
                 writeDownloadRequestToken(request);
+            } else {
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an unknown"
+                            + " error code");
+                }
+                sendErrorToApp(result, null);
             }
-            return result;
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
@@ -558,111 +598,232 @@
     }
 
     /**
-     * Registers a callback for a {@link DownloadRequest} previously requested via
+     * Registers a download status listener for a {@link DownloadRequest} previously requested via
      * {@link #download(DownloadRequest)}. This callback will only be called as long as both this
      * app and the middleware are both running -- if either one stops, no further calls on the
-     * provided {@link DownloadStateCallback} will be enqueued.
+     * provided {@link DownloadStatusListener} will be enqueued.
      *
      * If the middleware is not aware of the specified download request,
      * this method will throw an {@link IllegalArgumentException}.
      *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
      * @param request The {@link DownloadRequest} that you want updates on.
-     * @param callback The callback that should be called when the middleware has information to
-     *                 share on the download.
-     * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
+     * @param executor The {@link Executor} on which calls to {@code listener } should be executed.
+     * @param listener The listener that should be called when the middleware has information to
+     *                 share on the status download.
      */
-    public int registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
+    public void addStatusListener(@NonNull DownloadRequest request,
+            @NonNull Executor executor, @NonNull DownloadStatusListener listener) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
-        InternalDownloadStateCallback internalCallback =
-                new InternalDownloadStateCallback(callback, handler);
+        InternalDownloadStatusListener internalListener =
+                new InternalDownloadStatusListener(listener, executor);
 
         try {
-            int result = downloadService.registerStateCallback(request, internalCallback,
-                    callback.getCallbackFilterFlags());
+            int result = downloadService.addStatusListener(request, internalListener);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
                 }
-                return result;
+                sendErrorToApp(result, null);
+                return;
             }
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
         }
-        mInternalDownloadCallbacks.put(callback, internalCallback);
-        return MbmsErrors.SUCCESS;
+        mInternalDownloadStatusListeners.put(listener, internalListener);
     }
 
     /**
-     * Un-register a callback previously registered via
-     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
-     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * Un-register a listener previously registered via
+     * {@link #addStatusListener(DownloadRequest, Executor, DownloadStatusListener)}. After
+     * this method is called, no further calls will be enqueued on the {@link Executor}
      * provided upon registration, even if this method throws an exception.
      *
      * If the middleware is not aware of the specified download request,
      * this method will throw an {@link IllegalArgumentException}.
      *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
      * @param request The {@link DownloadRequest} provided during registration
-     * @param callback The callback provided during registration.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
+     * @param listener The listener provided during registration.
      */
-    public int unregisterStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback) {
+    public void removeStatusListener(@NonNull DownloadRequest request,
+            @NonNull DownloadStatusListener listener) {
         try {
             IMbmsDownloadService downloadService = mService.get();
             if (downloadService == null) {
                 throw new IllegalStateException("Middleware not yet bound");
             }
 
-            InternalDownloadStateCallback internalCallback =
-                    mInternalDownloadCallbacks.get(callback);
-            if (internalCallback == null) {
-                throw new IllegalArgumentException("Provided callback was never registered");
+            InternalDownloadStatusListener internalListener =
+                    mInternalDownloadStatusListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
             }
 
             try {
-                int result = downloadService.unregisterStateCallback(request, internalCallback);
+                int result = downloadService.removeStatusListener(request, internalListener);
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an"
+                            + " unknown error code");
+                }
                 if (result != MbmsErrors.SUCCESS) {
                     if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                         throw new IllegalArgumentException("Unknown download request.");
                     }
-                    return result;
+                    sendErrorToApp(result, null);
+                    return;
                 }
             } catch (RemoteException e) {
                 mService.set(null);
                 sIsInitialized.set(false);
-                return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
             }
         } finally {
-            InternalDownloadStateCallback internalCallback =
-                    mInternalDownloadCallbacks.remove(callback);
+            InternalDownloadStatusListener internalCallback =
+                    mInternalDownloadStatusListeners.remove(listener);
             if (internalCallback != null) {
                 internalCallback.stop();
             }
         }
-        return MbmsErrors.SUCCESS;
+    }
+
+    /**
+     * Registers a progress listener for a {@link DownloadRequest} previously requested via
+     * {@link #download(DownloadRequest)}. This listener will only be called as long as both this
+     * app and the middleware are both running -- if either one stops, no further calls on the
+     * provided {@link DownloadProgressListener} will be enqueued.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param request The {@link DownloadRequest} that you want updates on.
+     * @param executor The {@link Executor} on which calls to {@code listener} should be executed.
+     * @param listener The listener that should be called when the middleware has information to
+     *                 share on the progress of the download.
+     */
+    public void addProgressListener(@NonNull DownloadRequest request,
+            @NonNull Executor executor, @NonNull DownloadProgressListener listener) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalDownloadProgressListener internalListener =
+                new InternalDownloadProgressListener(listener, executor);
+
+        try {
+            int result = downloadService.addProgressListener(request, internalListener);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        mInternalDownloadProgressListeners.put(listener, internalListener);
+    }
+
+    /**
+     * Un-register a listener previously registered via
+     * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}. After
+     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * provided upon registration, even if this method throws an exception.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param listener The listener provided during registration.
+     */
+    public void removeProgressListener(@NonNull DownloadRequest request,
+            @NonNull DownloadProgressListener listener) {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                throw new IllegalStateException("Middleware not yet bound");
+            }
+
+            InternalDownloadProgressListener internalListener =
+                    mInternalDownloadProgressListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
+            }
+
+            try {
+                int result = downloadService.removeProgressListener(request, internalListener);
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not"
+                            + " return an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    sendErrorToApp(result, null);
+                    return;
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sIsInitialized.set(false);
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
+            }
+        } finally {
+            InternalDownloadProgressListener internalCallback =
+                    mInternalDownloadProgressListeners.remove(listener);
+            if (internalCallback != null) {
+                internalCallback.stop();
+            }
+        }
     }
 
     /**
      * Attempts to cancel the specified {@link DownloadRequest}.
      *
-     * If the middleware is not aware of the specified download request,
-     * this method will throw an {@link IllegalArgumentException}.
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
      *
      * @param downloadRequest The download request that you wish to cancel.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
      */
-    public int cancelDownload(@NonNull DownloadRequest downloadRequest) {
+    public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -670,46 +831,65 @@
 
         try {
             int result = downloadService.cancelDownload(downloadRequest);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
-                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
-                    throw new IllegalArgumentException("Unknown download request.");
-                }
+                sendErrorToApp(result, null);
             } else {
                 deleteDownloadRequestToken(downloadRequest);
             }
-            return result;
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
     /**
-     * Gets information about the status of a file pending download.
+     * Requests information about the state of a file pending download.
      *
-     * If there was a problem communicating with the middleware or if it has no records of the
+     * The state will be delivered as a callback via
+     * {@link DownloadStatusListener#onStatusUpdated(DownloadRequest, FileInfo, int)}. If no such
+     * callback has been registered via
+     * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}, this
+     * method will be a no-op.
+     *
+     * If the middleware has no record of the
      * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
-     * {@link #STATUS_UNKNOWN} will be returned.
+     * an {@link IllegalArgumentException} will be thrown.
      *
      * @param downloadRequest The download request to query.
      * @param fileInfo The particular file within the request to get information on.
-     * @return The status of the download.
      */
-    @DownloadStatus
-    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
+    public void requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         try {
-            return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+            int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO) {
+                    throw new IllegalArgumentException("Unknown file.");
+                }
+                sendErrorToApp(result, null);
+            }
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
             sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
-            return STATUS_UNKNOWN;
         }
     }
 
@@ -741,6 +921,11 @@
 
         try {
             int result = downloadService.resetDownloadKnowledge(downloadRequest);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
@@ -762,7 +947,7 @@
      * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)} to
      * obtain another instance of {@link MbmsDownloadSession} immediately after this method
      * returns.
      *
@@ -799,11 +984,11 @@
         try {
             if (!token.createNewFile()) {
                 throw new RuntimeException("Failed to create download token for request "
-                        + request);
+                        + request + ". Token location is " + token.getPath());
             }
         } catch (IOException e) {
             throw new RuntimeException("Failed to create download token for request " + request
-                    + " due to IOException " + e);
+                    + " due to IOException " + e + ". Attempted to write to " + token.getPath());
         }
     }
 
@@ -818,6 +1003,36 @@
         }
     }
 
+    private void checkDownloadRequestDestination(DownloadRequest request) {
+        File downloadRequestDestination = new File(request.getDestinationUri().getPath());
+        if (!downloadRequestDestination.isDirectory()) {
+            throw new IllegalArgumentException("The destination path must be a directory");
+        }
+        // Check if the request destination is okay to use by attempting to rename an empty
+        // file to there.
+        File testFile = new File(MbmsTempFileProvider.getEmbmsTempFileDir(mContext),
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+        File testFileDestination = new File(downloadRequestDestination,
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+
+        try {
+            if (!testFile.exists()) {
+                testFile.createNewFile();
+            }
+            if (!testFile.renameTo(testFileDestination)) {
+                throw new IllegalArgumentException("Destination provided in the download request " +
+                        "is invalid -- files in the temp file directory cannot be directly moved " +
+                        "there.");
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException while testing out the destination: "
+                    + e);
+        } finally {
+            testFile.delete();
+            testFileDestination.delete();
+        }
+    }
+
     private File getDownloadRequestTokenPath(DownloadRequest request) {
         File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
                 request.getFileServiceId());
@@ -827,10 +1042,6 @@
     }
 
     private void sendErrorToApp(int errorCode, String message) {
-        try {
-            mInternalCallback.onError(errorCode, message);
-        } catch (RemoteException e) {
-            // Ignore, should not happen locally.
-        }
+        mInternalCallback.onError(errorCode, message);
     }
 }
diff --git a/android/telephony/MbmsStreamingSession.java b/android/telephony/MbmsStreamingSession.java
index fb2ff7b..cd465d2 100644
--- a/android/telephony/MbmsStreamingSession.java
+++ b/android/telephony/MbmsStreamingSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -24,12 +26,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
-import android.telephony.mbms.InternalStreamingSessionCallback;
 import android.telephony.mbms.InternalStreamingServiceCallback;
+import android.telephony.mbms.InternalStreamingSessionCallback;
 import android.telephony.mbms.MbmsErrors;
 import android.telephony.mbms.MbmsStreamingSessionCallback;
 import android.telephony.mbms.MbmsUtils;
@@ -42,11 +42,10 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
 /**
  * This class provides functionality for streaming media over MBMS.
  */
@@ -89,14 +88,11 @@
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
-    private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
-                    int subscriptionId, Handler handler) {
+    private MbmsStreamingSession(Context context, Executor executor, int subscriptionId,
+            MbmsStreamingSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
+        mInternalCallback = new InternalStreamingSessionCallback(callback, executor);
     }
 
     /**
@@ -117,25 +113,25 @@
      * {@link MbmsStreamingSession} that you received before calling this method again.
      *
      * @param context The {@link Context} to use.
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param subscriptionId The subscription ID to use.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
-     * @param subscriptionId The subscription ID to use.
-     * @param handler The handler you wish to receive callbacks on.
      * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
      */
     public static @Nullable MbmsStreamingSession create(@NonNull Context context,
-            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
-            @NonNull Handler handler) {
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsStreamingSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
         }
-        MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
-                subscriptionId, handler);
+        MbmsStreamingSession session = new MbmsStreamingSession(context, executor,
+                subscriptionId, callback);
 
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -148,22 +144,22 @@
 
     /**
      * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+     * See {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)}.
      */
     public static MbmsStreamingSession create(@NonNull Context context,
-            @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsStreamingSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
      * Terminates this instance. Also terminates
      * any streaming services spawned from this instance as if
-     * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
+     * {@link StreamingService#close()} had been called on them. After this method returns,
      * no further callbacks originating from the middleware will be enqueued on the provided
      * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)} to
      * obtain another instance of {@link MbmsStreamingSession} immediately after this method
      * returns.
      *
@@ -212,6 +208,11 @@
         try {
             int returnCode = streamingService.requestUpdateStreamingServices(
                     mSubscriptionId, serviceClassList);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (returnCode != MbmsErrors.SUCCESS) {
                 sendErrorToApp(returnCode, null);
             }
@@ -237,20 +238,20 @@
      * {@link MbmsErrors.StreamingErrors}.
      *
      * @param serviceInfo The information about the service to stream.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param callback A callback that'll be called when something about the stream changes.
-     * @param handler A handler that calls to {@code callback} should be called on.
      * @return An instance of {@link StreamingService} through which the stream can be controlled.
      *         May be {@code null} if an error occurred.
      */
     public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            StreamingServiceCallback callback, @NonNull Handler handler) {
+            @NonNull Executor executor, StreamingServiceCallback callback) {
         IMbmsStreamingService streamingService = mService.get();
         if (streamingService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
-                callback, handler);
+                callback, executor);
 
         StreamingService serviceForApp = new StreamingService(
                 mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
@@ -259,6 +260,11 @@
         try {
             int returnCode = streamingService.startStreaming(
                     mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (returnCode != MbmsErrors.SUCCESS) {
                 sendErrorToApp(returnCode, null);
                 return null;
@@ -305,6 +311,12 @@
                             sIsInitialized.set(false);
                             return;
                         }
+                        if (result == MbmsErrors.UNKNOWN) {
+                            // Unbind and throw an obvious error
+                            close();
+                            throw new IllegalStateException("Middleware must not return"
+                                    + " an unknown error code");
+                        }
                         if (result != MbmsErrors.SUCCESS) {
                             sendErrorToApp(result, "Error returned during initialization");
                             sIsInitialized.set(false);
diff --git a/android/telephony/ModemActivityInfo.java b/android/telephony/ModemActivityInfo.java
index 03ce2d8..521adef 100644
--- a/android/telephony/ModemActivityInfo.java
+++ b/android/telephony/ModemActivityInfo.java
@@ -36,12 +36,12 @@
      */
     public static final int TX_POWER_LEVELS = 5;
 
-    private final long mTimestamp;
-    private final int mSleepTimeMs;
-    private final int mIdleTimeMs;
-    private final int [] mTxTimeMs = new int[TX_POWER_LEVELS];
-    private final int mRxTimeMs;
-    private final int mEnergyUsed;
+    private long mTimestamp;
+    private int mSleepTimeMs;
+    private int mIdleTimeMs;
+    private int [] mTxTimeMs = new int[TX_POWER_LEVELS];
+    private int mRxTimeMs;
+    private int mEnergyUsed;
 
     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
                         int[] txTimeMs, int rxTimeMs, int energyUsed) {
@@ -110,6 +110,10 @@
         return mTimestamp;
     }
 
+    public void setTimestamp(long timestamp) {
+        mTimestamp = timestamp;
+    }
+
     /**
      * @return tx time in ms. It's an array of tx times
      * with each index...
@@ -118,6 +122,10 @@
         return mTxTimeMs;
     }
 
+    public void setTxTimeMillis(int[] txTimeMs) {
+        mTxTimeMs = txTimeMs;
+    }
+
     /**
      * @return sleep time in ms.
      */
@@ -125,6 +133,10 @@
         return mSleepTimeMs;
     }
 
+    public void setSleepTimeMillis(int sleepTimeMillis) {
+        mSleepTimeMs = sleepTimeMillis;
+    }
+
     /**
      * @return idle time in ms.
      */
@@ -132,6 +144,10 @@
         return mIdleTimeMs;
     }
 
+    public void setIdleTimeMillis(int idleTimeMillis) {
+        mIdleTimeMs = idleTimeMillis;
+    }
+
     /**
      * @return rx time in ms.
      */
@@ -139,6 +155,10 @@
         return mRxTimeMs;
     }
 
+    public void setRxTimeMillis(int rxTimeMillis) {
+        mRxTimeMs = rxTimeMillis;
+    }
+
     /**
      * product of current(mA), voltage(V) and time(ms)
      * @return energy used
@@ -147,6 +167,10 @@
         return mEnergyUsed;
     }
 
+    public void setEnergyUsed(int energyUsed) {
+        mEnergyUsed = energyUsed;
+    }
+
     /**
      * @return if the record is valid
      */
diff --git a/android/telephony/NetworkRegistrationState.java b/android/telephony/NetworkRegistrationState.java
index e051069..bba779d 100644
--- a/android/telephony/NetworkRegistrationState.java
+++ b/android/telephony/NetworkRegistrationState.java
@@ -105,6 +105,11 @@
     @Nullable
     private final CellIdentity mCellIdentity;
 
+    @Nullable
+    private VoiceSpecificRegistrationStates mVoiceSpecificStates;
+
+    @Nullable
+    private DataSpecificRegistrationStates mDataSpecificStates;
 
     /**
      * @param transportType Transport type. Must be {@link AccessNetworkConstants.TransportType}
@@ -128,6 +133,34 @@
         mEmergencyOnly = emergencyOnly;
     }
 
+    /**
+     * Constructor for voice network registration states.
+     * @hide
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
+            int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
+        this(transportType, domain, regState, accessNetworkTechnology,
+                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+        mVoiceSpecificStates = new VoiceSpecificRegistrationStates(cssSupported, roamingIndicator,
+                systemIsInPrl, defaultRoamingIndicator);
+    }
+
+    /**
+     * Constructor for data network registration states.
+     * @hide
+     */
+    public NetworkRegistrationState(int transportType, int domain, int regState,
+            int accessNetworkTechnology, int reasonForDenial, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+        this(transportType, domain, regState, accessNetworkTechnology,
+                reasonForDenial, emergencyOnly, availableServices, cellIdentity);
+
+        mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
+    }
+
     protected NetworkRegistrationState(Parcel source) {
         mTransportType = source.readInt();
         mDomain = source.readInt();
@@ -137,6 +170,10 @@
         mEmergencyOnly = source.readBoolean();
         mAvailableServices = source.createIntArray();
         mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader());
+        mVoiceSpecificStates = source.readParcelable(
+                VoiceSpecificRegistrationStates.class.getClassLoader());
+        mDataSpecificStates = source.readParcelable(
+                DataSpecificRegistrationStates.class.getClassLoader());
     }
 
     /**
@@ -173,6 +210,36 @@
         return mAccessNetworkTechnology;
     }
 
+    /**
+     * @return Reason for denial from network.
+     */
+    public int getReasonForDenial() {
+        return mReasonForDenial;
+    }
+
+    /**
+     * @return The cell information.
+     */
+    public CellIdentity getCellIdentity() {
+        return mCellIdentity;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public VoiceSpecificRegistrationStates getVoiceSpecificStates() {
+        return mVoiceSpecificStates;
+    }
+
+    /**
+     * @hide
+     */
+    @Nullable
+    public DataSpecificRegistrationStates getDataSpecificStates() {
+        return mDataSpecificStates;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -202,13 +269,16 @@
                 .append(" emergencyEnabled=").append(mEmergencyOnly)
                 .append(" supportedServices=").append(mAvailableServices)
                 .append(" cellIdentity=").append(mCellIdentity)
+                .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
+                .append(" dataSpecificStates=").append(mDataSpecificStates)
                 .append("}").toString();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mTransportType, mDomain, mRegState, mAccessNetworkTechnology,
-                mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity);
+                mReasonForDenial, mEmergencyOnly, mAvailableServices, mCellIdentity,
+                mVoiceSpecificStates, mDataSpecificStates);
     }
 
     @Override
@@ -228,7 +298,9 @@
                 && mEmergencyOnly == other.mEmergencyOnly
                 && (mAvailableServices == other.mAvailableServices
                     || Arrays.equals(mAvailableServices, other.mAvailableServices))
-                && mCellIdentity == other.mCellIdentity;
+                && equals(mCellIdentity, other.mCellIdentity)
+                && equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
+                && equals(mDataSpecificStates, other.mDataSpecificStates);
     }
 
     @Override
@@ -241,6 +313,8 @@
         dest.writeBoolean(mEmergencyOnly);
         dest.writeIntArray(mAvailableServices);
         dest.writeParcelable(mCellIdentity, 0);
+        dest.writeParcelable(mVoiceSpecificStates, 0);
+        dest.writeParcelable(mDataSpecificStates, 0);
     }
 
     public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
@@ -255,4 +329,14 @@
             return new NetworkRegistrationState[size];
         }
     };
+
+    private static boolean equals(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        } else if (o1 == null) {
+            return false;
+        } else {
+            return o1.equals(o2);
+        }
+    }
 }
diff --git a/android/telephony/NetworkScan.java b/android/telephony/NetworkScan.java
index a277212..073c313 100644
--- a/android/telephony/NetworkScan.java
+++ b/android/telephony/NetworkScan.java
@@ -29,9 +29,9 @@
 
 /**
  * The caller of
- * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)}
+ * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)}
  * will receive an instance of {@link NetworkScan}, which contains a callback method
- * {@link #stop()} for stopping the in-progress scan.
+ * {@link #stopScan()} for stopping the in-progress scan.
  */
 public class NetworkScan {
 
@@ -106,16 +106,26 @@
      * Use this method to stop an ongoing scan. When user requests a new scan, a {@link NetworkScan}
      * object will be returned, and the user can stop the scan by calling this method.
      */
-    public void stop() throws RemoteException {
+    public void stopScan() {
+        ITelephony telephony = getITelephony();
+        if (telephony == null) {
+            Rlog.e(TAG, "Failed to get the ITelephony instance.");
+        }
         try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                telephony.stopNetworkScan(mSubId, mScanId);
-            } else {
-                throw new RemoteException("Failed to get the ITelephony instance.");
-            }
+            telephony.stopNetworkScan(mSubId, mScanId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
+        } catch (RuntimeException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RuntimeException", ex);
+        }
+    }
+
+    /** @deprecated Use {@link #stopScan()} */
+    @Deprecated
+    public void stop() throws RemoteException {
+        try {
+            stopScan();
+        } catch (RuntimeException ex) {
             throw new RemoteException("Failed to stop the network scan with id " + mScanId);
         }
     }
diff --git a/android/telephony/NetworkService.java b/android/telephony/NetworkService.java
index 6b3584c..35682a7 100644
--- a/android/telephony/NetworkService.java
+++ b/android/telephony/NetworkService.java
@@ -28,6 +28,8 @@
 import android.os.RemoteException;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -38,7 +40,7 @@
  * follow the following format:
  * ...
  * <service android:name=".xxxNetworkService"
- *     android:permission="android.permission.BIND_NETWORK_SERVICE" >
+ *     android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
  *     <intent-filter>
  *         <action android:name="android.telephony.NetworkService" />
  *     </intent-filter>
@@ -53,11 +55,13 @@
     public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
     public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
 
-    private static final int NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE       = 1;
-    private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE                    = 2;
-    private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE                 = 3;
-    private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE               = 4;
-    private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED          = 5;
+    private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER                 = 1;
+    private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER                 = 2;
+    private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS            = 3;
+    private static final int NETWORK_SERVICE_GET_REGISTRATION_STATE                          = 4;
+    private static final int NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE                       = 5;
+    private static final int NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE                     = 6;
+    private static final int NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED                = 7;
 
 
     private final HandlerThread mHandlerThread;
@@ -66,7 +70,11 @@
 
     private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
 
-    private final SparseArray<INetworkServiceWrapper> mBinderMap = new SparseArray<>();
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
 
     /**
      * The abstract class of the actual network service implementation. The network service provider
@@ -147,37 +155,50 @@
         public void handleMessage(Message message) {
             final int slotId = message.arg1;
             final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
-            NetworkServiceProvider service;
 
-            synchronized (mServiceMap) {
-                service = mServiceMap.get(slotId);
-            }
+            NetworkServiceProvider serviceProvider = mServiceMap.get(slotId);
 
             switch (message.what) {
-                case NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
-                    service = createNetworkServiceProvider(message.arg1);
-                    if (service != null) {
-                        mServiceMap.put(slotId, service);
+                case NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER:
+                    // If the service provider doesn't exist yet, we try to create it.
+                    if (serviceProvider == null) {
+                        mServiceMap.put(slotId, createNetworkServiceProvider(slotId));
                     }
                     break;
+                case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
+                    // If the service provider doesn't exist yet, we try to create it.
+                    if (serviceProvider != null) {
+                        serviceProvider.onDestroy();
+                        mServiceMap.remove(slotId);
+                    }
+                    break;
+                case NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS:
+                    for (int i = 0; i < mServiceMap.size(); i++) {
+                        serviceProvider = mServiceMap.get(i);
+                        if (serviceProvider != null) {
+                            serviceProvider.onDestroy();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
                 case NETWORK_SERVICE_GET_REGISTRATION_STATE:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     int domainId = message.arg2;
-                    service.getNetworkRegistrationState(domainId,
+                    serviceProvider.getNetworkRegistrationState(domainId,
                             new NetworkServiceCallback(callback));
 
                     break;
                 case NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE:
-                    if (service == null) break;
-                    service.registerForStateChanged(callback);
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForStateChanged(callback);
                     break;
                 case NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE:
-                    if (service == null) break;
-                    service.unregisterForStateChanged(callback);
+                    if (serviceProvider == null) break;
+                    serviceProvider.unregisterForStateChanged(callback);
                     break;
                 case NETWORK_SERVICE_INDICATION_NETWORK_STATE_CHANGED:
-                    if (service == null) break;
-                    service.notifyStateChangedToCallbacks();
+                    if (serviceProvider == null) break;
+                    serviceProvider.notifyStateChangedToCallbacks();
                     break;
                 default:
                     break;
@@ -212,47 +233,14 @@
             return null;
         }
 
-        int slotId = intent.getIntExtra(
-                NETWORK_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
-        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
-            loge("Invalid slot id " + slotId);
-            return null;
-        }
-
-        log("onBind: slot id=" + slotId);
-
-        INetworkServiceWrapper binder = mBinderMap.get(slotId);
-        if (binder == null) {
-            Message msg = mHandler.obtainMessage(
-                    NETWORK_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
-            msg.arg1 = slotId;
-            msg.sendToTarget();
-
-            binder = new INetworkServiceWrapper(slotId);
-            mBinderMap.put(slotId, binder);
-        }
-
-        return binder;
+        return mBinder;
     }
 
     /** @hide */
     @Override
     public boolean onUnbind(Intent intent) {
-        int slotId = intent.getIntExtra(NETWORK_SERVICE_EXTRA_SLOT_ID,
-                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-        if (mBinderMap.get(slotId) != null) {
-            NetworkServiceProvider serviceImpl;
-            synchronized (mServiceMap) {
-                serviceImpl = mServiceMap.get(slotId);
-            }
-            // We assume only one component might bind to the service. So if onUnbind is ever
-            // called, we destroy the serviceImpl.
-            if (serviceImpl != null) {
-                serviceImpl.onDestroy();
-            }
-            mBinderMap.remove(slotId);
-        }
+        mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
+                0, null).sendToTarget();
 
         return false;
     }
@@ -260,16 +248,6 @@
     /** @hide */
     @Override
     public void onDestroy() {
-        synchronized (mServiceMap) {
-            for (int i = 0; i < mServiceMap.size(); i++) {
-                NetworkServiceProvider serviceImpl = mServiceMap.get(i);
-                if (serviceImpl != null) {
-                    serviceImpl.onDestroy();
-                }
-            }
-            mServiceMap.clear();
-        }
-
         mHandlerThread.quit();
     }
 
@@ -279,27 +257,36 @@
      */
     private class INetworkServiceWrapper extends INetworkService.Stub {
 
-        private final int mSlotId;
-
-        INetworkServiceWrapper(int slotId) {
-            mSlotId = slotId;
+        @Override
+        public void createNetworkServiceProvider(int slotId) {
+            mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotId,
+                    0, null).sendToTarget();
         }
 
         @Override
-        public void getNetworkRegistrationState(int domain, INetworkServiceCallback callback) {
-            mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, mSlotId,
+        public void removeNetworkServiceProvider(int slotId) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotId,
+                    0, null).sendToTarget();
+        }
+
+        @Override
+        public void getNetworkRegistrationState(
+                int slotId, int domain, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_STATE, slotId,
                     domain, callback).sendToTarget();
         }
 
         @Override
-        public void registerForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
-            mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, mSlotId,
+        public void registerForNetworkRegistrationStateChanged(
+                int slotId, INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_STATE_CHANGE, slotId,
                     0, callback).sendToTarget();
         }
 
         @Override
-        public void unregisterForNetworkRegistrationStateChanged(INetworkServiceCallback callback) {
-            mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, mSlotId,
+        public void unregisterForNetworkRegistrationStateChanged(
+                int slotId,INetworkServiceCallback callback) {
+            mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_STATE_CHANGE, slotId,
                     0, callback).sendToTarget();
         }
     }
@@ -311,4 +298,4 @@
     private final void loge(String s) {
         Rlog.e(TAG, s);
     }
-}
\ No newline at end of file
+}
diff --git a/android/telephony/NetworkServiceCallback.java b/android/telephony/NetworkServiceCallback.java
index 92ebf36..dbad02f 100644
--- a/android/telephony/NetworkServiceCallback.java
+++ b/android/telephony/NetworkServiceCallback.java
@@ -83,6 +83,8 @@
             } catch (RemoteException e) {
                 Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
             }
+        } else {
+            Rlog.e(mTag, "Weak reference of callback is null.");
         }
     }
 }
\ No newline at end of file
diff --git a/android/telephony/PhoneNumberUtils.java b/android/telephony/PhoneNumberUtils.java
index ff67116..fadfc91 100644
--- a/android/telephony/PhoneNumberUtils.java
+++ b/android/telephony/PhoneNumberUtils.java
@@ -22,6 +22,7 @@
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
 import com.android.i18n.phonenumbers.ShortNumberInfo;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -42,6 +43,8 @@
 
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -49,8 +52,31 @@
 /**
  * Various utilities for dealing with phone number strings.
  */
-public class PhoneNumberUtils
-{
+public class PhoneNumberUtils {
+    /** {@hide} */
+    @IntDef(prefix = "BCD_EXTENDED_TYPE_", value = {
+            BCD_EXTENDED_TYPE_EF_ADN,
+            BCD_EXTENDED_TYPE_CALLED_PARTY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BcdExtendType {}
+
+    /*
+     * The BCD extended type used to determine the extended char for the digit which is greater than
+     * 9.
+     *
+     * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
+     */
+    public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
+
+    /*
+     * The BCD extended type used to determine the extended char for the digit which is greater than
+     * 9.
+     *
+     * see TS 24.008 section 10.5.4.7 Called party BCD number
+     */
+    public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
+
     /*
      * Special characters
      *
@@ -77,22 +103,6 @@
     public static final int TOA_International = 0x91;
     public static final int TOA_Unknown = 0x81;
 
-    /*
-     * The BCD extended type used to determine the extended char for the digit which is greater than
-     * 9.
-     *
-     * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
-     */
-    public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
-
-    /*
-     * The BCD extended type used to determine the extended char for the digit which is greater than
-     * 9.
-     *
-     * see TS 24.008 section 10.5.4.7 Called party BCD number
-     */
-    public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
-
     static final String LOG_TAG = "PhoneNumberUtils";
     private static final boolean DBG = false;
 
@@ -844,7 +854,7 @@
      *
      */
     public static String calledPartyBCDToString(
-            byte[] bytes, int offset, int length, int bcdExtType) {
+            byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
         boolean prependPlus = false;
         StringBuilder ret = new StringBuilder(1 + length * 2);
 
@@ -944,7 +954,8 @@
     }
 
     private static void internalCalledPartyBCDFragmentToString(
-            StringBuilder sb, byte [] bytes, int offset, int length, int bcdExtType) {
+            StringBuilder sb, byte [] bytes, int offset, int length,
+            @BcdExtendType int bcdExtType) {
         for (int i = offset ; i < length + offset ; i++) {
             byte b;
             char c;
@@ -999,7 +1010,7 @@
      * TOA byte. For example: SIM ADN extension fields
      */
     public static String calledPartyBCDFragmentToString(
-            byte[] bytes, int offset, int length, int bcdExtType) {
+            byte[] bytes, int offset, int length, @BcdExtendType int bcdExtType) {
         StringBuilder ret = new StringBuilder(length * 2);
         internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
         return ret.toString();
@@ -1009,7 +1020,7 @@
      * Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
      * invalid code.
      */
-    private static char bcdToChar(byte b, int bcdExtType) {
+    private static char bcdToChar(byte b, @BcdExtendType int bcdExtType) {
         if (b < 0xa) {
             return (char) ('0' + b);
         }
@@ -1027,7 +1038,7 @@
         return extended.charAt(b - 0xa);
     }
 
-    private static int charToBCD(char c, int bcdExtType) {
+    private static int charToBCD(char c, @BcdExtendType int bcdExtType) {
         if ('0' <= c && c <= '9') {
             return c - '0';
         }
@@ -1134,7 +1145,7 @@
      *
      * @return BCD byte array
      */
-    public static byte[] numberToCalledPartyBCD(String number, int bcdExtType) {
+    public static byte[] numberToCalledPartyBCD(String number, @BcdExtendType int bcdExtType) {
         return numberToCalledPartyBCDHelper(number, false, bcdExtType);
     }
 
@@ -1143,7 +1154,7 @@
      * the return array.
      */
     private static byte[] numberToCalledPartyBCDHelper(
-            String number, boolean includeLength, int bcdExtType) {
+            String number, boolean includeLength, @BcdExtendType int bcdExtType) {
         int numberLenReal = number.length();
         int numberLenEffective = numberLenReal;
         boolean hasPlus = number.indexOf('+') != -1;
diff --git a/android/telephony/PhoneStateListener.java b/android/telephony/PhoneStateListener.java
index 0ee870a..0ff2982 100644
--- a/android/telephony/PhoneStateListener.java
+++ b/android/telephony/PhoneStateListener.java
@@ -61,9 +61,6 @@
     /**
      * Listen for changes to the network signal strength (cellular).
      * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
-     * <p>
      *
      * @see #onSignalStrengthChanged
      *
@@ -76,7 +73,8 @@
      * Listen for changes to the message-waiting indicator.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
      * <p>
      * Example: The status bar uses this to determine when to display the
      * voicemail icon.
@@ -89,7 +87,9 @@
      * Listen for changes to the call-forwarding indicator.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @see #onCallForwardingIndicatorChanged
      */
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
@@ -372,7 +372,7 @@
                         break;
                     case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
                         PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
-                            (List<PhysicalChannelConfig>)msg.obj);
+                                (List<PhysicalChannelConfig>)msg.obj);
                         break;
                 }
             }
@@ -430,8 +430,9 @@
      * Callback invoked when device call state changes.
      * @param state call state
      * @param phoneNumber call phone number. If application does not have
-     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
-     * string will be passed as an argument.
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission or carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
+     * passed as an argument.
      *
      * @see TelephonyManager#CALL_STATE_IDLE
      * @see TelephonyManager#CALL_STATE_RINGING
@@ -700,6 +701,10 @@
         public void onCarrierNetworkChange(boolean active) {
             send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
         }
+
+        public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+            send(LISTEN_PHYSICAL_CHANNEL_CONFIGURATION, 0, 0, configs);
+        }
     }
 
     IPhoneStateListener callback = new IPhoneStateListenerStub(this);
diff --git a/android/telephony/PhysicalChannelConfig.java b/android/telephony/PhysicalChannelConfig.java
index 651d68d..ce444dd 100644
--- a/android/telephony/PhysicalChannelConfig.java
+++ b/android/telephony/PhysicalChannelConfig.java
@@ -27,8 +27,9 @@
  */
 public final class PhysicalChannelConfig implements Parcelable {
 
+    // TODO(b/72993578) consolidate these enums in a central location.
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING})
+    @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN})
     public @interface ConnectionStatus {}
 
     /**
@@ -41,6 +42,9 @@
      */
     public static final int CONNECTION_SECONDARY_SERVING = 2;
 
+    /** Connection status is unknown. */
+    public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+
     /**
      * Connection status of the cell.
      *
@@ -86,6 +90,7 @@
      *
      * @see #CONNECTION_PRIMARY_SERVING
      * @see #CONNECTION_SECONDARY_SERVING
+     * @see #CONNECTION_UNKNOWN
      *
      * @return Connection status of the cell
      */
diff --git a/android/telephony/ServiceState.java b/android/telephony/ServiceState.java
index 77706e8..e971d08 100644
--- a/android/telephony/ServiceState.java
+++ b/android/telephony/ServiceState.java
@@ -18,15 +18,17 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -36,6 +38,7 @@
  *
  * <ul>
  *   <li>Service state: IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF
+ *   <li>Duplex mode: UNKNOWN, FDD, TDD
  *   <li>Roaming indicator
  *   <li>Operator name, short name and numeric id
  *   <li>Network selection mode
@@ -71,46 +74,25 @@
      */
     public static final int STATE_POWER_OFF = 3;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+    public @interface DuplexMode {}
+
     /**
-     * RIL level registration state values from ril.h
-     * ((const char **)response)[0] is registration state 0-6,
-     *              0 - Not registered, MT is not currently searching
-     *                  a new operator to register
-     *              1 - Registered, home network
-     *              2 - Not registered, but MT is currently searching
-     *                  a new operator to register
-     *              3 - Registration denied
-     *              4 - Unknown
-     *              5 - Registered, roaming
-     *             10 - Same as 0, but indicates that emergency calls
-     *                  are enabled.
-     *             12 - Same as 2, but indicates that emergency calls
-     *                  are enabled.
-     *             13 - Same as 3, but indicates that emergency calls
-     *                  are enabled.
-     *             14 - Same as 4, but indicates that emergency calls
-     *                  are enabled.
-     * @hide
+     * Duplex mode for the phone is unknown.
      */
-    public static final int RIL_REG_STATE_NOT_REG = 0;
-    /** @hide */
-    public static final int RIL_REG_STATE_HOME = 1;
-    /** @hide */
-    public static final int RIL_REG_STATE_SEARCHING = 2;
-    /** @hide */
-    public static final int RIL_REG_STATE_DENIED = 3;
-    /** @hide */
-    public static final int RIL_REG_STATE_UNKNOWN = 4;
-    /** @hide */
-    public static final int RIL_REG_STATE_ROAMING = 5;
-    /** @hide */
-    public static final int RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED = 10;
-    /** @hide */
-    public static final int RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED = 12;
-    /** @hide */
-    public static final int RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED = 13;
-    /** @hide */
-    public static final int RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED = 14;
+    public static final int DUPLEX_MODE_UNKNOWN = 0;
+
+    /**
+     * Duplex mode for the phone is frequency-division duplexing.
+     */
+    public static final int DUPLEX_MODE_FDD = 1;
+
+    /**
+     * Duplex mode for the phone is time-division duplexing.
+     */
+    public static final int DUPLEX_MODE_TDD = 2;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -196,7 +178,6 @@
 
     /**
      * Number of radio technologies for GSM, UMTS and CDMA.
-     * @hide
      */
     private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20;
 
@@ -210,22 +191,6 @@
                     | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
                     | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
 
-    /**
-     * Available registration states for GSM, UMTS and CDMA.
-     */
-    /** @hide */
-    public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING = 0;
-    /** @hide */
-    public static final int REGISTRATION_STATE_HOME_NETWORK = 1;
-    /** @hide */
-    public static final int REGISTRATION_STATE_NOT_REGISTERED_AND_SEARCHING = 2;
-    /** @hide */
-    public static final int REGISTRATION_STATE_REGISTRATION_DENIED = 3;
-    /** @hide */
-    public static final int REGISTRATION_STATE_UNKNOWN = 4;
-    /** @hide */
-    public static final int REGISTRATION_STATE_ROAMING = 5;
-
     private int mVoiceRegState = STATE_OUT_OF_SERVICE;
     private int mDataRegState = STATE_OUT_OF_SERVICE;
 
@@ -255,7 +220,7 @@
     public static final int ROAMING_TYPE_INTERNATIONAL = 3;
 
     /**
-     * Unknown ID. Could be returned by {@link #getNetworkId()} or {@link #getSystemId()}
+     * Unknown ID. Could be returned by {@link #getCdmaNetworkId()} or {@link #getCdmaSystemId()}
      */
     public static final int UNKNOWN_ID = -1;
 
@@ -286,6 +251,9 @@
 
     private boolean mIsUsingCarrierAggregation;
 
+    private int mChannelNumber;
+    private int[] mCellBandwidths = new int[0];
+
     /* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
      * Reference: 3GPP TS 36.104 5.4.3 */
     private int mLteEarfcnRsrpBoost = 0;
@@ -371,6 +339,8 @@
         mIsEmergencyOnly = s.mIsEmergencyOnly;
         mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
         mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
+        mChannelNumber = s.mChannelNumber;
+        mCellBandwidths = Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
         mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
         mNetworkRegistrationStates = new ArrayList<>(s.mNetworkRegistrationStates);
     }
@@ -405,6 +375,8 @@
         mLteEarfcnRsrpBoost = in.readInt();
         mNetworkRegistrationStates = new ArrayList<>();
         in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
+        mChannelNumber = in.readInt();
+        mCellBandwidths = in.createIntArray();
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -433,6 +405,8 @@
         out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
         out.writeInt(mLteEarfcnRsrpBoost);
         out.writeList(mNetworkRegistrationStates);
+        out.writeInt(mChannelNumber);
+        out.writeIntArray(mCellBandwidths);
     }
 
     public int describeContents() {
@@ -486,6 +460,46 @@
     }
 
     /**
+     * Get the current duplex mode
+     *
+     * @see #DUPLEX_MODE_UNKNOWN
+     * @see #DUPLEX_MODE_FDD
+     * @see #DUPLEX_MODE_TDD
+     *
+     * @return Current {@code DuplexMode} for the phone
+     */
+    @DuplexMode
+    public int getDuplexMode() {
+        // only support LTE duplex mode
+        if (!isLte(mRilDataRadioTechnology)) {
+            return DUPLEX_MODE_UNKNOWN;
+        }
+
+        int band = AccessNetworkUtils.getOperatingBandForEarfcn(mChannelNumber);
+        return AccessNetworkUtils.getDuplexModeForEutranBand(band);
+    }
+
+    /**
+     * Get the channel number of the current primary serving cell, or -1 if unknown
+     *
+     * <p>This is EARFCN for LTE, UARFCN for UMTS, and ARFCN for GSM.
+     *
+     * @return Channel number of primary serving cell
+     */
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * Get an array of cell bandwidths (kHz) for the current serving cells
+     *
+     * @return Current serving cell bandwidths
+     */
+    public int[] getCellBandwidths() {
+        return mCellBandwidths == null ? new int[0] : mCellBandwidths;
+    }
+
+    /**
      * Get current roaming indicator of phone
      * (note: not just decoding from TS 27.007 7.2)
      *
@@ -713,6 +727,8 @@
                 + (mDataRegState * 37)
                 + mVoiceRoamingType
                 + mDataRoamingType
+                + mChannelNumber
+                + Arrays.hashCode(mCellBandwidths)
                 + (mIsManualNetworkSelection ? 1 : 0)
                 + ((null == mVoiceOperatorAlphaLong) ? 0 : mVoiceOperatorAlphaLong.hashCode())
                 + ((null == mVoiceOperatorAlphaShort) ? 0 : mVoiceOperatorAlphaShort.hashCode())
@@ -745,6 +761,8 @@
                 && mIsManualNetworkSelection == s.mIsManualNetworkSelection
                 && mVoiceRoamingType == s.mVoiceRoamingType
                 && mDataRoamingType == s.mDataRoamingType
+                && mChannelNumber == s.mChannelNumber
+                && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
                 && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
                 && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
                 && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
@@ -874,6 +892,9 @@
             .append("(" + rilServiceStateToString(mVoiceRegState) + ")")
             .append(", mDataRegState=").append(mDataRegState)
             .append("(" + rilServiceStateToString(mDataRegState) + ")")
+            .append(", mChannelNumber=").append(mChannelNumber)
+            .append(", duplexMode()=").append(getDuplexMode())
+            .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
             .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
             .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
             .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
@@ -905,6 +926,8 @@
         mDataRegState = state;
         mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING;
         mDataRoamingType = ROAMING_TYPE_NOT_ROAMING;
+        mChannelNumber = -1;
+        mCellBandwidths = new int[0];
         mVoiceOperatorAlphaLong = null;
         mVoiceOperatorAlphaShort = null;
         mVoiceOperatorNumeric = null;
@@ -953,6 +976,16 @@
         if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setDataRegState=" + mDataRegState);
     }
 
+    /** @hide */
+    public void setCellBandwidths(int[] bandwidths) {
+        mCellBandwidths = bandwidths;
+    }
+
+    /** @hide */
+    public void setChannelNumber(int channelNumber) {
+        mChannelNumber = channelNumber;
+    }
+
     public void setRoaming(boolean roaming) {
         mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING);
         mDataRoamingType = mVoiceRoamingType;
@@ -1101,6 +1134,8 @@
         mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
         mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
         mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
+        mChannelNumber = m.getInt("ChannelNumber");
+        mCellBandwidths = m.getIntArray("CellBandwidths");
     }
 
     /**
@@ -1132,6 +1167,8 @@
         m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
         m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
         m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
+        m.putInt("ChannelNumber", mChannelNumber);
+        m.putIntArray("CellBandwidths", mCellBandwidths);
     }
 
     /** @hide */
@@ -1182,7 +1219,8 @@
     }
 
     /** @hide */
-    public void setSystemAndNetworkId(int systemId, int networkId) {
+    @TestApi
+    public void setCdmaSystemAndNetworkId(int systemId, int networkId) {
         this.mSystemId = systemId;
         this.mNetworkId = networkId;
     }
@@ -1251,6 +1289,84 @@
     }
 
     /** @hide */
+    public static int rilRadioTechnologyToAccessNetworkType(@RilRadioTechnology int rt) {
+        switch(rt) {
+            case RIL_RADIO_TECHNOLOGY_GPRS:
+            case RIL_RADIO_TECHNOLOGY_EDGE:
+            case RIL_RADIO_TECHNOLOGY_GSM:
+                return AccessNetworkType.GERAN;
+            case RIL_RADIO_TECHNOLOGY_UMTS:
+            case RIL_RADIO_TECHNOLOGY_HSDPA:
+            case RIL_RADIO_TECHNOLOGY_HSPAP:
+            case RIL_RADIO_TECHNOLOGY_HSUPA:
+            case RIL_RADIO_TECHNOLOGY_HSPA:
+            case RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                return AccessNetworkType.UTRAN;
+            case RIL_RADIO_TECHNOLOGY_IS95A:
+            case RIL_RADIO_TECHNOLOGY_IS95B:
+            case RIL_RADIO_TECHNOLOGY_1xRTT:
+            case RIL_RADIO_TECHNOLOGY_EVDO_0:
+            case RIL_RADIO_TECHNOLOGY_EVDO_A:
+            case RIL_RADIO_TECHNOLOGY_EVDO_B:
+            case RIL_RADIO_TECHNOLOGY_EHRPD:
+                return AccessNetworkType.CDMA2000;
+            case RIL_RADIO_TECHNOLOGY_LTE:
+            case RIL_RADIO_TECHNOLOGY_LTE_CA:
+                return AccessNetworkType.EUTRAN;
+            case RIL_RADIO_TECHNOLOGY_IWLAN:
+                return AccessNetworkType.IWLAN;
+            case RIL_RADIO_TECHNOLOGY_UNKNOWN:
+            default:
+                return AccessNetworkType.UNKNOWN;
+        }
+    }
+
+    /** @hide */
+    public static int networkTypeToRilRadioTechnology(int networkType) {
+        switch(networkType) {
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_GPRS;
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EDGE;
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_UMTS;
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA;
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA;
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSPA;
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_IS95A;
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0;
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A;
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B;
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
+            case TelephonyManager.NETWORK_TYPE_GSM:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_GSM;
+            case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA;
+            case TelephonyManager.NETWORK_TYPE_IWLAN:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+            case TelephonyManager.NETWORK_TYPE_LTE_CA:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
+            default:
+                return ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+        }
+    }
+
+
+    /** @hide */
     public int getDataNetworkType() {
         return rilRadioTechnologyToNetworkType(mRilDataRadioTechnology);
     }
@@ -1270,7 +1386,7 @@
      * within a wireless system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA NID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getNetworkId() {
+    public int getCdmaNetworkId() {
         return this.mNetworkId;
     }
 
@@ -1279,7 +1395,7 @@
      * system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA SID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getSystemId() {
+    public int getCdmaSystemId() {
         return this.mSystemId;
     }
 
@@ -1416,7 +1532,9 @@
      */
     @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates() {
-        return mNetworkRegistrationStates;
+        synchronized (mNetworkRegistrationStates) {
+            return new ArrayList<>(mNetworkRegistrationStates);
+        }
     }
 
     /**
@@ -1429,11 +1547,15 @@
     @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
         List<NetworkRegistrationState> list = new ArrayList<>();
-        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
-            if (networkRegistrationState.getTransportType() == transportType) {
-                list.add(networkRegistrationState);
+
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getTransportType() == transportType) {
+                    list.add(networkRegistrationState);
+                }
             }
         }
+
         return list;
     }
 
@@ -1447,12 +1569,36 @@
      */
     @SystemApi
     public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
-        for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
-            if (networkRegistrationState.getTransportType() == transportType
-                    && networkRegistrationState.getDomain() == domain) {
-                return networkRegistrationState;
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getTransportType() == transportType
+                        && networkRegistrationState.getDomain() == domain) {
+                    return networkRegistrationState;
+                }
             }
         }
+
         return null;
     }
+
+    /**
+     * @hide
+     */
+    public void addNetworkRegistrationState(NetworkRegistrationState regState) {
+        if (regState == null) return;
+
+        synchronized (mNetworkRegistrationStates) {
+            for (int i = 0; i < mNetworkRegistrationStates.size(); i++) {
+                NetworkRegistrationState curRegState = mNetworkRegistrationStates.get(i);
+                if (curRegState.getTransportType() == regState.getTransportType()
+                        && curRegState.getDomain() == regState.getDomain()) {
+                    mNetworkRegistrationStates.remove(i);
+                    break;
+                }
+            }
+
+            mNetworkRegistrationStates.add(regState);
+        }
+    }
+
 }
diff --git a/android/telephony/SignalStrength.java b/android/telephony/SignalStrength.java
index fc2ef27..4e56396 100644
--- a/android/telephony/SignalStrength.java
+++ b/android/telephony/SignalStrength.java
@@ -25,6 +25,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Contains phone signal strength related information.
@@ -35,15 +36,20 @@
     private static final boolean DBG = false;
 
     /** @hide */
-    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0
     /** @hide */
-    public static final int SIGNAL_STRENGTH_POOR = 1;
+    public static final int SIGNAL_STRENGTH_POOR
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_POOR; // = 1
     /** @hide */
-    public static final int SIGNAL_STRENGTH_MODERATE = 2;
+    public static final int SIGNAL_STRENGTH_MODERATE
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_MODERATE; // = 2
     /** @hide */
-    public static final int SIGNAL_STRENGTH_GOOD = 3;
+    public static final int SIGNAL_STRENGTH_GOOD
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_GOOD; // = 3
     /** @hide */
-    public static final int SIGNAL_STRENGTH_GREAT = 4;
+    public static final int SIGNAL_STRENGTH_GREAT
+            = TelephonyProtoEnums.SIGNAL_STRENGTH_GREAT; // = 4
     /** @hide */
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
     /** @hide */
@@ -57,7 +63,16 @@
      */
     public static final int INVALID = Integer.MAX_VALUE;
 
-    private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
+    private static final int LTE_RSRP_THRESHOLDS_NUM = 4;
+    private static final int MAX_LTE_RSRP = -44;
+    private static final int MIN_LTE_RSRP = -140;
+
+    private static final int WCDMA_RSCP_THRESHOLDS_NUM = 4;
+    private static final int MAX_WCDMA_RSCP = -24;
+    private static final int MIN_WCDMA_RSCP = -120;
+
+    /* The type of signal measurement */
+    private static final String MEASUMENT_TYPE_RSCP = "rscp";
 
     /** Parameters reported by the Radio */
     private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
@@ -72,7 +87,10 @@
     private int mLteRsrq;
     private int mLteRssnr;
     private int mLteCqi;
-    private int mTdScdmaRscp;
+    private int mTdScdmaRscp; // Valid values are -24...-120dBm or INVALID if unknown
+    private int mWcdmaSignalStrength;
+    private int mWcdmaRscpAsu;  // the WCDMA RSCP in ASU as reported from the HAL
+    private int mWcdmaRscp;     // the WCDMA RSCP in dBm
 
     /** Parameters from the framework */
     private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
@@ -81,9 +99,17 @@
                             // onSignalStrengthResult.
     private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
 
-    // The threshold of LTE RSRP for determining the display level of LTE signal bar.
+    // The threshold of LTE RSRP for determining the display level of LTE signal bar. Note that the
+    // min and max are fixed at MIN_LTE_RSRP (-140) and MAX_LTE_RSRP (-44).
     private int mLteRsrpThresholds[] = new int[LTE_RSRP_THRESHOLDS_NUM];
 
+    // The type of default measurement for determining the display level of WCDMA signal bar.
+    private String mWcdmaDefaultSignalMeasurement;
+
+    // The threshold of WCDMA RSCP for determining the display level of WCDMA signal bar. Note that
+    // the min and max are fixed at MIN_WCDMA_RSCP (-120) and MAX_WCDMA_RSCP (-24).
+    private int mWcdmaRscpThresholds[] = new int[WCDMA_RSCP_THRESHOLDS_NUM];
+
     /**
      * Create a new SignalStrength from a intent notifier Bundle
      *
@@ -133,10 +159,15 @@
         mLteRssnr = INVALID;
         mLteCqi = INVALID;
         mTdScdmaRscp = INVALID;
+        mWcdmaSignalStrength = 99;
+        mWcdmaRscp = INVALID;
+        mWcdmaRscpAsu = 255;
         mLteRsrpBoost = 0;
         mIsGsm = gsmFlag;
         mUseOnlyRsrpForLteLevel = false;
+        mWcdmaDefaultSignalMeasurement = "";
         setLteRsrpThresholds(getDefaultLteRsrpThresholds());
+        setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
     }
 
     /**
@@ -149,9 +180,10 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp,
+            int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscpAsu,
             // values Added by config
-            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
+            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp,
+            String wcdmaDefaultMeasurement) {
         mGsmSignalStrength = gsmSignalStrength;
         mGsmBitErrorRate = gsmBitErrorRate;
         mCdmaDbm = cdmaDbm;
@@ -165,15 +197,20 @@
         mLteRssnr = lteRssnr;
         mLteCqi = lteCqi;
         mTdScdmaRscp = INVALID;
+        mWcdmaSignalStrength = wcdmaSignalStrength;
+        mWcdmaRscpAsu = wcdmaRscpAsu;
+        mWcdmaRscp = wcdmaRscpAsu - 120;
         mLteRsrpBoost = lteRsrpBoost;
         mIsGsm = gsmFlag;
         mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
+        mWcdmaDefaultSignalMeasurement = wcdmaDefaultMeasurement;
         setLteRsrpThresholds(getDefaultLteRsrpThresholds());
+        setWcdmaRscpThresholds(getDefaultWcdmaRscpThresholds());
         if (DBG) log("initialize: " + toString());
     }
 
     /**
-     * Constructor for only values provided by Radio HAL
+     * Constructor for only values provided by Radio HAL V1.0
      *
      * @hide
      */
@@ -184,7 +221,23 @@
             int tdScdmaRscp) {
         this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 0, true, false);
+                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 99, INVALID, 0, true, false, "");
+    }
+
+    /**
+     * Constructor for only values provided by Radio HAL V1.2
+     *
+     * @hide
+     */
+    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+            int cdmaDbm, int cdmaEcio,
+            int evdoDbm, int evdoEcio, int evdoSnr,
+            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+            int tdScdmaRscp, int wcdmaSignalStrength, int wcdmaRscp) {
+        this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, wcdmaSignalStrength, wcdmaRscp, 0, true,
+                false, "");
     }
 
     /**
@@ -215,10 +268,15 @@
         mLteRssnr = s.mLteRssnr;
         mLteCqi = s.mLteCqi;
         mTdScdmaRscp = s.mTdScdmaRscp;
+        mWcdmaSignalStrength = s.mWcdmaSignalStrength;
+        mWcdmaRscpAsu = s.mWcdmaRscpAsu;
+        mWcdmaRscp = s.mWcdmaRscp;
         mLteRsrpBoost = s.mLteRsrpBoost;
         mIsGsm = s.mIsGsm;
         mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
+        mWcdmaDefaultSignalMeasurement = s.mWcdmaDefaultSignalMeasurement;
         setLteRsrpThresholds(s.mLteRsrpThresholds);
+        setWcdmaRscpThresholds(s.mWcdmaRscpThresholds);
     }
 
     /**
@@ -242,10 +300,15 @@
         mLteRssnr = in.readInt();
         mLteCqi = in.readInt();
         mTdScdmaRscp = in.readInt();
+        mWcdmaSignalStrength = in.readInt();
+        mWcdmaRscpAsu = in.readInt();
+        mWcdmaRscp = in.readInt();
         mLteRsrpBoost = in.readInt();
         mIsGsm = in.readBoolean();
         mUseOnlyRsrpForLteLevel = in.readBoolean();
+        mWcdmaDefaultSignalMeasurement = in.readString();
         in.readIntArray(mLteRsrpThresholds);
+        in.readIntArray(mWcdmaRscpThresholds);
     }
 
     /**
@@ -265,10 +328,15 @@
         out.writeInt(mLteRssnr);
         out.writeInt(mLteCqi);
         out.writeInt(mTdScdmaRscp);
+        out.writeInt(mWcdmaSignalStrength);
+        out.writeInt(mWcdmaRscpAsu);
+        out.writeInt(mWcdmaRscp);
         out.writeInt(mLteRsrpBoost);
         out.writeBoolean(mIsGsm);
         out.writeBoolean(mUseOnlyRsrpForLteLevel);
+        out.writeString(mWcdmaDefaultSignalMeasurement);
         out.writeIntArray(mLteRsrpThresholds);
+        out.writeIntArray(mWcdmaRscpThresholds);
     }
 
     /**
@@ -308,24 +376,34 @@
         if (DBG) log("Signal before validate=" + this);
         // TS 27.007 8.5
         mGsmSignalStrength = mGsmSignalStrength >= 0 ? mGsmSignalStrength : 99;
+        mWcdmaSignalStrength = (mWcdmaSignalStrength >= 0) ? mWcdmaSignalStrength : 99;
+        mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
         // BER no change;
 
+        // WCDMA RSCP valid values are -120 through -24 as defined in TS 27.007 8.69
+        // but are reported in ASU which is 0 through 96, so we do the conversion here
+        mWcdmaRscpAsu =
+                ((mWcdmaRscpAsu - 120 >= MIN_WCDMA_RSCP) && (mWcdmaRscpAsu - 120 <= MAX_WCDMA_RSCP))
+                ? mWcdmaRscpAsu : 255;
+        mWcdmaRscp = ((mWcdmaRscp >= MIN_WCDMA_RSCP) && (mWcdmaRscp <= MAX_WCDMA_RSCP))
+                ? mWcdmaRscp : INVALID;
+
         mCdmaDbm = mCdmaDbm > 0 ? -mCdmaDbm : -120;
-        mCdmaEcio = (mCdmaEcio > 0) ? -mCdmaEcio : -160;
+        mCdmaEcio = (mCdmaEcio >= 0) ? -mCdmaEcio : -160;
 
         mEvdoDbm = (mEvdoDbm > 0) ? -mEvdoDbm : -120;
-        mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -1;
-        mEvdoSnr = ((mEvdoSnr > 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
+        mEvdoEcio = (mEvdoEcio >= 0) ? -mEvdoEcio : -160;
+        mEvdoSnr = ((mEvdoSnr >= 0) && (mEvdoSnr <= 8)) ? mEvdoSnr : -1;
 
         // TS 36.214 Physical Layer Section 5.1.3, TS 36.331 RRC
-        mLteSignalStrength = (mLteSignalStrength >= 0) ? mLteSignalStrength : 99;
-        mLteRsrp = ((mLteRsrp >= 44) && (mLteRsrp <= 140)) ? -mLteRsrp : SignalStrength.INVALID;
+        mLteRsrp = ((-mLteRsrp >= MIN_LTE_RSRP) && (-mLteRsrp <= MAX_LTE_RSRP)) ? -mLteRsrp
+                                : SignalStrength.INVALID;
         mLteRsrq = ((mLteRsrq >= 3) && (mLteRsrq <= 20)) ? -mLteRsrq : SignalStrength.INVALID;
         mLteRssnr = ((mLteRssnr >= -200) && (mLteRssnr <= 300)) ? mLteRssnr
                 : SignalStrength.INVALID;
 
-        mTdScdmaRscp = ((mTdScdmaRscp >= 25) && (mTdScdmaRscp <= 120))
-                ? -mTdScdmaRscp : SignalStrength.INVALID;
+        mTdScdmaRscp = ((mTdScdmaRscp >= 0) && (mTdScdmaRscp <= 96))
+                ? (mTdScdmaRscp - 120) : SignalStrength.INVALID;
         // Cqi no change
         if (DBG) log("Signal after validate=" + this);
     }
@@ -363,6 +441,16 @@
     }
 
     /**
+     * @param defaultMeasurement sets the type of WCDMA default signal measurement
+     *
+     * Used by phone to determine default measurement type for calculation WCDMA signal level.
+     * @hide
+     */
+    public void setWcdmaDefaultSignalMeasurement(String defaultMeasurement) {
+        mWcdmaDefaultSignalMeasurement = defaultMeasurement;
+    }
+
+    /**
      * @param lteRsrpBoost - signal strength offset
      *
      * Used by phone to set the lte signal strength offset which will be
@@ -406,6 +494,23 @@
     }
 
     /**
+     * Sets the threshold array for determining the display level of WCDMA signal bar.
+     *
+     * @param wcdmaRscpThresholds int array for determining the display level.
+     *
+     * @hide
+     */
+    public void setWcdmaRscpThresholds(int[] wcdmaRscpThresholds) {
+        if ((wcdmaRscpThresholds == null)
+                || (wcdmaRscpThresholds.length != WCDMA_RSCP_THRESHOLDS_NUM)) {
+            Log.wtf(LOG_TAG, "setWcdmaRscpThresholds - wcdmaRscpThresholds is invalid.");
+            return;
+        }
+        System.arraycopy(wcdmaRscpThresholds, 0, mWcdmaRscpThresholds, 0,
+                WCDMA_RSCP_THRESHOLDS_NUM);
+    }
+
+    /**
      * Get the CDMA RSSI value in dBm
      */
     public int getCdmaDbm() {
@@ -496,6 +601,8 @@
                 asuLevel = getLteAsuLevel();
             } else if (mTdScdmaRscp != SignalStrength.INVALID) {
                 asuLevel = getTdScdmaAsuLevel();
+            } else if (mWcdmaRscp != SignalStrength.INVALID) {
+                asuLevel = getWcdmaAsuLevel();
             } else {
                 asuLevel = getGsmAsuLevel();
             }
@@ -529,7 +636,11 @@
             dBm = getLteDbm();
             if (dBm == INVALID) {
                 if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                    dBm = getGsmDbm();
+                    if (getWcdmaDbm() == INVALID) {
+                        dBm = getGsmDbm();
+                    } else {
+                        dBm = getWcdmaDbm();
+                    }
                 } else {
                     dBm = getTdScdmaDbm();
                 }
@@ -735,24 +846,29 @@
      */
     public int getLteLevel() {
         /*
-         * TS 36.214 Physical Layer Section 5.1.3 TS 36.331 RRC RSSI = received
-         * signal + noise RSRP = reference signal dBm RSRQ = quality of signal
-         * dB= Number of Resource blocksxRSRP/RSSI SNR = gain=signal/noise ratio
-         * = -10log P1/P2 dB
+         * TS 36.214 Physical Layer Section 5.1.3
+         * TS 36.331 RRC
+         *
+         * RSSI = received signal + noise
+         * RSRP = reference signal dBm
+         * RSRQ = quality of signal dB = Number of Resource blocks*RSRP/RSSI
+         * SNR = gain = signal/noise ratio = -10log P1/P2 dB
          */
         int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
 
-        if (mLteRsrp > mLteRsrpThresholds[5]) {
-            rsrpIconLevel = -1;
-        } else if (mLteRsrp >= (mLteRsrpThresholds[4] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+        if (mLteRsrp > MAX_LTE_RSRP || mLteRsrp < MIN_LTE_RSRP) {
+            if (mLteRsrp != INVALID) {
+                Log.wtf(LOG_TAG, "getLteLevel - invalid lte rsrp: mLteRsrp=" + mLteRsrp);
+            }
         } else if (mLteRsrp >= (mLteRsrpThresholds[3] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+            rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
         } else if (mLteRsrp >= (mLteRsrpThresholds[2] - mLteRsrpBoost)) {
-            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+            rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
         } else if (mLteRsrp >= (mLteRsrpThresholds[1] - mLteRsrpBoost)) {
+            rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+        } else if (mLteRsrp >= (mLteRsrpThresholds[0] - mLteRsrpBoost)) {
             rsrpIconLevel = SIGNAL_STRENGTH_POOR;
-        } else if (mLteRsrp >= mLteRsrpThresholds[0]) {
+        } else {
             rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         }
 
@@ -796,17 +912,21 @@
         if (rsrpIconLevel != -1) return rsrpIconLevel;
 
         /* Valid values are (0-63, 99) as defined in TS 36.331 */
+        // TODO the range here is probably supposed to be (0..31, 99). It's unclear if anyone relies
+        // on the current incorrect range check, so this will be fixed in a future release with more
+        // soak time
         if (mLteSignalStrength > 63) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
         else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
         else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
         else if (mLteSignalStrength >= 0) rssiIconLevel = SIGNAL_STRENGTH_POOR;
 
-        if (DBG) log("getLTELevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
+        if (DBG) log("getLteLevel - rssi:" + mLteSignalStrength + " rssiIconLevel:"
                 + rssiIconLevel);
         return rssiIconLevel;
 
     }
+
     /**
      * Get the LTE signal level as an asu value between 0..97, 99 is unknown
      * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
@@ -899,6 +1019,105 @@
         return tdScdmaAsuLevel;
     }
 
+    /**
+     * Gets WCDMA RSCP as a dbm value between -120 and -24, as defined in TS 27.007 8.69.
+     *
+     * @hide
+     */
+    public int getWcdmaRscp() {
+        return mWcdmaRscp;
+    }
+
+    /**
+     * Get the WCDMA signal level as an ASU value between 0-96, 255 is unknown
+     *
+     * @hide
+     */
+    public int getWcdmaAsuLevel() {
+        /*
+         * 3GPP 27.007 (Ver 10.3.0) Sec 8.69
+         * 0      -120 dBm or less
+         * 1      -119 dBm
+         * 2...95 -118... -25 dBm
+         * 96     -24 dBm or greater
+         * 255    not known or not detectable
+         */
+        final int wcdmaDbm = getWcdmaDbm();
+        int wcdmaAsuLevel = 255;
+        // validateInput will always give a valid range between -120 to -24 as per ril.h. so RSCP
+        // outside range is already set to INVALID
+        if (wcdmaDbm == SignalStrength.INVALID) wcdmaAsuLevel =  255;
+        else wcdmaAsuLevel = wcdmaDbm + 120;
+        if (DBG) log("Wcdma Asu level: " + wcdmaAsuLevel);
+        return wcdmaAsuLevel;
+    }
+
+    /**
+     * Gets WCDMA signal strength as a dbm value between -120 and -24, as defined in TS 27.007 8.69.
+     *
+     * @hide
+     */
+    public int getWcdmaDbm() {
+        return mWcdmaRscp;
+    }
+
+    /**
+     * Get WCDMA as level 0..4
+     *
+     * @hide
+     */
+    public int getWcdmaLevel() {
+        int level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+
+        if (mWcdmaDefaultSignalMeasurement == null) {
+            Log.wtf(LOG_TAG, "getWcdmaLevel - WCDMA default signal measurement is invalid.");
+            return level;
+        }
+
+        switch (mWcdmaDefaultSignalMeasurement) {
+            case MEASUMENT_TYPE_RSCP:
+                // RSCP valid values are (-120 through -24) as defined in TS 27.007 8.69
+                if (mWcdmaRscp < MIN_WCDMA_RSCP || mWcdmaRscp > MAX_WCDMA_RSCP) {
+                    if (mWcdmaRscp != INVALID) {
+                        Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSCP: mWcdmaRscp="
+                                + mWcdmaRscp);
+                    }
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[3]) {
+                    level = SIGNAL_STRENGTH_GREAT;
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[2]) {
+                    level = SIGNAL_STRENGTH_GOOD;
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[1]) {
+                    level = SIGNAL_STRENGTH_MODERATE;
+                } else if (mWcdmaRscp >= mWcdmaRscpThresholds[0]) {
+                    level = SIGNAL_STRENGTH_POOR;
+                }
+                if (DBG) log("getWcdmaLevel=" + level + " WcdmaRscp=" + mWcdmaRscp);
+                break;
+
+            default:
+                // RSSI valid values are (0..31) as defined in TS 27.007 8.5
+                if (mWcdmaSignalStrength < 0 || mWcdmaSignalStrength > 31) {
+                    if (mWcdmaSignalStrength != 99) {
+                        Log.wtf(LOG_TAG, "getWcdmaLevel - invalid WCDMA RSSI: mWcdmaSignalStrength="
+                                + mWcdmaSignalStrength);
+                    }
+                } else if (mWcdmaSignalStrength >= 18) {
+                    level = SIGNAL_STRENGTH_GREAT;
+                } else if (mWcdmaSignalStrength >= 13) {
+                    level = SIGNAL_STRENGTH_GOOD;
+                } else if (mWcdmaSignalStrength >= 8) {
+                    level = SIGNAL_STRENGTH_MODERATE;
+                } else if (mWcdmaSignalStrength >= 3) {
+                    level = SIGNAL_STRENGTH_POOR;
+                }
+                if (DBG) log("getWcdmaLevel=" + level + " WcdmaSignalStrength=" +
+                        mWcdmaSignalStrength);
+                break;
+
+        }
+        return level;
+    }
+
    /**
      * @return hash code
      */
@@ -911,8 +1130,11 @@
                 + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
                 + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
                 + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (mIsGsm ? 1 : 0)
-                + (mUseOnlyRsrpForLteLevel ? 1 : 0) + (Arrays.hashCode(mLteRsrpThresholds)));
+                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum)
+                + (mWcdmaSignalStrength * primeNum) + (mWcdmaRscpAsu * primeNum)
+                + (mWcdmaRscp * primeNum) + (mIsGsm ? 1 : 0) + (mUseOnlyRsrpForLteLevel ? 1 : 0)
+                + (Objects.hashCode(mWcdmaDefaultSignalMeasurement))
+                + (Arrays.hashCode(mLteRsrpThresholds)) + (Arrays.hashCode(mWcdmaRscpThresholds)));
     }
 
     /**
@@ -946,9 +1168,14 @@
                 && mLteCqi == s.mLteCqi
                 && mLteRsrpBoost == s.mLteRsrpBoost
                 && mTdScdmaRscp == s.mTdScdmaRscp
+                && mWcdmaSignalStrength == s.mWcdmaSignalStrength
+                && mWcdmaRscpAsu == s.mWcdmaRscpAsu
+                && mWcdmaRscp == s.mWcdmaRscp
                 && mIsGsm == s.mIsGsm
                 && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
-                && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds));
+                && Objects.equals(mWcdmaDefaultSignalMeasurement, s.mWcdmaDefaultSignalMeasurement)
+                && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds)
+                && Arrays.equals(mWcdmaRscpThresholds, s.mWcdmaRscpThresholds));
     }
 
     /**
@@ -971,10 +1198,15 @@
                 + " " + mLteCqi
                 + " " + mLteRsrpBoost
                 + " " + mTdScdmaRscp
+                + " " + mWcdmaSignalStrength
+                + " " + mWcdmaRscpAsu
+                + " " + mWcdmaRscp
                 + " " + (mIsGsm ? "gsm|lte" : "cdma")
                 + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
                          "use_rsrp_and_rssnr_for_lte_level")
-                + " " + (Arrays.toString(mLteRsrpThresholds)));
+                + " " + mWcdmaDefaultSignalMeasurement
+                + " " + (Arrays.toString(mLteRsrpThresholds))
+                + " " + (Arrays.toString(mWcdmaRscpThresholds)));
     }
 
     /** Returns the signal strength related to GSM. */
@@ -983,7 +1215,10 @@
         if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
             level = getTdScdmaLevel();
             if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                level = getGsmLevel();
+                level = getWcdmaLevel();
+                if (level == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
+                    level = getGsmLevel();
+                }
             }
         }
         return level;
@@ -1028,12 +1263,20 @@
         mLteCqi = m.getInt("LteCqi");
         mLteRsrpBoost = m.getInt("LteRsrpBoost");
         mTdScdmaRscp = m.getInt("TdScdma");
+        mWcdmaSignalStrength = m.getInt("WcdmaSignalStrength");
+        mWcdmaRscpAsu = m.getInt("WcdmaRscpAsu");
+        mWcdmaRscp = m.getInt("WcdmaRscp");
         mIsGsm = m.getBoolean("IsGsm");
         mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
+        mWcdmaDefaultSignalMeasurement = m.getString("WcdmaDefaultSignalMeasurement");
         ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
         for (int i = 0; i < lteRsrpThresholds.size(); i++) {
             mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
         }
+        ArrayList<Integer> wcdmaRscpThresholds = m.getIntegerArrayList("wcdmaRscpThresholds");
+        for (int i = 0; i < wcdmaRscpThresholds.size(); i++) {
+            mWcdmaRscpThresholds[i] = wcdmaRscpThresholds.get(i);
+        }
     }
 
     /**
@@ -1057,13 +1300,22 @@
         m.putInt("LteCqi", mLteCqi);
         m.putInt("LteRsrpBoost", mLteRsrpBoost);
         m.putInt("TdScdma", mTdScdmaRscp);
+        m.putInt("WcdmaSignalStrength", mWcdmaSignalStrength);
+        m.putInt("WcdmaRscpAsu", mWcdmaRscpAsu);
+        m.putInt("WcdmaRscp", mWcdmaRscp);
         m.putBoolean("IsGsm", mIsGsm);
         m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+        m.putString("WcdmaDefaultSignalMeasurement", mWcdmaDefaultSignalMeasurement);
         ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
         for (int value : mLteRsrpThresholds) {
             lteRsrpThresholds.add(value);
         }
         m.putIntegerArrayList("lteRsrpThresholds", lteRsrpThresholds);
+        ArrayList<Integer> wcdmaRscpThresholds = new ArrayList<Integer>();
+        for (int value : mWcdmaRscpThresholds) {
+            wcdmaRscpThresholds.add(value);
+        }
+        m.putIntegerArrayList("wcdmaRscpThresholds", wcdmaRscpThresholds);
     }
 
     /**
@@ -1077,6 +1329,16 @@
     }
 
     /**
+     * Gets the default threshold array for determining the display level of WCDMA signal bar.
+     *
+     * @return int array for determining the display level.
+     */
+    private int[] getDefaultWcdmaRscpThresholds() {
+        return CarrierConfigManager.getDefaultConfig().getIntArray(
+                CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
+    }
+
+    /**
      * log
      */
     private static void log(String s) {
diff --git a/android/telephony/SmsManager.java b/android/telephony/SmsManager.java
index 5d88cf0..38bc640 100644
--- a/android/telephony/SmsManager.java
+++ b/android/telephony/SmsManager.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
@@ -343,15 +344,16 @@
      * applications. Intended for internal carrier use only.
      * </p>
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#SEND_SMS} and
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
-     * privileges.
-     * </p>
+     * <p>Requires Permission: Both {@link android.Manifest.permission#SEND_SMS} and
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is
+     * the default IMS app (see
+     * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}).
      *
      * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
      */
     @SystemApi
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.SEND_SMS
@@ -395,6 +397,112 @@
     }
 
     /**
+     * Send a text based SMS with messaging options.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param expectMore is a boolean to indicate the sending messages through same link or not.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     * {@hide}
+     */
+    public void sendTextMessage(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent,
+            int priority, boolean expectMore, int validityPeriod) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                true /* persistMessage*/, priority, expectMore, validityPeriod);
+    }
+
+    private void sendTextMessageInternal(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
+            int priority, boolean expectMore, int validityPeriod) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (TextUtils.isEmpty(text)) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        if (priority < 0x00 || priority > 0x03) {
+            throw new IllegalArgumentException("Invalid priority");
+        }
+
+        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            throw new IllegalArgumentException("Invalid validity period");
+        }
+
+        try {
+             ISms iccISms = getISmsServiceOrThrow();
+            if (iccISms != null) {
+                iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(),
+                        ActivityThread.currentPackageName(), destinationAddress, scAddress, text,
+                        sentIntent, deliveryIntent, persistMessage,  priority, expectMore,
+                        validityPeriod);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Send a text based SMS without writing it into the SMS Provider.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+     * privileges.
+     * </p>
+     *
+     * @see #sendTextMessage(String, String, String, PendingIntent,
+     * PendingIntent, int, boolean, int)
+     * @hide
+     */
+    public void sendTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
+            boolean expectMore, int validityPeriod) {
+        sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+                false /* persistMessage */, priority, expectMore, validityPeriod);
+    }
+
+    /**
+     *
      * Inject an SMS PDU into the android application framework.
      *
      * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
@@ -552,6 +660,140 @@
     }
 
     /**
+     * Send a multi-part text based SMS with messaging options. The callee should have already
+     * divided the message into correctly sized parts by calling
+     * <code>divideMessage</code>.
+     *
+     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
+     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
+     *
+     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
+     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
+     * writes messages sent using this method to the SMS Provider (the default SMS app is always
+     * responsible for writing its sent messages to the SMS Provider). For information about
+     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *   the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been sent.
+     *   The result code will be <code>Activity.RESULT_OK</code> for success,
+     *   or one of these errors:<br>
+     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *   <code>RESULT_ERROR_NULL_PDU</code><br>
+     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+     *   the extra "errorCode" containing a radio technology specific value,
+     *   generally only useful for troubleshooting.<br>
+     *   The per-application based SMS control checks sentIntent. If sentIntent
+     *   is NULL the caller will be checked against all unknown applications,
+     *   which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been delivered
+     *   to the recipient.  The raw pdu of the status report is in the
+     *   extended data ("pdu").
+     * @param priority Priority level of the message
+     *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+     *  ---------------------------------
+     *  PRIORITY      | Level of Priority
+     *  ---------------------------------
+     *      '00'      |     Normal
+     *      '01'      |     Interactive
+     *      '10'      |     Urgent
+     *      '11'      |     Emergency
+     *  ----------------------------------
+     *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
+     * @param expectMore is a boolean to indicate the sending messages through same link or not.
+     * @param validityPeriod Validity Period of the message in mins.
+     *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+     *  Validity Period(Minimum) -> 5 mins
+     *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+     *  Any Other values included Negative considered as Invalid Validity Period of the message.
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * {@hide}
+     */
+    public void sendMultipartTextMessage(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+            int priority, boolean expectMore, int validityPeriod) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, true /* persistMessage*/);
+    }
+
+    private void sendMultipartTextMessageInternal(
+            String destinationAddress, String scAddress, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+        if (parts == null || parts.size() < 1) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        if (priority < 0x00 || priority > 0x03) {
+            throw new IllegalArgumentException("Invalid priority");
+        }
+
+        if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            throw new IllegalArgumentException("Invalid validity period");
+        }
+
+        if (parts.size() > 1) {
+            try {
+                 ISms iccISms = getISmsServiceOrThrow();
+                if (iccISms != null) {
+                    iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
+                            ActivityThread.currentPackageName(), destinationAddress, scAddress,
+                            parts, sentIntents, deliveryIntents, persistMessage, priority,
+                            expectMore, validityPeriod);
+                }
+            } catch (RemoteException ex) {
+                // ignore it
+            }
+        } else {
+            PendingIntent sentIntent = null;
+            PendingIntent deliveryIntent = null;
+            if (sentIntents != null && sentIntents.size() > 0) {
+                sentIntent = sentIntents.get(0);
+            }
+            if (deliveryIntents != null && deliveryIntents.size() > 0) {
+                deliveryIntent = deliveryIntents.get(0);
+            }
+            sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
+                    sentIntent, deliveryIntent, persistMessage, priority, expectMore,
+                    validityPeriod);
+        }
+    }
+
+    /**
+     * Send a multi-part text based SMS without writing it into the SMS Provider.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
+     * privileges.
+     * </p>
+     *
+     * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
+     * ArrayList, int, boolean, int)
+     * @hide
+     **/
+    public void sendMultipartTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, List<String> parts,
+            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
+            int priority, boolean expectMore, int validityPeriod) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
+                deliveryIntents, false /* persistMessage*/, priority, expectMore,
+                validityPeriod);
+    }
+
+   /**
      * Send a data based SMS to a specific application port.
      *
      * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -1014,7 +1256,7 @@
      *   <code>getAllMessagesFromIcc</code>
      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
      */
-    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+    private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
         ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
         if (records != null) {
             int count = records.size();
@@ -1022,7 +1264,8 @@
                 SmsRawData data = records.get(i);
                 // List contains all records, including "free" records (null)
                 if (data != null) {
-                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+                            getSubscriptionId());
                     if (sms != null) {
                         messages.add(sms);
                     }
@@ -1134,7 +1377,11 @@
 
     // SMS send failure result codes
 
-    /** No error. {@hide}*/
+    /**
+     * No error.
+     * @hide
+     */
+    @SystemApi
     static public final int RESULT_ERROR_NONE    = 0;
     /** Generic failure cause */
     static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
@@ -1146,12 +1393,113 @@
     static public final int RESULT_ERROR_NO_SERVICE         = 4;
     /** Failed because we reached the sending queue limit. */
     static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
-    /** Failed because FDN is enabled. {@hide} */
+    /**
+     * Failed because FDN is enabled.
+     * @hide
+     */
+    @SystemApi
     static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
     /** Failed because user denied the sending of this short code. */
     static public final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
     /** Failed because the user has denied this app ever send premium short codes. */
     static public final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+    /**
+     * Failed because the radio was not available
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_RADIO_NOT_AVAILABLE = 9;
+    /**
+     * Failed because of network rejection
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NETWORK_REJECT = 10;
+    /**
+     * Failed because of invalid arguments
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_ARGUMENTS = 11;
+    /**
+     * Failed because of an invalid state
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_STATE = 12;
+    /**
+     * Failed because there is no memory
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NO_MEMORY = 13;
+    /**
+     * Failed because the sms format is not valid
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_SMS_FORMAT = 14;
+    /**
+     * Failed because of a system error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_SYSTEM_ERROR = 15;
+    /**
+     * Failed because of a modem error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_MODEM_ERROR = 16;
+    /**
+     * Failed because of a network error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NETWORK_ERROR = 17;
+    /**
+     * Failed because of an encoding error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_ENCODING_ERROR = 18;
+    /**
+     * Failed because of an invalid smsc address
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INVALID_SMSC_ADDRESS = 19;
+    /**
+     * Failed because the operation is not allowed
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_OPERATION_NOT_ALLOWED = 20;
+    /**
+     * Failed because of an internal error
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_INTERNAL_ERROR = 21;
+    /**
+     * Failed because there are no resources
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_NO_RESOURCES = 22;
+    /**
+     * Failed because the operation was cancelled
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_CANCELLED = 23;
+    /**
+     * Failed because the request is not supported
+     * @hide
+     */
+    @SystemApi
+    static public final int RESULT_REQUEST_NOT_SUPPORTED = 24;
+
 
     static private final String PHONE_PACKAGE_NAME = "com.android.phone";
 
diff --git a/android/telephony/SmsMessage.java b/android/telephony/SmsMessage.java
index a6dbc06..57f89e3 100644
--- a/android/telephony/SmsMessage.java
+++ b/android/telephony/SmsMessage.java
@@ -18,8 +18,8 @@
 
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
+import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.app.PendingIntent;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.text.TextUtils;
@@ -207,7 +207,10 @@
      */
     public static SmsMessage createFromPdu(byte[] pdu, String format) {
         SmsMessageBase wrappedMessage;
-
+        if (pdu == null) {
+            Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
+            return null;
+        }
         if (SmsConstants.FORMAT_3GPP2.equals(format)) {
             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
         } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
@@ -277,6 +280,31 @@
     }
 
     /**
+     * Create an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS record. This should be index in ArrayList
+     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param data Record data.
+     * @param subId Subscription Id of the SMS
+     * @return An SmsMessage representing the record.
+     *
+     * @hide
+     */
+    public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) {
+        SmsMessageBase wrappedMessage;
+
+        if (isCdmaVoice(subId)) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+                    index, data);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+                    index, data);
+        }
+
+        return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+    }
+
+    /**
      * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
      * length in bytes (not hex chars) less the SMSC header
      *
@@ -544,8 +572,16 @@
 
     /**
      * Returns the originating address (sender) of this SMS message in String
-     * form or null if unavailable
+     * form or null if unavailable.
+     *
+     * <p>If the address is a GSM-formatted address, it will be in a format specified by 3GPP
+     * 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2
+     * C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the
+     * should be careful to avoid assumptions about the returned content.
+     *
+     * @return a String representation of the address; null if unavailable.
      */
+    @Nullable
     public String getOriginatingAddress() {
         return mWrappedSmsMessage.getOriginatingAddress();
     }
@@ -828,6 +864,7 @@
          int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
          return (PHONE_TYPE_CDMA == activePhone);
    }
+
     /**
      * Decide if the carrier supports long SMS.
      * {@hide}
diff --git a/android/telephony/SubscriptionInfo.java b/android/telephony/SubscriptionInfo.java
index 38408fe..936505c 100644
--- a/android/telephony/SubscriptionInfo.java
+++ b/android/telephony/SubscriptionInfo.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -34,6 +35,8 @@
 import android.util.DisplayMetrics;
 
 import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A Parcelable class for Subscription Information.
@@ -332,12 +335,7 @@
         return this.mCountryIso;
     }
 
-    /**
-     * @return whether the subscription is an embedded one.
-     * @hide
-     *
-     * TODO(b/35851809): Make this public.
-     */
+    /** @return whether the subscription is an eUICC one. */
     public boolean isEmbedded() {
         return this.mIsEmbedded;
     }
@@ -351,9 +349,9 @@
      * @return whether the app is authorized to manage this subscription per its metadata.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
-     *
-     * TODO(b/35851809): Make this public.
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public boolean canManageSubscription(Context context) {
         return canManageSubscription(context, context.getPackageName());
     }
@@ -367,7 +365,9 @@
      * @return whether the app is authorized to manage this subscription per its metadata.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public boolean canManageSubscription(Context context, String packageName) {
         if (!isEmbedded()) {
             throw new UnsupportedOperationException("Not an embedded subscription");
@@ -395,14 +395,14 @@
      * @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription.
      * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
-    public @Nullable UiccAccessRule[] getAccessRules() {
+    @SystemApi
+    public @Nullable List<UiccAccessRule> getAccessRules() {
         if (!isEmbedded()) {
             throw new UnsupportedOperationException("Not an embedded subscription");
         }
-        return mAccessRules;
+        if (mAccessRules == null) return null;
+        return Arrays.asList(mAccessRules);
     }
 
     /**
diff --git a/android/telephony/SubscriptionManager.java b/android/telephony/SubscriptionManager.java
index debf43d..754fe68 100644
--- a/android/telephony/SubscriptionManager.java
+++ b/android/telephony/SubscriptionManager.java
@@ -25,11 +25,13 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.BroadcastOptions;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -41,7 +43,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.DisplayMetrics;
 
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -58,9 +59,6 @@
 /**
  * SubscriptionManager is the application interface to SubscriptionController
  * and provides information about the current Telephony Subscriptions.
- * <p>
- * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise
- * specified.
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 public class SubscriptionManager {
@@ -507,7 +505,7 @@
     public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
 
     private final Context mContext;
-    private INetworkPolicyManager mNetworkPolicy;
+    private volatile INetworkPolicyManager mNetworkPolicy;
 
     /**
      * A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -612,9 +610,9 @@
      *                 onSubscriptionsChanged overridden.
      */
     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
-        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         if (DBG) {
-            logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+            logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
                     + " listener=" + listener);
         }
         try {
@@ -623,7 +621,7 @@
             ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                     "telephony.registry"));
             if (tr != null) {
-                tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+                tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
             }
         } catch (RemoteException ex) {
             // Should not happen
@@ -659,9 +657,15 @@
     /**
      * Get the active SubscriptionInfo with the input subId.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @param subId The unique SubscriptionInfo key in database.
      * @return SubscriptionInfo, maybe null if its not active.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
         if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
         if (!isValidSubscriptionId(subId)) {
@@ -715,9 +719,16 @@
 
     /**
      * Get the active SubscriptionInfo associated with the slotIndex
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @param slotIndex the slot which the subscription is inserted
      * @return SubscriptionInfo, maybe null if its not active
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
         if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
         if (!isValidSlotIndex(slotIndex)) {
@@ -769,6 +780,11 @@
      * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
      * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
+     * to the calling app are returned.
+     *
      * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
      * <ul>
      * <li>
@@ -785,6 +801,8 @@
      * </li>
      * </ul>
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
@@ -822,10 +840,13 @@
      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </ul>
-     * @hide
      *
-     * TODO(b/35851809): Make this a SystemApi.
+     * <p>
+     * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
+     * for #getAvailableSubscriptionInfoList to be invoked.
+     * @hide
      */
+    @SystemApi
     public List<SubscriptionInfo> getAvailableSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
@@ -863,9 +884,6 @@
      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </ul>
-     * @hide
-     *
-     * TODO(b/35851809): Make this public.
      */
     public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
@@ -891,9 +909,8 @@
      *
      * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
     public void requestEmbeddedSubscriptionInfoListRefresh() {
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
@@ -928,10 +945,18 @@
     }
 
     /**
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, the count will include
+     * only those subscriptions accessible to the caller.
+     *
      * @return the current number of active subscriptions. There is no guarantee the value
      * returned by this method will be the same as the length of the list returned by
      * {@link #getActiveSubscriptionInfoList}.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getActiveSubscriptionInfoCount() {
         int result = 0;
 
@@ -1769,7 +1794,7 @@
             @DurationMillisLong long timeoutMillis) {
         try {
             final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
-            mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
+            getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
                     timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1803,7 +1828,7 @@
             @DurationMillisLong long timeoutMillis) {
         try {
             final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
-            mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
+            getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
                     timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1892,4 +1917,53 @@
         options.setTemporaryAppWhitelistDuration(TimeUnit.MINUTES.toMillis(1));
         mContext.sendBroadcast(intent, null, options.toBundle());
     }
+
+    /**
+     * Checks whether the app with the given context is authorized to manage the given subscription
+     * according to its metadata. Only supported for embedded subscriptions (if
+     * {@code SubscriptionInfo#isEmbedded} returns true).
+     *
+     * @param info The subscription to check.
+     * @return whether the app is authorized to manage this subscription per its metadata.
+     * @throws IllegalArgumentException if this subscription is not embedded.
+     */
+    public boolean canManageSubscription(SubscriptionInfo info) {
+        return canManageSubscription(info, mContext.getPackageName());
+    }
+
+    /**
+     * Checks whether the given app is authorized to manage the given subscription. An app can only
+     * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the
+     * {@link android.telephony.SubscriptionInfo} with the access status.
+     * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded}
+     * returns true).
+     *
+     * @param info The subscription to check.
+     * @param packageName Package name of the app to check.
+     * @return whether the app is authorized to manage this subscription per its access rules.
+     * @throws IllegalArgumentException if this subscription is not embedded.
+     * @hide
+     */
+    public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
+        if (!info.isEmbedded()) {
+            throw new IllegalArgumentException("Not an embedded subscription");
+        }
+        if (info.getAccessRules() == null) {
+            return false;
+        }
+        PackageManager packageManager = mContext.getPackageManager();
+        PackageInfo packageInfo;
+        try {
+            packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("Unknown package: " + packageName, e);
+        }
+        for (UiccAccessRule rule : info.getAccessRules()) {
+            if (rule.getCarrierPrivilegeStatus(packageInfo)
+                    == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/android/telephony/SubscriptionPlan.java b/android/telephony/SubscriptionPlan.java
index 9411652..4ffb70b 100644
--- a/android/telephony/SubscriptionPlan.java
+++ b/android/telephony/SubscriptionPlan.java
@@ -34,6 +34,7 @@
 import java.time.Period;
 import java.time.ZonedDateTime;
 import java.util.Iterator;
+import java.util.Objects;
 
 /**
  * Description of a billing relationship plan between a carrier and a specific
@@ -124,6 +125,27 @@
                 .append("}").toString();
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
+                dataUsageBytes, dataUsageTime);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof SubscriptionPlan) {
+            final SubscriptionPlan other = (SubscriptionPlan) obj;
+            return Objects.equals(cycleRule, other.cycleRule)
+                    && Objects.equals(title, other.title)
+                    && Objects.equals(summary, other.summary)
+                    && dataLimitBytes == other.dataLimitBytes
+                    && dataLimitBehavior == other.dataLimitBehavior
+                    && dataUsageBytes == other.dataUsageBytes
+                    && dataUsageTime == other.dataUsageTime;
+        }
+        return false;
+    }
+
     public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
         @Override
         public SubscriptionPlan createFromParcel(Parcel source) {
diff --git a/android/telephony/TelephonyManager.java b/android/telephony/TelephonyManager.java
index 0a6d960..e15d35b 100644
--- a/android/telephony/TelephonyManager.java
+++ b/android/telephony/TelephonyManager.java
@@ -23,9 +23,11 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.WorkerThread;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
@@ -34,10 +36,12 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.BatteryStats;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -48,12 +52,13 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
-import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ITelecomService;
@@ -72,6 +77,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -107,6 +113,13 @@
             BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
 
     /**
+     * The process name of the Phone app as well as many other apps that use this process name, such
+     * as settings and vendor components.
+     * @hide
+     */
+    public static final String PHONE_PROCESS_NAME = "com.android.phone";
+
+    /**
      * The allowed states of Wi-Fi calling.
      *
      * @hide
@@ -203,6 +216,10 @@
         return ActivityThread.currentOpPackageName();
     }
 
+    private boolean isSystemProcess() {
+        return Process.myUid() == Process.SYSTEM_UID;
+    }
+
     /**
      * Returns the multi SIM variant
      * Returns DSDS for Dual SIM Dual Standby
@@ -903,6 +920,61 @@
     public static final String EVENT_CALL_FORWARDED =
             "android.telephony.event.EVENT_CALL_FORWARDED";
 
+    /**
+     * {@link android.telecom.Connection} event used to indicate that a supplementary service
+     * notification has been received.
+     * <p>
+     * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+     * The {@link Bundle} parameter is expected to include the following extras:
+     * <ul>
+     *     <li>{@link #EXTRA_NOTIFICATION_TYPE} - the notification type.</li>
+     *     <li>{@link #EXTRA_NOTIFICATION_CODE} - the notification code.</li>
+     *     <li>{@link #EXTRA_NOTIFICATION_MESSAGE} - human-readable message associated with the
+     *     supplementary service notification.</li>
+     * </ul>
+     * @hide
+     */
+    public static final String EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION =
+            "android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION";
+
+    /**
+     * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+     * the type of supplementary service notification which occurred.
+     * Will be either
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_1}
+     * or
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_2}
+     * <p>
+     * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_TYPE =
+            "android.telephony.extra.NOTIFICATION_TYPE";
+
+    /**
+     * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+     * the supplementary service notification which occurred.
+     * <p>
+     * Depending on the {@link #EXTRA_NOTIFICATION_TYPE}, the code will be one of the {@code CODE_*}
+     * codes defined in {@link com.android.internal.telephony.gsm.SuppServiceNotification}.
+     * <p>
+     * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_CODE =
+            "android.telephony.extra.NOTIFICATION_CODE";
+
+    /**
+     * {@link CharSequence} extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION}
+     * which contains a human-readable message which can be displayed to the user for the
+     * supplementary service notification.
+     * <p>
+     * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+     * @hide
+     */
+    public static final String EXTRA_NOTIFICATION_MESSAGE =
+            "android.telephony.extra.NOTIFICATION_MESSAGE";
+
     /* Visual voicemail protocols */
 
     /**
@@ -996,6 +1068,13 @@
     public static final int UNKNOWN_CARRIER_ID = -1;
 
     /**
+     * An unknown carrier id list version.
+     * @hide
+     */
+    @TestApi
+    public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1;
+
+    /**
      * Broadcast Action: The subscription carrier identity has changed.
      * This intent could be sent on the following events:
      * <ul>
@@ -1023,7 +1102,7 @@
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
-     * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+     * the updated carrier id {@link TelephonyManager#getSimCarrierId()} of
      * the current subscription.
      * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
      * the carrier cannot be identified.
@@ -1033,7 +1112,7 @@
     /**
      * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
      * indicates the updated carrier name of the current subscription.
-     * {@see TelephonyManager#getSubscriptionCarrierName()}
+     * {@see TelephonyManager#getSimCarrierIdName()}
      * <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
      * usually the brand name of the subsidiary (e.g. T-Mobile).
      */
@@ -1055,7 +1134,11 @@
      * Returns the software version number for the device, for example,
      * the IMEI/SV for GSM phones. Return null if the software version is
      * not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceSoftwareVersion() {
         return getDeviceSoftwareVersion(getSlotIndex());
@@ -1087,10 +1170,14 @@
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
      * or ESN for CDMA phones. Return null if device ID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId() {
         try {
@@ -1109,12 +1196,16 @@
      * Returns the unique device ID of a subscription, for example, the IMEI for
      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which deviceID is returned
      *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
@@ -1133,7 +1224,11 @@
     /**
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei() {
         return getImei(getSlotIndex());
@@ -1143,8 +1238,12 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which IMEI is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1161,7 +1260,11 @@
 
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid() {
         return getMeid(getSlotIndex());
@@ -1170,8 +1273,12 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which MEID is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1188,10 +1295,11 @@
 
     /**
      * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getNai() {
         return getNaiBySubscriberId(getSubId());
@@ -1688,10 +1796,17 @@
      * invalid subscription ID is pinned to the TelephonyManager, the returned config will contain
      * default values.
      *
+     * <p>This method may take several seconds to complete, so it should only be called from a
+     * worker thread.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @see CarrierConfigManager#getConfigForSubId(int)
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PersistableBundle getCarrierConfig() {
@@ -1776,47 +1891,52 @@
     /*
      * When adding a network type to the list below, make sure to add the correct icon to
      * MobileSignalController.mapIconSets().
+     * Do not add negative types.
      */
     /** Network type is unknown */
-    public static final int NETWORK_TYPE_UNKNOWN = 0;
+    public static final int NETWORK_TYPE_UNKNOWN = TelephonyProtoEnums.NETWORK_TYPE_UNKNOWN; // = 0.
     /** Current network is GPRS */
-    public static final int NETWORK_TYPE_GPRS = 1;
+    public static final int NETWORK_TYPE_GPRS = TelephonyProtoEnums.NETWORK_TYPE_GPRS; // = 1.
     /** Current network is EDGE */
-    public static final int NETWORK_TYPE_EDGE = 2;
+    public static final int NETWORK_TYPE_EDGE = TelephonyProtoEnums.NETWORK_TYPE_EDGE; // = 2.
     /** Current network is UMTS */
-    public static final int NETWORK_TYPE_UMTS = 3;
+    public static final int NETWORK_TYPE_UMTS = TelephonyProtoEnums.NETWORK_TYPE_UMTS; // = 3.
     /** Current network is CDMA: Either IS95A or IS95B*/
-    public static final int NETWORK_TYPE_CDMA = 4;
+    public static final int NETWORK_TYPE_CDMA = TelephonyProtoEnums.NETWORK_TYPE_CDMA; // = 4.
     /** Current network is EVDO revision 0*/
-    public static final int NETWORK_TYPE_EVDO_0 = 5;
+    public static final int NETWORK_TYPE_EVDO_0 = TelephonyProtoEnums.NETWORK_TYPE_EVDO_0; // = 5.
     /** Current network is EVDO revision A*/
-    public static final int NETWORK_TYPE_EVDO_A = 6;
+    public static final int NETWORK_TYPE_EVDO_A = TelephonyProtoEnums.NETWORK_TYPE_EVDO_A; // = 6.
     /** Current network is 1xRTT*/
-    public static final int NETWORK_TYPE_1xRTT = 7;
+    public static final int NETWORK_TYPE_1xRTT = TelephonyProtoEnums.NETWORK_TYPE_1XRTT; // = 7.
     /** Current network is HSDPA */
-    public static final int NETWORK_TYPE_HSDPA = 8;
+    public static final int NETWORK_TYPE_HSDPA = TelephonyProtoEnums.NETWORK_TYPE_HSDPA; // = 8.
     /** Current network is HSUPA */
-    public static final int NETWORK_TYPE_HSUPA = 9;
+    public static final int NETWORK_TYPE_HSUPA = TelephonyProtoEnums.NETWORK_TYPE_HSUPA; // = 9.
     /** Current network is HSPA */
-    public static final int NETWORK_TYPE_HSPA = 10;
+    public static final int NETWORK_TYPE_HSPA = TelephonyProtoEnums.NETWORK_TYPE_HSPA; // = 10.
     /** Current network is iDen */
-    public static final int NETWORK_TYPE_IDEN = 11;
+    public static final int NETWORK_TYPE_IDEN = TelephonyProtoEnums.NETWORK_TYPE_IDEN; // = 11.
     /** Current network is EVDO revision B*/
-    public static final int NETWORK_TYPE_EVDO_B = 12;
+    public static final int NETWORK_TYPE_EVDO_B = TelephonyProtoEnums.NETWORK_TYPE_EVDO_B; // = 12.
     /** Current network is LTE */
-    public static final int NETWORK_TYPE_LTE = 13;
+    public static final int NETWORK_TYPE_LTE = TelephonyProtoEnums.NETWORK_TYPE_LTE; // = 13.
     /** Current network is eHRPD */
-    public static final int NETWORK_TYPE_EHRPD = 14;
+    public static final int NETWORK_TYPE_EHRPD = TelephonyProtoEnums.NETWORK_TYPE_EHRPD; // = 14.
     /** Current network is HSPA+ */
-    public static final int NETWORK_TYPE_HSPAP = 15;
+    public static final int NETWORK_TYPE_HSPAP = TelephonyProtoEnums.NETWORK_TYPE_HSPAP; // = 15.
     /** Current network is GSM */
-    public static final int NETWORK_TYPE_GSM = 16;
+    public static final int NETWORK_TYPE_GSM = TelephonyProtoEnums.NETWORK_TYPE_GSM; // = 16.
     /** Current network is TD_SCDMA */
-    public static final int NETWORK_TYPE_TD_SCDMA = 17;
+    public static final int NETWORK_TYPE_TD_SCDMA =
+            TelephonyProtoEnums.NETWORK_TYPE_TD_SCDMA; // = 17.
     /** Current network is IWLAN */
-    public static final int NETWORK_TYPE_IWLAN = 18;
+    public static final int NETWORK_TYPE_IWLAN = TelephonyProtoEnums.NETWORK_TYPE_IWLAN; // = 18.
     /** Current network is LTE_CA {@hide} */
-    public static final int NETWORK_TYPE_LTE_CA = 19;
+    public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+
+    /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
+    public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
     /**
      * @return the NETWORK_TYPE_xxxx for current data connection.
      */
@@ -1890,6 +2010,9 @@
      * If this object has been created with {@link #createForSubscriptionId}, applies to the given
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return the network type
      *
      * @see #NETWORK_TYPE_UNKNOWN
@@ -1909,6 +2032,7 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
@@ -1943,7 +2067,11 @@
 
     /**
      * Returns the NETWORK_TYPE_xxxx for voice
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getVoiceNetworkType() {
         return getVoiceNetworkType(getSubId());
@@ -2529,7 +2657,11 @@
     /**
      * Returns the serial number of the SIM, if applicable. Return null if it is
      * unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSimSerialNumber() {
          return getSimSerialNumber(getSubId());
@@ -2654,7 +2786,11 @@
     /**
      * Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
      * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSubscriberId() {
         return getSubscriberId(getSubId());
@@ -2697,18 +2833,17 @@
      * @return ImsiEncryptionInfo Carrier specific information that will be used to encrypt the
      *         IMSI and IMPI. This includes the public key and the key identifier. This information
      *         will be stored in the device keystore. The system will return a null when no key was
-     *         found, and the carrier does not require a key. The system will throw the following
-     *         exceptions:
-     *         1. IllegalArgumentException when an invalid key is sent.
-     *         2. RuntimeException if the key is required but not found; and also if there was an
-     *         internal exception.
+     *         found, and the carrier does not require a key. The system will throw
+     *         IllegalArgumentException when an invalid key is sent or when key is required but
+     *         not found.
      * @hide
      */
     public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null) {
-                throw new RuntimeException("IMSI error: Subscriber Info is null");
+                Rlog.e(TAG,"IMSI error: Subscriber Info is null");
+                return null;
             }
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
             if (keyType != KEY_TYPE_EPDG && keyType != KEY_TYPE_WLAN) {
@@ -2716,20 +2851,18 @@
             }
             ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption(
                     subId, keyType, mContext.getOpPackageName());
-            if (imsiEncryptionInfo  == null
-                    && isImsiEncryptionRequired(subId, keyType)) {
+            if (imsiEncryptionInfo == null && isImsiEncryptionRequired(subId, keyType)) {
                 Rlog.e(TAG, "IMSI error: key is required but not found");
-                throw new RuntimeException("IMSI error: key is required but not found");
+                throw new IllegalArgumentException("IMSI error: key is required but not found");
             }
             return imsiEncryptionInfo;
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
-            throw new RuntimeException("IMSI error: Remote Exception");
         } catch (NullPointerException ex) {
             // This could happen before phone restarts due to crashing
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
-            throw new RuntimeException("IMSI error: Null Pointer exception");
         }
+        return null;
     }
 
     /**
@@ -2745,17 +2878,19 @@
         try {
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null) {
-                throw new RuntimeException("IMSI error: Subscriber Info is null");
+                Rlog.e(TAG, "IMSI error: Subscriber Info is null");
+                if (!isSystemProcess()) {
+                    throw new RuntimeException("IMSI error: Subscriber Info is null");
+                }
+                return;
             }
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
             info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
-            throw new RuntimeException("IMSI error: Remote Exception");
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
-            throw new RuntimeException("IMSI error: Null Pointer exception");
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -2822,7 +2957,11 @@
     /**
      * Returns the Group Identifier Level1 for a GSM phone.
      * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getGroupIdLevel1() {
         try {
@@ -2863,9 +3002,15 @@
     /**
      * Returns the phone number string for line 1, for example, the MSISDN
      * for a GSM phone. Return null if it is unavailable.
-     * <p>
-     * The default SMS app can also use this.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_SMS,
@@ -2920,8 +3065,7 @@
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
@@ -2937,8 +3081,7 @@
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
@@ -3057,7 +3200,11 @@
 
     /**
      * Returns the voice mail number. Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailNumber() {
         return getVoiceMailNumber(getSubId());
@@ -3118,8 +3265,7 @@
     /**
      * Sets the voice mail number.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
@@ -3131,8 +3277,7 @@
     /**
      * Sets the voicemail number for the given subscriber.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param alphaTag The alpha tag to display.
@@ -3153,9 +3298,9 @@
     /**
      * Enables or disables the visual voicemail client for a phone account.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or
-     * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle the phone account to change the client state
      * @param enabled the new state of the client
@@ -3218,11 +3363,15 @@
      * to the TelephonyManager. Returns {@code null} when there is no package responsible for
      * processing visual voicemail for the subscription.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      * @see VisualVoicemailService
      */
     @Nullable
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVisualVoicemailPackageName() {
         try {
@@ -3465,15 +3614,14 @@
       * Sets the voice activation state
       *
       * <p>Requires Permission:
-      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-      * Or the calling app has carrier privileges.
+      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+      * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
       *
       * @param activationState The voice activation state
       * @see #SIM_ACTIVATION_STATE_UNKNOWN
       * @see #SIM_ACTIVATION_STATE_ACTIVATING
       * @see #SIM_ACTIVATION_STATE_ACTIVATED
       * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-      * @see #hasCarrierPrivileges
       * @hide
       */
     @SystemApi
@@ -3486,8 +3634,8 @@
      * Sets the voice activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param activationState The voice activation state of the given subscriber.
@@ -3495,7 +3643,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3513,8 +3660,8 @@
      * Sets the data activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param activationState The data activation state
      * @see #SIM_ACTIVATION_STATE_UNKNOWN
@@ -3522,7 +3669,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3535,8 +3681,8 @@
      * Sets the data activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param activationState The data activation state of the given subscriber.
@@ -3545,7 +3691,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3563,15 +3708,14 @@
      * Returns the voice activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return voiceActivationState
      * @see #SIM_ACTIVATION_STATE_UNKNOWN
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3584,8 +3728,8 @@
      * Returns the voice activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      *
@@ -3594,7 +3738,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3613,8 +3756,8 @@
      * Returns the data activation state
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return dataActivationState for the given subscriber
      * @see #SIM_ACTIVATION_STATE_UNKNOWN
@@ -3622,7 +3765,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @SystemApi
@@ -3635,8 +3777,8 @@
      * Returns the data activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * Or the calling app has carrier privileges.
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      *
@@ -3646,7 +3788,6 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
-     * @see #hasCarrierPrivileges
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3694,7 +3835,11 @@
     /**
      * Retrieves the alphabetic identifier associated with the voice
      * mail number.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailAlphaTag() {
         return getVoiceMailAlphaTag(getSubId());
@@ -3723,27 +3868,29 @@
     }
 
     /**
-     * Send the special dialer code. The IPC caller must be the current default dialer or has
-     * carrier privileges.
-     * @see #hasCarrierPrivileges
+     * Send the special dialer code. The IPC caller must be the current default dialer or have
+     * carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param inputCode The special dialer code to send
      *
      * @throws SecurityException if the caller does not have carrier privileges or is not the
      *         current default dialer
-     *
-     * @throws IllegalStateException if telephony service is unavailable.
      */
     public void sendDialerSpecialCode(String inputCode) {
         try {
             final ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                if (!isSystemProcess()) {
+                    throw new RuntimeException("Telephony service unavailable");
+                }
+                return;
+            }
             telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowFromSystemServer();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            throw new IllegalStateException("Telephony service unavailable");
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -3997,6 +4144,9 @@
      * To unregister a listener, pass the listener object and set the
      * events argument to
      * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
      *
      * @param listener The {@link PhoneStateListener} object to register
      *                 (or unregister)
@@ -4250,8 +4400,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return an IccOpenLogicalChannelResponse object.
@@ -4268,8 +4418,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
@@ -4285,8 +4435,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param AID Application id. See ETSI 102.221 and 101.220.
@@ -4311,8 +4461,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param channel is the channel id to be closed as retruned by a successful
      *            iccOpenLogicalChannel.
@@ -4328,8 +4478,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param channel is the channel id to be closed as retruned by a successful
@@ -4354,8 +4504,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
@@ -4381,8 +4531,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param channel is the channel id to be closed as returned by a successful
@@ -4417,8 +4567,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param cla Class of the APDU command.
      * @param instruction Instruction of the APDU command.
@@ -4442,8 +4592,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param cla Class of the APDU command.
@@ -4462,7 +4612,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.iccTransmitApduBasicChannel(subId, cla,
+                return telephony.iccTransmitApduBasicChannel(subId, getOpPackageName(), cla,
                     instruction, p1, p2, p3, data);
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -4474,8 +4624,8 @@
      * Returns the response APDU for a command APDU sent through SIM_IO.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param fileID
      * @param command
@@ -4494,8 +4644,8 @@
      * Returns the response APDU for a command APDU sent through SIM_IO.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param fileID
@@ -4523,8 +4673,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param content String containing SAT/USAT response in hexadecimal
      *                format starting with command tag. See TS 102 223 for
@@ -4541,8 +4691,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param content String containing SAT/USAT response in hexadecimal
@@ -4567,10 +4717,10 @@
     /**
      * Read one of the NV items defined in com.android.internal.telephony.RadioNVItems.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param itemID the ID of the item to read.
      * @return the NV item as a String, or null on any failure.
@@ -4593,10 +4743,10 @@
     /**
      * Write one of the NV items defined in com.android.internal.telephony.RadioNVItems.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param itemID the ID of the item to read.
      * @param itemValue the value to write, as a String.
@@ -4620,10 +4770,10 @@
     /**
      * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param preferredRoamingList byte array containing the new PRL.
      * @return true on success; false on any failure.
@@ -4647,10 +4797,10 @@
      * Perform the specified type of NV config reset. The radio will be taken offline
      * and the device must be rebooted after the operation. Used for device
      * configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
@@ -4745,7 +4895,7 @@
     }
 
     /**
-     * Sets the telephony property with the value specified.
+     * Sets a per-phone telephony property with the value specified.
      *
      * @hide
      */
@@ -4789,12 +4939,24 @@
             return;
         }
 
-        Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
-                " property=" + property + " value: " + value + " propVal=" + propVal);
         SystemProperties.set(property, propVal);
     }
 
     /**
+     * Sets a global telephony property with the value specified.
+     *
+     * @hide
+     */
+    public static void setTelephonyProperty(String property, String value) {
+        if (value == null) {
+            value = "";
+        }
+        Rlog.d(TAG, "setTelephonyProperty: success" + " property=" +
+                property + " value: " + value);
+        SystemProperties.set(property, value);
+    }
+
+    /**
      * Convenience function for retrieving a value from the secure settings
      * value list as an integer.  Note that internally setting values are
      * always stored as strings; this function converts the string to an
@@ -4853,10 +5015,10 @@
         String v = android.provider.Settings.Global.getString(cr, name);
 
         if (index == Integer.MAX_VALUE) {
-            throw new RuntimeException("putIntAtIndex index == MAX_VALUE index=" + index);
+            throw new IllegalArgumentException("putIntAtIndex index == MAX_VALUE index=" + index);
         }
         if (index < 0) {
-            throw new RuntimeException("putIntAtIndex index < 0 index=" + index);
+            throw new IllegalArgumentException("putIntAtIndex index < 0 index=" + index);
         }
         if (v != null) {
             valArray = v.split(",");
@@ -4883,7 +5045,7 @@
     }
 
     /**
-     * Gets the telephony property.
+     * Gets a per-phone telephony property.
      *
      * @hide
      */
@@ -4899,6 +5061,19 @@
         return propVal == null ? defaultVal : propVal;
     }
 
+    /**
+     * Gets a global telephony property.
+     *
+     * See also getTelephonyProperty(phoneId, property, defaultVal). Most telephony properties are
+     * per-phone.
+     *
+     * @hide
+     */
+    public static String getTelephonyProperty(String property, String defaultVal) {
+        String propVal = SystemProperties.get(property);
+        return propVal == null ? defaultVal : propVal;
+    }
+
     /** @hide */
     public int getSimCount() {
         // FIXME Need to get it from Telephony Dev Controller when that gets implemented!
@@ -4951,28 +5126,6 @@
         }
     }
 
-    /**
-     * Returns the response of ISIM Authetification through RIL.
-     * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
-     * @return the response of ISIM Authetification, or null if not available
-     * @hide
-     * @deprecated
-     * @see getIccAuthentication with appType=PhoneConstants.APPTYPE_ISIM
-     */
-    public String getIsimChallengeResponse(String nonce){
-        try {
-            IPhoneSubInfo info = getSubscriberInfo();
-            if (info == null)
-                return null;
-            return info.getIsimChallengeResponse(nonce);
-        } catch (RemoteException ex) {
-            return null;
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            return null;
-        }
-    }
-
     // ICC SIM Application Types
     /** UICC application type is SIM */
     public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
@@ -4995,8 +5148,8 @@
      * Returns the response of authentication for the default subscription.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges or READ_PRIVILEGED_PHONE_STATE
-     * permission.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
      * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
@@ -5004,9 +5157,10 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      */
+    // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
+    // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
+    // it's not public API.
     public String getIccAuthentication(int appType, int authType, String data) {
         return getIccAuthentication(getSubId(), appType, authType, data);
     }
@@ -5015,7 +5169,7 @@
      * Returns the response of USIM Authentication for specified subId.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges.
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId subscription ID used for authentication
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
@@ -5024,8 +5178,6 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      * @hide
      */
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
@@ -5046,8 +5198,12 @@
      * Returns an array of Forbidden PLMNs from the USIM App
      * Returns null if the query fails.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return an array of forbidden PLMNs or null if not available
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String[] getForbiddenPlmns() {
       return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
@@ -5068,7 +5224,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return null;
-            return telephony.getForbiddenPlmns(subId, appType);
+            return telephony.getForbiddenPlmns(subId, appType, mContext.getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -5094,57 +5250,60 @@
         }
     }
 
-    /** @hide */
-    @IntDef({ImsFeature.EMERGENCY_MMTEL, ImsFeature.MMTEL, ImsFeature.RCS})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Feature {}
-
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
-     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
-     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
-     * @param callback Listener that will send updates to ImsManager when there are updates to
-     * ImsServiceController.
-     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
-     * it is unavailable.
+     * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
+     * status updates, if not already enabled.
      * @hide
      */
-    public @Nullable IImsMMTelFeature getImsMMTelFeatureAndListen(int slotIndex,
-            IImsServiceFeatureCallback callback) {
+    public void enableIms(int slotId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getMMTelFeatureAndListen(slotIndex, callback);
+                telephony.enableIms(slotId);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsMMTelFeatureAndListen, RemoteException: "
+            Rlog.e(TAG, "enableIms, RemoteException: "
                     + e.getMessage());
         }
-        return null;
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id and MMTel
-     * feature for emergency calling or {@link null} if the service is not available. If an
-     * MMTelFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
-     * listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMMTelFeature} for.
+     * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature
+     * status updates to disabled.
+     * @hide
+     */
+    public void disableIms(int slotId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.disableIms(slotId);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "disableIms, RemoteException: "
+                    + e.getMessage());
+        }
+    }
+
+    /**
+     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
+     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
+     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
      * @param callback Listener that will send updates to ImsManager when there are updates to
      * ImsServiceController.
-     * @return {@link IImsMMTelFeature} interface for the feature specified or {@code null} if
+     * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
      * it is unavailable.
      * @hide
      */
-    public @Nullable IImsMMTelFeature getImsEmergencyMMTelFeatureAndListen(int slotIndex,
+    public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
             IImsServiceFeatureCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getEmergencyMMTelFeatureAndListen(slotIndex, callback);
+                return telephony.getMmTelFeatureAndListen(slotIndex, callback);
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsEmergencyMMTelFeatureAndListen, RemoteException: "
+            Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
                     + e.getMessage());
         }
         return null;
@@ -5196,6 +5355,42 @@
     }
 
     /**
+     * @return the {@IImsConfig} interface that corresponds with the slot index and feature.
+     * @param slotIndex The SIM slot corresponding to the ImsService ImsConfig is active for.
+     * @param feature An integer indicating the feature that we wish to get the ImsConfig for.
+     * Corresponds to features defined in ImsFeature.
+     * @hide
+     */
+    public @Nullable IImsConfig getImsConfig(int slotIndex, int feature) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getImsConfig(slotIndex, feature);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "getImsRegistration, RemoteException: " + e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * @return true if the IMS resolver is busy resolving a binding and should not be considered
+     * available, false if the IMS resolver is idle.
+     * @hide
+     */
+    public boolean isResolvingImsBinding() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isResolvingImsBinding();
+            }
+        } catch (RemoteException e) {
+            Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
+        }
+        return false;
+    }
+
+    /**
      * Set IMS registration state
      *
      * @param Registration state
@@ -5213,10 +5408,10 @@
     /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the preferred network type, defined in RILConstants.java.
      * @hide
@@ -5236,11 +5431,12 @@
 
     /**
      * Sets the network selection mode to automatic.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setNetworkSelectionModeAutomatic() {
         try {
@@ -5256,15 +5452,14 @@
     }
 
     /**
-     * Perform a radio scan and return the list of avialble networks.
+     * Perform a radio scan and return the list of available networks.
      *
      * The return value is a list of the OperatorInfo of the networks found. Note that this
      * scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @hide
      * TODO: Add an overload that takes no args.
@@ -5288,33 +5483,49 @@
      * This method is asynchronous, so the network scan results will be returned by callback.
      * The returned NetworkScan will contain a callback method which can be used to stop the scan.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
-            NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor,
+            TelephonyScanManager.NetworkScanCallback callback) {
         synchronized (this) {
             if (mTelephonyScanManager == null) {
                 mTelephonyScanManager = new TelephonyScanManager();
             }
         }
-        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, callback);
+        return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+    }
+
+    /**
+     * @deprecated
+     * Use {@link
+     * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
+     */
+    @Deprecated
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public NetworkScan requestNetworkScan(
+        NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
+        return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
     }
 
     /**
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param operatorNumeric the PLMN ID of the network to select.
      * @param persistSelection whether the selection will persist until reboot. If true, only allows
@@ -5322,6 +5533,7 @@
      * normal network selection next time.
      * @return true on success; false on any failure.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
         try {
@@ -5341,10 +5553,10 @@
     /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type, defined in RILConstants.java.
@@ -5368,9 +5580,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      */
@@ -5381,9 +5591,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      * @hide
@@ -5473,8 +5681,7 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
@@ -5491,8 +5698,7 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param brand The brand name to display/set.
@@ -6146,39 +6352,44 @@
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
      * <p>Requires Permission:
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
-     *     calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param enable Whether to enable mobile data.
      *
-     * @see #hasCarrierPrivileges
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
      */
-    @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(enable);
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
      * @hide
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+     * @deprecated use {@link #setDataEnabled(boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(int subId, boolean enable) {
-        setUserMobileDataEnabled(subId, enable);
+        try {
+            Log.d(TAG, "setDataEnabled: enabled=" + enable);
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setUserDataEnabled(subId, enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+        }
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @SystemApi
     @Deprecated
     public boolean getDataEnabled() {
-        return isUserMobileDataEnabled();
+        return isDataEnabled();
     }
 
     /**
@@ -6191,30 +6402,35 @@
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * <p>Note that this does not take into account any data restrictions that may be present on the
      * calling app. Such restrictions may be inspected with
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
-     *
-     * @see #hasCarrierPrivileges
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
      */
-    @Deprecated
     public boolean isDataEnabled() {
-        return isUserMobileDataEnabled();
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
     public boolean getDataEnabled(int subId) {
-        return isUserMobileDataEnabled(subId);
+        boolean retVal = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                retVal = telephony.isUserDataEnabled(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+        } catch (NullPointerException e) {
+        }
+        return retVal;
     }
 
     /** @hide */
@@ -6327,84 +6543,106 @@
         return false;
     }
 
-   /**
-    * Returns the IMS Registration Status
-    * @hide
-    */
-   public boolean isImsRegistered() {
-       try {
-           ITelephony telephony = getITelephony();
-           if (telephony == null)
-               return false;
-           return telephony.isImsRegistered();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
     /**
-     * Returns the IMS Registration Status for a particular Subscription ID
+     * Returns the IMS Registration Status for a particular Subscription ID.
      *
      * @param subId Subscription ID
      * @return true if IMS status is registered, false if the IMS status is not registered or a
      * RemoteException occurred.
-     *
      * @hide
      */
     public boolean isImsRegistered(int subId) {
-       try {
-           return getITelephony().isImsRegisteredForSubscriber(subId);
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-    }
-
-    /**
-     * Returns the Status of Volte
-     * @hide
-     */
-    public boolean isVolteAvailable() {
-       try {
-           return getITelephony().isVolteAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
-    /**
-     * Returns the Status of video telephony (VT)
-     * @hide
-     */
-    public boolean isVideoTelephonyAvailable() {
         try {
-            return getITelephony().isVideoTelephonyAvailable();
-        } catch (RemoteException ex) {
-            return false;
-        } catch (NullPointerException ex) {
+            return getITelephony().isImsRegistered(subId);
+        } catch (RemoteException | NullPointerException ex) {
             return false;
         }
     }
 
     /**
-     * Returns the Status of Wi-Fi Calling
+     * Returns the IMS Registration Status for a particular Subscription ID, which is determined
+     * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an
+     * invalid subscription ID is used during creation, will the default subscription ID will be
+     * used.
+     *
+     * @return true if IMS status is registered, false if the IMS status is not registered or a
+     * RemoteException occurred.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    public boolean isImsRegistered() {
+       try {
+           return getITelephony().isImsRegistered(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
+           return false;
+       }
+    }
+
+    /**
+     * The current status of Voice over LTE for the subscription associated with this instance when
+     * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used.
+     * @return true if Voice over LTE is available or false if it is unavailable or unknown.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    public boolean isVolteAvailable() {
+        try {
+            return getITelephony().isVolteAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * The availability of Video Telephony (VT) for the subscription ID specified when this instance
+     * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used. To query the
+     * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
+     * @return true if VT is available, or false if it is unavailable or unknown.
+     * @hide
+     */
+    public boolean isVideoTelephonyAvailable() {
+        try {
+            return getITelephony().isVideoTelephonyAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
+     * @param subId the subscription ID.
+     * @return true if VoWiFi is available, or false if it is unavailable or unknown.
      * @hide
      */
     public boolean isWifiCallingAvailable() {
        try {
-           return getITelephony().isWifiCallingAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
+           return getITelephony().isWifiCallingAvailable(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
            return false;
        }
    }
 
+    /**
+     * The technology that IMS is registered for for the MMTEL feature.
+     * @param subId subscription ID to get IMS registration technology for.
+     * @return The IMS registration technology that IMS is registered to for the MMTEL feature.
+     * Valid return results are:
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
+     *  result is unavailable.
+     *  @hide
+     */
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+        try {
+            return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+        } catch (RemoteException ex) {
+            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+        }
+    }
+
    /**
     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
     *
@@ -6586,11 +6824,48 @@
      * @hide
      */
     public void setBasebandVersionForPhone(int phoneId, String version) {
+        setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, version);
+    }
+
+    /**
+     * Get baseband version for the default phone.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    public String getBasebandVersion() {
+        int phoneId = getPhoneId();
+        return getBasebandVersionForPhone(phoneId);
+    }
+
+    /**
+     * Get baseband version for the default phone using the legacy approach.
+     * This change was added in P, to ensure backward compatiblity.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    private String getBasebandVersionLegacy(int phoneId) {
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
             String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
                     ((phoneId == 0) ? "" : Integer.toString(phoneId));
-            SystemProperties.set(prop, version);
+            return SystemProperties.get(prop);
         }
+        return null;
+    }
+
+    /**
+     * Get baseband version by phone id.
+     *
+     * @return baseband version.
+     * @hide
+     */
+    public String getBasebandVersionForPhone(int phoneId) {
+        String version = getBasebandVersionLegacy(phoneId);
+        if (version != null && !version.isEmpty()) {
+            setBasebandVersionForPhone(phoneId, version);
+        }
+        return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_BASEBAND_VERSION, "");
     }
 
     /**
@@ -6911,7 +7186,11 @@
 
     /**
      * Returns the current {@link ServiceState} information.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
@@ -6957,14 +7236,14 @@
     /**
      * Sets the per-account voicemail ringtone.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
      * voicemail ringtone.
      * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -7002,14 +7281,14 @@
     /**
      * Sets the per-account preference whether vibration is enabled for voicemail notifications.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
      * voicemail vibration setting.
      * @param enabled Whether to enable or disable vibration for voicemail notifications from a
      * specific PhoneAccount.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -7030,8 +7309,8 @@
     /**
      * Returns carrier id of the current subscription.
      * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
-     * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
-     * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android
+     * platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
      * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
      *
      * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
@@ -7039,45 +7318,39 @@
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
-     * @throws IllegalStateException if telephony service is unavailable.
      */
-    public int getAndroidCarrierIdForSubscription() {
+    public int getSimCarrierId() {
         try {
             ITelephony service = getITelephony();
-            return service.getSubscriptionCarrierId(getSubId());
+            if (service != null) {
+                return service.getSubscriptionCarrierId(getSubId());
+            }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing.
-            throw new IllegalStateException("Telephony service unavailable");
         }
         return UNKNOWN_CARRIER_ID;
     }
 
     /**
-     * Returns carrier name of the current subscription.
-     * <p>Carrier name is a user-facing name of carrier id
-     * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+     * Returns carrier id name of the current subscription.
+     * <p>Carrier id name is a user-facing name of carrier id
+     * {@link #getSimCarrierId()}, usually the brand name of the subsidiary
      * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
      * should have a single carrier name. Carrier name is not a canonical identity,
-     * use {@link #getAndroidCarrierIdForSubscription()} instead.
+     * use {@link #getSimCarrierId()} instead.
      * <p>The returned carrier name is unlocalized.
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
-     * @throws IllegalStateException if telephony service is unavailable.
      */
-    public CharSequence getAndroidCarrierNameForSubscription() {
+    public CharSequence getSimCarrierIdName() {
         try {
             ITelephony service = getITelephony();
-            return service.getSubscriptionCarrierName(getSubId());
+            if (service != null) {
+                return service.getSubscriptionCarrierName(getSubId());
+            }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing.
-            throw new IllegalStateException("Telephony service unavailable");
         }
         return null;
     }
@@ -7430,60 +7703,12 @@
     }
 
     /**
-     * Turns mobile data on or off.
-     * If the {@link TelephonyManager} object has been created with
-     * {@link #createForSubscriptionId}, this API applies to the given subId.
-     * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires Permission:
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
-     *     calling app has carrier privileges.
-     *
-     * @param enable Whether to enable mobile data.
-     *
-     * @see #hasCarrierPrivileges
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setUserMobileDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
-    }
-
-    /**
-     * Returns whether mobile data is enabled or not per user setting. There are other factors
-     * that could disable mobile data, but they are not considered here.
-     *
-     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
-     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires one of the following permissions:
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
-     *
-     * <p>Note that this does not take into account any data restrictions that may be present on the
-     * calling app. Such restrictions may be inspected with
-     * {@link ConnectivityManager#getRestrictBackgroundStatus}.
-     *
-     * @return true if mobile data is enabled.
-     *
-     * @see #hasCarrierPrivileges
-     */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE
-    })
-    public boolean isUserMobileDataEnabled() {
-        return isUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
-    }
-
-    /**
      * @hide
-     * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
-     * policyDataEnabled etc to give a final decision.
+     * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
+     * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
+     * capable of using.
      */
-    public boolean isMobileDataEnabled() {
+    public boolean isDataCapable() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -7498,31 +7723,142 @@
     }
 
     /**
-     * Utility class of {@link #isUserMobileDataEnabled()};
+     * In this mode, modem will not send specified indications when screen is off.
+     * @hide
      */
-    private boolean isUserMobileDataEnabled(int subId) {
-        boolean retVal = false;
+    public static final int INDICATION_UPDATE_MODE_NORMAL                   = 1;
+
+    /**
+     * In this mode, modem will still send specified indications when screen is off.
+     * @hide
+     */
+    public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF        = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "INDICATION_UPDATE_MODE_" }, value = {
+            INDICATION_UPDATE_MODE_NORMAL,
+            INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndicationUpdateMode{}
+
+    /**
+     * The indication for signal strength update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_SIGNAL_STRENGTH               = 0x1;
+
+    /**
+     * The indication for full network state update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_FULL_NETWORK_STATE            = 0x2;
+
+    /**
+     * The indication for data call dormancy changed update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED    = 0x4;
+
+    /**
+     * The indication for link capacity estimate update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE        = 0x8;
+
+    /**
+     * The indication for physical channel config update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG       = 0x10;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
+            INDICATION_FILTER_SIGNAL_STRENGTH,
+            INDICATION_FILTER_FULL_NETWORK_STATE,
+            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
+            INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
+            INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IndicationFilters{}
+
+    /**
+     * Sets radio indication update mode. This can be used to control the behavior of indication
+     * update from modem to Android frameworks. For example, by default several indication updates
+     * are turned off when screen is off, but in some special cases (e.g. carkit is connected but
+     * screen is off) we want to turn on those indications even when the screen is off.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX.
+     * @see #INDICATION_FILTER_SIGNAL_STRENGTH
+     * @see #INDICATION_FILTER_FULL_NETWORK_STATE
+     * @see #INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+     * @param updateMode The voice activation state
+     * @see #INDICATION_UPDATE_MODE_NORMAL
+     * @see #INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRadioIndicationUpdateMode(@IndicationFilters int filters,
+                                             @IndicationUpdateMode int updateMode) {
         try {
             ITelephony telephony = getITelephony();
-            if (telephony != null)
-                retVal = telephony.isUserDataEnabled(subId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
-        } catch (NullPointerException e) {
+            if (telephony != null) {
+                telephony.setRadioIndicationUpdateMode(getSubId(), filters, updateMode);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
-        return retVal;
     }
 
-    /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    private void setUserMobileDataEnabled(int subId, boolean enable) {
+    /**
+     * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
+     * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
+     * (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
+            String gid2, String plmn, String spn) {
         try {
-            Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
             ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.setUserDataEnabled(subId, enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+            if (telephony != null) {
+                telephony.setCarrierTestOverride(
+                        getSubId(), mccmnc, imsi, iccid, gid1, gid2, plmn, spn);
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
         }
     }
+
+    /**
+     * A test API to return installed carrier id list version
+     *
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @hide
+     */
+    @TestApi
+    public int getCarrierIdListVersion() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCarrierIdListVersion(getSubId());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return UNKNOWN_CARRIER_ID_LIST_VERSION;
+    }
 }
diff --git a/android/telephony/TelephonyScanManager.java b/android/telephony/TelephonyScanManager.java
index c182e34..96ff332 100644
--- a/android/telephony/TelephonyScanManager.java
+++ b/android/telephony/TelephonyScanManager.java
@@ -33,6 +33,7 @@
 import android.util.SparseArray;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import com.android.internal.telephony.ITelephony;
 
@@ -55,8 +56,10 @@
 
     /**
      * The caller of
-     * {@link TelephonyManager#requestNetworkScan(NetworkScanRequest, NetworkScanCallback)} should
-     * implement and provide this callback so that the scan results or errors can be returned.
+     * {@link
+     * TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)}
+     * should implement and provide this callback so that the scan results or errors can be
+     * returned.
      */
     public static abstract class NetworkScanCallback {
         /** Returns the scan results to the user, this callback will be called multiple times. */
@@ -83,10 +86,13 @@
 
     private static class NetworkScanInfo {
         private final NetworkScanRequest mRequest;
+        private final Executor mExecutor;
         private final NetworkScanCallback mCallback;
 
-        NetworkScanInfo(NetworkScanRequest request, NetworkScanCallback callback) {
+        NetworkScanInfo(
+                NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
             mRequest = request;
+            mExecutor = executor;
             mCallback = callback;
         }
     }
@@ -112,10 +118,15 @@
                         "Failed to find NetworkScanInfo with id " + message.arg2);
                 }
                 NetworkScanCallback callback = nsi.mCallback;
+                Executor executor = nsi.mExecutor;
                 if (callback == null) {
                     throw new RuntimeException(
                         "Failed to find NetworkScanCallback with id " + message.arg2);
                 }
+                if (executor == null) {
+                    throw new RuntimeException(
+                        "Failed to find Executor with id " + message.arg2);
+                }
 
                 switch (message.what) {
                     case CALLBACK_SCAN_RESULTS:
@@ -126,21 +137,31 @@
                             for (int i = 0; i < parcelables.length; i++) {
                                 ci[i] = (CellInfo) parcelables[i];
                             }
-                            callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            executor.execute(() ->{
+                                Rlog.d(TAG, "onResults: " + ci.toString());
+                                callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onResults", e);
                         }
                         break;
                     case CALLBACK_SCAN_ERROR:
                         try {
-                            callback.onError(message.arg1);
+                            final int errorCode = message.arg1;
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onError: " + errorCode);
+                                callback.onError(errorCode);
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onError", e);
                         }
                         break;
                     case CALLBACK_SCAN_COMPLETE:
                         try {
-                            callback.onComplete();
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onComplete");
+                                callback.onComplete();
+                            });
                             mScanInfo.remove(message.arg2);
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
@@ -171,12 +192,12 @@
      * @hide
      */
     public NetworkScan requestNetworkScan(int subId,
-            NetworkScanRequest request, NetworkScanCallback callback) {
+            NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
-                saveScanInfo(scanId, request, callback);
+                saveScanInfo(scanId, request, executor, callback);
                 return new NetworkScan(scanId, subId);
             }
         } catch (RemoteException ex) {
@@ -187,9 +208,10 @@
         return null;
     }
 
-    private void saveScanInfo(int id, NetworkScanRequest request, NetworkScanCallback callback) {
+    private void saveScanInfo(
+            int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
         synchronized (mScanInfo) {
-            mScanInfo.put(id, new NetworkScanInfo(request, callback));
+            mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
         }
     }
 
diff --git a/android/telephony/UiccAccessRule.java b/android/telephony/UiccAccessRule.java
index 3937201..d8836dc 100644
--- a/android/telephony/UiccAccessRule.java
+++ b/android/telephony/UiccAccessRule.java
@@ -16,6 +16,7 @@
 package android.telephony;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.pm.PackageInfo;
 import android.content.pm.Signature;
 import android.os.Parcel;
@@ -39,9 +40,8 @@
  * specification.
  *
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public final class UiccAccessRule implements Parcelable {
     private static final String TAG = "UiccAccessRule";
 
@@ -157,6 +157,13 @@
     }
 
     /**
+     * Returns the hex string of the certificate hash.
+     */
+    public String getCertificateHexString() {
+        return IccUtils.bytesToHexString(mCertificateHash);
+    }
+
+    /**
      * Returns the carrier privilege status associated with the given package.
      *
      * @param packageInfo package info fetched from
@@ -221,6 +228,15 @@
     }
 
     @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + Arrays.hashCode(mCertificateHash);
+        result = 31 * result + Objects.hashCode(mPackageName);
+        result = 31 * result + Objects.hashCode(mAccessType);
+        return result;
+    }
+
+    @Override
     public String toString() {
         return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
                 mPackageName + " access: " + mAccessType;
diff --git a/android/telephony/UiccSlotInfo.java b/android/telephony/UiccSlotInfo.java
index 0b3cbad..125161d 100644
--- a/android/telephony/UiccSlotInfo.java
+++ b/android/telephony/UiccSlotInfo.java
@@ -55,10 +55,12 @@
     /** Card state restricted. */
     public static final int CARD_STATE_INFO_RESTRICTED = 4;
 
-    public final boolean isActive;
-    public final boolean isEuicc;
-    public final String cardId;
-    public final @CardStateInfo int cardStateInfo;
+    private final boolean mIsActive;
+    private final boolean mIsEuicc;
+    private final String mCardId;
+    private final @CardStateInfo int mCardStateInfo;
+    private final int mLogicalSlotIdx;
+    private final boolean mIsExtendedApduSupported;
 
     public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
@@ -73,18 +75,22 @@
     };
 
     private UiccSlotInfo(Parcel in) {
-        isActive = in.readByte() != 0;
-        isEuicc = in.readByte() != 0;
-        cardId = in.readString();
-        cardStateInfo = in.readInt();
+        mIsActive = in.readByte() != 0;
+        mIsEuicc = in.readByte() != 0;
+        mCardId = in.readString();
+        mCardStateInfo = in.readInt();
+        mLogicalSlotIdx = in.readInt();
+        mIsExtendedApduSupported = in.readByte() != 0;
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (isActive ? 1 : 0));
-        dest.writeByte((byte) (isEuicc ? 1 : 0));
-        dest.writeString(cardId);
-        dest.writeInt(cardStateInfo);
+        dest.writeByte((byte) (mIsActive ? 1 : 0));
+        dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+        dest.writeString(mCardId);
+        dest.writeInt(mCardStateInfo);
+        dest.writeInt(mLogicalSlotIdx);
+        dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
     }
 
     @Override
@@ -93,28 +99,41 @@
     }
 
     public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo) {
-        this.isActive = isActive;
-        this.isEuicc = isEuicc;
-        this.cardId = cardId;
-        this.cardStateInfo = cardStateInfo;
+            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) {
+        this.mIsActive = isActive;
+        this.mIsEuicc = isEuicc;
+        this.mCardId = cardId;
+        this.mCardStateInfo = cardStateInfo;
+        this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mIsExtendedApduSupported = isExtendedApduSupported;
     }
 
     public boolean getIsActive() {
-        return isActive;
+        return mIsActive;
     }
 
     public boolean getIsEuicc() {
-        return isEuicc;
+        return mIsEuicc;
     }
 
     public String getCardId() {
-        return cardId;
+        return mCardId;
     }
 
     @CardStateInfo
     public int getCardStateInfo() {
-        return cardStateInfo;
+        return mCardStateInfo;
+    }
+
+    public int getLogicalSlotIdx() {
+        return mLogicalSlotIdx;
+    }
+
+    /**
+     * @return {@code true} if this slot supports extended APDU from ATR, {@code false} otherwise.
+     */
+    public boolean getIsExtendedApduSupported() {
+        return mIsExtendedApduSupported;
     }
 
     @Override
@@ -127,32 +146,40 @@
         }
 
         UiccSlotInfo that = (UiccSlotInfo) obj;
-        return (isActive == that.isActive)
-                && (isEuicc == that.isEuicc)
-                && (cardId == that.cardId)
-                && (cardStateInfo == that.cardStateInfo);
+        return (mIsActive == that.mIsActive)
+                && (mIsEuicc == that.mIsEuicc)
+                && (mCardId == that.mCardId)
+                && (mCardStateInfo == that.mCardStateInfo)
+                && (mLogicalSlotIdx == that.mLogicalSlotIdx)
+                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported);
     }
 
     @Override
     public int hashCode() {
         int result = 1;
-        result = 31 * result + (isActive ? 1 : 0);
-        result = 31 * result + (isEuicc ? 1 : 0);
-        result = 31 * result + Objects.hashCode(cardId);
-        result = 31 * result + cardStateInfo;
+        result = 31 * result + (mIsActive ? 1 : 0);
+        result = 31 * result + (mIsEuicc ? 1 : 0);
+        result = 31 * result + Objects.hashCode(mCardId);
+        result = 31 * result + mCardStateInfo;
+        result = 31 * result + mLogicalSlotIdx;
+        result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
         return result;
     }
 
     @Override
     public String toString() {
-        return "UiccSlotInfo (isActive="
-                + isActive
-                + ", isEuicc="
-                + isEuicc
-                + ", cardId="
-                + cardId
+        return "UiccSlotInfo (mIsActive="
+                + mIsActive
+                + ", mIsEuicc="
+                + mIsEuicc
+                + ", mCardId="
+                + mCardId
                 + ", cardState="
-                + cardStateInfo
+                + mCardStateInfo
+                + ", phoneId="
+                + mLogicalSlotIdx
+                + ", mIsExtendedApduSupported="
+                + mIsExtendedApduSupported
                 + ")";
     }
 }
diff --git a/android/telephony/VoiceSpecificRegistrationStates.java b/android/telephony/VoiceSpecificRegistrationStates.java
new file mode 100644
index 0000000..871ee4d
--- /dev/null
+++ b/android/telephony/VoiceSpecificRegistrationStates.java
@@ -0,0 +1,114 @@
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to voice network registration.
+ * @hide
+ */
+public class VoiceSpecificRegistrationStates implements Parcelable{
+    /**
+     * oncurrent services support indicator. if
+     * registered on a CDMA system.
+     * false - Concurrent services not supported,
+     * true - Concurrent services supported
+     */
+     public final boolean cssSupported;
+
+    /**
+     * TSB-58 Roaming Indicator if registered
+     * on a CDMA or EVDO system or -1 if not.
+     * Valid values are 0-255.
+     */
+    public final int roamingIndicator;
+
+    /**
+     * indicates whether the current system is in the
+     * PRL if registered on a CDMA or EVDO system or -1 if
+     * not. 0=not in the PRL, 1=in the PRL
+     */
+    public final int systemIsInPrl;
+
+    /**
+     * default Roaming Indicator from the PRL,
+     * if registered on a CDMA or EVDO system or -1 if not.
+     * Valid values are 0-255.
+     */
+    public final int defaultRoamingIndicator;
+
+    VoiceSpecificRegistrationStates(boolean cssSupported, int roamingIndicator, int systemIsInPrl,
+            int defaultRoamingIndicator) {
+        this.cssSupported = cssSupported;
+        this.roamingIndicator = roamingIndicator;
+        this.systemIsInPrl = systemIsInPrl;
+        this.defaultRoamingIndicator = defaultRoamingIndicator;
+    }
+
+    private VoiceSpecificRegistrationStates(Parcel source) {
+        this.cssSupported = source.readBoolean();
+        this.roamingIndicator = source.readInt();
+        this.systemIsInPrl = source.readInt();
+        this.defaultRoamingIndicator = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(cssSupported);
+        dest.writeInt(roamingIndicator);
+        dest.writeInt(systemIsInPrl);
+        dest.writeInt(defaultRoamingIndicator);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "VoiceSpecificRegistrationStates {"
+                + " mCssSupported=" + cssSupported
+                + " mRoamingIndicator=" + roamingIndicator
+                + " mSystemIsInPrl=" + systemIsInPrl
+                + " mDefaultRoamingIndicator=" + defaultRoamingIndicator + "}";
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cssSupported, roamingIndicator, systemIsInPrl,
+                defaultRoamingIndicator);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || !(o instanceof VoiceSpecificRegistrationStates)) {
+            return false;
+        }
+
+        VoiceSpecificRegistrationStates other = (VoiceSpecificRegistrationStates) o;
+        return this.cssSupported == other.cssSupported
+                && this.roamingIndicator == other.roamingIndicator
+                && this.systemIsInPrl == other.systemIsInPrl
+                && this.defaultRoamingIndicator == other.defaultRoamingIndicator;
+    }
+
+
+    public static final Parcelable.Creator<VoiceSpecificRegistrationStates> CREATOR =
+            new Parcelable.Creator<VoiceSpecificRegistrationStates>() {
+                @Override
+                public VoiceSpecificRegistrationStates createFromParcel(Parcel source) {
+                    return new VoiceSpecificRegistrationStates(source);
+                }
+
+                @Override
+                public VoiceSpecificRegistrationStates[] newArray(int size) {
+                    return new VoiceSpecificRegistrationStates[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/android/telephony/data/ApnSetting.java b/android/telephony/data/ApnSetting.java
index 73a05af..145ed7e 100644
--- a/android/telephony/data/ApnSetting.java
+++ b/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.StringDef;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
@@ -28,17 +28,15 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -46,25 +44,184 @@
  */
 public class ApnSetting implements Parcelable {
 
-    static final String LOG_TAG = "ApnSetting";
+    private static final String LOG_TAG = "ApnSetting";
     private static final boolean VDBG = false;
 
+    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+    private static final Map<Integer, String> PROTOCOL_INT_MAP;
+    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+    private static final int NOT_IN_MAP_INT = -1;
+    private static final int NO_PORT_SPECIFIED = -1;
+
+    /** All APN types except IA. */
+    private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+
+    /** APN type for default data traffic and HiPri traffic. */
+    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+    /** APN type for MMS traffic. */
+    public static final int TYPE_MMS = ApnTypes.MMS;
+    /** APN type for SUPL assisted GPS. */
+    public static final int TYPE_SUPL = ApnTypes.SUPL;
+    /** APN type for DUN traffic. */
+    public static final int TYPE_DUN = ApnTypes.DUN;
+    /** APN type for HiPri traffic. */
+    public static final int TYPE_HIPRI = ApnTypes.HIPRI;
+    /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */
+    public static final int TYPE_FOTA = ApnTypes.FOTA;
+    /** APN type for IMS. */
+    public static final int TYPE_IMS = ApnTypes.IMS;
+    /** APN type for CBS. */
+    public static final int TYPE_CBS = ApnTypes.CBS;
+    /** APN type for IA Initial Attach APN. */
+    public static final int TYPE_IA = ApnTypes.IA;
+    /**
+     * APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation.
+     */
+    public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+        TYPE_DEFAULT,
+        TYPE_MMS,
+        TYPE_SUPL,
+        TYPE_DUN,
+        TYPE_HIPRI,
+        TYPE_FOTA,
+        TYPE_IMS,
+        TYPE_CBS,
+        TYPE_IA,
+        TYPE_EMERGENCY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {}
+
+    // Possible values for authentication types.
+    /** No authentication type. */
+    public static final int AUTH_TYPE_NONE = 0;
+    /** Authentication type for PAP. */
+    public static final int AUTH_TYPE_PAP = 1;
+    /** Authentication type for CHAP. */
+    public static final int AUTH_TYPE_CHAP = 2;
+    /** Authentication type for PAP or CHAP. */
+    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "AUTH_TYPE_" }, value = {
+        AUTH_TYPE_NONE,
+        AUTH_TYPE_PAP,
+        AUTH_TYPE_CHAP,
+        AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    // Possible values for protocol.
+    /** Protocol type for IP. */
+    public static final int PROTOCOL_IP = 0;
+    /** Protocol type for IPV6. */
+    public static final int PROTOCOL_IPV6 = 1;
+    /** Protocol type for IPV4V6. */
+    public static final int PROTOCOL_IPV4V6 = 2;
+    /** Protocol type for PPP. */
+    public static final int PROTOCOL_PPP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+        PROTOCOL_IP,
+        PROTOCOL_IPV6,
+        PROTOCOL_IPV4V6,
+        PROTOCOL_PPP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    // Possible values for MVNO type.
+    /** MVNO type for service provider name. */
+    public static final int MVNO_TYPE_SPN = 0;
+    /** MVNO type for IMSI. */
+    public static final int MVNO_TYPE_IMSI = 1;
+    /** MVNO type for group identifier level 1. */
+    public static final int MVNO_TYPE_GID = 2;
+    /** MVNO type for ICCID. */
+    public static final int MVNO_TYPE_ICCID = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "MVNO_TYPE_" }, value = {
+        MVNO_TYPE_SPN,
+        MVNO_TYPE_IMSI,
+        MVNO_TYPE_GID,
+        MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    static {
+        APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+        APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
+        APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
+        APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
+        APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
+        APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
+        APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+        APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
+        APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
+        APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
+        APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
+        APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
+        APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+
+        PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+        PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
+        PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
+        PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
+        PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+        PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
+        PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+
+        MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
+        MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
+        MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
+        MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
+        MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid");
+    }
+
     private final String mEntryName;
     private final String mApnName;
-    private final InetAddress mProxy;
-    private final int mPort;
-    private final URL mMmsc;
-    private final InetAddress mMmsProxy;
-    private final int mMmsPort;
+    private final InetAddress mProxyAddress;
+    private final int mProxyPort;
+    private final Uri mMmsc;
+    private final InetAddress mMmsProxyAddress;
+    private final int mMmsProxyPort;
     private final String mUser;
     private final String mPassword;
     private final int mAuthType;
-    private final List<String> mTypes;
-    private final int mTypesBitmap;
+    private final int mApnTypeBitmask;
     private final int mId;
     private final String mOperatorNumeric;
-    private final String mProtocol;
-    private final String mRoamingProtocol;
+    private final int mProtocol;
+    private final int mRoamingProtocol;
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
@@ -78,22 +235,12 @@
     private final int mWaitTime;
     private final int mMaxConnsTime;
 
-    private final String mMvnoType;
+    private final int mMvnoType;
     private final String mMvnoMatchData;
 
     private boolean mPermanentFailed = false;
 
     /**
-     * Returns the types bitmap of the APN.
-     *
-     * @return types bitmap of the APN
-     * @hide
-     */
-    public int getTypesBitmap() {
-        return mTypesBitmap;
-    }
-
-    /**
      * Returns the MTU size of the mobile interface to which the APN connected.
      *
      * @return the MTU size of the APN
@@ -211,8 +358,8 @@
      *
      * @return proxy address.
      */
-    public InetAddress getProxy() {
-        return mProxy;
+    public InetAddress getProxyAddress() {
+        return mProxyAddress;
     }
 
     /**
@@ -220,15 +367,15 @@
      *
      * @return proxy port
      */
-    public int getPort() {
-        return mPort;
+    public int getProxyPort() {
+        return mProxyPort;
     }
     /**
-     * Returns the MMSC URL of the APN.
+     * Returns the MMSC Uri of the APN.
      *
-     * @return MMSC URL.
+     * @return MMSC Uri.
      */
-    public URL getMmsc() {
+    public Uri getMmsc() {
         return mMmsc;
     }
 
@@ -237,8 +384,8 @@
      *
      * @return MMS proxy address.
      */
-    public InetAddress getMmsProxy() {
-        return mMmsProxy;
+    public InetAddress getMmsProxyAddress() {
+        return mMmsProxyAddress;
     }
 
     /**
@@ -246,8 +393,8 @@
      *
      * @return MMS proxy port
      */
-    public int getMmsPort() {
-        return mMmsPort;
+    public int getMmsProxyPort() {
+        return mMmsProxyPort;
     }
 
     /**
@@ -268,21 +415,9 @@
         return mPassword;
     }
 
-    /** @hide */
-    @IntDef({
-            AUTH_TYPE_NONE,
-            AUTH_TYPE_PAP,
-            AUTH_TYPE_CHAP,
-            AUTH_TYPE_PAP_OR_CHAP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AuthType {}
-
     /**
      * Returns the authentication type of the APN.
      *
-     * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-     *
      * @return authentication type
      */
     @AuthType
@@ -290,32 +425,20 @@
         return mAuthType;
     }
 
-    /** @hide */
-    @StringDef({
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ApnType {}
-
     /**
-     * Returns the list of APN types of the APN.
+     * Returns the bitmask of APN types.
      *
-     * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+     * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+     * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+     * MMS-specific connections.
      *
-     * @return the list of APN types
+     * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+     *
+     * @see Builder#setApnTypeBitmask(int)
+     * @return a bitmask describing the types of the APN
      */
-    @ApnType
-    public List<String> getTypes() {
-        return mTypes;
+    public @ApnType int getApnTypeBitmask() {
+        return mApnTypeBitmask;
     }
 
     /**
@@ -328,7 +451,7 @@
     }
 
     /**
-     * Returns the numeric operator ID for the APN. Usually
+     * Returns the numeric operator ID for the APN. Numeric operator ID is defined as
      * {@link android.provider.Telephony.Carriers#MCC} +
      * {@link android.provider.Telephony.Carriers#MNC}.
      *
@@ -338,37 +461,29 @@
         return mOperatorNumeric;
     }
 
-    /** @hide */
-    @StringDef({
-            PROTOCOL_IP,
-            PROTOCOL_IPV6,
-            PROTOCOL_IPV4V6,
-            PROTOCOL_PPP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProtocolType {}
-
     /**
      * Returns the protocol to use to connect to this APN.
      *
-     * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-     * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+     * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setProtocol(int)
      * @return the protocol
      */
     @ProtocolType
-    public String getProtocol() {
+    public int getProtocol() {
         return mProtocol;
     }
 
     /**
-     * Returns the protocol to use to connect to this APN when roaming.
+     * Returns the protocol to use to connect to this APN while the device is roaming.
      *
-     * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+     * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setRoamingProtocol(int)
      * @return the roaming protocol
      */
-    public String getRoamingProtocol() {
+    @ProtocolType
+    public int getRoamingProtocol() {
         return mRoamingProtocol;
     }
 
@@ -398,41 +513,29 @@
         return mNetworkTypeBitmask;
     }
 
-    /** @hide */
-    @StringDef({
-            MVNO_TYPE_SPN,
-            MVNO_TYPE_IMSI,
-            MVNO_TYPE_GID,
-            MVNO_TYPE_ICCID,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MvnoType {}
-
     /**
      * Returns the MVNO match type for this APN.
      *
-     * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-     *
+     * @see Builder#setMvnoType(int)
      * @return the MVNO match type
      */
     @MvnoType
-    public String getMvnoType() {
+    public int getMvnoType() {
         return mMvnoType;
     }
 
     private ApnSetting(Builder builder) {
         this.mEntryName = builder.mEntryName;
         this.mApnName = builder.mApnName;
-        this.mProxy = builder.mProxy;
-        this.mPort = builder.mPort;
+        this.mProxyAddress = builder.mProxyAddress;
+        this.mProxyPort = builder.mProxyPort;
         this.mMmsc = builder.mMmsc;
-        this.mMmsProxy = builder.mMmsProxy;
-        this.mMmsPort = builder.mMmsPort;
+        this.mMmsProxyAddress = builder.mMmsProxyAddress;
+        this.mMmsProxyPort = builder.mMmsProxyPort;
         this.mUser = builder.mUser;
         this.mPassword = builder.mPassword;
         this.mAuthType = builder.mAuthType;
-        this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
-        this.mTypesBitmap = builder.mTypesBitmap;
+        this.mApnTypeBitmask = builder.mApnTypeBitmask;
         this.mId = builder.mId;
         this.mOperatorNumeric = builder.mOperatorNumeric;
         this.mProtocol = builder.mProtocol;
@@ -451,25 +554,25 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
-            String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
-            int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled,
+            String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
+            int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
+            int protocol, int roamingProtocol, boolean carrierEnabled,
             int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-            int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+            int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
                 .setOperatorNumeric(operatorNumeric)
                 .setEntryName(entryName)
                 .setApnName(apnName)
-                .setProxy(proxy)
-                .setPort(port)
+                .setProxyAddress(proxy)
+                .setProxyPort(port)
                 .setMmsc(mmsc)
-                .setMmsProxy(mmsProxy)
-                .setMmsPort(mmsPort)
+                .setMmsProxyAddress(mmsProxy)
+                .setMmsProxyPort(mmsPort)
                 .setUser(user)
                 .setPassword(password)
                 .setAuthType(authType)
-                .setTypes(types)
+                .setApnTypeBitmask(mApnTypeBitmask)
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
@@ -487,7 +590,7 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(Cursor cursor) {
-        String[] types = parseTypes(
+        final int apnTypesBitmask = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
         int networkTypeBitmask = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -507,7 +610,7 @@
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
-                URLFromString(cursor.getString(
+                UriFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
                 inetAddressFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
@@ -516,10 +619,12 @@
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
-                Arrays.asList(types),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)),
+                apnTypesBitmask,
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.ROAMING_PROTOCOL)))),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 networkTypeBitmask,
@@ -531,8 +636,9 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_TYPE)),
+                nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_TYPE)))),
                 cursor.getString(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MVNO_MATCH_DATA)));
     }
@@ -540,8 +646,8 @@
     /** @hide */
     public static ApnSetting makeApnSetting(ApnSetting apn) {
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
-                apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
-                apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+                apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
+                apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
                 apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
@@ -555,18 +661,14 @@
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
                 .append(", ").append(mApnName)
-                .append(", ").append(inetAddressToString(mProxy))
-                .append(", ").append(URLToString(mMmsc))
-                .append(", ").append(inetAddressToString(mMmsProxy))
-                .append(", ").append(portToString(mMmsPort))
-                .append(", ").append(portToString(mPort))
+                .append(", ").append(inetAddressToString(mProxyAddress))
+                .append(", ").append(UriToString(mMmsc))
+                .append(", ").append(inetAddressToString(mMmsProxyAddress))
+                .append(", ").append(portToString(mMmsProxyPort))
+                .append(", ").append(portToString(mProxyPort))
                 .append(", ").append(mAuthType).append(", ");
-        for (int i = 0; i < mTypes.size(); i++) {
-            sb.append(mTypes.get(i));
-            if (i < mTypes.size() - 1) {
-                sb.append(" | ");
-            }
-        }
+        final String[] types = deParseTypes(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types)).append(", ");
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
@@ -588,56 +690,37 @@
      * @hide
      */
     public boolean hasMvnoParams() {
-        return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+        return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
     }
 
     /** @hide */
-    public boolean canHandleType(String type) {
-        if (!mCarrierEnabled) return false;
-        boolean wildcardable = true;
-        if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
-        for (String t : mTypes) {
-            // DEFAULT handles all, and HIPRI is handled by DEFAULT
-            if (t.equalsIgnoreCase(type)
-                    || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
-                    || (t.equalsIgnoreCase(TYPE_DEFAULT)
-                    && type.equalsIgnoreCase(TYPE_HIPRI))) {
-                return true;
-            }
-        }
-        return false;
+    public boolean canHandleType(@ApnType int type) {
+        return mCarrierEnabled && ((mApnTypeBitmask & type) == type);
     }
 
     // check whether the types of two APN same (even only one type of each APN is same)
     private boolean typeSameAny(ApnSetting first, ApnSetting second) {
         if (VDBG) {
             StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
-            for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-                apnType1.append(first.mTypes.get(index1));
-                apnType1.append(",");
-            }
+            apnType1.append(deParseTypes(first.mApnTypeBitmask));
 
             StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
-            for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
-                apnType2.append(second.mTypes.get(index1));
-                apnType2.append(",");
-            }
+            apnType2.append(deParseTypes(second.mApnTypeBitmask));
+
             Rlog.d(LOG_TAG, "APN1: is " + apnType1);
             Rlog.d(LOG_TAG, "APN2: is " + apnType2);
         }
 
-        for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-            for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
-                if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
-                        || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
-                        || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
-                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
-                    return true;
-                }
+        if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) {
+            if (VDBG) {
+                Rlog.d(LOG_TAG, "typeSameAny: return true");
             }
+            return true;
         }
 
-        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        if (VDBG) {
+            Rlog.d(LOG_TAG, "typeSameAny: return false");
+        }
         return false;
     }
 
@@ -655,16 +738,15 @@
                 && Objects.equals(mId, other.mId)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort,other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort,other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -701,16 +783,15 @@
         return mEntryName.equals(other.mEntryName)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort, other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort, other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
                 && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -736,17 +817,17 @@
                 && !other.canHandleType(TYPE_DUN)
                 && Objects.equals(this.mApnName, other.mApnName)
                 && !typeSameAny(this, other)
-                && xorEqualsInetAddress(this.mProxy, other.mProxy)
-                && xorEqualsPort(this.mPort, other.mPort)
+                && xorEquals(this.mProxyAddress, other.mProxyAddress)
+                && xorEqualsPort(this.mProxyPort, other.mProxyPort)
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
-                && xorEqualsURL(this.mMmsc, other.mMmsc)
-                && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && xorEquals(this.mMmsc, other.mMmsc)
+                && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+                && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
                 && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
@@ -757,42 +838,23 @@
                 || TextUtils.isEmpty(second));
     }
 
-    // Equal or one is not specified.
-    private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
-        return first == null || second == null || first.equals(second);
-    }
-
-    // Equal or one is not specified.
-    private boolean xorEqualsURL(URL first, URL second) {
+    // Equal or one is not null.
+    private boolean xorEquals(Object first, Object second) {
         return first == null || second == null || first.equals(second);
     }
 
     // Equal or one is not specified.
     private boolean xorEqualsPort(int first, int second) {
-        return first == -1 || second == -1 || Objects.equals(first, second);
+        return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+            || Objects.equals(first, second);
     }
 
-    // Helper function to convert APN string into a 32-bit bitmask.
-    private static int getApnBitmask(String apn) {
-        switch (apn) {
-            case TYPE_DEFAULT: return ApnTypes.DEFAULT;
-            case TYPE_MMS: return ApnTypes.MMS;
-            case TYPE_SUPL: return ApnTypes.SUPL;
-            case TYPE_DUN: return ApnTypes.DUN;
-            case TYPE_HIPRI: return ApnTypes.HIPRI;
-            case TYPE_FOTA: return ApnTypes.FOTA;
-            case TYPE_IMS: return ApnTypes.IMS;
-            case TYPE_CBS: return ApnTypes.CBS;
-            case TYPE_IA: return ApnTypes.IA;
-            case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
-            case TYPE_ALL: return ApnTypes.ALL;
-            default: return ApnTypes.NONE;
-        }
-    }
-
-    private String deParseTypes(List<String> types) {
-        if (types == null) {
-            return null;
+    private String deParseTypes(int apnTypeBitmask) {
+        List<String> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((apnTypeBitmask & type) == type) {
+                types.add(APN_TYPE_INT_MAP.get(type));
+            }
         }
         return TextUtils.join(",", types);
     }
@@ -808,21 +870,25 @@
         apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
         apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
         apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
-        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
-        apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
-        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
-                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
+            : inetAddressToString(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
+        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
+                ? "" : inetAddressToString(mMmsProxyAddress));
         apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
         apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
-        String apnType = deParseTypes(mTypes);
+        String apnType = deParseTypes(mApnTypeBitmask);
         apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
-        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
-        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
+        apnValue.put(Telephony.Carriers.PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.MVNO_TYPE,
+            nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
         apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
@@ -830,32 +896,31 @@
 
     /**
      * @param types comma delimited list of APN types
-     * @return array of APN types
+     * @return bitmask of APN types
      * @hide
      */
-    public static String[] parseTypes(String types) {
-        String[] result;
-        // If unset, set to DEFAULT.
+    public static int parseTypes(String types) {
+        // If unset, set to ALL.
         if (TextUtils.isEmpty(types)) {
-            result = new String[1];
-            result[0] = TYPE_ALL;
+            return TYPE_ALL_BUT_IA;
         } else {
-            result = types.split(",");
-        }
-        return result;
-    }
-
-    private static URL URLFromString(String url) {
-        try {
-            return TextUtils.isEmpty(url) ? null : new URL(url);
-        } catch (MalformedURLException e) {
-            Log.e(LOG_TAG, "Can't parse URL from string.");
-            return null;
+            int result = 0;
+            for (String str : types.split(",")) {
+                Integer type = APN_TYPE_STRING_MAP.get(str);
+                if (type != null) {
+                    result |= type;
+                }
+            }
+            return result;
         }
     }
 
-    private static String URLToString(URL url) {
-        return url == null ? "" : url.toString();
+    private static Uri UriFromString(String uri) {
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    private static String UriToString(Uri uri) {
+        return uri == null ? "" : uri.toString();
     }
 
     private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
     }
 
     private static int portFromString(String strPort) {
-        int port = -1;
+        int port = NO_PORT_SPECIFIED;
         if (!TextUtils.isEmpty(strPort)) {
             try {
                 port = Integer.parseInt(strPort);
@@ -899,7 +964,7 @@
     }
 
     private static String portToString(int port) {
-        return port == -1 ? "" : Integer.toString(port);
+        return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
     }
 
     // Implement Parcelable.
@@ -916,19 +981,19 @@
         dest.writeString(mOperatorNumeric);
         dest.writeString(mEntryName);
         dest.writeString(mApnName);
-        dest.writeValue(mProxy);
-        dest.writeInt(mPort);
+        dest.writeValue(mProxyAddress);
+        dest.writeInt(mProxyPort);
         dest.writeValue(mMmsc);
-        dest.writeValue(mMmsProxy);
-        dest.writeInt(mMmsPort);
+        dest.writeValue(mMmsProxyAddress);
+        dest.writeInt(mMmsProxyPort);
         dest.writeString(mUser);
         dest.writeString(mPassword);
         dest.writeInt(mAuthType);
-        dest.writeStringArray(mTypes.toArray(new String[0]));
-        dest.writeString(mProtocol);
-        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mApnTypeBitmask);
+        dest.writeInt(mProtocol);
+        dest.writeInt(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
-        dest.writeString(mMvnoType);
+        dest.writeInt(mMvnoType);
         dest.writeInt(mNetworkTypeBitmask);
     }
 
@@ -939,23 +1004,23 @@
         final String apnName = in.readString();
         final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int port = in.readInt();
-        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader());
         final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int mmsPort = in.readInt();
         final String user = in.readString();
         final String password = in.readString();
         final int authType = in.readInt();
-        final List<String> types = Arrays.asList(in.readStringArray());
-        final String protocol = in.readString();
-        final String roamingProtocol = in.readString();
+        final int apnTypesBitmask = in.readInt();
+        final int protocol = in.readInt();
+        final int roamingProtocol = in.readInt();
         final boolean carrierEnabled = in.readInt() > 0;
-        final String mvnoType = in.readString();
+        final int mvnoType = in.readInt();
         final int networkTypeBitmask = in.readInt();
 
         return makeApnSetting(id, operatorNumeric, entryName, apnName,
-                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
-                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
-                0, 0, 0, 0, mvnoType, null);
+            proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask,
+            protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+            0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -971,89 +1036,26 @@
                 }
             };
 
-    /**
-     * APN types for data connections.  These are usage categories for an APN
-     * entry.  One APN entry may support multiple APN types, eg, a single APN
-     * may service regular internet traffic ("default") as well as MMS-specific
-     * connections.<br/>
-     * ALL is a special type to indicate that this APN entry can
-     * service all data connections.
-     */
-    public static final String TYPE_ALL = "*";
-    /** APN type for default data traffic */
-    public static final String TYPE_DEFAULT = "default";
-    /** APN type for MMS traffic */
-    public static final String TYPE_MMS = "mms";
-    /** APN type for SUPL assisted GPS */
-    public static final String TYPE_SUPL = "supl";
-    /** APN type for DUN traffic */
-    public static final String TYPE_DUN = "dun";
-    /** APN type for HiPri traffic */
-    public static final String TYPE_HIPRI = "hipri";
-    /** APN type for FOTA */
-    public static final String TYPE_FOTA = "fota";
-    /** APN type for IMS */
-    public static final String TYPE_IMS = "ims";
-    /** APN type for CBS */
-    public static final String TYPE_CBS = "cbs";
-    /** APN type for IA Initial Attach APN */
-    public static final String TYPE_IA = "ia";
-    /** APN type for Emergency PDN. This is not an IA apn, but is used
-     * for access to carrier services in an emergency call situation. */
-    public static final String TYPE_EMERGENCY = "emergency";
-    /**
-     * Array of all APN types
-     *
-     * @hide
-     */
-    public static final String[] ALL_TYPES = {
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    };
-
-    // Possible values for authentication types.
-    public static final int AUTH_TYPE_NONE = 0;
-    public static final int AUTH_TYPE_PAP = 1;
-    public static final int AUTH_TYPE_CHAP = 2;
-    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
-
-    // Possible values for protocol.
-    public static final String PROTOCOL_IP = "IP";
-    public static final String PROTOCOL_IPV6 = "IPV6";
-    public static final String PROTOCOL_IPV4V6 = "IPV4V6";
-    public static final String PROTOCOL_PPP = "PPP";
-
-    // Possible values for MVNO type.
-    public static final String MVNO_TYPE_SPN = "spn";
-    public static final String MVNO_TYPE_IMSI = "imsi";
-    public static final String MVNO_TYPE_GID = "gid";
-    public static final String MVNO_TYPE_ICCID = "iccid";
+    private static int nullToNotInMapInt(Integer value) {
+        return value == null ? NOT_IN_MAP_INT : value;
+    }
 
     public static class Builder{
         private String mEntryName;
         private String mApnName;
-        private InetAddress mProxy;
-        private int mPort = -1;
-        private URL mMmsc;
-        private InetAddress mMmsProxy;
-        private int mMmsPort = -1;
+        private InetAddress mProxyAddress;
+        private int mProxyPort = NO_PORT_SPECIFIED;
+        private Uri mMmsc;
+        private InetAddress mMmsProxyAddress;
+        private int mMmsProxyPort = NO_PORT_SPECIFIED;
         private String mUser;
         private String mPassword;
         private int mAuthType;
-        private List<String> mTypes;
-        private int mTypesBitmap;
+        private int mApnTypeBitmask;
         private int mId;
         private String mOperatorNumeric;
-        private String mProtocol;
-        private String mRoamingProtocol;
+        private int mProtocol = NOT_IN_MAP_INT;
+        private int mRoamingProtocol = NOT_IN_MAP_INT;
         private int mMtu;
         private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
@@ -1062,7 +1064,7 @@
         private int mMaxConns;
         private int mWaitTime;
         private int mMaxConnsTime;
-        private String mMvnoType;
+        private int mMvnoType = NOT_IN_MAP_INT;
         private String mMvnoMatchData;
 
         /**
@@ -1182,8 +1184,8 @@
          *
          * @param proxy the proxy address to set for the APN
          */
-        public Builder setProxy(InetAddress proxy) {
-            this.mProxy = proxy;
+        public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = proxy;
             return this;
         }
 
@@ -1192,17 +1194,17 @@
          *
          * @param port the proxy port to set for the APN
          */
-        public Builder setPort(int port) {
-            this.mPort = port;
+        public Builder setProxyPort(int port) {
+            this.mProxyPort = port;
             return this;
         }
 
         /**
-         * Sets the MMSC URL of the APN.
+         * Sets the MMSC Uri of the APN.
          *
-         * @param mmsc the MMSC URL to set for the APN
+         * @param mmsc the MMSC Uri to set for the APN
          */
-        public Builder setMmsc(URL mmsc) {
+        public Builder setMmsc(Uri mmsc) {
             this.mMmsc = mmsc;
             return this;
         }
@@ -1212,8 +1214,8 @@
          *
          * @param mmsProxy the MMS proxy address to set for the APN
          */
-        public Builder setMmsProxy(InetAddress mmsProxy) {
-            this.mMmsProxy = mmsProxy;
+        public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = mmsProxy;
             return this;
         }
 
@@ -1222,8 +1224,8 @@
          *
          * @param mmsPort the MMS proxy port to set for the APN
          */
-        public Builder setMmsPort(int mmsPort) {
-            this.mMmsPort = mmsPort;
+        public Builder setMmsProxyPort(int mmsPort) {
+            this.mMmsProxyPort = mmsPort;
             return this;
         }
 
@@ -1251,8 +1253,6 @@
         /**
          * Sets the authentication type of the APN.
          *
-         * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-         *
          * @param authType the authentication type to set for the APN
          */
         public Builder setAuthType(@AuthType int authType) {
@@ -1261,25 +1261,25 @@
         }
 
         /**
-         * Sets the list of APN types of the APN.
+         * Sets the bitmask of APN types.
          *
-         * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+         * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+         * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+         * MMS-specific connections.
          *
-         * @param types the list of APN types to set for the APN
+         * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+         *
+         * @param apnTypeBitmask a bitmask describing the types of the APN
          */
-        public Builder setTypes(@ApnType List<String> types) {
-            this.mTypes = types;
-            int apnBitmap = 0;
-            for (int i = 0; i < mTypes.size(); i++) {
-                mTypes.set(i, mTypes.get(i).toLowerCase());
-                apnBitmap |= getApnBitmask(mTypes.get(i));
-            }
-            this.mTypesBitmap = apnBitmap;
+        public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+            this.mApnTypeBitmask = apnTypeBitmask;
             return this;
         }
 
         /**
-         * Set the numeric operator ID for the APN.
+         * Sets the numeric operator ID for the APN. Numeric operator ID is defined as
+         * {@link android.provider.Telephony.Carriers#MCC} +
+         * {@link android.provider.Telephony.Carriers#MNC}.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
          */
@@ -1291,22 +1291,23 @@
         /**
          * Sets the protocol to use to connect to this APN.
          *
-         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-         * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+         * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param protocol the protocol to set to use to connect to this APN
          */
-        public Builder setProtocol(@ProtocolType String protocol) {
+        public Builder setProtocol(@ProtocolType int protocol) {
             this.mProtocol = protocol;
             return this;
         }
 
         /**
-         * Sets the protocol to use to connect to this APN when roaming.
+         * Sets the protocol to use to connect to this APN when the device is roaming.
+         *
+         * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
          */
-        public Builder setRoamingProtocol(String roamingProtocol) {
+        public Builder setRoamingProtocol(@ProtocolType  int roamingProtocol) {
             this.mRoamingProtocol = roamingProtocol;
             return this;
         }
@@ -1334,16 +1335,25 @@
         /**
          * Sets the MVNO match type for this APN.
          *
-         * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-         *
          * @param mvnoType the MVNO match type to set for this APN
          */
-        public Builder setMvnoType(@MvnoType String mvnoType) {
+        public Builder setMvnoType(@MvnoType int mvnoType) {
             this.mMvnoType = mvnoType;
             return this;
         }
 
+        /**
+         * Builds {@link ApnSetting} from this builder.
+         *
+         * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
+         * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit,
+         * {@link ApnSetting} built from this builder otherwise.
+         */
         public ApnSetting build() {
+            if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+                || TextUtils.isEmpty(mEntryName)) {
+                return null;
+            }
             return new ApnSetting(this);
         }
     }
diff --git a/android/telephony/data/DataCallResponse.java b/android/telephony/data/DataCallResponse.java
index ef3a183..25f5133 100644
--- a/android/telephony/data/DataCallResponse.java
+++ b/android/telephony/data/DataCallResponse.java
@@ -27,6 +27,7 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Description of the response of a setup data call connection request.
@@ -220,17 +221,8 @@
 
     @Override
     public int hashCode() {
-        return mStatus * 31
-                + mSuggestedRetryTime * 37
-                + mCid * 41
-                + mActive * 43
-                + mType.hashCode() * 47
-                + mIfname.hashCode() * 53
-                + mAddresses.hashCode() * 59
-                + mDnses.hashCode() * 61
-                + mGateways.hashCode() * 67
-                + mPcscfs.hashCode() * 71
-                + mMtu * 73;
+        return Objects.hash(mStatus, mSuggestedRetryTime, mCid, mActive, mType, mIfname, mAddresses,
+                mDnses, mGateways, mPcscfs, mMtu);
     }
 
     @Override
diff --git a/android/telephony/data/DataProfile.java b/android/telephony/data/DataProfile.java
index 41c1430..e8597b2 100644
--- a/android/telephony/data/DataProfile.java
+++ b/android/telephony/data/DataProfile.java
@@ -17,6 +17,7 @@
 package android.telephony.data;
 
 import android.annotation.SystemApi;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -230,8 +231,10 @@
 
     @Override
     public String toString() {
-        return "DataProfile=" + mProfileId + "/" + mApn + "/" + mProtocol + "/" + mAuthType
-                + "/" + mUserName + "/" + mPassword + "/" + mType + "/" + mMaxConnsTime
+        return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType
+                + "/" + (Build.IS_USER ? "***/***/***" :
+                         (mApn + "/" + mUserName + "/" + mPassword))
+                + "/" + mType + "/" + mMaxConnsTime
                 + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/"
                 + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/"
                 + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive;
diff --git a/android/telephony/data/DataService.java b/android/telephony/data/DataService.java
index fa19ea0..e8c1cb1 100644
--- a/android/telephony/data/DataService.java
+++ b/android/telephony/data/DataService.java
@@ -18,6 +18,8 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
@@ -30,9 +32,10 @@
 import android.os.RemoteException;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -86,15 +89,17 @@
     /** The reason of the data request is IWLAN handover */
     public static final int REQUEST_REASON_HANDOVER = 3;
 
-    private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE          = 1;
-    private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 2;
-    private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 3;
-    private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 4;
-    private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 5;
-    private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST                   = 6;
-    private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 7;
-    private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 8;
-    private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 9;
+    private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER                 = 1;
+    private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER                 = 2;
+    private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS            = 3;
+    private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 4;
+    private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 5;
+    private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 6;
+    private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 7;
+    private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST                   = 8;
+    private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 9;
+    private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 10;
+    private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 11;
 
     private final HandlerThread mHandlerThread;
 
@@ -102,7 +107,9 @@
 
     private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
 
-    private final SparseArray<IDataServiceWrapper> mBinderMap = new SparseArray<>();
+    /** @hide */
+    @VisibleForTesting
+    public final IDataServiceWrapper mBinder = new IDataServiceWrapper();
 
     /**
      * The abstract class of the actual data service implementation. The data service provider
@@ -136,19 +143,21 @@
          * the provided callback to notify the platform.
          *
          * @param accessNetworkType Access network type that the data call will be established on.
-         * Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
+         *        Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
          * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
          * @param isRoaming True if the device is data roaming.
          * @param allowRoaming True if data roaming is allowed by the user.
          * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
-         * {@link #REQUEST_REASON_HANDOVER}.
+         *        {@link #REQUEST_REASON_HANDOVER}.
          * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
-         * link properties of the existing data connection, otherwise null.
-         * @param callback The result callback for this request.
+         *        link properties of the existing data connection, otherwise null.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
          */
         public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
                                   boolean allowRoaming, @SetupDataReason int reason,
-                                  LinkProperties linkProperties, DataServiceCallback callback) {
+                                  @Nullable LinkProperties linkProperties,
+                                  @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
         }
@@ -159,13 +168,15 @@
          * provided callback to notify the platform.
          *
          * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
-         * int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
+         *        int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
          * @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
-         * {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
-         * @param callback The result callback for this request.
+         *        {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
+         *
          */
         public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
-                                       DataServiceCallback callback) {
+                                       @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
@@ -175,10 +186,11 @@
          *
          * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
          * @param isRoaming True if the device is data roaming.
-         * @param callback The result callback for this request.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
          */
         public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
-                                        DataServiceCallback callback) {
+                                        @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
@@ -190,10 +202,11 @@
          *
          * @param dps A list of data profiles.
          * @param isRoaming True if the device is data roaming.
-         * @param callback The result callback for this request.
+         * @param callback The result callback for this request. Null if the client does not care
+         *        about the result.
          */
         public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
-                                   DataServiceCallback callback) {
+                                   @Nullable DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
@@ -203,7 +216,7 @@
          *
          * @param callback The result callback for this request.
          */
-        public void getDataCallList(DataServiceCallback callback) {
+        public void getDataCallList(@NonNull DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
         }
@@ -321,70 +334,89 @@
         public void handleMessage(Message message) {
             IDataServiceCallback callback;
             final int slotId = message.arg1;
-            DataServiceProvider service;
-
-            synchronized (mServiceMap) {
-                service = mServiceMap.get(slotId);
-            }
+            DataServiceProvider serviceProvider = mServiceMap.get(slotId);
 
             switch (message.what) {
-                case DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE:
-                    service = createDataServiceProvider(message.arg1);
-                    if (service != null) {
-                        mServiceMap.put(slotId, service);
+                case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
+                    serviceProvider = createDataServiceProvider(message.arg1);
+                    if (serviceProvider != null) {
+                        mServiceMap.put(slotId, serviceProvider);
                     }
                     break;
+                case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
+                    if (serviceProvider != null) {
+                        serviceProvider.onDestroy();
+                        mServiceMap.remove(slotId);
+                    }
+                    break;
+                case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS:
+                    for (int i = 0; i < mServiceMap.size(); i++) {
+                        serviceProvider = mServiceMap.get(i);
+                        if (serviceProvider != null) {
+                            serviceProvider.onDestroy();
+                        }
+                    }
+                    mServiceMap.clear();
+                    break;
                 case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
-                    service.setupDataCall(setupDataCallRequest.accessNetworkType,
+                    serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
                             setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
                             setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
                             setupDataCallRequest.linkProperties,
-                            new DataServiceCallback(setupDataCallRequest.callback));
+                            (setupDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(setupDataCallRequest.callback)
+                                    : null);
 
                     break;
                 case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     DeactivateDataCallRequest deactivateDataCallRequest =
                             (DeactivateDataCallRequest) message.obj;
-                    service.deactivateDataCall(deactivateDataCallRequest.cid,
+                    serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
                             deactivateDataCallRequest.reason,
-                            new DataServiceCallback(deactivateDataCallRequest.callback));
+                            (deactivateDataCallRequest.callback != null)
+                                    ? new DataServiceCallback(deactivateDataCallRequest.callback)
+                                    : null);
                     break;
                 case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     SetInitialAttachApnRequest setInitialAttachApnRequest =
                             (SetInitialAttachApnRequest) message.obj;
-                    service.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
+                    serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
                             setInitialAttachApnRequest.isRoaming,
-                            new DataServiceCallback(setInitialAttachApnRequest.callback));
+                            (setInitialAttachApnRequest.callback != null)
+                                    ? new DataServiceCallback(setInitialAttachApnRequest.callback)
+                                    : null);
                     break;
                 case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     SetDataProfileRequest setDataProfileRequest =
                             (SetDataProfileRequest) message.obj;
-                    service.setDataProfile(setDataProfileRequest.dps,
+                    serviceProvider.setDataProfile(setDataProfileRequest.dps,
                             setDataProfileRequest.isRoaming,
-                            new DataServiceCallback(setDataProfileRequest.callback));
+                            (setDataProfileRequest.callback != null)
+                                    ? new DataServiceCallback(setDataProfileRequest.callback)
+                                    : null);
                     break;
                 case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
 
-                    service.getDataCallList(new DataServiceCallback(
+                    serviceProvider.getDataCallList(new DataServiceCallback(
                             (IDataServiceCallback) message.obj));
                     break;
                 case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
-                    if (service == null) break;
-                    service.registerForDataCallListChanged((IDataServiceCallback) message.obj);
+                    if (serviceProvider == null) break;
+                    serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
                     break;
                 case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     callback = (IDataServiceCallback) message.obj;
-                    service.unregisterForDataCallListChanged(callback);
+                    serviceProvider.unregisterForDataCallListChanged(callback);
                     break;
                 case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
-                    if (service == null) break;
+                    if (serviceProvider == null) break;
                     DataCallListChangedIndication indication =
                             (DataCallListChangedIndication) message.obj;
                     try {
@@ -423,67 +455,19 @@
             loge("Unexpected intent " + intent);
             return null;
         }
-
-        int slotId = intent.getIntExtra(
-                DATA_SERVICE_EXTRA_SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-
-        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
-            loge("Invalid slot id " + slotId);
-            return null;
-        }
-
-        log("onBind: slot id=" + slotId);
-
-        IDataServiceWrapper binder = mBinderMap.get(slotId);
-        if (binder == null) {
-            Message msg = mHandler.obtainMessage(DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE);
-            msg.arg1 = slotId;
-            msg.sendToTarget();
-
-            binder = new IDataServiceWrapper(slotId);
-            mBinderMap.put(slotId, binder);
-        }
-
-        return binder;
+        return mBinder;
     }
 
     /** @hide */
     @Override
     public boolean onUnbind(Intent intent) {
-        int slotId = intent.getIntExtra(DATA_SERVICE_EXTRA_SLOT_ID,
-                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-        if (mBinderMap.get(slotId) != null) {
-            DataServiceProvider serviceImpl;
-            synchronized (mServiceMap) {
-                serviceImpl = mServiceMap.get(slotId);
-            }
-            if (serviceImpl != null) {
-                serviceImpl.onDestroy();
-            }
-            mBinderMap.remove(slotId);
-        }
-
-        // If all clients unbinds, quit the handler thread
-        if (mBinderMap.size() == 0) {
-            mHandlerThread.quit();
-        }
-
+        mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
         return false;
     }
 
     /** @hide */
     @Override
     public void onDestroy() {
-        synchronized (mServiceMap) {
-            for (int i = 0; i < mServiceMap.size(); i++) {
-                DataServiceProvider serviceImpl = mServiceMap.get(i);
-                if (serviceImpl != null) {
-                    serviceImpl.onDestroy();
-                }
-            }
-            mServiceMap.clear();
-        }
-
         mHandlerThread.quit();
     }
 
@@ -491,68 +475,78 @@
      * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
      */
     private class IDataServiceWrapper extends IDataService.Stub {
-
-        private final int mSlotId;
-
-        IDataServiceWrapper(int slotId) {
-            mSlotId = slotId;
+        @Override
+        public void createDataServiceProvider(int slotId) {
+            mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0)
+                    .sendToTarget();
         }
 
         @Override
-        public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+        public void removeDataServiceProvider(int slotId) {
+            mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
                                   boolean isRoaming, boolean allowRoaming, int reason,
                                   LinkProperties linkProperties, IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
                     new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
                             allowRoaming, reason, linkProperties, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void deactivateDataCall(int cid, int reason, IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
+        public void deactivateDataCall(int slotId, int cid, int reason,
+                                       IDataServiceCallback callback) {
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
                     new DeactivateDataCallRequest(cid, reason, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+        public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming,
                                         IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, mSlotId, 0,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0,
                     new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+        public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming,
                                    IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, mSlotId, 0,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0,
                     new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
         }
 
         @Override
-        public void getDataCallList(IDataServiceCallback callback) {
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, mSlotId, 0,
+        public void getDataCallList(int slotId, IDataServiceCallback callback) {
+            if (callback == null) {
+                loge("getDataCallList: callback is null");
+                return;
+            }
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0,
                     callback).sendToTarget();
         }
 
         @Override
-        public void registerForDataCallListChanged(IDataServiceCallback callback) {
+        public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) {
             if (callback == null) {
-                loge("Callback is null");
+                loge("registerForDataCallListChanged: callback is null");
                 return;
             }
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId,
                     0, callback).sendToTarget();
         }
 
         @Override
-        public void unregisterForDataCallListChanged(IDataServiceCallback callback) {
+        public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) {
             if (callback == null) {
-                loge("Callback is null");
+                loge("unregisterForDataCallListChanged: callback is null");
                 return;
             }
-            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, mSlotId,
+            mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId,
                     0, callback).sendToTarget();
         }
     }
diff --git a/android/telephony/data/DataServiceCallback.java b/android/telephony/data/DataServiceCallback.java
index b6a81f9..4af31b5 100644
--- a/android/telephony/data/DataServiceCallback.java
+++ b/android/telephony/data/DataServiceCallback.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.net.LinkProperties;
 import android.os.RemoteException;
 import android.telephony.Rlog;
 import android.telephony.data.DataService.DataServiceProvider;
@@ -37,7 +38,7 @@
 @SystemApi
 public class DataServiceCallback {
 
-    private static final String mTag = DataServiceCallback.class.getSimpleName();
+    private static final String TAG = DataServiceCallback.class.getSimpleName();
 
     /**
      * Result of data requests
@@ -46,7 +47,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
             RESULT_ERROR_ILLEGAL_STATE})
-    public @interface Result {}
+    public @interface ResultCode {}
 
     /** Request is completed successfully */
     public static final int RESULT_SUCCESS              = 0;
@@ -68,35 +69,35 @@
 
     /**
      * Called to indicate result for the request {@link DataServiceProvider#setupDataCall(int,
-     * DataProfile, boolean, boolean, boolean, DataServiceCallback)}.
+     * DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)} .
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      * @param response Setup data call response.
      */
-    public void onSetupDataCallComplete(@Result int result, DataCallResponse response) {
+    public void onSetupDataCallComplete(@ResultCode int result, DataCallResponse response) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onSetupDataCallComplete(result, response);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onSetupDataCallComplete on the remote");
+                Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
             }
         }
     }
 
     /**
      * Called to indicate result for the request {@link DataServiceProvider#deactivateDataCall(int,
-     * boolean, boolean, DataServiceCallback)}.
+     * int, DataServiceCallback)}
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      */
-    public void onDeactivateDataCallComplete(@Result int result) {
+    public void onDeactivateDataCallComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onDeactivateDataCallComplete(result);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onDeactivateDataCallComplete on the remote");
+                Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
             }
         }
     }
@@ -105,15 +106,15 @@
      * Called to indicate result for the request {@link DataServiceProvider#setInitialAttachApn(
      * DataProfile, boolean, DataServiceCallback)}.
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      */
-    public void onSetInitialAttachApnComplete(@Result int result) {
+    public void onSetInitialAttachApnComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onSetInitialAttachApnComplete(result);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onSetInitialAttachApnComplete on the remote");
+                Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
             }
         }
     }
@@ -122,16 +123,16 @@
      * Called to indicate result for the request {@link DataServiceProvider#setDataProfile(List,
      * boolean, DataServiceCallback)}.
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      */
     @SystemApi
-    public void onSetDataProfileComplete(@Result int result) {
+    public void onSetDataProfileComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onSetDataProfileComplete(result);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onSetDataProfileComplete on the remote");
+                Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
             }
         }
     }
@@ -140,16 +141,17 @@
      * Called to indicate result for the request {@link DataServiceProvider#getDataCallList(
      * DataServiceCallback)}.
      *
-     * @param result The result code. Must be one of the {@link Result}.
+     * @param result The result code. Must be one of the {@link ResultCode}.
      * @param dataCallList List of the current active data connection.
      */
-    public void onGetDataCallListComplete(@Result int result, List<DataCallResponse> dataCallList) {
+    public void onGetDataCallListComplete(@ResultCode int result,
+                                          List<DataCallResponse> dataCallList) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
                 callback.onGetDataCallListComplete(result, dataCallList);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onGetDataCallListComplete on the remote");
+                Rlog.e(TAG, "Failed to onGetDataCallListComplete on the remote");
             }
         }
     }
@@ -165,7 +167,7 @@
             try {
                 callback.onDataCallListChanged(dataCallList);
             } catch (RemoteException e) {
-                Rlog.e(mTag, "Failed to onDataCallListChanged on the remote");
+                Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
             }
         }
     }
diff --git a/android/telephony/euicc/DownloadableSubscription.java b/android/telephony/euicc/DownloadableSubscription.java
index 01041c8..edf3b08 100644
--- a/android/telephony/euicc/DownloadableSubscription.java
+++ b/android/telephony/euicc/DownloadableSubscription.java
@@ -16,17 +16,24 @@
 package android.telephony.euicc;
 
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.UiccAccessRule;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 import com.android.internal.util.Preconditions;
 
 /**
- * Information about a subscription which is available for download.
+ * Information about a subscription which is downloadable to an eUICC using
+ * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
  *
- * TODO(b/35851809): Make this public.
- * @hide
+ * <p>For example, a DownloadableSubscription can be created through an activation code parsed from
+ * a QR code. A server address can be parsed from the activation code to download more information
+ * about the profile, such as carrier name, access rules, etc.
  */
 public final class DownloadableSubscription implements Parcelable {
 
@@ -46,11 +53,12 @@
     /**
      * Activation code. May be null for subscriptions which are not based on activation codes, e.g.
      * to download a default subscription assigned to this device.
+     * Should use getEncodedActivationCode() instead.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @deprecated - Do not use. This will be private. Use getEncodedActivationCode() instead.
      */
     @Nullable
+    @Deprecated
     public final String encodedActivationCode;
 
     @Nullable private String confirmationCode;
@@ -58,8 +66,16 @@
     // see getCarrierName and setCarrierName
     @Nullable
     private String carrierName;
+
     // see getAccessRules and setAccessRules
-    private UiccAccessRule[] accessRules;
+    @Nullable
+    private List<UiccAccessRule> accessRules;
+
+    /** Gets the activation code. */
+    @Nullable
+    public String getEncodedActivationCode() {
+        return encodedActivationCode;
+    }
 
     /** @hide */
     private DownloadableSubscription(String encodedActivationCode) {
@@ -70,13 +86,73 @@
         encodedActivationCode = in.readString();
         confirmationCode = in.readString();
         carrierName = in.readString();
-        accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
+        accessRules = new ArrayList<UiccAccessRule>();
+        in.readTypedList(accessRules, UiccAccessRule.CREATOR);
+    }
+
+    private DownloadableSubscription(String encodedActivationCode, String confirmationCode,
+            String carrierName, List<UiccAccessRule> accessRules) {
+        this.encodedActivationCode = encodedActivationCode;
+        this.confirmationCode = confirmationCode;
+        this.carrierName = carrierName;
+        this.accessRules = accessRules;
+    }
+
+    /** @hide */
+    @SystemApi
+    public static final class Builder {
+        @Nullable private String encodedActivationCode;
+        @Nullable private String confirmationCode;
+        @Nullable private String carrierName;
+        List<UiccAccessRule> accessRules;
+
+        public Builder() {}
+
+        public Builder(DownloadableSubscription baseSubscription) {
+            encodedActivationCode = baseSubscription.getEncodedActivationCode();
+            confirmationCode = baseSubscription.getConfirmationCode();
+            carrierName = baseSubscription.getCarrierName();
+            accessRules = baseSubscription.getAccessRules();
+        }
+
+        public DownloadableSubscription build() {
+            return new DownloadableSubscription(encodedActivationCode, confirmationCode,
+                    carrierName, accessRules);
+        }
+
+        public Builder setEncodedActivationCode(String value) {
+            encodedActivationCode = value;
+            return this;
+        }
+
+        public Builder setConfirmationCode(String value) {
+            confirmationCode = value;
+            return this;
+        }
+
+        public Builder setCarrierName(String value) {
+            carrierName = value;
+            return this;
+        }
+
+        public Builder setAccessRules(List<UiccAccessRule> value) {
+            accessRules = value;
+            return this;
+        }
     }
 
     /**
      * Create a DownloadableSubscription for the given activation code.
      *
-     * @param encodedActivationCode the activation code to use. Must not be null.
+     * <p>This fills the encodedActivationCode field. Other fields like confirmationCode,
+     * carrierName and accessRules may be filled in the implementation of
+     * {@code android.service.euicc.EuiccService} if exists.
+     *
+     * @param encodedActivationCode the activation code to use. An activation code can be parsed
+     *         from a user scanned QR code. The format of activation code is defined in SGP.22. For
+     *         example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For detail, see
+     *         {@code com.android.euicc.data.ActivationCode}. Must not be null.
+     *
      * @return the {@link DownloadableSubscription} which may be passed to
      *     {@link EuiccManager#downloadSubscription}.
      */
@@ -87,13 +163,19 @@
 
     /**
      * Sets the confirmation code.
+     * @hide
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public void setConfirmationCode(String confirmationCode) {
         this.confirmationCode = confirmationCode;
     }
 
     /**
      * Returns the confirmation code.
+     *
+     * <p>As an example, the confirmation code can be input by the user through a carrier app or the
+     * UI component of the eUICC local profile assistant (LPA) application.
      */
     @Nullable
     public String getConfirmationCode() {
@@ -103,9 +185,9 @@
     /**
      * Set the user-visible carrier name.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @deprecated - Do not use.
      */
+    @Deprecated
     public void setCarrierName(String carrierName) {
         this.carrierName = carrierName;
     }
@@ -117,44 +199,51 @@
      * those created with {@link #forActivationCode}). May be populated with
      * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
     @Nullable
     public String getCarrierName() {
         return carrierName;
     }
 
     /**
-     * Returns the {@link UiccAccessRule}s dictating access to this subscription.
+     * Returns the {@link UiccAccessRule}s in list dictating access to this subscription.
      *
      * <p>Only present for downloadable subscriptions that were queried from a server (as opposed to
      * those created with {@link #forActivationCode}). May be populated with
      * {@link EuiccManager#getDownloadableSubscriptionMetadata}.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
-    public UiccAccessRule[] getAccessRules() {
+    @SystemApi
+    public List<UiccAccessRule> getAccessRules() {
         return accessRules;
     }
 
     /**
      * Set the {@link UiccAccessRule}s dictating access to this subscription.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @deprecated - Do not use.
      */
-    public void setAccessRules(UiccAccessRule[] accessRules) {
+    @Deprecated
+    public void setAccessRules(List<UiccAccessRule> accessRules) {
         this.accessRules = accessRules;
     }
 
+    /**
+     * @hide
+     * @deprecated - Do not use.
+     */
+    @Deprecated
+    public void setAccessRules(UiccAccessRule[] accessRules) {
+        this.accessRules = Arrays.asList(accessRules);
+    }
+
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(encodedActivationCode);
         dest.writeString(confirmationCode);
         dest.writeString(carrierName);
-        dest.writeTypedArray(accessRules, flags);
+        dest.writeTypedList(accessRules);
     }
 
     @Override
diff --git a/android/telephony/euicc/EuiccCardManager.java b/android/telephony/euicc/EuiccCardManager.java
index 88bae33..38f9745 100644
--- a/android/telephony/euicc/EuiccCardManager.java
+++ b/android/telephony/euicc/EuiccCardManager.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -49,14 +50,14 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import android.annotation.CallbackExecutor;
+import java.util.concurrent.Executor;
 
 /**
  * EuiccCardManager is the application interface to an eSIM card.
- *
  * @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
  */
+@SystemApi
 public class EuiccCardManager {
     private static final String TAG = "EuiccCardManager";
 
@@ -68,6 +69,7 @@
             CANCEL_REASON_TIMEOUT,
             CANCEL_REASON_PPR_NOT_ALLOWED
     })
+    /** @hide */
     public @interface CancelReason {}
 
     /**
@@ -96,6 +98,7 @@
             RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
             RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
     })
+    /** @hide */
     public @interface ResetOption {}
 
     /** Deletes all operational profiles. */
@@ -110,6 +113,12 @@
     /** Result code of execution with no error. */
     public static final int RESULT_OK = 0;
 
+    /** Result code of an unknown error. */
+    public static final int RESULT_UNKNOWN_ERROR = -1;
+
+    /** Result code when the eUICC card with the given card Id is not found. */
+    public static final int RESULT_EUICC_NOT_FOUND = -2;
+
     /**
      * Callback to receive the result of an eUICC card API.
      *
@@ -140,18 +149,20 @@
     }
 
     /**
-     * Gets all the profiles on eUicc.
+     * Requests all the profiles on eUicc.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and all the profiles.
      */
-    public void getAllProfiles(String cardId, ResultCallback<EuiccProfileInfo[]> callback) {
+    public void requestAllProfiles(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<EuiccProfileInfo[]> callback) {
         try {
             getIEuiccCardController().getAllProfiles(mContext.getOpPackageName(), cardId,
                     new IGetAllProfilesCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
-                            callback.onComplete(resultCode, profiles);
+                            executor.execute(() -> callback.onComplete(resultCode, profiles));
                         }
                     });
         } catch (RemoteException e) {
@@ -161,19 +172,21 @@
     }
 
     /**
-     * Gets the profile of the given iccid.
+     * Requests the profile of the given iccid.
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and profile.
      */
-    public void getProfile(String cardId, String iccid, ResultCallback<EuiccProfileInfo> callback) {
+    public void requestProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
+            ResultCallback<EuiccProfileInfo> callback) {
         try {
             getIEuiccCardController().getProfile(mContext.getOpPackageName(), cardId, iccid,
                     new IGetProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            callback.onComplete(resultCode, profile);
+                            executor.execute(() -> callback.onComplete(resultCode, profile));
                         }
                     });
         } catch (RemoteException e) {
@@ -188,16 +201,17 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
      * @param refresh Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code.
      */
     public void disableProfile(String cardId, String iccid, boolean refresh,
-            ResultCallback<Void> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().disableProfile(mContext.getOpPackageName(), cardId, iccid,
                     refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -213,16 +227,17 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile to switch to.
      * @param refresh Whether sending the REFRESH command to modem.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
      */
     public void switchToProfile(String cardId, String iccid, boolean refresh,
-            ResultCallback<EuiccProfileInfo> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccProfileInfo> callback) {
         try {
             getIEuiccCardController().switchToProfile(mContext.getOpPackageName(), cardId, iccid,
                     refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            callback.onComplete(resultCode, profile);
+                            executor.execute(() -> callback.onComplete(resultCode, profile));
                         }
                     });
         } catch (RemoteException e) {
@@ -237,16 +252,17 @@
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
      * @param nickname The nickname of the profile.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code.
      */
     public void setNickname(String cardId, String iccid, String nickname,
-            ResultCallback<Void> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().setNickname(mContext.getOpPackageName(), cardId, iccid,
                     nickname, new ISetNicknameCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -260,15 +276,17 @@
      *
      * @param cardId The Id of the eUICC.
      * @param iccid The iccid of the profile.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code.
      */
-    public void deleteProfile(String cardId, String iccid, ResultCallback<Void> callback) {
+    public void deleteProfile(String cardId, String iccid, @CallbackExecutor Executor executor,
+            ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().deleteProfile(mContext.getOpPackageName(), cardId, iccid,
                     new IDeleteProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -283,15 +301,17 @@
      * @param cardId The Id of the eUICC.
      * @param options Bits of the options of resetting which parts of the eUICC memory. See
      *     EuiccCard for details.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code.
      */
-    public void resetMemory(String cardId, @ResetOption int options, ResultCallback<Void> callback) {
+    public void resetMemory(String cardId, @ResetOption int options,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().resetMemory(mContext.getOpPackageName(), cardId, options,
                     new IResetMemoryCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -301,18 +321,20 @@
     }
 
     /**
-     * Gets the default SM-DP+ address from eUICC.
+     * Requests the default SM-DP+ address from eUICC.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and the default SM-DP+ address.
      */
-    public void getDefaultSmdpAddress(String cardId, ResultCallback<String> callback) {
+    public void requestDefaultSmdpAddress(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<String> callback) {
         try {
             getIEuiccCardController().getDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
                     new IGetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            callback.onComplete(resultCode, address);
+                            executor.execute(() -> callback.onComplete(resultCode, address));
                         }
                     });
         } catch (RemoteException e) {
@@ -322,18 +344,20 @@
     }
 
     /**
-     * Gets the SM-DS address from eUICC.
+     * Requests the SM-DS address from eUICC.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code and the SM-DS address.
      */
-    public void getSmdsAddress(String cardId, ResultCallback<String> callback) {
+    public void requestSmdsAddress(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<String> callback) {
         try {
             getIEuiccCardController().getSmdsAddress(mContext.getOpPackageName(), cardId,
                     new IGetSmdsAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            callback.onComplete(resultCode, address);
+                            executor.execute(() -> callback.onComplete(resultCode, address));
                         }
                     });
         } catch (RemoteException e) {
@@ -347,16 +371,18 @@
      *
      * @param cardId The Id of the eUICC.
      * @param defaultSmdpAddress The default SM-DP+ address to set.
+     * @param executor The executor through which the callback should be invode.
      * @param callback The callback to get the result code.
      */
-    public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress, ResultCallback<Void> callback) {
+    public void setDefaultSmdpAddress(String cardId, String defaultSmdpAddress,
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().setDefaultSmdpAddress(mContext.getOpPackageName(), cardId,
                     defaultSmdpAddress,
                     new ISetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
@@ -366,18 +392,20 @@
     }
 
     /**
-     * Gets Rules Authorisation Table.
+     * Requests Rules Authorisation Table.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the rule authorisation table.
      */
-    public void getRulesAuthTable(String cardId, ResultCallback<EuiccRulesAuthTable> callback) {
+    public void requestRulesAuthTable(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<EuiccRulesAuthTable> callback) {
         try {
             getIEuiccCardController().getRulesAuthTable(mContext.getOpPackageName(), cardId,
                     new IGetRulesAuthTableCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
-                            callback.onComplete(resultCode, rat);
+                            executor.execute(() -> callback.onComplete(resultCode, rat));
                         }
                     });
         } catch (RemoteException e) {
@@ -387,18 +415,20 @@
     }
 
     /**
-     * Gets the eUICC challenge for new profile downloading.
+     * Requests the eUICC challenge for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the challenge.
      */
-    public void getEuiccChallenge(String cardId, ResultCallback<byte[]> callback) {
+    public void requestEuiccChallenge(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().getEuiccChallenge(mContext.getOpPackageName(), cardId,
                     new IGetEuiccChallengeCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] challenge) {
-                            callback.onComplete(resultCode, challenge);
+                            executor.execute(() -> callback.onComplete(resultCode, challenge));
                         }
                     });
         } catch (RemoteException e) {
@@ -408,18 +438,20 @@
     }
 
     /**
-     * Gets the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
+     * Requests the eUICC info1 defined in GSMA RSP v2.0+ for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the info1.
      */
-    public void getEuiccInfo1(String cardId, ResultCallback<byte[]> callback) {
+    public void requestEuiccInfo1(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().getEuiccInfo1(mContext.getOpPackageName(), cardId,
                     new IGetEuiccInfo1Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            callback.onComplete(resultCode, info);
+                            executor.execute(() -> callback.onComplete(resultCode, info));
                         }
                     });
         } catch (RemoteException e) {
@@ -432,15 +464,17 @@
      * Gets the eUICC info2 defined in GSMA RSP v2.0+ for new profile downloading.
      *
      * @param cardId The Id of the eUICC.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the info2.
      */
-    public void getEuiccInfo2(String cardId, ResultCallback<byte[]> callback) {
+    public void requestEuiccInfo2(String cardId, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().getEuiccInfo2(mContext.getOpPackageName(), cardId,
                     new IGetEuiccInfo2Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            callback.onComplete(resultCode, info);
+                            executor.execute(() -> callback.onComplete(resultCode, info));
                         }
                     });
         } catch (RemoteException e) {
@@ -463,12 +497,13 @@
      *     GSMA RSP v2.0+.
      * @param serverCertificate ASN.1 data in byte array indicating SM-DP+ Certificate returned by
      *     SM-DP+ server.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code AuthenticateServerResponse} defined in GSMA RSP v2.0+.
      */
     public void authenticateServer(String cardId, String matchingId, byte[] serverSigned1,
             byte[] serverSignature1, byte[] euiccCiPkIdToBeUsed, byte[] serverCertificate,
-            ResultCallback<byte[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().authenticateServer(
                     mContext.getOpPackageName(),
@@ -481,7 +516,7 @@
                     new IAuthenticateServerCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -502,11 +537,13 @@
      *     SM-DP+ server.
      * @param smdpCertificate ASN.1 data in byte array indicating the SM-DP+ Certificate returned
      *     by SM-DP+ server.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code PrepareDownloadResponse} defined in GSMA RSP v2.0+
      */
     public void prepareDownload(String cardId, @Nullable byte[] hashCc, byte[] smdpSigned2,
-            byte[] smdpSignature2, byte[] smdpCertificate, ResultCallback<byte[]> callback) {
+            byte[] smdpSignature2, byte[] smdpCertificate, @CallbackExecutor Executor executor,
+            ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().prepareDownload(
                     mContext.getOpPackageName(),
@@ -518,7 +555,7 @@
                     new IPrepareDownloadCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -532,11 +569,12 @@
      *
      * @param cardId The Id of the eUICC.
      * @param boundProfilePackage the Bound Profile Package data returned by SM-DP+ server.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and a byte array which represents a
      *     {@code LoadBoundProfilePackageResponse} defined in GSMA RSP v2.0+.
      */
     public void loadBoundProfilePackage(String cardId, byte[] boundProfilePackage,
-            ResultCallback<byte[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().loadBoundProfilePackage(
                     mContext.getOpPackageName(),
@@ -545,7 +583,7 @@
                     new ILoadBoundProfilePackageCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -560,11 +598,12 @@
      * @param cardId The Id of the eUICC.
      * @param transactionId the transaction ID returned by SM-DP+ server.
      * @param reason the cancel reason.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and an byte[] which represents a
      *     {@code CancelSessionResponse} defined in GSMA RSP v2.0+.
      */
     public void cancelSession(String cardId, byte[] transactionId, @CancelReason int reason,
-            ResultCallback<byte[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<byte[]> callback) {
         try {
             getIEuiccCardController().cancelSession(
                     mContext.getOpPackageName(),
@@ -574,7 +613,7 @@
                     new ICancelSessionCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            callback.onComplete(resultCode, response);
+                            executor.execute(() -> callback.onComplete(resultCode, response));
                         }
                     });
         } catch (RemoteException e) {
@@ -588,16 +627,17 @@
      *
      * @param cardId The Id of the eUICC.
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void listNotifications(String cardId, @EuiccNotification.Event int events,
-            ResultCallback<EuiccNotification[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
         try {
             getIEuiccCardController().listNotifications(mContext.getOpPackageName(), cardId, events,
                     new IListNotificationsCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            callback.onComplete(resultCode, notifications);
+                            executor.execute(() -> callback.onComplete(resultCode, notifications));
                         }
                     });
         } catch (RemoteException e) {
@@ -611,16 +651,17 @@
      *
      * @param cardId The Id of the eUICC.
      * @param events bits of the event types ({@link EuiccNotification.Event}) to list.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the list of notifications.
      */
     public void retrieveNotificationList(String cardId, @EuiccNotification.Event int events,
-            ResultCallback<EuiccNotification[]> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification[]> callback) {
         try {
             getIEuiccCardController().retrieveNotificationList(mContext.getOpPackageName(), cardId,
                     events, new IRetrieveNotificationListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            callback.onComplete(resultCode, notifications);
+                            executor.execute(() -> callback.onComplete(resultCode, notifications));
                         }
                     });
         } catch (RemoteException e) {
@@ -634,16 +675,17 @@
      *
      * @param cardId The Id of the eUICC.
      * @param seqNumber the sequence number of the notification.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code and the notification.
      */
     public void retrieveNotification(String cardId, int seqNumber,
-            ResultCallback<EuiccNotification> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<EuiccNotification> callback) {
         try {
             getIEuiccCardController().retrieveNotification(mContext.getOpPackageName(), cardId,
                     seqNumber, new IRetrieveNotificationCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification notification) {
-                            callback.onComplete(resultCode, notification);
+                            executor.execute(() -> callback.onComplete(resultCode, notification));
                         }
                     });
         } catch (RemoteException e) {
@@ -657,10 +699,11 @@
      *
      * @param cardId The Id of the eUICC.
      * @param seqNumber the sequence number of the notification.
+     * @param executor The executor through which the callback should be invode.
      * @param callback the callback to get the result code.
      */
     public void removeNotificationFromList(String cardId, int seqNumber,
-            ResultCallback<Void> callback) {
+            @CallbackExecutor Executor executor, ResultCallback<Void> callback) {
         try {
             getIEuiccCardController().removeNotificationFromList(
                     mContext.getOpPackageName(),
@@ -669,7 +712,7 @@
                     new IRemoveNotificationFromListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            callback.onComplete(resultCode, null);
+                            executor.execute(() -> callback.onComplete(resultCode, null));
                         }
                     });
         } catch (RemoteException e) {
diff --git a/android/telephony/euicc/EuiccInfo.java b/android/telephony/euicc/EuiccInfo.java
index 5bfff08..a4adf05 100644
--- a/android/telephony/euicc/EuiccInfo.java
+++ b/android/telephony/euicc/EuiccInfo.java
@@ -23,9 +23,6 @@
  * Information about an eUICC chip/device.
  *
  * @see EuiccManager#getEuiccInfo
- * @hide
- *
- * TODO(b/35851809): Make this public.
  */
 // WARNING: Do not add any privacy-sensitive fields to this class (such as an eUICC identifier)!
 // This API is accessible to all applications. Privacy-sensitive fields should be returned in their
@@ -45,12 +42,17 @@
                 }
             };
 
+    @Nullable
+    private final String osVersion;
+
     /**
-     * Version of the operating system running on the eUICC. This field is hardware-specific and is
-     * not guaranteed to match any particular format.
+     * Gets the version of the operating system running on the eUICC. This field is
+     * hardware-specific and is not guaranteed to match any particular format.
      */
     @Nullable
-    public final String osVersion;
+    public String getOsVersion() {
+        return osVersion;
+    }
 
     public EuiccInfo(@Nullable String osVersion) {
         this.osVersion = osVersion;
diff --git a/android/telephony/euicc/EuiccManager.java b/android/telephony/euicc/EuiccManager.java
index 7f913ce..b732d4d 100644
--- a/android/telephony/euicc/EuiccManager.java
+++ b/android/telephony/euicc/EuiccManager.java
@@ -15,11 +15,12 @@
  */
 package android.telephony.euicc;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -42,9 +43,6 @@
  * {@link Context#getSystemService(String)} and {@link Context#EUICC_SERVICE}.
  *
  * <p>See {@link #isEnabled} before attempting to use these APIs.
- *
- * TODO(b/35851809): Make this public.
- * @hide
  */
 public class EuiccManager {
 
@@ -56,6 +54,8 @@
      *
      * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
      * {@link #isEnabled} is false.
+     *
+     * This is ued by non-LPA app to bring up LUI.
      */
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
@@ -69,21 +69,22 @@
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
-     * TODO(b/35851809): Make this a SystemApi.
+     *
+     * @hide
      */
+    @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public static final String ACTION_OTA_STATUS_CHANGED =
             "android.telephony.euicc.action.OTA_STATUS_CHANGED";
 
     /**
      * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
      * completed.
-     *
-     * TODO(b/35851809): Make this a public API.
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NOTIFY_CARRIER_SETUP =
-            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP";
+    public static final String ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE =
+            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP_INCOMPLETE";
 
     /**
      * Intent action to provision an embedded subscription.
@@ -95,8 +96,9 @@
      * <p>The activity will immediately finish with {@link android.app.Activity#RESULT_CANCELED} if
      * {@link #isEnabled} is false.
      *
-     * TODO(b/35851809): Make this a SystemApi.
+     * @hide
      */
+    @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
             "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
@@ -140,11 +142,8 @@
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
 
     /**
-     * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
+     * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
      * callbacks providing the downloadable subscription metadata.
-     * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
@@ -153,9 +152,8 @@
      * Key for an extra set on {@link #getDefaultDownloadableSubscriptionList} PendingIntent result
      * callbacks providing the list of available downloadable subscriptions.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
 
@@ -201,6 +199,7 @@
      * Euicc OTA update status which can be got by {@link #getOtaStatus}
      * @hide
      */
+    @SystemApi
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"EUICC_OTA_"}, value = {
             EUICC_OTA_IN_PROGRESS,
@@ -215,15 +214,37 @@
     /**
      * An OTA is in progress. During this time, the eUICC is not available and the user may lose
      * network access.
+     * @hide
      */
+    @SystemApi
     public static final int EUICC_OTA_IN_PROGRESS = 1;
-    /** The OTA update failed. */
+
+    /**
+     * The OTA update failed.
+     * @hide
+     */
+    @SystemApi
     public static final int EUICC_OTA_FAILED = 2;
-    /** The OTA update finished successfully. */
+
+    /**
+     * The OTA update finished successfully.
+     * @hide
+     */
+    @SystemApi
     public static final int EUICC_OTA_SUCCEEDED = 3;
-    /** The OTA update not needed since current eUICC OS is latest. */
+
+    /**
+     * The OTA update not needed since current eUICC OS is latest.
+     * @hide
+     */
+    @SystemApi
     public static final int EUICC_OTA_NOT_NEEDED = 4;
-    /** The OTA status is unavailable since eUICC service is unavailable. */
+
+    /**
+     * The OTA status is unavailable since eUICC service is unavailable.
+     * @hide
+     */
+    @SystemApi
     public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
 
     private final Context mContext;
@@ -276,8 +297,11 @@
      *
      * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
      *     {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
-     * TODO(b/35851809): Make this a SystemApi.
+     *
+     * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -292,7 +316,7 @@
     /**
      * Attempt to download the given {@link DownloadableSubscription}.
      *
-     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
      * or the calling app must be authorized to manage both the currently-active subscription and
      * the subscription to be downloaded according to the subscription metadata. Without the former,
      * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
@@ -302,6 +326,7 @@
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -354,14 +379,17 @@
      *
      * <p>To be called by the LUI upon completion of a resolvable error flow.
      *
+     * <p>Requires that the calling app has the
+     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     *
      * @param resolutionIntent The original intent used to start the LUI.
      * @param resolutionExtras Resolution-specific extras depending on the result of the resolution.
      *     For example, this may indicate whether the user has consented or may include the input
      *     they provided.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
         if (!isEnabled()) {
             PendingIntent callbackIntent =
@@ -395,9 +423,9 @@
      * @param subscription the subscription which needs metadata filled in
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -426,9 +454,9 @@
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
-     *
-     * TODO(b/35851809): Make this a SystemApi.
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -468,11 +496,12 @@
      *
      * <p>Requires that the calling app has carrier privileges according to the metadata of the
      * profile to be deleted, or the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param subscriptionId the ID of the subscription to delete.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -489,7 +518,7 @@
     /**
      * Switch to (enable) the given subscription.
      *
-     * <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
+     * <p>Requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission,
      * or the calling app must be authorized to manage both the currently-active subscription and
      * the subscription to be enabled according to the subscription metadata. Without the former,
      * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
@@ -500,6 +529,7 @@
      *     current profile without activating another profile to replace it.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -525,6 +555,7 @@
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -543,12 +574,13 @@
      * Erase all subscriptions and reset the eUICC.
      *
      * <p>Requires that the calling app has the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
-     * internal system use only.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -599,11 +631,7 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    @TestApi
-    protected IEuiccController getIEuiccController() {
+    private static IEuiccController getIEuiccController() {
         return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
     }
 }
diff --git a/android/telephony/euicc/EuiccNotification.java b/android/telephony/euicc/EuiccNotification.java
index ef3c1ce..43a7707 100644
--- a/android/telephony/euicc/EuiccNotification.java
+++ b/android/telephony/euicc/EuiccNotification.java
@@ -17,6 +17,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -31,10 +32,9 @@
  * disabling, or deleting).
  *
  * @hide
- *
- * TODO(b/35851809): Make this a @SystemApi.
  */
-public class EuiccNotification implements Parcelable {
+@SystemApi
+public final class EuiccNotification implements Parcelable {
     /** Event */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "EVENT_" }, value = {
@@ -43,6 +43,7 @@
             EVENT_DISABLE,
             EVENT_DELETE
     })
+    /** @hide */
     public @interface Event {}
 
     /** A profile is downloaded and installed. */
@@ -57,7 +58,7 @@
     /** A profile is deleted. */
     public static final int EVENT_DELETE = 1 << 3;
 
-    /** Value of the bits of all above events */
+    /** Value of the bits of all the events including install, enable, disable and delete. */
     @Event
     public static final int ALL_EVENTS =
             EVENT_INSTALL | EVENT_ENABLE | EVENT_DISABLE | EVENT_DELETE;
diff --git a/android/telephony/euicc/EuiccRulesAuthTable.java b/android/telephony/euicc/EuiccRulesAuthTable.java
index 7efe043..67ae983 100644
--- a/android/telephony/euicc/EuiccRulesAuthTable.java
+++ b/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -16,6 +16,7 @@
 package android.telephony.euicc;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.carrier.CarrierIdentifier;
@@ -27,20 +28,21 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This represents the RAT (Rules Authorisation Table) stored on eUICC.
- *
  * @hide
- *
- * TODO(b/35851809): Make this a @SystemApi.
  */
+@SystemApi
 public final class EuiccRulesAuthTable implements Parcelable {
     /** Profile policy rule flags */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "POLICY_RULE_FLAG_" }, value = {
             POLICY_RULE_FLAG_CONSENT_REQUIRED
     })
+    /** @hide */
     public @interface PolicyRuleFlag {}
 
     /** User consent is required to install the profile. */
@@ -89,12 +91,14 @@
          * @throws ArrayIndexOutOfBoundsException If the {@code mPosition} is larger than the size
          *     this table.
          */
-        public Builder add(int policyRules, CarrierIdentifier[] carrierId, int policyRuleFlags) {
+        public Builder add(int policyRules, List<CarrierIdentifier> carrierId, int policyRuleFlags) {
             if (mPosition >= mPolicyRules.length) {
                 throw new ArrayIndexOutOfBoundsException(mPosition);
             }
             mPolicyRules[mPosition] = policyRules;
-            mCarrierIds[mPosition] = carrierId;
+            if (carrierId != null && carrierId.size() > 0) {
+                mCarrierIds[mPosition] = carrierId.toArray(new CarrierIdentifier[carrierId.size()]);
+            }
             mPolicyRuleFlags[mPosition] = policyRuleFlags;
             mPosition++;
             return this;
diff --git a/android/telephony/ims/ImsCallForwardInfo.java b/android/telephony/ims/ImsCallForwardInfo.java
new file mode 100644
index 0000000..2831127
--- /dev/null
+++ b/android/telephony/ims/ImsCallForwardInfo.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the call forward information for the supplementary service configuration.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsCallForwardInfo implements Parcelable {
+    // Refer to ImsUtInterface#CDIV_CF_XXX
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mCondition;
+    // 0: disabled, 1: enabled
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mStatus;
+    // 0x91: International, 0x81: Unknown
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mToA;
+    // Service class
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mServiceClass;
+    // Number (it will not include the "sip" or "tel" URI scheme)
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public String mNumber;
+    // No reply timer for CF
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter.
+    public int mTimeSeconds;
+
+    /** @hide */
+    // TODO: Will be removed in the future, use public constructor instead.
+    public ImsCallForwardInfo() {
+    }
+
+    /**
+     * IMS Call Forward Information.
+     */
+    public ImsCallForwardInfo(int condition, int status, int toA, int serviceClass, String number,
+            int replyTimerSec) {
+        mCondition = condition;
+        mStatus = status;
+        mToA = toA;
+        mServiceClass = serviceClass;
+        mNumber = number;
+        mTimeSeconds = replyTimerSec;
+    }
+
+    /** @hide */
+    public ImsCallForwardInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCondition);
+        out.writeInt(mStatus);
+        out.writeInt(mToA);
+        out.writeString(mNumber);
+        out.writeInt(mTimeSeconds);
+        out.writeInt(mServiceClass);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + ", Condition: " + mCondition
+            + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled")
+            + ", ToA: " + mToA
+            + ", Service Class: " + mServiceClass
+            + ", Number=" + mNumber
+            + ", Time (seconds): " + mTimeSeconds;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mCondition = in.readInt();
+        mStatus = in.readInt();
+        mToA = in.readInt();
+        mNumber = in.readString();
+        mTimeSeconds = in.readInt();
+        mServiceClass = in.readInt();
+    }
+
+    public static final Creator<ImsCallForwardInfo> CREATOR =
+            new Creator<ImsCallForwardInfo>() {
+        @Override
+        public ImsCallForwardInfo createFromParcel(Parcel in) {
+            return new ImsCallForwardInfo(in);
+        }
+
+        @Override
+        public ImsCallForwardInfo[] newArray(int size) {
+            return new ImsCallForwardInfo[size];
+        }
+    };
+
+    public int getCondition() {
+        return mCondition;
+    }
+
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public int getToA() {
+        return mToA;
+    }
+
+    public int getServiceClass() {
+        return mServiceClass;
+    }
+
+    public String getNumber() {
+        return mNumber;
+    }
+
+    public int getTimeSeconds() {
+        return mTimeSeconds;
+    }
+}
diff --git a/android/telephony/ims/ImsCallProfile.java b/android/telephony/ims/ImsCallProfile.java
new file mode 100644
index 0000000..350dfe3
--- /dev/null
+++ b/android/telephony/ims/ImsCallProfile.java
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.telecom.VideoProfile;
+import android.util.Log;
+
+import com.android.internal.telephony.PhoneConstants;
+
+/**
+ * Parcelable object to handle IMS call profile.
+ * It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111.
+ * It provides the service and call type, the additional information related to the call.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsCallProfile implements Parcelable {
+    private static final String TAG = "ImsCallProfile";
+
+    /**
+     * Service types
+     */
+    /**
+     * It is for a special case. It helps that the application can make a call
+     * without IMS connection (not registered).
+     * In the moment of the call initiation, the device try to connect to the IMS network
+     * and initiates the call.
+     */
+    public static final int SERVICE_TYPE_NONE = 0;
+    /**
+     * It is a default type and can be selected when the device is connected to the IMS network.
+     */
+    public static final int SERVICE_TYPE_NORMAL = 1;
+    /**
+     * It is for an emergency call.
+     */
+    public static final int SERVICE_TYPE_EMERGENCY = 2;
+
+    /**
+     * Call types
+     */
+    /**
+     * IMSPhone to support IR.92 & IR.94 (voice + video upgrade/downgrade)
+     */
+    public static final int CALL_TYPE_VOICE_N_VIDEO = 1;
+    /**
+     * IR.92 (Voice only)
+     */
+    public static final int CALL_TYPE_VOICE = 2;
+    /**
+     * VT to support IR.92 & IR.94 (voice + video upgrade/downgrade)
+     */
+    public static final int CALL_TYPE_VIDEO_N_VOICE = 3;
+    /**
+     * Video Telephony (audio / video two way)
+     */
+    public static final int CALL_TYPE_VT = 4;
+    /**
+     * Video Telephony (audio two way / video TX one way)
+     */
+    public static final int CALL_TYPE_VT_TX = 5;
+    /**
+     * Video Telephony (audio two way / video RX one way)
+     */
+    public static final int CALL_TYPE_VT_RX = 6;
+    /**
+     * Video Telephony (audio two way / video inactive)
+     */
+    public static final int CALL_TYPE_VT_NODIR = 7;
+    /**
+     * VideoShare (video two way)
+     */
+    public static final int CALL_TYPE_VS = 8;
+    /**
+     * VideoShare (video TX one way)
+     */
+    public static final int CALL_TYPE_VS_TX = 9;
+    /**
+     * VideoShare (video RX one way)
+     */
+    public static final int CALL_TYPE_VS_RX = 10;
+
+    /**
+     * Extra properties for IMS call.
+     */
+    /**
+     * Boolean extra properties - "true" / "false"
+     *  conference : Indicates if the session is for the conference call or not.
+     *  e_call : Indicates if the session is for the emergency call or not.
+     *  vms : Indicates if the session is connected to the voice mail system or not.
+     *  call_mode_changeable : Indicates if the session is able to upgrade/downgrade
+     *      the video during voice call.
+     *  conference_avail : Indicates if the session can be extended to the conference.
+     */
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CONFERENCE = "conference";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_E_CALL = "e_call";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_VMS = "vms";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CALL_MODE_CHANGEABLE = "call_mode_changeable";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CONFERENCE_AVAIL = "conference_avail";
+
+    // Extra string for internal use only. OEMs should not use
+    // this for packing extras.
+    /**
+     * @hide
+     */
+    public static final String EXTRA_OEM_EXTRAS = "OemCallExtras";
+
+    /**
+     * Rule for originating identity (number) presentation, MO/MT.
+     *      {@link ImsCallProfile#OIR_DEFAULT}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+     */
+    public static final String EXTRA_OIR = "oir";
+    /**
+     * Rule for calling name presentation
+     *      {@link ImsCallProfile#OIR_DEFAULT}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_RESTRICTED}
+     *      {@link ImsCallProfile#OIR_PRESENTATION_NOT_RESTRICTED}
+     */
+    public static final String EXTRA_CNAP = "cnap";
+    /**
+     * To identify the Ims call type, MO
+     *      {@link ImsCallProfile#DIALSTRING_NORMAL}
+     *      {@link ImsCallProfile#DIALSTRING_SS_CONF}
+     *      {@link ImsCallProfile#DIALSTRING_USSD}
+     */
+    public static final String EXTRA_DIALSTRING = "dialstring";
+
+    /**
+     * Values for EXTRA_OIR / EXTRA_CNAP
+     */
+    /**
+     * Default presentation for Originating Identity.
+     */
+    public static final int OIR_DEFAULT = 0;    // "user subscription default value"
+    /**
+     * Restricted presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_RESTRICTED = 1;
+    /**
+     * Not restricted presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+    /**
+     * Presentation unknown for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_UNKNOWN = 3;
+    /**
+     * Payphone presentation for Originating Identity.
+     */
+    public static final int OIR_PRESENTATION_PAYPHONE = 4;
+
+    //Values for EXTRA_DIALSTRING
+    /**
+     * A default or normal normal call.
+     */
+    public static final int DIALSTRING_NORMAL = 0;
+    /**
+     * Call for SIP-based user configuration
+     */
+    public static final int DIALSTRING_SS_CONF = 1;
+    /**
+     * Call for USSD message
+     */
+    public static final int DIALSTRING_USSD = 2;
+
+    /**
+     * Values for causes that restrict call types
+     */
+    // Default cause not restricted at peer and HD is supported
+    public static final int CALL_RESTRICT_CAUSE_NONE = 0;
+    // Service not supported by RAT at peer
+    public static final int CALL_RESTRICT_CAUSE_RAT = 1;
+    // Service Disabled at peer
+    public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
+    // HD is not supported
+    public static final int CALL_RESTRICT_CAUSE_HD = 3;
+
+    /**
+     * String extra properties
+     *  oi : Originating identity (number), MT only
+     *  cna : Calling name
+     *  ussd : For network-initiated USSD, MT only
+     *  remote_uri : Connected user identity (it can be used for the conference)
+     *  ChildNum: Child number info.
+     *  Codec: Codec info.
+     *  DisplayText: Display text for the call.
+     *  AdditionalCallInfo: Additional call info.
+     *  CallPull: Boolean value specifying if the call is a pulled call.
+     */
+    public static final String EXTRA_OI = "oi";
+    public static final String EXTRA_CNA = "cna";
+    public static final String EXTRA_USSD = "ussd";
+    public static final String EXTRA_REMOTE_URI = "remote_uri";
+    public static final String EXTRA_CHILD_NUMBER = "ChildNum";
+    public static final String EXTRA_CODEC = "Codec";
+    public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
+    public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
+    public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+    /**
+     * Extra key which the RIL can use to indicate the radio technology used for a call.
+     * Valid values are:
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined
+     * {@code RIL_RADIO_TECHNOLOGY_*} constants.
+     * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer
+     * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g.
+     * "14" vs (int) 14).
+     * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
+     *      updateWifiStateFromExtras(Bundle)} to determine whether to set the
+     * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
+     */
+    public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+
+    /**
+     * Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'.  Used to ensure
+     * compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
+     * extra key.  Should be removed when the non-compliant modems are fixed.
+     * @hide
+     */
+    public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+
+    /** @hide */
+    public int mServiceType;
+    /** @hide */
+    public int mCallType;
+    /** @hide */
+    public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+
+    /**
+     * Extras associated with this {@link ImsCallProfile}.
+     * <p>
+     * Valid data types include:
+     * <ul>
+     *     <li>{@link Integer} (and int)</li>
+     *     <li>{@link Long} (and long)</li>
+     *     <li>{@link Double} (and double)</li>
+     *     <li>{@link String}</li>
+     *     <li>{@code int[]}</li>
+     *     <li>{@code long[]}</li>
+     *     <li>{@code double[]}</li>
+     *     <li>{@code String[]}</li>
+     *     <li>{@link PersistableBundle}</li>
+     *     <li>{@link Boolean} (and boolean)</li>
+     *     <li>{@code boolean[]}</li>
+     *     <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
+     * </ul>
+     * <p>
+     * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across
+     * a {@link android.os.Binder}.
+     */
+    /** @hide */
+    public Bundle mCallExtras;
+    /** @hide */
+    public ImsStreamMediaProfile mMediaProfile;
+
+    /** @hide */
+    public ImsCallProfile(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Default Constructor that initializes the call profile with service type
+     * {@link #SERVICE_TYPE_NORMAL} and call type {@link #CALL_TYPE_VIDEO_N_VOICE}
+     */
+    public ImsCallProfile() {
+        mServiceType = SERVICE_TYPE_NORMAL;
+        mCallType = CALL_TYPE_VOICE_N_VIDEO;
+        mCallExtras = new Bundle();
+        mMediaProfile = new ImsStreamMediaProfile();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param serviceType the service type for the call. Can be one of the following:
+     *                    {@link #SERVICE_TYPE_NONE},
+     *                    {@link #SERVICE_TYPE_NORMAL},
+     *                    {@link #SERVICE_TYPE_EMERGENCY}
+     * @param callType the call type. Can be one of the following:
+     *                 {@link #CALL_TYPE_VOICE_N_VIDEO},
+     *                 {@link #CALL_TYPE_VOICE},
+     *                 {@link #CALL_TYPE_VIDEO_N_VOICE},
+     *                 {@link #CALL_TYPE_VT},
+     *                 {@link #CALL_TYPE_VT_TX},
+     *                 {@link #CALL_TYPE_VT_RX},
+     *                 {@link #CALL_TYPE_VT_NODIR},
+     *                 {@link #CALL_TYPE_VS},
+     *                 {@link #CALL_TYPE_VS_TX},
+     *                 {@link #CALL_TYPE_VS_RX}
+     */
+    public ImsCallProfile(int serviceType, int callType) {
+        mServiceType = serviceType;
+        mCallType = callType;
+        mCallExtras = new Bundle();
+        mMediaProfile = new ImsStreamMediaProfile();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param serviceType the service type for the call. Can be one of the following:
+     *                    {@link #SERVICE_TYPE_NONE},
+     *                    {@link #SERVICE_TYPE_NORMAL},
+     *                    {@link #SERVICE_TYPE_EMERGENCY}
+     * @param callType the call type. Can be one of the following:
+     *                 {@link #CALL_TYPE_VOICE_N_VIDEO},
+     *                 {@link #CALL_TYPE_VOICE},
+     *                 {@link #CALL_TYPE_VIDEO_N_VOICE},
+     *                 {@link #CALL_TYPE_VT},
+     *                 {@link #CALL_TYPE_VT_TX},
+     *                 {@link #CALL_TYPE_VT_RX},
+     *                 {@link #CALL_TYPE_VT_NODIR},
+     *                 {@link #CALL_TYPE_VS},
+     *                 {@link #CALL_TYPE_VS_TX},
+     *                 {@link #CALL_TYPE_VS_RX}
+     * @param callExtras A bundle with the call extras.
+     * @param mediaProfile The IMS stream media profile.
+     */
+    public ImsCallProfile(int serviceType, int callType, Bundle callExtras,
+            ImsStreamMediaProfile mediaProfile) {
+        mServiceType = serviceType;
+        mCallType = callType;
+        mCallExtras = callExtras;
+        mMediaProfile = mediaProfile;
+    }
+
+    public String getCallExtra(String name) {
+        return getCallExtra(name, "");
+    }
+
+    public String getCallExtra(String name, String defaultValue) {
+        if (mCallExtras == null) {
+            return defaultValue;
+        }
+
+        return mCallExtras.getString(name, defaultValue);
+    }
+
+    public boolean getCallExtraBoolean(String name) {
+        return getCallExtraBoolean(name, false);
+    }
+
+    public boolean getCallExtraBoolean(String name, boolean defaultValue) {
+        if (mCallExtras == null) {
+            return defaultValue;
+        }
+
+        return mCallExtras.getBoolean(name, defaultValue);
+    }
+
+    public int getCallExtraInt(String name) {
+        return getCallExtraInt(name, -1);
+    }
+
+    public int getCallExtraInt(String name, int defaultValue) {
+        if (mCallExtras == null) {
+            return defaultValue;
+        }
+
+        return mCallExtras.getInt(name, defaultValue);
+    }
+
+    public void setCallExtra(String name, String value) {
+        if (mCallExtras != null) {
+            mCallExtras.putString(name, value);
+        }
+    }
+
+    public void setCallExtraBoolean(String name, boolean value) {
+        if (mCallExtras != null) {
+            mCallExtras.putBoolean(name, value);
+        }
+    }
+
+    public void setCallExtraInt(String name, int value) {
+        if (mCallExtras != null) {
+            mCallExtras.putInt(name, value);
+        }
+    }
+
+    public void updateCallType(ImsCallProfile profile) {
+        mCallType = profile.mCallType;
+    }
+
+    public void updateCallExtras(ImsCallProfile profile) {
+        mCallExtras.clear();
+        mCallExtras = (Bundle) profile.mCallExtras.clone();
+    }
+
+    /**
+     * Updates the media profile for the call.
+     *
+     * @param profile Call profile with new media profile.
+     */
+    public void updateMediaProfile(ImsCallProfile profile) {
+        mMediaProfile = profile.mMediaProfile;
+    }
+
+
+    @Override
+    public String toString() {
+        return "{ serviceType=" + mServiceType +
+                ", callType=" + mCallType +
+                ", restrictCause=" + mRestrictCause +
+                ", mediaProfile=" + mMediaProfile.toString() + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        Bundle filteredExtras = maybeCleanseExtras(mCallExtras);
+        out.writeInt(mServiceType);
+        out.writeInt(mCallType);
+        out.writeBundle(filteredExtras);
+        out.writeParcelable(mMediaProfile, 0);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mServiceType = in.readInt();
+        mCallType = in.readInt();
+        mCallExtras = in.readBundle();
+        mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+    }
+
+    public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
+        @Override
+        public ImsCallProfile createFromParcel(Parcel in) {
+            return new ImsCallProfile(in);
+        }
+
+        @Override
+        public ImsCallProfile[] newArray(int size) {
+            return new ImsCallProfile[size];
+        }
+    };
+
+    public int getServiceType() {
+        return mServiceType;
+    }
+
+    public int getCallType() {
+        return mCallType;
+    }
+
+    public int getRestrictCause() {
+        return mRestrictCause;
+    }
+
+    public Bundle getCallExtras() {
+        return mCallExtras;
+    }
+
+    public ImsStreamMediaProfile getMediaProfile() {
+        return mMediaProfile;
+    }
+
+    /**
+     * Converts from the call types defined in {@link ImsCallProfile} to the
+     * video state values defined in {@link VideoProfile}.
+     *
+     * @param callProfile The call profile.
+     * @return The video state.
+     */
+    public static int getVideoStateFromImsCallProfile(ImsCallProfile callProfile) {
+        int videostate = getVideoStateFromCallType(callProfile.mCallType);
+        if (callProfile.isVideoPaused() && !VideoProfile.isAudioOnly(videostate)) {
+            videostate |= VideoProfile.STATE_PAUSED;
+        } else {
+            videostate &= ~VideoProfile.STATE_PAUSED;
+        }
+        return videostate;
+    }
+
+    /**
+     * Translates a {@link ImsCallProfile} {@code CALL_TYPE_*} constant into a video state.
+     * @param callType The call type.
+     * @return The video state.
+     */
+    public static int getVideoStateFromCallType(int callType) {
+        int videostate = VideoProfile.STATE_AUDIO_ONLY;
+        switch (callType) {
+            case CALL_TYPE_VT_TX:
+                videostate = VideoProfile.STATE_TX_ENABLED;
+                break;
+            case CALL_TYPE_VT_RX:
+                videostate = VideoProfile.STATE_RX_ENABLED;
+                break;
+            case CALL_TYPE_VT:
+                videostate = VideoProfile.STATE_BIDIRECTIONAL;
+                break;
+            case CALL_TYPE_VOICE:
+                videostate = VideoProfile.STATE_AUDIO_ONLY;
+                break;
+            default:
+                videostate = VideoProfile.STATE_AUDIO_ONLY;
+                break;
+        }
+        return videostate;
+    }
+
+    /**
+     * Converts from the video state values defined in {@link VideoProfile}
+     * to the call types defined in {@link ImsCallProfile}.
+     *
+     * @param videoState The video state.
+     * @return The call type.
+     */
+    public static int getCallTypeFromVideoState(int videoState) {
+        boolean videoTx = isVideoStateSet(videoState, VideoProfile.STATE_TX_ENABLED);
+        boolean videoRx = isVideoStateSet(videoState, VideoProfile.STATE_RX_ENABLED);
+        boolean isPaused = isVideoStateSet(videoState, VideoProfile.STATE_PAUSED);
+        if (isPaused) {
+            return ImsCallProfile.CALL_TYPE_VT_NODIR;
+        } else if (videoTx && !videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT_TX;
+        } else if (!videoTx && videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT_RX;
+        } else if (videoTx && videoRx) {
+            return ImsCallProfile.CALL_TYPE_VT;
+        }
+        return ImsCallProfile.CALL_TYPE_VOICE;
+    }
+
+    /**
+     * Badly named old method, kept for compatibility.
+     * See {@link #presentationToOir(int)}.
+     * @hide
+     */
+    public static int presentationToOIR(int presentation) {
+        switch (presentation) {
+            case PhoneConstants.PRESENTATION_RESTRICTED:
+                return ImsCallProfile.OIR_PRESENTATION_RESTRICTED;
+            case PhoneConstants.PRESENTATION_ALLOWED:
+                return ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED;
+            case PhoneConstants.PRESENTATION_PAYPHONE:
+                return ImsCallProfile.OIR_PRESENTATION_PAYPHONE;
+            case PhoneConstants.PRESENTATION_UNKNOWN:
+                return ImsCallProfile.OIR_PRESENTATION_UNKNOWN;
+            default:
+                return ImsCallProfile.OIR_DEFAULT;
+        }
+    }
+
+    /**
+     * Translate presentation value to OIR value
+     * @param presentation
+     * @return OIR values
+     */
+    public static int presentationToOir(int presentation) {
+        return presentationToOIR(presentation);
+    }
+
+    /**
+     * Translate OIR value to presentation value
+     * @param oir value
+     * @return presentation value
+     * @hide
+     */
+    public static int OIRToPresentation(int oir) {
+        switch(oir) {
+            case ImsCallProfile.OIR_PRESENTATION_RESTRICTED:
+                return PhoneConstants.PRESENTATION_RESTRICTED;
+            case ImsCallProfile.OIR_PRESENTATION_NOT_RESTRICTED:
+                return PhoneConstants.PRESENTATION_ALLOWED;
+            case ImsCallProfile.OIR_PRESENTATION_PAYPHONE:
+                return PhoneConstants.PRESENTATION_PAYPHONE;
+            case ImsCallProfile.OIR_PRESENTATION_UNKNOWN:
+                return PhoneConstants.PRESENTATION_UNKNOWN;
+            default:
+                return PhoneConstants.PRESENTATION_UNKNOWN;
+        }
+    }
+
+    /**
+     * Checks if video call is paused
+     * @return true if call is video paused
+     */
+    public boolean isVideoPaused() {
+        return mMediaProfile.mVideoDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE;
+    }
+
+    /**
+     * Determines if the {@link ImsCallProfile} represents a video call.
+     *
+     * @return {@code true} if the profile is for a video call, {@code false} otherwise.
+     */
+    public boolean isVideoCall() {
+        return VideoProfile.isVideo(getVideoStateFromCallType(mCallType));
+    }
+
+    /**
+     * Cleanses a {@link Bundle} to ensure that it contains only data of type:
+     * 1. Primitive data types (e.g. int, bool, and other values determined by
+     * {@link android.os.PersistableBundle#isValidType(Object)}).
+     * 2. Other Bundles.
+     * 3. {@link Parcelable} objects in the {@code android.*} namespace.
+     * @param extras the source {@link Bundle}
+     * @return where all elements are valid types the source {@link Bundle} is returned unmodified,
+     *      otherwise a copy of the {@link Bundle} with the invalid elements is returned.
+     */
+    private Bundle maybeCleanseExtras(Bundle extras) {
+        if (extras == null) {
+            return null;
+        }
+
+        int startSize = extras.size();
+        Bundle filtered = extras.filterValues();
+        int endSize = filtered.size();
+        if (startSize != endSize) {
+            Log.i(TAG, "maybeCleanseExtras: " + (startSize - endSize) + " extra values were "
+                    + "removed - only primitive types and system parcelables are permitted.");
+        }
+        return filtered;
+    }
+
+    /**
+     * Determines if a video state is set in a video state bit-mask.
+     *
+     * @param videoState The video state bit mask.
+     * @param videoStateToCheck The particular video state to check.
+     * @return True if the video state is set in the bit-mask.
+     */
+    private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
+        return (videoState & videoStateToCheck) == videoStateToCheck;
+    }
+}
diff --git a/android/telephony/ims/ImsCallSession.java b/android/telephony/ims/ImsCallSession.java
new file mode 100644
index 0000000..a20d4f5
--- /dev/null
+++ b/android/telephony/ims/ImsCallSession.java
@@ -0,0 +1,1425 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.util.Log;
+
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ * It directly communicates with IMS service which implements the IMS protocol behavior.
+ *
+ * @hide
+ */
+public class ImsCallSession {
+    private static final String TAG = "ImsCallSession";
+
+    /**
+     * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition.
+     * This is kept around for capability reasons.
+     */
+    public static class State {
+        public static final int IDLE = 0;
+        public static final int INITIATED = 1;
+        public static final int NEGOTIATING = 2;
+        public static final int ESTABLISHING = 3;
+        public static final int ESTABLISHED = 4;
+
+        public static final int RENEGOTIATING = 5;
+        public static final int REESTABLISHING = 6;
+
+        public static final int TERMINATING = 7;
+        public static final int TERMINATED = 8;
+
+        public static final int INVALID = (-1);
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case IDLE:
+                    return "IDLE";
+                case INITIATED:
+                    return "INITIATED";
+                case NEGOTIATING:
+                    return "NEGOTIATING";
+                case ESTABLISHING:
+                    return "ESTABLISHING";
+                case ESTABLISHED:
+                    return "ESTABLISHED";
+                case RENEGOTIATING:
+                    return "RENEGOTIATING";
+                case REESTABLISHING:
+                    return "REESTABLISHING";
+                case TERMINATING:
+                    return "TERMINATING";
+                case TERMINATED:
+                    return "TERMINATED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        private State() {
+        }
+    }
+
+    /**
+     * Listener for events relating to an IMS session, such as when a session is being
+     * recieved ("on ringing") or a call is outgoing ("on calling").
+     * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+     * @hide
+     */
+    public static class Listener {
+        /**
+         * Called when a request is sent out to initiate a new session
+         * and 1xx response is received from the network.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionProgressing(ImsCallSession session,
+                ImsStreamMediaProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is established.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionStarted(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session establishment is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session establishment failure
+         */
+        public void callSessionStartFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is terminated.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session termination
+         */
+        public void callSessionTerminated(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is in hold.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionHeld(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session hold is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session hold failure
+         */
+        public void callSessionHoldFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session hold is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionHoldReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session resume is done.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionResumed(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session resume is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session resume failure
+         */
+        public void callSessionResumeFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session resume is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionResumeReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session merge has been started.  At this point, the {@code newSession}
+         * represents the session which has been initiated to the IMS conference server for the
+         * new merged conference.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param newSession the session object that is merged with an active & hold session
+         */
+        public void callSessionMergeStarted(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session merge is successful and the merged session is active.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionMergeComplete(ImsCallSession session) {
+        }
+
+        /**
+         * Called when the session merge has failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the call merge failure
+         */
+        public void callSessionMergeFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is updated (except for hold/unhold).
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionUpdated(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session update is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session update failure
+         */
+        public void callSessionUpdateFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session update is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionUpdateReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is extended to the conference session.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param newSession the session object that is extended to the conference
+         *      from the active session
+         */
+        public void callSessionConferenceExtended(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the conference extension is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference extension failure
+         */
+        public void callSessionConferenceExtendFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the conference extension is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionConferenceExtendReceived(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the invitation request of the participants is delivered to the conference
+         * server.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Called when the invitation request of the participants is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference invitation failure
+         */
+        public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the removal request of the participants is delivered to the conference
+         * server.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Called when the removal request of the participants is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference removal failure
+         */
+        public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the conference state is updated.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionConferenceStateUpdated(ImsCallSession session,
+                ImsConferenceState state) {
+            // no-op
+        }
+
+        /**
+         * Called when the USSD message is received from the network.
+         *
+         * @param mode mode of the USSD message (REQUEST / NOTIFY)
+         * @param ussdMessage USSD message
+         */
+        public void callSessionUssdMessageReceived(ImsCallSession session,
+                int mode, String ussdMessage) {
+            // no-op
+        }
+
+        /**
+         * Called when an {@link ImsCallSession} may handover from one radio technology to another.
+         * For example, the session may handover from WIFI to LTE if conditions are right.
+         * <p>
+         * If handover is attempted,
+         * {@link #callSessionHandover(ImsCallSession, int, int, ImsReasonInfo)} or
+         * {@link #callSessionHandoverFailed(ImsCallSession, int, int, ImsReasonInfo)} will be
+         * called to indicate the success or failure of the handover.
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         */
+        public void callSessionMayHandover(ImsCallSession session, int srcAccessTech,
+                int targetAccessTech) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology changes
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         * @param reasonInfo
+         */
+        public void callSessionHandover(ImsCallSession session,
+                                 int srcAccessTech, int targetAccessTech,
+                                 ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology change fails
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         * @param reasonInfo handover failure reason
+         */
+        public void callSessionHandoverFailed(ImsCallSession session,
+                                       int srcAccessTech, int targetAccessTech,
+                                       ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when TTY mode of remote party changed
+         *
+         * @param session IMS session object
+         * @param mode TTY mode of remote party
+         */
+        public void callSessionTtyModeReceived(ImsCallSession session,
+                                       int mode) {
+            // no-op
+        }
+
+        /**
+         * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+         *
+         * @param session The call session.
+         * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+         *      otherwise.
+         */
+        public void callSessionMultipartyStateChanged(ImsCallSession session,
+                boolean isMultiParty) {
+            // no-op
+        }
+
+        /**
+         * Called when the session supplementary service is received
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionSuppServiceReceived(ImsCallSession session,
+                ImsSuppServiceNotification suppServiceInfo) {
+        }
+
+        /**
+         * Received RTT modify request from Remote Party
+         */
+        public void callSessionRttModifyRequestReceived(ImsCallSession session,
+            ImsCallProfile callProfile) {
+            // no-op
+        }
+
+        /**
+         * Received response for RTT modify request
+         */
+        public void callSessionRttModifyResponseReceived(int status) {
+            // no -op
+        }
+
+        /**
+         * Device received RTT message from Remote UE
+         */
+        public void callSessionRttMessageReceived(String rttMessage) {
+            // no-op
+        }
+    }
+
+    private final IImsCallSession miSession;
+    private boolean mClosed = false;
+    private Listener mListener;
+
+    /** @hide */
+    public ImsCallSession(IImsCallSession iSession) {
+        miSession = iSession;
+
+        if (iSession != null) {
+            try {
+                iSession.setListener(new IImsCallSessionListenerProxy());
+            } catch (RemoteException e) {
+            }
+        } else {
+            mClosed = true;
+        }
+    }
+
+    /** @hide */
+    public ImsCallSession(IImsCallSession iSession, Listener listener) {
+        this(iSession);
+        setListener(listener);
+    }
+
+    /**
+     * Closes this object. This object is not usable after being closed.
+     */
+    public void close() {
+        synchronized (this) {
+            if (mClosed) {
+                return;
+            }
+
+            try {
+                miSession.close();
+                mClosed = true;
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     */
+    public String getCallId() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getCallId();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the call profile that this session is associated with
+     *
+     * @return the call profile that this session is associated with
+     */
+    public ImsCallProfile getCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the local call profile that this session is associated with
+     *
+     * @return the local call profile that this session is associated with
+     */
+    public ImsCallProfile getLocalCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getLocalCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the remote call profile that this session is associated with
+     *
+     * @return the remote call profile that this session is associated with
+     */
+    public ImsCallProfile getRemoteCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getRemoteCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the video call provider for the session.
+     *
+     * @return The video call provider.
+     * @hide
+     */
+    public IImsVideoCallProvider getVideoCallProvider() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getVideoCallProvider();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the value associated with the specified property of this session.
+     *
+     * @return the string value associated with the specified property
+     */
+    public String getProperty(String name) {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getProperty(name);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the session state.
+     * The value returned must be one of the states in {@link State}.
+     *
+     * @return the session state
+     */
+    public int getState() {
+        if (mClosed) {
+            return State.INVALID;
+        }
+
+        try {
+            return miSession.getState();
+        } catch (RemoteException e) {
+            return State.INVALID;
+        }
+    }
+
+    /**
+     * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
+     * closed state).
+     *
+     * @return {@code True} if the session is alive.
+     */
+    public boolean isAlive() {
+        if (mClosed) {
+            return false;
+        }
+
+        int state = getState();
+        switch (state) {
+            case State.IDLE:
+            case State.INITIATED:
+            case State.NEGOTIATING:
+            case State.ESTABLISHING:
+            case State.ESTABLISHED:
+            case State.RENEGOTIATING:
+            case State.REESTABLISHING:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Gets the native IMS call session.
+     * @hide
+     */
+    public IImsCallSession getSession() {
+        return miSession;
+    }
+
+    /**
+     * Checks if the session is in call.
+     *
+     * @return true if the session is in call
+     */
+    public boolean isInCall() {
+        if (mClosed) {
+            return false;
+        }
+
+        try {
+            return miSession.isInCall();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Sets the listener to listen to the session events. A {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     * @hide
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call is muted, false otherwise
+     */
+    public void setMute(boolean muted) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.setMute(muted);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Initiates an IMS call with the specified target and call profile.
+     * The session listener is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+     */
+    public void start(String callee, ImsCallProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.start(callee, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Initiates an IMS conference call with the specified target and call profile.
+     * The session listener is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+     */
+    public void start(String[] participants, ImsCallProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.startConference(participants, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see Listener#callSessionStarted
+     */
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.accept(callType, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Deflects an incoming call.
+     *
+     * @param number number to be deflected to
+     */
+    public void deflect(String number) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.deflect(number);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call
+     * @see Listener#callSessionStartFailed
+     */
+    public void reject(int reason) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.reject(reason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @see Listener#callSessionTerminated
+     */
+    public void terminate(int reason) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.terminate(reason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
+     */
+    public void hold(ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.hold(profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
+     * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
+     */
+    public void resume(ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.resume(profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Merges the active & hold call. When it succeeds,
+     * {@link Listener#callSessionMergeStarted} is called.
+     *
+     * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
+     */
+    public void merge() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.merge();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+     */
+    public void update(int callType, ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.update(callType, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants list to be invited to the conference call after extending the call
+     * @see Listener#callSessionConferenceExtended
+     * @see Listener#callSessionConferenceExtendFailed
+     */
+    public void extendToConference(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.extendToConference(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants list to be invited to the conference call
+     * @see Listener#callSessionInviteParticipantsRequestDelivered
+     * @see Listener#callSessionInviteParticipantsRequestFailed
+     */
+    public void inviteParticipants(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.inviteParticipants(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see Listener#callSessionRemoveParticipantsRequestDelivered
+     * @see Listener#callSessionRemoveParticipantsRequestFailed
+     */
+    public void removeParticipants(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.removeParticipants(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void sendDtmf(char c, Message result) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendDtmf(c, result);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void startDtmf(char c) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.startDtmf(c);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Stops a DTMF code.
+     */
+    public void stopDtmf() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.stopDtmf();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    public void sendUssd(String ussdMessage) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendUssd(ussdMessage);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Determines if the session is multiparty.
+     *
+     * @return {@code True} if the session is multiparty.
+     */
+    public boolean isMultiparty() {
+        if (mClosed) {
+            return false;
+        }
+
+        try {
+            return miSession.isMultiparty();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Sends Rtt Message
+     *
+     * @param rttMessage rtt text to be sent
+     */
+    public void sendRttMessage(String rttMessage) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRttMessage(rttMessage);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends RTT Upgrade request
+     *
+     * @param to   : expected profile
+     */
+    public void sendRttModifyRequest(ImsCallProfile to) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRttModifyRequest(to);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends RTT Upgrade response
+     *
+     * @param response : response for upgrade
+     */
+    public void sendRttModifyResponse(boolean response) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendRttModifyResponse(response);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * A listener type for receiving notification on IMS call session events.
+     * When an event is generated for an {@link IImsCallSession},
+     * the application is notified by having one of the methods called on
+     * the {@link IImsCallSessionListener}.
+     */
+    private class IImsCallSessionListenerProxy extends IImsCallSessionListener.Stub {
+        /**
+         * Notifies the result of the basic session operation (setup / terminate).
+         */
+        @Override
+        public void callSessionProgressing(ImsStreamMediaProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionProgressing(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionInitiated(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionStarted(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the result of the call hold/resume operation.
+         */
+        @Override
+        public void callSessionHeld(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionHeld(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionHoldReceived(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionResumed(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionResumed(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionResumeReceived(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        /**
+         * Notifies the start of a call merge operation.
+         *
+         * @param newSession The merged call session.
+         * @param profile The call profile.
+         */
+        @Override
+        public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile) {
+            // This callback can be used for future use to add additional
+            // functionality that may be needed between conference start and complete
+            Log.d(TAG, "callSessionMergeStarted");
+        }
+
+        /**
+         * Notifies the successful completion of a call merge operation.
+         *
+         * @param newSession The call session.
+         */
+        @Override
+        public void callSessionMergeComplete(IImsCallSession newSession) {
+            if (mListener != null) {
+                if (newSession != null) {
+                    // Check if the active session is the same session that was
+                    // active before the merge request was sent.
+                    ImsCallSession validActiveSession = ImsCallSession.this;
+                    try {
+                        if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
+                            // New session created after conference
+                            validActiveSession = new ImsCallSession(newSession);
+                        }
+                    } catch (RemoteException rex) {
+                        Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
+                    }
+                    mListener.callSessionMergeComplete(validActiveSession);
+               } else {
+                   // Session already exists. Hence no need to pass
+                   mListener.callSessionMergeComplete(null);
+               }
+            }
+        }
+
+        /**
+         * Notifies of a failure to perform a call merge operation.
+         *
+         * @param reasonInfo The merge failure reason.
+         */
+        @Override
+        public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the result of call upgrade / downgrade or any other call updates.
+         */
+        @Override
+        public void callSessionUpdated(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionUpdated(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionUpdateReceived(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        /**
+         * Notifies the result of conference extension.
+         */
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession newSession,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtended(ImsCallSession.this,
+                        new ImsCallSession(newSession), profile);
+            }
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+                        new ImsCallSession(newSession), profile);
+            }
+        }
+
+        /**
+         * Notifies the result of the participant invitation / removal to/from
+         * the conference session.
+         */
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered() {
+            if (mListener != null) {
+                mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+                        reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered() {
+            if (mListener != null) {
+                mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+                        reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the changes of the conference info. in the conference session.
+         */
+        @Override
+        public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+            if (mListener != null) {
+                mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+            }
+        }
+
+        /**
+         * Notifies the incoming USSD message.
+         */
+        @Override
+        public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
+            if (mListener != null) {
+                mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+            }
+        }
+
+        /**
+         * Notifies of a case where a {@link ImsCallSession} may
+         * potentially handover from one radio technology to another.
+         * @param srcAccessTech The source radio access technology; one of the access technology
+         *                      constants defined in {@link android.telephony.ServiceState}.  For
+         *                      example
+         *                      {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+         * @param targetAccessTech The target radio access technology; one of the access technology
+         *                      constants defined in {@link android.telephony.ServiceState}.  For
+         *                      example
+         *                      {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+         */
+        @Override
+        public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+            if (mListener != null) {
+                mListener.callSessionMayHandover(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech);
+            }
+        }
+
+        /**
+         * Notifies of handover information for this call
+         */
+        @Override
+        public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies of handover failure info for this call
+         */
+        @Override
+        public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the TTY mode received from remote party.
+         */
+        @Override
+        public void callSessionTtyModeReceived(int mode) {
+            if (mListener != null) {
+                mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+            }
+        }
+
+        /**
+         * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+         *
+         * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+         *      otherwise.
+         */
+        public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+            if (mListener != null) {
+                mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+            }
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
+            if (mListener != null) {
+                mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+            }
+        }
+
+        /**
+         * Received RTT modify request from remote party
+         */
+        @Override
+        public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
+            if (mListener != null) {
+                mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
+            }
+        }
+
+        /**
+         * Received response for RTT modify request
+         */
+        @Override
+        public void callSessionRttModifyResponseReceived(int status) {
+            if (mListener != null) {
+                mListener.callSessionRttModifyResponseReceived(status);
+            }
+        }
+
+        /**
+         * RTT Message received
+         */
+        @Override
+        public void callSessionRttMessageReceived(String rttMessage) {
+            if (mListener != null) {
+                mListener.callSessionRttMessageReceived(rttMessage);
+            }
+        }
+    }
+
+    /**
+     * Provides a string representation of the {@link ImsCallSession}.  Primarily intended for
+     * use in log statements.
+     *
+     * @return String representation of session.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ImsCallSession objId:");
+        sb.append(System.identityHashCode(this));
+        sb.append(" state:");
+        sb.append(State.toString(getState()));
+        sb.append(" callId:");
+        sb.append(getCallId());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/android/telephony/ims/ImsCallSessionListener.java b/android/telephony/ims/ImsCallSessionListener.java
new file mode 100644
index 0000000..a7f124a
--- /dev/null
+++ b/android/telephony/ims/ImsCallSessionListener.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * Listener interface for notifying the Framework's {@link ImsCallSession} for updates to an ongoing
+ * IMS call.
+ *
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this implementation or you
+// will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+// TODO: APIs in here do not conform to API guidelines yet. This can be changed if
+// ImsCallSessionListenerConverter is also changed.
+@SystemApi
+public class ImsCallSessionListener {
+
+    private final IImsCallSessionListener mListener;
+
+    /** @hide */
+    public ImsCallSessionListener(IImsCallSessionListener l) {
+        mListener = l;
+    }
+
+    /**
+     * A request has been sent out to initiate a new IMS call session and a 1xx response has been
+     * received from the network.
+     */
+    public void callSessionProgressing(ImsStreamMediaProfile profile) {
+        try {
+            mListener.callSessionProgressing(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has been initiated.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionInitiated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionInitiated(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session establishment has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+     * establishment failure.
+     */
+    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionInitiatedFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has been terminated.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session termination.
+     */
+    public void callSessionTerminated(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionTerminated(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has started the process of holding the call. If it fails,
+     * {@link #callSessionHoldFailed(ImsReasonInfo)} should be called.
+     *
+     * If the IMS call session is resumed, call {@link #callSessionResumed(ImsCallProfile)}.
+     *
+     * @param profile The associated {@link ImsCallProfile} of the call session that has been put
+     * on hold.
+     */
+    public void callSessionHeld(ImsCallProfile profile) {
+        try {
+            mListener.callSessionHeld(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has failed to be held.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the session hold failure.
+     */
+    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHoldFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * This IMS Call session has been put on hold by the remote party.
+     *
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionHoldReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionHoldReceived(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has started the process of resuming the call. If the process of resuming
+     * the call fails, call {@link #callSessionResumeFailed(ImsReasonInfo)}.
+     *
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionResumed(ImsCallProfile profile) {
+        try {
+            mListener.callSessionResumed(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session resume has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the session resume
+     * failure.
+     */
+    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionResumeFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The remote party has resumed this IMS call session.
+     *
+     * @param profile {@link ImsCallProfile} associated with the IMS call session.
+     */
+    public void callSessionResumeReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionResumeReceived(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session merge has been started.  At this point, the {@code newSession}
+     * represents the IMS call session which represents the new merged conference and has been
+     * initiated to the IMS conference server.
+     *
+     * @param newSession the {@link ImsCallSessionImplBase} that represents the merged active & held
+     * sessions.
+     * @param profile The {@link ImsCallProfile} associated with this IMS call session.
+     */
+    public void callSessionMergeStarted(ImsCallSessionImplBase newSession, ImsCallProfile profile)
+    {
+        try {
+            mListener.callSessionMergeStarted(newSession != null ?
+                            newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations.
+     * See {@link #callSessionMergeStarted(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionMergeStarted(IImsCallSession newSession, ImsCallProfile profile)
+    {
+        try {
+            mListener.callSessionMergeStarted(newSession, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The session merge is successful and the merged {@link ImsCallSession} is active.
+     *
+     * @param newSession the new {@link ImsCallSessionImplBase}
+     *                  that represents the conference IMS call
+     * session.
+     */
+    public void callSessionMergeComplete(ImsCallSessionImplBase newSession) {
+        try {
+            mListener.callSessionMergeComplete(newSession != null ?
+                    newSession.getServiceImpl() : null);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method for older implementations of ImsService.
+     *
+     * See {@link #callSessionMergeComplete(ImsCallSessionImplBase)}}.
+     *
+     * @hide
+     */
+    public void callSessionMergeComplete(IImsCallSession newSession) {
+        try {
+            mListener.callSessionMergeComplete(newSession);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session merge has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} contining the reason for the call merge failure.
+     */
+    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionMergeFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session profile has been updated. Does not include holding or resuming a call.
+     *
+     * @param profile The {@link ImsCallProfile} associated with the updated IMS call session.
+     */
+    public void callSessionUpdated(ImsCallProfile profile) {
+        try {
+            mListener.callSessionUpdated(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session profile update has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing a reason for the session update failure.
+     */
+    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionUpdateFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session profile has received an update from the remote user.
+     *
+     * @param profile The new {@link ImsCallProfile} associated with the update.
+     */
+    public void callSessionUpdateReceived(ImsCallProfile profile) {
+        try {
+            mListener.callSessionUpdateReceived(profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Called when the session has been extended to a conference session.
+     *
+     * If the conference extension fails, call
+     * {@link #callSessionConferenceExtendFailed(ImsReasonInfo)}.
+     *
+     * @param newSession the session object that is extended to the conference from the active
+     * IMS Call session.
+     * @param profile The {@link ImsCallProfile} associated with the IMS call session.
+     */
+    public void callSessionConferenceExtended(ImsCallSessionImplBase newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtended(
+                    newSession != null ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method to interface with older versions of ImsService.
+     * See {@link #callSessionConferenceExtended(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionConferenceExtended(IImsCallSession newSession, ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtended(newSession, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The previous conference extension has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} containing the detailed reason of the conference
+     * extension failure.
+     */
+    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionConferenceExtendFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * A conference extension has been received received from the remote party.
+     *
+     * @param newSession An {@link ImsCallSessionImplBase}
+     *                   representing the extended IMS call session.
+     * @param profile The {@link ImsCallProfile} associated with the new IMS call session.
+     */
+    public void callSessionConferenceExtendReceived(ImsCallSessionImplBase newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtendReceived(newSession != null
+                    ? newSession.getServiceImpl() : null, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Compatibility method to interface with older versions of ImsService.
+     * See {@link #callSessionConferenceExtendReceived(ImsCallSessionImplBase, ImsCallProfile)}.
+     *
+     * @hide
+     */
+    public void callSessionConferenceExtendReceived(IImsCallSession newSession,
+            ImsCallProfile profile) {
+        try {
+            mListener.callSessionConferenceExtendReceived(newSession, profile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The request to invite participants to the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionInviteParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionInviteParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The previous request to invite participants to the conference (see
+     * {@link #callSessionInviteParticipantsRequestDelivered()}) has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason forthe conference invitation
+     * failure.
+     */
+    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+    {
+        try {
+            mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The request to remove participants from the conference has been delivered to the conference
+     * server.
+     */
+    public void callSessionRemoveParticipantsRequestDelivered() {
+        try {
+            mListener.callSessionRemoveParticipantsRequestDelivered();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The previous request to remove participants from the conference (see
+     * {@link #callSessionRemoveParticipantsRequestDelivered()}) has failed.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason for the conference removal
+     * failure.
+     */
+    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+    {
+        try {
+            mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session's conference state has changed.
+     *
+     * @param state The new {@link ImsConferenceState} associated with the conference.
+     */
+    public void callSessionConferenceStateUpdated(ImsConferenceState state) {
+        try {
+            mListener.callSessionConferenceStateUpdated(state);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session has received a Ussd message.
+     *
+     * @param mode The mode of the USSD message, either
+     * {@link ImsCallSessionImplBase#USSD_MODE_NOTIFY} or
+     * {@link ImsCallSessionImplBase#USSD_MODE_REQUEST}.
+     * @param ussdMessage The USSD message.
+     */
+    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+    {
+        try {
+            mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An {@link ImsCallSession} may potentially handover from one radio
+     * technology to another.
+     *
+     * @param srcAccessTech The source radio access technology; one of the access technology
+     * constants defined in {@link android.telephony.ServiceState}. For example
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     * @param targetAccessTech The target radio access technology; one of the access technology
+     * constants defined in {@link android.telephony.ServiceState}. For example
+     * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     */
+    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
+    {
+        try {
+            mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session's access technology has changed.
+     *
+     * @param srcAccessTech original access technology, defined in
+     * {@link android.telephony.ServiceState}.
+     * @param targetAccessTech new access technology, defined in
+     * {@link android.telephony.ServiceState}.
+     * @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+     */
+    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The IMS call session's access technology change has failed..
+     *
+     * @param srcAccessTech original access technology
+     * @param targetAccessTech new access technology
+     * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+     */
+    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The TTY mode has been changed by the remote party.
+     *
+     * @param mode one of the following: -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+     */
+    public void callSessionTtyModeReceived(int mode) {
+        try {
+            mListener.callSessionTtyModeReceived(mode);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The multiparty state has been changed for this {@code ImsCallSession}.
+     *
+     * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+     */
+    public void callSessionMultipartyStateChanged(boolean isMultiParty) {
+        try {
+            mListener.callSessionMultipartyStateChanged(isMultiParty);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Supplementary service information has been received for the current IMS call session.
+     *
+     * @param suppSrvNotification The {@link ImsSuppServiceNotification} containing the change.
+     */
+    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+    {
+        try {
+            mListener.callSessionSuppServiceReceived(suppSrvNotification);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An RTT modify request has been received from the remote party.
+     *
+     * @param callProfile An {@link ImsCallProfile} with the updated attributes
+     */
+    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+    {
+        try {
+            mListener.callSessionRttModifyRequestReceived(callProfile);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An RTT modify response has been received.
+     *
+     * @param status the received response for RTT modify request.
+     */
+    public void callSessionRttModifyResponseReceived(int status) {
+        try {
+            mListener.callSessionRttModifyResponseReceived(status);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * An RTT message has been received from the remote party.
+     *
+     * @param rttMessage The RTT message that has been received.
+     */
+    public void callSessionRttMessageReceived(String rttMessage) {
+        try {
+            mListener.callSessionRttMessageReceived(rttMessage);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
+
diff --git a/android/telephony/ims/ImsConferenceState.java b/android/telephony/ims/ImsConferenceState.java
new file mode 100644
index 0000000..66d2f8d
--- /dev/null
+++ b/android/telephony/ims/ImsConferenceState.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Call;
+import android.telecom.Connection;
+
+/**
+ * Provides the conference information (defined in RFC 4575) for IMS conference call.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsConferenceState implements Parcelable {
+    /**
+     * conference-info : user
+     */
+    // user (String) : Tel or SIP URI
+    public static final String USER = "user";
+    // user > display text (String)
+    public static final String DISPLAY_TEXT = "display-text";
+    // user > endpoint (String) : URI or GRUU or Phone number
+    public static final String ENDPOINT = "endpoint";
+    // user > endpoint > status
+    public static final String STATUS = "status";
+
+    /**
+     * status-type (String) :
+     * "pending" : Endpoint is not yet in the session, but it is anticipated that he/she will
+     *      join in the near future.
+     * "dialing-out" : Focus has dialed out to connect the endpoint to the conference,
+     *      but the endpoint is not yet in the roster (probably being authenticated).
+     * "dialing-in" : Endpoint is dialing into the conference, not yet in the roster
+     *      (probably being authenticated).
+     * "alerting" : PSTN alerting or SIP 180 Ringing was returned for the outbound call;
+     *      endpoint is being alerted.
+     * "on-hold" : Active signaling dialog exists between an endpoint and a focus,
+     *      but endpoint is "on-hold" for this conference, i.e., he/she is neither "hearing"
+     *      the conference mix nor is his/her media being mixed in the conference.
+     * "connected" : Endpoint is a participant in the conference. Depending on the media policies,
+     *      he/she can send and receive media to and from other participants.
+     * "disconnecting" : Focus is in the process of disconnecting the endpoint
+     *      (e.g. in SIP a DISCONNECT or BYE was sent to the endpoint).
+     * "disconnected" : Endpoint is not a participant in the conference, and no active dialog
+     *      exists between the endpoint and the focus.
+     * "muted-via-focus" : Active signaling dialog exists beween an endpoint and a focus and
+     *      the endpoint can "listen" to the conference, but the endpoint's media is not being
+     *      mixed into the conference.
+     * "connect-fail" : Endpoint fails to join the conference by rejecting the conference call.
+     */
+    public static final String STATUS_PENDING = "pending";
+    public static final String STATUS_DIALING_OUT = "dialing-out";
+    public static final String STATUS_DIALING_IN = "dialing-in";
+    public static final String STATUS_ALERTING = "alerting";
+    public static final String STATUS_ON_HOLD = "on-hold";
+    public static final String STATUS_CONNECTED = "connected";
+    public static final String STATUS_DISCONNECTING = "disconnecting";
+    public static final String STATUS_DISCONNECTED = "disconnected";
+    public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus";
+    public static final String STATUS_CONNECT_FAIL = "connect-fail";
+    public static final String STATUS_SEND_ONLY = "sendonly";
+    public static final String STATUS_SEND_RECV = "sendrecv";
+
+    /**
+     * conference-info : SIP status code (integer)
+     */
+    public static final String SIP_STATUS_CODE = "sipstatuscode";
+
+    public final HashMap<String, Bundle> mParticipants = new HashMap<String, Bundle>();
+
+    /** @hide */
+    public ImsConferenceState() {
+    }
+
+    private ImsConferenceState(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mParticipants.size());
+
+        if (mParticipants.size() > 0) {
+            Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
+
+            if (entries != null) {
+                Iterator<Entry<String, Bundle>> iterator = entries.iterator();
+
+                while (iterator.hasNext()) {
+                    Entry<String, Bundle> entry = iterator.next();
+
+                    out.writeString(entry.getKey());
+                    out.writeParcelable(entry.getValue(), 0);
+                }
+            }
+        }
+    }
+
+    private void readFromParcel(Parcel in) {
+        int size = in.readInt();
+
+        for (int i = 0; i < size; ++i) {
+            String user = in.readString();
+            Bundle state = in.readParcelable(null);
+            mParticipants.put(user, state);
+        }
+    }
+
+    public static final Creator<ImsConferenceState> CREATOR =
+            new Creator<ImsConferenceState>() {
+        @Override
+        public ImsConferenceState createFromParcel(Parcel in) {
+            return new ImsConferenceState(in);
+        }
+
+        @Override
+        public ImsConferenceState[] newArray(int size) {
+            return new ImsConferenceState[size];
+        }
+    };
+
+    /**
+     * Translates an {@code ImsConferenceState} status type to a telecom connection state.
+     *
+     * @param status The status type.
+     * @return The corresponding {@link android.telecom.Connection} state.
+     */
+    public static int getConnectionStateForStatus(String status) {
+        if (status.equals(STATUS_PENDING)) {
+            return Connection.STATE_INITIALIZING;
+        } else if (status.equals(STATUS_DIALING_IN)) {
+            return Connection.STATE_RINGING;
+        } else if (status.equals(STATUS_ALERTING) ||
+                status.equals(STATUS_DIALING_OUT)) {
+            return Connection.STATE_DIALING;
+        } else if (status.equals(STATUS_ON_HOLD) ||
+                status.equals(STATUS_SEND_ONLY)) {
+            return Connection.STATE_HOLDING;
+        } else if (status.equals(STATUS_CONNECTED) ||
+                status.equals(STATUS_MUTED_VIA_FOCUS) ||
+                status.equals(STATUS_DISCONNECTING) ||
+                status.equals(STATUS_SEND_RECV)) {
+            return Connection.STATE_ACTIVE;
+        } else if (status.equals(STATUS_DISCONNECTED)) {
+            return Connection.STATE_DISCONNECTED;
+        }
+        return Call.STATE_ACTIVE;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        sb.append(ImsConferenceState.class.getSimpleName());
+        sb.append(" ");
+        if (mParticipants.size() > 0) {
+            Set<Entry<String, Bundle>> entries = mParticipants.entrySet();
+
+            if (entries != null) {
+                Iterator<Entry<String, Bundle>> iterator = entries.iterator();
+                sb.append("<");
+                while (iterator.hasNext()) {
+                    Entry<String, Bundle> entry = iterator.next();
+                    sb.append(entry.getKey());
+                    sb.append(": ");
+                    Bundle participantData = entry.getValue();
+
+                    for (String key : participantData.keySet()) {
+                        sb.append(key);
+                        sb.append("=");
+                        if (ENDPOINT.equals(key) || USER.equals(key)) {
+                            sb.append(android.telecom.Log.pii(participantData.get(key)));
+                        } else {
+                            sb.append(participantData.get(key));
+                        }
+                        sb.append(", ");
+                    }
+                }
+                sb.append(">");
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/android/telephony/ims/ImsExternalCallState.java b/android/telephony/ims/ImsExternalCallState.java
new file mode 100644
index 0000000..e82c115
--- /dev/null
+++ b/android/telephony/ims/ImsExternalCallState.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telecom.Log;
+import android.telephony.Rlog;
+
+/*
+ * This file contains all the api's through which
+ * information received in Dialog Event Package can be
+ * queried
+ */
+
+/**
+ * Parcelable object to handle MultiEndpoint Dialog Information
+ * @hide
+ */
+@SystemApi
+public final class ImsExternalCallState implements Parcelable {
+
+    private static final String TAG = "ImsExternalCallState";
+
+    // Dialog States
+    public static final int CALL_STATE_CONFIRMED = 1;
+    public static final int CALL_STATE_TERMINATED = 2;
+    // Dialog Id
+    private int mCallId;
+    // Number
+    private Uri mAddress;
+    private boolean mIsPullable;
+    // CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
+    private int mCallState;
+    // ImsCallProfile#CALL_TYPE_*
+    private int mCallType;
+    private boolean mIsHeld;
+
+    /** @hide */
+    public ImsExternalCallState() {
+    }
+
+    /** @hide */
+    public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState,
+            int callType, boolean isCallheld) {
+        mCallId = callId;
+        mAddress = address;
+        mIsPullable = isPullable;
+        mCallState = callState;
+        mCallType = callType;
+        mIsHeld = isCallheld;
+        Rlog.d(TAG, "ImsExternalCallState = " + this);
+    }
+
+    /** @hide */
+    public ImsExternalCallState(Parcel in) {
+        mCallId = in.readInt();
+        ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
+        mAddress = in.readParcelable(classLoader);
+        mIsPullable = (in.readInt() != 0);
+        mCallState = in.readInt();
+        mCallType = in.readInt();
+        mIsHeld = (in.readInt() != 0);
+        Rlog.d(TAG, "ImsExternalCallState const = " + this);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCallId);
+        out.writeParcelable(mAddress, 0);
+        out.writeInt(mIsPullable ? 1 : 0);
+        out.writeInt(mCallState);
+        out.writeInt(mCallType);
+        out.writeInt(mIsHeld ? 1 : 0);
+        Rlog.d(TAG, "ImsExternalCallState writeToParcel = " + out.toString());
+    }
+
+    public static final Parcelable.Creator<ImsExternalCallState> CREATOR =
+            new Parcelable.Creator<ImsExternalCallState>() {
+        @Override
+        public ImsExternalCallState createFromParcel(Parcel in) {
+            return new ImsExternalCallState(in);
+        }
+
+        @Override
+        public ImsExternalCallState[] newArray(int size) {
+            return new ImsExternalCallState[size];
+        }
+    };
+
+    public int getCallId() {
+        return mCallId;
+    }
+
+    public Uri getAddress() {
+        return mAddress;
+    }
+
+    public boolean isCallPullable() {
+        return mIsPullable;
+    }
+
+    public int getCallState() {
+        return mCallState;
+    }
+
+    public int getCallType() {
+        return mCallType;
+    }
+
+    public boolean isCallHeld() {
+        return mIsHeld;
+    }
+
+    @Override
+    public String toString() {
+        return "ImsExternalCallState { mCallId = " + mCallId +
+                ", mAddress = " + Log.pii(mAddress) +
+                ", mIsPullable = " + mIsPullable +
+                ", mCallState = " + mCallState +
+                ", mCallType = " + mCallType +
+                ", mIsHeld = " + mIsHeld + "}";
+    }
+}
diff --git a/android/telephony/ims/ImsReasonInfo.java b/android/telephony/ims/ImsReasonInfo.java
new file mode 100644
index 0000000..34d7c65
--- /dev/null
+++ b/android/telephony/ims/ImsReasonInfo.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class enables an application to get details on why a method call failed.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsReasonInfo implements Parcelable {
+
+    /**
+     * Specific code of each types
+     */
+    public static final int CODE_UNSPECIFIED = 0;
+
+    /**
+     * LOCAL
+     */
+    // IMS -> Telephony
+    // The passed argument is an invalid
+    public static final int CODE_LOCAL_ILLEGAL_ARGUMENT = 101;
+    // The operation is invoked in invalid call state
+    public static final int CODE_LOCAL_ILLEGAL_STATE = 102;
+    // IMS service internal error
+    public static final int CODE_LOCAL_INTERNAL_ERROR = 103;
+    // IMS service goes down (service connection is lost)
+    public static final int CODE_LOCAL_IMS_SERVICE_DOWN = 106;
+    // No pending incoming call exists
+    public static final int CODE_LOCAL_NO_PENDING_CALL = 107;
+    // IMS Call ended during conference merge process
+    public static final int CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE = 108;
+
+    // IMS -> Telephony
+    // Service unavailable; by power off
+    public static final int CODE_LOCAL_POWER_OFF = 111;
+    // Service unavailable; by low battery
+    public static final int CODE_LOCAL_LOW_BATTERY = 112;
+    // Service unavailable; by out of service (data service state)
+    public static final int CODE_LOCAL_NETWORK_NO_SERVICE = 121;
+    // Service unavailable; by no LTE coverage
+    // (VoLTE is not supported even though IMS is registered)
+    public static final int CODE_LOCAL_NETWORK_NO_LTE_COVERAGE = 122;
+    // Service unavailable; by located in roaming area
+    public static final int CODE_LOCAL_NETWORK_ROAMING = 123;
+    // Service unavailable; by IP changed
+    public static final int CODE_LOCAL_NETWORK_IP_CHANGED = 124;
+    // Service unavailable; other
+    public static final int CODE_LOCAL_SERVICE_UNAVAILABLE = 131;
+    // Service unavailable; IMS connection is lost (IMS is not registered)
+    public static final int CODE_LOCAL_NOT_REGISTERED = 132;
+
+    // IMS <-> Telephony
+    // Max call exceeded
+    public static final int CODE_LOCAL_CALL_EXCEEDED = 141;
+    // IMS <- Telephony
+    // Call busy
+    public static final int CODE_LOCAL_CALL_BUSY = 142;
+    // Call decline
+    public static final int CODE_LOCAL_CALL_DECLINE = 143;
+    // IMS -> Telephony
+    // SRVCC is in progress
+    public static final int CODE_LOCAL_CALL_VCC_ON_PROGRESSING = 144;
+    // Resource reservation is failed (QoS precondition)
+    public static final int CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 145;
+    // Retry CS call; VoLTE service can't be provided by the network or remote end
+    // Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+    public static final int CODE_LOCAL_CALL_CS_RETRY_REQUIRED = 146;
+    // Retry VoLTE call; VoLTE service can't be provided by the network temporarily
+    public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147;
+    // IMS call is already terminated (in TERMINATED state)
+    public static final int CODE_LOCAL_CALL_TERMINATED = 148;
+    // Handover not feasible
+    public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149;
+
+    /**
+     * TIMEOUT (IMS -> Telephony)
+     */
+    // 1xx waiting timer is expired after sending INVITE request (MO only)
+    public static final int CODE_TIMEOUT_1XX_WAITING = 201;
+    // User no answer during call setup operation (MO/MT)
+    // MO : 200 OK to INVITE request is not received,
+    // MT : No action from user after alerting the call
+    public static final int CODE_TIMEOUT_NO_ANSWER = 202;
+    // User no answer during call update operation (MO/MT)
+    // MO : 200 OK to re-INVITE request is not received,
+    // MT : No action from user after alerting the call
+    public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
+
+    //Call was blocked by call barring
+    public static final int CODE_CALL_BARRED = 240;
+
+    //Call failures for FDN
+    public static final int CODE_FDN_BLOCKED = 241;
+
+    // Network does not accept the emergency call request because IMEI was used as identification
+    // and this capability is not supported by the network.
+    public static final int CODE_IMEI_NOT_ACCEPTED = 243;
+
+    //STK CC errors
+    public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+    public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
+
+    /**
+     * STATUSCODE (SIP response code) (IMS -> Telephony)
+     */
+    // 3xx responses
+    // SIP request is redirected
+    public static final int CODE_SIP_REDIRECTED = 321;
+    // 4xx responses
+    // 400 : Bad Request
+    public static final int CODE_SIP_BAD_REQUEST = 331;
+    // 403 : Forbidden
+    public static final int CODE_SIP_FORBIDDEN = 332;
+    // 404 : Not Found
+    public static final int CODE_SIP_NOT_FOUND = 333;
+    // 415 : Unsupported Media Type
+    // 416 : Unsupported URI Scheme
+    // 420 : Bad Extension
+    public static final int CODE_SIP_NOT_SUPPORTED = 334;
+    // 408 : Request Timeout
+    public static final int CODE_SIP_REQUEST_TIMEOUT = 335;
+    // 480 : Temporarily Unavailable
+    public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336;
+    // 484 : Address Incomplete
+    public static final int CODE_SIP_BAD_ADDRESS = 337;
+    // 486 : Busy Here
+    // 600 : Busy Everywhere
+    public static final int CODE_SIP_BUSY = 338;
+    // 487 : Request Terminated
+    public static final int CODE_SIP_REQUEST_CANCELLED = 339;
+    // 406 : Not Acceptable
+    // 488 : Not Acceptable Here
+    // 606 : Not Acceptable
+    public static final int CODE_SIP_NOT_ACCEPTABLE = 340;
+    // 410 : Gone
+    // 604 : Does Not Exist Anywhere
+    public static final int CODE_SIP_NOT_REACHABLE = 341;
+    // Others
+    public static final int CODE_SIP_CLIENT_ERROR = 342;
+    // 5xx responses
+    // 501 : Server Internal Error
+    public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351;
+    // 503 : Service Unavailable
+    public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352;
+    // 504 : Server Time-out
+    public static final int CODE_SIP_SERVER_TIMEOUT = 353;
+    // Others
+    public static final int CODE_SIP_SERVER_ERROR = 354;
+    // 6xx responses
+    // 603 : Decline
+    public static final int CODE_SIP_USER_REJECTED = 361;
+    // Others
+    public static final int CODE_SIP_GLOBAL_ERROR = 362;
+    // Emergency failure
+    public static final int CODE_EMERGENCY_TEMP_FAILURE = 363;
+    public static final int CODE_EMERGENCY_PERM_FAILURE = 364;
+
+    /**
+     * MEDIA (IMS -> Telephony)
+     */
+    // Media resource initialization failed
+    public static final int CODE_MEDIA_INIT_FAILED = 401;
+    // RTP timeout (no audio / video traffic in the session)
+    public static final int CODE_MEDIA_NO_DATA = 402;
+    // Media is not supported; so dropped the call
+    public static final int CODE_MEDIA_NOT_ACCEPTABLE = 403;
+    // Unknown media related errors
+    public static final int CODE_MEDIA_UNSPECIFIED = 404;
+
+    /**
+     * USER
+     */
+    // Telephony -> IMS
+    // User triggers the call end
+    public static final int CODE_USER_TERMINATED = 501;
+    // No action while an incoming call is ringing
+    public static final int CODE_USER_NOANSWER = 502;
+    // User ignores an incoming call
+    public static final int CODE_USER_IGNORE = 503;
+    // User declines an incoming call
+    public static final int CODE_USER_DECLINE = 504;
+    // Device declines/ends a call due to low battery
+    public static final int CODE_LOW_BATTERY = 505;
+    // Device declines call due to blacklisted call ID
+    public static final int CODE_BLACKLISTED_CALL_ID = 506;
+    // IMS -> Telephony
+    // The call is terminated by the network or remote user
+    public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+
+    /**
+     * Extra codes for the specific code value
+     * This value can be referred when the code is CODE_LOCAL_CALL_CS_RETRY_REQUIRED.
+     */
+    // Try to connect CS call; normal
+    public static final int EXTRA_CODE_CALL_RETRY_NORMAL = 1;
+    // Try to connect CS call without the notification to user
+    public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2;
+    // Try to connect CS call by the settings of the menu
+    public static final int EXTRA_CODE_CALL_RETRY_BY_SETTINGS = 3;
+
+    /**
+     * UT
+     */
+    public static final int CODE_UT_NOT_SUPPORTED = 801;
+    public static final int CODE_UT_SERVICE_UNAVAILABLE = 802;
+    public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
+    public static final int CODE_UT_NETWORK_ERROR = 804;
+    public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
+    //STK CC errors
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+    public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+    public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
+
+    /**
+     * ECBM
+     */
+    public static final int CODE_ECBM_NOT_SUPPORTED = 901;
+
+    /**
+     * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+     */
+    public static final int CODE_MULTIENDPOINT_NOT_SUPPORTED = 902;
+
+    /**
+     * Ims Registration error code
+     */
+    public static final int CODE_REGISTRATION_ERROR = 1000;
+
+    /**
+     * CALL DROP error codes (Call could drop because of many reasons like Network not available,
+     *  handover, failed, etc)
+     */
+
+    /**
+     * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+     * active wifi call and at the edge of coverage and there is no qualified LTE network available
+     * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+     * code is received as part of the handover message.
+     */
+    public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
+
+    /**
+     * MT call has ended due to a release from the network
+     * because the call was answered elsewhere
+     */
+    public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+    /**
+     * For MultiEndpoint - Call Pull request has failed
+     */
+    public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
+
+    /**
+     * For MultiEndpoint - Call has been pulled from primary to secondary
+     */
+    public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
+
+    /**
+     * Supplementary services (HOLD/RESUME) failure error codes.
+     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     */
+    public static final int CODE_SUPP_SVC_FAILED = 1201;
+    public static final int CODE_SUPP_SVC_CANCELLED = 1202;
+    public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203;
+
+    /**
+     * DPD Procedure received no response or send failed
+     */
+    public static final int CODE_IWLAN_DPD_FAILURE = 1300;
+
+    /**
+     * Establishment of the ePDG Tunnel Failed
+     */
+    public static final int CODE_EPDG_TUNNEL_ESTABLISH_FAILURE = 1400;
+
+    /**
+     * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+     */
+    public static final int CODE_EPDG_TUNNEL_REKEY_FAILURE = 1401;
+
+    /**
+     * Connection to the packet gateway is lost
+     */
+    public static final int CODE_EPDG_TUNNEL_LOST_CONNECTION = 1402;
+
+    /**
+     * The maximum number of calls allowed has been reached.  Used in a multi-endpoint scenario
+     * where the number of calls across all connected devices has reached the maximum.
+     */
+    public static final int CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED = 1403;
+
+    /**
+     * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+     * declined the call.  Used in a multi-endpoint scenario where a remote device declined an
+     * incoming call.
+     */
+    public static final int CODE_REMOTE_CALL_DECLINE = 1404;
+
+    /**
+     * Indicates the call was disconnected due to the user reaching their data limit.
+     */
+    public static final int CODE_DATA_LIMIT_REACHED = 1405;
+
+    /**
+     * Indicates the call was disconnected due to the user disabling cellular data.
+     */
+    public static final int CODE_DATA_DISABLED = 1406;
+
+    /**
+     * Indicates a call was disconnected due to loss of wifi signal.
+     */
+    public static final int CODE_WIFI_LOST = 1407;
+
+    /**
+     * Indicates the registration attempt on IWLAN failed due to IKEv2 authetication failure
+     * during tunnel establishment.
+     */
+    public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
+
+    /** The call cannot be established because RADIO is OFF */
+    public static final int CODE_RADIO_OFF = 1500;
+
+    /** The call cannot be established because of no valid SIM */
+    public static final int CODE_NO_VALID_SIM = 1501;
+
+    /** The failure is due internal error at modem */
+    public static final int CODE_RADIO_INTERNAL_ERROR = 1502;
+
+    /** The failure is due to UE timer expired while waiting for a response from network */
+    public static final int CODE_NETWORK_RESP_TIMEOUT = 1503;
+
+    /** The failure is due to explicit reject from network */
+    public static final int CODE_NETWORK_REJECT = 1504;
+
+    /** The failure is due to radio access failure. ex. RACH failure */
+    public static final int CODE_RADIO_ACCESS_FAILURE = 1505;
+
+    /** Call/IMS registration failed/dropped because of a RLF */
+    public static final int CODE_RADIO_LINK_FAILURE = 1506;
+
+    /** Call/IMS registration failed/dropped because of radio link lost */
+    public static final int CODE_RADIO_LINK_LOST = 1507;
+
+    /** The call Call/IMS registration failed because of a radio uplink issue */
+    public static final int CODE_RADIO_UPLINK_FAILURE = 1508;
+
+    /** Call failed because of a RRC connection setup failure */
+    public static final int CODE_RADIO_SETUP_FAILURE = 1509;
+
+    /** Call failed/dropped because of RRC connection release from NW */
+    public static final int CODE_RADIO_RELEASE_NORMAL = 1510;
+
+    /** Call failed/dropped because of RRC abnormally released by modem/network */
+    public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511;
+
+    /** Call failed because of access class barring */
+    public static final int CODE_ACCESS_CLASS_BLOCKED = 1512;
+
+    /** Call/IMS registration is failed/dropped because of a network detach */
+    public static final int CODE_NETWORK_DETACH = 1513;
+
+    /**
+     * Call failed due to SIP code 380 (Alternative Service response) while dialing an "undetected
+     * emergency number".  This scenario is important in some regions where the carrier network will
+     * identify other non-emergency help numbers (e.g. mountain rescue) when attempting to dial.
+     */
+    public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514;
+
+    /**
+     * Call failed because of unobtainable number
+     * @hide
+     */
+    public static final int CODE_UNOBTAINABLE_NUMBER = 1515;
+
+    /* OEM specific error codes. To be used by OEMs when they don't want to
+   reveal error code which would be replaced by ERROR_UNSPECIFIED */
+    public static final int CODE_OEM_CAUSE_1 = 0xf001;
+    public static final int CODE_OEM_CAUSE_2 = 0xf002;
+    public static final int CODE_OEM_CAUSE_3 = 0xf003;
+    public static final int CODE_OEM_CAUSE_4 = 0xf004;
+    public static final int CODE_OEM_CAUSE_5 = 0xf005;
+    public static final int CODE_OEM_CAUSE_6 = 0xf006;
+    public static final int CODE_OEM_CAUSE_7 = 0xf007;
+    public static final int CODE_OEM_CAUSE_8 = 0xf008;
+    public static final int CODE_OEM_CAUSE_9 = 0xf009;
+    public static final int CODE_OEM_CAUSE_10 = 0xf00a;
+    public static final int CODE_OEM_CAUSE_11 = 0xf00b;
+    public static final int CODE_OEM_CAUSE_12 = 0xf00c;
+    public static final int CODE_OEM_CAUSE_13 = 0xf00d;
+    public static final int CODE_OEM_CAUSE_14 = 0xf00e;
+    public static final int CODE_OEM_CAUSE_15 = 0xf00f;
+
+    /**
+     * Network string error messages.
+     * mExtraMessage may have these values.
+     */
+    public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED
+            = "Forbidden. Not Authorized for Service";
+
+
+    // For main reason code
+    /** @hide */
+    public int mCode;
+    // For the extra code value; it depends on the code value.
+    /** @hide */
+    public int mExtraCode;
+    // For the additional message of the reason info.
+    /** @hide */
+    public String mExtraMessage;
+
+    /** @hide */
+    public ImsReasonInfo() {
+        mCode = CODE_UNSPECIFIED;
+        mExtraCode = CODE_UNSPECIFIED;
+        mExtraMessage = null;
+    }
+
+    private ImsReasonInfo(Parcel in) {
+        mCode = in.readInt();
+        mExtraCode = in.readInt();
+        mExtraMessage = in.readString();
+    }
+
+    /** @hide */
+    public ImsReasonInfo(int code, int extraCode) {
+        mCode = code;
+        mExtraCode = extraCode;
+        mExtraMessage = null;
+    }
+
+    public ImsReasonInfo(int code, int extraCode, String extraMessage) {
+        mCode = code;
+        mExtraCode = extraCode;
+        mExtraMessage = extraMessage;
+    }
+
+    /**
+     *
+     */
+    public int getCode() {
+        return mCode;
+    }
+
+    /**
+     *
+     */
+    public int getExtraCode() {
+        return mExtraCode;
+    }
+
+    /**
+     *
+     */
+    public String getExtraMessage() {
+        return mExtraMessage;
+    }
+
+    /**
+     * Returns the string format of {@link ImsReasonInfo}
+     *
+     * @return the string format of {@link ImsReasonInfo}
+     */
+    public String toString() {
+        return "ImsReasonInfo :: {" + mCode + ", " + mExtraCode + ", " + mExtraMessage + "}";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mCode);
+        out.writeInt(mExtraCode);
+        out.writeString(mExtraMessage);
+    }
+
+    public static final Creator<ImsReasonInfo> CREATOR = new Creator<ImsReasonInfo>() {
+        @Override
+        public ImsReasonInfo createFromParcel(Parcel in) {
+            return new ImsReasonInfo(in);
+        }
+
+        @Override
+        public ImsReasonInfo[] newArray(int size) {
+            return new ImsReasonInfo[size];
+        }
+    };
+}
diff --git a/android/telephony/ims/ImsService.java b/android/telephony/ims/ImsService.java
index aaa0f08..c008711 100644
--- a/android/telephony/ims/ImsService.java
+++ b/android/telephony/ims/ImsService.java
@@ -16,25 +16,28 @@
 
 package android.telephony.ims;
 
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.IImsServiceControllerListener;
 import android.telephony.ims.feature.ImsFeature;
-import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsServiceController;
 import com.android.internal.annotations.VisibleForTesting;
 
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
@@ -48,9 +51,7 @@
  * ...
  * <service android:name=".EgImsService"
  *     android:permission="android.permission.BIND_IMS_SERVICE" >
- *     <!-- Apps must declare which features they support as metadata. The different categories are
- *     defined below. In this example, the RCS_FEATURE feature is supported. -->
- *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ *     ...
  *     <intent-filter>
  *         <action android:name="android.telephony.ims.ImsService" />
  *     </intent-filter>
@@ -64,13 +65,31 @@
  * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
  *
+ * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
+ * supports, dynamic or static definitions.
+ *
+ * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
+ * definition of the AndroidManifest.xml file as metadata:
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ *      defined below. In this example, the MMTEL_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
+ *
  * The features that are currently supported in an ImsService are:
  * - RCS_FEATURE: This ImsService implements the RcsFeature class.
- * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
- * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
- *   available to place emergency calls at all times. This MUST be implemented by the default
- *   ImsService provided in the device overlay.
- *   @hide
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
+ *   declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
+ *   circuit switch for emergency calling.
+ *
+ * In the dynamic definition, the supported features are not specified in the service definition
+ * of the AndroidManifest. Instead, the framework binds to this service and calls
+ * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
+ * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
+ * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
+ * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
+ * framework of the changes.
+ *
+ * @hide
  */
 @SystemApi
 public class ImsService extends Service {
@@ -89,20 +108,36 @@
     // call ImsFeature#onFeatureRemoved.
     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
 
+    private IImsServiceControllerListener mListener;
+
+
+    /**
+     * Listener that notifies the framework of ImsService changes.
+     * @hide
+     */
+    public static class Listener extends IImsServiceControllerListener.Stub {
+        /**
+         * The IMS features that this ImsService supports has changed.
+         * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+         *   that this ImsService supports. This may trigger the addition/removal of feature
+         *   in this service.
+         */
+        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+        }
+    }
+
     /**
      * @hide
      */
     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
-
         @Override
-        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
-                IImsFeatureStatusCallback c) {
-            return createEmergencyMMTelFeatureInternal(slotId, c);
+        public void setListener(IImsServiceControllerListener l) {
+            mListener = l;
         }
 
         @Override
-        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createMMTelFeatureInternal(slotId, c);
+        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMmTelFeatureInternal(slotId, c);
         }
 
         @Override
@@ -111,16 +146,41 @@
         }
 
         @Override
-        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
-                throws RemoteException {
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
             ImsService.this.removeImsFeature(slotId, featureType, c);
         }
 
         @Override
-        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
+            return ImsService.this.querySupportedImsFeatures();
+        }
+
+        @Override
+        public void notifyImsServiceReadyForFeatureCreation() {
+            ImsService.this.readyForFeatureCreation();
+        }
+
+        @Override
+        public IImsConfig getConfig(int slotId) {
+            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+            return c != null ? c.getIImsConfig() : null;
+        }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) {
             ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
             return r != null ? r.getBinder() : null;
         }
+
+        @Override
+        public void enableIms(int slotId) {
+            ImsService.this.enableIms(slotId);
+        }
+
+        @Override
+        public void disableIms(int slotId) {
+            ImsService.this.disableIms(slotId);
+        }
     };
 
     /**
@@ -143,47 +203,35 @@
         return mFeaturesBySlot.get(slotId);
     }
 
-    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        MmTelFeature f = createMmTelFeature(slotId);
         if (f != null) {
-            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
             return f.getBinder();
         } else {
-            return null;
-        }
-    }
-
-    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        MMTelFeature f = onCreateMMTelImsFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.MMTEL, c);
-            return f.getBinder();
-        } else {
+            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
             return null;
         }
     }
 
     private IImsRcsFeature createRcsFeatureInternal(int slotId,
             IImsFeatureStatusCallback c) {
-        RcsFeature f = onCreateRcsFeature(slotId);
+        RcsFeature f = createRcsFeature(slotId);
         if (f != null) {
-            setupFeature(f, slotId, ImsFeature.RCS, c);
+            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
             return f.getBinder();
         } else {
+            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
             return null;
         }
     }
 
     private void setupFeature(ImsFeature f, int slotId, int featureType,
             IImsFeatureStatusCallback c) {
-        f.setContext(this);
-        f.setSlotId(slotId);
         f.addImsFeatureStatusCallback(c);
+        f.initialize(this, slotId);
         addImsFeature(slotId, featureType, f);
-        // TODO: Remove once new onFeatureReady AIDL is merged in.
-        f.onFeatureReady();
     }
 
     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
@@ -222,37 +270,102 @@
     }
 
     /**
-     * @return An implementation of MMTelFeature that will be used by the system for MMTel
-     * functionality. Must be able to handle emergency calls at any time as well.
-     * @hide
+     * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
+     * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
+     * correspond to the {@link ImsFeature}s configured here.
+     *
+     * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
+     * {@link ImsFeature}s.
+     *
+     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
      */
-    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+    public ImsFeatureConfiguration querySupportedImsFeatures() {
+        // Return empty for base implementation
+        return new ImsFeatureConfiguration();
+    }
+
+    /**
+     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+     * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
+     * new ImsFeatures, depending on the configuration.
+     */
+    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+            throws RemoteException {
+        if (mListener == null) {
+            throw new IllegalStateException("Framework is not ready");
+        }
+        mListener.onUpdateSupportedImsFeatures(c);
+    }
+
+    /**
+     * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
+     * the ImsService has registered for with the framework, either in the manifest or via
+     * {@link #querySupportedImsFeatures()}.
+     *
+     * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+     * feature initialization because the framework may bind to this service multiple times to
+     * query the ImsService's {@link ImsFeatureConfiguration} via
+     * {@link #querySupportedImsFeatures()}before creating features.
+     */
+    public void readyForFeatureCreation() {
+    }
+
+    /**
+     * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
+     * and perform all appropriate initialization to bring up all ImsFeatures.
+     */
+    public void enableIms(int slotId) {
+    }
+
+    /**
+     * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
+     * and set capability status to false for all ImsFeatures.
+     */
+    public void disableIms(int slotId) {
+    }
+
+    /**
+     * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
+     * specified slot.
+     *
+     * @param slotId The slot ID that the MMTEL Feature is being created for.
+     * @return The newly created {@link MmTelFeature} associated with the slot or null if the
+     * feature is not supported.
+     */
+    public MmTelFeature createMmTelFeature(int slotId) {
         return null;
     }
 
     /**
-     * @return An implementation of MMTelFeature that will be used by the system for MMTel
-     * functionality.
-     * @hide
+     * When called, the framework is requesting that a new {@link RcsFeature} is created for the
+     * specified slot.
+     *
+     * @param slotId The slot ID that the RCS Feature is being created for.
+     * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
+     * is not supported.
      */
-    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+    public RcsFeature createRcsFeature(int slotId) {
         return null;
     }
 
     /**
-     * @return An implementation of RcsFeature that will be used by the system for RCS.
-     * @hide
+     * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
+     * will be used by the platform to get/set specific IMS related configurations.
+     *
+     * @param slotId The slot that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified slot.
      */
-    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
-        return null;
+    public ImsConfigImplBase getConfig(int slotId) {
+        return new ImsConfigImplBase();
     }
 
     /**
+     * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
+     *
      * @param slotId The slot that is associated with the IMS Registration.
      * @return the ImsRegistration implementation associated with the slot.
-     * @hide
      */
     public ImsRegistrationImplBase getRegistration(int slotId) {
         return new ImsRegistrationImplBase();
     }
-}
+}
\ No newline at end of file
diff --git a/android/telephony/ims/ImsSsData.java b/android/telephony/ims/ImsSsData.java
new file mode 100644
index 0000000..49ead77
--- /dev/null
+++ b/android/telephony/ims/ImsSsData.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides STK Call Control Supplementary Service information.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class ImsSsData implements Parcelable {
+
+    // Supplementary Service Type
+    // Call Forwarding
+    public static final int SS_CFU = 0;
+    public static final int SS_CF_BUSY = 1;
+    public static final int SS_CF_NO_REPLY = 2;
+    public static final int SS_CF_NOT_REACHABLE = 3;
+    public static final int SS_CF_ALL = 4;
+    public static final int SS_CF_ALL_CONDITIONAL = 5;
+    public static final int SS_CFUT = 6;
+    // Called Line Presentation
+    public static final int SS_CLIP = 7;
+    public static final int SS_CLIR = 8;
+    public static final int SS_COLP = 9;
+    public static final int SS_COLR = 10;
+    // Calling Name Presentation
+    public static final int SS_CNAP = 11;
+    // Call Waiting
+    public static final int SS_WAIT = 12;
+    // Call Barring
+    public static final int SS_BAOC = 13;
+    public static final int SS_BAOIC = 14;
+    public static final int SS_BAOIC_EXC_HOME = 15;
+    public static final int SS_BAIC = 16;
+    public static final int SS_BAIC_ROAMING = 17;
+    public static final int SS_ALL_BARRING = 18;
+    public static final int SS_OUTGOING_BARRING = 19;
+    public static final int SS_INCOMING_BARRING = 20;
+    public static final int SS_INCOMING_BARRING_DN = 21;
+    public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+    //Supplementary Service Request Types
+    public static final int SS_ACTIVATION = 0;
+    public static final int SS_DEACTIVATION = 1;
+    public static final int SS_INTERROGATION = 2;
+    public static final int SS_REGISTRATION = 3;
+    public static final int SS_ERASURE = 4;
+
+    // Supplementary Service Teleservice Type
+    public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
+    public static final int SS_ALL_TELESEVICES = 1;
+    public static final int SS_TELEPHONY = 2;
+    public static final int SS_ALL_DATA_TELESERVICES = 3;
+    public static final int SS_SMS_SERVICES = 4;
+    public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
+
+    // Service Class of Supplementary Service
+    // See 27.007 +CCFC or +CLCK
+    /** @hide */
+    public static final int SERVICE_CLASS_NONE = 0; // no user input
+    /** @hide */
+    public static final int SERVICE_CLASS_VOICE = 1;
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA = (1 << 1);
+    /** @hide */
+    public static final int SERVICE_CLASS_FAX = (1 << 2);
+    /** @hide */
+    public static final int SERVICE_CLASS_SMS = (1 << 3);
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA_SYNC = (1 << 4);
+    /** @hide */
+    public static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5);
+    /** @hide */
+    public static final int SERVICE_CLASS_PACKET = (1 << 6);
+    /** @hide */
+    public static final int SERVICE_CLASS_PAD = (1 << 7);
+
+    /**
+     * Result code used if the operation was successful. See {@link #result}.
+     * @hide
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SS_" }, value = {
+            SS_CFU,
+            SS_CF_BUSY,
+            SS_CF_NO_REPLY,
+            SS_CF_NOT_REACHABLE,
+            SS_CF_ALL,
+            SS_CF_ALL_CONDITIONAL,
+            SS_CFUT,
+            SS_CLIP,
+            SS_CLIR,
+            SS_COLP,
+            SS_COLR,
+            SS_CNAP,
+            SS_WAIT,
+            SS_BAOC,
+            SS_BAOIC,
+            SS_BAOIC_EXC_HOME,
+            SS_BAIC,
+            SS_BAIC_ROAMING,
+            SS_ALL_BARRING,
+            SS_OUTGOING_BARRING,
+            SS_INCOMING_BARRING,
+            SS_INCOMING_BARRING_DN,
+            SS_INCOMING_BARRING_ANONYMOUS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceType{}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SERVICE_CLASS" }, value = {
+            SERVICE_CLASS_NONE,
+            SERVICE_CLASS_VOICE,
+            SERVICE_CLASS_DATA,
+            SERVICE_CLASS_FAX,
+            SERVICE_CLASS_SMS,
+            SERVICE_CLASS_DATA_SYNC,
+            SERVICE_CLASS_DATA_ASYNC,
+            SERVICE_CLASS_PACKET,
+            SERVICE_CLASS_PAD
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ServiceClass{}
+
+    /**
+     * The Service type of this Supplementary service. Valid values include:
+     *     SS_CFU,
+     *     SS_CF_BUSY,
+     *     SS_CF_NO_REPLY,
+     *     SS_CF_NOT_REACHABLE,
+     *     SS_CF_ALL,
+     *     SS_CF_ALL_CONDITIONAL,
+     *     SS_CFUT,
+     *     SS_CLIP,
+     *     SS_CLIR,
+     *     SS_COLP,
+     *     SS_COLR,
+     *     SS_CNAP,
+     *     SS_WAIT,
+     *     SS_BAOC,
+     *     SS_BAOIC,
+     *     SS_BAOIC_EXC_HOME,
+     *     SS_BAIC,
+     *     SS_BAIC_ROAMING,
+     *     SS_ALL_BARRING,
+     *     SS_OUTGOING_BARRING,
+     *     SS_INCOMING_BARRING,
+     *     SS_INCOMING_BARRING_DN,
+     *     SS_INCOMING_BARRING_ANONYMOUS
+     *
+     * @hide
+     */
+    // TODO: Make final, do not modify this field directly!
+    public int serviceType;
+
+    /**
+     * Supplementary Service request Type. Valid values are:
+     *     SS_ACTIVATION,
+     *     SS_DEACTIVATION,
+     *     SS_INTERROGATION,
+     *     SS_REGISTRATION,
+     *     SS_ERASURE
+     *
+     * @hide
+     */
+    // TODO: Make final, do not modify this field directly!
+    public int requestType;
+
+    /**
+     * Supplementary Service teleservice type:
+     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
+     *     SS_TELESERVICE_ALL_TELESEVICES,
+     *     SS_TELESERVICE_TELEPHONY,
+     *     SS_TELESERVICE_ALL_DATA,
+     *     SS_TELESERVICE_SMS,
+     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public int teleserviceType;
+
+    /**
+     * Supplementary Service service class. Valid values are:
+     *     SERVICE_CLASS_NONE,
+     *     SERVICE_CLASS_VOICE,
+     *     SERVICE_CLASS_DATA,
+     *     SERVICE_CLASS_FAX,
+     *     SERVICE_CLASS_SMS,
+     *     SERVICE_CLASS_DATA_SYNC,
+     *     SERVICE_CLASS_DATA_ASYNC,
+     *     SERVICE_CLASS_PACKET,
+     *     SERVICE_CLASS_PAD
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public int serviceClass;
+
+    /**
+     * Result of Supplementary Service operation. Valid values are:
+     *     RESULT_SUCCESS if the result is success, or
+     *     ImsReasonInfo code if the result is a failure.
+     *
+     * @hide
+     */
+    // TODO: Make this param final! Do not try to modify this param directly.
+    public final int result;
+
+    private int[] mSsInfo;
+    private ImsCallForwardInfo[] mCfInfo;
+    private ImsSsInfo[] mImsSsInfo;
+
+    /**
+     * Generate IMS Supplementary Service information.
+     * @param serviceType The Supplementary Service type. Valid entries:
+     *     SS_CFU,
+     *     SS_CF_BUSY,
+     *     SS_CF_NO_REPLY,
+     *     SS_CF_NOT_REACHABLE,
+     *     SS_CF_ALL,
+     *     SS_CF_ALL_CONDITIONAL,
+     *     SS_CFUT,
+     *     SS_CLIP,
+     *     SS_CLIR,
+     *     SS_COLP,
+     *     SS_COLR,
+     *     SS_CNAP,
+     *     SS_WAIT,
+     *     SS_BAOC,
+     *     SS_BAOIC,
+     *     SS_BAOIC_EXC_HOME,
+     *     SS_BAIC,
+     *     SS_BAIC_ROAMING,
+     *     SS_ALL_BARRING,
+     *     SS_OUTGOING_BARRING,
+     *     SS_INCOMING_BARRING,
+     *     SS_INCOMING_BARRING_DN,
+     *     SS_INCOMING_BARRING_ANONYMOUS
+     * @param requestType Supplementary Service request Type. Valid values are:
+     *     SS_ACTIVATION,
+     *     SS_DEACTIVATION,
+     *     SS_INTERROGATION,
+     *     SS_REGISTRATION,
+     *     SS_ERASURE
+     * @param teleserviceType Supplementary Service teleservice type:
+     *     SS_TELESERVICE_ALL_TELE_AND_BEARER,
+     *     SS_TELESERVICE_ALL_TELESEVICES,
+     *     SS_TELESERVICE_TELEPHONY,
+     *     SS_TELESERVICE_ALL_DATA,
+     *     SS_TELESERVICE_SMS,
+     *     SS_TELESERVICE_ALL_TELESERVICES_EXCEPT_SMS
+     * @param serviceClass Supplementary Service service class. See See 27.007 +CCFC or +CLCK.
+     * @param result Result of Supplementary Service operation. Valid values are 0 if the result is
+     *               success, or ImsReasonInfo code if the result is a failure.
+     */
+    public ImsSsData(@ServiceType int serviceType, int requestType, int teleserviceType,
+            @ServiceClass int serviceClass, int result) {
+        this.serviceType = serviceType;
+        this.requestType = requestType;
+        this.teleserviceType = teleserviceType;
+        this.serviceClass = serviceClass;
+        this.result = result;
+    }
+
+    private ImsSsData(Parcel in) {
+        serviceType = in.readInt();
+        requestType = in.readInt();
+        teleserviceType = in.readInt();
+        serviceClass = in.readInt();
+        result = in.readInt();
+        mSsInfo = in.createIntArray();
+        mCfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
+    }
+
+    public static final Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
+        @Override
+        public ImsSsData createFromParcel(Parcel in) {
+            return new ImsSsData(in);
+        }
+
+        @Override
+        public ImsSsData[] newArray(int size) {
+            return new ImsSsData[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(serviceType);
+        out.writeInt(requestType);
+        out.writeInt(teleserviceType);
+        out.writeInt(serviceClass);
+        out.writeInt(result);
+        out.writeIntArray(mSsInfo);
+        out.writeParcelableArray(mCfInfo, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
+    public boolean isTypeCF() {
+        return (serviceType == SS_CFU || serviceType == SS_CF_BUSY ||
+              serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE ||
+              serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL);
+    }
+
+    public boolean isTypeCf() {
+        return isTypeCF();
+    }
+
+    public boolean isTypeUnConditional() {
+        return (serviceType == SS_CFU || serviceType == SS_CF_ALL);
+    }
+
+    /**
+     * Old method, kept for compatibility. See {@link #isTypeCf()}
+     * @hide
+     */
+    public boolean isTypeCW() {
+        return (serviceType == SS_WAIT);
+    }
+
+    public boolean isTypeCw() {
+        return isTypeCW();
+    }
+
+    public boolean isTypeClip() {
+        return (serviceType == SS_CLIP);
+    }
+
+    public boolean isTypeColr() {
+        return (serviceType == SS_COLR);
+    }
+
+    public boolean isTypeColp() {
+        return (serviceType == SS_COLP);
+    }
+
+    public boolean isTypeClir() {
+        return (serviceType == SS_CLIR);
+    }
+
+    public boolean isTypeIcb() {
+        return (serviceType == SS_INCOMING_BARRING_DN ||
+                serviceType == SS_INCOMING_BARRING_ANONYMOUS);
+    }
+
+    public boolean isTypeBarring() {
+        return (serviceType == SS_BAOC || serviceType == SS_BAOIC ||
+              serviceType == SS_BAOIC_EXC_HOME || serviceType == SS_BAIC ||
+              serviceType == SS_BAIC_ROAMING || serviceType == SS_ALL_BARRING ||
+              serviceType == SS_OUTGOING_BARRING || serviceType == SS_INCOMING_BARRING);
+    }
+
+    public boolean isTypeInterrogation() {
+        return (serviceType == SS_INTERROGATION);
+    }
+
+    /** @hide */
+    public void setSuppServiceInfo(int[] ssInfo) {
+        mSsInfo = ssInfo;
+    }
+
+    /** @hide */
+    public void setImsSpecificSuppServiceInfo(ImsSsInfo[] imsSsInfo) {
+        mImsSsInfo = imsSsInfo;
+    }
+
+    /** @hide */
+    public void setCallForwardingInfo(ImsCallForwardInfo[] cfInfo) {
+        mCfInfo = cfInfo;
+    }
+
+    /**
+     * This field will be null for RequestType SS_INTERROGATION
+     * and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
+     * SS_INCOMING_BARRING_ANONYMOUS.
+     *
+     * @hide
+     */
+    public int[] getSuppServiceInfo() {
+        return mSsInfo;
+    }
+
+    /**
+     * Valid only for ServiceTypes
+     *  - SS_INCOMING_BARRING_DN and
+     *  - ServiceType SS_INCOMING_BARRING_ANONYMOUS.
+     *  Will be null otherwise.
+     * @hide
+     */
+    public ImsSsInfo[] getImsSpecificSuppServiceInfo() {
+        return mImsSsInfo;
+    }
+
+    /**
+     * Valid only for supplementary services
+     * - ServiceType SS_CF_* and
+     * - RequestType SS_INTERROGATION.
+     * Will be null otherwise.
+     * @hide
+     **/
+    public ImsCallForwardInfo[] getCallForwardInfo() {
+        return mCfInfo;
+    }
+
+    public String toString() {
+        return "[ImsSsData] " + "ServiceType: " + serviceType
+            + " RequestType: " + requestType
+            + " TeleserviceType: " + teleserviceType
+            + " ServiceClass: " + serviceClass
+            + " Result: " + result;
+    }
+}
diff --git a/android/telephony/ims/ImsSsInfo.java b/android/telephony/ims/ImsSsInfo.java
new file mode 100644
index 0000000..c6f8622
--- /dev/null
+++ b/android/telephony/ims/ImsSsInfo.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides the result to the update operation for the supplementary service configuration.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsSsInfo implements Parcelable {
+    /**
+     * For the status of service registration or activation/deactivation.
+     */
+    public static final int NOT_REGISTERED = (-1);
+    public static final int DISABLED = 0;
+    public static final int ENABLED = 1;
+
+    // 0: disabled, 1: enabled
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter!
+    public int mStatus;
+    /** @hide */
+    // TODO: Make private, do not modify this field directly, use getter!
+    public String mIcbNum;
+
+    /**@hide*/
+    // TODO: Remove! Do not use this constructor, instead use public version.
+    public ImsSsInfo() {
+    }
+
+    /**
+     *
+     * @param status The status of the service registration of activation/deactiviation. Valid
+     *    entries include:
+     *    {@link #NOT_REGISTERED},
+     *    {@link #DISABLED},
+     *    {@link #ENABLED}
+     * @param icbNum The Incoming barring number.
+     */
+    public ImsSsInfo(int status, String icbNum) {
+        mStatus = status;
+        mIcbNum = icbNum;
+    }
+
+    private ImsSsInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mStatus);
+        out.writeString(mIcbNum);
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled");
+    }
+
+    private void readFromParcel(Parcel in) {
+        mStatus = in.readInt();
+        mIcbNum = in.readString();
+    }
+
+    public static final Creator<ImsSsInfo> CREATOR =
+            new Creator<ImsSsInfo>() {
+        @Override
+        public ImsSsInfo createFromParcel(Parcel in) {
+            return new ImsSsInfo(in);
+        }
+
+        @Override
+        public ImsSsInfo[] newArray(int size) {
+            return new ImsSsInfo[size];
+        }
+    };
+
+    /**
+     * @return Supplementary Service Configuration status. Valid Values are:
+     *     {@link #NOT_REGISTERED},
+     *     {@link #DISABLED},
+     *     {@link #ENABLED}
+     */
+    public int getStatus() {
+        return mStatus;
+    }
+
+    public String getIcbNum() {
+        return mIcbNum;
+    }
+}
diff --git a/android/telephony/ims/ImsStreamMediaProfile.java b/android/telephony/ims/ImsStreamMediaProfile.java
new file mode 100644
index 0000000..137106a
--- /dev/null
+++ b/android/telephony/ims/ImsStreamMediaProfile.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object to handle IMS stream media profile.
+ * It provides the media direction, quality of audio and/or video.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsStreamMediaProfile implements Parcelable {
+    private static final String TAG = "ImsStreamMediaProfile";
+
+    /**
+     * Media directions
+     */
+    public static final int DIRECTION_INVALID = (-1);
+    public static final int DIRECTION_INACTIVE = 0;
+    public static final int DIRECTION_RECEIVE = 1;
+    public static final int DIRECTION_SEND = 2;
+    public static final int DIRECTION_SEND_RECEIVE = 3;
+
+    /**
+     * Audio information
+     */
+    public static final int AUDIO_QUALITY_NONE = 0;
+    public static final int AUDIO_QUALITY_AMR = 1;
+    public static final int AUDIO_QUALITY_AMR_WB = 2;
+    public static final int AUDIO_QUALITY_QCELP13K = 3;
+    public static final int AUDIO_QUALITY_EVRC = 4;
+    public static final int AUDIO_QUALITY_EVRC_B = 5;
+    public static final int AUDIO_QUALITY_EVRC_WB = 6;
+    public static final int AUDIO_QUALITY_EVRC_NW = 7;
+    public static final int AUDIO_QUALITY_GSM_EFR = 8;
+    public static final int AUDIO_QUALITY_GSM_FR = 9;
+    public static final int AUDIO_QUALITY_GSM_HR = 10;
+    public static final int AUDIO_QUALITY_G711U = 11;
+    public static final int AUDIO_QUALITY_G723 = 12;
+    public static final int AUDIO_QUALITY_G711A = 13;
+    public static final int AUDIO_QUALITY_G722 = 14;
+    public static final int AUDIO_QUALITY_G711AB = 15;
+    public static final int AUDIO_QUALITY_G729 = 16;
+    public static final int AUDIO_QUALITY_EVS_NB = 17;
+    public static final int AUDIO_QUALITY_EVS_WB = 18;
+    public static final int AUDIO_QUALITY_EVS_SWB = 19;
+    public static final int AUDIO_QUALITY_EVS_FB = 20;
+
+   /**
+     * Video information
+     */
+    public static final int VIDEO_QUALITY_NONE = 0;
+    public static final int VIDEO_QUALITY_QCIF = (1 << 0);
+    public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = (1 << 1);
+    public static final int VIDEO_QUALITY_QVGA_PORTRAIT = (1 << 2);
+    public static final int VIDEO_QUALITY_VGA_LANDSCAPE = (1 << 3);
+    public static final int VIDEO_QUALITY_VGA_PORTRAIT = (1 << 4);
+
+    /**
+     * RTT Modes
+     */
+    public static final int RTT_MODE_DISABLED = 0;
+    public static final int RTT_MODE_FULL = 1;
+
+    // Audio related information
+    /** @hide */
+    public int mAudioQuality;
+    /** @hide */
+    public int mAudioDirection;
+    // Video related information
+    /** @hide */
+    public int mVideoQuality;
+    /** @hide */
+    public int mVideoDirection;
+    // Rtt related information
+    /** @hide */
+    public int mRttMode;
+
+    /** @hide */
+    public ImsStreamMediaProfile(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param audioQuality The audio quality. Can be one of the following:
+     *                     {@link #AUDIO_QUALITY_AMR},
+     *                     {@link #AUDIO_QUALITY_AMR_WB},
+     *                     {@link #AUDIO_QUALITY_QCELP13K},
+     *                     {@link #AUDIO_QUALITY_EVRC},
+     *                     {@link #AUDIO_QUALITY_EVRC_B},
+     *                     {@link #AUDIO_QUALITY_EVRC_WB},
+     *                     {@link #AUDIO_QUALITY_EVRC_NW},
+     *                     {@link #AUDIO_QUALITY_GSM_EFR},
+     *                     {@link #AUDIO_QUALITY_GSM_FR},
+     *                     {@link #AUDIO_QUALITY_GSM_HR},
+     *                     {@link #AUDIO_QUALITY_G711U},
+     *                     {@link #AUDIO_QUALITY_G723},
+     *                     {@link #AUDIO_QUALITY_G711A},
+     *                     {@link #AUDIO_QUALITY_G722},
+     *                     {@link #AUDIO_QUALITY_G711AB},
+     *                     {@link #AUDIO_QUALITY_G729},
+     *                     {@link #AUDIO_QUALITY_EVS_NB},
+     *                     {@link #AUDIO_QUALITY_EVS_WB},
+     *                     {@link #AUDIO_QUALITY_EVS_SWB},
+     *                     {@link #AUDIO_QUALITY_EVS_FB},
+     * @param audioDirection The audio direction. Can be one of the following:
+     *                       {@link #DIRECTION_INVALID},
+     *                       {@link #DIRECTION_INACTIVE},
+     *                       {@link #DIRECTION_RECEIVE},
+     *                       {@link #DIRECTION_SEND},
+     *                       {@link #DIRECTION_SEND_RECEIVE},
+     * @param videoQuality The video quality. Can be one of the following:
+     *                     {@link #VIDEO_QUALITY_NONE},
+     *                     {@link #VIDEO_QUALITY_QCIF},
+     *                     {@link #VIDEO_QUALITY_QVGA_LANDSCAPE},
+     *                     {@link #VIDEO_QUALITY_QVGA_PORTRAIT},
+     *                     {@link #VIDEO_QUALITY_VGA_LANDSCAPE},
+     *                     {@link #VIDEO_QUALITY_VGA_PORTRAIT},
+     * @param videoDirection The video direction. Can be one of the following:
+     *                       {@link #DIRECTION_INVALID},
+     *                       {@link #DIRECTION_INACTIVE},
+     *                       {@link #DIRECTION_RECEIVE},
+     *                       {@link #DIRECTION_SEND},
+     *                       {@link #DIRECTION_SEND_RECEIVE},
+     * @param rttMode The rtt mode. Can be one of the following:
+     *                {@link #RTT_MODE_DISABLED},
+     *                {@link #RTT_MODE_FULL}
+     */
+    public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+            int videoQuality, int videoDirection, int rttMode) {
+        mAudioQuality = audioQuality;
+        mAudioDirection = audioDirection;
+        mVideoQuality = videoQuality;
+        mVideoDirection = videoDirection;
+        mRttMode = rttMode;
+    }
+
+    /** @hide */
+    public ImsStreamMediaProfile() {
+        mAudioQuality = AUDIO_QUALITY_NONE;
+        mAudioDirection = DIRECTION_SEND_RECEIVE;
+        mVideoQuality = VIDEO_QUALITY_NONE;
+        mVideoDirection = DIRECTION_INVALID;
+        mRttMode = RTT_MODE_DISABLED;
+    }
+
+    /** @hide */
+    public ImsStreamMediaProfile(int audioQuality, int audioDirection,
+            int videoQuality, int videoDirection) {
+        mAudioQuality = audioQuality;
+        mAudioDirection = audioDirection;
+        mVideoQuality = videoQuality;
+        mVideoDirection = videoDirection;
+    }
+
+    /** @hide */
+    public ImsStreamMediaProfile(int rttMode) {
+        mRttMode = rttMode;
+    }
+
+    public void copyFrom(ImsStreamMediaProfile profile) {
+        mAudioQuality = profile.mAudioQuality;
+        mAudioDirection = profile.mAudioDirection;
+        mVideoQuality = profile.mVideoQuality;
+        mVideoDirection = profile.mVideoDirection;
+        mRttMode = profile.mRttMode;
+    }
+
+    @Override
+    public String toString() {
+        return "{ audioQuality=" + mAudioQuality +
+                ", audioDirection=" + mAudioDirection +
+                ", videoQuality=" + mVideoQuality +
+                ", videoDirection=" + mVideoDirection +
+                ", rttMode=" + mRttMode + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAudioQuality);
+        out.writeInt(mAudioDirection);
+        out.writeInt(mVideoQuality);
+        out.writeInt(mVideoDirection);
+        out.writeInt(mRttMode);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mAudioQuality = in.readInt();
+        mAudioDirection = in.readInt();
+        mVideoQuality = in.readInt();
+        mVideoDirection = in.readInt();
+        mRttMode = in.readInt();
+    }
+
+    public static final Creator<ImsStreamMediaProfile> CREATOR =
+            new Creator<ImsStreamMediaProfile>() {
+        @Override
+        public ImsStreamMediaProfile createFromParcel(Parcel in) {
+            return new ImsStreamMediaProfile(in);
+        }
+
+        @Override
+        public ImsStreamMediaProfile[] newArray(int size) {
+            return new ImsStreamMediaProfile[size];
+        }
+    };
+
+    /**
+     * Determines if it's RTT call
+     * @return true if RTT call, false otherwise.
+     */
+    public boolean isRttCall() {
+        return (mRttMode == RTT_MODE_FULL);
+    }
+
+    /**
+     * Updates the RttCall attribute
+     */
+    public void setRttMode(int rttMode) {
+        mRttMode = rttMode;
+    }
+
+    public int getAudioQuality() {
+        return mAudioQuality;
+    }
+
+    public int getAudioDirection() {
+        return mAudioDirection;
+    }
+
+    public int getVideoQuality() {
+        return mVideoQuality;
+    }
+
+    public int getVideoDirection() {
+        return mVideoDirection;
+    }
+
+    public int getRttMode() {
+        return mRttMode;
+    }
+}
diff --git a/android/telephony/ims/ImsSuppServiceNotification.java b/android/telephony/ims/ImsSuppServiceNotification.java
new file mode 100644
index 0000000..efaade8
--- /dev/null
+++ b/android/telephony/ims/ImsSuppServiceNotification.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+
+/**
+ * Parcelable object to handle IMS supplementary service notifications.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsSuppServiceNotification implements Parcelable {
+    private static final String TAG = "ImsSuppServiceNotification";
+
+    /** Type of notification: 0 = MO; 1 = MT */
+    public final int notificationType;
+    /** TS 27.007 7.17 "code1" or "code2" */
+    public final int code;
+    /** TS 27.007 7.17 "index" - Not used currently*/
+    public final int index;
+    /** TS 27.007 7.17 "type" (MT only) - Not used currently */
+    public final int type;
+    /** TS 27.007 7.17 "number" (MT only) */
+    public final String number;
+    /** List of forwarded numbers, if any */
+    public final String[] history;
+
+
+    public ImsSuppServiceNotification(int notificationType, int code, int index, int type,
+            String number, String[] history) {
+        this.notificationType = notificationType;
+        this.code = code;
+        this.index = index;
+        this.type = type;
+        this.number = number;
+        this.history = history;
+    }
+
+    /** @hide */
+    public ImsSuppServiceNotification(Parcel in) {
+        notificationType = in.readInt();
+        code = in.readInt();
+        index = in.readInt();
+        type = in.readInt();
+        number = in.readString();
+        history = in.createStringArray();
+    }
+
+    @Override
+    public String toString() {
+        return "{ notificationType=" + notificationType +
+                ", code=" + code +
+                ", index=" + index +
+                ", type=" + type +
+                ", number=" + number +
+                ", history=" + Arrays.toString(history) +
+                " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(notificationType);
+        out.writeInt(code);
+        out.writeInt(index);
+        out.writeInt(type);
+        out.writeString(number);
+        out.writeStringArray(history);
+    }
+
+    public static final Creator<ImsSuppServiceNotification> CREATOR =
+            new Creator<ImsSuppServiceNotification>() {
+        @Override
+        public ImsSuppServiceNotification createFromParcel(Parcel in) {
+            return new ImsSuppServiceNotification(in);
+        }
+
+        @Override
+        public ImsSuppServiceNotification[] newArray(int size) {
+            return new ImsSuppServiceNotification[size];
+        }
+    };
+}
diff --git a/android/telephony/ims/ImsUtListener.java b/android/telephony/ims/ImsUtListener.java
new file mode 100644
index 0000000..d50a0f7
--- /dev/null
+++ b/android/telephony/ims/ImsUtListener.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of the IMS UT listener interface, which implements stubs.
+ * Override these methods to implement functionality.
+ * @hide
+ */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtListener {
+    private IImsUtListener mServiceInterface;
+    private static final String LOG_TAG = "ImsUtListener";
+
+    public void onUtConfigurationUpdated(int id) {
+        try {
+            mServiceInterface.utConfigurationUpdated(null, id);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationUpdated: remote exception");
+        }
+    }
+
+    public void onUtConfigurationUpdateFailed(int id, ImsReasonInfo error) {
+        try {
+            mServiceInterface.utConfigurationUpdateFailed(null, id, error);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationUpdateFailed: remote exception");
+        }
+    }
+
+    public void onUtConfigurationQueried(int id, Bundle ssInfo) {
+        try {
+            mServiceInterface.utConfigurationQueried(null, id, ssInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationQueryFailed(int id, ImsReasonInfo error) {
+        try {
+            mServiceInterface.utConfigurationQueryFailed(null, id, error);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationQueryFailed: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallBarringQueried(int id, ImsSsInfo[] cbInfo) {
+        try {
+            mServiceInterface.utConfigurationCallBarringQueried(null, id, cbInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallBarringQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallForwardQueried(int id, ImsCallForwardInfo[] cfInfo) {
+        try {
+            mServiceInterface.utConfigurationCallForwardQueried(null, id, cfInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallForwardQueried: remote exception");
+        }
+    }
+
+    public void onUtConfigurationCallWaitingQueried(int id, ImsSsInfo[] cwInfo) {
+        try {
+            mServiceInterface.utConfigurationCallWaitingQueried(null, id, cwInfo);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "utConfigurationCallWaitingQueried: remote exception");
+        }
+    }
+
+    public void onSupplementaryServiceIndication(ImsSsData ssData) {
+        try {
+            mServiceInterface.onSupplementaryServiceIndication(ssData);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "onSupplementaryServiceIndication: remote exception");
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public ImsUtListener(IImsUtListener serviceInterface) {
+        mServiceInterface = serviceInterface;
+    }
+}
diff --git a/android/telephony/ims/ImsVideoCallProvider.java b/android/telephony/ims/ImsVideoCallProvider.java
new file mode 100644
index 0000000..b4f60b9
--- /dev/null
+++ b/android/telephony/ims/ImsVideoCallProvider.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+
+import com.android.ims.internal.IImsVideoCallCallback;
+import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.os.SomeArgs;
+
+/**
+ * @hide
+ */
+@SystemApi
+public abstract class ImsVideoCallProvider {
+    private static final int MSG_SET_CALLBACK = 1;
+    private static final int MSG_SET_CAMERA = 2;
+    private static final int MSG_SET_PREVIEW_SURFACE = 3;
+    private static final int MSG_SET_DISPLAY_SURFACE = 4;
+    private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+    private static final int MSG_SET_ZOOM = 6;
+    private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+    private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+    private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+    private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+    private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+    private final ImsVideoCallProviderBinder mBinder;
+
+    private IImsVideoCallCallback mCallback;
+
+    /**
+     * Default handler used to consolidate binder method calls onto a single thread.
+     */
+    private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_CALLBACK:
+                    mCallback = (IImsVideoCallCallback) msg.obj;
+                    break;
+                case MSG_SET_CAMERA:
+                {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        onSetCamera((String) args.arg1);
+                        onSetCamera((String) args.arg1, args.argi1);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SET_PREVIEW_SURFACE:
+                    onSetPreviewSurface((Surface) msg.obj);
+                    break;
+                case MSG_SET_DISPLAY_SURFACE:
+                    onSetDisplaySurface((Surface) msg.obj);
+                    break;
+                case MSG_SET_DEVICE_ORIENTATION:
+                    onSetDeviceOrientation(msg.arg1);
+                    break;
+                case MSG_SET_ZOOM:
+                    onSetZoom((Float) msg.obj);
+                    break;
+                case MSG_SEND_SESSION_MODIFY_REQUEST: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        VideoProfile fromProfile = (VideoProfile) args.arg1;
+                        VideoProfile toProfile = (VideoProfile) args.arg2;
+
+                        onSendSessionModifyRequest(fromProfile, toProfile);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SEND_SESSION_MODIFY_RESPONSE:
+                    onSendSessionModifyResponse((VideoProfile) msg.obj);
+                    break;
+                case MSG_REQUEST_CAMERA_CAPABILITIES:
+                    onRequestCameraCapabilities();
+                    break;
+                case MSG_REQUEST_CALL_DATA_USAGE:
+                    onRequestCallDataUsage();
+                    break;
+                case MSG_SET_PAUSE_IMAGE:
+                    onSetPauseImage((Uri) msg.obj);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    /**
+     * IImsVideoCallProvider stub implementation.
+     */
+    private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub {
+        public void setCallback(IImsVideoCallCallback callback) {
+            mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+        }
+
+        public void setCamera(String cameraId, int uid) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = cameraId;
+            args.argi1 = uid;
+            mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
+        }
+
+        public void setPreviewSurface(Surface surface) {
+            mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+        }
+
+        public void setDisplaySurface(Surface surface) {
+            mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+        }
+
+        public void setDeviceOrientation(int rotation) {
+            mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
+        }
+
+        public void setZoom(float value) {
+            mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+        }
+
+        public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = fromProfile;
+            args.arg2 = toProfile;
+            mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
+        }
+
+        public void sendSessionModifyResponse(VideoProfile responseProfile) {
+            mProviderHandler.obtainMessage(
+                    MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+        }
+
+        public void requestCameraCapabilities() {
+            mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+        }
+
+        public void requestCallDataUsage() {
+            mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+        }
+
+        public void setPauseImage(Uri uri) {
+            mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+        }
+    }
+
+    public ImsVideoCallProvider() {
+        mBinder = new ImsVideoCallProviderBinder();
+    }
+
+    /**
+     * Returns binder object which can be used across IPC methods.
+     * @hide
+     */
+    public final IImsVideoCallProvider getInterface() {
+        return mBinder;
+    }
+
+    /** @see Connection.VideoProvider#onSetCamera */
+    public abstract void onSetCamera(String cameraId);
+
+    /**
+     * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which
+     * the IMS service uses when opening the camera.  This ensures camera permissions are verified
+     * by the camera service.
+     *
+     * @param cameraId The id of the camera to be opened.
+     * @param uid The uid of the caller, used when opening the camera for permission verification.
+     * @see Connection.VideoProvider#onSetCamera
+     */
+    public void onSetCamera(String cameraId, int uid) {
+    }
+
+    /** @see Connection.VideoProvider#onSetPreviewSurface */
+    public abstract void onSetPreviewSurface(Surface surface);
+
+    /** @see Connection.VideoProvider#onSetDisplaySurface */
+    public abstract void onSetDisplaySurface(Surface surface);
+
+    /** @see Connection.VideoProvider#onSetDeviceOrientation */
+    public abstract void onSetDeviceOrientation(int rotation);
+
+    /** @see Connection.VideoProvider#onSetZoom */
+    public abstract void onSetZoom(float value);
+
+    /** @see Connection.VideoProvider#onSendSessionModifyRequest */
+    public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+            VideoProfile toProfile);
+
+    /** @see Connection.VideoProvider#onSendSessionModifyResponse */
+    public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+    /** @see Connection.VideoProvider#onRequestCameraCapabilities */
+    public abstract void onRequestCameraCapabilities();
+
+    /** @see Connection.VideoProvider#onRequestCallDataUsage */
+    public abstract void onRequestCallDataUsage();
+
+    /** @see Connection.VideoProvider#onSetPauseImage */
+    public abstract void onSetPauseImage(Uri uri);
+
+    /** @see Connection.VideoProvider#receiveSessionModifyRequest */
+    public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
+        if (mCallback != null) {
+            try {
+                mCallback.receiveSessionModifyRequest(VideoProfile);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#receiveSessionModifyResponse */
+    public void receiveSessionModifyResponse(
+            int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+        if (mCallback != null) {
+            try {
+                mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#handleCallSessionEvent */
+    public void handleCallSessionEvent(int event) {
+        if (mCallback != null) {
+            try {
+                mCallback.handleCallSessionEvent(event);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changePeerDimensions */
+    public void changePeerDimensions(int width, int height) {
+        if (mCallback != null) {
+            try {
+                mCallback.changePeerDimensions(width, height);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeCallDataUsage */
+    public void changeCallDataUsage(long dataUsage) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeCallDataUsage(dataUsage);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeCameraCapabilities */
+    public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeCameraCapabilities(CameraCapabilities);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeVideoQuality */
+    public void changeVideoQuality(int videoQuality) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeVideoQuality(videoQuality);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+}
diff --git a/android/telephony/ims/compat/ImsService.java b/android/telephony/ims/compat/ImsService.java
new file mode 100644
index 0000000..cf1efb3
--- /dev/null
+++ b/android/telephony/ims/compat/ImsService.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.compat.feature.ImsFeature;
+import android.telephony.ims.compat.feature.MMTelFeature;
+import android.telephony.ims.compat.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsRcsFeature;
+import com.android.ims.internal.IImsServiceController;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ *     android:permission="android.permission.BIND_IMS_SERVICE" >
+ *     <!-- Apps must declare which features they support as metadata. The different categories are
+ *     defined below. In this example, the RCS_FEATURE feature is supported. -->
+ *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ *     <intent-filter>
+ *         <action android:name="android.telephony.ims.compat.ImsService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ *    "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
+ *   available to place emergency calls at all times. This MUST be implemented by the default
+ *   ImsService provided in the device overlay.
+ *   @hide
+ */
+public class ImsService extends Service {
+
+    private static final String LOG_TAG = "ImsService(Compat)";
+
+    /**
+     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE = "android.telephony.ims.compat.ImsService";
+
+    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+    // slot.
+    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+    // call ImsFeature#onFeatureRemoved.
+    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+    /**
+     * @hide
+     */
+    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+        @Override
+        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
+                IImsFeatureStatusCallback c) {
+            return createEmergencyMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMMTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createRcsFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+                throws RemoteException {
+            ImsService.this.removeImsFeature(slotId, featureType, c);
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if(SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "ImsService(Compat) Bound.");
+            return mImsServiceController;
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public SparseArray<ImsFeature> getFeatures(int slotId) {
+        return mFeaturesBySlot.get(slotId);
+    }
+
+    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MMTelFeature f = onCreateMMTelImsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.MMTEL, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        RcsFeature f = onCreateRcsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.RCS, c);
+            return f.getBinder();
+        } else {
+            return null;
+        }
+    }
+
+    private void setupFeature(ImsFeature f, int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        f.setContext(this);
+        f.setSlotId(slotId);
+        f.addImsFeatureStatusCallback(c);
+        addImsFeature(slotId, featureType, f);
+        // TODO: Remove once new onFeatureReady AIDL is merged in.
+        f.onFeatureReady();
+    }
+
+    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                // Populate new SparseArray of features if it doesn't exist for this slot yet.
+                features = new SparseArray<>();
+                mFeaturesBySlot.put(slotId, features);
+            }
+            features.put(featureType, f);
+        }
+    }
+
+    private void removeImsFeature(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.removeImsFeatureStatusCallback(c);
+            f.onFeatureRemoved();
+            features.remove(featureType);
+        }
+    }
+
+    /**
+     * @return An implementation of MMTelFeature that will be used by the system for MMTel
+     * functionality. Must be able to handle emergency calls at any time as well.
+     * @hide
+     */
+    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @return An implementation of MMTelFeature that will be used by the system for MMTel
+     * functionality.
+     * @hide
+     */
+    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @return An implementation of RcsFeature that will be used by the system for RCS.
+     * @hide
+     */
+    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
+        return null;
+    }
+}
diff --git a/android/telephony/ims/compat/feature/ImsFeature.java b/android/telephony/ims/compat/feature/ImsFeature.java
new file mode 100644
index 0000000..0a12cae
--- /dev/null
+++ b/android/telephony/ims/compat/feature/ImsFeature.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.feature;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public abstract class ImsFeature {
+
+    private static final String LOG_TAG = "ImsFeature";
+
+    /**
+     * Action to broadcast when ImsService is up.
+     * Internal use only.
+     * Only defined here separately compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_UP =
+            "com.android.ims.IMS_SERVICE_UP";
+
+    /**
+     * Action to broadcast when ImsService is down.
+     * Internal use only.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_DOWN =
+            "com.android.ims.IMS_SERVICE_DOWN";
+
+    /**
+     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+     * A long value; the phone ID corresponding to the IMS service coming up or down.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     * @hide
+     */
+    public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+    // Invalid feature value
+    public static final int INVALID = -1;
+    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+    // defined values in ImsServiceClass for compatibility purposes.
+    public static final int EMERGENCY_MMTEL = 0;
+    public static final int MMTEL = 1;
+    public static final int RCS = 2;
+    // Total number of features defined
+    public static final int MAX = 3;
+
+    // Integer values defining the state of the ImsFeature at any time.
+    @IntDef(flag = true,
+            value = {
+                    STATE_NOT_AVAILABLE,
+                    STATE_INITIALIZING,
+                    STATE_READY,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsState {}
+    public static final int STATE_NOT_AVAILABLE = 0;
+    public static final int STATE_INITIALIZING = 1;
+    public static final int STATE_READY = 2;
+
+    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+    private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    protected Context mContext;
+
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    public void setSlotId(int slotId) {
+        mSlotId = slotId;
+    }
+
+    public int getFeatureState() {
+        return mState;
+    }
+
+    protected final void setFeatureState(@ImsState int state) {
+        if (mState != state) {
+            mState = state;
+            notifyFeatureState(state);
+        }
+    }
+
+    public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+        if (c == null) {
+            return;
+        }
+        try {
+            // If we have just connected, send queued status.
+            c.notifyImsFeatureStatus(mState);
+            // Add the callback if the callback completes successfully without a RemoteException.
+            synchronized (mStatusCallbacks) {
+                mStatusCallbacks.add(c);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+        }
+    }
+
+    public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+        if (c == null) {
+            return;
+        }
+        synchronized (mStatusCallbacks) {
+            mStatusCallbacks.remove(c);
+        }
+    }
+
+    /**
+     * Internal method called by ImsFeature when setFeatureState has changed.
+     * @param state
+     */
+    private void notifyFeatureState(@ImsState int state) {
+        synchronized (mStatusCallbacks) {
+            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+                 iter.hasNext(); ) {
+                IImsFeatureStatusCallback callback = iter.next();
+                try {
+                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+                    callback.notifyImsFeatureStatus(state);
+                } catch (RemoteException e) {
+                    // remove if the callback is no longer alive.
+                    iter.remove();
+                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+                }
+            }
+        }
+        sendImsServiceIntent(state);
+    }
+
+    /**
+     * Provide backwards compatibility using deprecated service UP/DOWN intents.
+     */
+    private void sendImsServiceIntent(@ImsState int state) {
+        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            return;
+        }
+        Intent intent;
+        switch (state) {
+            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_INITIALIZING:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+                break;
+            case ImsFeature.STATE_READY:
+                intent = new Intent(ACTION_IMS_SERVICE_UP);
+                break;
+            default:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+        }
+        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Called when the feature is ready to use.
+     */
+    public abstract void onFeatureReady();
+
+    /**
+     * Called when the feature is being removed and must be cleaned up.
+     */
+    public abstract void onFeatureRemoved();
+
+    /**
+     * @return Binder instance
+     */
+    public abstract IInterface getBinder();
+}
diff --git a/android/telephony/ims/compat/feature/MMTelFeature.java b/android/telephony/ims/compat/feature/MMTelFeature.java
new file mode 100644
index 0000000..d3d17f4
--- /dev/null
+++ b/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.feature;
+
+import android.app.PendingIntent;
+import android.os.Message;
+import android.os.RemoteException;
+
+import android.telephony.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.compat.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+
+/**
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
+ *
+ * @hide
+ */
+
+public class MMTelFeature extends ImsFeature {
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+
+    private final IImsMMTelFeature mImsMMTelBinder = new IImsMMTelFeature.Stub() {
+
+        @Override
+        public int startSession(PendingIntent incomingCallIntent,
+                IImsRegistrationListener listener) throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.startSession(incomingCallIntent, listener);
+            }
+        }
+
+        @Override
+        public void endSession(int sessionId) throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.endSession(sessionId);
+            }
+        }
+
+        @Override
+        public boolean isConnected(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.isConnected(callSessionType, callType);
+            }
+        }
+
+        @Override
+        public boolean isOpened() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.isOpened();
+            }
+        }
+
+        @Override
+        public int getFeatureStatus() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getFeatureState();
+            }
+        }
+
+        @Override
+        public void addRegistrationListener(IImsRegistrationListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.addRegistrationListener(listener);
+            }
+        }
+
+        @Override
+        public void removeRegistrationListener(IImsRegistrationListener listener)
+                throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.removeRegistrationListener(listener);
+            }
+        }
+
+        @Override
+        public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.createCallProfile(sessionId, callSessionType,  callType);
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.createCallSession(sessionId, profile, null);
+            }
+        }
+
+        @Override
+        public IImsCallSession getPendingCallSession(int sessionId, String callId)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getPendingCallSession(sessionId, callId);
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                ImsUtImplBase implBase = MMTelFeature.this.getUtInterface();
+                return implBase != null ? implBase.getInterface() : null;
+            }
+        }
+
+        @Override
+        public IImsConfig getConfigInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MMTelFeature.this.getConfigInterface();
+            }
+        }
+
+        @Override
+        public void turnOnIms() throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.turnOnIms();
+            }
+        }
+
+        @Override
+        public void turnOffIms() throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.turnOffIms();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                ImsEcbmImplBase implBase = MMTelFeature.this.getEcbmInterface();
+                return implBase != null ? implBase.getImsEcbm() : null;
+            }
+        }
+
+        @Override
+        public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
+            synchronized (mLock) {
+                MMTelFeature.this.setUiTTYMode(uiTtyMode, onComplete);
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                ImsMultiEndpointImplBase implBase = MMTelFeature.this.getMultiEndpointInterface();
+                return implBase != null ? implBase.getIImsMultiEndpoint() : null;
+            }
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMMTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+
+    /**
+     * Notifies the MMTel feature that you would like to start a session. This should always be
+     * done before making/receiving IMS calls. The IMS service will register the device to the
+     * operator's network with the credentials (from ISIM) periodically in order to receive calls
+     * from the operator's network. When the IMS service receives a new call, it will send out an
+     * intent with the provided action string. The intent contains a call ID extra
+     * {@link IImsCallSession#getCallId} and it can be used to take a call.
+     *
+     * @param incomingCallIntent When an incoming call is received, the IMS service will call
+     * {@link PendingIntent#send} to send back the intent to the caller with
+     * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+     * ID; It cannot be null.
+     * @param listener To listen to IMS registration events; It cannot be null
+     * @return an integer (greater than 0) representing the session id associated with the session
+     * that has been started.
+     */
+    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
+        return 0;
+    }
+
+    /**
+     * End a previously started session using the associated sessionId.
+     * @param sessionId an integer (greater than 0) representing the ongoing session. See
+     * {@link #startSession}.
+     */
+    public void endSession(int sessionId) {
+    }
+
+    /**
+     * Checks if the IMS service has successfully registered to the IMS network with the specified
+     * service & call type.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     * @return true if the specified service id is connected to the IMS network; false otherwise
+     */
+    public boolean isConnected(int callSessionType, int callType) {
+        return false;
+    }
+
+    /**
+     * Checks if the specified IMS service is opened.
+     *
+     * @return true if the specified service id is opened; false otherwise
+     */
+    public boolean isOpened() {
+        return false;
+    }
+
+    /**
+     * Add a new registration listener for the client associated with the session Id.
+     * @param listener An implementation of IImsRegistrationListener.
+     */
+    public void addRegistrationListener(IImsRegistrationListener listener) {
+    }
+
+    /**
+     * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+     * associated with the session Id.
+     * @param listener A previously registered IImsRegistrationListener
+     */
+    public void removeRegistrationListener(IImsRegistrationListener listener) {
+    }
+
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
+     * @return a {@link ImsCallProfile} object
+     */
+    public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
+        return null;
+    }
+
+    /**
+     * Creates an {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param profile a call profile to make the call
+     */
+    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+            IImsCallSessionListener listener) {
+        return null;
+    }
+
+    /**
+     * Retrieves the call session associated with a pending call.
+     *
+     * @param sessionId a session id which is obtained from {@link #startSession}
+     * @param callId a call id to make the call
+     */
+    public IImsCallSession getPendingCallSession(int sessionId, String callId) {
+        return null;
+    }
+
+    /**
+     * @return The Ut interface for the supplementary service configuration.
+     */
+    public ImsUtImplBase getUtInterface() {
+        return null;
+    }
+
+    /**
+     * @return The config interface for IMS Configuration
+     */
+    public IImsConfig getConfigInterface() {
+        return null;
+    }
+
+    /**
+     * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+     */
+    public void turnOnIms() {
+    }
+
+    /**
+     * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+     */
+    public void turnOffIms() {
+    }
+
+    /**
+     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+     */
+    public ImsEcbmImplBase getEcbmInterface() {
+        return null;
+    }
+
+    /**
+     * Sets the current UI TTY mode for the MMTelFeature.
+     * @param uiTtyMode An integer containing the new UI TTY Mode.
+     * @param onComplete A {@link Message} to be used when the mode has been set.
+     */
+    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
+    }
+
+    /**
+     * @return MultiEndpoint interface for DEP notifications
+     */
+    public ImsMultiEndpointImplBase getMultiEndpointInterface() {
+        return null;
+    }
+
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void onFeatureRemoved() {
+
+    }
+}
diff --git a/android/telephony/ims/compat/feature/RcsFeature.java b/android/telephony/ims/compat/feature/RcsFeature.java
new file mode 100644
index 0000000..228b330
--- /dev/null
+++ b/android/telephony/ims/compat/feature/RcsFeature.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.feature;
+
+
+import com.android.ims.internal.IImsRcsFeature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature {
+
+    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
+        // Empty Default Implementation.
+    };
+
+
+    public RcsFeature() {
+        super();
+    }
+
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    @Override
+    public void onFeatureRemoved() {
+
+    }
+
+    @Override
+    public final IImsRcsFeature getBinder() {
+        return mImsRcsBinder;
+    }
+}
diff --git a/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..e5ed825
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsConferenceState;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsSuppServiceNotification;
+import android.telephony.ims.aidl.IImsCallSessionListener;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+import android.telephony.ims.ImsCallSession;
+
+/**
+ * Compat implementation of ImsCallSessionImplBase for older implementations.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+    @Override
+    // convert to old implementation of listener
+    public final void setListener(IImsCallSessionListener listener)
+            throws RemoteException {
+        setListener(new ImsCallSessionListenerConverter(listener));
+    }
+
+    /**
+     * Sets the listener to listen to the session events. An {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(com.android.ims.internal.IImsCallSessionListener listener) {
+
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
+     */
+    @Override
+    public void close() {
+
+    }
+
+    /**
+     * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+     */
+    @Override
+    public String getCallId() {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+     * with.
+     */
+    @Override
+    public ImsCallProfile getCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    @Override
+    public ImsCallProfile getLocalCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    @Override
+    public ImsCallProfile getRemoteCallProfile() {
+        return null;
+    }
+
+    /**
+     * @param name The String extra key.
+     * @return The string extra value associated with the specified property.
+     */
+    @Override
+    public String getProperty(String name) {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallSessionImplBase} state.
+     */
+    @Override
+    public int getState() {
+        return -1;
+    }
+
+    /**
+     * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+     */
+    @Override
+    public boolean isInCall() {
+        return false;
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call should be muted, false otherwise.
+     */
+    @Override
+    public void setMute(boolean muted) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified number and call profile.
+     * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+     * defined session events.
+     * Only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void start(String callee, ImsCallProfile profile) {
+    }
+
+    /**
+     * Initiates an IMS call with the specified participants and call profile.
+     * The session listener set in {@link #setListener(IImsCallSessionListener)} is called back upon
+     * defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void startConference(String[] participants, ImsCallProfile profile) {
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see {@link ImsCallSession.Listener#callSessionStarted}
+     */
+    @Override
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Deflects an incoming call.
+     *
+     * @param deflectNumber number to deflect the call
+     */
+    @Override
+    public void deflect(String deflectNumber) {
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void reject(int reason) {
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionTerminated}
+     */
+    @Override
+    public void terminate(int reason) {
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+     * called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see {@link ImsCallSession.Listener#callSessionHeld},
+     * {@link ImsCallSession.Listener#callSessionHoldFailed}
+     */
+    @Override
+    public void hold(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link ImsCallSession.Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+     * @see {@link ImsCallSession.Listener#callSessionResumed},
+     * {@link ImsCallSession.Listener#callSessionResumeFailed}
+     */
+    @Override
+    public void resume(ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Merges the active and held call. When the merge starts,
+     * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+     * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+     * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+     * fails.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+     * {@link ImsCallSession.Listener#callSessionMergeComplete},
+     *      {@link ImsCallSession.Listener#callSessionMergeFailed}
+     */
+    @Override
+    public void merge() {
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see {@link ImsCallSession.Listener#callSessionUpdated},
+     * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+     */
+    @Override
+    public void update(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants participant list to be invited to the conference call after extending the
+     * call
+     * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+     * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+     */
+    @Override
+    public void extendToConference(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants participant list to be invited to the conference call
+     * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+     */
+    @Override
+    public void inviteParticipants(String[] participants) {
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+     */
+    @Override
+    public void removeParticipants(String[] participants) {
+    }
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void sendDtmf(char c, Message result) {
+    }
+
+    /**
+     * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void startDtmf(char c) {
+    }
+
+    /**
+     * Stop a DTMF code.
+     */
+    @Override
+    public void stopDtmf() {
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    @Override
+    public void sendUssd(String ussdMessage) {
+    }
+
+    @Override
+    public IImsVideoCallProvider getVideoCallProvider() {
+        return null;
+    }
+
+    /**
+     * Determines if the current session is multiparty.
+     * @return {@code True} if the session is multiparty.
+     */
+    @Override
+    public boolean isMultiparty() {
+        return false;
+    }
+
+    /**
+     * Device issues RTT modify request
+     * @param toProfile The profile with requested changes made
+     */
+    @Override
+    public void sendRttModifyRequest(ImsCallProfile toProfile) {
+    }
+
+    /**
+     * Device responds to Remote RTT modify request
+     * @param status true if the the request was accepted or false of the request is defined.
+     */
+    @Override
+    public void sendRttModifyResponse(boolean status) {
+    }
+
+    /**
+     * Device sends RTT message
+     * @param rttMessage RTT message to be sent
+     */
+    @Override
+    public void sendRttMessage(String rttMessage) {
+    }
+
+    /**
+     * There are two different ImsCallSessionListeners that need to reconciled here, we need to
+     * convert the "old" version of the com.android.ims.internal.IImsCallSessionListener to the
+     * "new" version of the Listener android.telephony.ims.ImsCallSessionListener when calling
+     * back to the framework.
+     */
+    private class ImsCallSessionListenerConverter
+            extends com.android.ims.internal.IImsCallSessionListener.Stub {
+
+        private final IImsCallSessionListener mNewListener;
+
+        public ImsCallSessionListenerConverter(IImsCallSessionListener listener) {
+            mNewListener = listener;
+        }
+
+        @Override
+        public void callSessionProgressing(IImsCallSession i,
+                ImsStreamMediaProfile imsStreamMediaProfile) throws RemoteException {
+            mNewListener.callSessionProgressing(imsStreamMediaProfile);
+        }
+
+        @Override
+        public void callSessionStarted(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionInitiated(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionStartFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionInitiatedFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionTerminated(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionTerminated(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionHeld(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionHeld(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionHoldFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionHoldFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionHoldReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionHoldReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionResumed(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionResumed(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionResumeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionResumeFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionResumeReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionResumeReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionMergeStarted(IImsCallSession i, IImsCallSession newSession,
+                ImsCallProfile profile)
+                throws RemoteException {
+            mNewListener.callSessionMergeStarted(newSession, profile);
+        }
+
+        @Override
+        public void callSessionMergeComplete(IImsCallSession iImsCallSession)
+                throws RemoteException {
+            mNewListener.callSessionMergeComplete(iImsCallSession);
+        }
+
+        @Override
+        public void callSessionMergeFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionMergeFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionUpdated(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionUpdated(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionUpdateFailed(IImsCallSession i, ImsReasonInfo imsReasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionUpdateFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionUpdateReceived(IImsCallSession i, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionUpdateReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession i, IImsCallSession newSession,
+                ImsCallProfile imsCallProfile) throws RemoteException {
+            mNewListener.callSessionConferenceExtended(newSession, imsCallProfile);
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionConferenceExtendFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession i,
+                IImsCallSession newSession, ImsCallProfile imsCallProfile)
+                throws RemoteException {
+            mNewListener.callSessionConferenceExtendReceived(newSession, imsCallProfile);
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered(IImsCallSession i)
+                throws RemoteException {
+            mNewListener.callSessionInviteParticipantsRequestDelivered();
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionInviteParticipantsRequestFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession i)
+                throws RemoteException {
+            mNewListener.callSessionRemoveParticipantsRequestDelivered();
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(IImsCallSession i,
+                ImsReasonInfo imsReasonInfo) throws RemoteException {
+            mNewListener.callSessionRemoveParticipantsRequestFailed(imsReasonInfo);
+        }
+
+        @Override
+        public void callSessionConferenceStateUpdated(IImsCallSession i,
+                ImsConferenceState imsConferenceState) throws RemoteException {
+            mNewListener.callSessionConferenceStateUpdated(imsConferenceState);
+        }
+
+        @Override
+        public void callSessionUssdMessageReceived(IImsCallSession i, int mode, String message)
+                throws RemoteException {
+            mNewListener.callSessionUssdMessageReceived(mode, message);
+        }
+
+        @Override
+        public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
+                ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+        }
+
+        @Override
+        public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
+                int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
+            mNewListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+        }
+
+        @Override
+        public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
+                throws RemoteException {
+            mNewListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+        }
+
+        @Override
+        public void callSessionTtyModeReceived(IImsCallSession iImsCallSession, int mode)
+                throws RemoteException {
+            mNewListener.callSessionTtyModeReceived(mode);
+        }
+
+        @Override
+        public void callSessionMultipartyStateChanged(IImsCallSession i, boolean isMultiparty)
+                throws RemoteException {
+            mNewListener.callSessionMultipartyStateChanged(isMultiparty);
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(IImsCallSession i,
+                ImsSuppServiceNotification imsSuppServiceNotification) throws RemoteException {
+            mNewListener.callSessionSuppServiceReceived(imsSuppServiceNotification);
+        }
+
+        @Override
+        public void callSessionRttModifyRequestReceived(IImsCallSession i,
+                ImsCallProfile imsCallProfile) throws RemoteException {
+            mNewListener.callSessionRttModifyRequestReceived(imsCallProfile);
+        }
+
+        @Override
+        public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+            mNewListener.callSessionRttModifyResponseReceived(status);
+        }
+
+        @Override
+        public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+            mNewListener.callSessionRttMessageReceived(rttMessage);
+        }
+    }
+}
diff --git a/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/android/telephony/ims/compat/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..2c325ba
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.stub;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
+
+/**
+ * Base implementation of ImsConfig.
+ * Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+    static final private String TAG = "ImsConfigImplBase";
+
+    ImsConfigStub mImsConfigStub;
+
+    public ImsConfigImplBase(Context context) {
+        mImsConfigStub = new ImsConfigStub(this, context);
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in Integer format.
+     */
+    public int getProvisionedValue(int item) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in String format.
+     */
+    public String getProvisionedStringValue(int item) throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setProvisionedValue(int item, int value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived.  Synchronous blocking call.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setProvisionedStringValue(int item, String value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Gets the value of the specified IMS feature item for specified network type.
+     * This operation gets the feature config value from the master storage (i.e. final
+     * value). Asynchronous non-blocking call.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param listener feature value returned asynchronously through listener.
+     */
+    public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item for specified network type.
+     * This operation stores the user setting in setting db from which master db
+     * is derived.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Gets the value for IMS VoLTE provisioned.
+     * This should be the same as the operator provisioned value if applies.
+     */
+    public boolean getVolteProvisioned() throws RemoteException {
+        return false;
+    }
+
+    /**
+     * Gets the value for IMS feature item video quality.
+     *
+     * @param listener Video quality value returned asynchronously through listener.
+     */
+    public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item video quality.
+     *
+     * @param quality, defines the value of video quality.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+    }
+
+    public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call #setProvisionedValue and assumes the result succeeded.
+     * This should only be used by modem when they implicitly changed provisioned values.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     */
+    public final void notifyProvisionedValueChanged(int item, int value) {
+        mImsConfigStub.updateCachedValue(item, value, true);
+    }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call #setProvisionedValue and assumes the result succeeded.
+     * This should only be used by modem when they implicitly changed provisioned values.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     */
+    public final void notifyProvisionedValueChanged(int item, String value) {
+        mImsConfigStub.updateCachedValue(item, value, true);
+    }
+
+    /**
+     * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+     * in order to get/set configuration parameters.
+     *
+     * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+     * with actual implementations from vendors. This class caches provisioned values from
+     * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+     * it first checks cache layer. If missed, it will call the vendor implementation of
+     * ImsConfigImplBase API.
+     * and cache the return value if the set succeeds.
+     *
+     * Provides APIs to get/set the IMS service feature/capability/parameters.
+     * The config items include:
+     * 1) Items provisioned by the operator.
+     * 2) Items configured by user. Mainly service feature class.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    static public class ImsConfigStub extends IImsConfig.Stub {
+        Context mContext;
+        WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+        private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+        private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+        @VisibleForTesting
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
+            mContext = context;
+            mImsConfigImplBaseWeakReference =
+                    new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @return value in Integer format.
+         */
+        @Override
+        public synchronized int getProvisionedValue(int item) throws RemoteException {
+            if (mProvisionedIntValue.containsKey(item)) {
+                return mProvisionedIntValue.get(item);
+            } else {
+                int retVal = getImsConfigImpl().getProvisionedValue(item);
+                if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+                    updateCachedValue(item, retVal, false);
+                }
+                return retVal;
+            }
+        }
+
+        /**
+         * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+         * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @return value in String format.
+         */
+        @Override
+        public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+            if (mProvisionedIntValue.containsKey(item)) {
+                return mProvisionedStringValue.get(item);
+            } else {
+                String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+                if (retVal != null) {
+                    updateCachedValue(item, retVal, false);
+                }
+                return retVal;
+            }
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the master value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in Integer format.
+         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         */
+        @Override
+        public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+            mProvisionedIntValue.remove(item);
+            int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+            if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                updateCachedValue(item, value, true);
+            } else {
+                Log.d(TAG, "Set provision value of " + item +
+                        " to " + value + " failed with error code " + retVal);
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Sets the value for IMS service/capabilities parameters by the operator device
+         * management entity. It sets the config item value in the provisioned storage
+         * from which the master value is derived, and write it into local cache.
+         * Synchronous blocking call.
+         *
+         * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param value in String format.
+         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         */
+        @Override
+        public synchronized int setProvisionedStringValue(int item, String value)
+                throws RemoteException {
+            mProvisionedStringValue.remove(item);
+            int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+            if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+                updateCachedValue(item, value, true);
+            }
+
+            return retVal;
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getFeatureValue.
+         */
+        @Override
+        public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().getFeatureValue(feature, network, listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.setFeatureValue.
+         */
+        @Override
+        public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().setFeatureValue(feature, network, value, listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
+         */
+        @Override
+        public boolean getVolteProvisioned() throws RemoteException {
+            return getImsConfigImpl().getVolteProvisioned();
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.getVideoQuality.
+         */
+        @Override
+        public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+            getImsConfigImpl().getVideoQuality(listener);
+        }
+
+        /**
+         * Wrapper function to call ImsConfigImplBase.setVideoQuality.
+         */
+        @Override
+        public void setVideoQuality(int quality, ImsConfigListener listener)
+                throws RemoteException {
+            getImsConfigImpl().setVideoQuality(quality, listener);
+        }
+
+        private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+            ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+            if (ref == null) {
+                throw new RemoteException("Fail to get ImsConfigImpl");
+            } else {
+                return ref;
+            }
+        }
+
+        private void sendImsConfigChangedIntent(int item, int value) {
+            sendImsConfigChangedIntent(item, Integer.toString(value));
+        }
+
+        private void sendImsConfigChangedIntent(int item, String value) {
+            Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+            configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+            if (mContext != null) {
+                mContext.sendBroadcast(configChangedIntent);
+            }
+        }
+
+        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+            mProvisionedIntValue.put(item, value);
+            if (notifyChange) {
+                sendImsConfigChangedIntent(item, value);
+            }
+        }
+
+        protected synchronized void updateCachedValue(
+                int item, String value, boolean notifyChange) {
+            mProvisionedStringValue.put(item, value);
+            if (notifyChange) {
+                sendImsConfigChangedIntent(item, value);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java b/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
new file mode 100644
index 0000000..b2aa080
--- /dev/null
+++ b/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.compat.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
+import android.telephony.ims.ImsSsInfo;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+    /**
+     * Notifies the result of the supplementary service configuration udpate.
+     */
+    @Override
+    public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+    }
+
+    @Override
+    public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the result of the supplementary service configuration query.
+     */
+    @Override
+    public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+    }
+
+    @Override
+    public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call barring supplementary service.
+     */
+    @Override
+    public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call forwarding supplementary service.
+     */
+    @Override
+    public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call waiting supplementary service.
+     */
+    @Override
+    public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies client when Supplementary Service indication is received
+     */
+    @Override
+    public void onSupplementaryServiceIndication(ImsSsData ssData) {}
+}
diff --git a/android/telephony/ims/feature/CapabilityChangeRequest.java b/android/telephony/ims/feature/CapabilityChangeRequest.java
new file mode 100644
index 0000000..7c793a5
--- /dev/null
+++ b/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Request to send to IMS provider, which will try to enable/disable capabilities that are added to
+ * the request.
+ * {@hide}
+ */
+@SystemApi
+public final class CapabilityChangeRequest implements Parcelable {
+
+    /**
+     * Contains a feature capability, defined as
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+     * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
+     * along with an associated technology, defined as
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     */
+    public static class CapabilityPair {
+        private final int mCapability;
+        private final int radioTech;
+
+        public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+                @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+            this.mCapability = capability;
+            this.radioTech = radioTech;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CapabilityPair)) return false;
+
+            CapabilityPair that = (CapabilityPair) o;
+
+            if (getCapability() != that.getCapability()) return false;
+            return getRadioTech() == that.getRadioTech();
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            int result = getCapability();
+            result = 31 * result + getRadioTech();
+            return result;
+        }
+
+        /**
+         * @return The stored capability, defined as
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+         */
+        public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+            return mCapability;
+        }
+
+        /**
+         * @return the stored radio technology, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+         */
+        public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
+            return radioTech;
+        }
+    }
+
+    // Pair contains <radio tech, mCapability>
+    private final Set<CapabilityPair> mCapabilitiesToEnable;
+    // Pair contains <radio tech, mCapability>
+    private final Set<CapabilityPair> mCapabilitiesToDisable;
+
+    /** @hide */
+    public CapabilityChangeRequest() {
+        mCapabilitiesToEnable = new ArraySet<>();
+        mCapabilitiesToDisable = new ArraySet<>();
+    }
+
+    /**
+     * Add one or many capabilities to the request to be enabled.
+     *
+     * @param capabilities A bitfield of capabilities to enable, valid values are defined in
+     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     * @param radioTech  the radio tech that these capabilities should be enabled for, valid
+     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+     */
+    public void addCapabilitiesToEnableForTech(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
+    }
+
+    /**
+     * Add one or many capabilities to the request to be disabled.
+     * @param capabilities A bitfield of capabilities to diable, valid values are defined in
+     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     * @param radioTech the radio tech that these capabilities should be disabled for, valid
+     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+     */
+    public void addCapabilitiesToDisableForTech(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
+    }
+
+    /**
+     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled.
+     */
+    public List<CapabilityPair> getCapabilitiesToEnable() {
+        return new ArrayList<>(mCapabilitiesToEnable);
+    }
+
+    /**
+     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled.
+     */
+    public List<CapabilityPair> getCapabilitiesToDisable() {
+        return new ArrayList<>(mCapabilitiesToDisable);
+    }
+
+    // Iterate through capabilities bitfield and add each one as a pair associated with the radio
+    // technology
+    private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) {
+        long highestCapability = Long.highestOneBit(capabilities);
+        for (int i = 1; i <= highestCapability; i *= 2) {
+            if ((i & capabilities) > 0) {
+                set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech));
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    protected CapabilityChangeRequest(Parcel in) {
+        int enableSize = in.readInt();
+        mCapabilitiesToEnable = new ArraySet<>(enableSize);
+        for (int i = 0; i < enableSize; i++) {
+            mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(),
+                    /*radioTech*/ in.readInt()));
+        }
+        int disableSize = in.readInt();
+        mCapabilitiesToDisable = new ArraySet<>(disableSize);
+        for (int i = 0; i < disableSize; i++) {
+            mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(),
+                    /*radioTech*/ in.readInt()));
+        }
+    }
+
+    public static final Creator<CapabilityChangeRequest> CREATOR =
+            new Creator<CapabilityChangeRequest>() {
+                @Override
+                public CapabilityChangeRequest createFromParcel(Parcel in) {
+                    return new CapabilityChangeRequest(in);
+                }
+
+                @Override
+                public CapabilityChangeRequest[] newArray(int size) {
+                    return new CapabilityChangeRequest[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCapabilitiesToEnable.size());
+        for (CapabilityPair pair : mCapabilitiesToEnable) {
+            dest.writeInt(pair.getCapability());
+            dest.writeInt(pair.getRadioTech());
+        }
+        dest.writeInt(mCapabilitiesToDisable.size());
+        for (CapabilityPair pair : mCapabilitiesToDisable) {
+            dest.writeInt(pair.getCapability());
+            dest.writeInt(pair.getRadioTech());
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CapabilityChangeRequest)) return false;
+
+        CapabilityChangeRequest
+                that = (CapabilityChangeRequest) o;
+
+        if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
+        return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int hashCode() {
+        int result = mCapabilitiesToEnable.hashCode();
+        result = 31 * result + mCapabilitiesToDisable.hashCode();
+        return result;
+    }
+}
diff --git a/android/telephony/ims/feature/ImsFeature.java b/android/telephony/ims/feature/ImsFeature.java
index d47cea3..d537699 100644
--- a/android/telephony/ims/feature/ImsFeature.java
+++ b/android/telephony/ims/feature/ImsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,28 +17,35 @@
 package android.telephony.ims.feature;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.IInterface;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Set;
 import java.util.WeakHashMap;
 
 /**
- * Base class for all IMS features that are supported by the framework.
+ * Base class for all IMS features that are supported by the framework. Use a concrete subclass
+ * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
+ *
  * @hide
  */
+@SystemApi
 public abstract class ImsFeature {
 
     private static final String LOG_TAG = "ImsFeature";
@@ -46,7 +53,8 @@
     /**
      * Action to broadcast when ImsService is up.
      * Internal use only.
-     * Only defined here separately compatibility purposes with the old ImsService.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_UP =
@@ -56,6 +64,7 @@
      * Action to broadcast when ImsService is down.
      * Internal use only.
      * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String ACTION_IMS_SERVICE_DOWN =
@@ -65,67 +74,331 @@
      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
      * A long value; the phone ID corresponding to the IMS service coming up or down.
      * Only defined here separately for compatibility purposes with the old ImsService.
+     *
      * @hide
      */
     public static final String EXTRA_PHONE_ID = "android:phone_id";
 
-    // Invalid feature value
-    public static final int INVALID = -1;
+    /**
+     * Invalid feature value
+     * @hide
+     */
+    public static final int FEATURE_INVALID = -1;
     // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
     // defined values in ImsServiceClass for compatibility purposes.
-    public static final int EMERGENCY_MMTEL = 0;
-    public static final int MMTEL = 1;
-    public static final int RCS = 2;
-    // Total number of features defined
-    public static final int MAX = 3;
+    /**
+     * This feature supports emergency calling over MMTEL. If defined, the framework will try to
+     * place an emergency call over IMS first. If it is not defined, the framework will only use
+     * CSFB for emergency calling.
+     */
+    public static final int FEATURE_EMERGENCY_MMTEL = 0;
+    /**
+     * This feature supports the MMTEL feature.
+     */
+    public static final int FEATURE_MMTEL = 1;
+    /**
+     * This feature supports the RCS feature.
+     */
+    public static final int FEATURE_RCS = 2;
+    /**
+     * Total number of features defined
+     * @hide
+     */
+    public static final int FEATURE_MAX = 3;
 
-    // Integer values defining the state of the ImsFeature at any time.
+    /**
+     * Integer values defining IMS features that are supported in ImsFeature.
+     * @hide
+     */
     @IntDef(flag = true,
             value = {
-                    STATE_NOT_AVAILABLE,
+                    FEATURE_EMERGENCY_MMTEL,
+                    FEATURE_MMTEL,
+                    FEATURE_RCS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureType {}
+
+    /**
+     * Integer values defining the state of the ImsFeature at any time.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    STATE_UNAVAILABLE,
                     STATE_INITIALIZING,
                     STATE_READY,
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ImsState {}
-    public static final int STATE_NOT_AVAILABLE = 0;
+
+    /**
+     * This {@link ImsFeature}'s state is unavailable and should not be communicated with.
+     */
+    public static final int STATE_UNAVAILABLE = 0;
+    /**
+     * This {@link ImsFeature} state is initializing and should not be communicated with.
+     */
     public static final int STATE_INITIALIZING = 1;
+    /**
+     * This {@link ImsFeature} is ready for communication.
+     */
     public static final int STATE_READY = 2;
 
+    /**
+     * Integer values defining the result codes that should be returned from
+     * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
+     * @hide
+     */
+    @IntDef(flag = true,
+            value = {
+                    CAPABILITY_ERROR_GENERIC,
+                    CAPABILITY_SUCCESS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsCapabilityError {}
+
+    /**
+     * The capability was unable to be changed.
+     */
+    public static final int CAPABILITY_ERROR_GENERIC = -1;
+    /**
+     * The capability was able to be changed.
+     */
+    public static final int CAPABILITY_SUCCESS = 0;
+
+
+    /**
+     * The framework implements this callback in order to register for Feature Capability status
+     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
+     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
+     * callbacks when the ImsService can not change the capability as requested, via
+     * {@link #onChangeCapabilityConfigurationError}.
+     *
+     * @hide
+     */
+    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+        @Override
+        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
+            onCapabilitiesStatusChanged(new Capabilities(config));
+        }
+
+        /**
+         * Returns the result of a query for the capability configuration of a requested capability.
+         *
+         * @param capability The capability that was requested.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param isEnabled true if the capability is enabled, false otherwise.
+         */
+        @Override
+        public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                boolean isEnabled) {
+
+        }
+
+        /**
+         * Called when a change to the capability configuration has returned an error.
+         *
+         * @param capability The capability that was requested to be changed.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param reason error associated with the failure to change configuration.
+         */
+        @Override
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+        }
+
+        /**
+         * The status of the feature's capabilities has changed to either available or unavailable.
+         * If unavailable, the feature is not able to support the unavailable capability at this
+         * time.
+         *
+         * @param config The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(Capabilities config) {
+        }
+    }
+
+    /**
+     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+     * provided.
+     */
+    protected static class CapabilityCallbackProxy {
+        private final IImsCapabilityCallback mCallback;
+
+        /** @hide */
+        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+            mCallback = c;
+        }
+
+        /**
+         * This method notifies the provided framework callback that the request to change the
+         * indicated capability has failed and has not changed.
+         *
+         * @param capability The Capability that will be notified to the framework, defined as
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
+         * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+         * @param radioTech The radio tech that this capability failed for, defined as
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+         * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+         * @param reason The reason this capability was unable to be changed, defined as
+         * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
+         */
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+            if (mCallback == null) {
+                return;
+            }
+            try {
+                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+            }
+        }
+    }
+
+    /**
+     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+     * @hide
+     */
+    public static class Capabilities {
+        protected int mCapabilities = 0;
+
+        public Capabilities() {
+        }
+
+        protected Capabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be added to the configuration in the form of a
+         *     bit mask.
+         */
+        public void addCapabilities(int capabilities) {
+            mCapabilities |= capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be removed to the configuration in the form of a
+         *     bit mask.
+         */
+        public void removeCapabilities(int capabilities) {
+            mCapabilities &= ~capabilities;
+        }
+
+        /**
+         * @return true if all of the capabilities specified are capable.
+         */
+        public boolean isCapable(int capabilities) {
+            return (mCapabilities & capabilities) == capabilities;
+        }
+
+        /**
+         * @return a deep copy of the Capabilites.
+         */
+        public Capabilities copy() {
+            return new Capabilities(mCapabilities);
+        }
+
+        /**
+         * @return a bitmask containing the capability flags directly.
+         */
+        public int getMask() {
+            return mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Capabilities)) return false;
+
+            Capabilities that = (Capabilities) o;
+
+            return mCapabilities == that.mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public int hashCode() {
+            return mCapabilities;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public String toString() {
+            return "Capabilities: " + Integer.toBinaryString(mCapabilities);
+        }
+    }
+
     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
-    private @ImsState int mState = STATE_NOT_AVAILABLE;
+    private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    /**
+     * @hide
+     */
     protected Context mContext;
+    private final Object mLock = new Object();
+    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
+            = new RemoteCallbackList<>();
+    private Capabilities mCapabilityStatus = new Capabilities();
 
-    public void setContext(Context context) {
+    /**
+     * @hide
+     */
+    public final void initialize(Context context, int slotId) {
         mContext = context;
-    }
-
-    public void setSlotId(int slotId) {
         mSlotId = slotId;
     }
 
+    /**
+     * @return The current state of the feature, defined as {@link #STATE_UNAVAILABLE},
+     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+     * @hide
+     */
     public int getFeatureState() {
-        return mState;
-    }
-
-    protected final void setFeatureState(@ImsState int state) {
-        if (mState != state) {
-            mState = state;
-            notifyFeatureState(state);
+        synchronized (mLock) {
+            return mState;
         }
     }
 
-    public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
-        if (c == null) {
-            return;
+    /**
+     * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
+     * stop communication, depending on the state sent.
+     * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
+     * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+     */
+    public final void setFeatureState(@ImsState int state) {
+        synchronized (mLock) {
+            if (mState != state) {
+                mState = state;
+                notifyFeatureState(state);
+            }
         }
+    }
+
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
         try {
             // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(mState);
+            c.notifyImsFeatureStatus(getFeatureState());
             // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mStatusCallbacks) {
+            synchronized (mLock) {
                 mStatusCallbacks.add(c);
             }
         } catch (RemoteException e) {
@@ -133,23 +406,24 @@
         }
     }
 
-    public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
-        if (c == null) {
-            return;
-        }
-        synchronized (mStatusCallbacks) {
+    /**
+     * Not final for testing, but shouldn't be extended!
+     * @hide
+     */
+    @VisibleForTesting
+    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        synchronized (mLock) {
             mStatusCallbacks.remove(c);
         }
     }
 
     /**
      * Internal method called by ImsFeature when setFeatureState has changed.
-     * @param state
      */
     private void notifyFeatureState(@ImsState int state) {
-        synchronized (mStatusCallbacks) {
+        synchronized (mLock) {
             for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                 iter.hasNext(); ) {
+                    iter.hasNext(); ) {
                 IImsFeatureStatusCallback callback = iter.next();
                 try {
                     Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
@@ -168,12 +442,12 @@
      * Provide backwards compatibility using deprecated service UP/DOWN intents.
      */
     private void sendImsServiceIntent(@ImsState int state) {
-        if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
             return;
         }
         Intent intent;
         switch (state) {
-            case ImsFeature.STATE_NOT_AVAILABLE:
+            case ImsFeature.STATE_UNAVAILABLE:
             case ImsFeature.STATE_INITIALIZING:
                 intent = new Intent(ACTION_IMS_SERVICE_DOWN);
                 break;
@@ -188,17 +462,104 @@
     }
 
     /**
-     * Called when the feature is ready to use.
+     * @hide
      */
-    public abstract void onFeatureReady();
+    public final void addCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.register(c);
+    }
 
     /**
-     * Called when the feature is being removed and must be cleaned up.
+     * @hide
+     */
+    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.unregister(c);
+    }
+
+    /**
+     * @return the cached capabilities status for this feature.
+     * @hide
+     */
+    @VisibleForTesting
+    public Capabilities queryCapabilityStatus() {
+        synchronized (mLock) {
+            return mCapabilityStatus.copy();
+        }
+    }
+
+    /**
+     * Called internally to request the change of enabled capabilities.
+     * @hide
+     */
+    @VisibleForTesting
+    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+            IImsCapabilityCallback c) {
+        if (request == null) {
+            throw new IllegalArgumentException(
+                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+        }
+        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+    }
+
+    /**
+     * Called by the ImsFeature when the capabilities status has changed.
+     *
+     * @param c A {@link Capabilities} containing the new Capabilities status.
+     *
+     * @hide
+     */
+    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+        synchronized (mLock) {
+            mCapabilityStatus = c.copy();
+        }
+        int count = mCapabilityCallbacks.beginBroadcast();
+        try {
+            for (int i = 0; i < count; i++) {
+                try {
+                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
+                            c.mCapabilities);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
+                            "callback.");
+                }
+            }
+        } finally {
+            mCapabilityCallbacks.finishBroadcast();
+        }
+    }
+
+    /**
+     * Features should override this method to receive Capability preference change requests from
+     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+     * each failed capability.
+     *
+     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+     *     enable/disable.
+     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+     * setting a subset of these capabilities fail, using
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+     */
+    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c);
+
+    /**
+     * Called when the framework is removing this feature and it needs to be cleaned up.
      */
     public abstract void onFeatureRemoved();
 
     /**
-     * @return Binder instance
+     * Called when the feature has been initialized and communication with the framework is set up.
+     * Any attempt by this feature to access the framework before this method is called will return
+     * with an {@link IllegalStateException}.
+     * The IMS provider should use this method to trigger registration for this feature on the IMS
+     * network, if needed.
      */
-    public abstract IInterface getBinder();
+    public abstract void onFeatureReady();
+
+    /**
+     * @return Binder instance that the framework will use to communicate with this feature.
+     * @hide
+     */
+    protected abstract IInterface getBinder();
 }
diff --git a/android/telephony/ims/feature/MMTelFeature.java b/android/telephony/ims/feature/MMTelFeature.java
deleted file mode 100644
index 5197107..0000000
--- a/android/telephony/ims/feature/MMTelFeature.java
+++ /dev/null
@@ -1,431 +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.telephony.ims.feature;
-
-import android.app.PendingIntent;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.internal.stub.SmsImplBase;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsSmsListener;
-import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
-
-/**
- * Base implementation for MMTel.
- * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
- * service supports.
- *
- * @hide
- */
-
-public class MMTelFeature extends ImsFeature {
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
-
-    private final IImsMMTelFeature mImsMMTelBinder = new IImsMMTelFeature.Stub() {
-
-        @Override
-        public int startSession(PendingIntent incomingCallIntent,
-                IImsRegistrationListener listener) throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.startSession(incomingCallIntent, listener);
-            }
-        }
-
-        @Override
-        public void endSession(int sessionId) throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.endSession(sessionId);
-            }
-        }
-
-        @Override
-        public boolean isConnected(int callSessionType, int callType)
-                throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.isConnected(callSessionType, callType);
-            }
-        }
-
-        @Override
-        public boolean isOpened() throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.isOpened();
-            }
-        }
-
-        @Override
-        public int getFeatureStatus() throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.getFeatureState();
-            }
-        }
-
-        @Override
-        public void addRegistrationListener(IImsRegistrationListener listener)
-                throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.addRegistrationListener(listener);
-            }
-        }
-
-        @Override
-        public void removeRegistrationListener(IImsRegistrationListener listener)
-                throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.removeRegistrationListener(listener);
-            }
-        }
-
-        @Override
-        public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
-                throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.createCallProfile(sessionId, callSessionType,  callType);
-            }
-        }
-
-        @Override
-        public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-                IImsCallSessionListener listener) throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.createCallSession(sessionId, profile, listener);
-            }
-        }
-
-        @Override
-        public IImsCallSession getPendingCallSession(int sessionId, String callId)
-                throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.getPendingCallSession(sessionId, callId);
-            }
-        }
-
-        @Override
-        public IImsUt getUtInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.getUtInterface();
-            }
-        }
-
-        @Override
-        public IImsConfig getConfigInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.getConfigInterface();
-            }
-        }
-
-        @Override
-        public void turnOnIms() throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.turnOnIms();
-            }
-        }
-
-        @Override
-        public void turnOffIms() throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.turnOffIms();
-            }
-        }
-
-        @Override
-        public IImsEcbm getEcbmInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.getEcbmInterface();
-            }
-        }
-
-        @Override
-        public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.setUiTTYMode(uiTtyMode, onComplete);
-            }
-        }
-
-        @Override
-        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MMTelFeature.this.getMultiEndpointInterface();
-            }
-        }
-
-        @Override
-        public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            synchronized (mLock) {
-                MMTelFeature.this.setSmsListener(l);
-            }
-        }
-
-        @Override
-        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
-                byte[] pdu) {
-            synchronized (mLock) {
-                MMTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
-            }
-        }
-
-        @Override
-        public void acknowledgeSms(int token, int messageRef, int result) {
-            synchronized (mLock) {
-                MMTelFeature.this.acknowledgeSms(token, messageRef, result);
-            }
-        }
-
-        @Override
-        public void acknowledgeSmsReport(int token, int messageRef, int result) {
-            synchronized (mLock) {
-                MMTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
-            }
-        }
-
-        @Override
-        public String getSmsFormat() {
-            synchronized (mLock) {
-                return MMTelFeature.this.getSmsFormat();
-            }
-        }
-    };
-
-    /**
-     * @hide
-     */
-    @Override
-    public final IImsMMTelFeature getBinder() {
-        return mImsMMTelBinder;
-    }
-
-    /**
-     * Notifies the MMTel feature that you would like to start a session. This should always be
-     * done before making/receiving IMS calls. The IMS service will register the device to the
-     * operator's network with the credentials (from ISIM) periodically in order to receive calls
-     * from the operator's network. When the IMS service receives a new call, it will send out an
-     * intent with the provided action string. The intent contains a call ID extra
-     * {@link IImsCallSession#getCallId} and it can be used to take a call.
-     *
-     * @param incomingCallIntent When an incoming call is received, the IMS service will call
-     * {@link PendingIntent#send} to send back the intent to the caller with
-     * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
-     * ID; It cannot be null.
-     * @param listener To listen to IMS registration events; It cannot be null
-     * @return an integer (greater than 0) representing the session id associated with the session
-     * that has been started.
-     */
-    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
-        return 0;
-    }
-
-    /**
-     * End a previously started session using the associated sessionId.
-     * @param sessionId an integer (greater than 0) representing the ongoing session. See
-     * {@link #startSession}.
-     */
-    public void endSession(int sessionId) {
-    }
-
-    /**
-     * Checks if the IMS service has successfully registered to the IMS network with the specified
-     * service & call type.
-     *
-     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
-     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
-     * @param callType a call type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
-     *        {@link ImsCallProfile#CALL_TYPE_VT}
-     *        {@link ImsCallProfile#CALL_TYPE_VS}
-     * @return true if the specified service id is connected to the IMS network; false otherwise
-     */
-    public boolean isConnected(int callSessionType, int callType) {
-        return false;
-    }
-
-    /**
-     * Checks if the specified IMS service is opened.
-     *
-     * @return true if the specified service id is opened; false otherwise
-     */
-    public boolean isOpened() {
-        return false;
-    }
-
-    /**
-     * Add a new registration listener for the client associated with the session Id.
-     * @param listener An implementation of IImsRegistrationListener.
-     */
-    public void addRegistrationListener(IImsRegistrationListener listener) {
-    }
-
-    /**
-     * Remove a previously registered listener using {@link #addRegistrationListener} for the client
-     * associated with the session Id.
-     * @param listener A previously registered IImsRegistrationListener
-     */
-    public void removeRegistrationListener(IImsRegistrationListener listener) {
-    }
-
-    /**
-     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
-     *
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
-     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
-     * @param callType a call type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
-     *        {@link ImsCallProfile#CALL_TYPE_VT}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
-     *        {@link ImsCallProfile#CALL_TYPE_VS}
-     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
-     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
-     * @return a {@link ImsCallProfile} object
-     */
-    public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
-        return null;
-    }
-
-    /**
-     * Creates an {@link ImsCallSession} with the specified call profile.
-     * Use other methods, if applicable, instead of interacting with
-     * {@link ImsCallSession} directly.
-     *
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     * @param profile a call profile to make the call
-     * @param listener An implementation of IImsCallSessionListener.
-     */
-    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
-            IImsCallSessionListener listener) {
-        return null;
-    }
-
-    /**
-     * Retrieves the call session associated with a pending call.
-     *
-     * @param sessionId a session id which is obtained from {@link #startSession}
-     * @param callId a call id to make the call
-     */
-    public IImsCallSession getPendingCallSession(int sessionId, String callId) {
-        return null;
-    }
-
-    /**
-     * @return The Ut interface for the supplementary service configuration.
-     */
-    public IImsUt getUtInterface() {
-        return null;
-    }
-
-    /**
-     * @return The config interface for IMS Configuration
-     */
-    public IImsConfig getConfigInterface() {
-        return null;
-    }
-
-    /**
-     * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
-     */
-    public void turnOnIms() {
-    }
-
-    /**
-     * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
-     */
-    public void turnOffIms() {
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public IImsEcbm getEcbmInterface() {
-        return null;
-    }
-
-    /**
-     * Sets the current UI TTY mode for the MMTelFeature.
-     * @param uiTtyMode An integer containing the new UI TTY Mode.
-     * @param onComplete A {@link Message} to be used when the mode has been set.
-     */
-    public void setUiTTYMode(int uiTtyMode, Message onComplete) {
-    }
-
-    /**
-     * @return MultiEndpoint interface for DEP notifications
-     */
-    public IImsMultiEndpoint getMultiEndpointInterface() {
-        return null;
-    }
-
-    public void setSmsListener(IImsSmsListener listener) {
-        getSmsImplementation().registerSmsListener(listener);
-    }
-
-    public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
-            byte[] pdu) {
-        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
-    }
-
-    public void acknowledgeSms(int token, int messageRef,
-            @SmsImplBase.DeliverStatusResult int result) {
-        getSmsImplementation().acknowledgeSms(token, messageRef, result);
-    }
-
-    public void acknowledgeSmsReport(int token, int messageRef,
-            @SmsImplBase.StatusReportResult int result) {
-        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
-    }
-
-    /**
-     * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
-     * non-functional implementation is returned.
-     *
-     * @return an instance of {@link SmsImplBase} which should be implemented by the IMS Provider.
-     */
-    protected SmsImplBase getSmsImplementation() {
-        return new SmsImplBase();
-    }
-
-    public String getSmsFormat() {
-        return getSmsImplementation().getSmsFormat();
-    }
-
-    @Override
-    public void onFeatureReady() {
-
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void onFeatureRemoved() {
-
-    }
-}
diff --git a/android/telephony/ims/feature/MmTelFeature.java b/android/telephony/ims/feature/MmTelFeature.java
new file mode 100644
index 0000000..aaf1a1c
--- /dev/null
+++ b/android/telephony/ims/feature/MmTelFeature.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.feature;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsCallSessionImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsMmTelListener;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import android.telephony.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsUt;
+import android.telephony.ims.ImsCallSession;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
+ *
+ * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
+ * service supports.
+ * @hide
+ */
+@SystemApi
+public class MmTelFeature extends ImsFeature {
+
+    private static final String LOG_TAG = "MmTelFeature";
+
+    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+        @Override
+        public void setListener(IImsMmTelListener l) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setListener(l);
+            }
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    return MmTelFeature.this.getFeatureState();
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+
+        @Override
+        public ImsCallProfile createCallProfile(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    return MmTelFeature.this.createCallProfile(callSessionType, callType);
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
+            synchronized (mLock) {
+                return createCallSessionInterface(profile);
+            }
+        }
+
+        @Override
+        public int shouldProcessCall(String[] numbers) {
+            synchronized (mLock) {
+                return MmTelFeature.this.shouldProcessCall(numbers);
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getUtInterface();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getEcbmInterface();
+            }
+        }
+
+        @Override
+        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+            synchronized (mLock) {
+                try {
+                    MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+                } catch (Exception e) {
+                    throw new RemoteException(e.getMessage());
+                }
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getMultiEndpointInterface();
+            }
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+            }
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
+            MmTelFeature.this.addCapabilityCallback(c);
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
+            MmTelFeature.this.removeCapabilityCallback(c);
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+                IImsCapabilityCallback c) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+            }
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) {
+            synchronized (mLock) {
+                queryCapabilityConfigurationInternal(capability, radioTech, c);
+            }
+        }
+
+        @Override
+        public void setSmsListener(IImsSmsListener l) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setSmsListener(l);
+            }
+        }
+
+        @Override
+        public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
+                byte[] pdu) {
+            synchronized (mLock) {
+                MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
+            }
+        }
+
+        @Override
+        public void acknowledgeSms(int token, int messageRef, int result) {
+            synchronized (mLock) {
+                MmTelFeature.this.acknowledgeSms(token, messageRef, result);
+            }
+        }
+
+        @Override
+        public void acknowledgeSmsReport(int token, int messageRef, int result) {
+            synchronized (mLock) {
+                MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
+            }
+        }
+
+        @Override
+        public String getSmsFormat() {
+            synchronized (mLock) {
+                return MmTelFeature.this.getSmsFormat();
+            }
+        }
+
+        @Override
+        public void onSmsReady() {
+            synchronized (mLock) {
+                MmTelFeature.this.onSmsReady();
+            }
+        }
+    };
+
+    /**
+     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
+     * The capabilities that are used in MmTelFeature are defined as
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
+     * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+     *
+     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
+     * {@link #queryCapabilityStatus()}.
+     *
+     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
+     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
+     * status can also be queried using {@link #queryCapabilityStatus()}.
+     */
+    public static class MmTelCapabilities extends Capabilities {
+
+        /**
+         * @hide
+         */
+        @VisibleForTesting
+        public MmTelCapabilities() {
+            super();
+        }
+
+        public MmTelCapabilities(Capabilities c) {
+            mCapabilities = c.mCapabilities;
+        }
+
+        public MmTelCapabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        @IntDef(flag = true,
+                value = {
+                        CAPABILITY_TYPE_VOICE,
+                        CAPABILITY_TYPE_VIDEO,
+                        CAPABILITY_TYPE_UT,
+                        CAPABILITY_TYPE_SMS
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface MmTelCapability {}
+
+        /**
+         * This MmTelFeature supports Voice calling (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
+
+        /**
+         * This MmTelFeature supports Video (IR.94)
+         */
+        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
+
+        /**
+         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_UT = 1 << 2;
+
+        /**
+         * This MmTelFeature supports SMS (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_SMS = 1 << 3;
+
+        @Override
+        public final void addCapabilities(@MmTelCapability int capabilities) {
+            super.addCapabilities(capabilities);
+        }
+
+        @Override
+        public final void removeCapabilities(@MmTelCapability int capability) {
+            super.removeCapabilities(capability);
+        }
+
+        @Override
+        public final boolean isCapable(@MmTelCapability int capabilities) {
+            return super.isCapable(capabilities);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
+            builder.append("Voice: ");
+            builder.append(isCapable(CAPABILITY_TYPE_VOICE));
+            builder.append(" Video: ");
+            builder.append(isCapable(CAPABILITY_TYPE_VIDEO));
+            builder.append(" UT: ");
+            builder.append(isCapable(CAPABILITY_TYPE_UT));
+            builder.append(" SMS: ");
+            builder.append(isCapable(CAPABILITY_TYPE_SMS));
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Listener that the framework implements for communication from the MmTelFeature.
+     * @hide
+     */
+    public static class Listener extends IImsMmTelListener.Stub {
+
+        /**
+         * Called when the IMS provider receives an incoming call.
+         * @param c The {@link ImsCallSession} associated with the new call.
+         */
+        @Override
+        public void onIncomingCall(IImsCallSession c, Bundle extras) {
+
+        }
+
+        /**
+         * Updates the Listener when the voice message count for IMS has changed.
+         * @param count an integer representing the new message count.
+         */
+        @Override
+        public void onVoiceMessageCountUpdate(int count) {
+
+        }
+    }
+
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
+     * outgoing call as IMS.
+     */
+    public static final int PROCESS_CALL_IMS = 0;
+    /**
+     * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
+     * not process the outgoing call as IMS and should instead use circuit switch.
+     */
+    public static final int PROCESS_CALL_CSFB = 1;
+
+    @IntDef(flag = true,
+            value = {
+                    PROCESS_CALL_IMS,
+                    PROCESS_CALL_CSFB
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProcessCallResult {}
+
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+    private IImsMmTelListener mListener;
+
+    /**
+     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
+     *     notifies the framework.
+     */
+    private void setListener(IImsMmTelListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+        if (mListener != null) {
+            onFeatureReady();
+        }
+    }
+
+    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
+            IImsCapabilityCallback c) {
+        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+        try {
+            if (c != null) {
+                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+        }
+    }
+
+    /**
+     * The current capability status that this MmTelFeature has defined is available. This
+     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
+     * available to be used.
+     *
+     * Should be a subset of the capabilities that are enabled by the framework in
+     * {@link #changeEnabledCapabilities}.
+     * @return A copy of the current MmTelFeature capability status.
+     */
+    @Override
+    public final MmTelCapabilities queryCapabilityStatus() {
+        return new MmTelCapabilities(super.queryCapabilityStatus());
+    }
+
+    /**
+     * Notify the framework that the status of the Capabilities has changed. Even though the
+     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
+     * the feature being unavailable from the network.
+     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
+     * the status of that capability is disabled. This can happen if the network does not currently
+     * support the capability that is enabled. A capability that is disabled by the framework (via
+     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
+     */
+    public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+        super.notifyCapabilitiesStatusChanged(c);
+    }
+
+    /**
+     * Notify the framework of an incoming call.
+     * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
+     */
+    public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onIncomingCall(c.getServiceImpl(), extras);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     *
+     * @hide
+     */
+    public final void notifyIncomingCallSession(IImsCallSession c, Bundle extras) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onIncomingCall(c, extras);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Notify the framework of a change in the Voice Message count.
+     * @link count the new Voice Message count.
+     */
+    public final void notifyVoiceMessageCountUpdate(int count) {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            try {
+                mListener.onVoiceMessageCountUpdate(count);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     */
+    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base implementation - Override to provide functionality
+        return false;
+    }
+
+    /**
+     * The MmTelFeature should override this method to handle the enabling/disabling of
+     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
+     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
+     * could not be set to their new values,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
+     * individually for each capability whose processing resulted in an error.
+     *
+     * Enabling/Disabling a capability here indicates that the capability should be registered or
+     * deregistered (depending on the capability change) and become available or unavailable to
+     * the framework.
+     */
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Base implementation, no-op
+    }
+
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
+     * @return a {@link ImsCallProfile} object
+     */
+    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
+            throws RemoteException {
+        ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
+        return s != null ? s.getServiceImpl() : null;
+    }
+
+    /**
+     * Creates an {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param profile a call profile to make the call
+     */
+    public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Called by the framework to determine if the outgoing call, designated by the outgoing
+     * {@link String}s, should be processed as an IMS call or CSFB call. If this method's
+     * functionality is not overridden, the platform will process every call as IMS as long as the
+     * MmTelFeature reports that the {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE} capability is
+     * available.
+     * @param numbers An array of {@link String}s that will be used for placing the call. There can
+     *         be multiple {@link String}s listed in the case when we want to place an outgoing
+     *         call as a conference.
+     * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
+     *        call will be placed over IMS or via CSFB.
+     */
+    public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+        return PROCESS_CALL_IMS;
+    }
+
+    /**
+     *
+     * @hide
+     */
+    protected IImsUt getUtInterface() throws RemoteException {
+        ImsUtImplBase utImpl = getUt();
+        return utImpl != null ? utImpl.getInterface() : null;
+    }
+
+    /**
+     * @hide
+     */
+    protected IImsEcbm getEcbmInterface() throws RemoteException {
+        ImsEcbmImplBase ecbmImpl = getEcbm();
+        return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+    }
+
+    /**
+     * @hide
+     */
+    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+        ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
+        return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+    }
+
+    /**
+     * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
+     * configuration.
+     */
+    public ImsUtImplBase getUt() {
+        // Base Implementation - Should be overridden
+        return new ImsUtImplBase();
+    }
+
+    /**
+     * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
+     * calls that support it.
+     */
+    public ImsEcbmImplBase getEcbm() {
+        // Base Implementation - Should be overridden
+        return new ImsEcbmImplBase();
+    }
+
+    /**
+     * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
+     * package processing for multi-endpoint.
+     */
+    public ImsMultiEndpointImplBase getMultiEndpoint() {
+        // Base Implementation - Should be overridden
+        return new ImsMultiEndpointImplBase();
+    }
+
+    /**
+     * Sets the current UI TTY mode for the MmTelFeature.
+     * @param mode An integer containing the new UI TTY Mode, can consist of
+     *         {@link TelecomManager#TTY_MODE_OFF},
+     *         {@link TelecomManager#TTY_MODE_FULL},
+     *         {@link TelecomManager#TTY_MODE_HCO},
+     *         {@link TelecomManager#TTY_MODE_VCO}
+     * @param onCompleteMessage A {@link Message} to be used when the mode has been set.
+     */
+    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+        // Base Implementation - Should be overridden
+    }
+
+    private void setSmsListener(IImsSmsListener listener) {
+        getSmsImplementation().registerSmsListener(listener);
+    }
+
+    private void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+            byte[] pdu) {
+        getSmsImplementation().sendSms(token, messageRef, format, smsc, isRetry, pdu);
+    }
+
+    private void acknowledgeSms(int token, int messageRef,
+            @ImsSmsImplBase.DeliverStatusResult int result) {
+        getSmsImplementation().acknowledgeSms(token, messageRef, result);
+    }
+
+    private void acknowledgeSmsReport(int token, int messageRef,
+            @ImsSmsImplBase.StatusReportResult int result) {
+        getSmsImplementation().acknowledgeSmsReport(token, messageRef, result);
+    }
+
+    private void onSmsReady() {
+        getSmsImplementation().onReady();
+    }
+
+    /**
+     * Must be overridden by IMS Provider to be able to support SMS over IMS. Otherwise a default
+     * non-functional implementation is returned.
+     *
+     * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
+     * Provider.
+     */
+    public ImsSmsImplBase getSmsImplementation() {
+        return new ImsSmsImplBase();
+    }
+
+    private String getSmsFormat() {
+        return getSmsImplementation().getSmsFormat();
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureRemoved() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMmTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+}
diff --git a/android/telephony/ims/feature/RcsFeature.java b/android/telephony/ims/feature/RcsFeature.java
index 40c5181..a637e16 100644
--- a/android/telephony/ims/feature/RcsFeature.java
+++ b/android/telephony/ims/feature/RcsFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,16 +16,18 @@
 
 package android.telephony.ims.feature;
 
-import com.android.ims.internal.IImsRcsFeature;
+import android.annotation.SystemApi;
+import android.telephony.ims.aidl.IImsRcsFeature;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
  * this class and provide implementations of the RcsFeature methods that they support.
  * @hide
  */
-
+@SystemApi
 public class RcsFeature extends ImsFeature {
 
+    /**{@inheritDoc}*/
     private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
         // Empty Default Implementation.
     };
@@ -35,16 +37,30 @@
         super();
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public void onFeatureReady() {
-
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Do nothing for base implementation.
     }
 
+    /**{@inheritDoc}*/
     @Override
     public void onFeatureRemoved() {
 
     }
 
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    /**
+     * @hide
+     */
     @Override
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
diff --git a/android/telephony/ims/internal/ImsCallSessionListener.java b/android/telephony/ims/internal/ImsCallSessionListener.java
deleted file mode 100644
index 5d16dd5..0000000
--- a/android/telephony/ims/internal/ImsCallSessionListener.java
+++ /dev/null
@@ -1,364 +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.telephony.ims.internal;
-
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.ImsCallSession;
-
-/**
- * Proxy class for interfacing with the framework's Call session for an ongoing IMS call.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListener {
-
-    private final IImsCallSessionListener mListener;
-
-    public ImsCallSessionListener(IImsCallSessionListener l) {
-        mListener = l;
-    }
-
-    /**
-     * Called when a request is sent out to initiate a new session
-     * and 1xx response is received from the network.
-     */
-    public void callSessionProgressing(ImsStreamMediaProfile profile)
-            throws RemoteException {
-        mListener.callSessionProgressing(profile);
-    }
-
-    /**
-     * Called when the session is initiated.
-     *
-     * @param profile the associated {@link ImsCallSession}.
-     */
-    public void callSessionInitiated(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionInitiated(profile);
-    }
-
-    /**
-     * Called when the session establishment has failed.
-     *
-     * @param reasonInfo detailed reason of the session establishment failure
-     */
-    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionInitiatedFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session is terminated.
-     *
-     * @param reasonInfo detailed reason of the session termination
-     */
-    public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionTerminated(reasonInfo);
-    }
-
-    /**
-     * Called when the session is on hold.
-     */
-    public void callSessionHeld(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionHeld(profile);
-    }
-
-    /**
-     * Called when the session hold has failed.
-     *
-     * @param reasonInfo detailed reason of the session hold failure
-     */
-    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHoldFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session hold is received from the remote user.
-     */
-    public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionHoldReceived(profile);
-    }
-
-    /**
-     * Called when the session resume is done.
-     */
-    public void callSessionResumed(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionResumed(profile);
-    }
-
-    /**
-     * Called when the session resume has failed.
-     *
-     * @param reasonInfo detailed reason of the session resume failure
-     */
-    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionResumeFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session resume is received from the remote user.
-     */
-    public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionResumeReceived(profile);
-    }
-
-    /**
-     * Called when the session merge has been started.  At this point, the {@code newSession}
-     * represents the session which has been initiated to the IMS conference server for the
-     * new merged conference.
-     *
-     * @param newSession the session object that is merged with an active & hold session
-     */
-    public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile)
-            throws RemoteException {
-        mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null,
-                profile);
-    }
-
-    /**
-     * Called when the session merge is successful and the merged session is active.
-     *
-     * @param newSession the new session object that is used for the conference
-     */
-    public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException {
-        mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null);
-    }
-
-    /**
-     * Called when the session merge has failed.
-     *
-     * @param reasonInfo detailed reason of the call merge failure
-     */
-    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionMergeFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session is updated (except for hold/unhold).
-     */
-    public void callSessionUpdated(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionUpdated(profile);
-    }
-
-    /**
-     * Called when the session update has failed.
-     *
-     * @param reasonInfo detailed reason of the session update failure
-     */
-    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionUpdateFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the session update is received from the remote user.
-     */
-    public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionUpdateReceived(profile);
-    }
-
-    /**
-     * Called when the session has been extended to a conference session.
-     *
-     * @param newSession the session object that is extended to the conference
-     *      from the active session
-     */
-    public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile)
-            throws RemoteException {
-        mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null,
-                profile);
-    }
-
-    /**
-     * Called when the conference extension has failed.
-     *
-     * @param reasonInfo detailed reason of the conference extension failure
-     */
-    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionConferenceExtendFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the conference extension is received from the remote user.
-     */
-    public void callSessionConferenceExtendReceived(ImsCallSession newSession,
-            ImsCallProfile profile) throws RemoteException {
-        mListener.callSessionConferenceExtendReceived(newSession != null
-                ? newSession.getSession() : null, profile);
-    }
-
-    /**
-     * Called when the invitation request of the participants is delivered to the conference
-     * server.
-     */
-    public void callSessionInviteParticipantsRequestDelivered() throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestDelivered();
-    }
-
-    /**
-     * Called when the invitation request of the participants has failed.
-     *
-     * @param reasonInfo detailed reason of the conference invitation failure
-     */
-    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
-            throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
-    }
-
-    /**
-     * Called when the removal request of the participants is delivered to the conference
-     * server.
-     */
-    public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException {
-        mListener.callSessionRemoveParticipantsRequestDelivered();
-    }
-
-    /**
-     * Called when the removal request of the participants has failed.
-     *
-     * @param reasonInfo detailed reason of the conference removal failure
-     */
-    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
-            throws RemoteException {
-        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
-    }
-
-    /**
-     * Notifies the framework of the updated Call session conference state.
-     *
-     * @param state the new {@link ImsConferenceState} associated with the conference.
-     */
-    public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException {
-        mListener.callSessionConferenceStateUpdated(state);
-    }
-
-    /**
-     * Notifies the incoming USSD message.
-     */
-    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
-            throws RemoteException {
-        mListener.callSessionUssdMessageReceived(mode, ussdMessage);
-    }
-
-    /**
-     * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
-     * handover from one radio technology to another.
-     *
-     * @param srcAccessTech    The source radio access technology; one of the access technology
-     *                         constants defined in {@link android.telephony.ServiceState}.  For
-     *                         example
-     *                         {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     * @param targetAccessTech The target radio access technology; one of the access technology
-     *                         constants defined in {@link android.telephony.ServiceState}.  For
-     *                         example
-     *                         {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     */
-    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
-            throws RemoteException {
-        mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
-    }
-
-    /**
-     * Called when session access technology changes.
-     *
-     * @param srcAccessTech original access technology
-     * @param targetAccessTech new access technology
-     * @param reasonInfo
-     */
-    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
-            ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
-    }
-
-    /**
-     * Called when session access technology change fails.
-     *
-     * @param srcAccessTech original access technology
-     * @param targetAccessTech new access technology
-     * @param reasonInfo handover failure reason
-     */
-    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
-            ImsReasonInfo reasonInfo) throws RemoteException {
-        mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
-    }
-
-    /**
-     * Called when the TTY mode is changed by the remote party.
-     *
-     * @param mode one of the following: -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
-     *             {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
-     */
-    public void callSessionTtyModeReceived(int mode) throws RemoteException {
-        mListener.callSessionTtyModeReceived(mode);
-    }
-
-    /**
-     * Called when the multiparty state is changed for this {@code ImsCallSession}.
-     *
-     * @param isMultiParty {@code true} if the session became multiparty,
-     *                     {@code false} otherwise.
-     */
-
-    public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException {
-        mListener.callSessionMultipartyStateChanged(isMultiParty);
-    }
-
-    /**
-     * Called when the supplementary service information is received for the current session.
-     */
-    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
-            throws RemoteException {
-        mListener.callSessionSuppServiceReceived(suppSrvNotification);
-    }
-
-    /**
-     * Received RTT modify request from the remote party.
-     *
-     * @param callProfile ImsCallProfile with updated attributes
-     */
-    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
-            throws RemoteException {
-        mListener.callSessionRttModifyRequestReceived(callProfile);
-    }
-
-    /**
-     * @param status the received response for RTT modify request.
-     */
-    public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
-        mListener.callSessionRttModifyResponseReceived(status);
-    }
-
-    /**
-     * Device received RTT message from Remote UE.
-     *
-     * @param rttMessage RTT message received
-     */
-    public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
-        mListener.callSessionRttMessageReceived(rttMessage);
-    }
-}
-
diff --git a/android/telephony/ims/internal/ImsService.java b/android/telephony/ims/internal/ImsService.java
deleted file mode 100644
index afaf332..0000000
--- a/android/telephony/ims/internal/ImsService.java
+++ /dev/null
@@ -1,339 +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.telephony.ims.internal;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.CarrierConfigManager;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-import android.telephony.ims.internal.aidl.IImsServiceController;
-import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.telephony.ims.internal.feature.RcsFeature;
-import android.telephony.ims.internal.stub.ImsConfigImplBase;
-import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsRegistration;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
- * ImsService must register the service in their AndroidManifest to be detected by the framework.
- * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
- * permission. Then, the ImsService definition in the manifest must follow the following format:
- *
- * ...
- * <service android:name=".EgImsService"
- *     android:permission="android.permission.BIND_IMS_SERVICE" >
- *     <!-- Apps must declare which features they support as metadata. The different categories are
- *     defined below. In this example, the RCS_FEATURE feature is supported. -->
- *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
- *     <intent-filter>
- *         <action android:name="android.telephony.ims.ImsService" />
- *     </intent-filter>
- * </service>
- * ...
- *
- * The telephony framework will then bind to the ImsService you have defined in your manifest
- * if you are either:
- * 1) Defined as the default ImsService for the device in the device overlay using
- *    "config_ims_package".
- * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
- *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
- *
- * The features that are currently supported in an ImsService are:
- * - RCS_FEATURE: This ImsService implements the RcsFeature class.
- * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
- *   @hide
- */
-public class ImsService extends Service {
-
-    private static final String LOG_TAG = "ImsService";
-
-    /**
-     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
-     * @hide
-     */
-    public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
-
-    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
-    // slot.
-    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
-    // call ImsFeature#onFeatureRemoved.
-    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
-
-    private IImsServiceControllerListener mListener;
-
-
-    /**
-     * Listener that notifies the framework of ImsService changes.
-     */
-    public static class Listener extends IImsServiceControllerListener.Stub {
-        /**
-         * The IMS features that this ImsService supports has changed.
-         * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
-         *   that this ImsService supports. This may trigger the addition/removal of feature
-         *   in this service.
-         */
-        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
-        }
-    }
-
-    /**
-     * @hide
-     */
-    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
-        @Override
-        public void setListener(IImsServiceControllerListener l) {
-            mListener = l;
-        }
-
-        @Override
-        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createMmTelFeatureInternal(slotId, c);
-        }
-
-        @Override
-        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
-            return createRcsFeatureInternal(slotId, c);
-        }
-
-        @Override
-        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
-                throws RemoteException {
-            ImsService.this.removeImsFeature(slotId, featureType, c);
-        }
-
-        @Override
-        public ImsFeatureConfiguration querySupportedImsFeatures() {
-            return ImsService.this.querySupportedImsFeatures();
-        }
-
-        @Override
-        public void notifyImsServiceReadyForFeatureCreation() {
-            ImsService.this.readyForFeatureCreation();
-        }
-
-        @Override
-        public void notifyImsFeatureReady(int slotId, int featureType)
-                throws RemoteException {
-            ImsService.this.notifyImsFeatureReady(slotId, featureType);
-        }
-
-        @Override
-        public IImsConfig getConfig(int slotId) throws RemoteException {
-            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
-            return c != null ? c.getBinder() : null;
-        }
-
-        @Override
-        public IImsRegistration getRegistration(int slotId) throws RemoteException {
-            ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
-            return r != null ? r.getBinder() : null;
-        }
-    };
-
-    /**
-     * @hide
-     */
-    @Override
-    public IBinder onBind(Intent intent) {
-        if(SERVICE_INTERFACE.equals(intent.getAction())) {
-            Log.i(LOG_TAG, "ImsService Bound.");
-            return mImsServiceController;
-        }
-        return null;
-    }
-
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    public SparseArray<ImsFeature> getFeatures(int slotId) {
-        return mFeaturesBySlot.get(slotId);
-    }
-
-    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        MmTelFeature f = createMmTelFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
-            return f.getBinder();
-        } else {
-            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
-            return null;
-        }
-    }
-
-    private IImsRcsFeature createRcsFeatureInternal(int slotId,
-            IImsFeatureStatusCallback c) {
-        RcsFeature f = createRcsFeature(slotId);
-        if (f != null) {
-            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
-            return f.getBinder();
-        } else {
-            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
-            return null;
-        }
-    }
-
-    private void setupFeature(ImsFeature f, int slotId, int featureType,
-            IImsFeatureStatusCallback c) {
-        f.addImsFeatureStatusCallback(c);
-        f.initialize(this, slotId);
-        addImsFeature(slotId, featureType, f);
-    }
-
-    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
-        synchronized (mFeaturesBySlot) {
-            // Get SparseArray for Features, by querying slot Id
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                // Populate new SparseArray of features if it doesn't exist for this slot yet.
-                features = new SparseArray<>();
-                mFeaturesBySlot.put(slotId, features);
-            }
-            features.put(featureType, f);
-        }
-    }
-
-    private void removeImsFeature(int slotId, int featureType,
-            IImsFeatureStatusCallback c) {
-        synchronized (mFeaturesBySlot) {
-            // get ImsFeature associated with the slot/feature
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
-                        + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.removeImsFeatureStatusCallback(c);
-            f.onFeatureRemoved();
-            features.remove(featureType);
-        }
-    }
-
-    private void notifyImsFeatureReady(int slotId, int featureType) {
-        synchronized (mFeaturesBySlot) {
-            // get ImsFeature associated with the slot/feature
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
-                        "slot " + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.onFeatureReady();
-        }
-    }
-
-    /**
-     * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
-     * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
-     * to the {@link ImsFeature.FeatureType}s configured here.
-     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
-     * defined in {@link ImsFeature.FeatureType}.
-     */
-    public ImsFeatureConfiguration querySupportedImsFeatures() {
-        // Return empty for base implementation
-        return new ImsFeatureConfiguration();
-    }
-
-    /**
-     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
-     * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
-     * trigger the framework to add/remove new ImsFeatures, depending on the configuration.
-     */
-    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
-            throws RemoteException {
-        if (mListener == null) {
-            throw new IllegalStateException("Framework is not ready");
-        }
-        mListener.onUpdateSupportedImsFeatures(c);
-    }
-
-    /**
-     * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
-     * the ImsService has registered for with the framework, either in the manifest or via
-     * The ImsService should use this signal instead of onCreate/onBind or similar to perform
-     * feature initialization because the framework may bind to this service multiple times to
-     * query the ImsService's {@link ImsFeatureConfiguration} via
-     * {@link #querySupportedImsFeatures()}before creating features.
-     */
-    public void readyForFeatureCreation() {
-    }
-
-    /**
-     * When called, the framework is requesting that a new MmTelFeature is created for the specified
-     * slot.
-     *
-     * @param slotId The slot ID that the MMTel Feature is being created for.
-     * @return The newly created MmTelFeature associated with the slot or null if the feature is not
-     * supported.
-     */
-    public MmTelFeature createMmTelFeature(int slotId) {
-        return null;
-    }
-
-    /**
-     * When called, the framework is requesting that a new RcsFeature is created for the specified
-     * slot
-     *
-     * @param slotId The slot ID that the RCS Feature is being created for.
-     * @return The newly created RcsFeature associated with the slot or null if the feature is not
-     * supported.
-     */
-    public RcsFeature createRcsFeature(int slotId) {
-        return null;
-    }
-
-    /**
-     * @param slotId The slot that the IMS configuration is associated with.
-     * @return ImsConfig implementation that is associated with the specified slot.
-     */
-    public ImsConfigImplBase getConfig(int slotId) {
-        return new ImsConfigImplBase();
-    }
-
-    /**
-     * @param slotId The slot that is associated with the IMS Registration.
-     * @return the ImsRegistration implementation associated with the slot.
-     */
-    public ImsRegistrationImplBase getRegistration(int slotId) {
-        return new ImsRegistrationImplBase();
-    }
-}
diff --git a/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
deleted file mode 100644
index 5dbf077..0000000
--- a/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
+++ /dev/null
@@ -1,197 +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.telephony.ims.internal.feature;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.util.ArraySet;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Request to send to IMS provider, which will try to enable/disable capabilities that are added to
- * the request.
- * {@hide}
- */
-public class CapabilityChangeRequest implements Parcelable {
-
-    public static class CapabilityPair {
-        private final int mCapability;
-        private final int radioTech;
-
-        public CapabilityPair(int capability, int radioTech) {
-            this.mCapability = capability;
-            this.radioTech = radioTech;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof CapabilityPair)) return false;
-
-            CapabilityPair that = (CapabilityPair) o;
-
-            if (getCapability() != that.getCapability()) return false;
-            return getRadioTech() == that.getRadioTech();
-        }
-
-        @Override
-        public int hashCode() {
-            int result = getCapability();
-            result = 31 * result + getRadioTech();
-            return result;
-        }
-
-        public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
-            return mCapability;
-        }
-
-        public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
-            return radioTech;
-        }
-    }
-
-    // Pair contains <radio tech, mCapability>
-    private final Set<CapabilityPair> mCapabilitiesToEnable;
-    // Pair contains <radio tech, mCapability>
-    private final Set<CapabilityPair> mCapabilitiesToDisable;
-
-    public CapabilityChangeRequest() {
-        mCapabilitiesToEnable = new ArraySet<>();
-        mCapabilitiesToDisable = new ArraySet<>();
-    }
-
-    /**
-     * Add one or many capabilities to the request to be enabled.
-     *
-     * @param capabilities A bitfield of capabilities to enable, valid values are defined in
-     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
-     * @param radioTech  the radio tech that these capabilities should be enabled for, valid
-     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
-     */
-    public void addCapabilitiesToEnableForTech(
-            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
-            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
-        addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
-    }
-
-    /**
-     * Add one or many capabilities to the request to be disabled.
-     * @param capabilities A bitfield of capabilities to diable, valid values are defined in
-     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
-     * @param radioTech the radio tech that these capabilities should be disabled for, valid
-     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
-     */
-    public void addCapabilitiesToDisableForTech(
-            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
-            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
-        addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
-    }
-
-    /**
-     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled.
-     */
-    public List<CapabilityPair> getCapabilitiesToEnable() {
-        return new ArrayList<>(mCapabilitiesToEnable);
-    }
-
-    /**
-     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled.
-     */
-    public List<CapabilityPair> getCapabilitiesToDisable() {
-        return new ArrayList<>(mCapabilitiesToDisable);
-    }
-
-    // Iterate through capabilities bitfield and add each one as a pair associated with the radio
-    // technology
-    private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) {
-        long highestCapability = Long.highestOneBit(capabilities);
-        for (int i = 1; i <= highestCapability; i *= 2) {
-            if ((i & capabilities) > 0) {
-                set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech));
-            }
-        }
-    }
-
-    protected CapabilityChangeRequest(Parcel in) {
-        int enableSize = in.readInt();
-        mCapabilitiesToEnable = new ArraySet<>(enableSize);
-        for (int i = 0; i < enableSize; i++) {
-            mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(),
-                    /*radioTech*/ in.readInt()));
-        }
-        int disableSize = in.readInt();
-        mCapabilitiesToDisable = new ArraySet<>(disableSize);
-        for (int i = 0; i < disableSize; i++) {
-            mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(),
-                    /*radioTech*/ in.readInt()));
-        }
-    }
-
-    public static final Creator<CapabilityChangeRequest> CREATOR =
-            new Creator<CapabilityChangeRequest>() {
-                @Override
-                public CapabilityChangeRequest createFromParcel(Parcel in) {
-                    return new CapabilityChangeRequest(in);
-                }
-
-                @Override
-                public CapabilityChangeRequest[] newArray(int size) {
-                    return new CapabilityChangeRequest[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mCapabilitiesToEnable.size());
-        for (CapabilityPair pair : mCapabilitiesToEnable) {
-            dest.writeInt(pair.getCapability());
-            dest.writeInt(pair.getRadioTech());
-        }
-        dest.writeInt(mCapabilitiesToDisable.size());
-        for (CapabilityPair pair : mCapabilitiesToDisable) {
-            dest.writeInt(pair.getCapability());
-            dest.writeInt(pair.getRadioTech());
-        }
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof CapabilityChangeRequest)) return false;
-
-        CapabilityChangeRequest that = (CapabilityChangeRequest) o;
-
-        if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
-        return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = mCapabilitiesToEnable.hashCode();
-        result = 31 * result + mCapabilitiesToDisable.hashCode();
-        return result;
-    }
-}
diff --git a/android/telephony/ims/internal/feature/ImsFeature.java b/android/telephony/ims/internal/feature/ImsFeature.java
deleted file mode 100644
index 9f82ad2..0000000
--- a/android/telephony/ims/internal/feature/ImsFeature.java
+++ /dev/null
@@ -1,462 +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.telephony.ims.internal.feature;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IInterface;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.util.Log;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-/**
- * Base class for all IMS features that are supported by the framework.
- *
- * @hide
- */
-public abstract class ImsFeature {
-
-    private static final String LOG_TAG = "ImsFeature";
-
-    /**
-     * Action to broadcast when ImsService is up.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_UP =
-            "com.android.ims.IMS_SERVICE_UP";
-
-    /**
-     * Action to broadcast when ImsService is down.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_DOWN =
-            "com.android.ims.IMS_SERVICE_DOWN";
-
-    /**
-     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
-     * A long value; the phone ID corresponding to the IMS service coming up or down.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PHONE_ID = "android:phone_id";
-
-    // Invalid feature value
-    public static final int FEATURE_INVALID = -1;
-    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
-    // defined values in ImsServiceClass for compatibility purposes.
-    public static final int FEATURE_EMERGENCY_MMTEL = 0;
-    public static final int FEATURE_MMTEL = 1;
-    public static final int FEATURE_RCS = 2;
-    // Total number of features defined
-    public static final int FEATURE_MAX = 3;
-
-    // Integer values defining IMS features that are supported in ImsFeature.
-    @IntDef(flag = true,
-            value = {
-                    FEATURE_EMERGENCY_MMTEL,
-                    FEATURE_MMTEL,
-                    FEATURE_RCS
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface FeatureType {}
-
-    // Integer values defining the state of the ImsFeature at any time.
-    @IntDef(flag = true,
-            value = {
-                    STATE_UNAVAILABLE,
-                    STATE_INITIALIZING,
-                    STATE_READY,
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsState {}
-
-    public static final int STATE_UNAVAILABLE = 0;
-    public static final int STATE_INITIALIZING = 1;
-    public static final int STATE_READY = 2;
-
-    // Integer values defining the result codes that should be returned from
-    // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability.
-    @IntDef(flag = true,
-            value = {
-                    CAPABILITY_ERROR_GENERIC,
-                    CAPABILITY_SUCCESS
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ImsCapabilityError {}
-
-    public static final int CAPABILITY_ERROR_GENERIC = -1;
-    public static final int CAPABILITY_SUCCESS = 0;
-
-
-    /**
-     * The framework implements this callback in order to register for Feature Capability status
-     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
-     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
-     * callbacks when the ImsService can not change the capability as requested, via
-     * {@link #onChangeCapabilityConfigurationError}.
-     */
-    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
-        @Override
-        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
-            onCapabilitiesStatusChanged(new Capabilities(config));
-        }
-
-        /**
-         * Returns the result of a query for the capability configuration of a requested capability.
-         *
-         * @param capability The capability that was requested.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param isEnabled true if the capability is enabled, false otherwise.
-         */
-        @Override
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-
-        }
-
-        /**
-         * Called when a change to the capability configuration has returned an error.
-         *
-         * @param capability The capability that was requested to be changed.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param reason error associated with the failure to change configuration.
-         */
-        @Override
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                int reason) {
-        }
-
-        /**
-         * The status of the feature's capabilities has changed to either available or unavailable.
-         * If unavailable, the feature is not able to support the unavailable capability at this
-         * time.
-         *
-         * @param config The new availability of the capabilities.
-         */
-        public void onCapabilitiesStatusChanged(Capabilities config) {
-        }
-    }
-
-    /**
-     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
-     * provided.
-     */
-    protected static class CapabilityCallbackProxy {
-        private final IImsCapabilityCallback mCallback;
-
-        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
-            mCallback = c;
-        }
-
-        /**
-         * This method notifies the provided framework callback that the request to change the
-         * indicated capability has failed and has not changed.
-         *
-         * @param capability The Capability that will be notified to the framework.
-         * @param radioTech The radio tech that this capability failed for.
-         * @param reason The reason this capability was unable to be changed.
-         */
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                @ImsCapabilityError int reason) {
-            try {
-                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
-            }
-        }
-
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-            try {
-                mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder.");
-            }
-        }
-    }
-
-    /**
-     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
-     */
-    public static class Capabilities {
-        protected int mCapabilities = 0;
-
-        public Capabilities() {
-        }
-
-        protected Capabilities(int capabilities) {
-            mCapabilities = capabilities;
-        }
-
-        /**
-         * @param capabilities Capabilities to be added to the configuration in the form of a
-         *     bit mask.
-         */
-        public void addCapabilities(int capabilities) {
-            mCapabilities |= capabilities;
-        }
-
-        /**
-         * @param capabilities Capabilities to be removed to the configuration in the form of a
-         *     bit mask.
-         */
-        public void removeCapabilities(int capabilities) {
-            mCapabilities &= ~capabilities;
-        }
-
-        /**
-         * @return true if all of the capabilities specified are capable.
-         */
-        public boolean isCapable(int capabilities) {
-            return (mCapabilities & capabilities) == capabilities;
-        }
-
-        public Capabilities copy() {
-            return new Capabilities(mCapabilities);
-        }
-
-        /**
-         * @return a bitmask containing the capability flags directly.
-         */
-        public int getMask() {
-            return mCapabilities;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof Capabilities)) return false;
-
-            Capabilities that = (Capabilities) o;
-
-            return mCapabilities == that.mCapabilities;
-        }
-
-        @Override
-        public int hashCode() {
-            return mCapabilities;
-        }
-    }
-
-    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
-            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
-    private @ImsState int mState = STATE_UNAVAILABLE;
-    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-    private Context mContext;
-    private final Object mLock = new Object();
-    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
-            = new RemoteCallbackList<>();
-    private Capabilities mCapabilityStatus = new Capabilities();
-
-    public final void initialize(Context context, int slotId) {
-        mContext = context;
-        mSlotId = slotId;
-    }
-
-    public final int getFeatureState() {
-        synchronized (mLock) {
-            return mState;
-        }
-    }
-
-    protected final void setFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            if (mState != state) {
-                mState = state;
-                notifyFeatureState(state);
-            }
-        }
-    }
-
-    // Not final for testing, but shouldn't be extended!
-    @VisibleForTesting
-    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        try {
-            // If we have just connected, send queued status.
-            c.notifyImsFeatureStatus(getFeatureState());
-            // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mLock) {
-                mStatusCallbacks.add(c);
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-        }
-    }
-
-    @VisibleForTesting
-    // Not final for testing, but should not be extended!
-    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        synchronized (mLock) {
-            mStatusCallbacks.remove(c);
-        }
-    }
-
-    /**
-     * Internal method called by ImsFeature when setFeatureState has changed.
-     */
-    private void notifyFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                    iter.hasNext(); ) {
-                IImsFeatureStatusCallback callback = iter.next();
-                try {
-                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
-                    callback.notifyImsFeatureStatus(state);
-                } catch (RemoteException e) {
-                    // remove if the callback is no longer alive.
-                    iter.remove();
-                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-                }
-            }
-        }
-        sendImsServiceIntent(state);
-    }
-
-    /**
-     * Provide backwards compatibility using deprecated service UP/DOWN intents.
-     */
-    private void sendImsServiceIntent(@ImsState int state) {
-        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-            return;
-        }
-        Intent intent;
-        switch (state) {
-            case ImsFeature.STATE_UNAVAILABLE:
-            case ImsFeature.STATE_INITIALIZING:
-                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
-                break;
-            case ImsFeature.STATE_READY:
-                intent = new Intent(ACTION_IMS_SERVICE_UP);
-                break;
-            default:
-                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
-        }
-        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
-        mContext.sendBroadcast(intent);
-    }
-
-    public final void addCapabilityCallback(IImsCapabilityCallback c) {
-        mCapabilityCallbacks.register(c);
-    }
-
-    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
-        mCapabilityCallbacks.unregister(c);
-    }
-
-    /**
-     * @return the cached capabilities status for this feature.
-     */
-    @VisibleForTesting
-    public Capabilities queryCapabilityStatus() {
-        synchronized (mLock) {
-            return mCapabilityStatus.copy();
-        }
-    }
-
-    // Called internally to request the change of enabled capabilities.
-    @VisibleForTesting
-    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
-            IImsCapabilityCallback c) throws RemoteException {
-        if (request == null) {
-            throw new IllegalArgumentException(
-                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
-        }
-        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
-    }
-
-    /**
-     * Called by the ImsFeature when the capabilities status has changed.
-     *
-     * @param c A {@link Capabilities} containing the new Capabilities status.
-     */
-    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
-        synchronized (mLock) {
-            mCapabilityStatus = c.copy();
-        }
-        int count = mCapabilityCallbacks.beginBroadcast();
-        try {
-            for (int i = 0; i < count; i++) {
-                try {
-                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
-                            c.mCapabilities);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
-                            "callback.");
-                }
-            }
-        } finally {
-            mCapabilityCallbacks.finishBroadcast();
-        }
-    }
-
-    /**
-     * Features should override this method to receive Capability preference change requests from
-     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
-     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
-     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
-     * each failed capability.
-     *
-     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
-     *     enable/disable.
-     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
-     * setting a subset of these capabilities fail, using
-     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
-     */
-    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c);
-
-    /**
-     * Called when the framework is removing this feature and it needs to be cleaned up.
-     */
-    public abstract void onFeatureRemoved();
-
-    /**
-     * Called when the feature has been initialized and communication with the framework is set up.
-     * Any attempt by this feature to access the framework before this method is called will return
-     * with an {@link IllegalStateException}.
-     * The IMS provider should use this method to trigger registration for this feature on the IMS
-     * network, if needed.
-     */
-    public abstract void onFeatureReady();
-
-    /**
-     * @return Binder instance that the framework will use to communicate with this feature.
-     */
-    protected abstract IInterface getBinder();
-}
diff --git a/android/telephony/ims/internal/feature/MmTelFeature.java b/android/telephony/ims/internal/feature/MmTelFeature.java
deleted file mode 100644
index 9b576c7..0000000
--- a/android/telephony/ims/internal/feature/MmTelFeature.java
+++ /dev/null
@@ -1,437 +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.telephony.ims.internal.feature;
-
-import android.annotation.IntDef;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telecom.TelecomManager;
-import android.telephony.ims.internal.ImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCallSessionListener;
-import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
-import android.telephony.ims.internal.aidl.IImsMmTelFeature;
-import android.telephony.ims.internal.aidl.IImsMmTelListener;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.ims.stub.ImsEcbmImplBase;
-import android.telephony.ims.stub.ImsMultiEndpointImplBase;
-import android.telephony.ims.stub.ImsUtImplBase;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.ImsCallSession;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
- *
- * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
- * service supports.
- * @hide
- */
-
-public class MmTelFeature extends ImsFeature {
-
-    private static final String LOG_TAG = "MmTelFeature";
-
-    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
-
-        @Override
-        public void setListener(IImsMmTelListener l) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setListener(l);
-            }
-        }
-
-        @Override
-        public int getFeatureState() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getFeatureState();
-            }
-        }
-
-
-        @Override
-        public ImsCallProfile createCallProfile(int callSessionType, int callType)
-                throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.createCallProfile(callSessionType,  callType);
-            }
-        }
-
-        @Override
-        public IImsCallSession createCallSession(ImsCallProfile profile,
-                IImsCallSessionListener listener) throws RemoteException {
-            synchronized (mLock) {
-                ImsCallSession s = MmTelFeature.this.createCallSession(profile,
-                        new ImsCallSessionListener(listener));
-                return s != null ? s.getSession() : null;
-            }
-        }
-
-        @Override
-        public IImsUt getUtInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getUt();
-            }
-        }
-
-        @Override
-        public IImsEcbm getEcbmInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getEcbm();
-            }
-        }
-
-        @Override
-        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
-            synchronized (mLock) {
-                MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
-            }
-        }
-
-        @Override
-        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-            synchronized (mLock) {
-                return MmTelFeature.this.getMultiEndpoint();
-            }
-        }
-
-        @Override
-        public int queryCapabilityStatus() throws RemoteException {
-            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
-        }
-
-        @Override
-        public void addCapabilityCallback(IImsCapabilityCallback c) {
-            MmTelFeature.this.addCapabilityCallback(c);
-        }
-
-        @Override
-        public void removeCapabilityCallback(IImsCapabilityCallback c) {
-            MmTelFeature.this.removeCapabilityCallback(c);
-        }
-
-        @Override
-        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
-                IImsCapabilityCallback c) throws RemoteException {
-            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
-        }
-
-        @Override
-        public void queryCapabilityConfiguration(int capability, int radioTech,
-                IImsCapabilityCallback c) {
-            queryCapabilityConfigurationInternal(capability, radioTech, c);
-        }
-    };
-
-    /**
-     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
-     * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}.
-     *
-     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
-     * {@link #queryCapabilityStatus()}.
-     *
-     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
-     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
-     * status can also be queried using {@link #queryCapabilityStatus()}.
-     */
-    public static class MmTelCapabilities extends Capabilities {
-
-        @VisibleForTesting
-        public MmTelCapabilities() {
-            super();
-        }
-
-        public MmTelCapabilities(Capabilities c) {
-            mCapabilities = c.mCapabilities;
-        }
-
-        @IntDef(flag = true,
-                value = {
-                        CAPABILITY_TYPE_VOICE,
-                        CAPABILITY_TYPE_VIDEO,
-                        CAPABILITY_TYPE_UT,
-                        CAPABILITY_TYPE_SMS
-                })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface MmTelCapability {}
-
-        /**
-         * This MmTelFeature supports Voice calling (IR.92)
-         */
-        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
-
-        /**
-         * This MmTelFeature supports Video (IR.94)
-         */
-        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
-
-        /**
-         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
-         */
-        public static final int CAPABILITY_TYPE_UT = 1 << 2;
-
-        /**
-         * This MmTelFeature supports SMS (IR.92)
-         */
-        public static final int CAPABILITY_TYPE_SMS = 1 << 3;
-
-        @Override
-        public final void addCapabilities(@MmTelCapability int capabilities) {
-            super.addCapabilities(capabilities);
-        }
-
-        @Override
-        public final void removeCapabilities(@MmTelCapability int capability) {
-            super.removeCapabilities(capability);
-        }
-
-        @Override
-        public final boolean isCapable(@MmTelCapability int capabilities) {
-            return super.isCapable(capabilities);
-        }
-    }
-
-    /**
-     * Listener that the framework implements for communication from the MmTelFeature.
-     */
-    public static class Listener extends IImsMmTelListener.Stub {
-
-        @Override
-        public final void onIncomingCall(IImsCallSession c) {
-            onIncomingCall(new ImsCallSession(c));
-        }
-
-        /**
-         * Updates the Listener when the voice message count for IMS has changed.
-         * @param count an integer representing the new message count.
-         */
-        @Override
-        public void onVoiceMessageCountUpdate(int count) {
-
-        }
-
-        /**
-         * Called when the IMS provider receives an incoming call.
-         * @param c The {@link ImsCallSession} associated with the new call.
-         */
-        public void onIncomingCall(ImsCallSession c) {
-        }
-    }
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
-    private IImsMmTelListener mListener;
-
-    /**
-     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
-     *     notifies the framework.
-     */
-    private void setListener(IImsMmTelListener listener) {
-        synchronized (mLock) {
-            mListener = listener;
-        }
-    }
-
-    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
-            IImsCapabilityCallback c) {
-        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
-        try {
-            if (c != null) {
-                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
-            }
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
-        }
-    }
-
-    /**
-     * The current capability status that this MmTelFeature has defined is available. This
-     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
-     * available to be used.
-     *
-     * Should be a subset of the capabilities that are enabled by the framework in
-     * {@link #changeEnabledCapabilities}.
-     * @return A copy of the current MmTelFeature capability status.
-     */
-    @Override
-    public final MmTelCapabilities queryCapabilityStatus() {
-        return new MmTelCapabilities(super.queryCapabilityStatus());
-    }
-
-    /**
-     * Notify the framework that the status of the Capabilities has changed. Even though the
-     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
-     * the feature being unavailable from the network.
-     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
-     * the status of that capability is disabled. This can happen if the network does not currently
-     * support the capability that is enabled. A capability that is disabled by the framework (via
-     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
-     */
-    protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
-        super.notifyCapabilitiesStatusChanged(c);
-    }
-
-    /**
-     * Notify the framework of an incoming call.
-     * @param c The {@link ImsCallSession} of the new incoming call.
-     *
-     * @throws RemoteException if the connection to the framework is not available. If this happens,
-     *     the call should be no longer considered active and should be cleaned up.
-     * */
-    protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Session is not available.");
-            }
-            mListener.onIncomingCall(c.getSession());
-        }
-    }
-
-    /**
-     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
-     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
-     * includes a capability A to enable or disable, this method should return the correct enabled
-     * status for capability A.
-     * @param capability The capability that we are querying the configuration for.
-     * @return true if the capability is enabled, false otherwise.
-     */
-    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
-            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
-        // Base implementation - Override to provide functionality
-        return false;
-    }
-
-    /**
-     * The MmTelFeature should override this method to handle the enabling/disabling of
-     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
-     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
-     * could not be set to their new values,
-     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
-     * individually for each capability whose processing resulted in an error.
-     *
-     * Enabling/Disabling a capability here indicates that the capability should be registered or
-     * deregistered (depending on the capability change) and become available or unavailable to
-     * the framework.
-     */
-    @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
-        // Base implementation, no-op
-    }
-
-    /**
-     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
-     *
-     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
-     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
-     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
-     * @param callType a call type that is specified in {@link ImsCallProfile}
-     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
-     *        {@link ImsCallProfile#CALL_TYPE_VT}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
-     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
-     *        {@link ImsCallProfile#CALL_TYPE_VS}
-     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
-     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
-     * @return a {@link ImsCallProfile} object
-     */
-    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * Creates an {@link ImsCallSession} with the specified call profile.
-     * Use other methods, if applicable, instead of interacting with
-     * {@link ImsCallSession} directly.
-     *
-     * @param profile a call profile to make the call
-     * @param listener An implementation of IImsCallSessionListener.
-     */
-    public ImsCallSession createCallSession(ImsCallProfile profile,
-            ImsCallSessionListener listener) {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Ut interface for the supplementary service configuration.
-     */
-    public ImsUtImplBase getUt() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public ImsEcbmImplBase getEcbm() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
-     */
-    public ImsMultiEndpointImplBase getMultiEndpoint() {
-        // Base Implementation - Should be overridden
-        return null;
-    }
-
-    /**
-     * Sets the current UI TTY mode for the MmTelFeature.
-     * @param mode An integer containing the new UI TTY Mode, can consist of
-     *         {@link TelecomManager#TTY_MODE_OFF},
-     *         {@link TelecomManager#TTY_MODE_FULL},
-     *         {@link TelecomManager#TTY_MODE_HCO},
-     *         {@link TelecomManager#TTY_MODE_VCO}
-     * @param onCompleteMessage A {@link Message} to be used when the mode has been set.
-     */
-    void setUiTtyMode(int mode, Message onCompleteMessage) {
-        // Base Implementation - Should be overridden
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureRemoved() {
-        // Base Implementation - Should be overridden
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureReady() {
-        // Base Implementation - Should be overridden
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public final IImsMmTelFeature getBinder() {
-        return mImsMMTelBinder;
-    }
-}
diff --git a/android/telephony/ims/internal/feature/RcsFeature.java b/android/telephony/ims/internal/feature/RcsFeature.java
deleted file mode 100644
index 8d1bd9d..0000000
--- a/android/telephony/ims/internal/feature/RcsFeature.java
+++ /dev/null
@@ -1,59 +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.telephony.ims.internal.feature;
-
-import android.telephony.ims.internal.aidl.IImsRcsFeature;
-
-/**
- * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
- * this class and provide implementations of the RcsFeature methods that they support.
- * @hide
- */
-
-public class RcsFeature extends ImsFeature {
-
-    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
-        // Empty Default Implementation.
-    };
-
-
-    public RcsFeature() {
-        super();
-    }
-
-    @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
-        // Do nothing for base implementation.
-    }
-
-    @Override
-    public void onFeatureRemoved() {
-
-    }
-
-    /**{@inheritDoc}*/
-    @Override
-    public void onFeatureReady() {
-
-    }
-
-    @Override
-    public final IImsRcsFeature getBinder() {
-        return mImsRcsBinder;
-    }
-}
diff --git a/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/android/telephony/ims/internal/stub/ImsConfigImplBase.java
deleted file mode 100644
index 33aec5d..0000000
--- a/android/telephony/ims/internal/stub/ImsConfigImplBase.java
+++ /dev/null
@@ -1,173 +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.telephony.ims.internal.stub;
-
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.telephony.ims.internal.aidl.IImsConfig;
-import android.telephony.ims.internal.aidl.IImsConfigCallback;
-
-import com.android.ims.ImsConfig;
-
-/**
- * Controls the modification of IMS specific configurations. For more information on the supported
- * IMS configuration constants, see {@link ImsConfig}.
- *
- * @hide
- */
-
-public class ImsConfigImplBase {
-
-    //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config
-    // work first.
-    private final IImsConfig mBinder = new IImsConfig.Stub() {
-
-        @Override
-        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
-            ImsConfigImplBase.this.addImsConfigCallback(c);
-        }
-
-        @Override
-        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
-            ImsConfigImplBase.this.removeImsConfigCallback(c);
-        }
-
-        @Override
-        public int getConfigInt(int item) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-
-        @Override
-        public String getConfigString(int item) throws RemoteException {
-            return null;
-        }
-
-        @Override
-        public int setConfigInt(int item, int value) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-
-        @Override
-        public int setConfigString(int item, String value) throws RemoteException {
-            return Integer.MIN_VALUE;
-        }
-    };
-
-    public class Callback extends IImsConfigCallback.Stub {
-
-        @Override
-        public final void onIntConfigChanged(int item, int value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        @Override
-        public final void onStringConfigChanged(int item, String value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new integer value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, int value) {
-            // Base Implementation
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new String value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, String value) {
-            // Base Implementation
-        }
-    }
-
-    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
-
-    /**
-     * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
-     * changes.
-     * @param c callback to add.
-     */
-    private void addImsConfigCallback(IImsConfigCallback c) {
-        mCallbacks.register(c);
-    }
-    /**
-     * Removes a {@link Callback} to the list of callbacks notified when a value in the
-     * configuration changes.
-     *
-     * @param c callback to remove.
-     */
-    private void removeImsConfigCallback(IImsConfigCallback c) {
-        mCallbacks.unregister(c);
-    }
-
-    public final IImsConfig getBinder() {
-        return mBinder;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in Integer format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setConfig(int item, int value) {
-        // Base Implementation - To be overridden.
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setConfig(int item, String value) {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in Integer format.
-     */
-    public int getConfigInt(int item) {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in String format.
-     */
-    public String getConfigString(int item) {
-        return null;
-    }
-}
diff --git a/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
deleted file mode 100644
index 244c957..0000000
--- a/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
+++ /dev/null
@@ -1,147 +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.telephony.ims.internal.stub;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.ims.internal.feature.ImsFeature;
-import android.util.ArraySet;
-
-import java.util.Arrays;
-import java.util.Set;
-
-/**
- * Container class for IMS Feature configuration. This class contains the features that the
- * ImsService supports, which are defined in {@link ImsFeature.FeatureType}.
- * @hide
- */
-public class ImsFeatureConfiguration implements Parcelable {
-    /**
-     * Features that this ImsService supports.
-     */
-    private final Set<Integer> mFeatures;
-
-    /**
-     * Creates an ImsFeatureConfiguration with the features
-     */
-    public static class Builder {
-            ImsFeatureConfiguration mConfig;
-        public Builder() {
-            mConfig = new ImsFeatureConfiguration();
-        }
-
-        /**
-         * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service
-         *         supports.
-         * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
-         */
-        public Builder addFeature(@ImsFeature.FeatureType int feature) {
-            mConfig.addFeature(feature);
-            return this;
-        }
-
-        public ImsFeatureConfiguration build() {
-            return mConfig;
-        }
-    }
-
-    /**
-     * Creates with all registration features empty.
-     *
-     * Consider using the provided {@link Builder} to create this configuration instead.
-     */
-    public ImsFeatureConfiguration() {
-        mFeatures = new ArraySet<>();
-    }
-
-    /**
-     * Configuration of the ImsService, which describes which features the ImsService supports
-     * (for registration).
-     * @param features an array of feature integers defined in {@link ImsFeature} that describe
-     * which features this ImsService supports.
-     */
-    public ImsFeatureConfiguration(int[] features) {
-        mFeatures = new ArraySet<>();
-
-        if (features != null) {
-            for (int i : features) {
-                mFeatures.add(i);
-            }
-        }
-    }
-
-    /**
-     * @return an int[] containing the features that this ImsService supports.
-     */
-    public int[] getServiceFeatures() {
-        return mFeatures.stream().mapToInt(i->i).toArray();
-    }
-
-    void addFeature(int feature) {
-        mFeatures.add(feature);
-    }
-
-    protected ImsFeatureConfiguration(Parcel in) {
-        int[] features = in.createIntArray();
-        if (features != null) {
-            mFeatures = new ArraySet<>(features.length);
-            for(Integer i : features) {
-                mFeatures.add(i);
-            }
-        } else {
-            mFeatures = new ArraySet<>();
-        }
-    }
-
-    public static final Creator<ImsFeatureConfiguration> CREATOR
-            = new Creator<ImsFeatureConfiguration>() {
-        @Override
-        public ImsFeatureConfiguration createFromParcel(Parcel in) {
-            return new ImsFeatureConfiguration(in);
-        }
-
-        @Override
-        public ImsFeatureConfiguration[] newArray(int size) {
-            return new ImsFeatureConfiguration[size];
-        }
-    };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof ImsFeatureConfiguration)) return false;
-
-        ImsFeatureConfiguration that = (ImsFeatureConfiguration) o;
-
-        return mFeatures.equals(that.mFeatures);
-    }
-
-    @Override
-    public int hashCode() {
-        return mFeatures.hashCode();
-    }
-}
diff --git a/android/telephony/ims/internal/stub/SmsImplBase.java b/android/telephony/ims/internal/stub/SmsImplBase.java
deleted file mode 100644
index 113dad4..0000000
--- a/android/telephony/ims/internal/stub/SmsImplBase.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.internal.stub;
-
-import android.annotation.IntDef;
-import android.os.RemoteException;
-import android.telephony.SmsManager;
-import android.telephony.SmsMessage;
-import android.telephony.ims.internal.feature.MmTelFeature;
-import android.util.Log;
-
-import com.android.ims.internal.IImsSmsListener;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Base implementation for SMS over IMS.
- *
- * Any service wishing to provide SMS over IMS should extend this class and implement all methods
- * that the service supports.
- * @hide
- */
-public class SmsImplBase {
-    private static final String LOG_TAG = "SmsImplBase";
-
-    @IntDef({
-            SEND_STATUS_OK,
-            SEND_STATUS_ERROR,
-            SEND_STATUS_ERROR_RETRY,
-            SEND_STATUS_ERROR_FALLBACK
-        })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SendStatusResult {}
-    /**
-     * Message was sent successfully.
-     */
-    public static final int SEND_STATUS_OK = 1;
-
-    /**
-     * IMS provider failed to send the message and platform should not retry falling back to sending
-     * the message using the radio.
-     */
-    public static final int SEND_STATUS_ERROR = 2;
-
-    /**
-     * IMS provider failed to send the message and platform should retry again after setting TP-RD bit
-     * to high.
-     */
-    public static final int SEND_STATUS_ERROR_RETRY = 3;
-
-    /**
-     * IMS provider failed to send the message and platform should retry falling back to sending
-     * the message using the radio.
-     */
-    public static final int SEND_STATUS_ERROR_FALLBACK = 4;
-
-    @IntDef({
-            DELIVER_STATUS_OK,
-            DELIVER_STATUS_ERROR
-        })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DeliverStatusResult {}
-    /**
-     * Message was delivered successfully.
-     */
-    public static final int DELIVER_STATUS_OK = 1;
-
-    /**
-     * Message was not delivered.
-     */
-    public static final int DELIVER_STATUS_ERROR = 2;
-
-    @IntDef({
-            STATUS_REPORT_STATUS_OK,
-            STATUS_REPORT_STATUS_ERROR
-        })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StatusReportResult {}
-
-    /**
-     * Status Report was set successfully.
-     */
-    public static final int STATUS_REPORT_STATUS_OK = 1;
-
-    /**
-     * Error while setting status report.
-     */
-    public static final int STATUS_REPORT_STATUS_ERROR = 2;
-
-
-    // Lock for feature synchronization
-    private final Object mLock = new Object();
-    private IImsSmsListener mListener;
-
-    /**
-     * Registers a listener responsible for handling tasks like delivering messages.
-     *
-     * @param listener listener to register.
-     *
-     * @hide
-     */
-    public final void registerSmsListener(IImsSmsListener listener) {
-        synchronized (mLock) {
-            mListener = listener;
-        }
-    }
-
-    /**
-     * This method will be triggered by the platform when the user attempts to send an SMS. This
-     * method should be implemented by the IMS providers to provide implementation of sending an SMS
-     * over IMS.
-     *
-     * @param token unique token generated by the platform that should be used when triggering
-     *             callbacks for this specific message.
-     * @param messageRef the message reference.
-     * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
-     *               {@link SmsMessage#FORMAT_3GPP2}.
-     * @param smsc the Short Message Service Center address.
-     * @param isRetry whether it is a retry of an already attempted message or not.
-     * @param pdu PDUs representing the contents of the message.
-     */
-    public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
-            byte[] pdu) {
-        // Base implementation returns error. Should be overridden.
-        try {
-            onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
-                    SmsManager.RESULT_ERROR_GENERIC_FAILURE);
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
-        }
-    }
-
-    /**
-     * This method will be triggered by the platform after {@link #onSmsReceived(int, String, byte[])}
-     * has been called to deliver the result to the IMS provider.
-     *
-     * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
-     * @param result result of delivering the message. Valid values are defined in
-     * {@link DeliverStatusResult}
-     * @param messageRef the message reference
-     */
-    public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
-        Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
-    }
-
-    /**
-     * This method will be triggered by the platform after
-     * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
-     * result to the IMS provider.
-     *
-     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
-     * @param result result of delivering the message. Valid values are defined in
-     * {@link StatusReportResult}
-     * @param messageRef the message reference
-     */
-    public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
-        Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
-    }
-
-    /**
-     * This method should be triggered by the IMS providers when there is an incoming message. The
-     * platform will deliver the message to the messages database and notify the IMS provider of the
-     * result by calling {@link #acknowledgeSms(int, int, int)}.
-     *
-     * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
-     *
-     * @param token unique token generated by IMS providers that the platform will use to trigger
-     *              callbacks for this message.
-     * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
-     * {@link SmsMessage#FORMAT_3GPP2}.
-     * @param pdu PDUs representing the contents of the message.
-     * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
-     */
-    public final void onSmsReceived(int token, String format, byte[] pdu)
-            throws IllegalStateException {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Feature not ready.");
-            }
-            try {
-                mListener.onSmsReceived(token, format, pdu);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
-                acknowledgeSms(token, 0, DELIVER_STATUS_ERROR);
-            }
-        }
-    }
-
-    /**
-     * This method should be triggered by the IMS providers to pass the result of the sent message
-     * to the platform.
-     *
-     * This method must not be called before {@link MmTelFeature#onFeatureReady()} is called.
-     *
-     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
-     * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
-     * @param status result of sending the SMS. Valid values are defined in {@link SendStatusResult}
-     * @param reason reason in case status is failure. Valid values are:
-     *  {@link SmsManager#RESULT_ERROR_NONE},
-     *  {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
-     *  {@link SmsManager#RESULT_ERROR_RADIO_OFF},
-     *  {@link SmsManager#RESULT_ERROR_NULL_PDU},
-     *  {@link SmsManager#RESULT_ERROR_NO_SERVICE},
-     *  {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
-     *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
-     *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED}
-     * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
-     * @throws RemoteException if the connection to the framework is not available. If this happens
-     *  attempting to send the SMS should be aborted.
-     */
-    public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
-            int reason) throws IllegalStateException, RemoteException {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Feature not ready.");
-            }
-            mListener.onSendSmsResult(token, messageRef, status, reason);
-        }
-    }
-
-    /**
-     * Sets the status report of the sent message.
-     *
-     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
-     * @param messageRef the message reference.
-     * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
-     * {@link SmsMessage#FORMAT_3GPP2}.
-     * @param pdu PDUs representing the content of the status report.
-     * @throws IllegalStateException if called before {@link MmTelFeature#onFeatureReady()}
-     */
-    public final void onSmsStatusReportReceived(int token, int messageRef, String format,
-            byte[] pdu) {
-        synchronized (mLock) {
-            if (mListener == null) {
-                throw new IllegalStateException("Feature not ready.");
-            }
-            try {
-                mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
-            } catch (RemoteException e) {
-                Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
-                acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
-            }
-        }
-    }
-
-    /**
-     * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
-     * Provider.
-     *
-     * @return  the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
-     * {@link SmsMessage#FORMAT_3GPP2}.
-     */
-    public String getSmsFormat() {
-      return SmsMessage.FORMAT_3GPP;
-    }
-}
diff --git a/android/telephony/ims/stub/ImsCallSessionImplBase.java b/android/telephony/ims/stub/ImsCallSessionImplBase.java
index 80b2f78..7b9fe2b 100644
--- a/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,105 +16,264 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Message;
 import android.os.RemoteException;
+import android.telephony.ims.ImsCallSessionListener;
+import android.telephony.ims.aidl.IImsCallSessionListener;
 
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.internal.ImsCallSession;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.ImsCallSession;
 import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
 import com.android.ims.internal.IImsVideoCallProvider;
+import android.telephony.ims.ImsVideoCallProvider;
+
+import dalvik.system.CloseGuard;
 
 /**
- * Base implementation of IImsCallSession, which implements stub versions of the methods in the
- * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ * Base implementation of IImsCallSession, which implements stub versions of the methods available.
  *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSession maintained by other ImsServices.
+ * Override the methods that your implementation of ImsCallSession supports.
  *
  * @hide
  */
-
-public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+@SystemApi
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsCallSession maintained by other ImsServices.
+public class ImsCallSessionImplBase implements AutoCloseable {
+    /**
+     * Notify USSD Mode.
+     */
+    public static final int USSD_MODE_NOTIFY = 0;
+    /**
+     * Request USSD Mode
+     */
+    public static final int USSD_MODE_REQUEST = 1;
 
     /**
-     * Closes the object. This object is not usable after being closed.
+     * Defines IMS call session state.
      */
-    @Override
-    public void close() throws RemoteException {
+    public static class State {
+        public static final int IDLE = 0;
+        public static final int INITIATED = 1;
+        public static final int NEGOTIATING = 2;
+        public static final int ESTABLISHING = 3;
+        public static final int ESTABLISHED = 4;
 
+        public static final int RENEGOTIATING = 5;
+        public static final int REESTABLISHING = 6;
+
+        public static final int TERMINATING = 7;
+        public static final int TERMINATED = 8;
+
+        public static final int INVALID = (-1);
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case IDLE:
+                    return "IDLE";
+                case INITIATED:
+                    return "INITIATED";
+                case NEGOTIATING:
+                    return "NEGOTIATING";
+                case ESTABLISHING:
+                    return "ESTABLISHING";
+                case ESTABLISHED:
+                    return "ESTABLISHED";
+                case RENEGOTIATING:
+                    return "RENEGOTIATING";
+                case REESTABLISHING:
+                    return "REESTABLISHING";
+                case TERMINATING:
+                    return "TERMINATING";
+                case TERMINATED:
+                    return "TERMINATED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        /**
+         * @hide
+         */
+        private State() {
+        }
     }
 
-    /**
-     * Gets the call ID of the session.
-     *
-     * @return the call ID
-     */
-    @Override
-    public String getCallId() throws RemoteException {
-        return null;
-    }
+    // Non-final for injection by tests
+    private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
+        @Override
+        public void close() {
+            ImsCallSessionImplBase.this.close();
+        }
+
+        @Override
+        public String getCallId() {
+            return ImsCallSessionImplBase.this.getCallId();
+        }
+
+        @Override
+        public ImsCallProfile getCallProfile() {
+            return ImsCallSessionImplBase.this.getCallProfile();
+        }
+
+        @Override
+        public ImsCallProfile getLocalCallProfile() {
+            return ImsCallSessionImplBase.this.getLocalCallProfile();
+        }
+
+        @Override
+        public ImsCallProfile getRemoteCallProfile() {
+            return ImsCallSessionImplBase.this.getRemoteCallProfile();
+        }
+
+        @Override
+        public String getProperty(String name) {
+            return ImsCallSessionImplBase.this.getProperty(name);
+        }
+
+        @Override
+        public int getState() {
+            return ImsCallSessionImplBase.this.getState();
+        }
+
+        @Override
+        public boolean isInCall() {
+            return ImsCallSessionImplBase.this.isInCall();
+        }
+
+        @Override
+        public void setListener(IImsCallSessionListener listener) {
+            ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+        }
+
+        @Override
+        public void setMute(boolean muted) {
+            ImsCallSessionImplBase.this.setMute(muted);
+        }
+
+        @Override
+        public void start(String callee, ImsCallProfile profile) {
+            ImsCallSessionImplBase.this.start(callee, profile);
+        }
+
+        @Override
+        public void startConference(String[] participants, ImsCallProfile profile) throws
+                RemoteException {
+            ImsCallSessionImplBase.this.startConference(participants, profile);
+        }
+
+        @Override
+        public void accept(int callType, ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.accept(callType, profile);
+        }
+
+        @Override
+        public void deflect(String deflectNumber) {
+            ImsCallSessionImplBase.this.deflect(deflectNumber);
+        }
+
+        @Override
+        public void reject(int reason) {
+            ImsCallSessionImplBase.this.reject(reason);
+        }
+
+        @Override
+        public void terminate(int reason) {
+            ImsCallSessionImplBase.this.terminate(reason);
+        }
+
+        @Override
+        public void hold(ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.hold(profile);
+        }
+
+        @Override
+        public void resume(ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.resume(profile);
+        }
+
+        @Override
+        public void merge() {
+            ImsCallSessionImplBase.this.merge();
+        }
+
+        @Override
+        public void update(int callType, ImsStreamMediaProfile profile) {
+            ImsCallSessionImplBase.this.update(callType, profile);
+        }
+
+        @Override
+        public void extendToConference(String[] participants) {
+            ImsCallSessionImplBase.this.extendToConference(participants);
+        }
+
+        @Override
+        public void inviteParticipants(String[] participants) {
+            ImsCallSessionImplBase.this.inviteParticipants(participants);
+        }
+
+        @Override
+        public void removeParticipants(String[] participants) {
+            ImsCallSessionImplBase.this.removeParticipants(participants);
+        }
+
+        @Override
+        public void sendDtmf(char c, Message result) {
+            ImsCallSessionImplBase.this.sendDtmf(c, result);
+        }
+
+        @Override
+        public void startDtmf(char c) {
+            ImsCallSessionImplBase.this.startDtmf(c);
+        }
+
+        @Override
+        public void stopDtmf() {
+            ImsCallSessionImplBase.this.stopDtmf();
+        }
+
+        @Override
+        public void sendUssd(String ussdMessage) {
+            ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+        }
+
+        @Override
+        public IImsVideoCallProvider getVideoCallProvider() {
+            return ImsCallSessionImplBase.this.getVideoCallProvider();
+        }
+
+        @Override
+        public boolean isMultiparty() {
+            return ImsCallSessionImplBase.this.isMultiparty();
+        }
+
+        @Override
+        public void sendRttModifyRequest(ImsCallProfile toProfile) {
+            ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+        }
+
+        @Override
+        public void sendRttModifyResponse(boolean status) {
+            ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+        }
+
+        @Override
+        public void sendRttMessage(String rttMessage) {
+            ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+        }
+    };
 
     /**
-     * Gets the call profile that this session is associated with
-     *
-     * @return the {@link ImsCallProfile} that this session is associated with
+     * @hide
      */
-    @Override
-    public ImsCallProfile getCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the local call profile that this session is associated with
-     *
-     * @return the local {@link ImsCallProfile} that this session is associated with
-     */
-    @Override
-    public ImsCallProfile getLocalCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the remote call profile that this session is associated with
-     *
-     * @return the remote {@link ImsCallProfile} that this session is associated with
-     */
-    @Override
-    public ImsCallProfile getRemoteCallProfile() throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the value associated with the specified property of this session.
-     *
-     * @return the string value associated with the specified property
-     */
-    @Override
-    public String getProperty(String name) throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Gets the session state.
-     * The value returned must be one of the states in {@link ImsCallSession.State}.
-     *
-     * @return the session state
-     */
-    @Override
-    public int getState() throws RemoteException {
-        return ImsCallSession.State.INVALID;
-    }
-
-    /**
-     * Checks if the session is in call.
-     *
-     * @return true if the session is in call, false otherwise
-     */
-    @Override
-    public boolean isInCall() throws RemoteException {
-        return false;
+    public final void setListener(IImsCallSessionListener listener) throws RemoteException {
+        setListener(new ImsCallSessionListener(listener));
     }
 
     /**
@@ -122,25 +281,87 @@
      * can only hold one listener at a time. Subsequent calls to this method
      * override the previous listener.
      *
-     * @param listener to listen to the session events of this object
+     * @param listener {@link ImsCallSessionListener} used to notify the framework of updates
+     * to the ImsCallSession
+     */
+    public void setListener(ImsCallSessionListener listener) {
+    }
+
+    /**
+     * Closes the object. This {@link ImsCallSessionImplBase} is not usable after being closed.
      */
     @Override
-    public void setListener(IImsCallSessionListener listener) throws RemoteException {
+    public void close() {
+
+    }
+
+    /**
+     * @return A String containing the unique call ID of this {@link ImsCallSessionImplBase}.
+     */
+    public String getCallId() {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is associated
+     * with.
+     */
+    public ImsCallProfile getCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The local {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    public ImsCallProfile getLocalCallProfile() {
+        return null;
+    }
+
+    /**
+     * @return The remote {@link ImsCallProfile} that this {@link ImsCallSessionImplBase} is
+     * associated with.
+     */
+    public ImsCallProfile getRemoteCallProfile() {
+        return null;
+    }
+
+    /**
+     * @param name The String extra key.
+     * @return The string extra value associated with the specified property.
+     */
+    public String getProperty(String name) {
+        return null;
+    }
+
+    /**
+     * @return The {@link ImsCallSessionImplBase} state, defined in
+     * {@link ImsCallSessionImplBase.State}.
+     */
+    public int getState() {
+        return ImsCallSessionImplBase.State.INVALID;
+    }
+
+    /**
+     * @return true if the {@link ImsCallSessionImplBase} is in a call, false otherwise.
+     */
+    public boolean isInCall() {
+        return false;
     }
 
     /**
      * Mutes or unmutes the mic for the active call.
      *
-     * @param muted true if the call is muted, false otherwise
+     * @param muted true if the call should be muted, false otherwise.
      */
-    @Override
-    public void setMute(boolean muted) throws RemoteException {
+    public void setMute(boolean muted) {
     }
 
     /**
-     * Initiates an IMS call with the specified target and call profile.
-     * The session listener set in {@link #setListener} is called back upon defined session events.
-     * The method is only valid to call when the session state is in
+     * Initiates an IMS call with the specified number and call profile.
+     * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+     * defined session events.
+     * Only valid to call when the session state is in
      * {@link ImsCallSession.State#IDLE}.
      *
      * @param callee dialed string to make the call to
@@ -149,13 +370,13 @@
      * @see {@link ImsCallSession.Listener#callSessionStarted},
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void start(String callee, ImsCallProfile profile) throws RemoteException {
+    public void start(String callee, ImsCallProfile profile) {
     }
 
     /**
      * Initiates an IMS call with the specified participants and call profile.
-     * The session listener set in {@link #setListener} is called back upon defined session events.
+     * The session listener set in {@link #setListener(ImsCallSessionListener)} is called back upon
+     * defined session events.
      * The method is only valid to call when the session state is in
      * {@link ImsCallSession.State#IDLE}.
      *
@@ -165,9 +386,7 @@
      * @see {@link ImsCallSession.Listener#callSessionStarted},
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void startConference(String[] participants, ImsCallProfile profile)
-            throws RemoteException {
+    public void startConference(String[] participants, ImsCallProfile profile) {
     }
 
     /**
@@ -177,31 +396,34 @@
      * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
      * @see {@link ImsCallSession.Listener#callSessionStarted}
      */
-    @Override
-    public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+    }
+
+    /**
+     * Deflects an incoming call.
+     *
+     * @param deflectNumber number to deflect the call
+     */
+    public void deflect(String deflectNumber) {
     }
 
     /**
      * Rejects an incoming call or session update.
      *
-     * @param reason reason code to reject an incoming call, defined in
-     *         com.android.ims.ImsReasonInfo
+     * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
      * {@link ImsCallSession.Listener#callSessionStartFailed}
      */
-    @Override
-    public void reject(int reason) throws RemoteException {
+    public void reject(int reason) {
     }
 
     /**
      * Terminates a call.
      *
-     * @param reason reason code to terminate a call, defined in
-     *         com.android.ims.ImsReasonInfo
+     * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
      *
      * @see {@link ImsCallSession.Listener#callSessionTerminated}
      */
-    @Override
-    public void terminate(int reason) throws RemoteException {
+    public void terminate(int reason) {
     }
 
     /**
@@ -212,8 +434,7 @@
      * @see {@link ImsCallSession.Listener#callSessionHeld},
      * {@link ImsCallSession.Listener#callSessionHoldFailed}
      */
-    @Override
-    public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+    public void hold(ImsStreamMediaProfile profile) {
     }
 
     /**
@@ -224,12 +445,11 @@
      * @see {@link ImsCallSession.Listener#callSessionResumed},
      * {@link ImsCallSession.Listener#callSessionResumeFailed}
      */
-    @Override
-    public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+    public void resume(ImsStreamMediaProfile profile) {
     }
 
     /**
-     * Merges the active & hold call. When the merge starts,
+     * Merges the active and held call. When the merge starts,
      * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
      * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
      * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
@@ -239,8 +459,7 @@
      * {@link ImsCallSession.Listener#callSessionMergeComplete},
      *      {@link ImsCallSession.Listener#callSessionMergeFailed}
      */
-    @Override
-    public void merge() throws RemoteException {
+    public void merge() {
     }
 
     /**
@@ -251,8 +470,7 @@
      * @see {@link ImsCallSession.Listener#callSessionUpdated},
      * {@link ImsCallSession.Listener#callSessionUpdateFailed}
      */
-    @Override
-    public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    public void update(int callType, ImsStreamMediaProfile profile) {
     }
 
     /**
@@ -263,8 +481,7 @@
      * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
      * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
      */
-    @Override
-    public void extendToConference(String[] participants) throws RemoteException {
+    public void extendToConference(String[] participants) {
     }
 
     /**
@@ -274,8 +491,7 @@
      * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
      *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
      */
-    @Override
-    public void inviteParticipants(String[] participants) throws RemoteException {
+    public void inviteParticipants(String[] participants) {
     }
 
     /**
@@ -285,8 +501,7 @@
      * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
      *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
      */
-    @Override
-    public void removeParticipants(String[] participants) throws RemoteException {
+    public void removeParticipants(String[] participants) {
     }
 
     /**
@@ -296,8 +511,7 @@
      *
      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
      */
-    @Override
-    public void sendDtmf(char c, Message result) throws RemoteException {
+    public void sendDtmf(char c, Message result) {
     }
 
     /**
@@ -307,15 +521,13 @@
      *
      * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
      */
-    @Override
-    public void startDtmf(char c) throws RemoteException {
+    public void startDtmf(char c) {
     }
 
     /**
      * Stop a DTMF code.
      */
-    @Override
-    public void stopDtmf() throws RemoteException {
+    public void stopDtmf() {
     }
 
     /**
@@ -323,17 +535,23 @@
      *
      * @param ussdMessage USSD message to send
      */
-    @Override
-    public void sendUssd(String ussdMessage) throws RemoteException {
+    public void sendUssd(String ussdMessage) {
     }
 
     /**
-     * Returns a binder for the video call provider implementation contained within the IMS service
-     * process. This binder is used by the VideoCallProvider subclass in Telephony which
-     * intermediates between the propriety implementation and Telecomm/InCall.
+     * See {@link #getImsVideoCallProvider()}, used directly in older ImsService implementations.
+     * @hide
      */
-    @Override
-    public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+    public IImsVideoCallProvider getVideoCallProvider() {
+        ImsVideoCallProvider provider = getImsVideoCallProvider();
+        return provider != null ? provider.getInterface() : null;
+    }
+
+    /**
+     * @return The {@link ImsVideoCallProvider} implementation contained within the IMS service
+     * process.
+     */
+    public ImsVideoCallProvider getImsVideoCallProvider() {
         return null;
     }
 
@@ -341,8 +559,7 @@
      * Determines if the current session is multiparty.
      * @return {@code True} if the session is multiparty.
      */
-    @Override
-    public boolean isMultiparty() throws RemoteException {
+    public boolean isMultiparty() {
         return false;
     }
 
@@ -350,16 +567,13 @@
      * Device issues RTT modify request
      * @param toProfile The profile with requested changes made
      */
-    @Override
     public void sendRttModifyRequest(ImsCallProfile toProfile) {
     }
 
     /**
      * Device responds to Remote RTT modify request
-     * @param status true  Accepted the request
-     *                false  Declined the request
+     * @param status true if the the request was accepted or false of the request is defined.
      */
-    @Override
     public void sendRttModifyResponse(boolean status) {
     }
 
@@ -367,7 +581,16 @@
      * Device sends RTT message
      * @param rttMessage RTT message to be sent
      */
-    @Override
     public void sendRttMessage(String rttMessage) {
     }
+
+    /** @hide */
+    public IImsCallSession getServiceImpl() {
+        return mServiceImpl;
+    }
+
+    /** @hide */
+    public void setServiceImpl(IImsCallSession serviceImpl) {
+        mServiceImpl = serviceImpl;
+    }
 }
diff --git a/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
deleted file mode 100644
index 6c18935..0000000
--- a/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
+++ /dev/null
@@ -1,298 +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.telephony.ims.stub;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.ImsConferenceState;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
-import com.android.ims.ImsSuppServiceNotification;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-
-/**
- * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
- * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
- * ImsCallSessionListener supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
- *
- * @hide
- */
-public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
-    /**
-     * Notifies the result of the basic session operation (setup / terminate).
-     */
-    @Override
-    public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of the call hold/resume operation.
-     */
-    @Override
-    public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of call merge operation.
-     */
-    @Override
-    public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionMergeComplete(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of call upgrade / downgrade or any other call
-     * updates.
-     */
-    @Override
-    public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of conference extension.
-     */
-    @Override
-    public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionConferenceExtendFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionConferenceExtendReceived(IImsCallSession session,
-            IImsCallSession newSession,
-            ImsCallProfile profile) {
-        // no-op
-    }
-
-    /**
-     * Notifies the result of the participant invitation / removal to/from the
-     * conference session.
-     */
-    @Override
-    public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the changes of the conference info. the conference session.
-     */
-    @Override
-    public void callSessionConferenceStateUpdated(IImsCallSession session,
-            ImsConferenceState state) {
-        // no-op
-    }
-
-    /**
-     * Notifies the incoming USSD message.
-     */
-    @Override
-    public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
-            String ussdMessage) {
-        // no-op
-    }
-
-    /**
-     * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
-     * handover from one radio technology to another.
-     * @param session
-     * @param srcAccessTech The source radio access technology; one of the access technology
-     *                      constants defined in {@link android.telephony.ServiceState}.  For
-     *                      example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     * @param targetAccessTech The target radio access technology; one of the access technology
-     *                      constants defined in {@link android.telephony.ServiceState}.  For
-     *                      example {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
-     */
-    @Override
-    public void callSessionMayHandover(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech) {
-        // no-op
-    }
-
-    /**
-     * Notifies of handover information for this call
-     */
-    @Override
-    public void callSessionHandover(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    @Override
-    public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
-            int targetAccessTech,
-            ImsReasonInfo reasonInfo) {
-        // no-op
-    }
-
-    /**
-     * Notifies the TTY mode change by remote party.
-     *
-     * @param mode one of the following: -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
-     *            {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
-     */
-    @Override
-    public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
-        // no-op
-    }
-
-    /**
-     * Notifies of a change to the multiparty state for this
-     * {@code ImsCallSession}.
-     *
-     * @param session The call session.
-     * @param isMultiParty {@code true} if the session became multiparty,
-     *            {@code false} otherwise.
-     */
-    @Override
-    public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
-        // no-op
-    }
-
-    /**
-     * Notifies the supplementary service information for the current session.
-     */
-    @Override
-    public void callSessionSuppServiceReceived(IImsCallSession session,
-            ImsSuppServiceNotification suppSrvNotification) {
-        // no-op
-    }
-
-    /**
-     * Received RTT modify request from Remote Party
-     * @param session The call session.
-     * @param callProfile ImsCallProfile with updated attribute
-     */
-    @Override
-    public void callSessionRttModifyRequestReceived(IImsCallSession session,
-            ImsCallProfile callProfile) {
-        // no-op
-    }
-
-    /**
-     * Received response for RTT modify request
-     * @param status true : Accepted the request
-     *               false : Declined the request
-     */
-    @Override
-    public void callSessionRttModifyResponseReceived(int status) {
-        // no -op
-    }
-
-    /**
-     * Device received RTT message from Remote UE
-     * @param rttMessage RTT message received
-     */
-    @Override
-    public void callSessionRttMessageReceived(String rttMessage) {
-        // no-op
-    }
-}
-
diff --git a/android/telephony/ims/stub/ImsConfigImplBase.java b/android/telephony/ims/stub/ImsConfigImplBase.java
index 1670e6b..dcd7ea7 100644
--- a/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,31 +16,24 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 import android.util.Log;
 
 import com.android.ims.ImsConfig;
-import com.android.ims.ImsConfigListener;
-import com.android.ims.internal.IImsConfig;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 
-
 /**
- * Base implementation of ImsConfig.
- * Override the methods that your implementation of ImsConfig supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsConfig maintained by other ImsServices.
- *
- * Provides APIs to get/set the IMS service feature/capability/parameters.
- * The config items include:
- * 1) Items provisioned by the operator.
- * 2) Items configured by user. Mainly service feature class.
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
  *
  * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
  * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
@@ -51,142 +44,10 @@
  * performed every time.
  * @hide
  */
-
+@SystemApi
 public class ImsConfigImplBase {
 
-    static final private String TAG = "ImsConfigImplBase";
-
-    ImsConfigStub mImsConfigStub;
-
-    public ImsConfigImplBase(Context context) {
-        mImsConfigStub = new ImsConfigStub(this, context);
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage. Synchronous blocking call.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in Integer format.
-     */
-    public int getProvisionedValue(int item) throws RemoteException {
-        return -1;
-    }
-
-    /**
-     * Gets the value for ims service/capabilities parameters from the provisioned
-     * value storage. Synchronous blocking call.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @return value in String format.
-     */
-    public String getProvisionedStringValue(int item) throws RemoteException {
-        return null;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived. Synchronous blocking call.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in Integer format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setProvisionedValue(int item, int value) throws RemoteException {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Sets the value for IMS service/capabilities parameters by the operator device
-     * management entity. It sets the config item value in the provisioned storage
-     * from which the master value is derived.  Synchronous blocking call.
-     *
-     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
-     */
-    public int setProvisionedStringValue(int item, String value) throws RemoteException {
-        return ImsConfig.OperationStatusConstants.FAILED;
-    }
-
-    /**
-     * Gets the value of the specified IMS feature item for specified network type.
-     * This operation gets the feature config value from the master storage (i.e. final
-     * value). Asynchronous non-blocking call.
-     *
-     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
-     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
-     * @param listener feature value returned asynchronously through listener.
-     */
-    public void getFeatureValue(int feature, int network, ImsConfigListener listener)
-            throws RemoteException {
-    }
-
-    /**
-     * Sets the value for IMS feature item for specified network type.
-     * This operation stores the user setting in setting db from which master db
-     * is derived.
-     *
-     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
-     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
-     * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
-     * @param listener, provided if caller needs to be notified for set result.
-     */
-    public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
-            throws RemoteException {
-    }
-
-    /**
-     * Gets the value for IMS VoLTE provisioned.
-     * This should be the same as the operator provisioned value if applies.
-     */
-    public boolean getVolteProvisioned() throws RemoteException {
-        return false;
-    }
-
-    /**
-     * Gets the value for IMS feature item video quality.
-     *
-     * @param listener Video quality value returned asynchronously through listener.
-     */
-    public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
-    }
-
-    /**
-     * Sets the value for IMS feature item video quality.
-     *
-     * @param quality, defines the value of video quality.
-     * @param listener, provided if caller needs to be notified for set result.
-     */
-    public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
-    }
-
-    public IImsConfig getIImsConfig() { return mImsConfigStub; }
-
-    /**
-     * Updates provisioning value and notifies the framework of the change.
-     * Doesn't call #setProvisionedValue and assumes the result succeeded.
-     * This should only be used by modem when they implicitly changed provisioned values.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in Integer format.
-     */
-    public final void notifyProvisionedValueChanged(int item, int value) {
-        mImsConfigStub.updateCachedValue(item, value, true);
-    }
-
-    /**
-     * Updates provisioning value and notifies the framework of the change.
-     * Doesn't call #setProvisionedValue and assumes the result succeeded.
-     * This should only be used by modem when they implicitly changed provisioned values.
-     *
-     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-     * @param value in String format.
-     */
-    public final void notifyProvisionedValueChanged(int item, String value) {
-        mImsConfigStub.updateCachedValue(item, value, true);
-    }
+    private static final String TAG = "ImsConfigImplBase";
 
     /**
      * Implements the IImsConfig AIDL interface, which is called by potentially many processes
@@ -208,32 +69,41 @@
      */
     @VisibleForTesting
     static public class ImsConfigStub extends IImsConfig.Stub {
-        Context mContext;
         WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
         private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
         private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
 
         @VisibleForTesting
-        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
-            mContext = context;
+        public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
             mImsConfigImplBaseWeakReference =
                     new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
         }
 
+        @Override
+        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            getImsConfigImpl().addImsConfigCallback(c);
+        }
+
+        @Override
+        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            getImsConfigImpl().removeImsConfigCallback(c);
+        }
+
         /**
          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
-         * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+         * if missed, it will call ImsConfigImplBase.getConfigInt.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
-         * @return value in Integer format.
+         * @param item integer key
+         * @return value in Integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+         * unavailable.
          */
         @Override
-        public synchronized int getProvisionedValue(int item) throws RemoteException {
+        public synchronized int getConfigInt(int item) throws RemoteException {
             if (mProvisionedIntValue.containsKey(item)) {
                 return mProvisionedIntValue.get(item);
             } else {
-                int retVal = getImsConfigImpl().getProvisionedValue(item);
+                int retVal = getImsConfigImpl().getConfigInt(item);
                 if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
                     updateCachedValue(item, retVal, false);
                 }
@@ -243,18 +113,18 @@
 
         /**
          * Gets the value for ims service/capabilities parameters. It first checks its local cache,
-         * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+         * if missed, it will call #ImsConfigImplBase.getConfigString.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param item integer key
          * @return value in String format.
          */
         @Override
-        public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+        public synchronized String getConfigString(int item) throws RemoteException {
             if (mProvisionedIntValue.containsKey(item)) {
                 return mProvisionedStringValue.get(item);
             } else {
-                String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+                String retVal = getImsConfigImpl().getConfigString(item);
                 if (retVal != null) {
                     updateCachedValue(item, retVal, false);
                 }
@@ -268,16 +138,17 @@
          * from which the master value is derived, and write it into local cache.
          * Synchronous blocking call.
          *
-         * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+         * @param item integer key
          * @param value in Integer format.
-         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
          */
         @Override
-        public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+        public synchronized int setConfigInt(int item, int value) throws RemoteException {
             mProvisionedIntValue.remove(item);
-            int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+            int retVal = getImsConfigImpl().setConfig(item, value);
             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
-                updateCachedValue(item, retVal, true);
+                updateCachedValue(item, value, true);
             } else {
                 Log.d(TAG, "Set provision value of " + item +
                         " to " + value + " failed with error code " + retVal);
@@ -294,63 +165,21 @@
          *
          * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
          * @param value in String format.
-         * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+         * @return the result of setting the configuration value, defined as either
+         * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
          */
         @Override
-        public synchronized int setProvisionedStringValue(int item, String value)
+        public synchronized int setConfigString(int item, String value)
                 throws RemoteException {
             mProvisionedStringValue.remove(item);
-            int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+            int retVal = getImsConfigImpl().setConfig(item, value);
             if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
-                updateCachedValue(item, retVal, true);
+                updateCachedValue(item, value, true);
             }
 
             return retVal;
         }
 
-        /**
-         * Wrapper function to call ImsConfigImplBase.getFeatureValue.
-         */
-        @Override
-        public void getFeatureValue(int feature, int network, ImsConfigListener listener)
-                throws RemoteException {
-            getImsConfigImpl().getFeatureValue(feature, network, listener);
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.setFeatureValue.
-         */
-        @Override
-        public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
-                throws RemoteException {
-            getImsConfigImpl().setFeatureValue(feature, network, value, listener);
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
-         */
-        @Override
-        public boolean getVolteProvisioned() throws RemoteException {
-            return getImsConfigImpl().getVolteProvisioned();
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.getVideoQuality.
-         */
-        @Override
-        public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
-            getImsConfigImpl().getVideoQuality(listener);
-        }
-
-        /**
-         * Wrapper function to call ImsConfigImplBase.setVideoQuality.
-         */
-        @Override
-        public void setVideoQuality(int quality, ImsConfigListener listener)
-                throws RemoteException {
-            getImsConfigImpl().setVideoQuality(quality, listener);
-        }
-
         private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
             ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
             if (ref == null) {
@@ -360,32 +189,228 @@
             }
         }
 
-        private void sendImsConfigChangedIntent(int item, int value) {
-            sendImsConfigChangedIntent(item, Integer.toString(value));
+        private void notifyImsConfigChanged(int item, int value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
         }
 
-        private void sendImsConfigChangedIntent(int item, String value) {
-            Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
-            configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
-            configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
-            if (mContext != null) {
-                mContext.sendBroadcast(configChangedIntent);
-            }
+        private void notifyImsConfigChanged(int item, String value) throws RemoteException {
+            getImsConfigImpl().notifyConfigChanged(item, value);
         }
 
-        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+        protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
+        throws RemoteException {
             mProvisionedIntValue.put(item, value);
             if (notifyChange) {
-                sendImsConfigChangedIntent(item, value);
+                notifyImsConfigChanged(item, value);
             }
         }
 
-        protected synchronized void updateCachedValue(
-                int item, String value, boolean notifyChange) {
+        protected synchronized void updateCachedValue(int item, String value,
+                boolean notifyChange) throws RemoteException {
             mProvisionedStringValue.put(item, value);
             if (notifyChange) {
-                sendImsConfigChangedIntent(item, value);
+                notifyImsConfigChanged(item, value);
             }
         }
     }
+
+    /**
+     * Callback that the framework uses for receiving Configuration change updates.
+     * {@hide}
+     */
+    public static class Callback extends IImsConfigCallback.Stub {
+
+        @Override
+        public final void onIntConfigChanged(int item, int value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        @Override
+        public final void onStringConfigChanged(int item, String value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new integer value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, int value) {
+            // Base Implementation
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new String value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, String value) {
+            // Base Implementation
+        }
+    }
+
+    /**
+     * The configuration requested resulted in an unknown result. This may happen if the
+     * IMS configurations are unavailable.
+     */
+    public static final int CONFIG_RESULT_UNKNOWN = -1;
+    /**
+     * Setting the configuration value completed.
+     */
+    public static final int CONFIG_RESULT_SUCCESS = 0;
+    /**
+     * Setting the configuration value failed.
+     */
+    public static final int CONFIG_RESULT_FAILED =  1;
+
+    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+    ImsConfigStub mImsConfigStub;
+
+    /**
+     * Used for compatibility between older versions of the ImsService.
+     * @hide
+     */
+    public ImsConfigImplBase(Context context) {
+        mImsConfigStub = new ImsConfigStub(this);
+    }
+
+    public ImsConfigImplBase() {
+        mImsConfigStub = new ImsConfigStub(this);
+    }
+
+    /**
+     * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
+     * changes.
+     * @param c callback to add.
+     */
+    private void addImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.register(c);
+    }
+    /**
+     * Removes a {@link Callback} to the list of callbacks notified when a value in the
+     * configuration changes.
+     *
+     * @param c callback to remove.
+     */
+    private void removeImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.unregister(c);
+    }
+
+    /**
+     * @param item
+     * @param value
+     */
+    private final void notifyConfigChanged(int item, int value) {
+        // can be null in testing
+        if (mCallbacks == null) {
+            return;
+        }
+        mCallbacks.broadcast(c -> {
+            try {
+                c.onIntConfigChanged(item, value);
+            } catch (RemoteException e) {
+                Log.w(TAG, "notifyConfigChanged(int): dead binder in notify, skipping.");
+            }
+        });
+    }
+
+    private void notifyConfigChanged(int item, String value) {
+        // can be null in testing
+        if (mCallbacks == null) {
+            return;
+        }
+        mCallbacks.broadcast(c -> {
+            try {
+                c.onStringConfigChanged(item, value);
+            } catch (RemoteException e) {
+                Log.w(TAG, "notifyConfigChanged(string): dead binder in notify, skipping.");
+            }
+        });
+    }
+
+    /**
+     * @hide
+     */
+    public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call {@link #setConfig(int,int)} and assumes the result succeeded.
+     * This should only be used when the IMS implementer implicitly changed provisioned values.
+     *
+     * @param item an integer key.
+     * @param value in Integer format.
+     */
+    public final void notifyProvisionedValueChanged(int item, int value) {
+        try {
+            mImsConfigStub.updateCachedValue(item, value, true);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * Updates provisioning value and notifies the framework of the change.
+     * Doesn't call {@link #setConfig(int,String)} and assumes the result succeeded.
+     * This should only be used when the IMS implementer implicitly changed provisioned values.
+     *
+     * @param item an integer key.
+     * @param value in String format.
+     */
+    public final void notifyProvisionedValueChanged(int item, String value) {
+        try {
+        mImsConfigStub.updateCachedValue(item, value, true);
+        } catch (RemoteException e) {
+            Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
+        }
+    }
+
+    /**
+     * Sets the configuration value for this ImsService.
+     *
+     * @param item an integer key.
+     * @param value an integer containing the configuration value.
+     * @return the result of setting the configuration value, defined as either
+     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     */
+    public int setConfig(int item, int value) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_FAILED;
+    }
+
+    /**
+     * Sets the configuration value for this ImsService.
+     *
+     * @param item an integer key.
+     * @param value a String containing the new configuration value.
+     * @return Result of setting the configuration value, defined as either
+     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     */
+    public int setConfig(int item, String value) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_FAILED;
+    }
+
+    /**
+     * Gets the currently stored value configuration value from the ImsService for {@code item}.
+     *
+     * @param item an integer key.
+     * @return configuration value, stored in integer format or {@link #CONFIG_RESULT_UNKNOWN} if
+     * unavailable.
+     */
+    public int getConfigInt(int item) {
+        // Base Implementation - To be overridden.
+        return CONFIG_RESULT_UNKNOWN;
+    }
+
+    /**
+     * Gets the currently stored value configuration value from the ImsService for {@code item}.
+     *
+     * @param item an integer key.
+     * @return configuration value, stored in String format or {@code null} if unavailable.
+     */
+    public String getConfigString(int item) {
+        // Base Implementation - To be overridden.
+        return null;
+    }
 }
diff --git a/android/telephony/ims/stub/ImsEcbmImplBase.java b/android/telephony/ims/stub/ImsEcbmImplBase.java
index 89f95ff..06c35ea 100644
--- a/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,7 +16,9 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.ims.internal.IImsEcbm;
 import com.android.ims.internal.IImsEcbmListener;
@@ -30,22 +32,65 @@
  *
  * @hide
  */
+@SystemApi
+public class ImsEcbmImplBase {
+    private static final String TAG = "ImsEcbmImplBase";
 
-public class ImsEcbmImplBase extends IImsEcbm.Stub {
+    private IImsEcbmListener mListener;
+    private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+        @Override
+        public void setListener(IImsEcbmListener listener) {
+            mListener = listener;
+        }
 
-    /**
-     * Sets the listener.
-     */
-    @Override
-    public void setListener(IImsEcbmListener listener) throws RemoteException {
+        @Override
+        public void exitEmergencyCallbackMode() {
+            ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+        }
+    };
 
+    /** @hide */
+    public IImsEcbm getImsEcbm() {
+        return mImsEcbm;
     }
 
     /**
-     * Requests Modem to come out of ECBM mode
+     * This method should be implemented by the IMS provider. Framework will trigger this method to
+     * request to come out of ECBM mode
      */
-    @Override
-    public void exitEmergencyCallbackMode() throws RemoteException {
+    public void exitEmergencyCallbackMode() {
+        Log.d(TAG, "exitEmergencyCallbackMode() not implemented");
+    }
 
+    /**
+     * Notifies the framework when the device enters Emergency Callback Mode.
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void enteredEcbm() {
+        Log.d(TAG, "Entered ECBM.");
+        if (mListener != null) {
+            try {
+                mListener.enteredECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Notifies the framework when the device exits Emergency Callback Mode.
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
+     */
+    public final void exitedEcbm() {
+        Log.d(TAG, "Exited ECBM.");
+        if (mListener != null) {
+            try {
+                mListener.exitedECBM();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/android/telephony/ims/stub/ImsFeatureConfiguration.java b/android/telephony/ims/stub/ImsFeatureConfiguration.java
new file mode 100644
index 0000000..2f52c0a
--- /dev/null
+++ b/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import java.util.Set;
+
+/**
+ * Container class for IMS Feature configuration. This class contains the features that the
+ * ImsService supports, which are defined in {@link ImsFeature} as
+ * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+ * {@link ImsFeature#FEATURE_RCS}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ImsFeatureConfiguration implements Parcelable {
+
+    public static final class FeatureSlotPair {
+        /**
+         * SIM slot that this feature is associated with.
+         */
+        public final int slotId;
+        /**
+         * The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public final @ImsFeature.FeatureType int featureType;
+
+        /**
+         * A mapping from slotId to IMS Feature type.
+         * @param slotId the SIM slot ID associated with this feature.
+         * @param featureType The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public FeatureSlotPair(int slotId, @ImsFeature.FeatureType int featureType) {
+            this.slotId = slotId;
+            this.featureType = featureType;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            FeatureSlotPair that = (FeatureSlotPair) o;
+
+            if (slotId != that.slotId) return false;
+            return featureType == that.featureType;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = slotId;
+            result = 31 * result + featureType;
+            return result;
+        }
+    }
+
+    /**
+     * Features that this ImsService supports.
+     */
+    private final Set<FeatureSlotPair> mFeatures;
+
+    /**
+     * Builder for {@link ImsFeatureConfiguration}.
+     */
+    public static class Builder {
+            ImsFeatureConfiguration mConfig;
+        public Builder() {
+            mConfig = new ImsFeatureConfiguration();
+        }
+
+        /**
+         * Adds an IMS feature associated with a SIM slot ID.
+         * @param slotId The slot ID associated with the IMS feature.
+         * @param featureType The feature that the slot ID supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
+         */
+        public Builder addFeature(int slotId, @ImsFeature.FeatureType int featureType) {
+            mConfig.addFeature(slotId, featureType);
+            return this;
+        }
+
+        public ImsFeatureConfiguration build() {
+            return mConfig;
+        }
+    }
+
+    /**
+     * Creates with all registration features empty.
+     * @hide
+     */
+    public ImsFeatureConfiguration() {
+        mFeatures = new ArraySet<>();
+    }
+
+    /**
+     * Configuration of the ImsService, which describes which features the ImsService supports
+     * (for registration).
+     * @param features a set of {@link FeatureSlotPair}s that describe which features this
+     *         ImsService supports.
+     * @hide
+     */
+    public ImsFeatureConfiguration(Set<FeatureSlotPair> features) {
+        mFeatures = new ArraySet<>();
+
+        if (features != null) {
+            mFeatures.addAll(features);
+        }
+    }
+
+    /**
+     * @return a set of supported slot ID to feature type pairs contained within a
+     * {@link FeatureSlotPair}.
+     */
+    public Set<FeatureSlotPair> getServiceFeatures() {
+        return new ArraySet<>(mFeatures);
+    }
+
+    /**
+     * @hide
+     */
+    void addFeature(int slotId, int feature) {
+        mFeatures.add(new FeatureSlotPair(slotId, feature));
+    }
+
+    /** @hide */
+    protected ImsFeatureConfiguration(Parcel in) {
+        int featurePairLength = in.readInt();
+        // length
+        mFeatures = new ArraySet<>(featurePairLength);
+        for (int i = 0; i < featurePairLength; i++) {
+            // pair of reads for each entry (slotId->featureType)
+            mFeatures.add(new FeatureSlotPair(in.readInt(), in.readInt()));
+        }
+    }
+
+    public static final Creator<ImsFeatureConfiguration> CREATOR
+            = new Creator<ImsFeatureConfiguration>() {
+        @Override
+        public ImsFeatureConfiguration createFromParcel(Parcel in) {
+            return new ImsFeatureConfiguration(in);
+        }
+
+        @Override
+        public ImsFeatureConfiguration[] newArray(int size) {
+            return new ImsFeatureConfiguration[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        FeatureSlotPair[] featureSlotPairs = new FeatureSlotPair[mFeatures.size()];
+        mFeatures.toArray(featureSlotPairs);
+        // length of list
+        dest.writeInt(featureSlotPairs.length);
+        // then pairs of integers for each entry (slotId->featureType).
+        for (FeatureSlotPair featureSlotPair : featureSlotPairs) {
+            dest.writeInt(featureSlotPair.slotId);
+            dest.writeInt(featureSlotPair.featureType);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ImsFeatureConfiguration)) return false;
+
+        ImsFeatureConfiguration
+                that = (ImsFeatureConfiguration) o;
+
+        return mFeatures.equals(that.mFeatures);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int hashCode() {
+        return mFeatures.hashCode();
+    }
+}
diff --git a/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 05da9da..ce2d89a 100644
--- a/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,11 +16,16 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.RemoteException;
+import android.util.Log;
 
+import android.telephony.ims.ImsExternalCallState;
 import com.android.ims.internal.IImsExternalCallStateListener;
 import com.android.ims.internal.IImsMultiEndpoint;
 
+import java.util.List;
+
 /**
  * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
  * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
@@ -31,23 +36,49 @@
  *
  * @hide
  */
+@SystemApi
+public class ImsMultiEndpointImplBase {
+    private static final String TAG = "MultiEndpointImplBase";
 
-public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+    private IImsExternalCallStateListener mListener;
+    private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+        @Override
+        public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+            mListener = listener;
+        }
 
-    /**
-     * Sets the listener.
-     */
-    @Override
-    public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+        @Override
+        public void requestImsExternalCallStateInfo() throws RemoteException {
+            ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+        }
+    };
 
+    /** @hide */
+    public IImsMultiEndpoint getIImsMultiEndpoint() {
+        return mImsMultiEndpoint;
     }
 
     /**
-     * Query API to get the latest Dialog Event Package information
-     * Should be invoked only after setListener is done
+     * Notifies framework when Dialog Event Package update is received
+     *
+     * @throws RuntimeException if the connection to the framework is not available.
      */
-    @Override
-    public void requestImsExternalCallStateInfo() throws RemoteException {
+    public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
+        Log.d(TAG, "ims external call state update triggered.");
+        if (mListener != null) {
+            try {
+                mListener.onImsExternalCallStateUpdate(externalCallDialogs);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
 
+    /**
+     * This method should be implemented by the IMS provider. Framework will trigger this to get the
+     * latest Dialog Event Package information. Should
+     */
+    public void requestImsExternalCallStateInfo() {
+        Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
     }
 }
diff --git a/android/telephony/ims/stub/ImsRegistrationImplBase.java b/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 42af083..4334d3a 100644
--- a/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -17,15 +17,16 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.net.Uri;
-import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.internal.IImsRegistration;
-import com.android.ims.internal.IImsRegistrationCallback;
+import android.telephony.ims.ImsReasonInfo;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -36,11 +37,14 @@
  * registration for this ImsService has changed status.
  * @hide
  */
-
+@SystemApi
 public class ImsRegistrationImplBase {
 
     private static final String LOG_TAG = "ImsRegistrationImplBase";
 
+    /**
+     * @hide
+     */
     // Defines the underlying radio technology type that we have registered for IMS over.
     @IntDef(flag = true,
             value = {
@@ -155,6 +159,9 @@
     // Locked on mLock, create unspecified disconnect cause.
     private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
 
+    /**
+     * @hide
+     */
     public final IImsRegistration getBinder() {
         return mBinder;
     }
@@ -171,8 +178,8 @@
     /**
      * Notify the framework that the device is connected to the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined in
-     * {@link ImsRegistrationTech}.
+     * @param imsRadioTech the radio access technology. Valid values are defined as
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
      */
     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
@@ -189,8 +196,8 @@
     /**
      * Notify the framework that the device is trying to connect the IMS network.
      *
-     * @param imsRadioTech the radio access technology. Valid values are defined in
-     * {@link ImsRegistrationTech}.
+     * @param imsRadioTech the radio access technology. Valid values are defined as
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
      */
     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
@@ -221,6 +228,13 @@
         });
     }
 
+    /**
+     * Notify the framework that the handover from the current radio technology to the technology
+     * defined in {@code imsRadioTech} has failed.
+     * @param imsRadioTech The technology that has failed to be changed. Valid values are
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
+     * @param info The {@link ImsReasonInfo} for the failure to change technology.
+     */
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
         mCallbacks.broadcast((c) -> {
@@ -233,6 +247,11 @@
         });
     }
 
+    /**
+     * The this device's subscriber associated {@link Uri}s have changed, which are used to filter
+     * out this device's {@link Uri}s during conference calling.
+     * @param uris
+     */
     public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
         mCallbacks.broadcast((c) -> {
             try {
@@ -264,6 +283,11 @@
         }
     }
 
+    /**
+     * @return the current registration connection type. Valid values are
+     * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}
+     * @hide
+     */
     @VisibleForTesting
     public final @ImsRegistrationTech int getConnectionType() {
         synchronized (mLock) {
diff --git a/android/telephony/ims/stub/ImsSmsImplBase.java b/android/telephony/ims/stub/ImsSmsImplBase.java
new file mode 100644
index 0000000..852c8e0
--- /dev/null
+++ b/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.ims.aidl.IImsSmsListener;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for SMS over IMS.
+ *
+ * Any service wishing to provide SMS over IMS should extend this class and implement all methods
+ * that the service supports.
+ *
+ * @hide
+ */
+@SystemApi
+public class ImsSmsImplBase {
+    private static final String LOG_TAG = "SmsImplBase";
+
+    /** @hide */
+    @IntDef({
+            SEND_STATUS_OK,
+            SEND_STATUS_ERROR,
+            SEND_STATUS_ERROR_RETRY,
+            SEND_STATUS_ERROR_FALLBACK
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SendStatusResult {}
+    /**
+     * Message was sent successfully.
+     */
+    public static final int SEND_STATUS_OK = 1;
+
+    /**
+     * IMS provider failed to send the message and platform should not retry falling back to sending
+     * the message using the radio.
+     */
+    public static final int SEND_STATUS_ERROR = 2;
+
+    /**
+     * IMS provider failed to send the message and platform should retry again after setting TP-RD
+     * bit to high.
+     */
+    public static final int SEND_STATUS_ERROR_RETRY = 3;
+
+    /**
+     * IMS provider failed to send the message and platform should retry falling back to sending
+     * the message using the radio.
+     */
+    public static final int SEND_STATUS_ERROR_FALLBACK = 4;
+
+    /** @hide */
+    @IntDef({
+            DELIVER_STATUS_OK,
+            DELIVER_STATUS_ERROR_GENERIC,
+            DELIVER_STATUS_ERROR_NO_MEMORY,
+            DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeliverStatusResult {}
+    /**
+     * Message was delivered successfully.
+     */
+    public static final int DELIVER_STATUS_OK = 1;
+
+    /**
+     * Message was not delivered.
+     */
+    public static final int DELIVER_STATUS_ERROR_GENERIC = 2;
+
+    /**
+     * Message was not delivered due to lack of memory.
+     */
+    public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3;
+
+    /**
+     * Message was not delivered as the request is not supported.
+     */
+    public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4;
+
+    /** @hide */
+    @IntDef({
+            STATUS_REPORT_STATUS_OK,
+            STATUS_REPORT_STATUS_ERROR
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusReportResult {}
+
+    /**
+     * Status Report was set successfully.
+     */
+    public static final int STATUS_REPORT_STATUS_OK = 1;
+
+    /**
+     * Error while setting status report.
+     */
+    public static final int STATUS_REPORT_STATUS_ERROR = 2;
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+    private IImsSmsListener mListener;
+
+    /**
+     * Registers a listener responsible for handling tasks like delivering messages.
+     *
+     * @param listener listener to register.
+     *
+     * @hide
+     */
+    public final void registerSmsListener(IImsSmsListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+    }
+
+    /**
+     * This method will be triggered by the platform when the user attempts to send an SMS. This
+     * method should be implemented by the IMS providers to provide implementation of sending an SMS
+     * over IMS.
+     *
+     * @param token unique token generated by the platform that should be used when triggering
+     *             callbacks for this specific message.
+     * @param messageRef the message reference.
+     * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+     *               {@link SmsMessage#FORMAT_3GPP2}.
+     * @param smsc the Short Message Service Center address.
+     * @param isRetry whether it is a retry of an already attempted message or not.
+     * @param pdu PDUs representing the contents of the message.
+     */
+    public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+            byte[] pdu) {
+        // Base implementation returns error. Should be overridden.
+        try {
+            onSendSmsResult(token, messageRef, SEND_STATUS_ERROR,
+                    SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+        } catch (RuntimeException e) {
+            Log.e(LOG_TAG, "Can not send sms: " + e.getMessage());
+        }
+    }
+
+    /**
+     * This method will be triggered by the platform after
+     * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS
+     * provider.
+     *
+     * @param token token provided in {@link #onSmsReceived(int, String, byte[])}
+     * @param result result of delivering the message. Valid values are:
+     *  {@link #DELIVER_STATUS_OK},
+     *  {@link #DELIVER_STATUS_ERROR_GENERIC},
+     *  {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
+     *  {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
+     * @param messageRef the message reference
+     */
+    public void acknowledgeSms(int token, @DeliverStatusResult int messageRef, int result) {
+        Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
+    }
+
+    /**
+     * This method will be triggered by the platform after
+     * {@link #onSmsStatusReportReceived(int, int, String, byte[])} has been called to provide the
+     * result to the IMS provider.
+     *
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param result result of delivering the message. Valid values are:
+     *  {@link #STATUS_REPORT_STATUS_OK},
+     *  {@link #STATUS_REPORT_STATUS_ERROR}
+     * @param messageRef the message reference
+     */
+    public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
+        Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
+    }
+
+    /**
+     * This method should be triggered by the IMS providers when there is an incoming message. The
+     * platform will deliver the message to the messages database and notify the IMS provider of the
+     * result by calling {@link #acknowledgeSms(int, int, int)}.
+     *
+     * This method must not be called before {@link #onReady()} is called or the call will fail. If
+     * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
+     * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
+     * @param token unique token generated by IMS providers that the platform will use to trigger
+     *              callbacks for this message.
+     * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+     * {@link SmsMessage#FORMAT_3GPP2}.
+     * @param pdu PDUs representing the contents of the message.
+     * @throws RuntimeException if called before {@link #onReady()} is triggered.
+     */
+    public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new RuntimeException("Feature not ready.");
+            }
+            try {
+                mListener.onSmsReceived(token, format, pdu);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
+                SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+                if (message != null && message.mWrappedSmsMessage != null) {
+                    acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
+                            DELIVER_STATUS_ERROR_GENERIC);
+                } else {
+                    Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
+                    acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method should be triggered by the IMS providers to pass the result of the sent message
+     * to the platform.
+     *
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+     * @param status result of sending the SMS. Valid values are:
+     *  {@link #SEND_STATUS_OK},
+     *  {@link #SEND_STATUS_ERROR},
+     *  {@link #SEND_STATUS_ERROR_RETRY},
+     *  {@link #SEND_STATUS_ERROR_FALLBACK},
+     * @param reason reason in case status is failure. Valid values are:
+     *  {@link SmsManager#RESULT_ERROR_NONE},
+     *  {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
+     *  {@link SmsManager#RESULT_ERROR_RADIO_OFF},
+     *  {@link SmsManager#RESULT_ERROR_NULL_PDU},
+     *  {@link SmsManager#RESULT_ERROR_NO_SERVICE},
+     *  {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
+     *  {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
+     *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
+     *  {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
+     *  {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
+     *  {@link SmsManager#RESULT_NETWORK_REJECT},
+     *  {@link SmsManager#RESULT_INVALID_ARGUMENTS},
+     *  {@link SmsManager#RESULT_INVALID_STATE},
+     *  {@link SmsManager#RESULT_NO_MEMORY},
+     *  {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
+     *  {@link SmsManager#RESULT_SYSTEM_ERROR},
+     *  {@link SmsManager#RESULT_MODEM_ERROR},
+     *  {@link SmsManager#RESULT_NETWORK_ERROR},
+     *  {@link SmsManager#RESULT_ENCODING_ERROR},
+     *  {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
+     *  {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
+     *  {@link SmsManager#RESULT_INTERNAL_ERROR},
+     *  {@link SmsManager#RESULT_NO_RESOURCES},
+     *  {@link SmsManager#RESULT_CANCELLED},
+     *  {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+     *
+     * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+     * connection to the framework is not available. If this happens attempting to send the SMS
+     * should be aborted.
+     */
+    public final void onSendSmsResult(int token, int messageRef,  @SendStatusResult int status,
+            int reason) throws RuntimeException {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new RuntimeException("Feature not ready.");
+            }
+            try {
+                mListener.onSendSmsResult(token, messageRef, status, reason);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Sets the status report of the sent message.
+     *
+     * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+     * @param messageRef the message reference.
+     * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+     * {@link SmsMessage#FORMAT_3GPP2}.
+     * @param pdu PDUs representing the content of the status report.
+     * @throws RuntimeException if called before {@link #onReady()} is triggered
+     */
+    public final void onSmsStatusReportReceived(int token, int messageRef, String format,
+            byte[] pdu) throws RuntimeException{
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new RuntimeException("Feature not ready.");
+            }
+            try {
+                mListener.onSmsStatusReportReceived(token, messageRef, format, pdu);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage());
+                acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR);
+            }
+        }
+    }
+
+    /**
+     * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
+     * Provider.
+     *
+     * @return  the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
+     * {@link SmsMessage#FORMAT_3GPP2}.
+     */
+    public String getSmsFormat() {
+      return SmsMessage.FORMAT_3GPP;
+    }
+
+    /**
+     * Called when ImsSmsImpl has been initialized and communication with the framework is set up.
+     * Any attempt by this class to access the framework before this method is called will return
+     * with a {@link RuntimeException}.
+     */
+    public void onReady() {
+        // Base Implementation - Should be overridden
+    }
+}
diff --git a/android/telephony/ims/stub/ImsUtImplBase.java b/android/telephony/ims/stub/ImsUtImplBase.java
index 054a8b2..fcd7faf 100644
--- a/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/android/telephony/ims/stub/ImsUtImplBase.java
@@ -16,177 +16,332 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.telephony.ims.ImsUtListener;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
 
 /**
- * Base implementation of ImsUt, which implements stub versions of the methods
- * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsUt maintained by other ImsServices.
- *
- * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ * Base implementation of IMS UT interface, which implements stubs. Override these methods to
+ * implement functionality.
  *
  * @hide
  */
+// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+// will break other implementations of ImsUt maintained by other ImsServices.
+@SystemApi
+public class ImsUtImplBase {
 
-public class ImsUtImplBase extends IImsUt.Stub {
+    private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+        @Override
+        public void close() throws RemoteException {
+            ImsUtImplBase.this.close();
+        }
+
+        @Override
+        public int queryCallBarring(int cbType) throws RemoteException {
+            return ImsUtImplBase.this.queryCallBarring(cbType);
+        }
+
+        @Override
+        public int queryCallForward(int condition, String number) throws RemoteException {
+            return ImsUtImplBase.this.queryCallForward(condition, number);
+        }
+
+        @Override
+        public int queryCallWaiting() throws RemoteException {
+            return ImsUtImplBase.this.queryCallWaiting();
+        }
+
+        @Override
+        public int queryCLIR() throws RemoteException {
+            return ImsUtImplBase.this.queryCLIR();
+        }
+
+        @Override
+        public int queryCLIP() throws RemoteException {
+            return ImsUtImplBase.this.queryCLIP();
+        }
+
+        @Override
+        public int queryCOLR() throws RemoteException {
+            return ImsUtImplBase.this.queryCOLR();
+        }
+
+        @Override
+        public int queryCOLP() throws RemoteException {
+            return ImsUtImplBase.this.queryCOLP();
+        }
+
+        @Override
+        public int transact(Bundle ssInfo) throws RemoteException {
+            return ImsUtImplBase.this.transact(ssInfo);
+        }
+
+        @Override
+        public int updateCallBarring(int cbType, int action, String[] barrList) throws
+                RemoteException {
+            return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+        }
+
+        @Override
+        public int updateCallForward(int action, int condition, String number, int serviceClass,
+                int timeSeconds) throws RemoteException {
+            return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
+                    timeSeconds);
+        }
+
+        @Override
+        public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+            return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+        }
+
+        @Override
+        public int updateCLIR(int clirMode) throws RemoteException {
+            return ImsUtImplBase.this.updateCLIR(clirMode);
+        }
+
+        @Override
+        public int updateCLIP(boolean enable) throws RemoteException {
+            return ImsUtImplBase.this.updateCLIP(enable);
+        }
+
+        @Override
+        public int updateCOLR(int presentation) throws RemoteException {
+            return ImsUtImplBase.this.updateCOLR(presentation);
+        }
+
+        @Override
+        public int updateCOLP(boolean enable) throws RemoteException {
+            return ImsUtImplBase.this.updateCOLP(enable);
+        }
+
+        @Override
+        public void setListener(IImsUtListener listener) throws RemoteException {
+            ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+        }
+
+        @Override
+        public int queryCallBarringForServiceClass(int cbType, int serviceClass)
+                throws RemoteException {
+            return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+        }
+
+        @Override
+        public int updateCallBarringForServiceClass(int cbType, int action,
+                String[] barrList, int serviceClass) throws RemoteException {
+            return ImsUtImplBase.this.updateCallBarringForServiceClass(
+                    cbType, action, barrList, serviceClass);
+        }
+    };
 
     /**
-     * Closes the object. This object is not usable after being closed.
+     * Called when the framework no longer needs to interact with the IMS UT implementation any
+     * longer.
      */
-    @Override
-    public void close() throws RemoteException {
+    public void close() {
 
     }
 
     /**
-     * Retrieves the configuration of the call barring.
+     * Retrieves the call barring configuration.
+     * @param cbType
      */
-    @Override
-    public int queryCallBarring(int cbType) throws RemoteException {
+    public int queryCallBarring(int cbType) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call barring for specified service class.
      */
-    @Override
-    public int queryCallBarringForServiceClass(int cbType, int serviceClass)
-            throws RemoteException {
+    public int queryCallBarringForServiceClass(int cbType, int serviceClass) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call forward.
      */
-    @Override
-    public int queryCallForward(int condition, String number) throws RemoteException {
+    public int queryCallForward(int condition, String number) {
         return -1;
     }
 
     /**
      * Retrieves the configuration of the call waiting.
      */
-    @Override
-    public int queryCallWaiting() throws RemoteException {
+    public int queryCallWaiting() {
         return -1;
     }
 
     /**
      * Retrieves the default CLIR setting.
+     * @hide
      */
-    @Override
-    public int queryCLIR() throws RemoteException {
+    public int queryCLIR() {
+        return queryClir();
+    }
+
+    /**
+     * Retrieves the CLIP call setting.
+     * @hide
+     */
+    public int queryCLIP() {
+        return queryClip();
+    }
+
+    /**
+     * Retrieves the COLR call setting.
+     * @hide
+     */
+    public int queryCOLR() {
+        return queryColr();
+    }
+
+    /**
+     * Retrieves the COLP call setting.
+     * @hide
+     */
+    public int queryCOLP() {
+        return queryColp();
+    }
+
+    /**
+     * Retrieves the default CLIR setting.
+     */
+    public int queryClir() {
         return -1;
     }
 
     /**
      * Retrieves the CLIP call setting.
      */
-    @Override
-    public int queryCLIP() throws RemoteException {
+    public int queryClip() {
         return -1;
     }
 
     /**
      * Retrieves the COLR call setting.
      */
-    @Override
-    public int queryCOLR() throws RemoteException {
+    public int queryColr() {
         return -1;
     }
 
     /**
      * Retrieves the COLP call setting.
      */
-    @Override
-    public int queryCOLP() throws RemoteException {
+    public int queryColp() {
         return -1;
     }
 
     /**
      * Updates or retrieves the supplementary service configuration.
      */
-    @Override
-    public int transact(Bundle ssInfo) throws RemoteException {
+    public int transact(Bundle ssInfo) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call barring.
      */
-    @Override
-    public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+    public int updateCallBarring(int cbType, int action, String[] barrList) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call barring for specified service class.
      */
-    @Override
     public int updateCallBarringForServiceClass(int cbType, int action, String[] barrList,
-            int serviceClass) throws RemoteException {
+            int serviceClass) {
         return -1;
     }
 
     /**
      * Updates the configuration of the call forward.
      */
-    @Override
     public int updateCallForward(int action, int condition, String number, int serviceClass,
-            int timeSeconds) throws RemoteException {
+            int timeSeconds) {
         return 0;
     }
 
     /**
      * Updates the configuration of the call waiting.
      */
-    @Override
-    public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+    public int updateCallWaiting(boolean enable, int serviceClass) {
         return -1;
     }
 
     /**
      * Updates the configuration of the CLIR supplementary service.
+     * @hide
      */
-    @Override
-    public int updateCLIR(int clirMode) throws RemoteException {
+    public int updateCLIR(int clirMode) {
+        return updateClir(clirMode);
+    }
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     * @hide
+     */
+    public int updateCLIP(boolean enable) {
+        return updateClip(enable);
+    }
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     * @hide
+     */
+    public int updateCOLR(int presentation) {
+        return updateColr(presentation);
+    }
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     * @hide
+     */
+    public int updateCOLP(boolean enable) {
+        return updateColp(enable);
+    }
+
+    /**
+     * Updates the configuration of the CLIR supplementary service.
+     */
+    public int updateClir(int clirMode) {
         return -1;
     }
 
     /**
      * Updates the configuration of the CLIP supplementary service.
      */
-    @Override
-    public int updateCLIP(boolean enable) throws RemoteException {
+    public int updateClip(boolean enable) {
         return -1;
     }
 
     /**
      * Updates the configuration of the COLR supplementary service.
      */
-    @Override
-    public int updateCOLR(int presentation) throws RemoteException {
+    public int updateColr(int presentation) {
         return -1;
     }
 
     /**
      * Updates the configuration of the COLP supplementary service.
      */
-    @Override
-    public int updateCOLP(boolean enable) throws RemoteException {
+    public int updateColp(boolean enable) {
         return -1;
     }
 
     /**
      * Sets the listener.
      */
-    @Override
-    public void setListener(IImsUtListener listener) throws RemoteException {
+    public void setListener(ImsUtListener listener) {
+    }
+
+    /**
+     * @hide
+     */
+    public IImsUt getInterface() {
+        return mServiceImpl;
     }
 }
diff --git a/android/telephony/ims/stub/ImsUtListenerImplBase.java b/android/telephony/ims/stub/ImsUtListenerImplBase.java
deleted file mode 100644
index daa74c8..0000000
--- a/android/telephony/ims/stub/ImsUtListenerImplBase.java
+++ /dev/null
@@ -1,95 +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.telephony.ims.stub;
-
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import com.android.ims.ImsCallForwardInfo;
-import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsSsData;
-import com.android.ims.ImsSsInfo;
-import com.android.ims.internal.IImsUt;
-import com.android.ims.internal.IImsUtListener;
-
-/**
- * Base implementation of ImsUtListener, which implements stub versions of the methods
- * in the IImsUtListener AIDL. Override the methods that your implementation of
- * ImsUtListener supports.
- *
- * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
- * will break other implementations of ImsUtListener maintained by other ImsServices.
- *
- * @hide
- */
-
-public class ImsUtListenerImplBase extends IImsUtListener.Stub {
-
-    /**
-     * Notifies the result of the supplementary service configuration udpate.
-     */
-    @Override
-    public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
-    }
-
-    @Override
-    public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
-            throws RemoteException {
-    }
-
-    /**
-     * Notifies the result of the supplementary service configuration query.
-     */
-    @Override
-    public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
-    }
-
-    @Override
-    public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
-            throws RemoteException {
-    }
-
-    /**
-     * Notifies the status of the call barring supplementary service.
-     */
-    @Override
-    public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
-            throws RemoteException {
-    }
-
-    /**
-     * Notifies the status of the call forwarding supplementary service.
-     */
-    @Override
-    public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
-            throws RemoteException {
-    }
-
-    /**
-     * Notifies the status of the call waiting supplementary service.
-     */
-    @Override
-    public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
-            throws RemoteException {
-    }
-
-    /**
-     * Notifies client when Supplementary Service indication is received
-     */
-    @Override
-    public void onSupplementaryServiceIndication(ImsSsData ssData) {}
-}
diff --git a/android/telephony/mbms/DownloadProgressListener.java b/android/telephony/mbms/DownloadProgressListener.java
new file mode 100644
index 0000000..4301cb1
--- /dev/null
+++ b/android/telephony/mbms/DownloadProgressListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadProgressListener {
+    /**
+     * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param currentDownloadSize is the current amount downloaded.
+     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
+     *   This may be different from the decoded final size, but is useful in gauging download
+     *   progress.
+     * @param currentDecodedSize is the number of bytes that have been decoded.
+     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
+     */
+    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+            int currentDownloadSize, int fullDownloadSize,
+            int currentDecodedSize, int fullDecodedSize) {
+    }
+}
diff --git a/android/telephony/mbms/DownloadRequest.java b/android/telephony/mbms/DownloadRequest.java
index f0d60b6..602c796 100644
--- a/android/telephony/mbms/DownloadRequest.java
+++ b/android/telephony/mbms/DownloadRequest.java
@@ -27,10 +27,13 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.File;
 import java.io.IOException;
+import java.io.ObjectInput;
 import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
-import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -54,34 +57,116 @@
     public static final int MAX_DESTINATION_URI_SIZE = 50000;
 
     /** @hide */
-    private static class OpaqueDataContainer implements Serializable {
-        private final String appIntent;
-        private final int version;
+    private static class SerializationDataContainer implements Externalizable {
+        private String fileServiceId;
+        private Uri source;
+        private Uri destination;
+        private int subscriptionId;
+        private String appIntent;
+        private int version;
 
-        public OpaqueDataContainer(String appIntent, int version) {
-            this.appIntent = appIntent;
-            this.version = version;
+        public SerializationDataContainer() {}
+
+        SerializationDataContainer(DownloadRequest request) {
+            fileServiceId = request.fileServiceId;
+            source = request.sourceUri;
+            destination = request.destinationUri;
+            subscriptionId = request.subscriptionId;
+            appIntent = request.serializedResultIntentForApp;
+            version = request.version;
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput objectOutput) throws IOException {
+            objectOutput.write(version);
+            objectOutput.writeUTF(fileServiceId);
+            objectOutput.writeUTF(source.toString());
+            objectOutput.writeUTF(destination.toString());
+            objectOutput.write(subscriptionId);
+            objectOutput.writeUTF(appIntent);
+        }
+
+        @Override
+        public void readExternal(ObjectInput objectInput) throws IOException {
+            version = objectInput.read();
+            fileServiceId = objectInput.readUTF();
+            source = Uri.parse(objectInput.readUTF());
+            destination = Uri.parse(objectInput.readUTF());
+            subscriptionId = objectInput.read();
+            appIntent = objectInput.readUTF();
+            // Do version checks here -- future versions may have other fields.
         }
     }
 
     public static class Builder {
         private String fileServiceId;
         private Uri source;
+        private Uri destination;
         private int subscriptionId;
         private String appIntent;
         private int version = CURRENT_VERSION;
 
+        /**
+         * Constructs a {@link Builder} from a {@link DownloadRequest}
+         * @param other The {@link DownloadRequest} from which the data for the {@link Builder}
+         *              should come.
+         * @return An instance of {@link Builder} pre-populated with data from the provided
+         *         {@link DownloadRequest}.
+         */
+        public static Builder fromDownloadRequest(DownloadRequest other) {
+            Builder result = new Builder(other.sourceUri, other.destinationUri)
+                    .setServiceId(other.fileServiceId)
+                    .setSubscriptionId(other.subscriptionId);
+            result.appIntent = other.serializedResultIntentForApp;
+            // Version of the result is going to be the current version -- as this class gets
+            // updated, new fields will be set to default values in here.
+            return result;
+        }
+
+        /**
+         * This method constructs a new instance of {@link Builder} based on the serialized data
+         * passed in.
+         * @param data A byte array, the contents of which should have been originally obtained
+         *             from {@link DownloadRequest#toByteArray()}.
+         */
+        public static Builder fromSerializedRequest(byte[] data) {
+            Builder builder;
+            try {
+                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+                SerializationDataContainer dataContainer =
+                        (SerializationDataContainer) stream.readObject();
+                builder = new Builder(dataContainer.source, dataContainer.destination);
+                builder.version = dataContainer.version;
+                builder.appIntent = dataContainer.appIntent;
+                builder.fileServiceId = dataContainer.fileServiceId;
+                builder.subscriptionId = dataContainer.subscriptionId;
+            } catch (IOException e) {
+                // Really should never happen
+                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            } catch (ClassNotFoundException e) {
+                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            }
+            return builder;
+        }
 
         /**
          * Builds a new DownloadRequest.
          * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
          *     never be null.
+         * @param destinationUri The final location for the file(s) that are to be downloaded. It
+         *     must be on the same filesystem as the temp file directory set via
+         *     {@link android.telephony.MbmsDownloadSession#setTempFileRootDirectory(File)}.
+         *     The provided path must be a directory that exists. An
+         *     {@link IllegalArgumentException} will be thrown otherwise.
          */
-        public Builder(@NonNull Uri sourceUri) {
-            if (sourceUri == null) {
-                throw new IllegalArgumentException("Source URI must be non-null.");
+        public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri) {
+            if (sourceUri == null || destinationUri == null) {
+                throw new IllegalArgumentException("Source and destination URIs must be non-null.");
             }
             source = sourceUri;
+            destination = destinationUri;
         }
 
         /**
@@ -130,68 +215,34 @@
             return this;
         }
 
-        /**
-         * For use by the middleware to set the byte array of opaque data. The opaque data
-         * includes information about the download request that is used by the client app and the
-         * manager code, but is irrelevant to the middleware.
-         * @param data A byte array, the contents of which should have been originally obtained
-         *             from {@link DownloadRequest#getOpaqueData()}.
-         * @hide
-         */
-        @SystemApi
-        public Builder setOpaqueData(byte[] data) {
-            try {
-                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
-                OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
-                version = dataContainer.version;
-                appIntent = dataContainer.appIntent;
-            } catch (IOException e) {
-                // Really should never happen
-                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
-                throw new IllegalArgumentException(e);
-            } catch (ClassNotFoundException e) {
-                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
-                throw new IllegalArgumentException(e);
-            }
-            return this;
-        }
-
         public DownloadRequest build() {
-            return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
+            return new DownloadRequest(fileServiceId, source, destination,
+                    subscriptionId, appIntent, version);
         }
     }
 
     private final String fileServiceId;
     private final Uri sourceUri;
+    private final Uri destinationUri;
     private final int subscriptionId;
     private final String serializedResultIntentForApp;
     private final int version;
 
     private DownloadRequest(String fileServiceId,
-            Uri source, int sub,
+            Uri source, Uri destination, int sub,
             String appIntent, int version) {
         this.fileServiceId = fileServiceId;
         sourceUri = source;
         subscriptionId = sub;
+        destinationUri = destination;
         serializedResultIntentForApp = appIntent;
         this.version = version;
     }
 
-    public static DownloadRequest copy(DownloadRequest other) {
-        return new DownloadRequest(other);
-    }
-
-    private DownloadRequest(DownloadRequest dr) {
-        fileServiceId = dr.fileServiceId;
-        sourceUri = dr.sourceUri;
-        subscriptionId = dr.subscriptionId;
-        serializedResultIntentForApp = dr.serializedResultIntentForApp;
-        version = dr.version;
-    }
-
     private DownloadRequest(Parcel in) {
         fileServiceId = in.readString();
         sourceUri = in.readParcelable(getClass().getClassLoader());
+        destinationUri = in.readParcelable(getClass().getClassLoader());
         subscriptionId = in.readInt();
         serializedResultIntentForApp = in.readString();
         version = in.readInt();
@@ -204,6 +255,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(fileServiceId);
         out.writeParcelable(sourceUri, flags);
+        out.writeParcelable(destinationUri, flags);
         out.writeInt(subscriptionId);
         out.writeString(serializedResultIntentForApp);
         out.writeInt(version);
@@ -224,6 +276,13 @@
     }
 
     /**
+     * @return The destination {@link Uri} of the downloaded file.
+     */
+    public Uri getDestinationUri() {
+        return destinationUri;
+    }
+
+    /**
      * @return The subscription ID on which to perform MBMS operations.
      */
     public int getSubscriptionId() {
@@ -244,19 +303,16 @@
     }
 
     /**
-     * For use by the middleware only. The byte array returned from this method should be
-     * persisted and sent back to the app upon download completion or failure by passing it into
-     * {@link Builder#setOpaqueData(byte[])}.
-     * @return A byte array of opaque data to persist.
-     * @hide
+     * This method returns a byte array that may be persisted to disk and restored to a
+     * {@link DownloadRequest}. The instance of {@link DownloadRequest} persisted by this method
+     * may be recovered via {@link Builder#fromSerializedRequest(byte[])}.
+     * @return A byte array of data to persist.
      */
-    @SystemApi
-    public byte[] getOpaqueData() {
+    public byte[] toByteArray() {
         try {
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
-            OpaqueDataContainer container = new OpaqueDataContainer(
-                    serializedResultIntentForApp, version);
+            SerializationDataContainer container = new SerializationDataContainer(this);
             stream.writeObject(container);
             stream.flush();
             return byteArrayOutputStream.toByteArray();
@@ -299,15 +355,6 @@
     }
 
     /**
-     * @hide
-     */
-    public boolean isMultipartDownload() {
-        // TODO: figure out what qualifies a request as a multipart download request.
-        return getSourceUri().getLastPathSegment() != null &&
-                getSourceUri().getLastPathSegment().contains("*");
-    }
-
-    /**
      * Retrieves the hash string that should be used as the filename when storing a token for
      * this DownloadRequest.
      * @hide
@@ -320,8 +367,9 @@
             throw new RuntimeException("Could not get sha256 hash object");
         }
         if (version >= 1) {
-            // Hash the source URI and the app intent
+            // Hash the source, destination, and the app intent
             digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
+            digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
             if (serializedResultIntentForApp != null) {
                 digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
             }
@@ -344,12 +392,13 @@
                 version == request.version &&
                 Objects.equals(fileServiceId, request.fileServiceId) &&
                 Objects.equals(sourceUri, request.sourceUri) &&
+                Objects.equals(destinationUri, request.destinationUri) &&
                 Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(fileServiceId, sourceUri,
+        return Objects.hash(fileServiceId, sourceUri, destinationUri,
                 subscriptionId, serializedResultIntentForApp, version);
     }
 }
diff --git a/android/telephony/mbms/DownloadStateCallback.java b/android/telephony/mbms/DownloadStateCallback.java
deleted file mode 100644
index 9f60cc3..0000000
--- a/android/telephony/mbms/DownloadStateCallback.java
+++ /dev/null
@@ -1,131 +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.telephony.mbms;
-
-import android.annotation.IntDef;
-import android.telephony.MbmsDownloadSession;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A optional listener class used by download clients to track progress. Apps should extend this
- * class and pass an instance into
- * {@link MbmsDownloadSession#download(DownloadRequest)}
- *
- * This is optionally specified when requesting a download and will only be called while the app
- * is running.
- */
-public class DownloadStateCallback {
-
-    /**
-     * Bitmask flags used for filtering out callback methods. Used when constructing the
-     * DownloadStateCallback as an optional parameter.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
-    public @interface FilterFlag {}
-
-    /**
-     * Receive all callbacks.
-     * Default value.
-     */
-    public static final int ALL_UPDATES = 0x00;
-    /**
-     * Receive callbacks for {@link #onProgressUpdated}.
-     */
-    public static final int PROGRESS_UPDATES = 0x01;
-    /**
-     * Receive callbacks for {@link #onStateUpdated}.
-     */
-    public static final int STATE_UPDATES = 0x02;
-
-    private final int mCallbackFilterFlags;
-
-    /**
-     * Creates a DownloadStateCallback that will receive all callbacks.
-     */
-    public DownloadStateCallback() {
-        mCallbackFilterFlags = ALL_UPDATES;
-    }
-
-    /**
-     * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
-     * via the filterFlags parameter.
-     * @param filterFlags A bitmask of filter flags that will specify which callback this instance
-     *     is interested in.
-     */
-    public DownloadStateCallback(int filterFlags) {
-        mCallbackFilterFlags = filterFlags;
-    }
-
-    /**
-     * Return the currently set filter flags.
-     * @return An integer containing the bitmask of flags that this instance is interested in.
-     * @hide
-     */
-    public int getCallbackFilterFlags() {
-        return mCallbackFilterFlags;
-    }
-
-    /**
-     * Returns true if a filter flag is set for a particular callback method. If the flag is set,
-     * the callback will be delivered to the listening process.
-     * @param flag A filter flag specifying whether or not a callback method is registered to
-     *     receive callbacks.
-     * @return true if registered to receive callbacks in the listening process, false if not.
-     */
-    public final boolean isFilterFlagSet(@FilterFlag int flag) {
-        if (mCallbackFilterFlags == ALL_UPDATES) {
-            return true;
-        }
-        return (mCallbackFilterFlags & flag) > 0;
-    }
-
-    /**
-     * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
-     *
-     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
-     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
-     *   the request may result in many files being downloaded and the client
-     *   may not have been able to get a list of them in advance.
-     * @param currentDownloadSize is the current amount downloaded.
-     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
-     *   This may be different from the decoded final size, but is useful in gauging download
-     *   progress.
-     * @param currentDecodedSize is the number of bytes that have been decoded.
-     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
-     */
-    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
-            int currentDownloadSize, int fullDownloadSize,
-            int currentDecodedSize, int fullDecodedSize) {
-    }
-
-    /**
-     * Gives download state callbacks for a file in a {@link DownloadRequest}.
-     *
-     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
-     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
-     *   the request may result in many files being downloaded and the client
-     *   may not have been able to get a list of them in advance.
-     * @param state The current state of the download.
-     */
-    public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-            @MbmsDownloadSession.DownloadStatus int state) {
-    }
-}
diff --git a/android/telephony/mbms/DownloadStatusListener.java b/android/telephony/mbms/DownloadStatusListener.java
new file mode 100644
index 0000000..ca77932
--- /dev/null
+++ b/android/telephony/mbms/DownloadStatusListener.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.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.telephony.MbmsDownloadSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadStatusListener {
+    /**
+     * Gives download status callbacks for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param status The current status of the download.
+     */
+    public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus int status) {
+    }
+}
diff --git a/android/telephony/mbms/InternalDownloadProgressListener.java b/android/telephony/mbms/InternalDownloadProgressListener.java
new file mode 100644
index 0000000..403f758
--- /dev/null
+++ b/android/telephony/mbms/InternalDownloadProgressListener.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class InternalDownloadProgressListener extends IDownloadProgressListener.Stub {
+    private final Executor mExecutor;
+    private final DownloadProgressListener mAppListener;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadProgressListener(DownloadProgressListener appListener,
+            Executor executor) {
+        mAppListener = appListener;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onProgressUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
+            final int fullDecodedSize) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android/telephony/mbms/InternalDownloadSessionCallback.java b/android/telephony/mbms/InternalDownloadSessionCallback.java
index a7a5958..2916f81 100644
--- a/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -16,70 +16,81 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
-import android.os.RemoteException;
+import android.os.Binder;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
 
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsDownloadSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
-    public void onError(final int errorCode, final String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) {
         if (mIsStopped) {
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
     @Override
-    public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+    public void onFileServicesUpdated(final List<FileServiceInfo> services) {
         if (mIsStopped) {
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onFileServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onFileServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
     @Override
-    public void onMiddlewareReady() throws RemoteException {
+    public void onMiddlewareReady() {
         if (mIsStopped) {
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/android/telephony/mbms/InternalDownloadStateCallback.java b/android/telephony/mbms/InternalDownloadStateCallback.java
deleted file mode 100644
index 8702952..0000000
--- a/android/telephony/mbms/InternalDownloadStateCallback.java
+++ /dev/null
@@ -1,70 +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.telephony.mbms;
-
-import android.os.Handler;
-import android.os.RemoteException;
-
-/**
- * @hide
- */
-public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
-    private final Handler mHandler;
-    private final DownloadStateCallback mAppCallback;
-    private volatile boolean mIsStopped = false;
-
-    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
-        mAppCallback = appCallback;
-        mHandler = handler;
-    }
-
-    @Override
-    public void onProgressUpdated(final DownloadRequest request, final FileInfo fileInfo,
-            final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
-            final int fullDecodedSize) throws RemoteException {
-        if (mIsStopped) {
-            return;
-        }
-
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
-                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
-            }
-        });
-    }
-
-    @Override
-    public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
-            final int state) throws RemoteException {
-        if (mIsStopped) {
-            return;
-        }
-
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mAppCallback.onStateUpdated(request, fileInfo, state);
-            }
-        });
-    }
-
-    public void stop() {
-        mIsStopped = true;
-    }
-}
diff --git a/android/telephony/mbms/InternalDownloadStatusListener.java b/android/telephony/mbms/InternalDownloadStatusListener.java
new file mode 100644
index 0000000..ad6bb54
--- /dev/null
+++ b/android/telephony/mbms/InternalDownloadStatusListener.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.telephony.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadSession;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStatusListener extends IDownloadStatusListener.Stub {
+    private final Executor mExecutor;
+    private final DownloadStatusListener mAppListener;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadStatusListener(DownloadStatusListener appCallback, Executor executor) {
+        mAppListener = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onStatusUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus final int status) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppListener.onStatusUpdated(request, fileInfo, status);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android/telephony/mbms/InternalStreamingServiceCallback.java b/android/telephony/mbms/InternalStreamingServiceCallback.java
index eb6579c..e9f39ff 100644
--- a/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -16,18 +16,21 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
+import java.util.concurrent.Executor;
+
 /** @hide */
 public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
     private final StreamingServiceCallback mAppCallback;
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private volatile boolean mIsStopped = false;
 
-    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback,
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -36,10 +39,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -50,10 +58,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamStateUpdated(state, reason);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamStateUpdated(state, reason);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -64,10 +77,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMediaDescriptionUpdated();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMediaDescriptionUpdated();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -78,10 +96,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -92,10 +115,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamMethodUpdated(methodType);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamMethodUpdated(methodType);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
diff --git a/android/telephony/mbms/InternalStreamingSessionCallback.java b/android/telephony/mbms/InternalStreamingSessionCallback.java
index d782d12..d47f5ad 100644
--- a/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -16,21 +16,22 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsStreamingSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -39,10 +40,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -54,10 +60,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamingServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamingServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -68,18 +79,19 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/android/telephony/mbms/MbmsDownloadReceiver.java b/android/telephony/mbms/MbmsDownloadReceiver.java
index 9ef188c..dd1061f 100644
--- a/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -21,24 +21,25 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.telephony.MbmsDownloadSession;
 import android.telephony.mbms.vendor.VendorUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.File;
 import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -62,6 +63,8 @@
     /** @hide */
     public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
 
+    private static final String EMBMS_INTENT_PERMISSION = "android.permission.SEND_EMBMS_INTENTS";
+
     /**
      * Indicates that the requested operation completed without error.
      * @hide
@@ -137,6 +140,8 @@
     /** @hide */
     @Override
     public void onReceive(Context context, Intent intent) {
+        verifyPermissionIntegrity(context);
+
         if (!verifyIntentContents(context, intent)) {
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
@@ -260,20 +265,21 @@
 
         FileInfo completedFileInfo =
                 (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
-        Path stagingDirectory = FileSystems.getDefault().getPath(
-                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
-                TEMP_FILE_STAGING_LOCATION);
+        Path appSpecifiedDestination = FileSystems.getDefault().getPath(
+                request.getDestinationUri().getPath());
 
-        Uri stagedFileLocation;
+        Uri finalLocation;
         try {
-            stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+            String relativeLocation = getFileRelativePath(request.getSourceUri().getPath(),
+                    completedFileInfo.getUri().getPath());
+            finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination,
+                    relativeLocation);
         } catch (IOException e) {
             Log.w(LOG_TAG, "Failed to move temp file to final destination");
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
             return;
         }
-        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
-                stagedFileLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalLocation);
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
 
         context.sendBroadcast(intentForApp);
@@ -296,7 +302,9 @@
         for (Uri tempFileUri : tempFiles) {
             if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
                 File tempFile = new File(tempFileUri.getSchemeSpecificPart());
-                tempFile.delete();
+                if (!tempFile.delete()) {
+                    Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+                }
             }
         }
     }
@@ -437,23 +445,57 @@
     }
 
     /*
-     * Moves a tempfile located at fromPath to a new location in the staging directory.
+     * Moves a tempfile located at fromPath to its final home where the app wants it
      */
-    private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
+    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath,
+            String relativeLocation) throws IOException {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
-            Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
+            Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
+                    " does not have a file scheme");
             return null;
         }
 
         Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
-        if (!Files.isDirectory(stagingDirectory)) {
-            Files.createDirectory(stagingDirectory);
+        Path toFile = appSpecifiedPath.resolve(relativeLocation);
+
+        if (!Files.isDirectory(toFile.getParent())) {
+            Files.createDirectories(toFile.getParent());
         }
-        Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
+        Path result = Files.move(fromFile, toFile,
+                StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
 
         return Uri.fromFile(result.toFile());
     }
 
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getFileRelativePath(String sourceUriPath, String fileInfoPath) {
+        if (sourceUriPath.endsWith("*")) {
+            // This is a wildcard path. Strip the last path component and use that as the root of
+            // the relative path.
+            int lastSlash = sourceUriPath.lastIndexOf('/');
+            sourceUriPath = sourceUriPath.substring(0, lastSlash);
+        }
+        if (!fileInfoPath.startsWith(sourceUriPath)) {
+            Log.e(LOG_TAG, "File location specified in FileInfo does not match the source URI."
+                    + " source: " + sourceUriPath + " fileinfo path: " + fileInfoPath);
+            return null;
+        }
+        if (fileInfoPath.length() == sourceUriPath.length()) {
+            // This is the single-file download case. Return the name of the file so that the
+            // receiver puts the file directly into the dest directory.
+            return sourceUriPath.substring(sourceUriPath.lastIndexOf('/') + 1);
+        }
+
+        String prefixOmittedPath = fileInfoPath.substring(sourceUriPath.length());
+        if (prefixOmittedPath.startsWith("/")) {
+            prefixOmittedPath = prefixOmittedPath.substring(1);
+        }
+        return prefixOmittedPath;
+    }
+
     private static boolean verifyTempFilePath(Context context, String serviceId,
             Uri filePath) {
         if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
@@ -470,6 +512,8 @@
 
         if (!MbmsUtils.isContainedIn(
                 MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+            Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," +
+                    " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId));
             return false;
         }
 
@@ -513,39 +557,29 @@
         return mMiddlewarePackageNameCache;
     }
 
-    private static boolean manualMove(File src, File dst) {
-        InputStream in = null;
-        OutputStream out = null;
-        try {
-            if (!dst.exists()) {
-                dst.createNewFile();
-            }
-            in = new FileInputStream(src);
-            out = new FileOutputStream(dst);
-            byte[] buffer = new byte[2048];
-            int len;
-            do {
-                len = in.read(buffer);
-                out.write(buffer, 0, len);
-            } while (len > 0);
-        } catch (IOException e) {
-            Log.w(LOG_TAG, "Manual file move failed due to exception "  + e);
-            if (dst.exists()) {
-                dst.delete();
-            }
-            return false;
-        } finally {
-            try {
-                if (in != null) {
-                    in.close();
-                }
-                if (out != null) {
-                    out.close();
-                }
-            } catch (IOException e) {
-                Log.w(LOG_TAG, "Error closing streams: " + e);
-            }
+    private void verifyPermissionIntegrity(Context context) {
+        PackageManager pm = context.getPackageManager();
+        Intent queryIntent = new Intent(context, MbmsDownloadReceiver.class);
+        List<ResolveInfo> infos = pm.queryBroadcastReceivers(queryIntent, 0);
+        if (infos.size() != 1) {
+            throw new IllegalStateException("Non-unique download receiver in your app");
         }
-        return true;
+        ActivityInfo selfInfo = infos.get(0).activityInfo;
+        if (selfInfo == null) {
+            throw new IllegalStateException("Queried ResolveInfo does not contain a receiver");
+        }
+        if (MbmsUtils.getOverrideServiceName(context,
+                MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION) != null) {
+            // If an override was specified, just make sure that the permission isn't null.
+            if (selfInfo.permission == null) {
+                throw new IllegalStateException(
+                        "MbmsDownloadReceiver must require some permission");
+            }
+            return;
+        }
+        if (!Objects.equals(EMBMS_INTENT_PERMISSION, selfInfo.permission)) {
+            throw new IllegalStateException("MbmsDownloadReceiver must require the " +
+                    "SEND_EMBMS_INTENTS permission.");
+        }
     }
 }
diff --git a/android/telephony/mbms/MbmsDownloadSessionCallback.java b/android/telephony/mbms/MbmsDownloadSessionCallback.java
index 77dea6f..5003b57 100644
--- a/android/telephony/mbms/MbmsDownloadSessionCallback.java
+++ b/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -16,8 +16,11 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.telephony.MbmsDownloadSession;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -25,6 +28,26 @@
  * cell-broadcast.
  */
 public class MbmsDownloadSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
+            MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
+            MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO}, prefix = { "ERROR_" })
+    private @interface DownloadError{}
 
     /**
      * Indicates that the middleware has encountered an asynchronous error.
@@ -32,7 +55,7 @@
      * @param message A message, intended for debugging purposes, describing the error in further
      *                detail.
      */
-    public void onError(int errorCode, String message) {
+    public void onError(@DownloadError int errorCode, String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/MbmsErrors.java b/android/telephony/mbms/MbmsErrors.java
index af0af24..7c4321b 100644
--- a/android/telephony/mbms/MbmsErrors.java
+++ b/android/telephony/mbms/MbmsErrors.java
@@ -19,6 +19,13 @@
 import android.telephony.MbmsStreamingSession;
 
 public class MbmsErrors {
+    /**
+     * Indicates that the middleware has sent an error code that is not defined in the version of
+     * the SDK targeted by your app. This is an illegal value for the middleware to return -- it
+     * should only ever be generated by the framework.
+     */
+    public static final int UNKNOWN = -1;
+
     /** Indicates that the operation was successful. */
     public static final int SUCCESS = 0;
 
@@ -108,8 +115,8 @@
 
         /**
          * Indicates that the app called
-         * {@link MbmsStreamingSession#startStreaming(
-         * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
+         * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
+         * java.util.concurrent.Executor, StreamingServiceCallback)}
          * more than once for the same {@link StreamingServiceInfo}.
          */
         public static final int ERROR_DUPLICATE_START_STREAM = 303;
@@ -128,6 +135,9 @@
 
         /** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */
         public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
+
+        /** Indicates the the middleware has no record of the supplied {@link FileInfo} */
+        public static final int ERROR_UNKNOWN_FILE_INFO = 403;
     }
 
     private MbmsErrors() {}
diff --git a/android/telephony/mbms/MbmsStreamingSessionCallback.java b/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 5c130a0..1bdb20b 100644
--- a/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -16,26 +16,50 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.os.Handler;
 import android.telephony.MbmsStreamingSession;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A callback class that is used to receive information from the middleware on MBMS streaming
  * services. An instance of this object should be passed into
- * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+ * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
  */
 public class MbmsStreamingSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+    private @interface StreamingError{}
+
     /**
      * Called by the middleware when it has detected an error condition. The possible error codes
      * are listed in {@link MbmsErrors}.
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, @Nullable String message) {
+    public void onError(@StreamingError int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/MbmsUtils.java b/android/telephony/mbms/MbmsUtils.java
index b4ad1d7..06b2120 100644
--- a/android/telephony/mbms/MbmsUtils.java
+++ b/android/telephony/mbms/MbmsUtils.java
@@ -50,7 +50,7 @@
         return new ComponentName(ci.packageName, ci.name);
     }
 
-    private static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+    public static ComponentName getOverrideServiceName(Context context, String serviceAction) {
         String metaDataKey = null;
         switch (serviceAction) {
             case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
@@ -130,8 +130,12 @@
      * Returns a File linked to the directory used to store temp files for this file service
      */
     public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
+        // Replace all non-alphanumerics/underscores with an underscore. Some filesystems don't
+        // like special characters.
+        String sanitizedServiceId = serviceId.replaceAll("[^a-zA-Z0-9_]", "_");
+
         File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
 
-        return new File(embmsTempFileDir, serviceId);
+        return new File(embmsTempFileDir, sanitizedServiceId);
     }
 }
diff --git a/android/telephony/mbms/StreamingService.java b/android/telephony/mbms/StreamingService.java
index ec9134a..b6239fe 100644
--- a/android/telephony/mbms/StreamingService.java
+++ b/android/telephony/mbms/StreamingService.java
@@ -29,11 +29,11 @@
 
 /**
  * Class used to represent a single MBMS stream. After a stream has been started with
- * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
- * StreamingServiceCallback, android.os.Handler)},
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo, java.util.concurrent.Executor,
+ * StreamingServiceCallback)},
  * this class is used to hold information about the stream and control it.
  */
-public class StreamingService {
+public class StreamingService implements AutoCloseable {
     private static final String LOG_TAG = "MbmsStreamingService";
 
     /**
@@ -41,7 +41,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
     public @interface StreamingState {}
     public final static int STATE_STOPPED = 1;
     public final static int STATE_STARTED = 2;
@@ -53,7 +53,8 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+    @IntDef(prefix = { "REASON_" },
+            value = {REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
             REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
             REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
     public @interface StreamingStateChangeReason {}
@@ -64,9 +65,9 @@
     public static final int REASON_NONE = 0;
 
     /**
-     * State changed due to a call to {@link #stopStreaming()} or
+     * State changed due to a call to {@link #close()} or
      * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
-     * StreamingServiceCallback, android.os.Handler)}
+     * java.util.concurrent.Executor, StreamingServiceCallback)}
      */
     public static final int REASON_BY_USER_REQUEST = 1;
 
@@ -161,7 +162,8 @@
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      */
-    public void stopStreaming() {
+    @Override
+    public void close() {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
         }
diff --git a/android/telephony/mbms/StreamingServiceCallback.java b/android/telephony/mbms/StreamingServiceCallback.java
index 0903824..c265db6 100644
--- a/android/telephony/mbms/StreamingServiceCallback.java
+++ b/android/telephony/mbms/StreamingServiceCallback.java
@@ -16,13 +16,34 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A callback class for use when the application is actively streaming content. The middleware
  * will provide updates on the status of the stream via this callback.
  */
 public class StreamingServiceCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+    private @interface StreamingServiceError{}
 
     /**
      * Indicates broadcast signal strength is not available for this service.
@@ -39,7 +60,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, @Nullable String message) {
+    public void onError(@StreamingServiceError int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 4fee3df..a9f10b1 100644
--- a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -24,11 +24,13 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
-import android.telephony.mbms.IDownloadStateCallback;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IDownloadStatusListener;
 import android.telephony.mbms.IMbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsErrors;
@@ -45,47 +47,50 @@
 @SystemApi
 @TestApi
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
-    private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+    private final Map<IBinder, DownloadStatusListener> mDownloadStatusListenerBinderMap =
+            new HashMap<>();
+    private final Map<IBinder, DownloadProgressListener> mDownloadProgressListenerBinderMap =
+            new HashMap<>();
     private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
 
+    private abstract static class VendorDownloadStatusListener extends DownloadStatusListener {
+        private final IDownloadStatusListener mListener;
+        public VendorDownloadStatusListener(IDownloadStatusListener listener) {
+            mListener = listener;
+        }
 
-    // Filters the DownloadStateCallbacks by its configuration from the app.
-    private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+        @Override
+        public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+                @MbmsDownloadSession.DownloadStatus int state) {
+            try {
+                mListener.onStatusUpdated(request, fileInfo, state);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
 
-        private final IDownloadStateCallback mCallback;
-        public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
-            super(callbackFlags);
-            mCallback = callback;
+        protected abstract void onRemoteException(RemoteException e);
+    }
+
+    private abstract static class VendorDownloadProgressListener extends DownloadProgressListener {
+        private final IDownloadProgressListener mListener;
+
+        public VendorDownloadProgressListener(IDownloadProgressListener listener) {
+            mListener = listener;
         }
 
         @Override
         public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
                 int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
                 int fullDecodedSize) {
-            if (!isFilterFlagSet(PROGRESS_UPDATES)) {
-                return;
-            }
             try {
-                mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                mListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
                         fullDownloadSize, currentDecodedSize, fullDecodedSize);
             } catch (RemoteException e) {
                 onRemoteException(e);
             }
         }
 
-        @Override
-        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-                @MbmsDownloadSession.DownloadStatus int state) {
-            if (!isFilterFlagSet(STATE_UPDATES)) {
-                return;
-            }
-            try {
-                mCallback.onStateUpdated(request, fileInfo, state);
-            } catch (RemoteException e) {
-                onRemoteException(e);
-            }
-        }
-
         protected abstract void onRemoteException(RemoteException e);
     }
 
@@ -125,6 +130,10 @@
             @Override
             public void onError(int errorCode, String message) {
                 try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
@@ -223,71 +232,70 @@
     }
 
     /**
-     * Registers a download state callbacks for the provided {@link DownloadRequest}.
+     * Registers a download status listener for the provided {@link DownloadRequest}.
      *
-     * This method is called by the app when it wants to request updates on the progress or
-     * status of the download.
+     * This method is called by the app when it wants to request updates on the status of
+     * the download.
      *
      * If the middleware is not aware of a download having been requested with the provided
-     *
      * {@link DownloadRequest} in the past,
      * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
      * must be returned.
      *
      * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
      *                        for which progress updates are being requested.
-     * @param callback The callback object to use.
+     * @param listener The listener object to use.
      */
-    public int registerStateCallback(DownloadRequest downloadRequest,
-            DownloadStateCallback callback) throws RemoteException {
+    public int addStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
         return 0;
     }
 
     /**
-     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
      * @hide
      */
     @Override
-    public final int registerStateCallback(final DownloadRequest downloadRequest,
-            final IDownloadStateCallback callback, int flags) throws RemoteException {
+    public final int addStatusListener(final DownloadRequest downloadRequest,
+            final IDownloadStatusListener listener) throws RemoteException {
         final int uid = Binder.getCallingUid();
         if (downloadRequest == null) {
             throw new NullPointerException("Download request must not be null");
         }
-        if (callback == null) {
+        if (listener == null) {
             throw new NullPointerException("Callback must not be null");
         }
 
-        DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
+        DownloadStatusListener exposedCallback = new VendorDownloadStatusListener(listener) {
             @Override
             protected void onRemoteException(RemoteException e) {
                 onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
             }
         };
 
-        int result = registerStateCallback(downloadRequest, exposedCallback);
+        int result = addStatusListener(downloadRequest, exposedCallback);
 
         if (result == MbmsErrors.SUCCESS) {
             DeathRecipient deathRecipient = new DeathRecipient() {
                 @Override
                 public void binderDied() {
                     onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
-                    mDownloadCallbackBinderMap.remove(callback.asBinder());
-                    mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                    mDownloadStatusListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
                 }
             };
-            mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
-            callback.asBinder().linkToDeath(deathRecipient, 0);
-            mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadStatusListenerBinderMap.put(listener.asBinder(), exposedCallback);
         }
 
         return result;
     }
 
     /**
-     * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+     * Un-registers a download status listener for the provided {@link DownloadRequest}.
      *
-     * This method is called by the app when it no longer wants to request updates on the
+     * This method is called by the app when it no longer wants to request status updates on the
      * download.
      *
      * If the middleware is not aware of a download having been requested with the provided
@@ -296,45 +304,157 @@
      * must be returned.
      *
      * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
-     * @param callback The callback object that
-     *                 {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+     * @param listener The callback object that
+     *                 {@link #addStatusListener(DownloadRequest, DownloadStatusListener)}
      *                 was called with.
      */
-    public int unregisterStateCallback(DownloadRequest downloadRequest,
-            DownloadStateCallback callback) throws RemoteException {
+    public int removeStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
         return 0;
     }
 
     /**
-     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
      * @hide
      */
-    @Override
-    public final int unregisterStateCallback(
-            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+    public final int removeStatusListener(
+            final DownloadRequest downloadRequest, final IDownloadStatusListener listener)
             throws RemoteException {
         if (downloadRequest == null) {
             throw new NullPointerException("Download request must not be null");
         }
-        if (callback == null) {
+        if (listener == null) {
             throw new NullPointerException("Callback must not be null");
         }
 
         DeathRecipient deathRecipient =
-                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
         if (deathRecipient == null) {
-            throw new IllegalArgumentException("Unknown callback");
+            throw new IllegalArgumentException("Unknown listener");
         }
 
-        callback.asBinder().unlinkToDeath(deathRecipient, 0);
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
 
-        DownloadStateCallback exposedCallback =
-                mDownloadCallbackBinderMap.remove(callback.asBinder());
+        DownloadStatusListener exposedCallback =
+                mDownloadStatusListenerBinderMap.remove(listener.asBinder());
         if (exposedCallback == null) {
-            throw new IllegalArgumentException("Unknown callback");
+            throw new IllegalArgumentException("Unknown listener");
         }
 
-        return unregisterStateCallback(downloadRequest, exposedCallback);
+        return removeStatusListener(downloadRequest, exposedCallback);
+    }
+
+    /**
+     * Registers a download progress listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it wants to request updates on the progress of
+     * the download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+     *                        for which progress updates are being requested.
+     * @param listener The listener object to use.
+     */
+    public int addProgressListener(DownloadRequest downloadRequest,
+            DownloadProgressListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int addProgressListener(final DownloadRequest downloadRequest,
+            final IDownloadProgressListener listener) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DownloadProgressListener exposedCallback = new VendorDownloadProgressListener(listener) {
+            @Override
+            protected void onRemoteException(RemoteException e) {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+            }
+        };
+
+        int result = addProgressListener(downloadRequest, exposedCallback);
+
+        if (result == MbmsErrors.SUCCESS) {
+            DeathRecipient deathRecipient = new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                    mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+                }
+            };
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadProgressListenerBinderMap.put(listener.asBinder(), exposedCallback);
+        }
+
+        return result;
+    }
+
+    /**
+     * Un-registers a download progress listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it no longer wants to request progress updates on the
+     * download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+     * @param listener The callback object that
+     *                 {@link #addProgressListener(DownloadRequest, DownloadProgressListener)}
+     *                 was called with.
+     */
+    public int removeProgressListener(DownloadRequest downloadRequest,
+            DownloadProgressListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    public final int removeProgressListener(
+            final DownloadRequest downloadRequest, final IDownloadProgressListener listener)
+            throws RemoteException {
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DeathRecipient deathRecipient =
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+        if (deathRecipient == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
+
+        DownloadProgressListener exposedCallback =
+                mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+        if (exposedCallback == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        return removeProgressListener(downloadRequest, exposedCallback);
     }
 
     /**
@@ -370,18 +490,18 @@
     }
 
     /**
-     * Gets information about the status of a file pending download.
+     * Requests information about the state of a file pending download.
      *
-     * If the middleware has not yet been properly initialized or if it has no records of the
+     * If the middleware has no records of the
      * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
-     * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_FILE_INFO} must be returned.
      *
      * @param downloadRequest The download request to query.
      * @param fileInfo The particular file within the request to get information on.
-     * @return The status of the download.
+     * @return {@link MbmsErrors#SUCCESS} if the request was successful, an error code otherwise.
      */
     @Override
-    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+    public int requestDownloadState(DownloadRequest downloadRequest, FileInfo fileInfo)
             throws RemoteException {
         return 0;
     }
diff --git a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index db177c0..5ce612d 100644
--- a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -77,6 +77,10 @@
             @Override
             public void onError(final int errorCode, final String message) {
                 try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
@@ -173,6 +177,10 @@
             @Override
             public void onError(final int errorCode, final String message) {
                 try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
diff --git a/android/test/ComparisonFailure.java b/android/test/ComparisonFailure.java
index 3fa76f5..d86b700 100644
--- a/android/test/ComparisonFailure.java
+++ b/android/test/ComparisonFailure.java
@@ -19,8 +19,9 @@
 /**
  * Thrown when an assert equals for Strings failed.
  * 
- * @deprecated use junit.framework.ComparisonFailure
+ * @deprecated use org.junit.ComparisonFailure
  */
+@Deprecated
 public class ComparisonFailure extends AssertionFailedError {
     private junit.framework.ComparisonFailure mComparison;
 
diff --git a/android/test/PerformanceTestCase.java b/android/test/PerformanceTestCase.java
index 65bd4a4..2584da2 100644
--- a/android/test/PerformanceTestCase.java
+++ b/android/test/PerformanceTestCase.java
@@ -21,6 +21,11 @@
  *
  * If you want your test to be used as a performance test, you must
  * implement this interface.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
 @Deprecated
 public interface PerformanceTestCase
diff --git a/android/test/TestSuiteProvider.java b/android/test/TestSuiteProvider.java
index c74651c..12cfcb7 100644
--- a/android/test/TestSuiteProvider.java
+++ b/android/test/TestSuiteProvider.java
@@ -20,6 +20,11 @@
 
 /**
  * Implementors will know how to get a test suite.
+ *
+ * @deprecated Use
+ * <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+ * AndroidJUnitRunner</a> instead. New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
  */
 @Deprecated
 public interface TestSuiteProvider {
diff --git a/android/test/mock/MockPackageManager.java b/android/test/mock/MockPackageManager.java
index 1ddc52c..c2aca6b 100644
--- a/android/test/mock/MockPackageManager.java
+++ b/android/test/mock/MockPackageManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -53,6 +54,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 
@@ -464,6 +466,11 @@
     }
 
     @Override
+    public ResolveInfo resolveServiceAsUser(Intent intent, int flags, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
         throw new UnsupportedOperationException();
     }
@@ -945,7 +952,8 @@
 
     /** @hide */
     @Override
-    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean hidden, int userId) {
+    public String[] setPackagesSuspended(String[] packageNames, boolean hidden,
+            PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) {
         throw new UnsupportedOperationException();
     }
 
@@ -1209,4 +1217,11 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public String getSystemTextClassifierPackageName() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/android/text/BoringLayout.java b/android/text/BoringLayout.java
index 6fa5312..ce38ebb 100644
--- a/android/text/BoringLayout.java
+++ b/android/text/BoringLayout.java
@@ -347,14 +347,7 @@
         TextLine line = TextLine.obtain();
         line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
                 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
-        if (text instanceof MeasuredText) {
-            MeasuredText mt = (MeasuredText) text;
-            // Reaching here means there is only one paragraph.
-            MeasuredParagraph mp = mt.getMeasuredParagraph(0);
-            fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength()));
-        } else {
-            fm.width = (int) Math.ceil(line.metrics(fm));
-        }
+        fm.width = (int) Math.ceil(line.metrics(fm));
         TextLine.recycle(line);
 
         return fm;
diff --git a/android/text/DynamicLayout.java b/android/text/DynamicLayout.java
index 18431ca..febca7e 100644
--- a/android/text/DynamicLayout.java
+++ b/android/text/DynamicLayout.java
@@ -704,7 +704,12 @@
         // Spans other than ReplacementSpan can be ignored because line top and bottom are
         // disjunction of all tops and bottoms, although it's not optimal.
         final Paint paint = getPaint();
-        paint.getTextBounds(text, start, end, mTempRect);
+        if (text instanceof PrecomputedText) {
+            PrecomputedText precomputed = (PrecomputedText) text;
+            precomputed.getBounds(start, end, mTempRect);
+        } else {
+            paint.getTextBounds(text, start, end, mTempRect);
+        }
         final Paint.FontMetricsInt fm = paint.getFontMetricsInt();
         return mTempRect.top < fm.top || mTempRect.bottom > fm.bottom;
     }
diff --git a/android/text/InputType.java b/android/text/InputType.java
index f38482e..7f903b6 100644
--- a/android/text/InputType.java
+++ b/android/text/InputType.java
@@ -24,7 +24,7 @@
  * <h3>Examples</h3>
  *
  * <dl>
- * <dt>A password field with with the password visible to the user:
+ * <dt>A password field with the password visible to the user:
  * <dd>inputType = TYPE_CLASS_TEXT |
  *     TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
  *
diff --git a/android/text/MeasuredParagraph.java b/android/text/MeasuredParagraph.java
index 45fbf6f..96edfa3 100644
--- a/android/text/MeasuredParagraph.java
+++ b/android/text/MeasuredParagraph.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.text.AutoGrowArray.ByteArray;
 import android.text.AutoGrowArray.FloatArray;
 import android.text.AutoGrowArray.IntArray;
@@ -297,6 +298,18 @@
     }
 
     /**
+     * Retrieves the bounding rectangle that encloses all of the characters, with an implied origin
+     * at (0, 0).
+     *
+     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+     */
+    public void getBounds(@NonNull Paint paint, @IntRange(from = 0) int start,
+            @IntRange(from = 0) int end, @NonNull Rect bounds) {
+        nGetBounds(mNativePtr, mCopiedBuffer, paint.getNativeInstance(), start, end,
+                paint.getBidiFlags(), bounds);
+    }
+
+    /**
      * Generates new MeasuredParagraph for Bidi computation.
      *
      * If recycle is null, this returns new instance. If recycle is not null, this fills computed
@@ -527,9 +540,6 @@
     private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
                                @IntRange(from = 0) int end,  // exclusive, in copied buffer
                                /* Maybe Zero */ long nativeBuilderPtr) {
-        if (nativeBuilderPtr != 0) {
-            mCachedPaint.getFontMetricsInt(mCachedFm);
-        }
 
         if (mLtrWithoutBidi) {
             // If the whole text is LTR direction, just apply whole region.
@@ -601,6 +611,10 @@
         final int startInCopiedBuffer = start - mTextStart;
         final int endInCopiedBuffer = end - mTextStart;
 
+        if (nativeBuilderPtr != 0) {
+            mCachedPaint.getFontMetricsInt(mCachedFm);
+        }
+
         if (replacement != null) {
             applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
                                 nativeBuilderPtr);
@@ -672,6 +686,13 @@
         return width;
     }
 
+    /**
+     * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
+     */
+    public @IntRange(from = 0) int getMemoryUsage() {
+        return nGetMemoryUsage(mNativePtr);
+    }
+
     private static native /* Non Zero */ long nInitBuilder();
 
     /**
@@ -718,4 +739,10 @@
 
     @CriticalNative
     private static native /* Non Zero */ long nGetReleaseFunc();
+
+    @CriticalNative
+    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+    private static native void nGetBounds(long nativePtr, char[] buf, long paintPtr, int start,
+            int end, int bidiFlag, Rect rect);
 }
diff --git a/android/text/MeasuredParagraph_Delegate.java b/android/text/MeasuredParagraph_Delegate.java
index e4dbee0..4694890 100644
--- a/android/text/MeasuredParagraph_Delegate.java
+++ b/android/text/MeasuredParagraph_Delegate.java
@@ -95,7 +95,7 @@
 
     @LayoutlibDelegate
     /*package*/ static long nBuildNativeMeasuredParagraph(long nativeBuilderPtr,
-            @NonNull char[] text, boolean computeHyphenation) {
+            @NonNull char[] text, boolean computeHyphenation, boolean computeLayout) {
         MeasuredParagraph_Delegate delegate = new MeasuredParagraph_Delegate();
         delegate.mNativeBuilderPtr = nativeBuilderPtr;
         return sManager.addNewDelegate(delegate);
@@ -107,6 +107,12 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static float nGetWidth(long nativePtr, int start, int end) {
+        // Ignore as it is not used for the layoutlib implementation
+        return 0.0f;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static long nGetReleaseFunc() {
         synchronized (MeasuredParagraph_Delegate.class) {
             if (sFinalizer == -1) {
@@ -117,6 +123,12 @@
         return sFinalizer;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static int nGetMemoryUsage(long nativePtr) {
+        // Ignore as it is not used for the layoutlib implementation
+        return 0;
+    }
+
     private static float measureText(long nativePaint, char[] text, int index, int count,
             float[] widths, int bidiFlags) {
         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
diff --git a/android/text/MeasuredText.java b/android/text/MeasuredText.java
deleted file mode 100644
index ff23395..0000000
--- a/android/text/MeasuredText.java
+++ /dev/null
@@ -1,413 +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.text;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has already been measured.
- */
-public class MeasuredText implements Spanned {
-    private static final char LINE_FEED = '\n';
-
-    // The original text.
-    private final @NonNull CharSequence mText;
-
-    // The inclusive start offset of the measuring target.
-    private final @IntRange(from = 0) int mStart;
-
-    // The exclusive end offset of the measuring target.
-    private final @IntRange(from = 0) int mEnd;
-
-    // The TextPaint used for measurement.
-    private final @NonNull TextPaint mPaint;
-
-    // The requested text direction.
-    private final @NonNull TextDirectionHeuristic mTextDir;
-
-    // The measured paragraph texts.
-    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
-    // The sorted paragraph end offsets.
-    private final @NonNull int[] mParagraphBreakPoints;
-
-    // The break strategy for this measured text.
-    private final @Layout.BreakStrategy int mBreakStrategy;
-
-    // The hyphenation frequency for this measured text.
-    private final @Layout.HyphenationFrequency int mHyphenationFrequency;
-
-    /**
-     * A Builder for MeasuredText
-     */
-    public static final class Builder {
-        // Mandatory parameters.
-        private final @NonNull CharSequence mText;
-        private final @NonNull TextPaint mPaint;
-
-        // Members to be updated by setters.
-        private @IntRange(from = 0) int mStart;
-        private @IntRange(from = 0) int mEnd;
-        private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
-        private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
-        private @Layout.HyphenationFrequency int mHyphenationFrequency =
-                Layout.HYPHENATION_FREQUENCY_NORMAL;
-
-
-        /**
-         * Builder constructor
-         *
-         * @param text The text to be measured.
-         * @param paint The paint to be used for drawing.
-         */
-        public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
-            Preconditions.checkNotNull(text);
-            Preconditions.checkNotNull(paint);
-
-            mText = text;
-            mPaint = paint;
-            mStart = 0;
-            mEnd = text.length();
-        }
-
-        /**
-         * Set the range of measuring target.
-         *
-         * @param start The measuring target start offset in the text.
-         * @param end The measuring target end offset in the text.
-         */
-        public @NonNull Builder setRange(@IntRange(from = 0) int start,
-                                         @IntRange(from = 0) int end) {
-            Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
-            Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
-            Preconditions.checkArgument(start <= end, "The range is reversed.");
-
-            mStart = start;
-            mEnd = end;
-            return this;
-        }
-
-        /**
-         * Set the text direction heuristic
-         *
-         * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
-         *
-         * @param textDir The text direction heuristic for resolving bidi behavior.
-         * @return this builder, useful for chaining.
-         */
-        public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
-            Preconditions.checkNotNull(textDir);
-            mTextDir = textDir;
-            return this;
-        }
-
-        /**
-         * Set the break strategy
-         *
-         * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
-         *
-         * @param breakStrategy The break strategy.
-         * @return this builder, useful for chaining.
-         */
-        public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
-            mBreakStrategy = breakStrategy;
-            return this;
-        }
-
-        /**
-         * Set the hyphenation frequency
-         *
-         * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
-         *
-         * @param hyphenationFrequency The hyphenation frequency.
-         * @return this builder, useful for chaining.
-         */
-        public @NonNull Builder setHyphenationFrequency(
-                @Layout.HyphenationFrequency int hyphenationFrequency) {
-            mHyphenationFrequency = hyphenationFrequency;
-            return this;
-        }
-
-        /**
-         * Build the measured text
-         *
-         * @return the measured text.
-         */
-        public @NonNull MeasuredText build() {
-            return build(true /* build full layout result */);
-        }
-
-        /** @hide */
-        public @NonNull MeasuredText build(boolean computeLayout) {
-            final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
-                    && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
-
-            final IntArray paragraphEnds = new IntArray();
-            final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
-
-            int paraEnd = 0;
-            for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
-                paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
-                if (paraEnd < 0) {
-                    // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
-                    // end.
-                    paraEnd = mEnd;
-                } else {
-                    paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
-                }
-
-                paragraphEnds.add(paraEnd);
-                measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
-                        mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
-                        computeLayout, null /* no recycle */));
-            }
-
-            return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
-                                    mHyphenationFrequency, measuredTexts.toArray(
-                                            new MeasuredParagraph[measuredTexts.size()]),
-                                    paragraphEnds.toArray());
-        }
-    };
-
-    // Use MeasuredText.Builder instead.
-    private MeasuredText(@NonNull CharSequence text,
-                         @IntRange(from = 0) int start,
-                         @IntRange(from = 0) int end,
-                         @NonNull TextPaint paint,
-                         @NonNull TextDirectionHeuristic textDir,
-                         @Layout.BreakStrategy int breakStrategy,
-                         @Layout.HyphenationFrequency int frequency,
-                         @NonNull MeasuredParagraph[] measuredTexts,
-                         @NonNull int[] paragraphBreakPoints) {
-        mText = text;
-        mStart = start;
-        mEnd = end;
-        // Copy the paint so that we can keep the reference of typeface in native layout result.
-        mPaint = new TextPaint(paint);
-        mMeasuredParagraphs = measuredTexts;
-        mParagraphBreakPoints = paragraphBreakPoints;
-        mTextDir = textDir;
-        mBreakStrategy = breakStrategy;
-        mHyphenationFrequency = frequency;
-    }
-
-    /**
-     * Return the underlying text.
-     */
-    public @NonNull CharSequence getText() {
-        return mText;
-    }
-
-    /**
-     * Returns the inclusive start offset of measured region.
-     */
-    public @IntRange(from = 0) int getStart() {
-        return mStart;
-    }
-
-    /**
-     * Returns the exclusive end offset of measured region.
-     */
-    public @IntRange(from = 0) int getEnd() {
-        return mEnd;
-    }
-
-    /**
-     * Returns the text direction associated with char sequence.
-     */
-    public @NonNull TextDirectionHeuristic getTextDir() {
-        return mTextDir;
-    }
-
-    /**
-     * Returns the paint used to measure this text.
-     */
-    public @NonNull TextPaint getPaint() {
-        return mPaint;
-    }
-
-    /**
-     * Returns the length of the paragraph of this text.
-     */
-    public @IntRange(from = 0) int getParagraphCount() {
-        return mParagraphBreakPoints.length;
-    }
-
-    /**
-     * Returns the paragraph start offset of the text.
-     */
-    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
-        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
-    }
-
-    /**
-     * Returns the paragraph end offset of the text.
-     */
-    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
-        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return mParagraphBreakPoints[paraIndex];
-    }
-
-    /** @hide */
-    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
-        return mMeasuredParagraphs[paraIndex];
-    }
-
-    /**
-     * Returns the break strategy for this text.
-     */
-    public @Layout.BreakStrategy int getBreakStrategy() {
-        return mBreakStrategy;
-    }
-
-    /**
-     * Returns the hyphenation frequency for this text.
-     */
-    public @Layout.HyphenationFrequency int getHyphenationFrequency() {
-        return mHyphenationFrequency;
-    }
-
-    /**
-     * Returns true if the given TextPaint gives the same result of text layout for this text.
-     * @hide
-     */
-    public boolean canUseMeasuredResult(@NonNull TextPaint paint) {
-        return mPaint.getTextSize() == paint.getTextSize()
-            && mPaint.getTextSkewX() == paint.getTextSkewX()
-            && mPaint.getTextScaleX() == paint.getTextScaleX()
-            && mPaint.getLetterSpacing() == paint.getLetterSpacing()
-            && mPaint.getWordSpacing() == paint.getWordSpacing()
-            && mPaint.getFlags() == paint.getFlags()  // Maybe not all flag affects text layout.
-            && mPaint.getTextLocales() == paint.getTextLocales()  // need to be equals?
-            && mPaint.getFontVariationSettings() == paint.getFontVariationSettings()
-            && mPaint.getTypeface() == paint.getTypeface()
-            && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings());
-    }
-
-    /** @hide */
-    public int findParaIndex(@IntRange(from = 0) int pos) {
-        // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout
-        //       support to StaticLayout.
-        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
-            if (pos < mParagraphBreakPoints[i]) {
-                return i;
-            }
-        }
-        throw new IndexOutOfBoundsException(
-            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
-            + ", gave " + pos);
-    }
-
-    /** @hide */
-    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        final int paraIndex = findParaIndex(start);
-        final int paraStart = getParagraphStart(paraIndex);
-        final int paraEnd = getParagraphEnd(paraIndex);
-        if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
-                + "para: (" + paraStart + ", " + paraEnd + "), "
-                + "request: (" + start + ", " + end + ")");
-        }
-        return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    // Spanned overrides
-    //
-    // Just proxy for underlying mText if appropriate.
-
-    @Override
-    public <T> T[] getSpans(int start, int end, Class<T> type) {
-        if (mText instanceof Spanned) {
-            return ((Spanned) mText).getSpans(start, end, type);
-        } else {
-            return ArrayUtils.emptyArray(type);
-        }
-    }
-
-    @Override
-    public int getSpanStart(Object tag) {
-        if (mText instanceof Spanned) {
-            return ((Spanned) mText).getSpanStart(tag);
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public int getSpanEnd(Object tag) {
-        if (mText instanceof Spanned) {
-            return ((Spanned) mText).getSpanEnd(tag);
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public int getSpanFlags(Object tag) {
-        if (mText instanceof Spanned) {
-            return ((Spanned) mText).getSpanFlags(tag);
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public int nextSpanTransition(int start, int limit, Class type) {
-        if (mText instanceof Spanned) {
-            return ((Spanned) mText).nextSpanTransition(start, limit, type);
-        } else {
-            return mText.length();
-        }
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    // CharSequence overrides.
-    //
-    // Just proxy for underlying mText.
-
-    @Override
-    public int length() {
-        return mText.length();
-    }
-
-    @Override
-    public char charAt(int index) {
-        // TODO: Should this be index + mStart ?
-        return mText.charAt(index);
-    }
-
-    @Override
-    public CharSequence subSequence(int start, int end) {
-        // TODO: return MeasuredText.
-        // TODO: Should this be index + mStart, end + mStart ?
-        return mText.subSequence(start, end);
-    }
-
-    @Override
-    public String toString() {
-        return mText.toString();
-    }
-}
diff --git a/android/text/PrecomputedText.java b/android/text/PrecomputedText.java
new file mode 100644
index 0000000..413df05
--- /dev/null
+++ b/android/text/PrecomputedText.java
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.text.style.MetricAffectingSpan;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * A text which has the character metrics data.
+ *
+ * A text object that contains the character metrics data and can be used to improve the performance
+ * of text layout operations. When a PrecomputedText is created with a given {@link CharSequence},
+ * it will measure the text metrics during the creation. This PrecomputedText instance can be set on
+ * {@link android.widget.TextView} or {@link StaticLayout}. Since the text layout information will
+ * be included in this instance, {@link android.widget.TextView} or {@link StaticLayout} will not
+ * have to recalculate this information.
+ *
+ * Note that the {@link PrecomputedText} created from different parameters of the target {@link
+ * android.widget.TextView} will be rejected internally and compute the text layout again with the
+ * current {@link android.widget.TextView} parameters.
+ *
+ * <pre>
+ * An example usage is:
+ * <code>
+ *  void asyncSetText(final TextView textView, final String longString, Handler bgThreadHandler) {
+ *      // construct precompute related parameters using the TextView that we will set the text on.
+ *      final PrecomputedText.Params params = textView.getTextParams();
+ *      bgThreadHandler.post(() -> {
+ *          final PrecomputedText precomputedText =
+ *                  PrecomputedText.create(expensiveLongString, params);
+ *          textView.post(() -> {
+ *              textView.setText(precomputedText);
+ *          });
+ *      });
+ *  }
+ * </code>
+ * </pre>
+ *
+ * Note that the {@link PrecomputedText} created from different parameters of the target
+ * {@link android.widget.TextView} will be rejected.
+ *
+ * Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
+ * PrecomputedText.
+ */
+public class PrecomputedText implements Spannable {
+    private static final char LINE_FEED = '\n';
+
+    /**
+     * The information required for building {@link PrecomputedText}.
+     *
+     * Contains information required for precomputing text measurement metadata, so it can be done
+     * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout
+     * constraints are not known.
+     */
+    public static final class Params {
+        // The TextPaint used for measurement.
+        private final @NonNull TextPaint mPaint;
+
+        // The requested text direction.
+        private final @NonNull TextDirectionHeuristic mTextDir;
+
+        // The break strategy for this measured text.
+        private final @Layout.BreakStrategy int mBreakStrategy;
+
+        // The hyphenation frequency for this measured text.
+        private final @Layout.HyphenationFrequency int mHyphenationFrequency;
+
+        /**
+         * A builder for creating {@link Params}.
+         */
+        public static class Builder {
+            // The TextPaint used for measurement.
+            private final @NonNull TextPaint mPaint;
+
+            // The requested text direction.
+            private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+
+            // The break strategy for this measured text.
+            private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
+
+            // The hyphenation frequency for this measured text.
+            private @Layout.HyphenationFrequency int mHyphenationFrequency =
+                    Layout.HYPHENATION_FREQUENCY_NORMAL;
+
+            /**
+             * Builder constructor.
+             *
+             * @param paint the paint to be used for drawing
+             */
+            public Builder(@NonNull TextPaint paint) {
+                mPaint = paint;
+            }
+
+            /**
+             * Set the line break strategy.
+             *
+             * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
+             *
+             * @param strategy the break strategy
+             * @return this builder, useful for chaining
+             * @see StaticLayout.Builder#setBreakStrategy
+             * @see android.widget.TextView#setBreakStrategy
+             */
+            public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) {
+                mBreakStrategy = strategy;
+                return this;
+            }
+
+            /**
+             * Set the hyphenation frequency.
+             *
+             * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
+             *
+             * @param frequency the hyphenation frequency
+             * @return this builder, useful for chaining
+             * @see StaticLayout.Builder#setHyphenationFrequency
+             * @see android.widget.TextView#setHyphenationFrequency
+             */
+            public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) {
+                mHyphenationFrequency = frequency;
+                return this;
+            }
+
+            /**
+             * Set the text direction heuristic.
+             *
+             * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+             *
+             * @param textDir the text direction heuristic for resolving bidi behavior
+             * @return this builder, useful for chaining
+             * @see StaticLayout.Builder#setTextDirection
+             */
+            public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
+                mTextDir = textDir;
+                return this;
+            }
+
+            /**
+             * Build the {@link Params}.
+             *
+             * @return the layout parameter
+             */
+            public @NonNull Params build() {
+                return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency);
+            }
+        }
+
+        // This is public hidden for internal use.
+        // For the external developers, use Builder instead.
+        /** @hide */
+        public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir,
+                @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
+            mPaint = paint;
+            mTextDir = textDir;
+            mBreakStrategy = strategy;
+            mHyphenationFrequency = frequency;
+        }
+
+        /**
+         * Returns the {@link TextPaint} for this text.
+         *
+         * @return A {@link TextPaint}
+         */
+        public @NonNull TextPaint getTextPaint() {
+            return mPaint;
+        }
+
+        /**
+         * Returns the {@link TextDirectionHeuristic} for this text.
+         *
+         * @return A {@link TextDirectionHeuristic}
+         */
+        public @NonNull TextDirectionHeuristic getTextDirection() {
+            return mTextDir;
+        }
+
+        /**
+         * Returns the break strategy for this text.
+         *
+         * @return A line break strategy
+         */
+        public @Layout.BreakStrategy int getBreakStrategy() {
+            return mBreakStrategy;
+        }
+
+        /**
+         * Returns the hyphenation frequency for this text.
+         *
+         * @return A hyphenation frequency
+         */
+        public @Layout.HyphenationFrequency int getHyphenationFrequency() {
+            return mHyphenationFrequency;
+        }
+
+        /** @hide */
+        public boolean isSameTextMetricsInternal(@NonNull TextPaint paint,
+                @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
+                @Layout.HyphenationFrequency int frequency) {
+            return mTextDir == textDir
+                && mBreakStrategy == strategy
+                && mHyphenationFrequency == frequency
+                && mPaint.equalsForTextMeasurement(paint);
+        }
+
+        /**
+         * Check if the same text layout.
+         *
+         * @return true if this and the given param result in the same text layout
+         */
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o == null || !(o instanceof Params)) {
+                return false;
+            }
+            Params param = (Params) o;
+            return isSameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
+                    param.mHyphenationFrequency);
+        }
+
+        @Override
+        public int hashCode() {
+            // TODO: implement MinikinPaint::hashCode and use it to keep consistency with equals.
+            return Objects.hash(mPaint.getTextSize(), mPaint.getTextScaleX(), mPaint.getTextSkewX(),
+                    mPaint.getLetterSpacing(), mPaint.getWordSpacing(), mPaint.getFlags(),
+                    mPaint.getTextLocales(), mPaint.getTypeface(),
+                    mPaint.getFontVariationSettings(), mPaint.isElegantTextHeight(), mTextDir,
+                    mBreakStrategy, mHyphenationFrequency);
+        }
+
+        @Override
+        public String toString() {
+            return "{"
+                + "textSize=" + mPaint.getTextSize()
+                + ", textScaleX=" + mPaint.getTextScaleX()
+                + ", textSkewX=" + mPaint.getTextSkewX()
+                + ", letterSpacing=" + mPaint.getLetterSpacing()
+                + ", textLocale=" + mPaint.getTextLocales()
+                + ", typeface=" + mPaint.getTypeface()
+                + ", variationSettings=" + mPaint.getFontVariationSettings()
+                + ", elegantTextHeight=" + mPaint.isElegantTextHeight()
+                + ", textDir=" + mTextDir
+                + ", breakStrategy=" + mBreakStrategy
+                + ", hyphenationFrequency=" + mHyphenationFrequency
+                + "}";
+        }
+    };
+
+    /** @hide */
+    public static class ParagraphInfo {
+        public final @IntRange(from = 0) int paragraphEnd;
+        public final @NonNull MeasuredParagraph measured;
+
+        /**
+         * @param paraEnd the end offset of this paragraph
+         * @param measured a measured paragraph
+         */
+        public ParagraphInfo(@IntRange(from = 0) int paraEnd, @NonNull MeasuredParagraph measured) {
+            this.paragraphEnd = paraEnd;
+            this.measured = measured;
+        }
+    };
+
+
+    // The original text.
+    private final @NonNull SpannableString mText;
+
+    // The inclusive start offset of the measuring target.
+    private final @IntRange(from = 0) int mStart;
+
+    // The exclusive end offset of the measuring target.
+    private final @IntRange(from = 0) int mEnd;
+
+    private final @NonNull Params mParams;
+
+    // The list of measured paragraph info.
+    private final @NonNull ParagraphInfo[] mParagraphInfo;
+
+    /**
+     * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
+     * positioning information.
+     * <p>
+     * This can be expensive, so computing this on a background thread before your text will be
+     * presented can save work on the UI thread.
+     * </p>
+     *
+     * Note that any {@link android.text.NoCopySpan} attached to the text won't be passed to the
+     * created PrecomputedText.
+     *
+     * @param text the text to be measured
+     * @param params parameters that define how text will be precomputed
+     * @return A {@link PrecomputedText}
+     */
+    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params params) {
+        ParagraphInfo[] paraInfo = createMeasuredParagraphs(
+                text, params, 0, text.length(), true /* computeLayout */);
+        return new PrecomputedText(text, 0, text.length(), params, paraInfo);
+    }
+
+    /** @hide */
+    public static ParagraphInfo[] createMeasuredParagraphs(
+            @NonNull CharSequence text, @NonNull Params params,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
+        ArrayList<ParagraphInfo> result = new ArrayList<>();
+
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(params);
+        final boolean needHyphenation = params.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
+                && params.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
+
+        int paraEnd = 0;
+        for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
+            paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
+            if (paraEnd < 0) {
+                // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
+                // end.
+                paraEnd = end;
+            } else {
+                paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
+            }
+
+            result.add(new ParagraphInfo(paraEnd, MeasuredParagraph.buildForStaticLayout(
+                    params.getTextPaint(), text, paraStart, paraEnd, params.getTextDirection(),
+                    needHyphenation, computeLayout, null /* no recycle */)));
+        }
+        return result.toArray(new ParagraphInfo[result.size()]);
+    }
+
+    // Use PrecomputedText.create instead.
+    private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
+            @IntRange(from = 0) int end, @NonNull Params params,
+            @NonNull ParagraphInfo[] paraInfo) {
+        mText = new SpannableString(text, true /* ignoreNoCopySpan */);
+        mStart = start;
+        mEnd = end;
+        mParams = params;
+        mParagraphInfo = paraInfo;
+    }
+
+    /**
+     * Return the underlying text.
+     */
+    public @NonNull CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Returns the inclusive start offset of measured region.
+     * @hide
+     */
+    public @IntRange(from = 0) int getStart() {
+        return mStart;
+    }
+
+    /**
+     * Returns the exclusive end offset of measured region.
+     * @hide
+     */
+    public @IntRange(from = 0) int getEnd() {
+        return mEnd;
+    }
+
+    /**
+     * Returns the layout parameters used to measure this text.
+     */
+    public @NonNull Params getParams() {
+        return mParams;
+    }
+
+    /**
+     * Returns the count of paragraphs.
+     */
+    public @IntRange(from = 0) int getParagraphCount() {
+        return mParagraphInfo.length;
+    }
+
+    /**
+     * Returns the paragraph start offset of the text.
+     */
+    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+        return paraIndex == 0 ? mStart : getParagraphEnd(paraIndex - 1);
+    }
+
+    /**
+     * Returns the paragraph end offset of the text.
+     */
+    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+        return mParagraphInfo[paraIndex].paragraphEnd;
+    }
+
+    /** @hide */
+    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+        return mParagraphInfo[paraIndex].measured;
+    }
+
+    /** @hide */
+    public @NonNull ParagraphInfo[] getParagraphInfo() {
+        return mParagraphInfo;
+    }
+
+    /**
+     * Returns true if the given TextPaint gives the same result of text layout for this text.
+     * @hide
+     */
+    public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
+            @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
+        final TextPaint mtPaint = mParams.getTextPaint();
+        return mStart == start
+            && mEnd == end
+            && mParams.isSameTextMetricsInternal(paint, textDir, strategy, frequency);
+    }
+
+    /** @hide */
+    public int findParaIndex(@IntRange(from = 0) int pos) {
+        // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
+        //       layout support to StaticLayout.
+        for (int i = 0; i < mParagraphInfo.length; ++i) {
+            if (pos < mParagraphInfo[i].paragraphEnd) {
+                return i;
+            }
+        }
+        throw new IndexOutOfBoundsException(
+            "pos must be less than " + mParagraphInfo[mParagraphInfo.length - 1].paragraphEnd
+            + ", gave " + pos);
+    }
+
+    /** @hide */
+    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+        final int paraIndex = findParaIndex(start);
+        final int paraStart = getParagraphStart(paraIndex);
+        final int paraEnd = getParagraphEnd(paraIndex);
+        if (start < paraStart || paraEnd < end) {
+            throw new RuntimeException("Cannot measured across the paragraph:"
+                + "para: (" + paraStart + ", " + paraEnd + "), "
+                + "request: (" + start + ", " + end + ")");
+        }
+        return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
+    }
+
+    /** @hide */
+    public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Rect bounds) {
+        final int paraIndex = findParaIndex(start);
+        final int paraStart = getParagraphStart(paraIndex);
+        final int paraEnd = getParagraphEnd(paraIndex);
+        if (start < paraStart || paraEnd < end) {
+            throw new RuntimeException("Cannot measured across the paragraph:"
+                + "para: (" + paraStart + ", " + paraEnd + "), "
+                + "request: (" + start + ", " + end + ")");
+        }
+        getMeasuredParagraph(paraIndex).getBounds(mParams.mPaint,
+                start - paraStart, end - paraStart, bounds);
+    }
+
+    /**
+     * Returns the size of native PrecomputedText memory usage.
+     *
+     * Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
+     * @hide
+     */
+    public int getMemoryUsage() {
+        int r = 0;
+        for (int i = 0; i < getParagraphCount(); ++i) {
+            r += getMeasuredParagraph(i).getMemoryUsage();
+        }
+        return r;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Spannable overrides
+    //
+    // Do not allow to modify MetricAffectingSpan
+
+    /**
+     * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+     */
+    @Override
+    public void setSpan(Object what, int start, int end, int flags) {
+        if (what instanceof MetricAffectingSpan) {
+            throw new IllegalArgumentException(
+                    "MetricAffectingSpan can not be set to PrecomputedText.");
+        }
+        mText.setSpan(what, start, end, flags);
+    }
+
+    /**
+     * @throws IllegalArgumentException if {@link MetricAffectingSpan} is specified.
+     */
+    @Override
+    public void removeSpan(Object what) {
+        if (what instanceof MetricAffectingSpan) {
+            throw new IllegalArgumentException(
+                    "MetricAffectingSpan can not be removed from PrecomputedText.");
+        }
+        mText.removeSpan(what);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Spanned overrides
+    //
+    // Just proxy for underlying mText if appropriate.
+
+    @Override
+    public <T> T[] getSpans(int start, int end, Class<T> type) {
+        return mText.getSpans(start, end, type);
+    }
+
+    @Override
+    public int getSpanStart(Object tag) {
+        return mText.getSpanStart(tag);
+    }
+
+    @Override
+    public int getSpanEnd(Object tag) {
+        return mText.getSpanEnd(tag);
+    }
+
+    @Override
+    public int getSpanFlags(Object tag) {
+        return mText.getSpanFlags(tag);
+    }
+
+    @Override
+    public int nextSpanTransition(int start, int limit, Class type) {
+        return mText.nextSpanTransition(start, limit, type);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // CharSequence overrides.
+    //
+    // Just proxy for underlying mText.
+
+    @Override
+    public int length() {
+        return mText.length();
+    }
+
+    @Override
+    public char charAt(int index) {
+        return mText.charAt(index);
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        return PrecomputedText.create(mText.subSequence(start, end), mParams);
+    }
+
+    @Override
+    public String toString() {
+        return mText.toString();
+    }
+}
diff --git a/android/text/PrecomputedTextMemoryUsageTest.java b/android/text/PrecomputedTextMemoryUsageTest.java
new file mode 100644
index 0000000..ccbccca
--- /dev/null
+++ b/android/text/PrecomputedTextMemoryUsageTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import static android.text.TextDirectionHeuristics.LTR;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.CharBuffer;
+import java.util.Random;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class PrecomputedTextMemoryUsageTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final boolean NO_STYLE_TEXT = false;
+
+    private static TextPaint PAINT = new TextPaint();
+
+    private static int TRIAL_COUNT = 100;
+
+    public PrecomputedTextMemoryUsageTest() {}
+
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
+
+    @Before
+    public void setUp() {
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    private void reportMemoryUsage(int memoryUsage, String key) {
+        Bundle status = new Bundle();
+        status.putInt(key + "_median", memoryUsage);
+        InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
+    }
+
+    private int median(int[] values) {
+        return values.length % 2 == 0 ?
+                (values[values.length / 2] + values[values.length / 2 - 1]) / 2:
+                values[values.length / 2];
+    }
+
+    @Test
+    public void testMemoryUsage_NoHyphenation() {
+        int[] memories = new int[TRIAL_COUNT];
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                .build();
+
+        // Report median of randomly generated PrecomputedText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
+                .getMemoryUsage();
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
+    }
+
+    @Test
+    public void testMemoryUsage_Hyphenation() {
+        int[] memories = new int[TRIAL_COUNT];
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .build();
+
+        // Report median of randomly generated PrecomputedText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
+                .getMemoryUsage();
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
+    }
+
+    @Test
+    public void testMemoryUsage_NoHyphenation_WidthOnly() {
+        int[] memories = new int[TRIAL_COUNT];
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                .build();
+
+        // Report median of randomly generated PrecomputedText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            PrecomputedText.ParagraphInfo[] paragraphInfo =
+                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
+            memories[i] = 0;
+            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
+                memories[i] += info.measured.getMemoryUsage();
+            }
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
+    }
+
+    @Test
+    public void testMemoryUsage_Hyphenatation_WidthOnly() {
+        int[] memories = new int[TRIAL_COUNT];
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .build();
+
+        // Report median of randomly generated PrecomputedText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            PrecomputedText.ParagraphInfo[] paragraphInfo =
+                    PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
+            memories[i] = 0;
+            for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
+                memories[i] += info.measured.getMemoryUsage();
+            }
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
+    }
+}
diff --git a/android/text/PrecomputedTextPerfTest.java b/android/text/PrecomputedTextPerfTest.java
new file mode 100644
index 0000000..1cd0ae1
--- /dev/null
+++ b/android/text/PrecomputedTextPerfTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import static android.text.TextDirectionHeuristics.LTR;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.CharBuffer;
+import java.util.Random;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class PrecomputedTextPerfTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
+    private static final boolean NO_STYLE_TEXT = false;
+    private static final boolean STYLE_TEXT = true;
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+    public PrecomputedTextPerfTest() {}
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
+
+    @Before
+    public void setUp() {
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    @Test
+    public void testCreate_NoStyled_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_NoStyled_NoHyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_NoStyled_Hyphenation_WidthOnly() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_NoStyled_NoHyphenation_WidthOnly() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_Styled_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_Styled_NoHyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_Styled_Hyphenation_WidthOnly() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+
+    @Test
+    public void testCreate_Styled_NoHyphenation_WidthOnly() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                .build();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            PrecomputedText.create(text, param);
+        }
+    }
+}
diff --git a/android/text/SpannableString.java b/android/text/SpannableString.java
index 56d0946..afb5df8 100644
--- a/android/text/SpannableString.java
+++ b/android/text/SpannableString.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-
 /**
  * This is the class for text whose content is immutable but to which
  * markup objects can be attached and detached.
@@ -26,12 +25,27 @@
 extends SpannableStringInternal
 implements CharSequence, GetChars, Spannable
 {
+    /**
+     * @param source source object to copy from
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
+     * @hide
+     */
+    public SpannableString(CharSequence source, boolean ignoreNoCopySpan) {
+        super(source, 0, source.length(), ignoreNoCopySpan);
+    }
+
+    /**
+     * For the backward compatibility reasons, this constructor copies all spans including {@link
+     * android.text.NoCopySpan}.
+     * @param source source text
+     */
     public SpannableString(CharSequence source) {
-        super(source, 0, source.length());
+        this(source, false /* ignoreNoCopySpan */);  // preserve existing NoCopySpan behavior
     }
 
     private SpannableString(CharSequence source, int start, int end) {
-        super(source, start, end);
+        // preserve existing NoCopySpan behavior
+        super(source, start, end, false /* ignoreNoCopySpan */);
     }
 
     public static SpannableString valueOf(CharSequence source) {
diff --git a/android/text/SpannableStringInternal.java b/android/text/SpannableStringInternal.java
index 366ec14..5dd1a52 100644
--- a/android/text/SpannableStringInternal.java
+++ b/android/text/SpannableStringInternal.java
@@ -26,7 +26,7 @@
 /* package */ abstract class SpannableStringInternal
 {
     /* package */ SpannableStringInternal(CharSequence source,
-                                          int start, int end) {
+                                          int start, int end, boolean ignoreNoCopySpan) {
         if (start == 0 && end == source.length())
             mText = source.toString();
         else
@@ -38,24 +38,37 @@
 
         if (source instanceof Spanned) {
             if (source instanceof SpannableStringInternal) {
-                copySpans((SpannableStringInternal) source, start, end);
+                copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan);
             } else {
-                copySpans((Spanned) source, start, end);
+                copySpans((Spanned) source, start, end, ignoreNoCopySpan);
             }
         }
     }
 
     /**
+     * This unused method is left since this is listed in hidden api list.
+     *
+     * Due to backward compatibility reasons, we copy even NoCopySpan by default
+     */
+    /* package */ SpannableStringInternal(CharSequence source, int start, int end) {
+        this(source, start, end, false /* ignoreNoCopySpan */);
+    }
+
+    /**
      * Copies another {@link Spanned} object's spans between [start, end] into this object.
      *
      * @param src Source object to copy from.
      * @param start Start index in the source object.
      * @param end End index in the source object.
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
      */
-    private final void copySpans(Spanned src, int start, int end) {
+    private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) {
         Object[] spans = src.getSpans(start, end, Object.class);
 
         for (int i = 0; i < spans.length; i++) {
+            if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) {
+                continue;
+            }
             int st = src.getSpanStart(spans[i]);
             int en = src.getSpanEnd(spans[i]);
             int fl = src.getSpanFlags(spans[i]);
@@ -76,35 +89,48 @@
      * @param src Source object to copy from.
      * @param start Start index in the source object.
      * @param end End index in the source object.
+     * @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons.
      */
-    private final void copySpans(SpannableStringInternal src, int start, int end) {
-        if (start == 0 && end == src.length()) {
+    private void copySpans(SpannableStringInternal src, int start, int end,
+            boolean ignoreNoCopySpan) {
+        int count = 0;
+        final int[] srcData = src.mSpanData;
+        final Object[] srcSpans = src.mSpans;
+        final int limit = src.mSpanCount;
+        boolean hasNoCopySpan = false;
+
+        for (int i = 0; i < limit; i++) {
+            int spanStart = srcData[i * COLUMNS + START];
+            int spanEnd = srcData[i * COLUMNS + END];
+            if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+            if (srcSpans[i] instanceof NoCopySpan) {
+                hasNoCopySpan = true;
+                if (ignoreNoCopySpan) {
+                    continue;
+                }
+            }
+            count++;
+        }
+
+        if (count == 0) return;
+
+        if (!hasNoCopySpan && start == 0 && end == src.length()) {
             mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length);
             mSpanData = new int[src.mSpanData.length];
             mSpanCount = src.mSpanCount;
             System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length);
             System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length);
         } else {
-            int count = 0;
-            int[] srcData = src.mSpanData;
-            int limit = src.mSpanCount;
-            for (int i = 0; i < limit; i++) {
-                int spanStart = srcData[i * COLUMNS + START];
-                int spanEnd = srcData[i * COLUMNS + END];
-                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
-                count++;
-            }
-
-            if (count == 0) return;
-
-            Object[] srcSpans = src.mSpans;
             mSpanCount = count;
             mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
             mSpanData = new int[mSpans.length * COLUMNS];
             for (int i = 0, j = 0; i < limit; i++) {
                 int spanStart = srcData[i * COLUMNS + START];
                 int spanEnd = srcData[i * COLUMNS + END];
-                if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+                if (isOutOfCopyRange(start, end, spanStart, spanEnd)
+                        || (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) {
+                    continue;
+                }
                 if (spanStart < start) spanStart = start;
                 if (spanEnd > end) spanEnd = end;
 
@@ -494,6 +520,21 @@
         return hash;
     }
 
+    /**
+     * Following two unused methods are left since these are listed in hidden api list.
+     *
+     * Due to backward compatibility reasons, we copy even NoCopySpan by default
+     */
+    private void copySpans(Spanned src, int start, int end) {
+        copySpans(src, start, end, false);
+    }
+
+    private void copySpans(SpannableStringInternal src, int start, int end) {
+        copySpans(src, start, end, false);
+    }
+
+
+
     private String mText;
     private Object[] mSpans;
     private int[] mSpanData;
diff --git a/android/text/SpannedString.java b/android/text/SpannedString.java
index afed221..acee3c5 100644
--- a/android/text/SpannedString.java
+++ b/android/text/SpannedString.java
@@ -26,12 +26,27 @@
 extends SpannableStringInternal
 implements CharSequence, GetChars, Spanned
 {
+    /**
+     * @param source source object to copy from
+     * @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
+     * @hide
+     */
+    public SpannedString(CharSequence source, boolean ignoreNoCopySpan) {
+        super(source, 0, source.length(), ignoreNoCopySpan);
+    }
+
+    /**
+     * For the backward compatibility reasons, this constructor copies all spans including {@link
+     * android.text.NoCopySpan}.
+     * @param source source text
+     */
     public SpannedString(CharSequence source) {
-        super(source, 0, source.length());
+        this(source, false /* ignoreNoCopySpan */);  // preserve existing NoCopySpan behavior
     }
 
     private SpannedString(CharSequence source, int start, int end) {
-        super(source, start, end);
+        // preserve existing NoCopySpan behavior
+        super(source, start, end, false /* ignoreNoCopySpan */);
     }
 
     public CharSequence subSequence(int start, int end) {
diff --git a/android/text/StaticLayout.java b/android/text/StaticLayout.java
index e62f421..0899074 100644
--- a/android/text/StaticLayout.java
+++ b/android/text/StaticLayout.java
@@ -651,48 +651,29 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
-        MeasuredText measured = null;
-        final Spanned spanned;
-        final boolean canUseMeasuredText;
-        if (source instanceof MeasuredText) {
-            measured = (MeasuredText) source;
-
-            if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
-                // The buffer position has changed. Re-measure here.
-                canUseMeasuredText = false;
-            } else if (b.mBreakStrategy != measured.getBreakStrategy()
-                    || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
-                // The computed hyphenation pieces may not be able to used. Re-measure it.
-                canUseMeasuredText = false;
-            } else {
-                // We can use measured information.
-                canUseMeasuredText = true;
+        PrecomputedText.ParagraphInfo[] paragraphInfo = null;
+        final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
+        if (source instanceof PrecomputedText) {
+            PrecomputedText precomputed = (PrecomputedText) source;
+            if (precomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
+                      b.mBreakStrategy, b.mHyphenationFrequency)) {
+                // Some parameters are different from the ones when measured text is created.
+                paragraphInfo = precomputed.getParagraphInfo();
             }
-        } else {
-            canUseMeasuredText = false;
         }
 
-        if (!canUseMeasuredText) {
-            measured = new MeasuredText.Builder(source, paint)
-                    .setRange(bufStart, bufEnd)
-                    .setTextDirection(textDir)
-                    .setBreakStrategy(b.mBreakStrategy)
-                    .setHyphenationFrequency(b.mHyphenationFrequency)
-                    .build(false /* full layout is not necessary for line breaking */);
-            spanned = (source instanceof Spanned) ? (Spanned) source : null;
-        } else {
-            final CharSequence original = measured.getText();
-            spanned = (original instanceof Spanned) ? (Spanned) original : null;
-            // Overwrite with the one when measured.
-            // TODO: Give an option for developer not to overwrite and measure again here?
-            textDir = measured.getTextDir();
-            paint = measured.getPaint();
+        if (paragraphInfo == null) {
+            final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
+                    b.mBreakStrategy, b.mHyphenationFrequency);
+            paragraphInfo = PrecomputedText.createMeasuredParagraphs(source, param, bufStart,
+                    bufEnd, false /* computeLayout */);
         }
 
         try {
-            for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
-                final int paraStart = measured.getParagraphStart(paraIndex);
-                final int paraEnd = measured.getParagraphEnd(paraIndex);
+            for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+                final int paraStart = paraIndex == 0
+                        ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+                final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
 
                 int firstWidthLineCount = 1;
                 int firstWidth = outerWidth;
@@ -758,7 +739,7 @@
                     }
                 }
 
-                final MeasuredParagraph measuredPara = measured.getMeasuredParagraph(paraIndex);
+                final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
                 final char[] chs = measuredPara.getChars();
                 final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
                 final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
diff --git a/android/text/StaticLayoutMultithreadPerfTest.java b/android/text/StaticLayoutMultithreadPerfTest.java
new file mode 100644
index 0000000..60c6d89
--- /dev/null
+++ b/android/text/StaticLayoutMultithreadPerfTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+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.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class StaticLayoutMultithreadPerfTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
+    private static final boolean NO_STYLE_TEXT = false;
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+    public StaticLayoutMultithreadPerfTest() {}
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private CountDownLatch mStartLatch;
+    private AtomicBoolean mThreadState;  // True for running, False for stopped.
+
+    private static final long TIMEOUT_MS = 5000;
+
+    private Thread[] startBackgroundThread(int numOfThreads) {
+        mStartLatch = new CountDownLatch(numOfThreads);
+        mThreadState = new AtomicBoolean(true);
+
+        Thread[] threads = new Thread[numOfThreads];
+        for (int i = 0; i < numOfThreads; ++i) {
+            final int seed = i + 1;
+            threads[i] = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    final TextPerfUtils util = new TextPerfUtils();
+                    util.resetRandom(seed);
+
+                    mStartLatch.countDown();
+                    while (mThreadState.get()) {
+                        final CharSequence text = util.nextRandomParagraph(
+                                WORD_LENGTH, NO_STYLE_TEXT);
+                        StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                                .build();
+                    }
+                }
+            });
+        }
+
+        for (int i = 0; i < numOfThreads; ++i) {
+            threads[i].start();
+        }
+
+        try {
+            mStartLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        return threads;
+    }
+
+    private void finishThreads(Thread[] threads) {
+        mThreadState.set(false);
+        for (Thread thread : threads) {
+            try {
+                thread.join();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        mStartLatch = null;
+        mThreadState = null;
+    }
+
+    private void runRandomTest(int numOfTotalThreads) {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final TextPerfUtils util = new TextPerfUtils();
+        Thread[] threads = startBackgroundThread(numOfTotalThreads - 1);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = util.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+        finishThreads(threads);
+    }
+
+    @Test
+    public void testCreate_RandomText_Thread_1() {
+        runRandomTest(1);
+    }
+
+    @Test
+    public void testCreate_RandomText_Thread_2() {
+        runRandomTest(2);
+    }
+
+    @Test
+    public void testCreate_RandomText_Thread_4() {
+        runRandomTest(4);
+    }
+}
diff --git a/android/text/StaticLayoutPerfTest.java b/android/text/StaticLayoutPerfTest.java
index 682885b..e1a38a0 100644
--- a/android/text/StaticLayoutPerfTest.java
+++ b/android/text/StaticLayoutPerfTest.java
@@ -43,74 +43,42 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class StaticLayoutPerfTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
+    private static final boolean NO_STYLE_TEXT = false;
+    private static final boolean STYLE_TEXT = true;
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
 
     public StaticLayoutPerfTest() {}
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
-    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
-    private static final int PARA_LENGTH = 500;  // Number of characters in a paragraph.
-
-    private static final boolean NO_STYLE_TEXT = false;
-    private static final boolean STYLE_TEXT = true;
-
-    private Random mRandom;
-
-    private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    private static final int ALPHABET_LENGTH = ALPHABET.length();
-
-    private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
-    private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
-    private static final int[] STYLES = {
-            Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
-    };
-
-    private final char[] mBuffer = new char[PARA_LENGTH];
-
-    private static TextPaint PAINT = new TextPaint();
-    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
-
-    private CharSequence generateRandomParagraph(int wordLen, boolean applyRandomStyle) {
-        for (int i = 0; i < PARA_LENGTH; i++) {
-            if (i % (wordLen + 1) == wordLen) {
-                mBuffer[i] = ' ';
-            } else {
-                mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
-            }
-        }
-
-        CharSequence cs = CharBuffer.wrap(mBuffer);
-        if (!applyRandomStyle) {
-            return cs;
-        }
-
-        SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
-        for (int i = 0; i < ssb.length(); i += WORD_LENGTH) {
-            final int spanStart = i;
-            final int spanEnd = (i + WORD_LENGTH) > ssb.length() ? ssb.length() : i + WORD_LENGTH;
-
-            final TextAppearanceSpan span = new TextAppearanceSpan(
-                  FAMILIES[mRandom.nextInt(FAMILIES.length)],
-                  STYLES[mRandom.nextInt(STYLES.length)],
-                  24 + mRandom.nextInt(32),  // text size. min 24 max 56
-                  TEXT_COLOR, TEXT_COLOR);
-
-            ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-        }
-        return ssb;
-    }
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
 
     @Before
     public void setUp() {
-        mRandom = new Random(0);
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) {
+        PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build();
+        return PrecomputedText.create(text, param);
+    }
+
+    private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy,
+                                      int frequency) {
+        PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint)
+                .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build();
+        return PrecomputedText.create(text, param);
     }
 
     @Test
     public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+        final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
         while (state.keepRunning()) {
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
@@ -124,7 +92,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -139,7 +107,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -154,7 +122,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -169,7 +137,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -184,7 +152,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -195,15 +163,13 @@
     }
 
     @Test
-    public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() {
+    public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
-                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                    .build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -214,15 +180,13 @@
     }
 
     @Test
-    public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() {
+    public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
-                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                    .build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -233,15 +197,13 @@
     }
 
     @Test
-    public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() {
+    public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
-                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                    .build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+                    Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -252,15 +214,13 @@
     }
 
     @Test
-    public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() {
+    public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
-                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                    .build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
+                    Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -271,15 +231,13 @@
     }
 
     @Test
-    public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() {
+    public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
-                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                    .build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT,
+                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -292,7 +250,7 @@
     @Test
     public void testDraw_FixedText_NoStyled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+        final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
@@ -311,7 +269,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -327,7 +285,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -343,7 +301,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -360,7 +318,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -372,13 +330,13 @@
     }
 
     @Test
-    public void testDraw_MeasuredText_Styled() {
+    public void testDraw_PrecomputedText_Styled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -389,13 +347,13 @@
     }
 
     @Test
-    public void testDraw_MeasuredText_NoStyled() {
+    public void testDraw_PrecomputedText_NoStyled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -406,13 +364,13 @@
     }
 
     @Test
-    public void testDraw_MeasuredText_Styled_WithoutCache() {
+    public void testDraw_PrecomputedText_Styled_WithoutCache() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -424,13 +382,13 @@
     }
 
     @Test
-    public void testDraw_MeasuredText_NoStyled_WithoutCache() {
+    public void testDraw_PrecomputedText_NoStyled_WithoutCache() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+            final PrecomputedText text = makeMeasured(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
diff --git a/android/text/TextLine.java b/android/text/TextLine.java
index 55367dc..fcb7d62 100644
--- a/android/text/TextLine.java
+++ b/android/text/TextLine.java
@@ -60,7 +60,7 @@
     private char[] mChars;
     private boolean mCharsValid;
     private Spanned mSpanned;
-    private MeasuredText mMeasured;
+    private PrecomputedText mComputed;
 
     // Additional width of whitespace for justification. This value is per whitespace, thus
     // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
@@ -119,7 +119,7 @@
         tl.mSpanned = null;
         tl.mTabs = null;
         tl.mChars = null;
-        tl.mMeasured = null;
+        tl.mComputed = null;
 
         tl.mMetricAffectingSpanSpanSet.recycle();
         tl.mCharacterStyleSpanSet.recycle();
@@ -170,11 +170,13 @@
             hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
         }
 
-        mMeasured = null;
-        if (text instanceof MeasuredText) {
-            MeasuredText mt = (MeasuredText) text;
-            if (mt.canUseMeasuredResult(paint)) {
-                mMeasured = mt;
+        mComputed = null;
+        if (text instanceof PrecomputedText) {
+            // Here, no need to check line break strategy or hyphenation frequency since there is no
+            // line break concept here.
+            mComputed = (PrecomputedText) text;
+            if (!mComputed.getParams().getTextPaint().equalsForTextMeasurement(paint)) {
+                mComputed = null;
             }
         }
 
@@ -746,12 +748,12 @@
             return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
         } else {
             final int delta = mStart;
-            if (mMeasured == null) {
+            if (mComputed == null) {
                 // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
                 return wp.getRunAdvance(mText, delta + start, delta + end,
                         delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
             } else {
-                return mMeasured.getWidth(start + delta, end + delta);
+                return mComputed.getWidth(start + delta, end + delta);
             }
         }
     }
diff --git a/android/text/TextPerfUtils.java b/android/text/TextPerfUtils.java
new file mode 100644
index 0000000..fefda64
--- /dev/null
+++ b/android/text/TextPerfUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import static android.text.TextDirectionHeuristics.LTR;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.CharBuffer;
+import java.util.Random;
+
+public class TextPerfUtils {
+
+    private static final int PARA_LENGTH = 500;  // Number of characters in a paragraph.
+
+    private Random mRandom = new Random(0);
+
+    private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    private static final int ALPHABET_LENGTH = ALPHABET.length();
+
+    private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
+    private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
+    private static final int[] STYLES = {
+            Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
+    };
+
+    private final char[] mBuffer = new char[PARA_LENGTH];
+
+    public void resetRandom(long seed) {
+        mRandom = new Random(seed);
+    }
+
+    public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle) {
+        for (int i = 0; i < PARA_LENGTH; i++) {
+            if (i % (wordLen + 1) == wordLen) {
+                mBuffer[i] = ' ';
+            } else {
+                mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
+            }
+        }
+
+        CharSequence cs = CharBuffer.wrap(mBuffer);
+        if (!applyRandomStyle) {
+            return cs;
+        }
+
+        SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
+        for (int i = 0; i < ssb.length(); i += wordLen + 1) {
+            final int spanStart = i;
+            final int spanEnd = (i + wordLen) > ssb.length() ? ssb.length() : i + wordLen;
+
+            final TextAppearanceSpan span = new TextAppearanceSpan(
+                  FAMILIES[mRandom.nextInt(FAMILIES.length)],
+                  STYLES[mRandom.nextInt(STYLES.length)],
+                  24 + mRandom.nextInt(32),  // text size. min 24 max 56
+                  TEXT_COLOR, TEXT_COLOR);
+
+            ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+        return ssb;
+    }
+}
diff --git a/android/text/format/DateFormat.java b/android/text/format/DateFormat.java
index de2dcce..94025ef 100644
--- a/android/text/format/DateFormat.java
+++ b/android/text/format/DateFormat.java
@@ -159,18 +159,19 @@
     private static Locale sIs24HourLocale;
     private static boolean sIs24Hour;
 
-
     /**
-     * Returns true if user preference is set to 24-hour format.
+     * Returns true if times should be formatted as 24 hour times, false if times should be
+     * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
      * @param context the context to use for the content resolver
      * @return true if 24 hour time format is selected, false otherwise.
      */
     public static boolean is24HourFormat(Context context) {
-        return is24HourFormat(context, UserHandle.myUserId());
+        return is24HourFormat(context, context.getUserId());
     }
 
     /**
-     * Returns true if user preference with the given user handle is set to 24-hour format.
+     * Returns true if times should be formatted as 24 hour times, false if times should be
+     * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
      * @param context the context to use for the content resolver
      * @param userHandle the user handle of the user to query.
      * @return true if 24 hour time format is selected, false otherwise.
@@ -270,7 +271,7 @@
      * @hide
      */
     public static String getTimeFormatString(Context context) {
-        return getTimeFormatString(context, UserHandle.myUserId());
+        return getTimeFormatString(context, context.getUserId());
     }
 
     /**
@@ -431,7 +432,7 @@
             int c = s.charAt(i);
 
             if (c == QUOTE) {
-                count = appendQuotedText(s, i, len);
+                count = appendQuotedText(s, i);
                 len = s.length();
                 continue;
             }
@@ -574,36 +575,48 @@
                             : String.format(Locale.getDefault(), "%d", year);
     }
 
-    private static int appendQuotedText(SpannableStringBuilder s, int i, int len) {
-        if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
-            s.delete(i, i + 1);
+
+    /**
+     * Strips quotation marks from the {@code formatString} and appends the result back to the
+     * {@code formatString}.
+     *
+     * @param formatString the format string, as described in
+     *                     {@link android.text.format.DateFormat}, to be modified
+     * @param index        index of the first quote
+     * @return the length of the quoted text that was appended.
+     * @hide
+     */
+    public static int appendQuotedText(SpannableStringBuilder formatString, int index) {
+        int length = formatString.length();
+        if (index + 1 < length && formatString.charAt(index + 1) == QUOTE) {
+            formatString.delete(index, index + 1);
             return 1;
         }
 
         int count = 0;
 
         // delete leading quote
-        s.delete(i, i + 1);
-        len--;
+        formatString.delete(index, index + 1);
+        length--;
 
-        while (i < len) {
-            char c = s.charAt(i);
+        while (index < length) {
+            char c = formatString.charAt(index);
 
             if (c == QUOTE) {
                 //  QUOTEQUOTE -> QUOTE
-                if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
+                if (index + 1 < length && formatString.charAt(index + 1) == QUOTE) {
 
-                    s.delete(i, i + 1);
-                    len--;
+                    formatString.delete(index, index + 1);
+                    length--;
                     count++;
-                    i++;
+                    index++;
                 } else {
                     //  Closing QUOTE ends quoted text copying
-                    s.delete(i, i + 1);
+                    formatString.delete(index, index + 1);
                     break;
                 }
             } else {
-                i++;
+                index++;
                 count++;
             }
         }
diff --git a/android/text/method/LinkMovementMethod.java b/android/text/method/LinkMovementMethod.java
index 31ed549..f332358 100644
--- a/android/text/method/LinkMovementMethod.java
+++ b/android/text/method/LinkMovementMethod.java
@@ -16,6 +16,7 @@
 
 package android.text.method;
 
+import android.os.Build;
 import android.text.Layout;
 import android.text.NoCopySpan;
 import android.text.Selection;
@@ -35,6 +36,8 @@
     private static final int UP = 2;
     private static final int DOWN = 3;
 
+    private static final int HIDE_FLOATING_TOOLBAR_DELAY_MS = 200;
+
     @Override
     public boolean canSelectArbitrarily() {
         return true;
@@ -65,7 +68,7 @@
 
         return super.up(widget, buffer);
     }
-        
+
     @Override
     protected boolean down(TextView widget, Spannable buffer) {
         if (action(DOWN, widget, buffer)) {
@@ -215,6 +218,12 @@
                 if (action == MotionEvent.ACTION_UP) {
                     links[0].onClick(widget);
                 } else if (action == MotionEvent.ACTION_DOWN) {
+                    if (widget.getContext().getApplicationInfo().targetSdkVersion
+                            > Build.VERSION_CODES.O_MR1) {
+                        // Selection change will reposition the toolbar. Hide it for a few ms for a
+                        // smoother transition.
+                        widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
+                    }
                     Selection.setSelection(buffer,
                         buffer.getSpanStart(links[0]),
                         buffer.getSpanEnd(links[0]));
diff --git a/android/text/style/AlignmentSpan.java b/android/text/style/AlignmentSpan.java
index 6158309..18c3e16 100644
--- a/android/text/style/AlignmentSpan.java
+++ b/android/text/style/AlignmentSpan.java
@@ -16,49 +16,90 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.Layout;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
 
+/**
+ * Span that allows defining the alignment of text at the paragraph level.
+ */
 public interface AlignmentSpan extends ParagraphStyle {
+
+    /**
+     * Returns the alignment of the text.
+     *
+     * @return the text alignment
+     */
     Layout.Alignment getAlignment();
 
+    /**
+     * Default implementation of the {@link AlignmentSpan}.
+     * <p>
+     * For example, a text written in a left to right language, like English, which is by default
+     * aligned to the left, can be aligned opposite to the layout direction like this:
+     * <pre>{@code SpannableString string = new SpannableString("Text with opposite alignment");
+     *string.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE), 0,
+     *string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+     * <img src="{@docRoot}reference/android/images/text/style/ltralignmentspan.png" />
+     * <figcaption>Align left to right text opposite to the layout direction.</figcaption>
+     * <p>
+     * A text written in a right to left language, like Hebrew, which is by default aligned to the
+     * right, can be aligned opposite to the layout direction like this:
+     * <pre>{@code SpannableString string = new SpannableString("טקסט עם יישור הפוך");
+     *string.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE), 0,
+     *string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+     * <img src="{@docRoot}reference/android/images/text/style/rtlalignmentspan.png" />
+     * <figcaption>Align right to left text opposite to the layout direction.</figcaption>
+     */
     class Standard implements AlignmentSpan, ParcelableSpan {
-        public Standard(Layout.Alignment align) {
+        private final Layout.Alignment mAlignment;
+
+        /**
+         * Constructs a {@link Standard} from an alignment.
+         */
+        public Standard(@NonNull Layout.Alignment align) {
             mAlignment = align;
         }
 
-        public Standard(Parcel src) {
+        /**
+         * Constructs a {@link Standard} from a parcel.
+         */
+        public Standard(@NonNull Parcel src) {
             mAlignment = Layout.Alignment.valueOf(src.readString());
         }
-        
-        public int getSpanTypeId() {
-        return getSpanTypeIdInternal();
-    }
 
-    /** @hide */
-    public int getSpanTypeIdInternal() {
+        @Override
+        public int getSpanTypeId() {
+            return getSpanTypeIdInternal();
+        }
+
+        /** @hide */
+        @Override
+        public int getSpanTypeIdInternal() {
             return TextUtils.ALIGNMENT_SPAN;
         }
-        
+
+        @Override
         public int describeContents() {
             return 0;
         }
 
-        public void writeToParcel(Parcel dest, int flags) {
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             writeToParcelInternal(dest, flags);
         }
 
         /** @hide */
-        public void writeToParcelInternal(Parcel dest, int flags) {
+        @Override
+        public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
             dest.writeString(mAlignment.name());
         }
 
+        @Override
         public Layout.Alignment getAlignment() {
             return mAlignment;
         }
-
-        private final Layout.Alignment mAlignment;
     }
 }
diff --git a/android/text/style/ClickableSpan.java b/android/text/style/ClickableSpan.java
index b098f16..60aed2a 100644
--- a/android/text/style/ClickableSpan.java
+++ b/android/text/style/ClickableSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.text.TextPaint;
 import android.view.View;
 
@@ -24,6 +25,16 @@
  * with a movement method of LinkMovementMethod, the affected spans of
  * text can be selected. If selected and clicked, the {@link #onClick} method will
  * be called.
+ * <p>
+ * The text with a <code>ClickableSpan</code> attached will be underlined and the link color will be
+ * used as a text color. The default link color is the theme's accent color or
+ * <code>android:textColorLink</code> if this attribute is defined in the theme.
+ * For example, considering that we have a <code>CustomClickableSpan</code> that extends
+ * <code>ClickableSpan</code>, it can be used like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with clickable text");
+ *string.setSpan(new CustomClickableSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/clickablespan.png" />
+ * <figcaption>Text with <code>ClickableSpan</code>.</figcaption>
  */
 public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
     private static int sIdCounter = 0;
@@ -33,13 +44,13 @@
     /**
      * Performs the click action associated with this span.
      */
-    public abstract void onClick(View widget);
+    public abstract void onClick(@NonNull View widget);
 
     /**
      * Makes the text underlined and in the link color.
      */
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setColor(ds.linkColor);
         ds.setUnderlineText(true);
     }
diff --git a/android/text/style/DrawableMarginSpan.java b/android/text/style/DrawableMarginSpan.java
index 3524179..cd199b3 100644
--- a/android/text/style/DrawableMarginSpan.java
+++ b/android/text/style/DrawableMarginSpan.java
@@ -16,60 +16,100 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.text.Layout;
 import android.text.Spanned;
 
-public class DrawableMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
-    public DrawableMarginSpan(Drawable b) {
-        mDrawable = b;
+/**
+ * A span which adds a drawable and a padding to the paragraph it's attached to.
+ * <p>
+ * If the height of the drawable is bigger than the height of the line it's attached to then the
+ * line height is increased to fit the drawable. <code>DrawableMarginSpan</code> allows setting a
+ * padding between the drawable and the text. The default value is 0. The span must be set from the
+ * beginning of the text, otherwise either the span won't be rendered or it will be rendered
+ * incorrectly.
+ * <p>
+ * For example, a drawable and a padding of 20px can be added like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with a drawable.");
+ * string.setSpan(new DrawableMarginSpan(drawable, 20), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/drawablemarginspan.png" />
+ * <figcaption>Text with a drawable and a padding.</figcaption>
+ * <p>
+ *
+ * @see IconMarginSpan for working with a {@link android.graphics.Bitmap} instead of
+ * a {@link Drawable}.
+ */
+public class DrawableMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+    private static final int STANDARD_PAD_WIDTH = 0;
+
+    @NonNull
+    private final Drawable mDrawable;
+    @Px
+    private final int mPad;
+
+    /**
+     * Creates a {@link DrawableMarginSpan} from a {@link Drawable}. The pad width will be 0.
+     *
+     * @param drawable the drawable to be added
+     */
+    public DrawableMarginSpan(@NonNull Drawable drawable) {
+        this(drawable, STANDARD_PAD_WIDTH);
     }
 
-    public DrawableMarginSpan(Drawable b, int pad) {
-        mDrawable = b;
+    /**
+     * Creates a {@link DrawableMarginSpan} from a {@link Drawable} and a padding, in pixels.
+     *
+     * @param drawable the drawable to be added
+     * @param pad      the distance between the drawable and the text
+     */
+    public DrawableMarginSpan(@NonNull Drawable drawable, int pad) {
+        mDrawable = drawable;
         mPad = pad;
     }
 
+    @Override
     public int getLeadingMargin(boolean first) {
         return mDrawable.getIntrinsicWidth() + mPad;
     }
 
-    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout layout) {
+    @Override
+    public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+            int top, int baseline, int bottom,
+            @NonNull CharSequence text, int start, int end,
+            boolean first, @NonNull Layout layout) {
         int st = ((Spanned) text).getSpanStart(this);
-        int ix = (int)x;
-        int itop = (int)layout.getLineTop(layout.getLineForOffset(st));
+        int ix = (int) x;
+        int itop = (int) layout.getLineTop(layout.getLineForOffset(st));
 
         int dw = mDrawable.getIntrinsicWidth();
         int dh = mDrawable.getIntrinsicHeight();
 
         // XXX What to do about Paint?
-        mDrawable.setBounds(ix, itop, ix+dw, itop+dh);
+        mDrawable.setBounds(ix, itop, ix + dw, itop + dh);
         mDrawable.draw(c);
     }
 
-    public void chooseHeight(CharSequence text, int start, int end,
-                             int istartv, int v,
-                             Paint.FontMetricsInt fm) {
+    @Override
+    public void chooseHeight(@NonNull CharSequence text, int start, int end,
+            int istartv, int v,
+            @NonNull Paint.FontMetricsInt fm) {
         if (end == ((Spanned) text).getSpanEnd(this)) {
             int ht = mDrawable.getIntrinsicHeight();
 
             int need = ht - (v + fm.descent - fm.ascent - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.descent += need;
+            }
 
             need = ht - (v + fm.bottom - fm.top - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.bottom += need;
+            }
         }
     }
-
-    private Drawable mDrawable;
-    private int mPad;
 }
diff --git a/android/text/style/DynamicDrawableSpan.java b/android/text/style/DynamicDrawableSpan.java
index 5b8a6dd..1b16f33 100644
--- a/android/text/style/DynamicDrawableSpan.java
+++ b/android/text/style/DynamicDrawableSpan.java
@@ -16,6 +16,9 @@
 
 package android.text.style;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
@@ -24,32 +27,71 @@
 import java.lang.ref.WeakReference;
 
 /**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text.
+ * <p>
+ * For an implementation that constructs the drawable from various sources (<code>Bitmap</code>,
+ * <code>Drawable</code>, resource id or <code>Uri</code>) use {@link ImageSpan}.
+ * <p>
+ * A simple implementation of <code>DynamicDrawableSpan</code> that uses drawables from resources
+ * looks like this:
+ * <pre>
+ * class MyDynamicDrawableSpan extends DynamicDrawableSpan {
  *
+ * private final Context mContext;
+ * private final int mResourceId;
+ *
+ * public MyDynamicDrawableSpan(Context context, @DrawableRes int resourceId) {
+ *     mContext = context;
+ *     mResourceId = resourceId;
+ * }
+ *
+ * {@literal @}Override
+ * public Drawable getDrawable() {
+ *      Drawable drawable = mContext.getDrawable(mResourceId);
+ *      drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ *      return drawable;
+ * }
+ * }</pre>
+ * The class can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a drawable span");
+ * string.setSpan(new MyDynamicDrawableSpan(context, R.mipmap.ic_launcher), 12, 20, Spanned
+ * .SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/dynamicdrawablespan.png" />
+ * <figcaption>Replacing text with a drawable.</figcaption>
  */
 public abstract class DynamicDrawableSpan extends ReplacementSpan {
-    private static final String TAG = "DynamicDrawableSpan";
-    
+
     /**
      * A constant indicating that the bottom of this span should be aligned
      * with the bottom of the surrounding text, i.e., at the same level as the
      * lowest descender in the text.
      */
     public static final int ALIGN_BOTTOM = 0;
-    
+
     /**
      * A constant indicating that the bottom of this span should be aligned
      * with the baseline of the surrounding text.
      */
     public static final int ALIGN_BASELINE = 1;
-    
+
     protected final int mVerticalAlignment;
-    
+
+    private WeakReference<Drawable> mDrawableRef;
+
+    /**
+     * Creates a {@link DynamicDrawableSpan}. The default vertical alignment is
+     * {@link #ALIGN_BOTTOM}
+     */
     public DynamicDrawableSpan() {
         mVerticalAlignment = ALIGN_BOTTOM;
     }
 
     /**
-     * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}.
+     * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\
+     *
+     * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE}
      */
     protected DynamicDrawableSpan(int verticalAlignment) {
         mVerticalAlignment = verticalAlignment;
@@ -64,22 +106,22 @@
     }
 
     /**
-     * Your subclass must implement this method to provide the bitmap   
+     * Your subclass must implement this method to provide the bitmap
      * to be drawn.  The dimensions of the bitmap must be the same
      * from each call to the next.
      */
     public abstract Drawable getDrawable();
 
     @Override
-    public int getSize(Paint paint, CharSequence text,
-                         int start, int end,
-                         Paint.FontMetricsInt fm) {
+    public int getSize(@NonNull Paint paint, CharSequence text,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @Nullable Paint.FontMetricsInt fm) {
         Drawable d = getCachedDrawable();
         Rect rect = d.getBounds();
 
         if (fm != null) {
-            fm.ascent = -rect.bottom; 
-            fm.descent = 0; 
+            fm.ascent = -rect.bottom;
+            fm.descent = 0;
 
             fm.top = fm.ascent;
             fm.bottom = 0;
@@ -89,12 +131,12 @@
     }
 
     @Override
-    public void draw(Canvas canvas, CharSequence text,
-                     int start, int end, float x, 
-                     int top, int y, int bottom, Paint paint) {
+    public void draw(@NonNull Canvas canvas, CharSequence text,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
+            int top, int y, int bottom, @NonNull Paint paint) {
         Drawable b = getCachedDrawable();
         canvas.save();
-        
+
         int transY = bottom - b.getBounds().bottom;
         if (mVerticalAlignment == ALIGN_BASELINE) {
             transY -= paint.getFontMetricsInt().descent;
@@ -109,8 +151,9 @@
         WeakReference<Drawable> wr = mDrawableRef;
         Drawable d = null;
 
-        if (wr != null)
+        if (wr != null) {
             d = wr.get();
+        }
 
         if (d == null) {
             d = getDrawable();
@@ -119,7 +162,5 @@
 
         return d;
     }
-
-    private WeakReference<Drawable> mDrawableRef;
 }
 
diff --git a/android/text/style/EasyEditSpan.java b/android/text/style/EasyEditSpan.java
index 7af1c2c..9ee0b07 100644
--- a/android/text/style/EasyEditSpan.java
+++ b/android/text/style/EasyEditSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
@@ -79,7 +80,7 @@
     /**
      * Constructor called from {@link TextUtils} to restore the span.
      */
-    public EasyEditSpan(Parcel source) {
+    public EasyEditSpan(@NonNull Parcel source) {
         mPendingIntent = source.readParcelable(null);
         mDeleteEnabled = (source.readByte() == 1);
     }
@@ -90,12 +91,12 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeParcelable(mPendingIntent, 0);
         dest.writeByte((byte) (mDeleteEnabled ? 1 : 0));
     }
diff --git a/android/text/style/IconMarginSpan.java b/android/text/style/IconMarginSpan.java
index 304c83f..ad78bd5 100644
--- a/android/text/style/IconMarginSpan.java
+++ b/android/text/style/IconMarginSpan.java
@@ -16,57 +16,98 @@
 
 package android.text.style;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.text.Layout;
 import android.text.Spanned;
 
-public class IconMarginSpan
-implements LeadingMarginSpan, LineHeightSpan
-{
-    public IconMarginSpan(Bitmap b) {
-        mBitmap = b;
+/**
+ * Paragraph affecting span, that draws a bitmap at the beginning of a text. The span also allows
+ * setting a padding between the bitmap and the text. The default value of the padding is 0px. The
+ * span should be attached from the first character of the text.
+ * <p>
+ * For example, an <code>IconMarginSpan</code> with a bitmap and a padding of 30px can be set
+ * like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with icon and padding");
+ * string.setSpan(new IconMarginSpan(bitmap, 30), 0, string.length(),
+ * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/iconmarginspan.png" />
+ * <figcaption>Text with <code>IconMarginSpan</code></figcaption>
+ * <p>
+ *
+ * @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
+ * a {@link Bitmap}.
+ */
+public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
+
+    @NonNull
+    private final Bitmap mBitmap;
+    @Px
+    private final int mPad;
+
+    /**
+     * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+     *
+     * @param bitmap bitmap to be rendered at the beginning of the text
+     */
+    public IconMarginSpan(@NonNull Bitmap bitmap) {
+        this(bitmap, 0);
     }
 
-    public IconMarginSpan(Bitmap b, int pad) {
-        mBitmap = b;
+    /**
+     * Creates an {@link IconMarginSpan} from a {@link Bitmap}.
+     *
+     * @param bitmap bitmap to be rendered at the beginning of the text
+     * @param pad    padding width, in pixels, between the bitmap and the text
+     */
+    public IconMarginSpan(@NonNull Bitmap bitmap, @IntRange(from = 0) int pad) {
+        mBitmap = bitmap;
         mPad = pad;
     }
 
+    @Override
     public int getLeadingMargin(boolean first) {
         return mBitmap.getWidth() + mPad;
     }
 
+    @Override
     public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout layout) {
+            int top, int baseline, int bottom,
+            CharSequence text, int start, int end,
+            boolean first, Layout layout) {
         int st = ((Spanned) text).getSpanStart(this);
         int itop = layout.getLineTop(layout.getLineForOffset(st));
 
-        if (dir < 0)
+        if (dir < 0) {
             x -= mBitmap.getWidth();
+        }
 
         c.drawBitmap(mBitmap, x, itop, p);
     }
 
+    @Override
     public void chooseHeight(CharSequence text, int start, int end,
-                             int istartv, int v,
-                             Paint.FontMetricsInt fm) {
+            int istartv, int v,
+            Paint.FontMetricsInt fm) {
         if (end == ((Spanned) text).getSpanEnd(this)) {
             int ht = mBitmap.getHeight();
 
             int need = ht - (v + fm.descent - fm.ascent - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.descent += need;
+            }
 
             need = ht - (v + fm.bottom - fm.top - istartv);
-            if (need > 0)
+            if (need > 0) {
                 fm.bottom += need;
+            }
         }
     }
 
-    private Bitmap mBitmap;
-    private int mPad;
 }
diff --git a/android/text/style/ImageSpan.java b/android/text/style/ImageSpan.java
index b0bff68..95f0b43 100644
--- a/android/text/style/ImageSpan.java
+++ b/android/text/style/ImageSpan.java
@@ -17,6 +17,8 @@
 package android.text.style;
 
 import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -27,18 +29,49 @@
 
 import java.io.InputStream;
 
+/**
+ * Span that replaces the text it's attached to with a {@link Drawable} that can be aligned with
+ * the bottom or with the baseline of the surrounding text. The drawable can be constructed from
+ * varied sources:
+ * <ul>
+ * <li>{@link Bitmap} - see {@link #ImageSpan(Context, Bitmap)} and
+ * {@link #ImageSpan(Context, Bitmap, int)}
+ * </li>
+ * <li>{@link Drawable} - see {@link #ImageSpan(Drawable, int)}</li>
+ * <li>resource id - see {@link #ImageSpan(Context, int, int)}</li>
+ * <li>{@link Uri} - see {@link #ImageSpan(Context, Uri, int)}</li>
+ * </ul>
+ * The default value for the vertical alignment is {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+ * <p>
+ * For example, an <code>ImagedSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = SpannableString("Bottom: span.\nBaseline: span.");
+ * // using the default alignment: ALIGN_BOTTOM
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher), 7, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(ImageSpan(this, R.mipmap.ic_launcher, DynamicDrawableSpan.ALIGN_BASELINE),
+ * 22, 23, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/imagespan.png" />
+ * <figcaption>Text with <code>ImageSpan</code>s aligned bottom and baseline.</figcaption>
+ */
 public class ImageSpan extends DynamicDrawableSpan {
+
+    @Nullable
     private Drawable mDrawable;
+    @Nullable
     private Uri mContentUri;
+    @DrawableRes
     private int mResourceId;
+    @Nullable
     private Context mContext;
+    @Nullable
     private String mSource;
 
     /**
      * @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
      */
     @Deprecated
-    public ImageSpan(Bitmap b) {
+    public ImageSpan(@NonNull Bitmap b) {
         this(null, b, ALIGN_BOTTOM);
     }
 
@@ -46,80 +79,143 @@
      * @deprecated Use {@link #ImageSpan(Context, Bitmap, int)} instead.
      */
     @Deprecated
-    public ImageSpan(Bitmap b, int verticalAlignment) {
+    public ImageSpan(@NonNull Bitmap b, int verticalAlignment) {
         this(null, b, verticalAlignment);
     }
 
-    public ImageSpan(Context context, Bitmap b) {
-        this(context, b, ALIGN_BOTTOM);
+    /**
+     * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Bitmap} with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+     *
+     * @param context context used to create a drawable from {@param bitmap} based on the display
+     *                metrics of the resources
+     * @param bitmap  bitmap to be rendered
+     */
+    public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap) {
+        this(context, bitmap, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Bitmap} and a vertical
+     * alignment.
+     *
+     * @param context           context used to create a drawable from {@param bitmap} based on
+     *                          the display metrics of the resources
+     * @param bitmap            bitmap to be rendered
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Context context, Bitmap b, int verticalAlignment) {
+    public ImageSpan(@NonNull Context context, @NonNull Bitmap bitmap, int verticalAlignment) {
         super(verticalAlignment);
         mContext = context;
         mDrawable = context != null
-                ? new BitmapDrawable(context.getResources(), b)
-                : new BitmapDrawable(b);
+                ? new BitmapDrawable(context.getResources(), bitmap)
+                : new BitmapDrawable(bitmap);
         int width = mDrawable.getIntrinsicWidth();
         int height = mDrawable.getIntrinsicHeight();
-        mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0); 
-    }
-
-    public ImageSpan(Drawable d) {
-        this(d, ALIGN_BOTTOM);
+        mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
     }
 
     /**
-     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     * Constructs an {@link ImageSpan} from a drawable with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}.
+     *
+     * @param drawable drawable to be rendered
      */
-    public ImageSpan(Drawable d, int verticalAlignment) {
-        super(verticalAlignment);
-        mDrawable = d;
-    }
-
-    public ImageSpan(Drawable d, String source) {
-        this(d, source, ALIGN_BOTTOM);
+    public ImageSpan(@NonNull Drawable drawable) {
+        this(drawable, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a drawable and a vertical alignment.
+     *
+     * @param drawable          drawable to be rendered
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Drawable d, String source, int verticalAlignment) {
+    public ImageSpan(@NonNull Drawable drawable, int verticalAlignment) {
         super(verticalAlignment);
-        mDrawable = d;
+        mDrawable = drawable;
+    }
+
+    /**
+     * Constructs an {@link ImageSpan} from a drawable and a source with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+     *
+     * @param drawable drawable to be rendered
+     * @param source   drawable's Uri source
+     */
+    public ImageSpan(@NonNull Drawable drawable, @NonNull String source) {
+        this(drawable, source, ALIGN_BOTTOM);
+    }
+
+    /**
+     * Constructs an {@link ImageSpan} from a drawable, a source and a vertical alignment.
+     *
+     * @param drawable          drawable to be rendered
+     * @param source            drawable's uri source
+     * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
+     */
+    public ImageSpan(@NonNull Drawable drawable, @NonNull String source, int verticalAlignment) {
+        super(verticalAlignment);
+        mDrawable = drawable;
         mSource = source;
     }
 
-    public ImageSpan(Context context, Uri uri) {
+    /**
+     * Constructs an {@link ImageSpan} from a {@link Context} and a {@link Uri} with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}. The Uri source can be retrieved via
+     * {@link #getSource()}
+     *
+     * @param context context used to create a drawable from {@param bitmap} based on the display
+     *                metrics of the resources
+     * @param uri     {@link Uri} used to construct the drawable that will be rendered
+     */
+    public ImageSpan(@NonNull Context context, @NonNull Uri uri) {
         this(context, uri, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a {@link Context}, a {@link Uri} and a vertical
+     * alignment. The Uri source can be retrieved via {@link #getSource()}
+     *
+     * @param context           context used to create a drawable from {@param bitmap} based on
+     *                          the display
+     *                          metrics of the resources
+     * @param uri               {@link Uri} used to construct the drawable that will be rendered.
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Context context, Uri uri, int verticalAlignment) {
+    public ImageSpan(@NonNull Context context, @NonNull Uri uri, int verticalAlignment) {
         super(verticalAlignment);
         mContext = context;
         mContentUri = uri;
         mSource = uri.toString();
     }
 
-    public ImageSpan(Context context, @DrawableRes int resourceId) {
+    /**
+     * Constructs an {@link ImageSpan} from a {@link Context} and a resource id with the default
+     * alignment {@link DynamicDrawableSpan#ALIGN_BOTTOM}
+     *
+     * @param context    context used to retrieve the drawable from resources
+     * @param resourceId drawable resource id based on which the drawable is retrieved
+     */
+    public ImageSpan(@NonNull Context context, @DrawableRes int resourceId) {
         this(context, resourceId, ALIGN_BOTTOM);
     }
 
     /**
+     * Constructs an {@link ImageSpan} from a {@link Context}, a resource id and a vertical
+     * alignment.
+     *
+     * @param context           context used to retrieve the drawable from resources
+     * @param resourceId        drawable resource id based on which the drawable is retrieved.
      * @param verticalAlignment one of {@link DynamicDrawableSpan#ALIGN_BOTTOM} or
-     * {@link DynamicDrawableSpan#ALIGN_BASELINE}.
+     *                          {@link DynamicDrawableSpan#ALIGN_BASELINE}
      */
-    public ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
+    public ImageSpan(@NonNull Context context, @DrawableRes int resourceId,
+            int verticalAlignment) {
         super(verticalAlignment);
         mContext = context;
         mResourceId = resourceId;
@@ -128,10 +224,10 @@
     @Override
     public Drawable getDrawable() {
         Drawable drawable = null;
-        
+
         if (mDrawable != null) {
             drawable = mDrawable;
-        } else  if (mContentUri != null) {
+        } else if (mContentUri != null) {
             Bitmap bitmap = null;
             try {
                 InputStream is = mContext.getContentResolver().openInputStream(
@@ -142,7 +238,7 @@
                         drawable.getIntrinsicHeight());
                 is.close();
             } catch (Exception e) {
-                Log.e("sms", "Failed to loaded content " + mContentUri, e);
+                Log.e("ImageSpan", "Failed to loaded content " + mContentUri, e);
             }
         } else {
             try {
@@ -150,8 +246,8 @@
                 drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                         drawable.getIntrinsicHeight());
             } catch (Exception e) {
-                Log.e("sms", "Unable to find resource: " + mResourceId);
-            }                
+                Log.e("ImageSpan", "Unable to find resource: " + mResourceId);
+            }
         }
 
         return drawable;
@@ -159,9 +255,12 @@
 
     /**
      * Returns the source string that was saved during construction.
+     *
+     * @return the source string that was saved during construction
+     * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
      */
+    @Nullable
     public String getSource() {
         return mSource;
     }
-
 }
diff --git a/android/text/style/LineHeightSpan.java b/android/text/style/LineHeightSpan.java
index 1ebee82..50ee5f3 100644
--- a/android/text/style/LineHeightSpan.java
+++ b/android/text/style/LineHeightSpan.java
@@ -19,16 +19,42 @@
 import android.graphics.Paint;
 import android.text.TextPaint;
 
-public interface LineHeightSpan
-extends ParagraphStyle, WrapTogetherSpan
-{
+/**
+ * The classes that affect the height of the line should implement this interface.
+ */
+public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
+    /**
+     * Classes that implement this should define how the height is being calculated.
+     *
+     * @param text       the text
+     * @param start      the start of the line
+     * @param end        the end of the line
+     * @param spanstartv the start of the span
+     * @param lineHeight the line height
+     * @param fm         font metrics of the paint, in integers
+     */
     public void chooseHeight(CharSequence text, int start, int end,
-                             int spanstartv, int v,
-                             Paint.FontMetricsInt fm);
+            int spanstartv, int lineHeight,
+            Paint.FontMetricsInt fm);
 
+    /**
+     * The classes that affect the height of the line with respect to density, should implement this
+     * interface.
+     */
     public interface WithDensity extends LineHeightSpan {
+
+        /**
+         * Classes that implement this should define how the height is being calculated.
+         *
+         * @param text       the text
+         * @param start      the start of the line
+         * @param end        the end of the line
+         * @param spanstartv the start of the span
+         * @param lineHeight the line height
+         * @param paint      the paint
+         */
         public void chooseHeight(CharSequence text, int start, int end,
-                                 int spanstartv, int v,
-                                 Paint.FontMetricsInt fm, TextPaint paint);
+                int spanstartv, int lineHeight,
+                Paint.FontMetricsInt fm, TextPaint paint);
     }
 }
diff --git a/android/text/style/MaskFilterSpan.java b/android/text/style/MaskFilterSpan.java
index 2ff52a8..d76ef94 100644
--- a/android/text/style/MaskFilterSpan.java
+++ b/android/text/style/MaskFilterSpan.java
@@ -18,15 +18,36 @@
 
 import android.graphics.MaskFilter;
 import android.text.TextPaint;
-
+/**
+ * Span that allows setting a {@link MaskFilter} to the text it's attached to.
+ * <p>
+ * For example, to blur a text, a {@link android.graphics.BlurMaskFilter} can be used:
+ * <pre>
+ * MaskFilter blurMask = new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL);
+ * SpannableString string = new SpannableString("Text with blur mask");
+ * string.setSpan(new MaskFilterSpan(blurMask), 10, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
+ * <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
+ */
 public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
 
     private MaskFilter mFilter;
 
+    /**
+     * Creates a {@link MaskFilterSpan} from a {@link MaskFilter}.
+     *
+     * @param filter the filter to be applied to the <code>TextPaint</code>
+     */
     public MaskFilterSpan(MaskFilter filter) {
         mFilter = filter;
     }
 
+    /**
+     * Return the mask filter for this span.
+     *
+     * @return the mask filter for this span
+     */
     public MaskFilter getMaskFilter() {
         return mFilter;
     }
diff --git a/android/text/style/MetricAffectingSpan.java b/android/text/style/MetricAffectingSpan.java
index 853ecc6..61b7947 100644
--- a/android/text/style/MetricAffectingSpan.java
+++ b/android/text/style/MetricAffectingSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.text.TextPaint;
 
 /**
@@ -23,13 +24,19 @@
  * changes the width or height of characters extend this class.
  */
 public abstract class MetricAffectingSpan
-extends CharacterStyle
-implements UpdateLayout {
-
-    public abstract void updateMeasureState(TextPaint p);
+        extends CharacterStyle
+        implements UpdateLayout {
 
     /**
-     * Returns "this" for most MetricAffectingSpans, but for 
+     * Classes that extend MetricAffectingSpan implement this method to update the text formatting
+     * in a way that can change the width or height of characters.
+     *
+     * @param textPaint the paint used for drawing the text
+     */
+    public abstract void updateMeasureState(@NonNull TextPaint textPaint);
+
+    /**
+     * Returns "this" for most MetricAffectingSpans, but for
      * MetricAffectingSpans that were generated by {@link #wrap},
      * returns the underlying MetricAffectingSpan.
      */
@@ -41,18 +48,18 @@
     /**
      * A Passthrough MetricAffectingSpan is one that
      * passes {@link #updateDrawState} and {@link #updateMeasureState}
-     * calls through to the specified MetricAffectingSpan 
+     * calls through to the specified MetricAffectingSpan
      * while still being a distinct object,
      * and is therefore able to be attached to the same Spannable
      * to which the specified MetricAffectingSpan is already attached.
      */
     /* package */ static class Passthrough extends MetricAffectingSpan {
         private MetricAffectingSpan mStyle;
-        
+
         /**
          * Creates a new Passthrough of the specfied MetricAffectingSpan.
          */
-        public Passthrough(MetricAffectingSpan cs) {
+        Passthrough(@NonNull MetricAffectingSpan cs) {
             mStyle = cs;
         }
 
@@ -60,7 +67,7 @@
          * Passes updateDrawState through to the underlying MetricAffectingSpan.
          */
         @Override
-        public void updateDrawState(TextPaint tp) {
+        public void updateDrawState(@NonNull TextPaint tp) {
             mStyle.updateDrawState(tp);
         }
 
@@ -68,10 +75,10 @@
          * Passes updateMeasureState through to the underlying MetricAffectingSpan.
          */
         @Override
-        public void updateMeasureState(TextPaint tp) {
+        public void updateMeasureState(@NonNull TextPaint tp) {
             mStyle.updateMeasureState(tp);
         }
-    
+
         /**
          * Returns the MetricAffectingSpan underlying this one, or the one
          * underlying it if it too is a Passthrough.
diff --git a/android/text/style/QuoteSpan.java b/android/text/style/QuoteSpan.java
index 7217e1e..a1c12c2 100644
--- a/android/text/style/QuoteSpan.java
+++ b/android/text/style/QuoteSpan.java
@@ -17,6 +17,9 @@
 package android.text.style;
 
 import android.annotation.ColorInt;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.os.Parcel;
@@ -24,68 +27,178 @@
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
 
+/**
+ * A span which styles paragraphs by adding a vertical stripe at the beginning of the text
+ * (respecting layout direction).
+ * <p>
+ * A <code>QuoteSpan</code> must be attached from the first character to the last character of a
+ * single paragraph, otherwise the span will not be displayed.
+ * <p>
+ * <code>QuoteSpans</code> allow configuring the following elements:
+ * <ul>
+ * <li><b>color</b> - the vertical stripe color. By default, the stripe color is 0xff0000ff</li>
+ * <li><b>gap width</b> - the distance, in pixels, between the stripe and the paragraph.
+ * Default value is 2px.</li>
+ * <li><b>stripe width</b> - the width, in pixels, of the stripe. Default value is
+ * 2px.</li>
+ * </ul>
+ * For example, a <code>QuoteSpan</code> using the default values can be constructed like this:
+ * <pre>{@code SpannableString string = new SpannableString("Text with quote span on a long line");
+ *string.setSpan(new QuoteSpan(), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/defaultquotespan.png" />
+ * <figcaption><code>QuoteSpan</code> constructed with default values.</figcaption>
+ * <p>
+ * <p>
+ * To construct a <code>QuoteSpan</code> with a green stripe, of 20px in width and a gap width of
+ * 40px:
+ * <pre>{@code SpannableString string = new SpannableString("Text with quote span on a long line");
+ *string.setSpan(new QuoteSpan(Color.GREEN, 20, 40), 0, string.length(),
+ *Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/customquotespan.png" />
+ * <figcaption>Customized <code>QuoteSpan</code>.</figcaption>
+ */
 public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan {
-    private static final int STRIPE_WIDTH = 2;
-    private static final int GAP_WIDTH = 2;
+    /**
+     * Default stripe width in pixels.
+     */
+    public static final int STANDARD_STRIPE_WIDTH_PX = 2;
 
+    /**
+     * Default gap width in pixels.
+     */
+    public static final int STANDARD_GAP_WIDTH_PX = 2;
+
+    /**
+     * Default color for the quote stripe.
+     */
+    @ColorInt
+    public static final int STANDARD_COLOR = 0xff0000ff;
+
+    @ColorInt
     private final int mColor;
+    @Px
+    private final int mStripeWidth;
+    @Px
+    private final int mGapWidth;
 
+    /**
+     * Creates a {@link QuoteSpan} with the default values.
+     */
     public QuoteSpan() {
-        super();
-        mColor = 0xff0000ff;
+        this(STANDARD_COLOR, STANDARD_STRIPE_WIDTH_PX, STANDARD_GAP_WIDTH_PX);
     }
 
+    /**
+     * Creates a {@link QuoteSpan} based on a color.
+     *
+     * @param color the color of the quote stripe.
+     */
     public QuoteSpan(@ColorInt int color) {
-        super();
+        this(color, STANDARD_STRIPE_WIDTH_PX, STANDARD_GAP_WIDTH_PX);
+    }
+
+    /**
+     * Creates a {@link QuoteSpan} based on a color, a stripe width and the width of the gap
+     * between the stripe and the text.
+     *
+     * @param color       the color of the quote stripe.
+     * @param stripeWidth the width of the stripe.
+     * @param gapWidth    the width of the gap between the stripe and the text.
+     */
+    public QuoteSpan(@ColorInt int color, @IntRange(from = 0) int stripeWidth,
+            @IntRange(from = 0) int gapWidth) {
         mColor = color;
+        mStripeWidth = stripeWidth;
+        mGapWidth = gapWidth;
     }
 
-    public QuoteSpan(Parcel src) {
+    /**
+     * Create a {@link QuoteSpan} from a parcel.
+     */
+    public QuoteSpan(@NonNull Parcel src) {
         mColor = src.readInt();
+        mStripeWidth = src.readInt();
+        mGapWidth = src.readInt();
     }
 
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
-    /** @hide */
+    /**
+     * @hide
+     */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.QUOTE_SPAN;
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
-    /** @hide */
+    /**
+     * @hide
+     */
+    @Override
     public void writeToParcelInternal(Parcel dest, int flags) {
         dest.writeInt(mColor);
+        dest.writeInt(mStripeWidth);
+        dest.writeInt(mGapWidth);
     }
 
+    /**
+     * Get the color of the quote stripe.
+     *
+     * @return the color of the quote stripe.
+     */
     @ColorInt
     public int getColor() {
         return mColor;
     }
 
-    public int getLeadingMargin(boolean first) {
-        return STRIPE_WIDTH + GAP_WIDTH;
+    /**
+     * Get the width of the quote stripe.
+     *
+     * @return the width of the quote stripe.
+     */
+    public int getStripeWidth() {
+        return mStripeWidth;
     }
 
-    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout layout) {
+    /**
+     * Get the width of the gap between the stripe and the text.
+     *
+     * @return the width of the gap between the stripe and the text.
+     */
+    public int getGapWidth() {
+        return mGapWidth;
+    }
+
+    @Override
+    public int getLeadingMargin(boolean first) {
+        return mStripeWidth + mGapWidth;
+    }
+
+    @Override
+    public void drawLeadingMargin(@NonNull Canvas c, @NonNull Paint p, int x, int dir,
+            int top, int baseline, int bottom,
+            @NonNull CharSequence text, int start, int end,
+            boolean first, @NonNull Layout layout) {
         Paint.Style style = p.getStyle();
         int color = p.getColor();
 
         p.setStyle(Paint.Style.FILL);
         p.setColor(mColor);
 
-        c.drawRect(x, top, x + dir * STRIPE_WIDTH, bottom, p);
+        c.drawRect(x, top, x + dir * mStripeWidth, bottom, p);
 
         p.setStyle(style);
         p.setColor(color);
diff --git a/android/text/style/StyleSpan.java b/android/text/style/StyleSpan.java
index f900db5..bdfa700 100644
--- a/android/text/style/StyleSpan.java
+++ b/android/text/style/StyleSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.os.Parcel;
@@ -24,55 +25,76 @@
 import android.text.TextUtils;
 
 /**
- * 
- * Describes a style in a span.
+ * Span that allows setting the style of the text it's attached to.
+ * Possible styles are: {@link Typeface#NORMAL}, {@link Typeface#BOLD}, {@link Typeface#ITALIC} and
+ * {@link Typeface#BOLD_ITALIC}.
+ * <p>
  * Note that styles are cumulative -- if both bold and italic are set in
  * separate spans, or if the base style is bold and a span calls for italic,
  * you get bold italic.  You can't turn off a style from the base style.
- *
+ * <p>
+ * For example, the <code>StyleSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Bold and italic text");
+ * string.setSpan(new StyleSpan(Typeface.BOLD), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new StyleSpan(Typeface.ITALIC), 9, 15, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
+ * <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
  */
 public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final int mStyle;
 
     /**
-     * 
+     * Creates a {@link StyleSpan} from a style.
+     *
      * @param style An integer constant describing the style for this span. Examples
-     * include bold, italic, and normal. Values are constants defined 
-     * in {@link android.graphics.Typeface}.
+     *              include bold, italic, and normal. Values are constants defined
+     *              in {@link Typeface}.
      */
     public StyleSpan(int style) {
         mStyle = style;
     }
 
-    public StyleSpan(Parcel src) {
+    /**
+     * Creates a {@link StyleSpan} from a parcel.
+     *
+     * @param src the parcel
+     */
+    public StyleSpan(@NonNull Parcel src) {
         mStyle = src.readInt();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.STYLE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mStyle);
     }
 
     /**
-     * Returns the style constant defined in {@link android.graphics.Typeface}. 
+     * Returns the style constant defined in {@link Typeface}.
      */
     public int getStyle() {
         return mStyle;
diff --git a/android/text/style/TabStopSpan.java b/android/text/style/TabStopSpan.java
index 0566428..2cceb2c 100644
--- a/android/text/style/TabStopSpan.java
+++ b/android/text/style/TabStopSpan.java
@@ -16,39 +16,54 @@
 
 package android.text.style;
 
+import android.annotation.IntRange;
+import android.annotation.Px;
+
 /**
- * Represents a single tab stop on a line.
+ * Paragraph affecting span that changes the position of the tab with respect to
+ * the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
+ * encountered on the first line of the text.
  */
-public interface TabStopSpan
-extends ParagraphStyle
-{
-    /**
-     * Returns the offset of the tab stop from the leading margin of the
-     * line.
-     * @return the offset
-     */
-    public int getTabStop();
+public interface TabStopSpan extends ParagraphStyle {
 
     /**
-     * The default implementation of TabStopSpan.
+     * Returns the offset of the tab stop from the leading margin of the line, in pixels.
+     *
+     * @return the offset, in pixels
      */
-    public static class Standard
-    implements TabStopSpan
-    {
+    int getTabStop();
+
+    /**
+     * The default implementation of TabStopSpan that allows setting the offset of the tab stop
+     * from the leading margin of the first line of text.
+     * <p>
+     * Let's consider that we have the following text: <i>"\tParagraph text beginning with tab."</i>
+     * and we want to move the tab stop with 100px. This can be achieved like this:
+     * <pre>
+     * SpannableString string = new SpannableString("\tParagraph text beginning with tab.");
+     * string.setSpan(new TabStopSpan.Standard(100), 0, string.length(),
+     * Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);</pre>
+     * <img src="{@docRoot}reference/android/images/text/style/tabstopspan.png" />
+     * <figcaption>Text with a tab stop and a <code>TabStopSpan</code></figcaption>
+     */
+    class Standard implements TabStopSpan {
+
+        @Px
+        private int mTabOffset;
+
         /**
-         * Constructor.
+         * Constructs a {@link TabStopSpan.Standard} based on an offset.
          *
-         * @param where the offset of the tab stop from the leading margin of
-         *        the line
+         * @param offset the offset of the tab stop from the leading margin of
+         *               the line, in pixels
          */
-        public Standard(int where) {
-            mTab = where;
+        public Standard(@IntRange(from = 0) int offset) {
+            mTabOffset = offset;
         }
 
+        @Override
         public int getTabStop() {
-            return mTab;
+            return mTabOffset;
         }
-
-        private int mTab;
     }
 }
diff --git a/android/text/style/TypefaceSpan.java b/android/text/style/TypefaceSpan.java
index aa622d8..908de29 100644
--- a/android/text/style/TypefaceSpan.java
+++ b/android/text/style/TypefaceSpan.java
@@ -16,6 +16,9 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.LeakyTypefaceStorage;
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.os.Parcel;
@@ -24,74 +27,149 @@
 import android.text.TextUtils;
 
 /**
- * Changes the typeface family of the text to which the span is attached.
+ * Span that updates the typeface of the text it's attached to. The <code>TypefaceSpan</code> can
+ * be constructed either based on a font family or based on a <code>Typeface</code>. When
+ * {@link #TypefaceSpan(String)} is used, the previous style of the <code>TextView</code> is kept.
+ * When {@link #TypefaceSpan(Typeface)} is used, the <code>Typeface</code> style replaces the
+ * <code>TextView</code>'s style.
+ * <p>
+ * For example, let's consider a <code>TextView</code> with
+ * <code>android:textStyle="italic"</code> and a typeface created based on a font from resources,
+ * with a bold style. When applying a <code>TypefaceSpan</code> based the typeface, the text will
+ * only keep the bold style, overriding the <code>TextView</code>'s textStyle. When applying a
+ * <code>TypefaceSpan</code> based on a font family: "monospace", the resulted text will keep the
+ * italic style.
+ * <pre>
+ * Typeface myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme),
+ * Typeface.BOLD);
+ * SpannableString string = new SpannableString("Text with typeface span.");
+ * string.setSpan(new TypefaceSpan(myTypeface), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
+ * <figcaption>Text with <code>TypefaceSpan</code>s constructed based on a font from resource and
+ * from a font family.</figcaption>
  */
 public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+
+    @Nullable
     private final String mFamily;
 
+    @Nullable
+    private final Typeface mTypeface;
+
     /**
+     * Constructs a {@link TypefaceSpan} based on the font family. The previous style of the
+     * TextPaint is kept. If the font family is null, the text paint is not modified.
+     *
      * @param family The font family for this typeface.  Examples include
-     * "monospace", "serif", and "sans-serif".
+     *               "monospace", "serif", and "sans-serif"
      */
-    public TypefaceSpan(String family) {
-        mFamily = family;
+    public TypefaceSpan(@Nullable String family) {
+        this(family, null);
     }
 
-    public TypefaceSpan(Parcel src) {
-        mFamily = src.readString();
+    /**
+     * Constructs a {@link TypefaceSpan} from a {@link Typeface}. The previous style of the
+     * TextPaint is overridden and the style of the typeface is used.
+     *
+     * @param typeface the typeface
+     */
+    public TypefaceSpan(@NonNull Typeface typeface) {
+        this(null, typeface);
     }
-    
+
+    /**
+     * Constructs a {@link TypefaceSpan} from a  parcel.
+     */
+    public TypefaceSpan(@NonNull Parcel src) {
+        mFamily = src.readString();
+        mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
+    }
+
+    private TypefaceSpan(@Nullable String family, @Nullable Typeface typeface) {
+        mFamily = family;
+        mTypeface = typeface;
+    }
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.TYPEFACE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeString(mFamily);
+        LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
     }
 
     /**
-     * Returns the font family name.
+     * Returns the font family name set in the span.
+     *
+     * @return the font family name
+     * @see #TypefaceSpan(String)
      */
+    @Nullable
     public String getFamily() {
         return mFamily;
     }
 
-    @Override
-    public void updateDrawState(TextPaint ds) {
-        apply(ds, mFamily);
+    /**
+     * Returns the typeface set in the span.
+     *
+     * @return the typeface set
+     * @see #TypefaceSpan(Typeface)
+     */
+    @Nullable
+    public Typeface getTypeface() {
+        return mTypeface;
     }
 
     @Override
-    public void updateMeasureState(TextPaint paint) {
-        apply(paint, mFamily);
+    public void updateDrawState(@NonNull TextPaint ds) {
+        updateTypeface(ds);
     }
 
-    private static void apply(Paint paint, String family) {
-        int oldStyle;
+    @Override
+    public void updateMeasureState(@NonNull TextPaint paint) {
+        updateTypeface(paint);
+    }
 
+    private void updateTypeface(@NonNull Paint paint) {
+        if (mTypeface != null) {
+            paint.setTypeface(mTypeface);
+        } else if (mFamily != null) {
+            applyFontFamily(paint, mFamily);
+        }
+    }
+
+    private void applyFontFamily(@NonNull Paint paint, @NonNull String family) {
+        int style;
         Typeface old = paint.getTypeface();
         if (old == null) {
-            oldStyle = 0;
+            style = Typeface.NORMAL;
         } else {
-            oldStyle = old.getStyle();
+            style = old.getStyle();
         }
-
-        Typeface tf = Typeface.create(family, oldStyle);
-        int fake = oldStyle & ~tf.getStyle();
+        final Typeface styledTypeface = Typeface.create(family, style);
+        int fake = style & ~styledTypeface.getStyle();
 
         if ((fake & Typeface.BOLD) != 0) {
             paint.setFakeBoldText(true);
@@ -100,7 +178,6 @@
         if ((fake & Typeface.ITALIC) != 0) {
             paint.setTextSkewX(-0.25f);
         }
-
-        paint.setTypeface(tf);
+        paint.setTypeface(styledTypeface);
     }
 }
diff --git a/android/text/style/URLSpan.java b/android/text/style/URLSpan.java
index 58239ef..eab1ef4 100644
--- a/android/text/style/URLSpan.java
+++ b/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -27,40 +28,71 @@
 import android.util.Log;
 import android.view.View;
 
+/**
+ * Implementation of the {@link ClickableSpan} that allows setting a url string. When
+ * selecting and clicking on the text to which the span is attached, the <code>URLSpan</code>
+ * will try to open the url, by launching an an Activity with an {@link Intent#ACTION_VIEW} intent.
+ * <p>
+ * For example, a <code>URLSpan</code> can be used like this:
+ * <pre>
+ * SpannableString string = new SpannableString("Text with a url span");
+ * string.setSpan(new URLSpan("http://www.developer.android.com"), 12, 15, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * </pre>
+ * <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
+ * <figcaption>Text with <code>URLSpan</code>.</figcaption>
+ */
 public class URLSpan extends ClickableSpan implements ParcelableSpan {
 
     private final String mURL;
 
+    /**
+     * Constructs a {@link URLSpan} from a url string.
+     *
+     * @param url the url string
+     */
     public URLSpan(String url) {
         mURL = url;
     }
 
-    public URLSpan(Parcel src) {
+    /**
+     * Constructs a {@link URLSpan} from a parcel.
+     */
+    public URLSpan(@NonNull Parcel src) {
         mURL = src.readString();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.URL_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeString(mURL);
     }
 
+    /**
+     * Get the url string for this span.
+     *
+     * @return the url string.
+     */
     public String getURL() {
         return mURL;
     }
diff --git a/android/text/util/Linkify.java b/android/text/util/Linkify.java
index 768aee9..9c6a3f5 100644
--- a/android/text/util/Linkify.java
+++ b/android/text/util/Linkify.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UiThread;
 import android.content.Context;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
@@ -29,12 +30,17 @@
 import android.text.method.MovementMethod;
 import android.text.style.URLSpan;
 import android.util.Patterns;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
+import android.view.textclassifier.TextLinksParams;
 import android.webkit.WebView;
 import android.widget.TextView;
 
 import com.android.i18n.phonenumbers.PhoneNumberMatch;
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
+import com.android.internal.util.Preconditions;
 
 import libcore.util.EmptyArray;
 
@@ -46,6 +52,11 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -479,6 +490,236 @@
         return hasMatches;
     }
 
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text (to avoid
+     * problems if you call it repeatedly on the same text) and sets the movement method for the
+     * TextView to LinkMovementMethod.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * @param textView TextView whose text is to be marked-up with links
+     * @param params optional parameters to specify how to generate the links
+     *
+     * @return a future that may be used to interrupt or query the background task
+     * @hide
+     */
+    @UiThread
+    public static Future<Void> addLinksAsync(
+            @NonNull TextView textView,
+            @Nullable TextLinksParams params) {
+        return addLinksAsync(textView, params, null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text (to avoid
+     * problems if you call it repeatedly on the same text) and sets the movement method for the
+     * TextView to LinkMovementMethod.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * @param textView TextView whose text is to be marked-up with links
+     * @param mask mask to define which kinds of links will be generated
+     *
+     * @return a future that may be used to interrupt or query the background task
+     * @hide
+     */
+    @UiThread
+    public static Future<Void> addLinksAsync(
+            @NonNull TextView textView,
+            @LinkifyMask int mask) {
+        return addLinksAsync(textView, TextLinksParams.fromLinkMask(mask),
+                null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text (to avoid
+     * problems if you call it repeatedly on the same text) and sets the movement method for the
+     * TextView to LinkMovementMethod.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * @param textView TextView whose text is to be marked-up with links
+     * @param params optional parameters to specify how to generate the links
+     * @param executor Executor that runs the background task
+     * @param callback Callback that receives the final status of the background task execution
+     *
+     * @return a future that may be used to interrupt or query the background task
+     * @hide
+     */
+    @UiThread
+    public static Future<Void> addLinksAsync(
+            @NonNull TextView textView,
+            @Nullable TextLinksParams params,
+            @Nullable Executor executor,
+            @Nullable Consumer<Integer> callback) {
+        Preconditions.checkNotNull(textView);
+        final CharSequence text = textView.getText();
+        final Spannable spannable = (text instanceof Spannable)
+                ? (Spannable) text : SpannableString.valueOf(text);
+        final Runnable modifyTextView = () -> {
+            addLinkMovementMethod(textView);
+            if (spannable != text) {
+                textView.setText(spannable);
+            }
+        };
+        return addLinksAsync(spannable, textView.getTextClassifier(),
+                params, executor, callback, modifyTextView);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text to avoid
+     * problems if you call it repeatedly on the same text.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * <p><strong>Note:</strong> If the text is currently attached to a TextView, this method
+     * should be called on the UI thread.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param classifier the TextClassifier to use to generate the links
+     * @param params optional parameters to specify how to generate the links
+     *
+     * @return a future that may be used to interrupt or query the background task
+     * @hide
+     */
+    public static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @Nullable TextLinksParams params) {
+        return addLinksAsync(text, classifier, params, null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by the link {@code mask} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text to avoid
+     * problems if you call it repeatedly on the same text.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * <p><strong>Note:</strong> If the text is currently attached to a TextView, this method
+     * should be called on the UI thread.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param classifier the TextClassifier to use to generate the links
+     * @param mask mask to define which kinds of links will be generated
+     *
+     * @return a future that may be used to interrupt or query the background task
+     * @hide
+     */
+    public static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @LinkifyMask int mask) {
+        return addLinksAsync(text, classifier, TextLinksParams.fromLinkMask(mask),
+                null /* executor */, null /* callback */);
+    }
+
+    /**
+     * Scans the text of the provided TextView and turns all occurrences of the entity types
+     * specified by {@code options} into clickable links. If links are found, this method
+     * removes any pre-existing {@link TextLinkSpan} attached to the text to avoid
+     * problems if you call it repeatedly on the same text.
+     *
+     * <p><strong>Note:</strong> This method returns immediately but generates the links with
+     * the specified classifier on a background thread. The generated links are applied on the
+     * calling thread.
+     *
+     * <p><strong>Note:</strong> If the text is currently attached to a TextView, this method
+     * should be called on the UI thread.
+     *
+     * @param text Spannable whose text is to be marked-up with links
+     * @param classifier the TextClassifier to use to generate the links
+     * @param params optional parameters to specify how to generate the links
+     * @param executor Executor that runs the background task
+     * @param callback Callback that receives the final status of the background task execution
+     *
+     * @return a future that may be used to interrupt or query the background task
+     * @hide
+     */
+    public static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @Nullable TextLinksParams params,
+            @Nullable Executor executor,
+            @Nullable Consumer<Integer> callback) {
+        return addLinksAsync(text, classifier, params, executor, callback,
+                null /* modifyTextView */);
+    }
+
+    private static Future<Void> addLinksAsync(
+            @NonNull Spannable text,
+            @NonNull TextClassifier classifier,
+            @Nullable TextLinksParams params,
+            @Nullable Executor executor,
+            @Nullable Consumer<Integer> callback,
+            @Nullable Runnable modifyTextView) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(classifier);
+
+        // TODO: This is a bug. We shouldnot call getMaxGenerateLinksTextLength() on the UI thread.
+        // The input text may exceed the maximum length the text classifier can handle. In such
+        // cases, we process the text up to the maximum length.
+        final CharSequence truncatedText = text.subSequence(
+                0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength()));
+
+        final TextClassifier.EntityConfig entityConfig = (params == null)
+                ? null : params.getEntityConfig();
+        final TextLinks.Request request = new TextLinks.Request.Builder(truncatedText)
+                .setLegacyFallback(true)
+                .setEntityConfig(entityConfig)
+                .build();
+        final Supplier<TextLinks> supplier = () -> classifier.generateLinks(request);
+        final Consumer<TextLinks> consumer = links -> {
+            if (links.getLinks().isEmpty()) {
+                if (callback != null) {
+                    callback.accept(TextLinks.STATUS_NO_LINKS_FOUND);
+                }
+                return;
+            }
+
+            // Remove spans only for the part of the text we generated links for.
+            final TextLinkSpan[] old =
+                    text.getSpans(0, truncatedText.length(), TextLinkSpan.class);
+            for (int i = old.length - 1; i >= 0; i--) {
+                text.removeSpan(old[i]);
+            }
+
+            final @TextLinks.Status int result = params.apply(text, links);
+            if (result == TextLinks.STATUS_LINKS_APPLIED) {
+                if (modifyTextView != null) {
+                    modifyTextView.run();
+                }
+            }
+            if (callback != null) {
+                callback.accept(result);
+            }
+        };
+        if (executor == null) {
+            return CompletableFuture.supplyAsync(supplier).thenAccept(consumer);
+        } else {
+            return CompletableFuture.supplyAsync(supplier, executor).thenAccept(consumer);
+        }
+    }
+
     private static final void applyLink(String url, int start, int end, Spannable text) {
         URLSpan span = new URLSpan(url);
 
diff --git a/android/transition/ArcMotion.java b/android/transition/ArcMotion.java
index da14834..172c837 100644
--- a/android/transition/ArcMotion.java
+++ b/android/transition/ArcMotion.java
@@ -216,7 +216,13 @@
 
         boolean isMovingUpwards = startY > endY;
 
-        if ((Math.abs(deltaX) < Math.abs(deltaY))) {
+        if (deltaY == 0) {
+            ex = dx;
+            ey = dy + (Math.abs(deltaX) * 0.5f * mMinimumHorizontalTangent);
+        } else if (deltaX == 0) {
+            ex = dx + (Math.abs(deltaY) * 0.5f * mMinimumVerticalTangent);
+            ey = dy;
+        } else 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
@@ -254,7 +260,7 @@
         float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
 
         float newArcDistance2 = 0;
-        if (arcDist2 < minimumArcDist2) {
+        if (arcDist2 != 0 && arcDist2 < minimumArcDist2) {
             newArcDistance2 = minimumArcDist2;
         } else if (arcDist2 > maximumArcDist2) {
             newArcDistance2 = maximumArcDist2;
diff --git a/android/transition/TransitionUtils.java b/android/transition/TransitionUtils.java
index 084b79d..46c9e0c 100644
--- a/android/transition/TransitionUtils.java
+++ b/android/transition/TransitionUtils.java
@@ -20,14 +20,13 @@
 import android.animation.AnimatorSet;
 import android.animation.TypeEvaluator;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Picture;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
@@ -128,10 +127,8 @@
         }
         int bitmapWidth = (int) (width * scale);
         int bitmapHeight = (int) (height * scale);
-        final RenderNode node = RenderNode.create("TransitionUtils", hostView);
-        node.setLeftTopRightBottom(0, 0, width, height);
-        node.setClipToBounds(false);
-        final DisplayListCanvas canvas = node.start(width, height);
+        final Picture picture = new Picture();
+        final Canvas canvas = picture.beginRecording(width, height);
         // Do stuff with the canvas
         Rect existingBounds = drawable.getBounds();
         int left = existingBounds.left;
@@ -141,8 +138,8 @@
         drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
         drawable.draw(canvas);
         drawable.setBounds(left, top, right, bottom);
-        node.end(canvas);
-        return ThreadedRenderer.createHardwareBitmap(node, width, height);
+        picture.endRecording();
+        return Bitmap.createBitmap(picture);
     }
 
     /**
@@ -163,10 +160,14 @@
     public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds,
             ViewGroup sceneRoot) {
         final boolean addToOverlay = !view.isAttachedToWindow();
+        ViewGroup parent = null;
+        int indexInParent = 0;
         if (addToOverlay) {
             if (sceneRoot == null || !sceneRoot.isAttachedToWindow()) {
                 return null;
             }
+            parent = (ViewGroup) view.getParent();
+            indexInParent = parent.indexOfChild(view);
             sceneRoot.getOverlay().add(view);
         }
         Bitmap bitmap = null;
@@ -179,17 +180,16 @@
             matrix.postTranslate(-bounds.left, -bounds.top);
             matrix.postScale(scale, scale);
 
-            final RenderNode node = RenderNode.create("TransitionUtils", view);
-            node.setLeftTopRightBottom(0, 0, bitmapWidth, bitmapHeight);
-            node.setClipToBounds(false);
-            final DisplayListCanvas canvas = node.start(bitmapWidth, bitmapHeight);
+            final Picture picture = new Picture();
+            final Canvas canvas = picture.beginRecording(bitmapWidth, bitmapHeight);
             canvas.concat(matrix);
             view.draw(canvas);
-            node.end(canvas);
-            bitmap = ThreadedRenderer.createHardwareBitmap(node, bitmapWidth, bitmapHeight);
+            picture.endRecording();
+            bitmap = Bitmap.createBitmap(picture);
         }
         if (addToOverlay) {
             sceneRoot.getOverlay().remove(view);
+            parent.addView(view, indexInParent);
         }
         return bitmap;
     }
diff --git a/android/transition/Visibility.java b/android/transition/Visibility.java
index f0838a1..77c652e 100644
--- a/android/transition/Visibility.java
+++ b/android/transition/Visibility.java
@@ -402,8 +402,11 @@
                 // Becoming GONE
                 if (startView == endView) {
                     viewToKeep = endView;
-                } else {
+                } else if (mCanRemoveViews) {
                     overlayView = startView;
+                } else {
+                    overlayView = TransitionUtils.copyViewImage(sceneRoot, startView,
+                            (View) startView.getParent());
                 }
             }
         }
diff --git a/android/util/AtomicFile.java b/android/util/AtomicFile.java
index 6342c8b..cf7ed9b 100644
--- a/android/util/AtomicFile.java
+++ b/android/util/AtomicFile.java
@@ -17,6 +17,7 @@
 package android.util;
 
 import android.os.FileUtils;
+import android.os.SystemClock;
 
 import libcore.io.IoUtils;
 
@@ -47,14 +48,25 @@
 public class AtomicFile {
     private final File mBaseName;
     private final File mBackupName;
+    private final String mCommitTag;
+    private long mStartTime;
 
     /**
      * Create a new AtomicFile for a file located at the given File path.
      * The secondary backup file will be the same file path with ".bak" appended.
      */
     public AtomicFile(File baseName) {
+        this(baseName, null);
+    }
+
+    /**
+     * @hide Internal constructor that also allows you to have the class
+     * automatically log commit events.
+     */
+    public AtomicFile(File baseName, String commitTag) {
         mBaseName = baseName;
         mBackupName = new File(baseName.getPath() + ".bak");
+        mCommitTag = commitTag;
     }
 
     /**
@@ -88,6 +100,18 @@
      * access to AtomicFile.
      */
     public FileOutputStream startWrite() throws IOException {
+        return startWrite(mCommitTag != null ? SystemClock.uptimeMillis() : 0);
+    }
+
+    /**
+     * @hide Internal version of {@link #startWrite()} that allows you to specify an earlier
+     * start time of the operation to adjust how the commit is logged.
+     * @param startTime The effective start time of the operation, in the time
+     * base of {@link SystemClock#uptimeMillis()}.
+     */
+    public FileOutputStream startWrite(long startTime) throws IOException {
+        mStartTime = startTime;
+
         // Rename the current file so it may be used as a backup during the next read
         if (mBaseName.exists()) {
             if (!mBackupName.exists()) {
@@ -135,6 +159,10 @@
             } catch (IOException e) {
                 Log.w("AtomicFile", "finishWrite: Got exception:", e);
             }
+            if (mCommitTag != null) {
+                com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+                        mCommitTag, SystemClock.uptimeMillis() - mStartTime);
+            }
         }
     }
 
diff --git a/android/util/AttributeSet.java b/android/util/AttributeSet.java
index eb8c168..7f327c7 100644
--- a/android/util/AttributeSet.java
+++ b/android/util/AttributeSet.java
@@ -17,6 +17,8 @@
 package android.util;
 
 
+import org.xmlpull.v1.XmlPullParser;
+
 /**
  * A collection of attributes, as found associated with a tag in an XML
  * document.  Often you will not want to use this interface directly, instead
@@ -54,18 +56,42 @@
  * compiled XML resource that is not available in a normal XML file, such
  * as {@link #getAttributeNameResource(int)} which returns the resource
  * identifier associated with a particular XML attribute name.
+ *
+ * @see XmlPullParser
  */
 public interface AttributeSet {
     /**
      * Returns the number of attributes available in the set.
-     * 
+     *
+     * <p>See also {@link XmlPullParser#getAttributeCount XmlPullParser.getAttributeCount()},
+     * which this method corresponds to when parsing a compiled XML file.</p>
+     *
      * @return A positive integer, or 0 if the set is empty.
      */
     public int getAttributeCount();
 
     /**
+     * Returns the namespace of the specified attribute.
+     *
+     * <p>See also {@link XmlPullParser#getAttributeNamespace XmlPullParser.getAttributeNamespace()},
+     * which this method corresponds to when parsing a compiled XML file.</p>
+     *
+     * @param index Index of the desired attribute, 0...count-1.
+     *
+     * @return A String containing the namespace of the attribute, or null if th
+     *         attribute cannot be found.
+     */
+    default String getAttributeNamespace (int index) {
+        // This is a new method since the first interface definition, so add stub impl.
+        return null;
+    }
+
+    /**
      * Returns the name of the specified attribute.
-     * 
+     *
+     * <p>See also {@link XmlPullParser#getAttributeName XmlPullParser.getAttributeName()},
+     * which this method corresponds to when parsing a compiled XML file.</p>
+     *
      * @param index Index of the desired attribute, 0...count-1.
      * 
      * @return A String containing the name of the attribute, or null if the
diff --git a/android/util/ByteStringUtils.java b/android/util/ByteStringUtils.java
index 333208d..f6460ad 100644
--- a/android/util/ByteStringUtils.java
+++ b/android/util/ByteStringUtils.java
@@ -22,61 +22,63 @@
  * @hide
  */
 public final class ByteStringUtils {
-  private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+    private static final char[] HEX_LOWERCASE_ARRAY = "0123456789abcdef".toCharArray();
+    private static final char[] HEX_UPPERCASE_ARRAY = "0123456789ABCDEF".toCharArray();
 
-  private ByteStringUtils() {
+    private ByteStringUtils() {
     /* hide constructor */
-  }
-
-  /**
-   * Returns the hex encoded string representation of bytes.
-   * @param bytes Byte array to encode.
-   * @return Hex encoded string representation of bytes.
-   */
-  public static String toHexString(byte[] bytes) {
-    if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
-      return null;
     }
 
-    final int byteLength = bytes.length;
-    final int charCount = 2 * byteLength;
-    final char[] chars = new char[charCount];
+    /**
+     * Returns the hex encoded string representation of bytes.
+     * @param bytes Byte array to encode.
+     * @return Hex encoded string representation of bytes.
+     */
+    public static String toHexString(byte[] bytes) {
+        if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
+            return null;
+        }
 
-    for (int i = 0; i < byteLength; i++) {
-      final int byteHex = bytes[i] & 0xFF;
-      chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
-      chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
-    }
-    return new String(chars);
-  }
+        final int byteLength = bytes.length;
+        final int charCount = 2 * byteLength;
+        final char[] chars = new char[charCount];
 
-  /**
-   * Returns the decoded byte array representation of str.
-   * @param str Hex encoded string to decode.
-   * @return Decoded byte array representation of str.
-   */
-  public static byte[] fromHexToByteArray(String str) {
-    if (str == null || str.length() == 0 || str.length() % 2 != 0) {
-      return null;
+        for (int i = 0; i < byteLength; i++) {
+            final int byteHex = bytes[i] & 0xFF;
+            chars[i * 2] = HEX_UPPERCASE_ARRAY[byteHex >>> 4];
+            chars[i * 2 + 1] = HEX_UPPERCASE_ARRAY[byteHex & 0x0F];
+        }
+        return new String(chars);
     }
 
-    final char[] chars = str.toCharArray();
-    final int charLength = chars.length;
-    final byte[] bytes = new byte[charLength / 2];
+    /**
+     * Returns the decoded byte array representation of str.
+     * @param str Hex encoded string to decode.
+     * @return Decoded byte array representation of str.
+     */
+    public static byte[] fromHexToByteArray(String str) {
+        if (str == null || str.length() == 0 || str.length() % 2 != 0) {
+            return null;
+        }
 
-    for (int i = 0; i < bytes.length; i++) {
-      bytes[i] =
-          (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F));
-    }
-    return bytes;
-  }
+        final char[] chars = str.toCharArray();
+        final int charLength = chars.length;
+        final byte[] bytes = new byte[charLength / 2];
 
-  private static int getIndex(char c) {
-    for (int i = 0; i < HEX_ARRAY.length; i++) {
-      if (HEX_ARRAY[i] == c) {
-        return i;
-      }
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] =
+                    (byte) (((getIndex(chars[i * 2]) << 4) & 0xF0)
+                            | (getIndex(chars[i * 2 + 1]) & 0x0F));
+        }
+        return bytes;
     }
-    return -1;
-  }
+
+    private static int getIndex(char c) {
+        for (int i = 0; i < HEX_UPPERCASE_ARRAY.length; i++) {
+            if (HEX_UPPERCASE_ARRAY[i] == c || HEX_LOWERCASE_ARRAY[i] == c) {
+                return i;
+            }
+        }
+        return -1;
+    }
 }
diff --git a/android/util/DataUnit.java b/android/util/DataUnit.java
index ea4266e..cf045b8 100644
--- a/android/util/DataUnit.java
+++ b/android/util/DataUnit.java
@@ -29,6 +29,8 @@
  * "kibibyte" as an IEC unit of 1024 bytes.
  * <p>
  * This design is mirrored after {@link TimeUnit} and {@link ChronoUnit}.
+ *
+ * @hide
  */
 public enum DataUnit {
     KILOBYTES { @Override public long toBytes(long v) { return v * 1_000; } },
diff --git a/android/util/DebugUtils.java b/android/util/DebugUtils.java
index c44f42b..46e3169 100644
--- a/android/util/DebugUtils.java
+++ b/android/util/DebugUtils.java
@@ -219,7 +219,7 @@
                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
                 try {
                     if (value == field.getInt(null)) {
-                        return field.getName().substring(prefix.length());
+                        return constNameWithoutPrefix(prefix, field);
                     }
                 } catch (IllegalAccessException ignored) {
                 }
@@ -236,6 +236,7 @@
      */
     public static String flagsToString(Class<?> clazz, String prefix, int flags) {
         final StringBuilder res = new StringBuilder();
+        boolean flagsWasZero = flags == 0;
 
         for (Field field : clazz.getDeclaredFields()) {
             final int modifiers = field.getModifiers();
@@ -243,9 +244,12 @@
                     && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
                 try {
                     final int value = field.getInt(null);
+                    if (value == 0 && flagsWasZero) {
+                        return constNameWithoutPrefix(prefix, field);
+                    }
                     if ((flags & value) != 0) {
                         flags &= ~value;
-                        res.append(field.getName().substring(prefix.length())).append('|');
+                        res.append(constNameWithoutPrefix(prefix, field)).append('|');
                     }
                 } catch (IllegalAccessException ignored) {
                 }
@@ -258,4 +262,8 @@
         }
         return res.toString();
     }
+
+    private static String constNameWithoutPrefix(String prefix, Field field) {
+        return field.getName().substring(prefix.length());
+    }
 }
diff --git a/android/util/DisplayMetrics.java b/android/util/DisplayMetrics.java
index b7099b6..13de172 100644
--- a/android/util/DisplayMetrics.java
+++ b/android/util/DisplayMetrics.java
@@ -120,6 +120,14 @@
     public static final int DENSITY_420 = 420;
 
     /**
+     * Intermediate density for screens that sit somewhere between
+     * {@link #DENSITY_XHIGH} (320 dpi) and {@link #DENSITY_XXHIGH} (480 dpi).
+     * This is not a density that applications should target, instead relying
+     * on the system to scale their {@link #DENSITY_XXHIGH} assets for them.
+     */
+    public static final int DENSITY_440 = 440;
+
+    /**
      * Standard quantized DPI for extra-extra-high-density screens.
      */
     public static final int DENSITY_XXHIGH = 480;
diff --git a/android/util/ExceptionUtils.java b/android/util/ExceptionUtils.java
index da7387f..1a397b3 100644
--- a/android/util/ExceptionUtils.java
+++ b/android/util/ExceptionUtils.java
@@ -86,4 +86,16 @@
         while (t.getCause() != null) t = t.getCause();
         return t;
     }
-}
+
+    /**
+     * Appends {@code cause} at the end of the causal chain of {@code t}
+     *
+     * @return {@code t} for convenience
+     */
+    public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) {
+        if (cause != null) {
+            getRootCause(t).initCause(cause);
+        }
+        return t;
+    }
+}
\ No newline at end of file
diff --git a/android/util/FeatureFlagUtils.java b/android/util/FeatureFlagUtils.java
index 25a177e..eecdb74 100644
--- a/android/util/FeatureFlagUtils.java
+++ b/android/util/FeatureFlagUtils.java
@@ -37,16 +37,12 @@
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
         DEFAULT_FLAGS = new HashMap<>();
-        DEFAULT_FLAGS.put("device_info_v2", "true");
-        DEFAULT_FLAGS.put("settings_app_info_v2", "true");
-        DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
-        DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
-        DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
-        DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
-        DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
+        DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
         DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
+        DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
+        DEFAULT_FLAGS.put("settings_audio_switcher", "false");
     }
 
     /**
diff --git a/android/util/KeyValueSettingObserver.java b/android/util/KeyValueSettingObserver.java
new file mode 100644
index 0000000..9fca8b2
--- /dev/null
+++ b/android/util/KeyValueSettingObserver.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+
+/**
+ * Abstract class for observing changes to a specified setting stored as a comma-separated key value
+ * list of parameters. Registers and unregisters a {@link ContentObserver} and handles updates when
+ * the setting changes.
+ *
+ * <p>Subclasses should pass in the relevant setting's {@link Uri} in the constructor and implement
+ * {@link #update(KeyValueListParser)} to receive updates when the value changes.
+ * Calls to {@link #update(KeyValueListParser)} only trigger after calling {@link
+ * #start()}.
+ *
+ * <p>To get the most up-to-date parameter values, first call {@link #start()} before accessing the
+ * values to start observing changes, and then call {@link #stop()} once finished.
+ *
+ * @hide
+ */
+public abstract class KeyValueSettingObserver {
+    private static final String TAG = "KeyValueSettingObserver";
+
+    private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+    private final ContentObserver mObserver;
+    private final ContentResolver mResolver;
+    private final Uri mSettingUri;
+
+    public KeyValueSettingObserver(Handler handler, ContentResolver resolver,
+            Uri uri) {
+        mObserver = new SettingObserver(handler);
+        mResolver = resolver;
+        mSettingUri = uri;
+    }
+
+    /** Starts observing changes for the setting. Pair with {@link #stop()}. */
+    public void start() {
+        mResolver.registerContentObserver(mSettingUri, false, mObserver);
+        setParserValue();
+        update(mParser);
+    }
+
+    /** Stops observing changes for the setting. */
+    public void stop() {
+        mResolver.unregisterContentObserver(mObserver);
+    }
+
+    /**
+     * Returns the {@link String} representation of the setting. Subclasses should implement this
+     * for their setting.
+     */
+    public abstract String getSettingValue(ContentResolver resolver);
+
+    /** Updates the parser with the current setting value. */
+    private void setParserValue() {
+        String setting = getSettingValue(mResolver);
+        try {
+            mParser.setString(setting);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Malformed setting: " + setting);
+        }
+    }
+
+    /** Subclasses should implement this to update references to their parameters. */
+    public abstract void update(KeyValueListParser parser);
+
+    private class SettingObserver extends ContentObserver {
+        private SettingObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            setParserValue();
+            update(mParser);
+        }
+    }
+}
diff --git a/android/util/LauncherIcons.java b/android/util/LauncherIcons.java
index 402bef9..cc9991a 100644
--- a/android/util/LauncherIcons.java
+++ b/android/util/LauncherIcons.java
@@ -110,9 +110,9 @@
         Drawable badgeColor = sysRes.getDrawable(
                 com.android.internal.R.drawable.ic_corp_icon_badge_color)
                 .getConstantState().newDrawable().mutate();
-        badgeColor.setTint(backgroundColor);
 
         Drawable badgeForeground = sysRes.getDrawable(foregroundRes);
+        badgeForeground.setTint(backgroundColor);
 
         Drawable[] drawables = base == null
                 ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
diff --git a/android/util/Log.java b/android/util/Log.java
index b94e48b..0299865 100644
--- a/android/util/Log.java
+++ b/android/util/Log.java
@@ -16,12 +16,45 @@
 
 package android.util;
 
+import android.os.DeadSystemException;
+
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.LineBreakBufferedWriter;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.io.Writer;
 import java.net.UnknownHostException;
 
 /**
- * Mock Log implementation for testing on non android host.
+ * API for sending log output.
+ *
+ * <p>Generally, you should use the {@link #v Log.v()}, {@link #d Log.d()},
+ * {@link #i Log.i()}, {@link #w Log.w()}, and {@link #e Log.e()} methods to write logs.
+ * You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
+ *
+ * <p>The order in terms of verbosity, from least to most is
+ * ERROR, WARN, INFO, DEBUG, VERBOSE.  Verbose should never be compiled
+ * into an application except during development.  Debug logs are compiled
+ * in but stripped at runtime.  Error, warning and info logs are always kept.
+ *
+ * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant
+ * in your class:
+ *
+ * <pre>private static final String TAG = "MyActivity";</pre>
+ *
+ * and use that in subsequent calls to the log methods.
+ * </p>
+ *
+ * <p><b>Tip:</b> Don't forget that when you make a call like
+ * <pre>Log.v(TAG, "index=" + i);</pre>
+ * that when you're building the string to pass into Log.d, the compiler uses a
+ * StringBuilder and at least three allocations occur: the StringBuilder
+ * itself, the buffer, and the String object.  Realistically, there is also
+ * another buffer allocation and copy, and even more pressure on the gc.
+ * That means that if your log message is filtered out, you might be doing
+ * significant work and incurring significant overhead.
  */
 public final class Log {
 
@@ -55,6 +88,29 @@
      */
     public static final int ASSERT = 7;
 
+    /**
+     * Exception class used to capture a stack trace in {@link #wtf}.
+     * @hide
+     */
+    public static class TerribleFailure extends Exception {
+        TerribleFailure(String msg, Throwable cause) { super(msg, cause); }
+    }
+
+    /**
+     * Interface to handle terrible failures from {@link #wtf}.
+     *
+     * @hide
+     */
+    public interface TerribleFailureHandler {
+        void onTerribleFailure(String tag, TerribleFailure what, boolean system);
+    }
+
+    private static TerribleFailureHandler sWtfHandler = new TerribleFailureHandler() {
+            public void onTerribleFailure(String tag, TerribleFailure what, boolean system) {
+                RuntimeInit.wtf(tag, what, system);
+            }
+        };
+
     private Log() {
     }
 
@@ -65,7 +121,7 @@
      * @param msg The message you would like logged.
      */
     public static int v(String tag, String msg) {
-        return println(LOG_ID_MAIN, VERBOSE, tag, msg);
+        return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
     }
 
     /**
@@ -76,7 +132,7 @@
      * @param tr An exception to log
      */
     public static int v(String tag, String msg, Throwable tr) {
-        return println(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
+        return printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr);
     }
 
     /**
@@ -86,7 +142,7 @@
      * @param msg The message you would like logged.
      */
     public static int d(String tag, String msg) {
-        return println(LOG_ID_MAIN, DEBUG, tag, msg);
+        return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
     }
 
     /**
@@ -97,7 +153,7 @@
      * @param tr An exception to log
      */
     public static int d(String tag, String msg, Throwable tr) {
-        return println(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
+        return printlns(LOG_ID_MAIN, DEBUG, tag, msg, tr);
     }
 
     /**
@@ -107,7 +163,7 @@
      * @param msg The message you would like logged.
      */
     public static int i(String tag, String msg) {
-        return println(LOG_ID_MAIN, INFO, tag, msg);
+        return println_native(LOG_ID_MAIN, INFO, tag, msg);
     }
 
     /**
@@ -118,7 +174,7 @@
      * @param tr An exception to log
      */
     public static int i(String tag, String msg, Throwable tr) {
-        return println(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));
+        return printlns(LOG_ID_MAIN, INFO, tag, msg, tr);
     }
 
     /**
@@ -128,7 +184,7 @@
      * @param msg The message you would like logged.
      */
     public static int w(String tag, String msg) {
-        return println(LOG_ID_MAIN, WARN, tag, msg);
+        return println_native(LOG_ID_MAIN, WARN, tag, msg);
     }
 
     /**
@@ -139,9 +195,31 @@
      * @param tr An exception to log
      */
     public static int w(String tag, String msg, Throwable tr) {
-        return println(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr));
+        return printlns(LOG_ID_MAIN, WARN, tag, msg, tr);
     }
 
+    /**
+     * Checks to see whether or not a log for the specified tag is loggable at the specified level.
+     *
+     *  The default level of any tag is set to INFO. This means that any level above and including
+     *  INFO will be logged. Before you make any calls to a logging method you should check to see
+     *  if your tag should be logged. You can change the default level by setting a system property:
+     *      'setprop log.tag.&lt;YOUR_LOG_TAG> &lt;LEVEL>'
+     *  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will
+     *  turn off all logging for your tag. You can also create a local.prop file that with the
+     *  following in it:
+     *      'log.tag.&lt;YOUR_LOG_TAG>=&lt;LEVEL>'
+     *  and place that in /data/local.prop.
+     *
+     * @param tag The tag to check.
+     * @param level The level to check.
+     * @return Whether or not that this is allowed to be logged.
+     * @throws IllegalArgumentException is thrown if the tag.length() > 23
+     *         for Nougat (7.0) releases (API <= 23) and prior, there is no
+     *         tag limit of concern after this API level.
+     */
+    public static native boolean isLoggable(String tag, int level);
+
     /*
      * Send a {@link #WARN} log message and log the exception.
      * @param tag Used to identify the source of a log message.  It usually identifies
@@ -149,7 +227,7 @@
      * @param tr An exception to log
      */
     public static int w(String tag, Throwable tr) {
-        return println(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
+        return printlns(LOG_ID_MAIN, WARN, tag, "", tr);
     }
 
     /**
@@ -159,7 +237,7 @@
      * @param msg The message you would like logged.
      */
     public static int e(String tag, String msg) {
-        return println(LOG_ID_MAIN, ERROR, tag, msg);
+        return println_native(LOG_ID_MAIN, ERROR, tag, msg);
     }
 
     /**
@@ -170,7 +248,82 @@
      * @param tr An exception to log
      */
     public static int e(String tag, String msg, Throwable tr) {
-        return println(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
+        return printlns(LOG_ID_MAIN, ERROR, tag, msg, tr);
+    }
+
+    /**
+     * What a Terrible Failure: Report a condition that should never happen.
+     * The error will always be logged at level ASSERT with the call stack.
+     * Depending on system configuration, a report may be added to the
+     * {@link android.os.DropBoxManager} and/or the process may be terminated
+     * immediately with an error dialog.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public static int wtf(String tag, String msg) {
+        return wtf(LOG_ID_MAIN, tag, msg, null, false, false);
+    }
+
+    /**
+     * Like {@link #wtf(String, String)}, but also writes to the log the full
+     * call stack.
+     * @hide
+     */
+    public static int wtfStack(String tag, String msg) {
+        return wtf(LOG_ID_MAIN, tag, msg, null, true, false);
+    }
+
+    /**
+     * What a Terrible Failure: Report an exception that should never happen.
+     * Similar to {@link #wtf(String, String)}, with an exception to log.
+     * @param tag Used to identify the source of a log message.
+     * @param tr An exception to log.
+     */
+    public static int wtf(String tag, Throwable tr) {
+        return wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, false, false);
+    }
+
+    /**
+     * What a Terrible Failure: Report an exception that should never happen.
+     * Similar to {@link #wtf(String, Throwable)}, with a message as well.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param tr An exception to log.  May be null.
+     */
+    public static int wtf(String tag, String msg, Throwable tr) {
+        return wtf(LOG_ID_MAIN, tag, msg, tr, false, false);
+    }
+
+    static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack,
+            boolean system) {
+        TerribleFailure what = new TerribleFailure(msg, tr);
+        // Only mark this as ERROR, do not use ASSERT since that should be
+        // reserved for cases where the system is guaranteed to abort.
+        // The onTerribleFailure call does not always cause a crash.
+        int bytes = printlns(logId, ERROR, tag, msg, localStack ? what : tr);
+        sWtfHandler.onTerribleFailure(tag, what, system);
+        return bytes;
+    }
+
+    static void wtfQuiet(int logId, String tag, String msg, boolean system) {
+        TerribleFailure what = new TerribleFailure(msg, null);
+        sWtfHandler.onTerribleFailure(tag, what, system);
+    }
+
+    /**
+     * Sets the terrible failure handler, for testing.
+     *
+     * @return the old handler
+     *
+     * @hide
+     */
+    public static TerribleFailureHandler setWtfHandler(TerribleFailureHandler handler) {
+        if (handler == null) {
+            throw new NullPointerException("handler == null");
+        }
+        TerribleFailureHandler oldHandler = sWtfHandler;
+        sWtfHandler = handler;
+        return oldHandler;
     }
 
     /**
@@ -193,7 +346,7 @@
         }
 
         StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
+        PrintWriter pw = new FastPrintWriter(sw, false, 256);
         tr.printStackTrace(pw);
         pw.flush();
         return sw.toString();
@@ -208,7 +361,7 @@
      * @return The number of bytes written.
      */
     public static int println(int priority, String tag, String msg) {
-        return println(LOG_ID_MAIN, priority, tag, msg);
+        return println_native(LOG_ID_MAIN, priority, tag, msg);
     }
 
     /** @hide */ public static final int LOG_ID_MAIN = 0;
@@ -217,9 +370,115 @@
     /** @hide */ public static final int LOG_ID_SYSTEM = 3;
     /** @hide */ public static final int LOG_ID_CRASH = 4;
 
-    /** @hide */ @SuppressWarnings("unused")
-    public static int println(int bufID,
-            int priority, String tag, String msg) {
-        return 0;
+    /** @hide */ public static native int println_native(int bufID,
+            int priority, String tag, String msg);
+
+    /**
+     * Return the maximum payload the log daemon accepts without truncation.
+     * @return LOGGER_ENTRY_MAX_PAYLOAD.
+     */
+    private static native int logger_entry_max_payload_native();
+
+    /**
+     * Helper function for long messages. Uses the LineBreakBufferedWriter to break
+     * up long messages and stacktraces along newlines, but tries to write in large
+     * chunks. This is to avoid truncation.
+     * @hide
+     */
+    public static int printlns(int bufID, int priority, String tag, String msg,
+            Throwable tr) {
+        ImmediateLogWriter logWriter = new ImmediateLogWriter(bufID, priority, tag);
+        // Acceptable buffer size. Get the native buffer size, subtract two zero terminators,
+        // and the length of the tag.
+        // Note: we implicitly accept possible truncation for Modified-UTF8 differences. It
+        //       is too expensive to compute that ahead of time.
+        int bufferSize = PreloadHolder.LOGGER_ENTRY_MAX_PAYLOAD    // Base.
+                - 2                                                // Two terminators.
+                - (tag != null ? tag.length() : 0)                 // Tag length.
+                - 32;                                              // Some slack.
+        // At least assume you can print *some* characters (tag is not too large).
+        bufferSize = Math.max(bufferSize, 100);
+
+        LineBreakBufferedWriter lbbw = new LineBreakBufferedWriter(logWriter, bufferSize);
+
+        lbbw.println(msg);
+
+        if (tr != null) {
+            // This is to reduce the amount of log spew that apps do in the non-error
+            // condition of the network being unavailable.
+            Throwable t = tr;
+            while (t != null) {
+                if (t instanceof UnknownHostException) {
+                    break;
+                }
+                if (t instanceof DeadSystemException) {
+                    lbbw.println("DeadSystemException: The system died; "
+                            + "earlier logs will point to the root cause");
+                    break;
+                }
+                t = t.getCause();
+            }
+            if (t == null) {
+                tr.printStackTrace(lbbw);
+            }
+        }
+
+        lbbw.flush();
+
+        return logWriter.getWritten();
+    }
+
+    /**
+     * PreloadHelper class. Caches the LOGGER_ENTRY_MAX_PAYLOAD value to avoid
+     * a JNI call during logging.
+     */
+    static class PreloadHolder {
+        public final static int LOGGER_ENTRY_MAX_PAYLOAD =
+                logger_entry_max_payload_native();
+    }
+
+    /**
+     * Helper class to write to the logcat. Different from LogWriter, this writes
+     * the whole given buffer and does not break along newlines.
+     */
+    private static class ImmediateLogWriter extends Writer {
+
+        private int bufID;
+        private int priority;
+        private String tag;
+
+        private int written = 0;
+
+        /**
+         * Create a writer that immediately writes to the log, using the given
+         * parameters.
+         */
+        public ImmediateLogWriter(int bufID, int priority, String tag) {
+            this.bufID = bufID;
+            this.priority = priority;
+            this.tag = tag;
+        }
+
+        public int getWritten() {
+            return written;
+        }
+
+        @Override
+        public void write(char[] cbuf, int off, int len) {
+            // Note: using String here has a bit of overhead as a Java object is created,
+            //       but using the char[] directly is not easier, as it needs to be translated
+            //       to a C char[] for logging.
+            written += println_native(bufID, priority, tag, new String(cbuf, off, len));
+        }
+
+        @Override
+        public void flush() {
+            // Ignored.
+        }
+
+        @Override
+        public void close() {
+            // Ignored.
+        }
     }
 }
diff --git a/android/util/LongArray.java b/android/util/LongArray.java
index 9b0489c..fa98096 100644
--- a/android/util/LongArray.java
+++ b/android/util/LongArray.java
@@ -16,11 +16,15 @@
 
 package android.util;
 
+import android.annotation.Nullable;
+
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
-import java.util.Arrays;
+
 import libcore.util.EmptyArray;
 
+import java.util.Arrays;
+
 /**
  * Implements a growing array of long primitives.
  *
@@ -216,4 +220,18 @@
             throw new ArrayIndexOutOfBoundsException(mSize, index);
         }
     }
+
+    /**
+     * Test if each element of {@code a} equals corresponding element from {@code b}
+     */
+    public static boolean elementsEqual(@Nullable LongArray a, @Nullable LongArray b) {
+        if (a == null || b == null) return a == b;
+        if (a.mSize != b.mSize) return false;
+        for (int i = 0; i < a.mSize; i++) {
+            if (a.get(i) != b.get(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/android/util/MapCollections.java b/android/util/MapCollections.java
index 80ab23c..a521268 100644
--- a/android/util/MapCollections.java
+++ b/android/util/MapCollections.java
@@ -16,13 +16,12 @@
 
 package android.util;
 
-import libcore.util.Objects;
-
 import java.lang.reflect.Array;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -143,8 +142,8 @@
                 return false;
             }
             Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
-            return Objects.equal(e.getKey(), colGetEntry(mIndex, 0))
-                    && Objects.equal(e.getValue(), colGetEntry(mIndex, 1));
+            return Objects.equals(e.getKey(), colGetEntry(mIndex, 0))
+                    && Objects.equals(e.getValue(), colGetEntry(mIndex, 1));
         }
 
         @Override
@@ -195,7 +194,7 @@
                 return false;
             }
             Object foundVal = colGetEntry(index, 1);
-            return Objects.equal(foundVal, e.getValue());
+            return Objects.equals(foundVal, e.getValue());
         }
 
         @Override
diff --git a/android/util/NtpTrustedTime.java b/android/util/NtpTrustedTime.java
index ed2d3c6..30d7b6c 100644
--- a/android/util/NtpTrustedTime.java
+++ b/android/util/NtpTrustedTime.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.Network;
 import android.net.NetworkInfo;
 import android.net.SntpClient;
 import android.os.SystemClock;
@@ -80,6 +81,18 @@
 
     @Override
     public boolean forceRefresh() {
+        // We can't do this at initialization time: ConnectivityService might not be running yet.
+        synchronized (this) {
+            if (mCM == null) {
+                mCM = sContext.getSystemService(ConnectivityManager.class);
+            }
+        }
+
+        final Network network = mCM == null ? null : mCM.getActiveNetwork();
+        return forceRefresh(network);
+    }
+
+    public boolean forceRefresh(Network network) {
         if (TextUtils.isEmpty(mServer)) {
             // missing server, so no trusted time available
             return false;
@@ -88,11 +101,11 @@
         // We can't do this at initialization time: ConnectivityService might not be running yet.
         synchronized (this) {
             if (mCM == null) {
-                mCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+                mCM = sContext.getSystemService(ConnectivityManager.class);
             }
         }
 
-        final NetworkInfo ni = mCM == null ? null : mCM.getActiveNetworkInfo();
+        final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network);
         if (ni == null || !ni.isConnected()) {
             if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
             return false;
@@ -101,7 +114,7 @@
 
         if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
         final SntpClient client = new SntpClient();
-        if (client.requestTime(mServer, (int) mTimeout)) {
+        if (client.requestTime(mServer, (int) mTimeout, network)) {
             mHasCache = true;
             mCachedNtpTime = client.getNtpTime();
             mCachedNtpElapsedRealtime = client.getNtpTimeReference();
diff --git a/android/util/RecurrenceRule.java b/android/util/RecurrenceRule.java
index 1fe638d..9f115eb 100644
--- a/android/util/RecurrenceRule.java
+++ b/android/util/RecurrenceRule.java
@@ -41,7 +41,7 @@
  */
 public class RecurrenceRule implements Parcelable {
     private static final String TAG = "RecurrenceRule";
-    private static final boolean DEBUG = true;
+    private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int VERSION_INIT = 0;
 
@@ -99,6 +99,7 @@
                 start = convertZonedDateTime(BackupUtils.readString(in));
                 end = convertZonedDateTime(BackupUtils.readString(in));
                 period = convertPeriod(BackupUtils.readString(in));
+                break;
             default:
                 throw new ProtocolException("Unknown version " + version);
         }
@@ -192,7 +193,7 @@
         public RecurringIterator() {
             final ZonedDateTime anchor = (end != null) ? end
                     : ZonedDateTime.now(sClock).withZoneSameInstant(start.getZone());
-            if (DEBUG) Log.d(TAG, "Resolving using anchor " + anchor);
+            if (LOGD) Log.d(TAG, "Resolving using anchor " + anchor);
 
             updateCycle();
 
@@ -231,7 +232,7 @@
 
         @Override
         public Pair<ZonedDateTime, ZonedDateTime> next() {
-            if (DEBUG) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
+            if (LOGD) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
             Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
             i--;
             updateCycle();
diff --git a/android/util/SparseSetArray.java b/android/util/SparseSetArray.java
new file mode 100644
index 0000000..d100f12
--- /dev/null
+++ b/android/util/SparseSetArray.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+/**
+ * A sparse array of ArraySets, which is suitable to hold userid->packages association.
+ *
+ * @hide
+ */
+public class SparseSetArray<T> {
+    private final SparseArray<ArraySet<T>> mData = new SparseArray<>();
+
+    public SparseSetArray() {
+    }
+
+    /**
+     * Add a value at index n.
+     * @return FALSE when the value already existed at the given index, TRUE otherwise.
+     */
+    public boolean add(int n, T value) {
+        ArraySet<T> set = mData.get(n);
+        if (set == null) {
+            set = new ArraySet<>();
+            mData.put(n, set);
+        }
+        if (set.contains(value)) {
+            return true;
+        }
+        set.add(value);
+        return false;
+    }
+
+    /**
+     * @return whether a value exists at index n.
+     */
+    public boolean contains(int n, T value) {
+        final ArraySet<T> set = mData.get(n);
+        if (set == null) {
+            return false;
+        }
+        return set.contains(value);
+    }
+
+    /**
+     * Remove a value from index n.
+     * @return TRUE when the value existed at the given index and removed, FALSE otherwise.
+     */
+    public boolean remove(int n, T value) {
+        final ArraySet<T> set = mData.get(n);
+        if (set == null) {
+            return false;
+        }
+        final boolean ret = set.remove(value);
+        if (set.size() == 0) {
+            mData.remove(n);
+        }
+        return ret;
+    }
+
+    /**
+     * Remove all values from index n.
+     */
+    public void remove(int n) {
+        mData.remove(n);
+    }
+    public int size() {
+        return mData.size();
+    }
+
+    public int keyAt(int index) {
+        return mData.keyAt(index);
+    }
+
+    public int sizeAt(int index) {
+        final ArraySet<T> set = mData.valueAt(index);
+        if (set == null) {
+            return 0;
+        }
+        return set.size();
+    }
+
+    public T valueAt(int intIndex, int valueIndex) {
+        return mData.valueAt(intIndex).valueAt(valueIndex);
+    }
+}
diff --git a/android/util/StatsLog.java b/android/util/StatsLog.java
index 4805318..e8b4197 100644
--- a/android/util/StatsLog.java
+++ b/android/util/StatsLog.java
@@ -16,10 +16,11 @@
 
 package android.util;
 
+import android.os.Process;
+
 /**
  * StatsLog provides an API for developers to send events to statsd. The events can be used to
- * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
- * statsd.
+ * define custom metrics inside statsd.
  */
 public final class StatsLog extends StatsLogInternal {
     private static final String TAG = "StatsManager";
@@ -34,7 +35,8 @@
      */
     public static boolean logStart(int label) {
         if (label >= 0 && label < 16) {
-            StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START);
+            StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(),
+                    label, APP_BREADCRUMB_REPORTED__STATE__START);
             return true;
         }
         return false;
@@ -48,7 +50,8 @@
      */
     public static boolean logStop(int label) {
         if (label >= 0 && label < 16) {
-            StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP);
+            StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(),
+                    label, APP_BREADCRUMB_REPORTED__STATE__STOP);
             return true;
         }
         return false;
@@ -62,7 +65,8 @@
      */
     public static boolean logEvent(int label) {
         if (label >= 0 && label < 16) {
-            StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED);
+            StatsLog.write(APP_BREADCRUMB_REPORTED, Process.myUid(), label,
+                    APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
             return true;
         }
         return false;
diff --git a/android/util/StatsManager.java b/android/util/StatsManager.java
deleted file mode 100644
index 687aa83..0000000
--- a/android/util/StatsManager.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.util;
-
-import android.Manifest;
-import android.annotation.RequiresPermission;
-import android.os.IBinder;
-import android.os.IStatsManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-
-/*
- *
- *
- *
- *
- * THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS.
- * The new StatsManager is to be found in android.app.StatsManager.
- * TODO: Delete this file!
- *
- *
- *
- *
- */
-
-
-/**
- * API for StatsD clients to send configurations and retrieve data.
- *
- * @hide
- */
-public class StatsManager {
-    IStatsManager mService;
-    private static final String TAG = "StatsManager";
-
-    /**
-     * Constructor for StatsManagerClient.
-     *
-     * @hide
-     */
-    public StatsManager() {
-    }
-
-    /**
-     * Temporary to prevent build failures. Will be deleted.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
-        // To prevent breakages of dependencies on old API.
-
-        return false;
-    }
-
-    /**
-     * Clients can send a configuration and simultaneously registers the name of a broadcast
-     * receiver that listens for when it should request data.
-     *
-     * @param configKey An arbitrary integer that allows clients to track the configuration.
-     * @param config    Wire-encoded StatsDConfig proto that specifies metrics (and all
-     *                  dependencies eg, conditions and matchers).
-     * @param pkg       The package name to receive the broadcast.
-     * @param cls       The name of the class that receives the broadcast.
-     * @return true if successful
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when adding configuration");
-                    return false;
-                }
-                return service.addConfiguration(configKey, config, pkg, cls);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when adding configuration");
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Temporary to prevent build failures. Will be deleted.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public boolean removeConfiguration(String configKey) {
-        // To prevent breakages of old dependencies.
-        return false;
-    }
-
-    /**
-     * Remove a configuration from logging.
-     *
-     * @param configKey Configuration key to remove.
-     * @return true if successful
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public boolean removeConfiguration(long configKey) {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when removing configuration");
-                    return false;
-                }
-                return service.removeConfiguration(configKey);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connect to statsd when removing configuration");
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Temporary to prevent build failures. Will be deleted.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getData(String configKey) {
-        // TODO: remove this and all other methods with String-based config keys.
-        // To prevent build breakages of dependencies.
-        return null;
-    }
-
-    /**
-     * Clients can request data with a binder call. This getter is destructive and also clears
-     * the retrieved metrics from statsd memory.
-     *
-     * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getData(long configKey) {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when getting data");
-                    return null;
-                }
-                return service.getData(configKey);
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connecto statsd when getting data");
-                return null;
-            }
-        }
-    }
-
-    /**
-     * Clients can request metadata for statsd. Will contain stats across all configurations but not
-     * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
-     * This getter is not destructive and will not reset any metrics/counters.
-     *
-     * @return Serialized StatsdStatsReport proto. Returns null on failure.
-     */
-    @RequiresPermission(Manifest.permission.DUMP)
-    public byte[] getMetadata() {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.d(TAG, "Failed to find statsd when getting metadata");
-                    return null;
-                }
-                return service.getMetadata();
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Failed to connecto statsd when getting metadata");
-                return null;
-            }
-        }
-    }
-
-    private class StatsdDeathRecipient implements IBinder.DeathRecipient {
-        @Override
-        public void binderDied() {
-            synchronized (this) {
-                mService = null;
-            }
-        }
-    }
-
-    private IStatsManager getIStatsManagerLocked() throws RemoteException {
-        if (mService != null) {
-            return mService;
-        }
-        mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
-        if (mService != null) {
-            mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
-        }
-        return mService;
-    }
-}
diff --git a/android/util/XmlPullAttributes.java b/android/util/XmlPullAttributes.java
index 6c8bb39..cb35eb5 100644
--- a/android/util/XmlPullAttributes.java
+++ b/android/util/XmlPullAttributes.java
@@ -34,6 +34,10 @@
         return mParser.getAttributeCount();
     }
 
+    public String getAttributeNamespace (int index) {
+        return mParser.getAttributeNamespace(index);
+    }
+
     public String getAttributeName(int index) {
         return mParser.getAttributeName(index);
     }
diff --git a/android/util/apk/ApkSignatureSchemeV2Verifier.java b/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 5a09dab..533d725 100644
--- a/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -213,7 +213,9 @@
 
         byte[] verityRootHash = null;
         if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
-            verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+            byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+            verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+                    verityDigest, apk.length(), signatureInfo);
         }
 
         return new VerifiedSigner(
@@ -412,6 +414,20 @@
         }
     }
 
+    static byte[] generateFsverityRootHash(String apkPath)
+            throws IOException, SignatureNotFoundException, DigestException,
+                   NoSuchAlgorithmException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            SignatureInfo signatureInfo = findSignature(apk);
+            VerifiedSigner vSigner = verify(apk, false);
+            if (vSigner.verityRootHash == null) {
+                return null;
+            }
+            return ApkVerityBuilder.generateFsverityRootHash(
+                    apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
+        }
+    }
+
     private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
         switch (sigAlgorithm) {
             case SIGNATURE_RSA_PSS_WITH_SHA256:
diff --git a/android/util/apk/ApkSignatureSchemeV3Verifier.java b/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 1b04eb2..758cd2b 100644
--- a/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -62,6 +62,7 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -165,7 +166,7 @@
     private static VerifiedSigner verify(
             RandomAccessFile apk,
             SignatureInfo signatureInfo,
-            boolean doVerifyIntegrity) throws SecurityException {
+            boolean doVerifyIntegrity) throws SecurityException, IOException {
         int signerCount = 0;
         Map<Integer, byte[]> contentDigests = new ArrayMap<>();
         VerifiedSigner result = null;
@@ -214,7 +215,9 @@
         }
 
         if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
-            result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+            byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+            result.verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
+                    verityDigest, apk.length(), signatureInfo);
         }
 
         return result;
@@ -438,8 +441,8 @@
         List<Integer> flagsList = new ArrayList<>();
 
         // Proof-of-rotation struct:
-        // is basically a singly linked list of nodes, called levels here, each of which have the
-        // following structure:
+        // A uint32 version code followed by basically a singly linked list of nodes, called levels
+        // here, each of which have the following structure:
         // * length-prefix for the entire level
         //     - length-prefixed signed data (if previous level exists)
         //         * length-prefixed X509 Certificate
@@ -450,9 +453,14 @@
         //     - length-prefixed signature over the signed data in this level.  The signature here
         //         is verified using the certificate from the previous level.
         // The linking is provided by the certificate of each level signing the one of the next.
-        while (porBuf.hasRemaining()) {
-            levelCount++;
-            try {
+
+        try {
+
+            // get the version code, but don't do anything with it: creator knew about all our flags
+            porBuf.getInt();
+            HashSet<X509Certificate> certHistorySet = new HashSet<>();
+            while (porBuf.hasRemaining()) {
+                levelCount++;
                 ByteBuffer level = getLengthPrefixedSlice(porBuf);
                 ByteBuffer signedData = getLengthPrefixedSlice(level);
                 int flags = level.getInt();
@@ -477,6 +485,7 @@
                     }
                 }
 
+                signedData.rewind();
                 byte[] encodedCert = readLengthPrefixedByteArray(signedData);
                 int signedSigAlgorithm = signedData.getInt();
                 if (lastCert != null && lastSigAlgorithm != signedSigAlgorithm) {
@@ -488,19 +497,25 @@
                 lastCert = new VerbatimX509Certificate(lastCert, encodedCert);
 
                 lastSigAlgorithm = sigAlgorithm;
+                if (certHistorySet.contains(lastCert)) {
+                    throw new SecurityException("Encountered duplicate entries in "
+                            + "Proof-of-rotation record at certificate #" + levelCount + ".  All "
+                            + "signing certificates should be unique");
+                }
+                certHistorySet.add(lastCert);
                 certs.add(lastCert);
                 flagsList.add(flags);
-            } catch (IOException | BufferUnderflowException e) {
-                throw new IOException("Failed to parse Proof-of-rotation record", e);
-            } catch (NoSuchAlgorithmException | InvalidKeyException
-                    | InvalidAlgorithmParameterException | SignatureException e) {
-                throw new SecurityException(
-                        "Failed to verify signature over signed data for certificate #"
-                                + levelCount + " when verifying Proof-of-rotation record", e);
-            } catch (CertificateException e) {
-                throw new SecurityException("Failed to decode certificate #" + levelCount
-                        + " when verifying Proof-of-rotation record", e);
             }
+        } catch (IOException | BufferUnderflowException e) {
+            throw new IOException("Failed to parse Proof-of-rotation record", e);
+        } catch (NoSuchAlgorithmException | InvalidKeyException
+                | InvalidAlgorithmParameterException | SignatureException e) {
+            throw new SecurityException(
+                    "Failed to verify signature over signed data for certificate #"
+                            + levelCount + " when verifying Proof-of-rotation record", e);
+        } catch (CertificateException e) {
+            throw new SecurityException("Failed to decode certificate #" + levelCount
+                    + " when verifying Proof-of-rotation record", e);
         }
         return new VerifiedProofOfRotation(certs, flagsList);
     }
@@ -523,6 +538,20 @@
         }
     }
 
+    static byte[] generateFsverityRootHash(String apkPath)
+            throws NoSuchAlgorithmException, DigestException, IOException,
+                   SignatureNotFoundException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            SignatureInfo signatureInfo = findSignature(apk);
+            VerifiedSigner vSigner = verify(apk, false);
+            if (vSigner.verityRootHash == null) {
+                return null;
+            }
+            return ApkVerityBuilder.generateFsverityRootHash(
+                    apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
+        }
+    }
+
     private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
         switch (sigAlgorithm) {
             case SIGNATURE_RSA_PSS_WITH_SHA256:
diff --git a/android/util/apk/ApkSignatureVerifier.java b/android/util/apk/ApkSignatureVerifier.java
index 8794372..de9f55b 100644
--- a/android/util/apk/ApkSignatureVerifier.java
+++ b/android/util/apk/ApkSignatureVerifier.java
@@ -427,6 +427,27 @@
     }
 
     /**
+     * Generates the FSVerity root hash from FSVerity header, extensions and Merkle tree root hash
+     * in Signing Block.
+     *
+     * @return FSverity root hash
+     */
+    public static byte[] generateFsverityRootHash(String apkPath)
+            throws NoSuchAlgorithmException, DigestException, IOException {
+        // first try v3
+        try {
+            return ApkSignatureSchemeV3Verifier.generateFsverityRootHash(apkPath);
+        } catch (SignatureNotFoundException e) {
+            // try older version
+        }
+        try {
+            return ApkSignatureSchemeV2Verifier.generateFsverityRootHash(apkPath);
+        } catch (SignatureNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
      * Result of a successful APK verification operation.
      */
     public static class Result {
diff --git a/android/util/apk/ApkSigningBlockUtils.java b/android/util/apk/ApkSigningBlockUtils.java
index 4146f6f..1c67434 100644
--- a/android/util/apk/ApkSigningBlockUtils.java
+++ b/android/util/apk/ApkSigningBlockUtils.java
@@ -285,11 +285,46 @@
         return result;
     }
 
+    /**
+     * Return the verity digest only if the length of digest content looks correct.
+     * When verity digest is generated, the last incomplete 4k chunk is padded with 0s before
+     * hashing. This means two almost identical APKs with different number of 0 at the end will have
+     * the same verity digest. To avoid this problem, the length of the source content (excluding
+     * Signing Block) is appended to the verity digest, and the digest is returned only if the
+     * length is consistent to the current APK.
+     */
+    static byte[] parseVerityDigestAndVerifySourceLength(
+            byte[] data, long fileSize, SignatureInfo signatureInfo) throws SecurityException {
+        // FORMAT:
+        // OFFSET       DATA TYPE  DESCRIPTION
+        // * @+0  bytes uint8[32]  Merkle tree root hash of SHA-256
+        // * @+32 bytes int64      Length of source data
+        int kRootHashSize = 32;
+        int kSourceLengthSize = 8;
+
+        if (data.length != kRootHashSize + kSourceLengthSize) {
+            throw new SecurityException("Verity digest size is wrong: " + data.length);
+        }
+        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+        buffer.position(kRootHashSize);
+        long expectedSourceLength = buffer.getLong();
+
+        long signingBlockSize = signatureInfo.centralDirOffset
+                - signatureInfo.apkSigningBlockOffset;
+        if (expectedSourceLength != fileSize - signingBlockSize) {
+            throw new SecurityException("APK content size did not verify");
+        }
+
+        return Arrays.copyOfRange(data, 0, kRootHashSize);
+    }
+
     private static void verifyIntegrityForVerityBasedAlgorithm(
-            byte[] expectedRootHash,
+            byte[] expectedDigest,
             RandomAccessFile apk,
             SignatureInfo signatureInfo) throws SecurityException {
         try {
+            byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
+                    apk.length(), signatureInfo);
             ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerity(apk,
                     signatureInfo, new ByteBufferFactory() {
                         @Override
@@ -373,9 +408,9 @@
     static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
     static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
     static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
-    static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0401;
-    static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0403;
-    static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0405;
+    static final int SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0421;
+    static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423;
+    static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425;
 
     static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
     static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
@@ -754,9 +789,6 @@
                 md.update(buffer);
             }
         }
-
-        @Override
-        public void finish() {}
     }
 
 }
diff --git a/android/util/apk/ApkVerityBuilder.java b/android/util/apk/ApkVerityBuilder.java
index ba21ccb..f15e1a1 100644
--- a/android/util/apk/ApkVerityBuilder.java
+++ b/android/util/apk/ApkVerityBuilder.java
@@ -72,22 +72,31 @@
                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
         long dataSize = apk.length() - signingBlockSize;
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
+        int merkleTreeSize = levelOffset[levelOffset.length - 1];
 
         ByteBuffer output = bufferFactory.create(
-                CHUNK_SIZE_BYTES +  // fsverity header + extensions + padding
-                levelOffset[levelOffset.length - 1]);  // Merkle tree size
+                merkleTreeSize
+                + CHUNK_SIZE_BYTES);  // maximum size of fsverity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
 
-        ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES);
-        ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit());
+        ByteBuffer tree = slice(output, 0, merkleTreeSize);
+        ByteBuffer header = slice(output, merkleTreeSize,
+                merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
+        ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
+                merkleTreeSize + CHUNK_SIZE_BYTES);
         byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
         ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
         apkDigest.order(ByteOrder.LITTLE_ENDIAN);
 
+        // NB: Buffer limit is set inside once finished.
         calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
 
-        output.rewind();
+        // Put the reverse offset to fs-verity header at the end.
+        output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
+        output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
+                + 4);  // size of this integer right before EOF
+        output.flip();
+
         return new ApkVerityResult(output, apkDigestBytes);
     }
 
@@ -101,23 +110,28 @@
         ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
                 .order(ByteOrder.LITTLE_ENDIAN);
         ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES);
+        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
+                CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
 
         calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
 
         MessageDigest md = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
-        md.update(DEFAULT_SALT);
-        md.update(verityBlock);
+        md.update(header);
+        md.update(extensions);
         md.update(apkDigest);
         return md.digest();
     }
 
+    /**
+     * Internal method to generate various parts of FSVerity constructs, including the header,
+     * extensions, Merkle tree, and the tree's root hash.  The output buffer is flipped to the
+     * generated data size and is readey for consuming.
+     */
     private static void calculateFsveritySignatureInternal(
             RandomAccessFile apk, SignatureInfo signatureInfo, ByteBuffer treeOutput,
             ByteBuffer rootHashOutput, ByteBuffer headerOutput, ByteBuffer extensionsOutput)
             throws IOException, NoSuchAlgorithmException, DigestException {
         assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-
         long signingBlockSize =
                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
         long dataSize = apk.length() - signingBlockSize - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
@@ -128,6 +142,7 @@
                     levelOffset, treeOutput);
             if (rootHashOutput != null) {
                 rootHashOutput.put(apkRootHash);
+                rootHashOutput.flip();
             }
         }
 
@@ -202,14 +217,10 @@
             }
         }
 
-        /** Finish the current digestion if any. */
-        @Override
-        public void finish() throws DigestException {
-            if (mBytesDigestedSinceReset == 0) {
-                return;
+        public void assertEmptyBuffer() throws DigestException {
+            if (mBytesDigestedSinceReset != 0) {
+                throw new IllegalStateException("Buffer is not empty: " + mBytesDigestedSinceReset);
             }
-            mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
-            mOutput.put(mDigestBuffer);
         }
 
         private void fillUpLastOutputChunk() {
@@ -274,9 +285,15 @@
                 new MemoryMappedFileDataSource(apk.getFD(), offsetAfterEocdCdOffsetField,
                     apk.length() - offsetAfterEocdCdOffsetField),
                 MMAP_REGION_SIZE_BYTES);
-        digester.finish();
 
-        // 5. Fill up the rest of buffer with 0s.
+        // 5. Pad 0s up to the nearest 4096-byte block before hashing.
+        int lastIncompleteChunkSize = (int) (apk.length() % CHUNK_SIZE_BYTES);
+        if (lastIncompleteChunkSize != 0) {
+            digester.consume(ByteBuffer.allocate(CHUNK_SIZE_BYTES - lastIncompleteChunkSize));
+        }
+        digester.assertEmptyBuffer();
+
+        // 6. Fill up the rest of buffer with 0s.
         digester.fillUpLastOutputChunk();
     }
 
@@ -295,8 +312,7 @@
             DataSource source = new ByteBufferDataSource(inputBuffer);
             BufferedDigester digester = new BufferedDigester(salt, outputBuffer);
             consumeByChunk(digester, source, CHUNK_SIZE_BYTES);
-            digester.finish();
-
+            digester.assertEmptyBuffer();
             digester.fillUpLastOutputChunk();
         }
 
@@ -304,18 +320,10 @@
         byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
         BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
         digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
-        digester.finish();
+        digester.assertEmptyBuffer();
         return rootHash;
     }
 
-    private static void bufferPut(ByteBuffer buffer, byte value) {
-        // FIXME(b/72459251): buffer.put(value) does NOT work surprisingly. The position() after put
-        // does NOT even change. This hack workaround the problem, but the root cause remains
-        // unkonwn yet.  This seems only happen when it goes through the apk install flow on my
-        // setup.
-        buffer.put(new byte[] { value });
-    }
-
     private static ByteBuffer generateFsverityHeader(ByteBuffer buffer, long fileSize, int depth,
             byte[] salt) {
         if (salt.length != 8) {
@@ -325,25 +333,25 @@
         // TODO(b/30972906): update the reference when there is a better one in public.
         buffer.put("TrueBrew".getBytes());  // magic
 
-        bufferPut(buffer, (byte) 1);        // major version
-        bufferPut(buffer, (byte) 0);        // minor version
-        bufferPut(buffer, (byte) 12);       // log2(block-size): log2(4096)
-        bufferPut(buffer, (byte) 7);        // log2(leaves-per-node): log2(4096 / 32)
+        buffer.put((byte) 1);               // major version
+        buffer.put((byte) 0);               // minor version
+        buffer.put((byte) 12);              // log2(block-size): log2(4096)
+        buffer.put((byte) 7);               // log2(leaves-per-node): log2(4096 / 32)
 
-        buffer.putShort((short) 1);         // meta algorithm, SHA256_MODE == 1
-        buffer.putShort((short) 1);         // data algorithm, SHA256_MODE == 1
+        buffer.putShort((short) 1);         // meta algorithm, SHA256 == 1
+        buffer.putShort((short) 1);         // data algorithm, SHA256 == 1
 
-        buffer.putInt(0x1);                 // flags, 0x1: has extension
+        buffer.putInt(0);                   // flags
         buffer.putInt(0);                   // reserved
 
         buffer.putLong(fileSize);           // original file size
 
-        bufferPut(buffer, (byte) 0);        // auth block offset, disabled here
-        bufferPut(buffer, (byte) 2);        // extension count
+        buffer.put((byte) 0);               // auth block offset, disabled here
+        buffer.put((byte) 2);               // extension count
         buffer.put(salt);                   // salt (8 bytes)
-        // skip(buffer, 22);                // reserved
+        skip(buffer, 22);                   // reserved
 
-        buffer.rewind();
+        buffer.flip();
         return buffer;
     }
 
@@ -364,12 +372,11 @@
         //
         // struct fsverity_extension_patch {
         //   __le64 offset;
-        //   u8 length;
-        //   u8 reserved[7];
         //   u8 databytes[];
         // };
 
         final int kSizeOfFsverityExtensionHeader = 8;
+        final int kExtensionSizeAlignment = 8;
 
         {
             // struct fsverity_extension #1
@@ -387,29 +394,28 @@
 
         {
             // struct fsverity_extension #2
-            final int kSizeOfFsverityPatchExtension =
-                    8 +  // offset size
-                    1 +  // size of length from offset (up to 255)
-                    7 +  // reserved
-                    ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
-            final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8);
+            final int kTotalSize = kSizeOfFsverityExtensionHeader
+                    + 8 // offset size
+                    + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
 
-            buffer.putShort((short)  // total size of extension, padded to 64-bit alignment
-                    (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding));
+            buffer.putShort((short) kTotalSize);
             buffer.put((byte) 1);    // ID of patch extension
             skip(buffer, 5);         // reserved
 
             // struct fsverity_extension_patch
-            buffer.putLong(eocdOffset);                                 // offset
-            buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE);  // length
-            skip(buffer, 7);                                            // reserved
-            buffer.putInt(Math.toIntExact(signingBlockOffset));         // databytes
+            buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);  // offset
+            buffer.putInt(Math.toIntExact(signingBlockOffset));  // databytes
 
-            // There are extra kPadding bytes of 0s here, included in the total size field of the
-            // extension header. The output ByteBuffer is assumed to be initialized to 0.
+            // The extension needs to be 0-padded at the end, since the length may not be multiple
+            // of 8.
+            int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment;
+            if (kPadding == kExtensionSizeAlignment) {
+                kPadding = 0;
+            }
+            skip(buffer, kPadding);                              // padding
         }
 
-        buffer.rewind();
+        buffer.flip();
         return buffer;
     }
 
diff --git a/android/util/apk/DataDigester.java b/android/util/apk/DataDigester.java
index 278be80..18d1dff 100644
--- a/android/util/apk/DataDigester.java
+++ b/android/util/apk/DataDigester.java
@@ -22,7 +22,4 @@
 interface DataDigester {
     /** Consumes the {@link ByteBuffer}. */
     void consume(ByteBuffer buffer) throws DigestException;
-
-    /** Finishes the digestion. Must be called after the last {@link #consume(ByteBuffer)}. */
-    void finish() throws DigestException;
 }
diff --git a/android/util/apk/VerbatimX509Certificate.java b/android/util/apk/VerbatimX509Certificate.java
index 9984c6d..391c5fc 100644
--- a/android/util/apk/VerbatimX509Certificate.java
+++ b/android/util/apk/VerbatimX509Certificate.java
@@ -18,6 +18,7 @@
 
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
 
 /**
  * For legacy reasons we need to return exactly the original encoded certificate bytes, instead
@@ -25,6 +26,7 @@
  */
 class VerbatimX509Certificate extends WrappedX509Certificate {
     private final byte[] mEncodedVerbatim;
+    private int mHash = -1;
 
     VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
         super(wrapped);
@@ -35,4 +37,30 @@
     public byte[] getEncoded() throws CertificateEncodingException {
         return mEncodedVerbatim;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof VerbatimX509Certificate)) return false;
+
+        try {
+            byte[] a = this.getEncoded();
+            byte[] b = ((VerbatimX509Certificate) o).getEncoded();
+            return Arrays.equals(a, b);
+        } catch (CertificateEncodingException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHash == -1) {
+            try {
+                mHash = Arrays.hashCode(this.getEncoded());
+            } catch (CertificateEncodingException e) {
+                mHash = 0;
+            }
+        }
+        return mHash;
+    }
 }
diff --git a/android/view/AttachInfo_Accessor.java b/android/view/AttachInfo_Accessor.java
index 4445a22..60c13c0 100644
--- a/android/view/AttachInfo_Accessor.java
+++ b/android/view/AttachInfo_Accessor.java
@@ -16,13 +16,12 @@
 
 package android.view;
 
-import com.android.layoutlib.bridge.android.BridgeWindow;
-import com.android.layoutlib.bridge.android.BridgeWindowSession;
-
 import android.content.Context;
 import android.os.Handler;
 import android.view.View.AttachInfo;
 
+import com.android.layoutlib.bridge.util.ReflectionUtils;
+
 /**
  * Class allowing access to package-protected methods/fields.
  */
@@ -33,8 +32,9 @@
         WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
         Display display = wm.getDefaultDisplay();
         ViewRootImpl root = new ViewRootImpl(context, display);
-        AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(),
-                display, root, new Handler(), null, context);
+        AttachInfo info = new AttachInfo(ReflectionUtils.createProxy(IWindowSession.class),
+                ReflectionUtils.createProxy(IWindow.class), display, root, new Handler(), null,
+                context);
         info.mHasWindowFocus = true;
         info.mWindowVisibility = View.VISIBLE;
         info.mInTouchMode = false; // this is so that we can display selections.
diff --git a/android/view/BridgeInflater.java b/android/view/BridgeInflater.java
index 58d8c52..84fd0ed 100644
--- a/android/view/BridgeInflater.java
+++ b/android/view/BridgeInflater.java
@@ -31,6 +31,8 @@
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.util.ReflectionUtils;
 import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
 import com.android.util.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -45,25 +47,13 @@
 import android.widget.NumberPicker;
 
 import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
+import java.util.function.BiFunction;
 
-import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
-import static com.android.SdkConstants.BUTTON;
-import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
-import static com.android.SdkConstants.CHECK_BOX;
-import static com.android.SdkConstants.EDIT_TEXT;
-import static com.android.SdkConstants.IMAGE_BUTTON;
-import static com.android.SdkConstants.IMAGE_VIEW;
-import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
-import static com.android.SdkConstants.RADIO_BUTTON;
-import static com.android.SdkConstants.SEEK_BAR;
-import static com.android.SdkConstants.SPINNER;
-import static com.android.SdkConstants.TEXT_VIEW;
 import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext;
 
 /**
@@ -72,21 +62,7 @@
 public final class BridgeInflater extends LayoutInflater {
 
     private final LayoutlibCallback mLayoutlibCallback;
-    /**
-     * If true, the inflater will try to replace the framework widgets with the AppCompat versions.
-     * Ideally, this should be based on the activity being an AppCompat activity but since that is
-     * not trivial to check from layoutlib, we currently base the decision on the current theme
-     * being an AppCompat theme.
-     */
-    private boolean mLoadAppCompatViews;
-    /**
-     * This set contains the framework views that have an AppCompat version but failed to load.
-     * This might happen because not all widgets are contained in all versions of the support
-     * library.
-     * This will help us to avoid trying to load the AppCompat version multiple times if it
-     * doesn't exist.
-     */
-    private Set<String> mFailedAppCompatViews = new HashSet<>();
+
     private boolean mIsInMerge = false;
     private ResourceReference mResourceReference;
     private Map<View, String> mOpenDrawerLayouts;
@@ -94,15 +70,6 @@
     // Keep in sync with the same value in LayoutInflater.
     private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme };
 
-    private static final String APPCOMPAT_WIDGET_PREFIX = "android.support.v7.widget.AppCompat";
-    /** List of platform widgets that have an AppCompat version */
-    private static final Set<String> APPCOMPAT_VIEWS = Collections.unmodifiableSet(
-            new HashSet<>(
-                    Arrays.asList(TEXT_VIEW, IMAGE_VIEW, BUTTON, EDIT_TEXT, SPINNER,
-                            IMAGE_BUTTON, CHECK_BOX, RADIO_BUTTON, CHECKED_TEXT_VIEW,
-                            AUTO_COMPLETE_TEXT_VIEW, MULTI_AUTO_COMPLETE_TEXT_VIEW, "RatingBar",
-                            SEEK_BAR)));
-
     /**
      * List of class prefixes which are tried first by default.
      * <p/>
@@ -113,6 +80,7 @@
         "android.webkit.",
         "android.app."
     };
+    private BiFunction<String, AttributeSet, View> mCustomInflater;
 
     public static String[] getClassPrefixList() {
         return sClassPrefixList;
@@ -121,13 +89,9 @@
     private BridgeInflater(LayoutInflater original, Context newContext) {
         super(original, newContext);
         newContext = getBaseContext(newContext);
-        if (newContext instanceof BridgeContext) {
-            mLayoutlibCallback = ((BridgeContext) newContext).getLayoutlibCallback();
-            mLoadAppCompatViews = ((BridgeContext) newContext).isAppCompatTheme();
-        } else {
-            mLayoutlibCallback = null;
-            mLoadAppCompatViews = false;
-        }
+        mLayoutlibCallback = (newContext instanceof BridgeContext) ?
+                ((BridgeContext) newContext).getLayoutlibCallback() :
+                null;
     }
 
     /**
@@ -140,26 +104,14 @@
         super(context);
         mLayoutlibCallback = layoutlibCallback;
         mConstructorArgs[0] = context;
-        mLoadAppCompatViews = context.isAppCompatTheme();
     }
 
     @Override
     public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
-        View view = null;
+        View view = createViewFromCustomInflater(name, attrs);
 
-        try {
-            if (mLoadAppCompatViews
-                    && APPCOMPAT_VIEWS.contains(name)
-                    && !mFailedAppCompatViews.contains(name)) {
-                // We are using an AppCompat theme so try to load the appcompat views
-                view = loadCustomView(APPCOMPAT_WIDGET_PREFIX + name, attrs, true);
-
-                if (view == null) {
-                    mFailedAppCompatViews.add(name); // Do not try this one anymore
-                }
-            }
-
-            if (view == null) {
+        if (view == null) {
+            try {
                 // First try to find a class using the default Android prefixes
                 for (String prefix : sClassPrefixList) {
                     try {
@@ -181,19 +133,19 @@
                 } catch (ClassNotFoundException e) {
                     // Ignore. We'll try again using the custom view loader below.
                 }
-            }
 
-            // Finally try again using the custom view loader
-            if (view == null) {
-                view = loadCustomView(name, attrs);
+                // Finally try again using the custom view loader
+                if (view == null) {
+                    view = loadCustomView(name, attrs);
+                }
+            } catch (InflateException e) {
+                // Don't catch the InflateException below as that results in hiding the real cause.
+                throw e;
+            } catch (Exception e) {
+                // Wrap the real exception in a ClassNotFoundException, so that the calling method
+                // can deal with it.
+                throw new ClassNotFoundException("onCreateView", e);
             }
-        } catch (InflateException e) {
-            // Don't catch the InflateException below as that results in hiding the real cause.
-            throw e;
-        } catch (Exception e) {
-            // Wrap the real exception in a ClassNotFoundException, so that the calling method
-            // can deal with it.
-            throw new ClassNotFoundException("onCreateView", e);
         }
 
         setupViewInContext(view, attrs);
@@ -201,6 +153,110 @@
         return view;
     }
 
+    /**
+     * Finds the createView method in the given customInflaterClass. Since createView is
+     * currently package protected, it will show in the declared class so we iterate up the
+     * hierarchy and return the first instance we find.
+     * The returned method will be accessible.
+     */
+    @NotNull
+    private static Method getCreateViewMethod(Class<?> customInflaterClass) throws NoSuchMethodException {
+        Class<?> current = customInflaterClass;
+        do {
+            try {
+                Method method = current.getDeclaredMethod("createView", View.class, String.class,
+                                Context.class, AttributeSet.class, boolean.class, boolean.class,
+                                boolean.class, boolean.class);
+                method.setAccessible(true);
+                return method;
+            } catch (NoSuchMethodException ignore) {
+            }
+            current = current.getSuperclass();
+        } while (current != null && current != Object.class);
+
+        throw new NoSuchMethodException();
+    }
+
+    /**
+     * Finds the custom inflater class. If it's defined in the theme, we'll use that one (if the
+     * class does not exist, null is returned).
+     * If {@code viewInflaterClass} is not defined in the theme, we'll try to instantiate
+     * {@code android.support.v7.app.AppCompatViewInflater}
+     */
+    @Nullable
+    private static Class<?> findCustomInflater(@NotNull BridgeContext bc,
+            @NotNull LayoutlibCallback layoutlibCallback) {
+        ResourceValue value = bc.getRenderResources().findItemInTheme("viewInflaterClass", false);
+        String inflaterName = value != null ? value.getValue() : null;
+
+        if (inflaterName != null) {
+            try {
+                return layoutlibCallback.findClass(inflaterName);
+            } catch (ClassNotFoundException ignore) {
+            }
+
+            // viewInflaterClass was defined but we couldn't find the class
+        } else if (bc.isAppCompatTheme()) {
+            // Older versions of AppCompat do not define the viewInflaterClass so try to get it
+            // manually
+            try {
+                return layoutlibCallback.findClass("android.support.v7.app.AppCompatViewInflater");
+            } catch (ClassNotFoundException ignore) {
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Checks if there is a custom inflater and, when present, tries to instantiate the view
+     * using it.
+     */
+    @Nullable
+    private View createViewFromCustomInflater(@NotNull String name, @NotNull AttributeSet attrs) {
+        if (mCustomInflater == null) {
+            Context context = getContext();
+            context = getBaseContext(context);
+            if (context instanceof BridgeContext) {
+                BridgeContext bc = (BridgeContext) context;
+                Class<?> inflaterClass = findCustomInflater(bc, mLayoutlibCallback);
+
+                if (inflaterClass != null) {
+                    try {
+                        Constructor<?> constructor =  inflaterClass.getDeclaredConstructor();
+                        constructor.setAccessible(true);
+                        Object inflater = constructor.newInstance();
+                        Method method = getCreateViewMethod(inflaterClass);
+                        Context finalContext = context;
+                        mCustomInflater = (viewName, attributeSet) -> {
+                            try {
+                                return (View) method.invoke(inflater, null, viewName, finalContext,
+                                        attributeSet,
+                                        false,
+                                        false /*readAndroidTheme*/, // No need after L
+                                        true /*readAppTheme*/,
+                                        true /*wrapContext*/);
+                            } catch (IllegalAccessException | InvocationTargetException e) {
+                                assert false : "Call to createView failed";
+                            }
+                            return null;
+                        };
+                    } catch (InvocationTargetException | IllegalAccessException |
+                            NoSuchMethodException | InstantiationException ignore) {
+                    }
+                }
+            }
+
+            if (mCustomInflater == null) {
+                // There is no custom inflater. We'll create a nop custom inflater to avoid the
+                // penalty of trying to instantiate again
+                mCustomInflater = (s, attributeSet) -> null;
+            }
+        }
+
+        return mCustomInflater.apply(name, attrs);
+    }
+
     @Override
     public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
             boolean ignoreThemeAttr) {
diff --git a/android/view/Display.java b/android/view/Display.java
index 5bd7446..31cfebc 100644
--- a/android/view/Display.java
+++ b/android/view/Display.java
@@ -267,21 +267,21 @@
      *
      * @see #getState
      */
-    public static final int STATE_UNKNOWN = 0;
+    public static final int STATE_UNKNOWN = ViewProtoEnums.DISPLAY_STATE_UNKNOWN; // 0
 
     /**
      * Display state: The display is off.
      *
      * @see #getState
      */
-    public static final int STATE_OFF = 1;
+    public static final int STATE_OFF = ViewProtoEnums.DISPLAY_STATE_OFF; // 1
 
     /**
      * Display state: The display is on.
      *
      * @see #getState
      */
-    public static final int STATE_ON = 2;
+    public static final int STATE_ON = ViewProtoEnums.DISPLAY_STATE_ON; // 2
 
     /**
      * Display state: The display is dozing in a low power state; it is still
@@ -291,7 +291,7 @@
      * @see #getState
      * @see android.os.PowerManager#isInteractive
      */
-    public static final int STATE_DOZE = 3;
+    public static final int STATE_DOZE = ViewProtoEnums.DISPLAY_STATE_DOZE; // 3
 
     /**
      * Display state: The display is dozing in a suspended low power state; it is still
@@ -303,7 +303,7 @@
      * @see #getState
      * @see android.os.PowerManager#isInteractive
      */
-    public static final int STATE_DOZE_SUSPEND = 4;
+    public static final int STATE_DOZE_SUSPEND = ViewProtoEnums.DISPLAY_STATE_DOZE_SUSPEND; // 4
 
     /**
      * Display state: The display is on and optimized for VR mode.
@@ -311,7 +311,7 @@
      * @see #getState
      * @see android.os.PowerManager#isInteractive
      */
-    public static final int STATE_VR = 5;
+    public static final int STATE_VR = ViewProtoEnums.DISPLAY_STATE_VR; // 5
 
     /**
      * Display state: The display is in a suspended full power state; it is still
@@ -323,7 +323,7 @@
      * @see #getState
      * @see android.os.PowerManager#isInteractive
      */
-    public static final int STATE_ON_SUSPEND = 6;
+    public static final int STATE_ON_SUSPEND = ViewProtoEnums.DISPLAY_STATE_ON_SUSPEND; // 6
 
     /* The color mode constants defined below must be kept in sync with the ones in
      * system/core/include/system/graphics-base.h */
diff --git a/android/view/DisplayCutout.java b/android/view/DisplayCutout.java
index a61c8c1..66a9c6c 100644
--- a/android/view/DisplayCutout.java
+++ b/android/view/DisplayCutout.java
@@ -18,15 +18,12 @@
 
 import static android.view.DisplayCutoutProto.BOUNDS;
 import static android.view.DisplayCutoutProto.INSETS;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
 import android.content.res.Resources;
 import android.graphics.Matrix;
 import android.graphics.Path;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -38,19 +35,33 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Represents a part of the display that is not functional for displaying content.
+ * Represents the area of the display that is not functional for displaying content.
  *
  * <p>{@code DisplayCutout} is immutable.
  */
 public final class DisplayCutout {
 
     private static final String TAG = "DisplayCutout";
+    private static final String BOTTOM_MARKER = "@bottom";
     private static final String DP_MARKER = "@dp";
+    private static final String RIGHT_MARKER = "@right";
+
+    /**
+     * Category for overlays that allow emulating a display cutout on devices that don't have
+     * one.
+     *
+     * @see android.content.om.IOverlayManager
+     * @hide
+     */
+    public static final String EMULATION_OVERLAY_CATEGORY =
+            "com.android.internal.display_cutout_emulation";
 
     private static final Rect ZERO_RECT = new Rect();
     private static final Region EMPTY_REGION = new Region();
@@ -60,7 +71,19 @@
      *
      * @hide
      */
-    public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION);
+    public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION,
+            false /* copyArguments */);
+
+
+    private static final Object CACHE_LOCK = new Object();
+    @GuardedBy("CACHE_LOCK")
+    private static String sCachedSpec;
+    @GuardedBy("CACHE_LOCK")
+    private static int sCachedDisplayWidth;
+    @GuardedBy("CACHE_LOCK")
+    private static float sCachedDensity;
+    @GuardedBy("CACHE_LOCK")
+    private static DisplayCutout sCachedCutout;
 
     private final Rect mSafeInsets;
     private final Region mBounds;
@@ -68,18 +91,34 @@
     /**
      * Creates a DisplayCutout instance.
      *
-     * NOTE: the Rects passed into this instance are not copied and MUST remain unchanged.
-     *
-     * @hide
+     * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+     *                   {@link #getSafeInsetTop()} etc.
+     * @param boundingRects the bounding rects of the display cutouts as returned by
+     *               {@link #getBoundingRects()} ()}.
      */
-    @VisibleForTesting
-    public DisplayCutout(Rect safeInsets, Region bounds) {
-        mSafeInsets = safeInsets != null ? safeInsets : ZERO_RECT;
-        mBounds = bounds != null ? bounds : Region.obtain();
+    // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+    public DisplayCutout(Rect safeInsets, List<Rect> boundingRects) {
+        this(safeInsets != null ? new Rect(safeInsets) : ZERO_RECT,
+                boundingRectsToRegion(boundingRects),
+                true /* copyArguments */);
     }
 
     /**
-     * Returns true if there is no cutout or it is outside of the content view.
+     * Creates a DisplayCutout instance.
+     *
+     * @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
+     *                      are not copied and MUST remain unchanged forever.
+     */
+    private DisplayCutout(Rect safeInsets, Region bounds, boolean copyArguments) {
+        mSafeInsets = safeInsets == null ? ZERO_RECT :
+                (copyArguments ? new Rect(safeInsets) : safeInsets);
+        mBounds = bounds == null ? Region.obtain() :
+                (copyArguments ? Region.obtain(bounds) : bounds);
+    }
+
+    /**
+     * Returns true if the safe insets are empty (and therefore the current view does not
+     * overlap with the cutout or cutout area).
      *
      * @hide
      */
@@ -87,28 +126,37 @@
         return mSafeInsets.equals(ZERO_RECT);
     }
 
-    /** Returns the inset from the top which avoids the display cutout. */
+    /**
+     * Returns true if there is no cutout, i.e. the bounds are empty.
+     *
+     * @hide
+     */
+    public boolean isBoundsEmpty() {
+        return mBounds.isEmpty();
+    }
+
+    /** Returns the inset from the top which avoids the display cutout in pixels. */
     public int getSafeInsetTop() {
         return mSafeInsets.top;
     }
 
-    /** Returns the inset from the bottom which avoids the display cutout. */
+    /** Returns the inset from the bottom which avoids the display cutout in pixels. */
     public int getSafeInsetBottom() {
         return mSafeInsets.bottom;
     }
 
-    /** Returns the inset from the left which avoids the display cutout. */
+    /** Returns the inset from the left which avoids the display cutout in pixels. */
     public int getSafeInsetLeft() {
         return mSafeInsets.left;
     }
 
-    /** Returns the inset from the right which avoids the display cutout. */
+    /** Returns the inset from the right which avoids the display cutout in pixels. */
     public int getSafeInsetRight() {
         return mSafeInsets.right;
     }
 
     /**
-     * Returns the safe insets in a rect.
+     * Returns the safe insets in a rect in pixel units.
      *
      * @return a rect which is set to the safe insets.
      * @hide
@@ -120,23 +168,60 @@
     /**
      * Returns the bounding region of the cutout.
      *
+     * <p>
+     * <strong>Note:</strong> There may be more than one cutout, in which case the returned
+     * {@code Region} will be non-contiguous and its bounding rect will be meaningless without
+     * intersecting it first.
+     *
+     * Example:
+     * <pre>
+     *     // Getting the bounding rectangle of the top display cutout
+     *     Region bounds = displayCutout.getBounds();
+     *     bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(), Region.Op.INTERSECT);
+     *     Rect topDisplayCutout = bounds.getBoundingRect();
+     * </pre>
+     *
      * @return the bounding region of the cutout. Coordinates are relative
-     *         to the top-left corner of the content view.
+     *         to the top-left corner of the content view and in pixel units.
+     * @hide
      */
     public Region getBounds() {
         return Region.obtain(mBounds);
     }
 
     /**
-     * Returns the bounding rect of the cutout.
+     * Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional
+     * area on the display.
      *
-     * @return the bounding rect of the cutout. Coordinates are relative
-     *         to the top-left corner of the content view.
-     * @hide
+     * There will be at most one non-functional area per short edge of the device, and none on
+     * the long edges.
+     *
+     * @return a list of bounding {@code Rect}s, one for each display cutout area.
      */
-    public Rect getBoundingRect() {
-        // TODO(roosa): Inline.
-        return mBounds.getBounds();
+    public List<Rect> getBoundingRects() {
+        List<Rect> result = new ArrayList<>();
+        Region bounds = Region.obtain();
+        // top
+        bounds.set(mBounds);
+        bounds.op(0, 0, Integer.MAX_VALUE, getSafeInsetTop(), Region.Op.INTERSECT);
+        if (!bounds.isEmpty()) {
+            result.add(bounds.getBounds());
+        }
+        // left
+        bounds.set(mBounds);
+        bounds.op(0, 0, getSafeInsetLeft(), Integer.MAX_VALUE, Region.Op.INTERSECT);
+        if (!bounds.isEmpty()) {
+            result.add(bounds.getBounds());
+        }
+        // right & bottom
+        bounds.set(mBounds);
+        bounds.op(getSafeInsetLeft() + 1, getSafeInsetTop() + 1,
+                Integer.MAX_VALUE, Integer.MAX_VALUE, Region.Op.INTERSECT);
+        if (!bounds.isEmpty()) {
+            result.add(bounds.getBounds());
+        }
+        bounds.recycle();
+        return result;
     }
 
     @Override
@@ -162,7 +247,7 @@
     @Override
     public String toString() {
         return "DisplayCutout{insets=" + mSafeInsets
-                + " boundingRect=" + getBoundingRect()
+                + " boundingRect=" + mBounds.getBounds()
                 + "}";
     }
 
@@ -207,74 +292,19 @@
         }
 
         bounds.translate(-insetLeft, -insetTop);
-
-        return new DisplayCutout(safeInsets, bounds);
+        return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
     }
 
     /**
-     * Calculates the safe insets relative to the given reference frame.
+     * Returns a copy of this instance with the safe insets replaced with the parameter.
      *
-     * @return a copy of this instance with the safe insets calculated
+     * @param safeInsets the new safe insets in pixels
+     * @return a copy of this instance with the safe insets replaced with the argument.
+     *
      * @hide
      */
-    public DisplayCutout calculateRelativeTo(Rect frame) {
-        if (mBounds.isEmpty() || !Rect.intersects(frame, mBounds.getBounds())) {
-            return NO_CUTOUT;
-        }
-
-        return DisplayCutout.calculateRelativeTo(frame, Region.obtain(mBounds));
-    }
-
-    private static DisplayCutout calculateRelativeTo(Rect frame, Region bounds) {
-        Rect boundingRect = bounds.getBounds();
-        Rect safeRect = new Rect();
-
-        int bestArea = 0;
-        int bestVariant = 0;
-        for (int variant = ROTATION_0; variant <= ROTATION_270; variant++) {
-            int area = calculateInsetVariantArea(frame, boundingRect, variant, safeRect);
-            if (bestArea < area) {
-                bestArea = area;
-                bestVariant = variant;
-            }
-        }
-        calculateInsetVariantArea(frame, boundingRect, bestVariant, safeRect);
-        if (safeRect.isEmpty()) {
-            // The entire frame overlaps with the cutout.
-            safeRect.set(0, frame.height(), 0, 0);
-        } else {
-            // Convert safeRect to insets relative to frame. We're reusing the rect here to avoid
-            // an allocation.
-            safeRect.set(
-                    Math.max(0, safeRect.left - frame.left),
-                    Math.max(0, safeRect.top - frame.top),
-                    Math.max(0, frame.right - safeRect.right),
-                    Math.max(0, frame.bottom - safeRect.bottom));
-        }
-
-        bounds.translate(-frame.left, -frame.top);
-
-        return new DisplayCutout(safeRect, bounds);
-    }
-
-    private static int calculateInsetVariantArea(Rect frame, Rect boundingRect, int variant,
-            Rect outSafeRect) {
-        switch (variant) {
-            case ROTATION_0:
-                outSafeRect.set(frame.left, frame.top, frame.right, boundingRect.top);
-                break;
-            case ROTATION_90:
-                outSafeRect.set(frame.left, frame.top, boundingRect.left, frame.bottom);
-                break;
-            case ROTATION_180:
-                outSafeRect.set(frame.left, boundingRect.bottom, frame.right, frame.bottom);
-                break;
-            case ROTATION_270:
-                outSafeRect.set(boundingRect.right, frame.top, frame.right, frame.bottom);
-                break;
-        }
-
-        return outSafeRect.isEmpty() ? 0 : outSafeRect.width() * outSafeRect.height();
+    public DisplayCutout replaceSafeInsets(Rect safeInsets) {
+        return new DisplayCutout(new Rect(safeInsets), mBounds, false /* copyArguments */);
     }
 
     private static int atLeastZero(int value) {
@@ -283,21 +313,17 @@
 
 
     /**
-     * Creates an instance from a bounding polygon.
+     * Creates an instance from a bounding rect.
      *
      * @hide
      */
-    public static DisplayCutout fromBoundingPolygon(List<Point> points) {
+    public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
         Path path = new Path();
         path.reset();
-        for (int i = 0; i < points.size(); i++) {
-            Point point = points.get(i);
-            if (i == 0) {
-                path.moveTo(point.x, point.y);
-            } else {
-                path.lineTo(point.x, point.y);
-            }
-        }
+        path.moveTo(left, top);
+        path.lineTo(left, bottom);
+        path.lineTo(right, bottom);
+        path.lineTo(right, top);
         path.close();
         return fromBounds(path);
     }
@@ -317,7 +343,7 @@
         Region bounds = new Region();
         bounds.setPath(path, clipRegion);
         clipRegion.recycle();
-        return new DisplayCutout(ZERO_RECT, bounds);
+        return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
     }
 
     /**
@@ -325,18 +351,49 @@
      *
      * @hide
      */
-    public static DisplayCutout fromResources(Resources res, int displayWidth) {
-        String spec = res.getString(R.string.config_mainBuiltInDisplayCutout);
+    public static DisplayCutout fromResources(Resources res, int displayWidth, int displayHeight) {
+        return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+                displayWidth, displayHeight, res.getDisplayMetrics().density);
+    }
+
+    /**
+     * Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = PRIVATE)
+    public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
+            float density) {
         if (TextUtils.isEmpty(spec)) {
             return null;
         }
+        synchronized (CACHE_LOCK) {
+            if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
+                    && sCachedDensity == density) {
+                return sCachedCutout;
+            }
+        }
         spec = spec.trim();
+        final float offsetX;
+        if (spec.endsWith(RIGHT_MARKER)) {
+            offsetX = displayWidth;
+            spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
+        } else {
+            offsetX = displayWidth / 2f;
+        }
         final boolean inDp = spec.endsWith(DP_MARKER);
         if (inDp) {
             spec = spec.substring(0, spec.length() - DP_MARKER.length());
         }
 
-        Path p;
+        String bottomSpec = null;
+        if (spec.contains(BOTTOM_MARKER)) {
+            String[] splits = spec.split(BOTTOM_MARKER, 2);
+            spec = splits[0].trim();
+            bottomSpec = splits[1].trim();
+        }
+
+        final Path p;
         try {
             p = PathParser.createPathFromPathData(spec);
         } catch (Throwable e) {
@@ -346,12 +403,43 @@
 
         final Matrix m = new Matrix();
         if (inDp) {
-            final float dpToPx = res.getDisplayMetrics().density;
-            m.postScale(dpToPx, dpToPx);
+            m.postScale(density, density);
         }
-        m.postTranslate(displayWidth / 2f, 0);
+        m.postTranslate(offsetX, 0);
         p.transform(m);
-        return fromBounds(p);
+
+        if (bottomSpec != null) {
+            final Path bottomPath;
+            try {
+                bottomPath = PathParser.createPathFromPathData(bottomSpec);
+            } catch (Throwable e) {
+                Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
+                return null;
+            }
+            // Keep top transform
+            m.postTranslate(0, displayHeight);
+            bottomPath.transform(m);
+            p.addPath(bottomPath);
+        }
+
+        final DisplayCutout result = fromBounds(p);
+        synchronized (CACHE_LOCK) {
+            sCachedSpec = spec;
+            sCachedDisplayWidth = displayWidth;
+            sCachedDensity = density;
+            sCachedCutout = result;
+        }
+        return result;
+    }
+
+    private static Region boundingRectsToRegion(List<Rect> rects) {
+        Region result = Region.obtain();
+        if (rects != null) {
+            for (Rect r : rects) {
+                result.op(r, Region.Op.UNION);
+            }
+        }
+        return result;
     }
 
     /**
@@ -439,7 +527,7 @@
             Rect safeInsets = in.readTypedObject(Rect.CREATOR);
             Region bounds = in.readTypedObject(Region.CREATOR);
 
-            return new DisplayCutout(safeInsets, bounds);
+            return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
         }
 
         public DisplayCutout get() {
diff --git a/android/view/DisplayInfo.java b/android/view/DisplayInfo.java
index 37e9815..913e592 100644
--- a/android/view/DisplayInfo.java
+++ b/android/view/DisplayInfo.java
@@ -20,6 +20,7 @@
 import static android.view.DisplayInfoProto.APP_WIDTH;
 import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
 import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
+import static android.view.DisplayInfoProto.NAME;
 
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -30,9 +31,8 @@
 import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
 
-import libcore.util.Objects;
-
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Describes the characteristics of a particular logical display.
@@ -294,8 +294,8 @@
                 && layerStack == other.layerStack
                 && flags == other.flags
                 && type == other.type
-                && Objects.equal(address, other.address)
-                && Objects.equal(uniqueId, other.uniqueId)
+                && Objects.equals(address, other.address)
+                && Objects.equals(uniqueId, other.uniqueId)
                 && appWidth == other.appWidth
                 && appHeight == other.appHeight
                 && smallestNominalAppWidth == other.smallestNominalAppWidth
@@ -308,13 +308,13 @@
                 && overscanTop == other.overscanTop
                 && overscanRight == other.overscanRight
                 && overscanBottom == other.overscanBottom
-                && Objects.equal(displayCutout, other.displayCutout)
+                && Objects.equals(displayCutout, other.displayCutout)
                 && rotation == other.rotation
                 && modeId == other.modeId
                 && defaultModeId == other.defaultModeId
                 && colorMode == other.colorMode
                 && Arrays.equals(supportedColorModes, other.supportedColorModes)
-                && Objects.equal(hdrCapabilities, other.hdrCapabilities)
+                && Objects.equals(hdrCapabilities, other.hdrCapabilities)
                 && logicalDensityDpi == other.logicalDensityDpi
                 && physicalXDpi == other.physicalXDpi
                 && physicalYDpi == other.physicalYDpi
@@ -322,7 +322,7 @@
                 && presentationDeadlineNanos == other.presentationDeadlineNanos
                 && state == other.state
                 && ownerUid == other.ownerUid
-                && Objects.equal(ownerPackageName, other.ownerPackageName)
+                && Objects.equals(ownerPackageName, other.ownerPackageName)
                 && removeMode == other.removeMode;
     }
 
@@ -685,6 +685,7 @@
         protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight);
         protoOutputStream.write(APP_WIDTH, appWidth);
         protoOutputStream.write(APP_HEIGHT, appHeight);
+        protoOutputStream.write(NAME, name);
         protoOutputStream.end(token);
     }
 
diff --git a/android/view/DisplayListCanvas.java b/android/view/DisplayListCanvas.java
index 8f9ae0e..671532c 100644
--- a/android/view/DisplayListCanvas.java
+++ b/android/view/DisplayListCanvas.java
@@ -198,8 +198,8 @@
      *
      * @param layer The layer to composite on this canvas
      */
-    void drawHardwareLayer(HardwareLayer layer) {
-        nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle());
+    void drawTextureLayer(TextureLayer layer) {
+        nDrawTextureLayer(mNativeCanvasWrapper, layer.getLayerHandle());
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -257,7 +257,7 @@
     @CriticalNative
     private static native void nDrawRenderNode(long renderer, long renderNode);
     @CriticalNative
-    private static native void nDrawLayer(long renderer, long layer);
+    private static native void nDrawTextureLayer(long renderer, long layer);
     @CriticalNative
     private static native void nDrawCircle(long renderer, long propCx,
             long propCy, long propRadius, long propPaint);
diff --git a/android/view/HardwareLayer.java b/android/view/HardwareLayer.java
deleted file mode 100644
index 7af1020..0000000
--- a/android/view/HardwareLayer.java
+++ /dev/null
@@ -1,153 +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.view;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.SurfaceTexture;
-
-import com.android.internal.util.VirtualRefBasePtr;
-
-/**
- * A hardware layer can be used to render graphics operations into a hardware
- * friendly buffer. For instance, with an OpenGL backend a hardware layer
- * would use a Frame Buffer Object (FBO.) The hardware layer can be used as
- * a drawing cache when a complex set of graphics operations needs to be
- * drawn several times.
- *
- * @hide
- */
-final class HardwareLayer {
-    private ThreadedRenderer mRenderer;
-    private VirtualRefBasePtr mFinalizer;
-
-    private HardwareLayer(ThreadedRenderer renderer, long deferredUpdater) {
-        if (renderer == null || deferredUpdater == 0) {
-            throw new IllegalArgumentException("Either hardware renderer: " + renderer
-                    + " or deferredUpdater: " + deferredUpdater + " is invalid");
-        }
-        mRenderer = renderer;
-        mFinalizer = new VirtualRefBasePtr(deferredUpdater);
-    }
-
-    /**
-     * Update the paint used when drawing this layer.
-     *
-     * @param paint The paint used when the layer is drawn into the destination canvas.
-     * @see View#setLayerPaint(android.graphics.Paint)
-     */
-    public void setLayerPaint(@Nullable Paint paint) {
-        nSetLayerPaint(mFinalizer.get(), paint != null ? paint.getNativeInstance() : 0);
-        mRenderer.pushLayerUpdate(this);
-    }
-
-    /**
-     * Indicates whether this layer can be rendered.
-     *
-     * @return True if the layer can be rendered into, false otherwise
-     */
-    public boolean isValid() {
-        return mFinalizer != null && mFinalizer.get() != 0;
-    }
-
-    /**
-     * Destroys resources without waiting for a GC.
-     */
-    public void destroy() {
-        if (!isValid()) {
-            // Already destroyed
-            return;
-        }
-        mRenderer.onLayerDestroyed(this);
-        mRenderer = null;
-        mFinalizer.release();
-        mFinalizer = null;
-    }
-
-    public long getDeferredLayerUpdater() {
-        return mFinalizer.get();
-    }
-
-    /**
-     * Copies this layer into the specified bitmap.
-     *
-     * @param bitmap The bitmap to copy they layer into
-     *
-     * @return True if the copy was successful, false otherwise
-     */
-    public boolean copyInto(Bitmap bitmap) {
-        return mRenderer.copyLayerInto(this, bitmap);
-    }
-
-    /**
-     * Update the layer's properties. Note that after calling this isValid() may
-     * return false if the requested width/height cannot be satisfied
-     *
-     * @param width The new width of this layer
-     * @param height The new height of this layer
-     * @param isOpaque Whether this layer is opaque
-     *
-     * @return true if the layer's properties will change, false if they already
-     *         match the desired values.
-     */
-    public boolean prepare(int width, int height, boolean isOpaque) {
-        return nPrepare(mFinalizer.get(), width, height, isOpaque);
-    }
-
-    /**
-     * Sets an optional transform on this layer.
-     *
-     * @param matrix The transform to apply to the layer.
-     */
-    public void setTransform(Matrix matrix) {
-        nSetTransform(mFinalizer.get(), matrix.native_instance);
-        mRenderer.pushLayerUpdate(this);
-    }
-
-    /**
-     * Indicates that this layer has lost its texture.
-     */
-    public void detachSurfaceTexture() {
-        mRenderer.detachSurfaceTexture(mFinalizer.get());
-    }
-
-    public long getLayerHandle() {
-        return mFinalizer.get();
-    }
-
-    public void setSurfaceTexture(SurfaceTexture surface) {
-        nSetSurfaceTexture(mFinalizer.get(), surface);
-        mRenderer.pushLayerUpdate(this);
-    }
-
-    public void updateSurfaceTexture() {
-        nUpdateSurfaceTexture(mFinalizer.get());
-        mRenderer.pushLayerUpdate(this);
-    }
-
-    static HardwareLayer adoptTextureLayer(ThreadedRenderer renderer, long layer) {
-        return new HardwareLayer(renderer, layer);
-    }
-
-    private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
-    private static native void nSetLayerPaint(long layerUpdater, long paint);
-    private static native void nSetTransform(long layerUpdater, long matrix);
-    private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface);
-    private static native void nUpdateSurfaceTexture(long layerUpdater);
-}
diff --git a/android/view/IWindowManagerImpl.java b/android/view/IWindowManagerImpl.java
index 93e6c0b..f62aa6c 100644
--- a/android/view/IWindowManagerImpl.java
+++ b/android/view/IWindowManagerImpl.java
@@ -346,7 +346,7 @@
     }
 
     @Override
-    public void setScreenCaptureDisabled(int userId, boolean disabled) {
+    public void refreshScreenCaptureDisabled(int userId) {
         // TODO Auto-generated method stub
     }
 
@@ -387,6 +387,15 @@
     }
 
     @Override
+    public void setShelfHeight(boolean visible, int shelfHeight) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
+    }
+
+    @Override
     public boolean stopViewServer() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
@@ -526,14 +535,6 @@
     }
 
     @Override
-    public void enableSurfaceTrace(ParcelFileDescriptor fd) throws RemoteException {
-    }
-
-    @Override
-    public void disableSurfaceTrace() throws RemoteException {
-    }
-
-    @Override
     public Region getCurrentImeTouchRegion() throws RemoteException {
         return null;
     }
@@ -561,4 +562,12 @@
     public boolean isWindowTraceEnabled() throws RemoteException {
         return false;
     }
+
+    @Override
+    public void requestUserActivityNotification() throws RemoteException {
+    }
+
+    @Override
+    public void dontOverrideDisplayInfo(int displayId) throws RemoteException {
+    }
 }
diff --git a/android/view/MenuInflater_Delegate.java b/android/view/MenuInflater_Delegate.java
index 977a2a7..d16d851 100644
--- a/android/view/MenuInflater_Delegate.java
+++ b/android/view/MenuInflater_Delegate.java
@@ -56,7 +56,10 @@
             }
         }
 
-        if (menuItem == null || !menuItem.getClass().getName().startsWith("android.support.")) {
+        String menuItemName = menuItem != null ? menuItem.getClass().getName() : null;
+        if (menuItemName == null ||
+                !menuItemName.startsWith("android.support.") ||
+                !menuItemName.startsWith("androidx.")) {
             // This means that Bridge did not take over the instantiation of some object properly.
             // This is most likely a bug in the LayoutLib code.
             // We suppress this error for AppCompat menus since we do not support them in the menu
diff --git a/android/view/NotificationHeaderView.java b/android/view/NotificationHeaderView.java
index fbba8ab..7a10364 100644
--- a/android/view/NotificationHeaderView.java
+++ b/android/view/NotificationHeaderView.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.content.Context;
 import android.content.res.Resources;
@@ -25,6 +26,7 @@
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -49,10 +51,15 @@
     private View mHeaderText;
     private View mSecondaryHeaderText;
     private OnClickListener mExpandClickListener;
+    private OnClickListener mAppOpsListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
     private ImageView mExpandButton;
     private CachingIconView mIcon;
     private View mProfileBadge;
+    private View mOverlayIcon;
+    private View mCameraIcon;
+    private View mMicIcon;
+    private View mAppOps;
     private int mIconColor;
     private int mOriginalNotificationColor;
     private boolean mExpanded;
@@ -108,6 +115,10 @@
         mExpandButton = findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
+        mCameraIcon = findViewById(com.android.internal.R.id.camera);
+        mMicIcon = findViewById(com.android.internal.R.id.mic);
+        mOverlayIcon = findViewById(com.android.internal.R.id.overlay);
+        mAppOps = findViewById(com.android.internal.R.id.app_ops);
     }
 
     @Override
@@ -198,6 +209,11 @@
                 layoutRight = end - paddingEnd;
                 end = layoutLeft = layoutRight - child.getMeasuredWidth();
             }
+            if (child == mAppOps) {
+                int paddingEnd = mContentEndMargin;
+                layoutRight = end - paddingEnd;
+                end = layoutLeft = layoutRight - child.getMeasuredWidth();
+            }
             if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
                 int ltrLeft = layoutLeft;
                 layoutLeft = getWidth() - layoutRight;
@@ -252,15 +268,26 @@
     }
 
     private void updateTouchListener() {
-        if (mExpandClickListener != null) {
-            mTouchListener.bindTouchRects();
+        if (mExpandClickListener == null && mAppOpsListener == null) {
+            setOnTouchListener(null);
+            return;
         }
+        setOnTouchListener(mTouchListener);
+        mTouchListener.bindTouchRects();
+    }
+
+    /**
+     * Sets onclick listener for app ops icons.
+     */
+    public void setAppOpsOnClickListener(OnClickListener l) {
+        mAppOpsListener = l;
+        mAppOps.setOnClickListener(mAppOpsListener);
+        updateTouchListener();
     }
 
     @Override
     public void setOnClickListener(@Nullable OnClickListener l) {
         mExpandClickListener = l;
-        setOnTouchListener(mExpandClickListener != null ? mTouchListener : null);
         mExpandButton.setOnClickListener(mExpandClickListener);
         updateTouchListener();
     }
@@ -289,6 +316,22 @@
         updateExpandButton();
     }
 
+    /**
+     * Shows or hides 'app op in use' icons based on app usage.
+     */
+    public void showAppOpsIcons(ArraySet<Integer> appOps) {
+        if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null || appOps == null) {
+            return;
+        }
+
+        mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW)
+                ? View.VISIBLE : View.GONE);
+        mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA)
+                ? View.VISIBLE : View.GONE);
+        mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO)
+                ? View.VISIBLE : View.GONE);
+    }
+
     private void updateExpandButton() {
         int drawableId;
         int contentDescriptionId;
@@ -335,6 +378,7 @@
 
         private final ArrayList<Rect> mTouchRects = new ArrayList<>();
         private Rect mExpandButtonRect;
+        private Rect mAppOpsRect;
         private int mTouchSlop;
         private boolean mTrackGesture;
         private float mDownX;
@@ -347,6 +391,7 @@
             mTouchRects.clear();
             addRectAroundView(mIcon);
             mExpandButtonRect = addRectAroundView(mExpandButton);
+            mAppOpsRect = addRectAroundView(mAppOps);
             addWidthRect();
             mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
         }
@@ -368,16 +413,18 @@
 
         private Rect getRectAroundView(View view) {
             float size = 48 * getResources().getDisplayMetrics().density;
+            float width = Math.max(size, view.getWidth());
+            float height = Math.max(size, view.getHeight());
             final Rect r = new Rect();
             if (view.getVisibility() == GONE) {
                 view = getFirstChildNotGone();
-                r.left = (int) (view.getLeft() - size / 2.0f);
+                r.left = (int) (view.getLeft() - width / 2.0f);
             } else {
-                r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - size / 2.0f);
+                r.left = (int) ((view.getLeft() + view.getRight()) / 2.0f - width / 2.0f);
             }
-            r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - size / 2.0f);
-            r.bottom = (int) (r.top + size);
-            r.right = (int) (r.left + size);
+            r.top = (int) ((view.getTop() + view.getBottom()) / 2.0f - height / 2.0f);
+            r.bottom = (int) (r.top + height);
+            r.right = (int) (r.left + width);
             return r;
         }
 
@@ -405,6 +452,11 @@
                     break;
                 case MotionEvent.ACTION_UP:
                     if (mTrackGesture) {
+                        if (mAppOps.isVisibleToUser() && (mAppOpsRect.contains((int) x, (int) y)
+                                || mAppOpsRect.contains((int) mDownX, (int) mDownY))) {
+                            mAppOps.performClick();
+                            return true;
+                        }
                         mExpandButton.performClick();
                     }
                     break;
diff --git a/android/view/PixelCopy.java b/android/view/PixelCopy.java
index a14609f..2797a4d 100644
--- a/android/view/PixelCopy.java
+++ b/android/view/PixelCopy.java
@@ -263,8 +263,16 @@
                     "Only able to copy windows with decor views");
         }
         Surface surface = null;
-        if (source.peekDecorView().getViewRootImpl() != null) {
-            surface = source.peekDecorView().getViewRootImpl().mSurface;
+        final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
+        if (root != null) {
+            surface = root.mSurface;
+            final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
+            if (srcRect == null) {
+                srcRect = new Rect(surfaceInsets.left, surfaceInsets.top,
+                        root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
+            } else {
+                srcRect.offset(surfaceInsets.left, surfaceInsets.top);
+            }
         }
         if (surface == null || !surface.isValid()) {
             throw new IllegalArgumentException(
diff --git a/android/view/PointerIcon.java b/android/view/PointerIcon.java
index 3fd4696..8cb46b7 100644
--- a/android/view/PointerIcon.java
+++ b/android/view/PointerIcon.java
@@ -23,6 +23,10 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -396,6 +400,33 @@
         return true;
     }
 
+    /**
+     *  Get the Bitmap from the Drawable.
+     *
+     *  If the Bitmap needed to be scaled up to account for density, BitmapDrawable
+     *  handles this at draw time. But this class doesn't actually draw the Bitmap;
+     *  it is just a holder for native code to access its SkBitmap. So this needs to
+     *  get a version that is scaled to account for density.
+     */
+    private Bitmap getBitmapFromDrawable(BitmapDrawable bitmapDrawable) {
+        Bitmap bitmap = bitmapDrawable.getBitmap();
+        final int scaledWidth  = bitmapDrawable.getIntrinsicWidth();
+        final int scaledHeight = bitmapDrawable.getIntrinsicHeight();
+        if (scaledWidth == bitmap.getWidth() && scaledHeight == bitmap.getHeight()) {
+            return bitmap;
+        }
+
+        Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        RectF dst = new RectF(0, 0, scaledWidth, scaledHeight);
+
+        Bitmap scaled = Bitmap.createBitmap(scaledWidth, scaledHeight, bitmap.getConfig());
+        Canvas canvas = new Canvas(scaled);
+        Paint paint = new Paint();
+        paint.setFilterBitmap(true);
+        canvas.drawBitmap(bitmap, src, dst, paint);
+        return scaled;
+    }
+
     private void loadResource(Context context, Resources resources, @XmlRes int resourceId) {
         final XmlResourceParser parser = resources.getXml(resourceId);
         final int bitmapRes;
@@ -452,7 +483,8 @@
                                 + "is different. All frames should have the exact same size and "
                                 + "share the same hotspot.");
                     }
-                    mBitmapFrames[i - 1] = ((BitmapDrawable)drawableFrame).getBitmap();
+                    BitmapDrawable bitmapDrawableFrame = (BitmapDrawable) drawableFrame;
+                    mBitmapFrames[i - 1] = getBitmapFromDrawable(bitmapDrawableFrame);
                 }
             }
         }
@@ -461,7 +493,8 @@
                     + "refer to a bitmap drawable.");
         }
 
-        final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+        final Bitmap bitmap = getBitmapFromDrawable(bitmapDrawable);
         validateHotSpot(bitmap, hotSpotX, hotSpotY);
         // Set the properties now that we have successfully loaded the icon.
         mBitmap = bitmap;
diff --git a/android/view/RecordingCanvas.java b/android/view/RecordingCanvas.java
index fbb862b..f7a41ff 100644
--- a/android/view/RecordingCanvas.java
+++ b/android/view/RecordingCanvas.java
@@ -34,7 +34,7 @@
 import android.graphics.RectF;
 import android.graphics.TemporaryBuffer;
 import android.text.GraphicsOperations;
-import android.text.MeasuredText;
+import android.text.PrecomputedText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -474,8 +474,7 @@
         }
 
         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
-                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */,
-                0 /* measured text offset */);
+                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
     }
 
     @Override
@@ -506,19 +505,16 @@
             char[] buf = TemporaryBuffer.obtain(contextLen);
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
-            int measuredTextOffset = 0;
-            if (text instanceof MeasuredText) {
-                MeasuredText mt = (MeasuredText) text;
+            if (text instanceof PrecomputedText) {
+                PrecomputedText mt = (PrecomputedText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
                     // Only support if the target is in the same paragraph.
                     measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
-                    measuredTextOffset = start - mt.getParagraphStart(paraIndex);
                 }
             }
             nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
-                    0, contextLen, x, y, isRtl, paint.getNativeInstance(),
-                    measuredTextPtr, measuredTextOffset);
+                    0, contextLen, x, y, isRtl, paint.getNativeInstance(), measuredTextPtr);
             TemporaryBuffer.recycle(buf);
         }
     }
@@ -641,7 +637,7 @@
     @FastNative
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativeMeasuredText, int measuredTextOffset);
+            long nativePrecomputedText);
 
     @FastNative
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
diff --git a/android/view/RemoteAnimationAdapter.java b/android/view/RemoteAnimationAdapter.java
index d597e59..a864e55 100644
--- a/android/view/RemoteAnimationAdapter.java
+++ b/android/view/RemoteAnimationAdapter.java
@@ -52,6 +52,9 @@
     private final long mDuration;
     private final long mStatusBarTransitionDelay;
 
+    /** @see #getCallingPid */
+    private int mCallingPid;
+
     /**
      * @param runner The interface that gets notified when we actually need to start the animation.
      * @param duration The duration of the animation.
@@ -83,6 +86,20 @@
         return mStatusBarTransitionDelay;
     }
 
+    /**
+     * To be called by system_server to keep track which pid is running this animation.
+     */
+    public void setCallingPid(int pid) {
+        mCallingPid = pid;
+    }
+
+    /**
+     * @return The pid of the process running the animation.
+     */
+    public int getCallingPid() {
+        return mCallingPid;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/android/view/RemoteAnimationDefinition.java b/android/view/RemoteAnimationDefinition.java
index 381f692..d2240e1 100644
--- a/android/view/RemoteAnimationDefinition.java
+++ b/android/view/RemoteAnimationDefinition.java
@@ -16,10 +16,14 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+
 import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.ActivityType;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.SparseArray;
 import android.view.WindowManager.TransitionType;
 
@@ -30,7 +34,7 @@
  */
 public class RemoteAnimationDefinition implements Parcelable {
 
-    private final SparseArray<RemoteAnimationAdapter> mTransitionAnimationMap;
+    private final SparseArray<RemoteAnimationAdapterEntry> mTransitionAnimationMap;
 
     public RemoteAnimationDefinition() {
         mTransitionAnimationMap = new SparseArray<>();
@@ -40,34 +44,80 @@
      * Registers a remote animation for a specific transition.
      *
      * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+     * @param activityTypeFilter The remote animation only runs if an activity with type of this
+     *                           parameter is involved in the transition.
+     * @param adapter The adapter that described how to run the remote animation.
+     */
+    public void addRemoteAnimation(@TransitionType int transition,
+            @ActivityType int activityTypeFilter, RemoteAnimationAdapter adapter) {
+        mTransitionAnimationMap.put(transition,
+                new RemoteAnimationAdapterEntry(adapter, activityTypeFilter));
+    }
+
+    /**
+     * Registers a remote animation for a specific transition without defining an activity type
+     * filter.
+     *
+     * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
      * @param adapter The adapter that described how to run the remote animation.
      */
     public void addRemoteAnimation(@TransitionType int transition, RemoteAnimationAdapter adapter) {
-        mTransitionAnimationMap.put(transition, adapter);
+        addRemoteAnimation(transition, ACTIVITY_TYPE_UNDEFINED, adapter);
     }
 
     /**
      * Checks whether a remote animation for specific transition is defined.
      *
      * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+     * @param activityTypes The set of activity types of activities that are involved in the
+     *                      transition. Will be used for filtering.
      * @return Whether this definition has defined a remote animation for the specified transition.
      */
-    public boolean hasTransition(@TransitionType int transition) {
-        return mTransitionAnimationMap.get(transition) != null;
+    public boolean hasTransition(@TransitionType int transition, ArraySet<Integer> activityTypes) {
+        return getAdapter(transition, activityTypes) != null;
     }
 
     /**
      * Retrieves the remote animation for a specific transition.
      *
      * @param transition The transition type. Must be one of WindowManager.TRANSIT_* values.
+     * @param activityTypes The set of activity types of activities that are involved in the
+     *                      transition. Will be used for filtering.
      * @return The remote animation adapter for the specified transition.
      */
-    public @Nullable RemoteAnimationAdapter getAdapter(@TransitionType int transition) {
-        return mTransitionAnimationMap.get(transition);
+    public @Nullable RemoteAnimationAdapter getAdapter(@TransitionType int transition,
+            ArraySet<Integer> activityTypes) {
+        final RemoteAnimationAdapterEntry entry = mTransitionAnimationMap.get(transition);
+        if (entry == null) {
+            return null;
+        }
+        if (entry.activityTypeFilter == ACTIVITY_TYPE_UNDEFINED
+                || activityTypes.contains(entry.activityTypeFilter)) {
+            return entry.adapter;
+        } else {
+            return null;
+        }
     }
 
     public RemoteAnimationDefinition(Parcel in) {
-        mTransitionAnimationMap = in.readSparseArray(null /* loader */);
+        final int size = in.readInt();
+        mTransitionAnimationMap = new SparseArray<>(size);
+        for (int i = 0; i < size; i++) {
+            final int transition = in.readInt();
+            final RemoteAnimationAdapterEntry entry = in.readTypedObject(
+                    RemoteAnimationAdapterEntry.CREATOR);
+            mTransitionAnimationMap.put(transition, entry);
+        }
+    }
+
+    /**
+     * To be called by system_server to keep track which pid is running the remote animations inside
+     * this definition.
+     */
+    public void setCallingPid(int pid) {
+        for (int i = mTransitionAnimationMap.size() - 1; i >= 0; i--) {
+            mTransitionAnimationMap.valueAt(i).adapter.setCallingPid(pid);
+        }
     }
 
     @Override
@@ -77,7 +127,12 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeSparseArray((SparseArray) mTransitionAnimationMap);
+        final int size = mTransitionAnimationMap.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            dest.writeInt(mTransitionAnimationMap.keyAt(i));
+            dest.writeTypedObject(mTransitionAnimationMap.valueAt(i), flags);
+        }
     }
 
     public static final Creator<RemoteAnimationDefinition> CREATOR =
@@ -90,4 +145,50 @@
             return new RemoteAnimationDefinition[size];
         }
     };
+
+    private static class RemoteAnimationAdapterEntry implements Parcelable {
+
+        final RemoteAnimationAdapter adapter;
+
+        /**
+         * Only run the transition if one of the activities matches the filter.
+         * {@link WindowConfiguration.ACTIVITY_TYPE_UNDEFINED} means no filter
+         */
+        @ActivityType final int activityTypeFilter;
+
+        RemoteAnimationAdapterEntry(RemoteAnimationAdapter adapter, int activityTypeFilter) {
+            this.adapter = adapter;
+            this.activityTypeFilter = activityTypeFilter;
+        }
+
+        private RemoteAnimationAdapterEntry(Parcel in) {
+            adapter = in.readParcelable(RemoteAnimationAdapter.class.getClassLoader());
+            activityTypeFilter = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeParcelable(adapter, flags);
+            dest.writeInt(activityTypeFilter);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        private static final Creator<RemoteAnimationAdapterEntry> CREATOR
+                = new Creator<RemoteAnimationAdapterEntry>() {
+
+            @Override
+            public RemoteAnimationAdapterEntry createFromParcel(Parcel in) {
+                return new RemoteAnimationAdapterEntry(in);
+            }
+
+            @Override
+            public RemoteAnimationAdapterEntry[] newArray(int size) {
+                return new RemoteAnimationAdapterEntry[size];
+            }
+        };
+    }
 }
diff --git a/android/view/RemoteAnimationTarget.java b/android/view/RemoteAnimationTarget.java
index c28c389..5b2cc81 100644
--- a/android/view/RemoteAnimationTarget.java
+++ b/android/view/RemoteAnimationTarget.java
@@ -16,13 +16,26 @@
 
 package android.view;
 
+import static android.app.RemoteAnimationTargetProto.CLIP_RECT;
+import static android.app.RemoteAnimationTargetProto.CONTENT_INSETS;
+import static android.app.RemoteAnimationTargetProto.IS_TRANSLUCENT;
+import static android.app.RemoteAnimationTargetProto.LEASH;
+import static android.app.RemoteAnimationTargetProto.MODE;
+import static android.app.RemoteAnimationTargetProto.POSITION;
+import static android.app.RemoteAnimationTargetProto.PREFIX_ORDER_INDEX;
+import static android.app.RemoteAnimationTargetProto.SOURCE_CONTAINER_BOUNDS;
+import static android.app.RemoteAnimationTargetProto.TASK_ID;
+import static android.app.RemoteAnimationTargetProto.WINDOW_CONFIGURATION;
+
 import android.annotation.IntDef;
 import android.app.WindowConfiguration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -79,6 +92,11 @@
     public final Rect clipRect;
 
     /**
+     * The insets of the main app window.
+     */
+    public final Rect contentInsets;
+
+    /**
      * The index of the element in the tree in prefix order. This should be used for z-layering
      * to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
      * happen.
@@ -104,18 +122,25 @@
      */
     public final WindowConfiguration windowConfiguration;
 
+    /**
+     * Whether the task is not presented in Recents UI.
+     */
+    public boolean isNotInRecents;
+
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
-            Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
-            WindowConfiguration windowConfig) {
+            Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
+            Rect sourceContainerBounds, WindowConfiguration windowConfig, boolean isNotInRecents) {
         this.mode = mode;
         this.taskId = taskId;
         this.leash = leash;
         this.isTranslucent = isTranslucent;
         this.clipRect = new Rect(clipRect);
+        this.contentInsets = new Rect(contentInsets);
         this.prefixOrderIndex = prefixOrderIndex;
         this.position = new Point(position);
         this.sourceContainerBounds = new Rect(sourceContainerBounds);
         this.windowConfiguration = windowConfig;
+        this.isNotInRecents = isNotInRecents;
     }
 
     public RemoteAnimationTarget(Parcel in) {
@@ -124,10 +149,12 @@
         leash = in.readParcelable(null);
         isTranslucent = in.readBoolean();
         clipRect = in.readParcelable(null);
+        contentInsets = in.readParcelable(null);
         prefixOrderIndex = in.readInt();
         position = in.readParcelable(null);
         sourceContainerBounds = in.readParcelable(null);
         windowConfiguration = in.readParcelable(null);
+        isNotInRecents = in.readBoolean();
     }
 
     @Override
@@ -142,10 +169,41 @@
         dest.writeParcelable(leash, 0 /* flags */);
         dest.writeBoolean(isTranslucent);
         dest.writeParcelable(clipRect, 0 /* flags */);
+        dest.writeParcelable(contentInsets, 0 /* flags */);
         dest.writeInt(prefixOrderIndex);
         dest.writeParcelable(position, 0 /* flags */);
         dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
         dest.writeParcelable(windowConfiguration, 0 /* flags */);
+        dest.writeBoolean(isNotInRecents);
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("mode="); pw.print(mode);
+        pw.print(" taskId="); pw.print(taskId);
+        pw.print(" isTranslucent="); pw.print(isTranslucent);
+        pw.print(" clipRect="); clipRect.printShortString(pw);
+        pw.print(" contentInsets="); contentInsets.printShortString(pw);
+        pw.print(" prefixOrderIndex="); pw.print(prefixOrderIndex);
+        pw.print(" position="); position.printShortString(pw);
+        pw.print(" sourceContainerBounds="); sourceContainerBounds.printShortString(pw);
+        pw.println();
+        pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
+        pw.print(prefix); pw.print("leash="); pw.println(leash);
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        proto.write(TASK_ID, taskId);
+        proto.write(MODE, mode);
+        leash.writeToProto(proto, LEASH);
+        proto.write(IS_TRANSLUCENT, isTranslucent);
+        clipRect.writeToProto(proto, CLIP_RECT);
+        contentInsets.writeToProto(proto, CONTENT_INSETS);
+        proto.write(PREFIX_ORDER_INDEX, prefixOrderIndex);
+        position.writeToProto(proto, POSITION);
+        sourceContainerBounds.writeToProto(proto, SOURCE_CONTAINER_BOUNDS);
+        windowConfiguration.writeToProto(proto, WINDOW_CONFIGURATION);
+        proto.end(token);
     }
 
     public static final Creator<RemoteAnimationTarget> CREATOR
diff --git a/android/view/RenderNode.java b/android/view/RenderNode.java
index 5070151..7c25fac 100644
--- a/android/view/RenderNode.java
+++ b/android/view/RenderNode.java
@@ -353,9 +353,24 @@
         return nHasShadow(mNativeRenderNode);
     }
 
-    /** setShadowColor */
-    public boolean setShadowColor(int color) {
-        return nSetShadowColor(mNativeRenderNode, color);
+    /** setSpotShadowColor */
+    public boolean setSpotShadowColor(int color) {
+        return nSetSpotShadowColor(mNativeRenderNode, color);
+    }
+
+    /** setAmbientShadowColor */
+    public boolean setAmbientShadowColor(int color) {
+        return nSetAmbientShadowColor(mNativeRenderNode, color);
+    }
+
+    /** getSpotShadowColor */
+    public int getSpotShadowColor() {
+        return nGetSpotShadowColor(mNativeRenderNode);
+    }
+
+    /** getAmbientShadowColor */
+    public int getAmbientShadowColor() {
+        return nGetAmbientShadowColor(mNativeRenderNode);
     }
 
     /**
@@ -672,6 +687,11 @@
         return nIsPivotExplicitlySet(mNativeRenderNode);
     }
 
+    /** lint */
+    public boolean resetPivot() {
+        return nResetPivot(mNativeRenderNode);
+    }
+
     /**
      * Sets the camera distance for the display list. Refer to
      * {@link View#setCameraDistance(float)} for more information on how to
@@ -888,6 +908,8 @@
     @CriticalNative
     private static native boolean nSetPivotX(long renderNode, float pivotX);
     @CriticalNative
+    private static native boolean nResetPivot(long renderNode);
+    @CriticalNative
     private static native boolean nSetLayerType(long renderNode, int layerType);
     @CriticalNative
     private static native boolean nSetLayerPaint(long renderNode, long paint);
@@ -915,7 +937,13 @@
     @CriticalNative
     private static native boolean nHasShadow(long renderNode);
     @CriticalNative
-    private static native boolean nSetShadowColor(long renderNode, int color);
+    private static native boolean nSetSpotShadowColor(long renderNode, int color);
+    @CriticalNative
+    private static native boolean nSetAmbientShadowColor(long renderNode, int color);
+    @CriticalNative
+    private static native int nGetSpotShadowColor(long renderNode);
+    @CriticalNative
+    private static native int nGetAmbientShadowColor(long renderNode);
     @CriticalNative
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
     @CriticalNative
diff --git a/android/view/RenderNodeAnimator.java b/android/view/RenderNodeAnimator.java
index c4a7160..d26a2f6 100644
--- a/android/view/RenderNodeAnimator.java
+++ b/android/view/RenderNodeAnimator.java
@@ -158,7 +158,7 @@
     }
 
     private void applyInterpolator() {
-        if (mInterpolator == null) return;
+        if (mInterpolator == null || mNativePtr == null) return;
 
         long ni;
         if (isNativeInterpolator(mInterpolator)) {
diff --git a/android/view/Surface.java b/android/view/Surface.java
index 8830c90..df81a31 100644
--- a/android/view/Surface.java
+++ b/android/view/Surface.java
@@ -250,6 +250,18 @@
     }
 
     /**
+     * Destroys the HwuiContext without completely
+     * releasing the Surface.
+     * @hide
+     */
+    public void hwuiDestroy() {
+        if (mHwuiContext != null) {
+            mHwuiContext.destroy();
+            mHwuiContext = null;
+        }
+    }
+
+    /**
      * Returns true if this object holds a valid surface.
      *
      * @return True if it holds a physical surface, so lockCanvas() will succeed.
@@ -396,7 +408,44 @@
         synchronized (mLock) {
             checkNotReleasedLocked();
             if (mHwuiContext == null) {
-                mHwuiContext = new HwuiContext();
+                mHwuiContext = new HwuiContext(false);
+            }
+            return mHwuiContext.lockCanvas(
+                    nativeGetWidth(mNativeObject),
+                    nativeGetHeight(mNativeObject));
+        }
+    }
+
+    /**
+     * Gets a {@link Canvas} for drawing into this surface that supports wide color gamut.
+     *
+     * After drawing into the provided {@link Canvas}, the caller must
+     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+     *
+     * Unlike {@link #lockCanvas(Rect)} and {@link #lockHardwareCanvas()},
+     * this will return a hardware-accelerated canvas that supports wide color gamut.
+     * See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported">
+     * unsupported drawing operations</a> for a list of what is and isn't
+     * supported in a hardware-accelerated canvas. It is also required to
+     * fully cover the surface every time {@link #lockHardwareCanvas()} is
+     * called as the buffer is not preserved between frames. Partial updates
+     * are not supported.
+     *
+     * @return A canvas for drawing into the surface.
+     *
+     * @throws IllegalStateException If the canvas cannot be locked.
+     *
+     * @hide
+     */
+    public Canvas lockHardwareWideColorGamutCanvas() {
+        synchronized (mLock) {
+            checkNotReleasedLocked();
+            if (mHwuiContext != null && !mHwuiContext.isWideColorGamut()) {
+                mHwuiContext.destroy();
+                mHwuiContext = null;
+            }
+            if (mHwuiContext == null) {
+                mHwuiContext = new HwuiContext(true);
             }
             return mHwuiContext.lockCanvas(
                     nativeGetWidth(mNativeObject),
@@ -829,11 +878,14 @@
         private final RenderNode mRenderNode;
         private long mHwuiRenderer;
         private DisplayListCanvas mCanvas;
+        private final boolean mIsWideColorGamut;
 
-        HwuiContext() {
+        HwuiContext(boolean isWideColorGamut) {
             mRenderNode = RenderNode.create("HwuiCanvas", null);
             mRenderNode.setClipToBounds(false);
-            mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
+            mIsWideColorGamut = isWideColorGamut;
+            mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject,
+                    isWideColorGamut);
         }
 
         Canvas lockCanvas(int width, int height) {
@@ -864,9 +916,13 @@
                 mHwuiRenderer = 0;
             }
         }
+
+        boolean isWideColorGamut() {
+            return mIsWideColorGamut;
+        }
     }
 
-    private static native long nHwuiCreate(long rootNode, long surface);
+    private static native long nHwuiCreate(long rootNode, long surface, boolean isWideColorGamut);
     private static native void nHwuiSetSurface(long renderer, long surface);
     private static native void nHwuiDraw(long renderer);
     private static native void nHwuiDestroy(long renderer);
diff --git a/android/view/SurfaceControl.java b/android/view/SurfaceControl.java
index bd7f8e5..d4610a5 100644
--- a/android/view/SurfaceControl.java
+++ b/android/view/SurfaceControl.java
@@ -152,6 +152,7 @@
     private static native void nativeSeverChildren(long transactionObj, long nativeObject);
     private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
             int scalingMode);
+    private static native void nativeDestroy(long transactionObj, long nativeObject);
     private static native IBinder nativeGetHandle(long nativeObject);
     private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
 
@@ -352,8 +353,8 @@
         private int mFormat = PixelFormat.OPAQUE;
         private String mName;
         private SurfaceControl mParent;
-        private int mWindowType;
-        private int mOwnerUid;
+        private int mWindowType = -1;
+        private int mOwnerUid = -1;
 
         /**
          * Begin building a SurfaceControl with a given {@link SurfaceSession}.
@@ -565,7 +566,7 @@
      */
     private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
             SurfaceControl parent, int windowType, int ownerUid)
-                    throws OutOfResourcesException {
+                    throws OutOfResourcesException, IllegalArgumentException {
         if (session == null) {
             throw new IllegalArgumentException("session must not be null");
         }
@@ -763,18 +764,14 @@
     }
 
     public void deferTransactionUntil(IBinder handle, long frame) {
-        if (frame > 0) {
-            synchronized(SurfaceControl.class) {
-                sGlobalTransaction.deferTransactionUntil(this, handle, frame);
-            }
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.deferTransactionUntil(this, handle, frame);
         }
     }
 
     public void deferTransactionUntil(Surface barrier, long frame) {
-        if (frame > 0) {
-            synchronized(SurfaceControl.class) {
-                sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
-            }
+        synchronized(SurfaceControl.class) {
+            sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
         }
     }
 
@@ -1479,6 +1476,9 @@
 
         public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
                 long frameNumber) {
+            if (frameNumber < 0) {
+                return this;
+            }
             sc.checkNotReleased();
             nativeDeferTransactionUntil(mNativeObject, sc.mNativeObject, handle, frameNumber);
             return this;
@@ -1486,6 +1486,9 @@
 
         public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
                 long frameNumber) {
+            if (frameNumber < 0) {
+                return this;
+            }
             sc.checkNotReleased();
             nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
                     barrierSurface.mNativeObject, frameNumber);
@@ -1570,6 +1573,16 @@
             return this;
         }
 
+        /**
+         * Same as {@link #destroy()} except this is invoked in a transaction instead of
+         * immediately.
+         */
+        public Transaction destroy(SurfaceControl sc) {
+            sc.checkNotReleased();
+            nativeDestroy(mNativeObject, sc.mNativeObject);
+            return this;
+        }
+
         public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
             if (displayToken == null) {
                 throw new IllegalArgumentException("displayToken must not be null");
diff --git a/android/view/TextureLayer.java b/android/view/TextureLayer.java
new file mode 100644
index 0000000..35a886f
--- /dev/null
+++ b/android/view/TextureLayer.java
@@ -0,0 +1,152 @@
+/*
+ * 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.view;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.SurfaceTexture;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+/**
+ * TextureLayer represents a SurfaceTexture that will be composited by RenderThread into the
+ * frame when drawn in a HW accelerated Canvas. This is backed by a DeferredLayerUpdater on
+ * the native side.
+ *
+ * @hide
+ */
+final class TextureLayer {
+    private ThreadedRenderer mRenderer;
+    private VirtualRefBasePtr mFinalizer;
+
+    private TextureLayer(ThreadedRenderer renderer, long deferredUpdater) {
+        if (renderer == null || deferredUpdater == 0) {
+            throw new IllegalArgumentException("Either hardware renderer: " + renderer
+                    + " or deferredUpdater: " + deferredUpdater + " is invalid");
+        }
+        mRenderer = renderer;
+        mFinalizer = new VirtualRefBasePtr(deferredUpdater);
+    }
+
+    /**
+     * Update the paint used when drawing this layer.
+     *
+     * @param paint The paint used when the layer is drawn into the destination canvas.
+     * @see View#setLayerPaint(android.graphics.Paint)
+     */
+    public void setLayerPaint(@Nullable Paint paint) {
+        nSetLayerPaint(mFinalizer.get(), paint != null ? paint.getNativeInstance() : 0);
+        mRenderer.pushLayerUpdate(this);
+    }
+
+    /**
+     * Indicates whether this layer can be rendered.
+     *
+     * @return True if the layer can be rendered into, false otherwise
+     */
+    public boolean isValid() {
+        return mFinalizer != null && mFinalizer.get() != 0;
+    }
+
+    /**
+     * Destroys resources without waiting for a GC.
+     */
+    public void destroy() {
+        if (!isValid()) {
+            // Already destroyed
+            return;
+        }
+        mRenderer.onLayerDestroyed(this);
+        mRenderer = null;
+        mFinalizer.release();
+        mFinalizer = null;
+    }
+
+    public long getDeferredLayerUpdater() {
+        return mFinalizer.get();
+    }
+
+    /**
+     * Copies this layer into the specified bitmap.
+     *
+     * @param bitmap The bitmap to copy they layer into
+     *
+     * @return True if the copy was successful, false otherwise
+     */
+    public boolean copyInto(Bitmap bitmap) {
+        return mRenderer.copyLayerInto(this, bitmap);
+    }
+
+    /**
+     * Update the layer's properties. Note that after calling this isValid() may
+     * return false if the requested width/height cannot be satisfied
+     *
+     * @param width The new width of this layer
+     * @param height The new height of this layer
+     * @param isOpaque Whether this layer is opaque
+     *
+     * @return true if the layer's properties will change, false if they already
+     *         match the desired values.
+     */
+    public boolean prepare(int width, int height, boolean isOpaque) {
+        return nPrepare(mFinalizer.get(), width, height, isOpaque);
+    }
+
+    /**
+     * Sets an optional transform on this layer.
+     *
+     * @param matrix The transform to apply to the layer.
+     */
+    public void setTransform(Matrix matrix) {
+        nSetTransform(mFinalizer.get(), matrix.native_instance);
+        mRenderer.pushLayerUpdate(this);
+    }
+
+    /**
+     * Indicates that this layer has lost its texture.
+     */
+    public void detachSurfaceTexture() {
+        mRenderer.detachSurfaceTexture(mFinalizer.get());
+    }
+
+    public long getLayerHandle() {
+        return mFinalizer.get();
+    }
+
+    public void setSurfaceTexture(SurfaceTexture surface) {
+        nSetSurfaceTexture(mFinalizer.get(), surface);
+        mRenderer.pushLayerUpdate(this);
+    }
+
+    public void updateSurfaceTexture() {
+        nUpdateSurfaceTexture(mFinalizer.get());
+        mRenderer.pushLayerUpdate(this);
+    }
+
+    static TextureLayer adoptTextureLayer(ThreadedRenderer renderer, long layer) {
+        return new TextureLayer(renderer, layer);
+    }
+
+    private static native boolean nPrepare(long layerUpdater, int width, int height,
+            boolean isOpaque);
+    private static native void nSetLayerPaint(long layerUpdater, long paint);
+    private static native void nSetTransform(long layerUpdater, long matrix);
+    private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface);
+    private static native void nUpdateSurfaceTexture(long layerUpdater);
+}
diff --git a/android/view/TextureView.java b/android/view/TextureView.java
index 25dce99..3717940 100644
--- a/android/view/TextureView.java
+++ b/android/view/TextureView.java
@@ -106,7 +106,7 @@
 public class TextureView extends View {
     private static final String LOG_TAG = "TextureView";
 
-    private HardwareLayer mLayer;
+    private TextureLayer mLayer;
     private SurfaceTexture mSurface;
     private SurfaceTextureListener mListener;
     private boolean mHadSurface;
@@ -336,13 +336,13 @@
         if (canvas.isHardwareAccelerated()) {
             DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
 
-            HardwareLayer layer = getHardwareLayer();
+            TextureLayer layer = getTextureLayer();
             if (layer != null) {
                 applyUpdate();
                 applyTransformMatrix();
 
                 mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
-                displayListCanvas.drawHardwareLayer(layer);
+                displayListCanvas.drawTextureLayer(layer);
             }
         }
     }
@@ -369,7 +369,7 @@
         }
     }
 
-    HardwareLayer getHardwareLayer() {
+    TextureLayer getTextureLayer() {
         if (mLayer == null) {
             if (mAttachInfo == null || mAttachInfo.mThreadedRenderer == null) {
                 return null;
@@ -602,7 +602,7 @@
             // the layer here thanks to the validate() call at the beginning of
             // this method
             if (mLayer == null && mUpdateSurface) {
-                getHardwareLayer();
+                getTextureLayer();
             }
 
             if (mLayer != null) {
diff --git a/android/view/ThreadedRenderer.java b/android/view/ThreadedRenderer.java
index 8b730f2..5eb7e9c 100644
--- a/android/view/ThreadedRenderer.java
+++ b/android/view/ThreadedRenderer.java
@@ -34,6 +34,7 @@
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
+import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
 import com.android.internal.util.VirtualRefBasePtr;
@@ -166,18 +167,6 @@
     public static final String OVERDRAW_PROPERTY_SHOW = "show";
 
     /**
-     * Defines the rendering pipeline to be used by the ThreadedRenderer.
-     *
-     * Possible values:
-     * "opengl", will use the existing OpenGL renderer
-     * "skiagl", will use Skia's OpenGL renderer
-     * "skiavk", will use Skia's Vulkan renderer
-     *
-     * @hide
-     */
-    public static final String DEBUG_RENDERER_PROPERTY = "debug.hwui.renderer";
-
-    /**
      * Turn on to debug non-rectangular clip operations.
      *
      * Possible values:
@@ -343,6 +332,7 @@
 
     private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
     private static final int FLAG_DUMP_RESET        = 1 << 1;
+    private static final int FLAG_DUMP_ALL          = FLAG_DUMP_FRAMESTATS;
 
     @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = {
             FLAG_DUMP_FRAMESTATS,
@@ -648,7 +638,10 @@
      */
     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
         pw.flush();
-        int flags = 0;
+        // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything.
+        // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only
+        // dump the summary information
+        int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0;
         for (int i = 0; i < args.length; i++) {
             switch (args[i]) {
                 case "framestats":
@@ -657,6 +650,9 @@
                 case "reset":
                     flags |= FLAG_DUMP_RESET;
                     break;
+                case "-a": // magic option passed when dumping a bugreport.
+                    flags = FLAG_DUMP_ALL;
+                    break;
             }
         }
         nDumpProfileInfo(mNativeProxy, fd, flags);
@@ -838,9 +834,9 @@
      *
      * @return A hardware layer
      */
-    HardwareLayer createTextureLayer() {
+    TextureLayer createTextureLayer() {
         long layer = nCreateTextureLayer(mNativeProxy);
-        return HardwareLayer.adoptTextureLayer(this, layer);
+        return TextureLayer.adoptTextureLayer(this, layer);
     }
 
 
@@ -849,7 +845,7 @@
     }
 
 
-    boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
+    boolean copyLayerInto(final TextureLayer layer, final Bitmap bitmap) {
         return nCopyLayerInto(mNativeProxy,
                 layer.getDeferredLayerUpdater(), bitmap);
     }
@@ -860,7 +856,7 @@
      *
      * @param layer The hardware layer that needs an update
      */
-    void pushLayerUpdate(HardwareLayer layer) {
+    void pushLayerUpdate(TextureLayer layer) {
         nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
@@ -868,7 +864,7 @@
      * Tells the HardwareRenderer that the layer is destroyed. The renderer
      * should remove the layer from any update queues.
      */
-    void onLayerDestroyed(HardwareLayer layer) {
+    void onLayerDestroyed(TextureLayer layer) {
         nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
@@ -938,6 +934,20 @@
         nSetHighContrastText(highContrastText);
     }
 
+    /**
+     * If set RenderThread will avoid doing any IPC using instead a fake vsync & DisplayInfo source
+     */
+    public static void setIsolatedProcess(boolean isIsolated) {
+        nSetIsolatedProcess(isIsolated);
+    }
+
+    /**
+     * If set extra graphics debugging abilities will be enabled such as dumping skp
+     */
+    public static void setDebuggingEnabled(boolean enable) {
+        nSetDebuggingEnabled(enable);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -948,6 +958,107 @@
         }
     }
 
+    /**
+     * Basic synchronous renderer. Currently only used to render the Magnifier, so use with care.
+     * TODO: deduplicate against ThreadedRenderer.
+     *
+     * @hide
+     */
+    public static class SimpleRenderer {
+        private final RenderNode mRootNode;
+        private long mNativeProxy;
+        private final float mLightY, mLightZ;
+        private Surface mSurface;
+        private final FrameInfo mFrameInfo = new FrameInfo();
+
+        public SimpleRenderer(final Context context, final String name, final Surface surface) {
+            final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
+            mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
+            mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
+            final float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
+            final int ambientShadowAlpha =
+                    (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
+            final int spotShadowAlpha =
+                    (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
+            a.recycle();
+
+            final long rootNodePtr = nCreateRootRenderNode();
+            mRootNode = RenderNode.adopt(rootNodePtr);
+            mRootNode.setClipToBounds(false);
+            mNativeProxy = nCreateProxy(true /* translucent */, rootNodePtr);
+            nSetName(mNativeProxy, name);
+
+            ProcessInitializer.sInstance.init(context, mNativeProxy);
+            nLoadSystemProperties(mNativeProxy);
+
+            nSetup(mNativeProxy, lightRadius, ambientShadowAlpha, spotShadowAlpha);
+
+            mSurface = surface;
+            nUpdateSurface(mNativeProxy, surface);
+        }
+
+        /**
+         * Set the light center.
+         */
+        public void setLightCenter(final Display display,
+                final int windowLeft, final int windowTop) {
+            // Adjust light position for window offsets.
+            final Point displaySize = new Point();
+            display.getRealSize(displaySize);
+            final float lightX = displaySize.x / 2f - windowLeft;
+            final float lightY = mLightY - windowTop;
+
+            nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
+        }
+
+        public RenderNode getRootNode() {
+            return mRootNode;
+        }
+
+        /**
+         * Draw the surface.
+         */
+        public void draw(final FrameDrawingCallback callback) {
+            final long vsync = AnimationUtils.currentAnimationTimeMillis() * 1000000L;
+            mFrameInfo.setVsync(vsync, vsync);
+            mFrameInfo.addFlags(1 << 2 /* VSYNC */);
+            if (callback != null) {
+                nSetFrameCallback(mNativeProxy, callback);
+            }
+            nSyncAndDrawFrame(mNativeProxy, mFrameInfo.mFrameInfo, mFrameInfo.mFrameInfo.length);
+        }
+
+        /**
+         * Destroy the renderer.
+         */
+        public void destroy() {
+            mSurface = null;
+            nDestroy(mNativeProxy, mRootNode.mNativeRenderNode);
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                nDeleteProxy(mNativeProxy);
+                mNativeProxy = 0;
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+
+    /**
+     * Interface used to receive callbacks when a frame is being drawn.
+     */
+    public interface FrameDrawingCallback {
+        /**
+         * Invoked during a frame drawing.
+         *
+         * @param frame The id of the frame being drawn.
+         */
+        void onFrameDraw(long frame);
+    }
+
     private static class ProcessInitializer {
         static ProcessInitializer sInstance = new ProcessInitializer();
 
@@ -970,7 +1081,10 @@
             mAppContext = context.getApplicationContext();
 
             initSched(renderProxy);
-            initGraphicsStats();
+
+            if (mAppContext != null) {
+                initGraphicsStats();
+            }
         }
 
         private void initSched(long renderProxy) {
@@ -1085,6 +1199,7 @@
     private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
     private static native void nSetContentDrawBounds(long nativeProxy, int left,
              int top, int right, int bottom);
+    private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
 
     private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
     private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
@@ -1096,4 +1211,6 @@
     private static native void nSetHighContrastText(boolean enabled);
     // For temporary experimentation b/66945974
     private static native void nHackySetRTAnimationsEnabled(boolean enabled);
+    private static native void nSetDebuggingEnabled(boolean enabled);
+    private static native void nSetIsolatedProcess(boolean enabled);
 }
diff --git a/android/view/TouchDelegate.java b/android/view/TouchDelegate.java
index dc50fa1..d6c43e8 100644
--- a/android/view/TouchDelegate.java
+++ b/android/view/TouchDelegate.java
@@ -105,11 +105,13 @@
         boolean hit = true;
         boolean handled = false;
 
-        switch (event.getAction()) {
+        switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mDelegateTargeted = mBounds.contains(x, y);
                 sendToDelegate = mDelegateTargeted;
                 break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+            case MotionEvent.ACTION_POINTER_UP:
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_MOVE:
                 sendToDelegate = mDelegateTargeted;
diff --git a/android/view/View.java b/android/view/View.java
index 3d6a6fe..97e11b1 100644
--- a/android/view/View.java
+++ b/android/view/View.java
@@ -75,6 +75,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -726,6 +727,8 @@
  * @attr ref android.R.styleable#View_nextFocusRight
  * @attr ref android.R.styleable#View_nextFocusUp
  * @attr ref android.R.styleable#View_onClick
+ * @attr ref android.R.styleable#View_outlineSpotShadowColor
+ * @attr ref android.R.styleable#View_outlineAmbientShadowColor
  * @attr ref android.R.styleable#View_padding
  * @attr ref android.R.styleable#View_paddingHorizontal
  * @attr ref android.R.styleable#View_paddingVertical
@@ -904,6 +907,13 @@
      */
     private static boolean sThrowOnInvalidFloatProperties;
 
+    /**
+     * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow.
+     * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface
+     * instead.
+     */
+    private static boolean sAcceptZeroSizeDragShadow;
+
     /** @hide */
     @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
     @Retention(RetentionPolicy.SOURCE)
@@ -2943,6 +2953,9 @@
      *       1                           PFLAG3_NO_REVEAL_ON_FOCUS
      *      1                            PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT
      *     1                             PFLAG3_SCREEN_READER_FOCUSABLE
+     *    1                              PFLAG3_AGGREGATED_VISIBLE
+     *   1                               PFLAG3_AUTOFILLID_EXPLICITLY_SET
+     *  1                                available
      * |-------|-------|-------|-------|
      */
 
@@ -3233,6 +3246,12 @@
      */
     private static final int PFLAG3_AGGREGATED_VISIBLE = 0x20000000;
 
+    /**
+     * Used to indicate that {@link #mAutofillId} was explicitly set through
+     * {@link #setAutofillId(AutofillId)}.
+     */
+    private static final int PFLAG3_AUTOFILLID_EXPLICITLY_SET = 0x40000000;
+
     /* End of masks for mPrivateFlags3 */
 
     /**
@@ -3975,6 +3994,7 @@
     /**
      * Current clip bounds. to which all drawing of this view are constrained.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     Rect mClipBounds = null;
 
     private boolean mLastIsOpaque;
@@ -4300,7 +4320,7 @@
 
         OnCapturedPointerListener mOnCapturedPointerListener;
 
-        private ArrayList<OnKeyFallbackListener> mKeyFallbackListeners;
+        private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
     }
 
     ListenerInfo mListenerInfo;
@@ -4442,6 +4462,7 @@
     private CheckForLongPress mPendingCheckForLongPress;
     private CheckForTap mPendingCheckForTap = null;
     private PerformClick mPerformClick;
+    private SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
 
     private UnsetPressedState mUnsetPressedState;
 
@@ -4794,6 +4815,7 @@
 
             Canvas.sCompatibilityRestore = targetSdkVersion < Build.VERSION_CODES.M;
             Canvas.sCompatibilitySetBitmap = targetSdkVersion < Build.VERSION_CODES.O;
+            Canvas.setCompatibilityVersion(targetSdkVersion);
 
             // In M and newer, our widgets can pass a "hint" value in the size
             // for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers
@@ -4836,6 +4858,8 @@
 
             sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
 
+            sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+
             sCompatibilityDone = true;
         }
     }
@@ -5445,6 +5469,12 @@
                         setAccessibilityPaneTitle(a.getString(attr));
                     }
                     break;
+                case R.styleable.View_outlineSpotShadowColor:
+                    setOutlineSpotShadowColor(a.getColor(attr, Color.BLACK));
+                    break;
+                case R.styleable.View_outlineAmbientShadowColor:
+                    setOutlineAmbientShadowColor(a.getColor(attr, Color.BLACK));
+                    break;
             }
         }
 
@@ -6516,7 +6546,7 @@
             } finally {
                 // Set it to already called so it's not called twice when called by
                 // performClickInternal()
-                mPrivateFlags |= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
+                mPrivateFlags &= ~PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK;
             }
         }
     }
@@ -7201,7 +7231,7 @@
         if (gainFocus) {
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
         } else {
-            notifyAccessibilityStateChanged(
+            notifyViewAccessibilityStateChangedIfNeeded(
                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
 
@@ -7251,7 +7281,7 @@
                         // becomes true where it should issue notifyViewEntered().
                         afm.notifyViewEntered(this);
                     }
-                } else if (!isFocused()) {
+                } else if (!enter && !isFocused()) {
                     afm.notifyViewExited(this);
                 }
             }
@@ -7259,19 +7289,22 @@
     }
 
     /**
-     * If this view is a visually distinct portion of a window, for example the content view of
-     * a fragment that is replaced, it is considered a pane for accessibility purposes. In order
-     * for accessibility services to understand the views role, and to announce its title as
-     * appropriate, such views should have pane titles.
+     * Visually distinct portion of a window with window-like semantics are considered panes for
+     * accessibility purposes. One example is the content view of a fragment that is replaced.
+     * In order for accessibility services to understand a pane's window-like behavior, panes
+     * should have descriptive titles. Views with pane titles produce {@link AccessibilityEvent}s
+     * when they appear, disappear, or change title.
      *
-     * @param accessibilityPaneTitle The pane's title.
+     * @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this
+     *                               View is not a pane.
      *
      * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)}
      */
-    public void setAccessibilityPaneTitle(CharSequence accessibilityPaneTitle) {
+    public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) {
         if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
             mAccessibilityPaneTitle = accessibilityPaneTitle;
-            notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
         }
     }
 
@@ -7282,10 +7315,14 @@
      *
      * {@see #setAccessibilityPaneTitle}.
      */
-    public CharSequence getAccessibilityPaneTitle() {
+    @Nullable public CharSequence getAccessibilityPaneTitle() {
         return mAccessibilityPaneTitle;
     }
 
+    private boolean isAccessibilityPane() {
+        return mAccessibilityPaneTitle != null;
+    }
+
     /**
      * Sends an accessibility event of the given type. If accessibility is
      * not enabled this method has no effect. The default implementation calls
@@ -7849,6 +7886,7 @@
                 structure.setAutofillHints(getAutofillHints());
                 structure.setAutofillValue(getAutofillValue());
             }
+            structure.setImportantForAutofill(getImportantForAutofill());
         }
 
         int ignoredParentLeft = 0;
@@ -7930,12 +7968,26 @@
      * optimal implementation providing this data.
      */
     public void onProvideVirtualStructure(ViewStructure structure) {
-        AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        onProvideVirtualStructureCompat(structure, false);
+    }
+
+    /**
+     * Fallback implementation to populate a ViewStructure from accessibility state.
+     *
+     * @param structure The structure to populate.
+     * @param forAutofill Whether the structure is needed for autofill.
+     */
+    private void onProvideVirtualStructureCompat(ViewStructure structure, boolean forAutofill) {
+        final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
         if (provider != null) {
-            AccessibilityNodeInfo info = createAccessibilityNodeInfo();
+            if (android.view.autofill.Helper.sVerbose && forAutofill) {
+                Log.v(VIEW_LOG_TAG, "onProvideVirtualStructureCompat() for " + this);
+            }
+
+            final AccessibilityNodeInfo info = createAccessibilityNodeInfo();
             structure.setChildCount(1);
-            ViewStructure root = structure.newChild(0);
-            populateVirtualStructure(root, provider, info);
+            final ViewStructure root = structure.newChild(0);
+            populateVirtualStructure(root, provider, info, forAutofill);
             info.recycle();
         }
     }
@@ -7964,12 +8016,18 @@
      *   <li>Call {@link android.view.autofill.AutofillManager#notifyViewEntered(View, int, Rect)}
      *       and/or {@link android.view.autofill.AutofillManager#notifyViewExited(View, int)}
      *       when the focused virtual child changed.
+     *   <li>Override {@link #isVisibleToUserForAutofill(int)} to allow the platform to query
+     *       whether a given virtual view is visible to the user in order to support triggering
+     *       save when all views of interest go away.
      *   <li>Call
      *    {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)}
      *       when the value of a virtual child changed.
      *   <li>Call {@link
      *    android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)}
      *       when the visibility of a virtual child changed.
+     *   <li>Call
+     *    {@link android.view.autofill.AutofillManager#notifyViewClicked(View, int)} when a virtual
+     *       child is clicked.
      *   <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure
      *       changed and the current context should be committed (for example, when the user tapped
      *       a {@code SUBMIT} button in an HTML page).
@@ -7999,6 +8057,9 @@
      * @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
      */
     public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
+        if (mContext.isAutofillCompatibilityEnabled()) {
+            onProvideVirtualStructureCompat(structure, true);
+        }
     }
 
     /**
@@ -8076,10 +8137,34 @@
      * @attr ref android.R.styleable#Theme_autofilledHighlight
      */
     public void autofill(@NonNull @SuppressWarnings("unused") SparseArray<AutofillValue> values) {
+        if (!mContext.isAutofillCompatibilityEnabled()) {
+            return;
+        }
+        final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        if (provider == null) {
+            return;
+        }
+        final int valueCount = values.size();
+        for (int i = 0; i < valueCount; i++) {
+            final AutofillValue value = values.valueAt(i);
+            if (value.isText()) {
+                final int virtualId = values.keyAt(i);
+                final CharSequence text = value.getTextValue();
+                final Bundle arguments = new Bundle();
+                arguments.putCharSequence(
+                        AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
+                provider.performAction(virtualId, AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
+            }
+        }
     }
 
     /**
-     * Gets the unique identifier of this view in the screen, for autofill purposes.
+     * Gets the unique, logical identifier of this view in the activity, for autofill purposes.
+     *
+     * <p>The autofill id is created on demand, unless it is explicitly set by
+     * {@link #setAutofillId(AutofillId)}.
+     *
+     * <p>See {@link #setAutofillId(AutofillId)} for more info.
      *
      * @return The View's autofill id.
      */
@@ -8093,6 +8178,73 @@
     }
 
     /**
+     * Sets the unique, logical identifier of this view in the activity, for autofill purposes.
+     *
+     * <p>The autofill id is created on demand, and this method should only be called when a view is
+     * reused after {@link #dispatchProvideAutofillStructure(ViewStructure, int)} is called, as
+     * that method creates a snapshot of the view that is passed along to the autofill service.
+     *
+     * <p>This method is typically used when view subtrees are recycled to represent different
+     * content* &mdash;in this case, the autofill id can be saved before the view content is swapped
+     * out, and restored later when it's swapped back in. For example:
+     *
+     * <pre>
+     * EditText reusableView = ...;
+     * ViewGroup parentView = ...;
+     * AutofillManager afm = ...;
+     *
+     * // Swap out the view and change its contents
+     * AutofillId oldId = reusableView.getAutofillId();
+     * CharSequence oldText = reusableView.getText();
+     * parentView.removeView(reusableView);
+     * AutofillId newId = afm.getNextAutofillId();
+     * reusableView.setText("New I am");
+     * reusableView.setAutofillId(newId);
+     * parentView.addView(reusableView);
+     *
+     * // Later, swap the old content back in
+     * parentView.removeView(reusableView);
+     * reusableView.setAutofillId(oldId);
+     * reusableView.setText(oldText);
+     * parentView.addView(reusableView);
+     * </pre>
+     *
+     * @param id an autofill ID that is unique in the {@link android.app.Activity} hosting the view,
+     * or {@code null} to reset it. Usually it's an id previously allocated to another view (and
+     * obtained through {@link #getAutofillId()}), or a new value obtained through
+     * {@link AutofillManager#getNextAutofillId()}.
+     *
+     * @throws IllegalStateException if the view is already {@link #isAttachedToWindow() attached to
+     * a window}.
+     *
+     * @throws IllegalArgumentException if the id is an autofill id associated with a virtual view.
+     */
+    public void setAutofillId(@Nullable AutofillId id) {
+        // TODO(b/37566627): add unit / CTS test for all possible combinations below
+        if (android.view.autofill.Helper.sVerbose) {
+            Log.v(VIEW_LOG_TAG, "setAutofill(): from " + mAutofillId + " to " + id);
+        }
+        if (isAttachedToWindow()) {
+            throw new IllegalStateException("Cannot set autofill id when view is attached");
+        }
+        if (id != null && id.isVirtual()) {
+            throw new IllegalStateException("Cannot set autofill id assigned to virtual views");
+        }
+        if (id == null && (mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) == 0) {
+            // Ignore reset because it was never explicitly set before.
+            return;
+        }
+        mAutofillId = id;
+        if (id != null) {
+            mAutofillViewId = id.getViewId();
+            mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET;
+        } else {
+            mAutofillViewId = NO_ID;
+            mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
+        }
+    }
+
+    /**
      * Describes the autofill type of this view, so an
      * {@link android.service.autofill.AutofillService} can create the proper {@link AutofillValue}
      * when autofilling the view.
@@ -8309,6 +8461,11 @@
             }
         }
 
+        // If the app developer explicitly set hints for it, it's important.
+        if (getAutofillHints() != null) {
+            return true;
+        }
+
         // Otherwise, assume it's not important...
         return false;
     }
@@ -8329,9 +8486,10 @@
     }
 
     private void populateVirtualStructure(ViewStructure structure,
-            AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
+            AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
+            boolean forAutofill) {
         structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
-                null, null, null);
+                null, null, info.getViewIdResourceName());
         Rect rect = structure.getTempRect();
         info.getBoundsInParent(rect);
         structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
@@ -8364,21 +8522,54 @@
         if (info.isContextClickable()) {
             structure.setContextClickable(true);
         }
+        if (forAutofill) {
+            structure.setAutofillId(new AutofillId(getAutofillId(),
+                    AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
+        }
         CharSequence cname = info.getClassName();
         structure.setClassName(cname != null ? cname.toString() : null);
         structure.setContentDescription(info.getContentDescription());
-        if ((info.getText() != null || info.getError() != null)) {
-            structure.setText(info.getText(), info.getTextSelectionStart(),
-                    info.getTextSelectionEnd());
+        if (forAutofill) {
+            final int maxTextLength = info.getMaxTextLength();
+            if (maxTextLength != -1) {
+                structure.setMaxTextLength(maxTextLength);
+            }
+            structure.setHint(info.getHintText());
+        }
+        CharSequence text = info.getText();
+        boolean hasText = text != null || info.getError() != null;
+        if (hasText) {
+            structure.setText(text, info.getTextSelectionStart(), info.getTextSelectionEnd());
+        }
+        if (forAutofill) {
+            if (info.isEditable()) {
+                structure.setDataIsSensitive(true);
+                if (hasText) {
+                    structure.setAutofillType(AUTOFILL_TYPE_TEXT);
+                    structure.setAutofillValue(AutofillValue.forText(text));
+                }
+                int inputType = info.getInputType();
+                if (inputType == 0 && info.isPassword()) {
+                    inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
+                }
+                structure.setInputType(inputType);
+            } else {
+                structure.setDataIsSensitive(false);
+            }
         }
         final int NCHILDREN = info.getChildCount();
         if (NCHILDREN > 0) {
             structure.setChildCount(NCHILDREN);
             for (int i=0; i<NCHILDREN; i++) {
+                if (AccessibilityNodeInfo.getVirtualDescendantId(info.getChildNodeIds().get(i))
+                        == AccessibilityNodeProvider.HOST_VIEW_ID) {
+                    Log.e(VIEW_LOG_TAG, "Virtual view pointing to its host. Ignoring");
+                    continue;
+                }
                 AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo(
                         AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
                 ViewStructure child = structure.newChild(i);
-                populateVirtualStructure(child, provider, cinfo);
+                populateVirtualStructure(child, provider, cinfo, forAutofill);
                 cinfo.recycle();
             }
         }
@@ -8707,6 +8898,31 @@
     }
 
     /**
+     * Computes whether this virtual autofill view is visible to the user.
+     *
+     * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy
+     * view must override it.
+     *
+     * @return Whether the view is visible on the screen.
+     */
+    public boolean isVisibleToUserForAutofill(int virtualId) {
+        if (mContext.isAutofillCompatibilityEnabled()) {
+            final AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+            if (provider != null) {
+                final AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(virtualId);
+                if (node != null) {
+                    return node.isVisibleToUser();
+                }
+                // if node is null, assume it's not visible anymore
+            } else {
+                Log.w(VIEW_LOG_TAG, "isVisibleToUserForAutofill(" + virtualId + "): no provider");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Computes whether this view is visible to the user. Such a view is
      * attached, visible, all its predecessors are visible, it is not clipped
      * entirely by its predecessors, and has an alpha greater than zero.
@@ -8715,7 +8931,7 @@
      *
      * @hide
      */
-    protected boolean isVisibleToUser() {
+    public boolean isVisibleToUser() {
         return isVisibleToUser(null);
     }
 
@@ -8924,9 +9140,9 @@
         final boolean nonEmptyDesc = contentDescription != null && contentDescription.length() > 0;
         if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         } else {
-            notifyAccessibilityStateChanged(
+            notifyViewAccessibilityStateChangedIfNeeded(
                     AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
         }
     }
@@ -8959,7 +9175,8 @@
             return;
         }
         mAccessibilityTraversalBeforeId = beforeId;
-        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyViewAccessibilityStateChangedIfNeeded(
+                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
@@ -9002,7 +9219,8 @@
             return;
         }
         mAccessibilityTraversalAfterId = afterId;
-        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyViewAccessibilityStateChangedIfNeeded(
+                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
@@ -9044,7 +9262,8 @@
                 && mID == View.NO_ID) {
             mID = generateViewId();
         }
-        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyViewAccessibilityStateChangedIfNeeded(
+                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     /**
@@ -10544,7 +10763,8 @@
 
         if (pflags3 != mPrivateFlags3) {
             mPrivateFlags3 = pflags3;
-            notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
 
@@ -11374,7 +11594,8 @@
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
             mPrivateFlags2 |= (mode << PFLAG2_ACCESSIBILITY_LIVE_REGION_SHIFT)
                     & PFLAG2_ACCESSIBILITY_LIVE_REGION_MASK;
-            notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
 
@@ -11431,9 +11652,10 @@
             mPrivateFlags2 |= (mode << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT)
                     & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK;
             if (!maySkipNotify || oldIncludeForAccessibility != includeForAccessibility()) {
-                notifyAccessibilitySubtreeChanged();
+                notifySubtreeAccessibilityStateChangedIfNeeded();
             } else {
-                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -11519,7 +11741,7 @@
         return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility()
                 || hasListenersForAccessibility() || getAccessibilityNodeProvider() != null
                 || getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE
-                || (mAccessibilityPaneTitle != null);
+                || isAccessibilityPane();
     }
 
     /**
@@ -11609,8 +11831,51 @@
      *
      * @hide
      */
-    public void notifyAccessibilityStateChanged(int changeType) {
-        notifyAccessibilityStateChanged(this, changeType);
+    public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) {
+        if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
+            return;
+        }
+
+        // Changes to views with a pane title count as window state changes, as the pane title
+        // marks them as significant parts of the UI.
+        if ((changeType != AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE)
+                && isAccessibilityPane()) {
+            // If the pane isn't visible, content changed events are sufficient unless we're
+            // reporting that the view just disappeared
+            if ((getVisibility() == VISIBLE)
+                    || (changeType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)) {
+                final AccessibilityEvent event = AccessibilityEvent.obtain();
+                event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+                event.setContentChangeTypes(changeType);
+                event.setSource(this);
+                onPopulateAccessibilityEvent(event);
+                if (mParent != null) {
+                    try {
+                        mParent.requestSendAccessibilityEvent(this, event);
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
+                                + " does not fully implement ViewParent", e);
+                    }
+                }
+                return;
+            }
+        }
+
+        // If this is a live region, we should send a subtree change event
+        // from this view immediately. Otherwise, we can let it propagate up.
+        if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
+            final AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+            event.setContentChangeTypes(changeType);
+            sendAccessibilityEventUnchecked(event);
+        } else if (mParent != null) {
+            try {
+                mParent.notifySubtreeAccessibilityStateChanged(this, this, changeType);
+            } catch (AbstractMethodError e) {
+                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                        " does not fully implement ViewParent", e);
+            }
+        }
     }
 
     /**
@@ -11624,42 +11889,23 @@
      *
      * @hide
      */
-    public void notifyAccessibilitySubtreeChanged() {
-        if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
-            mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
-            notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
-        }
-    }
-
-    void notifyAccessibilityStateChanged(View source, int changeType) {
+    public void notifySubtreeAccessibilityStateChangedIfNeeded() {
         if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
             return;
         }
-        // Changes to views with a pane title count as window state changes, as the pane title
-        // marks them as significant parts of the UI.
-        if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
-            final AccessibilityEvent event = AccessibilityEvent.obtain();
-            event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-            event.setContentChangeTypes(changeType);
-            onPopulateAccessibilityEvent(event);
+
+        if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
+            mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
             if (mParent != null) {
                 try {
-                    mParent.requestSendAccessibilityEvent(this, event);
+                    mParent.notifySubtreeAccessibilityStateChanged(
+                            this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
                 } catch (AbstractMethodError e) {
-                    Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
-                            + " does not fully implement ViewParent", e);
+                    Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                            " does not fully implement ViewParent", e);
                 }
             }
         }
-
-        if (mParent != null) {
-            try {
-                mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
-            } catch (AbstractMethodError e) {
-                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
-                        + " does not fully implement ViewParent", e);
-            }
-        }
     }
 
     /**
@@ -11679,10 +11925,8 @@
     /**
      * Reset the flag indicating the accessibility state of the subtree rooted
      * at this view changed.
-     *
-     * @hide
      */
-    public void resetSubtreeAccessibilityStateChanged() {
+    void resetSubtreeAccessibilityStateChanged() {
         mPrivateFlags2 &= ~PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
     }
 
@@ -11843,7 +12087,8 @@
                         || getAccessibilitySelectionEnd() != end)
                         && (start == end)) {
                     setAccessibilitySelection(start, end);
-                    notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+                    notifyViewAccessibilityStateChangedIfNeeded(
+                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                     return true;
                 }
             } break;
@@ -12643,7 +12888,7 @@
         }
         if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
             if (isVisible != oldVisible) {
-                notifyAccessibilityStateChanged(isVisible
+                notifyViewAccessibilityStateChangedIfNeeded(isVisible
                         ? AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED
                         : AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
             }
@@ -13672,11 +13917,15 @@
         mAttachInfo.mUnbufferedDispatchRequested = true;
     }
 
+    private boolean hasSize() {
+        return (mBottom > mTop) && (mRight > mLeft);
+    }
+
     private boolean canTakeFocus() {
         return ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
                 && ((mViewFlags & FOCUSABLE) == FOCUSABLE)
                 && ((mViewFlags & ENABLED_MASK) == ENABLED)
-                && (sCanFocusZeroSized || !isLayoutValid() || (mBottom > mTop) && (mRight > mLeft));
+                && (sCanFocusZeroSized || !isLayoutValid() || hasSize());
     }
 
     /**
@@ -13737,7 +13986,7 @@
                             || focusableChangedByAuto == 0
                             || viewRootImpl == null
                             || viewRootImpl.mThread == Thread.currentThread()) {
-                        shouldNotifyFocusableAvailable = true;
+                        shouldNotifyFocusableAvailable = canTakeFocus();
                     }
                 }
             }
@@ -13756,11 +14005,10 @@
 
                 needGlobalAttributesUpdate(true);
 
-                // a view becoming visible is worth notifying the parent
-                // about in case nothing has focus.  even if this specific view
-                // isn't focusable, it may contain something that is, so let
-                // the root view try to give this focus if nothing else does.
-                shouldNotifyFocusableAvailable = true;
+                // a view becoming visible is worth notifying the parent about in case nothing has
+                // focus. Even if this specific view isn't focusable, it may contain something that
+                // is, so let the root view try to give this focus if nothing else does.
+                shouldNotifyFocusableAvailable = hasSize();
             }
         }
 
@@ -13769,16 +14017,14 @@
                 // a view becoming enabled should notify the parent as long as the view is also
                 // visible and the parent wasn't already notified by becoming visible during this
                 // setFlags invocation.
-                shouldNotifyFocusableAvailable = true;
+                shouldNotifyFocusableAvailable = canTakeFocus();
             } else {
-                if (hasFocus()) clearFocus();
+                if (isFocused()) clearFocus();
             }
         }
 
-        if (shouldNotifyFocusableAvailable) {
-            if (mParent != null && canTakeFocus()) {
-                mParent.focusableViewAvailable(this);
-            }
+        if (shouldNotifyFocusableAvailable && mParent != null) {
+            mParent.focusableViewAvailable(this);
         }
 
         /* Check if the GONE bit has changed */
@@ -13860,7 +14106,7 @@
                         ((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown())) {
                     dispatchVisibilityAggregated(newVisibility == VISIBLE);
                 }
-                notifyAccessibilitySubtreeChanged();
+                notifySubtreeAccessibilityStateChangedIfNeeded();
             }
         }
 
@@ -13902,16 +14148,23 @@
         }
 
         if (accessibilityEnabled) {
+            // If we're an accessibility pane and the visibility changed, we already have sent
+            // a state change, so we really don't need to report other changes.
+            if (isAccessibilityPane()) {
+                changed &= ~VISIBILITY_MASK;
+            }
             if ((changed & FOCUSABLE) != 0 || (changed & VISIBILITY_MASK) != 0
                     || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
                     || (changed & CONTEXT_CLICKABLE) != 0) {
                 if (oldIncludeForAccessibility != includeForAccessibility()) {
-                    notifyAccessibilitySubtreeChanged();
+                    notifySubtreeAccessibilityStateChangedIfNeeded();
                 } else {
-                    notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+                    notifyViewAccessibilityStateChangedIfNeeded(
+                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
                 }
             } else if ((changed & ENABLED_MASK) != 0) {
-                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -13945,13 +14198,10 @@
      * @param oldt Previous vertical scroll origin.
      */
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        notifyAccessibilitySubtreeChanged();
+        notifySubtreeAccessibilityStateChangedIfNeeded();
 
-        ViewRootImpl root = getViewRootImpl();
-        if (root != null) {
-            root.getAccessibilityState()
-                    .getSendViewScrolledAccessibilityEvent()
-                    .post(this, /* dx */ l - oldl, /* dy */ t - oldt);
+        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            postSendViewScrolledAccessibilityEventCallback(l - oldl, t - oldt);
         }
 
         mBackgroundSizeChanged = true;
@@ -14094,7 +14344,7 @@
     }
 
     /**
-     * Return the width of the your view.
+     * Return the width of your view.
      *
      * @return The width of your view, in pixels.
      */
@@ -14347,7 +14597,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -14391,7 +14641,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -14435,7 +14685,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -14472,7 +14722,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -14509,7 +14759,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -14597,6 +14847,28 @@
     }
 
     /**
+     * Returns whether or not a pivot has been set by a call to {@link #setPivotX(float)} or
+     * {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center
+     * of the view.
+     *
+     * @return True if a pivot has been set, false if the default pivot is being used
+     */
+    public boolean isPivotSet() {
+        return mRenderNode.isPivotExplicitlySet();
+    }
+
+    /**
+     * Clears any pivot previously set by a call to  {@link #setPivotX(float)} or
+     * {@link #setPivotY(float)}. After calling this {@link #isPivotSet()} will be false
+     * and the pivot used for rotation will return to default of being centered on the view.
+     */
+    public void resetPivot() {
+        if (mRenderNode.resetPivot()) {
+            invalidateViewProperty(false, false);
+        }
+    }
+
+    /**
      * 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.
      *
@@ -14656,10 +14928,6 @@
      * ImageView with only the foreground image. The default implementation returns true; subclasses
      * should override if they have cases which can be optimized.</p>
      *
-     * <p>The current implementation of the saveLayer and saveLayerAlpha methods in {@link Canvas}
-     * necessitates that a View return true if it uses the methods internally without passing the
-     * {@link Canvas#CLIP_TO_LAYER_SAVE_FLAG}.</p>
-     *
      * <p><strong>Note:</strong> The return value of this method is ignored if {@link
      * #forceHasOverlappingRendering(boolean)} has been called on this view.</p>
      *
@@ -14712,7 +14980,7 @@
         if (mTransformationInfo.mAlpha != alpha) {
             // Report visibility changes, which can affect children, to accessibility
             if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
-                notifyAccessibilitySubtreeChanged();
+                notifySubtreeAccessibilityStateChangedIfNeeded();
             }
             mTransformationInfo.mAlpha = alpha;
             if (onSetAlpha((int) (alpha * 255))) {
@@ -15214,7 +15482,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -15248,7 +15516,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -15418,7 +15686,7 @@
     public void invalidateOutline() {
         rebuildOutline();
 
-        notifyAccessibilitySubtreeChanged();
+        notifySubtreeAccessibilityStateChangedIfNeeded();
         invalidateViewProperty(false, false);
     }
 
@@ -15459,14 +15727,61 @@
     }
 
     /**
-     * @hide
+     * Sets the color of the spot shadow that is drawn when the view has a positive Z or
+     * elevation value.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different views with different colors.
+     * <p>
+     * The opacity of the final spot shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineSpotShadowColor (typically opaque), and the
+     * {@link android.R.attr#spotShadowAlpha} theme attribute.
+     *
+     * @attr ref android.R.styleable#View_outlineSpotShadowColor
+     * @param color The color this View will cast for its elevation spot shadow.
      */
-    public void setShadowColor(@ColorInt int color) {
-        if (mRenderNode.setShadowColor(color)) {
+    public void setOutlineSpotShadowColor(@ColorInt int color) {
+        if (mRenderNode.setSpotShadowColor(color)) {
             invalidateViewProperty(true, true);
         }
     }
 
+    /**
+     * @return The shadow color set by {@link #setOutlineSpotShadowColor(int)}, or black if nothing
+     * was set
+     */
+    public @ColorInt int getOutlineSpotShadowColor() {
+        return mRenderNode.getSpotShadowColor();
+    }
+
+    /**
+     * Sets the color of the ambient shadow that is drawn when the view has a positive Z or
+     * elevation value.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different views with different colors.
+     * <p>
+     * The opacity of the final ambient shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+     * {@link android.R.attr#ambientShadowAlpha} theme attribute.
+     *
+     * @attr ref android.R.styleable#View_outlineAmbientShadowColor
+     * @param color The color this View will cast for its elevation shadow.
+     */
+    public void setOutlineAmbientShadowColor(@ColorInt int color) {
+        if (mRenderNode.setAmbientShadowColor(color)) {
+            invalidateViewProperty(true, true);
+        }
+    }
+
+    /**
+     * @return The shadow color set by {@link #setOutlineAmbientShadowColor(int)}, or black if
+     * nothing was set
+     */
+    public @ColorInt int getOutlineAmbientShadowColor() {
+        return mRenderNode.getAmbientShadowColor();
+    }
+
 
     /** @hide */
     public void setRevealClip(boolean shouldClip, float x, float y, float radius) {
@@ -15613,7 +15928,7 @@
                 }
                 invalidateParentIfNeeded();
             }
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -15661,7 +15976,7 @@
                 }
                 invalidateParentIfNeeded();
             }
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -16539,6 +16854,18 @@
     }
 
     /**
+     * Post a callback to send a {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
+     * This event is sent at most once every
+     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
+     */
+    private void postSendViewScrolledAccessibilityEventCallback(int dx, int dy) {
+        if (mSendViewScrolledAccessibilityEvent == null) {
+            mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
+        }
+        mSendViewScrolledAccessibilityEvent.post(dx, dy);
+    }
+
+    /**
      * Called by a parent to request that a child update its values for mScrollX
      * and mScrollY if necessary. This will typically be done if the child is
      * animating a scroll using a {@link android.widget.Scroller Scroller}
@@ -17793,13 +18120,7 @@
         removeUnsetPressCallback();
         removeLongPressCallback();
         removePerformClickCallback();
-        if (mAttachInfo != null
-                && mAttachInfo.mViewRootImpl.mAccessibilityState != null
-                && mAttachInfo.mViewRootImpl.mAccessibilityState.isScrollEventSenderInitialized()) {
-            mAttachInfo.mViewRootImpl.mAccessibilityState
-                    .getSendViewScrolledAccessibilityEvent()
-                    .cancelIfPendingFor(this);
-        }
+        cancel(mSendViewScrolledAccessibilityEvent);
         stopNestedScroll();
 
         // Anything that started animating right before detach should already
@@ -17847,19 +18168,20 @@
      * currently attached to.
      */
     public WindowId getWindowId() {
-        if (mAttachInfo == null) {
+        AttachInfo ai = mAttachInfo;
+        if (ai == null) {
             return null;
         }
-        if (mAttachInfo.mWindowId == null) {
+        if (ai.mWindowId == null) {
             try {
-                mAttachInfo.mIWindowId = mAttachInfo.mSession.getWindowId(
-                        mAttachInfo.mWindowToken);
-                mAttachInfo.mWindowId = new WindowId(
-                        mAttachInfo.mIWindowId);
+                ai.mIWindowId = ai.mSession.getWindowId(ai.mWindowToken);
+                if (ai.mIWindowId != null) {
+                    ai.mWindowId = new WindowId(ai.mIWindowId);
+                }
             } catch (RemoteException e) {
             }
         }
-        return mAttachInfo.mWindowId;
+        return ai.mWindowId;
     }
 
     /**
@@ -18255,7 +18577,17 @@
                 // Hence prevent the same autofill view id from being restored multiple times.
                 ((BaseSavedState) state).mSavedData &= ~BaseSavedState.AUTOFILL_ID;
 
-                mAutofillViewId = baseState.mAutofillViewId;
+                if ((mPrivateFlags3 & PFLAG3_AUTOFILLID_EXPLICITLY_SET) != 0) {
+                    // Ignore when view already set it through setAutofillId();
+                    if (android.view.autofill.Helper.sDebug) {
+                        Log.d(VIEW_LOG_TAG, "onRestoreInstanceState(): not setting autofillId to "
+                                + baseState.mAutofillViewId + " because view explicitly set it to "
+                                + mAutofillId);
+                    }
+                } else {
+                    mAutofillViewId = baseState.mAutofillViewId;
+                    mAutofillId = null; // will be set on demand by getAutofillId()
+                }
             }
         }
     }
@@ -19892,22 +20224,20 @@
 
         int solidColor = getSolidColor();
         if (solidColor == 0) {
-            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-
             if (drawTop) {
-                canvas.saveLayer(left, top, right, top + length, null, flags);
+                canvas.saveUnclippedLayer(left, top, right, top + length);
             }
 
             if (drawBottom) {
-                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
+                canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
             }
 
             if (drawLeft) {
-                canvas.saveLayer(left, top, left + length, bottom, null, flags);
+                canvas.saveUnclippedLayer(left, top, left + length, bottom);
             }
 
             if (drawRight) {
-                canvas.saveLayer(right - length, top, right, bottom, null, flags);
+                canvas.saveUnclippedLayer(right - length, top, right, bottom);
             }
         } else {
             scrollabilityCache.setFadeColor(solidColor);
@@ -20427,7 +20757,7 @@
                 mForegroundInfo.mBoundsChanged = true;
             }
 
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
         return changed;
     }
@@ -21871,7 +22201,8 @@
             if (selected) {
                 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
             } else {
-                notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -23458,8 +23789,7 @@
          * constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)}
          * and {@link #onDrawShadow(Canvas)} methods are also overridden in order
          * to supply the drag shadow's dimensions and appearance without
-         * reference to any View object. If they are not overridden, then the result is an
-         * invisible drag shadow.
+         * reference to any View object.
          */
         public DragShadowBuilder() {
             mView = new WeakReference<View>(null);
@@ -23605,9 +23935,19 @@
         Point shadowTouchPoint = new Point();
         shadowBuilder.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
 
-        if ((shadowSize.x <= 0) || (shadowSize.y <= 0)
+        if ((shadowSize.x < 0) || (shadowSize.y < 0)
                 || (shadowTouchPoint.x < 0) || (shadowTouchPoint.y < 0)) {
-            throw new IllegalStateException("Drag shadow dimensions must be positive");
+            throw new IllegalStateException("Drag shadow dimensions must not be negative");
+        }
+
+        // Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
+        // does not accept zero size surface.
+        if (shadowSize.x == 0  || shadowSize.y == 0) {
+            if (!sAcceptZeroSizeDragShadow) {
+                throw new IllegalStateException("Drag shadow dimensions must be positive");
+            }
+            shadowSize.x = 1;
+            shadowSize.y = 1;
         }
 
         if (ViewDebug.DEBUG_DRAG) {
@@ -25540,26 +25880,19 @@
     }
 
     /**
-     * Interface definition for a callback to be invoked when a hardware key event is
-     * dispatched to this view during the fallback phase. This means no view in the hierarchy
-     * has handled this event.
+     * Interface definition for a callback to be invoked when a hardware key event hasn't
+     * been handled by the view hierarchy.
      */
-    public interface OnKeyFallbackListener {
+    public interface OnUnhandledKeyEventListener {
         /**
-         * Called when a hardware key is dispatched to a view in the fallback phase. This allows
-         * listeners to respond to events after the view hierarchy has had a chance to respond.
-         * <p>Key presses in software keyboards will generally NOT trigger this method,
-         * although some may elect to do so in some situations. Do not assume a
-         * software input method has to be key-based; even if it is, it may use key presses
-         * in a different way than you expect, so there is no way to reliably catch soft
-         * input key presses.
+         * Called when a hardware key is dispatched to a view after being unhandled during normal
+         * {@link KeyEvent} dispatch.
          *
          * @param v The view the key has been dispatched to.
-         * @param event The KeyEvent object containing full information about
-         *        the event.
-         * @return True if the listener has consumed the event, false otherwise.
+         * @param event The KeyEvent object containing information about the event.
+         * @return {@code true} if the listener has consumed the event, {@code false} otherwise.
          */
-        boolean onKeyFallback(View v, KeyEvent event);
+        boolean onUnhandledKeyEvent(View v, KeyEvent event);
     }
 
     /**
@@ -26458,6 +26791,53 @@
     }
 
     /**
+     * Resuable callback for sending
+     * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+     */
+    private class SendViewScrolledAccessibilityEvent implements Runnable {
+        public volatile boolean mIsPending;
+        public int mDeltaX;
+        public int mDeltaY;
+
+        public void post(int dx, int dy) {
+            mDeltaX += dx;
+            mDeltaY += dy;
+            if (!mIsPending) {
+                mIsPending = true;
+                postDelayed(this, ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
+            }
+        }
+
+        @Override
+        public void run() {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                AccessibilityEvent event = AccessibilityEvent.obtain(
+                        AccessibilityEvent.TYPE_VIEW_SCROLLED);
+                event.setScrollDeltaX(mDeltaX);
+                event.setScrollDeltaY(mDeltaY);
+                sendAccessibilityEventUnchecked(event);
+            }
+            reset();
+        }
+
+        private void reset() {
+            mIsPending = false;
+            mDeltaX = 0;
+            mDeltaY = 0;
+        }
+    }
+
+    /**
+     * Remove the pending callback for sending a
+     * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+     */
+    private void cancel(@Nullable SendViewScrolledAccessibilityEvent callback) {
+        if (callback == null || !callback.mIsPending) return;
+        removeCallbacks(callback);
+        callback.reset();
+    }
+
+    /**
      * <p>
      * This class represents a delegate that can be registered in a {@link View}
      * to enhance accessibility support via composition rather via inheritance.
@@ -26903,6 +27283,8 @@
         stream.addProperty("drawing:scaleY", getScaleY());
         stream.addProperty("drawing:pivotX", getPivotX());
         stream.addProperty("drawing:pivotY", getPivotY());
+        stream.addProperty("drawing:clipBounds",
+                mClipBounds == null ? null : mClipBounds.toString());
         stream.addProperty("drawing:opaque", isOpaque());
         stream.addProperty("drawing:alpha", getAlpha());
         stream.addProperty("drawing:transitionAlpha", getTransitionAlpha());
@@ -26914,6 +27296,8 @@
         stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing());
         stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled());
         stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering());
+        stream.addProperty("drawing:outlineAmbientShadowColor", getOutlineAmbientShadowColor());
+        stream.addProperty("drawing:outlineSpotShadowColor", getOutlineSpotShadowColor());
 
         // focus
         stream.addProperty("focus:hasFocus", hasFocus());
@@ -27074,7 +27458,7 @@
         mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText);
         mAttachInfo.mTooltipHost = this;
         // The available accessibility actions have changed
-        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED);
         return true;
     }
 
@@ -27094,7 +27478,7 @@
             mAttachInfo.mTooltipHost = null;
         }
         // The available accessibility actions have changed
-        notifyAccessibilityStateChanged(CONTENT_CHANGE_TYPE_UNDEFINED);
+        notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
     private boolean showLongClickTooltip(int x, int y) {
@@ -27199,20 +27583,44 @@
     }
 
     /**
+     * @return {@code true} if the default focus highlight is enabled, {@code false} otherwies.
+     * @hide
+     */
+    @TestApi
+    public static boolean isDefaultFocusHighlightEnabled() {
+        return sUseDefaultFocusHighlight;
+    }
+
+    /**
+     * Dispatch a previously unhandled {@link KeyEvent} to this view. Unlike normal key dispatch,
+     * this dispatches to ALL child views until it is consumed. The dispatch order is z-order
+     * (visually on-top views first).
+     *
+     * @param evt the previously unhandled {@link KeyEvent}.
+     * @return the {@link View} which consumed the event or {@code null} if not consumed.
+     */
+    View dispatchUnhandledKeyEvent(KeyEvent evt) {
+        if (onUnhandledKeyEvent(evt)) {
+            return this;
+        }
+        return null;
+    }
+
+    /**
      * Allows this view to handle {@link KeyEvent}s which weren't handled by normal dispatch. This
      * occurs after the normal view hierarchy dispatch, but before the window callback. By default,
      * this will dispatch into all the listeners registered via
-     * {@link #addKeyFallbackListener(OnKeyFallbackListener)} in last-in-first-out order (most
-     * recently added will receive events first).
+     * {@link #addOnUnhandledKeyEventListener(OnUnhandledKeyEventListener)} in last-in-first-out
+     * order (most recently added will receive events first).
      *
-     * @param event A not-previously-handled event.
+     * @param event An unhandled event.
      * @return {@code true} if the event was handled, {@code false} otherwise.
-     * @see #addKeyFallbackListener
+     * @see #addOnUnhandledKeyEventListener
      */
-    public boolean onKeyFallback(@NonNull KeyEvent event) {
-        if (mListenerInfo != null && mListenerInfo.mKeyFallbackListeners != null) {
-            for (int i = mListenerInfo.mKeyFallbackListeners.size() - 1; i >= 0; --i) {
-                if (mListenerInfo.mKeyFallbackListeners.get(i).onKeyFallback(this, event)) {
+    boolean onUnhandledKeyEvent(@NonNull KeyEvent event) {
+        if (mListenerInfo != null && mListenerInfo.mUnhandledKeyListeners != null) {
+            for (int i = mListenerInfo.mUnhandledKeyListeners.size() - 1; i >= 0; --i) {
+                if (mListenerInfo.mUnhandledKeyListeners.get(i).onUnhandledKeyEvent(this, event)) {
                     return true;
                 }
             }
@@ -27220,31 +27628,47 @@
         return false;
     }
 
-    /**
-     * Adds a listener which will receive unhandled {@link KeyEvent}s.
-     * @param listener the receiver of fallback {@link KeyEvent}s.
-     * @see #onKeyFallback(KeyEvent)
-     */
-    public void addKeyFallbackListener(OnKeyFallbackListener listener) {
-        ArrayList<OnKeyFallbackListener> fallbacks = getListenerInfo().mKeyFallbackListeners;
-        if (fallbacks == null) {
-            fallbacks = new ArrayList<>();
-            getListenerInfo().mKeyFallbackListeners = fallbacks;
-        }
-        fallbacks.add(listener);
+    boolean hasUnhandledKeyListener() {
+        return (mListenerInfo != null && mListenerInfo.mUnhandledKeyListeners != null
+                && !mListenerInfo.mUnhandledKeyListeners.isEmpty());
     }
 
     /**
-     * Removes a listener which will receive unhandled {@link KeyEvent}s.
-     * @param listener the receiver of fallback {@link KeyEvent}s.
-     * @see #onKeyFallback(KeyEvent)
+     * Adds a listener which will receive unhandled {@link KeyEvent}s. This must be called on the
+     * UI thread.
+     *
+     * @param listener a receiver of unhandled {@link KeyEvent}s.
+     * @see #removeOnUnhandledKeyEventListener
      */
-    public void removeKeyFallbackListener(OnKeyFallbackListener listener) {
+    public void addOnUnhandledKeyEventListener(OnUnhandledKeyEventListener listener) {
+        ArrayList<OnUnhandledKeyEventListener> listeners = getListenerInfo().mUnhandledKeyListeners;
+        if (listeners == null) {
+            listeners = new ArrayList<>();
+            getListenerInfo().mUnhandledKeyListeners = listeners;
+        }
+        listeners.add(listener);
+        if (listeners.size() == 1 && mParent instanceof ViewGroup) {
+            ((ViewGroup) mParent).incrementChildUnhandledKeyListeners();
+        }
+    }
+
+    /**
+     * Removes a listener which will receive unhandled {@link KeyEvent}s. This must be called on the
+     * UI thread.
+     *
+     * @param listener a receiver of unhandled {@link KeyEvent}s.
+     * @see #addOnUnhandledKeyEventListener
+     */
+    public void removeOnUnhandledKeyEventListener(OnUnhandledKeyEventListener listener) {
         if (mListenerInfo != null) {
-            if (mListenerInfo.mKeyFallbackListeners != null) {
-                mListenerInfo.mKeyFallbackListeners.remove(listener);
-                if (mListenerInfo.mKeyFallbackListeners.isEmpty()) {
-                    mListenerInfo.mKeyFallbackListeners = null;
+            if (mListenerInfo.mUnhandledKeyListeners != null
+                    && !mListenerInfo.mUnhandledKeyListeners.isEmpty()) {
+                mListenerInfo.mUnhandledKeyListeners.remove(listener);
+                if (mListenerInfo.mUnhandledKeyListeners.isEmpty()) {
+                    mListenerInfo.mUnhandledKeyListeners = null;
+                    if (mParent instanceof ViewGroup) {
+                        ((ViewGroup) mParent).decrementChildUnhandledKeyListeners();
+                    }
                 }
             }
         }
diff --git a/android/view/ViewConfiguration.java b/android/view/ViewConfiguration.java
index c5a94da..7a9de45 100644
--- a/android/view/ViewConfiguration.java
+++ b/android/view/ViewConfiguration.java
@@ -303,6 +303,7 @@
     private final long mGlobalActionsKeyTimeout;
     private final float mVerticalScrollFactor;
     private final float mHorizontalScrollFactor;
+    private final boolean mShowMenuShortcutsWhenKeyboardPresent;
 
     private boolean sHasPermanentMenuKey;
     private boolean sHasPermanentMenuKeySet;
@@ -335,6 +336,7 @@
         mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT;
         mHorizontalScrollFactor = HORIZONTAL_SCROLL_FACTOR;
         mVerticalScrollFactor = VERTICAL_SCROLL_FACTOR;
+        mShowMenuShortcutsWhenKeyboardPresent = false;
     }
 
     /**
@@ -428,6 +430,10 @@
                 com.android.internal.R.dimen.config_horizontalScrollFactor);
         mVerticalScrollFactor = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.config_verticalScrollFactor);
+
+        mShowMenuShortcutsWhenKeyboardPresent = res.getBoolean(
+            com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent);
+
     }
 
     /**
@@ -910,6 +916,15 @@
     }
 
     /**
+     * Check if shortcuts should be displayed in menus.
+     *
+     * @return {@code True} if shortcuts should be displayed in menus.
+     */
+    public boolean shouldShowMenuShortcutsWhenKeyboardPresent() {
+        return mShowMenuShortcutsWhenKeyboardPresent;
+    }
+
+    /**
      * @hide
      * @return Whether or not marquee should use fading edges.
      */
diff --git a/android/view/ViewDebug.java b/android/view/ViewDebug.java
index b09934e..276f50a 100644
--- a/android/view/ViewDebug.java
+++ b/android/view/ViewDebug.java
@@ -21,7 +21,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Point;
+import android.graphics.Picture;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
@@ -1782,27 +1782,18 @@
      * @hide
      */
     public static class HardwareCanvasProvider implements CanvasProvider {
-
-        private View mView;
-        private Point mSize;
-        private RenderNode mNode;
-        private DisplayListCanvas mCanvas;
+        private Picture mPicture;
 
         @Override
         public Canvas getCanvas(View view, int width, int height) {
-            mView = view;
-            mSize = new Point(width, height);
-            mNode = RenderNode.create("ViewDebug", mView);
-            mNode.setLeftTopRightBottom(0, 0, width, height);
-            mNode.setClipToBounds(false);
-            mCanvas = mNode.start(width, height);
-            return mCanvas;
+            mPicture = new Picture();
+            return mPicture.beginRecording(width, height);
         }
 
         @Override
         public Bitmap createBitmap() {
-            mNode.end(mCanvas);
-            return ThreadedRenderer.createHardwareBitmap(mNode, mSize.x, mSize.y);
+            mPicture.endRecording();
+            return Bitmap.createBitmap(mPicture);
         }
     }
 
diff --git a/android/view/ViewGroup.java b/android/view/ViewGroup.java
index 4631261..6002fe5 100644
--- a/android/view/ViewGroup.java
+++ b/android/view/ViewGroup.java
@@ -583,6 +583,11 @@
     private List<Integer> mTransientIndices = null;
     private List<View> mTransientViews = null;
 
+    /**
+     * Keeps track of how many child views have UnhandledKeyEventListeners. This should only be
+     * updated on the UI thread so shouldn't require explicit synchronization.
+     */
+    int mChildUnhandledKeyListeners = 0;
 
     /**
      * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
@@ -3555,13 +3560,16 @@
     public void dispatchProvideAutofillStructure(ViewStructure structure,
             @AutofillFlags int flags) {
         super.dispatchProvideAutofillStructure(structure, flags);
+
         if (structure.getChildCount() != 0) {
             return;
         }
 
         if (!isLaidOut()) {
-            Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
-                    + mChildrenCount + " children of " + getAutofillId());
+            if (Helper.sVerbose) {
+                Log.v(VIEW_LOG_TAG, "dispatchProvideAutofillStructure(): not laid out, ignoring "
+                        + mChildrenCount + " children of " + getAutofillId());
+            }
             return;
         }
 
@@ -3649,34 +3657,44 @@
         return ViewGroup.class.getName();
     }
 
+    @Override
+    public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
+        // If this is a live region, we should send a subtree change event
+        // from this view. Otherwise, we can let it propagate up.
+        if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+        } else if (mParent != null) {
+            try {
+                mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
+            } catch (AbstractMethodError e) {
+                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                        " does not fully implement ViewParent", e);
+            }
+        }
+    }
+
     /** @hide */
     @Override
-    public void notifyAccessibilitySubtreeChanged() {
+    public void notifySubtreeAccessibilityStateChangedIfNeeded() {
         if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
             return;
         }
         // If something important for a11y is happening in this subtree, make sure it's dispatched
         // from a view that is important for a11y so it doesn't get lost.
-        if (getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                && !isImportantForAccessibility()
-                && getChildCount() > 0) {
+        if ((getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
+                && !isImportantForAccessibility() && (getChildCount() > 0)) {
             ViewParent a11yParent = getParentForAccessibility();
             if (a11yParent instanceof View) {
-                ((View) a11yParent).notifyAccessibilitySubtreeChanged();
+                ((View) a11yParent).notifySubtreeAccessibilityStateChangedIfNeeded();
                 return;
             }
         }
-        super.notifyAccessibilitySubtreeChanged();
+        super.notifySubtreeAccessibilityStateChangedIfNeeded();
     }
 
     @Override
-    public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
-        notifyAccessibilityStateChanged(source, changeType);
-    }
-
-    /** @hide */
-    @Override
-    public void resetSubtreeAccessibilityStateChanged() {
+    void resetSubtreeAccessibilityStateChanged() {
         super.resetSubtreeAccessibilityStateChanged();
         View[] children = mChildren;
         final int childCount = mChildrenCount;
@@ -3958,15 +3976,7 @@
     }
 
     /**
-     * Layout debugging code which draws rectangles around layout params.
-     *
-     * <p>This function is called automatically when the developer setting is enabled.<p/>
-     *
-     * <p>It is strongly advised to only call this function from debug builds as there is
-     * a risk of leaking unwanted layout information.<p/>
-     *
-     * @param canvas the canvas on which to draw
-     * @param paint the paint used to draw through
+     * @hide
      */
     protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
         for (int i = 0; i < getChildCount(); i++) {
@@ -3976,19 +3986,7 @@
     }
 
     /**
-     * Layout debugging code which draws rectangles around:
-     * <ul>
-     *     <li>optical bounds<li/>
-     *     <li>margins<li/>
-     *     <li>clip bounds<li/>
-     * <ul/>
-     *
-     * <p>This function is called automatically when the developer setting is enabled.<p/>
-     *
-     * <p>It is strongly advised to only call this function from debug builds as there is
-     * a risk of leaking unwanted layout information.<p/>
-     *
-     * @param canvas the canvas on which to draw
+     * @hide
      */
     protected void onDebugDraw(Canvas canvas) {
         Paint paint = getDebugPaint();
@@ -4292,6 +4290,13 @@
                 recreateChildDisplayList(child);
             }
         }
+        final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
+        for (int i = 0; i < transientCount; ++i) {
+            View child = mTransientViews.get(i);
+            if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
+                recreateChildDisplayList(child);
+            }
+        }
         if (mOverlay != null) {
             View overlayView = mOverlay.getOverlayView();
             recreateChildDisplayList(overlayView);
@@ -5055,6 +5060,9 @@
             child.assignParent(this);
         } else {
             child.mParent = this;
+            if (child.hasUnhandledKeyListener()) {
+                incrementChildUnhandledKeyListeners();
+            }
         }
 
         final boolean childHasFocus = child.hasFocus();
@@ -5088,7 +5096,7 @@
         }
 
         if (child.getVisibility() != View.GONE) {
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
 
         if (mTransientIndices != null) {
@@ -5110,6 +5118,20 @@
             // manually assembling the hierarchy, update the ancestor default-focus chain.
             setDefaultFocus(child);
         }
+
+        touchAccessibilityNodeProviderIfNeeded(child);
+    }
+
+    /**
+     * We may need to touch the provider to bring up the a11y layer. In a11y mode
+     * clients inspect the screen or the user touches it which triggers bringing up
+     * of the a11y infrastructure while in autofill mode we want the infra up and
+     * running from the beginning since we watch for a11y events to drive autofill.
+     */
+    private void touchAccessibilityNodeProviderIfNeeded(View child) {
+        if (mContext.isAutofillCompatibilityEnabled()) {
+            child.getAccessibilityNodeProvider();
+        }
     }
 
     private void addInArray(View child, int index) {
@@ -5345,6 +5367,10 @@
 
         removeFromArray(index);
 
+        if (view.hasUnhandledKeyListener()) {
+            decrementChildUnhandledKeyListeners();
+        }
+
         if (view == mDefaultFocus) {
             clearDefaultFocus(view);
         }
@@ -5358,7 +5384,7 @@
         dispatchViewRemoved(view);
 
         if (view.getVisibility() != View.GONE) {
-            notifyAccessibilitySubtreeChanged();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
 
         int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
@@ -6077,7 +6103,7 @@
         if (invalidate) {
             invalidateViewProperty(false, false);
         }
-        notifyAccessibilitySubtreeChanged();
+        notifySubtreeAccessibilityStateChangedIfNeeded();
     }
 
     @Override
@@ -7523,6 +7549,62 @@
         }
     }
 
+    @Override
+    boolean hasUnhandledKeyListener() {
+        return (mChildUnhandledKeyListeners > 0) || super.hasUnhandledKeyListener();
+    }
+
+    void incrementChildUnhandledKeyListeners() {
+        mChildUnhandledKeyListeners += 1;
+        if (mChildUnhandledKeyListeners == 1) {
+            if (mParent instanceof ViewGroup) {
+                ((ViewGroup) mParent).incrementChildUnhandledKeyListeners();
+            }
+        }
+    }
+
+    void decrementChildUnhandledKeyListeners() {
+        mChildUnhandledKeyListeners -= 1;
+        if (mChildUnhandledKeyListeners == 0) {
+            if (mParent instanceof ViewGroup) {
+                ((ViewGroup) mParent).decrementChildUnhandledKeyListeners();
+            }
+        }
+    }
+
+    @Override
+    View dispatchUnhandledKeyEvent(KeyEvent evt) {
+        if (!hasUnhandledKeyListener()) {
+            return null;
+        }
+        ArrayList<View> orderedViews = buildOrderedChildList();
+        if (orderedViews != null) {
+            try {
+                for (int i = orderedViews.size() - 1; i >= 0; --i) {
+                    View v = orderedViews.get(i);
+                    View consumer = v.dispatchUnhandledKeyEvent(evt);
+                    if (consumer != null) {
+                        return consumer;
+                    }
+                }
+            } finally {
+                orderedViews.clear();
+            }
+        } else {
+            for (int i = getChildCount() - 1; i >= 0; --i) {
+                View v = getChildAt(i);
+                View consumer = v.dispatchUnhandledKeyEvent(evt);
+                if (consumer != null) {
+                    return consumer;
+                }
+            }
+        }
+        if (onUnhandledKeyEvent(evt)) {
+            return this;
+        }
+        return null;
+    }
+
     /**
      * LayoutParams are used by views to tell their parents how they want to be
      * laid out. See
@@ -7705,14 +7787,10 @@
         /**
          * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
          *
-         * <p>This function is called automatically when the developer setting is enabled.<p/>
-         *
-         * <p>It is strongly advised to only call this function from debug builds as there is
-         * a risk of leaking unwanted layout information.<p/>
-         *
          * @param view the view that contains these layout parameters
          * @param canvas the canvas on which to draw
-         * @param paint the paint used to draw through
+         *
+         * @hide
          */
         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
         }
@@ -8216,6 +8294,9 @@
             return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
         }
 
+        /**
+         * @hide
+         */
         @Override
         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
             Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
diff --git a/android/view/ViewGroup_Delegate.java b/android/view/ViewGroup_Delegate.java
index 6daae20..10a4f31 100644
--- a/android/view/ViewGroup_Delegate.java
+++ b/android/view/ViewGroup_Delegate.java
@@ -79,12 +79,13 @@
         }
         Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
                 Density.getEnum(canvas.getDensity()));
+        canvas.save();
         Rect clipBounds = canvas.getClipBounds();
         Rect newBounds = new Rect(clipBounds);
         newBounds.inset((int)-elevation, (int)-elevation);
         canvas.clipRect(newBounds, Op.REPLACE);
         canvas.drawBitmap(bitmap, 0, 0, null);
-        canvas.clipRect(clipBounds, Op.REPLACE);
+        canvas.restore();
     }
 
     private static float getElevation(View child, ViewGroup parent) {
@@ -145,11 +146,11 @@
                         canvas.concat(transformToApply.getMatrix());
                         canvas.translate(transX, transY);
                     }
-                    if (!childHasIdentityMatrix) {
-                        canvas.translate(-transX, -transY);
-                        canvas.concat(child.getMatrix());
-                        canvas.translate(transX, transY);
-                    }
+                }
+                if (!childHasIdentityMatrix) {
+                    canvas.translate(-transX, -transY);
+                    canvas.concat(child.getMatrix());
+                    canvas.translate(transX, transY);
                 }
 
             }
diff --git a/android/view/ViewRootImpl.java b/android/view/ViewRootImpl.java
index 30f584c..433c90b 100644
--- a/android/view/ViewRootImpl.java
+++ b/android/view/ViewRootImpl.java
@@ -29,6 +29,7 @@
 import android.Manifest;
 import android.animation.LayoutTransition;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ResourcesManager;
@@ -71,6 +72,7 @@
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.LongArray;
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -89,13 +91,12 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityNodeProvider;
-import android.view.accessibility.AccessibilityViewHierarchyState;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
-import android.view.accessibility.ThrottlingAccessibilityEventSender;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.Interpolator;
+import android.view.autofill.AutofillManager;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
 
@@ -115,6 +116,9 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -376,7 +380,7 @@
     InputStage mFirstPostImeInputStage;
     InputStage mSyntheticInputStage;
 
-    private final KeyFallbackManager mKeyFallbackManager = new KeyFallbackManager();
+    private final UnhandledKeyManager mUnhandledKeyManager = new UnhandledKeyManager();
 
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
@@ -461,6 +465,10 @@
             new AccessibilityInteractionConnectionManager();
     final HighContrastTextManager mHighContrastTextManager;
 
+    SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
+
+    HashSet<View> mTempHashSet;
+
     private final int mDensity;
     private final int mNoncompatDensity;
 
@@ -475,8 +483,6 @@
 
     private boolean mNeedsRendererSetup;
 
-    protected AccessibilityViewHierarchyState mAccessibilityState;
-
     /**
      * Consistency verifier for debugging purposes.
      */
@@ -748,7 +754,7 @@
                     mAttachInfo.mRecomputeGlobalAttributes = true;
                     collectViewAttributes();
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(),
+                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                             mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                 } catch (RemoteException e) {
@@ -895,6 +901,26 @@
         return mWindowAttributes.getTitle();
     }
 
+    /**
+     * @return the width of the root view. Note that this will return {@code -1} until the first
+     *         layout traversal, when the width is set.
+     *
+     * @hide
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * @return the height of the root view. Note that this will return {@code -1} until the first
+     *         layout traversal, when the height is set.
+     *
+     * @hide
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
     void destroyHardwareResources() {
         if (mAttachInfo.mThreadedRenderer != null) {
             mAttachInfo.mThreadedRenderer.destroyHardwareResources(mView);
@@ -1686,8 +1712,8 @@
                 desiredWindowWidth = size.x;
                 desiredWindowHeight = size.y;
             } else {
-                desiredWindowWidth = dipToPx(config.screenWidthDp);
-                desiredWindowHeight = dipToPx(config.screenHeightDp);
+                desiredWindowWidth = mWinFrame.width();
+                desiredWindowHeight = mWinFrame.height();
             }
 
             // We used to use the following condition to choose 32 bits drawing caches:
@@ -1974,6 +2000,7 @@
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
+                surfaceChanged |= surfaceSizeChanged;
                 final boolean alwaysConsumeNavBarChanged =
                         mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar;
                 if (contentInsetsChanged) {
@@ -2547,6 +2574,10 @@
                         ~WindowManager.LayoutParams
                                 .SOFT_INPUT_IS_FORWARD_NAVIGATION;
                 mHasHadWindowFocus = true;
+
+                // Refocusing a window that has a focused view should fire a
+                // focus event for the view since the global focused view changed.
+                fireAccessibilityFocusEventIfHasFocusedNode();
             } else {
                 if (mPointerCapture) {
                     handlePointerCaptureChanged(false);
@@ -2556,6 +2587,86 @@
         mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
     }
 
+    private void fireAccessibilityFocusEventIfHasFocusedNode() {
+        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+            return;
+        }
+        final View focusedView = mView.findFocus();
+        if (focusedView == null) {
+            return;
+        }
+        final AccessibilityNodeProvider provider = focusedView.getAccessibilityNodeProvider();
+        if (provider == null) {
+            focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+        } else {
+            final AccessibilityNodeInfo focusedNode = findFocusedVirtualNode(provider);
+            if (focusedNode != null) {
+                final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        focusedNode.getSourceNodeId());
+                // This is a best effort since clearing and setting the focus via the
+                // provider APIs could have side effects. We don't have a provider API
+                // similar to that on View to ask a given event to be fired.
+                final AccessibilityEvent event = AccessibilityEvent.obtain(
+                        AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                event.setSource(focusedView, virtualId);
+                event.setPackageName(focusedNode.getPackageName());
+                event.setChecked(focusedNode.isChecked());
+                event.setContentDescription(focusedNode.getContentDescription());
+                event.setPassword(focusedNode.isPassword());
+                event.getText().add(focusedNode.getText());
+                event.setEnabled(focusedNode.isEnabled());
+                focusedView.getParent().requestSendAccessibilityEvent(focusedView, event);
+                focusedNode.recycle();
+            }
+        }
+    }
+
+    private AccessibilityNodeInfo findFocusedVirtualNode(AccessibilityNodeProvider provider) {
+        AccessibilityNodeInfo focusedNode = provider.findFocus(
+                AccessibilityNodeInfo.FOCUS_INPUT);
+        if (focusedNode != null) {
+            return focusedNode;
+        }
+
+        if (!mContext.isAutofillCompatibilityEnabled()) {
+            return null;
+        }
+
+        // Unfortunately some provider implementations don't properly
+        // implement AccessibilityNodeProvider#findFocus
+        AccessibilityNodeInfo current = provider.createAccessibilityNodeInfo(
+                AccessibilityNodeProvider.HOST_VIEW_ID);
+        if (current.isFocused()) {
+            return current;
+        }
+
+        final Queue<AccessibilityNodeInfo> fringe = new LinkedList<>();
+        fringe.offer(current);
+
+        while (!fringe.isEmpty()) {
+            current = fringe.poll();
+            final LongArray childNodeIds = current.getChildNodeIds();
+            if (childNodeIds== null || childNodeIds.size() <= 0) {
+                continue;
+            }
+            final int childCount = childNodeIds.size();
+            for (int i = 0; i < childCount; i++) {
+                final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(
+                        childNodeIds.get(i));
+                final AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(virtualId);
+                if (child != null) {
+                    if (child.isFocused()) {
+                        return child;
+                    }
+                    fringe.offer(child);
+                }
+            }
+            current.recycle();
+        }
+
+        return null;
+    }
+
     private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
         Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
         try {
@@ -3816,6 +3927,7 @@
     private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
     private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
     private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
+    private final static int MSG_DISPATCH_KEY_FROM_AUTOFILL = 12;
     private final static int MSG_CHECK_FOCUS = 13;
     private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14;
     private final static int MSG_DISPATCH_DRAG_EVENT = 15;
@@ -3857,6 +3969,8 @@
                     return "MSG_DISPATCH_GET_NEW_SURFACE";
                 case MSG_DISPATCH_KEY_FROM_IME:
                     return "MSG_DISPATCH_KEY_FROM_IME";
+                case MSG_DISPATCH_KEY_FROM_AUTOFILL:
+                    return "MSG_DISPATCH_KEY_FROM_AUTOFILL";
                 case MSG_CHECK_FOCUS:
                     return "MSG_CHECK_FOCUS";
                 case MSG_CLOSE_SYSTEM_DIALOGS:
@@ -4034,6 +4148,13 @@
                     }
                     enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
                 } break;
+                case MSG_DISPATCH_KEY_FROM_AUTOFILL: {
+                    if (LOCAL_LOGV) {
+                        Log.v(TAG, "Dispatching key " + msg.obj + " from Autofill to " + mView);
+                    }
+                    KeyEvent event = (KeyEvent) msg.obj;
+                    enqueueInputEvent(event, null, 0, true);
+                } break;
                 case MSG_CHECK_FOCUS: {
                     InputMethodManager imm = InputMethodManager.peekInstance();
                     if (imm != null) {
@@ -4324,7 +4445,8 @@
                 Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
                 return true;
             } else if ((!mAttachInfo.mHasWindowFocus
-                    && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) || mStopped
+                    && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
+                    && !isAutofillUiShowing()) || mStopped
                     || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
                     || (mPausedForTransition && !isBack(q.mEvent))) {
                 // This is a focus event and the window doesn't currently have input focus or
@@ -4659,6 +4781,14 @@
                 ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
             }
 
+            if (action == MotionEvent.ACTION_DOWN) {
+                // Upon motion event within app window, close autofill ui.
+                AutofillManager afm = getAutofillManager();
+                if (afm != null) {
+                    afm.requestHideFillUi();
+                }
+            }
+
             if (action == MotionEvent.ACTION_DOWN && mAttachInfo.mTooltipHost != null) {
                 mAttachInfo.mTooltipHost.hideTooltip();
             }
@@ -4845,10 +4975,10 @@
         private int processKeyEvent(QueuedInputEvent q) {
             final KeyEvent event = (KeyEvent)q.mEvent;
 
-            mKeyFallbackManager.mDispatched = false;
+            mUnhandledKeyManager.mDispatched = false;
 
-            if (mKeyFallbackManager.hasFocus()
-                    && mKeyFallbackManager.dispatchUnique(mView, event)) {
+            if (mUnhandledKeyManager.hasFocus()
+                    && mUnhandledKeyManager.dispatchUnique(mView, event)) {
                 return FINISH_HANDLED;
             }
 
@@ -4861,7 +4991,7 @@
                 return FINISH_NOT_HANDLED;
             }
 
-            if (mKeyFallbackManager.dispatchUnique(mView, event)) {
+            if (mUnhandledKeyManager.dispatchUnique(mView, event)) {
                 return FINISH_HANDLED;
             }
 
@@ -6297,6 +6427,28 @@
         return mAudioManager;
     }
 
+    private @Nullable AutofillManager getAutofillManager() {
+        if (mView instanceof ViewGroup) {
+            ViewGroup decorView = (ViewGroup) mView;
+            if (decorView.getChildCount() > 0) {
+                // We cannot use decorView's Context for querying AutofillManager: DecorView's
+                // context is based on Application Context, it would allocate a different
+                // AutofillManager instance.
+                return decorView.getChildAt(0).getContext()
+                        .getSystemService(AutofillManager.class);
+            }
+        }
+        return null;
+    }
+
+    private boolean isAutofillUiShowing() {
+        AutofillManager afm = getAutofillManager();
+        if (afm == null) {
+            return false;
+        }
+        return afm.isAutofillUiShowing();
+    }
+
     public AccessibilityInteractionController getAccessibilityInteractionController() {
         if (mView == null) {
             throw new IllegalStateException("getAccessibilityInteractionController"
@@ -6318,18 +6470,24 @@
             params.backup();
             mTranslator.translateWindowLayout(params);
         }
+
         if (params != null) {
             if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
-        }
 
-        if (params != null && mOrigWindowType != params.type) {
-            // For compatibility with old apps, don't crash here.
-            if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-                Slog.w(mTag, "Window type can not be changed after "
-                        + "the window is added; ignoring change of " + mView);
-                params.type = mOrigWindowType;
+            if (mOrigWindowType != params.type) {
+                // For compatibility with old apps, don't crash here.
+                if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                    Slog.w(mTag, "Window type can not be changed after "
+                            + "the window is added; ignoring change of " + mView);
+                    params.type = mOrigWindowType;
+                }
+            }
+
+            if (mSurface.isValid()) {
+                params.frameNumber = mSurface.getNextFrameNumber();
             }
         }
+
         int relayoutResult = mWindowSession.relayout(
                 mWindow, mSeq, params,
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
@@ -7153,6 +7311,12 @@
         mHandler.sendMessage(msg);
     }
 
+    public void dispatchKeyFromAutofill(KeyEvent event) {
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_AUTOFILL, event);
+        msg.setAsynchronous(true);
+        mHandler.sendMessage(msg);
+    }
+
     /**
      * Reinject unhandled {@link InputEvent}s in order to synthesize fallbacks events.
      *
@@ -7258,9 +7422,11 @@
      * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
      */
     private void postSendWindowContentChangedCallback(View source, int changeType) {
-        getAccessibilityState()
-                .getSendWindowContentChangedAccessibilityEvent()
-                .runOrPost(source, changeType);
+        if (mSendWindowContentChangedAccessibilityEvent == null) {
+            mSendWindowContentChangedAccessibilityEvent =
+                new SendWindowContentChangedAccessibilityEvent();
+        }
+        mSendWindowContentChangedAccessibilityEvent.runOrPost(source, changeType);
     }
 
     /**
@@ -7268,20 +7434,11 @@
      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
      */
     private void removeSendWindowContentChangedCallback() {
-        if (mAccessibilityState != null
-                && mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
-            ThrottlingAccessibilityEventSender.cancelIfPending(
-                    mAccessibilityState.getSendWindowContentChangedAccessibilityEvent());
+        if (mSendWindowContentChangedAccessibilityEvent != null) {
+            mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
         }
     }
 
-    AccessibilityViewHierarchyState getAccessibilityState() {
-        if (mAccessibilityState == null) {
-            mAccessibilityState = new AccessibilityViewHierarchyState();
-        }
-        return mAccessibilityState;
-    }
-
     @Override
     public boolean showContextMenuForChild(View originalView) {
         return false;
@@ -7317,8 +7474,12 @@
             return false;
         }
 
-        // Send any pending event to prevent reordering
-        flushPendingAccessibilityEvents();
+        // Immediately flush pending content changed event (if any) to preserve event order
+        if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+                && mSendWindowContentChangedAccessibilityEvent != null
+                && mSendWindowContentChangedAccessibilityEvent.mSource != null) {
+            mSendWindowContentChangedAccessibilityEvent.removeCallbacksAndRun();
+        }
 
         // Intercept accessibility focus events fired by virtual nodes to keep
         // track of accessibility focus position in such nodes.
@@ -7362,19 +7523,6 @@
         return true;
     }
 
-    /** @hide */
-    public void flushPendingAccessibilityEvents() {
-        if (mAccessibilityState != null) {
-            if (mAccessibilityState.isScrollEventSenderInitialized()) {
-                mAccessibilityState.getSendViewScrolledAccessibilityEvent().sendNowIfPending();
-            }
-            if (mAccessibilityState.isWindowContentChangedEventSenderInitialized()) {
-                mAccessibilityState.getSendWindowContentChangedAccessibilityEvent()
-                        .sendNowIfPending();
-            }
-        }
-    }
-
     /**
      * Updates the focused virtual view, when necessary, in response to a
      * content changed event.
@@ -7509,6 +7657,39 @@
         return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT;
     }
 
+    private View getCommonPredecessor(View first, View second) {
+        if (mTempHashSet == null) {
+            mTempHashSet = new HashSet<View>();
+        }
+        HashSet<View> seen = mTempHashSet;
+        seen.clear();
+        View firstCurrent = first;
+        while (firstCurrent != null) {
+            seen.add(firstCurrent);
+            ViewParent firstCurrentParent = firstCurrent.mParent;
+            if (firstCurrentParent instanceof View) {
+                firstCurrent = (View) firstCurrentParent;
+            } else {
+                firstCurrent = null;
+            }
+        }
+        View secondCurrent = second;
+        while (secondCurrent != null) {
+            if (seen.contains(secondCurrent)) {
+                seen.clear();
+                return secondCurrent;
+            }
+            ViewParent secondCurrentParent = secondCurrent.mParent;
+            if (secondCurrentParent instanceof View) {
+                secondCurrent = (View) secondCurrentParent;
+            } else {
+                secondCurrent = null;
+            }
+        }
+        seen.clear();
+        return null;
+    }
+
     void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
@@ -7617,7 +7798,7 @@
      * @return {@code true} if the event was handled, {@code false} otherwise.
      */
     public boolean dispatchKeyFallbackEvent(KeyEvent event) {
-        return mKeyFallbackManager.dispatch(mView, event);
+        return mUnhandledKeyManager.dispatch(mView, event);
     }
 
     class TakenSurfaceHolder extends BaseSurfaceHolder {
@@ -8119,18 +8300,91 @@
         }
     }
 
-    private static class KeyFallbackManager {
+    private class SendWindowContentChangedAccessibilityEvent implements Runnable {
+        private int mChangeTypes = 0;
 
-        // This is used to ensure that key-fallback events are only dispatched once. We attempt
+        public View mSource;
+        public long mLastEventTimeMillis;
+
+        @Override
+        public void run() {
+            // Protect against re-entrant code and attempt to do the right thing in the case that
+            // we're multithreaded.
+            View source = mSource;
+            mSource = null;
+            if (source == null) {
+                Log.e(TAG, "Accessibility content change has no source");
+                return;
+            }
+            // The accessibility may be turned off while we were waiting so check again.
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                mLastEventTimeMillis = SystemClock.uptimeMillis();
+                AccessibilityEvent event = AccessibilityEvent.obtain();
+                event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                event.setContentChangeTypes(mChangeTypes);
+                source.sendAccessibilityEventUnchecked(event);
+            } else {
+                mLastEventTimeMillis = 0;
+            }
+            // In any case reset to initial state.
+            source.resetSubtreeAccessibilityStateChanged();
+            mChangeTypes = 0;
+        }
+
+        public void runOrPost(View source, int changeType) {
+            if (mHandler.getLooper() != Looper.myLooper()) {
+                CalledFromWrongThreadException e = new CalledFromWrongThreadException("Only the "
+                        + "original thread that created a view hierarchy can touch its views.");
+                // TODO: Throw the exception
+                Log.e(TAG, "Accessibility content change on non-UI thread. Future Android "
+                        + "versions will throw an exception.", e);
+                // Attempt to recover. This code does not eliminate the thread safety issue, but
+                // it should force any issues to happen near the above log.
+                mHandler.removeCallbacks(this);
+                if (mSource != null) {
+                    // Dispatch whatever was pending. It's still possible that the runnable started
+                    // just before we removed the callbacks, and bad things will happen, but at
+                    // least they should happen very close to the logged error.
+                    run();
+                }
+            }
+            if (mSource != null) {
+                // If there is no common predecessor, then mSource points to
+                // a removed view, hence in this case always prefer the source.
+                View predecessor = getCommonPredecessor(mSource, source);
+                mSource = (predecessor != null) ? predecessor : source;
+                mChangeTypes |= changeType;
+                return;
+            }
+            mSource = source;
+            mChangeTypes = changeType;
+            final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
+            final long minEventIntevalMillis =
+                    ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
+            if (timeSinceLastMillis >= minEventIntevalMillis) {
+                removeCallbacksAndRun();
+            } else {
+                mHandler.postDelayed(this, minEventIntevalMillis - timeSinceLastMillis);
+            }
+        }
+
+        public void removeCallbacksAndRun() {
+            mHandler.removeCallbacks(this);
+            run();
+        }
+    }
+
+    private static class UnhandledKeyManager {
+
+        // This is used to ensure that unhandled events are only dispatched once. We attempt
         // to dispatch more than once in order to achieve a certain order. Specifically, if we
-        // are in an Activity or Dialog (and have a Window.Callback), the keyfallback events should
+        // are in an Activity or Dialog (and have a Window.Callback), the unhandled events should
         // be dispatched after the view hierarchy, but before the Activity. However, if we aren't
-        // in an activity, we still want key fallbacks to be dispatched.
+        // in an activity, we still want unhandled keys to be dispatched.
         boolean mDispatched = false;
 
         SparseBooleanArray mCapturedKeys = new SparseBooleanArray();
-        WeakReference<View> mFallbackReceiver = null;
-        int mVisitCount = 0;
+        WeakReference<View> mCurrentReceiver = null;
 
         private void updateCaptureState(KeyEvent event) {
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -8147,56 +8401,28 @@
 
             updateCaptureState(event);
 
-            if (mFallbackReceiver != null) {
-                View target = mFallbackReceiver.get();
+            if (mCurrentReceiver != null) {
+                View target = mCurrentReceiver.get();
                 if (mCapturedKeys.size() == 0) {
-                    mFallbackReceiver = null;
+                    mCurrentReceiver = null;
                 }
                 if (target != null && target.isAttachedToWindow()) {
-                    return target.onKeyFallback(event);
+                    target.onUnhandledKeyEvent(event);
                 }
                 // consume anyways so that we don't feed uncaptured key events to other views
                 return true;
             }
 
-            boolean result = dispatchInZOrder(root, event);
+            View consumer = root.dispatchUnhandledKeyEvent(event);
+            if (consumer != null) {
+                mCurrentReceiver = new WeakReference<>(consumer);
+            }
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-            return result;
-        }
-
-        private boolean dispatchInZOrder(View view, KeyEvent evt) {
-            if (view instanceof ViewGroup) {
-                ViewGroup vg = (ViewGroup) view;
-                ArrayList<View> orderedViews = vg.buildOrderedChildList();
-                if (orderedViews != null) {
-                    try {
-                        for (int i = orderedViews.size() - 1; i >= 0; --i) {
-                            View v = orderedViews.get(i);
-                            if (dispatchInZOrder(v, evt)) {
-                                return true;
-                            }
-                        }
-                    } finally {
-                        orderedViews.clear();
-                    }
-                } else {
-                    for (int i = vg.getChildCount() - 1; i >= 0; --i) {
-                        View v = vg.getChildAt(i);
-                        if (dispatchInZOrder(v, evt)) {
-                            return true;
-                        }
-                    }
-                }
-            }
-            if (view.onKeyFallback(evt)) {
-                mFallbackReceiver = new WeakReference<>(view);
-                return true;
-            }
-            return false;
+            return consumer != null;
         }
 
         boolean hasFocus() {
-            return mFallbackReceiver != null;
+            return mCurrentReceiver != null;
         }
 
         boolean dispatchUnique(View root, KeyEvent event) {
diff --git a/android/view/ViewStructure.java b/android/view/ViewStructure.java
index 1d94abe..3f7ab2a 100644
--- a/android/view/ViewStructure.java
+++ b/android/view/ViewStructure.java
@@ -23,6 +23,8 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.util.Pair;
+import android.view.View.AutofillImportance;
+import android.view.ViewStructure.HtmlInfo;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
@@ -347,6 +349,12 @@
     public abstract void setAutofillOptions(CharSequence[] options);
 
     /**
+     * Sets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of the
+     * view associated with this node.
+     */
+    public void setImportantForAutofill(@AutofillImportance int mode) {}
+
+    /**
      * Sets the {@link android.text.InputType} bits of this node.
      *
      * @param inputType inputType bits as defined by {@link android.text.InputType}.
diff --git a/android/view/Window.java b/android/view/Window.java
index 5bd0782..93b3fc2 100644
--- a/android/view/Window.java
+++ b/android/view/Window.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
-import android.annotation.SystemApi;
 import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -1255,14 +1254,6 @@
     }
 
     /** @hide */
-    @SystemApi
-    public void setDisableWallpaperTouchEvents(boolean disable) {
-        setPrivateFlags(disable
-                ? WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS : 0,
-                WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS);
-    }
-
-    /** @hide */
     public abstract void alwaysReadCloseOnTouchAttr();
 
     /** @hide */
diff --git a/android/view/WindowId.java b/android/view/WindowId.java
index c4cda2c..12e58f1 100644
--- a/android/view/WindowId.java
+++ b/android/view/WindowId.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -35,6 +37,7 @@
  * that doesn't allow the other process to negatively harm your window.
  */
 public class WindowId implements Parcelable {
+    @NonNull
     private final IWindowId mToken;
 
     /**
@@ -74,8 +77,7 @@
             }
         };
 
-        final HashMap<IBinder, WindowId> mRegistrations
-                = new HashMap<IBinder, WindowId>();
+        final HashMap<IBinder, WindowId> mRegistrations = new HashMap<>();
 
         class H extends Handler {
             @Override
@@ -163,10 +165,9 @@
      * same package.
      */
     @Override
-    public boolean equals(Object otherObj) {
+    public boolean equals(@Nullable Object otherObj) {
         if (otherObj instanceof WindowId) {
-            return mToken.asBinder().equals(((WindowId) otherObj)
-                    .mToken.asBinder());
+            return mToken.asBinder().equals(((WindowId) otherObj).mToken.asBinder());
         }
         return false;
     }
@@ -182,7 +183,7 @@
         sb.append("IntentSender{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(": ");
-        sb.append(mToken != null ? mToken.asBinder() : null);
+        sb.append(mToken.asBinder());
         sb.append('}');
         return sb.toString();
     }
@@ -195,30 +196,32 @@
         out.writeStrongBinder(mToken.asBinder());
     }
 
-    public static final Parcelable.Creator<WindowId> CREATOR
-            = new Parcelable.Creator<WindowId>() {
+    public static final Parcelable.Creator<WindowId> CREATOR = new Parcelable.Creator<WindowId>() {
+        @Override
         public WindowId createFromParcel(Parcel in) {
             IBinder target = in.readStrongBinder();
             return target != null ? new WindowId(target) : null;
         }
 
+        @Override
         public WindowId[] newArray(int size) {
             return new WindowId[size];
         }
     };
 
     /** @hide */
+    @NonNull
     public IWindowId getTarget() {
         return mToken;
     }
 
     /** @hide */
-    public WindowId(IWindowId target) {
+    public WindowId(@NonNull IWindowId target) {
         mToken = target;
     }
 
     /** @hide */
-    public WindowId(IBinder target) {
+    public WindowId(@NonNull IBinder target) {
         mToken = IWindowId.Stub.asInterface(target);
     }
 }
diff --git a/android/view/WindowInfo.java b/android/view/WindowInfo.java
index bb9e391..7bae28a 100644
--- a/android/view/WindowInfo.java
+++ b/android/view/WindowInfo.java
@@ -21,6 +21,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pools;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,7 +47,7 @@
     public final Rect boundsInScreen = new Rect();
     public List<IBinder> childTokens;
     public CharSequence title;
-    public int accessibilityIdOfAnchor = View.NO_ID;
+    public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
     public boolean inPictureInPicture;
 
     private WindowInfo() {
@@ -105,7 +106,7 @@
         parcel.writeInt(focused ? 1 : 0);
         boundsInScreen.writeToParcel(parcel, flags);
         parcel.writeCharSequence(title);
-        parcel.writeInt(accessibilityIdOfAnchor);
+        parcel.writeLong(accessibilityIdOfAnchor);
         parcel.writeInt(inPictureInPicture ? 1 : 0);
 
         if (childTokens != null && !childTokens.isEmpty()) {
@@ -142,7 +143,7 @@
         focused = (parcel.readInt() == 1);
         boundsInScreen.readFromParcel(parcel);
         title = parcel.readCharSequence();
-        accessibilityIdOfAnchor = parcel.readInt();
+        accessibilityIdOfAnchor = parcel.readLong();
         inPictureInPicture = (parcel.readInt() == 1);
 
         final boolean hasChildren = (parcel.readInt() == 1);
diff --git a/android/view/WindowManager.java b/android/view/WindowManager.java
index 1c5e871..f6181d7 100644
--- a/android/view/WindowManager.java
+++ b/android/view/WindowManager.java
@@ -63,6 +63,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -235,6 +236,18 @@
     int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
 
     /**
+     * A translucent activity is being opened.
+     * @hide
+     */
+    int TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+
+    /**
+     * A translucent activity is being closed.
+     * @hide
+     */
+    int TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_" }, value = {
@@ -257,7 +270,9 @@
             TRANSIT_KEYGUARD_GOING_AWAY,
             TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
             TRANSIT_KEYGUARD_OCCLUDE,
-            TRANSIT_KEYGUARD_UNOCCLUDE
+            TRANSIT_KEYGUARD_UNOCCLUDE,
+            TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+            TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionType {}
@@ -1832,7 +1847,9 @@
         public static final int SOFT_INPUT_MASK_STATE = 0x0f;
 
         /**
-         * Visibility state for {@link #softInputMode}: no state has been specified.
+         * Visibility state for {@link #softInputMode}: no state has been specified. The system may
+         * show or hide the software keyboard for better user experience when the window gains
+         * focus.
          */
         public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
 
@@ -2219,7 +2236,7 @@
         @IntDef(
                 flag = true,
                 value = {LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT,
-                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,
+                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES,
                         LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER})
         @interface LayoutInDisplayCutoutMode {}
 
@@ -2230,56 +2247,107 @@
          * Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
          *
          * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
          * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
          * @see DisplayCutout
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         @LayoutInDisplayCutoutMode
         public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
 
         /**
          * The window is allowed to extend into the {@link DisplayCutout} area, only if the
-         * {@link DisplayCutout} is fully contained within the status bar. Otherwise, the window is
+         * {@link DisplayCutout} is fully contained within a system bar. Otherwise, the window is
          * laid out such that it does not overlap with the {@link DisplayCutout} area.
          *
          * <p>
-         * In practice, this means that if the window did not set FLAG_FULLSCREEN or
-         * SYSTEM_UI_FLAG_FULLSCREEN, it can extend into the cutout area in portrait.
-         * Otherwise (i.e. fullscreen or landscape) it is laid out such that it does overlap the
+         * In practice, this means that if the window did not set {@link #FLAG_FULLSCREEN} or
+         * {@link View#SYSTEM_UI_FLAG_FULLSCREEN}, it can extend into the cutout area in portrait
+         * if the cutout is at the top edge. Similarly for
+         * {@link View#SYSTEM_UI_FLAG_HIDE_NAVIGATION} and a cutout at the bottom of the screen.
+         * Otherwise (i.e. fullscreen or landscape) it is laid out such that it does not overlap the
          * cutout area.
          *
          * <p>
-         * The usual precautions for not overlapping with the status bar are sufficient for ensuring
-         * that no important content overlaps with the DisplayCutout.
+         * The usual precautions for not overlapping with the status and navigation bar are
+         * sufficient for ensuring that no important content overlaps with the DisplayCutout.
          *
          * @see DisplayCutout
          * @see WindowInsets
+         * @see #layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
 
         /**
-         * The window is always allowed to extend into the {@link DisplayCutout} area,
-         * even if fullscreen or in landscape.
+         * @deprecated use {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES}
+         * @hide
+         */
+        @Deprecated
+        public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;
+
+        /**
+         * The window is always allowed to extend into the {@link DisplayCutout} areas on the short
+         * edges of the screen.
+         *
+         * The window will never extend into a {@link DisplayCutout} area on the long edges of the
+         * screen.
          *
          * <p>
          * The window must make sure that no important content overlaps with the
          * {@link DisplayCutout}.
          *
+         * <p>
+         * In this mode, the window extends under cutouts on the short edge of the display in both
+         * portrait and landscape, regardless of whether the window is hiding the system bars:<br/>
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/fullscreen_top_no_letterbox.png"
+         * height="720"
+         * alt="Screenshot of a fullscreen activity on a display with a cutout at the top edge in
+         *         portrait, no letterbox is applied."/>
+         *
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/landscape_top_no_letterbox.png"
+         * width="720"
+         * alt="Screenshot of an activity on a display with a cutout at the top edge in landscape,
+         *         no letterbox is applied."/>
+         *
+         * <p>
+         * A cutout in the corner is considered to be on the short edge: <br/>
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/fullscreen_corner_no_letterbox.png"
+         * height="720"
+         * alt="Screenshot of a fullscreen activity on a display with a cutout in the corner in
+         *         portrait, no letterbox is applied."/>
+         *
+         * <p>
+         * On the other hand, should the cutout be on the long edge of the display, a letterbox will
+         * be applied such that the window does not extend into the cutout on either long edge:
+         * <br/>
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/portrait_side_letterbox.png"
+         * height="720"
+         * alt="Screenshot of an activity on a display with a cutout on the long edge in portrait,
+         *         letterbox is applied."/>
+         *
          * @see DisplayCutout
          * @see WindowInsets#getDisplayCutout()
+         * @see #layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
-        public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;
+        public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;
 
         /**
          * The window is never allowed to overlap with the DisplayCutout area.
          *
          * <p>
-         * This should be used with windows that transiently set SYSTEM_UI_FLAG_FULLSCREEN to
-         * avoid a relayout of the window when the flag is set or cleared.
+         * This should be used with windows that transiently set
+         * {@link View#SYSTEM_UI_FLAG_FULLSCREEN} or {@link View#SYSTEM_UI_FLAG_HIDE_NAVIGATION}
+         * to avoid a relayout of the window when the respective flag is set or cleared.
          *
          * @see DisplayCutout
-         * @see View#SYSTEM_UI_FLAG_FULLSCREEN SYSTEM_UI_FLAG_FULLSCREEN
-         * @see View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+         * @see #layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
 
@@ -2344,7 +2412,7 @@
          *
          * @hide
          */
-        public int accessibilityIdOfAnchor = -1;
+        public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
 
         /**
          * The window title isn't kept in sync with what is displayed in the title bar, so we
@@ -2370,6 +2438,13 @@
         public long hideTimeoutMilliseconds = -1;
 
         /**
+         * A frame number in which changes requested in this layout will be rendered.
+         *
+         * @hide
+         */
+        public long frameNumber = -1;
+
+        /**
          * The color mode requested by this window. The target display may
          * not be able to honor the request. When the color mode is not set
          * to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2538,10 +2613,11 @@
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
-            out.writeInt(accessibilityIdOfAnchor);
+            out.writeLong(accessibilityIdOfAnchor);
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
             out.writeInt(mColorMode);
             out.writeLong(hideTimeoutMilliseconds);
+            out.writeLong(frameNumber);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -2594,10 +2670,11 @@
             hasManualSurfaceInsets = in.readInt() != 0;
             preservePreviousSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
-            accessibilityIdOfAnchor = in.readInt();
+            accessibilityIdOfAnchor = in.readLong();
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mColorMode = in.readInt();
             hideTimeoutMilliseconds = in.readLong();
+            frameNumber = in.readLong();
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2798,6 +2875,10 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
+            // The frame number changing is only relevant in the context of other
+            // changes, and so we don't need to track it with a flag.
+            frameNumber = o.frameNumber;
+
             if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
                 hasManualSurfaceInsets = o.hasManualSurfaceInsets;
                 changes |= SURFACE_INSETS_CHANGED;
@@ -2855,9 +2936,8 @@
         /**
          * @hide
          */
-        public String toString(String prefix) {
-            StringBuilder sb = new StringBuilder(256);
-            sb.append("{(");
+        public void dumpDimensions(StringBuilder sb) {
+            sb.append('(');
             sb.append(x);
             sb.append(',');
             sb.append(y);
@@ -2868,6 +2948,15 @@
             sb.append((height == MATCH_PARENT ? "fill" : (height == WRAP_CONTENT
                     ? "wrap" : String.valueOf(height))));
             sb.append(")");
+        }
+
+        /**
+         * @hide
+         */
+        public String toString(String prefix) {
+            StringBuilder sb = new StringBuilder(256);
+            sb.append('{');
+            dumpDimensions(sb);
             if (horizontalMargin != 0) {
                 sb.append(" hm=");
                 sb.append(horizontalMargin);
diff --git a/android/view/WindowManagerPolicyConstants.java b/android/view/WindowManagerPolicyConstants.java
index a6f36bb..23dc9da 100644
--- a/android/view/WindowManagerPolicyConstants.java
+++ b/android/view/WindowManagerPolicyConstants.java
@@ -51,6 +51,12 @@
     int NAV_BAR_BOTTOM = 1 << 2;
 
     /**
+     * Broadcast sent when a user activity is detected.
+     */
+    String ACTION_USER_ACTIVITY_NOTIFICATION =
+            "android.intent.action.USER_ACTIVITY_NOTIFICATION";
+
+    /**
      * Sticky broadcast of the current HDMI plugged state.
      */
     String ACTION_HDMI_PLUGGED = "android.intent.action.HDMI_PLUGGED";
diff --git a/android/view/accessibility/AccessibilityManager.java b/android/view/accessibility/AccessibilityManager.java
index dd8ba55..84b4064 100644
--- a/android/view/accessibility/AccessibilityManager.java
+++ b/android/view/accessibility/AccessibilityManager.java
@@ -17,6 +17,7 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -24,6 +25,7 @@
 import android.os.Handler;
 import android.view.IWindow;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent.EventType;
 
 import java.util.Collections;
 import java.util.List;
@@ -90,6 +92,60 @@
         public void onHighTextContrastStateChanged(boolean enabled);
     }
 
+    /**
+     * Policy to inject behavior into the accessibility manager.
+     *
+     * @hide
+     */
+    public interface AccessibilityPolicy {
+        /**
+         * Checks whether accessibility is enabled.
+         *
+         * @param accessibilityEnabled Whether the accessibility layer is enabled.
+         * @return whether accessibility is enabled.
+         */
+        boolean isEnabled(boolean accessibilityEnabled);
+
+        /**
+         * Notifies the policy for an accessibility event.
+         *
+         * @param event The event.
+         * @param accessibilityEnabled Whether the accessibility layer is enabled.
+         * @param relevantEventTypes The events relevant events.
+         * @return The event to dispatch or null.
+         */
+        @Nullable AccessibilityEvent onAccessibilityEvent(@NonNull AccessibilityEvent event,
+                boolean accessibilityEnabled, @EventType int relevantEventTypes);
+
+        /**
+         * Gets the list of relevant events.
+         *
+         * @param relevantEventTypes The relevant events.
+         * @return The relevant events to report.
+         */
+        @EventType int getRelevantEventTypes(@EventType int relevantEventTypes);
+
+        /**
+         * Gets the list of installed services to report.
+         *
+         * @param installedService The installed services.
+         * @return The services to report.
+         */
+        @NonNull List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+                @Nullable List<AccessibilityServiceInfo> installedService);
+
+        /**
+         * Gets the list of enabled accessibility services.
+         *
+         * @param feedbackTypeFlags The feedback type to query for.
+         * @param enabledService The enabled services.
+         * @return The services to report.
+         */
+        @Nullable List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+                @FeedbackType int feedbackTypeFlags,
+                @Nullable List<AccessibilityServiceInfo> enabledService);
+    }
+
     private final IAccessibilityManagerClient.Stub mClient =
             new IAccessibilityManagerClient.Stub() {
                 public void setState(int state) {
diff --git a/android/view/accessibility/AccessibilityNodeInfo.java b/android/view/accessibility/AccessibilityNodeInfo.java
index 23e7d61..4c437dd 100644
--- a/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3173,6 +3173,15 @@
      */
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        writeToParcelNoRecycle(parcel, flags);
+        // Since instances of this class are fetched via synchronous i.e. blocking
+        // calls in IPCs we always recycle as soon as the instance is marshaled.
+        recycle();
+    }
+
+    /** @hide */
+    @TestApi
+    public void writeToParcelNoRecycle(Parcel parcel, int flags) {
         // Write bit set of indices of fields with values differing from default
         long nonDefaultFields = 0;
         int fieldIndex = 0; // index of the current field
@@ -3194,7 +3203,7 @@
         fieldIndex++;
         if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
-        if (!Objects.equals(mChildNodeIds, DEFAULT.mChildNodeIds)) {
+        if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
@@ -3324,7 +3333,7 @@
                 final int actionCount = mActions.size();
 
                 int nonStandardActionCount = 0;
-                int defaultStandardActions = 0;
+                long defaultStandardActions = 0;
                 for (int i = 0; i < actionCount; i++) {
                     AccessibilityAction action = mActions.get(i);
                     if (isDefaultStandardAction(action)) {
@@ -3333,7 +3342,7 @@
                         nonStandardActionCount++;
                     }
                 }
-                parcel.writeInt(defaultStandardActions);
+                parcel.writeLong(defaultStandardActions);
 
                 parcel.writeInt(nonStandardActionCount);
                 for (int i = 0; i < actionCount; i++) {
@@ -3344,7 +3353,7 @@
                     }
                 }
             } else {
-                parcel.writeInt(0);
+                parcel.writeLong(0);
                 parcel.writeInt(0);
             }
         }
@@ -3406,10 +3415,6 @@
                         + " vs " + fieldIndex);
             }
         }
-
-        // Since instances of this class are fetched via synchronous i.e. blocking
-        // calls in IPCs we always recycle as soon as the instance is marshaled.
-        recycle();
     }
 
     /**
@@ -3535,7 +3540,7 @@
         }
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
-            final int standardActions = parcel.readInt();
+            final long standardActions = parcel.readLong();
             addStandardActions(standardActions);
             final int nonStandardActionCount = parcel.readInt();
             for (int i = 0; i < nonStandardActionCount; i++) {
@@ -3557,7 +3562,7 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             mContentDescription = parcel.readCharSequence();
         }
-        if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
+        if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
 
@@ -3616,7 +3621,7 @@
     }
 
     private static boolean isDefaultStandardAction(AccessibilityAction action) {
-        return action.mSerializationFlag != -1 && TextUtils.isEmpty(action.getLabel());
+        return (action.mSerializationFlag != -1L) && TextUtils.isEmpty(action.getLabel());
     }
 
     private static AccessibilityAction getActionSingleton(int actionId) {
@@ -3631,7 +3636,7 @@
         return null;
     }
 
-    private static AccessibilityAction getActionSingletonBySerializationFlag(int flag) {
+    private static AccessibilityAction getActionSingletonBySerializationFlag(long flag) {
         final int actions = AccessibilityAction.sStandardActions.size();
         for (int i = 0; i < actions; i++) {
             AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
@@ -3643,10 +3648,10 @@
         return null;
     }
 
-    private void addStandardActions(int serializationIdMask) {
-        int remainingIds = serializationIdMask;
+    private void addStandardActions(long serializationIdMask) {
+        long remainingIds = serializationIdMask;
         while (remainingIds > 0) {
-            final int id = 1 << Integer.numberOfTrailingZeros(remainingIds);
+            final long id = 1L << Long.numberOfTrailingZeros(remainingIds);
             remainingIds &= ~id;
             AccessibilityAction action = getActionSingletonBySerializationFlag(id);
             addAction(action);
@@ -3853,6 +3858,7 @@
         builder.append("; password: ").append(isPassword());
         builder.append("; scrollable: ").append(isScrollable());
         builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
+        builder.append("; visible: ").append(isVisibleToUser());
         builder.append("; actions: ").append(mActions);
 
         return builder.toString();
@@ -4271,7 +4277,7 @@
         private final CharSequence mLabel;
 
         /** @hide */
-        public int mSerializationFlag = -1;
+        public long mSerializationFlag = -1L;
 
         /**
          * Creates a new AccessibilityAction. For adding a standard action without a specific label,
@@ -4305,7 +4311,7 @@
         private AccessibilityAction(int standardActionId) {
             this(standardActionId, null);
 
-            mSerializationFlag = (int) bitAt(sStandardActions.size());
+            mSerializationFlag = bitAt(sStandardActions.size());
             sStandardActions.add(this);
         }
 
diff --git a/android/view/accessibility/AccessibilityViewHierarchyState.java b/android/view/accessibility/AccessibilityViewHierarchyState.java
deleted file mode 100644
index 447fafa..0000000
--- a/android/view/accessibility/AccessibilityViewHierarchyState.java
+++ /dev/null
@@ -1,61 +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.view.accessibility;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * Accessibility-related state of a {@link android.view.ViewRootImpl}
- *
- * @hide
- */
-public class AccessibilityViewHierarchyState {
-    private @Nullable SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
-    private @Nullable SendWindowContentChangedAccessibilityEvent
-            mSendWindowContentChangedAccessibilityEvent;
-
-    /**
-     * @return a {@link SendViewScrolledAccessibilityEvent}, creating one if needed
-     */
-    public @NonNull SendViewScrolledAccessibilityEvent getSendViewScrolledAccessibilityEvent() {
-        if (mSendViewScrolledAccessibilityEvent == null) {
-            mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
-        }
-        return mSendViewScrolledAccessibilityEvent;
-    }
-
-    public boolean isScrollEventSenderInitialized() {
-        return mSendViewScrolledAccessibilityEvent != null;
-    }
-
-    /**
-     * @return a {@link SendWindowContentChangedAccessibilityEvent}, creating one if needed
-     */
-    public @NonNull SendWindowContentChangedAccessibilityEvent
-            getSendWindowContentChangedAccessibilityEvent() {
-        if (mSendWindowContentChangedAccessibilityEvent == null) {
-            mSendWindowContentChangedAccessibilityEvent =
-                    new SendWindowContentChangedAccessibilityEvent();
-        }
-        return mSendWindowContentChangedAccessibilityEvent;
-    }
-
-    public boolean isWindowContentChangedEventSenderInitialized() {
-        return mSendWindowContentChangedAccessibilityEvent != null;
-    }
-}
diff --git a/android/view/accessibility/SendViewScrolledAccessibilityEvent.java b/android/view/accessibility/SendViewScrolledAccessibilityEvent.java
deleted file mode 100644
index 40a1b6a..0000000
--- a/android/view/accessibility/SendViewScrolledAccessibilityEvent.java
+++ /dev/null
@@ -1,58 +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.view.accessibility;
-
-
-import android.annotation.NonNull;
-import android.view.View;
-
-/**
- * Sender for {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
- *
- * @hide
- */
-public class SendViewScrolledAccessibilityEvent extends ThrottlingAccessibilityEventSender {
-
-    public int mDeltaX;
-    public int mDeltaY;
-
-    /**
-     * Post a scroll event to be sent for the given view
-     */
-    public void post(View source, int dx, int dy) {
-        if (!isPendingFor(source)) sendNowIfPending();
-
-        mDeltaX += dx;
-        mDeltaY += dy;
-
-        if (!isPendingFor(source)) scheduleFor(source);
-    }
-
-    @Override
-    protected void performSendEvent(@NonNull View source) {
-        AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
-        event.setScrollDeltaX(mDeltaX);
-        event.setScrollDeltaY(mDeltaY);
-        source.sendAccessibilityEventUnchecked(event);
-    }
-
-    @Override
-    protected void resetState(@NonNull View source) {
-        mDeltaX = 0;
-        mDeltaY = 0;
-    }
-}
diff --git a/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java b/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java
deleted file mode 100644
index df38fba..0000000
--- a/android/view/accessibility/SendWindowContentChangedAccessibilityEvent.java
+++ /dev/null
@@ -1,111 +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.view.accessibility;
-
-
-import static com.android.internal.util.ObjectUtils.firstNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.view.View;
-import android.view.ViewParent;
-
-import java.util.HashSet;
-
-/**
- * @hide
- */
-public class SendWindowContentChangedAccessibilityEvent
-        extends ThrottlingAccessibilityEventSender {
-
-    private int mChangeTypes = 0;
-
-    private HashSet<View> mTempHashSet;
-
-    @Override
-    protected void performSendEvent(@NonNull View source) {
-        AccessibilityEvent event = AccessibilityEvent.obtain();
-        event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-        event.setContentChangeTypes(mChangeTypes);
-        source.sendAccessibilityEventUnchecked(event);
-    }
-
-    @Override
-    protected void resetState(@Nullable View source) {
-        if (source != null) {
-            source.resetSubtreeAccessibilityStateChanged();
-        }
-        mChangeTypes = 0;
-    }
-
-    /**
-     * Post the {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event with the given
-     * {@link AccessibilityEvent#getContentChangeTypes change type} for the given view
-     */
-    public void runOrPost(View source, int changeType) {
-        if (source.getAccessibilityLiveRegion() != View.ACCESSIBILITY_LIVE_REGION_NONE) {
-            sendNowIfPending();
-            mChangeTypes = changeType;
-            sendNow(source);
-        } else {
-            mChangeTypes |= changeType;
-            scheduleFor(source);
-        }
-    }
-
-    @Override
-    protected @Nullable View tryMerge(@NonNull View oldSource, @NonNull View newSource) {
-        // If there is no common predecessor, then oldSource points to
-        // a removed view, hence in this case always prefer the newSource.
-        return firstNotNull(
-                getCommonPredecessor(oldSource, newSource),
-                newSource);
-    }
-
-    private View getCommonPredecessor(View first, View second) {
-        if (mTempHashSet == null) {
-            mTempHashSet = new HashSet<>();
-        }
-        HashSet<View> seen = mTempHashSet;
-        seen.clear();
-        View firstCurrent = first;
-        while (firstCurrent != null) {
-            seen.add(firstCurrent);
-            ViewParent firstCurrentParent = firstCurrent.getParent();
-            if (firstCurrentParent instanceof View) {
-                firstCurrent = (View) firstCurrentParent;
-            } else {
-                firstCurrent = null;
-            }
-        }
-        View secondCurrent = second;
-        while (secondCurrent != null) {
-            if (seen.contains(secondCurrent)) {
-                seen.clear();
-                return secondCurrent;
-            }
-            ViewParent secondCurrentParent = secondCurrent.getParent();
-            if (secondCurrentParent instanceof View) {
-                secondCurrent = (View) secondCurrentParent;
-            } else {
-                secondCurrent = null;
-            }
-        }
-        seen.clear();
-        return null;
-    }
-}
diff --git a/android/view/accessibility/ThrottlingAccessibilityEventSender.java b/android/view/accessibility/ThrottlingAccessibilityEventSender.java
deleted file mode 100644
index 66fa301..0000000
--- a/android/view/accessibility/ThrottlingAccessibilityEventSender.java
+++ /dev/null
@@ -1,248 +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.view.accessibility;
-
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
-import android.view.ViewRootImpl.CalledFromWrongThreadException;
-
-/**
- * A throttling {@link AccessibilityEvent} sender that relies on its currently associated
- * 'source' view's {@link View#postDelayed delayed execution} to delay and possibly
- * {@link #tryMerge merge} together any events that come in less than
- * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval
- * the configured amount of milliseconds} apart.
- *
- * The suggested usage is to create a singleton extending this class, holding any state specific to
- * the particular event type that the subclass represents, and have an 'entrypoint' method that
- * delegates to {@link #scheduleFor(View)}.
- * For example:
- *
- * {@code
- *     public void post(View view, String text, int resId) {
- *         mText = text;
- *         mId = resId;
- *         scheduleFor(view);
- *     }
- * }
- *
- * @see #scheduleFor(View)
- * @see #tryMerge(View, View)
- * @see #performSendEvent(View)
- * @hide
- */
-public abstract class ThrottlingAccessibilityEventSender {
-
-    private static final boolean DEBUG = false;
-    private static final String LOG_TAG = "ThrottlingA11ySender";
-
-    View mSource;
-    private long mLastSendTimeMillis = Long.MIN_VALUE;
-    private boolean mIsPending = false;
-
-    private final Runnable mWorker = () -> {
-        View source = mSource;
-        if (DEBUG) Log.d(LOG_TAG, thisClass() + ".run(mSource = " + source + ")");
-
-        if (!checkAndResetIsPending() || source == null) {
-            resetStateInternal();
-            return;
-        }
-
-        // Accessibility may be turned off while we were waiting
-        if (isAccessibilityEnabled(source)) {
-            mLastSendTimeMillis = SystemClock.uptimeMillis();
-            performSendEvent(source);
-        }
-        resetStateInternal();
-    };
-
-    /**
-     * Populate and send an {@link AccessibilityEvent} using the given {@code source} view, as well
-     * as any extra data from this instance's state.
-     *
-     * Send the event via {@link View#sendAccessibilityEventUnchecked(AccessibilityEvent)} or
-     * {@link View#sendAccessibilityEvent(int)} on the provided {@code source} view to allow for
-     * overrides of those methods on {@link View} subclasses to take effect, and/or make sure that
-     * an {@link View#getAccessibilityDelegate() accessibility delegate} is not ignored if any.
-     */
-    protected abstract void performSendEvent(@NonNull View source);
-
-    /**
-     * Perform optional cleanup after {@link #performSendEvent}
-     *
-     * @param source the view this event was associated with
-     */
-    protected abstract void resetState(@Nullable View source);
-
-    /**
-     * Attempt to merge the pending events for source views {@code oldSource} and {@code newSource}
-     * into one, with source set to the resulting {@link View}
-     *
-     * A result of {@code null} means merger is not possible, resulting in the currently pending
-     * event being flushed before proceeding.
-     */
-    protected @Nullable View tryMerge(@NonNull View oldSource, @NonNull View newSource) {
-        return null;
-    }
-
-    /**
-     * Schedules a {@link #performSendEvent} with the source {@link View} set to given
-     * {@code source}
-     *
-     * If an event is already scheduled a {@link #tryMerge merge} will be attempted.
-     * If merging is not possible (as indicated by the null result from {@link #tryMerge}),
-     * the currently scheduled event will be {@link #sendNow sent immediately} and the new one
-     * will be scheduled afterwards.
-     */
-    protected final void scheduleFor(@NonNull View source) {
-        if (DEBUG) Log.d(LOG_TAG, thisClass() + ".scheduleFor(source = " + source + ")");
-
-        Handler uiHandler = source.getHandler();
-        if (uiHandler == null || uiHandler.getLooper() != Looper.myLooper()) {
-            CalledFromWrongThreadException e = new CalledFromWrongThreadException(
-                    "Expected to be called from main thread but was called from "
-                            + Thread.currentThread());
-            // TODO: Throw the exception
-            Log.e(LOG_TAG, "Accessibility content change on non-UI thread. Future Android "
-                    + "versions will throw an exception.", e);
-        }
-
-        if (!isAccessibilityEnabled(source)) return;
-
-        if (mIsPending) {
-            View merged = tryMerge(mSource, source);
-            if (merged != null) {
-                setSource(merged);
-                return;
-            } else {
-                sendNow();
-            }
-        }
-
-        setSource(source);
-
-        final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastSendTimeMillis;
-        final long minEventIntervalMillis =
-                ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
-        if (timeSinceLastMillis >= minEventIntervalMillis) {
-            sendNow();
-        } else {
-            mSource.postDelayed(mWorker, minEventIntervalMillis - timeSinceLastMillis);
-        }
-    }
-
-    static boolean isAccessibilityEnabled(@NonNull View contextProvider) {
-        return AccessibilityManager.getInstance(contextProvider.getContext()).isEnabled();
-    }
-
-    protected final void sendNow(View source) {
-        setSource(source);
-        sendNow();
-    }
-
-    private void sendNow() {
-        mSource.removeCallbacks(mWorker);
-        mWorker.run();
-    }
-
-    /**
-     * Flush the event if one is pending
-     */
-    public void sendNowIfPending() {
-        if (mIsPending) sendNow();
-    }
-
-    /**
-     * Cancel the event if one is pending and is for the given view
-     */
-    public final void cancelIfPendingFor(@NonNull View source) {
-        if (isPendingFor(source)) cancelIfPending(this);
-    }
-
-    /**
-     * @return whether an event is currently pending for the given source view
-     */
-    protected final boolean isPendingFor(@Nullable View source) {
-        return mIsPending && mSource == source;
-    }
-
-    /**
-     * Cancel the event if one is not null and pending
-     */
-    public static void cancelIfPending(@Nullable ThrottlingAccessibilityEventSender sender) {
-        if (sender == null || !sender.checkAndResetIsPending()) return;
-        sender.mSource.removeCallbacks(sender.mWorker);
-        sender.resetStateInternal();
-    }
-
-    void resetStateInternal() {
-        if (DEBUG) Log.d(LOG_TAG, thisClass() + ".resetStateInternal()");
-
-        resetState(mSource);
-        setSource(null);
-    }
-
-    boolean checkAndResetIsPending() {
-        if (mIsPending) {
-            mIsPending = false;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private void setSource(@Nullable View source) {
-        if (DEBUG) Log.d(LOG_TAG, thisClass() + ".setSource(" + source + ")");
-
-        if (source == null && mIsPending) {
-            Log.e(LOG_TAG, "mSource nullified while callback still pending: " + this);
-            return;
-        }
-
-        if (source != null && !mIsPending) {
-            // At most one can be pending at any given time
-            View oldSource = mSource;
-            if (oldSource != null) {
-                ViewRootImpl viewRootImpl = oldSource.getViewRootImpl();
-                if (viewRootImpl != null) {
-                    viewRootImpl.flushPendingAccessibilityEvents();
-                }
-            }
-            mIsPending = true;
-        }
-        mSource = source;
-    }
-
-    String thisClass() {
-        return getClass().getSimpleName();
-    }
-
-    @Override
-    public String toString() {
-        return thisClass() + "(" + mSource + ")";
-    }
-
-}
diff --git a/android/view/animation/Animation.java b/android/view/animation/Animation.java
index 474db12..64686dd 100644
--- a/android/view/animation/Animation.java
+++ b/android/view/animation/Animation.java
@@ -206,6 +206,8 @@
      */
     private boolean mDetachWallpaper = false;
 
+    private boolean mShowWallpaper;
+
     private boolean mMore = true;
     private boolean mOneMoreTime = true;
 
@@ -253,7 +255,10 @@
 
         setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0));
 
-        setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));
+        setDetachWallpaper(
+                a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));
+        setShowWallpaper(
+                a.getBoolean(com.android.internal.R.styleable.Animation_showWallpaper, false));
 
         final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
 
@@ -661,6 +666,18 @@
     }
 
     /**
+     * If this animation is run as a window animation, this will make the wallpaper visible behind
+     * the animation.
+     *
+     * @param showWallpaper Whether the wallpaper should be shown during the animation.
+     * @attr ref android.R.styleable#Animation_detachWallpaper
+     * @hide
+     */
+    public void setShowWallpaper(boolean showWallpaper) {
+        mShowWallpaper = showWallpaper;
+    }
+
+    /**
      * Gets the acceleration curve type for this animation.
      *
      * @return the {@link Interpolator} associated to this animation
@@ -775,6 +792,16 @@
     }
 
     /**
+     * @return If run as a window animation, returns whether the wallpaper will be shown behind
+     *         during the animation.
+     * @attr ref android.R.styleable#Animation_showWallpaper
+     * @hide
+     */
+    public boolean getShowWallpaper() {
+        return mShowWallpaper;
+    }
+
+    /**
      * <p>Indicates whether or not this animation will affect the transformation
      * matrix. For instance, a fade animation will not affect the matrix whereas
      * a scale animation will.</p>
diff --git a/android/view/animation/AnimationUtils.java b/android/view/animation/AnimationUtils.java
index 990fbdb..29f8442 100644
--- a/android/view/animation/AnimationUtils.java
+++ b/android/view/animation/AnimationUtils.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AnimRes;
 import android.annotation.InterpolatorRes;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -58,14 +59,43 @@
         }
     };
 
-    /** @hide */
+    /**
+     * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current
+     * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses
+     * during a vsync update are synchronized to the timestamp of the vsync.
+     *
+     * It is also exposed to tests to allow for rapid, flake-free headless testing.
+     *
+     * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to
+     * progress. Failing to do this will result in stuck animations, scrolls, and flings.
+     *
+     * Note that time is not allowed to "rewind" and must perpetually flow forward. So the
+     * lock may fail if the time is in the past from a previously returned value, however
+     * time will be frozen for the duration of the lock. The clock is a thread-local, so
+     * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and
+     * {@link #currentAnimationTimeMillis()} are all called on the same thread.
+     *
+     * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()}
+     * will unlock the clock for everyone on the same thread. It is therefore recommended
+     * for tests to use their own thread to ensure that there is no collision with any existing
+     * {@link android.view.Choreographer} instance.
+     *
+     * @hide
+     * */
+    @TestApi
     public static void lockAnimationClock(long vsyncMillis) {
         AnimationState state = sAnimationState.get();
         state.animationClockLocked = true;
         state.currentVsyncTimeMillis = vsyncMillis;
     }
 
-    /** @hide */
+    /**
+     * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called
+     * to allow the animation clock to self-update.
+     *
+     * @hide
+     */
+    @TestApi
     public static void unlockAnimationClock() {
         sAnimationState.get().animationClockLocked = false;
     }
diff --git a/android/view/autofill/AutofillId.java b/android/view/autofill/AutofillId.java
index 5ce2421..cb1d89c 100644
--- a/android/view/autofill/AutofillId.java
+++ b/android/view/autofill/AutofillId.java
@@ -38,6 +38,7 @@
     }
 
     /** @hide */
+    @TestApi
     public AutofillId(AutofillId parent, int virtualChildId) {
         mVirtual = true;
         mViewId = parent.mViewId;
diff --git a/android/view/autofill/AutofillManager.java b/android/view/autofill/AutofillManager.java
index 4b24a71..88300db 100644
--- a/android/view/autofill/AutofillManager.java
+++ b/android/view/autofill/AutofillManager.java
@@ -20,14 +20,18 @@
 import static android.view.autofill.Helper.sDebug;
 import static android.view.autofill.Helper.sVerbose;
 
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.SystemService;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.metrics.LogMaker;
 import android.os.Bundle;
@@ -41,13 +45,24 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.Choreographer;
+import android.view.KeyEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -58,7 +73,7 @@
 import java.util.List;
 import java.util.Objects;
 
-// TODO: use java.lang.ref.Cleaner once Android supports Java 9
+//TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
 
 /**
@@ -122,6 +137,7 @@
  * <p>It is safe to call into its methods from any thread.
  */
 @SystemService(Context.AUTOFILL_MANAGER_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
 public final class AutofillManager {
 
     private static final String TAG = "AutofillManager";
@@ -178,7 +194,6 @@
     private static final String STATE_TAG = "android:state";
     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
 
-
     /** @hide */ public static final int ACTION_START_SESSION = 1;
     /** @hide */ public static final int ACTION_VIEW_ENTERED =  2;
     /** @hide */ public static final int ACTION_VIEW_EXITED = 3;
@@ -259,6 +274,16 @@
     public static final int STATE_DISABLED_BY_SERVICE = 4;
 
     /**
+     * Same as {@link #STATE_UNKNOWN}, but used on
+     * {@link AutofillManagerClient#setSessionFinished(int)} when the session was finished because
+     * the URL bar changed on client mode
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN_COMPAT_MODE = 5;
+
+
+    /**
      * Timeout in ms for calls to the field classification service.
      * @hide
      */
@@ -343,6 +368,16 @@
     @GuardedBy("mLock")
     @Nullable private ArraySet<AutofillId> mFillableIds;
 
+    /** id of last requested autofill ui */
+    @Nullable private AutofillId mIdShownFillUi;
+
+    /**
+     * Views that were already "entered" - if they're entered again when the session is not active,
+     * they're ignored
+     * */
+    @GuardedBy("mLock")
+    @Nullable private ArraySet<AutofillId> mEnteredIds;
+
     /** If set, session is commited when the field is clicked. */
     @GuardedBy("mLock")
     @Nullable private AutofillId mSaveTriggerId;
@@ -355,6 +390,10 @@
     @GuardedBy("mLock")
     private boolean mSaveOnFinish;
 
+    /** If compatibility mode is enabled - this is a bridge to interact with a11y */
+    @GuardedBy("mLock")
+    private CompatibilityBridge mCompatibilityBridge;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -364,13 +403,13 @@
          * @param intent The authentication intent.
          * @param fillInIntent The authentication fill-in intent.
          */
-        void autofillCallbackAuthenticate(int authenticationId, IntentSender intent,
+        void autofillClientAuthenticate(int authenticationId, IntentSender intent,
                 Intent fillInIntent);
 
         /**
          * Tells the client this manager has state to be reset.
          */
-        void autofillCallbackResetableStateAvailable();
+        void autofillClientResetableStateAvailable();
 
         /**
          * Request showing the autofill UI.
@@ -382,29 +421,43 @@
          * @param presenter The presenter that controls the fill UI window.
          * @return Whether the UI was shown.
          */
-        boolean autofillCallbackRequestShowFillUi(@NonNull View anchor, int width, int height,
+        boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width, int height,
                 @Nullable Rect virtualBounds, IAutofillWindowPresenter presenter);
 
         /**
+         * Dispatch unhandled keyevent from Autofill window
+         * @param anchor The real view the UI needs to anchor to.
+         * @param keyEvent Unhandled KeyEvent from autofill window.
+         */
+        void autofillClientDispatchUnhandledKey(@NonNull View anchor, @NonNull KeyEvent keyEvent);
+
+        /**
          * Request hiding the autofill UI.
          *
          * @return Whether the UI was hidden.
          */
-        boolean autofillCallbackRequestHideFillUi();
+        boolean autofillClientRequestHideFillUi();
+
+        /**
+         * Gets whether the fill UI is currenlty being shown.
+         *
+         * @return Whether the fill UI is currently being shown
+         */
+        boolean autofillClientIsFillUiShowing();
 
         /**
          * Checks if views are currently attached and visible.
          *
          * @return And array with {@code true} iff the view is attached or visible
          */
-        @NonNull boolean[] getViewVisibility(@NonNull int[] viewId);
+        @NonNull boolean[] autofillClientGetViewVisibility(@NonNull AutofillId[] autofillIds);
 
         /**
          * Checks is the client is currently visible as understood by autofill.
          *
          * @return {@code true} if the client is currently visible
          */
-        boolean isVisibleForAutofill();
+        boolean autofillClientIsVisibleForAutofill();
 
         /**
          * Client might disable enter/exit event e.g. when activity is paused.
@@ -414,30 +467,61 @@
         /**
          * Finds views by traversing the hierarchies of the client.
          *
-         * @param viewIds The autofill ids of the views to find
+         * @param autofillIds The autofill ids of the views to find
          *
          * @return And array containing the views (empty if no views found).
          */
-        @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
+        @NonNull View[] autofillClientFindViewsByAutofillIdTraversal(
+                @NonNull AutofillId[] autofillIds);
 
         /**
          * Finds a view by traversing the hierarchies of the client.
          *
-         * @param viewId The autofill id of the views to find
+         * @param autofillId The autofill id of the views to find
          *
          * @return The view, or {@code null} if not found
          */
-        @Nullable View findViewByAutofillIdTraversal(int viewId);
+        @Nullable View autofillClientFindViewByAutofillIdTraversal(@NonNull AutofillId autofillId);
+
+        /**
+         * Finds a view by a11y id in a given client window.
+         *
+         * @param viewId The accessibility id of the views to find
+         * @param windowId The accessibility window id where to search
+         *
+         * @return The view, or {@code null} if not found
+         */
+        @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId);
 
         /**
          * Runs the specified action on the UI thread.
          */
-        void runOnUiThread(Runnable action);
+        void autofillClientRunOnUiThread(Runnable action);
 
         /**
          * Gets the complete component name of this client.
          */
-        ComponentName getComponentName();
+        ComponentName autofillClientGetComponentName();
+
+        /**
+         * Gets the activity token
+         */
+        @Nullable IBinder autofillClientGetActivityToken();
+
+        /**
+          * @return Whether compatibility mode is enabled.
+          */
+        boolean autofillClientIsCompatibilityModeEnabled();
+
+        /**
+         * Gets the next unique autofill ID.
+         *
+         * <p>Typically used to manage views whose content is recycled - see
+         * {@link View#setAutofillId(AutofillId)} for more info.
+         *
+         * @return An ID that is unique in the activity.
+         */
+        @Nullable AutofillId autofillClientGetNextAutofillId();
     }
 
     /**
@@ -449,6 +533,19 @@
     }
 
     /**
+     * @hide
+     */
+    public void enableCompatibilityMode() {
+        synchronized (mLock) {
+            // The accessibility manager is a singleton so we may need to plug
+            // different bridge based on which activity is currently focused
+            // in the current process. Since compat would be rarely used, just
+            // create and register a new instance every time.
+            mCompatibilityBridge = new CompatibilityBridge();
+        }
+    }
+
+    /**
      * Restore state after activity lifecycle
      *
      * @param savedInstanceState The state to be restored
@@ -477,7 +574,8 @@
                 if (client != null) {
                     try {
                         final boolean sessionWasRestored = mService.restoreSession(mSessionId,
-                                mContext.getActivityToken(), mServiceClient.asBinder());
+                                client.autofillClientGetActivityToken(),
+                                mServiceClient.asBinder());
 
                         if (!sessionWasRestored) {
                             Log.w(TAG, "Session " + mSessionId + " could not be restored");
@@ -488,7 +586,7 @@
                                 Log.d(TAG, "session " + mSessionId + " was restored");
                             }
 
-                            client.autofillCallbackResetableStateAvailable();
+                            client.autofillClientResetableStateAvailable();
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "Could not figure out if there was an autofill session", e);
@@ -501,22 +599,29 @@
     /**
      * Called once the client becomes visible.
      *
-     * @see AutofillClient#isVisibleForAutofill()
+     * @see AutofillClient#autofillClientIsVisibleForAutofill()
      *
      * {@hide}
      */
     public void onVisibleForAutofill() {
-        synchronized (mLock) {
-            if (mEnabled && isActiveLocked() && mTrackedViews != null) {
-                mTrackedViews.onVisibleForAutofillLocked();
+        // This gets called when the client just got visible at which point the visibility
+        // of the tracked views may not have been computed (due to a pending layout, etc).
+        // While generally we have no way to know when the UI has settled. We will evaluate
+        // the tracked views state at the end of next frame to guarantee that everything
+        // that may need to be laid out is laid out.
+        Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
+            synchronized (mLock) {
+                if (mEnabled && isActiveLocked() && mTrackedViews != null) {
+                    mTrackedViews.onVisibleForAutofillChangedLocked();
+                }
             }
-        }
+        }, null);
     }
 
     /**
      * Called once the client becomes invisible.
      *
-     * @see AutofillClient#isVisibleForAutofill()
+     * @see AutofillClient#autofillClientIsVisibleForAutofill()
      *
      * {@hide}
      */
@@ -551,6 +656,14 @@
     }
 
     /**
+     * @hide
+     */
+    @GuardedBy("mLock")
+    public boolean isCompatibilityModeEnabledLocked() {
+        return mCompatibilityBridge != null;
+    }
+
+    /**
      * Checks whether autofill is enabled for the current user.
      *
      * <p>Typically used to determine whether the option to explicitly request autofill should
@@ -636,24 +749,37 @@
         notifyViewEntered(view, 0);
     }
 
-    private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
+    @GuardedBy("mLock")
+    private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
         if (isDisabledByServiceLocked()) {
             if (sVerbose) {
-                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
-                        + ") on state " + getStateAsStringLocked());
+                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                        + ") on state " + getStateAsStringLocked() + " because disabled by svc");
             }
             return true;
         }
-        if (sVerbose && isFinishedLocked()) {
-            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
-                    + ") on state " + getStateAsStringLocked());
+        if (isFinishedLocked()) {
+            // Session already finished: ignore if automatic request and view already entered
+            if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
+                    && mEnteredIds.contains(id)) {
+                if (sVerbose) {
+                    Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                            + ") on state " + getStateAsStringLocked()
+                            + " because view was already entered: " + mEnteredIds);
+                }
+                return true;
+            }
+        }
+        if (sVerbose) {
+            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                    + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
         }
         return false;
     }
 
     private boolean isClientVisibleForAutofillLocked() {
         final AutofillClient client = getClient();
-        return client != null && client.isVisibleForAutofill();
+        return client != null && client.autofillClientIsVisibleForAutofill();
     }
 
     private boolean isClientDisablingEnterExitEvent() {
@@ -676,8 +802,10 @@
     }
 
     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+    @GuardedBy("mLock")
     private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
-        if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+        final AutofillId id = view.getAutofillId();
+        if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
 
         AutofillCallback callback = null;
 
@@ -690,7 +818,6 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view);
                 final AutofillValue value = view.getAutofillValue();
 
                 if (!isActiveLocked()) {
@@ -700,6 +827,7 @@
                     // Update focus on existing session.
                     updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
                 }
+                addEnteredIdLocked(id);
             }
         }
         return callback;
@@ -719,13 +847,14 @@
         }
     }
 
+    @GuardedBy("mLock")
     void notifyViewExitedLocked(@NonNull View view) {
         ensureServiceClientAddedIfNeededLocked();
 
         if (mEnabled && isActiveLocked()) {
             // dont notify exited when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view);
+                final AutofillId id = view.getAutofillId();
 
                 // Update focus on existing session.
                 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
@@ -768,6 +897,7 @@
             if (mEnabled && isActiveLocked()) {
                 final AutofillId id = virtual ? getAutofillId(view, virtualId)
                         : view.getAutofillId();
+                if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
                 if (!isVisible && mFillableIds != null) {
                     if (mFillableIds.contains(id)) {
                         if (sDebug) Log.d(TAG, "Hidding UI when view " + id + " became invisible");
@@ -776,6 +906,8 @@
                 }
                 if (mTrackedViews != null) {
                     mTrackedViews.notifyViewVisibilityChangedLocked(id, isVisible);
+                } else if (sVerbose) {
+                    Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
                 }
             }
         }
@@ -820,10 +952,12 @@
     }
 
     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+    @GuardedBy("mLock")
     private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
                                                      int flags) {
+        final AutofillId id = getAutofillId(view, virtualId);
         AutofillCallback callback = null;
-        if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+        if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
 
         ensureServiceClientAddedIfNeededLocked();
 
@@ -834,8 +968,6 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view, virtualId);
-
                 if (!isActiveLocked()) {
                     // Starts new session.
                     startSessionLocked(id, bounds, null, flags);
@@ -843,11 +975,20 @@
                     // Update focus on existing session.
                     updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
                 }
+                addEnteredIdLocked(id);
             }
         }
         return callback;
     }
 
+    @GuardedBy("mLock")
+    private void addEnteredIdLocked(@NonNull AutofillId id) {
+        if (mEnteredIds == null) {
+            mEnteredIds = new ArraySet<>(1);
+        }
+        mEnteredIds.add(id);
+    }
+
     /**
      * Called when a virtual view that supports autofill is exited.
      *
@@ -855,6 +996,7 @@
      * @param virtualId id identifying the virtual child inside the parent view.
      */
     public void notifyViewExited(@NonNull View view, int virtualId) {
+        if (sVerbose) Log.v(TAG, "notifyViewExited(" + view.getAutofillId() + ", " + virtualId);
         if (!hasAutofillFeature()) {
             return;
         }
@@ -863,6 +1005,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
         ensureServiceClientAddedIfNeededLocked();
 
@@ -896,7 +1039,7 @@
             if (mLastAutofilledData == null) {
                 view.setAutofilled(false);
             } else {
-                id = getAutofillId(view);
+                id = view.getAutofillId();
                 if (mLastAutofilledData.containsKey(id)) {
                     value = view.getAutofillValue();
                     valueWasRead = true;
@@ -913,15 +1056,15 @@
             }
 
             if (!mEnabled || !isActiveLocked()) {
-                if (sVerbose && mEnabled) {
-                    Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
-                            + getStateAsStringLocked());
+                if (sVerbose) {
+                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+                            + "): ignoring on state " + getStateAsStringLocked());
                 }
                 return;
             }
 
             if (id == null) {
-                id = getAutofillId(view);
+                id = view.getAutofillId();
             }
 
             if (!valueWasRead) {
@@ -945,6 +1088,10 @@
         }
         synchronized (mLock) {
             if (!mEnabled || !isActiveLocked()) {
+                if (sVerbose) {
+                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
+                            + "): ignoring on state " + getStateAsStringLocked());
+                }
                 return;
             }
 
@@ -953,18 +1100,35 @@
         }
     }
 
+    /**
+     * Called to indicate a {@link View} is clicked.
+     *
+     * @param view view that has been clicked.
+     */
+    public void notifyViewClicked(@NonNull View view) {
+        notifyViewClicked(view.getAutofillId());
+    }
 
     /**
-     * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
+     * Called to indicate a virtual view has been clicked.
      *
-     * @hide
+     * @param view the virtual view parent.
+     * @param virtualId id identifying the virtual child inside the parent view.
      */
-    public void notifyViewClicked(View view) {
-        final AutofillId id = view.getAutofillId();
+    public void notifyViewClicked(@NonNull View view, int virtualId) {
+        notifyViewClicked(getAutofillId(view, virtualId));
+    }
 
+    private void notifyViewClicked(AutofillId id) {
+        if (!hasAutofillFeature()) {
+            return;
+        }
         if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
 
         synchronized (mLock) {
+            if (!mEnabled || !isActiveLocked()) {
+                return;
+            }
             if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
                 if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
                 commitLocked();
@@ -979,16 +1143,16 @@
      *
      * @hide
      */
-    public void onActivityFinished() {
+    public void onActivityFinishing() {
         if (!hasAutofillFeature()) {
             return;
         }
         synchronized (mLock) {
             if (mSaveOnFinish) {
-                if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
+                if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
                 commitLocked();
             } else {
-                if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
+                if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
                 cancelLocked();
             }
         }
@@ -1009,11 +1173,13 @@
         if (!hasAutofillFeature()) {
             return;
         }
+        if (sVerbose) Log.v(TAG, "commit() called by app");
         synchronized (mLock) {
             commitLocked();
         }
     }
 
+    @GuardedBy("mLock")
     private void commitLocked() {
         if (!mEnabled && !isActiveLocked()) {
             return;
@@ -1042,6 +1208,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void cancelLocked() {
         if (!mEnabled && !isActiveLocked()) {
             return;
@@ -1099,6 +1266,30 @@
     }
 
     /**
+     * Gets the id of the {@link UserData} used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>.
+     *
+     * <p>This method is useful when the service must check the status of the {@link UserData} in
+     * the device without fetching the whole object.
+     *
+     * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+     * and it's ignored if the caller currently doesn't have an enabled autofill service for
+     * the user.
+     *
+     * @return id of the {@link UserData} previously set by {@link #setUserData(UserData)}
+     * or {@code null} if it was reset or if the caller currently does not have an enabled autofill
+     * service for the user.
+     */
+    @Nullable public String getUserDataId() {
+        try {
+            return mService.getUserDataId();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
      * Gets the user data used for
      * <a href="AutofillService.html#FieldClassification">field classification</a>.
      *
@@ -1119,7 +1310,7 @@
     }
 
     /**
-     * Sets the user data used for
+     * Sets the {@link UserData} used for
      * <a href="AutofillService.html#FieldClassification">field classification</a>
      *
      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
@@ -1226,6 +1417,15 @@
         return client;
     }
 
+    /**
+     * Check if autofill ui is showing, must be called on UI thread.
+     * @hide
+     */
+    public boolean isAutofillUiShowing() {
+        final AutofillClient client = mContext.getAutofillClient();
+        return client != null && client.autofillClientIsFillUiShowing();
+    }
+
     /** @hide */
     public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
         if (!hasAutofillFeature()) {
@@ -1273,19 +1473,41 @@
         }
     }
 
-    private static AutofillId getAutofillId(View view) {
-        return new AutofillId(view.getAutofillViewId());
+    /**
+     * Gets the next unique autofill ID for the activity context.
+     *
+     * <p>Typically used to manage views whose content is recycled - see
+     * {@link View#setAutofillId(AutofillId)} for more info.
+     *
+     * @return An ID that is unique in the activity, or {@code null} if autofill is not supported in
+     * the {@link Context} associated with this {@link AutofillManager}.
+     */
+    @Nullable
+    public AutofillId getNextAutofillId() {
+        final AutofillClient client = getClient();
+        if (client == null) return null;
+
+        final AutofillId id = client.autofillClientGetNextAutofillId();
+
+        if (id == null && sDebug) {
+            Log.d(TAG, "getNextAutofillId(): client " + client + " returned null");
+        }
+
+        return id;
     }
 
     private static AutofillId getAutofillId(View parent, int virtualId) {
         return new AutofillId(parent.getAutofillViewId(), virtualId);
     }
 
+    @GuardedBy("mLock")
     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
             @NonNull AutofillValue value, int flags) {
         if (sVerbose) {
             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
-                    + ", flags=" + flags + ", state=" + getStateAsStringLocked());
+                    + ", flags=" + flags + ", state=" + getStateAsStringLocked()
+                    + ", compatMode=" + isCompatibilityModeEnabledLocked()
+                    + ", enteredIds=" + mEnteredIds);
         }
         if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
             if (sVerbose) {
@@ -1296,20 +1518,22 @@
         }
         try {
             final AutofillClient client = getClient();
-            if (client == null) return; // NOTE: getClient() already logd it..
+            if (client == null) return; // NOTE: getClient() already logged it..
 
-            mSessionId = mService.startSession(mContext.getActivityToken(),
+            mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
-                    mCallback != null, flags, client.getComponentName());
+                    mCallback != null, flags, client.autofillClientGetComponentName(),
+                    isCompatibilityModeEnabledLocked());
             if (mSessionId != NO_SESSION) {
                 mState = STATE_ACTIVE;
             }
-            client.autofillCallbackResetableStateAvailable();
+            client.autofillClientResetableStateAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
+    @GuardedBy("mLock")
     private void finishSessionLocked() {
         if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
 
@@ -1321,9 +1545,10 @@
             throw e.rethrowFromSystemServer();
         }
 
-        resetSessionLocked();
+        resetSessionLocked(/* resetEnteredIds= */ true);
     }
 
+    @GuardedBy("mLock")
     private void cancelSessionLocked() {
         if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
 
@@ -1335,20 +1560,26 @@
             throw e.rethrowFromSystemServer();
         }
 
-        resetSessionLocked();
+        resetSessionLocked(/* resetEnteredIds= */ true);
     }
 
-    private void resetSessionLocked() {
+    @GuardedBy("mLock")
+    private void resetSessionLocked(boolean resetEnteredIds) {
         mSessionId = NO_SESSION;
         mState = STATE_UNKNOWN;
         mTrackedViews = null;
         mFillableIds = null;
         mSaveTriggerId = null;
+        mIdShownFillUi = null;
+        if (resetEnteredIds) {
+            mEnteredIds = null;
+        }
     }
 
+    @GuardedBy("mLock")
     private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
             int flags) {
-        if (sVerbose && action != ACTION_VIEW_EXITED) {
+        if (sVerbose) {
             Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
                     + ", value=" + value + ", action=" + action + ", flags=" + flags);
         }
@@ -1359,14 +1590,16 @@
                 final AutofillClient client = getClient();
                 if (client == null) return; // NOTE: getClient() already logd it..
 
-                final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
+                final int newId = mService.updateOrRestartSession(
+                        client.autofillClientGetActivityToken(),
                         mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
-                        mCallback != null, flags, client.getComponentName(), mSessionId, action);
+                        mCallback != null, flags, client.autofillClientGetComponentName(),
+                        mSessionId, action, isCompatibilityModeEnabledLocked());
                 if (newId != mSessionId) {
                     if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
                     mSessionId = newId;
                     mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
-                    client.autofillCallbackResetableStateAvailable();
+                    client.autofillClientResetableStateAvailable();
                 }
             } else {
                 mService.updateSession(mSessionId, id, bounds, value, action, flags,
@@ -1378,6 +1611,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void ensureServiceClientAddedIfNeededLocked() {
         if (getClient() == null) {
             return;
@@ -1465,9 +1699,10 @@
                 AutofillClient client = getClient();
 
                 if (client != null) {
-                    if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
-                            anchorBounds, presenter) && mCallback != null) {
+                    if (client.autofillClientRequestShowFillUi(anchor, width, height,
+                            anchorBounds, presenter)) {
                         callback = mCallback;
+                        mIdShownFillUi = id;
                     }
                 }
             }
@@ -1492,7 +1727,25 @@
                     // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
                     // before onAuthenticationResult()
                     mOnInvisibleCalled = false;
-                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
+                    client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
+                }
+            }
+        }
+    }
+
+    private void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent keyEvent) {
+        final View anchor = findView(id);
+        if (anchor == null) {
+            return;
+        }
+
+        AutofillCallback callback = null;
+        synchronized (mLock) {
+            if (mSessionId == sessionId) {
+                AutofillClient client = getClient();
+
+                if (client != null) {
+                    client.autofillClientDispatchUnhandledKey(anchor, keyEvent);
                 }
             }
         }
@@ -1515,7 +1768,7 @@
             mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
             if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
                 // Reset the session state
-                resetSessionLocked();
+                resetSessionLocked(/* resetEnteredIds= */ true);
             }
             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
                 // Reset connection to system
@@ -1543,7 +1796,7 @@
                 if (mLastAutofilledData == null) {
                     mLastAutofilledData = new ParcelableMap(1);
                 }
-                mLastAutofilledData.put(getAutofillId(view), targetValue);
+                mLastAutofilledData.put(view.getAutofillId(), targetValue);
             }
             view.setAutofilled(true);
         }
@@ -1563,7 +1816,10 @@
             final int itemCount = ids.size();
             int numApplied = 0;
             ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
-            final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
+            final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
+                    Helper.toArray(ids));
+
+            ArrayList<AutofillId> failedIds = null;
 
             for (int i = 0; i < itemCount; i++) {
                 final AutofillId id = ids.get(i);
@@ -1571,7 +1827,14 @@
                 final int viewId = id.getViewId();
                 final View view = views[i];
                 if (view == null) {
-                    Log.w(TAG, "autofill(): no View with id " + viewId);
+                    // Most likely view has been removed after the initial request was sent to the
+                    // the service; this is fine, but we need to update the view status in the
+                    // server side so it can be triggered again.
+                    Log.d(TAG, "autofill(): no View with id " + id);
+                    if (failedIds == null) {
+                        failedIds = new ArrayList<>();
+                    }
+                    failedIds.add(id);
                     continue;
                 }
                 if (id.isVirtual()) {
@@ -1605,12 +1868,28 @@
                 }
             }
 
+            if (failedIds != null) {
+                if (sVerbose) {
+                    Log.v(TAG, "autofill(): total failed views: " + failedIds);
+                }
+                try {
+                    mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
+                } catch (RemoteException e) {
+                    // In theory, we could ignore this error since it's not a big deal, but
+                    // in reality, we rather crash the app anyways, as the failure could be
+                    // a consequence of something going wrong on the server side...
+                    e.rethrowFromSystemServer();
+                }
+            }
+
             if (virtualValues != null) {
                 for (int i = 0; i < virtualValues.size(); i++) {
                     final View parent = virtualValues.keyAt(i);
                     final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
                     parent.autofill(childrenValues);
                     numApplied += childrenValues.size();
+                    // TODO: we should provide a callback so the parent can call failures; something
+                    // like notifyAutofillFailed(View view, int[] childrenIds);
                 }
             }
 
@@ -1703,22 +1982,44 @@
      * Marks the state of the session as finished.
      *
      * @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
-     *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
-     *  {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
-     *  requests for the activity).
+     *  FillResponse), {@link #STATE_UNKNOWN} (because the session was removed),
+     *  {@link #STATE_UNKNOWN_COMPAT_MODE} (beucase the session was finished when the URL bar
+     *  changed on compat mode), or {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service
+     *  disabled further autofill requests for the activity).
      */
     private void setSessionFinished(int newState) {
         synchronized (mLock) {
-            if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
-            resetSessionLocked();
-            mState = newState;
+            if (sVerbose) {
+                Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to "
+                        + getStateAsString(newState));
+            }
+            if (newState == STATE_UNKNOWN_COMPAT_MODE) {
+                resetSessionLocked(/* resetEnteredIds= */ true);
+                mState = STATE_UNKNOWN;
+            } else {
+                resetSessionLocked(/* resetEnteredIds= */ false);
+                mState = newState;
+            }
         }
     }
 
-    private void requestHideFillUi(AutofillId id) {
-        final View anchor = findView(id);
+    /** @hide */
+    public void requestHideFillUi() {
+        requestHideFillUi(mIdShownFillUi, true);
+    }
+
+    private void requestHideFillUi(AutofillId id, boolean force) {
+        final View anchor = id == null ? null : findView(id);
         if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
         if (anchor == null) {
+            if (force) {
+                // When user taps outside autofill window, force to close fill ui even id does
+                // not match.
+                AutofillClient client = getClient();
+                if (client != null) {
+                    client.autofillClientRequestHideFillUi();
+                }
+            }
             return;
         }
         requestHideFillUi(id, anchor);
@@ -1734,7 +2035,8 @@
             //    service being uninstalled and the UI being dismissed.
             AutofillClient client = getClient();
             if (client != null) {
-                if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
+                if (client.autofillClientRequestHideFillUi()) {
+                    mIdShownFillUi = null;
                     callback = mCallback;
                 }
             }
@@ -1783,35 +2085,6 @@
     }
 
     /**
-     * Get an array of viewIds from a List of {@link AutofillId}.
-     *
-     * @param autofillIds The autofill ids to convert
-     *
-     * @return The array of viewIds.
-     */
-    // TODO: move to Helper as static method
-    @NonNull private int[] getViewIds(@NonNull AutofillId[] autofillIds) {
-        final int numIds = autofillIds.length;
-        final int[] viewIds = new int[numIds];
-        for (int i = 0; i < numIds; i++) {
-            viewIds[i] = autofillIds[i].getViewId();
-        }
-
-        return viewIds;
-    }
-
-    // TODO: move to Helper as static method
-    @NonNull private int[] getViewIds(@NonNull List<AutofillId> autofillIds) {
-        final int numIds = autofillIds.size();
-        final int[] viewIds = new int[numIds];
-        for (int i = 0; i < numIds; i++) {
-            viewIds[i] = autofillIds.get(i).getViewId();
-        }
-
-        return viewIds;
-    }
-
-    /**
      * Find a single view by its id.
      *
      * @param autofillId The autofill id of the view
@@ -1820,12 +2093,10 @@
      */
     private View findView(@NonNull AutofillId autofillId) {
         final AutofillClient client = getClient();
-
-        if (client == null) {
-            return null;
+        if (client != null) {
+            return client.autofillClientFindViewByAutofillIdTraversal(autofillId);
         }
-
-        return client.findViewByAutofillIdTraversal(autofillId.getViewId());
+        return null;
     }
 
     /** @hide */
@@ -1869,37 +2140,51 @@
             pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
         }
         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
+        pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
+        pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
+                isCompatibilityModeEnabledLocked());
         pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
         pw.print(" verbose: "); pw.println(sVerbose);
     }
 
+    @GuardedBy("mLock")
     private String getStateAsStringLocked() {
-        switch (mState) {
+        return getStateAsString(mState);
+    }
+
+    @NonNull
+    private static String getStateAsString(int state) {
+        switch (state) {
             case STATE_UNKNOWN:
-                return "STATE_UNKNOWN";
+                return "UNKNOWN";
             case STATE_ACTIVE:
-                return "STATE_ACTIVE";
+                return "ACTIVE";
             case STATE_FINISHED:
-                return "STATE_FINISHED";
+                return "FINISHED";
             case STATE_SHOWING_SAVE_UI:
-                return "STATE_SHOWING_SAVE_UI";
+                return "SHOWING_SAVE_UI";
             case STATE_DISABLED_BY_SERVICE:
-                return "STATE_DISABLED_BY_SERVICE";
+                return "DISABLED_BY_SERVICE";
+            case STATE_UNKNOWN_COMPAT_MODE:
+                return "UNKNOWN_COMPAT_MODE";
             default:
-                return "INVALID:" + mState;
+                return "INVALID:" + state;
         }
     }
 
+    @GuardedBy("mLock")
     private boolean isActiveLocked() {
         return mState == STATE_ACTIVE;
     }
 
+    @GuardedBy("mLock")
     private boolean isDisabledByServiceLocked() {
         return mState == STATE_DISABLED_BY_SERVICE;
     }
 
+    @GuardedBy("mLock")
     private boolean isFinishedLocked() {
         return mState == STATE_FINISHED;
     }
@@ -1910,7 +2195,242 @@
             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
             return;
         }
-        client.runOnUiThread(runnable);
+        client.autofillClientRunOnUiThread(runnable);
+    }
+
+    /**
+     * Implementation of the accessibility based compatibility.
+     */
+    private final class CompatibilityBridge implements AccessibilityManager.AccessibilityPolicy {
+        @GuardedBy("mLock")
+        private final Rect mFocusedBounds = new Rect();
+        @GuardedBy("mLock")
+        private final Rect mTempBounds = new Rect();
+
+        @GuardedBy("mLock")
+        private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+        @GuardedBy("mLock")
+        private long mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
+
+        // Need to report a fake service in case a11y clients check the service list
+        @NonNull
+        @GuardedBy("mLock")
+        AccessibilityServiceInfo mCompatServiceInfo;
+
+        CompatibilityBridge() {
+            final AccessibilityManager am = AccessibilityManager.getInstance(mContext);
+            am.setAccessibilityPolicy(this);
+        }
+
+        private AccessibilityServiceInfo getCompatServiceInfo() {
+            synchronized (mLock) {
+                if (mCompatServiceInfo != null) {
+                    return mCompatServiceInfo;
+                }
+                final Intent intent = new Intent();
+                intent.setComponent(new ComponentName("android",
+                        "com.android.server.autofill.AutofillCompatAccessibilityService"));
+                final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(
+                        intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+                try {
+                    mCompatServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
+                } catch (XmlPullParserException | IOException e) {
+                    Log.e(TAG, "Cannot find compat autofill service:" + intent);
+                    throw new IllegalStateException("Cannot find compat autofill service");
+                }
+                return mCompatServiceInfo;
+            }
+        }
+
+        @Override
+        public boolean isEnabled(boolean accessibilityEnabled) {
+            return true;
+        }
+
+        @Override
+        public int getRelevantEventTypes(int relevantEventTypes) {
+            return relevantEventTypes | AccessibilityEvent.TYPE_VIEW_FOCUSED
+                    | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+                    | AccessibilityEvent.TYPE_VIEW_CLICKED
+                    | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+        }
+
+        @Override
+        public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(
+               List<AccessibilityServiceInfo> installedServices) {
+            if (installedServices == null) {
+                installedServices = new ArrayList<>();
+            }
+            installedServices.add(getCompatServiceInfo());
+            return installedServices;
+        }
+
+        @Override
+        public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+                int feedbackTypeFlags, List<AccessibilityServiceInfo> enabledService) {
+            if (enabledService == null) {
+                enabledService = new ArrayList<>();
+            }
+            enabledService.add(getCompatServiceInfo());
+            return enabledService;
+        }
+
+        @Override
+        public AccessibilityEvent onAccessibilityEvent(AccessibilityEvent event,
+                boolean accessibilityEnabled, int relevantEventTypes) {
+            switch (event.getEventType()) {
+                case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
+                    synchronized (mLock) {
+                        if (mFocusedWindowId == event.getWindowId()
+                                && mFocusedNodeId == event.getSourceNodeId()) {
+                            return event;
+                        }
+                        if (mFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID
+                                && mFocusedNodeId != AccessibilityNodeInfo.UNDEFINED_NODE_ID) {
+                            notifyViewExited(mFocusedWindowId, mFocusedNodeId);
+                            mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                            mFocusedNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
+                            mFocusedBounds.set(0, 0, 0, 0);
+                        }
+                        final int windowId = event.getWindowId();
+                        final long nodeId = event.getSourceNodeId();
+                        if (notifyViewEntered(windowId, nodeId, mFocusedBounds)) {
+                            mFocusedWindowId = windowId;
+                            mFocusedNodeId = nodeId;
+                        }
+                    }
+                } break;
+
+                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: {
+                    synchronized (mLock) {
+                        if (mFocusedWindowId == event.getWindowId()
+                                && mFocusedNodeId == event.getSourceNodeId()) {
+                            notifyValueChanged(event.getWindowId(), event.getSourceNodeId());
+                        }
+                    }
+                } break;
+
+                case AccessibilityEvent.TYPE_VIEW_CLICKED: {
+                    synchronized (mLock) {
+                        notifyViewClicked(event.getWindowId(), event.getSourceNodeId());
+                    }
+                } break;
+
+                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+                    final AutofillClient client = getClient();
+                    if (client != null) {
+                        synchronized (mLock) {
+                            if (client.autofillClientIsFillUiShowing()) {
+                                notifyViewEntered(mFocusedWindowId, mFocusedNodeId, mFocusedBounds);
+                            }
+                            updateTrackedViewsLocked();
+                        }
+                    }
+                } break;
+            }
+
+            return accessibilityEnabled ? event : null;
+        }
+
+        private boolean notifyViewEntered(int windowId, long nodeId, Rect focusedBounds) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return false;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return false;
+            }
+            final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
+            if (node == null) {
+                return false;
+            }
+            if (!node.isEditable()) {
+                return false;
+            }
+            final Rect newBounds = mTempBounds;
+            node.getBoundsInScreen(newBounds);
+            if (newBounds.equals(focusedBounds)) {
+                return false;
+            }
+            focusedBounds.set(newBounds);
+            AutofillManager.this.notifyViewEntered(view, virtualId, newBounds);
+            return true;
+        }
+
+        private void notifyViewExited(int windowId, long nodeId) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return;
+            }
+            AutofillManager.this.notifyViewExited(view, virtualId);
+        }
+
+        private void notifyValueChanged(int windowId, long nodeId) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return;
+            }
+            final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
+            if (node == null) {
+                return;
+            }
+            AutofillManager.this.notifyValueChanged(view, virtualId,
+                    AutofillValue.forText(node.getText()));
+        }
+
+        private void notifyViewClicked(int windowId, long nodeId) {
+            final int virtualId = AccessibilityNodeInfo.getVirtualDescendantId(nodeId);
+            if (!isVirtualNode(virtualId)) {
+                return;
+            }
+            final View view = findViewByAccessibilityId(windowId, nodeId);
+            if (view == null) {
+                return;
+            }
+            final AccessibilityNodeInfo node = findVirtualNodeByAccessibilityId(view, virtualId);
+            if (node == null) {
+                return;
+            }
+            AutofillManager.this.notifyViewClicked(view, virtualId);
+        }
+
+        @GuardedBy("mLock")
+        private void updateTrackedViewsLocked() {
+            if (mTrackedViews != null) {
+                mTrackedViews.onVisibleForAutofillChangedLocked();
+            }
+        }
+
+        private View findViewByAccessibilityId(int windowId, long nodeId) {
+            final AutofillClient client = getClient();
+            if (client == null) {
+                return null;
+            }
+            final int viewId = AccessibilityNodeInfo.getAccessibilityViewId(nodeId);
+            return client.autofillClientFindViewByAccessibilityIdTraversal(viewId, windowId);
+        }
+
+        private AccessibilityNodeInfo findVirtualNodeByAccessibilityId(View view, int virtualId) {
+            final AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
+            if (provider == null) {
+                return null;
+            }
+            return provider.createAccessibilityNodeInfo(virtualId);
+        }
+
+        private boolean isVirtualNode(int nodeId) {
+            return nodeId != AccessibilityNodeProvider.HOST_VIEW_ID
+                    && nodeId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+        }
     }
 
     /**
@@ -1989,11 +2509,12 @@
          */
         TrackedViews(@Nullable AutofillId[] trackedIds) {
             final AutofillClient client = getClient();
-            if (trackedIds != null && client != null) {
+            if (!ArrayUtils.isEmpty(trackedIds) && client != null) {
                 final boolean[] isVisible;
 
-                if (client.isVisibleForAutofill()) {
-                    isVisible = client.getViewVisibility(getViewIds(trackedIds));
+                if (client.autofillClientIsVisibleForAutofill()) {
+                    if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
+                    isVisible = client.autofillClientGetViewVisibility(trackedIds);
                 } else {
                     // All false
                     isVisible = new boolean[trackedIds.length];
@@ -2012,7 +2533,7 @@
             }
 
             if (sVerbose) {
-                Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
+                Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
                         + " mVisibleTrackedIds=" + mVisibleTrackedIds
                         + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
             }
@@ -2028,9 +2549,10 @@
          * @param id the id of the view/virtual view whose visibility changed.
          * @param isVisible visible if the view is visible in the view hierarchy.
          */
+        @GuardedBy("mLock")
         void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
             if (sDebug) {
-                Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
+                Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
                         + isVisible);
             }
 
@@ -2059,20 +2581,25 @@
         /**
          * Called once the client becomes visible.
          *
-         * @see AutofillClient#isVisibleForAutofill()
+         * @see AutofillClient#autofillClientIsVisibleForAutofill()
          */
-        void onVisibleForAutofillLocked() {
+        @GuardedBy("mLock")
+        void onVisibleForAutofillChangedLocked() {
             // The visibility of the views might have changed while the client was not be visible,
             // hence update the visibility state for all views.
             AutofillClient client = getClient();
             ArraySet<AutofillId> updatedVisibleTrackedIds = null;
             ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
             if (client != null) {
+                if (sVerbose) {
+                    Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
+                            + " vis=" + mVisibleTrackedIds);
+                }
                 if (mInvisibleTrackedIds != null) {
                     final ArrayList<AutofillId> orderedInvisibleIds =
                             new ArrayList<>(mInvisibleTrackedIds);
-                    final boolean[] isVisible = client.getViewVisibility(
-                            getViewIds(orderedInvisibleIds));
+                    final boolean[] isVisible = client.autofillClientGetViewVisibility(
+                            Helper.toArray(orderedInvisibleIds));
 
                     final int numInvisibleTrackedIds = orderedInvisibleIds.size();
                     for (int i = 0; i < numInvisibleTrackedIds; i++) {
@@ -2092,8 +2619,8 @@
                 if (mVisibleTrackedIds != null) {
                     final ArrayList<AutofillId> orderedVisibleIds =
                             new ArrayList<>(mVisibleTrackedIds);
-                    final boolean[] isVisible = client.getViewVisibility(
-                            getViewIds(orderedVisibleIds));
+                    final boolean[] isVisible = client.autofillClientGetViewVisibility(
+                            Helper.toArray(orderedVisibleIds));
 
                     final int numVisibleTrackedIds = orderedVisibleIds.size();
                     for (int i = 0; i < numVisibleTrackedIds; i++) {
@@ -2116,6 +2643,9 @@
             }
 
             if (mVisibleTrackedIds == null) {
+                if (sVerbose) {
+                    Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
+                }
                 finishSessionLocked();
             }
         }
@@ -2232,7 +2762,7 @@
         public void requestHideFillUi(int sessionId, AutofillId id) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
-                afm.post(() -> afm.requestHideFillUi(id));
+                afm.post(() -> afm.requestHideFillUi(id, false));
             }
         }
 
@@ -2245,6 +2775,14 @@
         }
 
         @Override
+        public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
+            final AutofillManager afm = mAfm.get();
+            if (afm != null) {
+                afm.post(() -> afm.dispatchUnhandledKey(sessionId, id, fullScreen));
+            }
+        }
+
+        @Override
         public void startIntentSender(IntentSender intentSender, Intent intent) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
diff --git a/android/view/autofill/AutofillManagerInternal.java b/android/view/autofill/AutofillManagerInternal.java
index fc5d306..155fe72 100644
--- a/android/view/autofill/AutofillManagerInternal.java
+++ b/android/view/autofill/AutofillManagerInternal.java
@@ -15,6 +15,9 @@
  */
 package android.view.autofill;
 
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+
 /**
  * Autofill Manager local system service interface.
  *
@@ -26,4 +29,14 @@
      * Notifies the manager that the back key was pressed.
      */
     public abstract void onBackKeyPressed();
+
+    /**
+     * Gets whether compatibility mode is enabled for a package
+     *
+     * @param packageName The package for which to query.
+     * @param versionCode The package version code.
+     * @param userId The user id for which to query.
+     */
+    public abstract boolean isCompatibilityModeRequested(@NonNull String packageName,
+            long versionCode, @UserIdInt int userId);
 }
diff --git a/android/view/autofill/AutofillPopupWindow.java b/android/view/autofill/AutofillPopupWindow.java
index e80fdd9..1da998d 100644
--- a/android/view/autofill/AutofillPopupWindow.java
+++ b/android/view/autofill/AutofillPopupWindow.java
@@ -46,6 +46,7 @@
 
     private final WindowPresenter mWindowPresenter;
     private WindowManager.LayoutParams mWindowLayoutParams;
+    private boolean mFullScreen;
 
     private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
             new View.OnAttachStateChangeListener() {
@@ -104,12 +105,17 @@
      */
     public void update(View anchor, int offsetX, int offsetY, int width, int height,
             Rect virtualBounds) {
+        mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
         // If we are showing the popup for a virtual view we use a fake view which
         // delegates to the anchor but present itself with the same bounds as the
         // virtual view. This ensures that the location logic in popup works
         // symmetrically when the dropdown is below and above the anchor.
         final View actualAnchor;
-        if (virtualBounds != null) {
+        if (mFullScreen) {
+            offsetX = 0;
+            offsetY = 0;
+            actualAnchor = anchor;
+        } else if (virtualBounds != null) {
             final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
             actualAnchor = new View(anchor.getContext()) {
                 @Override
@@ -209,6 +215,17 @@
     }
 
     @Override
+    protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
+            int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
+        if (mFullScreen) {
+            // Do not patch LayoutParams if force full screen
+            return false;
+        }
+        return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
+                width, height, gravity, allowScroll);
+    }
+
+    @Override
     public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
         if (sVerbose) {
             Log.v(TAG, "showAsDropDown(): anchor=" + anchor + ", xoff=" + xoff + ", yoff=" + yoff
diff --git a/android/view/autofill/Helper.java b/android/view/autofill/Helper.java
index 4b2c53c..48d0dbb 100644
--- a/android/view/autofill/Helper.java
+++ b/android/view/autofill/Helper.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import java.util.Collection;
+
 /** @hide */
 public final class Helper {
 
@@ -59,6 +61,20 @@
         builder.append(" ]");
     }
 
+    /**
+     * Convers a collaction of {@link AutofillId AutofillIds} to an array.
+     * @param collection The collection.
+     * @return The array.
+     */
+    public static @NonNull AutofillId[] toArray(Collection<AutofillId> collection) {
+        if (collection == null) {
+            return new AutofillId[0];
+        }
+        final AutofillId[] array = new AutofillId[collection.size()];
+        collection.toArray(array);
+        return array;
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
diff --git a/android/view/inputmethod/InputConnection.java b/android/view/inputmethod/InputConnection.java
index eba9176..e554540 100644
--- a/android/view/inputmethod/InputConnection.java
+++ b/android/view/inputmethod/InputConnection.java
@@ -21,7 +21,6 @@
 import android.inputmethodservice.InputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.LocaleList;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
@@ -899,37 +898,4 @@
      */
     boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
             @Nullable Bundle opts);
-
-    /**
-     * Called by the input method to tell a hint about the locales of text to be committed.
-     *
-     * <p>This is just a hint for editor authors (and the system) to choose better options when
-     * they have to disambiguate languages, like editor authors can do for input methods with
-     * {@link EditorInfo#hintLocales}.</p>
-     *
-     * <p>The language hint provided by this callback should have higher priority than
-     * {@link InputMethodSubtype#getLanguageTag()}, which cannot be updated dynamically.</p>
-     *
-     * <p>Note that in general it is discouraged for input method to specify
-     * {@link android.text.style.LocaleSpan} when inputting text, mainly because of application
-     * compatibility concerns.</p>
-     * <ul>
-     *     <li>When an existing text that already has {@link android.text.style.LocaleSpan} is being
-     *     modified by both the input method and application, there is no reliable and easy way to
-     *     keep track of who modified {@link android.text.style.LocaleSpan}. For instance, if the
-     *     text was updated by JavaScript, it it highly likely that span information is completely
-     *     removed, while some input method attempts to preserve spans if possible.</li>
-     *     <li>There is no clear semantics regarding whether {@link android.text.style.LocaleSpan}
-     *     means a weak (ignorable) hint or a strong hint. This becomes more problematic when
-     *     multiple {@link android.text.style.LocaleSpan} instances are specified to the same
-     *     text region, especially when those spans are conflicting.</li>
-     * </ul>
-     * @param languageHint list of languages sorted by the priority and/or probability
-     */
-    default void reportLanguageHint(@NonNull LocaleList languageHint) {
-        // Intentionally empty.
-        //
-        // We need to have *some* default implementation for the source compatibility.
-        // See Bug 72127682 for details.
-    }
 }
diff --git a/android/view/inputmethod/InputConnectionWrapper.java b/android/view/inputmethod/InputConnectionWrapper.java
index cbe6856..f671e22 100644
--- a/android/view/inputmethod/InputConnectionWrapper.java
+++ b/android/view/inputmethod/InputConnectionWrapper.java
@@ -16,10 +16,8 @@
 
 package android.view.inputmethod;
 
-import android.annotation.NonNull;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.LocaleList;
 import android.view.KeyEvent;
 
 /**
@@ -305,13 +303,4 @@
     public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
         return mTarget.commitContent(inputContentInfo, flags, opts);
     }
-
-    /**
-     * {@inheritDoc}
-     * @throws NullPointerException if the target is {@code null}.
-     */
-    @Override
-    public void reportLanguageHint(@NonNull LocaleList languageHint) {
-        mTarget.reportLanguageHint(languageHint);
-    }
 }
diff --git a/android/view/inputmethod/InputMethodInfo.java b/android/view/inputmethod/InputMethodInfo.java
index c69543f..f0f30a0 100644
--- a/android/view/inputmethod/InputMethodInfo.java
+++ b/android/view/inputmethod/InputMethodInfo.java
@@ -261,8 +261,7 @@
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
-        // TODO(b/68948291): remove this meta-data before release.
-        mIsVrOnly = isVrOnly || service.serviceInfo.metaData.getBoolean("isVrOnly", false);
+        mIsVrOnly = isVrOnly;
     }
 
     InputMethodInfo(Parcel source) {
diff --git a/android/view/inputmethod/InputMethodManager.java b/android/view/inputmethod/InputMethodManager.java
index 7db5c32..4104728 100644
--- a/android/view/inputmethod/InputMethodManager.java
+++ b/android/view/inputmethod/InputMethodManager.java
@@ -20,9 +20,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
@@ -212,6 +215,7 @@
  * </ul>
  */
 @SystemService(Context.INPUT_METHOD_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_INPUT_METHODS)
 public final class InputMethodManager {
     static final boolean DEBUG = false;
     static final String TAG = "InputMethodManager";
@@ -1178,8 +1182,9 @@
         }
     }
 
-    /*
+    /**
      * This method toggles the input method window display.
+     *
      * If the input window is already displayed, it gets hidden.
      * If not the input window will be displayed.
      * @param showFlags Provides additional operating flags.  May be
@@ -1188,7 +1193,6 @@
      * @param hideFlags Provides additional operating flags.  May be
      * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
      * {@link #HIDE_NOT_ALWAYS} bit set.
-     * @hide
      */
     public void toggleSoftInput(int showFlags, int hideFlags) {
         if (mCurMethod != null) {
@@ -1808,9 +1812,9 @@
      * when it was started, which allows it to perform this operation on
      * itself.
      * @param id The unique identifier for the new input method to be switched to.
-     * @deprecated Use {@link InputMethodService#setInputMethod(String)} instead. This method
-     * was intended for IME developers who should be accessing APIs through the service. APIs in
-     * this class are intended for app developers interacting with the IME.
+     * @deprecated Use {@link InputMethodService#switchInputMethod(String)}
+     * instead. This method was intended for IME developers who should be accessing APIs through
+     * the service. APIs in this class are intended for app developers interacting with the IME.
      */
     @Deprecated
     public void setInputMethod(IBinder token, String id) {
@@ -1837,7 +1841,7 @@
      * @param id The unique identifier for the new input method to be switched to.
      * @param subtype The new subtype of the new input method to be switched to.
      * @deprecated Use
-     * {@link InputMethodService#setInputMethodAndSubtype(String, InputMethodSubtype)}
+     * {@link InputMethodService#switchInputMethod(String, InputMethodSubtype)}
      * instead. This method was intended for IME developers who should be accessing APIs through
      * the service. APIs in this class are intended for app developers interacting with the IME.
      */
@@ -2137,6 +2141,26 @@
     }
 
     /**
+     * A test API for CTS to make sure that {@link #showInputMethodPicker()} works as expected.
+     *
+     * <p>When customizing the implementation of {@link #showInputMethodPicker()} API, make sure
+     * that this test API returns when and only while and only while
+     * {@link #showInputMethodPicker()} is showing UI. Otherwise your OS implementation may not
+     * pass CTS.</p>
+     *
+     * @return {@code true} while and only while {@link #showInputMethodPicker()} is showing UI.
+     * @hide
+     */
+    @TestApi
+    public boolean isInputMethodPickerShown() {
+        try {
+            return mService.isInputMethodPickerShownForTest();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Show the settings for enabling subtypes of the specified input method.
      * @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
      * subtypes of all input methods will be shown.
@@ -2293,22 +2317,22 @@
      * which allows it to perform this operation on itself.
      * @return true if the current input method and subtype was successfully switched to the last
      * used input method and subtype.
-     * @deprecated Use {@link InputMethodService#switchToLastInputMethod()} instead. This method
+     * @deprecated Use {@link InputMethodService#switchToPreviousInputMethod()} instead. This method
      * was intended for IME developers who should be accessing APIs through the service. APIs in
      * this class are intended for app developers interacting with the IME.
      */
     @Deprecated
     public boolean switchToLastInputMethod(IBinder imeToken) {
-        return switchToLastInputMethodInternal(imeToken);
+        return switchToPreviousInputMethodInternal(imeToken);
     }
 
     /**
      * @hide
      */
-    public boolean switchToLastInputMethodInternal(IBinder imeToken) {
+    public boolean switchToPreviousInputMethodInternal(IBinder imeToken) {
         synchronized (mH) {
             try {
-                return mService.switchToLastInputMethod(imeToken);
+                return mService.switchToPreviousInputMethod(imeToken);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/android/view/inputmethod/InputMethodManager_Delegate.java b/android/view/inputmethod/InputMethodManager_Delegate.java
index 7c98847..b2a183b 100644
--- a/android/view/inputmethod/InputMethodManager_Delegate.java
+++ b/android/view/inputmethod/InputMethodManager_Delegate.java
@@ -16,10 +16,10 @@
 
 package android.view.inputmethod;
 
-import com.android.layoutlib.bridge.android.BridgeIInputMethodManager;
+import com.android.internal.view.IInputMethodManager;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.content.Context;
 import android.os.Looper;
 
 
@@ -39,8 +39,8 @@
         synchronized (InputMethodManager.class) {
             InputMethodManager imm = InputMethodManager.peekInstance();
             if (imm == null) {
-                imm = new InputMethodManager(
-                        new BridgeIInputMethodManager(), Looper.getMainLooper());
+                imm = new InputMethodManager(ReflectionUtils.createProxy(IInputMethodManager.class),
+                        Looper.getMainLooper());
                 InputMethodManager.sInstance = imm;
             }
             return imm;
diff --git a/android/view/textclassifier/DefaultLogger.java b/android/view/textclassifier/DefaultLogger.java
new file mode 100644
index 0000000..203ca56
--- /dev/null
+++ b/android/view/textclassifier/DefaultLogger.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.metrics.LogMaker;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * Default Logger.
+ * Used internally by TextClassifierImpl.
+ * @hide
+ */
+public final class DefaultLogger extends Logger {
+
+    private static final String LOG_TAG = "DefaultLogger";
+    static final String CLASSIFIER_ID = "androidtc";
+
+    private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
+    private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
+    private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
+    private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+    private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
+    private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+    private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+    private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
+    private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
+    private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
+    private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
+    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
+
+    private static final String ZERO = "0";
+    private static final String UNKNOWN = "unknown";
+
+    private final MetricsLogger mMetricsLogger;
+
+    public DefaultLogger(@NonNull Config config) {
+        super(config);
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    @VisibleForTesting
+    public DefaultLogger(@NonNull Config config, @NonNull MetricsLogger metricsLogger) {
+        super(config);
+        mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
+    }
+
+    @Override
+    public boolean isSmartSelection(@NonNull String signature) {
+        return CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
+    }
+
+    @Override
+    public void writeEvent(@NonNull SelectionEvent event) {
+        Preconditions.checkNotNull(event);
+        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
+                .setType(getLogType(event))
+                .setSubtype(getLogSubType(event))
+                .setPackageName(event.getPackageName())
+                .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
+                .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
+                .addTaggedData(INDEX, event.getEventIndex())
+                .addTaggedData(WIDGET_TYPE, event.getWidgetType())
+                .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
+                .addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getResultId()))
+                .addTaggedData(ENTITY_TYPE, event.getEntityType())
+                .addTaggedData(SMART_START, event.getSmartStart())
+                .addTaggedData(SMART_END, event.getSmartEnd())
+                .addTaggedData(EVENT_START, event.getStart())
+                .addTaggedData(EVENT_END, event.getEnd())
+                .addTaggedData(SESSION_ID, event.getSessionId());
+        mMetricsLogger.write(log);
+        debugLog(log);
+    }
+
+    private static int getLogType(SelectionEvent event) {
+        switch (event.getEventType()) {
+            case SelectionEvent.ACTION_OVERTYPE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
+            case SelectionEvent.ACTION_COPY:
+                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
+            case SelectionEvent.ACTION_PASTE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
+            case SelectionEvent.ACTION_CUT:
+                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
+            case SelectionEvent.ACTION_SHARE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
+            case SelectionEvent.ACTION_SMART_SHARE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+            case SelectionEvent.ACTION_DRAG:
+                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
+            case SelectionEvent.ACTION_ABANDON:
+                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
+            case SelectionEvent.ACTION_OTHER:
+                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
+            case SelectionEvent.ACTION_SELECT_ALL:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
+            case SelectionEvent.ACTION_RESET:
+                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
+            case SelectionEvent.EVENT_SELECTION_STARTED:
+                return MetricsEvent.ACTION_TEXT_SELECTION_START;
+            case SelectionEvent.EVENT_SELECTION_MODIFIED:
+                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
+            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
+            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
+            case SelectionEvent.EVENT_AUTO_SELECTION:
+                return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
+            default:
+                return MetricsEvent.VIEW_UNKNOWN;
+        }
+    }
+
+    private static int getLogSubType(SelectionEvent event) {
+        switch (event.getInvocationMethod()) {
+            case SelectionEvent.INVOCATION_MANUAL:
+                return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL;
+            case SelectionEvent.INVOCATION_LINK:
+                return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK;
+            default:
+                return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN;
+        }
+    }
+
+    private static String getLogTypeString(int logType) {
+        switch (logType) {
+            case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
+                return "OVERTYPE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
+                return "COPY";
+            case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
+                return "PASTE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
+                return "CUT";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
+                return "SHARE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
+                return "SMART_SHARE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
+                return "DRAG";
+            case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
+                return "ABANDON";
+            case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
+                return "OTHER";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
+                return "SELECT_ALL";
+            case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
+                return "RESET";
+            case MetricsEvent.ACTION_TEXT_SELECTION_START:
+                return "SELECTION_STARTED";
+            case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
+                return "SELECTION_MODIFIED";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
+                return "SMART_SELECTION_SINGLE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
+                return "SMART_SELECTION_MULTI";
+            case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
+                return "AUTO_SELECTION";
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    private static String getLogSubTypeString(int logSubType) {
+        switch (logSubType) {
+            case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL:
+                return "MANUAL";
+            case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK:
+                return "LINK";
+            default:
+                return UNKNOWN;
+        }
+    }
+
+    private static void debugLog(LogMaker log) {
+        if (!DEBUG_LOG_ENABLED) return;
+
+        final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
+        final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
+        final String widget = widgetVersion.isEmpty()
+                ? widgetType : widgetType + "-" + widgetVersion;
+        final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
+        if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
+            String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
+            sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
+            Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
+        }
+
+        final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
+        final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
+        final String type = getLogTypeString(log.getType());
+        final String subType = getLogSubTypeString(log.getSubtype());
+        final int smartStart = Integer.parseInt(
+                Objects.toString(log.getTaggedData(SMART_START), ZERO));
+        final int smartEnd = Integer.parseInt(
+                Objects.toString(log.getTaggedData(SMART_END), ZERO));
+        final int eventStart = Integer.parseInt(
+                Objects.toString(log.getTaggedData(EVENT_START), ZERO));
+        final int eventEnd = Integer.parseInt(
+                Objects.toString(log.getTaggedData(EVENT_END), ZERO));
+
+        Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+                index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget,
+                model));
+    }
+
+    /**
+     * Creates a string id that may be used to identify a TextClassifier result.
+     */
+    public static String createId(
+            String text, int start, int end, Context context, int modelVersion,
+            List<Locale> locales) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(locales);
+        final StringJoiner localesJoiner = new StringJoiner(",");
+        for (Locale locale : locales) {
+            localesJoiner.add(locale.toLanguageTag());
+        }
+        final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(),
+                modelVersion);
+        final int hash = Objects.hash(text, start, end, context.getPackageName());
+        return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
+    }
+
+    /**
+     * Helper for creating and parsing string ids for
+     * {@link android.view.textclassifier.TextClassifierImpl}.
+     */
+    @VisibleForTesting
+    public static final class SignatureParser {
+
+        static String createSignature(String classifierId, String modelName, int hash) {
+            return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
+        }
+
+        static String getClassifierId(String signature) {
+            Preconditions.checkNotNull(signature);
+            final int end = signature.indexOf("|");
+            if (end >= 0) {
+                return signature.substring(0, end);
+            }
+            return "";
+        }
+
+        static String getModelName(String signature) {
+            Preconditions.checkNotNull(signature);
+            final int start = signature.indexOf("|") + 1;
+            final int end = signature.indexOf("|", start);
+            if (start >= 1 && end >= start) {
+                return signature.substring(start, end);
+            }
+            return "";
+        }
+
+        static int getHash(String signature) {
+            Preconditions.checkNotNull(signature);
+            final int index1 = signature.indexOf("|");
+            final int index2 = signature.indexOf("|", index1);
+            if (index2 > 0) {
+                return Integer.parseInt(signature.substring(index2));
+            }
+            return 0;
+        }
+    }
+}
diff --git a/android/view/textclassifier/GenerateLinksLogger.java b/android/view/textclassifier/GenerateLinksLogger.java
new file mode 100644
index 0000000..73cf43b
--- /dev/null
+++ b/android/view/textclassifier/GenerateLinksLogger.java
@@ -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.view.textclassifier;
+
+import android.annotation.Nullable;
+import android.metrics.LogMaker;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
+
+/**
+ * A helper for logging calls to generateLinks.
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class GenerateLinksLogger {
+
+    private static final String LOG_TAG = "GenerateLinksLogger";
+    private static final String ZERO = "0";
+
+    private final MetricsLogger mMetricsLogger;
+    private final Random mRng;
+    private final int mSampleRate;
+
+    /**
+     * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01
+     *                   chance that a call to logGenerateLinks results in an event being written).
+     *                   To write all events, pass 1.
+     */
+    public GenerateLinksLogger(int sampleRate) {
+        mSampleRate = sampleRate;
+        mRng = new Random(System.nanoTime());
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    @VisibleForTesting
+    public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) {
+        mSampleRate = sampleRate;
+        mRng = new Random(System.nanoTime());
+        mMetricsLogger = metricsLogger;
+    }
+
+    /** Logs statistics about a call to generateLinks. */
+    public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName,
+            long latencyMs) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(links);
+        Preconditions.checkNotNull(callingPackageName);
+        if (!shouldLog()) {
+            return;
+        }
+
+        // Always populate the total stats, and per-entity stats for each entity type detected.
+        final LinkifyStats totalStats = new LinkifyStats();
+        final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>();
+        for (TextLinks.TextLink link : links.getLinks()) {
+            if (link.getEntityCount() == 0) continue;
+            final String entityType = link.getEntity(0);
+            if (entityType == null
+                    || TextClassifier.TYPE_OTHER.equals(entityType)
+                    || TextClassifier.TYPE_UNKNOWN.equals(entityType)) {
+                continue;
+            }
+            totalStats.countLink(link);
+            perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link);
+        }
+
+        final String callId = UUID.randomUUID().toString();
+        writeStats(callId, callingPackageName, null, totalStats, text, latencyMs);
+        for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) {
+            writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text,
+                       latencyMs);
+        }
+    }
+
+    /**
+     * Returns whether this particular event should be logged.
+     *
+     * Sampling is used to reduce the amount of logging data generated.
+     **/
+    private boolean shouldLog() {
+        if (mSampleRate <= 1) {
+            return true;
+        } else {
+            return mRng.nextInt(mSampleRate) == 0;
+        }
+    }
+
+    /** Writes a log event for the given stats. */
+    private void writeStats(String callId, String callingPackageName, @Nullable String entityType,
+                            LinkifyStats stats, CharSequence text, long latencyMs) {
+        final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS)
+                .setPackageName(callingPackageName)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length())
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs);
+        if (entityType != null) {
+            log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType);
+        }
+        mMetricsLogger.write(log);
+        debugLog(log);
+    }
+
+    private static void debugLog(LogMaker log) {
+        if (!Logger.DEBUG_LOG_ENABLED) return;
+
+        final String callId = Objects.toString(
+                log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
+        final String entityType = Objects.toString(
+                log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "ANY_ENTITY");
+        final int numLinks = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS), ZERO));
+        final int linkLength = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH), ZERO));
+        final int textLength = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH), ZERO));
+        final int latencyMs = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
+
+        Log.d(LOG_TAG, String.format("%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
+                numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
+    }
+
+    /** Helper class for storing per-entity type statistics. */
+    private static final class LinkifyStats {
+        int mNumLinks;
+        int mNumLinksTextLength;
+
+        void countLink(TextLinks.TextLink link) {
+            mNumLinks += 1;
+            mNumLinksTextLength += link.getEnd() - link.getStart();
+        }
+    }
+}
diff --git a/android/view/textclassifier/LinksInfo.java b/android/view/textclassifier/LinksInfo.java
deleted file mode 100644
index 754c9e9..0000000
--- a/android/view/textclassifier/LinksInfo.java
+++ /dev/null
@@ -1,42 +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.view.textclassifier;
-
-import android.annotation.NonNull;
-
-/**
- * Link information that can be applied to text. See: {@link #apply(CharSequence)}.
- * Typical implementations of this interface will annotate spannable text with e.g
- * {@link android.text.style.ClickableSpan}s or other annotations.
- * @hide
- */
-public interface LinksInfo {
-
-    /**
-     * @hide
-     */
-    LinksInfo NO_OP = text -> false;
-
-    /**
-     * Applies link annotations to the specified text.
-     * These annotations are not guaranteed to be applied. For example, the annotations may not be
-     * applied if the text has changed from what it was when the link spec was generated for it.
-     *
-     * @return Whether or not the link annotations were successfully applied.
-     */
-    boolean apply(@NonNull CharSequence text);
-}
diff --git a/android/view/textclassifier/Log.java b/android/view/textclassifier/Log.java
index 83ca15d..ef19ee5 100644
--- a/android/view/textclassifier/Log.java
+++ b/android/view/textclassifier/Log.java
@@ -35,6 +35,10 @@
         Slog.d(tag, msg);
     }
 
+    public static void w(String tag, String msg) {
+        Slog.w(tag, msg);
+    }
+
     public static void e(String tag, String msg, Throwable tr) {
         if (ENABLE_FULL_LOGGING) {
             Slog.e(tag, msg, tr);
diff --git a/android/view/textclassifier/Logger.java b/android/view/textclassifier/Logger.java
new file mode 100644
index 0000000..f03906a
--- /dev/null
+++ b/android/view/textclassifier/Logger.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import com.android.internal.util.Preconditions;
+
+import java.text.BreakIterator;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A helper for logging TextClassifier related events.
+ * @hide
+ */
+public abstract class Logger {
+
+    private static final String LOG_TAG = "Logger";
+    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
+
+    private @SelectionEvent.InvocationMethod int mInvocationMethod;
+    private SelectionEvent mPrevEvent;
+    private SelectionEvent mSmartEvent;
+    private SelectionEvent mStartEvent;
+
+    /**
+     * Logger that does not log anything.
+     * @hide
+     */
+    public static final Logger DISABLED = new Logger() {
+        @Override
+        public void writeEvent(SelectionEvent event) {}
+    };
+
+    @Nullable
+    private final Config mConfig;
+
+    public Logger(Config config) {
+        mConfig = Preconditions.checkNotNull(config);
+    }
+
+    private Logger() {
+        mConfig = null;
+    }
+
+    /**
+     * Writes the selection event to a log.
+     */
+    public abstract void writeEvent(@NonNull SelectionEvent event);
+
+    /**
+     * Returns true if the resultId matches that of a smart selection event (i.e.
+     * {@link SelectionEvent#EVENT_SMART_SELECTION_SINGLE} or
+     * {@link SelectionEvent#EVENT_SMART_SELECTION_MULTI}).
+     * Returns false otherwise.
+     */
+    public boolean isSmartSelection(@NonNull String resultId) {
+        return false;
+    }
+
+    /**
+     * Returns a token iterator for tokenizing text for logging purposes.
+     */
+    public BreakIterator getTokenIterator(@NonNull Locale locale) {
+        return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale));
+    }
+
+    /**
+     * Logs a "selection started" event.
+     *
+     * @param invocationMethod  the way the selection was triggered
+     * @param start  the token index of the selected token
+     */
+    public final void logSelectionStartedEvent(
+            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
+        if (mConfig == null) {
+            return;
+        }
+
+        mInvocationMethod = invocationMethod;
+        logEvent(new SelectionEvent(
+                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
+                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig));
+    }
+
+    /**
+     * Logs a "selection modified" event.
+     * Use when the user modifies the selection.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     */
+    public final void logSelectionModifiedEvent(int start, int end) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+
+        if (mConfig == null) {
+            return;
+        }
+
+        logEvent(new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig));
+    }
+
+    /**
+     * Logs a "selection modified" event.
+     * Use when the user modifies the selection and the selection's entity type is known.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     */
+    public final void logSelectionModifiedEvent(
+            int start, int end, @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        logEvent(new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                entityType, mInvocationMethod, classification.getId(), mConfig));
+    }
+
+    /**
+     * Logs a "selection modified" event.
+     * Use when a TextClassifier modifies the selection.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param selection  the TextSelection object returned by the TextClassifier for the
+     *      specified selection
+     */
+    public final void logSelectionModifiedEvent(
+            int start, int end, @NonNull TextSelection selection) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(selection);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        final int eventType;
+        if (isSmartSelection(selection.getId())) {
+            eventType = end - start > 1
+                    ? SelectionEvent.EVENT_SMART_SELECTION_MULTI
+                    : SelectionEvent.EVENT_SMART_SELECTION_SINGLE;
+
+        } else {
+            eventType = SelectionEvent.EVENT_AUTO_SELECTION;
+        }
+        final String entityType = selection.getEntityCount() > 0
+                ? selection.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod,
+                selection.getId(), mConfig));
+    }
+
+    /**
+     * Logs an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     */
+    public final void logSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        checkActionType(actionType);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        logEvent(new SelectionEvent(
+                start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod,
+                null, mConfig));
+    }
+
+    /**
+     * Logs an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text and the selection's
+     * entity type is known.
+     *
+     * @param start  the start token (inclusive) index of the selection
+     * @param end  the end token (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     *
+     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
+     */
+    public final void logSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType,
+            @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+        checkActionType(actionType);
+
+        if (mConfig == null) {
+            return;
+        }
+
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod,
+                classification.getId(), mConfig));
+    }
+
+    private void logEvent(@NonNull SelectionEvent event) {
+        Preconditions.checkNotNull(event);
+
+        if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
+                && mStartEvent == null) {
+            if (DEBUG_LOG_ENABLED) {
+                Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
+            }
+            return;
+        }
+
+        final long now = System.currentTimeMillis();
+        switch (event.getEventType()) {
+            case SelectionEvent.EVENT_SELECTION_STARTED:
+                Preconditions.checkArgument(event.getAbsoluteEnd() == event.getAbsoluteStart() + 1);
+                event.setSessionId(startNewSession());
+                mStartEvent = event;
+                break;
+            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
+            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                mSmartEvent = event;
+                break;
+            case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
+            case SelectionEvent.EVENT_AUTO_SELECTION:
+                if (mPrevEvent != null
+                        && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
+                        && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
+                    // Selection did not change. Ignore event.
+                    return;
+                }
+                break;
+            default:
+                // do nothing.
+        }
+
+        event.setEventTime(now);
+        if (mStartEvent != null) {
+            event.setSessionId(mStartEvent.getSessionId())
+                    .setDurationSinceSessionStart(now - mStartEvent.getEventTime())
+                    .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                    .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+        }
+        if (mSmartEvent != null) {
+            event.setResultId(mSmartEvent.getResultId())
+                    .setSmartStart(mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                    .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+        }
+        if (mPrevEvent != null) {
+            event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime())
+                    .setEventIndex(mPrevEvent.getEventIndex() + 1);
+        }
+        writeEvent(event);
+        mPrevEvent = event;
+
+        if (event.isTerminal()) {
+            endSession();
+        }
+    }
+
+    private TextClassificationSessionId startNewSession() {
+        endSession();
+        return new TextClassificationSessionId();
+    }
+
+    private void endSession() {
+        mPrevEvent = null;
+        mSmartEvent = null;
+        mStartEvent = null;
+    }
+
+    /**
+     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
+     */
+    private static void checkActionType(@SelectionEvent.EventType int eventType)
+            throws IllegalArgumentException {
+        switch (eventType) {
+            case SelectionEvent.ACTION_OVERTYPE:  // fall through
+            case SelectionEvent.ACTION_COPY:  // fall through
+            case SelectionEvent.ACTION_PASTE:  // fall through
+            case SelectionEvent.ACTION_CUT:  // fall through
+            case SelectionEvent.ACTION_SHARE:  // fall through
+            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
+            case SelectionEvent.ACTION_DRAG:  // fall through
+            case SelectionEvent.ACTION_ABANDON:  // fall through
+            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
+            case SelectionEvent.ACTION_RESET:  // fall through
+                return;
+            default:
+                throw new IllegalArgumentException(
+                        String.format(Locale.US, "%d is not an eventType", eventType));
+        }
+    }
+
+
+    /**
+     * A Logger config.
+     */
+    public static final class Config {
+
+        private final String mPackageName;
+        private final String mWidgetType;
+        @Nullable private final String mWidgetVersion;
+
+        /**
+         * @param context Context of the widget the logger logs for
+         * @param widgetType a name for the widget being logged for. e.g.
+         *      {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}
+         * @param widgetVersion a string version info for the widget the logger logs for
+         */
+        public Config(
+                @NonNull Context context,
+                @TextClassifier.WidgetType String widgetType,
+                @Nullable String widgetVersion) {
+            mPackageName = Preconditions.checkNotNull(context).getPackageName();
+            mWidgetType = widgetType;
+            mWidgetVersion = widgetVersion;
+        }
+
+        /**
+         * Returns the package name of the application the logger logs for.
+         */
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * Returns the name for the widget being logged for. e.g.
+         * {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}.
+         */
+        public String getWidgetType() {
+            return mWidgetType;
+        }
+
+        /**
+         * Returns string version info for the logger. This is specific to the text classifier.
+         */
+        @Nullable
+        public String getWidgetVersion() {
+            return mWidgetVersion;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPackageName, mWidgetType, mWidgetVersion);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == this) {
+                return true;
+            }
+
+            if (!(obj instanceof Config)) {
+                return false;
+            }
+
+            final Config other = (Config) obj;
+            return Objects.equals(mPackageName, other.mPackageName)
+                    && Objects.equals(mWidgetType, other.mWidgetType)
+                    && Objects.equals(mWidgetVersion, other.mWidgetType);
+        }
+    }
+}
diff --git a/android/view/textclassifier/SelectionEvent.java b/android/view/textclassifier/SelectionEvent.java
new file mode 100644
index 0000000..1e978cc
--- /dev/null
+++ b/android/view/textclassifier/SelectionEvent.java
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.textclassifier.TextClassifier.EntityType;
+import android.view.textclassifier.TextClassifier.WidgetType;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A selection event.
+ * Specify index parameters as word token indices.
+ */
+public final class SelectionEvent implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
+            ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
+            ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET})
+    // NOTE: ActionType values should not be lower than 100 to avoid colliding with the other
+    // EventTypes declared below.
+    public @interface ActionType {
+        /*
+         * Terminal event types range: [100,200).
+         * Non-terminal event types range: [200,300).
+         */
+    }
+
+    /** User typed over the selection. */
+    public static final int ACTION_OVERTYPE = 100;
+    /** User copied the selection. */
+    public static final int ACTION_COPY = 101;
+    /** User pasted over the selection. */
+    public static final int ACTION_PASTE = 102;
+    /** User cut the selection. */
+    public static final int ACTION_CUT = 103;
+    /** User shared the selection. */
+    public static final int ACTION_SHARE = 104;
+    /** User clicked the textAssist menu item. */
+    public static final int ACTION_SMART_SHARE = 105;
+    /** User dragged+dropped the selection. */
+    public static final int ACTION_DRAG = 106;
+    /** User abandoned the selection. */
+    public static final int ACTION_ABANDON = 107;
+    /** User performed an action on the selection. */
+    public static final int ACTION_OTHER = 108;
+
+    // Non-terminal actions.
+    /** User activated Select All */
+    public static final int ACTION_SELECT_ALL = 200;
+    /** User reset the smart selection. */
+    public static final int ACTION_RESET = 201;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ACTION_OVERTYPE, ACTION_COPY, ACTION_PASTE, ACTION_CUT,
+            ACTION_SHARE, ACTION_SMART_SHARE, ACTION_DRAG, ACTION_ABANDON,
+            ACTION_OTHER, ACTION_SELECT_ALL, ACTION_RESET,
+            EVENT_SELECTION_STARTED, EVENT_SELECTION_MODIFIED,
+            EVENT_SMART_SELECTION_SINGLE, EVENT_SMART_SELECTION_MULTI,
+            EVENT_AUTO_SELECTION})
+    // NOTE: EventTypes declared here must be less than 100 to avoid colliding with the
+    // ActionTypes declared above.
+    public @interface EventType {
+        /*
+         * Range: 1 -> 99.
+         */
+    }
+
+    /** User started a new selection. */
+    public static final int EVENT_SELECTION_STARTED = 1;
+    /** User modified an existing selection. */
+    public static final int EVENT_SELECTION_MODIFIED = 2;
+    /** Smart selection triggered for a single token (word). */
+    public static final int EVENT_SMART_SELECTION_SINGLE = 3;
+    /** Smart selection triggered spanning multiple tokens (words). */
+    public static final int EVENT_SMART_SELECTION_MULTI = 4;
+    /** Something else other than User or the default TextClassifier triggered a selection. */
+    public static final int EVENT_AUTO_SELECTION = 5;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({INVOCATION_MANUAL, INVOCATION_LINK, INVOCATION_UNKNOWN})
+    public @interface InvocationMethod {}
+
+    /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */
+    public static final int INVOCATION_MANUAL = 1;
+    /** Selection was invoked by the user tapping on a link. */
+    public static final int INVOCATION_LINK = 2;
+    /** Unknown invocation method */
+    public static final int INVOCATION_UNKNOWN = 0;
+
+    private static final String NO_SIGNATURE = "";
+
+    private final int mAbsoluteStart;
+    private final int mAbsoluteEnd;
+    private final @EntityType String mEntityType;
+
+    private @EventType int mEventType;
+    private String mPackageName = "";
+    private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
+    private @InvocationMethod int mInvocationMethod;
+    @Nullable private String mWidgetVersion;
+    @Nullable private String mResultId;
+    private long mEventTime;
+    private long mDurationSinceSessionStart;
+    private long mDurationSincePreviousEvent;
+    private int mEventIndex;
+    @Nullable private TextClassificationSessionId mSessionId;
+    private int mStart;
+    private int mEnd;
+    private int mSmartStart;
+    private int mSmartEnd;
+
+    SelectionEvent(
+            int start, int end,
+            @EventType int eventType, @EntityType String entityType,
+            @InvocationMethod int invocationMethod, @Nullable String resultId) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        mAbsoluteStart = start;
+        mAbsoluteEnd = end;
+        mEventType = eventType;
+        mEntityType = Preconditions.checkNotNull(entityType);
+        mResultId = resultId;
+        mInvocationMethod = invocationMethod;
+    }
+
+    SelectionEvent(
+            int start, int end,
+            @EventType int eventType, @EntityType String entityType,
+            @InvocationMethod int invocationMethod, @Nullable String resultId,
+            Logger.Config config) {
+        this(start, end, eventType, entityType, invocationMethod, resultId);
+        Preconditions.checkNotNull(config);
+        setTextClassificationSessionContext(
+                new TextClassificationContext.Builder(
+                        config.getPackageName(), config.getWidgetType())
+                        .setWidgetVersion(config.getWidgetVersion())
+                        .build());
+    }
+
+    private SelectionEvent(Parcel in) {
+        mAbsoluteStart = in.readInt();
+        mAbsoluteEnd = in.readInt();
+        mEventType = in.readInt();
+        mEntityType = in.readString();
+        mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
+        mPackageName = in.readString();
+        mWidgetType = in.readString();
+        mInvocationMethod = in.readInt();
+        mResultId = in.readString();
+        mEventTime = in.readLong();
+        mDurationSinceSessionStart = in.readLong();
+        mDurationSincePreviousEvent = in.readLong();
+        mEventIndex = in.readInt();
+        mSessionId = in.readInt() > 0
+                ? TextClassificationSessionId.CREATOR.createFromParcel(in) : null;
+        mStart = in.readInt();
+        mEnd = in.readInt();
+        mSmartStart = in.readInt();
+        mSmartEnd = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAbsoluteStart);
+        dest.writeInt(mAbsoluteEnd);
+        dest.writeInt(mEventType);
+        dest.writeString(mEntityType);
+        dest.writeInt(mWidgetVersion != null ? 1 : 0);
+        if (mWidgetVersion != null) {
+            dest.writeString(mWidgetVersion);
+        }
+        dest.writeString(mPackageName);
+        dest.writeString(mWidgetType);
+        dest.writeInt(mInvocationMethod);
+        dest.writeString(mResultId);
+        dest.writeLong(mEventTime);
+        dest.writeLong(mDurationSinceSessionStart);
+        dest.writeLong(mDurationSincePreviousEvent);
+        dest.writeInt(mEventIndex);
+        dest.writeInt(mSessionId != null ? 1 : 0);
+        if (mSessionId != null) {
+            mSessionId.writeToParcel(dest, flags);
+        }
+        dest.writeInt(mStart);
+        dest.writeInt(mEnd);
+        dest.writeInt(mSmartStart);
+        dest.writeInt(mSmartEnd);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Creates a "selection started" event.
+     *
+     * @param invocationMethod  the way the selection was triggered
+     * @param start  the index of the selected text
+     */
+    @NonNull
+    public static SelectionEvent createSelectionStartedEvent(
+            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
+        return new SelectionEvent(
+                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
+                TextClassifier.TYPE_UNKNOWN, invocationMethod, NO_SIGNATURE);
+    }
+
+    /**
+     * Creates a "selection modified" event.
+     * Use when the user modifies the selection.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionModifiedEvent(int start, int end) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        return new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN, NO_SIGNATURE);
+    }
+
+    /**
+     * Creates a "selection modified" event.
+     * Use when the user modifies the selection and the selection's entity type is known.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionModifiedEvent(
+            int start, int end, @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        return new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                entityType, INVOCATION_UNKNOWN, classification.getId());
+    }
+
+    /**
+     * Creates a "selection modified" event.
+     * Use when a TextClassifier modifies the selection.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param selection  the TextSelection object returned by the TextClassifier for the
+     *      specified selection
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionModifiedEvent(
+            int start, int end, @NonNull TextSelection selection) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(selection);
+        final String entityType = selection.getEntityCount() > 0
+                ? selection.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        return new SelectionEvent(
+                start, end, SelectionEvent.EVENT_AUTO_SELECTION,
+                entityType, INVOCATION_UNKNOWN, selection.getId());
+    }
+
+    /**
+     * Creates an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        checkActionType(actionType);
+        return new SelectionEvent(
+                start, end, actionType, TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN,
+                NO_SIGNATURE);
+    }
+
+    /**
+     * Creates an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text and the selection's
+     * entity type is known.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     *
+     * @throws IllegalArgumentException if end is less than start
+     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
+     */
+    @NonNull
+    public static SelectionEvent createSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType,
+            @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+        checkActionType(actionType);
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        return new SelectionEvent(start, end, actionType, entityType, INVOCATION_UNKNOWN,
+                classification.getId());
+    }
+
+    /**
+     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
+     */
+    private static void checkActionType(@SelectionEvent.EventType int eventType)
+            throws IllegalArgumentException {
+        switch (eventType) {
+            case SelectionEvent.ACTION_OVERTYPE:  // fall through
+            case SelectionEvent.ACTION_COPY:  // fall through
+            case SelectionEvent.ACTION_PASTE:  // fall through
+            case SelectionEvent.ACTION_CUT:  // fall through
+            case SelectionEvent.ACTION_SHARE:  // fall through
+            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
+            case SelectionEvent.ACTION_DRAG:  // fall through
+            case SelectionEvent.ACTION_ABANDON:  // fall through
+            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
+            case SelectionEvent.ACTION_RESET:  // fall through
+                return;
+            default:
+                throw new IllegalArgumentException(
+                        String.format(Locale.US, "%d is not an eventType", eventType));
+        }
+    }
+
+    int getAbsoluteStart() {
+        return mAbsoluteStart;
+    }
+
+    int getAbsoluteEnd() {
+        return mAbsoluteEnd;
+    }
+
+    /**
+     * Returns the type of event that was triggered. e.g. {@link #ACTION_COPY}.
+     */
+    @EventType
+    public int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * Sets the event type.
+     */
+    void setEventType(@EventType int eventType) {
+        mEventType = eventType;
+    }
+
+    /**
+     * Returns the type of entity that is associated with this event. e.g.
+     * {@link android.view.textclassifier.TextClassifier#TYPE_EMAIL}.
+     */
+    @EntityType
+    @NonNull
+    public String getEntityType() {
+        return mEntityType;
+    }
+
+    /**
+     * Returns the package name of the app that this event originated in.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the type of widget that was involved in triggering this event.
+     */
+    @WidgetType
+    @NonNull
+    public String getWidgetType() {
+        return mWidgetType;
+    }
+
+    /**
+     * Returns a string version info for the widget this event was triggered in.
+     */
+    @Nullable
+    public String getWidgetVersion() {
+        return mWidgetVersion;
+    }
+
+    /**
+     * Sets the {@link TextClassificationContext} for this event.
+     */
+    void setTextClassificationSessionContext(TextClassificationContext context) {
+        mPackageName = context.getPackageName();
+        mWidgetType = context.getWidgetType();
+        mWidgetVersion = context.getWidgetVersion();
+    }
+
+    /**
+     * Returns the way the selection mode was invoked.
+     */
+    public @InvocationMethod int getInvocationMethod() {
+        return mInvocationMethod;
+    }
+
+    /**
+     * Sets the invocationMethod for this event.
+     */
+    void setInvocationMethod(@InvocationMethod int invocationMethod) {
+        mInvocationMethod = invocationMethod;
+    }
+
+    /**
+     * Returns the id of the text classifier result associated with this event.
+     */
+    @Nullable
+    public String getResultId() {
+        return mResultId;
+    }
+
+    SelectionEvent setResultId(@Nullable String resultId) {
+        mResultId = resultId;
+        return this;
+    }
+
+    /**
+     * Returns the time this event was triggered.
+     */
+    public long getEventTime() {
+        return mEventTime;
+    }
+
+    SelectionEvent setEventTime(long timeMs) {
+        mEventTime = timeMs;
+        return this;
+    }
+
+    /**
+     * Returns the duration in ms between when this event was triggered and when the first event in
+     * the selection session was triggered.
+     */
+    public long getDurationSinceSessionStart() {
+        return mDurationSinceSessionStart;
+    }
+
+    SelectionEvent setDurationSinceSessionStart(long durationMs) {
+        mDurationSinceSessionStart = durationMs;
+        return this;
+    }
+
+    /**
+     * Returns the duration in ms between when this event was triggered and when the previous event
+     * in the selection session was triggered.
+     */
+    public long getDurationSincePreviousEvent() {
+        return mDurationSincePreviousEvent;
+    }
+
+    SelectionEvent setDurationSincePreviousEvent(long durationMs) {
+        this.mDurationSincePreviousEvent = durationMs;
+        return this;
+    }
+
+    /**
+     * Returns the index (e.g. 1st event, 2nd event, etc.) of this event in the selection session.
+     */
+    public int getEventIndex() {
+        return mEventIndex;
+    }
+
+    SelectionEvent setEventIndex(int index) {
+        mEventIndex = index;
+        return this;
+    }
+
+    /**
+     * Returns the selection session id.
+     */
+    @Nullable
+    public TextClassificationSessionId getSessionId() {
+        return mSessionId;
+    }
+
+    SelectionEvent setSessionId(TextClassificationSessionId id) {
+        mSessionId = id;
+        return this;
+    }
+
+    /**
+     * Returns the start index of this events relative to the index of the start selection
+     * event in the selection session.
+     */
+    public int getStart() {
+        return mStart;
+    }
+
+    SelectionEvent setStart(int start) {
+        mStart = start;
+        return this;
+    }
+
+    /**
+     * Returns the end index of this events relative to the index of the start selection
+     * event in the selection session.
+     */
+    public int getEnd() {
+        return mEnd;
+    }
+
+    SelectionEvent setEnd(int end) {
+        mEnd = end;
+        return this;
+    }
+
+    /**
+     * Returns the start index of this events relative to the index of the smart selection
+     * event in the selection session.
+     */
+    public int getSmartStart() {
+        return mSmartStart;
+    }
+
+    SelectionEvent setSmartStart(int start) {
+        this.mSmartStart = start;
+        return this;
+    }
+
+    /**
+     * Returns the end index of this events relative to the index of the smart selection
+     * event in the selection session.
+     */
+    public int getSmartEnd() {
+        return mSmartEnd;
+    }
+
+    SelectionEvent setSmartEnd(int end) {
+        mSmartEnd = end;
+        return this;
+    }
+
+    boolean isTerminal() {
+        return isTerminal(mEventType);
+    }
+
+    /**
+     * Returns true if the eventType is a terminal event type. Otherwise returns false.
+     * A terminal event is an event that ends a selection interaction.
+     */
+    public static boolean isTerminal(@EventType int eventType) {
+        switch (eventType) {
+            case ACTION_OVERTYPE:  // fall through
+            case ACTION_COPY:  // fall through
+            case ACTION_PASTE:  // fall through
+            case ACTION_CUT:  // fall through
+            case ACTION_SHARE:  // fall through
+            case ACTION_SMART_SHARE:  // fall through
+            case ACTION_DRAG:  // fall through
+            case ACTION_ABANDON:  // fall through
+            case ACTION_OTHER:  // fall through
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
+                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
+                mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
+                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SelectionEvent)) {
+            return false;
+        }
+
+        final SelectionEvent other = (SelectionEvent) obj;
+        return mAbsoluteStart == other.mAbsoluteStart
+                && mAbsoluteEnd == other.mAbsoluteEnd
+                && mEventType == other.mEventType
+                && Objects.equals(mEntityType, other.mEntityType)
+                && Objects.equals(mWidgetVersion, other.mWidgetVersion)
+                && Objects.equals(mPackageName, other.mPackageName)
+                && Objects.equals(mWidgetType, other.mWidgetType)
+                && mInvocationMethod == other.mInvocationMethod
+                && Objects.equals(mResultId, other.mResultId)
+                && mEventTime == other.mEventTime
+                && mDurationSinceSessionStart == other.mDurationSinceSessionStart
+                && mDurationSincePreviousEvent == other.mDurationSincePreviousEvent
+                && mEventIndex == other.mEventIndex
+                && Objects.equals(mSessionId, other.mSessionId)
+                && mStart == other.mStart
+                && mEnd == other.mEnd
+                && mSmartStart == other.mSmartStart
+                && mSmartEnd == other.mSmartEnd;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US,
+                "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
+                        + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
+                        + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+                        + "durationSincePreviousEvent=%d, eventIndex=%d,"
+                        + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
+                mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
+                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
+                mResultId, mEventTime, mDurationSinceSessionStart,
+                mDurationSincePreviousEvent, mEventIndex,
+                mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+    }
+
+    public static final Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
+        @Override
+        public SelectionEvent createFromParcel(Parcel in) {
+            return new SelectionEvent(in);
+        }
+
+        @Override
+        public SelectionEvent[] newArray(int size) {
+            return new SelectionEvent[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/android/view/textclassifier/SmartSelection.java b/android/view/textclassifier/SmartSelection.java
deleted file mode 100644
index 2c93a19..0000000
--- a/android/view/textclassifier/SmartSelection.java
+++ /dev/null
@@ -1,180 +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.view.textclassifier;
-
-import android.content.res.AssetFileDescriptor;
-
-/**
- *  Java wrapper for SmartSelection native library interface.
- *  This library is used for detecting entities in text.
- */
-final class SmartSelection {
-
-    static {
-        System.loadLibrary("textclassifier");
-    }
-
-    /** Hints the classifier that this may be a url. */
-    static final int HINT_FLAG_URL = 0x01;
-    /** Hints the classifier that this may be an email. */
-    static final int HINT_FLAG_EMAIL = 0x02;
-
-    private final long mCtx;
-
-    /**
-     * Creates a new instance of SmartSelect predictor, using the provided model image,
-     * given as a file descriptor.
-     */
-    SmartSelection(int fd) {
-        mCtx = nativeNew(fd);
-    }
-
-    /**
-     * Creates a new instance of SmartSelect predictor, using the provided model image, given as a
-     * file path.
-     */
-    SmartSelection(String path) {
-        mCtx = nativeNewFromPath(path);
-    }
-
-    /**
-     * Creates a new instance of SmartSelect predictor, using the provided model image, given as an
-     * AssetFileDescriptor.
-     */
-    SmartSelection(AssetFileDescriptor afd) {
-        mCtx = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength());
-        if (mCtx == 0L) {
-            throw new IllegalArgumentException(
-                "Couldn't initialize TC from given AssetFileDescriptor");
-        }
-    }
-
-    /**
-     * Given a string context and current selection, computes the SmartSelection suggestion.
-     *
-     * The begin and end are character indices into the context UTF8 string. selectionBegin is the
-     * character index where the selection begins, and selectionEnd is the index of one character
-     * past the selection span.
-     *
-     * The return value is an array of two ints: suggested selection beginning and end, with the
-     * same semantics as the input selectionBeginning and selectionEnd.
-     */
-    public int[] suggest(String context, int selectionBegin, int selectionEnd) {
-        return nativeSuggest(mCtx, context, selectionBegin, selectionEnd);
-    }
-
-    /**
-     * Given a string context and current selection, classifies the type of the selected text.
-     *
-     * The begin and end params are character indices in the context string.
-     *
-     * Returns an array of ClassificationResult objects with the probability
-     * scores for different collections.
-     */
-    public ClassificationResult[] classifyText(
-            String context, int selectionBegin, int selectionEnd, int hintFlags) {
-        return nativeClassifyText(mCtx, context, selectionBegin, selectionEnd, hintFlags);
-    }
-
-    /**
-     * Annotates given input text. Every word of the input is a part of some annotation.
-     * The annotations are sorted by their position in the context string.
-     * The annotations do not overlap.
-     */
-    public AnnotatedSpan[] annotate(String text) {
-        return nativeAnnotate(mCtx, text);
-    }
-
-    /**
-     * Frees up the allocated memory.
-     */
-    public void close() {
-        nativeClose(mCtx);
-    }
-
-    /**
-     * Returns the language of the model.
-     */
-    public static String getLanguage(int fd) {
-        return nativeGetLanguage(fd);
-    }
-
-    /**
-     * Returns the version of the model.
-     */
-    public static int getVersion(int fd) {
-        return nativeGetVersion(fd);
-    }
-
-    private static native long nativeNew(int fd);
-
-    private static native long nativeNewFromPath(String path);
-
-    private static native long nativeNewFromAssetFileDescriptor(AssetFileDescriptor afd,
-                                                                long offset, long size);
-
-    private static native int[] nativeSuggest(
-            long context, String text, int selectionBegin, int selectionEnd);
-
-    private static native ClassificationResult[] nativeClassifyText(
-            long context, String text, int selectionBegin, int selectionEnd, int hintFlags);
-
-    private static native AnnotatedSpan[] nativeAnnotate(long context, String text);
-
-    private static native void nativeClose(long context);
-
-    private static native String nativeGetLanguage(int fd);
-
-    private static native int nativeGetVersion(int fd);
-
-    /** Classification result for classifyText method. */
-    static final class ClassificationResult {
-        final String mCollection;
-        /** float range: 0 - 1 */
-        final float mScore;
-
-        ClassificationResult(String collection, float score) {
-            mCollection = collection;
-            mScore = score;
-        }
-    }
-
-    /** Represents a result of Annotate call. */
-    public static final class AnnotatedSpan {
-        final int mStartIndex;
-        final int mEndIndex;
-        final ClassificationResult[] mClassification;
-
-        AnnotatedSpan(int startIndex, int endIndex, ClassificationResult[] classification) {
-            mStartIndex = startIndex;
-            mEndIndex = endIndex;
-            mClassification = classification;
-        }
-
-        public int getStartIndex() {
-            return mStartIndex;
-        }
-
-        public int getEndIndex() {
-            return mEndIndex;
-        }
-
-        public ClassificationResult[] getClassification() {
-            return mClassification;
-        }
-    }
-}
diff --git a/android/view/textclassifier/SystemTextClassifier.java b/android/view/textclassifier/SystemTextClassifier.java
new file mode 100644
index 0000000..45fd6bf
--- /dev/null
+++ b/android/view/textclassifier/SystemTextClassifier.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.textclassifier.ITextClassificationCallback;
+import android.service.textclassifier.ITextClassifierService;
+import android.service.textclassifier.ITextLinksCallback;
+import android.service.textclassifier.ITextSelectionCallback;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Proxy to the system's default TextClassifier.
+ * @hide
+ */
+@VisibleForTesting(visibility = Visibility.PACKAGE)
+public final class SystemTextClassifier implements TextClassifier {
+
+    private static final String LOG_TAG = "SystemTextClassifier";
+
+    private final ITextClassifierService mManagerService;
+    private final TextClassificationConstants mSettings;
+    private final TextClassifier mFallback;
+    private final String mPackageName;
+
+    private final Object mLoggerLock = new Object();
+    @GuardedBy("mLoggerLock")
+    private Logger.Config mLoggerConfig;
+    @GuardedBy("mLoggerLock")
+    private Logger mLogger;
+    @GuardedBy("mLoggerLock")
+    private TextClassificationSessionId mSessionId;
+
+    public SystemTextClassifier(Context context, TextClassificationConstants settings)
+                throws ServiceManager.ServiceNotFoundException {
+        mManagerService = ITextClassifierService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
+        mSettings = Preconditions.checkNotNull(settings);
+        mFallback = new TextClassifierImpl(context, settings);
+        mPackageName = Preconditions.checkNotNull(context.getPackageName());
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    @WorkerThread
+    public TextSelection suggestSelection(TextSelection.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        try {
+            final TextSelectionCallback callback = new TextSelectionCallback();
+            mManagerService.onSuggestSelection(mSessionId, request, callback);
+            final TextSelection selection = callback.mReceiver.get();
+            if (selection != null) {
+                return selection;
+            }
+        } catch (RemoteException | InterruptedException e) {
+            Log.e(LOG_TAG, "Error suggesting selection for text. Using fallback.", e);
+        }
+        return mFallback.suggestSelection(request);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    @WorkerThread
+    public TextClassification classifyText(TextClassification.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        try {
+            final TextClassificationCallback callback = new TextClassificationCallback();
+            mManagerService.onClassifyText(mSessionId, request, callback);
+            final TextClassification classification = callback.mReceiver.get();
+            if (classification != null) {
+                return classification;
+            }
+        } catch (RemoteException | InterruptedException e) {
+            Log.e(LOG_TAG, "Error classifying text. Using fallback.", e);
+        }
+        return mFallback.classifyText(request);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    @WorkerThread
+    public TextLinks generateLinks(@NonNull TextLinks.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+
+        if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
+            return Utils.generateLegacyLinks(request);
+        }
+
+        try {
+            request.setCallingPackageName(mPackageName);
+            final TextLinksCallback callback = new TextLinksCallback();
+            mManagerService.onGenerateLinks(mSessionId, request, callback);
+            final TextLinks links = callback.mReceiver.get();
+            if (links != null) {
+                return links;
+            }
+        } catch (RemoteException | InterruptedException e) {
+            Log.e(LOG_TAG, "Error generating links. Using fallback.", e);
+        }
+        return mFallback.generateLinks(request);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    @Override
+    @WorkerThread
+    public int getMaxGenerateLinksTextLength() {
+        // TODO: retrieve this from the bound service.
+        return mFallback.getMaxGenerateLinksTextLength();
+    }
+
+    @Override
+    public Logger getLogger(@NonNull Logger.Config config) {
+        Preconditions.checkNotNull(config);
+        synchronized (mLoggerLock) {
+            if (mLogger == null || !config.equals(mLoggerConfig)) {
+                mLoggerConfig = config;
+                mLogger = new Logger(config) {
+                    @Override
+                    public void writeEvent(SelectionEvent event) {
+                        try {
+                            mManagerService.onSelectionEvent(mSessionId, event);
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, "Error reporting selection event.", e);
+                        }
+                    }
+                };
+            }
+        }
+        return mLogger;
+    }
+
+    @Override
+    public void destroy() {
+        try {
+            if (mSessionId != null) {
+                mManagerService.onDestroyTextClassificationSession(mSessionId);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Error destroying classification session.", e);
+        }
+    }
+
+    /**
+     * Attempts to initialize a new classification session.
+     *
+     * @param classificationContext the classification context
+     * @param sessionId the session's id
+     */
+    void initializeRemoteSession(
+            @NonNull TextClassificationContext classificationContext,
+            @NonNull TextClassificationSessionId sessionId) {
+        mSessionId = Preconditions.checkNotNull(sessionId);
+        try {
+            mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Error starting a new classification session.", e);
+        }
+    }
+
+    private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
+
+        final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
+
+        @Override
+        public void onSuccess(TextSelection selection) {
+            mReceiver.onSuccess(selection);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class TextClassificationCallback extends ITextClassificationCallback.Stub {
+
+        final ResponseReceiver<TextClassification> mReceiver = new ResponseReceiver<>();
+
+        @Override
+        public void onSuccess(TextClassification classification) {
+            mReceiver.onSuccess(classification);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class TextLinksCallback extends ITextLinksCallback.Stub {
+
+        final ResponseReceiver<TextLinks> mReceiver = new ResponseReceiver<>();
+
+        @Override
+        public void onSuccess(TextLinks links) {
+            mReceiver.onSuccess(links);
+        }
+
+        @Override
+        public void onFailure() {
+            mReceiver.onFailure();
+        }
+    }
+
+    private static final class ResponseReceiver<T> {
+
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+
+        private T mResponse;
+
+        public void onSuccess(T response) {
+            mResponse = response;
+            mLatch.countDown();
+        }
+
+        public void onFailure() {
+            Log.e(LOG_TAG, "Request failed.", null);
+            mLatch.countDown();
+        }
+
+        @Nullable
+        public T get() throws InterruptedException {
+            // If this is running on the main thread, do not block for a response.
+            // The response will unfortunately be null and the TextClassifier should depend on its
+            // fallback.
+            // NOTE that TextClassifier calls should preferably always be called on a worker thread.
+            if (Looper.myLooper() != Looper.getMainLooper()) {
+                mLatch.await(2, TimeUnit.SECONDS);
+            }
+            return mResponse;
+        }
+    }
+}
diff --git a/android/view/textclassifier/TextClassification.java b/android/view/textclassifier/TextClassification.java
index 7089677..37a5d9a 100644
--- a/android/view/textclassifier/TextClassification.java
+++ b/android/view/textclassifier/TextClassification.java
@@ -17,11 +17,17 @@
 package android.view.textclassifier;
 
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
@@ -32,10 +38,15 @@
 import android.util.ArrayMap;
 import android.view.View.OnClickListener;
 import android.view.textclassifier.TextClassifier.EntityType;
+import android.view.textclassifier.TextClassifier.Utils;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -70,25 +81,16 @@
  *   view.startActionMode(new ActionMode.Callback() {
  *
  *       public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- *           // Add the "primary" action.
- *           if (thisAppHasPermissionToInvokeIntent(classification.getIntent())) {
- *              menu.add(Menu.NONE, 0, 20, classification.getLabel())
- *                 .setIcon(classification.getIcon())
- *                 .setIntent(classification.getIntent());
- *           }
- *           // Add the "secondary" actions.
- *           for (int i = 0; i < classification.getSecondaryActionsCount(); i++) {
- *               if (thisAppHasPermissionToInvokeIntent(classification.getSecondaryIntent(i))) {
- *                   menu.add(Menu.NONE, i + 1, 20, classification.getSecondaryLabel(i))
- *                      .setIcon(classification.getSecondaryIcon(i))
- *                      .setIntent(classification.getSecondaryIntent(i));
- *               }
+ *           for (int i = 0; i < classification.getActions().size(); ++i) {
+ *              RemoteAction action = classification.getActions().get(i);
+ *              menu.add(Menu.NONE, i, 20, action.getTitle())
+ *                 .setIcon(action.getIcon());
  *           }
  *           return true;
  *       }
  *
  *       public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- *           context.startActivity(item.getIntent());
+ *           classification.getActions().get(item.getItemId()).getActionIntent().send();
  *           return true;
  *       }
  *
@@ -96,51 +98,51 @@
  *   });
  * }</pre>
  */
-public final class TextClassification {
+public final class TextClassification implements Parcelable {
 
     /**
      * @hide
      */
     static final TextClassification EMPTY = new TextClassification.Builder().build();
 
+    private static final String LOG_TAG = "TextClassification";
     // TODO(toki): investigate a way to derive this based on device properties.
-    private static final int MAX_PRIMARY_ICON_SIZE = 192;
-    private static final int MAX_SECONDARY_ICON_SIZE = 144;
+    private static final int MAX_LEGACY_ICON_SIZE = 192;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {IntentType.UNSUPPORTED, IntentType.ACTIVITY, IntentType.SERVICE})
+    private @interface IntentType {
+        int UNSUPPORTED = -1;
+        int ACTIVITY = 0;
+        int SERVICE = 1;
+    }
 
     @NonNull private final String mText;
-    @Nullable private final Drawable mPrimaryIcon;
-    @Nullable private final String mPrimaryLabel;
-    @Nullable private final Intent mPrimaryIntent;
-    @Nullable private final OnClickListener mPrimaryOnClickListener;
-    @NonNull private final List<Drawable> mSecondaryIcons;
-    @NonNull private final List<String> mSecondaryLabels;
-    @NonNull private final List<Intent> mSecondaryIntents;
+    @Nullable private final Drawable mLegacyIcon;
+    @Nullable private final String mLegacyLabel;
+    @Nullable private final Intent mLegacyIntent;
+    @Nullable private final OnClickListener mLegacyOnClickListener;
+    @NonNull private final List<RemoteAction> mActions;
     @NonNull private final EntityConfidence mEntityConfidence;
-    @NonNull private final String mSignature;
+    @Nullable private final String mId;
 
     private TextClassification(
             @Nullable String text,
-            @Nullable Drawable primaryIcon,
-            @Nullable String primaryLabel,
-            @Nullable Intent primaryIntent,
-            @Nullable OnClickListener primaryOnClickListener,
-            @NonNull List<Drawable> secondaryIcons,
-            @NonNull List<String> secondaryLabels,
-            @NonNull List<Intent> secondaryIntents,
+            @Nullable Drawable legacyIcon,
+            @Nullable String legacyLabel,
+            @Nullable Intent legacyIntent,
+            @Nullable OnClickListener legacyOnClickListener,
+            @NonNull List<RemoteAction> actions,
             @NonNull Map<String, Float> entityConfidence,
-            @NonNull String signature) {
-        Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size());
-        Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size());
+            @Nullable String id) {
         mText = text;
-        mPrimaryIcon = primaryIcon;
-        mPrimaryLabel = primaryLabel;
-        mPrimaryIntent = primaryIntent;
-        mPrimaryOnClickListener = primaryOnClickListener;
-        mSecondaryIcons = secondaryIcons;
-        mSecondaryLabels = secondaryLabels;
-        mSecondaryIntents = secondaryIntents;
+        mLegacyIcon = legacyIcon;
+        mLegacyLabel = legacyLabel;
+        mLegacyIntent = legacyIntent;
+        mLegacyOnClickListener = legacyOnClickListener;
+        mActions = Collections.unmodifiableList(actions);
         mEntityConfidence = new EntityConfidence(entityConfidence);
-        mSignature = signature;
+        mId = id;
     }
 
     /**
@@ -182,181 +184,143 @@
     }
 
     /**
-     * Returns the number of <i>secondary</i> actions that are available to act on the classified
-     * text.
-     *
-     * <p><strong>Note: </strong> that there may or may not be a <i>primary</i> action.
-     *
-     * @see #getSecondaryIntent(int)
-     * @see #getSecondaryLabel(int)
-     * @see #getSecondaryIcon(int)
+     * Returns a list of actions that may be performed on the text. The list is ordered based on
+     * the likelihood that a user will use the action, with the most likely action appearing first.
      */
-    @IntRange(from = 0)
-    public int getSecondaryActionsCount() {
-        return mSecondaryIntents.size();
+    public List<RemoteAction> getActions() {
+        return mActions;
     }
 
     /**
-     * Returns one of the <i>secondary</i> icons that maybe rendered on a widget used to act on the
-     * classified text.
+     * Returns an icon that may be rendered on a widget used to act on the classified text.
      *
-     * @param index Index of the action to get the icon for.
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getSecondaryActionsCount() for the number of actions available.
-     * @see #getSecondaryIntent(int)
-     * @see #getSecondaryLabel(int)
-     * @see #getIcon()
+     * @deprecated Use {@link #getActions()} instead.
      */
-    @Nullable
-    public Drawable getSecondaryIcon(int index) {
-        return mSecondaryIcons.get(index);
-    }
-
-    /**
-     * Returns an icon for the <i>primary</i> intent that may be rendered on a widget used to act
-     * on the classified text.
-     *
-     * @see #getSecondaryIcon(int)
-     */
+    @Deprecated
     @Nullable
     public Drawable getIcon() {
-        return mPrimaryIcon;
+        return mLegacyIcon;
     }
 
     /**
-     * Returns one of the <i>secondary</i> labels that may be rendered on a widget used to act on
-     * the classified text.
+     * Returns a label that may be rendered on a widget used to act on the classified text.
      *
-     * @param index Index of the action to get the label for.
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getSecondaryActionsCount()
-     * @see #getSecondaryIntent(int)
-     * @see #getSecondaryIcon(int)
-     * @see #getLabel()
+     * @deprecated Use {@link #getActions()} instead.
      */
-    @Nullable
-    public CharSequence getSecondaryLabel(int index) {
-        return mSecondaryLabels.get(index);
-    }
-
-    /**
-     * Returns a label for the <i>primary</i> intent that may be rendered on a widget used to act
-     * on the classified text.
-     *
-     * @see #getSecondaryLabel(int)
-     */
+    @Deprecated
     @Nullable
     public CharSequence getLabel() {
-        return mPrimaryLabel;
+        return mLegacyLabel;
     }
 
     /**
-     * Returns one of the <i>secondary</i> intents that may be fired to act on the classified text.
+     * Returns an intent that may be fired to act on the classified text.
      *
-     * @param index Index of the action to get the intent for.
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getSecondaryActionsCount()
-     * @see #getSecondaryLabel(int)
-     * @see #getSecondaryIcon(int)
-     * @see #getIntent()
+     * @deprecated Use {@link #getActions()} instead.
      */
-    @Nullable
-    public Intent getSecondaryIntent(int index) {
-        return mSecondaryIntents.get(index);
-    }
-
-    /**
-     * Returns the <i>primary</i> intent that may be fired to act on the classified text.
-     *
-     * @see #getSecondaryIntent(int)
-     */
+    @Deprecated
     @Nullable
     public Intent getIntent() {
-        return mPrimaryIntent;
+        return mLegacyIntent;
     }
 
     /**
-     * Returns the <i>primary</i> OnClickListener that may be triggered to act on the classified
-     * text. This field is not parcelable and will be null for all objects read from a parcel.
-     * Instead, call Context#startActivity(Intent) with the result of #getSecondaryIntent(int).
-     * Note that this may fail if the activity doesn't have permission to send the intent.
+     * Returns the OnClickListener that may be triggered to act on the classified text. This field
+     * is not parcelable and will be null for all objects read from a parcel. Instead, call
+     * Context#startActivity(Intent) with the result of #getSecondaryIntent(int). Note that this may
+     * fail if the activity doesn't have permission to send the intent.
+     *
+     * @deprecated Use {@link #getActions()} instead.
      */
     @Nullable
     public OnClickListener getOnClickListener() {
-        return mPrimaryOnClickListener;
+        return mLegacyOnClickListener;
     }
 
     /**
-     * Returns the signature for this object.
-     * The TextClassifier that generates this object may use it as a way to internally identify
-     * this object.
+     * Returns the id, if one exists, for this object.
      */
-    @NonNull
-    public String getSignature() {
-        return mSignature;
+    @Nullable
+    public String getId() {
+        return mId;
     }
 
     @Override
     public String toString() {
-        return String.format(Locale.US, "TextClassification {"
-                        + "text=%s, entities=%s, "
-                        + "primaryLabel=%s, secondaryLabels=%s, "
-                        + "primaryIntent=%s, secondaryIntents=%s, "
-                        + "signature=%s}",
-                mText, mEntityConfidence,
-                mPrimaryLabel, mSecondaryLabels,
-                mPrimaryIntent, mSecondaryIntents,
-                mSignature);
-    }
-
-    /** Helper for parceling via #ParcelableWrapper. */
-    private void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mText);
-        final Bitmap primaryIconBitmap = drawableToBitmap(mPrimaryIcon, MAX_PRIMARY_ICON_SIZE);
-        dest.writeInt(primaryIconBitmap != null ? 1 : 0);
-        if (primaryIconBitmap != null) {
-            primaryIconBitmap.writeToParcel(dest, flags);
-        }
-        dest.writeString(mPrimaryLabel);
-        dest.writeInt(mPrimaryIntent != null ? 1 : 0);
-        if (mPrimaryIntent != null) {
-            mPrimaryIntent.writeToParcel(dest, flags);
-        }
-        // mPrimaryOnClickListener is not parcelable.
-        dest.writeTypedList(drawablesToBitmaps(mSecondaryIcons, MAX_SECONDARY_ICON_SIZE));
-        dest.writeStringList(mSecondaryLabels);
-        dest.writeTypedList(mSecondaryIntents);
-        mEntityConfidence.writeToParcel(dest, flags);
-        dest.writeString(mSignature);
-    }
-
-    /** Helper for unparceling via #ParcelableWrapper. */
-    private TextClassification(Parcel in) {
-        mText = in.readString();
-        mPrimaryIcon = in.readInt() == 0
-                ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in));
-        mPrimaryLabel = in.readString();
-        mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
-        mPrimaryOnClickListener = null;  // not parcelable
-        mSecondaryIcons = bitmapsToDrawables(in.createTypedArrayList(Bitmap.CREATOR));
-        mSecondaryLabels = in.createStringArrayList();
-        mSecondaryIntents = in.createTypedArrayList(Intent.CREATOR);
-        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
-        mSignature = in.readString();
+        return String.format(Locale.US,
+                "TextClassification {text=%s, entities=%s, actions=%s, id=%s}",
+                mText, mEntityConfidence, mActions, mId);
     }
 
     /**
-     * Creates an OnClickListener that starts an activity with the specified intent.
+     * Creates an OnClickListener that triggers the specified PendingIntent.
+     *
+     * @hide
+     */
+    public static OnClickListener createIntentOnClickListener(@NonNull final PendingIntent intent) {
+        Preconditions.checkNotNull(intent);
+        return v -> {
+            try {
+                intent.send();
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(LOG_TAG, "Error sending PendingIntent", e);
+            }
+        };
+    }
+
+    /**
+     * Creates a PendingIntent for the specified intent.
+     * Returns null if the intent is not supported for the specified context.
      *
      * @throws IllegalArgumentException if context or intent is null
      * @hide
      */
-    @NonNull
-    public static OnClickListener createStartActivityOnClickListener(
+    @Nullable
+    public static PendingIntent createPendingIntent(
             @NonNull final Context context, @NonNull final Intent intent) {
+        switch (getIntentType(intent, context)) {
+            case IntentType.ACTIVITY:
+                return PendingIntent.getActivity(context, 0, intent, 0);
+            case IntentType.SERVICE:
+                return PendingIntent.getService(context, 0, intent, 0);
+            default:
+                return null;
+        }
+    }
+
+    @IntentType
+    private static int getIntentType(@NonNull Intent intent, @NonNull Context context) {
         Preconditions.checkArgument(context != null);
         Preconditions.checkArgument(intent != null);
-        return v -> context.startActivity(intent);
+
+        final ResolveInfo activityRI = context.getPackageManager().resolveActivity(intent, 0);
+        if (activityRI != null) {
+            if (context.getPackageName().equals(activityRI.activityInfo.packageName)) {
+                return IntentType.ACTIVITY;
+            }
+            final boolean exported = activityRI.activityInfo.exported;
+            if (exported && hasPermission(context, activityRI.activityInfo.permission)) {
+                return IntentType.ACTIVITY;
+            }
+        }
+
+        final ResolveInfo serviceRI = context.getPackageManager().resolveService(intent, 0);
+        if (serviceRI != null) {
+            if (context.getPackageName().equals(serviceRI.serviceInfo.packageName)) {
+                return IntentType.SERVICE;
+            }
+            final boolean exported = serviceRI.serviceInfo.exported;
+            if (exported && hasPermission(context, serviceRI.serviceInfo.permission)) {
+                return IntentType.SERVICE;
+            }
+        }
+
+        return IntentType.UNSUPPORTED;
+    }
+
+    private static boolean hasPermission(@NonNull Context context, @NonNull String permission) {
+        return permission == null
+                || context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
 
     /**
@@ -395,33 +359,6 @@
     }
 
     /**
-     * Returns a list of drawables converted to Bitmaps
-     *
-     * @param drawables The drawables to convert.
-     * @param maxDims The maximum edge length of the resulting bitmaps (in pixels).
-     */
-    private static List<Bitmap> drawablesToBitmaps(List<Drawable> drawables, int maxDims) {
-        final List<Bitmap> bitmaps = new ArrayList<>(drawables.size());
-        for (Drawable drawable : drawables) {
-            bitmaps.add(drawableToBitmap(drawable, maxDims));
-        }
-        return bitmaps;
-    }
-
-    /** Returns a list of drawable wrappers for a list of bitmaps. */
-    private static List<Drawable> bitmapsToDrawables(List<Bitmap> bitmaps) {
-        final List<Drawable> drawables = new ArrayList<>(bitmaps.size());
-        for (Bitmap bitmap : bitmaps) {
-            if (bitmap != null) {
-                drawables.add(new BitmapDrawable(null, bitmap));
-            } else {
-                drawables.add(null);
-            }
-        }
-        return drawables;
-    }
-
-    /**
      * Builder for building {@link TextClassification} objects.
      *
      * <p>e.g.
@@ -431,28 +368,26 @@
      *          .setText(classifiedText)
      *          .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
      *          .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
-     *          .setPrimaryAction(intent, label, icon)
-     *          .addSecondaryAction(intent1, label1, icon1)
-     *          .addSecondaryAction(intent2, label2, icon2)
+     *          .addAction(remoteAction1)
+     *          .addAction(remoteAction2)
      *          .build();
      * }</pre>
      */
     public static final class Builder {
 
         @NonNull private String mText;
-        @NonNull private final List<Drawable> mSecondaryIcons = new ArrayList<>();
-        @NonNull private final List<String> mSecondaryLabels = new ArrayList<>();
-        @NonNull private final List<Intent> mSecondaryIntents = new ArrayList<>();
+        @NonNull private List<RemoteAction> mActions = new ArrayList<>();
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @Nullable Drawable mPrimaryIcon;
-        @Nullable String mPrimaryLabel;
-        @Nullable Intent mPrimaryIntent;
-        @Nullable OnClickListener mPrimaryOnClickListener;
-        @NonNull private String mSignature = "";
+        @Nullable Drawable mLegacyIcon;
+        @Nullable String mLegacyLabel;
+        @Nullable Intent mLegacyIntent;
+        @Nullable OnClickListener mLegacyOnClickListener;
+        @Nullable private String mId;
 
         /**
          * Sets the classified text.
          */
+        @NonNull
         public Builder setText(@Nullable String text) {
             mText = text;
             return this;
@@ -467,6 +402,7 @@
          *      0 implies the entity does not exist for the classified text.
          *      Values greater than 1 are clamped to 1.
          */
+        @NonNull
         public Builder setEntityType(
                 @NonNull @EntityType String type,
                 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
@@ -475,60 +411,27 @@
         }
 
         /**
-         * Adds an <i>secondary</i> action that may be performed on the classified text.
-         * Secondary actions are in addition to the <i>primary</i> action which may or may not
-         * exist.
-         *
-         * <p>The label and icon are used for rendering of widgets that offer the intent.
-         * Actions should be added in order of priority.
-         *
-         * <p><stong>Note: </stong> If all input parameters are set to null, this method will be a
-         * no-op.
-         *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * Adds an action that may be performed on the classified text. Actions should be added in
+         * order of likelihood that the user will use them, with the most likely action being added
+         * first.
          */
-        public Builder addSecondaryAction(
-                @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
-            if (intent != null || label != null || icon != null) {
-                mSecondaryIntents.add(intent);
-                mSecondaryLabels.add(label);
-                mSecondaryIcons.add(icon);
-            }
+        @NonNull
+        public Builder addAction(@NonNull RemoteAction action) {
+            Preconditions.checkArgument(action != null);
+            mActions.add(action);
             return this;
         }
 
         /**
-         * Removes all the <i>secondary</i> actions.
-         */
-        public Builder clearSecondaryActions() {
-            mSecondaryIntents.clear();
-            mSecondaryLabels.clear();
-            mSecondaryIcons.clear();
-            return this;
-        }
-
-        /**
-         * Sets the <i>primary</i> action that may be performed on the classified text. This is
-         * equivalent to calling {@code setIntent(intent).setLabel(label).setIcon(icon)}.
-         *
-         * <p><strong>Note: </strong>If all input parameters are null, there will be no
-         * <i>primary</i> action but there may still be <i>secondary</i> actions.
-         *
-         * @see #addSecondaryAction(Intent, String, Drawable)
-         */
-        public Builder setPrimaryAction(
-                @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
-            return setIntent(intent).setLabel(label).setIcon(icon);
-        }
-
-        /**
          * Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
          * on the classified text.
          *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
+        @NonNull
         public Builder setIcon(@Nullable Drawable icon) {
-            mPrimaryIcon = icon;
+            mLegacyIcon = icon;
             return this;
         }
 
@@ -536,10 +439,12 @@
          * Sets the label for the <i>primary</i> action that may be rendered on a widget used to
          * act on the classified text.
          *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
+        @NonNull
         public Builder setLabel(@Nullable String label) {
-            mPrimaryLabel = label;
+            mLegacyLabel = label;
             return this;
         }
 
@@ -547,10 +452,12 @@
          * Sets the intent for the <i>primary</i> action that may be fired to act on the classified
          * text.
          *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
+        @NonNull
         public Builder setIntent(@Nullable Intent intent) {
-            mPrimaryIntent = intent;
+            mLegacyIntent = intent;
             return this;
         }
 
@@ -558,51 +465,82 @@
          * Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
          * the classified text. This field is not parcelable and will always be null when the
          * object is read from a parcel.
+         *
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
+        @NonNull
         public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
-            mPrimaryOnClickListener = onClickListener;
+            mLegacyOnClickListener = onClickListener;
             return this;
         }
 
         /**
-         * Sets a signature for the TextClassification object.
-         * The TextClassifier that generates the TextClassification object may use it as a way to
-         * internally identify the TextClassification object.
+         * Sets an id for the TextClassification object.
          */
-        public Builder setSignature(@NonNull String signature) {
-            mSignature = Preconditions.checkNotNull(signature);
+        @NonNull
+        public Builder setId(@Nullable String id) {
+            mId = id;
             return this;
         }
 
         /**
          * Builds and returns a {@link TextClassification} object.
          */
+        @NonNull
         public TextClassification build() {
-            return new TextClassification(
-                    mText,
-                    mPrimaryIcon, mPrimaryLabel, mPrimaryIntent, mPrimaryOnClickListener,
-                    mSecondaryIcons, mSecondaryLabels, mSecondaryIntents,
-                    mEntityConfidence, mSignature);
+            return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
+                    mLegacyOnClickListener, mActions, mEntityConfidence, mId);
         }
     }
 
     /**
-     * Optional input parameters for generating TextClassification.
+     * A request object for generating TextClassification.
      */
-    public static final class Options implements Parcelable {
+    public static final class Request implements Parcelable {
 
-        private @Nullable LocaleList mDefaultLocales;
+        private final CharSequence mText;
+        private final int mStartIndex;
+        private final int mEndIndex;
+        @Nullable private final LocaleList mDefaultLocales;
+        @Nullable private final ZonedDateTime mReferenceTime;
 
-        public Options() {}
+        private Request(
+                CharSequence text,
+                int startIndex,
+                int endIndex,
+                LocaleList defaultLocales,
+                ZonedDateTime referenceTime) {
+            mText = text;
+            mStartIndex = startIndex;
+            mEndIndex = endIndex;
+            mDefaultLocales = defaultLocales;
+            mReferenceTime = referenceTime;
+        }
 
         /**
-         * @param defaultLocales ordered list of locale preferences that may be used to disambiguate
-         *      the provided text. If no locale preferences exist, set this to null or an empty
-         *      locale list.
+         * Returns the text providing context for the text to classify (which is specified
+         *      by the sub sequence starting at startIndex and ending at endIndex)
          */
-        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
-            mDefaultLocales = defaultLocales;
-            return this;
+        @NonNull
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Returns start index of the text to classify.
+         */
+        @IntRange(from = 0)
+        public int getStartIndex() {
+            return mStartIndex;
+        }
+
+        /**
+         * Returns end index of the text to classify.
+         */
+        @IntRange(from = 0)
+        public int getEndIndex() {
+            return mEndIndex;
         }
 
         /**
@@ -614,6 +552,78 @@
             return mDefaultLocales;
         }
 
+        /**
+         * @return reference time based on which relative dates (e.g. "tomorrow") should be
+         *      interpreted.
+         */
+        @Nullable
+        public ZonedDateTime getReferenceTime() {
+            return mReferenceTime;
+        }
+
+        /**
+         * A builder for building TextClassification requests.
+         */
+        public static final class Builder {
+
+            private final CharSequence mText;
+            private final int mStartIndex;
+            private final int mEndIndex;
+
+            @Nullable private LocaleList mDefaultLocales;
+            @Nullable private ZonedDateTime mReferenceTime;
+
+            /**
+             * @param text text providing context for the text to classify (which is specified
+             *      by the sub sequence starting at startIndex and ending at endIndex)
+             * @param startIndex start index of the text to classify
+             * @param endIndex end index of the text to classify
+             */
+            public Builder(
+                    @NonNull CharSequence text,
+                    @IntRange(from = 0) int startIndex,
+                    @IntRange(from = 0) int endIndex) {
+                Utils.checkArgument(text, startIndex, endIndex);
+                mText = text;
+                mStartIndex = startIndex;
+                mEndIndex = endIndex;
+            }
+
+            /**
+             * @param defaultLocales ordered list of locale preferences that may be used to
+             *      disambiguate the provided text. If no locale preferences exist, set this to null
+             *      or an empty locale list.
+             *
+             * @return this builder
+             */
+            @NonNull
+            public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
+                mDefaultLocales = defaultLocales;
+                return this;
+            }
+
+            /**
+             * @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
+             *      should be interpreted. This should usually be the time when the text was
+             *      originally composed. If no reference time is set, now is used.
+             *
+             * @return this builder
+             */
+            @NonNull
+            public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
+                mReferenceTime = referenceTime;
+                return this;
+            }
+
+            /**
+             * Builds and returns the request object.
+             */
+            @NonNull
+            public Request build() {
+                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime);
+            }
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -621,72 +631,94 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mText.toString());
+            dest.writeInt(mStartIndex);
+            dest.writeInt(mEndIndex);
             dest.writeInt(mDefaultLocales != null ? 1 : 0);
             if (mDefaultLocales != null) {
                 mDefaultLocales.writeToParcel(dest, flags);
             }
+            dest.writeInt(mReferenceTime != null ? 1 : 0);
+            if (mReferenceTime != null) {
+                dest.writeString(mReferenceTime.toString());
+            }
         }
 
-        public static final Parcelable.Creator<Options> CREATOR =
-                new Parcelable.Creator<Options>() {
+        public static final Parcelable.Creator<Request> CREATOR =
+                new Parcelable.Creator<Request>() {
                     @Override
-                    public Options createFromParcel(Parcel in) {
-                        return new Options(in);
+                    public Request createFromParcel(Parcel in) {
+                        return new Request(in);
                     }
 
                     @Override
-                    public Options[] newArray(int size) {
-                        return new Options[size];
+                    public Request[] newArray(int size) {
+                        return new Request[size];
                     }
                 };
 
-        private Options(Parcel in) {
-            if (in.readInt() > 0) {
-                mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
-            }
+        private Request(Parcel in) {
+            mText = in.readString();
+            mStartIndex = in.readInt();
+            mEndIndex = in.readInt();
+            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
+            mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
         }
     }
 
-    /**
-     * Parcelable wrapper for TextClassification objects.
-     * @hide
-     */
-    public static final class ParcelableWrapper implements Parcelable {
+    @Override
+    public int describeContents() {
+        return 0;
+    }
 
-        @NonNull private TextClassification mTextClassification;
-
-        public ParcelableWrapper(@NonNull TextClassification textClassification) {
-            Preconditions.checkNotNull(textClassification);
-            mTextClassification = textClassification;
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mText);
+        final Bitmap legacyIconBitmap = drawableToBitmap(mLegacyIcon, MAX_LEGACY_ICON_SIZE);
+        dest.writeInt(legacyIconBitmap != null ? 1 : 0);
+        if (legacyIconBitmap != null) {
+            legacyIconBitmap.writeToParcel(dest, flags);
         }
-
-        @NonNull
-        public TextClassification getTextClassification() {
-            return mTextClassification;
+        dest.writeString(mLegacyLabel);
+        dest.writeInt(mLegacyIntent != null ? 1 : 0);
+        if (mLegacyIntent != null) {
+            mLegacyIntent.writeToParcel(dest, flags);
         }
+        // mOnClickListener is not parcelable.
+        dest.writeTypedList(mActions);
+        mEntityConfidence.writeToParcel(dest, flags);
+        dest.writeString(mId);
+    }
 
-        @Override
-        public int describeContents() {
-            return 0;
+    public static final Parcelable.Creator<TextClassification> CREATOR =
+            new Parcelable.Creator<TextClassification>() {
+                @Override
+                public TextClassification createFromParcel(Parcel in) {
+                    return new TextClassification(in);
+                }
+
+                @Override
+                public TextClassification[] newArray(int size) {
+                    return new TextClassification[size];
+                }
+            };
+
+    private TextClassification(Parcel in) {
+        mText = in.readString();
+        mLegacyIcon = in.readInt() == 0
+                ? null
+                : new BitmapDrawable(Resources.getSystem(), Bitmap.CREATOR.createFromParcel(in));
+        mLegacyLabel = in.readString();
+        if (in.readInt() == 0) {
+            mLegacyIntent = null;
+        } else {
+            mLegacyIntent = Intent.CREATOR.createFromParcel(in);
+            mLegacyIntent.removeFlags(
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            mTextClassification.writeToParcel(dest, flags);
-        }
-
-        public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
-                new Parcelable.Creator<ParcelableWrapper>() {
-                    @Override
-                    public ParcelableWrapper createFromParcel(Parcel in) {
-                        return new ParcelableWrapper(new TextClassification(in));
-                    }
-
-                    @Override
-                    public ParcelableWrapper[] newArray(int size) {
-                        return new ParcelableWrapper[size];
-                    }
-                };
-
+        mLegacyOnClickListener = null;  // not parcelable
+        mActions = in.createTypedArrayList(RemoteAction.CREATOR);
+        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+        mId = in.readString();
     }
 }
diff --git a/android/view/textclassifier/TextClassificationConstants.java b/android/view/textclassifier/TextClassificationConstants.java
new file mode 100644
index 0000000..21b5603
--- /dev/null
+++ b/android/view/textclassifier/TextClassificationConstants.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.Nullable;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * TextClassifier specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * <pre>
+ * smart_linkify_enabled                    (boolean)
+ * system_textclassifier_enabled            (boolean)
+ * model_dark_launch_enabled                (boolean)
+ * smart_selection_enabled                  (boolean)
+ * smart_text_share_enabled                 (boolean)
+ * smart_linkify_enabled                    (boolean)
+ * smart_select_animation_enabled           (boolean)
+ * suggest_selection_max_range_length       (int)
+ * classify_text_max_range_length           (int)
+ * generate_links_max_text_length           (int)
+ * generate_links_log_sample_rate           (int)
+ * entity_list_default                      (String[])
+ * entity_list_not_editable                 (String[])
+ * entity_list_editable                     (String[])
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
+ *
+ * Example of setting the values for testing.
+ * adb shell settings put global text_classifier_constants \
+ *      model_dark_launch_enabled=true,smart_selection_enabled=true,\
+ *      entity_list_default=phone:address
+ * @hide
+ */
+public final class TextClassificationConstants {
+
+    private static final String LOG_TAG = "TextClassificationConstants";
+
+    private static final String LOCAL_TEXT_CLASSIFIER_ENABLED =
+            "local_textclassifier_enabled";
+    private static final String SYSTEM_TEXT_CLASSIFIER_ENABLED =
+            "system_textclassifier_enabled";
+    private static final String MODEL_DARK_LAUNCH_ENABLED =
+            "model_dark_launch_enabled";
+    private static final String SMART_SELECTION_ENABLED =
+            "smart_selection_enabled";
+    private static final String SMART_TEXT_SHARE_ENABLED =
+            "smart_text_share_enabled";
+    private static final String SMART_LINKIFY_ENABLED =
+            "smart_linkify_enabled";
+    private static final String SMART_SELECT_ANIMATION_ENABLED =
+            "smart_select_animation_enabled";
+    private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
+            "suggest_selection_max_range_length";
+    private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
+            "classify_text_max_range_length";
+    private static final String GENERATE_LINKS_MAX_TEXT_LENGTH =
+            "generate_links_max_text_length";
+    private static final String GENERATE_LINKS_LOG_SAMPLE_RATE =
+            "generate_links_log_sample_rate";
+    private static final String ENTITY_LIST_DEFAULT =
+            "entity_list_default";
+    private static final String ENTITY_LIST_NOT_EDITABLE =
+            "entity_list_not_editable";
+    private static final String ENTITY_LIST_EDITABLE =
+            "entity_list_editable";
+
+    private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
+    private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
+    private static final boolean MODEL_DARK_LAUNCH_ENABLED_DEFAULT = false;
+    private static final boolean SMART_SELECTION_ENABLED_DEFAULT = true;
+    private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
+    private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
+    private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
+    private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
+    private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
+    private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
+    private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
+    private static final String ENTITY_LIST_DELIMITER = ":";
+    private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(ENTITY_LIST_DELIMITER)
+            .add(TextClassifier.TYPE_ADDRESS)
+            .add(TextClassifier.TYPE_EMAIL)
+            .add(TextClassifier.TYPE_PHONE)
+            .add(TextClassifier.TYPE_URL)
+            .add(TextClassifier.TYPE_DATE)
+            .add(TextClassifier.TYPE_DATE_TIME)
+            .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
+
+    private final boolean mSystemTextClassifierEnabled;
+    private final boolean mLocalTextClassifierEnabled;
+    private final boolean mModelDarkLaunchEnabled;
+    private final boolean mSmartSelectionEnabled;
+    private final boolean mSmartTextShareEnabled;
+    private final boolean mSmartLinkifyEnabled;
+    private final boolean mSmartSelectionAnimationEnabled;
+    private final int mSuggestSelectionMaxRangeLength;
+    private final int mClassifyTextMaxRangeLength;
+    private final int mGenerateLinksMaxTextLength;
+    private final int mGenerateLinksLogSampleRate;
+    private final List<String> mEntityListDefault;
+    private final List<String> mEntityListNotEditable;
+    private final List<String> mEntityListEditable;
+
+    private TextClassificationConstants(@Nullable String settings) {
+        final KeyValueListParser parser = new KeyValueListParser(',');
+        try {
+            parser.setString(settings);
+        } catch (IllegalArgumentException e) {
+            // Failed to parse the settings string, log this and move on with defaults.
+            Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
+        }
+        mSystemTextClassifierEnabled = parser.getBoolean(
+                SYSTEM_TEXT_CLASSIFIER_ENABLED,
+                SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
+        mLocalTextClassifierEnabled = parser.getBoolean(
+                LOCAL_TEXT_CLASSIFIER_ENABLED,
+                LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
+        mModelDarkLaunchEnabled = parser.getBoolean(
+                MODEL_DARK_LAUNCH_ENABLED,
+                MODEL_DARK_LAUNCH_ENABLED_DEFAULT);
+        mSmartSelectionEnabled = parser.getBoolean(
+                SMART_SELECTION_ENABLED,
+                SMART_SELECTION_ENABLED_DEFAULT);
+        mSmartTextShareEnabled = parser.getBoolean(
+                SMART_TEXT_SHARE_ENABLED,
+                SMART_TEXT_SHARE_ENABLED_DEFAULT);
+        mSmartLinkifyEnabled = parser.getBoolean(
+                SMART_LINKIFY_ENABLED,
+                SMART_LINKIFY_ENABLED_DEFAULT);
+        mSmartSelectionAnimationEnabled = parser.getBoolean(
+                SMART_SELECT_ANIMATION_ENABLED,
+                SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
+        mSuggestSelectionMaxRangeLength = parser.getInt(
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+                SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
+        mClassifyTextMaxRangeLength = parser.getInt(
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH,
+                CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
+        mGenerateLinksMaxTextLength = parser.getInt(
+                GENERATE_LINKS_MAX_TEXT_LENGTH,
+                GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
+        mGenerateLinksLogSampleRate = parser.getInt(
+                GENERATE_LINKS_LOG_SAMPLE_RATE,
+                GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
+        mEntityListDefault = parseEntityList(parser.getString(
+                ENTITY_LIST_DEFAULT,
+                ENTITY_LIST_DEFAULT_VALUE));
+        mEntityListNotEditable = parseEntityList(parser.getString(
+                ENTITY_LIST_NOT_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE));
+        mEntityListEditable = parseEntityList(parser.getString(
+                ENTITY_LIST_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE));
+    }
+
+    /** Load from a settings string. */
+    public static TextClassificationConstants loadFromString(String settings) {
+        return new TextClassificationConstants(settings);
+    }
+
+    public boolean isLocalTextClassifierEnabled() {
+        return mLocalTextClassifierEnabled;
+    }
+
+    public boolean isSystemTextClassifierEnabled() {
+        return mSystemTextClassifierEnabled;
+    }
+
+    public boolean isModelDarkLaunchEnabled() {
+        return mModelDarkLaunchEnabled;
+    }
+
+    public boolean isSmartSelectionEnabled() {
+        return mSmartSelectionEnabled;
+    }
+
+    public boolean isSmartTextShareEnabled() {
+        return mSmartTextShareEnabled;
+    }
+
+    public boolean isSmartLinkifyEnabled() {
+        return mSmartLinkifyEnabled;
+    }
+
+    public boolean isSmartSelectionAnimationEnabled() {
+        return mSmartSelectionAnimationEnabled;
+    }
+
+    public int getSuggestSelectionMaxRangeLength() {
+        return mSuggestSelectionMaxRangeLength;
+    }
+
+    public int getClassifyTextMaxRangeLength() {
+        return mClassifyTextMaxRangeLength;
+    }
+
+    public int getGenerateLinksMaxTextLength() {
+        return mGenerateLinksMaxTextLength;
+    }
+
+    public int getGenerateLinksLogSampleRate() {
+        return mGenerateLinksLogSampleRate;
+    }
+
+    public List<String> getEntityListDefault() {
+        return mEntityListDefault;
+    }
+
+    public List<String> getEntityListNotEditable() {
+        return mEntityListNotEditable;
+    }
+
+    public List<String> getEntityListEditable() {
+        return mEntityListEditable;
+    }
+
+    private static List<String> parseEntityList(String listStr) {
+        return Collections.unmodifiableList(Arrays.asList(listStr.split(ENTITY_LIST_DELIMITER)));
+    }
+}
diff --git a/android/view/textclassifier/TextClassificationContext.java b/android/view/textclassifier/TextClassificationContext.java
new file mode 100644
index 0000000..a15411f
--- /dev/null
+++ b/android/view/textclassifier/TextClassificationContext.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.textclassifier.TextClassifier.WidgetType;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+
+/**
+ * A representation of the context in which text classification would be performed.
+ * @see TextClassificationManager#createTextClassificationSession(TextClassificationContext)
+ */
+public final class TextClassificationContext implements Parcelable {
+
+    private final String mPackageName;
+    private final String mWidgetType;
+    @Nullable private final String mWidgetVersion;
+
+    private TextClassificationContext(
+            String packageName,
+            String widgetType,
+            String widgetVersion) {
+        mPackageName = Preconditions.checkNotNull(packageName);
+        mWidgetType = Preconditions.checkNotNull(widgetType);
+        mWidgetVersion = widgetVersion;
+    }
+
+    /**
+     * Returns the package name for the calling package.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the widget type for this classification context.
+     */
+    @NonNull
+    @WidgetType
+    public String getWidgetType() {
+        return mWidgetType;
+    }
+
+    /**
+     * Returns a custom version string for the widget type.
+     *
+     * @see #getWidgetType()
+     */
+    @Nullable
+    public String getWidgetVersion() {
+        return mWidgetVersion;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US, "TextClassificationContext{"
+                + "packageName=%s, widgetType=%s, widgetVersion=%s}",
+                mPackageName, mWidgetType, mWidgetVersion);
+    }
+
+    /**
+     * A builder for building a TextClassification context.
+     */
+    public static final class Builder {
+
+        private final String mPackageName;
+        private final String mWidgetType;
+
+        @Nullable private String mWidgetVersion;
+
+        /**
+         * Initializes a new builder for text classification context objects.
+         *
+         * @param packageName the name of the calling package
+         * @param widgetType the type of widget e.g. {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}
+         *
+         * @return this builder
+         */
+        public Builder(@NonNull String packageName, @NonNull @WidgetType String widgetType) {
+            mPackageName = Preconditions.checkNotNull(packageName);
+            mWidgetType = Preconditions.checkNotNull(widgetType);
+        }
+
+        /**
+         * Sets an optional custom version string for the widget type.
+         *
+         * @return this builder
+         */
+        public Builder setWidgetVersion(@Nullable String widgetVersion) {
+            mWidgetVersion = widgetVersion;
+            return this;
+        }
+
+        /**
+         * Builds the text classification context object.
+         *
+         * @return the built TextClassificationContext object
+         */
+        @NonNull
+        public TextClassificationContext build() {
+            return new TextClassificationContext(mPackageName, mWidgetType, mWidgetVersion);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mPackageName);
+        parcel.writeString(mWidgetType);
+        parcel.writeString(mWidgetVersion);
+    }
+
+    private TextClassificationContext(Parcel in) {
+        mPackageName = in.readString();
+        mWidgetType = in.readString();
+        mWidgetVersion = in.readString();
+    }
+
+    public static final Parcelable.Creator<TextClassificationContext> CREATOR =
+            new Parcelable.Creator<TextClassificationContext>() {
+                @Override
+                public TextClassificationContext createFromParcel(Parcel parcel) {
+                    return new TextClassificationContext(parcel);
+                }
+
+                @Override
+                public TextClassificationContext[] newArray(int size) {
+                    return new TextClassificationContext[size];
+                }
+            };
+}
diff --git a/android/view/textclassifier/TextClassificationManager.java b/android/view/textclassifier/TextClassificationManager.java
index d7b0776..262d9b8 100644
--- a/android/view/textclassifier/TextClassificationManager.java
+++ b/android/view/textclassifier/TextClassificationManager.java
@@ -16,10 +16,16 @@
 
 package android.view.textclassifier;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.service.textclassifier.TextClassifierService;
+import android.view.textclassifier.TextClassifier.TextClassifierType;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
 /**
@@ -28,23 +34,49 @@
 @SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
 public final class TextClassificationManager {
 
-    private final Object mTextClassifierLock = new Object();
+    private static final String LOG_TAG = "TextClassificationManager";
+
+    private final Object mLock = new Object();
+    private final TextClassificationSessionFactory mDefaultSessionFactory =
+            classificationContext -> new TextClassificationSession(
+                    classificationContext, getTextClassifier());
 
     private final Context mContext;
+    private final TextClassificationConstants mSettings;
+
+    @GuardedBy("mLock")
     private TextClassifier mTextClassifier;
+    @GuardedBy("mLock")
+    private TextClassifier mLocalTextClassifier;
+    @GuardedBy("mLock")
+    private TextClassifier mSystemTextClassifier;
+    @GuardedBy("mLock")
+    private TextClassificationSessionFactory mSessionFactory;
 
     /** @hide */
     public TextClassificationManager(Context context) {
         mContext = Preconditions.checkNotNull(context);
+        mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
+                context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+        mSessionFactory = mDefaultSessionFactory;
     }
 
     /**
-     * Returns the text classifier.
+     * Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}.
+     * If this is null, this method returns a default text classifier (i.e. either the system text
+     * classifier if one exists, or a local text classifier running in this app.)
+     *
+     * @see #setTextClassifier(TextClassifier)
      */
+    @NonNull
     public TextClassifier getTextClassifier() {
-        synchronized (mTextClassifierLock) {
+        synchronized (mLock) {
             if (mTextClassifier == null) {
-                mTextClassifier = new TextClassifierImpl(mContext);
+                if (isSystemTextClassifierEnabled()) {
+                    mTextClassifier = getSystemTextClassifier();
+                } else {
+                    mTextClassifier = getLocalTextClassifier();
+                }
             }
             return mTextClassifier;
         }
@@ -56,8 +88,134 @@
      * Set to {@link TextClassifier#NO_OP} to disable text classifier features.
      */
     public void setTextClassifier(@Nullable TextClassifier textClassifier) {
-        synchronized (mTextClassifierLock) {
+        synchronized (mLock) {
             mTextClassifier = textClassifier;
         }
     }
+
+    /**
+     * Returns a specific type of text classifier.
+     * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
+     *
+     * @see TextClassifier#LOCAL
+     * @see TextClassifier#SYSTEM
+     * @hide
+     */
+    public TextClassifier getTextClassifier(@TextClassifierType int type) {
+        switch (type) {
+            case TextClassifier.LOCAL:
+                return getLocalTextClassifier();
+            default:
+                return getSystemTextClassifier();
+        }
+    }
+
+    /** @hide */
+    public TextClassificationConstants getSettings() {
+        return mSettings;
+    }
+
+    /**
+     * Call this method to start a text classification session with the given context.
+     * A session is created with a context helping the classifier better understand
+     * what the user needs and consists of queries and feedback events. The queries
+     * are directly related to providing useful functionality to the user and the events
+     * are a feedback loop back to the classifier helping it learn and better serve
+     * future queries.
+     *
+     * <p> All interactions with the returned classifier are considered part of a single
+     * session and are logically grouped. For example, when a text widget is focused
+     * all user interactions around text editing (selection, editing, etc) can be
+     * grouped together to allow the classifier get better.
+     *
+     * @param classificationContext The context in which classification would occur
+     *
+     * @return An instance to perform classification in the given context
+     */
+    @NonNull
+    public TextClassifier createTextClassificationSession(
+            @NonNull TextClassificationContext classificationContext) {
+        Preconditions.checkNotNull(classificationContext);
+        final TextClassifier textClassifier =
+                mSessionFactory.createTextClassificationSession(classificationContext);
+        Preconditions.checkNotNull(textClassifier, "Session Factory should never return null");
+        return textClassifier;
+    }
+
+    /**
+     * @see #createTextClassificationSession(TextClassificationContext, TextClassifier)
+     * @hide
+     */
+    public TextClassifier createTextClassificationSession(
+            TextClassificationContext classificationContext, TextClassifier textClassifier) {
+        Preconditions.checkNotNull(classificationContext);
+        Preconditions.checkNotNull(textClassifier);
+        return new TextClassificationSession(classificationContext, textClassifier);
+    }
+
+    /**
+     * Sets a TextClassificationSessionFactory to be used to create session-aware TextClassifiers.
+     *
+     * @param factory the textClassification session factory. If this is null, the default factory
+     *      will be used.
+     */
+    public void setTextClassificationSessionFactory(
+            @Nullable TextClassificationSessionFactory factory) {
+        synchronized (mLock) {
+            if (factory != null) {
+                mSessionFactory = factory;
+            } else {
+                mSessionFactory = mDefaultSessionFactory;
+            }
+        }
+    }
+
+    private TextClassifier getSystemTextClassifier() {
+        synchronized (mLock) {
+            if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
+                try {
+                    mSystemTextClassifier = new SystemTextClassifier(mContext, mSettings);
+                    Log.d(LOG_TAG, "Initialized SystemTextClassifier");
+                } catch (ServiceManager.ServiceNotFoundException e) {
+                    Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
+                }
+            }
+        }
+        if (mSystemTextClassifier != null) {
+            return mSystemTextClassifier;
+        }
+        return TextClassifier.NO_OP;
+    }
+
+    private TextClassifier getLocalTextClassifier() {
+        synchronized (mLock) {
+            if (mLocalTextClassifier == null) {
+                if (mSettings.isLocalTextClassifierEnabled()) {
+                    mLocalTextClassifier = new TextClassifierImpl(mContext, mSettings);
+                } else {
+                    Log.d(LOG_TAG, "Local TextClassifier disabled");
+                    mLocalTextClassifier = TextClassifierImpl.NO_OP;
+                }
+            }
+            return mLocalTextClassifier;
+        }
+    }
+
+    private boolean isSystemTextClassifierEnabled() {
+        return mSettings.isSystemTextClassifierEnabled()
+                && TextClassifierService.getServiceComponentName(mContext) != null;
+    }
+
+    /** @hide */
+    public static TextClassificationConstants getSettings(Context context) {
+        Preconditions.checkNotNull(context);
+        final TextClassificationManager tcm =
+                context.getSystemService(TextClassificationManager.class);
+        if (tcm != null) {
+            return tcm.mSettings;
+        } else {
+            return TextClassificationConstants.loadFromString(Settings.Global.getString(
+                    context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+        }
+    }
 }
diff --git a/android/view/textclassifier/TextClassificationSession.java b/android/view/textclassifier/TextClassificationSession.java
new file mode 100644
index 0000000..e8e300a
--- /dev/null
+++ b/android/view/textclassifier/TextClassificationSession.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.WorkerThread;
+import android.view.textclassifier.DefaultLogger.SignatureParser;
+import android.view.textclassifier.SelectionEvent.InvocationMethod;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Session-aware TextClassifier.
+ */
+@WorkerThread
+final class TextClassificationSession implements TextClassifier {
+
+    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
+    private static final String LOG_TAG = "TextClassificationSession";
+
+    private final TextClassifier mDelegate;
+    private final SelectionEventHelper mEventHelper;
+    private final TextClassificationSessionId mSessionId;
+    private final TextClassificationContext mClassificationContext;
+
+    private boolean mDestroyed;
+
+    TextClassificationSession(TextClassificationContext context, TextClassifier delegate) {
+        mClassificationContext = Preconditions.checkNotNull(context);
+        mDelegate = Preconditions.checkNotNull(delegate);
+        mSessionId = new TextClassificationSessionId();
+        mEventHelper = new SelectionEventHelper(mSessionId, mClassificationContext);
+        initializeRemoteSession();
+    }
+
+    @Override
+    public TextSelection suggestSelection(TextSelection.Request request) {
+        checkDestroyed();
+        return mDelegate.suggestSelection(request);
+    }
+
+    private void initializeRemoteSession() {
+        if (mDelegate instanceof SystemTextClassifier) {
+            ((SystemTextClassifier) mDelegate).initializeRemoteSession(
+                    mClassificationContext, mSessionId);
+        }
+    }
+
+    @Override
+    public TextClassification classifyText(TextClassification.Request request) {
+        checkDestroyed();
+        return mDelegate.classifyText(request);
+    }
+
+    @Override
+    public TextLinks generateLinks(TextLinks.Request request) {
+        checkDestroyed();
+        return mDelegate.generateLinks(request);
+    }
+
+    @Override
+    public void onSelectionEvent(SelectionEvent event) {
+        checkDestroyed();
+        Preconditions.checkNotNull(event);
+        if (mEventHelper.sanitizeEvent(event)) {
+            mDelegate.onSelectionEvent(event);
+        }
+    }
+
+    @Override
+    public void destroy() {
+        mEventHelper.endSession();
+        mDelegate.destroy();
+        mDestroyed = true;
+    }
+
+    @Override
+    public boolean isDestroyed() {
+        return mDestroyed;
+    }
+
+    /**
+     * @throws IllegalStateException if this TextClassification session has been destroyed.
+     * @see #isDestroyed()
+     * @see #destroy()
+     */
+    private void checkDestroyed() {
+        if (mDestroyed) {
+            throw new IllegalStateException("This TextClassification session has been destroyed");
+        }
+    }
+
+    /**
+     * Helper class for updating SelectionEvent fields.
+     */
+    private static final class SelectionEventHelper {
+
+        private final TextClassificationSessionId mSessionId;
+        private final TextClassificationContext mContext;
+
+        @InvocationMethod
+        private int mInvocationMethod = SelectionEvent.INVOCATION_UNKNOWN;
+        private SelectionEvent mPrevEvent;
+        private SelectionEvent mSmartEvent;
+        private SelectionEvent mStartEvent;
+
+        SelectionEventHelper(
+                TextClassificationSessionId sessionId, TextClassificationContext context) {
+            mSessionId = Preconditions.checkNotNull(sessionId);
+            mContext = Preconditions.checkNotNull(context);
+        }
+
+        /**
+         * Updates the necessary fields in the event for the current session.
+         *
+         * @return true if the event should be reported. false if the event should be ignored
+         */
+        boolean sanitizeEvent(SelectionEvent event) {
+            updateInvocationMethod(event);
+            modifyAutoSelectionEventType(event);
+
+            if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
+                    && mStartEvent == null) {
+                if (DEBUG_LOG_ENABLED) {
+                    Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
+                }
+                return false;
+            }
+
+            final long now = System.currentTimeMillis();
+            switch (event.getEventType()) {
+                case SelectionEvent.EVENT_SELECTION_STARTED:
+                    Preconditions.checkArgument(
+                            event.getAbsoluteEnd() == event.getAbsoluteStart() + 1);
+                    event.setSessionId(mSessionId);
+                    mStartEvent = event;
+                    break;
+                case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
+                case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                    mSmartEvent = event;
+                    break;
+                case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
+                case SelectionEvent.EVENT_AUTO_SELECTION:
+                    if (mPrevEvent != null
+                            && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
+                            && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
+                        // Selection did not change. Ignore event.
+                        return false;
+                    }
+                    break;
+                default:
+                    // do nothing.
+            }
+
+            event.setEventTime(now);
+            if (mStartEvent != null) {
+                event.setSessionId(mStartEvent.getSessionId())
+                        .setDurationSinceSessionStart(now - mStartEvent.getEventTime())
+                        .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                        .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+            }
+            if (mSmartEvent != null) {
+                event.setResultId(mSmartEvent.getResultId())
+                        .setSmartStart(
+                                mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                        .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+            }
+            if (mPrevEvent != null) {
+                event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime())
+                        .setEventIndex(mPrevEvent.getEventIndex() + 1);
+            }
+            mPrevEvent = event;
+            return true;
+        }
+
+        void endSession() {
+            mPrevEvent = null;
+            mSmartEvent = null;
+            mStartEvent = null;
+        }
+
+        private void updateInvocationMethod(SelectionEvent event) {
+            event.setTextClassificationSessionContext(mContext);
+            if (event.getInvocationMethod() == SelectionEvent.INVOCATION_UNKNOWN) {
+                event.setInvocationMethod(mInvocationMethod);
+            } else {
+                mInvocationMethod = event.getInvocationMethod();
+            }
+        }
+
+        private void modifyAutoSelectionEventType(SelectionEvent event) {
+            switch (event.getEventType()) {
+                case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
+                case SelectionEvent.EVENT_SMART_SELECTION_MULTI:  // fall through
+                case SelectionEvent.EVENT_AUTO_SELECTION:
+                    if (isPlatformLocalTextClassifierSmartSelection(event.getResultId())) {
+                        if (event.getAbsoluteEnd() - event.getAbsoluteStart() > 1) {
+                            event.setEventType(SelectionEvent.EVENT_SMART_SELECTION_MULTI);
+                        } else {
+                            event.setEventType(SelectionEvent.EVENT_SMART_SELECTION_SINGLE);
+                        }
+                    } else {
+                        event.setEventType(SelectionEvent.EVENT_AUTO_SELECTION);
+                    }
+                    return;
+                default:
+                    return;
+            }
+        }
+
+        private static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
+            return DefaultLogger.CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
+        }
+    }
+}
diff --git a/android/view/textclassifier/TextClassificationSessionFactory.java b/android/view/textclassifier/TextClassificationSessionFactory.java
new file mode 100644
index 0000000..c0914b6
--- /dev/null
+++ b/android/view/textclassifier/TextClassificationSessionFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+
+/**
+ * An interface for creating a session-aware TextClassifier.
+ *
+ * @see TextClassificationManager#createTextClassificationSession(TextClassificationContext)
+ */
+public interface TextClassificationSessionFactory {
+
+    /**
+     * Creates and returns a session-aware TextClassifier.
+     *
+     * @param classificationContext the classification context
+     */
+    @NonNull
+    TextClassifier createTextClassificationSession(
+            @NonNull TextClassificationContext classificationContext);
+}
diff --git a/android/view/textclassifier/TextClassificationSessionId.java b/android/view/textclassifier/TextClassificationSessionId.java
new file mode 100644
index 0000000..1378bd9
--- /dev/null
+++ b/android/view/textclassifier/TextClassificationSessionId.java
@@ -0,0 +1,131 @@
+/*
+ * 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.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.UUID;
+
+/**
+ * This class represents the id of a text classification session.
+ */
+public final class TextClassificationSessionId implements Parcelable {
+    private final @NonNull String mValue;
+
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public TextClassificationSessionId() {
+        this(UUID.randomUUID().toString());
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param value The internal value.
+     *
+     * @hide
+     */
+    public TextClassificationSessionId(@NonNull String value) {
+        mValue = value;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + mValue.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TextClassificationSessionId other = (TextClassificationSessionId) obj;
+        if (!mValue.equals(other.mValue)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US, "TextClassificationSessionId {%s}", mValue);
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mValue);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flattens this id to a string.
+     *
+     * @return The flattened id.
+     *
+     * @hide
+     */
+    public @NonNull String flattenToString() {
+        return mValue;
+    }
+
+    /**
+     * Unflattens a print job id from a string.
+     *
+     * @param string The string.
+     * @return The unflattened id, or null if the string is malformed.
+     *
+     * @hide
+     */
+    public static @NonNull TextClassificationSessionId unflattenFromString(@NonNull String string) {
+        return new TextClassificationSessionId(string);
+    }
+
+    public static final Parcelable.Creator<TextClassificationSessionId> CREATOR =
+            new Parcelable.Creator<TextClassificationSessionId>() {
+                @Override
+                public TextClassificationSessionId createFromParcel(Parcel parcel) {
+                    return new TextClassificationSessionId(
+                            Preconditions.checkNotNull(parcel.readString()));
+                }
+
+                @Override
+                public TextClassificationSessionId[] newArray(int size) {
+                    return new TextClassificationSessionId[size];
+                }
+            };
+}
diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java
index e9715c5..54261be 100644
--- a/android/view/textclassifier/TextClassifier.java
+++ b/android/view/textclassifier/TextClassifier.java
@@ -23,8 +23,15 @@
 import android.annotation.StringDef;
 import android.annotation.WorkerThread;
 import android.os.LocaleList;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.URLSpan;
+import android.text.util.Linkify;
+import android.text.util.Linkify.LinkifyMask;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.internal.util.Preconditions;
@@ -35,24 +42,49 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Interface for providing text classification related features.
  *
- * <p>Unless otherwise stated, methods of this interface are blocking operations.
- * Avoid calling them on the UI thread.
+ * <p><strong>NOTE: </strong>Unless otherwise stated, methods of this interface are blocking
+ * operations. Call on a worker thread.
  */
 public interface TextClassifier {
 
     /** @hide */
     String DEFAULT_LOG_TAG = "androidtc";
 
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {LOCAL, SYSTEM})
+    @interface TextClassifierType {}  // TODO: Expose as system APIs.
+    /** Specifies a TextClassifier that runs locally in the app's process. @hide */
+    int LOCAL = 0;
+    /** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
+    int SYSTEM = 1;
+
+    /** The TextClassifier failed to run. */
     String TYPE_UNKNOWN = "";
+    /** The classifier ran, but didn't recognize a known entity. */
     String TYPE_OTHER = "other";
+    /** E-mail address (e.g. "[email protected]"). */
     String TYPE_EMAIL = "email";
+    /** Phone number (e.g. "555-123 456"). */
     String TYPE_PHONE = "phone";
+    /** Physical address. */
     String TYPE_ADDRESS = "address";
+    /** Web URL. */
     String TYPE_URL = "url";
+    /** Time reference that is no more specific than a date. May be absolute such as "01/01/2000" or
+     * relative like "tomorrow". **/
+    String TYPE_DATE = "date";
+    /** Time reference that includes a specific time. May be absolute such as "01/01/2000 5:30pm" or
+     * relative like "tomorrow at 5:30pm". **/
+    String TYPE_DATE_TIME = "datetime";
+    /** Flight number in IATA format. */
+    String TYPE_FLIGHT_NUMBER = "flight";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -63,22 +95,53 @@
             TYPE_PHONE,
             TYPE_ADDRESS,
             TYPE_URL,
+            TYPE_DATE,
+            TYPE_DATE_TIME,
+            TYPE_FLIGHT_NUMBER,
     })
     @interface EntityType {}
 
-    /** Designates that the TextClassifier should identify all entity types it can. **/
-    int ENTITY_PRESET_ALL = 0;
-    /** Designates that the TextClassifier should identify no entities. **/
-    int ENTITY_PRESET_NONE = 1;
-    /** Designates that the TextClassifier should identify a base set of entities determined by the
-     * TextClassifier. **/
-    int ENTITY_PRESET_BASE = 2;
+    /** Designates that the text in question is editable. **/
+    String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
+    /** Designates that the text in question is not editable. **/
+    String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "ENTITY_CONFIG_" },
-            value = {ENTITY_PRESET_ALL, ENTITY_PRESET_NONE, ENTITY_PRESET_BASE})
-    @interface EntityPreset {}
+    @StringDef(prefix = { "HINT_" }, value = {HINT_TEXT_IS_EDITABLE, HINT_TEXT_IS_NOT_EDITABLE})
+    @interface Hints {}
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDITTEXT,
+            WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW, WIDGET_TYPE_CUSTOM_EDITTEXT,
+            WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW, WIDGET_TYPE_UNKNOWN})
+    @interface WidgetType {}
+
+    /** The widget involved in the text classification session is a standard
+     * {@link android.widget.TextView}. */
+    String WIDGET_TYPE_TEXTVIEW = "textview";
+    /** The widget involved in the text classification session is a standard
+     * {@link android.widget.EditText}. */
+    String WIDGET_TYPE_EDITTEXT = "edittext";
+    /** The widget involved in the text classification session is a standard non-selectable
+     * {@link android.widget.TextView}. */
+    String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
+    /** The widget involved in the text classification session is a standard
+     * {@link android.webkit.WebView}. */
+    String WIDGET_TYPE_WEBVIEW = "webview";
+    /** The widget involved in the text classification session is a standard editable
+     * {@link android.webkit.WebView}. */
+    String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
+    /** The widget involved in the text classification session is a custom text widget. */
+    String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
+    /** The widget involved in the text classification session is a custom editable text widget. */
+    String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
+    /** The widget involved in the text classification session is a custom non-selectable text
+     * widget. */
+    String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
+    /** The widget involved in the text classification session is of an unknown/unspecified type. */
+    String WIDGET_TYPE_UNKNOWN = "unknown";
 
     /**
      * No-op TextClassifier.
@@ -90,63 +153,46 @@
      * Returns suggested text selection start and end indices, recognized entity types, and their
      * associated confidence scores. The entity types are ordered from highest to lowest scoring.
      *
-     * @param text text providing context for the selected text (which is specified
-     *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
-     * @param selectionStartIndex start index of the selected part of text
-     * @param selectionEndIndex end index of the selected part of text
-     * @param options optional input parameters
+     * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
-     *      selectionEndIndex is greater than text.length() or not greater than selectionStartIndex
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
-     * @see #suggestSelection(CharSequence, int, int)
+     * @param request the text selection request
      */
     @WorkerThread
     @NonNull
-    default TextSelection suggestSelection(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int selectionStartIndex,
-            @IntRange(from = 0) int selectionEndIndex,
-            @Nullable TextSelection.Options options) {
-        Utils.validateInput(text, selectionStartIndex, selectionEndIndex);
-        return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
+    default TextSelection suggestSelection(@NonNull TextSelection.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        return new TextSelection.Builder(request.getStartIndex(), request.getEndIndex()).build();
     }
 
     /**
      * Returns suggested text selection start and end indices, recognized entity types, and their
      * associated confidence scores. The entity types are ordered from highest to lowest scoring.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
-     * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
-     * calls this method, a stack overflow error will happen.
+     * {@link #suggestSelection(TextSelection.Request)}. If that method calls this method,
+     * a stack overflow error will happen.
      *
      * @param text text providing context for the selected text (which is specified
      *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
      * @param selectionStartIndex start index of the selected part of text
      * @param selectionEndIndex end index of the selected part of text
+     * @param defaultLocales ordered list of locale preferences that may be used to
+     *      disambiguate the provided text. If no locale preferences exist, set this to null
+     *      or an empty locale list.
      *
      * @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
      *      selectionEndIndex is greater than text.length() or not greater than selectionStartIndex
      *
-     * @see #suggestSelection(CharSequence, int, int, TextSelection.Options)
-     */
-    @WorkerThread
-    @NonNull
-    default TextSelection suggestSelection(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int selectionStartIndex,
-            @IntRange(from = 0) int selectionEndIndex) {
-        return suggestSelection(text, selectionStartIndex, selectionEndIndex,
-                (TextSelection.Options) null);
-    }
-
-    /**
-     * See {@link #suggestSelection(CharSequence, int, int)} or
-     * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}.
-     *
-     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
-     * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
-     * calls this method, a stack overflow error will happen.
+     * @see #suggestSelection(TextSelection.Request)
      */
     @WorkerThread
     @NonNull
@@ -155,35 +201,29 @@
             @IntRange(from = 0) int selectionStartIndex,
             @IntRange(from = 0) int selectionEndIndex,
             @Nullable LocaleList defaultLocales) {
-        final TextSelection.Options options = (defaultLocales != null)
-                ? new TextSelection.Options().setDefaultLocales(defaultLocales)
-                : null;
-        return suggestSelection(text, selectionStartIndex, selectionEndIndex, options);
+        final TextSelection.Request request = new TextSelection.Request.Builder(
+                text, selectionStartIndex, selectionEndIndex)
+                .setDefaultLocales(defaultLocales)
+                .build();
+        return suggestSelection(request);
     }
 
     /**
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
      *
-     * @param text text providing context for the text to classify (which is specified
-     *      by the sub sequence starting at startIndex and ending at endIndex)
-     * @param startIndex start index of the text to classify
-     * @param endIndex end index of the text to classify
-     * @param options optional input parameters
+     * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * @throws IllegalArgumentException if text is null; startIndex is negative;
-     *      endIndex is greater than text.length() or not greater than startIndex
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
-     * @see #classifyText(CharSequence, int, int)
+     * @param request the text classification request
      */
     @WorkerThread
     @NonNull
-    default TextClassification classifyText(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int startIndex,
-            @IntRange(from = 0) int endIndex,
-            @Nullable TextClassification.Options options) {
-        Utils.validateInput(text, startIndex, endIndex);
+    default TextClassification classifyText(@NonNull TextClassification.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
         return TextClassification.EMPTY;
     }
 
@@ -191,36 +231,27 @@
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
      *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
-     * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
-     * calls this method, a stack overflow error will happen.
+     * {@link #classifyText(TextClassification.Request)}. If that method calls this method,
+     * a stack overflow error will happen.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param text text providing context for the text to classify (which is specified
      *      by the sub sequence starting at startIndex and ending at endIndex)
      * @param startIndex start index of the text to classify
      * @param endIndex end index of the text to classify
+     * @param defaultLocales ordered list of locale preferences that may be used to
+     *      disambiguate the provided text. If no locale preferences exist, set this to null
+     *      or an empty locale list.
      *
      * @throws IllegalArgumentException if text is null; startIndex is negative;
      *      endIndex is greater than text.length() or not greater than startIndex
      *
-     * @see #classifyText(CharSequence, int, int, TextClassification.Options)
-     */
-    @WorkerThread
-    @NonNull
-    default TextClassification classifyText(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int startIndex,
-            @IntRange(from = 0) int endIndex) {
-        return classifyText(text, startIndex, endIndex, (TextClassification.Options) null);
-    }
-
-    /**
-     * See {@link #classifyText(CharSequence, int, int, TextClassification.Options)} or
-     * {@link #classifyText(CharSequence, int, int)}.
-     *
-     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
-     * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
-     * calls this method, a stack overflow error will happen.
+     * @see #classifyText(TextClassification.Request)
      */
     @WorkerThread
     @NonNull
@@ -229,77 +260,88 @@
             @IntRange(from = 0) int startIndex,
             @IntRange(from = 0) int endIndex,
             @Nullable LocaleList defaultLocales) {
-        final TextClassification.Options options = (defaultLocales != null)
-                ? new TextClassification.Options().setDefaultLocales(defaultLocales)
-                : null;
-        return classifyText(text, startIndex, endIndex, options);
+        final TextClassification.Request request = new TextClassification.Request.Builder(
+                text, startIndex, endIndex)
+                .setDefaultLocales(defaultLocales)
+                .build();
+        return classifyText(request);
     }
 
     /**
-     * Returns a {@link TextLinks} that may be applied to the text to annotate it with links
-     * information.
+     * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
+     * links information.
      *
-     * If no options are supplied, default values will be used, determined by the TextClassifier.
+     * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * @param text the text to generate annotations for
-     * @param options configuration for link generation
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
-     * @throws IllegalArgumentException if text is null
+     * @param request the text links request
      *
-     * @see #generateLinks(CharSequence)
+     * @see #getMaxGenerateLinksTextLength()
      */
     @WorkerThread
-    default TextLinks generateLinks(
-            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        Utils.validateInput(text);
-        return new TextLinks.Builder(text.toString()).build();
+    @NonNull
+    default TextLinks generateLinks(@NonNull TextLinks.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        return new TextLinks.Builder(request.getText().toString()).build();
     }
 
     /**
-     * Returns a {@link TextLinks} that may be applied to the text to annotate it with links
-     * information.
+     * Returns the maximal length of text that can be processed by generateLinks.
      *
-     * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
-     * {@link #generateLinks(CharSequence, TextLinks.Options)}. If that method calls this method,
-     * a stack overflow error will happen.
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
-     * @param text the text to generate annotations for
-     *
-     * @throws IllegalArgumentException if text is null
-     *
-     * @see #generateLinks(CharSequence, TextLinks.Options)
+     * @see #generateLinks(TextLinks.Request)
      */
     @WorkerThread
-    default TextLinks generateLinks(@NonNull CharSequence text) {
-        return generateLinks(text, null);
+    default int getMaxGenerateLinksTextLength() {
+        return Integer.MAX_VALUE;
     }
 
     /**
-     * Returns a {@link Collection} of the entity types in the specified preset.
+     * Returns a helper for logging TextClassifier related events.
      *
-     * @see #ENTITY_PRESET_ALL
-     * @see #ENTITY_PRESET_NONE
-     */
-    default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) {
-        return Collections.EMPTY_LIST;
-    }
-
-    /**
-     * Logs a TextClassifier event.
-     *
-     * @param source the text classifier used to generate this event
-     * @param event the text classifier related event
+     * @param config logger configuration
      * @hide
      */
     @WorkerThread
-    default void logEvent(String source, String event) {}
+    default Logger getLogger(@NonNull Logger.Config config) {
+        Preconditions.checkNotNull(config);
+        return Logger.DISABLED;
+    }
 
     /**
-     * Returns this TextClassifier's settings.
-     * @hide
+     * Reports a selection event.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      */
-    default TextClassifierConstants getSettings() {
-        return TextClassifierConstants.DEFAULT;
+    default void onSelectionEvent(@NonNull SelectionEvent event) {}
+
+    /**
+     * Destroys this TextClassifier.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
+     * <p>Subsequent calls to this method are no-ops.
+     */
+    default void destroy() {}
+
+    /**
+     * Returns whether or not this TextClassifier has been destroyed.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
+     * with the classifier and an attempt to do so would throw an {@link IllegalStateException}.
+     * However, this method should never throw an {@link IllegalStateException}.
+     *
+     * @see #destroy()
+     */
+    default boolean isDestroyed() {
+        return false;
     }
 
     /**
@@ -308,54 +350,93 @@
      * Configs are initially based on a predefined preset, and can be modified from there.
      */
     final class EntityConfig implements Parcelable {
-        private final @TextClassifier.EntityPreset int mEntityPreset;
+        private final Collection<String> mHints;
         private final Collection<String> mExcludedEntityTypes;
         private final Collection<String> mIncludedEntityTypes;
+        private final boolean mUseHints;
 
-        public EntityConfig(@TextClassifier.EntityPreset int mEntityPreset) {
-            this.mEntityPreset = mEntityPreset;
-            mExcludedEntityTypes = new ArraySet<>();
-            mIncludedEntityTypes = new ArraySet<>();
+        private EntityConfig(boolean useHints, Collection<String> hints,
+                Collection<String> includedEntityTypes, Collection<String> excludedEntityTypes) {
+            mHints = hints == null
+                    ? Collections.EMPTY_LIST
+                    : Collections.unmodifiableCollection(new ArraySet<>(hints));
+            mExcludedEntityTypes = excludedEntityTypes == null
+                    ? Collections.EMPTY_LIST : new ArraySet<>(excludedEntityTypes);
+            mIncludedEntityTypes = includedEntityTypes == null
+                    ? Collections.EMPTY_LIST : new ArraySet<>(includedEntityTypes);
+            mUseHints = useHints;
         }
 
         /**
-         * Specifies an entity to include in addition to any specified by the enity preset.
+         * Creates an EntityConfig.
+         *
+         * @param hints Hints for the TextClassifier to determine what types of entities to find.
+         */
+        public static EntityConfig createWithHints(@Nullable Collection<String> hints) {
+            return new EntityConfig(/* useHints */ true, hints,
+                    /* includedEntityTypes */null, /* excludedEntityTypes */ null);
+        }
+
+        /**
+         * Creates an EntityConfig.
+         *
+         * @param hints Hints for the TextClassifier to determine what types of entities to find
+         * @param includedEntityTypes Entity types, e.g. {@link #TYPE_EMAIL}, to explicitly include
+         * @param excludedEntityTypes Entity types, e.g. {@link #TYPE_PHONE}, to explicitly exclude
+         *
          *
          * Note that if an entity has been excluded, the exclusion will take precedence.
          */
-        public EntityConfig includeEntities(String... entities) {
-            for (String entity : entities) {
-                mIncludedEntityTypes.add(entity);
-            }
-            return this;
+        public static EntityConfig create(@Nullable Collection<String> hints,
+                @Nullable Collection<String> includedEntityTypes,
+                @Nullable Collection<String> excludedEntityTypes) {
+            return new EntityConfig(/* useHints */ true, hints,
+                    includedEntityTypes, excludedEntityTypes);
         }
 
         /**
-         * Specifies an entity to be excluded.
+         * Creates an EntityConfig with an explicit entity list.
+         *
+         * @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find.
+         *
          */
-        public EntityConfig excludeEntities(String... entities) {
-            for (String entity : entities) {
-                mExcludedEntityTypes.add(entity);
-            }
-            return this;
+        public static EntityConfig createWithExplicitEntityList(
+                @Nullable Collection<String> entityTypes) {
+            return new EntityConfig(/* useHints */ false, /* hints */ null,
+                    /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null);
         }
 
         /**
-         * Returns an unmodifiable list of the final set of entities to find.
+         * Returns a list of the final set of entities to find.
+         *
+         * @param entities Entities we think should be found before factoring in includes/excludes
+         *
+         * This method is intended for use by TextClassifier implementations.
          */
-        public List<String> getEntities(TextClassifier textClassifier) {
-            ArrayList<String> entities = new ArrayList<>();
-            for (String entity : textClassifier.getEntitiesForPreset(mEntityPreset)) {
-                if (!mExcludedEntityTypes.contains(entity)) {
-                    entities.add(entity);
+        public List<String> resolveEntityListModifications(@NonNull Collection<String> entities) {
+            final ArrayList<String> finalList = new ArrayList<>();
+            if (mUseHints) {
+                for (String entity : entities) {
+                    if (!mExcludedEntityTypes.contains(entity)) {
+                        finalList.add(entity);
+                    }
                 }
             }
             for (String entity : mIncludedEntityTypes) {
-                if (!mExcludedEntityTypes.contains(entity) && !entities.contains(entity)) {
-                    entities.add(entity);
+                if (!mExcludedEntityTypes.contains(entity) && !finalList.contains(entity)) {
+                    finalList.add(entity);
                 }
             }
-            return Collections.unmodifiableList(entities);
+            return finalList;
+        }
+
+        /**
+         * Retrieves the list of hints.
+         *
+         * @return An unmodifiable collection of the hints.
+         */
+        public Collection<String> getHints() {
+            return mHints;
         }
 
         @Override
@@ -365,9 +446,10 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mEntityPreset);
+            dest.writeStringList(new ArrayList<>(mHints));
             dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
             dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
+            dest.writeInt(mUseHints ? 1 : 0);
         }
 
         public static final Parcelable.Creator<EntityConfig> CREATOR =
@@ -384,9 +466,10 @@
                 };
 
         private EntityConfig(Parcel in) {
-            mEntityPreset = in.readInt();
+            mHints = new ArraySet<>(in.createStringArrayList());
             mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
             mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
+            mUseHints = in.readInt() == 1;
         }
     }
 
@@ -407,19 +490,79 @@
          *      endIndex is greater than text.length() or is not greater than startIndex;
          *      options is null
          */
-        static void validateInput(
-                @NonNull CharSequence text, int startIndex, int endIndex) {
+        static void checkArgument(@NonNull CharSequence text, int startIndex, int endIndex) {
             Preconditions.checkArgument(text != null);
             Preconditions.checkArgument(startIndex >= 0);
             Preconditions.checkArgument(endIndex <= text.length());
             Preconditions.checkArgument(endIndex > startIndex);
         }
 
+        static void checkTextLength(CharSequence text, int maxLength) {
+            Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
+        }
+
         /**
-         * @throws IllegalArgumentException if text is null or options is null
+         * Generates links using legacy {@link Linkify}.
          */
-        static void validateInput(@NonNull CharSequence text) {
-            Preconditions.checkArgument(text != null);
+        public static TextLinks generateLegacyLinks(@NonNull TextLinks.Request request) {
+            final String string = request.getText().toString();
+            final TextLinks.Builder links = new TextLinks.Builder(string);
+
+            final List<String> entities = request.getEntityConfig()
+                    .resolveEntityListModifications(Collections.emptyList());
+            if (entities.contains(TextClassifier.TYPE_URL)) {
+                addLinks(links, string, TextClassifier.TYPE_URL);
+            }
+            if (entities.contains(TextClassifier.TYPE_PHONE)) {
+                addLinks(links, string, TextClassifier.TYPE_PHONE);
+            }
+            if (entities.contains(TextClassifier.TYPE_EMAIL)) {
+                addLinks(links, string, TextClassifier.TYPE_EMAIL);
+            }
+            // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
+            return links.build();
+        }
+
+        private static void addLinks(
+                TextLinks.Builder links, String string, @EntityType String entityType) {
+            final Spannable spannable = new SpannableString(string);
+            if (Linkify.addLinks(spannable, linkMask(entityType))) {
+                final URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
+                for (URLSpan urlSpan : spans) {
+                    links.addLink(
+                            spannable.getSpanStart(urlSpan),
+                            spannable.getSpanEnd(urlSpan),
+                            entityScores(entityType),
+                            urlSpan);
+                }
+            }
+        }
+
+        @LinkifyMask
+        private static int linkMask(@EntityType String entityType) {
+            switch (entityType) {
+                case TextClassifier.TYPE_URL:
+                    return Linkify.WEB_URLS;
+                case TextClassifier.TYPE_PHONE:
+                    return Linkify.PHONE_NUMBERS;
+                case TextClassifier.TYPE_EMAIL:
+                    return Linkify.EMAIL_ADDRESSES;
+                default:
+                    // NOTE: Do not support MAP_ADDRESSES. Legacy version does not work well.
+                    return 0;
+            }
+        }
+
+        private static Map<String, Float> entityScores(@EntityType String entityType) {
+            final Map<String, Float> scores = new ArrayMap<>();
+            scores.put(entityType, 1f);
+            return scores;
+        }
+
+        static void checkMainThread() {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                Log.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
+            }
         }
     }
 }
diff --git a/android/view/textclassifier/TextClassifierConstants.java b/android/view/textclassifier/TextClassifierConstants.java
deleted file mode 100644
index 00695b7..0000000
--- a/android/view/textclassifier/TextClassifierConstants.java
+++ /dev/null
@@ -1,102 +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.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.util.KeyValueListParser;
-import android.util.Slog;
-
-/**
- * TextClassifier specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * <pre>
- * smart_selection_dark_launch              (boolean)
- * smart_selection_enabled_for_edit_text    (boolean)
- * </pre>
- *
- * <p>
- * Type: string
- * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
- *
- * Example of setting the values for testing.
- * adb shell settings put global text_classifier_constants smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true
- * @hide
- */
-public final class TextClassifierConstants {
-
-    private static final String LOG_TAG = "TextClassifierConstants";
-
-    private static final String SMART_SELECTION_DARK_LAUNCH =
-            "smart_selection_dark_launch";
-    private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
-            "smart_selection_enabled_for_edit_text";
-    private static final String SMART_LINKIFY_ENABLED =
-            "smart_linkify_enabled";
-
-    private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
-    private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
-    private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
-
-    /** Default settings. */
-    static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
-
-    private final boolean mDarkLaunch;
-    private final boolean mSuggestSelectionEnabledForEditableText;
-    private final boolean mSmartLinkifyEnabled;
-
-    private TextClassifierConstants() {
-        mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
-        mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
-        mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
-    }
-
-    private TextClassifierConstants(@Nullable String settings) {
-        final KeyValueListParser parser = new KeyValueListParser(',');
-        try {
-            parser.setString(settings);
-        } catch (IllegalArgumentException e) {
-            // Failed to parse the settings string, log this and move on with defaults.
-            Slog.e(LOG_TAG, "Bad TextClassifier settings: " + settings);
-        }
-        mDarkLaunch = parser.getBoolean(
-                SMART_SELECTION_DARK_LAUNCH,
-                SMART_SELECTION_DARK_LAUNCH_DEFAULT);
-        mSuggestSelectionEnabledForEditableText = parser.getBoolean(
-                SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
-                SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
-        mSmartLinkifyEnabled = parser.getBoolean(
-                SMART_LINKIFY_ENABLED,
-                SMART_LINKIFY_ENABLED_DEFAULT);
-    }
-
-    static TextClassifierConstants loadFromString(String settings) {
-        return new TextClassifierConstants(settings);
-    }
-
-    public boolean isDarkLaunch() {
-        return mDarkLaunch;
-    }
-
-    public boolean isSuggestSelectionEnabledForEditableText() {
-        return mSuggestSelectionEnabledForEditableText;
-    }
-
-    public boolean isSmartLinkifyEnabled() {
-        return mSmartLinkifyEnabled;
-    }
-}
diff --git a/android/view/textclassifier/TextClassifierImpl.java b/android/view/textclassifier/TextClassifierImpl.java
index 7db0e76..7e3748a 100644
--- a/android/view/textclassifier/TextClassifierImpl.java
+++ b/android/view/textclassifier/TextClassifierImpl.java
@@ -16,30 +16,39 @@
 
 package android.view.textclassifier;
 
+import static java.time.temporal.ChronoUnit.MILLIS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.app.RemoteAction;
+import android.app.SearchManager;
 import android.content.ComponentName;
+import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
+import android.os.UserManager;
 import android.provider.Browser;
+import android.provider.CalendarContract;
 import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.text.util.Linkify;
-import android.util.Patterns;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.time.Instant;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -49,6 +58,8 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.StringJoiner;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -61,82 +72,86 @@
  *
  * @hide
  */
-final class TextClassifierImpl implements TextClassifier {
+public final class TextClassifierImpl implements TextClassifier {
 
     private static final String LOG_TAG = DEFAULT_LOG_TAG;
     private static final String MODEL_DIR = "/etc/textclassifier/";
-    private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model";
+    private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model";
     private static final String UPDATED_MODEL_FILE_PATH =
-            "/data/misc/textclassifier/textclassifier.smartselection.model";
-    private static final List<String> ENTITY_TYPES_ALL =
-            Collections.unmodifiableList(Arrays.asList(
-                    TextClassifier.TYPE_ADDRESS,
-                    TextClassifier.TYPE_EMAIL,
-                    TextClassifier.TYPE_PHONE,
-                    TextClassifier.TYPE_URL));
-    private static final List<String> ENTITY_TYPES_BASE =
-            Collections.unmodifiableList(Arrays.asList(
-                    TextClassifier.TYPE_ADDRESS,
-                    TextClassifier.TYPE_EMAIL,
-                    TextClassifier.TYPE_PHONE,
-                    TextClassifier.TYPE_URL));
+            "/data/misc/textclassifier/textclassifier.model";
 
     private final Context mContext;
+    private final TextClassifier mFallback;
+    private final GenerateLinksLogger mGenerateLinksLogger;
 
-    private final MetricsLogger mMetricsLogger = new MetricsLogger();
+    private final Object mLock = new Object();
+    @GuardedBy("mLock") // Do not access outside this lock.
+    private List<ModelFile> mAllModelFiles;
+    @GuardedBy("mLock") // Do not access outside this lock.
+    private ModelFile mModel;
+    @GuardedBy("mLock") // Do not access outside this lock.
+    private TextClassifierImplNative mNative;
 
-    private final Object mSmartSelectionLock = new Object();
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
-    private Map<Locale, String> mModelFilePaths;
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
-    private Locale mLocale;
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
-    private int mVersion;
-    @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
-    private SmartSelection mSmartSelection;
+    private final Object mLoggerLock = new Object();
+    @GuardedBy("mLoggerLock") // Do not access outside this lock.
+    private Logger.Config mLoggerConfig;
+    @GuardedBy("mLoggerLock") // Do not access outside this lock.
+    private Logger mLogger;
+    @GuardedBy("mLoggerLock") // Do not access outside this lock.
+    private Logger mLogger2;  // This is the new logger. Will replace mLogger.
 
-    private TextClassifierConstants mSettings;
+    private final TextClassificationConstants mSettings;
 
-    TextClassifierImpl(Context context) {
+    public TextClassifierImpl(Context context, TextClassificationConstants settings) {
         mContext = Preconditions.checkNotNull(context);
+        mFallback = TextClassifier.NO_OP;
+        mSettings = Preconditions.checkNotNull(settings);
+        mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
     }
 
+    /** @inheritDoc */
     @Override
-    public TextSelection suggestSelection(
-            @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
-            @NonNull TextSelection.Options options) {
-        Utils.validateInput(text, selectionStartIndex, selectionEndIndex);
+    @WorkerThread
+    public TextSelection suggestSelection(TextSelection.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
         try {
-            if (text.length() > 0) {
-                final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
-                final SmartSelection smartSelection = getSmartSelection(locales);
-                final String string = text.toString();
+            final int rangeLength = request.getEndIndex() - request.getStartIndex();
+            final String string = request.getText().toString();
+            if (string.length() > 0
+                    && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
+                final String localesString = concatenateLocales(request.getDefaultLocales());
+                final ZonedDateTime refTime = ZonedDateTime.now();
+                final TextClassifierImplNative nativeImpl = getNative(request.getDefaultLocales());
                 final int start;
                 final int end;
-                if (getSettings().isDarkLaunch() && !options.isDarkLaunchAllowed()) {
-                    start = selectionStartIndex;
-                    end = selectionEndIndex;
+                if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
+                    start = request.getStartIndex();
+                    end = request.getEndIndex();
                 } else {
-                    final int[] startEnd = smartSelection.suggest(
-                            string, selectionStartIndex, selectionEndIndex);
+                    final int[] startEnd = nativeImpl.suggestSelection(
+                            string, request.getStartIndex(), request.getEndIndex(),
+                            new TextClassifierImplNative.SelectionOptions(localesString));
                     start = startEnd[0];
                     end = startEnd[1];
                 }
-                if (start <= end
+                if (start < end
                         && start >= 0 && end <= string.length()
-                        && start <= selectionStartIndex && end >= selectionEndIndex) {
+                        && start <= request.getStartIndex() && end >= request.getEndIndex()) {
                     final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
-                    final SmartSelection.ClassificationResult[] results =
-                            smartSelection.classifyText(
+                    final TextClassifierImplNative.ClassificationResult[] results =
+                            nativeImpl.classifyText(
                                     string, start, end,
-                                    getHintFlags(string, start, end));
+                                    new TextClassifierImplNative.ClassificationOptions(
+                                            refTime.toInstant().toEpochMilli(),
+                                            refTime.getZone().getId(),
+                                            localesString));
                     final int size = results.length;
                     for (int i = 0; i < size; i++) {
-                        tsBuilder.setEntityType(results[i].mCollection, results[i].mScore);
+                        tsBuilder.setEntityType(results[i].getCollection(), results[i].getScore());
                     }
-                    return tsBuilder
-                            .setSignature(
-                                    getSignature(string, selectionStartIndex, selectionEndIndex))
+                    return tsBuilder.setId(createId(
+                            string, request.getStartIndex(), request.getEndIndex()))
                             .build();
                 } else {
                     // We can not trust the result. Log the issue and ignore the result.
@@ -150,26 +165,34 @@
                     t);
         }
         // Getting here means something went wrong, return a NO_OP result.
-        return TextClassifier.NO_OP.suggestSelection(
-                text, selectionStartIndex, selectionEndIndex, options);
+        return mFallback.suggestSelection(request);
     }
 
+    /** @inheritDoc */
     @Override
-    public TextClassification classifyText(
-            @NonNull CharSequence text, int startIndex, int endIndex,
-            @NonNull TextClassification.Options options) {
-        Utils.validateInput(text, startIndex, endIndex);
+    @WorkerThread
+    public TextClassification classifyText(TextClassification.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
         try {
-            if (text.length() > 0) {
-                final String string = text.toString();
-                final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
-                final SmartSelection.ClassificationResult[] results = getSmartSelection(locales)
-                        .classifyText(string, startIndex, endIndex,
-                                getHintFlags(string, startIndex, endIndex));
+            final int rangeLength = request.getEndIndex() - request.getStartIndex();
+            final String string = request.getText().toString();
+            if (string.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
+                final String localesString = concatenateLocales(request.getDefaultLocales());
+                final ZonedDateTime refTime = request.getReferenceTime() != null
+                        ? request.getReferenceTime() : ZonedDateTime.now();
+                final TextClassifierImplNative.ClassificationResult[] results =
+                        getNative(request.getDefaultLocales())
+                                .classifyText(
+                                        string, request.getStartIndex(), request.getEndIndex(),
+                                        new TextClassifierImplNative.ClassificationOptions(
+                                                refTime.toInstant().toEpochMilli(),
+                                                refTime.getZone().getId(),
+                                                localesString));
                 if (results.length > 0) {
-                    final TextClassification classificationResult =
-                            createClassificationResult(results, string, startIndex, endIndex);
-                    return classificationResult;
+                    return createClassificationResult(
+                            results, string,
+                            request.getStartIndex(), request.getEndIndex(), refTime.toInstant());
                 }
             }
         } catch (Throwable t) {
@@ -177,327 +200,252 @@
             Log.e(LOG_TAG, "Error getting text classification info.", t);
         }
         // Getting here means something went wrong, return a NO_OP result.
-        return TextClassifier.NO_OP.classifyText(text, startIndex, endIndex, options);
+        return mFallback.classifyText(request);
     }
 
+    /** @inheritDoc */
     @Override
-    public TextLinks generateLinks(
-            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        Utils.validateInput(text);
-        final String textString = text.toString();
-        final TextLinks.Builder builder = new TextLinks.Builder(textString);
+    @WorkerThread
+    public TextLinks generateLinks(@NonNull TextLinks.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength());
+        Utils.checkMainThread();
 
-        if (!getSettings().isSmartLinkifyEnabled()) {
-            return builder.build();
+        if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
+            return Utils.generateLegacyLinks(request);
         }
 
+        final String textString = request.getText().toString();
+        final TextLinks.Builder builder = new TextLinks.Builder(textString);
+
         try {
-            final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
-            final Collection<String> entitiesToIdentify =
-                    options != null && options.getEntityConfig() != null
-                            ? options.getEntityConfig().getEntities(this) : ENTITY_TYPES_ALL;
-            final SmartSelection smartSelection = getSmartSelection(defaultLocales);
-            final SmartSelection.AnnotatedSpan[] annotations = smartSelection.annotate(textString);
-            for (SmartSelection.AnnotatedSpan span : annotations) {
-                final SmartSelection.ClassificationResult[] results = span.getClassification();
-                if (results.length == 0 || !entitiesToIdentify.contains(results[0].mCollection)) {
+            final long startTimeMs = System.currentTimeMillis();
+            final ZonedDateTime refTime = ZonedDateTime.now();
+            final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
+                    ? request.getEntityConfig().resolveEntityListModifications(
+                            getEntitiesForHints(request.getEntityConfig().getHints()))
+                    : mSettings.getEntityListDefault();
+            final TextClassifierImplNative nativeImpl =
+                    getNative(request.getDefaultLocales());
+            final TextClassifierImplNative.AnnotatedSpan[] annotations =
+                    nativeImpl.annotate(
+                        textString,
+                        new TextClassifierImplNative.AnnotationOptions(
+                                refTime.toInstant().toEpochMilli(),
+                                        refTime.getZone().getId(),
+                                concatenateLocales(request.getDefaultLocales())));
+            for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
+                final TextClassifierImplNative.ClassificationResult[] results =
+                        span.getClassification();
+                if (results.length == 0
+                        || !entitiesToIdentify.contains(results[0].getCollection())) {
                     continue;
                 }
                 final Map<String, Float> entityScores = new HashMap<>();
                 for (int i = 0; i < results.length; i++) {
-                    entityScores.put(results[i].mCollection, results[i].mScore);
+                    entityScores.put(results[i].getCollection(), results[i].getScore());
                 }
-                builder.addLink(new TextLinks.TextLink(
-                        textString, span.getStartIndex(), span.getEndIndex(), entityScores));
+                builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores);
             }
+            final TextLinks links = builder.build();
+            final long endTimeMs = System.currentTimeMillis();
+            final String callingPackageName = request.getCallingPackageName() == null
+                    ? mContext.getPackageName()  // local (in process) TC.
+                    : request.getCallingPackageName();
+            mGenerateLinksLogger.logGenerateLinks(
+                    request.getText(), links, callingPackageName, endTimeMs - startTimeMs);
+            return links;
         } catch (Throwable t) {
             // Avoid throwing from this method. Log the error.
             Log.e(LOG_TAG, "Error getting links info.", t);
         }
-        return builder.build();
+        return mFallback.generateLinks(request);
     }
 
+    /** @inheritDoc */
     @Override
-    public Collection<String> getEntitiesForPreset(@TextClassifier.EntityPreset int entityPreset) {
-        switch (entityPreset) {
-            case TextClassifier.ENTITY_PRESET_NONE:
-                return Collections.emptyList();
-            case TextClassifier.ENTITY_PRESET_BASE:
-                return ENTITY_TYPES_BASE;
-            case TextClassifier.ENTITY_PRESET_ALL:
-                // fall through
-            default:
-                return ENTITY_TYPES_ALL;
+    public int getMaxGenerateLinksTextLength() {
+        return mSettings.getGenerateLinksMaxTextLength();
+    }
+
+    private Collection<String> getEntitiesForHints(Collection<String> hints) {
+        final boolean editable = hints.contains(HINT_TEXT_IS_EDITABLE);
+        final boolean notEditable = hints.contains(HINT_TEXT_IS_NOT_EDITABLE);
+
+        // Use the default if there is no hint, or conflicting ones.
+        final boolean useDefault = editable == notEditable;
+        if (useDefault) {
+            return mSettings.getEntityListDefault();
+        } else if (editable) {
+            return mSettings.getEntityListEditable();
+        } else {  // notEditable
+            return mSettings.getEntityListNotEditable();
         }
     }
 
+    /** @inheritDoc */
     @Override
-    public void logEvent(String source, String event) {
-        if (LOG_TAG.equals(source)) {
-            mMetricsLogger.count(event, 1);
+    public Logger getLogger(@NonNull Logger.Config config) {
+        Preconditions.checkNotNull(config);
+        synchronized (mLoggerLock) {
+            if (mLogger == null || !config.equals(mLoggerConfig)) {
+                mLoggerConfig = config;
+                mLogger = new DefaultLogger(config);
+            }
         }
+        return mLogger;
     }
 
     @Override
-    public TextClassifierConstants getSettings() {
-        if (mSettings == null) {
-            mSettings = TextClassifierConstants.loadFromString(Settings.Global.getString(
-                    mContext.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+    public void onSelectionEvent(SelectionEvent event) {
+        Preconditions.checkNotNull(event);
+        synchronized (mLoggerLock) {
+            if (mLogger2 == null) {
+                mLogger2 = new DefaultLogger(
+                        new Logger.Config(mContext, WIDGET_TYPE_UNKNOWN, null));
+            }
+            mLogger2.writeEvent(event);
         }
-        return mSettings;
     }
 
-    private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
-        synchronized (mSmartSelectionLock) {
+    private TextClassifierImplNative getNative(LocaleList localeList)
+            throws FileNotFoundException {
+        synchronized (mLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
-            final Locale locale = findBestSupportedLocaleLocked(localeList);
-            if (locale == null) {
-                throw new FileNotFoundException("No file for null locale");
+            final ModelFile bestModel = findBestModelLocked(localeList);
+            if (bestModel == null) {
+                throw new FileNotFoundException("No model for " + localeList.toLanguageTags());
             }
-            if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
-                destroySmartSelectionIfExistsLocked();
-                final ParcelFileDescriptor fd = getFdLocked(locale);
-                final int modelFd = fd.getFd();
-                mVersion = SmartSelection.getVersion(modelFd);
-                mSmartSelection = new SmartSelection(modelFd);
+            if (mNative == null || !Objects.equals(mModel, bestModel)) {
+                Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
+                destroyNativeIfExistsLocked();
+                final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
+                        new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
+                mNative = new TextClassifierImplNative(fd.getFd());
                 closeAndLogError(fd);
-                mLocale = locale;
+                mModel = bestModel;
             }
-            return mSmartSelection;
+            return mNative;
         }
     }
 
-    private String getSignature(String text, int start, int end) {
-        synchronized (mSmartSelectionLock) {
-            final String versionInfo = (mLocale != null)
-                    ? String.format(Locale.US, "%s_v%d", mLocale.toLanguageTag(), mVersion)
-                    : "";
-            final int hash = Objects.hash(text, start, end, mContext.getPackageName());
-            return String.format(Locale.US, "%s|%s|%d", LOG_TAG, versionInfo, hash);
+    private String createId(String text, int start, int end) {
+        synchronized (mLock) {
+            return DefaultLogger.createId(text, start, end, mContext, mModel.getVersion(),
+                    mModel.getSupportedLocales());
         }
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
-    private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
-        ParcelFileDescriptor updateFd;
-        int updateVersion = -1;
-        try {
-            updateFd = ParcelFileDescriptor.open(
-                    new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
-            if (updateFd != null) {
-                updateVersion = SmartSelection.getVersion(updateFd.getFd());
-            }
-        } catch (FileNotFoundException e) {
-            updateFd = null;
-        }
-        ParcelFileDescriptor factoryFd;
-        int factoryVersion = -1;
-        try {
-            final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale);
-            if (factoryModelFilePath != null) {
-                factoryFd = ParcelFileDescriptor.open(
-                        new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
-                if (factoryFd != null) {
-                    factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
-                }
-            } else {
-                factoryFd = null;
-            }
-        } catch (FileNotFoundException e) {
-            factoryFd = null;
-        }
-
-        if (updateFd == null) {
-            if (factoryFd != null) {
-                return factoryFd;
-            } else {
-                throw new FileNotFoundException(
-                        String.format("No model file found for %s", locale));
-            }
-        }
-
-        final int updateFdInt = updateFd.getFd();
-        final boolean localeMatches = Objects.equals(
-                locale.getLanguage().trim().toLowerCase(),
-                SmartSelection.getLanguage(updateFdInt).trim().toLowerCase());
-        if (factoryFd == null) {
-            if (localeMatches) {
-                return updateFd;
-            } else {
-                closeAndLogError(updateFd);
-                throw new FileNotFoundException(
-                        String.format("No model file found for %s", locale));
-            }
-        }
-
-        if (!localeMatches) {
-            closeAndLogError(updateFd);
-            return factoryFd;
-        }
-
-        if (updateVersion > factoryVersion) {
-            closeAndLogError(factoryFd);
-            return updateFd;
-        } else {
-            closeAndLogError(updateFd);
-            return factoryFd;
+    @GuardedBy("mLock") // Do not call outside this lock.
+    private void destroyNativeIfExistsLocked() {
+        if (mNative != null) {
+            mNative.close();
+            mNative = null;
         }
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
-    private void destroySmartSelectionIfExistsLocked() {
-        if (mSmartSelection != null) {
-            mSmartSelection.close();
-            mSmartSelection = null;
-        }
+    private static String concatenateLocales(@Nullable LocaleList locales) {
+        return (locales == null) ? "" : locales.toLanguageTags();
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+    /**
+     * Finds the most appropriate model to use for the given target locale list.
+     *
+     * The basic logic is: we ignore all models that don't support any of the target locales. For
+     * the remaining candidates, we take the update model unless its version number is lower than
+     * the factory version. It's assumed that factory models do not have overlapping locale ranges
+     * and conflict resolution between these models hence doesn't matter.
+     */
+    @GuardedBy("mLock") // Do not call outside this lock.
     @Nullable
-    private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
+    private ModelFile findBestModelLocked(LocaleList localeList) {
         // Specified localeList takes priority over the system default, so it is listed first.
         final String languages = localeList.isEmpty()
                 ? LocaleList.getDefault().toLanguageTags()
                 : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
         final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
 
-        final List<Locale> supportedLocales =
-                new ArrayList<>(getFactoryModelFilePathsLocked().keySet());
-        final Locale updatedModelLocale = getUpdatedModelLocale();
-        if (updatedModelLocale != null) {
-            supportedLocales.add(updatedModelLocale);
+        ModelFile bestModel = null;
+        for (ModelFile model : listAllModelsLocked()) {
+            if (model.isAnyLanguageSupported(languageRangeList)) {
+                if (model.isPreferredTo(bestModel)) {
+                    bestModel = model;
+                }
+            }
         }
-        return Locale.lookup(languageRangeList, supportedLocales);
+        return bestModel;
     }
 
-    @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
-    private Map<Locale, String> getFactoryModelFilePathsLocked() {
-        if (mModelFilePaths == null) {
-            final Map<Locale, String> modelFilePaths = new HashMap<>();
+    /** Returns a list of all model files available, in order of precedence. */
+    @GuardedBy("mLock") // Do not call outside this lock.
+    private List<ModelFile> listAllModelsLocked() {
+        if (mAllModelFiles == null) {
+            final List<ModelFile> allModels = new ArrayList<>();
+            // The update model has the highest precedence.
+            if (new File(UPDATED_MODEL_FILE_PATH).exists()) {
+                final ModelFile updatedModel = ModelFile.fromPath(UPDATED_MODEL_FILE_PATH);
+                if (updatedModel != null) {
+                    allModels.add(updatedModel);
+                }
+            }
+            // Factory models should never have overlapping locales, so the order doesn't matter.
             final File modelsDir = new File(MODEL_DIR);
             if (modelsDir.exists() && modelsDir.isDirectory()) {
-                final File[] models = modelsDir.listFiles();
+                final File[] modelFiles = modelsDir.listFiles();
                 final Pattern modelFilenamePattern = Pattern.compile(MODEL_FILE_REGEX);
-                final int size = models.length;
-                for (int i = 0; i < size; i++) {
-                    final File modelFile = models[i];
+                for (File modelFile : modelFiles) {
                     final Matcher matcher = modelFilenamePattern.matcher(modelFile.getName());
                     if (matcher.matches() && modelFile.isFile()) {
-                        final String language = matcher.group(1);
-                        final Locale locale = Locale.forLanguageTag(language);
-                        modelFilePaths.put(locale, modelFile.getAbsolutePath());
+                        final ModelFile model = ModelFile.fromPath(modelFile.getAbsolutePath());
+                        if (model != null) {
+                            allModels.add(model);
+                        }
                     }
                 }
             }
-            mModelFilePaths = modelFilePaths;
+            mAllModelFiles = allModels;
         }
-        return mModelFilePaths;
-    }
-
-    @Nullable
-    private Locale getUpdatedModelLocale() {
-        try {
-            final ParcelFileDescriptor updateFd = ParcelFileDescriptor.open(
-                    new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
-            final Locale locale = Locale.forLanguageTag(
-                    SmartSelection.getLanguage(updateFd.getFd()));
-            closeAndLogError(updateFd);
-            return locale;
-        } catch (FileNotFoundException e) {
-            return null;
-        }
+        return mAllModelFiles;
     }
 
     private TextClassification createClassificationResult(
-            SmartSelection.ClassificationResult[] classifications,
-            String text, int start, int end) {
+            TextClassifierImplNative.ClassificationResult[] classifications,
+            String text, int start, int end, @Nullable Instant referenceTime) {
         final String classifiedText = text.substring(start, end);
         final TextClassification.Builder builder = new TextClassification.Builder()
                 .setText(classifiedText);
 
         final int size = classifications.length;
+        TextClassifierImplNative.ClassificationResult highestScoringResult = null;
+        float highestScore = Float.MIN_VALUE;
         for (int i = 0; i < size; i++) {
-            builder.setEntityType(classifications[i].mCollection, classifications[i].mScore);
-        }
-
-        final String type = getHighestScoringType(classifications);
-        addActions(builder, IntentFactory.create(mContext, type, classifiedText));
-
-        return builder.setSignature(getSignature(text, start, end)).build();
-    }
-
-    /** Extends the classification with the intents that can be resolved. */
-    private void addActions(
-            TextClassification.Builder builder, List<Intent> intents) {
-        final PackageManager pm = mContext.getPackageManager();
-        final int size = intents.size();
-        for (int i = 0; i < size; i++) {
-            final Intent intent = intents.get(i);
-            final ResolveInfo resolveInfo;
-            if (intent != null) {
-                resolveInfo = pm.resolveActivity(intent, 0);
-            } else {
-                resolveInfo = null;
-            }
-            if (resolveInfo != null && resolveInfo.activityInfo != null) {
-                final String packageName = resolveInfo.activityInfo.packageName;
-                CharSequence label;
-                Drawable icon;
-                if ("android".equals(packageName)) {
-                    // Requires the chooser to find an activity to handle the intent.
-                    label = IntentFactory.getLabel(mContext, intent);
-                    icon = null;
-                } else {
-                    // A default activity will handle the intent.
-                    intent.setComponent(
-                            new ComponentName(packageName, resolveInfo.activityInfo.name));
-                    icon = resolveInfo.activityInfo.loadIcon(pm);
-                    if (icon == null) {
-                        icon = resolveInfo.loadIcon(pm);
-                    }
-                    label = resolveInfo.activityInfo.loadLabel(pm);
-                    if (label == null) {
-                        label = resolveInfo.loadLabel(pm);
-                    }
-                }
-                final String labelString = (label != null) ? label.toString() : null;
-                if (i == 0) {
-                    builder.setPrimaryAction(intent, labelString, icon);
-                } else {
-                    builder.addSecondaryAction(intent, labelString, icon);
-                }
+            builder.setEntityType(classifications[i].getCollection(),
+                                  classifications[i].getScore());
+            if (classifications[i].getScore() > highestScore) {
+                highestScoringResult = classifications[i];
+                highestScore = classifications[i].getScore();
             }
         }
-    }
 
-    private static int getHintFlags(CharSequence text, int start, int end) {
-        int flag = 0;
-        final CharSequence subText = text.subSequence(start, end);
-        if (Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(subText).matches()) {
-            flag |= SmartSelection.HINT_FLAG_EMAIL;
-        }
-        if (Patterns.AUTOLINK_WEB_URL.matcher(subText).matches()
-                && Linkify.sUrlMatchFilter.acceptMatch(text, start, end)) {
-            flag |= SmartSelection.HINT_FLAG_URL;
-        }
-        return flag;
-    }
-
-    private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) {
-        if (types.length < 1) {
-            return "";
-        }
-
-        String type = types[0].mCollection;
-        float highestScore = types[0].mScore;
-        final int size = types.length;
-        for (int i = 1; i < size; i++) {
-            if (types[i].mScore > highestScore) {
-                type = types[i].mCollection;
-                highestScore = types[i].mScore;
+        boolean isPrimaryAction = true;
+        for (LabeledIntent labeledIntent : IntentFactory.create(
+                mContext, referenceTime, highestScoringResult, classifiedText)) {
+            RemoteAction action = labeledIntent.asRemoteAction(mContext);
+            if (isPrimaryAction) {
+                // For O backwards compatibility, the first RemoteAction is also written to the
+                // legacy API fields.
+                builder.setIcon(action.getIcon().loadDrawable(mContext));
+                builder.setLabel(action.getTitle().toString());
+                builder.setIntent(labeledIntent.getIntent());
+                builder.setOnClickListener(TextClassification.createIntentOnClickListener(
+                        TextClassification.createPendingIntent(mContext,
+                                labeledIntent.getIntent())));
+                isPrimaryAction = false;
             }
+            builder.addAction(action);
         }
-        return type;
+
+        return builder.setId(createId(text, start, end)).build();
     }
 
     /**
@@ -512,92 +460,346 @@
     }
 
     /**
+     * Describes TextClassifier model files on disk.
+     */
+    private static final class ModelFile {
+
+        private final String mPath;
+        private final String mName;
+        private final int mVersion;
+        private final List<Locale> mSupportedLocales;
+        private final boolean mLanguageIndependent;
+
+        /** Returns null if the path did not point to a compatible model. */
+        static @Nullable ModelFile fromPath(String path) {
+            final File file = new File(path);
+            try {
+                final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
+                        file, ParcelFileDescriptor.MODE_READ_ONLY);
+                final int version = TextClassifierImplNative.getVersion(modelFd.getFd());
+                final String supportedLocalesStr =
+                        TextClassifierImplNative.getLocales(modelFd.getFd());
+                if (supportedLocalesStr.isEmpty()) {
+                    Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
+                    return null;
+                }
+                final boolean languageIndependent = supportedLocalesStr.equals("*");
+                final List<Locale> supportedLocales = new ArrayList<>();
+                for (String langTag : supportedLocalesStr.split(",")) {
+                    supportedLocales.add(Locale.forLanguageTag(langTag));
+                }
+                closeAndLogError(modelFd);
+                return new ModelFile(path, file.getName(), version, supportedLocales,
+                                     languageIndependent);
+            } catch (FileNotFoundException e) {
+                Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e);
+                return null;
+            }
+        }
+
+        /** The absolute path to the model file. */
+        String getPath() {
+            return mPath;
+        }
+
+        /** A name to use for id generation. Effectively the name of the model file. */
+        String getName() {
+            return mName;
+        }
+
+        /** Returns the version tag in the model's metadata. */
+        int getVersion() {
+            return mVersion;
+        }
+
+        /** Returns whether the language supports any language in the given ranges. */
+        boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
+            return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
+        }
+
+        /** All locales supported by the model. */
+        List<Locale> getSupportedLocales() {
+            return Collections.unmodifiableList(mSupportedLocales);
+        }
+
+        public boolean isPreferredTo(ModelFile model) {
+            // A model is preferred to no model.
+            if (model == null) {
+                return true;
+            }
+
+            // A language-specific model is preferred to a language independent
+            // model.
+            if (!mLanguageIndependent && model.mLanguageIndependent) {
+                return true;
+            }
+
+            // A higher-version model is preferred.
+            if (getVersion() > model.getVersion()) {
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            } else if (other == null || !ModelFile.class.isAssignableFrom(other.getClass())) {
+                return false;
+            } else {
+                final ModelFile otherModel = (ModelFile) other;
+                return mPath.equals(otherModel.mPath);
+            }
+        }
+
+        @Override
+        public String toString() {
+            final StringJoiner localesJoiner = new StringJoiner(",");
+            for (Locale locale : mSupportedLocales) {
+                localesJoiner.add(locale.toLanguageTag());
+            }
+            return String.format(Locale.US, "ModelFile { path=%s name=%s version=%d locales=%s }",
+                    mPath, mName, mVersion, localesJoiner.toString());
+        }
+
+        private ModelFile(String path, String name, int version, List<Locale> supportedLocales,
+                          boolean languageIndependent) {
+            mPath = path;
+            mName = name;
+            mVersion = version;
+            mSupportedLocales = supportedLocales;
+            mLanguageIndependent = languageIndependent;
+        }
+    }
+
+    /**
+     * Helper class to store the information from which RemoteActions are built.
+     */
+    private static final class LabeledIntent {
+        private String mTitle;
+        private String mDescription;
+        private Intent mIntent;
+
+        LabeledIntent(String title, String description, Intent intent) {
+            mTitle = title;
+            mDescription = description;
+            mIntent = intent;
+        }
+
+        String getTitle() {
+            return mTitle;
+        }
+
+        String getDescription() {
+            return mDescription;
+        }
+
+        Intent getIntent() {
+            return mIntent;
+        }
+
+        RemoteAction asRemoteAction(Context context) {
+            final PackageManager pm = context.getPackageManager();
+            final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
+            final String packageName = resolveInfo != null && resolveInfo.activityInfo != null
+                    ? resolveInfo.activityInfo.packageName : null;
+            Icon icon = null;
+            boolean shouldShowIcon = false;
+            if (packageName != null && !"android".equals(packageName)) {
+                // There is a default activity handling the intent.
+                mIntent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
+                if (resolveInfo.activityInfo.getIconResource() != 0) {
+                    icon = Icon.createWithResource(
+                            packageName, resolveInfo.activityInfo.getIconResource());
+                    shouldShowIcon = true;
+                }
+            }
+            if (icon == null) {
+                // RemoteAction requires that there be an icon.
+                icon = Icon.createWithResource("android",
+                        com.android.internal.R.drawable.ic_more_items);
+            }
+            RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
+                    TextClassification.createPendingIntent(context, mIntent));
+            action.setShouldShowIcon(shouldShowIcon);
+            return action;
+        }
+    }
+
+    /**
      * Creates intents based on the classification type.
      */
-    private static final class IntentFactory {
+    static final class IntentFactory {
+
+        private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
+        private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
 
         private IntentFactory() {}
 
         @NonNull
-        public static List<Intent> create(Context context, String type, String text) {
-            final List<Intent> intents = new ArrayList<>();
-            type = type.trim().toLowerCase(Locale.ENGLISH);
+        public static List<LabeledIntent> create(
+                Context context,
+                @Nullable Instant referenceTime,
+                TextClassifierImplNative.ClassificationResult classification,
+                String text) {
+            final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
             text = text.trim();
             switch (type) {
                 case TextClassifier.TYPE_EMAIL:
-                    intents.add(new Intent(Intent.ACTION_SENDTO)
-                            .setData(Uri.parse(String.format("mailto:%s", text))));
-                    intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                            .putExtra(ContactsContract.Intents.Insert.EMAIL, text));
-                    break;
+                    return createForEmail(context, text);
                 case TextClassifier.TYPE_PHONE:
-                    intents.add(new Intent(Intent.ACTION_DIAL)
-                            .setData(Uri.parse(String.format("tel:%s", text))));
-                    intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                            .putExtra(ContactsContract.Intents.Insert.PHONE, text));
-                    intents.add(new Intent(Intent.ACTION_SENDTO)
-                            .setData(Uri.parse(String.format("smsto:%s", text))));
-                    break;
+                    return createForPhone(context, text);
                 case TextClassifier.TYPE_ADDRESS:
-                    intents.add(new Intent(Intent.ACTION_VIEW)
-                            .setData(Uri.parse(String.format("geo:0,0?q=%s", text))));
-                    break;
+                    return createForAddress(context, text);
                 case TextClassifier.TYPE_URL:
-                    final String httpPrefix = "http://";
-                    final String httpsPrefix = "https://";
-                    if (text.toLowerCase().startsWith(httpPrefix)) {
-                        text = httpPrefix + text.substring(httpPrefix.length());
-                    } else if (text.toLowerCase().startsWith(httpsPrefix)) {
-                        text = httpsPrefix + text.substring(httpsPrefix.length());
+                    return createForUrl(context, text);
+                case TextClassifier.TYPE_DATE:
+                case TextClassifier.TYPE_DATE_TIME:
+                    if (classification.getDatetimeResult() != null) {
+                        final Instant parsedTime = Instant.ofEpochMilli(
+                                classification.getDatetimeResult().getTimeMsUtc());
+                        return createForDatetime(context, type, referenceTime, parsedTime);
                     } else {
-                        text = httpPrefix + text;
+                        return new ArrayList<>();
                     }
-                    intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text))
-                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()));
-                    break;
+                case TextClassifier.TYPE_FLIGHT_NUMBER:
+                    return createForFlight(context, text);
+                default:
+                    return new ArrayList<>();
             }
-            return intents;
         }
 
-        @Nullable
-        public static String getLabel(Context context, @Nullable Intent intent) {
-            if (intent == null || intent.getAction() == null) {
-                return null;
+        @NonNull
+        private static List<LabeledIntent> createForEmail(Context context, String text) {
+            return Arrays.asList(
+                    new LabeledIntent(
+                            context.getString(com.android.internal.R.string.email),
+                            context.getString(com.android.internal.R.string.email_desc),
+                            new Intent(Intent.ACTION_SENDTO)
+                                    .setData(Uri.parse(String.format("mailto:%s", text)))),
+                    new LabeledIntent(
+                            context.getString(com.android.internal.R.string.add_contact),
+                            context.getString(com.android.internal.R.string.add_contact_desc),
+                            new Intent(Intent.ACTION_INSERT_OR_EDIT)
+                                    .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text)));
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForPhone(Context context, String text) {
+            final List<LabeledIntent> actions = new ArrayList<>();
+            final UserManager userManager = context.getSystemService(UserManager.class);
+            final Bundle userRestrictions = userManager != null
+                    ? userManager.getUserRestrictions() : new Bundle();
+            if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
+                actions.add(new LabeledIntent(
+                        context.getString(com.android.internal.R.string.dial),
+                        context.getString(com.android.internal.R.string.dial_desc),
+                        new Intent(Intent.ACTION_DIAL).setData(
+                                Uri.parse(String.format("tel:%s", text)))));
             }
-            switch (intent.getAction()) {
-                case Intent.ACTION_DIAL:
-                    return context.getString(com.android.internal.R.string.dial);
-                case Intent.ACTION_SENDTO:
-                    switch (intent.getScheme()) {
-                        case "mailto":
-                            return context.getString(com.android.internal.R.string.email);
-                        case "smsto":
-                            return context.getString(com.android.internal.R.string.sms);
-                        default:
-                            return null;
-                    }
-                case Intent.ACTION_INSERT_OR_EDIT:
-                    switch (intent.getDataString()) {
-                        case ContactsContract.Contacts.CONTENT_ITEM_TYPE:
-                            return context.getString(com.android.internal.R.string.add_contact);
-                        default:
-                            return null;
-                    }
-                case Intent.ACTION_VIEW:
-                    switch (intent.getScheme()) {
-                        case "geo":
-                            return context.getString(com.android.internal.R.string.map);
-                        case "http": // fall through
-                        case "https":
-                            return context.getString(com.android.internal.R.string.browse);
-                        default:
-                            return null;
-                    }
-                default:
-                    return null;
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.add_contact),
+                    context.getString(com.android.internal.R.string.add_contact_desc),
+                    new Intent(Intent.ACTION_INSERT_OR_EDIT)
+                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+                            .putExtra(ContactsContract.Intents.Insert.PHONE, text)));
+            if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
+                actions.add(new LabeledIntent(
+                        context.getString(com.android.internal.R.string.sms),
+                        context.getString(com.android.internal.R.string.sms_desc),
+                        new Intent(Intent.ACTION_SENDTO)
+                                .setData(Uri.parse(String.format("smsto:%s", text)))));
             }
+            return actions;
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForAddress(Context context, String text) {
+            final List<LabeledIntent> actions = new ArrayList<>();
+            try {
+                final String encText = URLEncoder.encode(text, "UTF-8");
+                actions.add(new LabeledIntent(
+                        context.getString(com.android.internal.R.string.map),
+                        context.getString(com.android.internal.R.string.map_desc),
+                        new Intent(Intent.ACTION_VIEW)
+                                .setData(Uri.parse(String.format("geo:0,0?q=%s", encText)))));
+            } catch (UnsupportedEncodingException e) {
+                Log.e(LOG_TAG, "Could not encode address", e);
+            }
+            return actions;
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForUrl(Context context, String text) {
+            final String httpPrefix = "http://";
+            final String httpsPrefix = "https://";
+            if (text.toLowerCase().startsWith(httpPrefix)) {
+                text = httpPrefix + text.substring(httpPrefix.length());
+            } else if (text.toLowerCase().startsWith(httpsPrefix)) {
+                text = httpsPrefix + text.substring(httpsPrefix.length());
+            } else {
+                text = httpPrefix + text;
+            }
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.browse),
+                    context.getString(com.android.internal.R.string.browse_desc),
+                    new Intent(Intent.ACTION_VIEW, Uri.parse(text))
+                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())));
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForDatetime(
+                Context context, String type, @Nullable Instant referenceTime,
+                Instant parsedTime) {
+            if (referenceTime == null) {
+                // If no reference time was given, use now.
+                referenceTime = Instant.now();
+            }
+            List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(createCalendarViewIntent(context, parsedTime));
+            final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
+            if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
+                actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
+            }
+            return actions;
+        }
+
+        @NonNull
+        private static List<LabeledIntent> createForFlight(Context context, String text) {
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.view_flight),
+                    context.getString(com.android.internal.R.string.view_flight_desc),
+                    new Intent(Intent.ACTION_WEB_SEARCH)
+                            .putExtra(SearchManager.QUERY, text)));
+        }
+
+        @NonNull
+        private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
+            Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
+            builder.appendPath("time");
+            ContentUris.appendId(builder, parsedTime.toEpochMilli());
+            return new LabeledIntent(
+                    context.getString(com.android.internal.R.string.view_calendar),
+                    context.getString(com.android.internal.R.string.view_calendar_desc),
+                    new Intent(Intent.ACTION_VIEW).setData(builder.build()));
+        }
+
+        @NonNull
+        private static LabeledIntent createCalendarCreateEventIntent(
+                Context context, Instant parsedTime, @EntityType String type) {
+            final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
+            return new LabeledIntent(
+                    context.getString(com.android.internal.R.string.add_calendar_event),
+                    context.getString(com.android.internal.R.string.add_calendar_event_desc),
+                    new Intent(Intent.ACTION_INSERT)
+                            .setData(CalendarContract.Events.CONTENT_URI)
+                            .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
+                            .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
+                                    parsedTime.toEpochMilli())
+                            .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
+                                    parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
         }
     }
 }
diff --git a/android/view/textclassifier/TextClassifierImplNative.java b/android/view/textclassifier/TextClassifierImplNative.java
new file mode 100644
index 0000000..3d4c8f2
--- /dev/null
+++ b/android/view/textclassifier/TextClassifierImplNative.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.view.textclassifier;
+
+import android.content.res.AssetFileDescriptor;
+
+/**
+ * Java wrapper for TextClassifier native library interface. This library is used for detecting
+ * entities in text.
+ */
+final class TextClassifierImplNative {
+
+    static {
+        System.loadLibrary("textclassifier");
+    }
+
+    private final long mModelPtr;
+
+    /**
+     * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
+     * a file descriptor.
+     */
+    TextClassifierImplNative(int fd) {
+        mModelPtr = nativeNew(fd);
+        if (mModelPtr == 0L) {
+            throw new IllegalArgumentException("Couldn't initialize TC from file descriptor.");
+        }
+    }
+
+    /**
+     * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
+     * a file path.
+     */
+    TextClassifierImplNative(String path) {
+        mModelPtr = nativeNewFromPath(path);
+        if (mModelPtr == 0L) {
+            throw new IllegalArgumentException("Couldn't initialize TC from given file.");
+        }
+    }
+
+    /**
+     * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
+     * an AssetFileDescriptor.
+     */
+    TextClassifierImplNative(AssetFileDescriptor afd) {
+        mModelPtr = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength());
+        if (mModelPtr == 0L) {
+            throw new IllegalArgumentException(
+                    "Couldn't initialize TC from given AssetFileDescriptor");
+        }
+    }
+
+    /**
+     * Given a string context and current selection, computes the SmartSelection suggestion.
+     *
+     * <p>The begin and end are character indices into the context UTF8 string. selectionBegin is
+     * the character index where the selection begins, and selectionEnd is the index of one
+     * character past the selection span.
+     *
+     * <p>The return value is an array of two ints: suggested selection beginning and end, with the
+     * same semantics as the input selectionBeginning and selectionEnd.
+     */
+    public int[] suggestSelection(
+            String context, int selectionBegin, int selectionEnd, SelectionOptions options) {
+        return nativeSuggestSelection(mModelPtr, context, selectionBegin, selectionEnd, options);
+    }
+
+    /**
+     * Given a string context and current selection, classifies the type of the selected text.
+     *
+     * <p>The begin and end params are character indices in the context string.
+     *
+     * <p>Returns an array of ClassificationResult objects with the probability scores for different
+     * collections.
+     */
+    public ClassificationResult[] classifyText(
+            String context, int selectionBegin, int selectionEnd, ClassificationOptions options) {
+        return nativeClassifyText(mModelPtr, context, selectionBegin, selectionEnd, options);
+    }
+
+    /**
+     * Annotates given input text. The annotations should cover the whole input context except for
+     * whitespaces, and are sorted by their position in the context string.
+     */
+    public AnnotatedSpan[] annotate(String text, AnnotationOptions options) {
+        return nativeAnnotate(mModelPtr, text, options);
+    }
+
+    /** Frees up the allocated memory. */
+    public void close() {
+        nativeClose(mModelPtr);
+    }
+
+    /** Returns a comma separated list of locales supported by the model as BCP 47 tags. */
+    public static String getLocales(int fd) {
+        return nativeGetLocales(fd);
+    }
+
+    /** Returns the version of the model. */
+    public static int getVersion(int fd) {
+        return nativeGetVersion(fd);
+    }
+
+    /** Represents a datetime parsing result from classifyText calls. */
+    public static final class DatetimeResult {
+        static final int GRANULARITY_YEAR = 0;
+        static final int GRANULARITY_MONTH = 1;
+        static final int GRANULARITY_WEEK = 2;
+        static final int GRANULARITY_DAY = 3;
+        static final int GRANULARITY_HOUR = 4;
+        static final int GRANULARITY_MINUTE = 5;
+        static final int GRANULARITY_SECOND = 6;
+
+        private final long mTimeMsUtc;
+        private final int mGranularity;
+
+        DatetimeResult(long timeMsUtc, int granularity) {
+            mGranularity = granularity;
+            mTimeMsUtc = timeMsUtc;
+        }
+
+        public long getTimeMsUtc() {
+            return mTimeMsUtc;
+        }
+
+        public int getGranularity() {
+            return mGranularity;
+        }
+    }
+
+    /** Represents a result of classifyText method call. */
+    public static final class ClassificationResult {
+        private final String mCollection;
+        private final float mScore;
+        private final DatetimeResult mDatetimeResult;
+
+        ClassificationResult(
+                String collection, float score, DatetimeResult datetimeResult) {
+            mCollection = collection;
+            mScore = score;
+            mDatetimeResult = datetimeResult;
+        }
+
+        public String getCollection() {
+            if (mCollection.equals(TextClassifier.TYPE_DATE) && mDatetimeResult != null) {
+                switch (mDatetimeResult.getGranularity()) {
+                    case DatetimeResult.GRANULARITY_HOUR:
+                        // fall through
+                    case DatetimeResult.GRANULARITY_MINUTE:
+                        // fall through
+                    case DatetimeResult.GRANULARITY_SECOND:
+                        return TextClassifier.TYPE_DATE_TIME;
+                    default:
+                        return TextClassifier.TYPE_DATE;
+                }
+            }
+            return mCollection;
+        }
+
+        public float getScore() {
+            return mScore;
+        }
+
+        public DatetimeResult getDatetimeResult() {
+            return mDatetimeResult;
+        }
+    }
+
+    /** Represents a result of Annotate call. */
+    public static final class AnnotatedSpan {
+        private final int mStartIndex;
+        private final int mEndIndex;
+        private final ClassificationResult[] mClassification;
+
+        AnnotatedSpan(
+                int startIndex, int endIndex, ClassificationResult[] classification) {
+            mStartIndex = startIndex;
+            mEndIndex = endIndex;
+            mClassification = classification;
+        }
+
+        public int getStartIndex() {
+            return mStartIndex;
+        }
+
+        public int getEndIndex() {
+            return mEndIndex;
+        }
+
+        public ClassificationResult[] getClassification() {
+            return mClassification;
+        }
+    }
+
+    /** Represents options for the suggestSelection call. */
+    public static final class SelectionOptions {
+        private final String mLocales;
+
+        SelectionOptions(String locales) {
+            mLocales = locales;
+        }
+
+        public String getLocales() {
+            return mLocales;
+        }
+    }
+
+    /** Represents options for the classifyText call. */
+    public static final class ClassificationOptions {
+        private final long mReferenceTimeMsUtc;
+        private final String mReferenceTimezone;
+        private final String mLocales;
+
+        ClassificationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) {
+            mReferenceTimeMsUtc = referenceTimeMsUtc;
+            mReferenceTimezone = referenceTimezone;
+            mLocales = locale;
+        }
+
+        public long getReferenceTimeMsUtc() {
+            return mReferenceTimeMsUtc;
+        }
+
+        public String getReferenceTimezone() {
+            return mReferenceTimezone;
+        }
+
+        public String getLocale() {
+            return mLocales;
+        }
+    }
+
+    /** Represents options for the Annotate call. */
+    public static final class AnnotationOptions {
+        private final long mReferenceTimeMsUtc;
+        private final String mReferenceTimezone;
+        private final String mLocales;
+
+        AnnotationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) {
+            mReferenceTimeMsUtc = referenceTimeMsUtc;
+            mReferenceTimezone = referenceTimezone;
+            mLocales = locale;
+        }
+
+        public long getReferenceTimeMsUtc() {
+            return mReferenceTimeMsUtc;
+        }
+
+        public String getReferenceTimezone() {
+            return mReferenceTimezone;
+        }
+
+        public String getLocale() {
+            return mLocales;
+        }
+    }
+
+    private static native long nativeNew(int fd);
+
+    private static native long nativeNewFromPath(String path);
+
+    private static native long nativeNewFromAssetFileDescriptor(
+            AssetFileDescriptor afd, long offset, long size);
+
+    private static native int[] nativeSuggestSelection(
+            long context,
+            String text,
+            int selectionBegin,
+            int selectionEnd,
+            SelectionOptions options);
+
+    private static native ClassificationResult[] nativeClassifyText(
+            long context,
+            String text,
+            int selectionBegin,
+            int selectionEnd,
+            ClassificationOptions options);
+
+    private static native AnnotatedSpan[] nativeAnnotate(
+            long context, String text, AnnotationOptions options);
+
+    private static native void nativeClose(long context);
+
+    private static native String nativeGetLocales(int fd);
+
+    private static native int nativeGetVersion(int fd);
+}
diff --git a/android/view/textclassifier/TextLinks.java b/android/view/textclassifier/TextLinks.java
index ba854e0..17c7b13 100644
--- a/android/view/textclassifier/TextLinks.java
+++ b/android/view/textclassifier/TextLinks.java
@@ -17,22 +17,32 @@
 package android.view.textclassifier;
 
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.SpannableString;
+import android.text.Spannable;
+import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
 import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.function.Function;
 
@@ -41,48 +51,102 @@
  * address, url, etc) they may be.
  */
 public final class TextLinks implements Parcelable {
+
+    /**
+     * Return status of an attempt to apply TextLinks to text.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
+            STATUS_DIFFERENT_TEXT})
+    public @interface Status {}
+
+    /** Links were successfully applied to the text. */
+    public static final int STATUS_LINKS_APPLIED = 0;
+
+    /** No links exist to apply to text. Links count is zero. */
+    public static final int STATUS_NO_LINKS_FOUND = 1;
+
+    /** No links applied to text. The links were filtered out. */
+    public static final int STATUS_NO_LINKS_APPLIED = 2;
+
+    /** The specified text does not match the text used to generate the links. */
+    public static final int STATUS_DIFFERENT_TEXT = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
+    public @interface ApplyStrategy {}
+
+    /**
+     * Do not replace {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to
+     * be applied to. Do not apply the TextLinkSpan.
+     */
+    public static final int APPLY_STRATEGY_IGNORE = 0;
+
+    /**
+     * Replace any {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to be
+     * applied to.
+     */
+    public static final int APPLY_STRATEGY_REPLACE = 1;
+
     private final String mFullText;
     private final List<TextLink> mLinks;
 
-    private TextLinks(String fullText, Collection<TextLink> links) {
+    private TextLinks(String fullText, ArrayList<TextLink> links) {
         mFullText = fullText;
-        mLinks = Collections.unmodifiableList(new ArrayList<>(links));
+        mLinks = Collections.unmodifiableList(links);
+    }
+
+    /**
+     * Returns the text that was used to generate these links.
+     * @hide
+     */
+    @NonNull
+    public String getText() {
+        return mFullText;
     }
 
     /**
      * Returns an unmodifiable Collection of the links.
      */
+    @NonNull
     public Collection<TextLink> getLinks() {
         return mLinks;
     }
 
     /**
      * Annotates the given text with the generated links. It will fail if the provided text doesn't
-     * match the original text used to crete the TextLinks.
+     * match the original text used to create the TextLinks.
      *
-     * @param text the text to apply the links to. Must match the original text.
-     * @param spanFactory a factory to generate spans from TextLinks. Will use a default if null.
+     * <p><strong>NOTE: </strong>It may be necessary to set a LinkMovementMethod on the TextView
+     * widget to properly handle links. See {@link TextView#setMovementMethod(MovementMethod)}
      *
-     * @return Success or failure.
+     * @param text the text to apply the links to. Must match the original text
+     * @param applyStrategy the apply strategy used to determine how to apply links to text.
+     *      e.g {@link TextLinks#APPLY_STRATEGY_IGNORE}
+     * @param spanFactory a custom span factory for converting TextLinks to TextLinkSpans.
+     *      Set to {@code null} to use the default span factory.
+     *
+     * @return a status code indicating whether or not the links were successfully applied
+     *      e.g. {@link #STATUS_LINKS_APPLIED}
      */
-    public boolean apply(
-            @NonNull SpannableString text,
-            @Nullable Function<TextLink, ClickableSpan> spanFactory) {
+    @Status
+    public int apply(
+            @NonNull Spannable text,
+            @ApplyStrategy int applyStrategy,
+            @Nullable Function<TextLink, TextLinkSpan> spanFactory) {
         Preconditions.checkNotNull(text);
-        if (!mFullText.equals(text.toString())) {
-            return false;
-        }
+        return new TextLinksParams.Builder()
+                .setApplyStrategy(applyStrategy)
+                .setSpanFactory(spanFactory)
+                .build()
+                .apply(text, this);
+    }
 
-        if (spanFactory == null) {
-            spanFactory = DEFAULT_SPAN_FACTORY;
-        }
-        for (TextLink link : mLinks) {
-            final ClickableSpan span = spanFactory.apply(link);
-            if (span != null) {
-                text.setSpan(span, link.getStart(), link.getEnd(), 0);
-            }
-        }
-        return true;
+    @Override
+    public String toString() {
+        return String.format(Locale.US, "TextLinks{fullText=%s, links=%s}", mFullText, mLinks);
     }
 
     @Override
@@ -119,30 +183,35 @@
      */
     public static final class TextLink implements Parcelable {
         private final EntityConfidence mEntityScores;
-        private final String mOriginalText;
         private final int mStart;
         private final int mEnd;
+        @Nullable final URLSpan mUrlSpan;
 
         /**
          * Create a new TextLink.
          *
-         * @throws IllegalArgumentException if entityScores is null or empty.
+         * @param start The start index of the identified subsequence
+         * @param end The end index of the identified subsequence
+         * @param entityScores A mapping of entity type to confidence score
+         * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
+         *
+         * @throws IllegalArgumentException if entityScores is null or empty
          */
-        public TextLink(String originalText, int start, int end, Map<String, Float> entityScores) {
-            Preconditions.checkNotNull(originalText);
+        TextLink(int start, int end, Map<String, Float> entityScores,
+                @Nullable URLSpan urlSpan) {
             Preconditions.checkNotNull(entityScores);
             Preconditions.checkArgument(!entityScores.isEmpty());
             Preconditions.checkArgument(start <= end);
-            mOriginalText = originalText;
             mStart = start;
             mEnd = end;
             mEntityScores = new EntityConfidence(entityScores);
+            mUrlSpan = urlSpan;
         }
 
         /**
          * Returns the start index of this link in the original text.
          *
-         * @return the start index.
+         * @return the start index
          */
         public int getStart() {
             return mStart;
@@ -151,7 +220,7 @@
         /**
          * Returns the end index of this link in the original text.
          *
-         * @return the end index.
+         * @return the end index
          */
         public int getEnd() {
             return mEnd;
@@ -160,7 +229,7 @@
         /**
          * Returns the number of entity types that have confidence scores.
          *
-         * @return the entity count.
+         * @return the entity count
          */
         public int getEntityCount() {
             return mEntityScores.getEntities().size();
@@ -169,23 +238,30 @@
         /**
          * Returns the entity type at a given index. Entity types are sorted by confidence.
          *
-         * @return the entity type at the provided index.
+         * @return the entity type at the provided index
          */
-        @NonNull public @TextClassifier.EntityType String getEntity(int index) {
+        @NonNull public @EntityType String getEntity(int index) {
             return mEntityScores.getEntities().get(index);
         }
 
         /**
          * Returns the confidence score for a particular entity type.
          *
-         * @param entityType the entity type.
+         * @param entityType the entity type
          */
         public @FloatRange(from = 0.0, to = 1.0) float getConfidenceScore(
-                @TextClassifier.EntityType String entityType) {
+                @EntityType String entityType) {
             return mEntityScores.getConfidenceScore(entityType);
         }
 
         @Override
+        public String toString() {
+            return String.format(Locale.US,
+                    "TextLink{start=%s, end=%s, entityScores=%s, urlSpan=%s}",
+                    mStart, mEnd, mEntityScores, mUrlSpan);
+        }
+
+        @Override
         public int describeContents() {
             return 0;
         }
@@ -193,7 +269,6 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             mEntityScores.writeToParcel(dest, flags);
-            dest.writeString(mOriginalText);
             dest.writeInt(mStart);
             dest.writeInt(mEnd);
         }
@@ -213,46 +288,47 @@
 
         private TextLink(Parcel in) {
             mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
-            mOriginalText = in.readString();
             mStart = in.readInt();
             mEnd = in.readInt();
+            mUrlSpan = null;
         }
     }
 
     /**
-     * Optional input parameters for generating TextLinks.
+     * A request object for generating TextLinks.
      */
-    public static final class Options implements Parcelable {
+    public static final class Request implements Parcelable {
 
-        private LocaleList mDefaultLocales;
-        private TextClassifier.EntityConfig mEntityConfig;
+        private final CharSequence mText;
+        @Nullable private final LocaleList mDefaultLocales;
+        @Nullable private final TextClassifier.EntityConfig mEntityConfig;
+        private final boolean mLegacyFallback;
+        private String mCallingPackageName;
 
-        public Options() {}
-
-        /**
-         * @param defaultLocales ordered list of locale preferences that may be used to
-         *                       disambiguate the provided text. If no locale preferences exist,
-         *                       set this to null or an empty locale list.
-         */
-        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
+        private Request(
+                CharSequence text,
+                LocaleList defaultLocales,
+                TextClassifier.EntityConfig entityConfig,
+                boolean legacyFallback,
+                String callingPackageName) {
+            mText = text;
             mDefaultLocales = defaultLocales;
-            return this;
+            mEntityConfig = entityConfig;
+            mLegacyFallback = legacyFallback;
+            mCallingPackageName = callingPackageName;
         }
 
         /**
-         * Sets the entity configuration to use. This determines what types of entities the
-         * TextClassifier will look for.
-         *
-         * @param entityConfig EntityConfig to use
+         * Returns the text to generate links for.
          */
-        public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
-            mEntityConfig = entityConfig;
-            return this;
+        @NonNull
+        public CharSequence getText() {
+            return mText;
         }
 
         /**
          * @return ordered list of locale preferences that can be used to disambiguate
-         *      the provided text.
+         *      the provided text
          */
         @Nullable
         public LocaleList getDefaultLocales() {
@@ -260,7 +336,7 @@
         }
 
         /**
-         * @return The config representing the set of entities to look for.
+         * @return The config representing the set of entities to look for
          * @see #setEntityConfig(TextClassifier.EntityConfig)
          */
         @Nullable
@@ -268,6 +344,114 @@
             return mEntityConfig;
         }
 
+        /**
+         * Returns whether the TextClassifier can fallback to legacy links if smart linkify is
+         * disabled.
+         * <strong>Note: </strong>This is not parcelled.
+         * @hide
+         */
+        public boolean isLegacyFallback() {
+            return mLegacyFallback;
+        }
+
+        /**
+         * Sets the name of the package that requested the links to get generated.
+         */
+        void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * A builder for building TextLinks requests.
+         */
+        public static final class Builder {
+
+            private final CharSequence mText;
+
+            @Nullable private LocaleList mDefaultLocales;
+            @Nullable private TextClassifier.EntityConfig mEntityConfig;
+            private boolean mLegacyFallback = true; // Use legacy fall back by default.
+            private String mCallingPackageName;
+
+            public Builder(@NonNull CharSequence text) {
+                mText = Preconditions.checkNotNull(text);
+            }
+
+            /**
+             * @param defaultLocales ordered list of locale preferences that may be used to
+             *                       disambiguate the provided text. If no locale preferences exist,
+             *                       set this to null or an empty locale list.
+             * @return this builder
+             */
+            @NonNull
+            public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
+                mDefaultLocales = defaultLocales;
+                return this;
+            }
+
+            /**
+             * Sets the entity configuration to use. This determines what types of entities the
+             * TextClassifier will look for.
+             * Set to {@code null} for the default entity config and teh TextClassifier will
+             * automatically determine what links to generate.
+             *
+             * @return this builder
+             */
+            @NonNull
+            public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+                mEntityConfig = entityConfig;
+                return this;
+            }
+
+            /**
+             * Sets whether the TextClassifier can fallback to legacy links if smart linkify is
+             * disabled.
+             *
+             * <p><strong>Note: </strong>This is not parcelled.
+             *
+             * @return this builder
+             * @hide
+             */
+            @NonNull
+            public Builder setLegacyFallback(boolean legacyFallback) {
+                mLegacyFallback = legacyFallback;
+                return this;
+            }
+
+            /**
+             * Sets the name of the package that requested the links to get generated.
+             *
+             * @return this builder
+             * @hide
+             */
+            @NonNull
+            public Builder setCallingPackageName(@Nullable String callingPackageName) {
+                mCallingPackageName = callingPackageName;
+                return this;
+            }
+
+            /**
+             * Builds and returns the request object.
+             */
+            @NonNull
+            public Request build() {
+                return new Request(
+                        mText, mDefaultLocales, mEntityConfig,
+                        mLegacyFallback, mCallingPackageName);
+            }
+
+        }
+
+        /**
+         * @return the name of the package that requested the links to get generated.
+         * TODO: make available as system API
+         * @hide
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -275,6 +459,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mText.toString());
             dest.writeInt(mDefaultLocales != null ? 1 : 0);
             if (mDefaultLocales != null) {
                 mDefaultLocales.writeToParcel(dest, flags);
@@ -283,61 +468,94 @@
             if (mEntityConfig != null) {
                 mEntityConfig.writeToParcel(dest, flags);
             }
+            dest.writeString(mCallingPackageName);
         }
 
-        public static final Parcelable.Creator<Options> CREATOR =
-                new Parcelable.Creator<Options>() {
+        public static final Parcelable.Creator<Request> CREATOR =
+                new Parcelable.Creator<Request>() {
                     @Override
-                    public Options createFromParcel(Parcel in) {
-                        return new Options(in);
+                    public Request createFromParcel(Parcel in) {
+                        return new Request(in);
                     }
 
                     @Override
-                    public Options[] newArray(int size) {
-                        return new Options[size];
+                    public Request[] newArray(int size) {
+                        return new Request[size];
                     }
                 };
 
-        private Options(Parcel in) {
-            if (in.readInt() > 0) {
-                mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
-            }
-            if (in.readInt() > 0) {
-                mEntityConfig = TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
-            }
+        private Request(Parcel in) {
+            mText = in.readString();
+            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
+            mEntityConfig = in.readInt() == 0
+                    ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
+            mLegacyFallback = true;
+            mCallingPackageName = in.readString();
         }
     }
 
     /**
-     * A function to create spans from TextLinks.
+     * A ClickableSpan for a TextLink.
      *
-     * Applies only to TextViews.
-     * We can hide this until we are convinced we want it to be part of the public API.
-     *
-     * @hide
+     * <p>Applies only to TextViews.
      */
-    public static final Function<TextLink, ClickableSpan> DEFAULT_SPAN_FACTORY =
-            textLink -> new ClickableSpan() {
-                @Override
-                public void onClick(View widget) {
-                    if (widget instanceof TextView) {
-                        final TextView textView = (TextView) widget;
-                        textView.requestActionMode(textLink);
+    public static class TextLinkSpan extends ClickableSpan {
+
+        private final TextLink mTextLink;
+
+        public TextLinkSpan(@NonNull TextLink textLink) {
+            mTextLink = textLink;
+        }
+
+        @Override
+        public void onClick(View widget) {
+            if (widget instanceof TextView) {
+                final TextView textView = (TextView) widget;
+                final Context context = textView.getContext();
+                if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
+                    if (textView.requestFocus()) {
+                        textView.requestActionMode(this);
+                    } else {
+                        // If textView can not take focus, then simply handle the click as it will
+                        // be difficult to get rid of the floating action mode.
+                        textView.handleClick(this);
+                    }
+                } else {
+                    if (mTextLink.mUrlSpan != null) {
+                        mTextLink.mUrlSpan.onClick(textView);
+                    } else {
+                        textView.handleClick(this);
                     }
                 }
-            };
+            }
+        }
+
+        public final TextLink getTextLink() {
+            return mTextLink;
+        }
+
+        /** @hide */
+        @VisibleForTesting(visibility = Visibility.PRIVATE)
+        @Nullable
+        public final String getUrl() {
+            if (mTextLink.mUrlSpan != null) {
+                return mTextLink.mUrlSpan.getURL();
+            }
+            return null;
+        }
+    }
 
     /**
      * A builder to construct a TextLinks instance.
      */
     public static final class Builder {
         private final String mFullText;
-        private final Collection<TextLink> mLinks;
+        private final ArrayList<TextLink> mLinks;
 
         /**
          * Create a new TextLinks.Builder.
          *
-         * @param fullText The full text that links will be added to.
+         * @param fullText The full text to annotate with links
          */
         public Builder(@NonNull String fullText) {
             mFullText = Preconditions.checkNotNull(fullText);
@@ -347,19 +565,44 @@
         /**
          * Adds a TextLink.
          *
-         * @return this instance.
+         * @param start The start index of the identified subsequence
+         * @param end The end index of the identified subsequence
+         * @param entityScores A mapping of entity type to confidence score
+         *
+         * @throws IllegalArgumentException if entityScores is null or empty.
          */
-        public Builder addLink(TextLink link) {
-            Preconditions.checkNotNull(link);
-            mLinks.add(link);
+        @NonNull
+        public Builder addLink(int start, int end, Map<String, Float> entityScores) {
+            mLinks.add(new TextLink(start, end, entityScores, null));
+            return this;
+        }
+
+        /**
+         * @see #addLink(int, int, Map)
+         * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
+         */
+        @NonNull
+        Builder addLink(int start, int end, Map<String, Float> entityScores,
+                @Nullable URLSpan urlSpan) {
+            mLinks.add(new TextLink(start, end, entityScores, urlSpan));
+            return this;
+        }
+
+        /**
+         * Removes all {@link TextLink}s.
+         */
+        @NonNull
+        public Builder clearTextLinks() {
+            mLinks.clear();
             return this;
         }
 
         /**
          * Constructs a TextLinks instance.
          *
-         * @return the constructed TextLinks.
+         * @return the constructed TextLinks
          */
+        @NonNull
         public TextLinks build() {
             return new TextLinks(mFullText, mLinks);
         }
diff --git a/android/view/textclassifier/TextLinksParams.java b/android/view/textclassifier/TextLinksParams.java
new file mode 100644
index 0000000..be4c3bc
--- /dev/null
+++ b/android/view/textclassifier/TextLinksParams.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.Spannable;
+import android.text.style.ClickableSpan;
+import android.text.util.Linkify;
+import android.text.util.Linkify.LinkifyMask;
+import android.view.textclassifier.TextLinks.TextLink;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Parameters for generating and applying links.
+ * @hide
+ */
+public final class TextLinksParams {
+
+    /**
+     * A function to create spans from TextLinks.
+     */
+    private static final Function<TextLink, TextLinkSpan> DEFAULT_SPAN_FACTORY =
+            textLink -> new TextLinkSpan(textLink);
+
+    @TextLinks.ApplyStrategy
+    private final int mApplyStrategy;
+    private final Function<TextLink, TextLinkSpan> mSpanFactory;
+    private final TextClassifier.EntityConfig mEntityConfig;
+
+    private TextLinksParams(
+            @TextLinks.ApplyStrategy int applyStrategy,
+            Function<TextLink, TextLinkSpan> spanFactory) {
+        mApplyStrategy = applyStrategy;
+        mSpanFactory = spanFactory;
+        mEntityConfig = TextClassifier.EntityConfig.createWithHints(null);
+    }
+
+    /**
+     * Returns a new TextLinksParams object based on the specified link mask.
+     *
+     * @param mask the link mask
+     *      e.g. {@link LinkifyMask#PHONE_NUMBERS} | {@link LinkifyMask#EMAIL_ADDRESSES}
+     * @hide
+     */
+    @NonNull
+    public static TextLinksParams fromLinkMask(@LinkifyMask int mask) {
+        final List<String> entitiesToFind = new ArrayList<>();
+        if ((mask & Linkify.WEB_URLS) != 0) {
+            entitiesToFind.add(TextClassifier.TYPE_URL);
+        }
+        if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
+            entitiesToFind.add(TextClassifier.TYPE_EMAIL);
+        }
+        if ((mask & Linkify.PHONE_NUMBERS) != 0) {
+            entitiesToFind.add(TextClassifier.TYPE_PHONE);
+        }
+        if ((mask & Linkify.MAP_ADDRESSES) != 0) {
+            entitiesToFind.add(TextClassifier.TYPE_ADDRESS);
+        }
+        return new TextLinksParams.Builder().setEntityConfig(
+                TextClassifier.EntityConfig.createWithExplicitEntityList(entitiesToFind))
+                .build();
+    }
+
+    /**
+     * Returns the entity config used to determine what entity types to generate.
+     */
+    @NonNull
+    public TextClassifier.EntityConfig getEntityConfig() {
+        return mEntityConfig;
+    }
+
+    /**
+     * Annotates the given text with the generated links. It will fail if the provided text doesn't
+     * match the original text used to crete the TextLinks.
+     *
+     * @param text the text to apply the links to. Must match the original text
+     * @param textLinks the links to apply to the text
+     *
+     * @return a status code indicating whether or not the links were successfully applied
+     * @hide
+     */
+    @TextLinks.Status
+    public int apply(@NonNull Spannable text, @NonNull TextLinks textLinks) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(textLinks);
+
+        final String textString = text.toString();
+        if (!textString.startsWith(textLinks.getText())) {
+            return TextLinks.STATUS_DIFFERENT_TEXT;
+        }
+        if (textLinks.getLinks().isEmpty()) {
+            return TextLinks.STATUS_NO_LINKS_FOUND;
+        }
+
+        int applyCount = 0;
+        for (TextLink link : textLinks.getLinks()) {
+            final TextLinkSpan span = mSpanFactory.apply(link);
+            if (span != null) {
+                final ClickableSpan[] existingSpans = text.getSpans(
+                        link.getStart(), link.getEnd(), ClickableSpan.class);
+                if (existingSpans.length > 0) {
+                    if (mApplyStrategy == TextLinks.APPLY_STRATEGY_REPLACE) {
+                        for (ClickableSpan existingSpan : existingSpans) {
+                            text.removeSpan(existingSpan);
+                        }
+                        text.setSpan(span, link.getStart(), link.getEnd(),
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                        applyCount++;
+                    }
+                } else {
+                    text.setSpan(span, link.getStart(), link.getEnd(),
+                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    applyCount++;
+                }
+            }
+        }
+        if (applyCount == 0) {
+            return TextLinks.STATUS_NO_LINKS_APPLIED;
+        }
+        return TextLinks.STATUS_LINKS_APPLIED;
+    }
+
+    /**
+     * A builder for building TextLinksParams.
+     */
+    public static final class Builder {
+
+        @TextLinks.ApplyStrategy
+        private int mApplyStrategy = TextLinks.APPLY_STRATEGY_IGNORE;
+        private Function<TextLink, TextLinkSpan> mSpanFactory = DEFAULT_SPAN_FACTORY;
+
+        /**
+         * Sets the apply strategy used to determine how to apply links to text.
+         *      e.g {@link TextLinks#APPLY_STRATEGY_IGNORE}
+         *
+         * @return this builder
+         */
+        public Builder setApplyStrategy(@TextLinks.ApplyStrategy int applyStrategy) {
+            mApplyStrategy = checkApplyStrategy(applyStrategy);
+            return this;
+        }
+
+        /**
+         * Sets a custom span factory for converting TextLinks to TextLinkSpans.
+         * Set to {@code null} to use the default span factory.
+         *
+         * @return this builder
+         */
+        public Builder setSpanFactory(@Nullable Function<TextLink, TextLinkSpan> spanFactory) {
+            mSpanFactory = spanFactory == null ? DEFAULT_SPAN_FACTORY : spanFactory;
+            return this;
+        }
+
+        /**
+         * Sets the entity configuration used to determine what entity types to generate.
+         * Set to {@code null} for the default entity config which will automatically determine
+         * what links to generate.
+         *
+         * @return this builder
+         */
+        public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+            return this;
+        }
+
+        /**
+         * Builds and returns a TextLinksParams object.
+         */
+        public TextLinksParams build() {
+            return new TextLinksParams(mApplyStrategy, mSpanFactory);
+        }
+    }
+
+    /** @throws IllegalArgumentException if the value is invalid */
+    @TextLinks.ApplyStrategy
+    private static int checkApplyStrategy(int applyStrategy) {
+        if (applyStrategy != TextLinks.APPLY_STRATEGY_IGNORE
+                && applyStrategy != TextLinks.APPLY_STRATEGY_REPLACE) {
+            throw new IllegalArgumentException(
+                    "Invalid apply strategy. See TextLinksParams.ApplyStrategy for options.");
+        }
+        return applyStrategy;
+    }
+}
+
diff --git a/android/view/textclassifier/TextSelection.java b/android/view/textclassifier/TextSelection.java
index 774d42d..939e717 100644
--- a/android/view/textclassifier/TextSelection.java
+++ b/android/view/textclassifier/TextSelection.java
@@ -25,6 +25,7 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 import android.view.textclassifier.TextClassifier.EntityType;
+import android.view.textclassifier.TextClassifier.Utils;
 
 import com.android.internal.util.Preconditions;
 
@@ -34,20 +35,19 @@
 /**
  * Information about where text selection should be.
  */
-public final class TextSelection {
+public final class TextSelection implements Parcelable {
 
     private final int mStartIndex;
     private final int mEndIndex;
-    @NonNull private final EntityConfidence mEntityConfidence;
-    @NonNull private final String mSignature;
+    private final EntityConfidence mEntityConfidence;
+    @Nullable private final String mId;
 
     private TextSelection(
-            int startIndex, int endIndex, @NonNull Map<String, Float> entityConfidence,
-            @NonNull String signature) {
+            int startIndex, int endIndex, Map<String, Float> entityConfidence, String id) {
         mStartIndex = startIndex;
         mEndIndex = endIndex;
         mEntityConfidence = new EntityConfidence(entityConfidence);
-        mSignature = signature;
+        mId = id;
     }
 
     /**
@@ -80,7 +80,8 @@
      * @see #getEntityCount() for the number of entities available.
      */
     @NonNull
-    public @EntityType String getEntity(int index) {
+    @EntityType
+    public String getEntity(int index) {
         return mEntityConfidence.getEntities().get(index);
     }
 
@@ -95,37 +96,19 @@
     }
 
     /**
-     * Returns the signature for this object.
-     * The TextClassifier that generates this object may use it as a way to internally identify
-     * this object.
+     * Returns the id, if one exists, for this object.
      */
-    @NonNull
-    public String getSignature() {
-        return mSignature;
+    @Nullable
+    public String getId() {
+        return mId;
     }
 
     @Override
     public String toString() {
         return String.format(
                 Locale.US,
-                "TextSelection {startIndex=%d, endIndex=%d, entities=%s, signature=%s}",
-                mStartIndex, mEndIndex, mEntityConfidence, mSignature);
-    }
-
-    /** Helper for parceling via #ParcelableWrapper. */
-    private void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mStartIndex);
-        dest.writeInt(mEndIndex);
-        mEntityConfidence.writeToParcel(dest, flags);
-        dest.writeString(mSignature);
-    }
-
-    /** Helper for unparceling via #ParcelableWrapper. */
-    private TextSelection(Parcel in) {
-        mStartIndex = in.readInt();
-        mEndIndex = in.readInt();
-        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
-        mSignature = in.readString();
+                "TextSelection {id=%s, startIndex=%d, endIndex=%d, entities=%s}",
+                mId, mStartIndex, mEndIndex, mEntityConfidence);
     }
 
     /**
@@ -135,8 +118,8 @@
 
         private final int mStartIndex;
         private final int mEndIndex;
-        @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @NonNull private String mSignature = "";
+        private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
+        @Nullable private String mId;
 
         /**
          * Creates a builder used to build {@link TextSelection} objects.
@@ -158,78 +141,86 @@
          *      0 implies the entity does not exist for the classified text.
          *      Values greater than 1 are clamped to 1.
          */
+        @NonNull
         public Builder setEntityType(
                 @NonNull @EntityType String type,
                 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+            Preconditions.checkNotNull(type);
             mEntityConfidence.put(type, confidenceScore);
             return this;
         }
 
         /**
-         * Sets a signature for the TextSelection object.
-         *
-         * The TextClassifier that generates the TextSelection object may use it as a way to
-         * internally identify the TextSelection object.
+         * Sets an id for the TextSelection object.
          */
-        public Builder setSignature(@NonNull String signature) {
-            mSignature = Preconditions.checkNotNull(signature);
+        @NonNull
+        public Builder setId(@NonNull String id) {
+            mId = Preconditions.checkNotNull(id);
             return this;
         }
 
         /**
          * Builds and returns {@link TextSelection} object.
          */
+        @NonNull
         public TextSelection build() {
             return new TextSelection(
-                    mStartIndex, mEndIndex, mEntityConfidence, mSignature);
+                    mStartIndex, mEndIndex, mEntityConfidence, mId);
         }
     }
 
     /**
-     * Optional input parameters for generating TextSelection.
+     * A request object for generating TextSelection.
      */
-    public static final class Options implements Parcelable {
+    public static final class Request implements Parcelable {
 
-        private @Nullable LocaleList mDefaultLocales;
-        private boolean mDarkLaunchAllowed;
+        private final CharSequence mText;
+        private final int mStartIndex;
+        private final int mEndIndex;
+        @Nullable private final LocaleList mDefaultLocales;
+        private final boolean mDarkLaunchAllowed;
 
-        public Options() {}
-
-        /**
-         * @param defaultLocales ordered list of locale preferences that may be used to disambiguate
-         *      the provided text. If no locale preferences exist, set this to null or an empty
-         *      locale list.
-         */
-        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
+        private Request(
+                CharSequence text,
+                int startIndex,
+                int endIndex,
+                LocaleList defaultLocales,
+                boolean darkLaunchAllowed) {
+            mText = text;
+            mStartIndex = startIndex;
+            mEndIndex = endIndex;
             mDefaultLocales = defaultLocales;
-            return this;
+            mDarkLaunchAllowed = darkLaunchAllowed;
         }
 
         /**
-         * @return ordered list of locale preferences that can be used to disambiguate
-         *      the provided text.
+         * Returns the text providing context for the selected text (which is specified by the
+         * sub sequence starting at startIndex and ending at endIndex).
          */
-        @Nullable
-        public LocaleList getDefaultLocales() {
-            return mDefaultLocales;
+        @NonNull
+        public CharSequence getText() {
+            return mText;
         }
 
         /**
-         * @param allowed whether or not the TextClassifier should return selection suggestions
-         *      when "dark launched". When a TextClassifier is dark launched, it can suggest
-         *      selection changes that should not be used to actually change the user's selection.
-         *      Instead, the suggested selection is logged, compared with the user's selection
-         *      interaction, and used to generate quality metrics for the TextClassifier.
-         *
-         * @hide
+         * Returns start index of the selected part of text.
          */
-        public void setDarkLaunchAllowed(boolean allowed) {
-            mDarkLaunchAllowed = allowed;
+        @IntRange(from = 0)
+        public int getStartIndex() {
+            return mStartIndex;
         }
 
         /**
-         * Returns true if the TextClassifier should return selection suggestions when
-         * "dark launched". Otherwise, returns false.
+         * Returns end index of the selected part of text.
+         */
+        @IntRange(from = 0)
+        public int getEndIndex() {
+            return mEndIndex;
+        }
+
+        /**
+         * Returns true if the TextClassifier should return selection suggestions when "dark
+         * launched". Otherwise, returns false.
          *
          * @hide
          */
@@ -237,6 +228,83 @@
             return mDarkLaunchAllowed;
         }
 
+        /**
+         * @return ordered list of locale preferences that can be used to disambiguate the
+         * provided text.
+         */
+        @Nullable
+        public LocaleList getDefaultLocales() {
+            return mDefaultLocales;
+        }
+
+        /**
+         * A builder for building TextSelection requests.
+         */
+        public static final class Builder {
+
+            private final CharSequence mText;
+            private final int mStartIndex;
+            private final int mEndIndex;
+
+            @Nullable private LocaleList mDefaultLocales;
+            private boolean mDarkLaunchAllowed;
+
+            /**
+             * @param text text providing context for the selected text (which is specified by the
+             *      sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
+             * @param startIndex start index of the selected part of text
+             * @param endIndex end index of the selected part of text
+             */
+            public Builder(
+                    @NonNull CharSequence text,
+                    @IntRange(from = 0) int startIndex,
+                    @IntRange(from = 0) int endIndex) {
+                Utils.checkArgument(text, startIndex, endIndex);
+                mText = text;
+                mStartIndex = startIndex;
+                mEndIndex = endIndex;
+            }
+
+            /**
+             * @param defaultLocales ordered list of locale preferences that may be used to
+             *      disambiguate the provided text. If no locale preferences exist, set this to null
+             *      or an empty locale list.
+             *
+             * @return this builder.
+             */
+            @NonNull
+            public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
+                mDefaultLocales = defaultLocales;
+                return this;
+            }
+
+            /**
+             * @param allowed whether or not the TextClassifier should return selection suggestions
+             *      when "dark launched". When a TextClassifier is dark launched, it can suggest
+             *      selection changes that should not be used to actually change the user's
+             *      selection. Instead, the suggested selection is logged, compared with the user's
+             *      selection interaction, and used to generate quality metrics for the
+             *      TextClassifier. Not parceled.
+             *
+             * @return this builder.
+             * @hide
+             */
+            @NonNull
+            public Builder setDarkLaunchAllowed(boolean allowed) {
+                mDarkLaunchAllowed = allowed;
+                return this;
+            }
+
+            /**
+             * Builds and returns the request object.
+             */
+            @NonNull
+            public Request build() {
+                return new Request(mText, mStartIndex, mEndIndex,
+                        mDefaultLocales, mDarkLaunchAllowed);
+            }
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -244,74 +312,67 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mText.toString());
+            dest.writeInt(mStartIndex);
+            dest.writeInt(mEndIndex);
             dest.writeInt(mDefaultLocales != null ? 1 : 0);
             if (mDefaultLocales != null) {
                 mDefaultLocales.writeToParcel(dest, flags);
             }
-            dest.writeInt(mDarkLaunchAllowed ? 1 : 0);
         }
 
-        public static final Parcelable.Creator<Options> CREATOR =
-                new Parcelable.Creator<Options>() {
+        public static final Parcelable.Creator<Request> CREATOR =
+                new Parcelable.Creator<Request>() {
                     @Override
-                    public Options createFromParcel(Parcel in) {
-                        return new Options(in);
+                    public Request createFromParcel(Parcel in) {
+                        return new Request(in);
                     }
 
                     @Override
-                    public Options[] newArray(int size) {
-                        return new Options[size];
+                    public Request[] newArray(int size) {
+                        return new Request[size];
                     }
                 };
 
-        private Options(Parcel in) {
-            if (in.readInt() > 0) {
-                mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
-            }
-            mDarkLaunchAllowed = in.readInt() != 0;
+        private Request(Parcel in) {
+            mText = in.readString();
+            mStartIndex = in.readInt();
+            mEndIndex = in.readInt();
+            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
+            mDarkLaunchAllowed = false;
         }
     }
 
-    /**
-     * Parcelable wrapper for TextSelection objects.
-     * @hide
-     */
-    public static final class ParcelableWrapper implements Parcelable {
+    @Override
+    public int describeContents() {
+        return 0;
+    }
 
-        @NonNull private TextSelection mTextSelection;
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mStartIndex);
+        dest.writeInt(mEndIndex);
+        mEntityConfidence.writeToParcel(dest, flags);
+        dest.writeString(mId);
+    }
 
-        public ParcelableWrapper(@NonNull TextSelection textSelection) {
-            Preconditions.checkNotNull(textSelection);
-            mTextSelection = textSelection;
-        }
+    public static final Parcelable.Creator<TextSelection> CREATOR =
+            new Parcelable.Creator<TextSelection>() {
+                @Override
+                public TextSelection createFromParcel(Parcel in) {
+                    return new TextSelection(in);
+                }
 
-        @NonNull
-        public TextSelection getTextSelection() {
-            return mTextSelection;
-        }
+                @Override
+                public TextSelection[] newArray(int size) {
+                    return new TextSelection[size];
+                }
+            };
 
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            mTextSelection.writeToParcel(dest, flags);
-        }
-
-        public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
-                new Parcelable.Creator<ParcelableWrapper>() {
-                    @Override
-                    public ParcelableWrapper createFromParcel(Parcel in) {
-                        return new ParcelableWrapper(new TextSelection(in));
-                    }
-
-                    @Override
-                    public ParcelableWrapper[] newArray(int size) {
-                        return new ParcelableWrapper[size];
-                    }
-                };
-
+    private TextSelection(Parcel in) {
+        mStartIndex = in.readInt();
+        mEndIndex = in.readInt();
+        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+        mId = in.readString();
     }
 }
diff --git a/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 157b3d8..f7d75cd 100644
--- a/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -473,7 +473,7 @@
             final String entityType = classification.getEntityCount() > 0
                     ? classification.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = getVersionInfo(classification.getSignature());
+            final String versionTag = getVersionInfo(classification.getId());
             return new SelectionEvent(
                     start, end, EventType.SELECTION_MODIFIED, entityType, versionTag);
         }
@@ -489,7 +489,7 @@
          */
         public static SelectionEvent selectionModified(
                 int start, int end, @NonNull TextSelection selection) {
-            final boolean smartSelection = getSourceClassifier(selection.getSignature())
+            final boolean smartSelection = getSourceClassifier(selection.getId())
                     .equals(TextClassifier.DEFAULT_LOG_TAG);
             final int eventType;
             if (smartSelection) {
@@ -503,7 +503,7 @@
             final String entityType = selection.getEntityCount() > 0
                     ? selection.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = getVersionInfo(selection.getSignature());
+            final String versionTag = getVersionInfo(selection.getId());
             return new SelectionEvent(start, end, eventType, entityType, versionTag);
         }
 
@@ -538,7 +538,7 @@
             final String entityType = classification.getEntityCount() > 0
                     ? classification.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = getVersionInfo(classification.getSignature());
+            final String versionTag = getVersionInfo(classification.getId());
             return new SelectionEvent(start, end, actionType, entityType, versionTag);
         }
 
diff --git a/android/view/textservice/SpellCheckerSession.java b/android/view/textservice/SpellCheckerSession.java
index 779eefb..886f5c8 100644
--- a/android/view/textservice/SpellCheckerSession.java
+++ b/android/view/textservice/SpellCheckerSession.java
@@ -445,9 +445,15 @@
         private void processOrEnqueueTask(SpellCheckerParams scp) {
             ISpellCheckerSession session;
             synchronized (this) {
+                if (scp.mWhat == TASK_CLOSE && (mState == STATE_CLOSED_AFTER_CONNECTION
+                        || mState == STATE_CLOSED_BEFORE_CONNECTION)) {
+                    // It is OK to call SpellCheckerSession#close() multiple times.
+                    // Don't output confusing/misleading warning messages.
+                    return;
+                }
                 if (mState != STATE_WAIT_CONNECTION && mState != STATE_CONNECTED) {
                     Log.e(TAG, "ignoring processOrEnqueueTask due to unexpected mState="
-                            + taskToString(scp.mWhat)
+                            + stateToString(mState)
                             + " scp.mWhat=" + taskToString(scp.mWhat));
                     return;
                 }
diff --git a/android/webkit/SafeBrowsingResponse.java b/android/webkit/SafeBrowsingResponse.java
index 1d3a617..960b56b 100644
--- a/android/webkit/SafeBrowsingResponse.java
+++ b/android/webkit/SafeBrowsingResponse.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,36 +16,5 @@
 
 package android.webkit;
 
-/**
- * Used to indicate an action to take when hitting a malicious URL. Instances of this class are
- * created by the WebView and passed to {@link android.webkit.WebViewClient#onSafeBrowsingHit}. The
- * host application must call {@link #showInterstitial(boolean)}, {@link #proceed(boolean)}, or
- * {@link #backToSafety(boolean)} to set the WebView's response to the Safe Browsing hit.
- *
- * <p>
- * If reporting is enabled, all reports will be sent according to the privacy policy referenced by
- * {@link android.webkit.WebView#getSafeBrowsingPrivacyPolicyUrl()}.
- */
-public abstract class SafeBrowsingResponse {
-
-    /**
-     * Display the default interstitial.
-     *
-     * @param allowReporting {@code true} if the interstitial should show a reporting checkbox.
-     */
-    public abstract void showInterstitial(boolean allowReporting);
-
-    /**
-     * Act as if the user clicked "visit this unsafe site."
-     *
-     * @param report {@code true} to enable Safe Browsing reporting.
-     */
-    public abstract void proceed(boolean report);
-
-    /**
-     * Act as if the user clicked "back to safety."
-     *
-     * @param report {@code true} to enable Safe Browsing reporting.
-     */
-    public abstract void backToSafety(boolean report);
+public class SafeBrowsingResponse {
 }
diff --git a/android/webkit/TracingConfig.java b/android/webkit/TracingConfig.java
index 75e2bf7..d95ca61 100644
--- a/android/webkit/TracingConfig.java
+++ b/android/webkit/TracingConfig.java
@@ -21,175 +21,123 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 /**
  * Holds tracing configuration information and predefined settings.
  */
 public class TracingConfig {
 
-    private final String mCustomCategoryPattern;
-    private final @PresetCategories int mPresetCategories;
+    private @PredefinedCategories int mPredefinedCategories;
+    private final List<String> mCustomIncludedCategories = new ArrayList<String>();
     private @TracingMode int mTracingMode;
 
     /** @hide */
-    @IntDef({CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY,
-            CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
+    @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_ALL, CATEGORIES_ANDROID_WEBVIEW,
+            CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING,
+            CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface PresetCategories {}
+    public @interface PredefinedCategories {}
 
     /**
-     * Indicates that there are no preset categories.
+     * Indicates that there are no predefined categories.
      */
-    public static final int CATEGORIES_NONE = -1;
+    public static final int CATEGORIES_NONE = 0;
 
     /**
-     * Predefined categories typically useful for web developers.
+     * Predefined set of categories, includes all categories enabled by default in chromium.
+     * Use with caution: this setting may produce large trace output.
+     */
+    public static final int CATEGORIES_ALL = 1 << 0;
+
+    /**
+     * Predefined set of categories typically useful for analyzing WebViews.
+     * Typically includes android_webview and Java.
+     */
+    public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1;
+
+    /**
+     * Predefined set of categories typically useful for web developers.
      * Typically includes blink, compositor, renderer.scheduler and v8 categories.
      */
-    public static final int CATEGORIES_WEB_DEVELOPER = 0;
+    public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2;
 
     /**
-     * Predefined categories for analyzing input latency issues.
+     * Predefined set of categories for analyzing input latency issues.
      * Typically includes input, renderer.scheduler categories.
      */
-    public static final int CATEGORIES_INPUT_LATENCY = 1;
+    public static final int CATEGORIES_INPUT_LATENCY = 1 << 3;
 
     /**
-     * Predefined categories for analyzing rendering issues.
+     * Predefined set of categories for analyzing rendering issues.
      * Typically includes blink, compositor and gpu categories.
      */
-    public static final int CATEGORIES_RENDERING = 2;
+    public static final int CATEGORIES_RENDERING = 1 << 4;
 
     /**
-     * Predefined categories for analyzing javascript and rendering issues.
-     * Typically includes blink, compositor, gpu, renderer.schduler and v8 categories.
+     * Predefined set of categories for analyzing javascript and rendering issues.
+     * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories.
      */
-    public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3;
+    public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5;
 
     /**
-     * Predefined categories for studying difficult rendering performance problems.
+     * Predefined set of categories for studying difficult rendering performance problems.
      * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
      * some other compositor categories which are disabled by default.
      */
-    public static final int CATEGORIES_FRAME_VIEWER = 4;
+    public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
 
     /** @hide */
-    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER,
-            RECORD_TO_CONSOLE})
+    @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TracingMode {}
 
     /**
-     * Record trace events until the internal tracing buffer is full. Default tracing mode.
-     * Typically the buffer memory usage is between {@link #RECORD_CONTINUOUSLY} and the
-     * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. Depending on the implementation typically allows
-     * up to 256k events to be stored.
+     * Record trace events until the internal tracing buffer is full.
+     *
+     * Typically the buffer memory usage is larger than {@link #RECORD_CONTINUOUSLY}.
+     * Depending on the implementation typically allows up to 256k events to be stored.
      */
     public static final int RECORD_UNTIL_FULL = 0;
 
     /**
-     * Record trace events continuously using an internal ring buffer. Overwrites
-     * old events if they exceed buffer capacity. Uses less memory than both
-     * {@link #RECORD_UNTIL_FULL} and {@link #RECORD_UNTIL_FULL_LARGE_BUFFER} modes.
-     * Depending on the implementation typically allows up to 64k events to be stored.
+     * Record trace events continuously using an internal ring buffer. Default tracing mode.
+     *
+     * Overwrites old events if they exceed buffer capacity. Uses less memory than the
+     * {@link #RECORD_UNTIL_FULL} mode. Depending on the implementation typically allows
+     * up to 64k events to be stored.
      */
     public static final int RECORD_CONTINUOUSLY = 1;
 
     /**
-     * Record trace events using a larger internal tracing buffer until it is full.
-     * Uses more memory than the other modes and may not be suitable on devices
-     * with smaller RAM. Depending on the implementation typically allows up to
-     * 512 million events to be stored.
+     * @hide
      */
-    public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2;
-
-    /**
-     * Record trace events to console (logcat). The events are discarded and nothing
-     * is sent back to the caller. Uses the least memory as compared to the other modes.
-     */
-    public static final int RECORD_TO_CONSOLE = 3;
-
-    /**
-     * Create config with the preset categories.
-     * <p>
-     * Example:
-     *    TracingConfig(CATEGORIES_WEB_DEVELOPER) -- records trace events from the "web developer"
-     *                                               preset categories.
-     *
-     * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
-     *                    {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
-     *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
-     *                    {@link #CATEGORIES_FRAME_VIEWER}.
-     *
-     * Note: for specifying custom categories without presets use
-     * {@link #TracingConfig(int, String, int)}.
-     *
-     */
-    public TracingConfig(@PresetCategories int presetCategories) {
-        this(presetCategories, "", RECORD_UNTIL_FULL);
+    public TracingConfig(@PredefinedCategories int predefinedCategories,
+            @NonNull List<String> customIncludedCategories,
+            @TracingMode int tracingMode) {
+        mPredefinedCategories = predefinedCategories;
+        mCustomIncludedCategories.addAll(customIncludedCategories);
+        mTracingMode = tracingMode;
     }
 
     /**
-     * Create a configuration with both preset categories and custom categories.
-     * Also allows to specify the tracing mode.
-     *
-     * Note that the categories are defined by the currently-in-use version of WebView. They live
-     * in chromium code and are not part of the Android API. See
-     * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
-     * chromium documentation on tracing</a> for more details.
-     *
-     * <p>
-     * Examples:
-     *
-     *  Preset category with a specified trace mode:
-     *    TracingConfig(CATEGORIES_WEB_DEVELOPER, "", RECORD_UNTIL_FULL_LARGE_BUFFER);
-     *  Custom categories:
-     *    TracingConfig(CATEGORIES_NONE, "browser", RECORD_UNTIL_FULL)
-     *      -- records only the trace events from the "browser" category.
-     *    TraceConfig(CATEGORIES_NONE, "-input,-gpu", RECORD_UNTIL_FULL)
-     *      -- records all trace events excluding the events from the "input" and 'gpu' categories.
-     *    TracingConfig(CATEGORIES_NONE, "blink*,devtools*", RECORD_UNTIL_FULL)
-     *      -- records only the trace events matching the "blink*" and "devtools*" patterns
-     *         (e.g. "blink_gc" and "devtools.timeline" categories).
-     *
-     *  Combination of preset and additional custom categories:
-     *    TracingConfig(CATEGORIES_WEB_DEVELOPER, "memory-infra", RECORD_CONTINUOUSLY)
-     *      -- records events from the "web developer" categories and events from the "memory-infra"
-     *         category to understand where memory is being used.
-     *
-     * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
-     *                    {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
-     *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
-     *                    {@link #CATEGORIES_FRAME_VIEWER}.
-     * @param customCategories a comma-delimited list of category wildcards. A category can
-     *                         have an optional '-' prefix to make it an excluded category.
-     * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
-     *                    {@link #RECORD_CONTINUOUSLY}, {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}
-     *                    or {@link #RECORD_TO_CONSOLE}.
+     * Returns a bitmask of the predefined categories values of this configuration.
      */
-    public TracingConfig(@PresetCategories int presetCategories,
-            @NonNull String customCategories, @TracingMode int tracingMode) {
-        mPresetCategories = presetCategories;
-        mCustomCategoryPattern = customCategories;
-        mTracingMode = RECORD_UNTIL_FULL;
+    @PredefinedCategories
+    public int getPredefinedCategories() {
+        return mPredefinedCategories;
     }
 
     /**
-     * Returns the custom category pattern for this configuration.
+     * Returns the list of included custom category patterns for this configuration.
      *
-     * @return empty string if no custom category pattern is specified.
+     * @return empty list if no custom category patterns are specified.
      */
     @NonNull
-    public String getCustomCategoryPattern() {
-        return mCustomCategoryPattern;
-    }
-
-    /**
-     * Returns the preset categories value of this configuration.
-     */
-    @PresetCategories
-    public int getPresetCategories() {
-        return mPresetCategories;
+    public List<String> getCustomIncludedCategories() {
+        return mCustomIncludedCategories;
     }
 
     /**
@@ -200,4 +148,112 @@
         return mTracingMode;
     }
 
+    /**
+     * Builder used to create {@link TracingConfig} objects.
+     *
+     * Examples:
+     *   new TracingConfig.Builder().build()
+     *       -- creates a configuration with default options: {@link #CATEGORIES_NONE},
+     *          {@link #RECORD_UNTIL_FULL}.
+     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()
+     *       -- records trace events from the "web developer" predefined category sets.
+     *   new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
+     *                                             CATEGORIES_INPUT_LATENCY).build()
+     *       -- records trace events from the "rendering" and "input latency" predefined
+     *          category sets.
+     *   new TracingConfig.Builder().addCategories("browser").build()
+     *       -- records only the trace events from the "browser" category.
+     *   new TracingConfig.Builder().addCategories("blink*","renderer*").build()
+     *       -- records only the trace events matching the "blink*" and "renderer*" patterns
+     *          (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
+     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
+     *                              .addCategories("disabled-by-default-v8.gc")
+     *                              .setTracingMode(RECORD_CONTINUOUSLY).build()
+     *       -- records events from the "web developer" predefined category set and events from
+     *          the "disabled-by-default-v8.gc" category to understand where garbage collection
+     *          is being triggered. Uses a ring buffer for internal storage during tracing.
+     */
+    public static class Builder {
+        private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
+        private final List<String> mCustomIncludedCategories = new ArrayList<String>();
+        private @TracingMode int mTracingMode = RECORD_CONTINUOUSLY;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Build {@link TracingConfig} using the current settings.
+         */
+        public TracingConfig build() {
+            return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories,
+                    mTracingMode);
+        }
+
+        /**
+         * Adds categories from a predefined set of categories to be included in the trace output.
+         *
+         * @param predefinedCategories list or bitmask of predefined category sets to use:
+         *                    {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
+         *                    {@link #CATEGORIES_ANDROID_WEBVIEW},
+         *                    {@link #CATEGORIES_WEB_DEVELOPER},
+         *                    {@link #CATEGORIES_INPUT_LATENCY},
+         *                    {@link #CATEGORIES_RENDERING},
+         *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+         *                    {@link #CATEGORIES_FRAME_VIEWER}.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addCategories(@PredefinedCategories int... predefinedCategories) {
+            for (int categorySet : predefinedCategories) {
+                mPredefinedCategories |= categorySet;
+            }
+            return this;
+        }
+
+        /**
+         * Adds custom categories to be included in trace output.
+         *
+         * Note that the categories are defined by the currently-in-use version of WebView. They
+         * live in chromium code and are not part of the Android API. See
+         * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
+         * chromium documentation on tracing</a> for more details.
+         *
+         * @param categories a list of category patterns. A category pattern can contain wilcards,
+         *        e.g. "blink*" or full category name e.g. "renderer.scheduler".
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addCategories(String... categories) {
+            for (String category: categories) {
+                mCustomIncludedCategories.add(category);
+            }
+            return this;
+        }
+
+        /**
+         * Adds custom categories to be included in trace output.
+         *
+         * Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter.
+         *
+         * @param categories a list of category patters.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addCategories(Collection<String> categories) {
+            mCustomIncludedCategories.addAll(categories);
+            return this;
+        }
+
+        /**
+         * Sets the tracing mode for this configuration.
+         *
+         * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
+         *                    {@link #RECORD_CONTINUOUSLY}.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder setTracingMode(@TracingMode int tracingMode) {
+            mTracingMode = tracingMode;
+            return this;
+        }
+    }
+
 }
diff --git a/android/webkit/TracingController.java b/android/webkit/TracingController.java
index cadb8a1..50068f5 100644
--- a/android/webkit/TracingController.java
+++ b/android/webkit/TracingController.java
@@ -16,9 +16,12 @@
 
 package android.webkit;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Handler;
+
+import java.io.OutputStream;
+import java.util.concurrent.Executor;
 
 /**
  * Manages tracing of WebViews. In particular provides functionality for the app
@@ -29,40 +32,22 @@
  * The resulting trace data is sent back as a byte sequence in json format. This
  * file can be loaded in "chrome://tracing" for further analysis.
  * <p>
- * Note: All methods in this class must be called on the UI thread. All callbacks
- * are also called on the UI thread.
- * <p>
  * Example usage:
  * <pre class="prettyprint">
  * TracingController tracingController = TracingController.getInstance();
- * tracingController.start(new TraceConfig(CATEGORIES_WEB_DEVELOPER));
+ * tracingController.start(new TraceConfig.Builder()
+ *                  .addCategories(CATEGORIES_WEB_DEVELOPER).build());
  * [..]
- * tracingController.stopAndFlush(new TraceFileOutput("trace.json"), null);
+ * tracingController.stop(new FileOutputStream("trace.json"),
+ *                        Executors.newSingleThreadExecutor());
  * </pre></p>
  */
 public abstract class TracingController {
 
     /**
-     * Interface for capturing tracing data.
-     */
-    public interface TracingOutputStream {
-        /**
-         * Will be called to return tracing data in chunks.
-         * Tracing data is returned in json format an array of bytes.
-         */
-        void write(byte[] chunk);
-
-        /**
-         * Called when tracing is finished and the data collection is over.
-         * There will be no calls to #write after #complete is called.
-         */
-        void complete();
-    }
-
-    /**
      * Returns the default TracingController instance. At present there is
      * only one TracingController instance for all WebView instances,
-     * however this restriction may be relaxed in the future.
+     * however this restriction may be relaxed in a future Android release.
      *
      * @return the default TracingController instance
      */
@@ -72,55 +57,37 @@
     }
 
     /**
-     * Starts tracing all webviews. Depeding on the trace mode in traceConfig
+     * Starts tracing all webviews. Depending on the trace mode in traceConfig
      * specifies how the trace events are recorded.
      *
-     * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL},
-     * {@link TracingConfig#RECORD_CONTINUOUSLY} and
-     * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded
+     * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL} and
+     * {@link TracingConfig#RECORD_CONTINUOUSLY} the events are recorded
      * using an internal buffer and flushed to the outputStream when
-     * {@link #stopAndFlush(TracingOutputStream, Handler)} is called.
+     * {@link #stop(OutputStream, Executor)} is called.
      *
      * @param tracingConfig configuration options to use for tracing
-     * @return false if the system is already tracing, true otherwise.
+     * @throws IllegalStateException if the system is already tracing.
      */
-    public abstract boolean start(TracingConfig tracingConfig);
+    public abstract void start(@NonNull TracingConfig tracingConfig);
 
     /**
-     * Stops tracing and discards all tracing data.
+     * Stops tracing and flushes tracing data to the specified outputStream.
      *
-     * This method is particularly useful in conjunction with the
-     * {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode because tracing data is logged to
-     * console and not sent to an outputStream as with
-     * {@link #stopAndFlush(TracingOutputStream, Handler)}.
+     * The data is sent to the specified output stream in json format typically
+     * in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion
+     * the {@link java.io.OutputStream#close()} method is called.
      *
+     * @param outputStream the output steam the tracing data will be sent to. If null
+     *                     the tracing data will be discarded.
+     * @param executor the {@link java.util.concurrent.Executor} on which the
+     *        outputStream #write and #close methods will be invoked.
      * @return false if the system was not tracing at the time of the call, true
      *         otherwise.
      */
-    public abstract boolean stop();
-
-    /**
-     * Stops tracing and flushes tracing data to the specifid outputStream.
-     *
-     * Note that if the {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode is used
-     * nothing will be sent to the outputStream and no TracingOuputStream methods will be
-     * called. In that case it is more convenient to just use {@link #stop()} instead.
-     *
-     * @param outputStream the output steam the tracing data will be sent to.
-     * @param handler the {@link android.os.Handler} on which the outputStream callbacks
-     *                will be invoked. If the handler is null the current thread's Looper
-     *                will be used.
-     * @return false if the system was not tracing at the time of the call, true
-     *         otherwise.
-     */
-    public abstract boolean stopAndFlush(TracingOutputStream outputStream,
-            @Nullable Handler handler);
+    public abstract boolean stop(@Nullable OutputStream outputStream,
+            @NonNull @CallbackExecutor Executor executor);
 
     /** True if the system is tracing */
     public abstract boolean isTracing();
 
-    // TODO: consider adding getTraceBufferUsage, percentage and approx event count.
-    // TODO: consider adding String getCategories(), for obtaining the actual list
-    // of categories used (given that presets are ints).
-
 }
diff --git a/android/webkit/TracingFileOutputStream.java b/android/webkit/TracingFileOutputStream.java
deleted file mode 100644
index 8a5fa36..0000000
--- a/android/webkit/TracingFileOutputStream.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.annotation.NonNull;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Simple TracingOutputStream implementation which writes the trace data from
- * {@link TracingController} to a new file.
- *
- */
-public class TracingFileOutputStream implements TracingController.TracingOutputStream {
-
-    private FileOutputStream mFileOutput;
-
-    public TracingFileOutputStream(@NonNull String filename) throws FileNotFoundException {
-        mFileOutput = new FileOutputStream(filename);
-    }
-
-    /**
-     * Writes bytes chunk to the file.
-     */
-    public void write(byte[] chunk) {
-        try {
-            mFileOutput.write(chunk);
-        } catch (IOException e) {
-            onIOException(e);
-        }
-    }
-
-    /**
-     * Closes the file.
-     */
-    public void complete() {
-        try {
-            mFileOutput.close();
-        } catch (IOException e) {
-            onIOException(e);
-        }
-    }
-
-    private void onIOException(IOException e) {
-        throw new RuntimeException(e);
-    }
-}
diff --git a/android/webkit/URLUtil.java b/android/webkit/URLUtil.java
index 84c000a..ed122a6 100644
--- a/android/webkit/URLUtil.java
+++ b/android/webkit/URLUtil.java
@@ -39,7 +39,7 @@
     // "file:///android_res/drawable/bar.png". Use "drawable" to refer to
     // "drawable-hdpi" directory as well.
     static final String RESOURCE_BASE = "file:///android_res/";
-    static final String FILE_BASE = "file://";
+    static final String FILE_BASE = "file:";
     static final String PROXY_BASE = "file:///cookieless_proxy/";
     static final String CONTENT_BASE = "content:";
 
diff --git a/android/webkit/WebSettings.java b/android/webkit/WebSettings.java
index 90cc481..cba11a8 100644
--- a/android/webkit/WebSettings.java
+++ b/android/webkit/WebSettings.java
@@ -931,12 +931,12 @@
      * Note that this setting affects only JavaScript access to file scheme
      * resources. Other access to such resources, for example, from image HTML
      * elements, is unaffected. To prevent possible violation of same domain policy
-     * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
-     * devices, you should explicitly set this value to {@code false}.
+     * when targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier,
+     * you should explicitly set this value to {@code false}.
      * <p>
-     * The default value is {@code true} for API level
+     * The default value is {@code true} for apps targeting
      * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
-     * and {@code false} for API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+     * and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
      * and above.
      *
      * @param flag whether JavaScript running in the context of a file scheme
@@ -947,18 +947,18 @@
     /**
      * Sets whether JavaScript running in the context of a file scheme URL
      * should be allowed to access content from other file scheme URLs. To
-     * enable the most restrictive, and therefore secure policy, this setting
+     * enable the most restrictive, and therefore secure, policy this setting
      * should be disabled. Note that the value of this setting is ignored if
      * the value of {@link #getAllowUniversalAccessFromFileURLs} is {@code true}.
      * Note too, that this setting affects only JavaScript access to file scheme
      * resources. Other access to such resources, for example, from image HTML
      * elements, is unaffected. To prevent possible violation of same domain policy
-     * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
-     * devices, you should explicitly set this value to {@code false}.
+     * when targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier,
+     * you should explicitly set this value to {@code false}.
      * <p>
-     * The default value is {@code true} for API level
+     * The default value is {@code true} for apps targeting
      * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
-     * and {@code false} for API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+     * and {@code false} when targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
      * and above.
      *
      * @param flag whether JavaScript running in the context of a file scheme
@@ -1394,26 +1394,26 @@
 
 
     /**
-     * Sets whether Safe Browsing is enabled. Safe browsing allows WebView to
+     * Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to
      * protect against malware and phishing attacks by verifying the links.
      *
      * <p>
-     * Safe browsing is disabled by default. The recommended way to enable Safe browsing is using a
-     * manifest tag to change the default value to enabled for all WebViews (read <a
-     * href="{@docRoot}reference/android/webkit/WebView.html">general Safe Browsing info</a>).
+     * Safe Browsing can be disabled for all WebViews using a manifest tag (read <a
+     * href="{@docRoot}reference/android/webkit/WebView.html">general Safe Browsing info</a>). The
+     * manifest tag has a lower precedence than this API.
      *
      * <p>
-     * This API overrides the manifest tag value for this WebView.
+     * Safe Browsing is enabled by default for devices which support it.
      *
-     * @param enabled Whether Safe browsing is enabled.
+     * @param enabled Whether Safe Browsing is enabled.
      */
     public abstract void setSafeBrowsingEnabled(boolean enabled);
 
     /**
-     * Gets whether Safe browsing is enabled.
+     * Gets whether Safe Browsing is enabled.
      * See {@link #setSafeBrowsingEnabled}.
      *
-     * @return {@code true} if Safe browsing is enabled and {@code false} otherwise.
+     * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise.
      */
     public abstract boolean getSafeBrowsingEnabled();
 
diff --git a/android/webkit/WebViewClient.java b/android/webkit/WebViewClient.java
index d0f9eee..f5d220c 100644
--- a/android/webkit/WebViewClient.java
+++ b/android/webkit/WebViewClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -13,545 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.webkit;
 
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.net.http.SslError;
-import android.os.Message;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.ViewRootImpl;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 public class WebViewClient {
-
-    /**
-     * Give the host application a chance to take over the control when a new
-     * url is about to be loaded in the current WebView. If WebViewClient is not
-     * provided, by default WebView will ask Activity Manager to choose the
-     * proper handler for the url. If WebViewClient is provided, return {@code true}
-     * means the host application handles the url, while return {@code false} means the
-     * current WebView handles the url.
-     * This method is not called for requests using the POST "method".
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param url The url to be loaded.
-     * @return {@code true} if the host application wants to leave the current WebView
-     *         and handle the url itself, otherwise return {@code false}.
-     * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
-     *             shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
-     */
-    @Deprecated
-    public boolean shouldOverrideUrlLoading(WebView view, String url) {
-        return false;
-    }
-
-    /**
-     * Give the host application a chance to take over the control when a new
-     * url is about to be loaded in the current WebView. If WebViewClient is not
-     * provided, by default WebView will ask Activity Manager to choose the
-     * proper handler for the url. If WebViewClient is provided, return {@code true}
-     * means the host application handles the url, while return {@code false} means the
-     * current WebView handles the url.
-     *
-     * <p>Notes:
-     * <ul>
-     * <li>This method is not called for requests using the POST &quot;method&quot;.</li>
-     * <li>This method is also called for subframes with non-http schemes, thus it is
-     * strongly disadvised to unconditionally call {@link WebView#loadUrl(String)}
-     * with the request's url from inside the method and then return {@code true},
-     * as this will make WebView to attempt loading a non-http url, and thus fail.</li>
-     * </ul>
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param request Object containing the details of the request.
-     * @return {@code true} if the host application wants to leave the current WebView
-     *         and handle the url itself, otherwise return {@code false}.
-     */
-    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
-        return shouldOverrideUrlLoading(view, request.getUrl().toString());
-    }
-
-    /**
-     * Notify the host application that a page has started loading. This method
-     * is called once for each main frame load so a page with iframes or
-     * framesets will call onPageStarted one time for the main frame. This also
-     * means that onPageStarted will not be called when the contents of an
-     * embedded frame changes, i.e. clicking a link whose target is an iframe,
-     * it will also not be called for fragment navigations (navigations to
-     * #fragment_id).
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param url The url to be loaded.
-     * @param favicon The favicon for this page if it already exists in the
-     *            database.
-     */
-    public void onPageStarted(WebView view, String url, Bitmap favicon) {
-    }
-
-    /**
-     * Notify the host application that a page has finished loading. This method
-     * is called only for main frame. When onPageFinished() is called, the
-     * rendering picture may not be updated yet. To get the notification for the
-     * new Picture, use {@link WebView.PictureListener#onNewPicture}.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param url The url of the page.
-     */
-    public void onPageFinished(WebView view, String url) {
-    }
-
-    /**
-     * Notify the host application that the WebView will load the resource
-     * specified by the given url.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param url The url of the resource the WebView will load.
-     */
-    public void onLoadResource(WebView view, String url) {
-    }
-
-    /**
-     * Notify the host application that {@link android.webkit.WebView} content left over from
-     * previous page navigations will no longer be drawn.
-     *
-     * <p>This callback can be used to determine the point at which it is safe to make a recycled
-     * {@link android.webkit.WebView} visible, ensuring that no stale content is shown. It is called
-     * at the earliest point at which it can be guaranteed that {@link WebView#onDraw} will no
-     * longer draw any content from previous navigations. The next draw will display either the
-     * {@link WebView#setBackgroundColor background color} of the {@link WebView}, or some of the
-     * contents of the newly loaded page.
-     *
-     * <p>This method is called when the body of the HTTP response has started loading, is reflected
-     * in the DOM, and will be visible in subsequent draws. This callback occurs early in the
-     * document loading process, and as such you should expect that linked resources (for example,
-     * CSS and images) may not be available.
-     *
-     * <p>For more fine-grained notification of visual state updates, see {@link
-     * WebView#postVisualStateCallback}.
-     *
-     * <p>Please note that all the conditions and recommendations applicable to
-     * {@link WebView#postVisualStateCallback} also apply to this API.
-     *
-     * <p>This callback is only called for main frame navigations.
-     *
-     * @param view The {@link android.webkit.WebView} for which the navigation occurred.
-     * @param url  The URL corresponding to the page navigation that triggered this callback.
-     */
-    public void onPageCommitVisible(WebView view, String url) {
-    }
-
-    /**
-     * Notify the host application of a resource request and allow the
-     * application to return the data.  If the return value is {@code null}, the WebView
-     * will continue to load the resource as usual.  Otherwise, the return
-     * response and data will be used.
-     *
-     * <p class="note"><b>Note:</b> This method is called on a thread
-     * other than the UI thread so clients should exercise caution
-     * when accessing private data or the view system.
-     *
-     * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
-     * Browsing checks. If this is undesired, whitelist the URL with {@link
-     * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
-     *
-     * @param view The {@link android.webkit.WebView} that is requesting the
-     *             resource.
-     * @param url The raw url of the resource.
-     * @return A {@link android.webkit.WebResourceResponse} containing the
-     *         response information or {@code null} if the WebView should load the
-     *         resource itself.
-     * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest)
-     *             shouldInterceptRequest(WebView, WebResourceRequest)} instead.
-     */
-    @Deprecated
-    @Nullable
-    public WebResourceResponse shouldInterceptRequest(WebView view,
-            String url) {
-        return null;
-    }
-
-    /**
-     * Notify the host application of a resource request and allow the
-     * application to return the data.  If the return value is {@code null}, the WebView
-     * will continue to load the resource as usual.  Otherwise, the return
-     * response and data will be used.
-     *
-     * <p class="note"><b>Note:</b> This method is called on a thread
-     * other than the UI thread so clients should exercise caution
-     * when accessing private data or the view system.
-     *
-     * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
-     * Browsing checks. If this is undesired, whitelist the URL with {@link
-     * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
-     *
-     * @param view The {@link android.webkit.WebView} that is requesting the
-     *             resource.
-     * @param request Object containing the details of the request.
-     * @return A {@link android.webkit.WebResourceResponse} containing the
-     *         response information or {@code null} if the WebView should load the
-     *         resource itself.
-     */
-    @Nullable
-    public WebResourceResponse shouldInterceptRequest(WebView view,
-            WebResourceRequest request) {
-        return shouldInterceptRequest(view, request.getUrl().toString());
-    }
-
-    /**
-     * Notify the host application that there have been an excessive number of
-     * HTTP redirects. As the host application if it would like to continue
-     * trying to load the resource. The default behavior is to send the cancel
-     * message.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param cancelMsg The message to send if the host wants to cancel
-     * @param continueMsg The message to send if the host wants to continue
-     * @deprecated This method is no longer called. When the WebView encounters
-     *             a redirect loop, it will cancel the load.
-     */
-    @Deprecated
-    public void onTooManyRedirects(WebView view, Message cancelMsg,
-            Message continueMsg) {
-        cancelMsg.sendToTarget();
-    }
-
-    // These ints must match up to the hidden values in EventHandler.
-    /** Generic error */
-    public static final int ERROR_UNKNOWN = -1;
-    /** Server or proxy hostname lookup failed */
-    public static final int ERROR_HOST_LOOKUP = -2;
-    /** Unsupported authentication scheme (not basic or digest) */
-    public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3;
-    /** User authentication failed on server */
-    public static final int ERROR_AUTHENTICATION = -4;
-    /** User authentication failed on proxy */
-    public static final int ERROR_PROXY_AUTHENTICATION = -5;
-    /** Failed to connect to the server */
-    public static final int ERROR_CONNECT = -6;
-    /** Failed to read or write to the server */
-    public static final int ERROR_IO = -7;
-    /** Connection timed out */
-    public static final int ERROR_TIMEOUT = -8;
-    /** Too many redirects */
-    public static final int ERROR_REDIRECT_LOOP = -9;
-    /** Unsupported URI scheme */
-    public static final int ERROR_UNSUPPORTED_SCHEME = -10;
-    /** Failed to perform SSL handshake */
-    public static final int ERROR_FAILED_SSL_HANDSHAKE = -11;
-    /** Malformed URL */
-    public static final int ERROR_BAD_URL = -12;
-    /** Generic file error */
-    public static final int ERROR_FILE = -13;
-    /** File not found */
-    public static final int ERROR_FILE_NOT_FOUND = -14;
-    /** Too many requests during this load */
-    public static final int ERROR_TOO_MANY_REQUESTS = -15;
-    /** Resource load was canceled by Safe Browsing */
-    public static final int ERROR_UNSAFE_RESOURCE = -16;
-
-    /** @hide */
-    @IntDef(prefix = { "SAFE_BROWSING_THREAT_" }, value = {
-            SAFE_BROWSING_THREAT_UNKNOWN,
-            SAFE_BROWSING_THREAT_MALWARE,
-            SAFE_BROWSING_THREAT_PHISHING,
-            SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SafeBrowsingThreat {}
-
-    /** The resource was blocked for an unknown reason */
-    public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0;
-    /** The resource was blocked because it contains malware */
-    public static final int SAFE_BROWSING_THREAT_MALWARE = 1;
-    /** The resource was blocked because it contains deceptive content */
-    public static final int SAFE_BROWSING_THREAT_PHISHING = 2;
-    /** The resource was blocked because it contains unwanted software */
-    public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 3;
-
-    /**
-     * Report an error to the host application. These errors are unrecoverable
-     * (i.e. the main resource is unavailable). The {@code errorCode} parameter
-     * corresponds to one of the {@code ERROR_*} constants.
-     * @param view The WebView that is initiating the callback.
-     * @param errorCode The error code corresponding to an ERROR_* value.
-     * @param description A String describing the error.
-     * @param failingUrl The url that failed to load.
-     * @deprecated Use {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError)
-     *             onReceivedError(WebView, WebResourceRequest, WebResourceError)} instead.
-     */
-    @Deprecated
-    public void onReceivedError(WebView view, int errorCode,
-            String description, String failingUrl) {
-    }
-
-    /**
-     * Report web resource loading error to the host application. These errors usually indicate
-     * inability to connect to the server. Note that unlike the deprecated version of the callback,
-     * the new version will be called for any resource (iframe, image, etc.), not just for the main
-     * page. Thus, it is recommended to perform minimum required work in this callback.
-     * @param view The WebView that is initiating the callback.
-     * @param request The originating request.
-     * @param error Information about the error occurred.
-     */
-    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
-        if (request.isForMainFrame()) {
-            onReceivedError(view,
-                    error.getErrorCode(), error.getDescription().toString(),
-                    request.getUrl().toString());
-        }
-    }
-
-    /**
-     * Notify the host application that an HTTP error has been received from the server while
-     * loading a resource.  HTTP errors have status codes &gt;= 400.  This callback will be called
-     * for any resource (iframe, image, etc.), not just for the main page. Thus, it is recommended
-     * to perform minimum required work in this callback. Note that the content of the server
-     * response may not be provided within the {@code errorResponse} parameter.
-     * @param view The WebView that is initiating the callback.
-     * @param request The originating request.
-     * @param errorResponse Information about the error occurred.
-     */
-    public void onReceivedHttpError(
-            WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
-    }
-
-    /**
-     * As the host application if the browser should resend data as the
-     * requested page was a result of a POST. The default is to not resend the
-     * data.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param dontResend The message to send if the browser should not resend
-     * @param resend The message to send if the browser should resend data
-     */
-    public void onFormResubmission(WebView view, Message dontResend,
-            Message resend) {
-        dontResend.sendToTarget();
-    }
-
-    /**
-     * Notify the host application to update its visited links database.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param url The url being visited.
-     * @param isReload {@code true} if this url is being reloaded.
-     */
-    public void doUpdateVisitedHistory(WebView view, String url,
-            boolean isReload) {
-    }
-
-    /**
-     * Notify the host application that an SSL error occurred while loading a
-     * resource. The host application must call either handler.cancel() or
-     * handler.proceed(). Note that the decision may be retained for use in
-     * response to future SSL errors. The default behavior is to cancel the
-     * load.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param handler An SslErrorHandler object that will handle the user's
-     *            response.
-     * @param error The SSL error object.
-     */
-    public void onReceivedSslError(WebView view, SslErrorHandler handler,
-            SslError error) {
-        handler.cancel();
-    }
-
-    /**
-     * Notify the host application to handle a SSL client certificate request. The host application
-     * is responsible for showing the UI if desired and providing the keys. There are three ways to
-     * respond: {@link ClientCertRequest#proceed}, {@link ClientCertRequest#cancel}, or {@link
-     * ClientCertRequest#ignore}. Webview stores the response in memory (for the life of the
-     * application) if {@link ClientCertRequest#proceed} or {@link ClientCertRequest#cancel} is
-     * called and does not call {@code onReceivedClientCertRequest()} again for the same host and
-     * port pair. Webview does not store the response if {@link ClientCertRequest#ignore}
-     * is called. Note that, multiple layers in chromium network stack might be
-     * caching the responses, so the behavior for ignore is only a best case
-     * effort.
-     *
-     * This method is called on the UI thread. During the callback, the
-     * connection is suspended.
-     *
-     * For most use cases, the application program should implement the
-     * {@link android.security.KeyChainAliasCallback} interface and pass it to
-     * {@link android.security.KeyChain#choosePrivateKeyAlias} to start an
-     * activity for the user to choose the proper alias. The keychain activity will
-     * provide the alias through the callback method in the implemented interface. Next
-     * the application should create an async task to call
-     * {@link android.security.KeyChain#getPrivateKey} to receive the key.
-     *
-     * An example implementation of client certificates can be seen at
-     * <A href="https://android.googlesource.com/platform/packages/apps/Browser/+/android-5.1.1_r1/src/com/android/browser/Tab.java">
-     * AOSP Browser</a>
-     *
-     * The default behavior is to cancel, returning no client certificate.
-     *
-     * @param view The WebView that is initiating the callback
-     * @param request An instance of a {@link ClientCertRequest}
-     *
-     */
-    public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
-        request.cancel();
-    }
-
-    /**
-     * Notifies the host application that the WebView received an HTTP
-     * authentication request. The host application can use the supplied
-     * {@link HttpAuthHandler} to set the WebView's response to the request.
-     * The default behavior is to cancel the request.
-     *
-     * @param view the WebView that is initiating the callback
-     * @param handler the HttpAuthHandler used to set the WebView's response
-     * @param host the host requiring authentication
-     * @param realm the realm for which authentication is required
-     * @see WebView#getHttpAuthUsernamePassword
-     */
-    public void onReceivedHttpAuthRequest(WebView view,
-            HttpAuthHandler handler, String host, String realm) {
-        handler.cancel();
-    }
-
-    /**
-     * Give the host application a chance to handle the key event synchronously.
-     * e.g. menu shortcut key events need to be filtered this way. If return
-     * true, WebView will not handle the key event. If return {@code false}, WebView
-     * will always handle the key event, so none of the super in the view chain
-     * will see the key event. The default behavior returns {@code false}.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param event The key event.
-     * @return {@code true} if the host application wants to handle the key event
-     *         itself, otherwise return {@code false}
-     */
-    public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
-        return false;
-    }
-
-    /**
-     * Notify the host application that a key was not handled by the WebView.
-     * Except system keys, WebView always consumes the keys in the normal flow
-     * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously
-     * from where the key is dispatched. It gives the host application a chance
-     * to handle the unhandled key events.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param event The key event.
-     */
-    public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
-        onUnhandledInputEventInternal(view, event);
-    }
-
-    /**
-     * Notify the host application that a input event was not handled by the WebView.
-     * Except system keys, WebView always consumes input events in the normal flow
-     * or if {@link #shouldOverrideKeyEvent} returns {@code true}. This is called asynchronously
-     * from where the event is dispatched. It gives the host application a chance
-     * to handle the unhandled input events.
-     *
-     * Note that if the event is a {@link android.view.MotionEvent}, then it's lifetime is only
-     * that of the function call. If the WebViewClient wishes to use the event beyond that, then it
-     * <i>must</i> create a copy of the event.
-     *
-     * It is the responsibility of overriders of this method to call
-     * {@link #onUnhandledKeyEvent(WebView, KeyEvent)}
-     * when appropriate if they wish to continue receiving events through it.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param event The input event.
-     * @removed
-     */
-    public void onUnhandledInputEvent(WebView view, InputEvent event) {
-        if (event instanceof KeyEvent) {
-            onUnhandledKeyEvent(view, (KeyEvent) event);
-            return;
-        }
-        onUnhandledInputEventInternal(view, event);
-    }
-
-    private void onUnhandledInputEventInternal(WebView view, InputEvent event) {
-        ViewRootImpl root = view.getViewRootImpl();
-        if (root != null) {
-            root.dispatchUnhandledInputEvent(event);
-        }
-    }
-
-    /**
-     * Notify the host application that the scale applied to the WebView has
-     * changed.
-     *
-     * @param view The WebView that is initiating the callback.
-     * @param oldScale The old scale factor
-     * @param newScale The new scale factor
-     */
-    public void onScaleChanged(WebView view, float oldScale, float newScale) {
-    }
-
-    /**
-     * Notify the host application that a request to automatically log in the
-     * user has been processed.
-     * @param view The WebView requesting the login.
-     * @param realm The account realm used to look up accounts.
-     * @param account An optional account. If not {@code null}, the account should be
-     *                checked against accounts on the device. If it is a valid
-     *                account, it should be used to log in the user.
-     * @param args Authenticator specific arguments used to log in the user.
-     */
-    public void onReceivedLoginRequest(WebView view, String realm,
-            @Nullable String account, String args) {
-    }
-
-    /**
-     * Notify host application that the given WebView's render process has exited.
-     *
-     * Multiple WebView instances may be associated with a single render process;
-     * onRenderProcessGone will be called for each WebView that was affected.
-     * The application's implementation of this callback should only attempt to
-     * clean up the specific WebView given as a parameter, and should not assume
-     * that other WebView instances are affected.
-     *
-     * The given WebView can't be used, and should be removed from the view hierarchy,
-     * all references to it should be cleaned up, e.g any references in the Activity
-     * or other classes saved using {@link android.view.View#findViewById} and similar calls, etc.
-     *
-     * To cause an render process crash for test purpose, the application can
-     * call {@code loadUrl("chrome://crash")} on the WebView. Note that multiple WebView
-     * instances may be affected if they share a render process, not just the
-     * specific WebView which loaded chrome://crash.
-     *
-     * @param view The WebView which needs to be cleaned up.
-     * @param detail the reason why it exited.
-     * @return {@code true} if the host application handled the situation that process has
-     *         exited, otherwise, application will crash if render process crashed,
-     *         or be killed if render process was killed by the system.
-     */
-    public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
-        return false;
-    }
-
-    /**
-     * Notify the host application that a loading URL has been flagged by Safe Browsing.
-     *
-     * The application must invoke the callback to indicate the preferred response. The default
-     * behavior is to show an interstitial to the user, with the reporting checkbox visible.
-     *
-     * If the application needs to show its own custom interstitial UI, the callback can be invoked
-     * asynchronously with {@link SafeBrowsingResponse#backToSafety} or {@link
-     * SafeBrowsingResponse#proceed}, depending on user response.
-     *
-     * @param view The WebView that hit the malicious resource.
-     * @param request Object containing the details of the request.
-     * @param threatType The reason the resource was caught by Safe Browsing, corresponding to a
-     *                   {@code SAFE_BROWSING_THREAT_*} value.
-     * @param callback Applications must invoke one of the callback methods.
-     */
-    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
-            @SafeBrowsingThreat int threatType, SafeBrowsingResponse callback) {
-        callback.showInterstitial(/* allowReporting */ true);
-    }
 }
diff --git a/android/webkit/WebViewFactory.java b/android/webkit/WebViewFactory.java
index e9fe481..e0ccda9 100644
--- a/android/webkit/WebViewFactory.java
+++ b/android/webkit/WebViewFactory.java
@@ -205,25 +205,21 @@
         }
 
         PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager();
-        PackageInfo packageInfo;
+        String libraryFileName;
         try {
-            packageInfo = packageManager.getPackageInfo(packageName,
+            PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
                     PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+            libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(LOGTAG, "Couldn't find package " + packageName);
             return LIBLOAD_WRONG_PACKAGE_NAME;
         }
 
-        try {
-            int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, packageInfo);
-            // If we failed waiting for relro we want to return that fact even if we successfully
-            // load the relro file.
-            if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
-            return loadNativeRet;
-        } catch (MissingWebViewPackageException e) {
-            Log.e(LOGTAG, "Couldn't load native library: " + e);
-            return LIBLOAD_FAILED_TO_LOAD_LIBRARY;
-        }
+        int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, libraryFileName);
+        // If we failed waiting for relro we want to return that fact even if we successfully
+        // load the relro file.
+        if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
+        return loadNativeRet;
     }
 
     static WebViewFactoryProvider getProvider() {
@@ -454,7 +450,8 @@
                 ClassLoader clazzLoader = webViewContext.getClassLoader();
 
                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
-                WebViewLibraryLoader.loadNativeLibrary(clazzLoader, sPackageInfo);
+                WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
+                        getWebViewLibrary(sPackageInfo.applicationInfo));
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
 
                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
diff --git a/android/webkit/WebViewFactoryProvider.java b/android/webkit/WebViewFactoryProvider.java
index 4f7cdab..4ff49ea 100644
--- a/android/webkit/WebViewFactoryProvider.java
+++ b/android/webkit/WebViewFactoryProvider.java
@@ -89,7 +89,7 @@
         * {@link android.webkit.WebView#setSafeBrowsingWhitelist(List<String>,
         * ValueCallback<Boolean>)}
         */
-        void setSafeBrowsingWhitelist(List<String> urls, ValueCallback<Boolean> callback);
+        void setSafeBrowsingWhitelist(List<String> hosts, ValueCallback<Boolean> callback);
 
         /**
          * Implement the API method
diff --git a/android/webkit/WebViewLibraryLoader.java b/android/webkit/WebViewLibraryLoader.java
index eb2b6bc..cabba06 100644
--- a/android/webkit/WebViewLibraryLoader.java
+++ b/android/webkit/WebViewLibraryLoader.java
@@ -234,17 +234,14 @@
      * <p class="note"><b>Note:</b> Assumes that we have waited for relro creation.
      *
      * @param clazzLoader class loader used to find the linker namespace to load the library into.
-     * @param packageInfo the package from which WebView is loaded.
+     * @param libraryFileName the filename of the library to load.
      */
-    static int loadNativeLibrary(ClassLoader clazzLoader, PackageInfo packageInfo)
-            throws WebViewFactory.MissingWebViewPackageException {
+    public static int loadNativeLibrary(ClassLoader clazzLoader, String libraryFileName) {
         if (!sAddressSpaceReserved) {
             Log.e(LOGTAG, "can't load with relro file; address space not reserved");
             return WebViewFactory.LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
         }
 
-        final String libraryFileName =
-                WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo);
         String relroPath = VMRuntime.getRuntime().is64Bit() ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
                                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_32;
         int result = nativeLoadWithRelroFile(libraryFileName, relroPath, clazzLoader);
diff --git a/android/webkit/WebViewProvider.java b/android/webkit/WebViewProvider.java
index a896925..00e782b 100644
--- a/android/webkit/WebViewProvider.java
+++ b/android/webkit/WebViewProvider.java
@@ -329,13 +329,16 @@
 
         public void onProvideVirtualStructure(android.view.ViewStructure structure);
 
-        @SuppressWarnings("unused")
-        public default void onProvideAutofillVirtualStructure(android.view.ViewStructure structure,
-                int flags) {
+        default void onProvideAutofillVirtualStructure(
+                @SuppressWarnings("unused") android.view.ViewStructure structure,
+                @SuppressWarnings("unused") int flags) {
         }
 
-        @SuppressWarnings("unused")
-        public default void autofill(SparseArray<AutofillValue>values) {
+        default void autofill(@SuppressWarnings("unused") SparseArray<AutofillValue> values) {
+        }
+
+        default boolean isVisibleToUserForAutofill(@SuppressWarnings("unused") int virtualId) {
+            return true; // true is the default value returned by View.isVisibleToUserForAutofill()
         }
 
         public AccessibilityNodeProvider getAccessibilityNodeProvider();
@@ -424,6 +427,11 @@
         public Handler getHandler(Handler originalHandler);
 
         public View findFocus(View originalFocusedView);
+
+        @SuppressWarnings("unused")
+        default boolean onCheckIsTextEditor() {
+            return false;
+        }
     }
 
     interface ScrollDelegate {
diff --git a/android/webkit/WebViewProviderInfo.java b/android/webkit/WebViewProviderInfo.java
index 5d091c9..b0e9f01 100644
--- a/android/webkit/WebViewProviderInfo.java
+++ b/android/webkit/WebViewProviderInfo.java
@@ -17,10 +17,10 @@
 package android.webkit;
 
 import android.annotation.SystemApi;
+import android.content.pm.Signature;
 import android.os.Parcel;
 import android.os.Parcelable;
-
-import java.util.Arrays;
+import android.util.Base64;
 
 /**
  * @hide
@@ -34,7 +34,14 @@
         this.description = description;
         this.availableByDefault = availableByDefault;
         this.isFallback = isFallback;
-        this.signatures = signatures;
+        if (signatures == null) {
+            this.signatures = new Signature[0];
+        } else {
+            this.signatures = new Signature[signatures.length];
+            for (int n = 0; n < signatures.length; n++) {
+                this.signatures[n] = new Signature(Base64.decode(signatures[n], Base64.DEFAULT));
+            }
+        }
     }
 
     // aidl stuff
@@ -54,7 +61,7 @@
         description = in.readString();
         availableByDefault = (in.readInt() > 0);
         isFallback = (in.readInt() > 0);
-        signatures = in.createStringArray();
+        signatures = in.createTypedArray(Signature.CREATOR);
     }
 
     @Override
@@ -68,7 +75,7 @@
         out.writeString(description);
         out.writeInt(availableByDefault ? 1 : 0);
         out.writeInt(isFallback ? 1 : 0);
-        out.writeStringArray(signatures);
+        out.writeTypedArray(signatures, 0);
     }
 
     // fields read from framework resource
@@ -76,5 +83,5 @@
     public final String description;
     public final boolean availableByDefault;
     public final boolean isFallback;
-    public final String[] signatures;
+    public final Signature[] signatures;
 }
diff --git a/android/webkit/WebViewZygote.java b/android/webkit/WebViewZygote.java
index db60ad8..49e11b8 100644
--- a/android/webkit/WebViewZygote.java
+++ b/android/webkit/WebViewZygote.java
@@ -19,29 +19,24 @@
 import android.app.LoadedApk;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.AsyncTask;
 import android.os.Build;
-import android.os.SystemService;
+import android.os.ChildZygoteProcess;
+import android.os.Process;
 import android.os.ZygoteProcess;
 import android.text.TextUtils;
-import android.util.AndroidRuntimeException;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.TimeoutException;
 
 /** @hide */
 public class WebViewZygote {
     private static final String LOGTAG = "WebViewZygote";
 
-    private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
-    private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
-    private static final String WEBVIEW_ZYGOTE_SOCKET = "webview_zygote";
-
     /**
      * Lock object that protects all other static members.
      */
@@ -52,14 +47,7 @@
      * zygote is not running or is not connected.
      */
     @GuardedBy("sLock")
-    private static ZygoteProcess sZygote;
-
-    /**
-     * Variable that allows us to determine whether the WebView zygote Service has already been
-     * started.
-     */
-    @GuardedBy("sLock")
-    private static boolean sStartedService = false;
+    private static ChildZygoteProcess sZygote;
 
     /**
      * Information about the selected WebView package. This is set from #onWebViewProviderChanged().
@@ -85,7 +73,7 @@
         synchronized (sLock) {
             if (sZygote != null) return sZygote;
 
-            waitForServiceStartAndConnect();
+            connectToZygoteIfNeededLocked();
             return sZygote;
         }
     }
@@ -107,21 +95,17 @@
             sMultiprocessEnabled = enabled;
 
             // When toggling between multi-process being on/off, start or stop the
-            // service. If it is enabled and the zygote is not yet started, bring up the service.
-            // Otherwise, bring down the service. The name may be null if the package
-            // information has not yet been resolved.
-            final String serviceName = getServiceNameLocked();
-            if (serviceName == null) return;
-
+            // zygote. If it is enabled and the zygote is not yet started, launch it.
+            // Otherwise, kill it. The name may be null if the package information has
+            // not yet been resolved.
             if (enabled) {
-                if (!sStartedService) {
-                    SystemService.start(serviceName);
-                    sStartedService = true;
-                }
+                // Run on a background thread as this waits for the zygote to start and we don't
+                // want to block the caller on this. It's okay if this is delayed as anyone trying
+                // to use the zygote will call it first anyway.
+                AsyncTask.THREAD_POOL_EXECUTOR.execute(WebViewZygote::getProcess);
             } else {
-                SystemService.stop(serviceName);
-                sStartedService = false;
-                sZygote = null;
+                // No need to run this in the background, it's very brief.
+                stopZygoteLocked();
             }
         }
     }
@@ -137,53 +121,21 @@
                 return;
             }
 
-            final String serviceName = getServiceNameLocked();
-            sZygote = null;
-
-            // The service may enter the RUNNING state before it opens the socket,
-            // so connectToZygoteIfNeededLocked() may still fail.
-            if (SystemService.isStopped(serviceName)) {
-                SystemService.start(serviceName);
-            } else {
-                SystemService.restart(serviceName);
-            }
-            sStartedService = true;
-        }
-    }
-
-    private static void waitForServiceStartAndConnect() {
-        if (!sStartedService) {
-            throw new AndroidRuntimeException("Tried waiting for the WebView Zygote Service to " +
-                    "start running without first starting the service.");
-        }
-
-        String serviceName;
-        synchronized (sLock) {
-            serviceName = getServiceNameLocked();
-        }
-        try {
-            SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
-        } catch (TimeoutException e) {
-            Log.e(LOGTAG, "Timed out waiting for " + serviceName);
-            return;
-        }
-
-        synchronized (sLock) {
-            connectToZygoteIfNeededLocked();
+            stopZygoteLocked();
         }
     }
 
     @GuardedBy("sLock")
-    private static String getServiceNameLocked() {
-        if (sPackage == null)
-            return null;
-
-        if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains(
-                    sPackage.applicationInfo.primaryCpuAbi)) {
-            return WEBVIEW_ZYGOTE_SERVICE_64;
+    private static void stopZygoteLocked() {
+        if (sZygote != null) {
+            // Close the connection and kill the zygote process. This will not cause
+            // child processes to be killed by itself. But if this is called in response to
+            // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater
+            // will kill all processes that depend on the WebView package.
+            sZygote.close();
+            Process.killProcess(sZygote.getPid());
+            sZygote = null;
         }
-
-        return WEBVIEW_ZYGOTE_SERVICE_32;
     }
 
     @GuardedBy("sLock")
@@ -197,14 +149,17 @@
             return;
         }
 
-        final String serviceName = getServiceNameLocked();
-        if (!SystemService.isRunning(serviceName)) {
-            Log.e(LOGTAG, serviceName + " is not running");
-            return;
-        }
-
         try {
-            sZygote = new ZygoteProcess(WEBVIEW_ZYGOTE_SOCKET, null);
+            sZygote = Process.zygoteProcess.startChildZygote(
+                    "com.android.internal.os.WebViewZygoteInit",
+                    "webview_zygote",
+                    Process.WEBVIEW_ZYGOTE_UID,
+                    Process.WEBVIEW_ZYGOTE_UID,
+                    null,  // gids
+                    0,  // runtimeFlags
+                    "webview_zygote",  // seInfo
+                    sPackage.applicationInfo.primaryCpuAbi,  // abi
+                    null);  // instructionSet
 
             // All the work below is usually done by LoadedApk, but the zygote can't talk to
             // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so
@@ -219,6 +174,8 @@
             final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
                     TextUtils.join(File.pathSeparator, zipPaths);
 
+            String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo);
+
             // In the case where the ApplicationInfo has been modified by the stub WebView,
             // we need to use the original ApplicationInfo to determine what the original classpath
             // would have been to use as a cache key.
@@ -226,14 +183,14 @@
             final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) :
                     TextUtils.join(File.pathSeparator, zipPaths);
 
-            ZygoteProcess.waitForConnectionToZygote(WEBVIEW_ZYGOTE_SOCKET);
+            ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress());
 
             Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
-            sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey,
+            sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey,
                                          Build.SUPPORTED_ABIS[0]);
         } catch (Exception e) {
-            Log.e(LOGTAG, "Error connecting to " + serviceName, e);
-            sZygote = null;
+            Log.e(LOGTAG, "Error connecting to webview zygote", e);
+            stopZygoteLocked();
         }
     }
 }
diff --git a/android/widget/AbsListView.java b/android/widget/AbsListView.java
index 594d240..298c61e 100644
--- a/android/widget/AbsListView.java
+++ b/android/widget/AbsListView.java
@@ -31,7 +31,6 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
-import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.StrictMode;
@@ -90,7 +89,7 @@
 
 /**
  * Base class that can be used to implement virtualized lists of items. A list does
- * not have a spatial definition here. For instance, subclases of this class can
+ * not have a spatial definition here. For instance, subclasses of this class can
  * display the content of the list in a grid, in a carousel, as stack, etc.
  *
  * @attr ref android.R.styleable#AbsListView_listSelector
@@ -6036,11 +6035,6 @@
         public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
             return getTarget().commitContent(inputContentInfo, flags, opts);
         }
-
-        @Override
-        public void reportLanguageHint(@NonNull LocaleList languageHint) {
-            getTarget().reportLanguageHint(languageHint);
-        }
     }
 
     /**
@@ -6864,7 +6858,7 @@
             // detached and we do not allow detached views to fire accessibility
             // events. So we are announcing that the subtree changed giving a chance
             // to clients holding on to a view in this subtree to refresh it.
-            notifyAccessibilityStateChanged(
+            notifyViewAccessibilityStateChangedIfNeeded(
                     AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
 
             // Don't scrap views that have transient state.
diff --git a/android/widget/AbsSeekBar.java b/android/widget/AbsSeekBar.java
index 1d1fcc9..61a5873 100644
--- a/android/widget/AbsSeekBar.java
+++ b/android/widget/AbsSeekBar.java
@@ -863,7 +863,7 @@
         }
 
         final int range = getMax() - getMin();
-        progress += scale * range;
+        progress += scale * range + getMin();
 
         setHotspot(x, y);
         setProgressInternal(Math.round(progress), true, false);
diff --git a/android/widget/AdapterView.java b/android/widget/AdapterView.java
index 08374cb..6c19256 100644
--- a/android/widget/AdapterView.java
+++ b/android/widget/AdapterView.java
@@ -1093,7 +1093,7 @@
             checkSelectionChanged();
         }
 
-        notifyAccessibilitySubtreeChanged();
+        notifySubtreeAccessibilityStateChangedIfNeeded();
     }
 
     /**
diff --git a/android/widget/CheckedTextView.java b/android/widget/CheckedTextView.java
index af01a3e..92bfd56 100644
--- a/android/widget/CheckedTextView.java
+++ b/android/widget/CheckedTextView.java
@@ -132,7 +132,7 @@
         if (mChecked != checked) {
             mChecked = checked;
             refreshDrawableState();
-            notifyAccessibilityStateChanged(
+            notifyViewAccessibilityStateChangedIfNeeded(
                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
diff --git a/android/widget/CompoundButton.java b/android/widget/CompoundButton.java
index e57f153..0762b15 100644
--- a/android/widget/CompoundButton.java
+++ b/android/widget/CompoundButton.java
@@ -158,7 +158,7 @@
             mCheckedFromResource = false;
             mChecked = checked;
             refreshDrawableState();
-            notifyAccessibilityStateChanged(
+            notifyViewAccessibilityStateChangedIfNeeded(
                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
 
             // Avoid infinite recursions if setChecked() is called from a listener
diff --git a/android/widget/Editor.java b/android/widget/Editor.java
index 7bb0db1..9946726 100644
--- a/android/widget/Editor.java
+++ b/android/widget/Editor.java
@@ -17,11 +17,13 @@
 package android.widget;
 
 import android.R;
+import android.animation.ValueAnimator;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
 import android.content.ClipData;
 import android.content.ClipData.Item;
 import android.content.Context;
@@ -37,6 +39,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
@@ -99,6 +102,7 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.EditorInfo;
@@ -107,7 +111,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextLinks;
+import android.view.textclassifier.TextClassificationManager;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.TextView.Drawables;
 import android.widget.TextView.OnEditorActionListener;
@@ -200,11 +204,11 @@
 
     private final boolean mHapticTextHandleEnabled;
 
-    private final Magnifier mMagnifier;
+    private final MagnifierMotionAnimator mMagnifierAnimator;
     private final Runnable mUpdateMagnifierRunnable = new Runnable() {
         @Override
         public void run() {
-            mMagnifier.update();
+            mMagnifierAnimator.update();
         }
     };
     // Update the magnifier contents whenever anything in the view hierarchy is updated.
@@ -215,7 +219,7 @@
             new ViewTreeObserver.OnDrawListener() {
         @Override
         public void onDraw() {
-            if (mMagnifier != null) {
+            if (mMagnifierAnimator != null) {
                 // Posting the method will ensure that updating the magnifier contents will
                 // happen right after the rendering of the current frame.
                 mTextView.post(mUpdateMagnifierRunnable);
@@ -262,7 +266,8 @@
     boolean mDiscardNextActionUp;
     boolean mIgnoreActionUpEvent;
 
-    long mShowCursor;
+    private long mShowCursor;
+    private boolean mRenderCursorRegardlessTiming;
     private Blink mBlink;
 
     boolean mCursorVisible = true;
@@ -285,6 +290,7 @@
     boolean mShowSoftInputOnFocus = true;
     private boolean mPreserveSelection;
     private boolean mRestartActionModeOnNextRefresh;
+    private boolean mRequestingLinkActionMode;
 
     private SelectionActionModeHelper mSelectionActionModeHelper;
 
@@ -370,7 +376,9 @@
         mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_enableHapticTextHandle);
 
-        mMagnifier = FLAG_USE_MAGNIFIER ? new Magnifier(mTextView) : null;
+        if (FLAG_USE_MAGNIFIER) {
+            mMagnifierAnimator = new MagnifierMotionAnimator(new Magnifier(mTextView));
+        }
     }
 
     ParcelableParcel saveInstanceState() {
@@ -681,11 +689,22 @@
         }
     }
 
-    boolean isCursorVisible() {
+    private boolean isCursorVisible() {
         // The default value is true, even when there is no associated Editor
         return mCursorVisible && mTextView.isTextEditable();
     }
 
+    boolean shouldRenderCursor() {
+        if (!isCursorVisible()) {
+            return false;
+        }
+        if (mRenderCursorRegardlessTiming) {
+            return true;
+        }
+        final long showCursorDelta = SystemClock.uptimeMillis() - mShowCursor;
+        return showCursorDelta % (2 * BLINK) < BLINK;
+    }
+
     void prepareCursorControllers() {
         boolean windowSupportsHandles = false;
 
@@ -1299,6 +1318,16 @@
             if (mSelectionModifierCursorController != null) {
                 mSelectionModifierCursorController.resetTouchOffsets();
             }
+
+            ensureNoSelectionIfNonSelectable();
+        }
+    }
+
+    private void ensureNoSelectionIfNonSelectable() {
+        // This could be the case if a TextLink has been tapped.
+        if (!mTextView.textCanBeSelected() && mTextView.hasSelection()) {
+            Selection.setSelection((Spannable) mTextView.getText(),
+                    mTextView.length(), mTextView.length());
         }
     }
 
@@ -1382,6 +1411,8 @@
 
             // Don't leave us in the middle of a batch edit. Same as in onFocusChanged
             ensureEndedBatchEdit();
+
+            ensureNoSelectionIfNonSelectable();
         }
     }
 
@@ -1745,16 +1776,19 @@
             highlight = null;
         }
 
+        if (mSelectionActionModeHelper != null) {
+            mSelectionActionModeHelper.onDraw(canvas);
+            if (mSelectionActionModeHelper.isDrawingHighlight()) {
+                highlight = null;
+            }
+        }
+
         if (mTextView.canHaveDisplayList() && canvas.isHardwareAccelerated()) {
             drawHardwareAccelerated(canvas, layout, highlight, highlightPaint,
                     cursorOffsetVertical);
         } else {
             layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
         }
-
-        if (mSelectionActionModeHelper != null) {
-            mSelectionActionModeHelper.onDraw(canvas);
-        }
     }
 
     private void drawHardwareAccelerated(Canvas canvas, Layout layout, Path highlight,
@@ -2090,13 +2124,13 @@
         getSelectionActionModeHelper().startSelectionActionModeAsync(adjustSelection);
     }
 
-    void startLinkActionModeAsync(TextLinks.TextLink link) {
-        Preconditions.checkNotNull(link);
+    void startLinkActionModeAsync(int start, int end) {
         if (!(mTextView.getText() instanceof Spannable)) {
             return;
         }
         stopTextActionMode();
-        getSelectionActionModeHelper().startLinkActionModeAsync(link);
+        mRequestingLinkActionMode = true;
+        getSelectionActionModeHelper().startLinkActionModeAsync(start, end);
     }
 
     /**
@@ -2181,7 +2215,9 @@
         mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
 
         final boolean selectionStarted = mTextActionMode != null;
-        if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
+        if (selectionStarted
+                && mTextView.isTextEditable() && !mTextView.isTextSelectable()
+                && mShowSoftInputOnFocus) {
             // Show the IME to be able to replace text, except when selecting non editable text.
             final InputMethodManager imm = InputMethodManager.peekInstance();
             if (imm != null) {
@@ -2291,10 +2327,14 @@
         if (!selectAllGotFocus && text.length() > 0) {
             // Move cursor
             final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
-            Selection.setSelection((Spannable) text, offset);
-            if (mSpellChecker != null) {
-                // When the cursor moves, the word that was typed may need spell check
-                mSpellChecker.onSelectionChanged();
+
+            final boolean shouldInsertCursor = !mRequestingLinkActionMode;
+            if (shouldInsertCursor) {
+                Selection.setSelection((Spannable) text, offset);
+                if (mSpellChecker != null) {
+                    // When the cursor moves, the word that was typed may need spell check
+                    mSpellChecker.onSelectionChanged();
+                }
             }
 
             if (!extractedTextModeWillBeStarted()) {
@@ -2304,16 +2344,17 @@
                         mTextView.removeCallbacks(mInsertionActionModeRunnable);
                     }
 
-                    mShowSuggestionRunnable = new Runnable() {
-                        public void run() {
-                            replace();
-                        }
-                    };
+                    mShowSuggestionRunnable = this::replace;
+
                     // removeCallbacks is performed on every touch
                     mTextView.postDelayed(mShowSuggestionRunnable,
                             ViewConfiguration.getDoubleTapTimeout());
                 } else if (hasInsertionController()) {
-                    getInsertionController().show();
+                    if (shouldInsertCursor) {
+                        getInsertionController().show();
+                    } else {
+                        getInsertionController().hide();
+                    }
                 }
             }
         }
@@ -3997,7 +4038,7 @@
 
         private void updateAssistMenuItems(Menu menu) {
             clearAssistMenuItems(menu);
-            if (!mTextView.isDeviceProvisioned()) {
+            if (!shouldEnableAssistMenuItems()) {
                 return;
             }
             final TextClassification textClassification =
@@ -4005,41 +4046,46 @@
             if (textClassification == null) {
                 return;
             }
-            if (isValidAssistMenuItem(
-                    textClassification.getIcon(),
-                    textClassification.getLabel(),
-                    textClassification.getIntent())) {
+            if (!textClassification.getActions().isEmpty()) {
+                // Primary assist action (Always shown).
+                final MenuItem item = addAssistMenuItem(menu,
+                        textClassification.getActions().get(0), TextView.ID_ASSIST,
+                        MENU_ITEM_ORDER_ASSIST, MenuItem.SHOW_AS_ACTION_ALWAYS);
+                item.setIntent(textClassification.getIntent());
+            } else if (hasLegacyAssistItem(textClassification)) {
+                // Legacy primary assist action (Always shown).
                 final MenuItem item = menu.add(
                         TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST,
                         textClassification.getLabel())
                         .setIcon(textClassification.getIcon())
                         .setIntent(textClassification.getIntent());
                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-                mAssistClickHandlers.put(
-                        item, TextClassification.createStartActivityOnClickListener(
-                                mTextView.getContext(), textClassification.getIntent()));
+                mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
+                        TextClassification.createPendingIntent(mTextView.getContext(),
+                                textClassification.getIntent())));
             }
-            final int count = textClassification.getSecondaryActionsCount();
-            for (int i = 0; i < count; i++) {
-                if (!isValidAssistMenuItem(
-                        textClassification.getSecondaryIcon(i),
-                        textClassification.getSecondaryLabel(i),
-                        textClassification.getSecondaryIntent(i))) {
-                    continue;
-                }
-                final int order = MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i;
-                final MenuItem item = menu.add(
-                        TextView.ID_ASSIST, Menu.NONE, order,
-                        textClassification.getSecondaryLabel(i))
-                        .setIcon(textClassification.getSecondaryIcon(i))
-                        .setIntent(textClassification.getSecondaryIntent(i));
-                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-                mAssistClickHandlers.put(item,
-                        TextClassification.createStartActivityOnClickListener(
-                                mTextView.getContext(), textClassification.getSecondaryIntent(i)));
+            final int count = textClassification.getActions().size();
+            for (int i = 1; i < count; i++) {
+                // Secondary assist action (Never shown).
+                addAssistMenuItem(menu, textClassification.getActions().get(i), Menu.NONE,
+                        MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i - 1,
+                        MenuItem.SHOW_AS_ACTION_NEVER);
             }
         }
 
+        private MenuItem addAssistMenuItem(Menu menu, RemoteAction action, int intemId, int order,
+                int showAsAction) {
+            final MenuItem item = menu.add(TextView.ID_ASSIST, intemId, order, action.getTitle())
+                    .setContentDescription(action.getContentDescription());
+            if (action.shouldShowIcon()) {
+                item.setIcon(action.getIcon().loadDrawable(mTextView.getContext()));
+            }
+            item.setShowAsAction(showAsAction);
+            mAssistClickHandlers.put(item,
+                    TextClassification.createIntentOnClickListener(action.getActionIntent()));
+            return item;
+        }
+
         private void clearAssistMenuItems(Menu menu) {
             int i = 0;
             while (i < menu.size()) {
@@ -4052,30 +4098,11 @@
             }
         }
 
-        private boolean isValidAssistMenuItem(Drawable icon, CharSequence label, Intent intent) {
-            final boolean hasUi = icon != null || !TextUtils.isEmpty(label);
-            final boolean hasAction = isSupportedIntent(intent);
-            return hasUi && hasAction;
-        }
-
-        private boolean isSupportedIntent(Intent intent) {
-            if (intent == null) {
-                return false;
-            }
-            final Context context = mTextView.getContext();
-            final ResolveInfo info = context.getPackageManager().resolveActivity(intent, 0);
-            final boolean samePackage = context.getPackageName().equals(
-                    info.activityInfo.packageName);
-            if (samePackage) {
-                return true;
-            }
-
-            final boolean exported =  info.activityInfo.exported;
-            final boolean requiresPermission = info.activityInfo.permission != null;
-            final boolean hasPermission = !requiresPermission
-                    || context.checkSelfPermission(info.activityInfo.permission)
-                            == PackageManager.PERMISSION_GRANTED;
-            return exported && hasPermission;
+        private boolean hasLegacyAssistItem(TextClassification classification) {
+            // Check whether we have the UI data and and action.
+            return (classification.getIcon() != null || !TextUtils.isEmpty(
+                    classification.getLabel())) && (classification.getIntent() != null
+                    || classification.getOnClickListener() != null);
         }
 
         private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) {
@@ -4083,7 +4110,7 @@
 
             final TextClassification textClassification =
                     getSelectionActionModeHelper().getTextClassification();
-            if (!mTextView.isDeviceProvisioned() || textClassification == null) {
+            if (!shouldEnableAssistMenuItems() || textClassification == null) {
                 // No textClassification result to handle the click. Eat the click.
                 return true;
             }
@@ -4092,8 +4119,8 @@
             if (onClickListener == null) {
                 final Intent intent = assistMenuItem.getIntent();
                 if (intent != null) {
-                    onClickListener = TextClassification.createStartActivityOnClickListener(
-                            mTextView.getContext(), intent);
+                    onClickListener = TextClassification.createIntentOnClickListener(
+                            TextClassification.createPendingIntent(mTextView.getContext(), intent));
                 }
             }
             if (onClickListener != null) {
@@ -4104,6 +4131,12 @@
             return true;
         }
 
+        private boolean shouldEnableAssistMenuItems() {
+            return mTextView.isDeviceProvisioned()
+                && TextClassificationManager.getSettings(mTextView.getContext())
+                        .isSmartTextShareEnabled();
+        }
+
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
             getSelectionActionModeHelper().onSelectionAction(item.getItemId());
@@ -4146,6 +4179,7 @@
             }
 
             mAssistClickHandlers.clear();
+            mRequestingLinkActionMode = false;
         }
 
         @Override
@@ -4171,7 +4205,7 @@
                         primaryHorizontal,
                         layout.getLineTop(line),
                         primaryHorizontal,
-                        layout.getLineBottom(line) - layout.getLineBottom(line) + mHandleHeight);
+                        layout.getLineBottom(line) + mHandleHeight);
             }
             // Take TextView's padding and scroll into account.
             int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
@@ -4290,6 +4324,88 @@
         }
     }
 
+    private static class MagnifierMotionAnimator {
+        private static final long DURATION = 100 /* miliseconds */;
+
+        // The magnifier being animated.
+        private final Magnifier mMagnifier;
+        // A value animator used to animate the magnifier.
+        private final ValueAnimator mAnimator;
+
+        // Whether the magnifier is currently visible.
+        private boolean mMagnifierIsShowing;
+        // The coordinates of the magnifier when the currently running animation started.
+        private float mAnimationStartX;
+        private float mAnimationStartY;
+        // The coordinates of the magnifier in the latest animation frame.
+        private float mAnimationCurrentX;
+        private float mAnimationCurrentY;
+        // The latest coordinates the motion animator was asked to #show() the magnifier at.
+        private float mLastX;
+        private float mLastY;
+
+        private MagnifierMotionAnimator(final Magnifier magnifier) {
+            mMagnifier = magnifier;
+            // Prepare the animator used to run the motion animation.
+            mAnimator = ValueAnimator.ofFloat(0, 1);
+            mAnimator.setDuration(DURATION);
+            mAnimator.setInterpolator(new LinearInterpolator());
+            mAnimator.addUpdateListener((animation) -> {
+                // Interpolate to find the current position of the magnifier.
+                mAnimationCurrentX = mAnimationStartX
+                        + (mLastX - mAnimationStartX) * animation.getAnimatedFraction();
+                mAnimationCurrentY = mAnimationStartY
+                        + (mLastY - mAnimationStartY) * animation.getAnimatedFraction();
+                mMagnifier.show(mAnimationCurrentX, mAnimationCurrentY);
+            });
+        }
+
+        /**
+         * Shows the magnifier at a new position.
+         * If the y coordinate is different from the previous y coordinate
+         * (probably corresponding to a line jump in the text), a short
+         * animation is added to the jump.
+         */
+        private void show(final float x, final float y) {
+            final boolean startNewAnimation = mMagnifierIsShowing && y != mLastY;
+
+            if (startNewAnimation) {
+                if (mAnimator.isRunning()) {
+                    mAnimator.cancel();
+                    mAnimationStartX = mAnimationCurrentX;
+                    mAnimationStartY = mAnimationCurrentY;
+                } else {
+                    mAnimationStartX = mLastX;
+                    mAnimationStartY = mLastY;
+                }
+                mAnimator.start();
+            } else {
+                if (!mAnimator.isRunning()) {
+                    mMagnifier.show(x, y);
+                }
+            }
+            mLastX = x;
+            mLastY = y;
+            mMagnifierIsShowing = true;
+        }
+
+        /**
+         * Updates the content of the magnifier.
+         */
+        private void update() {
+            mMagnifier.update();
+        }
+
+        /**
+         * Dismisses the magnifier, or does nothing if it is already dismissed.
+         */
+        private void dismiss() {
+            mMagnifier.dismiss();
+            mAnimator.cancel();
+            mMagnifierIsShowing = false;
+        }
+    }
+
     @VisibleForTesting
     public abstract class HandleView extends View implements TextViewPositionListener {
         protected Drawable mDrawable;
@@ -4469,8 +4585,8 @@
             return mContainer.isShowing();
         }
 
-        private boolean isVisible() {
-            // Always show a dragging handle.
+        private boolean shouldShow() {
+            // A dragging handle should always be shown.
             if (mIsDragging) {
                 return true;
             }
@@ -4483,6 +4599,10 @@
                     mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
         }
 
+        private void setVisible(final boolean visible) {
+            mContainer.getContentView().setVisibility(visible ? VISIBLE : INVISIBLE);
+        }
+
         public abstract int getCurrentCursorOffset();
 
         protected abstract void updateSelection(int offset);
@@ -4576,7 +4696,7 @@
                     onHandleMoved();
                 }
 
-                if (isVisible()) {
+                if (shouldShow()) {
                     // Transform to the window coordinates to follow the view tranformation.
                     final int[] pts = { mPositionX + mHotspotX + getHorizontalOffset(), mPositionY};
                     mTextView.transformFromViewToWindowSpace(pts);
@@ -4629,49 +4749,134 @@
             return 0;
         }
 
-        protected final void showMagnifier(@NonNull final MotionEvent event) {
-            if (mMagnifier == null) {
-                return;
-            }
+        private boolean tooLargeTextForMagnifier() {
+            final float magnifierContentHeight = Math.round(
+                    mMagnifierAnimator.mMagnifier.getHeight()
+                            / mMagnifierAnimator.mMagnifier.getZoom());
+            final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
+            final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
+            return glyphHeight > magnifierContentHeight;
+        }
+
+        /**
+         * Computes the position where the magnifier should be shown, relative to
+         * {@code mTextView}, and writes them to {@code showPosInView}. Also decides
+         * whether the magnifier should be shown or dismissed after this touch event.
+         * @return Whether the magnifier should be shown at the computed coordinates or dismissed.
+         */
+        private boolean obtainMagnifierShowCoordinates(@NonNull final MotionEvent event,
+                final PointF showPosInView) {
 
             final int trigger = getMagnifierHandleTrigger();
             final int offset;
+            final int otherHandleOffset;
             switch (trigger) {
-                case MagnifierHandleTrigger.INSERTION: // Fall through.
+                case MagnifierHandleTrigger.INSERTION:
+                    offset = mTextView.getSelectionStart();
+                    otherHandleOffset = -1;
+                    break;
                 case MagnifierHandleTrigger.SELECTION_START:
                     offset = mTextView.getSelectionStart();
+                    otherHandleOffset = mTextView.getSelectionEnd();
                     break;
                 case MagnifierHandleTrigger.SELECTION_END:
                     offset = mTextView.getSelectionEnd();
+                    otherHandleOffset = mTextView.getSelectionStart();
                     break;
                 default:
                     offset = -1;
+                    otherHandleOffset = -1;
                     break;
             }
 
             if (offset == -1) {
-                dismissMagnifier();
+                return false;
             }
 
             final Layout layout = mTextView.getLayout();
             final int lineNumber = layout.getLineForOffset(offset);
-            // Horizontally move the magnifier smoothly.
+            // Compute whether the selection handles are currently on the same line, and,
+            // in this particular case, whether the selected text is right to left.
+            final boolean sameLineSelection = otherHandleOffset != -1
+                    && lineNumber == layout.getLineForOffset(otherHandleOffset);
+            final boolean rtl = sameLineSelection
+                    && (offset < otherHandleOffset)
+                        != (getHorizontal(mTextView.getLayout(), offset)
+                            < getHorizontal(mTextView.getLayout(), otherHandleOffset));
+
+            // Horizontally move the magnifier smoothly, clamp inside the current line / selection.
             final int[] textViewLocationOnScreen = new int[2];
             mTextView.getLocationOnScreen(textViewLocationOnScreen);
-            final float xPosInView = event.getRawX() - textViewLocationOnScreen[0];
+            final float touchXInView = event.getRawX() - textViewLocationOnScreen[0];
+            float leftBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
+            float rightBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
+            if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_END) ^ rtl)) {
+                leftBound += getHorizontal(mTextView.getLayout(), otherHandleOffset);
+            } else {
+                leftBound += mTextView.getLayout().getLineLeft(lineNumber);
+            }
+            if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_START) ^ rtl)) {
+                rightBound += getHorizontal(mTextView.getLayout(), otherHandleOffset);
+            } else {
+                rightBound += mTextView.getLayout().getLineRight(lineNumber);
+            }
+            final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth()
+                    / mMagnifierAnimator.mMagnifier.getZoom());
+            if (touchXInView < leftBound - contentWidth / 2
+                    || touchXInView > rightBound + contentWidth / 2) {
+                // The touch is too far from the current line / selection, so hide the magnifier.
+                return false;
+            }
+            showPosInView.x = Math.max(leftBound, Math.min(rightBound, touchXInView));
+
             // Vertically snap to middle of current line.
-            final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber)
+            showPosInView.y = (mTextView.getLayout().getLineTop(lineNumber)
                     + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
                     + mTextView.getTotalPaddingTop() - mTextView.getScrollY();
 
-            suspendBlink();
-            mMagnifier.show(xPosInView, yPosInView);
+            return true;
+        }
+
+        private boolean handleOverlapsMagnifier() {
+            final int handleY = mContainer.getDecorViewLayoutParams().y;
+            final int magnifierBottomWhenAtWindowTop =
+                    mTextView.getRootWindowInsets().getSystemWindowInsetTop()
+                        + mMagnifierAnimator.mMagnifier.getHeight();
+            return handleY <= magnifierBottomWhenAtWindowTop;
+        }
+
+        protected final void updateMagnifier(@NonNull final MotionEvent event) {
+            if (mMagnifierAnimator == null) {
+                return;
+            }
+
+            final PointF showPosInView = new PointF();
+            final boolean shouldShow = !tooLargeTextForMagnifier()
+                    && obtainMagnifierShowCoordinates(event, showPosInView);
+            if (shouldShow) {
+                // Make the cursor visible and stop blinking.
+                mRenderCursorRegardlessTiming = true;
+                mTextView.invalidateCursorPath();
+                suspendBlink();
+                // Hide handle if it overlaps the magnifier.
+                if (handleOverlapsMagnifier()) {
+                    setVisible(false);
+                } else {
+                    setVisible(true);
+                }
+
+                mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
+            } else {
+                dismissMagnifier();
+            }
         }
 
         protected final void dismissMagnifier() {
-            if (mMagnifier != null) {
-                mMagnifier.dismiss();
+            if (mMagnifierAnimator != null) {
+                mMagnifierAnimator.dismiss();
+                mRenderCursorRegardlessTiming = false;
                 resumeBlink();
+                setVisible(true);
             }
         }
 
@@ -4853,11 +5058,11 @@
                 case MotionEvent.ACTION_DOWN:
                     mDownPositionX = ev.getRawX();
                     mDownPositionY = ev.getRawY();
-                    showMagnifier(ev);
+                    updateMagnifier(ev);
                     break;
 
                 case MotionEvent.ACTION_MOVE:
-                    showMagnifier(ev);
+                    updateMagnifier(ev);
                     break;
 
                 case MotionEvent.ACTION_UP:
@@ -5211,11 +5416,11 @@
                     // re-engages the handle.
                     mTouchWordDelta = 0.0f;
                     mPrevX = UNSET_X_VALUE;
-                    showMagnifier(event);
+                    updateMagnifier(event);
                     break;
 
                 case MotionEvent.ACTION_MOVE:
-                    showMagnifier(event);
+                    updateMagnifier(event);
                     break;
 
                 case MotionEvent.ACTION_UP:
diff --git a/android/widget/GridLayout.java b/android/widget/GridLayout.java
index 012b918..3aae849 100644
--- a/android/widget/GridLayout.java
+++ b/android/widget/GridLayout.java
@@ -904,6 +904,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @Override
     protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
         // Apply defaults, so as to remove UNDEFINED values
@@ -919,6 +922,9 @@
         }
     }
 
+    /**
+     * @hide
+     */
     @Override
     protected void onDebugDraw(Canvas canvas) {
         Paint paint = new Paint();
diff --git a/android/widget/ImageView.java b/android/widget/ImageView.java
index 1dc5b44..4b951fa 100644
--- a/android/widget/ImageView.java
+++ b/android/widget/ImageView.java
@@ -23,10 +23,12 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -53,7 +55,6 @@
 import com.android.internal.R;
 
 import java.io.IOException;
-import java.io.InputStream;
 
 /**
  * Displays image resources, for example {@link android.graphics.Bitmap}
@@ -946,21 +947,15 @@
             }
         } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                 || ContentResolver.SCHEME_FILE.equals(scheme)) {
-            InputStream stream = null;
             try {
-                stream = mContext.getContentResolver().openInputStream(uri);
-                return Drawable.createFromResourceStream(sCompatUseCorrectStreamDensity
-                        ? getResources() : null, null, stream, null);
-            } catch (Exception e) {
+                Resources res = sCompatUseCorrectStreamDensity ? getResources() : null;
+                ImageDecoder.Source src = ImageDecoder.createSource(mContext.getContentResolver(),
+                        uri, res);
+                return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
+            } catch (IOException e) {
                 Log.w(LOG_TAG, "Unable to open content: " + uri, e);
-            } finally {
-                if (stream != null) {
-                    try {
-                        stream.close();
-                    } catch (IOException e) {
-                        Log.w(LOG_TAG, "Unable to close content: " + uri, e);
-                    }
-                }
             }
         } else {
             return Drawable.createFromPath(uri.toString());
diff --git a/android/widget/LinearLayout.java b/android/widget/LinearLayout.java
index 7ea1f1e..d32e93c 100644
--- a/android/widget/LinearLayout.java
+++ b/android/widget/LinearLayout.java
@@ -917,7 +917,7 @@
         // measurement on any children, we need to measure them now.
         int remainingExcess = heightSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
-        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+        if (skippedMeasure || totalWeight > 0.0f) {
             float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             mTotalLength = 0;
@@ -1300,7 +1300,7 @@
         // measurement on any children, we need to measure them now.
         int remainingExcess = widthSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
-        if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+        if (skippedMeasure || totalWeight > 0.0f) {
             float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
diff --git a/android/widget/Magnifier.java b/android/widget/Magnifier.java
index 310b170..5eb6699 100644
--- a/android/widget/Magnifier.java
+++ b/android/widget/Magnifier.java
@@ -19,21 +19,38 @@
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UiThread;
 import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.view.Gravity;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.DisplayListCanvas;
 import android.view.LayoutInflater;
 import android.view.PixelCopy;
+import android.view.RenderNode;
 import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
 import android.view.SurfaceView;
+import android.view.ThreadedRenderer;
 import android.view.View;
-import android.view.ViewParent;
+import android.view.ViewRootImpl;
 
+import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
 /**
@@ -43,33 +60,45 @@
 public final class Magnifier {
     // Use this to specify that a previous configuration value does not exist.
     private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+    // The callbacks of the pixel copy requests will be invoked on
+    // the Handler of this Thread when the copy is finished.
+    private static final HandlerThread sPixelCopyHandlerThread =
+            new HandlerThread("magnifier pixel copy result handler");
+
     // The view to which this magnifier is attached.
     private final View mView;
     // The coordinates of the view in the surface.
     private final int[] mViewCoordinatesInSurface;
     // The window containing the magnifier.
-    private final PopupWindow mWindow;
+    private InternalPopupWindow mWindow;
     // The center coordinates of the window containing the magnifier.
     private final Point mWindowCoords = new Point();
     // The width of the window containing the magnifier.
     private final int mWindowWidth;
     // The height of the window containing the magnifier.
     private final int mWindowHeight;
-    // The bitmap used to display the contents of the magnifier.
-    private final Bitmap mBitmap;
+    // The zoom applied to the view region copied to the magnifier window.
+    private final float mZoom;
+    // The width of the bitmaps where the magnifier content is copied.
+    private final int mBitmapWidth;
+    // The height of the bitmaps where the magnifier content is copied.
+    private final int mBitmapHeight;
+    // The elevation of the window containing the magnifier.
+    private final float mWindowElevation;
+    // The corner radius of the window containing the magnifier.
+    private final float mWindowCornerRadius;
     // The center coordinates of the content that is to be magnified.
     private final Point mCenterZoomCoords = new Point();
-    // The callback of the pixel copy request will be invoked on this Handler when
-    // the copy is finished.
-    private final Handler mPixelCopyHandler = Handler.getMain();
-    // Current magnification scale.
-    private final float mZoomScale;
     // Variables holding previous states, used for detecting redundant calls and invalidation.
     private final Point mPrevStartCoordsInSurface = new Point(
             NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
     private final PointF mPrevPosInView = new PointF(
             NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+    // Rectangle defining the view surface area we pixel copy content from.
     private final Rect mPixelCopyRequestRect = new Rect();
+    // Lock to synchronize between the UI thread and the thread that handles pixel copy results.
+    // Only sync mWindow writes from UI thread with mWindow reads from sPixelCopyHandlerThread.
+    private final Object mLock = new Object();
 
     /**
      * Initializes a magnifier.
@@ -79,32 +108,36 @@
     public Magnifier(@NonNull View view) {
         mView = Preconditions.checkNotNull(view);
         final Context context = mView.getContext();
-        final float elevation = context.getResources().getDimension(
-                com.android.internal.R.dimen.magnifier_elevation);
-        final View content = LayoutInflater.from(context).inflate(
-                com.android.internal.R.layout.magnifier, null);
-        content.findViewById(com.android.internal.R.id.magnifier_inner).setClipToOutline(true);
-        mWindowWidth = context.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.magnifier_width);
-        mWindowHeight = context.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.magnifier_height);
-        mZoomScale = context.getResources().getFloat(
-                com.android.internal.R.dimen.magnifier_zoom_scale);
+        final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null);
+        content.findViewById(R.id.magnifier_inner).setClipToOutline(true);
+        mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width);
+        mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height);
+        mWindowElevation = context.getResources().getDimension(R.dimen.magnifier_elevation);
+        mWindowCornerRadius = getDeviceDefaultDialogCornerRadius();
+        mZoom = context.getResources().getFloat(R.dimen.magnifier_zoom_scale);
+        mBitmapWidth = Math.round(mWindowWidth / mZoom);
+        mBitmapHeight = Math.round(mWindowHeight / mZoom);
         // The view's surface coordinates will not be updated until the magnifier is first shown.
         mViewCoordinatesInSurface = new int[2];
+    }
 
-        mWindow = new PopupWindow(context);
-        mWindow.setContentView(content);
-        mWindow.setWidth(mWindowWidth);
-        mWindow.setHeight(mWindowHeight);
-        mWindow.setElevation(elevation);
-        mWindow.setTouchable(false);
-        mWindow.setBackgroundDrawable(null);
+    static {
+        sPixelCopyHandlerThread.start();
+    }
 
-        final int bitmapWidth = Math.round(mWindowWidth / mZoomScale);
-        final int bitmapHeight = Math.round(mWindowHeight / mZoomScale);
-        mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
-        getImageView().setImageBitmap(mBitmap);
+    /**
+     * Returns the device default theme dialog corner radius attribute.
+     * We retrieve this from the device default theme to avoid
+     * using the values set in the custom application themes.
+     */
+    private float getDeviceDefaultDialogCornerRadius() {
+        final Context deviceDefaultContext =
+                new ContextThemeWrapper(mView.getContext(), R.style.Theme_DeviceDefault);
+        final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
+                new int[]{android.R.attr.dialogCornerRadius});
+        final float dialogCornerRadius = ta.getDimension(0, 0);
+        ta.recycle();
+        return dialogCornerRadius;
     }
 
     /**
@@ -124,49 +157,28 @@
 
         configureCoordinates(xPosInView, yPosInView);
 
-        // Clamp startX value to avoid distorting the rendering of the magnifier content.
-        // For this, we compute:
-        // - zeroScrollXInSurface: this is the start x of mView, where this is not masked by a
-        //                         potential scrolling container. For example, if mView is a
-        //                         TextView contained in a HorizontalScrollView,
-        //                         mViewCoordinatesInSurface will reflect the surface position of
-        //                         the first text character, rather than the position of the first
-        //                         visible one. Therefore, we need to add back the amount of
-        //                         scrolling from the parent containers.
-        // - actualWidth: similarly, the width of a View will be larger than its actually visible
-        //                width when it is contained in a scrolling container. We need to use
-        //                the minimum width of a scrolling container which contains this view.
-        int zeroScrollXInSurface = mViewCoordinatesInSurface[0];
-        int actualWidth = mView.getWidth();
-        ViewParent viewParent = mView.getParent();
-        while (viewParent instanceof View) {
-            final View container = (View) viewParent;
-            if (container.canScrollHorizontally(-1 /* left scroll */)
-                    || container.canScrollHorizontally(1 /* right scroll */)) {
-                zeroScrollXInSurface += container.getScrollX();
-                actualWidth = Math.min(actualWidth, container.getWidth()
-                        - container.getPaddingLeft() - container.getPaddingRight());
-            }
-            viewParent = viewParent.getParent();
-        }
-
-        final int startX = Math.max(zeroScrollXInSurface, Math.min(
-                mCenterZoomCoords.x - mBitmap.getWidth() / 2,
-                zeroScrollXInSurface + actualWidth - mBitmap.getWidth()));
-        final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
+        // Clamp the startX location to avoid magnifying content which does not belong
+        // to the magnified view. This will not take into account overlapping views.
+        final Rect viewVisibleRegion = new Rect();
+        mView.getGlobalVisibleRect(viewVisibleRegion);
+        final int startX = Math.max(viewVisibleRegion.left, Math.min(
+                mCenterZoomCoords.x - mBitmapWidth / 2,
+                viewVisibleRegion.right - mBitmapWidth));
+        final int startY = mCenterZoomCoords.y - mBitmapHeight / 2;
 
         if (xPosInView != mPrevPosInView.x || yPosInView != mPrevPosInView.y) {
-            performPixelCopy(startX, startY);
-
+            if (mWindow == null) {
+                synchronized (mLock) {
+                    mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
+                            getValidViewSurface(),
+                            mWindowWidth, mWindowHeight, mWindowElevation, mWindowCornerRadius,
+                            Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
+                            mCallback);
+                }
+            }
+            performPixelCopy(startX, startY, true /* update window position */);
             mPrevPosInView.x = xPosInView;
             mPrevPosInView.y = yPosInView;
-
-            if (mWindow.isShowing()) {
-                mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
-                        mWindow.getHeight());
-            } else {
-                mWindow.showAtLocation(mView, Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y);
-            }
         }
     }
 
@@ -174,63 +186,56 @@
      * Dismisses the magnifier from the screen. Calling this on a dismissed magnifier is a no-op.
      */
     public void dismiss() {
-        mWindow.dismiss();
+        if (mWindow != null) {
+            synchronized (mLock) {
+                mWindow.destroy();
+                mWindow = null;
+            }
+            mPrevPosInView.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            mPrevPosInView.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            mPrevStartCoordsInSurface.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            mPrevStartCoordsInSurface.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+        }
     }
 
     /**
      * Forces the magnifier to update its content. It uses the previous coordinates passed to
      * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
-     *
-     * @hide
      */
     public void update() {
-        if (mWindow.isShowing()) {
-            // Update the contents shown in the magnifier.
-            performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y);
+        if (mWindow != null) {
+            // Update the content shown in the magnifier.
+            performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y,
+                    false /* update window position */);
         }
     }
 
-    private void configureCoordinates(float xPosInView, float yPosInView) {
-        final float posX;
-        final float posY;
-
-        if (mView instanceof SurfaceView) {
-            // No offset required if the backing Surface matches the size of the SurfaceView.
-            posX = xPosInView;
-            posY = yPosInView;
-        } else {
-            mView.getLocationInSurface(mViewCoordinatesInSurface);
-            posX = xPosInView + mViewCoordinatesInSurface[0];
-            posY = yPosInView + mViewCoordinatesInSurface[1];
-        }
-
-        mCenterZoomCoords.x = Math.round(posX);
-        mCenterZoomCoords.y = Math.round(posY);
-
-        final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.magnifier_offset);
-        mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2;
-        mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalMagnifierOffset;
+    /**
+     * @return The width of the magnifier window, in pixels.
+     */
+    public int getWidth() {
+        return mWindowWidth;
     }
 
-    private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
-        final Surface surface = getValidViewSurface();
-        if (surface != null) {
-            mPixelCopyRequestRect.set(startXInSurface, startYInSurface,
-                    startXInSurface + mBitmap.getWidth(), startYInSurface + mBitmap.getHeight());
+    /**
+     * @return The height of the magnifier window, in pixels.
+     */
+    public int getHeight() {
+        return mWindowHeight;
+    }
 
-            PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
-                    result -> {
-                        getImageView().invalidate();
-                        mPrevStartCoordsInSurface.x = startXInSurface;
-                        mPrevStartCoordsInSurface.y = startYInSurface;
-                    },
-                    mPixelCopyHandler);
-        }
+    /**
+     * @return The zoom applied to the magnified view region copied to the magnifier window.
+     * If the zoom is x and the magnifier window size is (width, height), the original size
+     * of the content copied in the magnifier will be (width / x, height / x).
+     */
+    public float getZoom() {
+        return mZoom;
     }
 
     @Nullable
     private Surface getValidViewSurface() {
+        // TODO: deduplicate this against the first part of #performPixelCopy
         final Surface surface;
         if (mView instanceof SurfaceView) {
             surface = ((SurfaceView) mView).getHolder().getSurface();
@@ -243,8 +248,443 @@
         return (surface != null && surface.isValid()) ? surface : null;
     }
 
-    private ImageView getImageView() {
-        return mWindow.getContentView().findViewById(
-                com.android.internal.R.id.magnifier_image);
+    private void configureCoordinates(final float xPosInView, final float yPosInView) {
+        // Compute the coordinates of the center of the content going to be displayed in the
+        // magnifier. These are relative to the surface the content is copied from.
+        final float posX;
+        final float posY;
+        if (mView instanceof SurfaceView) {
+            // No offset required if the backing Surface matches the size of the SurfaceView.
+            posX = xPosInView;
+            posY = yPosInView;
+        } else {
+            mView.getLocationInSurface(mViewCoordinatesInSurface);
+            posX = xPosInView + mViewCoordinatesInSurface[0];
+            posY = yPosInView + mViewCoordinatesInSurface[1];
+        }
+        mCenterZoomCoords.x = Math.round(posX);
+        mCenterZoomCoords.y = Math.round(posY);
+
+        // Compute the position of the magnifier window. Again, this has to be relative to the
+        // surface of the magnified view, as this surface is the parent of the magnifier surface.
+        final int verticalOffset = mView.getContext().getResources().getDimensionPixelSize(
+                R.dimen.magnifier_offset);
+        mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2;
+        mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalOffset;
+    }
+
+    private void performPixelCopy(final int startXInSurface, final int startYInSurface,
+            final boolean updateWindowPosition) {
+        // Get the view surface where the content will be copied from.
+        final Surface surface;
+        final int surfaceWidth;
+        final int surfaceHeight;
+        if (mView instanceof SurfaceView) {
+            final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder();
+            surface = surfaceHolder.getSurface();
+            surfaceWidth = surfaceHolder.getSurfaceFrame().right;
+            surfaceHeight = surfaceHolder.getSurfaceFrame().bottom;
+        } else if (mView.getViewRootImpl() != null) {
+            final ViewRootImpl viewRootImpl = mView.getViewRootImpl();
+            surface = viewRootImpl.mSurface;
+            surfaceWidth = viewRootImpl.getWidth();
+            surfaceHeight = viewRootImpl.getHeight();
+        } else {
+            surface = null;
+            surfaceWidth = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+            surfaceHeight = NONEXISTENT_PREVIOUS_CONFIG_VALUE;
+        }
+
+        if (surface == null || !surface.isValid()) {
+            return;
+        }
+
+        // Clamp copy coordinates inside the surface to avoid displaying distorted content.
+        final int clampedStartXInSurface = Math.max(0,
+                Math.min(startXInSurface, surfaceWidth - mBitmapWidth));
+        final int clampedStartYInSurface = Math.max(0,
+                Math.min(startYInSurface, surfaceHeight - mBitmapHeight));
+
+        // Clamp window coordinates inside the parent surface, to avoid displaying
+        // the magnifier out of screen or overlapping with system insets.
+        final Rect insets = mView.getRootWindowInsets().getSystemWindowInsets();
+        final int windowCoordsX = Math.max(insets.left,
+                Math.min(surfaceWidth - mWindowWidth - insets.right, mWindowCoords.x));
+        final int windowCoordsY = Math.max(insets.top,
+                Math.min(surfaceHeight - mWindowHeight - insets.bottom, mWindowCoords.y));
+
+        // Perform the pixel copy.
+        mPixelCopyRequestRect.set(clampedStartXInSurface,
+                clampedStartYInSurface,
+                clampedStartXInSurface + mBitmapWidth,
+                clampedStartYInSurface + mBitmapHeight);
+        final InternalPopupWindow currentWindowInstance = mWindow;
+        final Bitmap bitmap =
+                Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
+        PixelCopy.request(surface, mPixelCopyRequestRect, bitmap,
+                result -> {
+                    synchronized (mLock) {
+                        if (mWindow != currentWindowInstance) {
+                            // The magnifier was dismissed (and maybe shown again) in the meantime.
+                            return;
+                        }
+                        if (updateWindowPosition) {
+                            // TODO: pull the position update outside #performPixelCopy
+                            mWindow.setContentPositionForNextDraw(windowCoordsX, windowCoordsY);
+                        }
+                        mWindow.updateContent(bitmap);
+                    }
+                },
+                sPixelCopyHandlerThread.getThreadHandler());
+        mPrevStartCoordsInSurface.x = startXInSurface;
+        mPrevStartCoordsInSurface.y = startYInSurface;
+    }
+
+    /**
+     * Magnifier's own implementation of PopupWindow-similar floating window.
+     * This exists to ensure frame-synchronization between window position updates and window
+     * content updates. By using a PopupWindow, these events would happen in different frames,
+     * producing a shakiness effect for the magnifier content.
+     */
+    private static class InternalPopupWindow {
+        // The alpha set on the magnifier's content, which defines how
+        // prominent the white background is.
+        private static final int CONTENT_BITMAP_ALPHA = 242;
+
+        // Display associated to the view the magnifier is attached to.
+        private final Display mDisplay;
+        // The size of the content of the magnifier.
+        private final int mContentWidth;
+        private final int mContentHeight;
+        // The size of the allocated surface.
+        private final int mSurfaceWidth;
+        private final int mSurfaceHeight;
+        // The insets of the content inside the allocated surface.
+        private final int mOffsetX;
+        private final int mOffsetY;
+        // The surface we allocate for the magnifier content + shadow.
+        private final SurfaceSession mSurfaceSession;
+        private final SurfaceControl mSurfaceControl;
+        private final Surface mSurface;
+        // The renderer used for the allocated surface.
+        private final ThreadedRenderer.SimpleRenderer mRenderer;
+        // The RenderNode used to draw the magnifier content in the surface.
+        private final RenderNode mBitmapRenderNode;
+        // The job that will be post'd to apply the pending magnifier updates to the surface.
+        private final Runnable mMagnifierUpdater;
+        // The handler where the magnifier updater jobs will be post'd.
+        private final Handler mHandler;
+        // The callback to be run after the next draw. Only used for testing.
+        private Callback mCallback;
+
+        // Members below describe the state of the magnifier. Reads/writes to them
+        // have to be synchronized between the UI thread and the thread that handles
+        // the pixel copy results. This is the purpose of mLock.
+        private final Object mLock;
+        // Whether a magnifier frame draw is currently pending in the UI thread queue.
+        private boolean mFrameDrawScheduled;
+        // The content bitmap.
+        private Bitmap mBitmap;
+        // Whether the next draw will be the first one for the current instance.
+        private boolean mFirstDraw = true;
+        // The window position in the parent surface. Might be applied during the next draw,
+        // when mPendingWindowPositionUpdate is true.
+        private int mWindowPositionX;
+        private int mWindowPositionY;
+        private boolean mPendingWindowPositionUpdate;
+
+        // The lock used to synchronize the UI and render threads when a #destroy
+        // is performed on the UI thread and a frame callback on the render thread.
+        // When both mLock and mDestroyLock need to be held at the same time,
+        // mDestroyLock should be acquired before mLock in order to avoid deadlocks.
+        private final Object mDestroyLock = new Object();
+
+        InternalPopupWindow(final Context context, final Display display,
+                final Surface parentSurface,
+                final int width, final int height, final float elevation, final float cornerRadius,
+                final Handler handler, final Object lock, final Callback callback) {
+            mDisplay = display;
+            mLock = lock;
+            mCallback = callback;
+
+            mContentWidth = width;
+            mContentHeight = height;
+            mOffsetX = (int) (0.1f * width);
+            mOffsetY = (int) (0.1f * height);
+            // Setup the surface we will use for drawing the content and shadow.
+            mSurfaceWidth = mContentWidth + 2 * mOffsetX;
+            mSurfaceHeight = mContentHeight + 2 * mOffsetY;
+            mSurfaceSession = new SurfaceSession(parentSurface);
+            mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+                    .setFormat(PixelFormat.TRANSLUCENT)
+                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                    .setName("magnifier surface")
+                    .setFlags(SurfaceControl.HIDDEN)
+                    .build();
+            mSurface = new Surface();
+            mSurface.copyFrom(mSurfaceControl);
+
+            // Setup the RenderNode tree. The root has only one child, which contains the bitmap.
+            mRenderer = new ThreadedRenderer.SimpleRenderer(
+                    context,
+                    "magnifier renderer",
+                    mSurface
+            );
+            mBitmapRenderNode = createRenderNodeForBitmap(
+                    "magnifier content",
+                    elevation,
+                    cornerRadius
+            );
+
+            final DisplayListCanvas canvas = mRenderer.getRootNode().start(width, height);
+            try {
+                canvas.insertReorderBarrier();
+                canvas.drawRenderNode(mBitmapRenderNode);
+                canvas.insertInorderBarrier();
+            } finally {
+                mRenderer.getRootNode().end(canvas);
+            }
+
+            // Initialize the update job and the handler where this will be post'd.
+            mHandler = handler;
+            mMagnifierUpdater = this::doDraw;
+            mFrameDrawScheduled = false;
+        }
+
+        private RenderNode createRenderNodeForBitmap(final String name,
+                final float elevation, final float cornerRadius) {
+            final RenderNode bitmapRenderNode = RenderNode.create(name, null);
+
+            // Define the position of the bitmap in the parent render node. The surface regions
+            // outside the bitmap are used to draw elevation.
+            bitmapRenderNode.setLeftTopRightBottom(mOffsetX, mOffsetY,
+                    mOffsetX + mContentWidth, mOffsetY + mContentHeight);
+            bitmapRenderNode.setElevation(elevation);
+
+            final Outline outline = new Outline();
+            outline.setRoundRect(0, 0, mContentWidth, mContentHeight, cornerRadius);
+            outline.setAlpha(1.0f);
+            bitmapRenderNode.setOutline(outline);
+            bitmapRenderNode.setClipToOutline(true);
+
+            // Create a dummy draw, which will be replaced later with real drawing.
+            final DisplayListCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight);
+            try {
+                canvas.drawColor(0xFF00FF00);
+            } finally {
+                bitmapRenderNode.end(canvas);
+            }
+
+            return bitmapRenderNode;
+        }
+
+        /**
+         * Sets the position of the magnifier content relative to the parent surface.
+         * The position update will happen in the same frame with the next draw.
+         * The method has to be called in a context that holds {@link #mLock}.
+         *
+         * @param contentX the x coordinate of the content
+         * @param contentY the y coordinate of the content
+         */
+        public void setContentPositionForNextDraw(final int contentX, final int contentY) {
+            mWindowPositionX = contentX - mOffsetX;
+            mWindowPositionY = contentY - mOffsetY;
+            mPendingWindowPositionUpdate = true;
+            requestUpdate();
+        }
+
+        /**
+         * Sets the content that should be displayed in the magnifier.
+         * The update happens immediately, and possibly triggers a pending window movement set
+         * by {@link #setContentPositionForNextDraw(int, int)}.
+         * The method has to be called in a context that holds {@link #mLock}.
+         *
+         * @param bitmap the content bitmap
+         */
+        public void updateContent(final @NonNull Bitmap bitmap) {
+            if (mBitmap != null) {
+                mBitmap.recycle();
+            }
+            mBitmap = bitmap;
+            requestUpdate();
+        }
+
+        private void requestUpdate() {
+            if (mFrameDrawScheduled) {
+                return;
+            }
+            final Message request = Message.obtain(mHandler, mMagnifierUpdater);
+            request.setAsynchronous(true);
+            request.sendToTarget();
+            mFrameDrawScheduled = true;
+        }
+
+        /**
+         * Destroys this instance.
+         */
+        public void destroy() {
+            synchronized (mDestroyLock) {
+                mSurface.destroy();
+            }
+            synchronized (mLock) {
+                mRenderer.destroy();
+                mSurfaceControl.destroy();
+                mSurfaceSession.kill();
+                mBitmapRenderNode.destroy();
+                mHandler.removeCallbacks(mMagnifierUpdater);
+                if (mBitmap != null) {
+                    mBitmap.recycle();
+                }
+            }
+        }
+
+        private void doDraw() {
+            final ThreadedRenderer.FrameDrawingCallback callback;
+
+            // Draw the current bitmap to the surface, and prepare the callback which updates the
+            // surface position. These have to be in the same synchronized block, in order to
+            // guarantee the consistency between the bitmap content and the surface position.
+            synchronized (mLock) {
+                if (!mSurface.isValid()) {
+                    // Probably #destroy() was called for the current instance, so we skip the draw.
+                    return;
+                }
+
+                final DisplayListCanvas canvas =
+                        mBitmapRenderNode.start(mContentWidth, mContentHeight);
+                try {
+                    canvas.drawColor(Color.WHITE);
+
+                    final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+                    final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
+                    final Paint paint = new Paint();
+                    paint.setFilterBitmap(true);
+                    paint.setAlpha(CONTENT_BITMAP_ALPHA);
+                    canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
+                } finally {
+                    mBitmapRenderNode.end(canvas);
+                }
+
+                if (mPendingWindowPositionUpdate || mFirstDraw) {
+                    // If the window has to be shown or moved, defer this until the next draw.
+                    final boolean firstDraw = mFirstDraw;
+                    mFirstDraw = false;
+                    final boolean updateWindowPosition = mPendingWindowPositionUpdate;
+                    mPendingWindowPositionUpdate = false;
+                    final int pendingX = mWindowPositionX;
+                    final int pendingY = mWindowPositionY;
+
+                    callback = frame -> {
+                        synchronized (mDestroyLock) {
+                            if (!mSurface.isValid()) {
+                                return;
+                            }
+                            synchronized (mLock) {
+                                mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
+                                // Show or move the window at the content draw frame.
+                                SurfaceControl.openTransaction();
+                                mSurfaceControl.deferTransactionUntil(mSurface, frame);
+                                if (updateWindowPosition) {
+                                    mSurfaceControl.setPosition(pendingX, pendingY);
+                                }
+                                if (firstDraw) {
+                                    mSurfaceControl.show();
+                                }
+                                SurfaceControl.closeTransaction();
+                            }
+                        }
+                    };
+                } else {
+                    callback = null;
+                }
+
+                mFrameDrawScheduled = false;
+            }
+
+            mRenderer.draw(callback);
+            if (mCallback != null) {
+                mCallback.onOperationComplete();
+            }
+        }
+    }
+
+    // The rest of the file consists of test APIs.
+
+    /**
+     * See {@link #setOnOperationCompleteCallback(Callback)}.
+     */
+    @TestApi
+    private Callback mCallback;
+
+    /**
+     * Sets a callback which will be invoked at the end of the next
+     * {@link #show(float, float)} or {@link #update()} operation.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setOnOperationCompleteCallback(final Callback callback) {
+        mCallback = callback;
+        if (mWindow != null) {
+            mWindow.mCallback = callback;
+        }
+    }
+
+    /**
+     * @return the content being currently displayed in the magnifier, as bitmap
+     *
+     * @hide
+     */
+    @TestApi
+    public @Nullable Bitmap getContent() {
+        if (mWindow == null) {
+            return null;
+        }
+        synchronized (mWindow.mLock) {
+            return Bitmap.createScaledBitmap(mWindow.mBitmap, mWindowWidth, mWindowHeight, true);
+        }
+    }
+
+    /**
+     * @return the position of the magnifier window relative to the screen
+     *
+     * @hide
+     */
+    @TestApi
+    public Rect getWindowPositionOnScreen() {
+        final int[] viewLocationOnScreen = new int[2];
+        mView.getLocationOnScreen(viewLocationOnScreen);
+        final int[] viewLocationInSurface = new int[2];
+        mView.getLocationInSurface(viewLocationInSurface);
+
+        final int left = mWindowCoords.x + viewLocationOnScreen[0] - viewLocationInSurface[0];
+        final int top = mWindowCoords.y + viewLocationOnScreen[1] - viewLocationInSurface[1];
+        return new Rect(left, top, left + mWindowWidth, top + mWindowHeight);
+    }
+
+    /**
+     * @return the size of the magnifier window in dp
+     *
+     * @hide
+     */
+    @TestApi
+    public static PointF getMagnifierDefaultSize() {
+        final Resources resources = Resources.getSystem();
+        final float density = resources.getDisplayMetrics().density;
+        final PointF size = new PointF();
+        size.x = resources.getDimension(R.dimen.magnifier_width) / density;
+        size.y = resources.getDimension(R.dimen.magnifier_height) / density;
+        return size;
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public interface Callback {
+        /**
+         * Callback called after the drawing for a magnifier update has happened.
+         */
+        void onOperationComplete();
     }
 }
diff --git a/android/widget/MediaControlView2.java b/android/widget/MediaControlView2.java
index f1d633a..f52854a 100644
--- a/android/widget/MediaControlView2.java
+++ b/android/widget/MediaControlView2.java
@@ -20,18 +20,20 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.media.SessionToken2;
 import android.media.session.MediaController;
 import android.media.update.ApiLoader;
 import android.media.update.MediaControlView2Provider;
-import android.media.update.ViewProvider;
+import android.media.update.ViewGroupHelper;
 import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.View;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+
+// TODO: Use link annotation to refer VideoView2 once VideoView2 became unhidden.
 /**
+ * @hide
  * A View that contains the controls for MediaPlayer2.
  * It provides a wide range of UI including buttons such as "Play/Pause", "Rewind", "Fast Forward",
  * "Subtitle", "Full Screen", and it is also possible to add multiple custom buttons.
@@ -42,15 +44,24 @@
  * adds it to the view.
  * 2) Initialize MediaControlView2 programmatically and add it to a ViewGroup instance.
  *
- * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController2,
+ * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController,
  * which is necessary to communicate with MediaSession2. In the second option, however, the
- * developer needs to manually retrieve a MediaController2 instance and set it to MediaControlView2
- * by calling setController(MediaController2 controller).
+ * developer needs to manually retrieve a MediaController instance and set it to MediaControlView2
+ * by calling setController(MediaController controller).
  *
- * TODO PUBLIC API
- * @hide
+ * <p>
+ * There is no separate method that handles the show/hide behavior for MediaControlView2. Instead,
+ * one can directly change the visibility of this view by calling View.setVisibility(int). The
+ * values supported are View.VISIBLE and View.GONE.
+ * In addition, the following customization is supported:
+ * Set focus to the play/pause button by calling requestPlayButtonFocus().
+ *
+ * <p>
+ * It is also possible to add custom buttons with custom icons and actions inside MediaControlView2.
+ * Those buttons will be shown when the overflow button is clicked.
+ * See VideoView2#setCustomActions for more details on how to add.
  */
-public class MediaControlView2 extends FrameLayout {
+public class MediaControlView2 extends ViewGroupHelper<MediaControlView2Provider> {
     /** @hide */
     @IntDef({
             BUTTON_PLAY_PAUSE,
@@ -68,20 +79,62 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Button {}
 
+    /**
+     * MediaControlView2 button value for playing and pausing media.
+     * @hide
+     */
     public static final int BUTTON_PLAY_PAUSE = 1;
+    /**
+     * MediaControlView2 button value for jumping 30 seconds forward.
+     * @hide
+     */
     public static final int BUTTON_FFWD = 2;
+    /**
+     * MediaControlView2 button value for jumping 10 seconds backward.
+     * @hide
+     */
     public static final int BUTTON_REW = 3;
+    /**
+     * MediaControlView2 button value for jumping to next media.
+     * @hide
+     */
     public static final int BUTTON_NEXT = 4;
+    /**
+     * MediaControlView2 button value for jumping to previous media.
+     * @hide
+     */
     public static final int BUTTON_PREV = 5;
+    /**
+     * MediaControlView2 button value for showing/hiding subtitle track.
+     * @hide
+     */
     public static final int BUTTON_SUBTITLE = 6;
+    /**
+     * MediaControlView2 button value for toggling full screen.
+     * @hide
+     */
     public static final int BUTTON_FULL_SCREEN = 7;
+    /**
+     * MediaControlView2 button value for showing/hiding overflow buttons.
+     * @hide
+     */
     public static final int BUTTON_OVERFLOW = 8;
+    /**
+     * MediaControlView2 button value for muting audio.
+     * @hide
+     */
     public static final int BUTTON_MUTE = 9;
+    /**
+     * MediaControlView2 button value for adjusting aspect ratio of view.
+     * @hide
+     */
     public static final int BUTTON_ASPECT_RATIO = 10;
+    /**
+     * MediaControlView2 button value for showing/hiding settings page.
+     * @hide
+     */
     public static final int BUTTON_SETTINGS = 11;
 
-    private final MediaControlView2Provider mProvider;
-
     public MediaControlView2(@NonNull Context context) {
         this(context, null);
     }
@@ -91,189 +144,86 @@
     }
 
     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
-                            int defStyleAttr) {
+            int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
-                            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        mProvider = ApiLoader.getProvider(context)
-                .createMediaControlView2(this, new SuperProvider());
+            int defStyleAttr, int defStyleRes) {
+        super((instance, superProvider, privateProvider) ->
+                ApiLoader.getProvider().createMediaControlView2(
+                        (MediaControlView2) instance, superProvider, privateProvider,
+                        attrs, defStyleAttr, defStyleRes),
+                context, attrs, defStyleAttr, defStyleRes);
+        mProvider.initialize(attrs, defStyleAttr, defStyleRes);
     }
 
     /**
-     * @hide
+     * Sets MediaSession2 token to control corresponding MediaSession2.
      */
-    public MediaControlView2Provider getProvider() {
-        return mProvider;
+    public void setMediaSessionToken(SessionToken2 token) {
+        mProvider.setMediaSessionToken_impl(token);
     }
 
     /**
-     * Sets MediaController2 instance to control corresponding MediaSession2.
+     * Registers a callback to be invoked when the fullscreen mode should be changed.
+     * @param l The callback that will be run
+     */
+    public void setOnFullScreenListener(OnFullScreenListener l) {
+        mProvider.setOnFullScreenListener_impl(l);
+    }
+
+    /**
+     * @hide TODO: remove once the implementation is revised
      */
     public void setController(MediaController controller) {
         mProvider.setController_impl(controller);
     }
 
     /**
-     * Shows the control view on screen. It will disappear automatically after 3 seconds of
-     * inactivity.
-     */
-    public void show() {
-        mProvider.show_impl();
-    }
-
-    /**
-     * Shows the control view on screen. It will disappear automatically after {@code timeout}
-     * milliseconds of inactivity.
-     */
-    public void show(int timeout) {
-        mProvider.show_impl(timeout);
-    }
-
-    /**
-     * Returns whether the control view is currently shown or hidden.
-     */
-    public boolean isShowing() {
-        return mProvider.isShowing_impl();
-    }
-
-    /**
-     * Hide the control view from the screen.
-     */
-    public void hide() {
-        mProvider.hide_impl();
-    }
-
-    /**
-     * If the media selected has a subtitle track, calling this method will display the subtitle at
-     * the bottom of the view. If a media has multiple subtitle tracks, this method will select the
-     * first one of them.
-     */
-    public void showSubtitle() {
-        mProvider.showSubtitle_impl();
-    }
-
-    /**
-     * Hides the currently displayed subtitle.
-     */
-    public void hideSubtitle() {
-        mProvider.hideSubtitle_impl();
-    }
-
-    /**
-     * Set listeners for previous and next buttons to customize the behavior of clicking them.
-     * The UI for these buttons are provided as default and will be automatically displayed when
-     * this method is called.
+     * Changes the visibility state of an individual button. Default value is View.Visible.
      *
-     * @param next Listener for clicking next button
-     * @param prev Listener for clicking previous button
+     * @param button the {@code Button} assigned to individual buttons
+     * <ul>
+     * <li>{@link #BUTTON_PLAY_PAUSE}
+     * <li>{@link #BUTTON_FFWD}
+     * <li>{@link #BUTTON_REW}
+     * <li>{@link #BUTTON_NEXT}
+     * <li>{@link #BUTTON_PREV}
+     * <li>{@link #BUTTON_SUBTITLE}
+     * <li>{@link #BUTTON_FULL_SCREEN}
+     * <li>{@link #BUTTON_MUTE}
+     * <li>{@link #BUTTON_OVERFLOW}
+     * <li>{@link #BUTTON_ASPECT_RATIO}
+     * <li>{@link #BUTTON_SETTINGS}
+     * </ul>
+     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+     * @hide
      */
-    public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
-        mProvider.setPrevNextListeners_impl(next, prev);
+    public void setButtonVisibility(@Button int button, @Visibility int visibility) {
+        mProvider.setButtonVisibility_impl(button, visibility);
     }
 
     /**
-     * Hides the specified button from view.
-     *
-     * @param button the constant integer assigned to individual buttons
-     * @param visible whether the button should be visible or not
+     *  Requests focus for the play/pause button.
      */
-    public void setButtonVisibility(int button, boolean visible) {
-        mProvider.setButtonVisibility_impl(button, visible);
+    public void requestPlayButtonFocus() {
+        mProvider.requestPlayButtonFocus_impl();
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        mProvider.onAttachedToWindow_impl();
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        mProvider.onLayout_impl(changed, l, t, r, b);
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        mProvider.onDetachedFromWindow_impl();
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        return mProvider.getAccessibilityClassName_impl();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mProvider.onTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent ev) {
-        return mProvider.onTrackballEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        return mProvider.onKeyDown_impl(keyCode, event);
-    }
-
-    @Override
-    public void onFinishInflate() {
-        mProvider.onFinishInflate_impl();
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return mProvider.dispatchKeyEvent_impl(event);
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        mProvider.setEnabled_impl(enabled);
-    }
-
-    private class SuperProvider implements ViewProvider {
-        @Override
-        public void onAttachedToWindow_impl() {
-            MediaControlView2.super.onAttachedToWindow();
-        }
-
-        @Override
-        public void onDetachedFromWindow_impl() {
-            MediaControlView2.super.onDetachedFromWindow();
-        }
-
-        @Override
-        public CharSequence getAccessibilityClassName_impl() {
-            return MediaControlView2.super.getAccessibilityClassName();
-        }
-
-        @Override
-        public boolean onTouchEvent_impl(MotionEvent ev) {
-            return MediaControlView2.super.onTouchEvent(ev);
-        }
-
-        @Override
-        public boolean onTrackballEvent_impl(MotionEvent ev) {
-            return MediaControlView2.super.onTrackballEvent(ev);
-        }
-
-        @Override
-        public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-            return MediaControlView2.super.onKeyDown(keyCode, event);
-        }
-
-        @Override
-        public void onFinishInflate_impl() {
-            MediaControlView2.super.onFinishInflate();
-        }
-
-        @Override
-        public boolean dispatchKeyEvent_impl(KeyEvent event) {
-            return MediaControlView2.super.dispatchKeyEvent(event);
-        }
-
-        @Override
-        public void setEnabled_impl(boolean enabled) {
-            MediaControlView2.super.setEnabled(enabled);
-        }
+    /**
+     * Interface definition of a callback to be invoked to inform the fullscreen mode is changed.
+     * Application should handle the fullscreen mode accordingly.
+     */
+    public interface OnFullScreenListener {
+        /**
+         * Called to indicate a fullscreen mode change.
+         */
+        void onFullScreen(View view, boolean fullScreen);
     }
 }
diff --git a/android/widget/PopupWindow.java b/android/widget/PopupWindow.java
index e91db13..9553cf5 100644
--- a/android/widget/PopupWindow.java
+++ b/android/widget/PopupWindow.java
@@ -1583,7 +1583,7 @@
      *
      * @hide
      */
-    protected final boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+    protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
             int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
         final int anchorHeight = anchor.getHeight();
         final int anchorWidth = anchor.getWidth();
@@ -2563,7 +2563,9 @@
                     public void onViewDetachedFromWindow(View v) {
                         v.removeOnAttachStateChangeListener(this);
 
-                        TransitionManager.endTransitions(PopupDecorView.this);
+                        if (isAttachedToWindow()) {
+                            TransitionManager.endTransitions(PopupDecorView.this);
+                        }
                     }
                 };
 
diff --git a/android/widget/RadioGroup.java b/android/widget/RadioGroup.java
index 5c4d4d2..c987147 100644
--- a/android/widget/RadioGroup.java
+++ b/android/widget/RadioGroup.java
@@ -183,13 +183,17 @@
     }
 
     private void setCheckedId(@IdRes int id) {
+        boolean changed = id != mCheckedId;
         mCheckedId = id;
+
         if (mOnCheckedChangeListener != null) {
             mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
         }
-        final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
-        if (afm != null) {
-            afm.notifyValueChanged(this);
+        if (changed) {
+            final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
+            if (afm != null) {
+                afm.notifyValueChanged(this);
+            }
         }
     }
 
diff --git a/android/widget/RelativeLayout.java b/android/widget/RelativeLayout.java
index 75fc538..bbdf15c 100644
--- a/android/widget/RelativeLayout.java
+++ b/android/widget/RelativeLayout.java
@@ -1182,12 +1182,12 @@
      * determine where to position the view on the screen.  If the view is not contained
      * within a relative layout, these attributes are ignored.
      *
-     * See the <a href=“https://developer.android.com/guide/topics/ui/layout/relative.html”>
+     * See the <a href="/guide/topics/ui/layout/relative.html">
      * Relative Layout</a> guide for example code demonstrating how to use relative layout’s
      * layout parameters in a layout XML.
      *
      * To learn more about layout parameters and how they differ from typical view attributes,
-     * see the <a href=“https://developer.android.com/guide/topics/ui/declaring-layout.html#attributes”>
+     * see the <a href="/guide/topics/ui/declaring-layout.html#attributes">
      *     Layouts guide</a>.
      *
      *
diff --git a/android/widget/RemoteViews.java b/android/widget/RemoteViews.java
index a2c55b0..08513aa 100644
--- a/android/widget/RemoteViews.java
+++ b/android/widget/RemoteViews.java
@@ -69,8 +69,6 @@
 import com.android.internal.util.NotificationColorUtil;
 import com.android.internal.util.Preconditions;
 
-import libcore.util.Objects;
-
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -82,6 +80,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Stack;
 import java.util.concurrent.Executor;
 
@@ -291,9 +290,9 @@
                 return false;
             }
             MethodKey p = (MethodKey) o;
-            return Objects.equal(p.targetClass, targetClass)
-                    && Objects.equal(p.paramClass, paramClass)
-                    && Objects.equal(p.methodName, methodName);
+            return Objects.equals(p.targetClass, targetClass)
+                    && Objects.equals(p.paramClass, paramClass)
+                    && Objects.equals(p.methodName, methodName);
         }
 
         @Override
diff --git a/android/widget/SearchView.java b/android/widget/SearchView.java
index 519a7dd..225497b 100644
--- a/android/widget/SearchView.java
+++ b/android/widget/SearchView.java
@@ -1990,28 +1990,15 @@
 
         @Override
         public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK) {
-                // special case for the back key, we do not even try to send it
-                // to the drop down list but instead, consume it immediately
-                if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
-                    if (state != null) {
-                        state.startTracking(event, this);
-                    }
-                    return true;
-                } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
-                    if (state != null) {
-                        state.handleUpEvent(event);
-                    }
-                    if (event.isTracking() && !event.isCanceled()) {
-                        mSearchView.clearFocus();
-                        setImeVisibility(false);
-                        return true;
-                    }
-                }
+            final boolean consume = super.onKeyPreIme(keyCode, event);
+            if (consume && keyCode == KeyEvent.KEYCODE_BACK
+                    && event.getAction() == KeyEvent.ACTION_UP) {
+                // If AutoCompleteTextView closed its pop-up, it will return true, in which case
+                // we should also close the IME. Otherwise, the popup is already closed and we can
+                // leave the BACK event alone.
+                setImeVisibility(false);
             }
-            return super.onKeyPreIme(keyCode, event);
+            return consume;
         }
 
         /**
diff --git a/android/widget/SelectionActionModeHelper.java b/android/widget/SelectionActionModeHelper.java
index 3bfa520..b3327a7 100644
--- a/android/widget/SelectionActionModeHelper.java
+++ b/android/widget/SelectionActionModeHelper.java
@@ -33,12 +33,14 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ActionMode;
+import android.view.textclassifier.Logger;
+import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.SelectionEvent.InvocationMethod;
 import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationConstants;
+import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
-import android.view.textclassifier.logging.SmartSelectionEventTracker;
-import android.view.textclassifier.logging.SmartSelectionEventTracker.SelectionEvent;
 import android.widget.Editor.SelectionModifierCursorController;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -65,13 +67,12 @@
 
     private static final String LOG_TAG = "SelectActionModeHelper";
 
-    private static final boolean SMART_SELECT_ANIMATION_ENABLED = true;
-
     private final Editor mEditor;
     private final TextView mTextView;
     private final TextClassificationHelper mTextClassificationHelper;
+    private final TextClassificationConstants mTextClassificationSettings;
 
-    private TextClassification mTextClassification;
+    @Nullable private TextClassification mTextClassification;
     private AsyncTask mTextClassificationAsyncTask;
 
     private final SelectionTracker mSelectionTracker;
@@ -83,16 +84,17 @@
     SelectionActionModeHelper(@NonNull Editor editor) {
         mEditor = Preconditions.checkNotNull(editor);
         mTextView = mEditor.getTextView();
+        mTextClassificationSettings = TextClassificationManager.getSettings(mTextView.getContext());
         mTextClassificationHelper = new TextClassificationHelper(
                 mTextView.getContext(),
-                mTextView.getTextClassifier(),
+                mTextView::getTextClassifier,
                 getText(mTextView),
                 0, 1, mTextView.getTextLocales());
         mSelectionTracker = new SelectionTracker(mTextView);
 
-        if (SMART_SELECT_ANIMATION_ENABLED) {
+        if (mTextClassificationSettings.isSmartSelectionAnimationEnabled()) {
             mSmartSelectSprite = new SmartSelectSprite(mTextView.getContext(),
-                    mTextView::invalidate);
+                    editor.getTextView().mHighlightColor, mTextView::invalidate);
         } else {
             mSmartSelectSprite = null;
         }
@@ -103,14 +105,13 @@
      */
     public void startSelectionActionModeAsync(boolean adjustSelection) {
         // Check if the smart selection should run for editable text.
-        adjustSelection &= !mTextView.isTextEditable()
-                || mTextView.getTextClassifier().getSettings()
-                        .isSuggestSelectionEnabledForEditableText();
+        adjustSelection &= mTextClassificationSettings.isSmartSelectionEnabled();
 
         mSelectionTracker.onOriginalSelection(
                 getText(mTextView),
                 mTextView.getSelectionStart(),
-                mTextView.getSelectionEnd());
+                mTextView.getSelectionEnd(),
+                false /*isLink*/);
         cancelAsyncTask();
         if (skipTextClassification()) {
             startSelectionActionMode(null);
@@ -124,7 +125,8 @@
                             : mTextClassificationHelper::classifyText,
                     mSmartSelectSprite != null
                             ? this::startSelectionActionModeWithSmartSelectAnimation
-                            : this::startSelectionActionMode)
+                            : this::startSelectionActionMode,
+                    mTextClassificationHelper::getOriginalSelection)
                     .execute();
         }
     }
@@ -132,18 +134,19 @@
     /**
      * Starts Link ActionMode.
      */
-    public void startLinkActionModeAsync(TextLinks.TextLink textLink) {
-        //TODO: tracking/logging
+    public void startLinkActionModeAsync(int start, int end) {
+        mSelectionTracker.onOriginalSelection(getText(mTextView), start, end, true /*isLink*/);
         cancelAsyncTask();
         if (skipTextClassification()) {
             startLinkActionMode(null);
         } else {
-            resetTextClassificationHelper(textLink.getStart(), textLink.getEnd());
+            resetTextClassificationHelper(start, end);
             mTextClassificationAsyncTask = new TextClassificationAsyncTask(
                     mTextView,
                     mTextClassificationHelper.getTimeoutDuration(),
                     mTextClassificationHelper::classifyText,
-                    this::startLinkActionMode)
+                    this::startLinkActionMode,
+                    mTextClassificationHelper::getOriginalSelection)
                     .execute();
         }
     }
@@ -158,7 +161,8 @@
                     mTextView,
                     mTextClassificationHelper.getTimeoutDuration(),
                     mTextClassificationHelper::classifyText,
-                    this::invalidateActionMode)
+                    this::invalidateActionMode,
+                    mTextClassificationHelper::getOriginalSelection)
                     .execute();
         }
     }
@@ -172,7 +176,7 @@
     public void onSelectionDrag() {
         mSelectionTracker.onSelectionAction(
                 mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
-                SelectionEvent.ActionType.DRAG, mTextClassification);
+                SelectionEvent.ACTION_DRAG, mTextClassification);
     }
 
     public void onTextChanged(int start, int end) {
@@ -199,11 +203,15 @@
     }
 
     public void onDraw(final Canvas canvas) {
-        if (mSmartSelectSprite != null) {
+        if (isDrawingHighlight() && mSmartSelectSprite != null) {
             mSmartSelectSprite.draw(canvas);
         }
     }
 
+    public boolean isDrawingHighlight() {
+        return mSmartSelectSprite != null && mSmartSelectSprite.isAnimationActive();
+    }
+
     private void cancelAsyncTask() {
         if (mTextClassificationAsyncTask != null) {
             mTextClassificationAsyncTask.cancel(true);
@@ -214,7 +222,7 @@
 
     private boolean skipTextClassification() {
         // No need to make an async call for a no-op TextClassifier.
-        final boolean noOpTextClassifier = mTextView.getTextClassifier() == TextClassifier.NO_OP;
+        final boolean noOpTextClassifier = mTextView.usesNoOpTextClassifier();
         // Do not call the TextClassifier if there is no selection.
         final boolean noSelection = mTextView.getSelectionEnd() == mTextView.getSelectionStart();
         // Do not call the TextClassifier if this is a password field.
@@ -235,15 +243,15 @@
             @Editor.TextActionMode int actionMode, @Nullable SelectionResult result) {
         final CharSequence text = getText(mTextView);
         if (result != null && text instanceof Spannable
-                && (mTextView.isTextSelectable()
-                    || mTextView.isTextEditable()
-                    || actionMode == Editor.TextActionMode.TEXT_LINK)) {
+                && (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
             // Do not change the selection if TextClassifier should be dark launched.
-            if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
+            if (!mTextClassificationSettings.isModelDarkLaunchEnabled()) {
                 Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
                 mTextView.invalidate();
             }
             mTextClassification = result.mClassification;
+        } else if (result != null && actionMode == Editor.TextActionMode.TEXT_LINK) {
+            mTextClassification = result.mClassification;
         } else {
             mTextClassification = null;
         }
@@ -440,8 +448,7 @@
             selectionEnd = mTextView.getSelectionEnd();
         }
         mTextClassificationHelper.init(
-                mTextView.getContext(),
-                mTextView.getTextClassifier(),
+                mTextView::getTextClassifier,
                 getText(mTextView),
                 selectionStart, selectionEnd,
                 mTextView.getTextLocales());
@@ -482,7 +489,8 @@
         /**
          * Called when the original selection happens, before smart selection is triggered.
          */
-        public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) {
+        public void onOriginalSelection(
+                CharSequence text, int selectionStart, int selectionEnd, boolean isLink) {
             // If we abandoned a selection and created a new one very shortly after, we may still
             // have a pending request to log ABANDON, which we flush here.
             mDelayedLogAbandon.flush();
@@ -491,7 +499,9 @@
             mOriginalEnd = mSelectionEnd = selectionEnd;
             mAllowReset = false;
             maybeInvalidateLogger();
-            mLogger.logSelectionStarted(text, selectionStart);
+            mLogger.logSelectionStarted(mTextView.getTextClassificationSession(),
+                    text, selectionStart,
+                    isLink ? SelectionEvent.INVOCATION_LINK : SelectionEvent.INVOCATION_MANUAL);
         }
 
         /**
@@ -574,7 +584,7 @@
                     mSelectionEnd = editor.getTextView().getSelectionEnd();
                     mLogger.logSelectionAction(
                             textView.getSelectionStart(), textView.getSelectionEnd(),
-                            SelectionEvent.ActionType.RESET, null /* classification */);
+                            SelectionEvent.ACTION_RESET, null /* classification */);
                 }
                 return selected;
             }
@@ -583,7 +593,7 @@
 
         public void onTextChanged(int start, int end, TextClassification classification) {
             if (isSelectionStarted() && start == mSelectionStart && end == mSelectionEnd) {
-                onSelectionAction(start, end, SelectionEvent.ActionType.OVERTYPE, classification);
+                onSelectionAction(start, end, SelectionEvent.ACTION_OVERTYPE, classification);
             }
         }
 
@@ -622,8 +632,9 @@
                 if (mIsPending) {
                     mLogger.logSelectionAction(
                             mSelectionStart, mSelectionEnd,
-                            SelectionEvent.ActionType.ABANDON, null /* classification */);
+                            SelectionEvent.ACTION_ABANDON, null /* classification */);
                     mSelectionStart = mSelectionEnd = -1;
+                    mTextView.getTextClassificationSession().destroy();
                     mIsPending = false;
                 }
             }
@@ -643,43 +654,62 @@
      * Part selection of a word e.g. "or" is counted as selecting the
      * entire word i.e. equivalent to "York", and each special character is counted as a word, e.g.
      * "," is at [2, 3). Whitespaces are ignored.
+     *
+     * NOTE that the definition of a word is defined by the TextClassifier's Logger's token
+     * iterator.
      */
     private static final class SelectionMetricsLogger {
 
         private static final String LOG_TAG = "SelectionMetricsLogger";
         private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s+");
 
-        private final SmartSelectionEventTracker mDelegate;
+        private final Logger mLogger;
         private final boolean mEditTextLogger;
-        private final BreakIterator mWordIterator;
+        private final BreakIterator mTokenIterator;
+
+        @Nullable private TextClassifier mClassificationSession;
         private int mStartIndex;
         private String mText;
 
         SelectionMetricsLogger(TextView textView) {
             Preconditions.checkNotNull(textView);
-            final @SmartSelectionEventTracker.WidgetType int widgetType = textView.isTextEditable()
-                    ? SmartSelectionEventTracker.WidgetType.EDITTEXT
-                    : (textView.isTextSelectable()
-                            ? SmartSelectionEventTracker.WidgetType.TEXTVIEW
-                            : SmartSelectionEventTracker.WidgetType.UNSELECTABLE_TEXTVIEW);
-            mDelegate = new SmartSelectionEventTracker(textView.getContext(), widgetType);
+            mLogger = textView.getTextClassifier().getLogger(
+                    new Logger.Config(textView.getContext(), getWidetType(textView), null));
             mEditTextLogger = textView.isTextEditable();
-            mWordIterator = BreakIterator.getWordInstance(textView.getTextLocale());
+            mTokenIterator = mLogger.getTokenIterator(textView.getTextLocale());
         }
 
-        public void logSelectionStarted(CharSequence text, int index) {
+        @TextClassifier.WidgetType
+        private static String getWidetType(TextView textView) {
+            if (textView.isTextEditable()) {
+                return TextClassifier.WIDGET_TYPE_EDITTEXT;
+            }
+            if (textView.isTextSelectable()) {
+                return TextClassifier.WIDGET_TYPE_TEXTVIEW;
+            }
+            return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
+        }
+
+        public void logSelectionStarted(
+                TextClassifier classificationSession,
+                CharSequence text, int index,
+                @InvocationMethod int invocationMethod) {
             try {
                 Preconditions.checkNotNull(text);
                 Preconditions.checkArgumentInRange(index, 0, text.length(), "index");
                 if (mText == null || !mText.contentEquals(text)) {
                     mText = text.toString();
                 }
-                mWordIterator.setText(mText);
+                mTokenIterator.setText(mText);
                 mStartIndex = index;
-                mDelegate.logEvent(SelectionEvent.selectionStarted(0));
+                mLogger.logSelectionStartedEvent(invocationMethod, 0);
+                // TODO: Remove the above legacy logging.
+                mClassificationSession = classificationSession;
+                mClassificationSession.onSelectionEvent(
+                        SelectionEvent.createSelectionStartedEvent(invocationMethod, 0));
             } catch (Exception e) {
                 // Avoid crashes due to logging.
-                Log.d(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "" + e.getMessage(), e);
             }
         }
 
@@ -690,18 +720,36 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (selection != null) {
-                    mDelegate.logEvent(SelectionEvent.selectionModified(
-                            wordIndices[0], wordIndices[1], selection));
+                    mLogger.logSelectionModifiedEvent(
+                            wordIndices[0], wordIndices[1], selection);
+                    // TODO: Remove the above legacy logging.
+                    if (mClassificationSession != null) {
+                        mClassificationSession.onSelectionEvent(
+                                SelectionEvent.createSelectionModifiedEvent(
+                                        wordIndices[0], wordIndices[1], selection));
+                    }
                 } else if (classification != null) {
-                    mDelegate.logEvent(SelectionEvent.selectionModified(
-                            wordIndices[0], wordIndices[1], classification));
+                    mLogger.logSelectionModifiedEvent(
+                            wordIndices[0], wordIndices[1], classification);
+                    // TODO: Remove the above legacy logging.
+                    if (mClassificationSession != null) {
+                        mClassificationSession.onSelectionEvent(
+                                SelectionEvent.createSelectionModifiedEvent(
+                                        wordIndices[0], wordIndices[1], classification));
+                    }
                 } else {
-                    mDelegate.logEvent(SelectionEvent.selectionModified(
-                            wordIndices[0], wordIndices[1]));
+                    mLogger.logSelectionModifiedEvent(
+                            wordIndices[0], wordIndices[1]);
+                    // TODO: Remove the above legacy logging.
+                    if (mClassificationSession != null) {
+                        mClassificationSession.onSelectionEvent(
+                                SelectionEvent.createSelectionModifiedEvent(
+                                        wordIndices[0], wordIndices[1]));
+                    }
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
-                Log.d(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "" + e.getMessage(), e);
             }
         }
 
@@ -714,15 +762,27 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (classification != null) {
-                    mDelegate.logEvent(SelectionEvent.selectionAction(
-                            wordIndices[0], wordIndices[1], action, classification));
+                    mLogger.logSelectionActionEvent(
+                            wordIndices[0], wordIndices[1], action, classification);
+                    // TODO: Remove the above legacy logging.
+                    if (mClassificationSession != null) {
+                        mClassificationSession.onSelectionEvent(
+                                SelectionEvent.createSelectionActionEvent(
+                                        wordIndices[0], wordIndices[1], action, classification));
+                    }
                 } else {
-                    mDelegate.logEvent(SelectionEvent.selectionAction(
-                            wordIndices[0], wordIndices[1], action));
+                    mLogger.logSelectionActionEvent(
+                            wordIndices[0], wordIndices[1], action);
+                    // TODO: Remove the above legacy logging.
+                    if (mClassificationSession != null) {
+                        mClassificationSession.onSelectionEvent(
+                                SelectionEvent.createSelectionActionEvent(
+                                        wordIndices[0], wordIndices[1], action));
+                    }
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
-                Log.d(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "" + e.getMessage(), e);
             }
         }
 
@@ -741,10 +801,10 @@
                 wordIndices[0] = countWordsBackward(start);
 
                 // For the selection start index, avoid counting a partial word backwards.
-                if (!mWordIterator.isBoundary(start)
+                if (!mTokenIterator.isBoundary(start)
                         && !isWhitespace(
-                        mWordIterator.preceding(start),
-                        mWordIterator.following(start))) {
+                        mTokenIterator.preceding(start),
+                        mTokenIterator.following(start))) {
                     // We counted a partial word. Remove it.
                     wordIndices[0]--;
                 }
@@ -766,7 +826,7 @@
             int wordCount = 0;
             int offset = from;
             while (offset > mStartIndex) {
-                int start = mWordIterator.preceding(offset);
+                int start = mTokenIterator.preceding(offset);
                 if (!isWhitespace(start, offset)) {
                     wordCount++;
                 }
@@ -780,7 +840,7 @@
             int wordCount = 0;
             int offset = from;
             while (offset < mStartIndex) {
-                int end = mWordIterator.following(offset);
+                int end = mTokenIterator.following(offset);
                 if (!isWhitespace(offset, end)) {
                     wordCount++;
                 }
@@ -805,6 +865,7 @@
         private final int mTimeOutDuration;
         private final Supplier<SelectionResult> mSelectionResultSupplier;
         private final Consumer<SelectionResult> mSelectionResultCallback;
+        private final Supplier<SelectionResult> mTimeOutResultSupplier;
         private final TextView mTextView;
         private final String mOriginalText;
 
@@ -813,16 +874,19 @@
          * @param timeOut time in milliseconds to timeout the query if it has not completed
          * @param selectionResultSupplier fetches the selection results. Runs on a background thread
          * @param selectionResultCallback receives the selection results. Runs on the UiThread
+         * @param timeOutResultSupplier default result if the task times out
          */
         TextClassificationAsyncTask(
                 @NonNull TextView textView, int timeOut,
                 @NonNull Supplier<SelectionResult> selectionResultSupplier,
-                @NonNull Consumer<SelectionResult> selectionResultCallback) {
+                @NonNull Consumer<SelectionResult> selectionResultCallback,
+                @NonNull Supplier<SelectionResult> timeOutResultSupplier) {
             super(textView != null ? textView.getHandler() : null);
             mTextView = Preconditions.checkNotNull(textView);
             mTimeOutDuration = timeOut;
             mSelectionResultSupplier = Preconditions.checkNotNull(selectionResultSupplier);
             mSelectionResultCallback = Preconditions.checkNotNull(selectionResultCallback);
+            mTimeOutResultSupplier = Preconditions.checkNotNull(timeOutResultSupplier);
             // Make a copy of the original text.
             mOriginalText = getText(mTextView).toString();
         }
@@ -846,7 +910,7 @@
 
         private void onTimeOut() {
             if (getStatus() == Status.RUNNING) {
-                onPostExecute(null);
+                onPostExecute(mTimeOutResultSupplier.get());
             }
             cancel(true);
         }
@@ -861,8 +925,9 @@
 
         private static final int TRIM_DELTA = 120;  // characters
 
-        private Context mContext;
-        private TextClassifier mTextClassifier;
+        private final Context mContext;
+        private final boolean mDarkLaunchEnabled;
+        private Supplier<TextClassifier> mTextClassifier;
 
         /** The original TextView text. **/
         private String mText;
@@ -871,9 +936,8 @@
         /** End index relative to mText. */
         private int mSelectionEnd;
 
-        private final TextSelection.Options mSelectionOptions = new TextSelection.Options();
-        private final TextClassification.Options mClassificationOptions =
-                new TextClassification.Options();
+        @Nullable
+        private LocaleList mDefaultLocales;
 
         /** Trimmed text starting from mTrimStart in mText. */
         private CharSequence mTrimmedText;
@@ -894,24 +958,24 @@
         /** Whether the TextClassifier has been initialized. */
         private boolean mHot;
 
-        TextClassificationHelper(Context context, TextClassifier textClassifier,
+        TextClassificationHelper(Context context, Supplier<TextClassifier> textClassifier,
                 CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
-            init(context, textClassifier, text, selectionStart, selectionEnd, locales);
+            init(textClassifier, text, selectionStart, selectionEnd, locales);
+            mContext = Preconditions.checkNotNull(context);
+            mDarkLaunchEnabled = TextClassificationManager.getSettings(mContext)
+                    .isModelDarkLaunchEnabled();
         }
 
         @UiThread
-        public void init(Context context, TextClassifier textClassifier,
-                CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
-            mContext = Preconditions.checkNotNull(context);
+        public void init(Supplier<TextClassifier> textClassifier, CharSequence text,
+                int selectionStart, int selectionEnd, LocaleList locales) {
             mTextClassifier = Preconditions.checkNotNull(textClassifier);
             mText = Preconditions.checkNotNull(text).toString();
             mLastClassificationText = null; // invalidate.
             Preconditions.checkArgument(selectionEnd > selectionStart);
             mSelectionStart = selectionStart;
             mSelectionEnd = selectionEnd;
-            mClassificationOptions.setDefaultLocales(locales);
-            mSelectionOptions.setDefaultLocales(locales)
-                    .setDarkLaunchAllowed(true);
+            mDefaultLocales = locales;
         }
 
         @WorkerThread
@@ -926,16 +990,19 @@
             trimText();
             final TextSelection selection;
             if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
-                selection = mTextClassifier.suggestSelection(
-                        mTrimmedText, mRelativeStart, mRelativeEnd, mSelectionOptions);
+                final TextSelection.Request request = new TextSelection.Request.Builder(
+                        mTrimmedText, mRelativeStart, mRelativeEnd)
+                        .setDefaultLocales(mDefaultLocales)
+                        .setDarkLaunchAllowed(true)
+                        .build();
+                selection = mTextClassifier.get().suggestSelection(request);
             } else {
                 // Use old APIs.
-                selection = mTextClassifier.suggestSelection(
-                        mTrimmedText, mRelativeStart, mRelativeEnd,
-                        mSelectionOptions.getDefaultLocales());
+                selection = mTextClassifier.get().suggestSelection(
+                        mTrimmedText, mRelativeStart, mRelativeEnd, mDefaultLocales);
             }
             // Do not classify new selection boundaries if TextClassifier should be dark launched.
-            if (!mTextClassifier.getSettings().isDarkLaunch()) {
+            if (!mDarkLaunchEnabled) {
                 mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
                 mSelectionEnd = Math.min(
                         mText.length(), selection.getSelectionEndIndex() + mTrimStart);
@@ -943,6 +1010,10 @@
             return performClassification(selection);
         }
 
+        public SelectionResult getOriginalSelection() {
+            return new SelectionResult(mSelectionStart, mSelectionEnd, null, null);
+        }
+
         /**
          * Maximum time (in milliseconds) to wait for a textclassifier result before timing out.
          */
@@ -963,25 +1034,26 @@
             if (!Objects.equals(mText, mLastClassificationText)
                     || mSelectionStart != mLastClassificationSelectionStart
                     || mSelectionEnd != mLastClassificationSelectionEnd
-                    || !Objects.equals(
-                            mClassificationOptions.getDefaultLocales(),
-                            mLastClassificationLocales)) {
+                    || !Objects.equals(mDefaultLocales, mLastClassificationLocales)) {
 
                 mLastClassificationText = mText;
                 mLastClassificationSelectionStart = mSelectionStart;
                 mLastClassificationSelectionEnd = mSelectionEnd;
-                mLastClassificationLocales = mClassificationOptions.getDefaultLocales();
+                mLastClassificationLocales = mDefaultLocales;
 
                 trimText();
                 final TextClassification classification;
                 if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
-                    classification = mTextClassifier.classifyText(
-                            mTrimmedText, mRelativeStart, mRelativeEnd, mClassificationOptions);
+                    final TextClassification.Request request =
+                            new TextClassification.Request.Builder(
+                                    mTrimmedText, mRelativeStart, mRelativeEnd)
+                                    .setDefaultLocales(mDefaultLocales)
+                                    .build();
+                    classification = mTextClassifier.get().classifyText(request);
                 } else {
                     // Use old APIs.
-                    classification = mTextClassifier.classifyText(
-                            mTrimmedText, mRelativeStart, mRelativeEnd,
-                            mClassificationOptions.getDefaultLocales());
+                    classification = mTextClassifier.get().classifyText(
+                            mTrimmedText, mRelativeStart, mRelativeEnd, mDefaultLocales);
                 }
                 mLastClassificationResult = new SelectionResult(
                         mSelectionStart, mSelectionEnd, classification, selection);
@@ -1005,14 +1077,14 @@
     private static final class SelectionResult {
         private final int mStart;
         private final int mEnd;
-        private final TextClassification mClassification;
+        @Nullable private final TextClassification mClassification;
         @Nullable private final TextSelection mSelection;
 
         SelectionResult(int start, int end,
-                TextClassification classification, @Nullable TextSelection selection) {
+                @Nullable TextClassification classification, @Nullable TextSelection selection) {
             mStart = start;
             mEnd = end;
-            mClassification = Preconditions.checkNotNull(classification);
+            mClassification = classification;
             mSelection = selection;
         }
     }
@@ -1021,20 +1093,20 @@
     private static int getActionType(int menuItemId) {
         switch (menuItemId) {
             case TextView.ID_SELECT_ALL:
-                return SelectionEvent.ActionType.SELECT_ALL;
+                return SelectionEvent.ACTION_SELECT_ALL;
             case TextView.ID_CUT:
-                return SelectionEvent.ActionType.CUT;
+                return SelectionEvent.ACTION_CUT;
             case TextView.ID_COPY:
-                return SelectionEvent.ActionType.COPY;
+                return SelectionEvent.ACTION_COPY;
             case TextView.ID_PASTE:  // fall through
             case TextView.ID_PASTE_AS_PLAIN_TEXT:
-                return SelectionEvent.ActionType.PASTE;
+                return SelectionEvent.ACTION_PASTE;
             case TextView.ID_SHARE:
-                return SelectionEvent.ActionType.SHARE;
+                return SelectionEvent.ACTION_SHARE;
             case TextView.ID_ASSIST:
-                return SelectionEvent.ActionType.SMART_SHARE;
+                return SelectionEvent.ACTION_SMART_SHARE;
             default:
-                return SelectionEvent.ActionType.OTHER;
+                return SelectionEvent.ACTION_OTHER;
         }
     }
 
diff --git a/android/widget/SmartSelectSprite.java b/android/widget/SmartSelectSprite.java
index a391c6e..9a84f69 100644
--- a/android/widget/SmartSelectSprite.java
+++ b/android/widget/SmartSelectSprite.java
@@ -26,7 +26,6 @@
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
@@ -36,7 +35,6 @@
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.Shape;
 import android.text.Layout;
-import android.util.TypedValue;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
@@ -54,21 +52,15 @@
 final class SmartSelectSprite {
 
     private static final int EXPAND_DURATION = 300;
-    private static final int CORNER_DURATION = 150;
-    private static final float STROKE_WIDTH_DP = 1.5F;
-
-    // GBLUE700
-    @ColorInt
-    private static final int DEFAULT_STROKE_COLOR = 0xFF3367D6;
+    private static final int CORNER_DURATION = 50;
 
     private final Interpolator mExpandInterpolator;
     private final Interpolator mCornerInterpolator;
-    private final float mStrokeWidth;
 
     private Animator mActiveAnimator = null;
     private final Runnable mInvalidator;
     @ColorInt
-    private final int mStrokeColor;
+    private final int mFillColor;
 
     static final Comparator<RectF> RECTANGLE_COMPARATOR = Comparator
             .<RectF>comparingDouble(e -> e.bottom)
@@ -124,26 +116,11 @@
             return expansionDirection * -1;
         }
 
-        @Retention(SOURCE)
-        @IntDef({RectangleBorderType.FIT, RectangleBorderType.OVERSHOOT})
-        private @interface RectangleBorderType {
-        /** A rectangle which, fully expanded, fits inside of its bounding rectangle. */
-        int FIT = 0;
-        /**
-         * A rectangle which, when fully expanded, clips outside of its bounding rectangle so that
-         * its edges no longer appear rounded.
-         */
-        int OVERSHOOT = 1;
-        }
-
-        private final float mStrokeWidth;
         private final RectF mBoundingRectangle;
         private float mRoundRatio = 1.0f;
         private final @ExpansionDirection int mExpansionDirection;
-        private final @RectangleBorderType int mRectangleBorderType;
 
         private final RectF mDrawRect = new RectF();
-        private final RectF mClipRect = new RectF();
         private final Path mClipPath = new Path();
 
         /** How offset the left edge of the rectangle is from the left side of the bounding box. */
@@ -159,13 +136,9 @@
         private RoundedRectangleShape(
                 final RectF boundingRectangle,
                 final @ExpansionDirection int expansionDirection,
-                final @RectangleBorderType int rectangleBorderType,
-                final boolean inverted,
-                final float strokeWidth) {
+                final boolean inverted) {
             mBoundingRectangle = new RectF(boundingRectangle);
             mBoundingWidth = boundingRectangle.width();
-            mRectangleBorderType = rectangleBorderType;
-            mStrokeWidth = strokeWidth;
             mInverted = inverted && expansionDirection != ExpansionDirection.CENTER;
 
             if (inverted) {
@@ -182,14 +155,8 @@
         }
 
         /*
-         * In order to achieve the "rounded rectangle hits the wall" effect, the drawing needs to be
-         * done in two passes. In this context, the wall is the bounding rectangle and in the first
-         * pass we need to draw the rounded rectangle (expanded and with a corner radius as per
-         * object properties) clipped by the bounding box. If the rounded rectangle expands outside
-         * of the bounding box, one more pass needs to be done, as there will now be a hole in the
-         * rounded rectangle where it "flattened" against the bounding box. In order to fill just
-         * this hole, we need to draw the bounding box, but clip it with the rounded rectangle and
-         * this will connect the missing pieces.
+         * In order to achieve the "rounded rectangle hits the wall" effect, we draw an expanding
+         * rounded rectangle that is clipped by the bounding box of the selected text.
          */
         @Override
         public void draw(Canvas canvas, Paint paint) {
@@ -201,31 +168,8 @@
             final float adjustedCornerRadius = getAdjustedCornerRadius();
 
             mDrawRect.set(mBoundingRectangle);
-            mDrawRect.left = mBoundingRectangle.left + mLeftBoundary;
-            mDrawRect.right = mBoundingRectangle.left + mRightBoundary;
-
-            if (mRectangleBorderType == RectangleBorderType.OVERSHOOT) {
-                mDrawRect.left -= cornerRadius / 2;
-                mDrawRect.right += cornerRadius / 2;
-            } else {
-                switch (mExpansionDirection) {
-                    case ExpansionDirection.CENTER:
-                        break;
-                    case ExpansionDirection.LEFT:
-                        mDrawRect.right += cornerRadius;
-                        break;
-                    case ExpansionDirection.RIGHT:
-                        mDrawRect.left -= cornerRadius;
-                        break;
-                }
-            }
-
-            canvas.save();
-            mClipRect.set(mBoundingRectangle);
-            mClipRect.inset(-mStrokeWidth / 2, -mStrokeWidth / 2);
-            canvas.clipRect(mClipRect);
-            canvas.drawRoundRect(mDrawRect, adjustedCornerRadius, adjustedCornerRadius, paint);
-            canvas.restore();
+            mDrawRect.left = mBoundingRectangle.left + mLeftBoundary - cornerRadius / 2;
+            mDrawRect.right = mBoundingRectangle.left + mRightBoundary + cornerRadius / 2;
 
             canvas.save();
             mClipPath.reset();
@@ -272,11 +216,7 @@
         }
 
         private float getBoundingWidth() {
-            if (mRectangleBorderType == RectangleBorderType.OVERSHOOT) {
-                return (int) (mBoundingRectangle.width() + getCornerRadius());
-            } else {
-                return mBoundingRectangle.width();
-            }
+            return (int) (mBoundingRectangle.width() + getCornerRadius());
         }
 
     }
@@ -388,19 +328,20 @@
     }
 
     /**
-     * @param context     the {@link Context} in which the animation will run
+     * @param context the {@link Context} in which the animation will run
+     * @param highlightColor the highlight color of the underlying {@link TextView}
      * @param invalidator a {@link Runnable} which will be called every time the animation updates,
      *                    indicating that the view drawing the animation should invalidate itself
      */
-    SmartSelectSprite(final Context context, final Runnable invalidator) {
+    SmartSelectSprite(final Context context, @ColorInt int highlightColor,
+            final Runnable invalidator) {
         mExpandInterpolator = AnimationUtils.loadInterpolator(
                 context,
                 android.R.interpolator.fast_out_slow_in);
         mCornerInterpolator = AnimationUtils.loadInterpolator(
                 context,
                 android.R.interpolator.fast_out_linear_in);
-        mStrokeWidth = dpToPixel(context, STROKE_WIDTH_DP);
-        mStrokeColor = getStrokeColor(context);
+        mFillColor = highlightColor;
         mInvalidator = Preconditions.checkNotNull(invalidator);
     }
 
@@ -437,17 +378,14 @@
         RectangleWithTextSelectionLayout centerRectangle = null;
 
         int startingOffset = 0;
-        int startingRectangleIndex = 0;
-        for (int index = 0; index < rectangleCount; ++index) {
-            final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
-                    destinationRectangles.get(index);
+        for (RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout :
+                destinationRectangles) {
             final RectF rectangle = rectangleWithTextSelectionLayout.getRectangle();
             if (contains(rectangle, start)) {
                 centerRectangle = rectangleWithTextSelectionLayout;
                 break;
             }
             startingOffset += rectangle.width();
-            ++startingRectangleIndex;
         }
 
         if (centerRectangle == null) {
@@ -459,9 +397,6 @@
         final @RoundedRectangleShape.ExpansionDirection int[] expansionDirections =
                 generateDirections(centerRectangle, destinationRectangles);
 
-        final @RoundedRectangleShape.RectangleBorderType int[] rectangleBorderTypes =
-                generateBorderTypes(rectangleCount);
-
         for (int index = 0; index < rectangleCount; ++index) {
             final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
                     destinationRectangles.get(index);
@@ -469,10 +404,8 @@
             final RoundedRectangleShape shape = new RoundedRectangleShape(
                     rectangle,
                     expansionDirections[index],
-                    rectangleBorderTypes[index],
                     rectangleWithTextSelectionLayout.getTextSelectionLayout()
-                            == Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
-                    mStrokeWidth);
+                            == Layout.TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT);
             cornerAnimators.add(createCornerAnimator(shape, updateListener));
             shapes.add(shape);
         }
@@ -480,44 +413,23 @@
         final RectangleList rectangleList = new RectangleList(shapes);
         final ShapeDrawable shapeDrawable = new ShapeDrawable(rectangleList);
 
-        final float startingOffsetLeft;
-        final float startingOffsetRight;
-
-        final RoundedRectangleShape startingRectangleShape = shapes.get(startingRectangleIndex);
-        final float cornerRadius = startingRectangleShape.getCornerRadius();
-        if (startingRectangleShape.mRectangleBorderType
-                == RoundedRectangleShape.RectangleBorderType.FIT) {
-            switch (startingRectangleShape.mExpansionDirection) {
-                case RoundedRectangleShape.ExpansionDirection.LEFT:
-                    startingOffsetLeft = startingOffsetRight = startingOffset - cornerRadius / 2;
-                    break;
-                case RoundedRectangleShape.ExpansionDirection.RIGHT:
-                    startingOffsetLeft = startingOffsetRight = startingOffset + cornerRadius / 2;
-                    break;
-                case RoundedRectangleShape.ExpansionDirection.CENTER:  // fall through
-                default:
-                    startingOffsetLeft = startingOffset - cornerRadius / 2;
-                    startingOffsetRight = startingOffset + cornerRadius / 2;
-                    break;
-            }
-        } else {
-            startingOffsetLeft = startingOffsetRight = startingOffset;
-        }
-
         final Paint paint = shapeDrawable.getPaint();
-        paint.setColor(mStrokeColor);
-        paint.setStyle(Paint.Style.STROKE);
-        paint.setStrokeWidth(mStrokeWidth);
+        paint.setColor(mFillColor);
+        paint.setStyle(Paint.Style.FILL);
 
         mExistingRectangleList = rectangleList;
         mExistingDrawable = shapeDrawable;
 
-        mActiveAnimator = createAnimator(rectangleList, startingOffsetLeft, startingOffsetRight,
-                cornerAnimators, updateListener,
-                onAnimationEnd);
+        mActiveAnimator = createAnimator(rectangleList, startingOffset, startingOffset,
+                cornerAnimators, updateListener, onAnimationEnd);
         mActiveAnimator.start();
     }
 
+    /** Returns whether the sprite is currently animating. */
+    public boolean isAnimationActive() {
+        return mActiveAnimator != null && mActiveAnimator.isRunning();
+    }
+
     private Animator createAnimator(
             final RectangleList rectangleList,
             final float startingOffsetLeft,
@@ -625,36 +537,6 @@
         return result;
     }
 
-    private static @RoundedRectangleShape.RectangleBorderType int[] generateBorderTypes(
-            final int numberOfRectangles) {
-        final @RoundedRectangleShape.RectangleBorderType int[] result = new int[numberOfRectangles];
-
-        for (int i = 1; i < result.length - 1; ++i) {
-            result[i] = RoundedRectangleShape.RectangleBorderType.OVERSHOOT;
-        }
-
-        result[0] = RoundedRectangleShape.RectangleBorderType.FIT;
-        result[result.length - 1] = RoundedRectangleShape.RectangleBorderType.FIT;
-        return result;
-    }
-
-    private static float dpToPixel(final Context context, final float dp) {
-        return TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_DIP,
-                dp,
-                context.getResources().getDisplayMetrics());
-    }
-
-    @ColorInt
-    private static int getStrokeColor(final Context context) {
-        final TypedValue typedValue = new TypedValue();
-        final TypedArray array = context.obtainStyledAttributes(typedValue.data, new int[]{
-                android.R.attr.colorControlActivated});
-        final int result = array.getColor(0, DEFAULT_STROKE_COLOR);
-        array.recycle();
-        return result;
-    }
-
     /**
      * A variant of {@link RectF#contains(float, float)} that also allows the point to reside on
      * the right boundary of the rectangle.
diff --git a/android/widget/TextInputTimePickerView.java b/android/widget/TextInputTimePickerView.java
index 0cf8faa..e0261ad 100644
--- a/android/widget/TextInputTimePickerView.java
+++ b/android/widget/TextInputTimePickerView.java
@@ -174,7 +174,8 @@
      */
     void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour,
             boolean hourFormatStartsAtZero) {
-        final String format = "%d";
+        final String hourFormat = "%d";
+        final String minuteFormat = "%02d";
 
         mIs24Hour = is24Hour;
         mHourFormatStartsAtZero = hourFormatStartsAtZero;
@@ -187,8 +188,8 @@
             mAmPmSpinner.setSelection(1);
         }
 
-        mHourEditText.setText(String.format(format, localizedHour));
-        mMinuteEditText.setText(String.format(format, minute));
+        mHourEditText.setText(String.format(hourFormat, localizedHour));
+        mMinuteEditText.setText(String.format(minuteFormat, minute));
 
         if (mErrorShowing) {
             validateInput();
diff --git a/android/widget/TextView.java b/android/widget/TextView.java
index 7d3fcf4..11db6b6 100644
--- a/android/widget/TextView.java
+++ b/android/widget/TextView.java
@@ -36,6 +36,7 @@
 import android.annotation.StyleRes;
 import android.annotation.XmlRes;
 import android.app.Activity;
+import android.app.PendingIntent;
 import android.app.assist.AssistStructure;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -80,8 +81,8 @@
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
-import android.text.MeasuredText;
 import android.text.ParcelableSpan;
+import android.text.PrecomputedText;
 import android.text.Selection;
 import android.text.SpanWatcher;
 import android.text.Spannable;
@@ -162,6 +163,8 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
@@ -187,6 +190,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 
 /**
  * A user interface element that displays text to the user.
@@ -291,6 +298,7 @@
  * @attr ref android.R.styleable#TextView_drawableTintMode
  * @attr ref android.R.styleable#TextView_lineSpacingExtra
  * @attr ref android.R.styleable#TextView_lineSpacingMultiplier
+ * @attr ref android.R.styleable#TextView_justificationMode
  * @attr ref android.R.styleable#TextView_marqueeRepeatLimit
  * @attr ref android.R.styleable#TextView_inputType
  * @attr ref android.R.styleable#TextView_imeOptions
@@ -319,6 +327,11 @@
 
     // Enum for the "typeface" XML parameter.
     // TODO: How can we get this from the XML instead of hardcoding it here?
+    /** @hide */
+    @IntDef(value = {DEFAULT_TYPEFACE, SANS, SERIF, MONOSPACE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface XMLTypefaceAttr{}
+    private static final int DEFAULT_TYPEFACE = -1;
     private static final int SANS = 1;
     private static final int SERIF = 2;
     private static final int MONOSPACE = 3;
@@ -416,6 +429,7 @@
     private boolean mPreDrawListenerDetached;
 
     private TextClassifier mTextClassifier;
+    private TextClassifier mTextClassificationSession;
 
     // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
     // that if a user is holding down a movement key to traverse text, we shouldn't also traverse
@@ -633,8 +647,12 @@
      */
     private Layout mSavedMarqueeModeLayout;
 
+    // Do not update following mText/mSpannable/mPrecomputed except for setTextInternal()
     @ViewDebug.ExportedProperty(category = "text")
-    private CharSequence mText;
+    private @Nullable CharSequence mText;
+    private @Nullable Spannable mSpannable;
+    private @Nullable PrecomputedText mPrecomputed;
+
     private CharSequence mTransformed;
     private BufferType mBufferType = BufferType.NORMAL;
 
@@ -791,11 +809,18 @@
     // mAutoSizeStepGranularityInPx.
     private boolean mHasPresetAutoSizeValues = false;
 
+    // Autofill-related attributes
+    //
     // Indicates whether the text was set statically or dynamically, so it can be used to
     // sanitize autofill requests.
     private boolean mTextSetFromXmlOrResourceId = false;
-    // Resource id used to set the text - used for autofill purposes.
+    // Resource id used to set the text.
     private @StringRes int mTextId = ResourceId.ID_NULL;
+    // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding
+    // calls when the value did not change
+    private CharSequence mLastValueSentToAutofillManager;
+    //
+    // End of autofill-related attributes
 
     /**
      * Kick-start the font cache for the zygote process (to pay the cost of
@@ -856,7 +881,7 @@
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
 
-        mText = "";
+        setTextInternal("");
 
         final Resources res = getResources();
         final CompatibilityInfo compat = res.getCompatibilityInfo();
@@ -1597,6 +1622,13 @@
         }
     }
 
+    // Update mText and mPrecomputed
+    private void setTextInternal(@Nullable CharSequence text) {
+        mText = text;
+        mSpannable = (text instanceof Spannable) ? (Spannable) text : null;
+        mPrecomputed = (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
+    }
+
     /**
      * 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.
@@ -1905,19 +1937,12 @@
             // 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) {
-                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;
+                final int autoSizeValuesLength = ((int) Math.floor((mAutoSizeMaxTextSizeInPx
+                        - mAutoSizeMinTextSizeInPx) / mAutoSizeStepGranularityInPx)) + 1;
+                final int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
                 for (int i = 0; i < autoSizeValuesLength; i++) {
-                    autoSizeTextSizesInPx[i] = Math.round(sizeToAdd);
-                    sizeToAdd += mAutoSizeStepGranularityInPx;
+                    autoSizeTextSizesInPx[i] = Math.round(
+                            mAutoSizeMinTextSizeInPx + (i * mAutoSizeStepGranularityInPx));
                 }
                 mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
             }
@@ -1962,40 +1987,59 @@
                         }
                     }
                 }
-            } else if (mText instanceof Spannable) {
+            } else if (mSpannable != null) {
                 // Reset the selection.
-                Selection.setSelection((Spannable) mText, getSelectionEnd());
+                Selection.setSelection(mSpannable, getSelectionEnd());
             }
         }
     }
 
-    private void setTypefaceFromAttrs(Typeface fontTypeface, String familyName, int typefaceIndex,
-            int styleIndex) {
-        Typeface tf = fontTypeface;
-        if (tf == null && familyName != null) {
-            tf = Typeface.create(familyName, styleIndex);
-        } else if (tf != null && tf.getStyle() != styleIndex) {
-            tf = Typeface.create(tf, styleIndex);
+    /**
+     * Sets the Typeface taking into account the given attributes.
+     *
+     * @param typeface a typeface
+     * @param familyName family name string, e.g. "serif"
+     * @param typefaceIndex an index of the typeface enum, e.g. SANS, SERIF.
+     * @param style a typeface style
+     * @param weight a weight value for the Typeface or -1 if not specified.
+     */
+    private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
+            @XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
+            @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+        if (typeface == null && familyName != null) {
+            // Lookup normal Typeface from system font map.
+            final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
+            resolveStyleAndSetTypeface(normalTypeface, style, weight);
+        } else if (typeface != null) {
+            resolveStyleAndSetTypeface(typeface, style, weight);
+        } else {  // both typeface and familyName is null.
+            switch (typefaceIndex) {
+                case SANS:
+                    resolveStyleAndSetTypeface(Typeface.SANS_SERIF, style, weight);
+                    break;
+                case SERIF:
+                    resolveStyleAndSetTypeface(Typeface.SERIF, style, weight);
+                    break;
+                case MONOSPACE:
+                    resolveStyleAndSetTypeface(Typeface.MONOSPACE, style, weight);
+                    break;
+                case DEFAULT_TYPEFACE:
+                default:
+                    resolveStyleAndSetTypeface(null, style, weight);
+                    break;
+            }
         }
-        if (tf != null) {
-            setTypeface(tf);
-            return;
+    }
+
+    private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
+            @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+        if (weight >= 0) {
+            weight = Math.min(Typeface.MAX_WEIGHT, weight);
+            final boolean italic = (style & Typeface.ITALIC) != 0;
+            setTypeface(Typeface.create(typeface, weight, italic));
+        } else {
+            setTypeface(typeface, style);
         }
-        switch (typefaceIndex) {
-            case SANS:
-                tf = Typeface.SANS_SERIF;
-                break;
-
-            case SERIF:
-                tf = Typeface.SERIF;
-                break;
-
-            case MONOSPACE:
-                tf = Typeface.MONOSPACE;
-                break;
-        }
-
-        setTypeface(tf, styleIndex);
     }
 
     private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) {
@@ -2080,7 +2124,7 @@
      * @attr ref android.R.styleable#TextView_typeface
      * @attr ref android.R.styleable#TextView_textStyle
      */
-    public void setTypeface(Typeface tf, int style) {
+    public void setTypeface(@Nullable Typeface tf, @Typeface.Style int style) {
         if (style > 0) {
             if (tf == null) {
                 tf = Typeface.defaultFromStyle(style);
@@ -2329,7 +2373,7 @@
         if (mMovement != movement) {
             mMovement = movement;
 
-            if (movement != null && !(mText instanceof Spannable)) {
+            if (movement != null && mSpannable == null) {
                 setText(mText);
             }
 
@@ -2379,8 +2423,8 @@
             return;
         }
         if (mTransformation != null) {
-            if (mText instanceof Spannable) {
-                ((Spannable) mText).removeSpan(mTransformation);
+            if (mSpannable != null) {
+                mSpannable.removeSpan(mTransformation);
             }
         }
 
@@ -2397,7 +2441,7 @@
         setText(mText);
 
         if (hasPasswordTransformationMethod()) {
-            notifyAccessibilityStateChanged(
+            notifyViewAccessibilityStateChangedIfNeeded(
                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
 
@@ -3385,6 +3429,7 @@
         boolean mFontFamilyExplicit = false;
         int mTypefaceIndex = -1;
         int mStyleIndex = -1;
+        int mFontWeight = -1;
         boolean mAllCaps = false;
         int mShadowColor = 0;
         float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0;
@@ -3409,6 +3454,7 @@
                     + "    mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
                     + "    mTypefaceIndex:" + mTypefaceIndex + "\n"
                     + "    mStyleIndex:" + mStyleIndex + "\n"
+                    + "    mFontWeight:" + mFontWeight + "\n"
                     + "    mAllCaps:" + mAllCaps + "\n"
                     + "    mShadowColor:" + mShadowColor + "\n"
                     + "    mShadowDx:" + mShadowDx + "\n"
@@ -3444,6 +3490,8 @@
                 com.android.internal.R.styleable.TextAppearance_fontFamily);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle,
                 com.android.internal.R.styleable.TextAppearance_textStyle);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textFontWeight,
+                com.android.internal.R.styleable.TextAppearance_textFontWeight);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps,
                 com.android.internal.R.styleable.TextAppearance_textAllCaps);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor,
@@ -3529,6 +3577,9 @@
                 case com.android.internal.R.styleable.TextAppearance_textStyle:
                     attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex);
                     break;
+                case com.android.internal.R.styleable.TextAppearance_textFontWeight:
+                    attributes.mFontWeight = appearance.getInt(attr, attributes.mFontWeight);
+                    break;
                 case com.android.internal.R.styleable.TextAppearance_textAllCaps:
                     attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps);
                     break;
@@ -3591,7 +3642,7 @@
             attributes.mFontFamily = null;
         }
         setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily,
-                attributes.mTypefaceIndex, attributes.mStyleIndex);
+                attributes.mTypefaceIndex, attributes.mStyleIndex, attributes.mFontWeight);
 
         if (attributes.mShadowColor != 0) {
             setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy,
@@ -3858,7 +3909,7 @@
      * @attr ref android.R.styleable#TextView_typeface
      * @attr ref android.R.styleable#TextView_textStyle
      */
-    public void setTypeface(Typeface tf) {
+    public void setTypeface(@Nullable Typeface tf) {
         if (mTextPaint.getTypeface() != tf) {
             mTextPaint.setTypeface(tf);
 
@@ -4085,6 +4136,36 @@
     }
 
     /**
+     * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}.
+     *
+     * @return a current {@link PrecomputedText.Params}
+     * @see PrecomputedText
+     */
+    public @NonNull PrecomputedText.Params getTextMetricsParams() {
+        return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
+                mBreakStrategy, mHyphenationFrequency);
+    }
+
+    /**
+     * Apply the text layout parameter.
+     *
+     * Update the TextView parameters to be compatible with {@link PrecomputedText.Params}.
+     * @see PrecomputedText
+     */
+    public void setTextMetricsParams(@NonNull PrecomputedText.Params params) {
+        mTextPaint.set(params.getTextPaint());
+        mUserSetTextScaleX = true;
+        mTextDir = params.getTextDirection();
+        mBreakStrategy = params.getBreakStrategy();
+        mHyphenationFrequency = params.getHyphenationFrequency();
+        if (mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    /**
      * Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
      * last line is too short for justification, the last line will be displayed with the
      * alignment set by {@link android.view.View#setTextAlignment}.
@@ -5152,7 +5233,8 @@
     public void setAccessibilityHeading(boolean isHeading) {
         if (isHeading != mIsAccessibilityHeading) {
             mIsAccessibilityHeading = isHeading;
-            notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
         }
     }
 
@@ -5186,7 +5268,7 @@
         ((Editable) mText).append(text, start, end);
 
         if (mAutoLinkMask != 0) {
-            boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
+            boolean linksWereAdded = Linkify.addLinks(mSpannable, mAutoLinkMask);
             // Do not change the movement method for text that support text selection as it
             // would prevent an arbitrary cursor displacement.
             if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
@@ -5345,7 +5427,7 @@
         }
 
         if (ss.selStart >= 0 && ss.selEnd >= 0) {
-            if (mText instanceof Spannable) {
+            if (mSpannable != null) {
                 int len = mText.length();
 
                 if (ss.selStart > len || ss.selEnd > len) {
@@ -5358,7 +5440,7 @@
                     Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd
                             + " out of range for " + restored + "text " + mText);
                 } else {
-                    Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
+                    Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);
 
                     if (ss.frozenWithFocus) {
                         createEditorIfNeeded();
@@ -5460,9 +5542,16 @@
      * {@link android.text.Editable.Factory} to create final or intermediate
      * {@link Editable Editables}.
      *
+     * If the passed text is a {@link PrecomputedText} but the parameters used to create the
+     * PrecomputedText mismatches with this TextView, IllegalArgumentException is thrown. To ensure
+     * the parameters match, you can call {@link TextView#setTextMetricsParams} before calling this.
+     *
      * @param text text to be displayed
      *
      * @attr ref android.R.styleable#TextView_text
+     * @throws IllegalArgumentException if the passed text is a {@link PrecomputedText} but the
+     *                                  parameters used to create the PrecomputedText mismatches
+     *                                  with this TextView.
      */
     @android.view.RemotableViewMethod
     public final void setText(CharSequence text) {
@@ -5565,6 +5654,8 @@
             needEditableForNotification = true;
         }
 
+        PrecomputedText precomputed =
+                (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
         if (type == BufferType.EDITABLE || getKeyListener() != null
                 || needEditableForNotification) {
             createEditorIfNeeded();
@@ -5574,9 +5665,22 @@
             setFilters(t, mFilters);
             InputMethodManager imm = InputMethodManager.peekInstance();
             if (imm != null) imm.restartInput(this);
+        } else if (precomputed != null) {
+            if (mTextDir == null) {
+                mTextDir = getTextDirectionHeuristic();
+            }
+            if (!precomputed.getParams().isSameTextMetricsInternal(
+                    getPaint(), mTextDir, mBreakStrategy, mHyphenationFrequency)) {
+                throw new IllegalArgumentException(
+                        "PrecomputedText's Parameters don't match the parameters of this TextView."
+                        + "Consider using setTextMetricsParams(precomputedText.getParams()) "
+                        + "to override the settings of this TextView: "
+                        + "PrecomputedText: " + precomputed.getParams()
+                        + "TextView: " + getTextMetricsParams());
+            }
         } else if (type == BufferType.SPANNABLE || mMovement != null) {
             text = mSpannableFactory.newSpannable(text);
-        } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
+        } else if (!(text instanceof CharWrapper)) {
             text = TextUtils.stringOrSpannedString(text);
         }
 
@@ -5598,7 +5702,7 @@
                  * movement method, because setMovementMethod() may call
                  * setText() again to try to upgrade the buffer type.
                  */
-                mText = text;
+                setTextInternal(text);
 
                 // Do not change the movement method for text that support text selection as it
                 // would prevent an arbitrary cursor displacement.
@@ -5609,7 +5713,7 @@
         }
 
         mBufferType = type;
-        mText = text;
+        setTextInternal(text);
 
         if (mTransformation == null) {
             mTransformed = text;
@@ -5659,12 +5763,11 @@
         sendOnTextChanged(text, 0, oldlen, textLength);
         onTextChanged(text, 0, oldlen, textLength);
 
-        notifyAccessibilityStateChanged(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+        notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
 
         if (needEditableForNotification) {
             sendAfterTextChanged((Editable) text);
         } else {
-            // Always notify AutoFillManager - it will return right away if autofill is disabled.
             notifyAutoFillManagerAfterTextChangedIfNeeded();
         }
 
@@ -5736,8 +5839,8 @@
         setText(text, type);
 
         if (start >= 0 || end >= 0) {
-            if (mText instanceof Spannable) {
-                Selection.setSelection((Spannable) mText,
+            if (mSpannable != null) {
+                Selection.setSelection(mSpannable,
                                        Math.max(0, Math.min(start, len)),
                                        Math.max(0, Math.min(end, len)));
             }
@@ -5902,15 +6005,19 @@
         boolean forceUpdate = false;
         if (isPassword) {
             setTransformationMethod(PasswordTransformationMethod.getInstance());
-            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
+            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE,
+                    Typeface.NORMAL, -1 /* weight, not specifeid */);
         } else if (isVisiblePassword) {
             if (mTransformation == PasswordTransformationMethod.getInstance()) {
                 forceUpdate = true;
             }
-            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
+            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE,
+                    Typeface.NORMAL, -1 /* weight, not specified */);
         } else if (wasPassword || wasVisiblePassword) {
             // not in password mode, clean up typeface and transformation
-            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, -1, -1);
+            setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */,
+                    DEFAULT_TYPEFACE /* typeface index */, Typeface.NORMAL,
+                    -1 /* weight, not specified */);
             if (mTransformation == PasswordTransformationMethod.getInstance()) {
                 forceUpdate = true;
             }
@@ -5927,7 +6034,7 @@
         }
 
         if (!isSuggestionsEnabled()) {
-            mText = removeSuggestionSpans(mText);
+            setTextInternal(removeSuggestionSpans(mText));
         }
 
         InputMethodManager imm = InputMethodManager.peekInstance();
@@ -6393,7 +6500,7 @@
     public void setError(CharSequence error, Drawable icon) {
         createEditorIfNeeded();
         mEditor.setError(error, icon);
-        notifyAccessibilityStateChanged(
+        notifyViewAccessibilityStateChangedIfNeeded(
                 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
     }
 
@@ -6855,8 +6962,7 @@
     public boolean hasOverlappingRendering() {
         // horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
         return ((getBackground() != null && getBackground().getCurrent() != null)
-                || mText instanceof Spannable || hasSelection()
-                || isHorizontalFadingEdgeEnabled());
+                || mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
     }
 
     /**
@@ -6958,9 +7064,7 @@
         final int selEnd = getSelectionEnd();
         if (mMovement != null && (isFocused() || isPressed()) && selStart >= 0) {
             if (selStart == selEnd) {
-                if (mEditor != null && mEditor.isCursorVisible()
-                        && (SystemClock.uptimeMillis() - mEditor.mShowCursor)
-                        % (2 * Editor.BLINK) < Editor.BLINK) {
+                if (mEditor != null && mEditor.shouldRenderCursor()) {
                     if (mHighlightPathBogus) {
                         if (mHighlightPath == null) mHighlightPath = new Path();
                         mHighlightPath.reset();
@@ -7308,11 +7412,11 @@
 
     @Override
     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
-        if (mText instanceof Spannable && mLinksClickable) {
+        if (mSpannable != null && mLinksClickable) {
             final float x = event.getX(pointerIndex);
             final float y = event.getY(pointerIndex);
             final int offset = getOffsetForPosition(x, y);
-            final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
+            final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset,
                     ClickableSpan.class);
             if (clickables.length > 0) {
                 return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND);
@@ -7405,10 +7509,10 @@
 
         } else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
             // mMovement is not null from doKeyDown
-            mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
+            mMovement.onKeyUp(this, mSpannable, keyCode, up);
             while (--repeatCount > 0) {
-                mMovement.onKeyDown(this, (Spannable) mText, keyCode, down);
-                mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
+                mMovement.onKeyDown(this, mSpannable, keyCode, down);
+                mMovement.onKeyUp(this, mSpannable, keyCode, up);
             }
         }
 
@@ -7603,8 +7707,7 @@
             boolean doDown = true;
             if (otherEvent != null) {
                 try {
-                    boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
-                            otherEvent);
+                    boolean handled = mMovement.onKeyOther(this, mSpannable, otherEvent);
                     doDown = false;
                     if (handled) {
                         return KEY_EVENT_HANDLED;
@@ -7615,7 +7718,7 @@
                 }
             }
             if (doDown) {
-                if (mMovement.onKeyDown(this, (Spannable) mText, keyCode, event)) {
+                if (mMovement.onKeyDown(this, mSpannable, keyCode, event)) {
                     if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
                         mPreventDefaultMovement = true;
                     }
@@ -7757,7 +7860,7 @@
         }
 
         if (mMovement != null && mLayout != null) {
-            if (mMovement.onKeyUp(this, (Spannable) mText, keyCode, event)) {
+            if (mMovement.onKeyUp(this, mSpannable, keyCode, event)) {
                 return true;
             }
         }
@@ -7993,7 +8096,9 @@
         return false;
     }
 
-    private void nullLayouts() {
+    /** @hide */
+    @VisibleForTesting
+    public void nullLayouts() {
         if (mLayout instanceof BoringLayout && mSavedLayout == null) {
             mSavedLayout = (BoringLayout) mLayout;
         }
@@ -8087,7 +8192,8 @@
      * not the full view width with padding.
      * {@hide}
      */
-    protected void makeNewLayout(int wantWidth, int hintWidth,
+    @VisibleForTesting
+    public void makeNewLayout(int wantWidth, int hintWidth,
                                  BoringLayout.Metrics boring,
                                  BoringLayout.Metrics hintBoring,
                                  int ellipsisWidth, boolean bringIntoView) {
@@ -8220,13 +8326,23 @@
     }
 
     /**
+     * Returns true if DynamicLayout is required
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean useDynamicLayout() {
+        return isTextSelectable() || (mSpannable != null && mPrecomputed == null);
+    }
+
+    /**
      * @hide
      */
     protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
             Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
             boolean useSaved) {
         Layout result = null;
-        if (mText instanceof Spannable) {
+        if (useDynamicLayout()) {
             final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
                     wantWidth)
                     .setDisplayText(mTransformed)
@@ -8377,7 +8493,9 @@
         return mIncludePad;
     }
 
-    private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
+    /** @hide */
+    @VisibleForTesting
+    public static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -9166,7 +9284,7 @@
         }
 
         if (newStart != start) {
-            Selection.setSelection((Spannable) mText, newStart);
+            Selection.setSelection(mSpannable, newStart);
             return true;
         }
 
@@ -9696,11 +9814,21 @@
             return;
         }
         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
-        if (afm != null) {
+        if (afm == null) {
+            return;
+        }
+
+        if (mLastValueSentToAutofillManager == null
+                || !mLastValueSentToAutofillManager.equals(mText)) {
             if (android.view.autofill.Helper.sVerbose) {
-                Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText);
+                Log.v(LOG_TAG, "notifying AFM after text changed");
             }
             afm.notifyValueChanged(TextView.this);
+            mLastValueSentToAutofillManager = mText;
+        } else {
+            if (android.view.autofill.Helper.sVerbose) {
+                Log.v(LOG_TAG, "not notifying AFM on unchanged text");
+            }
         }
     }
 
@@ -9893,9 +10021,8 @@
         if (mEditor != null) mEditor.onFocusChanged(focused, direction);
 
         if (focused) {
-            if (mText instanceof Spannable) {
-                Spannable sp = (Spannable) mText;
-                MetaKeyKeyListener.resetMetaState(sp);
+            if (mSpannable != null) {
+                MetaKeyKeyListener.resetMetaState(mSpannable);
             }
         }
 
@@ -9933,7 +10060,7 @@
      */
     public void clearComposingText() {
         if (mText instanceof Spannable) {
-            BaseInputConnection.removeComposingSpans((Spannable) mText);
+            BaseInputConnection.removeComposingSpans(mSpannable);
         }
     }
 
@@ -9989,7 +10116,7 @@
             boolean handled = false;
 
             if (mMovement != null) {
-                handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
+                handled |= mMovement.onTouchEvent(this, mSpannable, event);
             }
 
             final boolean textIsSelectable = isTextSelectable();
@@ -9997,7 +10124,7 @@
                 // The LinkMovementMethod which should handle taps on links has not been installed
                 // on non editable text that support text selection.
                 // We reproduce its behavior here to open links for these.
-                ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
+                ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
                     getSelectionEnd(), ClickableSpan.class);
 
                 if (links.length > 0) {
@@ -10032,7 +10159,7 @@
     public boolean onGenericMotionEvent(MotionEvent event) {
         if (mMovement != null && mText instanceof Spannable && mLayout != null) {
             try {
-                if (mMovement.onGenericMotionEvent(this, (Spannable) mText, event)) {
+                if (mMovement.onGenericMotionEvent(this, mSpannable, event)) {
                     return true;
                 }
             } catch (AbstractMethodError ex) {
@@ -10093,8 +10220,8 @@
 
     @Override
     public boolean onTrackballEvent(MotionEvent event) {
-        if (mMovement != null && mText instanceof Spannable && mLayout != null) {
-            if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
+        if (mMovement != null && mSpannable != null && mLayout != null) {
+            if (mMovement.onTrackballEvent(this, mSpannable, event)) {
                 return true;
             }
         }
@@ -10833,7 +10960,7 @@
             final boolean ltrLine =
                     mLayout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
             final float[] widths = new float[offsetEnd - offsetStart];
-            mLayout.getPaint().getTextWidths(mText, offsetStart, offsetEnd, widths);
+            mLayout.getPaint().getTextWidths(mTransformed, offsetStart, offsetEnd, widths);
             final float top = mLayout.getLineTop(line);
             final float bottom = mLayout.getLineBottom(line);
             for (int offset = offsetStart; offset < offsetEnd; ++offset) {
@@ -11015,7 +11142,7 @@
                 if (mText != null) {
                     int updatedTextLength = mText.length();
                     if (updatedTextLength > 0) {
-                        Selection.setSelection((Spannable) mText, updatedTextLength);
+                        Selection.setSelection(mSpannable, updatedTextLength);
                     }
                 }
             } return true;
@@ -11403,29 +11530,132 @@
     @NonNull
     public TextClassifier getTextClassifier() {
         if (mTextClassifier == null) {
-            TextClassificationManager tcm =
+            final TextClassificationManager tcm =
                     mContext.getSystemService(TextClassificationManager.class);
             if (tcm != null) {
-                mTextClassifier = tcm.getTextClassifier();
-            } else {
-                mTextClassifier = TextClassifier.NO_OP;
+                return tcm.getTextClassifier();
             }
+            return TextClassifier.NO_OP;
         }
         return mTextClassifier;
     }
 
     /**
-     * Starts an ActionMode for the specified TextLink.
+     * Returns a session-aware text classifier.
+     * This method creates one if none already exists or the current one is destroyed.
+     */
+    @NonNull
+    TextClassifier getTextClassificationSession() {
+        if (mTextClassificationSession == null || mTextClassificationSession.isDestroyed()) {
+            final TextClassificationManager tcm =
+                    mContext.getSystemService(TextClassificationManager.class);
+            if (tcm != null) {
+                final String widgetType;
+                if (isTextEditable()) {
+                    widgetType = TextClassifier.WIDGET_TYPE_EDITTEXT;
+                } else if (isTextSelectable()) {
+                    widgetType = TextClassifier.WIDGET_TYPE_TEXTVIEW;
+                } else {
+                    widgetType = TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
+                }
+                // TODO: Tagged this widgetType with a * so it we can monitor if it reports
+                // SelectionEvents exactly as the older Logger does. Remove once investigations
+                // are complete.
+                final TextClassificationContext textClassificationContext =
+                        new TextClassificationContext.Builder(
+                                mContext.getPackageName(), "*" + widgetType)
+                                .build();
+                if (mTextClassifier != null) {
+                    mTextClassificationSession = tcm.createTextClassificationSession(
+                            textClassificationContext, mTextClassifier);
+                } else {
+                    mTextClassificationSession = tcm.createTextClassificationSession(
+                            textClassificationContext);
+                }
+            } else {
+                mTextClassificationSession = TextClassifier.NO_OP;
+            }
+        }
+        return mTextClassificationSession;
+    }
+
+    /**
+     * Returns true if this TextView uses a no-op TextClassifier.
+     */
+    boolean usesNoOpTextClassifier() {
+        return getTextClassifier() == TextClassifier.NO_OP;
+    }
+
+
+    /**
+     * Starts an ActionMode for the specified TextLinkSpan.
      *
      * @return Whether or not we're attempting to start the action mode.
      * @hide
      */
-    public boolean requestActionMode(@NonNull TextLinks.TextLink link) {
-        Preconditions.checkNotNull(link);
+    public boolean requestActionMode(@NonNull TextLinks.TextLinkSpan clickedSpan) {
+        Preconditions.checkNotNull(clickedSpan);
+
+        if (!(mText instanceof Spanned)) {
+            return false;
+        }
+
+        final int start = ((Spanned) mText).getSpanStart(clickedSpan);
+        final int end = ((Spanned) mText).getSpanEnd(clickedSpan);
+
+        if (start < 0 || end > mText.length() || start >= end) {
+            return false;
+        }
+
         createEditorIfNeeded();
-        mEditor.startLinkActionModeAsync(link);
+        mEditor.startLinkActionModeAsync(start, end);
         return true;
     }
+
+    /**
+     * Handles a click on the specified TextLinkSpan.
+     *
+     * @return Whether or not the click is being handled.
+     * @hide
+     */
+    public boolean handleClick(@NonNull TextLinks.TextLinkSpan clickedSpan) {
+        Preconditions.checkNotNull(clickedSpan);
+        if (mText instanceof Spanned) {
+            final Spanned spanned = (Spanned) mText;
+            final int start = spanned.getSpanStart(clickedSpan);
+            final int end = spanned.getSpanEnd(clickedSpan);
+            if (start >= 0 && end <= mText.length() && start < end) {
+                final TextClassification.Request request = new TextClassification.Request.Builder(
+                        mText, start, end)
+                        .setDefaultLocales(getTextLocales())
+                        .build();
+                final Supplier<TextClassification> supplier = () ->
+                        getTextClassifier().classifyText(request);
+                final Consumer<TextClassification> consumer = classification -> {
+                    if (classification != null) {
+                        if (!classification.getActions().isEmpty()) {
+                            try {
+                                classification.getActions().get(0).getActionIntent().send();
+                            } catch (PendingIntent.CanceledException e) {
+                                Log.e(LOG_TAG, "Error sending PendingIntent", e);
+                            }
+                        } else {
+                            Log.d(LOG_TAG, "No link action to perform");
+                        }
+                    } else {
+                        // classification == null
+                        Log.d(LOG_TAG, "Timeout while classifying text");
+                    }
+                };
+                CompletableFuture.supplyAsync(supplier)
+                        .completeOnTimeout(null, 1, TimeUnit.SECONDS)
+                        .thenAccept(consumer);
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @hide
      */
@@ -11435,6 +11665,13 @@
         }
     }
 
+    /** @hide */
+    public void hideFloatingToolbar(int durationMs) {
+        if (mEditor != null) {
+            mEditor.hideFloatingToolbar(durationMs);
+        }
+    }
+
     boolean canUndo() {
         return mEditor != null && mEditor.canUndo();
     }
@@ -11529,10 +11766,10 @@
     boolean selectAllText() {
         if (mEditor != null) {
             // Hide the toolbar before changing the selection to avoid flickering.
-            mEditor.hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
+            hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
         }
         final int length = mText.length();
-        Selection.setSelection((Spannable) mText, 0, length);
+        Selection.setSelection(mSpannable, 0, length);
         return length > 0;
     }
 
@@ -11560,7 +11797,7 @@
                 }
                 if (paste != null) {
                     if (!didFirst) {
-                        Selection.setSelection((Spannable) mText, max);
+                        Selection.setSelection(mSpannable, max);
                         ((Editable) mText).replace(min, max, paste);
                         didFirst = true;
                     } else {
@@ -11582,7 +11819,7 @@
             selectedText = TextUtils.trimToParcelableSize(selectedText);
             sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
             getContext().startActivity(Intent.createChooser(sharingIntent, null));
-            Selection.setSelection((Spannable) mText, getSelectionEnd());
+            Selection.setSelection(mSpannable, getSelectionEnd());
         }
     }
 
@@ -11657,7 +11894,7 @@
             case DragEvent.ACTION_DRAG_LOCATION:
                 if (mText instanceof Spannable) {
                     final int offset = getOffsetForPosition(event.getX(), event.getY());
-                    Selection.setSelection((Spannable) mText, offset);
+                    Selection.setSelection(mSpannable, offset);
                 }
                 return true;
 
@@ -11695,6 +11932,9 @@
     }
 
     /**
+     * Returns the current {@link TextDirectionHeuristic}.
+     *
+     * @return the current {@link TextDirectionHeuristic}.
      * @hide
      */
     protected TextDirectionHeuristic getTextDirectionHeuristic() {
@@ -12288,9 +12528,8 @@
                         + " before=" + before + " after=" + after + ": " + buffer);
             }
 
-            if (AccessibilityManager.getInstance(mContext).isEnabled()
-                    && !isPasswordInputType(getInputType()) && !hasPasswordTransformationMethod()) {
-                mBeforeText = buffer.toString();
+            if (AccessibilityManager.getInstance(mContext).isEnabled() && (mTransformed != null)) {
+                mBeforeText = mTransformed.toString();
             }
 
             TextView.this.sendBeforeTextChanged(buffer, start, before, after);
diff --git a/android/widget/TextViewPrecomputedTextPerfTest.java b/android/widget/TextViewPrecomputedTextPerfTest.java
new file mode 100644
index 0000000..dc34b7f
--- /dev/null
+++ b/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget;
+
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Typeface;
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.PrecomputedText;
+import android.text.Layout;
+import android.text.BoringLayout;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextPaint;
+import android.text.style.TextAppearanceSpan;
+import android.view.LayoutInflater;
+import android.text.TextPerfUtils;
+import android.view.View.MeasureSpec;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import com.android.perftests.core.R;
+
+import java.util.Random;
+import java.util.Locale;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+import static android.widget.TextView.UNKNOWN_BORING;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TextViewPrecomputedTextPerfTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
+    private static final boolean NO_STYLE_TEXT = false;
+    private static final boolean STYLE_TEXT = true;
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+    public TextViewPrecomputedTextPerfTest() {}
+
+    private static class TestableTextView extends TextView {
+        public TestableTextView(Context ctx) {
+            super(ctx);
+        }
+
+        public void onMeasure(int w, int h) {
+            super.onMeasure(w, h);
+        }
+
+        public void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+        }
+    }
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
+
+    @Before
+    public void setUp() {
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void testNewLayout_RandomText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TextView textView = new TextView(getContext());
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
+                TEXT_WIDTH, false);
+        }
+    }
+
+    @Test
+    public void testNewLayout_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TextView textView = new TextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
+                TEXT_WIDTH, false);
+        }
+    }
+
+    @Test
+    public void testNewLayout_PrecomputedText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TextView textView = new TextView(getContext());
+            textView.setTextMetricsParams(params);
+            textView.setText(text);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
+                TEXT_WIDTH, false);
+        }
+    }
+
+    @Test
+    public void testNewLayout_PrecomputedText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TextView textView = new TextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setTextMetricsParams(params);
+            textView.setText(text);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
+                TEXT_WIDTH, false);
+        }
+    }
+
+    @Test
+    public void testSetText_RandomText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TextView textView = new TextView(getContext());
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.setText(text);
+        }
+    }
+
+    @Test
+    public void testSetText_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TextView textView = new TextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.setText(text);
+        }
+    }
+
+    @Test
+    public void testSetText_PrecomputedText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TextView textView = new TextView(getContext());
+            textView.setTextMetricsParams(params);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.setText(text);
+        }
+    }
+
+    @Test
+    public void testSetText_PrecomputedText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BoringLayout.Metrics metrics = new BoringLayout.Metrics();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TextView textView = new TextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setTextMetricsParams(params);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.setText(text);
+        }
+    }
+
+    @Test
+    public void testOnMeasure_RandomText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onMeasure(width, height);
+        }
+    }
+
+    @Test
+    public void testOnMeasure_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onMeasure(width, height);
+        }
+    }
+
+    @Test
+    public void testOnMeasure_PrecomputedText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextMetricsParams(params);
+            textView.setText(text);
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onMeasure(width, height);
+        }
+    }
+
+    @Test
+    public void testOnMeasure_PrecomputedText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setTextMetricsParams(params);
+            textView.setText(text);
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onMeasure(width, height);
+        }
+    }
+
+    @Test
+    public void testOnDraw_RandomText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            final DisplayListCanvas c = node.start(
+                textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onDraw(c);
+        }
+    }
+
+    @Test
+    public void testOnDraw_RandomText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+            textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+            textView.setText(text);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            final DisplayListCanvas c = node.start(
+                textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onDraw(c);
+        }
+    }
+
+    @Test
+    public void testOnDraw_PrecomputedText() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
+        int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextMetricsParams(params);
+            textView.setText(text);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            final DisplayListCanvas c = node.start(
+                textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onDraw(c);
+        }
+    }
+
+    @Test
+    public void testOnDraw_PrecomputedText_Selectable() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
+        int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
+                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
+            final CharSequence text = PrecomputedText.create(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
+            final TestableTextView textView = new TestableTextView(getContext());
+            textView.setTextIsSelectable(true);
+            textView.setTextMetricsParams(params);
+            textView.setText(text);
+            textView.measure(width, height);
+            textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            final DisplayListCanvas c = node.start(
+                textView.getMeasuredWidth(), textView.getMeasuredHeight());
+            textView.nullLayouts();
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.onDraw(c);
+        }
+    }
+}
diff --git a/android/widget/TimePickerClockDelegate.java b/android/widget/TimePickerClockDelegate.java
index 706b0ce..77670b3 100644
--- a/android/widget/TimePickerClockDelegate.java
+++ b/android/widget/TimePickerClockDelegate.java
@@ -50,6 +50,7 @@
 import com.android.internal.widget.NumericTextView;
 import com.android.internal.widget.NumericTextView.OnValueChangedListener;
 
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Calendar;
@@ -804,20 +805,56 @@
     private void updateHeaderSeparator() {
         final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
                 (mIs24Hour) ? "Hm" : "hm");
-        final String separatorText;
-        // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
-        final char[] hourFormats = {'H', 'h', 'K', 'k'};
-        int hIndex = lastIndexOfAny(bestDateTimePattern, hourFormats);
-        if (hIndex == -1) {
-            // Default case
-            separatorText = ":";
-        } else {
-            separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
-        }
+        final String separatorText = getHourMinSeparatorFromPattern(bestDateTimePattern);
         mSeparatorView.setText(separatorText);
         mTextInputPickerView.updateSeparator(separatorText);
     }
 
+    /**
+     * This helper method extracts the time separator from the {@code datetimePattern}.
+     *
+     * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":".
+     *
+     * See http://unicode.org/cldr/trac/browser/trunk/common/main
+     *
+     * @return Separator string. This is the character or set of quoted characters just after the
+     * hour marker in {@code dateTimePattern}. Returns a colon (:) if it can't locate the
+     * separator.
+     *
+     * @hide
+     */
+    private static String getHourMinSeparatorFromPattern(String dateTimePattern) {
+        final String defaultSeparator = ":";
+        boolean foundHourPattern = false;
+        for (int i = 0; i < dateTimePattern.length(); i++) {
+            switch (dateTimePattern.charAt(i)) {
+                // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats.
+                case 'H':
+                case 'h':
+                case 'K':
+                case 'k':
+                    foundHourPattern = true;
+                    continue;
+                case ' ': // skip spaces
+                    continue;
+                case '\'':
+                    if (!foundHourPattern) {
+                        continue;
+                    }
+                    SpannableStringBuilder quotedSubstring = new SpannableStringBuilder(
+                            dateTimePattern.substring(i));
+                    int quotedTextLength = DateFormat.appendQuotedText(quotedSubstring, 0);
+                    return quotedSubstring.subSequence(0, quotedTextLength).toString();
+                default:
+                    if (!foundHourPattern) {
+                        continue;
+                    }
+                    return Character.toString(dateTimePattern.charAt(i));
+            }
+        }
+        return defaultSeparator;
+    }
+
     static private int lastIndexOfAny(String str, char[] any) {
         final int lengthAny = any.length;
         if (lengthAny > 0) {
diff --git a/android/widget/Toast.java b/android/widget/Toast.java
index edcf209..d74a60e 100644
--- a/android/widget/Toast.java
+++ b/android/widget/Toast.java
@@ -531,6 +531,14 @@
                     mWM.removeViewImmediate(mView);
                 }
 
+
+                // Now that we've removed the view it's safe for the server to release
+                // the resources.
+                try {
+                    getService().finishToken(mPackageName, this);
+                } catch (RemoteException e) {
+                }
+
                 mView = null;
             }
         }
diff --git a/android/widget/VideoView2.java b/android/widget/VideoView2.java
index 8650c0a..388eae2 100644
--- a/android/widget/VideoView2.java
+++ b/android/widget/VideoView2.java
@@ -22,32 +22,40 @@
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
-import android.media.MediaPlayerBase;
+import android.media.DataSourceDesc;
+import android.media.MediaItem2;
+import android.media.MediaMetadata2;
+import android.media.MediaPlayer2;
+import android.media.SessionToken2;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
 import android.media.update.ApiLoader;
 import android.media.update.VideoView2Provider;
-import android.media.update.ViewProvider;
+import android.media.update.ViewGroupHelper;
 import android.net.Uri;
+import android.os.Bundle;
 import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
-// TODO: Use @link tag to refer MediaPlayer2 in docs once MediaPlayer2.java is submitted. Same to
-// MediaSession2.
-// TODO: change the reference from MediaPlayer to MediaPlayer2.
+// TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted.
 /**
- * Displays a video file.  VideoView2 class is a View class which is wrapping MediaPlayer2 so that
- * developers can easily implement a video rendering application.
+ * @hide
+ * Displays a video file.  VideoView2 class is a View class which is wrapping {@link MediaPlayer2}
+ * so that developers can easily implement a video rendering application.
  *
  * <p>
  * <em> Data sources that VideoView2 supports : </em>
- * VideoView2 can play video files and audio-only fiels as
+ * VideoView2 can play video files and audio-only files as
  * well. It can load from various sources such as resources or content providers. The supported
- * media file formats are the same as MediaPlayer2.
+ * media file formats are the same as {@link MediaPlayer2}.
  *
  * <p>
  * <em> View type can be selected : </em>
@@ -76,8 +84,8 @@
  * If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute
  * to false and assign the customed media control widget using {@link #setMediaControlView2}.
  * <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer.
- * <li> VideoView2 is integrated with MediaSession2 and so it responses with media key events.
- * A VideoView2 keeps a MediaSession2 instance internally and connects it to a corresponding
+ * <li> VideoView2 is integrated with MediaSession and so it responses with media key events.
+ * A VideoView2 keeps a MediaSession instance internally and connects it to a corresponding
  * MediaControlView2 instance.
  * </p>
  * </ul>
@@ -96,10 +104,8 @@
  * does not restore the current play state, play position, selected tracks. Applications should save
  * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and
  * {@link android.app.Activity#onRestoreInstanceState}.
- *
- * @hide
  */
-public class VideoView2 extends FrameLayout {
+public class VideoView2 extends ViewGroupHelper<VideoView2Provider> {
     /** @hide */
     @IntDef({
             VIEW_TYPE_TEXTUREVIEW,
@@ -108,10 +114,19 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ViewType {}
 
+    /**
+     * Indicates video is rendering on SurfaceView.
+     *
+     * @see #setViewType
+     */
     public static final int VIEW_TYPE_SURFACEVIEW = 1;
-    public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
-    private final VideoView2Provider mProvider;
+    /**
+     * Indicates video is rendering on TextureView.
+     *
+     * @see #setViewType
+     */
+    public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
     public VideoView2(@NonNull Context context) {
         this(context, null);
@@ -128,17 +143,12 @@
     public VideoView2(
             @NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        mProvider = ApiLoader.getProvider(context).createVideoView2(this, new SuperProvider(),
-                attrs, defStyleAttr, defStyleRes);
-    }
-
-    /**
-     * @hide
-     */
-    public VideoView2Provider getProvider() {
-        return mProvider;
+        super((instance, superProvider, privateProvider) ->
+                ApiLoader.getProvider().createVideoView2(
+                        (VideoView2) instance, superProvider, privateProvider,
+                        attrs, defStyleAttr, defStyleRes),
+                context, attrs, defStyleAttr, defStyleRes);
+        mProvider.initialize(attrs, defStyleAttr, defStyleRes);
     }
 
     /**
@@ -146,9 +156,10 @@
      * instance if any.
      *
      * @param mediaControlView a media control view2 instance.
+     * @param intervalMs a time interval in milliseconds until VideoView2 hides MediaControlView2.
      */
-    public void setMediaControlView2(MediaControlView2 mediaControlView) {
-        mProvider.setMediaControlView2_impl(mediaControlView);
+    public void setMediaControlView2(MediaControlView2 mediaControlView, long intervalMs) {
+        mProvider.setMediaControlView2_impl(mediaControlView, intervalMs);
     }
 
     /**
@@ -160,92 +171,72 @@
     }
 
     /**
-     * Starts playback with the media contents specified by {@link #setVideoURI} and
-     * {@link #setVideoPath}.
-     * If it has been paused, this method will resume playback from the current position.
+     * Sets MediaMetadata2 instance. It will replace the previously assigned MediaMetadata2 instance
+     * if any.
+     *
+     * @param metadata a MediaMetadata2 instance.
+     * @hide
      */
-    public void start() {
-        mProvider.start_impl();
+    public void setMediaMetadata(MediaMetadata2 metadata) {
+        mProvider.setMediaMetadata_impl(metadata);
     }
 
     /**
-     * Pauses playback.
+     * Returns MediaMetadata2 instance which is retrieved from MediaPlayer2 inside VideoView2 by
+     * default or by {@link #setMediaMetadata} method.
+     * @hide
      */
-    public void pause() {
-        mProvider.pause_impl();
+    public MediaMetadata2 getMediaMetadata() {
+        // TODO: add to Javadoc whether this value can be null or not when integrating with
+        // MediaSession2.
+        return mProvider.getMediaMetadata_impl();
     }
 
     /**
-     * Gets the duration of the media content specified by #setVideoURI and #setVideoPath
-     * in milliseconds.
+     * Returns MediaController instance which is connected with MediaSession that VideoView2 is
+     * using. This method should be called when VideoView2 is attached to window, or it throws
+     * IllegalStateException, since internal MediaSession instance is not available until
+     * this view is attached to window. Please check {@link android.view.View#isAttachedToWindow}
+     * before calling this method.
+     *
+     * @throws IllegalStateException if interal MediaSession is not created yet.
+     * @hide  TODO: remove
      */
-    public int getDuration() {
-        return mProvider.getDuration_impl();
+    public MediaController getMediaController() {
+        return mProvider.getMediaController_impl();
     }
 
     /**
-     * Gets current playback position in milliseconds.
+     * Returns {@link android.media.SessionToken2} so that developers create their own
+     * {@link android.media.MediaController2} instance. This method should be called when VideoView2
+     * is attached to window, or it throws IllegalStateException.
+     *
+     * @throws IllegalStateException if interal MediaSession is not created yet.
      */
-    public int getCurrentPosition() {
-        return mProvider.getCurrentPosition_impl();
-    }
-
-    // TODO: mention about key-frame related behavior.
-    /**
-     * Moves the media by specified time position.
-     * @param msec the offset in milliseconds from the start to seek to.
-     */
-    public void seekTo(int msec) {
-        mProvider.seekTo_impl(msec);
+    public SessionToken2 getMediaSessionToken() {
+        return mProvider.getMediaSessionToken_impl();
     }
 
     /**
-     * Says if the media is currently playing.
-     * @return true if the media is playing, false if it is not (eg. paused or stopped).
+     * Shows or hides closed caption or subtitles if there is any.
+     * The first subtitle track will be chosen if there multiple subtitle tracks exist.
+     * Default behavior of VideoView2 is not showing subtitle.
+     * @param enable shows closed caption or subtitles if this value is true, or hides.
      */
-    public boolean isPlaying() {
-        return mProvider.isPlaying_impl();
-    }
-
-    // TODO: check what will return if it is a local media.
-    /**
-     * Gets the percentage (0-100) of the content that has been buffered or played so far.
-     */
-    public int getBufferPercentage() {
-        return mProvider.getBufferPercentage_impl();
+    public void setSubtitleEnabled(boolean enable) {
+        mProvider.setSubtitleEnabled_impl(enable);
     }
 
     /**
-     * Returns the audio session ID.
+     * Returns true if showing subtitle feature is enabled or returns false.
+     * Although there is no subtitle track or closed caption, it can return true, if the feature
+     * has been enabled by {@link #setSubtitleEnabled}.
      */
-    public int getAudioSessionId() {
-        return mProvider.getAudioSessionId_impl();
+    public boolean isSubtitleEnabled() {
+        return mProvider.isSubtitleEnabled_impl();
     }
 
     /**
-     * Starts rendering closed caption or subtitles if there is any. The first subtitle track will
-     * be chosen by default if there multiple subtitle tracks exist.
-     */
-    public void showSubtitle() {
-        mProvider.showSubtitle_impl();
-    }
-
-    /**
-     * Stops showing closed captions or subtitles.
-     */
-    public void hideSubtitle() {
-        mProvider.hideSubtitle_impl();
-    }
-
-    /**
-     * Sets full screen mode.
-     */
-    public void setFullScreen(boolean fullScreen) {
-        mProvider.setFullScreen_impl(fullScreen);
-    }
-
-    // TODO: This should be revised after integration with MediaPlayer2.
-    /**
      * Sets playback speed.
      *
      * It is expressed as a multiplicative factor, where normal speed is 1.0f. If it is less than
@@ -254,21 +245,12 @@
      * be reset to the normal speed 1.0f.
      * @param speed the playback speed. It should be positive.
      */
+    // TODO: Support this via MediaController2.
     public void setSpeed(float speed) {
         mProvider.setSpeed_impl(speed);
     }
 
     /**
-     * Returns current speed setting.
-     *
-     * If setSpeed() has never been called, returns the default value 1.0f.
-     * @return current speed setting
-     */
-    public float getSpeed() {
-        return mProvider.getSpeed_impl();
-    }
-
-    /**
      * Sets which type of audio focus will be requested during the playback, or configures playback
      * to not request audio focus. Valid values for focus requests are
      * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
@@ -297,24 +279,11 @@
     }
 
     /**
-     * Sets a remote player for handling playback of the selected route from MediaControlView2.
-     * If this is not called, MediaCotrolView2 will not show the route button.
-     *
-     * @param routeCategories        the list of media control categories in
-     *                               {@link android.support.v7.media.MediaControlIntent}
-     * @param player                 the player to handle the selected route. If null, a default
-     *                               route player will be used.
-     * @throws IllegalStateException if MediaControlView2 is not set.
-     */
-    public void setRouteAttributes(@NonNull List<String> routeCategories,
-            @Nullable MediaPlayerBase player) {
-        mProvider.setRouteAttributes_impl(routeCategories, player);
-    }
-
-    /**
      * Sets video path.
      *
      * @param path the path of the video.
+     *
+     * @hide TODO remove
      */
     public void setVideoPath(String path) {
         mProvider.setVideoPath_impl(path);
@@ -324,9 +293,11 @@
      * Sets video URI.
      *
      * @param uri the URI of the video.
+     *
+     * @hide TODO remove
      */
-    public void setVideoURI(Uri uri) {
-        mProvider.setVideoURI_impl(uri);
+    public void setVideoUri(Uri uri) {
+        mProvider.setVideoUri_impl(uri);
     }
 
     /**
@@ -338,9 +309,30 @@
      *                changed with key/value pairs through the headers parameter with
      *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
      *                to disallow or allow cross domain redirection.
+     *
+     * @hide TODO remove
      */
-    public void setVideoURI(Uri uri, Map<String, String> headers) {
-        mProvider.setVideoURI_impl(uri, headers);
+    public void setVideoUri(Uri uri, Map<String, String> headers) {
+        mProvider.setVideoUri_impl(uri, headers);
+    }
+
+    /**
+     * Sets {@link MediaItem2} object to render using VideoView2. Alternative way to set media
+     * object to VideoView2 is {@link #setDataSource}.
+     * @param mediaItem the MediaItem2 to play
+     * @see #setDataSource
+     */
+    public void setMediaItem(@NonNull MediaItem2 mediaItem) {
+        mProvider.setMediaItem_impl(mediaItem);
+    }
+
+    /**
+     * Sets {@link DataSourceDesc} object to render using VideoView2.
+     * @param dataSource the {@link DataSourceDesc} object to play.
+     * @see #setMediaItem
+     */
+    public void setDataSource(@NonNull DataSourceDesc dataSource) {
+        mProvider.setDataSource_impl(dataSource);
     }
 
     /**
@@ -367,236 +359,89 @@
     }
 
     /**
-     * Stops playback and release all the resources. This should be called whenever a VideoView2
-     * instance is no longer to be used.
-     */
-    public void stopPlayback() {
-        mProvider.stopPlayback_impl();
-    }
-
-    /**
-     * Registers a callback to be invoked when the media file is loaded and ready to go.
+     * Sets custom actions which will be shown as custom buttons in {@link MediaControlView2}.
      *
-     * @param l the callback that will be run.
+     * @param actionList A list of {@link PlaybackState.CustomAction}. The return value of
+     *                   {@link PlaybackState.CustomAction#getIcon()} will be used to draw buttons
+     *                   in {@link MediaControlView2}.
+     * @param executor executor to run callbacks on.
+     * @param listener A listener to be called when a custom button is clicked.
+     * @hide  TODO remove
      */
-    public void setOnPreparedListener(OnPreparedListener l) {
-        mProvider.setOnPreparedListener_impl(l);
-    }
-
-    /**
-     * Registers a callback to be invoked when the end of a media file has been reached during
-     * playback.
-     *
-     * @param l the callback that will be run.
-     */
-    public void setOnCompletionListener(OnCompletionListener l) {
-        mProvider.setOnCompletionListener_impl(l);
-    }
-
-    /**
-     * Registers a callback to be invoked when an error occurs during playback or setup.  If no
-     * listener is specified, or if the listener returned false, VideoView2 will inform the user of
-     * any errors.
-     *
-     * @param l The callback that will be run
-     */
-    public void setOnErrorListener(OnErrorListener l) {
-        mProvider.setOnErrorListener_impl(l);
-    }
-
-    /**
-     * Registers a callback to be invoked when an informational event occurs during playback or
-     * setup.
-     *
-     * @param l The callback that will be run
-     */
-    public void setOnInfoListener(OnInfoListener l) {
-        mProvider.setOnInfoListener_impl(l);
+    public void setCustomActions(List<PlaybackState.CustomAction> actionList,
+            Executor executor, OnCustomActionListener listener) {
+        mProvider.setCustomActions_impl(actionList, executor, listener);
     }
 
     /**
      * Registers a callback to be invoked when a view type change is done.
      * {@see #setViewType(int)}
      * @param l The callback that will be run
+     * @hide
      */
+    @VisibleForTesting
     public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) {
         mProvider.setOnViewTypeChangedListener_impl(l);
     }
 
     /**
      * Registers a callback to be invoked when the fullscreen mode should be changed.
+     * @param l The callback that will be run
+     * @hide  TODO remove
      */
-    public void setFullScreenChangedListener(OnFullScreenChangedListener l) {
-        mProvider.setFullScreenChangedListener_impl(l);
+    public void setFullScreenRequestListener(OnFullScreenRequestListener l) {
+        mProvider.setFullScreenRequestListener_impl(l);
     }
 
     /**
-     * Interface definition of a callback to be invoked when the viw type has been changed.
+     * Interface definition of a callback to be invoked when the view type has been changed.
+     *
+     * @hide
      */
+    @VisibleForTesting
     public interface OnViewTypeChangedListener {
         /**
          * Called when the view type has been changed.
          * @see #setViewType(int)
+         * @param view the View whose view type is changed
          * @param viewType
          * <ul>
          * <li>{@link #VIEW_TYPE_SURFACEVIEW}
          * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
          * </ul>
          */
-        void onViewTypeChanged(@ViewType int viewType);
-    }
-
-    /**
-     * Interface definition of a callback to be invoked when the media source is ready for playback.
-     */
-    public interface OnPreparedListener {
-        /**
-         * Called when the media file is ready for playback.
-         */
-        void onPrepared();
-    }
-
-    /**
-     * Interface definition for a callback to be invoked when playback of a media source has
-     * completed.
-     */
-    public interface OnCompletionListener {
-        /**
-         * Called when the end of a media source is reached during playback.
-         */
-        void onCompletion();
-    }
-
-    /**
-     * Interface definition of a callback to be invoked when there has been an error during an
-     * asynchronous operation.
-     */
-    public interface OnErrorListener {
-        // TODO: Redefine error codes.
-        /**
-         * Called to indicate an error.
-         * @param what the type of error that has occurred
-         * @param extra an extra code, specific to the error.
-         * @return true if the method handled the error, false if it didn't.
-         * @see MediaPlayer#OnErrorListener
-         */
-        boolean onError(int what, int extra);
-    }
-
-    /**
-     * Interface definition of a callback to be invoked to communicate some info and/or warning
-     * about the media or its playback.
-     */
-    public interface OnInfoListener {
-        /**
-         * Called to indicate an info or a warning.
-         * @param what the type of info or warning.
-         * @param extra an extra code, specific to the info.
-         *
-         * @see MediaPlayer#OnInfoListener
-         */
-        void onInfo(int what, int extra);
+        void onViewTypeChanged(View view, @ViewType int viewType);
     }
 
     /**
      * Interface definition of a callback to be invoked to inform the fullscreen mode is changed.
+     * Application should handle the fullscreen mode accordingly.
+     * @hide  TODO remove
      */
-    public interface OnFullScreenChangedListener {
+    public interface OnFullScreenRequestListener {
         /**
          * Called to indicate a fullscreen mode change.
          */
-        void onFullScreenChanged(boolean fullScreen);
+        void onFullScreenRequest(View view, boolean fullScreen);
+    }
+
+    /**
+     * Interface definition of a callback to be invoked to inform that a custom action is performed.
+     * @hide  TODO remove
+     */
+    public interface OnCustomActionListener {
+        /**
+         * Called to indicate that a custom action is performed.
+         *
+         * @param action The action that was originally sent in the
+         *               {@link PlaybackState.CustomAction}.
+         * @param extras Optional extras.
+         */
+        void onCustomAction(String action, Bundle extras);
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        mProvider.onAttachedToWindow_impl();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        mProvider.onDetachedFromWindow_impl();
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        return mProvider.getAccessibilityClassName_impl();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        return mProvider.onTouchEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent ev) {
-        return mProvider.onTrackballEvent_impl(ev);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        return mProvider.onKeyDown_impl(keyCode, event);
-    }
-
-    @Override
-    public void onFinishInflate() {
-        mProvider.onFinishInflate_impl();
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return mProvider.dispatchKeyEvent_impl(event);
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        mProvider.setEnabled_impl(enabled);
-    }
-
-    private class SuperProvider implements ViewProvider {
-        @Override
-        public void onAttachedToWindow_impl() {
-            VideoView2.super.onAttachedToWindow();
-        }
-
-        @Override
-        public void onDetachedFromWindow_impl() {
-            VideoView2.super.onDetachedFromWindow();
-        }
-
-        @Override
-        public CharSequence getAccessibilityClassName_impl() {
-            return VideoView2.super.getAccessibilityClassName();
-        }
-
-        @Override
-        public boolean onTouchEvent_impl(MotionEvent ev) {
-            return VideoView2.super.onTouchEvent(ev);
-        }
-
-        @Override
-        public boolean onTrackballEvent_impl(MotionEvent ev) {
-            return VideoView2.super.onTrackballEvent(ev);
-        }
-
-        @Override
-        public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-            return VideoView2.super.onKeyDown(keyCode, event);
-        }
-
-        @Override
-        public void onFinishInflate_impl() {
-            VideoView2.super.onFinishInflate();
-        }
-
-        @Override
-        public boolean dispatchKeyEvent_impl(KeyEvent event) {
-            return VideoView2.super.dispatchKeyEvent(event);
-        }
-
-        @Override
-        public void setEnabled_impl(boolean enabled) {
-            VideoView2.super.setEnabled(enabled);
-        }
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        mProvider.onLayout_impl(changed, l, t, r, b);
     }
 }